// You may want to add memory protection to your kernel. However, // implementing proper memory protection involves creating your own page // tables, which is quite involved, and also likely to significantly // slow down your context switch. // However, RedBoot provides a memory map with the 32 MB of ram divided // into sections of one megabyte each. So, it's not too difficult to // have memory protection with a granularity of 1 MB (which is actually // quite coarse, but still useful). // As of the time of writing, this code is severely undertested. // Use at your own risk. It's probably a good idea to also read the // relevant hardware documentation to understand what is going on. // This code is intentionally written with no attempt to work // correctly with optimizations on. #define asm __asm__ unsigned int read_control_register() { unsigned int ret; asm("mrc p15, 0, %[x], c1, c0, 0":[x]"=r"(ret)); return ret; } void write_control_register(int value) { asm("mcr p15, 0, %[x], c1, c0, 0"::[x]"r"(value)); } unsigned int read_base_register() { unsigned int ret; asm("mrc p15, 0, %[x], c2, c0, 0":[x]"=r"(ret)); return ret; } void write_base_register(int value) { asm("mcr p15, 0, %[x], c2, c0, 0"::[x]"r"(value)); } unsigned int read_domain_register() { unsigned int ret; asm("mrc p15, 0, %[x], c3, c0, 0":[x]"=r"(ret)); return ret; } void write_domain_register(int value) { asm("mcr p15, 0, %[x], c3, c0, 0"::[x]"r"(value)); } // You need to invalidate the TLB for the permission changes to // take effect. You also need to clean the DCache if it is enabled // (which seems to negate all the speed benefits of enabling it in // the first place). You technically need to clean the ICache too, // but presumably you won't be modifying the permissions of your // code sections, since every task needs access to them anyway. void invalidate_tlb() { asm("mcr p15, 0, %[x], c8, c7, 0"::[x]"r"(0)); } void clean_dcache() { for(unsigned int seg = 0; seg < 8; seg++) { for(unsigned int index = 0; index < 64; index++) { unsigned int packed = (index << 26) + (seg << 5); asm("mcr p15, 0, %[x], c7, c14, 2"::[x]"r"(packed)); } } } struct saved_state { unsigned int control, base, domain; unsigned int table[32]; }; void enable_protection(struct saved_state *old) { // Let's be nice and save the old state so we can restore it later. old->control = read_control_register(); old->base = read_base_register(); old->domain = read_domain_register(); unsigned int *table = (unsigned int *)read_base_register(); for(int i=0; i<32; i++) old->table[i] = table[i]; // Print the registers. // The base register should be 0x4000. bwprintf("control: 0x%x\n", read_control_register()); bwprintf("base: 0x%x\n", read_base_register()); bwprintf("domain: 0x%x\n", read_domain_register()); // Turn permission checking on in the domain register. unsigned int domain = read_domain_register(); domain = (domain & ~3) | 1; write_domain_register(domain); } void disable_protection(struct saved_state *old) { // Restore the original state. write_control_register(old->control); write_base_register(old->base); write_domain_register(old->domain); unsigned int *table = (unsigned int *)old->base; for(int i=0; i<32; i++) table[i] = old->table[i]; invalidate_tlb(); clean_dcache(); } #define AP_RESTRICTED 0 #define AP_SUPERVISOR 1 #define AP_EVERYONE 2 // Read-only for User, full access for Supervisor. #define AP_WRITEABLE 3 void set_protection(unsigned int mb, unsigned int ap) { unsigned int *table = (unsigned int *)read_base_register(); if(mb < 32) table[mb] = (table[mb] & ~(3 << 10)) | (ap << 10); else bwprintf("error: out of bounds: %d MB\n", mb); // Make sure you call invalidate_tlb() and possibly clean_dcache() // after you have finished making permission changes. } // Example usage. int main() { // Enable memory protection. struct saved_state saved; enable_protection(&saved); // Create a pointer to something in the "sixth" MB. int *p = (int *)(6 << 20); *p = 0x1234; bwprintf("0x%x\n", *p); // Make the sixth MB inaccessible. set_protection(6, AP_RESTRICTED); // Note that we can still access it. // This is because we forgot to invalidate the TLB. *p = 0x5678; bwprintf("0x%x\n", *p); // Now invalidate the TLB. invalidate_tlb(); // This should cause a fault. *p = 0x9abc; bwprintf("0x%x\n", *p); // Restore the original memory controller state. // (But we won't make it this far.) disable_protection(&saved); return 0; }