Enable the MMU even if the kernel is not loaded in physical memory at
the same place it was linked against.
This commit is contained in:
parent
e2a3ab602c
commit
d6e3899fc1
203
boot/start.S
203
boot/start.S
@ -1,51 +1,170 @@
|
|||||||
interrupt_vector_table:
|
/*
|
||||||
b . @ Reset
|
* Kernel entry in assembly. This handles relocating the kernel so that it is
|
||||||
b .
|
* in both physical and virtual memory where we want it to be. We copy the
|
||||||
b . @ SWI instruction
|
* kernel to a different physical location if necessary, turn on the MMU,
|
||||||
b .
|
* setting up a dual-mapping if the kernel is not in physical memory at the
|
||||||
b .
|
* same place it was linked against. Finally, we jump into the kernel's main()
|
||||||
b .
|
* function in C using the address it is linked against. When the MMU gets
|
||||||
b .
|
* initialized fully later, it will remove the initial 1:1 mapping.
|
||||||
b .
|
*/
|
||||||
|
|
||||||
.comm stack, 0x10000 @ Reserve 64k stack in the BSS
|
|
||||||
.globl start
|
.globl start
|
||||||
|
|
||||||
start:
|
start:
|
||||||
ldr r0, tt_base
|
str r1, machine_type /* Backup atags/machine type registers so we can access them later from C */
|
||||||
mcr p15, 0, r0, c2, c0, 0 /* TTBR0 */
|
str r2, atags_ptr
|
||||||
|
|
||||||
/* Setup page table entries for the page table and kernel (domain 0) */
|
bl copy_kernel
|
||||||
ldr r0, tt_tt_addr
|
copy_kernel_lr: /* Used to calculate address at which kernel is currently loaded by copy_kernel */
|
||||||
ldr r1, tt_tt_val
|
|
||||||
str r1, [r0]
|
|
||||||
|
|
||||||
ldr r0, kernel_tt_addr
|
bl setup_mmu
|
||||||
ldr r1, kernel_tt_val
|
|
||||||
str r1, [r0]
|
|
||||||
|
|
||||||
/* Set access permissions for domain 0 to "Manager" */
|
ldr sp, =stack+0x10000 /* Set up the stack */
|
||||||
mov r0, #0x3
|
bl main
|
||||||
mcr p15, 0, r0, c3, c0, 0 /* DACR */
|
|
||||||
|
|
||||||
/* Enable the MMU */
|
1:
|
||||||
mrc p15, 0, r0, c1, c0, 0 /* SCTLR */
|
b 1b /* Halt */
|
||||||
orr r0, r0, #0x1
|
|
||||||
mcr p15, 0, r0, c1, c0, 0 /* SCTLR */
|
|
||||||
|
|
||||||
ldr sp, =stack+0x10000 @ Set up the stack
|
copy_kernel:
|
||||||
bl main @ Jump to the main function
|
/*
|
||||||
|
* Because we're not necessarily loaded at an address that's aligned the same
|
||||||
|
* as where we're linked, copy the kernel over to fix that up.
|
||||||
|
*
|
||||||
|
* clobbers:
|
||||||
|
* r0-r10
|
||||||
|
* returns:
|
||||||
|
* r0 = new kernel base address
|
||||||
|
*/
|
||||||
|
sub r0, lr, $(copy_kernel_lr - start) /* r0 <- current address of start */
|
||||||
|
ldr r1, tt_section_align
|
||||||
|
ands r2, r0, r1 /* If we're already aligned to 1mb, early out */
|
||||||
|
bxeq lr
|
||||||
|
|
||||||
1:
|
mov r2, r0 /* r2 <- r0 <- current address of start */
|
||||||
b 1b @ Halt
|
mov r3, #1
|
||||||
|
lsl r3, r3, #20 /* r3 <- 1mb */
|
||||||
|
add r0, r0, r3
|
||||||
|
bic r0, r0, r1 /* r0 <- new address of start */
|
||||||
|
sub r1, r0, r2 /* r1 <- offset between current and new start */
|
||||||
|
|
||||||
tt_base:
|
/* TODO only copy kernel image sections that aren't zeroed (leave out .bss) */
|
||||||
.word 0x80000000
|
ldr r5, =start
|
||||||
tt_tt_addr:
|
ldr r6, =kernel_end
|
||||||
.word 0x80002000
|
sub r6, r6, r5
|
||||||
tt_tt_val:
|
add r6, r6, r2 /* r6 <- old kernel_end */
|
||||||
.word 0x80000c02 /* ...c02 means read/write at any priviledge level, and that it's a section w/o PXN bit set */
|
add r6, r6, #16
|
||||||
kernel_tt_addr:
|
bic r6, r6, #0xf /* r6 <- old kernel_end (aligned to 16 bytes) */
|
||||||
.word 0x80002004
|
add r5, r6, r1 /* r5 <- new kernel_end */
|
||||||
kernel_tt_val:
|
|
||||||
.word 0x80100c02
|
copy_kernel_loop:
|
||||||
|
/* Copy the kernel to its new location, 16 bytes at a time. We do this
|
||||||
|
* from the end to the begininning so we don't overwrite the old kernel if the
|
||||||
|
* destination and source overlap. */
|
||||||
|
sub r6, r6, #16
|
||||||
|
sub r5, r5, #16
|
||||||
|
ldm r6, {r7, r8, r9, r10}
|
||||||
|
stm r5, {r7, r8, r9, r10}
|
||||||
|
subs r4, r5, r0
|
||||||
|
bne copy_kernel_loop
|
||||||
|
|
||||||
|
add lr, lr, r1 /* Fixup link register for new kernel location */
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
setup_mmu:
|
||||||
|
/*
|
||||||
|
* Calculate the address at which we will store our translation table.
|
||||||
|
* Currently, we store it just past the end of the kernel. Getting the physical
|
||||||
|
* address of the end of the kernel is tricky, since kernel_end is the address
|
||||||
|
* the end of the kernel is linked at, so we have to do a little math.
|
||||||
|
*
|
||||||
|
* arguments:
|
||||||
|
* r0 = current kernel base address (physical), aligned to 1mb boundary
|
||||||
|
* clobbers:
|
||||||
|
* r0-r10
|
||||||
|
*/
|
||||||
|
/* Find future virtual address of the translation table */
|
||||||
|
ldr r1, =kernel_end
|
||||||
|
ldr r2, tt_base_align
|
||||||
|
ands r3, r1, r2
|
||||||
|
mov r3, r1
|
||||||
|
addne r3, r1, r2
|
||||||
|
bic r2, r3, r2 /* r2 <- future virtual address of translation table */
|
||||||
|
str r2, tt_base_virtual
|
||||||
|
|
||||||
|
/* Find physical address of the translation table */
|
||||||
|
ldr r1, =start
|
||||||
|
sub r1, r2, r1
|
||||||
|
add r1, r0, r1 /* r1 <- physical address of translation table */
|
||||||
|
str r1, tt_base_physical
|
||||||
|
|
||||||
|
/* How many sections do we need to map to make sure we have the kernel
|
||||||
|
* and translation table covered? */
|
||||||
|
ldr r3, tt_base_align
|
||||||
|
add r3, r3, r1
|
||||||
|
sub r3, r3, r0
|
||||||
|
lsr r3, r3, #20
|
||||||
|
add r3, r3, #1 /* r3 <- number of sections to map */
|
||||||
|
|
||||||
|
ldr r4, =start /* r4 <- kernel virtual start address */
|
||||||
|
lsr r5, r4, #18 /* 18 = 20 (1mb) - 2 (4 bytes per entry) */
|
||||||
|
add r5, r5, r1 /* r5 <- address of translation page entry for first kernel section (final mapping) */
|
||||||
|
|
||||||
|
mov r6, r0 /* r6 <- kernel physical start address */
|
||||||
|
lsr r7, r6, #18 /* 18 = 20 (1mb) - 2 (4 bytes per entry) */
|
||||||
|
add r7, r7, r1 /* r7 <- address of translation page entry for first kernel section (initial, 1:1 mapping) */
|
||||||
|
|
||||||
|
mov r8, #1
|
||||||
|
lsl r8, r8, #20 /* r8 <- 1mb */
|
||||||
|
mov r9, #0xc
|
||||||
|
lsl r9, r9, #8
|
||||||
|
orr r9, r9, #2 /* r9 <- 0xc02, which means read/write at any priviledge level, and that it's a section w/o PXN bit set */
|
||||||
|
|
||||||
|
initial_tt_loop:
|
||||||
|
/* Setup translation table entries for the translation table and kernel (domain 0) */
|
||||||
|
ldr r10, tt_section_align
|
||||||
|
bic r10, r6, r10
|
||||||
|
orr r10, r10, r9 /* r9=0xc02, which means read/write at any priviledge level */
|
||||||
|
str r10, [r7]
|
||||||
|
str r10, [r5]
|
||||||
|
|
||||||
|
add r6, r6, r8
|
||||||
|
add r7, r7, #4
|
||||||
|
add r5, r5, #4
|
||||||
|
|
||||||
|
subs r3, r3, #1
|
||||||
|
bne initial_tt_loop
|
||||||
|
|
||||||
|
mcr p15, 0, r1, c2, c0, 0 /* TTBR0 <- physical address of translation table */
|
||||||
|
|
||||||
|
/* Set access permissions for domain 0 to "Manager" */
|
||||||
|
mov r1, #0x3
|
||||||
|
mcr p15, 0, r1, c3, c0, 0 /* DACR */
|
||||||
|
|
||||||
|
/* Enable the MMU */
|
||||||
|
mrc p15, 0, r1, c1, c0, 0 /* SCTLR */
|
||||||
|
orr r1, r1, #0x1
|
||||||
|
mcr p15, 0, r1, c1, c0, 0 /* SCTLR */
|
||||||
|
|
||||||
|
/* Update lr for new memory mapping */
|
||||||
|
ldr r1, =start
|
||||||
|
sub r0, r1, r0
|
||||||
|
add lr, lr, r0
|
||||||
|
|
||||||
|
bx lr /* Finally, we jump into the new memory mapping, which matches where we were linked */
|
||||||
|
|
||||||
|
tt_base_align:
|
||||||
|
.word 0b111111111111111 /* 16k - 1 */
|
||||||
|
tt_section_align:
|
||||||
|
.word 0b11111111111111111111 /* 1mb - 1 */
|
||||||
|
.globl tt_base_virtual
|
||||||
|
tt_base_virtual:
|
||||||
|
.word 0
|
||||||
|
.globl tt_base_physical
|
||||||
|
tt_base_physical:
|
||||||
|
.word 0
|
||||||
|
.globl atags_ptr
|
||||||
|
atags_ptr:
|
||||||
|
.word 0
|
||||||
|
.globl machine_type
|
||||||
|
machine_type:
|
||||||
|
.word 0
|
||||||
|
|
||||||
|
.comm stack, 0x10000 /* Reserve 64k for the stack in .bss */
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef MMU_H
|
#ifndef MMU_H
|
||||||
#define MMU_H
|
#define MMU_H
|
||||||
|
|
||||||
|
extern unsigned int *kernel_start_phys, *kernel_start_virt, *kernel_end_phys, *kernel_end_virt;
|
||||||
|
|
||||||
void mmu_reinit();
|
void mmu_reinit();
|
||||||
|
|
||||||
#endif /* MMU_H */
|
#endif /* MMU_H */
|
||||||
|
31
kernel/mmu.c
31
kernel/mmu.c
@ -10,20 +10,41 @@
|
|||||||
#define cp_read(var, ...) _cp_read(var, __VA_ARGS__)
|
#define cp_read(var, ...) _cp_read(var, __VA_ARGS__)
|
||||||
#define cp_write(var, ...) _cp_write(var, __VA_ARGS__)
|
#define cp_write(var, ...) _cp_write(var, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define TT_BASE_SIZE (1<<14) /* 16k */
|
||||||
|
|
||||||
|
unsigned int *kernel_start_phys, *kernel_start_virt, *kernel_end_phys, *kernel_end_virt;
|
||||||
|
|
||||||
void mmu_reinit() {
|
void mmu_reinit() {
|
||||||
unsigned int *curr_tt_entry;
|
extern unsigned int tt_base_virtual, tt_base_physical, start;
|
||||||
unsigned int curr_addr;
|
unsigned int curr_addr;
|
||||||
|
unsigned int *curr_tt_entry;
|
||||||
|
int virt_phys_offset;
|
||||||
|
|
||||||
|
virt_phys_offset = tt_base_virtual - tt_base_physical;
|
||||||
|
kernel_start_virt = &start;
|
||||||
|
kernel_start_phys = kernel_start_virt - virt_phys_offset/4;
|
||||||
|
kernel_end_virt = (unsigned int *)(tt_base_virtual + TT_BASE_SIZE);
|
||||||
|
kernel_end_phys = (unsigned int *)(tt_base_physical + TT_BASE_SIZE);
|
||||||
|
|
||||||
//get the current translation table base address
|
//get the current translation table base address
|
||||||
cp_read(curr_tt_entry, TTBR0);
|
curr_tt_entry = (unsigned int *)tt_base_virtual;
|
||||||
|
|
||||||
//do first loop iteration outside the loop, because we have to check against wrapping back around to know we're done
|
//do first loop iteration outside the loop, because we have to check against wrapping back around to know we're done
|
||||||
*curr_tt_entry = 0xc02;
|
*curr_tt_entry = 0xc02; /* 0xc02 means read/write at any priviledge level, and that it's a section w/o PXN bit set */
|
||||||
curr_tt_entry++;
|
curr_tt_entry++;
|
||||||
|
|
||||||
//create identity mapping for entire address space using sections
|
//create identity mapping for entire address space using sections.
|
||||||
|
//BUT, if we've relocated the kernel from where it is in physical
|
||||||
|
//memory, make sure we keep those mappings correct, and we'll actually
|
||||||
|
//swap the twp mappings so all of memory is addressable.
|
||||||
for (curr_addr = 0x00100000; curr_addr != 0; curr_addr += 0x00100000) {
|
for (curr_addr = 0x00100000; curr_addr != 0; curr_addr += 0x00100000) {
|
||||||
*curr_tt_entry = curr_addr | 0xc02;
|
if ((unsigned int *)curr_addr >= kernel_start_phys && (unsigned int *)curr_addr < kernel_end_phys) {
|
||||||
|
*curr_tt_entry = (curr_addr + virt_phys_offset) | 0xc02;
|
||||||
|
} else if ((unsigned int *)curr_addr >= kernel_start_virt && (unsigned int *)curr_addr < kernel_end_virt) {
|
||||||
|
*curr_tt_entry = (curr_addr - virt_phys_offset) | 0xc02;
|
||||||
|
} else {
|
||||||
|
*curr_tt_entry = curr_addr | 0xc02;
|
||||||
|
}
|
||||||
curr_tt_entry++;
|
curr_tt_entry++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,6 @@
|
|||||||
#include <framebuffer.h>
|
#include <framebuffer.h>
|
||||||
#include <console.h>
|
#include <console.h>
|
||||||
|
|
||||||
extern const unsigned int kernel_end;
|
|
||||||
|
|
||||||
struct fb myfb;
|
struct fb myfb;
|
||||||
|
|
||||||
void video(void) {
|
void video(void) {
|
||||||
@ -64,7 +62,8 @@ int main(void) {
|
|||||||
//setup memory
|
//setup memory
|
||||||
mm_init();
|
mm_init();
|
||||||
mm_add_free_region((void*)0x60000000, (void*)0x7FFFFFFF);
|
mm_add_free_region((void*)0x60000000, (void*)0x7FFFFFFF);
|
||||||
lower = (char*) &kernel_end;
|
mm_add_free_region((void*)0x80000000, (void*)0x800FFFFF);
|
||||||
|
lower = (char*) &kernel_end_virt;
|
||||||
if ((unsigned int)lower % MM_PAGE_SIZE != 0)
|
if ((unsigned int)lower % MM_PAGE_SIZE != 0)
|
||||||
lower += (MM_PAGE_SIZE - ((unsigned int)lower % MM_PAGE_SIZE));
|
lower += (MM_PAGE_SIZE - ((unsigned int)lower % MM_PAGE_SIZE));
|
||||||
mm_add_free_region((void*)lower, (void*)0x9FFFFFFF); //subtract the memory used by the kernel
|
mm_add_free_region((void*)lower, (void*)0x9FFFFFFF); //subtract the memory used by the kernel
|
||||||
|
Loading…
Reference in New Issue
Block a user