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