29be5332680204e286b45263bba1bc46373b5439
[barrelfish] / kernel / arch / armv5 / boot.S
1 /**
2  * \file
3  * \brief Bootstrap the kernel.
4  */
5 /*
6  * Copyright (c) 2009 ETH Zurich.
7  * All rights reserved.
8  *
9  * This file is distributed under the terms in the attached LICENSE file.
10  * If you do not find this file, copies can be found by writing to:
11  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
12  */
13
14 #ifndef __ASSEMBLER__
15 #define __ASSEMBLER__   1
16 #endif
17
18 #include <barrelfish_kpi/flags_arch.h> // ARM_MODE_MASK
19 #include <offsets.h> // BOOT_STACK_PHYS
20         
21         .text
22         .arm
23
24         .globl start, halt, got_base
25
26         // Used to track phys memory allocator limit globally.
27         alloc_top .req r11
28
29 start:
30         // Entry constraints same as ARM Linux so QEMU can boot
31         // this file directly and to make it easy to use existing
32         // loaders on real h/w.
33         //
34         // The build process has built an ELF kernel image. The first
35         // word is overwritten to branch to entry point, `start', in
36         // the text section of the unpacked ELF file. The start routine
37         // then loads the ELF image, initializes the page table, and
38         // enables paging.
39         //
40         // NB Writing the branch instruction into the first word of the
41         // ELF header causes QEMU to treat the file as a Linux kernel
42         // image and it provides the ATAG_HEADER info.
43         //
44         // On entry:
45         //
46         // MMU disabled
47         // Caches in unknown state, but no lockdown
48         // No TLB lockdown.
49         // CPU is in a priveledged mode.
50         //
51         // r0 contains zero
52         // r1 contains board id
53         // r2 contains pointer to kernel args structure
54         // lr contains pointer to elf header + 4
55
56         sub     lr, lr, #4                      // lr = address of elf header
57         mov     r4, lr                          // r4 = p_elf_header [KEEP]
58         
59         mrs     r3, cpsr                        // Ensure in SYS mode
60         bic     r3, r3, #ARM_MODE_MASK
61         orr     r3, r3, #ARM_MODE_SYS
62         msr     cpsr_c, r3
63
64         mov     sp, #BOOT_STACK_PHYS
65         stmfd   sp!, {r1, r2}
66
67         /* Validate ELF header */
68         mov     r0, r4
69         bl      elf_header_is_valid
70         cmp     r0, #0
71         beq     .
72
73         /* Compute size of elf file */
74         mov     r0, r4                          // r0 = p_elf_header
75         bl      elf_get_file_size               // r0 = elf_file_size
76         mov     r5, r0                          // r5 = elf_file_size [KEEP]
77
78         mov     r0, r4
79         bl      elf_get_expanded_limits
80
81         mov     r6, r0                          // r6 = kernel v_addr [KEEP]
82         mov     r7, r1                          // r7 = kernel v_limit [KEEP]
83
84         /* Want to map low 1MB section with 1:1 to kernel address space */
85         mov     r2, #(512 * 1024 * 1024)
86         cmp     pc, r2
87         bhs     $start_rom_image
88
89         /*
90         The ELF image is in RAM, copy it just above the
91         physical pages of the unpacked kernel (1:1 mapping
92         between kernel v_addr and p_addr).
93          */
94
95         ldr     r3, =4095                       // Page align kernel v_limit
96         add     r0, r7, r3
97         orr     r3, r3, r3, LSL #20
98         bic     r0, r0, r3                      // r0 = new ELF file physical address
99         mov     r1, r4                          // r1 = old ELF file physical address
100         mov     r2, r5                          // r2 = ELF size
101         sub     r8, r0, r1                      // r8 = delta (new, old) ELF physical address
102         bl      memcpy
103
104         add     r4, r4, r8                      // Update r4, p_elf_header
105         sub     r8, r8, #4
106         add     pc, pc, r8                      // Jump to next line, but in relocated ELF image.
107
108 $start_rom_image:
109         // NOP
110
111 $start_expand_elf:
112         // Zero physical region
113         sub     r1, r7, r6                      // r1 = expanded ELF size
114         ldr     r2, =0xfff00000
115         bic     r0, r6, r2                      // r0 = physical address of expanded image
116         bl      memzero
117
118         // Expand file
119         ldr     r2, =0xfff00000
120         bic     r2, r6, r2                      // r2 = kernel p_addr
121         mov     r1, r6                          // r1 = kernel v_addr
122         mov     r0, r4                          // r0 = elf_header
123         bl      elf_expand
124
125         add     alloc_top, r4, r5               // alloc_top = end of ELF file.
126
127         mov     r0, #16384                      // allocate L1 page table (16K aligned to 16K).
128         mov     r1, r0
129         bl      alloc_phys
130         mov     r8, r0                          // r8 = L1 page table [KEEP]
131
132 $start_map_kernel:
133         ldr     r3, =0xfff00000                 // Map kernel section to physical section
134         and     r1, r6, r3
135         and     r2, sp, r3
136         bl      section_map                     // section_map (l1_addr, v_addr, p_addr)
137
138         ldr     r3, =0xfff00000                 // Map section containing program counter 1:1
139         and     r2, pc, r3
140         mov     r1, r1
141         mov     r0, r8
142         bl      section_map
143
144 $start_mmu_config:
145         mcr     p15, 0, r8, c2, c0, 0           // Load TTBR with L1
146         ldr     r0, =0x55555555                 // Initial domain permissions - all client [checked]
147         mcr     p15, 0, r0, c3, c0, 0
148
149         ldr     lr, =$start_with_mmu_enabled    // Address to continue at when paging is enabled
150         ldr     r0, =KERNEL_OFFSET
151         add     sp, sp, r0                      // Prepare stack for relocation
152
153         ldr     r1, =0x1007                     // Enable: D-Cache, I-Cache, Alignment, MMU
154         mrc     p15, 0, r0, c1, c0, 0
155         orr     r0, r0, r1
156         mcr     p15, 0, r0, c1, c0, 0           // MMU is enabled
157         mov     pc, lr                          // Flat fetched.
158         mov     r0, r0                          // Flat fetched.
159
160         // Up until this point PC is in ELF file.
161 $start_with_mmu_enabled:
162         // MMU is enabled and PC is in the loaded ELF image.
163
164         mov     r1, #0                          // Unmap section with VA = 0
165         mov     r0, r8                          // r0 = page table address
166         bl      section_unmap
167
168         mov     r1, #0
169         mcr     p15, 0, r1, c8, c7, 0           // Invalidate ID-TLB entry for section with VA = 0.
170
171 $start_set_got_register:
172         ldr     PIC_REGISTER, =got_base
173
174 $start_set_init_arguments:
175         ldmfd   sp!, {r0, r1}                   // r0 = board id
176                                                 // r1 = paddr of kern args
177         ldr     r2, =KERNEL_OFFSET              // Convert paddr's to vaddr's
178         add     r1, r1, r2                      // r1 is now vaddr of kern args
179         add     r3, alloc_top, r2               // r3 = alloc_top
180         add     r2, r4, r2                      // r2 = addr kernel ELF file
181         mov     lr, #0
182         b       arch_init
183
184 /**
185  * bool elf_header_is_valid(struct Elf32_EHdr*)
186  *
187  * A cursory check of ELF header. nb first word is known to be invalid
188  */
189 elf_header_is_valid:
190         ldr     r1, [r0, #4]!                   // e_ident[4..7]
191         ldr     r2, =0x00010101
192         eors    r3, r1, r2
193         bne     $done
194         ldr     r1, [r0, #4]!                   // e_ident[8..11]
195         mov     r2, #0
196         eors    r3, r1, r2
197         bne     $done
198         ldr     r1, [r0, #4]!                   // e_ident[12..15]
199         eors    r3, r1, r2
200         bne     $done
201         ldr     r1, [r0, #4]!                   // (e_type, e_machine)
202         ldr     r2, =0x00280002
203         eors    r3, r1, r2
204         bne     $done
205         ldr     r1, [r0, #4]!                   // e_version
206         mov     r2, #1
207         eors    r3, r1, r2
208 $done:
209         mvn     r3, r3
210         bx      lr
211
212 /**
213   * uint32_t elf_get_file_size(struct Elf32_EHdr*)
214   */
215 elf_get_file_size:
216         ldr     r1, [r0, #32]                   // r1 = offset of sections
217         ldrh    r2, [r0, #46]                   // r2 = e_shentsize
218         ldrh    r3, [r0, #48]                   // r3 = e_shnum
219         mul     r0, r2, r3
220         add     r0, r0, r1
221         bx      lr
222
223 /**
224   * (vaddr_t, size_t) elf_get_expanded_limits(struct Elf32_EHdr*)
225   */
226 elf_get_expanded_limits:
227         stmfd   sp!, {r4-r6, lr}
228         mov     r5, #0                          // r5 = max vaddr
229         sub     r4, r5, #1                      // r4 = min vaddr
230         ldr     r1, [r0, #28]                   // r1 = e_phoff
231         ldrh    r2, [r0, #42]                   // r2 = e_phentsize
232         ldrh    r3, [r0, #44]                   // r3 = e_phnum
233         add     r1, r1, r0                      // r1 = start of prog headers
234         mul     r0, r2, r3                      // r0 = size of prog headers
235         add     r3, r0, r1                      // r3 = end of prog headers
236         b       $looptest
237 $loopstart:
238         ldr     r6, [r1, #20]                   // r6 = memsz
239         cmp     r6, #0
240         beq     $loopinc                        // SKIP If memsz = 0
241         ldr     r0, [r1, #8]                    // r0 = vaddr
242         cmp     r0, r4
243         movlo   r4, r0                          // r4 = min(r4, vaddr)
244         add     r6, r0, r6                      // r0 = vaddr + memsz
245         cmp     r6, r5
246         movhs   r5, r6                          // r5 = max(r5, vaddr + memsz)
247         ldr     r0, [r1, #28]                   // r0 = alignment
248 $loopinc:
249         add     r1, r1, r2
250 $looptest:
251         cmp     r1, r3
252         bne     $loopstart
253         mov     r0, r4
254         mov     r1, r5
255         ldmfd   sp!, {r4-r6, pc}
256
257 /**
258  * void elf_expand(Struct Elf32_EHdr*, vaddr_t kernel_v, paddr_t kernel_p)
259  */
260 elf_expand:
261         stmfd   sp!, {r4-r7, lr}
262         ldr     r3, [r0, #28]                   // r3 = e_phoff
263         ldrh    r4, [r0, #44]                   // r4 = e_phnum
264         add     r3, r0, r3                      // r3 = addr phdr[0]
265 $elf_expand_start:
266         subs    r4, r4, #1
267         beq     $elf_expand_done
268         ldr     r5, [r3, #4]                    // r5 = p_offset
269         cmp     r5, #0
270         beq     $elf_expand_next
271         ldr     r6, [r3, #8]                    // r6 = p_vaddr
272         cmp     r6, #0
273         beq     $elf_expand_next
274         stmfd   sp!, {r0-r3}                    // Save scratch registers
275         mov     r7, r1                          // r7 = kernel_v
276         add     r1, r0, r5                      // r1 = ptr to segment in file
277         sub     r0, r6, r7                      // r0 = kernel_v_offset
278         add     r0, r0, r2                      // r0 = phys addr of segment
279         ldr     r2, [r3, #16]                   // r2 = p_filesz
280         bl      memcpy
281         ldmfd   sp!, {r0-r3}                    // Restore scratch registers
282 $elf_expand_next:
283         ldrh    r5, [r0, #42]                   // r5 = e_phentsize
284         add     r3, r3, r5                      // r3 = addr phdr[next]
285         b       $elf_expand_start
286 $elf_expand_done:
287         ldmfd   sp!, {r4-r7, pc}
288
289 /**
290  * void memzero(uintptr_t addr, size_t bytes)
291  * Assumes addr is 4-byte aligned and bytes is a multiple of 4.
292  */
293 memzero:
294         subs    r1, #4
295         blt     $zero0
296         mov     r2, #0
297         mov     r3, #0
298         subs    r1, #12
299         blt     $zero8
300 $zero16:
301         stmia   r0!, {r2-r3}
302         subs    r1, r1, #16
303         stmia   r0!, {r2-r3}
304         bge     $zero16
305 $zero8:
306         adds    r1, r1, #8
307         stmgeia r0!, {r2, r3}
308         subge   r1, #8
309 $zero4:
310         adds    r1, r1, #4
311         stmgeia r0!, {r2}
312 $zero0:
313         bx      lr
314
315 /**
316  * void memcpy(uintptr_t dest, uintptr_t src, size_t bytes)
317  *
318  * Assumes addr is 4-byte aligned and bytes is a multiple of 4.
319  */
320 memcpy:
321         stmfd   sp!, {r4, r5, lr}
322         subs    r2, #16
323         blt     $cpy8
324 $cpy16:
325         ldmia   r1!, {r4-r5}
326         stmia   r0!, {r4-r5}
327         subs    r2, r2, #16
328         ldmia   r1!, {r4-r5}
329         stmia   r0!, {r4-r5}
330         bge     $cpy16
331 $cpy8:
332         adds    r2, r2, #8
333         ldmgeia r1!, {r4, r5}
334         subge   r2, #8
335         stmgeia r0!, {r4, r5}
336 $cpy4:
337         adds    r2, r2, #4
338         ldrge   r4, [r0], #4
339         strge   r4, [r1], #4
340 $cpy0:
341         ldmfd   sp!, {r4, r5, pc}
342
343 /**
344  * Allocate Physical Memory.
345  *
346  * uintptr_t alloc_phys(size_t bytes, size_t align)
347  */
348 alloc_phys:
349         sub     r1, r1, #1
350         mov     r3, alloc_top
351         add     r3, r3, r1                      // Align start address
352         bic     r3, r3, r1                      // r3 = alloc address
353         add     r0, r0, r3                      // r0 = new alloc_top value
354         mov     alloc_top, r0
355         stmfd   sp!, {r3, lr}
356         sub     r1, r0, r3
357         mov     r0, r3
358         bl      memzero
359         ldmfd   sp!, {r0, pc}
360
361 /**
362  * void section_map(L1PageTable *p, vaddr_t v, paddr_t p)
363  */
364 section_map:
365         lsr     r1, r1, #20                     // r1 is table offset
366         lsr     r2, r2, #20
367         ldr     r3, =0x41e                      // AP = 01, Domain = 0, CB, Section
368         orr     r2, r3, r2, LSL #20             // r2 = Section Entry
369         str     r2, [r0, r1, LSL #2]            // table[v >> 20] = r2
370         bx      lr
371
372 /**
373  * void section_unmap(L1PageTable *p, vaddr_t v)
374  */
375 section_unmap:
376         mov     r2, #0                          // Invalid L1 PTE
377         str     r2, [r0, r1, LSL #18]
378         bx      lr
379
380 /**
381  * extern "C" void halt(void) __attribute__((noreturn))
382  */
383 halt:
384         b       .
385
386 .ltorg
387
388 got_base:
389         .word                                   // Initialized by linker
390
391         .end