aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWojtek Kosior <kwojtus@protonmail.com>2019-11-19 17:49:20 +0100
committerWojtek Kosior <kwojtus@protonmail.com>2019-11-19 17:49:20 +0100
commit39145f6d1b8c57abe2bc0167b2b413970d0dfdb6 (patch)
tree2e2a76590e749579676720505aa38c57f85a95a4
parent11541f1d1eaaefebd6d01b42e92a65606e4bc382 (diff)
downloadrpi-MMU-example-39145f6d1b8c57abe2bc0167b2b413970d0dfdb6.tar.gz
rpi-MMU-example-39145f6d1b8c57abe2bc0167b2b413970d0dfdb6.zip
split kernel into more files
-rw-r--r--Makefile8
-rw-r--r--demo_functionality.c115
-rw-r--r--demo_functionality.h10
-rw-r--r--kernel.c227
-rw-r--r--memory.h35
-rw-r--r--paging.c114
-rw-r--r--paging.h6
7 files changed, 298 insertions, 217 deletions
diff --git a/Makefile b/Makefile
index 6f2600e..caa0154 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/kernel.c b/kernel.c
index 5d4e6d7..ce0473a 100644
--- a/kernel.c
+++ b/kernel.c
@@ -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