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