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 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/ctype.h> 37 #include <sys/linker.h> 38 #include <sys/reboot.h> 39 #include <sys/sysctl.h> 40 #ifdef FDT 41 #include <sys/boot.h> 42 #endif 43 44 #include <machine/cpu.h> 45 #include <machine/machdep.h> 46 #include <machine/metadata.h> 47 #include <machine/vmparam.h> 48 49 #ifdef FDT 50 #include <contrib/libfdt/libfdt.h> 51 #include <dev/fdt/fdt_common.h> 52 #endif 53 54 extern int *end; 55 static char *loader_envp; 56 static char static_kenv[4096]; 57 58 59 #ifdef FDT 60 #define CMDLINE_GUARD "FreeBSD:" 61 #define LBABI_MAX_COMMAND_LINE 512 62 static char linux_command_line[LBABI_MAX_COMMAND_LINE + 1]; 63 #endif 64 65 /* 66 * Fake up a boot descriptor table 67 */ 68 #define PRELOAD_PUSH_VALUE(type, value) do { \ 69 *(type *)(preload_ptr + size) = (value); \ 70 size += sizeof(type); \ 71 } while (0) 72 73 #define PRELOAD_PUSH_STRING(str) do { \ 74 uint32_t ssize; \ 75 ssize = strlen(str) + 1; \ 76 PRELOAD_PUSH_VALUE(uint32_t, ssize); \ 77 strcpy((char*)(preload_ptr + size), str); \ 78 size += ssize; \ 79 size = roundup(size, sizeof(u_long)); \ 80 } while (0) 81 82 83 /* Build minimal set of metatda. */ 84 static vm_offset_t 85 fake_preload_metadata(void *dtb_ptr, size_t dtb_size) 86 { 87 vm_offset_t lastaddr; 88 static char fake_preload[256]; 89 caddr_t preload_ptr; 90 size_t size; 91 92 lastaddr = (vm_offset_t)&end; 93 preload_ptr = (caddr_t)&fake_preload[0]; 94 size = 0; 95 96 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_NAME); 97 PRELOAD_PUSH_STRING("kernel"); 98 99 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_TYPE); 100 PRELOAD_PUSH_STRING("elf kernel"); 101 102 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_ADDR); 103 PRELOAD_PUSH_VALUE(uint32_t, sizeof(vm_offset_t)); 104 PRELOAD_PUSH_VALUE(uint64_t, VM_MIN_KERNEL_ADDRESS); 105 106 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_SIZE); 107 PRELOAD_PUSH_VALUE(uint32_t, sizeof(size_t)); 108 PRELOAD_PUSH_VALUE(uint64_t, (size_t)(&end - VM_MIN_KERNEL_ADDRESS)); 109 110 if (dtb_ptr != NULL) { 111 /* Copy DTB to KVA space and insert it into module chain. */ 112 lastaddr = roundup(lastaddr, sizeof(int)); 113 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_METADATA | MODINFOMD_DTBP); 114 PRELOAD_PUSH_VALUE(uint32_t, sizeof(uint64_t)); 115 PRELOAD_PUSH_VALUE(uint64_t, (uint64_t)lastaddr); 116 memmove((void *)lastaddr, dtb_ptr, dtb_size); 117 lastaddr += dtb_size; 118 lastaddr = roundup(lastaddr, sizeof(int)); 119 } 120 /* End marker */ 121 PRELOAD_PUSH_VALUE(uint32_t, 0); 122 PRELOAD_PUSH_VALUE(uint32_t, 0); 123 124 preload_metadata = (caddr_t)(uintptr_t)fake_preload; 125 126 init_static_kenv(NULL, 0); 127 128 return (lastaddr); 129 } 130 131 #ifdef FDT 132 133 /* Convert the U-Boot command line into FreeBSD kenv and boot options. */ 134 static void 135 cmdline_set_env(char *cmdline, const char *guard) 136 { 137 size_t guard_len; 138 139 /* Skip leading spaces. */ 140 while (isspace(*cmdline)) 141 cmdline++; 142 143 /* Test and remove guard. */ 144 if (guard != NULL && guard[0] != '\0') { 145 guard_len = strlen(guard); 146 if (strncasecmp(cmdline, guard, guard_len) != 0) 147 return; 148 cmdline += guard_len; 149 } 150 151 boothowto |= boot_parse_cmdline(cmdline); 152 } 153 154 void 155 parse_fdt_bootargs(void) 156 { 157 158 if (loader_envp == NULL && fdt_get_chosen_bootargs(linux_command_line, 159 LBABI_MAX_COMMAND_LINE) == 0) { 160 init_static_kenv(static_kenv, sizeof(static_kenv)); 161 cmdline_set_env(linux_command_line, CMDLINE_GUARD); 162 } 163 } 164 165 #endif 166 167 #if defined(LINUX_BOOT_ABI) && defined(FDT) 168 static vm_offset_t 169 linux_parse_boot_param(struct arm64_bootparams *abp) 170 { 171 struct fdt_header *dtb_ptr; 172 size_t dtb_size; 173 174 if (abp->modulep == 0) 175 return (0); 176 /* Test if modulep point to valid DTB. */ 177 dtb_ptr = (struct fdt_header *)abp->modulep; 178 if (fdt_check_header(dtb_ptr) != 0) 179 return (0); 180 dtb_size = fdt_totalsize(dtb_ptr); 181 return (fake_preload_metadata(dtb_ptr, dtb_size)); 182 } 183 184 #endif 185 186 static vm_offset_t 187 freebsd_parse_boot_param(struct arm64_bootparams *abp) 188 { 189 vm_offset_t lastaddr = 0; 190 void *kmdp; 191 #ifdef DDB 192 vm_offset_t ksym_start; 193 vm_offset_t ksym_end; 194 #endif 195 196 if (abp->modulep == 0) 197 return (0); 198 199 preload_metadata = (caddr_t)(uintptr_t)(abp->modulep); 200 kmdp = preload_search_by_type("elf kernel"); 201 if (kmdp == NULL) 202 return (0); 203 204 boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int); 205 loader_envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *); 206 init_static_kenv(loader_envp, 0); 207 lastaddr = MD_FETCH(kmdp, MODINFOMD_KERNEND, vm_offset_t); 208 #ifdef DDB 209 ksym_start = MD_FETCH(kmdp, MODINFOMD_SSYM, uintptr_t); 210 ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, uintptr_t); 211 db_fetch_ksymtab(ksym_start, ksym_end, 0); 212 #endif 213 return (lastaddr); 214 } 215 216 vm_offset_t 217 parse_boot_param(struct arm64_bootparams *abp) 218 { 219 vm_offset_t lastaddr; 220 221 #if defined(LINUX_BOOT_ABI) && defined(FDT) 222 lastaddr = linux_parse_boot_param(abp); 223 if (lastaddr != 0) 224 return (lastaddr); 225 #endif 226 lastaddr = freebsd_parse_boot_param(abp); 227 if (lastaddr != 0) 228 return (lastaddr); 229 230 /* Fall back to hardcoded metadata. */ 231 lastaddr = fake_preload_metadata(NULL, 0); 232 233 return (lastaddr); 234 } 235