summaryrefslogtreecommitdiff
path: root/config/grub/xhci_nvme
diff options
context:
space:
mode:
Diffstat (limited to 'config/grub/xhci_nvme')
-rw-r--r--config/grub/xhci_nvme/config/payload321
-rw-r--r--config/grub/xhci_nvme/patches/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch90
-rw-r--r--config/grub/xhci_nvme/patches/0002-say-the-name-libreboot-in-the-grub-menu.patch25
-rw-r--r--config/grub/xhci_nvme/patches/0003-at_keyboard-coreboot-force-scancodes2-translate.patch107
-rw-r--r--config/grub/xhci_nvme/patches/0004-keylayouts-don-t-print-Unknown-key-message.patch38
-rw-r--r--config/grub/xhci_nvme/patches/0005-don-t-print-missing-prefix-errors-on-the-screen.patch102
-rw-r--r--config/grub/xhci_nvme/patches/0006-don-t-print-error-if-module-not-found.patch34
-rw-r--r--config/grub/xhci_nvme/patches/0007-don-t-print-empty-error-messages.patch31
-rw-r--r--config/grub/xhci_nvme/patches/0008-grub-core-bus-usb-Parse-SuperSpeed-companion-descrip.patch246
-rw-r--r--config/grub/xhci_nvme/patches/0009-usb-Add-enum-for-xHCI.patch29
-rw-r--r--config/grub/xhci_nvme/patches/0010-usbtrans-Set-default-maximum-packet-size.patch33
-rw-r--r--config/grub/xhci_nvme/patches/0011-grub-core-bus-usb-Add-function-pointer-for-attach-de.patch121
-rw-r--r--config/grub/xhci_nvme/patches/0012-grub-core-bus-usb-usbhub-Add-new-private-fields-for-.patch77
-rw-r--r--config/grub/xhci_nvme/patches/0013-grub-core-bus-usb-Add-xhci-support.patch2807
-rw-r--r--config/grub/xhci_nvme/patches/0014-grub-core-bus-usb-usbhub-Add-xHCI-non-root-hub-suppo.patch127
-rw-r--r--config/grub/xhci_nvme/patches/0015-xHCI-also-accept-SBRN-0x31-and-0x32.patch26
-rw-r--r--config/grub/xhci_nvme/patches/0016-xhci-fix-port-indexing.patch43
-rw-r--r--config/grub/xhci_nvme/patches/0017-xhci-configure-TT-for-non-root-hubs.patch98
-rw-r--r--config/grub/xhci_nvme/patches/0018-Fix-compilation-on-x86_64.patch90
-rw-r--r--config/grub/xhci_nvme/patches/0019-Add-native-NVMe-driver-based-on-SeaBIOS.patch1074
-rw-r--r--config/grub/xhci_nvme/patches/0020-kern-coreboot-mmap-Map-to-reserved.patch37
-rw-r--r--config/grub/xhci_nvme/patches/0021-Revert-configure-Check-linker-for-image-base-support.patch69
-rw-r--r--config/grub/xhci_nvme/target.cfg4
23 files changed, 5629 insertions, 0 deletions
diff --git a/config/grub/xhci_nvme/config/payload b/config/grub/xhci_nvme/config/payload
new file mode 100644
index 00000000..9db22fe2
--- /dev/null
+++ b/config/grub/xhci_nvme/config/payload
@@ -0,0 +1,321 @@
+# 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 xhci
+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) in 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/xhci_nvme/patches/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch b/config/grub/xhci_nvme/patches/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch
new file mode 100644
index 00000000..46e5059f
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch
@@ -0,0 +1,90 @@
+From 5e70ef9fa3a6474ec827c15329d13c9c984f147a 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/20] 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/xhci_nvme/patches/0002-say-the-name-libreboot-in-the-grub-menu.patch b/config/grub/xhci_nvme/patches/0002-say-the-name-libreboot-in-the-grub-menu.patch
new file mode 100644
index 00000000..b0d81978
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0002-say-the-name-libreboot-in-the-grub-menu.patch
@@ -0,0 +1,25 @@
+From f87a74e198cea4ee79051fc789a06ad3695ad1db 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/20] 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..1fabb9c86 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 RC1 Tenacious Tomato (GRUB menu): https://libreboot.org/"));
+ if (!msg_formatted)
+ return;
+
+--
+2.47.3
+
diff --git a/config/grub/xhci_nvme/patches/0003-at_keyboard-coreboot-force-scancodes2-translate.patch b/config/grub/xhci_nvme/patches/0003-at_keyboard-coreboot-force-scancodes2-translate.patch
new file mode 100644
index 00000000..d3547d11
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0003-at_keyboard-coreboot-force-scancodes2-translate.patch
@@ -0,0 +1,107 @@
+From e647f6b06788022a746e8f38ed2b092013d1d570 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/20] 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/xhci_nvme/patches/0004-keylayouts-don-t-print-Unknown-key-message.patch b/config/grub/xhci_nvme/patches/0004-keylayouts-don-t-print-Unknown-key-message.patch
new file mode 100644
index 00000000..b3fd8956
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0004-keylayouts-don-t-print-Unknown-key-message.patch
@@ -0,0 +1,38 @@
+From 1f0c3362178d1ad44998a98782bcb1e412af9d79 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/20] 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/xhci_nvme/patches/0005-don-t-print-missing-prefix-errors-on-the-screen.patch b/config/grub/xhci_nvme/patches/0005-don-t-print-missing-prefix-errors-on-the-screen.patch
new file mode 100644
index 00000000..0d49f993
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0005-don-t-print-missing-prefix-errors-on-the-screen.patch
@@ -0,0 +1,102 @@
+From b902f68dacba764fe4d4200cae000cf23385df8e 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/20] 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/xhci_nvme/patches/0006-don-t-print-error-if-module-not-found.patch b/config/grub/xhci_nvme/patches/0006-don-t-print-error-if-module-not-found.patch
new file mode 100644
index 00000000..07c5713f
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0006-don-t-print-error-if-module-not-found.patch
@@ -0,0 +1,34 @@
+From 2da7fd383afee0a91c1eeb5120a46e165b9386c3 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/20] 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/xhci_nvme/patches/0007-don-t-print-empty-error-messages.patch b/config/grub/xhci_nvme/patches/0007-don-t-print-empty-error-messages.patch
new file mode 100644
index 00000000..108f4b5f
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0007-don-t-print-empty-error-messages.patch
@@ -0,0 +1,31 @@
+From 5c26d4bb0cc69cb9f4b9e4f6de794f309d5210f4 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/20] 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/xhci_nvme/patches/0008-grub-core-bus-usb-Parse-SuperSpeed-companion-descrip.patch b/config/grub/xhci_nvme/patches/0008-grub-core-bus-usb-Parse-SuperSpeed-companion-descrip.patch
new file mode 100644
index 00000000..6144f1e0
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0008-grub-core-bus-usb-Parse-SuperSpeed-companion-descrip.patch
@@ -0,0 +1,246 @@
+From b47687966ebad85b16d58469237b048c10f86610 Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Sun, 15 Nov 2020 19:00:27 +0100
+Subject: [PATCH 08/20] grub-core/bus/usb: Parse SuperSpeed companion
+ descriptors
+
+Parse the SS_ENDPOINT_COMPANION descriptor, which is only present on USB 3.0
+capable devices and xHCI controllers. Make the descendp an array of pointers
+to the endpoint descriptor as it's no longer an continous array.
+
+Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
+---
+ grub-core/bus/usb/serial/common.c | 2 +-
+ grub-core/bus/usb/usb.c | 44 +++++++++++++++++++------------
+ grub-core/bus/usb/usbhub.c | 22 ++++++++++++----
+ grub-core/commands/usbtest.c | 2 +-
+ grub-core/disk/usbms.c | 2 +-
+ grub-core/term/usb_keyboard.c | 2 +-
+ include/grub/usb.h | 2 +-
+ include/grub/usbdesc.h | 11 +++++++-
+ 8 files changed, 59 insertions(+), 28 deletions(-)
+
+diff --git a/grub-core/bus/usb/serial/common.c b/grub-core/bus/usb/serial/common.c
+index e9c995a0a..fc847d66d 100644
+--- a/grub-core/bus/usb/serial/common.c
++++ b/grub-core/bus/usb/serial/common.c
+@@ -72,7 +72,7 @@ grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno,
+ for (j = 0; j < interf->endpointcnt; j++)
+ {
+ struct grub_usb_desc_endp *endp;
+- endp = &usbdev->config[0].interf[interfno].descendp[j];
++ endp = usbdev->config[0].interf[interfno].descendp[j];
+
+ if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2
+ && (in_endp == GRUB_USB_SERIAL_ENDPOINT_LAST_MATCHING
+diff --git a/grub-core/bus/usb/usb.c b/grub-core/bus/usb/usb.c
+index 7bd49d201..e578af793 100644
+--- a/grub-core/bus/usb/usb.c
++++ b/grub-core/bus/usb/usb.c
+@@ -118,7 +118,7 @@ grub_usb_device_initialize (grub_usb_device_t dev)
+ struct grub_usb_desc_device *descdev;
+ struct grub_usb_desc_config config;
+ grub_usb_err_t err;
+- int i;
++ int i, j;
+
+ /* First we have to read first 8 bytes only and determine
+ * max. size of packet */
+@@ -152,6 +152,7 @@ grub_usb_device_initialize (grub_usb_device_t dev)
+ int currif;
+ char *data;
+ struct grub_usb_desc *desc;
++ struct grub_usb_desc_endp *endp;
+
+ /* First just read the first 4 bytes of the configuration
+ descriptor, after that it is known how many bytes really have
+@@ -201,24 +202,27 @@ grub_usb_device_initialize (grub_usb_device_t dev)
+ = (struct grub_usb_desc_if *) &data[pos];
+ pos += dev->config[i].interf[currif].descif->length;
+
++ dev->config[i].interf[currif].descendp = grub_malloc (
++ dev->config[i].interf[currif].descif->endpointcnt *
++ sizeof(struct grub_usb_desc_endp));
++
++ j = 0;
+ while (pos < config.totallen)
+ {
+ desc = (struct grub_usb_desc *)&data[pos];
+- if (desc->type == GRUB_USB_DESCRIPTOR_ENDPOINT)
+- break;
+- if (!desc->length)
+- {
+- err = GRUB_USB_ERR_BADDEVICE;
+- goto fail;
+- }
+- pos += desc->length;
+- }
+-
+- /* Point to the first endpoint. */
+- dev->config[i].interf[currif].descendp
+- = (struct grub_usb_desc_endp *) &data[pos];
+- pos += (sizeof (struct grub_usb_desc_endp)
+- * dev->config[i].interf[currif].descif->endpointcnt);
++ if (desc->type == GRUB_USB_DESCRIPTOR_ENDPOINT) {
++ endp = (struct grub_usb_desc_endp *) &data[pos];
++ dev->config[i].interf[currif].descendp[j++] = endp;
++ pos += desc->length;
++ } else {
++ if (!desc->length)
++ {
++ err = GRUB_USB_ERR_BADDEVICE;
++ goto fail;
++ }
++ pos += desc->length;
++ }
++ }
+ }
+ }
+
+@@ -226,8 +230,14 @@ grub_usb_device_initialize (grub_usb_device_t dev)
+
+ fail:
+
+- for (i = 0; i < GRUB_USB_MAX_CONF; i++)
++ for (i = 0; i < GRUB_USB_MAX_CONF; i++) {
++ int currif;
++
++ for (currif = 0; currif < dev->config[i].descconf->numif; currif++)
++ grub_free (dev->config[i].interf[currif].descendp);
++
+ grub_free (dev->config[i].descconf);
++ }
+
+ return err;
+ }
+diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
+index f5608e330..2ae29cba1 100644
+--- a/grub-core/bus/usb/usbhub.c
++++ b/grub-core/bus/usb/usbhub.c
+@@ -82,8 +82,14 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
+ if (i == GRUB_USBHUB_MAX_DEVICES)
+ {
+ grub_error (GRUB_ERR_IO, "can't assign address to USB device");
+- for (i = 0; i < GRUB_USB_MAX_CONF; i++)
+- grub_free (dev->config[i].descconf);
++ for (i = 0; i < GRUB_USB_MAX_CONF; i++) {
++ int currif;
++
++ for (currif = 0; currif < dev->config[i].descconf->numif; currif++)
++ grub_free (dev->config[i].interf[currif].descendp);
++
++ grub_free (dev->config[i].descconf);
++ }
+ grub_free (dev);
+ return NULL;
+ }
+@@ -96,8 +102,14 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
+ i, 0, 0, NULL);
+ if (err)
+ {
+- for (i = 0; i < GRUB_USB_MAX_CONF; i++)
+- grub_free (dev->config[i].descconf);
++ for (i = 0; i < GRUB_USB_MAX_CONF; i++) {
++ int currif;
++
++ for (currif = 0; currif < dev->config[i].descconf->numif; currif++)
++ grub_free (dev->config[i].interf[currif].descendp);
++
++ grub_free (dev->config[i].descconf);
++ }
+ grub_free (dev);
+ return NULL;
+ }
+@@ -176,7 +188,7 @@ grub_usb_add_hub (grub_usb_device_t dev)
+ i++)
+ {
+ struct grub_usb_desc_endp *endp = NULL;
+- endp = &dev->config[0].interf[0].descendp[i];
++ endp = dev->config[0].interf[0].descendp[i];
+
+ if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
+ == GRUB_USB_EP_INTERRUPT)
+diff --git a/grub-core/commands/usbtest.c b/grub-core/commands/usbtest.c
+index 3184ac9af..7d128449d 100644
+--- a/grub-core/commands/usbtest.c
++++ b/grub-core/commands/usbtest.c
+@@ -185,7 +185,7 @@ usb_iterate (grub_usb_device_t dev, void *data __attribute__ ((unused)))
+ for (j = 0; j < interf->endpointcnt; j++)
+ {
+ struct grub_usb_desc_endp *endp;
+- endp = &dev->config[0].interf[i].descendp[j];
++ endp = dev->config[0].interf[i].descendp[j];
+
+ grub_printf ("Endpoint #%d: %s, max packed size: %d, transfer type: %s, latency: %d\n",
+ endp->endp_addr & 15,
+diff --git a/grub-core/disk/usbms.c b/grub-core/disk/usbms.c
+index b81e3ad9d..b1512dc12 100644
+--- a/grub-core/disk/usbms.c
++++ b/grub-core/disk/usbms.c
+@@ -184,7 +184,7 @@ grub_usbms_attach (grub_usb_device_t usbdev, int configno, int interfno)
+ for (j = 0; j < interf->endpointcnt; j++)
+ {
+ struct grub_usb_desc_endp *endp;
+- endp = &usbdev->config[0].interf[interfno].descendp[j];
++ endp = usbdev->config[0].interf[interfno].descendp[j];
+
+ if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2)
+ /* Bulk IN endpoint. */
+diff --git a/grub-core/term/usb_keyboard.c b/grub-core/term/usb_keyboard.c
+index 7322d8dff..d590979f5 100644
+--- a/grub-core/term/usb_keyboard.c
++++ b/grub-core/term/usb_keyboard.c
+@@ -175,7 +175,7 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
+ for (j = 0; j < usbdev->config[configno].interf[interfno].descif->endpointcnt;
+ j++)
+ {
+- endp = &usbdev->config[configno].interf[interfno].descendp[j];
++ endp = usbdev->config[configno].interf[interfno].descendp[j];
+
+ if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
+ == GRUB_USB_EP_INTERRUPT)
+diff --git a/include/grub/usb.h b/include/grub/usb.h
+index 0f346af12..688c11f6d 100644
+--- a/include/grub/usb.h
++++ b/include/grub/usb.h
+@@ -153,7 +153,7 @@ struct grub_usb_interface
+ {
+ struct grub_usb_desc_if *descif;
+
+- struct grub_usb_desc_endp *descendp;
++ struct grub_usb_desc_endp **descendp;
+
+ /* A driver is handling this interface. Do we need to support multiple drivers
+ for single interface?
+diff --git a/include/grub/usbdesc.h b/include/grub/usbdesc.h
+index aac5ab05a..bb2ab2e27 100644
+--- a/include/grub/usbdesc.h
++++ b/include/grub/usbdesc.h
+@@ -29,7 +29,8 @@ typedef enum {
+ GRUB_USB_DESCRIPTOR_INTERFACE,
+ GRUB_USB_DESCRIPTOR_ENDPOINT,
+ GRUB_USB_DESCRIPTOR_DEBUG = 10,
+- GRUB_USB_DESCRIPTOR_HUB = 0x29
++ GRUB_USB_DESCRIPTOR_HUB = 0x29,
++ GRUB_USB_DESCRIPTOR_SS_ENDPOINT_COMPANION = 0x30
+ } grub_usb_descriptor_t;
+
+ struct grub_usb_desc
+@@ -105,6 +106,14 @@ struct grub_usb_desc_endp
+ grub_uint8_t interval;
+ } GRUB_PACKED;
+
++struct grub_usb_desc_ssep {
++ grub_uint8_t length;
++ grub_uint8_t type;
++ grub_uint8_t maxburst;
++ grub_uint8_t attrib;
++ grub_uint16_t interval;
++} GRUB_PACKED;
++
+ struct grub_usb_desc_str
+ {
+ grub_uint8_t length;
+--
+2.47.3
+
diff --git a/config/grub/xhci_nvme/patches/0009-usb-Add-enum-for-xHCI.patch b/config/grub/xhci_nvme/patches/0009-usb-Add-enum-for-xHCI.patch
new file mode 100644
index 00000000..4f88de35
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0009-usb-Add-enum-for-xHCI.patch
@@ -0,0 +1,29 @@
+From 05b2607120d0ccfb826bc83d4f4d2741f4f0a6a1 Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Mon, 7 Dec 2020 08:41:22 +0100
+Subject: [PATCH 09/20] usb: Add enum for xHCI
+
+Will be used in future patches.
+
+Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
+---
+ include/grub/usb.h | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/include/grub/usb.h b/include/grub/usb.h
+index 688c11f6d..ea6ee8c2c 100644
+--- a/include/grub/usb.h
++++ b/include/grub/usb.h
+@@ -51,7 +51,8 @@ typedef enum
+ GRUB_USB_SPEED_NONE,
+ GRUB_USB_SPEED_LOW,
+ GRUB_USB_SPEED_FULL,
+- GRUB_USB_SPEED_HIGH
++ GRUB_USB_SPEED_HIGH,
++ GRUB_USB_SPEED_SUPER
+ } grub_usb_speed_t;
+
+ typedef int (*grub_usb_iterate_hook_t) (grub_usb_device_t dev, void *data);
+--
+2.47.3
+
diff --git a/config/grub/xhci_nvme/patches/0010-usbtrans-Set-default-maximum-packet-size.patch b/config/grub/xhci_nvme/patches/0010-usbtrans-Set-default-maximum-packet-size.patch
new file mode 100644
index 00000000..938b2266
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0010-usbtrans-Set-default-maximum-packet-size.patch
@@ -0,0 +1,33 @@
+From 8de1eafa067bfaae5d58ca0c5616e159356dc6c8 Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Mon, 7 Dec 2020 08:41:23 +0100
+Subject: [PATCH 10/20] usbtrans: Set default maximum packet size
+
+Set the maximum packet size to 512 for SuperSpeed devices.
+
+Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
+---
+ grub-core/bus/usb/usbtrans.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/bus/usb/usbtrans.c b/grub-core/bus/usb/usbtrans.c
+index c5680b33a..c1080bb33 100644
+--- a/grub-core/bus/usb/usbtrans.c
++++ b/grub-core/bus/usb/usbtrans.c
+@@ -128,8 +128,12 @@ grub_usb_control_msg (grub_usb_device_t dev,
+ setupdata_addr = grub_dma_get_phys (setupdata_chunk);
+
+ /* Determine the maximum packet size. */
+- if (dev->descdev.maxsize0)
++ if (dev->descdev.maxsize0 && dev->speed != GRUB_USB_SPEED_SUPER)
+ max = dev->descdev.maxsize0;
++ else if (dev->descdev.maxsize0 && dev->speed == GRUB_USB_SPEED_SUPER)
++ max = 1UL << dev->descdev.maxsize0;
++ else if (dev->speed == GRUB_USB_SPEED_SUPER)
++ max = 512;
+ else
+ max = 64;
+
+--
+2.47.3
+
diff --git a/config/grub/xhci_nvme/patches/0011-grub-core-bus-usb-Add-function-pointer-for-attach-de.patch b/config/grub/xhci_nvme/patches/0011-grub-core-bus-usb-Add-function-pointer-for-attach-de.patch
new file mode 100644
index 00000000..41f5c1e3
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0011-grub-core-bus-usb-Add-function-pointer-for-attach-de.patch
@@ -0,0 +1,121 @@
+From 4cda4c986931067b1442c165d6d207d6948529b8 Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Sun, 15 Nov 2020 19:51:42 +0100
+Subject: [PATCH 11/20] grub-core/bus/usb: Add function pointer for
+ attach/detach events
+
+The xHCI code needs to be called for attaching or detaching a device.
+Introduce two functions pointers and call it from the USB hub code.
+
+Will be used in future commits, currently this doesn't change any functionality.
+
+Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
+---
+ grub-core/bus/usb/ehci.c | 2 ++
+ grub-core/bus/usb/ohci.c | 2 ++
+ grub-core/bus/usb/uhci.c | 2 ++
+ grub-core/bus/usb/usbhub.c | 19 +++++++++++++++++++
+ include/grub/usb.h | 4 ++++
+ 5 files changed, 29 insertions(+)
+
+diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c
+index 2db07c7c0..1ee056015 100644
+--- a/grub-core/bus/usb/ehci.c
++++ b/grub-core/bus/usb/ehci.c
+@@ -1812,6 +1812,8 @@ static struct grub_usb_controller_dev usb_controller = {
+ .hubports = grub_ehci_hubports,
+ .portstatus = grub_ehci_portstatus,
+ .detect_dev = grub_ehci_detect_dev,
++ .attach_dev = NULL,
++ .detach_dev = NULL,
+ /* estimated max. count of TDs for one bulk transfer */
+ .max_bulk_tds = GRUB_EHCI_N_TD * 3 / 4
+ };
+diff --git a/grub-core/bus/usb/ohci.c b/grub-core/bus/usb/ohci.c
+index 5363a61f6..7a3f3e154 100644
+--- a/grub-core/bus/usb/ohci.c
++++ b/grub-core/bus/usb/ohci.c
+@@ -1440,6 +1440,8 @@ static struct grub_usb_controller_dev usb_controller =
+ .hubports = grub_ohci_hubports,
+ .portstatus = grub_ohci_portstatus,
+ .detect_dev = grub_ohci_detect_dev,
++ .attach_dev = NULL,
++ .detach_dev = NULL,
+ /* estimated max. count of TDs for one bulk transfer */
+ .max_bulk_tds = GRUB_OHCI_TDS * 3 / 4
+ };
+diff --git a/grub-core/bus/usb/uhci.c b/grub-core/bus/usb/uhci.c
+index 0fdea4c1e..03c4605b2 100644
+--- a/grub-core/bus/usb/uhci.c
++++ b/grub-core/bus/usb/uhci.c
+@@ -845,6 +845,8 @@ static struct grub_usb_controller_dev usb_controller =
+ .hubports = grub_uhci_hubports,
+ .portstatus = grub_uhci_portstatus,
+ .detect_dev = grub_uhci_detect_dev,
++ .attach_dev = NULL,
++ .detach_dev = NULL,
+ /* estimated max. count of TDs for one bulk transfer */
+ .max_bulk_tds = N_TD * 3 / 4
+ };
+diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
+index 2ae29cba1..8e963e84b 100644
+--- a/grub-core/bus/usb/usbhub.c
++++ b/grub-core/bus/usb/usbhub.c
+@@ -66,6 +66,15 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
+ dev->split_hubport = split_hubport;
+ dev->split_hubaddr = split_hubaddr;
+
++ if (controller->dev->attach_dev) {
++ err = controller->dev->attach_dev (controller, dev);
++ if (err)
++ {
++ grub_free (dev);
++ return NULL;
++ }
++ }
++
+ err = grub_usb_device_initialize (dev);
+ if (err)
+ {
+@@ -405,6 +414,8 @@ static void
+ detach_device (grub_usb_device_t dev)
+ {
+ unsigned i;
++ grub_usb_err_t err;
++
+ int k;
+ if (!dev)
+ return;
+@@ -425,6 +436,14 @@ detach_device (grub_usb_device_t dev)
+ if (inter && inter->detach_hook)
+ inter->detach_hook (dev, i, k);
+ }
++ if (dev->controller.dev->detach_dev) {
++ err = dev->controller.dev->detach_dev (&dev->controller, dev);
++ if (err)
++ {
++ // XXX
++ }
++ }
++
+ grub_usb_devs[dev->addr] = 0;
+ }
+
+diff --git a/include/grub/usb.h b/include/grub/usb.h
+index ea6ee8c2c..4dd179db2 100644
+--- a/include/grub/usb.h
++++ b/include/grub/usb.h
+@@ -126,6 +126,10 @@ struct grub_usb_controller_dev
+
+ grub_usb_speed_t (*detect_dev) (grub_usb_controller_t dev, int port, int *changed);
+
++ grub_usb_err_t (*attach_dev) (grub_usb_controller_t ctrl, grub_usb_device_t dev);
++
++ grub_usb_err_t (*detach_dev) (grub_usb_controller_t ctrl, grub_usb_device_t dev);
++
+ /* Per controller flag - port reset pending, don't do another reset */
+ grub_uint64_t pending_reset;
+
+--
+2.47.3
+
diff --git a/config/grub/xhci_nvme/patches/0012-grub-core-bus-usb-usbhub-Add-new-private-fields-for-.patch b/config/grub/xhci_nvme/patches/0012-grub-core-bus-usb-usbhub-Add-new-private-fields-for-.patch
new file mode 100644
index 00000000..9e947020
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0012-grub-core-bus-usb-usbhub-Add-new-private-fields-for-.patch
@@ -0,0 +1,77 @@
+From e29dd6f6ccc73f2e4c04261c0367e1c6a0dbbce6 Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Mon, 7 Dec 2020 08:41:25 +0100
+Subject: [PATCH 12/20] grub-core/bus/usb/usbhub: Add new private fields for
+ xHCI controller
+
+Store the root port number, the route, consisting out of the port ID
+in each nibble, and a pointer to driver private data.
+
+Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
+---
+ grub-core/bus/usb/usbhub.c | 11 ++++++++---
+ include/grub/usb.h | 5 +++++
+ 2 files changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
+index 8e963e84b..b4b3a1a61 100644
+--- a/grub-core/bus/usb/usbhub.c
++++ b/grub-core/bus/usb/usbhub.c
+@@ -49,7 +49,9 @@ static grub_usb_controller_dev_t grub_usb_list;
+ static grub_usb_device_t
+ grub_usb_hub_add_dev (grub_usb_controller_t controller,
+ grub_usb_speed_t speed,
+- int split_hubport, int split_hubaddr)
++ int split_hubport, int split_hubaddr,
++ int root_portno,
++ grub_uint32_t route)
+ {
+ grub_usb_device_t dev;
+ int i;
+@@ -65,6 +67,8 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
+ dev->speed = speed;
+ dev->split_hubport = split_hubport;
+ dev->split_hubaddr = split_hubaddr;
++ dev->root_port = root_portno;
++ dev->route = route;
+
+ if (controller->dev->attach_dev) {
+ err = controller->dev->attach_dev (controller, dev);
+@@ -245,7 +249,7 @@ attach_root_port (struct grub_usb_hub *hub, int portno,
+ and full/low speed device connected to OHCI/UHCI needs not
+ transaction translation - e.g. hubport and hubaddr should be
+ always none (zero) for any device connected to any root hub. */
+- dev = grub_usb_hub_add_dev (hub->controller, speed, 0, 0);
++ dev = grub_usb_hub_add_dev (hub->controller, speed, 0, 0, portno, 0);
+ hub->controller->dev->pending_reset = 0;
+ npending--;
+ if (! dev)
+@@ -676,7 +680,8 @@ poll_nonroot_hub (grub_usb_device_t dev)
+
+ /* Add the device and assign a device address to it. */
+ next_dev = grub_usb_hub_add_dev (&dev->controller, speed,
+- split_hubport, split_hubaddr);
++ split_hubport, split_hubaddr, dev->root_port,
++ dev->route << 4 | (i & 0xf));
+ if (dev->controller.dev->pending_reset)
+ {
+ dev->controller.dev->pending_reset = 0;
+diff --git a/include/grub/usb.h b/include/grub/usb.h
+index 4dd179db2..609faf7d0 100644
+--- a/include/grub/usb.h
++++ b/include/grub/usb.h
+@@ -237,6 +237,11 @@ struct grub_usb_device
+ int split_hubport;
+
+ int split_hubaddr;
++
++ /* xHCI specific information */
++ int root_port;
++ grub_uint32_t route;
++ void *xhci_priv;
+ };
+
+
+--
+2.47.3
+
diff --git a/config/grub/xhci_nvme/patches/0013-grub-core-bus-usb-Add-xhci-support.patch b/config/grub/xhci_nvme/patches/0013-grub-core-bus-usb-Add-xhci-support.patch
new file mode 100644
index 00000000..9661a425
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0013-grub-core-bus-usb-Add-xhci-support.patch
@@ -0,0 +1,2807 @@
+From aa09b16597347353c561f55a32eb27f662b41053 Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Mon, 7 Dec 2020 08:41:26 +0100
+Subject: [PATCH 13/20] grub-core/bus/usb: Add xhci support
+
+Add support for xHCI USB controllers.
+The code is based on seabios implementation, but has been heavily
+modified to match grubs internals.
+
+Changes done in version 2:
+* Code cleanup
+* Code style fixes
+* Don't leak memory buffers
+* Compile without warnings
+* Add more defines
+* Add more helper functions
+* Don't assume a 1:1 virtual to physical mapping
+* Flush cachelines after writing buffers
+* Don't use hardcoded page size
+* Proper scratchpad register setup
+* xHCI bios ownership handoff
+
+Changes done in version 3:
+* Fixed a race condition detecting events, which doesn't appear on
+ qemu based xHCI controllers
+* Don't accidently disable USB3.0 devices after first command
+* Support arbitrary protocol speed IDs
+* Coding style cleanup
+
+Tested:
+* Qemu system x86_64
+ * virtual USB HID keyboard (usb-kbd)
+ * virtual USB HID mass storage (usb-storage)
+* init Supermicro X11SSH-F
+ * iKVM HID keyboard
+ * USB3 HID mass storage (controller root port)
+ * USB HID keyboard
+
+TODO:
+ * Test on more hardware
+ * Test on USB3 hubs
+ * Support for USB 3.1 and USB 3.2 controllers
+
+Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
+Signed-off-by: sylv <sylv@sylv.io>
+---
+ Makefile.am | 2 +-
+ grub-core/Makefile.core.def | 7 +
+ grub-core/bus/usb/xhci-pci.c | 195 +++
+ grub-core/bus/usb/xhci.c | 2496 ++++++++++++++++++++++++++++++++++
+ include/grub/usb.h | 4 +
+ 5 files changed, 2703 insertions(+), 1 deletion(-)
+ create mode 100644 grub-core/bus/usb/xhci-pci.c
+ create mode 100644 grub-core/bus/usb/xhci.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 43635d5ff..65016f856 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 xhci 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 fa4bc54aa..208badbdf 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -675,6 +675,13 @@ module = {
+ enable = arm_coreboot;
+ };
+
++module = {
++ name = xhci;
++ common = bus/usb/xhci.c;
++ pci = bus/usb/xhci-pci.c;
++ enable = pci;
++};
++
+ module = {
+ name = pci;
+ common = bus/pci.c;
+diff --git a/grub-core/bus/usb/xhci-pci.c b/grub-core/bus/usb/xhci-pci.c
+new file mode 100644
+index 000000000..a5bd3c97d
+--- /dev/null
++++ b/grub-core/bus/usb/xhci-pci.c
+@@ -0,0 +1,195 @@
++/* xhci.c - XHCI Support. */
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 2020 9elements Cyber Security
++ *
++ * 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/pci.h>
++#include <grub/cpu/pci.h>
++#include <grub/cs5536.h>
++#include <grub/misc.h>
++#include <grub/mm.h>
++#include <grub/time.h>
++#include <grub/usb.h>
++
++#define GRUB_XHCI_PCI_SBRN_REG 0x60
++#define GRUB_XHCI_ADDR_MEM_MASK (~0xff)
++
++/* USBLEGSUP bits and related OS OWNED byte offset */
++enum
++{
++ GRUB_XHCI_BIOS_OWNED = (1 << 16),
++ GRUB_XHCI_OS_OWNED = (1 << 24)
++};
++
++/* PCI iteration function... */
++static int
++grub_xhci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid,
++ void *data __attribute__ ((unused)))
++{
++ volatile grub_uint32_t *regs;
++ grub_uint32_t base, base_h;
++ grub_uint32_t eecp_offset;
++ grub_uint32_t usblegsup = 0;
++ grub_uint64_t maxtime;
++ grub_uint32_t interf;
++ grub_uint32_t subclass;
++ grub_uint32_t class;
++ grub_uint8_t release;
++ grub_uint32_t class_code;
++
++ grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: begin\n");
++
++ if (pciid == GRUB_CS5536_PCIID)
++ {
++ grub_dprintf ("xhci", "CS5536 not supported\n");
++ return 0;
++ }
++ else
++ {
++ grub_pci_address_t addr;
++ addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
++ class_code = grub_pci_read (addr) >> 8;
++ interf = class_code & 0xFF;
++ subclass = (class_code >> 8) & 0xFF;
++ class = class_code >> 16;
++
++ /* If this is not an XHCI controller, just return. */
++ if (class != 0x0c || subclass != 0x03 || interf != 0x30)
++ return 0;
++
++ grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: class OK\n");
++
++ /* Check Serial Bus Release Number */
++ addr = grub_pci_make_address (dev, GRUB_XHCI_PCI_SBRN_REG);
++ release = grub_pci_read_byte (addr);
++ if (release != 0x30)
++ {
++ grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: Wrong SBRN: %0x\n",
++ release);
++ return 0;
++ }
++ grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: bus rev. num. OK\n");
++
++ /* Determine XHCI XHCC registers base address. */
++ addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
++ base = grub_pci_read (addr);
++ addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG1);
++ base_h = grub_pci_read (addr);
++ /* Stop if registers are mapped above 4G - GRUB does not currently
++ * work with registers mapped above 4G */
++ if (((base & GRUB_PCI_ADDR_MEM_TYPE_MASK) != GRUB_PCI_ADDR_MEM_TYPE_32)
++ && (base_h != 0))
++ {
++ grub_dprintf ("xhci",
++ "XHCI grub_xhci_pci_iter: registers above 4G are not supported\n");
++ return 0;
++ }
++ base &= GRUB_PCI_ADDR_MEM_MASK;
++ if (!base)
++ {
++ grub_dprintf ("xhci",
++ "XHCI: XHCI is not mapped\n");
++ return 0;
++ }
++
++ /* Set bus master - needed for coreboot, VMware, broken BIOSes etc. */
++ addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
++ grub_pci_write_word(addr,
++ GRUB_PCI_COMMAND_MEM_ENABLED
++ | GRUB_PCI_COMMAND_BUS_MASTER
++ | grub_pci_read_word(addr));
++
++ grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: 32-bit XHCI OK\n");
++ }
++
++ grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: iobase of XHCC: %08x\n",
++ (base & GRUB_XHCI_ADDR_MEM_MASK));
++
++ regs = grub_pci_device_map_range (dev,
++ (base & GRUB_XHCI_ADDR_MEM_MASK),
++ 0x100);
++
++ /* Is there EECP ? */
++ eecp_offset = (grub_le_to_cpu32 (regs[2]) >> 8) & 0xff;
++
++ /* Determine and change ownership. */
++ /* EECP offset valid in HCCPARAMS */
++ /* Ownership can be changed via EECP only */
++ if (pciid != GRUB_CS5536_PCIID && eecp_offset >= 0x40)
++ {
++ grub_pci_address_t pciaddr_eecp;
++ pciaddr_eecp = grub_pci_make_address (dev, eecp_offset);
++
++ usblegsup = grub_pci_read (pciaddr_eecp);
++ if (usblegsup & GRUB_XHCI_BIOS_OWNED)
++ {
++ grub_boot_time ("Taking ownership of XHCI controller");
++ grub_dprintf ("xhci",
++ "XHCI grub_xhci_pci_iter: XHCI owned by: BIOS\n");
++ /* Ownership change - set OS_OWNED bit */
++ grub_pci_write (pciaddr_eecp, usblegsup | GRUB_XHCI_OS_OWNED);
++ /* Ensure PCI register is written */
++ grub_pci_read (pciaddr_eecp);
++
++ /* Wait for finish of ownership change, XHCI specification
++ * doesn't say how long it can take... */
++ maxtime = grub_get_time_ms () + 1000;
++ while ((grub_pci_read (pciaddr_eecp) & GRUB_XHCI_BIOS_OWNED)
++ && (grub_get_time_ms () < maxtime));
++ if (grub_pci_read (pciaddr_eecp) & GRUB_XHCI_BIOS_OWNED)
++ {
++ grub_dprintf ("xhci",
++ "XHCI grub_xhci_pci_iter: XHCI change ownership timeout");
++ /* Change ownership in "hard way" - reset BIOS ownership */
++ grub_pci_write (pciaddr_eecp, GRUB_XHCI_OS_OWNED);
++ /* Ensure PCI register is written */
++ grub_pci_read (pciaddr_eecp);
++ }
++ }
++ else if (usblegsup & GRUB_XHCI_OS_OWNED)
++ /* XXX: What to do in this case - nothing ? Can it happen ? */
++ grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: XHCI owned by: OS\n");
++ else
++ {
++ grub_dprintf ("xhci",
++ "XHCI grub_Xhci_pci_iter: XHCI owned by: NONE\n");
++ /* XXX: What to do in this case ? Can it happen ?
++ * Is code below correct ? */
++ /* Ownership change - set OS_OWNED bit */
++ grub_pci_write (pciaddr_eecp, GRUB_XHCI_OS_OWNED);
++ /* Ensure PCI register is written */
++ grub_pci_read (pciaddr_eecp);
++ }
++
++ /* Disable SMI, just to be sure. */
++ pciaddr_eecp = grub_pci_make_address (dev, eecp_offset + 4);
++ grub_pci_write (pciaddr_eecp, 0);
++ /* Ensure PCI register is written */
++ grub_pci_read (pciaddr_eecp);
++ }
++
++ grub_dprintf ("xhci", "inithw: XHCI grub_xhci_pci_iter: ownership OK\n");
++
++ grub_xhci_init_device (regs);
++ return 0;
++}
++
++void
++grub_xhci_pci_scan (void)
++{
++ grub_pci_iterate (grub_xhci_pci_iter, NULL);
++}
+diff --git a/grub-core/bus/usb/xhci.c b/grub-core/bus/usb/xhci.c
+new file mode 100644
+index 000000000..f4591ffb5
+--- /dev/null
++++ b/grub-core/bus/usb/xhci.c
+@@ -0,0 +1,2496 @@
++/* xhci.c - XHCI Support. */
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 2020 9elements Cyber Security
++ *
++ * 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/>.
++ *
++ * Big parts of this software are inspired by seabios XHCI implementation
++ * Released under LGPLv3. Credits to:
++ *
++ * Copyright (C) 2013 Gerd Hoffmann <kraxel@redhat.com>
++ * Copyright (C) 2014 Kevin O'Connor <kevin@koconnor.net>
++ */
++
++#include <grub/dl.h>
++#include <grub/err.h>
++#include <grub/mm.h>
++#include <grub/usb.h>
++#include <grub/usbtrans.h>
++#include <grub/misc.h>
++#include <grub/time.h>
++#include <grub/loader.h>
++#include <grub/disk.h>
++#include <grub/dma.h>
++#include <grub/cache.h>
++#include <grub/i386/cpuid.h>
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++/* This simple GRUB implementation of XHCI driver */
++/* Based on the specification
++ * "eXtensible Host Controller Interface for Universal Serial Bus" Revision 1.2
++ */
++
++
++#define xhci_get_field(data, field) \
++ (((data) >> field##_SHIFT) & field##_MASK)
++#define XHCI_PORTSC_PLS_MASK 0xf
++#define XHCI_PORTSC_PLS_SHIFT 5
++#define XHCI_PORTSC_SPEED_MASK 0xf
++#define XHCI_PORTSC_SPEED_SHIFT 10
++
++enum
++{
++ XHCI_USB_FULLSPEED = 1,
++ XHCI_USB_LOWSPEED,
++ XHCI_USB_HIGHSPEED,
++ XHCI_USB_SUPERSPEED
++};
++
++/* Chapter 5.3 Host Controller Capability Registers */
++struct grub_xhci_caps {
++ grub_uint8_t caplength;
++ grub_uint8_t reserved_01;
++ grub_uint16_t hciversion;
++ grub_uint32_t hcsparams1;
++ grub_uint32_t hcsparams2;
++ grub_uint32_t hcsparams3;
++ grub_uint32_t hccparams;
++ grub_uint32_t dboff;
++ grub_uint32_t rtsoff;
++ grub_uint32_t hccparams2;
++} GRUB_PACKED;
++
++/* extended capabilities */
++struct grub_xhci_xcap {
++ grub_uint32_t cap;
++ grub_uint32_t data[];
++} GRUB_PACKED;
++
++#define XHCI_CAP_LEGACY_SUPPORT 1
++#define XHCI_CAP_SUPPORTED_PROTOCOL 2
++
++struct xhci_portmap {
++ grub_uint8_t start;
++ grub_uint8_t count;
++} GRUB_PACKED;
++
++struct grub_xhci_op {
++ grub_uint32_t usbcmd;
++ grub_uint32_t usbsts;
++ grub_uint32_t pagesize;
++ grub_uint32_t reserved_01[2];
++ grub_uint32_t dnctl;
++ grub_uint32_t crcr_low;
++ grub_uint32_t crcr_high;
++ grub_uint32_t reserved_02[4];
++ grub_uint32_t dcbaap_low;
++ grub_uint32_t dcbaap_high;
++ grub_uint32_t config;
++} GRUB_PACKED;
++
++enum
++{
++ GRUB_XHCI_CMD_RS = (1<<0),
++ GRUB_XHCI_CMD_HCRST = (1<<1),
++ GRUB_XHCI_CMD_INTE = (1<<2),
++ GRUB_XHCI_CMD_HSEE = (1<<3),
++ GRUB_XHCI_CMD_LHCRST = (1<<7),
++ GRUB_XHCI_CMD_CSS = (1<<8),
++ GRUB_XHCI_CMD_CRS = (1<<9),
++ GRUB_XHCI_CMD_EWE = (1<<10),
++ GRUB_XHCI_CMD_EU3S = (1<<11)
++};
++
++enum
++{
++ GRUB_XHCI_STS_HCH = (1<<0),
++ GRUB_XHCI_STS_HSE = (1<<2),
++ GRUB_XHCI_STS_EINT = (1<<3),
++ GRUB_XHCI_STS_PCD = (1<<4),
++ GRUB_XHCI_STS_SSS = (1<<8),
++ GRUB_XHCI_STS_RSS = (1<<9),
++ GRUB_XHCI_STS_SRE = (1<<10),
++ GRUB_XHCI_STS_CNR = (1<<11),
++ GRUB_XHCI_STS_HCE = (1<<12)
++};
++
++
++/* Port Registers Offset */
++#define GRUB_XHCI_PR_OFFSET 0x400
++/* Interrupter Registers Offset */
++#define GRUB_XHCI_IR_OFFSET 0x20
++
++/* Port Status and Control registers offsets */
++
++/* Chapter 6 Data Structures */
++#define ALIGN_SPBA 64
++#define ALIGN_DCBAA 64
++#define ALIGN_CMD_RING_SEG 64
++#define ALIGN_EVT_RING_SEG 64
++#define ALIGN_EVT_RING_TABLE 64
++#define ALIGN_TRB 16
++#define ALIGN_INCTX 64
++#define ALIGN_SLOTCTX 32
++
++#define BOUNDARY_RING 0x10000
++
++enum
++{
++ GRUB_XHCI_PORTSC_CCS = (1<<0),
++ GRUB_XHCI_PORTSC_PED = (1<<1),
++ GRUB_XHCI_PORTSC_OCA = (1<<3),
++ GRUB_XHCI_PORTSC_PR = (1<<4),
++ GRUB_XHCI_PORTSC_PP = (1<<9),
++ GRUB_XHCI_PORTSC_SPEED_FULL = (1<<10),
++ GRUB_XHCI_PORTSC_SPEED_LOW = (2<<10),
++ GRUB_XHCI_PORTSC_SPEED_HIGH = (3<<10),
++ GRUB_XHCI_PORTSC_SPEED_SUPER = (4<<10),
++ GRUB_XHCI_PORTSC_LWS = (1<<16),
++ GRUB_XHCI_PORTSC_CSC = (1<<17),
++ GRUB_XHCI_PORTSC_PEC = (1<<18),
++ GRUB_XHCI_PORTSC_WRC = (1<<19),
++ GRUB_XHCI_PORTSC_OCC = (1<<20),
++ GRUB_XHCI_PORTSC_PRC = (1<<21),
++ GRUB_XHCI_PORTSC_PLC = (1<<22),
++ GRUB_XHCI_PORTSC_CEC = (1<<23),
++ GRUB_XHCI_PORTSC_CAS = (1<<24),
++ GRUB_XHCI_PORTSC_WCE = (1<<25),
++ GRUB_XHCI_PORTSC_WDE = (1<<26),
++ GRUB_XHCI_PORTSC_WOE = (1<<27),
++ GRUB_XHCI_PORTSC_DR = (1<<30),
++ GRUB_XHCI_PORTSC_WPR = (1<<31)
++};
++
++/* XHCI memory data structs */
++#define GRUB_XHCI_MAX_ENDPOINTS 32
++
++#define GRUB_XHCI_RING_ITEMS 128
++#define GRUB_XHCI_RING_SIZE (GRUB_XHCI_RING_ITEMS*sizeof(struct grub_xhci_trb))
++/*
++ * xhci_ring structs are allocated with XHCI_RING_SIZE alignment,
++ * then we can get it from a trb pointer (provided by evt ring).
++ */
++#define XHCI_RING(_trb) \
++ ((struct grub_xhci_ring*)((grub_uint32_t)(_trb) & ~(GRUB_XHCI_RING_SIZE-1)))
++
++/* slot context */
++struct grub_xhci_slotctx {
++ grub_uint32_t ctx[4];
++ grub_uint32_t reserved_01[4];
++} GRUB_PACKED;
++
++/* endpoint context */
++struct grub_xhci_epctx {
++ grub_uint32_t ctx[2];
++ grub_uint32_t deq_low;
++ grub_uint32_t deq_high;
++ grub_uint32_t length;
++ grub_uint32_t reserved_01[3];
++} GRUB_PACKED;
++
++/* device context array element */
++struct grub_xhci_devlist {
++ grub_uint32_t ptr_low;
++ grub_uint32_t ptr_high;
++} GRUB_PACKED;
++
++/* input context */
++struct grub_xhci_inctx {
++ grub_uint32_t del;
++ grub_uint32_t add;
++ grub_uint32_t reserved_01[6];
++} GRUB_PACKED;
++
++/* transfer block (ring element) */
++struct grub_xhci_trb {
++ grub_uint32_t ptr_low;
++ grub_uint32_t ptr_high;
++ grub_uint32_t status;
++ grub_uint32_t control;
++} GRUB_PACKED;
++
++#define TRB_C (1<<0)
++#define TRB_TYPE_SHIFT 10
++#define TRB_TYPE_MASK 0x3f
++#define TRB_TYPE(t) (((t) >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK)
++
++#define TRB_EV_ED (1<<2)
++
++#define TRB_TR_ENT (1<<1)
++#define TRB_TR_ISP (1<<2)
++#define TRB_TR_NS (1<<3)
++#define TRB_TR_CH (1<<4)
++#define TRB_TR_IOC (1<<5)
++#define TRB_TR_IDT (1<<6)
++#define TRB_TR_TBC_SHIFT 7
++#define TRB_TR_TBC_MASK 0x3
++#define TRB_TR_BEI (1<<9)
++#define TRB_TR_TLBPC_SHIFT 16
++#define TRB_TR_TLBPC_MASK 0xf
++#define TRB_TR_FRAMEID_SHIFT 20
++#define TRB_TR_FRAMEID_MASK 0x7ff
++#define TRB_TR_SIA (1<<31)
++
++#define TRB_TR_DIR (1<<16)
++
++#define TRB_CR_SLOTID_SHIFT 24
++#define TRB_CR_SLOTID_MASK 0xff
++#define TRB_CR_EPID_SHIFT 16
++#define TRB_CR_EPID_MASK 0x1f
++
++#define TRB_CR_BSR (1<<9)
++#define TRB_CR_DC (1<<9)
++
++#define TRB_LK_TC (1<<1)
++
++#define TRB_INTR_SHIFT 22
++#define TRB_INTR_MASK 0x3ff
++#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK)
++
++typedef enum TRBType {
++ TRB_RESERVED = 0,
++ TR_NORMAL,
++ TR_SETUP,
++ TR_DATA,
++ TR_STATUS,
++ TR_ISOCH,
++ TR_LINK,
++ TR_EVDATA,
++ TR_NOOP,
++ CR_ENABLE_SLOT,
++ CR_DISABLE_SLOT,
++ CR_ADDRESS_DEVICE,
++ CR_CONFIGURE_ENDPOINT,
++ CR_EVALUATE_CONTEXT,
++ CR_RESET_ENDPOINT,
++ CR_STOP_ENDPOINT,
++ CR_SET_TR_DEQUEUE,
++ CR_RESET_DEVICE,
++ CR_FORCE_EVENT,
++ CR_NEGOTIATE_BW,
++ CR_SET_LATENCY_TOLERANCE,
++ CR_GET_PORT_BANDWIDTH,
++ CR_FORCE_HEADER,
++ CR_NOOP,
++ ER_TRANSFER = 32,
++ ER_COMMAND_COMPLETE,
++ ER_PORT_STATUS_CHANGE,
++ ER_BANDWIDTH_REQUEST,
++ ER_DOORBELL,
++ ER_HOST_CONTROLLER,
++ ER_DEVICE_NOTIFICATION,
++ ER_MFINDEX_WRAP,
++} TRBType;
++
++typedef enum TRBCCode {
++ CC_INVALID = 0,
++ CC_SUCCESS,
++ CC_DATA_BUFFER_ERROR,
++ CC_BABBLE_DETECTED,
++ CC_USB_TRANSACTION_ERROR,
++ CC_TRB_ERROR,
++ CC_STALL_ERROR,
++ CC_RESOURCE_ERROR,
++ CC_BANDWIDTH_ERROR,
++ CC_NO_SLOTS_ERROR,
++ CC_INVALID_STREAM_TYPE_ERROR,
++ CC_SLOT_NOT_ENABLED_ERROR,
++ CC_EP_NOT_ENABLED_ERROR,
++ CC_SHORT_PACKET,
++ CC_RING_UNDERRUN,
++ CC_RING_OVERRUN,
++ CC_VF_ER_FULL,
++ CC_PARAMETER_ERROR,
++ CC_BANDWIDTH_OVERRUN,
++ CC_CONTEXT_STATE_ERROR,
++ CC_NO_PING_RESPONSE_ERROR,
++ CC_EVENT_RING_FULL_ERROR,
++ CC_INCOMPATIBLE_DEVICE_ERROR,
++ CC_MISSED_SERVICE_ERROR,
++ CC_COMMAND_RING_STOPPED,
++ CC_COMMAND_ABORTED,
++ CC_STOPPED,
++ CC_STOPPED_LENGTH_INVALID,
++ CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29,
++ CC_ISOCH_BUFFER_OVERRUN = 31,
++ CC_EVENT_LOST_ERROR,
++ CC_UNDEFINED_ERROR,
++ CC_INVALID_STREAM_ID_ERROR,
++ CC_SECONDARY_BANDWIDTH_ERROR,
++ CC_SPLIT_TRANSACTION_ERROR
++} TRBCCode;
++
++enum {
++ PLS_U0 = 0,
++ PLS_U1 = 1,
++ PLS_U2 = 2,
++ PLS_U3 = 3,
++ PLS_DISABLED = 4,
++ PLS_RX_DETECT = 5,
++ PLS_INACTIVE = 6,
++ PLS_POLLING = 7,
++ PLS_RECOVERY = 8,
++ PLS_HOT_RESET = 9,
++ PLS_COMPILANCE_MODE = 10,
++ PLS_TEST_MODE = 11,
++ PLS_RESUME = 15,
++};
++
++/* event ring segment */
++struct grub_xhci_er_seg {
++ grub_uint32_t ptr_low;
++ grub_uint32_t ptr_high;
++ grub_uint32_t size;
++ grub_uint32_t reserved_01;
++} GRUB_PACKED;
++
++struct grub_xhci_ring {
++ struct grub_xhci_trb ring[GRUB_XHCI_RING_ITEMS];
++ struct grub_xhci_trb evt;
++ grub_uint32_t eidx;
++ grub_uint32_t nidx;
++ grub_uint32_t cs;
++};
++
++/* port registers */
++struct grub_xhci_pr {
++ grub_uint32_t portsc;
++ grub_uint32_t portpmsc;
++ grub_uint32_t portli;
++ grub_uint32_t reserved_01;
++} GRUB_PACKED;
++
++/* doorbell registers */
++struct grub_xhci_db {
++ grub_uint32_t doorbell;
++} GRUB_PACKED;
++
++/* runtime registers */
++struct grub_xhci_rts {
++ grub_uint32_t mfindex;
++} GRUB_PACKED;
++
++/* interrupter registers */
++struct grub_xhci_ir {
++ grub_uint32_t iman;
++ grub_uint32_t imod;
++ grub_uint32_t erstsz;
++ grub_uint32_t reserved_01;
++ grub_uint32_t erstba_low;
++ grub_uint32_t erstba_high;
++ grub_uint32_t erdp_low;
++ grub_uint32_t erdp_high;
++} GRUB_PACKED;
++
++struct grub_xhci_psid {
++ grub_uint8_t id;
++ grub_uint8_t psie;
++ grub_uint16_t psim;
++ grub_uint64_t bitrate;
++ grub_usb_speed_t grub_usb_speed;
++};
++
++struct grub_xhci_psids {
++ grub_uint8_t major;
++ grub_uint8_t minor;
++ struct grub_xhci_psid psids[16];
++};
++
++struct grub_xhci
++{
++ grub_uint8_t shutdown; /* 1 if preparing shutdown of controller */
++ /* xhci registers */
++ volatile struct grub_xhci_caps *caps; /* Capability registers */
++ volatile struct grub_xhci_op *op; /* Operational registers */
++ volatile struct grub_xhci_pr *pr; /* Port Registers */
++ volatile struct grub_xhci_db *db; /* doorbell */
++ volatile struct grub_xhci_ir *ir; /* Interrupt Registers */
++ /* devinfo */
++ grub_uint32_t xcap;
++ grub_uint32_t ports;
++ grub_uint32_t slots;
++ grub_uint8_t flag64;
++ grub_uint16_t spb;
++ grub_uint32_t pagesize;
++ struct xhci_portmap usb2;
++ struct xhci_portmap usb3;
++ struct grub_xhci_psids *psids;
++ /* xhci data structures */
++ struct grub_pci_dma_chunk *devs_dma;
++ volatile struct grub_xhci_devlist *devs;
++ struct grub_pci_dma_chunk *cmds_dma;
++ volatile struct grub_xhci_ring *cmds;
++ struct grub_pci_dma_chunk *evts_dma;
++ volatile struct grub_xhci_ring *evts;
++ struct grub_pci_dma_chunk *eseg_dma;
++ volatile struct grub_xhci_er_seg *eseg;
++ struct grub_pci_dma_chunk *spba_dma;
++ struct grub_pci_dma_chunk *spad_dma;
++
++ struct grub_xhci *next;
++};
++
++struct grub_xhci_priv {
++ grub_uint8_t slotid;
++ grub_uint32_t max_packet;
++ struct grub_pci_dma_chunk *enpoint_trbs_dma[32];
++ volatile struct grub_xhci_ring *enpoint_trbs[32];
++ struct grub_pci_dma_chunk *slotctx_dma;
++};
++
++struct grub_xhci_port {
++ grub_uint32_t portsc;
++ grub_uint32_t portpmsc;
++ grub_uint32_t portli;
++ grub_uint32_t reserved_01;
++};
++
++struct grub_xhci_transfer_controller_data {
++ grub_uint32_t transfer_size;
++};
++
++static struct grub_xhci *xhci;
++
++/****************************************************************
++ * general access functions
++ ****************************************************************/
++
++static inline void
++grub_xhci_write32(volatile void *addr, grub_uint32_t val) {
++ *(volatile grub_uint32_t *)addr = val;
++}
++static inline void
++grub_xhci_write16(volatile void *addr, grub_uint16_t val) {
++ *(volatile grub_uint16_t *)addr = val;
++}
++static inline void
++grub_xhci_write8(void *addr, grub_uint8_t val) {
++ *(volatile grub_uint8_t *)addr = val;
++}
++
++static inline grub_uint32_t
++grub_xhci_read32(volatile void *addr) {
++ return grub_le_to_cpu32 (*((volatile grub_uint32_t *)addr));
++}
++
++static inline grub_uint16_t
++grub_xhci_read16(volatile void *addr) {
++ return grub_le_to_cpu16 (*((volatile grub_uint32_t *)addr));
++}
++static inline grub_uint8_t
++grub_xhci_read8(volatile void *addr) {
++ return (*((volatile grub_uint32_t *)addr));
++}
++
++static inline grub_uint32_t
++grub_xhci_port_read (struct grub_xhci *x, grub_uint32_t port)
++{
++ return grub_xhci_read32(&x->pr[port].portsc);
++}
++
++static inline void
++grub_xhci_port_write (struct grub_xhci *x, grub_uint32_t port,
++ grub_uint32_t and_mask, grub_uint32_t or_mask)
++{
++ grub_uint32_t reg = grub_xhci_port_read(x, port);
++ reg &= and_mask;
++ reg |= or_mask;
++
++ grub_xhci_write32(&x->pr[port].portsc, reg);
++}
++
++/****************************************************************
++ * xhci status and support functions
++ ****************************************************************/
++
++static grub_uint32_t xhci_get_pagesize(struct grub_xhci *x)
++{
++ /* Chapter 5.4.3 Page Size Register (PAGESIZE) */
++ for (grub_uint8_t i = 0; i < 16; i++)
++ {
++ if (grub_xhci_read32(&x->op->pagesize) & (1 << i))
++ return 1 << (12 + i);
++ }
++ return 0;
++}
++
++static grub_uint8_t xhci_is_halted(struct grub_xhci *x)
++{
++ return !!(grub_xhci_read32(&x->op->usbsts) & 1);
++}
++
++/* Just for debugging */
++static void xhci_check_status(struct grub_xhci *x)
++{
++ grub_uint32_t reg;
++
++ reg = grub_xhci_read32(&x->op->usbsts);
++ if (reg & 1)
++ grub_dprintf("xhci", "%s: xHCI halted\n", __func__);
++ if (reg & 2)
++ grub_dprintf("xhci", "%s: Host system error detected\n", __func__);
++ if (reg & (1 << 12))
++ grub_dprintf("xhci", "%s: Internal error detected\n", __func__);
++ reg = grub_xhci_read32(&x->op->crcr_low);
++ if (reg & (1 << 3))
++ grub_dprintf("xhci", "%s: Command ring running\n", __func__);
++}
++
++/* xhci_memalign_dma32 allocates DMA memory satisfying alignment and boundary
++ * requirements without wasting to much memory */
++static struct grub_pci_dma_chunk *
++xhci_memalign_dma32(grub_size_t align,
++ grub_size_t size,
++ grub_size_t boundary)
++{
++ struct grub_pci_dma_chunk *tmp;
++ const grub_uint32_t mask = boundary - 1;
++ grub_uint32_t start, end;
++
++ /* Allocate some memory and check if it doesn't cross boundary */
++ tmp = grub_memalign_dma32(align, size);
++ start = grub_dma_get_phys(tmp);
++ end = start + size - 1;
++ if ((start & mask) == (end & mask))
++ return tmp;
++ /* Buffer isn't usable, allocate bigger one */
++ grub_dma_free(tmp);
++
++ return grub_memalign_dma32(boundary, size);
++}
++
++/****************************************************************
++ * helper functions for in context DMA buffer
++ ****************************************************************/
++
++static int
++grub_xhci_inctx_size(struct grub_xhci *x)
++{
++ const grub_uint8_t cnt = GRUB_XHCI_MAX_ENDPOINTS + 1;
++ return (sizeof(struct grub_xhci_inctx) * cnt) << x->flag64;
++}
++
++static void
++grub_xhci_inctx_sync_dma_caches(struct grub_xhci *x, struct grub_pci_dma_chunk *inctx)
++{
++ grub_arch_sync_dma_caches(inctx, grub_xhci_inctx_size(x));
++}
++
++static struct grub_pci_dma_chunk *
++grub_xhci_alloc_inctx(struct grub_xhci *x, int maxepid,
++ struct grub_usb_device *dev)
++{
++ int size = grub_xhci_inctx_size(x);
++ struct grub_pci_dma_chunk *dma = xhci_memalign_dma32(ALIGN_INCTX, size,
++ x->pagesize);
++ if (!dma)
++ return NULL;
++
++ volatile struct grub_xhci_inctx *in = grub_dma_get_virt(dma);
++ grub_memset((void *)in, 0, size);
++
++ struct grub_xhci_slotctx *slot = (void*)&in[1 << x->flag64];
++ slot->ctx[0] |= maxepid << 27; /* context entries */
++ grub_dprintf("xhci", "%s: speed=%d root_port=%d\n", __func__, dev->speed, dev->root_port);
++ switch (dev->speed) {
++ case GRUB_USB_SPEED_FULL:
++ slot->ctx[0] |= XHCI_USB_FULLSPEED << 20;
++ break;
++ case GRUB_USB_SPEED_HIGH:
++ slot->ctx[0] |= XHCI_USB_HIGHSPEED << 20;
++ break;
++ case GRUB_USB_SPEED_LOW:
++ slot->ctx[0] |= XHCI_USB_LOWSPEED << 20;
++ break;
++ case GRUB_USB_SPEED_SUPER:
++ slot->ctx[0] |= XHCI_USB_SUPERSPEED << 20;
++ break;
++ case GRUB_USB_SPEED_NONE:
++ slot->ctx[0] |= 0 << 20;
++ break;
++ }
++
++ /* Route is greater zero on devices that are connected to a non root hub */
++ if (dev->route)
++ {
++ /* FIXME: Implement this code for non SuperSpeed hub devices */
++ }
++ slot->ctx[0] |= dev->route;
++ slot->ctx[1] |= (dev->root_port+1) << 16;
++
++ grub_arch_sync_dma_caches(in, size);
++
++ return dma;
++}
++
++/****************************************************************
++ * xHCI event processing
++ ****************************************************************/
++
++/* Dequeue events on the XHCI event ring generated by the hardware */
++static void xhci_process_events(struct grub_xhci *x)
++{
++ volatile struct grub_xhci_ring *evts = x->evts;
++ /* XXX invalidate caches */
++
++ for (;;) {
++ /* check for event */
++ grub_uint32_t nidx = grub_xhci_read32(&evts->nidx);
++ grub_uint32_t cs = grub_xhci_read32(&evts->cs);
++ volatile struct grub_xhci_trb *etrb = evts->ring + nidx;
++ grub_uint32_t control = grub_xhci_read32(&etrb->control);
++ if ((control & TRB_C) != (cs ? 1 : 0))
++ return;
++
++ /* process event */
++ grub_uint32_t evt_type = TRB_TYPE(control);
++ grub_uint32_t evt_cc = (grub_xhci_read32(&etrb->status) >> 24) & 0xff;
++
++ switch (evt_type)
++ {
++ case ER_TRANSFER:
++ case ER_COMMAND_COMPLETE:
++ {
++ struct grub_xhci_trb *rtrb = (void*)grub_xhci_read32(&etrb->ptr_low);
++ struct grub_xhci_ring *ring = XHCI_RING(rtrb);
++ volatile struct grub_xhci_trb *evt = &ring->evt;
++ grub_uint32_t eidx = rtrb - ring->ring + 1;
++ grub_dprintf("xhci", "%s: ring %p [trb %p, evt %p, type %d, eidx %d, cc %d]\n",
++ __func__, ring, rtrb, evt, evt_type, eidx, evt_cc);
++ *evt = *etrb;
++ grub_xhci_write32(&ring->eidx, eidx);
++ break;
++ }
++ case ER_PORT_STATUS_CHANGE:
++ {
++ /* Nothing to do here. grub_xhci_detect_dev will handle it */
++ break;
++ }
++ default:
++ {
++ grub_dprintf("xhci", "%s: unknown event, type %d, cc %d\n",
++ __func__, evt_type, evt_cc);
++ break;
++ }
++ }
++
++ /* move ring index, notify xhci */
++ nidx++;
++ if (nidx == GRUB_XHCI_RING_ITEMS)
++ {
++ nidx = 0;
++ cs = cs ? 0 : 1;
++ grub_xhci_write32(&evts->cs, cs);
++ }
++ grub_xhci_write32(&evts->nidx, nidx);
++ volatile struct grub_xhci_ir *ir = x->ir;
++ grub_uint32_t erdp = (grub_uint32_t)(evts->ring + nidx);
++ grub_xhci_write32(&ir->erdp_low, erdp);
++ grub_xhci_write32(&ir->erdp_high, 0);
++ }
++}
++
++/****************************************************************
++ * TRB handling
++ ****************************************************************/
++
++/* Signal the hardware to process events on a TRB ring */
++static void xhci_doorbell(struct grub_xhci *x, grub_uint32_t slotid, grub_uint32_t value)
++{
++ xhci_check_status(x);
++ grub_dprintf("xhci", "%s: slotid %d, epid %d\n", __func__, slotid, value);
++ grub_xhci_write32(&x->db[slotid].doorbell, value);
++}
++
++/* Check if a ring has any pending TRBs */
++static int xhci_ring_busy(volatile struct grub_xhci_ring *ring)
++{
++ grub_uint32_t eidx = grub_xhci_read32(&ring->eidx);
++ grub_uint32_t nidx = grub_xhci_read32(&ring->nidx);
++
++ return (eidx != nidx);
++}
++
++/* Returns free space in ring */
++static int xhci_ring_free_space(volatile struct grub_xhci_ring *ring)
++{
++ grub_uint32_t eidx = grub_xhci_read32(&ring->eidx);
++ grub_uint32_t nidx = grub_xhci_read32(&ring->nidx);
++
++ /* nidx is never 0, so reduce ring buffer size by one */
++ return (eidx > nidx) ? eidx-nidx
++ : (ARRAY_SIZE(ring->ring) - 1) - nidx + eidx;
++}
++
++/* Check if a ring is full */
++static int xhci_ring_full(volatile struct grub_xhci_ring *ring)
++{
++ /* Might need to insert one link TRB */
++ return xhci_ring_free_space(ring) <= 1;
++}
++
++/* Check if a ring is almost full */
++static int xhci_ring_almost_full(volatile struct grub_xhci_ring *ring)
++{
++ /* Might need to insert one link TRB */
++ return xhci_ring_free_space(ring) <= 2;
++}
++
++/* Wait for a ring to empty (all TRBs processed by hardware) */
++static int xhci_event_wait(struct grub_xhci *x,
++ volatile struct grub_xhci_ring *ring,
++ grub_uint32_t timeout)
++{
++ grub_uint32_t end = grub_get_time_ms () + timeout;
++
++ for (;;)
++ {
++ xhci_check_status(x);
++ xhci_process_events(x);
++ if (!xhci_ring_busy(ring))
++ {
++ grub_uint32_t status = ring->evt.status;
++ return (status >> 24) & 0xff;
++ }
++ if (grub_get_time_ms () > end)
++ {
++ xhci_check_status(x);
++ grub_dprintf("xhci", "%s: Timeout waiting for event\n", __func__);
++ return -1;
++ }
++ }
++}
++
++/* Add a TRB to the given ring, either regular or inline */
++static void xhci_trb_fill(volatile struct grub_xhci_ring *ring,
++ grub_uint64_t ptr, grub_uint32_t xferlen,
++ grub_uint32_t flags)
++{
++ volatile struct grub_xhci_trb *dst = &ring->ring[ring->nidx];
++ dst->ptr_low = ptr & 0xffffffff;
++ dst->ptr_high = ptr >> 32;
++ dst->status = xferlen;
++ dst->control = flags | (ring->cs ? TRB_C : 0);
++
++ grub_arch_sync_dma_caches(dst, sizeof(ring->ring[0]));
++}
++
++/*
++ * Queue a TRB onto a ring.
++ *
++ * The caller must pass a pointer to the data in physical address-space or the
++ * data itself (but no more than 8 bytes) in data_or_addr. Inline data must have
++ * the flag TRB_TR_IDT set.
++ */
++static void xhci_trb_queue(volatile struct grub_xhci_ring *ring,
++ grub_uint64_t data_or_addr,
++ grub_uint32_t xferlen, grub_uint32_t flags)
++{
++ grub_dprintf("xhci", "%s: ring %p data %llx len %d flags 0x%x remain 0x%x\n", __func__,
++ ring, data_or_addr, xferlen & 0x1ffff, flags, xferlen >> 17);
++
++ if (xhci_ring_full(ring))
++ {
++ grub_dprintf("xhci", "%s: ERROR: ring %p is full, discarding TRB\n",
++ __func__, ring);
++ return;
++ }
++
++ if (ring->nidx >= ARRAY_SIZE(ring->ring) - 1)
++ {
++ /* Reset to command buffer pointer to the first element */
++ xhci_trb_fill(ring, (grub_addr_t)ring->ring, 0, (TR_LINK << 10) | TRB_LK_TC);
++ ring->nidx = 0;
++ ring->cs ^= 1;
++ grub_dprintf("xhci", "%s: ring %p [linked]\n", __func__, ring);
++ }
++
++ xhci_trb_fill(ring, data_or_addr, xferlen, flags);
++ ring->nidx++;
++ grub_dprintf("xhci", "%s: ring %p [nidx %d, len %d]\n",
++ __func__, ring, ring->nidx, xferlen);
++}
++
++/*
++ * Queue a TRB onto a ring and flush it if necessary.
++ *
++ * The caller must pass a pointer to the data in physical address-space or the
++ * data itself (but no more than 8 bytes) in data_or_addr. Inline data must have
++ * the flag TRB_TR_IDT set.
++ */
++static int xhci_trb_queue_and_flush(struct grub_xhci *x,
++ grub_uint32_t slotid,
++ grub_uint32_t epid,
++ volatile struct grub_xhci_ring *ring,
++ grub_uint64_t data_or_addr,
++ grub_uint32_t xferlen, grub_uint32_t flags)
++{
++ grub_uint8_t submit = 0;
++ if (xhci_ring_almost_full(ring))
++ {
++ grub_dprintf("xhci", "%s: almost full e %d n %d\n", __func__, ring->eidx, ring->nidx);
++ flags |= TRB_TR_IOC;
++ submit = 1;
++ }
++ /* Note: xhci_trb_queue might queue on or two elements, if the end of the TRB
++ * has been reached. The caller must account for that when filling the TRB. */
++ xhci_trb_queue(ring, data_or_addr, xferlen, flags);
++ /* Submit if less no free slot is remaining, we might need an additional
++ * one on the next call to this function. */
++ if (submit)
++ {
++ xhci_doorbell(x, slotid, epid);
++ int rc = xhci_event_wait(x, ring, 1000);
++ grub_dprintf("xhci", "%s: xhci_event_wait = %d\n", __func__, rc);
++ return rc;
++ }
++ return 0;
++}
++
++/****************************************************************
++ * xHCI command functions
++ ****************************************************************/
++
++/* Submit a command to the xHCI command TRB */
++static int xhci_cmd_submit(struct grub_xhci *x,
++ struct grub_pci_dma_chunk *inctx_dma,
++ grub_uint32_t flags)
++{
++ volatile struct grub_xhci_inctx *inctx;
++ /* Don't submit if halted, it will fail */
++ if (xhci_is_halted(x))
++ return -1;
++
++ if (inctx_dma)
++ {
++ grub_xhci_inctx_sync_dma_caches(x, inctx_dma);
++
++ inctx = grub_dma_get_virt(inctx_dma);
++
++ struct grub_xhci_slotctx *slot = (void*)&inctx[1 << x->flag64];
++ grub_uint32_t port = ((slot->ctx[1] >> 16) & 0xff) - 1;
++ grub_uint32_t portsc = grub_xhci_port_read(x, port);
++ if (!(portsc & GRUB_XHCI_PORTSC_CCS))
++ {
++ grub_dprintf("xhci", "%s: root port %d no longer connected\n",
++ __func__, port);
++ return -1;
++ }
++ xhci_trb_queue(x->cmds, grub_dma_get_phys(inctx_dma), 0, flags);
++ }
++ else
++ {
++ xhci_trb_queue(x->cmds, 0, 0, flags);
++ }
++
++ xhci_doorbell(x, 0, 0);
++ int rc = xhci_event_wait(x, x->cmds, 1000);
++ grub_dprintf("xhci", "%s: xhci_event_wait = %d\n", __func__, rc);
++
++ return rc;
++}
++
++static int xhci_cmd_enable_slot(struct grub_xhci *x)
++{
++ grub_uint32_t flags = 0;
++ flags |= (CR_ENABLE_SLOT << 10);
++
++ grub_dprintf("xhci", "%s:\n", __func__);
++ int cc = xhci_cmd_submit(x, NULL, flags);
++ if (cc != CC_SUCCESS)
++ return -1;
++ grub_dprintf("xhci", "%s: %p\n", __func__, &x->cmds->evt.control);
++ grub_dprintf("xhci", "%s: %x\n", __func__, grub_xhci_read32(&x->cmds->evt.control));
++
++ return (grub_xhci_read32(&x->cmds->evt.control) >> 24) & 0xff;
++}
++
++static int xhci_cmd_disable_slot(struct grub_xhci *x, grub_uint32_t slotid)
++{
++ grub_uint32_t flags = 0;
++ flags |= (CR_DISABLE_SLOT << 10);
++ flags |= (slotid << 24);
++
++ grub_dprintf("xhci", "%s: slotid %d\n", __func__, slotid);
++ return xhci_cmd_submit(x, NULL, flags);
++}
++
++static int xhci_cmd_stop_endpoint(struct grub_xhci *x, grub_uint32_t slotid
++ , grub_uint32_t epid
++ , grub_uint32_t suspend)
++{
++ grub_uint32_t flags = 0;
++ flags |= (CR_STOP_ENDPOINT << 10);
++ flags |= (epid << 16);
++ flags |= (suspend << 23) ;
++ flags |= (slotid << 24);
++
++ return xhci_cmd_submit(x, NULL, flags);
++}
++
++static int xhci_cmd_reset_endpoint(struct grub_xhci *x, grub_uint32_t slotid
++ , grub_uint32_t epid
++ , grub_uint32_t preserve)
++{
++ grub_uint32_t flags = 0;
++ flags |= (preserve << 9);
++ flags |= (CR_RESET_ENDPOINT << 10);
++ flags |= (epid << 16);
++ flags |= (slotid << 24);
++
++ return xhci_cmd_submit(x, NULL, flags);
++}
++
++static int xhci_cmd_set_dequeue_pointer(struct grub_xhci *x, grub_uint32_t slotid
++ , grub_uint32_t epid
++ , grub_addr_t tr_deque_pointer)
++{
++ grub_uint32_t flags = 0;
++ flags |= (CR_SET_TR_DEQUEUE << 10);
++ flags |= (epid << 16);
++ flags |= (slotid << 24);
++
++ xhci_trb_queue(x->cmds, tr_deque_pointer, 0, flags);
++
++ xhci_doorbell(x, 0, 0);
++ int rc = xhci_event_wait(x, x->cmds, 1000);
++ grub_dprintf("xhci", "%s: xhci_event_wait = %d\n", __func__, rc);
++
++ return rc;
++}
++
++static int xhci_cmd_address_device(struct grub_xhci *x, grub_uint32_t slotid,
++ struct grub_pci_dma_chunk *inctx_dma)
++{
++ grub_uint32_t flags = 0;
++ flags |= (CR_ADDRESS_DEVICE << 10);
++ flags |= (slotid << 24);
++
++ grub_dprintf("xhci", "%s: slotid %d\n", __func__, slotid);
++ return xhci_cmd_submit(x, inctx_dma, flags);
++}
++
++static int xhci_cmd_configure_endpoint(struct grub_xhci *x, grub_uint32_t slotid,
++ struct grub_pci_dma_chunk *inctx_dma)
++{
++ grub_uint32_t flags = 0;
++ flags |= (CR_CONFIGURE_ENDPOINT << 10);
++ flags |= (slotid << 24);
++
++ grub_dprintf("xhci", "%s: slotid %d\n", __func__, slotid);
++ return xhci_cmd_submit(x, inctx_dma, flags);
++}
++
++static int xhci_cmd_evaluate_context(struct grub_xhci *x, grub_uint32_t slotid,
++ struct grub_pci_dma_chunk *inctx_dma)
++{
++ grub_uint32_t flags = 0;
++ flags |= (CR_EVALUATE_CONTEXT << 10);
++ flags |= (slotid << 24);
++
++ grub_dprintf("xhci", "%s: slotid %d\n", __func__, slotid);
++ return xhci_cmd_submit(x, inctx_dma, flags);
++}
++
++/****************************************************************
++ * xHCI host controller initialization
++ ****************************************************************/
++
++static grub_usb_err_t
++grub_xhci_reset (struct grub_xhci *x)
++{
++ grub_uint32_t reg;
++ grub_uint32_t end;
++
++ reg = grub_xhci_read32(&x->op->usbcmd);
++ if (reg & GRUB_XHCI_CMD_RS)
++ {
++ reg &= ~GRUB_XHCI_CMD_RS;
++ grub_xhci_write32(&x->op->usbcmd, reg);
++
++ end = grub_get_time_ms () + 32;
++ while (grub_xhci_read32(&x->op->usbcmd) & GRUB_XHCI_STS_HCH)
++ {
++ if (grub_get_time_ms () > end)
++ return GRUB_USB_ERR_TIMEOUT;
++
++ grub_millisleep(1);
++ }
++ }
++
++ grub_dprintf("xhci", "grub_xhci_reset: resetting HC\n");
++ grub_xhci_write32(&x->op->usbcmd, GRUB_XHCI_CMD_HCRST);
++
++ /* Wait for device to complete reset and be enabled */
++ end = grub_get_time_ms () + 100;
++ while (grub_xhci_read32(&x->op->usbcmd) & GRUB_XHCI_CMD_HCRST)
++ {
++ if (grub_get_time_ms () > end)
++ return GRUB_USB_ERR_TIMEOUT;
++
++ grub_millisleep(1);
++ }
++
++ /* Wait for device to complete reset and be enabled */
++ end = grub_get_time_ms () + 100;
++ while (grub_xhci_read32(&x->op->usbsts) & GRUB_XHCI_STS_CNR)
++ {
++ if (grub_get_time_ms () > end)
++ return GRUB_USB_ERR_TIMEOUT;
++
++ grub_millisleep(1);
++ }
++
++ grub_xhci_write32(&x->op->config, x->slots);
++ grub_xhci_write32(&x->op->dcbaap_low, grub_dma_get_phys(x->devs_dma));
++ grub_xhci_write32(&x->op->dcbaap_high, 0);
++ grub_xhci_write32(&x->op->crcr_low, grub_dma_get_phys(x->cmds_dma)| 1);
++ grub_xhci_write32(&x->op->crcr_high, 0);
++ x->cmds->cs = 1;
++
++ grub_arch_sync_dma_caches(x->cmds, sizeof(*x->cmds));
++
++ x->eseg->ptr_low = grub_dma_get_phys(x->evts_dma);
++ x->eseg->ptr_high = 0;
++ x->eseg->size = GRUB_XHCI_RING_ITEMS;
++
++ grub_arch_sync_dma_caches(x->eseg, sizeof(*x->eseg));
++
++ grub_xhci_write32(&x->ir->erstsz, 1);
++ grub_xhci_write32(&x->ir->erdp_low, grub_dma_get_phys(x->evts_dma));
++ grub_xhci_write32(&x->ir->erdp_high, 0);
++ grub_xhci_write32(&x->ir->erstba_low, grub_dma_get_phys(x->eseg_dma));
++ grub_xhci_write32(&x->ir->erstba_high, 0);
++ x->evts->cs = 1;
++
++ grub_arch_sync_dma_caches(x->evts, sizeof(*x->eseg));
++
++ xhci_check_status(x);
++
++ grub_dprintf ("xhci", "XHCI OP COMMAND: %08x\n",
++ grub_xhci_read32 (&x->op->usbcmd));
++ grub_dprintf ("xhci", "XHCI OP STATUS: %08x\n",
++ grub_xhci_read32 (&x->op->usbsts));
++ grub_dprintf ("xhci", "XHCI OP PAGESIZE: %08x\n",
++ grub_xhci_read32 (&x->op->pagesize));
++ grub_dprintf ("xhci", "XHCI OP DNCTRL: %08x\n",
++ grub_xhci_read32 (&x->op->dnctl));
++ grub_dprintf ("xhci", "XHCI OP CRCR_LOW: %08x\n",
++ grub_xhci_read32 (&x->op->crcr_low));
++ grub_dprintf ("xhci", "XHCI OP CRCR_HIGH: %08x\n",
++ grub_xhci_read32 (&x->op->crcr_high));
++ grub_dprintf ("xhci", "XHCI OP DCBAAP_LOW: %08x\n",
++ grub_xhci_read32 (&x->op->dcbaap_low));
++ grub_dprintf ("xhci", "XHCI OP DCBAAP_HIGH: %08x\n",
++ grub_xhci_read32 (&x->op->dcbaap_high));
++ grub_dprintf ("xhci", "XHCI OP CONFIG: %08x\n",
++ grub_xhci_read32 (&x->op->config));
++ grub_dprintf ("xhci", "XHCI IR ERSTSZ: %08x\n",
++ grub_xhci_read32 (&x->ir->erstsz));
++ grub_dprintf ("xhci", "XHCI IR ERDP: %08x\n",
++ grub_xhci_read32 (&x->ir->erdp_low));
++ grub_dprintf ("xhci", "XHCI IR ERSTBA: %08x\n",
++ grub_xhci_read32 (&x->ir->erstba_low));
++
++ xhci_check_status(x);
++
++ return GRUB_USB_ERR_NONE;
++}
++
++static grub_usb_err_t
++grub_xhci_request_legacy_handoff(volatile struct grub_xhci_xcap *xcap)
++{
++ grub_uint32_t end;
++
++ end = grub_get_time_ms () + 10;
++ for (;;)
++ {
++ grub_uint32_t cap = grub_xhci_read32(&xcap->cap);
++ if (cap & (1 << 16))
++ grub_xhci_write32(&xcap->cap, cap | (1 << 24));
++ else
++ break;
++
++ if (grub_get_time_ms () > end)
++ {
++ grub_dprintf ("xhci","ERROR: %s TIMEOUT\n", __func__);
++ return GRUB_USB_ERR_TIMEOUT;
++ }
++ grub_millisleep(1);
++ }
++ return GRUB_USB_ERR_NONE;
++}
++
++static void
++grub_xhci_fill_default_speed_mapping(struct grub_xhci_psids *ids)
++{
++ /* Chapter 7.2.2.1.1 "Default USB Speed ID Mapping" */
++ ids->psids[0].id = 1;
++ ids->psids[0].psie = 2;
++ ids->psids[0].psim = 12;
++ ids->psids[1].id = 2;
++ ids->psids[1].psie = 1;
++ ids->psids[1].psim = 1500;
++ ids->psids[2].id = 3;
++ ids->psids[2].psie = 2;
++ ids->psids[2].psim = 480;
++ ids->psids[3].id = 4;
++ ids->psids[3].psie = 3;
++ ids->psids[3].psim = 5;
++ ids->psids[4].id = 5;
++ ids->psids[4].psie = 3;
++ ids->psids[4].psim = 10;
++ ids->psids[5].id = 6;
++ ids->psids[5].psie = 3;
++ ids->psids[5].psim = 10;
++ ids->psids[6].id = 7;
++ ids->psids[6].psie = 3;
++ ids->psids[6].psim = 20;
++}
++
++static void
++grub_xhci_calc_speed_mapping(struct grub_xhci_psids *ids)
++{
++ const grub_uint64_t mult[4] = {1ULL, 1000ULL, 1000000ULL, 1000000000ULL};
++
++ for (grub_uint8_t i = 0; i < 16; i++)
++ {
++ if (ids->psids[i].id == 0)
++ continue;
++ ids->psids[i].bitrate = mult[ids->psids[i].psie & 3] * (grub_uint64_t)ids->psids[i].psim;
++ if (ids->psids[i].bitrate < 12000000ULL)
++ ids->psids[i].grub_usb_speed = GRUB_USB_SPEED_LOW;
++ else if (ids->psids[i].bitrate < 480000000ULL)
++ ids->psids[i].grub_usb_speed = GRUB_USB_SPEED_FULL;
++ else if (ids->psids[i].bitrate > 1200000000ULL)
++ ids->psids[i].grub_usb_speed = GRUB_USB_SPEED_SUPER;
++ else
++ ids->psids[i].grub_usb_speed = GRUB_USB_SPEED_HIGH;
++ }
++}
++
++
++/* PCI iteration function... */
++void
++grub_xhci_init_device (volatile void *regs)
++{
++ struct grub_xhci *x;
++ grub_uint32_t hcs1, hcc, reg;
++
++ /* Allocate memory for the controller and fill basic values. */
++ x = grub_zalloc (sizeof (*x));
++ if (!x)
++ {
++ grub_dprintf("xhci", "Failed to allocate memory\n");
++ return;
++ }
++ x->caps = (volatile struct grub_xhci_caps *) regs;
++ x->op = (volatile struct grub_xhci_op *) (((grub_uint8_t *)regs) +
++ grub_xhci_read8(&x->caps->caplength));
++ x->pr = (volatile struct grub_xhci_pr *) (((grub_uint8_t *)x->op) +
++ GRUB_XHCI_PR_OFFSET);
++ x->db = (volatile struct grub_xhci_db *) (((grub_uint8_t *)regs) +
++ grub_xhci_read32(&x->caps->dboff));
++ x->ir = (volatile struct grub_xhci_ir *) (((grub_uint8_t *)regs) +
++ grub_xhci_read32(&x->caps->rtsoff) + GRUB_XHCI_IR_OFFSET);
++
++ grub_dprintf ("xhci", "XHCI init: CAPLENGTH: 0x%02x\n",
++ grub_xhci_read8 (&x->caps->caplength));
++ grub_dprintf ("xhci", "XHCI init: HCIVERSION: 0x%04x\n",
++ grub_xhci_read16 (&x->caps->hciversion));
++ grub_dprintf ("xhci", "XHCI init: HCSPARAMS1: 0x%08x\n",
++ grub_xhci_read32 (&x->caps->hcsparams1));
++ grub_dprintf ("xhci", "XHCI init: HCSPARAMS2: 0x%08x\n",
++ grub_xhci_read32 (&x->caps->hcsparams2));
++ grub_dprintf ("xhci", "XHCI init: HCSPARAMS3: 0x%08x\n",
++ grub_xhci_read32 (&x->caps->hcsparams3));
++ grub_dprintf ("xhci", "XHCI init: HCCPARAMS: 0x%08x\n",
++ grub_xhci_read32 (&x->caps->hcsparams3));
++ grub_dprintf ("xhci", "XHCI init: DBOFF: 0x%08x\n",
++ grub_xhci_read32 (&x->caps->dboff));
++ grub_dprintf ("xhci", "XHCI init: RTOFF: 0x%08x\n",
++ grub_xhci_read32 (&x->caps->rtsoff));
++
++ hcs1 = grub_xhci_read32(&x->caps->hcsparams1);
++ hcc = grub_xhci_read32(&x->caps->hccparams);
++ x->ports = (grub_uint32_t) ((hcs1 >> 24) & 0xff);
++ x->slots = (grub_uint32_t) (hcs1 & 0xff);
++ x->xcap = (grub_uint32_t) ((hcc >> 16) & 0xffff) * sizeof(grub_uint32_t);
++ x->flag64 = (grub_uint8_t) ((hcc & 0x04) ? 1 : 0);
++ grub_dprintf("xhci", "XHCI init: %d ports, %d slots, %d byte contexts\n"
++ , x->ports, x->slots, x->flag64 ? 64 : 32);
++
++ x->psids = grub_zalloc (sizeof (struct grub_xhci_psids) * x->ports);
++ if (x->xcap)
++ {
++ grub_uint32_t off;
++ volatile grub_uint8_t *addr = (grub_uint8_t *) x->caps + x->xcap;
++ do
++ {
++ volatile struct grub_xhci_xcap *xcap = (void *)addr;
++ grub_uint32_t ports, name, cap = grub_xhci_read32(&xcap->cap);
++ switch (cap & 0xff) {
++ case XHCI_CAP_LEGACY_SUPPORT:
++ {
++ if (grub_xhci_request_legacy_handoff(xcap) != GRUB_USB_ERR_NONE)
++ {
++ grub_dprintf("xhci", "XHCI init: Failed to get xHCI ownership\n");
++ goto fail;
++ }
++ break;
++ }
++ case XHCI_CAP_SUPPORTED_PROTOCOL:
++ {
++ name = grub_xhci_read32(&xcap->data[0]);
++ ports = grub_xhci_read32(&xcap->data[1]);
++ const grub_uint8_t major = (cap >> 24) & 0xff;
++ const grub_uint8_t minor = (cap >> 16) & 0xff;
++ const grub_uint8_t psic = (ports >> 28) & 0xf;
++ const grub_uint8_t count = (ports >> 8) & 0xff;
++ const grub_uint8_t start = (ports >> 0) & 0xff;
++ grub_dprintf("xhci", "XHCI init: protocol %c%c%c%c %x.%02x"
++ ", %d ports (offset %d), def %x, psic %d\n"
++ , (name >> 0) & 0xff
++ , (name >> 8) & 0xff
++ , (name >> 16) & 0xff
++ , (name >> 24) & 0xff
++ , major, minor
++ , count, start
++ , ports >> 16
++ , psic);
++ if (name == 0x20425355 /* "USB " */)
++ {
++ if (major == 2)
++ {
++ x->usb2.start = start;
++ x->usb2.count = count;
++ }
++ else if (major == 3)
++ {
++ x->usb3.start = start;
++ x->usb3.count = count;
++ }
++
++ for (grub_uint32_t p = start - 1; p < start + count - 1UL; p++)
++ {
++ x->psids[p].major = major;
++ x->psids[p].minor = minor;
++ grub_xhci_fill_default_speed_mapping(&x->psids[p]);
++ for (grub_uint8_t i = 0; i < psic; i++)
++ {
++ grub_uint32_t psid = grub_xhci_read32(&xcap->data[3 + i]);
++ x->psids[p].psids[i].id = (psid >> 0) & 0xf;
++ x->psids[p].psids[i].psie = (psid >> 4) & 0x3;
++ x->psids[p].psids[i].psim = (psid >> 16) & 0xfffff;
++ }
++ grub_xhci_calc_speed_mapping(&x->psids[p]);
++ }
++ }
++
++ break;
++ }
++ default:
++ {
++ grub_dprintf("xhci", "XHCI extcap 0x%x @ %p\n", cap & 0xff, addr);
++ break;
++ }
++ }
++ off = (cap >> 8) & 0xff;
++ addr += off << 2;
++ }
++ while (off > 0);
++ }
++
++ x->pagesize = xhci_get_pagesize(x);
++ grub_dprintf("xhci", "XHCI init: Minimum supported page size 0x%x\n",
++ x->pagesize);
++
++ /* Chapter 6.1 Device Context Base Address Array */
++ x->devs_dma = xhci_memalign_dma32(ALIGN_DCBAA,
++ sizeof(*x->devs) * (x->slots + 1),
++ x->pagesize);
++ if (!x->devs_dma)
++ goto fail;
++ x->devs = grub_dma_get_virt(x->devs_dma);
++ grub_memset((void *)x->devs, 0, sizeof(*x->devs) * (x->slots + 1));
++ grub_arch_sync_dma_caches(x->devs, sizeof(*x->devs) * (x->slots + 1));
++ grub_dprintf ("xhci", "XHCI init: device memory %p (%x)\n",
++ grub_dma_get_virt(x->devs_dma),
++ grub_dma_get_phys(x->devs_dma));
++
++ /* Chapter 6.5 Event Ring Segment Table */
++ x->eseg_dma = xhci_memalign_dma32(ALIGN_EVT_RING_TABLE, sizeof(*x->eseg), 0);
++ if (!x->eseg_dma)
++ goto fail;
++ x->eseg = grub_dma_get_virt(x->eseg_dma);
++ grub_memset((void *)x->eseg, 0, sizeof(*x->eseg));
++ grub_arch_sync_dma_caches(x->eseg, sizeof(*x->eseg));
++ grub_dprintf ("xhci", "XHCI init: event ring table memory %p (%x)\n",
++ grub_dma_get_virt(x->eseg_dma),
++ grub_dma_get_phys(x->eseg_dma));
++
++ x->cmds_dma = xhci_memalign_dma32(ALIGN_CMD_RING_SEG, sizeof(*x->cmds),
++ BOUNDARY_RING);
++ if (!x->cmds_dma)
++ goto fail;
++ x->cmds = grub_dma_get_virt(x->cmds_dma);
++ grub_memset((void *)x->cmds, 0, sizeof(*x->cmds));
++ grub_arch_sync_dma_caches(x->cmds, sizeof(*x->cmds));
++ grub_dprintf ("xhci", "XHCI init: command ring memory %p (%x)\n",
++ grub_dma_get_virt(x->cmds_dma),
++ grub_dma_get_phys(x->cmds_dma));
++
++ x->evts_dma = xhci_memalign_dma32(ALIGN_EVT_RING_SEG, sizeof(*x->evts),
++ BOUNDARY_RING);
++ if (!x->evts_dma)
++ goto fail;
++ x->evts = grub_dma_get_virt(x->evts_dma);
++ grub_memset((void *)x->evts, 0, sizeof(*x->evts));
++ grub_arch_sync_dma_caches(x->evts, sizeof(*x->evts));
++ grub_dprintf ("xhci", "XHCI init: event ring memory %p (%x)\n",
++ grub_dma_get_virt(x->evts_dma),
++ grub_dma_get_phys(x->evts_dma));
++
++ /* Chapter 4.20 Scratchpad Buffers */
++ reg = grub_xhci_read32(&x->caps->hcsparams2);
++ x->spb = (reg >> 21 & 0x1f) << 5 | reg >> 27;
++ if (x->spb)
++ {
++ volatile grub_uint64_t *spba;
++ grub_dprintf("xhci", "XHCI init: set up %d scratch pad buffers\n",
++ x->spb);
++ x->spba_dma = xhci_memalign_dma32(ALIGN_SPBA, sizeof(*spba) * x->spb,
++ x->pagesize);
++ if (!x->spba_dma)
++ goto fail;
++
++ x->spad_dma = xhci_memalign_dma32(x->pagesize, x->pagesize * x->spb,
++ x->pagesize);
++ if (!x->spad_dma)
++ {
++ grub_dma_free(x->spba_dma);
++ goto fail;
++ }
++
++ spba = grub_dma_get_virt(x->spba_dma);
++ for (grub_uint32_t i = 0; i < x->spb; i++)
++ spba[i] = (grub_addr_t)grub_dma_get_phys(x->spad_dma) + (i * x->pagesize);
++ grub_arch_sync_dma_caches(x->spba_dma, sizeof(*spba) * x->spb);
++
++ x->devs[0].ptr_low = grub_dma_get_phys(x->spba_dma);
++ x->devs[0].ptr_high = 0;
++ grub_arch_sync_dma_caches(x->devs_dma, sizeof(x->devs[0]));
++ grub_dprintf ("xhci", "XHCI init: Allocated %d scratch buffers of size 0x%x\n",
++ x->spb, x->pagesize);
++ }
++
++ grub_xhci_reset(x);
++
++ /* Set the running bit */
++ reg = grub_xhci_read32 (&x->op->usbcmd);
++ reg |= GRUB_XHCI_CMD_RS;
++ grub_xhci_write32 (&x->op->usbcmd, reg);
++
++
++ /* Link to xhci now that initialisation is successful. */
++ x->next = xhci;
++ xhci = x;
++
++ return;
++
++fail:
++ grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: FAILED!\n");
++ if (x)
++ {
++ if (x->devs_dma)
++ grub_dma_free (x->devs_dma);
++ if (x->eseg_dma)
++ grub_dma_free (x->eseg_dma);
++ if (x->cmds_dma)
++ grub_dma_free (x->cmds_dma);
++ if (x->evts_dma)
++ grub_dma_free (x->evts_dma);
++ if (x->spad_dma)
++ grub_dma_free (x->spad_dma);
++ if (x->spba_dma)
++ grub_dma_free (x->spba_dma);
++ }
++ grub_free (x);
++
++ return;
++}
++
++static int
++grub_xhci_iterate (grub_usb_controller_iterate_hook_t hook, void *hook_data)
++{
++ struct grub_xhci *x;
++ struct grub_usb_controller dev;
++
++ for (x = xhci; x; x = x->next)
++ {
++ dev.data = x;
++ if (hook (&dev, hook_data))
++ return 1;
++ }
++
++ return 0;
++}
++
++/****************************************************************
++ * xHCI maintainance functions
++ ****************************************************************/
++
++static grub_usb_err_t
++grub_xhci_update_hub_portcount (struct grub_xhci *x,
++ grub_usb_transfer_t transfer,
++ grub_uint32_t slotid)
++{
++ struct grub_pci_dma_chunk *in_dma;
++ volatile struct grub_xhci_slotctx *hdslot;
++ grub_uint32_t epid = 0;
++
++ if (!transfer || !transfer->dev || !transfer->dev->nports)
++ return GRUB_USB_ERR_NONE;
++
++ hdslot = grub_dma_phys2virt(x->devs[slotid].ptr_low, x->devs_dma);
++ if ((hdslot->ctx[3] >> 27) == 3)
++ /* Already configured */
++ return 0;
++
++ grub_dprintf("xhci", "%s: updating hub config to %d ports\n", __func__,
++ transfer->dev->nports);
++
++ xhci_check_status(x);
++
++ /* Allocate input context and initialize endpoint info. */
++ in_dma = grub_xhci_alloc_inctx(x, epid, transfer->dev);
++ if (!in_dma)
++ return GRUB_USB_ERR_INTERNAL;
++ volatile struct grub_xhci_inctx *in = grub_dma_get_virt(in_dma);
++
++ in->add = (1 << epid);
++
++ struct grub_xhci_epctx *ep = (void*)&in[(epid+1) << x->flag64];
++ ep->ctx[0] |= 1 << 26;
++ ep->ctx[1] |= transfer->dev->nports << 24;
++
++ int cc = xhci_cmd_configure_endpoint(x, slotid, in_dma);
++ grub_dma_free(in_dma);
++
++ if (cc != CC_SUCCESS)
++ {
++ grub_dprintf("xhci", "%s: reconf ctl endpoint: failed (cc %d)\n",
++ __func__, cc);
++ return GRUB_USB_ERR_BADDEVICE;
++ }
++
++ return GRUB_USB_ERR_NONE;
++}
++
++static grub_usb_err_t
++grub_xhci_update_max_paket_size (struct grub_xhci *x,
++ grub_usb_transfer_t transfer,
++ grub_uint32_t slotid,
++ grub_uint32_t max_packet)
++{
++ struct grub_pci_dma_chunk *in_dma;
++ grub_uint32_t epid = 1;
++
++ if (!transfer || !transfer->dev || !max_packet)
++ return GRUB_USB_ERR_NONE;
++
++ grub_dprintf("xhci", "%s: updating max packet size to 0x%x\n", __func__,
++ max_packet);
++
++ xhci_check_status(x);
++
++ /* Allocate input context and initialize endpoint info. */
++ in_dma = grub_xhci_alloc_inctx(x, epid, transfer->dev);
++ if (!in_dma)
++ return GRUB_USB_ERR_INTERNAL;
++ volatile struct grub_xhci_inctx *in = grub_dma_get_virt(in_dma);
++ in->add = (1 << epid);
++
++ struct grub_xhci_epctx *ep = (void*)&in[(epid+1) << x->flag64];
++ ep->ctx[1] |= max_packet << 16;
++
++ int cc = xhci_cmd_evaluate_context(x, slotid, in_dma);
++ grub_dma_free(in_dma);
++
++ if (cc != CC_SUCCESS)
++ {
++ grub_dprintf("xhci", "%s: reconf ctl endpoint: failed (cc %d)\n",
++ __func__, cc);
++ return GRUB_USB_ERR_BADDEVICE;
++ }
++
++ return GRUB_USB_ERR_NONE;
++}
++
++/****************************************************************
++ * xHCI endpoint enablement functions
++ ****************************************************************/
++
++static grub_usb_err_t
++grub_xhci_prepare_endpoint (struct grub_xhci *x,
++ struct grub_usb_device *dev,
++ grub_uint8_t endpoint,
++ grub_transfer_type_t dir,
++ grub_transaction_type_t type,
++ grub_uint32_t maxpaket,
++ struct grub_xhci_priv *priv)
++{
++ grub_uint32_t epid;
++ struct grub_pci_dma_chunk *reqs_dma;
++ struct grub_pci_dma_chunk *in_dma;
++ volatile struct grub_xhci_ring *reqs;
++ volatile struct grub_xhci_slotctx *slotctx;
++
++ if (!x || !priv)
++ return GRUB_USB_ERR_INTERNAL;
++
++ xhci_check_status(x);
++
++ if (endpoint == 0)
++ {
++ epid = 1;
++ }
++ else
++ {
++ epid = (endpoint & 0x0f) * 2;
++ epid += (dir == GRUB_USB_TRANSFER_TYPE_IN) ? 1 : 0;
++ }
++ grub_dprintf("xhci", "%s: epid %d\n", __func__, epid);
++
++ /* Test if already prepared */
++ if (priv->slotid > 0 && priv->enpoint_trbs[epid] != NULL)
++ return GRUB_USB_ERR_NONE;
++
++ /* Allocate DMA buffer as endpoint cmd TRB */
++ reqs_dma = xhci_memalign_dma32(ALIGN_TRB, sizeof(*reqs),
++ BOUNDARY_RING);
++ if (!reqs_dma)
++ return GRUB_USB_ERR_INTERNAL;
++ reqs = grub_dma_get_virt(reqs_dma);
++ grub_memset((void *)reqs, 0, sizeof(*reqs));
++ reqs->cs = 1;
++
++ grub_arch_sync_dma_caches(reqs, sizeof(*reqs));
++
++ /* Allocate input context and initialize endpoint info. */
++ in_dma = grub_xhci_alloc_inctx(x, epid, dev);
++ if (!in_dma)
++ {
++ grub_dma_free(reqs_dma);
++ return GRUB_USB_ERR_INTERNAL;
++ }
++ volatile struct grub_xhci_inctx *in = grub_dma_get_virt(in_dma);
++ in->add = 0x01 | (1 << epid);
++
++ struct grub_xhci_epctx *ep = (void*)&in[(epid+1) << x->flag64];
++ switch (type)
++ {
++ case GRUB_USB_TRANSACTION_TYPE_CONTROL:
++ ep->ctx[1] |= 0 << 3;
++ break;
++ case GRUB_USB_TRANSACTION_TYPE_BULK:
++ ep->ctx[1] |= 2 << 3;
++ break;
++ }
++ if (dir == GRUB_USB_TRANSFER_TYPE_IN
++ || type== GRUB_USB_TRANSACTION_TYPE_CONTROL)
++ ep->ctx[1] |= 1 << 5;
++ ep->ctx[1] |= maxpaket << 16;
++ ep->deq_low = grub_dma_get_phys(reqs_dma);
++ ep->deq_low |= 1; /* dcs */
++ ep->length = maxpaket;
++
++ grub_dprintf("xhci", "%s: ring %p, epid %d, max %d\n", __func__,
++ reqs, epid, maxpaket);
++ if (epid == 1 || priv->slotid == 0) {
++ /* Enable slot. */
++ int slotid = xhci_cmd_enable_slot(x);
++ if (slotid < 0)
++ {
++ grub_dprintf("xhci", "%s: enable slot: failed\n", __func__);
++ grub_dma_free(reqs_dma);
++ grub_dma_free(in_dma);
++ return GRUB_USB_ERR_BADDEVICE;
++ }
++ grub_dprintf("xhci", "%s: get slot %d assigned\n", __func__, slotid);
++
++ grub_uint32_t size = (sizeof(struct grub_xhci_slotctx) * GRUB_XHCI_MAX_ENDPOINTS) << x->flag64;
++
++ /* Allocate memory for the device specific slot context */
++ priv->slotctx_dma = xhci_memalign_dma32(ALIGN_SLOTCTX, size,
++ x->pagesize);
++ if (!priv->slotctx_dma)
++ {
++ grub_dprintf("xhci", "%s: grub_memalign_dma32 failed\n", __func__);
++ grub_dma_free(reqs_dma);
++ grub_dma_free(in_dma);
++ return GRUB_USB_ERR_INTERNAL;
++ }
++ slotctx = grub_dma_get_virt(priv->slotctx_dma);
++
++ grub_dprintf("xhci", "%s: enable slot: got slotid %d\n", __func__, slotid);
++ grub_memset((void *)slotctx, 0, size);
++ grub_arch_sync_dma_caches(slotctx, size);
++
++ x->devs[slotid].ptr_low = grub_dma_get_phys(priv->slotctx_dma);
++ x->devs[slotid].ptr_high = 0;
++ grub_arch_sync_dma_caches(&x->devs[slotid], sizeof(x->devs[0]));
++
++ /* Send set_address command. */
++ int cc = xhci_cmd_address_device(x, slotid, in_dma);
++ if (cc != CC_SUCCESS)
++ {
++ grub_dprintf("xhci","%s: address device: failed (cc %d)\n", __func__, cc);
++ cc = xhci_cmd_disable_slot(x, slotid);
++ if (cc != CC_SUCCESS) {
++ grub_dprintf("xhci", "%s: disable failed (cc %d)\n", __func__, cc);
++ } else {
++ x->devs[slotid].ptr_low = 0;
++ x->devs[slotid].ptr_high = 0;
++ grub_arch_sync_dma_caches(&x->devs[slotid], sizeof(x->devs[0]));
++ }
++ grub_dma_free(priv->slotctx_dma);
++ grub_dma_free(reqs_dma);
++ grub_dma_free(in_dma);
++ return GRUB_USB_ERR_BADDEVICE;
++ }
++ priv->enpoint_trbs[epid] = reqs;
++ priv->enpoint_trbs_dma[epid] = reqs_dma;
++ priv->slotid = slotid;
++ priv->max_packet = 0;
++ }
++ if (epid != 1)
++ {
++ /* Send configure command. */
++ int cc = xhci_cmd_configure_endpoint(x, priv->slotid, in_dma);
++ if (cc != CC_SUCCESS)
++ {
++ grub_dprintf("xhci", "%s: configure endpoint: failed (cc %d)\n",
++ __func__, cc);
++ grub_dma_free(reqs_dma);
++ grub_dma_free(in_dma);
++ return GRUB_USB_ERR_BADDEVICE;
++ }
++ priv->enpoint_trbs[epid] = reqs;
++ priv->enpoint_trbs_dma[epid] = reqs_dma;
++ }
++
++ grub_dprintf("xhci", "%s: done\n", __func__);
++ grub_dma_free(in_dma);
++
++ return GRUB_USB_ERR_NONE;
++}
++
++
++/****************************************************************
++ * xHCI transfer helper functions
++ ****************************************************************/
++
++static grub_usb_err_t
++grub_xhci_usb_to_grub_err (unsigned char status)
++{
++ if (status != CC_SUCCESS)
++ grub_dprintf("xhci", "%s: xfer failed (cc %d)\n", __func__, status);
++ else
++ grub_dprintf("xhci", "%s: xfer done (cc %d)\n", __func__, status);
++
++ if (status == CC_BABBLE_DETECTED)
++ return GRUB_USB_ERR_BABBLE;
++ else if (status == CC_DATA_BUFFER_ERROR)
++ return GRUB_USB_ERR_DATA;
++ else if (status == CC_STALL_ERROR)
++ return GRUB_USB_ERR_STALL;
++ else if (status != CC_SUCCESS)
++ return GRUB_USB_ERR_NAK;
++
++ return GRUB_USB_ERR_NONE;
++}
++
++static int
++grub_xhci_transfer_is_zlp(grub_usb_transfer_t transfer, int idx)
++{
++ if (idx >= transfer->transcnt)
++ return 0;
++
++ grub_usb_transaction_t tr = &transfer->transactions[idx];
++
++ return (tr->size == 0) &&
++ ((tr->pid == GRUB_USB_TRANSFER_TYPE_OUT) ||
++ (tr->pid == GRUB_USB_TRANSFER_TYPE_IN));
++}
++
++static int
++grub_xhci_transfer_is_last(grub_usb_transfer_t transfer, int idx)
++{
++ return (idx + 1) == transfer->transcnt;
++}
++
++static int
++grub_xhci_transfer_is_data(grub_usb_transfer_t transfer, int idx)
++{
++ grub_usb_transaction_t tr;
++
++ if (idx >= transfer->transcnt)
++ return 0;
++
++ tr = &transfer->transactions[idx];
++ if (tr->size == 0 ||
++ (tr->pid == GRUB_USB_TRANSFER_TYPE_SETUP))
++ return 0;
++
++ /* If there's are no DATA pakets before it's a DATA paket */
++ for (int i = idx - 1; i >= 0; i--)
++ {
++ tr = &transfer->transactions[i];
++ if (tr->size > 0 &&
++ ((tr->pid == GRUB_USB_TRANSFER_TYPE_OUT) ||
++ (tr->pid == GRUB_USB_TRANSFER_TYPE_IN)))
++ return 0;
++ }
++ return 1;
++}
++
++static int
++grub_xhci_transfer_is_in(grub_usb_transfer_t transfer, int idx)
++{
++ grub_usb_transaction_t tr;
++
++ if (idx >= transfer->transcnt)
++ return 0;
++
++ tr = &transfer->transactions[idx];
++
++ return tr->pid == GRUB_USB_TRANSFER_TYPE_IN;
++}
++
++static int
++grub_xhci_transfer_is_normal(grub_usb_transfer_t transfer, int idx)
++{
++ grub_usb_transaction_t tr;
++
++ if (idx >= transfer->transcnt)
++ return 0;
++
++ tr = &transfer->transactions[idx];
++ if (tr->size == 0 ||
++ (tr->pid == GRUB_USB_TRANSFER_TYPE_SETUP))
++ return 0;
++
++ /* If there's at least one DATA paket before it's a normal */
++ for (int i = idx - 1; i >= 0; i--)
++ {
++ tr = &transfer->transactions[i];
++ if (tr->size > 0 &&
++ ((tr->pid == GRUB_USB_TRANSFER_TYPE_OUT) ||
++ (tr->pid == GRUB_USB_TRANSFER_TYPE_IN)))
++ return 1;
++
++ }
++ return 0;
++}
++
++static int
++grub_xhci_transfer_next_is_normal(grub_usb_transfer_t transfer, int idx)
++{
++ return grub_xhci_transfer_is_normal(transfer, idx + 1);
++}
++
++static int
++grub_xhci_transfer_next_is_in(grub_usb_transfer_t transfer, int idx)
++{
++ return grub_xhci_transfer_is_in(transfer, idx + 1);
++}
++
++static grub_uint8_t grub_xhci_epid_from_transfer(grub_usb_transfer_t transfer)
++{
++ grub_uint8_t epid;
++
++ if (transfer->endpoint == 0) {
++ epid = 1;
++ } else {
++ epid = (transfer->endpoint & 0x0f) * 2;
++ epid += (transfer->dir == GRUB_USB_TRANSFER_TYPE_IN) ? 1 : 0;
++ }
++ return epid;
++}
++
++/****************************************************************
++ * xHCI transfer functions
++ ****************************************************************/
++
++static grub_usb_err_t
++grub_xhci_setup_transfer (grub_usb_controller_t dev,
++ grub_usb_transfer_t transfer)
++{
++ struct grub_xhci_transfer_controller_data *cdata;
++ struct grub_xhci *x = (struct grub_xhci *) dev->data;
++ grub_uint8_t epid;
++ grub_usb_err_t err;
++ volatile struct grub_xhci_ring *reqs;
++ int rc;
++ struct grub_xhci_priv *priv;
++
++ xhci_check_status(x);
++
++ if (!dev || !transfer || !transfer->dev || !transfer->dev->xhci_priv)
++ return GRUB_USB_ERR_INTERNAL;
++
++ priv = transfer->dev->xhci_priv;
++ err = grub_xhci_prepare_endpoint(x, transfer->dev,
++ transfer->endpoint,
++ transfer->dir,
++ transfer->type,
++ transfer->max,
++ priv);
++
++ if (err != GRUB_USB_ERR_NONE)
++ return err;
++
++ epid = grub_xhci_epid_from_transfer(transfer);
++
++ /* Update the max packet size once descdev.maxsize0 is valid */
++ if (epid == 1 &&
++ (priv->max_packet == 0) &&
++ (transfer->dev->descdev.maxsize0 > 0))
++ {
++ if (transfer->dev->speed == GRUB_USB_SPEED_SUPER)
++ priv->max_packet = 1UL << transfer->dev->descdev.maxsize0;
++ else
++ priv->max_packet = transfer->dev->descdev.maxsize0;
++ err = grub_xhci_update_max_paket_size(x, transfer, priv->slotid, priv->max_packet);
++ if (err != GRUB_USB_ERR_NONE)
++ {
++ grub_dprintf("xhci", "%s: Updating max paket size failed\n", __func__);
++ return err;
++ }
++ }
++ if (epid == 1 &&
++ transfer->dev->descdev.class == 9 &&
++ transfer->dev->nports > 0)
++ {
++ err = grub_xhci_update_hub_portcount(x, transfer, priv->slotid);
++ if (err != GRUB_USB_ERR_NONE)
++ {
++ grub_dprintf("xhci", "%s: Updating max paket size failed\n", __func__);
++ return err;
++ }
++ }
++
++ /* Allocate private data for the transfer */
++ cdata = grub_zalloc(sizeof(*cdata));
++ if (!cdata)
++ return GRUB_USB_ERR_INTERNAL;
++
++ reqs = priv->enpoint_trbs[epid];
++
++ transfer->controller_data = cdata;
++
++ /* Now queue the transfer onto the TRB */
++ if (transfer->type == GRUB_USB_TRANSACTION_TYPE_CONTROL)
++ {
++ volatile struct grub_usb_packet_setup *setupdata;
++ setupdata = (void *)transfer->transactions[0].data;
++ grub_dprintf("xhci", "%s: CONTROLL TRANS req %d\n", __func__, setupdata->request);
++ grub_dprintf("xhci", "%s: CONTROLL TRANS length %d\n", __func__, setupdata->length);
++
++ if (setupdata && setupdata->request == GRUB_USB_REQ_SET_ADDRESS)
++ return GRUB_USB_ERR_NONE;
++
++ if (transfer->transcnt < 2)
++ return GRUB_USB_ERR_INTERNAL;
++
++ for (int i = 0; i < transfer->transcnt; i++)
++ {
++ grub_uint32_t flags = 0;
++ grub_uint64_t inline_data;
++ grub_usb_transaction_t tr = &transfer->transactions[i];
++
++ switch (tr->pid)
++ {
++ case GRUB_USB_TRANSFER_TYPE_SETUP:
++ {
++ grub_dprintf("xhci", "%s: SETUP PKG\n", __func__);
++ grub_dprintf("xhci", "%s: transfer->size %d\n", __func__, transfer->size);
++ grub_dprintf("xhci", "%s: tr->size %d SETUP PKG\n", __func__, tr->size);
++
++ flags |= (TR_SETUP << 10);
++ flags |= TRB_TR_IDT;
++
++ if (transfer->size > 0)
++ {
++ if (grub_xhci_transfer_next_is_in(transfer, i))
++ flags |= (3 << 16); /* TRT IN */
++ else
++ flags |= (2 << 16); /* TRT OUT */
++ }
++ break;
++ }
++ case GRUB_USB_TRANSFER_TYPE_OUT:
++ {
++ grub_dprintf("xhci", "%s: OUT PKG\n", __func__);
++ cdata->transfer_size += tr->size;
++ break;
++ }
++ case GRUB_USB_TRANSFER_TYPE_IN:
++ {
++ grub_dprintf("xhci", "%s: IN PKG\n", __func__);
++ cdata->transfer_size += tr->size;
++ flags |= TRB_TR_DIR;
++ break;
++ }
++ }
++
++ if (grub_xhci_transfer_is_normal(transfer, i))
++ flags |= (TR_NORMAL << 10);
++ else if (grub_xhci_transfer_is_data(transfer, i))
++ flags |= (TR_DATA << 10);
++ else if (grub_xhci_transfer_is_zlp(transfer, i))
++ flags |= (TR_STATUS << 10);
++
++ if (grub_xhci_transfer_next_is_normal(transfer, i))
++ flags |= TRB_TR_CH;
++
++ if (grub_xhci_transfer_is_last(transfer, i))
++ flags |= TRB_TR_IOC;
++
++ /* Assume the ring has enough free space for all TRBs */
++ if (flags & TRB_TR_IDT && tr->size <= (int)sizeof(inline_data))
++ {
++ grub_memcpy(&inline_data, (void *)tr->data, tr->size);
++ xhci_trb_queue(reqs, inline_data, tr->size, flags);
++ }
++ else
++ {
++ xhci_trb_queue(reqs, tr->data, tr->size, flags);
++ }
++ }
++ }
++ else if (transfer->type == GRUB_USB_TRANSACTION_TYPE_BULK)
++ {
++ for (int i = 0; i < transfer->transcnt; i++)
++ {
++ grub_uint32_t flags = (TR_NORMAL << 10);
++ grub_usb_transaction_t tr = &transfer->transactions[i];
++ switch (tr->pid)
++ {
++ case GRUB_USB_TRANSFER_TYPE_OUT:
++ {
++ grub_dprintf("xhci", "%s: OUT PKG\n", __func__);
++ cdata->transfer_size += tr->size;
++ break;
++ }
++ case GRUB_USB_TRANSFER_TYPE_IN:
++ {
++ grub_dprintf("xhci", "%s: IN PKG\n", __func__);
++ cdata->transfer_size += tr->size;
++ flags |= TRB_TR_DIR;
++ break;
++ }
++ case GRUB_USB_TRANSFER_TYPE_SETUP:
++ break;
++ }
++ if (grub_xhci_transfer_is_last(transfer, i))
++ flags |= TRB_TR_IOC;
++
++ /* The ring might be to small, submit while adding new entries */
++ rc = xhci_trb_queue_and_flush(x, priv->slotid, epid,
++ reqs, tr->data, tr->size, flags);
++ if (rc < 0)
++ return GRUB_USB_ERR_TIMEOUT;
++ else if (rc > 1)
++ return grub_xhci_usb_to_grub_err(rc);
++
++ }
++ }
++ xhci_doorbell(x, priv->slotid, epid);
++
++ return GRUB_USB_ERR_NONE;
++}
++
++static grub_usb_err_t
++grub_xhci_check_transfer (grub_usb_controller_t dev,
++ grub_usb_transfer_t transfer, grub_size_t * actual)
++{
++ grub_uint32_t status;
++ grub_uint32_t remaining;
++ grub_uint8_t epid;
++ volatile struct grub_xhci_ring *reqs;
++ grub_usb_err_t err;
++ int rc;
++
++ if (!dev->data || !transfer->controller_data || !transfer->dev ||
++ !transfer->dev->xhci_priv)
++ return GRUB_USB_ERR_INTERNAL;
++
++
++ struct grub_xhci_priv *priv = transfer->dev->xhci_priv;
++ struct grub_xhci *x = (struct grub_xhci *) dev->data;
++ struct grub_xhci_transfer_controller_data *cdata =
++ transfer->controller_data;
++
++ xhci_check_status(x);
++ xhci_process_events(x);
++
++ epid = grub_xhci_epid_from_transfer(transfer);
++
++ reqs = priv->enpoint_trbs[epid];
++
++ /* XXX: invalidate caches */
++
++ /* Get current status from event ring buffer */
++ status = (reqs->evt.status>> 24) & 0xff;
++ remaining = reqs->evt.status & 0xffffff;
++
++ if (status != CC_STOPPED_LENGTH_INVALID)
++ *actual = cdata->transfer_size - remaining;
++ else
++ *actual = 0;
++
++ if (xhci_ring_busy(reqs))
++ return GRUB_USB_ERR_WAIT;
++
++ grub_free(cdata);
++
++ grub_dprintf("xhci", "%s: xfer done\n", __func__);
++
++ err = grub_xhci_usb_to_grub_err(status);
++ if (err != GRUB_USB_ERR_NONE)
++ {
++ if (status == CC_STALL_ERROR)
++ {
++ /* Clear the stall by resetting the endpoint */
++ rc = xhci_cmd_reset_endpoint(x, priv->slotid, epid, 1);
++
++ if (rc < 0)
++ return GRUB_USB_ERR_TIMEOUT;
++
++ return GRUB_USB_ERR_STALL;
++ }
++ else if (remaining > 0)
++ {
++ return GRUB_USB_ERR_DATA;
++ }
++ }
++
++ return err;
++}
++
++static grub_usb_err_t
++grub_xhci_cancel_transfer (grub_usb_controller_t dev,
++ grub_usb_transfer_t transfer)
++{
++ grub_uint8_t epid;
++ volatile struct grub_xhci_ring *reqs;
++ struct grub_pci_dma_chunk *enpoint_trbs_dma;
++ grub_addr_t deque_pointer;
++ int rc;
++
++ if (!dev->data || !transfer->controller_data || !transfer->dev ||
++ !transfer->dev->xhci_priv)
++ return GRUB_USB_ERR_INTERNAL;
++
++ struct grub_xhci *x = (struct grub_xhci *) dev->data;
++ struct grub_xhci_transfer_controller_data *cdata =
++ transfer->controller_data;
++ struct grub_xhci_priv *priv = transfer->dev->xhci_priv;
++
++ epid = grub_xhci_epid_from_transfer(transfer);
++
++ enpoint_trbs_dma = priv->enpoint_trbs_dma[epid];
++ reqs = priv->enpoint_trbs[epid];
++
++ /* Abort current command */
++ rc = xhci_cmd_stop_endpoint(x, priv->slotid, epid, 0);
++ if (rc < 0)
++ return GRUB_USB_ERR_TIMEOUT;
++
++ /* Reset state */
++ reqs->nidx = 0;
++ reqs->eidx = 0;
++ reqs->cs = 1;
++
++ grub_arch_sync_dma_caches(reqs, sizeof(*reqs));
++
++ /* Reset the dequeue pointer to the begging of the TRB */
++ deque_pointer = grub_dma_get_phys(enpoint_trbs_dma);
++ rc = xhci_cmd_set_dequeue_pointer(x, priv->slotid, epid, deque_pointer| 1 );
++ if (rc < 0)
++ return GRUB_USB_ERR_TIMEOUT;
++
++ reqs->evt.ptr_low = 0;
++ reqs->evt.ptr_high = 0;
++ reqs->evt.control = 0;
++ reqs->evt.status = 0;
++
++ grub_arch_sync_dma_caches(reqs, sizeof(*reqs));
++
++ /* Restart ring buffer processing */
++ xhci_doorbell(x, priv->slotid, epid);
++
++ grub_free (cdata);
++
++ return GRUB_USB_ERR_NONE;
++}
++
++/****************************************************************
++ * xHCI port status functions
++ ****************************************************************/
++
++static int
++grub_xhci_hubports (grub_usb_controller_t dev)
++{
++ struct grub_xhci *x = (struct grub_xhci *) dev->data;
++ grub_uint32_t portinfo;
++
++ portinfo = x->ports;
++ grub_dprintf ("xhci", "root hub ports=%d\n", portinfo);
++ return portinfo;
++}
++
++static grub_usb_err_t
++grub_xhci_portstatus (grub_usb_controller_t dev,
++ unsigned int port, unsigned int enable)
++{
++ struct grub_xhci *x = (struct grub_xhci *) dev->data;
++ grub_uint32_t portsc, pls;
++ grub_uint32_t end;
++
++ portsc = grub_xhci_port_read(x, port);
++ pls = xhci_get_field(portsc, XHCI_PORTSC_PLS);
++
++ grub_dprintf("xhci", "grub_xhci_portstatus port #%d: 0x%08x,%s%s pls %d enable %d\n",
++ port, portsc,
++ (portsc & GRUB_XHCI_PORTSC_PP) ? " powered," : "",
++ (portsc & GRUB_XHCI_PORTSC_PED) ? " enabled," : "",
++ pls, enable);
++ xhci_check_status(x);
++
++ if ((enable && (portsc & GRUB_XHCI_PORTSC_PED)) ||
++ (!enable && !(portsc & GRUB_XHCI_PORTSC_PED)))
++ return GRUB_USB_ERR_NONE;
++
++ if (!enable)
++ {
++ /* Disable port */
++ grub_xhci_port_write(x, port, ~0, GRUB_XHCI_PORTSC_PED);
++ return GRUB_USB_ERR_NONE;
++ }
++
++ grub_dprintf ("xhci", "portstatus: XHCI STATUS: %08x\n",
++ grub_xhci_read32(&x->op->usbsts));
++ grub_dprintf ("xhci",
++ "portstatus: begin, iobase=%p, port=%d, status=0x%08x\n",
++ x->caps, port, portsc);
++
++ switch (pls)
++ {
++ case PLS_U0:
++ /* A USB3 port - controller automatically performs reset */
++ break;
++ case PLS_POLLING:
++ /* A USB2 port - perform device reset */
++ grub_xhci_port_write(x, port, ~GRUB_XHCI_PORTSC_PED, GRUB_XHCI_PORTSC_PR);
++ break;
++ default:
++ return GRUB_USB_ERR_NONE;
++ }
++
++ /* Wait for device to complete reset and be enabled */
++ end = grub_get_time_ms () + 100;
++ for (;;)
++ {
++ portsc = grub_xhci_port_read(x, port);
++ if (!(portsc & GRUB_XHCI_PORTSC_CCS))
++ {
++ /* Device disconnected during reset */
++ grub_dprintf ("xhci","ERROR: %s device disconnected\n", __func__);
++ return GRUB_USB_ERR_BADDEVICE;
++ }
++ if (portsc & GRUB_XHCI_PORTSC_PED)
++ /* Reset complete */
++ break;
++ if (grub_get_time_ms () > end)
++ {
++ grub_dprintf ("xhci","ERROR: %s TIMEOUT\n", __func__);
++ return GRUB_USB_ERR_TIMEOUT;
++ }
++ }
++ xhci_check_status(x);
++
++ return GRUB_USB_ERR_NONE;
++}
++
++/****************************************************************
++ * xHCI detect device functions
++ ****************************************************************/
++
++static grub_usb_speed_t
++grub_xhci_detect_dev (grub_usb_controller_t dev, int port, int *changed)
++{
++ struct grub_xhci *x = (struct grub_xhci *) dev->data;
++ grub_uint32_t portsc, speed;
++
++ *changed = 0;
++ grub_dprintf("xhci", "%s: dev=%p USB%d_%d port %d\n", __func__, dev,
++ x->psids[port-1].major, x->psids[port-1].minor, port);
++
++ /* On shutdown advertise all ports as disconnected. This will trigger
++ * a gracefull detatch. */
++ if (x->shutdown)
++ {
++ *changed = 1;
++ return GRUB_USB_SPEED_NONE;
++ }
++
++ /* Don't advertise new devices, connecting will fail if halted */
++ if (xhci_is_halted(x))
++ return GRUB_USB_SPEED_NONE;
++
++ portsc = grub_xhci_port_read(x, port);
++ speed = xhci_get_field(portsc, XHCI_PORTSC_SPEED);
++ grub_uint8_t pls = xhci_get_field(portsc, XHCI_PORTSC_PLS);
++
++ grub_dprintf("xhci", "grub_xhci_portstatus port #%d: 0x%08x,%s%s pls %d\n",
++ port, portsc,
++ (portsc & GRUB_XHCI_PORTSC_PP) ? " powered," : "",
++ (portsc & GRUB_XHCI_PORTSC_PED) ? " enabled," : "",
++ pls);
++
++ /* Connect Status Change bit - it detects change of connection */
++ if (portsc & GRUB_XHCI_PORTSC_CSC)
++ {
++ *changed = 1;
++
++ grub_xhci_port_write(x, port, ~GRUB_XHCI_PORTSC_PED, GRUB_XHCI_PORTSC_CSC);
++ }
++
++ if (!(portsc & GRUB_XHCI_PORTSC_CCS))
++ return GRUB_USB_SPEED_NONE;
++
++ for (grub_uint8_t i = 0; i < 16 && x->psids[port-1].psids[i].id > 0; i++)
++ {
++ if (x->psids[port-1].psids[i].id == speed)
++ {
++ grub_dprintf("xhci", "%s: grub_usb_speed = %d\n", __func__,
++ x->psids[port-1].psids[i].grub_usb_speed );
++ return x->psids[port-1].psids[i].grub_usb_speed;
++ }
++ }
++
++ return GRUB_USB_SPEED_NONE;
++}
++
++/****************************************************************
++ * xHCI attach/detach functions
++ ****************************************************************/
++
++static grub_usb_err_t
++grub_xhci_attach_dev (grub_usb_controller_t ctrl, grub_usb_device_t dev)
++{
++ struct grub_xhci *x = (struct grub_xhci *) ctrl->data;
++ grub_usb_err_t err;
++ grub_uint32_t max;
++
++ grub_dprintf("xhci", "%s: dev=%p\n", __func__, dev);
++
++ if (!dev || !x)
++ return GRUB_USB_ERR_INTERNAL;
++
++ dev->xhci_priv = grub_zalloc (sizeof (struct grub_xhci_priv));
++ if (!dev->xhci_priv)
++ return GRUB_USB_ERR_INTERNAL;
++
++
++ switch (dev->speed)
++ {
++ case GRUB_USB_SPEED_LOW:
++ {
++ max = 8;
++ break;
++ }
++ case GRUB_USB_SPEED_FULL:
++ case GRUB_USB_SPEED_HIGH:
++ {
++ max = 64;
++ break;
++ }
++ case GRUB_USB_SPEED_SUPER:
++ {
++ max = 512;
++ break;
++ }
++ default:
++ case GRUB_USB_SPEED_NONE:
++ {
++ max = 0;
++ }
++ }
++
++ /* Assign a slot, assign an address and configure endpoint 0 */
++ err = grub_xhci_prepare_endpoint(x, dev,
++ 0,
++ 0,
++ GRUB_USB_TRANSACTION_TYPE_CONTROL,
++ max,
++ dev->xhci_priv);
++
++ return err;
++}
++
++static grub_usb_err_t
++grub_xhci_detach_dev (grub_usb_controller_t ctrl, grub_usb_device_t dev)
++{
++ struct grub_xhci *x = (struct grub_xhci *) ctrl->data;
++ struct grub_xhci_priv *priv;
++ int cc = CC_SUCCESS;
++
++ grub_dprintf("xhci", "%s: dev=%p\n", __func__, dev);
++
++ if (!dev)
++ return GRUB_USB_ERR_INTERNAL;
++
++ if (dev->xhci_priv)
++ {
++ priv = dev->xhci_priv;
++ /* Stop endpoints and free ring buffer */
++ for (int i = 0; i < GRUB_XHCI_MAX_ENDPOINTS; i++)
++ {
++ if (priv->enpoint_trbs[i] != NULL)
++ {
++ cc = xhci_cmd_stop_endpoint(x, priv->slotid, i, 1);
++ if (cc != CC_SUCCESS)
++ grub_dprintf("xhci", "Failed to disable EP%d on slot %d\n", i,
++ priv->slotid);
++
++ grub_dprintf("xhci", "grub_dma_free[%d]\n", i);
++
++ grub_dma_free(priv->enpoint_trbs_dma[i]);
++ priv->enpoint_trbs[i] = NULL;
++ priv->enpoint_trbs_dma[i] = NULL;
++ }
++ }
++
++ cc = xhci_cmd_disable_slot(x, priv->slotid);
++ if (cc == CC_SUCCESS)
++ {
++ if (priv->slotctx_dma)
++ grub_dma_free(priv->slotctx_dma);
++ x->devs[priv->slotid].ptr_low = 0;
++ x->devs[priv->slotid].ptr_high = 0;
++ grub_arch_sync_dma_caches(&x->devs[priv->slotid], sizeof(x->devs[0]));
++ }
++ else
++ grub_dprintf("xhci", "Failed to disable slot %d\n", priv->slotid);
++
++ grub_free(dev->xhci_priv);
++ }
++
++ dev->xhci_priv = NULL;
++
++ if (cc != CC_SUCCESS)
++ return GRUB_USB_ERR_BADDEVICE;
++ return GRUB_USB_ERR_NONE;
++}
++
++/****************************************************************
++ * xHCI terminate functions
++ ****************************************************************/
++
++static void
++grub_xhci_halt(struct grub_xhci *x)
++{
++ grub_uint32_t reg;
++
++ /* Halt the command ring */
++ reg = grub_xhci_read32(&x->op->crcr_low);
++ grub_xhci_write32(&x->op->crcr_low, reg | 4);
++
++ int rc = xhci_event_wait(x, x->cmds, 100);
++ grub_dprintf("xhci", "%s: xhci_event_wait = %d\n", __func__, rc);
++ if (rc < 0)
++ return;
++
++ /* Stop the controller */
++ reg = grub_xhci_read32(&x->op->usbcmd);
++ if (reg & GRUB_XHCI_CMD_RS)
++ {
++ reg &= ~GRUB_XHCI_CMD_RS;
++ grub_xhci_write32(&x->op->usbcmd, reg);
++ }
++
++ return;
++}
++
++static grub_err_t
++grub_xhci_fini_hw (int noreturn __attribute__ ((unused)))
++{
++ struct grub_xhci *x;
++
++ /* We should disable all XHCI HW to prevent any DMA access etc. */
++ for (x = xhci; x; x = x->next)
++ {
++ x->shutdown = 1;
++
++ /* Gracefully detach active devices */
++ grub_usb_poll_devices(0);
++
++ /* Check if xHCI is halted and halt it if not */
++ grub_xhci_halt (x);
++
++ /* Reset xHCI */
++ if (grub_xhci_reset (x) != GRUB_USB_ERR_NONE)
++ return GRUB_ERR_BAD_DEVICE;
++ }
++
++ return GRUB_ERR_NONE;
++}
++
++static struct grub_usb_controller_dev usb_controller = {
++ .name = "xhci",
++ .iterate = grub_xhci_iterate,
++ .setup_transfer = grub_xhci_setup_transfer,
++ .check_transfer = grub_xhci_check_transfer,
++ .cancel_transfer = grub_xhci_cancel_transfer,
++ .hubports = grub_xhci_hubports,
++ .portstatus = grub_xhci_portstatus,
++ .detect_dev = grub_xhci_detect_dev,
++ .attach_dev = grub_xhci_attach_dev,
++ .detach_dev = grub_xhci_detach_dev,
++ /* estimated max. count of TDs for one bulk transfer */
++ .max_bulk_tds = GRUB_XHCI_RING_ITEMS - 3
++};
++
++GRUB_MOD_INIT (xhci)
++{
++ grub_stop_disk_firmware ();
++
++ grub_boot_time ("Initing XHCI hardware");
++ grub_xhci_pci_scan ();
++ grub_boot_time ("Registering XHCI driver");
++ grub_usb_controller_dev_register (&usb_controller);
++ grub_boot_time ("XHCI driver registered");
++}
++
++GRUB_MOD_FINI (xhci)
++{
++ grub_xhci_fini_hw (0);
++ grub_usb_controller_dev_unregister (&usb_controller);
++}
+diff --git a/include/grub/usb.h b/include/grub/usb.h
+index 609faf7d0..eb71fa1c7 100644
+--- a/include/grub/usb.h
++++ b/include/grub/usb.h
+@@ -338,6 +338,10 @@ grub_usb_cancel_transfer (grub_usb_transfer_t trans);
+ void
+ grub_ehci_init_device (volatile void *regs);
+ void
++grub_xhci_init_device (volatile void *regs);
++void
+ grub_ehci_pci_scan (void);
++void
++grub_xhci_pci_scan (void);
+
+ #endif /* GRUB_USB_H */
+--
+2.47.3
+
diff --git a/config/grub/xhci_nvme/patches/0014-grub-core-bus-usb-usbhub-Add-xHCI-non-root-hub-suppo.patch b/config/grub/xhci_nvme/patches/0014-grub-core-bus-usb-usbhub-Add-xHCI-non-root-hub-suppo.patch
new file mode 100644
index 00000000..98f38569
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0014-grub-core-bus-usb-usbhub-Add-xHCI-non-root-hub-suppo.patch
@@ -0,0 +1,127 @@
+From 861b218be772b8abf090647fc24eb7c7ccc1d2db Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Mon, 7 Dec 2020 08:41:27 +0100
+Subject: [PATCH 14/20] grub-core/bus/usb/usbhub: Add xHCI non root hub support
+
+Tested on Intel PCH C246, the USB3 hub can be configured by grub.
+
+Issues:
+* USB3 devices connected behind that hub are sometimes not detected.
+
+Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
+---
+ grub-core/bus/usb/usbhub.c | 38 +++++++++++++++++++++++++++++++++-----
+ include/grub/usbdesc.h | 1 +
+ include/grub/usbtrans.h | 4 ++++
+ 3 files changed, 38 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
+index b4b3a1a61..e96505aa9 100644
+--- a/grub-core/bus/usb/usbhub.c
++++ b/grub-core/bus/usb/usbhub.c
+@@ -148,19 +148,32 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
+ return dev;
+ }
+
+-
++static grub_usb_err_t
++grub_usb_set_hub_depth(grub_usb_device_t dev, grub_uint8_t depth)
++{
++ return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
++ | GRUB_USB_REQTYPE_CLASS
++ | GRUB_USB_REQTYPE_TARGET_DEV),
++ GRUB_USB_HUB_REQ_SET_HUB_DEPTH, depth,
++ 0, 0, NULL);
++}
++
+ static grub_usb_err_t
+ grub_usb_add_hub (grub_usb_device_t dev)
+ {
+ struct grub_usb_usb_hubdesc hubdesc;
+ grub_usb_err_t err;
++ grub_uint16_t req;
+ int i;
+
++ req = (dev->speed == GRUB_USB_SPEED_SUPER) ? GRUB_USB_DESCRIPTOR_SS_HUB :
++ GRUB_USB_DESCRIPTOR_HUB;
++
+ err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
+ | GRUB_USB_REQTYPE_CLASS
+ | GRUB_USB_REQTYPE_TARGET_DEV),
+- GRUB_USB_REQ_GET_DESCRIPTOR,
+- (GRUB_USB_DESCRIPTOR_HUB << 8) | 0,
++ GRUB_USB_REQ_GET_DESCRIPTOR,
++ (req << 8) | 0,
+ 0, sizeof (hubdesc), (char *) &hubdesc);
+ if (err)
+ return err;
+@@ -183,6 +196,19 @@ grub_usb_add_hub (grub_usb_device_t dev)
+ return GRUB_USB_ERR_INTERNAL;
+ }
+
++ if (dev->speed == GRUB_USB_SPEED_SUPER)
++ {
++ grub_uint8_t depth;
++ grub_uint32_t route;
++ /* Depth maximum value is 5, but root hubs doesn't count */
++ for (depth = 0, route = dev->route; (route & 0xf) > 0; route >>= 4)
++ depth++;
++
++ err = grub_usb_set_hub_depth(dev, depth);
++ if (err)
++ return err;
++ }
++
+ /* Power on all Hub ports. */
+ for (i = 1; i <= hubdesc.portcnt; i++)
+ {
+@@ -637,7 +663,9 @@ poll_nonroot_hub (grub_usb_device_t dev)
+ int split_hubaddr = 0;
+
+ /* Determine the device speed. */
+- if (status & GRUB_USB_HUB_STATUS_PORT_LOWSPEED)
++ if (dev->speed == GRUB_USB_SPEED_SUPER)
++ speed = GRUB_USB_SPEED_SUPER;
++ else if (status & GRUB_USB_HUB_STATUS_PORT_LOWSPEED)
+ speed = GRUB_USB_SPEED_LOW;
+ else
+ {
+@@ -651,7 +679,7 @@ poll_nonroot_hub (grub_usb_device_t dev)
+ grub_millisleep (10);
+
+ /* Find correct values for SPLIT hubport and hubaddr */
+- if (speed == GRUB_USB_SPEED_HIGH)
++ if (speed == GRUB_USB_SPEED_HIGH || speed == GRUB_USB_SPEED_SUPER)
+ {
+ /* HIGH speed device needs not transaction translation */
+ split_hubport = 0;
+diff --git a/include/grub/usbdesc.h b/include/grub/usbdesc.h
+index bb2ab2e27..1697aa465 100644
+--- a/include/grub/usbdesc.h
++++ b/include/grub/usbdesc.h
+@@ -30,6 +30,7 @@ typedef enum {
+ GRUB_USB_DESCRIPTOR_ENDPOINT,
+ GRUB_USB_DESCRIPTOR_DEBUG = 10,
+ GRUB_USB_DESCRIPTOR_HUB = 0x29,
++ GRUB_USB_DESCRIPTOR_SS_HUB = 0x2a,
+ GRUB_USB_DESCRIPTOR_SS_ENDPOINT_COMPANION = 0x30
+ } grub_usb_descriptor_t;
+
+diff --git a/include/grub/usbtrans.h b/include/grub/usbtrans.h
+index 039ebed65..d6c3f71dc 100644
+--- a/include/grub/usbtrans.h
++++ b/include/grub/usbtrans.h
+@@ -110,6 +110,10 @@ enum
+ GRUB_USB_REQ_SET_INTERFACE = 0x0B,
+ GRUB_USB_REQ_SYNC_FRAME = 0x0C
+ };
++enum
++ {
++ GRUB_USB_HUB_REQ_SET_HUB_DEPTH = 0x0C,
++ };
+
+ #define GRUB_USB_FEATURE_ENDP_HALT 0x00
+ #define GRUB_USB_FEATURE_DEV_REMOTE_WU 0x01
+--
+2.47.3
+
diff --git a/config/grub/xhci_nvme/patches/0015-xHCI-also-accept-SBRN-0x31-and-0x32.patch b/config/grub/xhci_nvme/patches/0015-xHCI-also-accept-SBRN-0x31-and-0x32.patch
new file mode 100644
index 00000000..69d0e653
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0015-xHCI-also-accept-SBRN-0x31-and-0x32.patch
@@ -0,0 +1,26 @@
+From 0a1a2f3a2f4b3908f094133c86fda0e882dabda5 Mon Sep 17 00:00:00 2001
+From: Sven Anderson <sven@anderson.de>
+Date: Sat, 28 May 2022 21:39:23 +0200
+Subject: [PATCH 15/20] xHCI: also accept SBRN 0x31 and 0x32
+
+Signed-off-by: Sven Anderson <sven@anderson.de>
+---
+ grub-core/bus/usb/xhci-pci.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/bus/usb/xhci-pci.c b/grub-core/bus/usb/xhci-pci.c
+index a5bd3c97d..cde21f57a 100644
+--- a/grub-core/bus/usb/xhci-pci.c
++++ b/grub-core/bus/usb/xhci-pci.c
+@@ -76,7 +76,7 @@ grub_xhci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid,
+ /* Check Serial Bus Release Number */
+ addr = grub_pci_make_address (dev, GRUB_XHCI_PCI_SBRN_REG);
+ release = grub_pci_read_byte (addr);
+- if (release != 0x30)
++ if (release != 0x30 && release != 0x31 &&release != 0x32)
+ {
+ grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: Wrong SBRN: %0x\n",
+ release);
+--
+2.47.3
+
diff --git a/config/grub/xhci_nvme/patches/0016-xhci-fix-port-indexing.patch b/config/grub/xhci_nvme/patches/0016-xhci-fix-port-indexing.patch
new file mode 100644
index 00000000..bdbaf417
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0016-xhci-fix-port-indexing.patch
@@ -0,0 +1,43 @@
+From 9343cdda106d47561f25ce1e39a86d62cb0bfd08 Mon Sep 17 00:00:00 2001
+From: Sven Anderson <sven@anderson.de>
+Date: Mon, 13 Jan 2025 19:51:41 +0100
+Subject: [PATCH 16/20] xhci: fix port indexing
+
+---
+ grub-core/bus/usb/xhci.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/bus/usb/xhci.c b/grub-core/bus/usb/xhci.c
+index f4591ffb5..dc89b9619 100644
+--- a/grub-core/bus/usb/xhci.c
++++ b/grub-core/bus/usb/xhci.c
+@@ -2250,7 +2250,7 @@ grub_xhci_detect_dev (grub_usb_controller_t dev, int port, int *changed)
+
+ *changed = 0;
+ grub_dprintf("xhci", "%s: dev=%p USB%d_%d port %d\n", __func__, dev,
+- x->psids[port-1].major, x->psids[port-1].minor, port);
++ x->psids[port].major, x->psids[port].minor, port);
+
+ /* On shutdown advertise all ports as disconnected. This will trigger
+ * a gracefull detatch. */
+@@ -2285,13 +2285,13 @@ grub_xhci_detect_dev (grub_usb_controller_t dev, int port, int *changed)
+ if (!(portsc & GRUB_XHCI_PORTSC_CCS))
+ return GRUB_USB_SPEED_NONE;
+
+- for (grub_uint8_t i = 0; i < 16 && x->psids[port-1].psids[i].id > 0; i++)
++ for (grub_uint8_t i = 0; i < 16 && x->psids[port].psids[i].id > 0; i++)
+ {
+- if (x->psids[port-1].psids[i].id == speed)
++ if (x->psids[port].psids[i].id == speed)
+ {
+ grub_dprintf("xhci", "%s: grub_usb_speed = %d\n", __func__,
+- x->psids[port-1].psids[i].grub_usb_speed );
+- return x->psids[port-1].psids[i].grub_usb_speed;
++ x->psids[port].psids[i].grub_usb_speed );
++ return x->psids[port].psids[i].grub_usb_speed;
+ }
+ }
+
+--
+2.47.3
+
diff --git a/config/grub/xhci_nvme/patches/0017-xhci-configure-TT-for-non-root-hubs.patch b/config/grub/xhci_nvme/patches/0017-xhci-configure-TT-for-non-root-hubs.patch
new file mode 100644
index 00000000..83410e2e
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0017-xhci-configure-TT-for-non-root-hubs.patch
@@ -0,0 +1,98 @@
+From 41e966e3054862e1921e14bd2ecec3bfb20cba8f Mon Sep 17 00:00:00 2001
+From: Sven Anderson <sven@anderson.de>
+Date: Mon, 13 Jan 2025 20:26:32 +0100
+Subject: [PATCH 17/20] xhci: configure TT for non-root-hubs
+
+---
+ grub-core/bus/usb/usbhub.c | 6 +++++
+ grub-core/bus/usb/xhci.c | 45 +++++++++++++++++++++++++++++++++-----
+ include/grub/usb.h | 2 ++
+ 3 files changed, 47 insertions(+), 6 deletions(-)
+
+diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
+index e96505aa9..629b3ed53 100644
+--- a/grub-core/bus/usb/usbhub.c
++++ b/grub-core/bus/usb/usbhub.c
+@@ -818,3 +818,9 @@ grub_usb_iterate (grub_usb_iterate_hook_t hook, void *hook_data)
+
+ return 0;
+ }
++
++grub_usb_device_t
++grub_usb_get_dev (int addr)
++{
++ return grub_usb_devs[addr];
++}
+diff --git a/grub-core/bus/usb/xhci.c b/grub-core/bus/usb/xhci.c
+index dc89b9619..88c9ac57f 100644
+--- a/grub-core/bus/usb/xhci.c
++++ b/grub-core/bus/usb/xhci.c
+@@ -623,13 +623,46 @@ grub_xhci_alloc_inctx(struct grub_xhci *x, int maxepid,
+ break;
+ }
+
+- /* Route is greater zero on devices that are connected to a non root hub */
+- if (dev->route)
+- {
+- /* FIXME: Implement this code for non SuperSpeed hub devices */
++ /* Set routing string */
++ slot->ctx[0] |= dev->route;
++
++ /* Set root hub port number */
++ slot->ctx[1] |= (dev->root_port + 1) << 16;
++
++ if (dev->split_hubaddr && (dev->speed == GRUB_USB_SPEED_LOW ||
++ dev->speed == GRUB_USB_SPEED_FULL)) {
++
++ grub_usb_device_t hubdev = grub_usb_get_dev(dev->split_hubaddr);
++
++ if (!hubdev || hubdev->descdev.class != GRUB_USB_CLASS_HUB) {
++ grub_dprintf("xhci", "Invalid hub device at addr %d!\n", dev->split_hubaddr);
++ return NULL;
++ }
++
++ struct grub_xhci_priv *hub_priv = hubdev->xhci_priv;
++ if (!hub_priv) {
++ grub_dprintf("xhci", "Hub has no xhci_priv!\n");
++ return NULL;
++ }
++
++ if (hubdev->speed == GRUB_USB_SPEED_HIGH) {
++ /* Direct connection to high-speed hub - set up TT */
++ grub_dprintf("xhci", "Direct high-speed hub connection - configuring TT with "
++ "hub slot %d port %d\n", hub_priv->slotid, dev->split_hubport);
++ slot->ctx[2] |= hub_priv->slotid;
++ slot->ctx[2] |= dev->split_hubport << 8;
+ }
+- slot->ctx[0] |= dev->route;
+- slot->ctx[1] |= (dev->root_port+1) << 16;
++ else {
++ /* Hub is not high-speed, inherit TT settings from parent */
++ volatile struct grub_xhci_slotctx *hubslot;
++ grub_dprintf("xhci", "Non high-speed hub - inheriting TT settings from parent\n");
++ hubslot = grub_dma_phys2virt(x->devs[hub_priv->slotid].ptr_low, x->devs_dma);
++ slot->ctx[2] = hubslot->ctx[2];
++ }
++ }
++
++ grub_dprintf("xhci", "Slot context: ctx[0]=0x%08x ctx[1]=0x%08x ctx[2]=0x%08x\n",
++ slot->ctx[0], slot->ctx[1], slot->ctx[2]);
+
+ grub_arch_sync_dma_caches(in, size);
+
+diff --git a/include/grub/usb.h b/include/grub/usb.h
+index eb71fa1c7..df97a60cc 100644
+--- a/include/grub/usb.h
++++ b/include/grub/usb.h
+@@ -62,6 +62,8 @@ typedef int (*grub_usb_controller_iterate_hook_t) (grub_usb_controller_t dev,
+ /* Call HOOK with each device, until HOOK returns non-zero. */
+ int grub_usb_iterate (grub_usb_iterate_hook_t hook, void *hook_data);
+
++grub_usb_device_t grub_usb_get_dev (int addr);
++
+ grub_usb_err_t grub_usb_device_initialize (grub_usb_device_t dev);
+
+ grub_usb_err_t grub_usb_get_descriptor (grub_usb_device_t dev,
+--
+2.47.3
+
diff --git a/config/grub/xhci_nvme/patches/0018-Fix-compilation-on-x86_64.patch b/config/grub/xhci_nvme/patches/0018-Fix-compilation-on-x86_64.patch
new file mode 100644
index 00000000..405cfeb9
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0018-Fix-compilation-on-x86_64.patch
@@ -0,0 +1,90 @@
+From 114346473d1e5d3660ea0dae4ed9f4c8ad98bbeb Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Wed, 24 Feb 2021 08:25:41 +0100
+Subject: [PATCH 18/20] Fix compilation on x86_64
+
+Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
+---
+ grub-core/bus/usb/xhci.c | 24 ++++++++++++++++--------
+ 1 file changed, 16 insertions(+), 8 deletions(-)
+
+diff --git a/grub-core/bus/usb/xhci.c b/grub-core/bus/usb/xhci.c
+index 88c9ac57f..9b9bae6e5 100644
+--- a/grub-core/bus/usb/xhci.c
++++ b/grub-core/bus/usb/xhci.c
+@@ -184,7 +184,7 @@ enum
+ * then we can get it from a trb pointer (provided by evt ring).
+ */
+ #define XHCI_RING(_trb) \
+- ((struct grub_xhci_ring*)((grub_uint32_t)(_trb) & ~(GRUB_XHCI_RING_SIZE-1)))
++ ((struct grub_xhci_ring*)((grub_addr_t)(_trb) & ~(GRUB_XHCI_RING_SIZE-1)))
+
+ /* slot context */
+ struct grub_xhci_slotctx {
+@@ -495,6 +495,14 @@ grub_xhci_read8(volatile void *addr) {
+ return (*((volatile grub_uint32_t *)addr));
+ }
+
++static inline void *
++grub_xhci_read_etrb_ptr(volatile struct grub_xhci_trb *trb) {
++ grub_uint64_t tmp;
++ tmp = (grub_uint64_t)grub_xhci_read32(&trb->ptr_low);
++ tmp |= ((grub_uint64_t)grub_xhci_read32(&trb->ptr_high)) << 32;
++ return (void *)(grub_addr_t)tmp;
++}
++
+ static inline grub_uint32_t
+ grub_xhci_port_read (struct grub_xhci *x, grub_uint32_t port)
+ {
+@@ -697,7 +705,7 @@ static void xhci_process_events(struct grub_xhci *x)
+ case ER_TRANSFER:
+ case ER_COMMAND_COMPLETE:
+ {
+- struct grub_xhci_trb *rtrb = (void*)grub_xhci_read32(&etrb->ptr_low);
++ struct grub_xhci_trb *rtrb = grub_xhci_read_etrb_ptr(etrb);
+ struct grub_xhci_ring *ring = XHCI_RING(rtrb);
+ volatile struct grub_xhci_trb *evt = &ring->evt;
+ grub_uint32_t eidx = rtrb - ring->ring + 1;
+@@ -730,9 +738,9 @@ static void xhci_process_events(struct grub_xhci *x)
+ }
+ grub_xhci_write32(&evts->nidx, nidx);
+ volatile struct grub_xhci_ir *ir = x->ir;
+- grub_uint32_t erdp = (grub_uint32_t)(evts->ring + nidx);
+- grub_xhci_write32(&ir->erdp_low, erdp);
+- grub_xhci_write32(&ir->erdp_high, 0);
++ grub_uint64_t erdp = (grub_addr_t)(void *)(&evts->ring[nidx]);
++ grub_xhci_write32(&ir->erdp_low, erdp & 0xffffffff);
++ grub_xhci_write32(&ir->erdp_high, erdp >> 32);
+ }
+ }
+
+@@ -833,7 +841,7 @@ static void xhci_trb_queue(volatile struct grub_xhci_ring *ring,
+ grub_uint32_t xferlen, grub_uint32_t flags)
+ {
+ grub_dprintf("xhci", "%s: ring %p data %llx len %d flags 0x%x remain 0x%x\n", __func__,
+- ring, data_or_addr, xferlen & 0x1ffff, flags, xferlen >> 17);
++ ring, (unsigned long long)data_or_addr, xferlen & 0x1ffff, flags, xferlen >> 17);
+
+ if (xhci_ring_full(ring))
+ {
+@@ -1940,7 +1948,7 @@ grub_xhci_setup_transfer (grub_usb_controller_t dev,
+ if (transfer->type == GRUB_USB_TRANSACTION_TYPE_CONTROL)
+ {
+ volatile struct grub_usb_packet_setup *setupdata;
+- setupdata = (void *)transfer->transactions[0].data;
++ setupdata = (void *)(grub_addr_t)transfer->transactions[0].data;
+ grub_dprintf("xhci", "%s: CONTROLL TRANS req %d\n", __func__, setupdata->request);
+ grub_dprintf("xhci", "%s: CONTROLL TRANS length %d\n", __func__, setupdata->length);
+
+@@ -2007,7 +2015,7 @@ grub_xhci_setup_transfer (grub_usb_controller_t dev,
+ /* Assume the ring has enough free space for all TRBs */
+ if (flags & TRB_TR_IDT && tr->size <= (int)sizeof(inline_data))
+ {
+- grub_memcpy(&inline_data, (void *)tr->data, tr->size);
++ grub_memcpy(&inline_data, (void *)(grub_addr_t)tr->data, tr->size);
+ xhci_trb_queue(reqs, inline_data, tr->size, flags);
+ }
+ else
+--
+2.47.3
+
diff --git a/config/grub/xhci_nvme/patches/0019-Add-native-NVMe-driver-based-on-SeaBIOS.patch b/config/grub/xhci_nvme/patches/0019-Add-native-NVMe-driver-based-on-SeaBIOS.patch
new file mode 100644
index 00000000..57762843
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0019-Add-native-NVMe-driver-based-on-SeaBIOS.patch
@@ -0,0 +1,1074 @@
+From 2b255ee97a48f280a20acd807386bec52ea447c8 Mon Sep 17 00:00:00 2001
+From: Mate Kukri <km@mkukri.xyz>
+Date: Mon, 20 May 2024 11:43:35 +0100
+Subject: [PATCH 19/20] 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 65016f856..7bc0866ba 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 xhci 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 xhci 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 208badbdf..219282524 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -2775,3 +2775,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/xhci_nvme/patches/0020-kern-coreboot-mmap-Map-to-reserved.patch b/config/grub/xhci_nvme/patches/0020-kern-coreboot-mmap-Map-to-reserved.patch
new file mode 100644
index 00000000..9cf94a25
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0020-kern-coreboot-mmap-Map-to-reserved.patch
@@ -0,0 +1,37 @@
+From 4ece79a5e4708b96c2fd06a80b1aac0b6e232675 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 20/20] 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/xhci_nvme/patches/0021-Revert-configure-Check-linker-for-image-base-support.patch b/config/grub/xhci_nvme/patches/0021-Revert-configure-Check-linker-for-image-base-support.patch
new file mode 100644
index 00000000..6ec818ff
--- /dev/null
+++ b/config/grub/xhci_nvme/patches/0021-Revert-configure-Check-linker-for-image-base-support.patch
@@ -0,0 +1,69 @@
+From 70e4ffed2f1cd9b6871341260cae31a5e68c3081 Mon Sep 17 00:00:00 2001
+From: Leah Rowe <leah@libreboot.org>
+Date: Wed, 24 Dec 2025 01:42:17 +0100
+Subject: [PATCH 1/1] 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 a282bf7bf..17937baf4 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
+
+@@ -1793,18 +1795,6 @@ LIBS=""
+ grub_ASM_USCORE
+ grub_PROG_TARGET_CC
+ 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/xhci_nvme/target.cfg b/config/grub/xhci_nvme/target.cfg
new file mode 100644
index 00000000..d2be2d30
--- /dev/null
+++ b/config/grub/xhci_nvme/target.cfg
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+tree="xhci_nvme"
+rev="25b7f6b9344a4bac18c26ce143a156ac2bcb3ec4"