| #include <grub/dl.h> |
| #include <grub/cache.h> |
| #include <grub/arm/system.h> |
| #ifdef GRUB_MACHINE_UBOOT |
| #include <grub/uboot/uboot.h> |
| #include <grub/uboot/api_public.h> |
| #include <grub/mm.h> |
| #endif |
| |
| /* This is only about cache architecture. It doesn't imply |
| the CPU architecture. */ |
| static enum |
| { |
| ARCH_UNKNOWN, |
| ARCH_ARMV5_WRITE_THROUGH, |
| ARCH_ARMV6, |
| ARCH_ARMV6_UNIFIED, |
| ARCH_ARMV7 |
| } type = ARCH_UNKNOWN; |
| |
| static int is_v6_mmu; |
| |
| static grub_uint32_t grub_arch_cache_dlinesz; |
| static grub_uint32_t grub_arch_cache_ilinesz; |
| static grub_uint32_t grub_arch_cache_max_linesz; |
| |
| /* Prototypes for asm functions. */ |
| void grub_arm_clean_dcache_range_armv6 (grub_addr_t start, grub_addr_t end, |
| grub_addr_t dlinesz); |
| void grub_arm_clean_dcache_range_armv7 (grub_addr_t start, grub_addr_t end, |
| grub_addr_t dlinesz); |
| void grub_arm_clean_dcache_range_poc_armv7 (grub_addr_t start, grub_addr_t end, |
| grub_addr_t dlinesz); |
| void grub_arm_invalidate_icache_range_armv6 (grub_addr_t start, grub_addr_t end, |
| grub_addr_t dlinesz); |
| void grub_arm_invalidate_icache_range_armv7 (grub_addr_t start, grub_addr_t end, |
| grub_addr_t dlinesz); |
| void grub_arm_disable_caches_mmu_armv6 (void); |
| void grub_arm_disable_caches_mmu_armv7 (void); |
| grub_uint32_t grub_arm_main_id (void); |
| grub_uint32_t grub_arm_cache_type (void); |
| |
| static void |
| probe_caches (void) |
| { |
| grub_uint32_t main_id, cache_type; |
| |
| /* Read main ID Register */ |
| main_id = grub_arm_main_id (); |
| |
| switch ((main_id >> 16) & 0xf) |
| { |
| case 0x3: |
| case 0x4: |
| case 0x5: |
| case 0x6: |
| is_v6_mmu = 0; |
| break; |
| case 0x7: |
| case 0xf: |
| is_v6_mmu = 1; |
| break; |
| default: |
| grub_fatal ("Unsupported ARM ID 0x%x", main_id); |
| } |
| |
| /* Read Cache Type Register */ |
| cache_type = grub_arm_cache_type (); |
| |
| switch (cache_type >> 24) |
| { |
| case 0x00: |
| case 0x01: |
| grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3); |
| grub_arch_cache_ilinesz = 8 << (cache_type & 3); |
| type = ARCH_ARMV5_WRITE_THROUGH; |
| break; |
| case 0x04: |
| case 0x0a: |
| case 0x0c: |
| case 0x0e: |
| case 0x1c: |
| grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3); |
| grub_arch_cache_ilinesz = 8 << (cache_type & 3); |
| type = ARCH_ARMV6_UNIFIED; |
| break; |
| case 0x05: |
| case 0x0b: |
| case 0x0d: |
| case 0x0f: |
| case 0x1d: |
| grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3); |
| grub_arch_cache_ilinesz = 8 << (cache_type & 3); |
| type = ARCH_ARMV6; |
| break; |
| case 0x80 ... 0x8f: |
| grub_arch_cache_dlinesz = 4 << ((cache_type >> 16) & 0xf); |
| grub_arch_cache_ilinesz = 4 << (cache_type & 0xf); |
| type = ARCH_ARMV7; |
| break; |
| default: |
| grub_fatal ("Unsupported cache type 0x%x", cache_type); |
| } |
| if (grub_arch_cache_dlinesz > grub_arch_cache_ilinesz) |
| grub_arch_cache_max_linesz = grub_arch_cache_dlinesz; |
| else |
| grub_arch_cache_max_linesz = grub_arch_cache_ilinesz; |
| } |
| |
| #ifdef GRUB_MACHINE_UBOOT |
| |
| static void subdivide (grub_uint32_t *table, grub_uint32_t *subtable, |
| grub_uint32_t addr) |
| { |
| grub_uint32_t j; |
| addr = addr >> 20 << 20; |
| table[addr >> 20] = (grub_addr_t) subtable | 1; |
| for (j = 0; j < 256; j++) |
| subtable[j] = addr | (j << 12) |
| | (3 << 4) | (3 << 6) | (3 << 8) | (3 << 10) |
| | (0 << 3) | (1 << 2) | 2; |
| } |
| |
| void |
| grub_arm_enable_caches_mmu (void) |
| { |
| grub_uint32_t *table; |
| grub_uint32_t i; |
| grub_uint32_t border_crossing = 0; |
| grub_uint32_t *subtable; |
| struct sys_info *si = grub_uboot_get_sys_info (); |
| |
| if (!si || (si->mr_no == 0)) |
| { |
| grub_printf ("couldn't get memory map, not enabling caches"); |
| grub_errno = GRUB_ERR_NONE; |
| return; |
| } |
| |
| if (type == ARCH_UNKNOWN) |
| probe_caches (); |
| |
| for (i = 0; (signed) i < si->mr_no; i++) |
| { |
| if (si->mr[i].start & ((1 << 20) - 1)) |
| border_crossing++; |
| if ((si->mr[i].start + si->mr[i].size) & ((1 << 20) - 1)) |
| border_crossing++; |
| } |
| |
| grub_printf ("%d crossers\n", border_crossing); |
| |
| table = grub_memalign (1 << 14, (1 << 14) + (border_crossing << 10)); |
| if (!table) |
| { |
| grub_printf ("couldn't allocate place for MMU table, not enabling caches"); |
| grub_errno = GRUB_ERR_NONE; |
| return; |
| } |
| |
| subtable = table + (1 << 12); |
| /* Map all unknown as device. */ |
| for (i = 0; i < (1 << 12); i++) |
| table[i] = (i << 20) | (3 << 10) | (0 << 3) | (1 << 2) | 2; |
| /* |
| Device: TEX= 0, C=0, B=1 |
| normal: TEX= 0, C=1, B=1 |
| AP = 3 |
| IMP = 0 |
| Domain = 0 |
| */ |
| |
| for (i = 0; (signed) i < si->mr_no; i++) |
| { |
| if (si->mr[i].start & ((1 << 20) - 1)) |
| { |
| subdivide (table, subtable, si->mr[i].start); |
| subtable += (1 << 8); |
| } |
| if ((si->mr[i].start + si->mr[i].size) & ((1 << 20) - 1)) |
| { |
| subdivide (table, subtable, si->mr[i].start + si->mr[i].size); |
| subtable += (1 << 8); |
| } |
| } |
| |
| for (i = 0; (signed) i < si->mr_no; i++) |
| if ((si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_DRAM |
| || (si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_SRAM |
| || (si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_FLASH) |
| { |
| grub_uint32_t cur, end; |
| cur = si->mr[i].start; |
| end = si->mr[i].start + si->mr[i].size; |
| while (cur < end) |
| { |
| grub_uint32_t *st; |
| if ((table[cur >> 20] & 3) == 2) |
| { |
| cur = cur >> 20 << 20; |
| table[cur >> 20] = cur | (3 << 10) | (1 << 3) | (1 << 2) | 2; |
| cur += (1 << 20); |
| continue; |
| } |
| cur = cur >> 12 << 12; |
| st = (grub_uint32_t *) (table[cur >> 20] & ~0x3ff); |
| st[(cur >> 12) & 0xff] = cur | (3 << 4) | (3 << 6) |
| | (3 << 8) | (3 << 10) |
| | (1 << 3) | (1 << 2) | 2; |
| cur += (1 << 12); |
| } |
| } |
| |
| grub_printf ("MMU tables generated\n"); |
| if (is_v6_mmu) |
| grub_arm_clear_mmu_v6 (); |
| |
| grub_printf ("enabling MMU\n"); |
| grub_arm_enable_mmu (table); |
| grub_printf ("MMU enabled\n"); |
| } |
| |
| #endif |
| |
| void |
| grub_arch_sync_caches (void *address, grub_size_t len) |
| { |
| grub_addr_t start = (grub_addr_t) address; |
| grub_addr_t end = start + len; |
| |
| if (type == ARCH_UNKNOWN) |
| probe_caches (); |
| start = ALIGN_DOWN (start, grub_arch_cache_max_linesz); |
| end = ALIGN_UP (end, grub_arch_cache_max_linesz); |
| switch (type) |
| { |
| case ARCH_ARMV6: |
| grub_arm_clean_dcache_range_armv6 (start, end, grub_arch_cache_dlinesz); |
| grub_arm_invalidate_icache_range_armv6 (start, end, |
| grub_arch_cache_ilinesz); |
| break; |
| case ARCH_ARMV7: |
| grub_arm_clean_dcache_range_armv7 (start, end, grub_arch_cache_dlinesz); |
| grub_arm_invalidate_icache_range_armv7 (start, end, |
| grub_arch_cache_ilinesz); |
| break; |
| /* Nothing to do. */ |
| case ARCH_ARMV5_WRITE_THROUGH: |
| case ARCH_ARMV6_UNIFIED: |
| break; |
| /* Pacify GCC. */ |
| case ARCH_UNKNOWN: |
| break; |
| } |
| } |
| |
| void |
| grub_arch_sync_dma_caches (volatile void *address, grub_size_t len) |
| { |
| grub_addr_t start = (grub_addr_t) address; |
| grub_addr_t end = start + len; |
| |
| if (type == ARCH_UNKNOWN) |
| probe_caches (); |
| start = ALIGN_DOWN (start, grub_arch_cache_max_linesz); |
| end = ALIGN_UP (end, grub_arch_cache_max_linesz); |
| switch (type) |
| { |
| case ARCH_ARMV6: |
| grub_arm_clean_dcache_range_armv6 (start, end, grub_arch_cache_dlinesz); |
| grub_arm_invalidate_icache_range_armv6 (start, end, |
| grub_arch_cache_ilinesz); |
| break; |
| case ARCH_ARMV5_WRITE_THROUGH: |
| case ARCH_ARMV6_UNIFIED: |
| grub_arm_clean_dcache_range_armv6 (start, end, grub_arch_cache_dlinesz); |
| break; |
| case ARCH_ARMV7: |
| grub_arm_clean_dcache_range_poc_armv7 (start, end, grub_arch_cache_dlinesz); |
| grub_arm_invalidate_icache_range_armv7 (start, end, |
| grub_arch_cache_ilinesz); |
| break; |
| /* Pacify GCC. */ |
| case ARCH_UNKNOWN: |
| break; |
| } |
| } |
| |
| void |
| grub_arm_disable_caches_mmu (void) |
| { |
| if (type == ARCH_UNKNOWN) |
| probe_caches (); |
| switch (type) |
| { |
| case ARCH_ARMV5_WRITE_THROUGH: |
| case ARCH_ARMV6_UNIFIED: |
| case ARCH_ARMV6: |
| grub_arm_disable_caches_mmu_armv6 (); |
| break; |
| case ARCH_ARMV7: |
| grub_arm_disable_caches_mmu_armv7 (); |
| break; |
| /* Pacify GCC. */ |
| case ARCH_UNKNOWN: |
| break; |
| } |
| } |