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.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
31 #include "opt_platform.h"
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
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>
43 #include <machine/cpu.h>
44 #include <machine/machdep.h>
45 #include <machine/metadata.h>
46 #include <machine/vmparam.h>
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>
59 #define PRELOAD_PUSH_VALUE(type, value) do { \
60 *(type *)(preload_ptr + preload_size) = (value); \
61 preload_size += sizeof(type); \
64 #define PRELOAD_PUSH_STRING(str) do { \
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)); \
73 static int build_l2_block_pagetable(vm_offset_t, uint64_t,
74 struct arm64_bootparams *);
76 #define INITRD_START "linux,initrd-start"
77 #define INITRD_END "linux,initrd-end"
78 #define KENV_SIZE 2048
80 static char static_kenv[KENV_SIZE];
81 static caddr_t metadata_endptr;
84 #define PMAP_BOOTSTRAP_PAGES 2
86 extern vm_offset_t end;
89 * Fake up a boot descriptor table
92 fake_preload_metadata(void *dtb_ptr, size_t dtb_size,
93 struct arm64_bootparams *abp)
96 static char fake_preload[256];
100 preload_ptr = (caddr_t)&fake_preload[0];
103 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_NAME);
104 PRELOAD_PUSH_STRING("kernel");
106 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_TYPE);
107 PRELOAD_PUSH_STRING("elf kernel");
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);
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));
117 lastaddr = (vm_offset_t)&end;
118 lastaddr = roundup(lastaddr, sizeof(vm_offset_t));
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;
131 lastaddr = roundup(lastaddr, sizeof(vm_offset_t));
133 metadata_endptr = preload_ptr;
136 PRELOAD_PUSH_VALUE(uint32_t, 0);
137 PRELOAD_PUSH_VALUE(uint32_t, 0);
139 preload_metadata = fake_preload;
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.
149 linux_parse_boot_param(struct arm64_bootparams *abp)
151 uint32_t dtb_size = 0;
152 struct fdt_header *dtb_ptr = NULL;
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);
160 return (fake_preload_metadata(dtb_ptr, dtb_size, abp));
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
171 build_l2_block_pagetable(vm_offset_t lastaddr, uint64_t size,
172 struct arm64_bootparams *abp)
174 vm_offset_t l2_block_entry, *l2pt_entry;
176 volatile uint64_t output_bits;
178 /* Number of 2MiB pages */
179 count_2mib = ((lastaddr - KERNBASE) + size) >> L2_SHIFT;
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",
188 /* size fits within the last 2MiB page table entry */
189 if (((lastaddr - KERNBASE) >> L2_SHIFT) == count_2mib)
192 /* Build the L2 block entry */
193 l2_block_entry = ATTR_IDX(VM_MEMATTR_WRITE_BACK) | L2_BLOCK | ATTR_AF;
195 l2_block_entry |= ATTR_SH(ATTR_SH_IS);
197 /* Number of 2MiB pages mapped to kernel */
198 count_2mib = (lastaddr - KERNBASE) >> L2_SHIFT;
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) +
203 output_bits = (*l2pt_entry++ >> L2_SHIFT) + 1;
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;
214 * Align start addr to 1GiB boundary and build L1 page table entry for it.
215 * See build_l1_block_pagetable in locore.S
218 build_l1_block_pagetable(vm_offset_t start, struct arm64_bootparams *abp)
220 vm_offset_t l1_table_idx, l1_block_entry, phy_addr, *l1_table_entry;
222 /* Find the table index */
223 l1_table_idx = (start >> L1_SHIFT) & Ln_ADDR_MASK;
225 /* Build the L1 block entry */
226 l1_block_entry = ATTR_nG | ATTR_IDX(VM_MEMATTR_UNCACHEABLE) |
229 l1_block_entry |= ATTR_SH(ATTR_SH_IS);
232 /* Set the physical address */
233 phy_addr = l1_block_entry | (l1_table_idx << L1_SHIFT);
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;
242 * Copy the initrd image passed using U-Boot's booti command into
246 linux_load_initrd(vm_offset_t *lastaddr, struct arm64_bootparams *abp)
249 uint64_t initrd_start = 0, initrd_end = 0;
250 uint64_t initrd_size;
252 size_t preload_size = 0;
254 if ((chosen = OF_finddevice("/chosen")) == -1)
257 if (!(OF_hasprop(chosen, INITRD_START) &&
258 OF_hasprop(chosen, INITRD_END)))
261 if ((OF_getprop(chosen, INITRD_START, &initrd_start, sizeof(uint64_t))) > 0)
262 initrd_start = fdt64_to_cpu(initrd_start);
264 if ((OF_getprop(chosen, INITRD_END, &initrd_end, sizeof(uint64_t))) > 0)
265 initrd_end = fdt64_to_cpu(initrd_end);
267 if ((initrd_size = (initrd_end - initrd_start)) <= 0)
270 if (build_l2_block_pagetable(*lastaddr, initrd_size, abp) != 0)
273 build_l1_block_pagetable(initrd_start, abp);
275 /* Copy the initrd image to virtual address space */
276 memmove((void*)(*lastaddr), (void*)initrd_start, initrd_size);
278 preload_ptr = metadata_endptr;
280 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_NAME);
281 PRELOAD_PUSH_STRING("initrd");
283 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_TYPE);
284 PRELOAD_PUSH_STRING("md_image");
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);
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);
294 *lastaddr += initrd_size;
295 *lastaddr = roundup(*lastaddr, sizeof(vm_offset_t));
298 metadata_endptr = preload_ptr;
299 PRELOAD_PUSH_VALUE(uint32_t, 0);
300 PRELOAD_PUSH_VALUE(uint32_t, 0);
304 * Parse initrd image arguments, bootargs passed in FDT from U-Boot.
307 parse_bootargs(vm_offset_t *lastaddr, struct arm64_bootparams *abp)
311 * Fake metadata is used to support boot from U-Boot. Process bootargs,
312 * initrd args from FDT blob set in fake medadata.
314 if (metadata_endptr == NULL)
317 /* Booted from U-Boot */
318 linux_load_initrd(lastaddr, abp);
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.
324 build_l2_block_pagetable(*lastaddr,
325 (PMAP_BOOTSTRAP_PAGES * L2_SIZE) - 1, abp);
327 init_static_kenv(static_kenv, sizeof(static_kenv));
328 ofw_parse_bootargs();