// Assumptions: // cortex-a8, public world // memory type remapping disabled //-------------- Memory types and cacheability -------------------------------// // Cortex-A8 treats all shared memory as non-cacheable. The only difference // between shared and non-shared non-cacheable normal memory is that exclusive // operations are exported on the AXI bus for shared memory. TI SoCs however // don't support these and silently treat them as normal load/store, which means // that the semantics of 'shared' memory simply aren't available. // // Note also that on the Cortex-A8, L1 cache does not support write-allocate. enum { // device type (asynchronous / "posted" writes) type_device = 1 << 2 | 0 << 12 | 0 << 14, // device type with synchronous ("non-posted") writes type_sync = 0 << 2 | 0 << 12 | 0 << 14, // ARM calls this "strongly ordered", although in ARMv8 they changed // terminology to "Device-nGnRnE" ... not sure that's an improvement. }; // normal memory type with specified cache policy enum CachePolicy { nc = 0, // non-cacheable wt = 2, // write-through, read-allocate, no write-allocate wb = 3, // write-back, read-allocate, no write-allocate wa = 1, // write-back, read-allocate, write-allocate }; static inline u32 type_normal( CachePolicy l1, CachePolicy l2 ) { bool shared = false; return l1 << 2 | l2 << 12 | 1 << 14 | shared << 16; } //-------------- Permissions -------------------------------------------------// enum Perms { sect_access = 1 << 10, sect_user = 1 << 11, sect_readonly = 1 << 15, sect_noexec = 1 << 4, // section permissions: ______ = 0, rwx___ = sect_access, rwxrwx = sect_access | sect_user, r_x___ = rwx___ | sect_readonly, r_xr_x = rwxrwx | sect_readonly, r_____ = r_x___ | sect_noexec, rw____ = rwx___ | sect_noexec, r__r__ = r_xr_x | sect_noexec, rw_rw_ = rwxrwx | sect_noexec, // these are only valid if not using simplified permissions model: rwxr_x = sect_user, rw_r__ = rwxr_x | sect_noexec, }; //-------------- Section mapping primitives ----------------------------------// // // Beware that these routines do not perform any sanity-checking at all. // // Beware also that these routines are just meant to initialize the section // table *before* enabling the MMU. Making changes while the MMU is enabled is // of course possible, but the procedure is more involved and delicate. enum { sect_fault = 0, // translation fault sect_pgtbl = 1, // translation via page table sect_map = 2, // section translation // section translation descriptor flags sect_super = 1 << 18, // supersection (16 MB) sect_proc = 1 << 17, // process-specific (non-global) }; static u32 section_table[ 0x1000 ] alignas( 0x4000 ); // create 1 MB flat mapping static void map_section( u32 desc ) { desc |= sect_map; section_table[ desc >> 20 ] = desc; } // create 16 MB flat mapping static void map_supersection( u32 desc ) { desc |= sect_map | sect_super; for( uint i = 0; i < 16; i++ ) section_table[ ( desc >> 20 ) + i ] = desc; } // get L2 cache policy (assumes the address is mapped as normal memory) static CachePolicy get_L2_policy( u32 vaddr ) { return (CachePolicy)( section_table[ vaddr >> 20 ] >> 12 & 3 ); } //-------------- Cortex-A8 system control register ---------------------------// // // Convenience class for accessing it. I omitted a few bits I don't care about. struct A8_syscontrol { bool mmu_enabled : 1; bool alignment_checking : 1; bool caches_enabled : 1; // data and unified caches u32 : 8; bool branch_prediction : 1; // already enabled by bootrom bool icache_enabled : 1; u32 : 15; bool mem_type_remap : 1; bool simplified_perms : 1; // aka 'access flag enabled' bool exceptions_thumb : 1; u32 : 1; u32 &raw() { return *(u32 *) this; } void load() { asm( "mrc\tp15, 0, %0, c1, c0, 0" : "=r"(raw()) ); } void save() { asm( "mcr\tp15, 0, %0, c1, c0, 0" :: "r"(raw()) ); } A8_syscontrol() { load(); } }; //-------------- Configure and enable MMU and caches -------------------------// void init_mmu() { // peripherals for( u32 addr = 0x44000000; addr != 0x57000000; addr += 0x01000000 ) map_supersection( addr | rw_rw_ | type_device ); // external RAM for( u32 addr = 0x80000000; addr != 0xC0000000; addr += 0x01000000 ) map_supersection( addr | rwxrwx | type_normal( wb, wa ) ); // local memories map_section( 0x40000000 | r_xr_x | type_normal( wt, nc ) ); // mpuss rom map_section( 0x40200000 | rwxrwx | type_normal( wt, wa ) ); // mpuss ram map_section( 0x40300000 | rwxrwx | type_normal( wt, wa ) ); // ocmc ram // ensure writes complete before enabling MMU asm( "dsb st" ::: "memory" ); // configure the section table address and cache policy into the MMU u32 tr_base = (u32) section_table; tr_base |= get_L2_policy( tr_base ) << 3; asm( "mcr\tp15, 0, %0, c2, c0, 0" :: "r" (tr_base) ); // configure domain access asm( "mcr\tp15, 0, %0, c3, c0, 0" :: "r" (1) ); // enable MMU and caches A8_syscontrol ctl; ctl.mmu_enabled = true; ctl.caches_enabled = true; ctl.icache_enabled = true; ctl.save(); }