efifdt.c revision 1.15.2.3 1 /* $NetBSD: efifdt.c,v 1.15.2.3 2020/04/13 08:05:19 martin Exp $ */
2
3 /*-
4 * Copyright (c) 2019 Jason R. Thorpe
5 * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include "efiboot.h"
31 #include "efifdt.h"
32 #include "efiblock.h"
33
34 #include <libfdt.h>
35
36 #define FDT_TABLE_GUID \
37 { 0xb1b621d5, 0xf19c, 0x41a5, { 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 } }
38 static EFI_GUID FdtTableGuid = FDT_TABLE_GUID;
39
40 #define FDT_MEMORY_NODE_PATH "/memory"
41 #define FDT_MEMORY_NODE_NAME "memory"
42 #define FDT_CHOSEN_NODE_PATH "/chosen"
43 #define FDT_CHOSEN_NODE_NAME "chosen"
44
45 #define FDT_MEMORY_USABLE(_md) \
46 ((_md)->Type == EfiLoaderCode || (_md)->Type == EfiLoaderData || \
47 (_md)->Type == EfiBootServicesCode || (_md)->Type == EfiBootServicesData || \
48 (_md)->Type == EfiConventionalMemory)
49
50 #ifdef _LP64
51 #define PRIdUINTN "ld"
52 #define PRIxUINTN "lx"
53 #else
54 #define PRIdUINTN "d"
55 #define PRIxUINTN "x"
56 #endif
57 static void *fdt_data = NULL;
58
59 int
60 efi_fdt_probe(void)
61 {
62 EFI_STATUS status;
63
64 status = LibGetSystemConfigurationTable(&FdtTableGuid, &fdt_data);
65 if (EFI_ERROR(status))
66 return EIO;
67
68 if (fdt_check_header(fdt_data) != 0) {
69 fdt_data = NULL;
70 return EINVAL;
71 }
72
73 return 0;
74 }
75
76 int
77 efi_fdt_set_data(void *data)
78 {
79 if (fdt_check_header(data) != 0)
80 return EINVAL;
81
82 fdt_data = data;
83 return 0;
84 }
85
86 void *
87 efi_fdt_data(void)
88 {
89 return fdt_data;
90 }
91
92 int
93 efi_fdt_size(void)
94 {
95 return fdt_data == NULL ? 0 : fdt_totalsize(fdt_data);
96 }
97
98 bool
99 efi_fdt_overlay_is_compatible(void *dtbo)
100 {
101 const int system_root = fdt_path_offset(fdt_data, "/");
102 const int overlay_root = fdt_path_offset(dtbo, "/");
103
104 if (system_root < 0 || overlay_root < 0)
105 return false;
106
107 const int system_ncompat = fdt_stringlist_count(fdt_data, system_root,
108 "compatible");
109 const int overlay_ncompat = fdt_stringlist_count(dtbo, overlay_root,
110 "compatible");
111
112 if (system_ncompat <= 0 || overlay_ncompat <= 0)
113 return false;
114
115 const char *system_compatible, *overlay_compatible;
116 int si, oi;
117
118 for (si = 0; si < system_ncompat; si++) {
119 system_compatible = fdt_stringlist_get(fdt_data,
120 system_root, "compatible", si, NULL);
121 if (system_compatible == NULL)
122 continue;
123 for (oi = 0; oi < overlay_ncompat; oi++) {
124 overlay_compatible = fdt_stringlist_get(dtbo,
125 overlay_root, "compatible", oi, NULL);
126 if (overlay_compatible == NULL)
127 continue;
128 if (strcmp(system_compatible, overlay_compatible) == 0)
129 return true;
130 }
131 }
132
133 return false;
134 }
135
136 int
137 efi_fdt_overlay_apply(void *dtbo, int *fdterr)
138 {
139 int err = fdt_overlay_apply(fdt_data, dtbo);
140 if (fdterr)
141 *fdterr = err;
142 return err == 0 ? 0 : EIO;
143 }
144
145 void
146 efi_fdt_init(u_long addr, u_long len)
147 {
148 int error;
149
150 error = fdt_open_into(fdt_data, (void *)addr, len);
151 if (error < 0)
152 panic("fdt_open_into failed: %d", error);
153
154 fdt_data = (void *)addr;
155 }
156
157 void
158 efi_fdt_fini(void)
159 {
160 int error;
161
162 error = fdt_pack(fdt_data);
163 if (error < 0)
164 panic("fdt_pack failed: %d", error);
165 }
166
167 void
168 efi_fdt_show(void)
169 {
170 const char *model, *compat;
171 int n, ncompat;
172
173 if (fdt_data == NULL)
174 return;
175
176 model = fdt_getprop(fdt_data, fdt_path_offset(fdt_data, "/"), "model", NULL);
177 if (model)
178 printf("FDT: %s [", model);
179 ncompat = fdt_stringlist_count(fdt_data, fdt_path_offset(fdt_data, "/"), "compatible");
180 for (n = 0; n < ncompat; n++) {
181 compat = fdt_stringlist_get(fdt_data, fdt_path_offset(fdt_data, "/"),
182 "compatible", n, NULL);
183 printf("%s%s", n == 0 ? "" : ", ", compat);
184 }
185 printf("]\n");
186 }
187
188 void
189 efi_fdt_memory_map(void)
190 {
191 UINTN nentries = 0, mapkey, descsize;
192 EFI_MEMORY_DESCRIPTOR *md, *memmap;
193 UINT32 descver;
194 UINT64 phys_start, phys_size;
195 int n, memory, chosen;
196
197 memory = fdt_path_offset(fdt_data, FDT_MEMORY_NODE_PATH);
198 if (memory < 0)
199 memory = fdt_add_subnode(fdt_data, fdt_path_offset(fdt_data, "/"), FDT_MEMORY_NODE_NAME);
200 if (memory < 0)
201 panic("FDT: Failed to create " FDT_MEMORY_NODE_PATH " node");
202
203 chosen = fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH);
204 if (chosen < 0)
205 chosen = fdt_add_subnode(fdt_data, fdt_path_offset(fdt_data, "/"), FDT_CHOSEN_NODE_NAME);
206 if (chosen < 0)
207 panic("FDT: Failed to create " FDT_CHOSEN_NODE_PATH " node");
208
209 fdt_delprop(fdt_data, memory, "reg");
210
211 const int address_cells = fdt_address_cells(fdt_data, fdt_path_offset(fdt_data, "/"));
212 const int size_cells = fdt_size_cells(fdt_data, fdt_path_offset(fdt_data, "/"));
213
214 memmap = LibMemoryMap(&nentries, &mapkey, &descsize, &descver);
215 for (n = 0, md = memmap; n < nentries; n++, md = NextMemoryDescriptor(md, descsize)) {
216 fdt_appendprop_u32(fdt_data, fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH), "netbsd,uefi-memmap", md->Type);
217 fdt_appendprop_u64(fdt_data, fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH), "netbsd,uefi-memmap", md->PhysicalStart);
218 fdt_appendprop_u64(fdt_data, fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH), "netbsd,uefi-memmap", md->NumberOfPages);
219 fdt_appendprop_u64(fdt_data, fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH), "netbsd,uefi-memmap", md->Attribute);
220
221 if ((md->Attribute & EFI_MEMORY_RUNTIME) != 0)
222 continue;
223
224 if ((md->Attribute & EFI_MEMORY_WB) == 0)
225 continue;
226 if (!FDT_MEMORY_USABLE(md))
227 continue;
228 if ((address_cells == 1 || size_cells == 1) && md->PhysicalStart + (md->NumberOfPages * EFI_PAGE_SIZE) > 0xffffffff)
229 continue;
230 if (md->NumberOfPages <= 1)
231 continue;
232
233 phys_start = md->PhysicalStart;
234 phys_size = md->NumberOfPages * EFI_PAGE_SIZE;
235
236 if (phys_start & EFI_PAGE_MASK) {
237 /* UEFI spec says these should be 4KB aligned, but U-Boot doesn't always.. */
238 phys_start = (phys_start + EFI_PAGE_SIZE) & ~EFI_PAGE_MASK;
239 phys_size -= (EFI_PAGE_SIZE * 2);
240 if (phys_size == 0)
241 continue;
242 }
243
244 if (address_cells == 1)
245 fdt_appendprop_u32(fdt_data, fdt_path_offset(fdt_data, FDT_MEMORY_NODE_PATH),
246 "reg", (uint32_t)phys_start);
247 else
248 fdt_appendprop_u64(fdt_data, fdt_path_offset(fdt_data, FDT_MEMORY_NODE_PATH),
249 "reg", phys_start);
250
251 if (size_cells == 1)
252 fdt_appendprop_u32(fdt_data, fdt_path_offset(fdt_data, FDT_MEMORY_NODE_PATH),
253 "reg", (uint32_t)phys_size);
254 else
255 fdt_appendprop_u64(fdt_data, fdt_path_offset(fdt_data, FDT_MEMORY_NODE_PATH),
256 "reg", phys_size);
257 }
258 }
259
260 void
261 efi_fdt_gop(void)
262 {
263 EFI_STATUS status;
264 EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
265 EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode;
266 EFI_HANDLE *gop_handle;
267 UINTN ngop_handle, n;
268 char buf[48];
269 int fb;
270
271 status = LibLocateHandle(ByProtocol, &GraphicsOutputProtocol, NULL, &ngop_handle, &gop_handle);
272 if (EFI_ERROR(status) || ngop_handle == 0)
273 return;
274
275 for (n = 0; n < ngop_handle; n++) {
276 status = uefi_call_wrapper(BS->HandleProtocol, 3, gop_handle[n], &GraphicsOutputProtocol, (void **)&gop);
277 if (EFI_ERROR(status))
278 continue;
279
280 mode = gop->Mode;
281 if (mode == NULL)
282 continue;
283
284 #ifdef EFIBOOT_DEBUG
285 printf("GOP: FB @ 0x%" PRIx64 " size 0x%" PRIxUINTN "\n", mode->FrameBufferBase, mode->FrameBufferSize);
286 printf("GOP: Version %d\n", mode->Info->Version);
287 printf("GOP: HRes %d VRes %d\n", mode->Info->HorizontalResolution, mode->Info->VerticalResolution);
288 printf("GOP: PixelFormat %d\n", mode->Info->PixelFormat);
289 printf("GOP: PixelBitmask R 0x%x G 0x%x B 0x%x Res 0x%x\n",
290 mode->Info->PixelInformation.RedMask,
291 mode->Info->PixelInformation.GreenMask,
292 mode->Info->PixelInformation.BlueMask,
293 mode->Info->PixelInformation.ReservedMask);
294 printf("GOP: Pixels per scanline %d\n", mode->Info->PixelsPerScanLine);
295 #endif
296
297 if (mode->Info->PixelFormat == PixelBltOnly) {
298 printf("GOP: PixelBltOnly pixel format not supported\n");
299 continue;
300 }
301
302 fdt_setprop_u32(fdt_data,
303 fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH), "#address-cells", 2);
304 fdt_setprop_u32(fdt_data,
305 fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH), "#size-cells", 2);
306 fdt_setprop_empty(fdt_data,
307 fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH), "ranges");
308
309 snprintf(buf, sizeof(buf), "framebuffer@%" PRIx64, mode->FrameBufferBase);
310 fb = fdt_add_subnode(fdt_data, fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH), buf);
311 if (fb < 0)
312 panic("FDT: Failed to create framebuffer node");
313
314 fdt_appendprop_string(fdt_data, fb, "compatible", "simple-framebuffer");
315 fdt_appendprop_string(fdt_data, fb, "status", "okay");
316 fdt_appendprop_u64(fdt_data, fb, "reg", mode->FrameBufferBase);
317 fdt_appendprop_u64(fdt_data, fb, "reg", mode->FrameBufferSize);
318 fdt_appendprop_u32(fdt_data, fb, "width", mode->Info->HorizontalResolution);
319 fdt_appendprop_u32(fdt_data, fb, "height", mode->Info->VerticalResolution);
320 fdt_appendprop_u32(fdt_data, fb, "stride", mode->Info->PixelsPerScanLine * 4); /* XXX */
321 fdt_appendprop_string(fdt_data, fb, "format", "a8b8g8r8");
322
323 snprintf(buf, sizeof(buf), "/chosen/framebuffer@%" PRIx64, mode->FrameBufferBase);
324 fdt_setprop_string(fdt_data, fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH),
325 "stdout-path", buf);
326
327 return;
328 }
329 }
330
331 void
332 efi_fdt_bootargs(const char *bootargs)
333 {
334 struct efi_block_part *bpart = efi_block_boot_part();
335 uint8_t macaddr[6];
336 int chosen;
337
338 chosen = fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH);
339 if (chosen < 0)
340 chosen = fdt_add_subnode(fdt_data, fdt_path_offset(fdt_data, "/"), FDT_CHOSEN_NODE_NAME);
341 if (chosen < 0)
342 panic("FDT: Failed to create " FDT_CHOSEN_NODE_PATH " node");
343
344 if (*bootargs)
345 fdt_setprop_string(fdt_data, chosen, "bootargs", bootargs);
346
347 if (bpart) {
348 switch (bpart->type) {
349 case EFI_BLOCK_PART_DISKLABEL:
350 fdt_setprop(fdt_data, chosen, "netbsd,mbr",
351 bpart->hash, sizeof(bpart->hash));
352 fdt_setprop_u32(fdt_data, chosen, "netbsd,partition",
353 bpart->index);
354 break;
355 case EFI_BLOCK_PART_GPT:
356 if (bpart->gpt.ent.ent_name[0] == 0x0000) {
357 fdt_setprop(fdt_data, chosen, "netbsd,gpt-guid",
358 bpart->hash, sizeof(bpart->hash));
359 } else {
360 char *label = NULL;
361 int rv = ucs2_to_utf8(bpart->gpt.ent.ent_name, &label);
362 if (rv == 0) {
363 fdt_setprop_string(fdt_data, chosen, "netbsd,gpt-label", label);
364 FreePool(label);
365 }
366 }
367 break;
368 default:
369 break;
370 }
371 } else if (efi_net_get_booted_macaddr(macaddr) == 0) {
372 fdt_setprop(fdt_data, chosen, "netbsd,booted-mac-address", macaddr, sizeof(macaddr));
373 }
374 }
375
376 void
377 efi_fdt_initrd(u_long initrd_addr, u_long initrd_size)
378 {
379 int chosen;
380
381 if (initrd_size == 0)
382 return;
383
384 chosen = fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH);
385 if (chosen < 0)
386 chosen = fdt_add_subnode(fdt_data, fdt_path_offset(fdt_data, "/"), FDT_CHOSEN_NODE_NAME);
387 if (chosen < 0)
388 panic("FDT: Failed to create " FDT_CHOSEN_NODE_PATH " node");
389
390 fdt_setprop_u64(fdt_data, chosen, "linux,initrd-start", initrd_addr);
391 fdt_setprop_u64(fdt_data, chosen, "linux,initrd-end", initrd_addr + initrd_size);
392 }
393
394 void
395 efi_fdt_rndseed(u_long rndseed_addr, u_long rndseed_size)
396 {
397 int chosen;
398
399 if (rndseed_size == 0)
400 return;
401
402 chosen = fdt_path_offset(fdt_data, FDT_CHOSEN_NODE_PATH);
403 if (chosen < 0)
404 chosen = fdt_add_subnode(fdt_data,
405 fdt_path_offset(fdt_data, "/"),
406 FDT_CHOSEN_NODE_NAME);
407 if (chosen < 0)
408 panic("FDT: Failed to create " FDT_CHOSEN_NODE_PATH " node");
409
410 fdt_setprop_u64(fdt_data, chosen, "netbsd,rndseed-start",
411 rndseed_addr);
412 fdt_setprop_u64(fdt_data, chosen, "netbsd,rndseed-end",
413 rndseed_addr + rndseed_size);
414 }
415