#include #include #include #ifdef GRUB_MACHINE_UBOOT #include #include #include #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; default: /* * The CTR register is pretty much unchanged from v7 onwards, * and is guaranteed to be backward compatible (the IDC/DIC bits * allow certain CMOs to be elided, but performing them is never * wrong), hence handling it like its AArch64 equivalent. */ grub_arch_cache_dlinesz = 4 << ((cache_type >> 16) & 0xf); grub_arch_cache_ilinesz = 4 << (cache_type & 0xf); type = ARCH_ARMV7; } 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; } }