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 57 #ifdef FDT 58 static char static_kenv[4096]; 59 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 /* Build minimal set of metatda. */ 83 static vm_offset_t 84 fake_preload_metadata(void *dtb_ptr, size_t dtb_size) 85 { 86 vm_offset_t lastaddr; 87 static char fake_preload[256]; 88 caddr_t preload_ptr; 89 size_t size; 90 91 lastaddr = (vm_offset_t)&end; 92 preload_ptr = (caddr_t)&fake_preload[0]; 93 size = 0; 94 95 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_NAME); 96 PRELOAD_PUSH_STRING("kernel"); 97 98 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_TYPE); 99 PRELOAD_PUSH_STRING("elf kernel"); 100 101 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_ADDR); 102 PRELOAD_PUSH_VALUE(uint32_t, sizeof(vm_offset_t)); 103 PRELOAD_PUSH_VALUE(uint64_t, VM_MIN_KERNEL_ADDRESS); 104 105 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_SIZE); 106 PRELOAD_PUSH_VALUE(uint32_t, sizeof(size_t)); 107 PRELOAD_PUSH_VALUE(uint64_t, (size_t)(&end - VM_MIN_KERNEL_ADDRESS)); 108 109 if (dtb_ptr != NULL) { 110 /* Copy DTB to KVA space and insert it into module chain. */ 111 lastaddr = roundup(lastaddr, sizeof(int)); 112 PRELOAD_PUSH_VALUE(uint32_t, MODINFO_METADATA | MODINFOMD_DTBP); 113 PRELOAD_PUSH_VALUE(uint32_t, sizeof(uint64_t)); 114 PRELOAD_PUSH_VALUE(uint64_t, (uint64_t)lastaddr); 115 memmove((void *)lastaddr, dtb_ptr, dtb_size); 116 lastaddr += dtb_size; 117 lastaddr = roundup(lastaddr, sizeof(int)); 118 } 119 /* End marker */ 120 PRELOAD_PUSH_VALUE(uint32_t, 0); 121 PRELOAD_PUSH_VALUE(uint32_t, 0); 122 123 preload_metadata = (caddr_t)(uintptr_t)fake_preload; 124 125 init_static_kenv(NULL, 0); 126 127 return (lastaddr); 128 } 129 130 #ifdef FDT 131 132 /* Convert the U-Boot command line into FreeBSD kenv and boot options. */ 133 static void 134 cmdline_set_env(char *cmdline, const char *guard) 135 { 136 size_t guard_len; 137 138 /* Skip leading spaces. */ 139 while (isspace(*cmdline)) 140 cmdline++; 141 142 /* Test and remove guard. */ 143 if (guard != NULL && guard[0] != '\0') { 144 guard_len = strlen(guard); 145 if (strncasecmp(cmdline, guard, guard_len) != 0) 146 return; 147 cmdline += guard_len; 148 } 149 150 boothowto |= boot_parse_cmdline(cmdline); 151 } 152 153 void 154 parse_fdt_bootargs(void) 155 { 156 157 if (loader_envp == NULL && fdt_get_chosen_bootargs(linux_command_line, 158 LBABI_MAX_COMMAND_LINE) == 0) { 159 init_static_kenv(static_kenv, sizeof(static_kenv)); 160 cmdline_set_env(linux_command_line, CMDLINE_GUARD); 161 } 162 } 163 164 #endif 165 166 #if defined(LINUX_BOOT_ABI) && defined(FDT) 167 static vm_offset_t 168 linux_parse_boot_param(struct arm64_bootparams *abp) 169 { 170 struct fdt_header *dtb_ptr; 171 size_t dtb_size; 172 173 if (abp->modulep == 0) 174 return (0); 175 /* Test if modulep point to valid DTB. */ 176 dtb_ptr = (struct fdt_header *)abp->modulep; 177 if (fdt_check_header(dtb_ptr) != 0) 178 return (0); 179 dtb_size = fdt_totalsize(dtb_ptr); 180 return (fake_preload_metadata(dtb_ptr, dtb_size)); 181 } 182 183 #endif 184 185 static vm_offset_t 186 freebsd_parse_boot_param(struct arm64_bootparams *abp) 187 { 188 vm_offset_t lastaddr = 0; 189 void *kmdp; 190 #ifdef DDB 191 vm_offset_t ksym_start; 192 vm_offset_t ksym_end; 193 #endif 194 195 if (abp->modulep == 0) 196 return (0); 197 198 preload_metadata = (caddr_t)(uintptr_t)(abp->modulep); 199 kmdp = preload_search_by_type("elf kernel"); 200 if (kmdp == NULL) 201 return (0); 202 203 boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int); 204 loader_envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *); 205 init_static_kenv(loader_envp, 0); 206 lastaddr = MD_FETCH(kmdp, MODINFOMD_KERNEND, vm_offset_t); 207 #ifdef DDB 208 ksym_start = MD_FETCH(kmdp, MODINFOMD_SSYM, uintptr_t); 209 ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, uintptr_t); 210 db_fetch_ksymtab(ksym_start, ksym_end, 0); 211 #endif 212 return (lastaddr); 213 } 214 215 vm_offset_t 216 parse_boot_param(struct arm64_bootparams *abp) 217 { 218 vm_offset_t lastaddr; 219 220 #if defined(LINUX_BOOT_ABI) && defined(FDT) 221 lastaddr = linux_parse_boot_param(abp); 222 if (lastaddr != 0) 223 return (lastaddr); 224 #endif 225 lastaddr = freebsd_parse_boot_param(abp); 226 if (lastaddr != 0) 227 return (lastaddr); 228 229 /* Fall back to hardcoded metadata. */ 230 lastaddr = fake_preload_metadata(NULL, 0); 231 232 return (lastaddr); 233 } 234