1 /*-
2 * Copyright (c) 2022 Netflix, Inc
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7 #include <sys/param.h>
8 #include <machine/metadata.h>
9 #include <sys/linker.h>
10 #include <fdt_platform.h>
11 #include <libfdt.h>
12
13 #include "kboot.h"
14 #include "efi.h"
15
16 static bool
do_memory_from_fdt(int fd)17 do_memory_from_fdt(int fd)
18 {
19 struct stat sb;
20 char *buf = NULL;
21 int len, offset;
22 uint32_t sz, ver, esz;
23 uint64_t mmap_pa;
24 const uint32_t *u32p;
25 const uint64_t *u64p;
26
27 if (fstat(fd, &sb) < 0)
28 return false;
29 buf = malloc(sb.st_size);
30 if (buf == NULL)
31 return false;
32 len = read(fd, buf, sb.st_size);
33 /* NB: we're reading this from sysfs, so mismatch OK */
34 if (len <= 0)
35 goto errout;
36
37 /*
38 * Look for /chosen to find these values:
39 * linux,uefi-system-table PA of the UEFI System Table.
40 * linux,uefi-mmap-start PA of the UEFI memory map
41 * linux,uefi-mmap-size Size of mmap
42 * linux,uefi-mmap-desc-size Size of each entry of mmap
43 * linux,uefi-mmap-desc-ver Format version, should be 1
44 */
45 offset = fdt_path_offset(buf, "/chosen");
46 if (offset <= 0)
47 goto errout;
48 u64p = fdt_getprop(buf, offset, "linux,uefi-system-table", &len);
49 if (u64p == NULL)
50 goto errout;
51 efi_set_systbl(fdt64_to_cpu(*u64p));
52 u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-desc-ver", &len);
53 if (u32p == NULL)
54 goto errout;
55 ver = fdt32_to_cpu(*u32p);
56 u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-desc-size", &len);
57 if (u32p == NULL)
58 goto errout;
59 esz = fdt32_to_cpu(*u32p);
60 u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-size", &len);
61 if (u32p == NULL)
62 goto errout;
63 sz = fdt32_to_cpu(*u32p);
64 u64p = fdt_getprop(buf, offset, "linux,uefi-mmap-start", &len);
65 if (u64p == NULL)
66 goto errout;
67 mmap_pa = fdt64_to_cpu(*u64p);
68 free(buf);
69
70 printf("UEFI MMAP: Ver %d Ent Size %d Tot Size %d PA %#lx\n",
71 ver, esz, sz, mmap_pa);
72
73 efi_read_from_pa(mmap_pa, sz, esz, ver);
74 return true;
75
76 errout:
77 free(buf);
78 return false;
79 }
80
81 bool
enumerate_memory_arch(void)82 enumerate_memory_arch(void)
83 {
84 int fd = -1;
85 bool rv = false;
86
87 /*
88 * FDT publishes the parameters for the memory table in a series of
89 * nodes in the DTB. One of them is the physical address for the memory
90 * table. Try to open the fdt nblob to find this information if we can
91 * and try to grab things from memory. If we return rv == TRUE then
92 * we found it. The global efi_map_phys_src is set != 0 when we know
93 * the PA but can't read it.
94 */
95 fd = open("host:/sys/firmware/fdt", O_RDONLY);
96 if (fd != -1) {
97 rv = do_memory_from_fdt(fd);
98 close(fd);
99 }
100
101 /*
102 * One would think that one could use the raw EFI map to find memory
103 * that's free to boot the kernel with. However, Linux reserves some
104 * areas that it needs to properly. I'm not sure if the printf should be
105 * a panic, but for now, so I can debug (maybe at the loader prompt),
106 * I'm printing and carrying on.
107 */
108 if (!rv) {
109 printf("Could not obtain UEFI memory tables, expect failure\n");
110 }
111
112 populate_avail_from_iomem();
113 print_avail();
114
115 return true;
116 }
117
118 uint64_t
kboot_get_phys_load_segment(void)119 kboot_get_phys_load_segment(void)
120 {
121 #define HOLE_SIZE (64ul << 20)
122 #define KERN_ALIGN (2ul << 20)
123 static uint64_t s = 0;
124
125 if (s != 0)
126 return (s);
127
128 print_avail();
129 s = first_avail(KERN_ALIGN, HOLE_SIZE, SYSTEM_RAM);
130 printf("KBOOT GET PHYS Using %#llx\n", (long long)s);
131 if (s != 0)
132 return (s);
133 s = 0x40000000 | 0x4200000; /* should never get here */
134 /* XXX PANIC? XXX */
135 printf("Falling back to the crazy address %#lx which works in qemu\n", s);
136 return (s);
137 }
138
139 void
bi_loadsmap(struct preloaded_file * kfp)140 bi_loadsmap(struct preloaded_file *kfp)
141 {
142 efi_bi_loadsmap(kfp);
143 }
144