one patch, on grub-2.04

This commit is contained in:
Vincent Batts 2020-10-29 15:48:21 -04:00
parent 2a2e10c1b3
commit 1b24dcf433
61 changed files with 4887 additions and 97 deletions

1
.gitignore vendored
View file

@ -45,6 +45,7 @@ gensymlist.sh
gentrigtables
gentrigtables.exe
gettext_strings_test
gpt_unit_test
/gnulib
grub-bin2h
/grub-bios-setup

View file

@ -1175,6 +1175,18 @@ script = {
common = tests/grub_cmd_tr.in;
};
script = {
testcase;
name = gptrepair_test;
common = tests/gptrepair_test.in;
};
script = {
testcase;
name = gptprio_test;
common = tests/gptprio_test.in;
};
script = {
testcase;
name = file_filter_test;
@ -1270,6 +1282,25 @@ program = {
ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
program = {
testcase;
name = gpt_unit_test;
common = tests/gpt_unit_test.c;
common = tests/lib/unit_test.c;
common = grub-core/commands/search_part_label.c;
common = grub-core/commands/search_part_uuid.c;
common = grub-core/commands/search_disk_uuid.c;
common = grub-core/disk/host.c;
common = grub-core/kern/emu/hostfs.c;
common = grub-core/lib/gpt.c;
common = grub-core/tests/lib/test.c;
ldadd = libgrubmods.a;
ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
ldadd = grub-core/gnulib/libgnu.a;
ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
program = {
name = grub-menulst2cfg;
mansection = 1;

View file

@ -3944,6 +3944,7 @@ you forget a command, you can run the command @command{help}
* sha256sum:: Compute or check SHA256 hash
* sha512sum:: Compute or check SHA512 hash
* sleep:: Wait for a specified number of seconds
* smbios:: Retrieve SMBIOS information
* source:: Read a configuration file in same context
* test:: Check file types and compare values
* true:: Do nothing, successfully
@ -5083,6 +5084,74 @@ if timeout was interrupted by @key{ESC}.
@end deffn
@node smbios
@subsection smbios
@deffn Command smbios @
[@option{--type} @var{type}] @
[@option{--handle} @var{handle}] @
[@option{--match} @var{match}] @
(@option{--get-byte} | @option{--get-word} | @option{--get-dword} | @
@option{--get-qword} | @option{--get-string} | @option{--get-uuid}) @
@var{offset} @
[@option{--set} @var{variable}]
Retrieve SMBIOS information.
The @command{smbios} command returns the value of a field in an SMBIOS
structure. The following options determine which structure to select.
@itemize @bullet
@item
Specifying @option{--type} will select structures with a matching
@var{type}. The type can be any integer from 0 to 255.
@item
Specifying @option{--handle} will select structures with a matching
@var{handle}. The handle can be any integer from 0 to 65535.
@item
Specifying @option{--match} will select structure number @var{match} in the
filtered list of structures; e.g. @code{smbios --type 4 --match 2} will select
the second Process Information (Type 4) structure. The list is always ordered
the same as the hardware's SMBIOS table. The match number must be a positive
integer. If unspecified, the first matching structure will be selected.
@end itemize
The remaining options determine which field in the selected SMBIOS structure to
return. Only one of these options may be specified at a time.
@itemize @bullet
@item
When given @option{--get-byte}, return the value of the byte
at @var{offset} bytes into the selected SMBIOS structure.
@item
When given @option{--get-word}, return the value of the word (two bytes)
at @var{offset} bytes into the selected SMBIOS structure.
@item
When given @option{--get-dword}, return the value of the dword (four bytes)
at @var{offset} bytes into the selected SMBIOS structure.
@item
When given @option{--get-qword}, return the value of the qword (eight bytes)
at @var{offset} bytes into the selected SMBIOS structure.
@item
When given @option{--get-string}, return the string with its index found
at @var{offset} bytes into the selected SMBIOS structure.
@item
When given @option{--get-uuid}, return the value of the UUID (sixteen bytes)
at @var{offset} bytes into the selected SMBIOS structure.
@end itemize
The default action is to print the value of the requested field to the console,
but a variable name can be specified with @option{--set} to store the value
instead of printing it.
For example, this will store and then display the system manufacturer's name.
@example
smbios --type 1 --get-string 4 --set system_manufacturer
echo $system_manufacturer
@end example
@end deffn
@node source
@subsection source

View file

@ -92,6 +92,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/term.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/time.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm_private.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/net.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/tpm.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/memory.h
if COND_i386_pc

View file

@ -140,6 +140,7 @@ kernel = {
common = kern/rescue_parser.c;
common = kern/rescue_reader.c;
common = kern/term.c;
common = kern/tpm.c;
noemu = kern/compiler-rt.c;
noemu = kern/mm.c;
@ -203,6 +204,7 @@ kernel = {
efi = term/efi/console.c;
efi = kern/acpi.c;
efi = kern/efi/acpi.c;
efi = kern/efi/tpm.c;
i386_coreboot = kern/i386/pc/acpi.c;
i386_multiboot = kern/i386/pc/acpi.c;
i386_coreboot = kern/acpi.c;
@ -262,6 +264,7 @@ kernel = {
i386_pc = kern/i386/pc/init.c;
i386_pc = kern/i386/pc/mmap.c;
i386_pc = kern/i386/pc/tpm.c;
i386_pc = term/i386/pc/console.c;
i386_qemu = bus/pci.c;
@ -888,11 +891,32 @@ module = {
enable = x86_64_efi;
};
module = {
name = getenv;
common = commands/efi/getenv.c;
enable = efi;
};
module = {
name = gptsync;
common = commands/gptsync.c;
};
module = {
name = gptrepair;
common = commands/gptrepair.c;
};
module = {
name = gptprio;
common = commands/gptprio.c;
};
module = {
name = gpt;
common = lib/gpt.c;
};
module = {
name = halt;
nopc = commands/halt.c;
@ -1080,6 +1104,21 @@ module = {
common = commands/search_label.c;
};
module = {
name = search_part_uuid;
common = commands/search_part_uuid.c;
};
module = {
name = search_part_label;
common = commands/search_part_label.c;
};
module = {
name = search_disk_uuid;
common = commands/search_disk_uuid.c;
};
module = {
name = setpci;
common = commands/setpci.c;
@ -1097,6 +1136,21 @@ module = {
common = commands/sleep.c;
};
module = {
name = smbios;
common = commands/smbios.c;
efi = commands/efi/smbios.c;
i386_pc = commands/i386/pc/smbios.c;
i386_coreboot = commands/i386/pc/smbios.c;
i386_multiboot = commands/i386/pc/smbios.c;
enable = efi;
enable = i386_pc;
enable = i386_coreboot;
enable = i386_multiboot;
};
module = {
name = suspend;
ieee1275 = commands/ieee1275/suspend.c;
@ -1849,6 +1903,14 @@ module = {
enable = x86_64_efi;
};
module = {
name = linuxefi;
efi = loader/i386/efi/linux.c;
efi = lib/cmdline.c;
enable = i386_efi;
enable = x86_64_efi;
};
module = {
name = chain;
efi = loader/efi/chainloader.c;
@ -2503,3 +2565,8 @@ module = {
common = commands/i386/wrmsr.c;
enable = x86;
};
module = {
name = fwconfig;
common = commands/fwconfig.c;
enable = x86;
};

View file

@ -24,11 +24,14 @@
* defines for the code go here
*/
#define TPM 1
/* Print message string */
#define MSG(x) movw $x, %si; call LOCAL(message)
#define ERR(x) movw $x, %si; jmp LOCAL(error_message)
.macro floppy
#ifndef TPM
part_start:
LOCAL(probe_values):
@ -85,6 +88,7 @@ fd_probe_error_string: .asciz "Floppy"
movb MACRO_DOLLAR(79), %ch
jmp LOCAL(final_init)
#endif
.endm
.macro scratch
@ -255,6 +259,7 @@ real_start:
/* set %si to the disk address packet */
movw $disk_address_packet, %si
#ifndef TPM
/* check if LBA is supported */
movb $0x41, %ah
movw $0x55aa, %bx
@ -274,6 +279,7 @@ real_start:
andw $1, %cx
jz LOCAL(chs_mode)
#endif
LOCAL(lba_mode):
xorw %ax, %ax
@ -317,6 +323,9 @@ LOCAL(lba_mode):
jmp LOCAL(copy_buffer)
LOCAL(chs_mode):
#ifdef TPM
jmp LOCAL(general_error)
#else
/*
* Determine the hard disk geometry from the BIOS!
* We do this first, so that LS-120 IDE floppies work correctly.
@ -428,7 +437,7 @@ setup_sectors:
jc LOCAL(read_error)
movw %es, %bx
#endif /* TPM */
LOCAL(copy_buffer):
/*
* We need to save %cx and %si because the startup code in
@ -451,6 +460,25 @@ LOCAL(copy_buffer):
popw %ds
popa
#ifdef TPM
pusha
movw $0xBB00, %ax /* TCG_StatusCheck */
int $0x1A
test %eax, %eax
jnz boot /* No TPM or TPM deactivated */
movw $0xBB07, %ax /* TCG_CompactHashLogExtendEvent */
movw $GRUB_BOOT_MACHINE_KERNEL_ADDR, %di
xorl %esi, %esi
movl $0x41504354, %ebx /* TCPA */
movl $0x200, %ecx /* Measure 512 bytes */
movl $0x8, %edx /* PCR 8 */
int $0x1A
boot:
popa
#endif
/* boot kernel */
jmp *(LOCAL(kernel_address))

View file

@ -19,6 +19,8 @@
#include <grub/symbol.h>
#include <grub/machine/boot.h>
#define TPM 1
/*
* defines for the code go here
*/
@ -58,6 +60,21 @@ _start:
/* this sets up for the first run through "bootloop" */
movw $LOCAL(firstlist), %di
#ifdef TPM
/* clear EAX to remove potential garbage */
xorl %eax, %eax
/* 8(%di) = number of sectors to read */
movw 8(%di), %ax
/* Multiply number of sectors to read with 512 bytes. EAX is 32bit
* which is large enough to hold values of up to 4GB. I doubt there
* will ever be a core.img larger than that. ;-) */
shll $9, %eax
/* write result to bytes_to_measure var */
movl %eax, bytes_to_measure
#endif
/* save the sector number of the second sector in %ebp */
movl (%di), %ebp
@ -295,6 +312,29 @@ LOCAL(copy_buffer):
/* END OF MAIN LOOP */
LOCAL(bootit):
#ifdef TPM
pusha
movw $0xBB07, %ax /* TCG_CompactHashLogExtendEvent */
movw $0x0, %bx
movw %bx, %es
/* We've already measured the first 512 bytes, now measure the rest */
xorl %edi, %edi
movw $(GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200), %di
movl $0x41504354, %ebx /* EBX = "TCPA" */
/* %ecx = The length, in bytes, of the buffer to measure */
movl $bytes_to_measure, %esi
movl (%esi), %ecx
xorl %esi, %esi
movl $0x9, %edx /* PCR 9 */
int $0x1A
popa
#endif
/* print a newline */
MSG(notification_done)
popw %dx /* this makes sure %dl is our "boot" drive */
@ -329,6 +369,10 @@ geometry_error_string: .asciz "Geom"
read_error_string: .asciz "Read"
general_error_string: .asciz " Error"
#ifdef TPM
bytes_to_measure: .long 0
#endif
/*
* message: write the string pointed to by %si
*

View file

@ -0,0 +1,153 @@
/* getenv.c - retrieve EFI variables. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
* Copyright (C) 2014 CoreOS, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/efi/efi.h>
#include <grub/dl.h>
#include <grub/env.h>
#include <grub/err.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
#include <grub/misc.h>
#include <grub/mm.h>
GRUB_MOD_LICENSE ("GPLv3+");
static const struct grub_arg_option options_getenv[] = {
{"var-name", 'e', 0,
N_("Environment variable to query"),
N_("VARNAME"), ARG_TYPE_STRING},
{"var-guid", 'g', 0,
N_("GUID of environment variable to query"),
N_("GUID"), ARG_TYPE_STRING},
{"binary", 'b', 0,
N_("Read binary data and represent it as hex"),
0, ARG_TYPE_NONE},
{0, 0, 0, 0, 0, 0}
};
enum options_getenv
{
GETENV_VAR_NAME,
GETENV_VAR_GUID,
GETENV_BINARY,
};
static grub_err_t
grub_cmd_getenv (grub_extcmd_context_t ctxt, int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
char *envvar = NULL, *guid = NULL, *bindata = NULL, *data = NULL;
grub_size_t datasize;
grub_efi_guid_t efi_var_guid;
grub_efi_boolean_t binary = state[GETENV_BINARY].set;
unsigned int i;
if (!state[GETENV_VAR_NAME].set || !state[GETENV_VAR_GUID].set)
{
grub_error (GRUB_ERR_INVALID_COMMAND, N_("-e and -g are required"));
goto done;
}
if (argc != 1)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected arguments"));
goto done;
}
envvar = state[GETENV_VAR_NAME].arg;
guid = state[GETENV_VAR_GUID].arg;
if (grub_strlen(guid) != 36 ||
guid[8] != '-' ||
guid[13] != '-' ||
guid[18] != '-' ||
guid[23] != '-')
{
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid GUID"));
goto done;
}
/* Forgive me father for I have sinned */
guid[8] = 0;
efi_var_guid.data1 = grub_strtoul(guid, NULL, 16);
guid[13] = 0;
efi_var_guid.data2 = grub_strtoul(guid + 9, NULL, 16);
guid[18] = 0;
efi_var_guid.data3 = grub_strtoul(guid + 14, NULL, 16);
efi_var_guid.data4[7] = grub_strtoul(guid + 34, NULL, 16);
guid[34] = 0;
efi_var_guid.data4[6] = grub_strtoul(guid + 32, NULL, 16);
guid[32] = 0;
efi_var_guid.data4[5] = grub_strtoul(guid + 30, NULL, 16);
guid[30] = 0;
efi_var_guid.data4[4] = grub_strtoul(guid + 28, NULL, 16);
guid[28] = 0;
efi_var_guid.data4[3] = grub_strtoul(guid + 26, NULL, 16);
guid[26] = 0;
efi_var_guid.data4[2] = grub_strtoul(guid + 24, NULL, 16);
guid[23] = 0;
efi_var_guid.data4[1] = grub_strtoul(guid + 21, NULL, 16);
guid[21] = 0;
efi_var_guid.data4[0] = grub_strtoul(guid + 19, NULL, 16);
data = grub_efi_get_variable(envvar, &efi_var_guid, &datasize);
if (!data || !datasize)
{
grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("No such variable"));
goto done;
}
if (binary)
{
bindata = grub_zalloc(datasize * 2 + 1);
for (i=0; i<datasize; i++)
grub_snprintf(bindata + i*2, 3, "%02x", data[i] & 0xff);
if (grub_env_set (args[0], bindata))
goto done;
}
else if (grub_env_set (args[0], data))
{
goto done;
}
grub_errno = GRUB_ERR_NONE;
done:
grub_free(bindata);
grub_free(data);
return grub_errno;
}
static grub_extcmd_t cmd_getenv;
GRUB_MOD_INIT(getenv)
{
cmd_getenv = grub_register_extcmd ("getenv", grub_cmd_getenv, 0,
N_("-e envvar -g guidenv setvar"),
N_("Read a firmware environment variable"),
options_getenv);
}
GRUB_MOD_FINI(getenv)
{
grub_unregister_extcmd (cmd_getenv);
}

View file

@ -30,6 +30,7 @@ GRUB_MOD_LICENSE ("GPLv3+");
static grub_efi_guid_t acpi_guid = GRUB_EFI_ACPI_TABLE_GUID;
static grub_efi_guid_t acpi2_guid = GRUB_EFI_ACPI_20_TABLE_GUID;
static grub_efi_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID;
static grub_efi_guid_t smbios3_guid = GRUB_EFI_SMBIOS3_TABLE_GUID;
#define EBDA_SEG_ADDR 0x40e
#define LOW_MEM_ADDR 0x413
@ -93,7 +94,7 @@ static void
fake_bios_data (int use_rom)
{
unsigned i;
void *acpi, *smbios;
void *acpi, *smbios, *smbios3;
grub_uint16_t *ebda_seg_ptr, *low_mem_ptr;
ebda_seg_ptr = (grub_uint16_t *) EBDA_SEG_ADDR;
@ -103,6 +104,7 @@ fake_bios_data (int use_rom)
acpi = 0;
smbios = 0;
smbios3 = 0;
for (i = 0; i < grub_efi_system_table->num_table_entries; i++)
{
grub_efi_packed_guid_t *guid =
@ -127,6 +129,11 @@ fake_bios_data (int use_rom)
smbios = grub_efi_system_table->configuration_table[i].vendor_table;
grub_dprintf ("efi", "SMBIOS: %p\n", smbios);
}
else if (! grub_memcmp (guid, &smbios3_guid, sizeof (grub_efi_guid_t)))
{
smbios3 = grub_efi_system_table->configuration_table[i].vendor_table;
grub_dprintf ("efi", "SMBIOS3: %p\n", smbios3);
}
}
*ebda_seg_ptr = FAKE_EBDA_SEG;
@ -137,8 +144,13 @@ fake_bios_data (int use_rom)
if (acpi)
grub_memcpy ((char *) ((FAKE_EBDA_SEG << 4) + 16), acpi, 1024 - 16);
if ((use_rom) && (smbios))
grub_memcpy ((char *) SBIOS_ADDR, (char *) smbios + 16, 16);
if (use_rom)
{
if (smbios)
grub_memcpy ((char *) SBIOS_ADDR, (char *) smbios, 31);
if (smbios3)
grub_memcpy ((char *) SBIOS_ADDR + 32, (char *) smbios3, 24);
}
}
static grub_err_t

View file

@ -48,6 +48,7 @@ static const struct guid_mapping guid_mappings[] =
{ GRUB_EFI_MPS_TABLE_GUID, "MPS"},
{ GRUB_EFI_SAL_TABLE_GUID, "SAL"},
{ GRUB_EFI_SMBIOS_TABLE_GUID, "SMBIOS"},
{ GRUB_EFI_SMBIOS3_TABLE_GUID, "SMBIOS3"},
{ GRUB_EFI_SYSTEM_RESOURCE_TABLE_GUID, "SYSTEM RESOURCE TABLE"},
{ GRUB_EFI_TIANO_CUSTOM_DECOMPRESS_GUID, "TIANO CUSTOM DECOMPRESS"},
{ GRUB_EFI_TSC_FREQUENCY_GUID, "TSC FREQUENCY"},

View file

@ -0,0 +1,59 @@
/* smbios.c - get smbios tables. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2015 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/smbios.h>
#include <grub/misc.h>
#include <grub/efi/efi.h>
#include <grub/efi/api.h>
struct grub_smbios_eps *
grub_machine_smbios_get_eps (void)
{
unsigned i;
static grub_efi_packed_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID;
for (i = 0; i < grub_efi_system_table->num_table_entries; i++)
{
grub_efi_packed_guid_t *guid =
&grub_efi_system_table->configuration_table[i].vendor_guid;
if (! grub_memcmp (guid, &smbios_guid, sizeof (grub_efi_packed_guid_t)))
return (struct grub_smbios_eps *)
grub_efi_system_table->configuration_table[i].vendor_table;
}
return 0;
}
struct grub_smbios_eps3 *
grub_machine_smbios_get_eps3 (void)
{
unsigned i;
static grub_efi_packed_guid_t smbios3_guid = GRUB_EFI_SMBIOS3_TABLE_GUID;
for (i = 0; i < grub_efi_system_table->num_table_entries; i++)
{
grub_efi_packed_guid_t *guid =
&grub_efi_system_table->configuration_table[i].vendor_guid;
if (! grub_memcmp (guid, &smbios3_guid, sizeof (grub_efi_packed_guid_t)))
return (struct grub_smbios_eps3 *)
grub_efi_system_table->configuration_table[i].vendor_table;
}
return 0;
}

View file

@ -0,0 +1,122 @@
/* fwconfig.c - command to read config from qemu fwconfig */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2015 CoreOS, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/extcmd.h>
#include <grub/env.h>
#include <grub/cpu/io.h>
#include <grub/i18n.h>
#include <grub/mm.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define SELECTOR 0x510
#define DATA 0x511
#define SIGNATURE_INDEX 0x00
#define DIRECTORY_INDEX 0x19
static grub_extcmd_t cmd_read_fwconfig;
struct grub_qemu_fwcfgfile {
grub_uint32_t size;
grub_uint16_t select;
grub_uint16_t reserved;
char name[56];
};
static const struct grub_arg_option options[] =
{
{0, 'v', 0, N_("Save read value into variable VARNAME."),
N_("VARNAME"), ARG_TYPE_STRING},
{0, 0, 0, 0, 0, 0}
};
static grub_err_t
grub_cmd_fwconfig (grub_extcmd_context_t ctxt __attribute__ ((unused)),
int argc, char **argv)
{
grub_uint32_t i, j, value = 0;
struct grub_qemu_fwcfgfile file;
char fwsig[4], signature[4] = { 'Q', 'E', 'M', 'U' };
if (argc != 2)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected"));
/* Verify that we have meaningful hardware here */
grub_outw(SIGNATURE_INDEX, SELECTOR);
for (i=0; i<sizeof(fwsig); i++)
fwsig[i] = grub_inb(DATA);
if (grub_memcmp(fwsig, signature, sizeof(signature)) != 0)
return grub_error (GRUB_ERR_BAD_DEVICE, N_("invalid fwconfig hardware signature: got 0x%x%x%x%x"), fwsig[0], fwsig[1], fwsig[2], fwsig[3]);
/* Find out how many file entries we have */
grub_outw(DIRECTORY_INDEX, SELECTOR);
value = grub_inb(DATA) | grub_inb(DATA) << 8 | grub_inb(DATA) << 16 | grub_inb(DATA) << 24;
value = grub_be_to_cpu32(value);
/* Read the file description for each file */
for (i=0; i<value; i++)
{
for (j=0; j<sizeof(file); j++)
{
((char *)&file)[j] = grub_inb(DATA);
}
/* Check whether it matches what we're looking for, and if so read the file */
if (grub_strncmp(file.name, argv[0], sizeof(file.name)) == 0)
{
grub_uint32_t filesize = grub_be_to_cpu32(file.size);
grub_uint16_t location = grub_be_to_cpu16(file.select);
char *data = grub_malloc(filesize+1);
if (!data)
return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate buffer for data"));
grub_outw(location, SELECTOR);
for (j=0; j<filesize; j++)
{
data[j] = grub_inb(DATA);
}
data[filesize] = '\0';
grub_env_set (argv[1], data);
grub_free(data);
return 0;
}
}
return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("couldn't find entry %s"), argv[0]);
}
GRUB_MOD_INIT(fwconfig)
{
cmd_read_fwconfig =
grub_register_extcmd ("fwconfig", grub_cmd_fwconfig, 0,
N_("PATH VAR"),
N_("Set VAR to the contents of fwconfig PATH"),
options);
}
GRUB_MOD_FINI(fwconfig)
{
grub_unregister_extcmd (cmd_read_fwconfig);
}

View file

@ -0,0 +1,223 @@
/* gptprio.c - manage priority based partition selection. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
* Copyright (C) 2014 CoreOS, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/device.h>
#include <grub/env.h>
#include <grub/err.h>
#include <grub/extcmd.h>
#include <grub/gpt_partition.h>
#include <grub/i18n.h>
#include <grub/misc.h>
GRUB_MOD_LICENSE ("GPLv3+");
static const struct grub_arg_option options_next[] = {
{"set-device", 'd', 0,
N_("Set a variable to the name of selected partition."),
N_("VARNAME"), ARG_TYPE_STRING},
{"set-uuid", 'u', 0,
N_("Set a variable to the GPT UUID of selected partition."),
N_("VARNAME"), ARG_TYPE_STRING},
{0, 0, 0, 0, 0, 0}
};
enum options_next
{
NEXT_SET_DEVICE,
NEXT_SET_UUID,
};
static unsigned int
grub_gptprio_priority (struct grub_gpt_partentry *entry)
{
return (unsigned int) grub_gpt_entry_attribute
(entry, GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_PRIORITY, 4);
}
static unsigned int
grub_gptprio_tries_left (struct grub_gpt_partentry *entry)
{
return (unsigned int) grub_gpt_entry_attribute
(entry, GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_TRIES_LEFT, 4);
}
static void
grub_gptprio_set_tries_left (struct grub_gpt_partentry *entry,
unsigned int tries_left)
{
grub_gpt_entry_set_attribute
(entry, tries_left, GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_TRIES_LEFT, 4);
}
static unsigned int
grub_gptprio_successful (struct grub_gpt_partentry *entry)
{
return (unsigned int) grub_gpt_entry_attribute
(entry, GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_SUCCESSFUL, 1);
}
static grub_err_t
grub_find_next (const char *disk_name,
const grub_gpt_part_guid_t *part_type,
char **part_name, char **part_guid)
{
struct grub_gpt_partentry *part, *part_found = NULL;
grub_device_t dev = NULL;
grub_gpt_t gpt = NULL;
grub_uint32_t i, part_index;
dev = grub_device_open (disk_name);
if (!dev)
goto done;
gpt = grub_gpt_read (dev->disk);
if (!gpt)
goto done;
if (grub_gpt_repair (dev->disk, gpt))
goto done;
for (i = 0; (part = grub_gpt_get_partentry (gpt, i)) != NULL; i++)
{
if (grub_memcmp (part_type, &part->type, sizeof (*part_type)) == 0)
{
unsigned int priority, tries_left, successful, old_priority = 0;
priority = grub_gptprio_priority (part);
tries_left = grub_gptprio_tries_left (part);
successful = grub_gptprio_successful (part);
if (part_found)
old_priority = grub_gptprio_priority (part_found);
if ((tries_left || successful) && priority > old_priority)
{
part_index = i;
part_found = part;
}
}
}
if (!part_found)
{
grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such partition"));
goto done;
}
if (grub_gptprio_tries_left (part_found))
{
unsigned int tries_left = grub_gptprio_tries_left (part_found);
grub_gptprio_set_tries_left (part_found, tries_left - 1);
if (grub_gpt_update (gpt))
goto done;
if (grub_gpt_write (dev->disk, gpt))
goto done;
}
*part_name = grub_xasprintf ("%s,gpt%u", disk_name, part_index + 1);
if (!*part_name)
goto done;
*part_guid = grub_gpt_guid_to_str (&part_found->guid);
if (!*part_guid)
goto done;
grub_errno = GRUB_ERR_NONE;
done:
grub_gpt_free (gpt);
if (dev)
grub_device_close (dev);
return grub_errno;
}
static grub_err_t
grub_cmd_next (grub_extcmd_context_t ctxt, int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
char *p, *root = NULL, *part_name = NULL, *part_guid = NULL;
/* TODO: Add a uuid parser and a command line flag for providing type. */
grub_gpt_part_guid_t part_type = GRUB_GPT_PARTITION_TYPE_USR_X86_64;
if (!state[NEXT_SET_DEVICE].set || !state[NEXT_SET_UUID].set)
{
grub_error (GRUB_ERR_INVALID_COMMAND, N_("-d and -u are required"));
goto done;
}
if (argc == 0)
root = grub_strdup (grub_env_get ("root"));
else if (argc == 1)
root = grub_strdup (args[0]);
else
{
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected arguments"));
goto done;
}
if (!root)
goto done;
/* To make using $root practical strip off the partition name. */
p = grub_strchr (root, ',');
if (p)
*p = '\0';
if (grub_find_next (root, &part_type, &part_name, &part_guid))
goto done;
if (grub_env_set (state[NEXT_SET_DEVICE].arg, part_name))
goto done;
if (grub_env_set (state[NEXT_SET_UUID].arg, part_guid))
goto done;
grub_errno = GRUB_ERR_NONE;
done:
grub_free (root);
grub_free (part_name);
grub_free (part_guid);
return grub_errno;
}
static grub_extcmd_t cmd_next;
GRUB_MOD_INIT(gptprio)
{
cmd_next = grub_register_extcmd ("gptprio.next", grub_cmd_next, 0,
N_("-d VARNAME -u VARNAME [DEVICE]"),
N_("Select next partition to boot."),
options_next);
}
GRUB_MOD_FINI(gptprio)
{
grub_unregister_extcmd (cmd_next);
}

View file

@ -0,0 +1,110 @@
/* gptrepair.c - verify and restore GPT info from alternate location. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
* Copyright (C) 2014 CoreOS, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/command.h>
#include <grub/device.h>
#include <grub/err.h>
#include <grub/gpt_partition.h>
#include <grub/i18n.h>
#include <grub/misc.h>
GRUB_MOD_LICENSE ("GPLv3+");
static char *
trim_dev_name (char *name)
{
grub_size_t len = grub_strlen (name);
if (len && name[0] == '(' && name[len - 1] == ')')
{
name[len - 1] = '\0';
name = name + 1;
}
return name;
}
static grub_err_t
grub_cmd_gptrepair (grub_command_t cmd __attribute__ ((unused)),
int argc, char **args)
{
grub_device_t dev = NULL;
grub_gpt_t gpt = NULL;
char *dev_name;
if (argc != 1 || !grub_strlen(args[0]))
return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
dev_name = trim_dev_name (args[0]);
dev = grub_device_open (dev_name);
if (!dev)
goto done;
if (!dev->disk)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, "not a disk");
goto done;
}
gpt = grub_gpt_read (dev->disk);
if (!gpt)
goto done;
if (grub_gpt_both_valid (gpt))
{
grub_printf_ (N_("GPT already valid, %s unmodified.\n"), dev_name);
goto done;
}
if (!grub_gpt_primary_valid (gpt))
grub_printf_ (N_("Found invalid primary GPT on %s\n"), dev_name);
if (!grub_gpt_backup_valid (gpt))
grub_printf_ (N_("Found invalid backup GPT on %s\n"), dev_name);
if (grub_gpt_repair (dev->disk, gpt))
goto done;
if (grub_gpt_write (dev->disk, gpt))
goto done;
grub_printf_ (N_("Repaired GPT on %s\n"), dev_name);
done:
if (gpt)
grub_gpt_free (gpt);
if (dev)
grub_device_close (dev);
return grub_errno;
}
static grub_command_t cmd;
GRUB_MOD_INIT(gptrepair)
{
cmd = grub_register_command ("gptrepair", grub_cmd_gptrepair,
N_("DEVICE"),
N_("Verify and repair GPT on drive DEVICE."));
}
GRUB_MOD_FINI(gptrepair)
{
grub_unregister_command (cmd);
}

View file

@ -0,0 +1,50 @@
/* smbios.c - get smbios tables. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2015 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/acpi.h>
#include <grub/smbios.h>
#include <grub/misc.h>
struct grub_smbios_eps *
grub_machine_smbios_get_eps (void)
{
grub_uint8_t *ptr;
grub_dprintf ("smbios", "Looking for SMBIOS EPS. Scanning BIOS\n");
for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000;
ptr += 16)
if (grub_memcmp (ptr, "_SM_", 4) == 0
&& grub_byte_checksum (ptr, sizeof (struct grub_smbios_eps)) == 0)
return (struct grub_smbios_eps *) ptr;
return 0;
}
struct grub_smbios_eps3 *
grub_machine_smbios_get_eps3 (void)
{
grub_uint8_t *ptr;
grub_dprintf ("smbios", "Looking for SMBIOS3 EPS. Scanning BIOS\n");
for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000;
ptr += 16)
if (grub_memcmp (ptr, "_SM3_", 5) == 0
&& grub_byte_checksum (ptr, sizeof (struct grub_smbios_eps3)) == 0)
return (struct grub_smbios_eps3 *) ptr;
return 0;
}

View file

@ -759,6 +759,78 @@ grub_cmd_trust (grub_extcmd_context_t ctxt,
return GRUB_ERR_NONE;
}
static grub_ssize_t
pseudo_read (struct grub_file *file, char *buf, grub_size_t len)
{
grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len);
return len;
}
/* Filesystem descriptor. */
struct grub_fs pseudo_fs =
{
.name = "pseudo",
.fs_read = pseudo_read
};
static grub_err_t
grub_cmd_trust_var (grub_command_t cmd __attribute__ ((unused)),
int argc, char **args)
{
struct grub_file pseudo_file;
const char *var;
char *data;
struct grub_public_key *pk = NULL;
unsigned int i, idx0, idx1;
if (argc < 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
var = grub_env_get (args[0]);
if (!var)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unknown variable"));
data = grub_zalloc (grub_strlen (var) / 2);
if (!data)
return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate memory for key"));
/* For the want of sscanf() */
for (i = 0; i < grub_strlen (var); i += 2)
{
if (var[i] < 0x40)
idx0 = var[i] - 0x30;
else
idx0 = var[i] - 0x57;
if (var[i+1] < 0x40)
idx1 = var[i+1] - 0x30;
else
idx1 = var[i+1] - 0x57;
data[i/2] = ((idx0 << 4) & 0xf0) | (idx1 & 0x0f);
}
grub_memset (&pseudo_file, 0, sizeof (pseudo_file));
pseudo_file.fs = &pseudo_fs;
pseudo_file.size = grub_strlen (var) / 2;
pseudo_file.data = data;
pk = grub_load_public_key (&pseudo_file);
if (!pk)
{
grub_free(data);
return grub_errno;
}
pk->next = grub_pk_trusted;
grub_pk_trusted = pk;
grub_free(data);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_cmd_list (grub_command_t cmd __attribute__ ((unused)),
int argc __attribute__ ((unused)),
@ -922,21 +994,6 @@ grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)),
return grub_strdup (sec ? "enforce" : "no");
}
static grub_ssize_t
pseudo_read (struct grub_file *file, char *buf, grub_size_t len)
{
grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len);
return len;
}
/* Filesystem descriptor. */
struct grub_fs pseudo_fs =
{
.name = "pseudo",
.fs_read = pseudo_read
};
struct grub_file_verifier grub_pubkey_verifier =
{
.name = "pgp",
@ -947,7 +1004,7 @@ struct grub_file_verifier grub_pubkey_verifier =
};
static grub_extcmd_t cmd, cmd_trust;
static grub_command_t cmd_distrust, cmd_list;
static grub_command_t cmd_trust_var, cmd_distrust, cmd_list;
GRUB_MOD_INIT(pgp)
{
@ -998,6 +1055,9 @@ GRUB_MOD_INIT(pgp)
N_("[-s|--skip-sig] PUBKEY_FILE"),
N_("Add PUBKEY_FILE to trusted keys."),
options);
cmd_trust_var = grub_register_command ("trust_var", grub_cmd_trust_var,
N_("PUBKEY_VAR"),
N_("Add the contents of PUBKEY_VAR to trusted keys."));
cmd_list = grub_register_command ("list_trusted", grub_cmd_list,
0,
N_("Show the list of trusted keys."));
@ -1013,6 +1073,7 @@ GRUB_MOD_FINI(pgp)
grub_verifier_unregister (&grub_pubkey_verifier);
grub_unregister_extcmd (cmd);
grub_unregister_extcmd (cmd_trust);
grub_unregister_command (cmd_trust_var);
grub_unregister_command (cmd_list);
grub_unregister_command (cmd_distrust);
}

View file

@ -30,6 +30,10 @@
#include <grub/i18n.h>
#include <grub/disk.h>
#include <grub/partition.h>
#if defined(DO_SEARCH_PART_UUID) || defined(DO_SEARCH_PART_LABEL) || \
defined(DO_SEARCH_DISK_UUID)
#include <grub/gpt_partition.h>
#endif
GRUB_MOD_LICENSE ("GPLv3+");
@ -66,7 +70,7 @@ iterate_device (const char *name, void *data)
name[0] == 'f' && name[1] == 'd' && name[2] >= '0' && name[2] <= '9')
return 1;
#ifdef DO_SEARCH_FS_UUID
#if defined(DO_SEARCH_FS_UUID) || defined(DO_SEARCH_DISK_UUID)
#define compare_fn grub_strcasecmp
#else
#define compare_fn grub_strcmp
@ -90,6 +94,63 @@ iterate_device (const char *name, void *data)
}
grub_free (buf);
}
#elif defined(DO_SEARCH_PART_UUID)
{
grub_device_t dev;
char *quid;
dev = grub_device_open (name);
if (dev)
{
if (grub_gpt_part_uuid (dev, &quid) == GRUB_ERR_NONE)
{
if (grub_strcasecmp (quid, ctx->key) == 0)
found = 1;
grub_free (quid);
}
grub_device_close (dev);
}
}
#elif defined(DO_SEARCH_PART_LABEL)
{
grub_device_t dev;
char *quid;
dev = grub_device_open (name);
if (dev)
{
if (grub_gpt_part_label (dev, &quid) == GRUB_ERR_NONE)
{
if (grub_strcmp (quid, ctx->key) == 0)
found = 1;
grub_free (quid);
}
grub_device_close (dev);
}
}
#elif defined(DO_SEARCH_DISK_UUID)
{
grub_device_t dev;
char *quid;
dev = grub_device_open (name);
if (dev)
{
if (grub_gpt_disk_uuid (dev, &quid) == GRUB_ERR_NONE)
{
if (grub_strcmp (quid, ctx->key) == 0)
found = 1;
grub_free (quid);
}
grub_device_close (dev);
}
}
#else
{
/* SEARCH_FS_UUID or SEARCH_LABEL */
@ -313,8 +374,14 @@ static grub_command_t cmd;
#ifdef DO_SEARCH_FILE
GRUB_MOD_INIT(search_fs_file)
#elif defined(DO_SEARCH_PART_UUID)
GRUB_MOD_INIT(search_part_uuid)
#elif defined(DO_SEARCH_PART_LABEL)
GRUB_MOD_INIT(search_part_label)
#elif defined (DO_SEARCH_FS_UUID)
GRUB_MOD_INIT(search_fs_uuid)
#elif defined (DO_SEARCH_DISK_UUID)
GRUB_MOD_INIT(search_disk_uuid)
#else
GRUB_MOD_INIT(search_label)
#endif
@ -327,8 +394,14 @@ GRUB_MOD_INIT(search_label)
#ifdef DO_SEARCH_FILE
GRUB_MOD_FINI(search_fs_file)
#elif defined(DO_SEARCH_PART_UUID)
GRUB_MOD_FINI(search_part_uuid)
#elif defined(DO_SEARCH_PART_LABEL)
GRUB_MOD_FINI(search_part_label)
#elif defined (DO_SEARCH_FS_UUID)
GRUB_MOD_FINI(search_fs_uuid)
#elif defined (DO_SEARCH_DISK_UUID)
GRUB_MOD_FINI(search_disk_uuid)
#else
GRUB_MOD_FINI(search_label)
#endif

View file

@ -0,0 +1,5 @@
#define DO_SEARCH_DISK_UUID 1
#define FUNC_NAME grub_search_disk_uuid
#define COMMAND_NAME "search.disk_uuid"
#define HELP_MESSAGE N_("Search devices by disk UUID. If VARIABLE is specified, the first device found is set to a variable.")
#include "search.c"

View file

@ -0,0 +1,5 @@
#define DO_SEARCH_PART_LABEL 1
#define FUNC_NAME grub_search_part_label
#define COMMAND_NAME "search.part_label"
#define HELP_MESSAGE N_("Search devices by partition label. If VARIABLE is specified, the first device found is set to a variable.")
#include "search.c"

View file

@ -0,0 +1,5 @@
#define DO_SEARCH_PART_UUID 1
#define FUNC_NAME grub_search_part_uuid
#define COMMAND_NAME "search.part_uuid"
#define HELP_MESSAGE N_("Search devices by partition UUID. If VARIABLE is specified, the first device found is set to a variable.")
#include "search.c"

View file

@ -36,6 +36,12 @@ static const struct grub_arg_option options[] =
0, 0},
{"fs-uuid", 'u', 0, N_("Search devices by a filesystem UUID."),
0, 0},
{"part-label", 'L', 0, N_("Search devices by a partition label."),
0, 0},
{"part-uuid", 'U', 0, N_("Search devices by a partition UUID."),
0, 0},
{"disk-uuid", 'U', 0, N_("Search devices by a disk UUID."),
0, 0},
{"set", 's', GRUB_ARG_OPTION_OPTIONAL,
N_("Set a variable to the first device found."), N_("VARNAME"),
ARG_TYPE_STRING},
@ -71,6 +77,9 @@ enum options
SEARCH_FILE,
SEARCH_LABEL,
SEARCH_FS_UUID,
SEARCH_PART_LABEL,
SEARCH_PART_UUID,
SEARCH_DISK_UUID,
SEARCH_SET,
SEARCH_NO_FLOPPY,
SEARCH_HINT,
@ -186,6 +195,15 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args)
else if (state[SEARCH_FS_UUID].set)
grub_search_fs_uuid (id, var, state[SEARCH_NO_FLOPPY].set,
hints, nhints);
else if (state[SEARCH_PART_LABEL].set)
grub_search_part_label (id, var, state[SEARCH_NO_FLOPPY].set,
hints, nhints);
else if (state[SEARCH_PART_UUID].set)
grub_search_part_uuid (id, var, state[SEARCH_NO_FLOPPY].set,
hints, nhints);
else if (state[SEARCH_DISK_UUID].set)
grub_search_disk_uuid (id, var, state[SEARCH_NO_FLOPPY].set,
hints, nhints);
else if (state[SEARCH_FILE].set)
grub_search_fs_file (id, var, state[SEARCH_NO_FLOPPY].set,
hints, nhints);

