xref: /openbsd-src/sys/arch/amd64/stand/efiboot/exec_i386.c (revision 02c03184b3a77cc7142aa12091094759d56da8f6)
1 /*	$OpenBSD: exec_i386.c,v 1.12 2024/10/04 22:21:28 bluhm Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-1998 Michael Shalayeff
5  * Copyright (c) 1997 Tobias Weingartner
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 AUTHORS ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * 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 
31 #include <sys/param.h>
32 #include <sys/disklabel.h>
33 #include <dev/cons.h>
34 #include <lib/libsa/loadfile.h>
35 #include <machine/biosvar.h>
36 #include <machine/pte.h>
37 #include <machine/specialreg.h>
38 #include <stand/boot/bootarg.h>
39 
40 #include "cmd.h"
41 #include "disk.h"
42 #include "libsa.h"
43 
44 #ifdef SOFTRAID
45 #include <dev/softraidvar.h>
46 #include <lib/libsa/softraid.h>
47 #include "softraid_amd64.h"
48 #endif
49 
50 #include <efi.h>
51 #include <efiapi.h>
52 #include "efiboot.h"
53 
54 extern EFI_BOOT_SERVICES	*BS;
55 
56 typedef void (*startfuncp)(int, int, int, int, int, int, int, int)
57     __attribute__ ((noreturn));
58 
59 void ucode_load(void);
60 void protect_writeable(uint64_t, size_t);
61 extern struct cmd_state cmd;
62 
63 char *bootmac = NULL;
64 
65 void
66 run_loadfile(uint64_t *marks, int howto)
67 {
68 	u_long entry;
69 #ifdef EXEC_DEBUG
70 	extern int debug;
71 #endif
72 	dev_t bootdev = bootdev_dip->bootdev;
73 	size_t ac = BOOTARG_LEN;
74 	caddr_t av = (caddr_t)BOOTARG_OFF;
75 	bios_ddb_t ddb;
76 	extern int db_console;
77 	bios_bootduid_t bootduid;
78 #ifdef SOFTRAID
79 	bios_bootsr_t bootsr;
80 	struct sr_boot_volume *bv;
81 #endif
82 	int i;
83 	u_long delta;
84 	extern u_long efi_loadaddr;
85 
86 	if ((av = alloc(ac)) == NULL)
87 		panic("alloc for bootarg");
88 	efi_makebootargs();
89 	efi_setconsdev();
90 	delta = -efi_loadaddr;
91 	if (sa_cleanup != NULL)
92 		(*sa_cleanup)();
93 
94 	if (bootmac != NULL)
95 		addbootarg(BOOTARG_BOOTMAC, sizeof(bios_bootmac_t), bootmac);
96 
97 	if (db_console != -1) {
98 		ddb.db_console = db_console;
99 		addbootarg(BOOTARG_DDB, sizeof(ddb), &ddb);
100 	}
101 
102 	bcopy(bootdev_dip->disklabel.d_uid, &bootduid.duid, sizeof(bootduid));
103 	addbootarg(BOOTARG_BOOTDUID, sizeof(bootduid), &bootduid);
104 
105 	ucode_load();
106 
107 #ifdef SOFTRAID
108 	if (bootdev_dip->sr_vol != NULL) {
109 		bv = bootdev_dip->sr_vol;
110 		bzero(&bootsr, sizeof(bootsr));
111 		bcopy(&bv->sbv_uuid, &bootsr.uuid, sizeof(bootsr.uuid));
112 		if (bv->sbv_maskkey != NULL)
113 			bcopy(bv->sbv_maskkey, &bootsr.maskkey,
114 			    sizeof(bootsr.maskkey));
115 		addbootarg(BOOTARG_BOOTSR, sizeof(bios_bootsr_t), &bootsr);
116 		explicit_bzero(&bootsr, sizeof(bootsr));
117 	}
118 
119 	sr_clear_keys();
120 #endif
121 
122 	entry = marks[MARK_ENTRY] & 0x0fffffff;
123 	entry += delta;
124 
125 	printf("entry point at 0x%lx\n", entry);
126 
127 	/* Sync the memory map and call ExitBootServices() */
128 	efi_cleanup();
129 
130 	/* Pass memory map to the kernel */
131 	mem_pass();
132 
133 	/*
134 	 * This code may be used both for 64bit and 32bit.  Make sure the
135 	 * bootarg is always 32bit, even on amd64.
136 	 */
137 #ifdef __amd64__
138 	makebootargs32(av, &ac);
139 #else
140 	makebootargs(av, &ac);
141 #endif
142 
143 	/*
144 	 * Move the loaded kernel image to the usual place after calling
145 	 * ExitBootServices().
146 	 */
147 #ifdef __amd64__
148 	protect_writeable(marks[MARK_START] + delta,
149 	    marks[MARK_END] - marks[MARK_START]);
150 #endif
151 	memmove((void *)marks[MARK_START] + delta, (void *)marks[MARK_START],
152 	    marks[MARK_END] - marks[MARK_START]);
153 	for (i = 0; i < MARK_MAX; i++)
154 		marks[i] += delta;
155 
156 #ifdef __amd64__
157 	(*run_i386)((u_long)run_i386, entry, howto, bootdev, BOOTARG_APIVER,
158 	    marks[MARK_END], extmem, cnvmem, ac, (intptr_t)av);
159 #else
160 	/* stack and the gung is ok at this point, so, no need for asm setup */
161 	(*(startfuncp)entry)(howto, bootdev, BOOTARG_APIVER, marks[MARK_END],
162 	    extmem, cnvmem, ac, (int)av);
163 #endif
164 	/* not reached */
165 }
166 
167 void
168 ucode_load(void)
169 {
170 	EFI_PHYSICAL_ADDRESS addr;
171 	uint32_t model, family, stepping;
172 	uint32_t dummy, signature;
173 	uint32_t vendor[4];
174 	bios_ucode_t uc;
175 	struct stat sb;
176 	char path[128];
177 	size_t buflen;
178 	char *buf;
179 	int fd;
180 
181 	CPUID(0, dummy, vendor[0], vendor[2], vendor[1]);
182 	vendor[3] = 0; /* NULL-terminate */
183 	if (strcmp((char *)vendor, "GenuineIntel") != 0 &&
184 	    strcmp((char *)vendor, "AuthenticAMD") != 0)
185 		return;
186 
187 	CPUID(1, signature, dummy, dummy, dummy);
188 	family = (signature >> 8) & 0x0f;
189 	model = (signature >> 4) & 0x0f;
190 	if (family == 0x6 || family == 0xf) {
191 		family += (signature >> 20) & 0xff;
192 		model += ((signature >> 16) & 0x0f) << 4;
193 	}
194 	stepping = (signature >> 0) & 0x0f;
195 
196 	if (strcmp((char *)vendor, "GenuineIntel") == 0) {
197 		snprintf(path, sizeof(path),
198 		    "%s:/etc/firmware/intel/%02x-%02x-%02x",
199 		    cmd.bootdev, family, model, stepping);
200 	} else if (strcmp((char *)vendor, "AuthenticAMD") == 0) {
201 		if (family < 0x10)
202 			return;
203 		else if (family <= 0x14)
204 			snprintf(path, sizeof(path),
205 			    "%s:/etc/firmware/amd/microcode_amd.bin",
206 			    cmd.bootdev);
207 		else
208 			snprintf(path, sizeof(path),
209 			    "%s:/etc/firmware/amd/microcode_amd_fam%02xh.bin",
210 			    cmd.bootdev, family);
211 	}
212 
213 	fd = open(path, O_RDONLY);
214 	if (fd == -1)
215 		return;
216 
217 	if (fstat(fd, &sb) == -1)
218 		return;
219 
220 	buflen = sb.st_size;
221 	addr = 16 * 1024 * 1024;
222 	if (BS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
223 	    EFI_SIZE_TO_PAGES(buflen), &addr) != EFI_SUCCESS) {
224 		printf("cannot allocate memory for ucode\n");
225 		return;
226 	}
227 	buf = (char *)((paddr_t)addr);
228 
229 	if (read(fd, buf, buflen) != buflen) {
230 		close(fd);
231 		return;
232 	}
233 
234 	uc.uc_addr = (uint64_t)buf;
235 	uc.uc_size = (uint64_t)buflen;
236 	addbootarg(BOOTARG_UCODE, sizeof(uc), &uc);
237 
238 	close(fd);
239 }
240 
241 #ifdef __amd64__
242 int
243 detect_sev(void)
244 {
245 	uint32_t max_ex_leaf, sev_feat;
246 	uint32_t vendor[4];
247 	uint32_t sev_status, dummy;
248 
249 	/* check whether we have SEV feature cpuid leaf */
250 	CPUID(0x80000000, max_ex_leaf, vendor[0], vendor[2], vendor[1]);
251 	vendor[3] = 0; /* NULL-terminate */
252 	if (strcmp((char *)vendor, "AuthenticAMD") != 0 ||
253 	    max_ex_leaf < 0x8000001F)
254 		return -ENODEV;
255 
256 	CPUID(0x8000001F, sev_feat, dummy, dummy,  dummy);
257 	/* check that SEV is supported */
258 	if ((sev_feat & CPUIDEAX_SEV) == 0)
259 		return -ENODEV;
260 
261 	__asm volatile ("rdmsr" : "=a" (sev_status), "=d"(dummy) : "c"(MSR_SEV_STATUS));
262 	/* check whether SEV is enabled */
263 	if ((sev_status & SEV_STAT_ENABLED) == 0)
264 		return -ENODEV;
265 
266 	return 0;
267 }
268 
269 void
270 protect_writeable(uint64_t addr, size_t len)
271 {
272 	uint64_t end = addr + len;
273 	uint64_t *cr3, *p;
274 	uint64_t cr0;
275 	size_t idx;
276 
277 	if (detect_sev() == 0)
278 		return;
279 
280 	__asm volatile("movq %%cr0, %0;" : "=r"(cr0) : :);
281 	if ((cr0 & CR0_PG) == 0)
282 		return;
283 	__asm volatile("movq %%cr3, %0;" : "=r"(cr3) : :);
284 
285 	for (addr &= ~(uint64_t)PAGE_MASK; addr < end; addr += PAGE_SIZE) {
286 		idx = (addr & L4_MASK) >> L4_SHIFT;
287 		if ((cr3[idx] & PG_RW) == 0)
288 			cr3[idx] |= PG_RW;
289 		if (cr3[idx] & PG_PS)
290 			continue;
291 		p = (uint64_t *)(cr3[idx] & PG_FRAME);
292 
293 		idx = (addr & L3_MASK) >> L3_SHIFT;
294 		if ((p[idx] & PG_RW) == 0)
295 			p[idx] |= PG_RW;
296 		if (p[idx] & PG_PS)
297 			continue;
298 		p = (uint64_t *)(p[idx] & PG_FRAME);
299 
300 		idx = (addr & L2_MASK) >> L2_SHIFT;
301 		if ((p[idx] & PG_RW) == 0)
302 			p[idx] |= PG_RW;
303 		if (p[idx] & PG_PS)
304 			continue;
305 		p = (uint64_t *)(p[idx] & PG_FRAME);
306 
307 		idx = (addr & L1_MASK) >> L1_SHIFT;
308 		if ((p[idx] & PG_RW) == 0)
309 			p[idx] |= PG_RW;
310 	}
311 
312 	/* tlb flush */
313 	__asm volatile("movq %0,%%cr3" : : "r"(cr3) :);
314 }
315 #endif
316