]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/arm64/machdep_boot.c
MFV 354917, 354918, 354919
[FreeBSD/FreeBSD.git] / sys / arm64 / arm64 / machdep_boot.c
1 /*-
2  * Copyright (c) 2019 Juniper Networks, Inc
3  * Copyright (c) 2004 Olivier Houchard
4  * Copyright (c) 1994-1998 Mark Brinicombe.
5  * Copyright (c) 1994 Brini.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  */
30
31 #include "opt_platform.h"
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/ctype.h>
39 #include <sys/linker.h>
40 #include <sys/reboot.h>
41 #include <sys/sysctl.h>
42
43 #include <machine/cpu.h>
44 #include <machine/machdep.h>
45 #include <machine/metadata.h>
46 #include <machine/vmparam.h>
47
48 #ifdef FDT
49 #include <contrib/libfdt/libfdt.h>
50 #include <dev/fdt/fdt_common.h>
51 #include <dev/ofw/ofw_subr.h>
52 #include <dev/ofw/openfirm.h>
53 #include <machine/pte.h>
54 #include <machine/vm.h>
55 #include <sys/boot.h>
56 #endif
57
58 #ifdef FDT
59 #define PRELOAD_PUSH_VALUE(type, value) do {                    \
60         *(type *)(preload_ptr + preload_size) = (value);        \
61         preload_size += sizeof(type);                           \
62 } while (0)
63
64 #define PRELOAD_PUSH_STRING(str) do {                           \
65         uint32_t ssize;                                         \
66         ssize = strlen(str) + 1;                                \
67         PRELOAD_PUSH_VALUE(uint32_t, ssize);                    \
68         strcpy((char*)(preload_ptr + preload_size), str);       \
69         preload_size += ssize;                                  \
70         preload_size = roundup(preload_size, sizeof(u_long));   \
71 } while (0)
72
73 static int build_l2_block_pagetable(vm_offset_t, uint64_t,
74     struct arm64_bootparams *);
75
76 #define INITRD_START    "linux,initrd-start"
77 #define INITRD_END      "linux,initrd-end"
78 #define KENV_SIZE       2048
79
80 static char     static_kenv[KENV_SIZE];
81 static caddr_t  metadata_endptr;
82 #endif
83
84 #define PMAP_BOOTSTRAP_PAGES    2
85
86 extern vm_offset_t end;
87
88 /*
89  * Fake up a boot descriptor table
90  */
91 static vm_offset_t
92 fake_preload_metadata(void *dtb_ptr, size_t dtb_size,
93     struct arm64_bootparams *abp)
94 {
95         vm_offset_t     lastaddr;
96         static char     fake_preload[256];
97         caddr_t         preload_ptr;
98         size_t          preload_size;
99
100         preload_ptr = (caddr_t)&fake_preload[0];
101         preload_size = 0;
102
103         PRELOAD_PUSH_VALUE(uint32_t, MODINFO_NAME);
104         PRELOAD_PUSH_STRING("kernel");
105
106         PRELOAD_PUSH_VALUE(uint32_t, MODINFO_TYPE);
107         PRELOAD_PUSH_STRING("elf kernel");
108
109         PRELOAD_PUSH_VALUE(uint32_t, MODINFO_ADDR);
110         PRELOAD_PUSH_VALUE(uint32_t, sizeof(vm_offset_t));
111         PRELOAD_PUSH_VALUE(uint64_t, VM_MIN_KERNEL_ADDRESS);
112
113         PRELOAD_PUSH_VALUE(uint32_t, MODINFO_SIZE);
114         PRELOAD_PUSH_VALUE(uint32_t, sizeof(size_t));
115         PRELOAD_PUSH_VALUE(uint64_t, (size_t)(&end - VM_MIN_KERNEL_ADDRESS));
116
117         lastaddr = (vm_offset_t)&end;
118         lastaddr = roundup(lastaddr, sizeof(vm_offset_t));
119
120 #ifdef FDT
121         if (dtb_ptr != NULL &&
122             (build_l2_block_pagetable(lastaddr, dtb_size, abp) == 0)) {
123                 /* Copy DTB to KVA space and insert it into module chain. */
124                 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_METADATA | MODINFOMD_DTBP);
125                 PRELOAD_PUSH_VALUE(uint32_t, sizeof(uint64_t));
126                 PRELOAD_PUSH_VALUE(uint64_t, (uint64_t)lastaddr);
127                 memmove((void *)lastaddr, dtb_ptr, dtb_size);
128                 lastaddr += dtb_size;
129         }
130
131         lastaddr = roundup(lastaddr, sizeof(vm_offset_t));
132         /* End marker */
133         metadata_endptr = preload_ptr;
134 #endif
135
136         PRELOAD_PUSH_VALUE(uint32_t, 0);
137         PRELOAD_PUSH_VALUE(uint32_t, 0);
138
139         preload_metadata = fake_preload;
140
141         return (lastaddr);
142 }
143
144 /*
145  * Support booting from U-Boot's booti command. If modulep (register x0)
146  * is a valid address then it is a pointer to FDT.
147  */
148 vm_offset_t
149 linux_parse_boot_param(struct arm64_bootparams *abp)
150 {
151         uint32_t                dtb_size = 0;
152         struct fdt_header       *dtb_ptr = NULL;
153
154 #if defined(FDT) && !defined(FDT_DTB_STATIC)
155         /* Test if modulep (x0) point to valid DTB. */
156         dtb_ptr = (struct fdt_header *)abp->modulep;
157         if (dtb_ptr && fdt_check_header(dtb_ptr) == 0)
158                 dtb_size = fdt_totalsize(dtb_ptr);
159 #endif
160         return (fake_preload_metadata(dtb_ptr, dtb_size, abp));
161 }
162
163 #ifdef FDT
164 /*
165  * Builds count 2 MiB page table entries.
166  * During startup, locore.S maps kernel memory in L2 page table.
167  * Create space to copy size bytes following the kernel memory.
168  * See build_l2_block_pagetable in locore.S
169  */
170 static int
171 build_l2_block_pagetable(vm_offset_t lastaddr, uint64_t size,
172     struct arm64_bootparams *abp)
173 {
174         vm_offset_t     l2_block_entry, *l2pt_entry;
175         int32_t         count_2mib;
176         volatile uint64_t       output_bits;
177
178         /* Number of 2MiB pages */
179         count_2mib = ((lastaddr - KERNBASE) + size) >> L2_SHIFT;
180
181         /* All the memory must not cross a 1GiB boundary */
182         if (count_2mib >= Ln_ENTRIES) {
183                 printf("%s: Adding %#lx bytes makes kernel cross 1GiB boundary\n",
184                      __FUNCTION__, size);
185                 return EINVAL;
186         }
187
188         /* size fits within the last 2MiB page table entry */
189         if (((lastaddr - KERNBASE) >> L2_SHIFT) == count_2mib)
190                 return 0;
191
192         /* Build the L2 block entry */
193         l2_block_entry = ATTR_IDX(VM_MEMATTR_WRITE_BACK) | L2_BLOCK | ATTR_AF;
194 #ifdef SMP
195         l2_block_entry |= ATTR_SH(ATTR_SH_IS);
196 #endif
197         /* Number of 2MiB pages mapped to kernel */
198         count_2mib = (lastaddr - KERNBASE) >> L2_SHIFT;
199
200         /* Go to last L2 page table entry. Each pagetable entry is 8 bytes */
201         l2pt_entry = (vm_offset_t*)((abp->kern_l1pt - PAGE_SIZE) +
202             (count_2mib << 3));
203         output_bits = (*l2pt_entry++ >> L2_SHIFT) + 1;
204
205         /* Build count 2MiB page table entries */
206         for (count_2mib = size >> L2_SHIFT; count_2mib >= 0;
207             l2pt_entry++, output_bits++, count_2mib--)
208                 *l2pt_entry = (output_bits << L2_SHIFT) | l2_block_entry;
209
210         return 0;
211 }
212
213 /*
214  * Align start addr to 1GiB boundary and build L1 page table entry for it.
215  * See build_l1_block_pagetable in locore.S
216  */
217 static void
218 build_l1_block_pagetable(vm_offset_t start, struct arm64_bootparams *abp)
219 {
220         vm_offset_t l1_table_idx, l1_block_entry, phy_addr, *l1_table_entry;
221
222         /* Find the table index */
223         l1_table_idx = (start >> L1_SHIFT) & Ln_ADDR_MASK;
224
225         /* Build the L1 block entry */
226         l1_block_entry = ATTR_nG | ATTR_IDX(VM_MEMATTR_UNCACHEABLE) |
227             L1_BLOCK | ATTR_AF;
228 #ifdef SMP
229         l1_block_entry |= ATTR_SH(ATTR_SH_IS);
230 #endif
231
232         /* Set the physical address */
233         phy_addr = l1_block_entry | (l1_table_idx << L1_SHIFT);
234
235         /* Index of L1 pagetable. Each pagetable entry is 8 bytes */
236         l1_table_entry  = (vm_offset_t*)((abp->kern_l0pt + PAGE_SIZE) +
237             (l1_table_idx << 3));
238         *l1_table_entry = phy_addr;
239 }
240
241 /*
242  * Copy the initrd image passed using U-Boot's booti command into
243  * KVA space.
244  */
245 static void
246 linux_load_initrd(vm_offset_t *lastaddr, struct arm64_bootparams *abp)
247 {
248         phandle_t       chosen;
249         uint64_t        initrd_start = 0, initrd_end = 0;
250         uint64_t        initrd_size;
251         caddr_t         preload_ptr;
252         size_t          preload_size = 0;
253
254         if ((chosen = OF_finddevice("/chosen")) == -1)
255                 return;
256
257         if (!(OF_hasprop(chosen, INITRD_START) &&
258             OF_hasprop(chosen, INITRD_END)))
259                 return;
260
261         if ((OF_getprop(chosen, INITRD_START, &initrd_start, sizeof(uint64_t))) > 0)
262                 initrd_start = fdt64_to_cpu(initrd_start);
263
264         if ((OF_getprop(chosen, INITRD_END, &initrd_end, sizeof(uint64_t))) > 0)
265                 initrd_end = fdt64_to_cpu(initrd_end);
266
267         if ((initrd_size = (initrd_end - initrd_start)) <= 0)
268                 return;
269
270         if (build_l2_block_pagetable(*lastaddr, initrd_size, abp) != 0)
271                 return;
272
273         build_l1_block_pagetable(initrd_start, abp);
274
275         /* Copy the initrd image to virtual address space */
276         memmove((void*)(*lastaddr), (void*)initrd_start, initrd_size);
277
278         preload_ptr = metadata_endptr;
279
280         PRELOAD_PUSH_VALUE(uint32_t, MODINFO_NAME);
281         PRELOAD_PUSH_STRING("initrd");
282
283         PRELOAD_PUSH_VALUE(uint32_t, MODINFO_TYPE);
284         PRELOAD_PUSH_STRING("md_image");
285
286         PRELOAD_PUSH_VALUE(uint32_t, MODINFO_SIZE);
287         PRELOAD_PUSH_VALUE(uint32_t, sizeof(uint64_t));
288         PRELOAD_PUSH_VALUE(uint64_t, initrd_size);
289
290         PRELOAD_PUSH_VALUE(uint32_t, MODINFO_ADDR);
291         PRELOAD_PUSH_VALUE(uint32_t, sizeof(vm_offset_t));
292         PRELOAD_PUSH_VALUE(uint64_t, *lastaddr);
293
294         *lastaddr += initrd_size;
295         *lastaddr = roundup(*lastaddr, sizeof(vm_offset_t));
296
297         /* End marker */
298         metadata_endptr = preload_ptr;
299         PRELOAD_PUSH_VALUE(uint32_t, 0);
300         PRELOAD_PUSH_VALUE(uint32_t, 0);
301 }
302
303 /*
304  * Parse initrd image arguments, bootargs passed in FDT from U-Boot.
305  */
306 void
307 parse_bootargs(vm_offset_t *lastaddr, struct arm64_bootparams *abp)
308 {
309
310         /*
311          * Fake metadata is used to support boot from U-Boot. Process bootargs,
312          * initrd args from FDT blob set in fake medadata.
313          */
314         if (metadata_endptr == NULL)
315                 return;
316
317         /* Booted from U-Boot */
318         linux_load_initrd(lastaddr, abp);
319
320         /*
321          * L2 PTEs map addresses in order of kernel, dtb, initrd image.
322          * Add L2 pages at the end for pmap to bootstrap L2, L3 PTEs, etc.
323          */
324         build_l2_block_pagetable(*lastaddr,
325             (PMAP_BOOTSTRAP_PAGES * L2_SIZE) - 1, abp);
326
327         init_static_kenv(static_kenv, sizeof(static_kenv));
328         ofw_parse_bootargs();
329 }
330 #endif