xref: /freebsd/stand/kboot/kboot/arch/amd64/load_addr.c (revision 5f757f3f)
1 /*-
2  * Copyright (c) 2022 Netflix, Inc
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/param.h>
27 #include <machine/pc/bios.h>
28 #include <machine/metadata.h>
29 
30 #include "stand.h"
31 #include "host_syscall.h"
32 #include "kboot.h"
33 #include "bootstrap.h"
34 
35 /* Refactor when we do arm64 */
36 
37 enum types {
38 	system_ram = 1,
39 	acpi_tables,
40 	acpi_nv_storage,
41 	unusable,
42 	persistent_old,
43 	persistent,
44 	soft_reserved,
45 	reserved,
46 };
47 
48 struct kv
49 {
50 	uint64_t	type;
51 	char *		name;
52 } str2type_kv[] = {
53 	{ system_ram,		"System RAM" },
54 	{ acpi_tables,		"ACPI Tables" },
55 	{ acpi_nv_storage,	"ACPI Non-volatile Storage" },
56 	{ unusable,		"Unusable memory" },
57 	{ persistent_old,	"Persistent Memory (legacy)" },
58 	{ persistent,		"Persistent Memory" },
59 	{ soft_reserved,	"Soft Reserved" },
60 	{ reserved,		"reserved" },
61 	{ 0, NULL },
62 };
63 
64 #define MEMMAP "/sys/firmware/memmap"
65 
66 static struct memory_segments segs[64];	/* make dynamic later */
67 static int nr_seg;
68 
69 static bool
70 str2type(struct kv *kv, const char *buf, uint64_t *value)
71 {
72 	while (kv->name != NULL) {
73 		if (strcmp(kv->name, buf) == 0) {
74 			*value = kv->type;
75 			return true;
76 		}
77 		kv++;
78 	}
79 
80 	return false;
81 }
82 
83 bool
84 enumerate_memory_arch(void)
85 {
86 	int n;
87 	char name[MAXPATHLEN];
88 	char buf[80];
89 
90 	for (n = 0; n < nitems(segs); n++) {
91 		snprintf(name, sizeof(name), "%s/%d/start", MEMMAP, n);
92 		if (!file2u64(name, &segs[n].start))
93 			break;
94 		snprintf(name, sizeof(name), "%s/%d/end", MEMMAP, n);
95 		if (!file2u64(name, &segs[n].end))
96 			break;
97 		snprintf(name, sizeof(name), "%s/%d/type", MEMMAP, n);
98 		if (!file2str(name, buf, sizeof(buf)))
99 			break;
100 		if (!str2type(str2type_kv, buf, &segs[n].type))
101 			break;
102 	}
103 
104 	nr_seg = n;
105 
106 	return true;
107 }
108 
109 #define BAD_SEG ~0ULL
110 
111 #define SZ(s) (((s).end - (s).start) + 1)
112 
113 static uint64_t
114 find_ram(struct memory_segments *segs, int nr_seg, uint64_t minpa, uint64_t align,
115     uint64_t sz, uint64_t maxpa)
116 {
117 	uint64_t start;
118 
119 	printf("minpa %#jx align %#jx sz %#jx maxpa %#jx\n",
120 	    (uintmax_t)minpa,
121 	    (uintmax_t)align,
122 	    (uintmax_t)sz,
123 	    (uintmax_t)maxpa);
124 	/* XXX assume segs are sorted in numeric order -- assumed not ensured */
125 	for (int i = 0; i < nr_seg; i++) {
126 		if (segs[i].type != system_ram ||
127 		    SZ(segs[i]) < sz ||
128 		    minpa + sz > segs[i].end ||
129 		    maxpa < segs[i].start)
130 			continue;
131 		start = roundup(segs[i].start, align);
132 		if (start < minpa)	/* Too small, round up and try again */
133 			start = (roundup(minpa, align));
134 		if (start + sz > segs[i].end)	/* doesn't fit in seg */
135 			continue;
136 		if (start > maxpa ||		/* Over the edge */
137 		    start + sz > maxpa)		/* on the edge */
138 			break;			/* No hope to continue */
139 		return start;
140 	}
141 
142 	return BAD_SEG;
143 }
144 
145 uint64_t
146 kboot_get_phys_load_segment(void)
147 {
148 	static uint64_t base_seg = BAD_SEG;
149 
150 	if (base_seg != BAD_SEG)
151 		return (base_seg);
152 
153 	if (nr_seg > 0)
154 		base_seg = find_ram(segs, nr_seg, 2ULL << 20, 2ULL << 20,
155 		    64ULL << 20, 4ULL << 30);
156 	if (base_seg == BAD_SEG) {
157 		/* XXX Should fall back to using /proc/iomem maybe? */
158 		/* XXX PUNT UNTIL I NEED SOMETHING BETTER */
159 		base_seg = 300ULL * (1 << 20);
160 	}
161 	return (base_seg);
162 }
163 
164 void
165 bi_loadsmap(struct preloaded_file *kfp)
166 {
167 	struct bios_smap smap[32], *sm;
168 	struct memory_segments *s;
169 	int smapnum, len;
170 
171 	for (smapnum = 0; smapnum < min(32, nr_seg); smapnum++) {
172 		sm = &smap[smapnum];
173 		s = &segs[smapnum];
174 		sm->base = s->start;
175 		sm->length = s->end - s->start + 1;
176 		sm->type = SMAP_TYPE_MEMORY;
177 	}
178 
179         len = smapnum * sizeof(struct bios_smap);
180         file_addmetadata(kfp, MODINFOMD_SMAP, len, &smap[0]);
181 }
182