diff options
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | demo_functionality.c | 115 | ||||
-rw-r--r-- | demo_functionality.h | 10 | ||||
-rw-r--r-- | kernel.c | 227 | ||||
-rw-r--r-- | memory.h | 35 | ||||
-rw-r--r-- | paging.c | 114 | ||||
-rw-r--r-- | paging.h | 6 |
7 files changed, 298 insertions, 217 deletions
@@ -7,6 +7,12 @@ all : kernel7.img kernel.o : kernel.c arm-none-eabi-gcc $(CFLAGS) -c $^ -o $@ +paging.o : paging.c + arm-none-eabi-gcc $(CFLAGS) -c $^ -o $@ + +demo_functionality.o : demo_functionality.c + arm-none-eabi-gcc $(CFLAGS) -c $^ -o $@ + PL0_test.o : PL0_test.c arm-none-eabi-gcc $(CFLAGS) -c $^ -o $@ @@ -25,7 +31,7 @@ uart.o : uart.c boot.o : boot.S arm-none-eabi-as -mcpu=cortex-a7 $^ -o $@ -kernel.elf : boot.o kernel.o uart.o PL0_test.o PL_0_test_embeddable.o +kernel.elf : boot.o kernel.o uart.o PL_0_test_embeddable.o demo_functionality.o paging.o arm-none-eabi-gcc -T linker.ld -o $@ $(ELFFLAGS) $^ kernel7.img : kernel.elf diff --git a/demo_functionality.c b/demo_functionality.c new file mode 100644 index 0000000..53035b7 --- /dev/null +++ b/demo_functionality.c @@ -0,0 +1,115 @@ +#include "uart.h" +#include "psr.h" +#include "memory.h" +#include "translation_table_descriptors.h" + +void demo_paging_support(void) +{ + uint32_t ID_MMFR0; + // get contents of coprocessor register to check for paging support + asm("mrc p15, 0, %0, c0, c1, 4" : "=r" (ID_MMFR0)); + + char *paging; + + switch(ID_MMFR0 & 0xf) /* lowest 4 bits indicate VMSA support */ + { + case 0 : paging = "no paging\n\r"; break; + case 1 : paging = "implementation defined paging\n\r"; break; + case 2 : paging = "VMSAv6, with cache and TLB type registers\n\r"; break; + case 3 : paging = "VMSAv7, with support for remapping and access flag\n\r"; break; + case 4 : paging = "VMSAv7 with PXN bit supported\n\r"; break; + case 5 : paging = "VMSAv7, PXN and long format descriptors. EPAE is supported.\n\r"; break; + default : paging = "?_? unknown paging ?_?\n\r"; + } + + uart_puts(paging); +} + +void demo_mode_to_system(void) +{ + + // get content of current program status register to check the current + // processor mode + PSR_t CPSR = read_CPSR(); + + char *mode_name; + + switch(CPSR.fields.PSR_MODE_4_0) + { + 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("current mode: "); + uart_puts(mode_name); + + uart_puts("setting mode to system (PL1)...\r\n"); + set_system_mode(); +} + +extern char + _binary_PL_0_test_img_start, + _binary_PL_0_test_img_end, + _binary_PL_0_test_img_size; + +void demo_go_unprivileged(void) +{ + short_section_descriptor_t *translation_table = + (short_section_descriptor_t*) TRANSLATION_TABLE_BASE; + + volatile short_section_descriptor_t *PL0_code_section = + &translation_table[PL0_SECTION_NUMBER]; + volatile short_section_descriptor_t *UART_memory_section = + &translation_table[((uint32_t) GPIO_BASE) >> 20]; + + PL0_code_section->SECTION_BASE_ADDRESS_31_20 = + UNPRIVILEGED_MEMORY_START >> 20; + + // make the selected section and uart section available for PL0 + PL0_code_section->ACCESS_PERMISSIONS_2 = + AP_2_0_MODEL_RW_ALL >> 2; + PL0_code_section->ACCESS_PERMISSIONS_1_0 = + AP_2_0_MODEL_RW_ALL & 0b011; + + UART_memory_section->ACCESS_PERMISSIONS_2 = + AP_2_0_MODEL_RW_ALL >> 2; + UART_memory_section->ACCESS_PERMISSIONS_1_0 = + AP_2_0_MODEL_RW_ALL & 0b011; + + + // invalidate main Translation Lookup Buffer (just in case) + asm("mcr p15, 0, %0, c8, c7, 0\n\r" + "isb" :: "r" (0) : "memory"); + + // check that translation works... by copying a string using one + // mapping and reading it using other :D + char message[] = "mapped sections for PL0 code\n\r"; + + unsigned int i; + for (i = 0; i < sizeof(message); i++) + ((volatile char*) UNPRIVILEGED_MEMORY_START)[i] = message[i]; + + uart_puts((char*) VIRTUAL_PL0_MEMORY_START); + + // now paste a userspace program to that section, jump to it and + // switch to PL0 + for (size_t i = 0; i < (size_t) &_binary_PL_0_test_img_size; i++) + ((volatile char*) VIRTUAL_PL0_MEMORY_START)[i] = + (&_binary_PL_0_test_img_start)[i]; + + // jump to that copied code (switch to PL0 is done by that code) + asm volatile("mov r5, #0\n\r" + "movt r5, #"PL0_SECTION_NUMBER_STR"1111\n\r" + "mov sp, r5\n\r" // setting stack is important :D + "mov r5, #0\n\r" + "movt r5, #"PL0_SECTION_NUMBER_STR"0000\n\r" + "blx r5\n\r"); +} diff --git a/demo_functionality.h b/demo_functionality.h new file mode 100644 index 0000000..c8b163f --- /dev/null +++ b/demo_functionality.h @@ -0,0 +1,10 @@ +#ifndef DEMO_FUNCTIONALITY_H +#define DEMO_FUNCTIONALITY_H + +void demo_paging_support(void); + +void demo_mode_to_system(void); + +void demo_go_unprivileged(void); + +#endif // DEMO_FUNCTIONALITY_H @@ -1,18 +1,7 @@ #include "uart.h" -#include "strings.h" -#include "psr.h" -#include "translation_table_descriptors.h" -#include "cp_regs.h" +#include "demo_functionality.h" +#include "paging.h" -extern char __end; - -extern char - _binary_PL_0_test_img_start, - _binary_PL_0_test_img_end, - _binary_PL_0_test_img_size; - -void enabling_code_from_the_net(void); - void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags) { // Declare as unused @@ -29,212 +18,18 @@ void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags) uart_puts("Hello, kernel World!\r\n"); - uint32_t ID_MMFR0; - // get contents of coprocessor register to check for paging support - asm("mrc p15, 0, %0, c0, c1, 4" : "=r" (ID_MMFR0)); - - char *paging; - - switch(ID_MMFR0 & 0xf) /* lowest 4 bits indicate VMSA support */ - { - case 0 : paging = "no paging\n\r"; break; - case 1 : paging = "implementation defined paging\n\r"; break; - case 2 : paging = "VMSAv6, with cache and TLB type registers\n\r"; break; - case 3 : paging = "VMSAv7, with support for remapping and access flag\n\r"; break; - case 4 : paging = "VMSAv7 with PXN bit supported\n\r"; break; - case 5 : paging = "VMSAv7, PXN and long format descriptors. EPAE is supported.\n\r"; break; - default : paging = "?_? unknown paging ?_?\n\r"; - } - - uart_puts(paging); - - // get content of current program status register to check the current - // processor mode - PSR_t CPSR = read_CPSR(); - - char *mode_name; - - switch(CPSR.fields.PSR_MODE_4_0) - { - 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("current mode: "); - uart_puts(mode_name); - - uart_puts("setting mode to system (PL1)...\r\n"); - set_system_mode(); - - 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 = - ((kernel_end - 1) & ~((uint32_t) 0x3fff)) + (uint32_t) 0x4000; - - uint32_to_bits(translation_table_base, bits); - uart_puts("binary representation of chosen" - " lvl1 translation table address: "); - uart_puts(bits); uart_puts("\n\r"); - - // flat map all memory - uart_puts("preparing translation table\n\r"); - short_descriptor_lvl1_t volatile *translation_table = - (short_descriptor_lvl1_t*) translation_table_base; - - for (uint32_t i = 0; i < 4096; i++) - 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_1 = - SHORT_DESCRIPTOR_SECTION_OR_SUPERSECTION >> 1, - // rest of fields are 0s - }; - - // meddle with domain settings - uart_puts("setting domain0 to client access" - " and blocking other domains\n\r"); - - 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 - - // 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"); + // prints some info + demo_paging_support(); - SCTLR_t SCTLR; - asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR.raw)); - - 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 - asm("mcr p15, 0, r0, c7, c5, 0\n\r" // r0 gets ignored - "isb" ::: "memory"); - - // invalidate branch-prediction - asm("mcr p15, 0, r0, c7, c5, 6\n\r" // r0 - same as above - "isb" ::: "memory"); - - // invalidate main Translation Lookup Buffer - asm("mcr p15, 0, %0, c8, c7, 0\n\r" - "isb" :: "r" (0) : "memory"); - - // now set TTBCR to use TTBR0 exclusively - uart_puts("Setting TTBCR.N to 0, so that" - " TTBR0 is used everywhere\n\r"); - - uint32_t TTBCR = 0; - asm("mcr p15, 0, %0, c2, c0, 2" :: "r" (TTBCR)); - - // Now do stuff with TTBR0 - TTBR_t TTBR0; - TTBR0.raw = 0; - TTBR0.fields.TTBR_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 - uart_puts("enabling the MMU\n\r"); - - // redundant - we already have SCTLR contents in the variable - // asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR.raw)); - - SCTLR.fields.M = 1; - - asm("mcr p15, 0, %0, c1, c0, 0\n\r" - "isb" :: "r" (SCTLR.raw) : "memory"); - - volatile short_section_descriptor_t *PL0_code_section = - &translation_table[0b101010101010].section_fields; - volatile short_section_descriptor_t *UART_memory_section = - &translation_table[((uint32_t) GPIO_BASE) >> 20].section_fields; - - // map code section to the memory right after out kernel - uint32_t kernel_memory_end = (uint32_t) (translation_table + 4096); - uint32_t unprivileged_memory_start = - (((kernel_memory_end - 1) >> 20) + 1) << 20; - - PL0_code_section->SECTION_BASE_ADDRESS_31_20 = - unprivileged_memory_start >> 20; - - // make the selected section and uart section available for PL0 - PL0_code_section->ACCESS_PERMISSIONS_2 = - AP_2_0_MODEL_RW_ALL >> 2; - PL0_code_section->ACCESS_PERMISSIONS_1_0 = - AP_2_0_MODEL_RW_ALL & 0b011; - - UART_memory_section->ACCESS_PERMISSIONS_2 = - AP_2_0_MODEL_RW_ALL >> 2; - UART_memory_section->ACCESS_PERMISSIONS_1_0 = - AP_2_0_MODEL_RW_ALL & 0b011; - - - // invalidate main Translation Lookup Buffer (just in case) - asm("mcr p15, 0, %0, c8, c7, 0\n\r" - "isb" :: "r" (0) : "memory"); - - // check that translation works... by copying a string using one - // mapping and reading it using other :D - char message[] = "mapped sections for PL0 code\n\r"; - - unsigned int i; - for (i = 0; i < sizeof(message); i++) - ((volatile char*) unprivileged_memory_start)[i] = message[i]; - - uart_puts((char*) (((uint32_t) 0b101010101010) << 20)); + // prints some info and switches to system mode + demo_mode_to_system(); - // now paste a userspace program to that section, jump to it and - // switch to PL0 - for (size_t i = 0; i < (size_t) &_binary_PL_0_test_img_size; i++) - ((char*) (((uint32_t) 0b101010101010) << 20))[i] = - (&_binary_PL_0_test_img_start)[i]; + // prints some info and sets upp translation table, turns on MMU + setup_flat_map(); - // jump to that copied code (no switch to PL0 yet, just testing) - asm volatile("mov r5, #0\n\r" - "movt r5, #0b1010101010101111\n\r" - "mov sp, r5\n\r" - "mov r5, #0\n\r" - "movt r5, #0b1010101010100000\n\r" - "blx r5\n\r"); + // prints some info and sets up a section for PL0 code, loads a blob + // there and jumps to it... never, ever, ever returns + demo_go_unprivileged(); while(1); diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..2d0f6f4 --- /dev/null +++ b/memory.h @@ -0,0 +1,35 @@ +#ifndef MEMORY_H +#define MEMORY_H + +#define INTERRUPT_VECTOR_TABLE_START ((uint32_t) 0x0) + +#define STACK_START ((uint32_t) 0x4000) +#define STACK_END ((uint32_t) 0x8000) + +extern char __end; +extern char __start; + +#define KERNEL_START ((uint32_t) &__start) +#define KERNEL_END ((uint32_t) &__end) + +// first 2^14 aligned address after the kernel +#define TRANSLATION_TABLE_BASE \ + (((KERNEL_END - (uint32_t) 1) & ~((uint32_t) 0x3fff)) \ + + (uint32_t) 0x4000) + +#define TRANSLATION_TABLE_END \ + (TRANSLATION_TABLE_BASE + (uint32_t) (4096 * 4)) + +#define PRIVILEGED_MEMORY_END TRANSLATION_TABLE_END + +#define UNPRIVILEGED_MEMORY_START \ + (((PRIVILEGED_MEMORY_END - (uint32_t) 1) & ~((uint32_t) 0x3fff)) \ + + (uint32_t) 0x4000) + +#define PL0_SECTION_NUMBER ((uint32_t) 0b101010101010) +#define PL0_SECTION_NUMBER_STR "0b101010101010" + +#define VIRTUAL_PL0_MEMORY_START (PL0_SECTION_NUMBER << 20) + +#endif // MEMORY_H + diff --git a/paging.c b/paging.c new file mode 100644 index 0000000..7c2a9de --- /dev/null +++ b/paging.c @@ -0,0 +1,114 @@ +#include "cp_regs.h" +#include "uart.h" +#include "strings.h" +#include "memory.h" +#include "translation_table_descriptors.h" + +void setup_flat_map(void) +{ + 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_to_bits(TRANSLATION_TABLE_BASE, bits); + uart_puts("binary representation of chosen" + " lvl1 translation table address: "); + uart_puts(bits); uart_puts("\n\r"); + + // flat map all memory + uart_puts("preparing translation table\n\r"); + short_descriptor_lvl1_t volatile *translation_table = + (short_descriptor_lvl1_t*) TRANSLATION_TABLE_BASE; + + for (uint32_t i = 0; i < 4096; i++) + 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_1 = + SHORT_DESCRIPTOR_SECTION_OR_SUPERSECTION >> 1, + // rest of fields are 0s + }; + + // meddle with domain settings + uart_puts("setting domain0 to client access" + " and blocking other domains\n\r"); + + 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 + + // 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_t SCTLR; + asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR.raw)); + + 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 + asm("mcr p15, 0, r0, c7, c5, 0\n\r" // r0 gets ignored + "isb" ::: "memory"); + + // invalidate branch-prediction + asm("mcr p15, 0, r0, c7, c5, 6\n\r" // r0 - same as above + "isb" ::: "memory"); + + // invalidate main Translation Lookup Buffer + asm("mcr p15, 0, %0, c8, c7, 0\n\r" + "isb" :: "r" (0) : "memory"); + + // now set TTBCR to use TTBR0 exclusively + uart_puts("Setting TTBCR.N to 0, so that" + " TTBR0 is used everywhere\n\r"); + + uint32_t TTBCR = 0; + asm("mcr p15, 0, %0, c2, c0, 2" :: "r" (TTBCR)); + + // Now do stuff with TTBR0 + TTBR_t TTBR0; + TTBR0.raw = 0; + TTBR0.fields.TTBR_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 + uart_puts("enabling the MMU\n\r"); + + // redundant - we already have SCTLR contents in the variable + // asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR.raw)); + + SCTLR.fields.M = 1; + + asm("mcr p15, 0, %0, c1, c0, 0\n\r" + "isb" :: "r" (SCTLR.raw) : "memory"); +} diff --git a/paging.h b/paging.h new file mode 100644 index 0000000..d38408c --- /dev/null +++ b/paging.h @@ -0,0 +1,6 @@ +#ifndef PAGING_H +#define PAGING_H + +void setup_flat_map(void); + +#endif |