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("elf kernel"); 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 init_static_kenv(NULL, 0); 128 129 return (lastaddr); 130 } 131 132 #ifdef FDT 133 134 /* Convert the U-Boot command line into FreeBSD kenv and boot options. */ 135 static void 136 cmdline_set_env(char *cmdline, const char *guard) 137 { 138 size_t guard_len; 139 140 /* Skip leading spaces. */ 141 while (isspace(*cmdline)) 142 cmdline++; 143 144 /* Test and remove guard. */ 145 if (guard != NULL && guard[0] != '\0') { 146 guard_len = strlen(guard); 147 if (strncasecmp(cmdline, guard, guard_len) != 0) 148 return; 149 cmdline += guard_len; 150 } 151 152 boothowto |= boot_parse_cmdline(cmdline); 153 } 154 155 void 156 parse_fdt_bootargs(void) 157 { 158 159 if (loader_envp == NULL && fdt_get_chosen_bootargs(linux_command_line, 160 LBABI_MAX_COMMAND_LINE) == 0) { 161 init_static_kenv(static_kenv, sizeof(static_kenv)); 162 cmdline_set_env(linux_command_line, CMDLINE_GUARD); 163 } 164 } 165 166 #endif 167 168 #if defined(LINUX_BOOT_ABI) && defined(FDT) 169 static vm_offset_t 170 linux_parse_boot_param(struct arm64_bootparams *abp) 171 { 172 struct fdt_header *dtb_ptr; 173 size_t dtb_size; 174 175 if (abp->modulep == 0) 176 return (0); 177 /* Test if modulep point to valid DTB. */ 178 dtb_ptr = (struct fdt_header *)abp->modulep; 179 if (fdt_check_header(dtb_ptr) != 0) 180 return (0); 181 dtb_size = fdt_totalsize(dtb_ptr); 182 return (fake_preload_metadata(dtb_ptr, dtb_size)); 183 } 184 185 #endif 186 187 static vm_offset_t 188 freebsd_parse_boot_param(struct arm64_bootparams *abp) 189 { 190 vm_offset_t lastaddr = 0; 191 void *kmdp; 192 #ifdef DDB 193 vm_offset_t ksym_start; 194 vm_offset_t ksym_end; 195 #endif 196 197 if (abp->modulep == 0) 198 return (0); 199 200 preload_metadata = (caddr_t)(uintptr_t)(abp->modulep); 201 kmdp = preload_search_by_type("elf kernel"); 202 if (kmdp == NULL) 203 return (0); 204 205 boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int); 206 loader_envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *); 207 init_static_kenv(loader_envp, 0); 208 lastaddr = MD_FETCH(kmdp, MODINFOMD_KERNEND, vm_offset_t); 209 #ifdef DDB 210 ksym_start = MD_FETCH(kmdp, MODINFOMD_SSYM, uintptr_t); 211 ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, uintptr_t); 212 db_fetch_ksymtab(ksym_start, ksym_end, 0); 213 #endif 214 return (lastaddr); 215 } 216 217 vm_offset_t 218 parse_boot_param(struct arm64_bootparams *abp) 219 { 220 vm_offset_t lastaddr; 221 222 #if defined(LINUX_BOOT_ABI) && defined(FDT) 223 lastaddr = linux_parse_boot_param(abp); 224 if (lastaddr != 0) 225 return (lastaddr); 226 #endif 227 lastaddr = freebsd_parse_boot_param(abp); 228 if (lastaddr != 0) 229 return (lastaddr); 230 231 /* Fall back to hardcoded metadata. */ 232 lastaddr = fake_preload_metadata(NULL, 0); 233 234 return (lastaddr); 235 } 236