xref: /openbsd/sys/arch/amd64/stand/efiboot/exec_i386.c (revision 3bef86f7)
1 /*	$OpenBSD: exec_i386.c,v 1.11 2023/07/22 10:11:19 jsg 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 void
243 protect_writeable(uint64_t addr, size_t len)
244 {
245 	uint64_t end = addr + len;
246 	uint64_t *cr3, *p;
247 	uint64_t cr0;
248 	size_t idx;
249 
250 	__asm volatile("movq %%cr0, %0;" : "=r"(cr0) : :);
251 	if ((cr0 & CR0_PG) == 0)
252 		return;
253 	__asm volatile("movq %%cr3, %0;" : "=r"(cr3) : :);
254 
255 	for (addr &= ~(uint64_t)PAGE_MASK; addr < end; addr += PAGE_SIZE) {
256 		idx = (addr & L4_MASK) >> L4_SHIFT;
257 		if ((cr3[idx] & PG_RW) == 0)
258 			cr3[idx] |= PG_RW;
259 		if (cr3[idx] & PG_PS)
260 			continue;
261 		p = (uint64_t *)(cr3[idx] & PG_FRAME);
262 
263 		idx = (addr & L3_MASK) >> L3_SHIFT;
264 		if ((p[idx] & PG_RW) == 0)
265 			p[idx] |= PG_RW;
266 		if (p[idx] & PG_PS)
267 			continue;
268 		p = (uint64_t *)(p[idx] & PG_FRAME);
269 
270 		idx = (addr & L2_MASK) >> L2_SHIFT;
271 		if ((p[idx] & PG_RW) == 0)
272 			p[idx] |= PG_RW;
273 		if (p[idx] & PG_PS)
274 			continue;
275 		p = (uint64_t *)(p[idx] & PG_FRAME);
276 
277 		idx = (addr & L1_MASK) >> L1_SHIFT;
278 		if ((p[idx] & PG_RW) == 0)
279 			p[idx] |= PG_RW;
280 	}
281 
282 	/* tlb flush */
283 	__asm volatile("movq %0,%%cr3" : : "r"(cr3) :);
284 }
285 #endif
286