372
grub-core/commands/smbios.c Normal file
View file

@ -0,0 +1,372 @@
/* smbios.c - retrieve smbios information. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2013,2014,2015 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/dl.h>
#include <grub/env.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/smbios.h>
GRUB_MOD_LICENSE ("GPLv3+");
/* Locate the SMBIOS entry point structure depending on the hardware. */
struct grub_smbios_eps *
grub_smbios_get_eps (void)
{
static struct grub_smbios_eps *eps = NULL;
if (eps != NULL)
return eps;
eps = grub_machine_smbios_get_eps ();
return eps;
}
/* Locate the SMBIOS3 entry point structure depending on the hardware. */
struct grub_smbios_eps3 *
grub_smbios_get_eps3 (void)
{
static struct grub_smbios_eps3 *eps = NULL;
if (eps != NULL)
return eps;
eps = grub_machine_smbios_get_eps3 ();
return eps;
}
/* Abstract useful values found in either the SMBIOS3 or SMBIOS EPS. */
static struct {
grub_addr_t start;
grub_addr_t end;
grub_uint16_t structures;
} table_desc = {0, 0, 0};
/*
* These functions convert values from the various SMBIOS structure field types
* into a string formatted to be returned to the user. They expect that the
* structure and offset were already validated. The given buffer stores the
* newly formatted string if needed. When the requested data is successfully
* retrieved and formatted, the pointer to the string is returned; otherwise,
* NULL is returned on failure.
*/
static const char *
grub_smbios_format_byte (char *buffer, grub_size_t size,
const grub_uint8_t *structure, grub_uint8_t offset)
{
grub_snprintf (buffer, size, "%u", structure[offset]);
return (const char *)buffer;
}
static const char *
grub_smbios_format_word (char *buffer, grub_size_t size,
const grub_uint8_t *structure, grub_uint8_t offset)
{
grub_uint16_t value = grub_get_unaligned16 (structure + offset);
grub_snprintf (buffer, size, "%u", value);
return (const char *)buffer;
}
static const char *
grub_smbios_format_dword (char *buffer, grub_size_t size,
const grub_uint8_t *structure, grub_uint8_t offset)
{
grub_uint32_t value = grub_get_unaligned32 (structure + offset);
grub_snprintf (buffer, size, "%" PRIuGRUB_UINT32_T, value);
return (const char *)buffer;
}
static const char *
grub_smbios_format_qword (char *buffer, grub_size_t size,
const grub_uint8_t *structure, grub_uint8_t offset)
{
grub_uint64_t value = grub_get_unaligned64 (structure + offset);
grub_snprintf (buffer, size, "%" PRIuGRUB_UINT64_T, value);
return (const char *)buffer;
}
/* The matching string pointer is returned directly to avoid extra copying. */
static const char *
grub_smbios_get_string (char *buffer __attribute__ ((unused)),
grub_size_t size __attribute__ ((unused)),
const grub_uint8_t *structure, grub_uint8_t offset)
{
const grub_uint8_t *ptr = structure + structure[1];
const grub_uint8_t *table_end = (const grub_uint8_t *)table_desc.end;
const grub_uint8_t referenced_string_number = structure[offset];
grub_uint8_t i;
/* A string referenced with zero is interpreted as unset. */
if (referenced_string_number == 0)
return NULL;
/* Search the string set. */
for (i = 1; *ptr != 0 && ptr < table_end; i++)
if (i == referenced_string_number)
{
const char *str = (const char *)ptr;
while (*ptr++ != 0)
if (ptr >= table_end)
return NULL; /* The string isn't terminated. */
return str;
}
else
while (*ptr++ != 0 && ptr < table_end);
/* The string number is greater than the number of strings in the set. */
return NULL;
}
static const char *
grub_smbios_format_uuid (char *buffer, grub_size_t size,
const grub_uint8_t *structure, grub_uint8_t offset)
{
const grub_uint8_t *f = structure + offset; /* little-endian fields */
const grub_uint8_t *g = f + 8; /* byte-by-byte fields */
grub_snprintf (buffer, size,
"%02x%02x%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x-%02x%02x%02x%02x%02x%02x",
f[3], f[2], f[1], f[0], f[5], f[4], f[7], f[6],
g[0], g[1], g[2], g[3], g[4], g[5], g[6], g[7]);
return (const char *)buffer;
}
/* List the field formatting functions and the number of bytes they need. */
#define MAXIMUM_FORMAT_LENGTH (sizeof ("ffffffff-ffff-ffff-ffff-ffffffffffff"))
static const struct {
const char *(*format) (char *buffer, grub_size_t size,
const grub_uint8_t *structure, grub_uint8_t offset);
grub_uint8_t field_length;
} field_extractors[] = {
{grub_smbios_format_byte, 1},
{grub_smbios_format_word, 2},
{grub_smbios_format_dword, 4},
{grub_smbios_format_qword, 8},
{grub_smbios_get_string, 1},
{grub_smbios_format_uuid, 16}
};
/* List command options, with structure field getters ordered as above. */
#define FIRST_GETTER_OPT (3)
#define SETTER_OPT (FIRST_GETTER_OPT + ARRAY_SIZE(field_extractors))
static const struct grub_arg_option options[] = {
{"type", 't', 0, N_("Match entries with the given type."),
N_("type"), ARG_TYPE_INT},
{"handle", 'h', 0, N_("Match entries with the given handle."),
N_("handle"), ARG_TYPE_INT},
{"match", 'm', 0, N_("Select a structure when several match."),
N_("match"), ARG_TYPE_INT},
{"get-byte", 'b', 0, N_("Get the byte's value at the given offset."),
N_("offset"), ARG_TYPE_INT},
{"get-word", 'w', 0, N_("Get two bytes' value at the given offset."),
N_("offset"), ARG_TYPE_INT},
{"get-dword", 'd', 0, N_("Get four bytes' value at the given offset."),
N_("offset"), ARG_TYPE_INT},
{"get-qword", 'q', 0, N_("Get eight bytes' value at the given offset."),
N_("offset"), ARG_TYPE_INT},
{"get-string", 's', 0, N_("Get the string specified at the given offset."),
N_("offset"), ARG_TYPE_INT},
{"get-uuid", 'u', 0, N_("Get the UUID's value at the given offset."),
N_("offset"), ARG_TYPE_INT},
{"set", '\0', 0, N_("Store the value in the given variable name."),
N_("variable"), ARG_TYPE_STRING},
{0, 0, 0, 0, 0, 0}
};
/*
* Return a matching SMBIOS structure.
*
* This method can use up to three criteria for selecting a structure:
* - The "type" field (use -1 to ignore)
* - The "handle" field (use -1 to ignore)
* - Which to return if several match (use 0 to ignore)
*
* The return value is a pointer to the first matching structure. If no
* structures match the given parameters, NULL is returned.
*/
static const grub_uint8_t *
grub_smbios_match_structure (const grub_int16_t type,
const grub_int32_t handle,
const grub_uint16_t match)
{
const grub_uint8_t *ptr = (const grub_uint8_t *)table_desc.start;
const grub_uint8_t *table_end = (const grub_uint8_t *)table_desc.end;
grub_uint16_t structures = table_desc.structures;
grub_uint16_t structure_count = 0;
grub_uint16_t matches = 0;
while (ptr < table_end
&& ptr[1] >= 4 /* Valid structures include the 4-byte header. */
&& (structure_count++ < structures || structures == 0))
{
grub_uint16_t structure_handle = grub_get_unaligned16 (ptr + 2);
grub_uint8_t structure_type = ptr[0];
if ((handle < 0 || handle == structure_handle)
&& (type < 0 || type == structure_type)
&& (match == 0 || match == ++matches))
return ptr;
else
{
ptr += ptr[1];
while ((*ptr++ != 0 || *ptr++ != 0) && ptr < table_end);
}
if (structure_type == GRUB_SMBIOS_TYPE_END_OF_TABLE)
break;
}
return NULL;
}
static grub_err_t
grub_cmd_smbios (grub_extcmd_context_t ctxt,
int argc __attribute__ ((unused)),
char **argv __attribute__ ((unused)))
{
struct grub_arg_list *state = ctxt->state;
grub_int16_t type = -1;
grub_int32_t handle = -1;
grub_uint16_t match = 0;
grub_uint8_t offset = 0;
const grub_uint8_t *structure;
const char *value;
char buffer[MAXIMUM_FORMAT_LENGTH];
grub_int32_t option;
grub_int8_t field_type = -1;
grub_uint8_t i;
if (table_desc.start == 0)
return grub_error (GRUB_ERR_IO,
N_("the SMBIOS entry point structure was not found"));
/* Read the given filtering options. */
if (state[0].set)
{
option = grub_strtol (state[0].arg, NULL, 0);
if (option < 0 || option > 255)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("the type must be between 0 and 255"));
type = (grub_int16_t)option;
}
if (state[1].set)
{
option = grub_strtol (state[1].arg, NULL, 0);
if (option < 0 || option > 65535)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("the handle must be between 0 and 65535"));
handle = (grub_int32_t)option;
}
if (state[2].set)
{
option = grub_strtol (state[2].arg, NULL, 0);
if (option <= 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("the match must be a positive integer"));
match = (grub_uint16_t)option;
}
/* Determine the data type of the structure field to retrieve. */
for (i = 0; i < ARRAY_SIZE(field_extractors); i++)
if (state[FIRST_GETTER_OPT + i].set)
{
if (field_type >= 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("only one --get option is usable at a time"));
field_type = i;
}
/* Require a choice of a structure field to return. */
if (field_type < 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("one of the --get options is required"));
/* Locate a matching SMBIOS structure. */
structure = grub_smbios_match_structure (type, handle, match);
if (structure == NULL)
return grub_error (GRUB_ERR_IO,
N_("no structure matched the given options"));
/* Ensure the requested byte offset is inside the structure. */
option = grub_strtol (state[FIRST_GETTER_OPT + field_type].arg, NULL, 0);
if (option < 0 || option >= structure[1])
return grub_error (GRUB_ERR_OUT_OF_RANGE,
N_("the given offset is outside the structure"));
/* Ensure the requested data type at the offset is inside the structure. */
offset = (grub_uint8_t)option;
if (offset + field_extractors[field_type].field_length > structure[1])
return grub_error (GRUB_ERR_OUT_OF_RANGE,
N_("the field ends outside the structure"));
/* Format the requested structure field into a readable string. */
value = field_extractors[field_type].format (buffer, sizeof (buffer),
structure, offset);
if (value == NULL)
return grub_error (GRUB_ERR_IO,
N_("failed to retrieve the structure field"));
/* Store or print the formatted value. */
if (state[SETTER_OPT].set)
grub_env_set (state[SETTER_OPT].arg, value);
else
grub_printf ("%s\n", value);
return GRUB_ERR_NONE;
}
static grub_extcmd_t cmd;
GRUB_MOD_INIT(smbios)
{
struct grub_smbios_eps3 *eps3;
struct grub_smbios_eps *eps;
if ((eps3 = grub_smbios_get_eps3 ()))
{
table_desc.start = (grub_addr_t)eps3->table_address;
table_desc.end = table_desc.start + eps3->maximum_table_length;
table_desc.structures = 0; /* SMBIOS3 drops the structure count. */
}
else if ((eps = grub_smbios_get_eps ()))
{
table_desc.start = (grub_addr_t)eps->intermediate.table_address;
table_desc.end = table_desc.start + eps->intermediate.table_length;
table_desc.structures = eps->intermediate.structures;
}
cmd = grub_register_extcmd ("smbios", grub_cmd_smbios, 0,
N_("[-t type] [-h handle] [-m match] "
"(-b|-w|-d|-q|-s|-u) offset "
"[--set variable]"),
N_("Retrieve SMBIOS information."), options);
}
GRUB_MOD_FINI(smbios)
{
grub_unregister_extcmd (cmd);
}

