This change allows seabios to load bootorder not only from CBFS but also from the NVRAM as well, making use of coreboot's cmos.layout.
Tested on QEMU Q35
From 055a831ed9872b99bf6d060c70b495e286ded1ba Mon Sep 17 00:00:00 2001 From: Timothy Kenno Handojo timkenhan@mailbox.org Date: Thu, 31 Oct 2024 08:11:38 +0700 Subject: [PATCH] boot: Allow setting bootorder from CMOS entry
The original bootorder from CBFS remains, but takes lower priority.
Signed-off-by: Timothy Kenno Handojo timkenhan@mailbox.org --- Makefile | 2 +- src/boot.c | 9 +++- src/nvram.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/nvram.h | 37 +++++++++++++++ 4 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 src/nvram.c create mode 100644 src/nvram.h
diff --git a/Makefile b/Makefile index d3341870..48f0b9ac 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ SRC32FLAT=$(SRCBOTH) post.c e820map.c malloc.c romfile.c x86.c \ fw/mtrr.c fw/xen.c fw/acpi.c fw/mptable.c fw/pirtable.c \ fw/smbios.c fw/romfile_loader.c fw/dsdt_parser.c hw/virtio-ring.c \ hw/virtio-pci.c hw/virtio-mmio.c hw/virtio-blk.c hw/virtio-scsi.c \ - hw/tpm_drivers.c hw/nvme.c sha256.c sha512.c + hw/tpm_drivers.c hw/nvme.c sha256.c sha512.c nvram.c SRC32SEG=string.c output.c pcibios.c apm.c stacks.c hw/pci.c hw/serialio.c DIRS=src src/hw src/fw vgasrc
diff --git a/src/boot.c b/src/boot.c index 1effd802..0689d7de 100644 --- a/src/boot.c +++ b/src/boot.c @@ -21,6 +21,8 @@ #include "string.h" // memset #include "util.h" // irqtimer_calc #include "tcgbios.h" // tpm_* +#include "nvram.h" // get_nvram_bootorder +
/**************************************************************** * Helper search functions @@ -250,8 +252,13 @@ loadBootOrder(void) { if (!CONFIG_BOOTORDER) return; + char *f = NULL; + + f = get_nvram_bootorder(); + + if (!f) + f = romfile_loadfile("bootorder", NULL);
- char *f = romfile_loadfile("bootorder", NULL); if (!f) return;
diff --git a/src/nvram.c b/src/nvram.c new file mode 100644 index 00000000..0634cf9c --- /dev/null +++ b/src/nvram.c @@ -0,0 +1,130 @@ +#include "nvram.h" +#include "string.h" // memcmp, strlen +#include "malloc.h" // malloc_low +#include "x86.h" // inb, outb +#include "output.h" // dprintf +#include "util.h" // cb_header, find_cb_table, find_cb_subtable + +#define CB_TAG_CMOS_OPTION_TABLE 0x00c8 +#define CB_TAG_OPTION 0x00c9 +#define CB_TAG_OPTION_CHECKSUM 0x00cc + + +u8 nvram_read(u8 addr){ + u16 rtc_port = addr < 128 ? RTC_PORT_STANDARD : RTC_PORT_EXTENDED; + + outb(addr, rtc_port); + return inb(rtc_port + 1); +} + +void nvram_write(u8 val, u8 addr) +{ + u16 rtc_port = addr < 128 ? RTC_PORT_STANDARD : RTC_PORT_EXTENDED; + + outb(addr, rtc_port); + outb(val, rtc_port + 1); +} + +struct nvram_accessor { + u8 (*read)(u8 reg); + void (*write)(u8 val, u8 reg); +}; + +struct nvram_accessor *use_nvram = &(struct nvram_accessor) { + nvram_read, + nvram_write +}; + +static struct cb_cmos_entries *lookup_cmos_entry(struct cb_cmos_option_table *option_table, const char *name) +{ + struct cb_cmos_entries *cmos_entry, *next; + int len = name ? strlen(name) : 0; + + /* CMOS entries are located right after the option table */ + cmos_entry = (struct cb_cmos_entries*)((unsigned char *)option_table + option_table->header_length); + while (cmos_entry) { + if (memcmp((const char*)cmos_entry->name, name, len) == 0) + return cmos_entry; + next = (struct cb_cmos_entries*)((unsigned char *)cmos_entry + cmos_entry->size); + cmos_entry = (next->tag == CB_TAG_OPTION) ? next : NULL; + } + + dprintf(1, "ERROR: No such CMOS option (%s)\n", name); + return NULL; +} + +static int get_cmos_value(const struct nvram_accessor *nvram, u32 bitnum, u32 len, void *valptr) +{ + u8 *value = valptr; + int offs = 0; + u32 addr, bit; + u8 reg8; + + /* Convert to byte borders */ + addr=(bitnum / 8); + bit=(bitnum % 8); + + /* Handle single byte or less */ + if(len <= 8) { + reg8 = nvram->read(addr); + reg8 >>= bit; + value[0] = reg8 & ((1 << len) -1); + return 0; + } + + /* When handling more than a byte, copy whole bytes */ + while (len > 0) { + len -= 8; + value[offs++]=nvram->read(addr++); + } + + return 0; +} + +int options_checksum_valid(const struct nvram_accessor *nvram, struct cb_cmos_checksum *option_checksum) +{ + int i; + int checksum_location = option_checksum->location / 8; + u16 checksum = 0, checksum_old; + + for(i = option_checksum->range_start; i <= option_checksum->range_end; i++) { + checksum += nvram->read(i); + } + + checksum_old = ((nvram->read(checksum_location)<<8) | nvram->read(checksum_location+1)); + + return (checksum_old == checksum); +} + +void *get_nvram_bootorder() +{ + const char *name = "bootorder"; + struct cb_header *cbh = find_cb_table(); + + if (!cbh) + return NULL; + struct cb_cmos_option_table *option_table = find_cb_subtable(cbh, CB_TAG_CMOS_OPTION_TABLE); + struct cb_cmos_checksum *option_checksum = find_cb_subtable(cbh, CB_TAG_OPTION_CHECKSUM); + if (option_table == NULL) { + dprintf(1, "Could not find coreboot option table.\n"); + return NULL; + } + + struct cb_cmos_entries *cmos_entry = lookup_cmos_entry(option_table, name); + + if (!cmos_entry) + return NULL; + int cmos_length = cmos_entry->length; + + /* extra byte to ensure 0-terminated strings */ + void *buf = malloc_low(cmos_length+1); + memset(buf, 0, cmos_length+1); + + if(!options_checksum_valid(use_nvram, option_checksum)) { + dprintf(1, "Invalid option checksum.\n"); + return NULL; + } + get_cmos_value(use_nvram, cmos_entry->bit, cmos_entry->length, buf); + + return buf; +} diff --git a/src/nvram.h b/src/nvram.h new file mode 100644 index 00000000..4c9d559e --- /dev/null +++ b/src/nvram.h @@ -0,0 +1,37 @@ +#ifndef __NVRAM_H +#define __NVRAM_H + +#include "types.h" // u32, u8 + +#define RTC_PORT_STANDARD 0x70 +#define RTC_PORT_EXTENDED 0x72 + +struct cb_cmos_option_table { + u32 tag; + u32 size; + u32 header_length; +}; + +#define CB_CMOS_MAX_NAME_LENGTH 32 +struct cb_cmos_entries { + u32 tag; + u32 size; + u32 bit; + u32 length; + u32 config; + u32 config_id; + u8 name[CB_CMOS_MAX_NAME_LENGTH]; +}; + +struct cb_cmos_checksum { + u32 tag; + u32 size; + u32 range_start; + u32 range_end; + u32 location; + u32 type; +}; + +void *get_nvram_bootorder(); + +#endif \ No newline at end of file
On Wed, Nov 20, 2024 at 09:50:58PM +0700, Timothy Kenno Handojo wrote:
This change allows seabios to load bootorder not only from CBFS but also from the NVRAM as well, making use of coreboot's cmos.layout.
What is the use case? qemu? seabios already supports that (see boot_init, the CONFIG_QEMU block).
take care, Gerd
On Thu, Nov 21, 2024 at 11:34 PM Gerd Hoffmann kraxel@redhat.com wrote:
What is the use case? qemu? seabios already supports that (see boot_init, the CONFIG_QEMU block).
This would be for coreboot on real hardware. As for the boot_init, I am not aware of it working for real hardware. Do correct me if I'm wrong.
Regards, Timothy Kenno Handojo
On Fri, Nov 22, 2024 at 07:25:10PM +0700, Timothy Kenno Handojo wrote:
On Thu, Nov 21, 2024 at 11:34 PM Gerd Hoffmann kraxel@redhat.com wrote:
What is the use case? qemu? seabios already supports that (see boot_init, the CONFIG_QEMU block).
This would be for coreboot on real hardware. As for the boot_init, I am not aware of it working for real hardware. Do correct me if I'm wrong.
Yes, the current code is behind CONFIG_QEMU, so it'll not be used. The approach should work on real hardware too though, although the logic probably needs some adjustments to make it consider the coreboot nvram layout.
take care, Gerd