You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

190 lines
5.9 KiB

/*
Copyright (C) 2012, Aaron Lindsay <aaron@aclindsay.com>
This file is part of Aedrix.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* Kernel entry in assembly. This handles relocating the kernel so that it is
* in both physical and virtual memory where we want it to be. We copy the
* kernel to a different physical location if necessary, turn on the MMU,
* setting up a dual-mapping if the kernel is not in physical memory at the
* same place it was linked against. Finally, we jump into the kernel's main()
* function in C using the address it is linked against. When the MMU gets
* initialized fully later, it will remove the initial 1:1 mapping.
*/
.globl start
start:
str r1, machine_type /* Backup atags/machine type registers so we can access them later from C */
str r2, atags_ptr
bl copy_kernel
copy_kernel_lr: /* Used to calculate address at which kernel is currently loaded by copy_kernel */
bl setup_mmu
ldr sp, =stack+0x10000 /* Set up the stack */
bl main
1:
b 1b /* Halt */
copy_kernel:
/*
* 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
mov r2, r0 /* r2 <- r0 <- current address of start */
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 */
/* TODO only copy kernel image sections that aren't zeroed (leave out .bss) */
ldr r5, =start
ldr r6, =kernel_end
sub r6, r6, r5
add r6, r6, r2 /* r6 <- old kernel_end */
add r6, r6, #16
bic r6, r6, #0xf /* r6 <- old kernel_end (aligned to 16 bytes) */
add r5, r6, r1 /* r5 <- new kernel_end */
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 */