View file

@ -278,10 +278,14 @@ grub_biosdisk_call_hook (grub_disk_dev_iterate_hook_t hook, void *hook_data,
char name[10];
if (cd_drive && drive == cd_drive)
{
grub_dprintf ("biosdisk", "iterating cd\n");
return hook ("cd", hook_data);
}
grub_snprintf (name, sizeof (name),
(drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80));
grub_dprintf ("biosdisk", "iterating %s\n", name);
return hook (name, hook_data);
}
@ -301,7 +305,7 @@ grub_biosdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
if (grub_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1,
GRUB_MEMORY_MACHINE_SCRATCH_SEG) != 0)
{
grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive);
grub_dprintf ("biosdisk", "Read error when probing drive 0x%2x\n", drive);
break;
}
@ -336,6 +340,8 @@ grub_biosdisk_open (const char *name, grub_disk_t disk)
int drive;
struct grub_biosdisk_data *data;
grub_dprintf ("biosdisk", "opening %s\n", name);
drive = grub_biosdisk_get_drive (name);
if (drive < 0)
return grub_errno;
@ -393,6 +399,11 @@ grub_biosdisk_open (const char *name, grub_disk_t disk)
(1 << disk->log_sector_size) < drp->bytes_per_sector;
disk->log_sector_size++);
}
grub_dprintf ("biosdisk",
"LBA total = 0x%llx block size = 0x%lx\n",
(unsigned long long) total_sectors,
1L << disk->log_sector_size);
}
}
}
@ -428,6 +439,9 @@ grub_biosdisk_open (const char *name, grub_disk_t disk)
if (! total_sectors)
total_sectors = ((grub_uint64_t) data->cylinders)
* data->heads * data->sectors;
grub_dprintf ("biosdisk", "C/H/S %lu/%lu/%lu\n",
data->cylinders, data->heads, data->sectors);
}
disk->total_sectors = total_sectors;
@ -440,12 +454,15 @@ grub_biosdisk_open (const char *name, grub_disk_t disk)
disk->data = data;
grub_dprintf ("biosdisk", "opening %s succeeded\n", name);
return GRUB_ERR_NONE;
}
static void
grub_biosdisk_close (grub_disk_t disk)
{
grub_dprintf ("biosdisk", "closing %s\n", disk->name);
grub_free (disk->data);
}
@ -577,6 +594,9 @@ static grub_err_t
grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t size, char *buf)
{
grub_dprintf ("biosdisk", "reading 0x%lx sectors at 0x%llx from %s\n",
(unsigned long) size, (unsigned long long) sector, disk->name);
while (size)
{
grub_size_t len;
@ -607,6 +627,9 @@ grub_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
{
struct grub_biosdisk_data *data = disk->data;
grub_dprintf ("biosdisk", "writing 0x%lx sectors at 0x%llx to %s\n",
(unsigned long) size, (unsigned long long) sector, disk->name);
if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
return grub_error (GRUB_ERR_IO, N_("cannot write to CD-ROM"));

View file

@ -22,18 +22,22 @@
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/acpi.h>
#include <grub/smbios.h>
grub_err_t
grub_machine_efiemu_init_tables (void)
{
grub_uint8_t *ptr;
void *table;
grub_err_t err;
grub_efi_guid_t smbios = GRUB_EFI_SMBIOS_TABLE_GUID;
grub_efi_guid_t smbios3 = GRUB_EFI_SMBIOS3_TABLE_GUID;
grub_efi_guid_t acpi20 = GRUB_EFI_ACPI_20_TABLE_GUID;
grub_efi_guid_t acpi = GRUB_EFI_ACPI_TABLE_GUID;
err = grub_efiemu_unregister_configuration_table (smbios);
if (err)
return err;
err = grub_efiemu_unregister_configuration_table (smbios3);
if (err)
return err;
err = grub_efiemu_unregister_configuration_table (acpi);
@ -43,6 +47,20 @@ grub_machine_efiemu_init_tables (void)
if (err)
return err;
table = grub_smbios_get_eps ();
if (table)
{
err = grub_efiemu_register_configuration_table (smbios, 0, 0, table);
if (err)
return err;
}
table = grub_smbios_get_eps3 ();
if (table)
{
err = grub_efiemu_register_configuration_table (smbios3, 0, 0, table);
if (err)
return err;
}
table = grub_acpi_get_rsdpv1 ();
if (table)
{
@ -58,19 +76,5 @@ grub_machine_efiemu_init_tables (void)
return err;
}
for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000;
ptr += 16)
if (grub_memcmp (ptr, "_SM_", 4) == 0
&& grub_byte_checksum (ptr, *(ptr + 5)) == 0)
break;
if (ptr < (grub_uint8_t *) 0x100000)
{
grub_dprintf ("efiemu", "Registering SMBIOS\n");
err = grub_efiemu_register_configuration_table (smbios, 0, 0, ptr);
if (err)
return err;
}
return GRUB_ERR_NONE;
}

View file

@ -32,12 +32,21 @@
#include <grub/env.h>
#include <grub/cache.h>
#include <grub/i18n.h>
#include <grub/tpm.h>
/* Platforms where modules are in a readonly area of memory. */
#if defined(GRUB_MACHINE_QEMU)
#define GRUB_MODULES_MACHINE_READONLY
#endif
#ifdef GRUB_MACHINE_EMU
#include <sys/mman.h>
#endif
#ifdef GRUB_MACHINE_EFI
#include <grub/efi/efi.h>
#endif
#pragma GCC diagnostic ignored "-Wcast-align"
@ -686,6 +695,15 @@ grub_dl_load_file (const char *filename)
void *core = 0;
grub_dl_t mod = 0;
#ifdef GRUB_MACHINE_EFI
if (grub_efi_secure_boot ())
{
grub_error (GRUB_ERR_ACCESS_DENIED,
"Secure Boot forbids loading module from %s", filename);
return 0;
}
#endif
grub_boot_time ("Loading module %s", filename);
file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE);
@ -712,6 +730,9 @@ grub_dl_load_file (const char *filename)
opens of the same device. */
grub_file_close (file);
grub_tpm_measure(core, size, GRUB_BINARY_PCR, filename);
grub_print_error();
mod = grub_dl_load_core (core, size);
grub_free (core);
if (! mod)

View file

@ -273,6 +273,34 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid,
return NULL;
}
grub_efi_boolean_t
grub_efi_secure_boot (void)
{
grub_efi_guid_t efi_var_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID;
grub_size_t datasize;
char *secure_boot = NULL;
char *setup_mode = NULL;
grub_efi_boolean_t ret = 0;
secure_boot = grub_efi_get_variable("SecureBoot", &efi_var_guid, &datasize);
if (datasize != 1 || !secure_boot)
goto out;
setup_mode = grub_efi_get_variable("SetupMode", &efi_var_guid, &datasize);
if (datasize != 1 || !setup_mode)
goto out;
if (*secure_boot && !*setup_mode)
ret = 1;
out:
grub_free (secure_boot);
grub_free (setup_mode);
return ret;
}
#pragma GCC diagnostic ignored "-Wcast-align"
/* Search the mods section from the PE32/PE32+ image. This code uses

View file

@ -49,6 +49,38 @@ static grub_efi_uintn_t finish_desc_size;
static grub_efi_uint32_t finish_desc_version;
int grub_efi_is_finished = 0;
/* Allocate pages below a specified address */
void *
grub_efi_allocate_pages_max (grub_efi_physical_address_t max,
grub_efi_uintn_t pages)
{
grub_efi_status_t status;
grub_efi_boot_services_t *b;
grub_efi_physical_address_t address = max;
if (max > 0xffffffff)
return 0;
b = grub_efi_system_table->boot_services;
status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address);
if (status != GRUB_EFI_SUCCESS)
return 0;
if (address == 0)
{
/* Uggh, the address 0 was allocated... This is too annoying,
so reallocate another one. */
address = max;
status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address);
grub_efi_free_pages (0, pages);
if (status != GRUB_EFI_SUCCESS)
return 0;
}
return (void *) ((grub_addr_t) address);
}
/*
* We need to roll back EFI allocations on exit. Remember allocations that
* we'll free on exit.

273
grub-core/kern/efi/tpm.c Normal file
View file

@ -0,0 +1,273 @@
#include <grub/err.h>
#include <grub/i18n.h>
#include <grub/efi/api.h>
#include <grub/efi/efi.h>
#include <grub/efi/tpm.h>
#include <grub/mm.h>
#include <grub/tpm.h>
#include <grub/term.h>
static grub_efi_guid_t tpm_guid = EFI_TPM_GUID;
static grub_efi_guid_t tpm2_guid = EFI_TPM2_GUID;
static grub_efi_boolean_t grub_tpm_present(grub_efi_tpm_protocol_t *tpm)
{
grub_efi_status_t status;
TCG_EFI_BOOT_SERVICE_CAPABILITY caps;
grub_uint32_t flags;
grub_efi_physical_address_t eventlog, lastevent;
caps.Size = (grub_uint8_t)sizeof(caps);
status = efi_call_5(tpm->status_check, tpm, &caps, &flags, &eventlog,
&lastevent);
if (status != GRUB_EFI_SUCCESS || caps.TPMDeactivatedFlag
|| !caps.TPMPresentFlag)
return 0;
return 1;
}
static grub_efi_boolean_t grub_tpm2_present(grub_efi_tpm2_protocol_t *tpm)
{
grub_efi_status_t status;
EFI_TCG2_BOOT_SERVICE_CAPABILITY caps;
caps.Size = (grub_uint8_t)sizeof(caps);
status = efi_call_2(tpm->get_capability, tpm, &caps);
if (status != GRUB_EFI_SUCCESS || !caps.TPMPresentFlag)
return 0;
return 1;
}
static grub_efi_boolean_t grub_tpm_handle_find(grub_efi_handle_t *tpm_handle,
grub_efi_uint8_t *protocol_version)
{
grub_efi_handle_t *handles;
grub_efi_uintn_t num_handles;
handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &tpm_guid, NULL,
&num_handles);
if (handles && num_handles > 0) {
*tpm_handle = handles[0];
*protocol_version = 1;
return 1;
}
handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &tpm2_guid, NULL,
&num_handles);
if (handles && num_handles > 0) {
*tpm_handle = handles[0];
*protocol_version = 2;
return 1;
}
return 0;
}
static grub_err_t
grub_tpm1_execute(grub_efi_handle_t tpm_handle,
PassThroughToTPM_InputParamBlock *inbuf,
PassThroughToTPM_OutputParamBlock *outbuf)
{
grub_efi_status_t status;
grub_efi_tpm_protocol_t *tpm;
grub_uint32_t inhdrsize = sizeof(*inbuf) - sizeof(inbuf->TPMOperandIn);
grub_uint32_t outhdrsize = sizeof(*outbuf) - sizeof(outbuf->TPMOperandOut);
tpm = grub_efi_open_protocol (tpm_handle, &tpm_guid,
GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (!grub_tpm_present(tpm))
return 0;
/* UEFI TPM protocol takes the raw operand block, no param block header */
status = efi_call_5 (tpm->pass_through_to_tpm, tpm,
inbuf->IPBLength - inhdrsize, inbuf->TPMOperandIn,
outbuf->OPBLength - outhdrsize, outbuf->TPMOperandOut);
switch (status) {
case GRUB_EFI_SUCCESS:
return 0;
case GRUB_EFI_DEVICE_ERROR:
return grub_error (GRUB_ERR_IO, N_("Command failed"));
case GRUB_EFI_INVALID_PARAMETER:
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter"));
case GRUB_EFI_BUFFER_TOO_SMALL:
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Output buffer too small"));
case GRUB_EFI_NOT_FOUND:
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable"));
default:
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error"));
}
}
static grub_err_t
grub_tpm2_execute(grub_efi_handle_t tpm_handle,
PassThroughToTPM_InputParamBlock *inbuf,
PassThroughToTPM_OutputParamBlock *outbuf)
{
grub_efi_status_t status;
grub_efi_tpm2_protocol_t *tpm;
grub_uint32_t inhdrsize = sizeof(*inbuf) - sizeof(inbuf->TPMOperandIn);
grub_uint32_t outhdrsize = sizeof(*outbuf) - sizeof(outbuf->TPMOperandOut);
tpm = grub_efi_open_protocol (tpm_handle, &tpm2_guid,
GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (!grub_tpm2_present(tpm))
return 0;
/* UEFI TPM protocol takes the raw operand block, no param block header */
status = efi_call_5 (tpm->submit_command, tpm,
inbuf->IPBLength - inhdrsize, inbuf->TPMOperandIn,
outbuf->OPBLength - outhdrsize, outbuf->TPMOperandOut);
switch (status) {
case GRUB_EFI_SUCCESS:
return 0;
case GRUB_EFI_DEVICE_ERROR:
return grub_error (GRUB_ERR_IO, N_("Command failed"));
case GRUB_EFI_INVALID_PARAMETER:
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter"));
case GRUB_EFI_BUFFER_TOO_SMALL:
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Output buffer too small"));
case GRUB_EFI_NOT_FOUND:
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable"));
default:
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error"));
}
}
grub_err_t
grub_tpm_execute(PassThroughToTPM_InputParamBlock *inbuf,
PassThroughToTPM_OutputParamBlock *outbuf)
{
grub_efi_handle_t tpm_handle;
grub_uint8_t protocol_version;
/* It's not a hard failure for there to be no TPM */
if (!grub_tpm_handle_find(&tpm_handle, &protocol_version))
return 0;
if (protocol_version == 1) {
return grub_tpm1_execute(tpm_handle, inbuf, outbuf);
} else {
return grub_tpm2_execute(tpm_handle, inbuf, outbuf);
}
}
static grub_err_t
grub_tpm1_log_event(grub_efi_handle_t tpm_handle, unsigned char *buf,
grub_size_t size, grub_uint8_t pcr,
const char *description)
{
TCG_PCR_EVENT *event;
grub_efi_status_t status;
grub_efi_tpm_protocol_t *tpm;
grub_efi_physical_address_t lastevent;
grub_uint32_t algorithm;
grub_uint32_t eventnum = 0;
tpm = grub_efi_open_protocol (tpm_handle, &tpm_guid,
GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (!grub_tpm_present(tpm))
return 0;
event = grub_zalloc(sizeof (TCG_PCR_EVENT) + grub_strlen(description) + 1);
if (!event)
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
N_("cannot allocate TPM event buffer"));
event->PCRIndex = pcr;
event->EventType = EV_IPL;
event->EventSize = grub_strlen(description) + 1;
grub_memcpy(event->Event, description, event->EventSize);
algorithm = TCG_ALG_SHA;
status = efi_call_7 (tpm->log_extend_event, tpm, (grub_efi_physical_address_t)buf, (grub_uint64_t) size,
algorithm, event, &eventnum, &lastevent);
switch (status) {
case GRUB_EFI_SUCCESS:
return 0;
case GRUB_EFI_DEVICE_ERROR:
return grub_error (GRUB_ERR_IO, N_("Command failed"));
case GRUB_EFI_INVALID_PARAMETER:
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter"));
case GRUB_EFI_BUFFER_TOO_SMALL:
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Output buffer too small"));
case GRUB_EFI_NOT_FOUND:
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable"));
default:
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error"));
}
}
static grub_err_t
grub_tpm2_log_event(grub_efi_handle_t tpm_handle, unsigned char *buf,
grub_size_t size, grub_uint8_t pcr,
const char *description)
{
EFI_TCG2_EVENT *event;
grub_efi_status_t status;
grub_efi_tpm2_protocol_t *tpm;
tpm = grub_efi_open_protocol (tpm_handle, &tpm2_guid,
GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (!grub_tpm2_present(tpm))
return 0;
event = grub_zalloc(sizeof (EFI_TCG2_EVENT) + grub_strlen(description) + 1);
if (!event)
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
N_("cannot allocate TPM event buffer"));
event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER);
event->Header.HeaderVersion = 1;
event->Header.PCRIndex = pcr;
event->Header.EventType = EV_IPL;
event->Size = sizeof(*event) - sizeof(event->Event) + grub_strlen(description) + 1;
grub_memcpy(event->Event, description, grub_strlen(description) + 1);
status = efi_call_5 (tpm->hash_log_extend_event, tpm, 0, (grub_efi_physical_address_t)buf,
(grub_uint64_t) size, event);
switch (status) {
case GRUB_EFI_SUCCESS:
return 0;
case GRUB_EFI_DEVICE_ERROR:
return grub_error (GRUB_ERR_IO, N_("Command failed"));
case GRUB_EFI_INVALID_PARAMETER:
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter"));
case GRUB_EFI_BUFFER_TOO_SMALL:
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Output buffer too small"));
case GRUB_EFI_NOT_FOUND:
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable"));
default:
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error"));
}
}
grub_err_t
grub_tpm_log_event(unsigned char *buf, grub_size_t size, grub_uint8_t pcr,
const char *description)
{
grub_efi_handle_t tpm_handle;
grub_efi_uint8_t protocol_version;
if (!grub_tpm_handle_find(&tpm_handle, &protocol_version))
return 0;
if (protocol_version == 1) {
return grub_tpm1_log_event(tpm_handle, buf, size, pcr, description);
} else {
return grub_tpm2_log_event(tpm_handle, buf, size, pcr, description);
}
}

