summaryrefslogtreecommitdiff
path: root/config/grub/xhci/patches
diff options
context:
space:
mode:
Diffstat (limited to 'config/grub/xhci/patches')
-rw-r--r--config/grub/xhci/patches/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch90
-rw-r--r--config/grub/xhci/patches/0002-say-the-name-libreboot-in-the-grub-menu.patch25
-rw-r--r--config/grub/xhci/patches/0003-Add-CC0-license.patch42
-rw-r--r--config/grub/xhci/patches/0004-Define-GRUB_UINT32_MAX.patch39
-rw-r--r--config/grub/xhci/patches/0005-Add-Argon2-algorithm.patch2611
-rw-r--r--config/grub/xhci/patches/0006-Error-on-missing-Argon2id-parameters.patch58
-rw-r--r--config/grub/xhci/patches/0007-Compile-with-Argon2id-support.patch83
-rw-r--r--config/grub/xhci/patches/0008-Make-grub-install-work-with-Argon2.patch26
-rw-r--r--config/grub/xhci/patches/0009-at_keyboard-coreboot-force-scancodes2-translate.patch107
-rw-r--r--config/grub/xhci/patches/0010-keylayouts-don-t-print-Unknown-key-message.patch38
-rw-r--r--config/grub/xhci/patches/0011-don-t-print-missing-prefix-errors-on-the-screen.patch102
-rw-r--r--config/grub/xhci/patches/0012-don-t-print-error-if-module-not-found.patch34
-rw-r--r--config/grub/xhci/patches/0013-don-t-print-empty-error-messages.patch31
-rw-r--r--config/grub/xhci/patches/0014-grub-core-bus-usb-Parse-SuperSpeed-companion-descrip.patch246
-rw-r--r--config/grub/xhci/patches/0015-usb-Add-enum-for-xHCI.patch29
-rw-r--r--config/grub/xhci/patches/0016-usbtrans-Set-default-maximum-packet-size.patch33
-rw-r--r--config/grub/xhci/patches/0017-grub-core-bus-usb-Add-function-pointer-for-attach-de.patch121
-rw-r--r--config/grub/xhci/patches/0018-grub-core-bus-usb-usbhub-Add-new-private-fields-for-.patch77
-rw-r--r--config/grub/xhci/patches/0019-grub-core-bus-usb-Add-xhci-support.patch2814
-rw-r--r--config/grub/xhci/patches/0020-grub-core-bus-usb-usbhub-Add-xHCI-non-root-hub-suppo.patch127
-rw-r--r--config/grub/xhci/patches/0021-Fix-compilation-on-x86_64.patch90
-rw-r--r--config/grub/xhci/patches/0022-Add-native-NVMe-driver-based-on-SeaBIOS.patch1074
22 files changed, 7897 insertions, 0 deletions
diff --git a/config/grub/xhci/patches/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch b/config/grub/xhci/patches/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch
new file mode 100644
index 00000000..0cc0cb34
--- /dev/null
+++ b/config/grub/xhci/patches/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch
@@ -0,0 +1,90 @@
+From 1ee64f2373af3ad992993f9cf103a29df0359c3c 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/22] 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.39.2
+
diff --git a/config/grub/xhci/patches/0002-say-the-name-libreboot-in-the-grub-menu.patch b/config/grub/xhci/patches/0002-say-the-name-libreboot-in-the-grub-menu.patch
new file mode 100644
index 00000000..bf2b5940
--- /dev/null
+++ b/config/grub/xhci/patches/0002-say-the-name-libreboot-in-the-grub-menu.patch
@@ -0,0 +1,25 @@
+From 0e89a40423cdd6f8f20ad03d1c2f54ee7b5ea1b6 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/22] 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 bd4431000..300f55fe1 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -209,7 +209,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 20241008 release, based on coreboot. https://libreboot.org/"));
+ if (!msg_formatted)
+ return;
+
+--
+2.39.2
+
diff --git a/config/grub/xhci/patches/0003-Add-CC0-license.patch b/config/grub/xhci/patches/0003-Add-CC0-license.patch
new file mode 100644
index 00000000..cf35c343
--- /dev/null
+++ b/config/grub/xhci/patches/0003-Add-CC0-license.patch
@@ -0,0 +1,42 @@
+From c9be46c903d4aabc88fe4d9394d7a8e024868c32 Mon Sep 17 00:00:00 2001
+From: Ax333l <main@axelen.xyz>
+Date: Thu, 17 Aug 2023 00:00:00 +0000
+Subject: [PATCH 03/22] Add CC0 license
+
+Signed-off-by: Nicholas Johnson <nick@nicholasjohnson.ch>
+---
+ grub-core/kern/dl.c | 3 ++-
+ util/grub-module-verifierXX.c | 3 ++-
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
+index 0bf40caa6..4011e2d15 100644
+--- a/grub-core/kern/dl.c
++++ b/grub-core/kern/dl.c
+@@ -470,7 +470,8 @@ grub_dl_check_license (grub_dl_t mod, Elf_Ehdr *e)
+
+ if (grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3") == 0
+ || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3+") == 0
+- || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv2+") == 0)
++ || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv2+") == 0
++ || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=CC0") == 0)
+ return GRUB_ERR_NONE;
+
+ return grub_error (GRUB_ERR_BAD_MODULE,
+diff --git a/util/grub-module-verifierXX.c b/util/grub-module-verifierXX.c
+index a42c20bd1..7157a30aa 100644
+--- a/util/grub-module-verifierXX.c
++++ b/util/grub-module-verifierXX.c
+@@ -236,7 +236,8 @@ check_license (const char * const filename,
+ Elf_Shdr *s = find_section (arch, e, ".module_license", module_size);
+ if (s && (strcmp ((char *) e + grub_target_to_host(s->sh_offset), "LICENSE=GPLv3") == 0
+ || strcmp ((char *) e + grub_target_to_host(s->sh_offset), "LICENSE=GPLv3+") == 0
+- || strcmp ((char *) e + grub_target_to_host(s->sh_offset), "LICENSE=GPLv2+") == 0))
++ || strcmp ((char *) e + grub_target_to_host(s->sh_offset), "LICENSE=GPLv2+") == 0
++ || strcmp ((char *) e + grub_target_to_host(s->sh_offset), "LICENSE=CC0") == 0))
+ return;
+ grub_util_error ("%s: incompatible license", filename);
+ }
+--
+2.39.2
+
diff --git a/config/grub/xhci/patches/0004-Define-GRUB_UINT32_MAX.patch b/config/grub/xhci/patches/0004-Define-GRUB_UINT32_MAX.patch
new file mode 100644
index 00000000..08566ba9
--- /dev/null
+++ b/config/grub/xhci/patches/0004-Define-GRUB_UINT32_MAX.patch
@@ -0,0 +1,39 @@
+From c988b6b3bb7567f9ed6bb0332b992577011970c2 Mon Sep 17 00:00:00 2001
+From: Ax333l <main@axelen.xyz>
+Date: Thu, 17 Aug 2023 00:00:00 +0000
+Subject: [PATCH 04/22] Define GRUB_UINT32_MAX
+
+Signed-off-by: Nicholas Johnson <nick@nicholasjohnson.ch>
+---
+ include/grub/types.h | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/include/grub/types.h b/include/grub/types.h
+index 45079bf65..8c0b30395 100644
+--- a/include/grub/types.h
++++ b/include/grub/types.h
+@@ -156,6 +156,7 @@ typedef grub_int32_t grub_ssize_t;
+ #define GRUB_SHRT_MAX 0x7fff
+ #define GRUB_SHRT_MIN (-GRUB_SHRT_MAX - 1)
+ #define GRUB_UINT_MAX 4294967295U
++#define GRUB_UINT32_MAX 4294967295U
+ #define GRUB_INT_MAX 0x7fffffff
+ #define GRUB_INT_MIN (-GRUB_INT_MAX - 1)
+ #define GRUB_INT32_MAX 2147483647
+@@ -177,6 +178,13 @@ typedef grub_int32_t grub_ssize_t;
+ #define GRUB_TYPE_U_MAX(type) ((unsigned long long)((typeof (type))(~0)))
+ #define GRUB_TYPE_U_MIN(type) 0ULL
+
++# define GRUB_UINT32_C(x) x ## U
++# if GRUB_ULONG_MAX >> 31 >> 31 >> 1 == 1
++# define GRUB_UINT64_C(x) x##UL
++# elif 1
++# define GRUB_UINT64_C(x) x##ULL
++# endif
++
+ typedef grub_uint64_t grub_properly_aligned_t;
+
+ #define GRUB_PROPERLY_ALIGNED_ARRAY(name, size) grub_properly_aligned_t name[((size) + sizeof (grub_properly_aligned_t) - 1) / sizeof (grub_properly_aligned_t)]
+--
+2.39.2
+
diff --git a/config/grub/xhci/patches/0005-Add-Argon2-algorithm.patch b/config/grub/xhci/patches/0005-Add-Argon2-algorithm.patch
new file mode 100644
index 00000000..85a629c5
--- /dev/null
+++ b/config/grub/xhci/patches/0005-Add-Argon2-algorithm.patch
@@ -0,0 +1,2611 @@
+From e2cfe7dcdb384ce5268a7c6e5cc8a6e8e01fc05f Mon Sep 17 00:00:00 2001
+From: Ax333l <main@axelen.xyz>
+Date: Thu, 17 Aug 2023 00:00:00 +0000
+Subject: [PATCH 05/22] Add Argon2 algorithm
+
+Signed-off-by: Nicholas Johnson <nick@nicholasjohnson.ch>
+---
+ docs/grub-dev.texi | 64 +++
+ grub-core/Makefile.core.def | 8 +
+ grub-core/lib/argon2/LICENSE | 314 +++++++++++
+ grub-core/lib/argon2/argon2.c | 232 ++++++++
+ grub-core/lib/argon2/argon2.h | 264 +++++++++
+ grub-core/lib/argon2/blake2/blake2-impl.h | 151 ++++++
+ grub-core/lib/argon2/blake2/blake2.h | 89 +++
+ grub-core/lib/argon2/blake2/blake2b.c | 388 ++++++++++++++
+ .../lib/argon2/blake2/blamka-round-ref.h | 56 ++
+ grub-core/lib/argon2/core.c | 506 ++++++++++++++++++
+ grub-core/lib/argon2/core.h | 228 ++++++++
+ grub-core/lib/argon2/ref.c | 190 +++++++
+ 12 files changed, 2490 insertions(+)
+ create mode 100644 grub-core/lib/argon2/LICENSE
+ create mode 100644 grub-core/lib/argon2/argon2.c
+ create mode 100644 grub-core/lib/argon2/argon2.h
+ create mode 100644 grub-core/lib/argon2/blake2/blake2-impl.h
+ create mode 100644 grub-core/lib/argon2/blake2/blake2.h
+ create mode 100644 grub-core/lib/argon2/blake2/blake2b.c
+ create mode 100644 grub-core/lib/argon2/blake2/blamka-round-ref.h
+ create mode 100644 grub-core/lib/argon2/core.c
+ create mode 100644 grub-core/lib/argon2/core.h
+ create mode 100644 grub-core/lib/argon2/ref.c
+
+diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
+index 1276c5930..cd6fb0e1e 100644
+--- a/docs/grub-dev.texi
++++ b/docs/grub-dev.texi
+@@ -503,11 +503,75 @@ GRUB includes some code from other projects, and it is sometimes necessary
+ to update it.
+
+ @menu
++* Argon2::
+ * Gnulib::
+ * jsmn::
+ * minilzo::
+ @end menu
+
++@node Argon2
++@section Argon2
++
++Argon2 is a key derivation function used by LUKS2 in order to derive encryption
++keys from a user-provided password. GRUB imports the official reference
++implementation of Argon2 from @url{https://github.com/P-H-C/phc-winner-argon2}.
++In order to make the library usable for GRUB, we need to perform various
++conversions. This is mainly due to the fact that the imported code makes use of
++types and functions defined in the C standard library, which isn't available.
++Furthermore, using the POSIX wrapper library is not possible as the code needs
++to be part of the kernel.
++
++Updating the code can thus be performed like following:
++
++@example
++$ git clone https://github.com/P-H-C/phc-winner-argon2 argon2
++$ cp argon2/include/argon2.h argon2/src/@{argon2.c,core.c,core.h,ref.c@} \
++ grub-core/lib/argon2/
++$ cp argon2/src/blake2/@{blake2-impl.h,blake2.h,blake2b.c,blamka-round-ref.h@} \
++ grub-core/lib/argon2/blake2/
++$ sed -e 's/UINT32_C/GRUB_UINT32_C/g' \
++ -e 's/UINT64_C/GRUB_UINT64_C/g' \
++ -e 's/UINT32_MAX/GRUB_UINT32_MAX/g' \
++ -e 's/CHAR_BIT/GRUB_CHAR_BIT/g' \
++ -e 's/UINT_MAX/GRUB_UINT_MAX/g' \
++ -e 's/uintptr_t/grub_addr_t/g' \
++ -e 's/size_t/grub_size_t/g' \
++ -e 's/uint32_t/grub_uint32_t/g' \
++ -e 's/uint64_t/grub_uint64_t/g' \
++ -e 's/uint8_t/grub_uint8_t/g' \
++ -e 's/memset/grub_memset/g' \
++ -e 's/memcpy/grub_memcpy/g' \
++ -e 's/malloc/grub_malloc/g' \
++ -e 's/free/grub_free/g' \
++ -e 's/#elif _MSC_VER/#elif defined(_MSC_VER)/' \
++ grub-core/lib/argon2/@{*,blake2/*@}.@{c,h@} -i
++@end example
++
++Afterwards, you need to perform the following manual steps:
++
++@enumerate
++@item Remove all includes of standard library headers, "encoding.h" and
++ "thread.h".
++@item Add includes <grub/mm.h> and <grub/misc.h> to "argon2.h".
++@item Add include <grub/dl.h> and module license declaration to "argon2.c".
++@item Remove the following declarations and functions from "argon2.h" and
++ "argon2.c": argon2_type2string, argon2i_hash_encoded, argon2i_hash_raw,
++ argon2d_hash_encoded, argon2d_hash_raw, argon2id_hash_encoded,
++ argon2id_hash_raw, argon2_compare, argon2_verify, argon2i_verify,
++ argon2d_verify, argon2id_verify, argon2d_ctx, argon2i_ctx, argon2id_ctx,
++ argon2_verify_ctx, argon2d_verify_ctx, argon2i_verify_ctx,
++ argon2id_verify_ctx, argon2_encodedlen.
++@item Move the declaration of `clear_internal_memory()` in "blake2-impl.h" to
++ "blake2b.c".
++@item Remove code guarded by the ARGON2_NO_THREADS macro.
++@item Remove parameters `encoded` and `encodedlen` from `argon2_hash` and remove
++ the encoding block in that function.
++@item Remove parameter verifications in `validate_inputs()` for
++ ARGON2_MIN_PWD_LENGTH, ARGON2_MIN_SECRET, ARGON2_MIN_AD_LENGTH and
++ ARGON2_MAX_MEMORY to fix compiler warnings.
++@item Mark the function argon2_ctx as static.
++@end enumerate
++
+ @node Gnulib
+ @section Gnulib
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 705d73fab..452f11b20 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -1219,6 +1219,14 @@ module = {
+ common = lib/json/json.c;
+ };
+
++module = {
++ name = argon2;
++ common = lib/argon2/argon2.c;
++ common = lib/argon2/core.c;
++ common = lib/argon2/ref.c;
++ common = lib/argon2/blake2/blake2b.c;
++};
++
+ module = {
+ name = afsplitter;
+ common = disk/AFSplitter.c;
+diff --git a/grub-core/lib/argon2/LICENSE b/grub-core/lib/argon2/LICENSE
+new file mode 100644
+index 000000000..97aae2925
+--- /dev/null
++++ b/grub-core/lib/argon2/LICENSE
+@@ -0,0 +1,314 @@
++Argon2 reference source code package - reference C implementations
++
++Copyright 2015
++Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
++
++You may use this work under the terms of a Creative Commons CC0 1.0
++License/Waiver or the Apache Public License 2.0, at your option. The terms of
++these licenses can be found at:
++
++- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
++- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
++
++The terms of the licenses are reproduced below.
++
++--------------------------------------------------------------------------------
++
++Creative Commons Legal Code
++
++CC0 1.0 Universal
++
++ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
++ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
++ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
++ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
++ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
++ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
++ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
++ HEREUNDER.
++
++Statement of Purpose
++
++The laws of most jurisdictions throughout the world automatically confer
++exclusive Copyright and Related Rights (defined below) upon the creator
++and subsequent owner(s) (each and all, an "owner") of an original work of
++authorship and/or a database (each, a "Work").
++
++Certain owners wish to permanently relinquish those rights to a Work for
++the purpose of contributing to a commons of creative, cultural and
++scientific works ("Commons") that the public can reliably and without fear
++of later claims of infringement build upon, modify, incorporate in other
++works, reuse and redistribute as freely as possible in any form whatsoever
++and for any purposes, including without limitation commercial purposes.
++These owners may contribute to the Commons to promote the ideal of a free
++culture and the further production of creative, cultural and scientific
++works, or to gain reputation or greater distribution for their Work in
++part through the use and efforts of others.
++
++For these and/or other purposes and motivations, and without any
++expectation of additional consideration or compensation, the person
++associating CC0 with a Work (the "Affirmer"), to the extent that he or she
++is an owner of Copyright and Related Rights in the Work, voluntarily
++elects to apply CC0 to the Work and publicly distribute the Work under its
++terms, with knowledge of his or her Copyright and Related Rights in the
++Work and the meaning and intended legal effect of CC0 on those rights.
++
++1. Copyright and Related Rights. A Work made available under CC0 may be
++protected by copyright and related or neighboring rights ("Copyright and
++Related Rights"). Copyright and Related Rights include, but are not
++limited to, the following:
++
++ i. the right to reproduce, adapt, distribute, perform, display,
++ communicate, and translate a Work;
++ ii. moral rights retained by the original author(s) and/or performer(s);
++iii. publicity and privacy rights pertaining to a person's image or
++ likeness depicted in a Work;
++ iv. rights protecting against unfair competition in regards to a Work,
++ subject to the limitations in paragraph 4(a), below;
++ v. rights protecting the extraction, dissemination, use and reuse of data
++ in a Work;
++ vi. database rights (such as those arising under Directive 96/9/EC of the
++ European Parliament and of the Council of 11 March 1996 on the legal
++ protection of databases, and under any national implementation
++ thereof, including any amended or successor version of such
++ directive); and
++vii. other similar, equivalent or corresponding rights throughout the
++ world based on applicable law or treaty, and any national
++ implementations thereof.
++
++2. Waiver. To the greatest extent permitted by, but not in contravention
++of, applicable law, Affirmer hereby overtly, fully, permanently,
++irrevocably and unconditionally waives, abandons, and surrenders all of
++Affirmer's Copyright and Related Rights and associated claims and causes
++of action, whether now known or unknown (including existing as well as
++future claims and causes of action), in the Work (i) in all territories
++worldwide, (ii) for the maximum duration provided by applicable law or
++treaty (including future time extensions), (iii) in any current or future
++medium and for any number of copies, and (iv) for any purpose whatsoever,
++including without limitation commercial, advertising or promotional
++purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
++member of the public at large and to the detriment of Affirmer's heirs and
++successors, fully intending that such Waiver shall not be subject to
++revocation, rescission, cancellation, termination, or any other legal or
++equitable action to disrupt the quiet enjoyment of the Work by the public
++as contemplated by Affirmer's express Statement of Purpose.
++
++3. Public License Fallback. Should any part of the Waiver for any reason
++be judged legally invalid or ineffective under applicable law, then the
++Waiver shall be preserved to the maximum extent permitted taking into
++account Affirmer's express Statement of Purpose. In addition, to the
++extent the Waiver is so judged Affirmer hereby grants to each affected
++person a royalty-free, non transferable, non sublicensable, non exclusive,
++irrevocable and unconditional license to exercise Affirmer's Copyright and
++Related Rights in the Work (i) in all territories worldwide, (ii) for the
++maximum duration provided by applicable law or treaty (including future
++time extensions), (iii) in any current or future medium and for any number
++of copies, and (iv) for any purpose whatsoever, including without
++limitation commercial, advertising or promotional purposes (the
++"License"). The License shall be deemed effective as of the date CC0 was
++applied by Affirmer to the Work. Should any part of the License for any
++reason be judged legally invalid or ineffective under applicable law, such
++partial invalidity or ineffectiveness shall not invalidate the remainder
++of the License, and in such case Affirmer hereby affirms that he or she
++will not (i) exercise any of his or her remaining Copyright and Related
++Rights in the Work or (ii) assert any associated claims and causes of
++action with respect to the Work, in either case contrary to Affirmer's
++express Statement of Purpose.
++
++4. Limitations and Disclaimers.
++
++ a. No trademark or patent rights held by Affirmer are waived, abandoned,
++ surrendered, licensed or otherwise affected by this document.
++ b. Affirmer offers the Work as-is and makes no representations or
++ warranties of any kind concerning the Work, express, implied,
++ statutory or otherwise, including without limitation warranties of
++ title, merchantability, fitness for a particular purpose, non
++ infringement, or the absence of latent or other defects, accuracy, or
++ the present or absence of errors, whether or not discoverable, all to
++ the greatest extent permissible under applicable law.
++ c. Affirmer disclaims responsibility for clearing rights of other persons
++ that may apply to the Work or any use thereof, including without
++ limitation any person's Copyright and Related Rights in the Work.
++ Further, Affirmer disclaims responsibility for obtaining any necessary
++ consents, permissions or other rights required for any use of the
++ Work.
++ d. Affirmer understands and acknowledges that Creative Commons is not a
++ party to this document and has no duty or obligation with respect to
++ this CC0 or use of the Work.
++
++--------------------------------------------------------------------------------
++
++ Apache License
++ Version 2.0, January 2004
++ http://www.apache.org/licenses/
++
++ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++ 1. Definitions.
++
++ "License" shall mean the terms and conditions for use, reproduction,
++ and distribution as defined by Sections 1 through 9 of this document.
++
++ "Licensor" shall mean the copyright owner or entity authorized by
++ the copyright owner that is granting the License.
++
++ "Legal Entity" shall mean the union of the acting entity and all
++ other entities that control, are controlled by, or are under common
++ control with that entity. For the purposes of this definition,
++ "control" means (i) the power, direct or indirect, to cause the
++ direction or management of such entity, whether by contract or
++ otherwise, or (ii) ownership of fifty percent (50%) or more of the
++ outstanding shares, or (iii) beneficial ownership of such entity.
++
++ "You" (or "Your") shall mean an individual or Legal Entity
++ exercising permissions granted by this License.
++
++ "Source" form shall mean the preferred form for making modifications,
++ including but not limited to software source code, documentation
++ source, and configuration files.
++
++ "Object" form shall mean any form resulting from mechanical
++ transformation or translation of a Source form, including but
++ not limited to compiled object code, generated documentation,
++ and conversions to other media types.
++
++ "Work" shall mean the work of authorship, whether in Source or
++ Object form, made available under the License, as indicated by a
++ copyright notice that is included in or attached to the work
++ (an example is provided in the Appendix below).
++
++ "Derivative Works" shall mean any work, whether in Source or Object
++ form, that is based on (or derived from) the Work and for which the
++ editorial revisions, annotations, elaborations, or other modifications
++ represent, as a whole, an original work of authorship. For the purposes
++ of this License, Derivative Works shall not include works that remain
++ separable from, or merely link (or bind by name) to the interfaces of,
++ the Work and Derivative Works thereof.
++
++ "Contribution" shall mean any work of authorship, including
++ the original version of the Work and any modifications or additions
++ to that Work or Derivative Works thereof, that is intentionally
++ submitted to Licensor for inclusion in the Work by the copyright owner
++ or by an individual or Legal Entity authorized to submit on behalf of
++ the copyright owner. For the purposes of this definition, "submitted"
++ means any form of electronic, verbal, or written communication sent
++ to the Licensor or its representatives, including but not limited to
++ communication on electronic mailing lists, source code control systems,
++ and issue tracking systems that are managed by, or on behalf of, the
++ Licensor for the purpose of discussing and improving the Work, but
++ excluding communication that is conspicuously marked or otherwise
++ designated in writing by the copyright owner as "Not a Contribution."
++
++ "Contributor" shall mean Licensor and any individual or Legal Entity
++ on behalf of whom a Contribution has been received by Licensor and
++ subsequently incorporated within the Work.
++
++ 2. Grant of Copyright License. Subject to the terms and conditions of
++ this License, each Contributor hereby grants to You a perpetual,
++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++ copyright license to reproduce, prepare Derivative Works of,
++ publicly display, publicly perform, sublicense, and distribute the
++ Work and such Derivative Works in Source or Object form.
++
++ 3. Grant of Patent License. Subject to the terms and conditions of
++ this License, each Contributor hereby grants to You a perpetual,
++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++ (except as stated in this section) patent license to make, have made,
++ use, offer to sell, sell, import, and otherwise transfer the Work,
++ where such license applies only to those patent claims licensable
++ by such Contributor that are necessarily infringed by their
++ Contribution(s) alone or by combination of their Contribution(s)
++ with the Work to which such Contribution(s) was submitted. If You
++ institute patent litigation against any entity (including a
++ cross-claim or counterclaim in a lawsuit) alleging that the Work
++ or a Contribution incorporated within the Work constitutes direct
++ or contributory patent infringement, then any patent licenses
++ granted to You under this License for that Work shall terminate
++ as of the date such litigation is filed.
++
++ 4. Redistribution. You may reproduce and distribute copies of the
++ Work or Derivative Works thereof in any medium, with or without
++ modifications, and in Source or Object form, provided that You
++ meet the following conditions:
++
++ (a) You must give any other recipients of the Work or
++ Derivative Works a copy of this License; and
++
++ (b) You must cause any modified files to carry prominent notices
++ stating that You changed the files; and
++
++ (c) You must retain, in the Source form of any Derivative Works
++ that You distribute, all copyright, patent, trademark, and
++ attribution notices from the Source form of the Work,
++ excluding those notices that do not pertain to any part of
++ the Derivative Works; and
++
++ (d) If the Work includes a "NOTICE" text file as part of its
++ distribution, then any Derivative Works that You distribute must
++ include a readable copy of the attribution notices contained
++ within such NOTICE file, excluding those notices that do not
++ pertain to any part of the Derivative Works, in at least one
++ of the following places: within a NOTICE text file distributed
++ as part of the Derivative Works; within the Source form or
++ documentation, if provided along with the Derivative Works; or,
++ within a display generated by the Derivative Works, if and
++ wherever such third-party notices normally appear. The contents
++ of the NOTICE file are for informational purposes only and
++ do not modify the License. You may add Your own attribution
++ notices within Derivative Works that You distribute, alongside
++ or as an addendum to the NOTICE text from the Work, provided
++ that such additional attribution notices cannot be construed
++ as modifying the License.
++
++ You may add Your own copyright statement to Your modifications and
++ may provide additional or different license terms and conditions
++ for use, reproduction, or distribution of Your modifications, or
++ for any such Derivative Works as a whole, provided Your use,
++ reproduction, and distribution of the Work otherwise complies with
++ the conditions stated in this License.
++
++ 5. Submission of Contributions. Unless You explicitly state otherwise,
++ any Contribution intentionally submitted for inclusion in the Work
++ by You to the Licensor shall be under the terms and conditions of
++ this License, without any additional terms or conditions.
++ Notwithstanding the above, nothing herein shall supersede or modify
++ the terms of any separate license agreement you may have executed
++ with Licensor regarding such Contributions.
++
++ 6. Trademarks. This License does not grant permission to use the trade
++ names, trademarks, service marks, or product names of the Licensor,
++ except as required for reasonable and customary use in describing the
++ origin of the Work and reproducing the content of the NOTICE file.
++
++ 7. Disclaimer of Warranty. Unless required by applicable law or
++ agreed to in writing, Licensor provides the Work (and each
++ Contributor provides its Contributions) on an "AS IS" BASIS,
++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++ implied, including, without limitation, any warranties or conditions
++ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++ PARTICULAR PURPOSE. You are solely responsible for determining the
++ appropriateness of using or redistributing the Work and assume any
++ risks associated with Your exercise of permissions under this License.
++
++ 8. Limitation of Liability. In no event and under no legal theory,
++ whether in tort (including negligence), contract, or otherwise,
++ unless required by applicable law (such as deliberate and grossly
++ negligent acts) or agreed to in writing, shall any Contributor be
++ liable to You for damages, including any direct, indirect, special,
++ incidental, or consequential damages of any character arising as a
++ result of this License or out of the use or inability to use the
++ Work (including but not limited to damages for loss of goodwill,
++ work stoppage, computer failure or malfunction, or any and all
++ other commercial damages or losses), even if such Contributor
++ has been advised of the possibility of such damages.
++
++ 9. Accepting Warranty or Additional Liability. While redistributing
++ the Work or Derivative Works thereof, You may choose to offer,
++ and charge a fee for, acceptance of support, warranty, indemnity,
++ or other liability obligations and/or rights consistent with this
++ License. However, in accepting such obligations, You may act only
++ on Your own behalf and on Your sole responsibility, not on behalf
++ of any other Contributor, and only if You agree to indemnify,
++ defend, and hold each Contributor harmless for any liability
++ incurred by, or claims asserted against, such Contributor by reason
++ of your accepting any such warranty or additional liability.
+diff --git a/grub-core/lib/argon2/argon2.c b/grub-core/lib/argon2/argon2.c
+new file mode 100644
+index 000000000..49532fe80
+--- /dev/null
++++ b/grub-core/lib/argon2/argon2.c
+@@ -0,0 +1,232 @@
++/*
++ * Argon2 reference source code package - reference C implementations
++ *
++ * Copyright 2015
++ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
++ *
++ * You may use this work under the terms of a Creative Commons CC0 1.0
++ * License/Waiver or the Apache Public License 2.0, at your option. The terms of
++ * these licenses can be found at:
++ *
++ * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
++ * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * You should have received a copy of both of these licenses along with this
++ * software. If not, they may be obtained at the above URLs.
++ */
++
++#include <grub/dl.h>
++
++#include "argon2.h"
++#include "core.h"
++
++GRUB_MOD_LICENSE ("CC0");
++
++static int argon2_ctx(argon2_context *context, argon2_type type) {
++ /* 1. Validate all inputs */
++ int result = validate_inputs(context);
++ grub_uint32_t memory_blocks, segment_length;
++ argon2_instance_t instance;
++
++ if (ARGON2_OK != result) {
++ return result;
++ }
++
++ if (Argon2_d != type && Argon2_i != type && Argon2_id != type) {
++ return ARGON2_INCORRECT_TYPE;
++ }
++
++ /* 2. Align memory size */
++ /* Minimum memory_blocks = 8L blocks, where L is the number of lanes */
++ memory_blocks = context->m_cost;
++
++ if (memory_blocks < 2 * ARGON2_SYNC_POINTS * context->lanes) {
++ memory_blocks = 2 * ARGON2_SYNC_POINTS * context->lanes;
++ }
++
++ segment_length = memory_blocks / (context->lanes * ARGON2_SYNC_POINTS);
++ /* Ensure that all segments have equal length */
++ memory_blocks = segment_length * (context->lanes * ARGON2_SYNC_POINTS);
++
++ instance.version = context->version;
++ instance.memory = NULL;
++ instance.passes = context->t_cost;
++ instance.memory_blocks = memory_blocks;
++ instance.segment_length = segment_length;
++ instance.lane_length = segment_length * ARGON2_SYNC_POINTS;
++ instance.lanes = context->lanes;
++ instance.threads = context->threads;
++ instance.type = type;
++
++ if (instance.threads > instance.lanes) {
++ instance.threads = instance.lanes;
++ }
++
++ /* 3. Initialization: Hashing inputs, allocating memory, filling first
++ * blocks
++ */
++ result = initialize(&instance, context);
++
++ if (ARGON2_OK != result) {
++ return result;
++ }
++
++ /* 4. Filling memory */
++ result = fill_memory_blocks(&instance);
++
++ if (ARGON2_OK != result) {
++ return result;
++ }
++ /* 5. Finalization */
++ finalize(context, &instance);
++
++ return ARGON2_OK;
++}
++
++int argon2_hash(const grub_uint32_t t_cost, const grub_uint32_t m_cost,
++ const grub_uint32_t parallelism, const void *pwd,
++ const grub_size_t pwdlen, const void *salt, const grub_size_t saltlen,
++ void *hash, const grub_size_t hashlen, argon2_type type,
++ const grub_uint32_t version){
++
++ argon2_context context;
++ int result;
++ grub_uint8_t *out;
++
++ if (pwdlen > ARGON2_MAX_PWD_LENGTH) {
++ return ARGON2_PWD_TOO_LONG;
++ }
++
++ if (saltlen > ARGON2_MAX_SALT_LENGTH) {
++ return ARGON2_SALT_TOO_LONG;
++ }
++
++ if (hashlen > ARGON2_MAX_OUTLEN) {
++ return ARGON2_OUTPUT_TOO_LONG;
++ }
++
++ if (hashlen < ARGON2_MIN_OUTLEN) {
++ return ARGON2_OUTPUT_TOO_SHORT;
++ }
++
++ out = grub_malloc(hashlen);
++ if (!out) {
++ return ARGON2_MEMORY_ALLOCATION_ERROR;
++ }
++
++ context.out = (grub_uint8_t *)out;
++ context.outlen = (grub_uint32_t)hashlen;
++ context.pwd = CONST_CAST(grub_uint8_t *)pwd;
++ context.pwdlen = (grub_uint32_t)pwdlen;
++ context.salt = CONST_CAST(grub_uint8_t *)salt;
++ context.saltlen = (grub_uint32_t)saltlen;
++ context.secret = NULL;
++ context.secretlen = 0;
++ context.ad = NULL;
++ context.adlen = 0;
++ context.t_cost = t_cost;
++ context.m_cost = m_cost;
++ context.lanes = parallelism;
++ context.threads = parallelism;
++ context.allocate_cbk = NULL;
++ context.grub_free_cbk = NULL;
++ context.flags = ARGON2_DEFAULT_FLAGS;
++ context.version = version;
++
++ result = argon2_ctx(&context, type);
++
++ if (result != ARGON2_OK) {
++ clear_internal_memory(out, hashlen);
++ grub_free(out);
++ return result;
++ }
++
++ /* if raw hash requested, write it */
++ if (hash) {
++ grub_memcpy(hash, out, hashlen);
++ }
++
++ clear_internal_memory(out, hashlen);
++ grub_free(out);
++
++ return ARGON2_OK;
++}
++
++const char *argon2_error_message(int error_code) {
++ switch (error_code) {
++ case ARGON2_OK:
++ return "OK";
++ case ARGON2_OUTPUT_PTR_NULL:
++ return "Output pointer is NULL";
++ case ARGON2_OUTPUT_TOO_SHORT:
++ return "Output is too short";
++ case ARGON2_OUTPUT_TOO_LONG:
++ return "Output is too long";
++ case ARGON2_PWD_TOO_SHORT:
++ return "Password is too short";
++ case ARGON2_PWD_TOO_LONG:
++ return "Password is too long";
++ case ARGON2_SALT_TOO_SHORT:
++ return "Salt is too short";
++ case ARGON2_SALT_TOO_LONG:
++ return "Salt is too long";
++ case ARGON2_AD_TOO_SHORT:
++ return "Associated data is too short";
++ case ARGON2_AD_TOO_LONG:
++ return "Associated data is too long";
++ case ARGON2_SECRET_TOO_SHORT:
++ return "Secret is too short";
++ case ARGON2_SECRET_TOO_LONG:
++ return "Secret is too long";
++ case ARGON2_TIME_TOO_SMALL:
++ return "Time cost is too small";
++ case ARGON2_TIME_TOO_LARGE:
++ return "Time cost is too large";
++ case ARGON2_MEMORY_TOO_LITTLE:
++ return "Memory cost is too small";
++ case ARGON2_MEMORY_TOO_MUCH:
++ return "Memory cost is too large";
++ case ARGON2_LANES_TOO_FEW:
++ return "Too few lanes";
++ case ARGON2_LANES_TOO_MANY:
++ return "Too many lanes";
++ case ARGON2_PWD_PTR_MISMATCH:
++ return "Password pointer is NULL, but password length is not 0";
++ case ARGON2_SALT_PTR_MISMATCH:
++ return "Salt pointer is NULL, but salt length is not 0";
++ case ARGON2_SECRET_PTR_MISMATCH:
++ return "Secret pointer is NULL, but secret length is not 0";
++ case ARGON2_AD_PTR_MISMATCH:
++ return "Associated data pointer is NULL, but ad length is not 0";
++ case ARGON2_MEMORY_ALLOCATION_ERROR:
++ return "Memory allocation error";
++ case ARGON2_FREE_MEMORY_CBK_NULL:
++ return "The grub_free memory callback is NULL";
++ case ARGON2_ALLOCATE_MEMORY_CBK_NULL:
++ return "The allocate memory callback is NULL";
++ case ARGON2_INCORRECT_PARAMETER:
++ return "Argon2_Context context is NULL";
++ case ARGON2_INCORRECT_TYPE:
++ return "There is no such version of Argon2";
++ case ARGON2_OUT_PTR_MISMATCH:
++ return "Output pointer mismatch";
++ case ARGON2_THREADS_TOO_FEW:
++ return "Not enough threads";
++ case ARGON2_THREADS_TOO_MANY:
++ return "Too many threads";
++ case ARGON2_MISSING_ARGS:
++ return "Missing arguments";
++ case ARGON2_ENCODING_FAIL:
++ return "Encoding failed";
++ case ARGON2_DECODING_FAIL:
++ return "Decoding failed";
++ case ARGON2_THREAD_FAIL:
++ return "Threading failure";
++ case ARGON2_DECODING_LENGTH_FAIL:
++ return "Some of encoded parameters are too long or too short";
++ case ARGON2_VERIFY_MISMATCH:
++ return "The password does not match the supplied hash";
++ default:
++ return "Unknown error code";
++ }
++}
+diff --git a/grub-core/lib/argon2/argon2.h b/grub-core/lib/argon2/argon2.h
+new file mode 100644
+index 000000000..129f7efbd
+--- /dev/null
++++ b/grub-core/lib/argon2/argon2.h
+@@ -0,0 +1,264 @@
++/*
++ * Argon2 reference source code package - reference C implementations
++ *
++ * Copyright 2015
++ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
++ *
++ * You may use this work under the terms of a Creative Commons CC0 1.0
++ * License/Waiver or the Apache Public License 2.0, at your option. The terms of
++ * these licenses can be found at:
++ *
++ * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
++ * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * You should have received a copy of both of these licenses along with this
++ * software. If not, they may be obtained at the above URLs.
++ */
++
++#ifndef ARGON2_H
++#define ARGON2_H
++
++#include <grub/misc.h>
++#include <grub/mm.h>
++
++#if defined(__cplusplus)
++extern "C" {
++#endif
++
++/* Symbols visibility control */
++#ifdef A2_VISCTL
++#define ARGON2_PUBLIC __attribute__((visibility("default")))
++#define ARGON2_LOCAL __attribute__ ((visibility ("hidden")))
++#elif defined(_MSC_VER)
++#define ARGON2_PUBLIC __declspec(dllexport)
++#define ARGON2_LOCAL
++#else
++#define ARGON2_PUBLIC
++#define ARGON2_LOCAL
++#endif
++
++/*
++ * Argon2 input parameter restrictions
++ */
++
++/* Minimum and maximum number of lanes (degree of parallelism) */
++#define ARGON2_MIN_LANES GRUB_UINT32_C(1)
++#define ARGON2_MAX_LANES GRUB_UINT32_C(0xFFFFFF)
++
++/* Minimum and maximum number of threads */
++#define ARGON2_MIN_THREADS GRUB_UINT32_C(1)
++#define ARGON2_MAX_THREADS GRUB_UINT32_C(0xFFFFFF)
++
++/* Number of synchronization points between lanes per pass */
++#define ARGON2_SYNC_POINTS GRUB_UINT32_C(4)
++
++/* Minimum and maximum digest size in bytes */
++#define ARGON2_MIN_OUTLEN GRUB_UINT32_C(4)
++#define ARGON2_MAX_OUTLEN GRUB_UINT32_C(0xFFFFFFFF)
++
++/* Minimum and maximum number of memory blocks (each of BLOCK_SIZE bytes) */
++#define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS) /* 2 blocks per slice */
++
++#define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b))
++/* Max memory size is addressing-space/2, topping at 2^32 blocks (4 TB) */
++#define ARGON2_MAX_MEMORY_BITS \
++ ARGON2_MIN(GRUB_UINT32_C(32), (sizeof(void *) * GRUB_CHAR_BIT - 10 - 1))
++#define ARGON2_MAX_MEMORY \
++ ARGON2_MIN(GRUB_UINT32_C(0xFFFFFFFF), GRUB_UINT64_C(1) << ARGON2_MAX_MEMORY_BITS)
++
++/* Minimum and maximum number of passes */
++#define ARGON2_MIN_TIME GRUB_UINT32_C(1)
++#define ARGON2_MAX_TIME GRUB_UINT32_C(0xFFFFFFFF)
++
++/* Minimum and maximum password length in bytes */
++#define ARGON2_MIN_PWD_LENGTH GRUB_UINT32_C(0)
++#define ARGON2_MAX_PWD_LENGTH GRUB_UINT32_C(0xFFFFFFFF)
++
++/* Minimum and maximum associated data length in bytes */
++#define ARGON2_MIN_AD_LENGTH GRUB_UINT32_C(0)
++#define ARGON2_MAX_AD_LENGTH GRUB_UINT32_C(0xFFFFFFFF)
++
++/* Minimum and maximum salt length in bytes */
++#define ARGON2_MIN_SALT_LENGTH GRUB_UINT32_C(8)
++#define ARGON2_MAX_SALT_LENGTH GRUB_UINT32_C(0xFFFFFFFF)
++
++/* Minimum and maximum key length in bytes */
++#define ARGON2_MIN_SECRET GRUB_UINT32_C(0)
++#define ARGON2_MAX_SECRET GRUB_UINT32_C(0xFFFFFFFF)
++
++/* Flags to determine which fields are securely wiped (default = no wipe). */
++#define ARGON2_DEFAULT_FLAGS GRUB_UINT32_C(0)
++#define ARGON2_FLAG_CLEAR_PASSWORD (GRUB_UINT32_C(1) << 0)
++#define ARGON2_FLAG_CLEAR_SECRET (GRUB_UINT32_C(1) << 1)
++
++/* Global flag to determine if we are wiping internal memory buffers. This flag
++ * is defined in core.c and defaults to 1 (wipe internal memory). */
++extern int FLAG_clear_internal_memory;
++
++/* Error codes */
++typedef enum Argon2_ErrorCodes {
++ ARGON2_OK = 0,
++
++ ARGON2_OUTPUT_PTR_NULL = -1,
++
++ ARGON2_OUTPUT_TOO_SHORT = -2,
++ ARGON2_OUTPUT_TOO_LONG = -3,
++
++ ARGON2_PWD_TOO_SHORT = -4,
++ ARGON2_PWD_TOO_LONG = -5,
++
++ ARGON2_SALT_TOO_SHORT = -6,
++ ARGON2_SALT_TOO_LONG = -7,
++
++ ARGON2_AD_TOO_SHORT = -8,
++ ARGON2_AD_TOO_LONG = -9,
++
++ ARGON2_SECRET_TOO_SHORT = -10,
++ ARGON2_SECRET_TOO_LONG = -11,
++
++ ARGON2_TIME_TOO_SMALL = -12,
++ ARGON2_TIME_TOO_LARGE = -13,
++
++ ARGON2_MEMORY_TOO_LITTLE = -14,
++ ARGON2_MEMORY_TOO_MUCH = -15,
++
++ ARGON2_LANES_TOO_FEW = -16,
++ ARGON2_LANES_TOO_MANY = -17,
++
++ ARGON2_PWD_PTR_MISMATCH = -18, /* NULL ptr with non-zero length */
++ ARGON2_SALT_PTR_MISMATCH = -19, /* NULL ptr with non-zero length */
++ ARGON2_SECRET_PTR_MISMATCH = -20, /* NULL ptr with non-zero length */
++ ARGON2_AD_PTR_MISMATCH = -21, /* NULL ptr with non-zero length */
++
++ ARGON2_MEMORY_ALLOCATION_ERROR = -22,
++
++ ARGON2_FREE_MEMORY_CBK_NULL = -23,
++ ARGON2_ALLOCATE_MEMORY_CBK_NULL = -24,
++
++ ARGON2_INCORRECT_PARAMETER = -25,
++ ARGON2_INCORRECT_TYPE = -26,
++
++ ARGON2_OUT_PTR_MISMATCH = -27,
++
++ ARGON2_THREADS_TOO_FEW = -28,
++ ARGON2_THREADS_TOO_MANY = -29,
++
++ ARGON2_MISSING_ARGS = -30,
++
++ ARGON2_ENCODING_FAIL = -31,
++
++ ARGON2_DECODING_FAIL = -32,
++
++ ARGON2_THREAD_FAIL = -33,
++
++ ARGON2_DECODING_LENGTH_FAIL = -34,
++
++ ARGON2_VERIFY_MISMATCH = -35
++} argon2_error_codes;
++
++/* Memory allocator types --- for external allocation */
++typedef int (*allocate_fptr)(grub_uint8_t **memory, grub_size_t bytes_to_allocate);
++typedef void (*deallocate_fptr)(grub_uint8_t *memory, grub_size_t bytes_to_allocate);
++
++/* Argon2 external data structures */
++
++/*
++ *****
++ * Context: structure to hold Argon2 inputs:
++ * output array and its length,
++ * password and its length,
++ * salt and its length,
++ * secret and its length,
++ * associated data and its length,
++ * number of passes, amount of used memory (in KBytes, can be rounded up a bit)
++ * number of parallel threads that will be run.
++ * All the parameters above affect the output hash value.
++ * Additionally, two function pointers can be provided to allocate and
++ * deallocate the memory (if NULL, memory will be allocated internally).
++ * Also, three flags indicate whether to erase password, secret as soon as they
++ * are pre-hashed (and thus not needed anymore), and the entire memory
++ *****
++ * Simplest situation: you have output array out[8], password is stored in
++ * pwd[32], salt is stored in salt[16], you do not have keys nor associated
++ * data. You need to spend 1 GB of RAM and you run 5 passes of Argon2d with
++ * 4 parallel lanes.
++ * You want to erase the password, but you're OK with last pass not being
++ * erased. You want to use the default memory allocator.
++ * Then you initialize:
++ Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false)
++ */
++typedef struct Argon2_Context {
++ grub_uint8_t *out; /* output array */
++ grub_uint32_t outlen; /* digest length */
++
++ grub_uint8_t *pwd; /* password array */
++ grub_uint32_t pwdlen; /* password length */
++
++ grub_uint8_t *salt; /* salt array */
++ grub_uint32_t saltlen; /* salt length */
++
++ grub_uint8_t *secret; /* key array */
++ grub_uint32_t secretlen; /* key length */
++
++ grub_uint8_t *ad; /* associated data array */
++ grub_uint32_t adlen; /* associated data length */
++
++ grub_uint32_t t_cost; /* number of passes */
++ grub_uint32_t m_cost; /* amount of memory requested (KB) */
++ grub_uint32_t lanes; /* number of lanes */
++ grub_uint32_t threads; /* maximum number of threads */
++
++ grub_uint32_t version; /* version number */
++
++ allocate_fptr allocate_cbk; /* pointer to memory allocator */
++ deallocate_fptr grub_free_cbk; /* pointer to memory deallocator */
++
++ grub_uint32_t flags; /* array of bool options */
++} argon2_context;
++
++/* Argon2 primitive type */
++typedef enum Argon2_type {
++ Argon2_d = 0,
++ Argon2_i = 1,
++ Argon2_id = 2
++} argon2_type;
++
++/* Version of the algorithm */
++typedef enum Argon2_version {
++ ARGON2_VERSION_10 = 0x10,
++ ARGON2_VERSION_13 = 0x13,
++ ARGON2_VERSION_NUMBER = ARGON2_VERSION_13
++} argon2_version;
++
++/**
++ * Hashes a password with Argon2, producing a raw hash at @hash
++ * @param t_cost Number of iterations
++ * @param m_cost Sets memory usage to m_cost kibibytes
++ * @param parallelism Number of threads and compute lanes
++ * @param pwd Pointer to password
++ * @param pwdlen Password size in bytes
++ * @param salt Pointer to salt
++ * @param saltlen Salt size in bytes
++ * @param hash Buffer where to write the raw hash - updated by the function
++ * @param hashlen Desired length of the hash in bytes
++ * @pre Different parallelism levels will give different results
++ * @pre Returns ARGON2_OK if successful
++ */
++ARGON2_PUBLIC int argon2_hash(const grub_uint32_t t_cost, const grub_uint32_t m_cost,
++ const grub_uint32_t parallelism, const void *pwd,
++ const grub_size_t pwdlen, const void *salt,
++ const grub_size_t saltlen, void *hash,
++ const grub_size_t hashlen, argon2_type type,
++ const grub_uint32_t version);
++
++/**
++ * Get the associated error message for given error code
++ * @return The error message associated with the given error code
++ */
++ARGON2_PUBLIC const char *argon2_error_message(int error_code);
++
++#if defined(__cplusplus)
++}
++#endif
++
++#endif
+diff --git a/grub-core/lib/argon2/blake2/blake2-impl.h b/grub-core/lib/argon2/blake2/blake2-impl.h
+new file mode 100644
+index 000000000..3a795680b
+--- /dev/null
++++ b/grub-core/lib/argon2/blake2/blake2-impl.h
+@@ -0,0 +1,151 @@
++/*
++ * Argon2 reference source code package - reference C implementations
++ *
++ * Copyright 2015
++ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
++ *
++ * You may use this work under the terms of a Creative Commons CC0 1.0
++ * License/Waiver or the Apache Public License 2.0, at your option. The terms of
++ * these licenses can be found at:
++ *
++ * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
++ * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * You should have received a copy of both of these licenses along with this
++ * software. If not, they may be obtained at the above URLs.
++ */
++
++#ifndef PORTABLE_BLAKE2_IMPL_H
++#define PORTABLE_BLAKE2_IMPL_H
++
++#if defined(_MSC_VER)
++#define BLAKE2_INLINE __inline
++#elif defined(__GNUC__) || defined(__clang__)
++#define BLAKE2_INLINE __inline__
++#else
++#define BLAKE2_INLINE
++#endif
++
++/* Argon2 Team - Begin Code */
++/*
++ Not an exhaustive list, but should cover the majority of modern platforms
++ Additionally, the code will always be correct---this is only a performance
++ tweak.
++*/
++#if (defined(__BYTE_ORDER__) && \
++ (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \
++ defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \
++ defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \
++ defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \
++ defined(_M_ARM)
++#define NATIVE_LITTLE_ENDIAN
++#endif
++/* Argon2 Team - End Code */
++
++static BLAKE2_INLINE grub_uint32_t load32(const void *src) {
++#if defined(NATIVE_LITTLE_ENDIAN)
++ grub_uint32_t w;
++ grub_memcpy(&w, src, sizeof w);
++ return w;
++#else
++ const grub_uint8_t *p = (const grub_uint8_t *)src;
++ grub_uint32_t w = *p++;
++ w |= (grub_uint32_t)(*p++) << 8;
++ w |= (grub_uint32_t)(*p++) << 16;
++ w |= (grub_uint32_t)(*p++) << 24;
++ return w;
++#endif
++}
++
++static BLAKE2_INLINE grub_uint64_t load64(const void *src) {
++#if defined(NATIVE_LITTLE_ENDIAN)
++ grub_uint64_t w;
++ grub_memcpy(&w, src, sizeof w);
++ return w;
++#else
++ const grub_uint8_t *p = (const grub_uint8_t *)src;
++ grub_uint64_t w = *p++;
++ w |= (grub_uint64_t)(*p++) << 8;
++ w |= (grub_uint64_t)(*p++) << 16;
++ w |= (grub_uint64_t)(*p++) << 24;
++ w |= (grub_uint64_t)(*p++) << 32;
++ w |= (grub_uint64_t)(*p++) << 40;
++ w |= (grub_uint64_t)(*p++) << 48;
++ w |= (grub_uint64_t)(*p++) << 56;
++ return w;
++#endif
++}
++
++static BLAKE2_INLINE void store32(void *dst, grub_uint32_t w) {
++#if defined(NATIVE_LITTLE_ENDIAN)
++ grub_memcpy(dst, &w, sizeof w);
++#else
++ grub_uint8_t *p = (grub_uint8_t *)dst;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++#endif
++}
++
++static BLAKE2_INLINE void store64(void *dst, grub_uint64_t w) {
++#if defined(NATIVE_LITTLE_ENDIAN)
++ grub_memcpy(dst, &w, sizeof w);
++#else
++ grub_uint8_t *p = (grub_uint8_t *)dst;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++#endif
++}
++
++static BLAKE2_INLINE grub_uint64_t load48(const void *src) {
++ const grub_uint8_t *p = (const grub_uint8_t *)src;
++ grub_uint64_t w = *p++;
++ w |= (grub_uint64_t)(*p++) << 8;
++ w |= (grub_uint64_t)(*p++) << 16;
++ w |= (grub_uint64_t)(*p++) << 24;
++ w |= (grub_uint64_t)(*p++) << 32;
++ w |= (grub_uint64_t)(*p++) << 40;
++ return w;
++}
++
++static BLAKE2_INLINE void store48(void *dst, grub_uint64_t w) {
++ grub_uint8_t *p = (grub_uint8_t *)dst;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++ w >>= 8;
++ *p++ = (grub_uint8_t)w;
++}
++
++static BLAKE2_INLINE grub_uint32_t rotr32(const grub_uint32_t w, const unsigned c) {
++ return (w >> c) | (w << (32 - c));
++}
++
++static BLAKE2_INLINE grub_uint64_t rotr64(const grub_uint64_t w, const unsigned c) {
++ return (w >> c) | (w << (64 - c));
++}
++
++#endif
+diff --git a/grub-core/lib/argon2/blake2/blake2.h b/grub-core/lib/argon2/blake2/blake2.h
+new file mode 100644
+index 000000000..4e8efeb22
+--- /dev/null
++++ b/grub-core/lib/argon2/blake2/blake2.h
+@@ -0,0 +1,89 @@
++/*
++ * Argon2 reference source code package - reference C implementations
++ *
++ * Copyright 2015
++ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
++ *
++ * You may use this work under the terms of a Creative Commons CC0 1.0
++ * License/Waiver or the Apache Public License 2.0, at your option. The terms of
++ * these licenses can be found at:
++ *
++ * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
++ * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * You should have received a copy of both of these licenses along with this
++ * software. If not, they may be obtained at the above URLs.
++ */
++
++#ifndef PORTABLE_BLAKE2_H
++#define PORTABLE_BLAKE2_H
++
++#include "../argon2.h"
++
++#if defined(__cplusplus)
++extern "C" {
++#endif
++
++enum blake2b_constant {
++ BLAKE2B_BLOCKBYTES = 128,
++ BLAKE2B_OUTBYTES = 64,
++ BLAKE2B_KEYBYTES = 64,
++ BLAKE2B_SALTBYTES = 16,
++ BLAKE2B_PERSONALBYTES = 16
++};
++
++#pragma pack(push, 1)
++typedef struct __blake2b_param {
++ grub_uint8_t digest_length; /* 1 */
++ grub_uint8_t key_length; /* 2 */
++ grub_uint8_t fanout; /* 3 */
++ grub_uint8_t depth; /* 4 */
++ grub_uint32_t leaf_length; /* 8 */
++ grub_uint64_t node_offset; /* 16 */
++ grub_uint8_t node_depth; /* 17 */
++ grub_uint8_t inner_length; /* 18 */
++ grub_uint8_t reserved[14]; /* 32 */
++ grub_uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */
++ grub_uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */
++} blake2b_param;
++#pragma pack(pop)
++
++typedef struct __blake2b_state {
++ grub_uint64_t h[8];
++ grub_uint64_t t[2];
++ grub_uint64_t f[2];
++ grub_uint8_t buf[BLAKE2B_BLOCKBYTES];
++ unsigned buflen;
++ unsigned outlen;
++ grub_uint8_t last_node;
++} blake2b_state;
++
++/* Ensure param structs have not been wrongly padded */
++/* Poor man's static_assert */
++enum {
++ blake2_size_check_0 = 1 / !!(GRUB_CHAR_BIT == 8),
++ blake2_size_check_2 =
++ 1 / !!(sizeof(blake2b_param) == sizeof(grub_uint64_t) * GRUB_CHAR_BIT)
++};
++
++/* Streaming API */
++ARGON2_LOCAL int blake2b_init(blake2b_state *S, grub_size_t outlen);
++ARGON2_LOCAL int blake2b_init_key(blake2b_state *S, grub_size_t outlen, const void *key,
++ grub_size_t keylen);
++ARGON2_LOCAL int blake2b_init_param(blake2b_state *S, const blake2b_param *P);
++ARGON2_LOCAL int blake2b_update(blake2b_state *S, const void *in, grub_size_t inlen);
++ARGON2_LOCAL int blake2b_final(blake2b_state *S, void *out, grub_size_t outlen);
++
++/* Simple API */
++ARGON2_LOCAL int blake2b(void *out, grub_size_t outlen, const void *in, grub_size_t inlen,
++ const void *key, grub_size_t keylen);
++
++/* Argon2 Team - Begin Code */
++ARGON2_LOCAL int blake2b_long(void *out, grub_size_t outlen, const void *in, grub_size_t inlen);
++/* Argon2 Team - End Code */
++
++#if defined(__cplusplus)
++}
++#endif
++
++#endif
+diff --git a/grub-core/lib/argon2/blake2/blake2b.c b/grub-core/lib/argon2/blake2/blake2b.c
+new file mode 100644
+index 000000000..53abd7bef
+--- /dev/null
++++ b/grub-core/lib/argon2/blake2/blake2b.c
+@@ -0,0 +1,388 @@
++/*
++ * Argon2 reference source code package - reference C implementations
++ *
++ * Copyright 2015
++ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
++ *
++ * You may use this work under the terms of a Creative Commons CC0 1.0
++ * License/Waiver or the Apache Public License 2.0, at your option. The terms of
++ * these licenses can be found at:
++ *
++ * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
++ * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * You should have received a copy of both of these licenses along with this
++ * software. If not, they may be obtained at the above URLs.
++ */
++
++#include "blake2.h"
++#include "blake2-impl.h"
++
++static const grub_uint64_t blake2b_IV[8] = {
++ GRUB_UINT64_C(0x6a09e667f3bcc908), GRUB_UINT64_C(0xbb67ae8584caa73b),
++ GRUB_UINT64_C(0x3c6ef372fe94f82b), GRUB_UINT64_C(0xa54ff53a5f1d36f1),
++ GRUB_UINT64_C(0x510e527fade682d1), GRUB_UINT64_C(0x9b05688c2b3e6c1f),
++ GRUB_UINT64_C(0x1f83d9abfb41bd6b), GRUB_UINT64_C(0x5be0cd19137e2179)};
++
++static const unsigned int blake2b_sigma[12][16] = {
++ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
++ {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
++ {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
++ {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
++ {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
++ {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
++ {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
++ {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
++ {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
++ {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
++ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
++ {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
++};
++
++void clear_internal_memory(void *v, grub_size_t n);
++
++static BLAKE2_INLINE void blake2b_set_lastnode(blake2b_state *S) {
++ S->f[1] = (grub_uint64_t)-1;
++}
++
++static BLAKE2_INLINE void blake2b_set_lastblock(blake2b_state *S) {
++ if (S->last_node) {
++ blake2b_set_lastnode(S);
++ }
++ S->f[0] = (grub_uint64_t)-1;
++}
++
++static BLAKE2_INLINE void blake2b_increment_counter(blake2b_state *S,
++ grub_uint64_t inc) {
++ S->t[0] += inc;
++ S->t[1] += (S->t[0] < inc);
++}
++
++static BLAKE2_INLINE void blake2b_invalidate_state(blake2b_state *S) {
++ clear_internal_memory(S, sizeof(*S)); /* wipe */
++ blake2b_set_lastblock(S); /* invalidate for further use */
++}
++
++static BLAKE2_INLINE void blake2b_init0(blake2b_state *S) {
++ grub_memset(S, 0, sizeof(*S));
++ grub_memcpy(S->h, blake2b_IV, sizeof(S->h));
++}
++
++int blake2b_init_param(blake2b_state *S, const blake2b_param *P) {
++ const unsigned char *p = (const unsigned char *)P;
++ unsigned int i;
++
++ if (NULL == P || NULL == S) {
++ return -1;
++ }
++
++ blake2b_init0(S);
++ /* IV XOR Parameter Block */
++ for (i = 0; i < 8; ++i) {
++ S->h[i] ^= load64(&p[i * sizeof(S->h[i])]);
++ }
++ S->outlen = P->digest_length;
++ return 0;
++}
++
++/* Sequential blake2b initialization */
++int blake2b_init(blake2b_state *S, grub_size_t outlen) {
++ blake2b_param P;
++
++ if (S == NULL) {
++ return -1;
++ }
++
++ if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) {
++ blake2b_invalidate_state(S);
++ return -1;
++ }
++
++ /* Setup Parameter Block for unkeyed BLAKE2 */
++ P.digest_length = (grub_uint8_t)outlen;
++ P.key_length = 0;
++ P.fanout = 1;
++ P.depth = 1;
++ P.leaf_length = 0;
++ P.node_offset = 0;
++ P.node_depth = 0;
++ P.inner_length = 0;
++ grub_memset(P.reserved, 0, sizeof(P.reserved));
++ grub_memset(P.salt, 0, sizeof(P.salt));
++ grub_memset(P.personal, 0, sizeof(P.personal));
++
++ return blake2b_init_param(S, &P);
++}
++
++int blake2b_init_key(blake2b_state *S, grub_size_t outlen, const void *key,
++ grub_size_t keylen) {
++ blake2b_param P;
++
++ if (S == NULL) {
++ return -1;
++ }
++
++ if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) {
++ blake2b_invalidate_state(S);
++ return -1;
++ }
++
++ if ((key == 0) || (keylen == 0) || (keylen > BLAKE2B_KEYBYTES)) {
++ blake2b_invalidate_state(S);
++ return -1;
++ }
++
++ /* Setup Parameter Block for keyed BLAKE2 */
++ P.digest_length = (grub_uint8_t)outlen;
++ P.key_length = (grub_uint8_t)keylen;
++ P.fanout = 1;
++ P.depth = 1;
++ P.leaf_length = 0;
++ P.node_offset = 0;
++ P.node_depth = 0;
++ P.inner_length = 0;
++ grub_memset(P.reserved, 0, sizeof(P.reserved));
++ grub_memset(P.salt, 0, sizeof(P.salt));
++ grub_memset(P.personal, 0, sizeof(P.personal));
++
++ if (blake2b_init_param(S, &P) < 0) {
++ blake2b_invalidate_state(S);
++ return -1;
++ }
++
++ {
++ grub_uint8_t block[BLAKE2B_BLOCKBYTES];
++ grub_memset(block, 0, BLAKE2B_BLOCKBYTES);
++ grub_memcpy(block, key, keylen);
++ blake2b_update(S, block, BLAKE2B_BLOCKBYTES);
++ /* Burn the key from stack */
++ clear_internal_memory(block, BLAKE2B_BLOCKBYTES);
++ }
++ return 0;
++}
++
++static void blake2b_compress(blake2b_state *S, const grub_uint8_t *block) {
++ grub_uint64_t m[16];
++ grub_uint64_t v[16];
++ unsigned int i, r;
++
++ for (i = 0; i < 16; ++i) {
++ m[i] = load64(block + i * sizeof(m[i]));
++ }
++
++ for (i = 0; i < 8; ++i) {
++ v[i] = S->h[i];
++ }
++
++ v[8] = blake2b_IV[0];
++ v[9] = blake2b_IV[1];
++ v[10] = blake2b_IV[2];
++ v[11] = blake2b_IV[3];
++ v[12] = blake2b_IV[4] ^ S->t[0];
++ v[13] = blake2b_IV[5] ^ S->t[1];
++ v[14] = blake2b_IV[6] ^ S->f[0];
++ v[15] = blake2b_IV[7] ^ S->f[1];
++
++#define G(r, i, a, b, c, d) \
++ do { \
++ a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \
++ d = rotr64(d ^ a, 32); \
++ c = c + d; \
++ b = rotr64(b ^ c, 24); \
++ a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \
++ d = rotr64(d ^ a, 16); \
++ c = c + d; \
++ b = rotr64(b ^ c, 63); \
++ } while ((void)0, 0)
++
++#define ROUND(r) \
++ do { \
++ G(r, 0, v[0], v[4], v[8], v[12]); \
++ G(r, 1, v[1], v[5], v[9], v[13]); \
++ G(r, 2, v[2], v[6], v[10], v[14]); \
++ G(r, 3, v[3], v[7], v[11], v[15]); \
++ G(r, 4, v[0], v[5], v[10], v[15]); \
++ G(r, 5, v[1], v[6], v[11], v[12]); \
++ G(r, 6, v[2], v[7], v[8], v[13]); \
++ G(r, 7, v[3], v[4], v[9], v[14]); \
++ } while ((void)0, 0)
++
++ for (r = 0; r < 12; ++r) {
++ ROUND(r);
++ }
++
++ for (i = 0; i < 8; ++i) {
++ S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
++ }
++
++#undef G
++#undef ROUND
++}
++
++int blake2b_update(blake2b_state *S, const void *in, grub_size_t inlen) {
++ const grub_uint8_t *pin = (const grub_uint8_t *)in;
++
++ if (inlen == 0) {
++ return 0;
++ }
++
++ /* Sanity check */
++ if (S == NULL || in == NULL) {
++ return -1;
++ }
++
++ /* Is this a reused state? */
++ if (S->f[0] != 0) {
++ return -1;
++ }
++
++ if (S->buflen + inlen > BLAKE2B_BLOCKBYTES) {
++ /* Complete current block */
++ grub_size_t left = S->buflen;
++ grub_size_t fill = BLAKE2B_BLOCKBYTES - left;
++ grub_memcpy(&S->buf[left], pin, fill);
++ blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
++ blake2b_compress(S, S->buf);
++ S->buflen = 0;
++ inlen -= fill;
++ pin += fill;
++ /* Avoid buffer copies when possible */
++ while (inlen > BLAKE2B_BLOCKBYTES) {
++ blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
++ blake2b_compress(S, pin);
++ inlen -= BLAKE2B_BLOCKBYTES;
++ pin += BLAKE2B_BLOCKBYTES;
++ }
++ }
++ grub_memcpy(&S->buf[S->buflen], pin, inlen);
++ S->buflen += (unsigned int)inlen;
++ return 0;
++}
++
++int blake2b_final(blake2b_state *S, void *out, grub_size_t outlen) {
++ grub_uint8_t buffer[BLAKE2B_OUTBYTES] = {0};
++ unsigned int i;
++
++ /* Sanity checks */
++ if (S == NULL || out == NULL || outlen < S->outlen) {
++ return -1;
++ }
++
++ /* Is this a reused state? */
++ if (S->f[0] != 0) {
++ return -1;
++ }
++
++ blake2b_increment_counter(S, S->buflen);
++ blake2b_set_lastblock(S);
++ grub_memset(&S->buf[S->buflen], 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */
++ blake2b_compress(S, S->buf);
++
++ for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */
++ store64(buffer + sizeof(S->h[i]) * i, S->h[i]);
++ }
++
++ grub_memcpy(out, buffer, S->outlen);
++ clear_internal_memory(buffer, sizeof(buffer));
++ clear_internal_memory(S->buf, sizeof(S->buf));
++ clear_internal_memory(S->h, sizeof(S->h));
++ return 0;
++}
++
++int blake2b(void *out, grub_size_t outlen, const void *in, grub_size_t inlen,
++ const void *key, grub_size_t keylen) {
++ blake2b_state S;
++ int ret = -1;
++
++ /* Verify parameters */
++ if (NULL == in && inlen > 0) {
++ goto fail;
++ }
++
++ if (NULL == out || outlen == 0 || outlen > BLAKE2B_OUTBYTES) {
++ goto fail;
++ }
++
++ if ((NULL == key && keylen > 0) || keylen > BLAKE2B_KEYBYTES) {
++ goto fail;
++ }
++
++ if (keylen > 0) {
++ if (blake2b_init_key(&S, outlen, key, keylen) < 0) {
++ goto fail;
++ }
++ } else {
++ if (blake2b_init(&S, outlen) < 0) {
++ goto fail;
++ }
++ }
++
++ if (blake2b_update(&S, in, inlen) < 0) {
++ goto fail;
++ }
++ ret = blake2b_final(&S, out, outlen);
++
++fail:
++ clear_internal_memory(&S, sizeof(S));
++ return ret;
++}
++
++/* Argon2 Team - Begin Code */
++int blake2b_long(void *pout, grub_size_t outlen, const void *in, grub_size_t inlen) {
++ grub_uint8_t *out = (grub_uint8_t *)pout;
++ blake2b_state blake_state;
++ grub_uint8_t outlen_bytes[sizeof(grub_uint32_t)] = {0};
++ int ret = -1;
++
++ if (outlen > GRUB_UINT32_MAX) {
++ goto fail;
++ }
++
++ /* Ensure little-endian byte order! */
++ store32(outlen_bytes, (grub_uint32_t)outlen);
++
++#define TRY(statement) \
++ do { \
++ ret = statement; \
++ if (ret < 0) { \
++ goto fail; \
++ } \
++ } while ((void)0, 0)
++
++ if (outlen <= BLAKE2B_OUTBYTES) {
++ TRY(blake2b_init(&blake_state, outlen));
++ TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes)));
++ TRY(blake2b_update(&blake_state, in, inlen));
++ TRY(blake2b_final(&blake_state, out, outlen));
++ } else {
++ grub_uint32_t toproduce;
++ grub_uint8_t out_buffer[BLAKE2B_OUTBYTES];
++ grub_uint8_t in_buffer[BLAKE2B_OUTBYTES];
++ TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES));
++ TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes)));
++ TRY(blake2b_update(&blake_state, in, inlen));
++ TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES));
++ grub_memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2);
++ out += BLAKE2B_OUTBYTES / 2;
++ toproduce = (grub_uint32_t)outlen - BLAKE2B_OUTBYTES / 2;
++
++ while (toproduce > BLAKE2B_OUTBYTES) {
++ grub_memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES);
++ TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer,
++ BLAKE2B_OUTBYTES, NULL, 0));
++ grub_memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2);
++ out += BLAKE2B_OUTBYTES / 2;
++ toproduce -= BLAKE2B_OUTBYTES / 2;
++ }
++
++ grub_memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES);
++ TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL,
++ 0));
++ grub_memcpy(out, out_buffer, toproduce);
++ }
++fail:
++ clear_internal_memory(&blake_state, sizeof(blake_state));
++ return ret;
++#undef TRY
++}
++/* Argon2 Team - End Code */
+diff --git a/grub-core/lib/argon2/blake2/blamka-round-ref.h b/grub-core/lib/argon2/blake2/blamka-round-ref.h
+new file mode 100644
+index 000000000..7f0071ada
+--- /dev/null
++++ b/grub-core/lib/argon2/blake2/blamka-round-ref.h
+@@ -0,0 +1,56 @@
++/*
++ * Argon2 reference source code package - reference C implementations
++ *
++ * Copyright 2015
++ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
++ *
++ * You may use this work under the terms of a Creative Commons CC0 1.0
++ * License/Waiver or the Apache Public License 2.0, at your option. The terms of
++ * these licenses can be found at:
++ *
++ * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
++ * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * You should have received a copy of both of these licenses along with this
++ * software. If not, they may be obtained at the above URLs.
++ */
++
++#ifndef BLAKE_ROUND_MKA_H
++#define BLAKE_ROUND_MKA_H
++
++#include "blake2.h"
++#include "blake2-impl.h"
++
++/* designed by the Lyra PHC team */
++static BLAKE2_INLINE grub_uint64_t fBlaMka(grub_uint64_t x, grub_uint64_t y) {
++ const grub_uint64_t m = GRUB_UINT64_C(0xFFFFFFFF);
++ const grub_uint64_t xy = (x & m) * (y & m);
++ return x + y + 2 * xy;
++}
++
++#define G(a, b, c, d) \
++ do { \
++ a = fBlaMka(a, b); \
++ d = rotr64(d ^ a, 32); \
++ c = fBlaMka(c, d); \
++ b = rotr64(b ^ c, 24); \
++ a = fBlaMka(a, b); \
++ d = rotr64(d ^ a, 16); \
++ c = fBlaMka(c, d); \
++ b = rotr64(b ^ c, 63); \
++ } while ((void)0, 0)
++
++#define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \
++ v12, v13, v14, v15) \
++ do { \
++ G(v0, v4, v8, v12); \
++ G(v1, v5, v9, v13); \
++ G(v2, v6, v10, v14); \
++ G(v3, v7, v11, v15); \
++ G(v0, v5, v10, v15); \
++ G(v1, v6, v11, v12); \
++ G(v2, v7, v8, v13); \
++ G(v3, v4, v9, v14); \
++ } while ((void)0, 0)
++
++#endif
+diff --git a/grub-core/lib/argon2/core.c b/grub-core/lib/argon2/core.c
+new file mode 100644
+index 000000000..0fe5b74cb
+--- /dev/null
++++ b/grub-core/lib/argon2/core.c
+@@ -0,0 +1,506 @@
++/*
++ * Argon2 reference source code package - reference C implementations
++ *
++ * Copyright 2015
++ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
++ *
++ * You may use this work under the terms of a Creative Commons CC0 1.0
++ * License/Waiver or the Apache Public License 2.0, at your option. The terms of
++ * these licenses can be found at:
++ *
++ * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
++ * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * You should have received a copy of both of these licenses along with this
++ * software. If not, they may be obtained at the above URLs.
++ */
++
++/*For memory wiping*/
++#ifdef _MSC_VER
++#include <windows.h>
++#include <winbase.h> /* For SecureZeroMemory */
++#endif
++#if defined __STDC_LIB_EXT1__
++#define __STDC_WANT_LIB_EXT1__ 1
++#endif
++#define VC_GE_2005(version) (version >= 1400)
++
++#include "core.h"
++#include "blake2/blake2.h"
++#include "blake2/blake2-impl.h"
++
++#ifdef GENKAT
++#include "genkat.h"
++#endif
++
++#if defined(__clang__)
++#if __has_attribute(optnone)
++#define NOT_OPTIMIZED __attribute__((optnone))
++#endif
++#elif defined(__GNUC__)
++#define GCC_VERSION \
++ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
++#if GCC_VERSION >= 40400
++#define NOT_OPTIMIZED __attribute__((optimize("O0")))
++#endif
++#endif
++#ifndef NOT_OPTIMIZED
++#define NOT_OPTIMIZED
++#endif
++
++/***************Instance and Position constructors**********/
++void init_block_value(block *b, grub_uint8_t in) { grub_memset(b->v, in, sizeof(b->v)); }
++
++void copy_block(block *dst, const block *src) {
++ grub_memcpy(dst->v, src->v, sizeof(grub_uint64_t) * ARGON2_QWORDS_IN_BLOCK);
++}
++
++void xor_block(block *dst, const block *src) {
++ int i;
++ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
++ dst->v[i] ^= src->v[i];
++ }
++}
++
++static void load_block(block *dst, const void *input) {
++ unsigned i;
++ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
++ dst->v[i] = load64((const grub_uint8_t *)input + i * sizeof(dst->v[i]));
++ }
++}
++
++static void store_block(void *output, const block *src) {
++ unsigned i;
++ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
++ store64((grub_uint8_t *)output + i * sizeof(src->v[i]), src->v[i]);
++ }
++}
++
++/***************Memory functions*****************/
++
++int allocate_memory(const argon2_context *context, grub_uint8_t **memory,
++ grub_size_t num, grub_size_t size) {
++ grub_size_t memory_size = num*size;
++ if (memory == NULL) {
++ return ARGON2_MEMORY_ALLOCATION_ERROR;
++ }
++
++ /* 1. Check for multiplication overflow */
++ if (size != 0 && memory_size / size != num) {
++ return ARGON2_MEMORY_ALLOCATION_ERROR;
++ }
++
++ /* 2. Try to allocate with appropriate allocator */
++ if (context->allocate_cbk) {
++ (context->allocate_cbk)(memory, memory_size);
++ } else {
++ *memory = grub_malloc(memory_size);
++ }
++
++ if (*memory == NULL) {
++ return ARGON2_MEMORY_ALLOCATION_ERROR;
++ }
++
++ return ARGON2_OK;
++}
++
++void grub_free_memory(const argon2_context *context, grub_uint8_t *memory,
++ grub_size_t num, grub_size_t size) {
++ grub_size_t memory_size = num*size;
++ clear_internal_memory(memory, memory_size);
++ if (context->grub_free_cbk) {
++ (context->grub_free_cbk)(memory, memory_size);
++ } else {
++ grub_free(memory);
++ }
++}
++
++void NOT_OPTIMIZED secure_wipe_memory(void *v, grub_size_t n) {
++ static void *(*const volatile grub_memset_sec)(void *, int, grub_size_t) = &grub_memset;
++ grub_memset_sec(v, 0, n);
++}
++
++/* Memory clear flag defaults to true. */
++int FLAG_clear_internal_memory = 1;
++void clear_internal_memory(void *v, grub_size_t n) {
++ if (FLAG_clear_internal_memory && v) {
++ secure_wipe_memory(v, n);
++ }
++}
++
++void finalize(const argon2_context *context, argon2_instance_t *instance) {
++ if (context != NULL && instance != NULL) {
++ block blockhash;
++ grub_uint32_t l;
++
++ copy_block(&blockhash, instance->memory + instance->lane_length - 1);
++
++ /* XOR the last blocks */
++ for (l = 1; l < instance->lanes; ++l) {
++ grub_uint32_t last_block_in_lane =
++ l * instance->lane_length + (instance->lane_length - 1);
++ xor_block(&blockhash, instance->memory + last_block_in_lane);
++ }
++
++ /* Hash the result */
++ {
++ grub_uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
++ store_block(blockhash_bytes, &blockhash);
++ blake2b_long(context->out, context->outlen, blockhash_bytes,
++ ARGON2_BLOCK_SIZE);
++ /* clear blockhash and blockhash_bytes */
++ clear_internal_memory(blockhash.v, ARGON2_BLOCK_SIZE);
++ clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE);
++ }
++
++#ifdef GENKAT
++ print_tag(context->out, context->outlen);
++#endif
++
++ grub_free_memory(context, (grub_uint8_t *)instance->memory,
++ instance->memory_blocks, sizeof(block));
++ }
++}
++
++grub_uint32_t index_alpha(const argon2_instance_t *instance,
++ const argon2_position_t *position, grub_uint32_t pseudo_rand,
++ int same_lane) {
++ /*
++ * Pass 0:
++ * This lane : all already finished segments plus already constructed
++ * blocks in this segment
++ * Other lanes : all already finished segments
++ * Pass 1+:
++ * This lane : (SYNC_POINTS - 1) last segments plus already constructed
++ * blocks in this segment
++ * Other lanes : (SYNC_POINTS - 1) last segments
++ */
++ grub_uint32_t reference_area_size;
++ grub_uint64_t relative_position;
++ grub_uint64_t start_position, absolute_position;
++
++ if (0 == position->pass) {
++ /* First pass */
++ if (0 == position->slice) {
++ /* First slice */
++ reference_area_size =
++ position->index - 1; /* all but the previous */
++ } else {
++ if (same_lane) {
++ /* The same lane => add current segment */
++ reference_area_size =
++ position->slice * instance->segment_length +
++ position->index - 1;
++ } else {
++ reference_area_size =
++ position->slice * instance->segment_length +
++ ((position->index == 0) ? (-1) : 0);
++ }
++ }
++ } else {
++ /* Second pass */
++ if (same_lane) {
++ reference_area_size = instance->lane_length -
++ instance->segment_length + position->index -
++ 1;
++ } else {
++ reference_area_size = instance->lane_length -
++ instance->segment_length +
++ ((position->index == 0) ? (-1) : 0);
++ }
++ }
++
++ /* 1.2.4. Mapping pseudo_rand to 0..<reference_area_size-1> and produce
++ * relative position */
++ relative_position = pseudo_rand;
++ relative_position = relative_position * relative_position >> 32;
++ relative_position = reference_area_size - 1 -
++ (reference_area_size * relative_position >> 32);
++
++ /* 1.2.5 Computing starting position */
++ start_position = 0;
++
++ if (0 != position->pass) {
++ start_position = (position->slice == ARGON2_SYNC_POINTS - 1)
++ ? 0
++ : (position->slice + 1) * instance->segment_length;
++ }
++
++ /* 1.2.6. Computing absolute position */
++ grub_divmod64 (start_position + relative_position, instance->lane_length,
++ &absolute_position); /* absolute position */
++ return absolute_position;
++}
++
++/* Single-threaded version for p=1 case */
++static int fill_memory_blocks_st(argon2_instance_t *instance) {
++ grub_uint32_t r, s, l;
++
++ for (r = 0; r < instance->passes; ++r) {
++ for (s = 0; s < ARGON2_SYNC_POINTS; ++s) {
++ for (l = 0; l < instance->lanes; ++l) {
++ argon2_position_t position = {r, l, (grub_uint8_t)s, 0};
++ fill_segment(instance, position);
++ }
++ }
++#ifdef GENKAT
++ internal_kat(instance, r); /* Print all memory blocks */
++#endif
++ }
++ return ARGON2_OK;
++}
++
++int fill_memory_blocks(argon2_instance_t *instance) {
++ if (instance == NULL || instance->lanes == 0) {
++ return ARGON2_INCORRECT_PARAMETER;
++ }
++ return fill_memory_blocks_st(instance);
++}
++
++int validate_inputs(const argon2_context *context) {
++ if (NULL == context) {
++ return ARGON2_INCORRECT_PARAMETER;
++ }
++
++ if (NULL == context->out) {
++ return ARGON2_OUTPUT_PTR_NULL;
++ }
++
++ /* Validate output length */
++ if (ARGON2_MIN_OUTLEN > context->outlen) {
++ return ARGON2_OUTPUT_TOO_SHORT;
++ }
++
++ if (ARGON2_MAX_OUTLEN < context->outlen) {
++ return ARGON2_OUTPUT_TOO_LONG;
++ }
++
++ /* Validate password (required param) */
++ if (NULL == context->pwd) {
++ if (0 != context->pwdlen) {
++ return ARGON2_PWD_PTR_MISMATCH;
++ }
++ }
++
++ if (ARGON2_MAX_PWD_LENGTH < context->pwdlen) {
++ return ARGON2_PWD_TOO_LONG;
++ }
++
++ /* Validate salt (required param) */
++ if (NULL == context->salt) {
++ if (0 != context->saltlen) {
++ return ARGON2_SALT_PTR_MISMATCH;
++ }
++ }
++
++ if (ARGON2_MIN_SALT_LENGTH > context->saltlen) {
++ return ARGON2_SALT_TOO_SHORT;
++ }
++
++ if (ARGON2_MAX_SALT_LENGTH < context->saltlen) {
++ return ARGON2_SALT_TOO_LONG;
++ }
++
++ /* Validate secret (optional param) */
++ if (NULL == context->secret) {
++ if (0 != context->secretlen) {
++ return ARGON2_SECRET_PTR_MISMATCH;
++ }
++ } else {
++ if (ARGON2_MAX_SECRET < context->secretlen) {
++ return ARGON2_SECRET_TOO_LONG;
++ }
++ }
++
++ /* Validate associated data (optional param) */
++ if (NULL == context->ad) {
++ if (0 != context->adlen) {
++ return ARGON2_AD_PTR_MISMATCH;
++ }
++ } else {
++ if (ARGON2_MAX_AD_LENGTH < context->adlen) {
++ return ARGON2_AD_TOO_LONG;
++ }
++ }
++
++ /* Validate memory cost */
++ if (ARGON2_MIN_MEMORY > context->m_cost) {
++ return ARGON2_MEMORY_TOO_LITTLE;
++ }
++
++ if (context->m_cost < 8 * context->lanes) {
++ return ARGON2_MEMORY_TOO_LITTLE;
++ }
++
++ /* Validate time cost */
++ if (ARGON2_MIN_TIME > context->t_cost) {
++ return ARGON2_TIME_TOO_SMALL;
++ }
++
++ if (ARGON2_MAX_TIME < context->t_cost) {
++ return ARGON2_TIME_TOO_LARGE;
++ }
++
++ /* Validate lanes */
++ if (ARGON2_MIN_LANES > context->lanes) {
++ return ARGON2_LANES_TOO_FEW;
++ }
++
++ if (ARGON2_MAX_LANES < context->lanes) {
++ return ARGON2_LANES_TOO_MANY;
++ }
++
++ /* Validate threads */
++ if (ARGON2_MIN_THREADS > context->threads) {
++ return ARGON2_THREADS_TOO_FEW;
++ }
++
++ if (ARGON2_MAX_THREADS < context->threads) {
++ return ARGON2_THREADS_TOO_MANY;
++ }
++
++ if (NULL != context->allocate_cbk && NULL == context->grub_free_cbk) {
++ return ARGON2_FREE_MEMORY_CBK_NULL;
++ }
++
++ if (NULL == context->allocate_cbk && NULL != context->grub_free_cbk) {
++ return ARGON2_ALLOCATE_MEMORY_CBK_NULL;
++ }
++
++ return ARGON2_OK;
++}
++
++void fill_first_blocks(grub_uint8_t *blockhash, const argon2_instance_t *instance) {
++ grub_uint32_t l;
++ /* Make the first and second block in each lane as G(H0||0||i) or
++ G(H0||1||i) */
++ grub_uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
++ for (l = 0; l < instance->lanes; ++l) {
++
++ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0);
++ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l);
++ blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash,
++ ARGON2_PREHASH_SEED_LENGTH);
++ load_block(&instance->memory[l * instance->lane_length + 0],
++ blockhash_bytes);
++
++ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1);
++ blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash,
++ ARGON2_PREHASH_SEED_LENGTH);
++ load_block(&instance->memory[l * instance->lane_length + 1],
++ blockhash_bytes);
++ }
++ clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE);
++}
++
++void initial_hash(grub_uint8_t *blockhash, argon2_context *context,
++ argon2_type type) {
++ blake2b_state BlakeHash;
++ grub_uint8_t value[sizeof(grub_uint32_t)];
++
++ if (NULL == context || NULL == blockhash) {
++ return;
++ }
++
++ blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH);
++
++ store32(&value, context->lanes);
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)&value, sizeof(value));
++
++ store32(&value, context->outlen);
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)&value, sizeof(value));
++
++ store32(&value, context->m_cost);
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)&value, sizeof(value));
++
++ store32(&value, context->t_cost);
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)&value, sizeof(value));
++
++ store32(&value, context->version);
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)&value, sizeof(value));
++
++ store32(&value, (grub_uint32_t)type);
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)&value, sizeof(value));
++
++ store32(&value, context->pwdlen);
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)&value, sizeof(value));
++
++ if (context->pwd != NULL) {
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)context->pwd,
++ context->pwdlen);
++
++ if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) {
++ secure_wipe_memory(context->pwd, context->pwdlen);
++ context->pwdlen = 0;
++ }
++ }
++
++ store32(&value, context->saltlen);
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)&value, sizeof(value));
++
++ if (context->salt != NULL) {
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)context->salt,
++ context->saltlen);
++ }
++
++ store32(&value, context->secretlen);
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)&value, sizeof(value));
++
++ if (context->secret != NULL) {
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)context->secret,
++ context->secretlen);
++
++ if (context->flags & ARGON2_FLAG_CLEAR_SECRET) {
++ secure_wipe_memory(context->secret, context->secretlen);
++ context->secretlen = 0;
++ }
++ }
++
++ store32(&value, context->adlen);
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)&value, sizeof(value));
++
++ if (context->ad != NULL) {
++ blake2b_update(&BlakeHash, (const grub_uint8_t *)context->ad,
++ context->adlen);
++ }
++
++ blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH);
++}
++
++int initialize(argon2_instance_t *instance, argon2_context *context) {
++ grub_uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH];
++ int result = ARGON2_OK;
++
++ if (instance == NULL || context == NULL)
++ return ARGON2_INCORRECT_PARAMETER;
++ instance->context_ptr = context;
++
++ /* 1. Memory allocation */
++ result = allocate_memory(context, (grub_uint8_t **)&(instance->memory),
++ instance->memory_blocks, sizeof(block));
++ if (result != ARGON2_OK) {
++ return result;
++ }
++
++ /* 2. Initial hashing */
++ /* H_0 + 8 extra bytes to produce the first blocks */
++ /* grub_uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */
++ /* Hashing all inputs */
++ initial_hash(blockhash, context, instance->type);
++ /* Zeroing 8 extra bytes */
++ clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH,
++ ARGON2_PREHASH_SEED_LENGTH -
++ ARGON2_PREHASH_DIGEST_LENGTH);
++
++#ifdef GENKAT
++ initial_kat(blockhash, context, instance->type);
++#endif
++
++ /* 3. Creating first blocks, we always have at least two blocks in a slice
++ */
++ fill_first_blocks(blockhash, instance);
++ /* Clearing the hash */
++ clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH);
++
++ return ARGON2_OK;
++}
+diff --git a/grub-core/lib/argon2/core.h b/grub-core/lib/argon2/core.h
+new file mode 100644
+index 000000000..bbcd56998
+--- /dev/null
++++ b/grub-core/lib/argon2/core.h
+@@ -0,0 +1,228 @@
++/*
++ * Argon2 reference source code package - reference C implementations
++ *
++ * Copyright 2015
++ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
++ *
++ * You may use this work under the terms of a Creative Commons CC0 1.0
++ * License/Waiver or the Apache Public License 2.0, at your option. The terms of
++ * these licenses can be found at:
++ *
++ * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
++ * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * You should have received a copy of both of these licenses along with this
++ * software. If not, they may be obtained at the above URLs.
++ */
++
++#ifndef ARGON2_CORE_H
++#define ARGON2_CORE_H
++
++#include "argon2.h"
++
++#define CONST_CAST(x) (x)(grub_addr_t)
++
++/**********************Argon2 internal constants*******************************/
++
++enum argon2_core_constants {
++ /* Memory block size in bytes */
++ ARGON2_BLOCK_SIZE = 1024,
++ ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8,
++ ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16,
++ ARGON2_HWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 32,
++ ARGON2_512BIT_WORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 64,
++
++ /* Number of pseudo-random values generated by one call to Blake in Argon2i
++ to
++ generate reference block positions */
++ ARGON2_ADDRESSES_IN_BLOCK = 128,
++
++ /* Pre-hashing digest length and its extension*/
++ ARGON2_PREHASH_DIGEST_LENGTH = 64,
++ ARGON2_PREHASH_SEED_LENGTH = 72
++};
++
++/*************************Argon2 internal data types***********************/
++
++/*
++ * Structure for the (1KB) memory block implemented as 128 64-bit words.
++ * Memory blocks can be copied, XORed. Internal words can be accessed by [] (no
++ * bounds checking).
++ */
++typedef struct block_ { grub_uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block;
++
++/*****************Functions that work with the block******************/
++
++/* Initialize each byte of the block with @in */
++void init_block_value(block *b, grub_uint8_t in);
++
++/* Copy block @src to block @dst */
++void copy_block(block *dst, const block *src);
++
++/* XOR @src onto @dst bytewise */
++void xor_block(block *dst, const block *src);
++
++/*
++ * Argon2 instance: memory pointer, number of passes, amount of memory, type,
++ * and derived values.
++ * Used to evaluate the number and location of blocks to construct in each
++ * thread
++ */
++typedef struct Argon2_instance_t {
++ block *memory; /* Memory pointer */
++ grub_uint32_t version;
++ grub_uint32_t passes; /* Number of passes */
++ grub_uint32_t memory_blocks; /* Number of blocks in memory */
++ grub_uint32_t segment_length;
++ grub_uint32_t lane_length;
++ grub_uint32_t lanes;
++ grub_uint32_t threads;
++ argon2_type type;
++ int print_internals; /* whether to print the memory blocks */
++ argon2_context *context_ptr; /* points back to original context */
++} argon2_instance_t;
++
++/*
++ * Argon2 position: where we construct the block right now. Used to distribute
++ * work between threads.
++ */
++typedef struct Argon2_position_t {
++ grub_uint32_t pass;
++ grub_uint32_t lane;
++ grub_uint8_t slice;
++ grub_uint32_t index;
++} argon2_position_t;
++
++/*Struct that holds the inputs for thread handling FillSegment*/
++typedef struct Argon2_thread_data {
++ argon2_instance_t *instance_ptr;
++ argon2_position_t pos;
++} argon2_thread_data;
++
++/*************************Argon2 core functions********************************/
++
++/* Allocates memory to the given pointer, uses the appropriate allocator as
++ * specified in the context. Total allocated memory is num*size.
++ * @param context argon2_context which specifies the allocator
++ * @param memory pointer to the pointer to the memory
++ * @param size the size in bytes for each element to be allocated
++ * @param num the number of elements to be allocated
++ * @return ARGON2_OK if @memory is a valid pointer and memory is allocated
++ */
++int allocate_memory(const argon2_context *context, grub_uint8_t **memory,
++ grub_size_t num, grub_size_t size);
++
++/*
++ * Frees memory at the given pointer, uses the appropriate deallocator as
++ * specified in the context. Also cleans the memory using clear_internal_memory.
++ * @param context argon2_context which specifies the deallocator
++ * @param memory pointer to buffer to be grub_freed
++ * @param size the size in bytes for each element to be deallocated
++ * @param num the number of elements to be deallocated
++ */
++void grub_free_memory(const argon2_context *context, grub_uint8_t *memory,
++ grub_size_t num, grub_size_t size);
++
++/* Function that securely cleans the memory. This ignores any flags set
++ * regarding clearing memory. Usually one just calls clear_internal_memory.
++ * @param mem Pointer to the memory
++ * @param s Memory size in bytes
++ */
++void secure_wipe_memory(void *v, grub_size_t n);
++
++/* Function that securely clears the memory if FLAG_clear_internal_memory is
++ * set. If the flag isn't set, this function does nothing.
++ * @param mem Pointer to the memory
++ * @param s Memory size in bytes
++ */
++void clear_internal_memory(void *v, grub_size_t n);
++
++/*
++ * Computes absolute position of reference block in the lane following a skewed
++ * distribution and using a pseudo-random value as input
++ * @param instance Pointer to the current instance
++ * @param position Pointer to the current position
++ * @param pseudo_rand 32-bit pseudo-random value used to determine the position
++ * @param same_lane Indicates if the block will be taken from the current lane.
++ * If so we can reference the current segment
++ * @pre All pointers must be valid
++ */
++grub_uint32_t index_alpha(const argon2_instance_t *instance,
++ const argon2_position_t *position, grub_uint32_t pseudo_rand,
++ int same_lane);
++
++/*
++ * Function that validates all inputs against predefined restrictions and return
++ * an error code
++ * @param context Pointer to current Argon2 context
++ * @return ARGON2_OK if everything is all right, otherwise one of error codes
++ * (all defined in <argon2.h>
++ */
++int validate_inputs(const argon2_context *context);
++
++/*
++ * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears
++ * password and secret if needed
++ * @param context Pointer to the Argon2 internal structure containing memory
++ * pointer, and parameters for time and space requirements.
++ * @param blockhash Buffer for pre-hashing digest
++ * @param type Argon2 type
++ * @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes
++ * allocated
++ */
++void initial_hash(grub_uint8_t *blockhash, argon2_context *context,
++ argon2_type type);
++
++/*
++ * Function creates first 2 blocks per lane
++ * @param instance Pointer to the current instance
++ * @param blockhash Pointer to the pre-hashing digest
++ * @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values
++ */
++void fill_first_blocks(grub_uint8_t *blockhash, const argon2_instance_t *instance);
++
++/*
++ * Function allocates memory, hashes the inputs with Blake, and creates first
++ * two blocks. Returns the pointer to the main memory with 2 blocks per lane
++ * initialized
++ * @param context Pointer to the Argon2 internal structure containing memory
++ * pointer, and parameters for time and space requirements.
++ * @param instance Current Argon2 instance
++ * @return Zero if successful, -1 if memory failed to allocate. @context->state
++ * will be modified if successful.
++ */
++int initialize(argon2_instance_t *instance, argon2_context *context);
++
++/*
++ * XORing the last block of each lane, hashing it, making the tag. Deallocates
++ * the memory.
++ * @param context Pointer to current Argon2 context (use only the out parameters
++ * from it)
++ * @param instance Pointer to current instance of Argon2
++ * @pre instance->state must point to necessary amount of memory
++ * @pre context->out must point to outlen bytes of memory
++ * @pre if context->grub_free_cbk is not NULL, it should point to a function that
++ * deallocates memory
++ */
++void finalize(const argon2_context *context, argon2_instance_t *instance);
++
++/*
++ * Function that fills the segment using previous segments also from other
++ * threads
++ * @param context current context
++ * @param instance Pointer to the current instance
++ * @param position Current position
++ * @pre all block pointers must be valid
++ */
++void fill_segment(const argon2_instance_t *instance,
++ argon2_position_t position);
++
++/*
++ * Function that fills the entire memory t_cost times based on the first two
++ * blocks in each lane
++ * @param instance Pointer to the current instance
++ * @return ARGON2_OK if successful, @context->state
++ */
++int fill_memory_blocks(argon2_instance_t *instance);
++
++#endif
+diff --git a/grub-core/lib/argon2/ref.c b/grub-core/lib/argon2/ref.c
+new file mode 100644
+index 000000000..c933df80d
+--- /dev/null
++++ b/grub-core/lib/argon2/ref.c
+@@ -0,0 +1,190 @@
++/*
++ * Argon2 reference source code package - reference C implementations
++ *
++ * Copyright 2015
++ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
++ *
++ * You may use this work under the terms of a Creative Commons CC0 1.0
++ * License/Waiver or the Apache Public License 2.0, at your option. The terms of
++ * these licenses can be found at:
++ *
++ * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
++ * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * You should have received a copy of both of these licenses along with this
++ * software. If not, they may be obtained at the above URLs.
++ */
++
++#include "argon2.h"
++#include "core.h"
++
++#include "blake2/blamka-round-ref.h"
++#include "blake2/blake2-impl.h"
++#include "blake2/blake2.h"
++
++
++/*
++ * Function fills a new memory block and optionally XORs the old block over the new one.
++ * @next_block must be initialized.
++ * @param prev_block Pointer to the previous block
++ * @param ref_block Pointer to the reference block
++ * @param next_block Pointer to the block to be constructed
++ * @param with_xor Whether to XOR into the new block (1) or just overwrite (0)
++ * @pre all block pointers must be valid
++ */
++static void fill_block(const block *prev_block, const block *ref_block,
++ block *next_block, int with_xor) {
++ block blockR, block_tmp;
++ unsigned i;
++
++ copy_block(&blockR, ref_block);
++ xor_block(&blockR, prev_block);
++ copy_block(&block_tmp, &blockR);
++ /* Now blockR = ref_block + prev_block and block_tmp = ref_block + prev_block */
++ if (with_xor) {
++ /* Saving the next block contents for XOR over: */
++ xor_block(&block_tmp, next_block);
++ /* Now blockR = ref_block + prev_block and
++ block_tmp = ref_block + prev_block + next_block */
++ }
++
++ /* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
++ (16,17,..31)... finally (112,113,...127) */
++ for (i = 0; i < 8; ++i) {
++ BLAKE2_ROUND_NOMSG(
++ blockR.v[16 * i], blockR.v[16 * i + 1], blockR.v[16 * i + 2],
++ blockR.v[16 * i + 3], blockR.v[16 * i + 4], blockR.v[16 * i + 5],
++ blockR.v[16 * i + 6], blockR.v[16 * i + 7], blockR.v[16 * i + 8],
++ blockR.v[16 * i + 9], blockR.v[16 * i + 10], blockR.v[16 * i + 11],
++ blockR.v[16 * i + 12], blockR.v[16 * i + 13], blockR.v[16 * i + 14],
++ blockR.v[16 * i + 15]);
++ }
++
++ /* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then
++ (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */
++ for (i = 0; i < 8; i++) {
++ BLAKE2_ROUND_NOMSG(
++ blockR.v[2 * i], blockR.v[2 * i + 1], blockR.v[2 * i + 16],
++ blockR.v[2 * i + 17], blockR.v[2 * i + 32], blockR.v[2 * i + 33],
++ blockR.v[2 * i + 48], blockR.v[2 * i + 49], blockR.v[2 * i + 64],
++ blockR.v[2 * i + 65], blockR.v[2 * i + 80], blockR.v[2 * i + 81],
++ blockR.v[2 * i + 96], blockR.v[2 * i + 97], blockR.v[2 * i + 112],
++ blockR.v[2 * i + 113]);
++ }
++
++ copy_block(next_block, &block_tmp);
++ xor_block(next_block, &blockR);
++}
++
++static void next_addresses(block *address_block, block *input_block,
++ const block *zero_block) {
++ input_block->v[6]++;
++ fill_block(zero_block, input_block, address_block, 0);
++ fill_block(zero_block, address_block, address_block, 0);
++}
++
++void fill_segment(const argon2_instance_t *instance,
++ argon2_position_t position) {
++ block *ref_block = NULL, *curr_block = NULL;
++ block address_block, input_block, zero_block;
++ grub_uint64_t pseudo_rand, ref_index, ref_lane;
++ grub_uint32_t prev_offset, curr_offset;
++ grub_uint32_t starting_index;
++ grub_uint32_t i;
++ int data_independent_addressing;
++
++ if (instance == NULL) {
++ return;
++ }
++
++ data_independent_addressing =
++ (instance->type == Argon2_i) ||
++ (instance->type == Argon2_id && (position.pass == 0) &&
++ (position.slice < ARGON2_SYNC_POINTS / 2));
++
++ if (data_independent_addressing) {
++ init_block_value(&zero_block, 0);
++ init_block_value(&input_block, 0);
++
++ input_block.v[0] = position.pass;
++ input_block.v[1] = position.lane;
++ input_block.v[2] = position.slice;
++ input_block.v[3] = instance->memory_blocks;
++ input_block.v[4] = instance->passes;
++ input_block.v[5] = instance->type;
++ }
++
++ starting_index = 0;
++
++ if ((0 == position.pass) && (0 == position.slice)) {
++ starting_index = 2; /* we have already generated the first two blocks */
++
++ /* Don't forget to generate the first block of addresses: */
++ if (data_independent_addressing) {
++ next_addresses(&address_block, &input_block, &zero_block);
++ }
++ }
++
++ /* Offset of the current block */
++ curr_offset = position.lane * instance->lane_length +
++ position.slice * instance->segment_length + starting_index;
++
++ if (0 == curr_offset % instance->lane_length) {
++ /* Last block in this lane */
++ prev_offset = curr_offset + instance->lane_length - 1;
++ } else {
++ /* Previous block */
++ prev_offset = curr_offset - 1;
++ }
++
++ for (i = starting_index; i < instance->segment_length;
++ ++i, ++curr_offset, ++prev_offset) {
++ /*1.1 Rotating prev_offset if needed */
++ if (curr_offset % instance->lane_length == 1) {
++ prev_offset = curr_offset - 1;
++ }
++
++ /* 1.2 Computing the index of the reference block */
++ /* 1.2.1 Taking pseudo-random value from the previous block */
++ if (data_independent_addressing) {
++ if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) {
++ next_addresses(&address_block, &input_block, &zero_block);
++ }
++ pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK];
++ } else {
++ pseudo_rand = instance->memory[prev_offset].v[0];
++ }
++
++ /* 1.2.2 Computing the lane of the reference block */
++ grub_divmod64 (pseudo_rand >> 32, instance->lanes, &ref_lane);
++
++ if ((position.pass == 0) && (position.slice == 0)) {
++ /* Can not reference other lanes yet */
++ ref_lane = position.lane;
++ }
++
++ /* 1.2.3 Computing the number of possible reference block within the
++ * lane.
++ */
++ position.index = i;
++ ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF,
++ ref_lane == position.lane);
++
++ /* 2 Creating a new block */
++ ref_block =
++ instance->memory + instance->lane_length * ref_lane + ref_index;
++ curr_block = instance->memory + curr_offset;
++ if (ARGON2_VERSION_10 == instance->version) {
++ /* version 1.2.1 and earlier: overwrite, not XOR */
++ fill_block(instance->memory + prev_offset, ref_block, curr_block, 0);
++ } else {
++ if(0 == position.pass) {
++ fill_block(instance->memory + prev_offset, ref_block,
++ curr_block, 0);
++ } else {
++ fill_block(instance->memory + prev_offset, ref_block,
++ curr_block, 1);
++ }
++ }
++ }
++}
+--
+2.39.2
+
diff --git a/config/grub/xhci/patches/0006-Error-on-missing-Argon2id-parameters.patch b/config/grub/xhci/patches/0006-Error-on-missing-Argon2id-parameters.patch
new file mode 100644
index 00000000..30de08cc
--- /dev/null
+++ b/config/grub/xhci/patches/0006-Error-on-missing-Argon2id-parameters.patch
@@ -0,0 +1,58 @@
+From 08a2fce70c6e988eb0112d5ad2787843910811bc Mon Sep 17 00:00:00 2001
+From: Ax333l <main@axelen.xyz>
+Date: Thu, 17 Aug 2023 00:00:00 +0000
+Subject: [PATCH 06/22] Error on missing Argon2id parameters
+
+Signed-off-by: Nicholas Johnson <nick@nicholasjohnson.ch>
+---
+ grub-core/disk/luks2.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/disk/luks2.c b/grub-core/disk/luks2.c
+index d5106402f..bc818ea69 100644
+--- a/grub-core/disk/luks2.c
++++ b/grub-core/disk/luks2.c
+@@ -38,6 +38,7 @@ GRUB_MOD_LICENSE ("GPLv3+");
+ enum grub_luks2_kdf_type
+ {
+ LUKS2_KDF_TYPE_ARGON2I,
++ LUKS2_KDF_TYPE_ARGON2ID,
+ LUKS2_KDF_TYPE_PBKDF2
+ };
+ typedef enum grub_luks2_kdf_type grub_luks2_kdf_type_t;
+@@ -90,7 +91,7 @@ struct grub_luks2_keyslot
+ grub_int64_t time;
+ grub_int64_t memory;
+ grub_int64_t cpus;
+- } argon2i;
++ } argon2;
+ struct
+ {
+ const char *hash;
+@@ -160,10 +161,11 @@ luks2_parse_keyslot (grub_luks2_keyslot_t *out, const grub_json_t *keyslot)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing or invalid KDF");
+ else if (!grub_strcmp (type, "argon2i") || !grub_strcmp (type, "argon2id"))
+ {
+- out->kdf.type = LUKS2_KDF_TYPE_ARGON2I;
+- if (grub_json_getint64 (&out->kdf.u.argon2i.time, &kdf, "time") ||
+- grub_json_getint64 (&out->kdf.u.argon2i.memory, &kdf, "memory") ||
+- grub_json_getint64 (&out->kdf.u.argon2i.cpus, &kdf, "cpus"))
++ out->kdf.type = !grub_strcmp (type, "argon2i")
++ ? LUKS2_KDF_TYPE_ARGON2I : LUKS2_KDF_TYPE_ARGON2ID;
++ if (grub_json_getint64 (&out->kdf.u.argon2.time, &kdf, "time") ||
++ grub_json_getint64 (&out->kdf.u.argon2.memory, &kdf, "memory") ||
++ grub_json_getint64 (&out->kdf.u.argon2.cpus, &kdf, "cpus"))
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing Argon2i parameters");
+ }
+ else if (!grub_strcmp (type, "pbkdf2"))
+@@ -459,6 +461,7 @@ luks2_decrypt_key (grub_uint8_t *out_key,
+ switch (k->kdf.type)
+ {
+ case LUKS2_KDF_TYPE_ARGON2I:
++ case LUKS2_KDF_TYPE_ARGON2ID:
+ ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Argon2 not supported");
+ goto err;
+ case LUKS2_KDF_TYPE_PBKDF2:
+--
+2.39.2
+
diff --git a/config/grub/xhci/patches/0007-Compile-with-Argon2id-support.patch b/config/grub/xhci/patches/0007-Compile-with-Argon2id-support.patch
new file mode 100644
index 00000000..23f361dc
--- /dev/null
+++ b/config/grub/xhci/patches/0007-Compile-with-Argon2id-support.patch
@@ -0,0 +1,83 @@
+From 268da8d0ccce822ffa4c2d9d35fe717245daa726 Mon Sep 17 00:00:00 2001
+From: Ax333l <main@axelen.xyz>
+Date: Thu, 17 Aug 2023 00:00:00 +0000
+Subject: [PATCH 07/22] Compile with Argon2id support
+
+Signed-off-by: Nicholas Johnson <nick@nicholasjohnson.ch>
+---
+ Makefile.util.def | 6 +++++-
+ grub-core/Makefile.core.def | 2 +-
+ grub-core/disk/luks2.c | 13 +++++++++++--
+ 3 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/Makefile.util.def b/Makefile.util.def
+index 0f74a1680..5a15e5637 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -3,7 +3,7 @@ AutoGen definitions Makefile.tpl;
+ library = {
+ name = libgrubkern.a;
+ cflags = '$(CFLAGS_GNULIB)';
+- cppflags = '$(CPPFLAGS_GNULIB) -I$(srcdir)/grub-core/lib/json';
++ cppflags = '$(CPPFLAGS_GNULIB) -I$(srcdir)/grub-core/lib/json -I$(srcdir)/grub-core/lib/argon2';
+
+ common = util/misc.c;
+ common = grub-core/kern/command.c;
+@@ -36,6 +36,10 @@ library = {
+ common = grub-core/kern/misc.c;
+ common = grub-core/kern/partition.c;
+ common = grub-core/lib/crypto.c;
++ common = grub-core/lib/argon2/argon2.c;
++ common = grub-core/lib/argon2/core.c;
++ common = grub-core/lib/argon2/ref.c;
++ common = grub-core/lib/argon2/blake2/blake2b.c;
+ common = grub-core/lib/json/json.c;
+ common = grub-core/disk/luks.c;
+ common = grub-core/disk/luks2.c;
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 452f11b20..5c1af8682 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -1242,7 +1242,7 @@ module = {
+ common = disk/luks2.c;
+ common = lib/gnulib/base64.c;
+ cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+- cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/json';
++ cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/json -I$(srcdir)/lib/argon2';
+ };
+
+ module = {
+diff --git a/grub-core/disk/luks2.c b/grub-core/disk/luks2.c
+index bc818ea69..5b9eaa599 100644
+--- a/grub-core/disk/luks2.c
++++ b/grub-core/disk/luks2.c
+@@ -27,6 +27,7 @@
+ #include <grub/partition.h>
+ #include <grub/i18n.h>
+
++#include <argon2.h>
+ #include <base64.h>
+ #include <json.h>
+
+@@ -462,8 +463,16 @@ luks2_decrypt_key (grub_uint8_t *out_key,
+ {
+ case LUKS2_KDF_TYPE_ARGON2I:
+ case LUKS2_KDF_TYPE_ARGON2ID:
+- ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Argon2 not supported");
+- goto err;
++ ret = argon2_hash (k->kdf.u.argon2.time, k->kdf.u.argon2.memory, k->kdf.u.argon2.cpus,
++ passphrase, passphraselen, salt, saltlen, area_key, k->area.key_size,
++ k->kdf.type == LUKS2_KDF_TYPE_ARGON2I ? Argon2_i : Argon2_id,
++ ARGON2_VERSION_NUMBER);
++ if (ret)
++ {
++ grub_dprintf ("luks2", "Argon2 failed: %s\n", argon2_error_message (ret));
++ goto err;
++ }
++ break;
+ case LUKS2_KDF_TYPE_PBKDF2:
+ hash = grub_crypto_lookup_md_by_name (k->kdf.u.pbkdf2.hash);
+ if (!hash)
+--
+2.39.2
+
diff --git a/config/grub/xhci/patches/0008-Make-grub-install-work-with-Argon2.patch b/config/grub/xhci/patches/0008-Make-grub-install-work-with-Argon2.patch
new file mode 100644
index 00000000..ffdb306c
--- /dev/null
+++ b/config/grub/xhci/patches/0008-Make-grub-install-work-with-Argon2.patch
@@ -0,0 +1,26 @@
+From 5e540d3e4c01940c66425c4475a1cb6a35b188e8 Mon Sep 17 00:00:00 2001
+From: Ax333l <main@axelen.xyz>
+Date: Thu, 17 Aug 2023 00:00:00 +0000
+Subject: [PATCH 08/22] Make grub-install work with Argon2
+
+Signed-off-by: Nicholas Johnson <nick@nicholasjohnson.ch>
+---
+ util/grub-install.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/util/grub-install.c b/util/grub-install.c
+index 7dc5657bb..cf7315891 100644
+--- a/util/grub-install.c
++++ b/util/grub-install.c
+@@ -448,6 +448,8 @@ probe_mods (grub_disk_t disk)
+ {
+ grub_util_cryptodisk_get_abstraction (disk,
+ push_cryptodisk_module, NULL);
++ /* HACK: always push argon2 */
++ grub_install_push_module ("argon2");
+ have_abstractions = 1;
+ have_cryptodisk = 1;
+ }
+--
+2.39.2
+
diff --git a/config/grub/xhci/patches/0009-at_keyboard-coreboot-force-scancodes2-translate.patch b/config/grub/xhci/patches/0009-at_keyboard-coreboot-force-scancodes2-translate.patch
new file mode 100644
index 00000000..e4b26a38
--- /dev/null
+++ b/config/grub/xhci/patches/0009-at_keyboard-coreboot-force-scancodes2-translate.patch
@@ -0,0 +1,107 @@
+From 9e9c69a74e5c14fd87ae56c8b9171a808d89742e Mon Sep 17 00:00:00 2001
+From: Leah Rowe <leah@libreboot.org>
+Date: Mon, 30 Oct 2023 22:19:21 +0000
+Subject: [PATCH 09/22] 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.39.2
+
diff --git a/config/grub/xhci/patches/0010-keylayouts-don-t-print-Unknown-key-message.patch b/config/grub/xhci/patches/0010-keylayouts-don-t-print-Unknown-key-message.patch
new file mode 100644
index 00000000..e8a3ce14
--- /dev/null
+++ b/config/grub/xhci/patches/0010-keylayouts-don-t-print-Unknown-key-message.patch
@@ -0,0 +1,38 @@
+From ccbcdb93af6747fe77094dfa9e114a034420bdd5 Mon Sep 17 00:00:00 2001
+From: Leah Rowe <leah@libreboot.org>
+Date: Tue, 31 Oct 2023 10:33:28 +0000
+Subject: [PATCH 10/22] 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.39.2
+
diff --git a/config/grub/xhci/patches/0011-don-t-print-missing-prefix-errors-on-the-screen.patch b/config/grub/xhci/patches/0011-don-t-print-missing-prefix-errors-on-the-screen.patch
new file mode 100644
index 00000000..de68bb78
--- /dev/null
+++ b/config/grub/xhci/patches/0011-don-t-print-missing-prefix-errors-on-the-screen.patch
@@ -0,0 +1,102 @@
+From 21a829a3a6f5e7d9028059df8057fdf8bce8fe06 Mon Sep 17 00:00:00 2001
+From: Leah Rowe <leah@libreboot.org>
+Date: Sun, 5 Nov 2023 16:14:58 +0000
+Subject: [PATCH 11/22] 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 4011e2d15..af3bd00d0 100644
+--- a/grub-core/kern/dl.c
++++ b/grub-core/kern/dl.c
+@@ -758,7 +758,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.39.2
+
diff --git a/config/grub/xhci/patches/0012-don-t-print-error-if-module-not-found.patch b/config/grub/xhci/patches/0012-don-t-print-error-if-module-not-found.patch
new file mode 100644
index 00000000..37f52e03
--- /dev/null
+++ b/config/grub/xhci/patches/0012-don-t-print-error-if-module-not-found.patch
@@ -0,0 +1,34 @@
+From edb8208100c523b5776f2cb0712fdc0c9065e517 Mon Sep 17 00:00:00 2001
+From: Leah Rowe <leah@libreboot.org>
+Date: Sun, 5 Nov 2023 16:36:22 +0000
+Subject: [PATCH 12/22] 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 af3bd00d0..21d0cedb1 100644
+--- a/grub-core/kern/dl.c
++++ b/grub-core/kern/dl.c
+@@ -486,7 +486,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.39.2
+
diff --git a/config/grub/xhci/patches/0013-don-t-print-empty-error-messages.patch b/config/grub/xhci/patches/0013-don-t-print-empty-error-messages.patch
new file mode 100644
index 00000000..74d8bade
--- /dev/null
+++ b/config/grub/xhci/patches/0013-don-t-print-empty-error-messages.patch
@@ -0,0 +1,31 @@
+From 65cb1871a3e125355df78a6d1d6f1bc7e356c4e8 Mon Sep 17 00:00:00 2001
+From: Leah Rowe <leah@libreboot.org>
+Date: Sun, 5 Nov 2023 17:25:20 +0000
+Subject: [PATCH 13/22] 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 53c734de7..7cac53983 100644
+--- a/grub-core/kern/err.c
++++ b/grub-core/kern/err.c
+@@ -107,7 +107,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.39.2
+
diff --git a/config/grub/xhci/patches/0014-grub-core-bus-usb-Parse-SuperSpeed-companion-descrip.patch b/config/grub/xhci/patches/0014-grub-core-bus-usb-Parse-SuperSpeed-companion-descrip.patch
new file mode 100644
index 00000000..4c84b8c3
--- /dev/null
+++ b/config/grub/xhci/patches/0014-grub-core-bus-usb-Parse-SuperSpeed-companion-descrip.patch
@@ -0,0 +1,246 @@
+From 3273128b6dc6df83ef6b1d54d009a1ae26844bff 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 14/22] 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 2c6d93fe6..55a657635 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.39.2
+
diff --git a/config/grub/xhci/patches/0015-usb-Add-enum-for-xHCI.patch b/config/grub/xhci/patches/0015-usb-Add-enum-for-xHCI.patch
new file mode 100644
index 00000000..937ff1af
--- /dev/null
+++ b/config/grub/xhci/patches/0015-usb-Add-enum-for-xHCI.patch
@@ -0,0 +1,29 @@
+From 3b8f2defcda1a3b51ad0be8795a2338a0ed5ca59 Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Sun, 15 Nov 2020 19:47:06 +0100
+Subject: [PATCH 15/22] 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.39.2
+
diff --git a/config/grub/xhci/patches/0016-usbtrans-Set-default-maximum-packet-size.patch b/config/grub/xhci/patches/0016-usbtrans-Set-default-maximum-packet-size.patch
new file mode 100644
index 00000000..8f6bb001
--- /dev/null
+++ b/config/grub/xhci/patches/0016-usbtrans-Set-default-maximum-packet-size.patch
@@ -0,0 +1,33 @@
+From ba7ce6daec155bc3deac4e0c48d470afa024ab94 Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Sun, 15 Nov 2020 19:48:03 +0100
+Subject: [PATCH 16/22] 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.39.2
+
diff --git a/config/grub/xhci/patches/0017-grub-core-bus-usb-Add-function-pointer-for-attach-de.patch b/config/grub/xhci/patches/0017-grub-core-bus-usb-Add-function-pointer-for-attach-de.patch
new file mode 100644
index 00000000..60680bd0
--- /dev/null
+++ b/config/grub/xhci/patches/0017-grub-core-bus-usb-Add-function-pointer-for-attach-de.patch
@@ -0,0 +1,121 @@
+From c4cd7fbe3e2e8ff4cbe6d0db8c3356aeee614af5 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 17/22] 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 9abebc6bd..953b851c0 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.39.2
+
diff --git a/config/grub/xhci/patches/0018-grub-core-bus-usb-usbhub-Add-new-private-fields-for-.patch b/config/grub/xhci/patches/0018-grub-core-bus-usb-usbhub-Add-new-private-fields-for-.patch
new file mode 100644
index 00000000..bc0f957f
--- /dev/null
+++ b/config/grub/xhci/patches/0018-grub-core-bus-usb-usbhub-Add-new-private-fields-for-.patch
@@ -0,0 +1,77 @@
+From 1ab23afbfa7ae436741947c0b9bdacc434ad6153 Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Sun, 15 Nov 2020 19:54:40 +0100
+Subject: [PATCH 18/22] 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.39.2
+
diff --git a/config/grub/xhci/patches/0019-grub-core-bus-usb-Add-xhci-support.patch b/config/grub/xhci/patches/0019-grub-core-bus-usb-Add-xhci-support.patch
new file mode 100644
index 00000000..02a24d5a
--- /dev/null
+++ b/config/grub/xhci/patches/0019-grub-core-bus-usb-Add-xhci-support.patch
@@ -0,0 +1,2814 @@
+From 8c9e61e7b0f28a66d0f63c07b10fc6617a709010 Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Sun, 15 Nov 2020 19:59:25 +0100
+Subject: [PATCH 19/22] 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
+
+Tested on qemu using coreboot and grub as payload:
+
+qemu-system-x86_64 -M q35 -bios $firmware -device qemu-xhci,id=xhci -accel kvm -m 1024M \
+ -device usb-storage,drive=thumbdrive,bus=xhci.0,port=3 \
+ -drive if=none,format=raw,id=thumbdrive,file=ubuntu-20.04.1-desktop-amd64.iso \
+ -device usb-kbd,bus=xhci.0
+
+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 5c1af8682..9d59acd1e 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -667,6 +667,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.39.2
+
diff --git a/config/grub/xhci/patches/0020-grub-core-bus-usb-usbhub-Add-xHCI-non-root-hub-suppo.patch b/config/grub/xhci/patches/0020-grub-core-bus-usb-usbhub-Add-xHCI-non-root-hub-suppo.patch
new file mode 100644
index 00000000..735ef22d
--- /dev/null
+++ b/config/grub/xhci/patches/0020-grub-core-bus-usb-usbhub-Add-xHCI-non-root-hub-suppo.patch
@@ -0,0 +1,127 @@
+From 127961742cf7992f6989c6e89a18ab6d8f0b297f Mon Sep 17 00:00:00 2001
+From: Patrick Rudolph <patrick.rudolph@9elements.com>
+Date: Thu, 3 Dec 2020 13:44:55 +0100
+Subject: [PATCH 20/22] 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.39.2
+
diff --git a/config/grub/xhci/patches/0021-Fix-compilation-on-x86_64.patch b/config/grub/xhci/patches/0021-Fix-compilation-on-x86_64.patch
new file mode 100644
index 00000000..6a5f0502
--- /dev/null
+++ b/config/grub/xhci/patches/0021-Fix-compilation-on-x86_64.patch
@@ -0,0 +1,90 @@
+From 8d46c537d4df8c785af4b85644d311ba53af5964 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 21/22] 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 f4591ffb5..3495bb919 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)
+ {
+@@ -664,7 +672,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;
+@@ -697,9 +705,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);
+ }
+ }
+
+@@ -800,7 +808,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))
+ {
+@@ -1907,7 +1915,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);
+
+@@ -1974,7 +1982,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.39.2
+
diff --git a/config/grub/xhci/patches/0022-Add-native-NVMe-driver-based-on-SeaBIOS.patch b/config/grub/xhci/patches/0022-Add-native-NVMe-driver-based-on-SeaBIOS.patch
new file mode 100644
index 00000000..82452f1c
--- /dev/null
+++ b/config/grub/xhci/patches/0022-Add-native-NVMe-driver-based-on-SeaBIOS.patch
@@ -0,0 +1,1074 @@
+From 394102db8f4de6782b628b29c59d2634f2c72674 Mon Sep 17 00:00:00 2001
+From: Mate Kukri <km@mkukri.xyz>
+Date: Mon, 20 May 2024 11:43:35 +0100
+Subject: [PATCH 22/22] 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 9d59acd1e..56076728b 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -2621,3 +2621,9 @@ module = {
+ enable = efi;
+ depends = part_gpt;
+ };
++
++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.39.2
+