1a5602e1bSKeith M Wesolowski /*
2a5602e1bSKeith M Wesolowski  *  GRUB  --  GRand Unified Bootloader
3a5602e1bSKeith M Wesolowski  *  Copyright (C) 1999,2000,2001,2002,2003,2004  Free Software Foundation, Inc.
4a5602e1bSKeith M Wesolowski  *  Copyright (c) 2013 Joyent, Inc.  All rights reserved.
5*1a065e93SAndrew Stormont  *  Copyright 2021 RackTop Systems, Inc.
6a5602e1bSKeith M Wesolowski  *
7a5602e1bSKeith M Wesolowski  *  This program is free software; you can redistribute it and/or modify
8a5602e1bSKeith M Wesolowski  *  it under the terms of the GNU General Public License as published by
9a5602e1bSKeith M Wesolowski  *  the Free Software Foundation; either version 2 of the License, or
10a5602e1bSKeith M Wesolowski  *  (at your option) any later version.
11a5602e1bSKeith M Wesolowski  *
12a5602e1bSKeith M Wesolowski  *  This program is distributed in the hope that it will be useful,
13a5602e1bSKeith M Wesolowski  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14a5602e1bSKeith M Wesolowski  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15a5602e1bSKeith M Wesolowski  *  GNU General Public License for more details.
16a5602e1bSKeith M Wesolowski  *
17a5602e1bSKeith M Wesolowski  *  You should have received a copy of the GNU General Public License
18a5602e1bSKeith M Wesolowski  *  along with this program; if not, write to the Free Software
19a5602e1bSKeith M Wesolowski  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20a5602e1bSKeith M Wesolowski  */
21a5602e1bSKeith M Wesolowski 
22a5602e1bSKeith M Wesolowski #include <expand.h>
23a5602e1bSKeith M Wesolowski #include <shared.h>
24a5602e1bSKeith M Wesolowski 
25a5602e1bSKeith M Wesolowski #ifdef	SUPPORT_NETBOOT
26a5602e1bSKeith M Wesolowski #include <grub.h>
27a5602e1bSKeith M Wesolowski #endif
28a5602e1bSKeith M Wesolowski 
29a5602e1bSKeith M Wesolowski #include <cpu.h>
30a5602e1bSKeith M Wesolowski 
31a5602e1bSKeith M Wesolowski #define	EVF_DEFINED	0x01
32a5602e1bSKeith M Wesolowski #define	EVF_VALUESET	0x02
33a5602e1bSKeith M Wesolowski 
34a5602e1bSKeith M Wesolowski typedef struct variable {
35a5602e1bSKeith M Wesolowski 	char v_name[EV_NAMELEN];
36a5602e1bSKeith M Wesolowski 	unsigned int v_flags;
37a5602e1bSKeith M Wesolowski 	char v_value[220];	/* 256 - EV_NAMELEN - sizeof (fields) */
38a5602e1bSKeith M Wesolowski } variable_t;
39a5602e1bSKeith M Wesolowski 
40a5602e1bSKeith M Wesolowski static variable_t expvars[32];
41a5602e1bSKeith M Wesolowski static const unsigned int nexpvars = 32;
42a5602e1bSKeith M Wesolowski 
43a5602e1bSKeith M Wesolowski int
set_variable(const char * name,const char * value)44a5602e1bSKeith M Wesolowski set_variable(const char *name, const char *value)
45a5602e1bSKeith M Wesolowski {
46a5602e1bSKeith M Wesolowski 	unsigned int i;
47a5602e1bSKeith M Wesolowski 	unsigned int avail = nexpvars;
48a5602e1bSKeith M Wesolowski 
49a5602e1bSKeith M Wesolowski 	if (strlen(name) >= sizeof (expvars[0].v_name))
50a5602e1bSKeith M Wesolowski 		return (ERR_WONT_FIT);
51a5602e1bSKeith M Wesolowski 
52a5602e1bSKeith M Wesolowski 	if (value != NULL && strlen(value) >= sizeof (expvars[0].v_value))
53a5602e1bSKeith M Wesolowski 		return (ERR_WONT_FIT);
54a5602e1bSKeith M Wesolowski 
55a5602e1bSKeith M Wesolowski 	for (i = 0; i < nexpvars; i++) {
56a5602e1bSKeith M Wesolowski 		if (expvars[i].v_flags & EVF_DEFINED) {
57a5602e1bSKeith M Wesolowski 			if (grub_strcmp(expvars[i].v_name, name) == 0)
58a5602e1bSKeith M Wesolowski 				break;
59a5602e1bSKeith M Wesolowski 		} else if (i < avail) {
60a5602e1bSKeith M Wesolowski 			avail = i;
61a5602e1bSKeith M Wesolowski 		}
62a5602e1bSKeith M Wesolowski 	}
63a5602e1bSKeith M Wesolowski 
64a5602e1bSKeith M Wesolowski 	if (i == nexpvars) {
65a5602e1bSKeith M Wesolowski 		if (avail == nexpvars)
66a5602e1bSKeith M Wesolowski 			return (ERR_WONT_FIT);
67a5602e1bSKeith M Wesolowski 
68a5602e1bSKeith M Wesolowski 		i = avail;
69a5602e1bSKeith M Wesolowski 		(void) grub_strcpy(expvars[i].v_name, name);
70a5602e1bSKeith M Wesolowski 		expvars[i].v_flags = EVF_DEFINED;
71a5602e1bSKeith M Wesolowski 	}
72a5602e1bSKeith M Wesolowski 
73a5602e1bSKeith M Wesolowski 	if (value != NULL) {
74a5602e1bSKeith M Wesolowski 		(void) grub_strcpy(expvars[i].v_value, value);
75a5602e1bSKeith M Wesolowski 		expvars[i].v_flags |= EVF_VALUESET;
76a5602e1bSKeith M Wesolowski 	} else {
77a5602e1bSKeith M Wesolowski 		expvars[i].v_flags &= ~EVF_VALUESET;
78a5602e1bSKeith M Wesolowski 	}
79a5602e1bSKeith M Wesolowski 
80a5602e1bSKeith M Wesolowski 	return (0);
81a5602e1bSKeith M Wesolowski }
82a5602e1bSKeith M Wesolowski 
83a5602e1bSKeith M Wesolowski const char *
get_variable(const char * name)84a5602e1bSKeith M Wesolowski get_variable(const char *name)
85a5602e1bSKeith M Wesolowski {
86a5602e1bSKeith M Wesolowski 	unsigned int i;
87a5602e1bSKeith M Wesolowski 
88a5602e1bSKeith M Wesolowski 	for (i = 0; i < nexpvars; i++) {
89a5602e1bSKeith M Wesolowski 		if (!(expvars[i].v_flags & EVF_DEFINED))
90a5602e1bSKeith M Wesolowski 			continue;
91a5602e1bSKeith M Wesolowski 		if (grub_strcmp(expvars[i].v_name, name) == 0) {
92a5602e1bSKeith M Wesolowski 			if (expvars[i].v_flags & EVF_VALUESET)
93a5602e1bSKeith M Wesolowski 				return (expvars[i].v_value);
94a5602e1bSKeith M Wesolowski 			return ("");
95a5602e1bSKeith M Wesolowski 		}
96a5602e1bSKeith M Wesolowski 	}
97a5602e1bSKeith M Wesolowski 
98a5602e1bSKeith M Wesolowski 	return (NULL);
99a5602e1bSKeith M Wesolowski }
100a5602e1bSKeith M Wesolowski 
101a5602e1bSKeith M Wesolowski static int
detect_target_operating_mode(void)102a5602e1bSKeith M Wesolowski detect_target_operating_mode(void)
103a5602e1bSKeith M Wesolowski {
104a5602e1bSKeith M Wesolowski 	int ret, ah;
105a5602e1bSKeith M Wesolowski 
106a5602e1bSKeith M Wesolowski 	/*
107a5602e1bSKeith M Wesolowski 	 * This function returns 16 bits.  The upper 8 are the value of %ah
108a5602e1bSKeith M Wesolowski 	 * after calling int 15/ec00.  The lower 8 bits are zero if the BIOS
109a5602e1bSKeith M Wesolowski 	 * call left CF clear, nonzero otherwise.
110a5602e1bSKeith M Wesolowski 	 */
111a5602e1bSKeith M Wesolowski 	ret = get_target_operating_mode();
1127232236bSAndriy Gapon 	ah = ret >> 8;
1137232236bSAndriy Gapon 	ret &= 0xff;
114a5602e1bSKeith M Wesolowski 
115a5602e1bSKeith M Wesolowski 	if (ah == 0x86 && ret != 0) {
116a5602e1bSKeith M Wesolowski 		grub_printf("[BIOS 'Detect Target Operating Mode' "
117a5602e1bSKeith M Wesolowski 		    "callback unsupported on this platform]\n");
1187232236bSAndriy Gapon 		return (1);	/* unsupported, ignore */
119a5602e1bSKeith M Wesolowski 	}
120a5602e1bSKeith M Wesolowski 
121a5602e1bSKeith M Wesolowski 	if (ah == 0 && ret == 0) {
122a5602e1bSKeith M Wesolowski 		grub_printf("[BIOS accepted mixed-mode target setting!]\n");
1237232236bSAndriy Gapon 		return (1);	/* told the bios what we're up to */
124a5602e1bSKeith M Wesolowski 	}
125a5602e1bSKeith M Wesolowski 
126a5602e1bSKeith M Wesolowski 	if (ah == 0 && ret != 0) {
127a5602e1bSKeith M Wesolowski 		grub_printf("fatal: BIOS reports this machine CANNOT run in "
128a5602e1bSKeith M Wesolowski 		    "mixed 32/64-bit mode!\n");
129a5602e1bSKeith M Wesolowski 		return (0);
130a5602e1bSKeith M Wesolowski 	}
131a5602e1bSKeith M Wesolowski 
132a5602e1bSKeith M Wesolowski 	grub_printf("warning: BIOS Detect Target Operating Mode callback "
133a5602e1bSKeith M Wesolowski 	    "confused.\n         %%ax >> 8 = 0x%x, carry = %d\n", ah, ret);
134a5602e1bSKeith M Wesolowski 
135a5602e1bSKeith M Wesolowski 	return (1);
136a5602e1bSKeith M Wesolowski }
137a5602e1bSKeith M Wesolowski 
138a5602e1bSKeith M Wesolowski static int
amd64_config_cpu(void)139a5602e1bSKeith M Wesolowski amd64_config_cpu(void)
140a5602e1bSKeith M Wesolowski {
141a5602e1bSKeith M Wesolowski 	struct amd64_cpuid_regs __vcr, *vcr = &__vcr;
142a5602e1bSKeith M Wesolowski 	uint32_t maxeax;
143a5602e1bSKeith M Wesolowski 	uint32_t max_maxeax = 0x100;
144a5602e1bSKeith M Wesolowski 	char vendor[13];
145a5602e1bSKeith M Wesolowski 	int isamd64 = 0;
146a5602e1bSKeith M Wesolowski 	uint32_t stdfeatures = 0, xtdfeatures = 0;
147a5602e1bSKeith M Wesolowski 	uint64_t efer;
148a5602e1bSKeith M Wesolowski 
149a5602e1bSKeith M Wesolowski 	/*
150a5602e1bSKeith M Wesolowski 	 * This check may seem silly, but if the C preprocesor symbol __amd64
151a5602e1bSKeith M Wesolowski 	 * is #defined during compilation, something that may outwardly seem
152a5602e1bSKeith M Wesolowski 	 * like a good idea, uts/common/sys/isa_defs.h will #define _LP64,
153a5602e1bSKeith M Wesolowski 	 * which will cause uts/common/sys/int_types.h to typedef uint64_t as
154a5602e1bSKeith M Wesolowski 	 * an unsigned long - which is only 4 bytes in size when using a 32-bit
155a5602e1bSKeith M Wesolowski 	 * compiler.
156a5602e1bSKeith M Wesolowski 	 *
157a5602e1bSKeith M Wesolowski 	 * If that happens, all the page table translation routines will fail
158a5602e1bSKeith M Wesolowski 	 * horribly, so check the size of uint64_t just to insure some degree
159a5602e1bSKeith M Wesolowski 	 * of sanity in future operations.
160a5602e1bSKeith M Wesolowski 	 */
161a5602e1bSKeith M Wesolowski 	/*LINTED [sizeof result is invarient]*/
162a5602e1bSKeith M Wesolowski 	if (sizeof (uint64_t) != 8)
163a5602e1bSKeith M Wesolowski 		prom_panic("grub compiled improperly, unable to boot "
164a5602e1bSKeith M Wesolowski 		    "64-bit AMD64 executables");
165a5602e1bSKeith M Wesolowski 
166a5602e1bSKeith M Wesolowski 	/*
167a5602e1bSKeith M Wesolowski 	 * If the CPU doesn't support the CPUID instruction, it's definitely
168a5602e1bSKeith M Wesolowski 	 * not an AMD64.
169a5602e1bSKeith M Wesolowski 	 */
170a5602e1bSKeith M Wesolowski 	if (amd64_cpuid_supported() == 0)
171a5602e1bSKeith M Wesolowski 		return (0);
172a5602e1bSKeith M Wesolowski 
173a5602e1bSKeith M Wesolowski 	amd64_cpuid_insn(0, vcr);
174a5602e1bSKeith M Wesolowski 
175a5602e1bSKeith M Wesolowski 	maxeax = vcr->r_eax;
176a5602e1bSKeith M Wesolowski 	{
177a5602e1bSKeith M Wesolowski 		/*LINTED [vendor string from cpuid data]*/
178a5602e1bSKeith M Wesolowski 		uint32_t *iptr = (uint32_t *)vendor;
179a5602e1bSKeith M Wesolowski 
180a5602e1bSKeith M Wesolowski 		*iptr++ = vcr->r_ebx;
181a5602e1bSKeith M Wesolowski 		*iptr++ = vcr->r_edx;
182a5602e1bSKeith M Wesolowski 		*iptr++ = vcr->r_ecx;
183a5602e1bSKeith M Wesolowski 
184a5602e1bSKeith M Wesolowski 		vendor[12] = '\0';
185a5602e1bSKeith M Wesolowski 	}
186a5602e1bSKeith M Wesolowski 
187a5602e1bSKeith M Wesolowski 	if (maxeax > max_maxeax) {
188a5602e1bSKeith M Wesolowski 		grub_printf("cpu: warning, maxeax was 0x%x -> 0x%x\n",
189a5602e1bSKeith M Wesolowski 		    maxeax, max_maxeax);
190a5602e1bSKeith M Wesolowski 		maxeax = max_maxeax;
191a5602e1bSKeith M Wesolowski 	}
192a5602e1bSKeith M Wesolowski 
193a5602e1bSKeith M Wesolowski 	if (maxeax < 1)
1947232236bSAndriy Gapon 		return (0);	/* no additional functions, not an AMD64 */
195a5602e1bSKeith M Wesolowski 	else {
196a5602e1bSKeith M Wesolowski 		uint_t family, model, step;
197a5602e1bSKeith M Wesolowski 
198a5602e1bSKeith M Wesolowski 		amd64_cpuid_insn(1, vcr);
199a5602e1bSKeith M Wesolowski 
200a5602e1bSKeith M Wesolowski 		/*
201a5602e1bSKeith M Wesolowski 		 * All AMD64/IA32e processors technically SHOULD report
202a5602e1bSKeith M Wesolowski 		 * themselves as being in family 0xf, but for some reason
203a5602e1bSKeith M Wesolowski 		 * Simics doesn't, and this may change in the future, so
204a5602e1bSKeith M Wesolowski 		 * don't error out if it's not true.
205a5602e1bSKeith M Wesolowski 		 */
206a5602e1bSKeith M Wesolowski 		if ((family = BITX(vcr->r_eax, 11, 8)) == 0xf)
207a5602e1bSKeith M Wesolowski 			family += BITX(vcr->r_eax, 27, 20);
208a5602e1bSKeith M Wesolowski 
209a5602e1bSKeith M Wesolowski 		if ((model = BITX(vcr->r_eax, 7, 4)) == 0xf)
210a5602e1bSKeith M Wesolowski 			model += BITX(vcr->r_eax, 19, 16) << 4;
211a5602e1bSKeith M Wesolowski 		step = BITX(vcr->r_eax, 3, 0);
212a5602e1bSKeith M Wesolowski 
213a5602e1bSKeith M Wesolowski 		grub_printf("cpu: '%s' family %d model %d step %d\n",
214a5602e1bSKeith M Wesolowski 		    vendor, family, model, step);
215a5602e1bSKeith M Wesolowski 		stdfeatures = vcr->r_edx;
216a5602e1bSKeith M Wesolowski 	}
217a5602e1bSKeith M Wesolowski 
218a5602e1bSKeith M Wesolowski 	amd64_cpuid_insn(0x80000000, vcr);
219a5602e1bSKeith M Wesolowski 
220a5602e1bSKeith M Wesolowski 	if (vcr->r_eax & 0x80000000) {
221a5602e1bSKeith M Wesolowski 		uint32_t xmaxeax = vcr->r_eax;
222a5602e1bSKeith M Wesolowski 		const uint32_t max_xmaxeax = 0x80000100;
223a5602e1bSKeith M Wesolowski 
224a5602e1bSKeith M Wesolowski 		if (xmaxeax > max_xmaxeax) {
225a5602e1bSKeith M Wesolowski 			grub_printf("amd64: warning, xmaxeax was "
226a5602e1bSKeith M Wesolowski 			    "0x%x -> 0x%x\n", xmaxeax, max_xmaxeax);
227a5602e1bSKeith M Wesolowski 			xmaxeax = max_xmaxeax;
228a5602e1bSKeith M Wesolowski 		}
229a5602e1bSKeith M Wesolowski 
230a5602e1bSKeith M Wesolowski 		if (xmaxeax >= 0x80000001) {
231a5602e1bSKeith M Wesolowski 			amd64_cpuid_insn(0x80000001, vcr);
232a5602e1bSKeith M Wesolowski 			xtdfeatures = vcr->r_edx;
233a5602e1bSKeith M Wesolowski 		}
234a5602e1bSKeith M Wesolowski 	}
235a5602e1bSKeith M Wesolowski 
2367232236bSAndriy Gapon 	if (BITX(xtdfeatures, 29, 29))		/* long mode */
237a5602e1bSKeith M Wesolowski 		isamd64++;
238a5602e1bSKeith M Wesolowski 	else
239a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does NOT support long mode\n");
240a5602e1bSKeith M Wesolowski 
241a5602e1bSKeith M Wesolowski 	if (!BITX(stdfeatures, 0, 0)) {
242a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does NOT support FPU\n");
243a5602e1bSKeith M Wesolowski 		isamd64--;
244a5602e1bSKeith M Wesolowski 	}
245a5602e1bSKeith M Wesolowski 
246a5602e1bSKeith M Wesolowski 	if (!BITX(stdfeatures, 4, 4)) {
247a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does NOT support TSC\n");
248a5602e1bSKeith M Wesolowski 		isamd64--;
249a5602e1bSKeith M Wesolowski 	}
250a5602e1bSKeith M Wesolowski 
251a5602e1bSKeith M Wesolowski 	if (!BITX(stdfeatures, 5, 5)) {
252a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does NOT support MSRs\n");
253a5602e1bSKeith M Wesolowski 		isamd64--;
254a5602e1bSKeith M Wesolowski 	}
255a5602e1bSKeith M Wesolowski 
256a5602e1bSKeith M Wesolowski 	if (!BITX(stdfeatures, 6, 6)) {
257a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does NOT support PAE\n");
258a5602e1bSKeith M Wesolowski 		isamd64--;
259a5602e1bSKeith M Wesolowski 	}
260a5602e1bSKeith M Wesolowski 
261a5602e1bSKeith M Wesolowski 	if (!BITX(stdfeatures, 8, 8)) {
262a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does NOT support CX8\n");
263a5602e1bSKeith M Wesolowski 		isamd64--;
264a5602e1bSKeith M Wesolowski 	}
265a5602e1bSKeith M Wesolowski 
266a5602e1bSKeith M Wesolowski 	if (!BITX(stdfeatures, 13, 13)) {
267a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does NOT support PGE\n");
268a5602e1bSKeith M Wesolowski 		isamd64--;
269a5602e1bSKeith M Wesolowski 	}
270a5602e1bSKeith M Wesolowski 
271a5602e1bSKeith M Wesolowski 	if (!BITX(stdfeatures, 19, 19)) {
272a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does NOT support CLFSH\n");
273a5602e1bSKeith M Wesolowski 		isamd64--;
274a5602e1bSKeith M Wesolowski 	}
275a5602e1bSKeith M Wesolowski 
276a5602e1bSKeith M Wesolowski 	if (!BITX(stdfeatures, 23, 23)) {
277a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does NOT support MMX\n");
278a5602e1bSKeith M Wesolowski 		isamd64--;
279a5602e1bSKeith M Wesolowski 	}
280a5602e1bSKeith M Wesolowski 
281a5602e1bSKeith M Wesolowski 	if (!BITX(stdfeatures, 24, 24)) {
282a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does NOT support FXSR\n");
283a5602e1bSKeith M Wesolowski 		isamd64--;
284a5602e1bSKeith M Wesolowski 	}
285a5602e1bSKeith M Wesolowski 
286a5602e1bSKeith M Wesolowski 	if (!BITX(stdfeatures, 25, 25)) {
287a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does NOT support SSE\n");
288a5602e1bSKeith M Wesolowski 		isamd64--;
289a5602e1bSKeith M Wesolowski 	}
290a5602e1bSKeith M Wesolowski 
291a5602e1bSKeith M Wesolowski 	if (!BITX(stdfeatures, 26, 26)) {
292a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does NOT support SSE2\n");
293a5602e1bSKeith M Wesolowski 		isamd64--;
294a5602e1bSKeith M Wesolowski 	}
295a5602e1bSKeith M Wesolowski 
296a5602e1bSKeith M Wesolowski 	if (isamd64 < 1) {
297a5602e1bSKeith M Wesolowski 		grub_printf("amd64: CPU does not support amd64 executables.\n");
298a5602e1bSKeith M Wesolowski 		return (0);
299a5602e1bSKeith M Wesolowski 	}
300a5602e1bSKeith M Wesolowski 
301a5602e1bSKeith M Wesolowski 	amd64_rdmsr(MSR_AMD_EFER, &efer);
302a5602e1bSKeith M Wesolowski 	if (efer & AMD_EFER_SCE)
303a5602e1bSKeith M Wesolowski 		grub_printf("amd64: EFER_SCE (syscall/sysret) already "
304a5602e1bSKeith M Wesolowski 		    "enabled\n");
305a5602e1bSKeith M Wesolowski 	if (efer & AMD_EFER_NXE)
306a5602e1bSKeith M Wesolowski 		grub_printf("amd64: EFER_NXE (no-exec prot) already enabled\n");
307a5602e1bSKeith M Wesolowski 	if (efer & AMD_EFER_LME)
308a5602e1bSKeith M Wesolowski 		grub_printf("amd64: EFER_LME (long mode) already enabled\n");
309a5602e1bSKeith M Wesolowski 
310a5602e1bSKeith M Wesolowski 	return (detect_target_operating_mode());
311a5602e1bSKeith M Wesolowski }
312a5602e1bSKeith M Wesolowski 
313a5602e1bSKeith M Wesolowski static int
isamd64()314a5602e1bSKeith M Wesolowski isamd64()
315a5602e1bSKeith M Wesolowski {
316a5602e1bSKeith M Wesolowski 	static int ret = -1;
317a5602e1bSKeith M Wesolowski 
318a5602e1bSKeith M Wesolowski 	if (ret == -1)
319a5602e1bSKeith M Wesolowski 		ret = amd64_config_cpu();
320a5602e1bSKeith M Wesolowski 
321a5602e1bSKeith M Wesolowski 	return (ret);
322a5602e1bSKeith M Wesolowski }
323a5602e1bSKeith M Wesolowski 
324a5602e1bSKeith M Wesolowski static int
check_min_mem64(void)325a5602e1bSKeith M Wesolowski check_min_mem64(void)
326a5602e1bSKeith M Wesolowski {
327a5602e1bSKeith M Wesolowski 	if (min_mem64 == 0)
328a5602e1bSKeith M Wesolowski 		return (1);
329a5602e1bSKeith M Wesolowski 
330a5602e1bSKeith M Wesolowski 	if ((mbi.mem_upper / 10240) * 11 >= min_mem64)
331a5602e1bSKeith M Wesolowski 		return (1);
332a5602e1bSKeith M Wesolowski 
333a5602e1bSKeith M Wesolowski 	return (0);
334a5602e1bSKeith M Wesolowski }
335a5602e1bSKeith M Wesolowski 
336a5602e1bSKeith M Wesolowski /*
337a5602e1bSKeith M Wesolowski  * Given the nul-terminated input string s, expand all variable references
338a5602e1bSKeith M Wesolowski  * within that string into the buffer pointed to by d, which must be of length
339a5602e1bSKeith M Wesolowski  * not less than len bytes.
340a5602e1bSKeith M Wesolowski  *
341a5602e1bSKeith M Wesolowski  * We also expand the special case tokens "$ISADIR" and "$ZFS-BOOTFS" here.
342a5602e1bSKeith M Wesolowski  *
343a5602e1bSKeith M Wesolowski  * If the string will not fit, returns ERR_WONT_FIT.
344a5602e1bSKeith M Wesolowski  * If a nonexistent variable is referenced, returns ERR_NOVAR.
345a5602e1bSKeith M Wesolowski  * Otherwise, returns 0.  The resulting string is nul-terminated.  On error,
346a5602e1bSKeith M Wesolowski  * the contents of the destination buffer are undefined.
347a5602e1bSKeith M Wesolowski  */
348a5602e1bSKeith M Wesolowski int
expand_string(const char * s,char * d,unsigned int len)349a5602e1bSKeith M Wesolowski expand_string(const char *s, char *d, unsigned int len)
350a5602e1bSKeith M Wesolowski {
351a5602e1bSKeith M Wesolowski 	unsigned int i;
352a5602e1bSKeith M Wesolowski 	int vlen;
353a5602e1bSKeith M Wesolowski 	const char *p;
354a5602e1bSKeith M Wesolowski 	char *q;
355a5602e1bSKeith M Wesolowski 	const char *start;
356a5602e1bSKeith M Wesolowski 	char name[EV_NAMELEN];
357a5602e1bSKeith M Wesolowski 	const char *val;
358a5602e1bSKeith M Wesolowski 
359a5602e1bSKeith M Wesolowski 	for (p = s, q = d; *p != '\0' && q < d + len; ) {
360a5602e1bSKeith M Wesolowski 		/* Special case: $ISADIR */
361a5602e1bSKeith M Wesolowski 		if (grub_strncmp(p, "$ISADIR", 7) == 0) {
362a5602e1bSKeith M Wesolowski 			if (isamd64() && check_min_mem64()) {
363a5602e1bSKeith M Wesolowski 				if (q + 5 >= d + len)
364a5602e1bSKeith M Wesolowski 					return (ERR_WONT_FIT);
365a5602e1bSKeith M Wesolowski 				(void) grub_memcpy(q, "amd64", 5);
366a5602e1bSKeith M Wesolowski 				q += 5;	/* amd64 */
367a5602e1bSKeith M Wesolowski 			}
368a5602e1bSKeith M Wesolowski 			p += 7;	/* $ISADIR */
369a5602e1bSKeith M Wesolowski 			continue;
370a5602e1bSKeith M Wesolowski 		}
371a5602e1bSKeith M Wesolowski 		/* Special case: $ZFS-BOOTFS */
372a5602e1bSKeith M Wesolowski 		if (grub_strncmp(p, "$ZFS-BOOTFS", 11) == 0 &&
373a5602e1bSKeith M Wesolowski 		    is_zfs_mount != 0) {
374a5602e1bSKeith M Wesolowski 			if (current_bootpath[0] == '\0' &&
375a5602e1bSKeith M Wesolowski 			    current_devid[0] == '\0') {
376a5602e1bSKeith M Wesolowski 				return (ERR_NO_BOOTPATH);
377a5602e1bSKeith M Wesolowski 			}
378a5602e1bSKeith M Wesolowski 
379a5602e1bSKeith M Wesolowski 			/* zfs-bootfs=%s/%u */
380a5602e1bSKeith M Wesolowski 			vlen = (current_bootfs_obj > 0) ? 10 : 0;
381a5602e1bSKeith M Wesolowski 			vlen += 11;
382a5602e1bSKeith M Wesolowski 			vlen += strlen(current_rootpool);
383a5602e1bSKeith M Wesolowski 
384a5602e1bSKeith M Wesolowski 			/* ,bootpath=\"%s\" */
385a5602e1bSKeith M Wesolowski 			if (current_bootpath[0] != '\0')
386a5602e1bSKeith M Wesolowski 				vlen += 12 + strlen(current_bootpath);
387a5602e1bSKeith M Wesolowski 
388a5602e1bSKeith M Wesolowski 			/* ,diskdevid=\"%s\" */
389a5602e1bSKeith M Wesolowski 			if (current_devid[0] != '\0')
390a5602e1bSKeith M Wesolowski 				vlen += 13 + strlen(current_devid);
391a5602e1bSKeith M Wesolowski 
392*1a065e93SAndrew Stormont 			if (current_bootguid != 0) {
393*1a065e93SAndrew Stormont 				vlen += grub_sprintf(NULL,
394*1a065e93SAndrew Stormont 				    ",zfs-bootpool=\"%llu\"", current_bootguid);
395*1a065e93SAndrew Stormont 			}
396*1a065e93SAndrew Stormont 			if (current_bootvdev != 0) {
397*1a065e93SAndrew Stormont 				vlen += grub_sprintf(NULL,
398*1a065e93SAndrew Stormont 				    ",zfs-bootvdev=\"%llu\"", current_bootvdev);
399*1a065e93SAndrew Stormont 			}
400*1a065e93SAndrew Stormont 
401a5602e1bSKeith M Wesolowski 			if (q + vlen >= d + len)
402a5602e1bSKeith M Wesolowski 				return (ERR_WONT_FIT);
403a5602e1bSKeith M Wesolowski 
404a5602e1bSKeith M Wesolowski 			if (current_bootfs_obj > 0) {
405a5602e1bSKeith M Wesolowski 				q += grub_sprintf(q, "zfs-bootfs=%s/%u",
406a5602e1bSKeith M Wesolowski 				    current_rootpool, current_bootfs_obj);
407a5602e1bSKeith M Wesolowski 			} else {
408a5602e1bSKeith M Wesolowski 				q += grub_sprintf(q, "zfs-bootfs=%s",
409a5602e1bSKeith M Wesolowski 				    current_rootpool);
410a5602e1bSKeith M Wesolowski 			}
411a5602e1bSKeith M Wesolowski 			if (current_bootpath[0] != '\0') {
412a5602e1bSKeith M Wesolowski 				q += grub_sprintf(q, ",bootpath=\"%s\"",
413a5602e1bSKeith M Wesolowski 				    current_bootpath);
414a5602e1bSKeith M Wesolowski 			}
415a5602e1bSKeith M Wesolowski 			if (current_devid[0] != '\0') {
416a5602e1bSKeith M Wesolowski 				q += grub_sprintf(q, ",diskdevid=\"%s\"",
417a5602e1bSKeith M Wesolowski 				    current_devid);
418a5602e1bSKeith M Wesolowski 			}
419*1a065e93SAndrew Stormont 			if (current_bootguid != 0) {
420*1a065e93SAndrew Stormont 				q += grub_sprintf(q, ",zfs-bootpool=\"%llu\"",
421*1a065e93SAndrew Stormont 				    current_bootguid);
422*1a065e93SAndrew Stormont 			}
423*1a065e93SAndrew Stormont 			if (current_bootvdev != 0) {
424*1a065e93SAndrew Stormont 				q += grub_sprintf(q, ",zfs-bootvdev=\"%llu\"",
425*1a065e93SAndrew Stormont 				    current_bootvdev);
426*1a065e93SAndrew Stormont 			}
427a5602e1bSKeith M Wesolowski 
428a5602e1bSKeith M Wesolowski 			p += 11;	/* $ZFS-BOOTFS */
429a5602e1bSKeith M Wesolowski 			continue;
430a5602e1bSKeith M Wesolowski 		}
431a5602e1bSKeith M Wesolowski 		if (*p == '$' && *(p + 1) == '{') {
432a5602e1bSKeith M Wesolowski 			start = p + 2;
433a5602e1bSKeith M Wesolowski 			for (p = start; *p != '\0' && *p != '}' &&
434a5602e1bSKeith M Wesolowski 			    p - start < sizeof (name) - 1; p++) {
435a5602e1bSKeith M Wesolowski 				name[p - start] = *p;
436a5602e1bSKeith M Wesolowski 			}
437a5602e1bSKeith M Wesolowski 			/*
438a5602e1bSKeith M Wesolowski 			 * Unterminated reference.  Copy verbatim.
439a5602e1bSKeith M Wesolowski 			 */
440a5602e1bSKeith M Wesolowski 			if (p - start >= sizeof (name) - 1 || *p != '}') {
441a5602e1bSKeith M Wesolowski 				p = start;
442a5602e1bSKeith M Wesolowski 				*q++ = '$';
443a5602e1bSKeith M Wesolowski 				*q++ = '{';
444a5602e1bSKeith M Wesolowski 				continue;
445a5602e1bSKeith M Wesolowski 			}
446a5602e1bSKeith M Wesolowski 
447a5602e1bSKeith M Wesolowski 			name[p - start] = '\0';
448a5602e1bSKeith M Wesolowski 			val = get_variable(name);
449a5602e1bSKeith M Wesolowski 			if (val == NULL)
450a5602e1bSKeith M Wesolowski 				return (ERR_NOVAR);
451a5602e1bSKeith M Wesolowski 
452a5602e1bSKeith M Wesolowski 			if ((vlen = grub_strlen(val)) >= q + len - d)
453a5602e1bSKeith M Wesolowski 				return (ERR_WONT_FIT);
454a5602e1bSKeith M Wesolowski 
455a5602e1bSKeith M Wesolowski 			(void) grub_memcpy(q, val, vlen);
456a5602e1bSKeith M Wesolowski 			q += vlen;
457a5602e1bSKeith M Wesolowski 			p++;
458a5602e1bSKeith M Wesolowski 		} else {
459a5602e1bSKeith M Wesolowski 			*q++ = *p++;
460a5602e1bSKeith M Wesolowski 		}
461a5602e1bSKeith M Wesolowski 	}
462a5602e1bSKeith M Wesolowski 
463a5602e1bSKeith M Wesolowski 	if (q >= d + len)
464a5602e1bSKeith M Wesolowski 		return (ERR_WONT_FIT);
465a5602e1bSKeith M Wesolowski 
466a5602e1bSKeith M Wesolowski 	*q = '\0';
467a5602e1bSKeith M Wesolowski 
468a5602e1bSKeith M Wesolowski 	return (0);
469a5602e1bSKeith M Wesolowski }
470a5602e1bSKeith M Wesolowski 
471a5602e1bSKeith M Wesolowski void
dump_variables(void)472a5602e1bSKeith M Wesolowski dump_variables(void)
473a5602e1bSKeith M Wesolowski {
474a5602e1bSKeith M Wesolowski 	unsigned int i;
475a5602e1bSKeith M Wesolowski 
476a5602e1bSKeith M Wesolowski 	for (i = 0; i < nexpvars; i++) {
477a5602e1bSKeith M Wesolowski 		if (!(expvars[i].v_flags & EVF_DEFINED))
478a5602e1bSKeith M Wesolowski 			continue;
479a5602e1bSKeith M Wesolowski 		(void) grub_printf("[%u] '%s' => '%s'\n", i, expvars[i].v_name,
480a5602e1bSKeith M Wesolowski 		    (expvars[i].v_flags & EVF_VALUESET) ?
481a5602e1bSKeith M Wesolowski 		    expvars[i].v_value : "");
482a5602e1bSKeith M Wesolowski 	}
483a5602e1bSKeith M Wesolowski }
484