View file

@ -0,0 +1,145 @@
#include <grub/err.h>
#include <grub/i18n.h>
#include <grub/mm.h>
#include <grub/tpm.h>
#include <grub/misc.h>
#include <grub/i386/pc/int.h>
#define TCPA_MAGIC 0x41504354
static int tpm_presence = -1;
int tpm_present(void);
int tpm_present(void)
{
struct grub_bios_int_registers regs;
if (tpm_presence != -1)
return tpm_presence;
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
regs.eax = 0xbb00;
regs.ebx = TCPA_MAGIC;
grub_bios_interrupt (0x1a, &regs);
if (regs.eax == 0)
tpm_presence = 1;
else
tpm_presence = 0;
return tpm_presence;
}
grub_err_t
grub_tpm_execute(PassThroughToTPM_InputParamBlock *inbuf,
PassThroughToTPM_OutputParamBlock *outbuf)
{
struct grub_bios_int_registers regs;
grub_addr_t inaddr, outaddr;
if (!tpm_present())
return 0;
inaddr = (grub_addr_t) inbuf;
outaddr = (grub_addr_t) outbuf;
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
regs.eax = 0xbb02;
regs.ebx = TCPA_MAGIC;
regs.ecx = 0;
regs.edx = 0;
regs.es = (inaddr & 0xffff0000) >> 4;
regs.edi = inaddr & 0xffff;
regs.ds = outaddr >> 4;
regs.esi = outaddr & 0xf;
grub_bios_interrupt (0x1a, &regs);
if (regs.eax)
{
tpm_presence = 0;
return grub_error (GRUB_ERR_IO, N_("TPM error %x, disabling TPM"), regs.eax);
}
return 0;
}
typedef struct {
grub_uint32_t pcrindex;
grub_uint32_t eventtype;
grub_uint8_t digest[20];
grub_uint32_t eventdatasize;
grub_uint8_t event[0];
} GRUB_PACKED Event;
typedef struct {
grub_uint16_t ipblength;
grub_uint16_t reserved;
grub_uint32_t hashdataptr;
grub_uint32_t hashdatalen;
grub_uint32_t pcr;
grub_uint32_t reserved2;
grub_uint32_t logdataptr;
grub_uint32_t logdatalen;
} GRUB_PACKED EventIncoming;
typedef struct {
grub_uint16_t opblength;
grub_uint16_t reserved;
grub_uint32_t eventnum;
grub_uint8_t hashvalue[20];
} GRUB_PACKED EventOutgoing;
grub_err_t
grub_tpm_log_event(unsigned char *buf, grub_size_t size, grub_uint8_t pcr,
const char *description)
{
struct grub_bios_int_registers regs;
EventIncoming incoming;
EventOutgoing outgoing;
Event *event;
grub_uint32_t datalength;
if (!tpm_present())
return 0;
datalength = grub_strlen(description);
event = grub_zalloc(datalength + sizeof(Event));
if (!event)
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
N_("cannot allocate TPM event buffer"));
event->pcrindex = pcr;
event->eventtype = 0x0d;
event->eventdatasize = grub_strlen(description);
grub_memcpy(event->event, description, datalength);
incoming.ipblength = sizeof(incoming);
incoming.hashdataptr = (grub_uint32_t)buf;
incoming.hashdatalen = size;
incoming.pcr = pcr;
incoming.logdataptr = (grub_uint32_t)event;
incoming.logdatalen = datalength + sizeof(Event);
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
regs.eax = 0xbb01;
regs.ebx = TCPA_MAGIC;
regs.ecx = 0;
regs.edx = 0;
regs.es = (((grub_addr_t) &incoming) & 0xffff0000) >> 4;
regs.edi = ((grub_addr_t) &incoming) & 0xffff;
regs.ds = (((grub_addr_t) &outgoing) & 0xffff0000) >> 4;
regs.esi = ((grub_addr_t) &outgoing) & 0xffff;
grub_bios_interrupt (0x1a, &regs);
grub_free(event);
if (regs.eax)
{
tpm_presence = 0;
return grub_error (GRUB_ERR_IO, N_("TPM error %x, disabling TPM"), regs.eax);
}
return 0;
}

View file

@ -131,6 +131,9 @@ grub_set_prefix_and_root (void)
{
char *cmdpath;
grub_env_set ("cmddevice", fwdevice);
grub_env_export ("cmddevice");
cmdpath = grub_xasprintf ("(%s)%s", fwdevice, fwpath ? : "");
if (cmdpath)
{

14
grub-core/kern/tpm.c Normal file
View file

@ -0,0 +1,14 @@
#include <grub/err.h>
#include <grub/i18n.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/tpm.h>
#include <grub/term.h>
grub_err_t
grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr,
const char *description)
{
return grub_tpm_log_event (buf, size, pcr, description);
}

View file

@ -19,6 +19,7 @@
#include <grub/lib/cmdline.h>
#include <grub/misc.h>
#include <grub/tpm.h>
static unsigned int check_arg (char *c, int *has_space)
{

777
grub-core/lib/gpt.c Normal file
View file

@ -0,0 +1,777 @@
/* gpt.c - Read/Verify/Write GUID Partition Tables (GPT). */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2005,2006,2007,2008 Free Software Foundation, Inc.
* Copyright (C) 2014 CoreOS, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/charset.h>
#include <grub/crypto.h>
#include <grub/device.h>
#include <grub/disk.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/dl.h>
#include <grub/msdos_partition.h>
#include <grub/gpt_partition.h>
GRUB_MOD_LICENSE ("GPLv3+");
static grub_uint8_t grub_gpt_magic[] = GRUB_GPT_HEADER_MAGIC;
static grub_err_t
grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt,
struct grub_gpt_header *header,
void **ret_entries,
grub_size_t *ret_entries_size);
char *
grub_gpt_guid_to_str (grub_gpt_part_guid_t *guid)
{
return grub_xasprintf ("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
grub_le_to_cpu32 (guid->data1),
grub_le_to_cpu16 (guid->data2),
grub_le_to_cpu16 (guid->data3),
guid->data4[0], guid->data4[1],
guid->data4[2], guid->data4[3],
guid->data4[4], guid->data4[5],
guid->data4[6], guid->data4[7]);
}
static grub_err_t
grub_gpt_device_partentry (grub_device_t device,
struct grub_gpt_partentry *entry)
{
grub_disk_t disk = device->disk;
grub_partition_t p;
grub_err_t err;
if (!disk || !disk->partition)
return grub_error (GRUB_ERR_BUG, "not a partition");
if (grub_strcmp (disk->partition->partmap->name, "gpt"))
return grub_error (GRUB_ERR_BAD_ARGUMENT, "not a GPT partition");
p = disk->partition;
disk->partition = p->parent;
err = grub_disk_read (disk, p->offset, p->index, sizeof (*entry), entry);
disk->partition = p;
return err;
}
grub_err_t
grub_gpt_part_label (grub_device_t device, char **label)
{
struct grub_gpt_partentry entry;
const grub_size_t name_len = ARRAY_SIZE (entry.name);
const grub_size_t label_len = name_len * GRUB_MAX_UTF8_PER_UTF16 + 1;
grub_size_t i;
grub_uint8_t *end;
if (grub_gpt_device_partentry (device, &entry))
return grub_errno;
*label = grub_malloc (label_len);
if (!*label)
return grub_errno;
for (i = 0; i < name_len; i++)
entry.name[i] = grub_le_to_cpu16 (entry.name[i]);
end = grub_utf16_to_utf8 ((grub_uint8_t *) *label, entry.name, name_len);
*end = '\0';
return GRUB_ERR_NONE;
}
grub_err_t
grub_gpt_part_uuid (grub_device_t device, char **uuid)
{
struct grub_gpt_partentry entry;
if (grub_gpt_device_partentry (device, &entry))
return grub_errno;
*uuid = grub_gpt_guid_to_str (&entry.guid);
if (!*uuid)
return grub_errno;
return GRUB_ERR_NONE;
}
static struct grub_gpt_header *
grub_gpt_get_header (grub_gpt_t gpt)
{
if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID)
return &gpt->primary;
else if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID)
return &gpt->backup;
grub_error (GRUB_ERR_BUG, "No valid GPT header");
return NULL;
}
grub_err_t
grub_gpt_disk_uuid (grub_device_t device, char **uuid)
{
struct grub_gpt_header *header;
grub_gpt_t gpt = grub_gpt_read (device->disk);
if (!gpt)
goto done;
header = grub_gpt_get_header (gpt);
if (!header)
goto done;
*uuid = grub_gpt_guid_to_str (&header->guid);
done:
grub_gpt_free (gpt);
return grub_errno;
}
static grub_uint64_t
grub_gpt_size_to_sectors (grub_gpt_t gpt, grub_size_t size)
{
unsigned int sector_size;
grub_uint64_t sectors;
sector_size = 1U << gpt->log_sector_size;
sectors = size / sector_size;
if (size % sector_size)
sectors++;
return sectors;
}
/* Copied from grub-core/kern/disk_common.c grub_disk_adjust_range so we can
* avoid attempting to use disk->total_sectors when GRUB won't let us.
* TODO: Why is disk->total_sectors not set to GRUB_DISK_SIZE_UNKNOWN? */
static int
grub_gpt_disk_size_valid (grub_disk_t disk)
{
grub_disk_addr_t total_sectors;
/* Transform total_sectors to number of 512B blocks. */
total_sectors = disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
/* Some drivers have problems with disks above reasonable.
Treat unknown as 1EiB disk. While on it, clamp the size to 1EiB.
Just one condition is enough since GRUB_DISK_UNKNOWN_SIZE << ls is always
above 9EiB.
*/
if (total_sectors > (1ULL << 51))
return 0;
return 1;
}
static void
grub_gpt_lecrc32 (grub_uint32_t *crc, const void *data, grub_size_t len)
{
grub_uint32_t crc32_val;
grub_crypto_hash (GRUB_MD_CRC32, &crc32_val, data, len);
/* GRUB_MD_CRC32 always uses big endian, gpt is always little. */
*crc = grub_swap_bytes32 (crc32_val);
}
static void
grub_gpt_header_lecrc32 (grub_uint32_t *crc, struct grub_gpt_header *header)
{
grub_uint32_t old, new;
/* crc32 must be computed with the field cleared. */
old = header->crc32;
header->crc32 = 0;
grub_gpt_lecrc32 (&new, header, sizeof (*header));
header->crc32 = old;
*crc = new;
}
/* Make sure the MBR is a protective MBR and not a normal MBR. */
grub_err_t
grub_gpt_pmbr_check (struct grub_msdos_partition_mbr *mbr)
{
unsigned int i;
if (mbr->signature !=
grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE))
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid MBR signature");
for (i = 0; i < sizeof (mbr->entries); i++)
if (mbr->entries[i].type == GRUB_PC_PARTITION_TYPE_GPT_DISK)
return GRUB_ERR_NONE;
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid protective MBR");
}
static grub_uint64_t
grub_gpt_entries_size (struct grub_gpt_header *gpt)
{
return (grub_uint64_t) grub_le_to_cpu32 (gpt->maxpart) *
(grub_uint64_t) grub_le_to_cpu32 (gpt->partentry_size);
}
static grub_uint64_t
grub_gpt_entries_sectors (struct grub_gpt_header *gpt,
unsigned int log_sector_size)
{
grub_uint64_t sector_bytes, entries_bytes;
sector_bytes = 1ULL << log_sector_size;
entries_bytes = grub_gpt_entries_size (gpt);
return grub_divmod64(entries_bytes + sector_bytes - 1, sector_bytes, NULL);
}
static int
is_pow2 (grub_uint32_t n)
{
return (n & (n - 1)) == 0;
}
grub_err_t
grub_gpt_header_check (struct grub_gpt_header *gpt,
unsigned int log_sector_size)
{
grub_uint32_t crc = 0, size;
grub_uint64_t start, end;
if (grub_memcmp (gpt->magic, grub_gpt_magic, sizeof (grub_gpt_magic)) != 0)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT signature");
if (gpt->version != GRUB_GPT_HEADER_VERSION)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "unknown GPT version");
grub_gpt_header_lecrc32 (&crc, gpt);
if (gpt->crc32 != crc)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT header crc32");
/* The header size "must be greater than or equal to 92 and must be less
* than or equal to the logical block size." */
size = grub_le_to_cpu32 (gpt->headersize);
if (size < 92U || size > (1U << log_sector_size))
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT header size");
/* The partition entry size must be "a value of 128*(2^n) where n is an
* integer greater than or equal to zero (e.g., 128, 256, 512, etc.)." */
size = grub_le_to_cpu32 (gpt->partentry_size);
if (size < 128U || size % 128U || !is_pow2 (size / 128U))
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT entry size");
/* The minimum entries table size is specified in terms of bytes,
* regardless of how large the individual entry size is. */
if (grub_gpt_entries_size (gpt) < GRUB_GPT_DEFAULT_ENTRIES_SIZE)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT entry table size");
/* And of course there better be some space for partitions! */
start = grub_le_to_cpu64 (gpt->start);
end = grub_le_to_cpu64 (gpt->end);
if (start > end)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid usable sectors");
return GRUB_ERR_NONE;
}
static int
grub_gpt_headers_equal (grub_gpt_t gpt)
{
/* Assume headers passed grub_gpt_header_check so skip magic and version.
* Individual fields must be checked instead of just using memcmp because
* crc32, header, alternate, and partitions will all normally differ. */
if (gpt->primary.headersize != gpt->backup.headersize ||
gpt->primary.header_lba != gpt->backup.alternate_lba ||
gpt->primary.alternate_lba != gpt->backup.header_lba ||
gpt->primary.start != gpt->backup.start ||
gpt->primary.end != gpt->backup.end ||
gpt->primary.maxpart != gpt->backup.maxpart ||
gpt->primary.partentry_size != gpt->backup.partentry_size ||
gpt->primary.partentry_crc32 != gpt->backup.partentry_crc32)
return 0;
return grub_memcmp(&gpt->primary.guid, &gpt->backup.guid,
sizeof(grub_gpt_part_guid_t)) == 0;
}
static grub_err_t
grub_gpt_check_primary (grub_gpt_t gpt)
{
grub_uint64_t backup, primary, entries, entries_len, start, end;
primary = grub_le_to_cpu64 (gpt->primary.header_lba);
backup = grub_le_to_cpu64 (gpt->primary.alternate_lba);
entries = grub_le_to_cpu64 (gpt->primary.partitions);
entries_len = grub_gpt_entries_sectors(&gpt->primary, gpt->log_sector_size);
start = grub_le_to_cpu64 (gpt->primary.start);
end = grub_le_to_cpu64 (gpt->primary.end);
grub_dprintf ("gpt", "Primary GPT layout:\n"
"primary header = 0x%llx backup header = 0x%llx\n"
"entries location = 0x%llx length = 0x%llx\n"
"first usable = 0x%llx last usable = 0x%llx\n",
(unsigned long long) primary,
(unsigned long long) backup,
(unsigned long long) entries,
(unsigned long long) entries_len,
(unsigned long long) start,
(unsigned long long) end);
if (grub_gpt_header_check (&gpt->primary, gpt->log_sector_size))
return grub_errno;
if (primary != 1)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid primary GPT LBA");
if (entries <= 1 || entries+entries_len > start)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid entries location");
if (backup <= end)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid backup GPT LBA");
return GRUB_ERR_NONE;
}
static grub_err_t
grub_gpt_check_backup (grub_gpt_t gpt)
{
grub_uint64_t backup, primary, entries, entries_len, start, end;
backup = grub_le_to_cpu64 (gpt->backup.header_lba);
primary = grub_le_to_cpu64 (gpt->backup.alternate_lba);
entries = grub_le_to_cpu64 (gpt->backup.partitions);
entries_len = grub_gpt_entries_sectors(&gpt->backup, gpt->log_sector_size);
start = grub_le_to_cpu64 (gpt->backup.start);
end = grub_le_to_cpu64 (gpt->backup.end);
grub_dprintf ("gpt", "Backup GPT layout:\n"
"primary header = 0x%llx backup header = 0x%llx\n"
"entries location = 0x%llx length = 0x%llx\n"
"first usable = 0x%llx last usable = 0x%llx\n",
(unsigned long long) primary,
(unsigned long long) backup,
(unsigned long long) entries,
(unsigned long long) entries_len,
(unsigned long long) start,
(unsigned long long) end);
if (grub_gpt_header_check (&gpt->backup, gpt->log_sector_size))
return grub_errno;
if (primary != 1)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid primary GPT LBA");
if (entries <= end || entries+entries_len > backup)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid entries location");
if (backup <= end)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid backup GPT LBA");
/* If both primary and backup are valid but differ prefer the primary. */
if ((gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) &&
!grub_gpt_headers_equal (gpt))
return grub_error (GRUB_ERR_BAD_PART_TABLE, "backup GPT out of sync");
return GRUB_ERR_NONE;
}
static grub_err_t
grub_gpt_read_primary (grub_disk_t disk, grub_gpt_t gpt)
{
grub_disk_addr_t addr;
/* TODO: The gpt partmap module searches for the primary header instead
* of relying on the disk's sector size. For now trust the disk driver
* but eventually this code should match the existing behavior. */
gpt->log_sector_size = disk->log_sector_size;
grub_dprintf ("gpt", "reading primary GPT from sector 0x1\n");
addr = grub_gpt_sector_to_addr (gpt, 1);
if (grub_disk_read (disk, addr, 0, sizeof (gpt->primary), &gpt->primary))
return grub_errno;
if (grub_gpt_check_primary (gpt))
return grub_errno;
gpt->status |= GRUB_GPT_PRIMARY_HEADER_VALID;
if (grub_gpt_read_entries (disk, gpt, &gpt->primary,
&gpt->entries, &gpt->entries_size))
return grub_errno;
gpt->status |= GRUB_GPT_PRIMARY_ENTRIES_VALID;
return GRUB_ERR_NONE;
}
static grub_err_t
grub_gpt_read_backup (grub_disk_t disk, grub_gpt_t gpt)
{
void *entries = NULL;
grub_size_t entries_size;
grub_uint64_t sector;
grub_disk_addr_t addr;
/* Assumes gpt->log_sector_size == disk->log_sector_size */
if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID)
{
sector = grub_le_to_cpu64 (gpt->primary.alternate_lba);
if (grub_gpt_disk_size_valid (disk) && sector >= disk->total_sectors)
return grub_error (GRUB_ERR_OUT_OF_RANGE,
"backup GPT located at 0x%llx, "
"beyond last disk sector at 0x%llx",
(unsigned long long) sector,
(unsigned long long) disk->total_sectors - 1);
}
else if (grub_gpt_disk_size_valid (disk))
sector = disk->total_sectors - 1;
else
return grub_error (GRUB_ERR_OUT_OF_RANGE,
"size of disk unknown, cannot locate backup GPT");
grub_dprintf ("gpt", "reading backup GPT from sector 0x%llx\n",
(unsigned long long) sector);
addr = grub_gpt_sector_to_addr (gpt, sector);
if (grub_disk_read (disk, addr, 0, sizeof (gpt->backup), &gpt->backup))
return grub_errno;
if (grub_gpt_check_backup (gpt))
return grub_errno;
/* Ensure the backup header thinks it is located where we found it. */
if (grub_le_to_cpu64 (gpt->backup.header_lba) != sector)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid backup GPT LBA");
gpt->status |= GRUB_GPT_BACKUP_HEADER_VALID;
if (grub_gpt_read_entries (disk, gpt, &gpt->backup,
&entries, &entries_size))
return grub_errno;
if (gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID)
{
if (entries_size != gpt->entries_size ||
grub_memcmp (entries, gpt->entries, entries_size) != 0)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "backup GPT out of sync");
grub_free (entries);
}
else
{
gpt->entries = entries;
gpt->entries_size = entries_size;
}
gpt->status |= GRUB_GPT_BACKUP_ENTRIES_VALID;
return GRUB_ERR_NONE;
}
static grub_err_t
grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt,
struct grub_gpt_header *header,
void **ret_entries,
grub_size_t *ret_entries_size)
{
void *entries = NULL;
grub_uint32_t count, size, crc;
grub_uint64_t sector;
grub_disk_addr_t addr;
grub_size_t entries_size;
/* Grub doesn't include calloc, hence the manual overflow check. */
count = grub_le_to_cpu32 (header->maxpart);
size = grub_le_to_cpu32 (header->partentry_size);
entries_size = count *size;
if (size && entries_size / size != count)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
goto fail;
}
/* Double check that the header was validated properly. */
if (entries_size < GRUB_GPT_DEFAULT_ENTRIES_SIZE)
return grub_error (GRUB_ERR_BUG, "invalid GPT entries table size");
entries = grub_malloc (entries_size);
if (!entries)
goto fail;
sector = grub_le_to_cpu64 (header->partitions);
grub_dprintf ("gpt", "reading GPT %lu entries from sector 0x%llx\n",
(unsigned long) count,
(unsigned long long) sector);
addr = grub_gpt_sector_to_addr (gpt, sector);
if (grub_disk_read (disk, addr, 0, entries_size, entries))
goto fail;
grub_gpt_lecrc32 (&crc, entries, entries_size);
if (crc != header->partentry_crc32)
{
grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT entry crc32");
goto fail;
}
*ret_entries = entries;
*ret_entries_size = entries_size;
return GRUB_ERR_NONE;
fail:
grub_free (entries);
return grub_errno;
}
grub_gpt_t
grub_gpt_read (grub_disk_t disk)
{
grub_gpt_t gpt;
grub_dprintf ("gpt", "reading GPT from %s\n", disk->name);
gpt = grub_zalloc (sizeof (*gpt));
if (!gpt)
goto fail;
if (grub_disk_read (disk, 0, 0, sizeof (gpt->mbr), &gpt->mbr))
goto fail;
/* Check the MBR but errors aren't reported beyond the status bit. */
if (grub_gpt_pmbr_check (&gpt->mbr))
grub_errno = GRUB_ERR_NONE;
else
gpt->status |= GRUB_GPT_PROTECTIVE_MBR;
/* If both the primary and backup fail report the primary's error. */
if (grub_gpt_read_primary (disk, gpt))
{
grub_error_push ();
grub_gpt_read_backup (disk, gpt);
grub_error_pop ();
}
else
grub_gpt_read_backup (disk, gpt);
/* If either succeeded clear any possible error from the other. */
if (grub_gpt_primary_valid (gpt) || grub_gpt_backup_valid (gpt))
grub_errno = GRUB_ERR_NONE;
else
goto fail;
return gpt;
fail:
grub_gpt_free (gpt);
return NULL;
}
struct grub_gpt_partentry *
grub_gpt_get_partentry (grub_gpt_t gpt, grub_uint32_t n)
{
struct grub_gpt_header *header;
grub_size_t offset;
header = grub_gpt_get_header (gpt);
if (!header)
return NULL;
if (n >= grub_le_to_cpu32 (header->maxpart))
return NULL;
offset = (grub_size_t) grub_le_to_cpu32 (header->partentry_size) * n;
return (struct grub_gpt_partentry *) ((char *) gpt->entries + offset);
}
grub_err_t
grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt)
{
/* Skip if there is nothing to do. */
if (grub_gpt_both_valid (gpt))
return GRUB_ERR_NONE;
grub_dprintf ("gpt", "repairing GPT for %s\n", disk->name);
if (disk->log_sector_size != gpt->log_sector_size)
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"GPT sector size must match disk sector size");
if (grub_gpt_primary_valid (gpt))
{
grub_uint64_t backup_header;
grub_dprintf ("gpt", "primary GPT is valid\n");
/* Relocate backup to end if disk if the disk has grown. */
backup_header = grub_le_to_cpu64 (gpt->primary.alternate_lba);
if (grub_gpt_disk_size_valid (disk) &&
disk->total_sectors - 1 > backup_header)
{
backup_header = disk->total_sectors - 1;
grub_dprintf ("gpt", "backup GPT header relocated to 0x%llx\n",
(unsigned long long) backup_header);
gpt->primary.alternate_lba = grub_cpu_to_le64 (backup_header);
}
grub_memcpy (&gpt->backup, &gpt->primary, sizeof (gpt->backup));
gpt->backup.header_lba = gpt->primary.alternate_lba;
gpt->backup.alternate_lba = gpt->primary.header_lba;
gpt->backup.partitions = grub_cpu_to_le64 (backup_header -
grub_gpt_size_to_sectors (gpt, gpt->entries_size));
}
else if (grub_gpt_backup_valid (gpt))
{
grub_dprintf ("gpt", "backup GPT is valid\n");
grub_memcpy (&gpt->primary, &gpt->backup, sizeof (gpt->primary));
gpt->primary.header_lba = gpt->backup.alternate_lba;
gpt->primary.alternate_lba = gpt->backup.header_lba;
gpt->primary.partitions = grub_cpu_to_le64_compile_time (2);
}
else
return grub_error (GRUB_ERR_BUG, "No valid GPT");
if (grub_gpt_update (gpt))
return grub_errno;
grub_dprintf ("gpt", "repairing GPT for %s successful\n", disk->name);
return GRUB_ERR_NONE;
}
grub_err_t
grub_gpt_update (grub_gpt_t gpt)
{
grub_uint32_t crc;
/* Clear status bits, require revalidation of everything. */
gpt->status &= ~(GRUB_GPT_PRIMARY_HEADER_VALID |
GRUB_GPT_PRIMARY_ENTRIES_VALID |
GRUB_GPT_BACKUP_HEADER_VALID |
GRUB_GPT_BACKUP_ENTRIES_VALID);
/* Writing headers larger than our header structure are unsupported. */
gpt->primary.headersize =
grub_cpu_to_le32_compile_time (sizeof (gpt->primary));
gpt->backup.headersize =
grub_cpu_to_le32_compile_time (sizeof (gpt->backup));
grub_gpt_lecrc32 (&crc, gpt->entries, gpt->entries_size);
gpt->primary.partentry_crc32 = crc;
gpt->backup.partentry_crc32 = crc;
grub_gpt_header_lecrc32 (&gpt->primary.crc32, &gpt->primary);
grub_gpt_header_lecrc32 (&gpt->backup.crc32, &gpt->backup);
if (grub_gpt_check_primary (gpt))
{
grub_error_push ();
return grub_error (GRUB_ERR_BUG, "Generated invalid GPT primary header");
}
gpt->status |= (GRUB_GPT_PRIMARY_HEADER_VALID |
GRUB_GPT_PRIMARY_ENTRIES_VALID);
if (grub_gpt_check_backup (gpt))
{
grub_error_push ();
return grub_error (GRUB_ERR_BUG, "Generated invalid GPT backup header");
}
gpt->status |= (GRUB_GPT_BACKUP_HEADER_VALID |
GRUB_GPT_BACKUP_ENTRIES_VALID);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_gpt_write_table (grub_disk_t disk, grub_gpt_t gpt,
struct grub_gpt_header *header)
{
grub_disk_addr_t addr;
if (grub_le_to_cpu32 (header->headersize) != sizeof (*header))
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"Header size is %u, must be %u",
grub_le_to_cpu32 (header->headersize),
sizeof (*header));
addr = grub_gpt_sector_to_addr (gpt, grub_le_to_cpu64 (header->header_lba));
if (addr == 0)
return grub_error (GRUB_ERR_BUG,
"Refusing to write GPT header to address 0x0");
if (grub_disk_write (disk, addr, 0, sizeof (*header), header))
return grub_errno;
addr = grub_gpt_sector_to_addr (gpt, grub_le_to_cpu64 (header->partitions));
if (addr < 2)
return grub_error (GRUB_ERR_BUG,
"Refusing to write GPT entries to address 0x%llx",
(unsigned long long) addr);
if (grub_disk_write (disk, addr, 0, gpt->entries_size, gpt->entries))
return grub_errno;
return GRUB_ERR_NONE;
}
grub_err_t
grub_gpt_write (grub_disk_t disk, grub_gpt_t gpt)
{
grub_uint64_t backup_header;
/* TODO: update/repair protective MBRs too. */
if (!grub_gpt_both_valid (gpt))
return grub_error (GRUB_ERR_BAD_PART_TABLE, "Invalid GPT data");
/* Write the backup GPT first so if writing fails the update is aborted
* and the primary is left intact. However if the backup location is
* inaccessible we have to just skip and hope for the best, the backup
* will need to be repaired in the OS. */
backup_header = grub_le_to_cpu64 (gpt->backup.header_lba);
if (grub_gpt_disk_size_valid (disk) &&
backup_header >= disk->total_sectors)
{
grub_printf ("warning: backup GPT located at 0x%llx, "
"beyond last disk sector at 0x%llx\n",
(unsigned long long) backup_header,
(unsigned long long) disk->total_sectors - 1);
grub_printf ("warning: only writing primary GPT, "
"the backup GPT must be repaired from the OS\n");
}
else
{
grub_dprintf ("gpt", "writing backup GPT to %s\n", disk->name);
if (grub_gpt_write_table (disk, gpt, &gpt->backup))
return grub_errno;
}
grub_dprintf ("gpt", "writing primary GPT to %s\n", disk->name);
if (grub_gpt_write_table (disk, gpt, &gpt->primary))
return grub_errno;
return GRUB_ERR_NONE;
}
void
grub_gpt_free (grub_gpt_t gpt)
{
if (!gpt)
return;
grub_free (gpt->entries);
grub_free (gpt);
}

