2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
4 * Copyright (c) 2013 Joyent, Inc. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #ifdef SUPPORT_NETBOOT
30 #define EVF_DEFINED 0x01
31 #define EVF_VALUESET 0x02
33 typedef struct variable
{
34 char v_name
[EV_NAMELEN
];
36 char v_value
[220]; /* 256 - EV_NAMELEN - sizeof (fields) */
39 static variable_t expvars
[32];
40 static const unsigned int nexpvars
= 32;
43 set_variable(const char *name
, const char *value
)
46 unsigned int avail
= nexpvars
;
48 if (strlen(name
) >= sizeof (expvars
[0].v_name
))
49 return (ERR_WONT_FIT
);
51 if (value
!= NULL
&& strlen(value
) >= sizeof (expvars
[0].v_value
))
52 return (ERR_WONT_FIT
);
54 for (i
= 0; i
< nexpvars
; i
++) {
55 if (expvars
[i
].v_flags
& EVF_DEFINED
) {
56 if (grub_strcmp(expvars
[i
].v_name
, name
) == 0)
58 } else if (i
< avail
) {
64 if (avail
== nexpvars
)
65 return (ERR_WONT_FIT
);
68 (void) grub_strcpy(expvars
[i
].v_name
, name
);
69 expvars
[i
].v_flags
= EVF_DEFINED
;
73 (void) grub_strcpy(expvars
[i
].v_value
, value
);
74 expvars
[i
].v_flags
|= EVF_VALUESET
;
76 expvars
[i
].v_flags
&= ~EVF_VALUESET
;
83 get_variable(const char *name
)
87 for (i
= 0; i
< nexpvars
; i
++) {
88 if (!(expvars
[i
].v_flags
& EVF_DEFINED
))
90 if (grub_strcmp(expvars
[i
].v_name
, name
) == 0) {
91 if (expvars
[i
].v_flags
& EVF_VALUESET
)
92 return (expvars
[i
].v_value
);
101 detect_target_operating_mode(void)
106 * This function returns 16 bits. The upper 8 are the value of %ah
107 * after calling int 15/ec00. The lower 8 bits are zero if the BIOS
108 * call left CF clear, nonzero otherwise.
110 ret
= get_target_operating_mode();
114 if (ah
== 0x86 && ret
!= 0) {
115 grub_printf("[BIOS 'Detect Target Operating Mode' "
116 "callback unsupported on this platform]\n");
117 return (1); /* unsupported, ignore */
120 if (ah
== 0 && ret
== 0) {
121 grub_printf("[BIOS accepted mixed-mode target setting!]\n");
122 return (1); /* told the bios what we're up to */
125 if (ah
== 0 && ret
!= 0) {
126 grub_printf("fatal: BIOS reports this machine CANNOT run in "
127 "mixed 32/64-bit mode!\n");
131 grub_printf("warning: BIOS Detect Target Operating Mode callback "
132 "confused.\n %%ax >> 8 = 0x%x, carry = %d\n", ah
, ret
);
138 amd64_config_cpu(void)
140 struct amd64_cpuid_regs __vcr
, *vcr
= &__vcr
;
142 uint32_t max_maxeax
= 0x100;
145 uint32_t stdfeatures
= 0, xtdfeatures
= 0;
149 * This check may seem silly, but if the C preprocesor symbol __amd64
150 * is #defined during compilation, something that may outwardly seem
151 * like a good idea, uts/common/sys/isa_defs.h will #define _LP64,
152 * which will cause uts/common/sys/int_types.h to typedef uint64_t as
153 * an unsigned long - which is only 4 bytes in size when using a 32-bit
156 * If that happens, all the page table translation routines will fail
157 * horribly, so check the size of uint64_t just to insure some degree
158 * of sanity in future operations.
160 /*LINTED [sizeof result is invarient]*/
161 if (sizeof (uint64_t) != 8)
162 prom_panic("grub compiled improperly, unable to boot "
163 "64-bit AMD64 executables");
166 * If the CPU doesn't support the CPUID instruction, it's definitely
169 if (amd64_cpuid_supported() == 0)
172 amd64_cpuid_insn(0, vcr
);
176 /*LINTED [vendor string from cpuid data]*/
177 uint32_t *iptr
= (uint32_t *)vendor
;
179 *iptr
++ = vcr
->r_ebx
;
180 *iptr
++ = vcr
->r_edx
;
181 *iptr
++ = vcr
->r_ecx
;
186 if (maxeax
> max_maxeax
) {
187 grub_printf("cpu: warning, maxeax was 0x%x -> 0x%x\n",
193 return (0); /* no additional functions, not an AMD64 */
195 uint_t family
, model
, step
;
197 amd64_cpuid_insn(1, vcr
);
200 * All AMD64/IA32e processors technically SHOULD report
201 * themselves as being in family 0xf, but for some reason
202 * Simics doesn't, and this may change in the future, so
203 * don't error out if it's not true.
205 if ((family
= BITX(vcr
->r_eax
, 11, 8)) == 0xf)
206 family
+= BITX(vcr
->r_eax
, 27, 20);
208 if ((model
= BITX(vcr
->r_eax
, 7, 4)) == 0xf)
209 model
+= BITX(vcr
->r_eax
, 19, 16) << 4;
210 step
= BITX(vcr
->r_eax
, 3, 0);
212 grub_printf("cpu: '%s' family %d model %d step %d\n",
213 vendor
, family
, model
, step
);
214 stdfeatures
= vcr
->r_edx
;
217 amd64_cpuid_insn(0x80000000, vcr
);
219 if (vcr
->r_eax
& 0x80000000) {
220 uint32_t xmaxeax
= vcr
->r_eax
;
221 const uint32_t max_xmaxeax
= 0x80000100;
223 if (xmaxeax
> max_xmaxeax
) {
224 grub_printf("amd64: warning, xmaxeax was "
225 "0x%x -> 0x%x\n", xmaxeax
, max_xmaxeax
);
226 xmaxeax
= max_xmaxeax
;
229 if (xmaxeax
>= 0x80000001) {
230 amd64_cpuid_insn(0x80000001, vcr
);
231 xtdfeatures
= vcr
->r_edx
;
235 if (BITX(xtdfeatures
, 29, 29)) /* long mode */
238 grub_printf("amd64: CPU does NOT support long mode\n");
240 if (!BITX(stdfeatures
, 0, 0)) {
241 grub_printf("amd64: CPU does NOT support FPU\n");
245 if (!BITX(stdfeatures
, 4, 4)) {
246 grub_printf("amd64: CPU does NOT support TSC\n");
250 if (!BITX(stdfeatures
, 5, 5)) {
251 grub_printf("amd64: CPU does NOT support MSRs\n");
255 if (!BITX(stdfeatures
, 6, 6)) {
256 grub_printf("amd64: CPU does NOT support PAE\n");
260 if (!BITX(stdfeatures
, 8, 8)) {
261 grub_printf("amd64: CPU does NOT support CX8\n");
265 if (!BITX(stdfeatures
, 13, 13)) {
266 grub_printf("amd64: CPU does NOT support PGE\n");
270 if (!BITX(stdfeatures
, 19, 19)) {
271 grub_printf("amd64: CPU does NOT support CLFSH\n");
275 if (!BITX(stdfeatures
, 23, 23)) {
276 grub_printf("amd64: CPU does NOT support MMX\n");
280 if (!BITX(stdfeatures
, 24, 24)) {
281 grub_printf("amd64: CPU does NOT support FXSR\n");
285 if (!BITX(stdfeatures
, 25, 25)) {
286 grub_printf("amd64: CPU does NOT support SSE\n");
290 if (!BITX(stdfeatures
, 26, 26)) {
291 grub_printf("amd64: CPU does NOT support SSE2\n");
296 grub_printf("amd64: CPU does not support amd64 executables.\n");
300 amd64_rdmsr(MSR_AMD_EFER
, &efer
);
301 if (efer
& AMD_EFER_SCE
)
302 grub_printf("amd64: EFER_SCE (syscall/sysret) already "
304 if (efer
& AMD_EFER_NXE
)
305 grub_printf("amd64: EFER_NXE (no-exec prot) already enabled\n");
306 if (efer
& AMD_EFER_LME
)
307 grub_printf("amd64: EFER_LME (long mode) already enabled\n");
309 return (detect_target_operating_mode());
318 ret
= amd64_config_cpu();
324 check_min_mem64(void)
329 if ((mbi
.mem_upper
/ 10240) * 11 >= min_mem64
)
336 * Given the nul-terminated input string s, expand all variable references
337 * within that string into the buffer pointed to by d, which must be of length
338 * not less than len bytes.
340 * We also expand the special case tokens "$ISADIR" and "$ZFS-BOOTFS" here.
342 * If the string will not fit, returns ERR_WONT_FIT.
343 * If a nonexistent variable is referenced, returns ERR_NOVAR.
344 * Otherwise, returns 0. The resulting string is nul-terminated. On error,
345 * the contents of the destination buffer are undefined.
348 expand_string(const char *s
, char *d
, unsigned int len
)
355 char name
[EV_NAMELEN
];
358 for (p
= s
, q
= d
; *p
!= '\0' && q
< d
+ len
; ) {
359 /* Special case: $ISADIR */
360 if (grub_strncmp(p
, "$ISADIR", 7) == 0) {
361 if (isamd64() && check_min_mem64()) {
362 if (q
+ 5 >= d
+ len
)
363 return (ERR_WONT_FIT
);
364 (void) grub_memcpy(q
, "amd64", 5);
367 p
+= 7; /* $ISADIR */
370 /* Special case: $ZFS-BOOTFS */
371 if (grub_strncmp(p
, "$ZFS-BOOTFS", 11) == 0 &&
373 if (current_bootpath
[0] == '\0' &&
374 current_devid
[0] == '\0') {
375 return (ERR_NO_BOOTPATH
);
378 /* zfs-bootfs=%s/%u */
379 vlen
= (current_bootfs_obj
> 0) ? 10 : 0;
381 vlen
+= strlen(current_rootpool
);
383 /* ,bootpath=\"%s\" */
384 if (current_bootpath
[0] != '\0')
385 vlen
+= 12 + strlen(current_bootpath
);
387 /* ,diskdevid=\"%s\" */
388 if (current_devid
[0] != '\0')
389 vlen
+= 13 + strlen(current_devid
);
391 if (q
+ vlen
>= d
+ len
)
392 return (ERR_WONT_FIT
);
394 if (current_bootfs_obj
> 0) {
395 q
+= grub_sprintf(q
, "zfs-bootfs=%s/%u",
396 current_rootpool
, current_bootfs_obj
);
398 q
+= grub_sprintf(q
, "zfs-bootfs=%s",
401 if (current_bootpath
[0] != '\0') {
402 q
+= grub_sprintf(q
, ",bootpath=\"%s\"",
405 if (current_devid
[0] != '\0') {
406 q
+= grub_sprintf(q
, ",diskdevid=\"%s\"",
410 p
+= 11; /* $ZFS-BOOTFS */
413 if (*p
== '$' && *(p
+ 1) == '{') {
415 for (p
= start
; *p
!= '\0' && *p
!= '}' &&
416 p
- start
< sizeof (name
) - 1; p
++) {
417 name
[p
- start
] = *p
;
420 * Unterminated reference. Copy verbatim.
422 if (p
- start
>= sizeof (name
) - 1 || *p
!= '}') {
429 name
[p
- start
] = '\0';
430 val
= get_variable(name
);
434 if ((vlen
= grub_strlen(val
)) >= q
+ len
- d
)
435 return (ERR_WONT_FIT
);
437 (void) grub_memcpy(q
, val
, vlen
);
446 return (ERR_WONT_FIT
);
458 for (i
= 0; i
< nexpvars
; i
++) {
459 if (!(expvars
[i
].v_flags
& EVF_DEFINED
))
461 (void) grub_printf("[%u] '%s' => '%s'\n", i
, expvars
[i
].v_name
,
462 (expvars
[i
].v_flags
& EVF_VALUESET
) ?
463 expvars
[i
].v_value
: "");