1 /*- 2 * Copyright (c) 2004 Olivier Houchard 3 * Copyright (c) 1994-1998 Mark Brinicombe. 4 * Copyright (c) 1994 Brini. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "opt_platform.h" 30 #include "opt_ddb.h" 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/ctype.h> 35 #include <sys/linker.h> 36 #include <sys/reboot.h> 37 #include <sys/sysctl.h> 38 #ifdef FDT 39 #include <sys/boot.h> 40 #endif 41 42 #include <machine/cpu.h> 43 #include <machine/machdep.h> 44 #include <machine/metadata.h> 45 #include <machine/vmparam.h> 46 47 #ifdef FDT 48 #include <contrib/libfdt/libfdt.h> 49 #include <dev/fdt/fdt_common.h> 50 #endif 51 52 #ifdef DDB 53 #include <ddb/ddb.h> 54 #endif 55 56 extern int *end; 57 static char *loader_envp; 58 59 #ifdef FDT 60 static char static_kenv[4096]; 61 62 #define CMDLINE_GUARD "FreeBSD:" 63 #define LBABI_MAX_COMMAND_LINE 512 64 static char linux_command_line[LBABI_MAX_COMMAND_LINE + 1]; 65 #endif 66 67 /* 68 * Fake up a boot descriptor table 69 */ 70 #define PRELOAD_PUSH_VALUE(type, value) do { \ 71 *(type *)(preload_ptr + size) = (value); \ 72 size += sizeof(type); \ 73 } while (0) 74 75 #define PRELOAD_PUSH_STRING(str) do { \ 76 uint32_t ssize; \ 77 ssize = strlen(str) + 1; \ 78 PRELOAD_PUSH_VALUE(uint32_t, ssize); \ 79 strcpy((char*)(preload_ptr + size), str); \ 80 size += ssize; \ 81 size = roundup(size, sizeof(u_long)); \ 82 } while (0) 83 84 /* Build minimal set of metatda. */ 85 static vm_offset_t 86 fake_preload_metadata(void *dtb_ptr, size_t dtb_size) 87 { 88 vm_offset_t lastaddr; 89 static char fake_preload[256]; 90 caddr_t preload_ptr; 91 size_t size; 92 93 lastaddr = (vm_offset_t)&end; 94 preload_ptr = (caddr_t)&fake_preload[0]; 95 size = 0; 96 97 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_NAME); 98 PRELOAD_PUSH_STRING("kernel"); 99 100 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_TYPE); 101 PRELOAD_PUSH_STRING(preload_kerntype); 102 103 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_ADDR); 104 PRELOAD_PUSH_VALUE(uint32_t, sizeof(vm_offset_t)); 105 PRELOAD_PUSH_VALUE(uint64_t, VM_MIN_KERNEL_ADDRESS); 106 107 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_SIZE); 108 PRELOAD_PUSH_VALUE(uint32_t, sizeof(size_t)); 109 PRELOAD_PUSH_VALUE(uint64_t, (size_t)(&end - VM_MIN_KERNEL_ADDRESS)); 110 111 if (dtb_ptr != NULL) { 112 /* Copy DTB to KVA space and insert it into module chain. */ 113 lastaddr = roundup(lastaddr, sizeof(int)); 114 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_METADATA | MODINFOMD_DTBP); 115 PRELOAD_PUSH_VALUE(uint32_t, sizeof(uint64_t)); 116 PRELOAD_PUSH_VALUE(uint64_t, (uint64_t)lastaddr); 117 memmove((void *)lastaddr, dtb_ptr, dtb_size); 118 lastaddr += dtb_size; 119 lastaddr = roundup(lastaddr, sizeof(int)); 120 } 121 /* End marker */ 122 PRELOAD_PUSH_VALUE(uint32_t, 0); 123 PRELOAD_PUSH_VALUE(uint32_t, 0); 124 125 preload_metadata = (caddr_t)(uintptr_t)fake_preload; 126 127 /* Initialize preload_kmdp */ 128 preload_initkmdp(true); 129 130 init_static_kenv(NULL, 0); 131 132 return (lastaddr); 133 } 134 135 #ifdef FDT 136 137 /* Convert the U-Boot command line into FreeBSD kenv and boot options. */ 138 static void 139 cmdline_set_env(char *cmdline, const char *guard) 140 { 141 size_t guard_len; 142 143 /* Skip leading spaces. */ 144 while (isspace(*cmdline)) 145 cmdline++; 146 147 /* Test and remove guard. */ 148 if (guard != NULL && guard[0] != '\0') { 149 guard_len = strlen(guard); 150 if (strncasecmp(cmdline, guard, guard_len) != 0) 151 return; 152 cmdline += guard_len; 153 } 154 155 boothowto |= boot_parse_cmdline(cmdline); 156 } 157 158 void 159 parse_fdt_bootargs(void) 160 { 161 162 if (loader_envp == NULL && fdt_get_chosen_bootargs(linux_command_line, 163 LBABI_MAX_COMMAND_LINE) == 0) { 164 init_static_kenv(static_kenv, sizeof(static_kenv)); 165 cmdline_set_env(linux_command_line, CMDLINE_GUARD); 166 } 167 } 168 169 #endif 170 171 #if defined(LINUX_BOOT_ABI) && defined(FDT) 172 static vm_offset_t 173 linux_parse_boot_param(struct arm64_bootparams *abp) 174 { 175 struct fdt_header *dtb_ptr; 176 size_t dtb_size; 177 178 if (abp->modulep == 0) 179 return (0); 180 /* Test if modulep point to valid DTB. */ 181 dtb_ptr = (struct fdt_header *)abp->modulep; 182 if (fdt_check_header(dtb_ptr) != 0) 183 return (0); 184 dtb_size = fdt_totalsize(dtb_ptr); 185 return (fake_preload_metadata(dtb_ptr, dtb_size)); 186 } 187 188 #endif 189 190 static vm_offset_t 191 freebsd_parse_boot_param(struct arm64_bootparams *abp) 192 { 193 vm_offset_t lastaddr = 0; 194 #ifdef DDB 195 vm_offset_t ksym_start; 196 vm_offset_t ksym_end; 197 #endif 198 199 if (abp->modulep == 0) 200 return (0); 201 202 preload_metadata = (caddr_t)(uintptr_t)(abp->modulep); 203 204 /* Initialize preload_kmdp */ 205 preload_initkmdp(false); 206 if (preload_kmdp == NULL) 207 return (0); 208 209 boothowto = MD_FETCH(preload_kmdp, MODINFOMD_HOWTO, int); 210 loader_envp = MD_FETCH(preload_kmdp, MODINFOMD_ENVP, char *); 211 init_static_kenv(loader_envp, 0); 212 lastaddr = MD_FETCH(preload_kmdp, MODINFOMD_KERNEND, vm_offset_t); 213 #ifdef DDB 214 ksym_start = MD_FETCH(preload_kmdp, MODINFOMD_SSYM, uintptr_t); 215 ksym_end = MD_FETCH(preload_kmdp, MODINFOMD_ESYM, uintptr_t); 216 db_fetch_ksymtab(ksym_start, ksym_end, 0); 217 #endif 218 return (lastaddr); 219 } 220 221 vm_offset_t 222 parse_boot_param(struct arm64_bootparams *abp) 223 { 224 vm_offset_t lastaddr; 225 226 #if defined(LINUX_BOOT_ABI) && defined(FDT) 227 lastaddr = linux_parse_boot_param(abp); 228 if (lastaddr != 0) 229 return (lastaddr); 230 #endif 231 lastaddr = freebsd_parse_boot_param(abp); 232 if (lastaddr != 0) 233 return (lastaddr); 234 235 /* Fall back to hardcoded metadata. */ 236 lastaddr = fake_preload_metadata(NULL, 0); 237 238 return (lastaddr); 239 } 240