View file

@ -34,6 +34,8 @@
#include <grub/lib/cmdline.h>
#include <grub/verify.h>
#include <grub/verity-hash.h>
GRUB_MOD_LICENSE ("GPLv3+");
static grub_dl_t my_mod;
@ -333,7 +335,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE);
cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE)
+ VERITY_CMDLINE_LENGTH;
linux_args = grub_malloc (cmdline_size);
if (!linux_args)
{
@ -350,6 +353,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
if (grub_errno == GRUB_ERR_NONE)
{
grub_pass_verity_hash (kernel_addr, linux_args, cmdline_size);
grub_loader_set (grub_linux_boot, grub_linux_unload, 0);
loaded = 1;
}

View file

@ -0,0 +1,363 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2012 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/loader.h>
#include <grub/file.h>
#include <grub/err.h>
#include <grub/types.h>
#include <grub/mm.h>
#include <grub/cpu/linux.h>
#include <grub/command.h>
#include <grub/i18n.h>
#include <grub/lib/cmdline.h>
#include <grub/efi/efi.h>
#include <grub/tpm.h>
#include <grub/verity-hash.h>
GRUB_MOD_LICENSE ("GPLv3+");
static grub_dl_t my_mod;
static int loaded;
static void *kernel_mem;
static grub_uint64_t kernel_size;
static grub_uint8_t *initrd_mem;
static grub_uint32_t handover_offset;
struct linux_kernel_params *params;
static char *linux_cmdline;
#define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12)
#define SHIM_LOCK_GUID \
{ 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }
struct grub_efi_shim_lock
{
grub_efi_status_t (*verify) (void *buffer, grub_uint32_t size);
};
typedef struct grub_efi_shim_lock grub_efi_shim_lock_t;
static grub_efi_boolean_t
grub_linuxefi_secure_validate (void *data, grub_uint32_t size)
{
grub_efi_guid_t guid = SHIM_LOCK_GUID;
grub_efi_shim_lock_t *shim_lock;
shim_lock = grub_efi_locate_protocol(&guid, NULL);
if (!shim_lock) {
if (grub_efi_secure_boot())
return 0;
else
return 1;
}
if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS)
return 1;
return 0;
}
typedef void(*handover_func)(void *, grub_efi_system_table_t *, struct linux_kernel_params *);
static grub_err_t
grub_linuxefi_boot (void)
{
handover_func hf;
int offset = 0;
#ifdef __x86_64__
offset = 512;
#endif
hf = (handover_func)((char *)kernel_mem + handover_offset + offset);
asm volatile ("cli");
hf (grub_efi_image_handle, grub_efi_system_table, params);
/* Not reached */
return GRUB_ERR_NONE;
}
static grub_err_t
grub_linuxefi_unload (void)
{
grub_dl_unref (my_mod);
loaded = 0;
if (initrd_mem)
grub_efi_free_pages((grub_efi_physical_address_t)initrd_mem, BYTES_TO_PAGES(params->ramdisk_size));
if (linux_cmdline)
grub_efi_free_pages((grub_efi_physical_address_t)linux_cmdline, BYTES_TO_PAGES(params->cmdline_size + 1));
if (kernel_mem)
grub_efi_free_pages((grub_efi_physical_address_t)kernel_mem, BYTES_TO_PAGES(kernel_size));
if (params)
grub_efi_free_pages((grub_efi_physical_address_t)params, BYTES_TO_PAGES(16384));
return GRUB_ERR_NONE;
}
static grub_err_t
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t *files = 0;
int i, nfiles = 0;
grub_size_t size = 0;
grub_uint8_t *ptr;
if (argc == 0)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
goto fail;
}
if (!loaded)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
goto fail;
}
files = grub_zalloc (argc * sizeof (files[0]));
if (!files)
goto fail;
for (i = 0; i < argc; i++)
{
grub_file_filter_disable_compression ();
files[i] = grub_file_open (argv[i]);
if (! files[i])
goto fail;
nfiles++;
size += ALIGN_UP (grub_file_size (files[i]), 4);
}
initrd_mem = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(size));
if (!initrd_mem)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd"));
goto fail;
}
params->ramdisk_size = size;
params->ramdisk_image = (grub_uint32_t)(grub_uint64_t) initrd_mem;
ptr = initrd_mem;
for (i = 0; i < nfiles; i++)
{
grub_ssize_t cursize = grub_file_size (files[i]);
if (grub_file_read (files[i], ptr, cursize) != cursize)
{
if (!grub_errno)
grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
argv[i]);
goto fail;
}
// TODO figure out the GRUB_VERIFY_ equivalent for this one
//grub_tpm_measure (ptr, cursize, GRUB_BINARY_PCR, "Initrd");
//grub_print_error();
ptr += cursize;
grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4));
ptr += ALIGN_UP_OVERHEAD (cursize, 4);
}
params->ramdisk_size = size;
fail:
for (i = 0; i < nfiles; i++)
grub_file_close (files[i]);
grub_free (files);
if (initrd_mem && grub_errno)
grub_efi_free_pages((grub_efi_physical_address_t)initrd_mem, BYTES_TO_PAGES(size));
return grub_errno;
}
static grub_err_t
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t file = 0;
struct linux_kernel_header lh;
grub_ssize_t len, start, filelen;
void *kernel = NULL;
grub_dl_ref (my_mod);
if (argc == 0)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
goto fail;
}
file = grub_file_open (argv[0]);
if (! file)
goto fail;
filelen = grub_file_size (file);
kernel = grub_malloc(filelen);
if (!kernel)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer"));
goto fail;
}
if (grub_file_read (file, kernel, filelen) != filelen)
{
grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), argv[0]);
goto fail;
}
// TODO figure out the GRUB_VERIFY_ equivalent for this one
//grub_tpm_measure (kernel, filelen, GRUB_BINARY_PCR, "Kernel");
//grub_print_error();
if (! grub_linuxefi_secure_validate (kernel, filelen))
{
grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]);
grub_free (kernel);
goto fail;
}
params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384));
if (! params)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters");
goto fail;
}
grub_memset (params, 0, 16384);
grub_memcpy (&lh, kernel, sizeof (lh));
if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
{
grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number"));
goto fail;
}
if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
{
grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors"));
goto fail;
}
if (lh.version < grub_cpu_to_le16 (0x020b))
{
grub_error (GRUB_ERR_BAD_OS, N_("kernel too old"));
goto fail;
}
if (!lh.handover_offset)
{
grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover"));
goto fail;
}
linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff,
BYTES_TO_PAGES(lh.cmdline_size + 1));
if (!linux_cmdline)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline"));
goto fail;
}
grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
grub_create_loader_cmdline (argc, argv,
linux_cmdline + sizeof (LINUX_IMAGE) - 1,
lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1));
grub_pass_verity_hash(&lh, linux_cmdline, lh.cmdline_size);
lh.cmd_line_ptr = (grub_uint32_t)(grub_uint64_t)linux_cmdline;
handover_offset = lh.handover_offset;
start = (lh.setup_sects + 1) * 512;
len = grub_file_size(file) - start;
kernel_mem = grub_efi_allocate_pages(lh.pref_address,
BYTES_TO_PAGES(lh.init_size));
if (!kernel_mem)
kernel_mem = grub_efi_allocate_pages_max(0x3fffffff,
BYTES_TO_PAGES(lh.init_size));
if (!kernel_mem)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel"));
goto fail;
}
grub_memcpy (kernel_mem, (char *)kernel + start, len);
grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0);
loaded=1;
lh.code32_start = (grub_uint32_t)(grub_uint64_t) kernel_mem;
grub_memcpy (params, &lh, 2 * 512);
params->type_of_loader = 0x21;
fail:
if (file)
grub_file_close (file);
grub_free (kernel);
if (grub_errno != GRUB_ERR_NONE)
{
grub_dl_unref (my_mod);
loaded = 0;
}
if (linux_cmdline && !loaded)
grub_efi_free_pages((grub_efi_physical_address_t)linux_cmdline, BYTES_TO_PAGES(lh.cmdline_size + 1));
if (kernel_mem && !loaded)
grub_efi_free_pages((grub_efi_physical_address_t)kernel_mem, BYTES_TO_PAGES(kernel_size));
if (params && !loaded)
grub_efi_free_pages((grub_efi_physical_address_t)params, BYTES_TO_PAGES(16384));
return grub_errno;
}
static grub_command_t cmd_linux, cmd_initrd;
GRUB_MOD_INIT(linuxefi)
{
cmd_linux =
grub_register_command ("linuxefi", grub_cmd_linux,
0, N_("Load Linux."));
cmd_initrd =
grub_register_command ("initrdefi", grub_cmd_initrd,
0, N_("Load initrd."));
my_mod = mod;
}
GRUB_MOD_FINI(linuxefi)
{
grub_unregister_command (cmd_linux);
grub_unregister_command (cmd_initrd);
}

View file

@ -36,6 +36,9 @@
#include <grub/lib/cmdline.h>
#include <grub/linux.h>
#include <grub/machine/kernel.h>
#include <grub/tpm.h>
#include <grub/verity-hash.h>
GRUB_MOD_LICENSE ("GPLv3+");
@ -642,12 +645,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
grub_file_t file = 0;
struct linux_i386_kernel_header lh;
grub_uint8_t setup_sects;
grub_size_t real_size, prot_size, prot_file_size;
grub_size_t real_size, prot_size, prot_file_size, kernel_offset;
grub_ssize_t len;
int i;
grub_size_t align, min_align;
int relocatable;
grub_uint64_t preferred_address = GRUB_LINUX_BZIMAGE_ADDR;
grub_uint8_t *kernel = NULL;
grub_dl_ref (my_mod);
@ -661,7 +665,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
if (! file)
goto fail;
if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
len = grub_file_size (file);
kernel = grub_malloc (len);
if (!kernel)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer"));
goto fail;
}
if (grub_file_read (file, kernel, len) != len)
{
if (!grub_errno)
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
@ -669,6 +681,14 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
goto fail;
}
// TODO figure out the GRUB_VERIFY_ equivalent for this one
//grub_tpm_measure (kernel, len, GRUB_BINARY_PCR, "Kernel");
//grub_print_error();
grub_memcpy (&lh, kernel, sizeof (lh));
kernel_offset = sizeof (lh);
if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55))
{
grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
@ -789,6 +809,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
argv[0]);
goto fail;
}
kernel_offset += len;
linux_params.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE;
@ -847,7 +868,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
/* The other parameters are filled when booting. */
grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
kernel_offset = real_size + GRUB_DISK_SECTOR_SIZE;
grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n",
(unsigned) real_size, (unsigned) prot_size);
@ -1000,10 +1021,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
goto fail;
}
grub_pass_verity_hash(&lh, linux_cmdline, maximal_cmdline_size);
len = prot_file_size;
if (grub_file_read (file, prot_mode_mem, len) != len && !grub_errno)
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
argv[0]);
grub_memcpy (prot_mode_mem, kernel + kernel_offset, len);
kernel_offset += len;
if (grub_errno == GRUB_ERR_NONE)
{
@ -1014,6 +1035,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
fail:
grub_free (kernel);
if (file)
grub_file_close (file);

View file

@ -36,6 +36,7 @@
#include <grub/net.h>
#include <grub/i18n.h>
#include <grub/lib/cmdline.h>
#include <grub/tpm.h>
#ifdef GRUB_MACHINE_EFI
#include <grub/efi/efi.h>
@ -173,6 +174,10 @@ grub_multiboot_load (grub_file_t file, const char *filename)
return grub_errno;
}
// TODO figure out the GRUB_VERIFY_ equivalent for this one
//grub_tpm_measure((unsigned char*)buffer, len, GRUB_BINARY_PCR, filename);
//grub_print_error();
header = find_header (buffer, len);
if (header == 0)

View file

