diff options
Diffstat (limited to 'config/grub/nvme')
14 files changed, 2053 insertions, 0 deletions
diff --git a/config/grub/nvme/config/payload b/config/grub/nvme/config/payload new file mode 100644 index 00000000..4f3de36e --- /dev/null +++ b/config/grub/nvme/config/payload @@ -0,0 +1,320 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright (C) 2014-2016,2020-2021,2023-2025 Leah Rowe <leah@libreboot.org> +# Copyright (C) 2015 Klemens Nanni <contact@autoboot.org> + +set prefix=(memdisk)/boot/grub + +insmod at_keyboard +insmod usb_keyboard +insmod nativedisk +insmod ehci +insmod ohci +insmod uhci +insmod usb +insmod usbms +insmod regexp + +terminal_input --append at_keyboard +terminal_input --append usb_keyboard +terminal_output --append cbmemc + +# User interface overrides wherever "keystatus" is supported +# Keep SHIFT key pressed before powering on to disable graphics +if keystatus --shift; then + terminal_output --append vga_text +else + gfxpayload=keep + terminal_output --append gfxterm + + for dt in cbfsdisk memdisk; do + for it in png jpg; do + if [ -f (${dt})/background.${it} ]; then + insmod ${it} + background_image (${dt})/background.${it} + fi + done + done +fi + +# Keep CTRL pressed to enable default serial terminal (COM1 or the like) +if keystatus --ctrl; then + serial + terminal_input --append serial + terminal_output --append serial +fi + +# Keep ALT pressed to enable spkmodem +if keystatus --alt; then + terminal_output --append spkmodem +fi + + +set default="0" +if [ -f (cbfsdisk)/timeout.cfg ]; then + source (cbfsdisk)/timeout.cfg +else + set timeout=8 +fi +set grub_scan_disk="nvme ahci ata" +if [ -f (cbfsdisk)/scan.cfg ]; then + source (cbfsdisk)/scan.cfg +fi + +if [ -f (cbfsdisk)/keymap.gkb ]; then + keymap (cbfsdisk)/keymap.gkb +fi + +function really_try_user_config { + set root="${1}" + + if [ -f /"${2}"/grub.cfg ]; then + unset superusers + configfile /"${2}"/grub.cfg + fi +} + +function try_user_config { + # The @/... entries are for cases where the BTRFS filesystem is being used + for dir in grub boot/grub @/grub @/boot/grub grub2 boot/grub2 @/grub2 @/boot/grub2 boot @/boot; do + really_try_user_config "${1}" "${dir}" + done + for dir in ubuntu debian redhat; do + really_try_user_config "${1}" "EFI/${dir}" + done +} +function search_grub { + echo -n "Attempting to load grub.cfg from '${1}' devices" + for i in 0 1 2 3 4 5 6 7 8; do + for part in 1 2 3 4 5 6 7 8 9 10 11 12; do + if [ "${1}" != "nvme" ]; then + try_user_config "(${1}${i},${part})" + else + # TODO: do we care about other namesapces + try_user_config "(nvme${i}n1,${part})" + fi + done + if [ "${1}" != "nvme" ]; then + # raw devices e.g. (ahci0) instead of (ahci0,1) + try_user_config "(${1}${i})" + else + # TODO: do we care about other namesapces + try_user_config "(nvme${i}n1)" + fi + done + echo # Insert newline +} + +function try_isolinux_config { + set root="${1}" + for dir in '' /boot /EFI /@ /@/boot; do + if [ -f "${dir}"/isolinux/isolinux.cfg ]; then + syslinux_configfile -i "${dir}"/isolinux/isolinux.cfg + elif [ -f "${dir}"/syslinux/syslinux.cfg ]; then + syslinux_configfile -s "${dir}"/syslinux/syslinux.cfg + elif [ -f "${dir}"/syslinux/extlinux.conf ]; then + syslinux_configfile -s "${dir}"/syslinux/extlinux.conf + elif [ -f "${dir}"/extlinux/extlinux.conf ]; then + syslinux_configfile -s "${dir}"/extlinux/extlinux.conf + fi + done +} +function search_isolinux { + echo "\nAttempting to parse iso/sys/extlinux config from '${1}' devices" + for i in 0 1 2 3 4 5 6 7 8; do + for part in 1 2 3 4 5 6 7 8 9 10 11 12; do + if [ "${1}" != "nvme" ]; then + try_isolinux_config "(${1}${i},${part})" + else + # TODO: see above + try_isolinux_config "(nvme${i}n1,${part})" + fi + done + if [ "${1}" != "nvme" ]; then + # raw devices e.g. (usb0) instead of (usb0,1) + try_isolinux_config "(${1}${i})" + else + # TODO: do we care about other namesapces + try_isolinux_config "(nvme${i}n1)" + fi + done + echo # Insert newline +} +function try_bootcfg { + try_user_config "${1}" + try_isolinux_config "${1}" +} +function search_bootcfg { + search_grub "${1}" + search_isolinux "${1}" +} +menuentry 'Load Operating System (incl. fully encrypted disks) [o]' --hotkey='o' { + + for grub_disk in ${grub_scan_disk}; do + search_bootcfg ${grub_disk} + done + + # grub device enumeration is very slow, so checks are hardcoded + + raidvol="md/0 md/1 md/2 md/3 md/4 md/5 md/6 md/7 md/8 md/9" + + # in practise, doing multiple redundant checks is perfectly fast + # TODO: optimize grub itself, and use */? here for everything + + for vol in ${raidvol} ; do + try_bootcfg "${vol}" + done + + unset bootdev + for grub_disk in ${grub_scan_disk}; do + for i in 0 1 2 3 4 5 6 7 8; do + for part in 1 2 3 4 5 6 7 8 9 10 11 12; do + if [ "${grub_disk}" = "ahci" ]; then + bootdev="${bootdev} (ahci${i},${part})" + elif [ "${grub_disk}" = "ata" ]; then + bootdev="${bootdev} (ata${i},${part})" + elif [ "${grub_disk}" = "nvme" ]; then + # TODO: do we care about other namesapces + bootdev="${bootdev} (nvme${i}n1,${part})" + fi + done + done + done + + set pager=0 + echo -n "Attempting to unlock encrypted volumes" + for dev in ${bootdev} ${raidvol}; do + if cryptomount "${dev}" ; then break ; fi + done + set pager=1 + echo + + search_bootcfg crypto + + lvmvol="" + + # after cryptomount, lvm volumes might be available + # using * is slow on some machines, but we use it here, + # just once. in so doing, we find every lvm volume + for vol in (*); do + if regexp ^\\(lvm/ $vol; then + lvmvol="${lvmvol} ${vol}" + try_bootcfg "${vol}" + fi + done + + # user might have put luks inside lvm + set pager=0 + echo "Attempting to unlock encrypted LVMs" + for vol in ${lvmvol}; do + cryptomount "$vol" + done + set pager=1 + echo + + search_bootcfg crypto + + true # Prevent pager requiring to accept each line instead of whole screen +} + +menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on USB [s]' --hotkey='s' { + search_bootcfg usb +} +menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on AHCI [a]' --hotkey='a' { + search_bootcfg ahci +} +menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on ATA/IDE [d]' --hotkey='d' { + search_bootcfg ata +} +menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on NVMe [e]' --hotkey='e' { + search_bootcfg nvme +} +if [ -f (cbfsdisk)/grub.cfg ]; then +menuentry 'Load configuration (grub.cfg) in CBFS [t]' --hotkey='t' { + set root='(cbfsdisk)' + if [ -f /grub.cfg ]; then + configfile /grub.cfg + fi +} +fi +if [ -f (cbfsdisk)/grubtest.cfg ]; then +menuentry 'Load test configuration (grubtest.cfg) inside of CBFS [t]' --hotkey='t' { + set root='(cbfsdisk)' + if [ -f /grubtest.cfg ]; then + configfile /grubtest.cfg + fi +} +fi +if [ -f (cbfsdisk)/u-boot ]; then +menuentry 'U-Boot i386 payload (experimental) [u]' --hotkey='u' { + set root='cbfsdisk' + chainloader /u-boot +} +fi +if [ -f (cbfsdisk)/seabios.elf ]; then +if [ -f (cbfsdisk)/img/u-boot ]; then +menuentry 'Load SeaBIOS (U-Boot UEFI available in the ESC menu) [b]' --hotkey='b' { + set root='cbfsdisk' + chainloader /seabios.elf +} +else +menuentry 'Load SeaBIOS [b]' --hotkey='b' { + set root='cbfsdisk' + chainloader /seabios.elf +} +fi +fi +if [ -f (cbfsdisk)/img/grub2 ]; then +if [ -f (cbfsdisk)/img/u-boot ]; then +menuentry 'Return to SeaBIOS (U-Boot UEFI available in the ESC menu) [b]' --hotkey='b' { + set root='cbfsdisk' + chainloader /fallback/payload +} +else +menuentry 'Return to SeaBIOS [b]' --hotkey='b' { + set root='cbfsdisk' + chainloader /fallback/payload +} +fi +fi +menuentry 'Poweroff [p]' --hotkey='p' { + halt +} +menuentry 'Reboot [r]' --hotkey='r' { + reboot +} +if [ -f (cbfsdisk)/img/memtest ]; then +menuentry 'Load MemTest86+ [m]' --hotkey='m' { + set root='cbfsdisk' + chainloader /img/memtest +} +fi + +submenu 'Other [z]' --hotkey='z' { + menuentry 'Enable default serial terminal [s]' --hotkey='s' { + serial + terminal_input --append serial + terminal_output --append serial + } + + menuentry 'Disable default serial terminal' { + terminal_input --remove serial + terminal_output --remove serial + } + + menuentry 'Enable gfxterm' { + terminal_output --append gfxterm + terminal_output --remove vga_text + } + menuentry 'Disable gfxterm [g]' --hotkey='g' { + terminal_output --remove gfxterm + terminal_output --append vga_text + } + + menuentry 'Enable spkmodem [a]' --hotkey='a' { + terminal_output --append spkmodem + } + + menuentry 'Disable spkmodem [z]' --hotkey='z' { + terminal_output --remove spkmodem + } +} diff --git a/config/grub/nvme/patches/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch b/config/grub/nvme/patches/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch new file mode 100644 index 00000000..41194e98 --- /dev/null +++ b/config/grub/nvme/patches/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch @@ -0,0 +1,90 @@ +From 2c953c9160cbed357437891cf1790aeb1c2bd0f5 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Sun, 31 Oct 2021 03:47:05 +0000 +Subject: [PATCH 01/10] mitigate grub's missing characters for borders/arrow + characters + +This cleans up the display on the main screen in GRUB. + +Just don't draw a border, at all. +--- + grub-core/normal/menu_text.c | 49 ++---------------------------------- + 1 file changed, 2 insertions(+), 47 deletions(-) + +diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c +index 9c383e64a..8ec1dd1e8 100644 +--- a/grub-core/normal/menu_text.c ++++ b/grub-core/normal/menu_text.c +@@ -108,47 +108,6 @@ grub_print_message_indented (const char *msg, int margin_left, int margin_right, + grub_print_message_indented_real (msg, margin_left, margin_right, term, 0); + } + +-static void +-draw_border (struct grub_term_output *term, const struct grub_term_screen_geometry *geo) +-{ +- int i; +- +- grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL); +- +- grub_term_gotoxy (term, (struct grub_term_coordinate) { geo->first_entry_x - 1, +- geo->first_entry_y - 1 }); +- grub_putcode (GRUB_UNICODE_CORNER_UL, term); +- for (i = 0; i < geo->entry_width + 1; i++) +- grub_putcode (GRUB_UNICODE_HLINE, term); +- grub_putcode (GRUB_UNICODE_CORNER_UR, term); +- +- for (i = 0; i < geo->num_entries; i++) +- { +- grub_term_gotoxy (term, (struct grub_term_coordinate) { geo->first_entry_x - 1, +- geo->first_entry_y + i }); +- grub_putcode (GRUB_UNICODE_VLINE, term); +- grub_term_gotoxy (term, +- (struct grub_term_coordinate) { geo->first_entry_x + geo->entry_width + 1, +- geo->first_entry_y + i }); +- grub_putcode (GRUB_UNICODE_VLINE, term); +- } +- +- grub_term_gotoxy (term, +- (struct grub_term_coordinate) { geo->first_entry_x - 1, +- geo->first_entry_y - 1 + geo->num_entries + 1 }); +- grub_putcode (GRUB_UNICODE_CORNER_LL, term); +- for (i = 0; i < geo->entry_width + 1; i++) +- grub_putcode (GRUB_UNICODE_HLINE, term); +- grub_putcode (GRUB_UNICODE_CORNER_LR, term); +- +- grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL); +- +- grub_term_gotoxy (term, +- (struct grub_term_coordinate) { geo->first_entry_x - 1, +- (geo->first_entry_y - 1 + geo->num_entries +- + GRUB_TERM_MARGIN + 1) }); +-} +- + static int + print_message (int nested, int edit, struct grub_term_output *term, int dry_run) + { +@@ -167,10 +126,8 @@ command-line or ESC to discard edits and return to the GRUB menu."), + { + char *msg_translated; + +- msg_translated = grub_xasprintf (_("Use the %C and %C keys to select which " +- "entry is highlighted."), +- GRUB_UNICODE_UPARROW, +- GRUB_UNICODE_DOWNARROW); ++ msg_translated = grub_xasprintf (_("Use the arrow keys to select which " ++ "entry is highlighted.")); + if (!msg_translated) + return 0; + ret += grub_print_message_indented_real (msg_translated, STANDARD_MARGIN, +@@ -413,8 +370,6 @@ grub_menu_init_page (int nested, int edit, + + grub_term_normal_color = grub_color_menu_normal; + grub_term_highlight_color = grub_color_menu_highlight; +- if (geo->border) +- draw_border (term, geo); + grub_term_normal_color = old_color_normal; + grub_term_highlight_color = old_color_highlight; + geo->timeout_y = geo->first_entry_y + geo->num_entries +-- +2.47.3 + diff --git a/config/grub/nvme/patches/0002-say-the-name-libreboot-in-the-grub-menu.patch b/config/grub/nvme/patches/0002-say-the-name-libreboot-in-the-grub-menu.patch new file mode 100644 index 00000000..359f31f8 --- /dev/null +++ b/config/grub/nvme/patches/0002-say-the-name-libreboot-in-the-grub-menu.patch @@ -0,0 +1,25 @@ +From 71623385f90580b2138110cf00ee66b7a05abcf6 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Sat, 19 Nov 2022 16:30:24 +0000 +Subject: [PATCH 02/10] say the name libreboot, in the grub menu + +--- + grub-core/normal/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c +index de9a3f961..bed502cde 100644 +--- a/grub-core/normal/main.c ++++ b/grub-core/normal/main.c +@@ -215,7 +215,7 @@ grub_normal_init_page (struct grub_term_output *term, + + grub_term_cls (term); + +- msg_formatted = grub_xasprintf (_("GNU GRUB version %s"), PACKAGE_VERSION); ++ msg_formatted = grub_xasprintf (_("Libreboot 26.01 Magnanimous Max (GRUB menu): https://libreboot.org/")); + if (!msg_formatted) + return; + +-- +2.47.3 + diff --git a/config/grub/nvme/patches/0003-at_keyboard-coreboot-force-scancodes2-translate.patch b/config/grub/nvme/patches/0003-at_keyboard-coreboot-force-scancodes2-translate.patch new file mode 100644 index 00000000..ab26c6b2 --- /dev/null +++ b/config/grub/nvme/patches/0003-at_keyboard-coreboot-force-scancodes2-translate.patch @@ -0,0 +1,107 @@ +From a21c5e997d6247d713717745b5f7bd380b98fce1 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Mon, 30 Oct 2023 22:19:21 +0000 +Subject: [PATCH 03/10] at_keyboard coreboot: force scancodes2+translate + +Scan code set 2 with translation should be assumed in +every case, as the default starting position. + +However, GRUB is trying to detect and use other modes +such as set 2 without translation, or set 1 without +translation from set 2; it also detects no-mode and +assumes mode 1, on really old keyboards. + +The current behaviour has been retained, for everything +except GRUB_MACHINE_COREBOOT; for the latter, scan code +set 2 with translation is hardcoded, and forced in code. + +This is required to make keyboard initialisation work on +the MEC5035 EC used by the Dell Latitude E6400, when +running GRUB as a coreboot payload on that laptop. The +EC reports scancode set 2 with translation when probed, +but actually only outputs scancode set 1. + +Since GRUB is attempting to use it without translation, +and since the machine reports set 2 with translation, +but only ever outputs set 1 scancodes, this results in +wrong keypresses for every key. + +This fix fixed that, by forcing set 2 with translation, +treating it as set 1, but only on coreboot. This is the +same behaviour used in GNU+Linux systems and SeaBIOS. +With this change, GRUB keyboard initialisation now works +just fine on those machines. + +This has *also* been tested on other coreboot machines +running GRUB; several HP EliteBooks, ThinkPads and +Dell Precision T1650. All seems to work just fine. + +Signed-off-by: Leah Rowe <leah@libreboot.org> +--- + grub-core/term/at_keyboard.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c +index f8a129eb7..8207225c2 100644 +--- a/grub-core/term/at_keyboard.c ++++ b/grub-core/term/at_keyboard.c +@@ -138,6 +138,7 @@ write_mode (int mode) + return (i != GRUB_AT_TRIES); + } + ++#if !defined (GRUB_MACHINE_COREBOOT) + static int + query_mode (void) + { +@@ -161,10 +162,12 @@ query_mode (void) + return 3; + return 0; + } ++#endif + + static void + set_scancodes (void) + { ++#if !defined (GRUB_MACHINE_COREBOOT) + /* You must have visited computer museum. Keyboard without scancode set + knowledge. Assume XT. */ + if (!grub_keyboard_orig_set) +@@ -173,20 +176,33 @@ set_scancodes (void) + ps2_state.current_set = 1; + return; + } ++#endif + + #if !USE_SCANCODE_SET + ps2_state.current_set = 1; + return; +-#else ++#endif + ++#if defined (GRUB_MACHINE_COREBOOT) ++ /* enable translation */ ++ grub_keyboard_controller_write (grub_keyboard_controller_orig ++ & ~KEYBOARD_AT_DISABLE); ++#else ++ /* if not coreboot, disable translation and try mode 2 first, before 1 */ + grub_keyboard_controller_write (grub_keyboard_controller_orig + & ~KEYBOARD_AT_TRANSLATE + & ~KEYBOARD_AT_DISABLE); ++#endif + + keyboard_controller_wait_until_ready (); + grub_outb (KEYBOARD_COMMAND_ENABLE, KEYBOARD_REG_DATA); +- + write_mode (2); ++ ++#if defined (GRUB_MACHINE_COREBOOT) ++ /* mode 2 with translation, so make grub treat as set 1 */ ++ ps2_state.current_set = 1; ++#else ++ /* if not coreboot, translation isn't set; test 2 and fall back to 1 */ + ps2_state.current_set = query_mode (); + grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set); + if (ps2_state.current_set == 2) +-- +2.47.3 + diff --git a/config/grub/nvme/patches/0004-keylayouts-don-t-print-Unknown-key-message.patch b/config/grub/nvme/patches/0004-keylayouts-don-t-print-Unknown-key-message.patch new file mode 100644 index 00000000..68dfb2f7 --- /dev/null +++ b/config/grub/nvme/patches/0004-keylayouts-don-t-print-Unknown-key-message.patch @@ -0,0 +1,38 @@ +From 062379b1cd5a4f99324c625fb371c29eb00ad4a2 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Tue, 31 Oct 2023 10:33:28 +0000 +Subject: [PATCH 04/10] keylayouts: don't print "Unknown key" message + +on keyboards with stuck keys, this results in GRUB just +spewing it repeatedly, preventing use of GRUB. + +in such cases, it's still possible to use the keyboard, +and we should let the user at least boot. + +it often appears when people plug in faulty usb keyboards, +but can appear for laptop keyboards too; one of my e6400 +has stuck keys. + +with this patch, grub should be a bit more reliable in +terms of user experience, when the keyboard is faulty. + +Signed-off-by: Leah Rowe <leah@libreboot.org> +--- + grub-core/commands/keylayouts.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/grub-core/commands/keylayouts.c b/grub-core/commands/keylayouts.c +index aa3ba34f2..445fa0601 100644 +--- a/grub-core/commands/keylayouts.c ++++ b/grub-core/commands/keylayouts.c +@@ -174,7 +174,6 @@ grub_term_map_key (grub_keyboard_key_t code, int status) + key = map_key_core (code, status, &alt_gr_consumed); + + if (key == 0 || key == GRUB_TERM_SHIFT) { +- grub_printf ("Unknown key 0x%x detected\n", code); + return GRUB_TERM_NO_KEY; + } + +-- +2.47.3 + diff --git a/config/grub/nvme/patches/0005-don-t-print-missing-prefix-errors-on-the-screen.patch b/config/grub/nvme/patches/0005-don-t-print-missing-prefix-errors-on-the-screen.patch new file mode 100644 index 00000000..53dbdd87 --- /dev/null +++ b/config/grub/nvme/patches/0005-don-t-print-missing-prefix-errors-on-the-screen.patch @@ -0,0 +1,102 @@ +From e77ca67985c1bef8afd93587232da3d298a8dd51 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Sun, 5 Nov 2023 16:14:58 +0000 +Subject: [PATCH 05/10] don't print missing prefix errors on the screen + +we do actually set the prefix. this patch modifies +grub to still set grub_errno and return accordingly, +so the behaviour is otherwise identical, but it will +no longer print a warning message on the screen. + +Signed-off-by: Leah Rowe <leah@libreboot.org> +--- + grub-core/commands/keylayouts.c | 2 +- + grub-core/commands/loadenv.c | 2 +- + grub-core/commands/nativedisk.c | 2 +- + grub-core/efiemu/main.c | 3 +-- + grub-core/font/font.c | 2 +- + grub-core/kern/dl.c | 2 +- + 6 files changed, 6 insertions(+), 7 deletions(-) + +diff --git a/grub-core/commands/keylayouts.c b/grub-core/commands/keylayouts.c +index 445fa0601..00bcf7025 100644 +--- a/grub-core/commands/keylayouts.c ++++ b/grub-core/commands/keylayouts.c +@@ -211,7 +211,7 @@ grub_cmd_keymap (struct grub_command *cmd __attribute__ ((unused)), + { + const char *prefix = grub_env_get ("prefix"); + if (!prefix) +- return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("variable `%s' isn't set"), "prefix"); ++ return (grub_errno = GRUB_ERR_BAD_ARGUMENT); + filename = grub_xasprintf ("%s/layouts/%s.gkb", prefix, argv[0]); + if (!filename) + return grub_errno; +diff --git a/grub-core/commands/loadenv.c b/grub-core/commands/loadenv.c +index 166445849..699b39bfa 100644 +--- a/grub-core/commands/loadenv.c ++++ b/grub-core/commands/loadenv.c +@@ -58,7 +58,7 @@ open_envblk_file (char *filename, + prefix = grub_env_get ("prefix"); + if (! prefix) + { +- grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix"); ++ grub_errno = GRUB_ERR_FILE_NOT_FOUND; + return 0; + } + +diff --git a/grub-core/commands/nativedisk.c b/grub-core/commands/nativedisk.c +index 580c8d3b0..6806bff9c 100644 +--- a/grub-core/commands/nativedisk.c ++++ b/grub-core/commands/nativedisk.c +@@ -186,7 +186,7 @@ grub_cmd_nativedisk (grub_command_t cmd __attribute__ ((unused)), + prefix = grub_env_get ("prefix"); + + if (! prefix) +- return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix"); ++ return (grub_errno = GRUB_ERR_FILE_NOT_FOUND); + + if (prefix) + path_prefix = (prefix[0] == '(') ? grub_strchr (prefix, ')') : NULL; +diff --git a/grub-core/efiemu/main.c b/grub-core/efiemu/main.c +index e7037f4ed..e5d4dbff1 100644 +--- a/grub-core/efiemu/main.c ++++ b/grub-core/efiemu/main.c +@@ -231,8 +231,7 @@ grub_efiemu_autocore (void) + prefix = grub_env_get ("prefix"); + + if (! prefix) +- return grub_error (GRUB_ERR_FILE_NOT_FOUND, +- N_("variable `%s' isn't set"), "prefix"); ++ return (grub_errno = GRUB_ERR_FILE_NOT_FOUND); + + suffix = grub_efiemu_get_default_core_name (); + +diff --git a/grub-core/font/font.c b/grub-core/font/font.c +index 18de52562..2a0fea6c8 100644 +--- a/grub-core/font/font.c ++++ b/grub-core/font/font.c +@@ -461,7 +461,7 @@ grub_font_load (const char *filename) + + if (!prefix) + { +- grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix"); ++ grub_errno = GRUB_ERR_FILE_NOT_FOUND; + goto fail; + } + file = try_open_from_prefix (prefix, filename); +diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c +index de8c3aa8d..eac3ea48d 100644 +--- a/grub-core/kern/dl.c ++++ b/grub-core/kern/dl.c +@@ -880,7 +880,7 @@ grub_dl_load (const char *name) + return 0; + + if (! grub_dl_dir) { +- grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix"); ++ grub_errno = GRUB_ERR_FILE_NOT_FOUND; + return 0; + } + +-- +2.47.3 + diff --git a/config/grub/nvme/patches/0006-don-t-print-error-if-module-not-found.patch b/config/grub/nvme/patches/0006-don-t-print-error-if-module-not-found.patch new file mode 100644 index 00000000..3851c7fe --- /dev/null +++ b/config/grub/nvme/patches/0006-don-t-print-error-if-module-not-found.patch @@ -0,0 +1,34 @@ +From 9412b6be27bce2d79f154761387d35f44184c069 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Sun, 5 Nov 2023 16:36:22 +0000 +Subject: [PATCH 06/10] don't print error if module not found + +still set grub_errno accordingly, and otherwise +behave the same. in libreboot, we remove a lot of +modules but then rely on loading a grub.cfg +provided by a distro; in almost all cases that works, +but also in almost all cases, that will try to load +a module we don't actually need, but then it prints +a message. this can annoy some users, so silence it. + +Signed-off-by: Leah Rowe <leah@libreboot.org> +--- + grub-core/kern/dl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c +index eac3ea48d..6d67ba54f 100644 +--- a/grub-core/kern/dl.c ++++ b/grub-core/kern/dl.c +@@ -510,7 +510,7 @@ grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e) + + s = grub_dl_find_section (e, ".modname"); + if (!s) +- return grub_error (GRUB_ERR_BAD_MODULE, "no module name found"); ++ return (grub_errno = GRUB_ERR_BAD_MODULE); + + mod->name = grub_strdup ((char *) e + s->sh_offset); + if (! mod->name) +-- +2.47.3 + diff --git a/config/grub/nvme/patches/0007-don-t-print-empty-error-messages.patch b/config/grub/nvme/patches/0007-don-t-print-empty-error-messages.patch new file mode 100644 index 00000000..aa42420f --- /dev/null +++ b/config/grub/nvme/patches/0007-don-t-print-empty-error-messages.patch @@ -0,0 +1,31 @@ +From 2ad50d482cae148df832ccd68f90b8111684ecde Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Sun, 5 Nov 2023 17:25:20 +0000 +Subject: [PATCH 07/10] don't print empty error messages + +this is part two of the quest to kill the prefix +error message. after i disabled prefix-related +messages, it still printed "error: ." on screen. + +Signed-off-by: Leah Rowe <leah@libreboot.org> +--- + grub-core/kern/err.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/grub-core/kern/err.c b/grub-core/kern/err.c +index ba04b57fb..dab62646d 100644 +--- a/grub-core/kern/err.c ++++ b/grub-core/kern/err.c +@@ -116,7 +116,8 @@ grub_print_error (void) + { + if (grub_errno != GRUB_ERR_NONE) + { +- grub_err_printf (_("error: %s.\n"), grub_errmsg); ++ if (grub_strlen(grub_errmsg) > 0) ++ grub_err_printf (_("error: %s.\n"), grub_errmsg); + grub_err_printed_errors++; + } + } +-- +2.47.3 + diff --git a/config/grub/nvme/patches/0008-Add-native-NVMe-driver-based-on-SeaBIOS.patch b/config/grub/nvme/patches/0008-Add-native-NVMe-driver-based-on-SeaBIOS.patch new file mode 100644 index 00000000..e0b40f2f --- /dev/null +++ b/config/grub/nvme/patches/0008-Add-native-NVMe-driver-based-on-SeaBIOS.patch @@ -0,0 +1,1074 @@ +From 202e20bef3766480aee18e679b9306387be615dc Mon Sep 17 00:00:00 2001 +From: Mate Kukri <km@mkukri.xyz> +Date: Mon, 20 May 2024 11:43:35 +0100 +Subject: [PATCH 08/10] Add native NVMe driver based on SeaBIOS + +Tested to successfully boot Debian on QEMU and OptiPlex 3050. + +Signed-off-by: Mate Kukri <km@mkukri.xyz> +--- + Makefile.am | 2 +- + grub-core/Makefile.core.def | 6 + + grub-core/commands/nativedisk.c | 1 + + grub-core/disk/nvme-int.h | 208 +++++++++ + grub-core/disk/nvme.c | 781 ++++++++++++++++++++++++++++++++ + include/grub/disk.h | 1 + + 6 files changed, 998 insertions(+), 1 deletion(-) + create mode 100644 grub-core/disk/nvme-int.h + create mode 100644 grub-core/disk/nvme.c + +diff --git a/Makefile.am b/Makefile.am +index 43635d5ff..2c86dbbf6 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -434,7 +434,7 @@ if COND_i386_coreboot + FS_PAYLOAD_MODULES ?= $(shell cat grub-core/fs.lst) + default_payload.elf: grub-mkstandalone grub-mkimage FORCE + test -f $@ && rm $@ || true +- pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg ++ pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata nvme ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg + endif + + endif +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 0cf155128..176d35364 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2773,3 +2773,9 @@ module = { + cflags = '-Wno-uninitialized'; + cppflags = '-I$(srcdir)/lib/libtasn1-grub -I$(srcdir)/tests/asn1/'; + }; ++ ++module = { ++ name = nvme; ++ common = disk/nvme.c; ++ enable = pci; ++}; +diff --git a/grub-core/commands/nativedisk.c b/grub-core/commands/nativedisk.c +index 6806bff9c..fd68a513e 100644 +--- a/grub-core/commands/nativedisk.c ++++ b/grub-core/commands/nativedisk.c +@@ -78,6 +78,7 @@ get_uuid (const char *name, char **uuid, int getnative) + case GRUB_DISK_DEVICE_ATA_ID: + case GRUB_DISK_DEVICE_SCSI_ID: + case GRUB_DISK_DEVICE_XEN: ++ case GRUB_DISK_DEVICE_NVME_ID: + if (getnative) + break; + /* FALLTHROUGH */ +diff --git a/grub-core/disk/nvme-int.h b/grub-core/disk/nvme-int.h +new file mode 100644 +index 000000000..1295b58aa +--- /dev/null ++++ b/grub-core/disk/nvme-int.h +@@ -0,0 +1,208 @@ ++// NVMe datastructures and constants ++// ++// Copyright 2017 Amazon.com, Inc. or its affiliates. ++// ++// This file may be distributed under the terms of the GNU LGPLv3 license. ++ ++#ifndef __NVME_INT_H ++#define __NVME_INT_H ++ ++#include <grub/types.h> ++ ++/* Data structures */ ++ ++/* The register file of a NVMe host controller. This struct follows the naming ++ scheme in the NVMe specification. */ ++struct nvme_reg { ++ grub_uint64_t cap; /* controller capabilities */ ++ grub_uint32_t vs; /* version */ ++ grub_uint32_t intms; /* interrupt mask set */ ++ grub_uint32_t intmc; /* interrupt mask clear */ ++ grub_uint32_t cc; /* controller configuration */ ++ grub_uint32_t _res0; ++ grub_uint32_t csts; /* controller status */ ++ grub_uint32_t _res1; ++ grub_uint32_t aqa; /* admin queue attributes */ ++ grub_uint64_t asq; /* admin submission queue base address */ ++ grub_uint64_t acq; /* admin completion queue base address */ ++}; ++ ++/* Submission queue entry */ ++struct nvme_sqe { ++ union { ++ grub_uint32_t dword[16]; ++ struct { ++ grub_uint32_t cdw0; /* Command DWORD 0 */ ++ grub_uint32_t nsid; /* Namespace ID */ ++ grub_uint64_t _res0; ++ grub_uint64_t mptr; /* metadata ptr */ ++ ++ grub_uint64_t dptr_prp1; ++ grub_uint64_t dptr_prp2; ++ }; ++ }; ++}; ++ ++/* Completion queue entry */ ++struct nvme_cqe { ++ union { ++ grub_uint32_t dword[4]; ++ struct { ++ grub_uint32_t cdw0; ++ grub_uint32_t _res0; ++ grub_uint16_t sq_head; ++ grub_uint16_t sq_id; ++ grub_uint16_t cid; ++ grub_uint16_t status; ++ }; ++ }; ++}; ++ ++/* The common part of every submission or completion queue. */ ++struct nvme_queue { ++ grub_uint32_t *dbl; /* doorbell */ ++ grub_uint16_t mask; /* length - 1 */ ++}; ++ ++struct nvme_cq { ++ struct nvme_queue common; ++ struct nvme_cqe *cqe; ++ ++ /* We have read upto (but not including) this entry in the queue. */ ++ grub_uint16_t head; ++ ++ /* The current phase bit the controller uses to indicate that it has written ++ a new entry. This is inverted after each wrap. */ ++ unsigned phase : 1; ++}; ++ ++struct nvme_sq { ++ struct nvme_queue common; ++ struct nvme_sqe *sqe; ++ ++ /* Corresponding completion queue. We only support a single SQ per CQ. */ ++ struct nvme_cq *cq; ++ ++ /* The last entry the controller has fetched. */ ++ grub_uint16_t head; ++ ++ /* The last value we have written to the tail doorbell. */ ++ grub_uint16_t tail; ++}; ++ ++struct nvme_ctrl { ++ grub_pci_device_t pci; ++ struct nvme_reg volatile *reg; ++ ++ grub_uint32_t ctrlnum; ++ ++ grub_uint32_t doorbell_stride; /* in bytes */ ++ ++ struct nvme_sq admin_sq; ++ struct nvme_cq admin_cq; ++ ++ grub_uint32_t ns_count; ++ ++ struct nvme_sq io_sq; ++ struct nvme_cq io_cq; ++}; ++ ++struct nvme_namespace { ++ struct nvme_namespace *next; ++ struct nvme_namespace **prev; ++ ++ char *devname; ++ ++ grub_uint32_t nsnum; ++ ++ struct nvme_ctrl *ctrl; ++ ++ grub_uint32_t ns_id; ++ ++ grub_uint64_t lba_count; /* The total amount of sectors. */ ++ ++ grub_uint32_t block_size; ++ grub_uint32_t metadata_size; ++ grub_uint32_t max_req_size; ++}; ++ ++/* Data structures for NVMe admin identify commands */ ++ ++struct nvme_identify_ctrl { ++ grub_uint16_t vid; ++ grub_uint16_t ssvid; ++ char sn[20]; ++ char mn[40]; ++ char fr[8]; ++ ++ grub_uint8_t rab; ++ grub_uint8_t ieee[3]; ++ grub_uint8_t cmic; ++ grub_uint8_t mdts; ++ ++ char _boring[516 - 78]; ++ ++ grub_uint32_t nn; /* number of namespaces */ ++}; ++ ++struct nvme_identify_ns_list { ++ grub_uint32_t ns_id[1024]; ++}; ++ ++struct nvme_lba_format { ++ grub_uint16_t ms; ++ grub_uint8_t lbads; ++ grub_uint8_t rp; ++}; ++ ++struct nvme_identify_ns { ++ grub_uint64_t nsze; ++ grub_uint64_t ncap; ++ grub_uint64_t nuse; ++ grub_uint8_t nsfeat; ++ grub_uint8_t nlbaf; ++ grub_uint8_t flbas; ++ ++ char _boring[128 - 27]; ++ ++ struct nvme_lba_format lbaf[16]; ++}; ++ ++union nvme_identify { ++ struct nvme_identify_ns ns; ++ struct nvme_identify_ctrl ctrl; ++ struct nvme_identify_ns_list ns_list; ++}; ++ ++/* NVMe constants */ ++ ++#define NVME_CAP_CSS_NVME (1ULL << 37) ++ ++#define NVME_CSTS_FATAL (1U << 1) ++#define NVME_CSTS_RDY (1U << 0) ++ ++#define NVME_CC_EN (1U << 0) ++ ++#define NVME_SQE_OPC_ADMIN_CREATE_IO_SQ 1U ++#define NVME_SQE_OPC_ADMIN_CREATE_IO_CQ 5U ++#define NVME_SQE_OPC_ADMIN_IDENTIFY 6U ++ ++#define NVME_SQE_OPC_IO_WRITE 1U ++#define NVME_SQE_OPC_IO_READ 2U ++ ++#define NVME_ADMIN_IDENTIFY_CNS_ID_NS 0U ++#define NVME_ADMIN_IDENTIFY_CNS_ID_CTRL 1U ++#define NVME_ADMIN_IDENTIFY_CNS_GET_NS_LIST 2U ++ ++#define NVME_CQE_DW3_P (1U << 16) ++ ++#define NVME_PAGE_SIZE 4096 ++#define NVME_PAGE_MASK ~(NVME_PAGE_SIZE - 1) ++ ++/* Length for the queue entries. */ ++#define NVME_SQE_SIZE_LOG 6 ++#define NVME_CQE_SIZE_LOG 4 ++ ++#endif ++ ++/* EOF */ +diff --git a/grub-core/disk/nvme.c b/grub-core/disk/nvme.c +new file mode 100644 +index 000000000..093237c70 +--- /dev/null ++++ b/grub-core/disk/nvme.c +@@ -0,0 +1,781 @@ ++// Low level NVMe disk access ++// ++// Based on SeaBIOS NVMe driver - Copyright 2017 Amazon.com, Inc. or its affiliates. ++// Port to GRUB2 done by Mate Kukri ++// ++// This file may be distributed under the terms of the GNU LGPLv3 license. ++ ++#include <grub/disk.h> ++#include <grub/dl.h> ++#include <grub/pci.h> ++#include "nvme-int.h" ++ ++GRUB_MOD_LICENSE ("GPLv3"); /* LGPLv3 in reality but it is GPLv3 compatible */ ++ ++static grub_uint32_t grub_nvme_ctrlcnt; ++static grub_uint32_t grub_nvme_nscnt; ++ ++static struct nvme_namespace *grub_nvme_namespaces; ++ ++// Page aligned "dma bounce buffer" of size NVME_PAGE_SIZE ++static void *nvme_dma_buffer; ++ ++static void * ++zalloc_page_aligned(grub_uint32_t size) ++{ ++ void *res = grub_memalign(NVME_PAGE_SIZE, size); ++ if (res) grub_memset(res, 0, size); ++ return res; ++} ++ ++static void ++nvme_init_queue_common(struct nvme_ctrl *ctrl, struct nvme_queue *q, grub_uint16_t q_idx, ++ grub_uint16_t length) ++{ ++ grub_memset(q, 0, sizeof(*q)); ++ q->dbl = (grub_uint32_t *)((char *)ctrl->reg + 0x1000 + q_idx * ctrl->doorbell_stride); ++ grub_dprintf("nvme", " q %p q_idx %u dbl %p\n", q, q_idx, q->dbl); ++ q->mask = length - 1; ++} ++ ++static int ++nvme_init_sq(struct nvme_ctrl *ctrl, struct nvme_sq *sq, grub_uint16_t q_idx, grub_uint16_t length, ++ struct nvme_cq *cq) ++{ ++ nvme_init_queue_common(ctrl, &sq->common, q_idx, length); ++ sq->sqe = zalloc_page_aligned(sizeof(*sq->sqe) * length); ++ ++ if (!sq->sqe) { ++ return -1; ++ } ++ ++ grub_dprintf("nvme", "sq %p q_idx %u sqe %p\n", sq, q_idx, sq->sqe); ++ sq->cq = cq; ++ sq->head = 0; ++ sq->tail = 0; ++ ++ return 0; ++} ++ ++static int ++nvme_init_cq(struct nvme_ctrl *ctrl, struct nvme_cq *cq, grub_uint16_t q_idx, grub_uint16_t length) ++{ ++ nvme_init_queue_common(ctrl, &cq->common, q_idx, length); ++ cq->cqe = zalloc_page_aligned(sizeof(*cq->cqe) * length); ++ if (!cq->cqe) { ++ return -1; ++ } ++ ++ cq->head = 0; ++ ++ /* All CQE phase bits are initialized to zero. This means initially we wait ++ for the host controller to set these to 1. */ ++ cq->phase = 1; ++ ++ return 0; ++} ++ ++static int ++nvme_poll_cq(struct nvme_cq *cq) ++{ ++ grub_uint32_t dw3 = *(volatile grub_uint32_t *) &cq->cqe[cq->head].dword[3]; ++ return (!!(dw3 & NVME_CQE_DW3_P) == cq->phase); ++} ++ ++static int ++nvme_is_cqe_success(struct nvme_cqe const *cqe) ++{ ++ return ((cqe->status >> 1) & 0xFF) == 0; ++} ++ ++static struct nvme_cqe ++nvme_error_cqe(void) ++{ ++ struct nvme_cqe r; ++ ++ /* 0xFF is a vendor specific status code != success. Should be okay for ++ indicating failure. */ ++ grub_memset(&r, 0xFF, sizeof(r)); ++ return r; ++} ++ ++static struct nvme_cqe ++nvme_consume_cqe(struct nvme_sq *sq) ++{ ++ struct nvme_cq *cq = sq->cq; ++ ++ if (!nvme_poll_cq(cq)) { ++ /* Cannot consume a completion queue entry, if there is none ready. */ ++ return nvme_error_cqe(); ++ } ++ ++ struct nvme_cqe *cqe = &cq->cqe[cq->head]; ++ grub_uint16_t cq_next_head = (cq->head + 1) & cq->common.mask; ++ grub_dprintf("nvme", "cq %p head %u -> %u\n", cq, cq->head, cq_next_head); ++ if (cq_next_head < cq->head) { ++ grub_dprintf("nvme", "cq %p wrap\n", cq); ++ cq->phase = ~cq->phase; ++ } ++ cq->head = cq_next_head; ++ ++ /* Update the submission queue head. */ ++ if (cqe->sq_head != sq->head) { ++ sq->head = cqe->sq_head; ++ grub_dprintf("nvme", "sq %p advanced to %u\n", sq, cqe->sq_head); ++ } ++ ++ /* Tell the controller that we consumed the completion. */ ++ *(volatile grub_uint32_t *) cq->common.dbl = cq->head; ++ ++ return *cqe; ++} ++ ++static struct nvme_cqe ++nvme_wait(struct nvme_sq *sq) ++{ ++ // static const unsigned nvme_timeout = 5000 /* ms */; ++ // grub_uint32_t to = timer_calc(nvme_timeout); ++ while (!nvme_poll_cq(sq->cq)) { ++ /* FIXME ++ yield(); ++ ++ if (timer_check(to)) { ++ warn_timeout(); ++ return nvme_error_cqe(); ++ }*/ ++ } ++ ++ return nvme_consume_cqe(sq); ++} ++ ++/* Returns the next submission queue entry (or NULL if the queue is full). It ++ also fills out Command Dword 0 and clears the rest. */ ++static struct nvme_sqe * ++nvme_get_next_sqe(struct nvme_sq *sq, grub_uint8_t opc, void *metadata, void *data, void *data2) ++{ ++ if (((sq->head + 1) & sq->common.mask) == sq->tail) { ++ grub_dprintf("nvme", "submission queue is full\n"); ++ return NULL; ++ } ++ ++ struct nvme_sqe *sqe = &sq->sqe[sq->tail]; ++ grub_dprintf("nvme", "sq %p next_sqe %u\n", sq, sq->tail); ++ ++ grub_memset(sqe, 0, sizeof(*sqe)); ++ sqe->cdw0 = opc | (sq->tail << 16 /* CID */); ++ sqe->mptr = (grub_uint32_t)metadata; ++ sqe->dptr_prp1 = (grub_uint32_t)data; ++ sqe->dptr_prp2 = (grub_uint32_t)data2; ++ ++ return sqe; ++} ++ ++/* Call this after you've filled out an sqe that you've got from nvme_get_next_sqe. */ ++static void ++nvme_commit_sqe(struct nvme_sq *sq) ++{ ++ grub_dprintf("nvme", "sq %p commit_sqe %u\n", sq, sq->tail); ++ sq->tail = (sq->tail + 1) & sq->common.mask; ++ *(volatile grub_uint32_t *) sq->common.dbl = sq->tail; ++} ++ ++/* Perform an identify command on the admin queue and return the resulting ++ buffer. This may be a NULL pointer, if something failed. This function ++ cannot be used after initialization, because it uses buffers in tmp zone. */ ++static union nvme_identify * ++nvme_admin_identify(struct nvme_ctrl *ctrl, grub_uint8_t cns, grub_uint32_t nsid) ++{ ++ union nvme_identify *identify_buf = zalloc_page_aligned(4096); ++ if (!identify_buf) ++ return NULL; ++ ++ struct nvme_sqe *cmd_identify; ++ cmd_identify = nvme_get_next_sqe(&ctrl->admin_sq, ++ NVME_SQE_OPC_ADMIN_IDENTIFY, NULL, ++ identify_buf, NULL); ++ if (!cmd_identify) ++ goto error; ++ ++ cmd_identify->nsid = nsid; ++ cmd_identify->dword[10] = cns; ++ ++ nvme_commit_sqe(&ctrl->admin_sq); ++ ++ struct nvme_cqe cqe = nvme_wait(&ctrl->admin_sq); ++ ++ if (!nvme_is_cqe_success(&cqe)) { ++ goto error; ++ } ++ ++ return identify_buf; ++ error: ++ grub_free(identify_buf); ++ return NULL; ++} ++ ++static struct nvme_identify_ctrl * ++nvme_admin_identify_ctrl(struct nvme_ctrl *ctrl) ++{ ++ return &nvme_admin_identify(ctrl, NVME_ADMIN_IDENTIFY_CNS_ID_CTRL, 0)->ctrl; ++} ++ ++static struct nvme_identify_ns * ++nvme_admin_identify_ns(struct nvme_ctrl *ctrl, grub_uint32_t ns_id) ++{ ++ return &nvme_admin_identify(ctrl, NVME_ADMIN_IDENTIFY_CNS_ID_NS, ++ ns_id)->ns; ++} ++ ++static void ++nvme_probe_ns(struct nvme_ctrl *ctrl, grub_uint32_t ns_idx, grub_uint8_t mdts) ++{ ++ grub_uint32_t ns_id = ns_idx + 1; ++ ++ struct nvme_identify_ns *id = nvme_admin_identify_ns(ctrl, ns_id); ++ if (!id) { ++ grub_dprintf("nvme", "NVMe couldn't identify namespace %u.\n", ns_id); ++ goto free_buffer; ++ } ++ ++ grub_uint8_t current_lba_format = id->flbas & 0xF; ++ if (current_lba_format > id->nlbaf) { ++ grub_dprintf("nvme", "NVMe NS %u: current LBA format %u is beyond what the " ++ " namespace supports (%u)?\n", ++ ns_id, current_lba_format, id->nlbaf + 1); ++ goto free_buffer; ++ } ++ ++ if (!id->nsze) { ++ grub_dprintf("nvme", "NVMe NS %u is inactive.\n", ns_id); ++ goto free_buffer; ++ } ++ ++ if (!nvme_dma_buffer) { ++ nvme_dma_buffer = zalloc_page_aligned(NVME_PAGE_SIZE); ++ if (!nvme_dma_buffer) { ++ goto free_buffer; ++ } ++ } ++ ++ struct nvme_namespace *ns = grub_malloc(sizeof(*ns)); ++ if (!ns) { ++ goto free_buffer; ++ } ++ grub_memset(ns, 0, sizeof(*ns)); ++ ns->ctrl = ctrl; ++ ns->ns_id = ns_id; ++ ns->lba_count = id->nsze; ++ ++ struct nvme_lba_format *fmt = &id->lbaf[current_lba_format]; ++ ++ ns->block_size = 1U << fmt->lbads; ++ ns->metadata_size = fmt->ms; ++ ++ if (ns->block_size > NVME_PAGE_SIZE) { ++ /* If we see devices that trigger this path, we need to increase our ++ buffer size. */ ++ grub_free(ns); ++ goto free_buffer; ++ } ++ ++ if (mdts) { ++ ns->max_req_size = ((1U << mdts) * NVME_PAGE_SIZE) / ns->block_size; ++ grub_dprintf("nvme", "NVME NS %u max request size: %d sectors\n", ++ ns_id, ns->max_req_size); ++ } else { ++ ns->max_req_size = -1U; ++ } ++ ++ ns->devname = grub_xasprintf("nvme%un%u", ctrl->ctrlnum, ns_id); ++ ns->nsnum = grub_nvme_nscnt++; ++ ++ grub_list_push (GRUB_AS_LIST_P (&grub_nvme_namespaces), GRUB_AS_LIST (ns)); ++ ++free_buffer: ++ grub_free(id); ++} ++ ++ ++/* Release memory allocated for a completion queue */ ++static void ++nvme_destroy_cq(struct nvme_cq *cq) ++{ ++ grub_free(cq->cqe); ++ cq->cqe = NULL; ++} ++ ++/* Release memory allocated for a submission queue */ ++static void ++nvme_destroy_sq(struct nvme_sq *sq) ++{ ++ grub_free(sq->sqe); ++ sq->sqe = NULL; ++} ++ ++/* Returns 0 on success. */ ++static int ++nvme_create_io_cq(struct nvme_ctrl *ctrl, struct nvme_cq *cq, grub_uint16_t q_idx) ++{ ++ int rc; ++ struct nvme_sqe *cmd_create_cq; ++ grub_uint32_t length = 1 + (ctrl->reg->cap & 0xffff); ++ if (length > NVME_PAGE_SIZE / sizeof(struct nvme_cqe)) ++ length = NVME_PAGE_SIZE / sizeof(struct nvme_cqe); ++ ++ rc = nvme_init_cq(ctrl, cq, q_idx, length); ++ if (rc) { ++ goto err; ++ } ++ ++ cmd_create_cq = nvme_get_next_sqe(&ctrl->admin_sq, ++ NVME_SQE_OPC_ADMIN_CREATE_IO_CQ, NULL, ++ cq->cqe, NULL); ++ if (!cmd_create_cq) { ++ goto err_destroy_cq; ++ } ++ ++ cmd_create_cq->dword[10] = (cq->common.mask << 16) | (q_idx >> 1); ++ cmd_create_cq->dword[11] = 1 /* physically contiguous */; ++ ++ nvme_commit_sqe(&ctrl->admin_sq); ++ ++ struct nvme_cqe cqe = nvme_wait(&ctrl->admin_sq); ++ ++ if (!nvme_is_cqe_success(&cqe)) { ++ grub_dprintf("nvme", "create io cq failed: %08x %08x %08x %08x\n", ++ cqe.dword[0], cqe.dword[1], cqe.dword[2], cqe.dword[3]); ++ ++ goto err_destroy_cq; ++ } ++ ++ return 0; ++ ++err_destroy_cq: ++ nvme_destroy_cq(cq); ++err: ++ return -1; ++} ++ ++/* Returns 0 on success. */ ++static int ++nvme_create_io_sq(struct nvme_ctrl *ctrl, struct nvme_sq *sq, grub_uint16_t q_idx, struct nvme_cq *cq) ++{ ++ int rc; ++ struct nvme_sqe *cmd_create_sq; ++ grub_uint32_t length = 1 + (ctrl->reg->cap & 0xffff); ++ if (length > NVME_PAGE_SIZE / sizeof(struct nvme_cqe)) ++ length = NVME_PAGE_SIZE / sizeof(struct nvme_cqe); ++ ++ rc = nvme_init_sq(ctrl, sq, q_idx, length, cq); ++ if (rc) { ++ goto err; ++ } ++ ++ cmd_create_sq = nvme_get_next_sqe(&ctrl->admin_sq, ++ NVME_SQE_OPC_ADMIN_CREATE_IO_SQ, NULL, ++ sq->sqe, NULL); ++ if (!cmd_create_sq) { ++ goto err_destroy_sq; ++ } ++ ++ cmd_create_sq->dword[10] = (sq->common.mask << 16) | (q_idx >> 1); ++ cmd_create_sq->dword[11] = (q_idx >> 1) << 16 | 1 /* contiguous */; ++ grub_dprintf("nvme", "sq %p create dword10 %08x dword11 %08x\n", sq, ++ cmd_create_sq->dword[10], cmd_create_sq->dword[11]); ++ ++ nvme_commit_sqe(&ctrl->admin_sq); ++ ++ struct nvme_cqe cqe = nvme_wait(&ctrl->admin_sq); ++ ++ if (!nvme_is_cqe_success(&cqe)) { ++ grub_dprintf("nvme", "create io sq failed: %08x %08x %08x %08x\n", ++ cqe.dword[0], cqe.dword[1], cqe.dword[2], cqe.dword[3]); ++ goto err_destroy_sq; ++ } ++ ++ return 0; ++ ++err_destroy_sq: ++ nvme_destroy_sq(sq); ++err: ++ return -1; ++} ++ ++/* Reads count sectors into buf. The buffer cannot cross page boundaries. */ ++static int ++nvme_io_xfer(struct nvme_namespace *ns, grub_uint64_t lba, void *prp1, void *prp2, ++ grub_uint16_t count, int write) ++{ ++ if (((grub_uint32_t)prp1 & 0x3) || ((grub_uint32_t)prp2 & 0x3)) { ++ /* Buffer is misaligned */ ++ return -1; ++ } ++ ++ struct nvme_sqe *io_read = nvme_get_next_sqe(&ns->ctrl->io_sq, ++ write ? NVME_SQE_OPC_IO_WRITE ++ : NVME_SQE_OPC_IO_READ, ++ NULL, prp1, prp2); ++ io_read->nsid = ns->ns_id; ++ io_read->dword[10] = (grub_uint32_t)lba; ++ io_read->dword[11] = (grub_uint32_t)(lba >> 32); ++ io_read->dword[12] = (1U << 31 /* limited retry */) | (count - 1); ++ ++ nvme_commit_sqe(&ns->ctrl->io_sq); ++ ++ struct nvme_cqe cqe = nvme_wait(&ns->ctrl->io_sq); ++ ++ if (!nvme_is_cqe_success(&cqe)) { ++ grub_dprintf("nvme", "read io: %08x %08x %08x %08x\n", ++ cqe.dword[0], cqe.dword[1], cqe.dword[2], cqe.dword[3]); ++ ++ return -1; ++ } ++ ++ grub_dprintf("nvme", "ns %u %s lba %llu+%u\n", ns->ns_id, write ? "write" : "read", ++ lba, count); ++ return count; ++} ++ ++// Transfer up to one page of data using the internal dma bounce buffer ++static int ++nvme_bounce_xfer(struct nvme_namespace *ns, grub_uint64_t lba, void *buf, grub_uint16_t count, ++ int write) ++{ ++ grub_uint16_t const max_blocks = NVME_PAGE_SIZE / ns->block_size; ++ grub_uint16_t blocks = count < max_blocks ? count : max_blocks; ++ ++ if (write) ++ grub_memcpy(nvme_dma_buffer, buf, blocks * ns->block_size); ++ ++ int res = nvme_io_xfer(ns, lba, nvme_dma_buffer, NULL, blocks, write); ++ ++ if (!write && res >= 0) ++ grub_memcpy(buf, nvme_dma_buffer, res * ns->block_size); ++ ++ return res; ++} ++ ++#define NVME_MAX_PRPL_ENTRIES 15 /* Allows requests up to 64kb */ ++ ++// Transfer data using page list (if applicable) ++static int ++nvme_prpl_xfer(struct nvme_namespace *ns, grub_uint64_t lba, void *buf, grub_uint16_t count, ++ int write) ++{ ++ grub_uint32_t base = (long)buf; ++ grub_int32_t size; ++ ++ if (count > ns->max_req_size) ++ count = ns->max_req_size; ++ ++ size = count * ns->block_size; ++ /* Special case for transfers that fit into PRP1, but are unaligned */ ++ if (((size + (base & ~NVME_PAGE_MASK)) <= NVME_PAGE_SIZE)) ++ goto single; ++ ++ /* Every request has to be page aligned */ ++ if (base & ~NVME_PAGE_MASK) ++ goto bounce; ++ ++ /* Make sure a full block fits into the last chunk */ ++ if (size & (ns->block_size - 1ULL)) ++ goto bounce; ++ ++ /* Build PRP list if we need to describe more than 2 pages */ ++ if ((ns->block_size * count) > (NVME_PAGE_SIZE * 2)) { ++ grub_uint32_t prpl_len = 0; ++ grub_uint64_t *prpl = nvme_dma_buffer; ++ int first_page = 1; ++ for (; size > 0; base += NVME_PAGE_SIZE, size -= NVME_PAGE_SIZE) { ++ if (first_page) { ++ /* First page is special */ ++ first_page = 0; ++ continue; ++ } ++ if (prpl_len >= NVME_MAX_PRPL_ENTRIES) ++ goto bounce; ++ prpl[prpl_len++] = base; ++ } ++ return nvme_io_xfer(ns, lba, buf, prpl, count, write); ++ } ++ ++ /* Directly embed the 2nd page if we only need 2 pages */ ++ if ((ns->block_size * count) > NVME_PAGE_SIZE) ++ return nvme_io_xfer(ns, lba, buf, (char *) buf + NVME_PAGE_SIZE, count, write); ++ ++single: ++ /* One page is enough, don't expose anything else */ ++ return nvme_io_xfer(ns, lba, buf, NULL, count, write); ++ ++bounce: ++ /* Use bounce buffer to make transfer */ ++ return nvme_bounce_xfer(ns, lba, buf, count, write); ++} ++ ++static int ++nvme_create_io_queues(struct nvme_ctrl *ctrl) ++{ ++ if (nvme_create_io_cq(ctrl, &ctrl->io_cq, 3)) ++ goto err; ++ ++ if (nvme_create_io_sq(ctrl, &ctrl->io_sq, 2, &ctrl->io_cq)) ++ goto err_free_cq; ++ ++ return 0; ++ ++ err_free_cq: ++ nvme_destroy_cq(&ctrl->io_cq); ++ err: ++ return -1; ++} ++ ++/* Waits for CSTS.RDY to match rdy. Returns 0 on success. */ ++static int ++nvme_wait_csts_rdy(struct nvme_ctrl *ctrl, unsigned rdy) ++{ ++ // grub_uint32_t const max_to = 500 /* ms */ * ((ctrl->reg->cap >> 24) & 0xFFU); ++ // grub_uint32_t to = timer_calc(max_to); ++ grub_uint32_t csts; ++ ++ while (rdy != ((csts = ctrl->reg->csts) & NVME_CSTS_RDY)) { ++ // FIXME ++ //yield(); ++ ++ if (csts & NVME_CSTS_FATAL) { ++ grub_dprintf("nvme", "NVMe fatal error during controller shutdown\n"); ++ return -1; ++ } ++ ++ /* ++ if (timer_check(to)) { ++ warn_timeout(); ++ return -1; ++ }*/ ++ } ++ ++ return 0; ++} ++ ++/* Returns 0 on success. */ ++static int grub_nvme_controller_enable(struct nvme_ctrl *ctrl) ++{ ++ grub_pci_address_t addr; ++ int rc; ++ ++ addr = grub_pci_make_address (ctrl->pci, GRUB_PCI_REG_COMMAND); ++ grub_pci_write_word (addr, grub_pci_read_word (addr) | GRUB_PCI_COMMAND_BUS_MASTER); ++ ++ /* Turn the controller off. */ ++ ctrl->reg->cc = 0; ++ if (nvme_wait_csts_rdy(ctrl, 0)) { ++ grub_dprintf("nvme", "NVMe fatal error during controller shutdown\n"); ++ return -1; ++ } ++ ++ ctrl->doorbell_stride = 4U << ((ctrl->reg->cap >> 32) & 0xF); ++ ++ rc = nvme_init_cq(ctrl, &ctrl->admin_cq, 1, ++ NVME_PAGE_SIZE / sizeof(struct nvme_cqe)); ++ if (rc) { ++ return -1; ++ } ++ ++ rc = nvme_init_sq(ctrl, &ctrl->admin_sq, 0, ++ NVME_PAGE_SIZE / sizeof(struct nvme_sqe), &ctrl->admin_cq); ++ if (rc) { ++ goto err_destroy_admin_cq; ++ } ++ ++ ctrl->reg->aqa = ctrl->admin_cq.common.mask << 16 ++ | ctrl->admin_sq.common.mask; ++ ++ ctrl->reg->asq = (grub_uint32_t)ctrl->admin_sq.sqe; ++ ctrl->reg->acq = (grub_uint32_t)ctrl->admin_cq.cqe; ++ ++ grub_dprintf("nvme", " admin submission queue: %p\n", ctrl->admin_sq.sqe); ++ grub_dprintf("nvme", " admin completion queue: %p\n", ctrl->admin_cq.cqe); ++ ++ ctrl->reg->cc = NVME_CC_EN | (NVME_CQE_SIZE_LOG << 20) ++ | (NVME_SQE_SIZE_LOG << 16 /* IOSQES */); ++ ++ if (nvme_wait_csts_rdy(ctrl, 1)) { ++ grub_dprintf("nvme", "NVMe fatal error while enabling controller\n"); ++ goto err_destroy_admin_sq; ++ } ++ ++ /* The admin queue is set up and the controller is ready. Let's figure out ++ what namespaces we have. */ ++ ++ struct nvme_identify_ctrl *identify = nvme_admin_identify_ctrl(ctrl); ++ ++ if (!identify) { ++ grub_dprintf("nvme", "NVMe couldn't identify controller.\n"); ++ goto err_destroy_admin_sq; ++ } ++ ++ grub_dprintf("nvme", "NVMe has %u namespace%s.\n", ++ identify->nn, (identify->nn == 1) ? "" : "s"); ++ ++ ctrl->ns_count = identify->nn; ++ grub_uint8_t mdts = identify->mdts; ++ grub_free(identify); ++ ++ if ((ctrl->ns_count == 0) || nvme_create_io_queues(ctrl)) { ++ /* No point to continue, if the controller says it doesn't have ++ namespaces or we couldn't create I/O queues. */ ++ goto err_destroy_admin_sq; ++ } ++ ++ /* Give the controller a global number */ ++ ctrl->ctrlnum = grub_nvme_ctrlcnt++; ++ ++ /* Populate namespace IDs */ ++ for (grub_uint32_t ns_idx = 0; ns_idx < ctrl->ns_count; ns_idx++) { ++ nvme_probe_ns(ctrl, ns_idx, mdts); ++ } ++ ++ grub_dprintf("nvme", "NVMe initialization complete!\n"); ++ return 0; ++ ++ err_destroy_admin_sq: ++ nvme_destroy_sq(&ctrl->admin_sq); ++ err_destroy_admin_cq: ++ nvme_destroy_cq(&ctrl->admin_cq); ++ return -1; ++} ++ ++static int grub_nvme_pci_probe(grub_pci_device_t dev, grub_pci_id_t pciid __attribute__ ((unused)), void *data __attribute__ ((unused))) ++{ ++ grub_pci_address_t addr; ++ grub_uint32_t class, bar, version; ++ struct nvme_reg volatile *reg; ++ ++ class = grub_pci_read (grub_pci_make_address (dev, GRUB_PCI_REG_CLASS)); ++ if (class >> 16 != 0x0108) ++ return 0; ++ if ((class >> 8 & 0xff) != 2) { /* as of NVM 1.0e */ ++ grub_dprintf("nvme", "Found incompatble NVMe: prog-if=%02x\n", class >> 8 & 0xff); ++ return 0; ++ } ++ ++ bar = grub_pci_read (grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0)); ++ reg = grub_pci_device_map_range (dev, bar & GRUB_PCI_ADDR_MEM_MASK, sizeof (*reg)); ++ ++ addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); ++ grub_pci_write_word (addr, grub_pci_read_word (addr) | GRUB_PCI_COMMAND_MEM_ENABLED); ++ ++ version = reg->vs; ++ grub_dprintf("nvme", "Found NVMe controller with version %u.%u.%u.\n", version >> 16, (version >> 8) & 0xFF, version & 0xFF); ++ grub_dprintf("nvme", " Capabilities %016llx\n", reg->cap); ++ ++ if (~reg->cap & NVME_CAP_CSS_NVME) { ++ grub_dprintf("nvme", "Controller doesn't speak NVMe command set. Skipping.\n"); ++ goto err; ++ } ++ ++ struct nvme_ctrl *ctrl = grub_malloc(sizeof(*ctrl)); ++ if (!ctrl) ++ goto err; ++ ++ grub_memset(ctrl, 0, sizeof(*ctrl)); ++ ++ ctrl->reg = reg; ++ ctrl->pci = dev; ++ ++ if (grub_nvme_controller_enable(ctrl)) ++ goto err_free_ctrl; ++ ++ return 0; ++ ++ err_free_ctrl: ++ grub_free(ctrl); ++ err: ++ grub_dprintf("nvme", "Failed to enable NVMe controller.\n"); ++ return 0; ++} ++ ++static int ++grub_nvme_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, grub_disk_pull_t pull) ++{ ++ struct nvme_namespace *ns; ++ ++ if (pull != GRUB_DISK_PULL_NONE) ++ return 0; ++ ++ FOR_LIST_ELEMENTS(ns, grub_nvme_namespaces) ++ if (hook (ns->devname, hook_data)) ++ return 1; ++ ++ return 0; ++} ++ ++static grub_err_t ++grub_nvme_open (const char *name __attribute ((unused)), grub_disk_t disk __attribute ((unused))) ++{ ++ struct nvme_namespace *ns; ++ ++ FOR_LIST_ELEMENTS(ns, grub_nvme_namespaces) ++ if (grub_strcmp (ns->devname, name) == 0) ++ break; ++ ++ if (! ns) ++ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); ++ ++ disk->total_sectors = ns->lba_count; ++ disk->max_agglomerate = ns->max_req_size; ++ ++ disk->id = ns->nsnum; /* global id of the namespace */ ++ ++ disk->data = ns; ++ ++ return 0; ++} ++ ++static grub_err_t ++nvme_readwrite(struct nvme_namespace *ns, grub_disk_addr_t sector, grub_size_t num_sectors, char *buf, int write) ++{ ++ for (int i = 0; i < num_sectors;) { ++ grub_uint16_t blocks_remaining = num_sectors - i; ++ char *op_buf = buf + i * ns->block_size; ++ int blocks = nvme_prpl_xfer(ns, sector + i, op_buf, blocks_remaining, write); ++ if (blocks < 0) ++ return GRUB_ERR_IO; ++ i += blocks; ++ } ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_nvme_read (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t num_sectors, char *buf) ++{ ++ return nvme_readwrite((struct nvme_namespace *) disk->data, sector, num_sectors, buf, 0); ++} ++ ++static grub_err_t ++grub_nvme_write (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t num_sectors, const char *buf) ++{ ++ return nvme_readwrite((struct nvme_namespace *) disk->data, sector, num_sectors, buf, 1); ++} ++ ++static struct grub_disk_dev grub_nvme_dev = ++ { ++ .name = "nvme", ++ .id = GRUB_DISK_DEVICE_NVME_ID, ++ .disk_iterate = grub_nvme_iterate, ++ .disk_open = grub_nvme_open, ++ .disk_read = grub_nvme_read, ++ .disk_write = grub_nvme_write, ++ .next = 0 ++ }; ++ ++GRUB_MOD_INIT(nvme) ++{ ++ grub_stop_disk_firmware (); ++ grub_pci_iterate (grub_nvme_pci_probe, NULL); ++ grub_disk_dev_register (&grub_nvme_dev); ++} ++ ++GRUB_MOD_FINI(nvme) ++{ ++ grub_disk_dev_unregister (&grub_nvme_dev); ++} +diff --git a/include/grub/disk.h b/include/grub/disk.h +index fbf23df7f..186e76f0b 100644 +--- a/include/grub/disk.h ++++ b/include/grub/disk.h +@@ -52,6 +52,7 @@ enum grub_disk_dev_id + GRUB_DISK_DEVICE_UBOOTDISK_ID, + GRUB_DISK_DEVICE_XEN, + GRUB_DISK_DEVICE_OBDISK_ID, ++ GRUB_DISK_DEVICE_NVME_ID + }; + + struct grub_disk; +-- +2.47.3 + diff --git a/config/grub/nvme/patches/0009-kern-coreboot-mmap-Map-to-reserved.patch b/config/grub/nvme/patches/0009-kern-coreboot-mmap-Map-to-reserved.patch new file mode 100644 index 00000000..11b82429 --- /dev/null +++ b/config/grub/nvme/patches/0009-kern-coreboot-mmap-Map-to-reserved.patch @@ -0,0 +1,37 @@ +From 9ec8534a4d27db37cf4feebd44fd9f8973a89e59 Mon Sep 17 00:00:00 2001 +From: Paul Menzel <pmenzel@molgen.mpg.de> +Date: Mon, 17 May 2021 10:24:36 +0200 +Subject: [PATCH 09/10] kern/coreboot/mmap: Map to reserved + +https://git.savannah.gnu.org/cgit/grub.git/commit/?id=6de9ee86bf9ae50967413e6a73b5dfd13e5ffb50 + +Signed-off-by: Paul Menzel <pmenzel@molgen.mpg.de> +--- + grub-core/kern/coreboot/mmap.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/grub-core/kern/coreboot/mmap.c b/grub-core/kern/coreboot/mmap.c +index caf8f7cef..2fc316e8d 100644 +--- a/grub-core/kern/coreboot/mmap.c ++++ b/grub-core/kern/coreboot/mmap.c +@@ -59,7 +59,7 @@ iterate_linuxbios_table (grub_linuxbios_table_item_t table_item, void *data) + /* Multiboot mmaps match with the coreboot mmap + definition. Therefore, we can just pass type + through. */ +- mem_region->type, ++ (mem_region->type >= 13) ? 2 : mem_region->type, + ctx->hook_data)) + return 1; + if (start < 0xa0000) +@@ -81,7 +81,7 @@ iterate_linuxbios_table (grub_linuxbios_table_item_t table_item, void *data) + /* Multiboot mmaps match with the coreboot mmap + definition. Therefore, we can just pass type + through. */ +- mem_region->type, ++ (mem_region->type >= 13) ? 2 : mem_region->type, + ctx->hook_data)) + return 1; + } +-- +2.47.3 + diff --git a/config/grub/nvme/patches/0010-Revert-configure-Check-linker-for-image-base-support.patch b/config/grub/nvme/patches/0010-Revert-configure-Check-linker-for-image-base-support.patch new file mode 100644 index 00000000..8e78e8b7 --- /dev/null +++ b/config/grub/nvme/patches/0010-Revert-configure-Check-linker-for-image-base-support.patch @@ -0,0 +1,70 @@ +From 7b0d621389fc1857c918058f82093f9630ea7730 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Wed, 24 Dec 2025 01:42:17 +0100 +Subject: [PATCH 10/10] Revert "configure: Check linker for --image-base + support" + +This reverts commit 1a5417f39a0ccefcdd5440f2a67f84d2d2e26960. +--- + acinclude.m4 | 5 ----- + configure.ac | 14 ++------------ + 2 files changed, 2 insertions(+), 17 deletions(-) + +diff --git a/acinclude.m4 b/acinclude.m4 +index 70c1912f8..fa7840f09 100644 +--- a/acinclude.m4 ++++ b/acinclude.m4 +@@ -79,11 +79,6 @@ AC_DEFUN([grub_PROG_OBJCOPY_ABSOLUTE], + [AC_MSG_CHECKING([whether ${TARGET_OBJCOPY} works for absolute addresses]) + AC_CACHE_VAL(grub_cv_prog_objcopy_absolute, + [cat > conftest.c <<\EOF +-asm ( +- ".globl start, _start, __start\n" +- ".ifdef cmain; .set start = _start = __start = cmain\n.endif\n" +- ".ifdef _cmain; .set start = _start = __start = _cmain\n.endif\n" +-); + void cmain (void); + void + cmain (void) +diff --git a/configure.ac b/configure.ac +index d8ca1b7c1..041cfbab4 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1461,6 +1461,7 @@ elif test x$grub_cv_target_cc_link_format = x-mi386pe || test x$grub_cv_target_c + TARGET_IMG_LDSCRIPT='$(top_srcdir)'"/conf/i386-cygwin-img-ld.sc" + TARGET_IMG_LDFLAGS="-Wl,-T${TARGET_IMG_LDSCRIPT}" + TARGET_IMG_LDFLAGS_AC="-Wl,-T${srcdir}/conf/i386-cygwin-img-ld.sc" ++ TARGET_IMG_BASE_LDOPT="-Wl,-Ttext" + TARGET_IMG_CFLAGS= + else + TARGET_APPLE_LINKER=0 +@@ -1468,6 +1469,7 @@ else + TARGET_IMG_LDSCRIPT= + TARGET_IMG_LDFLAGS='-Wl,-N' + TARGET_IMG_LDFLAGS_AC='-Wl,-N' ++ TARGET_IMG_BASE_LDOPT="-Wl,-Ttext" + TARGET_IMG_CFLAGS= + fi + +@@ -1798,18 +1800,6 @@ grub_PROG_TARGET_CC + m4_ifndef([AX_CHECK_LINK_FLAG], [m4_fatal([autoconf-archive is missing. You must install it to generate the configure script.])]) + + if test "x$TARGET_APPLE_LINKER" != x1 ; then +-AX_CHECK_LINK_FLAG([-Wl,--image-base,0x400000], +- [TARGET_IMG_BASE_LDOPT="-Wl,--image-base"], +- [TARGET_IMG_BASE_LDOPT="-Wl,-Ttext"], +- [], +- [AC_LANG_SOURCE([ +-asm (".globl start; start:"); +-asm (".globl _start; _start:"); +-asm (".globl __start; __start:"); +-void __main (void); +-void __main (void) {} +-int main (void); +- ])]) + grub_PROG_OBJCOPY_ABSOLUTE + fi + grub_PROG_LD_BUILD_ID_NONE +-- +2.47.3 + diff --git a/config/grub/nvme/patches/0011-Revert-configure-Print-a-more-helpful-error-if-autoc.patch b/config/grub/nvme/patches/0011-Revert-configure-Print-a-more-helpful-error-if-autoc.patch new file mode 100644 index 00000000..c0a504ff --- /dev/null +++ b/config/grub/nvme/patches/0011-Revert-configure-Print-a-more-helpful-error-if-autoc.patch @@ -0,0 +1,30 @@ +From abf2bf6e2973ccaa994f63ff851ba11cbd45f1eb Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Mon, 19 Jan 2026 15:27:23 +0000 +Subject: [PATCH 1/1] Revert "configure: Print a more helpful error if + autoconf-archive is not installed" + +This reverts commit ac042f3f58d33ce9cd5ff61750f06da1a1d7b0eb. +--- + configure.ac | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 041cfbab4..209c0fb11 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1794,11 +1794,6 @@ LIBS="" + # Defined in acinclude.m4. + grub_ASM_USCORE + grub_PROG_TARGET_CC +- +-# The error message produced by autoconf if autoconf-archive is not installed is +-# quite misleading and not very helpful. So, try point people in the right direction. +-m4_ifndef([AX_CHECK_LINK_FLAG], [m4_fatal([autoconf-archive is missing. You must install it to generate the configure script.])]) +- + if test "x$TARGET_APPLE_LINKER" != x1 ; then + grub_PROG_OBJCOPY_ABSOLUTE + fi +-- +2.47.3 + diff --git a/config/grub/nvme/patches/0012-bootstrap-Don-t-download-po-files.patch b/config/grub/nvme/patches/0012-bootstrap-Don-t-download-po-files.patch new file mode 100644 index 00000000..cfb66047 --- /dev/null +++ b/config/grub/nvme/patches/0012-bootstrap-Don-t-download-po-files.patch @@ -0,0 +1,91 @@ +From 5d18c96a22d98d137ea40bfc6aabadce933c2d45 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Sun, 1 Feb 2026 20:30:55 +0100 +Subject: [PATCH 1/1] bootstrap: Don't download po files + +GRUB doesn't verify checksums at all, and it pulls from +this URL recursively: + +https://translationproject.org/latest/grub/ + +These files can change at any time, and GRUB is just +downloading them trustingly. Even if the upstream is +totally benevolent, what if they got hacked? + +I downloaded them, hashed them and decided to mirror +them on my RSYNC mirror. In this way, Libreboot can now +use them in a deterministic fashion. + +Simply adding them to the GRUB source code would mean +patching GRUB, which would add 8MB to lbmk. I won't do +it. + +Signed-off-by: Leah Rowe <leah@libreboot.org> +--- + bootstrap | 31 +++++++++++++------------------ + 1 file changed, 13 insertions(+), 18 deletions(-) + +diff --git a/bootstrap b/bootstrap +index dc9fb4383..9fc5a5c36 100755 +--- a/bootstrap ++++ b/bootstrap +@@ -1,5 +1,16 @@ + #! /bin/sh +-# DO NOT EDIT! GENERATED AUTOMATICALLY! ++# THIS FILE WAS EDITED BY LIBREBOOT TO REMOVE ++# HACKY GRUB BEHAVIOUR; po files now downloaded ++# by lbmk, via config/submodule/grub/ - so that ++# versioned files are possible, with proper checksum ++# verification, and mirrors are used. ++ ++# Yes. This file has been modified. I intend to ++# eventually remove this hacky script. Probably ++# replace the entire GRUB build system. ++ ++# Please do fix/edit or (when possible) remove ++# this file. Thank you. + + # Bootstrap this package from checked-out sources. + +@@ -145,13 +156,6 @@ bootstrap_post_import_hook() { :; } + # Override it via your own definition in bootstrap.conf. + bootstrap_epilogue() { :; } + +-# The command to download all .po files for a specified domain into a +-# specified directory. Fill in the first %s with the destination +-# directory and the second with the domain name. +-po_download_command_format=\ +-"wget --mirror --level=1 -nd -nv -A.po -P '%s' \ +- https://translationproject.org/latest/%s/" +- + # When extracting the package name from an AC_INIT invocation, + # prefer a non-empty tarname (4th argument of AC_INIT if given), else + # fall back to the package name (1st argument with munging). +@@ -909,14 +913,6 @@ autopull() + + # ----------------------------- Get translations. ----------------------------- + +-download_po_files() { +- subdir=$1 +- domain=$2 +- echo "$me: getting translations into $subdir for $domain..." +- cmd=$(printf "$po_download_command_format" "$subdir" "$domain") +- eval "$cmd" +-} +- + # Mirror .po files to $po_dir/.reference and copy only the new + # or modified ones into $po_dir. Also update $po_dir/LINGUAS. + # Note po files that exist locally only are left in $po_dir but will +@@ -932,8 +928,7 @@ update_po_files() { + ref_po_dir="$po_dir/.reference" + + test -d $ref_po_dir || mkdir $ref_po_dir || return +- download_po_files $ref_po_dir $domain \ +- && ls "$ref_po_dir"/*.po 2>/dev/null | ++ ls "$ref_po_dir"/*.po 2>/dev/null | + sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return + + for po in x $(ls $ref_po_dir | sed -n 's/\.po$//p'); do +-- +2.47.3 + diff --git a/config/grub/nvme/target.cfg b/config/grub/nvme/target.cfg new file mode 100644 index 00000000..e2a67cc0 --- /dev/null +++ b/config/grub/nvme/target.cfg @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +tree="nvme" +rev="eaa3b8f0f90605a82c6bfda4c5c4b73c58eb81ac" |
