1ac022732e3a23902e33c4f8cd8e6febcf9c8bc6
[barrelfish] / kernel / arch / xscale / 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_SVC
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         ldr     r3, =0xfff00000                 // Map ATAG headers 1:1
145         ldr     r0, =0x2c000100
146         and     r2, r0, r3
147         mov     r1, r2
148         mov     r0, r8
149         bl      section_map
150
151
152 $start_mmu_config:
153         mcr     p15, 0, r8, c2, c0, 0           // Load TTBR with L1
154         ldr     r0, =0x55555555                 // Initial domain permissions - all client [checked]
155         mcr     p15, 0, r0, c3, c0, 0
156
157         ldr     lr, =$start_with_mmu_enabled    // Address to continue at when paging is enabled
158         ldr     r0, =KERNEL_OFFSET
159         add     sp, sp, r0                      // Prepare stack for relocation
160
161 //      ldr     r1, =0x1007                     // Enable: D-Cache, I-Cache, Alignment, MMU
162         ldr     r1, =0x1005                     // Enable: D-Cache, I-Cache, MMU
163         mrc     p15, 0, r0, c1, c0, 0
164         orr     r0, r0, r1
165         mcr     p15, 0, r0, c1, c0, 0           // MMU is enabled
166         mov     pc, lr                          // Flat fetched.
167         mov     r0, r0                          // Flat fetched.
168
169         // Up until this point PC is in ELF file.
170 $start_with_mmu_enabled:
171         // MMU is enabled and PC is in the loaded ELF image.
172
173         mov     r1, #0                          // Unmap section with VA = 0
174         mov     r0, r8                          // r0 = page table address
175         bl      section_unmap
176
177         mov     r1, #0
178         mcr     p15, 0, r1, c8, c7, 0           // Invalidate ID-TLB entry for section with VA = 0.
179
180 $start_set_got_register:
181         ldr     PIC_REGISTER, =got_base
182
183
184 $start_set_init_arguments:
185         ldmfd   sp!, {r0, r1}                   // r0 = board id
186                                                 // r1 = paddr of kern args, already mapped 1:1 to vaddr
187         ldr     r2, =KERNEL_OFFSET              // Convert paddr's to vaddr's
188         add     r3, alloc_top, r2               // r3 = alloc_top
189         add     r2, r4, r2                      // r2 = addr kernel ELF file
190         mov     lr, #0
191         b       arch_init
192
193 /**
194  * bool elf_header_is_valid(struct Elf32_EHdr*)
195  *
196  * A cursory check of ELF header. nb first word is known to be invalid
197  */
198 elf_header_is_valid:
199         ldr     r1, [r0, #4]!                   // e_ident[4..7]
200         ldr     r2, =0x00010101
201         eors    r3, r1, r2
202         bne     $done
203         ldr     r1, [r0, #4]!                   // e_ident[8..11]
204         mov     r2, #0
205         eors    r3, r1, r2
206         bne     $done
207         ldr     r1, [r0, #4]!                   // e_ident[12..15]
208         eors    r3, r1, r2
209         bne     $done
210         ldr     r1, [r0, #4]!                   // (e_type, e_machine)
211         ldr     r2, =0x00280002
212         eors    r3, r1, r2
213         bne     $done
214         ldr     r1, [r0, #4]!                   // e_version
215         mov     r2, #1
216         eors    r3, r1, r2
217 $done:
218         mvn     r3, r3
219         bx      lr
220
221 /**
222   * uint32_t elf_get_file_size(struct Elf32_EHdr*)
223   */
224 elf_get_file_size:
225         ldr     r1, [r0, #32]                   // r1 = offset of sections
226         ldrh    r2, [r0, #46]                   // r2 = e_shentsize
227         ldrh    r3, [r0, #48]                   // r3 = e_shnum
228         mul     r0, r2, r3
229         add     r0, r0, r1
230         bx      lr
231
232 /**
233   * (vaddr_t, size_t) elf_get_expanded_limits(struct Elf32_EHdr*)
234   */
235 elf_get_expanded_limits:
236         stmfd   sp!, {r4-r6, lr}
237         mov     r5, #0                          // r5 = max vaddr
238         sub     r4, r5, #1                      // r4 = min vaddr
239         ldr     r1, [r0, #28]                   // r1 = e_phoff
240         ldrh    r2, [r0, #42]                   // r2 = e_phentsize
241         ldrh    r3, [r0, #44]                   // r3 = e_phnum
242         add     r1, r1, r0                      // r1 = start of prog headers
243         mul     r0, r2, r3                      // r0 = size of prog headers
244         add     r3, r0, r1                      // r3 = end of prog headers
245         b       $looptest
246 $loopstart:
247         ldr     r6, [r1, #20]                   // r6 = memsz
248         cmp     r6, #0
249         beq     $loopinc                        // SKIP If memsz = 0
250         ldr     r0, [r1, #8]                    // r0 = vaddr
251         cmp     r0, r4
252         movlo   r4, r0                          // r4 = min(r4, vaddr)
253         add     r6, r0, r6                      // r0 = vaddr + memsz
254         cmp     r6, r5
255         movhs   r5, r6                          // r5 = max(r5, vaddr + memsz)
256         ldr     r0, [r1, #28]                   // r0 = alignment
257 $loopinc:
258         add     r1, r1, r2
259 $looptest:
260         cmp     r1, r3
261         bne     $loopstart
262         mov     r0, r4
263         mov     r1, r5
264         ldmfd   sp!, {r4-r6, pc}
265
266 /**
267  * void elf_expand(Struct Elf32_EHdr*, vaddr_t kernel_v, paddr_t kernel_p)
268  */
269 elf_expand:
270         stmfd   sp!, {r4-r7, lr}
271         ldr     r3, [r0, #28]                   // r3 = e_phoff
272         ldrh    r4, [r0, #44]                   // r4 = e_phnum
273         add     r3, r0, r3                      // r3 = addr phdr[0]
274 $elf_expand_start:
275         subs    r4, r4, #1
276         beq     $elf_expand_done
277         ldr     r5, [r3, #4]                    // r5 = p_offset
278         cmp     r5, #0
279         beq     $elf_expand_next
280         ldr     r6, [r3, #8]                    // r6 = p_vaddr
281         cmp     r6, #0
282         beq     $elf_expand_next
283         stmfd   sp!, {r0-r3}                    // Save scratch registers
284         mov     r7, r1                          // r7 = kernel_v
285         add     r1, r0, r5                      // r1 = ptr to segment in file
286         sub     r0, r6, r7                      // r0 = kernel_v_offset
287         add     r0, r0, r2                      // r0 = phys addr of segment
288         ldr     r2, [r3, #16]                   // r2 = p_filesz
289         bl      memcpy
290         ldmfd   sp!, {r0-r3}                    // Restore scratch registers
291 $elf_expand_next:
292         ldrh    r5, [r0, #42]                   // r5 = e_phentsize
293         add     r3, r3, r5                      // r3 = addr phdr[next]
294         b       $elf_expand_start
295 $elf_expand_done:
296         ldmfd   sp!, {r4-r7, pc}
297
298 /**
299  * void memzero(uintptr_t addr, size_t bytes)
300  * Assumes addr is 4-byte aligned and bytes is a multiple of 4.
301  */
302 memzero:
303         subs    r1, #4
304         blt     $zero0
305         mov     r2, #0
306         mov     r3, #0
307         subs    r1, #12
308         blt     $zero8
309 $zero16:
310         stmia   r0!, {r2-r3}
311         subs    r1, r1, #16
312         stmia   r0!, {r2-r3}
313         bge     $zero16
314 $zero8:
315         adds    r1, r1, #8
316         stmgeia r0!, {r2, r3}
317         subge   r1, #8
318 $zero4:
319         adds    r1, r1, #4
320         stmgeia r0!, {r2}
321 $zero0:
322         bx      lr
323
324 /**
325  * void memcpy(uintptr_t dest, uintptr_t src, size_t bytes)
326  *
327  * Assumes addr is 4-byte aligned and bytes is a multiple of 4.
328  */
329 memcpy:
330         stmfd   sp!, {r4, r5, lr}
331         subs    r2, #16
332         blt     $cpy8
333 $cpy16:
334         ldmia   r1!, {r4-r5}
335         stmia   r0!, {r4-r5}
336         subs    r2, r2, #16
337         ldmia   r1!, {r4-r5}
338         stmia   r0!, {r4-r5}
339         bge     $cpy16
340 $cpy8:
341         adds    r2, r2, #8
342         ldmgeia r1!, {r4, r5}
343         subge   r2, #8
344         stmgeia r0!, {r4, r5}
345 $cpy4:
346         adds    r2, r2, #4
347         ldrge   r4, [r0], #4
348         strge   r4, [r1], #4
349 $cpy0:
350         ldmfd   sp!, {r4, r5, pc}
351
352 /**
353  * Allocate Physical Memory.
354  *
355  * uintptr_t alloc_phys(size_t bytes, size_t align)
356  */
357 alloc_phys:
358         sub     r1, r1, #1
359         mov     r3, alloc_top
360         add     r3, r3, r1                      // Align start address
361         bic     r3, r3, r1                      // r3 = alloc address
362         add     r0, r0, r3                      // r0 = new alloc_top value
363         mov     alloc_top, r0
364         stmfd   sp!, {r3, lr}
365         sub     r1, r0, r3
366         mov     r0, r3
367         bl      memzero
368         ldmfd   sp!, {r0, pc}
369
370 /**
371  * void section_map(L1PageTable *p, vaddr_t v, paddr_t p)
372  */
373 section_map:
374         lsr     r1, r1, #20                     // r1 is table offset
375         lsr     r2, r2, #20
376         ldr     r3, =0x41e                      // AP = 01, Domain = 0, CB, Section
377         orr     r2, r3, r2, LSL #20             // r2 = Section Entry
378         str     r2, [r0, r1, LSL #2]            // table[v >> 20] = r2
379         bx      lr
380
381 /**
382  * void section_unmap(L1PageTable *p, vaddr_t v)
383  */
384 section_unmap:
385         mov     r2, #0                          // Invalid L1 PTE
386         str     r2, [r0, r1, LSL #18]
387         bx      lr
388
389 /**
390  * extern "C" void halt(void) __attribute__((noreturn))
391  */
392 halt:
393         b       .
394
395 .ltorg
396
397 got_base:
398         .word                                   // Initialized by linker
399
400         .end