@ -35,6 +35,7 @@
#include <grub/i386/floppy.h>
#include <grub/lib/cmdline.h>
#include <grub/linux.h>
#include <grub/tpm.h>
GRUB_MOD_LICENSE ("GPLv3+");
@ -123,13 +124,14 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
grub_file_t file = 0;
struct linux_i386_kernel_header lh;
grub_uint8_t setup_sects;
grub_size_t real_size;
grub_size_t real_size, kernel_offset = 0;
grub_ssize_t len;
int i;
char *grub_linux_prot_chunk;
int grub_linux_is_bzimage;
grub_addr_t grub_linux_prot_target;
grub_err_t err;
grub_uint8_t *kernel = NULL;
grub_dl_ref (my_mod);
@ -143,7 +145,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
if (! file)
goto fail;
if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
len = grub_file_size (file);
kernel = grub_malloc (len);
if (!kernel)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer"));
goto fail;
}
if (grub_file_read (file, kernel, len) != len)
{
if (!grub_errno)
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
@ -151,6 +161,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
goto fail;
}
// TODO figure out the GRUB_VERIFY_ equivalent for this one
//grub_tpm_measure (kernel, len, GRUB_BINARY_PCR, "Kernel");
//grub_print_error();
grub_memcpy (&lh, kernel, sizeof (lh));
kernel_offset = sizeof (lh);
if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55))
{
grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
@ -314,13 +331,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
grub_memmove (grub_linux_real_chunk, &lh, sizeof (lh));
len = real_size + GRUB_DISK_SECTOR_SIZE - sizeof (lh);
if (grub_file_read (file, grub_linux_real_chunk + sizeof (lh), len) != len)
{
if (!grub_errno)
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
argv[0]);
goto fail;
}
grub_memcpy (grub_linux_real_chunk + sizeof (lh), kernel + kernel_offset,
len);
kernel_offset += len;
if (lh.header != grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_MAGIC_SIGNATURE)
|| grub_le_to_cpu16 (lh.version) < 0x0200)
@ -370,6 +383,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
fail:
grub_free (kernel);
if (file)
grub_file_close (file);

View file

@ -656,7 +656,9 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)),
if (!file)
return grub_errno;
elf = grub_xen_file (file);
elf = grub_xen_file_and_cmdline (file,
(char *) xen_state.next_start.cmd_line,
sizeof (xen_state.next_start.cmd_line) - 1);
if (!elf)
goto fail;

View file

@ -19,11 +19,20 @@
#include <grub/xen_file.h>
#include <grub/i386/linux.h>
#include <grub/misc.h>
#include <grub/verity-hash.h>
#define XZ_MAGIC "\3757zXZ\0"
grub_elf_t
grub_xen_file (grub_file_t file)
{
return grub_xen_file_and_cmdline (file, NULL, 0);
}
grub_elf_t
grub_xen_file_and_cmdline (grub_file_t file,
char *cmdline,
grub_size_t cmdline_max_len)
{
grub_elf_t elf;
struct linux_i386_kernel_header lh;
@ -64,6 +73,9 @@ grub_xen_file (grub_file_t file)
(unsigned long long) payload_offset,
(unsigned long long) lh.payload_length);
if (cmdline)
grub_pass_verity_hash (&lh, cmdline, cmdline_max_len);
grub_file_seek (file, payload_offset);
if (grub_file_read (file, &magic, sizeof (magic)) != sizeof (magic))

View file

@ -4,6 +4,7 @@
#include <grub/misc.h>
#include <grub/file.h>
#include <grub/mm.h>
#include <grub/tpm.h>
struct newc_head
{
@ -288,6 +289,10 @@ grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx,
grub_initrd_close (initrd_ctx);
return grub_errno;
}
// TODO figure out the GRUB_VERIFY_ equivalent for this one
//grub_tpm_measure (ptr, cursize, GRUB_BINARY_PCR, "Initrd");
//grub_print_error();
ptr += cursize;
}
if (newc)

View file

@ -50,6 +50,7 @@
#include <grub/video.h>
#include <grub/memory.h>
#include <grub/i18n.h>
#include <grub/tpm.h>
GRUB_MOD_LICENSE ("GPLv3+");
@ -437,6 +438,9 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
}
grub_file_close (file);
// TODO figure out the GRUB_VERIFY_ equivalent for this one
//grub_tpm_measure (module, size, GRUB_BINARY_PCR, argv[0]);
//grub_print_error();
return GRUB_ERR_NONE;
}

View file

@ -36,6 +36,7 @@
#include <grub/i18n.h>
#include <grub/net.h>
#include <grub/lib/cmdline.h>
#include <grub/tpm.h>
#if defined (GRUB_MACHINE_EFI)
#include <grub/efi/efi.h>
@ -131,6 +132,10 @@ grub_multiboot2_load (grub_file_t file, const char *filename)
COMPILE_TIME_ASSERT (MULTIBOOT_HEADER_ALIGN % 4 == 0);
// TODO figure out the GRUB_VERIFY_ equivalent for this one
//grub_tpm_measure ((unsigned char *)mld.buffer, len, GRUB_BINARY_PCR, filename);
//grub_print_error();
header = find_header (mld.buffer, len);
if (header == 0)

View file

@ -25,6 +25,20 @@
#include <grub/net/udp.h>
#include <grub/datetime.h>
#if !defined(GRUB_MACHINE_EFI) && (defined(__i386__) || defined(__x86_64__))
#define GRUB_NET_BOOTP_ARCH 0x0000
#elif defined(GRUB_MACHINE_EFI) && defined(__x86_64__)
#define GRUB_NET_BOOTP_ARCH 0x0007
#elif defined(GRUB_MACHINE_EFI) && defined(__aarch64__)
#define GRUB_NET_BOOTP_ARCH 0x000B
#else
#error "unknown bootp architecture"
#endif
static grub_uint8_t grub_userclass[] = {0x4D, 0x06, 0x05, 'G', 'R', 'U', 'B', '2'};
static grub_uint8_t grub_dhcpdiscover[] = {0x35, 0x01, 0x01};
static grub_uint8_t grub_dhcptime[] = {0x33, 0x04, 0x00, 0x00, 0x0e, 0x10};
struct grub_dhcp_discover_options
{
grub_uint8_t magic[4];
@ -90,11 +104,6 @@ enum
/* Max timeout when waiting for BOOTP/DHCP reply */
#define GRUB_DHCP_MAX_PACKET_TIMEOUT 32
#define GRUB_BOOTP_MAX_OPTIONS_SIZE 64
/* Max timeout when waiting for BOOTP/DHCP reply */
#define GRUB_DHCP_MAX_PACKET_TIMEOUT 32
static const void *
find_dhcp_option (const struct grub_net_bootp_packet *bp, grub_size_t size,
grub_uint8_t opt_code, grub_uint8_t *opt_len)
@ -429,6 +438,7 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface)
struct udphdr *udph;
grub_net_network_level_address_t target;
grub_net_link_level_address_t ll_target;
grub_uint8_t *offset;
static struct grub_dhcp_discover_options discover_options =
{
@ -488,19 +498,31 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface)
COMPILE_TIME_ASSERT (sizeof (discover_options) <= GRUB_BOOTP_MAX_OPTIONS_SIZE);
COMPILE_TIME_ASSERT (sizeof (request_options) <= GRUB_BOOTP_MAX_OPTIONS_SIZE);
nb = grub_netbuff_alloc (sizeof (*pack) + GRUB_BOOTP_MAX_OPTIONS_SIZE + 128);
nb = grub_netbuff_alloc (sizeof (*pack) + sizeof(grub_userclass)
+ sizeof(grub_dhcpdiscover)
+ sizeof(grub_dhcptime)
+ GRUB_BOOTP_MAX_OPTIONS_SIZE + 128);
if (!nb)
return grub_errno;
err = grub_netbuff_reserve (nb, sizeof (*pack) + GRUB_BOOTP_MAX_OPTIONS_SIZE + 128);
err = grub_netbuff_reserve (nb, sizeof (*pack) + sizeof(grub_userclass)
+ sizeof(grub_dhcpdiscover)
+ sizeof(grub_dhcptime)
+ GRUB_BOOTP_MAX_OPTIONS_SIZE + 128);
if (err)
goto out;
err = grub_netbuff_push (nb, GRUB_BOOTP_MAX_OPTIONS_SIZE);
err = grub_netbuff_push (nb, sizeof(grub_userclass)
+ sizeof(grub_dhcpdiscover)
+ sizeof(grub_dhcptime)
+ GRUB_BOOTP_MAX_OPTIONS_SIZE);
if (err)
goto out;
grub_memset (nb->data, 0, GRUB_BOOTP_MAX_OPTIONS_SIZE);
grub_memset (nb->data, 0, sizeof(grub_userclass)
+ sizeof(grub_dhcpdiscover)
+ sizeof(grub_dhcptime)
+ GRUB_BOOTP_MAX_OPTIONS_SIZE);
if (!iface->srv_id)
{
grub_memcpy (nb->data, &discover_options, sizeof (discover_options));
@ -537,6 +559,22 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface)
pack->ident = iface->xid;
grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, 6);
offset = (grub_uint8_t *)&pack->vendor;
grub_memcpy (offset, grub_dhcpdiscover, sizeof(grub_dhcpdiscover));
offset += sizeof(grub_dhcpdiscover);
grub_memcpy (offset, grub_userclass, sizeof(grub_userclass));
offset += sizeof(grub_userclass);
grub_memcpy (offset, grub_dhcptime, sizeof(grub_dhcptime));
/* insert Client System Architecture (option 93) */
offset += sizeof(grub_dhcptime);
offset[0] = 93;
offset[1] = 2;
offset[2] = (GRUB_NET_BOOTP_ARCH >> 8);
offset[3] = (GRUB_NET_BOOTP_ARCH & 0xFF);
/* option terminator */
offset[4] = 255;
grub_netbuff_push (nb, sizeof (*udph));

View file

