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 <timken...@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 <timken...@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
-- 
2.39.5
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-le...@seabios.org

Reply via email to