From 2fd668c8ebfb5e26cfc6964f0014a7121964a5c2 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Sat, 9 Nov 2019 11:09:40 +0100 Subject: enable MMU; warning: DO NOT MERGE THIS COMMIT - ONCE I KNOW HOW TO GET THIS WORK I SHALL WRITE SOME USABLE ROUTINES AND STRUCTS INSTEAD OF THIS CRAPPY CODE HERE --- kernel.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 196 insertions(+), 23 deletions(-) (limited to 'kernel.c') diff --git a/kernel.c b/kernel.c index a8df084..dcfac11 100644 --- a/kernel.c +++ b/kernel.c @@ -1,6 +1,12 @@ #include "uart.h" #include "cpsr.h" +#include "strings.h" +#include "short_descriptor.h" +extern char __end; + +void enabling_code_from_the_net(void); + void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags) { // Declare as unused @@ -41,42 +47,209 @@ void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags) // processor mode asm("mrs %0, cpsr" : "=r" (CPSR) :: "memory"); - char *mode; + char *mode_name; switch(read_processor_mode()) { - case 0x10 : mode = "User (PL0)\r\n"; break; - case 0x11 : mode = "FIQ (PL1)\r\n"; break; - case 0x12 : mode = "IRQ (PL1)\r\n"; break; - case 0x13 : mode = "Supervisor (PL1)\r\n"; break; - case 0x16 : mode = "Monitor (PL1)\r\n"; break; - case 0x17 : mode = "Abort (PL1)\r\n"; break; - case 0x1a : mode = "Hyp (PL2)\r\n"; break; - case 0x1b : mode = "Undefined (PL1)\r\n"; break; - case 0x1f : mode = "System (PL1)\r\n"; break; - default : mode = "Unknown mode\r\n"; break; + case MODE_USER : mode_name = "User (PL0)\r\n"; break; + case MODE_FIQ : mode_name = "FIQ (PL1)\r\n"; break; + case MODE_IRQ : mode_name = "IRQ (PL1)\r\n"; break; + case MODE_SUPERVISOR : mode_name = "Supervisor (PL1)\r\n"; break; + case MODE_MONITOR : mode_name = "Monitor (PL1)\r\n"; break; + case MODE_ABORT : mode_name = "Abort (PL1)\r\n"; break; + case MODE_HYPERVISOR : mode_name = "Hyp (PL2)\r\n"; break; + case MODE_UNDEFINED : mode_name = "Undefined (PL1)\r\n"; break; + case MODE_SYSTEM : mode_name = "System (PL1)\r\n"; break; + default : mode_name = "Unknown mode\r\n"; break; } - uart_puts(mode); + uart_puts("current mode: "); + uart_puts(mode_name); + uart_puts("setting mode to system...\r\n"); + uart_puts("current mode: "); set_system_mode(); switch(read_processor_mode()) { - case 0x10 : mode = "User (PL0)\r\n"; break; - case 0x11 : mode = "FIQ (PL1)\r\n"; break; - case 0x12 : mode = "IRQ (PL1)\r\n"; break; - case 0x13 : mode = "Supervisor (PL1)\r\n"; break; - case 0x16 : mode = "Monitor (PL1)\r\n"; break; - case 0x17 : mode = "Abort (PL1)\r\n"; break; - case 0x1a : mode = "Hyp (PL2)\r\n"; break; - case 0x1b : mode = "Undefined (PL1)\r\n"; break; - case 0x1f : mode = "System (PL1)\r\n"; break; - default : mode = "Unknown mode\r\n"; break; + case MODE_USER : mode_name = "User (PL0)\r\n"; break; + case MODE_FIQ : mode_name = "FIQ (PL1)\r\n"; break; + case MODE_IRQ : mode_name = "IRQ (PL1)\r\n"; break; + case MODE_SUPERVISOR : mode_name = "Supervisor (PL1)\r\n"; break; + case MODE_MONITOR : mode_name = "Monitor (PL1)\r\n"; break; + case MODE_ABORT : mode_name = "Abort (PL1)\r\n"; break; + case MODE_HYPERVISOR : mode_name = "Hyp (PL2)\r\n"; break; + case MODE_UNDEFINED : mode_name = "Undefined (PL1)\r\n"; break; + case MODE_SYSTEM : mode_name = "System (PL1)\r\n"; break; + default : mode_name = "Unknown mode\r\n"; break; } - uart_puts(mode); + uart_puts(mode_name); + + char bits[33]; + + // compute translation table address for TTBR0 + // translation table shall start at first 2^14-bytes aligned + // address after the kernel image + // uint32_t kernel_end = (uint32_t) &__end; + uint32_t translation_table_base = 0x4000; // for now try 0x4000 + // ((kernel_end - 1) & ~((uint32_t) 0x3fff)) + (uint32_t) 0x4000; + + uint32_to_bits(translation_table_base, bits); + uart_puts("\n\rbinary representation of chosen" + " lvl1 translation table address: "); + uart_puts(bits); + + uart_puts("\n\rpreparing translation table\n\r"); + uint32_t *translation_table = (uint32_t*) translation_table_base; + + // flat map all memory + translation_table[0] = sd_lvl1_make_section(0x0); + // make all other entries in translation table invalid :) + for (uint32_t i = 0; i < 4096; i++) + translation_table[i] = sd_lvl1_make_section(i << 20); + + uint32_to_bits(translation_table[0], bits); + uart_puts("translation_table_entry 0: \n\r"); + uart_puts(bits); + + // uart_puts("\n\renabling the MMU\n\r"); + // asm volatile("" ::: "memory"); + // enabling_code_from_the_net(); + // goto skip; + + + // meddle with domain settings + uint32_t DACR; + asm("mrc p15, 0, %0, c3, c0, 0" : "=r" (DACR)); + uint32_to_bits(DACR, bits); + + uart_puts("initial DACR contents: "); + uart_puts(bits); + + uart_puts("\n\rsetting domain0 to client access" + " and blocking other domains\n\r"); + + DACR = 1; + asm("mcr p15, 0, %0, c3, c0, 0" :: "r" (DACR)); + + asm("mrc p15, 0, %0, c3, c0, 0" : "=r" (DACR)); + uint32_to_bits(DACR, bits); + + uart_puts("new DACR contents: "); + uart_puts(bits); + + + // meddle with SCTLR, which determines how some bits in + // table descriptors work and also controls caches + // we don't want to use access flag, so we set AFE to 0 + // we don't want TEX remap, so we set TRE to 0 + // we also disable data and instruction caches and the MMU + uint32_t SCTLR; + asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR)); + uint32_to_bits(SCTLR, bits); + + uart_puts("\n\rSCTLR contents: "); + uart_puts(bits); + + uart_puts("\n\rsetting C, I, AFE and TRE to 0 in SCTLR\n\r"); + + SCTLR &= ~((((uint32_t) 1) << 29) | + (((uint32_t) 1) << 28) | + (((uint32_t) 1) << 12) | + (((uint32_t) 1) << 2)); // set AFE and TRE to 0 + asm("mcr p15, 0, %0, c1, c0, 0\n\r" + "isb" :: "r" (SCTLR)); + + asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR)); + uint32_to_bits(SCTLR, bits); + + uart_puts("new SCTLR contents: "); + uart_puts(bits); + + // invalidate instruction cache + uart_puts("\n\rinvalidating instruction cache\n\r"); + asm("mcr p15, 0, r0, c7, c5, 0\n\r" // r0 gets ignored + "isb" ::: "memory"); + + // invalidate branch-prediction + uart_puts("\n\rinvalidating branch-prediction\n\r"); + asm("mcr p15, 0, r0, c7, c5, 6\n\r" + "isb" ::: "memory"); + + // invalidate instruction cache + uart_puts("\n\rinvalidating entire main TLB\n\r"); + asm("mcr p15, 0, %0, c8, c7, 0\n\r" + "isb" :: "r" (0) : "memory"); + // now see what's in TTBCR + // set it use TTBR0 exclusively + uint32_t TTBCR; + asm("mrc p15, 0, %0, c2, c0, 2" : "=r" (TTBCR)); + uint32_to_bits(TTBCR, bits); + + uart_puts("\n\rTTBCR contents: "); + uart_puts(bits); + + uart_puts("\n\rSetting TTBCR.N to 0, so that" + " TTBR0 is used everywhere\n\r"); + + TTBCR &= ~((uint32_t) 0x7); // set N to 0 + asm("mcr p15, 0, %0, c2, c0, 2" :: "r" (TTBCR)); + + asm("mrc p15, 0, %0, c2, c0, 2" : "=r" (TTBCR)); + uint32_to_bits(TTBCR, bits); + + uart_puts("new TTBCR contents: "); + uart_puts(bits); + + + // Now do stuff with TTBR0 + uint32_t TTBR0; + asm("mrc p15, 0, %0, c2, c0, 0" : "=r" (TTBR0)); + uint32_to_bits(TTBR0, bits); + + uart_puts("\n\rTTBR0 contents: "); + uart_puts(bits); + + uart_puts("\n\r"); + + TTBR0 = ((TTBR0 << 18) >> 18) | translation_table_base; + TTBR0 &= ~((uint32_t) 0x1a); // set RGN and S in TTBR0 to 0 + asm("mcr p15, 0, %0, c2, c0, 0" :: "r" (TTBR0)); + + asm("mrc p15, 0, %0, c2, c0, 0" : "=r" (TTBR0)); + uint32_to_bits(TTBR0, bits); + + uart_puts("new TTBR0 contents: "); + uart_puts(bits); + + + // enable MMU + asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR)); + uint32_to_bits(SCTLR, bits); + + uart_puts("\n\rSCTLR contents before MMU enabling: "); + uart_puts(bits); + + uart_puts("\n\renabling the MMU\n\r"); + + // set M to 0 + SCTLR |= (uint32_t) 1; + + asm("mcr p15, 0, %0, c1, c0, 0" :: "r" (SCTLR)); + + asm("mrc p15, 0, %0, c1, c0, 0\r\n" + "isb" : "=r" (SCTLR)); + + uint32_to_bits(SCTLR, bits); + + uart_puts("SCTLR contents after MMU enabling: "); + uart_puts(bits); + + skip: + uart_puts("skip here\n\r"); + while (1) uart_putc(uart_getc()); } -- cgit v1.2.3 From eb286810ae841301b7647f3350b29a70d4a479b8 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Sat, 9 Nov 2019 15:36:45 +0100 Subject: enable the MMU - a cleaner way (describing registers with bitfield structs and unions) --- cp_regs.h | 111 ++++++++++++++++++++ kernel.c | 218 ++++++++++++++-------------------------- translation_table_descriptors.h | 99 ++++++++++++++++++ 3 files changed, 288 insertions(+), 140 deletions(-) create mode 100644 cp_regs.h create mode 100644 translation_table_descriptors.h (limited to 'kernel.c') diff --git a/cp_regs.h b/cp_regs.h new file mode 100644 index 0000000..98a8bb8 --- /dev/null +++ b/cp_regs.h @@ -0,0 +1,111 @@ +#include + + +// SCTLR - System Control Register + +// Wandering why I didn't typedef this struct with fields? +// That's because +typedef union +{ + uint32_t raw; + struct + { + uint32_t M : 1; // bit 0 + uint32_t A : 1; // bit 1 + uint32_t C : 1; // bit 2 + uint32_t Bits_4_3 : 2; // bits 3:4 + uint32_t CP15BEN : 1; // bit 5 + uint32_t Bit_6 : 1; // bit 6 + uint32_t B : 1; // bit 7 + uint32_t Bits_9_8 : 2; // bits 9:8 + uint32_t SW : 1; // bit 10 + uint32_t Z : 1; // bit 11 + uint32_t I : 1; // bit 12 + uint32_t V : 1; // bit 13 + uint32_t RR : 1; // bit 14 + uint32_t Bit_15 : 1; // bit 15 + uint32_t Bit_16 : 1; // bit 16 + uint32_t HA : 1; // bit 17 + uint32_t Bit_18 : 1; // bit 18 + uint32_t WXN : 1; // bit 19 + uint32_t UWXN : 1; // bit 20 + uint32_t FI : 1; // bit 21 + uint32_t U : 1; // bit 22 + uint32_t Bit_23 : 1; // bit 23 + uint32_t VE : 1; // bit 24 + uint32_t EE : 1; // bit 25 + uint32_t Bit_26 : 1; // bit 26 + uint32_t NMFI : 1; // bit 27 + uint32_t TRE : 1; // bit 28 + uint32_t AFE : 1; // bit 29 + uint32_t TE : 1; // bit 30 + uint32_t Bit_31 : 1; // bit 31 + } fields; +} SCTLR_t; + +// DACR - Domain Access Control Register +// DACR holds 16 pairs of bits; each pair represents access +// permissions to a respective memory domain. There's no point +// declaring a union for this. +typedef uint32_t DACR_t; + +inline static uint8_t domain_permissions(DACR_t DACR_contents, + int domain) +{ + return (DACR_contents << (30 - 2 * domain)) >> 30; +} + +inline static DACR_t set_domain_permissions(DACR_t DACR_contents, + int domain, + uint8_t permissions) +{ + uint32_t clear_domain_permissions_mask = ~(0b11 << (2 * domain)); + uint32_t new_domain_permissions_mask = + ((uint32_t) permissions) << (2 * domain); + + return (DACR_contents & clear_domain_permissions_mask) + | new_domain_permissions_mask; +} + +#define DOMAIN_NO_ACCESS 0b00 +#define DOMAIN_CLIENT_ACCESS 0b01 +#define DOMAIN_RESERVED 0b10 +#define DOMAIN_MANAGER_ACCESS 0b11 + +// TTBR - Translation Table Base Register (there're 2 of them with +// (almost) the same structure) + +// A field in TTBCR determines how long the address field is in TTBR0, +// but here we'll ignore this and just assume the greatest possible +// length of this field (18 bits). In TTBR1 it's always 18 bits. +typedef union +{ + uint32_t raw; + struct + { + + uint32_t C : 1; // bit 0 + uint32_t S : 1; // bit 1 + uint32_t IMP : 1; // bit 2 + uint32_t RGN : 2; // bits 4:3 + uint32_t NOS : 1; // bit 5 + uint32_t IRGN_0 : 1; // bit 6 + uint32_t Bits_13_6 : 7; // bits 13:7 + uint32_t Bits_31_14 : 18; // bits 31:14 + // with multiprocessing extensions the cacheable bit becomes + // upper IRGN bit +#define IRGN_1 C + + // i'm not sure 'interprocess region bits' is the right name, + // I'm just guessing (by analogy to RGN -> region bits) +#define CACHEABLE_BIT C +#define INTERPROCESS_REGION_BITS_1 IRGN_1 +#define SHAREABLE_BIT S +#define IMPLEMENTATION_DEFINED_BIT IMP +#define REGION_BITS_1_0 RGN +#define INTERPROCESS_REGION_BITS_0 IRGN_0 +#define NON_OUTER_SHAREABLE_BIT NOS +#define TRANSLATION_TABLE_BASE_ADDRESS Bits_31_14 + } fields; +} TTBR_t; + diff --git a/kernel.c b/kernel.c index dcfac11..8d2fdef 100644 --- a/kernel.c +++ b/kernel.c @@ -1,7 +1,8 @@ #include "uart.h" #include "cpsr.h" #include "strings.h" -#include "short_descriptor.h" +#include "translation_table_descriptors.h" +#include "cp_regs.h" extern char __end; @@ -66,190 +67,127 @@ void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags) uart_puts("current mode: "); uart_puts(mode_name); - uart_puts("setting mode to system...\r\n"); - uart_puts("current mode: "); + uart_puts("setting mode to system (PL1)...\r\n"); set_system_mode(); - switch(read_processor_mode()) - { - case MODE_USER : mode_name = "User (PL0)\r\n"; break; - case MODE_FIQ : mode_name = "FIQ (PL1)\r\n"; break; - case MODE_IRQ : mode_name = "IRQ (PL1)\r\n"; break; - case MODE_SUPERVISOR : mode_name = "Supervisor (PL1)\r\n"; break; - case MODE_MONITOR : mode_name = "Monitor (PL1)\r\n"; break; - case MODE_ABORT : mode_name = "Abort (PL1)\r\n"; break; - case MODE_HYPERVISOR : mode_name = "Hyp (PL2)\r\n"; break; - case MODE_UNDEFINED : mode_name = "Undefined (PL1)\r\n"; break; - case MODE_SYSTEM : mode_name = "System (PL1)\r\n"; break; - default : mode_name = "Unknown mode\r\n"; break; - } - - uart_puts(mode_name); - - char bits[33]; - - // compute translation table address for TTBR0 + char bits[33]; // for printing uint32_t bit values + + // compute translation table base address // translation table shall start at first 2^14-bytes aligned // address after the kernel image - // uint32_t kernel_end = (uint32_t) &__end; - uint32_t translation_table_base = 0x4000; // for now try 0x4000 - // ((kernel_end - 1) & ~((uint32_t) 0x3fff)) + (uint32_t) 0x4000; + uint32_t kernel_end = (uint32_t) &__end; + uint32_t translation_table_base = + ((kernel_end - 1) & ~((uint32_t) 0x3fff)) + (uint32_t) 0x4000; uint32_to_bits(translation_table_base, bits); - uart_puts("\n\rbinary representation of chosen" + uart_puts("binary representation of chosen" " lvl1 translation table address: "); - uart_puts(bits); + uart_puts(bits); uart_puts("\n\r"); - uart_puts("\n\rpreparing translation table\n\r"); - uint32_t *translation_table = (uint32_t*) translation_table_base; - // flat map all memory - translation_table[0] = sd_lvl1_make_section(0x0); - // make all other entries in translation table invalid :) + uart_puts("preparing translation table\n\r"); + short_descriptor_t *translation_table = + (short_descriptor_t*) translation_table_base; + for (uint32_t i = 0; i < 4096; i++) - translation_table[i] = sd_lvl1_make_section(i << 20); + translation_table[i].section_fields = + (short_section_descriptor_t) { + .SECTION_BASE_ADDRESS_31_20 = i, + .SECTION_OR_SUPERSECTION_BIT = DESCRIBES_SECTION, + .ACCESS_PERMISSIONS_2 = AP_2_0_MODEL_RW_PL1 >> 2, + .ACCESS_PERMISSIONS_1_0 = AP_2_0_MODEL_RW_PL1 & 0b011, + .DESCRIPTOR_TYPE_2 = + SHORT_DESCRIPTOR_SECTION_OR_SUPERSECTION >> 1, + // rest of fields are 0s + }; - uint32_to_bits(translation_table[0], bits); - uart_puts("translation_table_entry 0: \n\r"); - uart_puts(bits); - - // uart_puts("\n\renabling the MMU\n\r"); - // asm volatile("" ::: "memory"); - // enabling_code_from_the_net(); - // goto skip; - - // meddle with domain settings - uint32_t DACR; - asm("mrc p15, 0, %0, c3, c0, 0" : "=r" (DACR)); - uint32_to_bits(DACR, bits); - - uart_puts("initial DACR contents: "); - uart_puts(bits); - - uart_puts("\n\rsetting domain0 to client access" + uart_puts("setting domain0 to client access" " and blocking other domains\n\r"); - DACR = 1; - asm("mcr p15, 0, %0, c3, c0, 0" :: "r" (DACR)); - - asm("mrc p15, 0, %0, c3, c0, 0" : "=r" (DACR)); - uint32_to_bits(DACR, bits); - - uart_puts("new DACR contents: "); - uart_puts(bits); + DACR_t DACR = 0; + DACR = set_domain_permissions(DACR, 0, DOMAIN_CLIENT_ACCESS); + for (int i = 1; i < 16; i++) + DACR = set_domain_permissions(DACR, i, DOMAIN_NO_ACCESS); + // the above should do the same as this: + // DACR = 1; + + asm("mcr p15, 0, %0, c3, c0, 0" :: "r" (DACR)); // meddle with SCTLR, which determines how some bits in // table descriptors work and also controls caches // we don't want to use access flag, so we set AFE to 0 // we don't want TEX remap, so we set TRE to 0 // we also disable data and instruction caches and the MMU - uint32_t SCTLR; - asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR)); - uint32_to_bits(SCTLR, bits); - - uart_puts("\n\rSCTLR contents: "); - uart_puts(bits); - uart_puts("\n\rsetting C, I, AFE and TRE to 0 in SCTLR\n\r"); + // some of this is redundant (i.e. MMU should already be disabled) + uart_puts("setting C, I, AFE and TRE to 0 in SCTLR\n\r"); - SCTLR &= ~((((uint32_t) 1) << 29) | - (((uint32_t) 1) << 28) | - (((uint32_t) 1) << 12) | - (((uint32_t) 1) << 2)); // set AFE and TRE to 0 - asm("mcr p15, 0, %0, c1, c0, 0\n\r" - "isb" :: "r" (SCTLR)); + SCTLR_t SCTLR; + asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR.raw)); - asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR)); - uint32_to_bits(SCTLR, bits); - - uart_puts("new SCTLR contents: "); - uart_puts(bits); + SCTLR.fields.M = 0; // disable MMU + SCTLR.fields.C = 0; // disable data cache + SCTLR.fields.I = 0; // disable instruction cache + SCTLR.fields.TRE = 0; // disable TEX remap + SCTLR.fields.AFE = 0; // disable access flag usage + asm("mcr p15, 0, %0, c1, c0, 0\n\r" + "isb" :: "r" (SCTLR.raw) : "memory"); + // TODO: move invalidation instructions to some header as inlines + + uart_puts("invalidating instruction cache, branch prediction," + " and entire main TLB\n\r"); + // invalidate instruction cache - uart_puts("\n\rinvalidating instruction cache\n\r"); asm("mcr p15, 0, r0, c7, c5, 0\n\r" // r0 gets ignored "isb" ::: "memory"); // invalidate branch-prediction - uart_puts("\n\rinvalidating branch-prediction\n\r"); - asm("mcr p15, 0, r0, c7, c5, 6\n\r" + asm("mcr p15, 0, r0, c7, c5, 6\n\r" // r0 - same as above "isb" ::: "memory"); - // invalidate instruction cache - uart_puts("\n\rinvalidating entire main TLB\n\r"); + // invalidate main Translation Lookup Buffer asm("mcr p15, 0, %0, c8, c7, 0\n\r" "isb" :: "r" (0) : "memory"); - // now see what's in TTBCR - // set it use TTBR0 exclusively - uint32_t TTBCR; - asm("mrc p15, 0, %0, c2, c0, 2" : "=r" (TTBCR)); - uint32_to_bits(TTBCR, bits); - - uart_puts("\n\rTTBCR contents: "); - uart_puts(bits); - - uart_puts("\n\rSetting TTBCR.N to 0, so that" + // now set TTBCR to use TTBR0 exclusively + uart_puts("Setting TTBCR.N to 0, so that" " TTBR0 is used everywhere\n\r"); - - TTBCR &= ~((uint32_t) 0x7); // set N to 0 + + uint32_t TTBCR = 0; asm("mcr p15, 0, %0, c2, c0, 2" :: "r" (TTBCR)); - - asm("mrc p15, 0, %0, c2, c0, 2" : "=r" (TTBCR)); - uint32_to_bits(TTBCR, bits); - - uart_puts("new TTBCR contents: "); - uart_puts(bits); - // Now do stuff with TTBR0 - uint32_t TTBR0; - asm("mrc p15, 0, %0, c2, c0, 0" : "=r" (TTBR0)); - uint32_to_bits(TTBR0, bits); - - uart_puts("\n\rTTBR0 contents: "); - uart_puts(bits); - - uart_puts("\n\r"); - - TTBR0 = ((TTBR0 << 18) >> 18) | translation_table_base; - TTBR0 &= ~((uint32_t) 0x1a); // set RGN and S in TTBR0 to 0 - asm("mcr p15, 0, %0, c2, c0, 0" :: "r" (TTBR0)); - - asm("mrc p15, 0, %0, c2, c0, 0" : "=r" (TTBR0)); - uint32_to_bits(TTBR0, bits); - - uart_puts("new TTBR0 contents: "); - uart_puts(bits); + TTBR_t TTBR0; + TTBR0.raw = 0; + TTBR0.fields.TRANSLATION_TABLE_BASE_ADDRESS = + translation_table_base >> 14; + // rest of TTBR0 remains 0s + asm("mcr p15, 0, %0, c2, c0, 0" :: "r" (TTBR0.raw)); // enable MMU - asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR)); - uint32_to_bits(SCTLR, bits); - - uart_puts("\n\rSCTLR contents before MMU enabling: "); - uart_puts(bits); + uart_puts("enabling the MMU\n\r"); - uart_puts("\n\renabling the MMU\n\r"); - - // set M to 0 - SCTLR |= (uint32_t) 1; - - asm("mcr p15, 0, %0, c1, c0, 0" :: "r" (SCTLR)); + // redundant - we already have SCTLR contents in the variable + // asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR.raw)); - asm("mrc p15, 0, %0, c1, c0, 0\r\n" - "isb" : "=r" (SCTLR)); + SCTLR.fields.M = 1; - uint32_to_bits(SCTLR, bits); - - uart_puts("SCTLR contents after MMU enabling: "); - uart_puts(bits); + asm("mcr p15, 0, %0, c1, c0, 0\n\r" + "isb" :: "r" (SCTLR.raw) : "memory"); - skip: - uart_puts("skip here\n\r"); - while (1) - uart_putc(uart_getc()); + { + char c; + switch(c = uart_getc()) + { + case '\r': + uart_putc('\n'); + default: + uart_putc(c); + } + } } diff --git a/translation_table_descriptors.h b/translation_table_descriptors.h new file mode 100644 index 0000000..35062f8 --- /dev/null +++ b/translation_table_descriptors.h @@ -0,0 +1,99 @@ +#include + +// ARM lets you choose between 32-bit abd 64-bit translation table +// descriptors (called short and long descriptors respectively). +// The format of the descriptor differs depending on what it describes +// (section, supersection, a page table, etc...) and table of which +// level of lookup it belongs to. + +// Even in case of descriptor of a specified type (e.g. short-format +// section descriptor), a given field inside it may have different +// meanings depending on settings in coprocessor registers... (yeah, ARM +// looks a bit messy... all for backward compatibility, i guess) + +typedef struct +{ + uint32_t PXN : 1; // bit 0 + uint32_t Bit_1 : 1; // bit 1 + uint32_t B : 1; // bit 2 + uint32_t C : 1; // bit 3 + uint32_t XN : 1; // bit 4 + uint32_t Domain_3_0 : 4; // bits 8:5 + uint32_t Bit_9 : 1; // bit 9 + uint32_t AP_1_0 : 2; // bit 11:10 + uint32_t TEX_2_0 : 3; // bits 14:12 + uint32_t AP_2 : 1; // bit 15 + uint32_t S : 1; // bit 16 + uint32_t nG : 1; // bit 17 + uint32_t Bit_18 : 1; // bit 18 + uint32_t NS : 1; // bit 19 + uint32_t PA_31_20 : 12; // bits 31:20 +#define PRIVILEGED_EXECUTE_NEVER_BIT PXN +#define DESCRIPTOR_TYPE_2 Bit_1 +#define BUFFERABLE_BIT B +#define CACHEABLE_BIT C +#define EXECUTE_NEVER_BIT XN +#define DOMAIN_3_0 Domain_3_0 +#define IMPLEMENTATION_DEFINED_BIT Bit_9 +#define ACCESS_PERMISSIONS_1_0 AP_1_0 +#define TYPE_EXTENSION_2_0 TEX_2_0 +#define ACCESS_PERMISSIONS_2 AP_2 +#define SHAREABLE_BIT S +#define NON_GLOBAL_BIT nG +#define SECTION_OR_SUPERSECTION_BIT Bit_18 +#define NON_SECURE_BIT NS +#define SECTION_BASE_ADDRESS_31_20 PA_31_20 +} short_section_descriptor_t; + + +// How AP[2:0] is used depends on settings in SCTLR.AFE + +// Meaning of #define'd names below: +// RW - read-write +// RO - read-only +// PL1 - a given permission applies to privilege level PL1 +// PL2 - a given permission applies to privilege level PL2 +// ALL - a given permission applies to both privilege levels +// If only a permission for one privilege level is given in the name, +// it means the other one has no access. + +// When SCTLR.AFE is 0 (access flag not used) and short-format +// descritor table is used, the following access permission control +// schema for AP[2:0] is used: +#define AP_2_0_MODEL_NO_ACCESS 0b000 +#define AP_2_0_MODEL_RW_PL1 0b001 +#define AP_2_0_MODEL_RW_PL1_RO_PL0 0b010 +#define AP_2_0_MODEL_RW_ALL 0b011 +#define AP_2_0_MODEL_RESERVED 0b100 +#define AP_2_0_MODEL_RO_PL1 0b101 +#define AP_2_0_MODEL_RO_ALL_DEPRECATED 0b110 // use 0b111 instead +#define AP_2_0_MODEL_RO_ALL 0b111 // reserved in VMSAv6 +// TODO: the #define's of RO_ALL and reserved could be done +// conditionally depending on the VMSA version available (either give +// the programmer #including this the possibility to #define their +// VMSA version or assume the VMSA version respective to the ARM +// version we're compiling against) + +// Values for bit18, that determines whether a descriptor describes +// section or supersection: +#define DESCRIBES_SECTION 0b0 +#define DESCRIBES_SUPERSECTION 0b1 + +typedef union +{ + uint32_t raw; + uint8_t descriptor_type; + + short_section_descriptor_t section_fields; + // more to come here (e.g. short_supersection_descriptor_t) +} short_descriptor_t; + +// possible values of descriptor_type field: +#define SHORT_DESCRIPTOR_INVALID 0b00 +#define SHORT_DESCRIPTOR_PAGE_TABLE 0b01 +#define SHORT_DESCRIPTOR_SECTION_OR_SUPERSECTION 0b10 +#define SHORT_DESCRIPTOR_SECTION_OR_SUPERSECTION_PXN 0b11 +// on an implementation that does not support the PXN attribute +// 0b11 should not be used +#define SHORT_DESCRIPTOR_RESERVED 0b11 + -- cgit v1.2.3