@ -285,8 +285,8 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)),
ptr++;
ptr += 4;
}
*data->addresses = grub_malloc (sizeof ((*data->addresses)[0])
* grub_be_to_cpu16 (head->ancount));
*data->addresses = grub_realloc (*data->addresses, sizeof ((*data->addresses)[0])
* (grub_be_to_cpu16 (head->ancount) + *data->naddresses));
if (!*data->addresses)
{
grub_errno = GRUB_ERR_NONE;

View file

@ -309,7 +309,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
{
http_data_t data = file->data;
grub_uint8_t *ptr;
int i;
int i, port;
struct grub_net_buff *nb;
grub_err_t err;
@ -390,8 +390,12 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
grub_netbuff_put (nb, 2);
grub_memcpy (ptr, "\r\n", 2);
if (file->device->net->port)
port = file->device->net->port;
else
port = HTTP_PORT;
data->sock = grub_net_tcp_open (file->device->net->server,
HTTP_PORT, http_receive,
port, http_receive,
http_err, http_err,
file);
if (!data->sock)

View file

@ -1261,7 +1261,7 @@ grub_net_open_real (const char *name)
grub_net_app_level_t proto;
const char *protname, *server;
grub_size_t protnamelen;
int try;
int try, port = 0;
if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0)
{
@ -1278,7 +1278,18 @@ grub_net_open_real (const char *name)
else
{
const char *comma;
char *colon;
comma = grub_strchr (name, ',');
if (!comma)
{
comma = grub_strchr (name, ';');
}
colon = grub_strchr (name, ':');
if (colon)
{
port = (int) grub_strtol(colon+1, NULL, 10);
*colon = '\0';
}
if (comma)
{
protnamelen = comma - name;
@ -1311,11 +1322,16 @@ grub_net_open_real (const char *name)
return NULL;
ret->protocol = proto;
ret->server = grub_strdup (server);
if (!ret->server)
{
ret->server = grub_strdup (server);
ret->port = port;
if (!ret->server)
{
grub_free (ret);
return NULL;
}
}
ret->fs = &grub_net_fs;
return ret;
}

View file

@ -27,6 +27,7 @@
#include <grub/normal.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
#include <grub/tpm.h>
#include <grub/verify.h>
/* Max digits for a char is 3 (0xFF is 255), similarly for an int it

View file

@ -314,6 +314,11 @@
{ 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
}
#define GRUB_EFI_SMBIOS3_TABLE_GUID \
{ 0xf2fd1544, 0x9794, 0x4a2c, \
{ 0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94 } \
}
#define GRUB_EFI_SAL_TABLE_GUID \
{ 0xeb9d2d32, 0x2d88, 0x11d3, \
{ 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \

View file

@ -46,7 +46,8 @@ void *
EXPORT_FUNC(grub_efi_allocate_fixed) (grub_efi_physical_address_t address,
grub_efi_uintn_t pages);
void *
EXPORT_FUNC(grub_efi_allocate_any_pages) (grub_efi_uintn_t pages);
EXPORT_FUNC(grub_efi_allocate_pages_max) (grub_efi_physical_address_t max,
grub_efi_uintn_t pages);
void EXPORT_FUNC(grub_efi_free_pages) (grub_efi_physical_address_t address,
grub_efi_uintn_t pages);
grub_efi_uintn_t EXPORT_FUNC(grub_efi_find_mmap_size) (void);
@ -82,6 +83,7 @@ EXPORT_FUNC (grub_efi_set_variable) (const char *var,
const grub_efi_guid_t *guid,
void *data,
grub_size_t datasize);
grub_efi_boolean_t EXPORT_FUNC (grub_efi_secure_boot) (void);
int
EXPORT_FUNC (grub_efi_compare_device_paths) (const grub_efi_device_path_t *dp1,
const grub_efi_device_path_t *dp2);

View file

@ -132,14 +132,12 @@ typedef struct tdEFI_TCG2_BOOT_SERVICE_CAPABILITY EFI_TCG2_BOOT_SERVICE_CAPABILI
typedef grub_efi_uint32_t TCG_PCRINDEX;
typedef grub_efi_uint32_t TCG_EVENTTYPE;
struct tdEFI_TCG2_EVENT_HEADER
{
typedef struct tdEFI_TCG2_EVENT_HEADER {
grub_efi_uint32_t HeaderSize;
grub_efi_uint16_t HeaderVersion;
TCG_PCRINDEX PCRIndex;
TCG_EVENTTYPE EventType;
} GRUB_PACKED;
typedef struct tdEFI_TCG2_EVENT_HEADER EFI_TCG2_EVENT_HEADER;
} GRUB_PACKED EFI_TCG2_EVENT_HEADER;
struct tdEFI_TCG2_EVENT
{
@ -161,7 +159,7 @@ struct grub_efi_tpm2_protocol
EventLogLocation,
grub_efi_physical_address_t *
EventLogLastEntry,
grub_efi_boolean_t * EventLogTruncated);
grub_efi_boolean_t *EventLogTruncated);
grub_efi_status_t (*hash_log_extend_event) (struct grub_efi_tpm2_protocol *
this, grub_efi_uint64_t Flags,
grub_efi_physical_address_t

View file

@ -21,6 +21,7 @@
#include <grub/types.h>
#include <grub/partition.h>
#include <grub/msdos_partition.h>
struct grub_gpt_part_guid
{
@ -31,24 +32,43 @@ struct grub_gpt_part_guid
} GRUB_PACKED;
typedef struct grub_gpt_part_guid grub_gpt_part_guid_t;
#define GRUB_GPT_PARTITION_TYPE_EMPTY \
{ 0x0, 0x0, 0x0, \
{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } \
/* Format the raw little-endian GUID as a newly allocated string. */
char * grub_gpt_guid_to_str (grub_gpt_part_guid_t *guid);
#define GRUB_GPT_GUID_INIT(a, b, c, d1, d2, d3, d4, d5, d6, d7, d8) \
{ \
grub_cpu_to_le32_compile_time (a), \
grub_cpu_to_le16_compile_time (b), \
grub_cpu_to_le16_compile_time (c), \
{ d1, d2, d3, d4, d5, d6, d7, d8 } \
}
#define GRUB_GPT_PARTITION_TYPE_EMPTY \
GRUB_GPT_GUID_INIT (0x0, 0x0, 0x0, \
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
#define GRUB_GPT_PARTITION_TYPE_EFI_SYSTEM \
GRUB_GPT_GUID_INIT (0xc12a7328, 0xf81f, 0x11d2, \
0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b)
#define GRUB_GPT_PARTITION_TYPE_BIOS_BOOT \
{ grub_cpu_to_le32_compile_time (0x21686148), \
grub_cpu_to_le16_compile_time (0x6449), \
grub_cpu_to_le16_compile_time (0x6e6f), \
{ 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } \
}
GRUB_GPT_GUID_INIT (0x21686148, 0x6449, 0x6e6f, \
0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49)
#define GRUB_GPT_PARTITION_TYPE_LDM \
{ grub_cpu_to_le32_compile_time (0x5808C8AAU),\
grub_cpu_to_le16_compile_time (0x7E8F), \
grub_cpu_to_le16_compile_time (0x42E0), \
{ 0x85, 0xD2, 0xE1, 0xE9, 0x04, 0x34, 0xCF, 0xB3 } \
}
GRUB_GPT_GUID_INIT (0x5808c8aa, 0x7e8f, 0x42e0, \
0x85, 0xd2, 0xe1, 0xe9, 0x04, 0x34, 0xcf, 0xb3)
#define GRUB_GPT_PARTITION_TYPE_USR_X86_64 \
GRUB_GPT_GUID_INIT (0x5dfbf5f4, 0x2848, 0x4bac, \
0xaa, 0x5e, 0x0d, 0x9a, 0x20, 0xb7, 0x45, 0xa6)
#define GRUB_GPT_HEADER_MAGIC \
{ 0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54 }
#define GRUB_GPT_HEADER_VERSION \
grub_cpu_to_le32_compile_time (0x00010000U)
struct grub_gpt_header
{
@ -57,11 +77,11 @@ struct grub_gpt_header
grub_uint32_t headersize;
grub_uint32_t crc32;
grub_uint32_t unused1;
grub_uint64_t primary;
grub_uint64_t backup;
grub_uint64_t header_lba;
grub_uint64_t alternate_lba;
grub_uint64_t start;
grub_uint64_t end;
grub_uint8_t guid[16];
grub_gpt_part_guid_t guid;
grub_uint64_t partitions;
grub_uint32_t maxpart;
grub_uint32_t partentry_size;
@ -75,13 +95,172 @@ struct grub_gpt_partentry
grub_uint64_t start;
grub_uint64_t end;
grub_uint64_t attrib;
char name[72];
grub_uint16_t name[36];
} GRUB_PACKED;
enum grub_gpt_part_attr_offset
{
/* Standard partition attribute bits defined by UEFI. */
GRUB_GPT_PART_ATTR_OFFSET_REQUIRED = 0,
GRUB_GPT_PART_ATTR_OFFSET_NO_BLOCK_IO_PROTOCOL = 1,
GRUB_GPT_PART_ATTR_OFFSET_LEGACY_BIOS_BOOTABLE = 2,
/* De facto standard attribute bits defined by Microsoft and reused by
* http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec */
GRUB_GPT_PART_ATTR_OFFSET_READ_ONLY = 60,
GRUB_GPT_PART_ATTR_OFFSET_NO_AUTO = 63,
/* Partition attributes for priority based selection,
* Currently only valid for PARTITION_TYPE_USR_X86_64.
* TRIES_LEFT and PRIORITY are 4 bit wide fields. */
GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_PRIORITY = 48,
GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_TRIES_LEFT = 52,
GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_SUCCESSFUL = 56,
};
/* Helpers for reading/writing partition attributes. */
static inline grub_uint64_t
grub_gpt_entry_attribute (struct grub_gpt_partentry *entry,
enum grub_gpt_part_attr_offset offset,
unsigned int bits)
{
grub_uint64_t attrib = grub_le_to_cpu64 (entry->attrib);
return (attrib >> offset) & ((1ULL << bits) - 1);
}
static inline void
grub_gpt_entry_set_attribute (struct grub_gpt_partentry *entry,
grub_uint64_t value,
enum grub_gpt_part_attr_offset offset,
unsigned int bits)
{
grub_uint64_t attrib, mask;
mask = (((1ULL << bits) - 1) << offset);
attrib = grub_le_to_cpu64 (entry->attrib) & ~mask;
attrib |= ((value << offset) & mask);
entry->attrib = grub_cpu_to_le64 (attrib);
}
/* Basic GPT partmap module. */
grub_err_t
grub_gpt_partition_map_iterate (grub_disk_t disk,
grub_partition_iterate_hook_t hook,
void *hook_data);
/* Advanced GPT library. */
/* Status bits for the grub_gpt.status field. */
#define GRUB_GPT_PROTECTIVE_MBR 0x01
#define GRUB_GPT_HYBRID_MBR 0x02
#define GRUB_GPT_PRIMARY_HEADER_VALID 0x04
#define GRUB_GPT_PRIMARY_ENTRIES_VALID 0x08
#define GRUB_GPT_BACKUP_HEADER_VALID 0x10
#define GRUB_GPT_BACKUP_ENTRIES_VALID 0x20
/* UEFI requires the entries table to be at least 16384 bytes for a
* total of 128 entries given the standard 128 byte entry size. */
#define GRUB_GPT_DEFAULT_ENTRIES_SIZE 16384
#define GRUB_GPT_DEFAULT_ENTRIES_LENGTH \
(GRUB_GPT_DEFAULT_ENTRIES_SIZE / sizeof (struct grub_gpt_partentry))
struct grub_gpt
{
/* Bit field indicating which structures on disk are valid. */
unsigned status;
/* Protective or hybrid MBR. */
struct grub_msdos_partition_mbr mbr;
/* Each of the two GPT headers. */
struct grub_gpt_header primary;
struct grub_gpt_header backup;
/* Only need one entries table, on disk both copies are identical.
* The on disk entry size may be larger than our partentry struct so
* the table cannot be indexed directly. */
void *entries;
grub_size_t entries_size;
/* Logarithm of sector size, in case GPT and disk driver disagree. */
unsigned int log_sector_size;
};
typedef struct grub_gpt *grub_gpt_t;
/* Helpers for checking the gpt status field. */
static inline int
grub_gpt_mbr_valid (grub_gpt_t gpt)
{
return ((gpt->status & GRUB_GPT_PROTECTIVE_MBR) ||
(gpt->status & GRUB_GPT_HYBRID_MBR));
}
static inline int
grub_gpt_primary_valid (grub_gpt_t gpt)
{
return ((gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) &&
(gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID));
}
static inline int
grub_gpt_backup_valid (grub_gpt_t gpt)
{
return ((gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) &&
(gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID));
}
static inline int
grub_gpt_both_valid (grub_gpt_t gpt)
{
return grub_gpt_primary_valid (gpt) && grub_gpt_backup_valid (gpt);
}
/* Translate GPT sectors to GRUB's 512 byte block addresses. */
static inline grub_disk_addr_t
grub_gpt_sector_to_addr (grub_gpt_t gpt, grub_uint64_t sector)
{
return (sector << (gpt->log_sector_size - GRUB_DISK_SECTOR_BITS));
}
/* Allocates and fills new grub_gpt structure, free with grub_gpt_free. */
grub_gpt_t grub_gpt_read (grub_disk_t disk);
/* Helper for indexing into the entries table.
* Returns NULL when the end of the table has been reached. */
struct grub_gpt_partentry * grub_gpt_get_partentry (grub_gpt_t gpt,
grub_uint32_t n);
/* Sync and update primary and backup headers if either are invalid. */
grub_err_t grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt);
/* Recompute checksums and revalidate everything, must be called after
* modifying any GPT data. */
grub_err_t grub_gpt_update (grub_gpt_t gpt);
/* Write headers and entry tables back to disk. */
grub_err_t grub_gpt_write (grub_disk_t disk, grub_gpt_t gpt);
void grub_gpt_free (grub_gpt_t gpt);
grub_err_t grub_gpt_pmbr_check (struct grub_msdos_partition_mbr *mbr);
grub_err_t grub_gpt_header_check (struct grub_gpt_header *gpt,
unsigned int log_sector_size);
/* Utilities for simple partition data lookups, usage is intended to
* be similar to fs->label and fs->uuid functions. */
/* Return the partition label of the device DEVICE in LABEL.
* The label is in a new buffer and should be freed by the caller. */
grub_err_t grub_gpt_part_label (grub_device_t device, char **label);
/* Return the partition uuid of the device DEVICE in UUID.
* The uuid is in a new buffer and should be freed by the caller. */
grub_err_t grub_gpt_part_uuid (grub_device_t device, char **uuid);
/* Return the disk uuid of the device DEVICE in UUID.
* The uuid is in a new buffer and should be freed by the caller. */
grub_err_t grub_gpt_disk_uuid (grub_device_t device, char **uuid);
#endif /* ! GRUB_GPT_PARTITION_HEADER */

View file

@ -276,6 +276,7 @@ typedef struct grub_net
grub_fs_t fs;
int eof;
int stall;
int port;
} *grub_net_t;
extern grub_net_t (*EXPORT_VAR (grub_net_open)) (const char *name);

View file

@ -25,5 +25,11 @@ void grub_search_fs_uuid (const char *key, const char *var, int no_floppy,
char **hints, unsigned nhints);
void grub_search_label (const char *key, const char *var, int no_floppy,
char **hints, unsigned nhints);
void grub_search_part_uuid (const char *key, const char *var, int no_floppy,
char **hints, unsigned nhints);
void grub_search_part_label (const char *key, const char *var, int no_floppy,
char **hints, unsigned nhints);
void grub_search_disk_uuid (const char *key, const char *var, int no_floppy,
char **hints, unsigned nhints);
#endif

68
include/grub/smbios.h Normal file
View file

@ -0,0 +1,68 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2015 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRUB_SMBIOS_HEADER
#define GRUB_SMBIOS_HEADER 1
#include <grub/types.h>
#define GRUB_SMBIOS_TYPE_END_OF_TABLE ((grub_uint8_t)127)
struct grub_smbios_ieps
{
grub_uint8_t anchor[5]; /* "_DMI_" */
grub_uint8_t checksum;
grub_uint16_t table_length;
grub_uint32_t table_address;
grub_uint16_t structures;
grub_uint8_t revision;
} GRUB_PACKED;
struct grub_smbios_eps
{
grub_uint8_t anchor[4]; /* "_SM_" */
grub_uint8_t checksum;
grub_uint8_t length; /* 0x1f */
grub_uint8_t version_major;
grub_uint8_t version_minor;
grub_uint16_t maximum_structure_size;
grub_uint8_t revision;
grub_uint8_t formatted[5];
struct grub_smbios_ieps intermediate;
} GRUB_PACKED;
struct grub_smbios_eps3
{
grub_uint8_t anchor[5]; /* "_SM3_" */
grub_uint8_t checksum;
grub_uint8_t length; /* 0x18 */
grub_uint8_t version_major;
grub_uint8_t version_minor;
grub_uint8_t docrev;
grub_uint8_t revision;
grub_uint8_t reserved;
grub_uint32_t maximum_table_length;
grub_uint64_t table_address;
} GRUB_PACKED;
struct grub_smbios_eps *grub_smbios_get_eps (void);
struct grub_smbios_eps3 *grub_smbios_get_eps3 (void);
struct grub_smbios_eps *grub_machine_smbios_get_eps (void);
struct grub_smbios_eps3 *grub_machine_smbios_get_eps3 (void);
#endif /* ! GRUB_SMBIOS_HEADER */

View file

@ -0,0 +1,51 @@
/* CoreOS verity hash */
#define VERITY_ARG " verity.usrhash="
#define VERITY_ARG_LENGTH (sizeof (VERITY_ARG) - 1)
#define VERITY_HASH_LENGTH 64
#define VERITY_CMDLINE_LENGTH ((VERITY_ARG_LENGTH)+(VERITY_HASH_LENGTH))
#if defined(__aarch64__)
# define VERITY_HASH_OFFSET 512
#elif defined(__i386__) || defined(__amd64__)
# define VERITY_HASH_OFFSET 0x40
#else
# error Unsupported arch
#endif
/**
* grub_pass_verity_hash - Reads the CoreOS verity hash value from a well known
* kernel image offset and adds a kernel command line argument for it.
*
* @pImage: Kernel image buffer.
* @cmdline: Kernel command line buffer.
* @cmdline_max_len: Kernel command line buffer length.
*/
static inline void grub_pass_verity_hash(const void *pImage,
char *cmdline,
grub_size_t cmdline_max_len)
{
const char *buf = pImage;
grub_size_t cmdline_len;
int i;
for (i=VERITY_HASH_OFFSET; i<VERITY_HASH_OFFSET + VERITY_HASH_LENGTH; i++)
{
if (buf[i] < '0' || buf[i] > '9') // Not a number
if (buf[i] < 'a' || buf[i] > 'f') // Not a hex letter
return;
}
cmdline_len = grub_strlen(cmdline);
if (cmdline_len + VERITY_CMDLINE_LENGTH > cmdline_max_len)
return;
grub_memcpy (cmdline + cmdline_len, VERITY_ARG, VERITY_ARG_LENGTH);
cmdline_len += VERITY_ARG_LENGTH;
grub_memcpy (cmdline + cmdline_len, buf + VERITY_HASH_OFFSET,
VERITY_HASH_LENGTH);
cmdline_len += VERITY_HASH_LENGTH;
cmdline[cmdline_len] = '\0';
}

View file

@ -24,6 +24,9 @@
#include <grub/elfload.h>
grub_elf_t grub_xen_file (grub_file_t file);
grub_elf_t grub_xen_file_and_cmdline (grub_file_t file,
char *cmdline,
grub_size_t cmdline_max_len);
struct grub_xen_file_info
{

840
tests/gpt_unit_test.c Normal file
View file

@ -0,0 +1,840 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2014 CoreOS, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/command.h>
#include <grub/device.h>
#include <grub/disk.h>
#include <grub/emu/hostdisk.h>
#include <grub/emu/misc.h>
#include <grub/env.h>
#include <grub/err.h>
#include <grub/gpt_partition.h>
#include <grub/msdos_partition.h>
#include <grub/lib/hexdump.h>
#include <grub/search.h>
#include <grub/test.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
/* from gnulib */
#include <verify.h>
/* Confirm that the GPT structures conform to the sizes in the spec:
* The header size "must be greater than or equal to 92 and must be less
* than or equal to the logical block size."
* The partition entry size must be "a value of 128*(2^n) where n is an
* integer greater than or equal to zero (e.g., 128, 256, 512, etc.)." */
verify (sizeof (struct grub_gpt_header) == 92);
verify (sizeof (struct grub_gpt_partentry) == 128);
/* GPT section sizes. */
#define HEADER_SIZE (sizeof (struct grub_gpt_header))
#define HEADER_PAD (GRUB_DISK_SECTOR_SIZE - HEADER_SIZE)
#define ENTRY_SIZE (sizeof (struct grub_gpt_partentry))
#define TABLE_ENTRIES 0x80
#define TABLE_SIZE (TABLE_ENTRIES * ENTRY_SIZE)
#define TABLE_SECTORS (TABLE_SIZE / GRUB_DISK_SECTOR_SIZE)
/* Double check that the table size calculation was valid. */
verify (TABLE_SECTORS * GRUB_DISK_SECTOR_SIZE == TABLE_SIZE);
/* GPT section locations for a 1MiB disk. */
#define DISK_SECTORS 0x800
#define DISK_SIZE (GRUB_DISK_SECTOR_SIZE * DISK_SECTORS)
#define PRIMARY_HEADER_SECTOR 0x1
#define PRIMARY_TABLE_SECTOR 0x2
#define BACKUP_HEADER_SECTOR (DISK_SECTORS - 0x1)
#define BACKUP_TABLE_SECTOR (BACKUP_HEADER_SECTOR - TABLE_SECTORS)
#define DATA_START_SECTOR (PRIMARY_TABLE_SECTOR + TABLE_SECTORS)
#define DATA_END_SECTOR (BACKUP_TABLE_SECTOR - 0x1)
#define DATA_SECTORS (BACKUP_TABLE_SECTOR - DATA_START_SECTOR)
#define DATA_SIZE (GRUB_DISK_SECTOR_SIZE * DATA_SECTORS)
struct test_disk
{
struct grub_msdos_partition_mbr mbr;
struct grub_gpt_header primary_header;
grub_uint8_t primary_header_pad[HEADER_PAD];
struct grub_gpt_partentry primary_entries[TABLE_ENTRIES];
grub_uint8_t data[DATA_SIZE];
struct grub_gpt_partentry backup_entries[TABLE_ENTRIES];
struct grub_gpt_header backup_header;
grub_uint8_t backup_header_pad[HEADER_PAD];
} GRUB_PACKED;
/* Sanity check that all the above ugly math was correct. */
verify (sizeof (struct test_disk) == DISK_SIZE);
struct test_data
{
int fd;
grub_device_t dev;
struct test_disk *raw;
};
/* Sample primary GPT header for a 1MB disk. */
static const struct grub_gpt_header example_primary = {
.magic = GRUB_GPT_HEADER_MAGIC,
.version = GRUB_GPT_HEADER_VERSION,
.headersize = sizeof (struct grub_gpt_header),
.crc32 = grub_cpu_to_le32_compile_time (0xb985abe0),
.header_lba = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR),
.alternate_lba = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR),
.start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR),
.end = grub_cpu_to_le64_compile_time (DATA_END_SECTOR),
.guid = GRUB_GPT_GUID_INIT(0x69c131ad, 0x67d6, 0x46c6,
0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac),
.partitions = grub_cpu_to_le64_compile_time (PRIMARY_TABLE_SECTOR),
.maxpart = grub_cpu_to_le32_compile_time (TABLE_ENTRIES),
.partentry_size = grub_cpu_to_le32_compile_time (ENTRY_SIZE),
.partentry_crc32 = grub_cpu_to_le32_compile_time (0x074e052c),
};
static const struct grub_gpt_partentry example_entries[TABLE_ENTRIES] = {
{
.type = GRUB_GPT_PARTITION_TYPE_EFI_SYSTEM,
.guid = GRUB_GPT_GUID_INIT (0xa0f1792e, 0xb4ce, 0x4136, 0xbc, 0xf2,
0x1a, 0xfc, 0x13, 0x3c, 0x28, 0x28),
.start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR),
.end = grub_cpu_to_le64_compile_time (0x3f),
.attrib = 0x0,
.name = {
grub_cpu_to_le16_compile_time ('E'),
grub_cpu_to_le16_compile_time ('F'),
grub_cpu_to_le16_compile_time ('I'),
grub_cpu_to_le16_compile_time (' '),
grub_cpu_to_le16_compile_time ('S'),
grub_cpu_to_le16_compile_time ('Y'),
grub_cpu_to_le16_compile_time ('S'),
grub_cpu_to_le16_compile_time ('T'),
grub_cpu_to_le16_compile_time ('E'),
grub_cpu_to_le16_compile_time ('M'),
0x0,
}
},
{
.type = GRUB_GPT_PARTITION_TYPE_BIOS_BOOT,
.guid = GRUB_GPT_GUID_INIT (0x876c898d, 0x1b40, 0x4727, 0xa1, 0x61,
0xed, 0xf9, 0xb5, 0x48, 0x66, 0x74),
.start = grub_cpu_to_le64_compile_time (0x40),
.end = grub_cpu_to_le64_compile_time (0x7f),
.attrib = grub_cpu_to_le64_compile_time (
1ULL << GRUB_GPT_PART_ATTR_OFFSET_LEGACY_BIOS_BOOTABLE),
.name = {
grub_cpu_to_le16_compile_time ('B'),
grub_cpu_to_le16_compile_time ('I'),
grub_cpu_to_le16_compile_time ('O'),
grub_cpu_to_le16_compile_time ('S'),
grub_cpu_to_le16_compile_time (' '),
grub_cpu_to_le16_compile_time ('B'),
grub_cpu_to_le16_compile_time ('O'),
grub_cpu_to_le16_compile_time ('O'),
grub_cpu_to_le16_compile_time ('T'),
0x0,
}
},
};
/* And the backup header. */
static const struct grub_gpt_header example_backup = {
.magic = GRUB_GPT_HEADER_MAGIC,
.version = GRUB_GPT_HEADER_VERSION,
.headersize = sizeof (struct grub_gpt_header),
.crc32 = grub_cpu_to_le32_compile_time (0x0af785eb),
.header_lba = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR),
.alternate_lba = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR),
.start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR),
.end = grub_cpu_to_le64_compile_time (DATA_END_SECTOR),
.guid = GRUB_GPT_GUID_INIT(0x69c131ad, 0x67d6, 0x46c6,
0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac),
.partitions = grub_cpu_to_le64_compile_time (BACKUP_TABLE_SECTOR),
.maxpart = grub_cpu_to_le32_compile_time (TABLE_ENTRIES),
.partentry_size = grub_cpu_to_le32_compile_time (ENTRY_SIZE),
.partentry_crc32 = grub_cpu_to_le32_compile_time (0x074e052c),
};
/* Sample protective MBR for the same 1MB disk. Note, this matches
* parted and fdisk behavior. The UEFI spec uses different values. */
static const struct grub_msdos_partition_mbr example_pmbr = {
.entries = {{.flag = 0x00,
.start_head = 0x00,
.start_sector = 0x01,
.start_cylinder = 0x00,
.type = 0xee,
.end_head = 0xfe,
.end_sector = 0xff,
.end_cylinder = 0xff,
.start = grub_cpu_to_le32_compile_time (0x1),
.length = grub_cpu_to_le32_compile_time (DISK_SECTORS - 0x1),
}},
.signature = grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE),
};
/* If errors are left in grub's error stack things can get confused. */
static void
assert_error_stack_empty (void)
{
do
{
grub_test_assert (grub_errno == GRUB_ERR_NONE,
"error on stack: %s", grub_errmsg);
}
while (grub_error_pop ());
}
static grub_err_t
execute_command2 (const char *name, const char *arg1, const char *arg2)
{
grub_command_t cmd;
grub_err_t err;
char *argv[2];
cmd = grub_command_find (name);
if (!cmd)
grub_fatal ("can't find command %s", name);
argv[0] = strdup (arg1);
argv[1] = strdup (arg2);
err = (cmd->func) (cmd, 2, argv);
free (argv[0]);
free (argv[1]);
return err;
}
static void
sync_disk (struct test_data *data)
{
if (msync (data->raw, DISK_SIZE, MS_SYNC | MS_INVALIDATE) < 0)
grub_fatal ("Syncing disk failed: %s", strerror (errno));
grub_disk_cache_invalidate_all ();
}
static void
reset_disk (struct test_data *data)
{
memset (data->raw, 0, DISK_SIZE);
/* Initialize image with valid example tables. */
memcpy (&data->raw->mbr, &example_pmbr, sizeof (data->raw->mbr));
memcpy (&data->raw->primary_header, &example_primary,
sizeof (data->raw->primary_header));
memcpy (&data->raw->primary_entries, &example_entries,
sizeof (data->raw->primary_entries));
memcpy (&data->raw->backup_entries, &example_entries,
sizeof (data->raw->backup_entries));
memcpy (&data->raw->backup_header, &example_backup,
sizeof (data->raw->backup_header));
sync_disk (data);
}
static void
open_disk (struct test_data *data)
{
const char *loop = "loop0";
char template[] = "/tmp/grub_gpt_test.XXXXXX";
char host[sizeof ("(host)") + sizeof (template)];
data->fd = mkstemp (template);
if (data->fd < 0)
grub_fatal ("Creating %s failed: %s", template, strerror (errno));
if (ftruncate (data->fd, DISK_SIZE) < 0)
{
int err = errno;
unlink (template);
grub_fatal ("Resizing %s failed: %s", template, strerror (err));
}
data->raw = mmap (NULL, DISK_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, data->fd, 0);
if (data->raw == MAP_FAILED)
{
int err = errno;
unlink (template);
grub_fatal ("Maping %s failed: %s", template, strerror (err));
}
snprintf (host, sizeof (host), "(host)%s", template);
if (execute_command2 ("loopback", loop, host) != GRUB_ERR_NONE)
{
unlink (template);
grub_fatal ("loopback %s %s failed: %s", loop, host, grub_errmsg);
}
if (unlink (template) < 0)
grub_fatal ("Unlinking %s failed: %s", template, strerror (errno));
reset_disk (data);
data->dev = grub_device_open (loop);
if (!data->dev)
grub_fatal ("Opening %s failed: %s", loop, grub_errmsg);
}
static void
close_disk (struct test_data *data)
{
char *loop;
assert_error_stack_empty ();
if (munmap (data->raw, DISK_SIZE) || close (data->fd))
grub_fatal ("Closing disk image failed: %s", strerror (errno));
loop = strdup (data->dev->disk->name);
grub_test_assert (grub_device_close (data->dev) == GRUB_ERR_NONE,
"Closing disk device failed: %s", grub_errmsg);
grub_test_assert (execute_command2 ("loopback", "-d", loop) ==
GRUB_ERR_NONE, "loopback -d %s failed: %s", loop,
grub_errmsg);
free (loop);
}
static grub_gpt_t
read_disk (struct test_data *data)
{
grub_gpt_t gpt;
gpt = grub_gpt_read (data->dev->disk);
if (gpt == NULL)
grub_fatal ("grub_gpt_read failed: %s", grub_errmsg);
return gpt;
}
static void
pmbr_test (void)
{
struct grub_msdos_partition_mbr mbr;
memset (&mbr, 0, sizeof (mbr));
/* Empty is invalid. */
grub_gpt_pmbr_check (&mbr);
grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
/* A table without a protective partition is invalid. */
mbr.signature = grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE);
grub_gpt_pmbr_check (&mbr);
grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
/* A table with a protective type is ok. */
memcpy (&mbr, &example_pmbr, sizeof (mbr));
grub_gpt_pmbr_check (&mbr);
grub_test_assert (grub_errno == GRUB_ERR_NONE,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
}
static void
header_test (void)
{
struct grub_gpt_header primary, backup;
/* Example headers should be valid. */
memcpy (&primary, &example_primary, sizeof (primary));
grub_gpt_header_check (&primary, GRUB_DISK_SECTOR_BITS);
grub_test_assert (grub_errno == GRUB_ERR_NONE,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
memcpy (&backup, &example_backup, sizeof (backup));
grub_gpt_header_check (&backup, GRUB_DISK_SECTOR_BITS);
grub_test_assert (grub_errno == GRUB_ERR_NONE,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
/* Twiddle the GUID to invalidate the CRC. */
primary.guid.data1 = 0;
grub_gpt_header_check (&primary, GRUB_DISK_SECTOR_BITS);
grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
backup.guid.data1 = 0;
grub_gpt_header_check (&backup, GRUB_DISK_SECTOR_BITS);
grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
}
static void
read_valid_test (void)
{
struct test_data data;
grub_gpt_t gpt;
open_disk (&data);
gpt = read_disk (&data);
grub_test_assert (gpt->status == (GRUB_GPT_PROTECTIVE_MBR |
GRUB_GPT_PRIMARY_HEADER_VALID |
GRUB_GPT_PRIMARY_ENTRIES_VALID |
GRUB_GPT_BACKUP_HEADER_VALID |
GRUB_GPT_BACKUP_ENTRIES_VALID),
"unexpected status: 0x%02x", gpt->status);
grub_gpt_free (gpt);
close_disk (&data);
}
static void
read_invalid_entries_test (void)
{
struct test_data data;
grub_gpt_t gpt;
open_disk (&data);
/* Corrupt the first entry in both tables. */
memset (&data.raw->primary_entries[0], 0x55,
sizeof (data.raw->primary_entries[0]));
memset (&data.raw->backup_entries[0], 0x55,
sizeof (data.raw->backup_entries[0]));
sync_disk (&data);
gpt = grub_gpt_read (data.dev->disk);
grub_test_assert (gpt == NULL, "no error reported for corrupt entries");
grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
close_disk (&data);
}
static void
read_fallback_test (void)
{
struct test_data data;
grub_gpt_t gpt;
open_disk (&data);
/* Corrupt the primary header. */
memset (&data.raw->primary_header.guid, 0x55,
sizeof (data.raw->primary_header.guid));
sync_disk (&data);
gpt = read_disk (&data);
grub_test_assert ((gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) == 0,
"unreported corrupt primary header");
grub_gpt_free (gpt);
reset_disk (&data);
/* Corrupt the backup header. */
memset (&data.raw->backup_header.guid, 0x55,
sizeof (data.raw->backup_header.guid));
sync_disk (&data);
gpt = read_disk (&data);
grub_test_assert ((gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) == 0,
"unreported corrupt backup header");
grub_gpt_free (gpt);
reset_disk (&data);
/* Corrupt the primary entry table. */
memset (&data.raw->primary_entries[0], 0x55,
sizeof (data.raw->primary_entries[0]));
sync_disk (&data);
gpt = read_disk (&data);
grub_test_assert ((gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID) == 0,
"unreported corrupt primary entries table");
grub_gpt_free (gpt);
reset_disk (&data);
/* Corrupt the backup entry table. */
memset (&data.raw->backup_entries[0], 0x55,
sizeof (data.raw->backup_entries[0]));
sync_disk (&data);
gpt = read_disk (&data);
grub_test_assert ((gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID) == 0,
"unreported corrupt backup entries table");
grub_gpt_free (gpt);
reset_disk (&data);
/* If primary is corrupt and disk size is unknown fallback fails. */
memset (&data.raw->primary_header.guid, 0x55,
sizeof (data.raw->primary_header.guid));
sync_disk (&data);
data.dev->disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN;
gpt = grub_gpt_read (data.dev->disk);
grub_test_assert (gpt == NULL, "no error reported");
grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
close_disk (&data);
}
static void
repair_test (void)
{
struct test_data data;
grub_gpt_t gpt;
open_disk (&data);
/* Erase/Repair primary. */
memset (&data.raw->primary_header, 0, sizeof (data.raw->primary_header));
sync_disk (&data);
gpt = read_disk (&data);
grub_gpt_repair (data.dev->disk, gpt);
grub_test_assert (grub_errno == GRUB_ERR_NONE,
"repair failed: %s", grub_errmsg);
if (memcmp (&gpt->primary, &example_primary, sizeof (gpt->primary)))
{
printf ("Invalid restored primary header:\n");
hexdump (16, (char*)&gpt->primary, sizeof (gpt->primary));
printf ("Expected primary header:\n");
hexdump (16, (char*)&example_primary, sizeof (example_primary));
grub_test_assert (0, "repair did not restore primary header");
}
grub_gpt_free (gpt);
reset_disk (&data);
/* Erase/Repair backup. */
memset (&data.raw->backup_header, 0, sizeof (data.raw->backup_header));
sync_disk (&data);
gpt = read_disk (&data);
grub_gpt_repair (data.dev->disk, gpt);
grub_test_assert (grub_errno == GRUB_ERR_NONE,
"repair failed: %s", grub_errmsg);
if (memcmp (&gpt->backup, &example_backup, sizeof (gpt->backup)))
{
printf ("Invalid restored backup header:\n");
hexdump (16, (char*)&gpt->backup, sizeof (gpt->backup));
printf ("Expected backup header:\n");
hexdump (16, (char*)&example_backup, sizeof (example_backup));
grub_test_assert (0, "repair did not restore backup header");
}
grub_gpt_free (gpt);
reset_disk (&data);
close_disk (&data);
}
/* Finding/reading/writing the backup GPT may be difficult if the OS and
* BIOS report different sizes for the same disk. We need to gracefully
* recognize this and avoid causing trouble for the OS. */
static void
weird_disk_size_test (void)
{
struct test_data data;
grub_gpt_t gpt;
open_disk (&data);
/* Chop off 65536 bytes (128 512B sectors) which may happen when the
* BIOS thinks you are using a software RAID system that reserves that
* area for metadata when in fact you are not and using the bare disk. */
grub_test_assert(data.dev->disk->total_sectors == DISK_SECTORS,
"unexpected disk size: 0x%llx",
(unsigned long long) data.dev->disk->total_sectors);
data.dev->disk->total_sectors -= 128;
gpt = read_disk (&data);
assert_error_stack_empty ();
/* Reading the alternate_lba should have been blocked and reading
* the (new) end of disk should have found no useful data. */
grub_test_assert ((gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) == 0,
"unreported missing backup header");
/* We should be able to reconstruct the backup header and the location
* of the backup should remain unchanged, trusting the GPT data over
* what the BIOS is telling us. Further changes are left to the OS. */
grub_gpt_repair (data.dev->disk, gpt);
grub_test_assert (grub_errno == GRUB_ERR_NONE,
"repair failed: %s", grub_errmsg);
grub_test_assert (memcmp (&gpt->primary, &example_primary,
sizeof (gpt->primary)) == 0,
"repair corrupted primary header");
grub_gpt_free (gpt);
close_disk (&data);
}
static void
iterate_partitions_test (void)
{
struct test_data data;
struct grub_gpt_partentry *p;
grub_gpt_t gpt;
grub_uint32_t n;
open_disk (&data);
gpt = read_disk (&data);
for (n = 0; (p = grub_gpt_get_partentry (gpt, n)) != NULL; n++)
grub_test_assert (memcmp (p, &example_entries[n], sizeof (*p)) == 0,
"unexpected partition %d data", n);
grub_test_assert (n == TABLE_ENTRIES, "unexpected partition limit: %d", n);
grub_gpt_free (gpt);
close_disk (&data);
}
static void
large_partitions_test (void)
{
struct test_data data;
struct grub_gpt_partentry *p;
grub_gpt_t gpt;
grub_uint32_t n;
open_disk (&data);
/* Double the entry size, cut the number of entries in half. */
data.raw->primary_header.maxpart =
data.raw->backup_header.maxpart =
grub_cpu_to_le32_compile_time (TABLE_ENTRIES/2);
data.raw->primary_header.partentry_size =
data.raw->backup_header.partentry_size =
grub_cpu_to_le32_compile_time (ENTRY_SIZE*2);
data.raw->primary_header.partentry_crc32 =
data.raw->backup_header.partentry_crc32 =
grub_cpu_to_le32_compile_time (0xf2c45af8);
data.raw->primary_header.crc32 = grub_cpu_to_le32_compile_time (0xde00cc8f);
data.raw->backup_header.crc32 = grub_cpu_to_le32_compile_time (0x6d72e284);
memset (&data.raw->primary_entries, 0,
sizeof (data.raw->primary_entries));
for (n = 0; n < TABLE_ENTRIES/2; n++)
memcpy (&data.raw->primary_entries[n*2], &example_entries[n],
sizeof (data.raw->primary_entries[0]));
memcpy (&data.raw->backup_entries, &data.raw->primary_entries,
sizeof (data.raw->backup_entries));
sync_disk(&data);
gpt = read_disk (&data);
for (n = 0; (p = grub_gpt_get_partentry (gpt, n)) != NULL; n++)
grub_test_assert (memcmp (p, &example_entries[n], sizeof (*p)) == 0,
"unexpected partition %d data", n);
grub_test_assert (n == TABLE_ENTRIES/2, "unexpected partition limit: %d", n);
grub_gpt_free (gpt);
/* Editing memory beyond the entry structure should still change the crc. */
data.raw->primary_entries[1].attrib = 0xff;
sync_disk(&data);
gpt = read_disk (&data);
grub_test_assert (gpt->status == (GRUB_GPT_PROTECTIVE_MBR |
GRUB_GPT_PRIMARY_HEADER_VALID |
GRUB_GPT_BACKUP_HEADER_VALID |
GRUB_GPT_BACKUP_ENTRIES_VALID),
"unexpected status: 0x%02x", gpt->status);
grub_gpt_free (gpt);
close_disk (&data);
}
static void
invalid_partsize_test (void)
{
struct grub_gpt_header header = {
.magic = GRUB_GPT_HEADER_MAGIC,
.version = GRUB_GPT_HEADER_VERSION,
.headersize = sizeof (struct grub_gpt_header),
.crc32 = grub_cpu_to_le32_compile_time (0x1ff2a054),
.header_lba = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR),
.alternate_lba = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR),
.start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR),
.end = grub_cpu_to_le64_compile_time (DATA_END_SECTOR),
.guid = GRUB_GPT_GUID_INIT(0x69c131ad, 0x67d6, 0x46c6,
0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac),
.partitions = grub_cpu_to_le64_compile_time (PRIMARY_TABLE_SECTOR),
.maxpart = grub_cpu_to_le32_compile_time (TABLE_ENTRIES),
/* Triple the entry size, which is not valid. */
.partentry_size = grub_cpu_to_le32_compile_time (ENTRY_SIZE*3),
.partentry_crc32 = grub_cpu_to_le32_compile_time (0x074e052c),
};
grub_gpt_header_check(&header, GRUB_DISK_SECTOR_BITS);
grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE,
"unexpected error: %s", grub_errmsg);
grub_test_assert (strcmp(grub_errmsg, "invalid GPT entry size") == 0,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
}
static void
search_part_label_test (void)
{
struct test_data data;
const char *test_result;
char *expected_result;
open_disk (&data);
expected_result = grub_xasprintf ("%s,gpt1", data.dev->disk->name);
grub_env_unset ("test_result");
grub_search_part_label ("EFI SYSTEM", "test_result", 0, NULL, 0);
test_result = grub_env_get ("test_result");
grub_test_assert (test_result && strcmp (test_result, expected_result) == 0,
"wrong device: %s (%s)", test_result, expected_result);
grub_free (expected_result);
expected_result = grub_xasprintf ("%s,gpt2", data.dev->disk->name);
grub_env_unset ("test_result");
grub_search_part_label ("BIOS BOOT", "test_result", 0, NULL, 0);
test_result = grub_env_get ("test_result");
grub_test_assert (test_result && strcmp (test_result, expected_result) == 0,
"wrong device: %s (%s)", test_result, expected_result);
grub_free (expected_result);
grub_env_unset ("test_result");
grub_search_part_label ("bogus name", "test_result", 0, NULL, 0);
test_result = grub_env_get ("test_result");
grub_test_assert (test_result == NULL,
"unexpected device: %s", test_result);
grub_test_assert (grub_errno == GRUB_ERR_FILE_NOT_FOUND,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
close_disk (&data);
}
static void
search_part_uuid_test (void)
{
struct test_data data;
const char gpt1_uuid[] = "A0F1792E-B4CE-4136-BCF2-1AFC133C2828";
const char gpt2_uuid[] = "876c898d-1b40-4727-a161-edf9b5486674";
const char bogus_uuid[] = "1534c928-c50e-4866-9daf-6a9fd7918a76";
const char *test_result;
char *expected_result;
open_disk (&data);
expected_result = grub_xasprintf ("%s,gpt1", data.dev->disk->name);
grub_env_unset ("test_result");
grub_search_part_uuid (gpt1_uuid, "test_result", 0, NULL, 0);
test_result = grub_env_get ("test_result");
grub_test_assert (test_result && strcmp (test_result, expected_result) == 0,
"wrong device: %s (%s)", test_result, expected_result);
grub_free (expected_result);
expected_result = grub_xasprintf ("%s,gpt2", data.dev->disk->name);
grub_env_unset ("test_result");
grub_search_part_uuid (gpt2_uuid, "test_result", 0, NULL, 0);
test_result = grub_env_get ("test_result");
grub_test_assert (test_result && strcmp (test_result, expected_result) == 0,
"wrong device: %s (%s)", test_result, expected_result);
grub_free (expected_result);
grub_env_unset ("test_result");
grub_search_part_uuid (bogus_uuid, "test_result", 0, NULL, 0);
test_result = grub_env_get ("test_result");
grub_test_assert (test_result == NULL,
"unexpected device: %s", test_result);
grub_test_assert (grub_errno == GRUB_ERR_FILE_NOT_FOUND,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
close_disk (&data);
}
static void
search_disk_uuid_test (void)
{
struct test_data data;
const char disk_uuid[] = "69c131ad-67d6-46c6-93c4-124c755256ac";
const char bogus_uuid[] = "1534c928-c50e-4866-9daf-6a9fd7918a76";
const char *test_result;
char *expected_result;
open_disk (&data);
expected_result = grub_xasprintf ("%s", data.dev->disk->name);
grub_env_unset ("test_result");
grub_search_disk_uuid (disk_uuid, "test_result", 0, NULL, 0);
test_result = grub_env_get ("test_result");
grub_test_assert (test_result && strcmp (test_result, expected_result) == 0,
"wrong device: %s (%s)", test_result, expected_result);
grub_free (expected_result);
grub_env_unset ("test_result");
grub_search_disk_uuid (bogus_uuid, "test_result", 0, NULL, 0);
test_result = grub_env_get ("test_result");
grub_test_assert (test_result == NULL,
"unexpected device: %s", test_result);
grub_test_assert (grub_errno == GRUB_ERR_FILE_NOT_FOUND,
"unexpected error: %s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
close_disk (&data);
}
void
grub_unit_test_init (void)
{
grub_init_all ();
grub_hostfs_init ();
grub_host_init ();
grub_test_register ("gpt_pmbr_test", pmbr_test);
grub_test_register ("gpt_header_test", header_test);
grub_test_register ("gpt_read_valid_test", read_valid_test);
grub_test_register ("gpt_read_invalid_test", read_invalid_entries_test);
grub_test_register ("gpt_read_fallback_test", read_fallback_test);
grub_test_register ("gpt_repair_test", repair_test);
grub_test_register ("gpt_iterate_partitions_test", iterate_partitions_test);
grub_test_register ("gpt_large_partitions_test", large_partitions_test);
grub_test_register ("gpt_invalid_partsize_test", invalid_partsize_test);
grub_test_register ("gpt_weird_disk_size_test", weird_disk_size_test);
grub_test_register ("gpt_search_part_label_test", search_part_label_test);
grub_test_register ("gpt_search_uuid_test", search_part_uuid_test);
grub_test_register ("gpt_search_disk_uuid_test", search_disk_uuid_test);
}
void
grub_unit_test_fini (void)
{
grub_test_unregister ("gpt_pmbr_test");
grub_test_unregister ("gpt_header_test");
grub_test_unregister ("gpt_read_valid_test");
grub_test_unregister ("gpt_read_invalid_test");
grub_test_unregister ("gpt_read_fallback_test");
grub_test_unregister ("gpt_repair_test");
grub_test_unregister ("gpt_iterate_partitions_test");
grub_test_unregister ("gpt_large_partitions_test");
grub_test_unregister ("gpt_invalid_partsize_test");
grub_test_unregister ("gpt_weird_disk_size_test");
grub_test_unregister ("gpt_search_part_label_test");
grub_test_unregister ("gpt_search_part_uuid_test");
grub_test_unregister ("gpt_search_disk_uuid_test");
grub_fini_all ();
}

207
tests/gptprio_test.in Normal file
View file

@ -0,0 +1,207 @@
#! /bin/bash
set -e
# Copyright (C) 2010 Free Software Foundation, Inc.
# Copyright (C) 2014 CoreOS, Inc.
#
# GRUB is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GRUB is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see <http://www.gnu.org/licenses/>.
sgdisk=sgdisk
grubshell=@builddir@/grub-shell
if ! which "${sgdisk}" >/dev/null 2>&1; then
echo "sgdisk not installed; cannot test gptprio."
exit 77
fi
. "@builddir@/grub-core/modinfo.sh"
case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in
mips-qemu_mips | mipsel-qemu_mips | i386-qemu | i386-multiboot | i386-coreboot | mipsel-loongson)
disk=ata0
;;
powerpc-ieee1275)
disk=ieee1275//pci@80000000/mac-io@4/ata-3@20000/disk@0
# FIXME: QEMU firmware has bugs which prevent it from accessing hard disk w/o recognised label.
exit 0
;;
sparc64-ieee1275)
disk=ieee1275//pci@1fe\,0/pci-ata@5/ide0@500/disk@0
# FIXME: QEMU firmware has bugs which prevent it from accessing hard disk w/o recognised label.
exit 0
;;
i386-ieee1275)
disk=ieee1275/d
# FIXME: QEMU firmware has bugs which prevent it from accessing hard disk w/o recognised label.
exit 0
;;
mips-arc)
# FIXME: ARC firmware has bugs which prevent it from accessing hard disk w/o dvh disklabel.
exit 0 ;;
mipsel-arc)
disk=arc/scsi0/disk0/rdisk0
;;
*)
disk=hd0
;;
esac
img1="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 1
trap "rm -f '${img1}'" EXIT
prio_type="5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6"
declare -a prio_uuid
prio_uuid[2]="9b003904-d006-4ab3-97f1-73f547b7af1a"
prio_uuid[3]="1aa5a658-5b02-414d-9b71-f7e6c151f0cd"
prio_uuid[4]="8aa0240d-98af-42b0-b32a-ccbe0572d62b"
create_disk_image () {
size=$1
rm -f "${img1}"
dd if=/dev/zero of="${img1}" bs=512 count=1 seek=$((size - 1)) status=none
${sgdisk} \
-n 1:0:+1 -c 1:ESP -t 1:ef00 \
-n 2:0:+1 -c 2:A -t 2:"${prio_type}" -u 2:"${prio_uuid[2]}" \
-n 3:0:+1 -c 3:B -t 3:"${prio_type}" -u 3:"${prio_uuid[3]}" \
-n 4:0:+1 -c 4:C -t 4:"${prio_type}" -u 4:"${prio_uuid[4]}" \
"${img1}" >/dev/null
}
wipe_disk_area () {
sector=$1
size=$2
dd if=/dev/zero of="${img1}" bs=512 count=${size} seek=${sector} conv=notrunc status=none
}
is_zero () {
sector=$1
size=$2
cmp -s -i $((sector * 512)) -n $((size * 512)) /dev/zero "${img1}"
}
check_is_zero () {
sector=$1
size=$2
if ! is_zero "$@"; then
echo "$size sector(s) starting at $sector should be all zero"
exit 1
fi
}
check_not_zero () {
sector=$1
size=$2
if is_zero "$@"; then
echo "$size sector(s) starting at $sector should not be all zero"
exit 1
fi
}
fmt_prio () {
priority=$(( ( $1 & 15 ) << 48 ))
tries=$(( ( $2 & 15 ) << 52 ))
success=$(( ( $3 & 1 ) << 56 ))
printf %016x $(( priority | tries | success ))
}
set_prio () {
part="$1"
attr=$(fmt_prio $2 $3 $4)
${sgdisk} -A "${part}:=:${attr}" "${img1}" >/dev/null
}
check_prio () {
part="$1"
expect=$(fmt_prio $2 $3 $4)
result=$(LANG=C ${sgdisk} -i "${part}" "${img1}" 2>&1 \
| awk '/^Attribute flags: / {print $3}')
if [[ "${expect}" != "${result}" ]]; then
echo "Partition ${part} has attributes ${result:-??}, not ${expect}"
exit 1
fi
}
run_next() {
"${grubshell}" --disk="${img1}" --modules=gptprio <<EOF
gptprio.next -d next_dev -u next_uuid "${disk}"
echo next_dev=\$next_dev
echo next_uuid=\$next_uuid
EOF
}
check_next () {
part="$1"
output=$(run_next)
if grep ^error <<<"${output}"; then
exit 1
fi
if ! grep -q "^next_dev=${disk},gpt${part}$" <<<"${output}"; then
echo "Unexpected next_dev: (expected ${disk},gpt${part})"
echo "${output}"
exit 1
fi
if ! grep -q "^next_uuid=${prio_uuid[$part]}$" <<<"${output}"; then
echo "Unexpected next_uuid: (expected ${prio_uuid[$part]})"
echo "${output}"
exit 1
fi
check_prio "$@"
}
# Basic sanity check
create_disk_image 100
set_prio 2 3 2 1
check_prio 2 3 2 1
# Check gptprio works without modifying the disk when no update is required.
# Leaves any existing corruption as is, repairing in the OS is better.
create_disk_image 100
set_prio 2 1 0 1
wipe_disk_area 99 1
check_next 2 1 0 1
check_is_zero 99 1
create_disk_image 100
set_prio 2 1 0 1
wipe_disk_area 1 1
check_next 2 1 0 1
check_is_zero 1 1
# When writes do need to be made go ahead and perform the repair.
create_disk_image 100
set_prio 2 1 1 0
wipe_disk_area 99 1
check_next 2 1 0 0
check_not_zero 99 1
create_disk_image 100
set_prio 2 1 1 0
wipe_disk_area 1 1
check_next 2 1 0 0
check_not_zero 1 1
# Try two partitions before falling before falling back to a third
create_disk_image 100
set_prio 2 3 3 0
set_prio 3 2 2 0
set_prio 4 1 0 1
check_next 2 3 2 0
check_next 2 3 1 0
check_next 2 3 0 0
check_next 3 2 1 0
check_next 3 2 0 0
check_next 4 1 0 1
check_next 4 1 0 1
check_next 4 1 0 1
check_prio 2 3 0 0
check_prio 3 2 0 0

