diff options
Diffstat (limited to 'config/grub/xhci/patches')
22 files changed, 7896 insertions, 0 deletions
| diff --git a/config/grub/xhci/patches/0001-borderfix/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch b/config/grub/xhci/patches/0001-borderfix/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch new file mode 100644 index 00000000..183f5a91 --- /dev/null +++ b/config/grub/xhci/patches/0001-borderfix/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch @@ -0,0 +1,90 @@ +From ce13539fe2103abbd991814d995e06cf96e485f7 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Sun, 31 Oct 2021 03:47:05 +0000 +Subject: [PATCH 1/3] 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 b1321eb26..e76094dfd 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, +@@ -410,8 +367,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.25.1 + diff --git a/config/grub/xhci/patches/0001-borderfix/0002-say-the-name-libreboot-in-the-grub-menu.patch b/config/grub/xhci/patches/0001-borderfix/0002-say-the-name-libreboot-in-the-grub-menu.patch new file mode 100644 index 00000000..6ff97979 --- /dev/null +++ b/config/grub/xhci/patches/0001-borderfix/0002-say-the-name-libreboot-in-the-grub-menu.patch @@ -0,0 +1,25 @@ +From 70f9e72c3ff6381fe3519612de3b649c0cf26b9a Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Sat, 19 Nov 2022 16:30:24 +0000 +Subject: [PATCH 2/3] 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..31308e16a 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 20240504 release, based on coreboot.    https://libreboot.org/")); +   if (!msg_formatted) +     return; +  +--  +2.25.1 + diff --git a/config/grub/xhci/patches/0002-luks2/0003-Add-CC0-license.patch b/config/grub/xhci/patches/0002-luks2/0003-Add-CC0-license.patch new file mode 100644 index 00000000..dc9060c3 --- /dev/null +++ b/config/grub/xhci/patches/0002-luks2/0003-Add-CC0-license.patch @@ -0,0 +1,42 @@ +From de6e7cc62522ce1be21bd2f06e7c15cd234b5426 Mon Sep 17 00:00:00 2001 +From: Ax333l <main@axelen.xyz> +Date: Thu, 17 Aug 2023 00:00:00 +0000 +Subject: [PATCH 1/6] 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/0002-luks2/0004-Define-GRUB_UINT32_MAX.patch b/config/grub/xhci/patches/0002-luks2/0004-Define-GRUB_UINT32_MAX.patch new file mode 100644 index 00000000..be875e67 --- /dev/null +++ b/config/grub/xhci/patches/0002-luks2/0004-Define-GRUB_UINT32_MAX.patch @@ -0,0 +1,39 @@ +From 9edaaffac91d593a439e44bac3b6f5558f5a8245 Mon Sep 17 00:00:00 2001 +From: Ax333l <main@axelen.xyz> +Date: Thu, 17 Aug 2023 00:00:00 +0000 +Subject: [PATCH 2/6] 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 0d96006fe..a13f3a60b 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/0002-luks2/0005-Add-Argon2-algorithm.patch b/config/grub/xhci/patches/0002-luks2/0005-Add-Argon2-algorithm.patch new file mode 100644 index 00000000..1c6b4f04 --- /dev/null +++ b/config/grub/xhci/patches/0002-luks2/0005-Add-Argon2-algorithm.patch @@ -0,0 +1,2611 @@ +From 5b63da5c4267933f573ca37ce39a073098c443ba Mon Sep 17 00:00:00 2001 +From: Ax333l <main@axelen.xyz> +Date: Thu, 17 Aug 2023 00:00:00 +0000 +Subject: [PATCH 3/6] 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 a695b02f0..313335a53 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 d2cf29584..4a06789e5 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -1215,6 +1215,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/0002-luks2/0006-Error-on-missing-Argon2id-parameters.patch b/config/grub/xhci/patches/0002-luks2/0006-Error-on-missing-Argon2id-parameters.patch new file mode 100644 index 00000000..5d56bd61 --- /dev/null +++ b/config/grub/xhci/patches/0002-luks2/0006-Error-on-missing-Argon2id-parameters.patch @@ -0,0 +1,58 @@ +From 0044d32121bf52c4547c6b3c78f12d7305f57e6b Mon Sep 17 00:00:00 2001 +From: Ax333l <main@axelen.xyz> +Date: Thu, 17 Aug 2023 00:00:00 +0000 +Subject: [PATCH 4/6] 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/0002-luks2/0007-Compile-with-Argon2id-support.patch b/config/grub/xhci/patches/0002-luks2/0007-Compile-with-Argon2id-support.patch new file mode 100644 index 00000000..f2e26fd4 --- /dev/null +++ b/config/grub/xhci/patches/0002-luks2/0007-Compile-with-Argon2id-support.patch @@ -0,0 +1,83 @@ +From 0a21695c55f76f1c958bb633481d55b3168562f7 Mon Sep 17 00:00:00 2001 +From: Ax333l <main@axelen.xyz> +Date: Thu, 17 Aug 2023 00:00:00 +0000 +Subject: [PATCH 5/6] 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 1e9a13d3e..a167825c3 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 4a06789e5..e939dcc99 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -1238,7 +1238,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/0002-luks2/0008-Make-grub-install-work-with-Argon2.patch b/config/grub/xhci/patches/0002-luks2/0008-Make-grub-install-work-with-Argon2.patch new file mode 100644 index 00000000..dc65b7a6 --- /dev/null +++ b/config/grub/xhci/patches/0002-luks2/0008-Make-grub-install-work-with-Argon2.patch @@ -0,0 +1,26 @@ +From 6c9a6625c0dc038d1bdbdc13665f40e269e86496 Mon Sep 17 00:00:00 2001 +From: Ax333l <main@axelen.xyz> +Date: Thu, 17 Aug 2023 00:00:00 +0000 +Subject: [PATCH 6/6] 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 1ad04db36..a8a3330b8 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/0003-keyboardfix/0001-at_keyboard-coreboot-force-scancodes2-translate.patch b/config/grub/xhci/patches/0003-keyboardfix/0001-at_keyboard-coreboot-force-scancodes2-translate.patch new file mode 100644 index 00000000..21e8630b --- /dev/null +++ b/config/grub/xhci/patches/0003-keyboardfix/0001-at_keyboard-coreboot-force-scancodes2-translate.patch @@ -0,0 +1,107 @@ +From 96c0bbe5d406b616360a7fce7cee67d7692c0d6d Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Mon, 30 Oct 2023 22:19:21 +0000 +Subject: [PATCH 1/1] 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/0003-keyboardfix/0002-keylayouts-don-t-print-Unknown-key-message.patch b/config/grub/xhci/patches/0003-keyboardfix/0002-keylayouts-don-t-print-Unknown-key-message.patch new file mode 100644 index 00000000..fbef86a4 --- /dev/null +++ b/config/grub/xhci/patches/0003-keyboardfix/0002-keylayouts-don-t-print-Unknown-key-message.patch @@ -0,0 +1,38 @@ +From 0a6abeb40ac4284fbff6ef5958989d561b6290a7 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Tue, 31 Oct 2023 10:33:28 +0000 +Subject: [PATCH 1/1] 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/0004-prefix/0001-don-t-print-missing-prefix-errors-on-the-screen.patch b/config/grub/xhci/patches/0004-prefix/0001-don-t-print-missing-prefix-errors-on-the-screen.patch new file mode 100644 index 00000000..25091d16 --- /dev/null +++ b/config/grub/xhci/patches/0004-prefix/0001-don-t-print-missing-prefix-errors-on-the-screen.patch @@ -0,0 +1,102 @@ +From 9e7a651a0f15f2e9dec65a77765c3c4fd97b4165 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Sun, 5 Nov 2023 16:14:58 +0000 +Subject: [PATCH 1/1] 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/0004-prefix/0002-don-t-print-error-if-module-not-found.patch b/config/grub/xhci/patches/0004-prefix/0002-don-t-print-error-if-module-not-found.patch new file mode 100644 index 00000000..f4cf939e --- /dev/null +++ b/config/grub/xhci/patches/0004-prefix/0002-don-t-print-error-if-module-not-found.patch @@ -0,0 +1,34 @@ +From 6237c5762edccc1e1fa4746b1d4aa5e8d81e4883 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Sun, 5 Nov 2023 16:36:22 +0000 +Subject: [PATCH 1/1] 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/0004-prefix/0003-don-t-print-empty-error-messages.patch b/config/grub/xhci/patches/0004-prefix/0003-don-t-print-empty-error-messages.patch new file mode 100644 index 00000000..25221c9c --- /dev/null +++ b/config/grub/xhci/patches/0004-prefix/0003-don-t-print-empty-error-messages.patch @@ -0,0 +1,31 @@ +From e5b7ec81421487e71bcaf8b6b5a27f3649a62753 Mon Sep 17 00:00:00 2001 +From: Leah Rowe <leah@libreboot.org> +Date: Sun, 5 Nov 2023 17:25:20 +0000 +Subject: [PATCH 1/1] 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/0005-xhci/0001-grub-core-bus-usb-Parse-SuperSpeed-companion-descrip.patch b/config/grub/xhci/patches/0005-xhci/0001-grub-core-bus-usb-Parse-SuperSpeed-companion-descrip.patch new file mode 100644 index 00000000..f533269f --- /dev/null +++ b/config/grub/xhci/patches/0005-xhci/0001-grub-core-bus-usb-Parse-SuperSpeed-companion-descrip.patch @@ -0,0 +1,245 @@ +From 90c9011f2e0350a97e3df44b0fc6dd022e04c276 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 1/8] 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/0005-xhci/0002-usb-Add-enum-for-xHCI.patch b/config/grub/xhci/patches/0005-xhci/0002-usb-Add-enum-for-xHCI.patch new file mode 100644 index 00000000..d61da615 --- /dev/null +++ b/config/grub/xhci/patches/0005-xhci/0002-usb-Add-enum-for-xHCI.patch @@ -0,0 +1,29 @@ +From e111983ca5e2a52bfe2bdc5cd639b06bb2f7902d 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 2/8] 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/0005-xhci/0003-usbtrans-Set-default-maximum-packet-size.patch b/config/grub/xhci/patches/0005-xhci/0003-usbtrans-Set-default-maximum-packet-size.patch new file mode 100644 index 00000000..70e73ca2 --- /dev/null +++ b/config/grub/xhci/patches/0005-xhci/0003-usbtrans-Set-default-maximum-packet-size.patch @@ -0,0 +1,33 @@ +From 3e25c83a1d1c6e149c7e9f0660ddadb2beca2476 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 3/8] 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/0005-xhci/0004-grub-core-bus-usb-Add-function-pointer-for-attach-de.patch b/config/grub/xhci/patches/0005-xhci/0004-grub-core-bus-usb-Add-function-pointer-for-attach-de.patch new file mode 100644 index 00000000..a090e0ea --- /dev/null +++ b/config/grub/xhci/patches/0005-xhci/0004-grub-core-bus-usb-Add-function-pointer-for-attach-de.patch @@ -0,0 +1,121 @@ +From 89701aba00caa81bb566ab10da0c89264393be30 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 4/8] 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/0005-xhci/0005-grub-core-bus-usb-usbhub-Add-new-private-fields-for-.patch b/config/grub/xhci/patches/0005-xhci/0005-grub-core-bus-usb-usbhub-Add-new-private-fields-for-.patch new file mode 100644 index 00000000..7d69c3a6 --- /dev/null +++ b/config/grub/xhci/patches/0005-xhci/0005-grub-core-bus-usb-usbhub-Add-new-private-fields-for-.patch @@ -0,0 +1,77 @@ +From 5e5d74a4531770258e21dedd45c33f1a9d3afa6b 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 5/8] 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/0005-xhci/0006-grub-core-bus-usb-Add-xhci-support.patch b/config/grub/xhci/patches/0005-xhci/0006-grub-core-bus-usb-Add-xhci-support.patch new file mode 100644 index 00000000..11df42d8 --- /dev/null +++ b/config/grub/xhci/patches/0005-xhci/0006-grub-core-bus-usb-Add-xhci-support.patch @@ -0,0 +1,2814 @@ +From fe3a0bce527e059e9121eb5ad2c3cc099f07a4bf 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 6/8] 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 fb6078a34..64c3806ab 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/0005-xhci/0007-grub-core-bus-usb-usbhub-Add-xHCI-non-root-hub-suppo.patch b/config/grub/xhci/patches/0005-xhci/0007-grub-core-bus-usb-usbhub-Add-xHCI-non-root-hub-suppo.patch new file mode 100644 index 00000000..a37bbd6b --- /dev/null +++ b/config/grub/xhci/patches/0005-xhci/0007-grub-core-bus-usb-usbhub-Add-xHCI-non-root-hub-suppo.patch @@ -0,0 +1,127 @@ +From 2a2c64f6ea62337c1263a70f6ca9a9bade66b78b 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 7/8] 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/0005-xhci/0008-Fix-compilation-on-x86_64.patch b/config/grub/xhci/patches/0005-xhci/0008-Fix-compilation-on-x86_64.patch new file mode 100644 index 00000000..af79c3d0 --- /dev/null +++ b/config/grub/xhci/patches/0005-xhci/0008-Fix-compilation-on-x86_64.patch @@ -0,0 +1,90 @@ +From 871d768f8c5c960cb0d9761a9028b16882e1a7d3 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 8/8] 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/0006-nvme/0001-Add-native-NVMe-driver-based-on-SeaBIOS.patch b/config/grub/xhci/patches/0006-nvme/0001-Add-native-NVMe-driver-based-on-SeaBIOS.patch new file mode 100644 index 00000000..b00611a0 --- /dev/null +++ b/config/grub/xhci/patches/0006-nvme/0001-Add-native-NVMe-driver-based-on-SeaBIOS.patch @@ -0,0 +1,1074 @@ +From ef9d61ae9ed490301adf9ee064a9fc1190069d81 Mon Sep 17 00:00:00 2001 +From: Mate Kukri <km@mkukri.xyz> +Date: Mon, 20 May 2024 11:43:35 +0100 +Subject: 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 ea782666d..0f893369a 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2602,3 +2602,9 @@ module = { +   efi = commands/bli.c; +   enable = efi; + }; ++ ++module = { ++  name = nvme; ++  common = disk/nvme.c; ++  enable = pci; ++}; +diff --git a/grub-core/commands/nativedisk.c b/grub-core/commands/nativedisk.c +index 580c8d3b0..a2c766fbd 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 + | 
