From 0b37653ab908093a108cfe3cbcb4e8d378428d26 Mon Sep 17 00:00:00 2001 From: Leah Rowe Date: Wed, 12 Jun 2024 00:58:22 +0100 Subject: grub: only enable nvme if needed on a board remove nvme support from the "default" grub tree now there are three trees: * default: no xhci or nvme patches * nvme: contains nvme support * xhci: contains xhci and nvme support this is in case a bug like lbmk issue #216 ever occurs again, as referenced before during lbmk audit 5 there is no indication that the nvme patch causes any issues, but after previous experience i want to be sure Signed-off-by: Leah Rowe --- config/coreboot/g43t_am3/target.cfg | 1 + config/coreboot/g43t_am3_16mb/target.cfg | 1 + config/coreboot/ga_g41m_es2l/target.cfg | 1 + config/coreboot/hp8200sff_4mb/target.cfg | 1 + config/coreboot/hp8200sff_8mb/target.cfg | 1 + config/coreboot/hp8300cmt_16mb/target.cfg | 1 + config/coreboot/kcma_d8-udimm_16mb/target.cfg | 1 + config/coreboot/kcma_d8_rdimm_16mb/target.cfg | 1 + config/coreboot/kcma_d8_rdimm_2mb/target.cfg | 1 + config/coreboot/kcma_d8_udimm_2mb/target.cfg | 1 + config/coreboot/kgpe_d16_rdimm_16mb/target.cfg | 1 + config/coreboot/kgpe_d16_rdimm_2mb/target.cfg | 1 + config/coreboot/kgpe_d16_udimm_16mb/target.cfg | 1 + config/coreboot/kgpe_d16_udimm_2mb/target.cfg | 1 + config/coreboot/t1650_12mb/target.cfg | 1 + config/data/grub/module/default | 1 - config/data/grub/module/nvme | 156 ++ config/grub/default/config/payload | 20 +- ...1-Add-native-NVMe-driver-based-on-SeaBIOS.patch | 1074 -------- config/grub/nvme/config/payload | 289 +++ ...ub-s-missing-characters-for-borders-arrow.patch | 90 + ...2-say-the-name-libreboot-in-the-grub-menu.patch | 25 + .../patches/0002-luks2/0003-Add-CC0-license.patch | 42 + .../0002-luks2/0004-Define-GRUB_UINT32_MAX.patch | 39 + .../0002-luks2/0005-Add-Argon2-algorithm.patch | 2611 ++++++++++++++++++++ ...0006-Error-on-missing-Argon2id-parameters.patch | 58 + .../0007-Compile-with-Argon2id-support.patch | 83 + .../0008-Make-grub-install-work-with-Argon2.patch | 26 + ...board-coreboot-force-scancodes2-translate.patch | 107 + ...eylayouts-don-t-print-Unknown-key-message.patch | 38 + ...print-missing-prefix-errors-on-the-screen.patch | 102 + ...002-don-t-print-error-if-module-not-found.patch | 34 + .../0003-don-t-print-empty-error-messages.patch | 31 + ...1-Add-native-NVMe-driver-based-on-SeaBIOS.patch | 1074 ++++++++ config/grub/nvme/target.cfg | 5 + config/submodule/grub/nvme/gnulib/module.cfg | 3 + config/submodule/grub/nvme/module.list | 1 + 37 files changed, 4830 insertions(+), 1094 deletions(-) create mode 100755 config/data/grub/module/nvme delete mode 100644 config/grub/default/patches/0006-nvme/0001-Add-native-NVMe-driver-based-on-SeaBIOS.patch create mode 100644 config/grub/nvme/config/payload create mode 100644 config/grub/nvme/patches/0001-borderfix/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch create mode 100644 config/grub/nvme/patches/0001-borderfix/0002-say-the-name-libreboot-in-the-grub-menu.patch create mode 100644 config/grub/nvme/patches/0002-luks2/0003-Add-CC0-license.patch create mode 100644 config/grub/nvme/patches/0002-luks2/0004-Define-GRUB_UINT32_MAX.patch create mode 100644 config/grub/nvme/patches/0002-luks2/0005-Add-Argon2-algorithm.patch create mode 100644 config/grub/nvme/patches/0002-luks2/0006-Error-on-missing-Argon2id-parameters.patch create mode 100644 config/grub/nvme/patches/0002-luks2/0007-Compile-with-Argon2id-support.patch create mode 100644 config/grub/nvme/patches/0002-luks2/0008-Make-grub-install-work-with-Argon2.patch create mode 100644 config/grub/nvme/patches/0003-keyboardfix/0001-at_keyboard-coreboot-force-scancodes2-translate.patch create mode 100644 config/grub/nvme/patches/0003-keyboardfix/0002-keylayouts-don-t-print-Unknown-key-message.patch create mode 100644 config/grub/nvme/patches/0004-prefix/0001-don-t-print-missing-prefix-errors-on-the-screen.patch create mode 100644 config/grub/nvme/patches/0004-prefix/0002-don-t-print-error-if-module-not-found.patch create mode 100644 config/grub/nvme/patches/0004-prefix/0003-don-t-print-empty-error-messages.patch create mode 100644 config/grub/nvme/patches/0006-nvme/0001-Add-native-NVMe-driver-based-on-SeaBIOS.patch create mode 100644 config/grub/nvme/target.cfg create mode 100644 config/submodule/grub/nvme/gnulib/module.cfg create mode 100644 config/submodule/grub/nvme/module.list diff --git a/config/coreboot/g43t_am3/target.cfg b/config/coreboot/g43t_am3/target.cfg index 13f2a260..f0e838fb 100644 --- a/config/coreboot/g43t_am3/target.cfg +++ b/config/coreboot/g43t_am3/target.cfg @@ -3,3 +3,4 @@ xarch="i386-elf" payload_seabios="y" payload_memtest="y" grub_timeout=10 +grubtree="nvme" diff --git a/config/coreboot/g43t_am3_16mb/target.cfg b/config/coreboot/g43t_am3_16mb/target.cfg index bbe8a9fb..533d8b5a 100644 --- a/config/coreboot/g43t_am3_16mb/target.cfg +++ b/config/coreboot/g43t_am3_16mb/target.cfg @@ -4,3 +4,4 @@ payload_seabios="y" payload_memtest="y" grub_timeout=10 release="n" +grubtree="nvme" diff --git a/config/coreboot/ga_g41m_es2l/target.cfg b/config/coreboot/ga_g41m_es2l/target.cfg index 13f2a260..f0e838fb 100644 --- a/config/coreboot/ga_g41m_es2l/target.cfg +++ b/config/coreboot/ga_g41m_es2l/target.cfg @@ -3,3 +3,4 @@ xarch="i386-elf" payload_seabios="y" payload_memtest="y" grub_timeout=10 +grubtree="nvme" diff --git a/config/coreboot/hp8200sff_4mb/target.cfg b/config/coreboot/hp8200sff_4mb/target.cfg index 950a1223..82169ae7 100644 --- a/config/coreboot/hp8200sff_4mb/target.cfg +++ b/config/coreboot/hp8200sff_4mb/target.cfg @@ -5,3 +5,4 @@ payload_seabios_withgrub="y" payload_memtest="y" grub_timeout=10 grub_scan_disk="nvme ahci" +grubtree="nvme" diff --git a/config/coreboot/hp8200sff_8mb/target.cfg b/config/coreboot/hp8200sff_8mb/target.cfg index 950a1223..82169ae7 100644 --- a/config/coreboot/hp8200sff_8mb/target.cfg +++ b/config/coreboot/hp8200sff_8mb/target.cfg @@ -5,3 +5,4 @@ payload_seabios_withgrub="y" payload_memtest="y" grub_timeout=10 grub_scan_disk="nvme ahci" +grubtree="nvme" diff --git a/config/coreboot/hp8300cmt_16mb/target.cfg b/config/coreboot/hp8300cmt_16mb/target.cfg index 950a1223..82169ae7 100644 --- a/config/coreboot/hp8300cmt_16mb/target.cfg +++ b/config/coreboot/hp8300cmt_16mb/target.cfg @@ -5,3 +5,4 @@ payload_seabios_withgrub="y" payload_memtest="y" grub_timeout=10 grub_scan_disk="nvme ahci" +grubtree="nvme" diff --git a/config/coreboot/kcma_d8-udimm_16mb/target.cfg b/config/coreboot/kcma_d8-udimm_16mb/target.cfg index 1dc344a6..749c4e46 100644 --- a/config/coreboot/kcma_d8-udimm_16mb/target.cfg +++ b/config/coreboot/kcma_d8-udimm_16mb/target.cfg @@ -7,3 +7,4 @@ payload_memtest="y" xlang="c" grub_timout=10 grub_scan_disk="nvme ahci" +grubtree="nvme" diff --git a/config/coreboot/kcma_d8_rdimm_16mb/target.cfg b/config/coreboot/kcma_d8_rdimm_16mb/target.cfg index b5ebb46e..80d2018e 100644 --- a/config/coreboot/kcma_d8_rdimm_16mb/target.cfg +++ b/config/coreboot/kcma_d8_rdimm_16mb/target.cfg @@ -6,3 +6,4 @@ payload_memtest="y" xlang="c" grub_timeout=10 grub_scan_disk="nvme ahci" +grubtree="nvme" diff --git a/config/coreboot/kcma_d8_rdimm_2mb/target.cfg b/config/coreboot/kcma_d8_rdimm_2mb/target.cfg index eaee5b1a..744b6e10 100644 --- a/config/coreboot/kcma_d8_rdimm_2mb/target.cfg +++ b/config/coreboot/kcma_d8_rdimm_2mb/target.cfg @@ -5,3 +5,4 @@ payload_seabios_withgrub="y" payload_memtest="y" xlang="c" grub_scan_disk="nvme ahci" +grubtree="nvme" diff --git a/config/coreboot/kcma_d8_udimm_2mb/target.cfg b/config/coreboot/kcma_d8_udimm_2mb/target.cfg index 852b9e63..d4079eb4 100644 --- a/config/coreboot/kcma_d8_udimm_2mb/target.cfg +++ b/config/coreboot/kcma_d8_udimm_2mb/target.cfg @@ -7,3 +7,4 @@ payload_memtest="y" xlang="c" grub_timeout=10 grub_scan_disk="nvme ahci" +grubtree="nvme" diff --git a/config/coreboot/kgpe_d16_rdimm_16mb/target.cfg b/config/coreboot/kgpe_d16_rdimm_16mb/target.cfg index b5ebb46e..80d2018e 100644 --- a/config/coreboot/kgpe_d16_rdimm_16mb/target.cfg +++ b/config/coreboot/kgpe_d16_rdimm_16mb/target.cfg @@ -6,3 +6,4 @@ payload_memtest="y" xlang="c" grub_timeout=10 grub_scan_disk="nvme ahci" +grubtree="nvme" diff --git a/config/coreboot/kgpe_d16_rdimm_2mb/target.cfg b/config/coreboot/kgpe_d16_rdimm_2mb/target.cfg index b5ebb46e..80d2018e 100644 --- a/config/coreboot/kgpe_d16_rdimm_2mb/target.cfg +++ b/config/coreboot/kgpe_d16_rdimm_2mb/target.cfg @@ -6,3 +6,4 @@ payload_memtest="y" xlang="c" grub_timeout=10 grub_scan_disk="nvme ahci" +grubtree="nvme" diff --git a/config/coreboot/kgpe_d16_udimm_16mb/target.cfg b/config/coreboot/kgpe_d16_udimm_16mb/target.cfg index 852b9e63..d4079eb4 100644 --- a/config/coreboot/kgpe_d16_udimm_16mb/target.cfg +++ b/config/coreboot/kgpe_d16_udimm_16mb/target.cfg @@ -7,3 +7,4 @@ payload_memtest="y" xlang="c" grub_timeout=10 grub_scan_disk="nvme ahci" +grubtree="nvme" diff --git a/config/coreboot/kgpe_d16_udimm_2mb/target.cfg b/config/coreboot/kgpe_d16_udimm_2mb/target.cfg index 852b9e63..d4079eb4 100644 --- a/config/coreboot/kgpe_d16_udimm_2mb/target.cfg +++ b/config/coreboot/kgpe_d16_udimm_2mb/target.cfg @@ -7,3 +7,4 @@ payload_memtest="y" xlang="c" grub_timeout=10 grub_scan_disk="nvme ahci" +grubtree="nvme" diff --git a/config/coreboot/t1650_12mb/target.cfg b/config/coreboot/t1650_12mb/target.cfg index ebf3fca1..a2b3d31f 100644 --- a/config/coreboot/t1650_12mb/target.cfg +++ b/config/coreboot/t1650_12mb/target.cfg @@ -4,3 +4,4 @@ payload_seabios="y" payload_seabios_withgrub="y" payload_memtest="y" grub_scan_disk="nvme ahci" +grubtree="nvme" diff --git a/config/data/grub/module/default b/config/data/grub/module/default index 503c61c8..3555ad11 100755 --- a/config/data/grub/module/default +++ b/config/data/grub/module/default @@ -103,7 +103,6 @@ multiboot2 \ nativedisk \ normal \ ntfs \ -nvme \ ohci \ part_bsd \ part_dfly \ diff --git a/config/data/grub/module/nvme b/config/data/grub/module/nvme new file mode 100755 index 00000000..503c61c8 --- /dev/null +++ b/config/data/grub/module/nvme @@ -0,0 +1,156 @@ +# Install modules (installed, but not automatically loaded) +grub_install_modules=" \ +hexdump \ +newc \ +odc \ +usbserial_common \ +usbserial_ftdi \ +usbserial_pl2303 \ +usbserial_usbdebug \ +video_colors \ +" +# Modules (and always loaded) +grub_modules=" \ +acpi \ +ahci \ +at_keyboard \ +all_video \ +ata \ +bitmap \ +bitmap_scale \ +boot \ +bsd \ +btrfs \ +cat \ +cbfs \ +cbls \ +cbmemc \ +cbtime \ +chain \ +configfile \ +cpio \ +cpio_be \ +crc64 \ +crypto \ +cryptodisk \ +diskfilter \ +echo \ +ehci \ +eval \ +exfat \ +elf \ +ext2 \ +fat \ +f2fs \ +gcry_arcfour \ +gcry_blowfish \ +gcry_camellia \ +gcry_cast5 \ +gcry_crc \ +gcry_des \ +gcry_dsa \ +gcry_idea \ +gcry_md4 \ +gcry_md5 \ +gcry_rfc2268 \ +gcry_rijndael \ +gcry_rmd160 \ +gcry_rsa \ +gcry_seed \ +gcry_serpent \ +gcry_sha1 \ +gcry_sha256 \ +gcry_sha512 \ +gcry_tiger \ +gcry_twofish \ +gcry_whirlpool \ +geli \ +gfxmenu \ +gfxterm_background \ +gfxterm_menu \ +gzio \ +hashsum \ +halt \ +help \ +iorw \ +iso9660 \ +jpeg \ +json \ +keylayouts \ +keystatus \ +linux \ +linux16 \ +loadenv \ +loopback \ +ls \ +lsacpi \ +lsmmap \ +lspci \ +luks \ +luks2 \ +argon2 \ +lvm \ +lzopio \ +mdraid09 \ +mdraid09_be \ +mdraid1x \ +memdisk \ +memrw \ +minicmd \ +mmap \ +multiboot \ +multiboot2 \ +nativedisk \ +normal \ +ntfs \ +nvme \ +ohci \ +part_bsd \ +part_dfly \ +part_gpt \ +part_msdos \ +password \ +password_pbkdf2 \ +pata \ +pbkdf2 \ +pcidump \ +pgp \ +play \ +png \ +procfs \ +raid5rec \ +raid6rec \ +read \ +reboot \ +regexp \ +romfs \ +scsi \ +search \ +search_fs_file \ +search_fs_uuid \ +search_label \ +serial \ +syslinuxcfg \ +setjmp \ +setpci \ +spkmodem \ +squash4 \ +sleep \ +tar \ +test \ +true \ +uhci \ +udf \ +ufs1 \ +ufs1_be \ +ufs2 \ +usb \ +usb_keyboard \ +usbms \ +xfs \ +xzio \ +zfs \ +zfscrypt \ +zfsinfo \ +zstd \ +" diff --git a/config/grub/default/config/payload b/config/grub/default/config/payload index f81d5930..a405ae25 100644 --- a/config/grub/default/config/payload +++ b/config/grub/default/config/payload @@ -54,7 +54,7 @@ if [ -f (cbfsdisk)/timeout.cfg ]; then else set timeout=5 fi -set grub_scan_disk="nvme ahci ata" +set grub_scan_disk="ahci ata" if [ -f (cbfsdisk)/scan.cfg ]; then source (cbfsdisk)/scan.cfg fi @@ -87,17 +87,11 @@ function search_grub { for part in 1 2 3 4 5 6 7 8 9 10 11 12; do if [ "${1}" != "nvme" ]; then try_user_config "(${1}${i},${part})" - else - # TODO: do we care about other namesapces - try_user_config "(nvme${i}n1,${part})" fi done if [ "${1}" != "nvme" ]; then # raw devices e.g. (ahci0) instead of (ahci0,1) try_user_config "(${1}${i})" - else - # TODO: do we care about other namesapces - try_user_config "(nvme${i}n1)" fi done echo # Insert newline @@ -123,17 +117,11 @@ function search_isolinux { for part in 1 2 3 4 5 6 7 8 9 10 11 12; do if [ "${1}" != "nvme" ]; then try_isolinux_config "(${1}${i},${part})" - else - # TODO: see above - try_isolinux_config "(nvme${i}n1,${part})" fi done if [ "${1}" != "nvme" ]; then # raw devices e.g. (usb0) instead of (usb0,1) try_isolinux_config "(${1}${i})" - else - # TODO: do we care about other namesapces - try_isolinux_config "(nvme${i}n1)" fi done echo # Insert newline @@ -175,9 +163,6 @@ menuentry 'Load Operating System (incl. fully encrypted disks) [o]' --hotkey='o bootdev="${bootdev} (ahci${i},${part})" elif [ "${grub_disk}" = "ata" ]; then bootdev="${bootdev} (ata${i},${part})" - elif [ "${grub_disk}" = "nvme" ]; then - # TODO: do we care about other namesapces - bootdev="${bootdev} (nvme${i}n1,${part})" fi done done @@ -214,9 +199,6 @@ menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on AHCI [a]' --hotkey='a' menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on ATA/IDE [d]' --hotkey='d' { search_bootcfg ata } -menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on NVMe [e]' --hotkey='e' { - search_bootcfg nvme -} if [ -f (cbfsdisk)/grub.cfg ]; then menuentry 'Load configuration (grub.cfg) in CBFS [t]' --hotkey='t' { set root='(cbfsdisk)' diff --git a/config/grub/default/patches/0006-nvme/0001-Add-native-NVMe-driver-based-on-SeaBIOS.patch b/config/grub/default/patches/0006-nvme/0001-Add-native-NVMe-driver-based-on-SeaBIOS.patch deleted file mode 100644 index 4345ebae..00000000 --- a/config/grub/default/patches/0006-nvme/0001-Add-native-NVMe-driver-based-on-SeaBIOS.patch +++ /dev/null @@ -1,1074 +0,0 @@ -From 708c0092d44aec6409e0ecc1925cdbb3b76c6dfd Mon Sep 17 00:00:00 2001 -From: Mate Kukri -Date: Mon, 20 May 2024 11:43:35 +0100 -Subject: [PATCH 1/1] Add native NVMe driver based on SeaBIOS - -Tested to successfully boot Debian on QEMU and OptiPlex 3050. - -Signed-off-by: Mate Kukri ---- - Makefile.am | 2 +- - grub-core/Makefile.core.def | 6 + - grub-core/commands/nativedisk.c | 1 + - grub-core/disk/nvme-int.h | 208 +++++++++ - grub-core/disk/nvme.c | 781 ++++++++++++++++++++++++++++++++ - include/grub/disk.h | 1 + - 6 files changed, 998 insertions(+), 1 deletion(-) - create mode 100644 grub-core/disk/nvme-int.h - create mode 100644 grub-core/disk/nvme.c - -diff --git a/Makefile.am b/Makefile.am -index 43635d5ff..2c86dbbf6 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -434,7 +434,7 @@ if COND_i386_coreboot - FS_PAYLOAD_MODULES ?= $(shell cat grub-core/fs.lst) - default_payload.elf: grub-mkstandalone grub-mkimage FORCE - test -f $@ && rm $@ || true -- pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg -+ pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata nvme ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg - endif - - endif -diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def -index 560c66447..d3bf9f1c5 100644 ---- a/grub-core/Makefile.core.def -+++ b/grub-core/Makefile.core.def -@@ -2607,3 +2607,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 6806bff9c..fd68a513e 100644 ---- a/grub-core/commands/nativedisk.c -+++ b/grub-core/commands/nativedisk.c -@@ -78,6 +78,7 @@ get_uuid (const char *name, char **uuid, int getnative) - case GRUB_DISK_DEVICE_ATA_ID: - case GRUB_DISK_DEVICE_SCSI_ID: - case GRUB_DISK_DEVICE_XEN: -+ case GRUB_DISK_DEVICE_NVME_ID: - if (getnative) - break; - /* FALLTHROUGH */ -diff --git a/grub-core/disk/nvme-int.h b/grub-core/disk/nvme-int.h -new file mode 100644 -index 000000000..1295b58aa ---- /dev/null -+++ b/grub-core/disk/nvme-int.h -@@ -0,0 +1,208 @@ -+// NVMe datastructures and constants -+// -+// Copyright 2017 Amazon.com, Inc. or its affiliates. -+// -+// This file may be distributed under the terms of the GNU LGPLv3 license. -+ -+#ifndef __NVME_INT_H -+#define __NVME_INT_H -+ -+#include -+ -+/* 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 -+#include -+#include -+#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 - diff --git a/config/grub/nvme/config/payload b/config/grub/nvme/config/payload new file mode 100644 index 00000000..f81d5930 --- /dev/null +++ b/config/grub/nvme/config/payload @@ -0,0 +1,289 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright (C) 2014-2016,2020-2021,2023-2024 Leah Rowe +# Copyright (C) 2015 Klemens Nanni + +set prefix=(memdisk)/boot/grub + +insmod at_keyboard +insmod usb_keyboard +insmod nativedisk +insmod ehci +insmod ohci +insmod uhci +insmod usb +insmod usbms +insmod regexp + +terminal_input --append at_keyboard +terminal_input --append usb_keyboard +terminal_output --append cbmemc + +# User interface overrides wherever "keystatus" is supported +# Keep SHIFT key pressed before powering on to disable graphics +if keystatus --shift; then + terminal_output --append vga_text +else + gfxpayload=keep + terminal_output --append gfxterm + + if [ -f (cbfsdisk)/background.png ]; then + insmod png + background_image (cbfsdisk)/background.png + elif [ -f (cbfsdisk)/background.jpg ]; then + insmod jpeg + background_image (cbfsdisk)/background.jpg + fi +fi + +# Keep CTRL pressed to enable default serial terminal (COM1 or the like) +if keystatus --ctrl; then + serial + terminal_input --append serial + terminal_output --append serial +fi + +# Keep ALT pressed to enable spkmodem +if keystatus --alt; then + terminal_output --append spkmodem +fi + + +set default="0" +if [ -f (cbfsdisk)/timeout.cfg ]; then + source (cbfsdisk)/timeout.cfg +else + set timeout=5 +fi +set grub_scan_disk="nvme ahci ata" +if [ -f (cbfsdisk)/scan.cfg ]; then + source (cbfsdisk)/scan.cfg +fi + +if [ -f (cbfsdisk)/keymap.gkb ]; then + keymap (cbfsdisk)/keymap.gkb +fi + +function really_try_user_config { + set root="${1}" + + if [ -f /"${2}"/grub.cfg ]; then + unset superusers + configfile /"${2}"/grub.cfg + fi +} + +function try_user_config { + # The @/... entries are for cases where the BTRFS filesystem is being used + for dir in grub boot/grub @/grub @/boot/grub grub2 boot/grub2 @/grub2 @/boot/grub2 boot @/boot; do + really_try_user_config "${1}" "${dir}" + done + for dir in ubuntu debian redhat; do + really_try_user_config "${1}" "EFI/${dir}" + done +} +function search_grub { + echo -n "Attempting to load grub.cfg from '${1}' devices" + for i in 0 1 2 3 4 5 6 7 8; do + for part in 1 2 3 4 5 6 7 8 9 10 11 12; do + if [ "${1}" != "nvme" ]; then + try_user_config "(${1}${i},${part})" + else + # TODO: do we care about other namesapces + try_user_config "(nvme${i}n1,${part})" + fi + done + if [ "${1}" != "nvme" ]; then + # raw devices e.g. (ahci0) instead of (ahci0,1) + try_user_config "(${1}${i})" + else + # TODO: do we care about other namesapces + try_user_config "(nvme${i}n1)" + fi + done + echo # Insert newline +} + +function try_isolinux_config { + set root="${1}" + for dir in '' /boot /EFI /@ /@/boot; do + if [ -f "${dir}"/isolinux/isolinux.cfg ]; then + syslinux_configfile -i "${dir}"/isolinux/isolinux.cfg + elif [ -f "${dir}"/syslinux/syslinux.cfg ]; then + syslinux_configfile -s "${dir}"/syslinux/syslinux.cfg + elif [ -f "${dir}"/syslinux/extlinux.conf ]; then + syslinux_configfile -s "${dir}"/syslinux/extlinux.conf + elif [ -f "${dir}"/extlinux/extlinux.conf ]; then + syslinux_configfile -s "${dir}"/extlinux/extlinux.conf + fi + done +} +function search_isolinux { + echo "\nAttempting to parse iso/sys/extlinux config from '${1}' devices" + for i in 0 1 2 3 4 5 6 7 8; do + for part in 1 2 3 4 5 6 7 8 9 10 11 12; do + if [ "${1}" != "nvme" ]; then + try_isolinux_config "(${1}${i},${part})" + else + # TODO: see above + try_isolinux_config "(nvme${i}n1,${part})" + fi + done + if [ "${1}" != "nvme" ]; then + # raw devices e.g. (usb0) instead of (usb0,1) + try_isolinux_config "(${1}${i})" + else + # TODO: do we care about other namesapces + try_isolinux_config "(nvme${i}n1)" + fi + done + echo # Insert newline +} +function try_bootcfg { + try_user_config "${1}" + try_isolinux_config "${1}" +} +function search_bootcfg { + search_grub "${1}" + search_isolinux "${1}" +} +menuentry 'Load Operating System (incl. fully encrypted disks) [o]' --hotkey='o' { + + for grub_disk in ${grub_scan_disk}; do + search_bootcfg ${grub_disk} + done + + # grub device enumeration is very slow, so checks are hardcoded + + # TODO: add more strings, based on what distros set up when + # the user select auto-partitioning on those installers + lvmvol="lvm/grubcrypt-bootvol lvm/grubcrypt-rootvol" + + raidvol="md/0 md/1 md/2 md/3 md/4 md/5 md/6 md/7 md/8 md/9" + + # in practise, doing multiple redundant checks is perfectly fast and + # TODO: optimize grub itself, and use */? here for everything + + for vol in ${lvmvol} ${raidvol} ; do + try_bootcfg "${vol}" + done + + unset bootdev + for grub_disk in ${grub_scan_disk}; do + for i in 0 1 2 3 4 5 6 7 8; do + for part in 1 2 3 4 5 6 7 8 9 10 11 12; do + if [ "${grub_disk}" = "ahci" ]; then + bootdev="${bootdev} (ahci${i},${part})" + elif [ "${grub_disk}" = "ata" ]; then + bootdev="${bootdev} (ata${i},${part})" + elif [ "${grub_disk}" = "nvme" ]; then + # TODO: do we care about other namesapces + bootdev="${bootdev} (nvme${i}n1,${part})" + fi + done + done + done + + set pager=0 + echo -n "Attempting to unlock encrypted volumes" + for dev in ${bootdev} ${lvmvol} ${raidvol}; do + if cryptomount "${dev}" ; then break ; fi + done + set pager=1 + echo + + # after cryptomount, lvm volumes might be available + for vol in ${lvmvol}; do + try_bootcfg "${vol}" + done + + search_bootcfg crypto + + for vol in lvm/* ; do + try_bootcfg "${vol}" + done + + true # Prevent pager requiring to accept each line instead of whole screen +} + +menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on USB [s]' --hotkey='s' { + search_bootcfg usb +} +menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on AHCI [a]' --hotkey='a' { + search_bootcfg ahci +} +menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on ATA/IDE [d]' --hotkey='d' { + search_bootcfg ata +} +menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on NVMe [e]' --hotkey='e' { + search_bootcfg nvme +} +if [ -f (cbfsdisk)/grub.cfg ]; then +menuentry 'Load configuration (grub.cfg) in CBFS [t]' --hotkey='t' { + set root='(cbfsdisk)' + if [ -f /grub.cfg ]; then + configfile /grub.cfg + fi +} +fi +if [ -f (cbfsdisk)/grubtest.cfg ]; then +menuentry 'Load test configuration (grubtest.cfg) inside of CBFS [t]' --hotkey='t' { + set root='(cbfsdisk)' + if [ -f /grubtest.cfg ]; then + configfile /grubtest.cfg + fi +} +fi +if [ -f (cbfsdisk)/seabios.elf ]; then +menuentry 'Load SeaBIOS (payload) [b]' --hotkey='b' { + set root='cbfsdisk' + chainloader /seabios.elf +} +fi +if [ -f (cbfsdisk)/img/grub2 ]; then +menuentry 'Return to SeaBIOS [b]' --hotkey='b' { + set root='cbfsdisk' + chainloader /fallback/payload +} +fi +menuentry 'Poweroff [p]' --hotkey='p' { + halt +} +menuentry 'Reboot [r]' --hotkey='r' { + reboot +} +if [ -f (cbfsdisk)/img/memtest ]; then +menuentry 'Load MemTest86+ [m]' --hotkey='m' { + set root='cbfsdisk' + chainloader /img/memtest +} +fi + +submenu 'Other [z]' --hotkey='z' { + menuentry 'Enable default serial terminal [s]' --hotkey='s' { + serial + terminal_input --append serial + terminal_output --append serial + } + + menuentry 'Disable default serial terminal' { + terminal_input --remove serial + terminal_output --remove serial + } + + menuentry 'Enable gfxterm' { + terminal_output --append gfxterm + terminal_output --remove vga_text + } + menuentry 'Disable gfxterm [g]' --hotkey='g' { + terminal_output --remove gfxterm + terminal_output --append vga_text + } + + menuentry 'Enable spkmodem [a]' --hotkey='a' { + terminal_output --append spkmodem + } + + menuentry 'Disable spkmodem [z]' --hotkey='z' { + terminal_output --remove spkmodem + } +} diff --git a/config/grub/nvme/patches/0001-borderfix/0001-mitigate-grub-s-missing-characters-for-borders-arrow.patch b/config/grub/nvme/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/nvme/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 +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/nvme/patches/0001-borderfix/0002-say-the-name-libreboot-in-the-grub-menu.patch b/config/grub/nvme/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/nvme/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 +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/nvme/patches/0002-luks2/0003-Add-CC0-license.patch b/config/grub/nvme/patches/0002-luks2/0003-Add-CC0-license.patch new file mode 100644 index 00000000..dc9060c3 --- /dev/null +++ b/config/grub/nvme/patches/0002-luks2/0003-Add-CC0-license.patch @@ -0,0 +1,42 @@ +From de6e7cc62522ce1be21bd2f06e7c15cd234b5426 Mon Sep 17 00:00:00 2001 +From: Ax333l +Date: Thu, 17 Aug 2023 00:00:00 +0000 +Subject: [PATCH 1/6] Add CC0 license + +Signed-off-by: Nicholas Johnson +--- + 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/nvme/patches/0002-luks2/0004-Define-GRUB_UINT32_MAX.patch b/config/grub/nvme/patches/0002-luks2/0004-Define-GRUB_UINT32_MAX.patch new file mode 100644 index 00000000..be875e67 --- /dev/null +++ b/config/grub/nvme/patches/0002-luks2/0004-Define-GRUB_UINT32_MAX.patch @@ -0,0 +1,39 @@ +From 9edaaffac91d593a439e44bac3b6f5558f5a8245 Mon Sep 17 00:00:00 2001 +From: Ax333l +Date: Thu, 17 Aug 2023 00:00:00 +0000 +Subject: [PATCH 2/6] Define GRUB_UINT32_MAX + +Signed-off-by: Nicholas Johnson +--- + 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/nvme/patches/0002-luks2/0005-Add-Argon2-algorithm.patch b/config/grub/nvme/patches/0002-luks2/0005-Add-Argon2-algorithm.patch new file mode 100644 index 00000000..1c6b4f04 --- /dev/null +++ b/config/grub/nvme/patches/0002-luks2/0005-Add-Argon2-algorithm.patch @@ -0,0 +1,2611 @@ +From 5b63da5c4267933f573ca37ce39a073098c443ba Mon Sep 17 00:00:00 2001 +From: Ax333l +Date: Thu, 17 Aug 2023 00:00:00 +0000 +Subject: [PATCH 3/6] Add Argon2 algorithm + +Signed-off-by: Nicholas Johnson +--- + 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 and to "argon2.h". ++@item Add include 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 ++ ++#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 ++#include ++ ++#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 ++#include /* 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.. 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 ++ */ ++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/nvme/patches/0002-luks2/0006-Error-on-missing-Argon2id-parameters.patch b/config/grub/nvme/patches/0002-luks2/0006-Error-on-missing-Argon2id-parameters.patch new file mode 100644 index 00000000..5d56bd61 --- /dev/null +++ b/config/grub/nvme/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 +Date: Thu, 17 Aug 2023 00:00:00 +0000 +Subject: [PATCH 4/6] Error on missing Argon2id parameters + +Signed-off-by: Nicholas Johnson +--- + 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/nvme/patches/0002-luks2/0007-Compile-with-Argon2id-support.patch b/config/grub/nvme/patches/0002-luks2/0007-Compile-with-Argon2id-support.patch new file mode 100644 index 00000000..f2e26fd4 --- /dev/null +++ b/config/grub/nvme/patches/0002-luks2/0007-Compile-with-Argon2id-support.patch @@ -0,0 +1,83 @@ +From 0a21695c55f76f1c958bb633481d55b3168562f7 Mon Sep 17 00:00:00 2001 +From: Ax333l +Date: Thu, 17 Aug 2023 00:00:00 +0000 +Subject: [PATCH 5/6] Compile with Argon2id support + +Signed-off-by: Nicholas Johnson +--- + 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 + #include + ++#include + #include + #include + +@@ -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/nvme/patches/0002-luks2/0008-Make-grub-install-work-with-Argon2.patch b/config/grub/nvme/patches/0002-luks2/0008-Make-grub-install-work-with-Argon2.patch new file mode 100644 index 00000000..dc65b7a6 --- /dev/null +++ b/config/grub/nvme/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 +Date: Thu, 17 Aug 2023 00:00:00 +0000 +Subject: [PATCH 6/6] Make grub-install work with Argon2 + +Signed-off-by: Nicholas Johnson +--- + 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/nvme/patches/0003-keyboardfix/0001-at_keyboard-coreboot-force-scancodes2-translate.patch b/config/grub/nvme/patches/0003-keyboardfix/0001-at_keyboard-coreboot-force-scancodes2-translate.patch new file mode 100644 index 00000000..21e8630b --- /dev/null +++ b/config/grub/nvme/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 +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 +--- + 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/nvme/patches/0003-keyboardfix/0002-keylayouts-don-t-print-Unknown-key-message.patch b/config/grub/nvme/patches/0003-keyboardfix/0002-keylayouts-don-t-print-Unknown-key-message.patch new file mode 100644 index 00000000..fbef86a4 --- /dev/null +++ b/config/grub/nvme/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 +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 +--- + 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/nvme/patches/0004-prefix/0001-don-t-print-missing-prefix-errors-on-the-screen.patch b/config/grub/nvme/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/nvme/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 +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 +--- + 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/nvme/patches/0004-prefix/0002-don-t-print-error-if-module-not-found.patch b/config/grub/nvme/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/nvme/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 +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 +--- + 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/nvme/patches/0004-prefix/0003-don-t-print-empty-error-messages.patch b/config/grub/nvme/patches/0004-prefix/0003-don-t-print-empty-error-messages.patch new file mode 100644 index 00000000..25221c9c --- /dev/null +++ b/config/grub/nvme/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 +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 +--- + 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/nvme/patches/0006-nvme/0001-Add-native-NVMe-driver-based-on-SeaBIOS.patch b/config/grub/nvme/patches/0006-nvme/0001-Add-native-NVMe-driver-based-on-SeaBIOS.patch new file mode 100644 index 00000000..4345ebae --- /dev/null +++ b/config/grub/nvme/patches/0006-nvme/0001-Add-native-NVMe-driver-based-on-SeaBIOS.patch @@ -0,0 +1,1074 @@ +From 708c0092d44aec6409e0ecc1925cdbb3b76c6dfd Mon Sep 17 00:00:00 2001 +From: Mate Kukri +Date: Mon, 20 May 2024 11:43:35 +0100 +Subject: [PATCH 1/1] Add native NVMe driver based on SeaBIOS + +Tested to successfully boot Debian on QEMU and OptiPlex 3050. + +Signed-off-by: Mate Kukri +--- + Makefile.am | 2 +- + grub-core/Makefile.core.def | 6 + + grub-core/commands/nativedisk.c | 1 + + grub-core/disk/nvme-int.h | 208 +++++++++ + grub-core/disk/nvme.c | 781 ++++++++++++++++++++++++++++++++ + include/grub/disk.h | 1 + + 6 files changed, 998 insertions(+), 1 deletion(-) + create mode 100644 grub-core/disk/nvme-int.h + create mode 100644 grub-core/disk/nvme.c + +diff --git a/Makefile.am b/Makefile.am +index 43635d5ff..2c86dbbf6 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -434,7 +434,7 @@ if COND_i386_coreboot + FS_PAYLOAD_MODULES ?= $(shell cat grub-core/fs.lst) + default_payload.elf: grub-mkstandalone grub-mkimage FORCE + test -f $@ && rm $@ || true +- pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg ++ pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata nvme ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg + endif + + endif +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 560c66447..d3bf9f1c5 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2607,3 +2607,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 6806bff9c..fd68a513e 100644 +--- a/grub-core/commands/nativedisk.c ++++ b/grub-core/commands/nativedisk.c +@@ -78,6 +78,7 @@ get_uuid (const char *name, char **uuid, int getnative) + case GRUB_DISK_DEVICE_ATA_ID: + case GRUB_DISK_DEVICE_SCSI_ID: + case GRUB_DISK_DEVICE_XEN: ++ case GRUB_DISK_DEVICE_NVME_ID: + if (getnative) + break; + /* FALLTHROUGH */ +diff --git a/grub-core/disk/nvme-int.h b/grub-core/disk/nvme-int.h +new file mode 100644 +index 000000000..1295b58aa +--- /dev/null ++++ b/grub-core/disk/nvme-int.h +@@ -0,0 +1,208 @@ ++// NVMe datastructures and constants ++// ++// Copyright 2017 Amazon.com, Inc. or its affiliates. ++// ++// This file may be distributed under the terms of the GNU LGPLv3 license. ++ ++#ifndef __NVME_INT_H ++#define __NVME_INT_H ++ ++#include ++ ++/* 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 ++#include ++#include ++#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 + diff --git a/config/grub/nvme/target.cfg b/config/grub/nvme/target.cfg new file mode 100644 index 00000000..a85c97f5 --- /dev/null +++ b/config/grub/nvme/target.cfg @@ -0,0 +1,5 @@ +tree="nvme" +rev="8719cc2040368d43ab2de0b6e1b850b2c9cfc5b7" +bootstrapargs="--gnulib-srcdir=gnulib/ --no-git" +autoconfargs="--with-platform=coreboot --disable-werror" +makeargs="FS_PAYLOAD_MODULES=\"\"" diff --git a/config/submodule/grub/nvme/gnulib/module.cfg b/config/submodule/grub/nvme/gnulib/module.cfg new file mode 100644 index 00000000..6fd77871 --- /dev/null +++ b/config/submodule/grub/nvme/gnulib/module.cfg @@ -0,0 +1,3 @@ +subrepo="git://git.sv.gnu.org/gnulib" +subrepo_bkup="https://codeberg.org/libreboot/gnulib" +subhash="9f48fb992a3d7e96610c4ce8be969cff2d61a01b" diff --git a/config/submodule/grub/nvme/module.list b/config/submodule/grub/nvme/module.list new file mode 100644 index 00000000..0e57095c --- /dev/null +++ b/config/submodule/grub/nvme/module.list @@ -0,0 +1 @@ +gnulib -- cgit v1.2.1