102
tests/gptrepair_test.in Normal file
View file

@ -0,0 +1,102 @@
#! /bin/sh
set -e
# Copyright (C) 2010 Free Software Foundation, Inc.
# Copyright (C) 2014 CoreOS, Inc.
#
# GRUB is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GRUB is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see <http://www.gnu.org/licenses/>.
parted=parted
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in
mips-qemu_mips | mipsel-qemu_mips | i386-qemu | i386-multiboot | i386-coreboot | mipsel-loongson)
disk=ata0
;;
powerpc-ieee1275)
disk=ieee1275//pci@80000000/mac-io@4/ata-3@20000/disk@0
# FIXME: QEMU firmware has bugs which prevent it from accessing hard disk w/o recognised label.
exit 0
;;
sparc64-ieee1275)
disk=ieee1275//pci@1fe\,0/pci-ata@5/ide0@500/disk@0
# FIXME: QEMU firmware has bugs which prevent it from accessing hard disk w/o recognised label.
exit 0
;;
i386-ieee1275)
disk=ieee1275/d
# FIXME: QEMU firmware has bugs which prevent it from accessing hard disk w/o recognised label.
exit 0
;;
mips-arc)
# FIXME: ARC firmware has bugs which prevent it from accessing hard disk w/o dvh disklabel.
exit 0 ;;
mipsel-arc)
disk=arc/scsi0/disk0/rdisk0
;;
*)
disk=hd0
;;
esac
img1="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 1
img2="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 1
trap "rm -f '${img1}' '${img2}'" EXIT
create_disk_image () {
size=$1
rm -f "${img1}"
dd if=/dev/zero of="${img1}" bs=512 count=1 seek=$((size - 1)) status=none
${parted} -a none -s "${img1}" mklabel gpt
cp "${img1}" "${img2}"
}
wipe_disk_area () {
sector=$1
size=$2
dd if=/dev/zero of="${img2}" bs=512 count=${size} seek=${sector} conv=notrunc status=none
}
do_repair () {
output="`echo "gptrepair ($disk)" | "${grubshell}" --disk="${img2}"`"
if echo "${output}" | grep ^error; then
return 1
fi
if echo "${output}" | grep -v GPT; then
echo "Unexpected output ${output}"
return 1
fi
echo "${output}"
}
echo "Nothing to repair:"
create_disk_image 100
do_repair
cmp "${img1}" "${img2}"
echo
echo "Repair primary (MBR left intact)"
create_disk_image 100
wipe_disk_area 1 1
do_repair
cmp "${img1}" "${img2}"
echo
echo "Repair backup"
create_disk_image 100
wipe_disk_area 99 1
do_repair
cmp "${img1}" "${img2}"
echo