aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile36
-rw-r--r--PL0_test.c34
-rw-r--r--PL0_test.ld44
-rw-r--r--cp_regs.h21
-rw-r--r--cpsr.h39
-rw-r--r--demo_functionality.c115
-rw-r--r--demo_functionality.h10
-rw-r--r--global.h5
-rw-r--r--kernel.c172
-rw-r--r--memory.h35
-rw-r--r--paging.c114
-rw-r--r--paging.h6
-rw-r--r--pipe_image.c2
-rw-r--r--psr.h122
-rw-r--r--strings.h5
-rw-r--r--translation_table_descriptors.h129
-rw-r--r--uart.h5
17 files changed, 621 insertions, 273 deletions
diff --git a/Makefile b/Makefile
index d6e8885..ce29a19 100644
--- a/Makefile
+++ b/Makefile
@@ -1,29 +1,27 @@
CFLAGS=-mcpu=cortex-a7 -ffreestanding -std=gnu11 -Wall -Wextra -I.
ELFFLAGS=-ffreestanding -O2 -nostdlib -lgcc -I.
+ARM_OBJECTS=kernel.o paging.o demo_functionality.o PL0_test.o uart.o loader_stage1.o loader_stage2.o
all : kernel7.img
-kernel.o : kernel.c
+%.o : %.c
arm-none-eabi-gcc $(CFLAGS) -c $^ -o $@
-uart.o : uart.c
- arm-none-eabi-gcc $(CFLAGS) -c $^ -o $@
-
-interrupts.o : interrupts.c uart.o
- arm-none-eabi-gcc $(CFLAGS) -c $^ -o $@
-
-boot.o : boot.S
+%.o : %.S
arm-none-eabi-as -mcpu=cortex-a7 $^ -o $@
-kernel.elf : boot.o kernel.o uart.o
- arm-none-eabi-gcc -T linker.ld -o $@ $(ELFFLAGS) $^
-
-kernel7.img : kernel.elf
+%.img : %.elf
arm-none-eabi-objcopy $^ -O binary $@
-loader_stage2.o : loader_stage2.c
- arm-none-eabi-gcc $(CFLAGS) -c $^ -o $@
+%_embeddable.o : %.img
+ arm-none-eabi-objcopy -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata $^ $@
+
+PL_0_test.elf : PL0_test.o uart.o
+ arm-none-eabi-gcc -T PL0_test.ld -o $@ $(ELFFLAGS) $^
+
+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) $^
loader_stage2.elf : loader_stage2.o uart.o
arm-none-eabi-gcc -T loader_stage2.ld -o $@ $(ELFFLAGS) $^
@@ -32,22 +30,14 @@ loader_stage2.img : loader_stage2.elf
arm-none-eabi-objcopy $^ -O binary $@
test -n "$$(find $@ -size -16384c)" || exit -1
-loader_stage2_embeddable.o : loader_stage2.img
- arm-none-eabi-objcopy -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata $^ $@
-
-loader_stage1.o : loader_stage1.c
- arm-none-eabi-gcc $(CFLAGS) -c $^ -o $@
-
loader.elf : boot.o loader_stage1.o loader_stage2_embeddable.o
arm-none-eabi-gcc -T loader_stage1.ld -o $@ $(ELFFLAGS) $^
-loader.img : loader.elf
- arm-none-eabi-objcopy $^ -O binary $@
qemu-elf : kernel.elf
qemu-system-arm -m 256 -M raspi2 -serial stdio -kernel $^
-qemu-bin : loader.elf kernel7.img pipe_image
+qemu-bin : loader.elf kernel.img pipe_image
./pipe_image --stdout | qemu-system-arm -m 256 -M raspi2 -serial stdio -kernel $<
run-on-rpi : kernel7.img pipe_image
diff --git a/PL0_test.c b/PL0_test.c
new file mode 100644
index 0000000..7609294
--- /dev/null
+++ b/PL0_test.c
@@ -0,0 +1,34 @@
+#include "uart.h"
+#include "psr.h"
+
+void PL0_main(void)
+{
+ uart_puts("hello PL0! Switching to user mode!\n\r");
+
+ asm("cps #0b10000\n\r"
+ "isb" ::: "memory");
+
+ // if all went correct, Success! gets printed
+ uart_puts("Success!\n\r");
+
+ // if we're indeed i PL0, we should crash now, when trying to access
+ // memory we're not allowed to
+ char first_kernel_byte[2];
+
+ first_kernel_byte[0] = *(char*) ((uint32_t) 0x8000);
+ first_kernel_byte[1] = '\0';
+
+ uart_puts(first_kernel_byte);
+
+ while (1)
+ {
+ char c;
+ switch(c = uart_getc())
+ {
+ case '\r':
+ uart_putc('\n');
+ default:
+ uart_putc(c);
+ }
+ }
+}
diff --git a/PL0_test.ld b/PL0_test.ld
new file mode 100644
index 0000000..430e098
--- /dev/null
+++ b/PL0_test.ld
@@ -0,0 +1,44 @@
+ENTRY(_start)
+
+SECTIONS
+{
+ /* 0b10101010101000000000000000000000 */
+ . = 0xaaa00000;
+ __start = .;
+ __text_start = .;
+ .text :
+ {
+ /* have entry point at the beginning */
+ KEEP(*(.text.PL0main))
+ *(.text)
+ }
+ . = ALIGN(4096); /* align to page size */
+ __text_end = .;
+
+ __rodata_start = .;
+ .rodata :
+ {
+ *(.rodata)
+ }
+ . = ALIGN(4096); /* align to page size */
+ __rodata_end = .;
+
+ __data_start = .;
+ .data :
+ {
+ *(.data)
+ }
+ . = ALIGN(4096); /* align to page size */
+ __data_end = .;
+
+ __bss_start = .;
+ .bss :
+ {
+ bss = .;
+ *(.bss)
+ }
+ . = ALIGN(4096); /* align to page size */
+ __bss_end = .;
+ __bss_size = __bss_end - __bss_start;
+ __end = .;
+}
diff --git a/cp_regs.h b/cp_regs.h
index 98a8bb8..e5e7063 100644
--- a/cp_regs.h
+++ b/cp_regs.h
@@ -1,5 +1,7 @@
-#include <stdint.h>
+#ifndef CP_REGS_H
+#define CP_REGS_H
+#include <stdint.h>
// SCTLR - System Control Register
@@ -98,14 +100,15 @@ typedef union
// 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
+#define TTBR_CACHEABLE_BIT C
+#define TTBR_INTERPROCESS_REGION_BITS_1 IRGN_1
+#define TTBR_SHAREABLE_BIT S
+#define TTBR_IMPLEMENTATION_DEFINED_BIT IMP
+#define TTBR_REGION_BITS_1_0 RGN
+#define TTBR_INTERPROCESS_REGION_BITS_0 IRGN_0
+#define TTBR_NON_OUTER_SHAREABLE_BIT NOS
+#define TTBR_TRANSLATION_TABLE_BASE_ADDRESS Bits_31_14
} fields;
} TTBR_t;
+#endif // CP_REGS_H
diff --git a/cpsr.h b/cpsr.h
deleted file mode 100644
index 327bdf0..0000000
--- a/cpsr.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#include <stdint.h>
-
-enum execution_mode {
- MODE_USER = 0x10,
- MODE_FIQ = 0x11,
- MODE_IRQ = 0x12,
- MODE_SUPERVISOR = 0x13,
- MODE_MONITOR = 0x16,
- MODE_ABORT = 0x17,
- MODE_HYPERVISOR = 0x1a,
- MODE_UNDEFINED = 0x1b,
- MODE_SYSTEM = 0x1f,
-};
-
-inline static uint32_t read_CPSR(void)
-{
- uint32_t CPSR;
- // get content of current program status register
- asm("mrs %0, cpsr" : "=r" (CPSR) ::);
-
- return CPSR;
-}
-
-inline static enum execution_mode read_processor_mode(void)
-{
- /* lowest 5 bits indicate processor mode */
- return read_CPSR() & 0x1f;
-}
-
-inline static void set_system_mode(void)
-{
- uint32_t CPSR = read_CPSR();
-
- CPSR = (CPSR >> 5) << 5;
-
- CPSR |= MODE_SYSTEM;
-
- asm("msr cpsr, %0" :: "r" (CPSR) : "memory");
-}
diff --git a/demo_functionality.c b/demo_functionality.c
new file mode 100644
index 0000000..7bba6a6
--- /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_section =
+ &translation_table[PL0_SECTION_NUMBER];
+ volatile short_section_descriptor_t *UART_memory_section =
+ &translation_table[((uint32_t) GPIO_BASE) >> 20];
+
+ PL0_section->SECTION_BASE_ADDRESS_31_20 =
+ UNPRIVILEGED_MEMORY_START >> 20;
+
+ // make the selected section and uart section available for PL0
+ PL0_section->ACCESS_PERMISSIONS_2 =
+ AP_2_0_MODEL_RW_ALL >> 2;
+ PL0_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
diff --git a/global.h b/global.h
index 02fdedc..b041a65 100644
--- a/global.h
+++ b/global.h
@@ -1,3 +1,6 @@
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
// board type, raspi2
#define RASPI 2
@@ -10,3 +13,5 @@
#define GPIO_BASE 0x20200000
#endif // RASPI == 3 || RASPI == 2
#endif // RASPI == 4
+
+#endif // GLOBAL_H
diff --git a/kernel.c b/kernel.c
index 8a06565..f2e556c 100644
--- a/kernel.c
+++ b/kernel.c
@@ -1,13 +1,7 @@
#include "uart.h"
-#include "cpsr.h"
-#include "strings.h"
-#include "translation_table_descriptors.h"
-#include "cp_regs.h"
+#include "demo_functionality.h"
+#include "paging.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
@@ -20,163 +14,25 @@ void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags)
// When we attach screen session after loading kernel with socat
// we miss kernel's greeting... So we'll make the kernel wait for
// one char we're going to send from within screen
-// uart_getc();
- 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));
-
-// uart_puts("Hello, kernel World!\r\n");
-
- 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);
-
- uint32_t CPSR;
- // get content of current program status register to check the current
- // processor mode
- asm("mrs %0, cpsr" : "=r" (CPSR) :: "memory");
-
- char *mode_name;
-
- 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("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
+ // uart_getc();
- // 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;
+ uart_puts("Hello, kernel World!\r\n");
- 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_t *translation_table =
- (short_descriptor_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_2 =
- SHORT_DESCRIPTOR_SECTION_OR_SUPERSECTION >> 1,
- // rest of fields are 0s
- };
+ // prints some info
+ demo_paging_support();
- // meddle with domain settings
- uart_puts("setting domain0 to client access"
- " and blocking other domains\n\r");
+ // prints some info and switches to system mode
+ demo_mode_to_system();
- 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);
+ // prints some info and sets upp translation table, turns on MMU
+ setup_flat_map();
- // the above should do the same as this:
- // DACR = 1;
+ // 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();
- 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
+ while(1);
- // 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.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");
-
while (1)
{
char c;
diff --git a/memory.h b/memory.h
new file mode 100644
index 0000000..1472a8b
--- /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) 0xfffff)) \
+ + (uint32_t) 0x100000)
+
+#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..1218fb8
--- /dev/null
+++ b/paging.h
@@ -0,0 +1,6 @@
+#ifndef PAGING_H
+#define PAGING_H
+
+void setup_flat_map(void);
+
+#endif // PAGING_H
diff --git a/pipe_image.c b/pipe_image.c
index 1266e90..7e27fb9 100644
--- a/pipe_image.c
+++ b/pipe_image.c
@@ -13,7 +13,7 @@
/* It is intended to be used with our bootloader. */
int main(int argc, const char **argv) {
- const char *image_file_name = "kernel7.img";
+ const char *image_file_name = "kernel.img";
_Bool stdout_instead_of_uart = 0;
if (argc > 1)
diff --git a/psr.h b/psr.h
new file mode 100644
index 0000000..dfbb878
--- /dev/null
+++ b/psr.h
@@ -0,0 +1,122 @@
+#ifndef PSR_H
+#define PSR_H
+
+#include <stdint.h>
+
+enum execution_mode {
+ MODE_USER = 0b10000,
+ MODE_FIQ = 0b10001,
+ MODE_IRQ = 0b10010,
+ MODE_SUPERVISOR = 0b10011,
+ MODE_MONITOR = 0b10110,
+ MODE_ABORT = 0b10111,
+ MODE_HYPERVISOR = 0b11010,
+ MODE_UNDEFINED = 0b11011,
+ MODE_SYSTEM = 0b11111,
+};
+
+typedef union
+{
+ uint32_t raw;
+ struct
+ {
+ uint32_t M_4_0 : 5; // bits 4:0
+ uint32_t T : 1; // bit 5
+ uint32_t F : 1; // bit 6
+ uint32_t I : 1; // bit 7
+ uint32_t A : 1; // bit 8
+ uint32_t E : 1; // bit 9
+ uint32_t IT_7_2 : 6; // bits 15:10
+ uint32_t GE_3_0 : 4; // bits 19:16
+ uint32_t Bits_23_20 : 4; // bits 23:20
+ uint32_t J : 1; // bit 24
+ uint32_t IT_1_0 : 2; // bits 26:25
+ uint32_t Q : 1; // bit 27
+ uint32_t V : 1; // bit 28
+ uint32_t C : 1; // bit 29
+ uint32_t Z : 1; // bit 30
+ uint32_t N : 1; // bit 31
+#define PSR_MODE_4_0 M_4_0
+#define PSR_THUMB_BIT T
+#define PSR_FIQ_MASKK_BIT F
+#define PSR_IRQ_MASK_BIT I
+#define PSR_ASYNC_ABORT_MASK_BIT A
+#define PSR_ENDIANNESS_BIT E
+#define PSR_IF_THEN_STATE_7_2 IT_7_2
+#define PSR_GREATER_THAN_OR_EQUAL_FLAGS GE_3_0
+ // bits 23:20 are reserved
+#define PSR_JAZELLE_BIT J
+#define PSR_IF_THEN_STATE_1_0 IT_1_0
+#define PSR_CUMULATIVE_SATURATION_BIT Q
+#define PSR_OVERFLOW_CONDITION_BIT V
+#define PSR_CARRY_CONDITION_BIT C
+#define PSR_ZERO_CONDITION_BIT Z
+#define PSR_NEGATIVE_CONDITION_BIT N
+ } fields;
+} PSR_t;
+
+inline static PSR_t read_CPSR(void)
+{
+ PSR_t CPSR;
+ // get content of current program status register
+ asm("mrs %0, cpsr" : "=r" (CPSR.raw) :: "memory");
+
+ return CPSR;
+}
+
+//// Write function not working for some reason... Assembly from gcc
+//// looks ok. Needs checking on real hw.
+
+//inline static void write_CPSR(PSR_t CPSR)
+//{
+ // write to current program status register and synchronize context
+ // asm("msr cpsr, %0\n\r"
+ // "isb" :: "r" (CPSR.raw) : "memory");
+//}
+
+inline static void set_system_mode(void)
+{
+ // hack to fix an unexplained bug; volatile needed in case of
+ // compilation with optimizations
+ volatile PSR_t CPSR __attribute__((unused)) = read_CPSR();
+
+ //// there are 2 ways of changing mode, both with the same
+ //// problem (see the long comment below)
+
+ //// way 1
+ // CPSR.fields.M_4_0 = MODE_SYSTEM;
+ // write to current program status register and synchronize context
+ // asm("msr cpsr, %0\n\r"
+ // "isb":: "r" (CPSR.raw) : "memory");
+
+ //// way 2
+ asm("cps #0b11111\n\r"
+ "isb" ::: "memory");
+}
+
+// The thing with writing to cpsr is weird. I used to have a single
+// function that would set the system mode by:
+// 1. reading the cpsr
+// 2. modifying the value using bit shifts and logical or
+// 3. writing the value back
+// When introducing structs and bitfields I wanted to have separate
+// functions for reading and writing the cpsr and have code up the
+// call stack handle value modification. For some reason this didn't
+// work - all would just hang when writing the CPSR. It turned out
+// everything works if i call read_CPSR() from the same function, in
+// which i write to cpsr. And I don't even need to use the value
+// I read. I can just discard it (we're compiling without
+// optimizations, so read_CPSR() is still called) and use the value
+// passed as argument. I noticed an even weirder thing: the 2nd way
+// of changing mode (cps instruction) doesn't work normally, but,
+// just as msr, it does work when I call read_CPSR() before it. Even
+// weirder - I cannot replace read_CPSR() with the actual assembly it
+// does, because it stops working again! Asm generated by gcc looks
+// ok, so Idk, maybe its some qemu issue (haven't tested on real RPi
+// yet). I seem to have experienced uart printing inserted here and
+// there do the same magic read_CPSR() does (but, again, not always).
+// Unfortunately, nop repeated several hundreds times (or a recursive
+// nop function, that just calls itself a given number of times)
+// doesn't have this magical property :(
+
+#endif // PSR_H
diff --git a/strings.h b/strings.h
index 7721f63..2ceade8 100644
--- a/strings.h
+++ b/strings.h
@@ -1,3 +1,6 @@
+#ifndef STRINGS_H
+#define STRINGS_H
+
#include <stdint.h>
void uint32_to_bits(uint32_t number, char *buf)
@@ -7,3 +10,5 @@ void uint32_to_bits(uint32_t number, char *buf)
buf[32] = '\0';
}
+
+#endif // STRINGS_H
diff --git a/translation_table_descriptors.h b/translation_table_descriptors.h
index 8f92473..981c3c7 100644
--- a/translation_table_descriptors.h
+++ b/translation_table_descriptors.h
@@ -1,3 +1,6 @@
+#ifndef TRANSLATION_TABLE_DESCRIPTORS_H
+#define TRANSLATION_TABLE_DESCRIPTORS_H
+
#include <stdint.h>
// ARM lets you choose between 32-bit abd 64-bit translation table
@@ -11,6 +14,33 @@
// meanings depending on settings in coprocessor registers... (yeah, ARM
// looks a bit messy... all for backward compatibility, i guess)
+
+////// Here are the definitions for short-format descriptors
+
+//// short-format page table descriptor
+
+typedef struct
+{
+ uint32_t Bits_1_0 : 1; // bits 1:0
+ uint32_t PXN : 1; // bit 2
+ uint32_t NS : 1; // bit 3
+ uint32_t SBZ : 1; // bit 4
+ uint32_t Domain_3_0 : 4; // bits 8:5
+ uint32_t Bit_9 : 1; // bit 9
+ uint32_t Bits_31_10 : 22; // bits 31:10
+#define DESCRIPTOR_TYPE_1_0 Bits_1_0
+#define PRIVILEGED_EXECUTE_NEVER_BIT PXN
+#define NON_SECURE_BIT NS
+ // me thinks SBZ means "should be zero",
+ // but me sees no point #defining it
+#define DOMAIN_3_0 Domain_3_0
+#define IMPLEMENTATION_DEFINED_BIT Bit_9
+#define PAGE_TABLE_BASE_ADDRESS_31_10 Bits_31_10
+} short_page_table_descriptor_t;
+
+
+//// short-format section descriptor
+
typedef struct
{
uint32_t PXN : 1; // bit 0
@@ -28,56 +58,26 @@ typedef struct
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
+ // some of these are already defined the same for page table
+ //#define PRIVILEGED_EXECUTE_NEVER_BIT PXN
+#define DESCRIPTOR_TYPE_1 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 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 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
+//// short-format supersection descriptor
typedef struct
{
@@ -99,7 +99,7 @@ typedef struct
uint32_t PA_31_24 : 8; // bits 31:24
// most of these are already defined the same for section
//#define PRIVILEGED_EXECUTE_NEVER_BIT PXN
- //#define DESCRIPTOR_TYPE_2 Bit_1
+ //#define DESCRIPTOR_TYPE_1 Bit_1
//#define BUFFERABLE_BIT B
//#define CACHEABLE_BIT C
//#define EXECUTE_NEVER_BIT XN
@@ -116,17 +116,59 @@ typedef struct
#define SUPERSECTION_BASE_ADDRESS_31_24 PA_31_24
} short_supersection_descriptor_t;
+
+//// possible access permission field values
+
+// 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
+
+
+//// short-format descriptor generic type
+
typedef union
{
uint32_t raw;
- uint8_t descriptor_type;
-
+ uint8_t descriptor_type : 2;
+
+ short_page_table_descriptor_t page_table_fields;
short_section_descriptor_t section_fields;
short_supersection_descriptor_t supersection_fields;
- // more to come here (e.g. short_supersection_descriptor_t)
-} short_descriptor_t;
+} short_descriptor_lvl1_t;
+
+
+//// possible values of descriptor_type field:
-// possible values of descriptor_type field:
#define SHORT_DESCRIPTOR_INVALID 0b00
#define SHORT_DESCRIPTOR_PAGE_TABLE 0b01
#define SHORT_DESCRIPTOR_SECTION_OR_SUPERSECTION 0b10
@@ -135,3 +177,4 @@ typedef union
// 0b11 should not be used
#define SHORT_DESCRIPTOR_RESERVED 0b11
+#endif // TRANSLATION_TABLE_DESCRIPTORS_H
diff --git a/uart.h b/uart.h
index 6430cb1..ec29d47 100644
--- a/uart.h
+++ b/uart.h
@@ -1,3 +1,6 @@
+#ifndef UART_H
+#define UART_H
+
#include <stddef.h>
#include <stdint.h>
#include <global.h>
@@ -39,3 +42,5 @@ void uart_init();
void uart_putc(unsigned char c);
unsigned char uart_getc();
void uart_puts(const char* str);
+
+#endif // UART_H