1 1.38 jmcneill /* $NetBSD: efifdt.c,v 1.38 2024/12/07 19:29:04 jmcneill Exp $ */ 2 1.1 jmcneill 3 1.1 jmcneill /*- 4 1.15 thorpej * Copyright (c) 2019 Jason R. Thorpe 5 1.1 jmcneill * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca> 6 1.1 jmcneill * All rights reserved. 7 1.1 jmcneill * 8 1.1 jmcneill * Redistribution and use in source and binary forms, with or without 9 1.1 jmcneill * modification, are permitted provided that the following conditions 10 1.1 jmcneill * are met: 11 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright 12 1.1 jmcneill * notice, this list of conditions and the following disclaimer. 13 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the 15 1.1 jmcneill * documentation and/or other materials provided with the distribution. 16 1.1 jmcneill * 17 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 1.1 jmcneill * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 1.1 jmcneill * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 1.1 jmcneill * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 1.1 jmcneill * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 1.1 jmcneill * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 1.1 jmcneill * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 1.1 jmcneill * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 1.1 jmcneill * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 1.1 jmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 1.1 jmcneill * SUCH DAMAGE. 28 1.1 jmcneill */ 29 1.1 jmcneill 30 1.1 jmcneill #include "efiboot.h" 31 1.1 jmcneill #include "efifdt.h" 32 1.3 jmcneill #include "efiblock.h" 33 1.30 jmcneill #include "overlay.h" 34 1.30 jmcneill #include "module.h" 35 1.30 jmcneill 36 1.30 jmcneill #ifdef EFIBOOT_ACPI 37 1.25 jmcneill #include "efiacpi.h" 38 1.30 jmcneill #endif 39 1.1 jmcneill 40 1.1 jmcneill #include <libfdt.h> 41 1.1 jmcneill 42 1.1 jmcneill #define FDT_TABLE_GUID \ 43 1.1 jmcneill { 0xb1b621d5, 0xf19c, 0x41a5, { 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 } } 44 1.1 jmcneill static EFI_GUID FdtTableGuid = FDT_TABLE_GUID; 45 1.1 jmcneill 46 1.1 jmcneill #define FDT_MEMORY_NODE_PATH "/memory" 47 1.1 jmcneill #define FDT_MEMORY_NODE_NAME "memory" 48 1.1 jmcneill #define FDT_CHOSEN_NODE_PATH "/chosen" 49 1.3 jmcneill #define FDT_CHOSEN_NODE_NAME "chosen" 50 1.1 jmcneill 51 1.1 jmcneill #define FDT_MEMORY_USABLE(_md) \ 52 1.1 jmcneill ((_md)->Type == EfiLoaderCode || (_md)->Type == EfiLoaderData || \ 53 1.1 jmcneill (_md)->Type == EfiBootServicesCode || (_md)->Type == EfiBootServicesData || \ 54 1.1 jmcneill (_md)->Type == EfiConventionalMemory) 55 1.1 jmcneill 56 1.30 jmcneill #define FDT_SPACE (4 * 1024 * 1024) 57 1.30 jmcneill #define FDT_ALIGN (2 * 1024 * 1024) 58 1.30 jmcneill 59 1.21 skrll #ifdef _LP64 60 1.21 skrll #define PRIdUINTN "ld" 61 1.21 skrll #define PRIxUINTN "lx" 62 1.21 skrll #else 63 1.21 skrll #define PRIdUINTN "d" 64 1.21 skrll #define PRIxUINTN "x" 65 1.21 skrll #endif 66 1.1 jmcneill static void *fdt_data = NULL; 67 1.25 jmcneill static size_t fdt_data_size = 512*1024; 68 1.1 jmcneill 69 1.30 jmcneill static EFI_PHYSICAL_ADDRESS initrd_addr, dtb_addr, rndseed_addr; 70 1.30 jmcneill static u_long initrd_size = 0, dtb_size = 0, rndseed_size = 0; 71 1.30 jmcneill 72 1.30 jmcneill /* exec.c */ 73 1.30 jmcneill extern EFI_PHYSICAL_ADDRESS efirng_addr; 74 1.30 jmcneill extern u_long efirng_size; 75 1.30 jmcneill 76 1.30 jmcneill #ifdef EFIBOOT_ACPI 77 1.30 jmcneill #define ACPI_FDT_SIZE (128 * 1024) 78 1.30 jmcneill static int efi_fdt_create_acpifdt(void); 79 1.30 jmcneill #endif 80 1.30 jmcneill 81 1.1 jmcneill int 82 1.1 jmcneill efi_fdt_probe(void) 83 1.1 jmcneill { 84 1.1 jmcneill EFI_STATUS status; 85 1.1 jmcneill 86 1.1 jmcneill status = LibGetSystemConfigurationTable(&FdtTableGuid, &fdt_data); 87 1.1 jmcneill if (EFI_ERROR(status)) 88 1.1 jmcneill return EIO; 89 1.1 jmcneill 90 1.1 jmcneill if (fdt_check_header(fdt_data) != 0) { 91 1.1 jmcneill fdt_data = NULL; 92 1.1 jmcneill return EINVAL; 93 1.1 jmcneill } 94 1.1 jmcneill 95 1.1 jmcneill return 0; 96 1.1 jmcneill } 97 1.1 jmcneill 98 1.9 jmcneill int 99 1.9 jmcneill efi_fdt_set_data(void *data) 100 1.9 jmcneill { 101 1.25 jmcneill int err; 102 1.25 jmcneill 103 1.9 jmcneill if (fdt_check_header(data) != 0) 104 1.9 jmcneill return EINVAL; 105 1.9 jmcneill 106 1.25 jmcneill fdt_data = alloc(fdt_data_size); 107 1.25 jmcneill if (fdt_data == NULL) 108 1.25 jmcneill return ENOMEM; 109 1.25 jmcneill memset(fdt_data, 0, fdt_data_size); 110 1.25 jmcneill 111 1.25 jmcneill err = fdt_open_into(data, fdt_data, fdt_data_size); 112 1.25 jmcneill if (err != 0) { 113 1.25 jmcneill dealloc(fdt_data, fdt_data_size); 114 1.25 jmcneill fdt_data = NULL; 115 1.25 jmcneill return ENXIO; 116 1.25 jmcneill } 117 1.25 jmcneill 118 1.9 jmcneill return 0; 119 1.9 jmcneill } 120 1.9 jmcneill 121 1.1 jmcneill void * 122 1.1 jmcneill efi_fdt_data(void) 123 1.1 jmcneill { 124 1.1 jmcneill return fdt_data; 125 1.1 jmcneill } 126 1.1 jmcneill 127 1.1 jmcneill int 128 1.1 jmcneill efi_fdt_size(void) 129 1.1 jmcneill { 130 1.1 jmcneill return fdt_data == NULL ? 0 : fdt_totalsize(fdt_data); 131 1.1 jmcneill } 132 1.1 jmcneill 133 1.15 thorpej bool 134 1.15 thorpej efi_fdt_overlay_is_compatible(void *dtbo) 135 1.15 thorpej { 136 1.15 thorpej const int system_root = fdt_path_offset(fdt_data, "/"); 137 1.15 thorpej const int overlay_root = fdt_path_offset(dtbo, "/"); 138 1.15 thorpej 139 1.15 thorpej if (system_root < 0 || overlay_root < 0) 140 1.15 thorpej return false; 141 1.15 thorpej 142 1.15 thorpej const int system_ncompat = fdt_stringlist_count(fdt_data, system_root, 143 1.15 thorpej "compatible"); 144 1.15 thorpej const int overlay_ncompat = fdt_stringlist_count(dtbo, overlay_root, 145 1.15 thorpej "compatible"); 146 1.15 thorpej 147 1.15 thorpej if (system_ncompat <= 0 || overlay_ncompat <= 0) 148 1.15 thorpej return false; 149 1.15 thorpej 150 1.15 thorpej const char *system_compatible, *overlay_compatible; 151 1.15 thorpej int si, oi; 152 1.15 thorpej 153 1.15 thorpej for (si = 0; si < system_ncompat; si++) { 154 1.15 thorpej system_compatible = fdt_stringlist_get(fdt_data, 155 1.15 thorpej system_root, "compatible", si, NULL); 156 1.15 thorpej if (system_compatible == NULL) 157 1.15 thorpej continue; 158 1.15 thorpej for (oi = 0; oi < overlay_ncompat; oi++) { 159 1.15 thorpej overlay_compatible = fdt_stringlist_get(dtbo, 160 1.15 thorpej overlay_root, "compatible", oi, NULL); 161 1.15 thorpej if (overlay_compatible == NULL) 162 1.15 thorpej continue; 163 1.15 thorpej if (strcmp(system_compatible, overlay_compatible) == 0) 164 1.15 thorpej return true; 165 1.15 thorpej } 166 1.15 thorpej } 167 1.15 thorpej 168 1.15 thorpej return false; 169 1.15 thorpej } 170 1.15 thorpej 171 1.15 thorpej int 172 1.15 thorpej efi_fdt_overlay_apply(void *dtbo, int *fdterr) 173 1.15 thorpej { 174 1.15 thorpej int err = fdt_overlay_apply(fdt_data, dtbo); 175 1.15 thorpej if (fdterr) 176 1.15 thorpej *fdterr = err; 177 1.15 thorpej return err == 0 ? 0 : EIO; 178 1.15 thorpej } 179 1.15 thorpej 180 1.1 jmcneill void 181 1.8 jmcneill efi_fdt_init(u_long addr, u_long len) 182 1.8 jmcneill { 183 1.8 jmcneill int error; 184 1.8 jmcneill 185 1.8 jmcneill error = fdt_open_into(fdt_data, (void *)addr, len); 186 1.8 jmcneill if (error < 0) 187 1.8 jmcneill panic("fdt_open_into failed: %d", error); 188 1.8 jmcneill 189 1.8 jmcneill fdt_data = (void *)addr; 190 1.8 jmcneill } 191 1.8 jmcneill 192 1.8 jmcneill void 193 1.8 jmcneill efi_fdt_fini(void) 194 1.8 jmcneill { 195 1.8 jmcneill int error; 196 1.8 jmcneill 197 1.8 jmcneill error = fdt_pack(fdt_data); 198 1.8 jmcneill if (error < 0) 199 1.8 jmcneill panic("fdt_pack failed: %d", error); 200 1.8 jmcneill } 201 1.8 jmcneill 202 1.8 jmcneill void 203 1.7 jmcneill efi_fdt_show(void) 204 1.7 jmcneill { 205 1.7 jmcneill const char *model, *compat; 206 1.7 jmcneill int n, ncompat; 207 1.7 jmcneill 208 1.35 jmcneill if (fdt_data == NULL) { 209 1.7 jmcneill return; 210 1.35 jmcneill } 211 1.7 jmcneill 212 1.7 jmcneill model = fdt_getprop(fdt_data, fdt_path_offset(fdt_data, "/"), "model", NULL); 213 1.35 jmcneill if (model) { 214 1.35 jmcneill command_printtab("FDT", "%s [", model); 215 1.35 jmcneill } 216 1.7 jmcneill ncompat = fdt_stringlist_count(fdt_data, fdt_path_offset(fdt_data, "/"), "compatible"); 217 1.7 jmcneill for (n = 0; n < ncompat; n++) { 218 1.7 jmcneill compat = fdt_stringlist_get(fdt_data, fdt_path_offset(fdt_data, "/"), 219 1.7 jmcneill "compatible", n, NULL); 220 1.7 jmcneill printf("%s%s", n == 0 ? "" : ", ", compat); 221 1.7 jmcneill } 222 1.7 jmcneill printf("]\n"); 223 1.7 jmcneill } 224 1.7 jmcneill 225 1.23 riastrad static int 226 1.23 riastrad efi_fdt_chosen(void) 227 1.23 riastrad { 228 1.23 riastrad int chosen; 229 1.23 riastrad 230 1.23 riastrad chosen = fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH); 231 1.23 riastrad if (chosen < 0) 232 1.23 riastrad chosen = fdt_add_subnode(fdt_data, 233 1.23 riastrad fdt_path_offset(fdt_data, "/"), 234 1.23 riastrad FDT_CHOSEN_NODE_NAME); 235 1.23 riastrad if (chosen < 0) 236 1.23 riastrad panic("FDT: Failed to create " FDT_CHOSEN_NODE_PATH " node"); 237 1.23 riastrad 238 1.23 riastrad return chosen; 239 1.23 riastrad } 240 1.23 riastrad 241 1.7 jmcneill void 242 1.25 jmcneill efi_fdt_system_table(void) 243 1.25 jmcneill { 244 1.25 jmcneill #ifdef EFIBOOT_RUNTIME_ADDRESS 245 1.25 jmcneill int chosen; 246 1.25 jmcneill 247 1.25 jmcneill chosen = efi_fdt_chosen(); 248 1.25 jmcneill 249 1.25 jmcneill fdt_setprop_u64(fdt_data, chosen, "netbsd,uefi-system-table", (uint64_t)(uintptr_t)ST); 250 1.25 jmcneill #endif 251 1.25 jmcneill } 252 1.25 jmcneill 253 1.25 jmcneill void 254 1.1 jmcneill efi_fdt_memory_map(void) 255 1.1 jmcneill { 256 1.1 jmcneill UINTN nentries = 0, mapkey, descsize; 257 1.2 jmcneill EFI_MEMORY_DESCRIPTOR *md, *memmap; 258 1.1 jmcneill UINT32 descver; 259 1.2 jmcneill UINT64 phys_start, phys_size; 260 1.28 skrll int n, memory; 261 1.1 jmcneill 262 1.1 jmcneill memory = fdt_path_offset(fdt_data, FDT_MEMORY_NODE_PATH); 263 1.1 jmcneill if (memory < 0) 264 1.1 jmcneill memory = fdt_add_subnode(fdt_data, fdt_path_offset(fdt_data, "/"), FDT_MEMORY_NODE_NAME); 265 1.1 jmcneill if (memory < 0) 266 1.1 jmcneill panic("FDT: Failed to create " FDT_MEMORY_NODE_PATH " node"); 267 1.1 jmcneill 268 1.1 jmcneill fdt_delprop(fdt_data, memory, "reg"); 269 1.1 jmcneill 270 1.1 jmcneill const int address_cells = fdt_address_cells(fdt_data, fdt_path_offset(fdt_data, "/")); 271 1.1 jmcneill const int size_cells = fdt_size_cells(fdt_data, fdt_path_offset(fdt_data, "/")); 272 1.1 jmcneill 273 1.2 jmcneill memmap = LibMemoryMap(&nentries, &mapkey, &descsize, &descver); 274 1.2 jmcneill for (n = 0, md = memmap; n < nentries; n++, md = NextMemoryDescriptor(md, descsize)) { 275 1.28 skrll /* 276 1.28 skrll * create / find the chosen node for each iteration as it might have changed 277 1.28 skrll * when adding to the memory node 278 1.28 skrll */ 279 1.28 skrll int chosen = efi_fdt_chosen(); 280 1.28 skrll fdt_appendprop_u32(fdt_data, chosen, "netbsd,uefi-memmap", md->Type); 281 1.28 skrll fdt_appendprop_u64(fdt_data, chosen, "netbsd,uefi-memmap", md->PhysicalStart); 282 1.28 skrll fdt_appendprop_u64(fdt_data, chosen, "netbsd,uefi-memmap", md->NumberOfPages); 283 1.28 skrll fdt_appendprop_u64(fdt_data, chosen, "netbsd,uefi-memmap", md->Attribute); 284 1.18 jmcneill 285 1.11 jmcneill if ((md->Attribute & EFI_MEMORY_RUNTIME) != 0) 286 1.11 jmcneill continue; 287 1.16 jmcneill 288 1.1 jmcneill if ((md->Attribute & EFI_MEMORY_WB) == 0) 289 1.1 jmcneill continue; 290 1.1 jmcneill if (!FDT_MEMORY_USABLE(md)) 291 1.1 jmcneill continue; 292 1.2 jmcneill if ((address_cells == 1 || size_cells == 1) && md->PhysicalStart + (md->NumberOfPages * EFI_PAGE_SIZE) > 0xffffffff) 293 1.2 jmcneill continue; 294 1.2 jmcneill if (md->NumberOfPages <= 1) 295 1.2 jmcneill continue; 296 1.2 jmcneill 297 1.2 jmcneill phys_start = md->PhysicalStart; 298 1.2 jmcneill phys_size = md->NumberOfPages * EFI_PAGE_SIZE; 299 1.2 jmcneill 300 1.2 jmcneill if (phys_start & EFI_PAGE_MASK) { 301 1.28 skrll /* 302 1.28 skrll * UEFI spec says these should be 4KB aligned, but 303 1.28 skrll * U-Boot doesn't always, so round up to the next 304 1.28 skrll * page. 305 1.28 skrll */ 306 1.2 jmcneill phys_start = (phys_start + EFI_PAGE_SIZE) & ~EFI_PAGE_MASK; 307 1.2 jmcneill phys_size -= (EFI_PAGE_SIZE * 2); 308 1.2 jmcneill if (phys_size == 0) 309 1.2 jmcneill continue; 310 1.2 jmcneill } 311 1.1 jmcneill 312 1.28 skrll memory = fdt_path_offset(fdt_data, FDT_MEMORY_NODE_PATH); 313 1.1 jmcneill if (address_cells == 1) 314 1.28 skrll fdt_appendprop_u32(fdt_data, memory, "reg", 315 1.28 skrll (uint32_t)phys_start); 316 1.1 jmcneill else 317 1.28 skrll fdt_appendprop_u64(fdt_data, memory, "reg", 318 1.28 skrll phys_start); 319 1.1 jmcneill 320 1.1 jmcneill if (size_cells == 1) 321 1.28 skrll fdt_appendprop_u32(fdt_data, memory, "reg", 322 1.28 skrll (uint32_t)phys_size); 323 1.1 jmcneill else 324 1.28 skrll fdt_appendprop_u64(fdt_data, memory, "reg", 325 1.28 skrll phys_size); 326 1.1 jmcneill } 327 1.1 jmcneill } 328 1.1 jmcneill 329 1.1 jmcneill void 330 1.16 jmcneill efi_fdt_gop(void) 331 1.16 jmcneill { 332 1.16 jmcneill EFI_STATUS status; 333 1.16 jmcneill EFI_GRAPHICS_OUTPUT_PROTOCOL *gop; 334 1.16 jmcneill EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode; 335 1.16 jmcneill EFI_HANDLE *gop_handle; 336 1.16 jmcneill UINTN ngop_handle, n; 337 1.16 jmcneill char buf[48]; 338 1.25 jmcneill int fb, chosen; 339 1.16 jmcneill 340 1.16 jmcneill status = LibLocateHandle(ByProtocol, &GraphicsOutputProtocol, NULL, &ngop_handle, &gop_handle); 341 1.16 jmcneill if (EFI_ERROR(status) || ngop_handle == 0) 342 1.16 jmcneill return; 343 1.16 jmcneill 344 1.16 jmcneill for (n = 0; n < ngop_handle; n++) { 345 1.16 jmcneill status = uefi_call_wrapper(BS->HandleProtocol, 3, gop_handle[n], &GraphicsOutputProtocol, (void **)&gop); 346 1.16 jmcneill if (EFI_ERROR(status)) 347 1.16 jmcneill continue; 348 1.16 jmcneill 349 1.16 jmcneill mode = gop->Mode; 350 1.16 jmcneill if (mode == NULL) 351 1.16 jmcneill continue; 352 1.16 jmcneill 353 1.16 jmcneill #ifdef EFIBOOT_DEBUG 354 1.21 skrll printf("GOP: FB @ 0x%" PRIx64 " size 0x%" PRIxUINTN "\n", mode->FrameBufferBase, mode->FrameBufferSize); 355 1.16 jmcneill printf("GOP: Version %d\n", mode->Info->Version); 356 1.16 jmcneill printf("GOP: HRes %d VRes %d\n", mode->Info->HorizontalResolution, mode->Info->VerticalResolution); 357 1.16 jmcneill printf("GOP: PixelFormat %d\n", mode->Info->PixelFormat); 358 1.16 jmcneill printf("GOP: PixelBitmask R 0x%x G 0x%x B 0x%x Res 0x%x\n", 359 1.16 jmcneill mode->Info->PixelInformation.RedMask, 360 1.16 jmcneill mode->Info->PixelInformation.GreenMask, 361 1.16 jmcneill mode->Info->PixelInformation.BlueMask, 362 1.16 jmcneill mode->Info->PixelInformation.ReservedMask); 363 1.16 jmcneill printf("GOP: Pixels per scanline %d\n", mode->Info->PixelsPerScanLine); 364 1.16 jmcneill #endif 365 1.16 jmcneill 366 1.16 jmcneill if (mode->Info->PixelFormat == PixelBltOnly) { 367 1.16 jmcneill printf("GOP: PixelBltOnly pixel format not supported\n"); 368 1.16 jmcneill continue; 369 1.16 jmcneill } 370 1.16 jmcneill 371 1.25 jmcneill chosen = efi_fdt_chosen(); 372 1.25 jmcneill fdt_setprop_u32(fdt_data, chosen, "#address-cells", 2); 373 1.25 jmcneill fdt_setprop_u32(fdt_data, chosen, "#size-cells", 2); 374 1.25 jmcneill fdt_setprop_empty(fdt_data, chosen, "ranges"); 375 1.19 jmcneill 376 1.27 jmcneill snprintf(buf, sizeof(buf), "framebuffer@%" PRIx64, mode->FrameBufferBase); 377 1.25 jmcneill fb = fdt_add_subnode(fdt_data, chosen, buf); 378 1.27 jmcneill if (fb < 0) { 379 1.27 jmcneill /* Framebuffer node already exists. No need to create a new one! */ 380 1.27 jmcneill return; 381 1.27 jmcneill } 382 1.16 jmcneill 383 1.16 jmcneill fdt_appendprop_string(fdt_data, fb, "compatible", "simple-framebuffer"); 384 1.16 jmcneill fdt_appendprop_string(fdt_data, fb, "status", "okay"); 385 1.16 jmcneill fdt_appendprop_u64(fdt_data, fb, "reg", mode->FrameBufferBase); 386 1.16 jmcneill fdt_appendprop_u64(fdt_data, fb, "reg", mode->FrameBufferSize); 387 1.16 jmcneill fdt_appendprop_u32(fdt_data, fb, "width", mode->Info->HorizontalResolution); 388 1.16 jmcneill fdt_appendprop_u32(fdt_data, fb, "height", mode->Info->VerticalResolution); 389 1.16 jmcneill fdt_appendprop_u32(fdt_data, fb, "stride", mode->Info->PixelsPerScanLine * 4); /* XXX */ 390 1.16 jmcneill fdt_appendprop_string(fdt_data, fb, "format", "a8b8g8r8"); 391 1.16 jmcneill 392 1.31 jmcneill #ifdef EFIBOOT_ACPI 393 1.25 jmcneill /* 394 1.25 jmcneill * In ACPI mode, use GOP as console. 395 1.25 jmcneill */ 396 1.32 skrll if (efi_acpi_available()) { 397 1.25 jmcneill snprintf(buf, sizeof(buf), "/chosen/framebuffer@%" PRIx64, mode->FrameBufferBase); 398 1.25 jmcneill fdt_setprop_string(fdt_data, chosen, "stdout-path", buf); 399 1.25 jmcneill } 400 1.31 jmcneill #endif 401 1.16 jmcneill 402 1.16 jmcneill return; 403 1.16 jmcneill } 404 1.16 jmcneill } 405 1.16 jmcneill 406 1.16 jmcneill void 407 1.1 jmcneill efi_fdt_bootargs(const char *bootargs) 408 1.1 jmcneill { 409 1.3 jmcneill struct efi_block_part *bpart = efi_block_boot_part(); 410 1.14 jmcneill uint8_t macaddr[6]; 411 1.3 jmcneill int chosen; 412 1.3 jmcneill 413 1.23 riastrad chosen = efi_fdt_chosen(); 414 1.3 jmcneill 415 1.3 jmcneill if (*bootargs) 416 1.3 jmcneill fdt_setprop_string(fdt_data, chosen, "bootargs", bootargs); 417 1.3 jmcneill 418 1.3 jmcneill if (bpart) { 419 1.3 jmcneill switch (bpart->type) { 420 1.3 jmcneill case EFI_BLOCK_PART_DISKLABEL: 421 1.3 jmcneill fdt_setprop(fdt_data, chosen, "netbsd,mbr", 422 1.3 jmcneill bpart->hash, sizeof(bpart->hash)); 423 1.3 jmcneill fdt_setprop_u32(fdt_data, chosen, "netbsd,partition", 424 1.3 jmcneill bpart->index); 425 1.3 jmcneill break; 426 1.12 jmcneill case EFI_BLOCK_PART_GPT: 427 1.12 jmcneill if (bpart->gpt.ent.ent_name[0] == 0x0000) { 428 1.12 jmcneill fdt_setprop(fdt_data, chosen, "netbsd,gpt-guid", 429 1.12 jmcneill bpart->hash, sizeof(bpart->hash)); 430 1.12 jmcneill } else { 431 1.12 jmcneill char *label = NULL; 432 1.12 jmcneill int rv = ucs2_to_utf8(bpart->gpt.ent.ent_name, &label); 433 1.12 jmcneill if (rv == 0) { 434 1.12 jmcneill fdt_setprop_string(fdt_data, chosen, "netbsd,gpt-label", label); 435 1.12 jmcneill FreePool(label); 436 1.12 jmcneill } 437 1.12 jmcneill } 438 1.12 jmcneill break; 439 1.3 jmcneill default: 440 1.3 jmcneill break; 441 1.3 jmcneill } 442 1.14 jmcneill } else if (efi_net_get_booted_macaddr(macaddr) == 0) { 443 1.14 jmcneill fdt_setprop(fdt_data, chosen, "netbsd,booted-mac-address", macaddr, sizeof(macaddr)); 444 1.3 jmcneill } 445 1.1 jmcneill } 446 1.8 jmcneill 447 1.34 jmcneill static void 448 1.34 jmcneill efi_fdt_userconf_addprop(const char *cmd) 449 1.34 jmcneill { 450 1.34 jmcneill const int chosen = efi_fdt_chosen(); 451 1.34 jmcneill 452 1.34 jmcneill fdt_appendprop_string(fdt_data, chosen, "netbsd,userconf", cmd); 453 1.34 jmcneill } 454 1.34 jmcneill 455 1.34 jmcneill void 456 1.34 jmcneill efi_fdt_userconf(void) 457 1.34 jmcneill { 458 1.34 jmcneill userconf_foreach(efi_fdt_userconf_addprop); 459 1.34 jmcneill } 460 1.34 jmcneill 461 1.8 jmcneill void 462 1.8 jmcneill efi_fdt_initrd(u_long initrd_addr, u_long initrd_size) 463 1.8 jmcneill { 464 1.8 jmcneill int chosen; 465 1.8 jmcneill 466 1.8 jmcneill if (initrd_size == 0) 467 1.8 jmcneill return; 468 1.8 jmcneill 469 1.23 riastrad chosen = efi_fdt_chosen(); 470 1.8 jmcneill fdt_setprop_u64(fdt_data, chosen, "linux,initrd-start", initrd_addr); 471 1.8 jmcneill fdt_setprop_u64(fdt_data, chosen, "linux,initrd-end", initrd_addr + initrd_size); 472 1.8 jmcneill } 473 1.20 riastrad 474 1.22 riastrad /* pass in the NetBSD on-disk random seed */ 475 1.20 riastrad void 476 1.30 jmcneill efi_fdt_rndseed(u_long addr, u_long size) 477 1.20 riastrad { 478 1.20 riastrad int chosen; 479 1.20 riastrad 480 1.30 jmcneill if (size == 0) 481 1.20 riastrad return; 482 1.20 riastrad 483 1.23 riastrad chosen = efi_fdt_chosen(); 484 1.30 jmcneill fdt_setprop_u64(fdt_data, chosen, "netbsd,rndseed-start", addr); 485 1.30 jmcneill fdt_setprop_u64(fdt_data, chosen, "netbsd,rndseed-end", addr + size); 486 1.20 riastrad } 487 1.22 riastrad 488 1.22 riastrad /* pass in output from the EFI firmware's RNG from some unknown source */ 489 1.22 riastrad void 490 1.22 riastrad efi_fdt_efirng(u_long efirng_addr, u_long efirng_size) 491 1.22 riastrad { 492 1.22 riastrad int chosen; 493 1.22 riastrad 494 1.22 riastrad if (efirng_size == 0) 495 1.22 riastrad return; 496 1.22 riastrad 497 1.23 riastrad chosen = efi_fdt_chosen(); 498 1.22 riastrad fdt_setprop_u64(fdt_data, chosen, "netbsd,efirng-start", 499 1.22 riastrad efirng_addr); 500 1.22 riastrad fdt_setprop_u64(fdt_data, chosen, "netbsd,efirng-end", 501 1.22 riastrad efirng_addr + efirng_size); 502 1.22 riastrad } 503 1.24 jmcneill 504 1.24 jmcneill /* pass in module information */ 505 1.24 jmcneill void 506 1.24 jmcneill efi_fdt_module(const char *module_name, u_long module_addr, u_long module_size) 507 1.24 jmcneill { 508 1.24 jmcneill int chosen; 509 1.24 jmcneill 510 1.24 jmcneill if (module_size == 0) 511 1.24 jmcneill return; 512 1.24 jmcneill 513 1.24 jmcneill chosen = efi_fdt_chosen(); 514 1.24 jmcneill fdt_appendprop_string(fdt_data, chosen, "netbsd,module-names", module_name); 515 1.24 jmcneill fdt_appendprop_u64(fdt_data, chosen, "netbsd,modules", module_addr); 516 1.24 jmcneill fdt_appendprop_u64(fdt_data, chosen, "netbsd,modules", module_size); 517 1.24 jmcneill } 518 1.30 jmcneill 519 1.30 jmcneill static void 520 1.30 jmcneill apply_overlay(const char *path, void *dtbo) 521 1.30 jmcneill { 522 1.30 jmcneill 523 1.30 jmcneill if (!efi_fdt_overlay_is_compatible(dtbo)) { 524 1.30 jmcneill printf("boot: %s: incompatible overlay\n", path); 525 1.30 jmcneill return; 526 1.30 jmcneill } 527 1.30 jmcneill 528 1.30 jmcneill int fdterr; 529 1.30 jmcneill 530 1.30 jmcneill if (efi_fdt_overlay_apply(dtbo, &fdterr) != 0) { 531 1.30 jmcneill printf("boot: %s: error %d applying overlay\n", path, fdterr); 532 1.30 jmcneill } 533 1.30 jmcneill } 534 1.30 jmcneill 535 1.30 jmcneill static void 536 1.30 jmcneill apply_overlay_file(const char *path) 537 1.30 jmcneill { 538 1.30 jmcneill EFI_PHYSICAL_ADDRESS dtbo_addr; 539 1.30 jmcneill u_long dtbo_size; 540 1.30 jmcneill 541 1.30 jmcneill if (strlen(path) == 0) 542 1.30 jmcneill return; 543 1.30 jmcneill 544 1.30 jmcneill if (load_file(path, 0, false, &dtbo_addr, &dtbo_size) != 0 || 545 1.30 jmcneill dtbo_addr == 0) { 546 1.30 jmcneill /* Error messages have already been displayed. */ 547 1.30 jmcneill goto out; 548 1.30 jmcneill } 549 1.30 jmcneill 550 1.30 jmcneill apply_overlay(path, (void *)(uintptr_t)dtbo_addr); 551 1.30 jmcneill 552 1.30 jmcneill out: 553 1.30 jmcneill if (dtbo_addr) { 554 1.30 jmcneill uefi_call_wrapper(BS->FreePages, 2, dtbo_addr, 555 1.30 jmcneill EFI_SIZE_TO_PAGES(dtbo_size)); 556 1.30 jmcneill } 557 1.30 jmcneill } 558 1.30 jmcneill 559 1.30 jmcneill static void 560 1.30 jmcneill load_fdt_overlays(void) 561 1.30 jmcneill { 562 1.30 jmcneill if (!dtoverlay_enabled) 563 1.30 jmcneill return; 564 1.30 jmcneill 565 1.30 jmcneill dtoverlay_foreach(apply_overlay_file); 566 1.30 jmcneill } 567 1.30 jmcneill 568 1.30 jmcneill static void 569 1.30 jmcneill load_module(const char *module_name) 570 1.30 jmcneill { 571 1.30 jmcneill EFI_PHYSICAL_ADDRESS addr; 572 1.30 jmcneill u_long size; 573 1.30 jmcneill char path[PATH_MAX]; 574 1.30 jmcneill 575 1.30 jmcneill snprintf(path, sizeof(path), "%s/%s/%s.kmod", module_prefix, 576 1.30 jmcneill module_name, module_name); 577 1.30 jmcneill 578 1.30 jmcneill if (load_file(path, 0, false, &addr, &size) != 0 || addr == 0 || size == 0) 579 1.30 jmcneill return; 580 1.30 jmcneill 581 1.30 jmcneill efi_fdt_module(module_name, (u_long)addr, size); 582 1.30 jmcneill } 583 1.30 jmcneill 584 1.30 jmcneill static void 585 1.30 jmcneill load_modules(const char *kernel_name) 586 1.30 jmcneill { 587 1.30 jmcneill if (!module_enabled) 588 1.30 jmcneill return; 589 1.30 jmcneill 590 1.30 jmcneill module_init(kernel_name); 591 1.30 jmcneill module_foreach(load_module); 592 1.30 jmcneill } 593 1.30 jmcneill 594 1.30 jmcneill 595 1.30 jmcneill /* 596 1.30 jmcneill * Prepare kernel arguments and shutdown boot services. 597 1.30 jmcneill */ 598 1.30 jmcneill int 599 1.36 skrll efi_fdt_prepare_boot(const char *fname, const char *args, u_long *marks) 600 1.30 jmcneill { 601 1.37 skrll int error; 602 1.37 skrll 603 1.30 jmcneill load_file(get_initrd_path(), 0, false, &initrd_addr, &initrd_size); 604 1.30 jmcneill load_file(get_dtb_path(), 0, false, &dtb_addr, &dtb_size); 605 1.30 jmcneill 606 1.37 skrll error = efi_md_prepare_boot(fname, args, marks); 607 1.37 skrll if (error) { 608 1.37 skrll return error; 609 1.37 skrll } 610 1.30 jmcneill #ifdef EFIBOOT_ACPI 611 1.30 jmcneill /* ACPI support only works for little endian kernels */ 612 1.32 skrll if (efi_acpi_available() && netbsd_elf_data == ELFDATA2LSB) { 613 1.37 skrll error = efi_fdt_create_acpifdt(); 614 1.30 jmcneill if (error != 0) { 615 1.30 jmcneill return error; 616 1.30 jmcneill } 617 1.30 jmcneill } else 618 1.30 jmcneill #endif 619 1.33 jmcneill if (dtb_addr && efi_fdt_set_data((void *)(uintptr_t)dtb_addr) != 0) { 620 1.30 jmcneill return EINVAL; 621 1.30 jmcneill } 622 1.30 jmcneill 623 1.30 jmcneill if (efi_fdt_size() > 0) { 624 1.30 jmcneill /* 625 1.30 jmcneill * Load the rndseed as late as possible -- after we 626 1.30 jmcneill * have committed to using fdt and executing this 627 1.30 jmcneill * kernel -- so that it doesn't hang around in memory 628 1.30 jmcneill * if we have to bail or the kernel won't use it. 629 1.30 jmcneill */ 630 1.30 jmcneill load_file(get_rndseed_path(), 0, false, 631 1.30 jmcneill &rndseed_addr, &rndseed_size); 632 1.30 jmcneill 633 1.30 jmcneill efi_fdt_init((marks[MARK_END] + FDT_ALIGN - 1) & -FDT_ALIGN, FDT_ALIGN); 634 1.30 jmcneill load_modules(fname); 635 1.30 jmcneill load_fdt_overlays(); 636 1.30 jmcneill efi_fdt_initrd(initrd_addr, initrd_size); 637 1.30 jmcneill efi_fdt_rndseed(rndseed_addr, rndseed_size); 638 1.30 jmcneill efi_fdt_efirng(efirng_addr, efirng_size); 639 1.30 jmcneill efi_fdt_bootargs(args); 640 1.34 jmcneill efi_fdt_userconf(); 641 1.30 jmcneill efi_fdt_system_table(); 642 1.30 jmcneill efi_fdt_gop(); 643 1.30 jmcneill efi_fdt_memory_map(); 644 1.30 jmcneill } 645 1.30 jmcneill 646 1.30 jmcneill efi_cleanup(); 647 1.30 jmcneill 648 1.30 jmcneill if (efi_fdt_size() > 0) { 649 1.30 jmcneill efi_fdt_fini(); 650 1.30 jmcneill } 651 1.30 jmcneill 652 1.30 jmcneill return 0; 653 1.30 jmcneill } 654 1.30 jmcneill 655 1.30 jmcneill /* 656 1.30 jmcneill * Free memory after a failed boot. 657 1.30 jmcneill */ 658 1.30 jmcneill void 659 1.36 skrll efi_fdt_cleanup_boot(void) 660 1.30 jmcneill { 661 1.30 jmcneill if (rndseed_addr) { 662 1.30 jmcneill uefi_call_wrapper(BS->FreePages, 2, rndseed_addr, EFI_SIZE_TO_PAGES(rndseed_size)); 663 1.30 jmcneill rndseed_addr = 0; 664 1.30 jmcneill rndseed_size = 0; 665 1.30 jmcneill } 666 1.30 jmcneill if (initrd_addr) { 667 1.30 jmcneill uefi_call_wrapper(BS->FreePages, 2, initrd_addr, EFI_SIZE_TO_PAGES(initrd_size)); 668 1.30 jmcneill initrd_addr = 0; 669 1.30 jmcneill initrd_size = 0; 670 1.30 jmcneill } 671 1.30 jmcneill if (dtb_addr) { 672 1.30 jmcneill uefi_call_wrapper(BS->FreePages, 2, dtb_addr, EFI_SIZE_TO_PAGES(dtb_size)); 673 1.30 jmcneill dtb_addr = 0; 674 1.30 jmcneill dtb_size = 0; 675 1.30 jmcneill } 676 1.30 jmcneill } 677 1.30 jmcneill 678 1.30 jmcneill size_t 679 1.36 skrll efi_fdt_alloc_size(void) 680 1.30 jmcneill { 681 1.30 jmcneill return FDT_SPACE; 682 1.30 jmcneill } 683 1.30 jmcneill 684 1.30 jmcneill #ifdef EFIBOOT_ACPI 685 1.30 jmcneill int 686 1.30 jmcneill efi_fdt_create_acpifdt(void) 687 1.30 jmcneill { 688 1.30 jmcneill void *acpi_root = efi_acpi_root(); 689 1.30 jmcneill void *smbios_table = efi_acpi_smbios(); 690 1.30 jmcneill void *fdt; 691 1.30 jmcneill int error; 692 1.30 jmcneill 693 1.30 jmcneill if (acpi_root == NULL) 694 1.30 jmcneill return EINVAL; 695 1.30 jmcneill 696 1.30 jmcneill fdt = AllocatePool(ACPI_FDT_SIZE); 697 1.30 jmcneill if (fdt == NULL) 698 1.30 jmcneill return ENOMEM; 699 1.30 jmcneill 700 1.30 jmcneill error = fdt_create_empty_tree(fdt, ACPI_FDT_SIZE); 701 1.30 jmcneill if (error) 702 1.30 jmcneill return EIO; 703 1.30 jmcneill 704 1.30 jmcneill const char *model = efi_acpi_get_model(); 705 1.30 jmcneill 706 1.30 jmcneill fdt_setprop_string(fdt, fdt_path_offset(fdt, "/"), "compatible", "netbsd,generic-acpi"); 707 1.30 jmcneill fdt_setprop_string(fdt, fdt_path_offset(fdt, "/"), "model", model); 708 1.30 jmcneill fdt_setprop_cell(fdt, fdt_path_offset(fdt, "/"), "#address-cells", 2); 709 1.30 jmcneill fdt_setprop_cell(fdt, fdt_path_offset(fdt, "/"), "#size-cells", 2); 710 1.30 jmcneill 711 1.30 jmcneill fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"), "chosen"); 712 1.30 jmcneill fdt_setprop_u64(fdt, fdt_path_offset(fdt, "/chosen"), "netbsd,acpi-root-table", (uint64_t)(uintptr_t)acpi_root); 713 1.30 jmcneill if (smbios_table) 714 1.30 jmcneill fdt_setprop_u64(fdt, fdt_path_offset(fdt, "/chosen"), "netbsd,smbios-table", (uint64_t)(uintptr_t)smbios_table); 715 1.30 jmcneill 716 1.30 jmcneill fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"), "acpi"); 717 1.30 jmcneill fdt_setprop_string(fdt, fdt_path_offset(fdt, "/acpi"), "compatible", "netbsd,acpi"); 718 1.30 jmcneill 719 1.30 jmcneill return efi_fdt_set_data(fdt); 720 1.30 jmcneill } 721 1.30 jmcneill #endif 722 1.30 jmcneill 723 1.30 jmcneill #ifdef EFIBOOT_RUNTIME_ADDRESS 724 1.30 jmcneill static uint64_t 725 1.38 jmcneill efi_fdt_runtime_alloc_va(uint64_t pa, uint64_t npages) 726 1.30 jmcneill { 727 1.38 jmcneill #if EFIBOOT_RUNTIME_ADDRESS != 0 728 1.30 jmcneill static uint64_t va = EFIBOOT_RUNTIME_ADDRESS; 729 1.30 jmcneill static uint64_t sz = EFIBOOT_RUNTIME_SIZE; 730 1.30 jmcneill uint64_t nva; 731 1.30 jmcneill 732 1.30 jmcneill if (sz < (npages * EFI_PAGE_SIZE)) { 733 1.30 jmcneill panic("efi_acpi_alloc_va: couldn't allocate %" PRIu64 " pages", 734 1.30 jmcneill npages); 735 1.30 jmcneill } 736 1.30 jmcneill 737 1.30 jmcneill nva = va; 738 1.30 jmcneill va += (npages * EFI_PAGE_SIZE); 739 1.30 jmcneill sz -= (npages * EFI_PAGE_SIZE); 740 1.30 jmcneill 741 1.30 jmcneill return nva; 742 1.38 jmcneill #else 743 1.38 jmcneill return pa; 744 1.38 jmcneill #endif 745 1.30 jmcneill } 746 1.30 jmcneill 747 1.30 jmcneill void 748 1.36 skrll efi_fdt_set_virtual_address_map(EFI_MEMORY_DESCRIPTOR *memmap, UINTN nentries, 749 1.30 jmcneill UINTN mapkey, UINTN descsize, UINT32 descver) 750 1.30 jmcneill { 751 1.30 jmcneill EFI_MEMORY_DESCRIPTOR *md, *vmd, *vmemmap; 752 1.30 jmcneill EFI_STATUS status; 753 1.30 jmcneill int n, nrt; 754 1.30 jmcneill void *fdt; 755 1.30 jmcneill 756 1.30 jmcneill fdt = efi_fdt_data(); 757 1.30 jmcneill 758 1.30 jmcneill vmemmap = alloc(nentries * descsize); 759 1.30 jmcneill if (vmemmap == NULL) 760 1.30 jmcneill panic("FATAL: couldn't allocate virtual memory map"); 761 1.30 jmcneill 762 1.30 jmcneill for (n = 0, nrt = 0, vmd = vmemmap, md = memmap; 763 1.30 jmcneill n < nentries; 764 1.30 jmcneill n++, md = NextMemoryDescriptor(md, descsize)) { 765 1.30 jmcneill 766 1.30 jmcneill if ((md->Attribute & EFI_MEMORY_RUNTIME) == 0) { 767 1.30 jmcneill continue; 768 1.30 jmcneill } 769 1.30 jmcneill 770 1.38 jmcneill md->VirtualStart = 771 1.38 jmcneill efi_fdt_runtime_alloc_va(md->PhysicalStart, md->NumberOfPages); 772 1.30 jmcneill 773 1.30 jmcneill switch (md->Type) { 774 1.30 jmcneill case EfiRuntimeServicesCode: 775 1.30 jmcneill fdt_appendprop_u64(fdt, fdt_path_offset(fdt, "/chosen"), 776 1.30 jmcneill "netbsd,uefi-runtime-code", md->PhysicalStart); 777 1.30 jmcneill fdt_appendprop_u64(fdt, fdt_path_offset(fdt, "/chosen"), 778 1.30 jmcneill "netbsd,uefi-runtime-code", md->VirtualStart); 779 1.30 jmcneill fdt_appendprop_u64(fdt, fdt_path_offset(fdt, "/chosen"), 780 1.30 jmcneill "netbsd,uefi-runtime-code", 781 1.30 jmcneill md->NumberOfPages * EFI_PAGE_SIZE); 782 1.30 jmcneill break; 783 1.30 jmcneill case EfiRuntimeServicesData: 784 1.30 jmcneill fdt_appendprop_u64(fdt, fdt_path_offset(fdt, "/chosen"), 785 1.30 jmcneill "netbsd,uefi-runtime-data", md->PhysicalStart); 786 1.30 jmcneill fdt_appendprop_u64(fdt, fdt_path_offset(fdt, "/chosen"), 787 1.30 jmcneill "netbsd,uefi-runtime-data", md->VirtualStart); 788 1.30 jmcneill fdt_appendprop_u64(fdt, fdt_path_offset(fdt, "/chosen"), 789 1.30 jmcneill "netbsd,uefi-runtime-data", 790 1.30 jmcneill md->NumberOfPages * EFI_PAGE_SIZE); 791 1.30 jmcneill break; 792 1.30 jmcneill case EfiMemoryMappedIO: 793 1.30 jmcneill fdt_appendprop_u64(fdt, fdt_path_offset(fdt, "/chosen"), 794 1.30 jmcneill "netbsd,uefi-runtime-mmio", md->PhysicalStart); 795 1.30 jmcneill fdt_appendprop_u64(fdt, fdt_path_offset(fdt, "/chosen"), 796 1.30 jmcneill "netbsd,uefi-runtime-mmio", md->VirtualStart); 797 1.30 jmcneill fdt_appendprop_u64(fdt, fdt_path_offset(fdt, "/chosen"), 798 1.30 jmcneill "netbsd,uefi-runtime-mmio", 799 1.30 jmcneill md->NumberOfPages * EFI_PAGE_SIZE); 800 1.30 jmcneill break; 801 1.30 jmcneill default: 802 1.30 jmcneill break; 803 1.30 jmcneill } 804 1.30 jmcneill 805 1.30 jmcneill *vmd = *md; 806 1.30 jmcneill vmd = NextMemoryDescriptor(vmd, descsize); 807 1.30 jmcneill ++nrt; 808 1.30 jmcneill } 809 1.30 jmcneill 810 1.30 jmcneill status = uefi_call_wrapper(RT->SetVirtualAddressMap, 4, nrt * descsize, 811 1.30 jmcneill descsize, descver, vmemmap); 812 1.30 jmcneill if (EFI_ERROR(status)) { 813 1.30 jmcneill printf("WARNING: SetVirtualAddressMap failed\n"); 814 1.30 jmcneill return; 815 1.30 jmcneill } 816 1.30 jmcneill } 817 1.30 jmcneill #endif 818