From 5a5e453693f86718ef81b07ef4cdbd164a3c76b39844ac1c55d0b66992c8c374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Fri, 3 May 2024 13:19:28 +0200 Subject: [PATCH] Sync from SUSE:SLFO:Main grub2 revision 1dac218436a6d596013e239f4ee7d9e4 --- .gitattributes | 23 + ...-Add-grub_envblk_buf-helper-function.patch | 68 + ...or-Linux-EFI-stub-loading-on-aarch64.patch | 526 + 0001-Factor-out-grub_efi_linux_boot.patch | 215 + ...boot-loop-on-headless-system-in-qemu.patch | 66 + ...key-protection-on-boot-interruptions.patch | 286 + ...-grub.cfg-compatible-to-old-binaries.patch | 82 + ...-check-to-enable-btrfs-relative-path.patch | 150 + ...orkaround-volatile-efi-boot-variable.patch | 289 + ...d-support-for-UEFI-network-protocols.patch | 4923 +++++++++ ...x-EFI-loader-kernel-image-allocation.patch | 179 + ...rypttab-and-linux-modules-dependency.patch | 205 + ...Fix-missing-change-when-updating-to-.patch | 35 + 0001-efi-linux-provide-linux-command.patch | 103 + ...Try-memdisk-fonts-with-the-same-name.patch | 39 + ...Zero-file-data-not-backed-by-extents.patch | 33 + ...OB-write-when-parsing-the-ATTRIBUTE_.patch | 93 + ...-short-form-directory-data-boundary-.patch | 51 + ...-SUSE-signed-image-support-for-power.patch | 101 + ...-install-bailout-root-device-probing.patch | 163 + ...be-Deduplicate-probed-partmap-output.patch | 110 + ...p-a-default-boot-device-correctly-on.patch | 44 + ...le-device-path-for-a-nvmf-boot-devic.patch | 164 + ...-Avoiding-many-unecessary-open-close.patch | 142 + 0001-ieee1275-add-support-for-NVMeoFC.patch | 250 + ...lement-FCP-methods-for-WWPN-and-LUNs.patch | 146 + ...fdisk-retry-on-open-and-read-failure.patch | 172 + ...-implements-fibre-channel-discovery-.patch | 90 + 0001-install-fix-software-raid1-on-esp.patch | 396 + ...it-Restrict-high-memory-in-presence-.patch | 229 + 0001-kern-mm.c-Make-grub_calloc-inline.patch | 109 + ...pm2-token-for-TPM2-protected-volume-.patch | 142 + ...ure-stable-timestamps-for-generated-.patch | 76 + ...-boot-time-by-lookup-boot-disk-first.patch | 58 + ...t_devargs-and-get_devname-functions-.patch | 52 + ...tectors-Add-key-protectors-framework.patch | 177 + 0001-protectors-Implement-NV-index.patch | 80 + ...-the-path-of-usr-merged-kernel-confi.patch | 40 + ...pes-structures-and-command-constants.patch | 204 + ...sing-grub_arch_efi_linux_load_image_.patch | 83 + 0002-AUDIT-0-http-boot-tracker-bug.patch | 60 + ...grub_disk_write_tail-helper-function.patch | 53 + ...r-the-PE-magic-for-the-compiled-arch.patch | 57 + 0002-Fix-race-in-EFI-validation.patch | 92 + ...t-blocks-as-used-for-image-embedding.patch | 57 + ...tup-key-file-permission-for-better-s.patch | 59 + ...rict-file-access-on-cryptodisk-print.patch | 197 + ...fdt-has-address-cells-and-size-cells.patch | 40 + ...-Provide-cmdline-functions-as-module.patch | 47 + 0002-cryptodisk-Fallback-to-passphrase.patch | 41 + ...ey-before-entering-grub-shell-and-ed.patch | 88 + ...OB-read-when-reading-data-from-the-r.patch | 58 + ...xfs-Fix-XFS-directory-extent-parsing.patch | 171 + ...enable-NVMeoF-logical-device-transla.patch | 373 + ...erpc-enables-device-mapper-discovery.patch | 107 + ...ure-deterministic-tar-file-creation-.patch | 75 + ...-regex-for-Open-Firmware-device-spec.patch | 205 + 0002-tpm2-Add-TPM-Software-Stack-TSS.patch | 3569 +++++++ ...Add-more-marshal-unmarshal-functions.patch | 427 + ...rch-64-on-32-boot-in-linuxefi-loader.patch | 279 + 0003-Make-grub_error-more-verbose.patch | 57 + ...auto-file-completion-on-cryptodisk-p.patch | 117 + 0003-bootp-New-net_bootp6-command.patch | 1114 ++ ...-out-the-cached-keys-from-protectors.patch | 36 + ...OB-read-when-parsing-directory-entri.patch | 73 + ...-extent-counters-incompat-feature-su.patch | 115 + ...stall-support-prep-environment-block.patch | 137 + ...ge-the-logic-of-ieee1275_get_devargs.patch | 62 + 0003-protectors-Add-TPM2-Key-Protector.patch | 2052 ++++ 0003-tpm2-Implement-more-TPM2-commands.patch | 543 + ...igning-grub-with-an-appended-signatu.patch | 299 + 0004-Introduce-prep_load_env-command.patch | 272 + ...vocation-on-out-of-bound-file-access.patch | 91 + ...tter-locations-for-kernel-and-initrd.patch | 183 + ...-Better-memory-allocation-and-error-.patch | 301 + 0004-cryptodisk-Support-key-protectors.patch | 342 + ...ter-look-up-cryptodisk-devices-first.patch | 89 + 0004-efinet-UEFI-IPv6-PXE-support.patch | 116 + ...OB-read-when-parsing-bitmaps-for-ind.patch | 51 + 0004-ofpath-controller-name-update.patch | 28 + 0004-tpm2-Support-authorized-policy.patch | 170 + ...rub-Document-signing-grub-under-UEFI.patch | 59 + 0005-export-environment-at-start-up.patch | 164 + ...OOB-read-when-parsing-a-volume-label.patch | 61 + 0005-grub.texi-Add-net_bootp6-doument.patch | 48 + 0005-util-grub-protect-Add-new-tool.patch | 1620 +++ ...ce-buffers-for-reading-to-addresses-.patch | 105 + ...essing-DHCPACK-packet-from-HTTP-Boot.patch | 98 + ...nt-signing-grub-with-an-appended-sig.patch | 66 + ...se-address-before-jumping-to-the-PE-.patch | 59 + 0006-fs-ntfs-Make-code-more-readable.patch | 159 + ...-arrange-grub_cmd_linux-a-little-bit.patch | 133 + ...e-grub_dl_set_persistent-for-the-emu.patch | 43 + ...etting-network-from-UEFI-device-path.patch | 387 + ...e-our-own-allocator-for-kernel-stuff.patch | 249 + ...etting-DNS-server-from-UEFI-protocol.patch | 331 + ...linuxefi-Use-common-grub_initrd_load.patch | 151 + 0008-pgp-factor-out-rsa_pad.patch | 181 + ...itrd-params-cmdline-allocations-abov.patch | 175 + ...ry-to-obviate-the-need-to-input-pass.patch | 327 + ...rage-for-grub_crypto_pk_-to-crypto.c.patch | 74 + ...maximum-bounce-buffer-size-to-16-MiB.patch | 43 + ...integer-overflows-in-grub_cmd_initrd.patch | 52 + ...p-tweaks-in-preparation-for-libtasn1.patch | 58 + ...ates-import-etc-crypttab-to-grub.cfg.patch | 87 + ..._EFI_MAX_ALLOCATION_ADDRESS-for-RISC.patch | 27 + 0011-libtasn1-import-libtasn1-4.18.0.patch | 8996 +++++++++++++++++ ...asn1-disable-code-not-needed-in-grub.patch | 310 + 0012-tpm-Build-tpm-as-module.patch | 54 + ...tasn1-changes-for-grub-compatibility.patch | 212 + 0014-libtasn1-compile-into-asn1-module.patch | 65 + 0015-test_asn1-test-module-for-libtasn1.patch | 1401 +++ ...-support-embedding-x509-certificates.patch | 242 + ...res-import-GNUTLS-s-ASN.1-descriptio.patch | 634 ++ ...res-parse-PKCS-7-signedData-and-X.50.patch | 1876 ++++ ...res-support-verifying-appended-signa.patch | 755 ++ ...pended-signatures-verification-tests.patch | 1308 +++ 0021-appended-signatures-documentation.patch | 337 + ...er-lockdown-based-on-ibm-secure-boot.patch | 101 + ...tial-Signature-plus-other-Key-Usages.patch | 48 + 0044-squash-kern-Add-lockdown-support.patch | 115 + 20_memtest86+ | 66 + 80_suse_btrfs_snapshot | 28 + 90_persistent | 36 + ...lculation-for-the-synthesized-initrd.patch | 86 + PATCH_POLICY | 6 + README.ibm3215 | 59 + SLES-UEFI-CA-Certificate.crt | 39 + ...er-memory-type-for-kernel-allocation.patch | 51 + grub-2.12~rc1.tar.xz | 3 + ...ce-journal-draining-to-ensure-data-i.patch | 218 + grub-install-record-pcrs.patch | 16 + grub-read-pcr.patch | 165 + grub.default | 39 + grub2-Add-hidden-menu-entries.patch | 220 + grub2-SUSE-Add-the-t-hotkey.patch | 66 + ...-add-ability-to-boot-from-subvolumes.patch | 722 ++ grub2-btrfs-02-export-subvolume-envvars.patch | 19 + grub2-btrfs-03-follow_default.patch | 185 + grub2-btrfs-04-grub2-install.patch | 148 + grub2-btrfs-05-grub2-mkconfig.patch | 137 + grub2-btrfs-06-subvol-mount.patch | 521 + grub2-btrfs-07-subvol-fallback.patch | 44 + ...rkaround-snapshot-menu-default-entry.patch | 58 + grub2-btrfs-09-get-default-subvolume.patch | 282 + grub2-btrfs-10-config-directory.patch | 253 + grub2-btrfs-help-on-snapper-rollback.patch | 19 + grub2-change-bash-completion-dir.patch | 13 + grub2-check-default.sh | 145 + ...mands-introduce-read_file-subcommand.patch | 84 + grub2-default-distributor.patch | 201 + ...er-support-pv-without-metadatacopies.patch | 219 + grub2-efi-HP-workaround.patch | 95 + grub2-efi-chainload-harder.patch | 121 + ...-efi-disable-video-cirrus-and-bochus.patch | 31 + grub2-efi-xen-cfg-unquote.patch | 90 + grub2-efi-xen-chainload.patch | 188 + grub2-efi-xen-cmdline.patch | 23 + grub2-efi-xen-removable.patch | 117 + grub2-efi_gop-avoid-low-resolution.patch | 39 + grub2-emu-4-all.patch | 162 + ...x-error-terminal-gfxterm-isn-t-found.patch | 34 + grub2-fix-menu-in-xen-host-server.patch | 110 + grub2-getroot-scan-disk-pv.patch | 43 + grub2-getroot-support-nvdimm.patch | 66 + ...oot-treat-mdadm-ddf-as-simple-device.patch | 66 + ...-support-scrolling-menu-entry-s-text.patch | 269 + grub2-grubenv-in-btrfs-header.patch | 498 + grub2-increase-crypttab-path-buffer.patch | 26 + grub2-install-fix-not-a-directory-error.patch | 38 + ...seless-check-PReP-partition-is-empty.patch | 77 + grub2-instdev-fixup.pl | 336 + ...rate-and-hook-for-extended-partition.patch | 48 + grub2-linguas.sh-no-rsync.patch | 21 + grub2-linux.patch | 40 + grub2-linuxefi-fix-boot-params.patch | 18 + ...te-metadata-buffer-from-raw-contents.patch | 148 + grub2-menu-unrestricted.patch | 21 + grub2-mkconfig-aarch64.patch | 12 + grub2-mkconfig-arm.patch | 10 + grub2-mkconfig-default-entry-correction.patch | 14 + grub2-mkconfig-riscv64.patch | 12 + grub2-once | 309 + grub2-once.service | 17 + grub2-pass-corret-root-for-nfsroot.patch | 135 + grub2-ppc-terminfo.patch | 147 + grub2-ppc64-cas-fix-double-free.patch | 94 + grub2-ppc64-cas-new-scope.patch | 15 + grub2-ppc64-cas-reboot-support.patch | 166 + grub2-ppc64le-disable-video.patch | 43 + grub2-ppc64le-memory-map.patch | 78 + ...-files-added-in-order-to-allow-s390x.patch | 296 + grub2-s390x-03-output-7-bit-ascii.patch | 530 + grub2-s390x-04-grub2-install.patch | 1276 +++ grub2-s390x-05-grub2-mkconfig.patch | 145 + grub2-s390x-06-loadparm.patch | 45 + ...0x-07-add-image-param-for-zipl-setup.patch | 41 + grub2-s390x-08-workaround-part-to-disk.patch | 13 + grub2-s390x-09-improve-zipl-setup.patch | 209 + grub2-s390x-11-secureboot.patch | 133 + grub2-s390x-12-zipl-setup-usrmerge.patch | 84 + grub2-s390x-skip-zfcpdump-image.patch | 15 + grub2-secureboot-add-linuxefi.patch | 450 + grub2-secureboot-chainloader.patch | 596 ++ grub2-secureboot-install-signed-grub.patch | 225 + grub2-secureboot-no-insmod-on-sb.patch | 51 + ...up-try-fs-embed-if-mbr-gap-too-small.patch | 61 + grub2-simplefb.patch | 13 + grub2-snapper-plugin.sh | 281 + grub2-suse-remove-linux-root-param.patch | 75 + grub2-systemd-sleep.sh | 265 + ...Unifont-for-starfield-theme-terminal.patch | 15 + grub2-use-rpmsort-for-version-sorting.patch | 143 + grub2-util-30_os-prober-multiple-initrd.patch | 11 + ...-vbe-blacklist-preferred-1440x900x32.patch | 18 + ...-the-resolution-for-fixed-bimap-font.patch | 86 + grub2-xen-linux16.patch | 29 + grub2-xen-pv-firmware.cfg | 158 + grub2-zipl-setup-fix-btrfs-multipledev.patch | 17 + grub2.changes | 4289 ++++++++ grub2.rpmlintrc | 14 + grub2.spec | 1492 +++ info-dir-entry.patch | 29 + not-display-menu-when-boot-once.patch | 31 + openSUSE-UEFI-CA-Certificate.crt | 26 + rename-grub-info-file-to-grub2.patch | 36 + safe_tpm_pcr_snapshot.patch | 99 + tpm-record-pcrs.patch | 235 + use-grub2-as-a-package-name.patch | 25 + 229 files changed, 63390 insertions(+) create mode 100644 .gitattributes create mode 100644 0001-Add-grub_envblk_buf-helper-function.patch create mode 100644 0001-Add-support-for-Linux-EFI-stub-loading-on-aarch64.patch create mode 100644 0001-Factor-out-grub_efi_linux_boot.patch create mode 100644 0001-Fix-infinite-boot-loop-on-headless-system-in-qemu.patch create mode 100644 0001-Improve-TPM-key-protection-on-boot-interruptions.patch create mode 100644 0001-Make-grub.cfg-compatible-to-old-binaries.patch create mode 100644 0001-Unify-the-check-to-enable-btrfs-relative-path.patch create mode 100644 0001-Workaround-volatile-efi-boot-variable.patch create mode 100644 0001-add-support-for-UEFI-network-protocols.patch create mode 100644 0001-arm64-Fix-EFI-loader-kernel-image-allocation.patch create mode 100644 0001-clean-up-crypttab-and-linux-modules-dependency.patch create mode 100644 0001-disk-cryptodisk-Fix-missing-change-when-updating-to-.patch create mode 100644 0001-efi-linux-provide-linux-command.patch create mode 100644 0001-font-Try-memdisk-fonts-with-the-same-name.patch create mode 100644 0001-fs-btrfs-Zero-file-data-not-backed-by-extents.patch create mode 100644 0001-fs-ntfs-Fix-an-OOB-write-when-parsing-the-ATTRIBUTE_.patch create mode 100644 0001-fs-xfs-Incorrect-short-form-directory-data-boundary-.patch create mode 100644 0001-grub-install-Add-SUSE-signed-image-support-for-power.patch create mode 100644 0001-grub-install-bailout-root-device-probing.patch create mode 100644 0001-grub-probe-Deduplicate-probed-partmap-output.patch create mode 100644 0001-grub2-Can-t-setup-a-default-boot-device-correctly-on.patch create mode 100644 0001-grub2-Set-multiple-device-path-for-a-nvmf-boot-devic.patch create mode 100644 0001-ieee1275-Avoiding-many-unecessary-open-close.patch create mode 100644 0001-ieee1275-add-support-for-NVMeoFC.patch create mode 100644 0001-ieee1275-implement-FCP-methods-for-WWPN-and-LUNs.patch create mode 100644 0001-ieee1275-ofdisk-retry-on-open-and-read-failure.patch create mode 100644 0001-ieee1275-powerpc-implements-fibre-channel-discovery-.patch create mode 100644 0001-install-fix-software-raid1-on-esp.patch create mode 100644 0001-kern-ieee1275-init-Restrict-high-memory-in-presence-.patch create mode 100644 0001-kern-mm.c-Make-grub_calloc-inline.patch create mode 100644 0001-luks2-Use-grub-tpm2-token-for-TPM2-protected-volume-.patch create mode 100644 0001-mkstandalone-ensure-stable-timestamps-for-generated-.patch create mode 100644 0001-ofdisk-improve-boot-time-by-lookup-boot-disk-first.patch create mode 100644 0001-openfw-Ensure-get_devargs-and-get_devname-functions-.patch create mode 100644 0001-protectors-Add-key-protectors-framework.patch create mode 100644 0001-protectors-Implement-NV-index.patch create mode 100644 0001-templates-Follow-the-path-of-usr-merged-kernel-confi.patch create mode 100644 0001-tpm2-Add-TPM2-types-structures-and-command-constants.patch create mode 100644 0001-xen_boot-add-missing-grub_arch_efi_linux_load_image_.patch create mode 100644 0002-AUDIT-0-http-boot-tracker-bug.patch create mode 100644 0002-Add-grub_disk_write_tail-helper-function.patch create mode 100644 0002-Arm-check-for-the-PE-magic-for-the-compiled-arch.patch create mode 100644 0002-Fix-race-in-EFI-validation.patch create mode 100644 0002-Mark-environmet-blocks-as-used-for-image-embedding.patch create mode 100644 0002-Restrict-cryptsetup-key-file-permission-for-better-s.patch create mode 100644 0002-Restrict-file-access-on-cryptodisk-print.patch create mode 100644 0002-arm64-make-sure-fdt-has-address-cells-and-size-cells.patch create mode 100644 0002-cmdline-Provide-cmdline-functions-as-module.patch create mode 100644 0002-cryptodisk-Fallback-to-passphrase.patch create mode 100644 0002-discard-cached-key-before-entering-grub-shell-and-ed.patch create mode 100644 0002-fs-ntfs-Fix-an-OOB-read-when-reading-data-from-the-r.patch create mode 100644 0002-fs-xfs-Fix-XFS-directory-extent-parsing.patch create mode 100644 0002-ieee1275-ofpath-enable-NVMeoF-logical-device-transla.patch create mode 100644 0002-ieee1275-powerpc-enables-device-mapper-discovery.patch create mode 100644 0002-mkstandalone-ensure-deterministic-tar-file-creation-.patch create mode 100644 0002-prep_loadenv-Fix-regex-for-Open-Firmware-device-spec.patch create mode 100644 0002-tpm2-Add-TPM-Software-Stack-TSS.patch create mode 100644 0002-tpm2-Add-more-marshal-unmarshal-functions.patch create mode 100644 0003-Handle-multi-arch-64-on-32-boot-in-linuxefi-loader.patch create mode 100644 0003-Make-grub_error-more-verbose.patch create mode 100644 0003-Restrict-ls-and-auto-file-completion-on-cryptodisk-p.patch create mode 100644 0003-bootp-New-net_bootp6-command.patch create mode 100644 0003-cryptodisk-wipe-out-the-cached-keys-from-protectors.patch create mode 100644 0003-fs-ntfs-Fix-an-OOB-read-when-parsing-directory-entri.patch create mode 100644 0003-fs-xfs-add-large-extent-counters-incompat-feature-su.patch create mode 100644 0003-grub-install-support-prep-environment-block.patch create mode 100644 0003-ieee1275-change-the-logic-of-ieee1275_get_devargs.patch create mode 100644 0003-protectors-Add-TPM2-Key-Protector.patch create mode 100644 0003-tpm2-Implement-more-TPM2-commands.patch create mode 100644 0004-Add-suport-for-signing-grub-with-an-appended-signatu.patch create mode 100644 0004-Introduce-prep_load_env-command.patch create mode 100644 0004-Key-revocation-on-out-of-bound-file-access.patch create mode 100644 0004-Try-to-pick-better-locations-for-kernel-and-initrd.patch create mode 100644 0004-arm-arm64-loader-Better-memory-allocation-and-error-.patch create mode 100644 0004-cryptodisk-Support-key-protectors.patch create mode 100644 0004-diskfilter-look-up-cryptodisk-devices-first.patch create mode 100644 0004-efinet-UEFI-IPv6-PXE-support.patch create mode 100644 0004-fs-ntfs-Fix-an-OOB-read-when-parsing-bitmaps-for-ind.patch create mode 100644 0004-ofpath-controller-name-update.patch create mode 100644 0004-tpm2-Support-authorized-policy.patch create mode 100644 0005-docs-grub-Document-signing-grub-under-UEFI.patch create mode 100644 0005-export-environment-at-start-up.patch create mode 100644 0005-fs-ntfs-Fix-an-OOB-read-when-parsing-a-volume-label.patch create mode 100644 0005-grub.texi-Add-net_bootp6-doument.patch create mode 100644 0005-util-grub-protect-Add-new-tool.patch create mode 100644 0005-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch create mode 100644 0006-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch create mode 100644 0006-docs-grub-Document-signing-grub-with-an-appended-sig.patch create mode 100644 0006-efi-Set-image-base-address-before-jumping-to-the-PE-.patch create mode 100644 0006-fs-ntfs-Make-code-more-readable.patch create mode 100644 0006-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch create mode 100644 0007-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch create mode 100644 0007-efinet-Setting-network-from-UEFI-device-path.patch create mode 100644 0007-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch create mode 100644 0008-efinet-Setting-DNS-server-from-UEFI-protocol.patch create mode 100644 0008-linuxefi-Use-common-grub_initrd_load.patch create mode 100644 0008-pgp-factor-out-rsa_pad.patch create mode 100644 0008-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch create mode 100644 0009-Add-crypttab_entry-to-obviate-the-need-to-input-pass.patch create mode 100644 0009-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch create mode 100644 0009-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch create mode 100644 0010-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch create mode 100644 0010-posix_wrap-tweaks-in-preparation-for-libtasn1.patch create mode 100644 0010-templates-import-etc-crypttab-to-grub.cfg.patch create mode 100644 0011-Also-define-GRUB_EFI_MAX_ALLOCATION_ADDRESS-for-RISC.patch create mode 100644 0011-libtasn1-import-libtasn1-4.18.0.patch create mode 100644 0012-libtasn1-disable-code-not-needed-in-grub.patch create mode 100644 0012-tpm-Build-tpm-as-module.patch create mode 100644 0013-libtasn1-changes-for-grub-compatibility.patch create mode 100644 0014-libtasn1-compile-into-asn1-module.patch create mode 100644 0015-test_asn1-test-module-for-libtasn1.patch create mode 100644 0016-grub-install-support-embedding-x509-certificates.patch create mode 100644 0017-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch create mode 100644 0018-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch create mode 100644 0019-appended-signatures-support-verifying-appended-signa.patch create mode 100644 0020-appended-signatures-verification-tests.patch create mode 100644 0021-appended-signatures-documentation.patch create mode 100644 0022-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch create mode 100644 0023-x509-allow-Digitial-Signature-plus-other-Key-Usages.patch create mode 100644 0044-squash-kern-Add-lockdown-support.patch create mode 100644 20_memtest86+ create mode 100644 80_suse_btrfs_snapshot create mode 100644 90_persistent create mode 100644 Fix-the-size-calculation-for-the-synthesized-initrd.patch create mode 100644 PATCH_POLICY create mode 100644 README.ibm3215 create mode 100644 SLES-UEFI-CA-Certificate.crt create mode 100644 arm64-Use-proper-memory-type-for-kernel-allocation.patch create mode 100644 grub-2.12~rc1.tar.xz create mode 100644 grub-install-force-journal-draining-to-ensure-data-i.patch create mode 100644 grub-install-record-pcrs.patch create mode 100644 grub-read-pcr.patch create mode 100644 grub.default create mode 100644 grub2-Add-hidden-menu-entries.patch create mode 100644 grub2-SUSE-Add-the-t-hotkey.patch create mode 100644 grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch create mode 100644 grub2-btrfs-02-export-subvolume-envvars.patch create mode 100644 grub2-btrfs-03-follow_default.patch create mode 100644 grub2-btrfs-04-grub2-install.patch create mode 100644 grub2-btrfs-05-grub2-mkconfig.patch create mode 100644 grub2-btrfs-06-subvol-mount.patch create mode 100644 grub2-btrfs-07-subvol-fallback.patch create mode 100644 grub2-btrfs-08-workaround-snapshot-menu-default-entry.patch create mode 100644 grub2-btrfs-09-get-default-subvolume.patch create mode 100644 grub2-btrfs-10-config-directory.patch create mode 100644 grub2-btrfs-help-on-snapper-rollback.patch create mode 100644 grub2-change-bash-completion-dir.patch create mode 100644 grub2-check-default.sh create mode 100644 grub2-commands-introduce-read_file-subcommand.patch create mode 100644 grub2-default-distributor.patch create mode 100644 grub2-diskfilter-support-pv-without-metadatacopies.patch create mode 100644 grub2-efi-HP-workaround.patch create mode 100644 grub2-efi-chainload-harder.patch create mode 100644 grub2-efi-disable-video-cirrus-and-bochus.patch create mode 100644 grub2-efi-xen-cfg-unquote.patch create mode 100644 grub2-efi-xen-chainload.patch create mode 100644 grub2-efi-xen-cmdline.patch create mode 100644 grub2-efi-xen-removable.patch create mode 100644 grub2-efi_gop-avoid-low-resolution.patch create mode 100644 grub2-emu-4-all.patch create mode 100644 grub2-fix-error-terminal-gfxterm-isn-t-found.patch create mode 100644 grub2-fix-menu-in-xen-host-server.patch create mode 100644 grub2-getroot-scan-disk-pv.patch create mode 100644 grub2-getroot-support-nvdimm.patch create mode 100644 grub2-getroot-treat-mdadm-ddf-as-simple-device.patch create mode 100644 grub2-gfxmenu-support-scrolling-menu-entry-s-text.patch create mode 100644 grub2-grubenv-in-btrfs-header.patch create mode 100644 grub2-increase-crypttab-path-buffer.patch create mode 100644 grub2-install-fix-not-a-directory-error.patch create mode 100644 grub2-install-remove-useless-check-PReP-partition-is-empty.patch create mode 100644 grub2-instdev-fixup.pl create mode 100644 grub2-iterate-and-hook-for-extended-partition.patch create mode 100644 grub2-linguas.sh-no-rsync.patch create mode 100644 grub2-linux.patch create mode 100644 grub2-linuxefi-fix-boot-params.patch create mode 100644 grub2-lvm-allocate-metadata-buffer-from-raw-contents.patch create mode 100644 grub2-menu-unrestricted.patch create mode 100644 grub2-mkconfig-aarch64.patch create mode 100644 grub2-mkconfig-arm.patch create mode 100644 grub2-mkconfig-default-entry-correction.patch create mode 100644 grub2-mkconfig-riscv64.patch create mode 100644 grub2-once create mode 100644 grub2-once.service create mode 100644 grub2-pass-corret-root-for-nfsroot.patch create mode 100644 grub2-ppc-terminfo.patch create mode 100644 grub2-ppc64-cas-fix-double-free.patch create mode 100644 grub2-ppc64-cas-new-scope.patch create mode 100644 grub2-ppc64-cas-reboot-support.patch create mode 100644 grub2-ppc64le-disable-video.patch create mode 100644 grub2-ppc64le-memory-map.patch create mode 100644 grub2-s390x-01-Changes-made-and-files-added-in-order-to-allow-s390x.patch create mode 100644 grub2-s390x-03-output-7-bit-ascii.patch create mode 100644 grub2-s390x-04-grub2-install.patch create mode 100644 grub2-s390x-05-grub2-mkconfig.patch create mode 100644 grub2-s390x-06-loadparm.patch create mode 100644 grub2-s390x-07-add-image-param-for-zipl-setup.patch create mode 100644 grub2-s390x-08-workaround-part-to-disk.patch create mode 100644 grub2-s390x-09-improve-zipl-setup.patch create mode 100644 grub2-s390x-11-secureboot.patch create mode 100644 grub2-s390x-12-zipl-setup-usrmerge.patch create mode 100644 grub2-s390x-skip-zfcpdump-image.patch create mode 100644 grub2-secureboot-add-linuxefi.patch create mode 100644 grub2-secureboot-chainloader.patch create mode 100644 grub2-secureboot-install-signed-grub.patch create mode 100644 grub2-secureboot-no-insmod-on-sb.patch create mode 100644 grub2-setup-try-fs-embed-if-mbr-gap-too-small.patch create mode 100644 grub2-simplefb.patch create mode 100644 grub2-snapper-plugin.sh create mode 100644 grub2-suse-remove-linux-root-param.patch create mode 100644 grub2-systemd-sleep.sh create mode 100644 grub2-use-Unifont-for-starfield-theme-terminal.patch create mode 100644 grub2-use-rpmsort-for-version-sorting.patch create mode 100644 grub2-util-30_os-prober-multiple-initrd.patch create mode 100644 grub2-vbe-blacklist-preferred-1440x900x32.patch create mode 100644 grub2-video-limit-the-resolution-for-fixed-bimap-font.patch create mode 100644 grub2-xen-linux16.patch create mode 100644 grub2-xen-pv-firmware.cfg create mode 100644 grub2-zipl-setup-fix-btrfs-multipledev.patch create mode 100644 grub2.changes create mode 100644 grub2.rpmlintrc create mode 100644 grub2.spec create mode 100644 info-dir-entry.patch create mode 100644 not-display-menu-when-boot-once.patch create mode 100644 openSUSE-UEFI-CA-Certificate.crt create mode 100644 rename-grub-info-file-to-grub2.patch create mode 100644 safe_tpm_pcr_snapshot.patch create mode 100644 tpm-record-pcrs.patch create mode 100644 use-grub2-as-a-package-name.patch diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b03811 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +## Default LFS +*.7z filter=lfs diff=lfs merge=lfs -text +*.bsp filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.gem filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.lz filter=lfs diff=lfs merge=lfs -text +*.lzma filter=lfs diff=lfs merge=lfs -text +*.obscpio filter=lfs diff=lfs merge=lfs -text +*.oxt filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.rpm filter=lfs diff=lfs merge=lfs -text +*.tbz filter=lfs diff=lfs merge=lfs -text +*.tbz2 filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.txz filter=lfs diff=lfs merge=lfs -text +*.whl filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text diff --git a/0001-Add-grub_envblk_buf-helper-function.patch b/0001-Add-grub_envblk_buf-helper-function.patch new file mode 100644 index 0000000..21655a7 --- /dev/null +++ b/0001-Add-grub_envblk_buf-helper-function.patch @@ -0,0 +1,68 @@ +From a326e486bdcf99e6be973ba54c0abfb6d2d95b73 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Mon, 17 Jan 2022 17:45:00 +0800 +Subject: [PATCH 1/5] Add grub_envblk_buf helper function + +This helps in creation and initialization of memory buffer for +environment block of given size. + +Signed-off-by: Michael Chang +--- + grub-core/lib/envblk.c | 12 ++++++++++++ + include/grub/lib/envblk.h | 1 + + util/grub-editenv.c | 4 +--- + 3 files changed, 14 insertions(+), 3 deletions(-) + +diff --git a/grub-core/lib/envblk.c b/grub-core/lib/envblk.c +index 2e4e78b132..24efbe7ffa 100644 +--- a/grub-core/lib/envblk.c ++++ b/grub-core/lib/envblk.c +@@ -23,6 +23,18 @@ + #include + #include + ++char * ++grub_envblk_buf (grub_size_t size) ++{ ++ char *buf; ++ ++ buf = grub_malloc (size); ++ grub_memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1); ++ grub_memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#', size - sizeof (GRUB_ENVBLK_SIGNATURE) + 1); ++ ++ return buf; ++} ++ + grub_envblk_t + grub_envblk_open (char *buf, grub_size_t size) + { +diff --git a/include/grub/lib/envblk.h b/include/grub/lib/envblk.h +index c3e6559217..83f3fcf841 100644 +--- a/include/grub/lib/envblk.h ++++ b/include/grub/lib/envblk.h +@@ -31,6 +31,7 @@ struct grub_envblk + }; + typedef struct grub_envblk *grub_envblk_t; + ++char *grub_envblk_buf (grub_size_t size); + grub_envblk_t grub_envblk_open (char *buf, grub_size_t size); + int grub_envblk_set (grub_envblk_t envblk, const char *name, const char *value); + void grub_envblk_delete (grub_envblk_t envblk, const char *name); +diff --git a/util/grub-editenv.c b/util/grub-editenv.c +index b8219335f7..a02d3f2a63 100644 +--- a/util/grub-editenv.c ++++ b/util/grub-editenv.c +@@ -210,9 +210,7 @@ create_envblk_fs (void) + if (! fp) + grub_util_error (_("cannot open `%s': %s"), device, strerror (errno)); + +- buf = xmalloc (size); +- memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1); +- memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#', size - sizeof (GRUB_ENVBLK_SIGNATURE) + 1); ++ buf = grub_envblk_buf (size); + + if (fseek (fp, offset, SEEK_SET) < 0) + grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno)); +-- +2.34.1 + diff --git a/0001-Add-support-for-Linux-EFI-stub-loading-on-aarch64.patch b/0001-Add-support-for-Linux-EFI-stub-loading-on-aarch64.patch new file mode 100644 index 0000000..3baf8bc --- /dev/null +++ b/0001-Add-support-for-Linux-EFI-stub-loading-on-aarch64.patch @@ -0,0 +1,526 @@ +From db4da8095b5ba722d22502c8d090e66816a5577d Mon Sep 17 00:00:00 2001 +From: Matthew Garrett +Date: Fri, 6 Nov 2020 08:36:36 +0000 +Subject: [PATCH 1/9] Add support for Linux EFI stub loading on aarch64. + +Add support for Linux EFI stub loading on aarch64. + +v1: +Make efi handoff the default loader for arm64 platform. + +v2: +The efi shim_lock verifier has been moved to grub core so local +shim_lock protocol is no longer needed here for aarch64 efi to verify +the loaded kernel image. From now on the framework will take care the +verificaion, consolidating the integration of various security verifiers +like secure boot, gpg and tpm. + +--- + grub-core/Makefile.core.def | 4 +- + grub-core/loader/arm64/efi/linux.c | 443 +++++++++++++++++++++++++++++ + include/grub/arm/linux.h | 9 + + include/grub/arm64/linux.h | 10 + + 4 files changed, 465 insertions(+), 1 deletion(-) + create mode 100644 grub-core/loader/arm64/efi/linux.c + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -1854,7 +1854,7 @@ + arm_coreboot = loader/arm/linux.c; + arm_efi = loader/efi/linux.c; + arm_uboot = loader/arm/linux.c; +- arm64 = loader/efi/linux.c; ++ arm64 = loader/arm64/efi/linux.c; + loongarch64 = loader/efi/linux.c; + riscv32 = loader/efi/linux.c; + riscv64 = loader/efi/linux.c; +@@ -1922,7 +1922,7 @@ + + module = { + name = linuxefi; +- efi = lib/fake_module.c; ++ x86 = lib/fake_module.c; + enable = i386_efi; + enable = x86_64_efi; + }; +--- /dev/null ++++ b/grub-core/loader/arm64/efi/linux.c +@@ -0,0 +1,411 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2013 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++static grub_dl_t my_mod; ++static int loaded; ++ ++static void *kernel_addr; ++static grub_uint64_t kernel_size; ++static grub_uint32_t handover_offset; ++ ++static char *linux_args; ++static grub_uint32_t cmdline_size; ++ ++static grub_addr_t initrd_start; ++static grub_addr_t initrd_end; ++ ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wcast-align" ++ ++typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *); ++ ++static grub_err_t ++grub_efi_linux_boot (void *kernel_address, grub_off_t offset, ++ void *kernel_params) ++{ ++ handover_func hf; ++ ++ hf = (handover_func)((char *)kernel_address + offset); ++ hf (grub_efi_image_handle, grub_efi_system_table, kernel_params); ++ ++ return GRUB_ERR_BUG; ++} ++ ++#pragma GCC diagnostic pop ++ ++static grub_err_t ++grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh) ++{ ++ if (lh->magic != GRUB_LINUX_ARMXX_MAGIC_SIGNATURE) ++ return grub_error(GRUB_ERR_BAD_OS, "invalid magic number"); ++ ++ if ((lh->code0 & 0xffff) != GRUB_PE32_MAGIC) ++ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, ++ N_("plain image kernel not supported - rebuild with CONFIG_(U)EFI_STUB enabled")); ++ ++ grub_dprintf ("linux", "UEFI stub kernel:\n"); ++ grub_dprintf ("linux", "PE/COFF header @ %08x\n", lh->hdr_offset); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++finalize_params_linux (void) ++{ ++ grub_efi_loaded_image_t *loaded_image = NULL; ++ int node, retval, len; ++ ++ void *fdt; ++ ++ fdt = grub_fdt_load (GRUB_EFI_LINUX_FDT_EXTRA_SPACE); ++ ++ if (!fdt) ++ goto failure; ++ ++ node = grub_fdt_find_subnode (fdt, 0, "chosen"); ++ if (node < 0) ++ node = grub_fdt_add_subnode (fdt, 0, "chosen"); ++ ++ if (node < 1) ++ goto failure; ++ ++ /* Set initrd info */ ++ if (initrd_start && initrd_end > initrd_start) ++ { ++ grub_dprintf ("linux", "Initrd @ %p-%p\n", ++ (void *) initrd_start, (void *) initrd_end); ++ ++ retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-start", ++ initrd_start); ++ if (retval) ++ goto failure; ++ retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-end", ++ initrd_end); ++ if (retval) ++ goto failure; ++ } ++ ++ if (grub_fdt_install() != GRUB_ERR_NONE) ++ goto failure; ++ ++ grub_dprintf ("linux", "Installed/updated FDT configuration table @ %p\n", ++ fdt); ++ ++ /* Convert command line to UCS-2 */ ++ loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle); ++ if (!loaded_image) ++ goto failure; ++ ++ loaded_image->load_options_size = len = ++ (grub_strlen (linux_args) + 1) * sizeof (grub_efi_char16_t); ++ loaded_image->load_options = ++ grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); ++ if (!loaded_image->load_options) ++ return grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters"); ++ ++ loaded_image->load_options_size = ++ 2 * grub_utf8_to_utf16 (loaded_image->load_options, len, ++ (grub_uint8_t *) linux_args, len, NULL); ++ ++ return GRUB_ERR_NONE; ++ ++failure: ++ grub_fdt_unload(); ++ return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT"); ++} ++ ++static void ++free_params (void) ++{ ++ grub_efi_loaded_image_t *loaded_image = NULL; ++ ++ loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle); ++ if (loaded_image) ++ { ++ if (loaded_image->load_options) ++ grub_efi_free_pages ((grub_efi_physical_address_t)(grub_efi_uintn_t)loaded_image->load_options, ++ GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); ++ loaded_image->load_options = NULL; ++ loaded_image->load_options_size = 0; ++ } ++} ++ ++grub_err_t ++grub_arch_efi_linux_boot_image (grub_addr_t addr, ++ grub_size_t size __attribute__ ((unused)), ++ char *args) ++{ ++ grub_err_t retval; ++ ++ retval = finalize_params_linux (); ++ if (retval != GRUB_ERR_NONE) ++ return grub_errno; ++ ++ grub_dprintf ("linux", "linux command line: '%s'\n", args); ++ ++ retval = grub_efi_linux_boot ((char *)addr, handover_offset, (void *)addr); ++ ++ /* Never reached... */ ++ free_params(); ++ return retval; ++} ++ ++static grub_err_t ++grub_linux_boot (void) ++{ ++ return (grub_arch_efi_linux_boot_image ((grub_addr_t)kernel_addr, kernel_size, linux_args)); ++} ++ ++static grub_err_t ++grub_linux_unload (void) ++{ ++ grub_dl_unref (my_mod); ++ loaded = 0; ++ if (initrd_start) ++ grub_efi_free_pages ((grub_efi_physical_address_t) initrd_start, ++ GRUB_EFI_BYTES_TO_PAGES (initrd_end - initrd_start)); ++ initrd_start = initrd_end = 0; ++ grub_free (linux_args); ++ if (kernel_addr) ++ grub_efi_free_pages ((grub_addr_t) kernel_addr, ++ GRUB_EFI_BYTES_TO_PAGES (kernel_size)); ++ grub_fdt_unload (); ++ return GRUB_ERR_NONE; ++} ++ ++/* ++ * As per linux/Documentation/arm/Booting ++ * ARM initrd needs to be covered by kernel linear mapping, ++ * so place it in the first 512MB of DRAM. ++ * ++ * As per linux/Documentation/arm64/booting.txt ++ * ARM64 initrd needs to be contained entirely within a 1GB aligned window ++ * of up to 32GB of size that covers the kernel image as well. ++ * Since the EFI stub loader will attempt to load the kernel near start of ++ * RAM, place the buffer in the first 32GB of RAM. ++ */ ++#ifdef __arm__ ++#define INITRD_MAX_ADDRESS_OFFSET (512U * 1024 * 1024) ++#else /* __aarch64__ */ ++#define INITRD_MAX_ADDRESS_OFFSET (32ULL * 1024 * 1024 * 1024) ++#endif ++ ++/* ++ * This function returns a pointer to a legally allocated initrd buffer, ++ * or NULL if unsuccessful ++ */ ++static void * ++allocate_initrd_mem (int initrd_pages) ++{ ++ grub_addr_t max_addr; ++ ++ if (grub_efi_get_ram_base (&max_addr) != GRUB_ERR_NONE) ++ return NULL; ++ ++ max_addr += INITRD_MAX_ADDRESS_OFFSET - 1; ++ ++ return grub_efi_allocate_pages_real (max_addr, initrd_pages, ++ GRUB_EFI_ALLOCATE_MAX_ADDRESS, ++ GRUB_EFI_LOADER_DATA); ++} ++ ++static grub_err_t ++grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), ++ int argc, char *argv[]) ++{ ++ struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 }; ++ int initrd_size, initrd_pages; ++ void *initrd_mem = NULL; ++ ++ if (argc == 0) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); ++ goto fail; ++ } ++ ++ if (!loaded) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("you need to load the kernel first")); ++ goto fail; ++ } ++ ++ if (grub_initrd_init (argc, argv, &initrd_ctx)) ++ goto fail; ++ ++ initrd_size = grub_get_initrd_size (&initrd_ctx); ++ grub_dprintf ("linux", "Loading initrd\n"); ++ ++ initrd_pages = (GRUB_EFI_BYTES_TO_PAGES (initrd_size)); ++ initrd_mem = allocate_initrd_mem (initrd_pages); ++ ++ if (!initrd_mem) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); ++ goto fail; ++ } ++ ++ if (grub_initrd_load (&initrd_ctx, initrd_mem)) ++ goto fail; ++ ++ initrd_start = (grub_addr_t) initrd_mem; ++ initrd_end = initrd_start + initrd_size; ++ grub_dprintf ("linux", "[addr=%p, size=0x%x]\n", ++ (void *) initrd_start, initrd_size); ++ ++ fail: ++ grub_initrd_close (&initrd_ctx); ++ if (initrd_mem && !initrd_start) ++ grub_efi_free_pages ((grub_addr_t) initrd_mem, initrd_pages); ++ ++ return grub_errno; ++} ++ ++static grub_err_t ++grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), ++ int argc, char *argv[]) ++{ ++ grub_file_t file = 0; ++ struct linux_arch_kernel_header lh; ++ struct grub_armxx_linux_pe_header *pe; ++ grub_err_t err; ++ ++ grub_dl_ref (my_mod); ++ ++ if (argc == 0) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); ++ goto fail; ++ } ++ ++ file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL); ++ if (!file) ++ goto fail; ++ ++ kernel_size = grub_file_size (file); ++ ++ if (grub_file_read (file, &lh, sizeof (lh)) < (long) sizeof (lh)) ++ return grub_errno; ++ ++ if (grub_arch_efi_linux_check_image (&lh) != GRUB_ERR_NONE) ++ goto fail; ++ ++ grub_loader_unset(); ++ ++ grub_dprintf ("linux", "kernel file size: %lld\n", (long long) kernel_size); ++ kernel_addr = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (kernel_size)); ++ grub_dprintf ("linux", "kernel numpages: %lld\n", ++ (long long) GRUB_EFI_BYTES_TO_PAGES (kernel_size)); ++ if (!kernel_addr) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); ++ goto fail; ++ } ++ ++ grub_file_seek (file, 0); ++ if (grub_file_read (file, kernel_addr, kernel_size) ++ < (grub_int64_t) kernel_size) ++ { ++ if (!grub_errno) ++ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]); ++ goto fail; ++ } ++ ++ grub_dprintf ("linux", "kernel @ %p\n", kernel_addr); ++ ++ pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset); ++ handover_offset = pe->opt.entry_addr; ++ ++ cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE); ++ linux_args = grub_malloc (cmdline_size); ++ if (!linux_args) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); ++ goto fail; ++ } ++ grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE)); ++ err = grub_create_loader_cmdline (argc, argv, ++ linux_args + sizeof (LINUX_IMAGE) - 1, ++ cmdline_size, ++ GRUB_VERIFY_KERNEL_CMDLINE); ++ if (err) ++ goto fail; ++ ++ if (grub_errno == GRUB_ERR_NONE) ++ { ++ grub_loader_set (grub_linux_boot, grub_linux_unload, 0); ++ loaded = 1; ++ } ++ ++fail: ++ if (file) ++ grub_file_close (file); ++ ++ if (grub_errno != GRUB_ERR_NONE) ++ { ++ grub_dl_unref (my_mod); ++ loaded = 0; ++ } ++ ++ if (linux_args && !loaded) ++ grub_free (linux_args); ++ ++ if (kernel_addr && !loaded) ++ grub_efi_free_pages ((grub_addr_t) kernel_addr, ++ GRUB_EFI_BYTES_TO_PAGES (kernel_size)); ++ ++ return grub_errno; ++} ++ ++ ++static grub_command_t cmd_linux, cmd_initrd; ++ ++GRUB_MOD_INIT (linux) ++{ ++ cmd_linux = grub_register_command ("linux", grub_cmd_linux, 0, ++ N_("Load Linux.")); ++ cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, 0, ++ N_("Load initrd.")); ++ my_mod = mod; ++} ++ ++GRUB_MOD_FINI (linux) ++{ ++ grub_unregister_command (cmd_linux); ++ grub_unregister_command (cmd_initrd); ++} +--- a/include/grub/arm/linux.h ++++ b/include/grub/arm/linux.h +@@ -20,10 +20,22 @@ + #ifndef GRUB_ARM_LINUX_HEADER + #define GRUB_ARM_LINUX_HEADER 1 + ++#include + #include "system.h" + + #include + ++struct grub_arm_linux_pe_header ++{ ++ grub_uint32_t magic; ++ struct grub_pe32_coff_header coff; ++ struct grub_pe32_optional_header opt; ++}; ++ ++#if defined(__arm__) ++# define grub_armxx_linux_pe_header grub_arm_linux_pe_header ++#endif ++ + #if defined GRUB_MACHINE_UBOOT + # include + # define LINUX_ADDRESS (start_of_ram + 0x8000) +--- /dev/null ++++ b/include/grub/arm64/linux.h +@@ -0,0 +1,39 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2013 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_ARM64_LINUX_HEADER ++#define GRUB_ARM64_LINUX_HEADER 1 ++ ++#include ++#include ++ ++#define GRUB_LINUX_ARM64_MAGIC_SIGNATURE 0x644d5241 /* 'ARM\x64' */ ++ ++struct grub_arm64_linux_pe_header ++{ ++ grub_uint32_t magic; ++ struct grub_pe32_coff_header coff; ++ struct grub_pe64_optional_header opt; ++}; ++ ++#if defined(__aarch64__) ++# define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM64_MAGIC_SIGNATURE ++# define grub_armxx_linux_pe_header grub_arm64_linux_pe_header ++#endif ++ ++#endif /* ! GRUB_ARM64_LINUX_HEADER */ diff --git a/0001-Factor-out-grub_efi_linux_boot.patch b/0001-Factor-out-grub_efi_linux_boot.patch new file mode 100644 index 0000000..96e82e4 --- /dev/null +++ b/0001-Factor-out-grub_efi_linux_boot.patch @@ -0,0 +1,215 @@ +From 82d95254ca0496c8843113665bb9a99876101025 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Fri, 8 Oct 2021 13:36:45 +0800 +Subject: [PATCH 01/11] Factor out grub_efi_linux_boot + +Both x86 and arm64 on efi are using handover protocol to boot linux +kernel. To enable better code reuse, factor out grub_efi_linux_boot from +arm64 so that it can be shared with x86 platform for the common fixes. + +Signed-off-by: Michael Chang +--- + grub-core/Makefile.core.def | 1 + + grub-core/loader/arm64/efi/linux.c | 35 +----------------- + grub-core/loader/efi/linux.c | 58 ++++++++++++++++++++++++++++++ + grub-core/loader/i386/efi/linux.c | 13 ++----- + include/grub/efi/linux.h | 29 +++++++++++++++ + 5 files changed, 92 insertions(+), 44 deletions(-) + create mode 100644 grub-core/loader/efi/linux.c + create mode 100644 include/grub/efi/linux.h + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -1860,6 +1860,9 @@ + riscv64 = loader/efi/linux.c; + emu = loader/emu/linux.c; + common = loader/linux.c; ++ i386_efi = loader/efi/linux_boot.c; ++ x86_64_efi = loader/efi/linux_boot.c; ++ arm64 = loader/efi/linux_boot.c; + }; + + module = { +--- a/grub-core/loader/arm64/efi/linux.c ++++ b/grub-core/loader/arm64/efi/linux.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -51,40 +52,6 @@ + static grub_addr_t initrd_start; + static grub_addr_t initrd_end; + +-#pragma GCC diagnostic push +-#pragma GCC diagnostic ignored "-Wcast-align" +- +-typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *); +- +-static grub_err_t +-grub_efi_linux_boot (void *kernel_address, grub_off_t offset, +- void *kernel_params) +-{ +- grub_efi_loaded_image_t *loaded_image = NULL; +- handover_func hf; +- +- /* +- * Since the EFI loader is not calling the LoadImage() and StartImage() +- * services for loading the kernel and booting respectively, it has to +- * set the Loaded Image base address. +- */ +- loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle); +- if (loaded_image) +- loaded_image->image_base = kernel_addr; +- else +- grub_dprintf ("linux", "Loaded Image base address could not be set\n"); +- +- grub_dprintf ("linux", "kernel_addr: %p handover_offset: %p params: %p\n", +- kernel_address, (void *)(grub_efi_uintn_t)offset, kernel_params); +- hf = (handover_func)((char *)kernel_address + offset); +- grub_dprintf ("linux", "handover_func() = %p\n", hf); +- hf (grub_efi_image_handle, grub_efi_system_table, kernel_params); +- +- return GRUB_ERR_BUG; +-} +- +-#pragma GCC diagnostic pop +- + static grub_err_t + grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh) + { +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -40,26 +41,18 @@ + + #define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) + +-typedef void(*handover_func)(void *, grub_efi_system_table_t *, struct linux_kernel_params *); +- + static grub_err_t + grub_linuxefi_boot (void) + { +- handover_func hf; + int offset = 0; + + #ifdef __x86_64__ + offset = 512; + #endif +- +- hf = (handover_func)((char *)kernel_mem + handover_offset + offset); +- + asm volatile ("cli"); + +- hf (grub_efi_image_handle, grub_efi_system_table, params); +- +- /* Not reached */ +- return GRUB_ERR_NONE; ++ return grub_efi_linux_boot ((char *)kernel_mem, handover_offset + offset, ++ params); + } + + static grub_err_t +--- /dev/null ++++ b/include/grub/efi/linux.h +@@ -0,0 +1,29 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2014 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++#ifndef GRUB_EFI_LINUX_HEADER ++#define GRUB_EFI_LINUX_HEADER 1 ++ ++#include ++#include ++#include ++ ++grub_err_t ++EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset, ++ void *kernel_param); ++ ++#endif /* ! GRUB_EFI_LINUX_HEADER */ +--- /dev/null ++++ b/grub-core/loader/efi/linux_boot.c +@@ -0,0 +1,58 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2014 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wcast-align" ++ ++typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *); ++ ++grub_err_t ++grub_efi_linux_boot (void *kernel_addr, grub_off_t offset, ++ void *kernel_params) ++{ ++ grub_efi_loaded_image_t *loaded_image = NULL; ++ handover_func hf; ++ ++ /* ++ * Since the EFI loader is not calling the LoadImage() and StartImage() ++ * services for loading the kernel and booting respectively, it has to ++ * set the Loaded Image base address. ++ */ ++ loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle); ++ if (loaded_image) ++ loaded_image->image_base = kernel_addr; ++ else ++ grub_dprintf ("linux", "Loaded Image base address could not be set\n"); ++ ++ grub_dprintf ("linux", "kernel_addr: %p handover_offset: %p params: %p\n", ++ kernel_addr, (void *)(grub_efi_uintn_t)offset, kernel_params); ++ hf = (handover_func)((char *)kernel_addr + offset); ++ hf (grub_efi_image_handle, grub_efi_system_table, kernel_params); ++ ++ return GRUB_ERR_BUG; ++} ++ ++#pragma GCC diagnostic pop diff --git a/0001-Fix-infinite-boot-loop-on-headless-system-in-qemu.patch b/0001-Fix-infinite-boot-loop-on-headless-system-in-qemu.patch new file mode 100644 index 0000000..a4798e5 --- /dev/null +++ b/0001-Fix-infinite-boot-loop-on-headless-system-in-qemu.patch @@ -0,0 +1,66 @@ +From f76317d9dc35dbc576820ba6c2a6a8e41f5338b5 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 19 May 2022 13:08:12 +0800 +Subject: [PATCH] Fix infinite boot loop on headless system in qemu + +After finishing headless virtual machine installation via serial +console, the reboot fails in grub with infinte boot loop and also +keyboard input for serial console is unresponsive. + +The cause of infinte loop boils down to legacy vga driver in grub +crashes when '-dispaly none' is used as qemu's display type described in +the manual as: + +"Do not display video output. The guest will still see an emulated +graphics card, but its output will not be displayed tothe QEMU user. +This option differs from the -nographic option in that it only affects +what is done with video output; -nographic also changes the destination +of the serial and parallel port data." + +Given there's no sensible way found to skip the emulated device from the +legacy vga module, we ended up removing it from all_video dependency so +it wouldn't be loaded by default. In any case, the vbe module remain +loaded and should fulfill the requirement of most hardwares even twenty +years old or more. + +The unresponsive serial input is also fixed by ensuring that console +input is loaded via appended so that they won't fail altogether with +errors by other console device if specifying on the same list. + +Signed-off-by: Michael Chang +--- + grub-core/genmoddep.awk | 3 +++ + util/grub.d/00_header.in | 10 +++++++++- + 2 files changed, 12 insertions(+), 1 deletion(-) + +--- a/grub-core/genmoddep.awk ++++ b/grub-core/genmoddep.awk +@@ -98,6 +98,9 @@ + } + modlist = "" + while (getline <"video.lst") { ++ if ($1 == "vga") { ++ continue; ++ } + modlist = modlist " " $1; + } + printf "all_video:%s\n", modlist; +--- a/util/grub.d/00_header.in ++++ b/util/grub.d/00_header.in +@@ -287,7 +287,15 @@ + ;; + x*) + cat << EOF +-terminal_output ${GRUB_TERMINAL_OUTPUT} ++ ++for i in ${GRUB_TERMINAL_OUTPUT}; do ++ if [ x\${use_append} = xtrue ]; then ++ terminal_output --append \$i ++ elif terminal_output \$i; then ++ use_append=true; ++ fi ++done ++ + EOF + ;; + esac diff --git a/0001-Improve-TPM-key-protection-on-boot-interruptions.patch b/0001-Improve-TPM-key-protection-on-boot-interruptions.patch new file mode 100644 index 0000000..3669f4e --- /dev/null +++ b/0001-Improve-TPM-key-protection-on-boot-interruptions.patch @@ -0,0 +1,286 @@ +From fe7ed9104cef56f9e532a0c9a7164393d5d69ae1 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Fri, 17 Nov 2023 12:32:59 +0800 +Subject: [PATCH 1/4] Improve TPM key protection on boot interruptions + +The unattended boot process for full disk encryption relies on an +authorized TPM policy to ensure the system's integrity before releasing +the key to grub. Subsequently, grub assumes responsibility for securing +the boot process, directing it towards a trusted default without any +expected interruptions. Any interruption during this process indicates +potential modification attempts, and releasing the obtained key to the +next stage should not occur in such cases. + +This commit addresses a vulnerability associated with interrupted boot +processes that could potentially enable malicious modifications to the +default or trusted boot target. To reinforce system security, the code +has been updated to incorporate measures that discard the TPM protected +key in the event of boot interruptions. + +Furthermore, this patch aims to enhance code readability by renaming +structures and function names related to cryptographic keys, improving +clarity and maintainability. + +By implementing these changes, this enhancement seeks to fortify the +protection of TPM keys, thereby ensuring a more robust defense against +potential unauthorized modifications during the boot process. + +Signed-Off-by Michael Chang +--- + grub-core/commands/crypttab.c | 38 ++++++++++++++++++++++++++--------- + grub-core/disk/cryptodisk.c | 8 +++++++- + grub-core/loader/linux.c | 6 +++--- + grub-core/normal/main.c | 2 +- + grub-core/normal/menu.c | 7 +++++++ + grub-core/normal/menu_entry.c | 2 +- + include/grub/crypttab.h | 18 ++++++++++------- + 7 files changed, 59 insertions(+), 22 deletions(-) + +diff --git a/grub-core/commands/crypttab.c b/grub-core/commands/crypttab.c +index c2217ca98..9397bede9 100644 +--- a/grub-core/commands/crypttab.c ++++ b/grub-core/commands/crypttab.c +@@ -9,17 +9,20 @@ + + GRUB_MOD_LICENSE ("GPLv3+"); + +-struct grub_key_publisher *kpuber; ++grub_crypto_key_list_t *cryptokey_lst; + + grub_err_t +-grub_initrd_publish_key (const char *uuid, const char *key, grub_size_t key_len, const char *path) ++grub_cryptokey_add_or_update (const char *uuid, const char *key, grub_size_t key_len, const char *path, int is_tpmkey) + { +- struct grub_key_publisher *cur = NULL; ++ grub_crypto_key_list_t *cur = NULL; + +- FOR_LIST_ELEMENTS (cur, kpuber) ++ FOR_LIST_ELEMENTS (cur, cryptokey_lst) + if (grub_uuidcasecmp (cur->name, uuid, sizeof (cur->name)) == 0) + break; + ++ if (!cur && !uuid) ++ return GRUB_ERR_NONE; ++ + if (!cur) + cur = grub_zalloc (sizeof (*cur)); + if (!cur) +@@ -44,21 +47,24 @@ grub_initrd_publish_key (const char *uuid, const char *key, grub_size_t key_len, + cur->path = grub_strdup (path); + } + ++ if (is_tpmkey >= 0) ++ cur->is_tpmkey = is_tpmkey; ++ + if (!cur->name) + { + cur->name = grub_strdup (uuid); +- grub_list_push (GRUB_AS_LIST_P (&kpuber), GRUB_AS_LIST (cur)); ++ grub_list_push (GRUB_AS_LIST_P (&cryptokey_lst), GRUB_AS_LIST (cur)); + } + + return GRUB_ERR_NONE; + } + + void +-grub_initrd_discard_key (void) ++grub_cryptokey_discard (void) + { +- struct grub_key_publisher *cur, *nxt; ++ grub_crypto_key_list_t *cur, *nxt; + +- FOR_LIST_ELEMENTS_SAFE (cur, nxt, kpuber) ++ FOR_LIST_ELEMENTS_SAFE (cur, nxt, cryptokey_lst) + { + grub_list_remove (GRUB_AS_LIST (cur)); + grub_memset (cur->key, 0, cur->key_len); +@@ -69,6 +75,20 @@ grub_initrd_discard_key (void) + } + } + ++void ++grub_cryptokey_tpmkey_discard (void) ++{ ++ grub_crypto_key_list_t *cur = NULL; ++ ++ FOR_LIST_ELEMENTS (cur, cryptokey_lst) ++ if (cur->is_tpmkey) ++ break; ++ ++ /* Discard all keys if any of them is tpm */ ++ if (cur) ++ grub_cryptokey_discard(); ++} ++ + static grub_err_t + grub_cmd_crypttab_entry (grub_command_t cmd __attribute__ ((unused)), + int argc, char **argv) +@@ -92,7 +112,7 @@ grub_cmd_crypttab_entry (grub_command_t cmd __attribute__ ((unused)), + } + + /*FIXME: Validate UUID string*/ +- return grub_initrd_publish_key (argv[1], NULL, 0, path); ++ return grub_cryptokey_add_or_update (argv[1], NULL, 0, path, -1); + } + + static grub_command_t cmd; +diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c +index c79d4125a..d90ca06dc 100644 +--- a/grub-core/disk/cryptodisk.c ++++ b/grub-core/disk/cryptodisk.c +@@ -1071,6 +1071,9 @@ grub_cryptodisk_scan_device_real (const char *name, + struct cryptodisk_read_hook_ctx read_hook_data = {0}; + int askpass = 0; + char *part = NULL; ++#ifndef GRUB_UTIL ++ int is_tpmkey = 0; ++#endif + + dev = grub_cryptodisk_get_by_source_disk (source); + +@@ -1183,6 +1186,9 @@ grub_cryptodisk_scan_device_real (const char *name, + ret = grub_cryptodisk_insert (dev, name, source); + if (ret != GRUB_ERR_NONE) + goto error; ++#ifndef GRUB_UTIL ++ is_tpmkey = 1; ++#endif + goto cleanup; + } + } +@@ -1244,7 +1250,7 @@ grub_cryptodisk_scan_device_real (const char *name, + + #ifndef GRUB_UTIL + if (cargs->key_data && dev) +- grub_initrd_publish_key (dev->uuid, (const char *)cargs->key_data, cargs->key_len, NULL); ++ grub_cryptokey_add_or_update (dev->uuid, (const char *)cargs->key_data, cargs->key_len, NULL, is_tpmkey); + #endif + if (askpass) + { +diff --git a/grub-core/loader/linux.c b/grub-core/loader/linux.c +index 9ee8f3790..e5e792958 100644 +--- a/grub-core/loader/linux.c ++++ b/grub-core/loader/linux.c +@@ -226,13 +226,13 @@ grub_initrd_init (int argc, char *argv[], + int i; + int newc = 0; + struct dir *root = 0; +- struct grub_key_publisher *pk; ++ grub_crypto_key_list_t *pk; + int numkey = 0; + + initrd_ctx->nfiles = 0; + initrd_ctx->components = 0; + +- FOR_LIST_ELEMENTS (pk, kpuber) ++ FOR_LIST_ELEMENTS (pk, cryptokey_lst) + if (pk->key && pk->path) + numkey++; + +@@ -305,7 +305,7 @@ grub_initrd_init (int argc, char *argv[], + goto overflow; + } + +- FOR_LIST_ELEMENTS (pk, kpuber) ++ FOR_LIST_ELEMENTS (pk, cryptokey_lst) + if (pk->key && pk->path) + { + grub_initrd_component (pk->key, pk->key_len, pk->path, initrd_ctx); +diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c +index a3f711d1d..1b426af69 100644 +--- a/grub-core/normal/main.c ++++ b/grub-core/normal/main.c +@@ -479,7 +479,7 @@ grub_cmdline_run (int nested, int force_auth) + return; + } + +- grub_initrd_discard_key (); ++ grub_cryptokey_discard (); + grub_normal_reader_init (nested); + + while (1) +diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c +index 14b0ab1ec..1df2638d7 100644 +--- a/grub-core/normal/menu.c ++++ b/grub-core/normal/menu.c +@@ -32,6 +32,7 @@ + #include + #include + #include ++#include + + /* Time to delay after displaying an error message about a default/fallback + entry failing to boot. */ +@@ -708,6 +709,7 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot, int *notify_boot) + if (grub_key_is_interrupt (key)) + { + timeout = -1; ++ grub_cryptokey_tpmkey_discard(); + break; + } + +@@ -790,6 +792,11 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot, int *notify_boot) + clear_timeout (); + } + ++ /* Timeout is interrupted by external input, Forget tpmkey if timeout ++ * is not cut by enter */ ++ if (c != '\n' && c != '\r') ++ grub_cryptokey_tpmkey_discard(); ++ + switch (c) + { + case GRUB_TERM_KEY_HOME: +diff --git a/grub-core/normal/menu_entry.c b/grub-core/normal/menu_entry.c +index 384ab9ce3..e5ba91ea4 100644 +--- a/grub-core/normal/menu_entry.c ++++ b/grub-core/normal/menu_entry.c +@@ -1263,7 +1263,7 @@ grub_menu_entry_run (grub_menu_entry_t entry) + return; + } + +- grub_initrd_discard_key(); ++ grub_cryptokey_discard(); + + screen = make_screen (entry); + if (! screen) +diff --git a/include/grub/crypttab.h b/include/grub/crypttab.h +index 113c53cfc..f86404686 100644 +--- a/include/grub/crypttab.h ++++ b/include/grub/crypttab.h +@@ -4,21 +4,25 @@ + #include + #include + +-struct grub_key_publisher ++typedef struct grub_crypto_key_list + { +- struct grub_key_publisher *next; +- struct grub_key_publisher **prev; ++ struct grub_crypto_key_list *next; ++ struct grub_crypto_key_list **prev; + char *name; /* UUID */ + char *path; + char *key; + grub_size_t key_len; +-}; ++ int is_tpmkey; ++} grub_crypto_key_list_t; + +-extern struct grub_key_publisher *EXPORT_VAR (kpuber); ++extern grub_crypto_key_list_t *EXPORT_VAR (cryptokey_lst); + + grub_err_t +-grub_initrd_publish_key (const char *uuid, const char *key, grub_size_t key_len, const char *path); ++grub_cryptokey_add_or_update (const char *uuid, const char *key, grub_size_t key_len, const char *path, int is_tpmkey); + + void +-grub_initrd_discard_key (void); ++grub_cryptokey_discard (void); ++ ++void ++grub_cryptokey_tpmkey_discard (void); + #endif /* ! GRUB_CRYPTTAB_HEADER */ +-- +2.42.1 + diff --git a/0001-Make-grub.cfg-compatible-to-old-binaries.patch b/0001-Make-grub.cfg-compatible-to-old-binaries.patch new file mode 100644 index 0000000..57e3c66 --- /dev/null +++ b/0001-Make-grub.cfg-compatible-to-old-binaries.patch @@ -0,0 +1,82 @@ +From b8457f2e271917c5c83a4fee286bafedf8c5790c Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Tue, 8 Aug 2023 17:57:24 +0800 +Subject: [PATCH] Make grub.cfg compatible to old binaries + +The new added fwsetup test in the topmost menu is always executed +regardless older grub may not be able to handle and thus trapped in a +boot loop between grub and fwsetup. + +This in particular is to make sure a smooth transition if grub is rolled +back to older release and needs to boot newer snapshots. + +Also removing dashes in the UUID that every version released in the wild +can handle. + +Signed-off-by: Michael Chang +--- + util/grub-probe.c | 20 +++++++++++++++++++- + util/grub.d/30_uefi-firmware.in | 16 ++++++++++------ + 2 files changed, 29 insertions(+), 7 deletions(-) + +diff --git a/util/grub-probe.c b/util/grub-probe.c +index e7efcc268..99c738e44 100644 +--- a/util/grub-probe.c ++++ b/util/grub-probe.c +@@ -290,8 +290,26 @@ probe_cryptodisk_uuid (grub_disk_t disk, char delim) + } + if (disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) + { ++ grub_size_t i, j; + const char *uu = grub_util_cryptodisk_get_uuid (disk); +- grub_printf ("%s%c", uu, delim); ++ grub_size_t len = grub_strlen (uu); ++ char *p = grub_malloc (len + 1); ++ ++ /* Removing dash in the UUID string ++ * This keeps old grub binary to work with newer config in a system, ++ * especially for snapshots. It is a temporary change to make sure smooth ++ * transition from 2.06 to 2.12-rc1 and this hunk can be removed ++ * after 2.12-rc1 release stablized. ++ */ ++ for (i = 0, j = 0; i < len; i++) ++ { ++ if (uu[i] != '-') ++ p[j++] = uu[i]; ++ } ++ p[j] = '\0'; ++ ++ grub_printf ("%s%c", p, delim); ++ grub_free (p); + } + } + +diff --git a/util/grub.d/30_uefi-firmware.in b/util/grub.d/30_uefi-firmware.in +index 1c2365ddb..96ff112e5 100644 +--- a/util/grub.d/30_uefi-firmware.in ++++ b/util/grub.d/30_uefi-firmware.in +@@ -32,11 +32,15 @@ gettext_printf "Adding boot menu entry for UEFI Firmware Settings ...\n" >&2 + + cat << EOF + if [ "\$grub_platform" = "efi" ]; then +- fwsetup --is-supported +- if [ "\$?" = 0 ]; then +- menuentry '$LABEL' \$menuentry_id_option 'uefi-firmware' { +- fwsetup +- } +- fi ++ menuentry '$LABEL' \$menuentry_id_option 'uefi-firmware' { ++ fwsetup --is-supported ++ if [ "\$?" = 0 ]; then ++ fwsetup ++ else ++ echo "Your firmware doesn't support setup menu entry from a boot loader" ++ echo "Press any key to return ..." ++ read ++ fi ++ } + fi + EOF +-- +2.41.0 + diff --git a/0001-Unify-the-check-to-enable-btrfs-relative-path.patch b/0001-Unify-the-check-to-enable-btrfs-relative-path.patch new file mode 100644 index 0000000..3f9d79e --- /dev/null +++ b/0001-Unify-the-check-to-enable-btrfs-relative-path.patch @@ -0,0 +1,150 @@ +From 80bb1b17b3f596dbd7331cf9cb20a46c8ef9800b Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Sat, 22 Aug 2020 02:32:43 +0800 +Subject: [PATCH] Unify the check to enable btrfs relative path + +This unified the test in grub-install and grub-mkconfig that the path to +default or selected btrfs subvolume/snapshot is used if the root file +system is btrfs and the config has enabled btrfs snapshot booting. + +Signed-off-by: Michael Chang +--- + util/grub-install.c | 67 +++++++++++++++++++++++++++------------ + util/grub-mkconfig_lib.in | 3 +- + 2 files changed, 48 insertions(+), 22 deletions(-) + +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -886,6 +886,7 @@ + const char *efi_file = NULL; + char **grub_devices; + grub_fs_t grub_fs; ++ grub_fs_t root_fs; + grub_device_t grub_dev = NULL; + enum grub_install_plat platform; + char *grubdir, *device_map; +@@ -898,6 +899,8 @@ + int efidir_is_mac = 0; + int is_prep = 0; + const char *pkgdatadir; ++ char *rootdir_path; ++ char **rootdir_devices; + + grub_util_host_init (&argc, &argv); + product_version = xstrdup (PACKAGE_VERSION); +@@ -911,9 +914,6 @@ + + grub_util_load_config (&config); + +- if (config.is_suse_btrfs_snapshot_enabled) +- use_relative_path_on_btrfs = 1; +- + if (!bootloader_id && config.grub_distributor) + { + char *ptr; +@@ -1064,6 +1064,45 @@ + grub_hostfs_init (); + grub_host_init (); + ++ { ++ char *rootdir_grub_devname; ++ grub_device_t rootdir_grub_dev; ++ char *t = grub_util_path_concat (2, "/", rootdir); ++ ++ rootdir_path = grub_canonicalize_file_name (t); ++ if (!rootdir_path) ++ grub_util_error (_("failed to get canonical path of `%s'"), t); ++ ++ rootdir_devices = grub_guess_root_devices (rootdir_path); ++ if (!rootdir_devices || !rootdir_devices[0]) ++ grub_util_error (_("cannot find a device for %s (is /dev mounted?)"), ++ rootdir_path); ++ ++ for (curdev = rootdir_devices; *curdev; curdev++) ++ grub_util_pull_device (*curdev); ++ ++ rootdir_grub_devname = grub_util_get_grub_dev (rootdir_devices[0]); ++ if (!rootdir_grub_devname) ++ grub_util_error (_("cannot find a GRUB drive for %s. Check your device.map"), ++ rootdir_devices[0]); ++ ++ rootdir_grub_dev = grub_device_open (rootdir_grub_devname); ++ if (! rootdir_grub_dev) ++ grub_util_error ("%s", grub_errmsg); ++ ++ root_fs = grub_fs_probe (rootdir_grub_dev); ++ if (!root_fs) ++ grub_util_error ("%s", grub_errmsg); ++ ++ if (config.is_suse_btrfs_snapshot_enabled ++ && grub_strncmp(root_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0) ++ use_relative_path_on_btrfs = 1; ++ ++ free (t); ++ free (rootdir_grub_devname); ++ grub_device_close (rootdir_grub_dev); ++ } ++ + switch (platform) + { + case GRUB_INSTALL_PLATFORM_I386_EFI: +@@ -1454,8 +1493,7 @@ + debug_image); + } + +- if (config.is_suse_btrfs_snapshot_enabled +- && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0) ++ if (use_relative_path_on_btrfs) + { + if (!load_cfg_f) + load_cfg_f = grub_util_fopen (load_cfg, "wb"); +@@ -1669,21 +1707,13 @@ + + #ifdef __linux__ + +- if (config.is_suse_btrfs_snapshot_enabled +- && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0) ++ if (use_relative_path_on_btrfs) + { + char *subvol = NULL; + char *mount_path = NULL; +- char **rootdir_devices = NULL; +- char *t = grub_util_path_concat (2, "/", rootdir); +- char *rootdir_path = grub_canonicalize_file_name (t); +- +- if (rootdir_path && grub_util_is_directory (rootdir_path)) +- rootdir_devices = grub_guess_root_devices (rootdir_path); +- +- if (rootdir_devices && rootdir_devices[0]) +- if (grub_strcmp (rootdir_devices[0], grub_devices[0]) == 0) +- subvol = grub_util_get_btrfs_subvol (platdir, &mount_path); ++ ++ if (grub_strcmp (rootdir_devices[0], grub_devices[0]) == 0) ++ subvol = grub_util_get_btrfs_subvol (platdir, &mount_path); + + if (subvol && mount_path) + { +@@ -1708,11 +1738,6 @@ + } + } + +- free (t); +- free (rootdir_path); +- for (curdev = rootdir_devices; *curdev; curdev++) +- free (*curdev); +- free (rootdir_devices); + free (subvol); + free (mount_path); + } +--- a/util/grub-mkconfig_lib.in ++++ b/util/grub-mkconfig_lib.in +@@ -49,7 +49,8 @@ + + make_system_path_relative_to_its_root () + { +- if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] ; then ++ if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] && ++ [ "x${GRUB_FS}" = "xbtrfs" ] ; then + "${grub_mkrelpath}" -r "$1" + else + "${grub_mkrelpath}" "$1" diff --git a/0001-Workaround-volatile-efi-boot-variable.patch b/0001-Workaround-volatile-efi-boot-variable.patch new file mode 100644 index 0000000..2541f11 --- /dev/null +++ b/0001-Workaround-volatile-efi-boot-variable.patch @@ -0,0 +1,289 @@ +From 71575829c303fe8522b46fc96b1f99f1aa4178e7 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Fri, 19 Mar 2021 22:58:45 +0800 +Subject: [PATCH] Workaround volatile efi boot variable + +The efi variable in Microsoft Azure virtual machine is volatile that it cannot +persist across power cycling. If we use efi variable to communicate with efi +boot manager for booting a distribution, the process would silently fail as the +default loader in the efi system partition will start to take over the process +whenever the efi variable evaporated. + +That will lead to undefined symbol error one day as the default path didn't +receive any grub update so it cannot keep up with new ABI requirement by +updated grub modules. + +The patch will try to workaround the problem by providing grub update to the +default path along with the distribution specific one. To avoid negative side +effects of inadvertently overwritting other loader intended in default path, +care must be taken to ensure that: + +1. The workaround only takes place on detected Azure virtual machine +2. The default path is not in use by shim for the secure boot +--- + Makefile.util.def | 1 + + .../osdep/basic/efi_removable_fallback.c | 26 +++ + grub-core/osdep/efi_removable_fallback.c | 5 + + .../osdep/linux/efi_removable_fallback.c | 151 ++++++++++++++++++ + include/grub/util/install.h | 3 + + util/grub-install.c | 19 +++ + 6 files changed, 205 insertions(+) + create mode 100644 grub-core/osdep/basic/efi_removable_fallback.c + create mode 100644 grub-core/osdep/efi_removable_fallback.c + create mode 100644 grub-core/osdep/linux/efi_removable_fallback.c + +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -681,6 +681,9 @@ + common = grub-core/osdep/journaled_fs.c; + extra_dist = grub-core/osdep/basic/journaled_fs.c; + extra_dist = grub-core/osdep/linux/journaled_fs.c; ++ common = grub-core/osdep/efi_removable_fallback.c; ++ extra_dist = grub-core/osdep/basic/efi_removable_fallback.c; ++ extra_dist = grub-core/osdep/linux/efi_removable_fallback.c; + + ldadd = '$(LIBLZMA)'; + ldadd = libgrubmods.a; +--- /dev/null ++++ b/grub-core/osdep/basic/efi_removable_fallback.c +@@ -0,0 +1,26 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2013 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++ ++const char * ++grub_install_efi_removable_fallback (const char *efidir, enum grub_install_plat platform) ++{ ++ return NULL; ++} ++ +--- /dev/null ++++ b/grub-core/osdep/efi_removable_fallback.c +@@ -0,0 +1,5 @@ ++#ifdef __linux__ ++#include "linux/efi_removable_fallback.c" ++#else ++#include "basic/efi_removable_fallback.c" ++#endif +--- /dev/null ++++ b/grub-core/osdep/linux/efi_removable_fallback.c +@@ -0,0 +1,151 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2013 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++static char * ++get_dmi_id (const char *id) ++{ ++ FILE *fp; ++ char *buf = NULL; ++ size_t len = 0; ++ ++ char *dmi_entry; ++ ++ dmi_entry = grub_util_path_concat (2, "/sys/class/dmi/id", id); ++ ++ fp = grub_util_fopen (dmi_entry, "r"); ++ if (!fp) ++ { ++ free (dmi_entry); ++ return NULL; ++ } ++ ++ if (getline (&buf, &len, fp) == -1) ++ { ++ fclose (fp); ++ free (dmi_entry); ++ return NULL; ++ } ++ ++ fclose (fp); ++ free (dmi_entry); ++ return buf; ++} ++ ++ ++static struct dmi { ++ const char *id; ++ const char *val; ++} azure_dmi [3] = { ++ {"bios_vendor", "Microsoft Corporation"}, ++ {"product_name", "Virtual Machine"}, ++ {"sys_vendor", "Microsoft Corporation"}, ++}; ++ ++static int ++is_azure (void) ++{ ++ int i; ++ int n = sizeof (azure_dmi) / sizeof (struct dmi); ++ ++ for (i = 0; i < n; ++i) ++ { ++ char *val; ++ ++ val = get_dmi_id (azure_dmi[i].id); ++ if (!val) ++ break; ++ if (strncmp (val, azure_dmi[i].val, strlen (azure_dmi[i].val)) != 0) ++ { ++ free (val); ++ break; ++ } ++ free (val); ++ } ++ ++ return (i == n) ? 1 : 0; ++} ++ ++static int ++guess_shim_installed (const char *instdir) ++{ ++ const char *shim[] = {"fallback.efi", "MokManager.efi", NULL}; ++ const char **s; ++ ++ for (s = shim; *s ; ++s) ++ { ++ char *p = grub_util_path_concat (2, instdir, *s); ++ ++ if (access (p, F_OK) == 0) ++ { ++ free (p); ++ return 1; ++ } ++ free (p); ++ } ++ ++ return 0; ++} ++ ++const char * ++grub_install_efi_removable_fallback (const char *efidir, enum grub_install_plat platform) ++{ ++ char *instdir; ++ ++ if (!is_azure ()) ++ return NULL; ++ ++ instdir = grub_util_path_concat (3, efidir, "EFI", "BOOT"); ++ ++ if (guess_shim_installed (instdir)) ++ { ++ grub_util_info ("skip removable fallback occupied by shim"); ++ return NULL; ++ } ++ ++ free (instdir); ++ ++ switch (platform) ++ { ++ case GRUB_INSTALL_PLATFORM_I386_EFI: ++ return "BOOTIA32.EFI"; ++ case GRUB_INSTALL_PLATFORM_X86_64_EFI: ++ return "BOOTX64.EFI"; ++ case GRUB_INSTALL_PLATFORM_IA64_EFI: ++ return "BOOTIA64.EFI"; ++ case GRUB_INSTALL_PLATFORM_ARM_EFI: ++ return "BOOTARM.EFI"; ++ case GRUB_INSTALL_PLATFORM_ARM64_EFI: ++ return "BOOTAA64.EFI"; ++ case GRUB_INSTALL_PLATFORM_RISCV32_EFI: ++ return "BOOTRISCV32.EFI"; ++ case GRUB_INSTALL_PLATFORM_RISCV64_EFI: ++ return "BOOTRISCV64.EFI"; ++ default: ++ grub_util_error ("%s", _("You've found a bug")); ++ break; ++ } ++ return NULL; ++} ++ +--- a/include/grub/util/install.h ++++ b/include/grub/util/install.h +@@ -303,4 +303,7 @@ + + int + grub_install_sync_fs_journal (const char *path); ++ ++const char * ++grub_install_efi_removable_fallback (const char *efidir, enum grub_install_plat platform); + #endif +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -901,6 +901,7 @@ + const char *pkgdatadir; + char *rootdir_path; + char **rootdir_devices; ++ char *efidir_root; + + grub_util_host_init (&argc, &argv); + product_version = xstrdup (PACKAGE_VERSION); +@@ -1175,6 +1176,7 @@ + } + if (!efidir) + grub_util_error ("%s", _("cannot find EFI directory")); ++ efidir_root = grub_strdup (efidir); + efidir_device_names = grub_guess_root_devices (efidir); + if (!efidir_device_names || !efidir_device_names[0]) + grub_util_error (_("cannot find a device for %s (is /dev mounted?)"), +@@ -2217,6 +2219,23 @@ + free (grub_efi_cfg); + } + } ++ if (!removable) ++ { ++ const char *f; ++ ++ f = grub_install_efi_removable_fallback (efidir_root, platform); ++ if (f) ++ { ++ char *t = grub_util_path_concat (3, efidir_root, "EFI", "BOOT"); ++ char *dst = grub_util_path_concat (2, t, f); ++ ++ grub_install_mkdir_p (t); ++ fprintf (stderr, _("Install to %s as fallback.\n"), dst); ++ grub_install_copy_file (imgfile, dst, 1); ++ grub_free (t); ++ grub_free (dst); ++ } ++ } + if (!removable && update_nvram) + { + char * efifile_path; diff --git a/0001-add-support-for-UEFI-network-protocols.patch b/0001-add-support-for-UEFI-network-protocols.patch new file mode 100644 index 0000000..5a8b94d --- /dev/null +++ b/0001-add-support-for-UEFI-network-protocols.patch @@ -0,0 +1,4923 @@ +From 5d6111790e1cd07d1156f47bca0733f6d715337f Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Wed, 22 Feb 2017 14:27:50 +0800 +Subject: [PATCH] Support UEFI networking protocols + +References: fate#320130, bsc#1015589, bsc#1076132 +Patch-Mainline: no + +V1: + * Add preliminary support of UEFI networking protocols + * Support UEFI HTTPS Boot + +V2: + * Workaround http data access in firmware + * Fix DNS device path parsing for efinet device + * Relaxed UEFI Protocol requirement + * Support Intel OPA (Omni-Path Architecture) PXE Boot + +V3: + * Fix bufio in calculating address of next_buf + * Check HTTP respond code + * Use HEAD request method to test before GET + * Finish HTTP transaction in one go + * Fix bsc#1076132 + +V4: + * Add fs_ prefix with upstream commit + ad4bfeec5 Change fs functions to add fs_ prefix + +V5: + * Use overflow checking primitives where the arithmetic expression for + buffer allocations may include unvalidated data + * Use grub_calloc for overflow check and return NULL when it would + occur. + +V6: + * Don't force grub_print_error if no best route found as boot process + could be interrupted by logged error. The default interface will be + used as fallback in this case + +--- + grub-core/Makefile.core.def | 18 + + grub-core/io/bufio.c | 2 +- + grub-core/kern/efi/efi.c | 96 ++- + grub-core/net/drivers/efi/efinet.c | 27 + + grub-core/net/efi/dhcp.c | 397 ++++++++++ + grub-core/net/efi/efi_netfs.c | 57 ++ + grub-core/net/efi/http.c | 419 +++++++++++ + grub-core/net/efi/ip4_config.c | 398 ++++++++++ + grub-core/net/efi/ip6_config.c | 422 +++++++++++ + grub-core/net/efi/net.c | 1428 ++++++++++++++++++++++++++++++++++++ + grub-core/net/efi/pxe.c | 424 +++++++++++ + grub-core/net/net.c | 74 ++ + include/grub/efi/api.h | 181 ++++- + include/grub/efi/dhcp.h | 343 +++++++++ + include/grub/efi/http.h | 215 ++++++ + include/grub/net/efi.h | 144 ++++ + util/grub-mknetdir.c | 23 +- + 17 files changed, 4627 insertions(+), 41 deletions(-) + create mode 100644 grub-core/net/efi/dhcp.c + create mode 100644 grub-core/net/efi/efi_netfs.c + create mode 100644 grub-core/net/efi/http.c + create mode 100644 grub-core/net/efi/ip4_config.c + create mode 100644 grub-core/net/efi/ip6_config.c + create mode 100644 grub-core/net/efi/net.c + create mode 100644 grub-core/net/efi/pxe.c + create mode 100644 include/grub/efi/dhcp.h + create mode 100644 include/grub/efi/http.h + create mode 100644 include/grub/net/efi.h + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2362,6 +2362,12 @@ + common = net/ethernet.c; + common = net/arp.c; + common = net/netbuff.c; ++ efi = net/efi/net.c; ++ efi = net/efi/http.c; ++ efi = net/efi/pxe.c; ++ efi = net/efi/ip4_config.c; ++ efi = net/efi/ip6_config.c; ++ efi = net/efi/dhcp.c; + }; + + module = { +--- a/grub-core/io/bufio.c ++++ b/grub-core/io/bufio.c +@@ -139,7 +139,7 @@ + return res; + + /* Need to read some more. */ +- next_buf = (file->offset + res + len - 1) & ~((grub_off_t) bufio->block_size - 1); ++ next_buf = (grub_divmod64 (file->offset + res + len - 1, bufio->block_size, NULL)) * bufio->block_size; + /* Now read between file->offset + res and bufio->buffer_at. */ + if (file->offset + res < next_buf) + { +--- a/grub-core/kern/efi/efi.c ++++ b/grub-core/kern/efi/efi.c +@@ -770,7 +770,7 @@ + { + grub_efi_ipv4_device_path_t *ipv4 + = (grub_efi_ipv4_device_path_t *) dp; +- grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x)", ++ grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x", + (unsigned) ipv4->local_ip_address[0], + (unsigned) ipv4->local_ip_address[1], + (unsigned) ipv4->local_ip_address[2], +@@ -783,33 +783,60 @@ + (unsigned) ipv4->remote_port, + (unsigned) ipv4->protocol, + (unsigned) ipv4->static_ip_address); ++ if (len == sizeof (*ipv4)) ++ { ++ grub_printf (",%u.%u.%u.%u,%u.%u.%u.%u", ++ (unsigned) ipv4->gateway_ip_address[0], ++ (unsigned) ipv4->gateway_ip_address[1], ++ (unsigned) ipv4->gateway_ip_address[2], ++ (unsigned) ipv4->gateway_ip_address[3], ++ (unsigned) ipv4->subnet_mask[0], ++ (unsigned) ipv4->subnet_mask[1], ++ (unsigned) ipv4->subnet_mask[2], ++ (unsigned) ipv4->subnet_mask[3]); ++ } ++ grub_printf (")"); + } + break; + case GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE: + { + grub_efi_ipv6_device_path_t *ipv6 + = (grub_efi_ipv6_device_path_t *) dp; +- grub_printf ("/IPv6(%x:%x:%x:%x:%x:%x:%x:%x,%x:%x:%x:%x:%x:%x:%x:%x,%u,%u,%x,%x)", +- (unsigned) ipv6->local_ip_address[0], +- (unsigned) ipv6->local_ip_address[1], +- (unsigned) ipv6->local_ip_address[2], +- (unsigned) ipv6->local_ip_address[3], +- (unsigned) ipv6->local_ip_address[4], +- (unsigned) ipv6->local_ip_address[5], +- (unsigned) ipv6->local_ip_address[6], +- (unsigned) ipv6->local_ip_address[7], +- (unsigned) ipv6->remote_ip_address[0], +- (unsigned) ipv6->remote_ip_address[1], +- (unsigned) ipv6->remote_ip_address[2], +- (unsigned) ipv6->remote_ip_address[3], +- (unsigned) ipv6->remote_ip_address[4], +- (unsigned) ipv6->remote_ip_address[5], +- (unsigned) ipv6->remote_ip_address[6], +- (unsigned) ipv6->remote_ip_address[7], ++ grub_printf ("/IPv6(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,%u,%u,%x,%x", ++ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[0]), ++ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[1]), ++ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[2]), ++ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[3]), ++ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[4]), ++ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[5]), ++ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[6]), ++ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[7]), ++ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[0]), ++ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[1]), ++ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[2]), ++ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[3]), ++ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[4]), ++ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[5]), ++ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[6]), ++ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[7]), + (unsigned) ipv6->local_port, + (unsigned) ipv6->remote_port, + (unsigned) ipv6->protocol, + (unsigned) ipv6->static_ip_address); ++ if (len == sizeof (*ipv6)) ++ { ++ grub_printf (",%u,%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", ++ (unsigned) ipv6->prefix_length, ++ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[0]), ++ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[1]), ++ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[2]), ++ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[3]), ++ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[4]), ++ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[5]), ++ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[6]), ++ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[7])); ++ } ++ grub_printf (")"); + } + break; + case GRUB_EFI_INFINIBAND_DEVICE_PATH_SUBTYPE: +@@ -856,6 +883,39 @@ + dump_vendor_path ("Messaging", + (grub_efi_vendor_device_path_t *) dp); + break; ++ case GRUB_EFI_URI_DEVICE_PATH_SUBTYPE: ++ { ++ grub_efi_uri_device_path_t *uri ++ = (grub_efi_uri_device_path_t *) dp; ++ grub_printf ("/URI(%s)", uri->uri); ++ } ++ break; ++ case GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE: ++ { ++ grub_efi_dns_device_path_t *dns ++ = (grub_efi_dns_device_path_t *) dp; ++ if (dns->is_ipv6) ++ { ++ grub_printf ("/DNS(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x)", ++ (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[0]) >> 16), ++ (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[0])), ++ (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[1]) >> 16), ++ (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[1])), ++ (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[2]) >> 16), ++ (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[2])), ++ (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[3]) >> 16), ++ (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[3]))); ++ } ++ else ++ { ++ grub_printf ("/DNS(%d.%d.%d.%d)", ++ dns->dns_server_ip[0].v4.addr[0], ++ dns->dns_server_ip[0].v4.addr[1], ++ dns->dns_server_ip[0].v4.addr[2], ++ dns->dns_server_ip[0].v4.addr[3]); ++ } ++ } ++ break; + default: + grub_printf ("/UnknownMessaging(%x)", (unsigned) subtype); + break; +--- a/grub-core/net/drivers/efi/efinet.c ++++ b/grub-core/net/drivers/efi/efinet.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -345,7 +346,7 @@ + } + + static grub_efi_handle_t +-grub_efi_locate_device_path (grub_efi_guid_t *protocol, grub_efi_device_path_t *device_path, ++grub_efi_locate_device_path (grub_guid_t *protocol, grub_efi_device_path_t *device_path, + grub_efi_device_path_t **r_device_path) + { + grub_efi_handle_t handle; +@@ -498,6 +499,17 @@ + + ldp = grub_efi_find_last_device_path (ddp); + ++ /* Skip the DNS Device */ ++ if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE ++ && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE) ++ { ++ ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; ++ ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; ++ ldp->length = sizeof (*ldp); ++ ++ ldp = grub_efi_find_last_device_path (ddp); ++ } ++ + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) +@@ -765,6 +777,7 @@ + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE ++ && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)) + continue; + dup_dp = grub_efi_duplicate_device_path (dp); +@@ -780,6 +793,15 @@ + } + + dup_ldp = grub_efi_find_last_device_path (dup_dp); ++ if (GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp) == GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE) ++ { ++ dup_ldp = grub_efi_find_last_device_path (dup_dp); ++ dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; ++ dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; ++ dup_ldp->length = sizeof (*dup_ldp); ++ } ++ ++ dup_ldp = grub_efi_find_last_device_path (dup_dp); + dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + dup_ldp->length = sizeof (*dup_ldp); +@@ -860,6 +882,9 @@ + + GRUB_MOD_INIT(efinet) + { ++ if (grub_efi_net_config) ++ return; ++ + grub_efinet_findcards (); + grub_efi_net_config = grub_efi_net_config_real; + } +@@ -871,5 +896,7 @@ + FOR_NET_CARDS_SAFE (card, next) + if (card->driver == &efidriver) + grub_net_card_unregister (card); ++ ++ grub_efi_net_config = NULL; + } + +--- /dev/null ++++ b/grub-core/net/efi/dhcp.c +@@ -0,0 +1,399 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef GRUB_EFI_NET_DEBUG ++static void ++dhcp4_mode_print (grub_efi_dhcp4_mode_data_t *mode) ++{ ++ switch (mode->state) ++ { ++ case GRUB_EFI_DHCP4_STOPPED: ++ grub_printf ("STATE: STOPPED\n"); ++ break; ++ case GRUB_EFI_DHCP4_INIT: ++ grub_printf ("STATE: INIT\n"); ++ break; ++ case GRUB_EFI_DHCP4_SELECTING: ++ grub_printf ("STATE: SELECTING\n"); ++ break; ++ case GRUB_EFI_DHCP4_REQUESTING: ++ grub_printf ("STATE: REQUESTING\n"); ++ break; ++ case GRUB_EFI_DHCP4_BOUND: ++ grub_printf ("STATE: BOUND\n"); ++ break; ++ case GRUB_EFI_DHCP4_RENEWING: ++ grub_printf ("STATE: RENEWING\n"); ++ break; ++ case GRUB_EFI_DHCP4_REBINDING: ++ grub_printf ("STATE: REBINDING\n"); ++ break; ++ case GRUB_EFI_DHCP4_INIT_REBOOT: ++ grub_printf ("STATE: INIT_REBOOT\n"); ++ break; ++ case GRUB_EFI_DHCP4_REBOOTING: ++ grub_printf ("STATE: REBOOTING\n"); ++ break; ++ default: ++ grub_printf ("STATE: UNKNOWN\n"); ++ break; ++ } ++ ++ grub_printf ("CLIENT_ADDRESS: %u.%u.%u.%u\n", ++ mode->client_address[0], ++ mode->client_address[1], ++ mode->client_address[2], ++ mode->client_address[3]); ++ grub_printf ("SERVER_ADDRESS: %u.%u.%u.%u\n", ++ mode->server_address[0], ++ mode->server_address[1], ++ mode->server_address[2], ++ mode->server_address[3]); ++ grub_printf ("SUBNET_MASK: %u.%u.%u.%u\n", ++ mode->subnet_mask[0], ++ mode->subnet_mask[1], ++ mode->subnet_mask[2], ++ mode->subnet_mask[3]); ++ grub_printf ("ROUTER_ADDRESS: %u.%u.%u.%u\n", ++ mode->router_address[0], ++ mode->router_address[1], ++ mode->router_address[2], ++ mode->router_address[3]); ++} ++#endif ++ ++static grub_efi_ipv4_address_t * ++grub_efi_dhcp4_parse_dns (grub_efi_dhcp4_protocol_t *dhcp4, grub_efi_dhcp4_packet_t *reply_packet) ++{ ++ grub_efi_dhcp4_packet_option_t **option_list; ++ grub_efi_status_t status; ++ grub_efi_uint32_t option_count = 0; ++ grub_efi_uint32_t i; ++ ++ status = dhcp4->parse (dhcp4, reply_packet, &option_count, NULL); ++ ++ if (status != GRUB_EFI_BUFFER_TOO_SMALL) ++ return NULL; ++ ++ option_list = grub_calloc (option_count, sizeof(*option_list)); ++ if (!option_list) ++ return NULL; ++ ++ status = dhcp4->parse (dhcp4, reply_packet, &option_count, option_list); ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_free (option_list); ++ return NULL; ++ } ++ ++ for (i = 0; i < option_count; ++i) ++ { ++ if (option_list[i]->op_code == 6) ++ { ++ grub_efi_ipv4_address_t *dns_address; ++ ++ if (((option_list[i]->length & 0x3) != 0) || (option_list[i]->length == 0)) ++ continue; ++ ++ /* We only contact primary dns */ ++ dns_address = grub_malloc (sizeof (*dns_address)); ++ if (!dns_address) ++ { ++ grub_free (option_list); ++ return NULL; ++ } ++ grub_memcpy (dns_address, option_list[i]->data, sizeof (dns_address)); ++ grub_free (option_list); ++ return dns_address; ++ } ++ } ++ ++ grub_free (option_list); ++ return NULL; ++} ++ ++#if 0 ++/* Somehow this doesn't work ... */ ++static grub_err_t ++grub_cmd_efi_bootp (struct grub_command *cmd __attribute__ ((unused)), ++ int argc __attribute__ ((unused)), ++ char **args __attribute__ ((unused))) ++{ ++ struct grub_efi_net_device *dev; ++ for (dev = net_devices; dev; dev = dev->next) ++ { ++ grub_efi_pxe_t *pxe = dev->ip4_pxe; ++ grub_efi_pxe_mode_t *mode = pxe->mode; ++ grub_efi_status_t status; ++ ++ if (!mode->started) ++ { ++ status = pxe->start (pxe, 0); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ grub_printf ("Couldn't start PXE\n"); ++ } ++ ++ status = pxe->dhcp (pxe, 0); ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_printf ("dhcp4 configure failed, %d\n", (int)status); ++ continue; ++ } ++ ++ dev->prefer_ip6 = 0; ++ } ++ ++ return GRUB_ERR_NONE; ++} ++#endif ++ ++static grub_err_t ++grub_cmd_efi_bootp (struct grub_command *cmd __attribute__ ((unused)), ++ int argc, ++ char **args) ++{ ++ struct grub_efi_net_device *netdev; ++ ++ for (netdev = net_devices; netdev; netdev = netdev->next) ++ { ++ grub_efi_status_t status; ++ grub_efi_dhcp4_mode_data_t mode; ++ grub_efi_dhcp4_config_data_t config; ++ grub_efi_dhcp4_packet_option_t *options; ++ grub_efi_ipv4_address_t *dns_address; ++ grub_efi_net_ip_manual_address_t net_ip; ++ grub_efi_net_ip_address_t ip_addr; ++ grub_efi_net_interface_t *inf = NULL; ++ ++ if (argc > 0 && grub_strcmp (netdev->card_name, args[0]) != 0) ++ continue; ++ ++ grub_memset (&config, 0, sizeof(config)); ++ ++ config.option_count = 1; ++ options = grub_malloc (sizeof(*options) + 2); ++ /* Parameter request list */ ++ options->op_code = 55; ++ options->length = 3; ++ /* subnet mask */ ++ options->data[0] = 1; ++ /* router */ ++ options->data[1] = 3; ++ /* DNS */ ++ options->data[2] = 6; ++ config.option_list = &options; ++ ++ /* FIXME: What if the dhcp has bounded */ ++ status = netdev->dhcp4->configure (netdev->dhcp4, &config); ++ grub_free (options); ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_printf ("dhcp4 configure failed, %d\n", (int)status); ++ continue; ++ } ++ ++ status = netdev->dhcp4->start (netdev->dhcp4, NULL); ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_printf ("dhcp4 start failed, %d\n", (int)status); ++ continue; ++ } ++ ++ status = netdev->dhcp4->get_mode_data (netdev->dhcp4, &mode); ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_printf ("dhcp4 get mode failed, %d\n", (int)status); ++ continue; ++ } ++ ++#ifdef GRUB_EFI_NET_DEBUG ++ dhcp4_mode_print (&mode); ++#endif ++ ++ for (inf = netdev->net_interfaces; inf; inf = inf->next) ++ if (inf->prefer_ip6 == 0) ++ break; ++ ++ grub_memcpy (net_ip.ip4.address, mode.client_address, sizeof (net_ip.ip4.address)); ++ grub_memcpy (net_ip.ip4.subnet_mask, mode.subnet_mask, sizeof (net_ip.ip4.subnet_mask)); ++ ++ if (!inf) ++ { ++ char *name = grub_xasprintf ("%s:dhcp", netdev->card_name); ++ ++ net_ip.is_ip6 = 0; ++ inf = grub_efi_net_create_interface (netdev, ++ name, ++ &net_ip, ++ 1); ++ grub_free (name); ++ } ++ else ++ { ++ efi_net_interface_set_address (inf, &net_ip, 1); ++ } ++ ++ grub_memcpy (ip_addr.ip4, mode.router_address, sizeof (ip_addr.ip4)); ++ efi_net_interface_set_gateway (inf, &ip_addr); ++ ++ dns_address = grub_efi_dhcp4_parse_dns (netdev->dhcp4, mode.reply_packet); ++ if (dns_address) ++ efi_net_interface_set_dns (inf, (grub_efi_net_ip_address_t *)&dns_address); ++ ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++ ++static grub_err_t ++grub_cmd_efi_bootp6 (struct grub_command *cmd __attribute__ ((unused)), ++ int argc, ++ char **args) ++{ ++ struct grub_efi_net_device *dev; ++ grub_efi_uint32_t ia_id; ++ ++ for (dev = net_devices, ia_id = 0; dev; dev = dev->next, ia_id++) ++ { ++ grub_efi_dhcp6_config_data_t config; ++ grub_efi_dhcp6_packet_option_t *option_list[1]; ++ grub_efi_dhcp6_packet_option_t *opt; ++ grub_efi_status_t status; ++ grub_efi_dhcp6_mode_data_t mode; ++ grub_efi_dhcp6_retransmission_t retrans; ++ grub_efi_net_ip_manual_address_t net_ip; ++ grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; ++ grub_efi_net_interface_t *inf = NULL; ++ ++ if (argc > 0 && grub_strcmp (dev->card_name, args[0]) != 0) ++ continue; ++ ++ opt = grub_malloc (sizeof(*opt) + 2 * sizeof (grub_efi_uint16_t)); ++ ++#define GRUB_EFI_DHCP6_OPT_ORO 6 ++ ++ opt->op_code = grub_cpu_to_be16_compile_time (GRUB_EFI_DHCP6_OPT_ORO); ++ opt->op_len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_efi_uint16_t)); ++ ++#define GRUB_EFI_DHCP6_OPT_BOOT_FILE_URL 59 ++#define GRUB_EFI_DHCP6_OPT_DNS_SERVERS 23 ++ ++ grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_BOOT_FILE_URL)); ++ grub_set_unaligned16 (opt->data + 1 * sizeof (grub_efi_uint16_t), ++ grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_DNS_SERVERS)); ++ ++ option_list[0] = opt; ++ retrans.irt = 4; ++ retrans.mrc = 4; ++ retrans.mrt = 32; ++ retrans.mrd = 60; ++ ++ config.dhcp6_callback = NULL; ++ config.callback_context = NULL; ++ config.option_count = 1; ++ config.option_list = option_list; ++ config.ia_descriptor.ia_id = ia_id; ++ config.ia_descriptor.type = GRUB_EFI_DHCP6_IA_TYPE_NA; ++ config.ia_info_event = NULL; ++ config.reconfigure_accept = 0; ++ config.rapid_commit = 0; ++ config.solicit_retransmission = &retrans; ++ ++ status = dev->dhcp6->configure (dev->dhcp6, &config); ++ grub_free (opt); ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_printf ("dhcp6 configure failed, %d\n", (int)status); ++ continue; ++ } ++ status = dev->dhcp6->start (dev->dhcp6); ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_printf ("dhcp6 start failed, %d\n", (int)status); ++ continue; ++ } ++ ++ status = dev->dhcp6->get_mode_data (dev->dhcp6, &mode, NULL); ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_printf ("dhcp4 get mode failed, %d\n", (int)status); ++ continue; ++ } ++ ++ for (inf = dev->net_interfaces; inf; inf = inf->next) ++ if (inf->prefer_ip6 == 1) ++ break; ++ ++ grub_memcpy (net_ip.ip6.address, mode.ia->ia_address[0].ip_address, sizeof (net_ip.ip6.address)); ++ net_ip.ip6.prefix_length = 64; ++ net_ip.ip6.is_anycast = 0; ++ net_ip.is_ip6 = 1; ++ ++ if (!inf) ++ { ++ char *name = grub_xasprintf ("%s:dhcp", dev->card_name); ++ ++ inf = grub_efi_net_create_interface (dev, ++ name, ++ &net_ip, ++ 1); ++ grub_free (name); ++ } ++ else ++ { ++ efi_net_interface_set_address (inf, &net_ip, 1); ++ } ++ ++ { ++ grub_efi_uint32_t count = 0; ++ grub_efi_dhcp6_packet_option_t **options = NULL; ++ grub_efi_uint32_t i; ++ ++ status = dev->dhcp6->parse (dev->dhcp6, mode.ia->reply_packet, &count, NULL); ++ ++ if (status == GRUB_EFI_BUFFER_TOO_SMALL && count) ++ { ++ options = grub_calloc (count, sizeof(*options)); ++ if (!options) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); ++ status = dev->dhcp6->parse (dev->dhcp6, mode.ia->reply_packet, &count, options); ++ } ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ if (options) ++ grub_free (options); ++ continue; ++ } ++ ++ for (i = 0; i < count; ++i) ++ { ++ if (options[i]->op_code == grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_DNS_SERVERS)) ++ { ++ grub_efi_net_ip_address_t dns; ++ grub_memcpy (dns.ip6, options[i]->data, sizeof(net_ip.ip6)); ++ efi_net_interface_set_dns (inf, &dns); ++ break; ++ } ++ } ++ ++ if (options) ++ grub_free (options); ++ } ++ ++ b->free_pool (mode.client_id); ++ b->free_pool (mode.ia); ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_command_func_t grub_efi_net_bootp = grub_cmd_efi_bootp; ++grub_command_func_t grub_efi_net_bootp6 = grub_cmd_efi_bootp6; +--- /dev/null ++++ b/grub-core/net/efi/http.c +@@ -0,0 +1,424 @@ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static void ++http_configure (struct grub_efi_net_device *dev, int prefer_ip6) ++{ ++ grub_efi_http_config_data_t http_config; ++ grub_efi_httpv4_access_point_t httpv4_node; ++ grub_efi_httpv6_access_point_t httpv6_node; ++ grub_efi_status_t status; ++ ++ grub_efi_http_t *http = dev->http; ++ ++ grub_memset (&http_config, 0, sizeof(http_config)); ++ http_config.http_version = GRUB_EFI_HTTPVERSION11; ++ http_config.timeout_millisec = 5000; ++ ++ if (prefer_ip6) ++ { ++ grub_efi_uintn_t sz; ++ grub_efi_ip6_config_manual_address_t manual_address; ++ ++ http_config.local_address_is_ipv6 = 1; ++ sz = sizeof (manual_address); ++ status = dev->ip6_config->get_data (dev->ip6_config, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, ++ &sz, &manual_address); ++ ++ if (status == GRUB_EFI_NOT_FOUND) ++ { ++ grub_printf ("The MANUAL ADDRESS is not found\n"); ++ } ++ ++ /* FIXME: The manual interface would return BUFFER TOO SMALL !!! */ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_printf ("??? %d\n",(int) status); ++ return; ++ } ++ ++ grub_memcpy (httpv6_node.local_address, manual_address.address, sizeof (httpv6_node.local_address)); ++ httpv6_node.local_port = 0; ++ http_config.access_point.ipv6_node = &httpv6_node; ++ } ++ else ++ { ++ http_config.local_address_is_ipv6 = 0; ++ grub_memset (&httpv4_node, 0, sizeof(httpv4_node)); ++ httpv4_node.use_default_address = 1; ++ ++ /* Use random port here */ ++ /* See TcpBind() in edk2/NetworkPkg/TcpDxe/TcpDispatcher.c */ ++ httpv4_node.local_port = 0; ++ http_config.access_point.ipv4_node = &httpv4_node; ++ } ++ ++ status = http->configure (http, &http_config); ++ ++ if (status == GRUB_EFI_ALREADY_STARTED) ++ { ++ /* XXX: This hangs HTTPS boot */ ++#if 0 ++ if (http->configure (http, NULL) != GRUB_EFI_SUCCESS) ++ { ++ grub_error (GRUB_ERR_IO, N_("couldn't reset http instance")); ++ grub_print_error (); ++ return; ++ } ++ status = http->configure (http, &http_config); ++#endif ++ return; ++ } ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_error (GRUB_ERR_IO, N_("couldn't configure http protocol, reason: %d"), (int)status); ++ grub_print_error (); ++ return ; ++ } ++} ++ ++static grub_efi_boolean_t request_callback_done; ++static grub_efi_boolean_t response_callback_done; ++ ++static void __grub_efi_api ++grub_efi_http_request_callback (grub_efi_event_t event __attribute__ ((unused)), ++ void *context __attribute__ ((unused))) ++{ ++ request_callback_done = 1; ++} ++ ++static void __grub_efi_api ++grub_efi_http_response_callback (grub_efi_event_t event __attribute__ ((unused)), ++ void *context __attribute__ ((unused))) ++{ ++ response_callback_done = 1; ++} ++ ++static grub_err_t ++efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https, int headeronly, grub_off_t *file_size) ++{ ++ grub_efi_http_request_data_t request_data; ++ grub_efi_http_message_t request_message; ++ grub_efi_http_token_t request_token; ++ grub_efi_http_response_data_t response_data; ++ grub_efi_http_message_t response_message; ++ grub_efi_http_token_t response_token; ++ grub_efi_http_header_t request_headers[3]; ++ ++ grub_efi_status_t status; ++ grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; ++ char *url = NULL; ++ ++ request_headers[0].field_name = (grub_efi_char8_t *)"Host"; ++ request_headers[0].field_value = (grub_efi_char8_t *)server; ++ request_headers[1].field_name = (grub_efi_char8_t *)"Accept"; ++ request_headers[1].field_value = (grub_efi_char8_t *)"*/*"; ++ request_headers[2].field_name = (grub_efi_char8_t *)"User-Agent"; ++ request_headers[2].field_value = (grub_efi_char8_t *)"UefiHttpBoot/1.0"; ++ ++ { ++ grub_efi_ipv6_address_t address; ++ const char *rest; ++ grub_efi_char16_t *ucs2_url; ++ grub_size_t url_len, ucs2_url_len; ++ const char *protocol = (use_https == 1) ? "https" : "http"; ++ grub_size_t sz; ++ ++ if (grub_efi_string_to_ip6_address (server, &address, &rest) && *rest == 0) ++ url = grub_xasprintf ("%s://[%s]%s", protocol, server, name); ++ else ++ url = grub_xasprintf ("%s://%s%s", protocol, server, name); ++ ++ if (!url) ++ { ++ return grub_errno; ++ } ++ ++ url_len = grub_strlen (url); ++ if (grub_mul (url_len, GRUB_MAX_UTF16_PER_UTF8, &ucs2_url_len) || ++ grub_add (ucs2_url_len, 1, &sz)) ++ return GRUB_ERR_OUT_OF_RANGE; ++ ++ ucs2_url = grub_calloc (sz, sizeof (ucs2_url[0])); ++ ++ if (!ucs2_url) ++ { ++ grub_free (url); ++ return grub_errno; ++ } ++ ++ ucs2_url_len = grub_utf8_to_utf16 (ucs2_url, ucs2_url_len, (grub_uint8_t *)url, url_len, NULL); /* convert string format from ascii to usc2 */ ++ ucs2_url[ucs2_url_len] = 0; ++ grub_free (url); ++ request_data.url = ucs2_url; ++ } ++ ++ request_data.method = (headeronly > 0) ? GRUB_EFI_HTTPMETHODHEAD : GRUB_EFI_HTTPMETHODGET; ++ ++ request_message.data.request = &request_data; ++ request_message.header_count = 3; ++ request_message.headers = request_headers; ++ request_message.body_length = 0; ++ request_message.body = NULL; ++ ++ /* request token */ ++ request_token.event = NULL; ++ request_token.status = GRUB_EFI_NOT_READY; ++ request_token.message = &request_message; ++ ++ request_callback_done = 0; ++ status = b->create_event ( ++ GRUB_EFI_EVT_NOTIFY_SIGNAL, ++ GRUB_EFI_TPL_CALLBACK, ++ grub_efi_http_request_callback, ++ NULL, ++ &request_token.event); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_free (request_data.url); ++ return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%" PRIxGRUB_SIZE, status); ++ } ++ ++ status = http->request (http, &request_token); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ b->close_event (request_token.event); ++ grub_free (request_data.url); ++ return grub_error (GRUB_ERR_IO, "Fail to send a request! status=0x%" PRIxGRUB_SIZE, status); ++ } ++ /* TODO: Add Timeout */ ++ while (!request_callback_done) ++ http->poll (http); ++ ++ response_data.status_code = GRUB_EFI_HTTP_STATUS_UNSUPPORTED_STATUS; ++ response_message.data.response = &response_data; ++ /* herader_count will be updated by the HTTP driver on response */ ++ response_message.header_count = 0; ++ /* headers will be populated by the driver on response */ ++ response_message.headers = NULL; ++ /* use zero BodyLength to only receive the response headers */ ++ response_message.body_length = 0; ++ response_message.body = NULL; ++ response_token.event = NULL; ++ ++ status = b->create_event ( ++ GRUB_EFI_EVT_NOTIFY_SIGNAL, ++ GRUB_EFI_TPL_CALLBACK, ++ grub_efi_http_response_callback, ++ NULL, ++ &response_token.event); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ b->close_event (request_token.event); ++ grub_free (request_data.url); ++ return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%" PRIxGRUB_SIZE, status); ++ } ++ ++ response_token.status = GRUB_EFI_SUCCESS; ++ response_token.message = &response_message; ++ ++ /* wait for HTTP response */ ++ response_callback_done = 0; ++ status = http->response (http, &response_token); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ b->close_event (response_token.event); ++ b->close_event (request_token.event); ++ grub_free (request_data.url); ++ return grub_error (GRUB_ERR_IO, "Fail to receive a response! status=%d\n", (int)status); ++ } ++ ++ /* TODO: Add Timeout */ ++ while (!response_callback_done) ++ http->poll (http); ++ ++ if (response_message.data.response->status_code != GRUB_EFI_HTTP_STATUS_200_OK) ++ { ++ grub_efi_http_status_code_t status_code = response_message.data.response->status_code; ++ ++ if (response_message.headers) ++ b->free_pool (response_message.headers); ++ b->close_event (response_token.event); ++ b->close_event (request_token.event); ++ grub_free (request_data.url); ++ if (status_code == GRUB_EFI_HTTP_STATUS_404_NOT_FOUND) ++ { ++ return grub_error (GRUB_ERR_FILE_NOT_FOUND, _("file `%s' not found"), name); ++ } ++ else ++ { ++ return grub_error (GRUB_ERR_NET_UNKNOWN_ERROR, ++ _("unsupported uefi http status code 0x%x"), status_code); ++ } ++ } ++ ++ if (file_size) ++ { ++ int i; ++ /* parse the length of the file from the ContentLength header */ ++ for (*file_size = 0, i = 0; i < (int)response_message.header_count; ++i) ++ { ++ if (!grub_strcmp((const char*)response_message.headers[i].field_name, "Content-Length")) ++ { ++ *file_size = grub_strtoul((const char*)response_message.headers[i].field_value, 0, 10); ++ break; ++ } ++ } ++ } ++ ++ if (response_message.headers) ++ b->free_pool (response_message.headers); ++ b->close_event (response_token.event); ++ b->close_event (request_token.event); ++ grub_free (request_data.url); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_ssize_t ++efihttp_read (struct grub_efi_net_device *dev, ++ char *buf, ++ grub_size_t len) ++{ ++ grub_efi_http_message_t response_message; ++ grub_efi_http_token_t response_token; ++ ++ grub_efi_status_t status; ++ grub_size_t sum = 0; ++ grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; ++ grub_efi_http_t *http = dev->http; ++ ++ if (!len) ++ { ++ grub_error (GRUB_ERR_BUG, "Invalid arguments to EFI HTTP Read"); ++ return -1; ++ } ++ ++ b->create_event ( ++ GRUB_EFI_EVT_NOTIFY_SIGNAL, ++ GRUB_EFI_TPL_CALLBACK, ++ grub_efi_http_response_callback, ++ NULL, ++ &response_token.event); ++ ++ while (len) ++ { ++ response_message.data.response = NULL; ++ response_message.header_count = 0; ++ response_message.headers = NULL; ++ response_message.body_length = len; ++ response_message.body = buf; ++ ++ response_token.message = &response_message; ++ response_token.status = GRUB_EFI_NOT_READY; ++ ++ response_callback_done = 0; ++ ++ status = http->response (http, &response_token); ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ b->close_event (response_token.event); ++ grub_error (GRUB_ERR_IO, "Error! status=%d\n", (int)status); ++ return -1; ++ } ++ ++ while (!response_callback_done) ++ http->poll (http); ++ ++ sum += response_message.body_length; ++ buf += response_message.body_length; ++ len -= response_message.body_length; ++ } ++ ++ b->close_event (response_token.event); ++ ++ return sum; ++} ++ ++static grub_err_t ++grub_efihttp_open (struct grub_efi_net_device *dev, ++ int prefer_ip6 __attribute__ ((unused)), ++ grub_file_t file, ++ const char *filename __attribute__ ((unused)), ++ int type) ++{ ++ grub_err_t err; ++ grub_off_t size; ++ char *buf; ++ ++ err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 1, 0); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ ++ err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 0, &size); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ ++ buf = grub_malloc (size); ++ efihttp_read (dev, buf, size); ++ ++ file->size = size; ++ file->data = buf; ++ file->not_easily_seekable = 0; ++ file->device->net->offset = 0; ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_efihttp_close (struct grub_efi_net_device *dev __attribute__ ((unused)), ++ int prefer_ip6 __attribute__ ((unused)), ++ grub_file_t file) ++{ ++ if (file->data) ++ grub_free (file->data); ++ ++ file->data = 0; ++ file->offset = 0; ++ file->size = 0; ++ file->device->net->offset = 0; ++ return GRUB_ERR_NONE; ++} ++ ++static grub_ssize_t ++grub_efihttp_read (struct grub_efi_net_device *dev __attribute__((unused)), ++ int prefer_ip6 __attribute__((unused)), ++ grub_file_t file, ++ char *buf, ++ grub_size_t len) ++{ ++ grub_size_t r = len; ++ ++ if (!file->data || !buf || !len) ++ return 0; ++ ++ if ((file->device->net->offset + len) > file->size) ++ r = file->size - file->device->net->offset; ++ ++ if (r) ++ { ++ grub_memcpy (buf, (char *)file->data + file->device->net->offset, r); ++ file->device->net->offset += r; ++ } ++ ++ return r; ++} ++ ++struct grub_efi_net_io io_http = ++ { ++ .configure = http_configure, ++ .open = grub_efihttp_open, ++ .read = grub_efihttp_read, ++ .close = grub_efihttp_close ++ }; +--- /dev/null ++++ b/grub-core/net/efi/ip4_config.c +@@ -0,0 +1,409 @@ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++char * ++grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address) ++{ ++ char *hw_addr, *p; ++ int s; ++ int i; ++ grub_size_t sz; ++ ++ if (grub_mul (hw_address_size, sizeof ("XX:") - 1, &sz) || ++ grub_add (sz, 1, &sz)) ++ return NULL; ++ ++ hw_addr = grub_malloc (sz); ++ if (!hw_addr) ++ return NULL; ++ ++ p = hw_addr; ++ s = sz; ++ for (i = 0; i < (int)hw_address_size; i++) ++ { ++ grub_snprintf (p, sz, "%02x:", hw_address[i]); ++ p += sizeof ("XX:") - 1; ++ s -= sizeof ("XX:") - 1; ++ } ++ ++ hw_addr[sz - 2] = '\0'; ++ return hw_addr; ++} ++ ++char * ++grub_efi_ip4_address_to_string (grub_efi_ipv4_address_t *address) ++{ ++ char *addr; ++ ++ addr = grub_malloc (sizeof ("XXX.XXX.XXX.XXX")); ++ if (!addr) ++ return NULL; ++ ++ /* FIXME: Use grub_xasprintf ? */ ++ grub_snprintf (addr, ++ sizeof ("XXX.XXX.XXX.XXX"), ++ "%u.%u.%u.%u", ++ (*address)[0], ++ (*address)[1], ++ (*address)[2], ++ (*address)[3]); ++ ++ return addr; ++} ++ ++int ++grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest) ++{ ++ grub_uint32_t newip = 0; ++ int i; ++ const char *ptr = val; ++ ++ for (i = 0; i < 4; i++) ++ { ++ unsigned long t; ++ t = grub_strtoul (ptr, &ptr, 0); ++ if (grub_errno) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ return 0; ++ } ++ if (*ptr != '.' && i == 0) ++ { ++ /* XXX: t is in host byte order */ ++ newip = t; ++ break; ++ } ++ if (t & ~0xff) ++ return 0; ++ newip <<= 8; ++ newip |= t; ++ if (i != 3 && *ptr != '.') ++ return 0; ++ ptr++; ++ } ++ ++ newip = grub_cpu_to_be32 (newip); ++ ++ grub_memcpy (address, &newip, sizeof(*address)); ++ ++ if (rest) ++ *rest = (ptr - 1); ++ return 1; ++} ++ ++static grub_efi_ip4_config2_interface_info_t * ++efi_ip4_config_interface_info (grub_efi_ip4_config2_protocol_t *ip4_config) ++{ ++ grub_efi_uintn_t sz; ++ grub_efi_status_t status; ++ grub_efi_ip4_config2_interface_info_t *interface_info; ++ ++ sz = sizeof (*interface_info) + sizeof (*interface_info->route_table); ++ interface_info = grub_malloc (sz); ++ if (!interface_info) ++ return NULL; ++ ++ status = ip4_config->get_data (ip4_config, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, ++ &sz, interface_info); ++ ++ if (status == GRUB_EFI_BUFFER_TOO_SMALL) ++ { ++ grub_free (interface_info); ++ interface_info = grub_malloc (sz); ++ status = ip4_config->get_data (ip4_config, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, ++ &sz, interface_info); ++ } ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_free (interface_info); ++ return NULL; ++ } ++ ++ return interface_info; ++} ++ ++static grub_efi_ip4_config2_manual_address_t * ++efi_ip4_config_manual_address (grub_efi_ip4_config2_protocol_t *ip4_config) ++{ ++ grub_efi_uintn_t sz; ++ grub_efi_status_t status; ++ grub_efi_ip4_config2_manual_address_t *manual_address; ++ ++ sz = sizeof (*manual_address); ++ manual_address = grub_malloc (sz); ++ if (!manual_address) ++ return NULL; ++ ++ status = ip4_config->get_data (ip4_config, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, ++ &sz, manual_address); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_free (manual_address); ++ return NULL; ++ } ++ ++ return manual_address; ++} ++ ++char * ++grub_efi_ip4_interface_name (struct grub_efi_net_device *dev) ++{ ++ grub_efi_ip4_config2_interface_info_t *interface_info; ++ char *name; ++ ++ interface_info = efi_ip4_config_interface_info (dev->ip4_config); ++ ++ if (!interface_info) ++ return NULL; ++ ++ name = grub_malloc (GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE ++ * GRUB_MAX_UTF8_PER_UTF16 + 1); ++ *grub_utf16_to_utf8 ((grub_uint8_t *)name, interface_info->name, ++ GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE) = 0; ++ grub_free (interface_info); ++ return name; ++} ++ ++static char * ++grub_efi_ip4_interface_hw_address (struct grub_efi_net_device *dev) ++{ ++ grub_efi_ip4_config2_interface_info_t *interface_info; ++ char *hw_addr; ++ ++ interface_info = efi_ip4_config_interface_info (dev->ip4_config); ++ ++ if (!interface_info) ++ return NULL; ++ ++ hw_addr = grub_efi_hw_address_to_string (interface_info->hw_address_size, interface_info->hw_address); ++ grub_free (interface_info); ++ ++ return hw_addr; ++} ++ ++static char * ++grub_efi_ip4_interface_address (struct grub_efi_net_device *dev) ++{ ++ grub_efi_ip4_config2_manual_address_t *manual_address; ++ char *addr; ++ ++ manual_address = efi_ip4_config_manual_address (dev->ip4_config); ++ ++ if (!manual_address) ++ return NULL; ++ ++ addr = grub_efi_ip4_address_to_string (&manual_address->address); ++ grub_free (manual_address); ++ return addr; ++} ++ ++ ++static int ++address_mask_size (grub_efi_ipv4_address_t *address) ++{ ++ grub_uint8_t i; ++ grub_uint32_t u32_addr = grub_be_to_cpu32 (grub_get_unaligned32 (address)); ++ ++ if (u32_addr == 0) ++ return 0; ++ ++ for (i = 0; i < 32 ; ++i) ++ { ++ if (u32_addr == ((0xffffffff >> i) << i)) ++ return (32 - i); ++ } ++ ++ return -1; ++} ++ ++static char ** ++grub_efi_ip4_interface_route_table (struct grub_efi_net_device *dev) ++{ ++ grub_efi_ip4_config2_interface_info_t *interface_info; ++ char **ret; ++ int i, id; ++ grub_size_t sz; ++ ++ interface_info = efi_ip4_config_interface_info (dev->ip4_config); ++ if (!interface_info) ++ return NULL; ++ ++ if (grub_add (interface_info->route_table_size, 1, &sz)) ++ { ++ grub_free (interface_info); ++ return NULL; ++ } ++ ++ ret = grub_calloc (sz, sizeof (*ret)); ++ ++ if (!ret) ++ { ++ grub_free (interface_info); ++ return NULL; ++ } ++ ++ id = 0; ++ for (i = 0; i < (int)interface_info->route_table_size; i++) ++ { ++ char *subnet, *gateway, *mask; ++ grub_uint32_t u32_subnet, u32_gateway; ++ int mask_size; ++ grub_efi_ip4_route_table_t *route_table = interface_info->route_table + i; ++ grub_efi_net_interface_t *inf; ++ char *interface_name = NULL; ++ ++ for (inf = dev->net_interfaces; inf; inf = inf->next) ++ if (!inf->prefer_ip6) ++ interface_name = inf->name; ++ ++ u32_gateway = grub_get_unaligned32 (&route_table->gateway_address); ++ gateway = grub_efi_ip4_address_to_string (&route_table->gateway_address); ++ u32_subnet = grub_get_unaligned32 (&route_table->subnet_address); ++ subnet = grub_efi_ip4_address_to_string (&route_table->subnet_address); ++ mask_size = address_mask_size (&route_table->subnet_mask); ++ mask = grub_efi_ip4_address_to_string (&route_table->subnet_mask); ++ if (u32_subnet && !u32_gateway && interface_name) ++ ret[id++] = grub_xasprintf ("%s:local %s/%d %s", dev->card_name, subnet, mask_size, interface_name); ++ else if (u32_subnet && u32_gateway) ++ ret[id++] = grub_xasprintf ("%s:gw %s/%d gw %s", dev->card_name, subnet, mask_size, gateway); ++ else if (!u32_subnet && u32_gateway) ++ ret[id++] = grub_xasprintf ("%s:default %s/%d gw %s", dev->card_name, subnet, mask_size, gateway); ++ grub_free (subnet); ++ grub_free (gateway); ++ grub_free (mask); ++ } ++ ++ ret[id] = NULL; ++ grub_free (interface_info); ++ return ret; ++} ++ ++static grub_efi_net_interface_t * ++grub_efi_ip4_interface_match (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *ip_address) ++{ ++ grub_efi_ip4_config2_interface_info_t *interface_info; ++ grub_efi_net_interface_t *inf; ++ int i; ++ grub_efi_ipv4_address_t *address = &ip_address->ip4; ++ ++ interface_info = efi_ip4_config_interface_info (dev->ip4_config); ++ if (!interface_info) ++ return NULL; ++ ++ for (i = 0; i < (int)interface_info->route_table_size; i++) ++ { ++ grub_efi_ip4_route_table_t *route_table = interface_info->route_table + i; ++ grub_uint32_t u32_address, u32_mask, u32_subnet; ++ ++ u32_address = grub_get_unaligned32 (address); ++ u32_subnet = grub_get_unaligned32 (route_table->subnet_address); ++ u32_mask = grub_get_unaligned32 (route_table->subnet_mask); ++ ++ /* SKIP Default GATEWAY */ ++ if (!u32_subnet && !u32_mask) ++ continue; ++ ++ if ((u32_address & u32_mask) == u32_subnet) ++ { ++ for (inf = dev->net_interfaces; inf; inf = inf->next) ++ if (!inf->prefer_ip6) ++ { ++ grub_free (interface_info); ++ return inf; ++ } ++ } ++ } ++ ++ grub_free (interface_info); ++ return NULL; ++} ++ ++static int ++grub_efi_ip4_interface_set_manual_address (struct grub_efi_net_device *dev, ++ grub_efi_net_ip_manual_address_t *net_ip, ++ int with_subnet) ++{ ++ grub_efi_status_t status; ++ grub_efi_ip4_config2_manual_address_t *address = &net_ip->ip4; ++ ++ if (!with_subnet) ++ { ++ grub_efi_ip4_config2_manual_address_t *manual_address = ++ efi_ip4_config_manual_address (dev->ip4_config); ++ ++ if (manual_address) ++ { ++ grub_memcpy (address->subnet_mask, manual_address->subnet_mask, sizeof(address->subnet_mask)); ++ grub_free (manual_address); ++ } ++ else ++ { ++ /* XXX: */ ++ address->subnet_mask[0] = 0xff; ++ address->subnet_mask[1] = 0xff; ++ address->subnet_mask[2] = 0xff; ++ address->subnet_mask[3] = 0; ++ } ++ } ++ ++ status = dev->ip4_config->set_data (dev->ip4_config, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, ++ sizeof(*address), address); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return 0; ++ ++ return 1; ++} ++ ++static int ++grub_efi_ip4_interface_set_gateway (struct grub_efi_net_device *dev, ++ grub_efi_net_ip_address_t *address) ++{ ++ grub_efi_status_t status; ++ ++ status = dev->ip4_config->set_data (dev->ip4_config, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY, ++ sizeof (address->ip4), &address->ip4); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return 0; ++ return 1; ++} ++ ++/* FIXME: Multiple DNS */ ++static int ++grub_efi_ip4_interface_set_dns (struct grub_efi_net_device *dev, ++ grub_efi_net_ip_address_t *address) ++{ ++ grub_efi_status_t status; ++ ++ status = dev->ip4_config->set_data (dev->ip4_config, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, ++ sizeof (address->ip4), &address->ip4); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return 0; ++ return 1; ++} ++ ++grub_efi_net_ip_config_t *efi_net_ip4_config = &(grub_efi_net_ip_config_t) ++ { ++ .get_hw_address = grub_efi_ip4_interface_hw_address, ++ .get_address = grub_efi_ip4_interface_address, ++ .get_route_table = grub_efi_ip4_interface_route_table, ++ .best_interface = grub_efi_ip4_interface_match, ++ .set_address = grub_efi_ip4_interface_set_manual_address, ++ .set_gateway = grub_efi_ip4_interface_set_gateway, ++ .set_dns = grub_efi_ip4_interface_set_dns ++ }; +--- /dev/null ++++ b/grub-core/net/efi/ip6_config.c +@@ -0,0 +1,430 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++char * ++grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address) ++{ ++ char *str = grub_malloc (sizeof ("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX")); ++ char *p; ++ int i; ++ int squash; ++ ++ if (!str) ++ return NULL; ++ ++ p = str; ++ squash = 0; ++ for (i = 0; i < 8; ++i) ++ { ++ grub_uint16_t addr; ++ ++ if (i == 7) ++ squash = 2; ++ ++ addr = grub_get_unaligned16 (address->addr + i * 2); ++ ++ if (grub_be_to_cpu16 (addr)) ++ { ++ char buf[sizeof ("XXXX")]; ++ if (i > 0) ++ *p++ = ':'; ++ grub_snprintf (buf, sizeof (buf), "%x", grub_be_to_cpu16 (addr)); ++ grub_strcpy (p, buf); ++ p += grub_strlen (buf); ++ ++ if (squash == 1) ++ squash = 2; ++ } ++ else ++ { ++ if (squash == 0) ++ { ++ *p++ = ':'; ++ squash = 1; ++ } ++ else if (squash == 2) ++ { ++ *p++ = ':'; ++ *p++ = '0'; ++ } ++ } ++ } ++ *p = '\0'; ++ return str; ++} ++ ++int ++grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *address, const char **rest) ++{ ++ grub_uint16_t newip[8]; ++ const char *ptr = val; ++ int word, quaddot = -1; ++ int bracketed = 0; ++ ++ if (ptr[0] == '[') { ++ bracketed = 1; ++ ptr++; ++ } ++ ++ if (ptr[0] == ':' && ptr[1] != ':') ++ return 0; ++ if (ptr[0] == ':') ++ ptr++; ++ ++ for (word = 0; word < 8; word++) ++ { ++ unsigned long t; ++ if (*ptr == ':') ++ { ++ quaddot = word; ++ word--; ++ ptr++; ++ continue; ++ } ++ t = grub_strtoul (ptr, &ptr, 16); ++ if (grub_errno) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ break; ++ } ++ if (t & ~0xffff) ++ return 0; ++ newip[word] = grub_cpu_to_be16 (t); ++ if (*ptr != ':') ++ break; ++ ptr++; ++ } ++ if (quaddot == -1 && word < 7) ++ return 0; ++ if (quaddot != -1) ++ { ++ grub_memmove (&newip[quaddot + 7 - word], &newip[quaddot], ++ (word - quaddot + 1) * sizeof (newip[0])); ++ grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0])); ++ } ++ grub_memcpy (address, newip, 16); ++ if (bracketed && *ptr == ']') { ++ ptr++; ++ } ++ if (rest) ++ *rest = ptr; ++ return 1; ++} ++ ++static grub_efi_ip6_config_interface_info_t * ++efi_ip6_config_interface_info (grub_efi_ip6_config_protocol_t *ip6_config) ++{ ++ grub_efi_uintn_t sz; ++ grub_efi_status_t status; ++ grub_efi_ip6_config_interface_info_t *interface_info; ++ ++ sz = sizeof (*interface_info) + sizeof (*interface_info->route_table); ++ interface_info = grub_malloc (sz); ++ ++ status = ip6_config->get_data (ip6_config, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO, ++ &sz, interface_info); ++ ++ if (status == GRUB_EFI_BUFFER_TOO_SMALL) ++ { ++ grub_free (interface_info); ++ interface_info = grub_malloc (sz); ++ status = ip6_config->get_data (ip6_config, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO, ++ &sz, interface_info); ++ } ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_free (interface_info); ++ return NULL; ++ } ++ ++ return interface_info; ++} ++ ++static grub_efi_ip6_config_manual_address_t * ++efi_ip6_config_manual_address (grub_efi_ip6_config_protocol_t *ip6_config) ++{ ++ grub_efi_uintn_t sz; ++ grub_efi_status_t status; ++ grub_efi_ip6_config_manual_address_t *manual_address; ++ ++ sz = sizeof (*manual_address); ++ manual_address = grub_malloc (sz); ++ if (!manual_address) ++ return NULL; ++ ++ status = ip6_config->get_data (ip6_config, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, ++ &sz, manual_address); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_free (manual_address); ++ return NULL; ++ } ++ ++ return manual_address; ++} ++ ++char * ++grub_efi_ip6_interface_name (struct grub_efi_net_device *dev) ++{ ++ grub_efi_ip6_config_interface_info_t *interface_info; ++ char *name; ++ ++ interface_info = efi_ip6_config_interface_info (dev->ip6_config); ++ ++ if (!interface_info) ++ return NULL; ++ ++ name = grub_malloc (GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE ++ * GRUB_MAX_UTF8_PER_UTF16 + 1); ++ *grub_utf16_to_utf8 ((grub_uint8_t *)name, interface_info->name, ++ GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE) = 0; ++ grub_free (interface_info); ++ return name; ++} ++ ++static char * ++grub_efi_ip6_interface_hw_address (struct grub_efi_net_device *dev) ++{ ++ grub_efi_ip6_config_interface_info_t *interface_info; ++ char *hw_addr; ++ ++ interface_info = efi_ip6_config_interface_info (dev->ip6_config); ++ ++ if (!interface_info) ++ return NULL; ++ ++ hw_addr = grub_efi_hw_address_to_string (interface_info->hw_address_size, interface_info->hw_address); ++ grub_free (interface_info); ++ ++ return hw_addr; ++} ++ ++static char * ++grub_efi_ip6_interface_address (struct grub_efi_net_device *dev) ++{ ++ grub_efi_ip6_config_manual_address_t *manual_address; ++ char *addr; ++ ++ manual_address = efi_ip6_config_manual_address (dev->ip6_config); ++ ++ if (!manual_address) ++ return NULL; ++ ++ addr = grub_efi_ip6_address_to_string ((grub_efi_pxe_ipv6_address_t *)&manual_address->address); ++ grub_free (manual_address); ++ return addr; ++} ++ ++static char ** ++grub_efi_ip6_interface_route_table (struct grub_efi_net_device *dev) ++{ ++ grub_efi_ip6_config_interface_info_t *interface_info; ++ char **ret; ++ int i, id; ++ grub_size_t sz; ++ ++ interface_info = efi_ip6_config_interface_info (dev->ip6_config); ++ if (!interface_info) ++ return NULL; ++ ++ if (grub_add (interface_info->route_count, 1, &sz)) ++ { ++ grub_free (interface_info); ++ return NULL; ++ } ++ ++ ret = grub_calloc (sz, sizeof (*ret)); ++ ++ if (!ret) ++ { ++ grub_free (interface_info); ++ return NULL; ++ } ++ ++ id = 0; ++ for (i = 0; i < (int)interface_info->route_count ; i++) ++ { ++ char *gateway, *destination; ++ grub_uint64_t u64_gateway[2]; ++ grub_uint64_t u64_destination[2]; ++ grub_efi_ip6_route_table_t *route_table = interface_info->route_table + i; ++ grub_efi_net_interface_t *inf; ++ char *interface_name = NULL; ++ ++ gateway = grub_efi_ip6_address_to_string (&route_table->gateway); ++ destination = grub_efi_ip6_address_to_string (&route_table->destination); ++ ++ u64_gateway[0] = grub_get_unaligned64 (route_table->gateway.addr); ++ u64_gateway[1] = grub_get_unaligned64 (route_table->gateway.addr + 8); ++ u64_destination[0] = grub_get_unaligned64 (route_table->destination.addr); ++ u64_destination[1] = grub_get_unaligned64 (route_table->destination.addr + 8); ++ ++ for (inf = dev->net_interfaces; inf; inf = inf->next) ++ if (inf->prefer_ip6) ++ interface_name = inf->name; ++ ++ if ((!u64_gateway[0] && !u64_gateway[1]) ++ && (u64_destination[0] || u64_destination[1])) ++ { ++ if (interface_name) ++ { ++ if ((grub_be_to_cpu64 (u64_destination[0]) == 0xfe80000000000000ULL) ++ && (!u64_destination[1]) ++ && (route_table->prefix_length == 64)) ++ ret[id++] = grub_xasprintf ("%s:link %s/%d %s", dev->card_name, destination, route_table->prefix_length, interface_name); ++ else ++ ret[id++] = grub_xasprintf ("%s:local %s/%d %s", dev->card_name, destination, route_table->prefix_length, interface_name); ++ } ++ } ++ else if ((u64_gateway[0] || u64_gateway[1]) ++ && (u64_destination[0] || u64_destination[1])) ++ ret[id++] = grub_xasprintf ("%s:gw %s/%d gw %s", dev->card_name, destination, route_table->prefix_length, gateway); ++ else if ((u64_gateway[0] || u64_gateway[1]) ++ && (!u64_destination[0] && !u64_destination[1])) ++ ret[id++] = grub_xasprintf ("%s:default %s/%d gw %s", dev->card_name, destination, route_table->prefix_length, gateway); ++ ++ grub_free (gateway); ++ grub_free (destination); ++ } ++ ++ ret[id] = NULL; ++ grub_free (interface_info); ++ return ret; ++} ++ ++static grub_efi_net_interface_t * ++grub_efi_ip6_interface_match (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *ip_address) ++{ ++ grub_efi_ip6_config_interface_info_t *interface_info; ++ grub_efi_net_interface_t *inf; ++ int i; ++ grub_efi_ipv6_address_t *address = &ip_address->ip6; ++ ++ interface_info = efi_ip6_config_interface_info (dev->ip6_config); ++ if (!interface_info) ++ return NULL; ++ ++ for (i = 0; i < (int)interface_info->route_count ; i++) ++ { ++ grub_uint64_t u64_addr[2]; ++ grub_uint64_t u64_subnet[2]; ++ grub_uint64_t u64_mask[2]; ++ ++ grub_efi_ip6_route_table_t *route_table = interface_info->route_table + i; ++ ++ /* SKIP Default GATEWAY */ ++ if (route_table->prefix_length == 0) ++ continue; ++ ++ u64_addr[0] = grub_get_unaligned64 (address); ++ u64_addr[1] = grub_get_unaligned64 (address + 4); ++ u64_subnet[0] = grub_get_unaligned64 (route_table->destination.addr); ++ u64_subnet[1] = grub_get_unaligned64 (route_table->destination.addr + 8); ++ u64_mask[0] = (route_table->prefix_length <= 64) ? ++ 0xffffffffffffffffULL << (64 - route_table->prefix_length) : ++ 0xffffffffffffffffULL; ++ u64_mask[1] = (route_table->prefix_length <= 64) ? ++ 0 : ++ 0xffffffffffffffffULL << (128 - route_table->prefix_length); ++ ++ if (((u64_addr[0] & u64_mask[0]) == u64_subnet[0]) ++ && ((u64_addr[1] & u64_mask[1]) == u64_subnet[1])) ++ { ++ for (inf = dev->net_interfaces; inf; inf = inf->next) ++ if (inf->prefer_ip6) ++ { ++ grub_free (interface_info); ++ return inf; ++ } ++ } ++ } ++ ++ grub_free (interface_info); ++ return NULL; ++} ++ ++static int ++grub_efi_ip6_interface_set_manual_address (struct grub_efi_net_device *dev, ++ grub_efi_net_ip_manual_address_t *net_ip, ++ int with_subnet) ++{ ++ grub_efi_status_t status; ++ grub_efi_ip6_config_manual_address_t *address = &net_ip->ip6; ++ ++ if (!with_subnet) ++ { ++ grub_efi_ip6_config_manual_address_t *manual_address = ++ efi_ip6_config_manual_address (dev->ip6_config); ++ ++ if (manual_address) ++ { ++ address->prefix_length = manual_address->prefix_length; ++ grub_free (manual_address); ++ } ++ else ++ { ++ /* XXX: */ ++ address->prefix_length = 64; ++ } ++ } ++ ++ status = dev->ip6_config->set_data (dev->ip6_config, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, ++ sizeof(*address), address); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return 0; ++ ++ return 1; ++} ++ ++static int ++grub_efi_ip6_interface_set_gateway (struct grub_efi_net_device *dev, ++ grub_efi_net_ip_address_t *address) ++{ ++ grub_efi_status_t status; ++ ++ status = dev->ip6_config->set_data (dev->ip6_config, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_GATEWAY, ++ sizeof (address->ip6), &address->ip6); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return 0; ++ return 1; ++} ++ ++static int ++grub_efi_ip6_interface_set_dns (struct grub_efi_net_device *dev, ++ grub_efi_net_ip_address_t *address) ++{ ++ ++ grub_efi_status_t status; ++ ++ status = dev->ip6_config->set_data (dev->ip6_config, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, ++ sizeof (address->ip6), &address->ip6); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return 0; ++ return 1; ++} ++ ++grub_efi_net_ip_config_t *efi_net_ip6_config = &(grub_efi_net_ip_config_t) ++ { ++ .get_hw_address = grub_efi_ip6_interface_hw_address, ++ .get_address = grub_efi_ip6_interface_address, ++ .get_route_table = grub_efi_ip6_interface_route_table, ++ .best_interface = grub_efi_ip6_interface_match, ++ .set_address = grub_efi_ip6_interface_set_manual_address, ++ .set_gateway = grub_efi_ip6_interface_set_gateway, ++ .set_dns = grub_efi_ip6_interface_set_dns ++ }; +--- /dev/null ++++ b/grub-core/net/efi/net.c +@@ -0,0 +1,1440 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++#define GRUB_EFI_IP6_PREFIX_LENGTH 64 ++ ++static grub_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID; ++static grub_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID; ++static grub_guid_t http_service_binding_guid = GRUB_EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; ++static grub_guid_t http_guid = GRUB_EFI_HTTP_PROTOCOL_GUID; ++static grub_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID; ++static grub_guid_t dhcp4_service_binding_guid = GRUB_EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID; ++static grub_guid_t dhcp4_guid = GRUB_EFI_DHCP4_PROTOCOL_GUID; ++static grub_guid_t dhcp6_service_binding_guid = GRUB_EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID; ++static grub_guid_t dhcp6_guid = GRUB_EFI_DHCP6_PROTOCOL_GUID; ++ ++struct grub_efi_net_device *net_devices; ++ ++static char *default_server; ++static grub_efi_net_interface_t *net_interface; ++static grub_efi_net_interface_t *net_default_interface; ++ ++#define efi_net_interface_configure(inf) inf->io->configure (inf->dev, inf->prefer_ip6) ++#define efi_net_interface_open(inf, file, name) inf->io->open (inf->dev, inf->prefer_ip6, file, name, inf->io_type) ++#define efi_net_interface_read(inf, file, buf, sz) inf->io->read (inf->dev, inf->prefer_ip6, file, buf, sz) ++#define efi_net_interface_close(inf, file) inf->io->close (inf->dev, inf->prefer_ip6, file) ++#define efi_net_interface(m,...) efi_net_interface_ ## m (net_interface, ## __VA_ARGS__) ++ ++static grub_efi_handle_t ++grub_efi_locate_device_path (grub_guid_t *protocol, grub_efi_device_path_t *device_path, ++ grub_efi_device_path_t **r_device_path) ++{ ++ grub_efi_handle_t handle; ++ grub_efi_status_t status; ++ ++ status = grub_efi_system_table->boot_services->locate_device_path ( ++ protocol, &device_path, &handle); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return 0; ++ ++ if (r_device_path) ++ *r_device_path = device_path; ++ ++ return handle; ++} ++ ++static int ++url_parse_fields (const char *url, char **proto, char **host, char **path) ++{ ++ const char *p, *ps; ++ grub_size_t l; ++ ++ *proto = *host = *path = NULL; ++ ps = p = url; ++ ++ while ((p = grub_strchr (p, ':'))) ++ { ++ if (grub_strlen (p) < sizeof ("://") - 1) ++ break; ++ if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0) ++ { ++ l = p - ps; ++ *proto = grub_malloc (l + 1); ++ if (!*proto) ++ { ++ grub_print_error (); ++ return 0; ++ } ++ ++ grub_memcpy (*proto, ps, l); ++ (*proto)[l] = '\0'; ++ p += sizeof ("://") - 1; ++ break; ++ } ++ ++p; ++ } ++ ++ if (!*proto) ++ { ++ grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url); ++ return 0; ++ } ++ ++ ps = p; ++ p = grub_strchr (p, '/'); ++ ++ if (!p) ++ { ++ grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url); ++ grub_free (*proto); ++ *proto = NULL; ++ return 0; ++ } ++ ++ l = p - ps; ++ ++ if (l > 2 && ps[0] == '[' && ps[l - 1] == ']') ++ { ++ *host = grub_malloc (l - 1); ++ if (!*host) ++ { ++ grub_print_error (); ++ grub_free (*proto); ++ *proto = NULL; ++ return 0; ++ } ++ grub_memcpy (*host, ps + 1, l - 2); ++ (*host)[l - 2] = 0; ++ } ++ else ++ { ++ *host = grub_malloc (l + 1); ++ if (!*host) ++ { ++ grub_print_error (); ++ grub_free (*proto); ++ *proto = NULL; ++ return 0; ++ } ++ grub_memcpy (*host, ps, l); ++ (*host)[l] = 0; ++ } ++ ++ *path = grub_strdup (p); ++ if (!*path) ++ { ++ grub_print_error (); ++ grub_free (*host); ++ grub_free (*proto); ++ *host = NULL; ++ *proto = NULL; ++ return 0; ++ } ++ return 1; ++} ++ ++static void ++url_get_boot_location (const char *url, char **device, char **path, int is_default) ++{ ++ char *protocol, *server, *file; ++ char *slash; ++ ++ if (!url_parse_fields (url, &protocol, &server, &file)) ++ return; ++ ++ if ((slash = grub_strrchr (file, '/'))) ++ *slash = 0; ++ else ++ *file = 0; ++ ++ *device = grub_xasprintf ("%s,%s", protocol, server); ++ *path = grub_strdup(file); ++ ++ if (is_default) ++ default_server = server; ++ else ++ grub_free (server); ++ ++ grub_free (protocol); ++ grub_free (file); ++} ++ ++static void ++pxe_get_boot_location (const struct grub_net_bootp_packet *bp, ++ char **device, ++ char **path, ++ int is_default) ++{ ++ char *server = grub_xasprintf ("%d.%d.%d.%d", ++ ((grub_uint8_t *) &bp->server_ip)[0], ++ ((grub_uint8_t *) &bp->server_ip)[1], ++ ((grub_uint8_t *) &bp->server_ip)[2], ++ ((grub_uint8_t *) &bp->server_ip)[3]); ++ ++ *device = grub_xasprintf ("tftp,%s", server); ++ ++ *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file)); ++ ++ if (*path) ++ { ++ char *slash; ++ slash = grub_strrchr (*path, '/'); ++ if (slash) ++ *slash = 0; ++ else ++ **path = 0; ++ } ++ ++ if (is_default) ++ default_server = server; ++ else ++ grub_free (server); ++} ++ ++static void ++pxe_get_boot_location_v6 (const struct grub_net_dhcp6_packet *dp, ++ grub_size_t dhcp_size, ++ char **device, ++ char **path) ++{ ++ ++ struct grub_net_dhcp6_option *dhcp_opt; ++ grub_size_t dhcp_remain_size; ++ *device = *path = 0; ++ ++ if (dhcp_size < sizeof (*dp)) ++ { ++ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small")); ++ return; ++ } ++ ++ dhcp_remain_size = dhcp_size - sizeof (*dp); ++ dhcp_opt = (struct grub_net_dhcp6_option *)dp->dhcp_options; ++ ++ while (dhcp_remain_size) ++ { ++ grub_uint16_t code = grub_be_to_cpu16 (dhcp_opt->code); ++ grub_uint16_t len = grub_be_to_cpu16 (dhcp_opt->len); ++ grub_uint16_t option_size = sizeof (*dhcp_opt) + len; ++ ++ if (dhcp_remain_size < option_size || code == 0) ++ break; ++ ++ if (code == GRUB_NET_DHCP6_OPTION_BOOTFILE_URL) ++ { ++ char *url; ++ grub_size_t sz; ++ ++ if (grub_add (len, 1, &sz)) ++ return; ++ ++ url = grub_malloc (sz); ++ if (!url) ++ return; ++ ++ grub_memcpy (url, dhcp_opt->data, len); ++ url[len] = 0; ++ ++ url_get_boot_location ((const char *)url, device, path, 1); ++ grub_free (url); ++ break; ++ } ++ ++ dhcp_remain_size -= option_size; ++ dhcp_opt = (struct grub_net_dhcp6_option *)((grub_uint8_t *)dhcp_opt + option_size); ++ } ++} ++ ++static grub_efi_net_interface_t * ++grub_efi_net_config_from_device_path (grub_efi_device_path_t *dp, ++ struct grub_efi_net_device *netdev, ++ char **device, ++ char **path) ++{ ++ grub_efi_net_interface_t *inf = NULL; ++ ++ while (1) ++ { ++ grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); ++ grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); ++ grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); ++ ++ if (type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE) ++ { ++ if (subtype == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) ++ { ++ grub_efi_uri_device_path_t *uri_dp; ++ uri_dp = (grub_efi_uri_device_path_t *) dp; ++ /* Beware that uri_dp->uri may not be null terminated */ ++ url_get_boot_location ((const char *)uri_dp->uri, device, path, 1); ++ } ++ else if (subtype == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE) ++ { ++ grub_efi_net_ip_manual_address_t net_ip; ++ grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) dp; ++ ++ if (inf) ++ continue; ++ grub_memcpy (net_ip.ip4.address, ipv4->local_ip_address, sizeof (net_ip.ip4.address)); ++ grub_memcpy (net_ip.ip4.subnet_mask, ipv4->subnet_mask, sizeof (net_ip.ip4.subnet_mask)); ++ net_ip.is_ip6 = 0; ++ inf = grub_efi_net_create_interface (netdev, ++ netdev->card_name, ++ &net_ip, ++ 1); ++ } ++ else if (subtype == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE) ++ { ++ grub_efi_net_ip_manual_address_t net_ip; ++ grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) dp; ++ ++ if (inf) ++ continue; ++ grub_memcpy (net_ip.ip6.address, ipv6->local_ip_address, sizeof (net_ip.ip6.address)); ++ net_ip.ip6.prefix_length = GRUB_EFI_IP6_PREFIX_LENGTH; ++ net_ip.ip6.is_anycast = 0; ++ net_ip.is_ip6 = 1; ++ inf = grub_efi_net_create_interface (netdev, ++ netdev->card_name, ++ &net_ip, ++ 1); ++ } ++ } ++ ++ if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) ++ break; ++ dp = (grub_efi_device_path_t *) ((char *) dp + len); ++ } ++ ++ return inf; ++} ++ ++static grub_efi_net_interface_t * ++grub_efi_net_config_from_handle (grub_efi_handle_t *hnd, ++ struct grub_efi_net_device *netdev, ++ char **device, ++ char **path) ++{ ++ grub_efi_pxe_t *pxe = NULL; ++ ++ if (hnd == netdev->ip4_pxe_handle) ++ pxe = netdev->ip4_pxe; ++ else if (hnd == netdev->ip6_pxe_handle) ++ pxe = netdev->ip6_pxe; ++ ++ if (!pxe) ++ return (grub_efi_net_config_from_device_path ( ++ grub_efi_get_device_path (hnd), ++ netdev, ++ device, ++ path)); ++ ++ if (pxe->mode->using_ipv6) ++ { ++ grub_efi_net_ip_manual_address_t net_ip; ++ ++ pxe_get_boot_location_v6 ( ++ (const struct grub_net_dhcp6_packet *) &pxe->mode->dhcp_ack, ++ sizeof (pxe->mode->dhcp_ack), ++ device, ++ path); ++ ++ grub_memcpy (net_ip.ip6.address, pxe->mode->station_ip.v6.addr, sizeof(net_ip.ip6.address)); ++ net_ip.ip6.prefix_length = GRUB_EFI_IP6_PREFIX_LENGTH; ++ net_ip.ip6.is_anycast = 0; ++ net_ip.is_ip6 = 1; ++ return (grub_efi_net_create_interface (netdev, ++ netdev->card_name, ++ &net_ip, ++ 1)); ++ } ++ else ++ { ++ grub_efi_net_ip_manual_address_t net_ip; ++ ++ pxe_get_boot_location ( ++ (const struct grub_net_bootp_packet *) &pxe->mode->dhcp_ack, ++ device, ++ path, ++ 1); ++ ++ grub_memcpy (net_ip.ip4.address, pxe->mode->station_ip.v4.addr, sizeof (net_ip.ip4.address)); ++ grub_memcpy (net_ip.ip4.subnet_mask, pxe->mode->subnet_mask.v4.addr, sizeof (net_ip.ip4.subnet_mask)); ++ net_ip.is_ip6 = 0; ++ return (grub_efi_net_create_interface (netdev, ++ netdev->card_name, ++ &net_ip, ++ 1)); ++ } ++} ++ ++static const char * ++grub_efi_net_var_get_address (struct grub_env_var *var, ++ const char *val __attribute__ ((unused))) ++{ ++ struct grub_efi_net_device *dev; ++ ++ for (dev = net_devices; dev; dev = dev->next) ++ { ++ grub_efi_net_interface_t *inf; ++ ++ for (inf = dev->net_interfaces; inf; inf = inf->next) ++ { ++ char *var_name; ++ ++ var_name = grub_xasprintf ("net_%s_ip", inf->name); ++ if (grub_strcmp (var_name, var->name) == 0) ++ return efi_net_interface_get_address (inf); ++ grub_free (var_name); ++ var_name = grub_xasprintf ("net_%s_mac", inf->name); ++ if (grub_strcmp (var_name, var->name) == 0) ++ return efi_net_interface_get_hw_address (inf); ++ grub_free (var_name); ++ } ++ } ++ ++ return NULL; ++} ++ ++static char * ++grub_efi_net_var_set_interface (struct grub_env_var *var __attribute__ ((unused)), ++ const char *val) ++{ ++ struct grub_efi_net_device *dev; ++ grub_efi_net_interface_t *inf; ++ ++ for (dev = net_devices; dev; dev = dev->next) ++ for (inf = dev->net_interfaces; inf; inf = inf->next) ++ if (grub_strcmp (inf->name, val) == 0) ++ { ++ net_default_interface = inf; ++ return grub_strdup (val); ++ } ++ ++ return NULL; ++} ++ ++static char * ++grub_efi_net_var_set_server (struct grub_env_var *var __attribute__ ((unused)), ++ const char *val) ++{ ++ grub_free (default_server); ++ default_server = grub_strdup (val); ++ return grub_strdup (val); ++} ++ ++static const char * ++grub_efi_net_var_get_server (struct grub_env_var *var __attribute__ ((unused)), ++ const char *val __attribute__ ((unused))) ++{ ++ return default_server ? : ""; ++} ++ ++static const char * ++grub_efi_net_var_get_ip (struct grub_env_var *var __attribute__ ((unused)), ++ const char *val __attribute__ ((unused))) ++{ ++ const char *intf = grub_env_get ("net_default_interface"); ++ const char *ret = NULL; ++ if (intf) ++ { ++ char *buf = grub_xasprintf ("net_%s_ip", intf); ++ if (buf) ++ ret = grub_env_get (buf); ++ grub_free (buf); ++ } ++ return ret; ++} ++ ++static const char * ++grub_efi_net_var_get_mac (struct grub_env_var *var __attribute__ ((unused)), ++ const char *val __attribute__ ((unused))) ++{ ++ const char *intf = grub_env_get ("net_default_interface"); ++ const char *ret = NULL; ++ if (intf) ++ { ++ char *buf = grub_xasprintf ("net_%s_mac", intf); ++ if (buf) ++ ret = grub_env_get (buf); ++ grub_free (buf); ++ } ++ return ret; ++} ++ ++static void ++grub_efi_net_export_interface_vars (void) ++{ ++ struct grub_efi_net_device *dev; ++ ++ for (dev = net_devices; dev; dev = dev->next) ++ { ++ grub_efi_net_interface_t *inf; ++ ++ for (inf = dev->net_interfaces; inf; inf = inf->next) ++ { ++ char *var; ++ ++ var = grub_xasprintf ("net_%s_ip", inf->name); ++ grub_register_variable_hook (var, grub_efi_net_var_get_address, 0); ++ grub_env_export (var); ++ grub_free (var); ++ var = grub_xasprintf ("net_%s_mac", inf->name); ++ grub_register_variable_hook (var, grub_efi_net_var_get_address, 0); ++ grub_env_export (var); ++ grub_free (var); ++ } ++ } ++} ++ ++static void ++grub_efi_net_unset_interface_vars (void) ++{ ++ struct grub_efi_net_device *dev; ++ ++ for (dev = net_devices; dev; dev = dev->next) ++ { ++ grub_efi_net_interface_t *inf; ++ ++ for (inf = dev->net_interfaces; inf; inf = inf->next) ++ { ++ char *var; ++ ++ var = grub_xasprintf ("net_%s_ip", inf->name); ++ grub_register_variable_hook (var, 0, 0); ++ grub_env_unset (var); ++ grub_free (var); ++ var = grub_xasprintf ("net_%s_mac", inf->name); ++ grub_register_variable_hook (var, 0, 0); ++ grub_env_unset (var); ++ grub_free (var); ++ } ++ } ++} ++ ++grub_efi_net_interface_t * ++grub_efi_net_create_interface (struct grub_efi_net_device *dev, ++ const char *interface_name, ++ grub_efi_net_ip_manual_address_t *net_ip, ++ int has_subnet) ++{ ++ grub_efi_net_interface_t *inf; ++ ++ for (inf = dev->net_interfaces; inf; inf = inf->next) ++ { ++ if (inf->prefer_ip6 == net_ip->is_ip6) ++ break; ++ } ++ ++ if (!inf) ++ { ++ inf = grub_malloc (sizeof(*inf)); ++ inf->name = grub_strdup (interface_name); ++ inf->prefer_ip6 = net_ip->is_ip6; ++ inf->dev = dev; ++ inf->next = dev->net_interfaces; ++ inf->ip_config = (net_ip->is_ip6) ? efi_net_ip6_config : efi_net_ip4_config ; ++ dev->net_interfaces = inf; ++ } ++ else ++ { ++ grub_free (inf->name); ++ inf->name = grub_strdup (interface_name); ++ } ++ ++ if (!efi_net_interface_set_address (inf, net_ip, has_subnet)) ++ { ++ grub_error (GRUB_ERR_BUG, N_("Set Address Failed")); ++ return NULL; ++ } ++ ++ return inf; ++} ++ ++static void ++grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, ++ char **path) ++{ ++ grub_efi_handle_t config_hnd; ++ ++ struct grub_efi_net_device *netdev; ++ grub_efi_net_interface_t *inf; ++ ++ config_hnd = grub_efi_locate_device_path (&ip4_config_guid, grub_efi_get_device_path (hnd), NULL); ++ ++ if (!config_hnd) ++ return; ++ ++ for (netdev = net_devices; netdev; netdev = netdev->next) ++ if (netdev->handle == config_hnd) ++ break; ++ ++ if (!netdev) ++ return; ++ ++ if (!(inf = grub_efi_net_config_from_handle (hnd, netdev, device, path))) ++ return; ++ ++ grub_env_set ("net_default_interface", inf->name); ++ grub_efi_net_export_interface_vars (); ++} ++ ++static grub_err_t ++grub_efi_netfs_dir (grub_device_t device, const char *path __attribute__ ((unused)), ++ grub_fs_dir_hook_t hook __attribute__ ((unused)), ++ void *hook_data __attribute__ ((unused))) ++{ ++ if (!device->net) ++ return grub_error (GRUB_ERR_BUG, "invalid net device"); ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_efi_netfs_open (struct grub_file *file_out __attribute__ ((unused)), ++ const char *name __attribute__ ((unused))) ++{ ++ struct grub_file *file, *bufio; ++ ++ file = grub_malloc (sizeof (*file)); ++ if (!file) ++ return grub_errno; ++ ++ grub_memcpy (file, file_out, sizeof (struct grub_file)); ++ file->device->net->name = grub_strdup (name); ++ ++ if (!file->device->net->name) ++ { ++ grub_free (file); ++ return grub_errno; ++ } ++ ++ efi_net_interface(open, file, name); ++ grub_print_error (); ++ ++ bufio = grub_bufio_open (file, 32768); ++ if (!bufio) ++ { ++ grub_free (file->device->net->name); ++ grub_free (file); ++ return grub_errno; ++ } ++ grub_memcpy (file_out, bufio, sizeof (struct grub_file)); ++ grub_free (bufio); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_ssize_t ++grub_efihttp_chunk_read (grub_file_t file, char *buf, ++ grub_size_t len, grub_size_t chunk_size) ++{ ++ char *chunk = grub_malloc (chunk_size); ++ grub_size_t sum = 0; ++ ++ while (len) ++ { ++ grub_ssize_t rd; ++ grub_size_t sz = (len > chunk_size) ? chunk_size : len; ++ ++ rd = efi_net_interface (read, file, chunk, sz); ++ ++ if (rd <= 0) ++ return rd; ++ ++ if (buf) ++ { ++ grub_memcpy (buf, chunk, rd); ++ buf += rd; ++ } ++ sum += rd; ++ len -= rd; ++ } ++ ++ grub_free (chunk); ++ return sum; ++} ++ ++static grub_ssize_t ++grub_efi_netfs_read (grub_file_t file __attribute__ ((unused)), ++ char *buf __attribute__ ((unused)), grub_size_t len __attribute__ ((unused))) ++{ ++ if (file->offset > file->device->net->offset) ++ { ++ grub_efihttp_chunk_read (file, NULL, file->offset - file->device->net->offset, 10240); ++ } ++ else if (file->offset < file->device->net->offset) ++ { ++ efi_net_interface (close, file); ++ efi_net_interface (open, file, file->device->net->name); ++ if (file->offset) ++ grub_efihttp_chunk_read (file, NULL, file->offset, 10240); ++ } ++ ++ return efi_net_interface (read, file, buf, len); ++} ++ ++static grub_err_t ++grub_efi_netfs_close (grub_file_t file) ++{ ++ efi_net_interface (close, file); ++ return GRUB_ERR_NONE; ++} ++ ++static grub_efi_handle_t ++grub_efi_service_binding (grub_efi_handle_t dev, grub_guid_t *service_binding_guid) ++{ ++ grub_efi_service_binding_t *service; ++ grub_efi_status_t status; ++ grub_efi_handle_t child_dev = NULL; ++ ++ service = grub_efi_open_protocol (dev, service_binding_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); ++ if (!service) ++ { ++ grub_error (GRUB_ERR_IO, N_("couldn't open efi service binding protocol")); ++ return NULL; ++ } ++ ++ status = service->create_child (service, &child_dev); ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_error (GRUB_ERR_IO, N_("Failed to create child device of http service %" PRIxGRUB_SIZE), status); ++ return NULL; ++ } ++ ++ return child_dev; ++} ++ ++static grub_err_t ++grub_efi_net_parse_address (const char *address, ++ grub_efi_ip4_config2_manual_address_t *ip4, ++ grub_efi_ip6_config_manual_address_t *ip6, ++ int *is_ip6, ++ int *has_cidr) ++{ ++ const char *rest; ++ ++ if (grub_efi_string_to_ip4_address (address, &ip4->address, &rest)) ++ { ++ *is_ip6 = 0; ++ if (*rest == '/') ++ { ++ grub_uint32_t subnet_mask_size; ++ ++ subnet_mask_size = grub_strtoul (rest + 1, &rest, 0); ++ ++ if (!grub_errno && subnet_mask_size <= 32 && *rest == 0) ++ { ++ grub_uint32_t subnet_mask; ++ ++ subnet_mask = grub_cpu_to_be32 ((0xffffffffU << (32 - subnet_mask_size))); ++ grub_memcpy (ip4->subnet_mask, &subnet_mask, sizeof (ip4->subnet_mask)); ++ if (has_cidr) ++ *has_cidr = 1; ++ return GRUB_ERR_NONE; ++ } ++ } ++ else if (*rest == 0) ++ { ++ grub_uint32_t subnet_mask = 0xffffffffU; ++ grub_memcpy (ip4->subnet_mask, &subnet_mask, sizeof (ip4->subnet_mask)); ++ if (has_cidr) ++ *has_cidr = 0; ++ return GRUB_ERR_NONE; ++ } ++ } ++ else if (grub_efi_string_to_ip6_address (address, &ip6->address, &rest)) ++ { ++ *is_ip6 = 1; ++ if (*rest == '/') ++ { ++ grub_efi_uint8_t prefix_length; ++ ++ prefix_length = grub_strtoul (rest + 1, &rest, 0); ++ if (!grub_errno && prefix_length <= 128 && *rest == 0) ++ { ++ ip6->prefix_length = prefix_length; ++ ip6->is_anycast = 0; ++ if (has_cidr) ++ *has_cidr = 1; ++ return GRUB_ERR_NONE; ++ } ++ } ++ else if (*rest == 0) ++ { ++ ip6->prefix_length = 128; ++ ip6->is_anycast = 0; ++ if (has_cidr) ++ *has_cidr = 0; ++ return GRUB_ERR_NONE; ++ } ++ } ++ ++ return grub_error (GRUB_ERR_NET_BAD_ADDRESS, ++ N_("unrecognised network address `%s'"), ++ address); ++} ++ ++static grub_efi_net_interface_t * ++match_route (const char *server) ++{ ++ grub_err_t err; ++ grub_efi_ip4_config2_manual_address_t ip4; ++ grub_efi_ip6_config_manual_address_t ip6; ++ grub_efi_net_interface_t *inf; ++ int is_ip6 = 0; ++ ++ grub_error_push (); ++ err = grub_efi_net_parse_address (server, &ip4, &ip6, &is_ip6, 0); ++ ++ if (err) ++ { ++ grub_dprintf ("efinetfs", "error in matching route : %s\n", grub_errmsg); ++ grub_error_pop (); ++ return NULL; ++ } ++ grub_error_pop (); ++ ++ if (is_ip6) ++ { ++ struct grub_efi_net_device *dev; ++ grub_efi_net_ip_address_t addr; ++ ++ grub_memcpy (addr.ip6, ip6.address, sizeof(ip6.address)); ++ ++ for (dev = net_devices; dev; dev = dev->next) ++ if ((inf = efi_net_ip6_config->best_interface (dev, &addr))) ++ return inf; ++ } ++ else ++ { ++ struct grub_efi_net_device *dev; ++ grub_efi_net_ip_address_t addr; ++ ++ grub_memcpy (addr.ip4, ip4.address, sizeof(ip4.address)); ++ ++ for (dev = net_devices; dev; dev = dev->next) ++ if ((inf = efi_net_ip4_config->best_interface (dev, &addr))) ++ return inf; ++ } ++ ++ return 0; ++} ++ ++static void ++grub_efi_net_add_pxebc_to_cards (void) ++{ ++ grub_efi_uintn_t num_handles; ++ grub_efi_handle_t *handles; ++ grub_efi_handle_t *handle; ++ ++ handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &pxe_io_guid, ++ 0, &num_handles); ++ if (!handles) ++ return; ++ ++ for (handle = handles; num_handles--; handle++) ++ { ++ grub_efi_device_path_t *dp, *ddp, *ldp; ++ grub_efi_pxe_t *pxe; ++ struct grub_efi_net_device *d; ++ int is_ip6 = 0; ++ ++ dp = grub_efi_get_device_path (*handle); ++ if (!dp) ++ continue; ++ ++ ddp = grub_efi_duplicate_device_path (dp); ++ ldp = grub_efi_find_last_device_path (ddp); ++ ++ if (ldp->type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE ++ && ldp->subtype == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE) ++ { ++ ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; ++ ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; ++ ldp->length = sizeof (*ldp); ++ } ++ else if (ldp->type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE ++ && ldp->subtype == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE) ++ { ++ is_ip6 = 1; ++ ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; ++ ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; ++ ldp->length = sizeof (*ldp); ++ } ++ ++ for (d = net_devices; d; d = d->next) ++ if (grub_efi_compare_device_paths (ddp, grub_efi_get_device_path (d->handle)) == 0) ++ break; ++ ++ if (!d) ++ { ++ grub_free (ddp); ++ continue; ++ } ++ ++ pxe = grub_efi_open_protocol (*handle, &pxe_io_guid, ++ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); ++ ++ if (!pxe) ++ { ++ grub_free (ddp); ++ continue; ++ } ++ ++ if (is_ip6) ++ { ++ d->ip6_pxe_handle = *handle; ++ d->ip6_pxe = pxe; ++ } ++ else ++ { ++ d->ip4_pxe_handle = *handle; ++ d->ip4_pxe = pxe; ++ } ++ ++ grub_free (ddp); ++ } ++ ++ grub_free (handles); ++} ++ ++static void ++set_ip_policy_to_static (void) ++{ ++ struct grub_efi_net_device *dev; ++ ++ for (dev = net_devices; dev; dev = dev->next) ++ { ++ grub_efi_ip4_config2_policy_t ip4_policy = GRUB_EFI_IP4_CONFIG2_POLICY_STATIC; ++ ++ if (dev->ip4_config->set_data (dev->ip4_config, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_POLICY, ++ sizeof (ip4_policy), &ip4_policy) != GRUB_EFI_SUCCESS) ++ grub_dprintf ("efinetfs", "could not set GRUB_EFI_IP4_CONFIG2_POLICY_STATIC on dev `%s'", dev->card_name); ++ ++ if (dev->ip6_config) ++ { ++ grub_efi_ip6_config_policy_t ip6_policy = GRUB_EFI_IP6_CONFIG_POLICY_MANUAL; ++ ++ if (dev->ip6_config->set_data (dev->ip6_config, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_POLICY, ++ sizeof (ip6_policy), &ip6_policy) != GRUB_EFI_SUCCESS) ++ grub_dprintf ("efinetfs", "could not set GRUB_EFI_IP6_CONFIG_POLICY_MANUAL on dev `%s'", dev->card_name); ++ } ++ } ++} ++ ++/* FIXME: Do not fail if the card did not support any of the protocol (Eg http) */ ++static void ++grub_efi_net_find_cards (void) ++{ ++ grub_efi_uintn_t num_handles; ++ grub_efi_handle_t *handles; ++ grub_efi_handle_t *handle; ++ int id; ++ ++ handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &ip4_config_guid, ++ 0, &num_handles); ++ if (!handles) ++ return; ++ ++ for (id = 0, handle = handles; num_handles--; handle++, id++) ++ { ++ grub_efi_device_path_t *dp; ++ grub_efi_ip4_config2_protocol_t *ip4_config; ++ grub_efi_ip6_config_protocol_t *ip6_config; ++ grub_efi_handle_t http_handle; ++ grub_efi_http_t *http; ++ grub_efi_handle_t dhcp4_handle; ++ grub_efi_dhcp4_protocol_t *dhcp4; ++ grub_efi_handle_t dhcp6_handle; ++ grub_efi_dhcp6_protocol_t *dhcp6; ++ ++ struct grub_efi_net_device *d; ++ ++ dp = grub_efi_get_device_path (*handle); ++ if (!dp) ++ continue; ++ ++ ip4_config = grub_efi_open_protocol (*handle, &ip4_config_guid, ++ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); ++ if (!ip4_config) ++ continue; ++ ++ ip6_config = grub_efi_open_protocol (*handle, &ip6_config_guid, ++ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); ++ ++ http_handle = grub_efi_service_binding (*handle, &http_service_binding_guid); ++ grub_errno = GRUB_ERR_NONE; ++ http = (http_handle) ++ ? grub_efi_open_protocol (http_handle, &http_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL) ++ : NULL; ++ ++ dhcp4_handle = grub_efi_service_binding (*handle, &dhcp4_service_binding_guid); ++ grub_errno = GRUB_ERR_NONE; ++ dhcp4 = (dhcp4_handle) ++ ? grub_efi_open_protocol (dhcp4_handle, &dhcp4_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL) ++ : NULL; ++ ++ ++ dhcp6_handle = grub_efi_service_binding (*handle, &dhcp6_service_binding_guid); ++ grub_errno = GRUB_ERR_NONE; ++ dhcp6 = (dhcp6_handle) ++ ? grub_efi_open_protocol (dhcp6_handle, &dhcp6_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL) ++ : NULL; ++ ++ d = grub_malloc (sizeof (*d)); ++ if (!d) ++ { ++ grub_free (handles); ++ while (net_devices) ++ { ++ d = net_devices->next; ++ grub_free (net_devices); ++ net_devices = d; ++ } ++ return; ++ } ++ d->handle = *handle; ++ d->ip4_config = ip4_config; ++ d->ip6_config = ip6_config; ++ d->http_handle = http_handle; ++ d->http = http; ++ d->dhcp4_handle = dhcp4_handle; ++ d->dhcp4 = dhcp4; ++ d->dhcp6_handle = dhcp6_handle; ++ d->dhcp6 = dhcp6; ++ d->next = net_devices; ++ d->card_name = grub_xasprintf ("efinet%d", id); ++ d->net_interfaces = NULL; ++ net_devices = d; ++ } ++ ++ grub_efi_net_add_pxebc_to_cards (); ++ grub_free (handles); ++ set_ip_policy_to_static (); ++} ++ ++static void ++listroutes_ip4 (struct grub_efi_net_device *netdev) ++{ ++ char **routes; ++ ++ routes = NULL; ++ ++ if ((routes = efi_net_ip4_config->get_route_table (netdev))) ++ { ++ char **r; ++ ++ for (r = routes; *r; ++r) ++ grub_printf ("%s\n", *r); ++ } ++ ++ if (routes) ++ { ++ char **r; ++ ++ for (r = routes; *r; ++r) ++ grub_free (*r); ++ grub_free (routes); ++ } ++} ++ ++static void ++listroutes_ip6 (struct grub_efi_net_device *netdev) ++{ ++ char **routes; ++ ++ routes = NULL; ++ ++ if ((routes = efi_net_ip6_config->get_route_table (netdev))) ++ { ++ char **r; ++ ++ for (r = routes; *r; ++r) ++ grub_printf ("%s\n", *r); ++ } ++ ++ if (routes) ++ { ++ char **r; ++ ++ for (r = routes; *r; ++r) ++ grub_free (*r); ++ grub_free (routes); ++ } ++} ++ ++static grub_err_t ++grub_cmd_efi_listroutes (struct grub_command *cmd __attribute__ ((unused)), ++ int argc __attribute__ ((unused)), ++ char **args __attribute__ ((unused))) ++{ ++ struct grub_efi_net_device *netdev; ++ ++ for (netdev = net_devices; netdev; netdev = netdev->next) ++ { ++ listroutes_ip4 (netdev); ++ listroutes_ip6 (netdev); ++ } ++ ++ return GRUB_ERR_NONE; ++} ++static grub_err_t ++grub_cmd_efi_listcards (struct grub_command *cmd __attribute__ ((unused)), ++ int argc __attribute__ ((unused)), ++ char **args __attribute__ ((unused))) ++{ ++ struct grub_efi_net_device *dev; ++ ++ for (dev = net_devices; dev; dev = dev->next) ++ { ++ char *hw_addr; ++ ++ hw_addr = efi_net_ip4_config->get_hw_address (dev); ++ ++ if (hw_addr) ++ { ++ grub_printf ("%s %s\n", dev->card_name, hw_addr); ++ grub_free (hw_addr); ++ } ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_cmd_efi_listaddrs (struct grub_command *cmd __attribute__ ((unused)), ++ int argc __attribute__ ((unused)), ++ char **args __attribute__ ((unused))) ++{ ++ struct grub_efi_net_device *dev; ++ grub_efi_net_interface_t *inf; ++ ++ for (dev = net_devices; dev; dev = dev->next) ++ for (inf = dev->net_interfaces; inf; inf = inf->next) ++ { ++ char *hw_addr = NULL; ++ char *addr = NULL; ++ ++ if ((hw_addr = efi_net_interface_get_hw_address (inf)) ++ && (addr = efi_net_interface_get_address (inf))) ++ grub_printf ("%s %s %s\n", inf->name, hw_addr, addr); ++ ++ if (hw_addr) ++ grub_free (hw_addr); ++ if (addr) ++ grub_free (addr); ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++/* FIXME: support MAC specifying. */ ++static grub_err_t ++grub_cmd_efi_addaddr (struct grub_command *cmd __attribute__ ((unused)), ++ int argc, char **args) ++{ ++ struct grub_efi_net_device *dev; ++ grub_err_t err; ++ grub_efi_ip4_config2_manual_address_t ip4; ++ grub_efi_ip6_config_manual_address_t ip6; ++ grub_efi_net_ip_manual_address_t net_ip; ++ int is_ip6 = 0; ++ int cidr = 0; ++ ++ if (argc != 3) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("three arguments expected")); ++ ++ for (dev = net_devices; dev; dev = dev->next) ++ { ++ if (grub_strcmp (dev->card_name, args[1]) == 0) ++ break; ++ } ++ ++ if (!dev) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("card not found")); ++ ++ err = grub_efi_net_parse_address (args[2], &ip4, &ip6, &is_ip6, &cidr); ++ ++ if (err) ++ return err; ++ ++ net_ip.is_ip6 = is_ip6; ++ if (is_ip6) ++ grub_memcpy (&net_ip.ip6, &ip6, sizeof(net_ip.ip6)); ++ else ++ grub_memcpy (&net_ip.ip4, &ip4, sizeof(net_ip.ip4)); ++ ++ if (!grub_efi_net_create_interface (dev, ++ args[0], ++ &net_ip, ++ cidr)) ++ return grub_errno; ++ ++ return GRUB_ERR_NONE; ++} ++ ++static struct grub_fs grub_efi_netfs; ++ ++static grub_net_t ++grub_net_open_real (const char *name __attribute__ ((unused))) ++{ ++ grub_size_t protnamelen; ++ const char *protname, *server; ++ grub_net_t ret; ++ ++ net_interface = NULL; ++ ++ if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0) ++ { ++ protname = "tftp"; ++ protnamelen = sizeof ("tftp") - 1; ++ server = name + sizeof ("pxe:") - 1; ++ } ++ else if (grub_strcmp (name, "pxe") == 0) ++ { ++ protname = "tftp"; ++ protnamelen = sizeof ("tftp") - 1; ++ server = default_server; ++ } ++ else ++ { ++ const char *comma; ++ ++ comma = grub_strchr (name, ','); ++ if (comma) ++ { ++ protnamelen = comma - name; ++ server = comma + 1; ++ protname = name; ++ } ++ else ++ { ++ protnamelen = grub_strlen (name); ++ server = default_server; ++ protname = name; ++ } ++ } ++ ++ if (!server) ++ { ++ grub_error (GRUB_ERR_NET_BAD_ADDRESS, ++ N_("no server is specified")); ++ return NULL; ++ } ++ ++ /*FIXME: Use DNS translate name to address */ ++ net_interface = match_route (server); ++ ++ /*XXX: should we check device with default gateway ? */ ++ if (!net_interface && !(net_interface = net_default_interface)) ++ { ++ grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' no route found"), ++ name); ++ return NULL; ++ } ++ ++ if ((protnamelen == (sizeof ("https") - 1) ++ && grub_memcmp ("https", protname, protnamelen) == 0)) ++ { ++ net_interface->io = &io_http; ++ net_interface->io_type = 1; ++ } ++ else if ((protnamelen == (sizeof ("http") - 1) ++ && grub_memcmp ("http", protname, protnamelen) == 0)) ++ { ++ net_interface->io = &io_http; ++ net_interface->io_type = 0; ++ } ++ else if (protnamelen == (sizeof ("tftp") - 1) ++ && grub_memcmp ("tftp", protname, protnamelen) == 0) ++ { ++ net_interface->io = &io_pxe; ++ net_interface->io_type = 0; ++ } ++ else ++ { ++ grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"), ++ name); ++ return NULL; ++ } ++ ++ /*XXX: Should we try to avoid doing excess "reconfigure" here ??? */ ++ efi_net_interface (configure); ++ ++ ret = grub_zalloc (sizeof (*ret)); ++ if (!ret) ++ return NULL; ++ ++ ret->server = grub_strdup (server); ++ if (!ret->server) ++ { ++ grub_free (ret); ++ return NULL; ++ } ++ ++ ret->fs = &grub_efi_netfs; ++ return ret; ++} ++#if 0 ++static grub_command_t cmd_efi_lsaddr; ++static grub_command_t cmd_efi_lscards; ++static grub_command_t cmd_efi_lsroutes; ++static grub_command_t cmd_efi_addaddr; ++#endif ++ ++static struct grub_fs grub_efi_netfs = ++ { ++ .name = "efi netfs", ++ .fs_dir = grub_efi_netfs_dir, ++ .fs_open = grub_efi_netfs_open, ++ .fs_read = grub_efi_netfs_read, ++ .fs_close = grub_efi_netfs_close, ++ .fs_label = NULL, ++ .fs_uuid = NULL, ++ .fs_mtime = NULL, ++ }; ++ ++int ++grub_efi_net_boot_from_https (void) ++{ ++ grub_efi_loaded_image_t *image = NULL; ++ grub_efi_device_path_t *dp; ++ ++ image = grub_efi_get_loaded_image (grub_efi_image_handle); ++ if (!image) ++ return 0; ++ ++ dp = grub_efi_get_device_path (image->device_handle); ++ ++ while (1) ++ { ++ grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); ++ grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); ++ grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); ++ ++ if ((type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE) ++ && (subtype == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)) ++ { ++ grub_efi_uri_device_path_t *uri_dp = (grub_efi_uri_device_path_t *) dp; ++ return (grub_strncmp ((const char*)uri_dp->uri, "https://", sizeof ("https://") - 1) == 0) ? 1 : 0; ++ } ++ ++ if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) ++ break; ++ dp = (grub_efi_device_path_t *) ((char *) dp + len); ++ } ++ ++ return 0; ++} ++ ++int ++grub_efi_net_boot_from_opa (void) ++{ ++ grub_efi_loaded_image_t *image = NULL; ++ grub_efi_device_path_t *dp; ++ ++ image = grub_efi_get_loaded_image (grub_efi_image_handle); ++ if (!image) ++ return 0; ++ ++ dp = grub_efi_get_device_path (image->device_handle); ++ ++ while (1) ++ { ++ grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); ++ grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); ++ grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); ++ ++ if ((type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE) ++ && (subtype == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE)) ++ { ++ grub_efi_mac_address_device_path_t *mac_dp = (grub_efi_mac_address_device_path_t *)dp; ++ return (mac_dp->if_type == 0xC7) ? 1 : 0; ++ } ++ ++ if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) ++ break; ++ dp = (grub_efi_device_path_t *) ((char *) dp + len); ++ } ++ ++ return 0; ++} ++ ++static char * ++grub_env_write_readonly (struct grub_env_var *var __attribute__ ((unused)), ++ const char *val __attribute__ ((unused))) ++{ ++ return NULL; ++} ++ ++grub_command_func_t grub_efi_net_list_routes = grub_cmd_efi_listroutes; ++grub_command_func_t grub_efi_net_list_cards = grub_cmd_efi_listcards; ++grub_command_func_t grub_efi_net_list_addrs = grub_cmd_efi_listaddrs; ++grub_command_func_t grub_efi_net_add_addr = grub_cmd_efi_addaddr; ++ ++int ++grub_efi_net_fs_init () ++{ ++ grub_efi_net_find_cards (); ++ grub_efi_net_config = grub_efi_net_config_real; ++ grub_net_open = grub_net_open_real; ++ grub_register_variable_hook ("net_default_server", grub_efi_net_var_get_server, ++ grub_efi_net_var_set_server); ++ grub_env_export ("net_default_server"); ++ grub_register_variable_hook ("pxe_default_server", grub_efi_net_var_get_server, ++ grub_efi_net_var_set_server); ++ grub_env_export ("pxe_default_server"); ++ grub_register_variable_hook ("net_default_interface", 0, ++ grub_efi_net_var_set_interface); ++ grub_env_export ("net_default_interface"); ++ grub_register_variable_hook ("net_default_ip", grub_efi_net_var_get_ip, ++ 0); ++ grub_env_export ("net_default_ip"); ++ grub_register_variable_hook ("net_default_mac", grub_efi_net_var_get_mac, ++ 0); ++ grub_env_export ("net_default_mac"); ++ ++ grub_env_set ("grub_netfs_type", "efi"); ++ grub_register_variable_hook ("grub_netfs_type", 0, grub_env_write_readonly); ++ grub_env_export ("grub_netfs_type"); ++ ++ return 1; ++} ++ ++void ++grub_efi_net_fs_fini (void) ++{ ++ grub_env_unset ("grub_netfs_type"); ++ grub_efi_net_unset_interface_vars (); ++ grub_register_variable_hook ("net_default_server", 0, 0); ++ grub_env_unset ("net_default_server"); ++ grub_register_variable_hook ("net_default_interface", 0, 0); ++ grub_env_unset ("net_default_interface"); ++ grub_register_variable_hook ("pxe_default_server", 0, 0); ++ grub_env_unset ("pxe_default_server"); ++ grub_register_variable_hook ("net_default_ip", 0, 0); ++ grub_env_unset ("net_default_ip"); ++ grub_register_variable_hook ("net_default_mac", 0, 0); ++ grub_env_unset ("net_default_mac"); ++ grub_efi_net_config = NULL; ++ grub_net_open = NULL; ++ grub_fs_unregister (&grub_efi_netfs); ++} +--- /dev/null ++++ b/grub-core/net/efi/pxe.c +@@ -0,0 +1,424 @@ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static grub_efi_ip6_config_manual_address_t * ++efi_ip6_config_manual_address (grub_efi_ip6_config_protocol_t *ip6_config) ++{ ++ grub_efi_uintn_t sz; ++ grub_efi_status_t status; ++ grub_efi_ip6_config_manual_address_t *manual_address; ++ ++ sz = sizeof (*manual_address); ++ manual_address = grub_malloc (sz); ++ if (!manual_address) ++ return NULL; ++ ++ status = ip6_config->get_data (ip6_config, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, ++ &sz, manual_address); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_free (manual_address); ++ return NULL; ++ } ++ ++ return manual_address; ++} ++ ++static grub_efi_ip4_config2_manual_address_t * ++efi_ip4_config_manual_address (grub_efi_ip4_config2_protocol_t *ip4_config) ++{ ++ grub_efi_uintn_t sz; ++ grub_efi_status_t status; ++ grub_efi_ip4_config2_manual_address_t *manual_address; ++ ++ sz = sizeof (*manual_address); ++ manual_address = grub_malloc (sz); ++ if (!manual_address) ++ return NULL; ++ ++ status = ip4_config->get_data (ip4_config, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, ++ &sz, manual_address); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_free (manual_address); ++ return NULL; ++ } ++ ++ return manual_address; ++} ++ ++static void ++pxe_configure (struct grub_efi_net_device *dev, int prefer_ip6) ++{ ++ grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe; ++ ++ grub_efi_pxe_mode_t *mode = pxe->mode; ++ ++ if (!mode->started) ++ { ++ grub_efi_status_t status; ++ status = pxe->start (pxe, prefer_ip6); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ grub_printf ("Couldn't start PXE\n"); ++ } ++ ++#if 0 ++ grub_printf ("PXE STARTED: %u\n", mode->started); ++ grub_printf ("PXE USING IPV6: %u\n", mode->using_ipv6); ++#endif ++ ++ if (mode->using_ipv6) ++ { ++ grub_efi_ip6_config_manual_address_t *manual_address; ++ manual_address = efi_ip6_config_manual_address (dev->ip6_config); ++ ++ if (manual_address && ++ grub_memcmp (manual_address->address, mode->station_ip.v6.addr, sizeof (manual_address->address)) != 0) ++ { ++ grub_efi_status_t status; ++ grub_efi_pxe_ip_address_t station_ip; ++ ++ grub_memcpy (station_ip.v6.addr, manual_address->address, sizeof (station_ip.v6.addr)); ++ status = pxe->set_station_ip (pxe, &station_ip, NULL); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ grub_printf ("Couldn't set station ip\n"); ++ ++ grub_free (manual_address); ++ } ++ } ++ else ++ { ++ grub_efi_ip4_config2_manual_address_t *manual_address; ++ manual_address = efi_ip4_config_manual_address (dev->ip4_config); ++ ++ if (manual_address && ++ grub_memcmp (manual_address->address, mode->station_ip.v4.addr, sizeof (manual_address->address)) != 0) ++ { ++ grub_efi_status_t status; ++ grub_efi_pxe_ip_address_t station_ip; ++ grub_efi_pxe_ip_address_t subnet_mask; ++ ++ grub_memcpy (station_ip.v4.addr, manual_address->address, sizeof (station_ip.v4.addr)); ++ grub_memcpy (subnet_mask.v4.addr, manual_address->subnet_mask, sizeof (subnet_mask.v4.addr)); ++ ++ status = pxe->set_station_ip (pxe, &station_ip, &subnet_mask); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ grub_printf ("Couldn't set station ip\n"); ++ ++ grub_free (manual_address); ++ } ++ } ++ ++#if 0 ++ if (mode->using_ipv6) ++ { ++ grub_printf ("PXE STATION IP: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", ++ mode->station_ip.v6.addr[0], ++ mode->station_ip.v6.addr[1], ++ mode->station_ip.v6.addr[2], ++ mode->station_ip.v6.addr[3], ++ mode->station_ip.v6.addr[4], ++ mode->station_ip.v6.addr[5], ++ mode->station_ip.v6.addr[6], ++ mode->station_ip.v6.addr[7], ++ mode->station_ip.v6.addr[8], ++ mode->station_ip.v6.addr[9], ++ mode->station_ip.v6.addr[10], ++ mode->station_ip.v6.addr[11], ++ mode->station_ip.v6.addr[12], ++ mode->station_ip.v6.addr[13], ++ mode->station_ip.v6.addr[14], ++ mode->station_ip.v6.addr[15]); ++ } ++ else ++ { ++ grub_printf ("PXE STATION IP: %d.%d.%d.%d\n", ++ mode->station_ip.v4.addr[0], ++ mode->station_ip.v4.addr[1], ++ mode->station_ip.v4.addr[2], ++ mode->station_ip.v4.addr[3]); ++ grub_printf ("PXE SUBNET MASK: %d.%d.%d.%d\n", ++ mode->subnet_mask.v4.addr[0], ++ mode->subnet_mask.v4.addr[1], ++ mode->subnet_mask.v4.addr[2], ++ mode->subnet_mask.v4.addr[3]); ++ } ++#endif ++ ++ /* TODO: Set The Station IP to the IP2 Config */ ++} ++ ++static int ++parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest) ++{ ++ grub_uint16_t newip[8]; ++ const char *ptr = val; ++ int word, quaddot = -1; ++ int bracketed = 0; ++ ++ if (ptr[0] == '[') { ++ bracketed = 1; ++ ptr++; ++ } ++ ++ if (ptr[0] == ':' && ptr[1] != ':') ++ return 0; ++ if (ptr[0] == ':') ++ ptr++; ++ ++ for (word = 0; word < 8; word++) ++ { ++ unsigned long t; ++ if (*ptr == ':') ++ { ++ quaddot = word; ++ word--; ++ ptr++; ++ continue; ++ } ++ t = grub_strtoul (ptr, &ptr, 16); ++ if (grub_errno) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ break; ++ } ++ if (t & ~0xffff) ++ return 0; ++ newip[word] = grub_cpu_to_be16 (t); ++ if (*ptr != ':') ++ break; ++ ptr++; ++ } ++ if (quaddot == -1 && word < 7) ++ return 0; ++ if (quaddot != -1) ++ { ++ grub_memmove (&newip[quaddot + 7 - word], &newip[quaddot], ++ (word - quaddot + 1) * sizeof (newip[0])); ++ grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0])); ++ } ++ grub_memcpy (ip, newip, 16); ++ if (bracketed && *ptr == ']') { ++ ptr++; ++ } ++ if (rest) ++ *rest = ptr; ++ return 1; ++} ++ ++static grub_err_t ++pxe_open (struct grub_efi_net_device *dev, ++ int prefer_ip6, ++ grub_file_t file, ++ const char *filename, ++ int type __attribute__((unused))) ++{ ++ int i; ++ const char *p; ++ grub_efi_status_t status; ++ grub_efi_pxe_ip_address_t server_ip; ++ grub_efi_uint64_t file_size = 0; ++ grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe; ++ ++ if (pxe->mode->using_ipv6) ++ { ++ const char *rest; ++ grub_uint64_t ip6[2]; ++ if (parse_ip6 (file->device->net->server, ip6, &rest) && *rest == 0) ++ grub_memcpy (server_ip.v6.addr, ip6, sizeof (server_ip.v6.addr)); ++ /* TODO: ERROR Handling Here */ ++#if 0 ++ grub_printf ("PXE SERVER IP: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", ++ server_ip.v6.addr[0], ++ server_ip.v6.addr[1], ++ server_ip.v6.addr[2], ++ server_ip.v6.addr[3], ++ server_ip.v6.addr[4], ++ server_ip.v6.addr[5], ++ server_ip.v6.addr[6], ++ server_ip.v6.addr[7], ++ server_ip.v6.addr[8], ++ server_ip.v6.addr[9], ++ server_ip.v6.addr[10], ++ server_ip.v6.addr[11], ++ server_ip.v6.addr[12], ++ server_ip.v6.addr[13], ++ server_ip.v6.addr[14], ++ server_ip.v6.addr[15]); ++#endif ++ } ++ else ++ { ++ for (i = 0, p = file->device->net->server; i < 4; ++i, ++p) ++ server_ip.v4.addr[i] = grub_strtoul (p, &p, 10); ++ } ++ ++ status = pxe->mtftp ( ++ pxe, ++ GRUB_EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, ++ NULL, ++ 0, ++ &file_size, ++ NULL, ++ &server_ip, ++ (grub_efi_char8_t *)filename, ++ NULL, ++ 0); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return grub_error (GRUB_ERR_IO, "Couldn't get file size"); ++ ++ file->size = (grub_off_t)file_size; ++ file->not_easily_seekable = 0; ++ file->data = 0; ++ file->device->net->offset = 0; ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++pxe_close (struct grub_efi_net_device *dev __attribute__((unused)), ++ int prefer_ip6 __attribute__((unused)), ++ grub_file_t file __attribute__((unused))) ++{ ++ file->offset = 0; ++ file->size = 0; ++ file->device->net->offset = 0; ++ ++ if (file->data) ++ { ++ grub_free (file->data); ++ file->data = NULL; ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_ssize_t ++pxe_read (struct grub_efi_net_device *dev, ++ int prefer_ip6, ++ grub_file_t file, ++ char *buf, ++ grub_size_t len) ++{ ++ int i; ++ const char *p; ++ grub_efi_status_t status; ++ grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe; ++ grub_efi_uint64_t bufsz = len; ++ grub_efi_pxe_ip_address_t server_ip; ++ char *buf2 = NULL; ++ ++ if (file->data) ++ { ++ /* TODO: RANGE Check for offset and file size */ ++ grub_memcpy (buf, (char*)file->data + file->device->net->offset, len); ++ file->device->net->offset += len; ++ return len; ++ } ++ ++ if (file->device->net->offset) ++ { ++ grub_error (GRUB_ERR_BUG, "No Offet Read Possible"); ++ grub_print_error (); ++ return 0; ++ } ++ ++ if (pxe->mode->using_ipv6) ++ { ++ const char *rest; ++ grub_uint64_t ip6[2]; ++ if (parse_ip6 (file->device->net->server, ip6, &rest) && *rest == 0) ++ grub_memcpy (server_ip.v6.addr, ip6, sizeof (server_ip.v6.addr)); ++ /* TODO: ERROR Handling Here */ ++ } ++ else ++ { ++ for (i = 0, p = file->device->net->server; i < 4; ++i, ++p) ++ server_ip.v4.addr[i] = grub_strtoul (p, &p, 10); ++ } ++ ++ status = pxe->mtftp ( ++ pxe, ++ GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE, ++ buf, ++ 0, ++ &bufsz, ++ NULL, ++ &server_ip, ++ (grub_efi_char8_t *)file->device->net->name, ++ NULL, ++ 0); ++ ++ if (bufsz != file->size) ++ { ++ grub_error (GRUB_ERR_BUG, "Short read should not happen here"); ++ grub_print_error (); ++ return 0; ++ } ++ ++ if (status == GRUB_EFI_BUFFER_TOO_SMALL) ++ { ++ ++ buf2 = grub_malloc (bufsz); ++ ++ if (!buf2) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, "ERROR OUT OF MEMORY"); ++ grub_print_error (); ++ return 0; ++ } ++ ++ status = pxe->mtftp ( ++ pxe, ++ GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE, ++ buf2, ++ 0, ++ &bufsz, ++ NULL, ++ &server_ip, ++ (grub_efi_char8_t *)file->device->net->name, ++ NULL, ++ 0); ++ } ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ if (buf2) ++ grub_free (buf2); ++ ++ grub_error (GRUB_ERR_IO, "Failed to Read File"); ++ grub_print_error (); ++ return 0; ++ } ++ ++ if (buf2) ++ grub_memcpy (buf, buf2, len); ++ ++ file->device->net->offset = len; ++ ++ if (buf2) ++ file->data = buf2; ++ ++ return len; ++} ++ ++struct grub_efi_net_io io_pxe = ++ { ++ .configure = pxe_configure, ++ .open = pxe_open, ++ .read = pxe_read, ++ .close = pxe_close ++ }; ++ +--- a/grub-core/net/net.c ++++ b/grub-core/net/net.c +@@ -32,6 +32,9 @@ + #include + #include + #include ++#ifdef GRUB_MACHINE_EFI ++#include ++#endif + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -2014,8 +2017,49 @@ + static grub_command_t cmd_setvlan, cmd_lsroutes, cmd_lscards; + static grub_command_t cmd_lsaddr, cmd_slaac; + ++#ifdef GRUB_MACHINE_EFI ++ ++static enum { ++ INIT_MODE_NONE, ++ INIT_MODE_GRUB, ++ INIT_MODE_EFI ++} init_mode; ++ ++static grub_command_t cmd_bootp, cmd_bootp6; ++ ++#endif ++ + GRUB_MOD_INIT(net) + { ++#ifdef GRUB_MACHINE_EFI ++ if (grub_net_open) ++ return; ++ ++ if ((grub_efi_net_boot_from_https () || grub_efi_net_boot_from_opa ()) ++ && grub_efi_net_fs_init ()) ++ { ++ cmd_lsroutes = grub_register_command ("net_ls_routes", grub_efi_net_list_routes, ++ "", N_("list network routes")); ++ cmd_lscards = grub_register_command ("net_ls_cards", grub_efi_net_list_cards, ++ "", N_("list network cards")); ++ cmd_lsaddr = grub_register_command ("net_ls_addr", grub_efi_net_list_addrs, ++ "", N_("list network addresses")); ++ cmd_addaddr = grub_register_command ("net_add_addr", grub_efi_net_add_addr, ++ /* TRANSLATORS: HWADDRESS stands for ++ "hardware address". */ ++ N_("SHORTNAME CARD ADDRESS [HWADDRESS]"), ++ N_("Add a network address.")); ++ cmd_bootp = grub_register_command ("net_bootp", grub_efi_net_bootp, ++ N_("[CARD]"), ++ N_("perform a bootp autoconfiguration")); ++ cmd_bootp6 = grub_register_command ("net_bootp6", grub_efi_net_bootp6, ++ N_("[CARD]"), ++ N_("perform a bootp autoconfiguration")); ++ init_mode = INIT_MODE_EFI; ++ return; ++ } ++#endif ++ + grub_register_variable_hook ("net_default_server", defserver_get_env, + defserver_set_env); + grub_env_export ("net_default_server"); +@@ -2066,10 +2110,37 @@ + grub_net_restore_hw, + GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK); + grub_net_poll_cards_idle = grub_net_poll_cards_idle_real; ++ ++#ifdef GRUB_MACHINE_EFI ++ grub_env_set ("grub_netfs_type", "grub"); ++ grub_register_variable_hook ("grub_netfs_type", 0, grub_env_write_readonly); ++ grub_env_export ("grub_netfs_type"); ++ init_mode = INIT_MODE_GRUB; ++#endif ++ + } + + GRUB_MOD_FINI(net) + { ++ ++#ifdef GRUB_MACHINE_EFI ++ if (init_mode == INIT_MODE_NONE) ++ return; ++ ++ if (init_mode == INIT_MODE_EFI) ++ { ++ grub_unregister_command (cmd_lsroutes); ++ grub_unregister_command (cmd_lscards); ++ grub_unregister_command (cmd_lsaddr); ++ grub_unregister_command (cmd_addaddr); ++ grub_unregister_command (cmd_bootp); ++ grub_unregister_command (cmd_bootp6); ++ grub_efi_net_fs_fini (); ++ init_mode = INIT_MODE_NONE; ++ return; ++ } ++#endif ++ + grub_register_variable_hook ("net_default_server", 0, 0); + grub_register_variable_hook ("pxe_default_server", 0, 0); + +@@ -2088,4 +2159,7 @@ + grub_net_fini_hw (0); + grub_loader_unregister_preboot_hook (fini_hnd); + grub_net_poll_cards_idle = NULL; ++#ifdef GRUB_MACHINE_EFI ++ init_mode = INIT_MODE_NONE; ++#endif + } +--- a/include/grub/efi/api.h ++++ b/include/grub/efi/api.h +@@ -653,6 +653,23 @@ + typedef grub_uint8_t grub_efi_ip_address_t[8] __attribute__ ((aligned(4))); + typedef grub_efi_uint64_t grub_efi_physical_address_t; + typedef grub_efi_uint64_t grub_efi_virtual_address_t; ++typedef struct { ++ grub_uint8_t addr[4]; ++} grub_efi_pxe_ipv4_address_t; ++ ++typedef struct { ++ grub_uint8_t addr[16]; ++} grub_efi_pxe_ipv6_address_t; ++ ++typedef struct { ++ grub_uint8_t addr[32]; ++} grub_efi_pxe_mac_address_t; ++ ++typedef union { ++ grub_uint32_t addr[4]; ++ grub_efi_pxe_ipv4_address_t v4; ++ grub_efi_pxe_ipv6_address_t v6; ++} grub_efi_pxe_ip_address_t; + + /* XXX although the spec does not specify the padding, this actually + must have the padding! */ +@@ -902,6 +919,8 @@ + grub_efi_uint16_t remote_port; + grub_efi_uint16_t protocol; + grub_efi_uint8_t static_ip_address; ++ grub_efi_uint8_t prefix_length; ++ grub_efi_ipv6_address_t gateway_ip_address; + } GRUB_PACKED; + typedef struct grub_efi_ipv6_device_path grub_efi_ipv6_device_path_t; + +@@ -960,6 +979,15 @@ + } GRUB_PACKED; + typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t; + ++#define GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE 31 ++struct grub_efi_dns_device_path ++{ ++ grub_efi_device_path_t header; ++ grub_efi_uint8_t is_ipv6; ++ grub_efi_pxe_ip_address_t dns_server_ip[0]; ++} GRUB_PACKED; ++typedef struct grub_efi_dns_device_path grub_efi_dns_device_path_t; ++ + #define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE 10 + + /* Media Device Path. */ +@@ -1042,6 +1070,23 @@ + } GRUB_PACKED; + typedef struct grub_efi_bios_device_path grub_efi_bios_device_path_t; + ++/* Service Binding definitions */ ++struct grub_efi_service_binding; ++ ++typedef grub_efi_status_t ++(__grub_efi_api *grub_efi_service_binding_create_child) (struct grub_efi_service_binding *this, ++ grub_efi_handle_t *child_handle); ++ ++typedef grub_efi_status_t ++(__grub_efi_api *grub_efi_service_binding_destroy_child) (struct grub_efi_service_binding *this, ++ grub_efi_handle_t *child_handle); ++ ++typedef struct grub_efi_service_binding ++{ ++ grub_efi_service_binding_create_child create_child; ++ grub_efi_service_binding_destroy_child destroy_child; ++} grub_efi_service_binding_t; ++ + struct grub_efi_open_protocol_information_entry + { + grub_efi_handle_t agent_handle; +@@ -1544,23 +1589,28 @@ + + typedef grub_uint8_t grub_efi_pxe_packet_t[1472]; + +-typedef struct { +- grub_uint8_t addr[4]; +-} grub_efi_pxe_ipv4_address_t; ++typedef grub_efi_uint16_t grub_efi_pxe_base_code_udp_port_t; + +-typedef struct { +- grub_uint8_t addr[16]; +-} grub_efi_pxe_ipv6_address_t; ++typedef enum { ++ GRUB_EFI_PXE_BASE_CODE_TFTP_FIRST, ++ GRUB_EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, ++ GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE, ++ GRUB_EFI_PXE_BASE_CODE_TFTP_WRITE_FILE, ++ GRUB_EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY, ++ GRUB_EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE, ++ GRUB_EFI_PXE_BASE_CODE_MTFTP_READ_FILE, ++ GRUB_EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY, ++ GRUB_EFI_PXE_BASE_CODE_MTFTP_LAST ++} grub_efi_pxe_base_code_tftp_opcode_t; + +-typedef struct { +- grub_uint8_t addr[32]; +-} grub_efi_pxe_mac_address_t; + +-typedef union { +- grub_uint32_t addr[4]; +- grub_efi_pxe_ipv4_address_t v4; +- grub_efi_pxe_ipv6_address_t v6; +-} grub_efi_pxe_ip_address_t; ++typedef struct { ++ grub_efi_ip_address_t mcast_ip; ++ grub_efi_pxe_base_code_udp_port_t c_port; ++ grub_efi_pxe_base_code_udp_port_t s_port; ++ grub_efi_uint16_t listen_timeout; ++ grub_efi_uint16_t transmit_timeout; ++} grub_efi_pxe_base_code_mtftp_info_t; + + #define GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT 8 + typedef struct { +@@ -1610,18 +1660,32 @@ + typedef struct grub_efi_pxe + { + grub_uint64_t rev; +- void *start; +- void *stop; +- void *dhcp; +- void *discover; +- void *mftp; +- void *udpwrite; +- void *udpread; +- void *setipfilter; +- void *arp; +- void *setparams; +- void *setstationip; +- void *setpackets; ++ grub_efi_status_t (__grub_efi_api *start) (struct grub_efi_pxe *this, grub_efi_boolean_t use_ipv6); ++ void (__grub_efi_api *stop) (void); ++ grub_efi_status_t (__grub_efi_api *dhcp) (struct grub_efi_pxe *this, ++ grub_efi_boolean_t sort_offers); ++ void (__grub_efi_api *discover) (void); ++ grub_efi_status_t (__grub_efi_api *mtftp) (struct grub_efi_pxe *this, ++ grub_efi_pxe_base_code_tftp_opcode_t operation, ++ void *buffer_ptr, ++ grub_efi_boolean_t overwrite, ++ grub_efi_uint64_t *buffer_size, ++ grub_efi_uintn_t *block_size, ++ grub_efi_pxe_ip_address_t *server_ip, ++ //grub_efi_ip_address_t *server_ip, ++ grub_efi_char8_t *filename, ++ grub_efi_pxe_base_code_mtftp_info_t *info, ++ grub_efi_boolean_t dont_use_buffer); ++ void (__grub_efi_api *udpwrite) (void); ++ void (__grub_efi_api *udpread) (void); ++ void (__grub_efi_api *setipfilter) (void); ++ void (__grub_efi_api *arp) (void); ++ void (__grub_efi_api *setparams) (void); ++ grub_efi_status_t (__grub_efi_api *set_station_ip) (struct grub_efi_pxe *this, ++ grub_efi_pxe_ip_address_t *new_station_ip, ++ grub_efi_pxe_ip_address_t *new_subnet_mask); ++ //void (*setstationip) (void); ++ void (__grub_efi_api *setpackets) (void); + struct grub_efi_pxe_mode *mode; + } grub_efi_pxe_t; + +@@ -1921,6 +1985,44 @@ + }; + typedef struct grub_efi_ip4_config2_protocol grub_efi_ip4_config2_protocol_t; + ++struct grub_efi_ip4_route_table { ++ grub_efi_ipv4_address_t subnet_address; ++ grub_efi_ipv4_address_t subnet_mask; ++ grub_efi_ipv4_address_t gateway_address; ++}; ++ ++typedef struct grub_efi_ip4_route_table grub_efi_ip4_route_table_t; ++ ++#define GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE 32 ++ ++struct grub_efi_ip4_config2_interface_info { ++ grub_efi_char16_t name[GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE]; ++ grub_efi_uint8_t if_type; ++ grub_efi_uint32_t hw_address_size; ++ grub_efi_mac_address_t hw_address; ++ grub_efi_ipv4_address_t station_address; ++ grub_efi_ipv4_address_t subnet_mask; ++ grub_efi_uint32_t route_table_size; ++ grub_efi_ip4_route_table_t *route_table; ++}; ++ ++typedef struct grub_efi_ip4_config2_interface_info grub_efi_ip4_config2_interface_info_t; ++ ++enum grub_efi_ip4_config2_policy { ++ GRUB_EFI_IP4_CONFIG2_POLICY_STATIC, ++ GRUB_EFI_IP4_CONFIG2_POLICY_DHCP, ++ GRUB_EFI_IP4_CONFIG2_POLICY_MAX ++}; ++ ++typedef enum grub_efi_ip4_config2_policy grub_efi_ip4_config2_policy_t; ++ ++struct grub_efi_ip4_config2_manual_address { ++ grub_efi_ipv4_address_t address; ++ grub_efi_ipv4_address_t subnet_mask; ++}; ++ ++typedef struct grub_efi_ip4_config2_manual_address grub_efi_ip4_config2_manual_address_t; ++ + enum grub_efi_ip6_config_data_type { + GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_ALT_INTERFACEID, +@@ -1955,4 +2057,47 @@ + }; + typedef struct grub_efi_ip6_config_protocol grub_efi_ip6_config_protocol_t; + ++enum grub_efi_ip6_config_policy { ++ GRUB_EFI_IP6_CONFIG_POLICY_MANUAL, ++ GRUB_EFI_IP6_CONFIG_POLICY_AUTOMATIC ++}; ++typedef enum grub_efi_ip6_config_policy grub_efi_ip6_config_policy_t; ++ ++struct grub_efi_ip6_address_info { ++ grub_efi_ipv6_address_t address; ++ grub_efi_uint8_t prefix_length; ++}; ++typedef struct grub_efi_ip6_address_info grub_efi_ip6_address_info_t; ++ ++struct grub_efi_ip6_route_table { ++ grub_efi_pxe_ipv6_address_t gateway; ++ grub_efi_pxe_ipv6_address_t destination; ++ grub_efi_uint8_t prefix_length; ++}; ++typedef struct grub_efi_ip6_route_table grub_efi_ip6_route_table_t; ++ ++struct grub_efi_ip6_config_interface_info { ++ grub_efi_char16_t name[32]; ++ grub_efi_uint8_t if_type; ++ grub_efi_uint32_t hw_address_size; ++ grub_efi_mac_address_t hw_address; ++ grub_efi_uint32_t address_info_count; ++ grub_efi_ip6_address_info_t *address_info; ++ grub_efi_uint32_t route_count; ++ grub_efi_ip6_route_table_t *route_table; ++}; ++typedef struct grub_efi_ip6_config_interface_info grub_efi_ip6_config_interface_info_t; ++ ++struct grub_efi_ip6_config_dup_addr_detect_transmits { ++ grub_efi_uint32_t dup_addr_detect_transmits; ++}; ++typedef struct grub_efi_ip6_config_dup_addr_detect_transmits grub_efi_ip6_config_dup_addr_detect_transmits_t; ++ ++struct grub_efi_ip6_config_manual_address { ++ grub_efi_ipv6_address_t address; ++ grub_efi_boolean_t is_anycast; ++ grub_efi_uint8_t prefix_length; ++}; ++typedef struct grub_efi_ip6_config_manual_address grub_efi_ip6_config_manual_address_t; ++ + #endif /* ! GRUB_EFI_API_HEADER */ +--- /dev/null ++++ b/include/grub/efi/dhcp.h +@@ -0,0 +1,343 @@ ++#ifndef GRUB_EFI_DHCP_HEADER ++#define GRUB_EFI_DHCP_HEADER 1 ++ ++#define GRUB_EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID \ ++ { 0x9d9a39d8, 0xbd42, 0x4a73, \ ++ { 0xa4, 0xd5, 0x8e, 0xe9, 0x4b, 0xe1, 0x13, 0x80 } \ ++ } ++ ++#define GRUB_EFI_DHCP4_PROTOCOL_GUID \ ++ { 0x8a219718, 0x4ef5, 0x4761, \ ++ { 0x91, 0xc8, 0xc0, 0xf0, 0x4b, 0xda, 0x9e, 0x56 } \ ++ } ++ ++#define GRUB_EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID \ ++ { 0x9fb9a8a1, 0x2f4a, 0x43a6, \ ++ { 0x88, 0x9c, 0xd0, 0xf7, 0xb6, 0xc4 ,0x7a, 0xd5 } \ ++ } ++ ++#define GRUB_EFI_DHCP6_PROTOCOL_GUID \ ++ { 0x87c8bad7, 0x595, 0x4053, \ ++ { 0x82, 0x97, 0xde, 0xde, 0x39, 0x5f, 0x5d, 0x5b } \ ++ } ++ ++typedef struct grub_efi_dhcp4_protocol grub_efi_dhcp4_protocol_t; ++ ++enum grub_efi_dhcp4_state { ++ GRUB_EFI_DHCP4_STOPPED, ++ GRUB_EFI_DHCP4_INIT, ++ GRUB_EFI_DHCP4_SELECTING, ++ GRUB_EFI_DHCP4_REQUESTING, ++ GRUB_EFI_DHCP4_BOUND, ++ GRUB_EFI_DHCP4_RENEWING, ++ GRUB_EFI_DHCP4_REBINDING, ++ GRUB_EFI_DHCP4_INIT_REBOOT, ++ GRUB_EFI_DHCP4_REBOOTING ++}; ++ ++typedef enum grub_efi_dhcp4_state grub_efi_dhcp4_state_t; ++ ++struct grub_efi_dhcp4_header { ++ grub_efi_uint8_t op_code; ++ grub_efi_uint8_t hw_type; ++ grub_efi_uint8_t hw_addr_len; ++ grub_efi_uint8_t hops; ++ grub_efi_uint32_t xid; ++ grub_efi_uint16_t seconds; ++ grub_efi_uint16_t reserved; ++ grub_efi_ipv4_address_t client_addr; ++ grub_efi_ipv4_address_t your_addr; ++ grub_efi_ipv4_address_t server_addr; ++ grub_efi_ipv4_address_t gateway_addr; ++ grub_efi_uint8_t client_hw_addr[16]; ++ grub_efi_char8_t server_name[64]; ++ grub_efi_char8_t boot_file_name[128]; ++} GRUB_PACKED; ++ ++typedef struct grub_efi_dhcp4_header grub_efi_dhcp4_header_t; ++ ++struct grub_efi_dhcp4_packet { ++ grub_efi_uint32_t size; ++ grub_efi_uint32_t length; ++ struct { ++ grub_efi_dhcp4_header_t header; ++ grub_efi_uint32_t magik; ++ grub_efi_uint8_t option[1]; ++ } dhcp4; ++} GRUB_PACKED; ++ ++typedef struct grub_efi_dhcp4_packet grub_efi_dhcp4_packet_t; ++ ++struct grub_efi_dhcp4_listen_point { ++ grub_efi_ipv4_address_t listen_address; ++ grub_efi_ipv4_address_t subnet_mask; ++ grub_efi_uint16_t listen_port; ++}; ++ ++typedef struct grub_efi_dhcp4_listen_point grub_efi_dhcp4_listen_point_t; ++ ++struct grub_efi_dhcp4_transmit_receive_token { ++ grub_efi_status_t status; ++ grub_efi_event_t completion_event; ++ grub_efi_ipv4_address_t remote_address; ++ grub_efi_uint16_t remote_port; ++ grub_efi_ipv4_address_t gateway_address; ++ grub_efi_uint32_t listen_point_count; ++ grub_efi_dhcp4_listen_point_t *listen_points; ++ grub_efi_uint32_t timeout_value; ++ grub_efi_dhcp4_packet_t *packet; ++ grub_efi_uint32_t response_count; ++ grub_efi_dhcp4_packet_t *response_list; ++}; ++ ++typedef struct grub_efi_dhcp4_transmit_receive_token grub_efi_dhcp4_transmit_receive_token_t; ++ ++enum grub_efi_dhcp4_event { ++ GRUB_EFI_DHCP4_SEND_DISCOVER = 0X01, ++ GRUB_EFI_DHCP4_RCVD_OFFER, ++ GRUB_EFI_DHCP4_SELECT_OFFER, ++ GRUB_EFI_DHCP4_SEND_REQUEST, ++ GRUB_EFI_DHCP4_RCVD_ACK, ++ GRUB_EFI_DHCP4_RCVD_NAK, ++ GRUB_EFI_DHCP4_SEND_DECLINE, ++ GRUB_EFI_DHCP4_BOUND_COMPLETED, ++ GRUB_EFI_DHCP4_ENTER_RENEWING, ++ GRUB_EFI_DHCP4_ENTER_REBINDING, ++ GRUB_EFI_DHCP4_ADDRESS_LOST, ++ GRUB_EFI_DHCP4_FAIL ++}; ++ ++typedef enum grub_efi_dhcp4_event grub_efi_dhcp4_event_t; ++ ++struct grub_efi_dhcp4_packet_option { ++ grub_efi_uint8_t op_code; ++ grub_efi_uint8_t length; ++ grub_efi_uint8_t data[1]; ++} GRUB_PACKED; ++ ++typedef struct grub_efi_dhcp4_packet_option grub_efi_dhcp4_packet_option_t; ++ ++struct grub_efi_dhcp4_config_data { ++ grub_efi_uint32_t discover_try_count; ++ grub_efi_uint32_t *discover_timeout; ++ grub_efi_uint32_t request_try_count; ++ grub_efi_uint32_t *request_timeout; ++ grub_efi_ipv4_address_t client_address; ++ grub_efi_status_t (*dhcp4_callback) ( ++ grub_efi_dhcp4_protocol_t *this, ++ void *context, ++ grub_efi_dhcp4_state_t current_state, ++ grub_efi_dhcp4_event_t dhcp4_event, ++ grub_efi_dhcp4_packet_t *packet, ++ grub_efi_dhcp4_packet_t **new_packet ++ ); ++ void *callback_context; ++ grub_efi_uint32_t option_count; ++ grub_efi_dhcp4_packet_option_t **option_list; ++}; ++ ++typedef struct grub_efi_dhcp4_config_data grub_efi_dhcp4_config_data_t; ++ ++struct grub_efi_dhcp4_mode_data { ++ grub_efi_dhcp4_state_t state; ++ grub_efi_dhcp4_config_data_t config_data; ++ grub_efi_ipv4_address_t client_address; ++ grub_efi_mac_address_t client_mac_address; ++ grub_efi_ipv4_address_t server_address; ++ grub_efi_ipv4_address_t router_address; ++ grub_efi_ipv4_address_t subnet_mask; ++ grub_efi_uint32_t lease_time; ++ grub_efi_dhcp4_packet_t *reply_packet; ++}; ++ ++typedef struct grub_efi_dhcp4_mode_data grub_efi_dhcp4_mode_data_t; ++ ++struct grub_efi_dhcp4_protocol { ++ grub_efi_status_t (__grub_efi_api *get_mode_data) (grub_efi_dhcp4_protocol_t *this, ++ grub_efi_dhcp4_mode_data_t *dhcp4_mode_data); ++ grub_efi_status_t (__grub_efi_api *configure) (grub_efi_dhcp4_protocol_t *this, ++ grub_efi_dhcp4_config_data_t *dhcp4_cfg_data); ++ grub_efi_status_t (__grub_efi_api *start) (grub_efi_dhcp4_protocol_t *this, ++ grub_efi_event_t completion_event); ++ grub_efi_status_t (__grub_efi_api *renew_rebind) (grub_efi_dhcp4_protocol_t *this, ++ grub_efi_boolean_t rebind_request, ++ grub_efi_event_t completion_event); ++ grub_efi_status_t (__grub_efi_api *release) (grub_efi_dhcp4_protocol_t *this); ++ grub_efi_status_t (__grub_efi_api *stop) (grub_efi_dhcp4_protocol_t *this); ++ grub_efi_status_t (__grub_efi_api *build) (grub_efi_dhcp4_protocol_t *this, ++ grub_efi_dhcp4_packet_t *seed_packet, ++ grub_efi_uint32_t delete_count, ++ grub_efi_uint8_t *delete_list, ++ grub_efi_uint32_t append_count, ++ grub_efi_dhcp4_packet_option_t *append_list[], ++ grub_efi_dhcp4_packet_t **new_packet); ++ grub_efi_status_t (__grub_efi_api *transmit_receive) (grub_efi_dhcp4_protocol_t *this, ++ grub_efi_dhcp4_transmit_receive_token_t *token); ++ grub_efi_status_t (__grub_efi_api *parse) (grub_efi_dhcp4_protocol_t *this, ++ grub_efi_dhcp4_packet_t *packet, ++ grub_efi_uint32_t *option_count, ++ grub_efi_dhcp4_packet_option_t *packet_option_list[]); ++}; ++ ++typedef struct grub_efi_dhcp6_protocol grub_efi_dhcp6_protocol_t; ++ ++struct grub_efi_dhcp6_retransmission { ++ grub_efi_uint32_t irt; ++ grub_efi_uint32_t mrc; ++ grub_efi_uint32_t mrt; ++ grub_efi_uint32_t mrd; ++}; ++ ++typedef struct grub_efi_dhcp6_retransmission grub_efi_dhcp6_retransmission_t; ++ ++enum grub_efi_dhcp6_event { ++ GRUB_EFI_DHCP6_SEND_SOLICIT, ++ GRUB_EFI_DHCP6_RCVD_ADVERTISE, ++ GRUB_EFI_DHCP6_SELECT_ADVERTISE, ++ GRUB_EFI_DHCP6_SEND_REQUEST, ++ GRUB_EFI_DHCP6_RCVD_REPLY, ++ GRUB_EFI_DHCP6_RCVD_RECONFIGURE, ++ GRUB_EFI_DHCP6_SEND_DECLINE, ++ GRUB_EFI_DHCP6_SEND_CONFIRM, ++ GRUB_EFI_DHCP6_SEND_RELEASE, ++ GRUB_EFI_DHCP6_SEND_RENEW, ++ GRUB_EFI_DHCP6_SEND_REBIND ++}; ++ ++typedef enum grub_efi_dhcp6_event grub_efi_dhcp6_event_t; ++ ++struct grub_efi_dhcp6_packet_option { ++ grub_efi_uint16_t op_code; ++ grub_efi_uint16_t op_len; ++ grub_efi_uint8_t data[1]; ++} GRUB_PACKED; ++ ++typedef struct grub_efi_dhcp6_packet_option grub_efi_dhcp6_packet_option_t; ++ ++struct grub_efi_dhcp6_header { ++ grub_efi_uint32_t transaction_id:24; ++ grub_efi_uint32_t message_type:8; ++} GRUB_PACKED; ++ ++typedef struct grub_efi_dhcp6_header grub_efi_dhcp6_header_t; ++ ++struct grub_efi_dhcp6_packet { ++ grub_efi_uint32_t size; ++ grub_efi_uint32_t length; ++ struct { ++ grub_efi_dhcp6_header_t header; ++ grub_efi_uint8_t option[1]; ++ } dhcp6; ++} GRUB_PACKED; ++ ++typedef struct grub_efi_dhcp6_packet grub_efi_dhcp6_packet_t; ++ ++struct grub_efi_dhcp6_ia_address { ++ grub_efi_ipv6_address_t ip_address; ++ grub_efi_uint32_t preferred_lifetime; ++ grub_efi_uint32_t valid_lifetime; ++}; ++ ++typedef struct grub_efi_dhcp6_ia_address grub_efi_dhcp6_ia_address_t; ++ ++enum grub_efi_dhcp6_state { ++ GRUB_EFI_DHCP6_INIT, ++ GRUB_EFI_DHCP6_SELECTING, ++ GRUB_EFI_DHCP6_REQUESTING, ++ GRUB_EFI_DHCP6_DECLINING, ++ GRUB_EFI_DHCP6_CONFIRMING, ++ GRUB_EFI_DHCP6_RELEASING, ++ GRUB_EFI_DHCP6_BOUND, ++ GRUB_EFI_DHCP6_RENEWING, ++ GRUB_EFI_DHCP6_REBINDING ++}; ++ ++typedef enum grub_efi_dhcp6_state grub_efi_dhcp6_state_t; ++ ++#define GRUB_EFI_DHCP6_IA_TYPE_NA 3 ++#define GRUB_EFI_DHCP6_IA_TYPE_TA 4 ++ ++struct grub_efi_dhcp6_ia_descriptor { ++ grub_efi_uint16_t type; ++ grub_efi_uint32_t ia_id; ++}; ++ ++typedef struct grub_efi_dhcp6_ia_descriptor grub_efi_dhcp6_ia_descriptor_t; ++ ++struct grub_efi_dhcp6_ia { ++ grub_efi_dhcp6_ia_descriptor_t descriptor; ++ grub_efi_dhcp6_state_t state; ++ grub_efi_dhcp6_packet_t *reply_packet; ++ grub_efi_uint32_t ia_address_count; ++ grub_efi_dhcp6_ia_address_t ia_address[1]; ++}; ++ ++typedef struct grub_efi_dhcp6_ia grub_efi_dhcp6_ia_t; ++ ++struct grub_efi_dhcp6_duid { ++ grub_efi_uint16_t length; ++ grub_efi_uint8_t duid[1]; ++}; ++ ++typedef struct grub_efi_dhcp6_duid grub_efi_dhcp6_duid_t; ++ ++struct grub_efi_dhcp6_mode_data { ++ grub_efi_dhcp6_duid_t *client_id; ++ grub_efi_dhcp6_ia_t *ia; ++}; ++ ++typedef struct grub_efi_dhcp6_mode_data grub_efi_dhcp6_mode_data_t; ++ ++struct grub_efi_dhcp6_config_data { ++ grub_efi_status_t (*dhcp6_callback) (grub_efi_dhcp6_protocol_t this, ++ void *context, ++ grub_efi_dhcp6_state_t current_state, ++ grub_efi_dhcp6_event_t dhcp6_event, ++ grub_efi_dhcp6_packet_t *packet, ++ grub_efi_dhcp6_packet_t **new_packet); ++ void *callback_context; ++ grub_efi_uint32_t option_count; ++ grub_efi_dhcp6_packet_option_t **option_list; ++ grub_efi_dhcp6_ia_descriptor_t ia_descriptor; ++ grub_efi_event_t ia_info_event; ++ grub_efi_boolean_t reconfigure_accept; ++ grub_efi_boolean_t rapid_commit; ++ grub_efi_dhcp6_retransmission_t *solicit_retransmission; ++}; ++ ++typedef struct grub_efi_dhcp6_config_data grub_efi_dhcp6_config_data_t; ++ ++struct grub_efi_dhcp6_protocol { ++ grub_efi_status_t (__grub_efi_api *get_mode_data) (grub_efi_dhcp6_protocol_t *this, ++ grub_efi_dhcp6_mode_data_t *dhcp6_mode_data, ++ grub_efi_dhcp6_config_data_t *dhcp6_config_data); ++ grub_efi_status_t (__grub_efi_api *configure) (grub_efi_dhcp6_protocol_t *this, ++ grub_efi_dhcp6_config_data_t *dhcp6_cfg_data); ++ grub_efi_status_t (__grub_efi_api *start) (grub_efi_dhcp6_protocol_t *this); ++ grub_efi_status_t (__grub_efi_api *info_request) (grub_efi_dhcp6_protocol_t *this, ++ grub_efi_boolean_t send_client_id, ++ grub_efi_dhcp6_packet_option_t *option_request, ++ grub_efi_uint32_t option_count, ++ grub_efi_dhcp6_packet_option_t *option_list[], ++ grub_efi_dhcp6_retransmission_t *retransmission, ++ grub_efi_event_t timeout_event, ++ grub_efi_status_t (__grub_efi_api *reply_callback) (grub_efi_dhcp6_protocol_t *this, ++ void *context, ++ grub_efi_dhcp6_packet_t *packet), ++ void *callback_context); ++ grub_efi_status_t (__grub_efi_api *renew_rebind) (grub_efi_dhcp6_protocol_t *this, ++ grub_efi_boolean_t rebind_request); ++ grub_efi_status_t (__grub_efi_api *decline) (grub_efi_dhcp6_protocol_t *this, ++ grub_efi_uint32_t address_count, ++ grub_efi_ipv6_address_t *addresses); ++ grub_efi_status_t (__grub_efi_api *release) (grub_efi_dhcp6_protocol_t *this, ++ grub_efi_uint32_t address_count, ++ grub_efi_ipv6_address_t *addresses); ++ grub_efi_status_t (__grub_efi_api *stop) (grub_efi_dhcp6_protocol_t *this); ++ grub_efi_status_t (__grub_efi_api *parse) (grub_efi_dhcp6_protocol_t *this, ++ grub_efi_dhcp6_packet_t *packet, ++ grub_efi_uint32_t *option_count, ++ grub_efi_dhcp6_packet_option_t *packet_option_list[]); ++}; ++ ++#endif /* ! GRUB_EFI_DHCP_HEADER */ +--- /dev/null ++++ b/include/grub/efi/http.h +@@ -0,0 +1,215 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_EFI_HTTP_HEADER ++#define GRUB_EFI_HTTP_HEADER 1 ++ ++#include ++#include ++#include ++ ++#define GRUB_EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID \ ++ { 0xbdc8e6af, 0xd9bc, 0x4379, \ ++ { 0xa7, 0x2a, 0xe0, 0xc4, 0xe7, 0x5d, 0xae, 0x1c } \ ++ } ++ ++#define GRUB_EFI_HTTP_PROTOCOL_GUID \ ++ { 0x7A59B29B, 0x910B, 0x4171, \ ++ { 0x82, 0x42, 0xA8, 0x5A, 0x0D, 0xF2, 0x5B, 0x5B } \ ++ } ++ ++#define EFIHTTP_WAIT_TIME 10000 // 10000ms = 10s ++#define EFIHTTP_RX_BUF_LEN 10240 ++ ++//****************************************** ++// Protocol Interface Structure ++//****************************************** ++struct grub_efi_http; ++ ++//****************************************** ++// EFI_HTTP_VERSION ++//****************************************** ++typedef enum { ++ GRUB_EFI_HTTPVERSION10, ++ GRUB_EFI_HTTPVERSION11, ++ GRUB_EFI_HTTPVERSIONUNSUPPORTED ++} grub_efi_http_version_t; ++ ++//****************************************** ++// EFI_HTTPv4_ACCESS_POINT ++//****************************************** ++typedef struct { ++ grub_efi_boolean_t use_default_address; ++ grub_efi_ipv4_address_t local_address; ++ grub_efi_ipv4_address_t local_subnet; ++ grub_efi_uint16_t local_port; ++} grub_efi_httpv4_access_point_t; ++ ++//****************************************** ++// EFI_HTTPv6_ACCESS_POINT ++//****************************************** ++typedef struct { ++ grub_efi_ipv6_address_t local_address; ++ grub_efi_uint16_t local_port; ++} grub_efi_httpv6_access_point_t; ++ ++//****************************************** ++// EFI_HTTP_CONFIG_DATA ++//****************************************** ++typedef struct { ++ grub_efi_http_version_t http_version; ++ grub_efi_uint32_t timeout_millisec; ++ grub_efi_boolean_t local_address_is_ipv6; ++ union { ++ grub_efi_httpv4_access_point_t *ipv4_node; ++ grub_efi_httpv6_access_point_t *ipv6_node; ++ } access_point; ++} grub_efi_http_config_data_t; ++ ++//****************************************** ++// EFI_HTTP_METHOD ++//****************************************** ++typedef enum { ++ GRUB_EFI_HTTPMETHODGET, ++ GRUB_EFI_HTTPMETHODPOST, ++ GRUB_EFI_HTTPMETHODPATCH, ++ GRUB_EFI_HTTPMETHODOPTIONS, ++ GRUB_EFI_HTTPMETHODCONNECT, ++ GRUB_EFI_HTTPMETHODHEAD, ++ GRUB_EFI_HTTPMETHODPUT, ++ GRUB_EFI_HTTPMETHODDELETE, ++ GRUB_EFI_HTTPMETHODTRACE, ++} grub_efi_http_method_t; ++ ++//****************************************** ++// EFI_HTTP_REQUEST_DATA ++//****************************************** ++typedef struct { ++ grub_efi_http_method_t method; ++ grub_efi_char16_t *url; ++} grub_efi_http_request_data_t; ++ ++typedef enum { ++ GRUB_EFI_HTTP_STATUS_UNSUPPORTED_STATUS = 0, ++ GRUB_EFI_HTTP_STATUS_100_CONTINUE, ++ GRUB_EFI_HTTP_STATUS_101_SWITCHING_PROTOCOLS, ++ GRUB_EFI_HTTP_STATUS_200_OK, ++ GRUB_EFI_HTTP_STATUS_201_CREATED, ++ GRUB_EFI_HTTP_STATUS_202_ACCEPTED, ++ GRUB_EFI_HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION, ++ GRUB_EFI_HTTP_STATUS_204_NO_CONTENT, ++ GRUB_EFI_HTTP_STATUS_205_RESET_CONTENT, ++ GRUB_EFI_HTTP_STATUS_206_PARTIAL_CONTENT, ++ GRUB_EFI_HTTP_STATUS_300_MULTIPLE_CHIOCES, ++ GRUB_EFI_HTTP_STATUS_301_MOVED_PERMANENTLY, ++ GRUB_EFI_HTTP_STATUS_302_FOUND, ++ GRUB_EFI_HTTP_STATUS_303_SEE_OTHER, ++ GRUB_EFI_HTTP_STATUS_304_NOT_MODIFIED, ++ GRUB_EFI_HTTP_STATUS_305_USE_PROXY, ++ GRUB_EFI_HTTP_STATUS_307_TEMPORARY_REDIRECT, ++ GRUB_EFI_HTTP_STATUS_400_BAD_REQUEST, ++ GRUB_EFI_HTTP_STATUS_401_UNAUTHORIZED, ++ GRUB_EFI_HTTP_STATUS_402_PAYMENT_REQUIRED, ++ GRUB_EFI_HTTP_STATUS_403_FORBIDDEN, ++ GRUB_EFI_HTTP_STATUS_404_NOT_FOUND, ++ GRUB_EFI_HTTP_STATUS_405_METHOD_NOT_ALLOWED, ++ GRUB_EFI_HTTP_STATUS_406_NOT_ACCEPTABLE, ++ GRUB_EFI_HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED, ++ GRUB_EFI_HTTP_STATUS_408_REQUEST_TIME_OUT, ++ GRUB_EFI_HTTP_STATUS_409_CONFLICT, ++ GRUB_EFI_HTTP_STATUS_410_GONE, ++ GRUB_EFI_HTTP_STATUS_411_LENGTH_REQUIRED, ++ GRUB_EFI_HTTP_STATUS_412_PRECONDITION_FAILED, ++ GRUB_EFI_HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE, ++ GRUB_EFI_HTTP_STATUS_414_REQUEST_URI_TOO_LARGE, ++ GRUB_EFI_HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE, ++ GRUB_EFI_HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED, ++ GRUB_EFI_HTTP_STATUS_417_EXPECTATION_FAILED, ++ GRUB_EFI_HTTP_STATUS_500_INTERNAL_SERVER_ERROR, ++ GRUB_EFI_HTTP_STATUS_501_NOT_IMPLEMENTED, ++ GRUB_EFI_HTTP_STATUS_502_BAD_GATEWAY, ++ GRUB_EFI_HTTP_STATUS_503_SERVICE_UNAVAILABLE, ++ GRUB_EFI_HTTP_STATUS_504_GATEWAY_TIME_OUT, ++ GRUB_EFI_HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED ++} grub_efi_http_status_code_t; ++ ++//****************************************** ++// EFI_HTTP_RESPONSE_DATA ++//****************************************** ++typedef struct { ++ grub_efi_http_status_code_t status_code; ++} grub_efi_http_response_data_t; ++ ++//****************************************** ++// EFI_HTTP_HEADER ++//****************************************** ++typedef struct { ++ grub_efi_char8_t *field_name; ++ grub_efi_char8_t *field_value; ++} grub_efi_http_header_t; ++ ++//****************************************** ++// EFI_HTTP_MESSAGE ++//****************************************** ++typedef struct { ++ union { ++ grub_efi_http_request_data_t *request; ++ grub_efi_http_response_data_t *response; ++ } data; ++ grub_efi_uint32_t header_count; ++ grub_efi_http_header_t *headers; ++ grub_efi_uint32_t body_length; ++ void *body; ++} grub_efi_http_message_t; ++ ++//****************************************** ++// EFI_HTTP_TOKEN ++//****************************************** ++typedef struct { ++ grub_efi_event_t event; ++ grub_efi_status_t status; ++ grub_efi_http_message_t *message; ++} grub_efi_http_token_t; ++ ++struct grub_efi_http { ++ grub_efi_status_t ++ (__grub_efi_api *get_mode_data) (struct grub_efi_http *this, ++ grub_efi_http_config_data_t *http_config_data); ++ ++ grub_efi_status_t ++ (__grub_efi_api *configure) (struct grub_efi_http *this, ++ grub_efi_http_config_data_t *http_config_data); ++ ++ grub_efi_status_t ++ (__grub_efi_api *request) (struct grub_efi_http *this, ++ grub_efi_http_token_t *token); ++ ++ grub_efi_status_t ++ (__grub_efi_api *cancel) (struct grub_efi_http *this, ++ grub_efi_http_token_t *token); ++ ++ grub_efi_status_t ++ (__grub_efi_api *response) (struct grub_efi_http *this, ++ grub_efi_http_token_t *token); ++ ++ grub_efi_status_t ++ (__grub_efi_api *poll) (struct grub_efi_http *this); ++}; ++typedef struct grub_efi_http grub_efi_http_t; ++ ++#endif /* !GRUB_EFI_HTTP_HEADER */ +--- /dev/null ++++ b/include/grub/net/efi.h +@@ -0,0 +1,144 @@ ++#ifndef GRUB_NET_EFI_HEADER ++#define GRUB_NET_EFI_HEADER 1 ++ ++#include ++#include ++#include ++#include ++ ++typedef struct grub_efi_net_interface grub_efi_net_interface_t; ++typedef struct grub_efi_net_ip_config grub_efi_net_ip_config_t; ++typedef union grub_efi_net_ip_address grub_efi_net_ip_address_t; ++typedef struct grub_efi_net_ip_manual_address grub_efi_net_ip_manual_address_t; ++ ++struct grub_efi_net_interface ++{ ++ char *name; ++ int prefer_ip6; ++ struct grub_efi_net_device *dev; ++ struct grub_efi_net_io *io; ++ grub_efi_net_ip_config_t *ip_config; ++ int io_type; ++ struct grub_efi_net_interface *next; ++}; ++ ++#define efi_net_interface_get_hw_address(inf) inf->ip_config->get_hw_address (inf->dev) ++#define efi_net_interface_get_address(inf) inf->ip_config->get_address (inf->dev) ++#define efi_net_interface_get_route_table(inf) inf->ip_config->get_route_table (inf->dev) ++#define efi_net_interface_set_address(inf, addr, with_subnet) inf->ip_config->set_address (inf->dev, addr, with_subnet) ++#define efi_net_interface_set_gateway(inf, addr) inf->ip_config->set_gateway (inf->dev, addr) ++#define efi_net_interface_set_dns(inf, addr) inf->ip_config->set_dns (inf->dev, addr) ++ ++struct grub_efi_net_ip_config ++{ ++ char * (*get_hw_address) (struct grub_efi_net_device *dev); ++ char * (*get_address) (struct grub_efi_net_device *dev); ++ char ** (*get_route_table) (struct grub_efi_net_device *dev); ++ grub_efi_net_interface_t * (*best_interface) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *address); ++ int (*set_address) (struct grub_efi_net_device *dev, grub_efi_net_ip_manual_address_t *net_ip, int with_subnet); ++ int (*set_gateway) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *address); ++ int (*set_dns) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *dns); ++}; ++ ++union grub_efi_net_ip_address ++{ ++ grub_efi_ipv4_address_t ip4; ++ grub_efi_ipv6_address_t ip6; ++}; ++ ++struct grub_efi_net_ip_manual_address ++{ ++ int is_ip6; ++ union ++ { ++ grub_efi_ip4_config2_manual_address_t ip4; ++ grub_efi_ip6_config_manual_address_t ip6; ++ }; ++}; ++ ++struct grub_efi_net_device ++{ ++ grub_efi_handle_t handle; ++ grub_efi_ip4_config2_protocol_t *ip4_config; ++ grub_efi_ip6_config_protocol_t *ip6_config; ++ grub_efi_handle_t http_handle; ++ grub_efi_http_t *http; ++ grub_efi_handle_t ip4_pxe_handle; ++ grub_efi_pxe_t *ip4_pxe; ++ grub_efi_handle_t ip6_pxe_handle; ++ grub_efi_pxe_t *ip6_pxe; ++ grub_efi_handle_t dhcp4_handle; ++ grub_efi_dhcp4_protocol_t *dhcp4; ++ grub_efi_handle_t dhcp6_handle; ++ grub_efi_dhcp6_protocol_t *dhcp6; ++ char *card_name; ++ grub_efi_net_interface_t *net_interfaces; ++ struct grub_efi_net_device *next; ++}; ++ ++struct grub_efi_net_io ++{ ++ void (*configure) (struct grub_efi_net_device *dev, int prefer_ip6); ++ grub_err_t (*open) (struct grub_efi_net_device *dev, ++ int prefer_ip6, ++ grub_file_t file, ++ const char *filename, ++ int type); ++ grub_ssize_t (*read) (struct grub_efi_net_device *dev, ++ int prefer_ip6, ++ grub_file_t file, ++ char *buf, ++ grub_size_t len); ++ grub_err_t (*close) (struct grub_efi_net_device *dev, ++ int prefer_ip6, ++ grub_file_t file); ++}; ++ ++extern struct grub_efi_net_device *net_devices; ++ ++extern struct grub_efi_net_io io_http; ++extern struct grub_efi_net_io io_pxe; ++ ++extern grub_efi_net_ip_config_t *efi_net_ip4_config; ++extern grub_efi_net_ip_config_t *efi_net_ip6_config; ++ ++char * ++grub_efi_ip4_address_to_string (grub_efi_ipv4_address_t *address); ++ ++char * ++grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address); ++ ++char * ++grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address); ++ ++int ++grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest); ++ ++int ++grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *address, const char **rest); ++ ++char * ++grub_efi_ip6_interface_name (struct grub_efi_net_device *dev); ++ ++char * ++grub_efi_ip4_interface_name (struct grub_efi_net_device *dev); ++ ++grub_efi_net_interface_t * ++grub_efi_net_create_interface (struct grub_efi_net_device *dev, ++ const char *interface_name, ++ grub_efi_net_ip_manual_address_t *net_ip, ++ int has_subnet); ++ ++int grub_efi_net_fs_init (void); ++void grub_efi_net_fs_fini (void); ++int grub_efi_net_boot_from_https (void); ++int grub_efi_net_boot_from_opa (void); ++ ++extern grub_command_func_t grub_efi_net_list_routes; ++extern grub_command_func_t grub_efi_net_list_cards; ++extern grub_command_func_t grub_efi_net_list_addrs; ++extern grub_command_func_t grub_efi_net_add_addr; ++extern grub_command_func_t grub_efi_net_bootp; ++extern grub_command_func_t grub_efi_net_bootp6; ++ ++#endif /* ! GRUB_NET_EFI_HEADER */ diff --git a/0001-arm64-Fix-EFI-loader-kernel-image-allocation.patch b/0001-arm64-Fix-EFI-loader-kernel-image-allocation.patch new file mode 100644 index 0000000..d7305ab --- /dev/null +++ b/0001-arm64-Fix-EFI-loader-kernel-image-allocation.patch @@ -0,0 +1,179 @@ +From 10d0f70ac194931c63f2cbd6fdebd6697abae992 Mon Sep 17 00:00:00 2001 +From: Benjamin Herrenschmidt +Date: Mon, 2 Aug 2021 23:10:01 +1000 +Subject: [PATCH 1/2] arm64: Fix EFI loader kernel image allocation + +We are currently allocating just enough memory for the file size, +which means that the kernel BSS is in limbo (and not even zeroed). + +We are also not honoring the alignment specified in the image +PE header. + +This makes us use the PE optional header in which the kernel puts the +actual size it needs, including BSS, and make sure we clear it, and +honors the specified alignment for the image. + +Signed-off-by: Benjamin Herrenschmidt +--- + grub-core/loader/arm64/efi/linux.c | 92 ++++++++++++++++++++---------- + 1 file changed, 63 insertions(+), 29 deletions(-) + +diff --git a/grub-core/loader/arm64/efi/linux.c b/grub-core/loader/arm64/efi/linux.c +index b73105347..4da49a182 100644 +--- a/grub-core/loader/arm64/efi/linux.c ++++ b/grub-core/loader/arm64/efi/linux.c +@@ -39,6 +39,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); + static grub_dl_t my_mod; + static int loaded; + ++static void *kernel_alloc_addr; ++static grub_uint32_t kernel_alloc_pages; + static void *kernel_addr; + static grub_uint64_t kernel_size; + static grub_uint32_t handover_offset; +@@ -258,9 +260,8 @@ grub_linux_unload (void) + GRUB_EFI_BYTES_TO_PAGES (initrd_end - initrd_start)); + initrd_start = initrd_end = 0; + grub_free (linux_args); +- if (kernel_addr) +- grub_efi_free_pages ((grub_addr_t) kernel_addr, +- GRUB_EFI_BYTES_TO_PAGES (kernel_size)); ++ if (kernel_alloc_addr) ++ grub_efi_free_pages ((grub_addr_t) kernel_alloc_addr, kernel_alloc_pages); + grub_fdt_unload (); + return GRUB_ERR_NONE; + } +@@ -365,14 +366,35 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + return grub_errno; + } + ++static grub_err_t ++parse_pe_header (void *kernel, grub_uint64_t *total_size, ++ grub_uint32_t *entry_offset, ++ grub_uint32_t *alignment) ++{ ++ struct linux_arch_kernel_header *lh = kernel; ++ struct grub_armxx_linux_pe_header *pe; ++ ++ pe = (void *)((unsigned long)kernel + lh->hdr_offset); ++ ++ if (pe->opt.magic != GRUB_PE32_PE64_MAGIC) ++ return grub_error(GRUB_ERR_BAD_OS, "Invalid PE optional header magic"); ++ ++ *total_size = pe->opt.image_size; ++ *entry_offset = pe->opt.entry_addr; ++ *alignment = pe->opt.section_alignment; ++ ++ return GRUB_ERR_NONE; ++} ++ + static grub_err_t + grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) + { + grub_file_t file = 0; +- struct linux_arch_kernel_header lh; +- struct grub_armxx_linux_pe_header *pe; + grub_err_t err; ++ grub_off_t filelen; ++ grub_uint32_t align = 0; ++ void *kernel = NULL; + + grub_dl_ref (my_mod); + +@@ -386,39 +408,49 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + if (!file) + goto fail; + +- kernel_size = grub_file_size (file); ++ filelen = grub_file_size (file); ++ kernel = grub_malloc(filelen); ++ if (!kernel) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel load buffer")); ++ goto fail; ++ } + +- if (grub_file_read (file, &lh, sizeof (lh)) < (long) sizeof (lh)) +- return grub_errno; ++ if (grub_file_read (file, kernel, filelen) < (grub_ssize_t)filelen) ++ { ++ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), ++ argv[0]); ++ goto fail; ++ } + +- if (grub_arch_efi_linux_check_image (&lh) != GRUB_ERR_NONE) ++ grub_dprintf ("linux", "kernel @ %p\n", kernel_addr); ++ ++ if (grub_arch_efi_linux_check_image (kernel) != GRUB_ERR_NONE) ++ goto fail; ++ if (parse_pe_header (kernel, &kernel_size, &handover_offset, &align) != GRUB_ERR_NONE) + goto fail; ++ grub_dprintf ("linux", "kernel mem size : %lld\n", (long long) kernel_size); ++ grub_dprintf ("linux", "kernel entry offset : %d\n", handover_offset); ++ grub_dprintf ("linux", "kernel alignment : 0x%x\n", align); + + grub_loader_unset(); + +- grub_dprintf ("linux", "kernel file size: %lld\n", (long long) kernel_size); +- kernel_addr = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (kernel_size)); +- grub_dprintf ("linux", "kernel numpages: %lld\n", +- (long long) GRUB_EFI_BYTES_TO_PAGES (kernel_size)); +- if (!kernel_addr) ++ kernel_alloc_pages = GRUB_EFI_BYTES_TO_PAGES (kernel_size + align - 1); ++ kernel_alloc_addr = grub_efi_allocate_any_pages (kernel_alloc_pages); ++ grub_dprintf ("linux", "kernel numpages: %d\n", kernel_alloc_pages); ++ if (!kernel_alloc_addr) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto fail; + } +- +- grub_file_seek (file, 0); +- if (grub_file_read (file, kernel_addr, kernel_size) +- < (grub_int64_t) kernel_size) +- { +- if (!grub_errno) +- grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]); +- goto fail; +- } ++ kernel_addr = (void *)ALIGN_UP((grub_uint64_t)kernel_alloc_addr, align); + + grub_dprintf ("linux", "kernel @ %p\n", kernel_addr); +- +- pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset); +- handover_offset = pe->opt.entry_addr; ++ grub_memcpy (kernel_addr, kernel, grub_min(filelen, kernel_size)); ++ if (kernel_size > filelen) ++ grub_memset ((char *)kernel_addr + filelen, 0, kernel_size - filelen); ++ grub_free(kernel); ++ kernel = NULL; + + cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE); + linux_args = grub_malloc (cmdline_size); +@@ -442,6 +474,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + } + + fail: ++ if (kernel) ++ grub_free (kernel); ++ + if (file) + grub_file_close (file); + +@@ -454,9 +489,8 @@ fail: + if (linux_args && !loaded) + grub_free (linux_args); + +- if (kernel_addr && !loaded) +- grub_efi_free_pages ((grub_addr_t) kernel_addr, +- GRUB_EFI_BYTES_TO_PAGES (kernel_size)); ++ if (kernel_alloc_addr && !loaded) ++ grub_efi_free_pages ((grub_addr_t) kernel_alloc_addr, kernel_alloc_pages); + + return grub_errno; + } +-- +2.31.1 + diff --git a/0001-clean-up-crypttab-and-linux-modules-dependency.patch b/0001-clean-up-crypttab-and-linux-modules-dependency.patch new file mode 100644 index 0000000..e1e1421 --- /dev/null +++ b/0001-clean-up-crypttab-and-linux-modules-dependency.patch @@ -0,0 +1,205 @@ +From e9422d6869f1b2d78a7cfbfcae1610953d87705b Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 16 Feb 2023 21:28:07 +0800 +Subject: [PATCH 1/2] clean up crypttab and linux modules dependency + +The linux module could have quite a few dependency to other modules, the +i386-pc build in particular has many. + + linux: normal vbe video boot cmdline relocator mmap + +That will be easy to cause loop dependency if one of these modules has +to require function from linux. To avoid falling into the pitfall in +future extension, we move away the key publish related function from +linux to crypttab module in that it is also a right thing to do. + +Signed-off-by: Michael Chang +--- + grub-core/commands/crypttab.c | 48 +++++++++++++++++++++++++++++- + grub-core/disk/cryptodisk.c | 2 +- + grub-core/loader/linux.c | 55 +---------------------------------- + include/grub/crypttab.h | 22 ++++++++++++++ + include/grub/linux.h | 3 -- + 5 files changed, 71 insertions(+), 59 deletions(-) + create mode 100644 include/grub/crypttab.h + +--- a/grub-core/commands/crypttab.c ++++ b/grub-core/commands/crypttab.c +@@ -3,10 +3,56 @@ + #include + #include + #include +-#include ++#include ++#include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + ++struct grub_key_publisher *kpuber; ++ ++grub_err_t ++grub_initrd_publish_key (const char *uuid, const char *key, grub_size_t key_len, const char *path) ++{ ++ struct grub_key_publisher *cur = NULL; ++ ++ FOR_LIST_ELEMENTS (cur, kpuber) ++ if (grub_uuidcasecmp (cur->name, uuid, sizeof (cur->name)) == 0) ++ break; ++ ++ if (!cur) ++ cur = grub_zalloc (sizeof (*cur)); ++ if (!cur) ++ return grub_errno; ++ ++ if (key && key_len) ++ { ++ grub_free (cur->key); ++ cur->key = grub_malloc (key_len); ++ if (!cur->key) ++ { ++ grub_free (cur); ++ return grub_errno; ++ } ++ grub_memcpy (cur->key, key, key_len); ++ cur->key_len = key_len; ++ } ++ ++ if (path) ++ { ++ grub_free (cur->path); ++ cur->path = grub_strdup (path); ++ } ++ ++ if (!cur->name) ++ { ++ cur->name = grub_strdup (uuid); ++ grub_list_push (GRUB_AS_LIST_P (&kpuber), GRUB_AS_LIST (cur)); ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ + static grub_err_t + grub_cmd_crypttab_entry (grub_command_t cmd __attribute__ ((unused)), + int argc, char **argv) +--- a/grub-core/disk/cryptodisk.c ++++ b/grub-core/disk/cryptodisk.c +@@ -31,7 +31,7 @@ + #ifdef GRUB_UTIL + #include + #else +-#include ++#include + #endif + + GRUB_MOD_LICENSE ("GPLv3+"); +--- a/grub-core/loader/linux.c ++++ b/grub-core/loader/linux.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + + struct newc_head + { +@@ -40,18 +41,6 @@ + struct dir *child; + }; + +-struct grub_key_publisher +-{ +- struct grub_key_publisher *next; +- struct grub_key_publisher **prev; +- char *name; /* UUID */ +- char *path; +- char *key; +- grub_size_t key_len; +-}; +- +-static struct grub_key_publisher *kpuber; +- + static char + hex (grub_uint8_t val) + { +@@ -436,45 +425,3 @@ + root = 0; + return GRUB_ERR_NONE; + } +- +-grub_err_t +-grub_initrd_publish_key (const char *uuid, const char *key, grub_size_t key_len, const char *path) +-{ +- struct grub_key_publisher *cur = NULL; +- +- FOR_LIST_ELEMENTS (cur, kpuber) +- if (grub_uuidcasecmp (cur->name, uuid, sizeof (cur->name)) == 0) +- break; +- +- if (!cur) +- cur = grub_zalloc (sizeof (*cur)); +- if (!cur) +- return grub_errno; +- +- if (key && key_len) +- { +- grub_free (cur->key); +- cur->key = grub_malloc (key_len); +- if (!cur->key) +- { +- grub_free (cur); +- return grub_errno; +- } +- grub_memcpy (cur->key, key, key_len); +- cur->key_len = key_len; +- } +- +- if (path) +- { +- grub_free (cur->path); +- cur->path = grub_strdup (path); +- } +- +- if (!cur->name) +- { +- cur->name = grub_strdup (uuid); +- grub_list_push (GRUB_AS_LIST_P (&kpuber), GRUB_AS_LIST (cur)); +- } +- +- return GRUB_ERR_NONE; +-} +--- /dev/null ++++ b/include/grub/crypttab.h +@@ -0,0 +1,22 @@ ++#ifndef GRUB_CRYPTTAB_HEADER ++#define GRUB_CRYPTTAB_HEADER 1 ++ ++#include ++#include ++ ++struct grub_key_publisher ++{ ++ struct grub_key_publisher *next; ++ struct grub_key_publisher **prev; ++ char *name; /* UUID */ ++ char *path; ++ char *key; ++ grub_size_t key_len; ++}; ++ ++extern struct grub_key_publisher *EXPORT_VAR (kpuber); ++ ++grub_err_t ++grub_initrd_publish_key (const char *uuid, const char *key, grub_size_t key_len, const char *path); ++ ++#endif /* ! GRUB_CRYPTTAB_HEADER */ +--- a/include/grub/linux.h ++++ b/include/grub/linux.h +@@ -22,6 +22,3 @@ + grub_err_t + grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx, + void *target); +- +-grub_err_t +-grub_initrd_publish_key (const char *uuid, const char *key, grub_size_t key_len, const char *path); diff --git a/0001-disk-cryptodisk-Fix-missing-change-when-updating-to-.patch b/0001-disk-cryptodisk-Fix-missing-change-when-updating-to-.patch new file mode 100644 index 0000000..d7493bf --- /dev/null +++ b/0001-disk-cryptodisk-Fix-missing-change-when-updating-to-.patch @@ -0,0 +1,35 @@ +From 652b221a5eacb1421891c1469608028e2c2f0615 Mon Sep 17 00:00:00 2001 +From: Glenn Washburn +Date: Fri, 18 Aug 2023 12:27:22 -0500 +Subject: [PATCH] disk/cryptodisk: Fix missing change when updating to use + grub_uuidcasecmp + +This was causing the cryptomount command to return failure even though +the crypto device was successfully added. Of course, this meant that any +script using the return code would behave unexpectedly. + +Fixes: 3cf2e848bc03 (disk/cryptodisk: Allows UUIDs to be compared in a dash-insensitive manner) + +Suggested-by: Olaf Hering +Signed-off-by: Glenn Washburn +--- + grub-core/disk/cryptodisk.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c +index 802b191b2..c79d4125a 100644 +--- a/grub-core/disk/cryptodisk.c ++++ b/grub-core/disk/cryptodisk.c +@@ -1323,7 +1323,8 @@ grub_cryptodisk_scan_device (const char *name, + dev = grub_cryptodisk_scan_device_real (name, source, cargs); + if (dev) + { +- ret = (cargs->search_uuid != NULL && grub_strcasecmp (cargs->search_uuid, dev->uuid) == 0); ++ ret = (cargs->search_uuid != NULL ++ && grub_uuidcasecmp (cargs->search_uuid, dev->uuid, sizeof (dev->uuid)) == 0); + goto cleanup; + } + +-- +2.41.0 + diff --git a/0001-efi-linux-provide-linux-command.patch b/0001-efi-linux-provide-linux-command.patch new file mode 100644 index 0000000..9ef7342 --- /dev/null +++ b/0001-efi-linux-provide-linux-command.patch @@ -0,0 +1,103 @@ +From 987ab0dfbe7ef42bb6386fb7b428d3b965ba6d2b Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Mon, 7 Sep 2020 17:02:57 +0800 +Subject: [PATCH] efi/linux: provide linux command + +The linux kernel's efi handover entry point is used to boot efistub of +the linux kernel. Since then the efistub has been improved with many new +features and fixes that ordinary 32-bit entry point cannot provide. + +Besides, nearly every x86 efi kernel is built with efistub enabled so it +is of little value to keep 32-bit entry as default to boot kernel +without needed kconfig options enabled. + +For all good reasons, making efi handover the default entry point for +booting kernel in x86 efi platform so that linux command works in the +same way to linuxefi. This can also reduce the complexity of providing +general grub configuation for x86 system due to the linux command may +not be available in signed image for UEFI Secure Boot and linuxefi is +not available for leagcy bios booting. + +Signed-off-by: Michael Chang +--- + grub-core/Makefile.core.def | 6 ++++-- + grub-core/gensyminfo.sh.in | 3 +++ + grub-core/loader/i386/efi/linux.c | 17 +++++++++++++---- + 3 files changed, 20 insertions(+), 6 deletions(-) + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -1840,7 +1840,9 @@ + + module = { + name = linux; +- x86 = loader/i386/linux.c; ++ i386_pc = loader/i386/linux.c; ++ i386_efi = loader/i386/efi/linux.c; ++ x86_64_efi = loader/i386/efi/linux.c; + i386_xen_pvh = loader/i386/linux.c; + xen = loader/i386/xen.c; + i386_pc = lib/i386/pc/vesa_modes_table.c; +@@ -1856,8 +1858,6 @@ + loongarch64 = loader/efi/linux.c; + riscv32 = loader/efi/linux.c; + riscv64 = loader/efi/linux.c; +- i386_efi = loader/efi/linux.c; +- x86_64_efi = loader/efi/linux.c; + emu = loader/emu/linux.c; + common = loader/linux.c; + }; +@@ -1922,7 +1922,7 @@ + + module = { + name = linuxefi; +- efi = loader/i386/efi/linux.c; ++ efi = lib/fake_module.c; + enable = i386_efi; + enable = x86_64_efi; + }; +--- a/grub-core/gensyminfo.sh.in ++++ b/grub-core/gensyminfo.sh.in +@@ -35,3 +35,6 @@ + + # Print all undefined symbols used by module + @TARGET_NM@ -u @TARGET_NMFLAGS_MINUS_P@ -p $module | sed "s@^\([^ ]*\).*@undefined $modname \1@g" ++ ++# Specify linuxefi module should load default linux ++test "$modname" = "linuxefi" && echo "undefined $modname grub_initrd_init" || true +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -333,20 +333,29 @@ + } + + static grub_command_t cmd_linux, cmd_initrd; ++static grub_command_t cmd_linuxefi, cmd_initrdefi; + +-GRUB_MOD_INIT(linuxefi) ++GRUB_MOD_INIT(linux) + { +- cmd_linux = ++ cmd_linuxefi = + grub_register_command ("linuxefi", grub_cmd_linux, + 0, N_("Load Linux.")); +- cmd_initrd = ++ cmd_initrdefi = + grub_register_command ("initrdefi", grub_cmd_initrd, + 0, N_("Load initrd.")); ++ cmd_linux = ++ grub_register_command ("linux", grub_cmd_linux, ++ 0, N_("Load Linux.")); ++ cmd_initrd = ++ grub_register_command ("initrd", grub_cmd_initrd, ++ 0, N_("Load initrd.")); + my_mod = mod; + } + +-GRUB_MOD_FINI(linuxefi) ++GRUB_MOD_FINI(linux) + { ++ grub_unregister_command (cmd_linuxefi); ++ grub_unregister_command (cmd_initrdefi); + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_initrd); + } diff --git a/0001-font-Try-memdisk-fonts-with-the-same-name.patch b/0001-font-Try-memdisk-fonts-with-the-same-name.patch new file mode 100644 index 0000000..7ca5b3f --- /dev/null +++ b/0001-font-Try-memdisk-fonts-with-the-same-name.patch @@ -0,0 +1,39 @@ +From d02304f70b5b9c79761d8084ab9dfc66d84688e2 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Wed, 30 Nov 2022 17:02:50 +0800 +Subject: [PATCH] font: Try memdisk fonts with the same name + +--- + grub-core/font/font.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/grub-core/font/font.c b/grub-core/font/font.c +index 18de52562..92ff415bf 100644 +--- a/grub-core/font/font.c ++++ b/grub-core/font/font.c +@@ -451,7 +451,21 @@ grub_font_load (const char *filename) + #endif + + if (filename[0] == '(' || filename[0] == '/' || filename[0] == '+') +- file = grub_buffile_open (filename, GRUB_FILE_TYPE_FONT, 1024); ++ { ++ char *n = grub_strdup (filename); ++ char *p = grub_strrchr (n, '/'); ++ if (p) ++ { ++ char *q = grub_strrchr (p, '.'); ++ if (q) ++ *q = 0; ++ p++; ++ file = try_open_from_prefix ("(memdisk)", p); ++ } ++ grub_free (n); ++ if (!file) ++ file = grub_buffile_open (filename, GRUB_FILE_TYPE_FONT, 1024); ++ } + else + { + file = try_open_from_prefix ("(memdisk)", filename); +-- +2.41.0 + diff --git a/0001-fs-btrfs-Zero-file-data-not-backed-by-extents.patch b/0001-fs-btrfs-Zero-file-data-not-backed-by-extents.patch new file mode 100644 index 0000000..bde2dec --- /dev/null +++ b/0001-fs-btrfs-Zero-file-data-not-backed-by-extents.patch @@ -0,0 +1,33 @@ +From f903b9a9adb64e733e581771d2a24efae7fbe529 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt +Date: Thu, 5 Oct 2023 11:02:25 +0200 +Subject: [PATCH] fs/btrfs: Zero file data not backed by extents + +Implicit holes in file data need to be zeroed explicitly, instead of +just leaving the data in the buffer uninitialized. + +This led to kernels randomly failing to boot in "fun" ways when loaded +from btrfs with the no_holes feature enabled, because large blocks of +zeros in the kernel file contained random data instead. + +Signed-off-by: Fabian Vogt +--- + grub-core/fs/btrfs.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c +index 19bff4610..ba0c58352 100644 +--- a/grub-core/fs/btrfs.c ++++ b/grub-core/fs/btrfs.c +@@ -1603,6 +1603,8 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, + csize = grub_le_to_cpu64 (key_out.offset) - pos; + if (csize > len) + csize = len; ++ ++ grub_memset (buf, 0, csize); + buf += csize; + pos += csize; + len -= csize; +-- +2.42.0 + diff --git a/0001-fs-ntfs-Fix-an-OOB-write-when-parsing-the-ATTRIBUTE_.patch b/0001-fs-ntfs-Fix-an-OOB-write-when-parsing-the-ATTRIBUTE_.patch new file mode 100644 index 0000000..55a48d4 --- /dev/null +++ b/0001-fs-ntfs-Fix-an-OOB-write-when-parsing-the-ATTRIBUTE_.patch @@ -0,0 +1,93 @@ +From 43651027d24e62a7a463254165e1e46e42aecdea Mon Sep 17 00:00:00 2001 +From: Maxim Suhanov +Date: Mon, 28 Aug 2023 16:31:57 +0300 +Subject: [PATCH 1/6] fs/ntfs: Fix an OOB write when parsing the + $ATTRIBUTE_LIST attribute for the $MFT file + +When parsing an extremely fragmented $MFT file, i.e., the file described +using the $ATTRIBUTE_LIST attribute, current NTFS code will reuse a buffer +containing bytes read from the underlying drive to store sector numbers, +which are consumed later to read data from these sectors into another buffer. + +These sectors numbers, two 32-bit integers, are always stored at predefined +offsets, 0x10 and 0x14, relative to first byte of the selected entry within +the $ATTRIBUTE_LIST attribute. Usually, this won't cause any problem. + +However, when parsing a specially-crafted file system image, this may cause +the NTFS code to write these integers beyond the buffer boundary, likely +causing the GRUB memory allocator to misbehave or fail. These integers contain +values which are controlled by on-disk structures of the NTFS file system. + +Such modification and resulting misbehavior may touch a memory range not +assigned to the GRUB and owned by firmware or another EFI application/driver. + +This fix introduces checks to ensure that these sector numbers are never +written beyond the boundary. + +Fixes: CVE-2023-4692 + +Reported-by: Maxim Suhanov +Signed-off-by: Maxim Suhanov +Reviewed-by: Daniel Kiper +--- + grub-core/fs/ntfs.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c +index bbdbe24ad..c3c4db117 100644 +--- a/grub-core/fs/ntfs.c ++++ b/grub-core/fs/ntfs.c +@@ -184,7 +184,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) + } + if (at->attr_end) + { +- grub_uint8_t *pa; ++ grub_uint8_t *pa, *pa_end; + + at->emft_buf = grub_malloc (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); + if (at->emft_buf == NULL) +@@ -209,11 +209,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) + } + at->attr_nxt = at->edat_buf; + at->attr_end = at->edat_buf + u32at (pa, 0x30); ++ pa_end = at->edat_buf + n; + } + else + { + at->attr_nxt = at->attr_end + u16at (pa, 0x14); + at->attr_end = at->attr_end + u32at (pa, 4); ++ pa_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); + } + at->flags |= GRUB_NTFS_AF_ALST; + while (at->attr_nxt < at->attr_end) +@@ -230,6 +232,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) + at->flags |= GRUB_NTFS_AF_GPOS; + at->attr_cur = at->attr_nxt; + pa = at->attr_cur; ++ ++ if ((pa >= pa_end) || (pa_end - pa < 0x18)) ++ { ++ grub_error (GRUB_ERR_BAD_FS, "can\'t parse attribute list"); ++ return NULL; ++ } ++ + grub_set_unaligned32 ((char *) pa + 0x10, + grub_cpu_to_le32 (at->mft->data->mft_start)); + grub_set_unaligned32 ((char *) pa + 0x14, +@@ -240,6 +249,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) + { + if (*pa != attr) + break; ++ ++ if ((pa >= pa_end) || (pa_end - pa < 0x18)) ++ { ++ grub_error (GRUB_ERR_BAD_FS, "can\'t parse attribute list"); ++ return NULL; ++ } ++ + if (read_attr + (at, pa + 0x10, + u32at (pa, 0x10) * (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR), +-- +2.42.0 + diff --git a/0001-fs-xfs-Incorrect-short-form-directory-data-boundary-.patch b/0001-fs-xfs-Incorrect-short-form-directory-data-boundary-.patch new file mode 100644 index 0000000..7fc6d53 --- /dev/null +++ b/0001-fs-xfs-Incorrect-short-form-directory-data-boundary-.patch @@ -0,0 +1,51 @@ +From b541e93b4dab6f652941d086af4fe2da676d0ee3 Mon Sep 17 00:00:00 2001 +From: Lidong Chen +Date: Thu, 28 Sep 2023 22:33:44 +0000 +Subject: [PATCH 1/3] fs/xfs: Incorrect short form directory data boundary + check + +After parsing of the current entry, the entry pointer is advanced +to the next entry at the end of the "for" loop. In case where the +last entry is at the end of the data boundary, the advanced entry +pointer can point off the data boundary. The subsequent boundary +check for the advanced entry pointer can cause a failure. + +The fix is to include the boundary check into the "for" loop +condition. + +Signed-off-by: Lidong Chen +Reviewed-by: Daniel Kiper +Tested-by: Sebastian Andrzej Siewior +Tested-by: Marta Lewandowska +--- + grub-core/fs/xfs.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c +index b91cd32b4..ebf962793 100644 +--- a/grub-core/fs/xfs.c ++++ b/grub-core/fs/xfs.c +@@ -810,7 +810,8 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, + if (iterate_dir_call_hook (parent, "..", &ctx)) + return 1; + +- for (i = 0; i < head->count; i++) ++ for (i = 0; i < head->count && ++ (grub_uint8_t *) de < ((grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)); i++) + { + grub_uint64_t ino; + grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de); +@@ -845,10 +846,6 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, + de->name[de->len] = c; + + de = grub_xfs_inline_next_de(dir->data, head, de); +- +- if ((grub_uint8_t *) de >= (grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)) +- return grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); +- + } + break; + } +-- +2.42.1 + diff --git a/0001-grub-install-Add-SUSE-signed-image-support-for-power.patch b/0001-grub-install-Add-SUSE-signed-image-support-for-power.patch new file mode 100644 index 0000000..366e08d --- /dev/null +++ b/0001-grub-install-Add-SUSE-signed-image-support-for-power.patch @@ -0,0 +1,101 @@ +From 83a6f72e1896bd012b7fbca21317e96c2c22b327 Mon Sep 17 00:00:00 2001 +From: Michal Suchanek +Date: Wed, 12 Jan 2022 19:25:54 +0100 +Subject: [PATCH] grub-install: Add SUSE signed image support for powerpc. + +Signed-off-by: Michal Suchanek +--- + grub-core/osdep/linux/platform.c | 13 +++++++++++++ + include/grub/util/install.h | 3 +++ + util/grub-install.c | 29 ++++++++++++++++++++++++++--- + 3 files changed, 42 insertions(+), 3 deletions(-) + +--- a/grub-core/osdep/linux/platform.c ++++ b/grub-core/osdep/linux/platform.c +@@ -154,3 +154,16 @@ + grub_util_info ("... not found"); + return "i386-pc"; + } ++ ++int ++grub_install_get_powerpc_secure_boot (void) ++{ ++ int32_t ret = -1; ++ FILE *fp = grub_util_fopen ("/proc/device-tree/ibm,secure-boot", "rb"); ++ if (fp) { ++ if (fread (&ret , 1, sizeof(ret), fp) > 0) ++ ret = grub_be_to_cpu32(ret); ++ fclose(fp); ++ } ++ return ret; ++} +--- a/include/grub/util/install.h ++++ b/include/grub/util/install.h +@@ -233,6 +233,9 @@ + grub_install_get_default_x86_platform (void); + + int ++grub_install_get_powerpc_secure_boot (void); ++ ++int + grub_install_register_efi (grub_device_t efidir_grub_dev, + const char *efifile_path, + const char *efi_distributor); +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -321,10 +321,10 @@ + {"suse-enable-tpm", OPTION_SUSE_ENABLE_TPM, 0, 0, N_("install TPM modules"), 0}, + {"suse-force-signed", OPTION_SUSE_FORCE_SIGNED, 0, 0, + N_("force installation of signed grub" "%s." +- "This option is only available on ARM64 EFI targets."), 0}, ++ "This option is only available on ARM64 EFI and powerpc targets."), 0}, + {"suse-inhibit-signed", OPTION_SUSE_INHIBIT_SIGNED, 0, 0, + N_("inhibit installation of signed grub. " +- "This option is only available on ARM64 EFI targets."), 0}, ++ "This option is only available on ARM64 EFI and powerpc targets."), 0}, + {"debug", OPTION_DEBUG, 0, OPTION_HIDDEN, 0, 2}, + {"no-floppy", OPTION_NO_FLOPPY, 0, OPTION_HIDDEN, 0, 2}, + {"debug-image", OPTION_DEBUG_IMAGE, N_("STRING"), OPTION_HIDDEN, 0, 2}, +@@ -1749,6 +1749,7 @@ + char mkimage_target[200]; + const char *core_name = NULL; + char *signed_imgfile = NULL; ++ int ppc_sb_state = -1; + + switch (platform) + { +@@ -1796,11 +1797,33 @@ + grub_install_get_platform_platform (platform)); + break; + ++ ++ case GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275: ++ ppc_sb_state = grub_install_get_powerpc_secure_boot(); ++ ++ if ((signed_grub_mode >= SIGNED_GRUB_FORCE) || ((signed_grub_mode == SIGNED_GRUB_AUTO) && (ppc_sb_state > 0))) ++ { ++ signed_imgfile = grub_util_path_concat (2, grub_install_source_directory, "grub.elf"); ++ if (!grub_util_is_regular (signed_imgfile)) ++ { ++ if ((signed_grub_mode >= SIGNED_GRUB_FORCE) || (ppc_sb_state > 1)) ++ grub_util_error ("signed image `%s' does not exist\n", signed_imgfile); ++ else ++ { ++ free (signed_imgfile); ++ signed_imgfile = NULL; ++ } ++ } ++ } ++ ++ if (signed_imgfile) ++ fprintf (stderr, _("Use signed file in %s for installation.\n"), signed_imgfile); ++ ++ /* fallthrough. */ + case GRUB_INSTALL_PLATFORM_I386_COREBOOT: + case GRUB_INSTALL_PLATFORM_ARM_COREBOOT: + case GRUB_INSTALL_PLATFORM_I386_MULTIBOOT: + case GRUB_INSTALL_PLATFORM_I386_IEEE1275: +- case GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275: + case GRUB_INSTALL_PLATFORM_I386_XEN: + case GRUB_INSTALL_PLATFORM_X86_64_XEN: + case GRUB_INSTALL_PLATFORM_I386_XEN_PVH: diff --git a/0001-grub-install-bailout-root-device-probing.patch b/0001-grub-install-bailout-root-device-probing.patch new file mode 100644 index 0000000..36ab2a3 --- /dev/null +++ b/0001-grub-install-bailout-root-device-probing.patch @@ -0,0 +1,163 @@ +From 58dcf7985b20de876a6fc44a591aa377d0a0302c Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 10 Feb 2022 22:16:58 +0800 +Subject: [PATCH] grub-install: bailout root device probing + +The root device is probed to test if the filesystem is btrfs in order to setup +boot configs for snapshot booting. However when the root device is a lvm thin +volume, due to lack in grub support, the probing will be errored out and entire +installation process aborts. + +Here we call out stat to bailout the situation whenever grub fails to probe +filesystem in it's own right. + + stat -f -c %T / + +The command is also used by grub-mkconfig for the same purpose. + +Signed-off-by: Michael Chang +--- + grub-core/osdep/basic/no_platform.c | 5 +++++ + grub-core/osdep/unix/platform.c | 34 +++++++++++++++++++++++++++++ + grub-core/osdep/windows/platform.c | 6 +++++ + include/grub/util/install.h | 3 +++ + util/grub-install.c | 31 ++++++++++++++++++-------- + 5 files changed, 70 insertions(+), 9 deletions(-) + +--- a/grub-core/osdep/basic/no_platform.c ++++ b/grub-core/osdep/basic/no_platform.c +@@ -51,3 +51,8 @@ + grub_util_error ("%s", _("no zIPL routines are available for your platform")); + } + ++char * ++grub_install_get_filesystem (const char *path) ++{ ++ return NULL; ++} +--- a/grub-core/osdep/unix/platform.c ++++ b/grub-core/osdep/unix/platform.c +@@ -250,3 +250,37 @@ + "-z", dest, NULL })) + grub_util_error (_("`%s' failed.\n"), PACKAGE"-zipl-setup"); + } ++ ++char * ++grub_install_get_filesystem (const char *path) ++{ ++ int fd; ++ pid_t pid; ++ FILE *fp; ++ ssize_t len; ++ char *buf = NULL; ++ size_t bufsz = 0; ++ ++ pid = grub_util_exec_pipe ((const char * []){ "stat", "-f", "-c", "%T", path, NULL }, &fd); ++ if (!pid) ++ return NULL; ++ ++ fp = fdopen (fd, "r"); ++ if (!fp) ++ return NULL; ++ ++ len = getline (&buf, &bufsz, fp); ++ if (len == -1) ++ { ++ free (buf); ++ fclose (fp); ++ return NULL; ++ } ++ ++ fclose (fp); ++ ++ if (len > 0 && buf[len - 1] == '\n') ++ buf[len - 1] = '\0'; ++ ++ return buf; ++} +--- a/grub-core/osdep/windows/platform.c ++++ b/grub-core/osdep/windows/platform.c +@@ -440,3 +440,9 @@ + { + grub_util_error ("%s", _("no zIPL routines are available for your platform")); + } ++ ++char * ++grub_install_get_filesystem (const char *path) ++{ ++ return NULL; ++} +--- a/include/grub/util/install.h ++++ b/include/grub/util/install.h +@@ -251,6 +251,9 @@ + void + grub_install_zipl (const char *d, int i, int f); + ++char * ++grub_install_get_filesystem (const char *path); ++ + int + grub_install_compress_gzip (const char *src, const char *dest); + int +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -887,7 +887,6 @@ + const char *efi_file = NULL; + char **grub_devices; + grub_fs_t grub_fs; +- grub_fs_t root_fs; + grub_device_t grub_dev = NULL; + enum grub_install_plat platform; + char *grubdir, *device_map; +@@ -1067,8 +1066,10 @@ + grub_host_init (); + + { +- char *rootdir_grub_devname; +- grub_device_t rootdir_grub_dev; ++ grub_device_t rootdir_grub_dev = NULL; ++ char *rootdir_grub_devname = NULL; ++ char *root_fs_name = NULL; ++ + char *t = grub_util_path_concat (2, "/", rootdir); + + rootdir_path = grub_canonicalize_file_name (t); +@@ -1089,20 +1090,32 @@ + rootdir_devices[0]); + + rootdir_grub_dev = grub_device_open (rootdir_grub_devname); +- if (! rootdir_grub_dev) +- grub_util_error ("%s", grub_errmsg); ++ if (!rootdir_grub_dev) ++ { ++ root_fs_name = grub_install_get_filesystem (t); ++ if (root_fs_name) ++ grub_errno = 0; ++ } ++ else ++ { ++ grub_fs_t root_fs = grub_fs_probe (rootdir_grub_dev); ++ if (root_fs) ++ root_fs_name = grub_strdup (root_fs->name); ++ } + +- root_fs = grub_fs_probe (rootdir_grub_dev); +- if (!root_fs) ++ if (!root_fs_name) + grub_util_error ("%s", grub_errmsg); + + if (config.is_suse_btrfs_snapshot_enabled +- && grub_strncmp(root_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0) ++ && root_fs_name ++ && grub_strncmp(root_fs_name, "btrfs", sizeof ("btrfs") - 1) == 0) + use_relative_path_on_btrfs = 1; + ++ free (root_fs_name); + free (t); + free (rootdir_grub_devname); +- grub_device_close (rootdir_grub_dev); ++ if (rootdir_grub_dev) ++ grub_device_close (rootdir_grub_dev); + } + + switch (platform) diff --git a/0001-grub-probe-Deduplicate-probed-partmap-output.patch b/0001-grub-probe-Deduplicate-probed-partmap-output.patch new file mode 100644 index 0000000..019fbe5 --- /dev/null +++ b/0001-grub-probe-Deduplicate-probed-partmap-output.patch @@ -0,0 +1,110 @@ +From ed0ac581ad3866197fc05c7cf48e39419a51f606 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Fri, 18 Mar 2022 13:19:33 +0800 +Subject: [PATCH] grub-probe: Deduplicate probed partmap output + +If the target device being probed is staked on top of other physical or logical +devices, all containing device's partition map type will be printed once if +--target=partmap is used. This usually results in duplicated output as same +partition map type. + +This in turn may clutter grub.cfg with many duplicated insmod part_[a-z]+ if +the /boot is RAIDed because --target=partmap output is used to producing +partmap modules required to access disk device. + +Let's deduplicate that to make the grub.cfg looks better and disciplined. + +Signed-off-by: Michael Chang +--- + util/grub-probe.c | 59 +++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 55 insertions(+), 4 deletions(-) + +diff --git a/util/grub-probe.c b/util/grub-probe.c +index c08e46bbb..fb94f28fd 100644 +--- a/util/grub-probe.c ++++ b/util/grub-probe.c +@@ -153,6 +153,50 @@ do_print (const char *x, void *data) + grub_printf ("%s%c", x, delim); + } + ++static int ++check_duplicate_partmap (const char *name) ++{ ++ static int alloc, used; ++ static char **partmaps; ++ int i; ++ ++ if (!name) ++ { ++ if (partmaps) ++ { ++ for (i= 0; i < used; ++i) ++ free (partmaps[i]); ++ free (partmaps); ++ partmaps = NULL; ++ alloc = 0; ++ used = 0; ++ } ++ return 1; ++ } ++ ++ for (i= 0; i < used; ++i) ++ if (strcmp (partmaps[i], name) == 0) ++ return 1; ++ ++ if (alloc <= used) ++ { ++ alloc = (alloc) ? (alloc << 1) : 4; ++ partmaps = xrealloc (partmaps, alloc * sizeof (*partmaps)); ++ } ++ ++ partmaps[used++] = strdup (name); ++ return 0; ++} ++ ++static void ++do_print_partmap (const char *x, void *data) ++{ ++ char delim = *(const char *) data; ++ if (check_duplicate_partmap (x) != 0) ++ return; ++ grub_printf ("%s%c", x, delim); ++} ++ + static void + probe_partmap (grub_disk_t disk, char delim) + { +@@ -165,10 +209,14 @@ probe_partmap (grub_disk_t disk, char delim) + } + + for (part = disk->partition; part; part = part->parent) +- printf ("%s%c", part->partmap->name, delim); ++ { ++ if (check_duplicate_partmap (part->partmap->name) != 0) ++ continue; ++ printf ("%s%c", part->partmap->name, delim); ++ } + + if (disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID) +- grub_diskfilter_get_partmap (disk, do_print, &delim); ++ grub_diskfilter_get_partmap (disk, do_print_partmap, &delim); + + /* In case of LVM/RAID, check the member devices as well. */ + if (disk->dev->disk_memberlist) +@@ -674,8 +722,11 @@ probe (const char *path, char **device_names, char delim) + probe_cryptodisk_uuid (dev->disk, delim); + + else if (print == PRINT_PARTMAP) +- /* Check if dev->disk itself is contained in a partmap. */ +- probe_partmap (dev->disk, delim); ++ { ++ /* Check if dev->disk itself is contained in a partmap. */ ++ probe_partmap (dev->disk, delim); ++ check_duplicate_partmap (NULL); ++ } + + else if (print == PRINT_PARTUUID) + { +-- +2.35.1 + diff --git a/0001-grub2-Can-t-setup-a-default-boot-device-correctly-on.patch b/0001-grub2-Can-t-setup-a-default-boot-device-correctly-on.patch new file mode 100644 index 0000000..7f40e4a --- /dev/null +++ b/0001-grub2-Can-t-setup-a-default-boot-device-correctly-on.patch @@ -0,0 +1,44 @@ +From a59b58f6ae327a8f6949991cb5531db01e1ba14d Mon Sep 17 00:00:00 2001 +From: Wen Xiong +Date: Tue, 7 Feb 2023 15:10:15 -0500 +Subject: [PATCH] grub2: Can't setup a default boot device correctly on nvme + device in Beta3 + +The patch in Bug 200486 - SUSE1205666 - SLES15SP5 Beta1: Setup multiple dev path + for a nvmf boot device in grub2 caused the issue. That patch didn't consider +nvme devices carefully. + +The new patch will check "nvme-of" instead of "nvme" to call +build_multi_boot_device(). + +Signed-off-by: Wen Xiong +--- + grub-core/osdep/unix/platform.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/grub-core/osdep/unix/platform.c b/grub-core/osdep/unix/platform.c +index db8fa4b95..fb47c0ffa 100644 +--- a/grub-core/osdep/unix/platform.c ++++ b/grub-core/osdep/unix/platform.c +@@ -288,11 +288,15 @@ grub_install_register_ieee1275 (int is_prep, const char *install_device, + } + *ptr = '\0'; + } +- else if (grub_strstr(install_device, "nvme")) +- boot_device = build_multi_boot_device(install_device); +- else ++ else { + boot_device = get_ofpathname (install_device); + ++ if (grub_strstr(boot_device, "nvme-of")) { ++ free (boot_device); ++ boot_device = build_multi_boot_device(install_device); ++ } ++ } ++ + if (grub_util_exec ((const char * []){ "nvsetenv", "boot-device", + boot_device, NULL })) + { +-- +2.39.1 + diff --git a/0001-grub2-Set-multiple-device-path-for-a-nvmf-boot-devic.patch b/0001-grub2-Set-multiple-device-path-for-a-nvmf-boot-devic.patch new file mode 100644 index 0000000..46f096a --- /dev/null +++ b/0001-grub2-Set-multiple-device-path-for-a-nvmf-boot-devic.patch @@ -0,0 +1,164 @@ +From 3e77c5494fd06f430588ae9c304fea370439d531 Mon Sep 17 00:00:00 2001 +From: Wen Xiong +Date: Thu, 15 Dec 2022 21:33:41 -0500 +Subject: [PATCH] grub2: Set multiple device path for a nvmf boot device + +nvmf support native multipath(ANA) by default. +The patch added the support for setting multiple +device path for a nvmf boot device. + +localhost:~ grub2-install -v /dev/nvme1n1p1 +... +... +... +grub2-install: info: executing nvsetenv boot-device /pci@800000020000132/fibre-channel@0,1/nvme-of/controller@5005076810193675,ffff:nqn=nqn.1986-03.com.ibm:nvme:2145.0000020420006CEA/namespace@ec /pci@800000020000132/fibre-channel@0/nvme-of/controller@5005076810193675,ffff:nqn=nqn.1986-03.com.ibm:nvme:2145.0000020420006CEA/namespace@ec /pci@800000020000132/fibre-channel@0/nvme-of/controller@50050768101935e5,ffff:nqn=nqn.1986-03.com.ibm:nvme:2145.0000020420006CEA/namespace@ec /pci@800000020000132/fibre-channel@0,1/nvme-of/controller@50050768101935e5,ffff:nqn=nqn.1986-03.com.ibm:nvme:2145.0000020420006CEA/namespace@ec. +Installation finished. No error reported. + +localhost:~ # bootlist -m normal -o +nvme7n1 +nvme5n1 +nvme1n1 +nvme4n1 + +localhost:~ # bootlist -m normal -r +/pci@800000020000132/fibre-channel@0,1/nvme-of/controller@5005076810193675,ffff:nqn=nqn.1986-03.com.ibm:nvme:2145.0000020420006CEA/namespace@ec +/pci@800000020000132/fibre-channel@0/nvme-of/controller@5005076810193675,ffff:nqn=nqn.1986-03.com.ibm:nvme:2145.0000020420006CEA/namespace@ec +/pci@800000020000132/fibre-channel@0/nvme-of/controller@50050768101935e5,ffff:nqn=nqn.1986-03.com.ibm:nvme:2145.0000020420006CEA/namespace@ec +/pci@800000020000132/fibre-channel@0,1/nvme-of/controller@50050768101935e5,ffff:nqn=nqn.1986-03.com.ibm:nvme:2145.0000020420006CEA/namespace@ec + +Signed-off-by: Wen Xiong +--- + grub-core/osdep/linux/ofpath.c | 6 ++--- + grub-core/osdep/unix/platform.c | 48 +++++++++++++++++++++++++++++++++ + include/grub/util/install.h | 3 +++ + include/grub/util/ofpath.h | 9 +++++++ + 4 files changed, 63 insertions(+), 3 deletions(-) + +--- a/grub-core/osdep/linux/ofpath.c ++++ b/grub-core/osdep/linux/ofpath.c +@@ -209,7 +209,7 @@ + } + } + +-static char * ++char * + xrealpath (const char *in) + { + char *out; +@@ -224,7 +224,7 @@ + return out; + } + +-static char * ++char * + block_device_get_sysfs_path_and_link(const char *devicenode) + { + char *rpath; +@@ -535,7 +535,7 @@ + + } + +-static char * ++char * + nvme_get_syspath(const char *nvmedev) + { + char *sysfs_path, *controller_node; +--- a/grub-core/osdep/unix/platform.c ++++ b/grub-core/osdep/unix/platform.c +@@ -19,6 +19,7 @@ + #include + + #include ++#include + #include + #include + #include +@@ -131,6 +132,51 @@ + return rc; + } + ++char * ++build_multi_boot_device(const char *install_device) ++{ ++ char *sysfs_path; ++ char *nvme_ns; ++ unsigned int nsid; ++ char *ptr; ++ char *boot_device_string; ++ struct dirent *ep; ++ DIR *dp; ++ ++ nvme_ns = strchr(install_device, 'n'); ++ nsid = of_path_get_nvme_nsid(nvme_ns); ++ sysfs_path = nvme_get_syspath(nvme_ns); ++ strcat(sysfs_path, "/device"); ++ sysfs_path = xrealpath(sysfs_path); ++ ++ dp = opendir(sysfs_path); ++ ptr = boot_device_string = xmalloc (1000); ++ ++ /* We cannot have a boot list with more than five entries */ ++ while((ep = readdir(dp)) != NULL){ ++ char *nvme_device; ++ ++ if (grub_strstr(ep->d_name, "nvme")) { ++ nvme_device = xasprintf ("%s%s%x ", ++ get_ofpathname(ep->d_name),"/namespace@", nsid); ++ if ((strlen(boot_device_string) + strlen(nvme_device)) >= 200*5 - 1) { ++ grub_util_warn (_("More than five entries cannot be specified in the bootlist")); ++ free(nvme_device); ++ break; ++ } ++ ++ strncpy(ptr, nvme_device, strlen(nvme_device)); ++ ptr += strlen(nvme_device); ++ free(nvme_device); ++ } ++ } ++ ++ *--ptr = '\0'; ++ closedir(dp); ++ ++ return boot_device_string; ++} ++ + int + grub_install_register_efi (const grub_disk_t *efidir_grub_disk, + const char *efifile_path, +@@ -242,6 +288,8 @@ + } + *ptr = '\0'; + } ++ else if (grub_strstr(install_device, "nvme")) ++ boot_device = build_multi_boot_device(install_device); + else + boot_device = get_ofpathname (install_device); + +--- a/include/grub/util/install.h ++++ b/include/grub/util/install.h +@@ -241,6 +241,9 @@ + const char *efi_distributor, + const char *force_disk); + ++char * ++build_multi_boot_device(const char *install_device); ++ + void + grub_install_register_ieee1275 (int is_prep, const char *install_device, + int partno, const char *relpath); +--- a/include/grub/util/ofpath.h ++++ b/include/grub/util/ofpath.h +@@ -32,4 +32,13 @@ + + char* of_find_fc_host(char* host_wwpn); + ++char* nvme_get_syspath(const char *nvmedev); ++ ++char* block_device_get_sysfs_path_and_link(const char *devicenode); ++ ++char* xrealpath (const char *in); ++ ++unsigned int of_path_get_nvme_nsid(const char* devname); ++ ++ + #endif /* ! GRUB_OFPATH_MACHINE_UTIL_HEADER */ diff --git a/0001-ieee1275-Avoiding-many-unecessary-open-close.patch b/0001-ieee1275-Avoiding-many-unecessary-open-close.patch new file mode 100644 index 0000000..68364aa --- /dev/null +++ b/0001-ieee1275-Avoiding-many-unecessary-open-close.patch @@ -0,0 +1,142 @@ +From e9d3202d5cffb89223ff61ac93de86a0cac1b50c Mon Sep 17 00:00:00 2001 +From: Diego Domingos +Date: Thu, 19 Nov 2020 10:47:25 -0300 +Subject: [PATCH] ieee1275: Avoiding many unecessary open/close + +This patch aims to change the grub_ofdisk_open and grub_ofdisk_close behaviors. Since some devices (Fibre Channel and NVMe) can have a long time for shutdown notification, we should avoid open and close the disks as much as we can. + +So, we are changing how those functions works. The grub_ofdisk_close will take care of just changing the disk element status, by doing a soft close, i.e, the firmware will not be called. On the other hand, the grub_ofdisk_open will take care of closing the current disk opened only if the disk requested in the current call is different from the current one. This close will be responsible to request the firmware to actually close the disk. + +Yet, this patch modifies the grub_ofdisk_get_block_size function, avoiding open and close calls inside of it. + +Thank you Michael Chang (mchang@suse.com) for all support. + +Signed-off-by: Diego Domingos +--- + grub-core/disk/ieee1275/ofdisk.c | 64 +++++++++++++++++--------------- + 1 file changed, 35 insertions(+), 29 deletions(-) + +--- a/grub-core/disk/ieee1275/ofdisk.c ++++ b/grub-core/disk/ieee1275/ofdisk.c +@@ -44,7 +44,7 @@ + }; + + static grub_err_t +-grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size, ++grub_ofdisk_get_block_size (grub_uint32_t *block_size, + struct ofdisk_hash_ent *op); + + #define OFDISK_HASH_SZ 8 +@@ -461,6 +461,7 @@ + grub_ssize_t actual; + grub_uint32_t block_size = 0; + grub_err_t err; ++ struct ofdisk_hash_ent *op; + + if (grub_strncmp (name, "ieee1275/", sizeof ("ieee1275/") - 1) != 0) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, +@@ -471,6 +472,35 @@ + + grub_dprintf ("disk", "Opening `%s'.\n", devpath); + ++ op = ofdisk_hash_find (devpath); ++ if (!op) ++ op = ofdisk_hash_add (devpath, NULL); ++ if (!op) ++ { ++ grub_free (devpath); ++ return grub_errno; ++ } ++ ++ /* Check if the call to open is the same to the last disk already opened */ ++ if (last_devpath && !grub_strcmp(op->open_path,last_devpath)) ++ { ++ goto finish; ++ } ++ ++ /* If not, we need to close the previous disk and open the new one */ ++ else { ++ if (last_ihandle){ ++ grub_ieee1275_close (last_ihandle); ++ } ++ last_ihandle = 0; ++ last_devpath = NULL; ++ ++ grub_ieee1275_open (op->open_path, &last_ihandle); ++ if (! last_ihandle) ++ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); ++ last_devpath = op->open_path; ++ } ++ + if (grub_ieee1275_finddevice (devpath, &dev)) + { + grub_free (devpath); +@@ -491,25 +521,18 @@ + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a block device"); + } + ++ ++ finish: + /* XXX: There is no property to read the number of blocks. There + should be a property `#blocks', but it is not there. Perhaps it + is possible to use seek for this. */ + disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN; + + { +- struct ofdisk_hash_ent *op; +- op = ofdisk_hash_find (devpath); +- if (!op) +- op = ofdisk_hash_add (devpath, NULL); +- if (!op) +- { +- grub_free (devpath); +- return grub_errno; +- } + disk->id = (unsigned long) op; + disk->data = op->open_path; + +- err = grub_ofdisk_get_block_size (devpath, &block_size, op); ++ err = grub_ofdisk_get_block_size (&block_size, op); + if (err) + { + grub_free (devpath); +@@ -528,13 +551,6 @@ + static void + grub_ofdisk_close (grub_disk_t disk) + { +- if (disk->data == last_devpath) +- { +- if (last_ihandle) +- grub_ieee1275_close (last_ihandle); +- last_ihandle = 0; +- last_devpath = NULL; +- } + disk->data = 0; + } + +@@ -681,7 +697,7 @@ + } + + static grub_err_t +-grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size, ++grub_ofdisk_get_block_size (grub_uint32_t *block_size, + struct ofdisk_hash_ent *op) + { + struct size_args_ieee1275 +@@ -694,16 +710,6 @@ + grub_ieee1275_cell_t size2; + } args_ieee1275; + +- if (last_ihandle) +- grub_ieee1275_close (last_ihandle); +- +- last_ihandle = 0; +- last_devpath = NULL; +- +- grub_ieee1275_open (device, &last_ihandle); +- if (! last_ihandle) +- return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); +- + *block_size = 0; + + if (op->block_size_fails >= 2) diff --git a/0001-ieee1275-add-support-for-NVMeoFC.patch b/0001-ieee1275-add-support-for-NVMeoFC.patch new file mode 100644 index 0000000..f9cbc98 --- /dev/null +++ b/0001-ieee1275-add-support-for-NVMeoFC.patch @@ -0,0 +1,250 @@ +From c125cb45a7885d7bf168a05cfa4da3e681244649 Mon Sep 17 00:00:00 2001 +From: Diego Domingos +Date: Tue, 15 Feb 2022 13:11:48 -0500 +Subject: [PATCH 1/4] ieee1275: add support for NVMeoFC + +Implements the functions to scan and discovery of NVMeoFC. +--- + grub-core/disk/ieee1275/ofdisk.c | 217 ++++++++++++++++++++++++++++++- + 1 file changed, 213 insertions(+), 4 deletions(-) + +diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c +index 410f4b849..852bb95be 100644 +--- a/grub-core/disk/ieee1275/ofdisk.c ++++ b/grub-core/disk/ieee1275/ofdisk.c +@@ -206,12 +206,10 @@ dev_iterate_real (const char *name, const char *path) + return; + } + ++ + static void +-dev_iterate (const struct grub_ieee1275_devalias *alias) ++dev_iterate_fcp_disks(const struct grub_ieee1275_devalias *alias) + { +- if (grub_strcmp (alias->type, "fcp") == 0) +- { +- + /* If we are dealing with fcp devices, we need + * to find the WWPNs and LUNs to iterate them */ + grub_ieee1275_ihandle_t ihandle; +@@ -323,6 +321,217 @@ dev_iterate (const struct grub_ieee1275_devalias *alias) + grub_free (buf); + return; + ++} ++ ++static void ++dev_iterate_fcp_nvmeof (const struct grub_ieee1275_devalias *alias) ++{ ++ ++ ++ char *bufptr; ++ grub_ieee1275_ihandle_t ihandle; ++ ++ ++ // Create the structs for the parameters passing to PFW ++ struct nvme_args_ ++ { ++ struct grub_ieee1275_common_hdr common; ++ grub_ieee1275_cell_t method; ++ grub_ieee1275_cell_t ihandle; ++ grub_ieee1275_cell_t catch_result; ++ grub_ieee1275_cell_t nentries; ++ grub_ieee1275_cell_t table; ++ } nvme_discovery_controllers_args, nvme_controllers_args, nvme_namespaces_args; ++ ++ ++ // Create the structs for the results from PFW ++ ++ struct discovery_controllers_table_struct_ ++ { ++ grub_uint64_t table[256]; ++ grub_uint32_t len; ++ } discovery_controllers_table; ++ ++ /* struct nvme_controllers_table_entry ++ * this the return of nvme-controllers method tables, containing: ++ * - 2-byte controller ID ++ * - 256-byte transport address string ++ * - 256-byte field containing null-terminated NVM subsystem NQN string up to 223 characters ++ */ ++ struct nvme_controllers_table_entry_ ++ { ++ grub_uint16_t id; ++ char wwpn[256]; ++ char nqn[256]; ++ }; ++ ++ struct nvme_controllers_table_entry_* nvme_controllers_table = grub_malloc(sizeof(struct nvme_controllers_table_entry_)*256); ++ ++ grub_uint32_t nvme_controllers_table_entries; ++ ++ struct nvme_controllers_table_entry_real ++ { ++ grub_uint16_t id; ++ char wwpn[256]; ++ char nqn[256]; ++ }; ++ ++ /* Allocate memory for building the NVMeoF path */ ++ char *buf = grub_malloc (grub_strlen (alias->path) + 512); ++ if (!buf) ++ { ++ grub_ieee1275_close(ihandle); ++ return; ++ } ++ ++ /* Copy the alias->path to buf so we can work with */ ++ bufptr = grub_stpcpy (buf, alias->path); ++ grub_snprintf (bufptr, 32, "/nvme-of"); ++ ++ /* ++ * Open the nvme-of layer ++ * Ex. /pci@bus/fibre-channel@@dev,func/nvme-of ++ */ ++ if(grub_ieee1275_open (buf, &ihandle)) ++ { ++ grub_dprintf("disk", "failed to open the disk while iterating FCP disk path=%s\n", buf); ++ return; ++ } ++ ++ /* ++ * Call to nvme-discovery-controllers method from the nvme-of layer ++ * to get a list of the NVMe discovery controllers per the binding ++ */ ++ ++ INIT_IEEE1275_COMMON (&nvme_discovery_controllers_args.common, "call-method", 2, 2); ++ nvme_discovery_controllers_args.method = (grub_ieee1275_cell_t) "nvme-discovery-controllers"; ++ nvme_discovery_controllers_args.ihandle = ihandle; ++ ++ if (IEEE1275_CALL_ENTRY_FN (&nvme_discovery_controllers_args) == -1) ++ { ++ grub_dprintf("disk", "failed to get the targets while iterating FCP disk path=%s\n", buf); ++ grub_ieee1275_close(ihandle); ++ return; ++ } ++ ++ /* After closing the device, the info is lost. So lets copy each buffer in the buffers table */ ++ ++ discovery_controllers_table.len = (grub_uint32_t) nvme_discovery_controllers_args.nentries; ++ ++ unsigned int i=0; ++ for(i = 0; i < discovery_controllers_table.len; i++){ ++ discovery_controllers_table.table[i] = ((grub_uint64_t*)nvme_discovery_controllers_args.table)[i]; ++ } ++ ++ grub_ieee1275_close(ihandle); ++ ++ grub_dprintf("ofdisk","NVMeoF: Found %d discovery controllers\n",discovery_controllers_table.len); ++ ++ /* For each nvme discovery controller */ ++ int current_buffer_index; ++ for(current_buffer_index = 0; current_buffer_index < (int) discovery_controllers_table.len; current_buffer_index++){ ++ ++ ++ grub_snprintf (bufptr, 64, "/nvme-of/controller@%" PRIxGRUB_UINT64_T ",ffff", ++ discovery_controllers_table.table[current_buffer_index]); ++ ++ grub_dprintf("ofdisk","nvmeof controller=%s\n",buf); ++ ++ if(grub_ieee1275_open (buf, &ihandle)) ++ { ++ grub_dprintf("ofdisk", "failed to open the disk while getting nvme-controllers path=%s\n", buf); ++ continue; ++ } ++ ++ ++ INIT_IEEE1275_COMMON (&nvme_controllers_args.common, "call-method", 2, 2); ++ nvme_controllers_args.method = (grub_ieee1275_cell_t) "nvme-controllers"; ++ nvme_controllers_args.ihandle = ihandle; ++ nvme_controllers_args.catch_result = 0; ++ ++ ++ if (IEEE1275_CALL_ENTRY_FN (&nvme_controllers_args) == -1) ++ { ++ grub_dprintf("ofdisk", "failed to get the nvme-controllers while iterating FCP disk path\n"); ++ grub_ieee1275_close(ihandle); ++ continue; ++ } ++ ++ ++ /* Copy the buffer list to nvme_controllers_table */ ++ nvme_controllers_table_entries = ((grub_uint32_t) nvme_controllers_args.nentries); ++ struct nvme_controllers_table_entry_* nvme_controllers_table_ = (struct nvme_controllers_table_entry_*) nvme_controllers_args.table; ++ ++ for(i = 0; i < nvme_controllers_table_entries; i++){ ++ nvme_controllers_table[i].id = (grub_uint16_t) nvme_controllers_table_[i].id; ++ grub_strcpy(nvme_controllers_table[i].wwpn, nvme_controllers_table_[i].wwpn); ++ grub_strcpy(nvme_controllers_table[i].nqn, nvme_controllers_table_[i].nqn); ++ } ++ ++ grub_ieee1275_close(ihandle); ++ ++ int nvme_controller_index; ++ int bufptr_pos2; ++ grub_dprintf("ofdisk","NVMeoF: found %d nvme controllers\n",(int) nvme_controllers_args.nentries); ++ ++ /* For each nvme controller */ ++ for(nvme_controller_index = 0; nvme_controller_index < (int) nvme_controllers_args.nentries; nvme_controller_index++){ ++ /* Open the nvme controller ++ * /pci@bus/fibre-channel@dev,func/nvme-of/controller@transport-addr,ctlr-id:nqn=tgt-subsystem-nqn ++ */ ++ ++ bufptr_pos2 = grub_snprintf (bufptr, 512, "/nvme-of/controller@%s,ffff:nqn=%s", ++ nvme_controllers_table[nvme_controller_index].wwpn, nvme_controllers_table[nvme_controller_index].nqn); ++ ++ grub_dprintf("ofdisk","NVMeoF: nvmeof controller=%s\n",buf); ++ ++ if(grub_ieee1275_open (buf, &ihandle)){ ++ grub_dprintf("ofdisk","failed to open the path=%s\n",buf); ++ continue; ++ } ++ ++ INIT_IEEE1275_COMMON (&nvme_namespaces_args.common, "call-method", 2, 2); ++ nvme_namespaces_args.method = (grub_ieee1275_cell_t) "get-namespace-list"; ++ nvme_namespaces_args.ihandle = ihandle; ++ nvme_namespaces_args.catch_result = 0; ++ ++ if (IEEE1275_CALL_ENTRY_FN (&nvme_namespaces_args) == -1) ++ { ++ grub_dprintf("ofdisk", "failed to get the nvme-namespace-list while iterating FCP disk path\n"); ++ grub_ieee1275_close(ihandle); ++ continue; ++ } ++ ++ grub_uint32_t *namespaces = (grub_uint32_t*) nvme_namespaces_args.table; ++ grub_dprintf("ofdisk","NVMeoF: found %d namespaces\n",(int)nvme_namespaces_args.nentries); ++ ++ grub_ieee1275_close(ihandle); ++ ++ grub_uint32_t namespace_index = 0; ++ for(namespace_index=0; namespace_index < nvme_namespaces_args.nentries; namespace_index++){ ++ grub_snprintf (bufptr+bufptr_pos2, 512, "/namespace@%"PRIxGRUB_UINT32_T,namespaces[namespace_index]); ++ grub_dprintf("ofdisk","NVMeoF: namespace=%s\n",buf); ++ dev_iterate_real(buf,buf); ++ } ++ ++ dev_iterate_real(buf,buf); ++ } ++ } ++ grub_free(buf); ++ return; ++} ++ ++static void ++dev_iterate (const struct grub_ieee1275_devalias *alias) ++{ ++ if (grub_strcmp (alias->type, "fcp") == 0) ++ { ++ // Iterate disks ++ dev_iterate_fcp_disks(alias); ++ ++ // Iterate NVMeoF ++ dev_iterate_fcp_nvmeof(alias); ++ + } + else if (grub_strcmp (alias->type, "vscsi") == 0) + { +-- +2.35.3 + diff --git a/0001-ieee1275-implement-FCP-methods-for-WWPN-and-LUNs.patch b/0001-ieee1275-implement-FCP-methods-for-WWPN-and-LUNs.patch new file mode 100644 index 0000000..2c9be5b --- /dev/null +++ b/0001-ieee1275-implement-FCP-methods-for-WWPN-and-LUNs.patch @@ -0,0 +1,146 @@ +From a37d0cc089edd66ab35f1a27b0da09dd2f02deb3 Mon Sep 17 00:00:00 2001 +From: Diego Domingos +Date: Mon, 24 Jun 2019 10:15:56 -0400 +Subject: [PATCH] ieee1275: implement FCP methods for WWPN and LUNs + +This patch enables the fcp-targets and fcp-luns methods which are +responsible to get WWPNs and LUNs for fibre channel devices. + +Those methods are specially necessary if the boot directory and grub +installation are in different FCP disks, allowing the dev_iterate() +to find the WWPNs and LUNs when called by searchfs.uuid tool. +--- + grub-core/disk/ieee1275/ofdisk.c | 117 ++++++++++++++++++++++++++++++- + 1 file changed, 116 insertions(+), 1 deletion(-) + +diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c +index ea7f78ac7..258a6e389 100644 +--- a/grub-core/disk/ieee1275/ofdisk.c ++++ b/grub-core/disk/ieee1275/ofdisk.c +@@ -209,7 +209,122 @@ dev_iterate_real (const char *name, const char *path) + static void + dev_iterate (const struct grub_ieee1275_devalias *alias) + { +- if (grub_strcmp (alias->type, "vscsi") == 0) ++ if (grub_strcmp (alias->type, "fcp") == 0) ++ { ++ ++ /* If we are dealing with fcp devices, we need ++ * to find the WWPNs and LUNs to iterate them */ ++ grub_ieee1275_ihandle_t ihandle; ++ grub_uint64_t *ptr_targets, *ptr_luns, k, l; ++ unsigned int i, j, pos; ++ char *buf, *bufptr; ++ ++ struct set_fcp_targets_args ++ { ++ struct grub_ieee1275_common_hdr common; ++ grub_ieee1275_cell_t method; ++ grub_ieee1275_cell_t ihandle; ++ grub_ieee1275_cell_t catch_result; ++ grub_ieee1275_cell_t nentries; ++ grub_ieee1275_cell_t table; ++ } args_targets; ++ ++ struct set_fcp_luns_args ++ { ++ struct grub_ieee1275_common_hdr common; ++ grub_ieee1275_cell_t method; ++ grub_ieee1275_cell_t ihandle; ++ grub_ieee1275_cell_t wwpn_h; ++ grub_ieee1275_cell_t wwpn_l; ++ grub_ieee1275_cell_t catch_result; ++ grub_ieee1275_cell_t nentries; ++ grub_ieee1275_cell_t table; ++ } args_luns; ++ ++ struct args_ret ++ { ++ grub_uint64_t addr; ++ grub_uint64_t len; ++ }; ++ ++ if(grub_ieee1275_open (alias->path, &ihandle)) ++ { ++ grub_dprintf("disk", "failed to open the disk while iterating FCP disk path=%s\n", alias->path); ++ return; ++ } ++ ++ /* Setup the fcp-targets method to call via pfw*/ ++ INIT_IEEE1275_COMMON (&args_targets.common, "call-method", 2, 3); ++ args_targets.method = (grub_ieee1275_cell_t) "fcp-targets"; ++ args_targets.ihandle = ihandle; ++ ++ /* Setup the fcp-luns method to call via pfw */ ++ INIT_IEEE1275_COMMON (&args_luns.common, "call-method", 4, 3); ++ args_luns.method = (grub_ieee1275_cell_t) "fcp-luns"; ++ args_luns.ihandle = ihandle; ++ ++ if (IEEE1275_CALL_ENTRY_FN (&args_targets) == -1) ++ { ++ grub_dprintf("disk", "failed to get the targets while iterating FCP disk path=%s\n", alias->path); ++ grub_ieee1275_close(ihandle); ++ return; ++ } ++ ++ buf = grub_malloc (grub_strlen (alias->path) + 32 + 32); ++ ++ if (!buf) ++ { ++ grub_ieee1275_close(ihandle); ++ return; ++ } ++ ++ bufptr = grub_stpcpy (buf, alias->path); ++ ++ /* Iterate over entries returned by pfw. Each entry contains a ++ * pointer to wwpn table and his length. */ ++ struct args_ret *targets_table = (struct args_ret *)(args_targets.table); ++ for (i=0; i< args_targets.nentries; i++) ++ { ++ ptr_targets = (grub_uint64_t*)(grub_uint32_t) targets_table[i].addr; ++ /* Iterate over all wwpns in given table */ ++ for(k=0;k> 32); ++ pos=grub_snprintf (bufptr, 32, "/disk@%" PRIxGRUB_UINT64_T, ++ *ptr_targets++); ++ /* Get the luns for given wwpn target */ ++ if (IEEE1275_CALL_ENTRY_FN (&args_luns) == -1) ++ { ++ grub_dprintf("disk", "failed to get the LUNS while iterating FCP disk path=%s\n", buf); ++ grub_ieee1275_close (ihandle); ++ grub_free (buf); ++ return; ++ } ++ ++ struct args_ret *luns_table = (struct args_ret *)(args_luns.table); ++ ++ /* Iterate over all LUNs */ ++ for(j=0;jtype, "vscsi") == 0) + { + static grub_ieee1275_ihandle_t ihandle; + struct set_color_args +-- +2.31.1 + diff --git a/0001-ieee1275-ofdisk-retry-on-open-and-read-failure.patch b/0001-ieee1275-ofdisk-retry-on-open-and-read-failure.patch new file mode 100644 index 0000000..57875c0 --- /dev/null +++ b/0001-ieee1275-ofdisk-retry-on-open-and-read-failure.patch @@ -0,0 +1,172 @@ +From f4728ed5307b6be6377b7bdafcab55fd3676a761 Mon Sep 17 00:00:00 2001 +From: Mukesh Kumar Chaurasiya +Date: Mon, 17 Jul 2023 16:02:34 +0530 +Subject: [PATCH] ieee1275/ofdisk: retry on open and read failure + +Sometimes, when booting from a very busy SAN, the access to the +disk can fail and then grub will eventually drop to grub prompt. +This scenario is more frequent when deploying many machines at +the same time using the same SAN. +This patch aims to force the ofdisk module to retry the open or +read function for network disks excluding after it fails. We use +DEFAULT_RETRY_TIMEOUT, which is 15 seconds to specify the time it'll +retry to access the disk before it definitely fails. The timeout can be +changed by setting the environment variable ofdisk_retry_timeout. +If the environment variable fails to read, grub will consider the +default value of 15 seconds. + +Signed-off-by: Diego Domingos +Signed-off-by: Mukesh Kumar Chaurasiya +--- + docs/grub.texi | 8 ++++ + grub-core/disk/ieee1275/ofdisk.c | 80 +++++++++++++++++++++++++++++++- + 2 files changed, 86 insertions(+), 2 deletions(-) + +diff --git a/docs/grub.texi b/docs/grub.texi +index d3f0f6577..c8ebc083d 100644 +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -3315,6 +3315,7 @@ These variables have special meaning to GRUB. + * net_default_ip:: + * net_default_mac:: + * net_default_server:: ++* ofdisk_retry_timeout:: + * pager:: + * prefix:: + * pxe_blksize:: +@@ -3744,6 +3745,13 @@ The default is the value of @samp{color_normal} (@pxref{color_normal}). + @xref{Network}. + + ++@node ofdisk_retry_timeout ++@subsection ofdisk_retry_timeout ++ ++The time in seconds till which the grub will retry to open or read a disk in ++case of failure to do so. This value defaults to 15 seconds. ++ ++ + @node pager + @subsection pager + +diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c +index 7197d5401..f96bbb58c 100644 +--- a/grub-core/disk/ieee1275/ofdisk.c ++++ b/grub-core/disk/ieee1275/ofdisk.c +@@ -24,6 +24,9 @@ + #include + #include + #include ++#include ++ ++#define RETRY_DEFAULT_TIMEOUT 15 + + static char *last_devpath; + static grub_ieee1275_ihandle_t last_ihandle; +@@ -783,7 +786,7 @@ compute_dev_path (const char *name) + } + + static grub_err_t +-grub_ofdisk_open (const char *name, grub_disk_t disk) ++grub_ofdisk_open_real (const char *name, grub_disk_t disk) + { + grub_ieee1275_phandle_t dev; + char *devpath; +@@ -879,6 +882,56 @@ grub_ofdisk_open (const char *name, grub_disk_t disk) + return 0; + } + ++static grub_uint64_t ++grub_ofdisk_disk_timeout (grub_disk_t disk) ++{ ++ grub_uint64_t retry; ++ const char *timeout = grub_env_get ("ofdisk_retry_timeout"); ++ ++ if (!(grub_strstr (disk->name, "fibre-channel@") || ++ grub_strstr (disk->name, "vfc-client")) || ++ grub_strstr(disk->name, "nvme-of")) ++ { ++ /* Do not retry in case of non network drives */ ++ return 0; ++ } ++ ++ if (timeout != NULL) ++ { ++ retry = grub_strtoul (timeout, 0, 10); ++ if (grub_errno != GRUB_ERR_NONE) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ return RETRY_DEFAULT_TIMEOUT; ++ } ++ if (retry) ++ return retry; ++ } ++ return RETRY_DEFAULT_TIMEOUT; ++} ++ ++static grub_err_t ++grub_ofdisk_open (const char *name, grub_disk_t disk) ++{ ++ grub_err_t err; ++ grub_uint64_t timeout = grub_get_time_ms () + (grub_ofdisk_disk_timeout (disk) * 1000); ++ _Bool cont; ++ do ++ { ++ err = grub_ofdisk_open_real (name, disk); ++ cont = grub_get_time_ms () < timeout; ++ if (err == GRUB_ERR_UNKNOWN_DEVICE && cont) ++ { ++ grub_dprintf ("ofdisk","Failed to open disk %s. Retrying...\n", name); ++ grub_errno = GRUB_ERR_NONE; ++ } ++ else ++ break; ++ grub_millisleep (1000); ++ } while (cont); ++ return err; ++} ++ + static void + grub_ofdisk_close (grub_disk_t disk) + { +@@ -915,7 +968,7 @@ grub_ofdisk_prepare (grub_disk_t disk, grub_disk_addr_t sector) + } + + static grub_err_t +-grub_ofdisk_read (grub_disk_t disk, grub_disk_addr_t sector, ++grub_ofdisk_read_real (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) + { + grub_err_t err; +@@ -934,6 +987,29 @@ grub_ofdisk_read (grub_disk_t disk, grub_disk_addr_t sector, + return 0; + } + ++static grub_err_t ++grub_ofdisk_read (grub_disk_t disk, grub_disk_addr_t sector, ++ grub_size_t size, char *buf) ++{ ++ grub_err_t err; ++ grub_uint64_t timeout = grub_get_time_ms () + (grub_ofdisk_disk_timeout (disk) * 1000); ++ _Bool cont; ++ do ++ { ++ err = grub_ofdisk_read_real (disk, sector, size, buf); ++ cont = grub_get_time_ms () < timeout; ++ if (err == GRUB_ERR_UNKNOWN_DEVICE && cont) ++ { ++ grub_dprintf ("ofdisk","Failed to read disk %s. Retrying...\n", (char*)disk->data); ++ grub_errno = GRUB_ERR_NONE; ++ } ++ else ++ break; ++ grub_millisleep (1000); ++ } while (cont); ++ return err; ++} ++ + static grub_err_t + grub_ofdisk_write (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, const char *buf) +-- +2.41.0 + diff --git a/0001-ieee1275-powerpc-implements-fibre-channel-discovery-.patch b/0001-ieee1275-powerpc-implements-fibre-channel-discovery-.patch new file mode 100644 index 0000000..5bdc95a --- /dev/null +++ b/0001-ieee1275-powerpc-implements-fibre-channel-discovery-.patch @@ -0,0 +1,90 @@ +From ca30b3c6fd8c848f510445316d0c4a8fca6061ba Mon Sep 17 00:00:00 2001 +From: Diego Domingos +Date: Wed, 24 Jun 2020 08:17:18 -0400 +Subject: [PATCH 1/2] ieee1275/powerpc: implements fibre channel discovery for + ofpathname + +grub-ofpathname doesn't work with fibre channel because there is no +function currently implemented for it. +This patch enables it by prividing a function that looks for the port +name, building the entire path for OF devices. +--- + grub-core/osdep/linux/ofpath.c | 48 ++++++++++++++++++++++++++++++++++ + 1 file changed, 48 insertions(+) + +diff --git a/grub-core/osdep/linux/ofpath.c b/grub-core/osdep/linux/ofpath.c +index a6153d359..f2bc9fc5c 100644 +--- a/grub-core/osdep/linux/ofpath.c ++++ b/grub-core/osdep/linux/ofpath.c +@@ -399,6 +399,37 @@ of_path_of_nvme(const char *sys_devname __attribute__((unused)), + } + #endif + ++static void ++of_fc_port_name(const char *path, const char *subpath, char *port_name) ++{ ++ char *bname, *basepath, *p; ++ int fd; ++ ++ bname = xmalloc(sizeof(char)*150); ++ basepath = xmalloc(strlen(path)); ++ ++ /* Generate the path to get port name information from the drive */ ++ strncpy(basepath,path,subpath-path); ++ basepath[subpath-path-1] = '\0'; ++ p = get_basename(basepath); ++ snprintf(bname,sizeof(char)*150,"%s/fc_transport/%s/port_name",basepath,p); ++ ++ /* Read the information from the port name */ ++ fd = open (bname, O_RDONLY); ++ if (fd < 0) ++ grub_util_error (_("cannot open `%s': %s"), bname, strerror (errno)); ++ ++ if (read(fd,port_name,sizeof(char)*19) < 0) ++ grub_util_error (_("cannot read `%s': %s"), bname, strerror (errno)); ++ ++ sscanf(port_name,"0x%s",port_name); ++ ++ close(fd); ++ ++ free(bname); ++ free(basepath); ++} ++ + static int + vendor_is_ATA(const char *path) + { +@@ -577,6 +608,16 @@ of_path_of_scsi(const char *sys_devname __attribute__((unused)), const char *dev + digit_string = trailing_digits (device); + if (strncmp (of_path, "/vdevice/", sizeof ("/vdevice/") - 1) == 0) + { ++ if(strstr(of_path,"vfc-client")) ++ { ++ char * port_name = xmalloc(sizeof(char)*17); ++ of_fc_port_name(sysfs_path, p, port_name); ++ ++ snprintf(disk,sizeof(disk),"/%s@%s", disk_name, port_name); ++ free(port_name); ++ } ++ else ++ { + unsigned long id = 0x8000 | (tgt << 8) | (bus << 5) | lun; + if (*digit_string == '\0') + { +@@ -590,6 +631,13 @@ of_path_of_scsi(const char *sys_devname __attribute__((unused)), const char *dev + snprintf(disk, sizeof (disk), + "/%s@%04lx000000000000:%c", disk_name, id, 'a' + (part - 1)); + } ++ } ++ } else if (strstr(of_path,"fibre-channel")||(strstr(of_path,"vfc-client"))){ ++ char * port_name = xmalloc(sizeof(char)*17); ++ of_fc_port_name(sysfs_path, p, port_name); ++ ++ snprintf(disk,sizeof(disk),"/%s@%s", disk_name, port_name); ++ free(port_name); + } + else + { +-- +2.26.2 + diff --git a/0001-install-fix-software-raid1-on-esp.patch b/0001-install-fix-software-raid1-on-esp.patch new file mode 100644 index 0000000..e0a4ee5 --- /dev/null +++ b/0001-install-fix-software-raid1-on-esp.patch @@ -0,0 +1,396 @@ +From 6444774dae24f439dae3b4bc8d73449d50f06240 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 31 Dec 2020 21:54:07 +0800 +Subject: [PATCH] install: fix software raid1 on esp + +While running grub-install on an efi system where efi system partition +is configured as mdadm software raid1, it fails with errors like this: + + grub2-install: info: copying `/boot/grub2/x86_64-efi/core.efi' -> `/boot/efi/EFI/opensuse/grubx64.efi'. + grub2-install: info: Registering with EFI: distributor = `opensuse', path = `\EFI\opensuse\grubx64.efi', ESP at mduuid/9182c46b9d469f79b48850b68f3371a5. + grub2-install: info: executing efibootmgr --version /dev/null. + grub2-install: info: executing modprobe -q efivars. + grub2-install: info: executing efibootmgr -c -d. + efibootmgr: option requires an argument -- 'd' + efibootmgr version 14 + usage: efibootmgr [options] + +This should work with mdadm raid1 with metadata 0.9 and 1.0 whose +superblocks are at the end of device. However +grub_install_register_efi() doesn't seem to work if the target is +multiple devices so that it errors out. + +The patch changes grub_install_register_efi() to accept multiple devices +that can be used to creating efi boot entries for probed raid1 member +devices on mounted efi system partition. + +This patch also adds check for metadata 0.9 or 1.0 or the validation +will fail to continue the install. + +Signed-off-by: Michael Chang +--- + grub-core/disk/diskfilter.c | 27 +++---- + grub-core/disk/mdraid1x_linux.c | 3 + + grub-core/osdep/basic/no_platform.c | 3 +- + grub-core/osdep/unix/platform.c | 57 +++++++++++---- + grub-core/osdep/windows/platform.c | 3 +- + include/grub/diskfilter.h | 3 +- + include/grub/util/install.h | 5 +- + util/grub-install.c | 107 ++++++++++++++++++++++++++-- + 8 files changed, 171 insertions(+), 37 deletions(-) + +--- a/grub-core/disk/diskfilter.c ++++ b/grub-core/disk/diskfilter.c +@@ -159,8 +159,8 @@ + for (m = arr->pvs; m; m = m->next) + if (m->disk && m->disk->id == disk->id + && m->disk->dev->id == disk->dev->id +- && m->part_start == grub_partition_get_start (disk->partition) +- && m->part_size == grub_disk_native_sectors (disk)) ++ && grub_partition_get_start (m->disk->partition) == grub_partition_get_start (disk->partition) ++ && grub_disk_native_sectors (m->disk) == grub_disk_native_sectors (disk)) + return 0; + } + +@@ -1340,19 +1340,23 @@ + ? (grub_memcmp (pv->id.uuid, id->uuid, id->uuidlen) == 0) + : (pv->id.id == id->id)) + { ++ char *part_name = NULL; + struct grub_diskfilter_lv *lv; + /* FIXME: Check whether the update time of the superblocks are + the same. */ +- if (pv->disk && grub_disk_native_sectors (disk) >= pv->part_size) ++ if (pv->disk && grub_disk_native_sectors (disk) >= grub_disk_native_sectors (pv->disk)) + return GRUB_ERR_NONE; +- pv->disk = grub_disk_open (disk->name); ++ if (disk->partition) ++ { ++ char *p = grub_partition_get_name (disk->partition); ++ if (p) ++ part_name = grub_xasprintf ("%s,%s", disk->name, p); ++ grub_free (p); ++ } ++ pv->disk = grub_disk_open (part_name ? : disk->name); ++ grub_free (part_name); + if (!pv->disk) + return grub_errno; +- /* This could happen to LVM on RAID, pv->disk points to the +- raid device, we shouldn't change it. */ +- pv->start_sector -= pv->part_start; +- pv->part_start = grub_partition_get_start (disk->partition); +- pv->part_size = grub_disk_native_sectors (disk); + + #ifdef GRUB_UTIL + { +@@ -1369,7 +1373,6 @@ + #endif + if (start_sector != (grub_uint64_t)-1) + pv->start_sector = start_sector; +- pv->start_sector += pv->part_start; + /* Add the device to the array. */ + for (lv = array->lvs; lv; lv = lv->next) + if (!lv->became_readable_at && lv->fullname && is_lv_readable (lv, 0)) +@@ -1457,8 +1460,8 @@ + { + if (pv->disk && pv->disk->id == disk->id + && pv->disk->dev->id == disk->dev->id +- && pv->part_start == grub_partition_get_start (disk->partition) +- && pv->part_size == grub_disk_native_sectors (disk)) ++ && grub_partition_get_start (pv->disk->partition) == grub_partition_get_start (disk->partition) ++ && grub_disk_native_sectors (pv->disk) == grub_disk_native_sectors (disk)) + { + if (vg_out) + *vg_out = vg; +--- a/grub-core/disk/mdraid1x_linux.c ++++ b/grub-core/disk/mdraid1x_linux.c +@@ -208,6 +208,9 @@ + grub_le_to_cpu32 (sb.chunksize), + grub_le_to_cpu32 (sb.layout), + grub_le_to_cpu32 (sb.level)); ++#ifdef GRUB_UTIL ++ array->mdraid1x_minor_version = minor_version; ++#endif + + return array; + } +--- a/grub-core/osdep/basic/no_platform.c ++++ b/grub-core/osdep/basic/no_platform.c +@@ -33,7 +33,8 @@ + void + grub_install_register_efi (grub_device_t efidir_grub_dev, + const char *efifile_path, +- const char *efi_distributor) ++ const char *efi_distributor, ++ const char *force_disk) + { + grub_util_error ("%s", _("no EFI routines are available for your platform")); + } +--- a/grub-core/osdep/unix/platform.c ++++ b/grub-core/osdep/unix/platform.c +@@ -132,15 +132,14 @@ + } + + int +-grub_install_register_efi (grub_device_t efidir_grub_dev, ++grub_install_register_efi (const grub_disk_t *efidir_grub_disk, + const char *efifile_path, +- const char *efi_distributor) ++ const char *efi_distributor, ++ const char *force_disk) + { +- const char * efidir_disk; +- int efidir_part; + int ret; +- efidir_disk = grub_util_biosdisk_get_osdev (efidir_grub_dev->disk); +- efidir_part = efidir_grub_dev->disk->partition ? efidir_grub_dev->disk->partition->number + 1 : 1; ++ const grub_disk_t *curdisk; ++ int ndev = 0; + + if (grub_util_exec_redirect_null ((const char * []){ "efibootmgr", "--version", NULL })) + { +@@ -158,22 +157,50 @@ + if (ret) + return ret; + +- char *efidir_part_str = xasprintf ("%d", efidir_part); ++ for (curdisk = efidir_grub_disk; *curdisk; curdisk++) ++ ndev++; + +- if (!verbosity) +- ret = grub_util_exec ((const char * []){ "efibootmgr", "-q", ++ for (curdisk = efidir_grub_disk; *curdisk; curdisk++) ++ { ++ const char * efidir_disk; ++ int efidir_part; ++ char *efidir_part_str; ++ char *new_efi_distributor = NULL; ++ grub_disk_t disk = *curdisk; ++ ++ efidir_disk = force_disk ? : grub_util_biosdisk_get_osdev (disk); ++ if (!efidir_disk) ++ grub_util_error (_("%s: no device for efi"), disk->name); ++ ++ efidir_part = disk->partition ? disk->partition->number + 1 : 1; ++ efidir_part_str = xasprintf ("%d", efidir_part); ++ if (ndev > 1) ++ { ++ const char *p = grub_strrchr (efidir_disk, '/'); ++ new_efi_distributor = xasprintf ("%s (%s%d)\n", ++ efi_distributor, ++ p ? p + 1: efidir_disk, ++ efidir_part); ++ } ++ ++ if (!verbosity) ++ ret = grub_util_exec ((const char * []){ "efibootmgr", "-q", + "-c", "-d", efidir_disk, + "-p", efidir_part_str, "-w", +- "-L", efi_distributor, "-l", ++ "-L", new_efi_distributor ? : efi_distributor, "-l", + efifile_path, NULL }); +- else +- ret = grub_util_exec ((const char * []){ "efibootmgr", ++ else ++ ret = grub_util_exec ((const char * []){ "efibootmgr", + "-c", "-d", efidir_disk, + "-p", efidir_part_str, "-w", +- "-L", efi_distributor, "-l", ++ "-L", new_efi_distributor ? : efi_distributor, "-l", + efifile_path, NULL }); +- free (efidir_part_str); +- return ret; ++ free (efidir_part_str); ++ free (new_efi_distributor); ++ if (ret) ++ return ret; ++ } ++ return 0; + } + + void +--- a/grub-core/osdep/windows/platform.c ++++ b/grub-core/osdep/windows/platform.c +@@ -204,7 +204,8 @@ + int + grub_install_register_efi (grub_device_t efidir_grub_dev, + const char *efifile_path, +- const char *efi_distributor) ++ const char *efi_distributor, ++ const char *force_disk) + { + grub_uint16_t *boot_order, *new_boot_order; + grub_uint16_t *distributor16; +--- a/include/grub/diskfilter.h ++++ b/include/grub/diskfilter.h +@@ -49,6 +49,7 @@ + + #ifdef GRUB_UTIL + struct grub_diskfilter *driver; ++ grub_uint8_t mdraid1x_minor_version; + #endif + }; + +@@ -66,8 +67,6 @@ + /* Optional. */ + char *name; + grub_disk_t disk; +- grub_disk_addr_t part_start; +- grub_disk_addr_t part_size; + grub_disk_addr_t start_sector; /* Sector number where the data area starts. */ + struct grub_diskfilter_pv *next; + /* Optional. */ +--- a/include/grub/util/install.h ++++ b/include/grub/util/install.h +@@ -236,9 +236,10 @@ + grub_install_get_powerpc_secure_boot (void); + + int +-grub_install_register_efi (grub_device_t efidir_grub_dev, ++grub_install_register_efi (const grub_disk_t *efidir_grub_disk, + const char *efifile_path, +- const char *efi_distributor); ++ const char *efi_distributor, ++ const char *force_disk); + + void + grub_install_register_ieee1275 (int is_prep, const char *install_device, +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -1719,6 +1719,40 @@ + } + } + prefix_drive = xasprintf ("(%s)", grub_drives[0]); ++ ++ if (platform == GRUB_INSTALL_PLATFORM_X86_64_EFI ++ && grub_dev->disk ++ && grub_dev->disk->partition ++ && grub_fs->fs_uuid) ++ { ++ int raid_level; ++ char *uuid = NULL; ++ char *escaped_relpath = NULL; ++ ++ raid_level = probe_raid_level (grub_dev->disk); ++ if (raid_level != 1) ++ goto out; ++ ++ escaped_relpath = escape (relative_grubdir); ++ if (!escaped_relpath) ++ goto out; ++ ++ if (grub_fs->fs_uuid (grub_dev, &uuid) || !uuid) ++ { ++ grub_print_error (); ++ grub_errno = 0; ++ goto out; ++ } ++ ++ if (!load_cfg_f) ++ load_cfg_f = grub_util_fopen (load_cfg, "wb"); ++ have_load_cfg = 1; ++ fprintf (load_cfg_f, "search --no-floppy --fs-uuid --set=root --hint='%s' %s\n", grub_drives[0], uuid); ++ fprintf (load_cfg_f, "set prefix=($root)'%s'\n", escaped_relpath); ++ grub_install_push_module ("search"); ++ out: ++ grub_free (escaped_relpath); ++ } + } + + #ifdef __linux__ +@@ -2258,9 +2292,13 @@ + { + /* Try to make this image bootable using the EFI Boot Manager, if available. */ + int ret; +- ret = grub_install_register_efi (efidir_grub_dev, ++ grub_disk_t efidir_grub_disk[2]; ++ efidir_grub_disk[0] = efidir_grub_dev->disk; ++ efidir_grub_disk[1] = NULL; ++ ret = grub_install_register_efi (efidir_grub_disk, + "\\System\\Library\\CoreServices", +- efi_distributor); ++ efi_distributor, ++ NULL); + if (ret) + grub_util_error (_("efibootmgr failed to register the boot entry: %s"), + strerror (ret)); +@@ -2314,7 +2352,11 @@ + { + char * efifile_path; + char * part; ++ int raid_level; + int ret; ++ grub_disk_t *efidir_grub_disk; ++ grub_disk_memberlist_t list = NULL, cur; ++ char * force_disk = NULL; + + /* Try to make this image bootable using the EFI Boot Manager, if available. */ + if (!efi_distributor || efi_distributor[0] == '\0') +@@ -2331,8 +2373,65 @@ + efidir_grub_dev->disk->name, + (part ? ",": ""), (part ? : "")); + grub_free (part); +- ret = grub_install_register_efi (efidir_grub_dev, +- efifile_path, efi_distributor); ++ ++ raid_level = probe_raid_level (efidir_grub_dev->disk); ++ if (raid_level >= 0 && raid_level != 1) ++ grub_util_warn (_("unsupported raid level %d detected for efi system partition"), raid_level); ++ if (raid_level == 1 && !efidir_grub_dev->disk->partition) ++ { ++ const char *raidname = NULL; ++ ++ if (efidir_grub_dev->disk->dev->disk_raidname) ++ raidname = efidir_grub_dev->disk->dev->disk_raidname (efidir_grub_dev->disk); ++ if (raidname ++ && (grub_strncmp (raidname, "mdraid09", sizeof ("mdraid09")) == 0 ++ || (grub_strcmp (raidname, "mdraid1x") == 0 ++ && ((struct grub_diskfilter_lv *) efidir_grub_dev->disk->data)->vg->mdraid1x_minor_version == 0))) ++ { ++ if (efidir_grub_dev->disk->dev->disk_memberlist) ++ list = efidir_grub_dev->disk->dev->disk_memberlist (efidir_grub_dev->disk); ++ } ++ else ++ { ++ grub_util_warn (_("this array has metadata at the start and may not be suitable as a efi system partition." ++ " please ensure that your firmware understands md/v1.x metadata, or use --metadata=0.90" ++ " to create the array.")); ++ /* Try to continue regardless metadata, nothing to lose here */ ++ if (efidir_grub_dev->disk->dev->disk_memberlist) ++ list = efidir_grub_dev->disk->dev->disk_memberlist (efidir_grub_dev->disk); ++ } ++ } ++ else if (raid_level == 1) ++ force_disk = grub_util_get_os_disk (install_device); ++ if (list) ++ { ++ int i; ++ int ndisk = 0; ++ ++ for (cur = list; cur; cur = cur->next) ++ ++ndisk; ++ efidir_grub_disk = xcalloc (ndisk + 1, sizeof (*efidir_grub_disk)); ++ for (cur = list, i = 0; i < ndisk; cur = cur->next, i++) ++ efidir_grub_disk[i] = cur->disk; ++ efidir_grub_disk[ndisk] = NULL; ++ } ++ else ++ { ++ efidir_grub_disk = xcalloc (2, sizeof (*efidir_grub_disk)); ++ efidir_grub_disk[0] = efidir_grub_dev->disk; ++ efidir_grub_disk[1] = NULL; ++ } ++ ret = grub_install_register_efi (efidir_grub_disk, ++ efifile_path, efi_distributor, ++ force_disk); ++ while (list) ++ { ++ cur = list; ++ list = list->next; ++ grub_free (cur); ++ } ++ grub_free (force_disk); ++ grub_free (efidir_grub_disk); + if (ret) + grub_util_error (_("efibootmgr failed to register the boot entry: %s"), + strerror (ret)); diff --git a/0001-kern-ieee1275-init-Restrict-high-memory-in-presence-.patch b/0001-kern-ieee1275-init-Restrict-high-memory-in-presence-.patch new file mode 100644 index 0000000..0a7a61a --- /dev/null +++ b/0001-kern-ieee1275-init-Restrict-high-memory-in-presence-.patch @@ -0,0 +1,229 @@ +From 4bcf6f747c3ab0b998c6f5a361804e38bc9c4334 Mon Sep 17 00:00:00 2001 +From: Stefan Berger +Date: Wed, 4 Oct 2023 11:32:35 -0400 +Subject: [PATCH] kern/ieee1275/init: Restrict high memory in presence of + fadump on ppc64 + +When a kernel dump is present then restrict the high memory regions to +avoid allocating memory where the kernel dump resides. Use the +ibm,kernel-dump node under /rtas to determine whether a kernel dump +exists and up to which limit GRUB can use available memory. Set the +upper_mem_limit to the size of the kernel dump section of type +REAL_MODE_REGION and therefore only allow GRUB's memory usage for high +addresses from RMO_ADDR_MAX to upper_mem_limit. This means that GRUB can +use high memory in the range of RMO_ADDR_MAX (768MB) to upper_mem_limit +and the kernel-dump memory regions above upper_mem_limit remain +untouched. This change has no effect on memory allocations below +linux_rmo_save (typically at 640MB). + +Also, fall back to allocating below rmo_linux_save in case the chunk of +memory there would be larger than the chunk of memory above RMO_ADDR_MAX. +This can for example occur if a free memory area is found starting at 300MB +extending up to 1GB but a kernel dump is located at 768MB and therefore +does not allow the allocation of the high memory area but requiring to use +the chunk starting at 300MB to avoid an unnecessary out-of-memory condition. + +Signed-off-by: Stefan Berger +Reviewed-by: Hari Bathini +Cc: Pavithra Prakash +Cc: Michael Ellerman +Cc: Carolyn Scherrer +Cc: Mahesh Salgaonkar +Cc: Sourabh Jain +Reviewed-by: Daniel Kiper +--- + grub-core/kern/ieee1275/init.c | 144 ++++++++++++++++++++++++++++++++- + 1 file changed, 142 insertions(+), 2 deletions(-) + +diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c +index bd9a4804b..d6c9c9049 100644 +--- a/grub-core/kern/ieee1275/init.c ++++ b/grub-core/kern/ieee1275/init.c +@@ -17,6 +17,8 @@ + * along with GRUB. If not, see . + */ + ++#include /* offsetof() */ ++ + #include + #include + #include +@@ -196,6 +198,96 @@ grub_claim_heap (void) + #else + /* Helpers for mm on powerpc. */ + ++/* ibm,kernel-dump data structures */ ++struct kd_section ++{ ++ grub_uint32_t flags; ++ grub_uint16_t src_datatype; ++#define KD_SRC_DATATYPE_REAL_MODE_REGION 0x0011 ++ grub_uint16_t error_flags; ++ grub_uint64_t src_address; ++ grub_uint64_t num_bytes; ++ grub_uint64_t act_bytes; ++ grub_uint64_t dst_address; ++} GRUB_PACKED; ++ ++#define MAX_KD_SECTIONS 10 ++ ++struct kernel_dump ++{ ++ grub_uint32_t format; ++ grub_uint16_t num_sections; ++ grub_uint16_t status_flags; ++ grub_uint32_t offset_1st_section; ++ grub_uint32_t num_blocks; ++ grub_uint64_t start_block; ++ grub_uint64_t num_blocks_avail; ++ grub_uint32_t offet_path_string; ++ grub_uint32_t max_time_allowed; ++ struct kd_section kds[MAX_KD_SECTIONS]; /* offset_1st_section should point to kds[0] */ ++} GRUB_PACKED; ++ ++/* ++ * Determine if a kernel dump exists and if it does, then determine the highest ++ * address that grub can use for memory allocations. ++ * The caller must have initialized *highest to rmo_top. *highest will not ++ * be modified if no kernel dump is found. ++ */ ++static void ++check_kernel_dump (grub_uint64_t *highest) ++{ ++ struct kernel_dump kernel_dump; ++ grub_ssize_t kernel_dump_size; ++ grub_ieee1275_phandle_t rtas; ++ struct kd_section *kds; ++ grub_size_t i; ++ ++ /* If there's a kernel-dump it must have at least one section */ ++ if (grub_ieee1275_finddevice ("/rtas", &rtas) || ++ grub_ieee1275_get_property (rtas, "ibm,kernel-dump", &kernel_dump, ++ sizeof (kernel_dump), &kernel_dump_size) || ++ kernel_dump_size <= (grub_ssize_t) offsetof (struct kernel_dump, kds[1])) ++ return; ++ ++ kernel_dump_size = grub_min (kernel_dump_size, (grub_ssize_t) sizeof (kernel_dump)); ++ ++ if (grub_be_to_cpu32 (kernel_dump.format) != 1) ++ { ++ grub_printf (_("Error: ibm,kernel-dump has an unexpected format version '%u'\n"), ++ grub_be_to_cpu32 (kernel_dump.format)); ++ return; ++ } ++ ++ if (grub_be_to_cpu16 (kernel_dump.num_sections) > MAX_KD_SECTIONS) ++ { ++ grub_printf (_("Error: Too many kernel dump sections: %d\n"), ++ grub_be_to_cpu32 (kernel_dump.num_sections)); ++ return; ++ } ++ ++ for (i = 0; i < grub_be_to_cpu16 (kernel_dump.num_sections); i++) ++ { ++ kds = (struct kd_section *) ((grub_addr_t) &kernel_dump + ++ grub_be_to_cpu32 (kernel_dump.offset_1st_section) + ++ i * sizeof (struct kd_section)); ++ /* sanity check the address is within the 'kernel_dump' struct */ ++ if ((grub_addr_t) kds > (grub_addr_t) &kernel_dump + kernel_dump_size + sizeof (*kds)) ++ { ++ grub_printf (_("Error: 'kds' address beyond last available section\n")); ++ return; ++ } ++ ++ if ((grub_be_to_cpu16 (kds->src_datatype) == KD_SRC_DATATYPE_REAL_MODE_REGION) && ++ (grub_be_to_cpu64 (kds->src_address) == 0)) ++ { ++ *highest = grub_min (*highest, grub_be_to_cpu64 (kds->num_bytes)); ++ break; ++ } ++ } ++ ++ return; ++} ++ + /* + * How much memory does OF believe exists in total? + * +@@ -275,10 +367,31 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, + * + * Finally, we also want to make sure that when grub loads the kernel, + * it isn't going to use up all the memory we're trying to reserve! So +- * enforce our entire RUNTIME_MIN_SPACE here: ++ * enforce our entire RUNTIME_MIN_SPACE here (no fadump): ++ * ++ * | Top of memory == upper_mem_limit -| ++ * | | ++ * | available | ++ * | | ++ * |---------- 768 MB ----------| ++ * | | ++ * | reserved | ++ * | | ++ * |--- 768 MB - runtime min space ---| ++ * | | ++ * | available | ++ * | | ++ * |---------- 0 MB ----------| ++ * ++ * In case fadump is used, we allow the following: + * + * |---------- Top of memory ----------| + * | | ++ * | unavailable | ++ * | (kernel dump area) | ++ * | | ++ * |--------- upper_mem_limit ---------| ++ * | | + * | available | + * | | + * |---------- 768 MB ----------| +@@ -333,17 +446,44 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, + } + else + { ++ grub_uint64_t upper_mem_limit = rmo_top; ++ grub_uint64_t orig_addr = addr; ++ ++ check_kernel_dump (&upper_mem_limit); ++ + /* + * we order these cases to prefer higher addresses and avoid some + * splitting issues ++ * The following shows the order of variables: ++ * no kernel dump: linux_rmo_save < RMO_ADDR_MAX <= upper_mem_limit == rmo_top ++ * with kernel dump: liuxx_rmo_save < RMO_ADDR_MAX <= upper_mem_limit <= rmo_top + */ +- if (addr < RMO_ADDR_MAX && (addr + len) > RMO_ADDR_MAX) ++ if (addr < RMO_ADDR_MAX && (addr + len) > RMO_ADDR_MAX && upper_mem_limit >= RMO_ADDR_MAX) + { + grub_dprintf ("ieee1275", + "adjusting region for RUNTIME_MIN_SPACE: (%llx -> %llx) -> (%llx -> %llx)\n", + addr, addr + len, RMO_ADDR_MAX, addr + len); + len = (addr + len) - RMO_ADDR_MAX; + addr = RMO_ADDR_MAX; ++ ++ /* We must not exceed the upper_mem_limit (assuming it's >= RMO_ADDR_MAX) */ ++ if (addr + len > upper_mem_limit) ++ { ++ /* take the bigger chunk from either below linux_rmo_save or above upper_mem_limit */ ++ len = upper_mem_limit - addr; ++ if (orig_addr < linux_rmo_save && linux_rmo_save - orig_addr > len) ++ { ++ /* lower part is bigger */ ++ addr = orig_addr; ++ len = linux_rmo_save - addr; ++ } ++ ++ grub_dprintf ("ieee1275", "re-adjusted region to: (%llx -> %llx)\n", ++ addr, addr + len); ++ ++ if (len == 0) ++ return 0; ++ } + } + else if ((addr < linux_rmo_save) && ((addr + len) > linux_rmo_save)) + { +-- +2.42.0 + diff --git a/0001-kern-mm.c-Make-grub_calloc-inline.patch b/0001-kern-mm.c-Make-grub_calloc-inline.patch new file mode 100644 index 0000000..380a9e7 --- /dev/null +++ b/0001-kern-mm.c-Make-grub_calloc-inline.patch @@ -0,0 +1,109 @@ +From c2475f1337dff2e2a3e45514119d5186e55753c1 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 13 Aug 2020 09:36:45 +0800 +Subject: [PATCH] kern/mm.c : Make grub_calloc inline + +To circumvent the situation that symbol 'grub_calloc' not found would +happen if system is using stray grub (ie not managed by system update) +as stage1 that can be too old to load updated modules. +--- + grub-core/kern/mm.c | 28 ---------------------------- + include/grub/mm.h | 32 +++++++++++++++++++++++++++++++- + 2 files changed, 31 insertions(+), 29 deletions(-) + +--- a/grub-core/kern/mm.c ++++ b/grub-core/kern/mm.c +@@ -63,14 +63,10 @@ + + #include + #include +-#include +-#include + #include + #include + #include +-#include + #include +-#include + + #ifdef MM_DEBUG + # undef grub_calloc +@@ -553,30 +549,6 @@ + return 0; + } + +-/* +- * Allocate NMEMB instances of SIZE bytes and return the pointer, or error on +- * integer overflow. +- */ +-void * +-grub_calloc (grub_size_t nmemb, grub_size_t size) +-{ +- void *ret; +- grub_size_t sz = 0; +- +- if (grub_mul (nmemb, size, &sz)) +- { +- grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); +- return NULL; +- } +- +- ret = grub_memalign (0, sz); +- if (!ret) +- return NULL; +- +- grub_memset (ret, 0, sz); +- return ret; +-} +- + /* Allocate SIZE bytes and return the pointer. */ + void * + grub_malloc (grub_size_t size) +--- a/include/grub/mm.h ++++ b/include/grub/mm.h +@@ -47,7 +47,6 @@ + #endif + + void grub_mm_init_region (void *addr, grub_size_t size); +-void *EXPORT_FUNC(grub_calloc) (grub_size_t nmemb, grub_size_t size); + void *EXPORT_FUNC(grub_malloc) (grub_size_t size); + void *EXPORT_FUNC(grub_zalloc) (grub_size_t size); + void EXPORT_FUNC(grub_free) (void *ptr); +@@ -55,6 +54,37 @@ + #ifndef GRUB_MACHINE_EMU + void *EXPORT_FUNC(grub_memalign) (grub_size_t align, grub_size_t size); + #endif ++#if !defined(GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) ++#include ++#include ++#include ++#include ++/* ++ * Allocate NMEMB instances of SIZE bytes and return the pointer, or error on ++ * integer overflow. ++ */ ++static inline void * ++grub_calloc (grub_size_t nmemb, grub_size_t size) ++{ ++ void *ret; ++ grub_size_t sz = 0; ++ ++ if (grub_mul (nmemb, size, &sz)) ++ { ++ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); ++ return NULL; ++ } ++ ++ ret = grub_memalign (0, sz); ++ if (!ret) ++ return NULL; ++ ++ grub_memset (ret, 0, sz); ++ return ret; ++} ++#else ++void *EXPORT_FUNC(grub_calloc) (grub_size_t nmemb, grub_size_t size); ++#endif + + void grub_mm_check_real (const char *file, int line); + #define grub_mm_check() grub_mm_check_real (GRUB_FILE, __LINE__); diff --git a/0001-luks2-Use-grub-tpm2-token-for-TPM2-protected-volume-.patch b/0001-luks2-Use-grub-tpm2-token-for-TPM2-protected-volume-.patch new file mode 100644 index 0000000..ba58d6c --- /dev/null +++ b/0001-luks2-Use-grub-tpm2-token-for-TPM2-protected-volume-.patch @@ -0,0 +1,142 @@ +From 06af22d6c893b0249712e9a486e0cbae15160e5c Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Mon, 23 Oct 2023 16:11:53 +0800 +Subject: [PATCH] luks2: Use grub-tpm2 token for TPM2-protected volume unlock + +This commit enables the use of the grub-tpm2 token for unlocking LUKS2 +volumes protected by TPM2. The token tracks keyslots associated with a +sealed key, making the unsealing process more efficient and secure. + +Signed-Off-by Michael Chang +--- + grub-core/disk/luks2.c | 81 ++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 79 insertions(+), 2 deletions(-) + +diff --git a/grub-core/disk/luks2.c b/grub-core/disk/luks2.c +index d5106402f..fe5ba777a 100644 +--- a/grub-core/disk/luks2.c ++++ b/grub-core/disk/luks2.c +@@ -124,6 +124,14 @@ struct grub_luks2_digest + }; + typedef struct grub_luks2_digest grub_luks2_digest_t; + ++struct grub_luks2_token_tpm ++{ ++ grub_uint64_t idx; ++ grub_uint64_t keyslots; ++ const char *timestamp; ++}; ++typedef struct grub_luks2_token_tpm grub_luks2_token_tpm_t; ++ + gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, + grub_uint8_t * dst, grub_size_t blocksize, + grub_size_t blocknumbers); +@@ -257,6 +265,39 @@ luks2_parse_digest (grub_luks2_digest_t *out, const grub_json_t *digest) + return GRUB_ERR_NONE; + } + ++static grub_err_t ++luks2_parse_token_tpm (grub_luks2_token_tpm_t *out, const grub_json_t *token) ++{ ++ grub_json_t keyslots, o; ++ grub_size_t i, size; ++ grub_uint64_t bit; ++ const char *type; ++ ++ if (grub_json_getstring (&type, token, "type")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid token type"); ++ else if (grub_strcmp (type, "grub-tpm2")) ++ return GRUB_ERR_NONE; ++ ++ if (grub_json_getvalue (&keyslots, token, "keyslots") || ++ grub_json_getstring (&out->timestamp, token, "timestamp")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing token parameters"); ++ ++ if (grub_json_getsize (&size, &keyslots)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ "Token references no keyslots"); ++ ++ out->keyslots = 0; ++ for (i = 0; i < size; i++) ++ { ++ if (grub_json_getchild (&o, &keyslots, i) || ++ grub_json_getuint64 (&bit, &o, NULL)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid keyslot"); ++ out->keyslots |= (1 << bit); ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ + static grub_err_t + luks2_get_keyslot (grub_luks2_keyslot_t *k, grub_luks2_digest_t *d, grub_luks2_segment_t *s, + const grub_json_t *root, grub_size_t keyslot_json_idx) +@@ -561,13 +602,14 @@ luks2_recover_key (grub_disk_t source, + { + grub_uint8_t candidate_key[GRUB_CRYPTODISK_MAX_KEYLEN]; + char cipher[32], *json_header = NULL, *ptr; +- grub_size_t candidate_key_len = 0, json_idx, size; ++ grub_size_t candidate_key_len = 0, json_idx, size, tsize; + grub_luks2_header_t header; + grub_luks2_keyslot_t keyslot; + grub_luks2_digest_t digest; + grub_luks2_segment_t segment; ++ grub_luks2_token_tpm_t token_tpm; + gcry_err_code_t gcry_ret; +- grub_json_t *json = NULL, keyslots; ++ grub_json_t *json = NULL, keyslots, tokens; + grub_err_t ret; + + if (cargs->key_data == NULL || cargs->key_len == 0) +@@ -605,6 +647,37 @@ luks2_recover_key (grub_disk_t source, + goto err; + } + ++ token_tpm.keyslots = 0; ++ tsize = 0; ++ if (cargs->protectors) ++ { ++ int i; ++ for (i = 0; cargs->protectors[i]; i++) ++ if (grub_strcmp(cargs->protectors[i], "tpm2") == 0) ++ break; ++ ++ if (!cargs->protectors[i] || ++ cargs->key_cache[i].invalid || ++ grub_json_getvalue (&tokens, json, "tokens") || ++ grub_json_getsize (&tsize, &tokens)) ++ grub_dprintf ("luks2", "No valid token or not a tpm2 protector\n"); ++ } ++ ++ for (json_idx = 0; json_idx < tsize; json_idx++) ++ { ++ grub_json_t token; ++ ++ if (grub_json_getchild (&token, &tokens, json_idx) || ++ grub_json_getuint64 (&token_tpm.idx, &token, NULL) || ++ grub_json_getchild (&token, &token, 0) || ++ luks2_parse_token_tpm (&token_tpm, &token)) ++ { ++ grub_dprintf ("luks2", "Could not parse token index %" PRIuGRUB_SIZE "\n", json_idx); ++ grub_errno = GRUB_ERR_NONE; ++ continue; ++ } ++ } ++ + if (grub_disk_native_sectors (source) == GRUB_DISK_SIZE_UNKNOWN) + { + /* FIXME: Allow use of source disk, and maybe cause errors in read. */ +@@ -641,6 +714,10 @@ luks2_recover_key (grub_disk_t source, + continue; + } + ++ if (token_tpm.keyslots && ++ !(token_tpm.keyslots & (1 << keyslot.idx))) ++ continue; ++ + grub_dprintf ("luks2", "Trying keyslot \"%" PRIuGRUB_UINT64_T "\"\n", keyslot.idx); + + /* Sector size should be one of 512, 1024, 2048, or 4096. */ +-- +2.42.0 + diff --git a/0001-mkstandalone-ensure-stable-timestamps-for-generated-.patch b/0001-mkstandalone-ensure-stable-timestamps-for-generated-.patch new file mode 100644 index 0000000..a2b5a56 --- /dev/null +++ b/0001-mkstandalone-ensure-stable-timestamps-for-generated-.patch @@ -0,0 +1,76 @@ +From 1fdc9daf97a1518960e5603dd43a5f353cb3ca89 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 30 Nov 2023 13:45:13 +0800 +Subject: [PATCH 1/2] mkstandalone: ensure stable timestamps for generated + images + +This change mirrors a previous fix [1] but is specific to images +generated by grub-mkstandalone. + +The former fix (85a7be241) focused on utilizing a stable timestamp +during binary generation in the util/mkimage context. This commit +extends that approach to the images produced by grub-mkstandalone, +ensuring consistency and stability in timestamps across all generated +binaries. + +[1] 85a7be241 util/mkimage: Use stable timestamp when generating +binaries. + +Signed-off-by: Michael Chang +Signed-off-by: Bernhard Wiedemann +--- + util/grub-mkstandalone.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/util/grub-mkstandalone.c b/util/grub-mkstandalone.c +index bdbeea6a6..8e1229925 100644 +--- a/util/grub-mkstandalone.c ++++ b/util/grub-mkstandalone.c +@@ -30,6 +30,9 @@ + #pragma GCC diagnostic error "-Wmissing-prototypes" + #pragma GCC diagnostic error "-Wmissing-declarations" + ++/* use 2015-01-01T00:00:00+0000 as a stock timestamp */ ++#define STABLE_EMBEDDING_TIMESTAMP 1420070400 ++ + static char *output_image; + static char **files; + static int nfiles; +@@ -184,7 +187,6 @@ add_tar_file (const char *from, + struct head hd; + grub_util_fd_t in; + ssize_t r; +- grub_uint32_t mtime = 0; + grub_uint32_t size; + + COMPILE_TIME_ASSERT (sizeof (hd) == 512); +@@ -192,8 +194,6 @@ add_tar_file (const char *from, + if (grub_util_is_special_file (from)) + return; + +- mtime = grub_util_get_mtime (from); +- + optr = tcn = xmalloc (strlen (to) + 1); + for (iptr = to; *iptr == '/'; iptr++); + for (; *iptr; iptr++) +@@ -234,7 +234,7 @@ add_tar_file (const char *from, + memcpy (hd.gid, "0001750", 7); + + set_tar_value (hd.size, optr - tcn, 12); +- set_tar_value (hd.mtime, mtime, 12); ++ set_tar_value (hd.mtime, STABLE_EMBEDDING_TIMESTAMP, 12); + hd.typeflag = 'L'; + memcpy (hd.magic, MAGIC, sizeof (hd.magic)); + memcpy (hd.uname, "grub", 4); +@@ -264,7 +264,7 @@ add_tar_file (const char *from, + memcpy (hd.gid, "0001750", 7); + + set_tar_value (hd.size, size, 12); +- set_tar_value (hd.mtime, mtime, 12); ++ set_tar_value (hd.mtime, STABLE_EMBEDDING_TIMESTAMP, 12); + hd.typeflag = '0'; + memcpy (hd.magic, MAGIC, sizeof (hd.magic)); + memcpy (hd.uname, "grub", 4); +-- +2.43.0 + diff --git a/0001-ofdisk-improve-boot-time-by-lookup-boot-disk-first.patch b/0001-ofdisk-improve-boot-time-by-lookup-boot-disk-first.patch new file mode 100644 index 0000000..c23a3a8 --- /dev/null +++ b/0001-ofdisk-improve-boot-time-by-lookup-boot-disk-first.patch @@ -0,0 +1,58 @@ +From b0f9dcabe96e5689ecfba9b6abcd27e685eabd48 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Wed, 11 May 2022 09:56:11 -0400 +Subject: [PATCH] ofdisk: improve boot time by lookup boot disk first + +While booting lvm, grub will try to build up logical volumes via hooks +to disk iteration where on-disk metadata can be read and parsed. However +the process can become very slow on multipath as reachable disks are +duplicated by multiple I/O paths and they all get inspected. + +Fortunately grub allows lvm to be lazy binding and opportunistic that +root volume can be created when it's needed using a smaller set of +discovered disks. The disk iteration can also be controlled by pull +methods to only returning specified disks. That said we may be able to +take advantage of existing design to cause less overhead in lvm +construction. + +This patch will return boot disks in OpenFirmware so they can be used +first. If lvm managed to create root volume out of those boot disks then +it is all very nice as they are readily available. Otherwise disk +scanning will be performed to present all discoverable disks to grub as +what it was done in the past. The result maybe again time consuming but +we have nothing to lose here. + +Signed-off-by: Michael Chang +--- + grub-core/disk/ieee1275/ofdisk.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +--- a/grub-core/disk/ieee1275/ofdisk.c ++++ b/grub-core/disk/ieee1275/ofdisk.c +@@ -491,10 +491,11 @@ + { + unsigned i; + +- if (pull != GRUB_DISK_PULL_NONE) ++ if (pull > GRUB_DISK_PULL_REMOVABLE) + return 0; + +- scan (); ++ if (pull == GRUB_DISK_PULL_REMOVABLE) ++ scan (); + + for (i = 0; i < ARRAY_SIZE (ofdisk_hash); i++) + { +@@ -532,6 +533,12 @@ + if (!ent->is_boot && ent->is_removable) + continue; + ++ if (pull == GRUB_DISK_PULL_NONE && !ent->is_boot) ++ continue; ++ ++ if (pull == GRUB_DISK_PULL_REMOVABLE && ent->is_boot) ++ continue; ++ + if (hook (ent->grub_shortest, hook_data)) + return 1; + } diff --git a/0001-openfw-Ensure-get_devargs-and-get_devname-functions-.patch b/0001-openfw-Ensure-get_devargs-and-get_devname-functions-.patch new file mode 100644 index 0000000..f7f01c0 --- /dev/null +++ b/0001-openfw-Ensure-get_devargs-and-get_devname-functions-.patch @@ -0,0 +1,52 @@ +From 468628bdc39800341e7aa6ff7795cc0d93cfaf3f Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Tue, 11 Apr 2023 10:59:34 +0800 +Subject: [PATCH 1/2] openfw: Ensure get_devargs and get_devname functions are + consistent + +Commit 165c9b234 changed the logic of ieee1275_get_devargs() to use the +first or second occurrence of a colon as a separator between device name +and arguments. However, this didn't align with the complementary +function ieee1275_get_devname, which uses the first occurrence of a +colon after the namespace keyword as arguments for the nvme-of device. + +This commit addresses the inconsistency by ensuring that both functions +follow a common logic. Now, get_devargs and get_devname functions are +consistent with each other, making it easier to understand and maintain +the codebase. + +Signed-off-by: Michael Chang +--- + grub-core/kern/ieee1275/openfw.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c +index e2ffec32d..3bbd07d95 100644 +--- a/grub-core/kern/ieee1275/openfw.c ++++ b/grub-core/kern/ieee1275/openfw.c +@@ -354,13 +354,16 @@ static char * + grub_ieee1275_get_devargs (const char *path) + { + char *colon = grub_strchr (path, ':'); +- char *colon_check = colon; + +- /* Find the last occurence of colon */ +- while(colon_check){ +- colon = colon_check; +- colon_check = grub_strchr (colon+1, ':'); +- } ++ /* Use the same logic in grub_ieee1275_get_devname for nvme-of arguments */ ++ if (grub_strstr(path, "nvme-of")) ++ { ++ char *namespace_split = grub_strstr(path,"/namespace@"); ++ if (namespace_split) ++ colon = grub_strchr (namespace_split, ':'); ++ else ++ colon = NULL; ++ } + + if (! colon) + return 0; +-- +2.39.2 + diff --git a/0001-protectors-Add-key-protectors-framework.patch b/0001-protectors-Add-key-protectors-framework.patch new file mode 100644 index 0000000..e412879 --- /dev/null +++ b/0001-protectors-Add-key-protectors-framework.patch @@ -0,0 +1,177 @@ +From 5affde982dea827580e36ccc658e439397f51ce8 Mon Sep 17 00:00:00 2001 +From: Hernan Gatta +Date: Tue, 1 Feb 2022 05:02:53 -0800 +Subject: [PATCH 1/5] protectors: Add key protectors framework + +A key protector encapsulates functionality to retrieve an unlocking key +for a fully-encrypted disk from a specific source. A key protector +module registers itself with the key protectors framework when it is +loaded and unregisters when unloaded. Additionally, a key protector may +accept parameters that describe how it should operate. + +The key protectors framework, besides offering registration and +unregistration functions, also offers a one-stop routine for finding and +invoking a key protector by name. If a key protector with the specified +name exists and if an unlocking key is successfully retrieved by it, the +function returns to the caller the retrieved key and its length. + +Signed-off-by: Hernan Gatta +Signed-off-by: Gary Lin +--- + grub-core/Makefile.am | 1 + + grub-core/Makefile.core.def | 1 + + grub-core/kern/protectors.c | 75 +++++++++++++++++++++++++++++++++++++ + include/grub/protector.h | 48 ++++++++++++++++++++++++ + 4 files changed, 125 insertions(+) + create mode 100644 grub-core/kern/protectors.c + create mode 100644 include/grub/protector.h + +--- a/grub-core/Makefile.am ++++ b/grub-core/Makefile.am +@@ -90,6 +90,7 @@ + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/parser.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/partition.h ++KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/protector.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/stack_protector.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/term.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/time.h +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -149,6 +149,7 @@ + common = kern/misc.c; + common = kern/parser.c; + common = kern/partition.c; ++ common = kern/protectors.c; + common = kern/rescue_parser.c; + common = kern/rescue_reader.c; + common = kern/term.c; +--- /dev/null ++++ b/grub-core/kern/protectors.c +@@ -0,0 +1,75 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++ ++struct grub_key_protector *grub_key_protectors = NULL; ++ ++grub_err_t ++grub_key_protector_register (struct grub_key_protector *protector) ++{ ++ if (protector == NULL || protector->name == NULL || grub_strlen(protector->name) == 0) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ if (grub_key_protectors && ++ grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors), ++ protector->name)) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ grub_list_push (GRUB_AS_LIST_P (&grub_key_protectors), ++ GRUB_AS_LIST (protector)); ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_key_protector_unregister (struct grub_key_protector *protector) ++{ ++ if (protector == NULL) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ grub_list_remove (GRUB_AS_LIST (protector)); ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_key_protector_recover_key (const char *protector, grub_uint8_t **key, ++ grub_size_t *key_size) ++{ ++ struct grub_key_protector *kp = NULL; ++ ++ if (grub_key_protectors == NULL) ++ return GRUB_ERR_OUT_OF_RANGE; ++ ++ if (protector == NULL || grub_strlen (protector) == 0) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ kp = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors), ++ protector); ++ if (kp == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, ++ N_("A key protector with name '%s' could not be found. " ++ "Is the name spelled correctly and is the " ++ "corresponding module loaded?"), protector); ++ ++ return kp->recover_key (key, key_size); ++} +--- /dev/null ++++ b/include/grub/protector.h +@@ -0,0 +1,48 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_PROTECTOR_HEADER ++#define GRUB_PROTECTOR_HEADER 1 ++ ++#include ++#include ++ ++struct grub_key_protector ++{ ++ struct grub_key_protector *next; ++ struct grub_key_protector **prev; ++ ++ const char *name; ++ ++ grub_err_t (*recover_key) (grub_uint8_t **key, grub_size_t *key_size); ++}; ++ ++extern struct grub_key_protector *EXPORT_VAR (grub_key_protectors); ++ ++grub_err_t ++EXPORT_FUNC (grub_key_protector_register) (struct grub_key_protector *protector); ++ ++grub_err_t ++EXPORT_FUNC (grub_key_protector_unregister) (struct grub_key_protector *protector); ++ ++grub_err_t ++EXPORT_FUNC (grub_key_protector_recover_key) (const char *protector, ++ grub_uint8_t **key, ++ grub_size_t *key_size); ++ ++#endif /* ! GRUB_PROTECTOR_HEADER */ diff --git a/0001-protectors-Implement-NV-index.patch b/0001-protectors-Implement-NV-index.patch new file mode 100644 index 0000000..e183767 --- /dev/null +++ b/0001-protectors-Implement-NV-index.patch @@ -0,0 +1,80 @@ +From c3efb4ecbe91b63c127b92122dad3fa53d4efc69 Mon Sep 17 00:00:00 2001 +From: Patrick Colp +Date: Mon, 31 Jul 2023 07:01:45 -0700 +Subject: [PATCH 1/4] protectors: Implement NV index + +Currently with the TPM2 protector, only SRK mode is supported and +NV index support is just a stub. Implement the NV index option. + +Note: This only extends support on the unseal path. grub2_protect +has not been updated. tpm2-tools can be used to insert a key into +the NV index. + +An example of inserting a key using tpm2-tools: + + # Get random key. + tpm2_getrandom 32 > key.dat + + # Create primary object. + tpm2_createprimary -C o -g sha256 -G rsa -c primary.ctx + + # Create policy object. `pcrs.dat` contains the PCR values to seal against. + tpm2_startauthsession -S session.dat + tpm2_policypcr -S session.dat -l sha256:7,11 -f pcrs.dat -L policy.dat + tpm2_flushcontext session.dat + + # Seal key into TPM. + cat key.dat | tpm2_create -C primary.ctx -u key.pub -r key.priv -L policy.dat -i- + tpm2_load -C primary.ctx -u key.pub -r key.priv -n sealing.name -c sealing.ctx + tpm2_evictcontrol -C o -c sealing.ctx 0x81000000 + +Then to unseal the key in grub, add this to grub.cfg: + + tpm2_key_protector_init --mode=nv --nvindex=0x81000000 --pcrs=7,11 + cryptomount -u --protector tpm2 + +Signed-off-by: Patrick Colp +--- + grub-core/tpm2/module.c | 25 ++++++++++++++++++++----- + 1 file changed, 20 insertions(+), 5 deletions(-) + +diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c +index 5274296b7..d3a64187a 100644 +--- a/grub-core/tpm2/module.c ++++ b/grub-core/tpm2/module.c +@@ -757,12 +757,27 @@ static grub_err_t + grub_tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx, + grub_uint8_t **key, grub_size_t *key_size) + { +- (void)ctx; +- (void)key; +- (void)key_size; ++ TPM_HANDLE sealed_handle = ctx->nv; ++ tpm2key_policy_t policy_seq = NULL; ++ grub_err_t err; ++ ++ /* Create a basic policy sequence based on the given PCR selection */ ++ err = grub_tpm2_protector_simple_policy_seq (ctx, &policy_seq); ++ if (err != GRUB_ERR_NONE) ++ goto exit; ++ ++ err = grub_tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size); ++ ++ /* Pop error messages on success */ ++ if (err == GRUB_ERR_NONE) ++ while (grub_error_pop ()); ++ ++exit: ++ TPM2_FlushContext (sealed_handle); + +- return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, +- N_("NV Index mode is not implemented yet")); ++ grub_tpm2key_free_policy_seq (policy_seq); ++ ++ return err; + } + + static grub_err_t +-- +2.35.3 + diff --git a/0001-templates-Follow-the-path-of-usr-merged-kernel-confi.patch b/0001-templates-Follow-the-path-of-usr-merged-kernel-confi.patch new file mode 100644 index 0000000..c2b9f08 --- /dev/null +++ b/0001-templates-Follow-the-path-of-usr-merged-kernel-confi.patch @@ -0,0 +1,40 @@ +From 60d1d3b959e72c2cbd014be311c350a9b11b1289 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Tue, 7 Sep 2021 10:06:50 +0800 +Subject: [PATCH] templates: Follow the path of usr merged kernel config + +The background for usr merge can be found at: + +https://www.freedesktop.org/wiki/Software/systemd/TheCaseForTheUsrMerge/ + +This patch adapts related mkconfig scripts to follow the usr merge for +looking up kernel configs. + +Signed-off-by: Michael Chang +--- + util/grub.d/10_linux.in | 2 +- + util/grub.d/20_linux_xen.in | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/util/grub.d/10_linux.in ++++ b/util/grub.d/10_linux.in +@@ -351,7 +351,7 @@ + fi + + config= +- for i in "${dirname}/config-${version}" "${dirname}/config-${alt_version}" "/etc/kernels/kernel-config-${version}" ; do ++ for i in "${dirname}/config-${version}" "${dirname}/config-${alt_version}" "/etc/kernels/kernel-config-${version}" "/usr/lib/modules/${version}/config" ; do + if test -e "${i}" ; then + config="${i}" + break +--- a/util/grub.d/20_linux_xen.in ++++ b/util/grub.d/20_linux_xen.in +@@ -307,7 +307,7 @@ + version=$(echo $basename | sed -e "s,^[^0-9]*-,,g") + dirname=$(dirname $i) + config= +- for j in "${dirname}/config-${version}" "${dirname}/config-${alt_version}" "/etc/kernels/kernel-config-${version}" ; do ++ for j in "${dirname}/config-${version}" "${dirname}/config-${alt_version}" "/etc/kernels/kernel-config-${version}" "/usr/lib/modules/${version}/config" ; do + if test -e "${j}" ; then + config="${j}" + break diff --git a/0001-tpm2-Add-TPM2-types-structures-and-command-constants.patch b/0001-tpm2-Add-TPM2-types-structures-and-command-constants.patch new file mode 100644 index 0000000..dca72f6 --- /dev/null +++ b/0001-tpm2-Add-TPM2-types-structures-and-command-constants.patch @@ -0,0 +1,204 @@ +From 5a417f32f1afe0ffca7f5cbff67145a157b1589b Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Tue, 7 Feb 2023 18:31:12 +0800 +Subject: [PATCH 1/4] tpm2: Add TPM2 types, structures, and command constants + +Add new TPM2 types and structures as the preparation to support +authorized policy. + +* New types: + TPM_ALG_ECDAA, TPM_ALG_ECDSA, TPM_ALG_ECSCHNORR, TPM_ALG_RSASSA, + TPM_ALG_RSAPSS, TPM_ALG_SM2, and TPMI_ALG_SIG_SCHEME + +* New structures: + TPMS_EMPTY, TPMS_SIGNATURE_RSA, TPMS_SIGNATURE_ECC, + TPMS_SIGNATURE_ECDSA, TPMS_SIGNATURE_ECDAA, TPMS_SIGNATURE_SM2, + TPMS_SIGNATURE_ECSCHNORR, TPMU_SIGNATURE, and TPMT_TK_VERIFIED + +* New command constants: + TPM_CC_LoadExternal, TPM_CC_HashSequenceStart, TPM_CC_SequenceUpdate, + TPM_CC_SequenceComplete, TPM_CC_Hash, TPM_CC_VerifySignature, + TPM_CC_PolicyAuthorize + +Signed-off-by: Gary Lin +--- + include/grub/tpm2/internal/structs.h | 86 ++++++++++++++++++++++++++++ + include/grub/tpm2/internal/types.h | 42 +++++++++----- + 2 files changed, 114 insertions(+), 14 deletions(-) + +diff --git a/include/grub/tpm2/internal/structs.h b/include/grub/tpm2/internal/structs.h +index 72d71eb70..db9eb6cf6 100644 +--- a/include/grub/tpm2/internal/structs.h ++++ b/include/grub/tpm2/internal/structs.h +@@ -672,4 +672,90 @@ struct TPMT_TK_CREATION + }; + typedef struct TPMT_TK_CREATION TPMT_TK_CREATION; + ++/* TPMS_EMPTY Structure */ ++struct TPMS_EMPTY { ++ grub_uint8_t empty[1]; /* a structure with no member */ ++}; ++typedef struct TPMS_EMPTY TPMS_EMPTY; ++ ++/* TPMS_SIGNATURE_RSA Structure */ ++struct TPMS_SIGNATURE_RSA { ++ TPMI_ALG_HASH hash; ++ TPM2B_PUBLIC_KEY_RSA sig; ++}; ++typedef struct TPMS_SIGNATURE_RSA TPMS_SIGNATURE_RSA; ++ ++/* Definition of Types for RSA Signature */ ++typedef TPMS_SIGNATURE_RSA TPMS_SIGNATURE_RSASSA; ++typedef TPMS_SIGNATURE_RSA TPMS_SIGNATURE_RSAPSS; ++ ++/* TPMS_SIGNATURE_ECC Structure */ ++struct TPMS_SIGNATURE_ECC { ++ TPMI_ALG_HASH hash; ++ TPM2B_ECC_PARAMETER signatureR; ++ TPM2B_ECC_PARAMETER signatureS; ++}; ++typedef struct TPMS_SIGNATURE_ECC TPMS_SIGNATURE_ECC; ++ ++/* Definition of Types for ECC TPMS_SIGNATURE_ECC */ ++typedef TPMS_SIGNATURE_ECC TPMS_SIGNATURE_ECDSA; ++typedef TPMS_SIGNATURE_ECC TPMS_SIGNATURE_ECDAA; ++typedef TPMS_SIGNATURE_ECC TPMS_SIGNATURE_SM2; ++typedef TPMS_SIGNATURE_ECC TPMS_SIGNATURE_ECSCHNORR; ++ ++/* TPMU_SIGNATURE Structure */ ++union TPMU_SIGNATURE { ++ TPMS_SIGNATURE_RSASSA rsassa; ++ TPMS_SIGNATURE_RSAPSS rsapss; ++ TPMS_SIGNATURE_ECDSA ecdsa; ++ TPMS_SIGNATURE_ECDAA ecdaa; ++ TPMS_SIGNATURE_SM2 sm2; ++ TPMS_SIGNATURE_ECSCHNORR ecschnorr; ++ TPMT_HA hmac; ++ TPMS_SCHEME_HASH any; ++ TPMS_EMPTY null; ++}; ++typedef union TPMU_SIGNATURE TPMU_SIGNATURE; ++ ++/* TPMT_SIGNATURE Structure */ ++struct TPMT_SIGNATURE { ++ TPMI_ALG_SIG_SCHEME sigAlg; ++ TPMU_SIGNATURE signature; ++}; ++typedef struct TPMT_SIGNATURE TPMT_SIGNATURE; ++ ++static inline TPMI_ALG_HASH ++TPMT_SIGNATURE_get_hash_alg (TPMT_SIGNATURE *sig) ++{ ++ switch (sig->sigAlg) ++ { ++ case TPM_ALG_RSASSA: ++ return sig->signature.rsassa.hash; ++ case TPM_ALG_RSAPSS: ++ return sig->signature.rsapss.hash; ++ case TPM_ALG_ECDSA: ++ return sig->signature.ecdsa.hash; ++ case TPM_ALG_ECDAA: ++ return sig->signature.ecdaa.hash; ++ case TPM_ALG_SM2: ++ return sig->signature.sm2.hash; ++ case TPM_ALG_ECSCHNORR: ++ return sig->signature.ecschnorr.hash; ++ case TPM_ALG_HMAC: ++ return sig->signature.hmac.hashAlg; ++ default: ++ break; ++ } ++ ++ return TPM_ALG_NULL; ++} ++ ++/* TPMT_TK_VERIFIED Structure */ ++struct TPMT_TK_VERIFIED { ++ TPM_ST tag; ++ TPMI_RH_HIERARCHY hierarchy; ++ TPM2B_DIGEST digest; ++}; ++typedef struct TPMT_TK_VERIFIED TPMT_TK_VERIFIED; ++ + #endif /* ! GRUB_TPM2_INTERNAL_STRUCTS_HEADER */ +diff --git a/include/grub/tpm2/internal/types.h b/include/grub/tpm2/internal/types.h +index 9714f75d4..a1902ef0c 100644 +--- a/include/grub/tpm2/internal/types.h ++++ b/include/grub/tpm2/internal/types.h +@@ -181,6 +181,9 @@ typedef grub_uint16_t TPM_ALG_ID; + #define TPM_ALG_CFB ((TPM_ALG_ID) 0x0043) + #define TPM_ALG_ECB ((TPM_ALG_ID) 0x0044) + #define TPM_ALG_ECC ((TPM_ALG_ID) 0x0023) ++#define TPM_ALG_ECDAA ((TPM_ALG_ID) 0x001A) ++#define TPM_ALG_ECDSA ((TPM_ALG_ID) 0x0018) ++#define TPM_ALG_ECSCHNORR ((TPM_ALG_ID) 0x001C) + #define TPM_ALG_HMAC ((TPM_ALG_ID) 0x0005) + #define TPM_ALG_KDF1_SP800_108 ((TPM_ALG_ID) 0x0022) + #define TPM_ALG_KDF1_SP800_56A ((TPM_ALG_ID) 0x0020) +@@ -189,10 +192,13 @@ typedef grub_uint16_t TPM_ALG_ID; + #define TPM_ALG_MGF1 ((TPM_ALG_ID) 0x0007) + #define TPM_ALG_NULL ((TPM_ALG_ID) 0x0010) + #define TPM_ALG_RSA ((TPM_ALG_ID) 0x0001) ++#define TPM_ALG_RSASSA ((TPM_ALG_ID) 0x0014) ++#define TPM_ALG_RSAPSS ((TPM_ALG_ID) 0x0016) + #define TPM_ALG_SHA1 ((TPM_ALG_ID) 0x0004) + #define TPM_ALG_SHA256 ((TPM_ALG_ID) 0x000B) + #define TPM_ALG_SHA384 ((TPM_ALG_ID) 0x000C) + #define TPM_ALG_SHA512 ((TPM_ALG_ID) 0x000D) ++#define TPM_ALG_SM2 ((TPM_ALG_ID) 0x001B) + #define TPM_ALG_SM3_256 ((TPM_ALG_ID) 0x0012) + #define TPM_ALG_SM4 ((TPM_ALG_ID) 0x0013) + #define TPM_ALG_SYMCIPHER ((TPM_ALG_ID) 0x0025) +@@ -299,20 +305,27 @@ typedef grub_uint16_t TPM2_ECC_CURVE; + /* TPM_CC Constants */ + typedef grub_uint32_t TPM_CC; + +-#define TPM_CC_EvictControl ((TPM_CC) 0x00000120) +-#define TPM_CC_CreatePrimary ((TPM_CC) 0x00000131) +-#define TPM_CC_Create ((TPM_CC) 0x00000153) +-#define TPM_CC_FlushContext ((TPM_CC) 0x00000165) +-#define TPM_CC_ReadPublic ((TPM_CC) 0x00000173) +-#define TPM_CC_StartAuthSession ((TPM_CC) 0x00000176) +-#define TPM_CC_PolicyPCR ((TPM_CC) 0x0000017f) +-#define TPM_CC_NV_Read ((TPM_CC) 0x0000014e) +-#define TPM_CC_NV_ReadPublic ((TPM_CC) 0x00000169) +-#define TPM_CC_GetCapability ((TPM_CC) 0x0000017a) +-#define TPM_CC_PCR_Read ((TPM_CC) 0x0000017e) +-#define TPM_CC_Load ((TPM_CC) 0x00000157) +-#define TPM_CC_Unseal ((TPM_CC) 0x0000015e) +-#define TPM_CC_PolicyGetDigest ((TPM_CC) 0x00000189) ++#define TPM_CC_EvictControl ((TPM_CC) 0x00000120) ++#define TPM_CC_CreatePrimary ((TPM_CC) 0x00000131) ++#define TPM_CC_Create ((TPM_CC) 0x00000153) ++#define TPM_CC_FlushContext ((TPM_CC) 0x00000165) ++#define TPM_CC_ReadPublic ((TPM_CC) 0x00000173) ++#define TPM_CC_StartAuthSession ((TPM_CC) 0x00000176) ++#define TPM_CC_PolicyPCR ((TPM_CC) 0x0000017f) ++#define TPM_CC_NV_Read ((TPM_CC) 0x0000014e) ++#define TPM_CC_NV_ReadPublic ((TPM_CC) 0x00000169) ++#define TPM_CC_GetCapability ((TPM_CC) 0x0000017a) ++#define TPM_CC_PCR_Read ((TPM_CC) 0x0000017e) ++#define TPM_CC_Load ((TPM_CC) 0x00000157) ++#define TPM_CC_LoadExternal ((TPM_CC) 0x00000167) ++#define TPM_CC_Unseal ((TPM_CC) 0x0000015e) ++#define TPM_CC_PolicyGetDigest ((TPM_CC) 0x00000189) ++#define TPM_CC_HashSequenceStart ((TPM_CC) 0x00000186) ++#define TPM_CC_SequenceUpdate ((TPM_CC) 0x0000015c) ++#define TPM_CC_SequenceComplete ((TPM_CC) 0x0000013e) ++#define TPM_CC_Hash ((TPM_CC) 0x0000017d) ++#define TPM_CC_VerifySignature ((TPM_CC) 0x00000177) ++#define TPM_CC_PolicyAuthorize ((TPM_CC) 0x0000016a) + + /* Hash algorithm sizes */ + #define TPM_SHA1_DIGEST_SIZE 20 +@@ -354,6 +367,7 @@ typedef TPM_ALG_ID TPMI_ALG_ECC_SCHEME; + typedef TPM_ALG_ID TPMI_ALG_ASYM_SCHEME; + typedef TPM_ALG_ID TPMI_ALG_RSA_SCHEME; + typedef TPM_ALG_ID TPMI_ALG_SYM; ++typedef TPM_ALG_ID TPMI_ALG_SIG_SCHEME; + + /* TPM_KEY_BITS Type */ + typedef grub_uint16_t TPM_KEY_BITS; +-- +2.35.3 + diff --git a/0001-xen_boot-add-missing-grub_arch_efi_linux_load_image_.patch b/0001-xen_boot-add-missing-grub_arch_efi_linux_load_image_.patch new file mode 100644 index 0000000..ee5e3b9 --- /dev/null +++ b/0001-xen_boot-add-missing-grub_arch_efi_linux_load_image_.patch @@ -0,0 +1,83 @@ +From 6c06378c1bf6ae21788427e62ab0011b7f1bc2f0 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Fri, 25 Nov 2022 16:11:24 +0800 +Subject: [PATCH] xen_boot: add missing grub_arch_efi_linux_load_image_header + +The new xen_boot module has used grub_arch_efi_linux_load_image_header +exported by grub-core/loader/arm64/linux.c. It is not a problem for +upstream but many downstream projects may not use it and take +grub-core/loader/arm64/efi/linux.c as a replacement as PE entry is the +preferred way in combination with shim loader. + +This patch did a trivial workaround just adding back the dropped +defintion to the xen_boot itself. + +Signed-off-by: Michael Chang +--- + grub-core/loader/arm64/xen_boot.c | 50 +++++++++++++++++++++++++++++++ + 1 file changed, 50 insertions(+) + +diff --git a/grub-core/loader/arm64/xen_boot.c b/grub-core/loader/arm64/xen_boot.c +index 26e1472c9..b82a2db89 100644 +--- a/grub-core/loader/arm64/xen_boot.c ++++ b/grub-core/loader/arm64/xen_boot.c +@@ -84,6 +84,56 @@ static int loaded; + static struct xen_boot_binary *xen_hypervisor; + static struct xen_boot_binary *module_head; + ++/* The function is exported by grub-core/loader/arm64/linux.c that is not built ++ * because we use PE entry provided by grub-core/loader/arm64/efi/linux.c ++ */ ++static bool initrd_use_loadfile2 = false; ++ ++grub_err_t ++grub_arch_efi_linux_load_image_header (grub_file_t file, ++ struct linux_arch_kernel_header * lh) ++{ ++ grub_file_seek (file, 0); ++ if (grub_file_read (file, lh, sizeof (*lh)) < (grub_ssize_t) sizeof (*lh)) ++ return grub_error(GRUB_ERR_FILE_READ_ERROR, "failed to read Linux image header"); ++ ++ if ((lh->code0 & 0xffff) != GRUB_PE32_MAGIC) ++ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, ++ N_("plain image kernel not supported - rebuild with CONFIG_(U)EFI_STUB enabled")); ++ ++ grub_dprintf ("linux", "UEFI stub kernel:\n"); ++ grub_dprintf ("linux", "PE/COFF header @ %08x\n", lh->hdr_offset); ++ ++ /* ++ * The PE/COFF spec permits the COFF header to appear anywhere in the file, so ++ * we need to double check whether it was where we expected it, and if not, we ++ * must load it from the correct offset into the pe_image_header field of ++ * struct linux_arch_kernel_header. ++ */ ++ if ((grub_uint8_t *) lh + lh->hdr_offset != (grub_uint8_t *) &lh->pe_image_header) ++ { ++ if (grub_file_seek (file, lh->hdr_offset) == (grub_off_t) -1 ++ || grub_file_read (file, &lh->pe_image_header, ++ sizeof (struct grub_pe_image_header)) ++ != sizeof (struct grub_pe_image_header)) ++ return grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read COFF image header"); ++ } ++ ++ /* ++ * Linux kernels built for any architecture are guaranteed to support the ++ * LoadFile2 based initrd loading protocol if the image version is >= 1. ++ */ ++ if (lh->pe_image_header.optional_header.major_image_version >= 1) ++ initrd_use_loadfile2 = true; ++ else ++ initrd_use_loadfile2 = false; ++ ++ grub_dprintf ("linux", "LoadFile2 initrd loading %sabled\n", ++ initrd_use_loadfile2 ? "en" : "dis"); ++ ++ return GRUB_ERR_NONE; ++} ++ + static __inline grub_addr_t + xen_boot_address_align (grub_addr_t start, grub_size_t align) + { +-- +2.41.0 + diff --git a/0002-AUDIT-0-http-boot-tracker-bug.patch b/0002-AUDIT-0-http-boot-tracker-bug.patch new file mode 100644 index 0000000..7b1a798 --- /dev/null +++ b/0002-AUDIT-0-http-boot-tracker-bug.patch @@ -0,0 +1,60 @@ +From b5c3492f31a98f5ef0f9bec2c0665ad0b71ad5cb Mon Sep 17 00:00:00 2001 +From: Sebastian Krahmer +Date: Tue, 28 Nov 2017 17:24:38 +0800 +Subject: [PATCH] AUDIT-0: http boot tracker bug + +Fixing a memory leak in case of error, and a integer overflow, leading to a +heap overflow due to overly large chunk sizes. + +We need to check against some maximum value, otherwise values like 0xffffffff +will eventually lead in the allocation functions to small sized buffers, since +the len is rounded up to the next reasonable alignment. The following memcpy +will then smash the heap, leading to RCE. + +This is no big issue for pure http boot, since its going to execute an +untrusted kernel anyway, but it will break trusted boot scenarios, where only +signed code is allowed to be executed. + +v2: Fix GCC 13 build failure (bsc#1201089) + +Signed-off-by: Michael Chang +--- + grub-core/net/efi/net.c | 4 +++- + grub-core/net/http.c | 5 ++++- + 2 files changed, 7 insertions(+), 2 deletions(-) + +--- a/grub-core/net/efi/net.c ++++ b/grub-core/net/efi/net.c +@@ -654,8 +654,10 @@ + + rd = efi_net_interface (read, file, chunk, sz); + +- if (rd <= 0) ++ if (rd <= 0) { ++ grub_free (chunk); + return rd; ++ } + + if (buf) + { +--- a/grub-core/net/http.c ++++ b/grub-core/net/http.c +@@ -31,7 +31,8 @@ + + enum + { +- HTTP_PORT = 80 ++ HTTP_PORT = 80, ++ HTTP_MAX_CHUNK_SIZE = GRUB_INT_MAX + }; + + +@@ -86,6 +87,8 @@ + if (data->in_chunk_len == 2) + { + data->chunk_rem = grub_strtoul (ptr, 0, 16); ++ if (data->chunk_rem > HTTP_MAX_CHUNK_SIZE) ++ return GRUB_ERR_NET_PACKET_TOO_BIG; + grub_errno = GRUB_ERR_NONE; + if (data->chunk_rem == 0) + { diff --git a/0002-Add-grub_disk_write_tail-helper-function.patch b/0002-Add-grub_disk_write_tail-helper-function.patch new file mode 100644 index 0000000..fda6d9d --- /dev/null +++ b/0002-Add-grub_disk_write_tail-helper-function.patch @@ -0,0 +1,53 @@ +From c0d00403a297d6023eab6189ba87dc8a3f6d1e85 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Mon, 7 Feb 2022 20:44:40 +0800 +Subject: [PATCH 2/5] Add grub_disk_write_tail helper function + +This helps in writing data to partition where the end of buffer is +aligned to end of partition. + +Signed-off-by: Michael Chang +--- + grub-core/lib/disk.c | 18 ++++++++++++++++++ + include/grub/disk.h | 3 +++ + 2 files changed, 21 insertions(+) + +--- a/grub-core/lib/disk.c ++++ b/grub-core/lib/disk.c +@@ -52,6 +52,24 @@ + } + + grub_err_t ++grub_disk_write_tail (grub_disk_t disk, grub_size_t size, const void *buf) ++{ ++ grub_partition_t part; ++ grub_disk_addr_t sector; ++ grub_off_t offset; ++ ++ if (!disk->partition) ++ return GRUB_ERR_NONE; ++ ++ part = disk->partition; ++ sector = part->len; ++ sector -= (size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS; ++ offset = size & (GRUB_DISK_SECTOR_SIZE - 1); ++ ++ return grub_disk_write (disk, sector, offset, size, buf); ++} ++ ++grub_err_t + grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector, + grub_off_t offset, grub_size_t size, const void *buf) + { +--- a/include/grub/disk.h ++++ b/include/grub/disk.h +@@ -252,6 +252,9 @@ + grub_off_t offset, + grub_size_t size, + void *buf); ++grub_err_t grub_disk_write_tail (grub_disk_t disk, ++ grub_size_t size, ++ const void *buf); + grub_err_t grub_disk_write (grub_disk_t disk, + grub_disk_addr_t sector, + grub_off_t offset, diff --git a/0002-Arm-check-for-the-PE-magic-for-the-compiled-arch.patch b/0002-Arm-check-for-the-PE-magic-for-the-compiled-arch.patch new file mode 100644 index 0000000..cf62fcb --- /dev/null +++ b/0002-Arm-check-for-the-PE-magic-for-the-compiled-arch.patch @@ -0,0 +1,57 @@ +From 337b3d963d28b3544e8817428fb68ca559613a39 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Thu, 9 Sep 2021 10:59:28 -0400 +Subject: [PATCH 2/2] Arm: check for the PE magic for the compiled arch + +In "arm64: Fix EFI loader kernel image allocation", Ben fixed the kernel +alignment to match the alignment given in the PE header. In doing so, a +check for valid PE magic was added, which was hard-coded to the value +seen on Aarch64 (GRUB_PE32_PE64_MAGIC). + +Unfortunately, this code is shared between 64-bit and 32-bit, and so +that value broke 32-bit Arm systems. + +This patch adds a constant definition for GRUB_PE32_PEXX_MAGIC, which is +either GRUB_PE32_PE64_MAGIC or GRUB_PE32_PE32_MAGIC, depending on which +platform is being built, and uses it in the header magic check. + +Resolves: rhbz#2000756 + +Signed-off-by: Peter Jones +--- + grub-core/loader/arm64/efi/linux.c | 2 +- + include/grub/arm/linux.h | 1 + + include/grub/arm64/linux.h | 1 + + 3 files changed, 3 insertions(+), 1 deletion(-) + +--- a/grub-core/loader/arm64/efi/linux.c ++++ b/grub-core/loader/arm64/efi/linux.c +@@ -376,7 +376,7 @@ + + pe = (void *)((unsigned long)kernel + lh->hdr_offset); + +- if (pe->opt.magic != GRUB_PE32_PE64_MAGIC) ++ if (pe->opt.magic != GRUB_PE32_PEXX_MAGIC) + return grub_error(GRUB_ERR_BAD_OS, "Invalid PE optional header magic"); + + *total_size = pe->opt.image_size; +--- a/include/grub/arm/linux.h ++++ b/include/grub/arm/linux.h +@@ -33,6 +33,7 @@ + }; + + #if defined(__arm__) ++# define GRUB_PE32_PEXX_MAGIC GRUB_PE32_PE32_MAGIC + # define grub_armxx_linux_pe_header grub_arm_linux_pe_header + #endif + +--- a/include/grub/arm64/linux.h ++++ b/include/grub/arm64/linux.h +@@ -33,6 +33,7 @@ + + #if defined(__aarch64__) + # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM64_MAGIC_SIGNATURE ++# define GRUB_PE32_PEXX_MAGIC GRUB_PE32_PE64_MAGIC + # define grub_armxx_linux_pe_header grub_arm64_linux_pe_header + #endif + diff --git a/0002-Fix-race-in-EFI-validation.patch b/0002-Fix-race-in-EFI-validation.patch new file mode 100644 index 0000000..c58a65f --- /dev/null +++ b/0002-Fix-race-in-EFI-validation.patch @@ -0,0 +1,92 @@ +From e72dcb40356f56efd86ab88c2f5cb7411d1e898b Mon Sep 17 00:00:00 2001 +From: Matthew Garrett +Date: Tue, 14 Jul 2015 16:58:51 -0700 +Subject: [PATCH 02/11] Fix race in EFI validation + +--- + grub-core/loader/i386/efi/linux.c | 40 +++++++------------------------ + 1 file changed, 9 insertions(+), 31 deletions(-) + +diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c +index 06814cae3..1e09c88ab 100644 +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -154,7 +154,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + grub_file_t file = 0; + struct linux_i386_kernel_header lh; + grub_ssize_t len, start, filelen; +- void *kernel; ++ void *kernel = NULL; + grub_err_t err; + + grub_dl_ref (my_mod); +@@ -185,10 +185,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + goto fail; + } + +- grub_file_seek (file, 0); +- +- grub_free(kernel); +- + params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384)); + + if (! params) +@@ -199,13 +195,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + + grub_memset (params, 0, 16384); + +- if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) +- { +- if (!grub_errno) +- grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), +- argv[0]); +- goto fail; +- } ++ grub_memcpy (&lh, kernel, sizeof (lh)); + + if (lh.boot_flag != grub_cpu_to_le16 (0xaa55)) + { +@@ -271,26 +261,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + goto fail; + } + +- if (grub_file_seek (file, start) == (grub_off_t) -1) +- { +- grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), +- argv[0]); +- goto fail; +- } +- +- if (grub_file_read (file, kernel_mem, len) != len && !grub_errno) +- { +- grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), +- argv[0]); +- } +- +- if (grub_errno == GRUB_ERR_NONE) +- { +- grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); +- loaded = 1; +- lh.code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem; +- } ++ grub_memcpy (kernel_mem, (char *)kernel + start, len); ++ grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); ++ loaded=1; + ++ lh.code32_start = (grub_uint32_t)(grub_uint64_t) kernel_mem; + /* Grub linuxefi erroneously initialize linux's boot_params with non-zero values. (bsc#1025563) + + From https://www.kernel.org/doc/Documentation/x86/boot.txt: +@@ -307,6 +282,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + if (file) + grub_file_close (file); + ++ if (kernel) ++ grub_free (kernel); ++ + if (grub_errno != GRUB_ERR_NONE) + { + grub_dl_unref (my_mod); +-- +2.31.1 + diff --git a/0002-Mark-environmet-blocks-as-used-for-image-embedding.patch b/0002-Mark-environmet-blocks-as-used-for-image-embedding.patch new file mode 100644 index 0000000..119dc57 --- /dev/null +++ b/0002-Mark-environmet-blocks-as-used-for-image-embedding.patch @@ -0,0 +1,57 @@ +From f01314a822dbe9ad39b2f7d0f3717ef6e4c24f4a Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Fri, 15 Apr 2022 21:45:04 +0800 +Subject: [PATCH 2/2] Mark environmet blocks as used for image embedding. + +Now that grub will attempt to use full btrfs bootloader area, the +embedded image could have overlapped with environment blocks if it's +size grows too much. Let's define a dedicated area for environment +blocks to the used block mappings for the embedding process so it can be +skipped. + +Signed-off-by: Michael Chang +--- + grub-core/fs/btrfs.c | 3 ++- + include/grub/fs.h | 2 ++ + util/grub-editenv.c | 2 +- + 3 files changed, 5 insertions(+), 2 deletions(-) + +--- a/grub-core/fs/btrfs.c ++++ b/grub-core/fs/btrfs.c +@@ -2637,7 +2637,7 @@ + + static const struct { + struct embed_region available; +- struct embed_region used[6]; ++ struct embed_region used[7]; + } btrfs_head = { + .available = {0, GRUB_DISK_KiB_TO_SECTORS (1024)}, /* The first 1 MiB. */ + .used = { +@@ -2645,6 +2645,7 @@ + {GRUB_DISK_KiB_TO_SECTORS (64) - 1, 1}, /* Overflow guard. */ + {GRUB_DISK_KiB_TO_SECTORS (64), GRUB_DISK_KiB_TO_SECTORS (4)}, /* 4 KiB superblock. */ + {GRUB_DISK_KiB_TO_SECTORS (68), 1}, /* Overflow guard. */ ++ {GRUB_DISK_KiB_TO_SECTORS (ENV_BTRFS_OFFSET) - 1, 3}, /* Environment Block. */ + {GRUB_DISK_KiB_TO_SECTORS (1024) - 1, 1}, /* Overflow guard. */ + {0, 0} /* Array terminator. */ + } +--- a/include/grub/fs.h ++++ b/include/grub/fs.h +@@ -128,4 +128,6 @@ + + grub_fs_t EXPORT_FUNC(grub_fs_probe) (grub_device_t device); + ++#define ENV_BTRFS_OFFSET (256) ++ + #endif /* ! GRUB_FS_HEADER */ +--- a/util/grub-editenv.c ++++ b/util/grub-editenv.c +@@ -128,7 +128,7 @@ + int offset; + int size; + } fs_envblk_spec[] = { +- { "btrfs", 256 * 1024, GRUB_DISK_SECTOR_SIZE }, ++ { "btrfs", ENV_BTRFS_OFFSET * 1024, GRUB_DISK_SECTOR_SIZE }, + { NULL, 0, 0 } + }; + diff --git a/0002-Restrict-cryptsetup-key-file-permission-for-better-s.patch b/0002-Restrict-cryptsetup-key-file-permission-for-better-s.patch new file mode 100644 index 0000000..0501dd6 --- /dev/null +++ b/0002-Restrict-cryptsetup-key-file-permission-for-better-s.patch @@ -0,0 +1,59 @@ +From 9f18541245858f53fea72d8d60304f9015d88b5f Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Fri, 17 Mar 2023 22:00:23 +0800 +Subject: [PATCH 2/2] Restrict cryptsetup key file permission for better + security + +GRUB's default permission 777 for concatenated initrd files was too +permissive for the cryptsetup key file, causing a complaint from +systemd-cryptsetup during boot. This commit replaces the 0777 permission +with a more secure 0400 permission for the key file. + +Signed-off-by: Michael Chang +--- + grub-core/loader/linux.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/grub-core/loader/linux.c ++++ b/grub-core/loader/linux.c +@@ -32,6 +32,7 @@ + char *buf; + char *newc_name; + grub_off_t size; ++ grub_uint32_t mode; + }; + + struct dir +@@ -203,6 +204,7 @@ + grub_memcpy (comp->buf, buf, bufsz); + initrd_ctx->nfiles++; + comp->size = bufsz; ++ comp->mode = 0100400; + if (grub_add (initrd_ctx->size, comp->size, + &initrd_ctx->size)) + goto overflow; +@@ -272,6 +274,7 @@ + grub_initrd_close (initrd_ctx); + return grub_errno; + } ++ initrd_ctx->components[i].mode = 0100777; + name_len = grub_strlen (initrd_ctx->components[i].newc_name) + 1; + if (grub_add (initrd_ctx->size, + ALIGN_UP (sizeof (struct newc_head) + name_len, 4), +@@ -374,6 +377,7 @@ + if (initrd_ctx->components[i].newc_name) + { + grub_size_t dir_size; ++ grub_uint32_t mode = initrd_ctx->components[i].mode; + + if (insert_dir (initrd_ctx->components[i].newc_name, &root, ptr, + &dir_size)) +@@ -385,7 +389,7 @@ + ptr += dir_size; + ptr = make_header (ptr, initrd_ctx->components[i].newc_name, + grub_strlen (initrd_ctx->components[i].newc_name) + 1, +- 0100777, ++ mode, + initrd_ctx->components[i].size); + newc = 1; + } diff --git a/0002-Restrict-file-access-on-cryptodisk-print.patch b/0002-Restrict-file-access-on-cryptodisk-print.patch new file mode 100644 index 0000000..2280169 --- /dev/null +++ b/0002-Restrict-file-access-on-cryptodisk-print.patch @@ -0,0 +1,197 @@ +From 912384e63c1e3b6aa9d90effb71cd535a17da1e2 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Sat, 18 Nov 2023 19:02:31 +0800 +Subject: [PATCH 2/4] Restrict file access on cryptodisk print + +When the encrypted partition is automatically unlocked by TPM, granting +access to the system upon validation of its known good state, there's a +potential vulnerability. Grub gains access to file systems that were +previously inaccessible to the public, enabling certain commands from +the grub console to print content. This arises due to grub lacking +restrictions similar to those imposed by password authentication, which +typically occurs before privileged access is granted. + +Although the automatic unlocking process ensures system integrity and a +secure environment for grub to operate in, it doesn't directly address +the issue of authentication for viewing encrypted partition content. + +This commit addresses this security loophole by implementing a file +filter upon adding a TPM key. The newly added file filter will +specifically verify if the disk is encrypted, denying access and +returning an "Access Denied: prohibited to view encrypted data" error +message to alert the user. + +Since the policy to filter out unwanted commands from leaking encrypted +content is irreversible, it is advisable to make the loaded module +persistent to prevent its removal. + +This enhancement aims to bolster security measures and prevent +unauthorized access to encrypted data. + +Signed-Off-by Michael Chang +--- + grub-core/commands/crypttab.c | 35 ++++++++++++++++++++++++++++++++++- + grub-core/disk/diskfilter.c | 35 +++++++++++++++++++++++++++++++++++ + include/grub/disk.h | 10 ++++++++++ + include/grub/file.h | 1 + + 4 files changed, 80 insertions(+), 1 deletion(-) + +diff --git a/grub-core/commands/crypttab.c b/grub-core/commands/crypttab.c +index 9397bede9..d3acc4b59 100644 +--- a/grub-core/commands/crypttab.c ++++ b/grub-core/commands/crypttab.c +@@ -6,11 +6,39 @@ + #include + #include + #include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + + grub_crypto_key_list_t *cryptokey_lst; + ++static grub_file_t ++grub_nocat_open (grub_file_t io, enum grub_file_type type) ++{ ++ grub_disk_t disk; ++ ++ /* Network device */ ++ if (!io->device->disk) ++ return io; ++ ++ disk = io->device->disk; ++ ++ if (grub_disk_is_crypto (disk)) ++ { ++ switch (type & GRUB_FILE_TYPE_MASK) ++ { ++ case GRUB_FILE_TYPE_CAT: ++ case GRUB_FILE_TYPE_HEXCAT: ++ grub_error (GRUB_ERR_ACCESS_DENIED, N_("prohibited to view encrypted data")); ++ return NULL; ++ default: ++ break; ++ } ++ } ++ ++ return io; ++} ++ + grub_err_t + grub_cryptokey_add_or_update (const char *uuid, const char *key, grub_size_t key_len, const char *path, int is_tpmkey) + { +@@ -48,7 +76,11 @@ grub_cryptokey_add_or_update (const char *uuid, const char *key, grub_size_t key + } + + if (is_tpmkey >= 0) +- cur->is_tpmkey = is_tpmkey; ++ { ++ cur->is_tpmkey = is_tpmkey; ++ if (is_tpmkey) ++ grub_file_filter_register (GRUB_FILE_FILTER_NOCAT, grub_nocat_open); ++ } + + if (!cur->name) + { +@@ -121,6 +153,7 @@ GRUB_MOD_INIT(crypttab) + { + cmd = grub_register_command ("crypttab_entry", grub_cmd_crypttab_entry, + N_("VOLUME-NAME ENCRYPTED-DEVICE KEY-FILE") , N_("No description")); ++ grub_dl_set_persistent (mod); + } + + GRUB_MOD_FINI(crypttab) +diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c +index 5c5fabe1a..b0c1c880d 100644 +--- a/grub-core/disk/diskfilter.c ++++ b/grub-core/disk/diskfilter.c +@@ -558,6 +558,39 @@ find_lv (const char *name) + return NULL; + } + ++static int ++grub_diskfilter_has_cryptodisk (const struct grub_diskfilter_lv *lv) ++{ ++ struct grub_diskfilter_pv *pv; ++ ++ if (!lv) ++ return 0; ++ ++ if (lv->vg->pvs) ++ for (pv = lv->vg->pvs; pv; pv = pv->next) ++ { ++ if (!pv->disk) ++ { ++ grub_dprintf ("diskfilter", _("Couldn't find physical volume `%s'." ++ " Some modules may be missing from core image."), ++ pv->name); ++ continue; ++ } ++ ++ switch (pv->disk->dev->id) ++ { ++ case GRUB_DISK_DEVICE_CRYPTODISK_ID: ++ return 1; ++ case GRUB_DISK_DEVICE_DISKFILTER_ID: ++ return grub_diskfilter_has_cryptodisk (pv->disk->data); ++ default: ++ break; ++ } ++ } ++ ++ return 0; ++} ++ + static grub_err_t + grub_diskfilter_open (const char *name, grub_disk_t disk) + { +@@ -589,6 +622,8 @@ grub_diskfilter_open (const char *name, grub_disk_t disk) + + disk->total_sectors = lv->size; + disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE; ++ disk->is_crypto_diskfilter = grub_diskfilter_has_cryptodisk (lv); ++ + return 0; + } + +diff --git a/include/grub/disk.h b/include/grub/disk.h +index 3b3db6222..63982f16c 100644 +--- a/include/grub/disk.h ++++ b/include/grub/disk.h +@@ -147,6 +147,8 @@ struct grub_disk + + /* Device-specific data. */ + void *data; ++ ++ int is_crypto_diskfilter; + }; + typedef struct grub_disk *grub_disk_t; + +@@ -314,4 +316,12 @@ void grub_mdraid1x_fini (void); + void grub_diskfilter_fini (void); + #endif + ++static inline int ++grub_disk_is_crypto (grub_disk_t disk) ++{ ++ return ((disk->is_crypto_diskfilter || ++ disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) ? ++ 1 : 0); ++} ++ + #endif /* ! GRUB_DISK_HEADER */ +diff --git a/include/grub/file.h b/include/grub/file.h +index fde58f0fa..fcfd32ce2 100644 +--- a/include/grub/file.h ++++ b/include/grub/file.h +@@ -185,6 +185,7 @@ extern grub_disk_read_hook_t EXPORT_VAR(grub_file_progress_hook); + /* Filters with lower ID are executed first. */ + typedef enum grub_file_filter_id + { ++ GRUB_FILE_FILTER_NOCAT, + GRUB_FILE_FILTER_VERIFY, + GRUB_FILE_FILTER_GZIO, + GRUB_FILE_FILTER_XZIO, +-- +2.42.1 + diff --git a/0002-arm64-make-sure-fdt-has-address-cells-and-size-cells.patch b/0002-arm64-make-sure-fdt-has-address-cells-and-size-cells.patch new file mode 100644 index 0000000..fa2c6a0 --- /dev/null +++ b/0002-arm64-make-sure-fdt-has-address-cells-and-size-cells.patch @@ -0,0 +1,40 @@ +From e27acddebd30175587155613042abffd2e9a5de8 Mon Sep 17 00:00:00 2001 +From: Mark Salter +Date: Mon, 17 Apr 2017 08:44:29 -0400 +Subject: [PATCH 2/9] arm64: make sure fdt has #address-cells and #size-cells + properties + +Recent upstream changes to kexec-tools relies on #address-cells +and #size-cells properties in the FDT. If grub2 needs to create +a chosen node, it is likely because firmware did not provide one. +In that case, set #address-cells and #size-cells properties to +make sure they exist. +--- + grub-core/loader/arm64/efi/linux.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +--- a/grub-core/loader/arm64/efi/linux.c ++++ b/grub-core/loader/arm64/efi/linux.c +@@ -99,7 +99,21 @@ + + node = grub_fdt_find_subnode (fdt, 0, "chosen"); + if (node < 0) +- node = grub_fdt_add_subnode (fdt, 0, "chosen"); ++ { ++ /* ++ * If we have to create a chosen node, Make sure we ++ * have #address-cells and #size-cells properties. ++ */ ++ retval = grub_fdt_set_prop32(fdt, 0, "#address-cells", 2); ++ if (retval) ++ goto failure; ++ ++ retval = grub_fdt_set_prop32(fdt, 0, "#size-cells", 2); ++ if (retval) ++ goto failure; ++ ++ node = grub_fdt_add_subnode (fdt, 0, "chosen"); ++ } + + if (node < 1) + goto failure; diff --git a/0002-cmdline-Provide-cmdline-functions-as-module.patch b/0002-cmdline-Provide-cmdline-functions-as-module.patch new file mode 100644 index 0000000..fe21079 --- /dev/null +++ b/0002-cmdline-Provide-cmdline-functions-as-module.patch @@ -0,0 +1,47 @@ +From 42cb0ebbffd660608612f9e32150a6596c6933c4 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Mon, 17 Aug 2020 17:25:56 +0800 +Subject: [PATCH 2/2] cmdline: Provide cmdline functions as module + +The command line processing is needed by many loader modules, hence we should +make it a sharable one rather than belonging to linux loader. This can cut the +dependency to linux module among multiple loaders like multiboot linuxefi and +so on to make custom boot image much more flexible to compose. + +Signed-off-by: Michael Chang +--- + grub-core/Makefile.core.def | 6 +++++- + grub-core/lib/cmdline.c | 3 +++ + 2 files changed, 8 insertions(+), 1 deletion(-) + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -1860,7 +1860,6 @@ + x86_64_efi = loader/efi/linux.c; + emu = loader/emu/linux.c; + common = loader/linux.c; +- common = lib/cmdline.c; + }; + + module = { +@@ -2611,3 +2610,8 @@ + efi = commands/bli.c; + enable = efi; + }; ++ ++module = { ++ name = cmdline; ++ common = lib/cmdline.c; ++}; +--- a/grub-core/lib/cmdline.c ++++ b/grub-core/lib/cmdline.c +@@ -19,6 +19,9 @@ + + #include + #include ++#include ++ ++GRUB_MOD_LICENSE ("GPLv3+"); + + static unsigned int check_arg (char *c, int *has_space) + { diff --git a/0002-cryptodisk-Fallback-to-passphrase.patch b/0002-cryptodisk-Fallback-to-passphrase.patch new file mode 100644 index 0000000..3b0c921 --- /dev/null +++ b/0002-cryptodisk-Fallback-to-passphrase.patch @@ -0,0 +1,41 @@ +From 7cc578baf26986c2badce998125b429a2aeb4d33 Mon Sep 17 00:00:00 2001 +From: Patrick Colp +Date: Sun, 30 Jul 2023 12:58:18 -0700 +Subject: [PATCH 2/4] cryptodisk: Fallback to passphrase + +If a protector is specified, but it fails to unlock the disk, fall back +to asking for the passphrase. However, an error was set indicating that +the protector(s) failed. Later code (e.g., LUKS code) fails as +`grub_errno` is now set. Print the existing errors out first, before +proceeding with the passphrase. + +Signed-off-by: Patrick Colp +--- + grub-core/disk/cryptodisk.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c +index 6620fca00..cf37a0934 100644 +--- a/grub-core/disk/cryptodisk.c ++++ b/grub-core/disk/cryptodisk.c +@@ -1191,11 +1191,16 @@ grub_cryptodisk_scan_device_real (const char *name, + source->name, source->partition != NULL ? "," : "", + part != NULL ? part : N_("UNKNOWN"), dev->uuid); + grub_free (part); +- goto error; + } + + if (!cargs->key_len) + { ++ if (grub_errno) ++ { ++ grub_print_error (); ++ grub_errno = GRUB_ERR_NONE; ++ } ++ + /* Get the passphrase from the user, if no key data. */ + askpass = 1; + part = grub_partition_get_name (source->partition); +-- +2.35.3 + diff --git a/0002-discard-cached-key-before-entering-grub-shell-and-ed.patch b/0002-discard-cached-key-before-entering-grub-shell-and-ed.patch new file mode 100644 index 0000000..e80daec --- /dev/null +++ b/0002-discard-cached-key-before-entering-grub-shell-and-ed.patch @@ -0,0 +1,88 @@ +From 2271da7522d8406c528d2a9079d810e140f8041a Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Fri, 3 Feb 2023 19:40:31 +0800 +Subject: [PATCH 2/2] discard cached key before entering grub shell and editor + mode + +The cached key is cleared in case of anyone poking around it by means of +the interactive shell offerings. + +Signed-off-by: Michael Chang +--- + grub-core/commands/crypttab.c | 16 ++++++++++++++++ + grub-core/normal/main.c | 2 ++ + grub-core/normal/menu_entry.c | 3 +++ + include/grub/crypttab.h | 2 ++ + 4 files changed, 23 insertions(+) + +--- a/grub-core/commands/crypttab.c ++++ b/grub-core/commands/crypttab.c +@@ -53,6 +53,22 @@ + return GRUB_ERR_NONE; + } + ++void ++grub_initrd_discard_key (void) ++{ ++ struct grub_key_publisher *cur, *nxt; ++ ++ FOR_LIST_ELEMENTS_SAFE (cur, nxt, kpuber) ++ { ++ grub_list_remove (GRUB_AS_LIST (cur)); ++ grub_memset (cur->key, 0, cur->key_len); ++ grub_free (cur->name); ++ grub_free (cur->path); ++ grub_free (cur->key); ++ grub_free (cur); ++ } ++} ++ + static grub_err_t + grub_cmd_crypttab_entry (grub_command_t cmd __attribute__ ((unused)), + int argc, char **argv) +--- a/grub-core/normal/main.c ++++ b/grub-core/normal/main.c +@@ -37,6 +37,7 @@ + #ifdef GRUB_MACHINE_IEEE1275 + #include + #endif ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -478,6 +479,7 @@ + return; + } + ++ grub_initrd_discard_key (); + grub_normal_reader_init (nested); + + while (1) +--- a/grub-core/normal/menu_entry.c ++++ b/grub-core/normal/menu_entry.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + + enum update_mode + { +@@ -1262,6 +1263,8 @@ + return; + } + ++ grub_initrd_discard_key(); ++ + screen = make_screen (entry); + if (! screen) + return; +--- a/include/grub/crypttab.h ++++ b/include/grub/crypttab.h +@@ -19,4 +19,6 @@ + grub_err_t + grub_initrd_publish_key (const char *uuid, const char *key, grub_size_t key_len, const char *path); + ++void ++grub_initrd_discard_key (void); + #endif /* ! GRUB_CRYPTTAB_HEADER */ diff --git a/0002-fs-ntfs-Fix-an-OOB-read-when-reading-data-from-the-r.patch b/0002-fs-ntfs-Fix-an-OOB-read-when-reading-data-from-the-r.patch new file mode 100644 index 0000000..d1bd61b --- /dev/null +++ b/0002-fs-ntfs-Fix-an-OOB-read-when-reading-data-from-the-r.patch @@ -0,0 +1,58 @@ +From 0ed2458cc4eff6d9a9199527e2a0b6d445802f94 Mon Sep 17 00:00:00 2001 +From: Maxim Suhanov +Date: Mon, 28 Aug 2023 16:32:33 +0300 +Subject: [PATCH 2/6] fs/ntfs: Fix an OOB read when reading data from the + resident $DATA attribute + +When reading a file containing resident data, i.e., the file data is stored in +the $DATA attribute within the NTFS file record, not in external clusters, +there are no checks that this resident data actually fits the corresponding +file record segment. + +When parsing a specially-crafted file system image, the current NTFS code will +read the file data from an arbitrary, attacker-chosen memory offset and of +arbitrary, attacker-chosen length. + +This allows an attacker to display arbitrary chunks of memory, which could +contain sensitive information like password hashes or even plain-text, +obfuscated passwords from BS EFI variables. + +This fix implements a check to ensure that resident data is read from the +corresponding file record segment only. + +Fixes: CVE-2023-4693 + +Reported-by: Maxim Suhanov +Signed-off-by: Maxim Suhanov +Reviewed-by: Daniel Kiper +--- + grub-core/fs/ntfs.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c +index c3c4db117..a68e173d8 100644 +--- a/grub-core/fs/ntfs.c ++++ b/grub-core/fs/ntfs.c +@@ -401,7 +401,18 @@ read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa, grub_uint8_t *dest, + { + if (ofs + len > u32at (pa, 0x10)) + return grub_error (GRUB_ERR_BAD_FS, "read out of range"); +- grub_memcpy (dest, pa + u32at (pa, 0x14) + ofs, len); ++ ++ if (u32at (pa, 0x10) > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) ++ return grub_error (GRUB_ERR_BAD_FS, "resident attribute too large"); ++ ++ if (pa >= at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) ++ return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); ++ ++ if (u16at (pa, 0x14) + u32at (pa, 0x10) > ++ (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) pa) ++ return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); ++ ++ grub_memcpy (dest, pa + u16at (pa, 0x14) + ofs, len); + return 0; + } + +-- +2.42.0 + diff --git a/0002-fs-xfs-Fix-XFS-directory-extent-parsing.patch b/0002-fs-xfs-Fix-XFS-directory-extent-parsing.patch new file mode 100644 index 0000000..02fca17 --- /dev/null +++ b/0002-fs-xfs-Fix-XFS-directory-extent-parsing.patch @@ -0,0 +1,171 @@ +From 4a6a5c4a6bb2426235364be9f3698763ddcf4775 Mon Sep 17 00:00:00 2001 +From: Jon DeVree +Date: Tue, 17 Oct 2023 23:03:47 -0400 +Subject: [PATCH 2/3] fs/xfs: Fix XFS directory extent parsing + +The XFS directory entry parsing code has never been completely correct +for extent based directories. The parser correctly handles the case +where the directory is contained in a single extent, but then mistakenly +assumes the data blocks for the multiple extent case are each identical +to the single extent case. The difference in the format of the data +blocks between the two cases is tiny enough that its gone unnoticed for +a very long time. + +A recent change introduced some additional bounds checking into the XFS +parser. Like GRUB's existing parser, it is correct for the single extent +case but incorrect for the multiple extent case. When parsing a directory +with multiple extents, this new bounds checking is sometimes (but not +always) tripped and triggers an "invalid XFS directory entry" error. This +probably would have continued to go unnoticed but the /boot/grub/ +directory is large enough that it often has multiple extents. + +The difference between the two cases is that when there are multiple +extents, the data blocks do not contain a trailer nor do they contain +any leaf information. That information is stored in a separate set of +extents dedicated to just the leaf information. These extents come after +the directory entry extents and are not included in the inode size. So +the existing parser already ignores the leaf extents. + +The only reason to read the trailer/leaf information at all is so that +the parser can avoid misinterpreting that data as directory entries. So +this updates the parser as follows: + +For the single extent case the parser doesn't change much: +1. Read the size of the leaf information from the trailer +2. Set the end pointer for the parser to the start of the leaf + information. (The previous bounds checking set the end pointer to the + start of the trailer, so this is actually a small improvement.) +3. Set the entries variable to the expected number of directory entries. + +For the multiple extent case: +1. Set the end pointer to the end of the block. +2. Do not set up the entries variable. Figuring out how many entries are + in each individual block is complex and does not seem worth it when + it appears to be safe to just iterate over the entire block. + +The bounds check itself was also dependent upon the faulty XFS parser +because it accidentally used "filename + length - 1". Presumably this +was able to pass the fuzzer because in the old parser there was always +8 bytes of slack space between the tail pointer and the actual end of +the block. Since this is no longer the case the bounds check needs to be +updated to "filename + length + 1" in order to prevent a regression in +the handling of corrupt fliesystems. + +Notes: +* When there is only one extent there will only ever be one block. If + more than one block is required then XFS will always switch to holding + leaf information in a separate extent. +* B-tree based directories seems to be parsed properly by the same code + that handles multiple extents. This is unlikely to ever occur within + /boot though because its only used when there are an extremely large + number of directory entries. + +Fixes: ef7850c75 (fs/xfs: Fix issues found while fuzzing the XFS filesystem) +Fixes: b2499b29c (Adds support for the XFS filesystem.) +Fixes: https://savannah.gnu.org/bugs/?64376 + +Signed-off-by: Jon DeVree +Reviewed-by: Daniel Kiper +Tested-by: Sebastian Andrzej Siewior +Tested-by: Marta Lewandowska +--- + grub-core/fs/xfs.c | 52 +++++++++++++++++++++++++++++++++------------- + 1 file changed, 38 insertions(+), 14 deletions(-) + +diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c +index ebf962793..18edfcff4 100644 +--- a/grub-core/fs/xfs.c ++++ b/grub-core/fs/xfs.c +@@ -223,6 +223,12 @@ struct grub_xfs_inode + /* Size of struct grub_xfs_inode v2, up to unused4 member included. */ + #define XFS_V2_INODE_SIZE (XFS_V3_INODE_SIZE - 76) + ++struct grub_xfs_dir_leaf_entry ++{ ++ grub_uint32_t hashval; ++ grub_uint32_t address; ++} GRUB_PACKED; ++ + struct grub_xfs_dirblock_tail + { + grub_uint32_t leaf_count; +@@ -874,9 +880,8 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, + { + struct grub_xfs_dir2_entry *direntry = + grub_xfs_first_de(dir->data, dirblock); +- int entries; +- struct grub_xfs_dirblock_tail *tail = +- grub_xfs_dir_tail(dir->data, dirblock); ++ int entries = -1; ++ char *end = dirblock + dirblk_size; + + numread = grub_xfs_read_file (dir, 0, 0, + blk << dirblk_log2, +@@ -887,14 +892,27 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, + return 0; + } + +- entries = (grub_be_to_cpu32 (tail->leaf_count) +- - grub_be_to_cpu32 (tail->leaf_stale)); ++ /* ++ * Leaf and tail information are only in the data block if the number ++ * of extents is 1. ++ */ ++ if (dir->inode.nextents == grub_cpu_to_be32_compile_time (1)) ++ { ++ struct grub_xfs_dirblock_tail *tail = grub_xfs_dir_tail (dir->data, dirblock); ++ ++ end = (char *) tail; ++ ++ /* Subtract the space used by leaf nodes. */ ++ end -= grub_be_to_cpu32 (tail->leaf_count) * sizeof (struct grub_xfs_dir_leaf_entry); + +- if (!entries) +- continue; ++ entries = grub_be_to_cpu32 (tail->leaf_count) - grub_be_to_cpu32 (tail->leaf_stale); ++ ++ if (!entries) ++ continue; ++ } + + /* Iterate over all entries within this block. */ +- while ((char *)direntry < (char *)tail) ++ while ((char *) direntry < (char *) end) + { + grub_uint8_t *freetag; + char *filename; +@@ -914,7 +932,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, + } + + filename = (char *)(direntry + 1); +- if (filename + direntry->len - 1 > (char *) tail) ++ if (filename + direntry->len + 1 > (char *) end) + return grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); + + /* The byte after the filename is for the filetype, padding, or +@@ -928,11 +946,17 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, + return 1; + } + +- /* Check if last direntry in this block is +- reached. */ +- entries--; +- if (!entries) +- break; ++ /* ++ * The expected number of directory entries is only tracked for the ++ * single extent case. ++ */ ++ if (dir->inode.nextents == grub_cpu_to_be32_compile_time (1)) ++ { ++ /* Check if last direntry in this block is reached. */ ++ entries--; ++ if (!entries) ++ break; ++ } + + /* Select the next directory entry. */ + direntry = grub_xfs_next_de(dir->data, direntry); +-- +2.42.1 + diff --git a/0002-ieee1275-ofpath-enable-NVMeoF-logical-device-transla.patch b/0002-ieee1275-ofpath-enable-NVMeoF-logical-device-transla.patch new file mode 100644 index 0000000..19a1527 --- /dev/null +++ b/0002-ieee1275-ofpath-enable-NVMeoF-logical-device-transla.patch @@ -0,0 +1,373 @@ +From 9e61624db77e5073961126457f599bc70e877fd1 Mon Sep 17 00:00:00 2001 +From: Diego Domingos +Date: Tue, 15 Mar 2022 15:59:41 -0400 +Subject: [PATCH 2/4] ieee1275/ofpath: enable NVMeoF logical device translation + +This patch add code to enable the translation of logical devices to the of NVMeoFC paths. +--- + grub-core/osdep/linux/ofpath.c | 260 +++++++++++++++++++++++++++++++-- + include/grub/util/ofpath.h | 29 ++++ + 2 files changed, 280 insertions(+), 9 deletions(-) + +diff --git a/grub-core/osdep/linux/ofpath.c b/grub-core/osdep/linux/ofpath.c +index 89beceef4..212782d3f 100644 +--- a/grub-core/osdep/linux/ofpath.c ++++ b/grub-core/osdep/linux/ofpath.c +@@ -137,7 +137,7 @@ trim_newline (char *path) + *end-- = '\0'; + } + +-#define MAX_DISK_CAT 64 ++#define MAX_DISK_CAT 512 + + static char * + find_obppath (const char *sysfs_path_orig) +@@ -313,6 +313,69 @@ get_basename(char *p) + return ret; + } + ++ ++void ++add_filename_to_pile(char *filename, struct ofpath_files_list_root* root){ ++ struct ofpath_files_list_node* file; ++ ++ file = malloc(sizeof(struct ofpath_files_list_node)); ++ ++ file->filename = filename; ++ ++ if(root->first == NULL){ ++ root->items = 1; ++ root->first = file; ++ file->next = NULL; ++ } else { ++ root->items++; ++ file->next = root->first; ++ root->first = file; ++ } ++} ++ ++ ++void ++find_file(char* filename, char* directory, struct ofpath_files_list_root* root, int max_depth, int depth){ ++ struct dirent *ep; ++ struct stat statbuf; ++ DIR *dp; ++ ++ if(depth > max_depth){ ++ return; ++ } ++ ++ if((dp = opendir(directory)) == NULL){ ++ ++ return; ++ } ++ ++ while((ep = readdir(dp)) != NULL){ ++ ++ char* full_path = malloc(1024*sizeof(char)); ++ snprintf(full_path,1024,"%s/%s",directory,ep->d_name); ++ ++ lstat(full_path,&statbuf); ++ ++ if(S_ISLNK(statbuf.st_mode)){ ++ ++ continue; ++ } ++ ++ if(!strcmp(ep->d_name,".") || !strcmp(ep->d_name,"..")){ ++ continue; ++ } ++ ++ if(!strcmp(ep->d_name,filename)){ ++ add_filename_to_pile(full_path, root); ++ } ++ ++ find_file(filename, full_path, root, max_depth, depth+1); ++ ++ } ++ closedir(dp); ++} ++ ++ + static char * + of_path_of_vdisk(const char *sys_devname __attribute__((unused)), + const char *device, +@@ -351,7 +414,142 @@ of_path_of_ide(const char *sys_devname __attribute__((unused)), const char *devi + return ret; + } + +-#ifdef __sparc__ ++char* ++of_find_fc_host(char* host_wwpn){ ++ ++ FILE* fp; ++ char *buf; ++ char portname_filename[sizeof("port_name")] = "port_name"; ++ char devices_path[sizeof("/sys/devices")] = "/sys/devices"; ++ ++ struct ofpath_files_list_root* portnames_file_list; ++ ++ portnames_file_list=malloc(sizeof(portnames_file_list)); ++ portnames_file_list->items=0; ++ portnames_file_list->first=NULL; ++ ++ find_file(portname_filename, devices_path, portnames_file_list, 10, 0); ++ ++ struct ofpath_files_list_node* node = portnames_file_list->first; ++ while(node != NULL){ ++ fp = fopen(node->filename,"r"); ++ buf = malloc(sizeof(char)*512); ++ fscanf(fp, "%s", buf); ++ fclose(fp); ++ if((strcmp(buf,host_wwpn) == 0) && grub_strstr(node->filename, "fc_host")){ ++ return node->filename; ++ } ++ node = node->next; ++ } ++ ++ return NULL; ++} ++ ++void ++of_path_get_nvmeof_adapter_info(char* sysfs_path, ++ struct ofpath_nvmeof_info* nvmeof_info){ ++ ++ FILE *fp; ++ char *buf, *buf2, *buf3; ++ ++ nvmeof_info->host_wwpn = malloc(sizeof(char)*256); ++ nvmeof_info->target_wwpn = malloc(sizeof(char)*256); ++ nvmeof_info->nqn = malloc(sizeof(char)*256); ++ ++ buf = malloc(sizeof(char)*512); ++ snprintf(buf,512,"%s/subsysnqn",sysfs_path); ++ fp = fopen(buf,"r"); ++ fscanf(fp, "%s", nvmeof_info->nqn); ++ fclose(fp); ++ ++ snprintf(buf,512,"%s/cntlid",sysfs_path); ++ fp = fopen(buf,"r"); ++ fscanf(fp, "%u", &(nvmeof_info->cntlid)); ++ fclose(fp); ++ ++ //snprintf(buf,512,"%s/nsid",sysfs_path); ++ //fp = fopen(buf,"r"); ++ //fscanf(fp, "%u", &(nvmeof_info->nsid)); ++ //fclose(fp); ++ ++ snprintf(buf,512,"%s/address",sysfs_path); ++ fp = fopen(buf,"r"); ++ buf2 = malloc(sizeof(char)*512); ++ fscanf(fp, "%s", buf2); ++ fclose(fp); ++ ++ nvmeof_info->host_wwpn = strrchr(buf2,'-')+1; ++ ++ buf3=strchr(buf2,'-')+1; ++ buf3=strchr(buf3,'-')+1; ++ nvmeof_info->target_wwpn = buf3; ++ buf3 = strchr(nvmeof_info->target_wwpn,','); ++ *buf3 = '\0'; ++ ++ ++ free(buf); ++ ++ return; ++} ++ ++#define MAX_NVME_NSID_DIGITS 6 ++ ++static char * ++of_path_get_nvme_controller_name_node(const char* devname) ++{ ++ char *controller_node, *end; ++ ++ controller_node = strdup(devname); ++ ++ end = grub_strchr(controller_node+1, 'n'); ++ ++ if(end != NULL){ ++ *end = '\0'; ++ } ++ ++ return controller_node; ++} ++ ++unsigned int ++of_path_get_nvme_nsid(const char* devname) ++{ ++ unsigned int nsid; ++ char *sysfs_path, *buf; ++ FILE *fp; ++ ++ buf=malloc(sizeof(char)*512); ++ ++ sysfs_path = block_device_get_sysfs_path_and_link (devname); ++ ++ snprintf(buf,512,"%s/%s/nsid",sysfs_path,devname); ++ fp = fopen(buf,"r"); ++ fscanf(fp, "%u", &(nsid)); ++ fclose(fp); ++ ++ free(sysfs_path); ++ free(buf); ++ ++ return nsid; ++ ++} ++ ++static char * ++nvme_get_syspath(const char *nvmedev) ++{ ++ char *sysfs_path, *controller_node; ++ sysfs_path = block_device_get_sysfs_path_and_link (nvmedev); ++ ++ if(strstr(sysfs_path,"nvme-subsystem")){ ++ controller_node = of_path_get_nvme_controller_name_node(nvmedev); ++ strcat(sysfs_path,"/"); ++ strcat(sysfs_path,controller_node); ++ sysfs_path = xrealpath(sysfs_path); ++ } ++ ++ return sysfs_path; ++} ++ ++ + static char * + of_path_of_nvme(const char *sys_devname __attribute__((unused)), + const char *device, +@@ -360,6 +558,7 @@ of_path_of_nvme(const char *sys_devname __attribute__((unused)), + { + char *sysfs_path, *of_path, disk[MAX_DISK_CAT]; + const char *digit_string, *part_end; ++ int chars_written; + + digit_string = trailing_digits (device); + part_end = devicenode + strlen (devicenode) - 1; +@@ -379,15 +578,61 @@ of_path_of_nvme(const char *sys_devname __attribute__((unused)), + /* Remove the p. */ + *end = '\0'; + sscanf (digit_string, "%d", &part); +- snprintf (disk, sizeof (disk), "/disk@1:%c", 'a' + (part - 1)); +- sysfs_path = block_device_get_sysfs_path_and_link (nvmedev); ++ ++ sysfs_path = nvme_get_syspath(nvmedev); ++ ++ /* If is a NVMeoF */ ++ if(strstr(sysfs_path,"nvme-fabrics")){ ++ struct ofpath_nvmeof_info* nvmeof_info; ++ nvmeof_info = malloc(sizeof(nvmeof_info)); ++ ++ of_path_get_nvmeof_adapter_info(sysfs_path, nvmeof_info); ++ ++ sysfs_path = of_find_fc_host(nvmeof_info->host_wwpn); ++ ++ chars_written = snprintf(disk,sizeof(disk),"/nvme-of/controller@%s,%x:nqn=%s", ++ nvmeof_info->target_wwpn, ++ 0xffff, ++ nvmeof_info->nqn); ++ ++ unsigned int nsid = of_path_get_nvme_nsid(nvmedev); ++ ++ if(nsid){ ++ snprintf(disk+chars_written,sizeof(disk) - chars_written, ++ "/namespace@%x:%d",nsid, part); ++ } ++ ++ } else { ++ snprintf (disk, sizeof (disk), "/disk@1:%c", 'a' + (part - 1)); ++ } + free (nvmedev); + } + else + { + /* We do not have the parition. */ +- snprintf (disk, sizeof (disk), "/disk@1"); +- sysfs_path = block_device_get_sysfs_path_and_link (device); ++ sysfs_path = nvme_get_syspath (device); ++ if(strstr(sysfs_path,"nvme-fabrics")){ ++ struct ofpath_nvmeof_info* nvmeof_info; ++ nvmeof_info = malloc(sizeof(nvmeof_info)); ++ ++ of_path_get_nvmeof_adapter_info(sysfs_path, nvmeof_info); ++ ++ sysfs_path = of_find_fc_host(nvmeof_info->host_wwpn); ++ ++ chars_written = snprintf(disk,sizeof(disk),"/nvme-of/controller@%s,%x:nqn=%s", ++ nvmeof_info->target_wwpn, ++ 0xffff, ++ nvmeof_info->nqn); ++ ++ unsigned int nsid = of_path_get_nvme_nsid(device); ++ if(nsid){ ++ snprintf(disk+chars_written,sizeof(disk) - chars_written, ++ "/namespace@%x",nsid); ++ } ++ } else { ++ snprintf (disk, sizeof (disk), "/disk@1"); ++ } ++ + } + + of_path = find_obppath (sysfs_path); +@@ -398,7 +643,6 @@ of_path_of_nvme(const char *sys_devname __attribute__((unused)), + free (sysfs_path); + return of_path; + } +-#endif + + static void + of_fc_port_name(const char *path, const char *subpath, char *port_name) +@@ -840,11 +1084,9 @@ grub_util_devname_to_ofpath (const char *sys_devname) + /* All the models I've seen have a devalias "floppy". + New models have no floppy at all. */ + ofpath = xstrdup ("floppy"); +-#ifdef __sparc__ + else if (device[0] == 'n' && device[1] == 'v' && device[2] == 'm' + && device[3] == 'e') + ofpath = of_path_of_nvme (name_buf, device, devnode, devicenode); +-#endif + else + { + grub_util_warn (_("unknown device type %s"), device); +diff --git a/include/grub/util/ofpath.h b/include/grub/util/ofpath.h +index b43c523cb..a0ec30620 100644 +--- a/include/grub/util/ofpath.h ++++ b/include/grub/util/ofpath.h +@@ -3,4 +3,33 @@ + + char *grub_util_devname_to_ofpath (const char *devname); + ++struct ofpath_files_list_node { ++ char* filename; ++ struct ofpath_files_list_node* next; ++}; ++ ++struct ofpath_files_list_root { ++ int items; ++ struct ofpath_files_list_node* first; ++}; ++ ++struct ofpath_nvmeof_info { ++ char* host_wwpn; ++ char* target_wwpn; ++ char* nqn; ++ int cntlid; ++ int nsid; ++}; ++ ++void of_path_get_nvmeof_adapter_info(char* sysfs_path, ++ struct ofpath_nvmeof_info* nvmeof_info); ++ ++unsigned int of_path_get_nvme_nsid(const char* devname); ++ ++void add_filename_to_pile(char *filename, struct ofpath_files_list_root* root); ++ ++void find_file(char* filename, char* directory, struct ofpath_files_list_root* root, int max_depth, int depth); ++ ++char* of_find_fc_host(char* host_wwpn); ++ + #endif /* ! GRUB_OFPATH_MACHINE_UTIL_HEADER */ +-- +2.35.3 + diff --git a/0002-ieee1275-powerpc-enables-device-mapper-discovery.patch b/0002-ieee1275-powerpc-enables-device-mapper-discovery.patch new file mode 100644 index 0000000..bff4924 --- /dev/null +++ b/0002-ieee1275-powerpc-enables-device-mapper-discovery.patch @@ -0,0 +1,107 @@ +From 8b31ebfa42eb5af0633191d26fcdcea8c539e521 Mon Sep 17 00:00:00 2001 +From: Diego Domingos +Date: Wed, 24 Jun 2020 08:22:50 -0400 +Subject: [PATCH 2/2] ieee1275/powerpc: enables device mapper discovery + +this patch enables the device mapper discovery on ofpath.c. Currently, +when we are dealing with a device like /dev/dm-* the ofpath returns null +since there is no function implemented to handle this case. + +This patch implements a function that will look into /sys/block/dm-* +devices and search recursively inside slaves directory to find the root +disk. + +v2: +Fix gcc-12 error: pointer 'device_path' may be used after 'free' +[-Werror=use-after-free] + +--- + grub-core/osdep/linux/ofpath.c | 64 ++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 63 insertions(+), 1 deletion(-) + +--- a/grub-core/osdep/linux/ofpath.c ++++ b/grub-core/osdep/linux/ofpath.c +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + + #ifdef __sparc__ + typedef enum +@@ -754,13 +755,74 @@ + return new; + } + ++static char * ++get_slave_from_dm(const char * device){ ++ char *curr_device, *tmp; ++ char *directory; ++ char *ret = NULL; ++ ++ directory = grub_strdup (device); ++ tmp = get_basename(directory); ++ curr_device = grub_strdup (tmp); ++ *tmp = '\0'; ++ ++ /* Recursively check for slaves devices so we can find the root device */ ++ while ((curr_device[0] == 'd') && (curr_device[1] == 'm') && (curr_device[2] == '-')){ ++ DIR *dp; ++ struct dirent *ep; ++ char* device_path; ++ ++ device_path = grub_xasprintf ("/sys/block/%s/slaves", curr_device); ++ dp = opendir(device_path); ++ free(device_path); ++ ++ if (dp != NULL) ++ { ++ ep = readdir (dp); ++ while (ep != NULL){ ++ ++ /* avoid some system directories */ ++ if (!strcmp(ep->d_name,".")) ++ goto next_dir; ++ if (!strcmp(ep->d_name,"..")) ++ goto next_dir; ++ ++ free (curr_device); ++ free (ret); ++ curr_device = grub_strdup (ep->d_name); ++ ret = grub_xasprintf ("%s%s", directory, curr_device); ++ break; ++ ++ next_dir: ++ ep = readdir (dp); ++ continue; ++ } ++ closedir (dp); ++ } ++ else ++ grub_util_warn (_("cannot open directory `/sys/block/%s/slaves'"), curr_device); ++ } ++ ++ free (directory); ++ free (curr_device); ++ ++ return ret; ++} ++ + char * + grub_util_devname_to_ofpath (const char *sys_devname) + { +- char *name_buf, *device, *devnode, *devicenode, *ofpath; ++ char *name_buf, *device, *devnode, *devicenode, *ofpath, *realname; + + name_buf = xrealpath (sys_devname); + ++ realname = get_slave_from_dm (name_buf); ++ if (realname) ++ { ++ free (name_buf); ++ name_buf = realname; ++ } ++ + device = get_basename (name_buf); + devnode = strip_trailing_digits (name_buf); + devicenode = strip_trailing_digits (device); diff --git a/0002-mkstandalone-ensure-deterministic-tar-file-creation-.patch b/0002-mkstandalone-ensure-deterministic-tar-file-creation-.patch new file mode 100644 index 0000000..991dc3f --- /dev/null +++ b/0002-mkstandalone-ensure-deterministic-tar-file-creation-.patch @@ -0,0 +1,75 @@ +From bb9bbe0f66a8462a1b2477fbc2aa1d70973035d4 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 30 Nov 2023 16:30:45 +0800 +Subject: [PATCH 2/2] mkstandalone: ensure deterministic tar file creation by + sorting contents + +The add_tar_files() function currently iterates through a directory's +content using readdir(), which doesn't guarantee a specific order. This +lack of deterministic behavior impacts reproducibility in the build +process. + +This commit resolves the issue by introducing sorting functionality. The +list retrieved by readdir() is now sorted alphabetically before +incorporation into the tar archive, ensuring consistent and predictable +file ordering within the archive. + +Signed-off-by: Michael Chang +Signed-off-by: Bernhard Wiedemann +--- + util/grub-mkstandalone.c | 26 +++++++++++++++++++++++--- + 1 file changed, 23 insertions(+), 3 deletions(-) + +diff --git a/util/grub-mkstandalone.c b/util/grub-mkstandalone.c +index 8e1229925..e4b5bcab4 100644 +--- a/util/grub-mkstandalone.c ++++ b/util/grub-mkstandalone.c +@@ -205,22 +205,42 @@ add_tar_file (const char *from, + { + grub_util_fd_dir_t d; + grub_util_fd_dirent_t de; ++ char **from_files; ++ grub_size_t alloc = 8, used = 0; ++ grub_size_t i; + + d = grub_util_fd_opendir (from); + ++ from_files = xmalloc (alloc * sizeof (*from_files)); + while ((de = grub_util_fd_readdir (d))) + { +- char *fp, *tfp; + if (strcmp (de->d_name, ".") == 0) + continue; + if (strcmp (de->d_name, "..") == 0) + continue; +- fp = grub_util_path_concat (2, from, de->d_name); +- tfp = xasprintf ("%s/%s", to, de->d_name); ++ if (alloc <= used) ++ { ++ alloc <<= 1; ++ from_files = xrealloc (from_files, alloc * sizeof (*from_files)); ++ } ++ from_files[used++] = xstrdup(de->d_name); ++ } ++ qsort (from_files, used, sizeof (*from_files), grub_qsort_strcmp); ++ ++ for (i = 0; i < used; i++) ++ { ++ char *fp, *tfp; ++ ++ fp = grub_util_path_concat (2, from, from_files[i]); ++ tfp = xasprintf ("%s/%s", to, from_files[i]); + add_tar_file (fp, tfp); ++ free (tfp); + free (fp); ++ free (from_files[i]); + } ++ + grub_util_fd_closedir (d); ++ free (from_files); + free (tcn); + return; + } +-- +2.43.0 + diff --git a/0002-prep_loadenv-Fix-regex-for-Open-Firmware-device-spec.patch b/0002-prep_loadenv-Fix-regex-for-Open-Firmware-device-spec.patch new file mode 100644 index 0000000..493d331 --- /dev/null +++ b/0002-prep_loadenv-Fix-regex-for-Open-Firmware-device-spec.patch @@ -0,0 +1,205 @@ +From 990902e28c390217d25ea474e5ef163d79eadc7f Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Fri, 31 Mar 2023 15:19:58 +0800 +Subject: [PATCH 2/2] prep_loadenv: Fix regex for Open Firmware device + specifier with encoded commas + +The Open Firmware device specifier allows for comma-separated properties +of a component, but this conflicts with the way that grub separates +device and partition in its device specifier. To address this, grub +encodes commas in Open Firmware device strings with a leading backslash +as an established convention. + +However, the regular expression used to extract the boot device +substring from the $cmdpath environment variable did not properly retain +commas with leading backslashes as part of the device. This could cause +the comma to be incorrectly interpreted as a partition delimiter and +result in a broken name for the boot disk. + +To fix this issue, we have updated the regular expression to properly +handle the encoded comma in the Open Firmware device specifier, ensuring +that the correct boot device is identified and used. + +v2: +Fix the issue of freeing an uninitialized pointer in early_prep_loadenv. + +Signed-off-by: Michael Chang +--- + grub-core/commands/prep_loadenv.c | 108 ++++++++++++++++++++++-------- + 1 file changed, 79 insertions(+), 29 deletions(-) + +--- a/grub-core/commands/prep_loadenv.c ++++ b/grub-core/commands/prep_loadenv.c +@@ -15,7 +15,7 @@ + GRUB_MOD_LICENSE ("GPLv3+"); + + static char * +-match_substr (regmatch_t *match, const char *str) ++match_substr (const regmatch_t *match, const char *str) + { + if (match->rm_so != -1) + { +@@ -185,24 +185,18 @@ + return err; + } + +-static grub_err_t +-boot_disk_prep_partname (char **name) ++static regmatch_t * ++regex_match_str (const char *pattern, const char *str, grub_size_t *nmatch) + { + regex_t regex; + int ret; + grub_size_t s; + char *comperr; +- const char *cmdpath; + regmatch_t *matches = NULL; + grub_err_t err = GRUB_ERR_NONE; + +- *name = NULL; +- +- cmdpath = grub_env_get ("cmdpath"); +- if (!cmdpath) +- return GRUB_ERR_NONE; +- +- ret = regcomp (®ex, "\\(([^,]+)(,?.*)?\\)(.*)", REG_EXTENDED); ++ *nmatch = 0; ++ ret = regcomp (®ex, pattern, REG_EXTENDED); + if (ret) + goto fail; + +@@ -210,22 +204,11 @@ + if (! matches) + goto fail; + +- ret = regexec (®ex, cmdpath, regex.re_nsub + 1, matches, 0); +- if (!ret) ++ ret = regexec (®ex, str, regex.re_nsub + 1, matches, 0); ++ if (ret == 0) + { +- char *devname = devname = match_substr (matches + 1, cmdpath); +- if (!devname) +- { +- err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "%s contains no disk name", cmdpath); +- goto out; +- } +- +- err = prep_partname (devname, name); +- out: +- grub_free (devname); +- regfree (®ex); +- grub_free (matches); +- return err; ++ *nmatch = regex.re_nsub + 1; ++ return matches; + } + + fail: +@@ -235,13 +218,60 @@ + if (!comperr) + { + regfree (®ex); +- return grub_errno; ++ return NULL; + } + regerror (ret, ®ex, comperr, s); + err = grub_error (GRUB_ERR_TEST_FAILURE, "%s", comperr); + regfree (®ex); + grub_free (comperr); +- return err; ++ return NULL; ++} ++ ++static grub_err_t ++boot_disk_prep_partname (const char *varname, char **name) ++{ ++ const char *cmdpath; ++ regmatch_t *matches; ++ grub_size_t nmatch; ++ char *devname = NULL; ++ ++ *name = NULL; ++ ++ if (varname) ++ cmdpath = grub_env_get (varname); ++ else ++ cmdpath = grub_env_get ("cmdpath"); ++ if (!cmdpath) ++ return GRUB_ERR_NONE; ++ ++ matches = regex_match_str("\\((.*)\\)(.*)", cmdpath, &nmatch); ++ if (matches && nmatch >= 2) ++ devname = match_substr (matches + 1, cmdpath); ++ if (devname == NULL) ++ goto quit; ++ grub_free (matches); ++ ++ matches = regex_match_str ("(.*[^\\])(,.*)", devname, &nmatch); ++ if (matches && nmatch >= 2) ++ { ++ char *n = match_substr (matches + 1, devname); ++ grub_free (devname); ++ devname = n; ++ } ++ else ++ grub_errno = GRUB_ERR_NONE; ++ if (devname) ++ { ++ grub_printf ("search prep from disk `%s'\n", devname); ++ prep_partname (devname, name); ++ } ++ ++ quit: ++ grub_free (devname); ++ grub_free (matches); ++ if (grub_errno) ++ grub_print_error (); ++ return GRUB_ERR_NONE; + } + + static grub_err_t +@@ -274,13 +304,31 @@ + return GRUB_ERR_NONE; + } + ++static grub_err_t ++grub_cmd_prep_partname (grub_command_t cmd __attribute__ ((unused)), ++ int argc, ++ char **argv) ++{ ++ char *prep = NULL; ++ const char *varname = NULL; ++ ++ if (argc > 0) ++ varname = argv[0]; ++ ++ boot_disk_prep_partname(varname, &prep); ++ if (prep) ++ grub_printf ("prep: %s\n", prep); ++ ++ return GRUB_ERR_NONE; ++} ++ + static void + early_prep_loadenv (void) + { + grub_err_t err; +- char *prep; ++ char *prep = NULL; + +- err = boot_disk_prep_partname (&prep); ++ err = boot_disk_prep_partname (NULL, &prep); + if (err == GRUB_ERR_NONE && prep) + err = prep_read_envblk (prep); + if (err == GRUB_ERR_BAD_FILE_TYPE || err == GRUB_ERR_FILE_NOT_FOUND) +@@ -296,6 +344,10 @@ + { + early_env_hook = early_prep_loadenv; + cmd_prep_load = ++ grub_register_command("prep_partname", grub_cmd_prep_partname, ++ "VARNAME", ++ N_("Get partition name of PReP.")); ++ cmd_prep_load = + grub_register_command("prep_load_env", grub_cmd_prep_loadenv, + "DEVICE", + N_("Load variables from environment block file.")); diff --git a/0002-tpm2-Add-TPM-Software-Stack-TSS.patch b/0002-tpm2-Add-TPM-Software-Stack-TSS.patch new file mode 100644 index 0000000..81765e3 --- /dev/null +++ b/0002-tpm2-Add-TPM-Software-Stack-TSS.patch @@ -0,0 +1,3569 @@ +From c5a42cf3340aa740132bcdb8e8cee22c23306ef5 Mon Sep 17 00:00:00 2001 +From: Hernan Gatta +Date: Tue, 1 Feb 2022 05:02:54 -0800 +Subject: [PATCH v6 09/20] tpm2: Add TPM Software Stack (TSS) + +A Trusted Platform Module (TPM) Software Stack (TSS) provides logic to +compose, submit, and parse TPM commands and responses. + +A limited number of TPM commands may be accessed via the EFI TCG2 +protocol. This protocol exposes functionality that is primarily geared +toward TPM usage within the context of Secure Boot. For all other TPM +commands, however, such as sealing and unsealing, this protocol does not +provide any help, with the exception of passthrough command submission. + +The SubmitCommand method allows a caller to send raw commands to the +system's TPM and to receive the corresponding response. These +command/response pairs are formatted using the TPM wire protocol. To +construct commands in this way, and to parse the TPM's response, it is +necessary to, first, possess knowledge of the various TPM structures, and, +second, of the TPM wire protocol itself. + +As such, this patch includes a set of header files that define the +necessary TPM structures and TSS functions, implementations of various +TPM2_* functions (inventoried below), and logic to write and read command +and response buffers, respectively, using the TPM wire protocol. + +Functions: TPM2_Create, TPM2_CreatePrimary, TPM2_EvictControl, +TPM2_FlushContext, TPM2_Load, TPM2_PCR_Read, TPM2_PolicyGetDigest, +TPM2_PolicyPCR, TPM2_ReadPublic, TPM2_StartAuthSession, TPM2_Unseal. + +Signed-off-by: Hernan Gatta +Signed-off-by: Gary Lin +--- + grub-core/tpm2/buffer.c | 145 +++++ + grub-core/tpm2/mu.c | 807 +++++++++++++++++++++++++ + grub-core/tpm2/tcg2.c | 143 +++++ + grub-core/tpm2/tpm2.c | 761 +++++++++++++++++++++++ + include/grub/tpm2/buffer.h | 65 ++ + include/grub/tpm2/internal/functions.h | 117 ++++ + include/grub/tpm2/internal/structs.h | 675 +++++++++++++++++++++ + include/grub/tpm2/internal/types.h | 370 ++++++++++++ + include/grub/tpm2/mu.h | 292 +++++++++ + include/grub/tpm2/tcg2.h | 34 ++ + include/grub/tpm2/tpm2.h | 34 ++ + 11 files changed, 3443 insertions(+) + create mode 100644 grub-core/tpm2/buffer.c + create mode 100644 grub-core/tpm2/mu.c + create mode 100644 grub-core/tpm2/tcg2.c + create mode 100644 grub-core/tpm2/tpm2.c + create mode 100644 include/grub/tpm2/buffer.h + create mode 100644 include/grub/tpm2/internal/functions.h + create mode 100644 include/grub/tpm2/internal/structs.h + create mode 100644 include/grub/tpm2/internal/types.h + create mode 100644 include/grub/tpm2/mu.h + create mode 100644 include/grub/tpm2/tcg2.h + create mode 100644 include/grub/tpm2/tpm2.h + +diff --git a/grub-core/tpm2/buffer.c b/grub-core/tpm2/buffer.c +new file mode 100644 +index 000000000..cb9f29497 +--- /dev/null ++++ b/grub-core/tpm2/buffer.c +@@ -0,0 +1,145 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++ ++void grub_tpm2_buffer_init (grub_tpm2_buffer_t buffer) ++{ ++ grub_memset (buffer->data, 0xDD, sizeof (buffer->data)); ++ buffer->size = 0; ++ buffer->offset = 0; ++ buffer->cap = sizeof (buffer->data); ++ buffer->error = 0; ++} ++ ++void ++grub_tpm2_buffer_pack (grub_tpm2_buffer_t buffer, const void* data, ++ grub_size_t size) ++{ ++ grub_uint32_t r = buffer->cap - buffer->size; ++ ++ if (buffer->error) ++ return; ++ ++ if (size > r) ++ { ++ buffer->error = 1; ++ return; ++ } ++ ++ grub_memcpy (&buffer->data[buffer->size], (void*) data, size); ++ buffer->size += size; ++} ++ ++void ++grub_tpm2_buffer_pack_u8 (grub_tpm2_buffer_t buffer, grub_uint8_t value) ++{ ++ grub_tpm2_buffer_pack (buffer, (const char*) &value, sizeof (value)); ++} ++ ++void ++grub_tpm2_buffer_pack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t value) ++{ ++ grub_uint16_t tmp = grub_cpu_to_be16 (value); ++ grub_tpm2_buffer_pack (buffer, (const char*) &tmp, sizeof (tmp)); ++} ++ ++void ++grub_tpm2_buffer_pack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t value) ++{ ++ grub_uint32_t tmp = grub_cpu_to_be32 (value); ++ grub_tpm2_buffer_pack (buffer, (const char*) &tmp, sizeof (tmp)); ++} ++ ++void ++grub_tpm2_buffer_unpack (grub_tpm2_buffer_t buffer, void* data, ++ grub_size_t size) ++{ ++ grub_uint32_t r = buffer->size - buffer->offset; ++ ++ if (buffer->error) ++ return; ++ ++ if (size > r) ++ { ++ buffer->error = 1; ++ return; ++ } ++ ++ grub_memcpy (data, &buffer->data[buffer->offset], size); ++ buffer->offset += size; ++} ++ ++void ++grub_tpm2_buffer_unpack_u8 (grub_tpm2_buffer_t buffer, grub_uint8_t* value) ++{ ++ grub_uint32_t r = buffer->size - buffer->offset; ++ ++ if (buffer->error) ++ return; ++ ++ if (sizeof (*value) > r) ++ { ++ buffer->error = 1; ++ return; ++ } ++ ++ grub_memcpy (value, &buffer->data[buffer->offset], sizeof (*value)); ++ buffer->offset += sizeof (*value); ++} ++ ++void ++grub_tpm2_buffer_unpack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t* value) ++{ ++ grub_uint16_t tmp; ++ grub_uint32_t r = buffer->size - buffer->offset; ++ ++ if (buffer->error) ++ return; ++ ++ if (sizeof (tmp) > r) ++ { ++ buffer->error = 1; ++ return; ++ } ++ ++ grub_memcpy (&tmp, &buffer->data[buffer->offset], sizeof (tmp)); ++ buffer->offset += sizeof (tmp); ++ *value = grub_be_to_cpu16 (tmp); ++} ++ ++void ++grub_tpm2_buffer_unpack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t* value) ++{ ++ grub_uint32_t tmp; ++ grub_uint32_t r = buffer->size - buffer->offset; ++ ++ if (buffer->error) ++ return; ++ ++ if (sizeof (tmp) > r) ++ { ++ buffer->error = 1; ++ return; ++ } ++ ++ grub_memcpy (&tmp, &buffer->data[buffer->offset], sizeof (tmp)); ++ buffer->offset += sizeof (tmp); ++ *value = grub_be_to_cpu32 (tmp); ++} +diff --git a/grub-core/tpm2/mu.c b/grub-core/tpm2/mu.c +new file mode 100644 +index 000000000..1617f37cd +--- /dev/null ++++ b/grub-core/tpm2/mu.c +@@ -0,0 +1,807 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++ ++void ++grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMS_AUTH_COMMAND* authCommand) ++{ ++ grub_uint32_t start; ++ grub_uint32_t tmp; ++ ++ grub_tpm2_buffer_pack_u32 (buffer, 0); ++ start = buffer->size; ++ ++ grub_tpm2_buffer_pack_u32 (buffer, authCommand->sessionHandle); ++ ++ grub_tpm2_buffer_pack_u16 (buffer, authCommand->nonce.size); ++ grub_tpm2_buffer_pack (buffer, authCommand->nonce.buffer, ++ authCommand->nonce.size); ++ ++ grub_tpm2_buffer_pack_u8 (buffer, ++ *((const grub_uint8_t*) &authCommand->sessionAttributes)); ++ ++ grub_tpm2_buffer_pack_u16 (buffer, authCommand->hmac.size); ++ grub_tpm2_buffer_pack (buffer, authCommand->hmac.buffer, ++ authCommand->hmac.size); ++ ++ tmp = grub_cpu_to_be32 (buffer->size - start); ++ grub_memcpy (&buffer->data[start - sizeof (grub_uint32_t)], &tmp, ++ sizeof (tmp)); ++} ++ ++void ++grub_tpm2_mu_TPM2B_Marshal (grub_tpm2_buffer_t buffer, ++ const grub_uint16_t size, ++ const grub_uint8_t* b) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, size); ++ ++ for (grub_uint16_t i = 0; i < size; i++) ++ grub_tpm2_buffer_pack_u8 (buffer, b[i]); ++} ++ ++void ++grub_tpm2_mu_TPMU_SYM_KEY_BITS_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMI_ALG_SYM_OBJECT algorithm, ++ const TPMU_SYM_KEY_BITS *p) ++{ ++ switch (algorithm) ++ { ++ case TPM_ALG_AES: ++ case TPM_ALG_SM4: ++ case TPM_ALG_CAMELLIA: ++ case TPM_ALG_XOR: ++ grub_tpm2_buffer_pack_u16 (buffer, *((const grub_uint16_t*) p)); ++ break; ++ case TPM_ALG_NULL: ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMU_SYM_MODE_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMI_ALG_SYM_OBJECT algorithm, ++ const TPMU_SYM_MODE *p) ++{ ++ switch (algorithm) ++ { ++ case TPM_ALG_AES: ++ case TPM_ALG_SM4: ++ case TPM_ALG_CAMELLIA: ++ grub_tpm2_buffer_pack_u16 (buffer, *((const grub_uint16_t*) p)); ++ break; ++ case TPM_ALG_XOR: ++ case TPM_ALG_NULL: ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_SYM_DEF_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMT_SYM_DEF *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->algorithm); ++ grub_tpm2_mu_TPMU_SYM_KEY_BITS_Marshal (buffer, p->algorithm, &p->keyBits); ++ grub_tpm2_mu_TPMU_SYM_MODE_Marshal (buffer, p->algorithm, &p->mode); ++} ++ ++void ++grub_tpm2_mu_TPMS_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMS_PCR_SELECTION* pcrSelection) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, pcrSelection->hash); ++ grub_tpm2_buffer_pack_u8 (buffer, pcrSelection->sizeOfSelect); ++ ++ for (grub_uint32_t i = 0; i < pcrSelection->sizeOfSelect; i++) ++ grub_tpm2_buffer_pack_u8 (buffer, pcrSelection->pcrSelect[i]); ++} ++ ++void ++grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buffer, ++ const TPML_PCR_SELECTION* pcrSelection) ++{ ++ grub_tpm2_buffer_pack_u32 (buffer, pcrSelection->count); ++ ++ for (grub_uint32_t i = 0; i < pcrSelection->count; i++) ++ grub_tpm2_mu_TPMS_PCR_SELECTION_Marshal (buffer, ++ &pcrSelection->pcrSelections[i]); ++} ++ ++void ++grub_tpm2_mu_TPMA_OBJECT_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMA_OBJECT *p) ++{ ++ grub_tpm2_buffer_pack_u32 (buffer, *((const grub_uint32_t*) p)); ++} ++ ++void ++grub_tpm2_mu_TPMS_SCHEME_XOR_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMS_SCHEME_XOR *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->hashAlg); ++ grub_tpm2_buffer_pack_u16 (buffer, p->kdf); ++} ++ ++void ++grub_tpm2_mu_TPMS_SCHEME_HMAC_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMS_SCHEME_HMAC *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->hashAlg); ++} ++ ++void ++grub_tpm2_mu_TPMU_SCHEME_KEYEDHASH_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMI_ALG_KEYEDHASH_SCHEME scheme, ++ const TPMU_SCHEME_KEYEDHASH *p) ++{ ++ switch (scheme) ++ { ++ case TPM_ALG_HMAC: ++ grub_tpm2_mu_TPMS_SCHEME_HMAC_Marshal (buffer, &p->hmac); ++ break; ++ case TPM_ALG_XOR: ++ grub_tpm2_mu_TPMS_SCHEME_XOR_Marshal (buffer, &p->exclusiveOr); ++ break; ++ case TPM_ALG_NULL: ++ break; ++ default: ++ buffer->error = 1; ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_KEYEDHASH_SCHEME_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMT_KEYEDHASH_SCHEME *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->scheme); ++ grub_tpm2_mu_TPMU_SCHEME_KEYEDHASH_Marshal (buffer, p->scheme, &p->details); ++} ++ ++void ++grub_tpm2_mu_TPMS_KEYEDHASH_PARMS_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMS_KEYEDHASH_PARMS *p) ++{ ++ grub_tpm2_mu_TPMT_KEYEDHASH_SCHEME_Marshal (buffer, &p->scheme); ++} ++ ++void ++grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMT_SYM_DEF_OBJECT *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->algorithm); ++ grub_tpm2_mu_TPMU_SYM_KEY_BITS_Marshal (buffer, p->algorithm, &p->keyBits); ++ grub_tpm2_mu_TPMU_SYM_MODE_Marshal (buffer, p->algorithm, &p->mode); ++} ++ ++void ++grub_tpm2_mu_TPMU_ASYM_SCHEME_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMI_ALG_RSA_DECRYPT scheme, ++ const TPMU_ASYM_SCHEME *p __attribute__ ((unused))) ++{ ++ switch (scheme) ++ { ++ case TPM_ALG_NULL: ++ break; ++ default: ++ /* Unsupported */ ++ buffer->error = 1; ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_RSA_SCHEME_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMT_RSA_SCHEME *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->scheme); ++ grub_tpm2_mu_TPMU_ASYM_SCHEME_Marshal (buffer, p->scheme, &p->details); ++} ++ ++void ++grub_tpm2_mu_TPMS_RSA_PARMS_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMS_RSA_PARMS *p) ++{ ++ grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Marshal (buffer, &p->symmetric); ++ grub_tpm2_mu_TPMT_RSA_SCHEME_Marshal (buffer, &p->scheme); ++ grub_tpm2_buffer_pack_u16 (buffer, p->keyBits); ++ grub_tpm2_buffer_pack_u32 (buffer, p->exponent); ++} ++ ++void ++grub_tpm2_mu_TPMS_SYMCIPHER_PARMS_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMS_SYMCIPHER_PARMS *p) ++{ ++ grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Marshal (buffer, &p->sym); ++} ++ ++void ++grub_tpm2_mu_TPMT_ECC_SCHEME_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMT_ECC_SCHEME *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->scheme); ++ grub_tpm2_mu_TPMU_ASYM_SCHEME_Marshal (buffer, p->scheme, &p->details); ++} ++ ++void ++grub_tpm2_mu_TPMU_KDF_SCHEME_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMI_ALG_KDF scheme, ++ const TPMU_KDF_SCHEME *p) ++{ ++ switch (scheme) ++ { ++ case TPM_ALG_MGF1: ++ grub_tpm2_buffer_pack_u16 (buffer, p->mgf1.hashAlg); ++ break; ++ case TPM_ALG_KDF1_SP800_56A: ++ grub_tpm2_buffer_pack_u16 (buffer, p->kdf1_sp800_56a.hashAlg); ++ break; ++ case TPM_ALG_KDF2: ++ grub_tpm2_buffer_pack_u16 (buffer, p->kdf2.hashAlg); ++ break; ++ case TPM_ALG_KDF1_SP800_108: ++ grub_tpm2_buffer_pack_u16 (buffer, p->kdf1_sp800_108.hashAlg); ++ break; ++ case TPM_ALG_NULL: ++ break; ++ default: ++ buffer->error = 1; ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_KDF_SCHEME_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMT_KDF_SCHEME *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->scheme); ++ grub_tpm2_mu_TPMU_KDF_SCHEME_Marshal (buffer, p->scheme, &p->details); ++} ++ ++void ++grub_tpm2_mu_TPMS_ECC_PARMS_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMS_ECC_PARMS *p) ++{ ++ grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Marshal (buffer, &p->symmetric); ++ grub_tpm2_mu_TPMT_ECC_SCHEME_Marshal (buffer, &p->scheme); ++ grub_tpm2_buffer_pack_u16 (buffer, p->curveID); ++ grub_tpm2_mu_TPMT_KDF_SCHEME_Marshal (buffer, &p->kdf); ++} ++ ++void ++grub_tpm2_mu_TPMU_PUBLIC_PARMS_Marshal (grub_tpm2_buffer_t buffer, ++ const grub_uint32_t type, ++ const TPMU_PUBLIC_PARMS *p) ++{ ++ switch (type) ++ { ++ case TPM_ALG_KEYEDHASH: ++ grub_tpm2_mu_TPMS_KEYEDHASH_PARMS_Marshal (buffer, &p->keyedHashDetail); ++ break; ++ case TPM_ALG_SYMCIPHER: ++ grub_tpm2_mu_TPMS_SYMCIPHER_PARMS_Marshal (buffer, &p->symDetail); ++ break; ++ case TPM_ALG_RSA: ++ grub_tpm2_mu_TPMS_RSA_PARMS_Marshal (buffer, &p->rsaDetail); ++ break; ++ case TPM_ALG_ECC: ++ grub_tpm2_mu_TPMS_ECC_PARMS_Marshal (buffer, &p->eccDetail); ++ break; ++ default: ++ buffer->error = 1; ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMS_ECC_POINT_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMS_ECC_POINT *p) ++{ ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->x.size, p->x.buffer); ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->y.size, p->y.buffer); ++} ++ ++void ++grub_tpm2_mu_TPMU_PUBLIC_ID_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMI_ALG_PUBLIC type, ++ const TPMU_PUBLIC_ID *p) ++{ ++ switch(type) ++ { ++ case TPM_ALG_KEYEDHASH: ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->keyedHash.size, ++ p->keyedHash.buffer); ++ break; ++ case TPM_ALG_SYMCIPHER: ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->sym.size, p->sym.buffer); ++ break; ++ case TPM_ALG_RSA: ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->rsa.size, p->rsa.buffer); ++ break; ++ case TPM_ALG_ECC: ++ grub_tpm2_mu_TPMS_ECC_POINT_Marshal (buffer, &p->ecc); ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_PUBLIC_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMT_PUBLIC *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->type); ++ grub_tpm2_buffer_pack_u16 (buffer, p->nameAlg); ++ grub_tpm2_mu_TPMA_OBJECT_Marshal (buffer, &p->objectAttributes); ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->authPolicy.size, p->authPolicy.buffer); ++ grub_tpm2_mu_TPMU_PUBLIC_PARMS_Marshal (buffer, p->type, &p->parameters); ++ grub_tpm2_mu_TPMU_PUBLIC_ID_Marshal (buffer, p->type, &p->unique); ++} ++ ++void ++grub_tpm2_mu_TPM2B_PUBLIC_Marshal (grub_tpm2_buffer_t buffer, ++ const TPM2B_PUBLIC *p) ++{ ++ grub_uint32_t start; ++ grub_uint16_t size; ++ ++ if (p) ++ { ++ grub_tpm2_buffer_pack_u16 (buffer, p->size); ++ ++ start = buffer->size; ++ grub_tpm2_mu_TPMT_PUBLIC_Marshal (buffer, &p->publicArea); ++ size = grub_cpu_to_be16 (buffer->size - start); ++ grub_memcpy (&buffer->data[start - sizeof (grub_uint16_t)], &size, ++ sizeof (size)); ++ } ++ else ++ grub_tpm2_buffer_pack_u16 (buffer, 0); ++} ++ ++void ++grub_tpm2_mu_TPMS_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMS_SENSITIVE_CREATE *p) ++{ ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->userAuth.size, p->userAuth.buffer); ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->data.size, p->data.buffer); ++} ++ ++void ++grub_tpm2_mu_TPM2B_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buffer, ++ const TPM2B_SENSITIVE_CREATE *sensitiveCreate) ++{ ++ grub_uint32_t start; ++ grub_uint16_t size; ++ ++ if (sensitiveCreate) ++ { ++ grub_tpm2_buffer_pack_u16 (buffer, sensitiveCreate->size); ++ start = buffer->size; ++ grub_tpm2_mu_TPMS_SENSITIVE_CREATE_Marshal (buffer, ++ &sensitiveCreate->sensitive); ++ size = grub_cpu_to_be16 (buffer->size - start); ++ ++ grub_memcpy (&buffer->data[start - sizeof (grub_uint16_t)], &size, ++ sizeof (size)); ++ } ++ else ++ grub_tpm2_buffer_pack_u16 (buffer, 0); ++} ++ ++void ++grub_tpm2_mu_TPM2B_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPM2B* p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->size); ++ ++ for (grub_uint16_t i = 0; i < p->size; i++) ++ grub_tpm2_buffer_unpack_u8 (buffer, &p->buffer[i]); ++} ++ ++void ++grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_AUTH_RESPONSE* p) ++{ ++ grub_uint8_t tmp; ++ grub_uint32_t tmp32; ++ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->nonce.size); ++ ++ if (p->nonce.size) ++ grub_tpm2_buffer_unpack (buffer, &p->nonce.buffer, p->nonce.size); ++ ++ grub_tpm2_buffer_unpack_u8 (buffer, &tmp); ++ tmp32 = tmp; ++ grub_memcpy (&p->sessionAttributes, &tmp32, sizeof (grub_uint32_t)); ++ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->hmac.size); ++ ++ if (p->hmac.size) ++ grub_tpm2_buffer_unpack (buffer, &p->hmac.buffer, p->hmac.size); ++} ++ ++void ++grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPM2B_DIGEST* digest) ++{ ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*)digest); ++} ++ ++void ++grub_tpm2_mu_TPMA_OBJECT_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMA_OBJECT *p) ++{ ++ grub_tpm2_buffer_unpack_u32 (buffer, (grub_uint32_t*)p); ++} ++ ++void ++grub_tpm2_mu_TPMS_SCHEME_HMAC_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_SCHEME_HMAC *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->hashAlg); ++} ++ ++void ++grub_tpm2_mu_TPMS_SCHEME_XOR_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_SCHEME_XOR *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->hashAlg); ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->kdf); ++} ++ ++void ++grub_tpm2_mu_TPMU_SCHEME_KEYEDHASH_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMI_ALG_KEYEDHASH_SCHEME scheme, ++ TPMU_SCHEME_KEYEDHASH *p) ++{ ++ switch (scheme) ++ { ++ case TPM_ALG_HMAC: ++ grub_tpm2_mu_TPMS_SCHEME_HMAC_Unmarshal (buffer, &p->hmac); ++ break; ++ case TPM_ALG_XOR: ++ grub_tpm2_mu_TPMS_SCHEME_XOR_Unmarshal (buffer, &p->exclusiveOr); ++ break; ++ case TPM_ALG_NULL: ++ break; ++ default: ++ buffer->error = 1; ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_KEYEDHASH_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMT_KEYEDHASH_SCHEME *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->scheme); ++ grub_tpm2_mu_TPMU_SCHEME_KEYEDHASH_Unmarshal (buffer, p->scheme, &p->details); ++} ++ ++void ++grub_tpm2_mu_TPMS_KEYEDHASH_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_KEYEDHASH_PARMS *p) ++{ ++ grub_tpm2_mu_TPMT_KEYEDHASH_SCHEME_Unmarshal (buffer, &p->scheme); ++} ++ ++void ++grub_tpm2_mu_TPMU_SYM_KEY_BITS_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMI_ALG_SYM_OBJECT algorithm, ++ TPMU_SYM_KEY_BITS *p) ++{ ++ switch (algorithm) ++ { ++ case TPM_ALG_AES: ++ case TPM_ALG_SM4: ++ case TPM_ALG_CAMELLIA: ++ case TPM_ALG_XOR: ++ grub_tpm2_buffer_unpack_u16 (buffer, (grub_uint16_t*) p); ++ break; ++ case TPM_ALG_NULL: ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMU_SYM_MODE_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMI_ALG_SYM_OBJECT algorithm, ++ TPMU_SYM_MODE *p) ++{ ++ switch (algorithm) ++ { ++ case TPM_ALG_AES: ++ case TPM_ALG_SM4: ++ case TPM_ALG_CAMELLIA: ++ grub_tpm2_buffer_unpack_u16 (buffer, (grub_uint16_t*) p); ++ break; ++ case TPM_ALG_XOR: ++ case TPM_ALG_NULL: ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMT_SYM_DEF_OBJECT *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->algorithm); ++ grub_tpm2_mu_TPMU_SYM_KEY_BITS_Unmarshal (buffer, p->algorithm, &p->keyBits); ++ grub_tpm2_mu_TPMU_SYM_MODE_Unmarshal (buffer, p->algorithm, &p->mode); ++} ++ ++void ++grub_tpm2_mu_TPMS_SYMCIPHER_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_SYMCIPHER_PARMS *p) ++{ ++ grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Unmarshal (buffer, &p->sym); ++} ++ ++void ++grub_tpm2_mu_TPMU_ASYM_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMI_ALG_RSA_DECRYPT scheme, ++ TPMU_ASYM_SCHEME *p __attribute__((unused))) ++{ ++ switch (scheme) ++ { ++ case TPM_ALG_NULL: ++ break; ++ default: ++ /* Unsupported */ ++ buffer->error = 1; ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_RSA_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMT_RSA_SCHEME *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->scheme); ++ grub_tpm2_mu_TPMU_ASYM_SCHEME_Unmarshal (buffer, p->scheme, &p->details); ++} ++ ++void ++grub_tpm2_mu_TPMS_RSA_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_RSA_PARMS *p) ++{ ++ grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Unmarshal (buffer, &p->symmetric); ++ grub_tpm2_mu_TPMT_RSA_SCHEME_Unmarshal (buffer, &p->scheme); ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->keyBits); ++ grub_tpm2_buffer_unpack_u32 (buffer, &p->exponent); ++} ++ ++void ++grub_tpm2_mu_TPMT_ECC_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMT_ECC_SCHEME *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->scheme); ++ grub_tpm2_mu_TPMU_ASYM_SCHEME_Unmarshal (buffer, p->scheme, &p->details); ++} ++ ++void ++grub_tpm2_mu_TPMU_KDF_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMI_ALG_KDF scheme, ++ TPMU_KDF_SCHEME *p) ++{ ++ switch (scheme) ++ { ++ case TPM_ALG_MGF1: ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->mgf1.hashAlg); ++ break; ++ case TPM_ALG_KDF1_SP800_56A: ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->kdf1_sp800_56a.hashAlg); ++ break; ++ case TPM_ALG_KDF2: ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->kdf2.hashAlg); ++ break; ++ case TPM_ALG_KDF1_SP800_108: ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->kdf1_sp800_108.hashAlg); ++ break; ++ case TPM_ALG_NULL: ++ break; ++ default: ++ buffer->error = 1; ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_KDF_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMT_KDF_SCHEME *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->scheme); ++ grub_tpm2_mu_TPMU_KDF_SCHEME_Unmarshal (buffer, p->scheme, &p->details); ++} ++ ++void ++grub_tpm2_mu_TPMS_ECC_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_ECC_PARMS *p) ++{ ++ grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Unmarshal (buffer, &p->symmetric); ++ grub_tpm2_mu_TPMT_ECC_SCHEME_Unmarshal (buffer, &p->scheme ); ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->curveID); ++ grub_tpm2_mu_TPMT_KDF_SCHEME_Unmarshal (buffer, &p->kdf); ++} ++ ++void ++grub_tpm2_mu_TPMU_PUBLIC_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, ++ grub_uint32_t type, ++ TPMU_PUBLIC_PARMS *p) ++{ ++ switch (type) ++ { ++ case TPM_ALG_KEYEDHASH: ++ grub_tpm2_mu_TPMS_KEYEDHASH_PARMS_Unmarshal (buffer, &p->keyedHashDetail); ++ break; ++ case TPM_ALG_SYMCIPHER: ++ grub_tpm2_mu_TPMS_SYMCIPHER_PARMS_Unmarshal (buffer, &p->symDetail); ++ break; ++ case TPM_ALG_RSA: ++ grub_tpm2_mu_TPMS_RSA_PARMS_Unmarshal (buffer, &p->rsaDetail); ++ break; ++ case TPM_ALG_ECC: ++ grub_tpm2_mu_TPMS_ECC_PARMS_Unmarshal (buffer, &p->eccDetail); ++ break; ++ default: ++ buffer->error = 1; ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMS_ECC_POINT_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_ECC_POINT *p) ++{ ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->x); ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->y); ++} ++ ++void ++grub_tpm2_mu_TPMU_PUBLIC_ID_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMI_ALG_PUBLIC type, ++ TPMU_PUBLIC_ID *p) ++{ ++ switch(type) ++ { ++ case TPM_ALG_KEYEDHASH: ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->keyedHash); ++ break; ++ case TPM_ALG_SYMCIPHER: ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->sym); ++ break; ++ case TPM_ALG_RSA: ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->rsa); ++ break; ++ case TPM_ALG_ECC: ++ grub_tpm2_mu_TPMS_ECC_POINT_Unmarshal (buffer, &p->ecc); ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMT_PUBLIC *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->type); ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->nameAlg); ++ grub_tpm2_mu_TPMA_OBJECT_Unmarshal (buffer, &p->objectAttributes); ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->authPolicy); ++ grub_tpm2_mu_TPMU_PUBLIC_PARMS_Unmarshal (buffer, p->type, &p->parameters); ++ grub_tpm2_mu_TPMU_PUBLIC_ID_Unmarshal (buffer, p->type, &p->unique); ++} ++ ++void ++grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPM2B_PUBLIC *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->size); ++ grub_tpm2_mu_TPMT_PUBLIC_Unmarshal (buffer, &p->publicArea); ++} ++ ++void ++grub_tpm2_mu_TPMS_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_NV_PUBLIC *p) ++{ ++ grub_tpm2_buffer_unpack_u32 (buffer, &p->nvIndex); ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->nameAlg); ++ grub_tpm2_buffer_unpack_u32 (buffer, &p->attributes); ++ grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (buffer, &p->authPolicy); ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->dataSize); ++} ++ ++void ++grub_tpm2_mu_TPM2B_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPM2B_NV_PUBLIC *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->size); ++ grub_tpm2_mu_TPMS_NV_PUBLIC_Unmarshal (buffer, &p->nvPublic); ++} ++ ++void ++grub_tpm2_mu_TPM2B_NAME_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPM2B_NAME *n) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &n->size); ++ grub_tpm2_buffer_unpack (buffer, n->name, n->size); ++} ++ ++void ++grub_tpm2_mu_TPMS_TAGGED_PROPERTY_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_TAGGED_PROPERTY* property) ++{ ++ grub_tpm2_buffer_unpack_u32 (buffer, &property->property); ++ grub_tpm2_buffer_unpack_u32 (buffer, &property->value); ++} ++ ++void ++grub_tpm2_mu_TPMS_CAPABILITY_DATA_tpmProperties_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_CAPABILITY_DATA* capabilityData) ++{ ++ grub_tpm2_buffer_unpack_u32 (buffer, ++ &capabilityData->data.tpmProperties.count); ++ ++ if (buffer->error) ++ return; ++ ++ for (grub_uint32_t i = 0; i < capabilityData->data.tpmProperties.count; i++) ++ grub_tpm2_mu_TPMS_TAGGED_PROPERTY_Unmarshal (buffer, ++ &capabilityData->data.tpmProperties.tpmProperty[i]); ++} ++ ++void ++grub_tpm2_mu_TPMT_TK_CREATION_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMT_TK_CREATION *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->tag); ++ grub_tpm2_buffer_unpack_u32 (buffer, &p->hierarchy); ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->digest); ++} ++ ++void ++grub_tpm2_mu_TPMS_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_PCR_SELECTION* pcrSelection) ++{ ++ grub_tpm2_buffer_unpack_u16 (buf, &pcrSelection->hash); ++ grub_tpm2_buffer_unpack_u8 (buf, &pcrSelection->sizeOfSelect); ++ ++ for (grub_uint32_t i = 0; i < pcrSelection->sizeOfSelect; i++) ++ grub_tpm2_buffer_unpack_u8 (buf, &pcrSelection->pcrSelect[i]); ++} ++ ++void ++grub_tpm2_mu_TPML_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buf, ++ TPML_PCR_SELECTION* pcrSelection) ++{ ++ grub_tpm2_buffer_unpack_u32 (buf, &pcrSelection->count); ++ ++ for (grub_uint32_t i = 0; i < pcrSelection->count; i++) ++ grub_tpm2_mu_TPMS_PCR_SELECTION_Unmarshal (buf, &pcrSelection->pcrSelections[i]); ++} ++ ++void ++grub_tpm2_mu_TPML_DIGEST_Unmarshal (grub_tpm2_buffer_t buf, ++ TPML_DIGEST* digest) ++{ ++ grub_tpm2_buffer_unpack_u32 (buf, &digest->count); ++ ++ for (grub_uint32_t i = 0; i < digest->count; i++) ++ grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (buf, &digest->digests[i]); ++} +diff --git a/grub-core/tpm2/tcg2.c b/grub-core/tpm2/tcg2.c +new file mode 100644 +index 000000000..9e4b7f565 +--- /dev/null ++++ b/grub-core/tpm2/tcg2.c +@@ -0,0 +1,143 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static grub_err_t ++grub_tcg2_get_caps (grub_efi_tpm2_protocol_t *protocol, int *tpm2, ++ grub_size_t *max_output_size) ++{ ++ grub_efi_status_t status; ++ ++ static int has_caps = 0; ++ static EFI_TCG2_BOOT_SERVICE_CAPABILITY caps = ++ { ++ .Size = (grub_uint8_t) sizeof (caps) ++ }; ++ ++ if (has_caps) ++ goto exit; ++ ++ status = protocol->get_capability (protocol, &caps); ++ if (status != GRUB_EFI_SUCCESS || !caps.TPMPresentFlag) ++ return GRUB_ERR_FILE_NOT_FOUND; ++ ++ has_caps = 1; ++ ++exit: ++ if (tpm2) ++ *tpm2 = caps.TPMPresentFlag; ++ if (max_output_size) ++ *max_output_size = caps.MaxResponseSize; ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_tcg2_get_protocol (grub_efi_tpm2_protocol_t **protocol) ++{ ++ static grub_guid_t tpm2_guid = EFI_TPM2_GUID; ++ static grub_efi_tpm2_protocol_t *tpm2_protocol = NULL; ++ ++ int tpm2; ++ grub_efi_handle_t *handles; ++ grub_efi_uintn_t num_handles; ++ grub_efi_handle_t tpm2_handle; ++ grub_err_t err = GRUB_ERR_FILE_NOT_FOUND; ++ ++ if (tpm2_protocol) ++ { ++ *protocol = tpm2_protocol; ++ return GRUB_ERR_NONE; ++ } ++ ++ handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &tpm2_guid, NULL, ++ &num_handles); ++ if (!handles || !num_handles) ++ return err; ++ ++ tpm2_handle = handles[0]; ++ ++ tpm2_protocol = grub_efi_open_protocol (tpm2_handle, &tpm2_guid, ++ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); ++ if (!tpm2_protocol) ++ goto exit; ++ ++ err = grub_tcg2_get_caps (tpm2_protocol, &tpm2, NULL); ++ if (err || !tpm2) ++ goto exit; ++ ++ *protocol = tpm2_protocol; ++ err = GRUB_ERR_NONE; ++ ++exit: ++ grub_free (handles); ++ return err; ++} ++ ++grub_err_t ++grub_tcg2_get_max_output_size (grub_size_t *size) ++{ ++ grub_err_t err; ++ grub_size_t max; ++ grub_efi_tpm2_protocol_t *protocol; ++ ++ if (!size) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ err = grub_tcg2_get_protocol (&protocol); ++ if (err) ++ return err; ++ ++ err = grub_tcg2_get_caps (protocol, NULL, &max); ++ if (err) ++ return err; ++ ++ *size = max; ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_tcg2_submit_command (grub_size_t input_size, ++ grub_uint8_t *input, ++ grub_size_t output_size, ++ grub_uint8_t *output) ++{ ++ grub_err_t err; ++ grub_efi_status_t status; ++ grub_efi_tpm2_protocol_t *protocol; ++ ++ if (!input_size || !input || !output_size || !output) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ err = grub_tcg2_get_protocol (&protocol); ++ if (err) ++ return err; ++ ++ status = protocol->submit_command (protocol, input_size, input, ++ output_size, output); ++ if (status != GRUB_EFI_SUCCESS) ++ return GRUB_ERR_INVALID_COMMAND; ++ ++ return GRUB_ERR_NONE; ++} +diff --git a/grub-core/tpm2/tpm2.c b/grub-core/tpm2/tpm2.c +new file mode 100644 +index 000000000..d67699a24 +--- /dev/null ++++ b/grub-core/tpm2/tpm2.c +@@ -0,0 +1,761 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static TPM_RC ++grub_tpm2_submit_command_real (const TPMI_ST_COMMAND_TAG tag, ++ const TPM_CC commandCode, ++ TPM_RC *responseCode, ++ const struct grub_tpm2_buffer *in, ++ struct grub_tpm2_buffer *out) ++{ ++ grub_err_t err; ++ struct grub_tpm2_buffer buf; ++ TPMI_ST_COMMAND_TAG tag_out; ++ grub_uint32_t command_size; ++ grub_size_t max_output_size; ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&buf); ++ grub_tpm2_buffer_pack_u16 (&buf, tag); ++ grub_tpm2_buffer_pack_u32 (&buf, 0); ++ grub_tpm2_buffer_pack_u32 (&buf, commandCode); ++ grub_tpm2_buffer_pack (&buf, in->data, in->size); ++ ++ if (buf.error) ++ return TPM_RC_FAILURE; ++ ++ command_size = grub_swap_bytes32 (buf.size); ++ grub_memcpy (&buf.data[sizeof (grub_uint16_t)], &command_size, ++ sizeof (command_size)); ++ ++ /* Stay within output block limits */ ++ err = grub_tcg2_get_max_output_size (&max_output_size); ++ if (err || max_output_size > out->cap) ++ max_output_size = out->cap - 1; ++ ++ /* Submit */ ++ err = grub_tcg2_submit_command (buf.size, buf.data, max_output_size, ++ out->data); ++ if (err) ++ return TPM_RC_FAILURE; ++ ++ /* Unmarshal*/ ++ out->size = sizeof (grub_uint16_t) + sizeof (grub_uint32_t) + ++ sizeof (grub_uint32_t); ++ grub_tpm2_buffer_unpack_u16 (out, &tag_out); ++ grub_tpm2_buffer_unpack_u32 (out, &command_size); ++ grub_tpm2_buffer_unpack_u32 (out, responseCode); ++ out->size = command_size; ++ if (out->error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++static TPM_RC ++grub_tpm2_submit_command (const TPMI_ST_COMMAND_TAG tag, ++ const TPM_CC commandCode, ++ TPM_RC *responseCode, ++ const struct grub_tpm2_buffer *in, ++ struct grub_tpm2_buffer *out) ++{ ++ TPM_RC err; ++ int retry_cnt = 0; ++ ++ /* Catch TPM_RC_RETRY and send the command again */ ++ do { ++ err = grub_tpm2_submit_command_real (tag, commandCode, responseCode, ++ in, out); ++ if (*responseCode != TPM_RC_RETRY) ++ break; ++ ++ retry_cnt++; ++ } while (retry_cnt < 3); ++ ++ return err; ++} ++ ++TPM_RC ++TPM2_CreatePrimary (const TPMI_RH_HIERARCHY primaryHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_SENSITIVE_CREATE *inSensitive, ++ const TPM2B_PUBLIC *inPublic, ++ const TPM2B_DATA *outsideInfo, ++ const TPML_PCR_SELECTION *creationPCR, ++ TPM_HANDLE *objectHandle, ++ TPM2B_PUBLIC *outPublic, ++ TPM2B_CREATION_DATA *creationData, ++ TPM2B_DIGEST *creationHash, ++ TPMT_TK_CREATION *creationTicket, ++ TPM2B_NAME *name, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPM_HANDLE objectHandleTmp; ++ TPM2B_PUBLIC outPublicTmp; ++ TPM2B_CREATION_DATA creationDataTmp; ++ TPM2B_DIGEST creationHashTmp; ++ TPMT_TK_CREATION creationTicketTmp; ++ TPM2B_NAME nameTmp; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ grub_uint32_t parameterSize; ++ ++ if (!inSensitive || !inPublic || !outsideInfo || !creationPCR) ++ return TPM_RC_VALUE; ++ ++ if (!objectHandle) ++ objectHandle = &objectHandleTmp; ++ if (!outPublic) ++ outPublic = &outPublicTmp; ++ if (!creationData) ++ creationData = &creationDataTmp; ++ if (!creationHash) ++ creationHash = &creationHashTmp; ++ if (!creationTicket) ++ creationTicket = &creationTicketTmp; ++ if (!name) ++ name = &nameTmp; ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (outPublic, 0, sizeof (*outPublic)); ++ grub_memset (creationData, 0, sizeof (*creationData)); ++ grub_memset (creationHash, 0, sizeof (*creationHash)); ++ grub_memset (creationTicket, 0, sizeof (*creationTicket)); ++ grub_memset (name, 0, sizeof (*name)); ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, primaryHandle); ++ if (authCommand) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ grub_tpm2_mu_TPM2B_SENSITIVE_CREATE_Marshal (&in, inSensitive); ++ grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&in, inPublic); ++ grub_tpm2_mu_TPM2B_Marshal (&in, outsideInfo->size, outsideInfo->buffer); ++ grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (&in, creationPCR); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_CreatePrimary, &responseCode, &in, ++ &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ grub_tpm2_buffer_unpack_u32 (&out, objectHandle); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); ++ grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&out, outPublic); ++ grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)creationData); ++ grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)creationHash); ++ grub_tpm2_mu_TPMT_TK_CREATION_Unmarshal (&out, creationTicket); ++ grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)name); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_StartAuthSession (const TPMI_DH_OBJECT tpmKey, ++ const TPMI_DH_ENTITY bind, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_NONCE *nonceCaller, ++ const TPM2B_ENCRYPTED_SECRET *encryptedSalt, ++ const TPM_SE sessionType, ++ const TPMT_SYM_DEF *symmetric, ++ const TPMI_ALG_HASH authHash, ++ TPMI_SH_AUTH_SESSION *sessionHandle, ++ TPM2B_NONCE *nonceTpm, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPMI_SH_AUTH_SESSION sessionHandleTmp; ++ TPM2B_NONCE nonceTpmTmp; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ grub_uint32_t param_size; ++ ++ if (!nonceCaller || !symmetric) ++ return TPM_RC_VALUE; ++ ++ if (tpmKey == TPM_RH_NULL && ++ (encryptedSalt && encryptedSalt->size != 0)) ++ return TPM_RC_VALUE; ++ ++ if (!sessionHandle) ++ sessionHandle = &sessionHandleTmp; ++ if (!nonceTpm) ++ nonceTpm = &nonceTpmTmp; ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (sessionHandle, 0, sizeof (*sessionHandle)); ++ grub_memset (nonceTpm, 0, sizeof (*nonceTpm)); ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, tpmKey); ++ grub_tpm2_buffer_pack_u32 (&in, bind); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ grub_tpm2_mu_TPM2B_Marshal (&in, nonceCaller->size, nonceCaller->buffer); ++ if (encryptedSalt) ++ grub_tpm2_mu_TPM2B_Marshal (&in, encryptedSalt->size, encryptedSalt->secret); ++ else ++ grub_tpm2_buffer_pack_u16 (&in, 0); ++ grub_tpm2_buffer_pack_u8 (&in, sessionType); ++ grub_tpm2_mu_TPMT_SYM_DEF_Marshal (&in, symmetric); ++ grub_tpm2_buffer_pack_u16 (&in, authHash); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_StartAuthSession, &responseCode, ++ &in, &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ grub_tpm2_buffer_unpack_u32 (&out, sessionHandle); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); ++ grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)nonceTpm); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_PolicyPCR (const TPMI_SH_POLICY policySessions, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_DIGEST *pcrDigest, ++ const TPML_PCR_SELECTION *pcrs, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ grub_uint32_t param_size; ++ ++ if (!pcrs) ++ return TPM_RC_VALUE; ++ ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, policySessions); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ if (pcrDigest) ++ grub_tpm2_mu_TPM2B_Marshal (&in, pcrDigest->size, pcrDigest->buffer); ++ else ++ grub_tpm2_buffer_pack_u16 (&in, 0); ++ grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (&in, pcrs); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_PolicyPCR, &responseCode, &in, ++ &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_ReadPublic (const TPMI_DH_OBJECT objectHandle, ++ const TPMS_AUTH_COMMAND* authCommand, ++ TPM2B_PUBLIC *outPublic) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ grub_uint32_t parameterSize; ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, objectHandle); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_ReadPublic, &responseCode, &in, ++ &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); ++ grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&out, outPublic); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_Load (const TPMI_DH_OBJECT parent_handle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_PRIVATE *inPrivate, ++ const TPM2B_PUBLIC *inPublic, ++ TPM_HANDLE *objectHandle, ++ TPM2B_NAME *name, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPM_HANDLE objectHandleTmp; ++ TPM2B_NAME nonceTmp; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ grub_uint32_t param_size; ++ ++ if (!inPrivate || !inPublic) ++ return TPM_RC_VALUE; ++ ++ if (!objectHandle) ++ objectHandle = &objectHandleTmp; ++ if (!name) ++ name = &nonceTmp; ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (objectHandle, 0, sizeof (*objectHandle)); ++ grub_memset (name, 0, sizeof (*name)); ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, parent_handle); ++ if (authCommand) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ grub_tpm2_mu_TPM2B_Marshal (&in, inPrivate->size, inPrivate->buffer); ++ grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&in, inPublic); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_Load, &responseCode, &in, &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ grub_tpm2_buffer_unpack_u32 (&out, objectHandle); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); ++ grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)name); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_Unseal (const TPMI_DH_OBJECT itemHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ TPM2B_SENSITIVE_DATA *outData, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPM2B_SENSITIVE_DATA outDataTmp; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ grub_uint32_t param_size; ++ ++ if (!outData) ++ outData = &outDataTmp; ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (outData, 0, sizeof (*outData)); ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, itemHandle); ++ if (authCommand) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_Unseal, &responseCode, &in, &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ // Unmarhsal ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); ++ grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)outData); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_FlushContext (const TPMI_DH_CONTEXT handle) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPM_RC responseCode; ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, handle); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (TPM_ST_NO_SESSIONS, TPM_CC_FlushContext, ++ &responseCode, &in, &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_PCR_Read (const TPMS_AUTH_COMMAND *authCommand, ++ const TPML_PCR_SELECTION *pcrSelectionIn, ++ grub_uint32_t *pcrUpdateCounter, ++ TPML_PCR_SELECTION *pcrSelectionOut, ++ TPML_DIGEST *pcrValues, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ grub_uint32_t pcrUpdateCounterTmp; ++ TPML_PCR_SELECTION pcrSelectionOutTmp; ++ TPML_DIGEST pcrValuesTmp; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ grub_uint32_t parameterSize; ++ ++ if (!pcrSelectionIn) ++ return TPM_RC_VALUE; ++ ++ if (!pcrUpdateCounter) ++ pcrUpdateCounter = &pcrUpdateCounterTmp; ++ if (!pcrSelectionOut) ++ pcrSelectionOut = &pcrSelectionOutTmp; ++ if (!pcrValues) ++ pcrValues = &pcrValuesTmp; ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ if (authCommand) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (&in, pcrSelectionIn); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_PCR_Read, &responseCode, &in, ++ &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); ++ grub_tpm2_buffer_unpack_u32 (&out, pcrUpdateCounter); ++ grub_tpm2_mu_TPML_PCR_SELECTION_Unmarshal (&out, pcrSelectionOut); ++ grub_tpm2_mu_TPML_DIGEST_Unmarshal (&out, pcrValues); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_PolicyGetDigest (const TPMI_SH_POLICY policySession, ++ const TPMS_AUTH_COMMAND *authCommand, ++ TPM2B_DIGEST *policyDigest, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPM2B_DIGEST policyDigestTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ grub_uint32_t parameterSize; ++ ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ if (!policyDigest) ++ policyDigest = &policyDigestTmp; ++ ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ grub_memset (policyDigest, 0, sizeof (*policyDigest)); ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, policySession); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_PolicyGetDigest, &responseCode, ++ &in, &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); ++ grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)policyDigest); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_Create (const TPMI_DH_OBJECT parentHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_SENSITIVE_CREATE *inSensitive, ++ const TPM2B_PUBLIC *inPublic, ++ const TPM2B_DATA *outsideInfo, ++ const TPML_PCR_SELECTION *creationPCR, ++ TPM2B_PRIVATE *outPrivate, ++ TPM2B_PUBLIC *outPublic, ++ TPM2B_CREATION_DATA *creationData, ++ TPM2B_DIGEST *creationHash, ++ TPMT_TK_CREATION *creationTicket, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPM2B_PUBLIC outPublicTmp; ++ TPM2B_PRIVATE outPrivateTmp; ++ TPM2B_CREATION_DATA creationDataTmp; ++ TPM2B_DIGEST creationHashTmp; ++ TPMT_TK_CREATION creationTicketTmp; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS:TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ TPM_RC rc; ++ grub_uint32_t parameterSize; ++ ++ if (!inSensitive || !inPublic || !outsideInfo || !creationPCR) ++ return TPM_RC_VALUE; ++ ++ if (!outPrivate) ++ outPrivate = &outPrivateTmp; ++ if (!outPublic) ++ outPublic = &outPublicTmp; ++ if (!creationData) ++ creationData = &creationDataTmp; ++ if (!creationHash) ++ creationHash = &creationHashTmp; ++ if (!creationTicket) ++ creationTicket = &creationTicketTmp; ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (outPrivate, 0, sizeof (*outPrivate)); ++ grub_memset (outPublic, 0, sizeof (*outPublic)); ++ grub_memset (creationData, 0, sizeof (*creationData)); ++ grub_memset (creationHash, 0, sizeof (*creationHash)); ++ grub_memset (creationTicket, 0, sizeof (*creationTicket)); ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, parentHandle); ++ if (authCommand) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ grub_tpm2_mu_TPM2B_SENSITIVE_CREATE_Marshal (&in, inSensitive); ++ grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&in, inPublic); ++ grub_tpm2_mu_TPM2B_Marshal (&in, outsideInfo->size, outsideInfo->buffer); ++ grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (&in, creationPCR); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_Create, &responseCode, &in, ++ &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); ++ grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)outPrivate); ++ grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&out, outPublic); ++ grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)creationData); ++ grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)creationHash); ++ grub_tpm2_mu_TPMT_TK_CREATION_Unmarshal (&out, creationTicket); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal(&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_EvictControl (const TPMI_RH_PROVISION auth, ++ const TPMI_DH_OBJECT objectHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPMI_DH_PERSISTENT persistentHandle, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ TPM_RC rc; ++ grub_uint32_t parameterSize; ++ ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, auth); ++ grub_tpm2_buffer_pack_u32 (&in, objectHandle); ++ if (authCommand) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ grub_tpm2_buffer_pack_u32 (&in, persistentHandle); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_EvictControl, &responseCode, &in, ++ &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ if (tag == TPM_ST_SESSIONS) ++ { ++ grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal(&out, authResponse); ++ } ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} +diff --git a/include/grub/tpm2/buffer.h b/include/grub/tpm2/buffer.h +new file mode 100644 +index 000000000..87dcd8d6c +--- /dev/null ++++ b/include/grub/tpm2/buffer.h +@@ -0,0 +1,65 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_TPM2_BUFFER_HEADER ++#define GRUB_TPM2_BUFFER_HEADER 1 ++ ++#include ++ ++#define GRUB_TPM2_BUFFER_CAPACITY 4096 ++ ++struct grub_tpm2_buffer ++{ ++ grub_uint8_t data[GRUB_TPM2_BUFFER_CAPACITY]; ++ grub_size_t size; ++ grub_size_t offset; ++ grub_size_t cap; ++ int error; ++}; ++typedef struct grub_tpm2_buffer *grub_tpm2_buffer_t; ++ ++void ++grub_tpm2_buffer_init (grub_tpm2_buffer_t buffer); ++ ++void ++grub_tpm2_buffer_pack (grub_tpm2_buffer_t buffer, const void* data, ++ grub_size_t size); ++ ++void ++grub_tpm2_buffer_pack_u8 (grub_tpm2_buffer_t buffer, grub_uint8_t value); ++ ++void ++grub_tpm2_buffer_pack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t value); ++ ++void ++grub_tpm2_buffer_pack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t value); ++ ++void ++grub_tpm2_buffer_unpack (grub_tpm2_buffer_t buffer, void* data, ++ grub_size_t size); ++ ++void ++grub_tpm2_buffer_unpack_u8 (grub_tpm2_buffer_t buffer, grub_uint8_t* value); ++ ++void ++grub_tpm2_buffer_unpack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t* value); ++ ++void ++grub_tpm2_buffer_unpack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t* value); ++ ++#endif /* ! GRUB_TPM2_BUFFER_HEADER */ +diff --git a/include/grub/tpm2/internal/functions.h b/include/grub/tpm2/internal/functions.h +new file mode 100644 +index 000000000..9380f26a2 +--- /dev/null ++++ b/include/grub/tpm2/internal/functions.h +@@ -0,0 +1,117 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_TPM2_INTERNAL_FUNCTIONS_HEADER ++#define GRUB_TPM2_INTERNAL_FUNCTIONS_HEADER 1 ++ ++#include ++ ++TPM_RC ++TPM2_CreatePrimary (const TPMI_RH_HIERARCHY primaryHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_SENSITIVE_CREATE *inSensitive, ++ const TPM2B_PUBLIC *inPublic, ++ const TPM2B_DATA *outsideInfo, ++ const TPML_PCR_SELECTION *creationPCR, ++ TPM_HANDLE *objectHandle, ++ TPM2B_PUBLIC *outPublic, ++ TPM2B_CREATION_DATA *creationData, ++ TPM2B_DIGEST *creationHash, ++ TPMT_TK_CREATION *creationTicket, ++ TPM2B_NAME *name, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_StartAuthSession (const TPMI_DH_OBJECT tpmKey, ++ const TPMI_DH_ENTITY bind, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_NONCE *nonceCaller, ++ const TPM2B_ENCRYPTED_SECRET *encryptedSalt, ++ const TPM_SE sessionType, ++ const TPMT_SYM_DEF *symmetric, ++ const TPMI_ALG_HASH authHash, ++ TPMI_SH_AUTH_SESSION *sessionHandle, ++ TPM2B_NONCE *nonceTpm, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_PolicyPCR (const TPMI_SH_POLICY policySession, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_DIGEST *pcrDigest, ++ const TPML_PCR_SELECTION *pcrs, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_ReadPublic (const TPMI_DH_OBJECT objectHandle, ++ const TPMS_AUTH_COMMAND* authCommand, ++ TPM2B_PUBLIC *outPublic); ++ ++TPM_RC ++TPM2_Load (const TPMI_DH_OBJECT parent_handle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_PRIVATE *inPrivate, ++ const TPM2B_PUBLIC *inPublic, ++ TPM_HANDLE *objectHandle, ++ TPM2B_NAME *name, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_Unseal (const TPMI_DH_OBJECT item_handle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ TPM2B_SENSITIVE_DATA *outData, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_FlushContext (const TPMI_DH_CONTEXT handle); ++ ++TPM_RC ++TPM2_PCR_Read (const TPMS_AUTH_COMMAND *authCommand, ++ const TPML_PCR_SELECTION *pcrSelectionIn, ++ grub_uint32_t *pcrUpdateCounter, ++ TPML_PCR_SELECTION *pcrSelectionOut, ++ TPML_DIGEST *pcrValues, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_PolicyGetDigest (const TPMI_SH_POLICY policySession, ++ const TPMS_AUTH_COMMAND *authCommand, ++ TPM2B_DIGEST *policyDigest, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_Create (const TPMI_DH_OBJECT parentHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_SENSITIVE_CREATE *inSensitive, ++ const TPM2B_PUBLIC *inPublic, ++ const TPM2B_DATA *outsideInfo, ++ const TPML_PCR_SELECTION *creationPCR, ++ TPM2B_PRIVATE *outPrivate, ++ TPM2B_PUBLIC *outPublic, ++ TPM2B_CREATION_DATA *creationData, ++ TPM2B_DIGEST *creationHash, ++ TPMT_TK_CREATION *creationTicket, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_EvictControl (const TPMI_RH_PROVISION auth, ++ const TPMI_DH_OBJECT objectHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPMI_DH_PERSISTENT persistentHandle, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++#endif /* ! GRUB_TPM2_INTERNAL_FUNCTIONS_HEADER */ +diff --git a/include/grub/tpm2/internal/structs.h b/include/grub/tpm2/internal/structs.h +new file mode 100644 +index 000000000..72d71eb70 +--- /dev/null ++++ b/include/grub/tpm2/internal/structs.h +@@ -0,0 +1,675 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_TPM2_INTERNAL_STRUCTS_HEADER ++#define GRUB_TPM2_INTERNAL_STRUCTS_HEADER 1 ++ ++#include ++ ++/* TPMS_TAGGED_PROPERTY Structure */ ++struct TPMS_TAGGED_PROPERTY ++{ ++ TPM_PT property; ++ grub_uint32_t value; ++}; ++typedef struct TPMS_TAGGED_PROPERTY TPMS_TAGGED_PROPERTY; ++ ++/* TPML_TAGGED_TPM_PROPERTY Structure */ ++struct TPML_TAGGED_TPM_PROPERTY ++{ ++ grub_uint32_t count; ++ TPMS_TAGGED_PROPERTY tpmProperty[TPM_MAX_TPM_PROPERTIES]; ++}; ++typedef struct TPML_TAGGED_TPM_PROPERTY TPML_TAGGED_TPM_PROPERTY; ++ ++/* TPMU_CAPABILITIES Structure */ ++union TPMU_CAPABILITIES ++{ ++ TPML_TAGGED_TPM_PROPERTY tpmProperties; ++}; ++typedef union TPMU_CAPABILITIES TPMU_CAPABILITIES; ++ ++/* TPMS_CAPABILITY_DATA Structure */ ++struct TPMS_CAPABILITY_DATA ++{ ++ TPM_CAP capability; ++ TPMU_CAPABILITIES data; ++}; ++typedef struct TPMS_CAPABILITY_DATA TPMS_CAPABILITY_DATA; ++ ++/* TPMS_PCR_SELECT Structure */ ++struct TPMS_PCR_SELECT ++{ ++ grub_uint8_t sizeOfSelect; ++ grub_uint8_t pcrSelect[TPM_PCR_SELECT_MAX]; ++}; ++typedef struct TPMS_PCR_SELECT TPMS_PCR_SELECT; ++ ++/* TPMS_PCR_SELECTION Structure */ ++struct TPMS_PCR_SELECTION ++{ ++ TPMI_ALG_HASH hash; ++ grub_uint8_t sizeOfSelect; ++ grub_uint8_t pcrSelect[TPM_PCR_SELECT_MAX]; ++}; ++typedef struct TPMS_PCR_SELECTION TPMS_PCR_SELECTION; ++ ++static inline void TPMS_PCR_SELECTION_SelectPCR(TPMS_PCR_SELECTION* self, grub_uint32_t n) ++{ ++ self->pcrSelect[(n / 8)] |= (1 << (n % 8)); ++} ++ ++/* TPML_PCR_SELECTION Structure */ ++struct TPML_PCR_SELECTION ++{ ++ grub_uint32_t count; ++ TPMS_PCR_SELECTION pcrSelections[TPM_NUM_PCR_BANKS]; ++}; ++typedef struct TPML_PCR_SELECTION TPML_PCR_SELECTION; ++ ++/* TPMU_HA Structure */ ++union TPMU_HA ++{ ++ grub_uint8_t sha1[TPM_SHA1_DIGEST_SIZE]; ++ grub_uint8_t sha256[TPM_SHA256_DIGEST_SIZE]; ++ grub_uint8_t sha384[TPM_SHA384_DIGEST_SIZE]; ++ grub_uint8_t sha512[TPM_SHA512_DIGEST_SIZE]; ++ grub_uint8_t sm3_256[TPM_SM3_256_DIGEST_SIZE]; ++}; ++typedef union TPMU_HA TPMU_HA; ++ ++/* TPM2B Structure */ ++struct TPM2B ++{ ++ grub_uint16_t size; ++ grub_uint8_t buffer[1]; ++}; ++typedef struct TPM2B TPM2B; ++ ++/* TPM2B_DIGEST Structure */ ++struct TPM2B_DIGEST ++{ ++ grub_uint16_t size; ++ grub_uint8_t buffer[sizeof(TPMU_HA)]; ++}; ++typedef struct TPM2B_DIGEST TPM2B_DIGEST; ++ ++/* TPML_DIGEST Structure */ ++struct TPML_DIGEST ++{ ++ grub_uint32_t count; ++ TPM2B_DIGEST digests[8]; ++}; ++typedef struct TPML_DIGEST TPML_DIGEST; ++ ++/* TPM2B_NONCE Type */ ++typedef TPM2B_DIGEST TPM2B_NONCE; ++ ++/* TPMA_SESSION Structure */ ++struct TPMA_SESSION ++{ ++ unsigned int continueSession:1; ++ unsigned int auditExclusive:1; ++ unsigned int auditReset:1; ++ unsigned int reserved1:2; ++ unsigned int decrypt:1; ++ unsigned int encrypt:1; ++ unsigned int audit:1; ++ unsigned int reserved:24; ++}; ++typedef struct TPMA_SESSION TPMA_SESSION; ++ ++/* TPM2B_AUTH Type */ ++typedef TPM2B_DIGEST TPM2B_AUTH; ++ ++/* TPMS_AUTH_COMMAND Structure */ ++struct TPMS_AUTH_COMMAND ++{ ++ TPMI_SH_AUTH_SESSION sessionHandle; ++ TPM2B_NONCE nonce; ++ TPMA_SESSION sessionAttributes; ++ TPM2B_AUTH hmac; ++}; ++typedef struct TPMS_AUTH_COMMAND TPMS_AUTH_COMMAND; ++ ++/* TPMS_AUTH_RESPONSE Structure */ ++struct TPMS_AUTH_RESPONSE ++{ ++ TPM2B_NONCE nonce; ++ TPMA_SESSION sessionAttributes; ++ TPM2B_AUTH hmac; ++}; ++typedef struct TPMS_AUTH_RESPONSE TPMS_AUTH_RESPONSE; ++ ++/* TPM2B_SENSITIVE_DATA Structure */ ++struct TPM2B_SENSITIVE_DATA ++{ ++ grub_uint16_t size; ++ grub_uint8_t buffer[TPM_MAX_SYM_DATA]; ++}; ++typedef struct TPM2B_SENSITIVE_DATA TPM2B_SENSITIVE_DATA; ++ ++/* TPMS_SENSITIVE_CREATE Structure */ ++struct TPMS_SENSITIVE_CREATE ++{ ++ TPM2B_AUTH userAuth; ++ TPM2B_SENSITIVE_DATA data; ++}; ++typedef struct TPMS_SENSITIVE_CREATE TPMS_SENSITIVE_CREATE; ++ ++/* TPM2B_SENSITIVE_CREATE Structure */ ++struct TPM2B_SENSITIVE_CREATE ++{ ++ grub_uint16_t size; ++ TPMS_SENSITIVE_CREATE sensitive; ++}; ++typedef struct TPM2B_SENSITIVE_CREATE TPM2B_SENSITIVE_CREATE; ++ ++/* TPMA_OBJECT Structure */ ++struct TPMA_OBJECT ++{ ++ unsigned int reserved1:1; ++ unsigned int fixedTPM:1; ++ unsigned int stClear:1; ++ unsigned int reserved2:1; ++ unsigned int fixedParent:1; ++ unsigned int sensitiveDataOrigin:1; ++ unsigned int userWithAuth:1; ++ unsigned int adminWithPolicy:1; ++ unsigned int reserved3:2; ++ unsigned int noDA:1; ++ unsigned int encryptedDuplication:1; ++ unsigned int reserved4:4; ++ unsigned int restricted:1; ++ unsigned int decrypt:1; ++ unsigned int sign:1; ++ unsigned int reserved5:13; ++}; ++typedef struct TPMA_OBJECT TPMA_OBJECT; ++ ++/* TPMS_SCHEME_HASH Structure */ ++struct TPMS_SCHEME_HASH ++{ ++ TPMI_ALG_HASH hashAlg; ++}; ++typedef struct TPMS_SCHEME_HASH TPMS_SCHEME_HASH; ++ ++/* TPMS_SCHEME_HASH Types */ ++typedef TPMS_SCHEME_HASH TPMS_KEY_SCHEME_ECDH; ++typedef TPMS_SCHEME_HASH TPMS_KEY_SCHEME_ECMQV; ++typedef TPMS_SCHEME_HASH TPMS_SIG_SCHEME_RSASSA; ++typedef TPMS_SCHEME_HASH TPMS_SIG_SCHEME_RSAPSS; ++typedef TPMS_SCHEME_HASH TPMS_SIG_SCHEME_ECDSA; ++typedef TPMS_SCHEME_HASH TPMS_SIG_SCHEME_ECDAA; ++typedef TPMS_SCHEME_HASH TPMS_SIG_SCHEME_SM2; ++typedef TPMS_SCHEME_HASH TPMS_SIG_SCHEME_ECSCHNORR; ++typedef TPMS_SCHEME_HASH TPMS_ENC_SCHEME_RSAES; ++typedef TPMS_SCHEME_HASH TPMS_ENC_SCHEME_OAEP; ++typedef TPMS_SCHEME_HASH TPMS_SCHEME_KDF2; ++typedef TPMS_SCHEME_HASH TPMS_SCHEME_MGF1; ++typedef TPMS_SCHEME_HASH TPMS_SCHEME_KDF1_SP800_56A; ++typedef TPMS_SCHEME_HASH TPMS_SCHEME_KDF2; ++typedef TPMS_SCHEME_HASH TPMS_SCHEME_KDF1_SP800_108; ++ ++/* TPMS_SCHEME_HMAC Type */ ++typedef TPMS_SCHEME_HASH TPMS_SCHEME_HMAC; ++ ++/* TPMS_SCHEME_XOR Structure */ ++struct TPMS_SCHEME_XOR ++{ ++ TPMI_ALG_HASH hashAlg; ++ TPMI_ALG_KDF kdf; ++}; ++typedef struct TPMS_SCHEME_XOR TPMS_SCHEME_XOR; ++ ++/* TPMU_SCHEME_KEYEDHASH Union */ ++union TPMU_SCHEME_KEYEDHASH ++{ ++ TPMS_SCHEME_HMAC hmac; ++ TPMS_SCHEME_XOR exclusiveOr; ++}; ++typedef union TPMU_SCHEME_KEYEDHASH TPMU_SCHEME_KEYEDHASH; ++ ++/* TPMT_KEYEDHASH_SCHEME Structure */ ++struct TPMT_KEYEDHASH_SCHEME ++{ ++ TPMI_ALG_KEYEDHASH_SCHEME scheme; ++ TPMU_SCHEME_KEYEDHASH details; ++}; ++typedef struct TPMT_KEYEDHASH_SCHEME TPMT_KEYEDHASH_SCHEME; ++ ++/* TPMS_KEYEDHASH_PARMS Structure */ ++struct TPMS_KEYEDHASH_PARMS ++{ ++ TPMT_KEYEDHASH_SCHEME scheme; ++}; ++typedef struct TPMS_KEYEDHASH_PARMS TPMS_KEYEDHASH_PARMS; ++ ++/* TPMU_SYM_KEY_BITS Union */ ++union TPMU_SYM_KEY_BITS ++{ ++ TPM_KEY_BITS aes; ++ TPM_KEY_BITS exclusiveOr; ++ TPM_KEY_BITS sm4; ++ TPM_KEY_BITS camellia; ++}; ++typedef union TPMU_SYM_KEY_BITS TPMU_SYM_KEY_BITS; ++ ++/* TPMU_SYM_MODE Union */ ++union TPMU_SYM_MODE ++{ ++ TPMI_ALG_SYM_MODE aes; ++ TPMI_ALG_SYM_MODE sm4; ++ TPMI_ALG_SYM_MODE camellia; ++ TPMI_ALG_SYM_MODE sym; ++}; ++typedef union TPMU_SYM_MODE TPMU_SYM_MODE; ++ ++/* TPMT_SYM_DEF_OBJECT Structure */ ++struct TPMT_SYM_DEF_OBJECT ++{ ++ TPMI_ALG_SYM_OBJECT algorithm; ++ TPMU_SYM_KEY_BITS keyBits; ++ TPMU_SYM_MODE mode; ++}; ++typedef struct TPMT_SYM_DEF_OBJECT TPMT_SYM_DEF_OBJECT; ++ ++/* TPMS_SYMCIPHER_PARMS Structure */ ++struct TPMS_SYMCIPHER_PARMS ++{ ++ TPMT_SYM_DEF_OBJECT sym; ++}; ++typedef struct TPMS_SYMCIPHER_PARMS TPMS_SYMCIPHER_PARMS; ++ ++/* TPMU_ASYM_SCHEME Union */ ++union TPMU_ASYM_SCHEME ++{ ++ TPMS_KEY_SCHEME_ECDH ecdh; ++ TPMS_KEY_SCHEME_ECMQV ecmqv; ++ TPMS_SIG_SCHEME_RSASSA rsassa; ++ TPMS_SIG_SCHEME_RSAPSS rsapss; ++ TPMS_SIG_SCHEME_ECDSA ecdsa; ++ TPMS_SIG_SCHEME_ECDAA ecdaa; ++ TPMS_SIG_SCHEME_SM2 sm2; ++ TPMS_SIG_SCHEME_ECSCHNORR ecschnorr; ++ TPMS_ENC_SCHEME_RSAES rsaes; ++ TPMS_ENC_SCHEME_OAEP oaep; ++ TPMS_SCHEME_HASH anySig; ++ unsigned char padding[4]; ++}; ++typedef union TPMU_ASYM_SCHEME TPMU_ASYM_SCHEME; ++ ++/* TPMT_RSA_SCHEME Structure */ ++struct TPMT_RSA_SCHEME ++{ ++ TPMI_ALG_RSA_SCHEME scheme; ++ TPMU_ASYM_SCHEME details; ++}; ++typedef struct TPMT_RSA_SCHEME TPMT_RSA_SCHEME; ++ ++/* TPMS_RSA_PARMS Structure */ ++struct TPMS_RSA_PARMS ++{ ++ TPMT_SYM_DEF_OBJECT symmetric; ++ TPMT_RSA_SCHEME scheme; ++ TPM_KEY_BITS keyBits; ++ grub_uint32_t exponent; ++}; ++typedef struct TPMS_RSA_PARMS TPMS_RSA_PARMS; ++ ++/* TPMT_ECC_SCHEME Structure */ ++struct TPMT_ECC_SCHEME ++{ ++ TPMI_ALG_ECC_SCHEME scheme; ++ TPMU_ASYM_SCHEME details; ++}; ++typedef struct TPMT_ECC_SCHEME TPMT_ECC_SCHEME; ++ ++/* TPMU_KDF_SCHEME Union */ ++union TPMU_KDF_SCHEME ++{ ++ TPMS_SCHEME_MGF1 mgf1; ++ TPMS_SCHEME_KDF1_SP800_56A kdf1_sp800_56a; ++ TPMS_SCHEME_KDF2 kdf2; ++ TPMS_SCHEME_KDF1_SP800_108 kdf1_sp800_108; ++}; ++typedef union TPMU_KDF_SCHEME TPMU_KDF_SCHEME; ++ ++/* TPMT_KDF_SCHEME Structure */ ++struct TPMT_KDF_SCHEME ++{ ++ TPMI_ALG_KDF scheme; ++ TPMU_KDF_SCHEME details; ++}; ++typedef struct TPMT_KDF_SCHEME TPMT_KDF_SCHEME; ++ ++/* TPMS_ECC_PARMS Structure */ ++struct TPMS_ECC_PARMS ++{ ++ TPMT_SYM_DEF_OBJECT symmetric; ++ TPMT_ECC_SCHEME scheme; ++ TPMI_ECC_CURVE curveID; ++ TPMT_KDF_SCHEME kdf; ++}; ++typedef struct TPMS_ECC_PARMS TPMS_ECC_PARMS; ++ ++/* TPMT_ASYM_SCHEME Structure */ ++struct TPMT_ASYM_SCHEME ++{ ++ TPMI_ALG_ASYM_SCHEME scheme; ++ TPMU_ASYM_SCHEME details; ++}; ++typedef struct TPMT_ASYM_SCHEME TPMT_ASYM_SCHEME; ++ ++/* TPMS_ASYM_PARMS Structure */ ++struct TPMS_ASYM_PARMS ++{ ++ TPMT_SYM_DEF_OBJECT symmetric; ++ TPMT_ASYM_SCHEME scheme; ++}; ++typedef struct TPMS_ASYM_PARMS TPMS_ASYM_PARMS; ++ ++/* TPMU_PUBLIC_PARMS Union */ ++union TPMU_PUBLIC_PARMS ++{ ++ TPMS_KEYEDHASH_PARMS keyedHashDetail; ++ TPMS_SYMCIPHER_PARMS symDetail; ++ TPMS_RSA_PARMS rsaDetail; ++ TPMS_ECC_PARMS eccDetail; ++ TPMS_ASYM_PARMS asymDetail; ++}; ++typedef union TPMU_PUBLIC_PARMS TPMU_PUBLIC_PARMS; ++ ++/* TPM2B_PUBLIC_KEY_RSA Structure */ ++struct TPM2B_PUBLIC_KEY_RSA ++{ ++ grub_uint16_t size; ++ grub_uint8_t buffer[TPM_MAX_RSA_KEY_BYTES]; ++}; ++typedef struct TPM2B_PUBLIC_KEY_RSA TPM2B_PUBLIC_KEY_RSA; ++ ++/* TPM2B_ECC_PARAMETER Structure */ ++struct TPM2B_ECC_PARAMETER ++{ ++ grub_uint16_t size; ++ grub_uint8_t buffer[TPM_MAX_ECC_KEY_BYTES]; ++}; ++typedef struct TPM2B_ECC_PARAMETER TPM2B_ECC_PARAMETER; ++ ++/* TPMS_ECC_POINT Structure */ ++struct TPMS_ECC_POINT ++{ ++ TPM2B_ECC_PARAMETER x; ++ TPM2B_ECC_PARAMETER y; ++}; ++typedef struct TPMS_ECC_POINT TPMS_ECC_POINT; ++ ++/* TPMU_ENCRYPTED_SECRET Union */ ++union TPMU_ENCRYPTED_SECRET ++{ ++ grub_uint8_t ecc[sizeof(TPMS_ECC_POINT)]; ++ grub_uint8_t rsa[TPM_MAX_RSA_KEY_BYTES]; ++ grub_uint8_t symmetric[sizeof(TPM2B_DIGEST)]; ++ grub_uint8_t keyedHash[sizeof(TPM2B_DIGEST)]; ++}; ++typedef union TPMU_ENCRYPTED_SECRET TPMU_ENCRYPTED_SECRET; ++ ++/* TPM2B_ENCRYPTED_SECRET Structure */ ++struct TPM2B_ENCRYPTED_SECRET ++{ ++ grub_uint16_t size; ++ grub_uint8_t secret[sizeof(TPMU_ENCRYPTED_SECRET)]; ++}; ++typedef struct TPM2B_ENCRYPTED_SECRET TPM2B_ENCRYPTED_SECRET; ++ ++/* TPMU_PUBLIC_ID Union */ ++union TPMU_PUBLIC_ID ++{ ++ TPM2B_DIGEST keyedHash; ++ TPM2B_DIGEST sym; ++ TPM2B_PUBLIC_KEY_RSA rsa; ++ TPMS_ECC_POINT ecc; ++}; ++typedef union TPMU_PUBLIC_ID TPMU_PUBLIC_ID; ++ ++/* TPMT_PUBLIC Structure */ ++struct TPMT_PUBLIC ++{ ++ TPMI_ALG_PUBLIC type; ++ TPMI_ALG_HASH nameAlg; ++ TPMA_OBJECT objectAttributes; ++ TPM2B_DIGEST authPolicy; ++ TPMU_PUBLIC_PARMS parameters; ++ TPMU_PUBLIC_ID unique; ++}; ++typedef struct TPMT_PUBLIC TPMT_PUBLIC; ++ ++/* TPM2B_PUBLIC Structure */ ++struct TPM2B_PUBLIC ++{ ++ grub_uint16_t size; ++ TPMT_PUBLIC publicArea; ++}; ++typedef struct TPM2B_PUBLIC TPM2B_PUBLIC; ++ ++/* TPMT_HA Structure */ ++struct TPMT_HA ++{ ++ TPMI_ALG_HASH hashAlg; ++ TPMU_HA digest; ++}; ++typedef struct TPMT_HA TPMT_HA; ++ ++/* TPM2B_DATA Structure */ ++struct TPM2B_DATA ++{ ++ grub_uint16_t size; ++ grub_uint8_t buffer[sizeof(TPMT_HA)]; ++}; ++typedef struct TPM2B_DATA TPM2B_DATA; ++ ++/* TPMA_LOCALITY Structure */ ++struct TPMA_LOCALITY ++{ ++ unsigned char TPM_LOC_ZERO:1; ++ unsigned char TPM_LOC_ONE:1; ++ unsigned char TPM_LOC_TWO:1; ++ unsigned char TPM_LOC_THREE:1; ++ unsigned char TPM_LOC_FOUR:1; ++ unsigned char Extended:3; ++}; ++typedef struct TPMA_LOCALITY TPMA_LOCALITY; ++ ++/* TPMU_NAME Union */ ++union TPMU_NAME ++{ ++ TPMT_HA digest; ++ TPM_HANDLE handle; ++}; ++typedef union TPMU_NAME TPMU_NAME; ++ ++/* TPM2B_NAME Structure */ ++struct TPM2B_NAME ++{ ++ grub_uint16_t size; ++ grub_uint8_t name[sizeof(TPMU_NAME)]; ++}; ++typedef struct TPM2B_NAME TPM2B_NAME; ++ ++/* TPMS_CREATION_DATA Structure */ ++struct TPMS_CREATION_DATA ++{ ++ TPML_PCR_SELECTION pcrSelect; ++ TPM2B_DIGEST pcrDigest; ++ TPMA_LOCALITY locality; ++ TPM_ALG_ID parentNameAlg; ++ TPM2B_NAME parentName; ++ TPM2B_NAME parentQualifiedName; ++ TPM2B_DATA outsideInfo; ++}; ++typedef struct TPMS_CREATION_DATA TPMS_CREATION_DATA; ++ ++/* TPM2B_CREATION_DATA Structure */ ++struct TPM2B_CREATION_DATA ++{ ++ grub_uint16_t size; ++ TPMS_CREATION_DATA creationData; ++}; ++typedef struct TPM2B_CREATION_DATA TPM2B_CREATION_DATA; ++ ++/* TPMT_SYM_DEF Structure */ ++struct TPMT_SYM_DEF ++{ ++ TPMI_ALG_SYM algorithm; ++ TPMU_SYM_KEY_BITS keyBits; ++ TPMU_SYM_MODE mode; ++}; ++typedef struct TPMT_SYM_DEF TPMT_SYM_DEF; ++ ++/* TPM2B_MAX_BUFFER Structure */ ++struct TPM2B_MAX_BUFFER ++{ ++ grub_uint16_t size; ++ grub_uint8_t buffer[TPM_MAX_DIGEST_BUFFER]; ++}; ++typedef struct TPM2B_MAX_BUFFER TPM2B_MAX_BUFFER; ++ ++/* TPMT_TK_HASHCHECK Structure */ ++struct TPMT_TK_HASHCHECK ++{ ++ TPM_ST tag; ++ TPMI_RH_HIERARCHY hierarchy; ++ TPM2B_DIGEST digest; ++}; ++typedef struct TPMT_TK_HASHCHECK TPMT_TK_HASHCHECK; ++ ++/* TPM2B_SYM_KEY Structure */ ++struct TPM2B_SYM_KEY ++{ ++ grub_uint16_t size; ++ grub_uint8_t buffer[TPM_MAX_SYM_KEY_BYTES]; ++}; ++typedef struct TPM2B_SYM_KEY TPM2B_SYM_KEY; ++ ++/* TPM2B_PRIVATE_KEY_RSA Structure */ ++struct TPM2B_PRIVATE_KEY_RSA ++{ ++ grub_uint16_t size; ++ grub_uint8_t buffer[TPM_MAX_RSA_KEY_BYTES/2]; ++}; ++typedef struct TPM2B_PRIVATE_KEY_RSA TPM2B_PRIVATE_KEY_RSA; ++ ++/* TPM2B_PRIVATE_VENDOR_SPECIFIC Structure */ ++struct TPM2B_PRIVATE_VENDOR_SPECIFIC ++{ ++ grub_uint16_t size; ++ grub_uint8_t buffer[TPM_PRIVATE_VENDOR_SPECIFIC_BYTES]; ++}; ++typedef struct TPM2B_PRIVATE_VENDOR_SPECIFIC TPM2B_PRIVATE_VENDOR_SPECIFIC; ++ ++/* TPM2B_PRIVATE_VENDOR_SPECIFIC Union */ ++union TPMU_SENSITIVE_COMPOSITE ++{ ++ TPM2B_PRIVATE_KEY_RSA rsa; ++ TPM2B_ECC_PARAMETER ecc; ++ TPM2B_SENSITIVE_DATA bits; ++ TPM2B_SYM_KEY sym; ++ TPM2B_PRIVATE_VENDOR_SPECIFIC any; ++}; ++typedef union TPMU_SENSITIVE_COMPOSITE TPMU_SENSITIVE_COMPOSITE; ++ ++/* TPMT_SENSITIVE Structure */ ++struct TPMT_SENSITIVE ++{ ++ TPMI_ALG_PUBLIC sensitiveType; ++ TPM2B_AUTH authValue; ++ TPM2B_DIGEST seedValue; ++ TPMU_SENSITIVE_COMPOSITE sensitive; ++}; ++typedef struct TPMT_SENSITIVE TPMT_SENSITIVE; ++ ++/* TPM2B_SENSITIVE Structure */ ++struct TPM2B_SENSITIVE ++{ ++ grub_uint16_t size; ++ TPMT_SENSITIVE sensitiveArea; ++}; ++typedef struct TPM2B_SENSITIVE TPM2B_SENSITIVE; ++ ++/* _PRIVATE Structure */ ++struct _PRIVATE ++{ ++ TPM2B_DIGEST integrityOuter; ++ TPM2B_DIGEST integrityInner; ++ TPM2B_SENSITIVE sensitive; ++}; ++typedef struct _PRIVATE _PRIVATE; ++ ++/* TPM2B_PRIVATE Structure */ ++struct TPM2B_PRIVATE ++{ ++ grub_uint16_t size; ++ grub_uint8_t buffer[sizeof(_PRIVATE)]; ++}; ++typedef struct TPM2B_PRIVATE TPM2B_PRIVATE; ++ ++/* TPML_DIGEST_VALUES Structure */ ++struct TPML_DIGEST_VALUES ++{ ++ grub_uint16_t count; ++ TPMT_HA digests[TPM_NUM_PCR_BANKS]; ++}; ++typedef struct TPML_DIGEST_VALUES TPML_DIGEST_VALUES; ++ ++/* TPM2B_MAX_NV_BUFFER Structure */ ++struct TPM2B_MAX_NV_BUFFER ++{ ++ grub_uint16_t size; ++ grub_uint8_t buffer[TPM_MAX_NV_BUFFER_SIZE]; ++}; ++typedef struct TPM2B_MAX_NV_BUFFER TPM2B_MAX_NV_BUFFER; ++ ++/* TPMS_NV_PUBLIC Structure */ ++struct TPMS_NV_PUBLIC ++{ ++ TPMI_RH_NV_INDEX nvIndex; ++ TPMI_ALG_HASH nameAlg; ++ TPMA_NV attributes; ++ TPM2B_DIGEST authPolicy; ++ grub_uint16_t dataSize; ++}; ++typedef struct TPMS_NV_PUBLIC TPMS_NV_PUBLIC; ++ ++/* TPM2B_NV_PUBLIC Structure */ ++struct TPM2B_NV_PUBLIC ++{ ++ grub_uint16_t size; ++ TPMS_NV_PUBLIC nvPublic; ++}; ++typedef struct TPM2B_NV_PUBLIC TPM2B_NV_PUBLIC; ++ ++/* TPMT_TK_CREATION Structure */ ++struct TPMT_TK_CREATION ++{ ++ TPM_ST tag; ++ TPMI_RH_HIERARCHY hierarchy; ++ TPM2B_DIGEST digest; ++}; ++typedef struct TPMT_TK_CREATION TPMT_TK_CREATION; ++ ++#endif /* ! GRUB_TPM2_INTERNAL_STRUCTS_HEADER */ +diff --git a/include/grub/tpm2/internal/types.h b/include/grub/tpm2/internal/types.h +new file mode 100644 +index 000000000..9118cad5d +--- /dev/null ++++ b/include/grub/tpm2/internal/types.h +@@ -0,0 +1,370 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_TPM2_INTERNAL_TYPES_HEADER ++#define GRUB_TPM2_INTERNAL_TYPES_HEADER 1 ++ ++#include ++ ++/* TPM2_RC Constants */ ++typedef grub_uint32_t TPM_RC; ++ ++#define TPM_RC_1 ((TPM_RC) 0x100) ++#define TPM_RC_2 ((TPM_RC) 0x200) ++#define TPM_RC_3 ((TPM_RC) 0x300) ++#define TPM_RC_4 ((TPM_RC) 0x400) ++#define TPM_RC_5 ((TPM_RC) 0x500) ++#define TPM_RC_6 ((TPM_RC) 0x600) ++#define TPM_RC_7 ((TPM_RC) 0x700) ++#define TPM_RC_8 ((TPM_RC) 0x800) ++#define TPM_RC_9 ((TPM_RC) 0x900) ++#define TPM_RC_A ((TPM_RC) 0xA00) ++#define TPM_RC_ASYMMETRIC ((TPM_RC) 0x081) ++#define TPM_RC_ATTRIBUTES ((TPM_RC) 0x082) ++#define TPM_RC_AUTH_CONTEXT ((TPM_RC) 0x145) ++#define TPM_RC_AUTH_FAIL ((TPM_RC) 0x08E) ++#define TPM_RC_AUTH_MISSING ((TPM_RC) 0x125) ++#define TPM_RC_AUTHSIZE ((TPM_RC) 0x144) ++#define TPM_RC_AUTH_TYPE ((TPM_RC) 0x124) ++#define TPM_RC_AUTH_UNAVAILABLE ((TPM_RC) 0x12F) ++#define TPM_RC_B ((TPM_RC) 0xB00) ++#define TPM_RC_BAD_AUTH ((TPM_RC) 0x0A2) ++#define TPM_RC_BAD_CONTEXT ((TPM_RC) 0x150) ++#define TPM_RC_BAD_TAG ((TPM_RC) 0x01E) ++#define TPM_RC_BINDING ((TPM_RC) 0x0A5) ++#define TPM_RC_C ((TPM_RC) 0xC00) ++#define TPM_RC_CANCELED ((TPM_RC) 0x909) ++#define TPM_RC_COMMAND_CODE ((TPM_RC) 0x143) ++#define TPM_RC_COMMAND_SIZE ((TPM_RC) 0x142) ++#define TPM_RC_CONTEXT_GAP ((TPM_RC) 0x901) ++#define TPM_RC_CPHASH ((TPM_RC) 0x151) ++#define TPM_RC_CURVE ((TPM_RC) 0x0A6) ++#define TPM_RC_D ((TPM_RC) 0xD00) ++#define TPM_RC_DISABLED ((TPM_RC) 0x120) ++#define TPM_RC_E ((TPM_RC) 0xE00) ++#define TPM_RC_ECC_POINT ((TPM_RC) 0x0A7) ++#define TPM_RC_EXCLUSIVE ((TPM_RC) 0x121) ++#define TPM_RC_EXPIRED ((TPM_RC) 0x0A3) ++#define TPM_RC_F ((TPM_RC) 0xF00) ++#define TPM_RC_FAILURE ((TPM_RC) 0x101) ++#define TPM_RC_H ((TPM_RC) 0x000) ++#define TPM_RC_HANDLE ((TPM_RC) 0x08B) ++#define TPM_RC_HASH ((TPM_RC) 0x083) ++#define TPM_RC_HIERARCHY ((TPM_RC) 0x085) ++#define TPM_RC_HMAC ((TPM_RC) 0x119) ++#define TPM_RC_INITIALIZE ((TPM_RC) 0x100) ++#define TPM_RC_INSUFFICIENT ((TPM_RC) 0x09A) ++#define TPM_RC_INTEGRITY ((TPM_RC) 0x09F) ++#define TPM_RC_KDF ((TPM_RC) 0x08C) ++#define TPM_RC_KEY ((TPM_RC) 0x09C) ++#define TPM_RC_KEY_SIZE ((TPM_RC) 0x087) ++#define TPM_RC_LOCALITY ((TPM_RC) 0x907) ++#define TPM_RC_LOCKOUT ((TPM_RC) 0x921) ++#define TPM_RC_MEMORY ((TPM_RC) 0x904) ++#define TPM_RC_MGF ((TPM_RC) 0x088) ++#define TPM_RC_MODE ((TPM_RC) 0x089) ++#define TPM_RC_NEEDS_TEST ((TPM_RC) 0x153) ++#define TPM_RC_N_MASK ((TPM_RC) 0xF00) ++#define TPM_RC_NONCE ((TPM_RC) 0x08F) ++#define TPM_RC_NO_RESULT ((TPM_RC) 0x154) ++#define TPM_RC_NOT_USED ((TPM_RC) 0x97F) ++#define TPM_RC_NV_AUTHORIZATION ((TPM_RC) 0x149) ++#define TPM_RC_NV_DEFINED ((TPM_RC) 0x14C) ++#define TPM_RC_NV_LOCKED ((TPM_RC) 0x148) ++#define TPM_RC_NV_RANGE ((TPM_RC) 0x146) ++#define TPM_RC_NV_RATE ((TPM_RC) 0x920) ++#define TPM_RC_NV_SIZE ((TPM_RC) 0x147) ++#define TPM_RC_NV_SPACE ((TPM_RC) 0x14B) ++#define TPM_RC_NV_UNAVAILABLE ((TPM_RC) 0x923) ++#define TPM_RC_NV_UNINITIALIZED ((TPM_RC) 0x14A) ++#define TPM_RC_OBJECT_HANDLES ((TPM_RC) 0x906) ++#define TPM_RC_OBJECT_MEMORY ((TPM_RC) 0x902) ++#define TPM_RC_P ((TPM_RC) 0x040) ++#define TPM_RC_PARENT ((TPM_RC) 0x152) ++#define TPM_RC_PCR ((TPM_RC) 0x127) ++#define TPM_RC_PCR_CHANGED ((TPM_RC) 0x128) ++#define TPM_RC_POLICY ((TPM_RC) 0x126) ++#define TPM_RC_POLICY_CC ((TPM_RC) 0x0A4) ++#define TPM_RC_POLICY_FAIL ((TPM_RC) 0x09D) ++#define TPM_RC_PP ((TPM_RC) 0x090) ++#define TPM_RC_PRIVATE ((TPM_RC) 0x10B) ++#define TPM_RC_RANGE ((TPM_RC) 0x08D) ++#define TPM_RC_REBOOT ((TPM_RC) 0x130) ++#define TPM_RC_REFERENCE_H0 ((TPM_RC) 0x910) ++#define TPM_RC_REFERENCE_H1 ((TPM_RC) 0x911) ++#define TPM_RC_REFERENCE_H2 ((TPM_RC) 0x912) ++#define TPM_RC_REFERENCE_H3 ((TPM_RC) 0x913) ++#define TPM_RC_REFERENCE_H4 ((TPM_RC) 0x914) ++#define TPM_RC_REFERENCE_H5 ((TPM_RC) 0x915) ++#define TPM_RC_REFERENCE_H6 ((TPM_RC) 0x916) ++#define TPM_RC_REFERENCE_S0 ((TPM_RC) 0x918) ++#define TPM_RC_REFERENCE_S1 ((TPM_RC) 0x919) ++#define TPM_RC_REFERENCE_S2 ((TPM_RC) 0x91A) ++#define TPM_RC_REFERENCE_S3 ((TPM_RC) 0x91B) ++#define TPM_RC_REFERENCE_S4 ((TPM_RC) 0x91C) ++#define TPM_RC_REFERENCE_S5 ((TPM_RC) 0x91D) ++#define TPM_RC_REFERENCE_S6 ((TPM_RC) 0x91E) ++#define TPM_RC_RESERVED_BITS ((TPM_RC) 0x0A1) ++#define TPM_RC_RETRY ((TPM_RC) 0x922) ++#define TPM_RC_S ((TPM_RC) 0x800) ++#define TPM_RC_SCHEME ((TPM_RC) 0x092) ++#define TPM_RC_SELECTOR ((TPM_RC) 0x098) ++#define TPM_RC_SENSITIVE ((TPM_RC) 0x155) ++#define TPM_RC_SEQUENCE ((TPM_RC) 0x103) ++#define TPM_RC_SESSION_HANDLES ((TPM_RC) 0x905) ++#define TPM_RC_SESSION_MEMORY ((TPM_RC) 0x903) ++#define TPM_RC_SIGNATURE ((TPM_RC) 0x09B) ++#define TPM_RC_SIZE ((TPM_RC) 0x095) ++#define TPM_RC_SUCCESS ((TPM_RC) 0x000) ++#define TPM_RC_SYMMETRIC ((TPM_RC) 0x096) ++#define TPM_RC_TAG ((TPM_RC) 0x097) ++#define TPM_RC_TESTING ((TPM_RC) 0x90A) ++#define TPM_RC_TICKET ((TPM_RC) 0x0A0) ++#define TPM_RC_TOO_MANY_CONTEXTS ((TPM_RC) 0x12E) ++#define TPM_RC_TYPE ((TPM_RC) 0x08A) ++#define TPM_RC_UNBALANCED ((TPM_RC) 0x131) ++#define TPM_RC_UPGRADE ((TPM_RC) 0x12D) ++#define TPM_RC_VALUE ((TPM_RC) 0x084) ++#define TPM_RC_YIELDED ((TPM_RC) 0x908) ++ ++/* TPMA_NV Constants */ ++typedef grub_uint32_t TPMA_NV; ++ ++#define TPMA_NV_PPWRITE ((TPMA_NV) 0x00000001) ++#define TPMA_NV_OWNERWRITE ((TPMA_NV) 0x00000002) ++#define TPMA_NV_AUTHWRITE ((TPMA_NV) 0x00000004) ++#define TPMA_NV_POLICYWRITE ((TPMA_NV) 0x00000008) ++#define TPMA_NV_TPM2_NT_MASK ((TPMA_NV) 0x000000F0) ++#define TPMA_NV_TPM2_NT_SHIFT (4) ++#define TPMA_NV_RESERVED1_MASK ((TPMA_NV) 0x00000300) ++#define TPMA_NV_POLICY_DELETE ((TPMA_NV) 0x00000400) ++#define TPMA_NV_WRITELOCKED ((TPMA_NV) 0x00000800) ++#define TPMA_NV_WRITEALL ((TPMA_NV) 0x00001000) ++#define TPMA_NV_WRITEDEFINE ((TPMA_NV) 0x00002000) ++#define TPMA_NV_WRITE_STCLEAR ((TPMA_NV) 0x00004000) ++#define TPMA_NV_GLOBALLOCK ((TPMA_NV) 0x00008000) ++#define TPMA_NV_PPREAD ((TPMA_NV) 0x00010000) ++#define TPMA_NV_OWNERREAD ((TPMA_NV) 0x00020000) ++#define TPMA_NV_AUTHREAD ((TPMA_NV) 0x00040000) ++#define TPMA_NV_POLICYREAD ((TPMA_NV) 0x00080000) ++#define TPMA_NV_RESERVED2_MASK ((TPMA_NV) 0x01F00000) ++#define TPMA_NV_NO_DA ((TPMA_NV) 0x02000000) ++#define TPMA_NV_ORDERLY ((TPMA_NV) 0x04000000) ++#define TPMA_NV_CLEAR_STCLEAR ((TPMA_NV) 0x08000000) ++#define TPMA_NV_READLOCKED ((TPMA_NV) 0x10000000) ++#define TPMA_NV_WRITTEN ((TPMA_NV) 0x20000000) ++#define TPMA_NV_PLATFORMCREATE ((TPMA_NV) 0x40000000) ++#define TPMA_NV_READ_STCLEAR ((TPMA_NV) 0x80000000) ++ ++/* TPM_ALG_ID Constants */ ++typedef grub_uint16_t TPM_ALG_ID; ++ ++#define TPM_ALG_ERROR ((TPM_ALG_ID) 0x0000) ++#define TPM_ALG_AES ((TPM_ALG_ID) 0x0006) ++#define TPM_ALG_CAMELLIA ((TPM_ALG_ID) 0x0026) ++#define TPM_ALG_CBC ((TPM_ALG_ID) 0x0042) ++#define TPM_ALG_CFB ((TPM_ALG_ID) 0x0043) ++#define TPM_ALG_ECB ((TPM_ALG_ID) 0x0044) ++#define TPM_ALG_ECC ((TPM_ALG_ID) 0x0023) ++#define TPM_ALG_HMAC ((TPM_ALG_ID) 0x0005) ++#define TPM_ALG_KDF1_SP800_108 ((TPM_ALG_ID) 0x0022) ++#define TPM_ALG_KDF1_SP800_56A ((TPM_ALG_ID) 0x0020) ++#define TPM_ALG_KDF2 ((TPM_ALG_ID) 0x0021) ++#define TPM_ALG_KEYEDHASH ((TPM_ALG_ID) 0x0008) ++#define TPM_ALG_MGF1 ((TPM_ALG_ID) 0x0007) ++#define TPM_ALG_NULL ((TPM_ALG_ID) 0x0010) ++#define TPM_ALG_RSA ((TPM_ALG_ID) 0x0001) ++#define TPM_ALG_SHA1 ((TPM_ALG_ID) 0x0004) ++#define TPM_ALG_SHA256 ((TPM_ALG_ID) 0x000B) ++#define TPM_ALG_SHA384 ((TPM_ALG_ID) 0x000C) ++#define TPM_ALG_SHA512 ((TPM_ALG_ID) 0x000D) ++#define TPM_ALG_SM3_256 ((TPM_ALG_ID) 0x0012) ++#define TPM_ALG_SM4 ((TPM_ALG_ID) 0x0013) ++#define TPM_ALG_SYMCIPHER ((TPM_ALG_ID) 0x0025) ++#define TPM_ALG_XOR ((TPM_ALG_ID) 0x000A) ++ ++/* TPM_CAP Constants */ ++typedef grub_uint32_t TPM_CAP; ++ ++#define TPM_CAP_FIRST ((TPM_CAP) 0x00000000) ++#define TPM_CAP_ALGS ((TPM_CAP) 0x00000000) ++#define TPM_CAP_HANDLES ((TPM_CAP) 0x00000001) ++#define TPM_CAP_COMMANDS ((TPM_CAP) 0x00000002) ++#define TPM_CAP_PP_COMMANDS ((TPM_CAP) 0x00000003) ++#define TPM_CAP_AUDIT_COMMANDS ((TPM_CAP) 0x00000004) ++#define TPM_CAP_PCRS ((TPM_CAP) 0x00000005) ++#define TPM_CAP_TPM_PROPERTIES ((TPM_CAP) 0x00000006) ++#define TPM_CAP_PCR_PROPERTIES ((TPM_CAP) 0x00000007) ++#define TPM_CAP_ECC_CURVES ((TPM_CAP) 0x00000008) ++#define TPM_CAP_LAST ((TPM_CAP) 0x00000008) ++#define TPM_CAP_VENDOR_PROPERTY ((TPM_CAP) 0x00000100) ++ ++/* TPM_PT Constants */ ++typedef grub_uint32_t TPM_PT; ++ ++#define TPM_PT_NONE ((TPM_PT) 0x00000000) ++#define PT_GROUP ((TPM_PT) 0x00000100) ++#define PT_FIXED ((TPM_PT) (PT_GROUP * 1)) ++#define TPM_PT_FAMILY_INDICATOR ((TPM_PT) (PT_FIXED + 0)) ++#define TPM_PT_LEVEL ((TPM_PT) (PT_FIXED + 1)) ++#define TPM_PT_REVISION ((TPM_PT) (PT_FIXED + 2)) ++#define TPM_PT_DAY_OF_YEAR ((TPM_PT) (PT_FIXED + 3)) ++#define TPM_PT_YEAR ((TPM_PT) (PT_FIXED + 4)) ++#define TPM_PT_PCR_COUNT ((TPM_PT) (PT_FIXED + 18)) ++ ++/* TPM_SE Constants */ ++typedef grub_uint8_t TPM_SE; ++ ++#define TPM_SE_HMAC ((TPM_SE) 0x00) ++#define TPM_SE_POLICY ((TPM_SE) 0x01) ++#define TPM_SE_TRIAL ((TPM_SE) 0x03) ++ ++/* TPMI_YES_NO Constants */ ++typedef grub_uint8_t TPMI_YES_NO; ++ ++#define TPM_NO ((TPMI_YES_NO)0) ++#define TPM_YES ((TPMI_YES_NO)1) ++ ++/* TPM_ST Constants */ ++typedef grub_uint16_t TPM_ST; ++typedef TPM_ST TPMI_ST_COMMAND_TAG; ++ ++#define TPM_ST_NO_SESSIONS ((TPMI_ST_COMMAND_TAG) 0x8001) ++#define TPM_ST_SESSIONS ((TPMI_ST_COMMAND_TAG) 0x8002) ++ ++/* TPM_HANDLE Types */ ++typedef grub_uint32_t TPM_HANDLE; ++ ++typedef TPM_HANDLE TPMI_RH_HIERARCHY; ++typedef TPM_HANDLE TPMI_RH_LOCKOUT; ++typedef TPM_HANDLE TPMI_SH_AUTH_SESSION; ++typedef TPM_HANDLE TPMI_DH_CONTEXT; ++typedef TPM_HANDLE TPMI_DH_OBJECT; ++typedef TPM_HANDLE TPMI_DH_ENTITY; ++typedef TPM_HANDLE TPMI_SH_POLICY; ++typedef TPM_HANDLE TPMI_DH_PCR; ++typedef TPM_HANDLE TPMI_RH_NV_AUTH; ++typedef TPM_HANDLE TPMI_RH_NV_INDEX; ++ ++/* TPM_RH Constants */ ++typedef TPM_HANDLE TPM_RH; ++ ++#define TPM_RH_FIRST ((TPM_RH) 0x40000000) ++#define TPM_RH_SRK ((TPM_RH) 0x40000000) ++#define TPM_RH_OWNER ((TPM_RH) 0x40000001) ++#define TPM_RH_REVOKE ((TPM_RH) 0x40000002) ++#define TPM_RH_TRANSPORT ((TPM_RH) 0x40000003) ++#define TPM_RH_OPERATOR ((TPM_RH) 0x40000004) ++#define TPM_RH_ADMIN ((TPM_RH) 0x40000005) ++#define TPM_RH_EK ((TPM_RH) 0x40000006) ++#define TPM_RH_NULL ((TPM_RH) 0x40000007) ++#define TPM_RH_UNASSIGNED ((TPM_RH) 0x40000008) ++#define TPM_RS_PW ((TPM_RH) 0x40000009) ++#define TPM_RH_LOCKOUT ((TPM_RH) 0x4000000A) ++#define TPM_RH_ENDORSEMENT ((TPM_RH) 0x4000000B) ++#define TPM_RH_PLATFORM ((TPM_RH) 0x4000000C) ++#define TPM_RH_PLATFORM_NV ((TPM_RH) 0x4000000D) ++#define TPM_RH_AUTH_00 ((TPM_RH) 0x40000010) ++#define TPM_RH_AUTH_FF ((TPM_RH) 0x4000010F) ++#define TPM_RH_LAST ((TPM_RH) 0x4000010F) ++ ++/* TPM_ECC_CURVE Constants */ ++typedef grub_uint16_t TPM_ECC_CURVE; ++ ++#define TPM_ECC_NONE ((TPM_ECC_CURVE) 0x0000) ++#define TPM_ECC_NIST_P192 ((TPM_ECC_CURVE) 0x0001) ++#define TPM_ECC_NIST_P224 ((TPM_ECC_CURVE) 0x0002) ++#define TPM_ECC_NIST_P256 ((TPM_ECC_CURVE) 0x0003) ++#define TPM_ECC_NIST_P384 ((TPM_ECC_CURVE) 0x0004) ++#define TPM_ECC_NIST_P521 ((TPM_ECC_CURVE) 0x0005) ++#define TPM_ECC_BN_P256 ((TPM_ECC_CURVE) 0x0010) ++#define TPM_ECC_BN_P638 ((TPM_ECC_CURVE) 0x0011) ++#define TPM_ECC_SM2_P256 ((TPM_ECC_CURVE) 0x0020) ++ ++/* TPM_CC Constants */ ++typedef grub_uint32_t TPM_CC; ++ ++#define TPM_CC_EvictControl ((TPM_CC) 0x00000120) ++#define TPM_CC_CreatePrimary ((TPM_CC) 0x00000131) ++#define TPM_CC_Create ((TPM_CC) 0x00000153) ++#define TPM_CC_FlushContext ((TPM_CC) 0x00000165) ++#define TPM_CC_ReadPublic ((TPM_CC) 0x00000173) ++#define TPM_CC_StartAuthSession ((TPM_CC) 0x00000176) ++#define TPM_CC_PolicyPCR ((TPM_CC) 0x0000017f) ++#define TPM_CC_NV_Read ((TPM_CC) 0x0000014e) ++#define TPM_CC_NV_ReadPublic ((TPM_CC) 0x00000169) ++#define TPM_CC_GetCapability ((TPM_CC) 0x0000017a) ++#define TPM_CC_PCR_Read ((TPM_CC) 0x0000017e) ++#define TPM_CC_Load ((TPM_CC) 0x00000157) ++#define TPM_CC_Unseal ((TPM_CC) 0x0000015e) ++#define TPM_CC_PolicyGetDigest ((TPM_CC) 0x00000189) ++ ++/* Hash algorithm sizes */ ++#define TPM_SHA1_DIGEST_SIZE 20 ++#define TPM_SHA256_DIGEST_SIZE 32 ++#define TPM_SM3_256_DIGEST_SIZE 32 ++#define TPM_SHA384_DIGEST_SIZE 48 ++#define TPM_SHA512_DIGEST_SIZE 64 ++ ++/* Encryption algorithm sizes */ ++#define TPM_MAX_SYM_BLOCK_SIZE 16 ++#define TPM_MAX_SYM_DATA 256 ++#define TPM_MAX_ECC_KEY_BYTES 128 ++#define TPM_MAX_SYM_KEY_BYTES 32 ++#define TPM_MAX_RSA_KEY_BYTES 512 ++ ++/* Buffer Size Constants */ ++#define TPM_MAX_PCRS 32 ++#define TPM_NUM_PCR_BANKS 16 ++#define TPM_PCR_SELECT_MAX ((TPM_MAX_PCRS + 7) / 8) ++#define TPM_MAX_DIGEST_BUFFER 1024 ++#define TPM_MAX_TPM_PROPERTIES 8 ++#define TPM_MAX_NV_BUFFER_SIZE 2048 ++#define TPM_PRIVATE_VENDOR_SPECIFIC_BYTES 1280 ++ ++/* TPM_GENERATED Constants */ ++typedef grub_uint32_t TPM_GENERATED; ++ ++#define TPM_GENERATED_VALUE ((TPM_GENERATED) 0xff544347) ++ ++/* TPM_ALG_ID Types */ ++typedef TPM_ALG_ID TPMI_ALG_PUBLIC; ++typedef TPM_ALG_ID TPMI_ALG_HASH; ++typedef TPM_ALG_ID TPMI_ALG_KEYEDHASH_SCHEME; ++typedef TPM_ALG_ID TPMI_ALG_KDF; ++typedef TPM_ALG_ID TPMI_ALG_SYM_OBJECT; ++typedef TPM_ALG_ID TPMI_ALG_SYM_MODE; ++typedef TPM_ALG_ID TPMI_ALG_RSA_DECRYPT; ++typedef TPM_ALG_ID TPMI_ALG_ECC_SCHEME; ++typedef TPM_ALG_ID TPMI_ALG_ASYM_SCHEME; ++typedef TPM_ALG_ID TPMI_ALG_RSA_SCHEME; ++typedef TPM_ALG_ID TPMI_ALG_SYM; ++ ++/* TPM_KEY_BITS Type */ ++typedef grub_uint16_t TPM_KEY_BITS; ++ ++/* TPM_ECC_CURVE Types */ ++typedef TPM_ECC_CURVE TPMI_ECC_CURVE; ++ ++/* TPMI_RH_PROVISION Type */ ++typedef TPM_HANDLE TPMI_RH_PROVISION; ++ ++/* TPMI_RH_PROVISION Type */ ++typedef TPM_HANDLE TPMI_DH_PERSISTENT; ++ ++#endif /* ! GRUB_TPM2_INTERNAL_TYPES_HEADER */ +diff --git a/include/grub/tpm2/mu.h b/include/grub/tpm2/mu.h +new file mode 100644 +index 000000000..c545976db +--- /dev/null ++++ b/include/grub/tpm2/mu.h +@@ -0,0 +1,292 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_TPM2_MU_HEADER ++#define GRUB_TPM2_MU_HEADER 1 ++ ++#include ++#include ++ ++void ++grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (grub_tpm2_buffer_t buf, ++ const TPMS_AUTH_COMMAND* authCommand); ++ ++void ++grub_tpm2_mu_TPM2B_Marshal (grub_tpm2_buffer_t buf, ++ const grub_uint16_t size, ++ const grub_uint8_t* buffer); ++ ++void ++grub_tpm2_mu_TPMU_SYM_KEY_BITS_Marshal (grub_tpm2_buffer_t buf, ++ const TPMI_ALG_SYM_OBJECT algorithm, ++ const TPMU_SYM_KEY_BITS *p); ++ ++void ++grub_tpm2_mu_TPMU_SYM_MODE_Marshal (grub_tpm2_buffer_t buf, ++ const TPMI_ALG_SYM_OBJECT algorithm, ++ const TPMU_SYM_MODE *p); ++ ++void ++grub_tpm2_mu_TPMT_SYM_DEF_Marshal (grub_tpm2_buffer_t buf, ++ const TPMT_SYM_DEF *p); ++ ++void ++grub_tpm2_mu_TPMS_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buf, ++ const TPMS_PCR_SELECTION* pcrSelection); ++ ++void ++grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buf, ++ const TPML_PCR_SELECTION* pcrSelection); ++ ++void ++grub_tpm2_mu_TPMA_OBJECT_Marshal (grub_tpm2_buffer_t buf, ++ const TPMA_OBJECT *p); ++ ++void ++grub_tpm2_mu_TPMS_SCHEME_XOR_Marshal (grub_tpm2_buffer_t buf, ++ const TPMS_SCHEME_XOR *p); ++ ++void ++grub_tpm2_mu_TPMS_SCHEME_HMAC_Marshal (grub_tpm2_buffer_t buf, ++ const TPMS_SCHEME_HMAC *p); ++ ++void ++grub_tpm2_mu_TPMU_SCHEME_KEYEDHASH_Marshal (grub_tpm2_buffer_t buf, ++ const TPMI_ALG_KEYEDHASH_SCHEME scheme, ++ const TPMU_SCHEME_KEYEDHASH *p); ++ ++void ++grub_tpm2_mu_TPMT_KEYEDHASH_SCHEME_Marshal (grub_tpm2_buffer_t buf, ++ const TPMT_KEYEDHASH_SCHEME *p); ++ ++void ++grub_tpm2_mu_TPMS_KEYEDHASH_PARMS_Marshal (grub_tpm2_buffer_t buf, ++ const TPMS_KEYEDHASH_PARMS *p); ++ ++void ++grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Marshal (grub_tpm2_buffer_t buf, ++ const TPMT_SYM_DEF_OBJECT *p); ++ ++void ++grub_tpm2_mu_TPMU_ASYM_SCHEME_Marshal (grub_tpm2_buffer_t buf, ++ const TPMI_ALG_RSA_DECRYPT scheme, ++ const TPMU_ASYM_SCHEME *p); ++ ++void ++grub_tpm2_mu_TPMT_RSA_SCHEME_Marshal (grub_tpm2_buffer_t buf, ++ const TPMT_RSA_SCHEME *p); ++ ++void ++grub_tpm2_mu_TPMS_RSA_PARMS_Marshal (grub_tpm2_buffer_t buf, ++ const TPMS_RSA_PARMS *p); ++ ++void ++grub_tpm2_mu_TPMS_SYMCIPHER_PARMS_Marshal (grub_tpm2_buffer_t buf, ++ const TPMS_SYMCIPHER_PARMS *p); ++ ++void ++grub_tpm2_mu_TPMT_ECC_SCHEME_Marshal (grub_tpm2_buffer_t buf, ++ const TPMT_ECC_SCHEME *p); ++ ++void ++grub_tpm2_mu_TPMU_KDF_SCHEME_Marshal (grub_tpm2_buffer_t buf, ++ const TPMI_ALG_KDF scheme, ++ const TPMU_KDF_SCHEME *p); ++ ++void ++grub_tpm2_mu_TPMT_KDF_SCHEME_Marshal (grub_tpm2_buffer_t buf, ++ const TPMT_KDF_SCHEME *p); ++ ++void ++grub_tpm2_mu_TPMS_ECC_PARMS_Marshal (grub_tpm2_buffer_t buf, ++ const TPMS_ECC_PARMS *p); ++ ++void ++grub_tpm2_mu_TPMU_PUBLIC_PARMS_Marshal (grub_tpm2_buffer_t buf, ++ const grub_uint32_t type, ++ const TPMU_PUBLIC_PARMS *p); ++ ++void ++grub_tpm2_mu_TPMS_ECC_POINT_Marshal (grub_tpm2_buffer_t buf, ++ const TPMS_ECC_POINT *p); ++ ++void ++grub_tpm2_mu_TPMU_PUBLIC_ID_Marshal (grub_tpm2_buffer_t buf, ++ const TPMI_ALG_PUBLIC type, ++ const TPMU_PUBLIC_ID *p); ++ ++void ++grub_tpm2_mu_TPMT_PUBLIC_Marshal (grub_tpm2_buffer_t buf, ++ const TPMT_PUBLIC *p); ++ ++void ++grub_tpm2_mu_TPM2B_PUBLIC_Marshal (grub_tpm2_buffer_t buf, ++ const TPM2B_PUBLIC *p); ++ ++void ++grub_tpm2_mu_TPMS_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buf, ++ const TPMS_SENSITIVE_CREATE *p); ++ ++void ++grub_tpm2_mu_TPM2B_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buf, ++ const TPM2B_SENSITIVE_CREATE *sensitiveCreate); ++ ++void ++grub_tpm2_mu_TPM2B_Unmarshal (grub_tpm2_buffer_t buf, ++ TPM2B* p); ++ ++void ++grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_AUTH_RESPONSE* p); ++ ++void ++grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (grub_tpm2_buffer_t buf, ++ TPM2B_DIGEST* digest); ++ ++void ++grub_tpm2_mu_TPMA_OBJECT_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMA_OBJECT *p); ++ ++void ++grub_tpm2_mu_TPMS_SCHEME_HMAC_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_SCHEME_HMAC *p); ++ ++void ++grub_tpm2_mu_TPMS_SCHEME_XOR_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_SCHEME_XOR *p); ++ ++void ++grub_tpm2_mu_TPMU_SCHEME_KEYEDHASH_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMI_ALG_KEYEDHASH_SCHEME scheme, ++ TPMU_SCHEME_KEYEDHASH *p); ++ ++void ++grub_tpm2_mu_TPMT_KEYEDHASH_SCHEME_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMT_KEYEDHASH_SCHEME *p); ++ ++void ++grub_tpm2_mu_TPMS_KEYEDHASH_PARMS_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_KEYEDHASH_PARMS *p); ++ ++void ++grub_tpm2_mu_TPMU_SYM_KEY_BITS_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMI_ALG_SYM_OBJECT algorithm, ++ TPMU_SYM_KEY_BITS *p); ++ ++void ++grub_tpm2_mu_TPMU_SYM_MODE_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMI_ALG_SYM_OBJECT algorithm, ++ TPMU_SYM_MODE *p); ++ ++void ++grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMT_SYM_DEF_OBJECT *p); ++ ++void ++grub_tpm2_mu_TPMS_SYMCIPHER_PARMS_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_SYMCIPHER_PARMS *p); ++ ++void ++grub_tpm2_mu_TPMU_ASYM_SCHEME_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMI_ALG_RSA_DECRYPT scheme, ++ TPMU_ASYM_SCHEME *p); ++ ++void ++grub_tpm2_mu_TPMT_RSA_SCHEME_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMT_RSA_SCHEME *p); ++ ++void ++grub_tpm2_mu_TPMS_RSA_PARMS_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_RSA_PARMS *p); ++ ++void ++grub_tpm2_mu_TPMT_ECC_SCHEME_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMT_ECC_SCHEME *p); ++ ++void ++grub_tpm2_mu_TPMU_KDF_SCHEME_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMI_ALG_KDF scheme, ++ TPMU_KDF_SCHEME *p); ++ ++void ++grub_tpm2_mu_TPMT_KDF_SCHEME_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMT_KDF_SCHEME *p); ++ ++void ++grub_tpm2_mu_TPMS_ECC_PARMS_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_ECC_PARMS *p); ++ ++void ++grub_tpm2_mu_TPMU_PUBLIC_PARMS_Unmarshal (grub_tpm2_buffer_t buf, ++ grub_uint32_t type, ++ TPMU_PUBLIC_PARMS *p); ++ ++void ++grub_tpm2_mu_TPMS_ECC_POINT_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_ECC_POINT *p); ++ ++void ++grub_tpm2_mu_TPMU_PUBLIC_ID_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMI_ALG_PUBLIC type, ++ TPMU_PUBLIC_ID *p); ++ ++void ++grub_tpm2_mu_TPMT_PUBLIC_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMT_PUBLIC *p); ++ ++void ++grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (grub_tpm2_buffer_t buf, ++ TPM2B_PUBLIC *p); ++ ++void ++grub_tpm2_mu_TPMS_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_NV_PUBLIC *p); ++ ++void ++grub_tpm2_mu_TPM2B_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buf, ++ TPM2B_NV_PUBLIC *p); ++ ++void ++grub_tpm2_mu_TPM2B_NAME_Unmarshal (grub_tpm2_buffer_t buf, ++ TPM2B_NAME *n); ++ ++void ++grub_tpm2_mu_TPMS_TAGGED_PROPERTY_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_TAGGED_PROPERTY* property); ++ ++void ++grub_tpm2_mu_TPMS_CAPABILITY_DATA_tpmProperties_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_CAPABILITY_DATA* capabilityData); ++ ++void ++grub_tpm2_mu_TPMT_TK_CREATION_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMT_TK_CREATION *p); ++ ++void ++grub_tpm2_mu_TPMS_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_PCR_SELECTION* pcrSelection); ++ ++void ++grub_tpm2_mu_TPML_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buf, ++ TPML_PCR_SELECTION* pcrSelection); ++ ++void ++grub_tpm2_mu_TPML_DIGEST_Unmarshal (grub_tpm2_buffer_t buf, ++ TPML_DIGEST* digest); ++ ++#endif /* ! GRUB_TPM2_MU_HEADER */ +diff --git a/include/grub/tpm2/tcg2.h b/include/grub/tpm2/tcg2.h +new file mode 100644 +index 000000000..553b3fd93 +--- /dev/null ++++ b/include/grub/tpm2/tcg2.h +@@ -0,0 +1,34 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_TPM2_TCG2_HEADER ++#define GRUB_TPM2_TCG2_HEADER 1 ++ ++#include ++#include ++ ++grub_err_t ++grub_tcg2_get_max_output_size (grub_size_t *size); ++ ++grub_err_t ++grub_tcg2_submit_command (grub_size_t input_size, ++ grub_uint8_t *input, ++ grub_size_t output_size, ++ grub_uint8_t *output); ++ ++#endif /* ! GRUB_TPM2_TCG2_HEADER */ +diff --git a/include/grub/tpm2/tpm2.h b/include/grub/tpm2/tpm2.h +new file mode 100644 +index 000000000..cfdc9edcd +--- /dev/null ++++ b/include/grub/tpm2/tpm2.h +@@ -0,0 +1,34 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_TPM2_TPM2_HEADER ++#define GRUB_TPM2_TPM2_HEADER 1 ++ ++#include ++#include ++#include ++ ++/* Well-Known Windows SRK handle */ ++#define TPM2_SRK_HANDLE 0x81000001 ++ ++typedef struct TPM2_SEALED_KEY { ++ TPM2B_PUBLIC public; ++ TPM2B_PRIVATE private; ++} TPM2_SEALED_KEY; ++ ++#endif /* ! GRUB_TPM2_TPM2_HEADER */ +-- +2.35.3 + diff --git a/0002-tpm2-Add-more-marshal-unmarshal-functions.patch b/0002-tpm2-Add-more-marshal-unmarshal-functions.patch new file mode 100644 index 0000000..ba87585 --- /dev/null +++ b/0002-tpm2-Add-more-marshal-unmarshal-functions.patch @@ -0,0 +1,427 @@ +From 1d34522075949581ccb34a08dd73607566517824 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Tue, 7 Feb 2023 18:33:42 +0800 +Subject: [PATCH 2/4] tpm2: Add more marshal/unmarshal functions + +Add a few more marshal/unmarshal functions to support authorized policy. + +* Marshal: + grub_tpm2_mu_TPMU_SENSITIVE_COMPOSITE_Marshal() + grub_tpm2_mu_TPMT_SENSITIVE_Marshal() + grub_tpm2_mu_TPM2B_SENSITIVE_Marshal() + grub_tpm2_mu_TPMS_SIGNATURE_RSA_Marshal() + grub_tpm2_mu_TPMS_SIGNATURE_ECC_Marshal() + grub_tpm2_mu_TPMU_HA_Marshal() + grub_tpm2_mu_TPMT_HA_Marshal() + grub_tpm2_mu_TPMU_SIGNATURE_Marshal() + grub_tpm2_mu_TPMT_SIGNATURE_Marshal() + grub_tpm2_mu_TPMT_TK_VERIFIED_Marshal() + +* Unmarshal: + grub_tpm2_mu_TPMT_TK_HASHCHECK_Unmarshal() + grub_tpm2_mu_TPMT_TK_VERIFIED_Unmarshal() + grub_tpm2_mu_TPMS_SIGNATURE_RSA_Unmarshal() + grub_tpm2_mu_TPMS_SIGNATURE_ECC_Unmarshal() + grub_tpm2_mu_TPMU_HA_Unmarshal() + grub_tpm2_mu_TPMT_HA_Unmarshal() + grub_tpm2_mu_TPMU_SIGNATURE_Unmarshal() + grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal() + +Signed-off-by: Gary Lin +--- + grub-core/tpm2/mu.c | 262 +++++++++++++++++++++++++++++++++++++++++ + include/grub/tpm2/mu.h | 75 ++++++++++++ + 2 files changed, 337 insertions(+) + +diff --git a/grub-core/tpm2/mu.c b/grub-core/tpm2/mu.c +index 1617f37cd..3a9a3c1be 100644 +--- a/grub-core/tpm2/mu.c ++++ b/grub-core/tpm2/mu.c +@@ -383,6 +383,49 @@ grub_tpm2_mu_TPMS_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buffer, + grub_tpm2_mu_TPM2B_Marshal (buffer, p->data.size, p->data.buffer); + } + ++void ++grub_tpm2_mu_TPMU_SENSITIVE_COMPOSITE_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMI_ALG_PUBLIC type, ++ const TPMU_SENSITIVE_COMPOSITE *p) ++{ ++ switch(type) ++ { ++ case TPM_ALG_RSA: ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->rsa.size, p->rsa.buffer); ++ break; ++ case TPM_ALG_ECC: ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->ecc.size, p->ecc.buffer); ++ break; ++ case TPM_ALG_KEYEDHASH: ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->bits.size, p->bits.buffer); ++ break; ++ case TPM_ALG_SYMCIPHER: ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->sym.size, p->sym.buffer); ++ break; ++ default: ++ buffer->error = 1; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_SENSITIVE_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMT_SENSITIVE *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->sensitiveType); ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->authValue.size, p->authValue.buffer); ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->seedValue.size, p->seedValue.buffer); ++ grub_tpm2_mu_TPMU_SENSITIVE_COMPOSITE_Marshal (buffer, p->sensitiveType, ++ &p->sensitive); ++} ++ ++void ++grub_tpm2_mu_TPM2B_SENSITIVE_Marshal (grub_tpm2_buffer_t buffer, ++ const TPM2B_SENSITIVE *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->size); ++ grub_tpm2_mu_TPMT_SENSITIVE_Marshal (buffer, &p->sensitiveArea); ++} ++ + void + grub_tpm2_mu_TPM2B_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buffer, + const TPM2B_SENSITIVE_CREATE *sensitiveCreate) +@@ -405,6 +448,113 @@ grub_tpm2_mu_TPM2B_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buffer, + grub_tpm2_buffer_pack_u16 (buffer, 0); + } + ++void ++grub_tpm2_mu_TPMS_SIGNATURE_RSA_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMS_SIGNATURE_RSA *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->hash); ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->sig.size, p->sig.buffer); ++} ++ ++void ++grub_tpm2_mu_TPMS_SIGNATURE_ECC_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMS_SIGNATURE_ECC *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->hash); ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->signatureR.size, p->signatureR.buffer); ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->signatureS.size, p->signatureS.buffer); ++} ++ ++void ++grub_tpm2_mu_TPMU_HA_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMI_ALG_HASH hashAlg, ++ const TPMU_HA *p) ++{ ++ switch (hashAlg) ++ { ++ case TPM_ALG_SHA1: ++ for (grub_uint16_t i = 0; i < TPM_SHA1_DIGEST_SIZE; i++) ++ grub_tpm2_buffer_pack_u8 (buffer, p->sha1[i]); ++ break; ++ case TPM_ALG_SHA256: ++ for (grub_uint16_t i = 0; i < TPM_SHA256_DIGEST_SIZE; i++) ++ grub_tpm2_buffer_pack_u8 (buffer, p->sha256[i]); ++ break; ++ case TPM_ALG_SHA384: ++ for (grub_uint16_t i = 0; i < TPM_SHA384_DIGEST_SIZE; i++) ++ grub_tpm2_buffer_pack_u8 (buffer, p->sha384[i]); ++ break; ++ case TPM_ALG_SHA512: ++ for (grub_uint16_t i = 0; i < TPM_SHA512_DIGEST_SIZE; i++) ++ grub_tpm2_buffer_pack_u8 (buffer, p->sha512[i]); ++ break; ++ default: ++ buffer->error = 1; ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_HA_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMT_HA *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->hashAlg); ++ grub_tpm2_mu_TPMU_HA_Marshal (buffer, p->hashAlg, &p->digest); ++} ++ ++void ++grub_tpm2_mu_TPMU_SIGNATURE_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMI_ALG_SIG_SCHEME sigAlg, ++ const TPMU_SIGNATURE *p) ++{ ++ switch (sigAlg) ++ { ++ case TPM_ALG_RSASSA: ++ grub_tpm2_mu_TPMS_SIGNATURE_RSA_Marshal (buffer, (TPMS_SIGNATURE_RSA *)&p->rsassa); ++ break; ++ case TPM_ALG_RSAPSS: ++ grub_tpm2_mu_TPMS_SIGNATURE_RSA_Marshal (buffer, (TPMS_SIGNATURE_RSA *)&p->rsapss); ++ break; ++ case TPM_ALG_ECDSA: ++ grub_tpm2_mu_TPMS_SIGNATURE_ECC_Marshal (buffer, (TPMS_SIGNATURE_ECC *)&p->ecdsa); ++ break; ++ case TPM_ALG_ECDAA: ++ grub_tpm2_mu_TPMS_SIGNATURE_ECC_Marshal (buffer, (TPMS_SIGNATURE_ECC *)&p->ecdaa); ++ break; ++ case TPM_ALG_SM2: ++ grub_tpm2_mu_TPMS_SIGNATURE_ECC_Marshal (buffer, (TPMS_SIGNATURE_ECC *)&p->sm2); ++ break; ++ case TPM_ALG_ECSCHNORR: ++ grub_tpm2_mu_TPMS_SIGNATURE_ECC_Marshal (buffer, (TPMS_SIGNATURE_ECC *)&p->ecschnorr); ++ break; ++ case TPM_ALG_HMAC: ++ grub_tpm2_mu_TPMT_HA_Marshal (buffer, &p->hmac); ++ break; ++ case TPM_ALG_NULL: ++ break; ++ default: ++ buffer->error = 1; ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_SIGNATURE_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMT_SIGNATURE *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->sigAlg); ++ grub_tpm2_mu_TPMU_SIGNATURE_Marshal (buffer, p->sigAlg, &p->signature); ++} ++ ++void ++grub_tpm2_mu_TPMT_TK_VERIFIED_Marshal (grub_tpm2_buffer_t buffer, ++ const TPMT_TK_VERIFIED *p) ++{ ++ grub_tpm2_buffer_pack_u16 (buffer, p->tag); ++ grub_tpm2_buffer_pack_u32 (buffer, p->hierarchy); ++ grub_tpm2_mu_TPM2B_Marshal (buffer, p->digest.size, p->digest.buffer); ++} ++ + void + grub_tpm2_mu_TPM2B_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B* p) +@@ -775,6 +925,24 @@ grub_tpm2_mu_TPMT_TK_CREATION_Unmarshal (grub_tpm2_buffer_t buffer, + grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->digest); + } + ++void ++grub_tpm2_mu_TPMT_TK_HASHCHECK_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMT_TK_HASHCHECK *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->tag); ++ grub_tpm2_buffer_unpack_u32 (buffer, &p->hierarchy); ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->digest); ++} ++ ++void ++grub_tpm2_mu_TPMT_TK_VERIFIED_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMT_TK_VERIFIED *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->tag); ++ grub_tpm2_buffer_unpack_u32 (buffer, &p->hierarchy); ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->digest); ++} ++ + void + grub_tpm2_mu_TPMS_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buf, + TPMS_PCR_SELECTION* pcrSelection) +@@ -805,3 +973,97 @@ grub_tpm2_mu_TPML_DIGEST_Unmarshal (grub_tpm2_buffer_t buf, + for (grub_uint32_t i = 0; i < digest->count; i++) + grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (buf, &digest->digests[i]); + } ++ ++void ++grub_tpm2_mu_TPMS_SIGNATURE_RSA_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_SIGNATURE_RSA *rsa) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &rsa->hash); ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*)&rsa->sig); ++} ++ ++void ++grub_tpm2_mu_TPMS_SIGNATURE_ECC_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMS_SIGNATURE_ECC *ecc) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &ecc->hash); ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*)&ecc->signatureR); ++ grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*)&ecc->signatureS); ++} ++ ++void ++grub_tpm2_mu_TPMU_HA_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMI_ALG_HASH hashAlg, ++ TPMU_HA *p) ++{ ++ switch (hashAlg) ++ { ++ case TPM_ALG_SHA1: ++ grub_tpm2_buffer_unpack (buffer, &p->sha1, TPM_SHA1_DIGEST_SIZE); ++ break; ++ case TPM_ALG_SHA256: ++ grub_tpm2_buffer_unpack (buffer, &p->sha256, TPM_SHA256_DIGEST_SIZE); ++ break; ++ case TPM_ALG_SHA384: ++ grub_tpm2_buffer_unpack (buffer, &p->sha384, TPM_SHA384_DIGEST_SIZE); ++ break; ++ case TPM_ALG_SHA512: ++ grub_tpm2_buffer_unpack (buffer, &p->sha512, TPM_SHA512_DIGEST_SIZE); ++ break; ++ default: ++ buffer->error = 1; ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_HA_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMT_HA *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->hashAlg); ++ grub_tpm2_mu_TPMU_HA_Unmarshal (buffer, p->hashAlg, &p->digest); ++} ++ ++void ++grub_tpm2_mu_TPMU_SIGNATURE_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMI_ALG_SIG_SCHEME sigAlg, ++ TPMU_SIGNATURE *p) ++{ ++ switch (sigAlg) ++ { ++ case TPM_ALG_RSASSA: ++ grub_tpm2_mu_TPMS_SIGNATURE_RSA_Unmarshal (buffer, (TPMS_SIGNATURE_RSA *)&p->rsassa); ++ break; ++ case TPM_ALG_RSAPSS: ++ grub_tpm2_mu_TPMS_SIGNATURE_RSA_Unmarshal (buffer, (TPMS_SIGNATURE_RSA *)&p->rsapss); ++ break; ++ case TPM_ALG_ECDSA: ++ grub_tpm2_mu_TPMS_SIGNATURE_ECC_Unmarshal (buffer, (TPMS_SIGNATURE_ECC *)&p->ecdsa); ++ break; ++ case TPM_ALG_ECDAA: ++ grub_tpm2_mu_TPMS_SIGNATURE_ECC_Unmarshal (buffer, (TPMS_SIGNATURE_ECC *)&p->ecdaa); ++ break; ++ case TPM_ALG_SM2: ++ grub_tpm2_mu_TPMS_SIGNATURE_ECC_Unmarshal (buffer, (TPMS_SIGNATURE_ECC *)&p->sm2); ++ break; ++ case TPM_ALG_ECSCHNORR: ++ grub_tpm2_mu_TPMS_SIGNATURE_ECC_Unmarshal (buffer, (TPMS_SIGNATURE_ECC *)&p->ecschnorr); ++ break; ++ case TPM_ALG_HMAC: ++ grub_tpm2_mu_TPMT_HA_Unmarshal (buffer, &p->hmac); ++ break; ++ case TPM_ALG_NULL: ++ break; ++ default: ++ buffer->error = 1; ++ break; ++ } ++} ++ ++void ++grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal (grub_tpm2_buffer_t buffer, ++ TPMT_SIGNATURE *p) ++{ ++ grub_tpm2_buffer_unpack_u16 (buffer, &p->sigAlg); ++ grub_tpm2_mu_TPMU_SIGNATURE_Unmarshal (buffer, p->sigAlg, &p->signature); ++} +diff --git a/include/grub/tpm2/mu.h b/include/grub/tpm2/mu.h +index c545976db..afb842ab5 100644 +--- a/include/grub/tpm2/mu.h ++++ b/include/grub/tpm2/mu.h +@@ -147,6 +147,47 @@ grub_tpm2_mu_TPM2B_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buf, + const TPM2B_SENSITIVE_CREATE *sensitiveCreate); + + void ++grub_tpm2_mu_TPMU_SENSITIVE_COMPOSITE_Marshal (grub_tpm2_buffer_t buf, ++ const TPMI_ALG_PUBLIC type, ++ const TPMU_SENSITIVE_COMPOSITE *p); ++void ++grub_tpm2_mu_TPMT_SENSITIVE_Marshal (grub_tpm2_buffer_t buf, ++ const TPMT_SENSITIVE *p); ++ ++void ++grub_tpm2_mu_TPM2B_SENSITIVE_Marshal (grub_tpm2_buffer_t buf, ++ const TPM2B_SENSITIVE *p); ++ ++void ++grub_tpm2_mu_TPMS_SIGNATURE_RSA_Marshal (grub_tpm2_buffer_t buf, ++ const TPMS_SIGNATURE_RSA *p); ++ ++void ++grub_tpm2_mu_TPMS_SIGNATURE_ECC_Marshal (grub_tpm2_buffer_t buf, ++ const TPMS_SIGNATURE_ECC *p); ++ ++void ++grub_tpm2_mu_TPMU_HA_Marshal (grub_tpm2_buffer_t buf, ++ const TPMI_ALG_HASH hashAlg, ++ const TPMU_HA *p); ++ ++void ++grub_tpm2_mu_TPMT_HA_Marshal (grub_tpm2_buffer_t buf, ++ const TPMT_HA *p); ++ ++void ++grub_tpm2_mu_TPMU_SIGNATURE_Marshal (grub_tpm2_buffer_t buf, ++ const TPMI_ALG_SIG_SCHEME sigAlg, ++ const TPMU_SIGNATURE *p); ++ ++void ++grub_tpm2_mu_TPMT_SIGNATURE_Marshal (grub_tpm2_buffer_t buf, ++ const TPMT_SIGNATURE *p); ++ ++void ++grub_tpm2_mu_TPMT_TK_VERIFIED_Marshal (grub_tpm2_buffer_t buf, ++ const TPMT_TK_VERIFIED *p); ++void + grub_tpm2_mu_TPM2B_Unmarshal (grub_tpm2_buffer_t buf, + TPM2B* p); + +@@ -277,6 +318,14 @@ void + grub_tpm2_mu_TPMT_TK_CREATION_Unmarshal (grub_tpm2_buffer_t buf, + TPMT_TK_CREATION *p); + ++void ++grub_tpm2_mu_TPMT_TK_HASHCHECK_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMT_TK_HASHCHECK *p); ++ ++void ++grub_tpm2_mu_TPMT_TK_VERIFIED_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMT_TK_VERIFIED *p); ++ + void + grub_tpm2_mu_TPMS_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buf, + TPMS_PCR_SELECTION* pcrSelection); +@@ -289,4 +338,30 @@ void + grub_tpm2_mu_TPML_DIGEST_Unmarshal (grub_tpm2_buffer_t buf, + TPML_DIGEST* digest); + ++void ++grub_tpm2_mu_TPMS_SIGNATURE_RSA_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_SIGNATURE_RSA *p); ++ ++void ++grub_tpm2_mu_TPMS_SIGNATURE_ECC_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMS_SIGNATURE_ECC *p); ++ ++void ++grub_tpm2_mu_TPMU_HA_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMI_ALG_HASH hashAlg, ++ TPMU_HA *p); ++ ++void ++grub_tpm2_mu_TPMT_HA_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMT_HA *p); ++ ++void ++grub_tpm2_mu_TPMU_SIGNATURE_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMI_ALG_SIG_SCHEME sigAlg, ++ TPMU_SIGNATURE *p); ++ ++void ++grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal (grub_tpm2_buffer_t buf, ++ TPMT_SIGNATURE *p); ++ + #endif /* ! GRUB_TPM2_MU_HEADER */ +-- +2.35.3 + diff --git a/0003-Handle-multi-arch-64-on-32-boot-in-linuxefi-loader.patch b/0003-Handle-multi-arch-64-on-32-boot-in-linuxefi-loader.patch new file mode 100644 index 0000000..26912f2 --- /dev/null +++ b/0003-Handle-multi-arch-64-on-32-boot-in-linuxefi-loader.patch @@ -0,0 +1,279 @@ +From 25069a23257ba9c6db644bbe6114dafb879063e5 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Mon, 8 Jul 2019 12:32:37 +0200 +Subject: [PATCH 03/11] Handle multi-arch (64-on-32) boot in linuxefi loader. + +Allow booting 64-bit kernels on 32-bit EFI on x86. + +Signed-off-by: Peter Jones +--- + grub-core/loader/efi/linux.c | 11 ++- + grub-core/loader/i386/efi/linux.c | 127 +++++++++++++++++++----------- + include/grub/i386/linux.h | 7 +- + 3 files changed, 97 insertions(+), 48 deletions(-) + +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -44,14 +44,10 @@ + static grub_err_t + grub_linuxefi_boot (void) + { +- int offset = 0; +- +-#ifdef __x86_64__ +- offset = 512; +-#endif + asm volatile ("cli"); + +- return grub_efi_linux_boot ((char *)kernel_mem, handover_offset + offset, ++ return grub_efi_linux_boot ((char *)kernel_mem, ++ handover_offset, + params); + } + +@@ -147,14 +143,20 @@ + return grub_errno; + } + ++#define MIN(a, b) \ ++ ({ typeof (a) _a = (a); \ ++ typeof (b) _b = (b); \ ++ _a < _b ? _a : _b; }) ++ + static grub_err_t + grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) + { + grub_file_t file = 0; +- struct linux_i386_kernel_header lh; +- grub_ssize_t len, start, filelen; ++ struct linux_i386_kernel_header *lh = NULL; ++ grub_ssize_t start, filelen; + void *kernel = NULL; ++ int setup_header_end_offset; + grub_err_t err; + + grub_dl_ref (my_mod); +@@ -185,45 +187,79 @@ + goto fail; + } + +- params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384)); +- ++ params = grub_efi_allocate_pages_max (0x3fffffff, ++ BYTES_TO_PAGES(sizeof(*params))); + if (! params) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); + goto fail; + } + +- grub_memset (params, 0, 16384); ++ grub_dprintf ("linux", "params = %p\n", params); + +- grub_memcpy (&lh, kernel, sizeof (lh)); ++ grub_memset (params, 0, sizeof(*params)); + +- if (lh.boot_flag != grub_cpu_to_le16 (0xaa55)) ++ setup_header_end_offset = *((grub_uint8_t *)kernel + 0x201); ++ grub_dprintf ("linux", "copying %lu bytes from %p to %p\n", ++ MIN((grub_size_t)0x202+setup_header_end_offset, ++ sizeof (*params)) - 0x1f1, ++ (grub_uint8_t *)kernel + 0x1f1, ++ (grub_uint8_t *)params + 0x1f1); ++ grub_memcpy ((grub_uint8_t *)params + 0x1f1, ++ (grub_uint8_t *)kernel + 0x1f1, ++ MIN((grub_size_t)0x202+setup_header_end_offset,sizeof (*params)) - 0x1f1); ++ lh = (struct linux_i386_kernel_header *)params; ++ grub_dprintf ("linux", "lh is at %p\n", lh); ++ grub_dprintf ("linux", "checking lh->boot_flag\n"); ++ if (lh->boot_flag != grub_cpu_to_le16 (0xaa55)) + { + grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number")); + goto fail; + } + +- if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) ++ grub_dprintf ("linux", "checking lh->setup_sects\n"); ++ if (lh->setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) + { + grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors")); + goto fail; + } + +- if (lh.version < grub_cpu_to_le16 (0x020b)) ++ grub_dprintf ("linux", "checking lh->version\n"); ++ if (lh->version < grub_cpu_to_le16 (0x020b)) + { + grub_error (GRUB_ERR_BAD_OS, N_("kernel too old")); + goto fail; + } + +- if (!lh.handover_offset) ++ grub_dprintf ("linux", "checking lh->handover_offset\n"); ++ if (!lh->handover_offset) + { + grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover")); + goto fail; + } + +- linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff, +- BYTES_TO_PAGES(lh.cmdline_size + 1)); ++#if defined(__x86_64__) || defined(__aarch64__) ++ grub_dprintf ("linux", "checking lh->xloadflags\n"); ++ if (!(lh->xloadflags & LINUX_XLF_KERNEL_64)) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support 64-bit CPUs")); ++ goto fail; ++ } ++#endif + ++#if defined(__i386__) ++ if ((lh->xloadflags & LINUX_XLF_KERNEL_64) && ++ !(lh->xloadflags & LINUX_XLF_EFI_HANDOVER_32)) ++ { ++ grub_error (GRUB_ERR_BAD_OS, ++ N_("kernel doesn't support 32-bit handover")); ++ goto fail; ++ } ++#endif ++ ++ grub_dprintf ("linux", "setting up cmdline\n"); ++ linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff, ++ BYTES_TO_PAGES(lh->cmdline_size + 1)); + if (!linux_cmdline) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline")); +@@ -233,27 +269,26 @@ + grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + err = grub_create_loader_cmdline (argc, argv, + linux_cmdline + sizeof (LINUX_IMAGE) - 1, +- lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1), ++ lh->cmdline_size - (sizeof (LINUX_IMAGE) - 1), + GRUB_VERIFY_KERNEL_CMDLINE); + if (err) + goto fail; + +- lh.cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline; ++ grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline); ++ grub_dprintf ("linux", "setting lh->cmd_line_ptr\n"); ++ lh->cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline; + +- handover_offset = lh.handover_offset; ++ grub_dprintf ("linux", "computing handover offset\n"); ++ handover_offset = lh->handover_offset; + +- start = (lh.setup_sects + 1) * 512; +- len = grub_file_size(file) - start; ++ start = (lh->setup_sects + 1) * 512; + +- kernel_mem = grub_efi_allocate_fixed (lh.pref_address, +- BYTES_TO_PAGES(lh.init_size)); ++ kernel_mem = grub_efi_allocate_pages_max(lh->pref_address, ++ BYTES_TO_PAGES(lh->init_size)); + + if (!kernel_mem) +- { +- grub_errno = GRUB_ERR_NONE; +- kernel_mem = grub_efi_allocate_pages_max(0x3fffffff, +- BYTES_TO_PAGES(lh.init_size)); +- } ++ kernel_mem = grub_efi_allocate_pages_max(0x3fffffff, ++ BYTES_TO_PAGES(lh->init_size)); + + if (!kernel_mem) + { +@@ -261,21 +296,23 @@ + goto fail; + } + +- grub_memcpy (kernel_mem, (char *)kernel + start, len); ++ grub_dprintf ("linux", "kernel_mem = %lx\n", (unsigned long) kernel_mem); ++ + grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); + loaded=1; ++ grub_dprintf ("linux", "setting lh->code32_start to %p\n", kernel_mem); ++ lh->code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem; + +- lh.code32_start = (grub_uint32_t)(grub_uint64_t) kernel_mem; +- /* Grub linuxefi erroneously initialize linux's boot_params with non-zero values. (bsc#1025563) ++ grub_memcpy (kernel_mem, (char *)kernel + start, filelen - start); + +- From https://www.kernel.org/doc/Documentation/x86/boot.txt: +- The memory for struct boot_params could be allocated anywhere (even above 4G) +- and initialized to all zero. +- Then, the setup header at offset 0x01f1 of kernel image on should be +- loaded into struct boot_params and examined. */ +- grub_memcpy (¶ms->setup_sects, &lh.setup_sects, sizeof (lh) - 0x01f1); ++ grub_dprintf ("linux", "setting lh->type_of_loader\n"); ++ lh->type_of_loader = 0x6; + +- params->type_of_loader = 0x21; ++ grub_dprintf ("linux", "setting lh->ext_loader_{type,ver}\n"); ++ params->ext_loader_type = 0; ++ params->ext_loader_ver = 2; ++ grub_dprintf("linux", "kernel_mem: %p handover_offset: %08x\n", ++ kernel_mem, handover_offset); + + fail: + +@@ -291,8 +328,10 @@ + loaded = 0; + } + +- if (linux_cmdline && !loaded) +- grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)linux_cmdline, BYTES_TO_PAGES(lh.cmdline_size + 1)); ++ if (linux_cmdline && lh && !loaded) ++ grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t) ++ linux_cmdline, ++ BYTES_TO_PAGES(lh->cmdline_size + 1)); + + if (kernel_mem && !loaded) + grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); +--- a/include/grub/i386/linux.h ++++ b/include/grub/i386/linux.h +@@ -138,7 +138,12 @@ + grub_uint32_t kernel_alignment; + grub_uint8_t relocatable; + grub_uint8_t min_alignment; +- grub_uint8_t pad[2]; ++#define LINUX_XLF_KERNEL_64 (1<<0) ++#define LINUX_XLF_CAN_BE_LOADED_ABOVE_4G (1<<1) ++#define LINUX_XLF_EFI_HANDOVER_32 (1<<2) ++#define LINUX_XLF_EFI_HANDOVER_64 (1<<3) ++#define LINUX_XLF_EFI_KEXEC (1<<4) ++ grub_uint16_t xloadflags; + grub_uint32_t cmdline_size; + grub_uint32_t hardware_subarch; + grub_uint64_t hardware_subarch_data; +--- a/grub-core/loader/efi/linux_boot.c ++++ b/grub-core/loader/efi/linux_boot.c +@@ -30,11 +30,16 @@ + typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *); + + grub_err_t +-grub_efi_linux_boot (void *kernel_addr, grub_off_t offset, ++grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset, + void *kernel_params) + { + grub_efi_loaded_image_t *loaded_image = NULL; + handover_func hf; ++ int offset = 0; ++ ++#ifdef __x86_64__ ++ offset = 512; ++#endif + + /* + * Since the EFI loader is not calling the LoadImage() and StartImage() +@@ -48,8 +53,8 @@ + grub_dprintf ("linux", "Loaded Image base address could not be set\n"); + + grub_dprintf ("linux", "kernel_addr: %p handover_offset: %p params: %p\n", +- kernel_addr, (void *)(grub_efi_uintn_t)offset, kernel_params); +- hf = (handover_func)((char *)kernel_addr + offset); ++ kernel_addr, (void *)(grub_efi_uintn_t)handover_offset, kernel_params); ++ hf = (handover_func)((char *)kernel_addr + handover_offset + offset); + hf (grub_efi_image_handle, grub_efi_system_table, kernel_params); + + return GRUB_ERR_BUG; diff --git a/0003-Make-grub_error-more-verbose.patch b/0003-Make-grub_error-more-verbose.patch new file mode 100644 index 0000000..4a54152 --- /dev/null +++ b/0003-Make-grub_error-more-verbose.patch @@ -0,0 +1,57 @@ +From 3526c4e467ee01a3cfd2f4d627433d078a1ab780 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Mon, 27 Aug 2018 13:14:06 -0400 +Subject: [PATCH 3/9] Make grub_error() more verbose + +Signed-off-by: Peter Jones +--- + grub-core/kern/efi/mm.c | 17 ++++++++++++++--- + grub-core/kern/err.c | 13 +++++++++++-- + include/grub/err.h | 5 ++++- + 3 files changed, 29 insertions(+), 6 deletions(-) + +--- a/grub-core/kern/err.c ++++ b/grub-core/kern/err.c +@@ -33,15 +33,24 @@ + static int grub_error_stack_pos; + static int grub_error_stack_assert; + ++#ifdef grub_error ++#undef grub_error ++#endif ++ + grub_err_t +-grub_error (grub_err_t n, const char *fmt, ...) ++grub_error (grub_err_t n, const char *file, const int line, const char *fmt, ...) + { + va_list ap; ++ int m; + + grub_errno = n; + ++ m = grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "%s:%d:", file, line); ++ if (m < 0) ++ m = 0; ++ + va_start (ap, fmt); +- grub_vsnprintf (grub_errmsg, sizeof (grub_errmsg), _(fmt), ap); ++ grub_vsnprintf (grub_errmsg + m, sizeof (grub_errmsg) - m, _(fmt), ap); + va_end (ap); + + return n; +--- a/include/grub/err.h ++++ b/include/grub/err.h +@@ -86,8 +86,11 @@ + extern grub_err_t EXPORT_VAR(grub_errno); + extern char EXPORT_VAR(grub_errmsg)[GRUB_MAX_ERRMSG]; + +-grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *fmt, ...) +- __attribute__ ((format (GNU_PRINTF, 2, 3))); ++grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *file, const int line, const char *fmt, ...) ++ __attribute__ ((format (GNU_PRINTF, 4, 5))); ++ ++#define grub_error(n, fmt, ...) grub_error (n, __FILE__, __LINE__, fmt, ##__VA_ARGS__) ++ + void EXPORT_FUNC(grub_fatal) (const char *fmt, ...) __attribute__ ((noreturn)); + void EXPORT_FUNC(grub_error_push) (void); + int EXPORT_FUNC(grub_error_pop) (void); diff --git a/0003-Restrict-ls-and-auto-file-completion-on-cryptodisk-p.patch b/0003-Restrict-ls-and-auto-file-completion-on-cryptodisk-p.patch new file mode 100644 index 0000000..0f56012 --- /dev/null +++ b/0003-Restrict-ls-and-auto-file-completion-on-cryptodisk-p.patch @@ -0,0 +1,117 @@ +From 6c8d390809956d355fed8bc830f64e86838e3e82 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Sat, 18 Nov 2023 21:42:00 +0800 +Subject: [PATCH 3/4] Restrict 'ls' and auto file completion on cryptodisk + print + +The 'ls' command allows file listing, while file completion assists in +providing matched file names by partially inputting via the TAB key. +Both functionalities should be restricted when the disk is automatically +unlocked for the same reasons as highlighted in the previous patch +addressing the limitation on file access to the cryptodisk. + +Given that no file is explicitly opened for listing, employing file +filters becomes impractical. Consequently, this patch focuses on +modifying relevant routines separately to incorporate necessary checks. +The objective is to introduce measures that prevent 'ls' and auto file +completion from accessing encrypted data when the disk is automatically +unlocked. + +By implementing these modifications, any attempt to utilize 'ls' or file +completion on the cryptodisk will result in an "Access Denied: +prohibited to browse encrypted data" error message, thus effectively +alerting the user about the restricted access. + +While protecting content within disk files from viewing is essential, +it's equally crucial to restrict access to in-memory content. This +includes prohibiting access to the decrypted in-memory copies of disk +files. + +This enhancement aims to fortify security protocols by extending +restrictions to additional functionalities beyond direct file access. + +Signed-Off-by Michael Chang +--- + grub-core/commands/ls.c | 8 ++++++++ + grub-core/commands/minicmd.c | 6 ++++++ + grub-core/kern/corecmd.c | 8 ++++++++ + grub-core/normal/completion.c | 8 ++++++++ + 4 files changed, 30 insertions(+) + +diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c +index 8e98c73cc..aeb336a73 100644 +--- a/grub-core/commands/ls.c ++++ b/grub-core/commands/ls.c +@@ -183,6 +183,14 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) + if (! dev) + goto fail; + ++ if (dev->disk && ++ grub_disk_is_crypto (dev->disk) && ++ grub_file_filters[GRUB_FILE_FILTER_NOCAT]) ++ { ++ grub_error (GRUB_ERR_ACCESS_DENIED, N_("prohibited to browse encrypted content")); ++ goto fail; ++ } ++ + fs = grub_fs_probe (dev); + path = grub_strchr (dirname, ')'); + if (! path) +diff --git a/grub-core/commands/minicmd.c b/grub-core/commands/minicmd.c +index fa498931e..8f2ac0539 100644 +--- a/grub-core/commands/minicmd.c ++++ b/grub-core/commands/minicmd.c +@@ -101,6 +101,12 @@ grub_mini_cmd_dump (struct grub_command *cmd __attribute__ ((unused)), + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no address specified"); + ++ /* NOCAT filter is applied to prevent cat alike command from revealing file ++ * content, the dump command should also be prohibited to revealing memory ++ * content as well */ ++ if (grub_file_filters[GRUB_FILE_FILTER_NOCAT]) ++ return grub_error (GRUB_ERR_ACCESS_DENIED, N_("prohibited by security policy")); ++ + #if GRUB_CPU_SIZEOF_VOID_P == GRUB_CPU_SIZEOF_LONG + #define grub_strtoaddr grub_strtoul + #else +diff --git a/grub-core/kern/corecmd.c b/grub-core/kern/corecmd.c +index 62d434ba9..b639bc3ae 100644 +--- a/grub-core/kern/corecmd.c ++++ b/grub-core/kern/corecmd.c +@@ -135,6 +135,14 @@ grub_core_cmd_ls (struct grub_command *cmd __attribute__ ((unused)), + if (! dev) + goto fail; + ++ if (dev->disk && ++ grub_disk_is_crypto (dev->disk) && ++ grub_file_filters[GRUB_FILE_FILTER_NOCAT]) ++ { ++ grub_error (GRUB_ERR_ACCESS_DENIED, N_("prohibited to browse encrypted content")); ++ goto fail; ++ } ++ + fs = grub_fs_probe (dev); + path = grub_strchr (argv[0], ')'); + if (! path) +diff --git a/grub-core/normal/completion.c b/grub-core/normal/completion.c +index 18cadfa85..d003ec37d 100644 +--- a/grub-core/normal/completion.c ++++ b/grub-core/normal/completion.c +@@ -259,6 +259,14 @@ complete_file (void) + goto fail; + } + ++ if (dev->disk && ++ grub_disk_is_crypto (dev->disk) && ++ grub_file_filters[GRUB_FILE_FILTER_NOCAT]) ++ { ++ grub_error (GRUB_ERR_ACCESS_DENIED, N_("prohibited to browse encrypted content")); ++ goto fail; ++ } ++ + fs = grub_fs_probe (dev); + if (! fs) + { +-- +2.42.1 + diff --git a/0003-bootp-New-net_bootp6-command.patch b/0003-bootp-New-net_bootp6-command.patch new file mode 100644 index 0000000..c4a4edd --- /dev/null +++ b/0003-bootp-New-net_bootp6-command.patch @@ -0,0 +1,1114 @@ +From f6c8c7985fd88fc89cd49227c9e74feaf58cfdd6 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Sun, 10 Jul 2016 23:46:06 +0800 +Subject: [PATCH 3/8] bootp: New net_bootp6 command + +Implement new net_bootp6 command for IPv6 network auto configuration via the +DHCPv6 protocol (RFC3315). + +Signed-off-by: Michael Chang +Signed-off-by: Ken Lin + +V1: + * Use grub_calloc for overflow check and return NULL when it would + occur. + +--- + grub-core/net/bootp.c | 908 +++++++++++++++++++++++++++++++++++++++++++++++++- + grub-core/net/ip.c | 39 +++ + include/grub/net.h | 72 ++++ + 3 files changed, 1018 insertions(+), 1 deletion(-) + +--- a/grub-core/net/bootp.c ++++ b/grub-core/net/bootp.c +@@ -24,6 +24,98 @@ + #include + #include + #include ++#include ++#include ++ ++static int ++dissect_url (const char *url, char **proto, char **host, char **path) ++{ ++ const char *p, *ps; ++ grub_size_t l; ++ ++ *proto = *host = *path = NULL; ++ ps = p = url; ++ ++ while ((p = grub_strchr (p, ':'))) ++ { ++ if (grub_strlen (p) < sizeof ("://") - 1) ++ break; ++ if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0) ++ { ++ l = p - ps; ++ *proto = grub_malloc (l + 1); ++ if (!*proto) ++ { ++ grub_print_error (); ++ return 0; ++ } ++ ++ grub_memcpy (*proto, ps, l); ++ (*proto)[l] = '\0'; ++ p += sizeof ("://") - 1; ++ break; ++ } ++ ++p; ++ } ++ ++ if (!*proto) ++ { ++ grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url); ++ return 0; ++ } ++ ++ ps = p; ++ p = grub_strchr (p, '/'); ++ ++ if (!p) ++ { ++ grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url); ++ grub_free (*proto); ++ *proto = NULL; ++ return 0; ++ } ++ ++ l = p - ps; ++ ++ if (l > 2 && ps[0] == '[' && ps[l - 1] == ']') ++ { ++ *host = grub_malloc (l - 1); ++ if (!*host) ++ { ++ grub_print_error (); ++ grub_free (*proto); ++ *proto = NULL; ++ return 0; ++ } ++ grub_memcpy (*host, ps + 1, l - 2); ++ (*host)[l - 2] = 0; ++ } ++ else ++ { ++ *host = grub_malloc (l + 1); ++ if (!*host) ++ { ++ grub_print_error (); ++ grub_free (*proto); ++ *proto = NULL; ++ return 0; ++ } ++ grub_memcpy (*host, ps, l); ++ (*host)[l] = 0; ++ } ++ ++ *path = grub_strdup (p); ++ if (!*path) ++ { ++ grub_print_error (); ++ grub_free (*host); ++ grub_free (*proto); ++ *host = NULL; ++ *proto = NULL; ++ return 0; ++ } ++ return 1; ++} + + struct grub_dhcp_discover_options + { +@@ -610,6 +702,578 @@ + return err; + } + ++/* The default netbuff size for sending DHCPv6 packets which should be ++ large enough to hold the information */ ++#define GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE 512 ++ ++struct grub_dhcp6_options ++{ ++ grub_uint8_t *client_duid; ++ grub_uint16_t client_duid_len; ++ grub_uint8_t *server_duid; ++ grub_uint16_t server_duid_len; ++ grub_uint32_t iaid; ++ grub_uint32_t t1; ++ grub_uint32_t t2; ++ grub_net_network_level_address_t *ia_addr; ++ grub_uint32_t preferred_lifetime; ++ grub_uint32_t valid_lifetime; ++ grub_net_network_level_address_t *dns_server_addrs; ++ grub_uint16_t num_dns_server; ++ char *boot_file_proto; ++ char *boot_file_server_ip; ++ char *boot_file_path; ++}; ++ ++typedef struct grub_dhcp6_options *grub_dhcp6_options_t; ++ ++struct grub_dhcp6_session ++{ ++ struct grub_dhcp6_session *next; ++ struct grub_dhcp6_session **prev; ++ grub_uint32_t iaid; ++ grub_uint32_t transaction_id:24; ++ grub_uint64_t start_time; ++ struct grub_net_dhcp6_option_duid_ll duid; ++ struct grub_net_network_level_interface *iface; ++ ++ /* The associated dhcpv6 options */ ++ grub_dhcp6_options_t adv; ++ grub_dhcp6_options_t reply; ++}; ++ ++typedef struct grub_dhcp6_session *grub_dhcp6_session_t; ++ ++typedef void (*dhcp6_option_hook_fn) (const struct grub_net_dhcp6_option *opt, void *data); ++ ++static void ++foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size, ++ dhcp6_option_hook_fn hook, void *hook_data); ++ ++static void ++parse_dhcp6_iaaddr (const struct grub_net_dhcp6_option *opt, void *data) ++{ ++ grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t )data; ++ ++ grub_uint16_t code = grub_be_to_cpu16 (opt->code); ++ grub_uint16_t len = grub_be_to_cpu16 (opt->len); ++ ++ if (code == GRUB_NET_DHCP6_OPTION_IAADDR) ++ { ++ const struct grub_net_dhcp6_option_iaaddr *iaaddr; ++ iaaddr = (const struct grub_net_dhcp6_option_iaaddr *)opt->data; ++ ++ if (len < sizeof (*iaaddr)) ++ { ++ grub_dprintf ("bootp", "DHCPv6: code %u with insufficient length %u\n", code, len); ++ return; ++ } ++ if (!dhcp6->ia_addr) ++ { ++ dhcp6->ia_addr = grub_malloc (sizeof(*dhcp6->ia_addr)); ++ dhcp6->ia_addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; ++ dhcp6->ia_addr->ipv6[0] = grub_get_unaligned64 (iaaddr->addr); ++ dhcp6->ia_addr->ipv6[1] = grub_get_unaligned64 (iaaddr->addr + 8); ++ dhcp6->preferred_lifetime = grub_be_to_cpu32 (iaaddr->preferred_lifetime); ++ dhcp6->valid_lifetime = grub_be_to_cpu32 (iaaddr->valid_lifetime); ++ } ++ } ++} ++ ++static void ++parse_dhcp6_option (const struct grub_net_dhcp6_option *opt, void *data) ++{ ++ grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t)data; ++ grub_uint16_t code = grub_be_to_cpu16 (opt->code); ++ grub_uint16_t len = grub_be_to_cpu16 (opt->len); ++ ++ switch (code) ++ { ++ case GRUB_NET_DHCP6_OPTION_CLIENTID: ++ ++ if (dhcp6->client_duid || !len) ++ { ++ grub_dprintf ("bootp", "Skipped DHCPv6 CLIENTID with length %u\n", len); ++ break; ++ } ++ dhcp6->client_duid = grub_malloc (len); ++ grub_memcpy (dhcp6->client_duid, opt->data, len); ++ dhcp6->client_duid_len = len; ++ break; ++ ++ case GRUB_NET_DHCP6_OPTION_SERVERID: ++ ++ if (dhcp6->server_duid || !len) ++ { ++ grub_dprintf ("bootp", "Skipped DHCPv6 SERVERID with length %u\n", len); ++ break; ++ } ++ dhcp6->server_duid = grub_malloc (len); ++ grub_memcpy (dhcp6->server_duid, opt->data, len); ++ dhcp6->server_duid_len = len; ++ break; ++ ++ case GRUB_NET_DHCP6_OPTION_IA_NA: ++ { ++ const struct grub_net_dhcp6_option_iana *ia_na; ++ grub_uint16_t data_len; ++ ++ if (dhcp6->iaid || len < sizeof (*ia_na)) ++ { ++ grub_dprintf ("bootp", "Skipped DHCPv6 IA_NA with length %u\n", len); ++ break; ++ } ++ ia_na = (const struct grub_net_dhcp6_option_iana *)opt->data; ++ dhcp6->iaid = grub_be_to_cpu32 (ia_na->iaid); ++ dhcp6->t1 = grub_be_to_cpu32 (ia_na->t1); ++ dhcp6->t2 = grub_be_to_cpu32 (ia_na->t2); ++ ++ data_len = len - sizeof (*ia_na); ++ if (data_len) ++ foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)ia_na->data, data_len, parse_dhcp6_iaaddr, dhcp6); ++ } ++ break; ++ ++ case GRUB_NET_DHCP6_OPTION_DNS_SERVERS: ++ { ++ const grub_uint8_t *po; ++ grub_uint16_t ln; ++ grub_net_network_level_address_t *la; ++ ++ if (!len || len & 0xf) ++ { ++ grub_dprintf ("bootp", "Skip invalid length DHCPv6 DNS_SERVERS \n"); ++ break; ++ } ++ dhcp6->num_dns_server = ln = len >> 4; ++ dhcp6->dns_server_addrs = la = grub_calloc (ln, sizeof (*la)); ++ ++ for (po = opt->data; ln > 0; po += 0x10, la++, ln--) ++ { ++ la->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; ++ la->ipv6[0] = grub_get_unaligned64 (po); ++ la->ipv6[1] = grub_get_unaligned64 (po + 8); ++ la->option = DNS_OPTION_PREFER_IPV6; ++ } ++ } ++ break; ++ ++ case GRUB_NET_DHCP6_OPTION_BOOTFILE_URL: ++ dissect_url ((const char *)opt->data, ++ &dhcp6->boot_file_proto, ++ &dhcp6->boot_file_server_ip, ++ &dhcp6->boot_file_path); ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++static void ++foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size, dhcp6_option_hook_fn hook, void *hook_data) ++{ ++ while (size) ++ { ++ grub_uint16_t code, len; ++ ++ if (size < sizeof (*opt)) ++ { ++ grub_dprintf ("bootp", "DHCPv6: Options stopped with remaining size %" PRIxGRUB_SIZE "\n", size); ++ break; ++ } ++ size -= sizeof (*opt); ++ len = grub_be_to_cpu16 (opt->len); ++ code = grub_be_to_cpu16 (opt->code); ++ if (size < len) ++ { ++ grub_dprintf ("bootp", "DHCPv6: Options stopped at out of bound length %u for option %u\n", len, code); ++ break; ++ } ++ if (!len) ++ { ++ grub_dprintf ("bootp", "DHCPv6: Options stopped at zero length option %u\n", code); ++ break; ++ } ++ else ++ { ++ if (hook) ++ hook (opt, hook_data); ++ size -= len; ++ opt = (const struct grub_net_dhcp6_option *)((grub_uint8_t *)opt + len + sizeof (*opt)); ++ } ++ } ++} ++ ++static grub_dhcp6_options_t ++grub_dhcp6_options_get (const struct grub_net_dhcp6_packet *v6h, ++ grub_size_t size) ++{ ++ grub_dhcp6_options_t options; ++ ++ if (size < sizeof (*v6h)) ++ { ++ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small")); ++ return NULL; ++ } ++ ++ options = grub_zalloc (sizeof(*options)); ++ if (!options) ++ return NULL; ++ ++ foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)v6h->dhcp_options, ++ size - sizeof (*v6h), parse_dhcp6_option, options); ++ ++ return options; ++} ++ ++static void ++grub_dhcp6_options_free (grub_dhcp6_options_t options) ++{ ++ if (options->client_duid) ++ grub_free (options->client_duid); ++ if (options->server_duid) ++ grub_free (options->server_duid); ++ if (options->ia_addr) ++ grub_free (options->ia_addr); ++ if (options->dns_server_addrs) ++ grub_free (options->dns_server_addrs); ++ if (options->boot_file_proto) ++ grub_free (options->boot_file_proto); ++ if (options->boot_file_server_ip) ++ grub_free (options->boot_file_server_ip); ++ if (options->boot_file_path) ++ grub_free (options->boot_file_path); ++ ++ grub_free (options); ++} ++ ++static grub_dhcp6_session_t grub_dhcp6_sessions; ++#define FOR_DHCP6_SESSIONS(var) FOR_LIST_ELEMENTS (var, grub_dhcp6_sessions) ++ ++static void ++grub_net_configure_by_dhcp6_info (const char *name, ++ struct grub_net_card *card, ++ grub_dhcp6_options_t dhcp6, ++ int is_def, ++ int flags, ++ struct grub_net_network_level_interface **ret_inf) ++{ ++ grub_net_network_level_netaddress_t netaddr; ++ struct grub_net_network_level_interface *inf; ++ ++ if (dhcp6->ia_addr) ++ { ++ inf = grub_net_add_addr (name, card, dhcp6->ia_addr, &card->default_address, flags); ++ ++ netaddr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; ++ netaddr.ipv6.base[0] = dhcp6->ia_addr->ipv6[0]; ++ netaddr.ipv6.base[1] = 0; ++ netaddr.ipv6.masksize = 64; ++ grub_net_add_route (name, netaddr, inf); ++ ++ if (ret_inf) ++ *ret_inf = inf; ++ } ++ ++ if (dhcp6->dns_server_addrs) ++ { ++ grub_uint16_t i; ++ ++ for (i = 0; i < dhcp6->num_dns_server; ++i) ++ grub_net_add_dns_server (dhcp6->dns_server_addrs + i); ++ } ++ ++ if (dhcp6->boot_file_path) ++ grub_env_set_net_property (name, "boot_file", dhcp6->boot_file_path, ++ grub_strlen (dhcp6->boot_file_path)); ++ ++ if (is_def && dhcp6->boot_file_server_ip) ++ { ++ grub_net_default_server = grub_strdup (dhcp6->boot_file_server_ip); ++ grub_env_set ("net_default_interface", name); ++ grub_env_export ("net_default_interface"); ++ } ++} ++ ++static void ++grub_dhcp6_session_add (struct grub_net_network_level_interface *iface, ++ grub_uint32_t iaid) ++{ ++ grub_dhcp6_session_t se; ++ struct grub_datetime date; ++ grub_err_t err; ++ grub_int64_t t = 0; ++ ++ se = grub_malloc (sizeof (*se)); ++ ++ err = grub_get_datetime (&date); ++ if (err || !grub_datetime2unixtime (&date, &t)) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ t = 0; ++ } ++ ++ se->iface = iface; ++ se->iaid = iaid; ++ se->transaction_id = t; ++ se->start_time = grub_get_time_ms (); ++ se->duid.type = grub_cpu_to_be16_compile_time (3) ; ++ se->duid.hw_type = grub_cpu_to_be16_compile_time (1); ++ grub_memcpy (&se->duid.hwaddr, &iface->hwaddress.mac, sizeof (se->duid.hwaddr)); ++ se->adv = NULL; ++ se->reply = NULL; ++ grub_list_push (GRUB_AS_LIST_P (&grub_dhcp6_sessions), GRUB_AS_LIST (se)); ++} ++ ++static void ++grub_dhcp6_session_remove (grub_dhcp6_session_t se) ++{ ++ grub_list_remove (GRUB_AS_LIST (se)); ++ if (se->adv) ++ grub_dhcp6_options_free (se->adv); ++ if (se->reply) ++ grub_dhcp6_options_free (se->reply); ++ grub_free (se); ++} ++ ++static void ++grub_dhcp6_session_remove_all (void) ++{ ++ grub_dhcp6_session_t se; ++ ++ FOR_DHCP6_SESSIONS (se) ++ { ++ grub_dhcp6_session_remove (se); ++ se = grub_dhcp6_sessions; ++ } ++} ++ ++static grub_err_t ++grub_dhcp6_session_configure_network (grub_dhcp6_session_t se) ++{ ++ char *name; ++ ++ name = grub_xasprintf ("%s:dhcp6", se->iface->card->name); ++ if (!name) ++ return grub_errno; ++ ++ grub_net_configure_by_dhcp6_info (name, se->iface->card, se->reply, 1, 0, 0); ++ grub_free (name); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_dhcp6_session_send_request (grub_dhcp6_session_t se) ++{ ++ struct grub_net_buff *nb; ++ struct grub_net_dhcp6_option *opt; ++ struct grub_net_dhcp6_packet *v6h; ++ struct grub_net_dhcp6_option_iana *ia_na; ++ struct grub_net_dhcp6_option_iaaddr *iaaddr; ++ struct udphdr *udph; ++ grub_net_network_level_address_t multicast; ++ grub_net_link_level_address_t ll_multicast; ++ grub_uint64_t elapsed; ++ struct grub_net_network_level_interface *inf = se->iface; ++ grub_dhcp6_options_t dhcp6 = se->adv; ++ grub_err_t err = GRUB_ERR_NONE; ++ ++ multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; ++ multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48); ++ multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL); ++ ++ err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast); ++ if (err) ++ return err; ++ ++ nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); ++ ++ if (!nb) ++ return grub_errno; ++ ++ err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); ++ if (err) ++ { ++ grub_netbuff_free (nb); ++ return err; ++ } ++ ++ err = grub_netbuff_push (nb, dhcp6->client_duid_len + sizeof (*opt)); ++ if (err) ++ { ++ grub_netbuff_free (nb); ++ return err; ++ } ++ opt = (struct grub_net_dhcp6_option *)nb->data; ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID); ++ opt->len = grub_cpu_to_be16 (dhcp6->client_duid_len); ++ grub_memcpy (opt->data, dhcp6->client_duid , dhcp6->client_duid_len); ++ ++ err = grub_netbuff_push (nb, dhcp6->server_duid_len + sizeof (*opt)); ++ if (err) ++ { ++ grub_netbuff_free (nb); ++ return err; ++ } ++ opt = (struct grub_net_dhcp6_option *)nb->data; ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_SERVERID); ++ opt->len = grub_cpu_to_be16 (dhcp6->server_duid_len); ++ grub_memcpy (opt->data, dhcp6->server_duid , dhcp6->server_duid_len); ++ ++ err = grub_netbuff_push (nb, sizeof (*ia_na) + sizeof (*opt)); ++ if (err) ++ { ++ grub_netbuff_free (nb); ++ return err; ++ } ++ ++ if (dhcp6->ia_addr) ++ { ++ err = grub_netbuff_push (nb, sizeof(*iaaddr) + sizeof (*opt)); ++ if (err) ++ { ++ grub_netbuff_free (nb); ++ return err; ++ } ++ } ++ opt = (struct grub_net_dhcp6_option *)nb->data; ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); ++ opt->len = grub_cpu_to_be16 (sizeof (*ia_na)); ++ if (dhcp6->ia_addr) ++ opt->len += grub_cpu_to_be16 (sizeof(*iaaddr) + sizeof (*opt)); ++ ++ ia_na = (struct grub_net_dhcp6_option_iana *)opt->data; ++ ia_na->iaid = grub_cpu_to_be32 (dhcp6->iaid); ++ ++ ia_na->t1 = grub_cpu_to_be32 (dhcp6->t1); ++ ia_na->t2 = grub_cpu_to_be32 (dhcp6->t2); ++ ++ if (dhcp6->ia_addr) ++ { ++ opt = (struct grub_net_dhcp6_option *)ia_na->data; ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR); ++ opt->len = grub_cpu_to_be16 (sizeof (*iaaddr)); ++ iaaddr = (struct grub_net_dhcp6_option_iaaddr *)opt->data; ++ grub_set_unaligned64 (iaaddr->addr, dhcp6->ia_addr->ipv6[0]); ++ grub_set_unaligned64 (iaaddr->addr + 8, dhcp6->ia_addr->ipv6[1]); ++ ++ iaaddr->preferred_lifetime = grub_cpu_to_be32 (dhcp6->preferred_lifetime); ++ iaaddr->valid_lifetime = grub_cpu_to_be32 (dhcp6->valid_lifetime); ++ } ++ ++ err = grub_netbuff_push (nb, sizeof (*opt) + 2 * sizeof (grub_uint16_t)); ++ if (err) ++ { ++ grub_netbuff_free (nb); ++ return err; ++ } ++ ++ opt = (struct grub_net_dhcp6_option*) nb->data; ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ORO); ++ opt->len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_uint16_t)); ++ grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL)); ++ grub_set_unaligned16 (opt->data + 2, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS)); ++ ++ err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t)); ++ if (err) ++ { ++ grub_netbuff_free (nb); ++ return err; ++ } ++ opt = (struct grub_net_dhcp6_option*) nb->data; ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME); ++ opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t)); ++ ++ /* the time is expressed in hundredths of a second */ ++ elapsed = grub_divmod64 (grub_get_time_ms () - se->start_time, 10, 0); ++ ++ if (elapsed > 0xffff) ++ elapsed = 0xffff; ++ ++ grub_set_unaligned16 (opt->data, grub_cpu_to_be16 ((grub_uint16_t)elapsed)); ++ ++ err = grub_netbuff_push (nb, sizeof (*v6h)); ++ if (err) ++ { ++ grub_netbuff_free (nb); ++ return err; ++ } ++ ++ v6h = (struct grub_net_dhcp6_packet *) nb->data; ++ v6h->message_type = GRUB_NET_DHCP6_REQUEST; ++ v6h->transaction_id = se->transaction_id; ++ ++ err = grub_netbuff_push (nb, sizeof (*udph)); ++ if (err) ++ { ++ grub_netbuff_free (nb); ++ return err; ++ } ++ ++ udph = (struct udphdr *) nb->data; ++ udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT); ++ udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT); ++ udph->chksum = 0; ++ udph->len = grub_cpu_to_be16 (nb->tail - nb->data); ++ ++ udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, ++ &inf->address, ++ &multicast); ++ err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb, ++ GRUB_NET_IP_UDP); ++ ++ grub_netbuff_free (nb); ++ ++ return err; ++} ++ ++struct grub_net_network_level_interface * ++grub_net_configure_by_dhcpv6_reply (const char *name, ++ struct grub_net_card *card, ++ grub_net_interface_flags_t flags, ++ const struct grub_net_dhcp6_packet *v6h, ++ grub_size_t size, ++ int is_def, ++ char **device, char **path) ++{ ++ struct grub_net_network_level_interface *inf; ++ grub_dhcp6_options_t dhcp6; ++ ++ dhcp6 = grub_dhcp6_options_get (v6h, size); ++ if (!dhcp6) ++ { ++ grub_print_error (); ++ return NULL; ++ } ++ ++ grub_net_configure_by_dhcp6_info (name, card, dhcp6, is_def, flags, &inf); ++ ++ if (device && dhcp6->boot_file_proto && dhcp6->boot_file_server_ip) ++ { ++ *device = grub_xasprintf ("%s,%s", dhcp6->boot_file_proto, dhcp6->boot_file_server_ip); ++ grub_print_error (); ++ } ++ if (path && dhcp6->boot_file_path) ++ { ++ *path = grub_strdup (dhcp6->boot_file_path); ++ grub_print_error (); ++ if (*path) ++ { ++ char *slash; ++ slash = grub_strrchr (*path, '/'); ++ if (slash) ++ *slash = 0; ++ else ++ **path = 0; ++ } ++ } ++ ++ grub_dhcp6_options_free (dhcp6); ++ return inf; ++} ++ + /* + * This is called directly from net/ip.c:handle_dgram(), because those + * BOOTP/DHCP packets are a bit special due to their improper +@@ -678,6 +1342,77 @@ + } + } + ++grub_err_t ++grub_net_process_dhcp6 (struct grub_net_buff *nb, ++ struct grub_net_card *card __attribute__ ((unused))) ++{ ++ const struct grub_net_dhcp6_packet *v6h; ++ grub_dhcp6_session_t se; ++ grub_size_t size; ++ grub_dhcp6_options_t options; ++ ++ v6h = (const struct grub_net_dhcp6_packet *) nb->data; ++ size = nb->tail - nb->data; ++ ++ options = grub_dhcp6_options_get (v6h, size); ++ if (!options) ++ return grub_errno; ++ ++ if (!options->client_duid || !options->server_duid || !options->ia_addr) ++ { ++ grub_dhcp6_options_free (options); ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Bad DHCPv6 Packet"); ++ } ++ ++ FOR_DHCP6_SESSIONS (se) ++ { ++ if (se->transaction_id == v6h->transaction_id && ++ grub_memcmp (options->client_duid, &se->duid, sizeof (se->duid)) == 0 && ++ se->iaid == options->iaid) ++ break; ++ } ++ ++ if (!se) ++ { ++ grub_dprintf ("bootp", "DHCPv6 session not found\n"); ++ grub_dhcp6_options_free (options); ++ return GRUB_ERR_NONE; ++ } ++ ++ if (v6h->message_type == GRUB_NET_DHCP6_ADVERTISE) ++ { ++ if (se->adv) ++ { ++ grub_dprintf ("bootp", "Skipped DHCPv6 Advertised .. \n"); ++ grub_dhcp6_options_free (options); ++ return GRUB_ERR_NONE; ++ } ++ ++ se->adv = options; ++ return grub_dhcp6_session_send_request (se); ++ } ++ else if (v6h->message_type == GRUB_NET_DHCP6_REPLY) ++ { ++ if (!se->adv) ++ { ++ grub_dprintf ("bootp", "Skipped DHCPv6 Reply .. \n"); ++ grub_dhcp6_options_free (options); ++ return GRUB_ERR_NONE; ++ } ++ ++ se->reply = options; ++ grub_dhcp6_session_configure_network (se); ++ grub_dhcp6_session_remove (se); ++ return GRUB_ERR_NONE; ++ } ++ else ++ { ++ grub_dhcp6_options_free (options); ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ + static grub_err_t + grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +@@ -903,7 +1638,174 @@ + return err; + } + +-static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp; ++static grub_err_t ++grub_cmd_bootp6 (struct grub_command *cmd __attribute__ ((unused)), ++ int argc, char **args) ++{ ++ struct grub_net_card *card; ++ grub_uint32_t iaid = 0; ++ int interval; ++ grub_err_t err; ++ grub_dhcp6_session_t se; ++ ++ err = GRUB_ERR_NONE; ++ ++ FOR_NET_CARDS (card) ++ { ++ struct grub_net_network_level_interface *iface; ++ ++ if (argc > 0 && grub_strcmp (card->name, args[0]) != 0) ++ continue; ++ ++ iface = grub_net_ipv6_get_link_local (card, &card->default_address); ++ if (!iface) ++ { ++ grub_dhcp6_session_remove_all (); ++ return grub_errno; ++ } ++ ++ grub_dhcp6_session_add (iface, iaid++); ++ } ++ ++ for (interval = 200; interval < 10000; interval *= 2) ++ { ++ int done = 1; ++ ++ FOR_DHCP6_SESSIONS (se) ++ { ++ struct grub_net_buff *nb; ++ struct grub_net_dhcp6_option *opt; ++ struct grub_net_dhcp6_packet *v6h; ++ struct grub_net_dhcp6_option_duid_ll *duid; ++ struct grub_net_dhcp6_option_iana *ia_na; ++ grub_net_network_level_address_t multicast; ++ grub_net_link_level_address_t ll_multicast; ++ struct udphdr *udph; ++ ++ multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; ++ multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48); ++ multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL); ++ ++ err = grub_net_link_layer_resolve (se->iface, ++ &multicast, &ll_multicast); ++ if (err) ++ { ++ grub_dhcp6_session_remove_all (); ++ return err; ++ } ++ ++ nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); ++ ++ if (!nb) ++ { ++ grub_dhcp6_session_remove_all (); ++ return grub_errno; ++ } ++ ++ err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); ++ if (err) ++ { ++ grub_dhcp6_session_remove_all (); ++ grub_netbuff_free (nb); ++ return err; ++ } ++ ++ err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t)); ++ if (err) ++ { ++ grub_dhcp6_session_remove_all (); ++ grub_netbuff_free (nb); ++ return err; ++ } ++ ++ opt = (struct grub_net_dhcp6_option *)nb->data; ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME); ++ opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t)); ++ grub_set_unaligned16 (opt->data, 0); ++ ++ err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*duid)); ++ if (err) ++ { ++ grub_dhcp6_session_remove_all (); ++ grub_netbuff_free (nb); ++ return err; ++ } ++ ++ opt = (struct grub_net_dhcp6_option *)nb->data; ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID); ++ opt->len = grub_cpu_to_be16 (sizeof (*duid)); ++ ++ duid = (struct grub_net_dhcp6_option_duid_ll *) opt->data; ++ grub_memcpy (duid, &se->duid, sizeof (*duid)); ++ ++ err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*ia_na)); ++ if (err) ++ { ++ grub_dhcp6_session_remove_all (); ++ grub_netbuff_free (nb); ++ return err; ++ } ++ ++ opt = (struct grub_net_dhcp6_option *)nb->data; ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); ++ opt->len = grub_cpu_to_be16 (sizeof (*ia_na)); ++ ia_na = (struct grub_net_dhcp6_option_iana *)opt->data; ++ ia_na->iaid = grub_cpu_to_be32 (se->iaid); ++ ia_na->t1 = 0; ++ ia_na->t2 = 0; ++ ++ err = grub_netbuff_push (nb, sizeof (*v6h)); ++ if (err) ++ { ++ grub_dhcp6_session_remove_all (); ++ grub_netbuff_free (nb); ++ return err; ++ } ++ ++ v6h = (struct grub_net_dhcp6_packet *)nb->data; ++ v6h->message_type = GRUB_NET_DHCP6_SOLICIT; ++ v6h->transaction_id = se->transaction_id; ++ ++ grub_netbuff_push (nb, sizeof (*udph)); ++ ++ udph = (struct udphdr *) nb->data; ++ udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT); ++ udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT); ++ udph->chksum = 0; ++ udph->len = grub_cpu_to_be16 (nb->tail - nb->data); ++ ++ udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, ++ &se->iface->address, &multicast); ++ ++ err = grub_net_send_ip_packet (se->iface, &multicast, ++ &ll_multicast, nb, GRUB_NET_IP_UDP); ++ done = 0; ++ grub_netbuff_free (nb); ++ ++ if (err) ++ { ++ grub_dhcp6_session_remove_all (); ++ return err; ++ } ++ } ++ if (!done) ++ grub_net_poll_cards (interval, 0); ++ } ++ ++ FOR_DHCP6_SESSIONS (se) ++ { ++ grub_error_push (); ++ err = grub_error (GRUB_ERR_FILE_NOT_FOUND, ++ N_("couldn't autoconfigure %s"), ++ se->iface->card->name); ++ } ++ ++ grub_dhcp6_session_remove_all (); ++ ++ return err; ++} ++ ++static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp, cmd_bootp6; + + void + grub_bootp_init (void) +@@ -917,6 +1819,9 @@ + cmd_getdhcp = grub_register_command ("net_get_dhcp_option", grub_cmd_dhcpopt, + N_("VAR INTERFACE NUMBER DESCRIPTION"), + N_("retrieve DHCP option and save it into VAR. If VAR is - then print the value.")); ++ cmd_bootp6 = grub_register_command ("net_bootp6", grub_cmd_bootp6, ++ N_("[CARD]"), ++ N_("perform a DHCPv6 autoconfiguration")); + } + + void +@@ -925,4 +1830,5 @@ + grub_unregister_command (cmd_getdhcp); + grub_unregister_command (cmd_bootp); + grub_unregister_command (cmd_dhcp); ++ grub_unregister_command (cmd_bootp6); + } +--- a/grub-core/net/ip.c ++++ b/grub-core/net/ip.c +@@ -240,6 +240,45 @@ + { + struct udphdr *udph; + udph = (struct udphdr *) nb->data; ++ ++ if (proto == GRUB_NET_IP_UDP && udph->dst == grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT)) ++ { ++ if (udph->chksum) ++ { ++ grub_uint16_t chk, expected; ++ chk = udph->chksum; ++ udph->chksum = 0; ++ expected = grub_net_ip_transport_checksum (nb, ++ GRUB_NET_IP_UDP, ++ source, ++ dest); ++ if (expected != chk) ++ { ++ grub_dprintf ("net", "Invalid UDP checksum. " ++ "Expected %x, got %x\n", ++ grub_be_to_cpu16 (expected), ++ grub_be_to_cpu16 (chk)); ++ grub_netbuff_free (nb); ++ return GRUB_ERR_NONE; ++ } ++ udph->chksum = chk; ++ } ++ ++ err = grub_netbuff_pull (nb, sizeof (*udph)); ++ if (err) ++ { ++ grub_netbuff_free (nb); ++ return err; ++ } ++ ++ err = grub_net_process_dhcp6 (nb, card); ++ if (err) ++ grub_print_error (); ++ ++ grub_netbuff_free (nb); ++ return GRUB_ERR_NONE; ++ } ++ + if (proto == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 68) + { + const struct grub_net_bootp_packet *bootp; +--- a/include/grub/net.h ++++ b/include/grub/net.h +@@ -450,6 +450,66 @@ + grub_uint8_t vendor[0]; + } GRUB_PACKED; + ++struct grub_net_dhcp6_packet ++{ ++ grub_uint32_t message_type:8; ++ grub_uint32_t transaction_id:24; ++ grub_uint8_t dhcp_options[0]; ++} GRUB_PACKED; ++ ++struct grub_net_dhcp6_option { ++ grub_uint16_t code; ++ grub_uint16_t len; ++ grub_uint8_t data[0]; ++} GRUB_PACKED; ++ ++struct grub_net_dhcp6_option_iana { ++ grub_uint32_t iaid; ++ grub_uint32_t t1; ++ grub_uint32_t t2; ++ grub_uint8_t data[0]; ++} GRUB_PACKED; ++ ++struct grub_net_dhcp6_option_iaaddr { ++ grub_uint8_t addr[16]; ++ grub_uint32_t preferred_lifetime; ++ grub_uint32_t valid_lifetime; ++ grub_uint8_t data[0]; ++} GRUB_PACKED; ++ ++struct grub_net_dhcp6_option_duid_ll ++{ ++ grub_uint16_t type; ++ grub_uint16_t hw_type; ++ grub_uint8_t hwaddr[6]; ++} GRUB_PACKED; ++ ++enum ++ { ++ GRUB_NET_DHCP6_SOLICIT = 1, ++ GRUB_NET_DHCP6_ADVERTISE = 2, ++ GRUB_NET_DHCP6_REQUEST = 3, ++ GRUB_NET_DHCP6_REPLY = 7 ++ }; ++ ++enum ++ { ++ DHCP6_CLIENT_PORT = 546, ++ DHCP6_SERVER_PORT = 547 ++ }; ++ ++enum ++ { ++ GRUB_NET_DHCP6_OPTION_CLIENTID = 1, ++ GRUB_NET_DHCP6_OPTION_SERVERID = 2, ++ GRUB_NET_DHCP6_OPTION_IA_NA = 3, ++ GRUB_NET_DHCP6_OPTION_IAADDR = 5, ++ GRUB_NET_DHCP6_OPTION_ORO = 6, ++ GRUB_NET_DHCP6_OPTION_ELAPSED_TIME = 8, ++ GRUB_NET_DHCP6_OPTION_DNS_SERVERS = 23, ++ GRUB_NET_DHCP6_OPTION_BOOTFILE_URL = 59 ++ }; ++ + #define GRUB_NET_BOOTP_RFC1048_MAGIC_0 0x63 + #define GRUB_NET_BOOTP_RFC1048_MAGIC_1 0x82 + #define GRUB_NET_BOOTP_RFC1048_MAGIC_2 0x53 +@@ -485,6 +545,14 @@ + grub_size_t size, + int is_def, char **device, char **path); + ++struct grub_net_network_level_interface * ++grub_net_configure_by_dhcpv6_reply (const char *name, ++ struct grub_net_card *card, ++ grub_net_interface_flags_t flags, ++ const struct grub_net_dhcp6_packet *v6, ++ grub_size_t size, ++ int is_def, char **device, char **path); ++ + grub_err_t + grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf, + int mask); +@@ -493,6 +561,10 @@ + grub_net_process_dhcp (struct grub_net_buff *nb, + struct grub_net_network_level_interface *iface); + ++grub_err_t ++grub_net_process_dhcp6 (struct grub_net_buff *nb, ++ struct grub_net_card *card); ++ + int + grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a, + const grub_net_link_level_address_t *b); diff --git a/0003-cryptodisk-wipe-out-the-cached-keys-from-protectors.patch b/0003-cryptodisk-wipe-out-the-cached-keys-from-protectors.patch new file mode 100644 index 0000000..bec13f3 --- /dev/null +++ b/0003-cryptodisk-wipe-out-the-cached-keys-from-protectors.patch @@ -0,0 +1,36 @@ +From 64494ffc442a5de05b237ad48d27c70d22849a44 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Thu, 3 Aug 2023 15:52:52 +0800 +Subject: [PATCH 3/4] cryptodisk: wipe out the cached keys from protectors + +An attacker may insert a malicious disk with the same crypto UUID and +trick grub2 to mount the fake root. Even though the key from the key +protector fails to unlock the fake root, it's not wiped out cleanly so +the attacker could dump the memory to retrieve the secret key. To defend +such attack, wipe out the cached key when we don't need it. + +Signed-off-by: Gary Lin +--- + grub-core/disk/cryptodisk.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c +index cf37a0934..f42437f4e 100644 +--- a/grub-core/disk/cryptodisk.c ++++ b/grub-core/disk/cryptodisk.c +@@ -1348,7 +1348,11 @@ grub_cryptodisk_clear_key_cache (struct grub_cryptomount_args *cargs) + return; + + for (i = 0; cargs->protectors[i]; i++) +- grub_free (cargs->key_cache[i].key); ++ { ++ if (cargs->key_cache[i].key) ++ grub_memset (cargs->key_cache[i].key, 0, cargs->key_cache[i].key_len); ++ grub_free (cargs->key_cache[i].key); ++ } + + grub_free (cargs->key_cache); + } +-- +2.35.3 + diff --git a/0003-fs-ntfs-Fix-an-OOB-read-when-parsing-directory-entri.patch b/0003-fs-ntfs-Fix-an-OOB-read-when-parsing-directory-entri.patch new file mode 100644 index 0000000..3b7c3a1 --- /dev/null +++ b/0003-fs-ntfs-Fix-an-OOB-read-when-parsing-directory-entri.patch @@ -0,0 +1,73 @@ +From 7e5f031a6a6a3decc2360a7b0c71abbe598e7354 Mon Sep 17 00:00:00 2001 +From: Maxim Suhanov +Date: Mon, 28 Aug 2023 16:33:17 +0300 +Subject: [PATCH 3/6] fs/ntfs: Fix an OOB read when parsing directory entries + from resident and non-resident index attributes + +This fix introduces checks to ensure that index entries are never read +beyond the corresponding directory index. + +The lack of this check is a minor issue, likely not exploitable in any way. + +Reported-by: Maxim Suhanov +Signed-off-by: Maxim Suhanov +Reviewed-by: Daniel Kiper +--- + grub-core/fs/ntfs.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c +index a68e173d8..2d78b96e1 100644 +--- a/grub-core/fs/ntfs.c ++++ b/grub-core/fs/ntfs.c +@@ -599,7 +599,7 @@ get_utf8 (grub_uint8_t *in, grub_size_t len) + } + + static int +-list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, ++list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, grub_uint8_t *end_pos, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) + { + grub_uint8_t *np; +@@ -610,6 +610,9 @@ list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, + grub_uint8_t namespace; + char *ustr; + ++ if ((pos >= end_pos) || (end_pos - pos < 0x52)) ++ break; ++ + if (pos[0xC] & 2) /* end signature */ + break; + +@@ -617,6 +620,9 @@ list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, + ns = *(np++); + namespace = *(np++); + ++ if (2 * ns > end_pos - pos - 0x52) ++ break; ++ + /* + * Ignore files in DOS namespace, as they will reappear as Win32 + * names. +@@ -806,7 +812,9 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, + } + + cur_pos += 0x10; /* Skip index root */ +- ret = list_file (mft, cur_pos + u16at (cur_pos, 0), hook, hook_data); ++ ret = list_file (mft, cur_pos + u16at (cur_pos, 0), ++ at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR), ++ hook, hook_data); + if (ret) + goto done; + +@@ -893,6 +901,7 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, + (const grub_uint8_t *) "INDX"))) + goto done; + ret = list_file (mft, &indx[0x18 + u16at (indx, 0x18)], ++ indx + (mft->data->idx_size << GRUB_NTFS_BLK_SHR), + hook, hook_data); + if (ret) + goto done; +-- +2.42.0 + diff --git a/0003-fs-xfs-add-large-extent-counters-incompat-feature-su.patch b/0003-fs-xfs-add-large-extent-counters-incompat-feature-su.patch new file mode 100644 index 0000000..2edee66 --- /dev/null +++ b/0003-fs-xfs-add-large-extent-counters-incompat-feature-su.patch @@ -0,0 +1,115 @@ +From e7b1a524d5f86dcfddfbb069577e3b148dbb19cd Mon Sep 17 00:00:00 2001 +From: Anthony Iliopoulos via Grub-devel +Date: Thu, 26 Oct 2023 11:53:39 +0200 +Subject: [PATCH 3/3] fs/xfs: add large extent counters incompat feature + support + +XFS introduced 64-bit extent counters for inodes via a series of +upstream commits, and the feature was marked as stable in v6.5 via +commit 61d7e8274cd8 ("xfs: drop EXPERIMENTAL tag for large extent +counts"). + +Further, xfsprogs release v6.5.0 switched this feature on by default in +mkfs.xfs via commit e5b18d7d1d96 ("mkfs: enable large extent counts by +default"). + +Filesystems formatted with large extent count support (nrext64=1) are +thus currently not recognizable by grub, since this is an incompat +feature. Add the required support so that those filesystems and inodes +with large extent counters can be read by grub. + +Signed-off-by: Anthony Iliopoulos +--- + grub-core/fs/xfs.c | 30 +++++++++++++++++++++++++----- + 1 file changed, 25 insertions(+), 5 deletions(-) + +diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c +index 18edfcff4..bc2224dbb 100644 +--- a/grub-core/fs/xfs.c ++++ b/grub-core/fs/xfs.c +@@ -79,6 +79,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); + /* Inode flags2 flags */ + #define XFS_DIFLAG2_BIGTIME_BIT 3 + #define XFS_DIFLAG2_BIGTIME (1 << XFS_DIFLAG2_BIGTIME_BIT) ++#define XFS_DIFLAG2_NREXT64_BIT 4 ++#define XFS_DIFLAG2_NREXT64 (1 << XFS_DIFLAG2_NREXT64_BIT) + + /* incompat feature flags */ + #define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */ +@@ -86,6 +88,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); + #define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */ + #define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */ + #define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */ ++#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */ + + /* + * Directory entries with ftype are explicitly handled by GRUB code. +@@ -101,7 +104,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); + XFS_SB_FEAT_INCOMPAT_SPINODES | \ + XFS_SB_FEAT_INCOMPAT_META_UUID | \ + XFS_SB_FEAT_INCOMPAT_BIGTIME | \ +- XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR) ++ XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | \ ++ XFS_SB_FEAT_INCOMPAT_NREXT64) + + struct grub_xfs_sblock + { +@@ -203,7 +207,8 @@ struct grub_xfs_inode + grub_uint16_t mode; + grub_uint8_t version; + grub_uint8_t format; +- grub_uint8_t unused2[26]; ++ grub_uint8_t unused2[18]; ++ grub_uint64_t nextents_big; + grub_uint64_t atime; + grub_uint64_t mtime; + grub_uint64_t ctime; +@@ -545,11 +550,26 @@ get_fsb (const void *keys, int idx) + return grub_be_to_cpu64 (grub_get_unaligned64 (p)); + } + ++static int ++grub_xfs_inode_has_large_extent_counts (const struct grub_xfs_inode *inode) ++{ ++ return inode->version >= 3 && ++ (inode->flags2 & grub_cpu_to_be64_compile_time (XFS_DIFLAG2_NREXT64)); ++} ++ ++static grub_uint64_t ++grub_xfs_get_inode_nextents (struct grub_xfs_inode *inode) ++{ ++ return (grub_xfs_inode_has_large_extent_counts (inode)) ? ++ grub_be_to_cpu64 (inode->nextents_big) : ++ grub_be_to_cpu32 (inode->nextents); ++} ++ + static grub_disk_addr_t + grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) + { + struct grub_xfs_btree_node *leaf = 0; +- int ex, nrec; ++ grub_uint64_t ex, nrec; + struct grub_xfs_extent *exts; + grub_uint64_t ret = 0; + +@@ -574,7 +594,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) + / (2 * sizeof (grub_uint64_t)); + do + { +- int i; ++ grub_uint64_t i; + + for (i = 0; i < nrec; i++) + { +@@ -621,7 +641,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) + grub_addr_t exts_end = 0; + grub_addr_t data_end = 0; + +- nrec = grub_be_to_cpu32 (node->inode.nextents); ++ nrec = grub_xfs_get_inode_nextents (&node->inode); + exts = (struct grub_xfs_extent *) grub_xfs_inode_data(&node->inode); + + if (grub_mul (sizeof (struct grub_xfs_extent), nrec, &exts_end) || +-- +2.42.1 + diff --git a/0003-grub-install-support-prep-environment-block.patch b/0003-grub-install-support-prep-environment-block.patch new file mode 100644 index 0000000..60c7bcd --- /dev/null +++ b/0003-grub-install-support-prep-environment-block.patch @@ -0,0 +1,137 @@ +From c31fc5aa0ded9ce1e774d0a3526cfee19be1b77f Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Mon, 7 Feb 2022 20:49:01 +0800 +Subject: [PATCH 3/5] grub-install: support prep environment block + +The grub-install can be instructed to create environment block at end of +PReP paritition with probed device identities and properties in +variables to facilitate root device discovery. So far these variables +are defined for this purpose: + +ENV_FS_UUID - The filesystem uuid for the grub root device +ENV_CRYPTO_UUID - The crytodisk uuid for the grub root device separated +by space +ENV_GRUB_DIR - The path to grub prefix directory +ENV_HINT - The recommended hint string for searching root device + +The size of environment block is defined in GRUB_ENVBLK_PREP_SIZE which +is 4096 bytes and can be extended in the future. + +v2: Improve detection of ENV_CRYPTO_UUID by traversing all members of +the logical disk and utilize a space as a separator when multiple UUIDs +are found (bsc#1216075). + +Signed-off-by: Michael Chang +--- + include/grub/lib/envblk.h | 3 +++ + util/grub-install.c | 38 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 41 insertions(+) + +--- a/include/grub/lib/envblk.h ++++ b/include/grub/lib/envblk.h +@@ -24,6 +24,9 @@ + + #ifndef ASM_FILE + ++#include ++#define GRUB_ENVBLK_PREP_SIZE (GRUB_DISK_SECTOR_SIZE << 3) ++ + struct grub_envblk + { + char *buf; +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -43,6 +43,7 @@ + #include + #include + #include ++#include + + #include + +@@ -609,6 +610,41 @@ + } + } + ++static char * ++cryptodisk_uuids (grub_disk_t disk, int in_recurse) ++{ ++ grub_disk_memberlist_t list = NULL, tmp; ++ static char *ret; ++ ++ if (!in_recurse) ++ ret = NULL; ++ ++ if (disk->dev->disk_memberlist) ++ list = disk->dev->disk_memberlist (disk); ++ ++ while (list) ++ { ++ ret = cryptodisk_uuids (list->disk, 1); ++ tmp = list->next; ++ free (list); ++ list = tmp; ++ } ++ ++ if (disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) ++ { ++ if (!ret) ++ ret = grub_strdup (grub_util_cryptodisk_get_uuid (disk)); ++ else ++ { ++ char *s = grub_xasprintf ("%s %s", grub_util_cryptodisk_get_uuid (disk), ret); ++ grub_free (ret); ++ ret = s; ++ } ++ } ++ ++ return ret; ++} ++ + static int + is_same_disk (const char *a, const char *b) + { +@@ -2138,6 +2174,43 @@ + if (write_to_disk (ins_dev, imgfile)) + grub_util_error ("%s", _("failed to copy Grub to the PReP partition")); + grub_set_install_backup_ponr (); ++ ++ if ((signed_grub_mode >= SIGNED_GRUB_FORCE) || ((signed_grub_mode == SIGNED_GRUB_AUTO) && (ppc_sb_state > 0))) ++ { ++ char *uuid = NULL; ++ grub_envblk_t envblk = NULL; ++ char *buf; ++ char *cryptouuid = NULL; ++ ++ if (grub_dev->disk) ++ cryptouuid = cryptodisk_uuids (grub_dev->disk, 0); ++ ++ if (grub_fs->fs_uuid && grub_fs->fs_uuid (grub_dev, &uuid)) ++ { ++ grub_print_error (); ++ grub_errno = 0; ++ uuid = NULL; ++ } ++ buf = grub_envblk_buf (GRUB_ENVBLK_PREP_SIZE); ++ envblk = grub_envblk_open (buf, GRUB_ENVBLK_PREP_SIZE); ++ if (uuid) ++ grub_envblk_set (envblk, "ENV_FS_UUID", uuid); ++ if (cryptouuid) ++ grub_envblk_set (envblk, "ENV_CRYPTO_UUID", cryptouuid); ++ if (relative_grubdir) ++ grub_envblk_set (envblk, "ENV_GRUB_DIR", relative_grubdir); ++ if (have_abstractions) ++ grub_envblk_set (envblk, "ENV_HINT", grub_dev->disk->name); ++ if (use_relative_path_on_btrfs) ++ grub_envblk_set (envblk, "btrfs_relative_path", "1"); ++ if (envblk) ++ { ++ fprintf (stderr, _("Write environment block to PReP.\n")); ++ if (grub_disk_write_tail (ins_dev->disk, envblk->size, envblk->buf)) ++ grub_util_error ("%s", _("failed to write environment block to the PReP partition")); ++ } ++ grub_envblk_close (envblk); ++ } + grub_device_close (ins_dev); + if (update_nvram) + grub_install_register_ieee1275 (1, grub_util_get_os_disk (install_device), diff --git a/0003-ieee1275-change-the-logic-of-ieee1275_get_devargs.patch b/0003-ieee1275-change-the-logic-of-ieee1275_get_devargs.patch new file mode 100644 index 0000000..bc89763 --- /dev/null +++ b/0003-ieee1275-change-the-logic-of-ieee1275_get_devargs.patch @@ -0,0 +1,62 @@ +From 1729400ab816804a28ebf50cb1310607b2c4b75e Mon Sep 17 00:00:00 2001 +From: Diego Domingos +Date: Fri, 25 Feb 2022 12:49:51 -0500 +Subject: [PATCH 3/4] ieee1275: change the logic of ieee1275_get_devargs() + +Usually grub will parse the PFW arguments by searching for the first occurence of the character ':'. +However, we can have this char more than once on NQN. +This patch changes the logic to find the last occurence of this char so we can get the proper values +for NVMeoFC +--- + grub-core/kern/ieee1275/openfw.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c +index f819bd106..655a71310 100644 +--- a/grub-core/kern/ieee1275/openfw.c ++++ b/grub-core/kern/ieee1275/openfw.c +@@ -354,6 +354,13 @@ static char * + grub_ieee1275_get_devargs (const char *path) + { + char *colon = grub_strchr (path, ':'); ++ char *colon_check = colon; ++ ++ /* Find the last occurence of colon */ ++ while(colon_check){ ++ colon = colon_check; ++ colon_check = grub_strchr (colon+1, ':'); ++ } + + if (! colon) + return 0; +@@ -368,6 +375,18 @@ grub_ieee1275_get_devname (const char *path) + char *colon = grub_strchr (path, ':'); + int pathlen = grub_strlen (path); + struct grub_ieee1275_devalias curalias; ++ ++ /* Check some special cases */ ++ if(grub_strstr(path, "nvme-of")){ ++ char *namespace_split = grub_strstr(path,"/namespace@"); ++ if(namespace_split){ ++ colon = grub_strchr (namespace_split, ':'); ++ } else { ++ colon = NULL; ++ } ++ ++ } ++ + if (colon) + pathlen = (int)(colon - path); + +@@ -693,7 +712,7 @@ grub_ieee1275_get_boot_dev (void) + return NULL; + } + +- bootpath = (char *) grub_malloc ((grub_size_t) bootpath_size + 64); ++ bootpath = (char *) grub_malloc ((grub_size_t) bootpath_size + 64 + 256); + if (! bootpath) + { + grub_print_error (); +-- +2.35.3 + diff --git a/0003-protectors-Add-TPM2-Key-Protector.patch b/0003-protectors-Add-TPM2-Key-Protector.patch new file mode 100644 index 0000000..e5e4015 --- /dev/null +++ b/0003-protectors-Add-TPM2-Key-Protector.patch @@ -0,0 +1,2052 @@ +From 2a63876ca714d177f919b2392d8efa0e3bd3ebe2 Mon Sep 17 00:00:00 2001 +From: Hernan Gatta +Date: Tue, 1 Feb 2022 05:02:55 -0800 +Subject: [PATCH v7 10/20] protectors: Add TPM2 Key Protector + +The TPM2 key protector is a module that enables the automatic retrieval +of a fully-encrypted disk's unlocking key from a TPM 2.0. + +The theory of operation is such that the module accepts various +arguments, most of which are optional and therefore possess reasonable +defaults. One of these arguments is the keyfile/tpm2key parameter, which +is mandatory. There are two supported key formats: + +1. Raw Sealed Key (--keyfile) + When sealing a key with TPM2_Create, the public portion of the sealed + key is stored in TPM2B_PUBLIC, and the private portion is in + TPM2B_PRIVATE. The raw sealed key glues the fully marshalled + TPM2B_PUBLIC and TPM2B_PRIVATE into one file. + +2. TPM 2.0 Key (--tpm2key) + The following is the ASN.1 definition of TPM 2.0 Key File: + + TPMPolicy ::= SEQUENCE { + CommandCode [0] EXPLICIT INTEGER + CommandPolicy [1] EXPLICIT OCTET STRING + } + + TPMAuthPolicy ::= SEQUENCE { + Name [0] EXPLICIT UTF8STRING OPTIONAL + Policy [1] EXPLICIT SEQUENCE OF TPMPolicy + } + + TPMKey ::= SEQUENCE { + type OBJECT IDENTIFIER + emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL + policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL + secret [2] EXPLICIT OCTET STRING OPTIONAL + authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL + parent INTEGER + pubkey OCTET STRING + privkey OCTET STRING + } + + The TPM2 key protector only expects a "sealed" key in DER encoding, + so 'type' is always 2.23.133.10.1.5, 'emptyAuth' is 'TRUE', and + 'secret' is empty. 'policy' and 'authPolicy' are the possible policy + command sequences to construst the policy digest to unseal the key. + Similar to the raw sealed key, the public portion (TPM2B_PUBLIC) of + the sealed key is stored in 'pubkey', and the private portion + (TPM2B_PRIVATE) is in 'privkey'. + + For more details: https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html + +This sealed key file is created via the grub-protect tool. The tool +utilizes the TPM's sealing functionality to seal (i.e., encrypt) an +unlocking key using a Storage Root Key (SRK) to the values of various +Platform Configuration Registers (PCRs). These PCRs reflect the state +of the system as it boots. If the values are as expected, the system +may be considered trustworthy, at which point the TPM allows for a +caller to utilize the private component of the SRK to unseal (i.e., +decrypt) the sealed key file. The caller, in this case, is this key +protector. + +The TPM2 key protector registers two commands: + +- tpm2_key_protector_init: Initializes the state of the TPM2 key + protector for later usage, clearing any + previous state, too, if any. + +- tpm2_key_protector_clear: Clears any state set by tpm2_key_protector_init. + +The way this is expected to be used requires the user to, either +interactively or, normally, via a boot script, initialize/configure +the key protector and then specify that it be used by the 'cryptomount' +command (modifications to this command are in a different patch). + +For instance, to unseal the raw sealed key file: + +tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub2/sealed-1.key +cryptomount -u -P tpm2 + +tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub2/sealed-2.key --pcrs=7,11 +cryptomount -u -P tpm2 + +Or, to unseal the TPM 2.0 Key file: + +tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub2/sealed-1.tpm +cryptomount -u -P tpm2 + +tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub2/sealed-2.tpm --pcrs=7,11 +cryptomount -u -P tpm2 + +If a user does not initialize the key protector and attempts to use it +anyway, the protector returns an error. + +Before unsealing the key, the TPM2 key protector follows the "TPMPolicy" +sequences to enforce the TPM policy commands to construct a valid policy +digest to unseal the key. + +For the TPM 2.0 Key files, 'authPolicy' may contain multiple "TPMPolicy" +sequences, the TPM2 key protector iterates 'authPolicy' to find a valid +sequence to unseal key. If 'authPolicy' is empty or all sequences in +'authPolicy' fail, the protector tries the one from 'policy'. In case +'policy' is also empty, the protector creates a "TPMPolicy" sequence +based on the given PCR selection. + +For the raw sealed key, the TPM2 key protector treats the key file as a +TPM 2.0 Key file without 'authPolicy' and 'policy', so the "TPMPolicy" +sequence is always based on the PCR selection from the command +parameters. + +Currently, there is only one supported policy command: TPM2_PolicyPCR. +The command set can be extended to support advanced features, such as +authorized policy, in the future. + +Signed-off-by: Hernan Gatta +Signed-off-by: Gary Lin +--- + grub-core/Makefile.core.def | 13 + + grub-core/tpm2/args.c | 177 +++++ + grub-core/tpm2/module.c | 1028 +++++++++++++++++++++++++++++ + grub-core/tpm2/tpm2key.asn | 31 + + grub-core/tpm2/tpm2key.c | 447 +++++++++++++ + grub-core/tpm2/tpm2key_asn1_tab.c | 41 ++ + include/grub/tpm2/internal/args.h | 41 ++ + include/grub/tpm2/tpm2key.h | 83 +++ + 8 files changed, 1861 insertions(+) + create mode 100644 grub-core/tpm2/args.c + create mode 100644 grub-core/tpm2/module.c + create mode 100644 grub-core/tpm2/tpm2key.asn + create mode 100644 grub-core/tpm2/tpm2key.c + create mode 100644 grub-core/tpm2/tpm2key_asn1_tab.c + create mode 100644 include/grub/tpm2/internal/args.h + create mode 100644 include/grub/tpm2/tpm2key.h + +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 5831d4265..38571119e 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2553,6 +2553,19 @@ module = { + enable = efi; + }; + ++module = { ++ name = tpm2; ++ common = tpm2/args.c; ++ common = tpm2/buffer.c; ++ common = tpm2/module.c; ++ common = tpm2/mu.c; ++ common = tpm2/tpm2.c; ++ common = tpm2/tpm2key.c; ++ common = tpm2/tpm2key_asn1_tab.c; ++ efi = tpm2/tcg2.c; ++ enable = efi; ++}; ++ + module = { + name = tr; + common = commands/tr.c; +diff --git a/grub-core/tpm2/args.c b/grub-core/tpm2/args.c +new file mode 100644 +index 000000000..274f4fef0 +--- /dev/null ++++ b/grub-core/tpm2/args.c +@@ -0,0 +1,177 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++ ++grub_err_t ++grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs, ++ grub_uint8_t *pcr_count) ++{ ++ char *current_pcr = value; ++ char *next_pcr; ++ unsigned long pcr; ++ grub_uint8_t i; ++ ++ if (grub_strlen (value) == 0) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ *pcr_count = 0; ++ for (i = 0; i < TPM_MAX_PCRS; i++) ++ { ++ next_pcr = grub_strchr (current_pcr, ','); ++ if (next_pcr == current_pcr) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Empty entry in PCR list")); ++ if (next_pcr) ++ *next_pcr = '\0'; ++ ++ grub_errno = GRUB_ERR_NONE; ++ pcr = grub_strtoul (current_pcr, NULL, 10); ++ if (grub_errno != GRUB_ERR_NONE) ++ return grub_error (grub_errno, ++ N_("Entry '%s' in PCR list is not a number"), ++ current_pcr); ++ ++ if (pcr > TPM_MAX_PCRS) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, ++ N_("Entry %lu in PCR list is too large to be a PCR " ++ "number, PCR numbers range from 0 to %u"), ++ pcr, TPM_MAX_PCRS); ++ ++ pcrs[i] = (grub_uint8_t)pcr; ++ *pcr_count += 1; ++ ++ if (next_pcr == NULL) ++ break; ++ ++ current_pcr = next_pcr + 1; ++ if (*current_pcr == '\0') ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Trailing comma at the end of PCR list")); ++ } ++ ++ if (i == TPM_MAX_PCRS) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, ++ N_("Too many PCRs in PCR list, the maximum number of " ++ "PCRs is %u"), TPM_MAX_PCRS); ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_tpm2_protector_parse_asymmetric (const char *value, ++ TPM_ALG_ID *asymmetric, ++ TPM_KEY_BITS *rsa_bits, ++ TPM_ECC_CURVE *ecc_curve) ++{ ++ if (grub_strcasecmp (value, "ECC") == 0 || ++ grub_strcasecmp (value, "ECC_NIST_P256") == 0) ++ { ++ *asymmetric = TPM_ALG_ECC; ++ *ecc_curve = TPM_ECC_NIST_P256; ++ } ++ else if (grub_strcasecmp (value, "RSA") == 0 || ++ grub_strcasecmp (value, "RSA2048") == 0) ++ { ++ *asymmetric = TPM_ALG_RSA; ++ *rsa_bits = 2048; ++ } ++ else if (grub_strcasecmp (value, "RSA3072") == 0) ++ { ++ *asymmetric = TPM_ALG_RSA; ++ *rsa_bits = 3072; ++ } ++ else if (grub_strcasecmp (value, "RSA4096") == 0) ++ { ++ *asymmetric = TPM_ALG_RSA; ++ *rsa_bits = 4096; ++ } ++ else if (grub_strcasecmp (value, "ECC_NIST_P384") == 0) ++ { ++ *asymmetric = TPM_ALG_ECC; ++ *ecc_curve = TPM_ECC_NIST_P384; ++ } ++ else if (grub_strcasecmp (value, "ECC_NIST_P521") == 0) ++ { ++ *asymmetric = TPM_ALG_ECC; ++ *ecc_curve = TPM_ECC_NIST_P521; ++ } ++ else if (grub_strcasecmp (value, "ECC_BN_P256") == 0) ++ { ++ *asymmetric = TPM_ALG_ECC; ++ *ecc_curve = TPM_ECC_BN_P256; ++ } ++ else if (grub_strcasecmp (value, "ECC_BN_P638") == 0) ++ { ++ *asymmetric = TPM_ALG_ECC; ++ *ecc_curve = TPM_ECC_BN_P638; ++ } ++ else if (grub_strcasecmp (value, "ECC_SM2_P256") == 0) ++ { ++ *asymmetric = TPM_ALG_ECC; ++ *ecc_curve = TPM_ECC_SM2_P256; ++ } ++ else ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, ++ N_("Value '%s' is not a valid asymmetric key type"), ++ value); ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID *bank) ++{ ++ if (grub_strcasecmp (value, "SHA1") == 0) ++ *bank = TPM_ALG_SHA1; ++ else if (grub_strcasecmp (value, "SHA256") == 0) ++ *bank = TPM_ALG_SHA256; ++ else if (grub_strcasecmp (value, "SHA384") == 0) ++ *bank = TPM_ALG_SHA384; ++ else if (grub_strcasecmp (value, "SHA512") == 0) ++ *bank = TPM_ALG_SHA512; ++ else ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, ++ N_("Value '%s' is not a valid PCR bank"), value); ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE *handle) ++{ ++ unsigned long num; ++ ++ grub_errno = GRUB_ERR_NONE; ++ num = grub_strtoul (value, NULL, 0); ++ if (grub_errno != GRUB_ERR_NONE) ++ return grub_error (grub_errno, N_("TPM handle value '%s' is not a number"), ++ value); ++ ++ if (num > GRUB_UINT_MAX) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, ++ N_("Value %lu is too large to be a TPM handle, TPM " ++ "handles are unsigned 32-bit integers"), num); ++ ++ *handle = (TPM_HANDLE)num; ++ ++ return GRUB_ERR_NONE; ++} +diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c +new file mode 100644 +index 000000000..df0727215 +--- /dev/null ++++ b/grub-core/tpm2/module.c +@@ -0,0 +1,1028 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++typedef enum grub_tpm2_protector_mode ++{ ++ GRUB_TPM2_PROTECTOR_MODE_UNSET, ++ GRUB_TPM2_PROTECTOR_MODE_SRK, ++ GRUB_TPM2_PROTECTOR_MODE_NV ++} grub_tpm2_protector_mode_t; ++ ++enum grub_tpm2_protector_options ++{ ++ OPTION_MODE, ++ OPTION_PCRS, ++ OPTION_BANK, ++ OPTION_TPM2KEY, ++ OPTION_KEYFILE, ++ OPTION_SRK, ++ OPTION_ASYMMETRIC, ++ OPTION_NVINDEX ++}; ++ ++struct grub_tpm2_protector_context ++{ ++ grub_tpm2_protector_mode_t mode; ++ grub_uint8_t pcrs[TPM_MAX_PCRS]; ++ grub_uint8_t pcr_count; ++ TPM_ALG_ID asymmetric; ++ TPM_KEY_BITS rsa_bits; ++ TPM_ECC_CURVE ecc_curve; ++ TPM_ALG_ID bank; ++ const char *tpm2key; ++ const char *keyfile; ++ TPM_HANDLE srk; ++ TPM_HANDLE nv; ++}; ++ ++static const struct grub_arg_option grub_tpm2_protector_init_cmd_options[] = ++ { ++ /* Options for all modes */ ++ { ++ .longarg = "mode", ++ .shortarg = 'm', ++ .flags = 0, ++ .arg = NULL, ++ .type = ARG_TYPE_STRING, ++ .doc = ++ N_("Unseal key using SRK ('srk') (default) or retrieve it from an NV " ++ "Index ('nv')."), ++ }, ++ { ++ .longarg = "pcrs", ++ .shortarg = 'p', ++ .flags = 0, ++ .arg = NULL, ++ .type = ARG_TYPE_STRING, ++ .doc = ++ N_("Comma-separated list of PCRs used to authorize key release " ++ "(e.g., '7,11', default is 7."), ++ }, ++ { ++ .longarg = "bank", ++ .shortarg = 'b', ++ .flags = 0, ++ .arg = NULL, ++ .type = ARG_TYPE_STRING, ++ .doc = ++ N_("Bank of PCRs used to authorize key release: " ++ "SHA1, SHA256 (default), SHA384 or SHA512."), ++ }, ++ /* SRK-mode options */ ++ { ++ .longarg = "tpm2key", ++ .shortarg = 'T', ++ .flags = 0, ++ .arg = NULL, ++ .type = ARG_TYPE_STRING, ++ .doc = ++ N_("Required in SRK mode, path to the key file in TPM 2.0 Key File Format " ++ "to unseal using the TPM (e.g., (hd0,gpt1)/boot/grub2/secret.tpm)."), ++ }, ++ { ++ .longarg = "keyfile", ++ .shortarg = 'k', ++ .flags = 0, ++ .arg = NULL, ++ .type = ARG_TYPE_STRING, ++ .doc = ++ N_("Required in SRK mode, path to the sealed key file to unseal using " ++ "the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed_key). " ++ "Use '-tpm2key' instead"), ++ }, ++ { ++ .longarg = "srk", ++ .shortarg = 's', ++ .flags = 0, ++ .arg = NULL, ++ .type = ARG_TYPE_STRING, ++ .doc = ++ N_("In SRK mode, the SRK handle if the SRK is persistent."), ++ }, ++ { ++ .longarg = "asymmetric", ++ .shortarg = 'a', ++ .flags = 0, ++ .arg = NULL, ++ .type = ARG_TYPE_STRING, ++ .doc = ++ N_("In SRK mode, the type of SRK: RSA (RSA2048), RSA3072, " ++ "RSA4096, ECC (ECC_NIST_P256), ECC_NIST_P384, " ++ "ECC_NIST_P521, ECC_BN_P256, ECC_BN_P638, and ECC_SM2_P256. " ++ "(default is RSA2048)"), ++ }, ++ /* NV Index-mode options */ ++ { ++ .longarg = "nvindex", ++ .shortarg = 'n', ++ .flags = 0, ++ .arg = NULL, ++ .type = ARG_TYPE_STRING, ++ .doc = ++ N_("Required in NV Index mode, the NV handle to read which must " ++ "readily exist on the TPM and which contains the key."), ++ }, ++ /* End of list */ ++ {0, 0, 0, 0, 0, 0} ++ }; ++ ++static grub_extcmd_t grub_tpm2_protector_init_cmd; ++static grub_extcmd_t grub_tpm2_protector_clear_cmd; ++static struct grub_tpm2_protector_context grub_tpm2_protector_ctx = { 0 }; ++ ++static grub_err_t ++grub_tpm2_protector_srk_read_file (const char *filepath, void **buffer, ++ grub_size_t *buffer_size) ++{ ++ grub_file_t file; ++ grub_off_t file_size; ++ void *read_buffer; ++ grub_off_t read_n; ++ grub_err_t err; ++ ++ /* Using GRUB_FILE_TYPE_SIGNATURE ensures we do not hash the keyfile into PCR9 ++ * otherwise we'll never be able to predict the value of PCR9 at unseal time */ ++ file = grub_file_open (filepath, GRUB_FILE_TYPE_SIGNATURE); ++ if (file == NULL) ++ { ++ /* Push errno from grub_file_open() into the error message stack */ ++ grub_error_push(); ++ err = grub_error (GRUB_ERR_FILE_NOT_FOUND, ++ N_("Could not open file: %s\n"), ++ filepath); ++ goto error; ++ } ++ ++ file_size = grub_file_size (file); ++ if (file_size == 0) ++ { ++ err = grub_error (GRUB_ERR_OUT_OF_RANGE, ++ N_("Could not read file size: %s"), ++ filepath); ++ goto error; ++ } ++ ++ read_buffer = grub_malloc (file_size); ++ if (read_buffer == NULL) ++ { ++ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ N_("Could not allocate buffer for %s"), ++ filepath); ++ goto error; ++ } ++ ++ read_n = grub_file_read (file, read_buffer, file_size); ++ if (read_n != file_size) ++ { ++ grub_free (read_buffer); ++ err = grub_error (GRUB_ERR_FILE_READ_ERROR, ++ N_("Could not retrieve file contents: %s"), ++ filepath); ++ goto error; ++ } ++ ++ *buffer = read_buffer; ++ *buffer_size = file_size; ++ ++ err = GRUB_ERR_NONE; ++ ++error: ++ grub_file_close (file); ++ ++ return err; ++} ++ ++static grub_err_t ++grub_tpm2_protector_srk_unmarshal_keyfile (void *sealed_key, ++ grub_size_t sealed_key_size, ++ TPM2_SEALED_KEY *sk) ++{ ++ struct grub_tpm2_buffer buf; ++ ++ grub_tpm2_buffer_init (&buf); ++ if (sealed_key_size > buf.cap) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Sealed key larger than %" PRIuGRUB_SIZE " bytes"), ++ buf.cap); ++ ++ grub_memcpy (buf.data, sealed_key, sealed_key_size); ++ buf.size = sealed_key_size; ++ ++ grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public); ++ grub_tpm2_mu_TPM2B_Unmarshal (&buf, (TPM2B *)&sk->private); ++ ++ if (buf.error) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Malformed TPM wire key file")); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_tpm2_protector_srk_unmarshal_tpm2key (void *sealed_key, ++ grub_size_t sealed_key_size, ++ tpm2key_policy_t *policy_seq, ++ tpm2key_authpolicy_t *authpol_seq, ++ grub_uint32_t *parent, ++ TPM2_SEALED_KEY *sk) ++{ ++ asn1_node tpm2key = NULL; ++ grub_uint32_t parent_tmp; ++ void *sealed_pub = NULL; ++ grub_size_t sealed_pub_size; ++ void *sealed_priv = NULL; ++ grub_size_t sealed_priv_size; ++ struct grub_tpm2_buffer buf; ++ grub_err_t err; ++ ++ /* ++ * Start to parse the tpm2key file ++ * TPMKey ::= SEQUENCE { ++ * type OBJECT IDENTIFIER, ++ * emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, ++ * policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL, ++ * secret [2] EXPLICIT OCTET STRING OPTIONAL, ++ * authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL, ++ * parent INTEGER, ++ * pubkey OCTET STRING, ++ * privkey OCTET STRING ++ * } ++ */ ++ err = grub_tpm2key_start_parsing (&tpm2key, sealed_key, sealed_key_size); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ ++ /* ++ * Retrieve the policy sequence from 'policy' ++ * policy_seq will be NULL when 'policy' is not available ++ */ ++ err = grub_tpm2key_get_policy_seq (tpm2key, policy_seq); ++ if (err != GRUB_ERR_NONE) ++ goto error; ++ ++ /* ++ * Retrieve the authpolicy sequence from 'authPolicy' ++ * authpol_seq will be NULL when 'authPolicy' is not available ++ */ ++ err = grub_tpm2key_get_authpolicy_seq (tpm2key, authpol_seq); ++ if (err != GRUB_ERR_NONE) ++ goto error; ++ ++ /* Retrieve the parent handle */ ++ err = grub_tpm2key_get_parent (tpm2key, &parent_tmp); ++ if (err != GRUB_ERR_NONE) ++ goto error; ++ *parent = parent_tmp; ++ ++ /* Retrieve the public part of the sealed key */ ++ err = grub_tpm2key_get_pubkey (tpm2key, &sealed_pub, &sealed_pub_size); ++ if (err != GRUB_ERR_NONE) ++ goto error; ++ ++ /* Retrieve the private part of the sealed key */ ++ err = grub_tpm2key_get_privkey (tpm2key, &sealed_priv, &sealed_priv_size); ++ if (err != GRUB_ERR_NONE) ++ goto error; ++ ++ /* Unmarshal the sealed key */ ++ grub_tpm2_buffer_init (&buf); ++ if (sealed_pub_size + sealed_priv_size > buf.cap) ++ { ++ err = grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Sealed key larger than %" PRIuGRUB_SIZE " bytes"), ++ buf.cap); ++ goto error; ++ } ++ ++ grub_tpm2_buffer_pack (&buf, sealed_pub, sealed_pub_size); ++ grub_tpm2_buffer_pack (&buf, sealed_priv, sealed_priv_size); ++ ++ buf.offset = 0; ++ ++ grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public); ++ grub_tpm2_mu_TPM2B_Unmarshal (&buf, (TPM2B *)&sk->private); ++ ++ if (buf.error) ++ { ++ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Malformed TPM 2.0 key file")); ++ goto error; ++ } ++ ++ err = GRUB_ERR_NONE; ++ ++error: ++ /* End the parsing */ ++ grub_tpm2key_end_parsing (tpm2key); ++ grub_free (sealed_pub); ++ grub_free (sealed_priv); ++ ++ return err; ++} ++ ++static grub_err_t ++grub_tpm2_protector_srk_get (const struct grub_tpm2_protector_context *ctx, ++ TPM_HANDLE parent, TPM_HANDLE *srk) ++{ ++ TPM_RC rc; ++ TPM2B_PUBLIC public; ++ TPMS_AUTH_COMMAND authCommand = { 0 }; ++ TPM2B_SENSITIVE_CREATE inSensitive = { 0 }; ++ TPM2B_PUBLIC inPublic = { 0 }; ++ TPM2B_DATA outsideInfo = { 0 }; ++ TPML_PCR_SELECTION creationPcr = { 0 }; ++ TPM2B_PUBLIC outPublic = { 0 }; ++ TPM2B_CREATION_DATA creationData = { 0 }; ++ TPM2B_DIGEST creationHash = { 0 }; ++ TPMT_TK_CREATION creationTicket = { 0 }; ++ TPM2B_NAME srkName = { 0 }; ++ TPM_HANDLE srkHandle; ++ ++ if (ctx->srk != 0) ++ { ++ /* Find SRK */ ++ rc = TPM2_ReadPublic (ctx->srk, NULL, &public); ++ if (rc == TPM_RC_SUCCESS) ++ { ++ *srk = ctx->srk; ++ return GRUB_ERR_NONE; ++ } ++ ++ return grub_error (GRUB_ERR_BAD_DEVICE, ++ N_("Failed to retrieve SRK (TPM2_ReadPublic: 0x%x)"), ++ rc); ++ } ++ ++ /* Create SRK */ ++ authCommand.sessionHandle = TPM_RS_PW; ++ inPublic.publicArea.type = ctx->asymmetric; ++ inPublic.publicArea.nameAlg = TPM_ALG_SHA256; ++ inPublic.publicArea.objectAttributes.restricted = 1; ++ inPublic.publicArea.objectAttributes.userWithAuth = 1; ++ inPublic.publicArea.objectAttributes.decrypt = 1; ++ inPublic.publicArea.objectAttributes.fixedTPM = 1; ++ inPublic.publicArea.objectAttributes.fixedParent = 1; ++ inPublic.publicArea.objectAttributes.sensitiveDataOrigin = 1; ++ inPublic.publicArea.objectAttributes.noDA = 1; ++ ++ if (ctx->asymmetric == TPM_ALG_RSA) ++ { ++ inPublic.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES; ++ inPublic.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128; ++ inPublic.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB; ++ inPublic.publicArea.parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL; ++ inPublic.publicArea.parameters.rsaDetail.keyBits = ctx->rsa_bits; ++ inPublic.publicArea.parameters.rsaDetail.exponent = 0; ++ } ++ else if (ctx->asymmetric == TPM_ALG_ECC) ++ { ++ inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES; ++ inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128; ++ inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB; ++ inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL; ++ inPublic.publicArea.parameters.eccDetail.curveID = ctx->ecc_curve; ++ inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL; ++ } ++ else ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown SRK algorithm")); ++ ++ rc = TPM2_CreatePrimary (parent, &authCommand, &inSensitive, &inPublic, ++ &outsideInfo, &creationPcr, &srkHandle, &outPublic, ++ &creationData, &creationHash, &creationTicket, ++ &srkName, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ return grub_error (GRUB_ERR_BAD_DEVICE, ++ N_("Could not create SRK (TPM2_CreatePrimary: 0x%x)"), ++ rc); ++ ++ *srk = srkHandle; ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION session, ++ struct grub_tpm2_buffer *cmd_buf) ++{ ++ TPM2B_DIGEST pcr_digest; ++ TPML_PCR_SELECTION pcr_sel; ++ TPM_RC rc; ++ ++ grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (cmd_buf, &pcr_digest); ++ grub_tpm2_mu_TPML_PCR_SELECTION_Unmarshal (cmd_buf, &pcr_sel); ++ if (cmd_buf->error) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Failed to unmarshal CommandPolicy for TPM2_PolicyPCR")); ++ ++ rc = TPM2_PolicyPCR (session, NULL, &pcr_digest, &pcr_sel, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ return grub_error (GRUB_ERR_BAD_DEVICE, ++ N_("Failed to submit PCR policy (TPM2_PolicyPCR: 0x%x)"), ++ rc); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_tpm2_protector_enforce_policy (tpm2key_policy_t policy, TPMI_SH_AUTH_SESSION session) ++{ ++ struct grub_tpm2_buffer buf; ++ grub_err_t err; ++ ++ grub_tpm2_buffer_init (&buf); ++ if (policy->cmd_policy_len > buf.cap) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("CommandPolicy larger than TPM buffer")); ++ ++ grub_memcpy (buf.data, policy->cmd_policy, policy->cmd_policy_len); ++ buf.size = policy->cmd_policy_len; ++ ++ switch (policy->cmd_code) ++ { ++ case TPM_CC_PolicyPCR: ++ err = grub_tpm2_protector_policypcr (session, &buf); ++ break; ++ default: ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Unknown TPM Command: 0x%x"), policy->cmd_code); ++ } ++ ++ return err; ++} ++ ++static grub_err_t ++grub_tpm2_protector_enforce_policy_seq (tpm2key_policy_t policy_seq, ++ TPMI_SH_AUTH_SESSION session) ++{ ++ tpm2key_policy_t policy; ++ grub_err_t err; ++ ++ FOR_LIST_ELEMENTS (policy, policy_seq) ++ { ++ err = grub_tpm2_protector_enforce_policy (policy, session); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_tpm2_protector_simple_policy_seq (const struct grub_tpm2_protector_context *ctx, ++ tpm2key_policy_t *policy_seq) ++{ ++ tpm2key_policy_t policy = NULL; ++ struct grub_tpm2_buffer buf; ++ TPML_PCR_SELECTION pcr_sel = { ++ .count = 1, ++ .pcrSelections = { ++ { ++ .hash = ctx->bank, ++ .sizeOfSelect = 3, ++ .pcrSelect = { 0 } ++ }, ++ } ++ }; ++ grub_uint8_t i; ++ grub_err_t err; ++ ++ if (policy_seq == NULL) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ grub_tpm2_buffer_init (&buf); ++ ++ for (i = 0; i < ctx->pcr_count; i++) ++ TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], ctx->pcrs[i]); ++ ++ grub_tpm2_buffer_pack_u16 (&buf, 0); ++ grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (&buf, &pcr_sel); ++ ++ if (buf.error) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ policy = grub_malloc (sizeof(struct tpm2key_policy)); ++ if (policy == NULL) ++ { ++ err = GRUB_ERR_OUT_OF_MEMORY; ++ goto error; ++ } ++ policy->cmd_code = TPM_CC_PolicyPCR; ++ policy->cmd_policy = grub_malloc (buf.size); ++ if (policy->cmd_policy == NULL) ++ { ++ err = GRUB_ERR_OUT_OF_MEMORY; ++ goto error; ++ } ++ grub_memcpy (policy->cmd_policy, buf.data, buf.size); ++ policy->cmd_policy_len = buf.size; ++ ++ grub_list_push (GRUB_AS_LIST_P (policy_seq), GRUB_AS_LIST (policy)); ++ ++ return GRUB_ERR_NONE; ++ ++error: ++ grub_free (policy); ++ ++ return err; ++} ++ ++static grub_err_t ++grub_tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE sealed_handle, ++ grub_uint8_t **key, grub_size_t *key_size) ++{ ++ TPMS_AUTH_COMMAND authCmd = { 0 }; ++ TPM2B_SENSITIVE_DATA data; ++ TPM2B_NONCE nonceCaller = { 0 }; ++ TPMT_SYM_DEF symmetric = { 0 }; ++ TPMI_SH_AUTH_SESSION session; ++ grub_uint8_t *key_out; ++ TPM_RC rc; ++ grub_err_t err; ++ ++ /* Start Auth Session */ ++ nonceCaller.size = TPM_SHA256_DIGEST_SIZE; ++ symmetric.algorithm = TPM_ALG_NULL; ++ rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, NULL, &nonceCaller, NULL, ++ TPM_SE_POLICY, &symmetric, TPM_ALG_SHA256, ++ &session, NULL, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ return grub_error (GRUB_ERR_BAD_DEVICE, ++ N_("Failed to start auth session (TPM2_StartAuthSession: 0x%x)"), ++ rc); ++ ++ /* Enforce the policy command sequence */ ++ err = grub_tpm2_protector_enforce_policy_seq (policy_seq, session); ++ if (err != GRUB_ERR_NONE) ++ goto error; ++ ++ /* Unseal Sealed Key */ ++ authCmd.sessionHandle = session; ++ rc = TPM2_Unseal (sealed_handle, &authCmd, &data, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ { ++ err = grub_error (GRUB_ERR_BAD_DEVICE, ++ N_("Failed to unseal sealed key (TPM2_Unseal: 0x%x)"), ++ rc); ++ goto error; ++ } ++ ++ /* Epilogue */ ++ key_out = grub_malloc (data.size); ++ if (key_out == NULL) ++ { ++ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ N_("No memory left to allocate unlock key buffer")); ++ goto error; ++ } ++ ++ grub_memcpy (key_out, data.buffer, data.size); ++ ++ *key = key_out; ++ *key_size = data.size; ++ ++ err = GRUB_ERR_NONE; ++ ++error: ++ TPM2_FlushContext (session); ++ ++ return err; ++} ++ ++static grub_err_t ++grub_tpm2_protector_srk_recover (const struct grub_tpm2_protector_context *ctx, ++ grub_uint8_t **key, grub_size_t *key_size) ++{ ++ TPMS_AUTH_COMMAND authCmd = { 0 }; ++ TPM2_SEALED_KEY sealed_key = { 0 }; ++ TPM2B_NAME name = { 0 }; ++ void *file_bytes = NULL; ++ grub_size_t file_size = 0; ++ TPM_HANDLE parent_handle = 0; ++ TPM_HANDLE srk_handle = 0; ++ TPM_HANDLE sealed_handle = 0; ++ tpm2key_policy_t policy_seq = NULL; ++ tpm2key_authpolicy_t authpol = NULL; ++ tpm2key_authpolicy_t authpol_seq = NULL; ++ TPM_RC rc; ++ grub_err_t err; ++ ++ /* ++ * Retrieve sealed key, parent handle, policy sequence, and authpolicy ++ * sequence from the key file ++ */ ++ if (ctx->tpm2key) ++ { ++ err = grub_tpm2_protector_srk_read_file (ctx->tpm2key, &file_bytes, ++ &file_size); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ ++ err = grub_tpm2_protector_srk_unmarshal_tpm2key (file_bytes, ++ file_size, ++ &policy_seq, ++ &authpol_seq, ++ &parent_handle, ++ &sealed_key); ++ if (err != GRUB_ERR_NONE) ++ goto exit1; ++ } ++ else ++ { ++ err = grub_tpm2_protector_srk_read_file (ctx->keyfile, &file_bytes, ++ &file_size); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ ++ parent_handle = TPM_RH_OWNER; ++ err = grub_tpm2_protector_srk_unmarshal_keyfile (file_bytes, ++ file_size, ++ &sealed_key); ++ if (err != GRUB_ERR_NONE) ++ goto exit1; ++ } ++ ++ /* Get the SRK to unseal the sealed key */ ++ err = grub_tpm2_protector_srk_get (ctx, parent_handle, &srk_handle); ++ if (err != GRUB_ERR_NONE) ++ goto exit1; ++ ++ /* Load the sealed key and associate it with the SRK */ ++ authCmd.sessionHandle = TPM_RS_PW; ++ rc = TPM2_Load (srk_handle, &authCmd, &sealed_key.private, &sealed_key.public, ++ &sealed_handle, &name, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ { ++ err = grub_error (GRUB_ERR_BAD_DEVICE, ++ N_("Failed to load sealed key (TPM2_Load: 0x%x)"), ++ rc); ++ goto exit2; ++ } ++ ++ /* ++ * Set err to an error code to trigger the standalone policy sequence ++ * if there is no authpolicy sequence ++ */ ++ err = GRUB_ERR_READ_ERROR; ++ ++ /* Iterate the authpolicy sequence to find one that unseals the key */ ++ FOR_LIST_ELEMENTS (authpol, authpol_seq) ++ { ++ err = grub_tpm2_protector_unseal (authpol->policy_seq, sealed_handle, ++ key, key_size); ++ if (err == GRUB_ERR_NONE) ++ break; ++ ++ /* ++ * Push the error message into the grub_error stack ++ * Note: The grub_error stack may overflow if there are too many policy ++ * sequences. Anyway, we still can keep the error messages from ++ * the first few policy sequences which are usually most likely to ++ * unseal the key. ++ */ ++ grub_error_push(); ++ } ++ ++ /* Give the standalone policy sequence a try */ ++ if (err != GRUB_ERR_NONE) ++ { ++ /* ++ * Create a basic policy sequence based on the given PCR selection if the ++ * key file doesn't provide one ++ */ ++ if (policy_seq == NULL) ++ { ++ err = grub_tpm2_protector_simple_policy_seq (ctx, &policy_seq); ++ if (err != GRUB_ERR_NONE) ++ goto exit3; ++ } ++ ++ err = grub_tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size); ++ } ++ ++ /* Pop error messages on success */ ++ if (err == GRUB_ERR_NONE) ++ while (grub_error_pop ()); ++ ++exit3: ++ TPM2_FlushContext (sealed_handle); ++ ++exit2: ++ TPM2_FlushContext (srk_handle); ++ ++exit1: ++ grub_tpm2key_free_policy_seq (policy_seq); ++ grub_tpm2key_free_authpolicy_seq (authpol_seq); ++ grub_free (file_bytes); ++ return err; ++} ++ ++static grub_err_t ++grub_tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx, ++ grub_uint8_t **key, grub_size_t *key_size) ++{ ++ (void)ctx; ++ (void)key; ++ (void)key_size; ++ ++ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, ++ N_("NV Index mode is not implemented yet")); ++} ++ ++static grub_err_t ++grub_tpm2_protector_recover (const struct grub_tpm2_protector_context *ctx, ++ grub_uint8_t **key, grub_size_t *key_size) ++{ ++ switch (ctx->mode) ++ { ++ case GRUB_TPM2_PROTECTOR_MODE_SRK: ++ return grub_tpm2_protector_srk_recover (ctx, key, key_size); ++ case GRUB_TPM2_PROTECTOR_MODE_NV: ++ return grub_tpm2_protector_nv_recover (ctx, key, key_size); ++ default: ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++} ++ ++static grub_err_t ++grub_tpm2_protector_recover_key (grub_uint8_t **key, grub_size_t *key_size) ++{ ++ /* Expect a call to tpm2_protector_init before anybody tries to use us */ ++ if (grub_tpm2_protector_ctx.mode == GRUB_TPM2_PROTECTOR_MODE_UNSET) ++ return grub_error (GRUB_ERR_INVALID_COMMAND, ++ N_("Cannot use TPM2 key protector without initializing " ++ "it, call tpm2_protector_init first")); ++ ++ if (key == NULL || key_size == NULL) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ return grub_tpm2_protector_recover (&grub_tpm2_protector_ctx, key, key_size); ++} ++ ++static grub_err_t ++grub_tpm2_protector_check_args (struct grub_tpm2_protector_context *ctx) ++{ ++ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_UNSET) ++ ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK; ++ ++ /* Checks for SRK mode */ ++ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && ctx->keyfile == NULL ++ && ctx->tpm2key == NULL) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("In SRK mode, a key file must be specified: " ++ "--tpm2key/-T or --keyfile/-k")); ++ ++ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && ctx->keyfile ++ && ctx->tpm2key) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("In SRK mode, please specify a key file with " ++ "only --tpm2key/-T or --keyfile/-k")); ++ ++ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && ctx->nv) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("In SRK mode, an NV Index cannot be specified")); ++ ++ /* Checks for NV mode */ ++ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->nv == 0) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("In NV Index mode, an NV Index must be specified: " ++ "--nvindex or -n")); ++ ++ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ++ (ctx->tpm2key || ctx->keyfile)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("In NV Index mode, a keyfile cannot be specified")); ++ ++ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->srk) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("In NV Index mode, an SRK cannot be specified")); ++ ++ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->asymmetric) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("In NV Index mode, an asymmetric key type cannot be " ++ "specified")); ++ ++ /* Defaults assignment */ ++ if (ctx->bank == TPM_ALG_ERROR) ++ ctx->bank = TPM_ALG_SHA256; ++ ++ if (ctx->pcr_count == 0) ++ { ++ ctx->pcrs[0] = 7; ++ ctx->pcr_count = 1; ++ } ++ ++ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK) ++ { ++ if (!ctx->asymmetric) ++ { ++ ctx->asymmetric = TPM_ALG_RSA; ++ ctx->rsa_bits = 2048; ++ } ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_tpm2_protector_parse_file (const char *value, const char **file) ++{ ++ if (grub_strlen (value) == 0) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ *file = grub_strdup (value); ++ if (*file == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ N_("No memory to duplicate file path")); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_tpm2_protector_parse_mode (const char *value, ++ grub_tpm2_protector_mode_t *mode) ++{ ++ if (grub_strcmp (value, "srk") == 0) ++ *mode = GRUB_TPM2_PROTECTOR_MODE_SRK; ++ else if (grub_strcmp (value, "nv") == 0) ++ *mode = GRUB_TPM2_PROTECTOR_MODE_NV; ++ else ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, ++ N_("Value '%s' is not a valid TPM2 key protector mode"), ++ value); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_tpm2_protector_init_cmd_handler (grub_extcmd_context_t ctxt, int argc, ++ char **args __attribute__ ((unused))) ++{ ++ struct grub_arg_list *state = ctxt->state; ++ grub_err_t err; ++ ++ if (argc) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("The TPM2 key protector does not accept any " ++ "non-option arguments (i.e., like -o and/or --option " ++ "only)")); ++ ++ grub_free ((void *) grub_tpm2_protector_ctx.keyfile); ++ grub_memset (&grub_tpm2_protector_ctx, 0, sizeof (grub_tpm2_protector_ctx)); ++ ++ if (state[OPTION_MODE].set) /* mode */ ++ { ++ err = grub_tpm2_protector_parse_mode (state[OPTION_MODE].arg, ++ &grub_tpm2_protector_ctx.mode); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ } ++ ++ if (state[OPTION_PCRS].set) /* pcrs */ ++ { ++ err = grub_tpm2_protector_parse_pcrs (state[OPTION_PCRS].arg, ++ grub_tpm2_protector_ctx.pcrs, ++ &grub_tpm2_protector_ctx.pcr_count); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ } ++ ++ if (state[OPTION_BANK].set) /* bank */ ++ { ++ err = grub_tpm2_protector_parse_bank (state[OPTION_BANK].arg, ++ &grub_tpm2_protector_ctx.bank); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ } ++ ++ if (state[OPTION_TPM2KEY].set) /* tpm2key */ ++ { ++ err = grub_tpm2_protector_parse_file (state[OPTION_TPM2KEY].arg, ++ &grub_tpm2_protector_ctx.tpm2key); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ } ++ ++ if (state[OPTION_KEYFILE].set) /* keyfile */ ++ { ++ err = grub_tpm2_protector_parse_file (state[OPTION_KEYFILE].arg, ++ &grub_tpm2_protector_ctx.keyfile); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ } ++ ++ if (state[OPTION_SRK].set) /* srk */ ++ { ++ err = grub_tpm2_protector_parse_tpm_handle (state[OPTION_SRK].arg, ++ &grub_tpm2_protector_ctx.srk); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ } ++ ++ if (state[OPTION_ASYMMETRIC].set) /* asymmetric */ ++ { ++ err = grub_tpm2_protector_parse_asymmetric (state[OPTION_ASYMMETRIC].arg, ++ &grub_tpm2_protector_ctx.asymmetric, ++ &grub_tpm2_protector_ctx.rsa_bits, ++ &grub_tpm2_protector_ctx.ecc_curve); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ } ++ ++ if (state[OPTION_NVINDEX].set) /* nvindex */ ++ { ++ err = grub_tpm2_protector_parse_tpm_handle (state[OPTION_NVINDEX].arg, ++ &grub_tpm2_protector_ctx.nv); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ } ++ ++ err = grub_tpm2_protector_check_args (&grub_tpm2_protector_ctx); ++ ++ /* This command only initializes the protector, so nothing else to do. */ ++ ++ return err; ++} ++ ++static grub_err_t ++grub_tpm2_protector_clear_cmd_handler (grub_extcmd_context_t ctxt __attribute__ ((unused)), ++ int argc, ++ char **args __attribute__ ((unused))) ++{ ++ if (argc) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("tpm2_key_protector_clear accepts no arguments")); ++ ++ grub_free ((void *) grub_tpm2_protector_ctx.keyfile); ++ grub_memset (&grub_tpm2_protector_ctx, 0, sizeof (grub_tpm2_protector_ctx)); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static struct grub_key_protector grub_tpm2_key_protector = ++ { ++ .name = "tpm2", ++ .recover_key = grub_tpm2_protector_recover_key ++ }; ++ ++GRUB_MOD_INIT (tpm2) ++{ ++ grub_tpm2_protector_init_cmd = ++ grub_register_extcmd ("tpm2_key_protector_init", ++ grub_tpm2_protector_init_cmd_handler, 0, ++ N_("[-m mode] " ++ "[-p pcr_list] " ++ "[-b pcr_bank] " ++ "[-T tpm2_key_file_path] " ++ "[-k sealed_key_file_path] " ++ "[-s srk_handle] " ++ "[-a asymmetric_key_type] " ++ "[-n nv_index]"), ++ N_("Initialize the TPM2 key protector."), ++ grub_tpm2_protector_init_cmd_options); ++ grub_tpm2_protector_clear_cmd = ++ grub_register_extcmd ("tpm2_key_protector_clear", ++ grub_tpm2_protector_clear_cmd_handler, 0, NULL, ++ N_("Clear the TPM2 key protector if previously initialized."), ++ NULL); ++ grub_key_protector_register (&grub_tpm2_key_protector); ++} ++ ++GRUB_MOD_FINI (tpm2) ++{ ++ grub_free ((void *) grub_tpm2_protector_ctx.keyfile); ++ grub_memset (&grub_tpm2_protector_ctx, 0, sizeof (grub_tpm2_protector_ctx)); ++ ++ grub_key_protector_unregister (&grub_tpm2_key_protector); ++ grub_unregister_extcmd (grub_tpm2_protector_clear_cmd); ++ grub_unregister_extcmd (grub_tpm2_protector_init_cmd); ++} +diff --git a/grub-core/tpm2/tpm2key.asn b/grub-core/tpm2/tpm2key.asn +new file mode 100644 +index 000000000..e3b6a03e0 +--- /dev/null ++++ b/grub-core/tpm2/tpm2key.asn +@@ -0,0 +1,31 @@ ++-- ++-- TPM 2.0 key file format ++-- To generate tpm2key_asn1_tab.c: asn1Parser tpm2key.asn ++-- ++TPM2KEY {} ++DEFINITIONS IMPLICIT TAGS ::= ++ ++BEGIN ++ ++TPMPolicy ::= SEQUENCE { ++ CommandCode [0] EXPLICIT INTEGER, ++ CommandPolicy [1] EXPLICIT OCTET STRING ++} ++ ++TPMAuthPolicy ::= SEQUENCE { ++ Name [0] EXPLICIT UTF8String OPTIONAL, ++ Policy [1] EXPLICIT SEQUENCE OF TPMPolicy ++} ++ ++TPMKey ::= SEQUENCE { ++ type OBJECT IDENTIFIER, ++ emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, ++ policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL, ++ secret [2] EXPLICIT OCTET STRING OPTIONAL, ++ authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL, ++ parent INTEGER, ++ pubkey OCTET STRING, ++ privkey OCTET STRING ++} ++ ++END +diff --git a/grub-core/tpm2/tpm2key.c b/grub-core/tpm2/tpm2key.c +new file mode 100644 +index 000000000..a26c287c9 +--- /dev/null ++++ b/grub-core/tpm2/tpm2key.c +@@ -0,0 +1,447 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2023 SUSE LLC ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++extern asn1_static_node tpm2key_asn1_tab[]; ++const char *sealed_key_oid = "2.23.133.10.1.5"; ++ ++static int ++asn1_allocate_and_read (asn1_node node, const char *name, void **content, grub_size_t *content_size) ++{ ++ grub_uint8_t *tmpstr = NULL; ++ int tmpstr_size = 0; ++ int ret; ++ ++ if (content == NULL) ++ return ASN1_MEM_ERROR; ++ ++ ret = asn1_read_value (node, name, NULL, &tmpstr_size); ++ if (ret != ASN1_MEM_ERROR) ++ return ret; ++ ++ tmpstr = grub_malloc (tmpstr_size); ++ if (tmpstr == NULL) ++ return ASN1_MEM_ERROR; ++ ++ ret = asn1_read_value (node, name, tmpstr, &tmpstr_size); ++ if (ret != ASN1_SUCCESS) ++ return ret; ++ ++ *content = tmpstr; ++ *content_size = tmpstr_size; ++ ++ return ASN1_SUCCESS; ++} ++ ++static int ++asn1_read_uint32 (asn1_node node, const char *name, grub_uint32_t *out) ++{ ++ grub_uint32_t tmp = 0; ++ grub_uint8_t *ptr; ++ void *data = NULL; ++ grub_size_t data_size; ++ int ret; ++ ++ ret = asn1_allocate_and_read (node, name, &data, &data_size); ++ if (ret != ASN1_SUCCESS) ++ return ret; ++ ++ if (data_size > 4) ++ { ++ ret = ASN1_MEM_ERROR; ++ goto error; ++ } ++ ++ /* convert the big-endian integer to host uint32 */ ++ ptr = (grub_uint8_t *)&tmp + (4 - data_size); ++ grub_memcpy (ptr, data, data_size); ++ tmp = grub_be_to_cpu32 (tmp); ++ ++ *out = tmp; ++error: ++ if (data) ++ grub_free (data); ++ return ret; ++} ++ ++grub_err_t ++grub_tpm2key_start_parsing (asn1_node *parsed_tpm2key, void *data, grub_size_t size) ++{ ++ asn1_node tpm2key; ++ asn1_node tpm2key_asn1 = NULL; ++ void *type_oid = NULL; ++ grub_size_t type_oid_size = 0; ++ void *empty_auth = NULL; ++ grub_size_t empty_auth_size = 0; ++ int tmp_size = 0; ++ int ret; ++ grub_err_t err; ++ ++ /* ++ TPMKey ::= SEQUENCE { ++ type OBJECT IDENTIFIER, ++ emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, ++ policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL, ++ secret [2] EXPLICIT OCTET STRING OPTIONAL, ++ authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL, ++ parent INTEGER, ++ pubkey OCTET STRING, ++ privkey OCTET STRING ++ } ++ */ ++ ret = asn1_array2tree (tpm2key_asn1_tab, &tpm2key_asn1, NULL); ++ if (ret != ASN1_SUCCESS) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Failed to parse TPM2KEY ASN.1 array")); ++ ++ ret = asn1_create_element (tpm2key_asn1, "TPM2KEY.TPMKey", &tpm2key); ++ if (ret != ASN1_SUCCESS) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Failed to create TPM2KEY.TPMKey")); ++ ++ ret = asn1_der_decoding (&tpm2key, data, size, NULL); ++ if (ret != ASN1_SUCCESS) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Failed to decode TPM2KEY DER")); ++ ++ /* Check if 'type' is Sealed Key or not */ ++ ret = asn1_allocate_and_read (tpm2key, "type", &type_oid, &type_oid_size); ++ if (ret != ASN1_SUCCESS) ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ N_("Not a valid TPM2KEY file")); ++ ++ if (grub_memcmp (sealed_key_oid, type_oid, type_oid_size) != 0) ++ { ++ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ N_("Not a valid TPM2KEY file")); ++ goto error; ++ } ++ ++ /* 'emptyAuth' must be 'TRUE' since we don't support password authorization */ ++ ret = asn1_allocate_and_read (tpm2key, "emptyAuth", &empty_auth, &empty_auth_size); ++ if (ret != ASN1_SUCCESS || grub_strncmp ("TRUE", empty_auth, empty_auth_size) != 0) ++ { ++ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("emptyAuth not TRUE")); ++ goto error; ++ } ++ ++ /* 'secret' should not be in a sealed key */ ++ ret = asn1_read_value (tpm2key, "secret", NULL, &tmp_size); ++ if (ret != ASN1_ELEMENT_NOT_FOUND) ++ { ++ err = grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("\"secret\" not allowed for Sealed Key")); ++ goto error; ++ } ++ ++ *parsed_tpm2key = tpm2key; ++ ++ err = GRUB_ERR_NONE; ++ ++error: ++ if (type_oid) ++ grub_free (type_oid); ++ ++ if (empty_auth) ++ grub_free (empty_auth); ++ ++ return err; ++} ++ ++void ++grub_tpm2key_end_parsing (asn1_node tpm2key) ++{ ++ if (tpm2key) ++ asn1_delete_structure (&tpm2key); ++ tpm2key = NULL; ++} ++ ++grub_err_t ++grub_tpm2key_get_parent (asn1_node tpm2key, grub_uint32_t *parent) ++{ ++ int ret; ++ ++ if (parent == NULL) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer detected")); ++ ++ if (tpm2key == NULL) ++ return grub_error (GRUB_ERR_READ_ERROR, N_("Invalid parent node")); ++ ++ ret = asn1_read_uint32 (tpm2key, "parent", parent); ++ if (ret != ASN1_SUCCESS) ++ return grub_error (GRUB_ERR_READ_ERROR, N_("Failed to retrieve parent")); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++tpm2key_get_octstring (asn1_node tpm2key, const char *name, void **data, grub_size_t *size) ++{ ++ int ret; ++ ++ if (name == NULL || data == NULL || size == NULL) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter(s)")); ++ ++ if (tpm2key == NULL) ++ return grub_error (GRUB_ERR_READ_ERROR, N_("Invalid %s node"), name); ++ ++ ret = asn1_allocate_and_read (tpm2key, name, data, size); ++ if (ret != ASN1_SUCCESS) ++ return grub_error (GRUB_ERR_READ_ERROR, ++ N_("Failed to retrieve %s"), ++ name); ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_tpm2key_get_pubkey (asn1_node tpm2key, void **data, grub_size_t *size) ++{ ++ return tpm2key_get_octstring (tpm2key, "pubkey", data, size); ++} ++ ++grub_err_t ++grub_tpm2key_get_privkey (asn1_node tpm2key, void **data, grub_size_t *size) ++{ ++ return tpm2key_get_octstring (tpm2key, "privkey", data, size); ++} ++ ++/* ++ * The string to fetch 'Policy' from 'authPolicy': ++ * authPolicy.?XX.Policy ++ */ ++#define AUTHPOLICY_POL_MAX_STR "authPolicy.?XX.Policy" ++#define AUTHPOLICY_POL_MAX (sizeof (AUTHPOLICY_POL_MAX_STR)) ++ ++/* ++ * Expected strings for CommandCode and CommandPolicy: ++ * policy.?XX.CommandCode ++ * policy.?XX.CommandPolicy ++ * authPolicy.?XX.Policy.?YY.CommandCode ++ * authPolicy.?XX.Policy.?YY.CommandPolicy ++ */ ++#define CMD_CODE_MAX_STR AUTHPOLICY_POL_MAX_STR".?YY.CommandCode" ++#define CMD_POL_MAX_STR AUTHPOLICY_POL_MAX_STR".?YY.CommandPolicy" ++#define CMD_CODE_MAX (sizeof (CMD_CODE_MAX_STR)) ++#define CMD_POL_MAX (sizeof (CMD_POL_MAX_STR)) ++ ++static int ++tpm2key_get_policy_seq (asn1_node tpm2key, const char *prefix, ++ tpm2key_policy_t *policy_seq) ++{ ++ tpm2key_policy_t tmp_seq = NULL; ++ tpm2key_policy_t policy = NULL; ++ int policy_n; ++ char cmd_code[CMD_CODE_MAX]; ++ char cmd_pol[CMD_POL_MAX]; ++ grub_size_t cmd_policy_len; ++ int i; ++ int ret; ++ ++ ret = asn1_number_of_elements (tpm2key, prefix, &policy_n); ++ if (ret != ASN1_SUCCESS) ++ return ret; ++ ++ /* ++ * Limit the number of policy commands to two digits (99) ++ * Although there is no upper bound for the number of policy commands, ++ * in practice, it takes one or two policy commands to unseal the key, ++ * so the 99 commands limit is more than enough. ++ */ ++ if (policy_n > 100 || policy_n < 1) ++ return ASN1_VALUE_NOT_VALID; ++ ++ /* ++ * Iterate the policy commands backwards since grub_list_push() prepends ++ * the item into the list. ++ */ ++ for (i = policy_n; i >= 1; i--) { ++ policy = grub_zalloc (sizeof (struct tpm2key_policy)); ++ if (policy == NULL) ++ { ++ ret = ASN1_MEM_ALLOC_ERROR; ++ goto error; ++ } ++ grub_snprintf (cmd_code, CMD_CODE_MAX, "%s.?%d.CommandCode", prefix, i); ++ grub_snprintf (cmd_pol, CMD_POL_MAX, "%s.?%d.CommandPolicy", prefix, i); ++ ++ /* CommandCode [0] EXPLICIT INTEGER */ ++ ret = asn1_read_uint32 (tpm2key, cmd_code, &policy->cmd_code); ++ if (ret != ASN1_SUCCESS) ++ return ret; ++ ++ /* CommandPolicy [1] EXPLICIT OCTET STRING */ ++ ret = tpm2key_get_octstring (tpm2key, cmd_pol, &policy->cmd_policy, ++ &cmd_policy_len); ++ if (ret != ASN1_SUCCESS) ++ { ++ goto error; ++ } ++ else if (cmd_policy_len > GRUB_TPM2_BUFFER_CAPACITY) ++ { ++ /* ++ * CommandPolicy is the marshalled parameters for the TPM command so ++ * it should not be larger than the maximum TPM2 buffer. ++ */ ++ ret = ASN1_VALUE_NOT_VALID; ++ goto error; ++ } ++ policy->cmd_policy_len = (grub_uint16_t)cmd_policy_len; ++ ++ /* Prepend the policy command into the sequence */ ++ grub_list_push (GRUB_AS_LIST_P (&tmp_seq), GRUB_AS_LIST (policy)); ++ } ++ ++ *policy_seq = tmp_seq; ++ ++ return ASN1_SUCCESS; ++ ++error: ++ if (policy) ++ { ++ grub_free (policy->cmd_policy); ++ grub_free (policy); ++ } ++ grub_tpm2key_free_policy_seq (tmp_seq); ++ ++ return ret; ++} ++ ++grub_err_t ++grub_tpm2key_get_policy_seq (asn1_node tpm2key, tpm2key_policy_t *policy_seq) ++{ ++ int ret; ++ ++ ret = tpm2key_get_policy_seq (tpm2key, "policy", policy_seq); ++ if (ret == ASN1_ELEMENT_NOT_FOUND) ++ { ++ /* "policy" is optional, so it may not be available */ ++ *policy_seq = NULL; ++ return GRUB_ERR_NONE; ++ } ++ else if (ret != ASN1_SUCCESS) ++ return grub_error (GRUB_ERR_READ_ERROR, N_("Failed to retrieve policy")); ++ ++ return GRUB_ERR_NONE; ++} ++ ++void ++grub_tpm2key_free_policy_seq (tpm2key_policy_t policy_seq) ++{ ++ tpm2key_policy_t policy; ++ tpm2key_policy_t next; ++ ++ if (policy_seq == NULL) ++ return; ++ ++ FOR_LIST_ELEMENTS_SAFE (policy, next, policy_seq) ++ { ++ grub_free (policy->cmd_policy); ++ grub_free (policy); ++ } ++} ++ ++grub_err_t ++grub_tpm2key_get_authpolicy_seq (asn1_node tpm2key, tpm2key_authpolicy_t *authpol_seq) ++{ ++ tpm2key_authpolicy_t tmp_seq = NULL; ++ tpm2key_authpolicy_t authpol = NULL; ++ int authpol_n; ++ char authpol_pol[AUTHPOLICY_POL_MAX]; ++ int i; ++ int ret; ++ grub_err_t err; ++ ++ ret = asn1_number_of_elements (tpm2key, "authPolicy", &authpol_n); ++ if (ret == ASN1_ELEMENT_NOT_FOUND) ++ { ++ /* "authPolicy" is optional, so it may not be available */ ++ *authpol_seq = NULL; ++ return GRUB_ERR_NONE; ++ } ++ else if (ret != ASN1_SUCCESS) ++ return grub_error (GRUB_ERR_READ_ERROR, N_("Failed to retrieve authPolicy")); ++ ++ /* Limit the number of authPolicy elements to two digits (99) */ ++ if (authpol_n > 100 || authpol_n < 1) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, ++ N_("Invalid number of autoPolicy elements")); ++ ++ /* ++ * Iterate the authPolicy elements backwards since grub_list_push() prepends ++ * the item into the list. ++ */ ++ for (i = authpol_n; i >= 1; i--) { ++ authpol = grub_zalloc (sizeof (struct tpm2key_authpolicy)); ++ if (authpol == NULL) ++ { ++ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ N_("Failed to allocate memory for authPolicy")); ++ goto error; ++ } ++ grub_snprintf (authpol_pol, AUTHPOLICY_POL_MAX, "authPolicy.?%d.Policy", i); ++ ++ ret = tpm2key_get_policy_seq (tpm2key, authpol_pol, &authpol->policy_seq); ++ if (ret != ASN1_SUCCESS) ++ { ++ err = grub_error (GRUB_ERR_READ_ERROR, ++ N_("Failed to retrieve policy from authPolicy")); ++ goto error; ++ } ++ ++ /* Prepend the authPolicy element into the sequence */ ++ grub_list_push (GRUB_AS_LIST_P (&tmp_seq), GRUB_AS_LIST (authpol)); ++ } ++ ++ *authpol_seq = tmp_seq; ++ ++ return GRUB_ERR_NONE; ++ ++error: ++ if (authpol) ++ { ++ grub_tpm2key_free_policy_seq (authpol->policy_seq); ++ grub_free (authpol); ++ } ++ ++ grub_tpm2key_free_authpolicy_seq (tmp_seq); ++ ++ return err; ++} ++ ++void ++grub_tpm2key_free_authpolicy_seq (tpm2key_authpolicy_t authpol_seq) ++{ ++ tpm2key_authpolicy_t authpol; ++ tpm2key_authpolicy_t next; ++ ++ if (authpol_seq == NULL) ++ return; ++ ++ FOR_LIST_ELEMENTS_SAFE (authpol, next, authpol_seq) ++ { ++ grub_tpm2key_free_policy_seq (authpol->policy_seq); ++ grub_free (authpol); ++ } ++} +diff --git a/grub-core/tpm2/tpm2key_asn1_tab.c b/grub-core/tpm2/tpm2key_asn1_tab.c +new file mode 100644 +index 000000000..551fc46ec +--- /dev/null ++++ b/grub-core/tpm2/tpm2key_asn1_tab.c +@@ -0,0 +1,41 @@ ++/* ++ * This file is generated by 'asn1Parser tpm2key.asn' and the '#include' ++ * headers are replaced with the ones in grub2. ++ * - 'grub/mm.h' for the definition of 'NULL' ++ * - 'grub/libtasn1.h' for the definition of 'asn1_static_node' ++ */ ++ ++#include ++#include ++ ++const asn1_static_node tpm2key_asn1_tab[] = { ++ { "TPM2KEY", 536875024, NULL }, ++ { NULL, 1073741836, NULL }, ++ { "TPMPolicy", 1610612741, NULL }, ++ { "CommandCode", 1610620931, NULL }, ++ { NULL, 2056, "0"}, ++ { "CommandPolicy", 536879111, NULL }, ++ { NULL, 2056, "1"}, ++ { "TPMAuthPolicy", 1610612741, NULL }, ++ { "Name", 1610637346, NULL }, ++ { NULL, 2056, "0"}, ++ { "Policy", 536879115, NULL }, ++ { NULL, 1073743880, "1"}, ++ { NULL, 2, "TPMPolicy"}, ++ { "TPMKey", 536870917, NULL }, ++ { "type", 1073741836, NULL }, ++ { "emptyAuth", 1610637316, NULL }, ++ { NULL, 2056, "0"}, ++ { "policy", 1610637323, NULL }, ++ { NULL, 1073743880, "1"}, ++ { NULL, 2, "TPMPolicy"}, ++ { "secret", 1610637319, NULL }, ++ { NULL, 2056, "2"}, ++ { "authPolicy", 1610637323, NULL }, ++ { NULL, 1073743880, "3"}, ++ { NULL, 2, "TPMAuthPolicy"}, ++ { "parent", 1073741827, NULL }, ++ { "pubkey", 1073741831, NULL }, ++ { "privkey", 7, NULL }, ++ { NULL, 0, NULL } ++}; +diff --git a/include/grub/tpm2/internal/args.h b/include/grub/tpm2/internal/args.h +new file mode 100644 +index 000000000..58d13e031 +--- /dev/null ++++ b/include/grub/tpm2/internal/args.h +@@ -0,0 +1,41 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_TPM2_INTERNAL_ARGS_HEADER ++#define GRUB_TPM2_INTERNAL_ARGS_HEADER 1 ++ ++#include ++#include ++ ++grub_err_t ++grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs, ++ grub_uint8_t *pcr_count); ++ ++grub_err_t ++grub_tpm2_protector_parse_asymmetric (const char *value, ++ TPM_ALG_ID *asymmetric, ++ TPM_KEY_BITS *rsa_bits, ++ TPM_ECC_CURVE *ecc_curve); ++ ++grub_err_t ++grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID *bank); ++ ++grub_err_t ++grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE *handle); ++ ++#endif /* ! GRUB_TPM2_INTERNAL_ARGS_HEADER */ +diff --git a/include/grub/tpm2/tpm2key.h b/include/grub/tpm2/tpm2key.h +new file mode 100644 +index 000000000..df46203e3 +--- /dev/null ++++ b/include/grub/tpm2/tpm2key.h +@@ -0,0 +1,83 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2023 SUSE LLC ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_TPM2_TPM2KEY_HEADER ++#define GRUB_TPM2_TPM2KEY_HEADER 1 ++ ++#include ++#include ++ ++/* ++ * TPMPolicy ::= SEQUENCE { ++ * CommandCode [0] EXPLICIT INTEGER, ++ * CommandPolicy [1] EXPLICIT OCTET STRING ++ * } ++ */ ++struct tpm2key_policy { ++ struct tpm2key_policy *next; ++ struct tpm2key_policy **prev; ++ grub_uint32_t cmd_code; ++ void *cmd_policy; ++ grub_uint16_t cmd_policy_len; ++}; ++typedef struct tpm2key_policy *tpm2key_policy_t; ++ ++/* ++ * TPMAuthPolicy ::= SEQUENCE { ++ * Name [0] EXPLICIT UTF8String OPTIONAL, ++ * Policy [1] EXPLICIT SEQUENCE OF TPMPolicy ++ * } ++ * ++ * Name is not a necessary part to unseal the key. Ignore it. ++ */ ++struct tpm2key_authpolicy { ++ struct tpm2key_authpolicy *next; ++ struct tpm2key_authpolicy **prev; ++ /* char *name; */ ++ tpm2key_policy_t policy_seq; ++}; ++typedef struct tpm2key_authpolicy *tpm2key_authpolicy_t; ++ ++grub_err_t ++grub_tpm2key_start_parsing (asn1_node *parsed_tpm2key, void *data, grub_size_t size); ++ ++void ++grub_tpm2key_end_parsing (asn1_node tpm2key); ++ ++grub_err_t ++grub_tpm2key_get_parent (asn1_node tpm2key, grub_uint32_t *parent); ++ ++grub_err_t ++grub_tpm2key_get_pubkey (asn1_node tpm2key, void **data, grub_size_t *size); ++ ++grub_err_t ++grub_tpm2key_get_privkey (asn1_node tpm2key, void **data, grub_size_t *size); ++ ++grub_err_t ++grub_tpm2key_get_policy_seq (asn1_node tpm2key, tpm2key_policy_t *policy_seq); ++ ++void ++grub_tpm2key_free_policy_seq (tpm2key_policy_t policy_seq); ++ ++grub_err_t ++grub_tpm2key_get_authpolicy_seq (asn1_node tpm2key, tpm2key_authpolicy_t *authpol_seq); ++ ++void ++grub_tpm2key_free_authpolicy_seq (tpm2key_authpolicy_t authpol_seq); ++ ++#endif /* GRUB_TPM2_TPM2KEY_HEADER */ +-- +2.35.3 + diff --git a/0003-tpm2-Implement-more-TPM2-commands.patch b/0003-tpm2-Implement-more-TPM2-commands.patch new file mode 100644 index 0000000..b240980 --- /dev/null +++ b/0003-tpm2-Implement-more-TPM2-commands.patch @@ -0,0 +1,543 @@ +From a49c4dcbcb04078434f461ed3356c04042be461a Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Wed, 8 Feb 2023 10:30:55 +0800 +Subject: [PATCH 3/4] tpm2: Implement more TPM2 commands + +This commit implements a few more TPM2 commands as the preparation for +the authorized policy support. + +* TPM2_LoadExternal + This command is added to load the external public key to verify the + signed policy digest +* TPM2_HashSequenceStart, TPM2_SequenceUpdate, TPM2_SequenceComplete, + and TPM2_Hash + With those commands, we can use the TPM as a coprocessor to calculate + the hash of a given binary blob. +* TPM2_VerifySignature + This command verifies the given signature with the given public key + and returns the validation ticket to authorize the policy. +* TPM2_PolicyAuthorize + This command approves the given policy digest so that we can unseal + the key with the newly authorized policy. + +Signed-off-by: Gary Lin +--- + grub-core/tpm2/tpm2.c | 424 +++++++++++++++++++++++++ + include/grub/tpm2/internal/functions.h | 57 ++++ + 2 files changed, 481 insertions(+) + +diff --git a/grub-core/tpm2/tpm2.c b/grub-core/tpm2/tpm2.c +index d67699a24..159353b08 100644 +--- a/grub-core/tpm2/tpm2.c ++++ b/grub-core/tpm2/tpm2.c +@@ -427,6 +427,73 @@ TPM2_Load (const TPMI_DH_OBJECT parent_handle, + return TPM_RC_SUCCESS; + } + ++TPM_RC ++TPM2_LoadExternal (const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_SENSITIVE *inPrivate, ++ const TPM2B_PUBLIC *inPublic, ++ const TPMI_RH_HIERARCHY hierarchy, ++ TPM_HANDLE *objectHandle, ++ TPM2B_NAME *name, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPM_HANDLE objectHandleTmp; ++ TPM2B_NAME nameTmp; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ grub_uint32_t param_size; ++ ++ if (!inPublic) ++ return TPM_RC_VALUE; ++ ++ if (!objectHandle) ++ objectHandle = &objectHandleTmp; ++ if (!name) ++ name = &nameTmp; ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (objectHandle, 0, sizeof (*objectHandle)); ++ grub_memset (name, 0, sizeof (*name)); ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ if (authCommand) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ if (inPrivate) ++ grub_tpm2_mu_TPM2B_SENSITIVE_Marshal (&in, inPrivate); ++ else ++ grub_tpm2_buffer_pack_u16 (&in, 0); ++ grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&in, inPublic); ++ grub_tpm2_buffer_pack_u32 (&in, hierarchy); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_LoadExternal, &responseCode, &in, &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ grub_tpm2_buffer_unpack_u32 (&out, objectHandle); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); ++ grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)name); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ + TPM_RC + TPM2_Unseal (const TPMI_DH_OBJECT itemHandle, + const TPMS_AUTH_COMMAND *authCommand, +@@ -759,3 +826,360 @@ TPM2_EvictControl (const TPMI_RH_PROVISION auth, + + return TPM_RC_SUCCESS; + } ++ ++TPM_RC ++TPM2_HashSequenceStart (const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_AUTH *auth, ++ const TPMI_ALG_HASH hashAlg, ++ TPMI_DH_OBJECT *sequenceHandle, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPMI_DH_OBJECT sequenceHandleTmp; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ TPM_RC rc; ++ grub_uint32_t parameterSize; ++ ++ if (!auth) ++ return TPM_RC_VALUE; ++ ++ if (!sequenceHandle) ++ sequenceHandle = &sequenceHandleTmp; ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (sequenceHandle, 0, sizeof (*sequenceHandle)); ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ if (authCommand) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ grub_tpm2_mu_TPM2B_Marshal (&in, auth->size, auth->buffer); ++ grub_tpm2_buffer_pack_u16 (&in, hashAlg); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_HashSequenceStart, &responseCode, &in, ++ &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal */ ++ grub_tpm2_buffer_unpack_u32 (&out, sequenceHandle); ++ if (tag == TPM_ST_SESSIONS) ++ { ++ grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal(&out, authResponse); ++ } ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_SequenceUpdate (const TPMI_DH_OBJECT sequenceHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_MAX_BUFFER *buffer, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPM_RC responseCode; ++ TPM_RC rc; ++ grub_uint32_t parameterSize; ++ ++ if (!authCommand) ++ return TPM_RC_VALUE; ++ ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, sequenceHandle); ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ if (buffer) ++ grub_tpm2_mu_TPM2B_Marshal (&in, buffer->size, buffer->buffer); ++ else ++ grub_tpm2_buffer_pack_u16 (&in, 0); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (TPM_ST_SESSIONS, TPM_CC_SequenceUpdate, ++ &responseCode, &in, &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal */ ++ grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal(&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_SequenceComplete (const TPMI_DH_OBJECT sequenceHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_MAX_BUFFER *buffer, ++ const TPMI_RH_HIERARCHY hierarchy, ++ TPM2B_DIGEST *result, ++ TPMT_TK_HASHCHECK *validation, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPM2B_DIGEST resultTmp; ++ TPMT_TK_HASHCHECK validationTmp; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPM_RC responseCode; ++ TPM_RC rc; ++ grub_uint32_t parameterSize; ++ ++ if (!authCommand) ++ return TPM_RC_VALUE; ++ ++ if (!result) ++ result = &resultTmp; ++ if (!validation) ++ validation = &validationTmp; ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (result, 0, sizeof (*result)); ++ grub_memset (validation, 0, sizeof (*validation)); ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, sequenceHandle); ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ if (buffer) ++ grub_tpm2_mu_TPM2B_Marshal (&in, buffer->size, buffer->buffer); ++ else ++ grub_tpm2_buffer_pack_u16 (&in, 0); ++ grub_tpm2_buffer_pack_u32 (&in, hierarchy); ++ ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (TPM_ST_SESSIONS, TPM_CC_SequenceComplete, ++ &responseCode, &in, &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal */ ++ grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); ++ grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (&out, result); ++ grub_tpm2_mu_TPMT_TK_HASHCHECK_Unmarshal (&out, validation); ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal(&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_Hash (const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_MAX_BUFFER *data, ++ const TPMI_ALG_HASH hashAlg, ++ const TPMI_RH_HIERARCHY hierarchy, ++ TPM2B_DIGEST *outHash, ++ TPMT_TK_HASHCHECK *validation, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPM2B_DIGEST outHashTmp; ++ TPMT_TK_HASHCHECK validationTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ grub_uint32_t param_size; ++ ++ if (hashAlg == TPM_ALG_NULL) ++ return TPM_RC_VALUE; ++ ++ if (!outHash) ++ outHash = &outHashTmp; ++ if (!validation) ++ validation = &validationTmp; ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (outHash, 0, sizeof (*outHash)); ++ grub_memset (validation, 0, sizeof (*validation)); ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ if (authCommand) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ if (data) ++ grub_tpm2_mu_TPM2B_Marshal (&in, data->size, data->buffer); ++ else ++ grub_tpm2_buffer_pack_u16 (&in, 0); ++ grub_tpm2_buffer_pack_u16 (&in, hashAlg); ++ grub_tpm2_buffer_pack_u32 (&in, hierarchy); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_Hash, &responseCode, &in, &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); ++ grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (&out, outHash); ++ grub_tpm2_mu_TPMT_TK_HASHCHECK_Unmarshal (&out, validation); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_VerifySignature (const TPMI_DH_OBJECT keyHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_DIGEST *digest, ++ const TPMT_SIGNATURE *signature, ++ TPMT_TK_VERIFIED *validation, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPMT_TK_VERIFIED validationTmp; ++ TPM_RC responseCode; ++ grub_uint32_t param_size; ++ ++ if (!digest || !signature) ++ return TPM_RC_VALUE; ++ ++ if (!validation) ++ validation = &validationTmp; ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (validation, 0, sizeof (*validation)); ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ if (authCommand) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ grub_tpm2_buffer_pack_u32 (&in, keyHandle); ++ grub_tpm2_mu_TPM2B_Marshal (&in, digest->size, digest->buffer); ++ grub_tpm2_mu_TPMT_SIGNATURE_Marshal (&in, signature); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_VerifySignature, &responseCode, &in, &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); ++ grub_tpm2_mu_TPMT_TK_VERIFIED_Unmarshal (&out, validation); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} ++ ++TPM_RC ++TPM2_PolicyAuthorize (const TPMI_SH_POLICY policySession, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_DIGEST *approvedPolicy, ++ const TPM2B_NONCE *policyRef, ++ const TPM2B_NAME *keySign, ++ const TPMT_TK_VERIFIED *checkTicket, ++ TPMS_AUTH_RESPONSE *authResponse) ++{ ++ TPM_RC rc; ++ struct grub_tpm2_buffer in; ++ struct grub_tpm2_buffer out; ++ TPMS_AUTH_RESPONSE authResponseTmp; ++ TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; ++ TPM_RC responseCode; ++ grub_uint32_t param_size; ++ ++ if (!approvedPolicy || !keySign || !checkTicket) ++ return TPM_RC_VALUE; ++ ++ if (!authResponse) ++ authResponse = &authResponseTmp; ++ ++ grub_memset (authResponse, 0, sizeof (*authResponse)); ++ ++ /* Marshal */ ++ grub_tpm2_buffer_init (&in); ++ grub_tpm2_buffer_pack_u32 (&in, policySession); ++ if (authCommand) ++ grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); ++ grub_tpm2_mu_TPM2B_Marshal (&in, approvedPolicy->size, approvedPolicy->buffer); ++ if (policyRef) ++ grub_tpm2_mu_TPM2B_Marshal (&in, policyRef->size, policyRef->buffer); ++ else ++ grub_tpm2_buffer_pack_u16 (&in, 0); ++ grub_tpm2_mu_TPM2B_Marshal (&in, keySign->size, keySign->name); ++ grub_tpm2_mu_TPMT_TK_VERIFIED_Marshal (&in, checkTicket); ++ if (in.error) ++ return TPM_RC_FAILURE; ++ ++ /* Submit */ ++ grub_tpm2_buffer_init (&out); ++ rc = grub_tpm2_submit_command (tag, TPM_CC_PolicyAuthorize, &responseCode, &in, &out); ++ if (rc != TPM_RC_SUCCESS) ++ return rc; ++ if (responseCode != TPM_RC_SUCCESS) ++ return responseCode; ++ ++ /* Unmarshal*/ ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); ++ if (tag == TPM_ST_SESSIONS) ++ grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); ++ if (out.error) ++ return TPM_RC_FAILURE; ++ ++ return TPM_RC_SUCCESS; ++} +diff --git a/include/grub/tpm2/internal/functions.h b/include/grub/tpm2/internal/functions.h +index 9380f26a2..67b78fab8 100644 +--- a/include/grub/tpm2/internal/functions.h ++++ b/include/grub/tpm2/internal/functions.h +@@ -70,6 +70,15 @@ TPM2_Load (const TPMI_DH_OBJECT parent_handle, + TPM2B_NAME *name, + TPMS_AUTH_RESPONSE *authResponse); + ++TPM_RC ++TPM2_LoadExternal (const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_SENSITIVE *inPrivate, ++ const TPM2B_PUBLIC *inPublic, ++ const TPMI_RH_HIERARCHY hierarchy, ++ TPM_HANDLE *objectHandle, ++ TPM2B_NAME *name, ++ TPMS_AUTH_RESPONSE *authResponse); ++ + TPM_RC + TPM2_Unseal (const TPMI_DH_OBJECT item_handle, + const TPMS_AUTH_COMMAND *authCommand, +@@ -114,4 +123,52 @@ TPM2_EvictControl (const TPMI_RH_PROVISION auth, + const TPMI_DH_PERSISTENT persistentHandle, + TPMS_AUTH_RESPONSE *authResponse); + ++TPM_RC ++TPM2_HashSequenceStart (const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_AUTH *auth, ++ const TPMI_ALG_HASH hashAlg, ++ TPMI_DH_OBJECT *sequenceHandle, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_SequenceUpdate (const TPMI_DH_OBJECT sequenceHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_MAX_BUFFER *buffer, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_SequenceComplete (const TPMI_DH_OBJECT sequenceHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_MAX_BUFFER *buffer, ++ const TPMI_RH_HIERARCHY hierarchy, ++ TPM2B_DIGEST *result, ++ TPMT_TK_HASHCHECK *validation, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_Hash (const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_MAX_BUFFER *data, ++ const TPMI_ALG_HASH hashAlg, ++ const TPMI_RH_HIERARCHY hierarchy, ++ TPM2B_DIGEST *outHash, ++ TPMT_TK_HASHCHECK *validation, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_VerifySignature (const TPMI_DH_OBJECT keyHandle, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_DIGEST *digest, ++ const TPMT_SIGNATURE *signature, ++ TPMT_TK_VERIFIED *validation, ++ TPMS_AUTH_RESPONSE *authResponse); ++ ++TPM_RC ++TPM2_PolicyAuthorize (const TPMI_SH_POLICY policySession, ++ const TPMS_AUTH_COMMAND *authCommand, ++ const TPM2B_DIGEST *approvedPolicy, ++ const TPM2B_NONCE *policyRef, ++ const TPM2B_NAME *keySign, ++ const TPMT_TK_VERIFIED *checkTicket, ++ TPMS_AUTH_RESPONSE *authResponse); ++ + #endif /* ! GRUB_TPM2_INTERNAL_FUNCTIONS_HEADER */ +-- +2.35.3 + diff --git a/0004-Add-suport-for-signing-grub-with-an-appended-signatu.patch b/0004-Add-suport-for-signing-grub-with-an-appended-signatu.patch new file mode 100644 index 0000000..876be56 --- /dev/null +++ b/0004-Add-suport-for-signing-grub-with-an-appended-signatu.patch @@ -0,0 +1,299 @@ +From cf6b16f113b1b5e6efce79b569be1de3e504de8f Mon Sep 17 00:00:00 2001 +From: Rashmica Gupta +Date: Thu, 11 Jun 2020 11:26:23 +1000 +Subject: [PATCH 04/23] Add suport for signing grub with an appended signature + +Add infrastructure to allow firmware to verify the integrity of grub +by use of a Linux-kernel-module-style appended signature. We initially +target powerpc-ieee1275, but the code should be extensible to other +platforms. + +Usually these signatures are appended to a file without modifying the +ELF file itself. (This is what the 'sign-file' tool does, for example.) +The verifier loads the signed file from the file system and looks at the +end of the file for the appended signature. However, on powerpc-ieee1275 +platforms, the bootloader is often stored directly in the PReP partition +as raw bytes without a file-system. This makes determining the location +of an appended signature more difficult. + +To address this, we add a new ELF note. + +The name field of shall be the string "Appended-Signature", zero-padded +to 4 byte alignment. The type field shall be 0x41536967 (the ASCII values +for the string "ASig"). It must be the final section in the ELF binary. + +The description shall contain the appended signature structure as defined +by the Linux kernel. The description will also be padded to be a multiple +of 4 bytes. The padding shall be added before the appended signature +structure (not at the end) so that the final bytes of a signed ELF file +are the appended signature magic. + +A subsequent patch documents how to create a grub core.img validly signed +under this scheme. + +Signed-off-by: Daniel Axtens +Signed-off-by: Rashmica Gupta + +--- + +You can experiment with this code with a patched version of SLOF +that verifies these signatures. You can find one at: + https://github.com/daxtens/SLOF + +I will be proposing this for inclusion in a future Power Architecture +Platform Reference (PAPR). +--- + include/grub/util/install.h | 8 ++++++-- + include/grub/util/mkimage.h | 4 ++-- + util/grub-install-common.c | 15 +++++++++++--- + util/grub-mkimage.c | 11 +++++++++++ + util/grub-mkimagexx.c | 39 ++++++++++++++++++++++++++++++++++++- + util/mkimage.c | 13 +++++++------ + 6 files changed, 76 insertions(+), 14 deletions(-) + +--- a/include/grub/util/install.h ++++ b/include/grub/util/install.h +@@ -67,6 +67,9 @@ + N_("SBAT metadata"), 0 }, \ + { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, \ + N_("disable shim_lock verifier"), 0 }, \ ++ { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE,\ ++ "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), \ ++ 1}, \ + { "verbose", 'v', 0, 0, \ + N_("print verbose messages."), 1 } + +@@ -130,7 +133,8 @@ + GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS, + GRUB_INSTALL_OPTIONS_DTB, + GRUB_INSTALL_OPTIONS_SBAT, +- GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK ++ GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, ++ GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE + }; + + extern char *grub_install_source_directory; +@@ -190,7 +194,7 @@ + size_t npubkeys, + char *config_path, + const struct grub_install_image_target_desc *image_target, +- int note, ++ int note, size_t appsig_size, + grub_compression_t comp, const char *dtb_file, + const char *sbat_path, const int disable_shim_lock); + +--- a/include/grub/util/mkimage.h ++++ b/include/grub/util/mkimage.h +@@ -51,12 +51,12 @@ + const struct grub_install_image_target_desc *image_target); + void + grub_mkimage_generate_elf32 (const struct grub_install_image_target_desc *image_target, +- int note, char **core_img, size_t *core_size, ++ int note, size_t appsig_size, char **core_img, size_t *core_size, + Elf32_Addr target_addr, + struct grub_mkimage_layout *layout); + void + grub_mkimage_generate_elf64 (const struct grub_install_image_target_desc *image_target, +- int note, char **core_img, size_t *core_size, ++ int note, size_t appsig_size, char **core_img, size_t *core_size, + Elf64_Addr target_addr, + struct grub_mkimage_layout *layout); + +--- a/util/grub-install-common.c ++++ b/util/grub-install-common.c +@@ -466,10 +466,12 @@ + static char *sbat; + static int disable_shim_lock; + static grub_compression_t compression; ++static size_t appsig_size; + + int + grub_install_parse (int key, char *arg) + { ++ const char *end; + switch (key) + { + case GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS: +@@ -567,6 +569,12 @@ + grub_util_error (_("Unrecognized compression `%s'"), arg); + case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE: + return 1; ++ case GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE: ++ grub_errno = 0; ++ appsig_size = grub_strtol(arg, &end, 10); ++ if (grub_errno) ++ return 0; ++ return 1; + default: + return 0; + } +@@ -666,10 +674,11 @@ + " --output '%s' " + " --dtb '%s' " + "--sbat '%s' " +- "--format '%s' --compression '%s' %s %s %s\n", ++ "--format '%s' --compression '%s' " ++ "--appended-signature-size %zu %s %s %s\n", + dir, prefix, + outname, dtb ? : "", sbat ? : "", mkimage_target, +- compnames[compression], note ? "--note" : "", ++ compnames[compression], appsig_size, note ? "--note" : "", + disable_shim_lock ? "--disable-shim-lock" : "", s); + free (s); + +@@ -680,7 +689,7 @@ + grub_install_generate_image (dir, prefix, fp, outname, + modules.entries, memdisk_path, + pubkeys, npubkeys, config_path, tgt, +- note, compression, dtb, sbat, ++ note, appsig_size, compression, dtb, sbat, + disable_shim_lock); + while (dc--) + grub_install_pop_module (); +--- a/util/grub-mkimage.c ++++ b/util/grub-mkimage.c +@@ -84,6 +84,7 @@ + {"sbat", 's', N_("FILE"), 0, N_("SBAT metadata"), 0}, + {"disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, N_("disable shim_lock verifier"), 0}, + {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, ++ {"appended-signature-size", 'S', N_("SIZE"), 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), 0}, + { 0, 0, 0, 0, 0, 0 } + }; + +@@ -128,6 +129,7 @@ + char *sbat; + int note; + int disable_shim_lock; ++ size_t appsig_size; + const struct grub_install_image_target_desc *image_target; + grub_compression_t comp; + }; +@@ -138,6 +140,7 @@ + /* Get the input argument from argp_parse, which we + know is a pointer to our arguments structure. */ + struct arguments *arguments = state->input; ++ const char* end; + + switch (key) + { +@@ -170,6 +173,13 @@ + arguments->note = 1; + break; + ++ case 'S': ++ grub_errno = 0; ++ arguments->appsig_size = grub_strtol(arg, &end, 10); ++ if (grub_errno) ++ return 0; ++ break; ++ + case 'm': + if (arguments->memdisk) + free (arguments->memdisk); +@@ -324,6 +334,7 @@ + arguments.memdisk, arguments.pubkeys, + arguments.npubkeys, arguments.config, + arguments.image_target, arguments.note, ++ arguments.appsig_size, + arguments.comp, arguments.dtb, + arguments.sbat, arguments.disable_shim_lock); + +--- a/util/grub-mkimagexx.c ++++ b/util/grub-mkimagexx.c +@@ -85,6 +85,15 @@ + struct grub_ieee1275_note_desc descriptor; + }; + ++#define GRUB_APPENDED_SIGNATURE_NOTE_NAME "Appended-Signature" ++#define GRUB_APPENDED_SIGNATURE_NOTE_TYPE 0x41536967 /* "ASig" */ ++ ++struct grub_appended_signature_note ++{ ++ Elf32_Nhdr header; ++ char name[ALIGN_UP(sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME), 4)]; ++}; ++ + #define GRUB_XEN_NOTE_NAME "Xen" + + struct fixup_block_list +@@ -208,7 +217,7 @@ + + void + SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc *image_target, +- int note, char **core_img, size_t *core_size, ++ int note, size_t appsig_size, char **core_img, size_t *core_size, + Elf_Addr target_addr, + struct grub_mkimage_layout *layout) + { +@@ -222,6 +231,12 @@ + int shnum = 4; + int string_size = sizeof (".text") + sizeof ("mods") + 1; + ++ if (appsig_size) ++ { ++ phnum++; ++ footer_size += ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4); ++ } ++ + if (image_target->id != IMAGE_LOONGSON_ELF) + phnum += 2; + +@@ -485,6 +500,28 @@ + phdr->p_offset = grub_host_to_target32 (header_size + program_size); + } + ++ if (appsig_size) { ++ int note_size = ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4); ++ struct grub_appended_signature_note *note_ptr = (struct grub_appended_signature_note *) ++ (elf_img + program_size + header_size + (note ? sizeof (struct grub_ieee1275_note) : 0)); ++ ++ note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME)); ++ /* needs to sit at the end, so we round this up and sign some zero padding */ ++ note_ptr->header.n_descsz = grub_host_to_target32 (ALIGN_UP(appsig_size, 4)); ++ note_ptr->header.n_type = grub_host_to_target32 (GRUB_APPENDED_SIGNATURE_NOTE_TYPE); ++ strcpy (note_ptr->name, GRUB_APPENDED_SIGNATURE_NOTE_NAME); ++ ++ phdr++; ++ phdr->p_type = grub_host_to_target32 (PT_NOTE); ++ phdr->p_flags = grub_host_to_target32 (PF_R); ++ phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof); ++ phdr->p_vaddr = 0; ++ phdr->p_paddr = 0; ++ phdr->p_filesz = grub_host_to_target32 (note_size); ++ phdr->p_memsz = 0; ++ phdr->p_offset = grub_host_to_target32 (header_size + program_size + (note ? sizeof (struct grub_ieee1275_note) : 0)); ++ } ++ + { + char *str_start = (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr) + + shnum * sizeof (*shdr)); +--- a/util/mkimage.c ++++ b/util/mkimage.c +@@ -885,8 +885,9 @@ + char *memdisk_path, char **pubkey_paths, + size_t npubkeys, char *config_path, + const struct grub_install_image_target_desc *image_target, +- int note, grub_compression_t comp, const char *dtb_path, +- const char *sbat_path, int disable_shim_lock) ++ int note, size_t appsig_size, grub_compression_t comp, ++ const char *dtb_path, const char *sbat_path, ++ int disable_shim_lock) + { + char *kernel_img, *core_img; + size_t total_module_size, core_size; +@@ -1810,11 +1811,11 @@ + else + target_addr = image_target->link_addr; + if (image_target->voidp_sizeof == 4) +- grub_mkimage_generate_elf32 (image_target, note, &core_img, &core_size, +- target_addr, &layout); ++ grub_mkimage_generate_elf32 (image_target, note, appsig_size, &core_img, ++ &core_size, target_addr, &layout); + else +- grub_mkimage_generate_elf64 (image_target, note, &core_img, &core_size, +- target_addr, &layout); ++ grub_mkimage_generate_elf64 (image_target, note, appsig_size, &core_img, ++ &core_size, target_addr, &layout); + } + break; + } diff --git a/0004-Introduce-prep_load_env-command.patch b/0004-Introduce-prep_load_env-command.patch new file mode 100644 index 0000000..7baaedd --- /dev/null +++ b/0004-Introduce-prep_load_env-command.patch @@ -0,0 +1,272 @@ +From 3cf4fdf8d17423dea4e5913ab14fb6305f3c2571 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Fri, 18 Feb 2022 21:43:38 +0800 +Subject: [PATCH 4/5] Introduce prep_load_env command + +This command will accept grub disk device and perform load_env for +environment block located at end of PReP partition which belongs to that +input disk device. All variables read from that environment block are +exported to grub as environment variables. + +Please note there's no support for whitelist variables and also +--skip-sig option compared to ordinary load_env command. + +v2: +To avoid disrupting the boot process with errors, it's important to log +any errors that may occur and always return GRUB_ERR_NONE. + +v3: +Making the new module powerpc_ieee1275 specific. + +Signed-off-by: Michael Chang +--- + grub-core/Makefile.core.def | 5 + + grub-core/commands/prep_loadenv.c | 227 ++++++++++++++++++++++++++++++ + 2 files changed, 232 insertions(+) + create mode 100644 grub-core/commands/prep_loadenv.c + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2673,3 +2673,9 @@ + common = lib/libtasn1_wrap/tests/Test_strings.c; + common = lib/libtasn1_wrap/wrap_tests.c; + }; ++ ++module = { ++ name = prep_loadenv; ++ common = commands/prep_loadenv.c; ++ enable = powerpc_ieee1275; ++}; +--- /dev/null ++++ b/grub-core/commands/prep_loadenv.c +@@ -0,0 +1,230 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++static char * ++match_substr (regmatch_t *match, const char *str) ++{ ++ if (match->rm_so != -1) ++ { ++ char *substr; ++ regoff_t sz = match->rm_eo - match->rm_so; ++ ++ if (!sz) ++ return NULL; ++ substr = grub_malloc (1 + sz); ++ if (!substr) ++ { ++ grub_print_error (); ++ return NULL; ++ } ++ grub_memcpy (substr, str + match->rm_so, sz); ++ substr[sz] = '\0'; ++ return substr; ++ } ++ ++ return NULL; ++} ++ ++static int ++is_prep_partition (grub_device_t dev) ++{ ++ if (!dev->disk) ++ return 0; ++ if (!dev->disk->partition) ++ return 0; ++ if (grub_strcmp (dev->disk->partition->partmap->name, "msdos") == 0) ++ return (dev->disk->partition->msdostype == 0x41); ++ ++ if (grub_strcmp (dev->disk->partition->partmap->name, "gpt") == 0) ++ { ++ struct grub_gpt_partentry gptdata; ++ grub_partition_t p = dev->disk->partition; ++ int ret = 0; ++ dev->disk->partition = dev->disk->partition->parent; ++ ++ if (grub_disk_read (dev->disk, p->offset, p->index, ++ sizeof (gptdata), &gptdata) == 0) ++ { ++ const grub_guid_t template = { ++ grub_cpu_to_le32_compile_time (0x9e1a2d38), ++ grub_cpu_to_le16_compile_time (0xc612), ++ grub_cpu_to_le16_compile_time (0x4316), ++ { 0xaa, 0x26, 0x8b, 0x49, 0x52, 0x1e, 0x5a, 0x8b } ++ }; ++ ++ ret = grub_memcmp (&template, &gptdata.type, ++ sizeof (template)) == 0; ++ } ++ dev->disk->partition = p; ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++part_hook (grub_disk_t disk, const grub_partition_t partition, void *data) ++{ ++ char **ret = data; ++ char *partition_name, *devname; ++ grub_device_t dev; ++ ++ partition_name = grub_partition_get_name (partition); ++ if (! partition_name) ++ return 2; ++ ++ devname = grub_xasprintf ("%s,%s", disk->name, partition_name); ++ grub_free (partition_name); ++ if (!devname) ++ return 2; ++ ++ dev = grub_device_open (devname); ++ if (!dev) ++ { ++ grub_free (devname); ++ return 2; ++ } ++ if (is_prep_partition (dev)) ++ { ++ *ret = devname; ++ return 1; ++ } ++ grub_free (devname); ++ grub_device_close (dev); ++ return 0; ++} ++ ++static int ++set_var (const char *name, const char *value, ++ void *hook_data __attribute__ ((unused))) ++{ ++ grub_env_set (name, value); ++ grub_env_export (name); ++ return 0; ++} ++ ++static grub_err_t ++prep_read_envblk (const char *devname) ++{ ++ char *buf = NULL; ++ grub_device_t dev = NULL; ++ grub_envblk_t envblk = NULL; ++ ++ dev = grub_device_open (devname); ++ if (!dev) ++ return grub_errno; ++ ++ if (!dev->disk || !dev->disk->partition) ++ { ++ grub_error (GRUB_ERR_BAD_DEVICE, "disk device required"); ++ goto fail; ++ } ++ ++ buf = grub_malloc (GRUB_ENVBLK_PREP_SIZE); ++ if (!buf) ++ goto fail; ++ ++ if (grub_disk_read (dev->disk, dev->disk->partition->len - (GRUB_ENVBLK_PREP_SIZE >> GRUB_DISK_SECTOR_BITS), 0, GRUB_ENVBLK_PREP_SIZE, buf)) ++ goto fail; ++ ++ envblk = grub_envblk_open (buf, GRUB_ENVBLK_PREP_SIZE); ++ if (!envblk) ++ { ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block"); ++ goto fail; ++ } ++ grub_envblk_iterate (envblk, NULL, set_var); ++ ++ fail: ++ if (envblk) ++ grub_envblk_close (envblk); ++ else ++ grub_free (buf); ++ if (dev) ++ grub_device_close (dev); ++ return grub_errno; ++} ++ ++static grub_err_t ++prep_partname (const char *devname, char **prep) ++{ ++ grub_device_t dev = NULL; ++ grub_err_t err; ++ int ret; ++ ++ dev = grub_device_open (devname); ++ if (!dev) ++ return grub_errno; ++ ++ ret = grub_partition_iterate (dev->disk, part_hook, prep); ++ if (ret == 1 && *prep) ++ { ++ err = GRUB_ERR_NONE; ++ goto out; ++ } ++ else if (ret == 0 && grub_errno == GRUB_ERR_NONE) ++ err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "no prep partition"); ++ else ++ err = grub_errno; ++ ++ out: ++ grub_device_close (dev); ++ return err; ++} ++ ++static grub_err_t ++grub_cmd_prep_loadenv (grub_command_t cmd __attribute__ ((unused)), ++ int argc, ++ char **argv) ++{ ++ char *devname, *prep = NULL; ++ grub_err_t err; ++ ++ if (argc < 1) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); ++ ++ devname = grub_file_get_device_name(argv[0]); ++ if (!devname) ++ return grub_errno; ++ ++ err = prep_partname (devname, &prep); ++ if (prep == NULL || err != GRUB_ERR_NONE) ++ goto out; ++ ++ err = prep_read_envblk (prep); ++ ++ out: ++ grub_free (devname); ++ grub_free (prep); ++ ++ if (err) ++ grub_print_error (); ++ return GRUB_ERR_NONE; ++} ++ ++static grub_command_t cmd_prep_load; ++ ++GRUB_MOD_INIT(prep_loadenv) ++{ ++ cmd_prep_load = ++ grub_register_command("prep_load_env", grub_cmd_prep_loadenv, ++ "DEVICE", ++ N_("Load variables from environment block file.")); ++} ++ ++GRUB_MOD_FINI(prep_loadenv) ++{ ++ grub_unregister_command (cmd_prep_load); ++} diff --git a/0004-Key-revocation-on-out-of-bound-file-access.patch b/0004-Key-revocation-on-out-of-bound-file-access.patch new file mode 100644 index 0000000..2a85d8d --- /dev/null +++ b/0004-Key-revocation-on-out-of-bound-file-access.patch @@ -0,0 +1,91 @@ +From 6547d22fc9e20720d1a896be82b2d50d842f86b0 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Mon, 20 Nov 2023 09:25:53 +0800 +Subject: [PATCH 4/4] Key revocation on out of bound file access + +After successful disk unlocking, grub now takes on the responsibility of +safeguarding passwords or TPM keys exclusively within authenticated +cryptodisk files. Any attempt to access boot-related files outside this +trust realm triggers immediate key revocation, preventing potential +compromise by out of bound access. + +This patch strengthens security measures by restricting grub's access to +system boot files, except for essential internal processes like memdisk +and procfs, ensuring key protection against potential breaches due to +inadvertent customizations in grub.cfg. + +Signed-Off-by Michael Chang +--- + grub-core/commands/crypttab.c | 36 +++++++++++++++++++++++++++++++++++ + include/grub/file.h | 1 + + 2 files changed, 37 insertions(+) + +diff --git a/grub-core/commands/crypttab.c b/grub-core/commands/crypttab.c +index d3acc4b59..e09296c57 100644 +--- a/grub-core/commands/crypttab.c ++++ b/grub-core/commands/crypttab.c +@@ -121,6 +121,41 @@ grub_cryptokey_tpmkey_discard (void) + grub_cryptokey_discard(); + } + ++static grub_file_t ++grub_distrust_open (grub_file_t io, ++ enum grub_file_type type __attribute__ ((unused))) ++{ ++ grub_disk_t disk = io->device->disk; ++ ++ if (io->device->disk && ++ (io->device->disk->dev->id == GRUB_DISK_DEVICE_MEMDISK_ID ++ || io->device->disk->dev->id == GRUB_DISK_DEVICE_PROCFS_ID)) ++ return io; ++ ++ /* Ensure second stage files is in a protected location or grub won't hand ++ * over the key and discards it */ ++ switch (type & GRUB_FILE_TYPE_MASK) ++ { ++ case GRUB_FILE_TYPE_ACPI_TABLE: ++ case GRUB_FILE_TYPE_CONFIG: ++ case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE: ++ case GRUB_FILE_TYPE_FONT: ++ case GRUB_FILE_TYPE_GRUB_MODULE: ++ case GRUB_FILE_TYPE_GRUB_MODULE_LIST: ++ case GRUB_FILE_TYPE_LINUX_KERNEL: ++ case GRUB_FILE_TYPE_LINUX_INITRD: ++ case GRUB_FILE_TYPE_LOADENV: ++ case GRUB_FILE_TYPE_THEME: ++ if (!disk || !grub_disk_is_crypto (disk)) ++ grub_cryptokey_discard (); ++ break; ++ default: ++ break; ++ } ++ ++ return io; ++} ++ + static grub_err_t + grub_cmd_crypttab_entry (grub_command_t cmd __attribute__ ((unused)), + int argc, char **argv) +@@ -153,6 +188,7 @@ GRUB_MOD_INIT(crypttab) + { + cmd = grub_register_command ("crypttab_entry", grub_cmd_crypttab_entry, + N_("VOLUME-NAME ENCRYPTED-DEVICE KEY-FILE") , N_("No description")); ++ grub_file_filter_register (GRUB_FILE_FILTER_DISTRUST, grub_distrust_open); + grub_dl_set_persistent (mod); + } + +diff --git a/include/grub/file.h b/include/grub/file.h +index fcfd32ce2..daf23a9c9 100644 +--- a/include/grub/file.h ++++ b/include/grub/file.h +@@ -185,6 +185,7 @@ extern grub_disk_read_hook_t EXPORT_VAR(grub_file_progress_hook); + /* Filters with lower ID are executed first. */ + typedef enum grub_file_filter_id + { ++ GRUB_FILE_FILTER_DISTRUST, + GRUB_FILE_FILTER_NOCAT, + GRUB_FILE_FILTER_VERIFY, + GRUB_FILE_FILTER_GZIO, +-- +2.42.1 + diff --git a/0004-Try-to-pick-better-locations-for-kernel-and-initrd.patch b/0004-Try-to-pick-better-locations-for-kernel-and-initrd.patch new file mode 100644 index 0000000..f0a8ade --- /dev/null +++ b/0004-Try-to-pick-better-locations-for-kernel-and-initrd.patch @@ -0,0 +1,183 @@ +From 384763d7990f769839ca74d6756fbd85580873d4 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Thu, 11 Jul 2019 17:17:02 +0200 +Subject: [PATCH 04/11] Try to pick better locations for kernel and initrd + +- Don't limit allocations on 64-bit platforms to < 0x[37f]fffffff if + we're using the "large" code model ; use __UINTPTR_MAX__. +- Get the comparison right to check the address we've allocated. +- Fix the allocation for the command line as well. + +*But*, when we did this some systems started failing badly; coudln't +parse partition tables, etc. What's going on here is the disk controller +is silently failing DMAs to addresses above 4GB, so we're trying to parse +uninitialized (or HW zeroed) ram when looking for the partition table, +etc. + +So to limit this, we make grub_malloc() pick addresses below 4GB on +x86_64, but the direct EFI page allocation functions can get addresses +above that. + +Additionally, we now try to locate kernel+initrd+cmdline+etc below +0x7fffffff, and if they're too big to fit any memory window there, then +we try a higher address. + +Signed-off-by: Peter Jones +--- + grub-core/kern/efi/mm.c | 8 ++++---- + grub-core/loader/i386/efi/linux.c | 25 +++++++++++++++++-------- + include/grub/arm/efi/memory.h | 1 + + include/grub/arm64/efi/memory.h | 1 + + include/grub/i386/efi/memory.h | 1 + + include/grub/ia64/efi/memory.h | 1 + + include/grub/x86_64/efi/memory.h | 4 +++- + 7 files changed, 28 insertions(+), 13 deletions(-) + +--- a/grub-core/kern/efi/mm.c ++++ b/grub-core/kern/efi/mm.c +@@ -121,7 +121,7 @@ + grub_efi_boot_services_t *b; + grub_efi_physical_address_t address = max; + +- if (max > 0xffffffff) ++ if (max > GRUB_EFI_MAX_USABLE_ADDRESS) + return 0; + + b = grub_efi_system_table->boot_services; +@@ -481,7 +481,7 @@ + { + if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY + #if 1 +- && desc->physical_start <= GRUB_EFI_MAX_USABLE_ADDRESS ++ && desc->physical_start <= GRUB_EFI_MAX_ALLOCATION_ADDRESS + #endif + && desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000 + && desc->num_pages != 0) +@@ -499,9 +499,9 @@ + #if 1 + if (BYTES_TO_PAGES (filtered_desc->physical_start) + + filtered_desc->num_pages +- > BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS)) ++ > BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_ALLOCATION_ADDRESS)) + filtered_desc->num_pages +- = (BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS) ++ = (BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_ALLOCATION_ADDRESS) + - BYTES_TO_PAGES (filtered_desc->physical_start)); + #endif + +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -102,8 +103,9 @@ + size += ALIGN_UP (grub_file_size (files[i]), 4); + } + +- initrd_mem = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(size)); +- ++ initrd_mem = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, BYTES_TO_PAGES(size)); ++ if (!initrd_mem) ++ initrd_mem = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, BYTES_TO_PAGES(size)); + if (!initrd_mem) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd")); +@@ -187,8 +189,11 @@ + goto fail; + } + +- params = grub_efi_allocate_pages_max (0x3fffffff, ++ params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, + BYTES_TO_PAGES(sizeof(*params))); ++ if (!params) ++ params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, ++ BYTES_TO_PAGES(sizeof(*params))); + if (! params) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); +@@ -258,8 +263,11 @@ + #endif + + grub_dprintf ("linux", "setting up cmdline\n"); +- linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff, +- BYTES_TO_PAGES(lh->cmdline_size + 1)); ++ linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS, ++ BYTES_TO_PAGES(lh->cmdline_size + 1)); ++ if (!linux_cmdline) ++ linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_USABLE_ADDRESS, ++ BYTES_TO_PAGES(lh->cmdline_size + 1)); + if (!linux_cmdline) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline")); +@@ -285,11 +293,12 @@ + + kernel_mem = grub_efi_allocate_pages_max(lh->pref_address, + BYTES_TO_PAGES(lh->init_size)); +- + if (!kernel_mem) +- kernel_mem = grub_efi_allocate_pages_max(0x3fffffff, ++ kernel_mem = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS, ++ BYTES_TO_PAGES(lh->init_size)); ++ if (!kernel_mem) ++ kernel_mem = grub_efi_allocate_pages_max(GRUB_EFI_MAX_USABLE_ADDRESS, + BYTES_TO_PAGES(lh->init_size)); +- + if (!kernel_mem) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel")); +--- a/include/grub/arm/efi/memory.h ++++ b/include/grub/arm/efi/memory.h +@@ -2,5 +2,6 @@ + #include + + #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff ++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS + + #endif /* ! GRUB_MEMORY_CPU_HEADER */ +--- a/include/grub/arm64/efi/memory.h ++++ b/include/grub/arm64/efi/memory.h +@@ -2,5 +2,6 @@ + #include + + #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffffffffULL ++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS + + #endif /* ! GRUB_MEMORY_CPU_HEADER */ +--- a/include/grub/i386/efi/memory.h ++++ b/include/grub/i386/efi/memory.h +@@ -2,5 +2,6 @@ + #include + + #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff ++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS + + #endif /* ! GRUB_MEMORY_CPU_HEADER */ +--- a/include/grub/ia64/efi/memory.h ++++ b/include/grub/ia64/efi/memory.h +@@ -2,5 +2,6 @@ + #include + + #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff ++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS + + #endif /* ! GRUB_MEMORY_CPU_HEADER */ +--- a/include/grub/x86_64/efi/memory.h ++++ b/include/grub/x86_64/efi/memory.h +@@ -2,9 +2,11 @@ + #include + + #if defined (__code_model_large__) +-#define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff ++#define GRUB_EFI_MAX_USABLE_ADDRESS __UINTPTR_MAX__ ++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS 0x7fffffff + #else + #define GRUB_EFI_MAX_USABLE_ADDRESS 0x7fffffff ++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS + #endif + + #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/0004-arm-arm64-loader-Better-memory-allocation-and-error-.patch b/0004-arm-arm64-loader-Better-memory-allocation-and-error-.patch new file mode 100644 index 0000000..2f226b4 --- /dev/null +++ b/0004-arm-arm64-loader-Better-memory-allocation-and-error-.patch @@ -0,0 +1,301 @@ +From 5d417346956bc3108183020a8a9f20ddda034b48 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Thu, 11 Jul 2019 14:38:57 +0200 +Subject: [PATCH 4/9] arm/arm64 loader: Better memory allocation and error + messages. + +On mustang, our memory map looks like: + +Type Physical start - end #Pages Size Attributes +reserved 0000004000000000-00000040001fffff 00000200 2MiB UC WC WT WB +conv-mem 0000004000200000-0000004393ffffff 00393e00 14654MiB UC WC WT WB +ldr-code 0000004394000000-00000043f7ffffff 00064000 1600MiB UC WC WT WB +BS-data 00000043f8000000-00000043f801ffff 00000020 128KiB UC WC WT WB +conv-mem 00000043f8020000-00000043fa15bfff 0000213c 34032KiB UC WC WT WB +ldr-code 00000043fa15c000-00000043fa2a1fff 00000146 1304KiB UC WC WT WB +ldr-data 00000043fa2a2000-00000043fa3e8fff 00000147 1308KiB UC WC WT WB +conv-mem 00000043fa3e9000-00000043fa3e9fff 00000001 4KiB UC WC WT WB +ldr-data 00000043fa3ea000-00000043fa3eafff 00000001 4KiB UC WC WT WB +ldr-code 00000043fa3eb000-00000043fa4affff 000000c5 788KiB UC WC WT WB +BS-code 00000043fa4b0000-00000043fa59ffff 000000f0 960KiB UC WC WT WB +RT-code 00000043fa5a0000-00000043fa5affff 00000010 64KiB RT UC WC WT WB +RT-data 00000043fa5b0000-00000043fa5bffff 00000010 64KiB RT UC WC WT WB +RT-code 00000043fa5c0000-00000043fa5cffff 00000010 64KiB RT UC WC WT WB +ldr-data 00000043fa5d0000-00000043fa5d0fff 00000001 4KiB UC WC WT WB +BS-code 00000043fa5d1000-00000043fa5ddfff 0000000d 52KiB UC WC WT WB +reserved 00000043fa5de000-00000043fa60ffff 00000032 200KiB UC WC WT WB +ACPI-rec 00000043fa610000-00000043fa6affff 000000a0 640KiB UC WC WT WB +ACPI-nvs 00000043fa6b0000-00000043fa6bffff 00000010 64KiB UC WC WT WB +ACPI-rec 00000043fa6c0000-00000043fa70ffff 00000050 320KiB UC WC WT WB +RT-code 00000043fa710000-00000043fa72ffff 00000020 128KiB RT UC WC WT WB +RT-data 00000043fa730000-00000043fa78ffff 00000060 384KiB RT UC WC WT WB +RT-code 00000043fa790000-00000043fa79ffff 00000010 64KiB RT UC WC WT WB +RT-data 00000043fa7a0000-00000043fa99ffff 00000200 2MiB RT UC WC WT WB +RT-code 00000043fa9a0000-00000043fa9affff 00000010 64KiB RT UC WC WT WB +RT-data 00000043fa9b0000-00000043fa9cffff 00000020 128KiB RT UC WC WT WB +BS-code 00000043fa9d0000-00000043fa9d9fff 0000000a 40KiB UC WC WT WB +reserved 00000043fa9da000-00000043fa9dbfff 00000002 8KiB UC WC WT WB +conv-mem 00000043fa9dc000-00000043fc29dfff 000018c2 25352KiB UC WC WT WB +BS-data 00000043fc29e000-00000043fc78afff 000004ed 5044KiB UC WC WT WB +conv-mem 00000043fc78b000-00000043fca01fff 00000277 2524KiB UC WC WT WB +BS-data 00000043fca02000-00000043fcea3fff 000004a2 4744KiB UC WC WT WB +conv-mem 00000043fcea4000-00000043fcea4fff 00000001 4KiB UC WC WT WB +BS-data 00000043fcea5000-00000043fd192fff 000002ee 3000KiB UC WC WT WB +conv-mem 00000043fd193000-00000043fd2b0fff 0000011e 1144KiB UC WC WT WB +BS-data 00000043fd2b1000-00000043ff80ffff 0000255f 38268KiB UC WC WT WB +BS-code 00000043ff810000-00000043ff99ffff 00000190 1600KiB UC WC WT WB +RT-code 00000043ff9a0000-00000043ff9affff 00000010 64KiB RT UC WC WT WB +conv-mem 00000043ff9b0000-00000043ff9bffff 00000010 64KiB UC WC WT WB +RT-data 00000043ff9c0000-00000043ff9effff 00000030 192KiB RT UC WC WT WB +conv-mem 00000043ff9f0000-00000043ffa05fff 00000016 88KiB UC WC WT WB +BS-data 00000043ffa06000-00000043ffffffff 000005fa 6120KiB UC WC WT WB +MMIO 0000000010510000-0000000010510fff 00000001 4KiB RT +MMIO 0000000010548000-0000000010549fff 00000002 8KiB RT +MMIO 0000000017000000-0000000017001fff 00000002 8KiB RT +MMIO 000000001c025000-000000001c025fff 00000001 4KiB RT + +This patch adds a requirement when we're trying to find the base of ram, that +the memory we choose is actually /allocatable/ conventional memory, not merely +write-combining. On this machine that means we wind up with an allocation +around 0x4392XXXXXX, which is a reasonable address. + +This also changes grub_efi_allocate_pages_real() so that if 0 is allocated, it +tries to allocate again starting with the same max address it did the first +time, rather than interposing GRUB_EFI_MAX_USABLE_ADDRESS there, so that any +per-platform constraints on its given address are maintained. + +Signed-off-by: Peter Jones + +squash! arm/arm64 loader: Better memory allocation and error messages. + +Use PRIxGRUB_* conversion specifier in printf's format string to +correspond properly to the data type of arguments. + +Signed-off-by: Michael Chang +--- + grub-core/kern/efi/mm.c | 33 ++++++++++--- + grub-core/loader/arm64/efi/linux.c | 78 ++++++++++++++++++++++-------- + 2 files changed, 84 insertions(+), 27 deletions(-) + +--- a/grub-core/kern/efi/mm.c ++++ b/grub-core/kern/efi/mm.c +@@ -153,6 +153,7 @@ + { + grub_efi_status_t status; + grub_efi_boot_services_t *b; ++ grub_efi_physical_address_t ret = address; + + /* Limit the memory access to less than 4GB for 32-bit platforms. */ + if (address > GRUB_EFI_MAX_USABLE_ADDRESS) +@@ -169,19 +170,22 @@ + } + + b = grub_efi_system_table->boot_services; +- status = b->allocate_pages (alloctype, memtype, pages, &address); ++ status = b->allocate_pages (alloctype, memtype, pages, &ret); + if (status != GRUB_EFI_SUCCESS) + { ++ grub_dprintf ("efi", ++ "allocate_pages(%d, %d, 0x%0" PRIxGRUB_SIZE ", 0x%016" PRIxGRUB_UINT64_T ") = 0x%016" PRIxGRUB_SIZE "\n", ++ alloctype, memtype, pages, address, status); + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + return NULL; + } + +- if (address == 0) ++ if (ret == 0) + { + /* Uggh, the address 0 was allocated... This is too annoying, + so reallocate another one. */ +- address = GRUB_EFI_MAX_USABLE_ADDRESS; +- status = b->allocate_pages (alloctype, memtype, pages, &address); ++ ret = address; ++ status = b->allocate_pages (alloctype, memtype, pages, &ret); + grub_efi_free_pages (0, pages); + if (status != GRUB_EFI_SUCCESS) + { +@@ -190,9 +194,9 @@ + } + } + +- grub_efi_store_alloc (address, pages); ++ grub_efi_store_alloc (ret, pages); + +- return (void *) ((grub_addr_t) address); ++ return (void *) ((grub_addr_t) ret); + } + + void * +@@ -711,8 +715,21 @@ + for (desc = memory_map, *base_addr = GRUB_EFI_MAX_USABLE_ADDRESS; + (grub_addr_t) desc < ((grub_addr_t) memory_map + memory_map_size); + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) +- if (desc->attribute & GRUB_EFI_MEMORY_WB) +- *base_addr = grub_min (*base_addr, desc->physical_start); ++ { ++ if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY && ++ (desc->attribute & GRUB_EFI_MEMORY_WB)) ++ { ++ *base_addr = grub_min (*base_addr, desc->physical_start); ++ grub_dprintf ("efi", "setting base_addr=0x%016" PRIxGRUB_ADDR "\n", *base_addr); ++ } ++ else ++ { ++ grub_dprintf ("efi", "ignoring address 0x%016" PRIxGRUB_UINT64_T "\n", desc->physical_start); ++ } ++ } ++ ++ if (*base_addr == GRUB_EFI_MAX_USABLE_ADDRESS) ++ grub_dprintf ("efi", "base_addr 0x%016" PRIxGRUB_ADDR " is probably wrong.\n", *base_addr); + + grub_free(memory_map); + +--- a/grub-core/loader/arm64/efi/linux.c ++++ b/grub-core/loader/arm64/efi/linux.c +@@ -89,13 +89,15 @@ + { + grub_efi_loaded_image_t *loaded_image = NULL; + int node, retval, len; +- ++ grub_err_t err = GRUB_ERR_NONE; + void *fdt; + + fdt = grub_fdt_load (GRUB_EFI_LINUX_FDT_EXTRA_SPACE); +- + if (!fdt) +- goto failure; ++ { ++ err = grub_error(GRUB_ERR_BAD_OS, "failed to load FDT"); ++ goto failure; ++ } + + node = grub_fdt_find_subnode (fdt, 0, "chosen"); + if (node < 0) +@@ -106,17 +108,26 @@ + */ + retval = grub_fdt_set_prop32(fdt, 0, "#address-cells", 2); + if (retval) +- goto failure; ++ { ++ err = grub_error(retval, "Could not find #address-cells"); ++ goto failure; ++ } + + retval = grub_fdt_set_prop32(fdt, 0, "#size-cells", 2); + if (retval) +- goto failure; ++ { ++ err = grub_error(retval, "Could not find #size-cells"); ++ goto failure; ++ } + + node = grub_fdt_add_subnode (fdt, 0, "chosen"); + } + + if (node < 1) +- goto failure; ++ { ++ err = grub_error(grub_errno, "failed to load chosen fdt node."); ++ goto failure; ++ } + + /* Set initrd info */ + if (initrd_start && initrd_end > initrd_start) +@@ -127,15 +138,26 @@ + retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-start", + initrd_start); + if (retval) +- goto failure; ++ { ++ err = grub_error(retval, "Failed to set linux,initrd-start property"); ++ goto failure; ++ } ++ + retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-end", + initrd_end); + if (retval) +- goto failure; ++ { ++ err = grub_error(retval, "Failed to set linux,initrd-end property"); ++ goto failure; ++ } + } + +- if (grub_fdt_install() != GRUB_ERR_NONE) +- goto failure; ++ retval = grub_fdt_install(); ++ if (retval != GRUB_ERR_NONE) ++ { ++ err = grub_error(retval, "Failed to install fdt"); ++ goto failure; ++ } + + grub_dprintf ("linux", "Installed/updated FDT configuration table @ %p\n", + fdt); +@@ -143,14 +165,20 @@ + /* Convert command line to UCS-2 */ + loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (!loaded_image) +- goto failure; ++ { ++ err = grub_error(grub_errno, "Failed to install fdt"); ++ goto failure; ++ } + + loaded_image->load_options_size = len = + (grub_strlen (linux_args) + 1) * sizeof (grub_efi_char16_t); + loaded_image->load_options = + grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); + if (!loaded_image->load_options) +- return grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters"); ++ { ++ err = grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters"); ++ goto failure; ++ } + + loaded_image->load_options_size = + 2 * grub_utf8_to_utf16 (loaded_image->load_options, len, +@@ -160,7 +188,7 @@ + + failure: + grub_fdt_unload(); +- return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT"); ++ return err; + } + + static void +@@ -246,16 +274,28 @@ + static void * + allocate_initrd_mem (int initrd_pages) + { +- grub_addr_t max_addr; ++ grub_addr_t max_addr = 0; ++ grub_err_t err; ++ void *ret; ++ ++ err = grub_efi_get_ram_base (&max_addr); ++ if (err != GRUB_ERR_NONE) ++ { ++ grub_error (err, "grub_efi_get_ram_base() failed"); ++ return NULL; ++ } + +- if (grub_efi_get_ram_base (&max_addr) != GRUB_ERR_NONE) +- return NULL; ++ grub_dprintf ("linux", "max_addr: 0x%016lx, INITRD_MAX_ADDRESS_OFFSET: 0x%016llx\n", ++ max_addr, INITRD_MAX_ADDRESS_OFFSET); + + max_addr += INITRD_MAX_ADDRESS_OFFSET - 1; ++ grub_dprintf ("linux", "calling grub_efi_allocate_pages_real (0x%016lx, 0x%08x, EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA)", max_addr, initrd_pages); + +- return grub_efi_allocate_pages_real (max_addr, initrd_pages, +- GRUB_EFI_ALLOCATE_MAX_ADDRESS, +- GRUB_EFI_LOADER_DATA); ++ ret = grub_efi_allocate_pages_real (max_addr, initrd_pages, ++ GRUB_EFI_ALLOCATE_MAX_ADDRESS, ++ GRUB_EFI_LOADER_DATA); ++ grub_dprintf ("linux", "got 0x%016llx\n", (unsigned long long)ret); ++ return ret; + } + + static grub_err_t diff --git a/0004-cryptodisk-Support-key-protectors.patch b/0004-cryptodisk-Support-key-protectors.patch new file mode 100644 index 0000000..e5f2b87 --- /dev/null +++ b/0004-cryptodisk-Support-key-protectors.patch @@ -0,0 +1,342 @@ +From 9888bf40d960339a59dc18fb6e1df5f65b4668e3 Mon Sep 17 00:00:00 2001 +From: Hernan Gatta +Date: Tue, 1 Feb 2022 05:02:56 -0800 +Subject: [PATCH 13/14] cryptodisk: Support key protectors + +Add a new parameter to cryptomount to support the key protectors framework: -k. +The parameter is used to automatically retrieve a key from specified key +protectors. The parameter may be repeated to specify any number of key +protectors. These are tried in order until one provides a usable key for any +given disk. + +Signed-off-by: +--- + Makefile.util.def | 1 + + grub-core/disk/cryptodisk.c | 166 +++++++++++++++++++++++++++++------- + include/grub/cryptodisk.h | 14 +++ + 3 files changed, 151 insertions(+), 30 deletions(-) + +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -35,6 +35,7 @@ + common = grub-core/kern/list.c; + common = grub-core/kern/misc.c; + common = grub-core/kern/partition.c; ++ common = grub-core/kern/protectors.c; + common = grub-core/lib/crypto.c; + common = grub-core/lib/json/json.c; + common = grub-core/disk/luks.c; +--- a/grub-core/disk/cryptodisk.c ++++ b/grub-core/disk/cryptodisk.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + #ifdef GRUB_UTIL + #include +@@ -44,7 +45,8 @@ + OPTION_KEYFILE, + OPTION_KEYFILE_OFFSET, + OPTION_KEYFILE_SIZE, +- OPTION_HEADER ++ OPTION_HEADER, ++ OPTION_PROTECTOR + }; + + static const struct grub_arg_option options[] = +@@ -58,6 +60,8 @@ + {"keyfile-offset", 'O', 0, N_("Key file offset (bytes)"), 0, ARG_TYPE_INT}, + {"keyfile-size", 'S', 0, N_("Key file data size (bytes)"), 0, ARG_TYPE_INT}, + {"header", 'H', 0, N_("Read header from file"), 0, ARG_TYPE_STRING}, ++ {"protector", 'P', GRUB_ARG_OPTION_REPEATABLE, ++ N_("Unlock volume(s) using key protector(s)."), 0, ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; + +@@ -1061,6 +1065,7 @@ + grub_err_t ret = GRUB_ERR_NONE; + grub_cryptodisk_t dev; + grub_cryptodisk_dev_t cr; ++ int i; + struct cryptodisk_read_hook_ctx read_hook_data = {0}; + int askpass = 0; + char *part = NULL; +@@ -1113,41 +1118,112 @@ + goto error_no_close; + if (!dev) + continue; ++ break; ++ } + +- if (!cargs->key_len) +- { +- /* Get the passphrase from the user, if no key data. */ +- askpass = 1; +- part = grub_partition_get_name (source->partition); +- grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, +- source->partition != NULL ? "," : "", +- part != NULL ? part : N_("UNKNOWN"), +- dev->uuid); +- grub_free (part); +- +- cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); +- if (cargs->key_data == NULL) +- goto error_no_close; ++ if (dev == NULL) ++ { ++ grub_error (GRUB_ERR_BAD_MODULE, ++ "no cryptodisk module can handle this device"); ++ goto error_no_close; ++ } + +- if (!grub_password_get ((char *) cargs->key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE)) +- { +- grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied"); +- goto error; +- } +- cargs->key_len = grub_strlen ((char *) cargs->key_data); +- } ++ if (cargs->protectors) ++ { ++ for (i = 0; cargs->protectors[i]; i++) ++ { ++ if (cargs->key_cache[i].invalid) ++ continue; + +- ret = cr->recover_key (source, dev, cargs); +- if (ret != GRUB_ERR_NONE) +- goto error; ++ if (cargs->key_cache[i].key == NULL) ++ { ++ ret = grub_key_protector_recover_key (cargs->protectors[i], ++ &cargs->key_cache[i].key, ++ &cargs->key_cache[i].key_len); ++ if (ret != GRUB_ERR_NONE) ++ { ++ if (grub_errno) ++ { ++ grub_print_error (); ++ grub_errno = GRUB_ERR_NONE; ++ } ++ ++ grub_dprintf ("cryptodisk", ++ "failed to recover a key from key protector " ++ "%s, will not try it again for any other " ++ "disks, if any, during this invocation of " ++ "cryptomount\n", ++ cargs->protectors[i]); ++ ++ cargs->key_cache[i].invalid = 1; ++ continue; ++ } ++ } ++ ++ cargs->key_data = cargs->key_cache[i].key; ++ cargs->key_len = cargs->key_cache[i].key_len; ++ ++ ret = cr->recover_key (source, dev, cargs); ++ if (ret != GRUB_ERR_NONE) ++ { ++ part = grub_partition_get_name (source->partition); ++ grub_dprintf ("cryptodisk", ++ "recovered a key from key protector %s but it " ++ "failed to unlock %s%s%s (%s)\n", ++ cargs->protectors[i], source->name, ++ source->partition != NULL ? "," : "", ++ part != NULL ? part : N_("UNKNOWN"), dev->uuid); ++ grub_free (part); ++ continue; ++ } ++ else ++ { ++ ret = grub_cryptodisk_insert (dev, name, source); ++ if (ret != GRUB_ERR_NONE) ++ goto error; ++ goto cleanup; ++ } ++ } + +- ret = grub_cryptodisk_insert (dev, name, source); +- if (ret != GRUB_ERR_NONE) ++ part = grub_partition_get_name (source->partition); ++ grub_error (GRUB_ERR_ACCESS_DENIED, ++ N_("no key protector provided a usable key for %s%s%s (%s)"), ++ source->name, source->partition != NULL ? "," : "", ++ part != NULL ? part : N_("UNKNOWN"), dev->uuid); ++ grub_free (part); + goto error; ++ } ++ ++ if (!cargs->key_len) ++ { ++ /* Get the passphrase from the user, if no key data. */ ++ askpass = 1; ++ part = grub_partition_get_name (source->partition); ++ grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, ++ source->partition != NULL ? "," : "", ++ part != NULL ? part : N_("UNKNOWN"), dev->uuid); ++ grub_free (part); ++ ++ cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); ++ if (cargs->key_data == NULL) ++ goto error; ++ ++ if (!grub_password_get ((char *) cargs->key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE)) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied"); ++ goto error; ++ } ++ cargs->key_len = grub_strlen ((char *) cargs->key_data); ++ } ++ ++ ret = cr->recover_key (source, dev, cargs); ++ if (ret != GRUB_ERR_NONE) ++ goto error; ++ ++ ret = grub_cryptodisk_insert (dev, name, source); ++ if (ret != GRUB_ERR_NONE) ++ goto error; + +- goto cleanup; +- } +- grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk module can handle this device"); + goto cleanup; + + error: +@@ -1258,6 +1334,20 @@ + return ret; + } + ++static void ++grub_cryptodisk_clear_key_cache (struct grub_cryptomount_args *cargs) ++{ ++ int i; ++ ++ if (cargs->key_cache == NULL || cargs->protectors == NULL) ++ return; ++ ++ for (i = 0; cargs->protectors[i]; i++) ++ grub_free (cargs->key_cache[i].key); ++ ++ grub_free (cargs->key_cache); ++} ++ + static grub_err_t + grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) + { +@@ -1270,6 +1360,10 @@ + if (grub_cryptodisk_list == NULL) + return grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk modules loaded"); + ++ if (state[OPTION_PASSWORD].set && state[OPTION_PROTECTOR].set) /* password and key protector */ ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ "a password and a key protector cannot both be set"); ++ + if (state[OPTION_PASSWORD].set) /* password */ + { + cargs.key_data = (grub_uint8_t *) state[OPTION_PASSWORD].arg; +@@ -1362,6 +1456,15 @@ + return grub_errno; + } + ++ if (state[OPTION_PROTECTOR].set) /* key protector(s) */ ++ { ++ cargs.key_cache = grub_zalloc (state[OPTION_PROTECTOR].set * sizeof (*cargs.key_cache)); ++ if (cargs.key_cache == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "no memory for key protector key cache"); ++ cargs.protectors = state[OPTION_PROTECTOR].args; ++ } ++ + if (state[OPTION_UUID].set) /* uuid */ + { + int found_uuid; +@@ -1370,6 +1473,7 @@ + dev = grub_cryptodisk_get_by_uuid (args[0]); + if (dev) + { ++ grub_cryptodisk_clear_key_cache (&cargs); + grub_dprintf ("cryptodisk", + "already mounted as crypto%lu\n", dev->id); + return GRUB_ERR_NONE; +@@ -1378,6 +1482,7 @@ + cargs.check_boot = state[OPTION_BOOT].set; + cargs.search_uuid = args[0]; + found_uuid = grub_device_iterate (&grub_cryptodisk_scan_device, &cargs); ++ grub_cryptodisk_clear_key_cache (&cargs); + + if (found_uuid) + return GRUB_ERR_NONE; +@@ -1397,6 +1502,7 @@ + { + cargs.check_boot = state[OPTION_BOOT].set; + grub_device_iterate (&grub_cryptodisk_scan_device, &cargs); ++ grub_cryptodisk_clear_key_cache (&cargs); + return GRUB_ERR_NONE; + } + else +@@ -1420,6 +1526,7 @@ + disk = grub_disk_open (diskname); + if (!disk) + { ++ grub_cryptodisk_clear_key_cache (&cargs); + if (disklast) + *disklast = ')'; + return grub_errno; +@@ -1430,12 +1537,14 @@ + { + grub_dprintf ("cryptodisk", "already mounted as crypto%lu\n", dev->id); + grub_disk_close (disk); ++ grub_cryptodisk_clear_key_cache (&cargs); + if (disklast) + *disklast = ')'; + return GRUB_ERR_NONE; + } + + dev = grub_cryptodisk_scan_device_real (diskname, disk, &cargs); ++ grub_cryptodisk_clear_key_cache (&cargs); + + grub_disk_close (disk); + if (disklast) +@@ -1576,6 +1685,7 @@ + cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0, + N_("[ [-p password] | [-k keyfile" + " [-O keyoffset] [-S keysize] ] ] [-H file]" ++ " [-P protector [-P protector ...]]" + " "), + N_("Mount a crypto device."), options); + grub_procfs_register ("luks_script", &luks_script); +--- a/include/grub/cryptodisk.h ++++ b/include/grub/cryptodisk.h +@@ -70,6 +70,18 @@ + (*grub_cryptodisk_rekey_func_t) (struct grub_cryptodisk *dev, + grub_uint64_t zoneno); + ++struct grub_cryptomount_cached_key ++{ ++ grub_uint8_t *key; ++ grub_size_t key_len; ++ ++ /* ++ * The key protector associated with this cache entry failed, so avoid it ++ * even if the cached entry (an instance of this structure) is empty. ++ */ ++ int invalid; ++}; ++ + struct grub_cryptomount_args + { + /* scan: Flag to indicate that only bootable volumes should be decrypted */ +@@ -81,6 +93,10 @@ + /* recover_key: Length of key_data */ + grub_size_t key_len; + grub_file_t hdr_file; ++ /* recover_key: Names of the key protectors to use (NULL-terminated) */ ++ char **protectors; ++ /* recover_key: Key cache to avoid invoking the same key protector twice */ ++ struct grub_cryptomount_cached_key *key_cache; + }; + typedef struct grub_cryptomount_args *grub_cryptomount_args_t; + diff --git a/0004-diskfilter-look-up-cryptodisk-devices-first.patch b/0004-diskfilter-look-up-cryptodisk-devices-first.patch new file mode 100644 index 0000000..344023c --- /dev/null +++ b/0004-diskfilter-look-up-cryptodisk-devices-first.patch @@ -0,0 +1,89 @@ +From b7e2fb6a680447b7bb7eb18bb7570afa8d2b7f09 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Thu, 10 Aug 2023 10:19:29 +0800 +Subject: [PATCH 4/4] diskfilter: look up cryptodisk devices first + +When using disk auto-unlocking with TPM 2.0, the typical grub.cfg may +look like this: + + tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm + cryptomount -u -P tpm2 + search --fs-uuid --set=root + +Since the disk search order is based on the order of module loading, the +attacker could insert a malicious disk with the same FS-UUID root to +trick grub2 to boot into th malicious root and further dump memory to +steal the unsealed key. + +To defend such attack, we can specify the hint provided by 'grub-probe' +to search the encrypted partition first: + +search --fs-uuid --set=root --hint='cryptouuid/' + +However, for LVM on a encrypted partition, the search hint provided by +'grub-probe' is: + + --hint='lvmid//' + +It doesn't guarantee to look up the logical volume from the encrypted +partition, so the attacker may have the chance to fool grub2 to boot +into the malicious disk. + +To mininize the attack surface, this commit tweaks the disk device search +in diskfilter to look up cryptodisk devices first and then others, so +that the auto-unlocked disk will be found first, not the attacker's disk. + +Signed-off-by: Gary Lin +--- + grub-core/disk/diskfilter.c | 35 ++++++++++++++++++++++++++--------- + 1 file changed, 26 insertions(+), 9 deletions(-) + +diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c +index 61a311efd..94832c8dd 100644 +--- a/grub-core/disk/diskfilter.c ++++ b/grub-core/disk/diskfilter.c +@@ -226,15 +226,32 @@ scan_devices (const char *arname) + int need_rescan; + + for (pull = 0; pull < GRUB_DISK_PULL_MAX; pull++) +- for (p = grub_disk_dev_list; p; p = p->next) +- if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID +- && p->disk_iterate) +- { +- if ((p->disk_iterate) (scan_disk_hook, NULL, pull)) +- return; +- if (arname && is_lv_readable (find_lv (arname), 1)) +- return; +- } ++ { ++ /* look up the crytodisk devices first */ ++ for (p = grub_disk_dev_list; p; p = p->next) ++ if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID ++ && p->disk_iterate) ++ { ++ if ((p->disk_iterate) (scan_disk_hook, NULL, pull)) ++ return; ++ if (arname && is_lv_readable (find_lv (arname), 1)) ++ return; ++ break; ++ } ++ ++ /* check the devices other than crytodisk */ ++ for (p = grub_disk_dev_list; p; p = p->next) ++ if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) ++ continue; ++ else if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID ++ && p->disk_iterate) ++ { ++ if ((p->disk_iterate) (scan_disk_hook, NULL, pull)) ++ return; ++ if (arname && is_lv_readable (find_lv (arname), 1)) ++ return; ++ } ++ } + + scan_depth = 0; + need_rescan = 1; +-- +2.35.3 + diff --git a/0004-efinet-UEFI-IPv6-PXE-support.patch b/0004-efinet-UEFI-IPv6-PXE-support.patch new file mode 100644 index 0000000..2282609 --- /dev/null +++ b/0004-efinet-UEFI-IPv6-PXE-support.patch @@ -0,0 +1,116 @@ +From ca482c7c1efe5faf792bf0912a116ea8e0642e24 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Wed, 15 Apr 2015 14:48:30 +0800 +Subject: [PATCH 4/8] efinet: UEFI IPv6 PXE support + +When grub2 image is booted from UEFI IPv6 PXE, the DHCPv6 Reply packet is +cached in firmware buffer which can be obtained by PXE Base Code protocol. The +network interface can be setup through the parameters in that obtained packet. + +Signed-off-by: Michael Chang +Signed-off-by: Ken Lin +--- + grub-core/net/drivers/efi/efinet.c | 24 +++++++++++++---- + include/grub/efi/api.h | 55 +++++++++++++++++++++++++++++++++++++- + 2 files changed, 73 insertions(+), 6 deletions(-) + +--- a/grub-core/net/drivers/efi/efinet.c ++++ b/grub-core/net/drivers/efi/efinet.c +@@ -400,6 +400,18 @@ + continue; + pxe_mode = pxe->mode; + ++ if (pxe_mode->using_ipv6) ++ { ++ grub_net_configure_by_dhcpv6_reply (card->name, card, 0, ++ (struct grub_net_dhcp6_packet *) ++ &pxe_mode->dhcp_ack, ++ sizeof (pxe_mode->dhcp_ack), ++ 1, device, path); ++ if (grub_errno) ++ grub_print_error (); ++ } ++ else ++ { + inter = grub_net_configure_by_dhcp_ack (card->name, card, 0, + (struct grub_net_bootp_packet *) + &pxe_mode->dhcp_ack, +@@ -428,6 +440,7 @@ + vlan_dp = (grub_efi_device_path_t *) ((grub_efi_uint8_t *) vlan_dp + vlan_dp_len); + } + } ++ } + return; + } + } +--- a/include/grub/efi/api.h ++++ b/include/grub/efi/api.h +@@ -1523,14 +1523,67 @@ + + typedef grub_uint8_t grub_efi_pxe_packet_t[1472]; + ++typedef struct { ++ grub_uint8_t addr[4]; ++} grub_efi_pxe_ipv4_address_t; ++ ++typedef struct { ++ grub_uint8_t addr[16]; ++} grub_efi_pxe_ipv6_address_t; ++ ++typedef struct { ++ grub_uint8_t addr[32]; ++} grub_efi_pxe_mac_address_t; ++ ++typedef union { ++ grub_uint32_t addr[4]; ++ grub_efi_pxe_ipv4_address_t v4; ++ grub_efi_pxe_ipv6_address_t v6; ++} grub_efi_pxe_ip_address_t; ++ ++#define GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT 8 ++typedef struct { ++ grub_uint8_t filters; ++ grub_uint8_t ip_cnt; ++ grub_uint16_t reserved; ++ grub_efi_pxe_ip_address_t ip_list[GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT]; ++} grub_efi_pxe_ip_filter_t; ++ ++typedef struct { ++ grub_efi_pxe_ip_address_t ip_addr; ++ grub_efi_pxe_mac_address_t mac_addr; ++} grub_efi_pxe_arp_entry_t; ++ ++typedef struct { ++ grub_efi_pxe_ip_address_t ip_addr; ++ grub_efi_pxe_ip_address_t subnet_mask; ++ grub_efi_pxe_ip_address_t gw_addr; ++} grub_efi_pxe_route_entry_t; ++ ++ ++#define GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES 8 ++#define GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES 8 ++ + typedef struct grub_efi_pxe_mode + { +- grub_uint8_t unused[52]; ++ grub_uint8_t started; ++ grub_uint8_t ipv6_available; ++ grub_uint8_t ipv6_supported; ++ grub_uint8_t using_ipv6; ++ grub_uint8_t unused[16]; ++ grub_efi_pxe_ip_address_t station_ip; ++ grub_efi_pxe_ip_address_t subnet_mask; + grub_efi_pxe_packet_t dhcp_discover; + grub_efi_pxe_packet_t dhcp_ack; + grub_efi_pxe_packet_t proxy_offer; + grub_efi_pxe_packet_t pxe_discover; + grub_efi_pxe_packet_t pxe_reply; ++ grub_efi_pxe_packet_t pxe_bis_reply; ++ grub_efi_pxe_ip_filter_t ip_filter; ++ grub_uint32_t arp_cache_entries; ++ grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES]; ++ grub_uint32_t route_table_entries; ++ grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES]; + } grub_efi_pxe_mode_t; + + typedef struct grub_efi_pxe diff --git a/0004-fs-ntfs-Fix-an-OOB-read-when-parsing-bitmaps-for-ind.patch b/0004-fs-ntfs-Fix-an-OOB-read-when-parsing-bitmaps-for-ind.patch new file mode 100644 index 0000000..0c9fa91 --- /dev/null +++ b/0004-fs-ntfs-Fix-an-OOB-read-when-parsing-bitmaps-for-ind.patch @@ -0,0 +1,51 @@ +From 7a5a116739fa6d8a625da7d6b9272c9a2462f967 Mon Sep 17 00:00:00 2001 +From: Maxim Suhanov +Date: Mon, 28 Aug 2023 16:33:44 +0300 +Subject: [PATCH 4/6] fs/ntfs: Fix an OOB read when parsing bitmaps for index + attributes + +This fix introduces checks to ensure that bitmaps for directory indices +are never read beyond their actual sizes. + +The lack of this check is a minor issue, likely not exploitable in any way. + +Reported-by: Maxim Suhanov +Signed-off-by: Maxim Suhanov +Reviewed-by: Daniel Kiper +--- + grub-core/fs/ntfs.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c +index 2d78b96e1..bb70c89fb 100644 +--- a/grub-core/fs/ntfs.c ++++ b/grub-core/fs/ntfs.c +@@ -843,6 +843,25 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, + + if (is_resident) + { ++ if (bitmap_len > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) ++ { ++ grub_error (GRUB_ERR_BAD_FS, "resident bitmap too large"); ++ goto done; ++ } ++ ++ if (cur_pos >= at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) ++ { ++ grub_error (GRUB_ERR_BAD_FS, "resident bitmap out of range"); ++ goto done; ++ } ++ ++ if (u16at (cur_pos, 0x14) + u32at (cur_pos, 0x10) > ++ (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) cur_pos) ++ { ++ grub_error (GRUB_ERR_BAD_FS, "resident bitmap out of range"); ++ goto done; ++ } ++ + grub_memcpy (bmp, cur_pos + u16at (cur_pos, 0x14), + bitmap_len); + } +-- +2.42.0 + diff --git a/0004-ofpath-controller-name-update.patch b/0004-ofpath-controller-name-update.patch new file mode 100644 index 0000000..3ab01e5 --- /dev/null +++ b/0004-ofpath-controller-name-update.patch @@ -0,0 +1,28 @@ +From 7717cd9c27f18703287403af1a955588e3d0261f Mon Sep 17 00:00:00 2001 +From: mamatha +Date: Sat, 24 Sep 2022 11:22:39 +0530 +Subject: [PATCH 4/4] ofpath controller name update + +patch to update ofpath controller name + +Signed-off-by: mamatha +--- + grub-core/osdep/linux/ofpath.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/grub-core/osdep/linux/ofpath.c b/grub-core/osdep/linux/ofpath.c +index 212782d3f..7d31cfd0f 100644 +--- a/grub-core/osdep/linux/ofpath.c ++++ b/grub-core/osdep/linux/ofpath.c +@@ -483,6 +483,8 @@ of_path_get_nvmeof_adapter_info(char* sysfs_path, + buf3=strchr(buf2,'-')+1; + buf3=strchr(buf3,'-')+1; + nvmeof_info->target_wwpn = buf3; ++ buf3=strchr(buf3,'x')+1; ++ nvmeof_info->target_wwpn = buf3; + buf3 = strchr(nvmeof_info->target_wwpn,','); + *buf3 = '\0'; + +-- +2.35.3 + diff --git a/0004-tpm2-Support-authorized-policy.patch b/0004-tpm2-Support-authorized-policy.patch new file mode 100644 index 0000000..39c1391 --- /dev/null +++ b/0004-tpm2-Support-authorized-policy.patch @@ -0,0 +1,170 @@ +From 542c4fc6e067e04e8b96f798882ae968c59f4948 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Thu, 6 Apr 2023 16:00:25 +0800 +Subject: [PATCH v7 16/20] tpm2: Support authorized policy + +This commit handles the TPM2_PolicyAuthorize command from the key file +in TPM 2.0 Key File format. + +TPM2_PolicyAuthorize is the essential command to support authorized +policy which allows the users to sign TPM policies with their own keys. +Per TPM 2.0 Key File(*1), CommandPolicy for TPM2_PolicyAuthorize +comprises 'TPM2B_PUBLIC pubkey', 'TPM2B_DIGEST policy_ref', and +'TPMT_SIGNATURE signature'. To verify the signature, the current policy +digest is hashed with the hash algorithm written in 'signature', and then +'signature' is verified with the hashed policy digest and 'pubkey'. Once +TPM accepts 'signature', TPM2_PolicyAuthorize is invoked to authorize the +signed policy. + +To create the key file with authorized policy, here are the pcr-oracle(*2) +commands: + + # Generate the RSA key and create the authorized policy file + $ pcr-oracle \ + --rsa-generate-key \ + --private-key policy-key.pem \ + --auth authorized.policy \ + create-authorized-policy 0,2,4,7,9 + + # Seal the secret with the authorized policy + $ pcr-oracle \ + --key-format tpm2.0 \ + --auth authorized.policy \ + --input disk-secret.txt \ + --output sealed.key \ + seal-secret + + # Sign the predicted PCR policy + $ pcr-oracle \ + --key-format tpm2.0 \ + --private-key policy-key.pem \ + --from eventlog \ + --stop-event "grub-file=grub.cfg" \ + --after \ + --input sealed.key \ + --output sealed.tpm \ + sign 0,2,4,7.9 + +Then specify the key file and the key protector to grub.cfg in the EFI +system partition: + +tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm +cryptomount -u -P tpm2 + +For any change in the boot components, just run the 'sign' command again +to update the signature in sealed.tpm, and TPM can unseal the key file +with the updated PCR policy. + +(*1) https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html +(*2) https://github.com/okirch/pcr-oracle + +Signed-off-by: Gary Lin +--- + grub-core/tpm2/module.c | 84 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 84 insertions(+) + +diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c +index df0727215..0cbfd06e8 100644 +--- a/grub-core/tpm2/module.c ++++ b/grub-core/tpm2/module.c +@@ -453,6 +453,87 @@ grub_tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION session, + return GRUB_ERR_NONE; + } + ++static grub_err_t ++grub_tpm2_protector_policyauthorize (TPMI_SH_AUTH_SESSION session, ++ struct grub_tpm2_buffer *cmd_buf) ++{ ++ TPM2B_PUBLIC pubkey; ++ TPM2B_DIGEST policy_ref; ++ TPMT_SIGNATURE signature; ++ TPM2B_DIGEST pcr_policy; ++ TPM2B_DIGEST pcr_policy_hash; ++ TPMI_ALG_HASH sig_hash; ++ TPMT_TK_VERIFIED verification_ticket; ++ TPM_HANDLE pubkey_handle = 0; ++ TPM2B_NAME pubname; ++ TPM_RC rc; ++ grub_err_t err; ++ ++ grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (cmd_buf, &pubkey); ++ grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (cmd_buf, &policy_ref); ++ grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal (cmd_buf, &signature); ++ if (cmd_buf->error != 0) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Failed to unmarshal the buffer for TPM2_PolicyAuthorize")); ++ ++ /* Retrieve Policy Digest */ ++ rc = TPM2_PolicyGetDigest (session, NULL, &pcr_policy, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ return grub_error (GRUB_ERR_BAD_DEVICE, ++ N_("Failed to get policy digest (TPM2_PolicyGetDigest: 0x%x)."), ++ rc); ++ ++ /* Calculate the digest of the polcy for VerifySignature */ ++ sig_hash = TPMT_SIGNATURE_get_hash_alg (&signature); ++ if (sig_hash == TPM_ALG_NULL) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Failed to get the hash algorithm of the signature")); ++ ++ rc = TPM2_Hash (NULL, (TPM2B_MAX_BUFFER *)&pcr_policy, sig_hash, ++ TPM_RH_NULL, &pcr_policy_hash, NULL, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ return grub_error (GRUB_ERR_BAD_DEVICE, ++ N_("Failed to create PCR policy hash (TPM2_Hash: 0x%x)"), ++ rc); ++ ++ /* Load the public key */ ++ rc = TPM2_LoadExternal (NULL, NULL, &pubkey, TPM_RH_OWNER, ++ &pubkey_handle, &pubname, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ return grub_error (GRUB_ERR_BAD_DEVICE, ++ N_("Failed to load public key (TPM2_LoadExternal: 0x%x)"), ++ rc); ++ ++ /* Verify the signature against the public key and the policy digest */ ++ rc = TPM2_VerifySignature (pubkey_handle, NULL, &pcr_policy_hash, &signature, ++ &verification_ticket, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ { ++ err = grub_error (GRUB_ERR_BAD_DEVICE, ++ N_("Failed to verify signature (TPM2_VerifySignature: 0x%x)"), ++ rc); ++ goto error; ++ } ++ ++ /* Authorize the signed policy with the public key and the verification ticket */ ++ rc = TPM2_PolicyAuthorize (session, NULL, &pcr_policy, &policy_ref, &pubname, ++ &verification_ticket, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ { ++ err = grub_error (GRUB_ERR_BAD_DEVICE, ++ N_("Failed to authorize PCR policy (TPM2_PolicyAuthorize: 0x%x)"), ++ rc); ++ goto error; ++ } ++ ++ err = GRUB_ERR_NONE; ++ ++error: ++ TPM2_FlushContext (pubkey_handle); ++ ++ return err; ++} ++ + static grub_err_t + grub_tpm2_protector_enforce_policy (tpm2key_policy_t policy, TPMI_SH_AUTH_SESSION session) + { +@@ -472,6 +553,9 @@ grub_tpm2_protector_enforce_policy (tpm2key_policy_t policy, TPMI_SH_AUTH_SESSIO + case TPM_CC_PolicyPCR: + err = grub_tpm2_protector_policypcr (session, &buf); + break; ++ case TPM_CC_PolicyAuthorize: ++ err = grub_tpm2_protector_policyauthorize (session, &buf); ++ break; + default: + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Unknown TPM Command: 0x%x"), policy->cmd_code); +-- +2.35.3 + diff --git a/0005-docs-grub-Document-signing-grub-under-UEFI.patch b/0005-docs-grub-Document-signing-grub-under-UEFI.patch new file mode 100644 index 0000000..48dcbb7 --- /dev/null +++ b/0005-docs-grub-Document-signing-grub-under-UEFI.patch @@ -0,0 +1,59 @@ +From f7b9580133cf346d77f345d175fa5cb8a591be16 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Sat, 15 Aug 2020 02:00:57 +1000 +Subject: [PATCH 05/23] docs/grub: Document signing grub under UEFI + +Before adding information about how grub is signed with an appended +signature scheme, it's worth adding some information about how it +can currently be signed for UEFI. + +Signed-off-by: Daniel Axtens +--- + docs/grub.texi | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -6345,6 +6345,7 @@ + * Secure Boot Advanced Targeting:: Embedded information for generation number based revocation + * Measured Boot:: Measuring boot components + * Lockdown:: Lockdown when booting on a secure setup ++* Signing GRUB itself:: Ensuring the integrity of the GRUB core image + @end menu + + @node Authentication and authorisation +@@ -6423,7 +6424,7 @@ + + GRUB's @file{core.img} can optionally provide enforcement that all files + subsequently read from disk are covered by a valid digital signature. +-This document does @strong{not} cover how to ensure that your ++This section does @strong{not} cover how to ensure that your + platform's firmware (e.g., Coreboot) validates @file{core.img}. + + If environment variable @code{check_signatures} +@@ -6586,6 +6587,25 @@ + The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down. + Otherwise it does not exit. + ++@node Signing GRUB itself ++@section Signing GRUB itself ++ ++To ensure a complete secure-boot chain, there must be a way for the code that ++loads GRUB to verify the integrity of the core image. ++ ++This is ultimately platform-specific and individual platforms can define their ++own mechanisms. However, there are general-purpose mechanisms that can be used ++with GRUB. ++ ++@section Signing GRUB for UEFI secure boot ++ ++On UEFI platforms, @file{core.img} is a PE binary. Therefore, it can be signed ++with a tool such as @command{pesign} or @command{sbsign}. Refer to the ++suggestions in @pxref{UEFI secure boot and shim} to ensure that the final ++image works under UEFI secure boot and can maintain the secure-boot chain. It ++will also be necessary to enrol the public key used into a relevant firmware ++key database. ++ + @node Platform limitations + @chapter Platform limitations + diff --git a/0005-export-environment-at-start-up.patch b/0005-export-environment-at-start-up.patch new file mode 100644 index 0000000..dc23832 --- /dev/null +++ b/0005-export-environment-at-start-up.patch @@ -0,0 +1,164 @@ +From 496b6b20cbce3fc27228d1d8290089fb7107b8de Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Fri, 18 Feb 2022 21:51:16 +0800 +Subject: [PATCH 5/5] export environment at start up + +If the prep_loadenv module is built into the core image, it will read +the environment block automatically during start up and export all +variables. The will ease integration with those without early scripts to +running the command. + +Signed-off-by: Michael Chang +--- + grub-core/Makefile.core.def | 2 + + grub-core/commands/prep_loadenv.c | 77 +++++++++++++++++++++++++++++++ + grub-core/kern/env.c | 2 + + grub-core/kern/main.c | 3 ++ + include/grub/env.h | 1 + + 5 files changed, 85 insertions(+) + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2678,4 +2678,6 @@ + name = prep_loadenv; + common = commands/prep_loadenv.c; + enable = powerpc_ieee1275; ++ cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; ++ cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)'; + }; +--- a/grub-core/commands/prep_loadenv.c ++++ b/grub-core/commands/prep_loadenv.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -185,6 +186,65 @@ + } + + static grub_err_t ++boot_disk_prep_partname (char **name) ++{ ++ regex_t regex; ++ int ret; ++ grub_size_t s; ++ char *comperr; ++ const char *cmdpath; ++ regmatch_t *matches = NULL; ++ grub_err_t err = GRUB_ERR_NONE; ++ ++ *name = NULL; ++ ++ cmdpath = grub_env_get ("cmdpath"); ++ if (!cmdpath) ++ return GRUB_ERR_NONE; ++ ++ ret = regcomp (®ex, "\\(([^,]+)(,?.*)?\\)(.*)", REG_EXTENDED); ++ if (ret) ++ goto fail; ++ ++ matches = grub_calloc (regex.re_nsub + 1, sizeof (*matches)); ++ if (! matches) ++ goto fail; ++ ++ ret = regexec (®ex, cmdpath, regex.re_nsub + 1, matches, 0); ++ if (!ret) ++ { ++ char *devname = devname = match_substr (matches + 1, cmdpath); ++ if (!devname) ++ { ++ err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "%s contains no disk name", cmdpath); ++ goto out; ++ } ++ ++ err = prep_partname (devname, name); ++ out: ++ grub_free (devname); ++ regfree (®ex); ++ grub_free (matches); ++ return err; ++ } ++ ++ fail: ++ grub_free (matches); ++ s = regerror (ret, ®ex, 0, 0); ++ comperr = grub_malloc (s); ++ if (!comperr) ++ { ++ regfree (®ex); ++ return grub_errno; ++ } ++ regerror (ret, ®ex, comperr, s); ++ err = grub_error (GRUB_ERR_TEST_FAILURE, "%s", comperr); ++ regfree (®ex); ++ grub_free (comperr); ++ return err; ++} ++ ++static grub_err_t + grub_cmd_prep_loadenv (grub_command_t cmd __attribute__ ((unused)), + int argc, + char **argv) +@@ -214,10 +274,27 @@ + return GRUB_ERR_NONE; + } + ++static void ++early_prep_loadenv (void) ++{ ++ grub_err_t err; ++ char *prep; ++ ++ err = boot_disk_prep_partname (&prep); ++ if (err == GRUB_ERR_NONE && prep) ++ err = prep_read_envblk (prep); ++ if (err == GRUB_ERR_BAD_FILE_TYPE || err == GRUB_ERR_FILE_NOT_FOUND) ++ grub_error_pop (); ++ if (err != GRUB_ERR_NONE) ++ grub_print_error (); ++ grub_free (prep); ++} ++ + static grub_command_t cmd_prep_load; + + GRUB_MOD_INIT(prep_loadenv) + { ++ early_env_hook = early_prep_loadenv; + cmd_prep_load = + grub_register_command("prep_load_env", grub_cmd_prep_loadenv, + "DEVICE", +--- a/grub-core/kern/env.c ++++ b/grub-core/kern/env.c +@@ -28,6 +28,8 @@ + /* The current context. */ + struct grub_env_context *grub_current_context = &initial_context; + ++void (*early_env_hook) (void) = NULL; ++ + /* Return the hash representation of the string S. */ + static unsigned int + grub_env_hashval (const char *s) +--- a/grub-core/kern/main.c ++++ b/grub-core/kern/main.c +@@ -309,6 +309,9 @@ + + grub_boot_time ("Before execution of embedded config."); + ++ if (early_env_hook != NULL) ++ early_env_hook (); ++ + if (load_config) + grub_parser_execute (load_config); + +--- a/include/grub/env.h ++++ b/include/grub/env.h +@@ -69,5 +69,6 @@ + grub_err_t + grub_env_extractor_close (int source); + ++extern void (*EXPORT_VAR (early_env_hook)) (void); + + #endif /* ! GRUB_ENV_HEADER */ diff --git a/0005-fs-ntfs-Fix-an-OOB-read-when-parsing-a-volume-label.patch b/0005-fs-ntfs-Fix-an-OOB-read-when-parsing-a-volume-label.patch new file mode 100644 index 0000000..c24570a --- /dev/null +++ b/0005-fs-ntfs-Fix-an-OOB-read-when-parsing-a-volume-label.patch @@ -0,0 +1,61 @@ +From 1fe82c41e070385e273d7bb1cfb482627a3c28e8 Mon Sep 17 00:00:00 2001 +From: Maxim Suhanov +Date: Mon, 28 Aug 2023 16:38:19 +0300 +Subject: [PATCH 5/6] fs/ntfs: Fix an OOB read when parsing a volume label + +This fix introduces checks to ensure that an NTFS volume label is always +read from the corresponding file record segment. + +The current NTFS code allows the volume label string to be read from an +arbitrary, attacker-chosen memory location. However, the bytes read are +always treated as UTF-16LE. So, the final string displayed is mostly +unreadable and it can't be easily converted back to raw bytes. + +The lack of this check is a minor issue, likely not causing a significant +data leak. + +Reported-by: Maxim Suhanov +Signed-off-by: Maxim Suhanov +Reviewed-by: Daniel Kiper +--- + grub-core/fs/ntfs.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c +index bb70c89fb..ff5e3740f 100644 +--- a/grub-core/fs/ntfs.c ++++ b/grub-core/fs/ntfs.c +@@ -1213,13 +1213,29 @@ grub_ntfs_label (grub_device_t device, char **label) + + init_attr (&mft->attr, mft); + pa = find_attr (&mft->attr, GRUB_NTFS_AT_VOLUME_NAME); ++ ++ if (pa >= mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR)) ++ { ++ grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label"); ++ goto fail; ++ } ++ ++ if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa < 0x16) ++ { ++ grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label"); ++ goto fail; ++ } ++ + if ((pa) && (pa[8] == 0) && (u32at (pa, 0x10))) + { + int len; + + len = u32at (pa, 0x10) / 2; + pa += u16at (pa, 0x14); +- *label = get_utf8 (pa, len); ++ if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa >= 2 * len) ++ *label = get_utf8 (pa, len); ++ else ++ grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label"); + } + + fail: +-- +2.42.0 + diff --git a/0005-grub.texi-Add-net_bootp6-doument.patch b/0005-grub.texi-Add-net_bootp6-doument.patch new file mode 100644 index 0000000..4382f40 --- /dev/null +++ b/0005-grub.texi-Add-net_bootp6-doument.patch @@ -0,0 +1,48 @@ +From 2c997c8c058b41d3a59a81f2bf654288b7cdf8f2 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Tue, 5 May 2015 14:19:24 +0800 +Subject: [PATCH 5/8] grub.texi: Add net_bootp6 doument + +Update grub documentation for net_bootp6 command. + +Signed-off-by: Michael Chang +Signed-off-by: Ken Lin +--- + docs/grub.texi | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -5894,6 +5894,7 @@ + * net_add_dns:: Add a DNS server + * net_add_route:: Add routing entry + * net_bootp:: Perform a bootp/DHCP autoconfiguration ++* net_bootp6:: Perform a DHCPv6 autoconfiguration + * net_del_addr:: Remove IP address from interface + * net_del_dns:: Remove a DNS server + * net_del_route:: Remove a route entry +@@ -5951,6 +5952,24 @@ + + @end deffn + ++ ++@node net_bootp6 ++@subsection net_bootp6 ++ ++@deffn Command net_bootp6 [@var{card}] ++Perform configuration of @var{card} using DHCPv6 protocol. If no card name is ++specified, try to configure all existing cards. If configuration was ++successful, interface with name @var{card}@samp{:dhcp6} and configured address ++is added to @var{card}. ++ ++@table @samp ++@item 1 (Domain Name Server) ++Adds all servers from option value to the list of servers used during name ++resolution. ++@end table ++ ++@end deffn ++ + + @node net_del_addr + @subsection net_del_addr diff --git a/0005-util-grub-protect-Add-new-tool.patch b/0005-util-grub-protect-Add-new-tool.patch new file mode 100644 index 0000000..5ff71ed --- /dev/null +++ b/0005-util-grub-protect-Add-new-tool.patch @@ -0,0 +1,1620 @@ +From 1116bc4b9a27aceaec53421e89eb887e6ad3aef8 Mon Sep 17 00:00:00 2001 +From: Hernan Gatta +Date: Tue, 1 Feb 2022 05:02:57 -0800 +Subject: [PATCH v7 12/20] util/grub-protect: Add new tool + +To utilize the key protectors framework, there must be a way to protect +full-disk encryption keys in the first place. The grub-protect tool +includes support for the TPM2 key protector but other protectors that +require setup ahead of time can be supported in the future. + +For the TPM2 key protector, the intended flow is for a user to have a +LUKS 1 or LUKS 2-protected fully-encrypted disk. The user then creates a +new LUKS key file, say by reading /dev/urandom into a file, and creates +a new LUKS key slot for this key. Then, the user invokes the grub-protect +tool to seal this key file to a set of PCRs using the system's TPM 2.0. +The resulting sealed key file is stored in an unencrypted partition such +as the EFI System Partition (ESP) so that GRUB may read it. The user also +has to ensure the cryptomount command is included in GRUB's boot script +and that it carries the requisite key protector (-P) parameter. + +Sample usage: + +$ dd if=/dev/urandom of=luks-key bs=1 count=32 +$ sudo cryptsetup luksAddKey /dev/sdb1 luks-key --pbkdf=pbkdf2 --hash=sha512 + +To seal the key with TPM 2.0 Key File (recommended): + +$ sudo grub-protect --action=add \ + --protector=tpm2 \ + --tpm2key \ + --tpm2-keyfile=luks-key \ + --tpm2-outfile=/boot/efi/boot/grub2/sealed.tpm + +Or, to seal the key with the raw sealed key: + +$ sudo grub-protect --action=add \ + --protector=tpm2 \ + --tpm2-keyfile=luks-key \ + --tpm2-outfile=/boot/efi/boot/grub2/sealed.key + +Then, in the boot script, for TPM 2.0 Key File: + +tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm +cryptomount -u -P tpm2 + +Or, for the raw sealed key: + +tpm2_key_protector_init --keyfile=(hd0,gpt1)/boot/grub2/sealed.key +cryptomount -u -P tpm2 + +Signed-off-by: Hernan Gatta +Signed-off-by: Gary Lin +--- + Makefile.util.def | 22 + + configure.ac | 9 + + util/grub-protect.c | 1492 +++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 1525 insertions(+) + create mode 100644 util/grub-protect.c + +diff --git a/Makefile.util.def b/Makefile.util.def +index e89abb38f..f43c223b9 100644 +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -207,6 +207,28 @@ program = { + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + }; + ++program = { ++ name = grub-protect; ++ ++ common = grub-core/osdep/init.c; ++ common = grub-core/tpm2/args.c; ++ common = grub-core/tpm2/buffer.c; ++ common = grub-core/tpm2/mu.c; ++ common = grub-core/tpm2/tpm2.c; ++ common = grub-core/tpm2/tpm2key_asn1_tab.c; ++ common = util/grub-protect.c; ++ common = util/probe.c; ++ ++ ldadd = libgrubmods.a; ++ ldadd = libgrubgcry.a; ++ ldadd = libgrubkern.a; ++ ldadd = grub-core/lib/gnulib/libgnu.a; ++ ldadd = '$(LIBTASN1)'; ++ ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; ++ ++ enable = efi; ++}; ++ + program = { + name = grub-mkrelpath; + mansection = 1; +diff --git a/configure.ac b/configure.ac +index c19779c14..9796e5f9b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -76,6 +76,7 @@ grub_TRANSFORM([grub-mkpasswd-pbkdf2]) + grub_TRANSFORM([grub-mkrelpath]) + grub_TRANSFORM([grub-mkrescue]) + grub_TRANSFORM([grub-probe]) ++grub_TRANSFORM([grub-protect]) + grub_TRANSFORM([grub-reboot]) + grub_TRANSFORM([grub-script-check]) + grub_TRANSFORM([grub-set-default]) +@@ -2018,6 +2019,14 @@ fi + AC_SUBST([LIBZFS]) + AC_SUBST([LIBNVPAIR]) + ++LIBTASN1= ++if test x"$platform" = xefi; then ++ AC_CHECK_LIB([tasn1], [asn1_write_value], [], ++ [AC_MSG_ERROR([Your platform requires libtasn1])]) ++ LIBTASN1="-ltasn1" ++fi ++AC_SUBST([LIBTASN1]) ++ + LIBS="" + + AC_SUBST([FONT_SOURCE]) +diff --git a/util/grub-protect.c b/util/grub-protect.c +new file mode 100644 +index 000000000..c6d41ea40 +--- /dev/null ++++ b/util/grub-protect.c +@@ -0,0 +1,1492 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Microsoft Corporation ++ * Copyright (C) 2023 SUSE LLC ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#pragma GCC diagnostic ignored "-Wmissing-prototypes" ++#pragma GCC diagnostic ignored "-Wmissing-declarations" ++#include ++#pragma GCC diagnostic error "-Wmissing-prototypes" ++#pragma GCC diagnostic error "-Wmissing-declarations" ++ ++#include "progname.h" ++ ++/* Unprintable option keys for argp */ ++typedef enum grub_protect_opt ++{ ++ /* General */ ++ GRUB_PROTECT_OPT_ACTION = 'a', ++ GRUB_PROTECT_OPT_PROTECTOR = 'p', ++ /* TPM2 */ ++ GRUB_PROTECT_OPT_TPM2_DEVICE = 0x100, ++ GRUB_PROTECT_OPT_TPM2_PCRS, ++ GRUB_PROTECT_OPT_TPM2_ASYMMETRIC, ++ GRUB_PROTECT_OPT_TPM2_BANK, ++ GRUB_PROTECT_OPT_TPM2_SRK, ++ GRUB_PROTECT_OPT_TPM2_KEYFILE, ++ GRUB_PROTECT_OPT_TPM2_OUTFILE, ++ GRUB_PROTECT_OPT_TPM2_EVICT, ++ GRUB_PROTECT_OPT_TPM2_TPM2KEY ++} grub_protect_opt; ++ ++/* Option flags to keep track of specified arguments */ ++typedef enum grub_protect_arg ++{ ++ /* General */ ++ GRUB_PROTECT_ARG_ACTION = 1 << 0, ++ GRUB_PROTECT_ARG_PROTECTOR = 1 << 1, ++ /* TPM2 */ ++ GRUB_PROTECT_ARG_TPM2_DEVICE = 1 << 2, ++ GRUB_PROTECT_ARG_TPM2_PCRS = 1 << 3, ++ GRUB_PROTECT_ARG_TPM2_ASYMMETRIC = 1 << 4, ++ GRUB_PROTECT_ARG_TPM2_BANK = 1 << 5, ++ GRUB_PROTECT_ARG_TPM2_SRK = 1 << 6, ++ GRUB_PROTECT_ARG_TPM2_KEYFILE = 1 << 7, ++ GRUB_PROTECT_ARG_TPM2_OUTFILE = 1 << 8, ++ GRUB_PROTECT_ARG_TPM2_EVICT = 1 << 9, ++ GRUB_PROTECT_ARG_TPM2_TPM2KEY = 1 << 10 ++} grub_protect_arg_t; ++ ++typedef enum grub_protect_protector ++{ ++ GRUB_PROTECT_TYPE_ERROR, ++ GRUB_PROTECT_TYPE_TPM2 ++} grub_protect_protector_t; ++ ++typedef enum grub_protect_action ++{ ++ GRUB_PROTECT_ACTION_ERROR, ++ GRUB_PROTECT_ACTION_ADD, ++ GRUB_PROTECT_ACTION_REMOVE ++} grub_protect_action_t; ++ ++struct grub_protect_args ++{ ++ grub_protect_arg_t args; ++ grub_protect_action_t action; ++ grub_protect_protector_t protector; ++ ++ const char *tpm2_device; ++ grub_uint8_t tpm2_pcrs[TPM_MAX_PCRS]; ++ grub_uint8_t tpm2_pcr_count; ++ TPM_ALG_ID tpm2_asymmetric; ++ TPM_KEY_BITS rsa_bits; ++ TPM_ECC_CURVE ecc_curve; ++ TPM_ALG_ID tpm2_bank; ++ TPM_HANDLE tpm2_srk; ++ const char *tpm2_keyfile; ++ const char *tpm2_outfile; ++ int tpm2_evict; ++ int tpm2_tpm2key; ++}; ++ ++static struct argp_option grub_protect_options[] = ++ { ++ /* Top-level options */ ++ { ++ .name = "action", ++ .key = 'a', ++ .arg = "add|remove", ++ .flags = 0, ++ .doc = ++ N_("Add or remove a key protector to or from a key."), ++ .group = 0 ++ }, ++ { ++ .name = "protector", ++ .key = 'p', ++ .arg = "tpm2", ++ .flags = 0, ++ .doc = ++ N_("Key protector to use (only tpm2 is currently supported)."), ++ .group = 0 ++ }, ++ /* TPM2 key protector options */ ++ { ++ .name = "tpm2-device", ++ .key = GRUB_PROTECT_OPT_TPM2_DEVICE, ++ .arg = "FILE", ++ .flags = 0, ++ .doc = ++ N_("Path to the TPM2 device (default is /dev/tpm0)."), ++ .group = 0 ++ }, ++ { ++ .name = "tpm2-pcrs", ++ .key = GRUB_PROTECT_OPT_TPM2_PCRS, ++ .arg = "0[,1]...", ++ .flags = 0, ++ .doc = ++ N_("Comma-separated list of PCRs used to authorize key release " ++ "(e.g., '7,11', default is 7."), ++ .group = 0 ++ }, ++ { ++ .name = "tpm2-bank", ++ .key = GRUB_PROTECT_OPT_TPM2_BANK, ++ .arg = "ALG", ++ .flags = 0, ++ .doc = ++ N_("Bank of PCRs used to authorize key release: " ++ "SHA1, SHA256 (default), or SHA512."), ++ .group = 0 ++ }, ++ { ++ .name = "tpm2-keyfile", ++ .key = GRUB_PROTECT_OPT_TPM2_KEYFILE, ++ .arg = "FILE", ++ .flags = 0, ++ .doc = ++ N_("Path to a file that contains the cleartext key to protect."), ++ .group = 0 ++ }, ++ { ++ .name = "tpm2-outfile", ++ .key = GRUB_PROTECT_OPT_TPM2_OUTFILE, ++ .arg = "FILE", ++ .flags = 0, ++ .doc = ++ N_("Path to the file that will contain the key after sealing (must be " ++ "accessible to GRUB during boot)."), ++ .group = 0 ++ }, ++ { ++ .name = "tpm2-srk", ++ .key = GRUB_PROTECT_OPT_TPM2_SRK, ++ .arg = "NUM", ++ .flags = 0, ++ .doc = ++ N_("The SRK handle if the SRK is to be made persistent."), ++ .group = 0 ++ }, ++ { ++ .name = "tpm2-asymmetric", ++ .key = GRUB_PROTECT_OPT_TPM2_ASYMMETRIC, ++ .arg = "TYPE", ++ .flags = 0, ++ .doc = ++ N_("The type of SRK: RSA (RSA2048), RSA3072, RSA4096, " ++ "ECC (ECC_NIST_P256), ECC_NIST_P384, ECC_NIST_P521, " ++ "ECC_BN_P256, ECC_BN_P638, and ECC_SM2_P256. " ++ "(default is RSA2048)"), ++ .group = 0 ++ }, ++ { ++ .name = "tpm2-evict", ++ .key = GRUB_PROTECT_OPT_TPM2_EVICT, ++ .arg = NULL, ++ .flags = 0, ++ .doc = ++ N_("Evict a previously persisted SRK from the TPM, if any."), ++ .group = 0 ++ }, ++ { ++ .name = "tpm2key", ++ .key = GRUB_PROTECT_OPT_TPM2_TPM2KEY, ++ .arg = NULL, ++ .flags = 0, ++ .doc = ++ N_("Use TPM 2.0 Key File format instead of the raw format."), ++ .group = 0 ++ }, ++ /* End of list */ ++ { 0, 0, 0, 0, 0, 0 } ++ }; ++ ++static int grub_protector_tpm2_fd = -1; ++ ++static grub_err_t ++grub_protect_read_file (const char *filepath, void **buffer, ++ size_t *buffer_size) ++{ ++ grub_err_t err; ++ FILE *f; ++ long len; ++ void *buf; ++ ++ f = fopen (filepath, "rb"); ++ if (f == NULL) ++ return GRUB_ERR_FILE_NOT_FOUND; ++ ++ if (fseek (f, 0, SEEK_END)) ++ { ++ err = GRUB_ERR_FILE_READ_ERROR; ++ goto exit1; ++ } ++ ++ len = ftell (f); ++ if (len == 0) ++ { ++ err = GRUB_ERR_FILE_READ_ERROR; ++ goto exit1; ++ } ++ ++ rewind (f); ++ ++ buf = grub_malloc (len); ++ if (buf == NULL) ++ { ++ err = GRUB_ERR_OUT_OF_MEMORY; ++ goto exit1; ++ } ++ ++ if (fread (buf, len, 1, f) != 1) ++ { ++ err = GRUB_ERR_FILE_READ_ERROR; ++ goto exit2; ++ } ++ ++ *buffer = buf; ++ *buffer_size = len; ++ ++ buf = NULL; ++ err = GRUB_ERR_NONE; ++ ++exit2: ++ grub_free (buf); ++ ++exit1: ++ fclose (f); ++ ++ return err; ++} ++ ++static grub_err_t ++grub_protect_write_file (const char *filepath, void *buffer, size_t buffer_size) ++{ ++ grub_err_t err; ++ FILE *f; ++ ++ f = fopen (filepath, "wb"); ++ if (f == NULL) ++ return GRUB_ERR_FILE_NOT_FOUND; ++ ++ if (fwrite (buffer, buffer_size, 1, f) != 1) ++ { ++ err = GRUB_ERR_WRITE_ERROR; ++ goto exit1; ++ } ++ ++ err = GRUB_ERR_NONE; ++ ++exit1: ++ fclose (f); ++ ++ return err; ++} ++ ++static grub_err_t ++grub_protect_get_grub_drive_for_file (const char *filepath, char **drive) ++{ ++ grub_err_t err = GRUB_ERR_IO; ++ char *disk; ++ char **devices; ++ char *grub_dev; ++ char *grub_path; ++ char *efi_drive; ++ char *partition; ++ char *grub_drive; ++ grub_device_t dev; ++ grub_size_t grub_drive_len; ++ int n; ++ ++ grub_path = grub_canonicalize_file_name (filepath); ++ if (grub_path == NULL) ++ goto exit1; ++ ++ devices = grub_guess_root_devices (grub_path); ++ if (devices == NULL || devices[0] == NULL) ++ goto exit2; ++ ++ disk = devices[0]; ++ ++ grub_util_pull_device (disk); ++ ++ grub_dev = grub_util_get_grub_dev (disk); ++ if (grub_dev == NULL) ++ goto exit3; ++ ++ dev = grub_device_open (grub_dev); ++ if (dev == NULL) ++ goto exit4; ++ ++ efi_drive = grub_util_guess_efi_drive (disk); ++ if (efi_drive == NULL) ++ goto exit5; ++ ++ partition = grub_partition_get_name (dev->disk->partition); ++ if (partition == NULL) ++ goto exit6; ++ ++ grub_drive_len = grub_strlen (efi_drive) + grub_strlen (partition) + 3; ++ grub_drive = grub_malloc (grub_drive_len + 1); ++ if (grub_drive == NULL) ++ goto exit7; ++ ++ n = grub_snprintf (grub_drive, grub_drive_len + 1, "(%s,%s)", efi_drive, ++ partition); ++ if (n != grub_drive_len) ++ goto exit8; ++ ++ *drive = grub_drive; ++ grub_drive = NULL; ++ err = GRUB_ERR_NONE; ++ ++exit8: ++ grub_free (grub_drive); ++ ++exit7: ++ grub_free (partition); ++ ++exit6: ++ grub_free (efi_drive); ++ ++exit5: ++ grub_device_close (dev); ++ ++exit4: ++ grub_free (grub_dev); ++ ++exit3: ++ grub_free (devices); ++ ++exit2: ++ grub_free (grub_path); ++ ++exit1: ++ return err; ++} ++ ++grub_err_t ++grub_tcg2_get_max_output_size (grub_size_t *size) ++{ ++ if (size == NULL) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ *size = GRUB_TPM2_BUFFER_CAPACITY; ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_tcg2_submit_command (grub_size_t input_size, grub_uint8_t *input, ++ grub_size_t output_size, grub_uint8_t *output) ++{ ++ static const grub_size_t header_size = sizeof (grub_uint16_t) + ++ (2 * sizeof(grub_uint32_t)); ++ ++ if (write (grub_protector_tpm2_fd, input, input_size) != input_size) ++ return GRUB_ERR_BAD_DEVICE; ++ ++ if (read (grub_protector_tpm2_fd, output, output_size) < header_size) ++ return GRUB_ERR_BAD_DEVICE; ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_protect_tpm2_open_device (const char *dev_node) ++{ ++ if (grub_protector_tpm2_fd != -1) ++ return GRUB_ERR_NONE; ++ ++ grub_protector_tpm2_fd = open (dev_node, O_RDWR); ++ if (grub_protector_tpm2_fd == -1) ++ { ++ fprintf (stderr, _("Could not open TPM device (Error: %u).\n"), errno); ++ return GRUB_ERR_FILE_NOT_FOUND; ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_protect_tpm2_close_device (void) ++{ ++ int err; ++ ++ if (grub_protector_tpm2_fd == -1) ++ return GRUB_ERR_NONE; ++ ++ err = close (grub_protector_tpm2_fd); ++ if (err != GRUB_ERR_NONE) ++ { ++ fprintf (stderr, _("Could not close TPM device (Error: %u).\n"), errno); ++ return GRUB_ERR_IO; ++ } ++ ++ grub_protector_tpm2_fd = -1; ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_protect_tpm2_get_policy_digest (struct grub_protect_args *args, ++ TPM2B_DIGEST *digest) ++{ ++ TPM_RC rc; ++ TPML_PCR_SELECTION pcr_sel = { ++ .count = 1, ++ .pcrSelections = { ++ { ++ .hash = args->tpm2_bank, ++ .sizeOfSelect = 3, ++ .pcrSelect = { 0 } ++ }, ++ } ++ }; ++ TPML_PCR_SELECTION pcr_sel_out = { 0 }; ++ TPML_DIGEST pcr_values = { 0 }; ++ grub_uint8_t *pcr_digest; ++ grub_size_t pcr_digest_len; ++ grub_uint8_t *pcr_concat; ++ grub_size_t pcr_concat_len; ++ grub_uint8_t *pcr_cursor; ++ const gcry_md_spec_t *hash_spec; ++ TPM2B_NONCE nonce = { 0 }; ++ TPM2B_ENCRYPTED_SECRET salt = { 0 }; ++ TPMT_SYM_DEF symmetric = { 0 }; ++ TPMI_SH_AUTH_SESSION session = 0; ++ TPM2B_DIGEST pcr_digest_in = { ++ .size = TPM_SHA256_DIGEST_SIZE, ++ .buffer = { 0 } ++ }; ++ TPM2B_DIGEST policy_digest = { 0 }; ++ grub_uint8_t i; ++ grub_err_t err; ++ ++ /* PCR Read */ ++ for (i = 0; i < args->tpm2_pcr_count; i++) ++ TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], args->tpm2_pcrs[i]); ++ ++ rc = TPM2_PCR_Read (NULL, &pcr_sel, NULL, &pcr_sel_out, &pcr_values, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ { ++ fprintf (stderr, _("Failed to read PCRs (TPM2_PCR_Read: 0x%x).\n"), rc); ++ return GRUB_ERR_BAD_DEVICE; ++ } ++ ++ if ((pcr_sel_out.count != pcr_sel.count) || ++ (pcr_sel.pcrSelections[0].sizeOfSelect != ++ pcr_sel_out.pcrSelections[0].sizeOfSelect)) ++ { ++ fprintf (stderr, _("Could not read all the specified PCRs.\n")); ++ return GRUB_ERR_BAD_DEVICE; ++ } ++ ++ /* Compute PCR Digest */ ++ switch (args->tpm2_bank) ++ { ++ case TPM_ALG_SHA1: ++ pcr_digest_len = TPM_SHA1_DIGEST_SIZE; ++ hash_spec = GRUB_MD_SHA1; ++ break; ++ case TPM_ALG_SHA256: ++ pcr_digest_len = TPM_SHA256_DIGEST_SIZE; ++ hash_spec = GRUB_MD_SHA256; ++ break; ++ case TPM_ALG_SHA512: ++ pcr_digest_len = TPM_SHA512_DIGEST_SIZE; ++ hash_spec = GRUB_MD_SHA512; ++ break; ++ /* Although SHA384 can be parsed by grub_tpm2_protector_parse_bank(), ++ it's not supported by the built-in libgcrypt, and we won't be able to ++ calculate the PCR digest, so SHA384 is marked as unsupported. */ ++ default: ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ pcr_digest = grub_malloc (pcr_digest_len); ++ if (!pcr_digest) ++ { ++ fprintf (stderr, _("Failed to allocate PCR digest buffer.\n")); ++ return GRUB_ERR_OUT_OF_MEMORY; ++ } ++ ++ pcr_concat_len = pcr_digest_len * args->tpm2_pcr_count; ++ pcr_concat = grub_malloc (pcr_concat_len); ++ if (pcr_concat == NULL) ++ { ++ err = GRUB_ERR_OUT_OF_MEMORY; ++ fprintf (stderr, _("Failed to allocate PCR concatenation buffer.\n")); ++ goto exit1; ++ } ++ ++ pcr_cursor = pcr_concat; ++ for (i = 0; i < args->tpm2_pcr_count; i++) ++ { ++ if (pcr_values.digests[i].size != pcr_digest_len) ++ { ++ fprintf (stderr, ++ _("Bad PCR value size: expected %" PRIuGRUB_SIZE " bytes but got %u bytes.\n"), ++ pcr_digest_len, pcr_values.digests[i].size); ++ goto exit2; ++ } ++ ++ grub_memcpy (pcr_cursor, pcr_values.digests[i].buffer, pcr_digest_len); ++ pcr_cursor += pcr_digest_len; ++ } ++ ++ grub_crypto_hash (hash_spec, pcr_digest, pcr_concat, pcr_concat_len); ++ ++ /* Start Trial Session */ ++ nonce.size = TPM_SHA256_DIGEST_SIZE; ++ symmetric.algorithm = TPM_ALG_NULL; ++ ++ rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, 0, &nonce, &salt, ++ TPM_SE_TRIAL, &symmetric, TPM_ALG_SHA256, ++ &session, NULL, 0); ++ if (rc != TPM_RC_SUCCESS) ++ { ++ fprintf (stderr, ++ _("Failed to start trial policy session (TPM2_StartAuthSession: 0x%x).\n"), ++ rc); ++ err = GRUB_ERR_BAD_DEVICE; ++ goto exit2; ++ } ++ ++ /* PCR Policy */ ++ memcpy (pcr_digest_in.buffer, pcr_digest, TPM_SHA256_DIGEST_SIZE); ++ ++ rc = TPM2_PolicyPCR (session, NULL, &pcr_digest_in, &pcr_sel, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ { ++ fprintf (stderr, _("Failed to submit PCR policy (TPM2_PolicyPCR: 0x%x).\n"), ++ rc); ++ err = GRUB_ERR_BAD_DEVICE; ++ goto exit3; ++ } ++ ++ /* Retrieve Policy Digest */ ++ rc = TPM2_PolicyGetDigest (session, NULL, &policy_digest, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ { ++ fprintf (stderr, _("Failed to get policy digest (TPM2_PolicyGetDigest: 0x%x).\n"), ++ rc); ++ err = GRUB_ERR_BAD_DEVICE; ++ goto exit3; ++ } ++ ++ /* Epilogue */ ++ *digest = policy_digest; ++ err = GRUB_ERR_NONE; ++ ++exit3: ++ TPM2_FlushContext (session); ++ ++exit2: ++ grub_free (pcr_concat); ++ ++exit1: ++ grub_free (pcr_digest); ++ ++ return err; ++} ++ ++static grub_err_t ++grub_protect_tpm2_get_srk (struct grub_protect_args *args, TPM_HANDLE *srk) ++{ ++ TPM_RC rc; ++ TPM2B_PUBLIC public; ++ TPMS_AUTH_COMMAND authCommand = { 0 }; ++ TPM2B_SENSITIVE_CREATE inSensitive = { 0 }; ++ TPM2B_PUBLIC inPublic = { 0 }; ++ TPM2B_DATA outsideInfo = { 0 }; ++ TPML_PCR_SELECTION creationPcr = { 0 }; ++ TPM2B_PUBLIC outPublic = { 0 }; ++ TPM2B_CREATION_DATA creationData = { 0 }; ++ TPM2B_DIGEST creationHash = { 0 }; ++ TPMT_TK_CREATION creationTicket = { 0 }; ++ TPM2B_NAME srkName = { 0 }; ++ TPM_HANDLE srkHandle; ++ ++ if (args->tpm2_srk != 0) ++ { ++ /* Find SRK */ ++ rc = TPM2_ReadPublic (args->tpm2_srk, NULL, &public); ++ if (rc == TPM_RC_SUCCESS) ++ { ++ printf (_("Read SRK from 0x%x\n"), args->tpm2_srk); ++ *srk = args->tpm2_srk; ++ return GRUB_ERR_NONE; ++ } ++ ++ /* The handle exists but its public area could not be read. */ ++ if ((rc & ~TPM_RC_N_MASK) != TPM_RC_HANDLE) ++ { ++ fprintf (stderr, ++ _("Failed to retrieve SRK from 0x%x (TPM2_ReadPublic: 0x%x).\n"), ++ args->tpm2_srk, rc); ++ return GRUB_ERR_BAD_DEVICE; ++ } ++ } ++ ++ /* Create SRK */ ++ authCommand.sessionHandle = TPM_RS_PW; ++ inPublic.publicArea.type = args->tpm2_asymmetric; ++ inPublic.publicArea.nameAlg = TPM_ALG_SHA256; ++ inPublic.publicArea.objectAttributes.restricted = 1; ++ inPublic.publicArea.objectAttributes.userWithAuth = 1; ++ inPublic.publicArea.objectAttributes.decrypt = 1; ++ inPublic.publicArea.objectAttributes.fixedTPM = 1; ++ inPublic.publicArea.objectAttributes.fixedParent = 1; ++ inPublic.publicArea.objectAttributes.sensitiveDataOrigin = 1; ++ inPublic.publicArea.objectAttributes.noDA = 1; ++ ++ switch (args->tpm2_asymmetric) ++ { ++ case TPM_ALG_RSA: ++ inPublic.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES; ++ inPublic.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128; ++ inPublic.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB; ++ inPublic.publicArea.parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL; ++ inPublic.publicArea.parameters.rsaDetail.keyBits = args->rsa_bits; ++ inPublic.publicArea.parameters.rsaDetail.exponent = 0; ++ break; ++ ++ case TPM_ALG_ECC: ++ inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES; ++ inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128; ++ inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB; ++ inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL; ++ inPublic.publicArea.parameters.eccDetail.curveID = args->ecc_curve; ++ inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL; ++ break; ++ ++ default: ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ rc = TPM2_CreatePrimary (TPM_RH_OWNER, &authCommand, &inSensitive, &inPublic, ++ &outsideInfo, &creationPcr, &srkHandle, &outPublic, ++ &creationData, &creationHash, &creationTicket, ++ &srkName, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ { ++ fprintf (stderr, _("Failed to create SRK (TPM2_CreatePrimary: 0x%x).\n"), rc); ++ return GRUB_ERR_BAD_DEVICE; ++ } ++ ++ /* Persist SRK */ ++ if (args->tpm2_srk != 0) ++ { ++ rc = TPM2_EvictControl (TPM_RH_OWNER, srkHandle, &authCommand, ++ args->tpm2_srk, NULL); ++ if (rc == TPM_RC_SUCCESS) ++ { ++ TPM2_FlushContext (srkHandle); ++ srkHandle = args->tpm2_srk; ++ } ++ else ++ fprintf (stderr, ++ _("Warning: Failed to persist SRK (0x%x) (TPM2_EvictControl: 0x%x\n). " ++ "Continuing anyway...\n"), args->tpm2_srk, rc); ++ } ++ ++ /* Epilogue */ ++ *srk = srkHandle; ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_protect_tpm2_seal (TPM2B_DIGEST *policyDigest, TPM_HANDLE srk, ++ grub_uint8_t *clearText, grub_size_t clearTextLength, ++ TPM2_SEALED_KEY *sealed_key) ++{ ++ TPM_RC rc; ++ TPMS_AUTH_COMMAND authCommand = { 0 }; ++ TPM2B_SENSITIVE_CREATE inSensitive = { 0 }; ++ TPM2B_PUBLIC inPublic = { 0 }; ++ TPM2B_DATA outsideInfo = { 0 }; ++ TPML_PCR_SELECTION pcr_sel = { 0 }; ++ TPM2B_PRIVATE outPrivate = { 0 }; ++ TPM2B_PUBLIC outPublic = { 0 }; ++ ++ /* Seal Data */ ++ authCommand.sessionHandle = TPM_RS_PW; ++ ++ inSensitive.sensitive.data.size = clearTextLength; ++ memcpy(inSensitive.sensitive.data.buffer, clearText, clearTextLength); ++ ++ inPublic.publicArea.type = TPM_ALG_KEYEDHASH; ++ inPublic.publicArea.nameAlg = TPM_ALG_SHA256; ++ inPublic.publicArea.parameters.keyedHashDetail.scheme.scheme = TPM_ALG_NULL; ++ inPublic.publicArea.authPolicy = *policyDigest; ++ ++ rc = TPM2_Create (srk, &authCommand, &inSensitive, &inPublic, &outsideInfo, ++ &pcr_sel, &outPrivate, &outPublic, NULL, NULL, NULL, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ { ++ fprintf (stderr, _("Failed to seal key (TPM2_Create: 0x%x).\n"), rc); ++ return GRUB_ERR_BAD_DEVICE; ++ } ++ ++ /* Epilogue */ ++ sealed_key->public = outPublic; ++ sealed_key->private = outPrivate; ++ ++ return GRUB_ERR_NONE; ++} ++ ++extern asn1_static_node tpm2key_asn1_tab[]; ++ ++static grub_err_t ++grub_protect_tpm2_export_tpm2key (const struct grub_protect_args *args, ++ TPM2_SEALED_KEY *sealed_key) ++{ ++ const char *sealed_key_oid = "2.23.133.10.1.5"; ++ asn1_node asn1_def = NULL; ++ asn1_node tpm2key = NULL; ++ grub_uint32_t parent; ++ grub_uint32_t cmd_code; ++ struct grub_tpm2_buffer pol_buf; ++ TPML_PCR_SELECTION pcr_sel = { ++ .count = 1, ++ .pcrSelections = { ++ { ++ .hash = args->tpm2_bank, ++ .sizeOfSelect = 3, ++ .pcrSelect = { 0 } ++ }, ++ } ++ }; ++ struct grub_tpm2_buffer pub_buf; ++ struct grub_tpm2_buffer priv_buf; ++ void *der_buf = NULL; ++ int der_buf_size = 0; ++ int i; ++ int ret; ++ grub_err_t err; ++ ++ for (i = 0; i < args->tpm2_pcr_count; i++) ++ TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], args->tpm2_pcrs[i]); ++ ++ /* ++ * Prepare the parameters for TPM_CC_PolicyPCR: ++ * empty pcrDigest and the user selected PCRs ++ */ ++ grub_tpm2_buffer_init (&pol_buf); ++ grub_tpm2_buffer_pack_u16 (&pol_buf, 0); ++ grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (&pol_buf, &pcr_sel); ++ ++ grub_tpm2_buffer_init (&pub_buf); ++ grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&pub_buf, &sealed_key->public); ++ grub_tpm2_buffer_init (&priv_buf); ++ grub_tpm2_mu_TPM2B_Marshal (&priv_buf, sealed_key->private.size, ++ sealed_key->private.buffer); ++ if (pub_buf.error != 0 || priv_buf.error != 0) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ ret = asn1_array2tree (tpm2key_asn1_tab, &asn1_def, NULL); ++ if (ret != ASN1_SUCCESS) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ ret = asn1_create_element (asn1_def, "TPM2KEY.TPMKey" , &tpm2key); ++ if (ret != ASN1_SUCCESS) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ /* Set 'type' to "sealed key" */ ++ ret = asn1_write_value (tpm2key, "type", sealed_key_oid, 1); ++ if (ret != ASN1_SUCCESS) ++ { ++ err = GRUB_ERR_BAD_ARGUMENT; ++ goto error; ++ } ++ ++ /* Set 'emptyAuth' to TRUE */ ++ ret = asn1_write_value (tpm2key, "emptyAuth", "TRUE", 1); ++ if (ret != ASN1_SUCCESS) ++ { ++ err = GRUB_ERR_BAD_ARGUMENT; ++ goto error; ++ } ++ ++ /* Set 'policy' */ ++ ret = asn1_write_value (tpm2key, "policy", "NEW", 1); ++ if (ret != ASN1_SUCCESS) ++ { ++ err = GRUB_ERR_BAD_ARGUMENT; ++ goto error; ++ } ++ cmd_code = grub_cpu_to_be32 (TPM_CC_PolicyPCR); ++ ret = asn1_write_value (tpm2key, "policy.?LAST.CommandCode", &cmd_code, ++ sizeof (cmd_code)); ++ if (ret != ASN1_SUCCESS) ++ { ++ err = GRUB_ERR_BAD_ARGUMENT; ++ goto error; ++ } ++ ret = asn1_write_value (tpm2key, "policy.?LAST.CommandPolicy", &pol_buf.data, ++ pol_buf.size); ++ if (ret != ASN1_SUCCESS) ++ { ++ err = GRUB_ERR_BAD_ARGUMENT; ++ goto error; ++ } ++ ++ /* Remove 'secret' */ ++ ret = asn1_write_value (tpm2key, "secret", NULL, 0); ++ if (ret != ASN1_SUCCESS) ++ { ++ err = GRUB_ERR_BAD_ARGUMENT; ++ goto error; ++ } ++ ++ /* Remove 'authPolicy' */ ++ ret = asn1_write_value (tpm2key, "authPolicy", NULL, 0); ++ if (ret != ASN1_SUCCESS) ++ { ++ err = GRUB_ERR_BAD_ARGUMENT; ++ goto error; ++ } ++ ++ /* Use TPM_RH_OWNER as the default parent handle */ ++ parent = grub_cpu_to_be32 (TPM_RH_OWNER); ++ ret = asn1_write_value (tpm2key, "parent", &parent, sizeof (parent)); ++ if (ret != ASN1_SUCCESS) ++ { ++ err = GRUB_ERR_BAD_ARGUMENT; ++ goto error; ++ } ++ ++ /* Set the pubkey */ ++ ret = asn1_write_value (tpm2key, "pubkey", pub_buf.data, pub_buf.size); ++ if (ret != ASN1_SUCCESS) ++ { ++ err = GRUB_ERR_BAD_ARGUMENT; ++ goto error; ++ } ++ ++ /* Set the privkey */ ++ ret = asn1_write_value (tpm2key, "privkey", priv_buf.data, priv_buf.size); ++ if (ret != ASN1_SUCCESS) ++ { ++ err = GRUB_ERR_BAD_ARGUMENT; ++ goto error; ++ } ++ ++ /* Create the DER binary */ ++ der_buf_size = 0; ++ ret = asn1_der_coding (tpm2key, "", NULL, &der_buf_size, NULL); ++ ++ der_buf = grub_malloc (der_buf_size); ++ if (der_buf == NULL) ++ { ++ err = GRUB_ERR_OUT_OF_MEMORY; ++ goto error; ++ } ++ ++ ret = asn1_der_coding (tpm2key, "", der_buf, &der_buf_size, NULL); ++ if (ret != ASN1_SUCCESS) ++ { ++ err = GRUB_ERR_BAD_ARGUMENT; ++ goto error; ++ } ++ ++ err = grub_protect_write_file (args->tpm2_outfile, der_buf, der_buf_size); ++ if (err != GRUB_ERR_NONE) ++ fprintf (stderr, _("Could not write tpm2key file (Error: %u).\n"), ++ errno); ++ ++error: ++ grub_free (der_buf); ++ ++ if (tpm2key) ++ asn1_delete_structure (&tpm2key); ++ ++ return err; ++} ++ ++static grub_err_t ++grub_protect_tpm2_export_sealed_key (const char *filepath, ++ TPM2_SEALED_KEY *sealed_key) ++{ ++ grub_err_t err; ++ struct grub_tpm2_buffer buf; ++ ++ grub_tpm2_buffer_init (&buf); ++ grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&buf, &sealed_key->public); ++ grub_tpm2_mu_TPM2B_Marshal (&buf, sealed_key->private.size, ++ sealed_key->private.buffer); ++ if (buf.error != 0) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ err = grub_protect_write_file (filepath, buf.data, buf.size); ++ if (err != GRUB_ERR_NONE) ++ fprintf (stderr, _("Could not write sealed key file (Error: %u).\n"), ++ errno); ++ ++ return err; ++} ++ ++static grub_err_t ++grub_protect_tpm2_add (struct grub_protect_args *args) ++{ ++ grub_err_t err; ++ grub_uint8_t *key; ++ grub_size_t key_size; ++ TPM_HANDLE srk; ++ TPM2B_DIGEST policy_digest; ++ TPM2_SEALED_KEY sealed_key; ++ char *grub_drive = NULL; ++ ++ grub_protect_get_grub_drive_for_file (args->tpm2_outfile, &grub_drive); ++ ++ err = grub_protect_tpm2_open_device (args->tpm2_device); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ ++ err = grub_protect_read_file (args->tpm2_keyfile, (void **)&key, &key_size); ++ if (err != GRUB_ERR_NONE) ++ goto exit1; ++ ++ if (key_size > TPM_MAX_SYM_DATA) ++ { ++ fprintf (stderr, ++ _("Input key is too long, maximum allowed size is %u bytes.\n"), ++ TPM_MAX_SYM_DATA); ++ return GRUB_ERR_OUT_OF_RANGE; ++ } ++ ++ err = grub_protect_tpm2_get_srk (args, &srk); ++ if (err != GRUB_ERR_NONE) ++ goto exit2; ++ ++ err = grub_protect_tpm2_get_policy_digest (args, &policy_digest); ++ if (err != GRUB_ERR_NONE) ++ goto exit3; ++ ++ err = grub_protect_tpm2_seal (&policy_digest, srk, key, key_size, ++ &sealed_key); ++ if (err != GRUB_ERR_NONE) ++ goto exit3; ++ ++ if (args->tpm2_tpm2key) ++ err = grub_protect_tpm2_export_tpm2key (args, &sealed_key); ++ else ++ err = grub_protect_tpm2_export_sealed_key (args->tpm2_outfile, &sealed_key); ++ if (err != GRUB_ERR_NONE) ++ goto exit3; ++ ++ if (grub_drive) ++ { ++ printf (_("GRUB drive for the sealed key file: %s\n"), grub_drive); ++ grub_free (grub_drive); ++ } ++ else ++ { ++ fprintf (stderr, ++ _("Warning: Could not determine GRUB drive for sealed key " ++ "file.\n")); ++ err = GRUB_ERR_NONE; ++ } ++ ++exit3: ++ TPM2_FlushContext (srk); ++ ++exit2: ++ grub_free (key); ++ ++exit1: ++ grub_protect_tpm2_close_device (); ++ ++ return err; ++} ++ ++static grub_err_t ++grub_protect_tpm2_remove (struct grub_protect_args *args) ++{ ++ TPM_RC rc; ++ TPM2B_PUBLIC public; ++ TPMS_AUTH_COMMAND authCommand = { 0 }; ++ grub_err_t err; ++ ++ if (args->tpm2_evict == 0) ++ { ++ printf (_("--tpm2-evict not specified, nothing to do.\n")); ++ return GRUB_ERR_NONE; ++ } ++ ++ err = grub_protect_tpm2_open_device (args->tpm2_device); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ ++ /* Find SRK */ ++ rc = TPM2_ReadPublic (args->tpm2_srk, NULL, &public); ++ if (rc != TPM_RC_SUCCESS) ++ { ++ fprintf (stderr, _("SRK with handle 0x%x not found.\n"), args->tpm2_srk); ++ err = GRUB_ERR_BAD_ARGUMENT; ++ goto exit1; ++ } ++ ++ /* Evict SRK */ ++ authCommand.sessionHandle = TPM_RS_PW; ++ ++ rc = TPM2_EvictControl (TPM_RH_OWNER, args->tpm2_srk, &authCommand, ++ args->tpm2_srk, NULL); ++ if (rc != TPM_RC_SUCCESS) ++ { ++ fprintf (stderr, ++ _("Failed to evict SRK with handle 0x%x (TPM2_EvictControl: 0x%x).\n"), ++ args->tpm2_srk, rc); ++ err = GRUB_ERR_BAD_DEVICE; ++ goto exit2; ++ } ++ ++ err = GRUB_ERR_NONE; ++ ++exit2: ++ TPM2_FlushContext (args->tpm2_srk); ++ ++exit1: ++ grub_protect_tpm2_close_device (); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_protect_tpm2_run (struct grub_protect_args *args) ++{ ++ switch (args->action) ++ { ++ case GRUB_PROTECT_ACTION_ADD: ++ return grub_protect_tpm2_add (args); ++ ++ case GRUB_PROTECT_ACTION_REMOVE: ++ return grub_protect_tpm2_remove (args); ++ ++ default: ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++} ++ ++static grub_err_t ++grub_protect_tpm2_args_verify (struct grub_protect_args *args) ++{ ++ switch (args->action) ++ { ++ case GRUB_PROTECT_ACTION_ADD: ++ if (args->args & GRUB_PROTECT_ARG_TPM2_EVICT) ++ { ++ fprintf (stderr, ++ _("--tpm2-evict is invalid when --action is 'add'.\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ if (args->tpm2_keyfile == NULL) ++ { ++ fprintf (stderr, _("--tpm2-keyfile must be specified.\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ if (args->tpm2_outfile == NULL) ++ { ++ fprintf (stderr, _("--tpm2-outfile must be specified.\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ if (args->tpm2_device == NULL) ++ args->tpm2_device = "/dev/tpm0"; ++ ++ if (args->tpm2_pcr_count == 0) ++ { ++ args->tpm2_pcrs[0] = 7; ++ args->tpm2_pcr_count = 1; ++ } ++ ++ if (args->tpm2_asymmetric == TPM_ALG_ERROR) ++ { ++ args->tpm2_asymmetric = TPM_ALG_RSA; ++ args->rsa_bits = 2048; ++ } ++ ++ if (args->tpm2_bank == TPM_ALG_ERROR) ++ args->tpm2_bank = TPM_ALG_SHA256; ++ ++ break; ++ ++ case GRUB_PROTECT_ACTION_REMOVE: ++ if (args->args & GRUB_PROTECT_ARG_TPM2_ASYMMETRIC) ++ { ++ fprintf (stderr, ++ _("--tpm2-asymmetric is invalid when --action is 'remove'.\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ if (args->args & GRUB_PROTECT_ARG_TPM2_BANK) ++ { ++ fprintf (stderr, ++ _("--tpm2-bank is invalid when --action is 'remove'.\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ if (args->args & GRUB_PROTECT_ARG_TPM2_KEYFILE) ++ { ++ fprintf (stderr, ++ _("--tpm2-keyfile is invalid when --action is 'remove'.\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ if (args->args & GRUB_PROTECT_ARG_TPM2_OUTFILE) ++ { ++ fprintf (stderr, ++ _("--tpm2-outfile is invalid when --action is 'remove'.\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ if (args->args & GRUB_PROTECT_ARG_TPM2_PCRS) ++ { ++ fprintf (stderr, ++ _("--tpm2-pcrs is invalid when --action is 'remove'.\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ if (args->tpm2_srk == 0) ++ { ++ fprintf (stderr, ++ _("--tpm2-srk is not specified when --action is 'remove'.\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ if (args->tpm2_device == NULL) ++ args->tpm2_device = "/dev/tpm0"; ++ ++ break; ++ ++ default: ++ fprintf (stderr, ++ _("The TPM2 key protector only supports the following actions: " ++ "add, remove.\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static error_t ++grub_protect_argp_parser (int key, char *arg, struct argp_state *state) ++{ ++ grub_err_t err; ++ struct grub_protect_args *args = state->input; ++ ++ switch (key) ++ { ++ case GRUB_PROTECT_OPT_ACTION: ++ if (args->args & GRUB_PROTECT_ARG_ACTION) ++ { ++ fprintf (stderr, _("--action|-a can only be specified once.\n")); ++ return EINVAL; ++ } ++ ++ if (grub_strcmp (arg, "add") == 0) ++ args->action = GRUB_PROTECT_ACTION_ADD; ++ else if (grub_strcmp (arg, "remove") == 0) ++ args->action = GRUB_PROTECT_ACTION_REMOVE; ++ else ++ { ++ fprintf (stderr, _("'%s' is not a valid action.\n"), arg); ++ return EINVAL; ++ } ++ ++ args->args |= GRUB_PROTECT_ARG_ACTION; ++ break; ++ ++ case GRUB_PROTECT_OPT_PROTECTOR: ++ if (args->args & GRUB_PROTECT_ARG_PROTECTOR) ++ { ++ fprintf (stderr, _("--protector|-p can only be specified once.\n")); ++ return EINVAL; ++ } ++ ++ if (grub_strcmp (arg, "tpm2") == 0) ++ args->protector = GRUB_PROTECT_TYPE_TPM2; ++ else ++ { ++ fprintf (stderr, _("'%s' is not a valid protector.\n"), arg); ++ return EINVAL; ++ } ++ ++ args->args |= GRUB_PROTECT_ARG_PROTECTOR; ++ break; ++ ++ case GRUB_PROTECT_OPT_TPM2_DEVICE: ++ if (args->args & GRUB_PROTECT_ARG_TPM2_DEVICE) ++ { ++ fprintf (stderr, _("--tpm2-device can only be specified once.\n")); ++ return EINVAL; ++ } ++ ++ args->tpm2_device = xstrdup(arg); ++ args->args |= GRUB_PROTECT_ARG_TPM2_DEVICE; ++ break; ++ ++ case GRUB_PROTECT_OPT_TPM2_PCRS: ++ if (args->args & GRUB_PROTECT_ARG_TPM2_PCRS) ++ { ++ fprintf (stderr, _("--tpm2-pcrs can only be specified once.\n")); ++ return EINVAL; ++ } ++ ++ err = grub_tpm2_protector_parse_pcrs (arg, args->tpm2_pcrs, ++ &args->tpm2_pcr_count); ++ if (err != GRUB_ERR_NONE) ++ { ++ if (grub_errno != GRUB_ERR_NONE) ++ grub_print_error (); ++ return EINVAL; ++ } ++ ++ args->args |= GRUB_PROTECT_ARG_TPM2_PCRS; ++ break; ++ ++ case GRUB_PROTECT_OPT_TPM2_SRK: ++ if (args->args & GRUB_PROTECT_ARG_TPM2_SRK) ++ { ++ fprintf (stderr, _("--tpm2-srk can only be specified once.\n")); ++ return EINVAL; ++ } ++ ++ err = grub_tpm2_protector_parse_tpm_handle (arg, &args->tpm2_srk); ++ if (err != GRUB_ERR_NONE) ++ { ++ if (grub_errno != GRUB_ERR_NONE) ++ grub_print_error (); ++ return EINVAL; ++ } ++ ++ args->args |= GRUB_PROTECT_ARG_TPM2_SRK; ++ break; ++ ++ case GRUB_PROTECT_OPT_TPM2_ASYMMETRIC: ++ if (args->args & GRUB_PROTECT_ARG_TPM2_ASYMMETRIC) ++ { ++ fprintf (stderr, _("--tpm2-asymmetric can only be specified once.\n")); ++ return EINVAL; ++ } ++ ++ err = grub_tpm2_protector_parse_asymmetric (arg, &args->tpm2_asymmetric, ++ &args->rsa_bits, &args->ecc_curve); ++ if (err != GRUB_ERR_NONE) ++ { ++ if (grub_errno != GRUB_ERR_NONE) ++ grub_print_error (); ++ return EINVAL; ++ } ++ ++ args->args |= GRUB_PROTECT_ARG_TPM2_ASYMMETRIC; ++ break; ++ ++ case GRUB_PROTECT_OPT_TPM2_BANK: ++ if (args->args & GRUB_PROTECT_ARG_TPM2_BANK) ++ { ++ fprintf (stderr, _("--tpm2-bank can only be specified once.\n")); ++ return EINVAL; ++ } ++ ++ err = grub_tpm2_protector_parse_bank (arg, &args->tpm2_bank); ++ if (err != GRUB_ERR_NONE) ++ { ++ if (grub_errno != GRUB_ERR_NONE) ++ grub_print_error (); ++ return EINVAL; ++ } ++ ++ args->args |= GRUB_PROTECT_ARG_TPM2_BANK; ++ break; ++ ++ case GRUB_PROTECT_OPT_TPM2_KEYFILE: ++ if (args->args & GRUB_PROTECT_ARG_TPM2_KEYFILE) ++ { ++ fprintf (stderr, _("--tpm2-keyfile can only be specified once.\n")); ++ return EINVAL; ++ } ++ ++ args->tpm2_keyfile = xstrdup(arg); ++ args->args |= GRUB_PROTECT_ARG_TPM2_KEYFILE; ++ break; ++ ++ case GRUB_PROTECT_OPT_TPM2_OUTFILE: ++ if (args->args & GRUB_PROTECT_ARG_TPM2_OUTFILE) ++ { ++ fprintf (stderr, _("--tpm2-outfile can only be specified once.\n")); ++ return EINVAL; ++ } ++ ++ args->tpm2_outfile = xstrdup(arg); ++ args->args |= GRUB_PROTECT_ARG_TPM2_OUTFILE; ++ break; ++ ++ case GRUB_PROTECT_OPT_TPM2_EVICT: ++ if (args->args & GRUB_PROTECT_ARG_TPM2_EVICT) ++ { ++ fprintf (stderr, _("--tpm2-evict can only be specified once.\n")); ++ return EINVAL; ++ } ++ ++ args->tpm2_evict = 1; ++ args->args |= GRUB_PROTECT_ARG_TPM2_EVICT; ++ break; ++ ++ case GRUB_PROTECT_OPT_TPM2_TPM2KEY: ++ if (args->args & GRUB_PROTECT_ARG_TPM2_TPM2KEY) ++ { ++ fprintf (stderr, _("--tpm2-tpm2key can only be specified once.\n")); ++ return EINVAL; ++ } ++ ++ args->tpm2_tpm2key = 1; ++ args->args |= GRUB_PROTECT_ARG_TPM2_TPM2KEY; ++ break; ++ ++ default: ++ return ARGP_ERR_UNKNOWN; ++ } ++ ++ return 0; ++} ++ ++static grub_err_t ++grub_protect_args_verify (struct grub_protect_args *args) ++{ ++ if (args->action == GRUB_PROTECT_ACTION_ERROR) ++ { ++ fprintf (stderr, "--action is mandatory.\n"); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ /* At the moment, the only configurable key protector is the TPM2 one, so it ++ * is the only key protector supported by this tool. */ ++ if (args->protector != GRUB_PROTECT_TYPE_TPM2) ++ { ++ fprintf (stderr, ++ _("--protector is mandatory and only 'tpm2' is currently " ++ "supported.\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ switch (args->protector) ++ { ++ case GRUB_PROTECT_TYPE_TPM2: ++ return grub_protect_tpm2_args_verify (args); ++ default: ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_protect_dispatch (struct grub_protect_args *args) ++{ ++ switch (args->protector) ++ { ++ case GRUB_PROTECT_TYPE_TPM2: ++ return grub_protect_tpm2_run (args); ++ default: ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++} ++ ++static void ++grub_protect_init (int *argc, char **argv[]) ++{ ++ grub_util_host_init (argc, argv); ++ ++ grub_util_biosdisk_init (NULL); ++ ++ grub_init_all (); ++ grub_gcry_init_all (); ++ ++ grub_lvm_fini (); ++ grub_mdraid09_fini (); ++ grub_mdraid1x_fini (); ++ grub_diskfilter_fini (); ++ grub_diskfilter_init (); ++ grub_mdraid09_init (); ++ grub_mdraid1x_init (); ++ grub_lvm_init (); ++} ++ ++static void ++grub_protect_fini (void) ++{ ++ grub_gcry_fini_all (); ++ grub_fini_all (); ++ grub_util_biosdisk_fini (); ++} ++ ++static struct argp grub_protect_argp = ++{ ++ .options = grub_protect_options, ++ .parser = grub_protect_argp_parser, ++ .args_doc = NULL, ++ .doc = ++ N_("Protect a cleartext key using a GRUB key protector that can retrieve " ++ "the key during boot to unlock fully-encrypted disks automatically."), ++ .children = NULL, ++ .help_filter = NULL, ++ .argp_domain = NULL ++}; ++ ++int ++main (int argc, char *argv[]) ++{ ++ grub_err_t err; ++ struct grub_protect_args args = { 0 }; ++ ++ if (argp_parse (&grub_protect_argp, argc, argv, 0, 0, &args) != 0) ++ { ++ fprintf (stderr, _("Could not parse arguments.\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ grub_protect_init (&argc, &argv); ++ ++ err = grub_protect_args_verify (&args); ++ if (err != GRUB_ERR_NONE) ++ goto exit; ++ ++ err = grub_protect_dispatch (&args); ++ if (err != GRUB_ERR_NONE) ++ goto exit; ++ ++exit: ++ grub_protect_fini (); ++ ++ return err; ++} +-- +2.35.3 + diff --git a/0005-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch b/0005-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch new file mode 100644 index 0000000..217d605 --- /dev/null +++ b/0005-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch @@ -0,0 +1,105 @@ +From a33acb675fe0d0464637175e4f06176e4c329025 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Fri, 12 Jul 2019 09:53:32 +0200 +Subject: [PATCH 05/11] x86-efi: Use bounce buffers for reading to addresses > + 4GB + +Lots of machines apparently can't DMA correctly above 4GB during UEFI, +so use bounce buffers for the initramfs read. + +Signed-off-by: Peter Jones +--- + grub-core/loader/i386/efi/linux.c | 52 ++++++++++++++++++++++++++----- + 1 file changed, 45 insertions(+), 7 deletions(-) + +diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c +index f3abbd025..d6bed4fb4 100644 +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -35,11 +35,16 @@ static grub_dl_t my_mod; + static int loaded; + static void *kernel_mem; + static grub_uint64_t kernel_size; +-static grub_uint8_t *initrd_mem; ++static void *initrd_mem; + static grub_uint32_t handover_offset; + struct linux_kernel_params *params; + static char *linux_cmdline; + ++#define MIN(a, b) \ ++ ({ typeof (a) _a = (a); \ ++ typeof (b) _b = (b); \ ++ _a < _b ? _a : _b; }) ++ + #define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) + + static grub_err_t +@@ -68,6 +73,44 @@ grub_linuxefi_unload (void) + return GRUB_ERR_NONE; + } + ++#define BOUNCE_BUFFER_MAX 0x10000000ull ++ ++static grub_ssize_t ++read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len) ++{ ++ grub_ssize_t bufpos = 0; ++ static grub_size_t bbufsz = 0; ++ static char *bbuf = NULL; ++ ++ if (bbufsz == 0) ++ bbufsz = MIN(BOUNCE_BUFFER_MAX, len); ++ ++ while (!bbuf && bbufsz) ++ { ++ bbuf = grub_malloc(bbufsz); ++ if (!bbuf) ++ bbufsz >>= 1; ++ } ++ if (!bbuf) ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate bounce buffer")); ++ ++ while (bufpos < (long long)len) ++ { ++ grub_ssize_t sz; ++ ++ sz = grub_file_read (file, bbuf, MIN(bbufsz, len - bufpos)); ++ if (sz < 0) ++ return sz; ++ if (sz == 0) ++ break; ++ ++ grub_memcpy(bufp + bufpos, bbuf, sz); ++ bufpos += sz; ++ } ++ ++ return bufpos; ++} ++ + static grub_err_t + grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +@@ -120,7 +163,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + for (i = 0; i < nfiles; i++) + { + grub_ssize_t cursize = grub_file_size (files[i]); +- if (grub_file_read (files[i], ptr, cursize) != cursize) ++ if (read (files[i], ptr, cursize) != cursize) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), +@@ -145,11 +188,6 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + return grub_errno; + } + +-#define MIN(a, b) \ +- ({ typeof (a) _a = (a); \ +- typeof (b) _b = (b); \ +- _a < _b ? _a : _b; }) +- + static grub_err_t + grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +-- +2.31.1 + diff --git a/0006-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch b/0006-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch new file mode 100644 index 0000000..776ba7a --- /dev/null +++ b/0006-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch @@ -0,0 +1,98 @@ +From 8191aae462f8b755972a0a801f4adbdd6ecaa66c Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 14 Jul 2016 18:45:14 +0800 +Subject: [PATCH 6/8] bootp: Add processing DHCPACK packet from HTTP Boot + +The vendor class identifier with the string "HTTPClient" is used to denote the +packet as responding to HTTP boot request. In DHCP4 config, the filename for +HTTP boot is the URL of the boot file while for PXE boot it is the path to the +boot file. As a consequence, the next-server becomes obseleted because the HTTP +URL already contains the server address for the boot file. For DHCP6 config, +there's no difference definition in existing config as dhcp6.bootfile-url can +be used to specify URL for both HTTP and PXE boot file. + +This patch adds processing for "HTTPClient" vendor class identifier in DHCPACK +packet by treating it as HTTP format, not as the PXE format. + +Signed-off-by: Michael Chang +Signed-off-by: Ken Lin +--- + grub-core/net/bootp.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++-- + include/grub/net.h | 1 + + 2 files changed, 66 insertions(+), 2 deletions(-) + +--- a/grub-core/net/bootp.c ++++ b/grub-core/net/bootp.c +@@ -352,6 +352,53 @@ + if (!inter) + return 0; + ++ /* FIXME: Introduce new http flag for better synergy with existing tftp code base */ ++ if (size > OFFSET_OF (vendor, bp)) ++ { ++ char *cidvar; ++ const char *cid; ++ ++ opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER, &opt_len); ++ if (opt && opt_len) ++ grub_env_set_net_property (name, "vendor_class_identifier", (const char *) opt, opt_len); ++ cidvar = grub_xasprintf ("net_%s_%s", name, "vendor_class_identifier"); ++ cid = grub_env_get (cidvar); ++ grub_free (cidvar); ++ ++ if (cid && grub_strcmp (cid, "HTTPClient") == 0) ++ { ++ char *proto, *ip, *pa; ++ ++ /* FIXME: Provide better URL function that returns in place pointers ++ * so that we don't have to free them. ++ */ ++ if (!dissect_url (bp->boot_file, &proto, &ip, &pa)) ++ return inter; ++ ++ if (is_def) ++ { ++ grub_net_default_server = grub_strdup (ip); ++ grub_env_set ("net_default_interface", name); ++ grub_env_export ("net_default_interface"); ++ } ++ if (device && !*device) ++ { ++ *device = grub_xasprintf ("%s,%s", proto, ip); ++ grub_print_error (); ++ } ++ ++ boot_file = pa; ++ boot_file_len = grub_strlen (pa); ++ ++ /* FIXME: Don't use malloc buffer here */ ++ grub_free (proto); ++ grub_free (ip); ++ ++ /* FIXME: NEED TO FREE boot_file */ ++ goto boot_file; ++ } ++ } ++ + opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_OVERLOAD, &opt_len); + if (opt && opt_len == 1) + overload = *opt; +@@ -428,6 +475,8 @@ + } + } + ++boot_file: ++ + if (boot_file) + { + grub_env_set_net_property (name, "boot_file", boot_file, boot_file_len); +--- a/include/grub/net.h ++++ b/include/grub/net.h +@@ -530,6 +530,7 @@ + GRUB_NET_DHCP_MESSAGE_TYPE = 53, + GRUB_NET_DHCP_SERVER_IDENTIFIER = 54, + GRUB_NET_DHCP_PARAMETER_REQUEST_LIST = 55, ++ GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER = 60, + GRUB_NET_BOOTP_CLIENT_ID = 61, + GRUB_NET_DHCP_TFTP_SERVER_NAME = 66, + GRUB_NET_DHCP_BOOTFILE_NAME = 67, diff --git a/0006-docs-grub-Document-signing-grub-with-an-appended-sig.patch b/0006-docs-grub-Document-signing-grub-with-an-appended-sig.patch new file mode 100644 index 0000000..27b63f5 --- /dev/null +++ b/0006-docs-grub-Document-signing-grub-with-an-appended-sig.patch @@ -0,0 +1,66 @@ +From ac539a315495792cd75fe8ab1c474f26e0a78852 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Sat, 15 Aug 2020 02:19:36 +1000 +Subject: [PATCH 06/23] docs/grub: Document signing grub with an appended + signature + +Signing grub for firmware that verifies an appended signature is a +bit fiddly. I don't want people to have to figure it out from scratch +so document it here. + +Signed-off-by: Daniel Axtens +--- + docs/grub.texi | 42 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 42 insertions(+) + +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -6606,6 +6606,48 @@ + will also be necessary to enrol the public key used into a relevant firmware + key database. + ++@section Signing GRUB with an appended signature ++ ++The @file{core.img} itself can be signed with a Linux kernel module-style ++appended signature. ++ ++To support IEEE1275 platforms where the boot image is often loaded directly ++from a disk partition rather than from a file system, the @file{core.img} ++can specify the size and location of the appended signature with an ELF ++note added by @command{grub-install}. ++ ++An image can be signed this way using the @command{sign-file} command from ++the Linux kernel: ++ ++@example ++@group ++# grub.key is your private key and certificate.der is your public key ++ ++# Determine the size of the appended signature. It depends on the signing ++# certificate and the hash algorithm ++touch empty ++sign-file SHA256 grub.key certificate.der empty empty.sig ++SIG_SIZE=`stat -c '%s' empty.sig` ++rm empty empty.sig ++ ++# Build a grub image with $SIG_SIZE reserved for the signature ++grub-install --appended-signature-size $SIG_SIZE --modules="..." ... ++ ++# Replace the reserved size with a signature: ++# cut off the last $SIG_SIZE bytes with truncate's minus modifier ++truncate -s -$SIG_SIZE /boot/grub/powerpc-ieee1275/core.elf core.elf.unsigned ++# sign the trimmed file with an appended signature, restoring the correct size ++sign-file SHA256 grub.key certificate.der core.elf.unsigned core.elf.signed ++ ++# Don't forget to install the signed image as required ++# (e.g. on powerpc-ieee1275, to the PReP partition) ++@end group ++@end example ++ ++As with UEFI secure boot, it is necessary to build in the required modules, ++or sign them separately. ++ ++ + @node Platform limitations + @chapter Platform limitations + diff --git a/0006-efi-Set-image-base-address-before-jumping-to-the-PE-.patch b/0006-efi-Set-image-base-address-before-jumping-to-the-PE-.patch new file mode 100644 index 0000000..bfc7c3e --- /dev/null +++ b/0006-efi-Set-image-base-address-before-jumping-to-the-PE-.patch @@ -0,0 +1,59 @@ +From 3741c6807923ae97b0d87e61c59c8de8af544484 Mon Sep 17 00:00:00 2001 +From: Javier Martinez Canillas +Date: Thu, 23 Apr 2020 15:06:46 +0200 +Subject: [PATCH 6/9] efi: Set image base address before jumping to the PE/COFF + entry point + +Upstream GRUB uses the EFI LoadImage() and StartImage() to boot the Linux +kernel. But our custom EFI loader that supports Secure Boot instead uses +the EFI handover protocol (for x86) or jumping directly to the PE/COFF +entry point (for aarch64). + +This is done to allow the bootloader to verify the images using the shim +lock protocol to avoid booting untrusted binaries. + +Since the bootloader loads the kernel from the boot media instead of using +LoadImage(), it is responsible to set the Loaded Image base address before +booting the kernel. + +Otherwise the kernel EFI stub will complain that it was not set correctly +and print the following warning message: + +EFI stub: ERROR: FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value + +Resolves: rhbz#1825411 + +Signed-off-by: Javier Martinez Canillas +--- + grub-core/loader/arm64/efi/linux.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +Index: grub-2.06~rc1/grub-core/loader/arm64/efi/linux.c +=================================================================== +--- grub-2.06~rc1.orig/grub-core/loader/arm64/efi/linux.c ++++ grub-2.06~rc1/grub-core/loader/arm64/efi/linux.c +@@ -58,9 +58,24 @@ static grub_err_t + grub_efi_linux_boot (void *kernel_address, grub_off_t offset, + void *kernel_params) + { ++ grub_efi_loaded_image_t *loaded_image = NULL; + handover_func hf; + ++ /* ++ * Since the EFI loader is not calling the LoadImage() and StartImage() ++ * services for loading the kernel and booting respectively, it has to ++ * set the Loaded Image base address. ++ */ ++ loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle); ++ if (loaded_image) ++ loaded_image->image_base = kernel_addr; ++ else ++ grub_dprintf ("linux", "Loaded Image base address could not be set\n"); ++ ++ grub_dprintf ("linux", "kernel_addr: %p handover_offset: %p params: %p\n", ++ kernel_address, (void *)(grub_efi_uintn_t)offset, kernel_params); + hf = (handover_func)((char *)kernel_address + offset); ++ grub_dprintf ("linux", "handover_func() = %p\n", hf); + hf (grub_efi_image_handle, grub_efi_system_table, kernel_params); + + return GRUB_ERR_BUG; diff --git a/0006-fs-ntfs-Make-code-more-readable.patch b/0006-fs-ntfs-Make-code-more-readable.patch new file mode 100644 index 0000000..717776d --- /dev/null +++ b/0006-fs-ntfs-Make-code-more-readable.patch @@ -0,0 +1,159 @@ +From e58b870ff926415e23fc386af41ff81b2f588763 Mon Sep 17 00:00:00 2001 +From: Maxim Suhanov +Date: Mon, 28 Aug 2023 16:40:07 +0300 +Subject: [PATCH 6/6] fs/ntfs: Make code more readable + +Move some calls used to access NTFS attribute header fields into +functions with human-readable names. + +Suggested-by: Daniel Kiper +Signed-off-by: Maxim Suhanov +Reviewed-by: Daniel Kiper +--- + grub-core/fs/ntfs.c | 48 +++++++++++++++++++++++++++++++-------------- + 1 file changed, 33 insertions(+), 15 deletions(-) + +diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c +index ff5e3740f..de435aa14 100644 +--- a/grub-core/fs/ntfs.c ++++ b/grub-core/fs/ntfs.c +@@ -52,6 +52,24 @@ u64at (void *ptr, grub_size_t ofs) + return grub_le_to_cpu64 (grub_get_unaligned64 ((char *) ptr + ofs)); + } + ++static grub_uint16_t ++first_attr_off (void *mft_buf_ptr) ++{ ++ return u16at (mft_buf_ptr, 0x14); ++} ++ ++static grub_uint16_t ++res_attr_data_off (void *res_attr_ptr) ++{ ++ return u16at (res_attr_ptr, 0x14); ++} ++ ++static grub_uint32_t ++res_attr_data_len (void *res_attr_ptr) ++{ ++ return u32at (res_attr_ptr, 0x10); ++} ++ + grub_ntfscomp_func_t grub_ntfscomp_func; + + static grub_err_t +@@ -106,7 +124,7 @@ init_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft) + { + at->mft = mft; + at->flags = (mft == &mft->data->mmft) ? GRUB_NTFS_AF_MMFT : 0; +- at->attr_nxt = mft->buf + u16at (mft->buf, 0x14); ++ at->attr_nxt = mft->buf + first_attr_off (mft->buf); + at->attr_end = at->emft_buf = at->edat_buf = at->sbuf = NULL; + } + +@@ -154,7 +172,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) + return NULL; + } + +- new_pos = &at->emft_buf[u16at (at->emft_buf, 0x14)]; ++ new_pos = &at->emft_buf[first_attr_off (at->emft_buf)]; + while (*new_pos != 0xFF) + { + if ((*new_pos == *at->attr_cur) +@@ -213,7 +231,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) + } + else + { +- at->attr_nxt = at->attr_end + u16at (pa, 0x14); ++ at->attr_nxt = at->attr_end + res_attr_data_off (pa); + at->attr_end = at->attr_end + u32at (pa, 4); + pa_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); + } +@@ -399,20 +417,20 @@ read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa, grub_uint8_t *dest, + + if (pa[8] == 0) + { +- if (ofs + len > u32at (pa, 0x10)) ++ if (ofs + len > res_attr_data_len (pa)) + return grub_error (GRUB_ERR_BAD_FS, "read out of range"); + +- if (u32at (pa, 0x10) > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) ++ if (res_attr_data_len (pa) > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + return grub_error (GRUB_ERR_BAD_FS, "resident attribute too large"); + + if (pa >= at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); + +- if (u16at (pa, 0x14) + u32at (pa, 0x10) > ++ if (res_attr_data_off (pa) + res_attr_data_len (pa) > + (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) pa) + return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); + +- grub_memcpy (dest, pa + u16at (pa, 0x14) + ofs, len); ++ grub_memcpy (dest, pa + res_attr_data_off (pa) + ofs, len); + return 0; + } + +@@ -556,7 +574,7 @@ init_file (struct grub_ntfs_file *mft, grub_uint64_t mftno) + (unsigned long long) mftno); + + if (!pa[8]) +- mft->size = u32at (pa, 0x10); ++ mft->size = res_attr_data_len (pa); + else + mft->size = u64at (pa, 0x30); + +@@ -805,7 +823,7 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, + (u32at (cur_pos, 0x18) != 0x490024) || + (u32at (cur_pos, 0x1C) != 0x300033)) + continue; +- cur_pos += u16at (cur_pos, 0x14); ++ cur_pos += res_attr_data_off (cur_pos); + if (*cur_pos != 0x30) /* Not filename index */ + continue; + break; +@@ -834,7 +852,7 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, + { + int is_resident = (cur_pos[8] == 0); + +- bitmap_len = ((is_resident) ? u32at (cur_pos, 0x10) : ++ bitmap_len = ((is_resident) ? res_attr_data_len (cur_pos) : + u32at (cur_pos, 0x28)); + + bmp = grub_malloc (bitmap_len); +@@ -855,14 +873,14 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, + goto done; + } + +- if (u16at (cur_pos, 0x14) + u32at (cur_pos, 0x10) > ++ if (res_attr_data_off (cur_pos) + res_attr_data_len (cur_pos) > + (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) cur_pos) + { + grub_error (GRUB_ERR_BAD_FS, "resident bitmap out of range"); + goto done; + } + +- grub_memcpy (bmp, cur_pos + u16at (cur_pos, 0x14), ++ grub_memcpy (bmp, cur_pos + res_attr_data_off (cur_pos), + bitmap_len); + } + else +@@ -1226,12 +1244,12 @@ grub_ntfs_label (grub_device_t device, char **label) + goto fail; + } + +- if ((pa) && (pa[8] == 0) && (u32at (pa, 0x10))) ++ if ((pa) && (pa[8] == 0) && (res_attr_data_len (pa))) + { + int len; + +- len = u32at (pa, 0x10) / 2; +- pa += u16at (pa, 0x14); ++ len = res_attr_data_len (pa) / 2; ++ pa += res_attr_data_off (pa); + if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa >= 2 * len) + *label = get_utf8 (pa, len); + else +-- +2.42.0 + diff --git a/0006-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch b/0006-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch new file mode 100644 index 0000000..fa72fe0 --- /dev/null +++ b/0006-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch @@ -0,0 +1,133 @@ +From 24b5c0a3788c5c02b72ea61312f5cf8c39429db1 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Thu, 13 Sep 2018 14:42:34 -0400 +Subject: [PATCH 06/11] x86-efi: Re-arrange grub_cmd_linux() a little bit. + +This just helps the next patch be easier to read. + +Signed-off-by: Peter Jones +--- + grub-core/loader/i386/efi/linux.c | 73 +++++++++++++++++-------------- + 1 file changed, 40 insertions(+), 33 deletions(-) + +diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c +index d6bed4fb4..096a52eb5 100644 +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -227,32 +227,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + goto fail; + } + +- params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, +- BYTES_TO_PAGES(sizeof(*params))); +- if (!params) +- params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, +- BYTES_TO_PAGES(sizeof(*params))); +- if (! params) +- { +- grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); +- goto fail; +- } +- +- grub_dprintf ("linux", "params = %p\n", params); +- +- grub_memset (params, 0, sizeof(*params)); ++ lh = (struct linux_i386_kernel_header *)kernel; ++ grub_dprintf ("linux", "original lh is at %p\n", kernel); + +- setup_header_end_offset = *((grub_uint8_t *)kernel + 0x201); +- grub_dprintf ("linux", "copying %lu bytes from %p to %p\n", +- MIN((grub_size_t)0x202+setup_header_end_offset, +- sizeof (*params)) - 0x1f1, +- (grub_uint8_t *)kernel + 0x1f1, +- (grub_uint8_t *)params + 0x1f1); +- grub_memcpy ((grub_uint8_t *)params + 0x1f1, +- (grub_uint8_t *)kernel + 0x1f1, +- MIN((grub_size_t)0x202+setup_header_end_offset,sizeof (*params)) - 0x1f1); +- lh = (struct linux_i386_kernel_header *)params; +- grub_dprintf ("linux", "lh is at %p\n", lh); + grub_dprintf ("linux", "checking lh->boot_flag\n"); + if (lh->boot_flag != grub_cpu_to_le16 (0xaa55)) + { +@@ -300,6 +277,34 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + } + #endif + ++ params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, ++ BYTES_TO_PAGES(sizeof(*params))); ++ if (!params) ++ params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, ++ BYTES_TO_PAGES(sizeof(*params))); ++ if (! params) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); ++ goto fail; ++ } ++ ++ grub_dprintf ("linux", "params = %p\n", params); ++ ++ grub_memset (params, 0, sizeof(*params)); ++ ++ setup_header_end_offset = *((grub_uint8_t *)kernel + 0x201); ++ grub_dprintf ("linux", "copying %" PRIuGRUB_SIZE " bytes from %p to %p\n", ++ MIN((grub_size_t)0x202+setup_header_end_offset, ++ sizeof (*params)) - 0x1f1, ++ (grub_uint8_t *)kernel + 0x1f1, ++ (grub_uint8_t *)params + 0x1f1); ++ grub_memcpy ((grub_uint8_t *)params + 0x1f1, ++ (grub_uint8_t *)kernel + 0x1f1, ++ MIN((grub_size_t)0x202+setup_header_end_offset,sizeof (*params)) - 0x1f1); ++ ++ lh = (struct linux_i386_kernel_header *)params; ++ grub_dprintf ("linux", "new lh is at %p\n", lh); ++ + grub_dprintf ("linux", "setting up cmdline\n"); + linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS, + BYTES_TO_PAGES(lh->cmdline_size + 1)); +@@ -324,8 +329,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + grub_dprintf ("linux", "setting lh->cmd_line_ptr\n"); + lh->cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline; + +- grub_dprintf ("linux", "computing handover offset\n"); + handover_offset = lh->handover_offset; ++ grub_dprintf("linux", "handover_offset: %08x\n", handover_offset); + + start = (lh->setup_sects + 1) * 512; + +@@ -342,24 +347,26 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel")); + goto fail; + } +- +- grub_dprintf ("linux", "kernel_mem = %lx\n", (unsigned long) kernel_mem); ++ grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem); + + grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); +- loaded=1; ++ ++ loaded = 1; ++ + grub_dprintf ("linux", "setting lh->code32_start to %p\n", kernel_mem); + lh->code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem; + + grub_memcpy (kernel_mem, (char *)kernel + start, filelen - start); + +- grub_dprintf ("linux", "setting lh->type_of_loader\n"); + lh->type_of_loader = 0x6; ++ grub_dprintf ("linux", "setting lh->type_of_loader = 0x%02x\n", ++ lh->type_of_loader); + +- grub_dprintf ("linux", "setting lh->ext_loader_{type,ver}\n"); + params->ext_loader_type = 0; + params->ext_loader_ver = 2; +- grub_dprintf("linux", "kernel_mem: %p handover_offset: %08x\n", +- kernel_mem, handover_offset); ++ grub_dprintf ("linux", ++ "setting lh->ext_loader_{type,ver} = {0x%02x,0x%02x}\n", ++ params->ext_loader_type, params->ext_loader_ver); + + fail: + +-- +2.31.1 + diff --git a/0007-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch b/0007-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch new file mode 100644 index 0000000..f4b7f27 --- /dev/null +++ b/0007-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch @@ -0,0 +1,43 @@ +From 4773f90bdefb72dde55fb5961f7f37b467307016 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Thu, 30 Jul 2020 00:13:21 +1000 +Subject: [PATCH 07/23] dl: provide a fake grub_dl_set_persistent for the emu + target + +Trying to start grub-emu with a module that calls grub_dl_set_persistent +will crash because grub-emu fakes modules and passes NULL to the module +init function. + +Provide an empty function for the emu case. + +Fixes: ee7808e2197c (dl: Add support for persistent modules) +Signed-off-by: Daniel Axtens +--- + include/grub/dl.h | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/include/grub/dl.h ++++ b/include/grub/dl.h +@@ -242,11 +242,22 @@ + return 0; + } + ++#ifdef GRUB_MACHINE_EMU ++/* ++ * Under grub-emu, modules are faked and NULL is passed to GRUB_MOD_INIT. ++ * So we fake this out to avoid a NULL deref. ++ */ ++static inline void ++grub_dl_set_persistent (grub_dl_t mod __attribute__((unused))) ++{ ++} ++#else + static inline void + grub_dl_set_persistent (grub_dl_t mod) + { + mod->persistent = 1; + } ++#endif + + static inline int + grub_dl_is_persistent (grub_dl_t mod) diff --git a/0007-efinet-Setting-network-from-UEFI-device-path.patch b/0007-efinet-Setting-network-from-UEFI-device-path.patch new file mode 100644 index 0000000..45feed6 --- /dev/null +++ b/0007-efinet-Setting-network-from-UEFI-device-path.patch @@ -0,0 +1,387 @@ +From 369df8e3006000a4acacc674f5882d8729781811 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Sun, 10 Jul 2016 23:46:31 +0800 +Subject: [PATCH 7/8] efinet: Setting network from UEFI device path + +The PXE Base Code protocol used to obtain cached PXE DHCPACK packet is no +longer provided for HTTP Boot. Instead, we have to get the HTTP boot +information from the device path nodes defined in following UEFI Specification +sections. + + 9.3.5.12 IPv4 Device Path + 9.3.5.13 IPv6 Device Path + 9.3.5.23 Uniform Resource Identifiers (URI) Device Path + +This patch basically does: + +include/grub/efi/api.h: +Add new structure of Uniform Resource Identifiers (URI) Device Path + +grub-core/net/drivers/efi/efinet.c: +Check if PXE Base Code is available, if not it will try to obtain the netboot +information from the device path where the image booted from. The DHCPACK +packet is recoverd from the information in device patch and feed into the same +DHCP packet processing functions to ensure the network interface is setting up +the same way it used to be. + +Signed-off-by: Michael Chang +Signed-off-by: Ken Lin +--- + grub-core/net/drivers/efi/efinet.c | 268 +++++++++++++++++++++++++++++++++++-- + include/grub/efi/api.h | 11 ++ + 2 files changed, 270 insertions(+), 9 deletions(-) + +--- a/grub-core/net/drivers/efi/efinet.c ++++ b/grub-core/net/drivers/efi/efinet.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -341,6 +342,221 @@ + grub_free (handles); + } + ++static struct grub_net_buff * ++grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6) ++{ ++ grub_efi_uint16_t uri_len; ++ grub_efi_device_path_t *ldp, *ddp; ++ grub_efi_uri_device_path_t *uri_dp; ++ struct grub_net_buff *nb; ++ grub_err_t err; ++ ++ ddp = grub_efi_duplicate_device_path (dp); ++ ldp = grub_efi_find_last_device_path (ddp); ++ ++ if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE ++ || GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) ++ { ++ grub_free (ddp); ++ return NULL; ++ } ++ ++ uri_len = GRUB_EFI_DEVICE_PATH_LENGTH (ldp) > 4 ? GRUB_EFI_DEVICE_PATH_LENGTH (ldp) - 4 : 0; ++ ++ if (!uri_len) ++ { ++ grub_free (ddp); ++ return NULL; ++ } ++ ++ uri_dp = (grub_efi_uri_device_path_t *) ldp; ++ ++ ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; ++ ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; ++ ldp->length = sizeof (*ldp); ++ ++ ldp = grub_efi_find_last_device_path (ddp); ++ ++ if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE ++ || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE ++ && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) ++ { ++ grub_free (ddp); ++ return NULL; ++ } ++ ++ nb = grub_netbuff_alloc (512); ++ if (!nb) ++ return NULL; ++ ++ if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE) ++ { ++ grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp; ++ struct grub_net_bootp_packet *bp; ++ grub_uint8_t *ptr; ++ ++ bp = (struct grub_net_bootp_packet *) nb->tail; ++ err = grub_netbuff_put (nb, sizeof (*bp) + 4); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ ++ if (sizeof(bp->boot_file) < uri_len) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ grub_memcpy (bp->boot_file, uri_dp->uri, uri_len); ++ grub_memcpy (&bp->your_ip, ipv4->local_ip_address, sizeof (bp->your_ip)); ++ grub_memcpy (&bp->server_ip, ipv4->remote_ip_address, sizeof (bp->server_ip)); ++ ++ bp->vendor[0] = GRUB_NET_BOOTP_RFC1048_MAGIC_0; ++ bp->vendor[1] = GRUB_NET_BOOTP_RFC1048_MAGIC_1; ++ bp->vendor[2] = GRUB_NET_BOOTP_RFC1048_MAGIC_2; ++ bp->vendor[3] = GRUB_NET_BOOTP_RFC1048_MAGIC_3; ++ ++ ptr = nb->tail; ++ err = grub_netbuff_put (nb, sizeof (ipv4->subnet_mask) + 2); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ *ptr++ = GRUB_NET_BOOTP_NETMASK; ++ *ptr++ = sizeof (ipv4->subnet_mask); ++ grub_memcpy (ptr, ipv4->subnet_mask, sizeof (ipv4->subnet_mask)); ++ ++ ptr = nb->tail; ++ err = grub_netbuff_put (nb, sizeof (ipv4->gateway_ip_address) + 2); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ *ptr++ = GRUB_NET_BOOTP_ROUTER; ++ *ptr++ = sizeof (ipv4->gateway_ip_address); ++ grub_memcpy (ptr, ipv4->gateway_ip_address, sizeof (ipv4->gateway_ip_address)); ++ ++ ptr = nb->tail; ++ err = grub_netbuff_put (nb, sizeof ("HTTPClient") + 1); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ *ptr++ = GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER; ++ *ptr++ = sizeof ("HTTPClient") - 1; ++ grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1); ++ ++ ptr = nb->tail; ++ err = grub_netbuff_put (nb, 1); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ *ptr = GRUB_NET_BOOTP_END; ++ *use_ipv6 = 0; ++ ++ ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; ++ ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; ++ ldp->length = sizeof (*ldp); ++ ldp = grub_efi_find_last_device_path (ddp); ++ ++ if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE) ++ { ++ grub_efi_mac_address_device_path_t *mac = (grub_efi_mac_address_device_path_t *) ldp; ++ bp->hw_type = mac->if_type; ++ bp->hw_len = sizeof (bp->mac_addr); ++ grub_memcpy (bp->mac_addr, mac->mac_address, bp->hw_len); ++ } ++ } ++ else ++ { ++ grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) ldp; ++ ++ struct grub_net_dhcp6_packet *d6p; ++ struct grub_net_dhcp6_option *opt; ++ struct grub_net_dhcp6_option_iana *iana; ++ struct grub_net_dhcp6_option_iaaddr *iaaddr; ++ ++ d6p = (struct grub_net_dhcp6_packet *)nb->tail; ++ err = grub_netbuff_put (nb, sizeof(*d6p)); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ d6p->message_type = GRUB_NET_DHCP6_REPLY; ++ ++ opt = (struct grub_net_dhcp6_option *)nb->tail; ++ err = grub_netbuff_put (nb, sizeof(*opt)); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); ++ opt->len = grub_cpu_to_be16_compile_time (sizeof(*iana) + sizeof(*opt) + sizeof(*iaaddr)); ++ ++ err = grub_netbuff_put (nb, sizeof(*iana)); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ ++ opt = (struct grub_net_dhcp6_option *)nb->tail; ++ err = grub_netbuff_put (nb, sizeof(*opt)); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR); ++ opt->len = grub_cpu_to_be16_compile_time (sizeof (*iaaddr)); ++ ++ iaaddr = (struct grub_net_dhcp6_option_iaaddr *)nb->tail; ++ err = grub_netbuff_put (nb, sizeof(*iaaddr)); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ grub_memcpy (iaaddr->addr, ipv6->local_ip_address, sizeof(ipv6->local_ip_address)); ++ ++ opt = (struct grub_net_dhcp6_option *)nb->tail; ++ err = grub_netbuff_put (nb, sizeof(*opt) + uri_len); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL); ++ opt->len = grub_cpu_to_be16 (uri_len); ++ grub_memcpy (opt->data, uri_dp->uri, uri_len); ++ ++ *use_ipv6 = 1; ++ } ++ ++ grub_free (ddp); ++ return nb; ++} ++ + static void + grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, + char **path) +@@ -361,6 +577,11 @@ + grub_efi_device_path_t *cdp; + struct grub_efi_pxe *pxe; + struct grub_efi_pxe_mode *pxe_mode; ++ grub_uint8_t *packet_buf; ++ grub_size_t packet_bufsz ; ++ int ipv6; ++ struct grub_net_buff *nb = NULL; ++ + if (card->driver != &efidriver) + continue; + cdp = grub_efi_get_device_path (card->efi_handle); +@@ -380,11 +601,21 @@ + ldp = grub_efi_find_last_device_path (dp); + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE +- && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) ++ && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE ++ && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)) + continue; + dup_dp = grub_efi_duplicate_device_path (dp); + if (!dup_dp) + continue; ++ ++ if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) ++ { ++ dup_ldp = grub_efi_find_last_device_path (dup_dp); ++ dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; ++ dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; ++ dup_ldp->length = sizeof (*dup_ldp); ++ } ++ + dup_ldp = grub_efi_find_last_device_path (dup_dp); + dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; +@@ -396,16 +627,31 @@ + } + pxe = grub_efi_open_protocol (hnd, &pxe_io_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); +- if (! pxe) +- continue; +- pxe_mode = pxe->mode; ++ if (!pxe) ++ { ++ nb = grub_efinet_create_dhcp_ack_from_device_path (dp, &ipv6); ++ if (!nb) ++ { ++ grub_print_error (); ++ continue; ++ } ++ packet_buf = nb->head; ++ packet_bufsz = nb->tail - nb->head; ++ } ++ else ++ { ++ pxe_mode = pxe->mode; ++ packet_buf = (grub_uint8_t *) &pxe_mode->dhcp_ack; ++ packet_bufsz = sizeof (pxe_mode->dhcp_ack); ++ ipv6 = pxe_mode->using_ipv6; ++ } + +- if (pxe_mode->using_ipv6) ++ if (ipv6) + { + grub_net_configure_by_dhcpv6_reply (card->name, card, 0, + (struct grub_net_dhcp6_packet *) +- &pxe_mode->dhcp_ack, +- sizeof (pxe_mode->dhcp_ack), ++ packet_buf, ++ packet_bufsz, + 1, device, path); + if (grub_errno) + grub_print_error (); +@@ -414,8 +660,8 @@ + { + inter = grub_net_configure_by_dhcp_ack (card->name, card, 0, + (struct grub_net_bootp_packet *) +- &pxe_mode->dhcp_ack, +- sizeof (pxe_mode->dhcp_ack), ++ packet_buf, ++ packet_bufsz, + 1, device, path); + + if (inter != NULL) +@@ -441,6 +687,10 @@ + } + } + } ++ ++ if (nb) ++ grub_netbuff_free (nb); ++ + return; + } + } +--- a/include/grub/efi/api.h ++++ b/include/grub/efi/api.h +@@ -876,6 +876,8 @@ + grub_efi_uint16_t remote_port; + grub_efi_uint16_t protocol; + grub_efi_uint8_t static_ip_address; ++ grub_efi_ipv4_address_t gateway_ip_address; ++ grub_efi_ipv4_address_t subnet_mask; + } GRUB_PACKED; + typedef struct grub_efi_ipv4_device_path grub_efi_ipv4_device_path_t; + +@@ -939,6 +941,15 @@ + } GRUB_PACKED; + typedef struct grub_efi_vlan_device_path grub_efi_vlan_device_path_t; + ++#define GRUB_EFI_URI_DEVICE_PATH_SUBTYPE 24 ++ ++struct grub_efi_uri_device_path ++{ ++ grub_efi_device_path_t header; ++ grub_efi_uint8_t uri[0]; ++} GRUB_PACKED; ++typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t; ++ + #define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE 10 + + /* Media Device Path. */ diff --git a/0007-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch b/0007-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch new file mode 100644 index 0000000..63b7369 --- /dev/null +++ b/0007-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch @@ -0,0 +1,249 @@ +From 2a84f1a50c6f8770808fd4ec590eb8cff4228aed Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Wed, 12 Sep 2018 16:03:55 -0400 +Subject: [PATCH 07/11] x86-efi: Make our own allocator for kernel stuff + +This helps enable allocations above 4GB. + +Signed-off-by: Peter Jones +--- + grub-core/loader/i386/efi/linux.c | 155 ++++++++++++++++++------------ + 1 file changed, 94 insertions(+), 61 deletions(-) + +diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c +index 096a52eb5..d284db5d1 100644 +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -47,6 +47,65 @@ static char *linux_cmdline; + + #define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) + ++struct allocation_choice { ++ grub_efi_physical_address_t addr; ++ grub_efi_allocate_type_t alloc_type; ++}; ++ ++static struct allocation_choice max_addresses[] = ++ { ++ { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, ++ { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, ++ { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, ++ { 0, 0 } ++ }; ++ ++static inline void ++kernel_free(void *addr, grub_efi_uintn_t size) ++{ ++ if (addr && size) ++ grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)addr, ++ BYTES_TO_PAGES(size)); ++} ++ ++static void * ++kernel_alloc(grub_efi_uintn_t size, const char * const errmsg) ++{ ++ void *addr = 0; ++ unsigned int i; ++ grub_efi_physical_address_t prev_max = 0; ++ ++ for (i = 0; max_addresses[i].addr != 0 && addr == 0; i++) ++ { ++ grub_uint64_t max = max_addresses[i].addr; ++ grub_efi_uintn_t pages; ++ ++ if (max == prev_max) ++ continue; ++ ++ pages = BYTES_TO_PAGES(size); ++ grub_dprintf ("linux", "Trying to allocate %" PRIuGRUB_SIZE" pages from %p\n", ++ pages, (void *)(grub_addr_t)max); ++ ++ prev_max = max; ++ addr = grub_efi_allocate_pages_real (max, pages, ++ max_addresses[i].alloc_type, ++ GRUB_EFI_LOADER_DATA); ++ if (addr) ++ grub_dprintf ("linux", "Allocated at %p\n", addr); ++ } ++ ++ while (grub_error_pop ()) ++ { ++ ; ++ } ++ ++ if (addr == NULL) ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, "%s", errmsg); ++ ++ return addr; ++} ++ + static grub_err_t + grub_linuxefi_boot (void) + { +@@ -62,14 +121,12 @@ grub_linuxefi_unload (void) + { + grub_dl_unref (my_mod); + loaded = 0; +- if (initrd_mem) +- grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)initrd_mem, BYTES_TO_PAGES(params->ramdisk_size)); +- if (linux_cmdline) +- grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)linux_cmdline, BYTES_TO_PAGES(params->cmdline_size + 1)); +- if (kernel_mem) +- grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); +- if (params) +- grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)params, BYTES_TO_PAGES(16384)); ++ ++ kernel_free(initrd_mem, params->ramdisk_size); ++ kernel_free(linux_cmdline, params->cmdline_size + 1); ++ kernel_free(kernel_mem, kernel_size); ++ kernel_free(params, sizeof(*params)); ++ + return GRUB_ERR_NONE; + } + +@@ -146,17 +203,13 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + size += ALIGN_UP (grub_file_size (files[i]), 4); + } + +- initrd_mem = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, BYTES_TO_PAGES(size)); +- if (!initrd_mem) +- initrd_mem = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, BYTES_TO_PAGES(size)); +- if (!initrd_mem) +- { +- grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd")); +- goto fail; +- } ++ initrd_mem = kernel_alloc(size, N_("can't allocate initrd")); ++ if (initrd_mem == NULL) ++ goto fail; ++ grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem); + + params->ramdisk_size = size; +- params->ramdisk_image = (grub_uint32_t)(grub_addr_t) initrd_mem; ++ params->ramdisk_image = initrd_mem; + + ptr = initrd_mem; + +@@ -214,7 +267,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + filelen = grub_file_size (file); + + kernel = grub_malloc(filelen); +- + if (!kernel) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); +@@ -258,7 +310,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + goto fail; + } + +-#if defined(__x86_64__) || defined(__aarch64__) ++#if defined(__x86_64__) + grub_dprintf ("linux", "checking lh->xloadflags\n"); + if (!(lh->xloadflags & LINUX_XLF_KERNEL_64)) + { +@@ -277,17 +329,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + } + #endif + +- params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, +- BYTES_TO_PAGES(sizeof(*params))); ++ params = kernel_alloc (sizeof(*params), "cannot allocate kernel parameters"); + if (!params) +- params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, +- BYTES_TO_PAGES(sizeof(*params))); +- if (! params) +- { +- grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); +- goto fail; +- } +- ++ goto fail; + grub_dprintf ("linux", "params = %p\n", params); + + grub_memset (params, 0, sizeof(*params)); +@@ -306,16 +350,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + grub_dprintf ("linux", "new lh is at %p\n", lh); + + grub_dprintf ("linux", "setting up cmdline\n"); +- linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS, +- BYTES_TO_PAGES(lh->cmdline_size + 1)); ++ linux_cmdline = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline")); + if (!linux_cmdline) +- linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_USABLE_ADDRESS, +- BYTES_TO_PAGES(lh->cmdline_size + 1)); +- if (!linux_cmdline) +- { +- grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline")); +- goto fail; +- } ++ goto fail; ++ grub_dprintf ("linux", "linux_cmdline = %p\n", linux_cmdline); + + grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + err = grub_create_loader_cmdline (argc, argv, +@@ -326,27 +364,24 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + goto fail; + + grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline); +- grub_dprintf ("linux", "setting lh->cmd_line_ptr\n"); +- lh->cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline; ++ grub_dprintf ("linux", "setting lh->cmd_line_ptr to 0x%08x\n", ++ linux_cmdline); ++ lh->cmd_line_ptr = linux_cmdline; + + handover_offset = lh->handover_offset; +- grub_dprintf("linux", "handover_offset: %08x\n", handover_offset); ++ grub_dprintf("linux", "handover_offset: 0x%08x\n", handover_offset); + + start = (lh->setup_sects + 1) * 512; + +- kernel_mem = grub_efi_allocate_pages_max(lh->pref_address, +- BYTES_TO_PAGES(lh->init_size)); +- if (!kernel_mem) +- kernel_mem = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS, +- BYTES_TO_PAGES(lh->init_size)); +- if (!kernel_mem) +- kernel_mem = grub_efi_allocate_pages_max(GRUB_EFI_MAX_USABLE_ADDRESS, +- BYTES_TO_PAGES(lh->init_size)); +- if (!kernel_mem) ++ grub_dprintf ("linux", "lh->pref_address: %p\n", (void *)(grub_addr_t)lh->pref_address); ++ if (lh->pref_address < (grub_uint64_t)GRUB_EFI_MAX_ALLOCATION_ADDRESS) + { +- grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel")); +- goto fail; ++ max_addresses[0].addr = lh->pref_address; ++ max_addresses[0].alloc_type = GRUB_EFI_ALLOCATE_ADDRESS; + } ++ kernel_mem = kernel_alloc (lh->init_size, N_("can't allocate kernel")); ++ if (!kernel_mem) ++ goto fail; + grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem); + + grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); +@@ -382,16 +417,14 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + loaded = 0; + } + +- if (linux_cmdline && lh && !loaded) +- grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t) +- linux_cmdline, +- BYTES_TO_PAGES(lh->cmdline_size + 1)); +- +- if (kernel_mem && !loaded) +- grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); ++ if (!loaded) ++ { ++ if (lh) ++ kernel_free (linux_cmdline, lh->cmdline_size + 1); + +- if (params && !loaded) +- grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)params, BYTES_TO_PAGES(16384)); ++ kernel_free (kernel_mem, kernel_size); ++ kernel_free (params, sizeof(*params)); ++ } + + return grub_errno; + } +-- +2.31.1 + diff --git a/0008-efinet-Setting-DNS-server-from-UEFI-protocol.patch b/0008-efinet-Setting-DNS-server-from-UEFI-protocol.patch new file mode 100644 index 0000000..b3f00c8 --- /dev/null +++ b/0008-efinet-Setting-DNS-server-from-UEFI-protocol.patch @@ -0,0 +1,331 @@ +From 0c7ae6d7527d08e54a6eeebddb6c5c697c4b37d2 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 14 Jul 2016 17:48:45 +0800 +Subject: [PATCH 8/8] efinet: Setting DNS server from UEFI protocol + +In the URI device path node, any name rahter than address can be used for +looking up the resources so that DNS service become needed to get answer of the +name's address. Unfortunately the DNS is not defined in any of the device path +nodes so that we use the EFI_IP4_CONFIG2_PROTOCOL and EFI_IP6_CONFIG_PROTOCOL +to obtain it. + +These two protcols are defined the sections of UEFI specification. + + 27.5 EFI IPv4 Configuration II Protocol + 27.7 EFI IPv6 Configuration Protocol + +include/grub/efi/api.h: +Add new structure and protocol UUID of EFI_IP4_CONFIG2_PROTOCOL and +EFI_IP6_CONFIG_PROTOCOL. + +grub-core/net/drivers/efi/efinet.c: +Use the EFI_IP4_CONFIG2_PROTOCOL and EFI_IP6_CONFIG_PROTOCOL to obtain the list +of DNS server address for IPv4 and IPv6 respectively. The address of DNS +servers is structured into DHCPACK packet and feed into the same DHCP packet +processing functions to ensure the network interface is setting up the same way +it used to be. + +Signed-off-by: Michael Chang +Signed-off-by: Ken Lin +--- + grub-core/net/drivers/efi/efinet.c | 163 +++++++++++++++++++++++++++++++++++++ + include/grub/efi/api.h | 76 +++++++++++++++++ + 2 files changed, 239 insertions(+) + +--- a/grub-core/net/drivers/efi/efinet.c ++++ b/grub-core/net/drivers/efi/efinet.c +@@ -30,6 +30,8 @@ + /* GUID. */ + static grub_guid_t net_io_guid = GRUB_EFI_SIMPLE_NETWORK_GUID; + static grub_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID; ++static grub_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID; ++static grub_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID; + + static grub_err_t + send_card_buffer (struct grub_net_card *dev, +@@ -342,6 +344,125 @@ + grub_free (handles); + } + ++static grub_efi_handle_t ++grub_efi_locate_device_path (grub_efi_guid_t *protocol, grub_efi_device_path_t *device_path, ++ grub_efi_device_path_t **r_device_path) ++{ ++ grub_efi_handle_t handle; ++ grub_efi_status_t status; ++ ++ status = grub_efi_system_table->boot_services->locate_device_path ( ++ protocol, &device_path, &handle); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return 0; ++ ++ if (r_device_path) ++ *r_device_path = device_path; ++ ++ return handle; ++} ++ ++static grub_efi_ipv4_address_t * ++grub_dns_server_ip4_address (grub_efi_device_path_t *dp, grub_efi_uintn_t *num_dns) ++{ ++ grub_efi_handle_t hnd; ++ grub_efi_status_t status; ++ grub_efi_ip4_config2_protocol_t *conf; ++ grub_efi_ipv4_address_t *addrs; ++ grub_efi_uintn_t data_size = 1 * sizeof (grub_efi_ipv4_address_t); ++ ++ hnd = grub_efi_locate_device_path (&ip4_config_guid, dp, NULL); ++ ++ if (!hnd) ++ return 0; ++ ++ conf = grub_efi_open_protocol (hnd, &ip4_config_guid, ++ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); ++ ++ if (!conf) ++ return 0; ++ ++ addrs = grub_malloc (data_size); ++ if (!addrs) ++ return 0; ++ ++ status = conf->get_data (conf, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, ++ &data_size, addrs); ++ ++ if (status == GRUB_EFI_BUFFER_TOO_SMALL) ++ { ++ grub_free (addrs); ++ addrs = grub_malloc (data_size); ++ if (!addrs) ++ return 0; ++ ++ status = conf->get_data (conf, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, ++ &data_size, addrs); ++ } ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_free (addrs); ++ return 0; ++ } ++ ++ *num_dns = data_size / sizeof (grub_efi_ipv4_address_t); ++ return addrs; ++} ++ ++static grub_efi_ipv6_address_t * ++grub_dns_server_ip6_address (grub_efi_device_path_t *dp, grub_efi_uintn_t *num_dns) ++{ ++ grub_efi_handle_t hnd; ++ grub_efi_status_t status; ++ grub_efi_ip6_config_protocol_t *conf; ++ grub_efi_ipv6_address_t *addrs; ++ grub_efi_uintn_t data_size = 1 * sizeof (grub_efi_ipv6_address_t); ++ ++ hnd = grub_efi_locate_device_path (&ip6_config_guid, dp, NULL); ++ ++ if (!hnd) ++ return 0; ++ ++ conf = grub_efi_open_protocol (hnd, &ip6_config_guid, ++ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); ++ ++ if (!conf) ++ return 0; ++ ++ addrs = grub_malloc (data_size); ++ if (!addrs) ++ return 0; ++ ++ status = conf->get_data (conf, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, ++ &data_size, addrs); ++ ++ if (status == GRUB_EFI_BUFFER_TOO_SMALL) ++ { ++ grub_free (addrs); ++ addrs = grub_malloc (data_size); ++ if (!addrs) ++ return 0; ++ ++ status = conf->get_data (conf, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, ++ &data_size, addrs); ++ } ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_free (addrs); ++ return 0; ++ } ++ ++ *num_dns = data_size / sizeof (grub_efi_ipv6_address_t); ++ return addrs; ++} ++ + static struct grub_net_buff * + grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6) + { +@@ -394,6 +515,8 @@ + grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp; + struct grub_net_bootp_packet *bp; + grub_uint8_t *ptr; ++ grub_efi_ipv4_address_t *dns; ++ grub_efi_uintn_t num_dns; + + bp = (struct grub_net_bootp_packet *) nb->tail; + err = grub_netbuff_put (nb, sizeof (*bp) + 4); +@@ -455,6 +578,25 @@ + *ptr++ = sizeof ("HTTPClient") - 1; + grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1); + ++ dns = grub_dns_server_ip4_address (dp, &num_dns); ++ if (dns) ++ { ++ grub_efi_uintn_t size_dns = sizeof (*dns) * num_dns; ++ ++ ptr = nb->tail; ++ err = grub_netbuff_put (nb, size_dns + 2); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ *ptr++ = GRUB_NET_BOOTP_DNS; ++ *ptr++ = size_dns; ++ grub_memcpy (ptr, dns, size_dns); ++ grub_free (dns); ++ } ++ + ptr = nb->tail; + err = grub_netbuff_put (nb, 1); + if (err) +@@ -487,6 +629,8 @@ + struct grub_net_dhcp6_option *opt; + struct grub_net_dhcp6_option_iana *iana; + struct grub_net_dhcp6_option_iaaddr *iaaddr; ++ grub_efi_ipv6_address_t *dns; ++ grub_efi_uintn_t num_dns; + + d6p = (struct grub_net_dhcp6_packet *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*d6p)); +@@ -550,6 +694,25 @@ + opt->len = grub_cpu_to_be16 (uri_len); + grub_memcpy (opt->data, uri_dp->uri, uri_len); + ++ dns = grub_dns_server_ip6_address (dp, &num_dns); ++ if (dns) ++ { ++ grub_efi_uintn_t size_dns = sizeof (*dns) * num_dns; ++ ++ opt = (struct grub_net_dhcp6_option *)nb->tail; ++ err = grub_netbuff_put (nb, sizeof(*opt) + size_dns); ++ if (err) ++ { ++ grub_free (ddp); ++ grub_netbuff_free (nb); ++ return NULL; ++ } ++ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS); ++ opt->len = grub_cpu_to_be16 (size_dns); ++ grub_memcpy (opt->data, dns, size_dns); ++ grub_free (dns); ++ } ++ + *use_ipv6 = 1; + } + +--- a/include/grub/efi/api.h ++++ b/include/grub/efi/api.h +@@ -379,6 +379,16 @@ + {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } \ + } + ++#define GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID \ ++ { 0x5b446ed1, 0xe30b, 0x4faa, \ ++ { 0x87, 0x1a, 0x36, 0x54, 0xec, 0xa3, 0x60, 0x80 } \ ++ } ++ ++#define GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID \ ++ { 0x937fe521, 0x95ae, 0x4d1a, \ ++ { 0x89, 0x29, 0x48, 0xbc, 0xd9, 0x0a, 0xd3, 0x1a } \ ++ } ++ + struct grub_efi_sal_system_table + { + grub_uint32_t signature; +@@ -1879,4 +1889,70 @@ + } GRUB_PACKED; + typedef struct initrd_media_device_path initrd_media_device_path_t; + ++enum grub_efi_ip4_config2_data_type { ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_POLICY, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, ++ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MAXIMUM ++}; ++typedef enum grub_efi_ip4_config2_data_type grub_efi_ip4_config2_data_type_t; ++ ++struct grub_efi_ip4_config2_protocol ++{ ++ grub_efi_status_t (__grub_efi_api *set_data) (struct grub_efi_ip4_config2_protocol *this, ++ grub_efi_ip4_config2_data_type_t data_type, ++ grub_efi_uintn_t data_size, ++ void *data); ++ ++ grub_efi_status_t (__grub_efi_api *get_data) (struct grub_efi_ip4_config2_protocol *this, ++ grub_efi_ip4_config2_data_type_t data_type, ++ grub_efi_uintn_t *data_size, ++ void *data); ++ ++ grub_efi_status_t (__grub_efi_api *register_data_notify) (struct grub_efi_ip4_config2_protocol *this, ++ grub_efi_ip4_config2_data_type_t data_type, ++ grub_efi_event_t event); ++ ++ grub_efi_status_t (__grub_efi_api *unregister_datanotify) (struct grub_efi_ip4_config2_protocol *this, ++ grub_efi_ip4_config2_data_type_t data_type, ++ grub_efi_event_t event); ++}; ++typedef struct grub_efi_ip4_config2_protocol grub_efi_ip4_config2_protocol_t; ++ ++enum grub_efi_ip6_config_data_type { ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_ALT_INTERFACEID, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_POLICY, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_DUP_ADDR_DETECT_TRANSMITS, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_GATEWAY, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, ++ GRUB_EFI_IP6_CONFIG_DATA_TYPE_MAXIMUM ++}; ++typedef enum grub_efi_ip6_config_data_type grub_efi_ip6_config_data_type_t; ++ ++struct grub_efi_ip6_config_protocol ++{ ++ grub_efi_status_t (__grub_efi_api *set_data) (struct grub_efi_ip6_config_protocol *this, ++ grub_efi_ip6_config_data_type_t data_type, ++ grub_efi_uintn_t data_size, ++ void *data); ++ ++ grub_efi_status_t (__grub_efi_api *get_data) (struct grub_efi_ip6_config_protocol *this, ++ grub_efi_ip6_config_data_type_t data_type, ++ grub_efi_uintn_t *data_size, ++ void *data); ++ ++ grub_efi_status_t (__grub_efi_api *register_data_notify) (struct grub_efi_ip6_config_protocol *this, ++ grub_efi_ip6_config_data_type_t data_type, ++ grub_efi_event_t event); ++ ++ grub_efi_status_t (__grub_efi_api *unregister_datanotify) (struct grub_efi_ip6_config_protocol *this, ++ grub_efi_ip6_config_data_type_t data_type, ++ grub_efi_event_t event); ++}; ++typedef struct grub_efi_ip6_config_protocol grub_efi_ip6_config_protocol_t; ++ + #endif /* ! GRUB_EFI_API_HEADER */ diff --git a/0008-linuxefi-Use-common-grub_initrd_load.patch b/0008-linuxefi-Use-common-grub_initrd_load.patch new file mode 100644 index 0000000..e65d363 --- /dev/null +++ b/0008-linuxefi-Use-common-grub_initrd_load.patch @@ -0,0 +1,151 @@ +From adf486860fe0d395579be8b01d4fda8b93377768 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Wed, 8 Jun 2022 16:04:12 +0800 +Subject: [PATCH 08/10] linuxefi: Use common grub_initrd_load + +By using the common initrd loading routine factored out allows to share between +features like concatenating initramfs component. + +For eg. + + initrdefi /initrd-5.16.15-1-default newc:grub.cfg:/grub2/grub.cfg + +The file /grub2/grub.cfg read off from root disk will be available to use as +/grub.cfg in the target initramfs loaded by grub. + +Signed-off-by: Michael Chang +--- + grub-core/loader/i386/efi/linux.c | 87 ++++--------------------------- + 1 file changed, 10 insertions(+), 77 deletions(-) + +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -146,44 +147,6 @@ + return GRUB_ERR_NONE; + } + +-#define BOUNCE_BUFFER_MAX 0x1000000ull +- +-static grub_ssize_t +-read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len) +-{ +- grub_ssize_t bufpos = 0; +- static grub_size_t bbufsz = 0; +- static char *bbuf = NULL; +- +- if (bbufsz == 0) +- bbufsz = MIN(BOUNCE_BUFFER_MAX, len); +- +- while (!bbuf && bbufsz) +- { +- bbuf = grub_malloc(bbufsz); +- if (!bbuf) +- bbufsz >>= 1; +- } +- if (!bbuf) +- grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate bounce buffer")); +- +- while (bufpos < (long long)len) +- { +- grub_ssize_t sz; +- +- sz = grub_file_read (file, bbuf, MIN(bbufsz, len - bufpos)); +- if (sz < 0) +- return sz; +- if (sz == 0) +- break; +- +- grub_memcpy(bufp + bufpos, bbuf, sz); +- bufpos += sz; +- } +- +- return bufpos; +-} +- + #define LOW_U32(val) ((grub_uint32_t)(((grub_addr_t)(val)) & 0xffffffffull)) + #define HIGH_U32(val) ((grub_uint32_t)(((grub_addr_t)(val) >> 32) & 0xffffffffull)) + +@@ -191,10 +154,8 @@ + grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) + { +- grub_file_t *files = 0; +- int i, nfiles = 0; ++ struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 }; + grub_size_t size = 0; +- grub_uint8_t *ptr; + + if (argc == 0) + { +@@ -208,24 +169,10 @@ + goto fail; + } + +- files = grub_calloc (argc, sizeof (files[0])); +- if (!files) ++ if (grub_initrd_init (argc, argv, &initrd_ctx)) + goto fail; + +- for (i = 0; i < argc; i++) +- { +- files[i] = grub_file_open (argv[i], GRUB_FILE_TYPE_LINUX_INITRD +- | GRUB_FILE_TYPE_NO_DECOMPRESS); +- if (! files[i]) +- goto fail; +- nfiles++; +- if (grub_add (size, ALIGN_UP (grub_file_size (files[i]), 4), &size)) +- { +- grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); +- goto fail; +- } +- } +- ++ size = grub_get_initrd_size (&initrd_ctx); + initrd_mem = kernel_alloc(size, N_("can't allocate initrd")); + if (initrd_mem == NULL) + goto fail; +@@ -238,30 +185,16 @@ + params->ext_ramdisk_image = HIGH_U32(initrd_mem); + #endif + +- ptr = initrd_mem; +- +- for (i = 0; i < nfiles; i++) +- { +- grub_ssize_t cursize = grub_file_size (files[i]); +- if (read (files[i], ptr, cursize) != cursize) +- { +- if (!grub_errno) +- grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), +- argv[i]); +- goto fail; +- } +- ptr += cursize; +- grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); +- ptr += ALIGN_UP_OVERHEAD (cursize, 4); +- } ++ /* FIXME: Use bounce buffers as many UEFI machines apparently can't DMA ++ * correctly above 4GB ++ */ ++ if (grub_initrd_load (&initrd_ctx, initrd_mem)) ++ goto fail; + + params->ramdisk_size = size; + + fail: +- for (i = 0; i < nfiles; i++) +- grub_file_close (files[i]); +- grub_free (files); +- ++ grub_initrd_close (&initrd_ctx); + if (initrd_mem && grub_errno) + grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)initrd_mem, BYTES_TO_PAGES(size)); + diff --git a/0008-pgp-factor-out-rsa_pad.patch b/0008-pgp-factor-out-rsa_pad.patch new file mode 100644 index 0000000..0690b83 --- /dev/null +++ b/0008-pgp-factor-out-rsa_pad.patch @@ -0,0 +1,181 @@ +From 923c8f6807cbd93b72d4dcb16c213d0d2a6b5b9a Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Thu, 1 Oct 2020 20:23:48 +1000 +Subject: [PATCH 08/23] pgp: factor out rsa_pad + +rsa_pad does the PKCS#1 v1.5 padding for the RSA signature scheme. +We want to use it in other RSA signature verification applications. + +I considered and rejected putting it in lib/crypto.c. That file doesn't +currently require any MPI functions, but rsa_pad does. That's not so +much of a problem for the grub kernel and modules, but crypto.c also +gets built into all the grub utilities. So - despite the utils not +using any asymmetric ciphers - we would need to built the entire MPI +infrastructure in to them. + +A better and simpler solution is just to spin rsa_pad out into its own +PKCS#1 v1.5 module. + +Signed-off-by: Daniel Axtens +--- + grub-core/Makefile.core.def | 8 +++++ + grub-core/commands/pgp.c | 28 ++---------------- + grub-core/lib/pkcs1_v15.c | 59 +++++++++++++++++++++++++++++++++++++ + include/grub/pkcs1_v15.h | 27 +++++++++++++++++ + 4 files changed, 96 insertions(+), 26 deletions(-) + create mode 100644 grub-core/lib/pkcs1_v15.c + create mode 100644 include/grub/pkcs1_v15.h + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2542,6 +2542,14 @@ + }; + + module = { ++ name = pkcs1_v15; ++ common = lib/pkcs1_v15.c; ++ ++ cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare'; ++ cppflags = '$(CPPFLAGS_GCRY)'; ++}; ++ ++module = { + name = all_video; + common = lib/fake_module.c; + }; +--- a/grub-core/commands/pgp.c ++++ b/grub-core/commands/pgp.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -411,32 +412,7 @@ + rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, + const gcry_md_spec_t *hash, struct grub_public_subkey *sk) + { +- grub_size_t tlen, emlen, fflen; +- grub_uint8_t *em, *emptr; +- unsigned nbits = gcry_mpi_get_nbits (sk->mpis[0]); +- int ret; +- tlen = hash->mdlen + hash->asnlen; +- emlen = (nbits + 7) / 8; +- if (emlen < tlen + 11) +- return 1; +- +- em = grub_malloc (emlen); +- if (!em) +- return 1; +- +- em[0] = 0x00; +- em[1] = 0x01; +- fflen = emlen - tlen - 3; +- for (emptr = em + 2; emptr < em + 2 + fflen; emptr++) +- *emptr = 0xff; +- *emptr++ = 0x00; +- grub_memcpy (emptr, hash->asnoid, hash->asnlen); +- emptr += hash->asnlen; +- grub_memcpy (emptr, hval, hash->mdlen); +- +- ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0); +- grub_free (em); +- return ret; ++ return grub_crypto_rsa_pad(hmpi, hval, hash, sk->mpis[0]); + } + + struct grub_pubkey_context +--- /dev/null ++++ b/grub-core/lib/pkcs1_v15.c +@@ -0,0 +1,59 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2013 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++/* ++ * Given a hash value 'hval', of hash specification 'hash', perform ++ * the EMSA-PKCS1-v1_5 padding suitable for a key with modulus 'mod' ++ * (see RFC 8017 s 9.2) and place the result in 'hmpi'. ++ */ ++gcry_err_code_t ++grub_crypto_rsa_pad (gcry_mpi_t * hmpi, grub_uint8_t * hval, ++ const gcry_md_spec_t * hash, gcry_mpi_t mod) ++{ ++ grub_size_t tlen, emlen, fflen; ++ grub_uint8_t *em, *emptr; ++ unsigned nbits = gcry_mpi_get_nbits (mod); ++ int ret; ++ tlen = hash->mdlen + hash->asnlen; ++ emlen = (nbits + 7) / 8; ++ if (emlen < tlen + 11) ++ return GPG_ERR_TOO_SHORT; ++ ++ em = grub_malloc (emlen); ++ if (!em) ++ return 1; ++ ++ em[0] = 0x00; ++ em[1] = 0x01; ++ fflen = emlen - tlen - 3; ++ for (emptr = em + 2; emptr < em + 2 + fflen; emptr++) ++ *emptr = 0xff; ++ *emptr++ = 0x00; ++ grub_memcpy (emptr, hash->asnoid, hash->asnlen); ++ emptr += hash->asnlen; ++ grub_memcpy (emptr, hval, hash->mdlen); ++ ++ ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0); ++ grub_free (em); ++ return ret; ++} +--- /dev/null ++++ b/include/grub/pkcs1_v15.h +@@ -0,0 +1,27 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2013 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++/* ++ * Given a hash value 'hval', of hash specification 'hash', perform ++ * the EMSA-PKCS1-v1_5 padding suitable for a key with modulus 'mod' ++ * (See RFC 8017 s 9.2) ++ */ ++gcry_err_code_t ++grub_crypto_rsa_pad (gcry_mpi_t * hmpi, grub_uint8_t * hval, ++ const gcry_md_spec_t * hash, gcry_mpi_t mod); ++ diff --git a/0008-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch b/0008-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch new file mode 100644 index 0000000..6c8c61e --- /dev/null +++ b/0008-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch @@ -0,0 +1,175 @@ +From 8fbcf9f2e97c98bdb63ae7d544aa9bb273022403 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Wed, 12 Sep 2018 16:12:27 -0400 +Subject: [PATCH 08/11] x86-efi: Allow initrd+params+cmdline allocations above + 4GB. + +This enables everything except the kernel itself to be above 4GB. +Putting the kernel up there still doesn't work, because of the way +params->code32_start is used. + +Signed-off-by: Peter Jones +--- + grub-core/loader/i386/efi/linux.c | 67 +++++++++++++++++++++++++++---- + include/grub/i386/linux.h | 6 ++- + 2 files changed, 65 insertions(+), 8 deletions(-) + +diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c +index d284db5d1..d49749269 100644 +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -52,13 +52,22 @@ struct allocation_choice { + grub_efi_allocate_type_t alloc_type; + }; + +-static struct allocation_choice max_addresses[] = ++static struct allocation_choice max_addresses[4] = + { ++ /* the kernel overrides this one with pref_address and ++ * GRUB_EFI_ALLOCATE_ADDRESS */ + { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, ++ /* this one is always below 4GB, which we still *prefer* even if the flag ++ * is set. */ + { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, ++ /* If the flag in params is set, this one gets changed to be above 4GB. */ + { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + { 0, 0 } + }; ++static struct allocation_choice saved_addresses[4]; ++ ++#define save_addresses() grub_memcpy(saved_addresses, max_addresses, sizeof(max_addresses)) ++#define restore_addresses() grub_memcpy(max_addresses, saved_addresses, sizeof(max_addresses)) + + static inline void + kernel_free(void *addr, grub_efi_uintn_t size) +@@ -80,6 +89,11 @@ kernel_alloc(grub_efi_uintn_t size, const char * const errmsg) + grub_uint64_t max = max_addresses[i].addr; + grub_efi_uintn_t pages; + ++ /* ++ * When we're *not* loading the kernel, or >4GB allocations aren't ++ * supported, these entries are basically all the same, so don't re-try ++ * the same parameters. ++ */ + if (max == prev_max) + continue; + +@@ -168,6 +182,9 @@ read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len) + return bufpos; + } + ++#define LOW_U32(val) ((grub_uint32_t)(((grub_addr_t)(val)) & 0xffffffffull)) ++#define HIGH_U32(val) ((grub_uint32_t)(((grub_addr_t)(val) >> 32) & 0xffffffffull)) ++ + static grub_err_t + grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +@@ -208,8 +225,12 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + goto fail; + grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem); + +- params->ramdisk_size = size; +- params->ramdisk_image = initrd_mem; ++ params->ramdisk_size = LOW_U32(size); ++ params->ramdisk_image = LOW_U32(initrd_mem); ++#if defined(__x86_64__) ++ params->ext_ramdisk_size = HIGH_U32(size); ++ params->ext_ramdisk_image = HIGH_U32(initrd_mem); ++#endif + + ptr = initrd_mem; + +@@ -329,6 +350,18 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + } + #endif + ++#if defined(__x86_64__) ++ if (lh->xloadflags & LINUX_XLF_CAN_BE_LOADED_ABOVE_4G) ++ { ++ grub_dprintf ("linux", "Loading kernel above 4GB is supported; enabling.\n"); ++ max_addresses[2].addr = GRUB_EFI_MAX_USABLE_ADDRESS; ++ } ++ else ++ { ++ grub_dprintf ("linux", "Loading kernel above 4GB is not supported\n"); ++ } ++#endif ++ + params = kernel_alloc (sizeof(*params), "cannot allocate kernel parameters"); + if (!params) + goto fail; +@@ -365,21 +398,40 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + + grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline); + grub_dprintf ("linux", "setting lh->cmd_line_ptr to 0x%08x\n", +- linux_cmdline); +- lh->cmd_line_ptr = linux_cmdline; ++ LOW_U32(linux_cmdline)); ++ lh->cmd_line_ptr = LOW_U32(linux_cmdline); ++#if defined(__x86_64__) ++ if ((grub_efi_uintn_t)linux_cmdline > 0xffffffffull) ++ { ++ grub_dprintf ("linux", "setting params->ext_cmd_line_ptr to 0x%08x\n", ++ HIGH_U32(linux_cmdline)); ++ params->ext_cmd_line_ptr = HIGH_U32(linux_cmdline); ++ } ++#endif + + handover_offset = lh->handover_offset; + grub_dprintf("linux", "handover_offset: 0x%08x\n", handover_offset); + + start = (lh->setup_sects + 1) * 512; + ++ /* ++ * AFAICS >4GB for kernel *cannot* work because of params->code32_start being ++ * 32-bit and getting called unconditionally in head_64.S from either entry ++ * point. ++ * ++ * so nerf that out here... ++ */ ++ save_addresses(); + grub_dprintf ("linux", "lh->pref_address: %p\n", (void *)(grub_addr_t)lh->pref_address); + if (lh->pref_address < (grub_uint64_t)GRUB_EFI_MAX_ALLOCATION_ADDRESS) + { + max_addresses[0].addr = lh->pref_address; + max_addresses[0].alloc_type = GRUB_EFI_ALLOCATE_ADDRESS; + } ++ max_addresses[1].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; ++ max_addresses[2].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; + kernel_mem = kernel_alloc (lh->init_size, N_("can't allocate kernel")); ++ restore_addresses(); + if (!kernel_mem) + goto fail; + grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem); +@@ -388,8 +440,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + + loaded = 1; + +- grub_dprintf ("linux", "setting lh->code32_start to %p\n", kernel_mem); +- lh->code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem; ++ grub_dprintf ("linux", "setting lh->code32_start to 0x%08x\n", ++ LOW_U32(kernel_mem)); ++ lh->code32_start = LOW_U32(kernel_mem); + + grub_memcpy (kernel_mem, (char *)kernel + start, filelen - start); + +diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h +index 25ef52c04..fac22476c 100644 +--- a/include/grub/i386/linux.h ++++ b/include/grub/i386/linux.h +@@ -236,7 +236,11 @@ struct linux_kernel_params + grub_uint32_t ofw_cif_handler; /* b8 */ + grub_uint32_t ofw_idt; /* bc */ + +- grub_uint8_t padding7[0x1b8 - 0xc0]; ++ grub_uint32_t ext_ramdisk_image; /* 0xc0 */ ++ grub_uint32_t ext_ramdisk_size; /* 0xc4 */ ++ grub_uint32_t ext_cmd_line_ptr; /* 0xc8 */ ++ ++ grub_uint8_t padding7[0x1b8 - 0xcc]; + + union + { +-- +2.31.1 + diff --git a/0009-Add-crypttab_entry-to-obviate-the-need-to-input-pass.patch b/0009-Add-crypttab_entry-to-obviate-the-need-to-input-pass.patch new file mode 100644 index 0000000..5d9b230 --- /dev/null +++ b/0009-Add-crypttab_entry-to-obviate-the-need-to-input-pass.patch @@ -0,0 +1,327 @@ +From 749f7dee6f63217e536663aebb817aec72a65d5a Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 9 Jun 2022 21:06:00 +0800 +Subject: [PATCH 09/10] Add crypttab_entry to obviate the need to input + password twice + +This patch adds crypttab_entry command to hint grub where to put the key file +automatically loaded by linux cryptsetup. It's syntax is similar to +/etc/crypttab so that it is relatively straightforward to import. + + crypttab_entry + +For eg: + + crypttab_entry cr_root 5e1dd109e39343f984da57fd742d3f23 none + +Please note the "encrypted-device" only accepts UUID without dashes as it is +the only identification used by grub's cryptodisk device. The crypttab_entry +can also be used multiple times to specify encrypted volumes unlocked by +"cryptomount -a". + +Signed-off-by: Michael Chang +--- + grub-core/Makefile.core.def | 5 + + grub-core/commands/crypttab.c | 47 ++++++++++++++ + grub-core/disk/cryptodisk.c | 6 + + grub-core/loader/linux.c | 137 ++++++++++++++++++++++++++++++++++++++++-- + include/grub/linux.h | 3 + 5 files changed, 193 insertions(+), 5 deletions(-) + create mode 100644 grub-core/commands/crypttab.c + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2695,3 +2695,8 @@ + cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; + cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)'; + }; ++ ++module = { ++ name = crypttab; ++ common = commands/crypttab.c; ++}; +--- /dev/null ++++ b/grub-core/commands/crypttab.c +@@ -0,0 +1,47 @@ ++ ++#include ++#include ++#include ++#include ++#include ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++static grub_err_t ++grub_cmd_crypttab_entry (grub_command_t cmd __attribute__ ((unused)), ++ int argc, char **argv) ++{ ++ char buf[64]; ++ const char *path = NULL; ++ ++ if (argc == 2) ++ path = NULL; ++ else if (argc == 3) ++ path = argv[2]; ++ else ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two or three arguments expected")); ++ ++ if (!path ++ || grub_strcmp (path, "none") == 0 ++ || grub_strcmp (path, "-") == 0) ++ { ++ grub_snprintf (buf, sizeof (buf), "/etc/cryptsetup-keys.d/%s.key", argv[0]); ++ path = buf; ++ } ++ ++ /*FIXME: Validate UUID string*/ ++ return grub_initrd_publish_key (argv[1], NULL, 0, path); ++} ++ ++static grub_command_t cmd; ++ ++GRUB_MOD_INIT(crypttab) ++{ ++ cmd = grub_register_command ("crypttab_entry", grub_cmd_crypttab_entry, ++ N_("VOLUME-NAME ENCRYPTED-DEVICE KEY-FILE") , N_("No description")); ++} ++ ++GRUB_MOD_FINI(crypttab) ++{ ++ grub_unregister_command (cmd); ++} +--- a/grub-core/disk/cryptodisk.c ++++ b/grub-core/disk/cryptodisk.c +@@ -30,6 +30,8 @@ + + #ifdef GRUB_UTIL + #include ++#else ++#include + #endif + + GRUB_MOD_LICENSE ("GPLv3+"); +@@ -1235,6 +1237,10 @@ + if (cargs->hdr_file != NULL) + source->read_hook = NULL; + ++#ifndef GRUB_UTIL ++ if (cargs->key_data && dev) ++ grub_initrd_publish_key (dev->uuid, (const char *)cargs->key_data, cargs->key_len, NULL); ++#endif + if (askpass) + { + cargs->key_len = 0; +--- a/grub-core/loader/linux.c ++++ b/grub-core/loader/linux.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + + struct newc_head + { +@@ -27,6 +28,7 @@ + struct grub_linux_initrd_component + { + grub_file_t file; ++ char *buf; + char *newc_name; + grub_off_t size; + }; +@@ -38,6 +40,18 @@ + struct dir *child; + }; + ++struct grub_key_publisher ++{ ++ struct grub_key_publisher *next; ++ struct grub_key_publisher **prev; ++ char *name; /* UUID */ ++ char *path; ++ char *key; ++ grub_size_t key_len; ++}; ++ ++static struct grub_key_publisher *kpuber; ++ + static char + hex (grub_uint8_t val) + { +@@ -162,6 +176,65 @@ + return GRUB_ERR_NONE; + } + ++static grub_err_t ++grub_initrd_component (const char *buf, int bufsz, const char *newc_name, ++ struct grub_linux_initrd_context *initrd_ctx) ++{ ++ struct dir *root = 0; ++ struct grub_linux_initrd_component *comp = initrd_ctx->components + initrd_ctx->nfiles; ++ grub_size_t dir_size, name_len; ++ ++ while (*newc_name == '/') ++ newc_name++; ++ ++ initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4); ++ comp->newc_name = grub_strdup (newc_name); ++ if (!comp->newc_name || ++ insert_dir (comp->newc_name, &root, 0, &dir_size)) ++ { ++ /* FIXME: Check NULL file pointer before close */ ++ grub_initrd_close (initrd_ctx); ++ return grub_errno; ++ } ++ /* Should name_len count terminating null ? */ ++ name_len = grub_strlen (comp->newc_name) + 1; ++ if (grub_add (initrd_ctx->size, ++ ALIGN_UP (sizeof (struct newc_head) + name_len, 4), ++ &initrd_ctx->size) || ++ grub_add (initrd_ctx->size, dir_size, &initrd_ctx->size)) ++ goto overflow; ++ ++ comp->buf = grub_malloc (bufsz); ++ if (!comp->buf) ++ { ++ free_dir (root); ++ grub_initrd_close (initrd_ctx); ++ return grub_errno; ++ } ++ grub_memcpy (comp->buf, buf, bufsz); ++ initrd_ctx->nfiles++; ++ comp->size = bufsz; ++ if (grub_add (initrd_ctx->size, comp->size, ++ &initrd_ctx->size)) ++ goto overflow; ++ ++ initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4); ++ if (grub_add (initrd_ctx->size, ++ ALIGN_UP (sizeof (struct newc_head) ++ + sizeof ("TRAILER!!!") - 1, 4), ++ &initrd_ctx->size)) ++ goto overflow; ++ ++ free_dir (root); ++ root = 0; ++ return GRUB_ERR_NONE; ++ ++ overflow: ++ free_dir (root); ++ grub_initrd_close (initrd_ctx); ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); ++} ++ + grub_err_t + grub_initrd_init (int argc, char *argv[], + struct grub_linux_initrd_context *initrd_ctx) +@@ -169,11 +242,17 @@ + int i; + int newc = 0; + struct dir *root = 0; ++ struct grub_key_publisher *pk; ++ int numkey = 0; + + initrd_ctx->nfiles = 0; + initrd_ctx->components = 0; + +- initrd_ctx->components = grub_calloc (argc, sizeof (initrd_ctx->components[0])); ++ FOR_LIST_ELEMENTS (pk, kpuber) ++ if (pk->key && pk->path) ++ numkey++; ++ ++ initrd_ctx->components = grub_calloc (argc + numkey, sizeof (initrd_ctx->components[0])); + if (!initrd_ctx->components) + return grub_errno; + +@@ -253,6 +332,10 @@ + root = 0; + } + ++ FOR_LIST_ELEMENTS (pk, kpuber) ++ if (pk->key && pk->path) ++ grub_initrd_component (pk->key, pk->key_len, pk->path, initrd_ctx); ++ + return GRUB_ERR_NONE; + + overflow: +@@ -276,7 +359,9 @@ + for (i = 0; i < initrd_ctx->nfiles; i++) + { + grub_free (initrd_ctx->components[i].newc_name); +- grub_file_close (initrd_ctx->components[i].file); ++ if (initrd_ctx->components[i].file) ++ grub_file_close (initrd_ctx->components[i].file); ++ grub_free (initrd_ctx->components[i].buf); + } + grub_free (initrd_ctx->components); + initrd_ctx->components = 0; +@@ -325,7 +410,12 @@ + } + + cursize = initrd_ctx->components[i].size; +- if (grub_file_read (initrd_ctx->components[i].file, ptr, cursize) ++ if (initrd_ctx->components[i].buf) ++ { ++ grub_memcpy (ptr, initrd_ctx->components[i].buf, cursize); ++ newc = 1; ++ } ++ else if (grub_file_read (initrd_ctx->components[i].file, ptr, cursize) + != cursize) + { + if (!grub_errno) +@@ -346,3 +436,45 @@ + root = 0; + return GRUB_ERR_NONE; + } ++ ++grub_err_t ++grub_initrd_publish_key (const char *uuid, const char *key, grub_size_t key_len, const char *path) ++{ ++ struct grub_key_publisher *cur = NULL; ++ ++ FOR_LIST_ELEMENTS (cur, kpuber) ++ if (grub_uuidcasecmp (cur->name, uuid, sizeof (cur->name)) == 0) ++ break; ++ ++ if (!cur) ++ cur = grub_zalloc (sizeof (*cur)); ++ if (!cur) ++ return grub_errno; ++ ++ if (key && key_len) ++ { ++ grub_free (cur->key); ++ cur->key = grub_malloc (key_len); ++ if (!cur->key) ++ { ++ grub_free (cur); ++ return grub_errno; ++ } ++ grub_memcpy (cur->key, key, key_len); ++ cur->key_len = key_len; ++ } ++ ++ if (path) ++ { ++ grub_free (cur->path); ++ cur->path = grub_strdup (path); ++ } ++ ++ if (!cur->name) ++ { ++ cur->name = grub_strdup (uuid); ++ grub_list_push (GRUB_AS_LIST_P (&kpuber), GRUB_AS_LIST (cur)); ++ } ++ ++ return GRUB_ERR_NONE; ++} +--- a/include/grub/linux.h ++++ b/include/grub/linux.h +@@ -22,3 +22,6 @@ + grub_err_t + grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx, + void *target); ++ ++grub_err_t ++grub_initrd_publish_key (const char *uuid, const char *key, grub_size_t key_len, const char *path); diff --git a/0009-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch b/0009-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch new file mode 100644 index 0000000..0fe0d4e --- /dev/null +++ b/0009-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch @@ -0,0 +1,74 @@ +From def9a985bdb1a12db49be42b748b646abc156411 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Fri, 2 Oct 2020 10:49:26 +1000 +Subject: [PATCH 09/23] crypto: move storage for grub_crypto_pk_* to crypto.c + +The way gcry_rsa and friends (the asymmetric ciphers) are loaded for the +pgp module is a bit quirky. + +include/grub/crypto.h contains: + extern struct gcry_pk_spec *grub_crypto_pk_rsa; + +commands/pgp.c contains the actual storage: + struct gcry_pk_spec *grub_crypto_pk_rsa; + +And the module itself saves to the storage in pgp.c: + GRUB_MOD_INIT(gcry_rsa) + { + grub_crypto_pk_rsa = &_gcry_pubkey_spec_rsa; + } + +This is annoying: gcry_rsa now has a dependency on pgp! + +We want to be able to bring in gcry_rsa without bringing in PGP, +so move the storage to crypto.c. + +Previously, gcry_rsa depended on pgp and mpi. Now it depends on +crypto and mpi. As pgp depends on crypto, this doesn't add any new +module dependencies using the PGP verfier. + +[FWIW, the story is different for the symmetric ciphers. cryptodisk +and friends (zfs encryption etc) use grub_crypto_lookup_cipher_by_name() +to get a cipher handle. That depends on grub_ciphers being populated +by people calling grub_cipher_register. import_gcry.py ensures that the +symmetric ciphers call it.] + +Signed-off-by: Daniel Axtens +--- + grub-core/commands/pgp.c | 4 ---- + grub-core/lib/crypto.c | 4 ++++ + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c +index 2408db499..355a43844 100644 +--- a/grub-core/commands/pgp.c ++++ b/grub-core/commands/pgp.c +@@ -147,10 +147,6 @@ const char *hashes[] = { + [0x0b] = "sha224" + }; + +-struct gcry_pk_spec *grub_crypto_pk_dsa; +-struct gcry_pk_spec *grub_crypto_pk_ecdsa; +-struct gcry_pk_spec *grub_crypto_pk_rsa; +- + static int + dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, + const gcry_md_spec_t *hash, struct grub_public_subkey *sk); +diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c +index ca334d5a4..c578128a5 100644 +--- a/grub-core/lib/crypto.c ++++ b/grub-core/lib/crypto.c +@@ -121,6 +121,10 @@ grub_md_unregister (gcry_md_spec_t *cipher) + } + } + ++struct gcry_pk_spec *grub_crypto_pk_dsa; ++struct gcry_pk_spec *grub_crypto_pk_ecdsa; ++struct gcry_pk_spec *grub_crypto_pk_rsa; ++ + void + grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in, + grub_size_t inlen) +-- +2.31.1 + diff --git a/0009-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch b/0009-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch new file mode 100644 index 0000000..2c1f1ce --- /dev/null +++ b/0009-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch @@ -0,0 +1,43 @@ +From a89b55330ff0930c998cf64ab534cd8ff7e3a74c Mon Sep 17 00:00:00 2001 +From: Javier Martinez Canillas +Date: Tue, 26 May 2020 16:59:28 +0200 +Subject: [PATCH 09/11] x86-efi: Reduce maximum bounce buffer size to 16 MiB + +The EFI linux loader allocates a bounce buffer to copy the initrd since in +some machines doing DMA on addresses above 4GB is not possible during EFI. + +But the verifiers framework also allocates a buffer to copy the initrd in +its grub_file_open() handler. It does this since the data to verify has to +be passed as a single chunk to modules that use the verifiers framework. + +If the initrd image size is big there may not be enough memory in the heap +to allocate two buffers of that size. This causes an allocation failure in +the verifiers framework and leads to the initrd not being read. + +To prevent these allocation failures, let's reduce the maximum size of the +bounce buffer used in the EFI loader. Since the data read can be copied to +the actual initrd address in multilple chunks. + +Resolves: rhbz#1838633 + +Signed-off-by: Javier Martinez Canillas +--- + grub-core/loader/i386/efi/linux.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c +index d49749269..652212227 100644 +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -144,7 +144,7 @@ grub_linuxefi_unload (void) + return GRUB_ERR_NONE; + } + +-#define BOUNCE_BUFFER_MAX 0x10000000ull ++#define BOUNCE_BUFFER_MAX 0x1000000ull + + static grub_ssize_t + read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len) +-- +2.31.1 + diff --git a/0010-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch b/0010-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch new file mode 100644 index 0000000..d859ee5 --- /dev/null +++ b/0010-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch @@ -0,0 +1,52 @@ +From 1fc593b372bfe9bba82f4c59236d5a0cffebd8e2 Mon Sep 17 00:00:00 2001 +From: Colin Watson +Date: Fri, 24 Jul 2020 17:18:09 +0100 +Subject: [PATCH 10/11] efilinux: Fix integer overflows in grub_cmd_initrd + +These could be triggered by an extremely large number of arguments to +the initrd command on 32-bit architectures, or a crafted filesystem with +very large files on any architecture. + +Signed-off-by: Colin Watson +--- + grub-core/loader/i386/efi/linux.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c +index 652212227..6b06a8f2f 100644 +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -28,6 +28,8 @@ + #include + #include + #include ++#include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -206,7 +208,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + goto fail; + } + +- files = grub_zalloc (argc * sizeof (files[0])); ++ files = grub_calloc (argc, sizeof (files[0])); + if (!files) + goto fail; + +@@ -217,7 +219,11 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + if (! files[i]) + goto fail; + nfiles++; +- size += ALIGN_UP (grub_file_size (files[i]), 4); ++ if (grub_add (size, ALIGN_UP (grub_file_size (files[i]), 4), &size)) ++ { ++ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); ++ goto fail; ++ } + } + + initrd_mem = kernel_alloc(size, N_("can't allocate initrd")); +-- +2.31.1 + diff --git a/0010-posix_wrap-tweaks-in-preparation-for-libtasn1.patch b/0010-posix_wrap-tweaks-in-preparation-for-libtasn1.patch new file mode 100644 index 0000000..ca05c29 --- /dev/null +++ b/0010-posix_wrap-tweaks-in-preparation-for-libtasn1.patch @@ -0,0 +1,58 @@ +From fa3436ad10a63d5ad3d27cc330fb2594e699cc34 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Sat, 2 May 2020 00:27:57 +1000 +Subject: [PATCH 10/23] posix_wrap: tweaks in preparation for libtasn1 + + - Define SIZEOF_UNSIGNED_LONG_INT, it's the same as + SIZEOF_UNSIGNED_LONG. + + - Define WORD_BIT, the size in bits of an int. This is a defined + in the Single Unix Specification and in gnulib's limits.h. gnulib + assumes it's 32 bits on all our platforms, including 64 bit + platforms, so we also use that value. + + - Provide strto[u]l[l] preprocessor macros that resolve to + grub_strto[u]l[l]. To avoid gcrypt redefining strtoul, we + also define HAVE_STRTOUL here. + +Signed-off-by: Daniel Axtens +--- + grub-core/lib/posix_wrap/limits.h | 1 + + grub-core/lib/posix_wrap/stdlib.h | 8 ++++++++ + grub-core/lib/posix_wrap/sys/types.h | 1 + + 3 files changed, 10 insertions(+) + +--- a/grub-core/lib/posix_wrap/limits.h ++++ b/grub-core/lib/posix_wrap/limits.h +@@ -41,5 +41,6 @@ + #define LONG_MAX GRUB_LONG_MAX + + #define CHAR_BIT 8 ++#define WORD_BIT 32 + + #endif +--- a/grub-core/lib/posix_wrap/stdlib.h ++++ b/grub-core/lib/posix_wrap/stdlib.h +@@ -64,4 +64,12 @@ + grub_abort (); + } + ++#define strtol grub_strtol ++ ++/* for libgcrypt */ ++#define HAVE_STRTOUL ++#define strtoul grub_strtoul ++ ++#define strtoull grub_strtoull ++ + #endif +--- a/grub-core/lib/posix_wrap/sys/types.h ++++ b/grub-core/lib/posix_wrap/sys/types.h +@@ -50,6 +50,7 @@ + typedef grub_addr_t uintptr_t; + + #define SIZEOF_UNSIGNED_LONG GRUB_CPU_SIZEOF_LONG ++#define SIZEOF_UNSIGNED_LONG_INT GRUB_CPU_SIZEOF_LONG + #define SIZEOF_UNSIGNED_INT 4 + #define SIZEOF_UNSIGNED_LONG_LONG 8 + #define SIZEOF_UNSIGNED_SHORT 2 diff --git a/0010-templates-import-etc-crypttab-to-grub.cfg.patch b/0010-templates-import-etc-crypttab-to-grub.cfg.patch new file mode 100644 index 0000000..308d9ed --- /dev/null +++ b/0010-templates-import-etc-crypttab-to-grub.cfg.patch @@ -0,0 +1,87 @@ +From 2d3130c289b293269dcf558a26674f83f77729a6 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Tue, 14 Jun 2022 17:10:01 +0800 +Subject: [PATCH 10/10] templates: import /etc/crypttab to grub.cfg + +The /etc/crypptab is used to setup location of encryption key files during +boot, among other things. It is useful to make use the information by grub to +determine where keys are being looked up. + +This script can be used to import relevant /etc/crypptab entry to grub.cfg. + +Signed-off-by: Michael Chang +--- + Makefile.util.def | 7 ++++++ + util/grub.d/05_crypttab.in | 50 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 57 insertions(+) + create mode 100644 util/grub.d/05_crypttab.in + +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -483,6 +483,13 @@ + }; + + script = { ++ name = '05_crypttab'; ++ common = util/grub.d/05_crypttab.in; ++ installdir = grubconf; ++ condition = COND_HOST_LINUX; ++}; ++ ++script = { + name = '10_windows'; + common = util/grub.d/10_windows.in; + installdir = grubconf; +--- /dev/null ++++ b/util/grub.d/05_crypttab.in +@@ -0,0 +1,50 @@ ++#! /bin/sh ++set -e ++ ++# grub-mkconfig helper script. ++# Copyright (C) 2022 Free Software Foundation, Inc. ++# ++# GRUB is free software: you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# GRUB is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with GRUB. If not, see . ++ ++prefix="@prefix@" ++exec_prefix="@exec_prefix@" ++datarootdir="@datarootdir@" ++ ++export TEXTDOMAIN=@PACKAGE@ ++export TEXTDOMAINDIR="@localedir@" ++ ++. "$pkgdatadir/grub-mkconfig_lib" ++ ++CRYPTTAB=/etc/crypttab ++ ++if [ -r "$CRYPTTAB" ]; then ++ awk ' ++ /^\s*#/ || $3 ~ /(^\/dev\/|^\/proc\/|^\/sys\/|:)/ { next } ++ { key[0] = $3 } ++ $3 ~ /(^$|none|-)/ { ++ key[0] = "/etc/cryptsetup-keys.d/" $1 ".key" ++ key[1] = "/run/cryptsetup-keys.d/" $1 ".key" ++ } ++ { ++ for (d in key) ++ if (system("test -f " key[d]) == 0) ++ next ++ } ++ /UUID=/ { ++ sub(/UUID=/,"",$2); ++ gsub(/-/,"",$2); ++ printf("crypttab_entry %s %s %s\n",$1,$2,$3) ++ } ++ ' "$CRYPTTAB" ++fi diff --git a/0011-Also-define-GRUB_EFI_MAX_ALLOCATION_ADDRESS-for-RISC.patch b/0011-Also-define-GRUB_EFI_MAX_ALLOCATION_ADDRESS-for-RISC.patch new file mode 100644 index 0000000..b49816d --- /dev/null +++ b/0011-Also-define-GRUB_EFI_MAX_ALLOCATION_ADDRESS-for-RISC.patch @@ -0,0 +1,27 @@ +From 76caed15754338f7261b2a95a3c7cc15a25f6a01 Mon Sep 17 00:00:00 2001 +From: David Abdurachmanov +Date: Thu, 16 Jan 2020 13:10:10 +0100 +Subject: [PATCH 11/11] Also define GRUB_EFI_MAX_ALLOCATION_ADDRESS for RISC-V + +The commit "Try to pick better locations for kernel and initrd" missed to +define this macro for the RISC-V (riscv64) architecture, so add it there. + +Signed-off-by: David Abdurachmanov +--- + include/grub/riscv64/efi/memory.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/grub/riscv64/efi/memory.h b/include/grub/riscv64/efi/memory.h +index c6cb32417..acb61dca4 100644 +--- a/include/grub/riscv64/efi/memory.h ++++ b/include/grub/riscv64/efi/memory.h +@@ -2,5 +2,6 @@ + #include + + #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffffffffULL ++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS + + #endif /* ! GRUB_MEMORY_CPU_HEADER */ +-- +2.31.1 + diff --git a/0011-libtasn1-import-libtasn1-4.18.0.patch b/0011-libtasn1-import-libtasn1-4.18.0.patch new file mode 100644 index 0000000..c0db810 --- /dev/null +++ b/0011-libtasn1-import-libtasn1-4.18.0.patch @@ -0,0 +1,8996 @@ +From c3a6fbeb436470aff90a63bb98444eb897fbe2f6 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Wed, 10 Jun 2020 16:31:22 +1000 +Subject: [PATCH 11/23] libtasn1: import libtasn1-4.18.0 + +Import a very trimmed-down set of libtasn1 files: + +pushd /tmp +wget https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.18.0.tar.gz +tar -xf libtasn1-4.18.0.tar.gz +popd +pushd grub-core/lib +rm -rf libtasn1 +mkdir libtasn1 +cp /tmp/libtasn1-4.18.0/{README.md,COPYING} libtasn1/ +mkdir libtasn1/lib +cp /tmp/libtasn1-4.18.0/lib/{coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h} libtasn1/lib +cp /tmp/libtasn1-4.18.0/lib/includes/libtasn1.h ../../include/grub/ +git add libtasn1/ ../../include/grub/libtasn1.h +popd + +Signed-off-by: Daniel Axtens +--- + grub-core/lib/libtasn1/COPYING | 16 + + grub-core/lib/libtasn1/README.md | 98 + + grub-core/lib/libtasn1/lib/coding.c | 1425 +++++++++++++ + grub-core/lib/libtasn1/lib/decoding.c | 2501 +++++++++++++++++++++++ + grub-core/lib/libtasn1/lib/element.c | 1109 ++++++++++ + grub-core/lib/libtasn1/lib/element.h | 42 + + grub-core/lib/libtasn1/lib/errors.c | 100 + + grub-core/lib/libtasn1/lib/gstr.c | 74 + + grub-core/lib/libtasn1/lib/gstr.h | 50 + + grub-core/lib/libtasn1/lib/int.h | 221 ++ + grub-core/lib/libtasn1/lib/parser_aux.c | 1178 +++++++++++ + grub-core/lib/libtasn1/lib/parser_aux.h | 172 ++ + grub-core/lib/libtasn1/lib/structure.c | 1225 +++++++++++ + grub-core/lib/libtasn1/lib/structure.h | 46 + + include/grub/libtasn1.h | 639 ++++++ + 15 files changed, 8896 insertions(+) + create mode 100644 grub-core/lib/libtasn1/COPYING + create mode 100644 grub-core/lib/libtasn1/README.md + create mode 100644 grub-core/lib/libtasn1/lib/coding.c + create mode 100644 grub-core/lib/libtasn1/lib/decoding.c + create mode 100644 grub-core/lib/libtasn1/lib/element.c + create mode 100644 grub-core/lib/libtasn1/lib/element.h + create mode 100644 grub-core/lib/libtasn1/lib/errors.c + create mode 100644 grub-core/lib/libtasn1/lib/gstr.c + create mode 100644 grub-core/lib/libtasn1/lib/gstr.h + create mode 100644 grub-core/lib/libtasn1/lib/int.h + create mode 100644 grub-core/lib/libtasn1/lib/parser_aux.c + create mode 100644 grub-core/lib/libtasn1/lib/parser_aux.h + create mode 100644 grub-core/lib/libtasn1/lib/structure.c + create mode 100644 grub-core/lib/libtasn1/lib/structure.h + create mode 100644 include/grub/libtasn1.h + +--- /dev/null ++++ b/grub-core/lib/libtasn1/COPYING +@@ -0,0 +1,16 @@ ++LICENSING ++========= ++ ++The libtasn1 library is released under the GNU Lesser General Public ++License (LGPL) version 2.1 or later; see [COPYING.LESSER](doc/COPYING.LESSER) ++for the license terms. ++ ++The GNU LGPL applies to the main libtasn1 library, while the ++included applications library are under the GNU GPL version 3. ++The libtasn1 library is located in the lib directory, while the applications ++in src/. ++ ++The documentation in doc/ is under the GNU FDL license 1.3. ++ ++For any copyright year range specified as YYYY-ZZZZ in this package ++note that the range specifies every single year in that closed interval. +--- /dev/null ++++ b/grub-core/lib/libtasn1/README.md +@@ -0,0 +1,98 @@ ++# Libtasn1 README -- Introduction information ++ ++This is GNU Libtasn1, a small ASN.1 library. ++ ++The C library (libtasn1.*) is licensed under the GNU Lesser General ++Public License version 2.1 or later. See the file COPYING.LIB. ++ ++The command line tool, self tests, examples, and other auxilliary ++files, are licensed under the GNU General Public License version 3.0 ++or later. See the file COPYING. ++ ++## Building the library ++ ++We require several tools to build the software, including: ++ ++* [Make](https://www.gnu.org/software/make/) ++* [Automake](https://www.gnu.org/software/automake/) (use 1.11.3 or later) ++* [Autoconf](https://www.gnu.org/software/autoconf/) ++* [Libtool](https://www.gnu.org/software/libtool/) ++* [Texinfo](https://www.gnu.org/software/texinfo/) ++* [help2man](http://www.gnu.org/software/help2man/) ++* [Tar](https://www.gnu.org/software/tar/) ++* [Gzip](https://www.gnu.org/software/gzip/) ++* [bison](https://www.gnu.org/software/bison/) ++* [Texlive & epsf](https://www.tug.org/texlive/) (for PDF manual) ++* [GTK-DOC](https://www.gtk.org/gtk-doc/) (for API manual) ++* [Git](https://git-scm.com/) ++* [libabigail](https://pagure.io/libabigail/) (for abi comparison in make dist) ++* [Valgrind](https://valgrind.org/) (optional) ++ ++The required software is typically distributed with your operating ++system, and the instructions for installing them differ. Here are ++some hints: ++ ++Debian/Ubuntu: ++``` ++sudo apt-get install make git autoconf automake libtool bison ++sudo apt-get install texinfo help2man gtk-doc-tools valgrind abigail-tools ++``` ++ ++PDF manual - Debian <= stretch: ++``` ++sudo apt-get install texlive-generic-recommended texlive texlive-extra-utils ++``` ++ ++PDF manual - Debian >= buster: ++``` ++sudo apt-get install texlive-plain-generic texlive texlive-extra-utils ++``` ++ ++The next step is to run autoreconf, ./configure, etc: ++ ++``` ++$ ./bootstrap ++``` ++ ++Then build the project normally: ++ ++``` ++$ ./configure ++$ make check ++``` ++ ++Happy hacking! ++ ++ ++## Manual ++ ++The manual is in the `doc/` directory of the release. ++ ++You can also browse the manual online at: ++ ++ - https://www.gnu.org/software/libtasn1/manual/ ++ - https://gnutls.gitlab.io/libtasn1/manual/ ++ - https://gnutls.gitlab.io/libtasn1/manual/libtasn1.html ++ - https://gnutls.gitlab.io/libtasn1/manual/libtasn1.pdf ++ - https://gnutls.gitlab.io/libtasn1/reference/ ++ - https://gnutls.gitlab.io/libtasn1/reference/libtasn1.pdf ++ ++ ++## Code coverage report ++ ++The coverage report is at: ++ ++ - https://gnutls.gitlab.io/libtasn1/coverage ++ ++ ++## Issue trackers ++ ++ - [Main issue tracker](https://gitlab.com/gnutls/libtasn1/issues) ++ - [oss-fuzz found issues](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=libtasn1&can=2) ++ ++ ++## Homepage ++ ++The project homepage at the gnu site is at: ++ ++https://www.gnu.org/software/libtasn1/ +--- /dev/null ++++ b/grub-core/lib/libtasn1/lib/coding.c +@@ -0,0 +1,1425 @@ ++/* ++ * Copyright (C) 2002-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * The LIBTASN1 library is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ */ ++ ++ ++/*****************************************************/ ++/* File: coding.c */ ++/* Description: Functions to create a DER coding of */ ++/* an ASN1 type. */ ++/*****************************************************/ ++ ++#include ++#include "parser_aux.h" ++#include ++#include "element.h" ++#include "minmax.h" ++#include ++ ++#define MAX_TAG_LEN 16 ++ ++/******************************************************/ ++/* Function : _asn1_error_description_value_not_found */ ++/* Description: creates the ErrorDescription string */ ++/* for the ASN1_VALUE_NOT_FOUND error. */ ++/* Parameters: */ ++/* node: node of the tree where the value is NULL. */ ++/* ErrorDescription: string returned. */ ++/* Return: */ ++/******************************************************/ ++static void ++_asn1_error_description_value_not_found (asn1_node node, ++ char *ErrorDescription) ++{ ++ ++ if (ErrorDescription == NULL) ++ return; ++ ++ Estrcpy (ErrorDescription, ":: value of element '"); ++ _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription), ++ ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40); ++ Estrcat (ErrorDescription, "' not found"); ++ ++} ++ ++/** ++ * asn1_length_der: ++ * @len: value to convert. ++ * @der: buffer to hold the returned encoding (may be %NULL). ++ * @der_len: number of meaningful bytes of ANS (der[0]..der[der_len-1]). ++ * ++ * Creates the DER encoding of the provided length value. ++ * The @der buffer must have enough room for the output. The maximum ++ * length this function will encode is %ASN1_MAX_LENGTH_SIZE. ++ * ++ * To know the size of the DER encoding use a %NULL value for @der. ++ **/ ++void ++asn1_length_der (unsigned long int len, unsigned char *der, int *der_len) ++{ ++ int k; ++ unsigned char temp[ASN1_MAX_LENGTH_SIZE]; ++#if SIZEOF_UNSIGNED_LONG_INT > 8 ++ len &= 0xFFFFFFFFFFFFFFFF; ++#endif ++ ++ if (len < 128) ++ { ++ /* short form */ ++ if (der != NULL) ++ der[0] = (unsigned char) len; ++ *der_len = 1; ++ } ++ else ++ { ++ /* Long form */ ++ k = 0; ++ while (len) ++ { ++ temp[k++] = len & 0xFF; ++ len = len >> 8; ++ } ++ *der_len = k + 1; ++ if (der != NULL) ++ { ++ der[0] = ((unsigned char) k & 0x7F) + 128; ++ while (k--) ++ der[*der_len - 1 - k] = temp[k]; ++ } ++ } ++} ++ ++/******************************************************/ ++/* Function : _asn1_tag_der */ ++/* Description: creates the DER coding for the CLASS */ ++/* and TAG parameters. */ ++/* It is limited by the ASN1_MAX_TAG_SIZE variable */ ++/* Parameters: */ ++/* class: value to convert. */ ++/* tag_value: value to convert. */ ++/* ans: string returned. */ ++/* ans_len: number of meaningful bytes of ANS */ ++/* (ans[0]..ans[ans_len-1]). */ ++/* Return: */ ++/******************************************************/ ++static void ++_asn1_tag_der (unsigned char class, unsigned int tag_value, ++ unsigned char ans[ASN1_MAX_TAG_SIZE], int *ans_len) ++{ ++ int k; ++ unsigned char temp[ASN1_MAX_TAG_SIZE]; ++ ++ if (tag_value < 31) ++ { ++ /* short form */ ++ ans[0] = (class & 0xE0) + ((unsigned char) (tag_value & 0x1F)); ++ *ans_len = 1; ++ } ++ else ++ { ++ /* Long form */ ++ ans[0] = (class & 0xE0) + 31; ++ k = 0; ++ while (tag_value != 0) ++ { ++ temp[k++] = tag_value & 0x7F; ++ tag_value >>= 7; ++ ++ if (k > ASN1_MAX_TAG_SIZE - 1) ++ break; /* will not encode larger tags */ ++ } ++ *ans_len = k + 1; ++ while (k--) ++ ans[*ans_len - 1 - k] = temp[k] + 128; ++ ans[*ans_len - 1] -= 128; ++ } ++} ++ ++/** ++ * asn1_octet_der: ++ * @str: the input data. ++ * @str_len: STR length (str[0]..str[*str_len-1]). ++ * @der: encoded string returned. ++ * @der_len: number of meaningful bytes of DER (der[0]..der[der_len-1]). ++ * ++ * Creates a length-value DER encoding for the input data. ++ * The DER encoding of the input data will be placed in the @der variable. ++ * ++ * Note that the OCTET STRING tag is not included in the output. ++ * ++ * This function does not return any value because it is expected ++ * that @der_len will contain enough bytes to store the string ++ * plus the DER encoding. The DER encoding size can be obtained using ++ * asn1_length_der(). ++ **/ ++void ++asn1_octet_der (const unsigned char *str, int str_len, ++ unsigned char *der, int *der_len) ++{ ++ int len_len; ++ ++ if (der == NULL || str_len < 0) ++ return; ++ ++ asn1_length_der (str_len, der, &len_len); ++ memcpy (der + len_len, str, str_len); ++ *der_len = str_len + len_len; ++} ++ ++ ++/** ++ * asn1_encode_simple_der: ++ * @etype: The type of the string to be encoded (ASN1_ETYPE_) ++ * @str: the string data. ++ * @str_len: the string length ++ * @tl: the encoded tag and length ++ * @tl_len: the bytes of the @tl field ++ * ++ * Creates the DER encoding for various simple ASN.1 types like strings etc. ++ * It stores the tag and length in @tl, which should have space for at least ++ * %ASN1_MAX_TL_SIZE bytes. Initially @tl_len should contain the size of @tl. ++ * ++ * The complete DER encoding should consist of the value in @tl appended ++ * with the provided @str. ++ * ++ * Returns: %ASN1_SUCCESS if successful or an error value. ++ **/ ++int ++asn1_encode_simple_der (unsigned int etype, const unsigned char *str, ++ unsigned int str_len, unsigned char *tl, ++ unsigned int *tl_len) ++{ ++ int tag_len, len_len; ++ unsigned tlen; ++ unsigned char der_tag[ASN1_MAX_TAG_SIZE]; ++ unsigned char der_length[ASN1_MAX_LENGTH_SIZE]; ++ unsigned char *p; ++ ++ if (str == NULL) ++ return ASN1_VALUE_NOT_VALID; ++ ++ if (ETYPE_OK (etype) == 0) ++ return ASN1_VALUE_NOT_VALID; ++ ++ /* doesn't handle constructed classes */ ++ if (ETYPE_CLASS (etype) != ASN1_CLASS_UNIVERSAL) ++ return ASN1_VALUE_NOT_VALID; ++ ++ _asn1_tag_der (ETYPE_CLASS (etype), ETYPE_TAG (etype), der_tag, &tag_len); ++ ++ asn1_length_der (str_len, der_length, &len_len); ++ ++ if (tag_len <= 0 || len_len <= 0) ++ return ASN1_VALUE_NOT_VALID; ++ ++ tlen = tag_len + len_len; ++ ++ if (*tl_len < tlen) ++ return ASN1_MEM_ERROR; ++ ++ p = tl; ++ memcpy (p, der_tag, tag_len); ++ p += tag_len; ++ memcpy (p, der_length, len_len); ++ ++ *tl_len = tlen; ++ ++ return ASN1_SUCCESS; ++} ++ ++/******************************************************/ ++/* Function : _asn1_time_der */ ++/* Description: creates the DER coding for a TIME */ ++/* type (length included). */ ++/* Parameters: */ ++/* str: TIME null-terminated string. */ ++/* der: string returned. */ ++/* der_len: number of meaningful bytes of DER */ ++/* (der[0]..der[ans_len-1]). Initially it */ ++/* if must store the lenght of DER. */ ++/* Return: */ ++/* ASN1_MEM_ERROR when DER isn't big enough */ ++/* ASN1_SUCCESS otherwise */ ++/******************************************************/ ++static int ++_asn1_time_der (unsigned char *str, int str_len, unsigned char *der, ++ int *der_len) ++{ ++ int len_len; ++ int max_len; ++ ++ max_len = *der_len; ++ ++ asn1_length_der (str_len, (max_len > 0) ? der : NULL, &len_len); ++ ++ if ((len_len + str_len) <= max_len) ++ memcpy (der + len_len, str, str_len); ++ *der_len = len_len + str_len; ++ ++ if ((*der_len) > max_len) ++ return ASN1_MEM_ERROR; ++ ++ return ASN1_SUCCESS; ++} ++ ++ ++/* ++void ++_asn1_get_utctime_der(unsigned char *der,int *der_len,unsigned char *str) ++{ ++ int len_len,str_len; ++ char temp[20]; ++ ++ if(str==NULL) return; ++ str_len=asn1_get_length_der(der,*der_len,&len_len); ++ if (str_len<0) return; ++ memcpy(temp,der+len_len,str_len); ++ *der_len=str_len+len_len; ++ switch(str_len) ++ { ++ case 11: ++ temp[10]=0; ++ strcat(temp,"00+0000"); ++ break; ++ case 13: ++ temp[12]=0; ++ strcat(temp,"+0000"); ++ break; ++ case 15: ++ temp[15]=0; ++ memmove(temp+12,temp+10,6); ++ temp[10]=temp[11]='0'; ++ break; ++ case 17: ++ temp[17]=0; ++ break; ++ default: ++ return; ++ } ++ strcpy(str,temp); ++} ++*/ ++ ++static void ++encode_val (uint64_t val, unsigned char *der, int max_len, int *der_len) ++{ ++ int first, k; ++ unsigned char bit7; ++ ++ first = 0; ++ for (k = sizeof (val); k >= 0; k--) ++ { ++ bit7 = (val >> (k * 7)) & 0x7F; ++ if (bit7 || first || !k) ++ { ++ if (k) ++ bit7 |= 0x80; ++ if (max_len > (*der_len)) ++ der[*der_len] = bit7; ++ (*der_len)++; ++ first = 1; ++ } ++ } ++} ++ ++/******************************************************/ ++/* Function : _asn1_object_id_der */ ++/* Description: creates the DER coding for an */ ++/* OBJECT IDENTIFIER type (length included). */ ++/* Parameters: */ ++/* str: OBJECT IDENTIFIER null-terminated string. */ ++/* der: string returned. */ ++/* der_len: number of meaningful bytes of DER */ ++/* (der[0]..der[ans_len-1]). Initially it */ ++/* must store the length of DER. */ ++/* Return: */ ++/* ASN1_MEM_ERROR when DER isn't big enough */ ++/* ASN1_SUCCESS if succesful */ ++/* or an error value. */ ++/******************************************************/ ++static int ++_asn1_object_id_der (const char *str, unsigned char *der, int *der_len) ++{ ++ int len_len, counter, max_len; ++ char *temp, *n_end, *n_start; ++ uint64_t val, val1 = 0; ++ int str_len = _asn1_strlen (str); ++ ++ max_len = *der_len; ++ *der_len = 0; ++ ++ if (der == NULL && max_len > 0) ++ return ASN1_VALUE_NOT_VALID; ++ ++ temp = malloc (str_len + 2); ++ if (temp == NULL) ++ return ASN1_MEM_ALLOC_ERROR; ++ ++ memcpy (temp, str, str_len); ++ temp[str_len] = '.'; ++ temp[str_len + 1] = 0; ++ ++ counter = 0; ++ n_start = temp; ++ while ((n_end = strchr (n_start, '.'))) ++ { ++ *n_end = 0; ++ val = _asn1_strtou64 (n_start, NULL, 10); ++ counter++; ++ ++ if (counter == 1) ++ { ++ val1 = val; ++ } ++ else if (counter == 2) ++ { ++ uint64_t val0; ++ ++ if (val1 > 2) ++ { ++ free (temp); ++ return ASN1_VALUE_NOT_VALID; ++ } ++ else if ((val1 == 0 || val1 == 1) && val > 39) ++ { ++ free (temp); ++ return ASN1_VALUE_NOT_VALID; ++ } ++ ++ val0 = 40 * val1 + val; ++ encode_val (val0, der, max_len, der_len); ++ } ++ else ++ { ++ encode_val (val, der, max_len, der_len); ++ } ++ n_start = n_end + 1; ++ } ++ ++ asn1_length_der (*der_len, NULL, &len_len); ++ if (max_len >= (*der_len + len_len)) ++ { ++ memmove (der + len_len, der, *der_len); ++ asn1_length_der (*der_len, der, &len_len); ++ } ++ *der_len += len_len; ++ ++ free (temp); ++ ++ if (max_len < (*der_len)) ++ return ASN1_MEM_ERROR; ++ ++ return ASN1_SUCCESS; ++} ++ ++/** ++ * asn1_object_id_der: ++ * @str: An object identifier in numeric, dot format. ++ * @der: buffer to hold the returned encoding (may be %NULL). ++ * @der_len: initially the size of @der; will hold the final size. ++ * @flags: must be zero ++ * ++ * Creates the DER encoding of the provided object identifier. ++ * ++ * Returns: %ASN1_SUCCESS if DER encoding was OK, %ASN1_VALUE_NOT_VALID ++ * if @str is not a valid OID, %ASN1_MEM_ERROR if the @der ++ * vector isn't big enough and in this case @der_len will contain the ++ * length needed. ++ **/ ++int ++asn1_object_id_der (const char *str, unsigned char *der, int *der_len, ++ unsigned flags) ++{ ++ unsigned char tag_der[MAX_TAG_LEN]; ++ int tag_len = 0, r; ++ int max_len = *der_len; ++ ++ *der_len = 0; ++ ++ _asn1_tag_der (ETYPE_CLASS (ASN1_ETYPE_OBJECT_ID), ++ ETYPE_TAG (ASN1_ETYPE_OBJECT_ID), tag_der, &tag_len); ++ ++ if (max_len > tag_len) ++ { ++ memcpy (der, tag_der, tag_len); ++ } ++ max_len -= tag_len; ++ der += tag_len; ++ ++ r = _asn1_object_id_der (str, der, &max_len); ++ if (r == ASN1_MEM_ERROR || r == ASN1_SUCCESS) ++ { ++ *der_len = max_len + tag_len; ++ } ++ ++ return r; ++} ++ ++static const unsigned char bit_mask[] = ++ { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 }; ++ ++/** ++ * asn1_bit_der: ++ * @str: BIT string. ++ * @bit_len: number of meaningful bits in STR. ++ * @der: string returned. ++ * @der_len: number of meaningful bytes of DER ++ * (der[0]..der[ans_len-1]). ++ * ++ * Creates a length-value DER encoding for the input data ++ * as it would have been for a BIT STRING. ++ * The DER encoded data will be copied in @der. ++ * ++ * Note that the BIT STRING tag is not included in the output. ++ * ++ * This function does not return any value because it is expected ++ * that @der_len will contain enough bytes to store the string ++ * plus the DER encoding. The DER encoding size can be obtained using ++ * asn1_length_der(). ++ **/ ++void ++asn1_bit_der (const unsigned char *str, int bit_len, ++ unsigned char *der, int *der_len) ++{ ++ int len_len, len_byte, len_pad; ++ ++ if (der == NULL) ++ return; ++ ++ len_byte = bit_len >> 3; ++ len_pad = 8 - (bit_len & 7); ++ if (len_pad == 8) ++ len_pad = 0; ++ else ++ len_byte++; ++ asn1_length_der (len_byte + 1, der, &len_len); ++ der[len_len] = len_pad; ++ ++ if (str) ++ memcpy (der + len_len + 1, str, len_byte); ++ der[len_len + len_byte] &= bit_mask[len_pad]; ++ *der_len = len_byte + len_len + 1; ++} ++ ++ ++/******************************************************/ ++/* Function : _asn1_complete_explicit_tag */ ++/* Description: add the length coding to the EXPLICIT */ ++/* tags. */ ++/* Parameters: */ ++/* node: pointer to the tree element. */ ++/* der: string with the DER coding of the whole tree*/ ++/* counter: number of meaningful bytes of DER */ ++/* (der[0]..der[*counter-1]). */ ++/* max_len: size of der vector */ ++/* Return: */ ++/* ASN1_MEM_ERROR if der vector isn't big enough, */ ++/* otherwise ASN1_SUCCESS. */ ++/******************************************************/ ++static int ++_asn1_complete_explicit_tag (asn1_node node, unsigned char *der, ++ int *counter, int *max_len) ++{ ++ asn1_node p; ++ int is_tag_implicit, len2, len3; ++ unsigned char temp[SIZEOF_UNSIGNED_INT]; ++ ++ if (der == NULL && *max_len > 0) ++ return ASN1_VALUE_NOT_VALID; ++ ++ is_tag_implicit = 0; ++ ++ if (node->type & CONST_TAG) ++ { ++ p = node->down; ++ if (p == NULL) ++ return ASN1_DER_ERROR; ++ /* When there are nested tags we must complete them reverse to ++ the order they were created. This is because completing a tag ++ modifies all data within it, including the incomplete tags ++ which store buffer positions -- simon@josefsson.org 2002-09-06 ++ */ ++ while (p->right) ++ p = p->right; ++ while (p && p != node->down->left) ++ { ++ if (type_field (p->type) == ASN1_ETYPE_TAG) ++ { ++ if (p->type & CONST_EXPLICIT) ++ { ++ len2 = strtol (p->name, NULL, 10); ++ _asn1_set_name (p, NULL); ++ ++ asn1_length_der (*counter - len2, temp, &len3); ++ if (len3 <= (*max_len)) ++ { ++ memmove (der + len2 + len3, der + len2, ++ *counter - len2); ++ memcpy (der + len2, temp, len3); ++ } ++ *max_len -= len3; ++ *counter += len3; ++ is_tag_implicit = 0; ++ } ++ else ++ { /* CONST_IMPLICIT */ ++ if (!is_tag_implicit) ++ { ++ is_tag_implicit = 1; ++ } ++ } ++ } ++ p = p->left; ++ } ++ } ++ ++ if (*max_len < 0) ++ return ASN1_MEM_ERROR; ++ ++ return ASN1_SUCCESS; ++} ++ ++const tag_and_class_st _asn1_tags[] = { ++ [ASN1_ETYPE_GENERALSTRING] = ++ {ASN1_TAG_GENERALSTRING, ASN1_CLASS_UNIVERSAL, "type:GENERALSTRING"}, ++ [ASN1_ETYPE_NUMERIC_STRING] = ++ {ASN1_TAG_NUMERIC_STRING, ASN1_CLASS_UNIVERSAL, "type:NUMERIC_STR"}, ++ [ASN1_ETYPE_IA5_STRING] = ++ {ASN1_TAG_IA5_STRING, ASN1_CLASS_UNIVERSAL, "type:IA5_STR"}, ++ [ASN1_ETYPE_TELETEX_STRING] = ++ {ASN1_TAG_TELETEX_STRING, ASN1_CLASS_UNIVERSAL, "type:TELETEX_STR"}, ++ [ASN1_ETYPE_PRINTABLE_STRING] = ++ {ASN1_TAG_PRINTABLE_STRING, ASN1_CLASS_UNIVERSAL, "type:PRINTABLE_STR"}, ++ [ASN1_ETYPE_UNIVERSAL_STRING] = ++ {ASN1_TAG_UNIVERSAL_STRING, ASN1_CLASS_UNIVERSAL, "type:UNIVERSAL_STR"}, ++ [ASN1_ETYPE_BMP_STRING] = ++ {ASN1_TAG_BMP_STRING, ASN1_CLASS_UNIVERSAL, "type:BMP_STR"}, ++ [ASN1_ETYPE_UTF8_STRING] = ++ {ASN1_TAG_UTF8_STRING, ASN1_CLASS_UNIVERSAL, "type:UTF8_STR"}, ++ [ASN1_ETYPE_VISIBLE_STRING] = ++ {ASN1_TAG_VISIBLE_STRING, ASN1_CLASS_UNIVERSAL, "type:VISIBLE_STR"}, ++ [ASN1_ETYPE_OCTET_STRING] = ++ {ASN1_TAG_OCTET_STRING, ASN1_CLASS_UNIVERSAL, "type:OCT_STR"}, ++ [ASN1_ETYPE_BIT_STRING] = ++ {ASN1_TAG_BIT_STRING, ASN1_CLASS_UNIVERSAL, "type:BIT_STR"}, ++ [ASN1_ETYPE_OBJECT_ID] = ++ {ASN1_TAG_OBJECT_ID, ASN1_CLASS_UNIVERSAL, "type:OBJ_ID"}, ++ [ASN1_ETYPE_NULL] = {ASN1_TAG_NULL, ASN1_CLASS_UNIVERSAL, "type:NULL"}, ++ [ASN1_ETYPE_BOOLEAN] = ++ {ASN1_TAG_BOOLEAN, ASN1_CLASS_UNIVERSAL, "type:BOOLEAN"}, ++ [ASN1_ETYPE_INTEGER] = ++ {ASN1_TAG_INTEGER, ASN1_CLASS_UNIVERSAL, "type:INTEGER"}, ++ [ASN1_ETYPE_ENUMERATED] = ++ {ASN1_TAG_ENUMERATED, ASN1_CLASS_UNIVERSAL, "type:ENUMERATED"}, ++ [ASN1_ETYPE_SEQUENCE] = ++ {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, ++ "type:SEQUENCE"}, ++ [ASN1_ETYPE_SEQUENCE_OF] = ++ {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, ++ "type:SEQ_OF"}, ++ [ASN1_ETYPE_SET] = ++ {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, "type:SET"}, ++ [ASN1_ETYPE_SET_OF] = ++ {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, ++ "type:SET_OF"}, ++ [ASN1_ETYPE_GENERALIZED_TIME] = ++ {ASN1_TAG_GENERALIZEDTime, ASN1_CLASS_UNIVERSAL, "type:GENERALIZED_TIME"}, ++ [ASN1_ETYPE_UTC_TIME] = ++ {ASN1_TAG_UTCTime, ASN1_CLASS_UNIVERSAL, "type:UTC_TIME"}, ++}; ++ ++unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]); ++ ++/******************************************************/ ++/* Function : _asn1_insert_tag_der */ ++/* Description: creates the DER coding of tags of one */ ++/* NODE. */ ++/* Parameters: */ ++/* node: pointer to the tree element. */ ++/* der: string returned */ ++/* counter: number of meaningful bytes of DER */ ++/* (counter[0]..der[*counter-1]). */ ++/* max_len: size of der vector */ ++/* Return: */ ++/* ASN1_GENERIC_ERROR if the type is unknown, */ ++/* ASN1_MEM_ERROR if der vector isn't big enough, */ ++/* otherwise ASN1_SUCCESS. */ ++/******************************************************/ ++static int ++_asn1_insert_tag_der (asn1_node node, unsigned char *der, int *counter, ++ int *max_len) ++{ ++ asn1_node p; ++ int tag_len, is_tag_implicit; ++ unsigned char class, class_implicit = ++ 0, temp[MAX (SIZEOF_UNSIGNED_INT * 3 + 1, LTOSTR_MAX_SIZE)]; ++ unsigned long tag_implicit = 0; ++ unsigned char tag_der[MAX_TAG_LEN]; ++ ++ is_tag_implicit = 0; ++ ++ if (node->type & CONST_TAG) ++ { ++ p = node->down; ++ while (p) ++ { ++ if (type_field (p->type) == ASN1_ETYPE_TAG) ++ { ++ if (p->type & CONST_APPLICATION) ++ class = ASN1_CLASS_APPLICATION; ++ else if (p->type & CONST_UNIVERSAL) ++ class = ASN1_CLASS_UNIVERSAL; ++ else if (p->type & CONST_PRIVATE) ++ class = ASN1_CLASS_PRIVATE; ++ else ++ class = ASN1_CLASS_CONTEXT_SPECIFIC; ++ ++ if (p->type & CONST_EXPLICIT) ++ { ++ if (is_tag_implicit) ++ _asn1_tag_der (class_implicit, tag_implicit, tag_der, ++ &tag_len); ++ else ++ _asn1_tag_der (class | ASN1_CLASS_STRUCTURED, ++ _asn1_strtoul (p->value, NULL, 10), ++ tag_der, &tag_len); ++ ++ *max_len -= tag_len; ++ if (der && *max_len >= 0) ++ memcpy (der + *counter, tag_der, tag_len); ++ *counter += tag_len; ++ ++ _asn1_ltostr (*counter, (char *) temp); ++ _asn1_set_name (p, (const char *) temp); ++ ++ is_tag_implicit = 0; ++ } ++ else ++ { /* CONST_IMPLICIT */ ++ if (!is_tag_implicit) ++ { ++ if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) || ++ (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF) ++ || (type_field (node->type) == ASN1_ETYPE_SET) ++ || (type_field (node->type) == ASN1_ETYPE_SET_OF)) ++ class |= ASN1_CLASS_STRUCTURED; ++ class_implicit = class; ++ tag_implicit = _asn1_strtoul (p->value, NULL, 10); ++ is_tag_implicit = 1; ++ } ++ } ++ } ++ p = p->right; ++ } ++ } ++ ++ if (is_tag_implicit) ++ { ++ _asn1_tag_der (class_implicit, tag_implicit, tag_der, &tag_len); ++ } ++ else ++ { ++ unsigned type = type_field (node->type); ++ switch (type) ++ { ++ CASE_HANDLED_ETYPES: ++ _asn1_tag_der (_asn1_tags[type].class, _asn1_tags[type].tag, ++ tag_der, &tag_len); ++ break; ++ case ASN1_ETYPE_TAG: ++ case ASN1_ETYPE_CHOICE: ++ case ASN1_ETYPE_ANY: ++ tag_len = 0; ++ break; ++ default: ++ return ASN1_GENERIC_ERROR; ++ } ++ } ++ ++ *max_len -= tag_len; ++ if (der && *max_len >= 0) ++ memcpy (der + *counter, tag_der, tag_len); ++ *counter += tag_len; ++ ++ if (*max_len < 0) ++ return ASN1_MEM_ERROR; ++ ++ return ASN1_SUCCESS; ++} ++ ++/******************************************************/ ++/* Function : _asn1_ordering_set */ ++/* Description: puts the elements of a SET type in */ ++/* the correct order according to DER rules. */ ++/* Parameters: */ ++/* der: string with the DER coding. */ ++/* node: pointer to the SET element. */ ++/* Return: */ ++/* ASN1_SUCCESS if successful */ ++/* or an error value. */ ++/******************************************************/ ++static int ++_asn1_ordering_set (unsigned char *der, int der_len, asn1_node node) ++{ ++ struct vet ++ { ++ int end; ++ unsigned long value; ++ struct vet *next, *prev; ++ }; ++ ++ int counter, len, len2; ++ struct vet *first, *last, *p_vet, *p2_vet; ++ asn1_node p; ++ unsigned char class, *temp; ++ unsigned long tag, t; ++ int err; ++ ++ counter = 0; ++ ++ if (type_field (node->type) != ASN1_ETYPE_SET) ++ return ASN1_VALUE_NOT_VALID; ++ ++ p = node->down; ++ while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) || ++ (type_field (p->type) == ASN1_ETYPE_SIZE))) ++ p = p->right; ++ ++ if ((p == NULL) || (p->right == NULL)) ++ return ASN1_SUCCESS; ++ ++ first = last = NULL; ++ while (p) ++ { ++ p_vet = malloc (sizeof (struct vet)); ++ if (p_vet == NULL) ++ { ++ err = ASN1_MEM_ALLOC_ERROR; ++ goto error; ++ } ++ ++ p_vet->next = NULL; ++ p_vet->prev = last; ++ if (first == NULL) ++ first = p_vet; ++ else ++ last->next = p_vet; ++ last = p_vet; ++ ++ /* tag value calculation */ ++ err = asn1_get_tag_der (der + counter, der_len - counter, &class, &len2, ++ &tag); ++ if (err != ASN1_SUCCESS) ++ goto error; ++ ++ t = ((unsigned int) class) << 24; ++ p_vet->value = t | tag; ++ counter += len2; ++ ++ /* extraction and length */ ++ len2 = asn1_get_length_der (der + counter, der_len - counter, &len); ++ if (len2 < 0) ++ { ++ err = ASN1_DER_ERROR; ++ goto error; ++ } ++ counter += len + len2; ++ ++ p_vet->end = counter; ++ p = p->right; ++ } ++ ++ p_vet = first; ++ ++ while (p_vet) ++ { ++ p2_vet = p_vet->next; ++ counter = 0; ++ while (p2_vet) ++ { ++ if (p_vet->value > p2_vet->value) ++ { ++ /* change position */ ++ temp = malloc (p_vet->end - counter); ++ if (temp == NULL) ++ { ++ err = ASN1_MEM_ALLOC_ERROR; ++ goto error; ++ } ++ ++ memcpy (temp, der + counter, p_vet->end - counter); ++ memcpy (der + counter, der + p_vet->end, ++ p2_vet->end - p_vet->end); ++ memcpy (der + counter + p2_vet->end - p_vet->end, temp, ++ p_vet->end - counter); ++ free (temp); ++ ++ tag = p_vet->value; ++ p_vet->value = p2_vet->value; ++ p2_vet->value = tag; ++ ++ p_vet->end = counter + (p2_vet->end - p_vet->end); ++ } ++ counter = p_vet->end; ++ ++ p2_vet = p2_vet->next; ++ p_vet = p_vet->next; ++ } ++ ++ if (p_vet != first) ++ p_vet->prev->next = NULL; ++ else ++ first = NULL; ++ free (p_vet); ++ p_vet = first; ++ } ++ return ASN1_SUCCESS; ++ ++error: ++ while (first != NULL) ++ { ++ p_vet = first; ++ first = first->next; ++ free (p_vet); ++ } ++ return err; ++} ++ ++struct vet ++{ ++ unsigned char *ptr; ++ int size; ++}; ++ ++static int ++setof_compar (const void *_e1, const void *_e2) ++{ ++ unsigned length; ++ const struct vet *e1 = _e1, *e2 = _e2; ++ int rval; ++ ++ /* The encodings of the component values of a set-of value shall ++ * appear in ascending order, the encodings being compared ++ * as octet strings with the shorter components being ++ * padded at their trailing end with 0-octets. ++ * The padding octets are for comparison purposes and ++ * do not appear in the encodings. ++ */ ++ length = MIN (e1->size, e2->size); ++ ++ rval = memcmp (e1->ptr, e2->ptr, length); ++ if (rval == 0 && e1->size != e2->size) ++ { ++ if (e1->size > e2->size) ++ rval = 1; ++ else if (e2->size > e1->size) ++ rval = -1; ++ } ++ ++ return rval; ++} ++ ++/******************************************************/ ++/* Function : _asn1_ordering_set_of */ ++/* Description: puts the elements of a SET OF type in */ ++/* the correct order according to DER rules. */ ++/* Parameters: */ ++/* der: string with the DER coding. */ ++/* node: pointer to the SET OF element. */ ++/* Return: */ ++/* ASN1_SUCCESS if successful */ ++/* or an error value. */ ++/******************************************************/ ++static int ++_asn1_ordering_set_of (unsigned char *der, int der_len, asn1_node node) ++{ ++ int counter, len, len2; ++ struct vet *list = NULL, *tlist; ++ unsigned list_size = 0; ++ struct vet *p_vet; ++ asn1_node p; ++ unsigned char class; ++ unsigned i; ++ unsigned char *out = NULL; ++ int err; ++ ++ counter = 0; ++ ++ if (type_field (node->type) != ASN1_ETYPE_SET_OF) ++ return ASN1_VALUE_NOT_VALID; ++ ++ p = node->down; ++ while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) || ++ (type_field (p->type) == ASN1_ETYPE_SIZE))) ++ p = p->right; ++ if (p == NULL) ++ return ASN1_VALUE_NOT_VALID; ++ p = p->right; ++ ++ if ((p == NULL) || (p->right == NULL)) ++ return ASN1_SUCCESS; ++ ++ while (p) ++ { ++ list_size++; ++ tlist = realloc (list, list_size * sizeof (struct vet)); ++ if (tlist == NULL) ++ { ++ err = ASN1_MEM_ALLOC_ERROR; ++ goto error; ++ } ++ list = tlist; ++ p_vet = &list[list_size - 1]; ++ ++ p_vet->ptr = der + counter; ++ p_vet->size = 0; ++ ++ /* extraction of tag and length */ ++ if (der_len - counter > 0) ++ { ++ err = asn1_get_tag_der (der + counter, der_len - counter, &class, ++ &len, NULL); ++ if (err != ASN1_SUCCESS) ++ goto error; ++ counter += len; ++ p_vet->size += len; ++ ++ len2 = asn1_get_length_der (der + counter, der_len - counter, &len); ++ if (len2 < 0) ++ { ++ err = ASN1_DER_ERROR; ++ goto error; ++ } ++ counter += len + len2; ++ p_vet->size += len + len2; ++ ++ } ++ else ++ { ++ err = ASN1_DER_ERROR; ++ goto error; ++ } ++ p = p->right; ++ } ++ ++ if (counter > der_len) ++ { ++ err = ASN1_DER_ERROR; ++ goto error; ++ } ++ ++ qsort (list, list_size, sizeof (struct vet), setof_compar); ++ ++ out = malloc (der_len); ++ if (out == NULL) ++ { ++ err = ASN1_MEM_ERROR; ++ goto error; ++ } ++ ++ /* the sum of p_vet->size == der_len */ ++ counter = 0; ++ for (i = 0; i < list_size; i++) ++ { ++ p_vet = &list[i]; ++ memcpy (out + counter, p_vet->ptr, p_vet->size); ++ counter += p_vet->size; ++ } ++ memcpy (der, out, der_len); ++ free (out); ++ ++ err = ASN1_SUCCESS; ++ ++error: ++ free (list); ++ return err; ++} ++ ++/** ++ * asn1_der_coding: ++ * @element: pointer to an ASN1 element ++ * @name: the name of the structure you want to encode (it must be ++ * inside *POINTER). ++ * @ider: vector that will contain the DER encoding. DER must be a ++ * pointer to memory cells already allocated. ++ * @len: number of bytes of *@ider: @ider[0]..@ider[len-1], Initialy ++ * holds the sizeof of der vector. ++ * @ErrorDescription: return the error description or an empty ++ * string if success. ++ * ++ * Creates the DER encoding for the NAME structure (inside *POINTER ++ * structure). ++ * ++ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND ++ * if @name is not a valid element, %ASN1_VALUE_NOT_FOUND if there ++ * is an element without a value, %ASN1_MEM_ERROR if the @ider ++ * vector isn't big enough and in this case @len will contain the ++ * length needed. ++ **/ ++int ++asn1_der_coding (asn1_node_const element, const char *name, void *ider, ++ int *len, char *ErrorDescription) ++{ ++ asn1_node node, p, p2; ++ unsigned char temp[MAX (LTOSTR_MAX_SIZE, SIZEOF_UNSIGNED_LONG_INT * 3 + 1)]; ++ int counter, counter_old, len2, len3, move, max_len, max_len_old; ++ int err; ++ unsigned char *der = ider; ++ unsigned char dummy; ++ ++ if (ErrorDescription) ++ ErrorDescription[0] = 0; ++ ++ node = asn1_find_node (element, name); ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ /* Node is now a locally allocated variable. ++ * That is because in some point we modify the ++ * structure, and I don't know why! --nmav ++ */ ++ node = _asn1_copy_structure3 (node); ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ max_len = *len; ++ ++ if (der == NULL && max_len > 0) ++ { ++ err = ASN1_VALUE_NOT_VALID; ++ goto error; ++ } ++ ++ counter = 0; ++ move = DOWN; ++ p = node; ++ ++ while (1) ++ { ++ ++ counter_old = counter; ++ max_len_old = max_len; ++ if (move != UP) ++ { ++ p->start = counter; ++ err = _asn1_insert_tag_der (p, der, &counter, &max_len); ++ if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) ++ goto error; ++ } ++ switch (type_field (p->type)) ++ { ++ case ASN1_ETYPE_NULL: ++ max_len--; ++ if (der != NULL && max_len >= 0) ++ der[counter] = 0; ++ counter++; ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_BOOLEAN: ++ if ((p->type & CONST_DEFAULT) && (p->value == NULL)) ++ { ++ counter = counter_old; ++ max_len = max_len_old; ++ } ++ else ++ { ++ if (p->value == NULL) ++ { ++ _asn1_error_description_value_not_found (p, ++ ErrorDescription); ++ err = ASN1_VALUE_NOT_FOUND; ++ goto error; ++ } ++ max_len -= 2; ++ if (der != NULL && max_len >= 0) ++ { ++ der[counter++] = 1; ++ if (p->value[0] == 'F') ++ der[counter++] = 0; ++ else ++ der[counter++] = 0xFF; ++ } ++ else ++ counter += 2; ++ } ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_INTEGER: ++ case ASN1_ETYPE_ENUMERATED: ++ if ((p->type & CONST_DEFAULT) && (p->value == NULL)) ++ { ++ counter = counter_old; ++ max_len = max_len_old; ++ } ++ else ++ { ++ if (p->value == NULL) ++ { ++ _asn1_error_description_value_not_found (p, ++ ErrorDescription); ++ err = ASN1_VALUE_NOT_FOUND; ++ goto error; ++ } ++ len2 = asn1_get_length_der (p->value, p->value_len, &len3); ++ if (len2 < 0) ++ { ++ err = ASN1_DER_ERROR; ++ goto error; ++ } ++ max_len -= len2 + len3; ++ if (der != NULL && max_len >= 0) ++ memcpy (der + counter, p->value, len3 + len2); ++ counter += len3 + len2; ++ } ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_OBJECT_ID: ++ if ((p->type & CONST_DEFAULT) && (p->value == NULL)) ++ { ++ counter = counter_old; ++ max_len = max_len_old; ++ } ++ else ++ { ++ if (p->value == NULL) ++ { ++ _asn1_error_description_value_not_found (p, ++ ErrorDescription); ++ err = ASN1_VALUE_NOT_FOUND; ++ goto error; ++ } ++ len2 = max_len; ++ err = ++ _asn1_object_id_der ((char *) p->value, ++ der ? der + counter : &dummy, &len2); ++ if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) ++ goto error; ++ ++ max_len -= len2; ++ counter += len2; ++ } ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_GENERALIZED_TIME: ++ case ASN1_ETYPE_UTC_TIME: ++ if (p->value == NULL) ++ { ++ _asn1_error_description_value_not_found (p, ErrorDescription); ++ err = ASN1_VALUE_NOT_FOUND; ++ goto error; ++ } ++ len2 = max_len; ++ err = ++ _asn1_time_der (p->value, p->value_len, ++ der ? der + counter : &dummy, &len2); ++ if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) ++ goto error; ++ ++ max_len -= len2; ++ counter += len2; ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_OCTET_STRING: ++ case ASN1_ETYPE_GENERALSTRING: ++ case ASN1_ETYPE_NUMERIC_STRING: ++ case ASN1_ETYPE_IA5_STRING: ++ case ASN1_ETYPE_TELETEX_STRING: ++ case ASN1_ETYPE_PRINTABLE_STRING: ++ case ASN1_ETYPE_UNIVERSAL_STRING: ++ case ASN1_ETYPE_BMP_STRING: ++ case ASN1_ETYPE_UTF8_STRING: ++ case ASN1_ETYPE_VISIBLE_STRING: ++ case ASN1_ETYPE_BIT_STRING: ++ if (p->value == NULL) ++ { ++ _asn1_error_description_value_not_found (p, ErrorDescription); ++ err = ASN1_VALUE_NOT_FOUND; ++ goto error; ++ } ++ len2 = asn1_get_length_der (p->value, p->value_len, &len3); ++ if (len2 < 0) ++ { ++ err = ASN1_DER_ERROR; ++ goto error; ++ } ++ max_len -= len2 + len3; ++ if (der != NULL && max_len >= 0) ++ memcpy (der + counter, p->value, len3 + len2); ++ counter += len3 + len2; ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_SEQUENCE: ++ case ASN1_ETYPE_SET: ++ if (move != UP) ++ { ++ p->tmp_ival = counter; ++ if (p->down == NULL) ++ { ++ move = UP; ++ continue; ++ } ++ else ++ { ++ p2 = p->down; ++ while (p2 && (type_field (p2->type) == ASN1_ETYPE_TAG)) ++ p2 = p2->right; ++ if (p2) ++ { ++ p = p2; ++ move = RIGHT; ++ continue; ++ } ++ move = UP; ++ continue; ++ } ++ } ++ else ++ { /* move==UP */ ++ len2 = p->tmp_ival; ++ p->tmp_ival = 0; ++ if ((type_field (p->type) == ASN1_ETYPE_SET) && (max_len >= 0)) ++ { ++ err = ++ _asn1_ordering_set (der ? der + len2 : &dummy, ++ counter - len2, p); ++ if (err != ASN1_SUCCESS) ++ goto error; ++ } ++ asn1_length_der (counter - len2, temp, &len3); ++ max_len -= len3; ++ if (der != NULL && max_len >= 0) ++ { ++ memmove (der + len2 + len3, der + len2, counter - len2); ++ memcpy (der + len2, temp, len3); ++ } ++ counter += len3; ++ move = RIGHT; ++ } ++ break; ++ case ASN1_ETYPE_SEQUENCE_OF: ++ case ASN1_ETYPE_SET_OF: ++ if (move != UP) ++ { ++ p->tmp_ival = counter; ++ p = p->down; ++ while ((type_field (p->type) == ASN1_ETYPE_TAG) ++ || (type_field (p->type) == ASN1_ETYPE_SIZE)) ++ p = p->right; ++ if (p->right) ++ { ++ p = p->right; ++ move = RIGHT; ++ continue; ++ } ++ else ++ p = _asn1_find_up (p); ++ move = UP; ++ } ++ if (move == UP) ++ { ++ len2 = p->tmp_ival; ++ p->tmp_ival = 0; ++ if ((type_field (p->type) == ASN1_ETYPE_SET_OF) ++ && (counter - len2 > 0) && (max_len >= 0)) ++ { ++ err = ++ _asn1_ordering_set_of (der ? der + len2 : &dummy, ++ counter - len2, p); ++ if (err != ASN1_SUCCESS) ++ goto error; ++ } ++ asn1_length_der (counter - len2, temp, &len3); ++ max_len -= len3; ++ if (der != NULL && max_len >= 0) ++ { ++ memmove (der + len2 + len3, der + len2, counter - len2); ++ memcpy (der + len2, temp, len3); ++ } ++ counter += len3; ++ move = RIGHT; ++ } ++ break; ++ case ASN1_ETYPE_ANY: ++ if (p->value == NULL) ++ { ++ _asn1_error_description_value_not_found (p, ErrorDescription); ++ err = ASN1_VALUE_NOT_FOUND; ++ goto error; ++ } ++ len2 = asn1_get_length_der (p->value, p->value_len, &len3); ++ if (len2 < 0) ++ { ++ err = ASN1_DER_ERROR; ++ goto error; ++ } ++ max_len -= len2; ++ if (der != NULL && max_len >= 0) ++ memcpy (der + counter, p->value + len3, len2); ++ counter += len2; ++ move = RIGHT; ++ break; ++ default: ++ move = (move == UP) ? RIGHT : DOWN; ++ break; ++ } ++ ++ if ((move != DOWN) && (counter != counter_old)) ++ { ++ p->end = counter - 1; ++ err = _asn1_complete_explicit_tag (p, der, &counter, &max_len); ++ if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) ++ goto error; ++ } ++ ++ if (p == node && move != DOWN) ++ break; ++ ++ if (move == DOWN) ++ { ++ if (p->down) ++ p = p->down; ++ else ++ move = RIGHT; ++ } ++ if (move == RIGHT) ++ { ++ if (p->right) ++ p = p->right; ++ else ++ move = UP; ++ } ++ if (move == UP) ++ p = _asn1_find_up (p); ++ } ++ ++ *len = counter; ++ ++ if (max_len < 0) ++ { ++ err = ASN1_MEM_ERROR; ++ goto error; ++ } ++ ++ err = ASN1_SUCCESS; ++ ++error: ++ asn1_delete_structure (&node); ++ return err; ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1/lib/decoding.c +@@ -0,0 +1,2501 @@ ++/* ++ * Copyright (C) 2002-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * The LIBTASN1 library is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ */ ++ ++ ++/*****************************************************/ ++/* File: decoding.c */ ++/* Description: Functions to manage DER decoding */ ++/*****************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "c-ctype.h" ++ ++#ifdef DEBUG ++# define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__) ++#else ++# define warn() ++#endif ++ ++#define IS_ERR(len, flags) (len < -1 || ((flags & ASN1_DECODE_FLAG_STRICT_DER) && len < 0)) ++ ++#define HAVE_TWO(x) (x>=2?1:0) ++ ++/* Decoding flags (dflags) used in several decoding functions. ++ * DECODE_FLAG_HAVE_TAG: The provided buffer includes a tag ++ * DECODE_FLAG_CONSTRUCTED: The provided buffer is of indefinite encoding (useful ++ * when no tags are present). ++ * DECODE_FLAG_LEVEL1: Internal flag to indicate a level of recursion for BER strings. ++ * DECODE_FLAG_LEVEL2: Internal flag to indicate two levels of recursion for BER strings. ++ * DECODE_FLAG_LEVEL3: Internal flag to indicate three levels of recursion for BER strings. ++ * This is the maximum levels of recursion possible to prevent stack ++ * exhaustion. ++ */ ++ ++#define DECODE_FLAG_HAVE_TAG 1 ++#define DECODE_FLAG_CONSTRUCTED (1<<1) ++#define DECODE_FLAG_LEVEL1 (1<<2) ++#define DECODE_FLAG_LEVEL2 (1<<3) ++#define DECODE_FLAG_LEVEL3 (1<<4) ++ ++#define DECR_LEN(l, s) do { \ ++ l -= s; \ ++ if (l < 0) { \ ++ warn(); \ ++ result = ASN1_DER_ERROR; \ ++ goto cleanup; \ ++ } \ ++ } while (0) ++ ++static int ++_asn1_get_indefinite_length_string (const unsigned char *der, int der_len, ++ int *len); ++ ++static int ++_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, ++ unsigned int _der_len, unsigned char **str, ++ unsigned int *str_len, unsigned int *ber_len, ++ unsigned dflags); ++ ++static int ++_asn1_decode_simple_der (unsigned int etype, const unsigned char *der, ++ unsigned int _der_len, const unsigned char **str, ++ unsigned int *str_len, unsigned dflags); ++ ++static void ++_asn1_error_description_tag_error (asn1_node node, char *ErrorDescription) ++{ ++ ++ Estrcpy (ErrorDescription, ":: tag error near element '"); ++ _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription), ++ ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40); ++ Estrcat (ErrorDescription, "'"); ++ ++} ++ ++/** ++ * asn1_get_length_der: ++ * @der: DER data to decode. ++ * @der_len: Length of DER data to decode. ++ * @len: Output variable containing the length of the DER length field. ++ * ++ * Extract a length field from DER data. ++ * ++ * Returns: Return the decoded length value, or -1 on indefinite ++ * length, or -2 when the value was too big to fit in a int, or -4 ++ * when the decoded length value plus @len would exceed @der_len. ++ **/ ++long ++asn1_get_length_der (const unsigned char *der, int der_len, int *len) ++{ ++ unsigned int ans; ++ int k, punt, sum; ++ ++ *len = 0; ++ if (der_len <= 0) ++ return 0; ++ ++ if (!(der[0] & 128)) ++ { ++ /* short form */ ++ *len = 1; ++ ans = der[0]; ++ } ++ else ++ { ++ /* Long form */ ++ k = der[0] & 0x7F; ++ punt = 1; ++ if (k) ++ { /* definite length method */ ++ ans = 0; ++ while (punt <= k && punt < der_len) ++ { ++ if (INT_MULTIPLY_OVERFLOW (ans, 256)) ++ return -2; ++ ans *= 256; ++ ++ if (INT_ADD_OVERFLOW (ans, ((unsigned) der[punt]))) ++ return -2; ++ ans += der[punt]; ++ punt++; ++ } ++ } ++ else ++ { /* indefinite length method */ ++ *len = punt; ++ return -1; ++ } ++ ++ *len = punt; ++ } ++ ++ sum = ans; ++ if (ans >= INT_MAX || INT_ADD_OVERFLOW (sum, (*len))) ++ return -2; ++ sum += *len; ++ ++ if (sum > der_len) ++ return -4; ++ ++ return ans; ++} ++ ++/** ++ * asn1_get_tag_der: ++ * @der: DER data to decode. ++ * @der_len: Length of DER data to decode. ++ * @cls: Output variable containing decoded class. ++ * @len: Output variable containing the length of the DER TAG data. ++ * @tag: Output variable containing the decoded tag (may be %NULL). ++ * ++ * Decode the class and TAG from DER code. ++ * ++ * Returns: Returns %ASN1_SUCCESS on success, or an error. ++ **/ ++int ++asn1_get_tag_der (const unsigned char *der, int der_len, ++ unsigned char *cls, int *len, unsigned long *tag) ++{ ++ unsigned int ris; ++ int punt; ++ ++ if (der == NULL || der_len < 2 || len == NULL) ++ return ASN1_DER_ERROR; ++ ++ *cls = der[0] & 0xE0; ++ if ((der[0] & 0x1F) != 0x1F) ++ { ++ /* short form */ ++ *len = 1; ++ ris = der[0] & 0x1F; ++ } ++ else ++ { ++ /* Long form */ ++ punt = 1; ++ ris = 0; ++ while (punt < der_len && der[punt] & 128) ++ { ++ ++ if (INT_MULTIPLY_OVERFLOW (ris, 128)) ++ return ASN1_DER_ERROR; ++ ris *= 128; ++ ++ if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F)))) ++ return ASN1_DER_ERROR; ++ ris += (der[punt] & 0x7F); ++ punt++; ++ } ++ ++ if (punt >= der_len) ++ return ASN1_DER_ERROR; ++ ++ if (INT_MULTIPLY_OVERFLOW (ris, 128)) ++ return ASN1_DER_ERROR; ++ ris *= 128; ++ ++ if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F)))) ++ return ASN1_DER_ERROR; ++ ris += (der[punt] & 0x7F); ++ punt++; ++ ++ *len = punt; ++ } ++ ++ if (tag) ++ *tag = ris; ++ return ASN1_SUCCESS; ++} ++ ++/** ++ * asn1_get_length_ber: ++ * @ber: BER data to decode. ++ * @ber_len: Length of BER data to decode. ++ * @len: Output variable containing the length of the BER length field. ++ * ++ * Extract a length field from BER data. The difference to ++ * asn1_get_length_der() is that this function will return a length ++ * even if the value has indefinite encoding. ++ * ++ * Returns: Return the decoded length value, or negative value when ++ * the value was too big. ++ * ++ * Since: 2.0 ++ **/ ++long ++asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len) ++{ ++ int ret; ++ long err; ++ ++ ret = asn1_get_length_der (ber, ber_len, len); ++ ++ if (ret == -1 && ber_len > 1) ++ { /* indefinite length method */ ++ err = _asn1_get_indefinite_length_string (ber + 1, ber_len - 1, &ret); ++ if (err != ASN1_SUCCESS) ++ return -3; ++ } ++ ++ return ret; ++} ++ ++/** ++ * asn1_get_octet_der: ++ * @der: DER data to decode containing the OCTET SEQUENCE. ++ * @der_len: The length of the @der data to decode. ++ * @ret_len: Output variable containing the encoded length of the DER data. ++ * @str: Pre-allocated output buffer to put decoded OCTET SEQUENCE in. ++ * @str_size: Length of pre-allocated output buffer. ++ * @str_len: Output variable containing the length of the contents of the OCTET SEQUENCE. ++ * ++ * Extract an OCTET SEQUENCE from DER data. Note that this function ++ * expects the DER data past the tag field, i.e., the length and ++ * content octets. ++ * ++ * Returns: Returns %ASN1_SUCCESS on success, or an error. ++ **/ ++int ++asn1_get_octet_der (const unsigned char *der, int der_len, ++ int *ret_len, unsigned char *str, int str_size, ++ int *str_len) ++{ ++ int len_len = 0; ++ ++ if (der_len <= 0) ++ return ASN1_GENERIC_ERROR; ++ ++ *str_len = asn1_get_length_der (der, der_len, &len_len); ++ ++ if (*str_len < 0) ++ return ASN1_DER_ERROR; ++ ++ *ret_len = *str_len + len_len; ++ if (str_size >= *str_len) ++ { ++ if (*str_len > 0 && str != NULL) ++ memcpy (str, der + len_len, *str_len); ++ } ++ else ++ { ++ return ASN1_MEM_ERROR; ++ } ++ ++ return ASN1_SUCCESS; ++} ++ ++ ++/*- ++ * _asn1_get_time_der: ++ * @type: %ASN1_ETYPE_GENERALIZED_TIME or %ASN1_ETYPE_UTC_TIME ++ * @der: DER data to decode containing the time ++ * @der_len: Length of DER data to decode. ++ * @ret_len: Output variable containing the length of the DER data. ++ * @str: Pre-allocated output buffer to put the textual time in. ++ * @str_size: Length of pre-allocated output buffer. ++ * @flags: Zero or %ASN1_DECODE_FLAG_STRICT_DER ++ * ++ * Performs basic checks in the DER encoded time object and returns its textual form. ++ * The textual form will be in the YYYYMMDD000000Z format for GeneralizedTime ++ * and YYMMDD000000Z for UTCTime. ++ * ++ * Returns: %ASN1_SUCCESS on success, or an error. ++ -*/ ++static int ++_asn1_get_time_der (unsigned type, const unsigned char *der, int der_len, ++ int *ret_len, char *str, int str_size, unsigned flags) ++{ ++ int len_len, str_len; ++ unsigned i; ++ unsigned sign_count = 0; ++ unsigned dot_count = 0; ++ const unsigned char *p; ++ ++ if (der_len <= 0 || str == NULL) ++ return ASN1_DER_ERROR; ++ ++ str_len = asn1_get_length_der (der, der_len, &len_len); ++ if (str_len <= 0 || str_size < str_len) ++ return ASN1_DER_ERROR; ++ ++ /* perform some sanity checks on the data */ ++ if (str_len < 8) ++ { ++ warn (); ++ return ASN1_TIME_ENCODING_ERROR; ++ } ++ ++ if ((flags & ASN1_DECODE_FLAG_STRICT_DER) ++ && !(flags & ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME)) ++ { ++ p = &der[len_len]; ++ for (i = 0; i < (unsigned) (str_len - 1); i++) ++ { ++ if (c_isdigit (p[i]) == 0) ++ { ++ if (type == ASN1_ETYPE_GENERALIZED_TIME) ++ { ++ /* tolerate lax encodings */ ++ if (p[i] == '.' && dot_count == 0) ++ { ++ dot_count++; ++ continue; ++ } ++ ++ /* This is not really valid DER, but there are ++ * structures using that */ ++ if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && ++ (p[i] == '+' || p[i] == '-') && sign_count == 0) ++ { ++ sign_count++; ++ continue; ++ } ++ } ++ ++ warn (); ++ return ASN1_TIME_ENCODING_ERROR; ++ } ++ } ++ ++ if (sign_count == 0 && p[str_len - 1] != 'Z') ++ { ++ warn (); ++ return ASN1_TIME_ENCODING_ERROR; ++ } ++ } ++ memcpy (str, der + len_len, str_len); ++ str[str_len] = 0; ++ *ret_len = str_len + len_len; ++ ++ return ASN1_SUCCESS; ++} ++ ++/** ++ * asn1_get_object_id_der: ++ * @der: DER data to decode containing the OBJECT IDENTIFIER ++ * @der_len: Length of DER data to decode. ++ * @ret_len: Output variable containing the length of the DER data. ++ * @str: Pre-allocated output buffer to put the textual object id in. ++ * @str_size: Length of pre-allocated output buffer. ++ * ++ * Converts a DER encoded object identifier to its textual form. This ++ * function expects the DER object identifier without the tag. ++ * ++ * Returns: %ASN1_SUCCESS on success, or an error. ++ **/ ++int ++asn1_get_object_id_der (const unsigned char *der, int der_len, int *ret_len, ++ char *str, int str_size) ++{ ++ int len_len, len, k; ++ int leading, parsed; ++ char temp[LTOSTR_MAX_SIZE]; ++ uint64_t val, val1, val0; ++ ++ *ret_len = 0; ++ if (str && str_size > 0) ++ str[0] = 0; /* no oid */ ++ ++ if (str == NULL || der_len <= 0) ++ return ASN1_GENERIC_ERROR; ++ ++ len = asn1_get_length_der (der, der_len, &len_len); ++ ++ if (len <= 0 || len + len_len > der_len) ++ return ASN1_DER_ERROR; ++ ++ /* leading octet can never be 0x80 */ ++ if (der[len_len] == 0x80) ++ return ASN1_DER_ERROR; ++ ++ val0 = 0; ++ ++ for (k = 0; k < len; k++) ++ { ++ if (INT_LEFT_SHIFT_OVERFLOW (val0, 7)) ++ return ASN1_DER_ERROR; ++ ++ val0 <<= 7; ++ val0 |= der[len_len + k] & 0x7F; ++ if (!(der[len_len + k] & 0x80)) ++ break; ++ } ++ parsed = ++k; ++ ++ /* val0 = (X*40) + Y, X={0,1,2}, Y<=39 when X={0,1} */ ++ /* X = val, Y = val1 */ ++ ++ /* check if X == 0 */ ++ val = 0; ++ val1 = val0; ++ if (val1 > 39) ++ { ++ val = 1; ++ val1 = val0 - 40; ++ if (val1 > 39) ++ { ++ val = 2; ++ val1 = val0 - 80; ++ } ++ } ++ ++ _asn1_str_cpy (str, str_size, _asn1_ltostr (val, temp)); ++ _asn1_str_cat (str, str_size, "."); ++ _asn1_str_cat (str, str_size, _asn1_ltostr (val1, temp)); ++ ++ val = 0; ++ leading = 1; ++ for (k = parsed; k < len; k++) ++ { ++ /* X.690 mandates that the leading byte must never be 0x80 ++ */ ++ if (leading != 0 && der[len_len + k] == 0x80) ++ return ASN1_DER_ERROR; ++ leading = 0; ++ ++ /* check for wrap around */ ++ if (INT_LEFT_SHIFT_OVERFLOW (val, 7)) ++ return ASN1_DER_ERROR; ++ ++ val = val << 7; ++ val |= der[len_len + k] & 0x7F; ++ ++ if (!(der[len_len + k] & 0x80)) ++ { ++ _asn1_str_cat (str, str_size, "."); ++ _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp)); ++ val = 0; ++ leading = 1; ++ } ++ } ++ ++ if (INT_ADD_OVERFLOW (len, len_len)) ++ return ASN1_DER_ERROR; ++ ++ *ret_len = len + len_len; ++ ++ return ASN1_SUCCESS; ++} ++ ++/** ++ * asn1_get_bit_der: ++ * @der: DER data to decode containing the BIT SEQUENCE. ++ * @der_len: Length of DER data to decode. ++ * @ret_len: Output variable containing the length of the DER data. ++ * @str: Pre-allocated output buffer to put decoded BIT SEQUENCE in. ++ * @str_size: Length of pre-allocated output buffer. ++ * @bit_len: Output variable containing the size of the BIT SEQUENCE. ++ * ++ * Extract a BIT SEQUENCE from DER data. ++ * ++ * Returns: %ASN1_SUCCESS on success, or an error. ++ **/ ++int ++asn1_get_bit_der (const unsigned char *der, int der_len, ++ int *ret_len, unsigned char *str, int str_size, ++ int *bit_len) ++{ ++ int len_len = 0, len_byte; ++ ++ if (der_len <= 0) ++ return ASN1_GENERIC_ERROR; ++ ++ len_byte = asn1_get_length_der (der, der_len, &len_len) - 1; ++ if (len_byte < 0) ++ return ASN1_DER_ERROR; ++ ++ *ret_len = len_byte + len_len + 1; ++ *bit_len = len_byte * 8 - der[len_len]; ++ ++ if (*bit_len < 0) ++ return ASN1_DER_ERROR; ++ ++ if (str_size >= len_byte) ++ { ++ if (len_byte > 0 && str) ++ memcpy (str, der + len_len + 1, len_byte); ++ } ++ else ++ { ++ return ASN1_MEM_ERROR; ++ } ++ ++ return ASN1_SUCCESS; ++} ++ ++/* tag_len: the total tag length (explicit+inner) ++ * inner_tag_len: the inner_tag length ++ */ ++static int ++_asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len, ++ int *tag_len, int *inner_tag_len, unsigned flags) ++{ ++ asn1_node p; ++ int counter, len2, len3, is_tag_implicit; ++ int result; ++ unsigned long tag, tag_implicit = 0; ++ unsigned char class, class2, class_implicit = 0; ++ ++ if (der_len <= 0) ++ return ASN1_GENERIC_ERROR; ++ ++ counter = is_tag_implicit = 0; ++ ++ if (node->type & CONST_TAG) ++ { ++ p = node->down; ++ while (p) ++ { ++ if (type_field (p->type) == ASN1_ETYPE_TAG) ++ { ++ if (p->type & CONST_APPLICATION) ++ class2 = ASN1_CLASS_APPLICATION; ++ else if (p->type & CONST_UNIVERSAL) ++ class2 = ASN1_CLASS_UNIVERSAL; ++ else if (p->type & CONST_PRIVATE) ++ class2 = ASN1_CLASS_PRIVATE; ++ else ++ class2 = ASN1_CLASS_CONTEXT_SPECIFIC; ++ ++ if (p->type & CONST_EXPLICIT) ++ { ++ if (asn1_get_tag_der ++ (der + counter, der_len, &class, &len2, ++ &tag) != ASN1_SUCCESS) ++ return ASN1_DER_ERROR; ++ ++ DECR_LEN (der_len, len2); ++ counter += len2; ++ ++ if (flags & ASN1_DECODE_FLAG_STRICT_DER) ++ len3 = ++ asn1_get_length_der (der + counter, der_len, &len2); ++ else ++ len3 = ++ asn1_get_length_ber (der + counter, der_len, &len2); ++ if (len3 < 0) ++ return ASN1_DER_ERROR; ++ ++ DECR_LEN (der_len, len2); ++ counter += len2; ++ ++ if (!is_tag_implicit) ++ { ++ if ((class != (class2 | ASN1_CLASS_STRUCTURED)) || ++ (tag != strtoul ((char *) p->value, NULL, 10))) ++ return ASN1_TAG_ERROR; ++ } ++ else ++ { /* ASN1_TAG_IMPLICIT */ ++ if ((class != class_implicit) || (tag != tag_implicit)) ++ return ASN1_TAG_ERROR; ++ } ++ is_tag_implicit = 0; ++ } ++ else ++ { /* ASN1_TAG_IMPLICIT */ ++ if (!is_tag_implicit) ++ { ++ if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) || ++ (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF) ++ || (type_field (node->type) == ASN1_ETYPE_SET) ++ || (type_field (node->type) == ASN1_ETYPE_SET_OF)) ++ class2 |= ASN1_CLASS_STRUCTURED; ++ class_implicit = class2; ++ tag_implicit = strtoul ((char *) p->value, NULL, 10); ++ is_tag_implicit = 1; ++ } ++ } ++ } ++ p = p->right; ++ } ++ } ++ ++ if (is_tag_implicit) ++ { ++ if (asn1_get_tag_der ++ (der + counter, der_len, &class, &len2, &tag) != ASN1_SUCCESS) ++ return ASN1_DER_ERROR; ++ ++ DECR_LEN (der_len, len2); ++ ++ if ((class != class_implicit) || (tag != tag_implicit)) ++ { ++ if (type_field (node->type) == ASN1_ETYPE_OCTET_STRING) ++ { ++ class_implicit |= ASN1_CLASS_STRUCTURED; ++ if ((class != class_implicit) || (tag != tag_implicit)) ++ return ASN1_TAG_ERROR; ++ } ++ else ++ return ASN1_TAG_ERROR; ++ } ++ } ++ else ++ { ++ unsigned type = type_field (node->type); ++ if (type == ASN1_ETYPE_TAG) ++ { ++ *tag_len = 0; ++ if (inner_tag_len) ++ *inner_tag_len = 0; ++ return ASN1_SUCCESS; ++ } ++ ++ if (asn1_get_tag_der ++ (der + counter, der_len, &class, &len2, &tag) != ASN1_SUCCESS) ++ return ASN1_DER_ERROR; ++ ++ DECR_LEN (der_len, len2); ++ ++ switch (type) ++ { ++ case ASN1_ETYPE_NULL: ++ case ASN1_ETYPE_BOOLEAN: ++ case ASN1_ETYPE_INTEGER: ++ case ASN1_ETYPE_ENUMERATED: ++ case ASN1_ETYPE_OBJECT_ID: ++ case ASN1_ETYPE_GENERALSTRING: ++ case ASN1_ETYPE_NUMERIC_STRING: ++ case ASN1_ETYPE_IA5_STRING: ++ case ASN1_ETYPE_TELETEX_STRING: ++ case ASN1_ETYPE_PRINTABLE_STRING: ++ case ASN1_ETYPE_UNIVERSAL_STRING: ++ case ASN1_ETYPE_BMP_STRING: ++ case ASN1_ETYPE_UTF8_STRING: ++ case ASN1_ETYPE_VISIBLE_STRING: ++ case ASN1_ETYPE_BIT_STRING: ++ case ASN1_ETYPE_SEQUENCE: ++ case ASN1_ETYPE_SEQUENCE_OF: ++ case ASN1_ETYPE_SET: ++ case ASN1_ETYPE_SET_OF: ++ case ASN1_ETYPE_GENERALIZED_TIME: ++ case ASN1_ETYPE_UTC_TIME: ++ if ((class != _asn1_tags[type].class) ++ || (tag != _asn1_tags[type].tag)) ++ return ASN1_DER_ERROR; ++ break; ++ ++ case ASN1_ETYPE_OCTET_STRING: ++ /* OCTET STRING is handled differently to allow ++ * BER encodings (structured class). */ ++ if (((class != ASN1_CLASS_UNIVERSAL) ++ && (class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED))) ++ || (tag != ASN1_TAG_OCTET_STRING)) ++ return ASN1_DER_ERROR; ++ break; ++ case ASN1_ETYPE_ANY: ++ counter -= len2; ++ break; ++ case ASN1_ETYPE_CHOICE: ++ counter -= len2; ++ break; ++ default: ++ return ASN1_DER_ERROR; ++ break; ++ } ++ } ++ ++ counter += len2; ++ *tag_len = counter; ++ if (inner_tag_len) ++ *inner_tag_len = len2; ++ return ASN1_SUCCESS; ++ ++cleanup: ++ return result; ++} ++ ++static int ++extract_tag_der_recursive (asn1_node node, const unsigned char *der, ++ int der_len, int *ret_len, int *inner_len, ++ unsigned flags) ++{ ++ asn1_node p; ++ int ris = ASN1_DER_ERROR; ++ ++ if (type_field (node->type) == ASN1_ETYPE_CHOICE) ++ { ++ p = node->down; ++ while (p) ++ { ++ ris = ++ _asn1_extract_tag_der (p, der, der_len, ret_len, inner_len, ++ flags); ++ if (ris == ASN1_SUCCESS) ++ break; ++ p = p->right; ++ } ++ ++ *ret_len = 0; ++ return ris; ++ } ++ else ++ return _asn1_extract_tag_der (node, der, der_len, ret_len, inner_len, ++ flags); ++} ++ ++static int ++_asn1_delete_not_used (asn1_node node) ++{ ++ asn1_node p, p2; ++ ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ p = node; ++ while (p) ++ { ++ if (p->type & CONST_NOT_USED) ++ { ++ p2 = NULL; ++ if (p != node) ++ { ++ p2 = _asn1_find_left (p); ++ if (!p2) ++ p2 = _asn1_find_up (p); ++ } ++ asn1_delete_structure (&p); ++ p = p2; ++ } ++ ++ if (!p) ++ break; /* reach node */ ++ ++ if (p->down) ++ { ++ p = p->down; ++ } ++ else ++ { ++ if (p == node) ++ p = NULL; ++ else if (p->right) ++ p = p->right; ++ else ++ { ++ while (1) ++ { ++ p = _asn1_find_up (p); ++ if (p == node) ++ { ++ p = NULL; ++ break; ++ } ++ if (p->right) ++ { ++ p = p->right; ++ break; ++ } ++ } ++ } ++ } ++ } ++ return ASN1_SUCCESS; ++} ++ ++static int ++_asn1_get_indefinite_length_string (const unsigned char *der, ++ int der_len, int *len) ++{ ++ int len2, len3, counter, indefinite; ++ int result; ++ unsigned long tag; ++ unsigned char class; ++ ++ counter = indefinite = 0; ++ ++ while (1) ++ { ++ if (HAVE_TWO (der_len) && (der[counter] == 0) ++ && (der[counter + 1] == 0)) ++ { ++ counter += 2; ++ DECR_LEN (der_len, 2); ++ ++ indefinite--; ++ if (indefinite <= 0) ++ break; ++ else ++ continue; ++ } ++ ++ if (asn1_get_tag_der ++ (der + counter, der_len, &class, &len2, &tag) != ASN1_SUCCESS) ++ return ASN1_DER_ERROR; ++ ++ DECR_LEN (der_len, len2); ++ counter += len2; ++ ++ len2 = asn1_get_length_der (der + counter, der_len, &len3); ++ if (len2 < -1) ++ return ASN1_DER_ERROR; ++ ++ if (len2 == -1) ++ { ++ indefinite++; ++ counter += 1; ++ DECR_LEN (der_len, 1); ++ } ++ else ++ { ++ counter += len2 + len3; ++ DECR_LEN (der_len, len2 + len3); ++ } ++ } ++ ++ *len = counter; ++ return ASN1_SUCCESS; ++ ++cleanup: ++ return result; ++} ++ ++static void ++delete_unneeded_choice_fields (asn1_node p) ++{ ++ asn1_node p2; ++ ++ while (p->right) ++ { ++ p2 = p->right; ++ asn1_delete_structure (&p2); ++ } ++} ++ ++ ++/** ++ * asn1_der_decoding2 ++ * @element: pointer to an ASN1 structure. ++ * @ider: vector that contains the DER encoding. ++ * @max_ider_len: pointer to an integer giving the information about the ++ * maximal number of bytes occupied by *@ider. The real size of the DER ++ * encoding is returned through this pointer. ++ * @flags: flags controlling the behaviour of the function. ++ * @errorDescription: null-terminated string contains details when an ++ * error occurred. ++ * ++ * Fill the structure *@element with values of a DER encoding string. The ++ * structure must just be created with function asn1_create_element(). ++ * ++ * If %ASN1_DECODE_FLAG_ALLOW_PADDING flag is set then the function will ignore ++ * padding after the decoded DER data. Upon a successful return the value of ++ * *@max_ider_len will be set to the number of bytes decoded. ++ * ++ * If %ASN1_DECODE_FLAG_STRICT_DER flag is set then the function will ++ * not decode any BER-encoded elements. ++ * ++ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND ++ * if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or ++ * %ASN1_DER_ERROR if the der encoding doesn't match the structure ++ * name (*@ELEMENT deleted). ++ **/ ++int ++asn1_der_decoding2 (asn1_node * element, const void *ider, int *max_ider_len, ++ unsigned int flags, char *errorDescription) ++{ ++ asn1_node node, p, p2, p3; ++ char temp[128]; ++ int counter, len2, len3, len4, move, ris, tlen; ++ struct node_tail_cache_st tcache = { NULL, NULL }; ++ unsigned char class; ++ unsigned long tag; ++ int tag_len; ++ int indefinite, result, total_len = *max_ider_len, ider_len = *max_ider_len; ++ int inner_tag_len; ++ unsigned char *ptmp; ++ const unsigned char *ptag; ++ const unsigned char *der = ider; ++ ++ node = *element; ++ ++ if (errorDescription != NULL) ++ errorDescription[0] = 0; ++ ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ if (node->type & CONST_OPTION) ++ { ++ result = ASN1_GENERIC_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ ++ counter = 0; ++ move = DOWN; ++ p = node; ++ while (1) ++ { ++ tag_len = 0; ++ inner_tag_len = 0; ++ ris = ASN1_SUCCESS; ++ if (move != UP) ++ { ++ if (p->type & CONST_SET) ++ { ++ p2 = _asn1_find_up (p); ++ len2 = p2->tmp_ival; ++ if (len2 == -1) ++ { ++ if (HAVE_TWO (ider_len) && !der[counter] ++ && !der[counter + 1]) ++ { ++ p = p2; ++ move = UP; ++ counter += 2; ++ DECR_LEN (ider_len, 2); ++ continue; ++ } ++ } ++ else if (counter == len2) ++ { ++ p = p2; ++ move = UP; ++ continue; ++ } ++ else if (counter > len2) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ p2 = p2->down; ++ while (p2) ++ { ++ if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED)) ++ { ++ ris = ++ extract_tag_der_recursive (p2, der + counter, ++ ider_len, &len2, NULL, ++ flags); ++ if (ris == ASN1_SUCCESS) ++ { ++ p2->type &= ~CONST_NOT_USED; ++ p = p2; ++ break; ++ } ++ } ++ p2 = p2->right; ++ } ++ if (p2 == NULL) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ } ++ ++ /* the position in the DER structure this starts */ ++ p->start = counter; ++ p->end = total_len - 1; ++ ++ if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) ++ { ++ p2 = _asn1_find_up (p); ++ len2 = p2->tmp_ival; ++ if (counter == len2) ++ { ++ if (p->right) ++ { ++ p2 = p->right; ++ move = RIGHT; ++ } ++ else ++ move = UP; ++ ++ if (p->type & CONST_OPTION) ++ asn1_delete_structure (&p); ++ ++ p = p2; ++ continue; ++ } ++ } ++ ++ if (type_field (p->type) == ASN1_ETYPE_CHOICE) ++ { ++ while (p->down) ++ { ++ ris = ++ extract_tag_der_recursive (p->down, der + counter, ++ ider_len, &len2, NULL, flags); ++ ++ if (ris == ASN1_SUCCESS) ++ { ++ delete_unneeded_choice_fields (p->down); ++ break; ++ } ++ else if (ris == ASN1_ERROR_TYPE_ANY) ++ { ++ result = ASN1_ERROR_TYPE_ANY; ++ warn (); ++ goto cleanup; ++ } ++ else ++ { ++ p2 = p->down; ++ asn1_delete_structure (&p2); ++ } ++ } ++ ++ if (p->down == NULL) ++ { ++ if (!(p->type & CONST_OPTION)) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ } ++ else if (type_field (p->type) != ASN1_ETYPE_CHOICE) ++ p = p->down; ++ ++ p->start = counter; ++ } ++ ++ if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) ++ { ++ p2 = _asn1_find_up (p); ++ len2 = p2->tmp_ival; ++ ++ if ((len2 != -1) && (counter > len2)) ++ ris = ASN1_TAG_ERROR; ++ } ++ ++ if (ris == ASN1_SUCCESS) ++ ris = ++ extract_tag_der_recursive (p, der + counter, ider_len, ++ &tag_len, &inner_tag_len, flags); ++ ++ if (ris != ASN1_SUCCESS) ++ { ++ if (p->type & CONST_OPTION) ++ { ++ p->type |= CONST_NOT_USED; ++ move = RIGHT; ++ } ++ else if (p->type & CONST_DEFAULT) ++ { ++ _asn1_set_value (p, NULL, 0); ++ move = RIGHT; ++ } ++ else ++ { ++ if (errorDescription != NULL) ++ _asn1_error_description_tag_error (p, errorDescription); ++ ++ result = ASN1_TAG_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ } ++ else ++ { ++ DECR_LEN (ider_len, tag_len); ++ counter += tag_len; ++ } ++ } ++ ++ if (ris == ASN1_SUCCESS) ++ { ++ switch (type_field (p->type)) ++ { ++ case ASN1_ETYPE_NULL: ++ DECR_LEN (ider_len, 1); ++ if (der[counter]) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ counter++; ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_BOOLEAN: ++ DECR_LEN (ider_len, 2); ++ ++ if (der[counter++] != 1) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ if (der[counter++] == 0) ++ _asn1_set_value (p, "F", 1); ++ else ++ _asn1_set_value (p, "T", 1); ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_INTEGER: ++ case ASN1_ETYPE_ENUMERATED: ++ len2 = asn1_get_length_der (der + counter, ider_len, &len3); ++ if (len2 < 0) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ ++ DECR_LEN (ider_len, len3 + len2); ++ ++ _asn1_set_value (p, der + counter, len3 + len2); ++ counter += len3 + len2; ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_OBJECT_ID: ++ result = ++ asn1_get_object_id_der (der + counter, ider_len, &len2, ++ temp, sizeof (temp)); ++ if (result != ASN1_SUCCESS) ++ { ++ warn (); ++ goto cleanup; ++ } ++ ++ DECR_LEN (ider_len, len2); ++ ++ tlen = strlen (temp); ++ if (tlen > 0) ++ _asn1_set_value (p, temp, tlen + 1); ++ ++ counter += len2; ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_GENERALIZED_TIME: ++ case ASN1_ETYPE_UTC_TIME: ++ result = ++ _asn1_get_time_der (type_field (p->type), der + counter, ++ ider_len, &len2, temp, sizeof (temp) - 1, ++ flags); ++ if (result != ASN1_SUCCESS) ++ { ++ warn (); ++ goto cleanup; ++ } ++ ++ DECR_LEN (ider_len, len2); ++ ++ tlen = strlen (temp); ++ if (tlen > 0) ++ _asn1_set_value (p, temp, tlen); ++ ++ counter += len2; ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_OCTET_STRING: ++ if (counter < inner_tag_len) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ ++ ptag = der + counter - inner_tag_len; ++ if ((flags & ASN1_DECODE_FLAG_STRICT_DER) ++ || !(ptag[0] & ASN1_CLASS_STRUCTURED)) ++ { ++ if (ptag[0] & ASN1_CLASS_STRUCTURED) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ ++ len2 = asn1_get_length_der (der + counter, ider_len, &len3); ++ if (len2 < 0) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ ++ DECR_LEN (ider_len, len3 + len2); ++ ++ _asn1_set_value (p, der + counter, len3 + len2); ++ counter += len3 + len2; ++ } ++ else ++ { ++ unsigned dflags = 0, vlen, ber_len; ++ ++ if (ptag[0] & ASN1_CLASS_STRUCTURED) ++ dflags |= DECODE_FLAG_CONSTRUCTED; ++ ++ result = ++ _asn1_decode_simple_ber (type_field (p->type), ++ der + counter, ider_len, &ptmp, ++ &vlen, &ber_len, dflags); ++ if (result != ASN1_SUCCESS) ++ { ++ warn (); ++ goto cleanup; ++ } ++ ++ DECR_LEN (ider_len, ber_len); ++ ++ _asn1_set_value_lv (p, ptmp, vlen); ++ ++ counter += ber_len; ++ free (ptmp); ++ } ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_GENERALSTRING: ++ case ASN1_ETYPE_NUMERIC_STRING: ++ case ASN1_ETYPE_IA5_STRING: ++ case ASN1_ETYPE_TELETEX_STRING: ++ case ASN1_ETYPE_PRINTABLE_STRING: ++ case ASN1_ETYPE_UNIVERSAL_STRING: ++ case ASN1_ETYPE_BMP_STRING: ++ case ASN1_ETYPE_UTF8_STRING: ++ case ASN1_ETYPE_VISIBLE_STRING: ++ case ASN1_ETYPE_BIT_STRING: ++ len2 = asn1_get_length_der (der + counter, ider_len, &len3); ++ if (len2 < 0) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ ++ DECR_LEN (ider_len, len3 + len2); ++ ++ _asn1_set_value (p, der + counter, len3 + len2); ++ counter += len3 + len2; ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_SEQUENCE: ++ case ASN1_ETYPE_SET: ++ if (move == UP) ++ { ++ len2 = p->tmp_ival; ++ p->tmp_ival = 0; ++ if (len2 == -1) ++ { /* indefinite length method */ ++ DECR_LEN (ider_len, 2); ++ if ((der[counter]) || der[counter + 1]) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ counter += 2; ++ } ++ else ++ { /* definite length method */ ++ if (len2 != counter) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ } ++ move = RIGHT; ++ } ++ else ++ { /* move==DOWN || move==RIGHT */ ++ len3 = asn1_get_length_der (der + counter, ider_len, &len2); ++ if (IS_ERR (len3, flags)) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ ++ DECR_LEN (ider_len, len2); ++ counter += len2; ++ ++ if (len3 > 0) ++ { ++ p->tmp_ival = counter + len3; ++ move = DOWN; ++ } ++ else if (len3 == 0) ++ { ++ p2 = p->down; ++ while (p2) ++ { ++ if (type_field (p2->type) != ASN1_ETYPE_TAG) ++ { ++ p3 = p2->right; ++ asn1_delete_structure (&p2); ++ p2 = p3; ++ } ++ else ++ p2 = p2->right; ++ } ++ move = RIGHT; ++ } ++ else ++ { /* indefinite length method */ ++ p->tmp_ival = -1; ++ move = DOWN; ++ } ++ } ++ break; ++ case ASN1_ETYPE_SEQUENCE_OF: ++ case ASN1_ETYPE_SET_OF: ++ if (move == UP) ++ { ++ len2 = p->tmp_ival; ++ if (len2 == -1) ++ { /* indefinite length method */ ++ if (!HAVE_TWO (ider_len) ++ || ((der[counter]) || der[counter + 1])) ++ { ++ result = _asn1_append_sequence_set (p, &tcache); ++ if (result != 0) ++ { ++ warn (); ++ goto cleanup; ++ } ++ p = tcache.tail; ++ move = RIGHT; ++ continue; ++ } ++ ++ p->tmp_ival = 0; ++ tcache.tail = NULL; /* finished decoding this structure */ ++ tcache.head = NULL; ++ DECR_LEN (ider_len, 2); ++ counter += 2; ++ } ++ else ++ { /* definite length method */ ++ if (len2 > counter) ++ { ++ result = _asn1_append_sequence_set (p, &tcache); ++ if (result != 0) ++ { ++ warn (); ++ goto cleanup; ++ } ++ p = tcache.tail; ++ move = RIGHT; ++ continue; ++ } ++ ++ p->tmp_ival = 0; ++ tcache.tail = NULL; /* finished decoding this structure */ ++ tcache.head = NULL; ++ ++ if (len2 != counter) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ } ++ } ++ else ++ { /* move==DOWN || move==RIGHT */ ++ len3 = asn1_get_length_der (der + counter, ider_len, &len2); ++ if (IS_ERR (len3, flags)) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ ++ DECR_LEN (ider_len, len2); ++ counter += len2; ++ if (len3) ++ { ++ if (len3 > 0) ++ { /* definite length method */ ++ p->tmp_ival = counter + len3; ++ } ++ else ++ { /* indefinite length method */ ++ p->tmp_ival = -1; ++ } ++ ++ p2 = p->down; ++ if (p2 == NULL) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ ++ while ((type_field (p2->type) == ASN1_ETYPE_TAG) ++ || (type_field (p2->type) == ASN1_ETYPE_SIZE)) ++ p2 = p2->right; ++ if (p2->right == NULL) ++ { ++ result = _asn1_append_sequence_set (p, &tcache); ++ if (result != 0) ++ { ++ warn (); ++ goto cleanup; ++ } ++ } ++ p = p2; ++ } ++ } ++ move = RIGHT; ++ break; ++ case ASN1_ETYPE_ANY: ++ /* Check indefinite lenth method in an EXPLICIT TAG */ ++ ++ if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) ++ && (p->type & CONST_TAG) && tag_len == 2 ++ && (der[counter - 1] == 0x80)) ++ indefinite = 1; ++ else ++ indefinite = 0; ++ ++ if (asn1_get_tag_der ++ (der + counter, ider_len, &class, &len2, ++ &tag) != ASN1_SUCCESS) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ ++ DECR_LEN (ider_len, len2); ++ ++ len4 = ++ asn1_get_length_der (der + counter + len2, ider_len, &len3); ++ if (IS_ERR (len4, flags)) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ if (len4 != -1) /* definite */ ++ { ++ len2 += len4; ++ ++ DECR_LEN (ider_len, len4 + len3); ++ _asn1_set_value_lv (p, der + counter, len2 + len3); ++ counter += len2 + len3; ++ } ++ else /* == -1 */ ++ { /* indefinite length */ ++ ider_len += len2; /* undo DECR_LEN */ ++ ++ if (counter == 0) ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ ++ result = ++ _asn1_get_indefinite_length_string (der + counter, ++ ider_len, &len2); ++ if (result != ASN1_SUCCESS) ++ { ++ warn (); ++ goto cleanup; ++ } ++ ++ DECR_LEN (ider_len, len2); ++ _asn1_set_value_lv (p, der + counter, len2); ++ counter += len2; ++ ++ } ++ ++ /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with ++ an indefinite length method. */ ++ if (indefinite) ++ { ++ DECR_LEN (ider_len, 2); ++ if (!der[counter] && !der[counter + 1]) ++ { ++ counter += 2; ++ } ++ else ++ { ++ result = ASN1_DER_ERROR; ++ warn (); ++ goto cleanup; ++ } ++ } ++ ++ move = RIGHT; ++ break; ++ default: ++ move = (move == UP) ? RIGHT : DOWN; ++ break; ++ } ++ } ++ ++ if (p) ++ { ++ p->end = counter - 1; ++ } ++ ++ if (p == node && move != DOWN) ++ break; ++ ++ if (move == DOWN) ++ { ++ if (p->down) ++ p = p->down; ++ else ++ move = RIGHT; ++ } ++ if ((move == RIGHT) && !(p->type & CONST_SET)) ++ { ++ if (p->right) ++ p = p->right; ++ else ++ move = UP; ++ } ++ if (move == UP) ++ p = _asn1_find_up (p); ++ } ++ ++ _asn1_delete_not_used (*element); ++ ++ if ((ider_len < 0) || ++ (!(flags & ASN1_DECODE_FLAG_ALLOW_PADDING) && (ider_len != 0))) ++ { ++ warn (); ++ result = ASN1_DER_ERROR; ++ goto cleanup; ++ } ++ ++ *max_ider_len = total_len - ider_len; ++ ++ return ASN1_SUCCESS; ++ ++cleanup: ++ asn1_delete_structure (element); ++ return result; ++} ++ ++ ++/** ++ * asn1_der_decoding: ++ * @element: pointer to an ASN1 structure. ++ * @ider: vector that contains the DER encoding. ++ * @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1]. ++ * @errorDescription: null-terminated string contains details when an ++ * error occurred. ++ * ++ * Fill the structure *@element with values of a DER encoding ++ * string. The structure must just be created with function ++ * asn1_create_element(). ++ * ++ * Note that the *@element variable is provided as a pointer for ++ * historical reasons. ++ * ++ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND ++ * if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or ++ * %ASN1_DER_ERROR if the der encoding doesn't match the structure ++ * name (*@ELEMENT deleted). ++ **/ ++int ++asn1_der_decoding (asn1_node * element, const void *ider, int ider_len, ++ char *errorDescription) ++{ ++ return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription); ++} ++ ++/** ++ * asn1_der_decoding_element: ++ * @structure: pointer to an ASN1 structure ++ * @elementName: name of the element to fill ++ * @ider: vector that contains the DER encoding of the whole structure. ++ * @len: number of bytes of *der: der[0]..der[len-1] ++ * @errorDescription: null-terminated string contains details when an ++ * error occurred. ++ * ++ * Fill the element named @ELEMENTNAME with values of a DER encoding ++ * string. The structure must just be created with function ++ * asn1_create_element(). The DER vector must contain the encoding ++ * string of the whole @STRUCTURE. If an error occurs during the ++ * decoding procedure, the *@STRUCTURE is deleted and set equal to ++ * %NULL. ++ * ++ * This function is deprecated and may just be an alias to asn1_der_decoding ++ * in future versions. Use asn1_der_decoding() instead. ++ * ++ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND ++ * if ELEMENT is %NULL or @elementName == NULL, and ++ * %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding doesn't ++ * match the structure @structure (*ELEMENT deleted). ++ **/ ++int ++asn1_der_decoding_element (asn1_node * structure, const char *elementName, ++ const void *ider, int len, char *errorDescription) ++{ ++ return asn1_der_decoding (structure, ider, len, errorDescription); ++} ++ ++/** ++ * asn1_der_decoding_startEnd: ++ * @element: pointer to an ASN1 element ++ * @ider: vector that contains the DER encoding. ++ * @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1] ++ * @name_element: an element of NAME structure. ++ * @start: the position of the first byte of NAME_ELEMENT decoding ++ * (@ider[*start]) ++ * @end: the position of the last byte of NAME_ELEMENT decoding ++ * (@ider[*end]) ++ * ++ * Find the start and end point of an element in a DER encoding ++ * string. I mean that if you have a der encoding and you have already ++ * used the function asn1_der_decoding() to fill a structure, it may ++ * happen that you want to find the piece of string concerning an ++ * element of the structure. ++ * ++ * One example is the sequence "tbsCertificate" inside an X509 ++ * certificate. ++ * ++ * Note that since libtasn1 3.7 the @ider and @ider_len parameters ++ * can be omitted, if the element is already decoded using asn1_der_decoding(). ++ * ++ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND ++ * if ELEMENT is %asn1_node EMPTY or @name_element is not a valid ++ * element, %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding ++ * doesn't match the structure ELEMENT. ++ **/ ++int ++asn1_der_decoding_startEnd (asn1_node element, const void *ider, int ider_len, ++ const char *name_element, int *start, int *end) ++{ ++ asn1_node node, node_to_find; ++ int result = ASN1_DER_ERROR; ++ ++ node = element; ++ ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ node_to_find = asn1_find_node (node, name_element); ++ ++ if (node_to_find == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ *start = node_to_find->start; ++ *end = node_to_find->end; ++ ++ if (*start == 0 && *end == 0) ++ { ++ if (ider == NULL || ider_len == 0) ++ return ASN1_GENERIC_ERROR; ++ ++ /* it seems asn1_der_decoding() wasn't called before. Do it now */ ++ result = asn1_der_decoding (&node, ider, ider_len, NULL); ++ if (result != ASN1_SUCCESS) ++ { ++ warn (); ++ return result; ++ } ++ ++ node_to_find = asn1_find_node (node, name_element); ++ if (node_to_find == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ *start = node_to_find->start; ++ *end = node_to_find->end; ++ } ++ ++ if (*end < *start) ++ return ASN1_GENERIC_ERROR; ++ ++ return ASN1_SUCCESS; ++} ++ ++/** ++ * asn1_expand_any_defined_by: ++ * @definitions: ASN1 definitions ++ * @element: pointer to an ASN1 structure ++ * ++ * Expands every "ANY DEFINED BY" element of a structure created from ++ * a DER decoding process (asn1_der_decoding function). The element ++ * ANY must be defined by an OBJECT IDENTIFIER. The type used to ++ * expand the element ANY is the first one following the definition of ++ * the actual value of the OBJECT IDENTIFIER. ++ * ++ * Returns: %ASN1_SUCCESS if Substitution OK, %ASN1_ERROR_TYPE_ANY if ++ * some "ANY DEFINED BY" element couldn't be expanded due to a ++ * problem in OBJECT_ID -> TYPE association, or other error codes ++ * depending on DER decoding. ++ **/ ++int ++asn1_expand_any_defined_by (asn1_node_const definitions, asn1_node * element) ++{ ++ char name[2 * ASN1_MAX_NAME_SIZE + 2], value[ASN1_MAX_NAME_SIZE]; ++ int retCode = ASN1_SUCCESS, result; ++ int len, len2, len3; ++ asn1_node_const p2; ++ asn1_node p, p3, aux = NULL; ++ char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; ++ const char *definitionsName; ++ ++ if ((definitions == NULL) || (*element == NULL)) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ definitionsName = definitions->name; ++ ++ p = *element; ++ while (p) ++ { ++ ++ switch (type_field (p->type)) ++ { ++ case ASN1_ETYPE_ANY: ++ if ((p->type & CONST_DEFINED_BY) && (p->value)) ++ { ++ /* search the "DEF_BY" element */ ++ p2 = p->down; ++ while ((p2) && (type_field (p2->type) != ASN1_ETYPE_CONSTANT)) ++ p2 = p2->right; ++ ++ if (!p2) ++ { ++ retCode = ASN1_ERROR_TYPE_ANY; ++ break; ++ } ++ ++ p3 = _asn1_find_up (p); ++ ++ if (!p3) ++ { ++ retCode = ASN1_ERROR_TYPE_ANY; ++ break; ++ } ++ ++ p3 = p3->down; ++ while (p3) ++ { ++ if (!(strcmp (p3->name, p2->name))) ++ break; ++ p3 = p3->right; ++ } ++ ++ if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) || ++ (p3->value == NULL)) ++ { ++ ++ p3 = _asn1_find_up (p); ++ p3 = _asn1_find_up (p3); ++ ++ if (!p3) ++ { ++ retCode = ASN1_ERROR_TYPE_ANY; ++ break; ++ } ++ ++ p3 = p3->down; ++ ++ while (p3) ++ { ++ if (!(strcmp (p3->name, p2->name))) ++ break; ++ p3 = p3->right; ++ } ++ ++ if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) ++ || (p3->value == NULL)) ++ { ++ retCode = ASN1_ERROR_TYPE_ANY; ++ break; ++ } ++ } ++ ++ /* search the OBJECT_ID into definitions */ ++ p2 = definitions->down; ++ while (p2) ++ { ++ if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) && ++ (p2->type & CONST_ASSIGN)) ++ { ++ snprintf (name, sizeof (name), "%s.%s", definitionsName, ++ p2->name); ++ ++ len = ASN1_MAX_NAME_SIZE; ++ result = ++ asn1_read_value (definitions, name, value, &len); ++ ++ if ((result == ASN1_SUCCESS) ++ && (!_asn1_strcmp (p3->value, value))) ++ { ++ p2 = p2->right; /* pointer to the structure to ++ use for expansion */ ++ while ((p2) && (p2->type & CONST_ASSIGN)) ++ p2 = p2->right; ++ ++ if (p2) ++ { ++ snprintf (name, sizeof (name), "%s.%s", ++ definitionsName, p2->name); ++ ++ result = ++ asn1_create_element (definitions, name, &aux); ++ if (result == ASN1_SUCCESS) ++ { ++ _asn1_cpy_name (aux, p); ++ len2 = ++ asn1_get_length_der (p->value, ++ p->value_len, &len3); ++ if (len2 < 0) ++ return ASN1_DER_ERROR; ++ ++ result = ++ asn1_der_decoding (&aux, p->value + len3, ++ len2, ++ errorDescription); ++ if (result == ASN1_SUCCESS) ++ { ++ ++ _asn1_set_right (aux, p->right); ++ _asn1_set_right (p, aux); ++ ++ result = asn1_delete_structure (&p); ++ if (result == ASN1_SUCCESS) ++ { ++ p = aux; ++ aux = NULL; ++ break; ++ } ++ else ++ { /* error with asn1_delete_structure */ ++ asn1_delete_structure (&aux); ++ retCode = result; ++ break; ++ } ++ } ++ else ++ { /* error with asn1_der_decoding */ ++ retCode = result; ++ break; ++ } ++ } ++ else ++ { /* error with asn1_create_element */ ++ retCode = result; ++ break; ++ } ++ } ++ else ++ { /* error with the pointer to the structure to exapand */ ++ retCode = ASN1_ERROR_TYPE_ANY; ++ break; ++ } ++ } ++ } ++ p2 = p2->right; ++ } /* end while */ ++ ++ if (!p2) ++ { ++ retCode = ASN1_ERROR_TYPE_ANY; ++ break; ++ } ++ ++ } ++ break; ++ default: ++ break; ++ } ++ ++ ++ if (p->down) ++ { ++ p = p->down; ++ } ++ else if (p == *element) ++ { ++ p = NULL; ++ break; ++ } ++ else if (p->right) ++ p = p->right; ++ else ++ { ++ while (1) ++ { ++ p = _asn1_find_up (p); ++ if (p == *element) ++ { ++ p = NULL; ++ break; ++ } ++ if (p->right) ++ { ++ p = p->right; ++ break; ++ } ++ } ++ } ++ } ++ ++ return retCode; ++} ++ ++/** ++ * asn1_expand_octet_string: ++ * @definitions: ASN1 definitions ++ * @element: pointer to an ASN1 structure ++ * @octetName: name of the OCTECT STRING field to expand. ++ * @objectName: name of the OBJECT IDENTIFIER field to use to define ++ * the type for expansion. ++ * ++ * Expands an "OCTET STRING" element of a structure created from a DER ++ * decoding process (the asn1_der_decoding() function). The type used ++ * for expansion is the first one following the definition of the ++ * actual value of the OBJECT IDENTIFIER indicated by OBJECTNAME. ++ * ++ * Returns: %ASN1_SUCCESS if substitution OK, %ASN1_ELEMENT_NOT_FOUND ++ * if @objectName or @octetName are not correct, ++ * %ASN1_VALUE_NOT_VALID if it wasn't possible to find the type to ++ * use for expansion, or other errors depending on DER decoding. ++ **/ ++int ++asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element, ++ const char *octetName, const char *objectName) ++{ ++ char name[2 * ASN1_MAX_NAME_SIZE + 1], value[ASN1_MAX_NAME_SIZE]; ++ int retCode = ASN1_SUCCESS, result; ++ int len, len2, len3; ++ asn1_node_const p2; ++ asn1_node aux = NULL; ++ asn1_node octetNode = NULL, objectNode = NULL; ++ char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; ++ ++ if ((definitions == NULL) || (*element == NULL)) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ octetNode = asn1_find_node (*element, octetName); ++ if (octetNode == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ if (type_field (octetNode->type) != ASN1_ETYPE_OCTET_STRING) ++ return ASN1_ELEMENT_NOT_FOUND; ++ if (octetNode->value == NULL) ++ return ASN1_VALUE_NOT_FOUND; ++ ++ objectNode = asn1_find_node (*element, objectName); ++ if (objectNode == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ if (type_field (objectNode->type) != ASN1_ETYPE_OBJECT_ID) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ if (objectNode->value == NULL) ++ return ASN1_VALUE_NOT_FOUND; ++ ++ ++ /* search the OBJECT_ID into definitions */ ++ p2 = definitions->down; ++ while (p2) ++ { ++ if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) && ++ (p2->type & CONST_ASSIGN)) ++ { ++ strcpy (name, definitions->name); ++ strcat (name, "."); ++ strcat (name, p2->name); ++ ++ len = sizeof (value); ++ result = asn1_read_value (definitions, name, value, &len); ++ ++ if ((result == ASN1_SUCCESS) ++ && (!_asn1_strcmp (objectNode->value, value))) ++ { ++ ++ p2 = p2->right; /* pointer to the structure to ++ use for expansion */ ++ while ((p2) && (p2->type & CONST_ASSIGN)) ++ p2 = p2->right; ++ ++ if (p2) ++ { ++ strcpy (name, definitions->name); ++ strcat (name, "."); ++ strcat (name, p2->name); ++ ++ result = asn1_create_element (definitions, name, &aux); ++ if (result == ASN1_SUCCESS) ++ { ++ _asn1_cpy_name (aux, octetNode); ++ len2 = ++ asn1_get_length_der (octetNode->value, ++ octetNode->value_len, &len3); ++ if (len2 < 0) ++ return ASN1_DER_ERROR; ++ ++ result = ++ asn1_der_decoding (&aux, octetNode->value + len3, ++ len2, errorDescription); ++ if (result == ASN1_SUCCESS) ++ { ++ ++ _asn1_set_right (aux, octetNode->right); ++ _asn1_set_right (octetNode, aux); ++ ++ result = asn1_delete_structure (&octetNode); ++ if (result == ASN1_SUCCESS) ++ { ++ aux = NULL; ++ break; ++ } ++ else ++ { /* error with asn1_delete_structure */ ++ asn1_delete_structure (&aux); ++ retCode = result; ++ break; ++ } ++ } ++ else ++ { /* error with asn1_der_decoding */ ++ retCode = result; ++ break; ++ } ++ } ++ else ++ { /* error with asn1_create_element */ ++ retCode = result; ++ break; ++ } ++ } ++ else ++ { /* error with the pointer to the structure to exapand */ ++ retCode = ASN1_VALUE_NOT_VALID; ++ break; ++ } ++ } ++ } ++ ++ p2 = p2->right; ++ ++ } ++ ++ if (!p2) ++ retCode = ASN1_VALUE_NOT_VALID; ++ ++ return retCode; ++} ++ ++/*- ++ * _asn1_decode_simple_der: ++ * @etype: The type of the string to be encoded (ASN1_ETYPE_) ++ * @der: the encoded string ++ * @_der_len: the bytes of the encoded string ++ * @str: a pointer to the data ++ * @str_len: the length of the data ++ * @dflags: DECODE_FLAG_* ++ * ++ * Decodes a simple DER encoded type (e.g. a string, which is not constructed). ++ * The output is a pointer inside the @der. ++ * ++ * Returns: %ASN1_SUCCESS if successful or an error value. ++ -*/ ++static int ++_asn1_decode_simple_der (unsigned int etype, const unsigned char *der, ++ unsigned int _der_len, const unsigned char **str, ++ unsigned int *str_len, unsigned dflags) ++{ ++ int tag_len, len_len; ++ const unsigned char *p; ++ int der_len = _der_len; ++ unsigned char class; ++ unsigned long tag; ++ long ret; ++ ++ if (der == NULL || der_len == 0) ++ return ASN1_VALUE_NOT_VALID; ++ ++ if (ETYPE_OK (etype) == 0 || ETYPE_IS_STRING (etype) == 0) ++ return ASN1_VALUE_NOT_VALID; ++ ++ /* doesn't handle constructed classes */ ++ class = ETYPE_CLASS (etype); ++ if (class != ASN1_CLASS_UNIVERSAL) ++ return ASN1_VALUE_NOT_VALID; ++ ++ p = der; ++ ++ if (dflags & DECODE_FLAG_HAVE_TAG) ++ { ++ ret = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag); ++ if (ret != ASN1_SUCCESS) ++ return ret; ++ ++ if (class != ETYPE_CLASS (etype) || tag != ETYPE_TAG (etype)) ++ { ++ warn (); ++ return ASN1_DER_ERROR; ++ } ++ ++ p += tag_len; ++ der_len -= tag_len; ++ if (der_len <= 0) ++ return ASN1_DER_ERROR; ++ } ++ ++ ret = asn1_get_length_der (p, der_len, &len_len); ++ if (ret < 0) ++ return ASN1_DER_ERROR; ++ ++ p += len_len; ++ der_len -= len_len; ++ if (der_len <= 0) ++ return ASN1_DER_ERROR; ++ ++ *str_len = ret; ++ *str = p; ++ ++ return ASN1_SUCCESS; ++} ++ ++/** ++ * asn1_decode_simple_der: ++ * @etype: The type of the string to be encoded (ASN1_ETYPE_) ++ * @der: the encoded string ++ * @_der_len: the bytes of the encoded string ++ * @str: a pointer to the data ++ * @str_len: the length of the data ++ * ++ * Decodes a simple DER encoded type (e.g. a string, which is not constructed). ++ * The output is a pointer inside the @der. ++ * ++ * Returns: %ASN1_SUCCESS if successful or an error value. ++ **/ ++int ++asn1_decode_simple_der (unsigned int etype, const unsigned char *der, ++ unsigned int _der_len, const unsigned char **str, ++ unsigned int *str_len) ++{ ++ return _asn1_decode_simple_der (etype, der, _der_len, str, str_len, ++ DECODE_FLAG_HAVE_TAG); ++} ++ ++static int ++append (uint8_t ** dst, unsigned *dst_size, const unsigned char *src, ++ unsigned src_size) ++{ ++ if (src_size == 0) ++ return ASN1_SUCCESS; ++ ++ *dst = _asn1_realloc (*dst, *dst_size + src_size); ++ if (*dst == NULL) ++ return ASN1_MEM_ALLOC_ERROR; ++ memcpy (*dst + *dst_size, src, src_size); ++ *dst_size += src_size; ++ return ASN1_SUCCESS; ++} ++ ++/*- ++ * _asn1_decode_simple_ber: ++ * @etype: The type of the string to be encoded (ASN1_ETYPE_) ++ * @der: the encoded string ++ * @_der_len: the bytes of the encoded string ++ * @str: a pointer to the data ++ * @str_len: the length of the data ++ * @ber_len: the total length occupied by BER (may be %NULL) ++ * @have_tag: whether a DER tag is included ++ * ++ * Decodes a BER encoded type. The output is an allocated value ++ * of the data. This decodes BER STRINGS only. Other types are ++ * decoded as DER. ++ * ++ * Returns: %ASN1_SUCCESS if successful or an error value. ++ -*/ ++static int ++_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, ++ unsigned int _der_len, unsigned char **str, ++ unsigned int *str_len, unsigned int *ber_len, ++ unsigned dflags) ++{ ++ int tag_len, len_len; ++ const unsigned char *p; ++ int der_len = _der_len; ++ uint8_t *total = NULL; ++ unsigned total_size = 0; ++ unsigned char class; ++ unsigned long tag; ++ unsigned char *out = NULL; ++ const unsigned char *cout = NULL; ++ unsigned out_len; ++ long result; ++ ++ if (ber_len) ++ *ber_len = 0; ++ ++ if (der == NULL || der_len == 0) ++ { ++ warn (); ++ return ASN1_VALUE_NOT_VALID; ++ } ++ ++ if (ETYPE_OK (etype) == 0) ++ { ++ warn (); ++ return ASN1_VALUE_NOT_VALID; ++ } ++ ++ /* doesn't handle constructed + definite classes */ ++ class = ETYPE_CLASS (etype); ++ if (class != ASN1_CLASS_UNIVERSAL) ++ { ++ warn (); ++ return ASN1_VALUE_NOT_VALID; ++ } ++ ++ p = der; ++ ++ if (dflags & DECODE_FLAG_HAVE_TAG) ++ { ++ result = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag); ++ if (result != ASN1_SUCCESS) ++ { ++ warn (); ++ return result; ++ } ++ ++ if (tag != ETYPE_TAG (etype)) ++ { ++ warn (); ++ return ASN1_DER_ERROR; ++ } ++ ++ p += tag_len; ++ ++ DECR_LEN (der_len, tag_len); ++ ++ if (ber_len) ++ *ber_len += tag_len; ++ } ++ ++ /* indefinite constructed */ ++ if ((((dflags & DECODE_FLAG_CONSTRUCTED) || class == ASN1_CLASS_STRUCTURED) ++ && ETYPE_IS_STRING (etype)) && !(dflags & DECODE_FLAG_LEVEL3)) ++ { ++ if (der_len == 0) ++ { ++ warn (); ++ result = ASN1_DER_ERROR; ++ goto cleanup; ++ } ++ ++ if (der_len > 0 && p[0] == 0x80) /* indefinite */ ++ { ++ len_len = 1; ++ DECR_LEN (der_len, len_len); ++ p += len_len; ++ ++ if (ber_len) ++ *ber_len += len_len; ++ ++ /* decode the available octet strings */ ++ do ++ { ++ unsigned tmp_len; ++ unsigned flags = DECODE_FLAG_HAVE_TAG; ++ ++ if (dflags & DECODE_FLAG_LEVEL1) ++ flags |= DECODE_FLAG_LEVEL2; ++ else if (dflags & DECODE_FLAG_LEVEL2) ++ flags |= DECODE_FLAG_LEVEL3; ++ else ++ flags |= DECODE_FLAG_LEVEL1; ++ ++ result = ++ _asn1_decode_simple_ber (etype, p, der_len, &out, &out_len, ++ &tmp_len, flags); ++ if (result != ASN1_SUCCESS) ++ { ++ warn (); ++ goto cleanup; ++ } ++ ++ p += tmp_len; ++ DECR_LEN (der_len, tmp_len); ++ ++ if (ber_len) ++ *ber_len += tmp_len; ++ ++ DECR_LEN (der_len, 2); /* we need the EOC */ ++ ++ result = append (&total, &total_size, out, out_len); ++ if (result != ASN1_SUCCESS) ++ { ++ warn (); ++ goto cleanup; ++ } ++ ++ free (out); ++ out = NULL; ++ ++ if (p[0] == 0 && p[1] == 0) /* EOC */ ++ { ++ if (ber_len) ++ *ber_len += 2; ++ break; ++ } ++ ++ /* no EOC */ ++ der_len += 2; ++ ++ if (der_len == 2) ++ { ++ warn (); ++ result = ASN1_DER_ERROR; ++ goto cleanup; ++ } ++ } ++ while (1); ++ } ++ else /* constructed */ ++ { ++ long const_len; ++ ++ result = asn1_get_length_ber (p, der_len, &len_len); ++ if (result < 0) ++ { ++ warn (); ++ result = ASN1_DER_ERROR; ++ goto cleanup; ++ } ++ ++ DECR_LEN (der_len, len_len); ++ p += len_len; ++ ++ const_len = result; ++ ++ if (ber_len) ++ *ber_len += len_len; ++ ++ /* decode the available octet strings */ ++ while (const_len > 0) ++ { ++ unsigned tmp_len; ++ unsigned flags = DECODE_FLAG_HAVE_TAG; ++ ++ if (dflags & DECODE_FLAG_LEVEL1) ++ flags |= DECODE_FLAG_LEVEL2; ++ else if (dflags & DECODE_FLAG_LEVEL2) ++ flags |= DECODE_FLAG_LEVEL3; ++ else ++ flags |= DECODE_FLAG_LEVEL1; ++ ++ result = ++ _asn1_decode_simple_ber (etype, p, der_len, &out, &out_len, ++ &tmp_len, flags); ++ if (result != ASN1_SUCCESS) ++ { ++ warn (); ++ goto cleanup; ++ } ++ ++ p += tmp_len; ++ DECR_LEN (der_len, tmp_len); ++ DECR_LEN (const_len, tmp_len); ++ ++ if (ber_len) ++ *ber_len += tmp_len; ++ ++ result = append (&total, &total_size, out, out_len); ++ if (result != ASN1_SUCCESS) ++ { ++ warn (); ++ goto cleanup; ++ } ++ ++ free (out); ++ out = NULL; ++ } ++ } ++ } ++ else if (class == ETYPE_CLASS (etype)) ++ { ++ if (ber_len) ++ { ++ result = asn1_get_length_der (p, der_len, &len_len); ++ if (result < 0) ++ { ++ warn (); ++ result = ASN1_DER_ERROR; ++ goto cleanup; ++ } ++ *ber_len += result + len_len; ++ } ++ ++ /* non-string values are decoded as DER */ ++ result = ++ _asn1_decode_simple_der (etype, der, _der_len, &cout, &out_len, ++ dflags); ++ if (result != ASN1_SUCCESS) ++ { ++ warn (); ++ goto cleanup; ++ } ++ ++ result = append (&total, &total_size, cout, out_len); ++ if (result != ASN1_SUCCESS) ++ { ++ warn (); ++ goto cleanup; ++ } ++ } ++ else ++ { ++ warn (); ++ result = ASN1_DER_ERROR; ++ goto cleanup; ++ } ++ ++ *str = total; ++ *str_len = total_size; ++ ++ return ASN1_SUCCESS; ++cleanup: ++ free (out); ++ free (total); ++ return result; ++} ++ ++/** ++ * asn1_decode_simple_ber: ++ * @etype: The type of the string to be encoded (ASN1_ETYPE_) ++ * @der: the encoded string ++ * @_der_len: the bytes of the encoded string ++ * @str: a pointer to the data ++ * @str_len: the length of the data ++ * @ber_len: the total length occupied by BER (may be %NULL) ++ * ++ * Decodes a BER encoded type. The output is an allocated value ++ * of the data. This decodes BER STRINGS only. Other types are ++ * decoded as DER. ++ * ++ * Returns: %ASN1_SUCCESS if successful or an error value. ++ **/ ++int ++asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, ++ unsigned int _der_len, unsigned char **str, ++ unsigned int *str_len, unsigned int *ber_len) ++{ ++ return _asn1_decode_simple_ber (etype, der, _der_len, str, str_len, ber_len, ++ DECODE_FLAG_HAVE_TAG); ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1/lib/element.c +@@ -0,0 +1,1109 @@ ++/* ++ * Copyright (C) 2000-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * The LIBTASN1 library is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ */ ++ ++/*****************************************************/ ++/* File: element.c */ ++/* Description: Functions with the read and write */ ++/* functions. */ ++/*****************************************************/ ++ ++ ++#include ++#include "parser_aux.h" ++#include ++#include "structure.h" ++#include "c-ctype.h" ++#include "element.h" ++ ++void ++_asn1_hierarchical_name (asn1_node_const node, char *name, int name_size) ++{ ++ asn1_node_const p; ++ char tmp_name[64]; ++ ++ p = node; ++ ++ name[0] = 0; ++ ++ while (p != NULL) ++ { ++ if (p->name[0] != 0) ++ { ++ _asn1_str_cpy (tmp_name, sizeof (tmp_name), name), ++ _asn1_str_cpy (name, name_size, p->name); ++ _asn1_str_cat (name, name_size, "."); ++ _asn1_str_cat (name, name_size, tmp_name); ++ } ++ p = _asn1_find_up (p); ++ } ++ ++ if (name[0] == 0) ++ _asn1_str_cpy (name, name_size, "ROOT"); ++} ++ ++ ++/******************************************************************/ ++/* Function : _asn1_convert_integer */ ++/* Description: converts an integer from a null terminated string */ ++/* to der decoding. The convertion from a null */ ++/* terminated string to an integer is made with */ ++/* the 'strtol' function. */ ++/* Parameters: */ ++/* value: null terminated string to convert. */ ++/* value_out: convertion result (memory must be already */ ++/* allocated). */ ++/* value_out_size: number of bytes of value_out. */ ++/* len: number of significant byte of value_out. */ ++/* Return: ASN1_MEM_ERROR or ASN1_SUCCESS */ ++/******************************************************************/ ++int ++_asn1_convert_integer (const unsigned char *value, unsigned char *value_out, ++ int value_out_size, int *len) ++{ ++ char negative; ++ unsigned char val[SIZEOF_UNSIGNED_LONG_INT]; ++ long valtmp; ++ int k, k2; ++ ++ valtmp = _asn1_strtol (value, NULL, 10); ++ ++ for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++) ++ { ++ val[SIZEOF_UNSIGNED_LONG_INT - k - 1] = (valtmp >> (8 * k)) & 0xFF; ++ } ++ ++ if (val[0] & 0x80) ++ negative = 1; ++ else ++ negative = 0; ++ ++ for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT - 1; k++) ++ { ++ if (negative && (val[k] != 0xFF)) ++ break; ++ else if (!negative && val[k]) ++ break; ++ } ++ ++ if ((negative && !(val[k] & 0x80)) || (!negative && (val[k] & 0x80))) ++ k--; ++ ++ *len = SIZEOF_UNSIGNED_LONG_INT - k; ++ ++ if (SIZEOF_UNSIGNED_LONG_INT - k > value_out_size) ++ /* VALUE_OUT is too short to contain the value conversion */ ++ return ASN1_MEM_ERROR; ++ ++ if (value_out != NULL) ++ { ++ for (k2 = k; k2 < SIZEOF_UNSIGNED_LONG_INT; k2++) ++ value_out[k2 - k] = val[k2]; ++ } ++ ++#if 0 ++ printf ("_asn1_convert_integer: valueIn=%s, lenOut=%d", value, *len); ++ for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++) ++ printf (", vOut[%d]=%d", k, value_out[k]); ++ printf ("\n"); ++#endif ++ ++ return ASN1_SUCCESS; ++} ++ ++/* Appends a new element into the sequence (or set) defined by this ++ * node. The new element will have a name of '?number', where number ++ * is a monotonically increased serial number. ++ * ++ * The last element in the list may be provided in @pcache, to avoid ++ * traversing the list, an expensive operation in long lists. ++ * ++ * On success it returns in @pcache the added element (which is the ++ * tail in the list of added elements). ++ */ ++int ++_asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache) ++{ ++ asn1_node p, p2; ++ char temp[LTOSTR_MAX_SIZE + 1]; ++ long n; ++ ++ if (!node || !(node->down)) ++ return ASN1_GENERIC_ERROR; ++ ++ p = node->down; ++ while ((type_field (p->type) == ASN1_ETYPE_TAG) ++ || (type_field (p->type) == ASN1_ETYPE_SIZE)) ++ p = p->right; ++ ++ p2 = _asn1_copy_structure3 (p); ++ if (p2 == NULL) ++ return ASN1_GENERIC_ERROR; ++ ++ if (pcache == NULL || pcache->tail == NULL || pcache->head != node) ++ { ++ while (p->right) ++ { ++ p = p->right; ++ } ++ } ++ else ++ { ++ p = pcache->tail; ++ } ++ ++ _asn1_set_right (p, p2); ++ if (pcache) ++ { ++ pcache->head = node; ++ pcache->tail = p2; ++ } ++ ++ if (p->name[0] == 0) ++ _asn1_str_cpy (temp, sizeof (temp), "?1"); ++ else ++ { ++ n = strtol (p->name + 1, NULL, 0); ++ n++; ++ temp[0] = '?'; ++ _asn1_ltostr (n, temp + 1); ++ } ++ _asn1_set_name (p2, temp); ++ /* p2->type |= CONST_OPTION; */ ++ ++ return ASN1_SUCCESS; ++} ++ ++ ++/** ++ * asn1_write_value: ++ * @node_root: pointer to a structure ++ * @name: the name of the element inside the structure that you want to set. ++ * @ivalue: vector used to specify the value to set. If len is >0, ++ * VALUE must be a two's complement form integer. if len=0 *VALUE ++ * must be a null terminated string with an integer value. ++ * @len: number of bytes of *value to use to set the value: ++ * value[0]..value[len-1] or 0 if value is a null terminated string ++ * ++ * Set the value of one element inside a structure. ++ * ++ * If an element is OPTIONAL and you want to delete it, you must use ++ * the value=NULL and len=0. Using "pkix.asn": ++ * ++ * result=asn1_write_value(cert, "tbsCertificate.issuerUniqueID", ++ * NULL, 0); ++ * ++ * Description for each type: ++ * ++ * INTEGER: VALUE must contain a two's complement form integer. ++ * ++ * value[0]=0xFF , len=1 -> integer=-1. ++ * value[0]=0xFF value[1]=0xFF , len=2 -> integer=-1. ++ * value[0]=0x01 , len=1 -> integer= 1. ++ * value[0]=0x00 value[1]=0x01 , len=2 -> integer= 1. ++ * value="123" , len=0 -> integer= 123. ++ * ++ * ENUMERATED: As INTEGER (but only with not negative numbers). ++ * ++ * BOOLEAN: VALUE must be the null terminated string "TRUE" or ++ * "FALSE" and LEN != 0. ++ * ++ * value="TRUE" , len=1 -> boolean=TRUE. ++ * value="FALSE" , len=1 -> boolean=FALSE. ++ * ++ * OBJECT IDENTIFIER: VALUE must be a null terminated string with ++ * each number separated by a dot (e.g. "1.2.3.543.1"). LEN != 0. ++ * ++ * value="1 2 840 10040 4 3" , len=1 -> OID=dsa-with-sha. ++ * ++ * UTCTime: VALUE must be a null terminated string in one of these ++ * formats: "YYMMDDhhmmssZ", "YYMMDDhhmmssZ", ++ * "YYMMDDhhmmss+hh'mm'", "YYMMDDhhmmss-hh'mm'", ++ * "YYMMDDhhmm+hh'mm'", or "YYMMDDhhmm-hh'mm'". LEN != 0. ++ * ++ * value="9801011200Z" , len=1 -> time=Jannuary 1st, 1998 ++ * at 12h 00m Greenwich Mean Time ++ * ++ * GeneralizedTime: VALUE must be in one of this format: ++ * "YYYYMMDDhhmmss.sZ", "YYYYMMDDhhmmss.sZ", ++ * "YYYYMMDDhhmmss.s+hh'mm'", "YYYYMMDDhhmmss.s-hh'mm'", ++ * "YYYYMMDDhhmm+hh'mm'", or "YYYYMMDDhhmm-hh'mm'" where ss.s ++ * indicates the seconds with any precision like "10.1" or "01.02". ++ * LEN != 0 ++ * ++ * value="2001010112001.12-0700" , len=1 -> time=Jannuary ++ * 1st, 2001 at 12h 00m 01.12s Pacific Daylight Time ++ * ++ * OCTET STRING: VALUE contains the octet string and LEN is the ++ * number of octets. ++ * ++ * value="$\backslash$x01$\backslash$x02$\backslash$x03" , ++ * len=3 -> three bytes octet string ++ * ++ * GeneralString: VALUE contains the generalstring and LEN is the ++ * number of octets. ++ * ++ * value="$\backslash$x01$\backslash$x02$\backslash$x03" , ++ * len=3 -> three bytes generalstring ++ * ++ * BIT STRING: VALUE contains the bit string organized by bytes and ++ * LEN is the number of bits. ++ * ++ * value="$\backslash$xCF" , len=6 -> bit string="110011" (six ++ * bits) ++ * ++ * CHOICE: if NAME indicates a choice type, VALUE must specify one of ++ * the alternatives with a null terminated string. LEN != 0. Using ++ * "pkix.asn"\: ++ * ++ * result=asn1_write_value(cert, ++ * "certificate1.tbsCertificate.subject", "rdnSequence", ++ * 1); ++ * ++ * ANY: VALUE indicates the der encoding of a structure. LEN != 0. ++ * ++ * SEQUENCE OF: VALUE must be the null terminated string "NEW" and ++ * LEN != 0. With this instruction another element is appended in ++ * the sequence. The name of this element will be "?1" if it's the ++ * first one, "?2" for the second and so on. ++ * ++ * Using "pkix.asn"\: ++ * ++ * result=asn1_write_value(cert, ++ * "certificate1.tbsCertificate.subject.rdnSequence", "NEW", 1); ++ * ++ * SET OF: the same as SEQUENCE OF. Using "pkix.asn": ++ * ++ * result=asn1_write_value(cert, ++ * "tbsCertificate.subject.rdnSequence.?LAST", "NEW", 1); ++ * ++ * Returns: %ASN1_SUCCESS if the value was set, ++ * %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, and ++ * %ASN1_VALUE_NOT_VALID if @ivalue has a wrong format. ++ **/ ++int ++asn1_write_value (asn1_node node_root, const char *name, ++ const void *ivalue, int len) ++{ ++ asn1_node node, p, p2; ++ unsigned char *temp, *value_temp = NULL, *default_temp = NULL; ++ int len2, k, k2, negative; ++ size_t i; ++ const unsigned char *value = ivalue; ++ unsigned int type; ++ ++ node = asn1_find_node (node_root, name); ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ if ((node->type & CONST_OPTION) && (value == NULL) && (len == 0)) ++ { ++ asn1_delete_structure (&node); ++ return ASN1_SUCCESS; ++ } ++ ++ type = type_field (node->type); ++ ++ if ((type == ASN1_ETYPE_SEQUENCE_OF || type == ASN1_ETYPE_SET_OF) ++ && (value == NULL) && (len == 0)) ++ { ++ p = node->down; ++ while ((type_field (p->type) == ASN1_ETYPE_TAG) ++ || (type_field (p->type) == ASN1_ETYPE_SIZE)) ++ p = p->right; ++ ++ while (p->right) ++ asn1_delete_structure (&p->right); ++ ++ return ASN1_SUCCESS; ++ } ++ ++ /* Don't allow element deletion for other types */ ++ if (value == NULL) ++ { ++ return ASN1_VALUE_NOT_VALID; ++ } ++ ++ switch (type) ++ { ++ case ASN1_ETYPE_BOOLEAN: ++ if (!_asn1_strcmp (value, "TRUE")) ++ { ++ if (node->type & CONST_DEFAULT) ++ { ++ p = node->down; ++ while (type_field (p->type) != ASN1_ETYPE_DEFAULT) ++ p = p->right; ++ if (p->type & CONST_TRUE) ++ _asn1_set_value (node, NULL, 0); ++ else ++ _asn1_set_value (node, "T", 1); ++ } ++ else ++ _asn1_set_value (node, "T", 1); ++ } ++ else if (!_asn1_strcmp (value, "FALSE")) ++ { ++ if (node->type & CONST_DEFAULT) ++ { ++ p = node->down; ++ while (type_field (p->type) != ASN1_ETYPE_DEFAULT) ++ p = p->right; ++ if (p->type & CONST_FALSE) ++ _asn1_set_value (node, NULL, 0); ++ else ++ _asn1_set_value (node, "F", 1); ++ } ++ else ++ _asn1_set_value (node, "F", 1); ++ } ++ else ++ return ASN1_VALUE_NOT_VALID; ++ break; ++ case ASN1_ETYPE_INTEGER: ++ case ASN1_ETYPE_ENUMERATED: ++ if (len == 0) ++ { ++ if ((c_isdigit (value[0])) || (value[0] == '-')) ++ { ++ value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); ++ if (value_temp == NULL) ++ return ASN1_MEM_ALLOC_ERROR; ++ ++ _asn1_convert_integer (value, value_temp, ++ SIZEOF_UNSIGNED_LONG_INT, &len); ++ } ++ else ++ { /* is an identifier like v1 */ ++ if (!(node->type & CONST_LIST)) ++ return ASN1_VALUE_NOT_VALID; ++ p = node->down; ++ while (p) ++ { ++ if (type_field (p->type) == ASN1_ETYPE_CONSTANT) ++ { ++ if (!_asn1_strcmp (p->name, value)) ++ { ++ value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); ++ if (value_temp == NULL) ++ return ASN1_MEM_ALLOC_ERROR; ++ ++ _asn1_convert_integer (p->value, ++ value_temp, ++ SIZEOF_UNSIGNED_LONG_INT, ++ &len); ++ break; ++ } ++ } ++ p = p->right; ++ } ++ if (p == NULL) ++ return ASN1_VALUE_NOT_VALID; ++ } ++ } ++ else ++ { /* len != 0 */ ++ value_temp = malloc (len); ++ if (value_temp == NULL) ++ return ASN1_MEM_ALLOC_ERROR; ++ memcpy (value_temp, value, len); ++ } ++ ++ if (value_temp[0] & 0x80) ++ negative = 1; ++ else ++ negative = 0; ++ ++ if (negative && (type_field (node->type) == ASN1_ETYPE_ENUMERATED)) ++ { ++ free (value_temp); ++ return ASN1_VALUE_NOT_VALID; ++ } ++ ++ for (k = 0; k < len - 1; k++) ++ if (negative && (value_temp[k] != 0xFF)) ++ break; ++ else if (!negative && value_temp[k]) ++ break; ++ ++ if ((negative && !(value_temp[k] & 0x80)) || ++ (!negative && (value_temp[k] & 0x80))) ++ k--; ++ ++ _asn1_set_value_lv (node, value_temp + k, len - k); ++ ++ if (node->type & CONST_DEFAULT) ++ { ++ p = node->down; ++ while (type_field (p->type) != ASN1_ETYPE_DEFAULT) ++ p = p->right; ++ if ((c_isdigit (p->value[0])) || (p->value[0] == '-')) ++ { ++ default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); ++ if (default_temp == NULL) ++ { ++ free (value_temp); ++ return ASN1_MEM_ALLOC_ERROR; ++ } ++ ++ _asn1_convert_integer (p->value, default_temp, ++ SIZEOF_UNSIGNED_LONG_INT, &len2); ++ } ++ else ++ { /* is an identifier like v1 */ ++ if (!(node->type & CONST_LIST)) ++ { ++ free (value_temp); ++ return ASN1_VALUE_NOT_VALID; ++ } ++ p2 = node->down; ++ while (p2) ++ { ++ if (type_field (p2->type) == ASN1_ETYPE_CONSTANT) ++ { ++ if (!_asn1_strcmp (p2->name, p->value)) ++ { ++ default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); ++ if (default_temp == NULL) ++ { ++ free (value_temp); ++ return ASN1_MEM_ALLOC_ERROR; ++ } ++ ++ _asn1_convert_integer (p2->value, ++ default_temp, ++ SIZEOF_UNSIGNED_LONG_INT, ++ &len2); ++ break; ++ } ++ } ++ p2 = p2->right; ++ } ++ if (p2 == NULL) ++ { ++ free (value_temp); ++ return ASN1_VALUE_NOT_VALID; ++ } ++ } ++ ++ ++ if ((len - k) == len2) ++ { ++ for (k2 = 0; k2 < len2; k2++) ++ if (value_temp[k + k2] != default_temp[k2]) ++ { ++ break; ++ } ++ if (k2 == len2) ++ _asn1_set_value (node, NULL, 0); ++ } ++ free (default_temp); ++ } ++ free (value_temp); ++ break; ++ case ASN1_ETYPE_OBJECT_ID: ++ for (i = 0; i < _asn1_strlen (value); i++) ++ if ((!c_isdigit (value[i])) && (value[i] != '.') && (value[i] != '+')) ++ return ASN1_VALUE_NOT_VALID; ++ if (node->type & CONST_DEFAULT) ++ { ++ p = node->down; ++ while (type_field (p->type) != ASN1_ETYPE_DEFAULT) ++ p = p->right; ++ if (!_asn1_strcmp (value, p->value)) ++ { ++ _asn1_set_value (node, NULL, 0); ++ break; ++ } ++ } ++ _asn1_set_value (node, value, _asn1_strlen (value) + 1); ++ break; ++ case ASN1_ETYPE_UTC_TIME: ++ { ++ len = _asn1_strlen (value); ++ if (len < 11) ++ return ASN1_VALUE_NOT_VALID; ++ for (k = 0; k < 10; k++) ++ if (!c_isdigit (value[k])) ++ return ASN1_VALUE_NOT_VALID; ++ switch (len) ++ { ++ case 11: ++ if (value[10] != 'Z') ++ return ASN1_VALUE_NOT_VALID; ++ break; ++ case 13: ++ if ((!c_isdigit (value[10])) || (!c_isdigit (value[11])) || ++ (value[12] != 'Z')) ++ return ASN1_VALUE_NOT_VALID; ++ break; ++ case 15: ++ if ((value[10] != '+') && (value[10] != '-')) ++ return ASN1_VALUE_NOT_VALID; ++ for (k = 11; k < 15; k++) ++ if (!c_isdigit (value[k])) ++ return ASN1_VALUE_NOT_VALID; ++ break; ++ case 17: ++ if ((!c_isdigit (value[10])) || (!c_isdigit (value[11]))) ++ return ASN1_VALUE_NOT_VALID; ++ if ((value[12] != '+') && (value[12] != '-')) ++ return ASN1_VALUE_NOT_VALID; ++ for (k = 13; k < 17; k++) ++ if (!c_isdigit (value[k])) ++ return ASN1_VALUE_NOT_VALID; ++ break; ++ default: ++ return ASN1_VALUE_NOT_FOUND; ++ } ++ _asn1_set_value (node, value, len); ++ } ++ break; ++ case ASN1_ETYPE_GENERALIZED_TIME: ++ len = _asn1_strlen (value); ++ _asn1_set_value (node, value, len); ++ break; ++ case ASN1_ETYPE_OCTET_STRING: ++ case ASN1_ETYPE_GENERALSTRING: ++ case ASN1_ETYPE_NUMERIC_STRING: ++ case ASN1_ETYPE_IA5_STRING: ++ case ASN1_ETYPE_TELETEX_STRING: ++ case ASN1_ETYPE_PRINTABLE_STRING: ++ case ASN1_ETYPE_UNIVERSAL_STRING: ++ case ASN1_ETYPE_BMP_STRING: ++ case ASN1_ETYPE_UTF8_STRING: ++ case ASN1_ETYPE_VISIBLE_STRING: ++ if (len == 0) ++ len = _asn1_strlen (value); ++ _asn1_set_value_lv (node, value, len); ++ break; ++ case ASN1_ETYPE_BIT_STRING: ++ if (len == 0) ++ len = _asn1_strlen (value); ++ asn1_length_der ((len >> 3) + 2, NULL, &len2); ++ temp = malloc ((len >> 3) + 2 + len2); ++ if (temp == NULL) ++ return ASN1_MEM_ALLOC_ERROR; ++ ++ asn1_bit_der (value, len, temp, &len2); ++ _asn1_set_value_m (node, temp, len2); ++ temp = NULL; ++ break; ++ case ASN1_ETYPE_CHOICE: ++ p = node->down; ++ while (p) ++ { ++ if (!_asn1_strcmp (p->name, value)) ++ { ++ p2 = node->down; ++ while (p2) ++ { ++ if (p2 != p) ++ { ++ asn1_delete_structure (&p2); ++ p2 = node->down; ++ } ++ else ++ p2 = p2->right; ++ } ++ break; ++ } ++ p = p->right; ++ } ++ if (!p) ++ return ASN1_ELEMENT_NOT_FOUND; ++ break; ++ case ASN1_ETYPE_ANY: ++ _asn1_set_value_lv (node, value, len); ++ break; ++ case ASN1_ETYPE_SEQUENCE_OF: ++ case ASN1_ETYPE_SET_OF: ++ if (_asn1_strcmp (value, "NEW")) ++ return ASN1_VALUE_NOT_VALID; ++ _asn1_append_sequence_set (node, NULL); ++ break; ++ default: ++ return ASN1_ELEMENT_NOT_FOUND; ++ break; ++ } ++ ++ return ASN1_SUCCESS; ++} ++ ++ ++#define PUT_VALUE( ptr, ptr_size, data, data_size) \ ++ *len = data_size; \ ++ if (ptr_size < data_size) { \ ++ return ASN1_MEM_ERROR; \ ++ } else { \ ++ if (ptr && data_size > 0) \ ++ memcpy (ptr, data, data_size); \ ++ } ++ ++#define PUT_STR_VALUE( ptr, ptr_size, data) \ ++ *len = _asn1_strlen (data) + 1; \ ++ if (ptr_size < *len) { \ ++ return ASN1_MEM_ERROR; \ ++ } else { \ ++ /* this strcpy is checked */ \ ++ if (ptr) { \ ++ _asn1_strcpy (ptr, data); \ ++ } \ ++ } ++ ++#define PUT_AS_STR_VALUE( ptr, ptr_size, data, data_size) \ ++ *len = data_size + 1; \ ++ if (ptr_size < *len) { \ ++ return ASN1_MEM_ERROR; \ ++ } else { \ ++ /* this strcpy is checked */ \ ++ if (ptr) { \ ++ if (data_size > 0) \ ++ memcpy (ptr, data, data_size); \ ++ ptr[data_size] = 0; \ ++ } \ ++ } ++ ++#define ADD_STR_VALUE( ptr, ptr_size, data) \ ++ *len += _asn1_strlen(data); \ ++ if (ptr_size < (int) *len) { \ ++ (*len)++; \ ++ return ASN1_MEM_ERROR; \ ++ } else { \ ++ /* this strcat is checked */ \ ++ if (ptr) _asn1_strcat (ptr, data); \ ++ } ++ ++/** ++ * asn1_read_value: ++ * @root: pointer to a structure. ++ * @name: the name of the element inside a structure that you want to read. ++ * @ivalue: vector that will contain the element's content, must be a ++ * pointer to memory cells already allocated (may be %NULL). ++ * @len: number of bytes of *value: value[0]..value[len-1]. Initialy ++ * holds the sizeof value. ++ * ++ * Returns the value of one element inside a structure. ++ * If an element is OPTIONAL and this returns ++ * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present ++ * in the der encoding that created the structure. The first element ++ * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and ++ * so on. If the @root provided is a node to specific sequence element, ++ * then the keyword "?CURRENT" is also acceptable and indicates the ++ * current sequence element of this node. ++ * ++ * Note that there can be valid values with length zero. In these case ++ * this function will succeed and @len will be zero. ++ * ++ * INTEGER: VALUE will contain a two's complement form integer. ++ * ++ * integer=-1 -> value[0]=0xFF , len=1. ++ * integer=1 -> value[0]=0x01 , len=1. ++ * ++ * ENUMERATED: As INTEGER (but only with not negative numbers). ++ * ++ * BOOLEAN: VALUE will be the null terminated string "TRUE" or ++ * "FALSE" and LEN=5 or LEN=6. ++ * ++ * OBJECT IDENTIFIER: VALUE will be a null terminated string with ++ * each number separated by a dot (i.e. "1.2.3.543.1"). ++ * ++ * LEN = strlen(VALUE)+1 ++ * ++ * UTCTime: VALUE will be a null terminated string in one of these ++ * formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'". ++ * LEN=strlen(VALUE)+1. ++ * ++ * GeneralizedTime: VALUE will be a null terminated string in the ++ * same format used to set the value. ++ * ++ * OCTET STRING: VALUE will contain the octet string and LEN will be ++ * the number of octets. ++ * ++ * GeneralString: VALUE will contain the generalstring and LEN will ++ * be the number of octets. ++ * ++ * BIT STRING: VALUE will contain the bit string organized by bytes ++ * and LEN will be the number of bits. ++ * ++ * CHOICE: If NAME indicates a choice type, VALUE will specify the ++ * alternative selected. ++ * ++ * ANY: If NAME indicates an any type, VALUE will indicate the DER ++ * encoding of the structure actually used. ++ * ++ * Returns: %ASN1_SUCCESS if value is returned, ++ * %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, ++ * %ASN1_VALUE_NOT_FOUND if there isn't any value for the element ++ * selected, and %ASN1_MEM_ERROR if The value vector isn't big enough ++ * to store the result, and in this case @len will contain the number of ++ * bytes needed. On the occasion that the stored data are of zero-length ++ * this function may return %ASN1_SUCCESS even if the provided @len is zero. ++ **/ ++int ++asn1_read_value (asn1_node_const root, const char *name, void *ivalue, ++ int *len) ++{ ++ return asn1_read_value_type (root, name, ivalue, len, NULL); ++} ++ ++/** ++ * asn1_read_value_type: ++ * @root: pointer to a structure. ++ * @name: the name of the element inside a structure that you want to read. ++ * @ivalue: vector that will contain the element's content, must be a ++ * pointer to memory cells already allocated (may be %NULL). ++ * @len: number of bytes of *value: value[0]..value[len-1]. Initialy ++ * holds the sizeof value. ++ * @etype: The type of the value read (ASN1_ETYPE) ++ * ++ * Returns the type and value of one element inside a structure. ++ * If an element is OPTIONAL and this returns ++ * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present ++ * in the der encoding that created the structure. The first element ++ * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and ++ * so on. If the @root provided is a node to specific sequence element, ++ * then the keyword "?CURRENT" is also acceptable and indicates the ++ * current sequence element of this node. ++ * ++ * Note that there can be valid values with length zero. In these case ++ * this function will succeed and @len will be zero. ++ * ++ * ++ * INTEGER: VALUE will contain a two's complement form integer. ++ * ++ * integer=-1 -> value[0]=0xFF , len=1. ++ * integer=1 -> value[0]=0x01 , len=1. ++ * ++ * ENUMERATED: As INTEGER (but only with not negative numbers). ++ * ++ * BOOLEAN: VALUE will be the null terminated string "TRUE" or ++ * "FALSE" and LEN=5 or LEN=6. ++ * ++ * OBJECT IDENTIFIER: VALUE will be a null terminated string with ++ * each number separated by a dot (i.e. "1.2.3.543.1"). ++ * ++ * LEN = strlen(VALUE)+1 ++ * ++ * UTCTime: VALUE will be a null terminated string in one of these ++ * formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'". ++ * LEN=strlen(VALUE)+1. ++ * ++ * GeneralizedTime: VALUE will be a null terminated string in the ++ * same format used to set the value. ++ * ++ * OCTET STRING: VALUE will contain the octet string and LEN will be ++ * the number of octets. ++ * ++ * GeneralString: VALUE will contain the generalstring and LEN will ++ * be the number of octets. ++ * ++ * BIT STRING: VALUE will contain the bit string organized by bytes ++ * and LEN will be the number of bits. ++ * ++ * CHOICE: If NAME indicates a choice type, VALUE will specify the ++ * alternative selected. ++ * ++ * ANY: If NAME indicates an any type, VALUE will indicate the DER ++ * encoding of the structure actually used. ++ * ++ * Returns: %ASN1_SUCCESS if value is returned, ++ * %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, ++ * %ASN1_VALUE_NOT_FOUND if there isn't any value for the element ++ * selected, and %ASN1_MEM_ERROR if The value vector isn't big enough ++ * to store the result, and in this case @len will contain the number of ++ * bytes needed. On the occasion that the stored data are of zero-length ++ * this function may return %ASN1_SUCCESS even if the provided @len is zero. ++ **/ ++int ++asn1_read_value_type (asn1_node_const root, const char *name, void *ivalue, ++ int *len, unsigned int *etype) ++{ ++ asn1_node_const node, p, p2; ++ int len2, len3, result; ++ int value_size = *len; ++ unsigned char *value = ivalue; ++ unsigned type; ++ ++ node = asn1_find_node (root, name); ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ type = type_field (node->type); ++ ++ if ((type != ASN1_ETYPE_NULL) && ++ (type != ASN1_ETYPE_CHOICE) && ++ !(node->type & CONST_DEFAULT) && !(node->type & CONST_ASSIGN) && ++ (node->value == NULL)) ++ return ASN1_VALUE_NOT_FOUND; ++ ++ if (etype) ++ *etype = type; ++ switch (type) ++ { ++ case ASN1_ETYPE_NULL: ++ PUT_STR_VALUE (value, value_size, "NULL"); ++ break; ++ case ASN1_ETYPE_BOOLEAN: ++ if ((node->type & CONST_DEFAULT) && (node->value == NULL)) ++ { ++ p = node->down; ++ while (type_field (p->type) != ASN1_ETYPE_DEFAULT) ++ p = p->right; ++ if (p->type & CONST_TRUE) ++ { ++ PUT_STR_VALUE (value, value_size, "TRUE"); ++ } ++ else ++ { ++ PUT_STR_VALUE (value, value_size, "FALSE"); ++ } ++ } ++ else if (node->value[0] == 'T') ++ { ++ PUT_STR_VALUE (value, value_size, "TRUE"); ++ } ++ else ++ { ++ PUT_STR_VALUE (value, value_size, "FALSE"); ++ } ++ break; ++ case ASN1_ETYPE_INTEGER: ++ case ASN1_ETYPE_ENUMERATED: ++ if ((node->type & CONST_DEFAULT) && (node->value == NULL)) ++ { ++ p = node->down; ++ while (type_field (p->type) != ASN1_ETYPE_DEFAULT) ++ p = p->right; ++ if ((c_isdigit (p->value[0])) || (p->value[0] == '-') ++ || (p->value[0] == '+')) ++ { ++ result = _asn1_convert_integer ++ (p->value, value, value_size, len); ++ if (result != ASN1_SUCCESS) ++ return result; ++ } ++ else ++ { /* is an identifier like v1 */ ++ p2 = node->down; ++ while (p2) ++ { ++ if (type_field (p2->type) == ASN1_ETYPE_CONSTANT) ++ { ++ if (!_asn1_strcmp (p2->name, p->value)) ++ { ++ result = _asn1_convert_integer ++ (p2->value, value, value_size, len); ++ if (result != ASN1_SUCCESS) ++ return result; ++ break; ++ } ++ } ++ p2 = p2->right; ++ } ++ } ++ } ++ else ++ { ++ len2 = -1; ++ result = asn1_get_octet_der ++ (node->value, node->value_len, &len2, value, value_size, len); ++ if (result != ASN1_SUCCESS) ++ return result; ++ } ++ break; ++ case ASN1_ETYPE_OBJECT_ID: ++ if (node->type & CONST_ASSIGN) ++ { ++ *len = 0; ++ if (value) ++ value[0] = 0; ++ p = node->down; ++ while (p) ++ { ++ if (type_field (p->type) == ASN1_ETYPE_CONSTANT) ++ { ++ ADD_STR_VALUE (value, value_size, p->value); ++ if (p->right) ++ { ++ ADD_STR_VALUE (value, value_size, "."); ++ } ++ } ++ p = p->right; ++ } ++ (*len)++; ++ } ++ else if ((node->type & CONST_DEFAULT) && (node->value == NULL)) ++ { ++ p = node->down; ++ while (type_field (p->type) != ASN1_ETYPE_DEFAULT) ++ p = p->right; ++ PUT_STR_VALUE (value, value_size, p->value); ++ } ++ else ++ { ++ PUT_STR_VALUE (value, value_size, node->value); ++ } ++ break; ++ case ASN1_ETYPE_GENERALIZED_TIME: ++ case ASN1_ETYPE_UTC_TIME: ++ PUT_AS_STR_VALUE (value, value_size, node->value, node->value_len); ++ break; ++ case ASN1_ETYPE_OCTET_STRING: ++ case ASN1_ETYPE_GENERALSTRING: ++ case ASN1_ETYPE_NUMERIC_STRING: ++ case ASN1_ETYPE_IA5_STRING: ++ case ASN1_ETYPE_TELETEX_STRING: ++ case ASN1_ETYPE_PRINTABLE_STRING: ++ case ASN1_ETYPE_UNIVERSAL_STRING: ++ case ASN1_ETYPE_BMP_STRING: ++ case ASN1_ETYPE_UTF8_STRING: ++ case ASN1_ETYPE_VISIBLE_STRING: ++ len2 = -1; ++ result = asn1_get_octet_der ++ (node->value, node->value_len, &len2, value, value_size, len); ++ if (result != ASN1_SUCCESS) ++ return result; ++ break; ++ case ASN1_ETYPE_BIT_STRING: ++ len2 = -1; ++ result = asn1_get_bit_der ++ (node->value, node->value_len, &len2, value, value_size, len); ++ if (result != ASN1_SUCCESS) ++ return result; ++ break; ++ case ASN1_ETYPE_CHOICE: ++ PUT_STR_VALUE (value, value_size, node->down->name); ++ break; ++ case ASN1_ETYPE_ANY: ++ len3 = -1; ++ len2 = asn1_get_length_der (node->value, node->value_len, &len3); ++ if (len2 < 0) ++ return ASN1_DER_ERROR; ++ PUT_VALUE (value, value_size, node->value + len3, len2); ++ break; ++ default: ++ return ASN1_ELEMENT_NOT_FOUND; ++ break; ++ } ++ return ASN1_SUCCESS; ++} ++ ++ ++/** ++ * asn1_read_tag: ++ * @root: pointer to a structure ++ * @name: the name of the element inside a structure. ++ * @tagValue: variable that will contain the TAG value. ++ * @classValue: variable that will specify the TAG type. ++ * ++ * Returns the TAG and the CLASS of one element inside a structure. ++ * CLASS can have one of these constants: %ASN1_CLASS_APPLICATION, ++ * %ASN1_CLASS_UNIVERSAL, %ASN1_CLASS_PRIVATE or ++ * %ASN1_CLASS_CONTEXT_SPECIFIC. ++ * ++ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if ++ * @name is not a valid element. ++ **/ ++int ++asn1_read_tag (asn1_node_const root, const char *name, int *tagValue, ++ int *classValue) ++{ ++ asn1_node node, p, pTag; ++ ++ node = asn1_find_node (root, name); ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ p = node->down; ++ ++ /* pTag will points to the IMPLICIT TAG */ ++ pTag = NULL; ++ if (node->type & CONST_TAG) ++ { ++ while (p) ++ { ++ if (type_field (p->type) == ASN1_ETYPE_TAG) ++ { ++ if ((p->type & CONST_IMPLICIT) && (pTag == NULL)) ++ pTag = p; ++ else if (p->type & CONST_EXPLICIT) ++ pTag = NULL; ++ } ++ p = p->right; ++ } ++ } ++ ++ if (pTag) ++ { ++ *tagValue = _asn1_strtoul (pTag->value, NULL, 10); ++ ++ if (pTag->type & CONST_APPLICATION) ++ *classValue = ASN1_CLASS_APPLICATION; ++ else if (pTag->type & CONST_UNIVERSAL) ++ *classValue = ASN1_CLASS_UNIVERSAL; ++ else if (pTag->type & CONST_PRIVATE) ++ *classValue = ASN1_CLASS_PRIVATE; ++ else ++ *classValue = ASN1_CLASS_CONTEXT_SPECIFIC; ++ } ++ else ++ { ++ unsigned type = type_field (node->type); ++ *classValue = ASN1_CLASS_UNIVERSAL; ++ ++ switch (type) ++ { ++ CASE_HANDLED_ETYPES: ++ *tagValue = _asn1_tags[type].tag; ++ break; ++ case ASN1_ETYPE_TAG: ++ case ASN1_ETYPE_CHOICE: ++ case ASN1_ETYPE_ANY: ++ *tagValue = -1; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return ASN1_SUCCESS; ++} ++ ++/** ++ * asn1_read_node_value: ++ * @node: pointer to a node. ++ * @data: a point to a asn1_data_node_st ++ * ++ * Returns the value a data node inside a asn1_node structure. ++ * The data returned should be handled as constant values. ++ * ++ * Returns: %ASN1_SUCCESS if the node exists. ++ **/ ++int ++asn1_read_node_value (asn1_node_const node, asn1_data_node_st * data) ++{ ++ data->name = node->name; ++ data->value = node->value; ++ data->value_len = node->value_len; ++ data->type = type_field (node->type); ++ ++ return ASN1_SUCCESS; ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1/lib/element.h +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (C) 2000-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * The LIBTASN1 library is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ */ ++ ++#ifndef _ELEMENT_H ++# define _ELEMENT_H ++ ++ ++struct node_tail_cache_st ++{ ++ asn1_node head; /* the first element of the sequence */ ++ asn1_node tail; ++}; ++ ++int _asn1_append_sequence_set (asn1_node node, ++ struct node_tail_cache_st *pcached); ++ ++int _asn1_convert_integer (const unsigned char *value, ++ unsigned char *value_out, ++ int value_out_size, int *len); ++ ++void _asn1_hierarchical_name (asn1_node_const node, char *name, ++ int name_size); ++ ++#endif +--- /dev/null ++++ b/grub-core/lib/libtasn1/lib/errors.c +@@ -0,0 +1,100 @@ ++/* ++ * Copyright (C) 2002-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * The LIBTASN1 library is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ */ ++ ++#include ++#ifdef STDC_HEADERS ++# include ++#endif ++ ++#define LIBTASN1_ERROR_ENTRY(name) { #name, name } ++ ++struct libtasn1_error_entry ++{ ++ const char *name; ++ int number; ++}; ++typedef struct libtasn1_error_entry libtasn1_error_entry; ++ ++static const libtasn1_error_entry error_algorithms[] = { ++ LIBTASN1_ERROR_ENTRY (ASN1_SUCCESS), ++ LIBTASN1_ERROR_ENTRY (ASN1_FILE_NOT_FOUND), ++ LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_FOUND), ++ LIBTASN1_ERROR_ENTRY (ASN1_IDENTIFIER_NOT_FOUND), ++ LIBTASN1_ERROR_ENTRY (ASN1_DER_ERROR), ++ LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_FOUND), ++ LIBTASN1_ERROR_ENTRY (ASN1_GENERIC_ERROR), ++ LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_VALID), ++ LIBTASN1_ERROR_ENTRY (ASN1_TAG_ERROR), ++ LIBTASN1_ERROR_ENTRY (ASN1_TAG_IMPLICIT), ++ LIBTASN1_ERROR_ENTRY (ASN1_ERROR_TYPE_ANY), ++ LIBTASN1_ERROR_ENTRY (ASN1_SYNTAX_ERROR), ++ LIBTASN1_ERROR_ENTRY (ASN1_MEM_ERROR), ++ LIBTASN1_ERROR_ENTRY (ASN1_MEM_ALLOC_ERROR), ++ LIBTASN1_ERROR_ENTRY (ASN1_DER_OVERFLOW), ++ LIBTASN1_ERROR_ENTRY (ASN1_NAME_TOO_LONG), ++ LIBTASN1_ERROR_ENTRY (ASN1_ARRAY_ERROR), ++ LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_EMPTY), ++ LIBTASN1_ERROR_ENTRY (ASN1_TIME_ENCODING_ERROR), ++ LIBTASN1_ERROR_ENTRY (ASN1_RECURSION), ++ {0, 0} ++}; ++ ++/** ++ * asn1_perror: ++ * @error: is an error returned by a libtasn1 function. ++ * ++ * Prints a string to stderr with a description of an error. This ++ * function is like perror(). The only difference is that it accepts ++ * an error returned by a libtasn1 function. ++ * ++ * Since: 1.6 ++ **/ ++void ++asn1_perror (int error) ++{ ++ const char *str = asn1_strerror (error); ++ fprintf (stderr, "LIBTASN1 ERROR: %s\n", str ? str : "(null)"); ++} ++ ++/** ++ * asn1_strerror: ++ * @error: is an error returned by a libtasn1 function. ++ * ++ * Returns a string with a description of an error. This function is ++ * similar to strerror. The only difference is that it accepts an ++ * error (number) returned by a libtasn1 function. ++ * ++ * Returns: Pointer to static zero-terminated string describing error ++ * code. ++ * ++ * Since: 1.6 ++ **/ ++const char * ++asn1_strerror (int error) ++{ ++ const libtasn1_error_entry *p; ++ ++ for (p = error_algorithms; p->name != NULL; p++) ++ if (p->number == error) ++ return p->name + sizeof ("ASN1_") - 1; ++ ++ return NULL; ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1/lib/gstr.c +@@ -0,0 +1,74 @@ ++/* ++ * Copyright (C) 2002-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * The LIBTASN1 library is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ */ ++ ++#include ++#include "gstr.h" ++ ++/* These function are like strcat, strcpy. They only ++ * do bounds checking (they shouldn't cause buffer overruns), ++ * and they always produce null terminated strings. ++ * ++ * They should be used only with null terminated strings. ++ */ ++void ++_asn1_str_cat (char *dest, size_t dest_tot_size, const char *src) ++{ ++ size_t str_size = strlen (src); ++ size_t dest_size = strlen (dest); ++ ++ if (dest_tot_size - dest_size > str_size) ++ { ++ strcat (dest, src); ++ } ++ else ++ { ++ if (dest_tot_size > dest_size) ++ { ++ strncat (dest, src, (dest_tot_size - dest_size) - 1); ++ dest[dest_tot_size - 1] = 0; ++ } ++ } ++} ++ ++/* Returns the bytes copied (not including the null terminator) */ ++unsigned int ++_asn1_str_cpy (char *dest, size_t dest_tot_size, const char *src) ++{ ++ size_t str_size = strlen (src); ++ ++ if (dest_tot_size > str_size) ++ { ++ strcpy (dest, src); ++ return str_size; ++ } ++ else ++ { ++ if (dest_tot_size > 0) ++ { ++ str_size = dest_tot_size - 1; ++ memcpy (dest, src, str_size); ++ dest[str_size] = 0; ++ return str_size; ++ } ++ else ++ return 0; ++ } ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1/lib/gstr.h +@@ -0,0 +1,50 @@ ++/* ++ * Copyright (C) 2002-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * The LIBTASN1 library is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ */ ++ ++#ifndef GSTR_H ++# define GSTR_H ++ ++unsigned int _asn1_str_cpy (char *dest, size_t dest_tot_size, ++ const char *src); ++void _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src); ++ ++# define Estrcpy(x,y) _asn1_str_cpy(x,ASN1_MAX_ERROR_DESCRIPTION_SIZE,y) ++# define Estrcat(x,y) _asn1_str_cat(x,ASN1_MAX_ERROR_DESCRIPTION_SIZE,y) ++ ++inline static void ++safe_memset (void *data, int c, size_t size) ++{ ++ volatile unsigned volatile_zero = 0; ++ volatile char *vdata = (volatile char *) data; ++ ++ /* This is based on a nice trick for safe memset, ++ * sent by David Jacobson in the openssl-dev mailing list. ++ */ ++ ++ if (size > 0) ++ do ++ { ++ memset (data, c, size); ++ } ++ while (vdata[volatile_zero] != c); ++} ++ ++#endif /* GSTR_H */ +--- /dev/null ++++ b/grub-core/lib/libtasn1/lib/int.h +@@ -0,0 +1,221 @@ ++/* ++ * Copyright (C) 2002-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * The LIBTASN1 library is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ */ ++ ++#ifndef INT_H ++# define INT_H ++ ++# ifdef HAVE_CONFIG_H ++# include ++# endif ++ ++# include ++# include ++# include ++# include ++ ++# ifdef HAVE_SYS_TYPES_H ++# include ++# endif ++ ++# include ++ ++# define ASN1_SMALL_VALUE_SIZE 16 ++ ++/* This structure is also in libtasn1.h, but then contains less ++ fields. You cannot make any modifications to these first fields ++ without breaking ABI. */ ++struct asn1_node_st ++{ ++ /* public fields: */ ++ char name[ASN1_MAX_NAME_SIZE + 1]; /* Node name */ ++ unsigned int name_hash; ++ unsigned int type; /* Node type */ ++ unsigned char *value; /* Node value */ ++ int value_len; ++ asn1_node down; /* Pointer to the son node */ ++ asn1_node right; /* Pointer to the brother node */ ++ asn1_node left; /* Pointer to the next list element */ ++ /* private fields: */ ++ unsigned char small_value[ASN1_SMALL_VALUE_SIZE]; /* For small values */ ++ ++ /* values used during decoding/coding */ ++ int tmp_ival; ++ unsigned start; /* the start of the DER sequence - if decoded */ ++ unsigned end; /* the end of the DER sequence - if decoded */ ++}; ++ ++typedef struct tag_and_class_st ++{ ++ unsigned tag; ++ unsigned class; ++ const char *desc; ++} tag_and_class_st; ++ ++/* the types that are handled in _asn1_tags */ ++# define CASE_HANDLED_ETYPES \ ++ case ASN1_ETYPE_NULL: \ ++ case ASN1_ETYPE_BOOLEAN: \ ++ case ASN1_ETYPE_INTEGER: \ ++ case ASN1_ETYPE_ENUMERATED: \ ++ case ASN1_ETYPE_OBJECT_ID: \ ++ case ASN1_ETYPE_OCTET_STRING: \ ++ case ASN1_ETYPE_GENERALSTRING: \ ++ case ASN1_ETYPE_NUMERIC_STRING: \ ++ case ASN1_ETYPE_IA5_STRING: \ ++ case ASN1_ETYPE_TELETEX_STRING: \ ++ case ASN1_ETYPE_PRINTABLE_STRING: \ ++ case ASN1_ETYPE_UNIVERSAL_STRING: \ ++ case ASN1_ETYPE_BMP_STRING: \ ++ case ASN1_ETYPE_UTF8_STRING: \ ++ case ASN1_ETYPE_VISIBLE_STRING: \ ++ case ASN1_ETYPE_BIT_STRING: \ ++ case ASN1_ETYPE_SEQUENCE: \ ++ case ASN1_ETYPE_SEQUENCE_OF: \ ++ case ASN1_ETYPE_SET: \ ++ case ASN1_ETYPE_UTC_TIME: \ ++ case ASN1_ETYPE_GENERALIZED_TIME: \ ++ case ASN1_ETYPE_SET_OF ++ ++# define ETYPE_TAG(etype) (_asn1_tags[etype].tag) ++# define ETYPE_CLASS(etype) (_asn1_tags[etype].class) ++# define ETYPE_OK(etype) (((etype) != ASN1_ETYPE_INVALID && \ ++ (etype) <= _asn1_tags_size && \ ++ _asn1_tags[(etype)].desc != NULL)?1:0) ++ ++# define ETYPE_IS_STRING(etype) ((etype == ASN1_ETYPE_GENERALSTRING || \ ++ etype == ASN1_ETYPE_NUMERIC_STRING || etype == ASN1_ETYPE_IA5_STRING || \ ++ etype == ASN1_ETYPE_TELETEX_STRING || etype == ASN1_ETYPE_PRINTABLE_STRING || \ ++ etype == ASN1_ETYPE_UNIVERSAL_STRING || etype == ASN1_ETYPE_BMP_STRING || \ ++ etype == ASN1_ETYPE_UTF8_STRING || etype == ASN1_ETYPE_VISIBLE_STRING || \ ++ etype == ASN1_ETYPE_OCTET_STRING)?1:0) ++ ++extern unsigned int _asn1_tags_size; ++extern const tag_and_class_st _asn1_tags[]; ++ ++# define _asn1_strlen(s) strlen((const char *) s) ++# define _asn1_strtol(n,e,b) strtol((const char *) n, e, b) ++# define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b) ++# define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b) ++# define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b) ++# define _asn1_strcat(a,b) strcat((char *)a, (const char *)b) ++ ++# if SIZEOF_UNSIGNED_LONG_INT == 8 ++# define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b) ++# else ++# define _asn1_strtou64(n,e,b) strtoull((const char *) n, e, b) ++# endif ++ ++# define MAX_LOG_SIZE 1024 /* maximum number of characters of a log message */ ++ ++/* Define used for visiting trees. */ ++# define UP 1 ++# define RIGHT 2 ++# define DOWN 3 ++ ++/***********************************************************************/ ++/* List of constants to better specify the type of typedef asn1_node_st. */ ++/***********************************************************************/ ++/* Used with TYPE_TAG */ ++# define CONST_UNIVERSAL (1U<<8) ++# define CONST_PRIVATE (1U<<9) ++# define CONST_APPLICATION (1U<<10) ++# define CONST_EXPLICIT (1U<<11) ++# define CONST_IMPLICIT (1U<<12) ++ ++# define CONST_TAG (1U<<13) /* Used in ASN.1 assignement */ ++# define CONST_OPTION (1U<<14) ++# define CONST_DEFAULT (1U<<15) ++# define CONST_TRUE (1U<<16) ++# define CONST_FALSE (1U<<17) ++ ++# define CONST_LIST (1U<<18) /* Used with TYPE_INTEGER and TYPE_BIT_STRING */ ++# define CONST_MIN_MAX (1U<<19) ++ ++# define CONST_1_PARAM (1U<<20) ++ ++# define CONST_SIZE (1U<<21) ++ ++# define CONST_DEFINED_BY (1U<<22) ++ ++/* Those two are deprecated and used for backwards compatibility */ ++# define CONST_GENERALIZED (1U<<23) ++# define CONST_UTC (1U<<24) ++ ++/* #define CONST_IMPORTS (1U<<25) */ ++ ++# define CONST_NOT_USED (1U<<26) ++# define CONST_SET (1U<<27) ++# define CONST_ASSIGN (1U<<28) ++ ++# define CONST_DOWN (1U<<29) ++# define CONST_RIGHT (1U<<30) ++ ++ ++# define ASN1_ETYPE_TIME 17 ++/****************************************/ ++/* Returns the first 8 bits. */ ++/* Used with the field type of asn1_node_st */ ++/****************************************/ ++inline static unsigned int ++type_field (unsigned int ntype) ++{ ++ return (ntype & 0xff); ++} ++ ++/* To convert old types from a static structure */ ++inline static unsigned int ++convert_old_type (unsigned int ntype) ++{ ++ unsigned int type = ntype & 0xff; ++ if (type == ASN1_ETYPE_TIME) ++ { ++ if (ntype & CONST_UTC) ++ type = ASN1_ETYPE_UTC_TIME; ++ else ++ type = ASN1_ETYPE_GENERALIZED_TIME; ++ ++ ntype &= ~(CONST_UTC | CONST_GENERALIZED); ++ ntype &= 0xffffff00; ++ ntype |= type; ++ ++ return ntype; ++ } ++ else ++ return ntype; ++} ++ ++static inline void * ++_asn1_realloc (void *ptr, size_t size) ++{ ++ void *ret; ++ ++ if (size == 0) ++ return ptr; ++ ++ ret = realloc (ptr, size); ++ if (ret == NULL) ++ { ++ free (ptr); ++ } ++ return ret; ++} ++ ++#endif /* INT_H */ +--- /dev/null ++++ b/grub-core/lib/libtasn1/lib/parser_aux.c +@@ -0,0 +1,1178 @@ ++/* ++ * Copyright (C) 2000-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * The LIBTASN1 library is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ */ ++ ++#include // WORD_BIT ++ ++#include "int.h" ++#include "parser_aux.h" ++#include "gstr.h" ++#include "structure.h" ++#include "element.h" ++#include "c-ctype.h" ++ ++char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1]; /* identifier name not found */ ++ ++/* Return a hash of the N bytes of X using the method described by ++ Bruno Haible in https://www.haible.de/bruno/hashfunc.html. ++ Note that while many hash functions reduce their result via modulo ++ to a 0..table_size-1 range, this function does not do that. ++ ++ This implementation has been changed from size_t -> unsigned int. */ ++ ++#ifdef __clang__ ++__attribute__((no_sanitize ("integer"))) ++#endif ++ _GL_ATTRIBUTE_PURE static unsigned int _asn1_hash_name (const char *x) ++{ ++ const unsigned char *s = (unsigned char *) x; ++ unsigned h = 0; ++ ++ while (*s) ++ h = (*s++) + ((h << 9) | (h >> (WORD_BIT - 9))); ++ ++ return h; ++} ++ ++/******************************************************/ ++/* Function : _asn1_add_static_node */ ++/* Description: creates a new NODE_ASN element and */ ++/* puts it in the list pointed by e_list. */ ++/* Parameters: */ ++/* e_list: of type list_type; must be NULL initially */ ++/* type: type of the new element (see ASN1_ETYPE_ */ ++/* and CONST_ constants). */ ++/* Return: pointer to the new element. */ ++/******************************************************/ ++asn1_node ++_asn1_add_static_node (list_type ** e_list, unsigned int type) ++{ ++ list_type *p; ++ asn1_node punt; ++ ++ punt = calloc (1, sizeof (struct asn1_node_st)); ++ if (punt == NULL) ++ return NULL; ++ ++ p = malloc (sizeof (list_type)); ++ if (p == NULL) ++ { ++ free (punt); ++ return NULL; ++ } ++ ++ p->node = punt; ++ p->next = *e_list; ++ *e_list = p; ++ ++ punt->type = type; ++ ++ return punt; ++} ++ ++static int ++_asn1_add_static_node2 (list_type ** e_list, asn1_node node) ++{ ++ list_type *p; ++ ++ p = malloc (sizeof (list_type)); ++ if (p == NULL) ++ { ++ return -1; ++ } ++ ++ p->node = node; ++ p->next = *e_list; ++ *e_list = p; ++ ++ return 0; ++} ++ ++/** ++ * asn1_find_node: ++ * @pointer: NODE_ASN element pointer. ++ * @name: null terminated string with the element's name to find. ++ * ++ * Searches for an element called @name starting from @pointer. The ++ * name is composed by different identifiers separated by dots. When ++ * *@pointer has a name, the first identifier must be the name of ++ * *@pointer, otherwise it must be the name of one child of *@pointer. ++ * ++ * Returns: the search result, or %NULL if not found. ++ **/ ++asn1_node ++asn1_find_node (asn1_node_const pointer, const char *name) ++{ ++ asn1_node_const p; ++ char *n_end, n[ASN1_MAX_NAME_SIZE + 1]; ++ const char *n_start; ++ unsigned int nsize; ++ unsigned int nhash; ++ ++ if (pointer == NULL) ++ return NULL; ++ ++ if (name == NULL) ++ return NULL; ++ ++ p = pointer; ++ n_start = name; ++ ++ if (name[0] == '?' && name[1] == 'C' && p->name[0] == '?') ++ { /* ?CURRENT */ ++ n_start = strchr (n_start, '.'); ++ if (n_start) ++ n_start++; ++ } ++ else if (p->name[0] != 0) ++ { /* has *pointer got a name ? */ ++ n_end = strchr (n_start, '.'); /* search the first dot */ ++ if (n_end) ++ { ++ nsize = n_end - n_start; ++ if (nsize >= sizeof (n)) ++ return NULL; ++ ++ memcpy (n, n_start, nsize); ++ n[nsize] = 0; ++ n_start = n_end; ++ n_start++; ++ ++ nhash = _asn1_hash_name (n); ++ } ++ else ++ { ++ _asn1_str_cpy (n, sizeof (n), n_start); ++ nhash = _asn1_hash_name (n); ++ ++ n_start = NULL; ++ } ++ ++ while (p) ++ { ++ if (nhash == p->name_hash && (!strcmp (p->name, n))) ++ break; ++ else ++ p = p->right; ++ } /* while */ ++ ++ if (p == NULL) ++ return NULL; ++ } ++ else ++ { /* *pointer doesn't have a name */ ++ if (n_start[0] == 0) ++ return (asn1_node) p; ++ } ++ ++ while (n_start) ++ { /* Has the end of NAME been reached? */ ++ n_end = strchr (n_start, '.'); /* search the next dot */ ++ if (n_end) ++ { ++ nsize = n_end - n_start; ++ if (nsize >= sizeof (n)) ++ return NULL; ++ ++ memcpy (n, n_start, nsize); ++ n[nsize] = 0; ++ n_start = n_end; ++ n_start++; ++ ++ nhash = _asn1_hash_name (n); ++ } ++ else ++ { ++ _asn1_str_cpy (n, sizeof (n), n_start); ++ nhash = _asn1_hash_name (n); ++ n_start = NULL; ++ } ++ ++ if (p->down == NULL) ++ return NULL; ++ ++ p = p->down; ++ if (p == NULL) ++ return NULL; ++ ++ /* The identifier "?LAST" indicates the last element ++ in the right chain. */ ++ if (n[0] == '?' && n[1] == 'L') /* ?LAST */ ++ { ++ while (p->right) ++ p = p->right; ++ } ++ else ++ { /* no "?LAST" */ ++ while (p) ++ { ++ if (p->name_hash == nhash && !strcmp (p->name, n)) ++ break; ++ else ++ p = p->right; ++ } ++ } ++ if (p == NULL) ++ return NULL; ++ } /* while */ ++ ++ return (asn1_node) p; ++} ++ ++ ++/******************************************************************/ ++/* Function : _asn1_set_value */ ++/* Description: sets the field VALUE in a NODE_ASN element. The */ ++/* previous value (if exist) will be lost */ ++/* Parameters: */ ++/* node: element pointer. */ ++/* value: pointer to the value that you want to set. */ ++/* len: character number of value. */ ++/* Return: pointer to the NODE_ASN element. */ ++/******************************************************************/ ++asn1_node ++_asn1_set_value (asn1_node node, const void *value, unsigned int len) ++{ ++ if (node == NULL) ++ return node; ++ if (node->value) ++ { ++ if (node->value != node->small_value) ++ free (node->value); ++ node->value = NULL; ++ node->value_len = 0; ++ } ++ ++ if (!len) ++ return node; ++ ++ if (len < sizeof (node->small_value)) ++ { ++ node->value = node->small_value; ++ } ++ else ++ { ++ node->value = malloc (len); ++ if (node->value == NULL) ++ return NULL; ++ } ++ node->value_len = len; ++ ++ memcpy (node->value, value, len); ++ return node; ++} ++ ++/******************************************************************/ ++/* Function : _asn1_set_value_lv */ ++/* Description: sets the field VALUE in a NODE_ASN element. The */ ++/* previous value (if exist) will be lost. The value */ ++/* given is stored as an length-value format (LV */ ++/* Parameters: */ ++/* node: element pointer. */ ++/* value: pointer to the value that you want to set. */ ++/* len: character number of value. */ ++/* Return: pointer to the NODE_ASN element. */ ++/******************************************************************/ ++asn1_node ++_asn1_set_value_lv (asn1_node node, const void *value, unsigned int len) ++{ ++ int len2; ++ void *temp; ++ ++ if (node == NULL) ++ return node; ++ ++ asn1_length_der (len, NULL, &len2); ++ temp = malloc (len + len2); ++ if (temp == NULL) ++ return NULL; ++ ++ asn1_octet_der (value, len, temp, &len2); ++ return _asn1_set_value_m (node, temp, len2); ++} ++ ++/* the same as _asn1_set_value except that it sets an already malloc'ed ++ * value. ++ */ ++asn1_node ++_asn1_set_value_m (asn1_node node, void *value, unsigned int len) ++{ ++ if (node == NULL) ++ return node; ++ ++ if (node->value) ++ { ++ if (node->value != node->small_value) ++ free (node->value); ++ node->value = NULL; ++ node->value_len = 0; ++ } ++ ++ if (!len) ++ return node; ++ ++ node->value = value; ++ node->value_len = len; ++ ++ return node; ++} ++ ++/******************************************************************/ ++/* Function : _asn1_append_value */ ++/* Description: appends to the field VALUE in a NODE_ASN element. */ ++/* */ ++/* Parameters: */ ++/* node: element pointer. */ ++/* value: pointer to the value that you want to be appended. */ ++/* len: character number of value. */ ++/* Return: pointer to the NODE_ASN element. */ ++/******************************************************************/ ++asn1_node ++_asn1_append_value (asn1_node node, const void *value, unsigned int len) ++{ ++ if (node == NULL) ++ return node; ++ ++ if (node->value == NULL) ++ return _asn1_set_value (node, value, len); ++ ++ if (len == 0) ++ return node; ++ ++ if (node->value == node->small_value) ++ { ++ /* value is in node */ ++ int prev_len = node->value_len; ++ node->value_len += len; ++ node->value = malloc (node->value_len); ++ if (node->value == NULL) ++ { ++ node->value_len = 0; ++ return NULL; ++ } ++ ++ if (prev_len > 0) ++ memcpy (node->value, node->small_value, prev_len); ++ ++ memcpy (&node->value[prev_len], value, len); ++ ++ return node; ++ } ++ else /* if (node->value != NULL && node->value != node->small_value) */ ++ { ++ /* value is allocated */ ++ int prev_len = node->value_len; ++ node->value_len += len; ++ ++ node->value = _asn1_realloc (node->value, node->value_len); ++ if (node->value == NULL) ++ { ++ node->value_len = 0; ++ return NULL; ++ } ++ ++ memcpy (&node->value[prev_len], value, len); ++ ++ return node; ++ } ++} ++ ++/******************************************************************/ ++/* Function : _asn1_set_name */ ++/* Description: sets the field NAME in a NODE_ASN element. The */ ++/* previous value (if exist) will be lost */ ++/* Parameters: */ ++/* node: element pointer. */ ++/* name: a null terminated string with the name that you want */ ++/* to set. */ ++/* Return: pointer to the NODE_ASN element. */ ++/******************************************************************/ ++asn1_node ++_asn1_set_name (asn1_node node, const char *name) ++{ ++ if (node == NULL) ++ return node; ++ ++ _asn1_str_cpy (node->name, sizeof (node->name), name ? name : ""); ++ node->name_hash = _asn1_hash_name (node->name); ++ ++ return node; ++} ++ ++/******************************************************************/ ++/* Function : _asn1_cpy_name */ ++/* Description: copies the field NAME in a NODE_ASN element. */ ++/* Parameters: */ ++/* dst: a dest element pointer. */ ++/* src: a source element pointer. */ ++/* Return: pointer to the NODE_ASN element. */ ++/******************************************************************/ ++asn1_node ++_asn1_cpy_name (asn1_node dst, asn1_node_const src) ++{ ++ if (dst == NULL) ++ return dst; ++ ++ if (src == NULL) ++ { ++ dst->name[0] = 0; ++ dst->name_hash = _asn1_hash_name (dst->name); ++ return dst; ++ } ++ ++ _asn1_str_cpy (dst->name, sizeof (dst->name), src->name); ++ dst->name_hash = src->name_hash; ++ ++ return dst; ++} ++ ++/******************************************************************/ ++/* Function : _asn1_set_right */ ++/* Description: sets the field RIGHT in a NODE_ASN element. */ ++/* Parameters: */ ++/* node: element pointer. */ ++/* right: pointer to a NODE_ASN element that you want be pointed*/ ++/* by NODE. */ ++/* Return: pointer to *NODE. */ ++/******************************************************************/ ++asn1_node ++_asn1_set_right (asn1_node node, asn1_node right) ++{ ++ if (node == NULL) ++ return node; ++ node->right = right; ++ if (right) ++ right->left = node; ++ return node; ++} ++ ++ ++/******************************************************************/ ++/* Function : _asn1_get_last_right */ ++/* Description: return the last element along the right chain. */ ++/* Parameters: */ ++/* node: starting element pointer. */ ++/* Return: pointer to the last element along the right chain. */ ++/******************************************************************/ ++asn1_node ++_asn1_get_last_right (asn1_node_const node) ++{ ++ asn1_node_const p; ++ ++ if (node == NULL) ++ return NULL; ++ p = node; ++ while (p->right) ++ p = p->right; ++ return (asn1_node) p; ++} ++ ++/******************************************************************/ ++/* Function : _asn1_remove_node */ ++/* Description: gets free the memory allocated for an NODE_ASN */ ++/* element (not the elements pointed by it). */ ++/* Parameters: */ ++/* node: NODE_ASN element pointer. */ ++/* flags: ASN1_DELETE_FLAG_* */ ++/******************************************************************/ ++void ++_asn1_remove_node (asn1_node node, unsigned int flags) ++{ ++ if (node == NULL) ++ return; ++ ++ if (node->value != NULL) ++ { ++ if (flags & ASN1_DELETE_FLAG_ZEROIZE) ++ { ++ safe_memset (node->value, 0, node->value_len); ++ } ++ ++ if (node->value != node->small_value) ++ free (node->value); ++ } ++ free (node); ++} ++ ++/******************************************************************/ ++/* Function : _asn1_find_up */ ++/* Description: return the father of the NODE_ASN element. */ ++/* Parameters: */ ++/* node: NODE_ASN element pointer. */ ++/* Return: Null if not found. */ ++/******************************************************************/ ++asn1_node ++_asn1_find_up (asn1_node_const node) ++{ ++ asn1_node_const p; ++ ++ if (node == NULL) ++ return NULL; ++ ++ p = node; ++ ++ while ((p->left != NULL) && (p->left->right == p)) ++ p = p->left; ++ ++ return p->left; ++} ++ ++static unsigned ++_asn1_is_up (asn1_node_const up_cand, asn1_node_const down) ++{ ++ asn1_node_const d, u; ++ ++ if (up_cand == NULL || down == NULL) ++ return 0; ++ ++ d = down; ++ ++ while ((u = _asn1_find_up (d)) != NULL && u != d) ++ { ++ if (u == up_cand) ++ return 1; ++ d = u; ++ } ++ ++ return 0; ++} ++ ++/******************************************************************/ ++/* Function : _asn1_delete_node_from_list */ ++/* Description: deletes the list element given */ ++/******************************************************************/ ++void ++_asn1_delete_node_from_list (list_type * list, asn1_node node) ++{ ++ list_type *p = list; ++ ++ while (p) ++ { ++ if (p->node == node) ++ p->node = NULL; ++ p = p->next; ++ } ++} ++ ++/******************************************************************/ ++/* Function : _asn1_delete_list */ ++/* Description: deletes the list elements (not the elements */ ++/* pointed by them). */ ++/******************************************************************/ ++void ++_asn1_delete_list (list_type * e_list) ++{ ++ list_type *p; ++ ++ while (e_list) ++ { ++ p = e_list; ++ e_list = e_list->next; ++ free (p); ++ } ++} ++ ++/******************************************************************/ ++/* Function : _asn1_delete_list_and nodes */ ++/* Description: deletes the list elements and the elements */ ++/* pointed by them. */ ++/******************************************************************/ ++void ++_asn1_delete_list_and_nodes (list_type * e_list) ++{ ++ list_type *p; ++ ++ while (e_list) ++ { ++ p = e_list; ++ e_list = e_list->next; ++ _asn1_remove_node (p->node, 0); ++ free (p); ++ } ++} ++ ++ ++char * ++_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]) ++{ ++ uint64_t d, r; ++ char temp[LTOSTR_MAX_SIZE]; ++ int count, k, start; ++ uint64_t val; ++ ++ if (v < 0) ++ { ++ str[0] = '-'; ++ start = 1; ++ val = -((uint64_t) v); ++ } ++ else ++ { ++ val = v; ++ start = 0; ++ } ++ ++ count = 0; ++ do ++ { ++ d = val / 10; ++ r = val - d * 10; ++ temp[start + count] = '0' + (char) r; ++ count++; ++ val = d; ++ } ++ while (val && ((start + count) < LTOSTR_MAX_SIZE - 1)); ++ ++ for (k = 0; k < count; k++) ++ str[k + start] = temp[start + count - k - 1]; ++ str[count + start] = 0; ++ return str; ++} ++ ++ ++/******************************************************************/ ++/* Function : _asn1_change_integer_value */ ++/* Description: converts into DER coding the value assign to an */ ++/* INTEGER constant. */ ++/* Parameters: */ ++/* node: root of an ASN1element. */ ++/* Return: */ ++/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ ++/* otherwise ASN1_SUCCESS */ ++/******************************************************************/ ++int ++_asn1_change_integer_value (asn1_node node) ++{ ++ asn1_node p; ++ unsigned char val[SIZEOF_UNSIGNED_LONG_INT]; ++ unsigned char val2[SIZEOF_UNSIGNED_LONG_INT + 1]; ++ int len; ++ ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ p = node; ++ while (p) ++ { ++ if ((type_field (p->type) == ASN1_ETYPE_INTEGER) ++ && (p->type & CONST_ASSIGN)) ++ { ++ if (p->value) ++ { ++ _asn1_convert_integer (p->value, val, sizeof (val), &len); ++ asn1_octet_der (val, len, val2, &len); ++ _asn1_set_value (p, val2, len); ++ } ++ } ++ ++ if (p->down) ++ { ++ p = p->down; ++ } ++ else ++ { ++ if (p == node) ++ p = NULL; ++ else if (p->right) ++ p = p->right; ++ else ++ { ++ while (1) ++ { ++ p = _asn1_find_up (p); ++ if (p == node) ++ { ++ p = NULL; ++ break; ++ } ++ if (p && p->right) ++ { ++ p = p->right; ++ break; ++ } ++ } ++ } ++ } ++ } ++ ++ return ASN1_SUCCESS; ++} ++ ++#define MAX_CONSTANTS 1024 ++/******************************************************************/ ++/* Function : _asn1_expand_object_id */ ++/* Description: expand the IDs of an OBJECT IDENTIFIER constant. */ ++/* Parameters: */ ++/* list: root of an object list */ ++/* node: root of an ASN1 element. */ ++/* Return: */ ++/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ ++/* otherwise ASN1_SUCCESS */ ++/******************************************************************/ ++int ++_asn1_expand_object_id (list_type ** list, asn1_node node) ++{ ++ asn1_node p, p2, p3, p4, p5; ++ char name_root[ASN1_MAX_NAME_SIZE], name2[2 * ASN1_MAX_NAME_SIZE + 1]; ++ int move, tlen, tries; ++ unsigned max_constants; ++ ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ _asn1_str_cpy (name_root, sizeof (name_root), node->name); ++ ++ p = node; ++ move = DOWN; ++ tries = 0; ++ ++ while (!((p == node) && (move == UP))) ++ { ++ if (move != UP) ++ { ++ if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) ++ && (p->type & CONST_ASSIGN)) ++ { ++ p2 = p->down; ++ if (p2 && (type_field (p2->type) == ASN1_ETYPE_CONSTANT)) ++ { ++ if (p2->value && !c_isdigit (p2->value[0])) ++ { ++ _asn1_str_cpy (name2, sizeof (name2), name_root); ++ _asn1_str_cat (name2, sizeof (name2), "."); ++ _asn1_str_cat (name2, sizeof (name2), ++ (char *) p2->value); ++ p3 = asn1_find_node (node, name2); ++ if (!p3 || _asn1_is_up (p2, p3) || ++ (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) || ++ !(p3->type & CONST_ASSIGN)) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ _asn1_set_down (p, p2->right); ++ if (p2->down) ++ _asn1_delete_structure (*list, &p2->down, 0); ++ _asn1_delete_node_from_list (*list, p2); ++ _asn1_remove_node (p2, 0); ++ p2 = p; ++ p4 = p3->down; ++ max_constants = 0; ++ while (p4) ++ { ++ if (type_field (p4->type) == ASN1_ETYPE_CONSTANT) ++ { ++ max_constants++; ++ if (max_constants == MAX_CONSTANTS) ++ return ASN1_RECURSION; ++ ++ p5 = ++ _asn1_add_single_node (ASN1_ETYPE_CONSTANT); ++ _asn1_set_name (p5, p4->name); ++ if (p4->value) ++ { ++ tlen = _asn1_strlen (p4->value); ++ if (tlen > 0) ++ _asn1_set_value (p5, p4->value, tlen + 1); ++ } ++ _asn1_add_static_node2 (list, p5); ++ ++ if (p2 == p) ++ { ++ _asn1_set_right (p5, p->down); ++ _asn1_set_down (p, p5); ++ } ++ else ++ { ++ _asn1_set_right (p5, p2->right); ++ _asn1_set_right (p2, p5); ++ } ++ p2 = p5; ++ } ++ p4 = p4->right; ++ } ++ move = DOWN; ++ ++ tries++; ++ if (tries >= EXPAND_OBJECT_ID_MAX_RECURSION) ++ return ASN1_RECURSION; ++ ++ continue; ++ } ++ } ++ } ++ move = DOWN; ++ } ++ else ++ move = RIGHT; ++ ++ tries = 0; ++ if (move == DOWN) ++ { ++ if (p->down) ++ p = p->down; ++ else ++ move = RIGHT; ++ } ++ ++ if (p == node) ++ { ++ move = UP; ++ continue; ++ } ++ ++ if (move == RIGHT) ++ { ++ if (p && p->right) ++ p = p->right; ++ else ++ move = UP; ++ } ++ if (move == UP) ++ p = _asn1_find_up (p); ++ } ++ ++ /*******************************/ ++ /* expand DEFAULT */ ++ /*******************************/ ++ p = node; ++ move = DOWN; ++ ++ while (!((p == node) && (move == UP))) ++ { ++ if (move != UP) ++ { ++ if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && ++ (p->type & CONST_DEFAULT)) ++ { ++ p2 = p->down; ++ if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT)) ++ { ++ _asn1_str_cpy (name2, sizeof (name2), name_root); ++ _asn1_str_cat (name2, sizeof (name2), "."); ++ if (p2->value) ++ _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); ++ p3 = asn1_find_node (node, name2); ++ if (!p3 || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) ++ || !(p3->type & CONST_ASSIGN)) ++ return ASN1_ELEMENT_NOT_FOUND; ++ p4 = p3->down; ++ name2[0] = 0; ++ while (p4) ++ { ++ if (type_field (p4->type) == ASN1_ETYPE_CONSTANT) ++ { ++ if (p4->value == NULL) ++ return ASN1_VALUE_NOT_FOUND; ++ ++ if (name2[0]) ++ _asn1_str_cat (name2, sizeof (name2), "."); ++ _asn1_str_cat (name2, sizeof (name2), ++ (char *) p4->value); ++ } ++ p4 = p4->right; ++ } ++ tlen = strlen (name2); ++ if (tlen > 0) ++ _asn1_set_value (p2, name2, tlen + 1); ++ } ++ } ++ move = DOWN; ++ } ++ else ++ move = RIGHT; ++ ++ if (move == DOWN) ++ { ++ if (p->down) ++ p = p->down; ++ else ++ move = RIGHT; ++ } ++ ++ if (p == node) ++ { ++ move = UP; ++ continue; ++ } ++ ++ if (move == RIGHT) ++ { ++ if (p && p->right) ++ p = p->right; ++ else ++ move = UP; ++ } ++ if (move == UP) ++ p = _asn1_find_up (p); ++ } ++ ++ return ASN1_SUCCESS; ++} ++ ++ ++/******************************************************************/ ++/* Function : _asn1_type_set_config */ ++/* Description: sets the CONST_SET and CONST_NOT_USED properties */ ++/* in the fields of the SET elements. */ ++/* Parameters: */ ++/* node: root of an ASN1 element. */ ++/* Return: */ ++/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ ++/* otherwise ASN1_SUCCESS */ ++/******************************************************************/ ++int ++_asn1_type_set_config (asn1_node node) ++{ ++ asn1_node p, p2; ++ int move; ++ ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ p = node; ++ move = DOWN; ++ ++ while (!((p == node) && (move == UP))) ++ { ++ if (move != UP) ++ { ++ if (type_field (p->type) == ASN1_ETYPE_SET) ++ { ++ p2 = p->down; ++ while (p2) ++ { ++ if (type_field (p2->type) != ASN1_ETYPE_TAG) ++ p2->type |= CONST_SET | CONST_NOT_USED; ++ p2 = p2->right; ++ } ++ } ++ move = DOWN; ++ } ++ else ++ move = RIGHT; ++ ++ if (move == DOWN) ++ { ++ if (p->down) ++ p = p->down; ++ else ++ move = RIGHT; ++ } ++ ++ if (p == node) ++ { ++ move = UP; ++ continue; ++ } ++ ++ if (move == RIGHT) ++ { ++ if (p && p->right) ++ p = p->right; ++ else ++ move = UP; ++ } ++ if (move == UP) ++ p = _asn1_find_up (p); ++ } ++ ++ return ASN1_SUCCESS; ++} ++ ++ ++/******************************************************************/ ++/* Function : _asn1_check_identifier */ ++/* Description: checks the definitions of all the identifiers */ ++/* and the first element of an OBJECT_ID (e.g. {pkix 0 4}). */ ++/* The _asn1_identifierMissing global variable is filled if */ ++/* necessary. */ ++/* Parameters: */ ++/* node: root of an ASN1 element. */ ++/* Return: */ ++/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ ++/* ASN1_IDENTIFIER_NOT_FOUND if an identifier is not defined, */ ++/* otherwise ASN1_SUCCESS */ ++/******************************************************************/ ++int ++_asn1_check_identifier (asn1_node_const node) ++{ ++ asn1_node_const p, p2; ++ char name2[ASN1_MAX_NAME_SIZE * 2 + 2]; ++ ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ p = node; ++ while (p) ++ { ++ if (p->value && type_field (p->type) == ASN1_ETYPE_IDENTIFIER) ++ { ++ _asn1_str_cpy (name2, sizeof (name2), node->name); ++ _asn1_str_cat (name2, sizeof (name2), "."); ++ _asn1_str_cat (name2, sizeof (name2), (char *) p->value); ++ p2 = asn1_find_node (node, name2); ++ if (p2 == NULL) ++ { ++ if (p->value) ++ _asn1_str_cpy (_asn1_identifierMissing, ++ sizeof (_asn1_identifierMissing), ++ (char *) p->value); ++ else ++ _asn1_strcpy (_asn1_identifierMissing, "(null)"); ++ return ASN1_IDENTIFIER_NOT_FOUND; ++ } ++ } ++ else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && ++ (p->type & CONST_DEFAULT)) ++ { ++ p2 = p->down; ++ if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT)) ++ { ++ _asn1_str_cpy (name2, sizeof (name2), node->name); ++ if (p2->value) ++ { ++ _asn1_str_cat (name2, sizeof (name2), "."); ++ _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); ++ _asn1_str_cpy (_asn1_identifierMissing, ++ sizeof (_asn1_identifierMissing), ++ (char *) p2->value); ++ } ++ else ++ _asn1_strcpy (_asn1_identifierMissing, "(null)"); ++ ++ p2 = asn1_find_node (node, name2); ++ if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID) || ++ !(p2->type & CONST_ASSIGN)) ++ return ASN1_IDENTIFIER_NOT_FOUND; ++ else ++ _asn1_identifierMissing[0] = 0; ++ } ++ } ++ else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && ++ (p->type & CONST_ASSIGN)) ++ { ++ p2 = p->down; ++ if (p2 && (type_field (p2->type) == ASN1_ETYPE_CONSTANT)) ++ { ++ if (p2->value && !c_isdigit (p2->value[0])) ++ { ++ _asn1_str_cpy (name2, sizeof (name2), node->name); ++ _asn1_str_cat (name2, sizeof (name2), "."); ++ _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); ++ _asn1_str_cpy (_asn1_identifierMissing, ++ sizeof (_asn1_identifierMissing), ++ (char *) p2->value); ++ ++ p2 = asn1_find_node (node, name2); ++ if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID) ++ || !(p2->type & CONST_ASSIGN)) ++ return ASN1_IDENTIFIER_NOT_FOUND; ++ else ++ _asn1_identifierMissing[0] = 0; ++ } ++ } ++ } ++ ++ if (p->down) ++ { ++ p = p->down; ++ } ++ else if (p->right) ++ p = p->right; ++ else ++ { ++ while (p) ++ { ++ p = _asn1_find_up (p); ++ if (p == node) ++ { ++ p = NULL; ++ break; ++ } ++ if (p && p->right) ++ { ++ p = p->right; ++ break; ++ } ++ } ++ } ++ } ++ ++ return ASN1_SUCCESS; ++} ++ ++ ++/******************************************************************/ ++/* Function : _asn1_set_default_tag */ ++/* Description: sets the default IMPLICIT or EXPLICIT property in */ ++/* the tagged elements that don't have this declaration. */ ++/* Parameters: */ ++/* node: pointer to a DEFINITIONS element. */ ++/* Return: */ ++/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL or not a pointer to */ ++/* a DEFINITIONS element, */ ++/* otherwise ASN1_SUCCESS */ ++/******************************************************************/ ++int ++_asn1_set_default_tag (asn1_node node) ++{ ++ asn1_node p; ++ ++ if ((node == NULL) || (type_field (node->type) != ASN1_ETYPE_DEFINITIONS)) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ p = node; ++ while (p) ++ { ++ if ((type_field (p->type) == ASN1_ETYPE_TAG) && ++ !(p->type & CONST_EXPLICIT) && !(p->type & CONST_IMPLICIT)) ++ { ++ if (node->type & CONST_EXPLICIT) ++ p->type |= CONST_EXPLICIT; ++ else ++ p->type |= CONST_IMPLICIT; ++ } ++ ++ if (p->down) ++ { ++ p = p->down; ++ } ++ else if (p->right) ++ p = p->right; ++ else ++ { ++ while (1) ++ { ++ p = _asn1_find_up (p); ++ if (p == node) ++ { ++ p = NULL; ++ break; ++ } ++ if (p && p->right) ++ { ++ p = p->right; ++ break; ++ } ++ } ++ } ++ } ++ ++ return ASN1_SUCCESS; ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1/lib/parser_aux.h +@@ -0,0 +1,172 @@ ++/* ++ * Copyright (C) 2000-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * The LIBTASN1 library is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ */ ++ ++#ifndef _PARSER_AUX_H ++# define _PARSER_AUX_H ++ ++/***********************************************/ ++/* Type: list_type */ ++/* Description: type used in the list during */ ++/* the structure creation. */ ++/***********************************************/ ++typedef struct list_struct ++{ ++ asn1_node node; ++ struct list_struct *next; ++} list_type; ++ ++/***************************************/ ++/* Functions used by ASN.1 parser */ ++/***************************************/ ++asn1_node _asn1_add_static_node (list_type ** e_list, unsigned int type); ++ ++void _asn1_delete_list (list_type * e_list); ++ ++void _asn1_delete_list_and_nodes (list_type * e_list); ++ ++void _asn1_delete_node_from_list (list_type * list, asn1_node node); ++ ++asn1_node ++_asn1_set_value (asn1_node node, const void *value, unsigned int len); ++ ++asn1_node _asn1_set_value_m (asn1_node node, void *value, unsigned int len); ++ ++asn1_node ++_asn1_set_value_lv (asn1_node node, const void *value, unsigned int len); ++ ++asn1_node ++_asn1_append_value (asn1_node node, const void *value, unsigned int len); ++ ++asn1_node _asn1_set_name (asn1_node node, const char *name); ++ ++asn1_node _asn1_cpy_name (asn1_node dst, asn1_node_const src); ++ ++asn1_node _asn1_set_right (asn1_node node, asn1_node right); ++ ++asn1_node _asn1_get_last_right (asn1_node_const node); ++ ++void _asn1_remove_node (asn1_node node, unsigned int flags); ++ ++/* Max 64-bit integer length is 20 chars + 1 for sign + 1 for null termination */ ++# define LTOSTR_MAX_SIZE 22 ++char *_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]); ++ ++asn1_node _asn1_find_up (asn1_node_const node); ++ ++int _asn1_change_integer_value (asn1_node node); ++ ++# define EXPAND_OBJECT_ID_MAX_RECURSION 16 ++int _asn1_expand_object_id (list_type ** list, asn1_node node); ++ ++int _asn1_type_set_config (asn1_node node); ++ ++int _asn1_check_identifier (asn1_node_const node); ++ ++int _asn1_set_default_tag (asn1_node node); ++ ++/******************************************************************/ ++/* Function : _asn1_get_right */ ++/* Description: returns the element pointed by the RIGHT field of */ ++/* a NODE_ASN element. */ ++/* Parameters: */ ++/* node: NODE_ASN element pointer. */ ++/* Return: field RIGHT of NODE. */ ++/******************************************************************/ ++inline static asn1_node ++_asn1_get_right (asn1_node_const node) ++{ ++ if (node == NULL) ++ return NULL; ++ return node->right; ++} ++ ++/******************************************************************/ ++/* Function : _asn1_set_down */ ++/* Description: sets the field DOWN in a NODE_ASN element. */ ++/* Parameters: */ ++/* node: element pointer. */ ++/* down: pointer to a NODE_ASN element that you want be pointed */ ++/* by NODE. */ ++/* Return: pointer to *NODE. */ ++/******************************************************************/ ++inline static asn1_node ++_asn1_set_down (asn1_node node, asn1_node down) ++{ ++ if (node == NULL) ++ return node; ++ node->down = down; ++ if (down) ++ down->left = node; ++ return node; ++} ++ ++/******************************************************************/ ++/* Function : _asn1_get_down */ ++/* Description: returns the element pointed by the DOWN field of */ ++/* a NODE_ASN element. */ ++/* Parameters: */ ++/* node: NODE_ASN element pointer. */ ++/* Return: field DOWN of NODE. */ ++/******************************************************************/ ++inline static asn1_node ++_asn1_get_down (asn1_node_const node) ++{ ++ if (node == NULL) ++ return NULL; ++ return node->down; ++} ++ ++/******************************************************************/ ++/* Function : _asn1_get_name */ ++/* Description: returns the name of a NODE_ASN element. */ ++/* Parameters: */ ++/* node: NODE_ASN element pointer. */ ++/* Return: a null terminated string. */ ++/******************************************************************/ ++inline static char * ++_asn1_get_name (asn1_node_const node) ++{ ++ if (node == NULL) ++ return NULL; ++ return (char *) node->name; ++} ++ ++/******************************************************************/ ++/* Function : _asn1_mod_type */ ++/* Description: change the field TYPE of an NODE_ASN element. */ ++/* The new value is the old one | (bitwise or) the */ ++/* paramener VALUE. */ ++/* Parameters: */ ++/* node: NODE_ASN element pointer. */ ++/* value: the integer value that must be or-ed with the current */ ++/* value of field TYPE. */ ++/* Return: NODE pointer. */ ++/******************************************************************/ ++inline static asn1_node ++_asn1_mod_type (asn1_node node, unsigned int value) ++{ ++ if (node == NULL) ++ return node; ++ node->type |= value; ++ return node; ++} ++ ++#endif +--- /dev/null ++++ b/grub-core/lib/libtasn1/lib/structure.c +@@ -0,0 +1,1225 @@ ++/* ++ * Copyright (C) 2002-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * The LIBTASN1 library is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ */ ++ ++ ++/*****************************************************/ ++/* File: structure.c */ ++/* Description: Functions to create and delete an */ ++/* ASN1 tree. */ ++/*****************************************************/ ++ ++ ++#include ++#include ++#include "parser_aux.h" ++#include ++ ++ ++extern char _asn1_identifierMissing[]; ++ ++ ++/******************************************************/ ++/* Function : _asn1_add_single_node */ ++/* Description: creates a new NODE_ASN element. */ ++/* Parameters: */ ++/* type: type of the new element (see ASN1_ETYPE_ */ ++/* and CONST_ constants). */ ++/* Return: pointer to the new element. */ ++/******************************************************/ ++asn1_node ++_asn1_add_single_node (unsigned int type) ++{ ++ asn1_node punt; ++ ++ punt = calloc (1, sizeof (struct asn1_node_st)); ++ if (punt == NULL) ++ return NULL; ++ ++ punt->type = type; ++ ++ return punt; ++} ++ ++ ++/******************************************************************/ ++/* Function : _asn1_find_left */ ++/* Description: returns the NODE_ASN element with RIGHT field that*/ ++/* points the element NODE. */ ++/* Parameters: */ ++/* node: NODE_ASN element pointer. */ ++/* Return: NULL if not found. */ ++/******************************************************************/ ++asn1_node ++_asn1_find_left (asn1_node_const node) ++{ ++ if ((node == NULL) || (node->left == NULL) || (node->left->down == node)) ++ return NULL; ++ ++ return node->left; ++} ++ ++ ++int ++_asn1_create_static_structure (asn1_node_const pointer, ++ char *output_file_name, char *vector_name) ++{ ++ FILE *file; ++ asn1_node_const p; ++ unsigned long t; ++ ++ file = fopen (output_file_name, "w"); ++ ++ if (file == NULL) ++ return ASN1_FILE_NOT_FOUND; ++ ++ fprintf (file, "#if HAVE_CONFIG_H\n"); ++ fprintf (file, "# include \"config.h\"\n"); ++ fprintf (file, "#endif\n\n"); ++ ++ fprintf (file, "#include \n\n"); ++ ++ fprintf (file, "const asn1_static_node %s[] = {\n", vector_name); ++ ++ p = pointer; ++ ++ while (p) ++ { ++ fprintf (file, " { "); ++ ++ if (p->name[0] != 0) ++ fprintf (file, "\"%s\", ", p->name); ++ else ++ fprintf (file, "NULL, "); ++ ++ t = p->type; ++ if (p->down) ++ t |= CONST_DOWN; ++ if (p->right) ++ t |= CONST_RIGHT; ++ ++ fprintf (file, "%lu, ", t); ++ ++ if (p->value) ++ fprintf (file, "\"%s\"},\n", p->value); ++ else ++ fprintf (file, "NULL },\n"); ++ ++ if (p->down) ++ { ++ p = p->down; ++ } ++ else if (p->right) ++ { ++ p = p->right; ++ } ++ else ++ { ++ while (1) ++ { ++ p = _asn1_find_up (p); ++ if (p == pointer) ++ { ++ p = NULL; ++ break; ++ } ++ if (p->right) ++ { ++ p = p->right; ++ break; ++ } ++ } ++ } ++ } ++ ++ fprintf (file, " { NULL, 0, NULL }\n};\n"); ++ ++ fclose (file); ++ ++ return ASN1_SUCCESS; ++} ++ ++ ++/** ++ * asn1_array2tree: ++ * @array: specify the array that contains ASN.1 declarations ++ * @definitions: return the pointer to the structure created by ++ * *ARRAY ASN.1 declarations ++ * @errorDescription: return the error description. ++ * ++ * Creates the structures needed to manage the ASN.1 definitions. ++ * @array is a vector created by asn1_parser2array(). ++ * ++ * Returns: %ASN1_SUCCESS if structure was created correctly, ++ * %ASN1_ELEMENT_NOT_EMPTY if *@definitions not NULL, ++ * %ASN1_IDENTIFIER_NOT_FOUND if in the file there is an identifier ++ * that is not defined (see @errorDescription for more information), ++ * %ASN1_ARRAY_ERROR if the array pointed by @array is wrong. ++ **/ ++int ++asn1_array2tree (const asn1_static_node * array, asn1_node * definitions, ++ char *errorDescription) ++{ ++ asn1_node p, p_last = NULL; ++ unsigned long k; ++ int move; ++ int result; ++ unsigned int type; ++ list_type *e_list = NULL; ++ ++ if (errorDescription) ++ errorDescription[0] = 0; ++ ++ if (*definitions != NULL) ++ return ASN1_ELEMENT_NOT_EMPTY; ++ ++ move = UP; ++ ++ for (k = 0; array[k].value || array[k].type || array[k].name; k++) ++ { ++ type = convert_old_type (array[k].type); ++ ++ p = _asn1_add_static_node (&e_list, type & (~CONST_DOWN)); ++ if (array[k].name) ++ _asn1_set_name (p, array[k].name); ++ if (array[k].value) ++ _asn1_set_value (p, array[k].value, strlen (array[k].value) + 1); ++ ++ if (*definitions == NULL) ++ *definitions = p; ++ ++ if (move == DOWN) ++ { ++ if (p_last && p_last->down) ++ _asn1_delete_structure (e_list, &p_last->down, 0); ++ _asn1_set_down (p_last, p); ++ } ++ else if (move == RIGHT) ++ { ++ if (p_last && p_last->right) ++ _asn1_delete_structure (e_list, &p_last->right, 0); ++ _asn1_set_right (p_last, p); ++ } ++ ++ p_last = p; ++ ++ if (type & CONST_DOWN) ++ move = DOWN; ++ else if (type & CONST_RIGHT) ++ move = RIGHT; ++ else ++ { ++ while (p_last != *definitions) ++ { ++ p_last = _asn1_find_up (p_last); ++ ++ if (p_last == NULL) ++ break; ++ ++ if (p_last->type & CONST_RIGHT) ++ { ++ p_last->type &= ~CONST_RIGHT; ++ move = RIGHT; ++ break; ++ } ++ } /* while */ ++ } ++ } /* while */ ++ ++ if (p_last == *definitions) ++ { ++ result = _asn1_check_identifier (*definitions); ++ if (result == ASN1_SUCCESS) ++ { ++ _asn1_change_integer_value (*definitions); ++ result = _asn1_expand_object_id (&e_list, *definitions); ++ } ++ } ++ else ++ { ++ result = ASN1_ARRAY_ERROR; ++ } ++ ++ if (errorDescription != NULL) ++ { ++ if (result == ASN1_IDENTIFIER_NOT_FOUND) ++ { ++ Estrcpy (errorDescription, ":: identifier '"); ++ Estrcat (errorDescription, _asn1_identifierMissing); ++ Estrcat (errorDescription, "' not found"); ++ } ++ else ++ errorDescription[0] = 0; ++ } ++ ++ if (result != ASN1_SUCCESS) ++ { ++ _asn1_delete_list_and_nodes (e_list); ++ *definitions = NULL; ++ } ++ else ++ _asn1_delete_list (e_list); ++ ++ return result; ++} ++ ++/** ++ * asn1_delete_structure: ++ * @structure: pointer to the structure that you want to delete. ++ * ++ * Deletes the structure *@structure. At the end, *@structure is set ++ * to NULL. ++ * ++ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if ++ * *@structure was NULL. ++ **/ ++int ++asn1_delete_structure (asn1_node * structure) ++{ ++ return _asn1_delete_structure (NULL, structure, 0); ++} ++ ++/** ++ * asn1_delete_structure2: ++ * @structure: pointer to the structure that you want to delete. ++ * @flags: additional flags (see %ASN1_DELETE_FLAG_ZEROIZE) ++ * ++ * Deletes the structure *@structure. At the end, *@structure is set ++ * to NULL. ++ * ++ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if ++ * *@structure was NULL. ++ **/ ++int ++asn1_delete_structure2 (asn1_node * structure, unsigned int flags) ++{ ++ return _asn1_delete_structure (NULL, structure, flags); ++} ++ ++int ++_asn1_delete_structure (list_type * e_list, asn1_node * structure, ++ unsigned int flags) ++{ ++ asn1_node p, p2, p3; ++ ++ if (*structure == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ p = *structure; ++ while (p) ++ { ++ if (p->down) ++ { ++ p = p->down; ++ } ++ else ++ { /* no down */ ++ p2 = p->right; ++ if (p != *structure) ++ { ++ p3 = _asn1_find_up (p); ++ _asn1_set_down (p3, p2); ++ if (e_list) ++ _asn1_delete_node_from_list (e_list, p); ++ _asn1_remove_node (p, flags); ++ p = p3; ++ } ++ else ++ { /* p==root */ ++ p3 = _asn1_find_left (p); ++ if (!p3) ++ { ++ p3 = _asn1_find_up (p); ++ if (p3) ++ _asn1_set_down (p3, p2); ++ else ++ { ++ if (p->right) ++ p->right->left = NULL; ++ } ++ } ++ else ++ _asn1_set_right (p3, p2); ++ if (e_list) ++ _asn1_delete_node_from_list (e_list, p); ++ _asn1_remove_node (p, flags); ++ p = NULL; ++ } ++ } ++ } ++ ++ *structure = NULL; ++ return ASN1_SUCCESS; ++} ++ ++ ++/** ++ * asn1_delete_element: ++ * @structure: pointer to the structure that contains the element you ++ * want to delete. ++ * @element_name: element's name you want to delete. ++ * ++ * Deletes the element named *@element_name inside *@structure. ++ * ++ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if ++ * the @element_name was not found. ++ **/ ++int ++asn1_delete_element (asn1_node structure, const char *element_name) ++{ ++ asn1_node p2, p3, source_node; ++ ++ source_node = asn1_find_node (structure, element_name); ++ ++ if (source_node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ p2 = source_node->right; ++ p3 = _asn1_find_left (source_node); ++ if (!p3) ++ { ++ p3 = _asn1_find_up (source_node); ++ if (p3) ++ _asn1_set_down (p3, p2); ++ else if (source_node->right) ++ source_node->right->left = NULL; ++ } ++ else ++ _asn1_set_right (p3, p2); ++ ++ return asn1_delete_structure (&source_node); ++} ++ ++#ifndef __clang_analyzer__ ++asn1_node ++_asn1_copy_structure3 (asn1_node_const source_node) ++{ ++ asn1_node_const p_s; ++ asn1_node dest_node, p_d, p_d_prev; ++ int move; ++ ++ if (source_node == NULL) ++ return NULL; ++ ++ dest_node = _asn1_add_single_node (source_node->type); ++ if (dest_node == NULL) ++ return dest_node; ++ ++ p_s = source_node; ++ p_d = dest_node; ++ ++ move = DOWN; ++ ++ do ++ { ++ if (move != UP) ++ { ++ if (p_s->name[0] != 0) ++ _asn1_cpy_name (p_d, p_s); ++ if (p_s->value) ++ _asn1_set_value (p_d, p_s->value, p_s->value_len); ++ if (p_s->down) ++ { ++ p_s = p_s->down; ++ p_d_prev = p_d; ++ p_d = _asn1_add_single_node (p_s->type); ++ _asn1_set_down (p_d_prev, p_d); ++ continue; ++ } ++ p_d->start = p_s->start; ++ p_d->end = p_s->end; ++ } ++ ++ if (p_s == source_node) ++ break; ++ ++ if (p_s->right) ++ { ++ move = RIGHT; ++ p_s = p_s->right; ++ p_d_prev = p_d; ++ p_d = _asn1_add_single_node (p_s->type); ++ _asn1_set_right (p_d_prev, p_d); ++ } ++ else ++ { ++ move = UP; ++ p_s = _asn1_find_up (p_s); ++ p_d = _asn1_find_up (p_d); ++ } ++ } ++ while (p_s != source_node); ++ return dest_node; ++} ++#else ++ ++/* Non-production code */ ++asn1_node ++_asn1_copy_structure3 (asn1_node_const source_node) ++{ ++ return NULL; ++} ++#endif /* __clang_analyzer__ */ ++ ++ ++static asn1_node ++_asn1_copy_structure2 (asn1_node_const root, const char *source_name) ++{ ++ asn1_node source_node; ++ ++ source_node = asn1_find_node (root, source_name); ++ ++ return _asn1_copy_structure3 (source_node); ++ ++} ++ ++ ++static int ++_asn1_type_choice_config (asn1_node node) ++{ ++ asn1_node p, p2, p3, p4; ++ int move, tlen; ++ ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ p = node; ++ move = DOWN; ++ ++ while (!((p == node) && (move == UP))) ++ { ++ if (move != UP) ++ { ++ if ((type_field (p->type) == ASN1_ETYPE_CHOICE) ++ && (p->type & CONST_TAG)) ++ { ++ p2 = p->down; ++ while (p2) ++ { ++ if (type_field (p2->type) != ASN1_ETYPE_TAG) ++ { ++ p2->type |= CONST_TAG; ++ p3 = _asn1_find_left (p2); ++ while (p3) ++ { ++ if (type_field (p3->type) == ASN1_ETYPE_TAG) ++ { ++ p4 = _asn1_add_single_node (p3->type); ++ tlen = _asn1_strlen (p3->value); ++ if (tlen > 0) ++ _asn1_set_value (p4, p3->value, tlen + 1); ++ _asn1_set_right (p4, p2->down); ++ _asn1_set_down (p2, p4); ++ } ++ p3 = _asn1_find_left (p3); ++ } ++ } ++ p2 = p2->right; ++ } ++ p->type &= ~(CONST_TAG); ++ p2 = p->down; ++ while (p2) ++ { ++ p3 = p2->right; ++ if (type_field (p2->type) == ASN1_ETYPE_TAG) ++ asn1_delete_structure (&p2); ++ p2 = p3; ++ } ++ } ++ move = DOWN; ++ } ++ else ++ move = RIGHT; ++ ++ if (move == DOWN) ++ { ++ if (p->down) ++ p = p->down; ++ else ++ move = RIGHT; ++ } ++ ++ if (p == node) ++ { ++ move = UP; ++ continue; ++ } ++ ++ if (move == RIGHT) ++ { ++ if (p->right) ++ p = p->right; ++ else ++ move = UP; ++ } ++ if (move == UP) ++ p = _asn1_find_up (p); ++ } ++ ++ return ASN1_SUCCESS; ++} ++ ++ ++static int ++_asn1_expand_identifier (asn1_node * node, asn1_node_const root) ++{ ++ asn1_node p, p2, p3; ++ char name2[ASN1_MAX_NAME_SIZE + 2]; ++ int move; ++ ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ p = *node; ++ move = DOWN; ++ ++ while (!((p == *node) && (move == UP))) ++ { ++ if (move != UP) ++ { ++ if (type_field (p->type) == ASN1_ETYPE_IDENTIFIER) ++ { ++ snprintf (name2, sizeof (name2), "%s.%s", root->name, p->value); ++ p2 = _asn1_copy_structure2 (root, name2); ++ if (p2 == NULL) ++ { ++ return ASN1_IDENTIFIER_NOT_FOUND; ++ } ++ _asn1_cpy_name (p2, p); ++ p2->right = p->right; ++ p2->left = p->left; ++ if (p->right) ++ p->right->left = p2; ++ p3 = p->down; ++ if (p3) ++ { ++ while (p3->right) ++ p3 = p3->right; ++ _asn1_set_right (p3, p2->down); ++ _asn1_set_down (p2, p->down); ++ } ++ ++ p3 = _asn1_find_left (p); ++ if (p3) ++ _asn1_set_right (p3, p2); ++ else ++ { ++ p3 = _asn1_find_up (p); ++ if (p3) ++ _asn1_set_down (p3, p2); ++ else ++ { ++ p2->left = NULL; ++ } ++ } ++ ++ if (p->type & CONST_SIZE) ++ p2->type |= CONST_SIZE; ++ if (p->type & CONST_TAG) ++ p2->type |= CONST_TAG; ++ if (p->type & CONST_OPTION) ++ p2->type |= CONST_OPTION; ++ if (p->type & CONST_DEFAULT) ++ p2->type |= CONST_DEFAULT; ++ if (p->type & CONST_SET) ++ p2->type |= CONST_SET; ++ if (p->type & CONST_NOT_USED) ++ p2->type |= CONST_NOT_USED; ++ ++ if (p == *node) ++ *node = p2; ++ _asn1_remove_node (p, 0); ++ p = p2; ++ move = DOWN; ++ continue; ++ } ++ move = DOWN; ++ } ++ else ++ move = RIGHT; ++ ++ if (move == DOWN) ++ { ++ if (p->down) ++ p = p->down; ++ else ++ move = RIGHT; ++ } ++ ++ if (p == *node) ++ { ++ move = UP; ++ continue; ++ } ++ ++ if (move == RIGHT) ++ { ++ if (p->right) ++ p = p->right; ++ else ++ move = UP; ++ } ++ if (move == UP) ++ p = _asn1_find_up (p); ++ } ++ ++ return ASN1_SUCCESS; ++} ++ ++ ++/** ++ * asn1_create_element: ++ * @definitions: pointer to the structure returned by "parser_asn1" function ++ * @source_name: the name of the type of the new structure (must be ++ * inside p_structure). ++ * @element: pointer to the structure created. ++ * ++ * Creates a structure of type @source_name. Example using ++ * "pkix.asn": ++ * ++ * rc = asn1_create_element(cert_def, "PKIX1.Certificate", certptr); ++ * ++ * Returns: %ASN1_SUCCESS if creation OK, %ASN1_ELEMENT_NOT_FOUND if ++ * @source_name is not known. ++ **/ ++int ++asn1_create_element (asn1_node_const definitions, const char *source_name, ++ asn1_node * element) ++{ ++ asn1_node dest_node; ++ int res; ++ ++ dest_node = _asn1_copy_structure2 (definitions, source_name); ++ ++ if (dest_node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ _asn1_set_name (dest_node, ""); ++ ++ res = _asn1_expand_identifier (&dest_node, definitions); ++ _asn1_type_choice_config (dest_node); ++ ++ *element = dest_node; ++ ++ return res; ++} ++ ++ ++/** ++ * asn1_print_structure: ++ * @out: pointer to the output file (e.g. stdout). ++ * @structure: pointer to the structure that you want to visit. ++ * @name: an element of the structure ++ * @mode: specify how much of the structure to print, can be ++ * %ASN1_PRINT_NAME, %ASN1_PRINT_NAME_TYPE, ++ * %ASN1_PRINT_NAME_TYPE_VALUE, or %ASN1_PRINT_ALL. ++ * ++ * Prints on the @out file descriptor the structure's tree starting ++ * from the @name element inside the structure @structure. ++ **/ ++void ++asn1_print_structure (FILE * out, asn1_node_const structure, const char *name, ++ int mode) ++{ ++ asn1_node_const p, root; ++ int k, indent = 0, len, len2, len3; ++ ++ if (out == NULL) ++ return; ++ ++ root = asn1_find_node (structure, name); ++ ++ if (root == NULL) ++ return; ++ ++ p = root; ++ while (p) ++ { ++ if (mode == ASN1_PRINT_ALL) ++ { ++ for (k = 0; k < indent; k++) ++ fprintf (out, " "); ++ fprintf (out, "name:"); ++ if (p->name[0] != 0) ++ fprintf (out, "%s ", p->name); ++ else ++ fprintf (out, "NULL "); ++ } ++ else ++ { ++ switch (type_field (p->type)) ++ { ++ case ASN1_ETYPE_CONSTANT: ++ case ASN1_ETYPE_TAG: ++ case ASN1_ETYPE_SIZE: ++ break; ++ default: ++ for (k = 0; k < indent; k++) ++ fprintf (out, " "); ++ fprintf (out, "name:"); ++ if (p->name[0] != 0) ++ fprintf (out, "%s ", p->name); ++ else ++ fprintf (out, "NULL "); ++ } ++ } ++ ++ if (mode != ASN1_PRINT_NAME) ++ { ++ unsigned type = type_field (p->type); ++ switch (type) ++ { ++ case ASN1_ETYPE_CONSTANT: ++ if (mode == ASN1_PRINT_ALL) ++ fprintf (out, "type:CONST"); ++ break; ++ case ASN1_ETYPE_TAG: ++ if (mode == ASN1_PRINT_ALL) ++ fprintf (out, "type:TAG"); ++ break; ++ case ASN1_ETYPE_SIZE: ++ if (mode == ASN1_PRINT_ALL) ++ fprintf (out, "type:SIZE"); ++ break; ++ case ASN1_ETYPE_DEFAULT: ++ fprintf (out, "type:DEFAULT"); ++ break; ++ case ASN1_ETYPE_IDENTIFIER: ++ fprintf (out, "type:IDENTIFIER"); ++ break; ++ case ASN1_ETYPE_ANY: ++ fprintf (out, "type:ANY"); ++ break; ++ case ASN1_ETYPE_CHOICE: ++ fprintf (out, "type:CHOICE"); ++ break; ++ case ASN1_ETYPE_DEFINITIONS: ++ fprintf (out, "type:DEFINITIONS"); ++ break; ++ CASE_HANDLED_ETYPES: ++ fprintf (out, "%s", _asn1_tags[type].desc); ++ break; ++ default: ++ break; ++ } ++ } ++ ++ if ((mode == ASN1_PRINT_NAME_TYPE_VALUE) || (mode == ASN1_PRINT_ALL)) ++ { ++ switch (type_field (p->type)) ++ { ++ case ASN1_ETYPE_CONSTANT: ++ if (mode == ASN1_PRINT_ALL) ++ if (p->value) ++ fprintf (out, " value:%s", p->value); ++ break; ++ case ASN1_ETYPE_TAG: ++ if (mode == ASN1_PRINT_ALL) ++ if (p->value) ++ fprintf (out, " value:%s", p->value); ++ break; ++ case ASN1_ETYPE_SIZE: ++ if (mode == ASN1_PRINT_ALL) ++ if (p->value) ++ fprintf (out, " value:%s", p->value); ++ break; ++ case ASN1_ETYPE_DEFAULT: ++ if (p->value) ++ fprintf (out, " value:%s", p->value); ++ else if (p->type & CONST_TRUE) ++ fprintf (out, " value:TRUE"); ++ else if (p->type & CONST_FALSE) ++ fprintf (out, " value:FALSE"); ++ break; ++ case ASN1_ETYPE_IDENTIFIER: ++ if (p->value) ++ fprintf (out, " value:%s", p->value); ++ break; ++ case ASN1_ETYPE_INTEGER: ++ if (p->value) ++ { ++ len2 = -1; ++ len = asn1_get_length_der (p->value, p->value_len, &len2); ++ fprintf (out, " value:0x"); ++ if (len > 0) ++ for (k = 0; k < len; k++) ++ fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); ++ } ++ break; ++ case ASN1_ETYPE_ENUMERATED: ++ if (p->value) ++ { ++ len2 = -1; ++ len = asn1_get_length_der (p->value, p->value_len, &len2); ++ fprintf (out, " value:0x"); ++ if (len > 0) ++ for (k = 0; k < len; k++) ++ fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); ++ } ++ break; ++ case ASN1_ETYPE_BOOLEAN: ++ if (p->value) ++ { ++ if (p->value[0] == 'T') ++ fprintf (out, " value:TRUE"); ++ else if (p->value[0] == 'F') ++ fprintf (out, " value:FALSE"); ++ } ++ break; ++ case ASN1_ETYPE_BIT_STRING: ++ if (p->value) ++ { ++ len2 = -1; ++ len = asn1_get_length_der (p->value, p->value_len, &len2); ++ if (len > 0) ++ { ++ fprintf (out, " value(%i):", ++ (len - 1) * 8 - (p->value[len2])); ++ for (k = 1; k < len; k++) ++ fprintf (out, "%02x", ++ (unsigned) (p->value)[k + len2]); ++ } ++ } ++ break; ++ case ASN1_ETYPE_GENERALIZED_TIME: ++ case ASN1_ETYPE_UTC_TIME: ++ if (p->value) ++ { ++ fprintf (out, " value:"); ++ for (k = 0; k < p->value_len; k++) ++ fprintf (out, "%c", (p->value)[k]); ++ } ++ break; ++ case ASN1_ETYPE_GENERALSTRING: ++ case ASN1_ETYPE_NUMERIC_STRING: ++ case ASN1_ETYPE_IA5_STRING: ++ case ASN1_ETYPE_TELETEX_STRING: ++ case ASN1_ETYPE_PRINTABLE_STRING: ++ case ASN1_ETYPE_UNIVERSAL_STRING: ++ case ASN1_ETYPE_UTF8_STRING: ++ case ASN1_ETYPE_VISIBLE_STRING: ++ if (p->value) ++ { ++ len2 = -1; ++ len = asn1_get_length_der (p->value, p->value_len, &len2); ++ fprintf (out, " value:"); ++ if (len > 0) ++ for (k = 0; k < len; k++) ++ fprintf (out, "%c", (p->value)[k + len2]); ++ } ++ break; ++ case ASN1_ETYPE_BMP_STRING: ++ case ASN1_ETYPE_OCTET_STRING: ++ if (p->value) ++ { ++ len2 = -1; ++ len = asn1_get_length_der (p->value, p->value_len, &len2); ++ fprintf (out, " value:"); ++ if (len > 0) ++ for (k = 0; k < len; k++) ++ fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); ++ } ++ break; ++ case ASN1_ETYPE_OBJECT_ID: ++ if (p->value) ++ fprintf (out, " value:%s", p->value); ++ break; ++ case ASN1_ETYPE_ANY: ++ if (p->value) ++ { ++ len3 = -1; ++ len2 = asn1_get_length_der (p->value, p->value_len, &len3); ++ fprintf (out, " value:"); ++ if (len2 > 0) ++ for (k = 0; k < len2; k++) ++ fprintf (out, "%02x", (unsigned) (p->value)[k + len3]); ++ } ++ break; ++ case ASN1_ETYPE_SET: ++ case ASN1_ETYPE_SET_OF: ++ case ASN1_ETYPE_CHOICE: ++ case ASN1_ETYPE_DEFINITIONS: ++ case ASN1_ETYPE_SEQUENCE_OF: ++ case ASN1_ETYPE_SEQUENCE: ++ case ASN1_ETYPE_NULL: ++ break; ++ default: ++ break; ++ } ++ } ++ ++ if (mode == ASN1_PRINT_ALL) ++ { ++ if (p->type & 0x1FFFFF00) ++ { ++ fprintf (out, " attr:"); ++ if (p->type & CONST_UNIVERSAL) ++ fprintf (out, "UNIVERSAL,"); ++ if (p->type & CONST_PRIVATE) ++ fprintf (out, "PRIVATE,"); ++ if (p->type & CONST_APPLICATION) ++ fprintf (out, "APPLICATION,"); ++ if (p->type & CONST_EXPLICIT) ++ fprintf (out, "EXPLICIT,"); ++ if (p->type & CONST_IMPLICIT) ++ fprintf (out, "IMPLICIT,"); ++ if (p->type & CONST_TAG) ++ fprintf (out, "TAG,"); ++ if (p->type & CONST_DEFAULT) ++ fprintf (out, "DEFAULT,"); ++ if (p->type & CONST_TRUE) ++ fprintf (out, "TRUE,"); ++ if (p->type & CONST_FALSE) ++ fprintf (out, "FALSE,"); ++ if (p->type & CONST_LIST) ++ fprintf (out, "LIST,"); ++ if (p->type & CONST_MIN_MAX) ++ fprintf (out, "MIN_MAX,"); ++ if (p->type & CONST_OPTION) ++ fprintf (out, "OPTION,"); ++ if (p->type & CONST_1_PARAM) ++ fprintf (out, "1_PARAM,"); ++ if (p->type & CONST_SIZE) ++ fprintf (out, "SIZE,"); ++ if (p->type & CONST_DEFINED_BY) ++ fprintf (out, "DEF_BY,"); ++ if (p->type & CONST_GENERALIZED) ++ fprintf (out, "GENERALIZED,"); ++ if (p->type & CONST_UTC) ++ fprintf (out, "UTC,"); ++ if (p->type & CONST_SET) ++ fprintf (out, "SET,"); ++ if (p->type & CONST_NOT_USED) ++ fprintf (out, "NOT_USED,"); ++ if (p->type & CONST_ASSIGN) ++ fprintf (out, "ASSIGNMENT,"); ++ } ++ } ++ ++ if (mode == ASN1_PRINT_ALL) ++ { ++ fprintf (out, "\n"); ++ } ++ else ++ { ++ switch (type_field (p->type)) ++ { ++ case ASN1_ETYPE_CONSTANT: ++ case ASN1_ETYPE_TAG: ++ case ASN1_ETYPE_SIZE: ++ break; ++ default: ++ fprintf (out, "\n"); ++ } ++ } ++ ++ if (p->down) ++ { ++ p = p->down; ++ indent += 2; ++ } ++ else if (p == root) ++ { ++ p = NULL; ++ break; ++ } ++ else if (p->right) ++ p = p->right; ++ else ++ { ++ while (1) ++ { ++ p = _asn1_find_up (p); ++ if (p == root) ++ { ++ p = NULL; ++ break; ++ } ++ indent -= 2; ++ if (p->right) ++ { ++ p = p->right; ++ break; ++ } ++ } ++ } ++ } ++} ++ ++ ++ ++/** ++ * asn1_number_of_elements: ++ * @element: pointer to the root of an ASN1 structure. ++ * @name: the name of a sub-structure of ROOT. ++ * @num: pointer to an integer where the result will be stored ++ * ++ * Counts the number of elements of a sub-structure called NAME with ++ * names equal to "?1","?2", ... ++ * ++ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if ++ * @name is not known, %ASN1_GENERIC_ERROR if pointer @num is %NULL. ++ **/ ++int ++asn1_number_of_elements (asn1_node_const element, const char *name, int *num) ++{ ++ asn1_node_const node, p; ++ ++ if (num == NULL) ++ return ASN1_GENERIC_ERROR; ++ ++ *num = 0; ++ ++ node = asn1_find_node (element, name); ++ if (node == NULL) ++ return ASN1_ELEMENT_NOT_FOUND; ++ ++ p = node->down; ++ ++ while (p) ++ { ++ if (p->name[0] == '?') ++ (*num)++; ++ p = p->right; ++ } ++ ++ return ASN1_SUCCESS; ++} ++ ++ ++/** ++ * asn1_find_structure_from_oid: ++ * @definitions: ASN1 definitions ++ * @oidValue: value of the OID to search (e.g. "1.2.3.4"). ++ * ++ * Search the structure that is defined just after an OID definition. ++ * ++ * Returns: %NULL when @oidValue not found, otherwise the pointer to a ++ * constant string that contains the element name defined just after ++ * the OID. ++ **/ ++const char * ++asn1_find_structure_from_oid (asn1_node_const definitions, ++ const char *oidValue) ++{ ++ char name[2 * ASN1_MAX_NAME_SIZE + 2]; ++ char value[ASN1_MAX_NAME_SIZE]; ++ asn1_node p; ++ int len; ++ int result; ++ const char *definitionsName; ++ ++ if ((definitions == NULL) || (oidValue == NULL)) ++ return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ ++ ++ definitionsName = definitions->name; ++ ++ /* search the OBJECT_ID into definitions */ ++ p = definitions->down; ++ while (p) ++ { ++ if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && ++ (p->type & CONST_ASSIGN)) ++ { ++ snprintf (name, sizeof (name), "%s.%s", definitionsName, p->name); ++ ++ len = ASN1_MAX_NAME_SIZE; ++ result = asn1_read_value (definitions, name, value, &len); ++ ++ if ((result == ASN1_SUCCESS) && (!strcmp (oidValue, value))) ++ { ++ p = p->right; ++ if (p == NULL) /* reach the end of ASN1 definitions */ ++ return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ ++ ++ return p->name; ++ } ++ } ++ p = p->right; ++ } ++ ++ return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ ++} ++ ++/** ++ * asn1_copy_node: ++ * @dst: Destination asn1 node. ++ * @dst_name: Field name in destination node. ++ * @src: Source asn1 node. ++ * @src_name: Field name in source node. ++ * ++ * Create a deep copy of a asn1_node variable. That ++ * function requires @dst to be expanded using asn1_create_element(). ++ * ++ * Returns: Return %ASN1_SUCCESS on success. ++ **/ ++int ++asn1_copy_node (asn1_node dst, const char *dst_name, ++ asn1_node_const src, const char *src_name) ++{ ++ int result; ++ asn1_node dst_node; ++ void *data = NULL; ++ int size = 0; ++ ++ result = asn1_der_coding (src, src_name, NULL, &size, NULL); ++ if (result != ASN1_MEM_ERROR) ++ return result; ++ ++ data = malloc (size); ++ if (data == NULL) ++ return ASN1_MEM_ERROR; ++ ++ result = asn1_der_coding (src, src_name, data, &size, NULL); ++ if (result != ASN1_SUCCESS) ++ { ++ free (data); ++ return result; ++ } ++ ++ dst_node = asn1_find_node (dst, dst_name); ++ if (dst_node == NULL) ++ { ++ free (data); ++ return ASN1_ELEMENT_NOT_FOUND; ++ } ++ ++ result = asn1_der_decoding (&dst_node, data, size, NULL); ++ ++ free (data); ++ ++ return result; ++} ++ ++/** ++ * asn1_dup_node: ++ * @src: Source asn1 node. ++ * @src_name: Field name in source node. ++ * ++ * Create a deep copy of a asn1_node variable. This function ++ * will return an exact copy of the provided structure. ++ * ++ * Returns: Return %NULL on failure. ++ **/ ++asn1_node ++asn1_dup_node (asn1_node_const src, const char *src_name) ++{ ++ return _asn1_copy_structure2 (src, src_name); ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1/lib/structure.h +@@ -0,0 +1,46 @@ ++/* ++ * Copyright (C) 2002-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * The LIBTASN1 library is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ */ ++ ++/*************************************************/ ++/* File: structure.h */ ++/* Description: list of exported object by */ ++/* "structure.c" */ ++/*************************************************/ ++ ++#ifndef _STRUCTURE_H ++# define _STRUCTURE_H ++ ++# include "parser_aux.h" // list_type ++ ++int _asn1_create_static_structure (asn1_node_const pointer, ++ char *output_file_name, char *vector_name); ++ ++asn1_node _asn1_copy_structure3 (asn1_node_const source_node); ++ ++asn1_node _asn1_add_single_node (unsigned int type); ++ ++asn1_node _asn1_find_left (asn1_node_const node); ++ ++int ++_asn1_delete_structure (list_type * e_list, asn1_node * structure, ++ unsigned int flags); ++ ++#endif +--- /dev/null ++++ b/include/grub/libtasn1.h +@@ -0,0 +1,639 @@ ++/* ++ * Copyright (C) 2002-2021 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * LIBTASN1 is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Lesser General Public License as ++ * published by the Free Software Foundation; either version 2.1 of ++ * the License, or (at your option) any later version. ++ * ++ * LIBTASN1 is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with LIBTASN1; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * ++ */ ++ ++/** ++ * SECTION:libtasn1 ++ * @short_description: GNU ASN.1 library ++ * ++ * The Libtasn1 library provides Abstract Syntax Notation One (ASN.1, as ++ * specified by the X.680 ITU-T recommendation) parsing and structures ++ * management, and Distinguished Encoding Rules (DER, as per X.690) ++ * encoding and decoding functions. ++ */ ++ ++ ++#ifndef LIBTASN1_H ++# define LIBTASN1_H ++ ++# ifndef ASN1_API ++# if defined ASN1_BUILDING && defined HAVE_VISIBILITY && HAVE_VISIBILITY ++# define ASN1_API __attribute__((__visibility__("default"))) ++# elif defined ASN1_BUILDING && defined _MSC_VER && ! defined ASN1_STATIC ++# define ASN1_API __declspec(dllexport) ++# elif defined _MSC_VER && ! defined ASN1_STATIC ++# define ASN1_API __declspec(dllimport) ++# else ++# define ASN1_API ++# endif ++# endif ++ ++# ifdef __GNUC__ ++# define __LIBTASN1_CONST__ __attribute__((const)) ++# define __LIBTASN1_PURE__ __attribute__((pure)) ++# else ++# define __LIBTASN1_CONST__ ++# define __LIBTASN1_PURE__ ++# endif ++ ++# include ++# include ++# include /* for FILE* */ ++ ++# ifdef __cplusplus ++extern "C" ++{ ++# endif ++ ++/** ++ * ASN1_VERSION: ++ * ++ * Version of the library as a string. ++ */ ++# define ASN1_VERSION "4.18.0" ++ ++/** ++ * ASN1_VERSION_MAJOR: ++ * ++ * Major version number of the library. ++ */ ++# define ASN1_VERSION_MAJOR 4 ++ ++/** ++ * ASN1_VERSION_MINOR: ++ * ++ * Minor version number of the library. ++ */ ++# define ASN1_VERSION_MINOR 18 ++ ++/** ++ * ASN1_VERSION_PATCH: ++ * ++ * Patch version number of the library. ++ */ ++# define ASN1_VERSION_PATCH 0 ++ ++/** ++ * ASN1_VERSION_NUMBER: ++ * ++ * Version number of the library as a number. ++ */ ++# define ASN1_VERSION_NUMBER 0x041200 ++ ++ ++# if defined __GNUC__ && !defined ASN1_INTERNAL_BUILD ++# define _ASN1_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) ++# if _ASN1_GCC_VERSION >= 30100 ++# define _ASN1_GCC_ATTR_DEPRECATED __attribute__ ((__deprecated__)) ++# endif ++# endif ++ ++# ifndef _ASN1_GCC_ATTR_DEPRECATED ++# define _ASN1_GCC_ATTR_DEPRECATED ++# endif ++ ++/*****************************************/ ++/* Errors returned by libtasn1 functions */ ++/*****************************************/ ++# define ASN1_SUCCESS 0 ++# define ASN1_FILE_NOT_FOUND 1 ++# define ASN1_ELEMENT_NOT_FOUND 2 ++# define ASN1_IDENTIFIER_NOT_FOUND 3 ++# define ASN1_DER_ERROR 4 ++# define ASN1_VALUE_NOT_FOUND 5 ++# define ASN1_GENERIC_ERROR 6 ++# define ASN1_VALUE_NOT_VALID 7 ++# define ASN1_TAG_ERROR 8 ++# define ASN1_TAG_IMPLICIT 9 ++# define ASN1_ERROR_TYPE_ANY 10 ++# define ASN1_SYNTAX_ERROR 11 ++# define ASN1_MEM_ERROR 12 ++# define ASN1_MEM_ALLOC_ERROR 13 ++# define ASN1_DER_OVERFLOW 14 ++# define ASN1_NAME_TOO_LONG 15 ++# define ASN1_ARRAY_ERROR 16 ++# define ASN1_ELEMENT_NOT_EMPTY 17 ++# define ASN1_TIME_ENCODING_ERROR 18 ++# define ASN1_RECURSION 19 ++ ++/*************************************/ ++/* Constants used in asn1_visit_tree */ ++/*************************************/ ++# define ASN1_PRINT_NAME 1 ++# define ASN1_PRINT_NAME_TYPE 2 ++# define ASN1_PRINT_NAME_TYPE_VALUE 3 ++# define ASN1_PRINT_ALL 4 ++ ++/*****************************************/ ++/* Constants returned by asn1_read_tag */ ++/*****************************************/ ++# define ASN1_CLASS_UNIVERSAL 0x00 /* old: 1 */ ++# define ASN1_CLASS_APPLICATION 0x40 /* old: 2 */ ++# define ASN1_CLASS_CONTEXT_SPECIFIC 0x80 /* old: 3 */ ++# define ASN1_CLASS_PRIVATE 0xC0 /* old: 4 */ ++# define ASN1_CLASS_STRUCTURED 0x20 ++ ++/*****************************************/ ++/* Constants returned by asn1_read_tag */ ++/*****************************************/ ++# define ASN1_TAG_BOOLEAN 0x01 ++# define ASN1_TAG_INTEGER 0x02 ++# define ASN1_TAG_SEQUENCE 0x10 ++# define ASN1_TAG_SET 0x11 ++# define ASN1_TAG_OCTET_STRING 0x04 ++# define ASN1_TAG_BIT_STRING 0x03 ++# define ASN1_TAG_UTCTime 0x17 ++# define ASN1_TAG_GENERALIZEDTime 0x18 ++# define ASN1_TAG_OBJECT_ID 0x06 ++# define ASN1_TAG_ENUMERATED 0x0A ++# define ASN1_TAG_NULL 0x05 ++# define ASN1_TAG_GENERALSTRING 0x1B ++# define ASN1_TAG_NUMERIC_STRING 0x12 ++# define ASN1_TAG_IA5_STRING 0x16 ++# define ASN1_TAG_TELETEX_STRING 0x14 ++# define ASN1_TAG_PRINTABLE_STRING 0x13 ++# define ASN1_TAG_UNIVERSAL_STRING 0x1C ++# define ASN1_TAG_BMP_STRING 0x1E ++# define ASN1_TAG_UTF8_STRING 0x0C ++# define ASN1_TAG_VISIBLE_STRING 0x1A ++ ++/** ++ * asn1_node: ++ * ++ * Structure definition used for the node of the tree ++ * that represents an ASN.1 DEFINITION. ++ */ ++ typedef struct asn1_node_st asn1_node_st; ++ ++ typedef asn1_node_st *asn1_node; ++ typedef const asn1_node_st *asn1_node_const; ++ ++/** ++ * ASN1_MAX_NAME_SIZE: ++ * ++ * Maximum number of characters of a name ++ * inside a file with ASN1 definitions. ++ */ ++# define ASN1_MAX_NAME_SIZE 64 ++ ++ ++/** ++ * asn1_static_node: ++ * @name: Node name ++ * @type: Node typ ++ * @value: Node value ++ * ++ * For the on-disk format of ASN.1 trees, created by asn1_parser2array(). ++ */ ++ typedef struct asn1_static_node_st ++ { ++ const char *name; /* Node name */ ++ unsigned int type; /* Node type */ ++ const void *value; /* Node value */ ++ } asn1_static_node; ++ ++/* List of constants for field type of asn1_static_node */ ++# define ASN1_ETYPE_INVALID 0 ++# define ASN1_ETYPE_CONSTANT 1 ++# define ASN1_ETYPE_IDENTIFIER 2 ++# define ASN1_ETYPE_INTEGER 3 ++# define ASN1_ETYPE_BOOLEAN 4 ++# define ASN1_ETYPE_SEQUENCE 5 ++# define ASN1_ETYPE_BIT_STRING 6 ++# define ASN1_ETYPE_OCTET_STRING 7 ++# define ASN1_ETYPE_TAG 8 ++# define ASN1_ETYPE_DEFAULT 9 ++# define ASN1_ETYPE_SIZE 10 ++# define ASN1_ETYPE_SEQUENCE_OF 11 ++# define ASN1_ETYPE_OBJECT_ID 12 ++# define ASN1_ETYPE_ANY 13 ++# define ASN1_ETYPE_SET 14 ++# define ASN1_ETYPE_SET_OF 15 ++# define ASN1_ETYPE_DEFINITIONS 16 ++# define ASN1_ETYPE_CHOICE 18 ++# define ASN1_ETYPE_IMPORTS 19 ++# define ASN1_ETYPE_NULL 20 ++# define ASN1_ETYPE_ENUMERATED 21 ++# define ASN1_ETYPE_GENERALSTRING 27 ++# define ASN1_ETYPE_NUMERIC_STRING 28 ++# define ASN1_ETYPE_IA5_STRING 29 ++# define ASN1_ETYPE_TELETEX_STRING 30 ++# define ASN1_ETYPE_PRINTABLE_STRING 31 ++# define ASN1_ETYPE_UNIVERSAL_STRING 32 ++# define ASN1_ETYPE_BMP_STRING 33 ++# define ASN1_ETYPE_UTF8_STRING 34 ++# define ASN1_ETYPE_VISIBLE_STRING 35 ++# define ASN1_ETYPE_UTC_TIME 36 ++# define ASN1_ETYPE_GENERALIZED_TIME 37 ++ ++/** ++ * ASN1_DELETE_FLAG_ZEROIZE: ++ * ++ * Used by: asn1_delete_structure2() ++ * ++ * Zeroize values prior to deinitialization. ++ */ ++# define ASN1_DELETE_FLAG_ZEROIZE 1 ++ ++/** ++ * ASN1_DECODE_FLAG_ALLOW_PADDING: ++ * ++ * Used by: asn1_der_decoding2() ++ * ++ * This flag would allow arbitrary data past the DER data. ++ */ ++# define ASN1_DECODE_FLAG_ALLOW_PADDING 1 ++/** ++ * ASN1_DECODE_FLAG_STRICT_DER: ++ * ++ * Used by: asn1_der_decoding2() ++ * ++ * This flag would ensure that no BER decoding takes place. ++ */ ++# define ASN1_DECODE_FLAG_STRICT_DER (1<<1) ++/** ++ * ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME: ++ * ++ * Used by: asn1_der_decoding2() ++ * ++ * This flag will tolerate Time encoding errors when in strict DER. ++ */ ++# define ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME (1<<2) ++ ++ ++/** ++ * asn1_data_node_st: ++ * @name: Node name ++ * @value: Node value ++ * @value_len: Node value size ++ * @type: Node value type (ASN1_ETYPE_*) ++ * ++ * Data node inside a #asn1_node structure. ++ */ ++ struct asn1_data_node_st ++ { ++ const char *name; /* Node name */ ++ const void *value; /* Node value */ ++ unsigned int value_len; /* Node value size */ ++ unsigned int type; /* Node value type (ASN1_ETYPE_*) */ ++ }; ++ typedef struct asn1_data_node_st asn1_data_node_st; ++ ++/***********************************/ ++/* Fixed constants */ ++/***********************************/ ++ ++/** ++ * ASN1_MAX_ERROR_DESCRIPTION_SIZE: ++ * ++ * Maximum number of characters ++ * of a description message ++ * (null character included). ++ */ ++# define ASN1_MAX_ERROR_DESCRIPTION_SIZE 128 ++ ++/***********************************/ ++/* Functions definitions */ ++/***********************************/ ++ ++ extern ASN1_API int ++ asn1_parser2tree (const char *file, ++ asn1_node * definitions, char *error_desc); ++ ++ extern ASN1_API int ++ asn1_parser2array (const char *inputFileName, ++ const char *outputFileName, ++ const char *vectorName, char *error_desc); ++ ++ extern ASN1_API int ++ asn1_array2tree (const asn1_static_node * array, ++ asn1_node * definitions, char *errorDescription); ++ ++ extern ASN1_API void ++ asn1_print_structure (FILE * out, asn1_node_const structure, ++ const char *name, int mode); ++ ++ extern ASN1_API int ++ asn1_create_element (asn1_node_const definitions, ++ const char *source_name, asn1_node * element); ++ ++ extern ASN1_API int asn1_delete_structure (asn1_node * structure); ++ ++ extern ASN1_API int asn1_delete_structure2 (asn1_node * structure, ++ unsigned int flags); ++ ++ extern ASN1_API int ++ asn1_delete_element (asn1_node structure, const char *element_name); ++ ++ extern ASN1_API int ++ asn1_write_value (asn1_node node_root, const char *name, ++ const void *ivalue, int len); ++ ++ extern ASN1_API int ++ asn1_read_value (asn1_node_const root, const char *name, ++ void *ivalue, int *len); ++ ++ extern ASN1_API int ++ asn1_read_value_type (asn1_node_const root, const char *name, ++ void *ivalue, int *len, unsigned int *etype); ++ ++ extern ASN1_API int ++ asn1_read_node_value (asn1_node_const node, asn1_data_node_st * data); ++ ++ extern ASN1_API int ++ asn1_number_of_elements (asn1_node_const element, const char *name, ++ int *num); ++ ++ extern ASN1_API int ++ asn1_der_coding (asn1_node_const element, const char *name, ++ void *ider, int *len, char *ErrorDescription); ++ ++ extern ASN1_API int ++ asn1_der_decoding2 (asn1_node * element, const void *ider, ++ int *max_ider_len, unsigned int flags, ++ char *errorDescription); ++ ++ extern ASN1_API int ++ asn1_der_decoding (asn1_node * element, const void *ider, ++ int ider_len, char *errorDescription); ++ ++/* Do not use. Use asn1_der_decoding() instead. */ ++ extern ASN1_API int ++ asn1_der_decoding_element (asn1_node * structure, ++ const char *elementName, ++ const void *ider, int len, ++ char *errorDescription) ++ _ASN1_GCC_ATTR_DEPRECATED; ++ ++ extern ASN1_API int ++ asn1_der_decoding_startEnd (asn1_node element, ++ const void *ider, int ider_len, ++ const char *name_element, ++ int *start, int *end); ++ ++ extern ASN1_API int ++ asn1_expand_any_defined_by (asn1_node_const definitions, ++ asn1_node * element); ++ ++ extern ASN1_API int ++ asn1_expand_octet_string (asn1_node_const definitions, ++ asn1_node * element, ++ const char *octetName, const char *objectName); ++ ++ extern ASN1_API int ++ asn1_read_tag (asn1_node_const root, const char *name, ++ int *tagValue, int *classValue); ++ ++ extern ASN1_API const char *asn1_find_structure_from_oid (asn1_node_const ++ definitions, ++ const char ++ *oidValue); ++ ++ __LIBTASN1_PURE__ ++ extern ASN1_API const char *asn1_check_version (const char *req_version); ++ ++ __LIBTASN1_PURE__ extern ASN1_API const char *asn1_strerror (int error); ++ ++ extern ASN1_API void asn1_perror (int error); ++ ++# define ASN1_MAX_TAG_SIZE 4 ++# define ASN1_MAX_LENGTH_SIZE 9 ++# define ASN1_MAX_TL_SIZE (ASN1_MAX_TAG_SIZE+ASN1_MAX_LENGTH_SIZE) ++ extern ASN1_API long ++ asn1_get_length_der (const unsigned char *der, int der_len, int *len); ++ ++ extern ASN1_API long ++ asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len); ++ ++ extern ASN1_API void ++ asn1_length_der (unsigned long int len, unsigned char *der, int *der_len); ++ ++/* Other utility functions. */ ++ ++ extern ASN1_API ++ int asn1_decode_simple_der (unsigned int etype, const unsigned char *der, ++ unsigned int _der_len, ++ const unsigned char **str, ++ unsigned int *str_len); ++ ++ extern ASN1_API ++ int asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, ++ unsigned int _der_len, ++ unsigned char **str, ++ unsigned int *str_len, unsigned int *ber_len); ++ ++ extern ASN1_API int ++ asn1_encode_simple_der (unsigned int etype, const unsigned char *str, ++ unsigned int str_len, unsigned char *tl, ++ unsigned int *tl_len); ++ ++ extern ASN1_API asn1_node ++ asn1_find_node (asn1_node_const pointer, const char *name); ++ ++ extern ASN1_API int ++ asn1_copy_node (asn1_node dst, const char *dst_name, ++ asn1_node_const src, const char *src_name); ++ extern ASN1_API asn1_node ++ asn1_dup_node (asn1_node_const src, const char *src_name); ++ ++/* Internal and low-level DER utility functions. */ ++ ++ extern ASN1_API int ++ asn1_get_tag_der (const unsigned char *der, int der_len, ++ unsigned char *cls, int *len, unsigned long *tag); ++ ++ extern ASN1_API void ++ asn1_octet_der (const unsigned char *str, int str_len, ++ unsigned char *der, int *der_len); ++ ++ extern ASN1_API int ++ asn1_get_octet_der (const unsigned char *der, int der_len, ++ int *ret_len, unsigned char *str, ++ int str_size, int *str_len); ++ ++ extern ASN1_API void asn1_bit_der (const unsigned char *str, int bit_len, ++ unsigned char *der, int *der_len); ++ ++ extern ASN1_API int ++ asn1_get_bit_der (const unsigned char *der, int der_len, ++ int *ret_len, unsigned char *str, ++ int str_size, int *bit_len); ++ ++ extern ASN1_API int ++ asn1_get_object_id_der (const unsigned char *der, ++ int der_len, int *ret_len, ++ char *str, int str_size); ++ ++ extern ASN1_API int ++ asn1_object_id_der (const char *str, unsigned char *der, int *der_len, ++ unsigned flags); ++ ++/* Compatibility types */ ++ ++/** ++ * asn1_retCode: ++ * ++ * Type formerly returned by libtasn1 functions. ++ * ++ * Deprecated: 3.0: Use int instead. ++ */ ++ typedef int asn1_retCode _ASN1_GCC_ATTR_DEPRECATED; ++ ++/** ++ * node_asn_struct: ++ * ++ * Compat #define. ++ * ++ * Deprecated: 3.0: Use #asn1_node instead. ++ */ ++# ifndef ASN1_DISABLE_DEPRECATED ++# if _ASN1_GCC_VERSION >= 30100 ++# define node_asn_struct _Pragma ("GCC warning \"'node_asn_struct' macro is deprecated, use 'asn1_node' instead.\"") asn1_node_st ++# else ++# define node_asn_struct asn1_node_st ++# endif ++# endif /* !ASN1_DISABLE_DEPRECATED */ ++ ++/** ++ * node_asn: ++ * ++ * Compat #define. ++ * ++ * Deprecated: 3.0: Use #asn1_node instead. ++ */ ++# ifndef ASN1_DISABLE_DEPRECATED ++# if _ASN1_GCC_VERSION >= 30100 ++# define node_asn _Pragma ("GCC warning \"'node_asn' macro is deprecated, use 'asn1_node' instead.\"") asn1_node_st ++# else ++# define node_asn asn1_node_st ++# endif ++# endif /* !ASN1_DISABLE_DEPRECATED */ ++ ++/** ++ * ASN1_TYPE: ++ * ++ * Compat #define. ++ * ++ * Deprecated: 3.0: Use #asn1_node instead. ++ */ ++# ifndef ASN1_DISABLE_DEPRECATED ++# if _ASN1_GCC_VERSION >= 30100 ++# define ASN1_TYPE _Pragma ("GCC warning \"'ASN1_TYPE' macro is deprecated, use 'asn1_node' instead.\"") asn1_node ++# else ++# define ASN1_TYPE asn1_node ++# endif ++# endif /* !ASN1_DISABLE_DEPRECATED */ ++ ++/** ++ * ASN1_TYPE_EMPTY: ++ * ++ * Compat #define. ++ * ++ * Deprecated: 3.0: Use NULL instead. ++ */ ++# ifndef ASN1_DISABLE_DEPRECATED ++# if _ASN1_GCC_VERSION >= 30100 ++# define ASN1_TYPE_EMPTY _Pragma ("GCC warning \"'ASN1_TYPE_EMPTY' macro is deprecated, use 'NULL' instead.\"") NULL ++# else ++# define ASN1_TYPE_EMPTY NULL ++# endif ++# endif /* !ASN1_DISABLE_DEPRECATED */ ++ ++/** ++ * static_struct_asn: ++ * ++ * Compat #define. ++ * ++ * Deprecated: 3.0: Use #asn1_static_node instead. ++ */ ++# ifndef ASN1_DISABLE_DEPRECATED ++# if _ASN1_GCC_VERSION >= 30100 ++# define static_struct_asn _Pragma ("GCC warning \"'static_struct_asn' macro is deprecated, use 'asn1_static_node_st' instead.\"") asn1_static_node_st ++# else ++# define static_struct_asn asn1_static_node_st ++# endif ++# endif /* !ASN1_DISABLE_DEPRECATED */ ++ ++/** ++ * ASN1_ARRAY_TYPE: ++ * ++ * Compat #define. ++ * ++ * Deprecated: 3.0: Use #asn1_static_node instead. ++ */ ++# ifndef ASN1_DISABLE_DEPRECATED ++# if _ASN1_GCC_VERSION >= 30100 ++# define ASN1_ARRAY_TYPE _Pragma ("GCC warning \"'ASN1_ARRAY_TYPE' macro is deprecated, use 'asn1_static_node' instead.\"") asn1_static_node ++# else ++# define ASN1_ARRAY_TYPE asn1_static_node ++# endif ++# endif /* !ASN1_DISABLE_DEPRECATED */ ++ ++/** ++ * asn1_static_node_t: ++ * ++ * Compat #define. ++ * ++ * Deprecated: 3.0: Use #asn1_static_node instead. ++ */ ++# ifndef ASN1_DISABLE_DEPRECATED ++# if _ASN1_GCC_VERSION >= 30100 ++# define asn1_static_node_t _Pragma ("GCC warning \"'asn1_static_node_t' macro is deprecated, use 'asn1_static_node' instead.\"") asn1_static_node ++# else ++# define asn1_static_node_t asn1_static_node ++# endif ++# endif /* !ASN1_DISABLE_DEPRECATED */ ++ ++/** ++ * node_data_struct: ++ * ++ * Compat #define. ++ * ++ * Deprecated: 3.0: Use #asn1_data_node_st instead. ++ */ ++# ifndef ASN1_DISABLE_DEPRECATED ++# if _ASN1_GCC_VERSION >= 30100 ++# define node_data_struct _Pragma ("GCC warning \"'node_data_struct' macro is deprecated, use 'asn1_data_node_st' instead.\"") asn1_data_node_st ++# else ++# define node_data_struct asn1_data_node_st ++# endif ++# endif /* !ASN1_DISABLE_DEPRECATED */ ++ ++/** ++ * ASN1_DATA_NODE: ++ * ++ * Compat #define. ++ * ++ * Deprecated: 3.0: Use #asn1_data_node_st instead. ++ */ ++# ifndef ASN1_DISABLE_DEPRECATED ++# if _ASN1_GCC_VERSION >= 30100 ++# define ASN1_DATA_NODE _Pragma ("GCC warning \"'asn1_static_node_t' macro is deprecated, use 'asn1_static_node' instead.\"") asn1_data_node_st ++# else ++# define ASN1_DATA_NODE asn1_data_node_st ++# endif ++# endif /* !ASN1_DISABLE_DEPRECATED */ ++ ++# ifdef __cplusplus ++} ++# endif ++ ++#endif /* LIBTASN1_H */ diff --git a/0012-libtasn1-disable-code-not-needed-in-grub.patch b/0012-libtasn1-disable-code-not-needed-in-grub.patch new file mode 100644 index 0000000..12e6d9e --- /dev/null +++ b/0012-libtasn1-disable-code-not-needed-in-grub.patch @@ -0,0 +1,310 @@ +From 40099cf0d4d68e79db9e71d78070f37c73f998a0 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Fri, 1 May 2020 17:12:23 +1000 +Subject: [PATCH 12/23] libtasn1: disable code not needed in grub + +We don't expect to be able to write ASN.1, only read it, +so we can disable some code. + +Do that with #if 0/#endif, rather than deletion. This means +that the difference between upstream and grub is smaller, +which should make updating libtasn1 easier in the future. + +With these exclusions we also avoid the need for minmax.h, +which is convenient because it means we don't have to +import it from gnulib. + +Signed-off-by: Daniel Axtens +--- + grub-core/lib/libtasn1/lib/coding.c | 12 ++++++++++-- + grub-core/lib/libtasn1/lib/decoding.c | 2 ++ + grub-core/lib/libtasn1/lib/element.c | 4 ++-- + grub-core/lib/libtasn1/lib/errors.c | 3 +++ + grub-core/lib/libtasn1/lib/structure.c | 10 ++++++---- + include/grub/libtasn1.h | 15 +++++++++++++++ + 6 files changed, 38 insertions(+), 8 deletions(-) + +diff --git a/grub-core/lib/libtasn1/lib/coding.c b/grub-core/lib/libtasn1/lib/coding.c +index 671104f63..b3d826710 100644 +--- a/grub-core/lib/libtasn1/lib/coding.c ++++ b/grub-core/lib/libtasn1/lib/coding.c +@@ -30,11 +30,11 @@ + #include "parser_aux.h" + #include + #include "element.h" +-#include "minmax.h" + #include + + #define MAX_TAG_LEN 16 + ++#if 0 + /******************************************************/ + /* Function : _asn1_error_description_value_not_found */ + /* Description: creates the ErrorDescription string */ +@@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node, + Estrcat (ErrorDescription, "' not found"); + + } ++#endif + + /** + * asn1_length_der: +@@ -244,6 +245,7 @@ asn1_encode_simple_der (unsigned int etype, const unsigned char *str, + return ASN1_SUCCESS; + } + ++#if 0 + /******************************************************/ + /* Function : _asn1_time_der */ + /* Description: creates the DER coding for a TIME */ +@@ -278,7 +280,7 @@ _asn1_time_der (unsigned char *str, int str_len, unsigned char *der, + + return ASN1_SUCCESS; + } +- ++#endif + + /* + void +@@ -519,6 +521,7 @@ asn1_bit_der (const unsigned char *str, int bit_len, + } + + ++#if 0 + /******************************************************/ + /* Function : _asn1_complete_explicit_tag */ + /* Description: add the length coding to the EXPLICIT */ +@@ -595,6 +598,7 @@ _asn1_complete_explicit_tag (asn1_node node, unsigned char *der, + + return ASN1_SUCCESS; + } ++#endif + + const tag_and_class_st _asn1_tags[] = { + [ASN1_ETYPE_GENERALSTRING] = +@@ -647,6 +651,8 @@ const tag_and_class_st _asn1_tags[] = { + + unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]); + ++ ++#if 0 + /******************************************************/ + /* Function : _asn1_insert_tag_der */ + /* Description: creates the DER coding of tags of one */ +@@ -1423,3 +1429,5 @@ error: + asn1_delete_structure (&node); + return err; + } ++ ++#endif +\ No newline at end of file +diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c +index b1a35356f..b8130b956 100644 +--- a/grub-core/lib/libtasn1/lib/decoding.c ++++ b/grub-core/lib/libtasn1/lib/decoding.c +@@ -1620,6 +1620,7 @@ asn1_der_decoding (asn1_node * element, const void *ider, int ider_len, + return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription); + } + ++#if 0 + /** + * asn1_der_decoding_element: + * @structure: pointer to an ASN1 structure +@@ -1650,6 +1651,7 @@ asn1_der_decoding_element (asn1_node * structure, const char *elementName, + { + return asn1_der_decoding (structure, ider, len, errorDescription); + } ++#endif + + /** + * asn1_der_decoding_startEnd: +diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c +index 86e64f2cf..8cd6b662c 100644 +--- a/grub-core/lib/libtasn1/lib/element.c ++++ b/grub-core/lib/libtasn1/lib/element.c +@@ -191,7 +191,7 @@ _asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache) + return ASN1_SUCCESS; + } + +- ++#if 0 + /** + * asn1_write_value: + * @node_root: pointer to a structure +@@ -646,7 +646,7 @@ asn1_write_value (asn1_node node_root, const char *name, + + return ASN1_SUCCESS; + } +- ++#endif + + #define PUT_VALUE( ptr, ptr_size, data, data_size) \ + *len = data_size; \ +diff --git a/grub-core/lib/libtasn1/lib/errors.c b/grub-core/lib/libtasn1/lib/errors.c +index 4dadbd96d..41921d813 100644 +--- a/grub-core/lib/libtasn1/lib/errors.c ++++ b/grub-core/lib/libtasn1/lib/errors.c +@@ -57,6 +57,8 @@ static const libtasn1_error_entry error_algorithms[] = { + {0, 0} + }; + ++ ++#if 0 + /** + * asn1_perror: + * @error: is an error returned by a libtasn1 function. +@@ -73,6 +75,7 @@ asn1_perror (int error) + const char *str = asn1_strerror (error); + fprintf (stderr, "LIBTASN1 ERROR: %s\n", str ? str : "(null)"); + } ++#endif + + /** + * asn1_strerror: +diff --git a/grub-core/lib/libtasn1/lib/structure.c b/grub-core/lib/libtasn1/lib/structure.c +index c0802202e..45435732c 100644 +--- a/grub-core/lib/libtasn1/lib/structure.c ++++ b/grub-core/lib/libtasn1/lib/structure.c +@@ -76,7 +76,7 @@ _asn1_find_left (asn1_node_const node) + return node->left; + } + +- ++#if 0 + int + _asn1_create_static_structure (asn1_node_const pointer, + char *output_file_name, char *vector_name) +@@ -155,7 +155,7 @@ _asn1_create_static_structure (asn1_node_const pointer, + + return ASN1_SUCCESS; + } +- ++#endif + + /** + * asn1_array2tree: +@@ -721,7 +721,7 @@ asn1_create_element (asn1_node_const definitions, const char *source_name, + return res; + } + +- ++#if 0 + /** + * asn1_print_structure: + * @out: pointer to the output file (e.g. stdout). +@@ -1062,7 +1062,7 @@ asn1_print_structure (FILE * out, asn1_node_const structure, const char *name, + } + } + } +- ++#endif + + + /** +@@ -1158,6 +1158,7 @@ asn1_find_structure_from_oid (asn1_node_const definitions, + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ + } + ++#if 0 + /** + * asn1_copy_node: + * @dst: Destination asn1 node. +@@ -1207,6 +1208,7 @@ asn1_copy_node (asn1_node dst, const char *dst_name, + + return result; + } ++#endif + + /** + * asn1_dup_node: +diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h +index fc695a28a..0c3a44881 100644 +--- a/include/grub/libtasn1.h ++++ b/include/grub/libtasn1.h +@@ -314,6 +314,8 @@ extern "C" + /* Functions definitions */ + /***********************************/ + ++/* These functions are not used in grub and should not be referenced. */ ++# if 0 + extern ASN1_API int + asn1_parser2tree (const char *file, + asn1_node * definitions, char *error_desc); +@@ -322,14 +324,17 @@ extern "C" + asn1_parser2array (const char *inputFileName, + const char *outputFileName, + const char *vectorName, char *error_desc); ++# endif + + extern ASN1_API int + asn1_array2tree (const asn1_static_node * array, + asn1_node * definitions, char *errorDescription); + ++# if 0 + extern ASN1_API void + asn1_print_structure (FILE * out, asn1_node_const structure, + const char *name, int mode); ++# endif + + extern ASN1_API int + asn1_create_element (asn1_node_const definitions, +@@ -343,9 +348,11 @@ extern "C" + extern ASN1_API int + asn1_delete_element (asn1_node structure, const char *element_name); + ++# if 0 + extern ASN1_API int + asn1_write_value (asn1_node node_root, const char *name, + const void *ivalue, int len); ++# endif + + extern ASN1_API int + asn1_read_value (asn1_node_const root, const char *name, +@@ -362,9 +369,11 @@ extern "C" + asn1_number_of_elements (asn1_node_const element, const char *name, + int *num); + ++# if 0 + extern ASN1_API int + asn1_der_coding (asn1_node_const element, const char *name, + void *ider, int *len, char *ErrorDescription); ++# endif + + extern ASN1_API int + asn1_der_decoding2 (asn1_node * element, const void *ider, +@@ -375,6 +384,7 @@ extern "C" + asn1_der_decoding (asn1_node * element, const void *ider, + int ider_len, char *errorDescription); + ++# if 0 + /* Do not use. Use asn1_der_decoding() instead. */ + extern ASN1_API int + asn1_der_decoding_element (asn1_node * structure, +@@ -382,6 +392,7 @@ extern "C" + const void *ider, int len, + char *errorDescription) + _ASN1_GCC_ATTR_DEPRECATED; ++# endif + + extern ASN1_API int + asn1_der_decoding_startEnd (asn1_node element, +@@ -407,12 +418,16 @@ extern "C" + const char + *oidValue); + ++# if 0 + __LIBTASN1_PURE__ + extern ASN1_API const char *asn1_check_version (const char *req_version); ++# endif + + __LIBTASN1_PURE__ extern ASN1_API const char *asn1_strerror (int error); + ++# if 0 + extern ASN1_API void asn1_perror (int error); ++# endif + + # define ASN1_MAX_TAG_SIZE 4 + # define ASN1_MAX_LENGTH_SIZE 9 +-- +2.31.1 + diff --git a/0012-tpm-Build-tpm-as-module.patch b/0012-tpm-Build-tpm-as-module.patch new file mode 100644 index 0000000..4b15a27 --- /dev/null +++ b/0012-tpm-Build-tpm-as-module.patch @@ -0,0 +1,54 @@ +From 54b6ba5f27dd9eb9ec2f1a41e7160964ab94451c Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Wed, 23 Nov 2016 16:52:16 +0800 +Subject: Build tpm as module + +Add --suse-enable-tpm option to grub2-install. + +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -81,6 +81,7 @@ + static char *label_bgcolor; + static char *product_version; + static int add_rs_codes = 1; ++static int suse_enable_tpm = 0; + + enum + { +@@ -107,6 +108,7 @@ + OPTION_DISK_MODULE, + OPTION_NO_BOOTSECTOR, + OPTION_NO_RS_CODES, ++ OPTION_SUSE_ENABLE_TPM, + OPTION_MACPPC_DIRECTORY, + OPTION_ZIPL_DIRECTORY, + OPTION_LABEL_FONT, +@@ -232,6 +234,10 @@ + add_rs_codes = 0; + return 0; + ++ case OPTION_SUSE_ENABLE_TPM: ++ suse_enable_tpm = 1; ++ return 0; ++ + case OPTION_DEBUG: + verbosity++; + return 0; +@@ -293,6 +299,7 @@ + {"no-rs-codes", OPTION_NO_RS_CODES, 0, 0, + N_("Do not apply any reed-solomon codes when embedding core.img. " + "This option is only available on x86 BIOS targets."), 0}, ++ {"suse-enable-tpm", OPTION_SUSE_ENABLE_TPM, 0, 0, N_("install TPM modules"), 0}, + + {"debug", OPTION_DEBUG, 0, OPTION_HIDDEN, 0, 2}, + {"no-floppy", OPTION_NO_FLOPPY, 0, OPTION_HIDDEN, 0, 2}, +@@ -1373,6 +1380,9 @@ + else if (disk_module && disk_module[0]) + grub_install_push_module (disk_module); + ++ if (suse_enable_tpm && platform == GRUB_INSTALL_PLATFORM_X86_64_EFI) ++ grub_install_push_module ("tpm"); ++ + relative_grubdir = grub_make_system_path_relative_to_its_root (grubdir); + if (relative_grubdir[0] == '\0') + { diff --git a/0013-libtasn1-changes-for-grub-compatibility.patch b/0013-libtasn1-changes-for-grub-compatibility.patch new file mode 100644 index 0000000..2560fd0 --- /dev/null +++ b/0013-libtasn1-changes-for-grub-compatibility.patch @@ -0,0 +1,212 @@ +From f05ba09c9adea447d3ca837c73498b9619306180 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Fri, 1 May 2020 20:44:29 +1000 +Subject: [PATCH 13/23] libtasn1: changes for grub compatibility + +Do a few things to make libtasn1 compile as part of grub: + + - redefine _asn1_strcat. grub removed strcat so replace it with the + appropriate calls to memcpy and strlen. Use this internally where + strcat was used. + + - replace c_isdigit with grub_isdigit (and don't import c-ctype from + gnulib) grub_isdigit provides the same functionality as c_isdigit: it + determines if the input is an ASCII digit without regard for locale. + + - replace GL_ATTRIBUTE_PURE with __attribute__((pure)) which been + supported since gcc-2.96. This avoids messing around with gnulib. + + - adjust libtasn1.h: drop the ASN1_API logic, it's not needed for our + modules. Unconditionally support const and pure attributes and adjust + header paths. + + - adjust header paths to "grub/libtasn1.h". + + - replace a 64 bit division with a call to grub_divmod64, preventing + creation of __udivdi3 calls on 32 bit platforms. + +Signed-off-by: Daniel Axtens + +--- + +v2: Clean up strcat handling, thanks Stefan Berger. +--- + grub-core/lib/libtasn1/lib/decoding.c | 11 +++++----- + grub-core/lib/libtasn1/lib/element.c | 3 ++- + grub-core/lib/libtasn1/lib/gstr.c | 4 ++-- + grub-core/lib/libtasn1/lib/int.h | 4 ++-- + grub-core/lib/libtasn1/lib/parser_aux.c | 7 +++--- + include/grub/libtasn1.h | 29 +++++++------------------ + 6 files changed, 24 insertions(+), 34 deletions(-) + +diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c +index b8130b956..beeb6a176 100644 +--- a/grub-core/lib/libtasn1/lib/decoding.c ++++ b/grub-core/lib/libtasn1/lib/decoding.c +@@ -32,7 +32,8 @@ + #include + #include + #include +-#include "c-ctype.h" ++ ++#define c_isdigit grub_isdigit + + #ifdef DEBUG + # define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__) +@@ -2016,8 +2017,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element, + (p2->type & CONST_ASSIGN)) + { + strcpy (name, definitions->name); +- strcat (name, "."); +- strcat (name, p2->name); ++ _asn1_strcat (name, "."); ++ _asn1_strcat (name, p2->name); + + len = sizeof (value); + result = asn1_read_value (definitions, name, value, &len); +@@ -2034,8 +2035,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element, + if (p2) + { + strcpy (name, definitions->name); +- strcat (name, "."); +- strcat (name, p2->name); ++ _asn1_strcat (name, "."); ++ _asn1_strcat (name, p2->name); + + result = asn1_create_element (definitions, name, &aux); + if (result == ASN1_SUCCESS) +diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c +index 8cd6b662c..150b9b377 100644 +--- a/grub-core/lib/libtasn1/lib/element.c ++++ b/grub-core/lib/libtasn1/lib/element.c +@@ -30,9 +30,10 @@ + #include "parser_aux.h" + #include + #include "structure.h" +-#include "c-ctype.h" + #include "element.h" + ++#define c_isdigit grub_isdigit ++ + void + _asn1_hierarchical_name (asn1_node_const node, char *name, int name_size) + { +diff --git a/grub-core/lib/libtasn1/lib/gstr.c b/grub-core/lib/libtasn1/lib/gstr.c +index 1475ed51b..b729089db 100644 +--- a/grub-core/lib/libtasn1/lib/gstr.c ++++ b/grub-core/lib/libtasn1/lib/gstr.c +@@ -36,13 +36,13 @@ _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src) + + if (dest_tot_size - dest_size > str_size) + { +- strcat (dest, src); ++ _asn1_strcat (dest, src); + } + else + { + if (dest_tot_size > dest_size) + { +- strncat (dest, src, (dest_tot_size - dest_size) - 1); ++ memcpy (dest + dest_size, src, (dest_tot_size - dest_size) - 1); + dest[dest_tot_size - 1] = 0; + } + } +diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h +index 404cd1562..edfe84a0e 100644 +--- a/grub-core/lib/libtasn1/lib/int.h ++++ b/grub-core/lib/libtasn1/lib/int.h +@@ -35,7 +35,7 @@ + # include + # endif + +-# include ++# include "grub/libtasn1.h" + + # define ASN1_SMALL_VALUE_SIZE 16 + +@@ -115,7 +115,7 @@ extern const tag_and_class_st _asn1_tags[]; + # define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b) + # define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b) + # define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b) +-# define _asn1_strcat(a,b) strcat((char *)a, (const char *)b) ++# define _asn1_strcat(a,b) memcpy((char *)a + strlen((const char *)a), (const char *)b, strlen((const char *)b) + 1) + + # if SIZEOF_UNSIGNED_LONG_INT == 8 + # define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b) +diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c b/grub-core/lib/libtasn1/lib/parser_aux.c +index c99c5a4cb..a933f03ed 100644 +--- a/grub-core/lib/libtasn1/lib/parser_aux.c ++++ b/grub-core/lib/libtasn1/lib/parser_aux.c +@@ -26,7 +26,8 @@ + #include "gstr.h" + #include "structure.h" + #include "element.h" +-#include "c-ctype.h" ++ ++#define c_isdigit grub_isdigit + + char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1]; /* identifier name not found */ + +@@ -40,7 +41,7 @@ char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1]; /* identifier name not fou + #ifdef __clang__ + __attribute__((no_sanitize ("integer"))) + #endif +- _GL_ATTRIBUTE_PURE static unsigned int _asn1_hash_name (const char *x) ++ __attribute__((__pure__)) static unsigned int _asn1_hash_name (const char *x) + { + const unsigned char *s = (unsigned char *) x; + unsigned h = 0; +@@ -632,7 +633,7 @@ _asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]) + count = 0; + do + { +- d = val / 10; ++ d = grub_divmod64(val, 10, NULL); + r = val - d * 10; + temp[start + count] = '0' + (char) r; + count++; +diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h +index 0c3a44881..2ea058a3b 100644 +--- a/include/grub/libtasn1.h ++++ b/include/grub/libtasn1.h +@@ -34,29 +34,16 @@ + #ifndef LIBTASN1_H + # define LIBTASN1_H + +-# ifndef ASN1_API +-# if defined ASN1_BUILDING && defined HAVE_VISIBILITY && HAVE_VISIBILITY +-# define ASN1_API __attribute__((__visibility__("default"))) +-# elif defined ASN1_BUILDING && defined _MSC_VER && ! defined ASN1_STATIC +-# define ASN1_API __declspec(dllexport) +-# elif defined _MSC_VER && ! defined ASN1_STATIC +-# define ASN1_API __declspec(dllimport) +-# else +-# define ASN1_API +-# endif +-# endif ++/* grub: ASN1_API is not used */ ++# define ASN1_API ++ ++/* grub: all our supported compilers support these attributes */ ++# define __LIBTASN1_CONST__ __attribute__((const)) ++# define __LIBTASN1_PURE__ __attribute__((pure)) + +-# ifdef __GNUC__ +-# define __LIBTASN1_CONST__ __attribute__((const)) +-# define __LIBTASN1_PURE__ __attribute__((pure)) +-# else +-# define __LIBTASN1_CONST__ +-# define __LIBTASN1_PURE__ +-# endif + +-# include +-# include +-# include /* for FILE* */ ++# include ++# include + + # ifdef __cplusplus + extern "C" +-- +2.31.1 + diff --git a/0014-libtasn1-compile-into-asn1-module.patch b/0014-libtasn1-compile-into-asn1-module.patch new file mode 100644 index 0000000..942a0f1 --- /dev/null +++ b/0014-libtasn1-compile-into-asn1-module.patch @@ -0,0 +1,65 @@ +From f3b818444fe8628d581f5efe23d55554f23718c8 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Fri, 5 Jun 2020 17:47:25 +1000 +Subject: [PATCH 14/23] libtasn1: compile into asn1 module + +Create a wrapper file that specifies the module license. +Set up the makefile so it is built. + +Signed-off-by: Daniel Axtens +--- + grub-core/Makefile.core.def | 15 +++++++++++++++ + grub-core/lib/libtasn1_wrap/wrap.c | 26 ++++++++++++++++++++++++++ + 2 files changed, 41 insertions(+) + create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2624,3 +2624,18 @@ + name = cmdline; + common = lib/cmdline.c; + }; ++ ++module = { ++ name = asn1; ++ common = lib/libtasn1/lib/decoding.c; ++ common = lib/libtasn1/lib/coding.c; ++ common = lib/libtasn1/lib/element.c; ++ common = lib/libtasn1/lib/structure.c; ++ common = lib/libtasn1/lib/parser_aux.c; ++ common = lib/libtasn1/lib/gstr.c; ++ common = lib/libtasn1/lib/errors.c; ++ common = lib/libtasn1_wrap/wrap.c; ++ cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; ++ // -Wno-type-limits comes from libtasn1's configure.ac ++ cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1/lib -Wno-type-limits'; ++}; +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/wrap.c +@@ -0,0 +1,26 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2020 IBM Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++ ++/* ++ * libtasn1 is provided under LGPL2.1+, which is compatible ++ * with GPL3+. As Grub as a whole is under GPL3+, this module ++ * is therefore under GPL3+ also. ++ */ ++GRUB_MOD_LICENSE ("GPLv3+"); diff --git a/0015-test_asn1-test-module-for-libtasn1.patch b/0015-test_asn1-test-module-for-libtasn1.patch new file mode 100644 index 0000000..0d5c6dc --- /dev/null +++ b/0015-test_asn1-test-module-for-libtasn1.patch @@ -0,0 +1,1401 @@ +From 5fcbd6e61cd5390e3e0d0f65eb622761d23bab4e Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Wed, 10 Jun 2020 17:48:42 +1000 +Subject: [PATCH 15/23] test_asn1: test module for libtasn1 + +Import tests from libtasn1 that don't use functionality we don't +import. I have put them here rather than in the libtasn1 directory +because: + + - They need much more significant changes to run in the grub + context. + + - I don't expect they will need to be changed when updating + libtasn1: I expect the old tests will usually continue to pass on + new versions. + +This doesn't test the full decoder but that will be exercised in +test suites for coming patch sets. + +Signed-off-by: Daniel Axtens +--- + Makefile.util.def | 6 + + grub-core/Makefile.core.def | 13 ++ + .../tests/CVE-2018-1000654-1_asn1_tab.h | 32 +++ + .../tests/CVE-2018-1000654-2_asn1_tab.h | 36 +++ + .../libtasn1_wrap/tests/CVE-2018-1000654.c | 61 +++++ + .../lib/libtasn1_wrap/tests/Test_overflow.c | 138 ++++++++++++ + .../lib/libtasn1_wrap/tests/Test_simple.c | 207 +++++++++++++++++ + .../lib/libtasn1_wrap/tests/Test_strings.c | 150 +++++++++++++ + .../libtasn1_wrap/tests/object-id-decoding.c | 116 ++++++++++ + .../libtasn1_wrap/tests/object-id-encoding.c | 120 ++++++++++ + .../lib/libtasn1_wrap/tests/octet-string.c | 211 ++++++++++++++++++ + .../lib/libtasn1_wrap/tests/reproducers.c | 81 +++++++ + grub-core/lib/libtasn1_wrap/wrap_tests.c | 75 +++++++ + grub-core/lib/libtasn1_wrap/wrap_tests.h | 38 ++++ + tests/test_asn1.in | 12 + + 15 files changed, 1296 insertions(+) + create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h + create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h + create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c + create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_overflow.c + create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_simple.c + create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_strings.c + create mode 100644 grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c + create mode 100644 grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c + create mode 100644 grub-core/lib/libtasn1_wrap/tests/octet-string.c + create mode 100644 grub-core/lib/libtasn1_wrap/tests/reproducers.c + create mode 100644 grub-core/lib/libtasn1_wrap/wrap_tests.c + create mode 100644 grub-core/lib/libtasn1_wrap/wrap_tests.h + create mode 100644 tests/test_asn1.in + +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -1304,6 +1304,12 @@ + common = tests/luks2_test.in; + }; + ++script = { ++ testcase = native; ++ name = test_asn1; ++ common = tests/test_asn1.in; ++}; ++ + program = { + testcase = native; + name = example_unit_test; +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2639,3 +2639,16 @@ + // -Wno-type-limits comes from libtasn1's configure.ac + cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1/lib -Wno-type-limits'; + }; ++ ++module = { ++ name = test_asn1; ++ common = lib/libtasn1_wrap/tests/CVE-2018-1000654.c; ++ common = lib/libtasn1_wrap/tests/object-id-decoding.c; ++ common = lib/libtasn1_wrap/tests/object-id-encoding.c; ++ common = lib/libtasn1_wrap/tests/octet-string.c; ++ common = lib/libtasn1_wrap/tests/reproducers.c; ++ common = lib/libtasn1_wrap/tests/Test_overflow.c; ++ common = lib/libtasn1_wrap/tests/Test_simple.c; ++ common = lib/libtasn1_wrap/tests/Test_strings.c; ++ common = lib/libtasn1_wrap/wrap_tests.c; ++}; +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h +@@ -0,0 +1,32 @@ ++#if HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include ++ ++const asn1_static_node CVE_2018_1000654_1_asn1_tab[] = { ++ { "TEST_TREE", 536875024, NULL }, ++ { NULL, 1610612748, NULL }, ++ { "iso", 1073741825, "1"}, ++ { "identified-organization", 1073741825, "3"}, ++ { "dod", 1073741825, "6"}, ++ { "internet", 1073741825, "1"}, ++ { "security", 1073741825, "5"}, ++ { "mechanisms", 1073741825, "5"}, ++ { "pkix", 1073741825, "7"}, ++ { "id-mod", 1073741825, "0"}, ++ { "id-pkix1-implicit-88", 1, "2"}, ++ { "id-xnyTest", 1879048204, NULL }, ++ { NULL, 1073741825, "id-ix"}, ++ { NULL, 1073741825, "29"}, ++ { NULL, 1, "1"}, ++ { "id-ix", 1880096780, "OBJECR"}, ++ { NULL, 1073741825, "id-ix"}, ++ { NULL, 1073741825, "29"}, ++ { NULL, 1, "2"}, ++ { "id-xnyTest", 805306380, NULL }, ++ { NULL, 1073741825, "id-ix"}, ++ { NULL, 1073741825, "29"}, ++ { NULL, 1, "1"}, ++ { NULL, 0, NULL } ++}; +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h +@@ -0,0 +1,36 @@ ++#if HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include ++ ++const asn1_static_node CVE_2018_1000654_2_asn1_tab[] = { ++ { "TEST_TREE", 536875024, NULL }, ++ { NULL, 1610612748, NULL }, ++ { "iso", 1073741825, "1"}, ++ { "identified-organization", 1073741825, "3"}, ++ { "dod", 1073741825, "6"}, ++ { "internet", 1073741825, "1"}, ++ { "security", 1073741825, "5"}, ++ { "mechanisms", 1073741825, "5"}, ++ { "pkix", 1073741825, "7"}, ++ { "id-mod", 1073741825, "0"}, ++ { "id-pkix1-implicit-88", 1, "2"}, ++ { "id-oneTest", 1879048204, NULL }, ++ { NULL, 1073741825, "id-two"}, ++ { NULL, 1073741825, "9"}, ++ { NULL, 1, "1"}, ++ { "id-two", 1879048204, NULL }, ++ { NULL, 1073741825, "id-three"}, ++ { NULL, 1073741825, "2"}, ++ { NULL, 1, "2"}, ++ { "id-three", 1879048204, NULL }, ++ { NULL, 1073741825, "id-four"}, ++ { NULL, 1073741825, "3"}, ++ { NULL, 1, "3"}, ++ { "id-four", 805306380, NULL }, ++ { NULL, 1073741825, "id-two"}, ++ { NULL, 1073741825, "3"}, ++ { NULL, 1, "3"}, ++ { NULL, 0, NULL } ++}; +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (C) 2002-2018 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ */ ++ ++/****************************************************************/ ++/* Description: reproducer for CVE-2018-1000654 */ ++/****************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include "../wrap_tests.h" ++ ++#include "CVE-2018-1000654-1_asn1_tab.h" ++#include "CVE-2018-1000654-2_asn1_tab.h" ++ ++void ++test_CVE_2018_1000654 (void) ++{ ++ int result; ++ asn1_node definitions = NULL; ++ char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; ++ ++ result = asn1_array2tree (CVE_2018_1000654_1_asn1_tab, &definitions, errorDescription); ++ if (result != ASN1_RECURSION) ++ { ++ grub_fatal ("Error: %s\nErrorDescription = %s\n\n", ++ asn1_strerror (result), errorDescription); ++ return; ++ } ++ ++ asn1_delete_structure (&definitions); ++ ++ result = asn1_array2tree (CVE_2018_1000654_2_asn1_tab, &definitions, errorDescription); ++ if (result != ASN1_RECURSION) ++ { ++ grub_fatal ("Error: %s\nErrorDescription = %s\n\n", ++ asn1_strerror (result), errorDescription); ++ return; ++ } ++ ++ asn1_delete_structure (&definitions); ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c +@@ -0,0 +1,138 @@ ++/* ++ * Copyright (C) 2012-2014 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ */ ++ ++/* Written by Simon Josefsson */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "../wrap_tests.h" ++ ++void ++test_overflow(void) ++{ ++ /* Test that values larger than long are rejected. This has worked ++ fine with all versions of libtasn1. */ ++ ++ { ++ unsigned char der[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; ++ long l; ++ int len; ++ ++ l = asn1_get_length_der (der, sizeof der, &len); ++ ++ if (l != -2L) ++ { ++ grub_fatal ("ERROR: asn1_get_length_der bignum (l %ld len %d)\n", l, len); ++ return; ++ } ++ } ++ ++ /* Test that values larger than int but smaller than long are ++ rejected. This limitation was introduced with libtasn1 2.12. */ ++#if (GRUB_LONG_MAX > GRUB_INT_MAX) ++ { ++ unsigned long num = ((long) GRUB_UINT_MAX) << 2; ++ unsigned char der[20]; ++ int der_len; ++ long l; ++ int len; ++ ++ asn1_length_der (num, der, &der_len); ++ ++ l = asn1_get_length_der (der, der_len, &len); ++ ++ if (l != -2L) ++ { ++ grub_fatal ("ERROR: asn1_get_length_der intnum (l %ld len %d)\n", l, ++ len); ++ return; ++ } ++ } ++#endif ++ ++ /* Test that values larger than would fit in the input string are ++ rejected. This problem was fixed in libtasn1 2.12. */ ++ { ++ unsigned long num = 64; ++ unsigned char der[20]; ++ int der_len; ++ long l; ++ int len; ++ ++ asn1_length_der (num, der, &der_len); ++ ++ der_len = sizeof (der); ++ l = asn1_get_length_der (der, der_len, &len); ++ ++ if (l != -4L) ++ { ++ grub_fatal ("ERROR: asn1_get_length_der overflow-small (l %ld len %d)\n", ++ l, len); ++ return; ++ } ++ } ++ ++ /* Test that values larger than would fit in the input string are ++ rejected. This problem was fixed in libtasn1 2.12. */ ++ { ++ unsigned long num = 1073741824; ++ unsigned char der[20]; ++ int der_len; ++ long l; ++ int len; ++ ++ asn1_length_der (num, der, &der_len); ++ ++ der_len = sizeof (der); ++ l = asn1_get_length_der (der, der_len, &len); ++ ++ if (l != -4L) ++ { ++ grub_fatal ("ERROR: asn1_get_length_der overflow-large1 (l %ld len %d)\n", ++ l, len); ++ return; ++ } ++ } ++ ++ /* Test that values larger than would fit in the input string are ++ rejected. This problem was fixed in libtasn1 2.12. */ ++ { ++ unsigned long num = 2147483649; ++ unsigned char der[20]; ++ int der_len; ++ long l; ++ int len; ++ ++ asn1_length_der (num, der, &der_len); ++ ++ der_len = sizeof (der); ++ l = asn1_get_length_der (der, der_len, &len); ++ ++ if (l != -2L) ++ { ++ grub_fatal ("ERROR: asn1_get_length_der overflow-large2 (l %ld len %d)\n", ++ l, len); ++ return; ++ } ++ } ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/tests/Test_simple.c +@@ -0,0 +1,207 @@ ++/* ++ * Copyright (C) 2011-2014 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Written by Simon Josefsson ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include "../wrap_tests.h" ++ ++struct tv ++{ ++ int bitlen; ++ const char *bitstr; ++ int derlen; ++ const char *der; ++}; ++ ++static const struct tv tv[] = { ++ {0, "", 2, "\x01\x00"}, ++ {1, "\x00", 3, "\x02\x07\x00"}, ++ {2, "\x00", 3, "\x02\x06\x00"}, ++ {3, "\x00", 3, "\x02\x05\x00"}, ++ {4, "\x00", 3, "\x02\x04\x00"}, ++ {5, "\x00", 3, "\x02\x03\x00"}, ++ {6, "\x00", 3, "\x02\x02\x00"}, ++ {7, "\x00", 3, "\x02\x01\x00"}, ++ {8, "\x00\x00", 3, "\x02\x00\x00"}, ++ {9, "\x00\x00", 4, "\x03\x07\x00\x00"}, ++ {10, "\x00\x00", 4, "\x03\x06\x00\x00"}, ++ {11, "\x00\x00", 4, "\x03\x05\x00\x00"}, ++ {12, "\x00\x00", 4, "\x03\x04\x00\x00"}, ++ {13, "\x00\x00", 4, "\x03\x03\x00\x00"}, ++ {14, "\x00\x00", 4, "\x03\x02\x00\x00"}, ++ {15, "\x00\x00", 4, "\x03\x01\x00\x00"}, ++ {16, "\x00\x00", 4, "\x03\x00\x00\x00"}, ++ {17, "\x00\x00\x00", 5, "\x04\x07\x00\x00\x00"}, ++ {18, "\x00\x00\x00", 5, "\x04\x06\x00\x00\x00"}, ++ {19, "\x00\x00\x00", 5, "\x04\x05\x00\x00\x00"}, ++ {1, "\xFF", 3, "\x02\x07\x80"}, ++ {2, "\xFF", 3, "\x02\x06\xc0"}, ++ {3, "\xFF", 3, "\x02\x05\xe0"}, ++ {4, "\xFF", 3, "\x02\x04\xf0"}, ++ {5, "\xFF", 3, "\x02\x03\xf8"}, ++ {6, "\xFF", 3, "\x02\x02\xfc"}, ++ {7, "\xFF", 3, "\x02\x01\xfe"}, ++ {8, "\xFF\xFF", 3, "\x02\x00\xff"}, ++ {9, "\xFF\xFF", 4, "\x03\x07\xff\x80"}, ++ {10, "\xFF\xFF", 4, "\x03\x06\xff\xc0"}, ++ {11, "\xFF\xFF", 4, "\x03\x05\xff\xe0"}, ++ {12, "\xFF\xFF", 4, "\x03\x04\xff\xf0"}, ++ {13, "\xFF\xFF", 4, "\x03\x03\xff\xf8"}, ++ {14, "\xFF\xFF", 4, "\x03\x02\xff\xfc"}, ++ {15, "\xFF\xFF", 4, "\x03\x01\xff\xfe"}, ++ {16, "\xFF\xFF", 4, "\x03\x00\xff\xff"}, ++ {17, "\xFF\xFF\xFF", 5, "\x04\x07\xff\xff\x80"}, ++ {18, "\xFF\xFF\xFF", 5, "\x04\x06\xff\xff\xc0"}, ++ {19, "\xFF\xFF\xFF", 5, "\x04\x05\xff\xff\xe0"}, ++}; ++ ++void ++test_simple (void) ++{ ++ int result; ++ unsigned char der[100]; ++ unsigned char str[100]; ++ int der_len = sizeof (der); ++ int str_size = sizeof (str); ++ int ret_len, bit_len; ++ grub_size_t i; ++ ++ /* Dummy test */ ++ ++ asn1_bit_der (NULL, 0, der, &der_len); ++ result = asn1_get_bit_der (der, 0, &ret_len, str, str_size, &bit_len); ++ if (result != ASN1_GENERIC_ERROR) ++ { ++ grub_fatal ("asn1_get_bit_der zero\n"); ++ return; ++ } ++ ++ /* Encode short strings with increasing bit lengths */ ++ ++ for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) ++ { ++ /* Encode */ ++ ++ asn1_bit_der ((const unsigned char *) tv[i].bitstr, tv[i].bitlen, ++ der, &der_len); ++ ++#if 0 ++ { ++ size_t j; ++ for (j = 0; j < der_len; j++) ++ printf ("\\x%02x", der[j]); ++ printf ("\n"); ++ } ++#endif ++ ++ if (der_len != tv[i].derlen || grub_memcmp (der, tv[i].der, der_len) != 0) ++ { ++ grub_fatal ("asn1_bit_der iter %lu\n", (unsigned long) i); ++ return; ++ } ++ ++ /* Decode it */ ++ ++ result = asn1_get_bit_der (der, der_len, &ret_len, str, ++ str_size, &bit_len); ++ if (result != ASN1_SUCCESS || ret_len != tv[i].derlen ++ || bit_len != tv[i].bitlen) ++ { ++ grub_fatal ("asn1_get_bit_der iter %lu, err: %d\n", (unsigned long) i, result); ++ return; ++ } ++ } ++ ++ ++ /* Decode sample from "A Layman's Guide to a Subset of ASN.1, BER, ++ and DER" section 5.4 "BIT STRING": "The BER encoding of the BIT ++ STRING value "011011100101110111" can be any of the following, ++ among others, depending on the choice of padding bits, the form ++ of length octets [...]". ++ */ ++ ++ /* 03 04 06 6e 5d c0 DER encoding */ ++ ++ grub_memcpy (der, "\x04\x06\x6e\x5d\xc0", 5); ++ der_len = 5; ++ ++ result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); ++ if (result != ASN1_SUCCESS || ret_len != 5 ++ || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0) ++ { ++ grub_fatal ("asn1_get_bit_der example\n"); ++ return; ++ } ++ ++ der_len = sizeof (der); ++ asn1_bit_der (str, bit_len, der, &der_len); ++ if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) ++ { ++ grub_fatal ("asn1_bit_der example roundtrip\n"); ++ return; ++ } ++ ++ /* 03 04 06 6e 5d e0 padded with "100000" */ ++ ++ grub_memcpy (der, "\x04\x06\x6e\x5d\xe0", 5); ++ der_len = 5; ++ ++ result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); ++ if (result != ASN1_SUCCESS || ret_len != 5 ++ || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xe0", 3) != 0) ++ { ++ grub_fatal ("asn1_get_bit_der example padded\n"); ++ return; ++ } ++ ++ der_len = sizeof (der); ++ asn1_bit_der (str, bit_len, der, &der_len); ++ if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) ++ { ++ grub_fatal ("asn1_bit_der example roundtrip\n"); ++ return; ++ } ++ ++ /* 03 81 04 06 6e 5d c0 long form of length octets */ ++ ++ grub_memcpy (der, "\x81\x04\x06\x6e\x5d\xc0", 6); ++ der_len = 6; ++ ++ result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); ++ ++ if (result != ASN1_SUCCESS || ret_len != 6 ++ || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0) ++ { ++ grub_fatal ("asn1_get_bit_der example long form\n"); ++ return; ++ } ++ ++ der_len = sizeof (der); ++ asn1_bit_der (str, bit_len, der, &der_len); ++ if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) ++ { ++ grub_fatal ("asn1_bit_der example roundtrip\n"); ++ return; ++ } ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/tests/Test_strings.c +@@ -0,0 +1,150 @@ ++/* ++ * Copyright (C) 2012-2014 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Written by Simon Josefsson ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include "../wrap_tests.h" ++ ++struct tv ++{ ++ unsigned int etype; ++ unsigned int str_len; ++ const void *str; ++ unsigned int der_len; ++ const void *der; ++}; ++ ++static const struct tv tv[] = { ++ {ASN1_ETYPE_IA5_STRING, 20, ++ "\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72", ++ 22, ++ "\x16\x14\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72"}, ++ {ASN1_ETYPE_PRINTABLE_STRING, 5, "\x4e\x69\x6b\x6f\x73", ++ 7, "\x13\x05\x4e\x69\x6b\x6f\x73"}, ++ {ASN1_ETYPE_UTF8_STRING, 12, "Αττική", ++ 14, "\x0c\x0c\xce\x91\xcf\x84\xcf\x84\xce\xb9\xce\xba\xce\xae"}, ++ {ASN1_ETYPE_TELETEX_STRING, 15, ++ "\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e", ++ 17, ++ "\x14\x0f\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e"}, ++ {ASN1_ETYPE_OCTET_STRING, 36, ++ "\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A", ++ 38, ++ "\x04\x24\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A"} ++}; ++ ++#define SSTR(x) sizeof(x)-1,x ++static const struct tv ber[] = { ++ {ASN1_ETYPE_OCTET_STRING, ++ SSTR("\xa0\xa0"), ++ SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x00\x00")}, ++ {ASN1_ETYPE_OCTET_STRING, ++ SSTR("\xa0\xa0\xb0\xb0\xb0"), ++ SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x00\x00")}, ++ {ASN1_ETYPE_OCTET_STRING, ++ SSTR("\xa0\xa0\xb0\xb0\xb0\xa1\xa1"), ++ SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x00\x00\x00\x00")}, ++ {ASN1_ETYPE_OCTET_STRING, ++ SSTR("\xa0\xa0\xb0\xb0\xb0\xa1\xa1\xc1"), ++ SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x04\x82\x00\x01\xc1\x00\x00\x00\x00")}, ++}; ++ ++void ++test_strings () ++{ ++ int ret; ++ unsigned char tl[ASN1_MAX_TL_SIZE]; ++ unsigned int tl_len, der_len, str_len; ++ const unsigned char *str; ++ unsigned char *b; ++ unsigned int i; ++ ++ /* Dummy test */ ++ ++ for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) ++ { ++ /* Encode */ ++ tl_len = sizeof (tl); ++ ret = asn1_encode_simple_der (tv[i].etype, tv[i].str, tv[i].str_len, ++ tl, &tl_len); ++ if (ret != ASN1_SUCCESS) ++ { ++ grub_fatal ("Encoding error in %u: %s\n", i, ++ asn1_strerror (ret)); ++ return; ++ } ++ der_len = tl_len + tv[i].str_len; ++ ++ if (der_len != tv[i].der_len || grub_memcmp (tl, tv[i].der, tl_len) != 0) ++ { ++ grub_fatal ( ++ "DER encoding differs in %u! (size: %u, expected: %u)\n", ++ i, der_len, tv[i].der_len); ++ return; ++ } ++ ++ /* decoding */ ++ ret = ++ asn1_decode_simple_der (tv[i].etype, tv[i].der, tv[i].der_len, &str, ++ &str_len); ++ if (ret != ASN1_SUCCESS) ++ { ++ grub_fatal ("Decoding error in %u: %s\n", i, ++ asn1_strerror (ret)); ++ return; ++ } ++ ++ if (str_len != tv[i].str_len || grub_memcmp (str, tv[i].str, str_len) != 0) ++ { ++ grub_fatal ( ++ "DER decoded data differ in %u! (size: %u, expected: %u)\n", ++ i, der_len, tv[i].str_len); ++ return; ++ } ++ } ++ ++ /* BER decoding */ ++ for (i = 0; i < sizeof (ber) / sizeof (ber[0]); i++) ++ { ++ /* decoding */ ++ ret = ++ asn1_decode_simple_ber (ber[i].etype, ber[i].der, ber[i].der_len, &b, ++ &str_len, NULL); ++ if (ret != ASN1_SUCCESS) ++ { ++ grub_fatal ("BER decoding error in %u: %s\n", i, ++ asn1_strerror (ret)); ++ return; ++ } ++ ++ if (str_len != ber[i].str_len || grub_memcmp (b, ber[i].str, str_len) != 0) ++ { ++ grub_fatal ( ++ "BER decoded data differ in %u! (size: %u, expected: %u)\n", ++ i, str_len, ber[i].str_len); ++ return; ++ } ++ grub_free(b); ++ } ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c +@@ -0,0 +1,116 @@ ++/* ++ * Copyright (C) 2016 Red Hat, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include "../wrap_tests.h" ++ ++struct tv ++{ ++ int der_len; ++ const unsigned char *der; ++ const char *oid; ++ int expected_error; ++}; ++ ++static const struct tv tv[] = { ++ {.der_len = 5, ++ .der = (void *) "\x06\x03\x80\x37\x03", ++ .oid = "2.999.3", ++ .expected_error = ASN1_DER_ERROR /* leading 0x80 */ ++ }, ++ {.der_len = 12, ++ .der = (void *) "\x06\x0a\x2b\x06\x01\x80\x01\x92\x08\x09\x05\x01", ++ .oid = "1.3.6.1.4.1.2312.9.5.1", ++ .expected_error = ASN1_DER_ERROR /* leading 0x80 */ ++ }, ++ {.der_len = 6, ++ .der = (void *) "\x06\x04\x01\x02\x03\x04", ++ .oid = "0.1.2.3.4", ++ .expected_error = ASN1_SUCCESS}, ++ {.der_len = 5, ++ .der = (void *) "\x06\x03\x51\x02\x03", ++ .oid = "2.1.2.3", ++ .expected_error = ASN1_SUCCESS}, ++ {.der_len = 5, ++ .der = (void *) "\x06\x03\x88\x37\x03", ++ .oid = "2.999.3", ++ .expected_error = ASN1_SUCCESS}, ++ {.der_len = 12, ++ .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01", ++ .oid = "1.3.6.1.4.1.2312.9.5.1", ++ .expected_error = ASN1_SUCCESS}, ++ {.der_len = 19, ++ .der = (void *) "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d", ++ .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109", ++ .expected_error = ASN1_SUCCESS}, ++ {.der_len = 19, ++ .der = ++ (void *) ++ "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07", ++ .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7", ++ .expected_error = ASN1_SUCCESS}, ++}; ++ ++void ++test_object_id_decoding (void) ++{ ++ char str[128]; ++ int ret, ret_len; ++ grub_size_t i; ++ ++ for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) ++ { ++ /* decode */ ++ ret = ++ asn1_get_object_id_der (tv[i].der+1, ++ tv[i].der_len-1, &ret_len, str, ++ sizeof (str)); ++ if (ret != tv[i].expected_error) ++ { ++ grub_fatal ( ++ "%d: asn1_get_object_id_der iter %lu: got '%s' expected %d\n", ++ __LINE__, (unsigned long) i, asn1_strerror(ret), tv[i].expected_error); ++ return; ++ } ++ ++ if (tv[i].expected_error != ASN1_SUCCESS) ++ continue; ++ ++ if (ret_len != tv[i].der_len-1) ++ { ++ grub_fatal ( ++ "%d: iter %lu: error in DER, length returned is %d, had %d\n", ++ __LINE__, (unsigned long)i, ret_len, tv[i].der_len-1); ++ return; ++ } ++ ++ if (grub_strcmp (tv[i].oid, str) != 0) ++ { ++ grub_fatal ( ++ "%d: strcmp iter %lu: got invalid OID: %s, expected: %s\n", ++ __LINE__, (unsigned long) i, str, tv[i].oid); ++ return; ++ } ++ ++ } ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c +@@ -0,0 +1,120 @@ ++/* ++ * Copyright (C) 2019 Nikos Mavrogiannopoulos ++ * ++ * This file is part of LIBTASN1. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include "../wrap_tests.h" ++ ++struct tv ++{ ++ int der_len; ++ const unsigned char *der; ++ const char *oid; ++ int expected_error; ++}; ++ ++static const struct tv tv[] = { ++ {.der_len = 0, ++ .der = (void *) "", ++ .oid = "5.999.3", ++ .expected_error = ASN1_VALUE_NOT_VALID /* cannot start with 5 */ ++ }, ++ {.der_len = 0, ++ .der = (void *) "", ++ .oid = "0.48.9", ++ .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 48 */ ++ }, ++ {.der_len = 0, ++ .der = (void *) "", ++ .oid = "1.40.9", ++ .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 40 */ ++ }, ++ {.der_len = 4, ++ .der = (void *) "\x06\x02\x4f\x63", ++ .oid = "1.39.99", ++ .expected_error = ASN1_SUCCESS, ++ }, ++ {.der_len = 6, ++ .der = (void *) "\x06\x04\x01\x02\x03\x04", ++ .oid = "0.1.2.3.4", ++ .expected_error = ASN1_SUCCESS}, ++ {.der_len = 5, ++ .der = (void *) "\x06\x03\x51\x02\x03", ++ .oid = "2.1.2.3", ++ .expected_error = ASN1_SUCCESS}, ++ {.der_len = 5, ++ .der = (void *) "\x06\x03\x88\x37\x03", ++ .oid = "2.999.3", ++ .expected_error = ASN1_SUCCESS}, ++ {.der_len = 12, ++ .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01", ++ .oid = "1.3.6.1.4.1.2312.9.5.1", ++ .expected_error = ASN1_SUCCESS}, ++ {.der_len = 19, ++ .der = (void *) "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d", ++ .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109", ++ .expected_error = ASN1_SUCCESS}, ++ {.der_len = 19, ++ .der = ++ (void *) ++ "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07", ++ .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7", ++ .expected_error = ASN1_SUCCESS}, ++}; ++ ++void ++test_object_id_encoding(void) ++{ ++ unsigned char der[128]; ++ int ret, der_len, i; ++ ++ for (i = 0; i < (int)(sizeof (tv) / sizeof (tv[0])); i++) ++ { ++ der_len = sizeof(der); ++ ret = asn1_object_id_der(tv[i].oid, der, &der_len, 0); ++ if (ret != ASN1_SUCCESS) ++ { ++ if (ret == tv[i].expected_error) ++ continue; ++ grub_fatal ( ++ "%d: iter %lu, encoding of OID failed: %s\n", ++ __LINE__, (unsigned long) i, asn1_strerror(ret)); ++ return; ++ } ++ else if (ret != tv[i].expected_error) ++ { ++ grub_fatal ( ++ "%d: iter %lu, encoding of OID %s succeeded when expecting failure\n", ++ __LINE__, (unsigned long) i, tv[i].oid); ++ return; ++ } ++ ++ if (der_len != tv[i].der_len || grub_memcmp(der, tv[i].der, der_len) != 0) ++ { ++ grub_fatal ( ++ "%d: iter %lu, re-encoding of OID %s resulted to different string (%d vs %d bytes)\n", ++ __LINE__, (unsigned long) i, tv[i].oid, der_len, tv[i].der_len); ++ ++ return; ++ } ++ } ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/tests/octet-string.c +@@ -0,0 +1,211 @@ ++/* ++ * Copyright (C) 2011-2020 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Written by Simon Josefsson and Nikos Mavrogiannopoulos ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include "../wrap_tests.h" ++ ++ ++struct tv ++{ ++ const char *name; ++ int der_len; ++ const unsigned char *der_str; ++ int len; ++ const unsigned char *string; ++ int expected_error; ++ int ber; ++}; ++ ++static const struct tv tv[] = { ++ {.name = "primitive octet strings", ++ .der_len = 10, ++ .der_str = ++ (void*)"\x04\x08\x01\x23\x45\x67\x89\xab\xcd\xef", ++ .len = 8, ++ .string = ++ (void*)"\x01\x23\x45\x67\x89\xab\xcd\xef", ++ .ber = 0}, ++ {.der_len = 22, ++ .der_str = ++ (void*)"\x04\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27", ++ .len = 20, ++ .string = ++ (void*)"\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27"}, ++ ++ {.name = "long type of length", ++ .der_len = 23, ++ .der_str = ++ (void*)"\x04\x81\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27", ++ .len = 20, ++ .string = ++ (void*)"\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27", ++ .ber = 1}, ++ {.der_len = 11, ++ .der_str = ++ (void*)"\x04\x81\x08\x01\x23\x45\x67\x89\xab\xcd\xef", ++ .len = 8, ++ .string = ++ (void*)"\x01\x23\x45\x67\x89\xab\xcd\xef", ++ .ber = 1}, ++ ++ {.name = "constructed - indefinite", ++ .der_len = 11, ++ .der_str = (void*)"\x24\x80\x04\x05\x01\x02\x03\x04\x05\x00\x00", ++ .len = 5, ++ .string = (void*)"\x01\x02\x03\x04\x05", ++ .ber = 1, ++ }, ++ ++ {.name = "constructed - definite - concat", ++ .der_len = 12, ++ .der_str = (void*)"\x24\x0a\x04\x04\x0a\x0b\x0c\x0d\x04\x02\x0e\x0f", ++ .len = 6, ++ .string = (void*)"\x0a\x0b\x0c\x0d\x0e\x0f", ++ .ber = 1, ++ }, ++ {.name = "constructed - definite - recursive", ++ .der_len = 15, ++ .der_str = (void*)"\x24\x0d\x04\x04\x0a\x0b\x0c\x0d\x24\x05\x04\x00\x04\x01\x0f", ++ .len = 5, ++ .string = (void*)"\x0a\x0b\x0c\x0d\x0f", ++ .ber = 1, ++ }, ++ {.name = "constructed - definite - single", ++ .der_len = 7, ++ .der_str = (void*)"\x24\x05\x04\x03\x01\x02\x03", ++ .len = 3, ++ .string = (void*)"\x01\x02\x03", ++ .ber = 1, ++ }, ++ ++ /* a large amount of recursive indefinite encoding */ ++ {.name = "a large amount of recursive indefinite encoding", ++ .der_len = 29325, ++ .der_str = (void*)"\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80", ++ .len = 0, ++ .ber = 1, ++ .expected_error = ASN1_DER_ERROR ++ } ++}; ++ ++void ++test_octet_string (void) ++{ ++ unsigned char str[100]; ++ unsigned char der[100]; ++ int der_len = sizeof (der); ++ int str_size = sizeof (str); ++ unsigned char *tmp = NULL; ++ int ret, ret_len; ++ grub_size_t i; ++ ++ for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) ++ { ++ /* Decode */ ++ ++ if (tv[i].ber == 0) ++ { ++ str_size = sizeof (str); ++ ret = ++ asn1_get_octet_der (tv[i].der_str + 1, ++ tv[i].der_len - 1, &ret_len, str, ++ sizeof (str), &str_size); ++ if (ret != tv[i].expected_error) ++ { ++ grub_fatal ( ++ "%d: asn1_get_octet_der: %s: got %d expected %d\n", ++ __LINE__, tv[i].name, ret, ++ tv[i].expected_error); ++ return; ++ } ++ if (tv[i].expected_error) ++ continue; ++ ++ if (ret_len != tv[i].der_len - 1) ++ { ++ grub_fatal ( ++ "%d: error in DER, length returned is %d, had %d\n", ++ __LINE__, ret_len, tv[i].der_len - 1); ++ return; ++ } ++ ++ if (str_size != tv[i].len ++ || grub_memcmp (tv[i].string, str, tv[i].len) != 0) ++ { ++ grub_fatal ( ++ "%d: memcmp: %s: got invalid decoding\n", ++ __LINE__, tv[i].name); ++ ++ return; ++ } ++ ++ /* Encode */ ++ der_len = sizeof (der); ++ asn1_octet_der (str, str_size, der, &der_len); ++ ++ if (der_len != tv[i].der_len - 1 ++ || grub_memcmp (tv[i].der_str + 1, der, tv[i].der_len - 1) != 0) ++ { ++ grub_fatal ( ++ "encoding: %s: got invalid encoding\n", ++ tv[i].name); ++ return; ++ } ++ } ++ ++ ret = ++ asn1_decode_simple_ber (ASN1_ETYPE_OCTET_STRING, ++ tv[i].der_str, tv[i].der_len, ++ &tmp, (unsigned int*)&str_size, (unsigned int*)&der_len); ++ if (ret != tv[i].expected_error) ++ { ++ grub_fatal ( ++ "%d: asn1_decode_simple_ber: %s: got %s expected %s\n", ++ __LINE__, tv[i].name, asn1_strerror(ret), asn1_strerror(tv[i].expected_error)); ++ return; ++ } ++ if (tv[i].expected_error) ++ continue; ++ ++ if (der_len != tv[i].der_len) ++ { ++ grub_fatal ( ++ "%d: error: %s: DER, length returned is %d, had %d\n", ++ __LINE__, tv[i].name, der_len, tv[i].der_len); ++ return; ++ } ++ ++ if (str_size != tv[i].len || grub_memcmp (tv[i].string, tmp, tv[i].len) != 0) ++ { ++ grub_fatal ( ++ "%d: memcmp: %s: got invalid decoding\n", ++ __LINE__, tv[i].name); ++ return; ++ } ++ grub_free (tmp); ++ tmp = NULL; ++ ++ } ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/tests/reproducers.c +@@ -0,0 +1,81 @@ ++/* ++ * Copyright (C) 2019 Free Software Foundation, Inc. ++ * ++ * This file is part of LIBTASN1. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ */ ++ ++/****************************************************************/ ++/* Description: run reproducers for several fixed issues */ ++/****************************************************************/ ++ ++#include ++#include ++#include ++#include "../wrap_tests.h" ++ ++#define CONST_DOWN (1U<<29) ++ ++/* produces endless loop (fixed by d4b624b2): ++ * The following translates into a single node with all pointers ++ * (right,left,down) set to NULL. */ ++const asn1_static_node endless_asn1_tab[] = { ++ { "TEST_TREE", 536875024, NULL }, ++ { NULL, 0, NULL } ++}; ++ ++/* produces memory leak (fixed by f16d1ff9): ++ * 152 bytes in 1 blocks are definitely lost in loss record 1 of 1 ++ * at 0x4837B65: calloc (vg_replace_malloc.c:762) ++ * by 0x4851C0D: _asn1_add_static_node (parser_aux.c:71) ++ * by 0x4853AAC: asn1_array2tree (structure.c:200) ++ * by 0x10923B: main (single_node.c:67) ++ */ ++const asn1_static_node tab[] = { ++{ "a", CONST_DOWN, "" }, ++{ "b", 0, "" }, ++{ "c", 0, "" }, ++{ NULL, 0, NULL } ++}; ++ ++void ++test_reproducers (void) ++{ ++ int result; ++ asn1_node definitions = NULL; ++ char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; ++ ++ result = asn1_array2tree (endless_asn1_tab, &definitions, errorDescription); ++ if (result != ASN1_SUCCESS) ++ { ++ grub_fatal ("Error: %s\nErrorDescription = %s\n\n", ++ asn1_strerror (result), errorDescription); ++ return; ++ } ++ ++ asn1_delete_structure (&definitions); ++ ++ definitions = NULL; ++ result = asn1_array2tree (tab, &definitions, errorDescription); ++ if (result != ASN1_SUCCESS) ++ { ++ grub_fatal ("Error: %s\nErrorDescription = %s\n\n", ++ asn1_strerror (result), errorDescription); ++ return; ++ } ++ ++ asn1_delete_structure (&definitions); ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/wrap_tests.c +@@ -0,0 +1,75 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2020 IBM Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include "wrap_tests.h" ++ ++/* ++ * libtasn1 tests - from which this is derived - are provided under GPL3+. ++ */ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++static grub_command_t cmd; ++ ++static grub_err_t ++grub_cmd_asn1test (grub_command_t cmdd __attribute__((unused)), ++ int argc __attribute__((unused)), ++ char **args __attribute__((unused))) ++{ ++ grub_printf ("test_CVE_2018_1000654\n"); ++ test_CVE_2018_1000654 (); ++ ++ grub_printf ("test_object_id_decoding\n"); ++ test_object_id_decoding (); ++ ++ grub_printf ("test_object_id_encoding\n"); ++ test_object_id_encoding (); ++ ++ grub_printf ("test_octet_string\n"); ++ test_octet_string (); ++ ++ grub_printf ("test_overflow\n"); ++ test_overflow (); ++ ++ grub_printf ("test_reproducers\n"); ++ test_overflow (); ++ ++ grub_printf ("test_simple\n"); ++ test_simple (); ++ ++ grub_printf ("test_strings\n"); ++ test_strings (); ++ ++ grub_printf ("ASN.1 self-tests passed\n"); ++ ++ return GRUB_ERR_NONE; ++} ++ ++ ++GRUB_MOD_INIT(test_asn1) ++{ ++ cmd = grub_register_command ("test_asn1", grub_cmd_asn1test, NULL, ++ "Run self-tests for the ASN.1 parser."); ++} ++ ++GRUB_MOD_FINI(test_asn1) ++{ ++ grub_unregister_command (cmd); ++} +--- /dev/null ++++ b/grub-core/lib/libtasn1_wrap/wrap_tests.h +@@ -0,0 +1,38 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2020 IBM Corporation ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef LIBTASN1_WRAP_TESTS_H ++#define LIBTASN1_WRAP_TESTS_H ++ ++void test_CVE_2018_1000654 (void); ++ ++void test_object_id_encoding (void); ++ ++void test_object_id_decoding (void); ++ ++void test_octet_string (void); ++ ++void test_overflow (void); ++ ++void test_reproducers (void); ++ ++void test_simple (void); ++ ++void test_strings (void); ++ ++#endif +--- /dev/null ++++ b/tests/test_asn1.in +@@ -0,0 +1,12 @@ ++#! @BUILD_SHEBANG@ ++set -e ++ ++. "@builddir@/grub-core/modinfo.sh" ++ ++out=`echo test_asn1 | @builddir@/grub-shell` ++ ++if [ "$(echo "$out" | tail -n 1)" != "ASN.1 self-tests passed" ]; then ++ echo "ASN.1 test failure: $out" ++ exit 1 ++fi ++ diff --git a/0016-grub-install-support-embedding-x509-certificates.patch b/0016-grub-install-support-embedding-x509-certificates.patch new file mode 100644 index 0000000..b15c6fa --- /dev/null +++ b/0016-grub-install-support-embedding-x509-certificates.patch @@ -0,0 +1,242 @@ +From a0e17ba00ac128759d40e2bfc751716f45542ac0 Mon Sep 17 00:00:00 2001 +From: Alastair D'Silva +Date: Mon, 6 Jul 2020 13:33:04 +1000 +Subject: [PATCH 16/23] grub-install: support embedding x509 certificates + +To support verification of appended signatures, we need a way to +embed the necessary public keys. Existing appended signature schemes +in the Linux kernel use X.509 certificates, so allow certificates to +be embedded in the grub core image in the same way as PGP keys. + +Signed-off-by: Alastair D'Silva +Signed-off-by: Daniel Axtens +--- + grub-core/commands/pgp.c | 2 +- + include/grub/kernel.h | 3 ++- + include/grub/util/install.h | 7 +++++-- + util/grub-install-common.c | 22 +++++++++++++++++++- + util/grub-mkimage.c | 15 ++++++++++++-- + util/mkimage.c | 41 ++++++++++++++++++++++++++++++++++--- + 6 files changed, 80 insertions(+), 10 deletions(-) + +--- a/grub-core/commands/pgp.c ++++ b/grub-core/commands/pgp.c +@@ -944,7 +944,7 @@ + grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); + + /* Not an ELF module, skip. */ +- if (header->type != OBJ_TYPE_PUBKEY) ++ if (header->type != OBJ_TYPE_GPG_PUBKEY) + continue; + + pseudo_file.fs = &pseudo_fs; +--- a/include/grub/kernel.h ++++ b/include/grub/kernel.h +@@ -28,7 +28,8 @@ + OBJ_TYPE_MEMDISK, + OBJ_TYPE_CONFIG, + OBJ_TYPE_PREFIX, +- OBJ_TYPE_PUBKEY, ++ OBJ_TYPE_GPG_PUBKEY, ++ OBJ_TYPE_X509_PUBKEY, + OBJ_TYPE_DTB, + OBJ_TYPE_DISABLE_SHIM_LOCK + }; +--- a/include/grub/util/install.h ++++ b/include/grub/util/install.h +@@ -67,6 +67,8 @@ + N_("SBAT metadata"), 0 }, \ + { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, \ + N_("disable shim_lock verifier"), 0 }, \ ++ { "x509key", 'x', N_("FILE"), 0, \ ++ N_("embed FILE as an x509 certificate for signature checking"), 0}, \ + { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE,\ + "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), \ + 1}, \ +@@ -190,8 +192,9 @@ + grub_install_generate_image (const char *dir, const char *prefix, + FILE *out, + const char *outname, char *mods[], +- char *memdisk_path, char **pubkey_paths, +- size_t npubkeys, ++ char *memdisk_path, ++ char **pubkey_paths, size_t npubkeys, ++ char **x509key_paths, size_t nx509keys, + char *config_path, + const struct grub_install_image_target_desc *image_target, + int note, size_t appsig_size, +--- a/util/grub-install-common.c ++++ b/util/grub-install-common.c +@@ -465,6 +465,8 @@ + static size_t npubkeys; + static char *sbat; + static int disable_shim_lock; ++static char **x509keys; ++static size_t nx509keys; + static grub_compression_t compression; + static size_t appsig_size; + +@@ -506,6 +508,12 @@ + case GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK: + disable_shim_lock = 1; + return 1; ++ case 'x': ++ x509keys = xrealloc (x509keys, ++ sizeof (x509keys[0]) ++ * (nx509keys + 1)); ++ x509keys[nx509keys++] = xstrdup (arg); ++ return 1; + + case GRUB_INSTALL_OPTIONS_VERBOSITY: + verbosity++; +@@ -632,6 +640,9 @@ + for (pk = pubkeys; pk < pubkeys + npubkeys; pk++) + slen += 20 + grub_strlen (*pk); + ++ for (pk = x509keys; pk < x509keys + nx509keys; pk++) ++ slen += 10 + grub_strlen (*pk); ++ + for (md = modules.entries; *md; md++) + { + slen += 10 + grub_strlen (*md); +@@ -660,6 +671,14 @@ + *p++ = ' '; + } + ++ for (pk = x509keys; pk < x509keys + nx509keys; pk++) ++ { ++ p = grub_stpcpy (p, "--x509 '"); ++ p = grub_stpcpy (p, *pk); ++ *p++ = '\''; ++ *p++ = ' '; ++ } ++ + for (md = modules.entries; *md; md++) + { + *p++ = '\''; +@@ -688,7 +707,8 @@ + + grub_install_generate_image (dir, prefix, fp, outname, + modules.entries, memdisk_path, +- pubkeys, npubkeys, config_path, tgt, ++ pubkeys, npubkeys, x509keys, nx509keys, ++ config_path, tgt, + note, appsig_size, compression, dtb, sbat, + disable_shim_lock); + while (dc--) +--- a/util/grub-mkimage.c ++++ b/util/grub-mkimage.c +@@ -75,7 +75,8 @@ + /* TRANSLATORS: "embed" is a verb (command description). "*/ + {"config", 'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0}, + /* TRANSLATORS: "embed" is a verb (command description). "*/ +- {"pubkey", 'k', N_("FILE"), 0, N_("embed FILE as public key for signature checking"), 0}, ++ {"pubkey", 'k', N_("FILE"), 0, N_("embed FILE as public key for PGP signature checking"), 0}, ++ {"x509", 'x', N_("FILE"), 0, N_("embed FILE as an x509 certificate for appended signature checking"), 0}, + /* TRANSLATORS: NOTE is a name of segment. */ + {"note", 'n', 0, 0, N_("add NOTE segment for CHRP IEEE1275"), 0}, + {"output", 'o', N_("FILE"), 0, N_("output a generated image to FILE [default=stdout]"), 0}, +@@ -124,6 +125,8 @@ + char *dtb; + char **pubkeys; + size_t npubkeys; ++ char **x509keys; ++ size_t nx509keys; + char *font; + char *config; + char *sbat; +@@ -206,6 +209,13 @@ + arguments->pubkeys[arguments->npubkeys++] = xstrdup (arg); + break; + ++ case 'x': ++ arguments->x509keys = xrealloc (arguments->x509keys, ++ sizeof (arguments->x509keys[0]) ++ * (arguments->nx509keys + 1)); ++ arguments->x509keys[arguments->nx509keys++] = xstrdup (arg); ++ break; ++ + case 'c': + if (arguments->config) + free (arguments->config); +@@ -332,7 +342,8 @@ + grub_install_generate_image (arguments.dir, arguments.prefix, fp, + arguments.output, arguments.modules, + arguments.memdisk, arguments.pubkeys, +- arguments.npubkeys, arguments.config, ++ arguments.npubkeys, arguments.x509keys, ++ arguments.nx509keys, arguments.config, + arguments.image_target, arguments.note, + arguments.appsig_size, + arguments.comp, arguments.dtb, +--- a/util/mkimage.c ++++ b/util/mkimage.c +@@ -882,8 +882,10 @@ + void + grub_install_generate_image (const char *dir, const char *prefix, + FILE *out, const char *outname, char *mods[], +- char *memdisk_path, char **pubkey_paths, +- size_t npubkeys, char *config_path, ++ char *memdisk_path, ++ char **pubkey_paths, size_t npubkeys, ++ char **x509key_paths, size_t nx509keys, ++ char *config_path, + const struct grub_install_image_target_desc *image_target, + int note, size_t appsig_size, grub_compression_t comp, + const char *dtb_path, const char *sbat_path, +@@ -929,6 +931,19 @@ + } + } + ++ { ++ size_t i; ++ for (i = 0; i < nx509keys; i++) ++ { ++ size_t curs; ++ curs = ALIGN_ADDR (grub_util_get_image_size (x509key_paths[i])); ++ grub_util_info ("the size of x509 public key %u is 0x%" ++ GRUB_HOST_PRIxLONG_LONG, ++ (unsigned) i, (unsigned long long) curs); ++ total_module_size += curs + sizeof (struct grub_module_header); ++ } ++ } ++ + if (memdisk_path) + { + memdisk_size = ALIGN_UP(grub_util_get_image_size (memdisk_path), 512); +@@ -1050,7 +1065,7 @@ + curs = grub_util_get_image_size (pubkey_paths[i]); + + header = (struct grub_module_header *) (kernel_img + offset); +- header->type = grub_host_to_target32 (OBJ_TYPE_PUBKEY); ++ header->type = grub_host_to_target32 (OBJ_TYPE_GPG_PUBKEY); + header->size = grub_host_to_target32 (curs + sizeof (*header)); + offset += sizeof (*header); + +@@ -1059,6 +1074,26 @@ + } + } + ++ { ++ size_t i; ++ for (i = 0; i < nx509keys; i++) ++ { ++ size_t curs; ++ struct grub_module_header *header; ++ ++ curs = grub_util_get_image_size (x509key_paths[i]); ++ ++ header = (struct grub_module_header *) (kernel_img + offset); ++ header->type = grub_host_to_target32 (OBJ_TYPE_X509_PUBKEY); ++ header->size = grub_host_to_target32 (curs + sizeof (*header)); ++ offset += sizeof (*header); ++ ++ grub_util_load_image (x509key_paths[i], kernel_img + offset); ++ offset += ALIGN_ADDR (curs); ++ } ++ } ++ ++ + if (memdisk_path) + { + struct grub_module_header *header; diff --git a/0017-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch b/0017-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch new file mode 100644 index 0000000..38b25da --- /dev/null +++ b/0017-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch @@ -0,0 +1,634 @@ +From f948da0bef477fe9d95fbcb13eb2c4f45f1b6ea0 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Thu, 30 Jul 2020 01:35:10 +1000 +Subject: [PATCH 17/23] appended signatures: import GNUTLS's ASN.1 description + files + +In order to parse PKCS#7 messages and X.509 certificates with libtasn1, +we need some information about how they are encoded. + +We get these from GNUTLS, which has the benefit that they support the +features we need and are well tested. + +The GNUTLS license is LGPLv2.1+, which is GPLv3 compatible, allowing +us to import it without issue. + +Signed-off-by: Daniel Axtens +--- + .../commands/appendedsig/gnutls_asn1_tab.c | 121 +++++ + .../commands/appendedsig/pkix_asn1_tab.c | 484 ++++++++++++++++++ + 2 files changed, 605 insertions(+) + create mode 100644 grub-core/commands/appendedsig/gnutls_asn1_tab.c + create mode 100644 grub-core/commands/appendedsig/pkix_asn1_tab.c + +--- /dev/null ++++ b/grub-core/commands/appendedsig/gnutls_asn1_tab.c +@@ -0,0 +1,121 @@ ++#include ++#include ++ ++const asn1_static_node gnutls_asn1_tab[] = { ++ { "GNUTLS", 536872976, NULL }, ++ { NULL, 1073741836, NULL }, ++ { "RSAPublicKey", 1610612741, NULL }, ++ { "modulus", 1073741827, NULL }, ++ { "publicExponent", 3, NULL }, ++ { "RSAPrivateKey", 1610612741, NULL }, ++ { "version", 1073741827, NULL }, ++ { "modulus", 1073741827, NULL }, ++ { "publicExponent", 1073741827, NULL }, ++ { "privateExponent", 1073741827, NULL }, ++ { "prime1", 1073741827, NULL }, ++ { "prime2", 1073741827, NULL }, ++ { "exponent1", 1073741827, NULL }, ++ { "exponent2", 1073741827, NULL }, ++ { "coefficient", 1073741827, NULL }, ++ { "otherPrimeInfos", 16386, "OtherPrimeInfos"}, ++ { "ProvableSeed", 1610612741, NULL }, ++ { "algorithm", 1073741836, NULL }, ++ { "seed", 7, NULL }, ++ { "OtherPrimeInfos", 1612709899, NULL }, ++ { "MAX", 1074266122, "1"}, ++ { NULL, 2, "OtherPrimeInfo"}, ++ { "OtherPrimeInfo", 1610612741, NULL }, ++ { "prime", 1073741827, NULL }, ++ { "exponent", 1073741827, NULL }, ++ { "coefficient", 3, NULL }, ++ { "AlgorithmIdentifier", 1610612741, NULL }, ++ { "algorithm", 1073741836, NULL }, ++ { "parameters", 541081613, NULL }, ++ { "algorithm", 1, NULL }, ++ { "DigestInfo", 1610612741, NULL }, ++ { "digestAlgorithm", 1073741826, "DigestAlgorithmIdentifier"}, ++ { "digest", 7, NULL }, ++ { "DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, ++ { "DSAPublicKey", 1073741827, NULL }, ++ { "DSAParameters", 1610612741, NULL }, ++ { "p", 1073741827, NULL }, ++ { "q", 1073741827, NULL }, ++ { "g", 3, NULL }, ++ { "DSASignatureValue", 1610612741, NULL }, ++ { "r", 1073741827, NULL }, ++ { "s", 3, NULL }, ++ { "DSAPrivateKey", 1610612741, NULL }, ++ { "version", 1073741827, NULL }, ++ { "p", 1073741827, NULL }, ++ { "q", 1073741827, NULL }, ++ { "g", 1073741827, NULL }, ++ { "Y", 1073741827, NULL }, ++ { "priv", 3, NULL }, ++ { "DHParameter", 1610612741, NULL }, ++ { "prime", 1073741827, NULL }, ++ { "base", 1073741827, NULL }, ++ { "privateValueLength", 16387, NULL }, ++ { "ECParameters", 1610612754, NULL }, ++ { "namedCurve", 12, NULL }, ++ { "ECPrivateKey", 1610612741, NULL }, ++ { "Version", 1073741827, NULL }, ++ { "privateKey", 1073741831, NULL }, ++ { "parameters", 1610637314, "ECParameters"}, ++ { NULL, 2056, "0"}, ++ { "publicKey", 536895494, NULL }, ++ { NULL, 2056, "1"}, ++ { "PrincipalName", 1610612741, NULL }, ++ { "name-type", 1610620931, NULL }, ++ { NULL, 2056, "0"}, ++ { "name-string", 536879115, NULL }, ++ { NULL, 1073743880, "1"}, ++ { NULL, 27, NULL }, ++ { "KRB5PrincipalName", 1610612741, NULL }, ++ { "realm", 1610620955, NULL }, ++ { NULL, 2056, "0"}, ++ { "principalName", 536879106, "PrincipalName"}, ++ { NULL, 2056, "1"}, ++ { "RSAPSSParameters", 1610612741, NULL }, ++ { "hashAlgorithm", 1610637314, "AlgorithmIdentifier"}, ++ { NULL, 2056, "0"}, ++ { "maskGenAlgorithm", 1610637314, "AlgorithmIdentifier"}, ++ { NULL, 2056, "1"}, ++ { "saltLength", 1610653699, NULL }, ++ { NULL, 1073741833, "20"}, ++ { NULL, 2056, "2"}, ++ { "trailerField", 536911875, NULL }, ++ { NULL, 1073741833, "1"}, ++ { NULL, 2056, "3"}, ++ { "GOSTParameters", 1610612741, NULL }, ++ { "publicKeyParamSet", 1073741836, NULL }, ++ { "digestParamSet", 16396, NULL }, ++ { "GOSTParametersOld", 1610612741, NULL }, ++ { "publicKeyParamSet", 1073741836, NULL }, ++ { "digestParamSet", 1073741836, NULL }, ++ { "encryptionParamSet", 16396, NULL }, ++ { "GOSTPrivateKey", 1073741831, NULL }, ++ { "GOSTPrivateKeyOld", 1073741827, NULL }, ++ { "IssuerSignTool", 1610612741, NULL }, ++ { "signTool", 1073741858, NULL }, ++ { "cATool", 1073741858, NULL }, ++ { "signToolCert", 1073741858, NULL }, ++ { "cAToolCert", 34, NULL }, ++ { "Gost28147-89-EncryptedKey", 1610612741, NULL }, ++ { "encryptedKey", 1073741831, NULL }, ++ { "maskKey", 1610637319, NULL }, ++ { NULL, 4104, "0"}, ++ { "macKey", 7, NULL }, ++ { "SubjectPublicKeyInfo", 1610612741, NULL }, ++ { "algorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "subjectPublicKey", 6, NULL }, ++ { "GostR3410-TransportParameters", 1610612741, NULL }, ++ { "encryptionParamSet", 1073741836, NULL }, ++ { "ephemeralPublicKey", 1610637314, "SubjectPublicKeyInfo"}, ++ { NULL, 4104, "0"}, ++ { "ukm", 7, NULL }, ++ { "GostR3410-KeyTransport", 536870917, NULL }, ++ { "sessionEncryptedKey", 1073741826, "Gost28147-89-EncryptedKey"}, ++ { "transportParameters", 536895490, "GostR3410-TransportParameters"}, ++ { NULL, 4104, "0"}, ++ { NULL, 0, NULL } ++}; +--- /dev/null ++++ b/grub-core/commands/appendedsig/pkix_asn1_tab.c +@@ -0,0 +1,484 @@ ++#include ++#include ++ ++const asn1_static_node pkix_asn1_tab[] = { ++ { "PKIX1", 536875024, NULL }, ++ { NULL, 1073741836, NULL }, ++ { "PrivateKeyUsagePeriod", 1610612741, NULL }, ++ { "notBefore", 1610637349, NULL }, ++ { NULL, 4104, "0"}, ++ { "notAfter", 536895525, NULL }, ++ { NULL, 4104, "1"}, ++ { "AuthorityKeyIdentifier", 1610612741, NULL }, ++ { "keyIdentifier", 1610637319, NULL }, ++ { NULL, 4104, "0"}, ++ { "authorityCertIssuer", 1610637314, "GeneralNames"}, ++ { NULL, 4104, "1"}, ++ { "authorityCertSerialNumber", 536895490, "CertificateSerialNumber"}, ++ { NULL, 4104, "2"}, ++ { "SubjectKeyIdentifier", 1073741831, NULL }, ++ { "KeyUsage", 1073741830, NULL }, ++ { "DirectoryString", 1610612754, NULL }, ++ { "teletexString", 1612709918, NULL }, ++ { "MAX", 524298, "1"}, ++ { "printableString", 1612709919, NULL }, ++ { "MAX", 524298, "1"}, ++ { "universalString", 1612709920, NULL }, ++ { "MAX", 524298, "1"}, ++ { "utf8String", 1612709922, NULL }, ++ { "MAX", 524298, "1"}, ++ { "bmpString", 1612709921, NULL }, ++ { "MAX", 524298, "1"}, ++ { "ia5String", 538968093, NULL }, ++ { "MAX", 524298, "1"}, ++ { "SubjectAltName", 1073741826, "GeneralNames"}, ++ { "GeneralNames", 1612709899, NULL }, ++ { "MAX", 1074266122, "1"}, ++ { NULL, 2, "GeneralName"}, ++ { "GeneralName", 1610612754, NULL }, ++ { "otherName", 1610620930, "AnotherName"}, ++ { NULL, 4104, "0"}, ++ { "rfc822Name", 1610620957, NULL }, ++ { NULL, 4104, "1"}, ++ { "dNSName", 1610620957, NULL }, ++ { NULL, 4104, "2"}, ++ { "x400Address", 1610620941, NULL }, ++ { NULL, 4104, "3"}, ++ { "directoryName", 1610620939, NULL }, ++ { NULL, 1073743880, "4"}, ++ { NULL, 2, "RelativeDistinguishedName"}, ++ { "ediPartyName", 1610620941, NULL }, ++ { NULL, 4104, "5"}, ++ { "uniformResourceIdentifier", 1610620957, NULL }, ++ { NULL, 4104, "6"}, ++ { "iPAddress", 1610620935, NULL }, ++ { NULL, 4104, "7"}, ++ { "registeredID", 536879116, NULL }, ++ { NULL, 4104, "8"}, ++ { "AnotherName", 1610612741, NULL }, ++ { "type-id", 1073741836, NULL }, ++ { "value", 541073421, NULL }, ++ { NULL, 1073743880, "0"}, ++ { "type-id", 1, NULL }, ++ { "IssuerAltName", 1073741826, "GeneralNames"}, ++ { "BasicConstraints", 1610612741, NULL }, ++ { "cA", 1610645508, NULL }, ++ { NULL, 131081, NULL }, ++ { "pathLenConstraint", 537411587, NULL }, ++ { "0", 10, "MAX"}, ++ { "CRLDistributionPoints", 1612709899, NULL }, ++ { "MAX", 1074266122, "1"}, ++ { NULL, 2, "DistributionPoint"}, ++ { "DistributionPoint", 1610612741, NULL }, ++ { "distributionPoint", 1610637314, "DistributionPointName"}, ++ { NULL, 2056, "0"}, ++ { "reasons", 1610637314, "ReasonFlags"}, ++ { NULL, 4104, "1"}, ++ { "cRLIssuer", 536895490, "GeneralNames"}, ++ { NULL, 4104, "2"}, ++ { "DistributionPointName", 1610612754, NULL }, ++ { "fullName", 1610620930, "GeneralNames"}, ++ { NULL, 4104, "0"}, ++ { "nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"}, ++ { NULL, 4104, "1"}, ++ { "ReasonFlags", 1073741830, NULL }, ++ { "ExtKeyUsageSyntax", 1612709899, NULL }, ++ { "MAX", 1074266122, "1"}, ++ { NULL, 12, NULL }, ++ { "AuthorityInfoAccessSyntax", 1612709899, NULL }, ++ { "MAX", 1074266122, "1"}, ++ { NULL, 2, "AccessDescription"}, ++ { "AccessDescription", 1610612741, NULL }, ++ { "accessMethod", 1073741836, NULL }, ++ { "accessLocation", 2, "GeneralName"}, ++ { "Attribute", 1610612741, NULL }, ++ { "type", 1073741836, NULL }, ++ { "values", 536870927, NULL }, ++ { NULL, 13, NULL }, ++ { "AttributeTypeAndValue", 1610612741, NULL }, ++ { "type", 1073741836, NULL }, ++ { "value", 13, NULL }, ++ { "Name", 1610612754, NULL }, ++ { "rdnSequence", 536870923, NULL }, ++ { NULL, 2, "RelativeDistinguishedName"}, ++ { "DistinguishedName", 1610612747, NULL }, ++ { NULL, 2, "RelativeDistinguishedName"}, ++ { "RelativeDistinguishedName", 1612709903, NULL }, ++ { "MAX", 1074266122, "1"}, ++ { NULL, 2, "AttributeTypeAndValue"}, ++ { "Certificate", 1610612741, NULL }, ++ { "tbsCertificate", 1073741826, "TBSCertificate"}, ++ { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "signature", 6, NULL }, ++ { "TBSCertificate", 1610612741, NULL }, ++ { "version", 1610653699, NULL }, ++ { NULL, 1073741833, "0"}, ++ { NULL, 2056, "0"}, ++ { "serialNumber", 1073741826, "CertificateSerialNumber"}, ++ { "signature", 1073741826, "AlgorithmIdentifier"}, ++ { "issuer", 1073741826, "Name"}, ++ { "validity", 1073741826, "Validity"}, ++ { "subject", 1073741826, "Name"}, ++ { "subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"}, ++ { "issuerUniqueID", 1610637314, "UniqueIdentifier"}, ++ { NULL, 4104, "1"}, ++ { "subjectUniqueID", 1610637314, "UniqueIdentifier"}, ++ { NULL, 4104, "2"}, ++ { "extensions", 536895490, "Extensions"}, ++ { NULL, 2056, "3"}, ++ { "CertificateSerialNumber", 1073741827, NULL }, ++ { "Validity", 1610612741, NULL }, ++ { "notBefore", 1073741826, "Time"}, ++ { "notAfter", 2, "Time"}, ++ { "Time", 1610612754, NULL }, ++ { "utcTime", 1073741860, NULL }, ++ { "generalTime", 37, NULL }, ++ { "UniqueIdentifier", 1073741830, NULL }, ++ { "SubjectPublicKeyInfo", 1610612741, NULL }, ++ { "algorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "subjectPublicKey", 6, NULL }, ++ { "Extensions", 1612709899, NULL }, ++ { "MAX", 1074266122, "1"}, ++ { NULL, 2, "Extension"}, ++ { "Extension", 1610612741, NULL }, ++ { "extnID", 1073741836, NULL }, ++ { "critical", 1610645508, NULL }, ++ { NULL, 131081, NULL }, ++ { "extnValue", 7, NULL }, ++ { "CertificateList", 1610612741, NULL }, ++ { "tbsCertList", 1073741826, "TBSCertList"}, ++ { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "signature", 6, NULL }, ++ { "TBSCertList", 1610612741, NULL }, ++ { "version", 1073758211, NULL }, ++ { "signature", 1073741826, "AlgorithmIdentifier"}, ++ { "issuer", 1073741826, "Name"}, ++ { "thisUpdate", 1073741826, "Time"}, ++ { "nextUpdate", 1073758210, "Time"}, ++ { "revokedCertificates", 1610629131, NULL }, ++ { NULL, 536870917, NULL }, ++ { "userCertificate", 1073741826, "CertificateSerialNumber"}, ++ { "revocationDate", 1073741826, "Time"}, ++ { "crlEntryExtensions", 16386, "Extensions"}, ++ { "crlExtensions", 536895490, "Extensions"}, ++ { NULL, 2056, "0"}, ++ { "AlgorithmIdentifier", 1610612741, NULL }, ++ { "algorithm", 1073741836, NULL }, ++ { "parameters", 541081613, NULL }, ++ { "algorithm", 1, NULL }, ++ { "Dss-Sig-Value", 1610612741, NULL }, ++ { "r", 1073741827, NULL }, ++ { "s", 3, NULL }, ++ { "Dss-Parms", 1610612741, NULL }, ++ { "p", 1073741827, NULL }, ++ { "q", 1073741827, NULL }, ++ { "g", 3, NULL }, ++ { "pkcs-7-ContentInfo", 1610612741, NULL }, ++ { "contentType", 1073741836, NULL }, ++ { "content", 541073421, NULL }, ++ { NULL, 1073743880, "0"}, ++ { "contentType", 1, NULL }, ++ { "pkcs-7-DigestInfo", 1610612741, NULL }, ++ { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "digest", 7, NULL }, ++ { "pkcs-7-SignedData", 1610612741, NULL }, ++ { "version", 1073741827, NULL }, ++ { "digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"}, ++ { "encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"}, ++ { "certificates", 1610637314, "pkcs-7-CertificateSet"}, ++ { NULL, 4104, "0"}, ++ { "crls", 1610637314, "pkcs-7-CertificateRevocationLists"}, ++ { NULL, 4104, "1"}, ++ { "signerInfos", 2, "pkcs-7-SignerInfos"}, ++ { "pkcs-7-DigestAlgorithmIdentifiers", 1610612751, NULL }, ++ { NULL, 2, "AlgorithmIdentifier"}, ++ { "pkcs-7-EncapsulatedContentInfo", 1610612741, NULL }, ++ { "eContentType", 1073741836, NULL }, ++ { "eContent", 536895501, NULL }, ++ { NULL, 2056, "0"}, ++ { "pkcs-7-CertificateRevocationLists", 1610612751, NULL }, ++ { NULL, 13, NULL }, ++ { "pkcs-7-CertificateChoices", 1610612754, NULL }, ++ { "certificate", 13, NULL }, ++ { "pkcs-7-CertificateSet", 1610612751, NULL }, ++ { NULL, 2, "pkcs-7-CertificateChoices"}, ++ { "IssuerAndSerialNumber", 1610612741, NULL }, ++ { "issuer", 1073741826, "Name"}, ++ { "serialNumber", 2, "CertificateSerialNumber"}, ++ { "pkcs-7-SignerInfo", 1610612741, NULL }, ++ { "version", 1073741827, NULL }, ++ { "sid", 1073741826, "SignerIdentifier"}, ++ { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "signedAttrs", 1610637314, "SignedAttributes"}, ++ { NULL, 4104, "0"}, ++ { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "signature", 1073741831, NULL }, ++ { "unsignedAttrs", 536895490, "SignedAttributes"}, ++ { NULL, 4104, "1"}, ++ { "SignedAttributes", 1612709903, NULL }, ++ { "MAX", 1074266122, "1"}, ++ { NULL, 2, "Attribute"}, ++ { "SignerIdentifier", 1610612754, NULL }, ++ { "issuerAndSerialNumber", 1073741826, "IssuerAndSerialNumber"}, ++ { "subjectKeyIdentifier", 536879111, NULL }, ++ { NULL, 4104, "0"}, ++ { "pkcs-7-SignerInfos", 1610612751, NULL }, ++ { NULL, 2, "pkcs-7-SignerInfo"}, ++ { "pkcs-10-CertificationRequestInfo", 1610612741, NULL }, ++ { "version", 1073741827, NULL }, ++ { "subject", 1073741826, "Name"}, ++ { "subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"}, ++ { "attributes", 536879106, "Attributes"}, ++ { NULL, 4104, "0"}, ++ { "Attributes", 1610612751, NULL }, ++ { NULL, 2, "Attribute"}, ++ { "pkcs-10-CertificationRequest", 1610612741, NULL }, ++ { "certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"}, ++ { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "signature", 6, NULL }, ++ { "pkcs-9-at-challengePassword", 1879048204, NULL }, ++ { "iso", 1073741825, "1"}, ++ { "member-body", 1073741825, "2"}, ++ { "us", 1073741825, "840"}, ++ { "rsadsi", 1073741825, "113549"}, ++ { "pkcs", 1073741825, "1"}, ++ { NULL, 1073741825, "9"}, ++ { NULL, 1, "7"}, ++ { "pkcs-9-challengePassword", 1610612754, NULL }, ++ { "printableString", 1073741855, NULL }, ++ { "utf8String", 34, NULL }, ++ { "pkcs-9-localKeyId", 1073741831, NULL }, ++ { "pkcs-8-PrivateKeyInfo", 1610612741, NULL }, ++ { "version", 1073741827, NULL }, ++ { "privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "privateKey", 1073741831, NULL }, ++ { "attributes", 536895490, "Attributes"}, ++ { NULL, 4104, "0"}, ++ { "pkcs-8-EncryptedPrivateKeyInfo", 1610612741, NULL }, ++ { "encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "encryptedData", 2, "pkcs-8-EncryptedData"}, ++ { "pkcs-8-EncryptedData", 1073741831, NULL }, ++ { "pkcs-5-des-CBC-params", 1612709895, NULL }, ++ { NULL, 1048586, "8"}, ++ { "pkcs-5-des-EDE3-CBC-params", 1612709895, NULL }, ++ { NULL, 1048586, "8"}, ++ { "pkcs-5-aes128-CBC-params", 1612709895, NULL }, ++ { NULL, 1048586, "16"}, ++ { "pkcs-5-aes192-CBC-params", 1612709895, NULL }, ++ { NULL, 1048586, "16"}, ++ { "pkcs-5-aes256-CBC-params", 1612709895, NULL }, ++ { NULL, 1048586, "16"}, ++ { "Gost28147-89-Parameters", 1610612741, NULL }, ++ { "iv", 1073741831, NULL }, ++ { "encryptionParamSet", 12, NULL }, ++ { "pkcs-5-PBE-params", 1610612741, NULL }, ++ { "salt", 1073741831, NULL }, ++ { "iterationCount", 3, NULL }, ++ { "pkcs-5-PBES2-params", 1610612741, NULL }, ++ { "keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, ++ { "encryptionScheme", 2, "AlgorithmIdentifier"}, ++ { "pkcs-5-PBKDF2-params", 1610612741, NULL }, ++ { "salt", 1610612754, NULL }, ++ { "specified", 1073741831, NULL }, ++ { "otherSource", 2, "AlgorithmIdentifier"}, ++ { "iterationCount", 1611137027, NULL }, ++ { "1", 10, "MAX"}, ++ { "keyLength", 1611153411, NULL }, ++ { "1", 10, "MAX"}, ++ { "prf", 16386, "AlgorithmIdentifier"}, ++ { "pkcs-12-PFX", 1610612741, NULL }, ++ { "version", 1610874883, NULL }, ++ { "v3", 1, "3"}, ++ { "authSafe", 1073741826, "pkcs-7-ContentInfo"}, ++ { "macData", 16386, "pkcs-12-MacData"}, ++ { "pkcs-12-PbeParams", 1610612741, NULL }, ++ { "salt", 1073741831, NULL }, ++ { "iterations", 3, NULL }, ++ { "pkcs-12-MacData", 1610612741, NULL }, ++ { "mac", 1073741826, "pkcs-7-DigestInfo"}, ++ { "macSalt", 1073741831, NULL }, ++ { "iterations", 536903683, NULL }, ++ { NULL, 9, "1"}, ++ { "pkcs-12-AuthenticatedSafe", 1610612747, NULL }, ++ { NULL, 2, "pkcs-7-ContentInfo"}, ++ { "pkcs-12-SafeContents", 1610612747, NULL }, ++ { NULL, 2, "pkcs-12-SafeBag"}, ++ { "pkcs-12-SafeBag", 1610612741, NULL }, ++ { "bagId", 1073741836, NULL }, ++ { "bagValue", 1614815245, NULL }, ++ { NULL, 1073743880, "0"}, ++ { "badId", 1, NULL }, ++ { "bagAttributes", 536887311, NULL }, ++ { NULL, 2, "Attribute"}, ++ { "pkcs-12-CertBag", 1610612741, NULL }, ++ { "certId", 1073741836, NULL }, ++ { "certValue", 541073421, NULL }, ++ { NULL, 1073743880, "0"}, ++ { "certId", 1, NULL }, ++ { "pkcs-12-CRLBag", 1610612741, NULL }, ++ { "crlId", 1073741836, NULL }, ++ { "crlValue", 541073421, NULL }, ++ { NULL, 1073743880, "0"}, ++ { "crlId", 1, NULL }, ++ { "pkcs-12-SecretBag", 1610612741, NULL }, ++ { "secretTypeId", 1073741836, NULL }, ++ { "secretValue", 541073421, NULL }, ++ { NULL, 1073743880, "0"}, ++ { "secretTypeId", 1, NULL }, ++ { "pkcs-7-Data", 1073741831, NULL }, ++ { "pkcs-7-EncryptedData", 1610612741, NULL }, ++ { "version", 1073741827, NULL }, ++ { "encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"}, ++ { "unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"}, ++ { NULL, 4104, "1"}, ++ { "pkcs-7-EncryptedContentInfo", 1610612741, NULL }, ++ { "contentType", 1073741836, NULL }, ++ { "contentEncryptionAlgorithm", 1073741826, "pkcs-7-ContentEncryptionAlgorithmIdentifier"}, ++ { "encryptedContent", 536895495, NULL }, ++ { NULL, 4104, "0"}, ++ { "pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, ++ { "pkcs-7-UnprotectedAttributes", 1612709903, NULL }, ++ { "MAX", 1074266122, "1"}, ++ { NULL, 2, "Attribute"}, ++ { "ProxyCertInfo", 1610612741, NULL }, ++ { "pCPathLenConstraint", 1611153411, NULL }, ++ { "0", 10, "MAX"}, ++ { "proxyPolicy", 2, "ProxyPolicy"}, ++ { "ProxyPolicy", 1610612741, NULL }, ++ { "policyLanguage", 1073741836, NULL }, ++ { "policy", 16391, NULL }, ++ { "certificatePolicies", 1612709899, NULL }, ++ { "MAX", 1074266122, "1"}, ++ { NULL, 2, "PolicyInformation"}, ++ { "PolicyInformation", 1610612741, NULL }, ++ { "policyIdentifier", 1073741836, NULL }, ++ { "policyQualifiers", 538984459, NULL }, ++ { "MAX", 1074266122, "1"}, ++ { NULL, 2, "PolicyQualifierInfo"}, ++ { "PolicyQualifierInfo", 1610612741, NULL }, ++ { "policyQualifierId", 1073741836, NULL }, ++ { "qualifier", 541065229, NULL }, ++ { "policyQualifierId", 1, NULL }, ++ { "CPSuri", 1073741853, NULL }, ++ { "UserNotice", 1610612741, NULL }, ++ { "noticeRef", 1073758210, "NoticeReference"}, ++ { "explicitText", 16386, "DisplayText"}, ++ { "NoticeReference", 1610612741, NULL }, ++ { "organization", 1073741826, "DisplayText"}, ++ { "noticeNumbers", 536870923, NULL }, ++ { NULL, 3, NULL }, ++ { "DisplayText", 1610612754, NULL }, ++ { "ia5String", 1612709917, NULL }, ++ { "200", 524298, "1"}, ++ { "visibleString", 1612709923, NULL }, ++ { "200", 524298, "1"}, ++ { "bmpString", 1612709921, NULL }, ++ { "200", 524298, "1"}, ++ { "utf8String", 538968098, NULL }, ++ { "200", 524298, "1"}, ++ { "OCSPRequest", 1610612741, NULL }, ++ { "tbsRequest", 1073741826, "TBSRequest"}, ++ { "optionalSignature", 536895490, "Signature"}, ++ { NULL, 2056, "0"}, ++ { "TBSRequest", 1610612741, NULL }, ++ { "version", 1610653699, NULL }, ++ { NULL, 1073741833, "0"}, ++ { NULL, 2056, "0"}, ++ { "requestorName", 1610637314, "GeneralName"}, ++ { NULL, 2056, "1"}, ++ { "requestList", 1610612747, NULL }, ++ { NULL, 2, "Request"}, ++ { "requestExtensions", 536895490, "Extensions"}, ++ { NULL, 2056, "2"}, ++ { "Signature", 1610612741, NULL }, ++ { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "signature", 1073741830, NULL }, ++ { "certs", 536895499, NULL }, ++ { NULL, 1073743880, "0"}, ++ { NULL, 2, "Certificate"}, ++ { "Request", 1610612741, NULL }, ++ { "reqCert", 1073741826, "CertID"}, ++ { "singleRequestExtensions", 536895490, "Extensions"}, ++ { NULL, 2056, "0"}, ++ { "CertID", 1610612741, NULL }, ++ { "hashAlgorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "issuerNameHash", 1073741831, NULL }, ++ { "issuerKeyHash", 1073741831, NULL }, ++ { "serialNumber", 2, "CertificateSerialNumber"}, ++ { "OCSPResponse", 1610612741, NULL }, ++ { "responseStatus", 1073741826, "OCSPResponseStatus"}, ++ { "responseBytes", 536895490, "ResponseBytes"}, ++ { NULL, 2056, "0"}, ++ { "OCSPResponseStatus", 1610874901, NULL }, ++ { "successful", 1073741825, "0"}, ++ { "malformedRequest", 1073741825, "1"}, ++ { "internalError", 1073741825, "2"}, ++ { "tryLater", 1073741825, "3"}, ++ { "sigRequired", 1073741825, "5"}, ++ { "unauthorized", 1, "6"}, ++ { "ResponseBytes", 1610612741, NULL }, ++ { "responseType", 1073741836, NULL }, ++ { "response", 7, NULL }, ++ { "BasicOCSPResponse", 1610612741, NULL }, ++ { "tbsResponseData", 1073741826, "ResponseData"}, ++ { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, ++ { "signature", 1073741830, NULL }, ++ { "certs", 536895499, NULL }, ++ { NULL, 1073743880, "0"}, ++ { NULL, 2, "Certificate"}, ++ { "ResponseData", 1610612741, NULL }, ++ { "version", 1610653699, NULL }, ++ { NULL, 1073741833, "0"}, ++ { NULL, 2056, "0"}, ++ { "responderID", 1073741826, "ResponderID"}, ++ { "producedAt", 1073741861, NULL }, ++ { "responses", 1610612747, NULL }, ++ { NULL, 2, "SingleResponse"}, ++ { "responseExtensions", 536895490, "Extensions"}, ++ { NULL, 2056, "1"}, ++ { "ResponderID", 1610612754, NULL }, ++ { "byName", 1610620939, NULL }, ++ { NULL, 1073743880, "1"}, ++ { NULL, 2, "RelativeDistinguishedName"}, ++ { "byKey", 536879111, NULL }, ++ { NULL, 2056, "2"}, ++ { "SingleResponse", 1610612741, NULL }, ++ { "certID", 1073741826, "CertID"}, ++ { "certStatus", 1073741826, "CertStatus"}, ++ { "thisUpdate", 1073741861, NULL }, ++ { "nextUpdate", 1610637349, NULL }, ++ { NULL, 2056, "0"}, ++ { "singleExtensions", 536895490, "Extensions"}, ++ { NULL, 2056, "1"}, ++ { "CertStatus", 1610612754, NULL }, ++ { "good", 1610620948, NULL }, ++ { NULL, 4104, "0"}, ++ { "revoked", 1610620930, "RevokedInfo"}, ++ { NULL, 4104, "1"}, ++ { "unknown", 536879106, "UnknownInfo"}, ++ { NULL, 4104, "2"}, ++ { "RevokedInfo", 1610612741, NULL }, ++ { "revocationTime", 1073741861, NULL }, ++ { "revocationReason", 537157653, NULL }, ++ { NULL, 1073743880, "0"}, ++ { "unspecified", 1, "0"}, ++ { "UnknownInfo", 1073741844, NULL }, ++ { "NameConstraints", 1610612741, NULL }, ++ { "permittedSubtrees", 1610637314, "GeneralSubtrees"}, ++ { NULL, 4104, "0"}, ++ { "excludedSubtrees", 536895490, "GeneralSubtrees"}, ++ { NULL, 4104, "1"}, ++ { "GeneralSubtrees", 1612709899, NULL }, ++ { "MAX", 1074266122, "1"}, ++ { NULL, 2, "GeneralSubtree"}, ++ { "GeneralSubtree", 1610612741, NULL }, ++ { "base", 1073741826, "GeneralName"}, ++ { "minimum", 1610653699, NULL }, ++ { NULL, 1073741833, "0"}, ++ { NULL, 4104, "0"}, ++ { "maximum", 536895491, NULL }, ++ { NULL, 4104, "1"}, ++ { "TlsFeatures", 536870923, NULL }, ++ { NULL, 3, NULL }, ++ { NULL, 0, NULL } ++}; diff --git a/0018-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch b/0018-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch new file mode 100644 index 0000000..71d8dc8 --- /dev/null +++ b/0018-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch @@ -0,0 +1,1876 @@ +From 7a583c9b29ffa6ac32e93b6b20054c0290521e82 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Thu, 30 Jul 2020 01:33:46 +1000 +Subject: [PATCH 18/23] appended signatures: parse PKCS#7 signedData and X.509 + certificates + +This code allows us to parse: + + - PKCS#7 signedData messages. Only a single signerInfo is supported, + which is all that the Linux sign-file utility supports creating + out-of-the-box. Only RSA, SHA-256 and SHA-512 are supported. + Any certificate embedded in the PKCS#7 message will be ignored. + + - X.509 certificates: at least enough to verify the signatures on the + PKCS#7 messages. We expect that the certificates embedded in grub will + be leaf certificates, not CA certificates. The parser enforces this. + + - X.509 certificates support the Extended Key Usage extension and handle + it by verifying that the certificate has a single purpose, that is code + signing. This is required because Red Hat certificates have both Key + Usage and Extended Key Usage extensions present. + +Signed-off-by: Javier Martinez Canillas # EKU support +Signed-off-by: Daniel Axtens + +--- + +v3 changes: + - fixes for libtasn1-4.18.0 + +v2 changes: + + - Handle the Extended Key Usage extension + - Fix 2 leaks in x509 cert parsing + - Improve x509 parser function name + - Constify the data parameter in parser signatures + - Support multiple signers in a pkcs7 message. Accept any passing sig. + - Allow padding after a pkcs7 message in an appended signature, required + to support my model for signers separated in time. + - Fix a test that used GRUB_ERR_NONE rather than ASN1_SUCCESS. They're + both 0 so no harm was done, but better to be correct. + - Various code and comment cleanups. + +Thanks to Nayna Jain and Stefan Berger for their reviews. +--- + grub-core/commands/appendedsig/appendedsig.h | 118 ++ + grub-core/commands/appendedsig/asn1util.c | 103 ++ + grub-core/commands/appendedsig/pkcs7.c | 509 +++++++++ + grub-core/commands/appendedsig/x509.c | 1079 ++++++++++++++++++ + 4 files changed, 1809 insertions(+) + create mode 100644 grub-core/commands/appendedsig/appendedsig.h + create mode 100644 grub-core/commands/appendedsig/asn1util.c + create mode 100644 grub-core/commands/appendedsig/pkcs7.c + create mode 100644 grub-core/commands/appendedsig/x509.c + +--- /dev/null ++++ b/grub-core/commands/appendedsig/appendedsig.h +@@ -0,0 +1,118 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2020 IBM Corporation. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++ ++extern asn1_node _gnutls_gnutls_asn; ++extern asn1_node _gnutls_pkix_asn; ++ ++#define MAX_OID_LEN 32 ++ ++/* ++ * One or more x509 certificates. ++ * ++ * We do limited parsing: extracting only the serial, CN and RSA public key. ++ */ ++struct x509_certificate ++{ ++ struct x509_certificate *next; ++ ++ grub_uint8_t *serial; ++ grub_size_t serial_len; ++ ++ char *subject; ++ grub_size_t subject_len; ++ ++ /* We only support RSA public keys. This encodes [modulus, publicExponent] */ ++ gcry_mpi_t mpis[2]; ++}; ++ ++/* ++ * A PKCS#7 signedData signerInfo. ++ */ ++struct pkcs7_signerInfo ++{ ++ const gcry_md_spec_t *hash; ++ gcry_mpi_t sig_mpi; ++}; ++ ++/* ++ * A PKCS#7 signedData message. ++ * ++ * We make no attempt to match intelligently, so we don't save any info about ++ * the signer. ++ */ ++struct pkcs7_signedData ++{ ++ int signerInfo_count; ++ struct pkcs7_signerInfo *signerInfos; ++}; ++ ++ ++/* Do libtasn1 init */ ++int asn1_init (void); ++ ++/* ++ * Import a DER-encoded certificate at 'data', of size 'size'. ++ * ++ * Place the results into 'results', which must be already allocated. ++ */ ++grub_err_t ++parse_x509_certificate (const void *data, grub_size_t size, ++ struct x509_certificate *results); ++ ++/* ++ * Release all the storage associated with the x509 certificate. ++ * If the caller dynamically allocated the certificate, it must free it. ++ * The caller is also responsible for maintenance of the linked list. ++ */ ++void certificate_release (struct x509_certificate *cert); ++ ++/* ++ * Parse a PKCS#7 message, which must be a signedData message. ++ * ++ * The message must be in 'sigbuf' and of size 'data_size'. The result is ++ * placed in 'msg', which must already be allocated. ++ */ ++grub_err_t ++parse_pkcs7_signedData (const void *sigbuf, grub_size_t data_size, ++ struct pkcs7_signedData *msg); ++ ++/* ++ * Release all the storage associated with the PKCS#7 message. ++ * If the caller dynamically allocated the message, it must free it. ++ */ ++void pkcs7_signedData_release (struct pkcs7_signedData *msg); ++ ++/* ++ * Read a value from an ASN1 node, allocating memory to store it. ++ * ++ * It will work for anything where the size libtasn1 returns is right: ++ * - Integers ++ * - Octet strings ++ * - DER encoding of other structures ++ * It will _not_ work for things where libtasn1 size requires adjustment: ++ * - Strings that require an extra NULL byte at the end ++ * - Bit strings because libtasn1 returns the length in bits, not bytes. ++ * ++ * If the function returns a non-NULL value, the caller must free it. ++ */ ++void *grub_asn1_allocate_and_read (asn1_node node, const char *name, ++ const char *friendly_name, ++ int *content_size); +--- /dev/null ++++ b/grub-core/commands/appendedsig/asn1util.c +@@ -0,0 +1,103 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2020 IBM Corporation. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "appendedsig.h" ++ ++asn1_node _gnutls_gnutls_asn = NULL; ++asn1_node _gnutls_pkix_asn = NULL; ++ ++extern const asn1_static_node gnutls_asn1_tab[]; ++extern const asn1_static_node pkix_asn1_tab[]; ++ ++/* ++ * Read a value from an ASN1 node, allocating memory to store it. ++ * ++ * It will work for anything where the size libtasn1 returns is right: ++ * - Integers ++ * - Octet strings ++ * - DER encoding of other structures ++ * It will _not_ work for things where libtasn1 size requires adjustment: ++ * - Strings that require an extra NULL byte at the end ++ * - Bit strings because libtasn1 returns the length in bits, not bytes. ++ * ++ * If the function returns a non-NULL value, the caller must free it. ++ */ ++void * ++grub_asn1_allocate_and_read (asn1_node node, const char *name, ++ const char *friendly_name, int *content_size) ++{ ++ int result; ++ grub_uint8_t *tmpstr = NULL; ++ int tmpstr_size = 0; ++ ++ result = asn1_read_value (node, name, NULL, &tmpstr_size); ++ if (result != ASN1_MEM_ERROR) ++ { ++ grub_snprintf (grub_errmsg, sizeof (grub_errmsg), ++ _ ++ ("Reading size of %s did not return expected status: %s"), ++ friendly_name, asn1_strerror (result)); ++ grub_errno = GRUB_ERR_BAD_FILE_TYPE; ++ return NULL; ++ } ++ ++ tmpstr = grub_malloc (tmpstr_size); ++ if (tmpstr == NULL) ++ { ++ grub_snprintf (grub_errmsg, sizeof (grub_errmsg), ++ "Could not allocate memory to store %s", friendly_name); ++ grub_errno = GRUB_ERR_OUT_OF_MEMORY; ++ return NULL; ++ } ++ ++ result = asn1_read_value (node, name, tmpstr, &tmpstr_size); ++ if (result != ASN1_SUCCESS) ++ { ++ grub_free (tmpstr); ++ grub_snprintf (grub_errmsg, sizeof (grub_errmsg), ++ "Error reading %s: %s", ++ friendly_name, asn1_strerror (result)); ++ grub_errno = GRUB_ERR_BAD_FILE_TYPE; ++ return NULL; ++ } ++ ++ *content_size = tmpstr_size; ++ ++ return tmpstr; ++} ++ ++int ++asn1_init (void) ++{ ++ int res; ++ res = asn1_array2tree (gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL); ++ if (res != ASN1_SUCCESS) ++ { ++ return res; ++ } ++ res = asn1_array2tree (pkix_asn1_tab, &_gnutls_pkix_asn, NULL); ++ return res; ++} +--- /dev/null ++++ b/grub-core/commands/appendedsig/pkcs7.c +@@ -0,0 +1,509 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2020 IBM Corporation. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include "appendedsig.h" ++#include ++#include ++#include ++#include ++ ++static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; ++ ++/* ++ * RFC 5652 s 5.1 ++ */ ++const char *signedData_oid = "1.2.840.113549.1.7.2"; ++ ++/* ++ * RFC 4055 s 2.1 ++ */ ++const char *sha256_oid = "2.16.840.1.101.3.4.2.1"; ++const char *sha512_oid = "2.16.840.1.101.3.4.2.3"; ++ ++static grub_err_t ++process_content (grub_uint8_t * content, int size, ++ struct pkcs7_signedData *msg) ++{ ++ int res; ++ asn1_node signed_part; ++ grub_err_t err = GRUB_ERR_NONE; ++ char algo_oid[MAX_OID_LEN]; ++ int algo_oid_size = sizeof (algo_oid); ++ int algo_count; ++ int signer_count; ++ int i; ++ char version; ++ int version_size = sizeof (version); ++ grub_uint8_t *result_buf; ++ int result_size = 0; ++ int crls_size = 0; ++ gcry_error_t gcry_err; ++ bool sha256_in_da, sha256_in_si, sha512_in_da, sha512_in_si; ++ char *da_path; ++ char *si_sig_path; ++ char *si_da_path; ++ ++ res = asn1_create_element (_gnutls_pkix_asn, "PKIX1.pkcs-7-SignedData", ++ &signed_part); ++ if (res != ASN1_SUCCESS) ++ { ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not create ASN.1 structure for PKCS#7 signed part."); ++ } ++ ++ res = asn1_der_decoding2 (&signed_part, content, &size, ++ ASN1_DECODE_FLAG_STRICT_DER, asn1_error); ++ if (res != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Error reading PKCS#7 signed data: %s", asn1_error); ++ goto cleanup_signed_part; ++ } ++ ++ /* SignedData ::= SEQUENCE { ++ * version CMSVersion, ++ * digestAlgorithms DigestAlgorithmIdentifiers, ++ * encapContentInfo EncapsulatedContentInfo, ++ * certificates [0] IMPLICIT CertificateSet OPTIONAL, ++ * crls [1] IMPLICIT RevocationInfoChoices OPTIONAL, ++ * signerInfos SignerInfos } ++ */ ++ ++ /* version per the algo in 5.1, must be 1 */ ++ res = asn1_read_value (signed_part, "version", &version, &version_size); ++ if (res != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Error reading signedData version: %s", ++ asn1_strerror (res)); ++ goto cleanup_signed_part; ++ } ++ ++ if (version != 1) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Unexpected signature version v%d, only v1 supported", ++ version); ++ goto cleanup_signed_part; ++ } ++ ++ /* ++ * digestAlgorithms DigestAlgorithmIdentifiers ++ * ++ * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier ++ * DigestAlgorithmIdentifer is an X.509 AlgorithmIdentifier (10.1.1) ++ * ++ * RFC 4055 s 2.1: ++ * sha256Identifier AlgorithmIdentifier ::= { id-sha256, NULL } ++ * sha512Identifier AlgorithmIdentifier ::= { id-sha512, NULL } ++ * ++ * We only support 1 element in the set, and we do not check parameters atm. ++ */ ++ res = ++ asn1_number_of_elements (signed_part, "digestAlgorithms", &algo_count); ++ if (res != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Error counting number of digest algorithms: %s", ++ asn1_strerror (res)); ++ goto cleanup_signed_part; ++ } ++ ++ if (algo_count <= 0) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "A minimum of 1 digest algorithm is required"); ++ goto cleanup_signed_part; ++ } ++ ++ if (algo_count > 2) ++ { ++ err = ++ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, ++ "A maximum of 2 digest algorithms are supported"); ++ goto cleanup_signed_part; ++ } ++ ++ sha256_in_da = false; ++ sha512_in_da = false; ++ ++ for (i = 0; i < algo_count; i++) ++ { ++ da_path = grub_xasprintf ("digestAlgorithms.?%d.algorithm", i + 1); ++ if (!da_path) ++ { ++ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not allocate path for digest algorithm parsing path"); ++ goto cleanup_signed_part; ++ } ++ ++ algo_oid_size = sizeof (algo_oid); ++ res = asn1_read_value (signed_part, da_path, algo_oid, &algo_oid_size); ++ if (res != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Error reading digest algorithm: %s", ++ asn1_strerror (res)); ++ grub_free (da_path); ++ goto cleanup_signed_part; ++ } ++ ++ if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0) ++ { ++ if (!sha512_in_da) ++ { ++ sha512_in_da = true; ++ } ++ else ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "SHA-512 specified twice in digest algorithm list"); ++ grub_free (da_path); ++ goto cleanup_signed_part; ++ } ++ } ++ else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0) ++ { ++ if (!sha256_in_da) ++ { ++ sha256_in_da = true; ++ } ++ else ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "SHA-256 specified twice in digest algorithm list"); ++ grub_free (da_path); ++ goto cleanup_signed_part; ++ } ++ } ++ else ++ { ++ err = ++ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, ++ "Only SHA-256 and SHA-512 hashes are supported, found OID %s", ++ algo_oid); ++ grub_free (da_path); ++ goto cleanup_signed_part; ++ } ++ ++ grub_free (da_path); ++ } ++ ++ /* at this point, at least one of sha{256,512}_in_da must be true */ ++ ++ /* ++ * We ignore the certificates, but we don't permit CRLs. ++ * A CRL entry might be revoking the certificate we're using, and we have ++ * no way of dealing with that at the moment. ++ */ ++ res = asn1_read_value (signed_part, "crls", NULL, &crls_size); ++ if (res != ASN1_ELEMENT_NOT_FOUND) ++ { ++ err = ++ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, ++ "PKCS#7 messages with embedded CRLs are not supported"); ++ goto cleanup_signed_part; ++ } ++ ++ /* read the signatures */ ++ ++ res = asn1_number_of_elements (signed_part, "signerInfos", &signer_count); ++ if (res != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Error counting number of signers: %s", ++ asn1_strerror (res)); ++ goto cleanup_signed_part; ++ } ++ ++ if (signer_count <= 0) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "A minimum of 1 signer is required"); ++ goto cleanup_signed_part; ++ } ++ ++ msg->signerInfos = grub_calloc (signer_count, ++ sizeof (struct pkcs7_signerInfo)); ++ if (!msg->signerInfos) ++ { ++ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not allocate space for %d signers", ++ signer_count); ++ goto cleanup_signed_part; ++ } ++ ++ msg->signerInfo_count = 0; ++ for (i = 0; i < signer_count; i++) ++ { ++ si_da_path = ++ grub_xasprintf ("signerInfos.?%d.digestAlgorithm.algorithm", i + 1); ++ if (!si_da_path) ++ { ++ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not allocate path for signer %d's digest algorithm parsing path", ++ i); ++ goto cleanup_signerInfos; ++ } ++ ++ algo_oid_size = sizeof (algo_oid); ++ res = ++ asn1_read_value (signed_part, si_da_path, algo_oid, &algo_oid_size); ++ if (res != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Error reading signer %d's digest algorithm: %s", ++ i, asn1_strerror (res)); ++ grub_free (si_da_path); ++ goto cleanup_signerInfos; ++ } ++ ++ grub_free (si_da_path); ++ ++ if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0) ++ { ++ if (!sha512_in_da) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Signer %d claims a SHA-512 signature which was not specified in the outer DigestAlgorithms", ++ i); ++ goto cleanup_signerInfos; ++ } ++ else ++ { ++ sha512_in_si = true; ++ msg->signerInfos[i].hash = ++ grub_crypto_lookup_md_by_name ("sha512"); ++ } ++ } ++ else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0) ++ { ++ if (!sha256_in_da) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Signer %d claims a SHA-256 signature which was not specified in the outer DigestAlgorithms", ++ i); ++ goto cleanup_signerInfos; ++ } ++ else ++ { ++ sha256_in_si = true; ++ msg->signerInfos[i].hash = ++ grub_crypto_lookup_md_by_name ("sha256"); ++ } ++ } ++ else ++ { ++ err = ++ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, ++ "Only SHA-256 and SHA-512 hashes are supported, found OID %s", ++ algo_oid); ++ goto cleanup_signerInfos; ++ } ++ ++ if (!msg->signerInfos[i].hash) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Hash algorithm for signer %d (OID %s) not loaded", i, ++ algo_oid); ++ goto cleanup_signerInfos; ++ } ++ ++ si_sig_path = grub_xasprintf ("signerInfos.?%d.signature", i + 1); ++ if (!si_sig_path) ++ { ++ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not allocate path for signer %d's signature parsing path", ++ i); ++ goto cleanup_signerInfos; ++ } ++ ++ result_buf = ++ grub_asn1_allocate_and_read (signed_part, si_sig_path, ++ "signature data", &result_size); ++ if (!result_buf) ++ { ++ err = grub_errno; ++ grub_free (si_sig_path); ++ goto cleanup_signerInfos; ++ } ++ grub_free (si_sig_path); ++ ++ gcry_err = ++ gcry_mpi_scan (&(msg->signerInfos[i].sig_mpi), GCRYMPI_FMT_USG, ++ result_buf, result_size, NULL); ++ if (gcry_err != GPG_ERR_NO_ERROR) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Error loading signature %d into MPI structure: %d", ++ i, gcry_err); ++ grub_free (result_buf); ++ goto cleanup_signerInfos; ++ } ++ ++ grub_free (result_buf); ++ ++ /* use msg->signerInfo_count to track fully populated signerInfos so we ++ know how many we need to clean up */ ++ msg->signerInfo_count++; ++ } ++ ++ /* Final consistency check of signerInfo.*.digestAlgorithm vs ++ digestAlgorithms.*.algorithm. An algorithm must be present in both ++ digestAlgorithms and signerInfo or in neither. We have already checked ++ for an algorithm in signerInfo that is not in digestAlgorithms, here we ++ check for algorithms in digestAlgorithms but not in signerInfos. */ ++ if (sha512_in_da && !sha512_in_si) ++ { ++ err = grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "SHA-512 specified in DigestAlgorithms but did not appear in SignerInfos"); ++ goto cleanup_signerInfos; ++ } ++ ++ if (sha256_in_da && !sha256_in_si) ++ { ++ err = grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "SHA-256 specified in DigestAlgorithms but did not appear in SignerInfos"); ++ goto cleanup_signerInfos; ++ } ++ ++ asn1_delete_structure (&signed_part); ++ return GRUB_ERR_NONE; ++ ++cleanup_signerInfos: ++ for (i = 0; i < msg->signerInfo_count; i++) ++ gcry_mpi_release (msg->signerInfos[i].sig_mpi); ++ grub_free (msg->signerInfos); ++cleanup_signed_part: ++ asn1_delete_structure (&signed_part); ++ return err; ++} ++ ++grub_err_t ++parse_pkcs7_signedData (const void *sigbuf, grub_size_t data_size, ++ struct pkcs7_signedData *msg) ++{ ++ int res; ++ asn1_node content_info; ++ grub_err_t err = GRUB_ERR_NONE; ++ char content_oid[MAX_OID_LEN]; ++ grub_uint8_t *content; ++ int content_size; ++ int content_oid_size = sizeof (content_oid); ++ int size; ++ ++ if (data_size > GRUB_INT_MAX) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, ++ "Cannot parse a PKCS#7 message where data size > INT_MAX"); ++ size = (int) data_size; ++ ++ res = asn1_create_element (_gnutls_pkix_asn, ++ "PKIX1.pkcs-7-ContentInfo", &content_info); ++ if (res != ASN1_SUCCESS) ++ { ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not create ASN.1 structure for PKCS#7 data: %s", ++ asn1_strerror (res)); ++ } ++ ++ res = asn1_der_decoding2 (&content_info, sigbuf, &size, ++ ASN1_DECODE_FLAG_STRICT_DER | ++ ASN1_DECODE_FLAG_ALLOW_PADDING, asn1_error); ++ if (res != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Error decoding PKCS#7 message DER: %s", asn1_error); ++ goto cleanup; ++ } ++ ++ /* ++ * ContentInfo ::= SEQUENCE { ++ * contentType ContentType, ++ * content [0] EXPLICIT ANY DEFINED BY contentType } ++ * ++ * ContentType ::= OBJECT IDENTIFIER ++ */ ++ res = ++ asn1_read_value (content_info, "contentType", content_oid, ++ &content_oid_size); ++ if (res != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Error reading PKCS#7 content type: %s", ++ asn1_strerror (res)); ++ goto cleanup; ++ } ++ ++ /* OID for SignedData defined in 5.1 */ ++ if (grub_strncmp (signedData_oid, content_oid, content_oid_size) != 0) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_SIGNATURE, ++ "Unexpected content type in PKCS#7 message: OID %s", ++ content_oid); ++ goto cleanup; ++ } ++ ++ content = ++ grub_asn1_allocate_and_read (content_info, "content", ++ "PKCS#7 message content", &content_size); ++ if (!content) ++ { ++ err = grub_errno; ++ goto cleanup; ++ } ++ ++ err = process_content (content, content_size, msg); ++ grub_free (content); ++ ++cleanup: ++ asn1_delete_structure (&content_info); ++ return err; ++} ++ ++/* ++ * Release all the storage associated with the PKCS#7 message. ++ * If the caller dynamically allocated the message, it must free it. ++ */ ++void ++pkcs7_signedData_release (struct pkcs7_signedData *msg) ++{ ++ grub_ssize_t i; ++ for (i = 0; i < msg->signerInfo_count; i++) ++ { ++ gcry_mpi_release (msg->signerInfos[i].sig_mpi); ++ } ++ grub_free (msg->signerInfos); ++} +--- /dev/null ++++ b/grub-core/commands/appendedsig/x509.c +@@ -0,0 +1,1079 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2020 IBM Corporation. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "appendedsig.h" ++ ++static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; ++ ++/* ++ * RFC 3279 2.3.1 RSA Keys ++ */ ++const char *rsaEncryption_oid = "1.2.840.113549.1.1.1"; ++ ++/* ++ * RFC 5280 Appendix A ++ */ ++const char *commonName_oid = "2.5.4.3"; ++ ++/* ++ * RFC 5280 4.2.1.3 Key Usage ++ */ ++const char *keyUsage_oid = "2.5.29.15"; ++ ++const grub_uint8_t digitalSignatureUsage = 0x80; ++ ++/* ++ * RFC 5280 4.2.1.9 Basic Constraints ++ */ ++const char *basicConstraints_oid = "2.5.29.19"; ++ ++/* ++ * RFC 5280 4.2.1.12 Extended Key Usage ++ */ ++const char *extendedKeyUsage_oid = "2.5.29.37"; ++const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3"; ++ ++/* ++ * RFC 3279 2.3.1 ++ * ++ * The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey: ++ * ++ * RSAPublicKey ::= SEQUENCE { ++ * modulus INTEGER, -- n ++ * publicExponent INTEGER } -- e ++ * ++ * where modulus is the modulus n, and publicExponent is the public ++ * exponent e. ++ */ ++static grub_err_t ++grub_parse_rsa_pubkey (grub_uint8_t *der, int dersize, ++ struct x509_certificate *certificate) ++{ ++ int result; ++ asn1_node spk = NULL; ++ grub_uint8_t *m_data, *e_data; ++ int m_size, e_size; ++ grub_err_t err = GRUB_ERR_NONE; ++ gcry_error_t gcry_err; ++ ++ result = ++ asn1_create_element (_gnutls_gnutls_asn, "GNUTLS.RSAPublicKey", &spk); ++ if (result != ASN1_SUCCESS) ++ { ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Cannot create storage for public key ASN.1 data"); ++ } ++ ++ result = asn1_der_decoding2 (&spk, der, &dersize, ++ ASN1_DECODE_FLAG_STRICT_DER, asn1_error); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Cannot decode certificate public key DER: %s", ++ asn1_error); ++ goto cleanup; ++ } ++ ++ m_data = ++ grub_asn1_allocate_and_read (spk, "modulus", "RSA modulus", &m_size); ++ if (!m_data) ++ { ++ err = grub_errno; ++ goto cleanup; ++ } ++ ++ e_data = ++ grub_asn1_allocate_and_read (spk, "publicExponent", "RSA public exponent", ++ &e_size); ++ if (!e_data) ++ { ++ err = grub_errno; ++ goto cleanup_m_data; ++ } ++ ++ /* ++ * convert m, e to mpi ++ * ++ * nscanned is not set for FMT_USG, it's only set for FMT_PGP, ++ * so we can't verify it ++ */ ++ gcry_err = ++ gcry_mpi_scan (&certificate->mpis[0], GCRYMPI_FMT_USG, m_data, m_size, ++ NULL); ++ if (gcry_err != GPG_ERR_NO_ERROR) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error loading RSA modulus into MPI structure: %d", ++ gcry_err); ++ goto cleanup_e_data; ++ } ++ ++ gcry_err = ++ gcry_mpi_scan (&certificate->mpis[1], GCRYMPI_FMT_USG, e_data, e_size, ++ NULL); ++ if (gcry_err != GPG_ERR_NO_ERROR) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error loading RSA exponent into MPI structure: %d", ++ gcry_err); ++ goto cleanup_m_mpi; ++ } ++ ++ grub_free (e_data); ++ grub_free (m_data); ++ asn1_delete_structure (&spk); ++ return GRUB_ERR_NONE; ++ ++cleanup_m_mpi: ++ gcry_mpi_release (certificate->mpis[0]); ++cleanup_e_data: ++ grub_free (e_data); ++cleanup_m_data: ++ grub_free (m_data); ++cleanup: ++ asn1_delete_structure (&spk); ++ return err; ++} ++ ++ ++/* ++ * RFC 5280: ++ * SubjectPublicKeyInfo ::= SEQUENCE { ++ * algorithm AlgorithmIdentifier, ++ * subjectPublicKey BIT STRING } ++ * ++ * AlgorithmIdentifiers come from RFC 3279, we are not strictly compilant as we ++ * only support RSA Encryption. ++ */ ++ ++static grub_err_t ++grub_x509_read_subject_public_key (asn1_node asn, ++ struct x509_certificate *results) ++{ ++ int result; ++ grub_err_t err; ++ const char *algo_name = ++ "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm"; ++ const char *params_name = ++ "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters"; ++ const char *pk_name = ++ "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey"; ++ char algo_oid[MAX_OID_LEN]; ++ int algo_size = sizeof (algo_oid); ++ char params_value[2]; ++ int params_size = sizeof (params_value); ++ grub_uint8_t *key_data = NULL; ++ int key_size = 0; ++ unsigned int key_type; ++ ++ /* algorithm: see notes for rsaEncryption_oid */ ++ result = asn1_read_value (asn, algo_name, algo_oid, &algo_size); ++ if (result != ASN1_SUCCESS) ++ { ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading x509 public key algorithm: %s", ++ asn1_strerror (result)); ++ } ++ ++ if (grub_strncmp (algo_oid, rsaEncryption_oid, sizeof (rsaEncryption_oid)) ++ != 0) ++ { ++ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, ++ "Unsupported x509 public key algorithm: %s", ++ algo_oid); ++ } ++ ++ /* ++ * RFC 3279 2.3.1 ++ * The rsaEncryption OID is intended to be used in the algorithm field ++ * of a value of type AlgorithmIdentifier. The parameters field MUST ++ * have ASN.1 type NULL for this algorithm identifier. ++ */ ++ result = asn1_read_value (asn, params_name, params_value, ¶ms_size); ++ if (result != ASN1_SUCCESS) ++ { ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading x509 public key parameters: %s", ++ asn1_strerror (result)); ++ } ++ ++ if (params_value[0] != ASN1_TAG_NULL) ++ { ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Invalid x509 public key parameters: expected NULL"); ++ } ++ ++ /* ++ * RFC 3279 2.3.1: The DER encoded RSAPublicKey is the value of the BIT ++ * STRING subjectPublicKey. ++ */ ++ result = asn1_read_value_type (asn, pk_name, NULL, &key_size, &key_type); ++ if (result != ASN1_MEM_ERROR) ++ { ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading size of x509 public key: %s", ++ asn1_strerror (result)); ++ } ++ if (key_type != ASN1_ETYPE_BIT_STRING) ++ { ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Unexpected ASN.1 type when reading x509 public key: %x", ++ key_type); ++ } ++ ++ /* length is in bits */ ++ key_size = (key_size + 7) / 8; ++ ++ key_data = grub_malloc (key_size); ++ if (!key_data) ++ { ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Out of memory for x509 public key"); ++ } ++ ++ result = asn1_read_value (asn, pk_name, key_data, &key_size); ++ if (result != ASN1_SUCCESS) ++ { ++ grub_free (key_data); ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading public key data"); ++ } ++ key_size = (key_size + 7) / 8; ++ ++ err = grub_parse_rsa_pubkey (key_data, key_size, results); ++ grub_free (key_data); ++ ++ return err; ++} ++ ++/* Decode a string as defined in Appendix A */ ++static grub_err_t ++decode_string (char *der, int der_size, char **string, ++ grub_size_t *string_size) ++{ ++ asn1_node strasn; ++ int result; ++ char *choice; ++ int choice_size = 0; ++ int tmp_size = 0; ++ grub_err_t err = GRUB_ERR_NONE; ++ ++ result = ++ asn1_create_element (_gnutls_pkix_asn, "PKIX1.DirectoryString", &strasn); ++ if (result != ASN1_SUCCESS) ++ { ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not create ASN.1 structure for certificate: %s", ++ asn1_strerror (result)); ++ } ++ ++ result = asn1_der_decoding2 (&strasn, der, &der_size, ++ ASN1_DECODE_FLAG_STRICT_DER, asn1_error); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Could not parse DER for DirectoryString: %s", ++ asn1_error); ++ goto cleanup; ++ } ++ ++ choice = ++ grub_asn1_allocate_and_read (strasn, "", "DirectoryString choice", ++ &choice_size); ++ if (!choice) ++ { ++ err = grub_errno; ++ goto cleanup; ++ } ++ ++ if (grub_strncmp ("utf8String", choice, choice_size) == 0) ++ { ++ result = asn1_read_value (strasn, "utf8String", NULL, &tmp_size); ++ if (result != ASN1_MEM_ERROR) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading size of UTF-8 string: %s", ++ asn1_strerror (result)); ++ goto cleanup_choice; ++ } ++ } ++ else if (grub_strncmp ("printableString", choice, choice_size) == 0) ++ { ++ result = asn1_read_value (strasn, "printableString", NULL, &tmp_size); ++ if (result != ASN1_MEM_ERROR) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading size of UTF-8 string: %s", ++ asn1_strerror (result)); ++ goto cleanup_choice; ++ } ++ } ++ else ++ { ++ err = ++ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, ++ "Only UTF-8 and printable DirectoryStrings are supported, got %s", ++ choice); ++ goto cleanup_choice; ++ } ++ ++ /* read size does not include trailing null */ ++ tmp_size++; ++ ++ *string = grub_malloc (tmp_size); ++ if (!*string) ++ { ++ err = ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Cannot allocate memory for DirectoryString contents"); ++ goto cleanup_choice; ++ } ++ ++ result = asn1_read_value (strasn, choice, *string, &tmp_size); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading out %s in DirectoryString: %s", ++ choice, asn1_strerror (result)); ++ grub_free (*string); ++ goto cleanup_choice; ++ } ++ *string_size = tmp_size + 1; ++ (*string)[tmp_size] = '\0'; ++ ++cleanup_choice: ++ grub_free (choice); ++cleanup: ++ asn1_delete_structure (&strasn); ++ return err; ++} ++ ++/* ++ * TBSCertificate ::= SEQUENCE { ++ * version [0] EXPLICIT Version DEFAULT v1, ++ * ... ++ * ++ * Version ::= INTEGER { v1(0), v2(1), v3(2) } ++ */ ++static grub_err_t ++check_version (asn1_node certificate) ++{ ++ int rc; ++ const char *name = "tbsCertificate.version"; ++ grub_uint8_t version; ++ int len = sizeof (version); ++ ++ rc = asn1_read_value (certificate, name, &version, &len); ++ ++ /* require version 3 */ ++ if (rc != ASN1_SUCCESS || len != 1) ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading certificate version"); ++ ++ if (version != 0x02) ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Invalid x509 certificate version, expected v3 (0x02), got 0x%02x", ++ version); ++ ++ return GRUB_ERR_NONE; ++} ++ ++/* ++ * This is an X.501 Name, which is complex. ++ * ++ * For simplicity, we extract only the CN. ++ */ ++static grub_err_t ++read_name (asn1_node asn, const char *name_path, char **name, ++ grub_size_t *name_size) ++{ ++ int seq_components, set_components; ++ int result; ++ int i, j; ++ char *top_path, *set_path, *type_path, *val_path; ++ char type[MAX_OID_LEN]; ++ int type_len = sizeof (type); ++ int string_size = 0; ++ char *string_der; ++ grub_err_t err; ++ ++ *name = NULL; ++ ++ top_path = grub_xasprintf ("%s.rdnSequence", name_path); ++ if (!top_path) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not allocate memory for %s name parsing path", ++ name_path); ++ ++ result = asn1_number_of_elements (asn, top_path, &seq_components); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error counting name components: %s", ++ asn1_strerror (result)); ++ goto cleanup; ++ } ++ ++ for (i = 1; i <= seq_components; i++) ++ { ++ set_path = grub_xasprintf ("%s.?%d", top_path, i); ++ if (!set_path) ++ { ++ err = ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not allocate memory for %s name set parsing path", ++ name_path); ++ goto cleanup_set; ++ } ++ /* this brings us, hopefully, to a set */ ++ result = asn1_number_of_elements (asn, set_path, &set_components); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error counting name sub-components components (element %d): %s", ++ i, asn1_strerror (result)); ++ goto cleanup_set; ++ } ++ for (j = 1; j <= set_components; j++) ++ { ++ type_path = grub_xasprintf ("%s.?%d.?%d.type", top_path, i, j); ++ if (!type_path) ++ { ++ err = ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not allocate memory for %s name component type path", ++ name_path); ++ goto cleanup_set; ++ } ++ type_len = sizeof (type); ++ result = asn1_read_value (asn, type_path, type, &type_len); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading %s name component type: %s", ++ name_path, asn1_strerror (result)); ++ goto cleanup_type; ++ } ++ ++ if (grub_strncmp (type, commonName_oid, type_len) != 0) ++ { ++ grub_free (type_path); ++ continue; ++ } ++ ++ val_path = grub_xasprintf ("%s.?%d.?%d.value", top_path, i, j); ++ if (!val_path) ++ { ++ err = ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not allocate memory for %s name component value path", ++ name_path); ++ goto cleanup_set; ++ } ++ ++ string_der = ++ grub_asn1_allocate_and_read (asn, val_path, name_path, ++ &string_size); ++ if (!string_der) ++ { ++ err = grub_errno; ++ goto cleanup_val_path; ++ } ++ ++ err = decode_string (string_der, string_size, name, name_size); ++ if (err) ++ goto cleanup_string; ++ ++ grub_free (string_der); ++ grub_free (type_path); ++ grub_free (val_path); ++ break; ++ } ++ grub_free (set_path); ++ ++ if (*name) ++ break; ++ } ++ ++ grub_free (top_path); ++ ++ return GRUB_ERR_NONE; ++ ++cleanup_string: ++ grub_free (string_der); ++cleanup_val_path: ++ grub_free (val_path); ++cleanup_type: ++ grub_free (type_path); ++cleanup_set: ++ grub_free (set_path); ++cleanup: ++ grub_free (top_path); ++ return err; ++} ++ ++/* ++ * Verify the Key Usage extension. ++ * We only permit the Digital signature usage. ++ */ ++static grub_err_t ++verify_key_usage (grub_uint8_t *value, int value_size) ++{ ++ asn1_node usageasn; ++ int result; ++ grub_err_t err = GRUB_ERR_NONE; ++ grub_uint8_t usage = 0xff; ++ int usage_size = sizeof (usage_size); ++ ++ result = ++ asn1_create_element (_gnutls_pkix_asn, "PKIX1.KeyUsage", &usageasn); ++ if (result != ASN1_SUCCESS) ++ { ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not create ASN.1 structure for key usage"); ++ } ++ ++ result = asn1_der_decoding2 (&usageasn, value, &value_size, ++ ASN1_DECODE_FLAG_STRICT_DER, asn1_error); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error parsing DER for Key Usage: %s", asn1_error); ++ goto cleanup; ++ } ++ ++ result = asn1_read_value (usageasn, "", &usage, &usage_size); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading Key Usage value: %s", ++ asn1_strerror (result)); ++ goto cleanup; ++ } ++ ++ if (usage != digitalSignatureUsage) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected Key Usage value: %x", ++ usage); ++ goto cleanup; ++ } ++ ++cleanup: ++ asn1_delete_structure (&usageasn); ++ return err; ++} ++ ++/* ++ * BasicConstraints ::= SEQUENCE { ++ * cA BOOLEAN DEFAULT FALSE, ++ * pathLenConstraint INTEGER (0..MAX) OPTIONAL } ++ */ ++static grub_err_t ++verify_basic_constraints (grub_uint8_t *value, int value_size) ++{ ++ asn1_node basicasn; ++ int result; ++ grub_err_t err = GRUB_ERR_NONE; ++ char cA[6]; /* FALSE or TRUE */ ++ int cA_size = sizeof (cA); ++ ++ result = ++ asn1_create_element (_gnutls_pkix_asn, "PKIX1.BasicConstraints", ++ &basicasn); ++ if (result != ASN1_SUCCESS) ++ { ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not create ASN.1 structure for Basic Constraints"); ++ } ++ ++ result = asn1_der_decoding2 (&basicasn, value, &value_size, ++ ASN1_DECODE_FLAG_STRICT_DER, asn1_error); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error parsing DER for Basic Constraints: %s", ++ asn1_error); ++ goto cleanup; ++ } ++ ++ result = asn1_read_value (basicasn, "cA", cA, &cA_size); ++ if (result == ASN1_ELEMENT_NOT_FOUND) ++ { ++ /* Not present, default is False, so this is OK */ ++ err = GRUB_ERR_NONE; ++ goto cleanup; ++ } ++ else if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading Basic Constraints cA value: %s", ++ asn1_strerror (result)); ++ goto cleanup; ++ } ++ ++ /* The certificate must not be a CA certificate */ ++ if (grub_strncmp ("FALSE", cA, cA_size) != 0) ++ { ++ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected CA value: %s", ++ cA); ++ goto cleanup; ++ } ++ ++cleanup: ++ asn1_delete_structure (&basicasn); ++ return err; ++} ++ ++/* ++ * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId ++ * ++ * KeyPurposeId ::= OBJECT IDENTIFIER ++ */ ++static grub_err_t ++verify_extended_key_usage (grub_uint8_t *value, int value_size) ++{ ++ asn1_node extendedasn; ++ int result, count; ++ grub_err_t err = GRUB_ERR_NONE; ++ char usage[MAX_OID_LEN]; ++ int usage_size = sizeof (usage); ++ ++ result = ++ asn1_create_element (_gnutls_pkix_asn, "PKIX1.ExtKeyUsageSyntax", ++ &extendedasn); ++ if (result != ASN1_SUCCESS) ++ { ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not create ASN.1 structure for Extended Key Usage"); ++ } ++ ++ result = asn1_der_decoding2 (&extendedasn, value, &value_size, ++ ASN1_DECODE_FLAG_STRICT_DER, asn1_error); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error parsing DER for Extended Key Usage: %s", ++ asn1_error); ++ goto cleanup; ++ } ++ ++ /* ++ * If EKUs are present, there must be exactly 1 and it must be a ++ * codeSigning usage. ++ */ ++ result = asn1_number_of_elements (extendedasn, "", &count); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error counting number of Extended Key Usages: %s", ++ asn1_strerror (result)); ++ goto cleanup; ++ } ++ ++ if (count != 1) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Unexpected number of Extended Key Usages: %d, 1 expected", ++ count); ++ goto cleanup; ++ } ++ ++ result = asn1_read_value (extendedasn, "?1", usage, &usage_size); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading Extended Key Usage: %s", ++ asn1_strerror (result)); ++ goto cleanup; ++ } ++ ++ if (grub_strncmp (codeSigningUsage_oid, usage, usage_size) != 0) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Unexpected Extended Key Usage OID, got: %s", usage); ++ goto cleanup; ++ } ++ ++cleanup: ++ asn1_delete_structure (&extendedasn); ++ return err; ++} ++ ++/* ++ * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension ++ * ++ * Extension ::= SEQUENCE { ++ * extnID OBJECT IDENTIFIER, ++ * critical BOOLEAN DEFAULT FALSE, ++ * extnValue OCTET STRING ++ * -- contains the DER encoding of an ASN.1 value ++ * -- corresponding to the extension type identified ++ * -- by extnID ++ * } ++ * ++ * A certificate must: ++ * - contain the Digital Signature usage only ++ * - not be a CA ++ * - contain no extended usages, or only a code signing extended usage ++ * - not contain any other critical extensions (RFC 5280 s 4.2) ++ */ ++static grub_err_t ++verify_extensions (asn1_node cert) ++{ ++ int result; ++ int ext, num_extensions = 0; ++ int usage_present = 0, constraints_present = 0, extended_usage_present = 0; ++ char *oid_path, *critical_path, *value_path; ++ char extnID[MAX_OID_LEN]; ++ int extnID_size; ++ grub_err_t err; ++ char critical[6]; /* we get either "TRUE" or "FALSE" */ ++ int critical_size; ++ grub_uint8_t *value; ++ int value_size; ++ ++ result = ++ asn1_number_of_elements (cert, "tbsCertificate.extensions", ++ &num_extensions); ++ if (result != ASN1_SUCCESS) ++ { ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error counting number of extensions: %s", ++ asn1_strerror (result)); ++ } ++ ++ if (num_extensions < 2) ++ { ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Insufficient number of extensions for certificate, need at least 2, got %d", ++ num_extensions); ++ } ++ ++ for (ext = 1; ext <= num_extensions; ext++) ++ { ++ oid_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnID", ext); ++ ++ extnID_size = sizeof (extnID); ++ result = asn1_read_value (cert, oid_path, extnID, &extnID_size); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading extension OID: %s", ++ asn1_strerror (result)); ++ goto cleanup_oid_path; ++ } ++ ++ critical_path = ++ grub_xasprintf ("tbsCertificate.extensions.?%d.critical", ext); ++ critical_size = sizeof (critical); ++ result = ++ asn1_read_value (cert, critical_path, critical, &critical_size); ++ if (result == ASN1_ELEMENT_NOT_FOUND) ++ { ++ critical[0] = '\0'; ++ } ++ else if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Error reading extension criticality: %s", ++ asn1_strerror (result)); ++ goto cleanup_critical_path; ++ } ++ ++ value_path = ++ grub_xasprintf ("tbsCertificate.extensions.?%d.extnValue", ext); ++ value = ++ grub_asn1_allocate_and_read (cert, value_path, ++ "certificate extension value", ++ &value_size); ++ if (!value) ++ { ++ err = grub_errno; ++ goto cleanup_value_path; ++ } ++ ++ /* ++ * Now we must see if we recognise the OID. ++ * If we have an unrecognised critical extension we MUST bail. ++ */ ++ if (grub_strncmp (keyUsage_oid, extnID, extnID_size) == 0) ++ { ++ err = verify_key_usage (value, value_size); ++ if (err != GRUB_ERR_NONE) ++ { ++ goto cleanup_value; ++ } ++ usage_present++; ++ } ++ else if (grub_strncmp (basicConstraints_oid, extnID, extnID_size) == 0) ++ { ++ err = verify_basic_constraints (value, value_size); ++ if (err != GRUB_ERR_NONE) ++ { ++ goto cleanup_value; ++ } ++ constraints_present++; ++ } ++ else if (grub_strncmp (extendedKeyUsage_oid, extnID, extnID_size) == 0) ++ { ++ err = verify_extended_key_usage (value, value_size); ++ if (err != GRUB_ERR_NONE) ++ { ++ goto cleanup_value; ++ } ++ extended_usage_present++; ++ } ++ else if (grub_strncmp ("TRUE", critical, critical_size) == 0) ++ { ++ /* ++ * per the RFC, we must not process a certificate with ++ * a critical extension we do not understand. ++ */ ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Unhandled critical x509 extension with OID %s", ++ extnID); ++ goto cleanup_value; ++ } ++ ++ grub_free (value); ++ grub_free (value_path); ++ grub_free (critical_path); ++ grub_free (oid_path); ++ } ++ ++ if (usage_present != 1) ++ { ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Unexpected number of Key Usage extensions - expected 1, got %d", ++ usage_present); ++ } ++ if (constraints_present != 1) ++ { ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Unexpected number of basic constraints extensions - expected 1, got %d", ++ constraints_present); ++ } ++ if (extended_usage_present > 1) ++ { ++ return grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Unexpected number of Extended Key Usage extensions - expected 0 or 1, got %d", ++ extended_usage_present); ++ } ++ return GRUB_ERR_NONE; ++ ++cleanup_value: ++ grub_free (value); ++cleanup_value_path: ++ grub_free (value_path); ++cleanup_critical_path: ++ grub_free (critical_path); ++cleanup_oid_path: ++ grub_free (oid_path); ++ return err; ++} ++ ++/* ++ * Parse a certificate whose DER-encoded form is in @data, of size @data_size. ++ * Return the results in @results, which must point to an allocated x509 certificate. ++ */ ++grub_err_t ++parse_x509_certificate (const void *data, grub_size_t data_size, ++ struct x509_certificate *results) ++{ ++ int result = 0; ++ asn1_node cert; ++ grub_err_t err; ++ int size; ++ int tmp_size; ++ ++ if (data_size > GRUB_INT_MAX) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, ++ "Cannot parse a certificate where data size > INT_MAX"); ++ size = (int) data_size; ++ ++ result = asn1_create_element (_gnutls_pkix_asn, "PKIX1.Certificate", &cert); ++ if (result != ASN1_SUCCESS) ++ { ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ "Could not create ASN.1 structure for certificate: %s", ++ asn1_strerror (result)); ++ } ++ ++ result = asn1_der_decoding2 (&cert, data, &size, ++ ASN1_DECODE_FLAG_STRICT_DER, asn1_error); ++ if (result != ASN1_SUCCESS) ++ { ++ err = ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, ++ "Could not parse DER for certificate: %s", asn1_error); ++ goto cleanup; ++ } ++ ++ /* ++ * TBSCertificate ::= SEQUENCE { ++ * version [0] EXPLICIT Version DEFAULT v1 ++ */ ++ err = check_version (cert); ++ if (err != GRUB_ERR_NONE) ++ { ++ goto cleanup; ++ } ++ ++ /* ++ * serialNumber CertificateSerialNumber, ++ * ++ * CertificateSerialNumber ::= INTEGER ++ */ ++ results->serial = ++ grub_asn1_allocate_and_read (cert, "tbsCertificate.serialNumber", ++ "certificate serial number", &tmp_size); ++ if (!results->serial) ++ { ++ err = grub_errno; ++ goto cleanup; ++ } ++ /* ++ * It's safe to cast the signed int to an unsigned here, we know ++ * length is non-negative ++ */ ++ results->serial_len = tmp_size; ++ ++ /* ++ * signature AlgorithmIdentifier, ++ * ++ * We don't load the signature or issuer at the moment, ++ * as we don't attempt x509 verification. ++ */ ++ ++ /* ++ * issuer Name, ++ * ++ * The RFC only requires the serial number to be unique within ++ * issuers, so to avoid ambiguity we _technically_ ought to make ++ * this available. ++ */ ++ ++ /* ++ * validity Validity, ++ * ++ * Validity ::= SEQUENCE { ++ * notBefore Time, ++ * notAfter Time } ++ * ++ * We can't validate this reasonably, we have no true time source on several ++ * platforms. For now we do not parse them. ++ */ ++ ++ /* ++ * subject Name, ++ * ++ * This is an X501 name, we parse out just the CN. ++ */ ++ err = ++ read_name (cert, "tbsCertificate.subject", &results->subject, ++ &results->subject_len); ++ if (err != GRUB_ERR_NONE) ++ goto cleanup_serial; ++ ++ /* ++ * TBSCertificate ::= SEQUENCE { ++ * ... ++ * subjectPublicKeyInfo SubjectPublicKeyInfo, ++ * ... ++ */ ++ err = grub_x509_read_subject_public_key (cert, results); ++ if (err != GRUB_ERR_NONE) ++ goto cleanup_name; ++ ++ /* ++ * TBSCertificate ::= SEQUENCE { ++ * ... ++ * extensions [3] EXPLICIT Extensions OPTIONAL ++ * -- If present, version MUST be v3 ++ * } ++ */ ++ ++ err = verify_extensions (cert); ++ if (err != GRUB_ERR_NONE) ++ goto cleanup_mpis; ++ ++ /* ++ * We do not read or check the signature on the certificate: ++ * as discussed we do not try to validate the certificate but trust ++ * it implictly. ++ */ ++ ++ asn1_delete_structure (&cert); ++ return GRUB_ERR_NONE; ++ ++cleanup_mpis: ++ gcry_mpi_release (results->mpis[0]); ++ gcry_mpi_release (results->mpis[1]); ++cleanup_name: ++ grub_free (results->subject); ++cleanup_serial: ++ grub_free (results->serial); ++cleanup: ++ asn1_delete_structure (&cert); ++ return err; ++} ++ ++/* ++ * Release all the storage associated with the x509 certificate. ++ * If the caller dynamically allocated the certificate, it must free it. ++ * The caller is also responsible for maintenance of the linked list. ++ */ ++void ++certificate_release (struct x509_certificate *cert) ++{ ++ grub_free (cert->subject); ++ grub_free (cert->serial); ++ gcry_mpi_release (cert->mpis[0]); ++ gcry_mpi_release (cert->mpis[1]); ++} diff --git a/0019-appended-signatures-support-verifying-appended-signa.patch b/0019-appended-signatures-support-verifying-appended-signa.patch new file mode 100644 index 0000000..c793053 --- /dev/null +++ b/0019-appended-signatures-support-verifying-appended-signa.patch @@ -0,0 +1,755 @@ +From 97104dbd207a07fa3759b23766fa60f7bb8d16b4 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Thu, 30 Jul 2020 01:35:43 +1000 +Subject: [PATCH 19/23] appended signatures: support verifying appended + signatures + +Building on the parsers and the ability to embed x509 certificates, as +well as the existing gcrypt functionality, add a module for verifying +appended signatures. + +This includes a verifier that requires that Linux kernels and grub modules +have appended signatures, and commands to manage the list of trusted +certificates for verification. + +Verification must be enabled by setting check_appended_signatures. If +GRUB is locked down when the module is loaded, verification will be +enabled and locked automatically. + +As with the PGP verifier, it is not a complete secure-boot solution: +other mechanisms, such as a password or lockdown, must be used to ensure +that a user cannot drop to the grub shell and disable verification. + +Signed-off-by: Daniel Axtens + +--- + +v2 changes: + + - Improve x509 parser function name + - Constify data parameters in function signatures + - Support multiple signers + - Use an enum rather than 0, 1 and 2 for various signature + enforcement states. + - Spin out a file reading function that was duplicated. + - Fix some code style and clarity issues. + +Thanks to Nayna Jain and Stefan Berger for their reviews. + +Revert "fixups so that you can build pkcs7 without posixly" + +This reverts commit 676a19fa8a7f9cca7a58ce2180110f609185b2bd. +--- + grub-core/Makefile.core.def | 14 + + grub-core/commands/appendedsig/appendedsig.c | 669 +++++++++++++++++++ + include/grub/file.h | 2 + + 3 files changed, 685 insertions(+) + create mode 100644 grub-core/commands/appendedsig/appendedsig.c + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -979,6 +979,21 @@ + }; + + module = { ++ name = appendedsig; ++ common = commands/appendedsig/appendedsig.c; ++ common = commands/appendedsig/x509.c; ++ common = commands/appendedsig/pkcs7.c; ++ common = commands/appendedsig/asn1util.c; ++ common = commands/appendedsig/gnutls_asn1_tab.c; ++ common = commands/appendedsig/pkix_asn1_tab.c; ++ ++ extra_dist = commands/appendedsig/appendedsig.h; ++ // posix wrapper required for gcry to get sys/types.h ++ cflags = '$(CFLAGS_POSIX)'; ++ cppflags = '-I$(srcdir)/lib/posix_wrap'; ++}; ++ ++module = { + name = hdparm; + common = commands/hdparm.c; + enable = pci; +--- /dev/null ++++ b/grub-core/commands/appendedsig/appendedsig.c +@@ -0,0 +1,669 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2020-2021 IBM Corporation. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "appendedsig.h" ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++const char magic[] = "~Module signature appended~\n"; ++ ++/* ++ * This structure is extracted from scripts/sign-file.c in the linux kernel ++ * source. It was licensed as LGPLv2.1+, which is GPLv3+ compatible. ++ */ ++struct module_signature ++{ ++ grub_uint8_t algo; /* Public-key crypto algorithm [0] */ ++ grub_uint8_t hash; /* Digest algorithm [0] */ ++ grub_uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */ ++ grub_uint8_t signer_len; /* Length of signer's name [0] */ ++ grub_uint8_t key_id_len; /* Length of key identifier [0] */ ++ grub_uint8_t __pad[3]; ++ grub_uint32_t sig_len; /* Length of signature data */ ++} GRUB_PACKED; ++ ++ ++/* This represents an entire, parsed, appended signature */ ++struct grub_appended_signature ++{ ++ grub_size_t signature_len; /* Length of PKCS#7 data + ++ * metadata + magic */ ++ ++ struct module_signature sig_metadata; /* Module signature metadata */ ++ struct pkcs7_signedData pkcs7; /* Parsed PKCS#7 data */ ++}; ++ ++/* Trusted certificates for verifying appended signatures */ ++struct x509_certificate *grub_trusted_key; ++ ++/* ++ * Force gcry_rsa to be a module dependency. ++ * ++ * If we use grub_crypto_pk_rsa, then then the gcry_rsa module won't be built ++ * in if you add 'appendedsig' to grub-install --modules. You would need to ++ * add 'gcry_rsa' too. That's confusing and seems suboptimal, especially when ++ * we only support RSA. ++ * ++ * Dynamic loading also causes some concerns. We can't load gcry_rsa from the ++ * the filesystem after we install the verifier - we won't be able to verify ++ * it without having it already present. We also shouldn't load it before we ++ * install the verifier, because that would mean it wouldn't be verified - an ++ * attacker could insert any code they wanted into the module. ++ * ++ * So instead, reference the internal symbol from gcry_rsa. That creates a ++ * direct dependency on gcry_rsa, so it will be built in when this module ++ * is built in. Being built in (assuming the core image is itself signed!) ++ * also resolves our concerns about loading from the filesystem. ++ */ ++extern gcry_pk_spec_t _gcry_pubkey_spec_rsa; ++ ++static enum ++{ check_sigs_no = 0, ++ check_sigs_enforce = 1, ++ check_sigs_forced = 2 ++} check_sigs = check_sigs_no; ++ ++static const char * ++grub_env_read_sec (struct grub_env_var *var __attribute__((unused)), ++ const char *val __attribute__((unused))) ++{ ++ if (check_sigs == check_sigs_forced) ++ return "forced"; ++ else if (check_sigs == check_sigs_enforce) ++ return "enforce"; ++ else ++ return "no"; ++} ++ ++static char * ++grub_env_write_sec (struct grub_env_var *var __attribute__((unused)), ++ const char *val) ++{ ++ /* Do not allow the value to be changed if set to forced */ ++ if (check_sigs == check_sigs_forced) ++ return grub_strdup ("forced"); ++ ++ if ((*val == '2') || (*val == 'f')) ++ check_sigs = check_sigs_forced; ++ else if ((*val == '1') || (*val == 'e')) ++ check_sigs = check_sigs_enforce; ++ else if ((*val == '0') || (*val == 'n')) ++ check_sigs = check_sigs_no; ++ ++ return grub_strdup (grub_env_read_sec (NULL, NULL)); ++} ++ ++static grub_err_t ++file_read_all (grub_file_t file, grub_uint8_t **buf, grub_size_t *len) ++{ ++ grub_off_t full_file_size; ++ grub_size_t file_size, total_read_size = 0; ++ grub_ssize_t read_size; ++ ++ full_file_size = grub_file_size (file); ++ if (full_file_size == GRUB_FILE_SIZE_UNKNOWN) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Cannot read a file of unknown size into a buffer")); ++ ++ if (full_file_size > GRUB_SIZE_MAX) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, ++ N_("File is too large to read: %" PRIuGRUB_UINT64_T ++ " bytes"), full_file_size); ++ ++ file_size = (grub_size_t) full_file_size; ++ ++ *buf = grub_malloc (file_size); ++ if (!*buf) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ N_("Could not allocate file data buffer size %" ++ PRIuGRUB_SIZE), file_size); ++ ++ while (total_read_size < file_size) ++ { ++ read_size = ++ grub_file_read (file, *buf + total_read_size, ++ file_size - total_read_size); ++ ++ if (read_size < 0) ++ { ++ grub_free (*buf); ++ return grub_errno; ++ } ++ else if (read_size == 0) ++ { ++ grub_free (*buf); ++ return grub_error (GRUB_ERR_IO, ++ N_("Could not read full file size (%" ++ PRIuGRUB_SIZE "), only %" PRIuGRUB_SIZE ++ " bytes read"), file_size, total_read_size); ++ } ++ ++ total_read_size += read_size; ++ } ++ *len = file_size; ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++read_cert_from_file (grub_file_t f, struct x509_certificate *certificate) ++{ ++ grub_err_t err; ++ grub_uint8_t *buf; ++ grub_size_t file_size; ++ ++ err = file_read_all (f, &buf, &file_size); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ ++ err = parse_x509_certificate (buf, file_size, certificate); ++ if (err != GRUB_ERR_NONE) ++ { ++ grub_free (buf); ++ return err; ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize, ++ struct grub_appended_signature *sig) ++{ ++ grub_err_t err; ++ grub_size_t pkcs7_size; ++ grub_size_t remaining_len; ++ const grub_uint8_t *appsigdata = buf + bufsize - grub_strlen (magic); ++ ++ if (bufsize < grub_strlen (magic)) ++ return grub_error (GRUB_ERR_BAD_SIGNATURE, ++ N_("File too short for signature magic")); ++ ++ if (grub_memcmp (appsigdata, (grub_uint8_t *) magic, grub_strlen (magic))) ++ return grub_error (GRUB_ERR_BAD_SIGNATURE, ++ N_("Missing or invalid signature magic")); ++ ++ remaining_len = bufsize - grub_strlen (magic); ++ ++ if (remaining_len < sizeof (struct module_signature)) ++ return grub_error (GRUB_ERR_BAD_SIGNATURE, ++ N_("File too short for signature metadata")); ++ ++ appsigdata -= sizeof (struct module_signature); ++ ++ /* extract the metadata */ ++ grub_memcpy (&(sig->sig_metadata), appsigdata, ++ sizeof (struct module_signature)); ++ ++ remaining_len -= sizeof (struct module_signature); ++ ++ if (sig->sig_metadata.id_type != 2) ++ return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("Wrong signature type")); ++ ++ pkcs7_size = grub_be_to_cpu32 (sig->sig_metadata.sig_len); ++ ++ if (pkcs7_size > remaining_len) ++ return grub_error (GRUB_ERR_BAD_SIGNATURE, ++ N_("File too short for PKCS#7 message")); ++ ++ grub_dprintf ("appendedsig", "sig len %" PRIuGRUB_SIZE "\n", pkcs7_size); ++ ++ sig->signature_len = ++ grub_strlen (magic) + sizeof (struct module_signature) + pkcs7_size; ++ ++ /* rewind pointer and parse pkcs7 data */ ++ appsigdata -= pkcs7_size; ++ ++ err = parse_pkcs7_signedData (appsigdata, pkcs7_size, &sig->pkcs7); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize) ++{ ++ grub_err_t err = GRUB_ERR_NONE; ++ grub_size_t datasize; ++ void *context; ++ unsigned char *hash; ++ gcry_mpi_t hashmpi; ++ gcry_err_code_t rc; ++ struct x509_certificate *pk; ++ struct grub_appended_signature sig; ++ struct pkcs7_signerInfo *si; ++ int i; ++ ++ if (!grub_trusted_key) ++ return grub_error (GRUB_ERR_BAD_SIGNATURE, ++ N_("No trusted keys to verify against")); ++ ++ err = extract_appended_signature (buf, bufsize, &sig); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ ++ datasize = bufsize - sig.signature_len; ++ ++ for (i = 0; i < sig.pkcs7.signerInfo_count; i++) ++ { ++ /* This could be optimised in a couple of ways: ++ - we could only compute hashes once per hash type ++ - we could track signer information and only verify where IDs match ++ For now we do the naive O(trusted keys * pkcs7 signers) approach. ++ */ ++ si = &sig.pkcs7.signerInfos[i]; ++ context = grub_zalloc (si->hash->contextsize); ++ if (!context) ++ return grub_errno; ++ ++ si->hash->init (context); ++ si->hash->write (context, buf, datasize); ++ si->hash->final (context); ++ hash = si->hash->read (context); ++ ++ grub_dprintf ("appendedsig", ++ "data size %" PRIxGRUB_SIZE ", signer %d hash %02x%02x%02x%02x...\n", ++ datasize, i, hash[0], hash[1], hash[2], hash[3]); ++ ++ err = GRUB_ERR_BAD_SIGNATURE; ++ for (pk = grub_trusted_key; pk; pk = pk->next) ++ { ++ rc = grub_crypto_rsa_pad (&hashmpi, hash, si->hash, pk->mpis[0]); ++ if (rc) ++ { ++ err = grub_error (GRUB_ERR_BAD_SIGNATURE, ++ N_("Error padding hash for RSA verification: %d"), ++ rc); ++ grub_free (context); ++ goto cleanup; ++ } ++ ++ rc = _gcry_pubkey_spec_rsa.verify (0, hashmpi, &si->sig_mpi, ++ pk->mpis, NULL, NULL); ++ gcry_mpi_release (hashmpi); ++ ++ if (rc == 0) ++ { ++ grub_dprintf ("appendedsig", ++ "verify signer %d with key '%s' succeeded\n", i, ++ pk->subject); ++ err = GRUB_ERR_NONE; ++ break; ++ } ++ ++ grub_dprintf ("appendedsig", ++ "verify signer %d with key '%s' failed with %d\n", i, ++ pk->subject, rc); ++ } ++ ++ grub_free (context); ++ ++ if (err == GRUB_ERR_NONE) ++ break; ++ } ++ ++ /* If we didn't verify, provide a neat message */ ++ if (err != GRUB_ERR_NONE) ++ err = grub_error (GRUB_ERR_BAD_SIGNATURE, ++ N_("Failed to verify signature against a trusted key")); ++ ++cleanup: ++ pkcs7_signedData_release (&sig.pkcs7); ++ ++ return err; ++} ++ ++static grub_err_t ++grub_cmd_verify_signature (grub_command_t cmd __attribute__((unused)), ++ int argc, char **args) ++{ ++ grub_file_t f; ++ grub_err_t err = GRUB_ERR_NONE; ++ grub_uint8_t *data; ++ grub_size_t file_size; ++ ++ if (argc < 1) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); ++ ++ grub_dprintf ("appendedsig", "verifying %s\n", args[0]); ++ ++ f = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE); ++ if (!f) ++ { ++ err = grub_errno; ++ goto cleanup; ++ } ++ ++ err = file_read_all (f, &data, &file_size); ++ if (err != GRUB_ERR_NONE) ++ goto cleanup; ++ ++ err = grub_verify_appended_signature (data, file_size); ++ ++ grub_free (data); ++ ++cleanup: ++ if (f) ++ grub_file_close (f); ++ return err; ++} ++ ++static grub_err_t ++grub_cmd_distrust (grub_command_t cmd __attribute__((unused)), ++ int argc, char **args) ++{ ++ unsigned long cert_num, i; ++ struct x509_certificate *cert, *prev; ++ ++ if (argc != 1) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("One argument expected")); ++ ++ grub_errno = GRUB_ERR_NONE; ++ cert_num = grub_strtoul (args[0], NULL, 10); ++ if (grub_errno != GRUB_ERR_NONE) ++ return grub_errno; ++ ++ if (cert_num < 1) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("Certificate number too small - numbers start at 1")); ++ ++ if (cert_num == 1) ++ { ++ cert = grub_trusted_key; ++ grub_trusted_key = cert->next; ++ ++ certificate_release (cert); ++ grub_free (cert); ++ return GRUB_ERR_NONE; ++ } ++ i = 2; ++ prev = grub_trusted_key; ++ cert = grub_trusted_key->next; ++ while (cert) ++ { ++ if (i == cert_num) ++ { ++ prev->next = cert->next; ++ certificate_release (cert); ++ grub_free (cert); ++ return GRUB_ERR_NONE; ++ } ++ i++; ++ prev = cert; ++ cert = cert->next; ++ } ++ ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("No certificate number %lu found - only %lu certificates in the store"), ++ cert_num, i - 1); ++} ++ ++static grub_err_t ++grub_cmd_trust (grub_command_t cmd __attribute__((unused)), ++ int argc, char **args) ++{ ++ grub_file_t certf; ++ struct x509_certificate *cert = NULL; ++ grub_err_t err; ++ ++ if (argc != 1) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); ++ ++ certf = grub_file_open (args[0], ++ GRUB_FILE_TYPE_CERTIFICATE_TRUST ++ | GRUB_FILE_TYPE_NO_DECOMPRESS); ++ if (!certf) ++ return grub_errno; ++ ++ ++ cert = grub_zalloc (sizeof (struct x509_certificate)); ++ if (!cert) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, ++ N_("Could not allocate memory for certificate")); ++ ++ err = read_cert_from_file (certf, cert); ++ grub_file_close (certf); ++ if (err != GRUB_ERR_NONE) ++ { ++ grub_free (cert); ++ return err; ++ } ++ grub_dprintf ("appendedsig", "Loaded certificate with CN: %s\n", ++ cert->subject); ++ ++ cert->next = grub_trusted_key; ++ grub_trusted_key = cert; ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_cmd_list (grub_command_t cmd __attribute__((unused)), ++ int argc __attribute__((unused)), ++ char **args __attribute__((unused))) ++{ ++ struct x509_certificate *cert; ++ int cert_num = 1; ++ grub_size_t i; ++ ++ for (cert = grub_trusted_key; cert; cert = cert->next) ++ { ++ grub_printf (N_("Certificate %d:\n"), cert_num); ++ ++ grub_printf (N_("\tSerial: ")); ++ for (i = 0; i < cert->serial_len - 1; i++) ++ { ++ grub_printf ("%02x:", cert->serial[i]); ++ } ++ grub_printf ("%02x\n", cert->serial[cert->serial_len - 1]); ++ ++ grub_printf ("\tCN: %s\n\n", cert->subject); ++ cert_num++; ++ ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++appendedsig_init (grub_file_t io __attribute__((unused)), ++ enum grub_file_type type, ++ void **context __attribute__((unused)), ++ enum grub_verify_flags *flags) ++{ ++ if (check_sigs == check_sigs_no) ++ { ++ *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; ++ return GRUB_ERR_NONE; ++ } ++ ++ switch (type & GRUB_FILE_TYPE_MASK) ++ { ++ case GRUB_FILE_TYPE_CERTIFICATE_TRUST: ++ /* ++ * This is a certificate to add to trusted keychain. ++ * ++ * This needs to be verified or blocked. Ideally we'd write an x509 ++ * verifier, but we lack the hubris required to take this on. Instead, ++ * require that it have an appended signature. ++ */ ++ ++ /* Fall through */ ++ ++ case GRUB_FILE_TYPE_LINUX_KERNEL: ++ case GRUB_FILE_TYPE_GRUB_MODULE: ++ /* ++ * Appended signatures are only defined for ELF binaries. ++ * Out of an abundance of caution, we only verify Linux kernels and ++ * GRUB modules at this point. ++ */ ++ *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK; ++ return GRUB_ERR_NONE; ++ ++ case GRUB_FILE_TYPE_ACPI_TABLE: ++ case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE: ++ /* ++ * It is possible to use appended signature verification without ++ * lockdown - like the PGP verifier. When combined with an embedded ++ * config file in a signed grub binary, this could still be a meaningful ++ * secure-boot chain - so long as it isn't subverted by something like a ++ * rouge ACPI table or DT image. Defer them explicitly. ++ */ ++ *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH; ++ return GRUB_ERR_NONE; ++ ++ default: ++ *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; ++ return GRUB_ERR_NONE; ++ } ++} ++ ++static grub_err_t ++appendedsig_write (void *ctxt __attribute__((unused)), ++ void *buf, grub_size_t size) ++{ ++ return grub_verify_appended_signature (buf, size); ++} ++ ++struct grub_file_verifier grub_appendedsig_verifier = { ++ .name = "appendedsig", ++ .init = appendedsig_init, ++ .write = appendedsig_write, ++}; ++ ++static grub_ssize_t ++pseudo_read (struct grub_file *file, char *buf, grub_size_t len) ++{ ++ grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len); ++ return len; ++} ++ ++/* Filesystem descriptor. */ ++static struct grub_fs pseudo_fs = { ++ .name = "pseudo", ++ .fs_read = pseudo_read ++}; ++ ++static grub_command_t cmd_verify, cmd_list, cmd_distrust, cmd_trust; ++ ++GRUB_MOD_INIT (appendedsig) ++{ ++ int rc; ++ struct grub_module_header *header; ++ ++ /* If in lockdown, immediately enter forced mode */ ++ if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) ++ check_sigs = check_sigs_forced; ++ ++ grub_trusted_key = NULL; ++ ++ grub_register_variable_hook ("check_appended_signatures", ++ grub_env_read_sec, grub_env_write_sec); ++ grub_env_export ("check_appended_signatures"); ++ ++ rc = asn1_init (); ++ if (rc) ++ grub_fatal ("Error initing ASN.1 data structures: %d: %s\n", rc, ++ asn1_strerror (rc)); ++ ++ FOR_MODULES (header) ++ { ++ struct grub_file pseudo_file; ++ struct x509_certificate *pk = NULL; ++ grub_err_t err; ++ ++ /* Not an ELF module, skip. */ ++ if (header->type != OBJ_TYPE_X509_PUBKEY) ++ continue; ++ ++ grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); ++ pseudo_file.fs = &pseudo_fs; ++ pseudo_file.size = header->size - sizeof (struct grub_module_header); ++ pseudo_file.data = (char *) header + sizeof (struct grub_module_header); ++ ++ grub_dprintf ("appendedsig", ++ "Found an x509 key, size=%" PRIuGRUB_UINT64_T "\n", ++ pseudo_file.size); ++ ++ pk = grub_zalloc (sizeof (struct x509_certificate)); ++ if (!pk) ++ { ++ grub_fatal ("Out of memory loading initial certificates"); ++ } ++ ++ err = read_cert_from_file (&pseudo_file, pk); ++ if (err != GRUB_ERR_NONE) ++ grub_fatal ("Error loading initial key: %s", grub_errmsg); ++ ++ grub_dprintf ("appendedsig", "loaded certificate CN='%s'\n", pk->subject); ++ ++ pk->next = grub_trusted_key; ++ grub_trusted_key = pk; ++ } ++ ++ cmd_trust = ++ grub_register_command ("trust_certificate", grub_cmd_trust, ++ N_("X509_CERTIFICATE"), ++ N_("Add X509_CERTIFICATE to trusted certificates.")); ++ cmd_list = ++ grub_register_command ("list_certificates", grub_cmd_list, 0, ++ N_("Show the list of trusted x509 certificates.")); ++ cmd_verify = ++ grub_register_command ("verify_appended", grub_cmd_verify_signature, ++ N_("FILE"), ++ N_("Verify FILE against the trusted x509 certificates.")); ++ cmd_distrust = ++ grub_register_command ("distrust_certificate", grub_cmd_distrust, ++ N_("CERT_NUMBER"), ++ N_("Remove CERT_NUMBER (as listed by list_certificates) from trusted certificates.")); ++ ++ grub_verifier_register (&grub_appendedsig_verifier); ++ grub_dl_set_persistent (mod); ++} ++ ++GRUB_MOD_FINI (appendedsig) ++{ ++ /* ++ * grub_dl_set_persistent should prevent this from actually running, but ++ * it does still run under emu. ++ */ ++ ++ grub_verifier_unregister (&grub_appendedsig_verifier); ++ grub_unregister_command (cmd_verify); ++ grub_unregister_command (cmd_list); ++ grub_unregister_command (cmd_trust); ++ grub_unregister_command (cmd_distrust); ++} +--- a/include/grub/file.h ++++ b/include/grub/file.h +@@ -80,6 +80,8 @@ + GRUB_FILE_TYPE_PUBLIC_KEY, + /* File holding public key to add to trused keys. */ + GRUB_FILE_TYPE_PUBLIC_KEY_TRUST, ++ /* File holding x509 certificiate to add to trusted keys. */ ++ GRUB_FILE_TYPE_CERTIFICATE_TRUST, + /* File of which we intend to print a blocklist to the user. */ + GRUB_FILE_TYPE_PRINT_BLOCKLIST, + /* File we intend to use for test loading or testing speed. */ diff --git a/0020-appended-signatures-verification-tests.patch b/0020-appended-signatures-verification-tests.patch new file mode 100644 index 0000000..b747987 --- /dev/null +++ b/0020-appended-signatures-verification-tests.patch @@ -0,0 +1,1308 @@ +From 221f2419c70cd953515562901b1e72946632cd13 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Thu, 30 Jul 2020 01:31:02 +1000 +Subject: [PATCH 20/23] appended signatures: verification tests + +These tests are run through all_functional_test and test a range +of commands and behaviours. + +Signed-off-by: Daniel Axtens + +--- + +v2 changes: + + - add a test for EKU + - add tests for files signed with multiple signers + - add a test of padded PKCS#7 messages + - use macros to reduce duplication for exposing certificate files + to the test via procfs + - more useful comments +--- + grub-core/Makefile.core.def | 6 + + grub-core/tests/appended_signature_test.c | 273 ++++++ + grub-core/tests/appended_signatures.h | 975 ++++++++++++++++++++++ + grub-core/tests/lib/functional_test.c | 1 + + 4 files changed, 1255 insertions(+) + create mode 100644 grub-core/tests/appended_signature_test.c + create mode 100644 grub-core/tests/appended_signatures.h + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2191,6 +2191,12 @@ + }; + + module = { ++ name = appended_signature_test; ++ common = tests/appended_signature_test.c; ++ common = tests/appended_signatures.h; ++}; ++ ++module = { + name = signature_test; + common = tests/signature_test.c; + common = tests/signatures.h; +--- /dev/null ++++ b/grub-core/tests/appended_signature_test.c +@@ -0,0 +1,273 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2020 IBM Corporation. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "appended_signatures.h" ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++#define PROC_FILE(identifier, file_name) \ ++static char * \ ++get_ ## identifier (grub_size_t *sz) \ ++{ \ ++ char *ret; \ ++ \ ++ *sz = identifier ## _len; \ ++ ret = grub_malloc (*sz); \ ++ if (ret) \ ++ grub_memcpy (ret, identifier, *sz); \ ++ return ret; \ ++} \ ++\ ++static struct grub_procfs_entry identifier ## _entry = \ ++{ \ ++ .name = file_name, \ ++ .get_contents = get_ ## identifier \ ++}; ++ ++#define DEFINE_TEST_CASE(case_name) PROC_FILE(case_name, #case_name) ++ ++#define DO_TEST(case_name, is_valid) \ ++{ \ ++ grub_procfs_register (#case_name, &case_name ## _entry); \ ++ do_verify ("(proc)/" #case_name, is_valid); \ ++ grub_procfs_unregister (&case_name ## _entry); \ ++} ++ ++ ++DEFINE_TEST_CASE (hi_signed); ++DEFINE_TEST_CASE (hi_signed_sha256); ++DEFINE_TEST_CASE (hj_signed); ++DEFINE_TEST_CASE (short_msg); ++DEFINE_TEST_CASE (unsigned_msg); ++DEFINE_TEST_CASE (hi_signed_2nd); ++DEFINE_TEST_CASE (hi_double); ++DEFINE_TEST_CASE (hi_double_extended); ++ ++PROC_FILE (certificate_der, "certificate.der") ++PROC_FILE (certificate2_der, "certificate2.der") ++PROC_FILE (certificate_printable_der, "certificate_printable.der") ++PROC_FILE (certificate_eku_der, "certificate_eku.der") ++ ++static void ++do_verify (const char *f, int is_valid) ++{ ++ grub_command_t cmd; ++ char *args[] = { (char *) f, NULL }; ++ grub_err_t err; ++ ++ cmd = grub_command_find ("verify_appended"); ++ if (!cmd) ++ { ++ grub_test_assert (0, "can't find command `%s'", "verify_appended"); ++ return; ++ } ++ err = (cmd->func) (cmd, 1, args); ++ if (is_valid) ++ { ++ grub_test_assert (err == GRUB_ERR_NONE, ++ "verification of %s failed: %d: %s", f, grub_errno, ++ grub_errmsg); ++ } ++ else ++ { ++ grub_test_assert (err == GRUB_ERR_BAD_SIGNATURE, ++ "verification of %s unexpectedly succeeded", f); ++ } ++ grub_errno = GRUB_ERR_NONE; ++ ++} ++ ++static void ++appended_signature_test (void) ++{ ++ grub_command_t cmd_trust, cmd_distrust; ++ char *trust_args[] = { (char *) "(proc)/certificate.der", NULL }; ++ char *trust_args2[] = { (char *) "(proc)/certificate2.der", NULL }; ++ char *trust_args_printable[] = { (char *) "(proc)/certificate_printable.der", ++ NULL }; ++ char *trust_args_eku[] = { (char *) "(proc)/certificate_eku.der", NULL }; ++ char *distrust_args[] = { (char *) "1", NULL }; ++ char *distrust2_args[] = { (char *) "2", NULL }; ++ grub_err_t err; ++ ++ grub_procfs_register ("certificate.der", &certificate_der_entry); ++ grub_procfs_register ("certificate2.der", &certificate2_der_entry); ++ grub_procfs_register ("certificate_printable.der", ++ &certificate_printable_der_entry); ++ grub_procfs_register ("certificate_eku.der", &certificate_eku_der_entry); ++ ++ cmd_trust = grub_command_find ("trust_certificate"); ++ if (!cmd_trust) ++ { ++ grub_test_assert (0, "can't find command `%s'", "trust_certificate"); ++ return; ++ } ++ err = (cmd_trust->func) (cmd_trust, 1, trust_args); ++ ++ grub_test_assert (err == GRUB_ERR_NONE, ++ "loading certificate failed: %d: %s", grub_errno, ++ grub_errmsg); ++ ++ /* If we have no certificate the remainder of the tests are meaningless */ ++ if (err != GRUB_ERR_NONE) ++ return; ++ ++ /* ++ * Reload the command: this works around some 'interesting' behaviour in the ++ * dynamic command dispatcher. The first time you call cmd->func you get a ++ * dispatcher that loads the module, finds the real cmd, calls it, and then ++ * releases some internal storage. This means it's not safe to call a second ++ * time and we need to reload it. ++ */ ++ cmd_trust = grub_command_find ("trust_certificate"); ++ ++ /* hi, signed with key 1, SHA-512 */ ++ DO_TEST (hi_signed, 1); ++ ++ /* hi, signed with key 1, SHA-256 */ ++ DO_TEST (hi_signed_sha256, 1); ++ ++ /* hi, key 1, SHA-512, second byte corrupted */ ++ DO_TEST (hj_signed, 0); ++ ++ /* message too short for a signature */ ++ DO_TEST (short_msg, 0); ++ ++ /* lorem ipsum */ ++ DO_TEST (unsigned_msg, 0); ++ ++ /* hi, signed with both keys, SHA-512 */ ++ DO_TEST (hi_double, 1); ++ ++ /* ++ * hi, signed with both keys and with empty space to test we haven't broken ++ * support for adding more signatures after the fact ++ */ ++ DO_TEST (hi_double_extended, 1); ++ ++ /* ++ * in enforcing mode, we shouldn't be able to load a certificate that isn't ++ * signed by an existing trusted key. ++ * ++ * However, procfs files automatically skip the verification test, so we can't ++ * easily test this. ++ */ ++ ++ /* ++ * verify that testing with 2 trusted certs works ++ */ ++ DO_TEST (hi_signed_2nd, 0); ++ ++ err = (cmd_trust->func) (cmd_trust, 1, trust_args2); ++ ++ grub_test_assert (err == GRUB_ERR_NONE, ++ "loading certificate 2 failed: %d: %s", grub_errno, ++ grub_errmsg); ++ ++ if (err != GRUB_ERR_NONE) ++ return; ++ ++ DO_TEST (hi_signed_2nd, 1); ++ DO_TEST (hi_signed, 1); ++ DO_TEST (hi_double, 1); ++ DO_TEST (hi_double_extended, 1); ++ ++ /* ++ * Check certificate removal. They're added to the _top_ of the list and ++ * removed by position in the list. Current the list looks like [#2, #1]. ++ * ++ * First test removing the second certificate in the list, which is ++ * certificate #1, giving us just [#2]. ++ */ ++ cmd_distrust = grub_command_find ("distrust_certificate"); ++ if (!cmd_distrust) ++ { ++ grub_test_assert (0, "can't find command `%s'", "distrust_certificate"); ++ return; ++ } ++ ++ err = (cmd_distrust->func) (cmd_distrust, 1, distrust2_args); ++ grub_test_assert (err == GRUB_ERR_NONE, ++ "distrusting certificate 1 failed: %d: %s", grub_errno, ++ grub_errmsg); ++ DO_TEST (hi_signed_2nd, 1); ++ DO_TEST (hi_signed, 0); ++ DO_TEST (hi_double, 1); ++ ++ /* ++ * Now reload certificate #1. This will make the list look like [#1, #2] ++ */ ++ err = (cmd_trust->func) (cmd_trust, 1, trust_args); ++ ++ grub_test_assert (err == GRUB_ERR_NONE, ++ "reloading certificate 1 failed: %d: %s", grub_errno, ++ grub_errmsg); ++ DO_TEST (hi_signed, 1); ++ ++ /* Remove the first certificate in the list, giving us just [#2] */ ++ err = (cmd_distrust->func) (cmd_distrust, 1, distrust_args); ++ grub_test_assert (err == GRUB_ERR_NONE, ++ "distrusting certificate 1 (first time) failed: %d: %s", ++ grub_errno, grub_errmsg); ++ DO_TEST (hi_signed_2nd, 1); ++ DO_TEST (hi_signed, 0); ++ ++ /* ++ * Remove the first certificate again, giving an empty list. ++ * ++ * verify_appended should fail if there are no certificates to verify against. ++ */ ++ err = (cmd_distrust->func) (cmd_distrust, 1, distrust_args); ++ grub_test_assert (err == GRUB_ERR_NONE, ++ "distrusting certificate 1 (second time) failed: %d: %s", ++ grub_errno, grub_errmsg); ++ DO_TEST (hi_signed_2nd, 0); ++ DO_TEST (hi_double, 0); ++ ++ /* ++ * Lastly, check a certificate that uses printableString rather than ++ * utf8String loads properly, and that a certificate with an appropriate ++ * extended key usage loads. ++ */ ++ err = (cmd_trust->func) (cmd_trust, 1, trust_args_printable); ++ grub_test_assert (err == GRUB_ERR_NONE, ++ "trusting printable certificate failed: %d: %s", ++ grub_errno, grub_errmsg); ++ ++ err = (cmd_trust->func) (cmd_trust, 1, trust_args_eku); ++ grub_test_assert (err == GRUB_ERR_NONE, ++ "trusting certificate with extended key usage failed: %d: %s", ++ grub_errno, grub_errmsg); ++ ++ grub_procfs_unregister (&certificate_der_entry); ++ grub_procfs_unregister (&certificate2_der_entry); ++ grub_procfs_unregister (&certificate_printable_der_entry); ++ grub_procfs_unregister (&certificate_eku_der_entry); ++} ++ ++GRUB_FUNCTIONAL_TEST (appended_signature_test, appended_signature_test); +--- /dev/null ++++ b/grub-core/tests/appended_signatures.h +@@ -0,0 +1,975 @@ ++unsigned char certificate_der[] = { ++ 0x30, 0x82, 0x05, 0x5d, 0x30, 0x82, 0x03, 0x45, 0xa0, 0x03, 0x02, 0x01, ++ 0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, ++ 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, ++ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, ++ 0x05, 0x00, 0x30, 0x3d, 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, ++ 0x03, 0x0c, 0x32, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, ++ 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, ++ 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, ++ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, ++ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x31, 0x30, ++ 0x36, 0x32, 0x39, 0x30, 0x38, 0x33, 0x36, 0x31, 0x33, 0x5a, 0x18, 0x0f, ++ 0x32, 0x31, 0x32, 0x31, 0x30, 0x36, 0x30, 0x35, 0x30, 0x38, 0x33, 0x36, ++ 0x31, 0x33, 0x5a, 0x30, 0x33, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, ++ 0x04, 0x03, 0x0c, 0x28, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, ++ 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, ++ 0x75, 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x69, 0x67, ++ 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02, 0x22, ++ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, ++ 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, ++ 0x02, 0x82, 0x02, 0x01, 0x00, 0xb9, 0x09, 0xb2, 0xf6, 0x24, 0x34, 0xdc, ++ 0x62, 0xe6, 0x4e, 0xee, 0x04, 0xdb, 0x29, 0xdc, 0x94, 0xcc, 0xee, 0x8a, ++ 0x5b, 0xc3, 0x9e, 0x06, 0xba, 0xa7, 0x9b, 0xa4, 0x5f, 0x15, 0x59, 0x8e, ++ 0xb8, 0x6e, 0x3c, 0xeb, 0x2e, 0xf2, 0xac, 0x21, 0x42, 0xbd, 0x30, 0xa1, ++ 0x39, 0xe5, 0xb9, 0x4f, 0xa0, 0x53, 0xd5, 0x42, 0xdc, 0x8a, 0x87, 0x30, ++ 0x38, 0x93, 0x44, 0x80, 0x3b, 0x1a, 0x7e, 0x9e, 0x8e, 0x3e, 0xea, 0x45, ++ 0xa0, 0x11, 0x8b, 0xfb, 0x78, 0xe4, 0xbc, 0x65, 0x6b, 0x73, 0xea, 0x6e, ++ 0xdf, 0x7c, 0x5b, 0x63, 0x7e, 0x5b, 0x0a, 0x1c, 0xe6, 0x76, 0x19, 0xb5, ++ 0x01, 0xde, 0xf6, 0x65, 0x51, 0x30, 0x0a, 0x56, 0x69, 0x69, 0xe8, 0x20, ++ 0xf9, 0x13, 0xf1, 0xbf, 0x6f, 0xdd, 0xce, 0x94, 0x96, 0x6e, 0x63, 0xd6, ++ 0xfa, 0xa4, 0x91, 0x5f, 0xb3, 0x9c, 0xc7, 0xfa, 0xa9, 0xff, 0x66, 0x5f, ++ 0xf3, 0xab, 0x5e, 0xdf, 0x4e, 0xca, 0x11, 0xcf, 0xbf, 0xf8, 0xad, 0x65, ++ 0xb1, 0x49, 0x8b, 0xe9, 0x2a, 0xad, 0x7d, 0xf3, 0x0b, 0xfa, 0x5b, 0x6a, ++ 0x6a, 0x20, 0x12, 0x77, 0xef, 0x4b, 0xb6, 0xbe, 0x92, 0xba, 0x14, 0x9c, ++ 0x5e, 0xea, 0xdc, 0x56, 0x6d, 0x92, 0xd3, 0x64, 0x22, 0xf6, 0x12, 0xe8, ++ 0x7d, 0x5e, 0x9c, 0xd6, 0xf9, 0x75, 0x68, 0x7f, 0x8f, 0xd3, 0x6e, 0x05, ++ 0x94, 0x91, 0x4f, 0xa1, 0xd6, 0x50, 0x72, 0x3b, 0x11, 0x1f, 0x28, 0x13, ++ 0xe8, 0x25, 0x6b, 0xdf, 0xff, 0x72, 0x46, 0x25, 0xe9, 0x05, 0x6f, 0x02, ++ 0xc7, 0x1e, 0xc9, 0xcf, 0x99, 0xe9, 0xa7, 0xe2, 0xae, 0xbc, 0xc1, 0x22, ++ 0x32, 0x73, 0x2d, 0xa3, 0x70, 0x8f, 0xa7, 0x8d, 0xbf, 0x5f, 0x74, 0x05, ++ 0x1b, 0x5e, 0xfe, 0x97, 0x3c, 0xe7, 0x3b, 0x86, 0x0d, 0xf6, 0x38, 0xdb, ++ 0xd2, 0x39, 0x47, 0x82, 0x00, 0x44, 0x6c, 0x7b, 0x40, 0x24, 0x0b, 0x3a, ++ 0xd4, 0x19, 0x31, 0xba, 0x4e, 0x8e, 0xa3, 0x33, 0xa6, 0x78, 0xef, 0x72, ++ 0x9f, 0x06, 0x37, 0x01, 0x9b, 0x79, 0x0d, 0x04, 0xbf, 0xba, 0xd5, 0x1f, ++ 0x27, 0xdc, 0x85, 0xbb, 0xef, 0xd2, 0x60, 0xda, 0xa0, 0x3f, 0x66, 0xce, ++ 0x9f, 0xa2, 0x7e, 0xa8, 0x8d, 0xee, 0x14, 0x4b, 0xcb, 0x93, 0xf1, 0x38, ++ 0xac, 0x4f, 0xd8, 0x29, 0xf3, 0x6f, 0xd4, 0xfd, 0x4d, 0x34, 0x77, 0x58, ++ 0x99, 0xdb, 0x16, 0xc1, 0xd0, 0xc7, 0x43, 0x41, 0x70, 0xc4, 0xad, 0x01, ++ 0x29, 0x65, 0x22, 0x43, 0x00, 0x6f, 0xb3, 0x00, 0x27, 0x38, 0xc1, 0x4f, ++ 0xda, 0x28, 0x96, 0x42, 0xdc, 0xbc, 0x3e, 0x34, 0x8e, 0x14, 0xb8, 0xf3, ++ 0x86, 0x4a, 0xea, 0x16, 0x90, 0xf9, 0x0e, 0x9e, 0x8f, 0x66, 0x0c, 0xbf, ++ 0x29, 0xd3, 0x8f, 0xfc, 0x4d, 0x38, 0x68, 0xe2, 0xe7, 0x64, 0x32, 0x47, ++ 0xdd, 0x56, 0xc9, 0xe4, 0x47, 0x9f, 0x18, 0x89, 0xfc, 0x30, 0x7a, 0xae, ++ 0x63, 0xe4, 0xec, 0x93, 0x04, 0xd4, 0x61, 0xe7, 0xbf, 0x0a, 0x06, 0x29, ++ 0xc2, 0xa6, 0xd5, 0x53, 0x5d, 0x65, 0x6d, 0x4a, 0xd0, 0xb7, 0x68, 0x4d, ++ 0x46, 0x0a, 0xb5, 0xff, 0x52, 0x5e, 0x92, 0x7e, 0x75, 0x08, 0xa4, 0x63, ++ 0x0a, 0x6c, 0x31, 0x7a, 0xaa, 0x0c, 0x52, 0xf4, 0x2e, 0xcd, 0x08, 0xeb, ++ 0xb3, 0xbd, 0xad, 0x8b, 0x8b, 0x9b, 0x8d, 0x71, 0x42, 0x30, 0x8e, 0xc7, ++ 0xfd, 0xec, 0xb7, 0xe6, 0x26, 0x96, 0xf2, 0x74, 0x1b, 0x78, 0x95, 0x22, ++ 0x14, 0xf3, 0xc9, 0xd3, 0x79, 0x11, 0xd9, 0xb7, 0x4d, 0x0d, 0x61, 0x60, ++ 0x5c, 0x47, 0x50, 0xf3, 0xca, 0x84, 0x4c, 0x5c, 0x30, 0x2c, 0x6a, 0x18, ++ 0x26, 0xb0, 0xf3, 0xd1, 0x15, 0x19, 0x39, 0xc3, 0x23, 0x13, 0x0f, 0x9c, ++ 0x97, 0x2b, 0x97, 0x93, 0xf9, 0xf8, 0x18, 0x9b, 0x4a, 0x4d, 0xd6, 0xd3, ++ 0xf5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c, ++ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, ++ 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07, ++ 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, ++ 0x8f, 0xba, 0x8b, 0xf5, 0xf4, 0x77, 0xb2, 0xa4, 0x19, 0xef, 0x43, 0xb1, ++ 0x8b, 0x03, 0x4b, 0x45, 0x47, 0xb5, 0x2a, 0x48, 0x30, 0x1f, 0x06, 0x03, ++ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x59, 0x1c, 0xb5, ++ 0x52, 0x62, 0x83, 0x05, 0x3b, 0x41, 0x4c, 0x63, 0x4d, 0x5b, 0xf4, 0x8c, ++ 0xe6, 0xd7, 0xda, 0x87, 0x54, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, ++ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, ++ 0x00, 0x36, 0x2d, 0x0a, 0xcb, 0x49, 0x54, 0x75, 0xd7, 0xca, 0x21, 0x86, ++ 0xae, 0x40, 0x0f, 0x63, 0x10, 0x35, 0xfd, 0xbc, 0xba, 0x28, 0x31, 0x33, ++ 0x07, 0x08, 0x64, 0x03, 0x6c, 0xd3, 0xd5, 0xf7, 0xb7, 0x79, 0x11, 0x0c, ++ 0xa8, 0x9e, 0xfd, 0x34, 0xa2, 0xba, 0x77, 0x15, 0x15, 0x2d, 0x2c, 0x96, ++ 0xae, 0x47, 0xbb, 0x82, 0x89, 0x09, 0x7f, 0xd1, 0x95, 0x69, 0x9b, 0xfe, ++ 0xd7, 0x6f, 0x4e, 0x68, 0xf6, 0xe7, 0x5f, 0x54, 0xa1, 0x3a, 0xeb, 0xa4, ++ 0xbf, 0x7a, 0xb6, 0x7f, 0xaa, 0xd8, 0xd7, 0x99, 0xcb, 0xae, 0x88, 0x6d, ++ 0x7a, 0xf3, 0xfa, 0x9e, 0x44, 0x2f, 0x30, 0xa8, 0xe6, 0xb9, 0x75, 0xa0, ++ 0x82, 0xd6, 0xb0, 0xe3, 0x03, 0xb3, 0x12, 0xa3, 0xdc, 0xb9, 0x4d, 0x93, ++ 0xd4, 0x30, 0xea, 0xce, 0x96, 0x92, 0x07, 0xf8, 0xba, 0xe4, 0x0f, 0x41, ++ 0xe3, 0x04, 0xaa, 0x8c, 0x07, 0x1a, 0x34, 0x60, 0xfc, 0xc0, 0x05, 0xd2, ++ 0x5a, 0xa8, 0x66, 0xef, 0xe0, 0x94, 0xc5, 0x2f, 0x0f, 0xff, 0xdc, 0x70, ++ 0xfb, 0xe2, 0x9d, 0x61, 0x51, 0x25, 0x02, 0xff, 0x4b, 0x69, 0xfd, 0x66, ++ 0xb9, 0xeb, 0x0c, 0xc8, 0x50, 0xd3, 0xb1, 0x08, 0x1e, 0x09, 0x54, 0x87, ++ 0xe8, 0xa3, 0x4b, 0xef, 0x0c, 0x32, 0x0a, 0x6c, 0xec, 0x27, 0x22, 0xba, ++ 0x7f, 0xdc, 0x52, 0x27, 0x31, 0x14, 0x9a, 0xa8, 0xf7, 0xf9, 0xeb, 0xc8, ++ 0xb5, 0x8d, 0x12, 0xed, 0x94, 0xab, 0x3d, 0x9a, 0xfb, 0x4e, 0x04, 0x05, ++ 0xd2, 0x3c, 0x7c, 0x8a, 0xed, 0x46, 0x1b, 0x7c, 0xb5, 0x6c, 0x40, 0xb8, ++ 0xc1, 0xbf, 0xb0, 0xd2, 0x93, 0x8e, 0xa8, 0x0f, 0xde, 0x78, 0xf3, 0x8c, ++ 0xd8, 0x9f, 0xf8, 0xdc, 0xa1, 0x23, 0x20, 0x40, 0x17, 0xb4, 0xdb, 0xb7, ++ 0x09, 0x74, 0xa7, 0x80, 0xc2, 0x12, 0xd9, 0x76, 0x79, 0x5b, 0x71, 0xa9, ++ 0x6c, 0xd4, 0x57, 0x48, 0xe8, 0xfe, 0xc5, 0xc2, 0x6e, 0xe7, 0x83, 0x5a, ++ 0x07, 0xf0, 0x33, 0xc1, 0xc1, 0x1d, 0x34, 0xd4, 0xc8, 0xb0, 0xb7, 0xdb, ++ 0xeb, 0xe9, 0xe3, 0x59, 0xdc, 0x7f, 0x36, 0x58, 0xa9, 0xb8, 0x52, 0xdd, ++ 0xf9, 0xfd, 0x1c, 0x22, 0x2f, 0x93, 0x3d, 0x53, 0x89, 0x80, 0xde, 0xa2, ++ 0xb5, 0xa5, 0x36, 0xbd, 0xc3, 0x92, 0x03, 0xf3, 0x93, 0xc8, 0xc7, 0x4a, ++ 0x0b, 0x8b, 0x62, 0xfe, 0xd0, 0xf8, 0x0d, 0x7a, 0x32, 0xb4, 0x39, 0x1a, ++ 0xb7, 0x4e, 0xaa, 0xc4, 0x33, 0x32, 0x90, 0x8c, 0xab, 0xd4, 0xae, 0xa5, ++ 0xa4, 0x85, 0xcf, 0xba, 0xe1, 0x1b, 0x26, 0x7f, 0x74, 0x02, 0x12, 0x09, ++ 0x89, 0x56, 0xe4, 0xe7, 0x9d, 0x91, 0xde, 0x88, 0xe7, 0x1c, 0xed, 0x80, ++ 0x05, 0xa8, 0x58, 0x9a, 0x3e, 0x16, 0x97, 0xd5, 0xbc, 0x54, 0xcc, 0xf0, ++ 0x32, 0xf2, 0x93, 0x09, 0x94, 0x9f, 0x3c, 0xd9, 0x58, 0xca, 0x68, 0x0b, ++ 0xde, 0x3f, 0x73, 0x64, 0xb7, 0xf4, 0xd7, 0x5f, 0x2b, 0xe7, 0x7b, 0x06, ++ 0xca, 0xb1, 0x3e, 0xed, 0xd2, 0xb9, 0x29, 0xc1, 0x95, 0x87, 0xad, 0xd6, ++ 0x63, 0x69, 0xb8, 0x1f, 0x70, 0xdb, 0xeb, 0xc7, 0x11, 0x7d, 0xe2, 0x99, ++ 0x64, 0x6a, 0xf5, 0x3f, 0x30, 0x74, 0x5f, 0x2a, 0x21, 0xda, 0xef, 0x44, ++ 0x1d, 0xad, 0x97, 0xa1, 0xfe, 0x14, 0xa7, 0x88, 0x99, 0xd0, 0x1e, 0xb0, ++ 0x61, 0x88, 0x09, 0xc9, 0xfa, 0xd1, 0xb3, 0xcb, 0x1d, 0x76, 0x04, 0xbe, ++ 0x06, 0x44, 0xd2, 0x30, 0x5e, 0x95, 0x4b, 0x96, 0xc0, 0xd6, 0xbe, 0xd0, ++ 0x4d, 0xf2, 0xf4, 0x71, 0x72, 0xa9, 0xbd, 0x07, 0x4f, 0xbc, 0xb3, 0x78, ++ 0xb4, 0x8a, 0x44, 0xbd, 0x58, 0xd5, 0x21, 0xb6, 0x47, 0x9c, 0x88, 0x1f, ++ 0xbc, 0xbd, 0x54, 0xfa, 0x1d, 0x49, 0xec, 0x51, 0xd9, 0x43, 0x49, 0x9c, ++ 0x0c, 0xfa, 0x18, 0xdb, 0xeb, 0x05, 0x77, 0xa2, 0x9a ++}; ++unsigned int certificate_der_len = 1377; ++ ++unsigned char hi_signed[] = { ++ 0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48, ++ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82, ++ 0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, ++ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, ++ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02, ++ 0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, ++ 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, ++ 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, ++ 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, ++ 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, ++ 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, ++ 0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, ++ 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, ++ 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, ++ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, ++ 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, ++ 0x97, 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, ++ 0xdf, 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, ++ 0x77, 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, ++ 0x55, 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, ++ 0xc2, 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, ++ 0x88, 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, ++ 0xd1, 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, ++ 0xe3, 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, ++ 0x45, 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, ++ 0x86, 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, ++ 0xcd, 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, ++ 0x27, 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, ++ 0xf1, 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, ++ 0x1a, 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, ++ 0x8d, 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, ++ 0xc5, 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, ++ 0xa1, 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, ++ 0x04, 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, ++ 0x9a, 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, ++ 0x1c, 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, ++ 0xd6, 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, ++ 0xe9, 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, ++ 0x60, 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, ++ 0x83, 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, ++ 0xd7, 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, ++ 0x35, 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, ++ 0x0f, 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, ++ 0xa0, 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, ++ 0xaa, 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, ++ 0xe2, 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, ++ 0xc3, 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, ++ 0x24, 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, ++ 0xe6, 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, ++ 0x5e, 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, ++ 0x74, 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, ++ 0x3b, 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, ++ 0x8a, 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, ++ 0xdc, 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, ++ 0x83, 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, ++ 0x21, 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, ++ 0x51, 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, ++ 0x19, 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, ++ 0x35, 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, ++ 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, ++ 0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a ++}; ++unsigned int hi_signed_len = 739; ++ ++unsigned char hj_signed[] = { ++ 0x68, 0x6a, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48, ++ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82, ++ 0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, ++ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, ++ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02, ++ 0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, ++ 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, ++ 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, ++ 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, ++ 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, ++ 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, ++ 0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, ++ 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, ++ 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, ++ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, ++ 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, ++ 0x97, 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, ++ 0xdf, 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, ++ 0x77, 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, ++ 0x55, 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, ++ 0xc2, 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, ++ 0x88, 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, ++ 0xd1, 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, ++ 0xe3, 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, ++ 0x45, 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, ++ 0x86, 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, ++ 0xcd, 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, ++ 0x27, 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, ++ 0xf1, 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, ++ 0x1a, 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, ++ 0x8d, 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, ++ 0xc5, 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, ++ 0xa1, 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, ++ 0x04, 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, ++ 0x9a, 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, ++ 0x1c, 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, ++ 0xd6, 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, ++ 0xe9, 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, ++ 0x60, 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, ++ 0x83, 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, ++ 0xd7, 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, ++ 0x35, 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, ++ 0x0f, 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, ++ 0xa0, 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, ++ 0xaa, 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, ++ 0xe2, 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, ++ 0xc3, 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, ++ 0x24, 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, ++ 0xe6, 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, ++ 0x5e, 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, ++ 0x74, 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, ++ 0x3b, 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, ++ 0x8a, 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, ++ 0xdc, 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, ++ 0x83, 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, ++ 0x21, 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, ++ 0x51, 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, ++ 0x19, 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, ++ 0x35, 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, ++ 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, ++ 0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a ++}; ++unsigned int hj_signed_len = 739; ++ ++unsigned char hi_signed_sha256[] = { ++ 0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48, ++ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82, ++ 0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, ++ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0b, 0x06, 0x09, ++ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02, ++ 0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, ++ 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, ++ 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, ++ 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, ++ 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, ++ 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, ++ 0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, ++ 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, ++ 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, ++ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, ++ 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0x96, 0x02, 0x7b, 0xa4, 0x07, ++ 0xa7, 0x39, 0x8d, 0xa6, 0x0b, 0xde, 0x33, 0xdd, 0xf8, 0xec, 0x24, 0x5d, ++ 0x06, 0x81, 0xe7, 0x3c, 0x2d, 0x0c, 0x53, 0xfb, 0x7e, 0x5a, 0xf3, 0xee, ++ 0xe5, 0x4c, 0x7c, 0xf7, 0xe7, 0x8f, 0x36, 0x62, 0x35, 0xb8, 0x99, 0xc3, ++ 0xeb, 0x85, 0x1d, 0x2e, 0x40, 0x6e, 0x2a, 0xb4, 0x3a, 0x76, 0x48, 0x4f, ++ 0x8b, 0x29, 0xd4, 0x9e, 0x5c, 0xd2, 0x41, 0x4d, 0xc1, 0x72, 0x0f, 0x97, ++ 0xe0, 0x7d, 0x88, 0xed, 0x1a, 0xb0, 0xde, 0x1b, 0x21, 0xa6, 0x0c, 0x19, ++ 0xd8, 0xb0, 0x12, 0x54, 0x7b, 0xd8, 0x19, 0x03, 0xbd, 0x77, 0x83, 0x23, ++ 0xeb, 0xeb, 0x68, 0x0a, 0x7b, 0x3a, 0x4d, 0x25, 0x44, 0xe1, 0x64, 0x8d, ++ 0x43, 0x5a, 0x1c, 0x9f, 0x74, 0x79, 0x31, 0x3f, 0xc7, 0x8e, 0xae, 0xe1, ++ 0xf9, 0x1e, 0x54, 0x12, 0x36, 0x85, 0xf2, 0x55, 0xba, 0x42, 0x60, 0x64, ++ 0x25, 0x9f, 0x73, 0x62, 0x42, 0xd2, 0x1c, 0x5e, 0x39, 0x4f, 0x7d, 0x91, ++ 0xb8, 0xf9, 0x59, 0x3c, 0x13, 0x6b, 0x84, 0x76, 0x6d, 0x8a, 0xc3, 0xcb, ++ 0x2d, 0x14, 0x27, 0x16, 0xdc, 0x20, 0x2c, 0xbc, 0x6b, 0xc9, 0xda, 0x9f, ++ 0xef, 0xe2, 0x2d, 0xc3, 0x83, 0xd8, 0xf9, 0x94, 0x18, 0xbc, 0xfe, 0x8f, ++ 0xa9, 0x44, 0xad, 0xff, 0x1b, 0xcb, 0x86, 0x30, 0x96, 0xa8, 0x3c, 0x7a, ++ 0x4b, 0x73, 0x1b, 0xa9, 0xc3, 0x3b, 0xaa, 0xd7, 0x44, 0xa8, 0x4d, 0xd6, ++ 0x92, 0xb6, 0x00, 0x04, 0x09, 0x05, 0x4a, 0x95, 0x02, 0x90, 0x19, 0x8c, ++ 0x9a, 0xa5, 0xee, 0x58, 0x24, 0xb0, 0xca, 0x5e, 0x6f, 0x73, 0xdb, 0xf5, ++ 0xa1, 0xf4, 0xf0, 0xa9, 0xeb, 0xe4, 0xdc, 0x55, 0x9f, 0x8f, 0x7a, 0xd0, ++ 0xf7, 0xb6, 0xaa, 0xa6, 0xb5, 0xb4, 0xab, 0xb8, 0x65, 0xad, 0x12, 0x32, ++ 0x1c, 0xe6, 0x99, 0x71, 0x93, 0xe8, 0xb4, 0x1e, 0x21, 0x27, 0x52, 0xea, ++ 0x8c, 0xc8, 0x79, 0x96, 0x2e, 0x48, 0x60, 0x57, 0x1c, 0x7d, 0x8c, 0x0d, ++ 0x07, 0xa7, 0x12, 0x83, 0x0a, 0x76, 0x6a, 0x64, 0xed, 0xbe, 0x8d, 0xaf, ++ 0xdf, 0x51, 0x05, 0xdd, 0xf2, 0xd3, 0xb8, 0x93, 0xa9, 0x13, 0xa5, 0x96, ++ 0xe8, 0xfa, 0x82, 0x02, 0x18, 0x71, 0x7a, 0x71, 0xbb, 0x39, 0x6f, 0x85, ++ 0xee, 0x16, 0x82, 0x27, 0x42, 0x9f, 0x83, 0xc8, 0xab, 0x6a, 0x3b, 0x99, ++ 0xba, 0x38, 0x92, 0x38, 0xae, 0x59, 0xfa, 0xaa, 0x40, 0x2b, 0x52, 0x95, ++ 0xca, 0x5e, 0xe1, 0x9b, 0x00, 0xbd, 0xb9, 0x63, 0x25, 0x8d, 0xc7, 0x22, ++ 0xaf, 0xe5, 0x67, 0x76, 0x91, 0xf4, 0xda, 0xc9, 0x7e, 0x9e, 0xec, 0x9b, ++ 0x1f, 0x7d, 0x3b, 0xfe, 0xa1, 0x20, 0x52, 0xac, 0xd0, 0xe5, 0xa6, 0xf1, ++ 0xfd, 0x4c, 0x08, 0x59, 0x7d, 0x50, 0xbb, 0x0c, 0xcf, 0xd8, 0xb6, 0x0f, ++ 0xc7, 0x19, 0xcb, 0x7a, 0x96, 0x6f, 0x0f, 0x6c, 0x71, 0x56, 0x72, 0xd1, ++ 0x06, 0x29, 0x0f, 0x08, 0xa2, 0x46, 0x3e, 0x58, 0x42, 0xc4, 0x8c, 0xe0, ++ 0x6e, 0xe9, 0x37, 0xd5, 0x2f, 0x74, 0x36, 0x1d, 0x14, 0xcb, 0x10, 0x0e, ++ 0x7d, 0x67, 0xbd, 0x38, 0x0e, 0xa4, 0x27, 0x1d, 0x3c, 0x78, 0x4d, 0x0d, ++ 0x15, 0x42, 0x70, 0x20, 0xe0, 0x1d, 0x83, 0x6c, 0x4d, 0xf1, 0x02, 0xa1, ++ 0x51, 0xc4, 0xc5, 0x5d, 0x69, 0x90, 0x58, 0x82, 0x94, 0x50, 0x36, 0x22, ++ 0xb3, 0xa4, 0x15, 0x77, 0xdc, 0x44, 0xb0, 0x50, 0xa2, 0x3f, 0xd0, 0x0e, ++ 0x1b, 0xfc, 0xf4, 0x5b, 0x3b, 0x7d, 0x63, 0x94, 0x22, 0xf3, 0x87, 0x0a, ++ 0x41, 0x8a, 0x27, 0x48, 0xcb, 0x6c, 0xfd, 0x70, 0x66, 0x5f, 0x11, 0x6f, ++ 0x74, 0x2c, 0x42, 0xaf, 0x74, 0x45, 0x3f, 0x0c, 0x03, 0xc8, 0x80, 0xe2, ++ 0x71, 0x08, 0x93, 0xbd, 0x4d, 0x18, 0x78, 0x1e, 0x8e, 0xb9, 0x3a, 0xd6, ++ 0x1a, 0xde, 0xf9, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, ++ 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, ++ 0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a ++}; ++unsigned int hi_signed_sha256_len = 739; ++ ++unsigned char short_msg[] = { ++ 0x68, 0x69, 0x0a ++}; ++unsigned int short_msg_len = 3; ++ ++unsigned char unsigned_msg[] = { ++ 0x53, 0x65, 0x64, 0x20, 0x75, 0x74, 0x20, 0x70, 0x65, 0x72, 0x73, 0x70, ++ 0x69, 0x63, 0x69, 0x61, 0x74, 0x69, 0x73, 0x20, 0x75, 0x6e, 0x64, 0x65, ++ 0x20, 0x6f, 0x6d, 0x6e, 0x69, 0x73, 0x20, 0x69, 0x73, 0x74, 0x65, 0x20, ++ 0x6e, 0x61, 0x74, 0x75, 0x73, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, ++ 0x73, 0x69, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, ++ 0x65, 0x6d, 0x20, 0x61, 0x63, 0x63, 0x75, 0x73, 0x61, 0x6e, 0x74, 0x69, ++ 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x71, 0x75, ++ 0x65, 0x20, 0x6c, 0x61, 0x75, 0x64, 0x61, 0x6e, 0x74, 0x69, 0x75, 0x6d, ++ 0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6d, 0x20, 0x72, 0x65, 0x6d, 0x20, ++ 0x61, 0x70, 0x65, 0x72, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x65, 0x61, 0x71, ++ 0x75, 0x65, 0x20, 0x69, 0x70, 0x73, 0x61, 0x20, 0x71, 0x75, 0x61, 0x65, ++ 0x20, 0x61, 0x62, 0x20, 0x69, 0x6c, 0x6c, 0x6f, 0x20, 0x69, 0x6e, 0x76, ++ 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, 0x69, 0x74, ++ 0x61, 0x74, 0x69, 0x73, 0x20, 0x65, 0x74, 0x20, 0x71, 0x75, 0x61, 0x73, ++ 0x69, 0x20, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x6f, ++ 0x20, 0x62, 0x65, 0x61, 0x74, 0x61, 0x65, 0x20, 0x76, 0x69, 0x74, 0x61, ++ 0x65, 0x20, 0x64, 0x69, 0x63, 0x74, 0x61, 0x20, 0x73, 0x75, 0x6e, 0x74, ++ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62, 0x6f, 0x2e, 0x20, ++ 0x4e, 0x65, 0x6d, 0x6f, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, 0x69, 0x70, ++ 0x73, 0x61, 0x6d, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, ++ 0x65, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, ++ 0x70, 0x74, 0x61, 0x73, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x73, 0x70, ++ 0x65, 0x72, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x20, 0x61, 0x75, 0x74, 0x20, ++ 0x6f, 0x64, 0x69, 0x74, 0x20, 0x61, 0x75, 0x74, 0x20, 0x66, 0x75, 0x67, ++ 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, 0x20, 0x71, 0x75, 0x69, 0x61, ++ 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x75, 0x6e, 0x74, 0x75, ++ 0x72, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, ++ 0x72, 0x65, 0x73, 0x20, 0x65, 0x6f, 0x73, 0x20, 0x71, 0x75, 0x69, 0x20, ++ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x20, 0x76, 0x6f, 0x6c, 0x75, ++ 0x70, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x20, 0x73, 0x65, 0x71, 0x75, 0x69, ++ 0x20, 0x6e, 0x65, 0x73, 0x63, 0x69, 0x75, 0x6e, 0x74, 0x2e, 0x20, 0x4e, ++ 0x65, 0x71, 0x75, 0x65, 0x20, 0x70, 0x6f, 0x72, 0x72, 0x6f, 0x20, 0x71, ++ 0x75, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x73, 0x74, 0x2c, ++ 0x20, 0x71, 0x75, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, ++ 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, ++ 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, ++ 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, ++ 0x74, 0x75, 0x72, 0x2c, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63, ++ 0x69, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, ++ 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e, 0x75, ++ 0x6d, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x69, 0x75, 0x73, 0x20, 0x6d, ++ 0x6f, 0x64, 0x69, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x20, ++ 0x69, 0x6e, 0x63, 0x69, 0x64, 0x75, 0x6e, 0x74, 0x20, 0x75, 0x74, 0x20, ++ 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x65, 0x20, 0x65, 0x74, 0x20, 0x64, 0x6f, ++ 0x6c, 0x6f, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x61, 0x6d, 0x20, ++ 0x61, 0x6c, 0x69, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x71, 0x75, 0x61, 0x65, ++ 0x72, 0x61, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, ++ 0x65, 0x6d, 0x2e, 0x20, 0x55, 0x74, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, ++ 0x61, 0x64, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x20, 0x76, 0x65, ++ 0x6e, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x71, 0x75, 0x69, 0x73, 0x20, 0x6e, ++ 0x6f, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x20, 0x65, 0x78, 0x65, 0x72, 0x63, ++ 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x6d, 0x20, 0x75, 0x6c, ++ 0x6c, 0x61, 0x6d, 0x20, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x69, 0x73, ++ 0x20, 0x73, 0x75, 0x73, 0x63, 0x69, 0x70, 0x69, 0x74, 0x20, 0x6c, 0x61, ++ 0x62, 0x6f, 0x72, 0x69, 0x6f, 0x73, 0x61, 0x6d, 0x2c, 0x20, 0x6e, 0x69, ++ 0x73, 0x69, 0x20, 0x75, 0x74, 0x20, 0x61, 0x6c, 0x69, 0x71, 0x75, 0x69, ++ 0x64, 0x20, 0x65, 0x78, 0x20, 0x65, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, ++ 0x6f, 0x64, 0x69, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, ++ 0x74, 0x75, 0x72, 0x3f, 0x20, 0x51, 0x75, 0x69, 0x73, 0x20, 0x61, 0x75, ++ 0x74, 0x65, 0x6d, 0x20, 0x76, 0x65, 0x6c, 0x20, 0x65, 0x75, 0x6d, 0x20, ++ 0x69, 0x75, 0x72, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x68, 0x65, ++ 0x6e, 0x64, 0x65, 0x72, 0x69, 0x74, 0x20, 0x71, 0x75, 0x69, 0x20, 0x69, ++ 0x6e, 0x20, 0x65, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, ++ 0x74, 0x65, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x20, 0x65, 0x73, 0x73, ++ 0x65, 0x20, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x6e, 0x69, 0x68, 0x69, 0x6c, ++ 0x20, 0x6d, 0x6f, 0x6c, 0x65, 0x73, 0x74, 0x69, 0x61, 0x65, 0x20, 0x63, ++ 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, 0x74, 0x75, 0x72, 0x2c, 0x20, ++ 0x76, 0x65, 0x6c, 0x20, 0x69, 0x6c, 0x6c, 0x75, 0x6d, 0x20, 0x71, 0x75, ++ 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x65, 0x75, ++ 0x6d, 0x20, 0x66, 0x75, 0x67, 0x69, 0x61, 0x74, 0x20, 0x71, 0x75, 0x6f, ++ 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x73, 0x20, 0x6e, 0x75, ++ 0x6c, 0x6c, 0x61, 0x20, 0x70, 0x61, 0x72, 0x69, 0x61, 0x74, 0x75, 0x72, ++ 0x3f, 0x0a ++}; ++unsigned int unsigned_msg_len = 866; ++ ++unsigned char certificate2_der[] = { ++ 0x30, 0x82, 0x05, 0x52, 0x30, 0x82, 0x03, 0x3a, 0xa0, 0x03, 0x02, 0x01, ++ 0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, ++ 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, ++ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, ++ 0x05, 0x00, 0x30, 0x3a, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, ++ 0x03, 0x0c, 0x2f, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, ++ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, ++ 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, ++ 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, ++ 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x37, 0x32, 0x38, ++ 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32, ++ 0x30, 0x30, 0x37, 0x30, 0x34, 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a, ++ 0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, ++ 0x20, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, ++ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, ++ 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02, ++ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, ++ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, ++ 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xb0, 0x2f, 0x50, 0x01, 0x9c, 0x0e, ++ 0xd6, 0x8c, 0x07, 0xca, 0xc1, 0xcf, 0xbc, 0x03, 0xdd, 0xd3, 0xfa, 0xe3, ++ 0x4f, 0x71, 0xc1, 0x30, 0xaa, 0x09, 0x96, 0xe4, 0xd0, 0x6c, 0x42, 0x93, ++ 0xdb, 0x35, 0xf6, 0x7e, 0x1b, 0x67, 0xc0, 0xc2, 0x2d, 0x5b, 0xec, 0xca, ++ 0x35, 0x06, 0x32, 0x6c, 0x7b, 0x2c, 0xd3, 0x71, 0x2b, 0xe9, 0x7a, 0x19, ++ 0xd1, 0xf2, 0xa0, 0x7f, 0xd7, 0x4d, 0x6e, 0x28, 0xbb, 0xae, 0x49, 0x4a, ++ 0xbc, 0xea, 0x47, 0x67, 0xb8, 0x36, 0xa6, 0xf5, 0x0d, 0x0e, 0x20, 0x14, ++ 0x0c, 0x66, 0x67, 0x28, 0xb5, 0x97, 0x8b, 0x1f, 0x5e, 0x32, 0x06, 0x29, ++ 0x9c, 0x99, 0x92, 0x0f, 0x73, 0xac, 0xfd, 0xd2, 0x1d, 0xf2, 0xa8, 0x55, ++ 0x9d, 0x1b, 0xd8, 0x3d, 0xb0, 0x76, 0x9a, 0xb6, 0x6c, 0x9f, 0x62, 0x37, ++ 0x2f, 0xc0, 0xef, 0x44, 0xb3, 0x0d, 0x4a, 0x3e, 0x4f, 0x7d, 0xbd, 0xdb, ++ 0xd8, 0x75, 0x5f, 0x68, 0xe3, 0xf0, 0xec, 0x82, 0x66, 0x7c, 0x31, 0x70, ++ 0xa9, 0xa1, 0x6f, 0x38, 0x9f, 0xdf, 0xf5, 0xf0, 0x7d, 0x23, 0x9d, 0x34, ++ 0xa5, 0x85, 0xd3, 0xdf, 0x68, 0x41, 0xfc, 0x4f, 0x89, 0x45, 0x3c, 0x24, ++ 0x81, 0xa6, 0xf2, 0x3c, 0x02, 0x26, 0x09, 0x48, 0xdd, 0xfe, 0x4b, 0xb6, ++ 0x66, 0xbf, 0x8f, 0xe5, 0x5f, 0xf0, 0x5d, 0x8a, 0x61, 0x2e, 0x5f, 0x9f, ++ 0x80, 0xd9, 0xd5, 0xe6, 0x41, 0xd8, 0x10, 0x5e, 0x7a, 0xc6, 0xdb, 0x89, ++ 0xc7, 0xca, 0x6c, 0x5b, 0xb1, 0x4e, 0x7d, 0x0c, 0x03, 0xfd, 0x50, 0xca, ++ 0xbf, 0xbb, 0xe2, 0x69, 0x4b, 0x4e, 0xc2, 0x3d, 0x75, 0xfa, 0xd1, 0xcc, ++ 0xd6, 0xf9, 0x39, 0xb9, 0xdc, 0x53, 0xad, 0x62, 0xfb, 0x1b, 0x94, 0x26, ++ 0x7f, 0x21, 0x54, 0x5c, 0xb7, 0xdc, 0xe7, 0x96, 0x8c, 0xce, 0x75, 0xe0, ++ 0x17, 0x01, 0x3a, 0x3c, 0x77, 0x6e, 0xa4, 0x8b, 0x7a, 0x83, 0x28, 0x7a, ++ 0xf7, 0xb0, 0x5f, 0xfc, 0x7f, 0x2d, 0x2e, 0xec, 0xf5, 0xeb, 0x9c, 0x63, ++ 0x74, 0xd0, 0xe5, 0xdc, 0x19, 0xe4, 0x71, 0xc5, 0x4a, 0x8a, 0x54, 0xa4, ++ 0xe0, 0x7d, 0x4e, 0xbf, 0x53, 0x30, 0xaf, 0xd0, 0xeb, 0x96, 0xc3, 0xbb, ++ 0x65, 0xf7, 0x67, 0xf5, 0xae, 0xd3, 0x96, 0xf2, 0x63, 0xc8, 0x69, 0xf7, ++ 0x47, 0xcb, 0x27, 0x79, 0xe1, 0xff, 0x2f, 0x68, 0xdf, 0x1e, 0xb3, 0xb8, ++ 0x0c, 0xc5, 0x58, 0x73, 0xcc, 0xfe, 0x8c, 0xda, 0x4e, 0x3b, 0x01, 0x04, ++ 0xcd, 0xcb, 0xb8, 0x3e, 0x06, 0xfd, 0x4c, 0x0a, 0x9f, 0x5e, 0x76, 0x8c, ++ 0x0c, 0x83, 0x75, 0x09, 0x08, 0xb2, 0xdb, 0xf4, 0x49, 0x4e, 0xa0, 0xf2, ++ 0x0c, 0x7b, 0x87, 0x38, 0x9e, 0x22, 0x67, 0xbd, 0xd1, 0x97, 0x57, 0x24, ++ 0xf1, 0x46, 0x07, 0xf9, 0xd2, 0x1b, 0xec, 0x25, 0x5e, 0x67, 0xd9, 0x66, ++ 0x23, 0x1b, 0xd3, 0xe4, 0xaa, 0xec, 0x88, 0xf0, 0x7e, 0x15, 0x83, 0x51, ++ 0x31, 0x67, 0x51, 0x76, 0x5f, 0x55, 0xd7, 0x36, 0xdf, 0x4a, 0x84, 0x0b, ++ 0x6f, 0x5c, 0xbb, 0x5b, 0x8f, 0x37, 0x23, 0x7f, 0xf8, 0x17, 0x84, 0xa2, ++ 0x70, 0x20, 0x07, 0x0c, 0x90, 0x3a, 0x04, 0xfd, 0xf0, 0x08, 0x4a, 0xb1, ++ 0x16, 0x0f, 0xe6, 0xf6, 0x40, 0x51, 0x83, 0xd2, 0x87, 0x40, 0x9c, 0x1c, ++ 0x9f, 0x13, 0x38, 0x17, 0xd3, 0x34, 0x58, 0xad, 0x05, 0x71, 0xa0, 0x73, ++ 0xca, 0x40, 0xa6, 0xa4, 0x81, 0x02, 0xee, 0xa8, 0x72, 0x41, 0xa1, 0x41, ++ 0x18, 0x64, 0x8a, 0x86, 0x8a, 0x5d, 0xe6, 0x4f, 0x0a, 0xc5, 0x95, 0x98, ++ 0xf9, 0x78, 0xfe, 0x19, 0x0d, 0xc9, 0xb3, 0x89, 0xc1, 0x2b, 0x09, 0xbe, ++ 0xf1, 0xd2, 0x04, 0x5d, 0xcc, 0x28, 0xf5, 0x4b, 0xd2, 0x20, 0x4f, 0xc5, ++ 0x41, 0x9d, 0x8c, 0x85, 0xd8, 0xb0, 0x68, 0x5e, 0xc1, 0x0c, 0xb7, 0x24, ++ 0x4d, 0x67, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, ++ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, ++ 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, ++ 0x07, 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, ++ 0x14, 0xac, 0xf5, 0x47, 0x17, 0xd9, 0x7d, 0xc1, 0xb1, 0xc4, 0x41, 0xe1, ++ 0x41, 0x60, 0xcb, 0x37, 0x11, 0x60, 0x28, 0x78, 0x5f, 0x30, 0x1f, 0x06, ++ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x21, 0x94, ++ 0xfb, 0xf9, 0xb2, 0x43, 0xe9, 0x33, 0xd7, 0x50, 0x7d, 0xc7, 0x37, 0xdb, ++ 0xd5, 0x82, 0x5a, 0x4e, 0xbe, 0x1b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, ++ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, ++ 0x01, 0x00, 0x96, 0x70, 0x65, 0x26, 0x42, 0xf8, 0xdc, 0x69, 0xde, 0xcf, ++ 0x41, 0x3a, 0x2e, 0x7f, 0x5b, 0xf1, 0xf9, 0x3b, 0x9b, 0xd2, 0x4e, 0x64, ++ 0x48, 0x81, 0xe4, 0x5d, 0x1e, 0x22, 0xce, 0x68, 0x63, 0x62, 0xe5, 0x1b, ++ 0x9b, 0xf2, 0xc7, 0x12, 0xda, 0x1e, 0x9b, 0x90, 0x84, 0x79, 0x48, 0x12, ++ 0xe6, 0x21, 0x6f, 0x2f, 0x7e, 0x18, 0x77, 0xdb, 0x8c, 0xc4, 0xd1, 0x0d, ++ 0x91, 0xbf, 0x39, 0x22, 0x0f, 0x64, 0xcf, 0x25, 0x2e, 0x8c, 0x1f, 0x91, ++ 0x81, 0xb5, 0xe9, 0x6c, 0x02, 0x3a, 0xf8, 0x07, 0xa2, 0x6f, 0x46, 0x5d, ++ 0x7b, 0xfd, 0x43, 0xff, 0x41, 0x0f, 0xe2, 0x57, 0x1c, 0xbd, 0x48, 0x60, ++ 0x53, 0x11, 0x48, 0x87, 0x88, 0x9d, 0x13, 0x82, 0x40, 0x68, 0x44, 0x2c, ++ 0xc6, 0xc8, 0x95, 0x27, 0x4f, 0xb6, 0xb9, 0x4a, 0x22, 0x0a, 0xfd, 0xe4, ++ 0x46, 0x8f, 0x35, 0x12, 0x98, 0x5a, 0x34, 0x6f, 0x2b, 0x57, 0x62, 0xa1, ++ 0x4d, 0x8d, 0x79, 0x37, 0xe4, 0x6b, 0x8a, 0x32, 0x5b, 0xcb, 0xef, 0x79, ++ 0x11, 0xed, 0xa7, 0xf8, 0x7a, 0x1c, 0xbd, 0x86, 0xdc, 0x0e, 0x2e, 0xfd, ++ 0xd3, 0x51, 0xbb, 0x73, 0xad, 0x00, 0xa0, 0x1b, 0xf9, 0x1d, 0xd1, 0x4a, ++ 0xe4, 0xd4, 0x02, 0x63, 0x2b, 0x39, 0x5f, 0x18, 0x08, 0x2f, 0x42, 0xb7, ++ 0x23, 0x4b, 0x48, 0x46, 0x1f, 0x63, 0x87, 0xae, 0x6d, 0xd5, 0xdb, 0x60, ++ 0xf8, 0x5f, 0xd3, 0x13, 0xec, 0xca, 0xdd, 0x60, 0x60, 0x79, 0x52, 0x70, ++ 0x47, 0xae, 0x1d, 0x38, 0x78, 0x71, 0xcf, 0xb3, 0x04, 0x03, 0xbe, 0xba, ++ 0x81, 0xba, 0x74, 0xb1, 0x30, 0x35, 0xdc, 0xea, 0x21, 0x4a, 0x9b, 0x70, ++ 0xfb, 0xd6, 0x60, 0x59, 0x78, 0x0c, 0x4d, 0x39, 0x19, 0x1d, 0xe5, 0x75, ++ 0xba, 0x07, 0xf4, 0x22, 0x37, 0x64, 0xb7, 0xf2, 0x9a, 0xc9, 0x11, 0x2d, ++ 0x8e, 0x58, 0xa6, 0xcf, 0x83, 0xf1, 0xcb, 0x6c, 0x7f, 0x02, 0xbd, 0xda, ++ 0x03, 0x92, 0xa9, 0x45, 0x24, 0x56, 0xc5, 0xbd, 0x41, 0xd1, 0x20, 0x86, ++ 0xc0, 0xb6, 0xb7, 0xe8, 0xa7, 0xb2, 0x46, 0xf7, 0x8e, 0xa9, 0x38, 0x0e, ++ 0x23, 0x77, 0x3c, 0x0d, 0x66, 0x83, 0x6a, 0x1a, 0x6b, 0x7f, 0x54, 0x11, ++ 0x58, 0x0d, 0x4a, 0xb5, 0x74, 0x60, 0xca, 0xed, 0xff, 0x91, 0x47, 0xd9, ++ 0x29, 0xe0, 0xaa, 0x8c, 0xa8, 0x8f, 0x10, 0x4c, 0x15, 0x7d, 0xce, 0x95, ++ 0xf9, 0x87, 0x1e, 0x18, 0x38, 0x18, 0xfc, 0xcc, 0xaf, 0x91, 0x17, 0x3f, ++ 0xfa, 0xf0, 0x8a, 0x09, 0x6f, 0xba, 0x4e, 0x53, 0xf7, 0xfa, 0x4f, 0x20, ++ 0xa3, 0xf4, 0x4a, 0x5a, 0xde, 0x17, 0x1c, 0x29, 0x6a, 0x6f, 0x03, 0x48, ++ 0xdf, 0xad, 0x4f, 0xe4, 0xbc, 0x71, 0xc4, 0x72, 0x32, 0x11, 0x84, 0xac, ++ 0x09, 0xd2, 0x18, 0x44, 0x35, 0xf1, 0xcd, 0xaf, 0xa8, 0x98, 0xe0, 0x8b, ++ 0xec, 0xa0, 0x83, 0x37, 0xc3, 0x35, 0x85, 0xd6, 0xd8, 0x1b, 0xe0, 0x75, ++ 0xdc, 0xfd, 0xde, 0xc9, 0xeb, 0xd5, 0x18, 0x0f, 0xd3, 0x4c, 0x2f, 0x71, ++ 0xdc, 0x48, 0xe3, 0x14, 0xeb, 0xda, 0x00, 0x24, 0x24, 0x9e, 0xa3, 0x8e, ++ 0x3e, 0x08, 0x6f, 0x22, 0x24, 0xd6, 0xc4, 0x85, 0x8f, 0x68, 0x00, 0x4a, ++ 0x82, 0x4c, 0x33, 0x6e, 0xa5, 0x35, 0x7b, 0xeb, 0x4b, 0xdc, 0xa0, 0xa6, ++ 0x65, 0x6f, 0x5a, 0x7a, 0xdf, 0x8a, 0x01, 0x52, 0xa1, 0x6c, 0xff, 0x59, ++ 0x22, 0x7f, 0xe1, 0x96, 0x1b, 0x19, 0xb8, 0xf9, 0x5d, 0x44, 0x9f, 0x91, ++ 0x03, 0x3c, 0x3d, 0xa1, 0x2a, 0xb6, 0x5a, 0x51, 0xa0, 0xce, 0x4a, 0x88, ++ 0x22, 0x72, 0x9c, 0xdc, 0xc0, 0x47, 0x76, 0x35, 0x84, 0x75, 0x9b, 0x87, ++ 0x5c, 0xd3, 0xcf, 0xe7, 0xdd, 0xa3, 0x57, 0x14, 0xdf, 0x00, 0xfd, 0x19, ++ 0x2a, 0x7d, 0x89, 0x27, 0x1c, 0x78, 0x97, 0x04, 0x58, 0x48 ++}; ++unsigned int certificate2_der_len = 1366; ++ ++unsigned char hi_signed_2nd[] = { ++ 0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb1, 0x06, 0x09, 0x2a, 0x86, 0x48, ++ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa2, 0x30, 0x82, ++ 0x02, 0x9e, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, ++ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, ++ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02, ++ 0x7b, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a, ++ 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47, ++ 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74, ++ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, ++ 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, ++ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14, ++ 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07, ++ 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09, ++ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06, ++ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, ++ 0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90, ++ 0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99, ++ 0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07, ++ 0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe, ++ 0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f, ++ 0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f, ++ 0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f, ++ 0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53, ++ 0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2, ++ 0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0, ++ 0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf, ++ 0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05, ++ 0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d, ++ 0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d, ++ 0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81, ++ 0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36, ++ 0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5, ++ 0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc, ++ 0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70, ++ 0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94, ++ 0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3, ++ 0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20, ++ 0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2, ++ 0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac, ++ 0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb, ++ 0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49, ++ 0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0, ++ 0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb, ++ 0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76, ++ 0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5, ++ 0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49, ++ 0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b, ++ 0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25, ++ 0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e, ++ 0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0, ++ 0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98, ++ 0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b, ++ 0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83, ++ 0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e, ++ 0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a, ++ 0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7, ++ 0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26, ++ 0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac, ++ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xb5, ++ 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, ++ 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, ++ 0x65, 0x64, 0x7e, 0x0a ++}; ++unsigned int hi_signed_2nd_len = 736; ++ ++unsigned char hi_double[] = { ++ 0x68, 0x69, 0x0a, 0x30, 0x82, 0x05, 0x2f, 0x06, 0x09, 0x2a, 0x86, 0x48, ++ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x05, 0x20, 0x30, 0x82, ++ 0x05, 0x1c, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, ++ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, ++ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x04, ++ 0xf9, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a, ++ 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47, ++ 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74, ++ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, ++ 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, ++ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14, ++ 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07, ++ 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09, ++ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06, ++ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, ++ 0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90, ++ 0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99, ++ 0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07, ++ 0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe, ++ 0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f, ++ 0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f, ++ 0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f, ++ 0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53, ++ 0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2, ++ 0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0, ++ 0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf, ++ 0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05, ++ 0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d, ++ 0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d, ++ 0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81, ++ 0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36, ++ 0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5, ++ 0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc, ++ 0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70, ++ 0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94, ++ 0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3, ++ 0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20, ++ 0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2, ++ 0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac, ++ 0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb, ++ 0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49, ++ 0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0, ++ 0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb, ++ 0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76, ++ 0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5, ++ 0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49, ++ 0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b, ++ 0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25, ++ 0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e, ++ 0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0, ++ 0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98, ++ 0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b, ++ 0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83, ++ 0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e, ++ 0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a, ++ 0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7, ++ 0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26, ++ 0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac, ++ 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, 0x31, ++ 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, 0x72, ++ 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, ++ 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x65, ++ 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, ++ 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, ++ 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, ++ 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, 0x0b, ++ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, ++ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, ++ 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, 0x97, ++ 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, 0xdf, ++ 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, 0x77, ++ 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, 0x55, ++ 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, 0xc2, ++ 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, 0x88, ++ 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, 0xd1, ++ 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, 0xe3, ++ 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, 0x45, ++ 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, 0x86, ++ 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, 0xcd, ++ 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, 0x27, ++ 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, 0xf1, ++ 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, 0x1a, ++ 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, 0x8d, ++ 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, 0xc5, ++ 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, 0xa1, ++ 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, 0x04, ++ 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, 0x9a, ++ 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, 0x1c, ++ 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, 0xd6, ++ 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, 0xe9, ++ 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, 0x60, ++ 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, 0x83, ++ 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, 0xd7, ++ 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, 0x35, ++ 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, 0x0f, ++ 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, 0xa0, ++ 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, 0xaa, ++ 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, 0xe2, ++ 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, 0xc3, ++ 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, 0x24, ++ 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, 0xe6, ++ 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, 0x5e, ++ 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, 0x74, ++ 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, 0x3b, ++ 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, 0x8a, ++ 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, 0xdc, ++ 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, 0x83, ++ 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, 0x21, ++ 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, 0x51, ++ 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, 0x19, ++ 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, 0x35, ++ 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x05, 0x33, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, ++ 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, ++ 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a ++}; ++unsigned int hi_double_len = 1374; ++ ++unsigned char hi_double_extended[] = { ++ 0x68, 0x69, 0x0a, 0x30, 0x82, 0x05, 0x2f, 0x06, 0x09, 0x2a, 0x86, 0x48, ++ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x05, 0x20, 0x30, 0x82, ++ 0x05, 0x1c, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, ++ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, ++ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x04, ++ 0xf9, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a, ++ 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47, ++ 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74, ++ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, ++ 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, ++ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14, ++ 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07, ++ 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09, ++ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06, ++ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, ++ 0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90, ++ 0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99, ++ 0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07, ++ 0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe, ++ 0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f, ++ 0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f, ++ 0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f, ++ 0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53, ++ 0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2, ++ 0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0, ++ 0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf, ++ 0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05, ++ 0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d, ++ 0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d, ++ 0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81, ++ 0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36, ++ 0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5, ++ 0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc, ++ 0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70, ++ 0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94, ++ 0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3, ++ 0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20, ++ 0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2, ++ 0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac, ++ 0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb, ++ 0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49, ++ 0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0, ++ 0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb, ++ 0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76, ++ 0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5, ++ 0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49, ++ 0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b, ++ 0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25, ++ 0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e, ++ 0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0, ++ 0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98, ++ 0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b, ++ 0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83, ++ 0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e, ++ 0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a, ++ 0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7, ++ 0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26, ++ 0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac, ++ 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, 0x31, ++ 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, 0x72, ++ 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, ++ 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x65, ++ 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, ++ 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, ++ 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, ++ 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, 0x0b, ++ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, ++ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, ++ 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, 0x97, ++ 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, 0xdf, ++ 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, 0x77, ++ 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, 0x55, ++ 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, 0xc2, ++ 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, 0x88, ++ 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, 0xd1, ++ 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, 0xe3, ++ 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, 0x45, ++ 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, 0x86, ++ 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, 0xcd, ++ 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, 0x27, ++ 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, 0xf1, ++ 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, 0x1a, ++ 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, 0x8d, ++ 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, 0xc5, ++ 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, 0xa1, ++ 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, 0x04, ++ 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, 0x9a, ++ 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, 0x1c, ++ 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, 0xd6, ++ 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, 0xe9, ++ 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, 0x60, ++ 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, 0x83, ++ 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, 0xd7, ++ 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, 0x35, ++ 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, 0x0f, ++ 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, 0xa0, ++ 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, 0xaa, ++ 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, 0xe2, ++ 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, 0xc3, ++ 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, 0x24, ++ 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, 0xe6, ++ 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, 0x5e, ++ 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, 0x74, ++ 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, 0x3b, ++ 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, 0x8a, ++ 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, 0xdc, ++ 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, 0x83, ++ 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, 0x21, ++ 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, 0x51, ++ 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, 0x19, ++ 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, 0x35, ++ 0x23, 0x2d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x05, 0x34, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, ++ 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, ++ 0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a ++}; ++unsigned int hi_double_extended_len = 1375; ++ ++unsigned char certificate_printable_der[] = { ++ 0x30, 0x82, 0x03, 0x39, 0x30, 0x82, 0x02, 0x21, 0xa0, 0x03, 0x02, 0x01, ++ 0x02, 0x02, 0x09, 0x00, 0xde, 0xf6, 0x22, 0xc4, 0xf2, 0xf1, 0x86, 0x02, ++ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, ++ 0x0b, 0x05, 0x00, 0x30, 0x2a, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, ++ 0x04, 0x03, 0x13, 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, ++ 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, ++ 0x43, 0x41, 0x20, 0x32, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30, ++ 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x33, 0x31, 0x31, 0x34, 0x31, ++ 0x39, 0x32, 0x33, 0x5a, 0x17, 0x0d, 0x33, 0x37, 0x31, 0x30, 0x32, 0x35, ++ 0x31, 0x34, 0x31, 0x39, 0x32, 0x33, 0x5a, 0x30, 0x2f, 0x31, 0x2d, 0x30, ++ 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x52, 0x65, 0x64, 0x20, ++ 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, ++ 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, ++ 0x33, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30, 0x82, 0x01, 0x22, ++ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, ++ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, ++ 0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0xda, 0xa1, 0xed, 0x8d, 0x8e, 0x15, ++ 0x5c, 0xf8, 0x01, 0x77, 0x48, 0x4a, 0x60, 0x96, 0xf9, 0x27, 0xfa, 0xe2, ++ 0xb1, 0x69, 0x0f, 0x51, 0x19, 0x52, 0x7e, 0xc4, 0x34, 0x8e, 0xe1, 0x9b, ++ 0x9c, 0xa4, 0xb1, 0x5c, 0xd6, 0x81, 0x98, 0x78, 0xfe, 0xa9, 0xe5, 0x0b, ++ 0x00, 0xba, 0x9c, 0x64, 0x7e, 0xc7, 0xcc, 0x72, 0xb1, 0x73, 0x4b, 0x11, ++ 0x07, 0x52, 0xf0, 0x20, 0x96, 0x8b, 0x99, 0x39, 0xde, 0xdb, 0xfa, 0x3d, ++ 0x45, 0xe2, 0x98, 0x7b, 0x0c, 0x41, 0xe4, 0x0c, 0xb5, 0x5d, 0x92, 0x74, ++ 0x39, 0x96, 0xe1, 0x97, 0x97, 0xa1, 0xad, 0x2e, 0xcc, 0xd0, 0x1b, 0x4d, ++ 0x9d, 0xbd, 0x3e, 0xa9, 0x36, 0x8e, 0xcc, 0xc7, 0x5f, 0x6a, 0x7d, 0x39, ++ 0x5e, 0x0b, 0x8d, 0xca, 0xe4, 0x83, 0xe9, 0x3b, 0x5c, 0x86, 0x47, 0xd4, ++ 0xba, 0x7d, 0x98, 0x26, 0xa1, 0xf4, 0xe8, 0x90, 0x6b, 0x0f, 0xf1, 0x6b, ++ 0x8c, 0xe3, 0xa2, 0x80, 0x3c, 0x96, 0xf1, 0x0a, 0xb6, 0x66, 0xc0, 0x4b, ++ 0x61, 0xf7, 0x74, 0xcd, 0xd3, 0x7b, 0x8e, 0x5e, 0x39, 0xda, 0x99, 0x20, ++ 0x33, 0x93, 0xd3, 0xf0, 0x7f, 0xad, 0x35, 0xe9, 0x88, 0x8d, 0x9c, 0xbf, ++ 0x65, 0xf1, 0x47, 0x02, 0xf9, 0x7c, 0xed, 0x27, 0x5f, 0x4a, 0x65, 0x3c, ++ 0xcf, 0x5f, 0x0e, 0x88, 0x95, 0x74, 0xde, 0xfb, 0x9e, 0x2e, 0x91, 0x9b, ++ 0x45, 0x37, 0xc8, 0x85, 0xff, 0xe3, 0x41, 0x70, 0xfe, 0xd5, 0xef, 0x0e, ++ 0x82, 0x22, 0x08, 0xb7, 0x3b, 0x44, 0x3e, 0xdc, 0x5b, 0x7f, 0xba, 0xbf, ++ 0xe6, 0x58, 0x9d, 0x02, 0x6e, 0x75, 0xbf, 0x50, 0xec, 0xcf, 0x3f, 0xa5, ++ 0x91, 0x0a, 0xe2, 0x59, 0x2c, 0xc3, 0xe7, 0x05, 0x03, 0xe8, 0xf2, 0x6f, ++ 0x2a, 0x04, 0x68, 0x9a, 0x31, 0x32, 0x8f, 0x04, 0x35, 0xcd, 0x1f, 0x34, ++ 0xcc, 0x4f, 0x79, 0x5a, 0x99, 0x8d, 0x9d, 0x5c, 0xf5, 0x02, 0x03, 0x01, ++ 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, ++ 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, ++ 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x1d, 0x06, ++ 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x65, 0xc5, 0xbe, 0xca, ++ 0xe6, 0x59, 0x6a, 0xfd, 0x6c, 0x71, 0xc4, 0xa7, 0x98, 0xc6, 0x25, 0x8d, ++ 0x7b, 0x67, 0x05, 0xd0, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, ++ 0x18, 0x30, 0x16, 0x80, 0x14, 0x81, 0xf8, 0xee, 0x47, 0x5c, 0x3e, 0xed, ++ 0xfb, 0xce, 0xa5, 0x84, 0xbe, 0xd7, 0xae, 0xdb, 0xd3, 0x7d, 0x64, 0xb3, ++ 0x2a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, ++ 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x66, 0x1e, 0x3d, ++ 0x1d, 0x53, 0x33, 0xde, 0x4e, 0xc7, 0xc4, 0xf4, 0xdf, 0xda, 0x18, 0x19, ++ 0x8a, 0xa9, 0xff, 0xe2, 0x63, 0x2b, 0xbe, 0xf2, 0x61, 0x63, 0xe2, 0xf6, ++ 0xed, 0x47, 0x1a, 0x71, 0x02, 0xec, 0x2a, 0xef, 0x89, 0x77, 0xe3, 0xfd, ++ 0x86, 0x69, 0xf1, 0x3f, 0x0d, 0xf9, 0x6e, 0xf9, 0x3b, 0xad, 0x26, 0x47, ++ 0xb7, 0xf2, 0x0d, 0xad, 0x23, 0xa3, 0x67, 0x3b, 0xcb, 0x6d, 0x9e, 0x03, ++ 0x0f, 0xbc, 0x69, 0x73, 0x9f, 0xd4, 0xa5, 0x0f, 0x6f, 0xf8, 0xab, 0x4d, ++ 0x36, 0xd1, 0xe0, 0xe0, 0x5d, 0x20, 0x43, 0x90, 0xc4, 0x65, 0x61, 0x93, ++ 0xe2, 0x0f, 0x51, 0x59, 0x0a, 0xf7, 0x88, 0x70, 0x57, 0xb9, 0x04, 0xa9, ++ 0x32, 0x57, 0x9c, 0xb3, 0x57, 0x38, 0x8b, 0x8e, 0x46, 0xc8, 0x32, 0x6c, ++ 0xb4, 0xf3, 0x96, 0x7f, 0x4b, 0xf0, 0x88, 0xf9, 0x7f, 0xe2, 0x71, 0xe1, ++ 0x8b, 0xe2, 0x14, 0xf1, 0x4b, 0x25, 0x00, 0x48, 0x1c, 0x7e, 0xe5, 0x8d, ++ 0x65, 0x2d, 0xeb, 0x72, 0x4f, 0x92, 0x44, 0xf3, 0xe6, 0xe0, 0xd0, 0xdf, ++ 0x85, 0xa8, 0x13, 0x4a, 0xfb, 0x99, 0xca, 0x14, 0x2c, 0x97, 0x80, 0x93, ++ 0x27, 0xd3, 0x20, 0xf8, 0x6d, 0x29, 0x28, 0x2c, 0xb9, 0x77, 0xea, 0xb1, ++ 0x63, 0xbd, 0x7d, 0x53, 0xfd, 0x4a, 0x62, 0x64, 0x0b, 0x98, 0xa8, 0xae, ++ 0x11, 0xfc, 0x6e, 0x8d, 0x63, 0xd4, 0x15, 0x55, 0xc6, 0x4c, 0x74, 0xf5, ++ 0x5f, 0xa0, 0xb9, 0x2c, 0x2d, 0x9a, 0x7a, 0x87, 0x6e, 0xf0, 0x5e, 0x25, ++ 0xed, 0xfc, 0xd8, 0xc4, 0x34, 0x33, 0x32, 0xad, 0x01, 0xd4, 0x4b, 0x49, ++ 0x51, 0xc2, 0x07, 0x7f, 0x90, 0x6d, 0xea, 0xf5, 0x4c, 0x41, 0x71, 0x64, ++ 0xeb, 0x1f, 0x29, 0xa3, 0x1f, 0x64, 0xa2, 0x1e, 0x0e, 0x6f, 0xa1, 0x67, ++ 0x99, 0x8d, 0x98, 0x1c, 0xb8, 0x53, 0x9d, 0x30, 0x1d, 0xae, 0x32, 0x56, ++ 0xd2 ++}; ++unsigned int certificate_printable_der_len = 829; ++ ++unsigned char certificate_eku_der[] = { ++ 0x30, 0x82, 0x03, 0x90, 0x30, 0x82, 0x02, 0x78, 0xa0, 0x03, 0x02, 0x01, ++ 0x02, 0x02, 0x09, 0x00, 0xd3, 0x9c, 0x41, 0x33, 0xdd, 0x6b, 0x5f, 0x45, ++ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, ++ 0x0b, 0x05, 0x00, 0x30, 0x47, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, ++ 0x04, 0x03, 0x0c, 0x18, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, ++ 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, ++ 0x43, 0x41, 0x20, 0x36, 0x31, 0x22, 0x30, 0x20, 0x06, 0x09, 0x2a, 0x86, ++ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x13, 0x73, 0x65, 0x63, ++ 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74, ++ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x32, ++ 0x31, 0x35, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, 0x17, 0x0d, 0x33, ++ 0x38, 0x30, 0x31, 0x31, 0x37, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, ++ 0x30, 0x4e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, ++ 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63, ++ 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67, ++ 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x36, 0x30, 0x32, 0x31, 0x22, 0x30, 0x20, ++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, ++ 0x13, 0x73, 0x65, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, ++ 0x64, 0x68, 0x61, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, ++ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, ++ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, ++ 0x02, 0x82, 0x01, 0x01, 0x00, 0xaa, 0x6f, 0xbb, 0x92, 0x77, 0xd7, 0x15, ++ 0xef, 0x88, 0x80, 0x88, 0xc0, 0xe7, 0x89, 0xeb, 0x35, 0x76, 0xf4, 0x85, ++ 0x05, 0x0f, 0x19, 0xe4, 0x5f, 0x25, 0xdd, 0xc1, 0xa2, 0xe5, 0x5c, 0x06, ++ 0xfb, 0xf1, 0x06, 0xb5, 0x65, 0x45, 0xcb, 0xbd, 0x19, 0x33, 0x54, 0xb5, ++ 0x1a, 0xcd, 0xe4, 0xa8, 0x35, 0x2a, 0xfe, 0x9c, 0x53, 0xf4, 0xc6, 0x76, ++ 0xdb, 0x1f, 0x8a, 0xd4, 0x7b, 0x18, 0x11, 0xaf, 0xa3, 0x90, 0xd4, 0xdd, ++ 0x4d, 0xd5, 0x42, 0xcc, 0x14, 0x9a, 0x64, 0x6b, 0xc0, 0x7f, 0xaa, 0x1c, ++ 0x94, 0x47, 0x4d, 0x79, 0xbd, 0x57, 0x9a, 0xbf, 0x99, 0x4e, 0x96, 0xa9, ++ 0x31, 0x2c, 0xa9, 0xe7, 0x14, 0x65, 0x86, 0xc8, 0xac, 0x79, 0x5e, 0x78, ++ 0xa4, 0x3c, 0x00, 0x24, 0xd3, 0xf7, 0xe1, 0xf5, 0x12, 0xad, 0xa0, 0x29, ++ 0xe5, 0xfe, 0x80, 0xae, 0xf8, 0xaa, 0x60, 0x36, 0xe7, 0xe8, 0x94, 0xcb, ++ 0xe9, 0xd1, 0xcc, 0x0b, 0x4d, 0xf7, 0xde, 0xeb, 0x52, 0xd2, 0x73, 0x09, ++ 0x28, 0xdf, 0x48, 0x99, 0x53, 0x9f, 0xc5, 0x9a, 0xd4, 0x36, 0xa3, 0xc6, ++ 0x5e, 0x8d, 0xbe, 0xd5, 0xdc, 0x76, 0xb4, 0x74, 0xb8, 0x26, 0x18, 0x27, ++ 0xfb, 0xf2, 0xfb, 0xd0, 0x9b, 0x3d, 0x7f, 0x10, 0xe2, 0xab, 0x44, 0xc7, ++ 0x88, 0x7f, 0xb4, 0x3d, 0x3e, 0xa3, 0xff, 0x6d, 0x06, 0x4b, 0x3e, 0x55, ++ 0xb2, 0x84, 0xf4, 0xad, 0x54, 0x88, 0x81, 0xc3, 0x9c, 0xf8, 0xb6, 0x68, ++ 0x96, 0x38, 0x8b, 0xcd, 0x90, 0x6d, 0x25, 0x4b, 0xbf, 0x0c, 0x44, 0x90, ++ 0xa5, 0x5b, 0x98, 0xd0, 0x40, 0x2f, 0xbb, 0x0d, 0xa8, 0x4b, 0x8a, 0x62, ++ 0x82, 0x46, 0x46, 0x18, 0x38, 0xae, 0x82, 0x07, 0xd0, 0xb4, 0x2f, 0x16, ++ 0x79, 0x55, 0x9f, 0x1b, 0xc5, 0x08, 0x6d, 0x85, 0xdf, 0x3f, 0xa9, 0x9b, ++ 0x4b, 0xc6, 0x28, 0xd3, 0x58, 0x72, 0x3d, 0x37, 0x11, 0x02, 0x03, 0x01, ++ 0x00, 0x01, 0xa3, 0x78, 0x30, 0x76, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, ++ 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03, ++ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, ++ 0x30, 0x16, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x0c, ++ 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, ++ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6c, ++ 0xe4, 0x6c, 0x27, 0xaa, 0xcd, 0x0d, 0x4b, 0x74, 0x21, 0xa4, 0xf6, 0x5f, ++ 0x87, 0xb5, 0x31, 0xfe, 0x10, 0xbb, 0xa7, 0x30, 0x1f, 0x06, 0x03, 0x55, ++ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe8, 0x6a, 0x1c, 0xab, ++ 0x2c, 0x48, 0xf9, 0x60, 0x36, 0xa2, 0xf0, 0x7b, 0x8e, 0xd2, 0x9d, 0xb4, ++ 0x2a, 0x28, 0x98, 0xc8, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, ++ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, ++ 0x55, 0x34, 0xe2, 0xfa, 0xf6, 0x89, 0x86, 0xad, 0x92, 0x21, 0xec, 0xb9, ++ 0x54, 0x0e, 0x18, 0x47, 0x0d, 0x1b, 0xa7, 0x58, 0xad, 0x69, 0xe4, 0xef, ++ 0x3b, 0xe6, 0x8d, 0xdd, 0xda, 0x0c, 0x45, 0xf6, 0xe8, 0x96, 0xa4, 0x29, ++ 0x0f, 0xbb, 0xcf, 0x16, 0xae, 0x93, 0xd0, 0xcb, 0x2a, 0x26, 0x1a, 0x7b, ++ 0xfc, 0x51, 0x22, 0x76, 0x98, 0x31, 0xa7, 0x0f, 0x29, 0x35, 0x79, 0xbf, ++ 0xe2, 0x4f, 0x0f, 0x14, 0xf5, 0x1f, 0xcb, 0xbf, 0x87, 0x65, 0x13, 0x32, ++ 0xa3, 0x19, 0x4a, 0xd1, 0x3f, 0x45, 0xd4, 0x4b, 0xe2, 0x00, 0x26, 0xa9, ++ 0x3e, 0xd7, 0xa5, 0x37, 0x9f, 0xf5, 0xad, 0x61, 0xe2, 0x40, 0xa9, 0x74, ++ 0x24, 0x53, 0xf2, 0x78, 0xeb, 0x10, 0x9b, 0x2c, 0x27, 0x88, 0x46, 0xcb, ++ 0xe4, 0x60, 0xca, 0xf5, 0x06, 0x24, 0x40, 0x2a, 0x97, 0x3a, 0xcc, 0xd0, ++ 0x81, 0xb1, 0x15, 0xa3, 0x4f, 0xd0, 0x2b, 0x4f, 0xca, 0x6e, 0xaa, 0x24, ++ 0x31, 0xb3, 0xac, 0xa6, 0x75, 0x05, 0xfe, 0x8a, 0xf4, 0x41, 0xc4, 0x06, ++ 0x8a, 0xc7, 0x0a, 0x83, 0x4e, 0x49, 0xd4, 0x3f, 0x83, 0x50, 0xec, 0x57, ++ 0x04, 0x97, 0x14, 0x49, 0xf5, 0xe1, 0xb1, 0x7a, 0x9c, 0x09, 0x4f, 0x61, ++ 0x87, 0xc3, 0x97, 0x22, 0x17, 0xc2, 0xeb, 0xcc, 0x32, 0x81, 0x31, 0x21, ++ 0x3f, 0x10, 0x57, 0x5b, 0x43, 0xbe, 0xcd, 0x68, 0x82, 0xbe, 0xe5, 0xc1, ++ 0x65, 0x94, 0x7e, 0xc2, 0x34, 0x76, 0x2b, 0xcf, 0x89, 0x3c, 0x2b, 0x81, ++ 0x23, 0x72, 0x95, 0xcf, 0xc9, 0x67, 0x19, 0x2a, 0xd5, 0x5c, 0xca, 0xa3, ++ 0x46, 0xbd, 0x48, 0x06, 0x0b, 0xa6, 0xa3, 0x96, 0x50, 0x28, 0xc7, 0x7e, ++ 0xcf, 0x62, 0xf2, 0xfa, 0xc4, 0xf2, 0x53, 0xe3, 0xc9, 0xe8, 0x2e, 0xdd, ++ 0x29, 0x37, 0x07, 0x47, 0xff, 0xff, 0x8a, 0x32, 0xbd, 0xa2, 0xb7, 0x21, ++ 0x89, 0xa0, 0x55, 0xf7 ++}; ++unsigned int certificate_eku_der_len = 916; +--- a/grub-core/tests/lib/functional_test.c ++++ b/grub-core/tests/lib/functional_test.c +@@ -73,6 +73,7 @@ + grub_dl_load ("xnu_uuid_test"); + grub_dl_load ("pbkdf2_test"); + grub_dl_load ("signature_test"); ++ grub_dl_load ("appended_signature_test"); + grub_dl_load ("sleep_test"); + grub_dl_load ("bswap_test"); + grub_dl_load ("ctz_test"); diff --git a/0021-appended-signatures-documentation.patch b/0021-appended-signatures-documentation.patch new file mode 100644 index 0000000..ac571ee --- /dev/null +++ b/0021-appended-signatures-documentation.patch @@ -0,0 +1,337 @@ +From 4edb825a012e9e7b9ad08aa693cfdebf42ae40e6 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Thu, 1 Oct 2020 13:02:09 +1000 +Subject: [PATCH 21/23] appended signatures: documentation + +This explains how appended signatures can be used to form part of +a secure boot chain, and documents the commands and variables +introduced. + +Signed-off-by: Daniel Axtens + +--- + +v2: fix a grammar issue, thanks Stefan Berger. +--- + docs/grub.texi | 193 +++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 176 insertions(+), 17 deletions(-) + +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -3278,6 +3278,7 @@ + + @menu + * biosnum:: ++* check_appended_signatures:: + * check_signatures:: + * chosen:: + * cmdpath:: +@@ -3342,11 +3343,18 @@ + chain-loaded system, @pxref{drivemap}. + + ++@node check_appended_signatures ++@subsection check_appended_signatures ++ ++This variable controls whether GRUB enforces appended signature validation on ++certain loaded files. @xref{Using appended signatures}. ++ ++ + @node check_signatures + @subsection check_signatures + +-This variable controls whether GRUB enforces digital signature +-validation on loaded files. @xref{Using digital signatures}. ++This variable controls whether GRUB enforces GPG-style digital signature ++validation on loaded files. @xref{Using GPG-style digital signatures}. + + @node chosen + @subsection chosen +@@ -4322,6 +4330,7 @@ + * date:: Display or set current date and time + * devicetree:: Load a device tree blob + * distrust:: Remove a pubkey from trusted keys ++* distrust_certificate:: Remove a certificate from the list of trusted certificates + * drivemap:: Map a drive to another + * echo:: Display a line of text + * efitextmode:: Set/Get text output mode resolution +@@ -4337,6 +4346,7 @@ + * help:: Show help messages + * insmod:: Insert a module + * keystatus:: Check key modifier status ++* list_certificates:: List trusted certificates + * list_env:: List variables in environment block + * list_trusted:: List trusted public keys + * load_env:: Load variables from environment block +@@ -4375,8 +4385,10 @@ + * test:: Check file types and compare values + * true:: Do nothing, successfully + * trust:: Add public key to list of trusted keys ++* trust_certificate:: Add an x509 certificate to the list of trusted certificates + * unset:: Unset an environment variable + @comment * vbeinfo:: List available video modes ++* verify_appended:: Verify appended digital signature + * verify_detached:: Verify detached digital signature + * videoinfo:: List available video modes + * wrmsr:: Write values to model-specific registers +@@ -4710,9 +4722,28 @@ + @code{check_signatures} is set to @code{enforce} + (@pxref{check_signatures}), and by some invocations of + @command{verify_detached} (@pxref{verify_detached}). @xref{Using +-digital signatures}, for more information. ++GPG-style digital signatures}, for more information. ++@end deffn ++ ++ ++@node distrust_certificate ++@subsection distrust_certificate ++ ++@deffn Command distrust_certificate cert_number ++Remove the x509 certificate numbered @var{cert_number} from GRUB's keyring of ++trusted x509 certificates for verifying appended signatures. ++ ++@var{cert_number} is the certificate number as listed by ++@command{list_certificates} (@pxref{list_certificates}). ++ ++These certificates are used to validate appended signatures when environment ++variable @code{check_appended_signatures} is set to @code{enforce} ++(@pxref{check_appended_signatures}), and by @command{verify_appended} ++(@pxref{verify_appended}). See @xref{Using appended signatures} for more ++information. + @end deffn + ++ + @node drivemap + @subsection drivemap + +@@ -4975,6 +5006,21 @@ + @end deffn + + ++@node list_certificates ++@subsection list_certificates ++ ++@deffn Command list_certificates ++List all x509 certificates trusted by GRUB for validating appended signatures. ++The output is a numbered list of certificates, showing the certificate's serial ++number and Common Name. ++ ++The certificate number can be used as an argument to ++@command{distrust_certificate} (@pxref{distrust_certificate}). ++ ++See @xref{Using appended signatures} for more information. ++@end deffn ++ ++ + @node list_env + @subsection list_env + +@@ -4994,7 +5040,7 @@ + @code{gpg --fingerprint}). The least significant four bytes (last + eight hexadecimal digits) can be used as an argument to + @command{distrust} (@pxref{distrust}). +-@xref{Using digital signatures}, for more information about uses for ++@xref{Using GPG-style digital signatures}, for more information about uses for + these keys. + @end deffn + +@@ -5029,8 +5075,12 @@ + administrator to configure a system to boot only signed + configurations, but to allow the user to select from among multiple + configurations, and to enable ``one-shot'' boot attempts and +-``savedefault'' behavior. @xref{Using digital signatures}, for more ++``savedefault'' behavior. @xref{Using GPG-style digital signatures}, for more + information. ++ ++Extra care should be taken when combining this command with appended signatures ++(@pxref{Using appended signatures}), as this file is not validated by an ++appended signature and could set @code{check_appended_signatures=no}. + @end deffn + + +@@ -5401,7 +5451,7 @@ + file from within GRUB using this command, such that its signature will + no longer be valid on subsequent boots. Care should be taken in such + advanced configurations to avoid rendering the system +-unbootable. @xref{Using digital signatures}, for more information. ++unbootable. @xref{Using GPG-style digital signatures}, for more information. + @end deffn + + +@@ -5817,11 +5867,31 @@ + must itself be properly signed. The @option{--skip-sig} option can be + used to disable signature-checking when reading @var{pubkey_file} + itself. It is expected that @option{--skip-sig} is useful for testing +-and manual booting. @xref{Using digital signatures}, for more ++and manual booting. @xref{Using GPG-style digital signatures}, for more + information. + @end deffn + + ++@node trust_certificate ++@subsection trust_certificate ++ ++@deffn Command trust_certificate x509_certificate ++Read a DER-formatted x509 certificate from the file @var{x509_certificate} ++and add it to GRUB's internal list of trusted x509 certificates. These ++certificates are used to validate appended signatures when the environment ++variable @code{check_appended_signatures} is set to @code{enforce}. ++ ++Note that if @code{check_appended_signatures} is set to @code{enforce} ++when @command{trust_certificate} is executed, then @var{x509_certificate} ++must itself bear an appended signature. (It is not sufficient that ++@var{x509_certificate} be signed by a trusted certificate according to the ++x509 rules: grub does not include support for validating signatures within x509 ++certificates themselves.) ++ ++See @xref{Using appended signatures} for more information. ++@end deffn ++ ++ + @node unset + @subsection unset + +@@ -5840,6 +5910,18 @@ + @end deffn + @end ignore + ++@node verify_appended ++@subsection verify_appended ++ ++@deffn Command verify_appended file ++Verifies an appended signature on @var{file} against the trusted certificates ++known to GRUB (See @pxref{list_certificates}, @pxref{trust_certificate}, and ++@pxref{distrust_certificate}). ++ ++Exit code @code{$?} is set to 0 if the signature validates ++successfully. If validation fails, it is set to a non-zero value. ++See @xref{Using appended signatures}, for more information. ++@end deffn + + @node verify_detached + @subsection verify_detached +@@ -5858,7 +5940,7 @@ + + Exit code @code{$?} is set to 0 if the signature validates + successfully. If validation fails, it is set to a non-zero value. +-@xref{Using digital signatures}, for more information. ++@xref{Using GPG-style digital signatures}, for more information. + @end deffn + + @node videoinfo +@@ -6339,13 +6421,14 @@ + @chapter Security + + @menu +-* Authentication and authorisation:: Users and access control +-* Using digital signatures:: Booting digitally signed code +-* UEFI secure boot and shim:: Booting digitally signed PE files +-* Secure Boot Advanced Targeting:: Embedded information for generation number based revocation +-* Measured Boot:: Measuring boot components +-* Lockdown:: Lockdown when booting on a secure setup +-* Signing GRUB itself:: Ensuring the integrity of the GRUB core image ++* Authentication and authorisation:: Users and access control ++* Using GPG-style digital signatures:: Booting digitally signed code ++* Using appended signatures:: An alternative approach to booting digitally signed code ++* UEFI secure boot and shim:: Booting digitally signed PE files ++* Secure Boot Advanced Targeting:: Embedded information for generation number based revocation ++* Measured Boot:: Measuring boot components ++* Lockdown:: Lockdown when booting on a secure setup ++* Signing GRUB itself:: Ensuring the integrity of the GRUB core image + @end menu + + @node Authentication and authorisation +@@ -6419,8 +6502,8 @@ + adding @kbd{set superusers=} and @kbd{password} or @kbd{password_pbkdf2} + commands. + +-@node Using digital signatures +-@section Using digital signatures in GRUB ++@node Using GPG-style digital signatures ++@section Using GPG-style digital signatures in GRUB + + GRUB's @file{core.img} can optionally provide enforcement that all files + subsequently read from disk are covered by a valid digital signature. +@@ -6503,6 +6586,82 @@ + (attacker-controlled) device. GRUB is at best only one link in a + secure boot chain. + ++@node Using appended signatures ++@section Using appended signatures in GRUB ++ ++GRUB supports verifying Linux-style 'appended signatures' for secure boot. ++Appended signatures are PKCS#7 messages containing a signature over the ++contents of a file, plus some metadata, appended to the end of a file. A file ++with an appended signature ends with the magic string: ++ ++@example ++~Module signature appended~\n ++@end example ++ ++where @code{\n} represents the carriage-return character, @code{0x0a}. ++ ++To enable appended signature verification, load the appendedsig module and an ++x509 certificate for verification. Building the appendedsig module into the ++core grub image is recommended. ++ ++Certificates can be managed at boot time using the @pxref{trust_certificate}, ++@pxref{distrust_certificate} and @pxref{list_certificates} commands. ++Certificates can also be built in to the core image using the @code{--x509} ++parameter to @command{grub-install} or @command{grub-mkimage}. ++ ++A file can be explictly verified using the @pxref{verify_appended} command. ++ ++Only signatures made with the SHA-256 or SHA-512 hash algorithm are supported, ++and only RSA signatures are supported. ++ ++A file can be signed with the @command{sign-file} utility supplied with the ++Linux kernel source. For example, if you have @code{signing.key} as the private ++key and @code{certificate.der} as the x509 certificate containing the public key: ++ ++@example ++sign-file SHA256 signing.key certificate.der vmlinux vmlinux.signed ++@end example ++ ++Enforcement of signature verification is controlled by the ++@code{check_appended_signatures} variable. Verification will only take place ++when files are loaded if the variable is set to @code{enforce}. If a ++certificate is built into the grub core image with the @code{--x509} parameter, ++the variable will be automatically set to @code{enforce} when the appendedsig ++module is loaded. ++ ++Unlike GPG-style signatures, not all files loaded by GRUB are required to be ++signed. Once verification is turned on, the following file types must carry ++appended signatures: ++ ++@enumerate ++@item Linux, Multiboot, BSD, XNU and Plan9 kernels ++@item Grub modules, except those built in to the core image ++@item Any new certificate files to be trusted ++@end enumerate ++ ++ACPI tables and Device Tree images will not be checked for appended signatures ++but must be verified by another mechanism such as GPG-style signatures before ++they will be loaded. ++ ++No attempt is made to validate any other file type. In particular, ++chain-loaded binaries are not verified - if your platform supports ++chain-loading and this cannot be disabled, consider an alternative secure ++boot mechanism. ++ ++As with GPG-style appended signatures, signature checking does @strong{not} ++stop an attacker with console access from dropping manually to the GRUB ++console and executing: ++ ++@example ++set check_appended_signatures=no ++@end example ++ ++Refer to the section on password-protecting GRUB (@pxref{Authentication ++and authorisation}) for more information on preventing this. ++ ++Additionally, special care must be taken around the @command{loadenv} command, ++which can be used to turn off @code{check_appended_signature}. ++ + @node UEFI secure boot and shim + @section UEFI secure boot and shim support + diff --git a/0022-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch b/0022-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch new file mode 100644 index 0000000..03fede7 --- /dev/null +++ b/0022-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch @@ -0,0 +1,101 @@ +From 806bb18c3493537530b6b0387143752478078f5b Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Mon, 28 Sep 2020 11:11:17 +1000 +Subject: [PATCH 22/23] ieee1275: enter lockdown based on /ibm,secure-boot + +If the 'ibm,secure-boot' property of the root node is 2 or greater, +enter lockdown. + +Signed-off-by: Daniel Axtens +--- + docs/grub.texi | 4 ++-- + grub-core/Makefile.core.def | 1 + + grub-core/kern/ieee1275/init.c | 27 +++++++++++++++++++++++++++ + include/grub/lockdown.h | 3 ++- + 4 files changed, 32 insertions(+), 3 deletions(-) + +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -6740,8 +6740,8 @@ + @section Lockdown when booting on a secure setup + + The GRUB can be locked down when booted on a secure boot environment, for example +-if the UEFI secure boot is enabled. On a locked down configuration, the GRUB will +-be restricted and some operations/commands cannot be executed. ++if UEFI or Power secure boot is enabled. On a locked down configuration, the ++GRUB will be restricted and some operations/commands cannot be executed. + + The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down. + Otherwise it does not exit. +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -331,6 +331,7 @@ + powerpc_ieee1275 = kern/powerpc/cache.S; + powerpc_ieee1275 = kern/powerpc/dl.c; + powerpc_ieee1275 = kern/powerpc/compiler-rt.S; ++ powerpc_ieee1275 = kern/lockdown.c; + + sparc64_ieee1275 = kern/sparc64/cache.S; + sparc64_ieee1275 = kern/sparc64/dl.c; +--- a/grub-core/kern/ieee1275/init.c ++++ b/grub-core/kern/ieee1275/init.c +@@ -44,6 +44,7 @@ + #ifdef __sparc__ + #include + #endif ++#include + + /* The maximum heap size we're going to claim at boot. Not used by sparc. */ + #ifdef __i386__ +@@ -708,6 +709,30 @@ + } + } + ++static void ++grub_get_ieee1275_secure_boot (void) ++{ ++ grub_ieee1275_phandle_t root; ++ int rc; ++ grub_uint32_t is_sb; ++ ++ grub_ieee1275_finddevice ("/", &root); ++ ++ rc = grub_ieee1275_get_integer_property (root, "ibm,secure-boot", &is_sb, ++ sizeof (is_sb), 0); ++ ++ /* ibm,secure-boot: ++ * 0 - disabled ++ * 1 - audit ++ * 2 - enforce ++ * 3 - enforce + OS-specific behaviour ++ * ++ * We only support enforce. ++ */ ++ if (rc >= 0 && is_sb >= 2) ++ grub_lockdown (); ++} ++ + grub_addr_t grub_modbase; + + void +@@ -733,6 +758,8 @@ + #else + grub_install_get_time_ms (grub_rtc_get_time_ms); + #endif ++ ++ grub_get_ieee1275_secure_boot (); + } + + void +--- a/include/grub/lockdown.h ++++ b/include/grub/lockdown.h +@@ -24,7 +24,8 @@ + #define GRUB_LOCKDOWN_DISABLED 0 + #define GRUB_LOCKDOWN_ENABLED 1 + +-#ifdef GRUB_MACHINE_EFI ++#if defined(GRUB_MACHINE_EFI) || \ ++ (defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275)) + extern void + EXPORT_FUNC (grub_lockdown) (void); + extern int diff --git a/0023-x509-allow-Digitial-Signature-plus-other-Key-Usages.patch b/0023-x509-allow-Digitial-Signature-plus-other-Key-Usages.patch new file mode 100644 index 0000000..dc5342f --- /dev/null +++ b/0023-x509-allow-Digitial-Signature-plus-other-Key-Usages.patch @@ -0,0 +1,48 @@ +From 5a690183091c2c161481123b17e1925148e516e4 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Tue, 30 Nov 2021 15:00:57 +1100 +Subject: [PATCH 23/23] x509: allow Digitial Signature plus other Key Usages + +Currently the x509 certificate parser for appended signature +verification requires that the certificate have the Digitial Signature +key usage and _only_ the Digitial Signature use. This is overly strict +and becomes policy enforcement rather than a security property. + +Require that the Digitial Signature usage is present, but do not +require that it is the only usage present. + +Reported-by: Michal Suchanek +Signed-off-by: Daniel Axtens +--- + grub-core/commands/appendedsig/x509.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c +index 70480aa73..6ae985b30 100644 +--- a/grub-core/commands/appendedsig/x509.c ++++ b/grub-core/commands/appendedsig/x509.c +@@ -547,7 +547,7 @@ cleanup: + + /* + * Verify the Key Usage extension. +- * We only permit the Digital signature usage. ++ * We require the Digital signature usage. + */ + static grub_err_t + verify_key_usage (grub_uint8_t *value, int value_size) +@@ -586,10 +586,10 @@ verify_key_usage (grub_uint8_t *value, int value_size) + goto cleanup; + } + +- if (usage != digitalSignatureUsage) ++ if (!(usage & digitalSignatureUsage)) + { + err = +- grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected Key Usage value: %x", ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, "Key Usage (0x%x) missing Digital Signature usage", + usage); + goto cleanup; + } +-- +2.31.1 + diff --git a/0044-squash-kern-Add-lockdown-support.patch b/0044-squash-kern-Add-lockdown-support.patch new file mode 100644 index 0000000..639d75c --- /dev/null +++ b/0044-squash-kern-Add-lockdown-support.patch @@ -0,0 +1,115 @@ +From 3c612287086a5f590f80d874e8c5c6042bf7f6a0 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Wed, 24 Feb 2021 23:51:38 +0800 +Subject: [PATCH 44/46] squash! kern: Add lockdown support + +Since the lockdown feature is efi specific, the +grub_{command,extcmd}_lockdown functions can be removed from other +platform for not taking up space in kernel image. +--- + grub-core/commands/extcmd.c | 2 ++ + grub-core/kern/command.c | 2 ++ + include/grub/command.h | 11 +++++++++++ + include/grub/extcmd.h | 13 +++++++++++++ + 4 files changed, 28 insertions(+) + +diff --git a/grub-core/commands/extcmd.c b/grub-core/commands/extcmd.c +index 90a5ca24a..4ac111a99 100644 +--- a/grub-core/commands/extcmd.c ++++ b/grub-core/commands/extcmd.c +@@ -111,6 +111,7 @@ grub_register_extcmd (const char *name, grub_extcmd_func_t func, + summary, description, parser, 1); + } + ++#ifdef GRUB_MACHINE_EFI + static grub_err_t + grub_extcmd_lockdown (grub_extcmd_context_t ctxt __attribute__ ((unused)), + int argc __attribute__ ((unused)), +@@ -132,6 +133,7 @@ grub_register_extcmd_lockdown (const char *name, grub_extcmd_func_t func, + + return grub_register_extcmd (name, func, flags, summary, description, parser); + } ++#endif + + void + grub_unregister_extcmd (grub_extcmd_t ext) +diff --git a/grub-core/kern/command.c b/grub-core/kern/command.c +index 4aabcd4b5..17363af7b 100644 +--- a/grub-core/kern/command.c ++++ b/grub-core/kern/command.c +@@ -78,6 +78,7 @@ grub_register_command_prio (const char *name, + return cmd; + } + ++#ifdef GRUB_MACHINE_EFI + static grub_err_t + grub_cmd_lockdown (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), +@@ -100,6 +101,7 @@ grub_register_command_lockdown (const char *name, + + return grub_register_command_prio (name, func, summary, description, 0); + } ++#endif + + void + grub_unregister_command (grub_command_t cmd) +diff --git a/include/grub/command.h b/include/grub/command.h +index 2a6f7f846..b518e262e 100644 +--- a/include/grub/command.h ++++ b/include/grub/command.h +@@ -86,11 +86,22 @@ EXPORT_FUNC(grub_register_command_prio) (const char *name, + const char *summary, + const char *description, + int prio); ++#ifdef GRUB_MACHINE_EFI + grub_command_t + EXPORT_FUNC(grub_register_command_lockdown) (const char *name, + grub_command_func_t func, + const char *summary, + const char *description); ++#else ++static inline grub_command_t ++grub_register_command_lockdown (const char *name, ++ grub_command_func_t func, ++ const char *summary, ++ const char *description) ++{ ++ return grub_register_command_prio (name, func, summary, description, 0); ++} ++#endif + void EXPORT_FUNC(grub_unregister_command) (grub_command_t cmd); + + static inline grub_command_t +diff --git a/include/grub/extcmd.h b/include/grub/extcmd.h +index fe9248b8b..fa1328ea5 100644 +--- a/include/grub/extcmd.h ++++ b/include/grub/extcmd.h +@@ -62,12 +62,25 @@ grub_extcmd_t EXPORT_FUNC(grub_register_extcmd) (const char *name, + const char *description, + const struct grub_arg_option *parser); + ++#ifdef GRUB_MACHINE_EFI + grub_extcmd_t EXPORT_FUNC(grub_register_extcmd_lockdown) (const char *name, + grub_extcmd_func_t func, + grub_command_flags_t flags, + const char *summary, + const char *description, + const struct grub_arg_option *parser); ++#else ++static inline grub_extcmd_t ++grub_register_extcmd_lockdown (const char *name, ++ grub_extcmd_func_t func, ++ grub_command_flags_t flags, ++ const char *summary, ++ const char *description, ++ const struct grub_arg_option *parser) ++{ ++ return grub_register_extcmd (name, func, flags, summary, description, parser); ++} ++#endif + + grub_extcmd_t EXPORT_FUNC(grub_register_extcmd_prio) (const char *name, + grub_extcmd_func_t func, +-- +2.26.2 + diff --git a/20_memtest86+ b/20_memtest86+ new file mode 100644 index 0000000..e0b4149 --- /dev/null +++ b/20_memtest86+ @@ -0,0 +1,66 @@ +#! /bin/sh +set -e + +# grub-mkconfig helper script. +# Copyright (C) 2011 Michal Ambroz +# Adapted for openSUSE by Andrey Borzenkov +# Adapted for EFI by Hans-Peter Jansen +# +# you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with the script. If not, see . + +. "$pkgdatadir/grub-mkconfig_lib" + +export TEXTDOMAIN=grub2 +export TEXTDOMAINDIR=/usr/share/locale + +CLASS="--class memtest86 --class gnu --class tools" + +if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then + OS=Memtest +else + OS="${GRUB_DISTRIBUTOR} Memtest" + CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1) ${CLASS}" +fi + +# memtest86+ comes in two flavours, one EFI and one suitable for x86 real mode. +# The EFI module requires security disabled in BIOS (Boot Mode: Other OS) +if [ -d /sys/firmware/efi -a -f /boot/efi/EFI/memtest86/memtest.efi ]; then + memtest=/boot/efi/EFI/memtest86/memtest.efi + loader='linux ' + message="$(gettext_printf "Loading EFI memtest ...\n" | grub_quote)" + # locate the real EFI partition + GRUB_DEVICE_BOOT=$(grub2-probe -t device "$memtest") +else + memtest=/boot/memtest.bin + loader='linux16' + message="$(gettext_printf "Loading x86 memtest ...\n" | grub_quote)" +fi + +if grub_file_is_not_garbage "$memtest" ; then + gettext_printf "Found memtest image: %s\n" "$memtest" >&2 + basename=`basename $memtest` + dirname=`dirname $memtest` + rel_dirname=`make_system_path_relative_to_its_root $dirname` + boot_device_id="$(grub_get_device_id "${GRUB_DEVICE_BOOT}")" + + printf "menuentry '%s' %s \$menuentry_id_option '%s' {\n" "${OS}" "${CLASS}" "memtest-$boot_device_id" + prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | sed -e "s/^/\t/")" + printf '%s\n' "${prepare_boot_cache}" + cat << EOF + echo '$message' + ${loader} ${rel_dirname}/${basename} +} + +EOF +fi diff --git a/80_suse_btrfs_snapshot b/80_suse_btrfs_snapshot new file mode 100644 index 0000000..4260140 --- /dev/null +++ b/80_suse_btrfs_snapshot @@ -0,0 +1,28 @@ +#! /bin/sh +set -e +SNAPSHOTS="/.snapshots" +if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] && + [ "x${GRUB_FS}" = "xbtrfs" ] && + [ -d "${SNAPSHOTS}" ]; then + machine=`uname -m` + case "x$machine" in + xs390 | xs390x) : ;; + *) + SNAPSHOT_RID=`btrfs inspect-internal rootid ${SNAPSHOTS}` + ROOT_RID=`btrfs inspect-internal rootid /` + if [ -n "${SNAPSHOT_RID}" -a "${SNAPSHOT_RID}" != "${ROOT_RID}" ]; then + SNAPSHOT_SUBVOL=`btrfs inspect-internal subvolid-resolve ${SNAPSHOT_RID} /` + ROOT_SUBVOL=`btrfs inspect-internal subvolid-resolve ${ROOT_RID} /` + INODE=`stat -c '%i' ${SNAPSHOTS}` + if [ "x${INODE}" = "x256" -a "x${ROOT_SUBVOL}${SNAPSHOTS}" != "x${SNAPSHOT_SUBVOL}" ]; then + echo "btrfs-mount-subvol (\$root) ${SNAPSHOTS} ${SNAPSHOT_SUBVOL}" + fi + fi + ;; + esac + cat <. + +# The output of this script is copied from part of grub.cfg +# that correspond to itself. The achievement is that user can +# modify that part of grub.cfg directly, and it will be persistent +# across update-grub runs. + +transform="s&^&&;s,grub,grub2," +ME=$(echo $0 |sed 's,/,\\/,g') +GRUBCFG=/boot/`echo grub | sed ${transform}`/grub.cfg + +# Exit gracefully if there's no configuration file yet +[ -f ${GRUBCFG} ] || exit 0 + +awk " + BEGIN {echo = 0} + /### BEGIN $ME ###/ {echo = 1; next} + /### END $ME ###/ {echo = 0; next} + {if (echo) print} +" ${GRUBCFG} diff --git a/Fix-the-size-calculation-for-the-synthesized-initrd.patch b/Fix-the-size-calculation-for-the-synthesized-initrd.patch new file mode 100644 index 0000000..8c4e2c1 --- /dev/null +++ b/Fix-the-size-calculation-for-the-synthesized-initrd.patch @@ -0,0 +1,86 @@ +From d441356c924102b43b303520cc1c62a624b014d6 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Thu, 26 Oct 2023 13:18:24 +0800 +Subject: [PATCH] Fix the size calculation for the synthesized initrd + +When calculating the size of the synthesized initrd in +grub_initrd_component(), the ending "TRAILER!!!" is counted in for every +synthesized initrd. However, in grub_initrd_load(), only one "TRAILER!!!" +will be appended for one group of consecutive synthesized initrds. The +additional size calculation for the ending "TRAILER!!!" could make the +linux kernel to read uninitialized bytes and result in the error message +like this: + +Initramfs unpacking failed: invalid magic at start of compressed archive + +To fit into the original 'newc' design, the ending "TRAILER!!!" is +removed from grub_initrd_component(). Instead, in grub_initrd_init(), +the 'newc' flag is set when calculating size of the synthesized initrd +to append the ending "TRAILER!!!" later. As for grub_initrd_load(), +since the path to the unsealed key is specified in 'newc_name', it's +unnecessary to set the 'newc' flag while copying the unsealed key +because the flag is already set when parsing the path name. + +Signed-off-by: Gary Lin +--- + grub-core/loader/linux.c | 23 ++++++++--------------- + 1 file changed, 8 insertions(+), 15 deletions(-) + +diff --git a/grub-core/loader/linux.c b/grub-core/loader/linux.c +index 4e028f5..9ee8f37 100644 +--- a/grub-core/loader/linux.c ++++ b/grub-core/loader/linux.c +@@ -209,13 +209,6 @@ grub_initrd_component (const char *buf, int bufsz, const char *newc_name, + &initrd_ctx->size)) + goto overflow; + +- initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4); +- if (grub_add (initrd_ctx->size, +- ALIGN_UP (sizeof (struct newc_head) +- + sizeof ("TRAILER!!!") - 1, 4), +- &initrd_ctx->size)) +- goto overflow; +- + free_dir (root); + root = 0; + return GRUB_ERR_NONE; +@@ -312,6 +305,13 @@ grub_initrd_init (int argc, char *argv[], + goto overflow; + } + ++ FOR_LIST_ELEMENTS (pk, kpuber) ++ if (pk->key && pk->path) ++ { ++ grub_initrd_component (pk->key, pk->key_len, pk->path, initrd_ctx); ++ newc = 1; ++ } ++ + if (newc) + { + initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4); +@@ -324,10 +324,6 @@ grub_initrd_init (int argc, char *argv[], + root = 0; + } + +- FOR_LIST_ELEMENTS (pk, kpuber) +- if (pk->key && pk->path) +- grub_initrd_component (pk->key, pk->key_len, pk->path, initrd_ctx); +- + return GRUB_ERR_NONE; + + overflow: +@@ -404,10 +400,7 @@ grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx, + + cursize = initrd_ctx->components[i].size; + if (initrd_ctx->components[i].buf) +- { +- grub_memcpy (ptr, initrd_ctx->components[i].buf, cursize); +- newc = 1; +- } ++ grub_memcpy (ptr, initrd_ctx->components[i].buf, cursize); + else if (grub_file_read (initrd_ctx->components[i].file, ptr, cursize) + != cursize) + { +-- +2.35.3 + diff --git a/PATCH_POLICY b/PATCH_POLICY new file mode 100644 index 0000000..ae2f30b --- /dev/null +++ b/PATCH_POLICY @@ -0,0 +1,6 @@ +Make sure the patches you add contain tags similar to patches in the kernel +RPM. This means, it should contain From, Subject, Patch-mainline tags and also +a description of the problem, i.e. what the patch is for. + +Also, if it is not a SUSE/openSUSE-specific patch (unlikely is), post the patch to +upstream too. diff --git a/README.ibm3215 b/README.ibm3215 new file mode 100644 index 0000000..d691738 --- /dev/null +++ b/README.ibm3215 @@ -0,0 +1,59 @@ +[Disclaimer: I do not know enough (by far) about the inner workings +and secrets of these printer-consoles, so please correct me/send advice, +if there are better solutions!] + +On 3215/327x things are dramatically different from everywhere else. +You'll have to live with some severe limitations: + +0. Interactivity is quite limited. You'll need to "blindly" type, + most of the time, to see the effect only on "submission" ([Enter]). + In edit and shell mode it's sometimes useful to insert underlines + just to see, where the curser (AKA "point") is. (BTW, 3270 is _much_ + better at displaying/refreshing grub2 screens than 3215.) +1. No cursor-movement-, alt-, meta-, and control-keys (like [ESC]). +2. To work around the lack of control-keys, the "[^][C]-sends-interrupt"- + trick is extended to translate sequences of caret followed by character + to the respective control-character. In the following this sequence + of two keystrokes is referred to as '^c' instead of that somewhat balky + [^][C]. Thus an [ESC] keypress can be generated with '^[' ("caret" + followed by "opening square bracket"). +3. If a caret itself is needed, send one on it's own (i.e. a solitary [^] + followed by [Enter] -- or use '^^'. +4. No '[Enter]', because it can't be avoided on *any* input. +5. If you still need one to arrive at the application, you may either + press '[Enter]' *twice* (one empty line, sort of) or add '^j' to your + input. In menu mode '^f' works as well (see below). But using "empty + lines" does now work very reliably, so explicit control sequences + are to be preferred. This has the additional advantage, that combined + sequences can be sent, e.g. to exit from 'grub2-emu' without doing + anything, you can simply type 'cexit^j' and submit that with [Enter]. + +Common Substitutes: + '^j'` => [Enter] "engage" + '^[' => [ESC] "abort" / return to previous "state" + '^i' => [TAB] try completion (in edit & shell mode) + +Available Keys in Menu Mode: + '^a' first entry '^e' last entry + '^p' previous entry '^n' next entry + '^g' previous page '^c' next page + '^f' boot selected entry/enter sub-menu (same as '^j') + 'e' edit selected entry 'c' enter grub-shell + +Available Keys in Edit Mode: + '^p' previous line '^n' next line + '^b' backward char '^f' forward char + '^a' beginning of line '^e' end of line + '^h' backspace '^d' delete + '^k' kill (to end of) line '^y' yank + '^o' open line '^l' refresh screen + '^x' boot entry '^c' enter grub-shell + +Availble Keys on Command Line Mode: + '^p' previous command '^n' next command (from history) + '^a' beginning of line '^e' end of line + '^b' backward char '^f' forward char + '^h' backspace '^d' delete + '^k' kill (to end of) line '^u' discard line + '^y' yank + diff --git a/SLES-UEFI-CA-Certificate.crt b/SLES-UEFI-CA-Certificate.crt new file mode 100644 index 0000000..56f3fce --- /dev/null +++ b/SLES-UEFI-CA-Certificate.crt @@ -0,0 +1,39 @@ +-----BEGIN CERTIFICATE----- +MIIG5TCCBM2gAwIBAgIBATANBgkqhkiG9w0BAQsFADCBpjEtMCsGA1UEAwwkU1VT +RSBMaW51eCBFbnRlcnByaXNlIFNlY3VyZSBCb290IENBMQswCQYDVQQGEwJERTES +MBAGA1UEBwwJTnVyZW1iZXJnMSEwHwYDVQQKDBhTVVNFIExpbnV4IFByb2R1Y3Rz +IEdtYkgxEzARBgNVBAsMCkJ1aWxkIFRlYW0xHDAaBgkqhkiG9w0BCQEWDWJ1aWxk +QHN1c2UuZGUwHhcNMTMwMTIyMTQyMDA4WhcNMzQxMjE4MTQyMDA4WjCBpjEtMCsG +A1UEAwwkU1VTRSBMaW51eCBFbnRlcnByaXNlIFNlY3VyZSBCb290IENBMQswCQYD +VQQGEwJERTESMBAGA1UEBwwJTnVyZW1iZXJnMSEwHwYDVQQKDBhTVVNFIExpbnV4 +IFByb2R1Y3RzIEdtYkgxEzARBgNVBAsMCkJ1aWxkIFRlYW0xHDAaBgkqhkiG9w0B +CQEWDWJ1aWxkQHN1c2UuZGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCrLYL1Uq02iIgro6x6PFESFDtUKU7xO/bJanI7+AQAroowFuLBI67BBSmoq3hR +QnH3OtQusGV8y+wvjaaunppvWMfjViZ88zssj5fKXrDr5U6BB566DJgHreWaEs2d +FD13XpKRr3Nk9zdjAJu5YsR7hI1NMXsnj1X8w71OY9HLjv+Kq9917PJwZQjOGnAJ +BQTi0ogHuLiwDqMKgg5rrYD4cJDPzoLEmEXnwHDIOSiWdD0bCzhN6GQDKldIxQ2O +d/mjUgzB+dWslIb+bUKaoJgDtyPV20W74t7Y2uwoaEVr9QkPoM3tOPttf4qsWo8B +J1TgeoF01ZeKcvSyvOXCKbfAN9sqURK2ZUTNThqZ//VPQmJP6fByrMJsbvTOSsQt +HI+fFPrg1DC2KT8SzuGtWDRscHZ7MofvUKEQolVgkGwp8u68t/RAAwDpUdqIajzi +yfp9qSDD+9uMeyiLa4rrAr2ATGohNBa0qha95slgvSepXbYKuHG5b4fWMsG7z4Uc +dqE2vK8cQma1nsAeQBaq2/89294TOHEzKyspesfCBCnKQ3q+l9xelYRdvapj1CH/ +cfUZf2/6X3VHN1P88RfRrPubswmrcOCEBT41upa2WKRDJ1GS6YhL6LJnrZSTjfe+ +KsfNVS1D+KqSKiK0hfk6YK6O88mMGeAKQs3Ap8WthBLf0QIDAQABo4IBGjCCARYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPU1Az5OFOQJLHPxaEt7f6LF+dV8w +gdMGA1UdIwSByzCByIAUPU1Az5OFOQJLHPxaEt7f6LF+dV+hgaykgakwgaYxLTAr +BgNVBAMMJFNVU0UgTGludXggRW50ZXJwcmlzZSBTZWN1cmUgQm9vdCBDQTELMAkG +A1UEBhMCREUxEjAQBgNVBAcMCU51cmVtYmVyZzEhMB8GA1UECgwYU1VTRSBMaW51 +eCBQcm9kdWN0cyBHbWJIMRMwEQYDVQQLDApCdWlsZCBUZWFtMRwwGgYJKoZIhvcN +AQkBFg1idWlsZEBzdXNlLmRlggEBMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0B +AQsFAAOCAgEANtdMT47CjQtuERYa5jfygIO5F+urB4fl8pYcQQ/hTPE0KtAnAtrS +1strtMrVQ1t7Wu3fVbWYA6MZMXXkcwyyNbaWfj6roaSC6G5ZqCJ69oSyzaCbyaTI +eOgzIIiVGOAj7tiM6T88Xp9qx4Xa3F6UQHF6xfwBT3nNKerGKOG01p7mBfBewwO5 +Hxp7OAZmennUxV1uuT5/AsArxw9lMlawXhIAS7tRYHW+32D4tjHPDycldOw1hBjt +z5JdehBiTmxhJ6onl0HSpsX84IMSbkeFIxLfxIF0TNas1pGnSGmh8FcV+ck9js3P +yamJcNkgCstIwo3QZ2D5YdtQjOusyEuGjCIpDIQx36OMzeOo0SayOdzb2dSmcrHv +4DIkXDUELyIzu79A2R2KR7OQaGL6HGAVy6+yXHHygTbbUrb6ck2+aOG8913ChABc +ZAiSFFRKVZzzj7FeIxZNA8GBUbhd20eQB2fUXDypeAnTG6P3dtTs84xNb1qGm3VC +OAKjkWYQijLWmAOs9Q4NM/AXOeDTgXxA7iX7kWHRNeDbACirp7zM2ZOIP5ObIS6z +yMqcG9DecSVbXiH3MJDTBoB1idQTTyreqpM/l6N8xNNVjEiLJGMEM1SeYq6S1lFV +a+GcdOaLYkh7ya3I42l/tDOqH2OLIf7FEtocnc1xU6jTz8au1tZxec8= +-----END CERTIFICATE----- diff --git a/arm64-Use-proper-memory-type-for-kernel-allocation.patch b/arm64-Use-proper-memory-type-for-kernel-allocation.patch new file mode 100644 index 0000000..561f106 --- /dev/null +++ b/arm64-Use-proper-memory-type-for-kernel-allocation.patch @@ -0,0 +1,51 @@ +From 4f9d3f4f8d7866c69e52ba7d81562daea38b22e6 Mon Sep 17 00:00:00 2001 +From: Maximilian Luz +Date: Tue, 28 Jun 2022 23:06:46 +0200 +Subject: [PATCH] arm64: Use proper memory type for kernel allocation +References: bsc#1215151 +Patch-Mainline: no, it's a downstream fix based on Fedora/openSUSE grub2 + +Currently, the kernel pages are allocated with type EFI_LOADER_DATA. +While the vast majority of systems will happily execute code from those +pages (i.e. don't care about memory protection), the Microsoft Surface +Pro X stalls, as this memory is not designated as "executable". + +Therefore, allocate the kernel pages as EFI_LOADER_CODE to request +memory that is actually executable. + +Link: https://github.com/rhboot/grub2/commit/4f9d3f4f8d7866c69e52ba7d81562daea38b22e6 +Signed-off-by: Maximilian Luz +Signed-off-by: Chester Lin +--- + grub-core/loader/arm64/linux.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/grub-core/loader/arm64/efi/linux.c b/grub-core/loader/arm64/efi/linux.c +index 419f2201d..a3a193c25 100644 +--- a/grub-core/loader/arm64/efi/linux.c ++++ b/grub-core/loader/arm64/efi/linux.c +@@ -26,7 +26,9 @@ + #include + #include + #include ++#include + #include ++#include + #include + #include + #include +@@ -403,7 +405,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + grub_loader_unset(); + + kernel_alloc_pages = GRUB_EFI_BYTES_TO_PAGES (kernel_size + align - 1); +- kernel_alloc_addr = grub_efi_allocate_any_pages (kernel_alloc_pages); ++ kernel_alloc_addr = grub_efi_allocate_pages_real (GRUB_EFI_MAX_USABLE_ADDRESS, ++ kernel_alloc_pages, ++ GRUB_EFI_ALLOCATE_MAX_ADDRESS, ++ GRUB_EFI_LOADER_CODE); + grub_dprintf ("linux", "kernel numpages: %d\n", kernel_alloc_pages); + if (!kernel_alloc_addr) + { +-- +2.40.0 + diff --git a/grub-2.12~rc1.tar.xz b/grub-2.12~rc1.tar.xz new file mode 100644 index 0000000..367ca07 --- /dev/null +++ b/grub-2.12~rc1.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a60c08b0ff1bac630cae6293b73871a541610a7fb1a7337aeb5e96f359cd650 +size 6589460 diff --git a/grub-install-force-journal-draining-to-ensure-data-i.patch b/grub-install-force-journal-draining-to-ensure-data-i.patch new file mode 100644 index 0000000..d5604e8 --- /dev/null +++ b/grub-install-force-journal-draining-to-ensure-data-i.patch @@ -0,0 +1,218 @@ +From 3085db0922a1d803d4a9dfe54daae6fef20e4340 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Mon, 13 Apr 2020 16:08:59 +0800 +Subject: [PATCH] grub-install: force journal draining to ensure data integrity + +In XFS, the system would end up in unbootable state if an abrupt power +off after grub-install is occuring. It can be easily reproduced with. + + grub-install /dev/vda; reboot -f + +The grub error would show many different kinds of corruption in +filesystem and the problem boils down to incompleted journal transaction +which would leave pending writes behind in the on-disk journal. It is +therefore necessary to recover the system via re-mounting the filesystem +from linux system that all pending journal log can be replayed. + +On the other hand if journal draining can be enforced by grub-install +then it can bring more resilience to such abrupt power loss. The fsync +is not enough here for XFS, because that only writes in-memory log to +on-disk (ie makes sure broken state can be repaired). Unfortunately +there's no designated system call to serve solely for the journal +draining, so it can only be achieved via fsfreeze system call that the +journal draining can happen as a byproduct during the process. + +This patch adds fsfreeze/unfreeze at the end of grub-install to induce +journal draining on journaled file system. However btrfs is excluded +from the list as it is using fsync to drain journal and also is not +desired as reportedly having negative side effect. With this patch +applied, the boot falilure can no longer be reproduced with above +procedure. + +v2: +Fix boot failure after kdump due to the content of grub.cfg is not +completed with pending modificaton in xfs journal (bsc#1186975) + +Signed-off-by: Michael Chang +--- + Makefile.util.def | 1 + + grub-core/osdep/basic/journaled_fs.c | 26 +++++++++++++++++++ + grub-core/osdep/journaled_fs.c | 5 ++++ + grub-core/osdep/linux/journaled_fs.c | 48 ++++++++++++++++++++++++++++++++++++ + include/grub/util/install.h | 2 ++ + util/grub-install.c | 20 +++++++++++++++ + 6 files changed, 102 insertions(+) + create mode 100644 grub-core/osdep/basic/journaled_fs.c + create mode 100644 grub-core/osdep/journaled_fs.c + create mode 100644 grub-core/osdep/linux/journaled_fs.c + +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -672,6 +672,9 @@ + emu_condition = COND_s390x; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; ++ common = grub-core/osdep/journaled_fs.c; ++ extra_dist = grub-core/osdep/basic/journaled_fs.c; ++ extra_dist = grub-core/osdep/linux/journaled_fs.c; + + ldadd = '$(LIBLZMA)'; + ldadd = libgrubmods.a; +--- /dev/null ++++ b/grub-core/osdep/basic/journaled_fs.c +@@ -0,0 +1,26 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2010,2011,2012,2013 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++ ++int ++grub_install_sync_fs_journal (const char *path) ++{ ++ return 1; ++} ++ +--- /dev/null ++++ b/grub-core/osdep/journaled_fs.c +@@ -0,0 +1,5 @@ ++#ifdef __linux__ ++#include "linux/journaled_fs.c" ++#else ++#include "basic/journaled_fs.c" ++#endif +--- /dev/null ++++ b/grub-core/osdep/linux/journaled_fs.c +@@ -0,0 +1,48 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2010,2011,2012,2013 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++int ++grub_install_sync_fs_journal (const char *path) ++{ ++ int fd, ret; ++ ++ fd = open (path, O_RDONLY); ++ ++ if (fd == -1) ++ return 1; ++ ++ if (ioctl (fd, FIFREEZE, 0) == 0) ++ { ++ ioctl(fd, FITHAW, 0); ++ ret = 1; ++ } ++ else if (errno == EOPNOTSUPP) ++ ret = 1; ++ else ++ ret = 0; ++ ++ close (fd); ++ return ret; ++} ++ +--- a/include/grub/util/install.h ++++ b/include/grub/util/install.h +@@ -301,4 +301,6 @@ + } + #endif + ++int ++grub_install_sync_fs_journal (const char *path); + #endif +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include + +@@ -2074,6 +2075,24 @@ + break; + } + ++ { ++ const char *journaled_fs[] = {"xfs", "ext2", NULL}; ++ int i; ++ ++ for (i = 0; journaled_fs[i]; ++i) ++ if (grub_strcmp (grub_fs->name, journaled_fs[i]) == 0) ++ { ++ int retries = 10; ++ ++ /* If the fs is already frozen at that point, we could generally ++ * expected that it will be soon unfrozen again (assuming some other ++ * process has frozen it for snapshotting or something), so we may ++ * as well retry a few (limited) times in a delay loop. */ ++ while (retries-- && !grub_install_sync_fs_journal (grubdir)) ++ grub_sleep (1); ++ break; ++ } ++ } + /* + * Either there are no platform specific code, or it didn't raise + * ponr. Raise it here, because usually this is already past point +--- a/util/grub-mkconfig.in ++++ b/util/grub-mkconfig.in +@@ -335,6 +335,15 @@ + esac + done + ++sync_fs_journal () { ++ if test "x$GRUB_DEVICE" = "x$GRUB_DEVICE_BOOT" && ++ test "x$GRUB_FS" = "xxfs" -o "x$GRUB_FS" = "xext2" && ++ test "x${grub_cfg}" != "x" -a "x`make_system_path_relative_to_its_root $grub_cfg`" = "x/boot/grub2/grub.cfg" && ++ test -x /usr/sbin/fsfreeze; then ++ /usr/sbin/fsfreeze --freeze / && /usr/sbin/fsfreeze --unfreeze / ++ fi ++} >&2 ++ + if test "x${grub_cfg}" != "x" ; then + if ! ${grub_script_check} ${grub_cfg}.new; then + # TRANSLATORS: %s is replaced by filename +@@ -351,6 +360,7 @@ + cat ${grub_cfg}.new > ${grub_cfg} + umask $oldumask + rm -f ${grub_cfg}.new ++ sync_fs_journal || true + fi + fi + diff --git a/grub-install-record-pcrs.patch b/grub-install-record-pcrs.patch new file mode 100644 index 0000000..8f11a4e --- /dev/null +++ b/grub-install-record-pcrs.patch @@ -0,0 +1,16 @@ +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -1501,6 +1501,13 @@ + + grub_util_unlink (load_cfg); + ++ if (1) ++ { ++ load_cfg_f = grub_util_fopen (load_cfg, "wb"); ++ have_load_cfg = 1; ++ fprintf (load_cfg_f, "tpm_record_pcrs 0-9\n"); ++ } ++ + if (debug_image && debug_image[0]) + { + load_cfg_f = grub_util_fopen (load_cfg, "wb"); diff --git a/grub-read-pcr.patch b/grub-read-pcr.patch new file mode 100644 index 0000000..faf72e5 --- /dev/null +++ b/grub-read-pcr.patch @@ -0,0 +1,165 @@ +--- a/include/grub/tpm.h ++++ b/include/grub/tpm.h +@@ -36,6 +36,12 @@ + + #define EV_IPL 0x0d + ++struct grub_tpm_digest { ++ const char * algorithm; ++ unsigned int size; ++ unsigned char value[1]; /* variable length */ ++}; ++ + grub_err_t grub_tpm_measure (unsigned char *buf, grub_size_t size, + grub_uint8_t pcr, const char *description); + int grub_tpm_present (void); +@@ -45,5 +51,7 @@ + { + return grub_env_get_bool ("tpm_fail_fatal", false); + } ++struct grub_tpm_digest *grub_tpm_read_pcr (grub_uint8_t index, const char *algo); ++void grub_tpm_digest_free (struct grub_tpm_digest *d); + + #endif +--- a/grub-core/commands/efi/tpm.c ++++ b/grub-core/commands/efi/tpm.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -186,6 +187,91 @@ + return grub_efi_log_event_status (status); + } + ++static void ++grub_tpm2_select_pcr(TPML_PCR_SELECTION *o, unsigned int pcrIndex, unsigned int algo) ++{ ++ TPMS_PCR_SELECTION *pcr; ++ ++ pcr = &o->pcrSelections[o->count++]; ++ pcr->hash = algo; ++ pcr->sizeOfSelect = 3; ++ pcr->pcrSelect[TPM2_PCR_TO_SELECT(pcrIndex)] |= TPM2_PCR_TO_BIT(pcrIndex); ++} ++ ++struct grub_tpm_hash_info { ++ const char *name; ++ grub_size_t size; ++ int id; ++}; ++ ++static const struct grub_tpm_hash_info * ++grub_tpm2_get_digest_info (const char *algo) ++{ ++ static struct grub_tpm_hash_info __hashes[] = { ++ { "sha256", 32, TPM_ALG_SHA256 }, /* first entry is the default */ ++ { "sha512", 64, TPM_ALG_SHA512 }, ++ { "sha1", 20, TPM_ALG_SHA1 }, ++ { NULL } ++ }; ++ struct grub_tpm_hash_info *h; ++ ++ if (algo == NULL) ++ return &__hashes[0]; ++ ++ for (h = __hashes; h->name; ++h) ++ if (!grub_strcmp(h->name, algo)) ++ return h; ++ ++ return NULL; ++} ++ ++static grub_err_t ++grub_tpm2_read_pcr (grub_int8_t pcrIndex, const char *algo, struct grub_tpm_digest **ret) ++{ ++ const struct grub_tpm_hash_info *info; ++ TPML_PCR_SELECTION inSelection, outSelection; ++ grub_uint32_t pcrUpdateCounter; ++ TPML_DIGEST digests = { 0 }; ++ TPM2B_DIGEST *d; ++ struct grub_tpm_digest *result; ++ int rc; ++ ++ info = grub_tpm2_get_digest_info (algo); ++ if (info == NULL) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown digest algorithm %s"), algo); ++ ++ grub_memset(&inSelection, 0, sizeof(inSelection)); ++ grub_memset(&outSelection, 0, sizeof(outSelection)); ++ grub_tpm2_select_pcr(&inSelection, pcrIndex, info->id); ++ ++ rc = TPM2_PCR_Read( ++ NULL, ++ &inSelection, ++ &pcrUpdateCounter, ++ &outSelection, ++ &digests, ++ NULL ++ ); ++ ++ if (rc != 0) ++ return grub_error (GRUB_ERR_BAD_DEVICE, "TPM2_PCR_Read failed, status=%d", rc); ++ ++ d = &digests.digests[0]; ++ ++ *ret = result = grub_malloc (sizeof (*result) + d->size); ++ grub_memcpy (result->value, d->buffer, d->size); ++ result->algorithm = info->name; ++ result->size = d->size; ++ ++ return GRUB_ERR_NONE; ++} ++ ++void ++grub_tpm_digest_free (struct grub_tpm_digest *d) ++{ ++ grub_free (d); ++} ++ + static grub_err_t + grub_tpm2_log_event (grub_efi_handle_t tpm_handle, unsigned char *buf, + grub_size_t size, grub_uint8_t pcr, +@@ -323,3 +409,26 @@ + return grub_tpm2_present (tpm); + } + } ++ ++struct grub_tpm_digest * ++grub_tpm_read_pcr (grub_uint8_t pcr, const char *algo) ++{ ++ grub_efi_handle_t tpm_handle; ++ grub_efi_uint8_t protocol_version; ++ struct grub_tpm_digest *result = NULL; ++ ++ ++ if (!grub_tpm_handle_find (&tpm_handle, &protocol_version)) ++ return 0; ++ ++ if (protocol_version != 2) ++ { ++ grub_error (GRUB_ERR_BAD_DEVICE, N_("%s: TPM version %d not implemented"), __func__, protocol_version); ++ return NULL; ++ } ++ ++ if (grub_tpm2_read_pcr (pcr, algo, &result)) ++ return NULL; ++ ++ return result; ++} +--- a/include/grub/tpm2/tpm2.h ++++ b/include/grub/tpm2/tpm2.h +@@ -23,6 +23,10 @@ + #include + #include + ++/* Defined in: TCG TPM Specification, v1.59, Part 2, Section 10.6.1. */ ++#define TPM2_PCR_TO_SELECT(x) ((x) / 8) ++#define TPM2_PCR_TO_BIT(x) (1 << ((x) % 8)) ++ + /* Well-Known Windows SRK handle */ + #define TPM2_SRK_HANDLE 0x81000001 + diff --git a/grub.default b/grub.default new file mode 100644 index 0000000..c75de63 --- /dev/null +++ b/grub.default @@ -0,0 +1,39 @@ +# If you change this file, run 'grub2-mkconfig -o /boot/grub2/grub.cfg' afterwards to update +# /boot/grub2/grub.cfg. + +# Uncomment to set your own custom distributor. If you leave it unset or empty, the default +# policy is to determine the value from /etc/os-release +# GRUB_DISTRIBUTOR="" + +GRUB_DEFAULT=0 +GRUB_HIDDEN_TIMEOUT=0 +GRUB_HIDDEN_TIMEOUT_QUIET=true +GRUB_TIMEOUT=10 +GRUB_CMDLINE_LINUX_DEFAULT="" +GRUB_CMDLINE_LINUX="" + +# Uncomment to automatically save last booted menu entry in GRUB2 environment +# variable `saved_entry' +#GRUB_SAVEDEFAULT="true" + +# Uncomment to enable BadRAM filtering, modify to suit your needs +# This works with Linux (no patch required) and with any kernel that obtains +# the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...) +#GRUB_BADRAM="0x01234567,0xfefefefe,0x89abcdef,0xefefefef" + +# Uncomment to disable graphical terminal (grub-pc only) +#GRUB_TERMINAL=console + +# The resolution used on graphical terminal +# note that you can use only modes which your graphic card supports via VBE +# you can see them in real GRUB with the command `vbeinfo' +#GRUB_GFXMODE=640x480 + +# Uncomment if you don't want GRUB to pass "root=UUID=xxx" parameter to Linux +#GRUB_DISABLE_LINUX_UUID=true + +# Uncomment to disable generation of recovery mode menu entries +#GRUB_DISABLE_RECOVERY="true" + +# Uncomment to get a beep at grub start +#GRUB_INIT_TUNE="480 440 1" diff --git a/grub2-Add-hidden-menu-entries.patch b/grub2-Add-hidden-menu-entries.patch new file mode 100644 index 0000000..c4d7ac4 --- /dev/null +++ b/grub2-Add-hidden-menu-entries.patch @@ -0,0 +1,220 @@ +From a06004f4c668abd7c760a2818d0a8205da7568e7 Mon Sep 17 00:00:00 2001 +From: Alexander Graf +Date: Tue, 26 Apr 2016 15:29:25 +0200 +Subject: [PATCH v3] Add hidden menu entries + +The menu infrastructure is quite powerful. It allows you to define menu +entries that can contain arbitrary grub commands that can do a lot more +than just boot kernel entries. + +For some of these it makes sense to hide them inside the normal menu +though and instead have them available through hotkeys that get advertised +differently. My main use case is to switch to the serial console when +gfxterm is loaded. + +So this patch adds support for hidden menu entries that are accessible +using hotkeys, but are not accessible in the grub menu. + +Signed-off-by: Alexander Graf + +--- + +v1 -> v2: + + - fix default entry selection + +v2 -> v3: + + - replace "--hidden" parameter with new command "hiddenentry" + +--- a/grub-core/commands/legacycfg.c ++++ b/grub-core/commands/legacycfg.c +@@ -143,7 +143,7 @@ + args[0] = oldname; + grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy", + NULL, NULL, +- entrysrc, 0); ++ entrysrc, 0, 0); + grub_free (args); + entrysrc[0] = 0; + grub_free (oldname); +@@ -205,7 +205,7 @@ + } + args[0] = entryname; + grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, +- NULL, NULL, entrysrc, 0); ++ NULL, NULL, entrysrc, 0, 0); + grub_free (args); + } + +--- a/grub-core/commands/menuentry.c ++++ b/grub-core/commands/menuentry.c +@@ -78,7 +78,7 @@ + char **classes, const char *id, + const char *users, const char *hotkey, + const char *prefix, const char *sourcecode, +- int submenu) ++ int submenu, int hidden) + { + int menu_hotkey = 0; + char **menu_args = NULL; +@@ -188,8 +188,11 @@ + (*last)->args = menu_args; + (*last)->sourcecode = menu_sourcecode; + (*last)->submenu = submenu; ++ (*last)->hidden = hidden; ++ ++ if (!hidden) ++ menu->size++; + +- menu->size++; + return GRUB_ERR_NONE; + + fail: +@@ -286,7 +289,8 @@ + users, + ctxt->state[2].arg, 0, + ctxt->state[3].arg, +- ctxt->extcmd->cmd->name[0] == 's'); ++ ctxt->extcmd->cmd->name[0] == 's', ++ ctxt->extcmd->cmd->name[0] == 'h'); + + src = args[argc - 1]; + args[argc - 1] = NULL; +@@ -303,7 +307,8 @@ + ctxt->state[0].args, ctxt->state[4].arg, + users, + ctxt->state[2].arg, prefix, src + 1, +- ctxt->extcmd->cmd->name[0] == 's'); ++ ctxt->extcmd->cmd->name[0] == 's', ++ ctxt->extcmd->cmd->name[0] == 'h'); + + src[len - 1] = ch; + args[argc - 1] = src; +@@ -311,7 +316,7 @@ + return r; + } + +-static grub_extcmd_t cmd, cmd_sub; ++static grub_extcmd_t cmd, cmd_sub, cmd_hidden; + + void + grub_menu_init (void) +@@ -327,6 +332,13 @@ + | GRUB_COMMAND_FLAG_EXTRACTOR, + N_("BLOCK"), N_("Define a submenu."), + options); ++ cmd_hidden = grub_register_extcmd ("hiddenentry", grub_cmd_menuentry, ++ GRUB_COMMAND_FLAG_BLOCKS ++ | GRUB_COMMAND_ACCEPT_DASH ++ | GRUB_COMMAND_FLAG_EXTRACTOR, ++ N_("BLOCK"), ++ N_("Define a hidden menu entry."), ++ options); + } + + void +--- a/grub-core/normal/menu.c ++++ b/grub-core/normal/menu.c +@@ -40,6 +40,8 @@ + grub_err_t (*grub_gfxmenu_try_hook) (int entry, grub_menu_t menu, + int nested) = NULL; + ++#define MENU_INCLUDE_HIDDEN 0x10000 ++ + enum timeout_style { + TIMEOUT_STYLE_MENU, + TIMEOUT_STYLE_COUNTDOWN, +@@ -80,8 +82,20 @@ + { + grub_menu_entry_t e; + +- for (e = menu->entry_list; e && no > 0; e = e->next, no--) +- ; ++ if (no & MENU_INCLUDE_HIDDEN) { ++ no &= ~MENU_INCLUDE_HIDDEN; ++ ++ for (e = menu->entry_list; e && no > 0; e = e->next, no--) ++ ; ++ } else { ++ for (e = menu->entry_list; e && no > 0; e = e->next, no--) { ++ /* Skip hidden entries */ ++ while (e && e->hidden) ++ e = e->next; ++ } ++ while (e && e->hidden) ++ e = e->next; ++ } + + return e; + } +@@ -93,10 +107,10 @@ + grub_menu_entry_t entry; + int i; + +- for (i = 0, entry = menu->entry_list; i < menu->size; ++ for (i = 0, entry = menu->entry_list; entry; + i++, entry = entry->next) + if (entry->hotkey == hotkey) +- return i; ++ return i | MENU_INCLUDE_HIDDEN; + + return -1; + } +@@ -519,6 +533,10 @@ + grub_menu_entry_t e = menu->entry_list; + int i; + ++ /* Skip hidden entries */ ++ while (e && e->hidden) ++ e = e->next; ++ + grub_errno = GRUB_ERR_NONE; + + for (i = 0; e; i++) +@@ -530,6 +548,10 @@ + break; + } + e = e->next; ++ ++ /* Skip hidden entries */ ++ while (e && e->hidden) ++ e = e->next; + } + + if (! e) +--- a/grub-core/normal/menu_text.c ++++ b/grub-core/normal/menu_text.c +@@ -318,6 +318,10 @@ + e, data); + if (e) + e = e->next; ++ ++ /* Skip hidden entries */ ++ while (e && e->hidden) ++ e = e->next; + } + + grub_term_gotoxy (data->term, +--- a/include/grub/menu.h ++++ b/include/grub/menu.h +@@ -58,6 +58,8 @@ + + int submenu; + ++ int hidden; ++ + /* The next element. */ + struct grub_menu_entry *next; + }; +--- a/include/grub/normal.h ++++ b/include/grub/normal.h +@@ -145,7 +145,7 @@ + const char *id, + const char *users, const char *hotkey, + const char *prefix, const char *sourcecode, +- int submenu); ++ int submenu, int hidden); + + grub_err_t + grub_normal_set_password (const char *user, const char *password); diff --git a/grub2-SUSE-Add-the-t-hotkey.patch b/grub2-SUSE-Add-the-t-hotkey.patch new file mode 100644 index 0000000..35daa11 --- /dev/null +++ b/grub2-SUSE-Add-the-t-hotkey.patch @@ -0,0 +1,66 @@ +From f6be3d41e24e685846dfc90ac1ca447501813687 Mon Sep 17 00:00:00 2001 +From: Alexander Graf +Date: Tue, 26 Apr 2016 15:59:03 +0200 +Subject: [PATCH] SUSE: Add the "t" hotkey + +While graphical output is fancy and nice, in some environments (EFI) we can +only have fancy graphical on frame buffer _or_ ugly serial on any output. + +To give the user a nicely graphical screen in the default case, but still +allow them to get their boot menu on the serial console, let's add a new +hidden option "t" that switches the output device back to the firmware default. + +Signed-off-by: Alexander Graf +--- + +v1 -> v2 + + - use hiddenentry instead of --hidden + +v2 -> v3 (by fvogt@suse.de) + + - make it a runtime decision (bsc#1164385) + +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -552,6 +552,12 @@ + installdir = grubconf; + }; + ++script = { ++ name = '95_textmode'; ++ common = util/grub.d/95_textmode.in; ++ installdir = grubconf; ++}; ++ + program = { + mansection = 1; + name = grub-mkrescue; +--- a/util/grub.d/00_header.in ++++ b/util/grub.d/00_header.in +@@ -247,6 +247,10 @@ + fi + + cat << EOF ++ if [ "\${grub_platform}" = "efi" ]; then ++ echo "Please press 't' to show the boot menu on this console" ++ fi ++ + set gfxmode=${GRUB_GFXMODE} + load_video + insmod gfxterm +--- /dev/null ++++ b/util/grub.d/95_textmode.in +@@ -0,0 +1,12 @@ ++#!/bin/sh ++ ++cat < +Subject: grub2/btrfs: Add ability to boot from subvolumes + +This patch adds the ability to specify a different root on a btrfs +filesystem too boot from other than the default one. + +btrfs-list-snapshots will list the subvolumes available on the +filesystem. + +set btrfs_subvol= and set btrfs_subvolid= will specify +which subvolume to use and any pathnames provided with either of those +variables set will start using that root. If the subvolume or subvolume id +doesn't exist, then an error case will result. + +It is possible to boot into a separate GRUB instance by exporting the +variable and loading the config file from the subvolume. + +Signed-off-by: Jeff Mahoney + +V1: + * Use overflow checking primitives where the arithmetic expression for + buffer allocations may include unvalidated data + +V2: + * Fix gcc-12 error: the comparison will always evaluate as 'true' for the + address of 'label' will never be NULL [-Werror=address] + +--- + + grub-core/fs/btrfs.c | 561 +++++++++++++++++++++++++++++++++++++++++++++++++-- + include/grub/btrfs.h | 1 + 2 files changed, 544 insertions(+), 18 deletions(-) + +--- a/grub-core/fs/btrfs.c ++++ b/grub-core/fs/btrfs.c +@@ -41,6 +41,9 @@ + #include + #include + #include ++#include ++#include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -79,9 +82,11 @@ + grub_uint64_t generation; + grub_uint64_t root_tree; + grub_uint64_t chunk_tree; +- grub_uint8_t dummy2[0x20]; ++ grub_uint8_t dummy2[0x18]; ++ grub_uint64_t bytes_used; + grub_uint64_t root_dir_objectid; +- grub_uint8_t dummy3[0x41]; ++ grub_uint64_t num_devices; ++ grub_uint8_t dummy3[0x39]; + struct grub_btrfs_device this_device; + char label[0x100]; + grub_uint8_t dummy4[0x100]; +@@ -121,6 +126,7 @@ + grub_uint64_t exttree; + grub_size_t extsize; + struct grub_btrfs_extent_data *extent; ++ grub_uint64_t fs_tree; + }; + + struct grub_btrfs_chunk_item +@@ -191,6 +197,14 @@ + } *data; + }; + ++struct grub_btrfs_root_ref ++{ ++ grub_uint64_t dirid; ++ grub_uint64_t sequence; ++ grub_uint16_t name_len; ++ const char name[0]; ++} __attribute__ ((packed)); ++ + struct grub_btrfs_time + { + grub_int64_t sec; +@@ -236,6 +250,14 @@ + + #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100 + ++#define GRUB_BTRFS_ROOT_TREE_OBJECTID 1ULL ++#define GRUB_BTRFS_FS_TREE_OBJECTID 5ULL ++#define GRUB_BTRFS_ROOT_REF_KEY 156 ++#define GRUB_BTRFS_ROOT_ITEM_KEY 132 ++ ++static grub_uint64_t btrfs_default_subvolid = 0; ++static char *btrfs_default_subvol = NULL; ++ + static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2, + 256 * 1048576 * 2, 1048576ULL * 1048576ULL * 2 + }; +@@ -1252,6 +1274,62 @@ + return GRUB_ERR_NONE; + } + ++static grub_err_t ++get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, ++ grub_uint64_t objectid, grub_uint64_t offset, ++ grub_uint64_t *fs_root); ++ ++static grub_err_t ++lookup_root_by_id(struct grub_btrfs_data *data, grub_uint64_t id) ++{ ++ grub_err_t err; ++ grub_uint64_t tree; ++ ++ err = get_fs_root(data, data->sblock.root_tree, id, -1, &tree); ++ if (!err) ++ data->fs_tree = tree; ++ return err; ++} ++ ++static grub_err_t ++find_path (struct grub_btrfs_data *data, ++ const char *path, struct grub_btrfs_key *key, ++ grub_uint64_t *tree, grub_uint8_t *type); ++ ++static grub_err_t ++lookup_root_by_name(struct grub_btrfs_data *data, const char *path) ++{ ++ grub_err_t err; ++ grub_uint64_t tree = 0; ++ grub_uint8_t type; ++ struct grub_btrfs_key key; ++ ++ err = find_path (data, path, &key, &tree, &type); ++ if (err) ++ return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path); ++ ++ if (key.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) ++ return grub_error(GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", path); ++ ++ data->fs_tree = tree; ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++btrfs_handle_subvol(struct grub_btrfs_data *data __attribute__ ((unused))) ++{ ++ if (btrfs_default_subvol) ++ return lookup_root_by_name(data, btrfs_default_subvol); ++ ++ if (btrfs_default_subvolid) ++ return lookup_root_by_id(data, btrfs_default_subvolid); ++ ++ data->fs_tree = 0; ++ ++ return GRUB_ERR_NONE; ++} ++ ++ + static struct grub_btrfs_data * + grub_btrfs_mount (grub_device_t dev) + { +@@ -1287,6 +1365,13 @@ + data->devices_attached[0].dev = dev; + data->devices_attached[0].id = data->sblock.this_device.device_id; + ++ err = btrfs_handle_subvol (data); ++ if (err) ++ { ++ grub_free (data); ++ return NULL; ++ } ++ + return data; + } + +@@ -1783,6 +1868,98 @@ + } + + static grub_err_t ++find_pathname(struct grub_btrfs_data *data, grub_uint64_t objectid, ++ grub_uint64_t fs_root, const char *name, char **pathname) ++{ ++ grub_err_t err; ++ struct grub_btrfs_key key = { ++ .object_id = objectid, ++ .type = GRUB_BTRFS_ITEM_TYPE_INODE_REF, ++ .offset = 0, ++ }; ++ struct grub_btrfs_key key_out; ++ struct grub_btrfs_leaf_descriptor desc; ++ char *p = grub_strdup (name); ++ grub_disk_addr_t elemaddr; ++ grub_size_t elemsize; ++ grub_size_t alloc = grub_strlen(name) + 1; ++ ++ err = lower_bound(data, &key, &key_out, fs_root, ++ &elemaddr, &elemsize, &desc, 0); ++ if (err) ++ return grub_error(err, "lower_bound caught %d\n", err); ++ ++ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) ++ next(data, &desc, &elemaddr, &elemsize, &key_out); ++ ++ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) ++ { ++ return grub_error(GRUB_ERR_FILE_NOT_FOUND, ++ "Can't find inode ref for {%"PRIuGRUB_UINT64_T ++ ", %u, %"PRIuGRUB_UINT64_T"} %"PRIuGRUB_UINT64_T ++ "/%"PRIuGRUB_SIZE"\n", ++ key_out.object_id, key_out.type, ++ key_out.offset, elemaddr, elemsize); ++ } ++ ++ ++ while (key_out.type == GRUB_BTRFS_ITEM_TYPE_INODE_REF && ++ key_out.object_id != key_out.offset) { ++ struct grub_btrfs_inode_ref *inode_ref; ++ char *new; ++ grub_size_t sz; ++ ++ if (grub_add (elemsize, 1, &sz)) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); ++ ++ inode_ref = grub_malloc(sz); ++ if (!inode_ref) ++ return grub_error(GRUB_ERR_OUT_OF_MEMORY, ++ "couldn't allocate memory for inode_ref (%"PRIuGRUB_SIZE")\n", elemsize); ++ ++ err = grub_btrfs_read_logical(data, elemaddr, inode_ref, elemsize, 0); ++ if (err) ++ return grub_error(err, "read_logical caught %d\n", err); ++ ++ if (grub_add (grub_le_to_cpu16 (inode_ref->n), 2, &sz) || ++ grub_add (alloc, sz, &alloc)) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); ++ ++ new = grub_malloc(alloc); ++ if (!new) ++ return grub_error(GRUB_ERR_OUT_OF_MEMORY, ++ "couldn't allocate memory for name (%"PRIuGRUB_SIZE")\n", alloc); ++ ++ grub_memcpy(new, inode_ref->name, grub_le_to_cpu16 (inode_ref->n)); ++ if (p) ++ { ++ new[grub_le_to_cpu16 (inode_ref->n)] = '/'; ++ grub_strcpy (new + grub_le_to_cpu16 (inode_ref->n) + 1, p); ++ grub_free(p); ++ } ++ else ++ new[grub_le_to_cpu16 (inode_ref->n)] = 0; ++ grub_free(inode_ref); ++ ++ p = new; ++ ++ key.object_id = key_out.offset; ++ ++ err = lower_bound(data, &key, &key_out, fs_root, &elemaddr, ++ &elemsize, &desc, 0); ++ if (err) ++ return grub_error(err, "lower_bound caught %d\n", err); ++ ++ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) ++ next(data, &desc, &elemaddr, &elemsize, &key_out); ++ ++ } ++ ++ *pathname = p; ++ return 0; ++} ++ ++static grub_err_t + find_path (struct grub_btrfs_data *data, + const char *path, struct grub_btrfs_key *key, + grub_uint64_t *tree, grub_uint8_t *type) +@@ -1800,14 +1977,26 @@ + char *origpath = NULL; + unsigned symlinks_max = 32; + +- err = get_root (data, key, tree, type); +- if (err) +- return err; +- + origpath = grub_strdup (path); + if (!origpath) + return grub_errno; + ++ if (data->fs_tree) ++ { ++ *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; ++ *tree = data->fs_tree; ++ /* This is a tree root, so everything starts at objectid 256 */ ++ key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); ++ key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; ++ key->offset = 0; ++ } ++ else ++ { ++ err = get_root (data, key, tree, type); ++ if (err) ++ return err; ++ } ++ + while (1) + { + while (path[0] == '/') +@@ -1980,13 +2169,25 @@ + path = path_alloc = tmp; + if (path[0] == '/') + { +- err = get_root (data, key, tree, type); +- if (err) ++ if (data->fs_tree) + { +- grub_free (direl); +- grub_free (path_alloc); +- grub_free (origpath); +- return err; ++ *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; ++ *tree = data->fs_tree; ++ /* This is a tree root, so everything starts at objectid 256 */ ++ key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); ++ key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; ++ key->offset = 0; ++ } ++ else ++ { ++ err = get_root (data, key, tree, type); ++ if (err) ++ { ++ grub_free (direl); ++ grub_free (path_alloc); ++ grub_free (origpath); ++ return err; ++ } + } + } + continue; +@@ -2254,6 +2455,20 @@ + data->tree, file->offset, buf, len); + } + ++static char * ++btrfs_unparse_uuid(struct grub_btrfs_data *data) ++{ ++ return grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", ++ grub_be_to_cpu16 (data->sblock.uuid[0]), ++ grub_be_to_cpu16 (data->sblock.uuid[1]), ++ grub_be_to_cpu16 (data->sblock.uuid[2]), ++ grub_be_to_cpu16 (data->sblock.uuid[3]), ++ grub_be_to_cpu16 (data->sblock.uuid[4]), ++ grub_be_to_cpu16 (data->sblock.uuid[5]), ++ grub_be_to_cpu16 (data->sblock.uuid[6]), ++ grub_be_to_cpu16 (data->sblock.uuid[7])); ++} ++ + static grub_err_t + grub_btrfs_uuid (grub_device_t device, char **uuid) + { +@@ -2265,15 +2480,7 @@ + if (!data) + return grub_errno; + +- *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", +- grub_be_to_cpu16 (data->sblock.uuid[0]), +- grub_be_to_cpu16 (data->sblock.uuid[1]), +- grub_be_to_cpu16 (data->sblock.uuid[2]), +- grub_be_to_cpu16 (data->sblock.uuid[3]), +- grub_be_to_cpu16 (data->sblock.uuid[4]), +- grub_be_to_cpu16 (data->sblock.uuid[5]), +- grub_be_to_cpu16 (data->sblock.uuid[6]), +- grub_be_to_cpu16 (data->sblock.uuid[7])); ++ *uuid = btrfs_unparse_uuid(data); + + grub_btrfs_unmount (data); + +@@ -2394,6 +2601,248 @@ + } + #endif + ++static grub_err_t ++grub_cmd_btrfs_info (grub_command_t cmd __attribute__ ((unused)), int argc, ++ char **argv) ++{ ++ grub_device_t dev; ++ char *devname; ++ struct grub_btrfs_data *data; ++ char *uuid; ++ ++ if (argc < 1) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); ++ ++ devname = grub_file_get_device_name(argv[0]); ++ ++ if (!devname) ++ return grub_errno; ++ ++ dev = grub_device_open (devname); ++ grub_free (devname); ++ if (!dev) ++ return grub_errno; ++ ++ data = grub_btrfs_mount (dev); ++ if (!data) ++ { ++ grub_device_close(dev); ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to open fs"); ++ } ++ ++ if (*data->sblock.label != '\0') ++ grub_printf("Label: '%s' ", data->sblock.label); ++ else ++ grub_printf("Label: none "); ++ ++ uuid = btrfs_unparse_uuid(data); ++ ++ grub_printf(" uuid: %s\n\tTotal devices %" PRIuGRUB_UINT64_T ++ " FS bytes used %" PRIuGRUB_UINT64_T "\n", ++ uuid, grub_cpu_to_le64(data->sblock.num_devices), ++ grub_cpu_to_le64(data->sblock.bytes_used)); ++ ++ grub_btrfs_unmount (data); ++ ++ return 0; ++} ++ ++static grub_err_t ++get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, ++ grub_uint64_t objectid, grub_uint64_t offset, ++ grub_uint64_t *fs_root) ++{ ++ grub_err_t err; ++ struct grub_btrfs_key key_in = { ++ .object_id = objectid, ++ .type = GRUB_BTRFS_ROOT_ITEM_KEY, ++ .offset = offset, ++ }, key_out; ++ struct grub_btrfs_leaf_descriptor desc; ++ grub_disk_addr_t elemaddr; ++ grub_size_t elemsize; ++ struct grub_btrfs_root_item ri; ++ ++ err = lower_bound(data, &key_in, &key_out, tree, ++ &elemaddr, &elemsize, &desc, 0); ++ ++ if (err) ++ return err; ++ ++ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM || elemaddr == 0) ++ return grub_error(GRUB_ERR_FILE_NOT_FOUND, ++ N_("can't find fs root for subvol %"PRIuGRUB_UINT64_T"\n"), ++ key_in.object_id); ++ ++ err = grub_btrfs_read_logical (data, elemaddr, &ri, sizeof (ri), 0); ++ if (err) ++ return err; ++ ++ *fs_root = ri.tree; ++ ++ return GRUB_ERR_NONE; ++} ++ ++static const struct grub_arg_option options[] = { ++ {"output", 'o', 0, N_("Output to a variable instead of the console."), ++ N_("VARNAME"), ARG_TYPE_STRING}, ++ {"path-only", 'p', 0, N_("Show only the path of the subvolume."), 0, 0}, ++ {"id-only", 'i', 0, N_("Show only the id of the subvolume."), 0, 0}, ++ {0, 0, 0, 0, 0, 0} ++}; ++ ++static grub_err_t ++grub_cmd_btrfs_list_subvols (struct grub_extcmd_context *ctxt, ++ int argc, char **argv) ++{ ++ struct grub_btrfs_data *data; ++ grub_device_t dev; ++ char *devname; ++ grub_uint64_t tree; ++ struct grub_btrfs_key key_in = { ++ .object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), ++ .type = GRUB_BTRFS_ROOT_REF_KEY, ++ .offset = 0, ++ }, key_out; ++ struct grub_btrfs_leaf_descriptor desc; ++ grub_disk_addr_t elemaddr; ++ grub_uint64_t fs_root = 0; ++ grub_size_t elemsize; ++ grub_size_t allocated = 0; ++ int r = 0; ++ grub_err_t err; ++ char *buf = NULL; ++ int print = 1; ++ int path_only = ctxt->state[1].set; ++ int num_only = ctxt->state[2].set; ++ char *varname = NULL; ++ char *output = NULL; ++ ++ if (ctxt->state[0].set) { ++ varname = ctxt->state[0].arg; ++ print = 0; ++ } ++ ++ if (argc < 1) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); ++ ++ devname = grub_file_get_device_name(argv[0]); ++ if (!devname) ++ return grub_errno; ++ ++ dev = grub_device_open (devname); ++ grub_free (devname); ++ if (!dev) ++ return grub_errno; ++ ++ data = grub_btrfs_mount(dev); ++ if (!data) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "could not open device"); ++ ++ tree = data->sblock.root_tree; ++ err = get_fs_root(data, tree, grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), ++ 0, &fs_root); ++ if (err) ++ goto out; ++ ++ err = lower_bound(data, &key_in, &key_out, tree, ++ &elemaddr, &elemsize, &desc, 0); ++ ++ if (err) ++ { ++ grub_btrfs_unmount(data); ++ return err; ++ } ++ ++ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF || elemaddr == 0) ++ { ++ r = next(data, &desc, &elemaddr, &elemsize, &key_out); ++ } ++ ++ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) { ++ err = GRUB_ERR_FILE_NOT_FOUND; ++ grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root refs")); ++ goto out; ++ } ++ ++ do ++ { ++ struct grub_btrfs_root_ref *ref; ++ char *p = NULL; ++ ++ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) ++ { ++ r = 0; ++ break; ++ } ++ ++ if (elemsize > allocated) ++ { ++ grub_size_t sz; ++ ++ grub_free(buf); ++ ++ if (grub_mul (elemsize, 2, &allocated) || ++ grub_add (allocated, 1, &sz)) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); ++ ++ buf = grub_malloc(sz); ++ if (!buf) ++ { ++ r = -grub_errno; ++ break; ++ } ++ } ++ ref = (struct grub_btrfs_root_ref *)buf; ++ ++ err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0); ++ if (err) ++ { ++ r = -err; ++ break; ++ } ++ buf[elemsize] = 0; ++ ++ find_pathname(data, ref->dirid, fs_root, ref->name, &p); ++ ++ if (print) ++ { ++ if (num_only) ++ grub_printf("ID %"PRIuGRUB_UINT64_T"\n", key_out.offset); ++ else if (path_only) ++ grub_printf("%s\n", p); ++ else ++ grub_printf("ID %"PRIuGRUB_UINT64_T" path %s\n", key_out.offset, p); ++ } else { ++ char *old = output; ++ if (num_only) ++ output = grub_xasprintf("%s%"PRIuGRUB_UINT64_T"\n", ++ old ?: "", key_out.offset); ++ else if (path_only) ++ output = grub_xasprintf("%s%s\n", old ?: "", p); ++ else ++ output = grub_xasprintf("%sID %"PRIuGRUB_UINT64_T" path %s\n", ++ old ?: "", key_out.offset, p); ++ ++ if (old) ++ grub_free(old); ++ } ++ ++ r = next(data, &desc, &elemaddr, &elemsize, &key_out); ++ } while(r > 0); ++ ++ if (output) ++ grub_env_set(varname, output); ++ ++out: ++ free_iterator(&desc); ++ grub_btrfs_unmount(data); ++ ++ grub_device_close (dev); ++ ++ return 0; ++} ++ + static struct grub_fs grub_btrfs_fs = { + .name = "btrfs", + .fs_dir = grub_btrfs_dir, +@@ -2409,12 +2858,88 @@ + #endif + }; + ++static grub_command_t cmd_info; ++static grub_extcmd_t cmd_list_subvols; ++ ++static char * ++subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)), ++ const char *val) ++{ ++ unsigned long long result = 0; ++ ++ grub_errno = GRUB_ERR_NONE; ++ if (*val) ++ { ++ result = grub_strtoull(val, NULL, 10); ++ if (grub_errno) ++ return NULL; ++ } ++ ++ grub_free (btrfs_default_subvol); ++ btrfs_default_subvol = NULL; ++ btrfs_default_subvolid = result; ++ return grub_strdup(val); ++} ++ ++static const char * ++subvolid_get_env (struct grub_env_var *var __attribute__ ((unused)), ++ const char *val __attribute__ ((unused))) ++{ ++ if (btrfs_default_subvol) ++ return grub_xasprintf("subvol:%s", btrfs_default_subvol); ++ else if (btrfs_default_subvolid) ++ return grub_xasprintf("%"PRIuGRUB_UINT64_T, btrfs_default_subvolid); ++ else ++ return ""; ++} ++ ++static char * ++subvol_set_env (struct grub_env_var *var __attribute__ ((unused)), ++ const char *val) ++{ ++ grub_free (btrfs_default_subvol); ++ btrfs_default_subvol = grub_strdup (val); ++ btrfs_default_subvolid = 0; ++ return grub_strdup(val); ++} ++ ++static const char * ++subvol_get_env (struct grub_env_var *var __attribute__ ((unused)), ++ const char *val __attribute__ ((unused))) ++{ ++ if (btrfs_default_subvol) ++ return btrfs_default_subvol; ++ else if (btrfs_default_subvolid) ++ return grub_xasprintf("subvolid:%" PRIuGRUB_UINT64_T, ++ btrfs_default_subvolid); ++ else ++ return ""; ++} ++ + GRUB_MOD_INIT (btrfs) + { + grub_fs_register (&grub_btrfs_fs); ++ cmd_info = grub_register_command("btrfs-info", grub_cmd_btrfs_info, ++ "DEVICE", ++ "Print BtrFS info about DEVICE."); ++ cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols", ++ grub_cmd_btrfs_list_subvols, 0, ++ "[-p|-n] [-o var] DEVICE", ++ "Print list of BtrFS subvolumes on " ++ "DEVICE.", options); ++ grub_register_variable_hook ("btrfs_subvol", subvol_get_env, ++ subvol_set_env); ++ grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, ++ subvolid_set_env); + } + + GRUB_MOD_FINI (btrfs) + { ++ grub_register_variable_hook ("btrfs_subvol", NULL, NULL); ++ grub_register_variable_hook ("btrfs_subvolid", NULL, NULL); ++ grub_unregister_command (cmd_info); ++ grub_unregister_extcmd (cmd_list_subvols); + grub_fs_unregister (&grub_btrfs_fs); + } ++ ++// vim: si et sw=2: +--- a/include/grub/btrfs.h ++++ b/include/grub/btrfs.h +@@ -29,6 +29,7 @@ + GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM = 0x84, + GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF = 0x90, + GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8, ++ GRUB_BTRFS_ITEM_TYPE_ROOT_REF = 0x9c, + GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4 + }; + diff --git a/grub2-btrfs-02-export-subvolume-envvars.patch b/grub2-btrfs-02-export-subvolume-envvars.patch new file mode 100644 index 0000000..292870a --- /dev/null +++ b/grub2-btrfs-02-export-subvolume-envvars.patch @@ -0,0 +1,19 @@ +From: Michael Chang +Subject: export btrfs_subvol and btrfs_subvolid + +We should export btrfs_subvol and btrfs_subvolid to have both visible +to subsidiary configuration files loaded using configfile. + +Signed-off-by: Michael Chang + +--- a/grub-core/fs/btrfs.c ++++ b/grub-core/fs/btrfs.c +@@ -2931,6 +2931,8 @@ + subvol_set_env); + grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, + subvolid_set_env); ++ grub_env_export ("btrfs_subvol"); ++ grub_env_export ("btrfs_subvolid"); + } + + GRUB_MOD_FINI (btrfs) diff --git a/grub2-btrfs-03-follow_default.patch b/grub2-btrfs-03-follow_default.patch new file mode 100644 index 0000000..f207dbe --- /dev/null +++ b/grub2-btrfs-03-follow_default.patch @@ -0,0 +1,185 @@ +--- a/grub-core/fs/btrfs.c ++++ b/grub-core/fs/btrfs.c +@@ -1335,6 +1335,7 @@ + { + struct grub_btrfs_data *data; + grub_err_t err; ++ const char *relpath = grub_env_get ("btrfs_relative_path"); + + if (!dev->disk) + { +@@ -1365,11 +1366,14 @@ + data->devices_attached[0].dev = dev; + data->devices_attached[0].id = data->sblock.this_device.device_id; + +- err = btrfs_handle_subvol (data); +- if (err) ++ if (relpath && (relpath[0] == '1' || relpath[0] == 'y')) + { +- grub_free (data); +- return NULL; ++ err = btrfs_handle_subvol (data); ++ if (err) ++ { ++ grub_free (data); ++ return NULL; ++ } + } + + return data; +@@ -1971,24 +1975,39 @@ + grub_size_t allocated = 0; + struct grub_btrfs_dir_item *direl = NULL; + struct grub_btrfs_key key_out; ++ int follow_default; + const char *ctoken; + grub_size_t ctokenlen; + char *path_alloc = NULL; + char *origpath = NULL; + unsigned symlinks_max = 32; ++ const char *relpath = grub_env_get ("btrfs_relative_path"); + ++ follow_default = 0; + origpath = grub_strdup (path); + if (!origpath) + return grub_errno; + +- if (data->fs_tree) ++ if (relpath && (relpath[0] == '1' || relpath[0] == 'y')) + { +- *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; +- *tree = data->fs_tree; +- /* This is a tree root, so everything starts at objectid 256 */ +- key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); +- key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; +- key->offset = 0; ++ if (data->fs_tree) ++ { ++ *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; ++ *tree = data->fs_tree; ++ /* This is a tree root, so everything starts at objectid 256 */ ++ key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); ++ key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; ++ key->offset = 0; ++ } ++ else ++ { ++ *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; ++ *tree = data->sblock.root_tree; ++ key->object_id = data->sblock.root_dir_objectid; ++ key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; ++ key->offset = 0; ++ follow_default = 1; ++ } + } + else + { +@@ -1999,15 +2018,23 @@ + + while (1) + { +- while (path[0] == '/') +- path++; +- if (!path[0]) +- break; +- slash = grub_strchr (path, '/'); +- if (!slash) +- slash = path + grub_strlen (path); +- ctoken = path; +- ctokenlen = slash - path; ++ if (!follow_default) ++ { ++ while (path[0] == '/') ++ path++; ++ if (!path[0]) ++ break; ++ slash = grub_strchr (path, '/'); ++ if (!slash) ++ slash = path + grub_strlen (path); ++ ctoken = path; ++ ctokenlen = slash - path; ++ } ++ else ++ { ++ ctoken = "default"; ++ ctokenlen = sizeof ("default") - 1; ++ } + + if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) + { +@@ -2018,7 +2045,9 @@ + + if (ctokenlen == 1 && ctoken[0] == '.') + { +- path = slash; ++ if (!follow_default) ++ path = slash; ++ follow_default = 0; + continue; + } + if (ctokenlen == 2 && ctoken[0] == '.' && ctoken[1] == '.') +@@ -2049,8 +2078,9 @@ + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + key->object_id = key_out.offset; + +- path = slash; +- ++ if (!follow_default) ++ path = slash; ++ follow_default = 0; + continue; + } + +@@ -2119,7 +2149,9 @@ + return err; + } + +- path = slash; ++ if (!follow_default) ++ path = slash; ++ follow_default = 0; + if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK) + { + struct grub_btrfs_inode inode; +@@ -2169,14 +2201,26 @@ + path = path_alloc = tmp; + if (path[0] == '/') + { +- if (data->fs_tree) ++ if (relpath && (relpath[0] == '1' || relpath[0] == 'y')) + { +- *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; +- *tree = data->fs_tree; +- /* This is a tree root, so everything starts at objectid 256 */ +- key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); +- key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; +- key->offset = 0; ++ if (data->fs_tree) ++ { ++ *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; ++ *tree = data->fs_tree; ++ /* This is a tree root, so everything starts at objectid 256 */ ++ key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); ++ key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; ++ key->offset = 0; ++ } ++ else ++ { ++ *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; ++ *tree = data->sblock.root_tree; ++ key->object_id = data->sblock.root_dir_objectid; ++ key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; ++ key->offset = 0; ++ follow_default = 1; ++ } + } + else + { +@@ -2933,6 +2977,7 @@ + subvolid_set_env); + grub_env_export ("btrfs_subvol"); + grub_env_export ("btrfs_subvolid"); ++ grub_env_export ("btrfs_relative_path"); + } + + GRUB_MOD_FINI (btrfs) diff --git a/grub2-btrfs-04-grub2-install.patch b/grub2-btrfs-04-grub2-install.patch new file mode 100644 index 0000000..5bdce56 --- /dev/null +++ b/grub2-btrfs-04-grub2-install.patch @@ -0,0 +1,148 @@ +--- a/grub-core/osdep/unix/config.c ++++ b/grub-core/osdep/unix/config.c +@@ -207,6 +207,19 @@ + if (v) + cfg->grub_distributor = xstrdup (v); + ++ v = getenv ("SUSE_BTRFS_SNAPSHOT_BOOTING"); ++ if (v) ++ { ++ if (grub_strncmp(v, "true", sizeof ("true") - 1) == 0) ++ { ++ cfg->is_suse_btrfs_snapshot_enabled = 1; ++ } ++ else ++ { ++ cfg->is_suse_btrfs_snapshot_enabled = 0; ++ } ++ } ++ + cfgfile = grub_util_get_config_filename (); + if (!grub_util_is_regular (cfgfile)) + return; +@@ -230,8 +243,8 @@ + *ptr++ = *iptr; + } + +- strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\n\" " +- "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\""); ++ strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\nSUSE_BTRFS_SNAPSHOT_BOOTING=%s\\n\" " ++ "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\" \"$SUSE_BTRFS_SNAPSHOT_BOOTING\""); + + argv[2] = script; + argv[3] = '\0'; +--- a/include/grub/emu/config.h ++++ b/include/grub/emu/config.h +@@ -37,6 +37,7 @@ + { + int is_cryptodisk_enabled; + char *grub_distributor; ++ int is_suse_btrfs_snapshot_enabled; + }; + + void +--- a/util/config.c ++++ b/util/config.c +@@ -42,6 +42,16 @@ + cfg->is_cryptodisk_enabled = 1; + continue; + } ++ if (grub_strncmp (ptr, "SUSE_BTRFS_SNAPSHOT_BOOTING=", ++ sizeof ("SUSE_BTRFS_SNAPSHOT_BOOTING=") - 1) == 0) ++ { ++ ptr += sizeof ("SUSE_BTRFS_SNAPSHOT_BOOTING=") - 1; ++ if (*ptr == '"' || *ptr == '\'') ++ ptr++; ++ if (grub_strncmp(ptr, "true", sizeof ("true") - 1) == 0) ++ cfg->is_suse_btrfs_snapshot_enabled = 1; ++ continue; ++ } + if (grub_strncmp (ptr, "GRUB_DISTRIBUTOR=", + sizeof ("GRUB_DISTRIBUTOR=") - 1) == 0) + { +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -857,6 +857,8 @@ + } + #endif + ++extern int use_relative_path_on_btrfs; ++ + int + main (int argc, char *argv[]) + { +@@ -890,6 +892,9 @@ + + grub_util_load_config (&config); + ++ if (config.is_suse_btrfs_snapshot_enabled) ++ use_relative_path_on_btrfs = 1; ++ + if (!bootloader_id && config.grub_distributor) + { + char *ptr; +@@ -1426,6 +1431,16 @@ + fprintf (load_cfg_f, "set debug='%s'\n", + debug_image); + } ++ ++ if (config.is_suse_btrfs_snapshot_enabled ++ && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0) ++ { ++ if (!load_cfg_f) ++ load_cfg_f = grub_util_fopen (load_cfg, "wb"); ++ have_load_cfg = 1; ++ fprintf (load_cfg_f, "set btrfs_relative_path='y'\n"); ++ } ++ + char *prefix_drive = NULL; + char *install_drive = NULL; + +--- a/grub-core/osdep/linux/getroot.c ++++ b/grub-core/osdep/linux/getroot.c +@@ -373,6 +373,7 @@ + return NULL; + } + ++int use_relative_path_on_btrfs = 0; + + char ** + grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) +@@ -516,6 +517,12 @@ + { + ret = grub_find_root_devices_from_btrfs (dir); + fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path); ++ if (use_relative_path_on_btrfs) ++ { ++ if (fs_prefix) ++ free (fs_prefix); ++ fs_prefix = xstrdup ("/"); ++ } + } + else if (!retry && grub_strcmp (entries[i].fstype, "autofs") == 0) + { +--- a/util/grub-mkrelpath.c ++++ b/util/grub-mkrelpath.c +@@ -40,9 +40,12 @@ + }; + + static struct argp_option options[] = { ++ {"relative", 'r', 0, 0, "use relative path on btrfs", 0}, + { 0, 0, 0, 0, 0, 0 } + }; + ++extern int use_relative_path_on_btrfs; ++ + static error_t + argp_parser (int key, char *arg, struct argp_state *state) + { +@@ -52,6 +55,9 @@ + + switch (key) + { ++ case 'r': ++ use_relative_path_on_btrfs = 1; ++ break; + case ARGP_KEY_ARG: + if (state->arg_num == 0) + arguments->pathname = xstrdup (arg); diff --git a/grub2-btrfs-05-grub2-mkconfig.patch b/grub2-btrfs-05-grub2-mkconfig.patch new file mode 100644 index 0000000..4a1e498 --- /dev/null +++ b/grub2-btrfs-05-grub2-mkconfig.patch @@ -0,0 +1,137 @@ + +Always declare path specification in case of inconsistent declaration +elsewhere. (bsc#1209165) + +--- + util/grub-mkconfig.in | 3 ++- + util/grub-mkconfig_lib.in | 4 ++++ + util/grub.d/00_header.in | 23 ++++++++++++++++++++++- + util/grub.d/10_linux.in | 11 ++++++++++- + util/grub.d/20_linux_xen.in | 4 ++++ + 5 files changed, 42 insertions(+), 3 deletions(-) + +--- a/util/grub-mkconfig_lib.in ++++ b/util/grub-mkconfig_lib.in +@@ -49,7 +49,11 @@ + + make_system_path_relative_to_its_root () + { ++ if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] ; then ++ "${grub_mkrelpath}" -r "$1" ++ else + "${grub_mkrelpath}" "$1" ++ fi + } + + is_path_readable_by_grub () +--- a/util/grub.d/00_header.in ++++ b/util/grub.d/00_header.in +@@ -27,6 +27,21 @@ + + . "$pkgdatadir/grub-mkconfig_lib" + ++if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] && ++ [ "x${GRUB_FS}" = "xbtrfs" ] ; then ++ cat </dev/null || true` +@@ -295,7 +299,12 @@ + if [ $PLATFORM != "emu" ]; then + hotkey=0 + else +- rel_dirname=$dirname ++ if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] && ++ [ "x${GRUB_FS}" = "xbtrfs" ] ; then ++ rel_dirname="\${btrfs_subvol}$dirname" ++ else ++ rel_dirname="$dirname" ++ fi + fi + version=`echo $basename | sed -e "s,^[^0-9]*-,,g"` + alt_version=`echo $version | sed -e "s,\.old$,,g"` +--- a/util/grub.d/20_linux_xen.in ++++ b/util/grub.d/20_linux_xen.in +@@ -81,10 +81,14 @@ + + case x"$GRUB_FS" in + xbtrfs) ++ if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ]; then ++ GRUB_CMDLINE_LINUX="${GRUB_CMDLINE_LINUX} \${extra_cmdline}" ++ else + rootsubvol="`make_system_path_relative_to_its_root /`" + rootsubvol="${rootsubvol#/}" + if [ "x${rootsubvol}" != x ]; then + GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}" ++ fi + fi;; + xzfs) + rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true` diff --git a/grub2-btrfs-06-subvol-mount.patch b/grub2-btrfs-06-subvol-mount.patch new file mode 100644 index 0000000..eabffd3 --- /dev/null +++ b/grub2-btrfs-06-subvol-mount.patch @@ -0,0 +1,521 @@ + +V2: +* Fix grub2-install --root-directory does not work for /boot/grub2/ on + separate btrfs subvolume (boo#1098420) + +v3: +* Fix executable stack on which function trampoline is constructed to support + closure (nested function). The closure sematic is replaced. + +--- a/grub-core/fs/btrfs.c ++++ b/grub-core/fs/btrfs.c +@@ -44,6 +44,7 @@ + #include + #include + #include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -266,6 +267,12 @@ + grub_btrfs_read_logical (struct grub_btrfs_data *data, + grub_disk_addr_t addr, void *buf, grub_size_t size, + int recursion_depth); ++static grub_err_t ++get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key, ++ grub_uint64_t *tree, grub_uint8_t *type); ++ ++grub_uint64_t ++find_mtab_subvol_tree (const char *path, char **path_in_subvol); + + static grub_err_t + read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb) +@@ -1302,9 +1309,26 @@ + grub_err_t err; + grub_uint64_t tree = 0; + grub_uint8_t type; ++ grub_uint64_t saved_tree; + struct grub_btrfs_key key; + ++ if (path[0] == '\0') ++ { ++ data->fs_tree = 0; ++ return GRUB_ERR_NONE; ++ } ++ ++ err = get_root (data, &key, &tree, &type); ++ if (err) ++ return err; ++ ++ saved_tree = data->fs_tree; ++ data->fs_tree = tree; ++ + err = find_path (data, path, &key, &tree, &type); ++ ++ data->fs_tree = saved_tree; ++ + if (err) + return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path); + +@@ -2321,11 +2345,20 @@ + grub_uint64_t tree; + grub_uint8_t type; + grub_size_t est_size = 0; ++ char *new_path = NULL; + + if (!data) + return grub_errno; + +- err = find_path (data, path, &key_in, &tree, &type); ++ tree = find_mtab_subvol_tree (path, &new_path); ++ ++ if (tree) ++ data->fs_tree = tree; ++ ++ err = find_path (data, new_path ? new_path : path, &key_in, &tree, &type); ++ if (new_path) ++ grub_free (new_path); ++ + if (err) + { + grub_btrfs_unmount (data); +@@ -2452,11 +2485,21 @@ + struct grub_btrfs_inode inode; + grub_uint8_t type; + struct grub_btrfs_key key_in; ++ grub_uint64_t tree; ++ char *new_path = NULL; + + if (!data) + return grub_errno; + +- err = find_path (data, name, &key_in, &data->tree, &type); ++ tree = find_mtab_subvol_tree (name, &new_path); ++ ++ if (tree) ++ data->fs_tree = tree; ++ ++ err = find_path (data, new_path ? new_path : name, &key_in, &data->tree, &type); ++ if (new_path) ++ grub_free (new_path); ++ + if (err) + { + grub_btrfs_unmount (data); +@@ -2691,6 +2734,150 @@ + return 0; + } + ++struct grub_btrfs_mtab ++{ ++ struct grub_btrfs_mtab *next; ++ struct grub_btrfs_mtab **prev; ++ char *path; ++ char *subvol; ++ grub_uint64_t tree; ++}; ++ ++typedef struct grub_btrfs_mtab* grub_btrfs_mtab_t; ++ ++static struct grub_btrfs_mtab *btrfs_mtab; ++ ++#define FOR_GRUB_MTAB(var) FOR_LIST_ELEMENTS (var, btrfs_mtab) ++#define FOR_GRUB_MTAB_SAFE(var, next) FOR_LIST_ELEMENTS_SAFE((var), (next), btrfs_mtab) ++ ++static void ++add_mountpoint (const char *path, const char *subvol, grub_uint64_t tree) ++{ ++ grub_btrfs_mtab_t m = grub_malloc (sizeof (*m)); ++ ++ m->path = grub_strdup (path); ++ m->subvol = grub_strdup (subvol); ++ m->tree = tree; ++ grub_list_push (GRUB_AS_LIST_P (&btrfs_mtab), GRUB_AS_LIST (m)); ++} ++ ++static grub_err_t ++grub_cmd_btrfs_mount_subvol (grub_command_t cmd __attribute__ ((unused)), int argc, ++ char **argv) ++{ ++ char *devname, *dirname, *subvol; ++ struct grub_btrfs_key key_in; ++ grub_uint8_t type; ++ grub_uint64_t tree; ++ grub_uint64_t saved_tree; ++ grub_err_t err; ++ struct grub_btrfs_data *data = NULL; ++ grub_device_t dev = NULL; ++ ++ if (argc < 3) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "required and "); ++ ++ devname = grub_file_get_device_name(argv[0]); ++ dev = grub_device_open (devname); ++ grub_free (devname); ++ ++ if (!dev) ++ { ++ err = grub_errno; ++ goto err_out; ++ } ++ ++ dirname = argv[1]; ++ subvol = argv[2]; ++ ++ data = grub_btrfs_mount (dev); ++ if (!data) ++ { ++ err = grub_errno; ++ goto err_out; ++ } ++ ++ err = find_path (data, dirname, &key_in, &tree, &type); ++ if (err) ++ goto err_out; ++ ++ if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) ++ { ++ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); ++ goto err_out; ++ } ++ ++ err = get_root (data, &key_in, &tree, &type); ++ ++ if (err) ++ goto err_out; ++ ++ saved_tree = data->fs_tree; ++ data->fs_tree = tree; ++ err = find_path (data, subvol, &key_in, &tree, &type); ++ data->fs_tree = saved_tree; ++ ++ if (err) ++ goto err_out; ++ ++ if (key_in.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) ++ { ++ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", subvol); ++ goto err_out; ++ } ++ ++ grub_btrfs_unmount (data); ++ grub_device_close (dev); ++ add_mountpoint (dirname, subvol, tree); ++ ++ return GRUB_ERR_NONE; ++ ++err_out: ++ ++ if (data) ++ grub_btrfs_unmount (data); ++ ++ if (dev) ++ grub_device_close (dev); ++ ++ return err; ++} ++ ++grub_uint64_t ++find_mtab_subvol_tree (const char *path, char **path_in_subvol) ++{ ++ grub_btrfs_mtab_t m, cm; ++ grub_uint64_t tree; ++ ++ if (!path || !path_in_subvol) ++ return 0; ++ ++ *path_in_subvol = NULL; ++ tree = 0; ++ cm = NULL; ++ ++ FOR_GRUB_MTAB (m) ++ { ++ if (grub_strncmp (path, m->path, grub_strlen (m->path)) == 0) ++ { ++ if (!cm) ++ cm = m; ++ else ++ if (grub_strcmp (m->path, cm->path) > 0) ++ cm = m; ++ } ++ } ++ ++ if (cm) ++ { ++ const char *s = path + grub_strlen (cm->path); ++ *path_in_subvol = (s[0] == '\0') ? grub_strdup ("/") : grub_strdup (s); ++ tree = cm->tree; ++ } ++ ++ return tree; ++} ++ + static grub_err_t + get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, + grub_uint64_t objectid, grub_uint64_t offset, +@@ -2903,6 +3090,7 @@ + }; + + static grub_command_t cmd_info; ++static grub_command_t cmd_mount_subvol; + static grub_extcmd_t cmd_list_subvols; + + static char * +@@ -2966,6 +3154,9 @@ + cmd_info = grub_register_command("btrfs-info", grub_cmd_btrfs_info, + "DEVICE", + "Print BtrFS info about DEVICE."); ++ cmd_mount_subvol = grub_register_command("btrfs-mount-subvol", grub_cmd_btrfs_mount_subvol, ++ "DEVICE DIRECTORY SUBVOL", ++ "Set btrfs DEVICE the DIRECTORY a mountpoint of SUBVOL."); + cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols", + grub_cmd_btrfs_list_subvols, 0, + "[-p|-n] [-o var] DEVICE", +--- a/grub-core/osdep/linux/getroot.c ++++ b/grub-core/osdep/linux/getroot.c +@@ -103,6 +103,14 @@ + grub_uint32_t unused[9]; + }; + ++struct btrfs_ioctl_search_header { ++ grub_uint64_t transid; ++ grub_uint64_t objectid; ++ grub_uint64_t offset; ++ grub_uint32_t type; ++ grub_uint32_t len; ++}; ++ + struct btrfs_ioctl_search_args { + struct btrfs_ioctl_search_key key; + grub_uint64_t buf[(4096 - sizeof(struct btrfs_ioctl_search_key)) +@@ -375,6 +383,109 @@ + + int use_relative_path_on_btrfs = 0; + ++static char * ++get_btrfs_subvol (const char *path) ++{ ++ struct btrfs_ioctl_ino_lookup_args args; ++ grub_uint64_t tree_id; ++ int fd = -1; ++ char *ret = NULL; ++ ++ fd = open (path, O_RDONLY); ++ ++ if (fd < 0) ++ return NULL; ++ ++ memset (&args, 0, sizeof(args)); ++ args.objectid = GRUB_BTRFS_TREE_ROOT_OBJECTID; ++ ++ if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) ++ goto error; ++ ++ tree_id = args.treeid; ++ ++ while (tree_id != GRUB_BTRFS_ROOT_VOL_OBJECTID) ++ { ++ struct btrfs_ioctl_search_args sargs; ++ struct grub_btrfs_root_backref *br; ++ struct btrfs_ioctl_search_header *search_header; ++ char *old; ++ grub_uint16_t len; ++ grub_uint64_t inode_id; ++ ++ memset (&sargs, 0, sizeof(sargs)); ++ ++ sargs.key.tree_id = 1; ++ sargs.key.min_objectid = tree_id; ++ sargs.key.max_objectid = tree_id; ++ ++ sargs.key.min_offset = 0; ++ sargs.key.max_offset = ~0ULL; ++ sargs.key.min_transid = 0; ++ sargs.key.max_transid = ~0ULL; ++ sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF; ++ sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF; ++ ++ sargs.key.nr_items = 1; ++ ++ if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0) ++ goto error; ++ ++ if (sargs.key.nr_items == 0) ++ goto error; ++ ++ search_header = (struct btrfs_ioctl_search_header *)sargs.buf; ++ br = (struct grub_btrfs_root_backref *) (search_header + 1); ++ ++ len = grub_le_to_cpu16 (br->n); ++ inode_id = grub_le_to_cpu64 (br->inode_id); ++ tree_id = search_header->offset; ++ ++ old = ret; ++ ret = malloc (len + 1); ++ memcpy (ret, br->name, len); ++ ret[len] = '\0'; ++ ++ if (inode_id != GRUB_BTRFS_TREE_ROOT_OBJECTID) ++ { ++ char *s; ++ ++ memset(&args, 0, sizeof(args)); ++ args.treeid = search_header->offset; ++ args.objectid = inode_id; ++ ++ if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) ++ goto error; ++ ++ s = xasprintf ("%s%s", args.name, ret); ++ free (ret); ++ ret = s; ++ } ++ ++ if (old) ++ { ++ char *s = xasprintf ("%s/%s", ret, old); ++ free (ret); ++ free (old); ++ ret = s; ++ } ++ } ++ ++ close (fd); ++ return ret; ++ ++error: ++ ++ if (fd >= 0) ++ close (fd); ++ if (ret) ++ free (ret); ++ ++ return NULL; ++} ++ ++static char *grub_btrfs_mount_path; ++ + char ** + grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) + { +@@ -516,12 +627,17 @@ + else if (grub_strcmp (entries[i].fstype, "btrfs") == 0) + { + ret = grub_find_root_devices_from_btrfs (dir); +- fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path); + if (use_relative_path_on_btrfs) + { +- if (fs_prefix) +- free (fs_prefix); + fs_prefix = xstrdup ("/"); ++ ++ if (grub_btrfs_mount_path) ++ grub_free (grub_btrfs_mount_path); ++ grub_btrfs_mount_path = grub_strdup (entries[i].enc_path); ++ } ++ else ++ { ++ fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path); + } + } + else if (!retry && grub_strcmp (entries[i].fstype, "autofs") == 0) +@@ -1202,6 +1318,24 @@ + return grub_dev; + } + ++ ++char * ++grub_util_get_btrfs_subvol (const char *path, char **mount_path) ++{ ++ if (mount_path) ++ *mount_path = NULL; ++ ++ grub_free (grub_find_root_devices_from_mountinfo (path, NULL)); ++ ++ if (!grub_btrfs_mount_path) ++ return NULL; ++ ++ if (mount_path) ++ *mount_path = grub_strdup (grub_btrfs_mount_path); ++ ++ return get_btrfs_subvol (grub_btrfs_mount_path); ++} ++ + char * + grub_make_system_path_relative_to_its_root_os (const char *path) + { +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -1645,6 +1645,58 @@ + prefix_drive = xasprintf ("(%s)", grub_drives[0]); + } + ++#ifdef __linux__ ++ ++ if (config.is_suse_btrfs_snapshot_enabled ++ && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0) ++ { ++ char *subvol = NULL; ++ char *mount_path = NULL; ++ char **rootdir_devices = NULL; ++ char *t = grub_util_path_concat (2, "/", rootdir); ++ char *rootdir_path = grub_canonicalize_file_name (t); ++ ++ if (rootdir_path && grub_util_is_directory (rootdir_path)) ++ rootdir_devices = grub_guess_root_devices (rootdir_path); ++ ++ if (rootdir_devices && rootdir_devices[0]) ++ if (grub_strcmp (rootdir_devices[0], grub_devices[0]) == 0) ++ subvol = grub_util_get_btrfs_subvol (platdir, &mount_path); ++ ++ if (subvol && mount_path) ++ { ++ char *def_subvol; ++ ++ def_subvol = grub_util_get_btrfs_subvol (rootdir_path, NULL); ++ ++ if (def_subvol) ++ { ++ char *rootdir_mount_path = NULL; ++ if (!load_cfg_f) ++ load_cfg_f = grub_util_fopen (load_cfg, "wb"); ++ have_load_cfg = 1; ++ ++ if (grub_strncmp (rootdir_path, mount_path, grub_strlen (rootdir_path)) == 0) ++ rootdir_mount_path = grub_util_path_concat (2, "/", mount_path + grub_strlen (rootdir_path)); ++ ++ if (grub_strcmp (subvol, def_subvol) != 0 && rootdir_mount_path) ++ fprintf (load_cfg_f, "btrfs-mount-subvol ($root) %s %s\n", rootdir_mount_path, subvol); ++ free (rootdir_mount_path); ++ free (def_subvol); ++ } ++ } ++ ++ free (t); ++ free (rootdir_path); ++ for (curdev = rootdir_devices; *curdev; curdev++) ++ free (*curdev); ++ free (rootdir_devices); ++ free (subvol); ++ free (mount_path); ++ } ++ ++#endif ++ + char mkimage_target[200]; + const char *core_name = NULL; + +--- a/include/grub/emu/getroot.h ++++ b/include/grub/emu/getroot.h +@@ -53,6 +53,11 @@ + grub_find_root_devices_from_mountinfo (const char *dir, char **relroot); + #endif + ++#ifdef __linux__ ++char * ++grub_util_get_btrfs_subvol (const char *path, char **mount_path); ++#endif ++ + /* Devmapper functions provided by getroot_devmapper.c. */ + void + grub_util_pull_devmapper (const char *os_dev); diff --git a/grub2-btrfs-07-subvol-fallback.patch b/grub2-btrfs-07-subvol-fallback.patch new file mode 100644 index 0000000..b7b9325 --- /dev/null +++ b/grub2-btrfs-07-subvol-fallback.patch @@ -0,0 +1,44 @@ +--- a/grub-core/fs/btrfs.c ++++ b/grub-core/fs/btrfs.c +@@ -1340,10 +1340,40 @@ + } + + static grub_err_t ++lookup_root_by_name_fallback(struct grub_btrfs_data *data, const char *path) ++{ ++ grub_err_t err; ++ grub_uint64_t tree = 0; ++ grub_uint8_t type; ++ struct grub_btrfs_key key; ++ ++ err = find_path (data, path, &key, &tree, &type); ++ if (err) ++ return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path); ++ ++ if (key.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) ++ return grub_error(GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", path); ++ ++ data->fs_tree = tree; ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t + btrfs_handle_subvol(struct grub_btrfs_data *data __attribute__ ((unused))) + { + if (btrfs_default_subvol) +- return lookup_root_by_name(data, btrfs_default_subvol); ++ { ++ grub_err_t err; ++ err = lookup_root_by_name(data, btrfs_default_subvol); ++ ++ /* Fallback to old schemes */ ++ if (err == GRUB_ERR_FILE_NOT_FOUND) ++ { ++ err = GRUB_ERR_NONE; ++ return lookup_root_by_name_fallback(data, btrfs_default_subvol); ++ } ++ return err; ++ } + + if (btrfs_default_subvolid) + return lookup_root_by_id(data, btrfs_default_subvolid); diff --git a/grub2-btrfs-08-workaround-snapshot-menu-default-entry.patch b/grub2-btrfs-08-workaround-snapshot-menu-default-entry.patch new file mode 100644 index 0000000..668c93b --- /dev/null +++ b/grub2-btrfs-08-workaround-snapshot-menu-default-entry.patch @@ -0,0 +1,58 @@ + +v2: Add menuentry "Help on bootable snapshot" to be excluded as default entry. + +--- a/grub-core/normal/menu.c ++++ b/grub-core/normal/menu.c +@@ -574,6 +574,43 @@ + grub_refresh (); + } + ++/* bsc#956046 - The first entry titled 'Bootable snapshot #$NUM' is inserted on ++ top at runtime to display current snapshot information. If default entry is ++ using number as key to index the entry, the result will be shifted so here we ++ add specical handling to shift it back. We apply this workaround until a better ++ solution can be found. */ ++static void ++workaround_snapshot_menu_default_entry (grub_menu_t menu, const char *name, int *default_entry) ++{ ++ grub_menu_entry_t entry; ++ if ((entry = grub_menu_get_entry (menu, 0)) && ++ ((entry->submenu && grub_strncmp (entry->title, "Bootable snapshot", sizeof("Bootable snapshot") - 1) == 0) || ++ (!entry->submenu && grub_strncmp (entry->title, "Help on bootable snapshot", sizeof("Help on bootable snapshot") - 1) == 0))) ++ { ++ const char *val; ++ ++ if (*default_entry == -1 && menu->size > 1) ++ { ++ *default_entry = 1; ++ return; ++ } ++ ++ val = grub_env_get (name); ++ ++ grub_error_push (); ++ ++ if (val) ++ grub_strtoul (val, 0, 0); ++ ++ if (*default_entry < (menu->size - 1) && grub_errno == GRUB_ERR_NONE) ++ ++(*default_entry); ++ ++ grub_error_pop (); ++ } ++ ++ return; ++} ++ + #define GRUB_MENU_PAGE_SIZE 10 + + /* Show the menu and handle menu entry selection. Returns the menu entry +@@ -594,6 +631,8 @@ + + default_entry = get_entry_number (menu, "default"); + ++ workaround_snapshot_menu_default_entry (menu, "default", &default_entry); ++ + /* If DEFAULT_ENTRY is not within the menu entries, fall back to + the first entry. */ + if (default_entry < 0 || default_entry >= menu->size) diff --git a/grub2-btrfs-09-get-default-subvolume.patch b/grub2-btrfs-09-get-default-subvolume.patch new file mode 100644 index 0000000..d9368d8 --- /dev/null +++ b/grub2-btrfs-09-get-default-subvolume.patch @@ -0,0 +1,282 @@ + +V1: + * Use overflow checking primitives where the arithmetic expression for + buffer allocations may include unvalidated data + +--- a/grub-core/fs/btrfs.c ++++ b/grub-core/fs/btrfs.c +@@ -3104,6 +3104,254 @@ + return 0; + } + ++static grub_err_t ++grub_btrfs_get_parent_subvol_path (struct grub_btrfs_data *data, ++ grub_uint64_t child_id, ++ const char *child_path, ++ grub_uint64_t *parent_id, ++ char **path_out) ++{ ++ grub_uint64_t fs_root = 0; ++ struct grub_btrfs_key key_in = { ++ .object_id = child_id, ++ .type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF, ++ .offset = 0, ++ }, key_out; ++ struct grub_btrfs_root_ref *ref; ++ char *buf; ++ struct grub_btrfs_leaf_descriptor desc; ++ grub_size_t elemsize; ++ grub_disk_addr_t elemaddr; ++ grub_err_t err; ++ char *parent_path; ++ grub_size_t sz; ++ ++ *parent_id = 0; ++ *path_out = 0; ++ ++ err = lower_bound(data, &key_in, &key_out, data->sblock.root_tree, ++ &elemaddr, &elemsize, &desc, 0); ++ if (err) ++ return err; ++ ++ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF || elemaddr == 0) ++ next(data, &desc, &elemaddr, &elemsize, &key_out); ++ ++ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF) ++ { ++ free_iterator(&desc); ++ return grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root backrefs")); ++ } ++ ++ if (grub_add (elemsize, 1, &sz)) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); ++ ++ buf = grub_malloc(sz); ++ if (!buf) ++ { ++ free_iterator(&desc); ++ return grub_errno; ++ } ++ ++ err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0); ++ if (err) ++ { ++ grub_free(buf); ++ free_iterator(&desc); ++ return err; ++ } ++ ++ buf[elemsize] = 0; ++ ref = (struct grub_btrfs_root_ref *)buf; ++ ++ err = get_fs_root(data, data->sblock.root_tree, grub_le_to_cpu64 (key_out.offset), ++ 0, &fs_root); ++ if (err) ++ { ++ grub_free(buf); ++ free_iterator(&desc); ++ return err; ++ } ++ ++ find_pathname(data, grub_le_to_cpu64 (ref->dirid), fs_root, ref->name, &parent_path); ++ ++ if (child_path) ++ { ++ *path_out = grub_xasprintf ("%s/%s", parent_path, child_path); ++ grub_free (parent_path); ++ } ++ else ++ *path_out = parent_path; ++ ++ *parent_id = grub_le_to_cpu64 (key_out.offset); ++ ++ grub_free(buf); ++ free_iterator(&desc); ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_btrfs_get_default_subvolume_id (struct grub_btrfs_data *data, grub_uint64_t *id) ++{ ++ grub_err_t err; ++ grub_disk_addr_t elemaddr; ++ grub_size_t elemsize; ++ struct grub_btrfs_key key, key_out; ++ struct grub_btrfs_dir_item *direl = NULL; ++ const char *ctoken = "default"; ++ grub_size_t ctokenlen = sizeof ("default") - 1; ++ grub_size_t sz; ++ ++ *id = 0; ++ key.object_id = data->sblock.root_dir_objectid; ++ key.type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; ++ key.offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen)); ++ err = lower_bound (data, &key, &key_out, data->sblock.root_tree, &elemaddr, &elemsize, ++ NULL, 0); ++ if (err) ++ return err; ++ ++ if (key_cmp (&key, &key_out) != 0) ++ return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); ++ ++ struct grub_btrfs_dir_item *cdirel; ++ ++ if (grub_add (elemsize, 1, &sz)) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); ++ ++ direl = grub_malloc (sz); ++ if (!direl) ++ return grub_errno; ++ ++ err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize, 0); ++ if (err) ++ { ++ grub_free (direl); ++ return err; ++ } ++ for (cdirel = direl; ++ (grub_uint8_t *) cdirel - (grub_uint8_t *) direl ++ < (grub_ssize_t) elemsize; ++ cdirel = (void *) ((grub_uint8_t *) (direl + 1) ++ + grub_le_to_cpu16 (cdirel->n) ++ + grub_le_to_cpu16 (cdirel->m))) ++ { ++ if (ctokenlen == grub_le_to_cpu16 (cdirel->n) ++ && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0) ++ break; ++ } ++ if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl ++ >= (grub_ssize_t) elemsize) ++ { ++ grub_free (direl); ++ err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); ++ return err; ++ } ++ ++ if (cdirel->key.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM) ++ { ++ grub_free (direl); ++ err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); ++ return err; ++ } ++ ++ *id = grub_le_to_cpu64 (cdirel->key.object_id); ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt, ++ int argc, char **argv) ++{ ++ char *devname; ++ grub_device_t dev; ++ struct grub_btrfs_data *data; ++ grub_err_t err; ++ grub_uint64_t id; ++ char *subvol = NULL; ++ grub_uint64_t subvolid = 0; ++ char *varname = NULL; ++ char *output = NULL; ++ int path_only = ctxt->state[1].set; ++ int num_only = ctxt->state[2].set; ++ ++ if (ctxt->state[0].set) ++ varname = ctxt->state[0].arg; ++ ++ if (argc < 1) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); ++ ++ devname = grub_file_get_device_name(argv[0]); ++ if (!devname) ++ return grub_errno; ++ ++ dev = grub_device_open (devname); ++ grub_free (devname); ++ if (!dev) ++ return grub_errno; ++ ++ data = grub_btrfs_mount(dev); ++ if (!data) ++ { ++ grub_device_close (dev); ++ grub_dprintf ("btrfs", "failed to open fs\n"); ++ grub_errno = GRUB_ERR_NONE; ++ return 0; ++ } ++ ++ err = grub_btrfs_get_default_subvolume_id (data, &subvolid); ++ if (err) ++ { ++ grub_btrfs_unmount (data); ++ grub_device_close (dev); ++ return err; ++ } ++ ++ id = subvolid; ++ ++ if (id == GRUB_BTRFS_ROOT_VOL_OBJECTID) ++ subvol = grub_strdup (""); ++ else ++ while (id != GRUB_BTRFS_ROOT_VOL_OBJECTID) ++ { ++ grub_uint64_t parent_id; ++ char *path_out; ++ ++ err = grub_btrfs_get_parent_subvol_path (data, grub_cpu_to_le64 (id), subvol, &parent_id, &path_out); ++ if (err) ++ { ++ grub_btrfs_unmount (data); ++ grub_device_close (dev); ++ return err; ++ } ++ ++ if (subvol) ++ grub_free (subvol); ++ subvol = path_out; ++ id = parent_id; ++ } ++ ++ if (num_only && path_only) ++ output = grub_xasprintf ("%"PRIuGRUB_UINT64_T" /%s", subvolid, subvol); ++ else if (num_only) ++ output = grub_xasprintf ("%"PRIuGRUB_UINT64_T, subvolid); ++ else ++ output = grub_xasprintf ("/%s", subvol); ++ ++ if (varname) ++ grub_env_set(varname, output); ++ else ++ grub_printf ("%s\n", output); ++ ++ grub_free (output); ++ grub_free (subvol); ++ ++ grub_btrfs_unmount (data); ++ grub_device_close (dev); ++ ++ return GRUB_ERR_NONE; ++} ++ + static struct grub_fs grub_btrfs_fs = { + .name = "btrfs", + .fs_dir = grub_btrfs_dir, +@@ -3122,6 +3370,7 @@ + static grub_command_t cmd_info; + static grub_command_t cmd_mount_subvol; + static grub_extcmd_t cmd_list_subvols; ++static grub_extcmd_t cmd_get_default_subvol; + + static char * + subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)), +@@ -3192,6 +3441,11 @@ + "[-p|-n] [-o var] DEVICE", + "Print list of BtrFS subvolumes on " + "DEVICE.", options); ++ cmd_get_default_subvol = grub_register_extcmd("btrfs-get-default-subvol", ++ grub_cmd_btrfs_get_default_subvol, 0, ++ "[-p|-n] [-o var] DEVICE", ++ "Print default BtrFS subvolume on " ++ "DEVICE.", options); + grub_register_variable_hook ("btrfs_subvol", subvol_get_env, + subvol_set_env); + grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, diff --git a/grub2-btrfs-10-config-directory.patch b/grub2-btrfs-10-config-directory.patch new file mode 100644 index 0000000..ee591ec --- /dev/null +++ b/grub2-btrfs-10-config-directory.patch @@ -0,0 +1,253 @@ +v1: +References: bsc#1063443 + +v2: +References: bsc#1106381 +Fix outputting invalid btrfs subvol path on non btrfs filesystem due to bogus +return code handling. + +--- a/grub-core/fs/btrfs.c ++++ b/grub-core/fs/btrfs.c +@@ -3260,8 +3260,7 @@ + } + + static grub_err_t +-grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt, +- int argc, char **argv) ++grub_btrfs_get_default_subvol (const char *name, grub_uint64_t *ret_subvolid, char **ret_subvol) + { + char *devname; + grub_device_t dev; +@@ -3270,21 +3269,8 @@ + grub_uint64_t id; + char *subvol = NULL; + grub_uint64_t subvolid = 0; +- char *varname = NULL; +- char *output = NULL; +- int path_only = ctxt->state[1].set; +- int num_only = ctxt->state[2].set; +- +- if (ctxt->state[0].set) +- varname = ctxt->state[0].arg; +- +- if (argc < 1) +- return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); +- +- devname = grub_file_get_device_name(argv[0]); +- if (!devname) +- return grub_errno; + ++ devname = grub_file_get_device_name(name); + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) +@@ -3295,8 +3281,7 @@ + { + grub_device_close (dev); + grub_dprintf ("btrfs", "failed to open fs\n"); +- grub_errno = GRUB_ERR_NONE; +- return 0; ++ return grub_errno; + } + + err = grub_btrfs_get_default_subvolume_id (data, &subvolid); +@@ -3325,12 +3310,47 @@ + return err; + } + +- if (subvol) +- grub_free (subvol); ++ grub_free (subvol); + subvol = path_out; + id = parent_id; + } + ++ if (ret_subvolid) ++ *ret_subvolid = subvolid; ++ if (ret_subvol) ++ *ret_subvol = subvol; ++ ++ grub_btrfs_unmount (data); ++ grub_device_close (dev); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt, ++ int argc, char **argv) ++{ ++ grub_err_t err; ++ char *subvol = NULL; ++ grub_uint64_t subvolid = 0; ++ char *varname = NULL; ++ char *output = NULL; ++ int path_only = ctxt->state[1].set; ++ int num_only = ctxt->state[2].set; ++ ++ if (ctxt->state[0].set) ++ varname = ctxt->state[0].arg; ++ ++ if (argc < 1) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); ++ ++ if ((err = grub_btrfs_get_default_subvol (argv[0], &subvolid, &subvol)) != GRUB_ERR_NONE) ++ { ++ if (err == GRUB_ERR_BAD_FS) ++ err = grub_errno = GRUB_ERR_NONE; ++ return err; ++ } ++ + if (num_only && path_only) + output = grub_xasprintf ("%"PRIuGRUB_UINT64_T" /%s", subvolid, subvol); + else if (num_only) +@@ -3346,9 +3366,6 @@ + grub_free (output); + grub_free (subvol); + +- grub_btrfs_unmount (data); +- grub_device_close (dev); +- + return GRUB_ERR_NONE; + } + +@@ -3427,6 +3444,122 @@ + return ""; + } + ++ ++static char * ++grub_btrfs_path_to_abs (const char *path) ++{ ++ grub_err_t err; ++ char *device_name = NULL; ++ char *subvol = NULL; ++ const char *file_name; ++ char *ret; ++ ++ if (!path) ++ return NULL; ++ ++ if ((err = grub_btrfs_get_default_subvol (path, 0, &subvol)) != GRUB_ERR_NONE) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ return NULL; ++ } ++ ++ if (!subvol || *subvol == '\0') ++ return NULL; ++ ++ file_name = (path[0] == '(') ? grub_strchr (path, ')') : NULL; ++ if (file_name) ++ file_name++; ++ else ++ file_name = path; ++ device_name = grub_file_get_device_name (path); ++ if (device_name) ++ ret = grub_xasprintf ("(%s)/%s%s", device_name, subvol, file_name); ++ else ++ ret = grub_xasprintf ("/%s%s", subvol, file_name); ++ ++ grub_free (device_name); ++ grub_free (subvol); ++ ++ return ret; ++} ++ ++static char * ++grub_btrfs_path_to_rel (const char *path) ++{ ++ grub_err_t err; ++ char *subvol = NULL; ++ const char *file_name; ++ ++ if (!path) ++ return NULL; ++ ++ if ((err = grub_btrfs_get_default_subvol (path, 0, &subvol)) != GRUB_ERR_NONE) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ return NULL; ++ } ++ ++ if (!subvol || *subvol == '\0') ++ return NULL; ++ ++ file_name = (path[0] == '(') ? grub_strchr (path, ')') : NULL; ++ if (file_name) ++ file_name++; ++ else ++ file_name = path; ++ ++ if (*file_name == '/') ++ file_name++; ++ ++ if (grub_strncmp (file_name, subvol, grub_strlen (subvol)) == 0) ++ { ++ char *device_name; ++ char *ret; ++ ++ device_name = grub_file_get_device_name (path); ++ file_name += grub_strlen (subvol); ++ if (device_name) ++ ret = grub_xasprintf ("(%s)%s", device_name, file_name); ++ else ++ ret = grub_strdup (file_name); ++ grub_free (device_name); ++ grub_free (subvol); ++ return ret; ++ } ++ ++ grub_free (subvol); ++ return NULL; ++} ++ ++static char * ++relpath_set_env (struct grub_env_var *var, ++ const char *val) ++{ ++ int new_val, old_val; ++ new_val = (val[0] == '1' || val[0] == 'y') ? 1 : 0; ++ old_val = (var->value[0] == '1' || var->value[0] == 'y') ? 1 : 0; ++ ++ if (new_val != old_val) ++ { ++ const char **n; ++ char * (*path_to_xxx) (const char *); ++ const char *envname[] = {"config_file", "config_directory", NULL}; ++ ++ path_to_xxx = (new_val == 1) ? grub_btrfs_path_to_rel : grub_btrfs_path_to_abs; ++ for (n = envname; *n; n++) ++ { ++ char *ctmp = path_to_xxx (grub_env_get (*n)); ++ if (ctmp) ++ { ++ grub_env_set (*n, ctmp); ++ grub_free (ctmp); ++ } ++ } ++ } ++ ++ return grub_strdup (val); ++} ++ + GRUB_MOD_INIT (btrfs) + { + grub_fs_register (&grub_btrfs_fs); +@@ -3450,6 +3583,8 @@ + subvol_set_env); + grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, + subvolid_set_env); ++ grub_register_variable_hook ("btrfs_relative_path", NULL, ++ relpath_set_env); + grub_env_export ("btrfs_subvol"); + grub_env_export ("btrfs_subvolid"); + grub_env_export ("btrfs_relative_path"); +@@ -3459,6 +3594,7 @@ + { + grub_register_variable_hook ("btrfs_subvol", NULL, NULL); + grub_register_variable_hook ("btrfs_subvolid", NULL, NULL); ++ grub_register_variable_hook ("btrfs_relative_path", NULL, NULL); + grub_unregister_command (cmd_info); + grub_unregister_extcmd (cmd_list_subvols); + grub_fs_unregister (&grub_btrfs_fs); diff --git a/grub2-btrfs-help-on-snapper-rollback.patch b/grub2-btrfs-help-on-snapper-rollback.patch new file mode 100644 index 0000000..dfe1055 --- /dev/null +++ b/grub2-btrfs-help-on-snapper-rollback.patch @@ -0,0 +1,19 @@ +--- a/util/grub.d/00_header.in ++++ b/util/grub.d/00_header.in +@@ -428,8 +428,14 @@ + # Note: No $snapshot_num on *read-only* rollback! (bsc#901487) + cat </!d' -e '/>.*$/s///'` + + if [ -n "$FALLBACK_MATCH" ]; then + for i in $MENU_ENTRIES; do + if expr match "$i" "^$FALLBACK_MATCH" >/dev/null ; then + echo "$i" + return 0 + fi + done + fi + return 0 +} + +run_command () { + [ x"$DEBUG_RUN" = x1 ] && echo $@ || $@ +} + +debug_print () { + [ x"$DEBUG_RUN" = x1 ] && echo $@ || true +} + +case $1 in +-d|--debug) + DEBUG_RUN=1 + ;; +esac + +GRUB_EDITENV="/usr/bin/grub2-editenv" +GRUB_SET_DEFAULT="/usr/sbin/grub2-set-default" + +SAVED_ENTRY=`${GRUB_EDITENV} list | sed -ne "/^saved_entry=/s///p"` + +debug_print "SAVED_ENTRY=$SAVED_ENTRY" + +if [ -z "$SAVED_ENTRY" ] || expr match "$SAVED_ENTRY" "^[0-9]\+$" >/dev/null; then + exit 0 +fi + +MENU_ENTRIES=`awk ' + BEGIN { + bracket = 0 + } + { + patsplit($0, words, "([^[:blank:]]+)|(\"[^\"]+\")|('\''[^'\'']*'\'')", sep) + + cmd = words[1] + arg1 = words[2] + + if (substr(arg1, 1, 1) == "\"" || substr(arg1, 1, 1) == "'\''") { + len = length(arg1) + arg1 = substr(arg1, 2, len - 2) + } + + if (cmd == "submenu") { + submenu[bracket] = arg1 + } else if (cmd == "menuentry") { + title = "" + for (i = 0; i < bracket; i++) { + if (i in submenu) + title = title submenu[i] ">" + } + print title arg1 + } + + for (w in words) { + if (words[w] == "{") { + bracket++ + } else if (words[w] == "}") { + bracket-- + } + } + } +' /boot/grub2/grub.cfg` + +IFS=$'\n' + +debug_print "MENU_ENTRIES=" +for i in $MENU_ENTRIES; do + debug_print "$i" +done + +for i in $MENU_ENTRIES; do + if [ "$SAVED_ENTRY" = "$i" ]; then + exit 0 + fi +done + +FALLBACK=`fallback_entry $SAVED_ENTRY` + +if [ -n "$FALLBACK" ]; then + run_command ${GRUB_SET_DEFAULT} "$FALLBACK" + exit 0 +fi + +for i in /etc/os-release /usr/lib/os-release ; do + if [ -f "$i" ]; then + OS_RELEASE="$i" + break + fi +done + +if [ -z "$OS_RELEASE" ]; then + debug_print "No os-release file present" + exit 0 +fi + +debug_print "Applying settings from $OS_RELEASE" +source "$OS_RELEASE" + +if [ -z "$VERSION" ]; then + debug_print "No os-release version tag present, assuming rolling release" + exit 0 +fi + +NEW_SAVED_ENTRY=`echo $SAVED_ENTRY | sed -ne "s/$NAME [0-9a-zA-Z_.-]\{1,\}/$NAME $VERSION/pg"` + +debug_print "NEW_SAVED_ENTRY=$NEW_SAVED_ENTRY" + +if [ -z "$NEW_SAVED_ENTRY" -o "$NEW_SAVED_ENTRY" = "$SAVED_ENTRY" ]; then + exit 0 +fi + +IFS=$'\n' +for i in $MENU_ENTRIES; do + if [ "$NEW_SAVED_ENTRY" = "$i" ]; then + run_command ${GRUB_SET_DEFAULT} "$NEW_SAVED_ENTRY" + exit 0 + fi +done + +FALLBACK=`fallback_entry $NEW_SAVED_ENTRY` + +if [ -n "$FALLBACK" ]; then + run_command ${GRUB_SET_DEFAULT} "$FALLBACK" + exit 0 +fi + +exit 0 diff --git a/grub2-commands-introduce-read_file-subcommand.patch b/grub2-commands-introduce-read_file-subcommand.patch new file mode 100644 index 0000000..9f1aa54 --- /dev/null +++ b/grub2-commands-introduce-read_file-subcommand.patch @@ -0,0 +1,84 @@ +From: Raymund Will +Subject: Introduce a 'read_file' sub-command. +References: bsc#892852, bsc#891946 +Patch-Mainline: not yet + +Needed to allow s390x-emu to be telecontrolled via LOADPARM. + +v2: Added GRUB_FILE_TYPE_READ_ENVVAR as file type by read_file sub-command +tracked by verifier framework. + +--- + grub-core/commands/read.c | 34 ++++++++++++++++++++++++++++++++++ + 1 file changed, 34 insertions(+) + +--- a/grub-core/commands/read.c ++++ b/grub-core/commands/read.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -88,16 +89,49 @@ + return 0; + } + ++static grub_err_t ++grub_cmd_read_from_file (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) ++{ ++ char *line; ++ int i = 0; ++ grub_file_t file; ++ ++ if (argc < 1) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("file name expected")); ++ if (argc < 2) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("variable name expected")); ++ file = grub_file_open (args[i++], GRUB_FILE_TYPE_READ_ENVVAR); ++ if (! file) ++ return grub_errno; ++ while ( i < argc ) ++ { ++ line = grub_file_getline (file); ++ if ( !line ) ++ break; ++ grub_env_set (args[i++], line); ++ grub_free (line); ++ } ++ grub_file_close (file); ++ if (i != argc) ++ return GRUB_ERR_OUT_OF_RANGE; ++ return 0; ++} ++ + static grub_extcmd_t cmd; ++static grub_command_t cme; + + GRUB_MOD_INIT(read) + { + cmd = grub_register_extcmd ("read", grub_cmd_read, 0, + N_("[-s] [ENVVAR]"), + N_("Set variable with user input."), options); ++ cme = grub_register_command ("read_file", grub_cmd_read_from_file, ++ N_("FILE ENVVAR [...]"), ++ N_("Set variable(s) with line(s) from FILE.")); + } + + GRUB_MOD_FINI(read) + { + grub_unregister_extcmd (cmd); ++ grub_unregister_command (cme); + } +--- a/include/grub/file.h ++++ b/include/grub/file.h +@@ -126,6 +126,7 @@ + GRUB_FILE_TYPE_FS_SEARCH, + GRUB_FILE_TYPE_AUDIO, + GRUB_FILE_TYPE_VBE_DUMP, ++ GRUB_FILE_TYPE_READ_ENVVAR, + + GRUB_FILE_TYPE_LOADENV, + GRUB_FILE_TYPE_SAVEENV, diff --git a/grub2-default-distributor.patch b/grub2-default-distributor.patch new file mode 100644 index 0000000..c08829d --- /dev/null +++ b/grub2-default-distributor.patch @@ -0,0 +1,201 @@ +v1: +As long as VERSION in /etc/os-release has been commented out for rolling +release, we can replace openSUSE Tumbleweed specific handling for grub +distributor with a generic one. + +v2: +Use /usr/lib/os-release as fallback to /etc/os-release + +Index: grub-2.06/grub-core/osdep/unix/config.c +=================================================================== +--- grub-2.06.orig/grub-core/osdep/unix/config.c ++++ grub-2.06/grub-core/osdep/unix/config.c +@@ -61,6 +61,131 @@ grub_util_get_localedir (void) + return LOCALEDIR; + } + ++#ifdef __linux__ ++static char * ++os_release_get_val (const char *buf, const char *key) ++{ ++ const char *ptr = buf; ++ char *ret; ++ ++ while (*ptr && grub_isspace(*ptr)) ++ ptr++; ++ ++ if (*ptr == '#') ++ return NULL; ++ ++ if (grub_strncmp (ptr, key, grub_strlen (key)) != 0) ++ return NULL; ++ ++ ptr += grub_strlen (key); ++ if (*ptr++ != '=' || *ptr == '\0') ++ return NULL; ++ ++ if (*ptr == '"' || *ptr == '\'') ++ { ++ char c = *ptr; ++ int i = 0; ++ char *tmp, *ptmp; ++ ++ if (*++ptr == '\0') ++ return NULL; ++ ++ tmp = grub_strdup (ptr); ++ if ((ptmp = grub_strrchr (tmp, c))) ++ *ptmp = '\0'; ++ ++ ret = malloc (grub_strlen (tmp) + 1); ++ ptmp = tmp; ++ while (*ptmp) ++ { ++ if (*ptmp != '\\' || *(ptmp + 1) != c) ++ ret[i++] = *ptmp; ++ ++ptmp; ++ } ++ ++ grub_free (tmp); ++ ret[i] = '\0'; ++ } ++ else ++ { ++ char *pret; ++ ++ ret = grub_strdup (ptr); ++ if ((pret = grub_strchr (ret, ' '))) ++ *pret = '\0'; ++ } ++ ++ return ret; ++} ++ ++static char* ++grub_util_default_distributor (void) ++{ ++ char *cfgfile; ++ char buf[1024]; ++ FILE *fp = NULL; ++ char *os_name = NULL; ++ char *os_version = NULL; ++ ++ cfgfile = grub_util_path_concat (2, GRUB_SYSCONFDIR, "os-release"); ++ if (!grub_util_is_regular (cfgfile)) ++ { ++ grub_free (cfgfile); ++ cfgfile = grub_util_path_concat (2, "/usr/lib", "os-release"); ++ if (!grub_util_is_regular (cfgfile)) ++ { ++ grub_free (cfgfile); ++ return NULL; ++ } ++ } ++ ++ fp = grub_util_fopen (cfgfile, "r"); ++ ++ if (!fp) ++ { ++ grub_util_warn (_("cannot open configuration file `%s': %s"), ++ cfgfile, strerror (errno)); ++ grub_free (cfgfile); ++ return NULL; ++ } ++ ++ grub_free (cfgfile); ++ ++ while (fgets (buf, sizeof (buf), fp)) ++ { ++ if (buf[grub_strlen(buf) - 1] == '\n') ++ buf[grub_strlen(buf) - 1] = '\0'; ++ ++ if (!os_name ++ && (os_name = os_release_get_val (buf, "NAME"))) ++ continue; ++ if (!os_version ++ && (os_version = os_release_get_val (buf, "VERSION"))) ++ continue; ++ if (os_name && os_version) ++ break; ++ } ++ ++ fclose (fp); ++ ++ if (os_name && os_version) ++ { ++ char *os_name_version; ++ ++ os_name_version = grub_xasprintf ("%s %s", os_name, os_version); ++ ++ grub_free (os_name); ++ grub_free (os_version); ++ ++ return os_name_version; ++ } ++ ++ grub_free (os_version); ++ ++ return os_name; ++} ++#endif ++ + void + grub_util_load_config (struct grub_util_config *cfg) + { +@@ -125,7 +250,17 @@ grub_util_load_config (struct grub_util_ + waitpid (pid, NULL, 0); + } + if (f) +- return; ++ { ++#ifdef __linux__ ++ if (!cfg->grub_distributor || cfg->grub_distributor[0] == '\0') ++ { ++ if (cfg->grub_distributor) ++ grub_free (cfg->grub_distributor); ++ cfg->grub_distributor = grub_util_default_distributor (); ++ } ++#endif ++ return; ++ } + + f = grub_util_fopen (cfgfile, "r"); + if (f) +@@ -136,4 +271,13 @@ grub_util_load_config (struct grub_util_ + else + grub_util_warn (_("cannot open configuration file `%s': %s"), + cfgfile, strerror (errno)); ++ ++#ifdef __linux__ ++ if (!cfg->grub_distributor || cfg->grub_distributor[0] == '\0') ++ { ++ if (cfg->grub_distributor) ++ grub_free (cfg->grub_distributor); ++ cfg->grub_distributor = grub_util_default_distributor (); ++ } ++#endif + } +Index: grub-2.06/util/grub-mkconfig.in +=================================================================== +--- grub-2.06.orig/util/grub-mkconfig.in ++++ grub-2.06/util/grub-mkconfig.in +@@ -225,6 +225,19 @@ GRUB_ACTUAL_DEFAULT="$GRUB_DEFAULT" + + if [ "x${GRUB_ACTUAL_DEFAULT}" = "xsaved" ] ; then GRUB_ACTUAL_DEFAULT="`"${grub_editenv}" - list | sed -n '/^saved_entry=/ s,^saved_entry=,,p'`" ; fi + ++if [ x"${GRUB_DISTRIBUTOR}" = x ] ; then ++ for i in "${sysconfdir}/os-release" "/usr/lib/os-release" ; do ++ if [ -f "$i" ] ; then ++ . "$i" ++ break ++ fi ++ done ++ if [ x"${NAME}" != x ] && [ x"${VERSION}" != x ] ; then ++ GRUB_DISTRIBUTOR="${NAME} ${VERSION}" ++ else ++ GRUB_DISTRIBUTOR="${NAME}" ++ fi ++fi + + # These are defined in this script, export them here so that user can + # override them. diff --git a/grub2-diskfilter-support-pv-without-metadatacopies.patch b/grub2-diskfilter-support-pv-without-metadatacopies.patch new file mode 100644 index 0000000..c549be3 --- /dev/null +++ b/grub2-diskfilter-support-pv-without-metadatacopies.patch @@ -0,0 +1,219 @@ +From a28bc19400b4e70725ce5532bc5e4c374c72d7a9 Mon Sep 17 00:00:00 2001 +From: Lidong Zhong +Date: Wed, 26 Apr 2017 15:52:40 +0800 +Subject: [PATCH] diskfilter: implementation of processing no metadata recorded + in PV + +If one PV underlying the root LV is created with no metadata, such as + +pvcreate --metadatacopies 0 /dev/sda + +then we could get a lot of error messages when generating a new +configuration file. + +Generating grub configuration file ... +error: unknown LVM metadata header. +error: unknown LVM metadata header. +/usr/sbin/grub2-probe: warning: Couldn't find physical volume `pv1'. +Some modules may be missing from core image.. +(For details, please refer to + https://bugzilla.suse.com/show_bug.cgi?id=1027526) + +When one labelled PV which dose not have any metadata is found, we put +it into a global grub_detached_pv list. and we search all the PVs in the +current array list to check if it is a member of grub_detached_pv list. +So we can know if the PV is really missing or just without metadata. + +Signed-off-by: Lidong Zhong +--- + grub-core/disk/diskfilter.c | 112 +++++++++++++++++++++++++++++++++++++++++++- + grub-core/disk/lvm.c | 15 ++++-- + 2 files changed, 121 insertions(+), 6 deletions(-) + +--- a/grub-core/disk/diskfilter.c ++++ b/grub-core/disk/diskfilter.c +@@ -28,6 +28,7 @@ + #include + #include + #endif ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -43,7 +44,17 @@ + find_lv (const char *name); + static int is_lv_readable (struct grub_diskfilter_lv *lv, int easily); + +- ++struct grub_detached_pv { ++ struct grub_detached_pv *next; ++ struct grub_detached_pv **prev; ++ struct grub_diskfilter_pv_id id; ++ grub_disk_t disk; ++ grub_diskfilter_t diskfilter; ++} ; ++ ++static struct grub_detached_pv *detached_pv_list; ++ ++#define FOR_DETACHED_PVS(var) for (var = detached_pv_list; var; var = var->next) + + static grub_err_t + is_node_readable (const struct grub_diskfilter_node *node, int easily) +@@ -132,6 +143,7 @@ + grub_disk_addr_t start_sector; + struct grub_diskfilter_pv_id id; + grub_diskfilter_t diskfilter; ++ struct grub_detached_pv *pv; + + grub_dprintf ("diskfilter", "Scanning for DISKFILTER devices on disk %s\n", + name); +@@ -168,6 +180,28 @@ + grub_free (id.uuid); + return 0; + } ++ /*insert the special LVM PV into detached_pv_list*/ ++ if (!arr && (id.uuidlen > 0) && (grub_strcmp(diskfilter->name, "lvm") == 0)) ++ { ++ pv = grub_zalloc(sizeof(*pv)); ++ if (!pv) ++ return 1; ++ pv->id.uuidlen = GRUB_LVM_ID_STRLEN; ++ pv->id.uuid = grub_malloc(GRUB_LVM_ID_STRLEN); ++ if (!pv->id.uuid) ++ goto fail_id; ++ grub_memcpy(pv->id.uuid, id.uuid, GRUB_LVM_ID_STRLEN); ++ /*It's safe to save disk into this standalone pv list*/ ++ pv->disk = grub_disk_open(name); ++ if (!pv->disk) ++ goto fail_id; ++ pv->diskfilter = diskfilter; ++ grub_list_push (GRUB_AS_LIST_P (&detached_pv_list), ++ GRUB_AS_LIST(pv)); ++#ifdef GRUB_UTIL ++ grub_util_info ("adding disk %s into detached pv list", name); ++#endif ++ } + if (arr && id.uuidlen) + grub_free (id.uuid); + +@@ -180,6 +214,65 @@ + } + + return 0; ++fail_id: ++ if (pv->id.uuidlen) ++ grub_free(pv->id.uuid); ++ grub_free(pv); ++ return 1; ++} ++ ++static int ++process_detached_pv_list(void) ++{ ++ struct grub_diskfilter_vg *arr; ++ struct grub_diskfilter_pv *pv1; ++ struct grub_detached_pv *pv2; ++ unsigned found = 0; ++ ++ for (arr = array_list; arr != NULL; arr = arr->next) ++ { ++ for (pv1 = arr->pvs; pv1; pv1 = pv1->next) ++ { ++ if (pv1->disk) ++ continue; ++ FOR_DETACHED_PVS(pv2) ++ { ++ if (pv2->id.uuidlen == pv1->id.uuidlen && ++ !grub_memcmp(pv2->id.uuid, pv1->id.uuid, pv1->id.uuidlen)) ++ { ++ if (insert_array(pv2->disk, &(pv2->id), arr, -1, pv2->diskfilter)) ++ return grub_errno; ++ else ++ { ++#ifdef GRUB_UTIL ++ grub_util_info ("found disk %s in detached pv list", pv1->disk->name); ++#endif ++ found = 1; ++ break; ++ } ++ } ++ } ++ /*remove pv2 from the list*/ ++ if (found) ++ { ++#ifdef GRUB_UTIL ++ grub_util_info ("removing disk %s from detached pv list", pv1->disk->name); ++#endif ++ grub_list_remove(GRUB_AS_LIST (pv2)); ++ if (pv2->id.uuidlen) ++ { ++ pv2->id.uuidlen = 0; ++ grub_free(pv2->id.uuid); ++ } ++ grub_disk_close(pv2->disk); ++ grub_free(pv2); ++ break; ++ } ++ } ++ if (found) ++ break; ++ } ++ return 0; + } + + static int +@@ -206,6 +299,9 @@ + grub_partition_iterate (disk, scan_disk_partition_iter, (void *) name); + grub_disk_close (disk); + scan_depth--; ++ ++ /*process the detached_pv_list*/ ++ process_detached_pv_list(); + return 0; + } + +@@ -1287,6 +1383,20 @@ + static void + free_array (void) + { ++ while(detached_pv_list) ++ { ++ struct grub_detached_pv *pv; ++ pv = detached_pv_list; ++ detached_pv_list = detached_pv_list->next; ++#ifdef GRUB_UTIL ++ grub_util_warn (_("Couldn't find disk for physical volume `%s'." ++ "Some LVs may not work normally."),pv->disk->name); ++#endif ++ if (pv->id.uuidlen) ++ grub_free(pv->id.uuid); ++ grub_disk_close(pv->disk); ++ grub_free(pv); ++ } + while (array_list) + { + struct grub_diskfilter_vg *vg; +--- a/grub-core/disk/lvm.c ++++ b/grub-core/disk/lvm.c +@@ -235,11 +235,16 @@ + sizeof (mdah->magic))) + || (grub_le_to_cpu32 (mdah->version) != GRUB_LVM_FMTT_VERSION)) + { +- grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, +- "unknown LVM metadata header"); +-#ifdef GRUB_UTIL +- grub_util_info ("unknown LVM metadata header"); +-#endif ++ /* ++ * It's not necessarily an error. There is no metadata recorded when ++ * PV is created with pvmetadatacopies set to zero. We need to process ++ * this kind of PV seperately. ++ */ ++ id->uuid = grub_malloc(GRUB_LVM_ID_STRLEN); ++ if (!id->uuid) ++ goto fail; ++ grub_memcpy(id->uuid, pv_id, GRUB_LVM_ID_STRLEN); ++ id->uuidlen = GRUB_LVM_ID_STRLEN; + goto fail; + } + diff --git a/grub2-efi-HP-workaround.patch b/grub2-efi-HP-workaround.patch new file mode 100644 index 0000000..e1b1c28 --- /dev/null +++ b/grub2-efi-HP-workaround.patch @@ -0,0 +1,95 @@ + +v2: Add GRUB_FILE_TYPE_CONFIG to grub_file_open, see also upstream commit +ca0a4f689 verifiers: File type for fine-grained signature-verification controlling + +--- a/grub-core/kern/efi/init.c ++++ b/grub-core/kern/efi/init.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + + #ifdef GRUB_STACK_PROTECTOR +@@ -135,6 +136,67 @@ + void (*grub_efi_net_config) (grub_efi_handle_t hnd, + char **device, + char **path); ++static char * ++workaround_efi_firmware_path (const char *device, const char *path) ++{ ++ char *config = NULL;; ++ char *config_upper = NULL; ++ char *path_upper = NULL; ++ char *ret_path = NULL; ++ grub_file_t config_fd = NULL; ++ char *s; ++ ++ if (!device || !path) ++ return NULL; ++ ++ /* only workaround if booting off from cd device */ ++ if (grub_strncmp (device, "cd", 2) != 0) ++ goto quit; ++ ++ config = grub_xasprintf ("(%s)%s/grub.cfg", device, path); ++ config_fd = grub_file_open (config, GRUB_FILE_TYPE_CONFIG); ++ ++ /* everything's fine, so quit the workaround */ ++ if (config_fd) ++ goto quit; ++ ++ /* reset grub error state because noone else does... */ ++ grub_errno = GRUB_ERR_NONE; ++ ++ /* try again, this time upper case path */ ++ path_upper = grub_strdup (path); ++ if (! path_upper) ++ goto quit; ++ ++ s = path_upper; ++ for (; *s; s++) *s = grub_toupper(*s); ++ ++ config_upper = grub_xasprintf ("(%s)%s/grub.cfg", device, path_upper); ++ if (! config_upper) ++ goto quit; ++ ++ config_fd = grub_file_open (config_upper, GRUB_FILE_TYPE_CONFIG); ++ ++ /* if config can be found by the upper case path, return it */ ++ if (config_fd) ++ ret_path = grub_strdup (path_upper); ++ ++quit: ++ ++ if (config_fd) ++ grub_file_close (config_fd); ++ ++ if (grub_errno) ++ grub_errno = GRUB_ERR_NONE; ++ ++ if (config) ++ grub_free (config); ++ ++ if (config_upper) ++ grub_free (config_upper); ++ ++ return ret_path; ++} + + void + grub_machine_get_bootlocation (char **device, char **path) +@@ -159,6 +221,12 @@ + p = grub_strrchr (*path, '/'); + if (p) + *p = '\0'; ++ ++ if ((p = workaround_efi_firmware_path (*device, *path))) ++ { ++ grub_free (*path); ++ *path = p; ++ } + } + } + diff --git a/grub2-efi-chainload-harder.patch b/grub2-efi-chainload-harder.patch new file mode 100644 index 0000000..68fc383 --- /dev/null +++ b/grub2-efi-chainload-harder.patch @@ -0,0 +1,121 @@ + +v2: +Use grub_efi_get_secureboot to get secure boot status + +v3: +Fix null sb_context->file_path due to missing assignment of chainloaded image's +file_path (bsc#1216081) + +--- + grub-core/loader/efi/chainloader.c | 62 +++++++++++++++++++++---------------- + 1 file changed, 36 insertions(+), 26 deletions(-) + +--- a/grub-core/loader/efi/chainloader.c ++++ b/grub-core/loader/efi/chainloader.c +@@ -305,40 +305,41 @@ + static grub_efi_boolean_t + read_header (void *data, grub_efi_uint32_t size, pe_coff_loader_image_context_t *context) + { +- grub_efi_guid_t guid = SHIM_LOCK_GUID; +- grub_efi_shim_lock_t *shim_lock; +- grub_efi_status_t status; +- +- shim_lock = grub_efi_locate_protocol (&guid, NULL); ++ char *msdos = (char *)data; ++ struct grub_pe32_header_no_msdos_stub *pe32 = (struct grub_pe32_header_no_msdos_stub *)data; + +- if (!shim_lock) ++ if (size < sizeof (*pe32)) + { +- grub_error (GRUB_ERR_BAD_ARGUMENT, "no shim lock protocol"); ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid image"); + return 0; + } + +- status = shim_lock->context (data, size, context); +- +- if (status == GRUB_EFI_SUCCESS) ++ if (grub_memcmp (msdos, "MZ", 2) == 0) + { +- grub_dprintf ("chain", "context success\n"); +- return 1; ++ grub_uint32_t off = *((grub_uint32_t *) (msdos + 0x3c)); ++ pe32 = (struct grub_pe32_header_no_msdos_stub *) ((char *)data + off); + } + +- switch (status) ++ if (grub_memcmp (pe32->signature, "PE\0\0", 4) != 0 || ++ pe32->coff_header.machine != GRUB_PE32_MACHINE_X86_64 || ++ pe32->optional_header.magic != GRUB_PE32_PE64_MAGIC) + { +- case GRUB_EFI_UNSUPPORTED: +- grub_error (GRUB_ERR_BAD_ARGUMENT, "context error unsupported"); +- break; +- case GRUB_EFI_INVALID_PARAMETER: +- grub_error (GRUB_ERR_BAD_ARGUMENT, "context error invalid parameter"); +- break; +- default: +- grub_error (GRUB_ERR_BAD_ARGUMENT, "context error code"); +- break; ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "Not supported image"); ++ return 0; + } + +- return 0; ++ context->number_of_rva_and_sizes = pe32->optional_header.num_data_directories; ++ context->size_of_headers = pe32->optional_header.header_size; ++ context->image_size = pe32->optional_header.image_size; ++ context->image_address = pe32->optional_header.image_base; ++ context->entry_point = pe32->optional_header.entry_addr; ++ context->reloc_dir = &pe32->optional_header.base_relocation_table; ++ context->sec_dir = &pe32->optional_header.certificate_table; ++ context->number_of_sections = pe32->coff_header.num_sections; ++ context->pe_hdr = pe32; ++ context->first_section = (struct grub_pe32_section_table *)((char *)(&pe32->optional_header) + pe32->coff_header.optional_header_size); ++ ++ return 1; + } + + static void* +@@ -607,6 +608,9 @@ + if (buffer) + b->free_pool (buffer); + ++ if (grub_errno) ++ grub_print_error (); ++ + return 0; + + } +@@ -825,6 +829,31 @@ + status = b->load_image (0, grub_efi_image_handle, file_path, + boot_image, size, + &image_handle); ++#ifdef SUPPORT_SECURE_BOOT ++ if (status == GRUB_EFI_SECURITY_VIOLATION && grub_efi_get_secureboot () != GRUB_EFI_SECUREBOOT_MODE_ENABLED) ++ { ++ /* If it failed with security violation while not in secure boot mode, ++ the firmware might be broken. We try to workaround on that by forcing ++ the SB method! (bsc#887793) */ ++ struct grub_secureboot_chainloader_context *sb_context; ++ ++ grub_dprintf ("chain", "Possible firmware flaw! Security violation while not in secure boot mode.\n"); ++ sb_context = grub_malloc (sizeof (*sb_context)); ++ if (!sb_context) ++ goto fail; ++ sb_context->cmdline = cmdline; ++ sb_context->cmdline_len = cmdline_len; ++ sb_context->fsize = size; ++ sb_context->dev_handle = dev_handle; ++ sb_context->address = address; ++ sb_context->pages = pages; ++ sb_context->file_path = file_path; ++ grub_file_close (file); ++ grub_loader_set_ex (grub_secureboot_chainloader_boot, ++ grub_secureboot_chainloader_unload, sb_context, 0); ++ return 0; ++ } ++#endif + if (status != GRUB_EFI_SUCCESS) + { + if (status == GRUB_EFI_OUT_OF_RESOURCES) diff --git a/grub2-efi-disable-video-cirrus-and-bochus.patch b/grub2-efi-disable-video-cirrus-and-bochus.patch new file mode 100644 index 0000000..1259df3 --- /dev/null +++ b/grub2-efi-disable-video-cirrus-and-bochus.patch @@ -0,0 +1,31 @@ +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -2142,13 +2142,13 @@ + module = { + name = video_cirrus; + x86 = video/cirrus.c; +- enable = x86; ++ enable = x86_noefi; + }; + + module = { + name = video_bochs; + x86 = video/bochs.c; +- enable = x86; ++ enable = x86_noefi; + }; + + module = { +--- a/gentpl.py ++++ b/gentpl.py +@@ -92,6 +92,10 @@ + GROUPS["i386_coreboot_multiboot_qemu"] = ["i386_coreboot", "i386_multiboot", "i386_qemu"] + GROUPS["nopc"] = GRUB_PLATFORMS[:]; GROUPS["nopc"].remove("i386_pc") + ++# x86 without efi ++GROUPS["x86_noefi"] = GROUPS["x86"][:] ++GROUPS["x86_noefi"].remove("i386_efi"); GROUPS["x86_noefi"].remove("x86_64_efi"); ++ + # + # Create platform => groups reverse map, where groups covering that + # platform are ordered by their sizes diff --git a/grub2-efi-xen-cfg-unquote.patch b/grub2-efi-xen-cfg-unquote.patch new file mode 100644 index 0000000..3b437df --- /dev/null +++ b/grub2-efi-xen-cfg-unquote.patch @@ -0,0 +1,90 @@ +From: Petr Tesarik +Subject: Unquote parameters written to Xen EFI config file +References: bsc#900418 +Patch-mainline: not yet + +The GRUB_CMDLINE_* value is copied verbatim to grub.conf, so it is first +parsed by GRUB2 before being passed down to the kernel. OTOH Xen EFI loader +takes the config file options verbatim. This means that any special GRUB2 +syntax must be evaluated when generating that file. + +Of course, some things are not even possible (e.g. substituting GRUB runtime +variables), but let's call them known limitations. + +Signed-off-by: Petr Tesarik + +--- + util/grub.d/20_linux_xen.in | 54 ++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 52 insertions(+), 2 deletions(-) + +--- a/util/grub.d/20_linux_xen.in ++++ b/util/grub.d/20_linux_xen.in +@@ -139,6 +139,52 @@ + is_efi=false + fi + ++grub2_unquote () ++{ ++ awk ' ++ BEGIN { ++ bare = "[^{}|&$;<> \t\n'\''\"\\\\]+" ++ esc = "\\\\." ++ id = "[[:alpha:]_][[:alnum:]_]*|[[:digit:]]+|[?#*@]" ++ var = "\\$("id")|\\$\\{("id")\\}" ++ dqesc = "\\\\[$\"\\\\]" ++ dqstr = "\\$?\"([^\"]|"var"|"dqesc")*\"" ++ sqstr = "'\''[^'\'']*'\''" ++ pat = bare"|"esc"|"var"|"dqstr"|"sqstr ++ ORS = "" ++ } ++ { ++ patsplit($0, words, pat, sep) ++ print sep[0] ++ for (i in words) { ++ w = words[i] ++ if (w ~ /^\$?"/) { ++ # Double-quoted string ++ patsplit(w, segs, var"|"dqesc, ssep) ++ print ssep[0] ++ for (j in segs) { ++ if (segs[j] ~ /^\\/) ++ print substr(segs[j], 2) ++ print ssep[j] ++ } ++ } else if (w ~ /^'\''/) { ++ # Single-quoted string ++ print substr(w, 2, length(w)-2) ++ } else if (w ~ /^\\/) { ++ # Escape sequence ++ print substr(w, 2) ++ } else if (w ~ /^\$/) { ++ # Variable expansion ++ } else { ++ # Bare word ++ print w ++ } ++ print sep[i] ++ } ++ print "\n" ++ }' ++} ++ + linux_entry () + { + linux_entry_xsm "$@" false +@@ -209,11 +255,13 @@ + else + section="failsafe.$section_count" + fi ++ xen_args_unq=$(echo $xen_args | grub2_unquote) ++ args_unq=$(echo $args | grub2_unquote) + cat <<-EOF >> $grub_dir/$xen_cfg + + [$section] +- options=${xen_args} +- kernel=${basename} root=${linux_root_device_thisversion} ${args} ++ options=${xen_args_unq} ++ kernel=${basename} root=${linux_root_device_thisversion} ${args_unq} + ramdisk=${initrd_real} + EOF + message="$(gettext_printf "Loading Xen %s with Linux %s ..." ${xen_version} ${version})" diff --git a/grub2-efi-xen-chainload.patch b/grub2-efi-xen-chainload.patch new file mode 100644 index 0000000..b5ceaed --- /dev/null +++ b/grub2-efi-xen-chainload.patch @@ -0,0 +1,188 @@ +From: Raymund Will +Subject: Use chainloader to boot xen.efi under UEFI. +References: bnc#871857, bnc#879148 +Patch-Mainline: no + +As XEN on SLE12 is not multiboot2 ready, some very dirty hacking +is necessary to boot via xen.efi and separate configfile snippets +(as done in SLE11SP3 secureboot). + +To that end said configfile snippets, xen efi-binaries, kernels and initrds +need to copied to the EFI system partition during 'grub2-mkconfig'! + +V0: +- first, somewhat fragile version, without any sort of cleanup for ESP. +V1: +- add missing whitespace. (bnc879148) +V2: +- second, much less fragile version, using only one config file per + XEN hypervisor version with sections for different kernels, avoiding + useless duplicates for sym-linked hypervisors. and removing previously + installed files from ESP. +V3: +- support move to '/usr/share/efi/$machine' for EFI-binaries. (bsc#1122563) + +--- + util/grub.d/20_linux_xen.in | 109 +++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 97 insertions(+), 12 deletions(-) + +--- a/util/grub.d/20_linux_xen.in ++++ b/util/grub.d/20_linux_xen.in +@@ -21,6 +21,8 @@ + exec_prefix="@exec_prefix@" + datarootdir="@datarootdir@" + ++ME=$(basename $0) ++ + . "$pkgdatadir/grub-mkconfig_lib" + + export TEXTDOMAIN=@PACKAGE@ +@@ -36,11 +38,23 @@ + + if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then + OS=GNU/Linux ++ os=linux + else + OS="${GRUB_DISTRIBUTOR}" +- CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}" ++ os="$(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1)" ++ CLASS="--class ${os} ${CLASS}" + fi + ++machine=`uname -m` ++ ++case "$machine" in ++ i?86) GENKERNEL_ARCH="x86" ;; ++ mips|mips64) GENKERNEL_ARCH="mips" ;; ++ mipsel|mips64el) GENKERNEL_ARCH="mipsel" ;; ++ arm*) GENKERNEL_ARCH="arm" ;; ++ *) GENKERNEL_ARCH="$machine" ;; ++esac ++ + # loop-AES arranges things so that /dev/loop/X can be our root device, but + # the initrds that Linux uses don't like that. + case ${GRUB_DEVICE} in +@@ -99,6 +113,32 @@ + + title_correction_code= + ++if [ -d /sys/firmware/efi ]; then ++ is_efi=true ++ err_msg="" ++ efi_dir="/boot/efi/efi/${os}" ++ grub_dir=/boot/@PACKAGE@ ++ xen_dir=/usr/share/efi/$machine ++ [ -d $xen_dir ] || xen_dir=/usr/lib64/efi ++ for d in $grub_dir $efi_dir $xen_dir; do ++ [ ! -d "$d" ] || continue ++ err_msg="${err_msg}$ME: Essential directory '$d' not found!\n" ++ done ++ if ! [ -d "$efi_dir" -a -d "$grub_dir" -a -d "$xen_dir" ]; then ++ err_msg="${err_msg}$ME: XEN configuration skipped!\n" ++ else ++ rm -f $grub_dir/xen*.cfg ++ if [ -s $efi_dir/grub.xen-files ]; then ++ for f in $(sort $efi_dir/grub.xen-files| uniq); do ++ rm -f $efi_dir/$f ++ done ++ : > $efi_dir/grub.xen-files ++ fi ++ fi ++else ++ is_efi=false ++fi ++ + linux_entry () + { + linux_entry_xsm "$@" false +@@ -154,6 +194,40 @@ + save_default_entry | grub_add_tab | sed "s/^/$submenu_indentation/" + fi + ++ if $is_efi; then ++ xen_cfg=${xen_basename/.efi/.cfg} ++ if [ "$section_count" = 0 ]; then ++ cat <<-EOF > $grub_dir/$xen_cfg ++ # disclaimer ++ [global] ++ #default= ++ EOF ++ fi ++ section_count=$(expr $section_count + 1) ++ if [ x$type != xrecovery ] ; then ++ section="config.$section_count" ++ else ++ section="failsafe.$section_count" ++ fi ++ cat <<-EOF >> $grub_dir/$xen_cfg ++ ++ [$section] ++ options=${xen_args} ++ kernel=${basename} root=${linux_root_device_thisversion} ${args} ++ ramdisk=${initrd_real} ++ EOF ++ message="$(gettext_printf "Loading Xen %s with Linux %s ..." ${xen_version} ${version})" ++ sed "s/^/$submenu_indentation/" <<-EOF ++ echo '$(echo "$message" | grub_quote)' ++ chainloader \$cmdpath/${xen_basename} ${xen_basename} $section ++ } ++ EOF ++ for f in ${grub_dir}/$xen_cfg ${xen_dir}/${xen_basename} ${dirname}/${basename} ${dirname}/${initrd_real}; do ++ cp --preserve=timestamps $f $efi_dir ++ echo $(basename $f) >> $efi_dir/grub.xen-files ++ done ++ return ++ fi + if [ -z "${prepare_boot_cache}" ]; then + prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | grub_add_tab)" + fi +@@ -245,16 +319,6 @@ + + title_correction_code= + +-machine=`uname -m` +- +-case "$machine" in +- i?86) GENKERNEL_ARCH="x86" ;; +- mips|mips64) GENKERNEL_ARCH="mips" ;; +- mipsel|mips64el) GENKERNEL_ARCH="mipsel" ;; +- arm*) GENKERNEL_ARCH="arm" ;; +- *) GENKERNEL_ARCH="$machine" ;; +-esac +- + # Extra indentation to add to menu entries in a submenu. We're not in a submenu + # yet, so it's empty. In a submenu it will be equal to '\t' (one tab). + submenu_indentation="" +@@ -325,6 +389,24 @@ + xen_dirname=`dirname ${current_xen}` + rel_xen_dirname=`make_system_path_relative_to_its_root $xen_dirname` + xen_version=`echo $xen_basename | sed -e "s,.gz$,,g;s,^xen-,,g"` ++ xen_list=`echo $xen_list | tr ' ' '\n' | grep -vx $current_xen | tr '\n' ' '` ++ if $is_efi; then ++ xen_basename=${xen_basename/.gz/.efi} ++ if ! [ -f ${xen_dir}/${xen_basename} ]; then ++ echo "Skip missing hypervisor $xen_dir/$xen_basename" >&2 ++ continue ++ elif [ -L ${xen_dir}/${xen_basename} ]; then ++ xen_target=$(basename $(readlink -e ${xen_dir}/${xen_basename})) ++ if [ -f ${efi_dir}/${xen_target} ]; then ++ echo "Skip duplicate $xen_dir/$xen_basename for $xen_target" >&2 ++ continue ++ fi ++ elif [ -n "$err_msg" ]; then ++ break ++ fi ++ gettext_printf "Found hypervisor: %s\n" "$current_xen" >&2 ++ section_count=0 ++ fi + if [ -z "$boot_device_id" ]; then + boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" + fi +@@ -445,3 +527,7 @@ + fi + + echo "$title_correction_code" ++ ++if [ -n "$err_msg" ]; then ++ echo -en "$err_msg" >&2 ++fi diff --git a/grub2-efi-xen-cmdline.patch b/grub2-efi-xen-cmdline.patch new file mode 100644 index 0000000..b104163 --- /dev/null +++ b/grub2-efi-xen-cmdline.patch @@ -0,0 +1,23 @@ +--- a/util/grub-mkconfig.in ++++ b/util/grub-mkconfig.in +@@ -298,7 +298,8 @@ + GRUB_BADRAM \ + GRUB_OS_PROBER_SKIP_LIST \ + GRUB_DISABLE_SUBMENU \ +- SUSE_BTRFS_SNAPSHOT_BOOTING ++ SUSE_BTRFS_SNAPSHOT_BOOTING \ ++ SUSE_CMDLINE_XENEFI + + if test "x${grub_cfg}" != "x"; then + rm -f "${grub_cfg}.new" +--- a/util/grub.d/20_linux_xen.in ++++ b/util/grub.d/20_linux_xen.in +@@ -219,7 +219,7 @@ + message="$(gettext_printf "Loading Xen %s with Linux %s ..." ${xen_version} ${version})" + sed "s/^/$submenu_indentation/" <<-EOF + echo '$(echo "$message" | grub_quote)' +- chainloader \$cmdpath/${xen_basename} ${xen_basename} $section ++ chainloader \$cmdpath/${xen_basename} ${xen_basename} ${SUSE_CMDLINE_XENEFI} $section + } + EOF + for f in ${grub_dir}/$xen_cfg ${xen_dir}/${xen_basename} ${dirname}/${basename} ${dirname}/${initrd_real}; do diff --git a/grub2-efi-xen-removable.patch b/grub2-efi-xen-removable.patch new file mode 100644 index 0000000..0efb58f --- /dev/null +++ b/grub2-efi-xen-removable.patch @@ -0,0 +1,117 @@ +From: Michael Chang +References: bsc#1085842 +Patch-Mainline: no + +The grub can be installed with removable option to support booting from +removable media with standard UEFI default file path of the form: + \EFI\BOOT\BOOT{machine type short-name}.EFI + +It does not make use of distributor directory, which becomes a problem for UEFI +Xen installation as it requires that directory to be present for storing xen +stuff like chainloaded hypervisor, xen kernel and so on. Moreover it makes bad +assumption that hypervisor will be chainloaded by grub under the same +directory, which is also not always true. + +This patch fixes the problem by ensuring the directory available to Xen +installation if any Xen hypervisor found and independent to grub boot path +$cmdpath to work. + +--- + util/grub.d/20_linux_xen.in | 62 ++++++++++++++++++++++++-------------------- + 1 file changed, 35 insertions(+), 27 deletions(-) + +--- a/util/grub.d/20_linux_xen.in ++++ b/util/grub.d/20_linux_xen.in +@@ -113,32 +113,6 @@ + + title_correction_code= + +-if [ -d /sys/firmware/efi ]; then +- is_efi=true +- err_msg="" +- efi_dir="/boot/efi/efi/${os}" +- grub_dir=/boot/@PACKAGE@ +- xen_dir=/usr/share/efi/$machine +- [ -d $xen_dir ] || xen_dir=/usr/lib64/efi +- for d in $grub_dir $efi_dir $xen_dir; do +- [ ! -d "$d" ] || continue +- err_msg="${err_msg}$ME: Essential directory '$d' not found!\n" +- done +- if ! [ -d "$efi_dir" -a -d "$grub_dir" -a -d "$xen_dir" ]; then +- err_msg="${err_msg}$ME: XEN configuration skipped!\n" +- else +- rm -f $grub_dir/xen*.cfg +- if [ -s $efi_dir/grub.xen-files ]; then +- for f in $(sort $efi_dir/grub.xen-files| uniq); do +- rm -f $efi_dir/$f +- done +- : > $efi_dir/grub.xen-files +- fi +- fi +-else +- is_efi=false +-fi +- + grub2_unquote () + { + awk ' +@@ -264,10 +238,15 @@ + kernel=${basename} root=${linux_root_device_thisversion} ${args_unq} + ramdisk=${initrd_real} + EOF ++ if [ -z "${prepare_efi_cache}" ]; then ++ grub_device_efi="`${grub_probe} --target=device /boot/efi`" ++ prepare_efi_cache="$(prepare_grub_to_access_device ${grub_device_efi} | grub_add_tab)" ++ fi ++ printf '%s\n' "${prepare_efi_cache}" | sed "s/^/$submenu_indentation/" + message="$(gettext_printf "Loading Xen %s with Linux %s ..." ${xen_version} ${version})" + sed "s/^/$submenu_indentation/" <<-EOF + echo '$(echo "$message" | grub_quote)' +- chainloader \$cmdpath/${xen_basename} ${xen_basename} ${SUSE_CMDLINE_XENEFI} $section ++ chainloader ${rel_efi_dir}/${xen_basename} ${xen_basename} ${SUSE_CMDLINE_XENEFI} $section + } + EOF + for f in ${grub_dir}/$xen_cfg ${xen_dir}/${xen_basename} ${dirname}/${basename} ${dirname}/${initrd_real}; do +@@ -363,6 +342,7 @@ + done + fi + prepare_boot_cache= ++prepare_efi_cache= + boot_device_id= + + title_correction_code= +@@ -432,6 +412,34 @@ + + is_top_level=true + ++if [ -d /sys/firmware/efi ] && [ "x${xen_list}" != "x" ]; then ++ is_efi=true ++ err_msg="" ++ efi_dir="/boot/efi/efi/${os}" ++ grub_dir=/boot/grub2 ++ xen_dir=/usr/share/efi/$machine ++ [ -d $xen_dir ] || xen_dir=/usr/lib64/efi ++ for d in $grub_dir $xen_dir; do ++ [ ! -d "$d" ] || continue ++ err_msg="${err_msg}$ME: Essential directory '$d' not found!\n" ++ done ++ if ! [ -d "$grub_dir" -a -d "$xen_dir" ]; then ++ err_msg="${err_msg}$ME: XEN configuration skipped!\n" ++ else ++ mkdir -p $efi_dir ++ rel_efi_dir=`make_system_path_relative_to_its_root $efi_dir` ++ rm -f $grub_dir/xen*.cfg ++ if [ -s $efi_dir/grub.xen-files ]; then ++ for f in $(sort $efi_dir/grub.xen-files| uniq); do ++ rm -f $efi_dir/$f ++ done ++ : > $efi_dir/grub.xen-files ++ fi ++ fi ++else ++ is_efi=false ++fi ++ + for current_xen in ${reverse_sorted_xen_list}; do + xen_basename=`basename ${current_xen}` + xen_dirname=`dirname ${current_xen}` diff --git a/grub2-efi_gop-avoid-low-resolution.patch b/grub2-efi_gop-avoid-low-resolution.patch new file mode 100644 index 0000000..5226539 --- /dev/null +++ b/grub2-efi_gop-avoid-low-resolution.patch @@ -0,0 +1,39 @@ +--- + grub-core/video/efi_gop.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +--- a/grub-core/video/efi_gop.c ++++ b/grub-core/video/efi_gop.c +@@ -358,6 +358,7 @@ grub_video_gop_setup (unsigned int width + grub_err_t err; + unsigned bpp; + int found = 0; ++ int avoid_low_resolution = 1; + unsigned long long best_volume = 0; + unsigned int preferred_width = 0, preferred_height = 0; + grub_uint8_t *buffer; +@@ -376,8 +377,11 @@ grub_video_gop_setup (unsigned int width + } + } + ++again: + /* Keep current mode if possible. */ +- if (gop->mode->info) ++ if (gop->mode->info && ++ (!avoid_low_resolution || ++ (gop->mode->info->width >= 800 && gop->mode->info->height >= 600))) + { + bpp = grub_video_gop_get_bpp (gop->mode->info); + if (bpp && ((width == gop->mode->info->width +@@ -450,6 +454,11 @@ grub_video_gop_setup (unsigned int width + + if (!found) + { ++ if (avoid_low_resolution && gop->mode->info) ++ { ++ avoid_low_resolution = 0; ++ goto again; ++ } + grub_dprintf ("video", "GOP: no mode found\n"); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found"); + } diff --git a/grub2-emu-4-all.patch b/grub2-emu-4-all.patch new file mode 100644 index 0000000..39b20fa --- /dev/null +++ b/grub2-emu-4-all.patch @@ -0,0 +1,162 @@ +--- + Makefile.util.def | 10 +++++----- + configure.ac | 1 + + grub-core/Makefile.core.def | 14 +++++--------- + grub-core/osdep/unix/emuconsole.c | 5 +++-- + 4 files changed, 14 insertions(+), 16 deletions(-) + +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -377,7 +377,7 @@ + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + cppflags = '-DGRUB_SETUP_FUNC=grub_util_bios_setup'; +- emu_condition = COND_NOT_s390x; ++ emu_condition = COND_NOT_emu; + }; + + program = { +@@ -398,7 +398,7 @@ + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + cppflags = '-DGRUB_SETUP_FUNC=grub_util_sparc_setup'; +- emu_condition = COND_NOT_s390x; ++ emu_condition = COND_NOT_emu; + }; + + program = { +@@ -414,7 +414,7 @@ + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +- emu_condition = COND_NOT_s390x; ++ emu_condition = COND_NOT_emu; + }; + + program = { +@@ -445,7 +445,7 @@ + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +- emu_condition = COND_NOT_s390x; ++ emu_condition = COND_NOT_emu; + }; + + data = { +@@ -1420,7 +1420,7 @@ + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +- emu_condition = COND_NOT_s390x; ++ emu_condition = COND_NOT_emu; + }; + + program = { +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -1183,7 +1183,7 @@ + module = { + name = videotest; + common = commands/videotest.c; +- emu_condition = COND_NOT_s390x; ++ emu_condition = COND_NOT_emu; + }; + + module = { +@@ -1638,7 +1638,7 @@ + common = gfxmenu/gui_progress_bar.c; + common = gfxmenu/gui_util.c; + common = gfxmenu/gui_string_util.c; +- emu_condition = COND_NOT_s390x; ++ emu_condition = COND_NOT_emu; + }; + + module = { +@@ -2077,13 +2077,13 @@ + name = gfxterm; + common = term/gfxterm.c; + enable = videomodules; +- emu_condition = COND_NOT_s390x; ++ emu_condition = COND_NOT_emu; + }; + + module = { + name = gfxterm_background; + common = term/gfxterm_background.c; +- emu_condition = COND_NOT_s390x; ++ emu_condition = COND_NOT_emu; + }; + + module = { +@@ -2204,9 +2204,7 @@ + enable = i386_xen_pvh; + enable = i386_efi; + enable = x86_64_efi; +- enable = emu; + enable = xen; +- emu_condition = COND_NOT_s390x; + }; + + module = { +@@ -2253,7 +2251,7 @@ + module = { + name = gfxterm_menu; + common = tests/gfxterm_menu.c; +- emu_condition = COND_NOT_s390x; ++ emu_condition = COND_NOT_emu; + }; + + module = { +@@ -2413,9 +2411,7 @@ + enable = i386_xen_pvh; + enable = i386_efi; + enable = x86_64_efi; +- enable = emu; + enable = xen; +- emu_condition = COND_NOT_s390x; + }; + + module = { +--- a/configure.ac ++++ b/configure.ac +@@ -2061,6 +2061,7 @@ + + AM_CONDITIONAL([COND_real_platform], [test x$platform != xnone]) + AM_CONDITIONAL([COND_emu], [test x$platform = xemu]) ++AM_CONDITIONAL([COND_NOT_emu], [test x$platform != xemu]) + AM_CONDITIONAL([COND_arm], [test x$target_cpu = xarm ]) + AM_CONDITIONAL([COND_arm_uboot], [test x$target_cpu = xarm -a x$platform = xuboot]) + AM_CONDITIONAL([COND_arm_coreboot], [test x$target_cpu = xarm -a x$platform = xcoreboot]) +--- a/grub-core/osdep/unix/emuconsole.c ++++ b/grub-core/osdep/unix/emuconsole.c +@@ -50,13 +50,12 @@ + static int console_mode = 0; + + #define MAX_LEN 1023 +-#if defined(__s390x__) ++ + static int + dummy (void) + { + return 0; + } +-#endif + #if 0 + static char msg[MAX_LEN+1]; + static void +@@ -128,6 +127,7 @@ + return -1; + } + ++#if defined(__s390x__) + #define NO_KEY ((grub_uint8_t)-1) + static int + readkey_dumb (struct grub_term_input *term) +@@ -158,6 +158,7 @@ + p = c; + return c; + } ++#endif + + static void + grub_dumb_putchar (struct grub_term_output *term, diff --git a/grub2-fix-error-terminal-gfxterm-isn-t-found.patch b/grub2-fix-error-terminal-gfxterm-isn-t-found.patch new file mode 100644 index 0000000..bd44eef --- /dev/null +++ b/grub2-fix-error-terminal-gfxterm-isn-t-found.patch @@ -0,0 +1,34 @@ +From e2e0fe44cf2a03744e96f886f95ab2c2a8aed331 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Wed, 18 Jul 2012 14:54:32 +0800 +Subject: [PATCH] fix error: terminal 'gfxterm' isn't found + +References: bnc#771393 +Patch-Mainline: no + +If set GRUB_TERMINAL="gfxterm", the error message "terminal +'gfxterm' isn't found" will be logged to screen. This is caused +by GRUB_TERMINAL_INPUT erroneously set to gfxterm. This patch +fixes the issue by not setting it. + +v2: +Fix error gfxterm isn't found with multiple terminals (bsc#1187565) + +--- + util/grub-mkconfig.in | 6 +++++- + 1 files changed, 5 insertions(+), 1 deletions(-) + +Index: grub-2.06/util/grub-mkconfig.in +=================================================================== +--- grub-2.06.orig/util/grub-mkconfig.in ++++ grub-2.06/util/grub-mkconfig.in +@@ -172,7 +172,8 @@ fi + + # XXX: should this be deprecated at some point? + if [ "x${GRUB_TERMINAL}" != "x" ] ; then +- GRUB_TERMINAL_INPUT="${GRUB_TERMINAL}" ++# bnc#771393, bsc#1187565 - fix error: terminal 'gfxterm' isn't found. ++ GRUB_TERMINAL_INPUT="`echo ${GRUB_TERMINAL} | sed -e '/\bgfxterm\b/{s/\bconsole\b//g;s/\bgfxterm\b/console/}'`" + GRUB_TERMINAL_OUTPUT="${GRUB_TERMINAL}" + fi + diff --git a/grub2-fix-menu-in-xen-host-server.patch b/grub2-fix-menu-in-xen-host-server.patch new file mode 100644 index 0000000..00065e1 --- /dev/null +++ b/grub2-fix-menu-in-xen-host-server.patch @@ -0,0 +1,110 @@ +From b411dc88b46890400a2e1ba0aa8650e00f738c23 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 19 Jul 2012 18:43:55 +0800 +Subject: [PATCH] fix menu in xen host server + +References: bnc#771689, bnc#757895 +Patch-Mainline: no + +When system is configred as "Xen Virtual Machines Host Server", the +grub2 menu is not well organized. We could see some issues on it. + + - Many duplicated xen entries generated by links to xen hypervisor + - Non bootable kernel entries trying to boot xen kernel natively + - The -dbg xen hypervisor takes precedence over release version + +This patch fixes above three issues. + +v2: +References: bnc#877040 +Create only hypervisor pointed by /boot/xen.gz symlink to not clutter +the menu with multiple versions and also not include -dbg. Use custom.cfg +if you need any other custom entries. + +--- + util/grub-mkconfig_lib.in | 5 +++++ + util/grub.d/10_linux.in | 12 ++++++++++-- + util/grub.d/20_linux_xen.in | 6 ++++-- + 3 files changed, 19 insertions(+), 4 deletions(-) + +--- a/util/grub.d/20_linux_xen.in ++++ b/util/grub.d/20_linux_xen.in +@@ -26,6 +26,12 @@ + export TEXTDOMAIN=@PACKAGE@ + export TEXTDOMAINDIR="@localedir@" + ++if [ ! -e /proc/xen/xsd_port -a -e /proc/xen ]; then ++# we're running on xen domU guest ++# prevent setting up nested virt on HVM or PV domU guest ++ exit 0 ++fi ++ + CLASS="--class gnu-linux --class gnu --class os --class xen" + + if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then +@@ -213,10 +219,18 @@ + esac + } + +-xen_list= +-for i in /boot/xen*; do +- if grub_file_is_not_garbage "$i" && file_is_not_xen_garbage "$i" ; then xen_list="$xen_list $i" ; fi +-done ++# bnc#877040 - Duplicate entries for boot menu created ++# only create /boot/xen.gz symlink boot entry ++if test -L /boot/xen.gz; then ++ xen_list=`readlink -f /boot/xen.gz` ++else ++ # bnc#757895 - Grub2 menu items incorrect when "Xen Virtual Machines Host Server" selected ++ # wildcard expasion with correct suffix (.gz) for not generating many duplicated menu entries ++ xen_list= ++ for i in /boot/xen*.gz; do ++ if grub_file_is_not_garbage "$i" && file_is_not_sym "$i" ; then xen_list="$xen_list $i" ; fi ++ done ++fi + prepare_boot_cache= + boot_device_id= + +--- a/util/grub.d/10_linux.in ++++ b/util/grub.d/10_linux.in +@@ -258,6 +258,40 @@ + fi + done + ++ # try to get the kernel config if $linux is a symlink ++ if test -z "${config}" ; then ++ lnk_version=`basename \`readlink -f $linux\` | sed -e "s,^[^0-9]*-,,g"` ++ if (test -n ${lnk_version} && test -e "${dirname}/config-${lnk_version}") ; then ++ config="${dirname}/config-${lnk_version}" ++ fi ++ fi ++ ++ # check if we are in xen domU ++ if [ ! -e /proc/xen/xsd_port -a -e /proc/xen ]; then ++ # we're running on xen domU guest ++ dmi=/sys/class/dmi/id ++ if [ -r "${dmi}/product_name" -a -r "${dmi}/sys_vendor" ]; then ++ product_name=`cat ${dmi}/product_name` ++ sys_vendor=`cat ${dmi}/sys_vendor` ++ if test "${sys_vendor}" = "Xen" -a "${product_name}" = "HVM domU"; then ++ # xen HVM guest ++ xen_pv_domU=false ++ fi ++ fi ++ else ++ # we're running on baremetal or xen dom0 ++ xen_pv_domU=false ++ fi ++ ++ if test "$xen_pv_domU" = "false" ; then ++ # prevent xen kernel without pv_opt support from booting ++ if (grep -qx "CONFIG_XEN=y" "${config}" 2> /dev/null && ! grep -qx "CONFIG_PARAVIRT=y" "${config}" 2> /dev/null); then ++ echo "Skip xenlinux kernel $linux" >&2 ++ list=`echo $list | tr ' ' '\n' | grep -vx $linux | tr '\n' ' '` ++ continue ++ fi ++ fi ++ + initramfs= + if test -n "${config}" ; then + initramfs=`grep CONFIG_INITRAMFS_SOURCE= "${config}" | cut -f2 -d= | tr -d \"` diff --git a/grub2-getroot-scan-disk-pv.patch b/grub2-getroot-scan-disk-pv.patch new file mode 100644 index 0000000..745e99e --- /dev/null +++ b/grub2-getroot-scan-disk-pv.patch @@ -0,0 +1,43 @@ +From: Michael Chang +Subject: Fix grub2-mkconfig warning when disk is LVM PV +References: bsc#1071239 + +When a disk device was found in grub_util_biosdisk_get_grub_dev, its grub +hostdisk device name just returned. Since the disk could also be used as PV +disk, use grub_util_get_ldm to kick scanning of on disk metadata and adding it +to VG array. + +--- +Index: grub-2.02/util/getroot.c +=================================================================== +--- grub-2.02.orig/util/getroot.c ++++ grub-2.02/util/getroot.c +@@ -272,8 +272,28 @@ grub_util_biosdisk_get_grub_dev (const c + grub_util_info ("%s is a parent of %s", sys_disk, os_dev); + if (!is_part) + { ++#if defined(__APPLE__) + free (sys_disk); + return make_device_name (drive); ++#else ++ char *name, *ldm_name; ++ grub_disk_t disk; ++ ++ free (sys_disk); ++ name = make_device_name (drive); ++ disk = grub_disk_open (name); ++ if (!disk) ++ return name; ++ ldm_name = grub_util_get_ldm (disk, 0); ++ if (ldm_name) ++ { ++ grub_disk_close (disk); ++ grub_free (name); ++ return ldm_name; ++ } ++ grub_disk_close (disk); ++ return name; ++#endif + } + free (sys_disk); + diff --git a/grub2-getroot-support-nvdimm.patch b/grub2-getroot-support-nvdimm.patch new file mode 100644 index 0000000..3a5051a --- /dev/null +++ b/grub2-getroot-support-nvdimm.patch @@ -0,0 +1,66 @@ +From 889c0894d358e48c02f8225426893094f20004e5 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Thu, 4 Oct 2018 10:32:07 +0800 +Subject: [PATCH] linux/getroot: Support NVDIMM device names + +There are two types of NVDIMM block devices in linux: fsdax and blk. +For fsdax, the device name would be /dev/pmemXpY, /dev/pmemXsY, +/dev/pmemX.YpZ, or /dev/pmemX.YsZ. +For blk, the name would be /dev/ndblkX.YpZ or /dev/ndblkX.YsZ +--- + grub-core/osdep/linux/getroot.c | 44 +++++++++++++++++++++++++++++++++ + 1 file changed, 44 insertions(+) + +--- a/grub-core/osdep/linux/getroot.c ++++ b/grub-core/osdep/linux/getroot.c +@@ -971,6 +971,50 @@ + *pp = '\0'; + return path; + } ++ ++ /* If this is a NVDIMM device in fsdax mode */ ++ if (strncmp ("pmem", p, 4) == 0 && p[4] >= '0' && p[4] <= '9') ++ { ++ /* /dev/pmem[0-9]+(\.[0-9]+)?((p[0-9]+)?|s[0-9]*) */ ++ char *pp = strchr (p + 4, 'p'); ++ if (pp) ++ { ++ *is_part = 1; ++ *pp = '\0'; ++ } ++ else ++ { ++ pp = strchr (p + 4, 's'); ++ if (pp && pp[1] >= '0' && pp[1] <= '9') ++ { ++ *is_part = 1; ++ pp[1] = '\0'; ++ } ++ } ++ return path; ++ } ++ ++ /* If this is a NVDIMM device in block mode */ ++ if (strncmp ("ndblk", p, 5) == 0 && p[5] >= '0' && p[5] <= '9') ++ { ++ /* /dev/ndblk[0-9]+\.[0-9]+((p[0-9]+)?|s[0-9]*) */ ++ char *pp = strchr (p + 5, 'p'); ++ if (pp) ++ { ++ *is_part = 1; ++ *pp = '\0'; ++ } ++ else ++ { ++ pp = strchr (p + 5, 's'); ++ if (pp && pp[1] >= '0' && pp[1] <= '9') ++ { ++ *is_part = 1; ++ pp[1] = '\0'; ++ } ++ } ++ return path; ++ } + } + + return path; diff --git a/grub2-getroot-treat-mdadm-ddf-as-simple-device.patch b/grub2-getroot-treat-mdadm-ddf-as-simple-device.patch new file mode 100644 index 0000000..cf75d71 --- /dev/null +++ b/grub2-getroot-treat-mdadm-ddf-as-simple-device.patch @@ -0,0 +1,66 @@ +From: Michael Chang +Subject: treat mdadm ddf fakeraid as simple device +References: bnc#872360 +Patch-Mainline: no + +--- a/grub-core/osdep/linux/getroot.c ++++ b/grub-core/osdep/linux/getroot.c +@@ -119,7 +119,7 @@ + struct btrfs_ioctl_fs_info_args) + + static int +-grub_util_is_imsm (const char *os_dev); ++grub_util_is_imsm_or_ddf (const char *os_dev); + + + #define ESCAPED_PATH_MAX (4 * PATH_MAX) +@@ -635,10 +635,10 @@ + } + + static int +-grub_util_is_imsm (const char *os_dev) ++grub_util_is_imsm_or_ddf (const char *os_dev) + { + int retry; +- int is_imsm = 0; ++ int is_imsm_or_ddf = 0; + int container_seen = 0; + const char *dev = os_dev; + +@@ -699,10 +699,17 @@ + if (strncmp (buf, "MD_METADATA=imsm", + sizeof ("MD_METADATA=imsm") - 1) == 0) + { +- is_imsm = 1; ++ is_imsm_or_ddf = 1; + grub_util_info ("%s is imsm", dev); + break; + } ++ if (strncmp (buf, "MD_METADATA=ddf", ++ sizeof ("MD_METADATA=ddf") - 1) == 0) ++ { ++ is_imsm_or_ddf = 1; ++ grub_util_info ("%s is ddf", dev); ++ break; ++ } + } + + free (buf); +@@ -713,7 +720,7 @@ + + if (dev != os_dev) + free ((void *) dev); +- return is_imsm; ++ return is_imsm_or_ddf; + } + + char * +@@ -1078,7 +1085,7 @@ + + /* Check for RAID. */ + if (!strncmp (os_dev, "/dev/md", 7) && ! grub_util_device_is_mapped (os_dev) +- && !grub_util_is_imsm (os_dev)) ++ && !grub_util_is_imsm_or_ddf (os_dev)) + return GRUB_DEV_ABSTRACTION_RAID; + return GRUB_DEV_ABSTRACTION_NONE; + } diff --git a/grub2-gfxmenu-support-scrolling-menu-entry-s-text.patch b/grub2-gfxmenu-support-scrolling-menu-entry-s-text.patch new file mode 100644 index 0000000..fd9f600 --- /dev/null +++ b/grub2-gfxmenu-support-scrolling-menu-entry-s-text.patch @@ -0,0 +1,269 @@ +From b4c5f80fbfaf912553eca1b12da6fc49de4ae37f Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Mon, 7 Jan 2019 17:55:05 +0800 +Subject: [PATCH] gfxmenu: support scrolling menu entry's text + +If menu entry's title text is longer than its display width, the +overlong text simply get truncated. The only possible way to view the +full text is through the menu editing mode, but is a hassle switching +over the mode back and forth. Also menu editing mode could be password +protected which makes it not generally available to everyone. + +This patch implemented scrolling text support to the title of grub's +gfxmenu to make it convenient for viewing the truncated text by pressing +the ctrl+l and ctrl+r to scroll the highlighted text left and right. The +scrolled result will remain in place to help memorizing it after +changing highlight to other entry. + +V1: + * Use grub_calloc for overflow check and return NULL when it would + occur. +--- + grub-core/gfxmenu/gfxmenu.c | 3 +++ + grub-core/gfxmenu/gui_label.c | 2 ++ + grub-core/gfxmenu/gui_list.c | 38 ++++++++++++++++++++++++++++++++++ + grub-core/gfxmenu/view.c | 48 +++++++++++++++++++++++++++++++++++++++++++ + grub-core/normal/menu.c | 16 +++++++++++++++ + include/grub/gfxmenu_view.h | 4 ++++ + include/grub/menu_viewer.h | 1 + + 7 files changed, 112 insertions(+) + +--- a/grub-core/gfxmenu/gfxmenu.c ++++ b/grub-core/gfxmenu/gfxmenu.c +@@ -108,6 +108,15 @@ + view->menu = menu; + view->nested = nested; + view->first_timeout = -1; ++ if (menu->size) ++ { ++ view->menu_title_offset = grub_calloc (menu->size, sizeof (*view->menu_title_offset)); ++ if (!view->menu_title_offset) ++ { ++ grub_free (instance); ++ return grub_errno; ++ } ++ } + + grub_video_set_viewport (0, 0, mode_info.width, mode_info.height); + if (view->double_repaint) +@@ -123,6 +132,7 @@ + instance->fini = grub_gfxmenu_viewer_fini; + instance->print_timeout = grub_gfxmenu_print_timeout; + instance->clear_timeout = grub_gfxmenu_clear_timeout; ++ instance->scroll_chosen_entry = grub_gfxmenu_scroll_chosen_entry; + + grub_menu_register_viewer (instance); + +--- a/grub-core/gfxmenu/gui_label.c ++++ b/grub-core/gfxmenu/gui_label.c +@@ -192,6 +192,8 @@ + "or `c' for a command-line."); + else if (grub_strcmp (value, "@KEYMAP_SHORT@") == 0) + value = _("enter: boot, `e': options, `c': cmd-line"); ++ else if (grub_strcmp (value, "@SUSE_KEYMAP_SCROLL_ENTRY@") == 0) ++ value = _("ctrl+l: scroll entry left, ctrl+r: scroll entry right"); + /* FIXME: Add more templates here if needed. */ + + if (grub_printf_fmt_check(value, "%d") != GRUB_ERR_NONE) +--- a/grub-core/gfxmenu/gui_list.c ++++ b/grub-core/gfxmenu/gui_list.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + enum scrollbar_slice_mode { + SCROLLBAR_SLICE_WEST, +@@ -314,6 +315,33 @@ + thumb->draw (thumb, thumbx, thumby); + } + ++static const char * ++grub_utf8_offset_code (const char *src, grub_size_t srcsize, int num) ++{ ++ int count = 0; ++ grub_uint32_t code = 0; ++ ++ while (srcsize && num) ++ { ++ if (srcsize != (grub_size_t)-1) ++ srcsize--; ++ if (!grub_utf8_process ((grub_uint8_t)*src++, &code, &count)) ++ return 0; ++ if (count != 0) ++ continue; ++ if (code == 0) ++ return 0; ++ if (code > GRUB_UNICODE_LAST_VALID) ++ return 0; ++ --num; ++ } ++ ++ if (!num) ++ return src; ++ ++ return 0; ++} ++ + /* Draw the list of items. */ + static void + draw_menu (list_impl_t self, int num_shown_items) +@@ -433,6 +461,16 @@ + const char *item_title = + grub_menu_get_entry (self->view->menu, menu_index)->title; + ++ { ++ int off = self->view->menu_title_offset[menu_index]; ++ const char *scrolled_title; ++ ++ scrolled_title = grub_utf8_offset_code (item_title, grub_strlen (item_title), off); ++ ++ if (scrolled_title) ++ item_title = scrolled_title; ++ } ++ + sviewport.y = item_top + top_pad; + sviewport.width = viewport_width; + grub_gui_set_viewport (&sviewport, &svpsave); +--- a/grub-core/gfxmenu/view.c ++++ b/grub-core/gfxmenu/view.c +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + + static void + init_terminal (grub_gfxmenu_view_t view); +@@ -103,6 +104,7 @@ + view->title_text = grub_strdup (_("GRUB Boot Menu")); + view->progress_message_text = 0; + view->theme_path = 0; ++ view->menu_title_offset = 0; + + /* Set the timeout bar's frame. */ + view->progress_message_frame.width = view->screen.width * 4 / 5; +@@ -142,6 +144,7 @@ + grub_free (view->title_text); + grub_free (view->progress_message_text); + grub_free (view->theme_path); ++ grub_free (view->menu_title_offset); + if (view->canvas) + view->canvas->component.ops->destroy (view->canvas); + grub_free (view); +@@ -410,6 +413,52 @@ + grub_gfxmenu_redraw_menu (view); + } + ++static int ++grub_utf8_get_num_code (const char *src, grub_size_t srcsize) ++{ ++ int count = 0; ++ grub_uint32_t code = 0; ++ int num = 0; ++ ++ while (srcsize) ++ { ++ if (srcsize != (grub_size_t)-1) ++ srcsize--; ++ if (!grub_utf8_process ((grub_uint8_t)*src++, &code, &count)) ++ return 0; ++ if (count != 0) ++ continue; ++ if (code == 0) ++ return num; ++ if (code > GRUB_UNICODE_LAST_VALID) ++ return 0; ++ ++num; ++ } ++ ++ return num; ++} ++ ++void ++grub_gfxmenu_scroll_chosen_entry (void *data, int diren) ++{ ++ grub_gfxmenu_view_t view = data; ++ const char *item_title; ++ int off; ++ ++ if (!view->menu->size) ++ return; ++ ++ item_title =grub_menu_get_entry (view->menu, view->selected)->title; ++ off = view->menu_title_offset[view->selected] + diren; ++ ++ if (off < 0 ++ || off > grub_utf8_get_num_code (item_title, grub_strlen(item_title))) ++ return; ++ ++ view->menu_title_offset[view->selected] = off; ++ grub_gfxmenu_redraw_menu (view); ++} ++ + static void + grub_gfxmenu_draw_terminal_box (void) + { +--- a/grub-core/normal/menu.c ++++ b/grub-core/normal/menu.c +@@ -400,6 +400,15 @@ + } + + static void ++menu_scroll_chosen_entry (int diren) ++{ ++ struct grub_menu_viewer *cur; ++ for (cur = viewers; cur; cur = cur->next) ++ if (cur->scroll_chosen_entry) ++ cur->scroll_chosen_entry (cur->data, diren); ++} ++ ++static void + menu_print_timeout (int timeout) + { + struct grub_menu_viewer *cur; +@@ -829,6 +838,13 @@ + menu_set_chosen_entry (current_entry); + break; + ++ case GRUB_TERM_CTRL | 'w': ++ menu_scroll_chosen_entry (1); ++ break; ++ case GRUB_TERM_CTRL | 'r': ++ menu_scroll_chosen_entry (-1); ++ break; ++ + case '\n': + case '\r': + case GRUB_TERM_KEY_RIGHT: +--- a/include/grub/gfxmenu_view.h ++++ b/include/grub/gfxmenu_view.h +@@ -61,6 +61,8 @@ + grub_gfxmenu_print_timeout (int timeout, void *data); + void + grub_gfxmenu_set_chosen_entry (int entry, void *data); ++void ++grub_gfxmenu_scroll_chosen_entry (void *data, int diren); + + grub_err_t grub_font_draw_string (const char *str, + grub_font_t font, +@@ -119,6 +121,8 @@ + int nested; + + int first_timeout; ++ ++ int *menu_title_offset; + }; + + #endif /* ! GRUB_GFXMENU_VIEW_HEADER */ +--- a/include/grub/menu_viewer.h ++++ b/include/grub/menu_viewer.h +@@ -33,6 +33,7 @@ + void (*set_chosen_entry) (int entry, void *data); + void (*print_timeout) (int timeout, void *data); + void (*clear_timeout) (void *data); ++ void (*scroll_chosen_entry) (void *data, int diren); + void (*fini) (void *fini); + }; + diff --git a/grub2-grubenv-in-btrfs-header.patch b/grub2-grubenv-in-btrfs-header.patch new file mode 100644 index 0000000..560dead --- /dev/null +++ b/grub2-grubenv-in-btrfs-header.patch @@ -0,0 +1,498 @@ +GRUB cannot write Btrfs file systems from the bootloader, so it cannot +modify values set from userspace (e.g. "next_entry" set by grub2-once). +As a workaround use the Btrfs header to store known data of the GRUB environment +block. + +v2: export env_block and make sure to use the device of grubenv + +v3: + * Use xcalloc for overflow check and return NULL when it would + occur. + +--- +--- a/grub-core/kern/fs.c ++++ b/grub-core/kern/fs.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + grub_fs_t grub_fs_list = 0; + +@@ -236,6 +237,13 @@ + size, buf) != GRUB_ERR_NONE) + return -1; + ++ if (file->read_hook) ++ { ++ grub_disk_addr_t part_start; ++ ++ part_start = grub_partition_get_start (file->device->disk->partition); ++ file->read_hook (p->offset + sector + part_start, (unsigned)offset, (unsigned)size, NULL, file->read_hook_data); ++ } + ret += size; + len -= size; + sector -= ((size + offset) >> GRUB_DISK_SECTOR_BITS); +--- a/util/grub-editenv.c ++++ b/util/grub-editenv.c +@@ -23,8 +23,11 @@ + #include + #include + #include +-#include ++#include + #include ++#include ++#include ++#include + + #include + #include +@@ -120,6 +123,140 @@ + NULL, help_filter, NULL + }; + ++struct fs_envblk_spec { ++ const char *fs_name; ++ int offset; ++ int size; ++} fs_envblk_spec[] = { ++ { "btrfs", 256 * 1024, GRUB_DISK_SECTOR_SIZE }, ++ { NULL, 0, 0 } ++}; ++ ++struct fs_envblk { ++ struct fs_envblk_spec *spec; ++ const char *dev; ++}; ++ ++typedef struct fs_envblk_spec *fs_envblk_spec_t; ++typedef struct fs_envblk *fs_envblk_t; ++ ++fs_envblk_t fs_envblk = NULL; ++ ++static int ++read_envblk_fs (const char *varname, const char *value, void *hook_data) ++{ ++ grub_envblk_t *p_envblk = (grub_envblk_t *)hook_data; ++ ++ if (!p_envblk || !fs_envblk) ++ return 0; ++ ++ if (strcmp (varname, "env_block") == 0) ++ { ++ int off, sz; ++ char *p; ++ ++ off = strtol (value, &p, 10); ++ if (*p == '+') ++ sz = strtol (p+1, &p, 10); ++ ++ if (*p == '\0') ++ { ++ FILE *fp; ++ char *buf; ++ ++ off <<= GRUB_DISK_SECTOR_BITS; ++ sz <<= GRUB_DISK_SECTOR_BITS; ++ ++ fp = grub_util_fopen (fs_envblk->dev, "rb"); ++ if (! fp) ++ grub_util_error (_("cannot open `%s': %s"), fs_envblk->dev, ++ strerror (errno)); ++ ++ ++ if (fseek (fp, off, SEEK_SET) < 0) ++ grub_util_error (_("cannot seek `%s': %s"), fs_envblk->dev, ++ strerror (errno)); ++ ++ buf = xmalloc (sz); ++ if ((fread (buf, 1, sz, fp)) != sz) ++ grub_util_error (_("cannot read `%s': %s"), fs_envblk->dev, ++ strerror (errno)); ++ ++ fclose (fp); ++ ++ *p_envblk = grub_envblk_open (buf, sz); ++ } ++ } ++ ++ return 0; ++} ++ ++static void ++create_envblk_fs (void) ++{ ++ FILE *fp; ++ char *buf; ++ const char *device; ++ int offset, size; ++ ++ if (!fs_envblk) ++ return; ++ ++ device = fs_envblk->dev; ++ offset = fs_envblk->spec->offset; ++ size = fs_envblk->spec->size; ++ ++ fp = grub_util_fopen (device, "r+b"); ++ if (! fp) ++ grub_util_error (_("cannot open `%s': %s"), device, strerror (errno)); ++ ++ buf = xmalloc (size); ++ memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1); ++ memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#', size - sizeof (GRUB_ENVBLK_SIGNATURE) + 1); ++ ++ if (fseek (fp, offset, SEEK_SET) < 0) ++ grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno)); ++ ++ if (fwrite (buf, 1, size, fp) != size) ++ grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno)); ++ ++ grub_util_file_sync (fp); ++ free (buf); ++ fclose (fp); ++} ++ ++static grub_envblk_t ++open_envblk_fs (grub_envblk_t envblk) ++{ ++ grub_envblk_t envblk_fs = NULL; ++ char *val; ++ int offset, size; ++ ++ if (!fs_envblk) ++ return NULL; ++ ++ offset = fs_envblk->spec->offset; ++ size = fs_envblk->spec->size; ++ ++ grub_envblk_iterate (envblk, &envblk_fs, read_envblk_fs); ++ ++ if (envblk_fs && grub_envblk_size (envblk_fs) == size) ++ return envblk_fs; ++ ++ create_envblk_fs (); ++ ++ offset = offset >> GRUB_DISK_SECTOR_BITS; ++ size = (size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS; ++ ++ val = xasprintf ("%d+%d", offset, size); ++ if (! grub_envblk_set (envblk, "env_block", val)) ++ grub_util_error ("%s", _("environment block too small")); ++ grub_envblk_iterate (envblk, &envblk_fs, read_envblk_fs); ++ free (val); ++ ++ return envblk_fs; ++} ++ + static grub_envblk_t + open_envblk_file (const char *name) + { +@@ -182,10 +319,17 @@ + list_variables (const char *name) + { + grub_envblk_t envblk; ++ grub_envblk_t envblk_fs = NULL; + + envblk = open_envblk_file (name); ++ grub_envblk_iterate (envblk, &envblk_fs, read_envblk_fs); + grub_envblk_iterate (envblk, NULL, print_var); + grub_envblk_close (envblk); ++ if (envblk_fs) ++ { ++ grub_envblk_iterate (envblk_fs, NULL, print_var); ++ grub_envblk_close (envblk_fs); ++ } + } + + static void +@@ -209,6 +353,38 @@ + } + + static void ++write_envblk_fs (grub_envblk_t envblk) ++{ ++ FILE *fp; ++ const char *device; ++ int offset, size; ++ ++ if (!fs_envblk) ++ return; ++ ++ device = fs_envblk->dev; ++ offset = fs_envblk->spec->offset; ++ size = fs_envblk->spec->size; ++ ++ if (grub_envblk_size (envblk) > size) ++ grub_util_error ("%s", _("environment block too small")); ++ ++ fp = grub_util_fopen (device, "r+b"); ++ ++ if (! fp) ++ grub_util_error (_("cannot open `%s': %s"), device, strerror (errno)); ++ ++ if (fseek (fp, offset, SEEK_SET) < 0) ++ grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno)); ++ ++ if (fwrite (grub_envblk_buffer (envblk), 1, grub_envblk_size (envblk), fp) != grub_envblk_size (envblk)) ++ grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno)); ++ ++ grub_util_file_sync (fp); ++ fclose (fp); ++} ++ ++static void + set_variables (const char *name, int argc, char *argv[]) + { + grub_envblk_t envblk; +@@ -224,8 +400,27 @@ + + *(p++) = 0; + +- if (! grub_envblk_set (envblk, argv[0], p)) +- grub_util_error ("%s", _("environment block too small")); ++ if ((strcmp (argv[0], "next_entry") == 0 || ++ strcmp (argv[0], "health_checker_flag") == 0) && fs_envblk) ++ { ++ grub_envblk_t envblk_fs; ++ envblk_fs = open_envblk_fs (envblk); ++ if (!envblk_fs) ++ grub_util_error ("%s", _("can't open fs environment block")); ++ if (! grub_envblk_set (envblk_fs, argv[0], p)) ++ grub_util_error ("%s", _("environment block too small")); ++ write_envblk_fs (envblk_fs); ++ grub_envblk_close (envblk_fs); ++ } ++ else if (strcmp (argv[0], "env_block") == 0) ++ { ++ grub_util_warn ("can't set env_block as it's read-only"); ++ } ++ else ++ { ++ if (! grub_envblk_set (envblk, argv[0], p)) ++ grub_util_error ("%s", _("environment block too small")); ++ } + + argc--; + argv++; +@@ -233,26 +428,158 @@ + + write_envblk (name, envblk); + grub_envblk_close (envblk); ++ + } + + static void + unset_variables (const char *name, int argc, char *argv[]) + { + grub_envblk_t envblk; ++ grub_envblk_t envblk_fs; + + envblk = open_envblk_file (name); ++ ++ envblk_fs = NULL; ++ if (fs_envblk) ++ envblk_fs = open_envblk_fs (envblk); ++ + while (argc) + { + grub_envblk_delete (envblk, argv[0]); + ++ if (envblk_fs) ++ grub_envblk_delete (envblk_fs, argv[0]); ++ + argc--; + argv++; + } + + write_envblk (name, envblk); + grub_envblk_close (envblk); ++ ++ if (envblk_fs) ++ { ++ write_envblk_fs (envblk_fs); ++ grub_envblk_close (envblk_fs); ++ } ++} ++ ++int have_abstraction = 0; ++static void ++probe_abstraction (grub_disk_t disk) ++{ ++ if (disk->partition == NULL) ++ grub_util_info ("no partition map found for %s", disk->name); ++ ++ if (disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID || ++ disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) ++ { ++ have_abstraction = 1; ++ } + } + ++static fs_envblk_t ++probe_fs_envblk (fs_envblk_spec_t spec) ++{ ++ char **grub_devices; ++ char **curdev, **curdrive; ++ size_t ndev = 0; ++ char **grub_drives; ++ grub_device_t grub_dev = NULL; ++ grub_fs_t grub_fs; ++ const char *fs_envblk_device; ++ ++#ifdef __s390x__ ++ return NULL; ++#endif ++ ++ grub_util_biosdisk_init (DEFAULT_DEVICE_MAP); ++ grub_init_all (); ++ grub_gcry_init_all (); ++ ++ grub_lvm_fini (); ++ grub_mdraid09_fini (); ++ grub_mdraid1x_fini (); ++ grub_diskfilter_fini (); ++ grub_diskfilter_init (); ++ grub_mdraid09_init (); ++ grub_mdraid1x_init (); ++ grub_lvm_init (); ++ ++ grub_devices = grub_guess_root_devices (DEFAULT_DIRECTORY); ++ ++ if (!grub_devices || !grub_devices[0]) ++ grub_util_error (_("cannot find a device for %s (is /dev mounted?)"), DEFAULT_DIRECTORY); ++ ++ fs_envblk_device = grub_devices[0]; ++ ++ for (curdev = grub_devices; *curdev; curdev++) ++ { ++ grub_util_pull_device (*curdev); ++ ndev++; ++ } ++ ++ grub_drives = xcalloc ((ndev + 1), sizeof (grub_drives[0])); ++ ++ for (curdev = grub_devices, curdrive = grub_drives; *curdev; curdev++, ++ curdrive++) ++ { ++ *curdrive = grub_util_get_grub_dev (*curdev); ++ if (! *curdrive) ++ grub_util_error (_("cannot find a GRUB drive for %s. Check your device.map"), ++ *curdev); ++ } ++ *curdrive = 0; ++ ++ grub_dev = grub_device_open (grub_drives[0]); ++ if (! grub_dev) ++ grub_util_error ("%s", grub_errmsg); ++ ++ grub_fs = grub_fs_probe (grub_dev); ++ if (! grub_fs) ++ grub_util_error ("%s", grub_errmsg); ++ ++ if (grub_dev->disk) ++ { ++ probe_abstraction (grub_dev->disk); ++ } ++ for (curdrive = grub_drives + 1; *curdrive; curdrive++) ++ { ++ grub_device_t dev = grub_device_open (*curdrive); ++ if (!dev) ++ continue; ++ if (dev->disk) ++ probe_abstraction (dev->disk); ++ grub_device_close (dev); ++ } ++ ++ free (grub_drives); ++ grub_device_close (grub_dev); ++ grub_gcry_fini_all (); ++ grub_fini_all (); ++ grub_util_biosdisk_fini (); ++ ++ fs_envblk_spec_t p; ++ ++ for (p = spec; p->fs_name; p++) ++ { ++ if (strcmp (grub_fs->name, p->fs_name) == 0 && !have_abstraction) ++ { ++ if (p->offset % GRUB_DISK_SECTOR_SIZE == 0 && ++ p->size % GRUB_DISK_SECTOR_SIZE == 0) ++ { ++ fs_envblk = xmalloc (sizeof (fs_envblk_t)); ++ fs_envblk->spec = p; ++ fs_envblk->dev = strdup(fs_envblk_device); ++ return fs_envblk; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++ + int + main (int argc, char *argv[]) + { +@@ -284,6 +611,9 @@ + command = argv[curindex++]; + } + ++ if (strcmp (filename, DEFAULT_ENVBLK_PATH) == 0) ++ fs_envblk = probe_fs_envblk (fs_envblk_spec); ++ + if (strcmp (command, "create") == 0) + grub_util_create_envblk_file (filename); + else if (strcmp (command, "list") == 0) +--- a/util/grub.d/00_header.in ++++ b/util/grub.d/00_header.in +@@ -46,6 +46,13 @@ + if [ -s \$prefix/grubenv ]; then + load_env + fi ++ ++if [ "\${env_block}" ] ; then ++ set env_block="(\${root})\${env_block}" ++ export env_block ++ load_env -f "\${env_block}" ++fi ++ + EOF + if [ "x$GRUB_BUTTON_CMOS_ADDRESS" != "x" ]; then + cat < +Date: Fri, 16 Dec 2022 09:19:50 +0800 +Subject: [PATCH] commands/crypttab: increase the size of the path buffer + +Allocate a larger buffer for the cryptsetup.d path in case the system +uses a long volume name. + +Signed-off-by: Gary Lin +--- + grub-core/commands/crypttab.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: grub-2.06/grub-core/commands/crypttab.c +=================================================================== +--- grub-2.06.orig/grub-core/commands/crypttab.c ++++ grub-2.06/grub-core/commands/crypttab.c +@@ -11,7 +11,7 @@ static grub_err_t + grub_cmd_crypttab_entry (grub_command_t cmd __attribute__ ((unused)), + int argc, char **argv) + { +- char buf[64]; ++ char buf[256]; + const char *path = NULL; + + if (argc == 2) diff --git a/grub2-install-fix-not-a-directory-error.patch b/grub2-install-fix-not-a-directory-error.patch new file mode 100644 index 0000000..d4ed8f1 --- /dev/null +++ b/grub2-install-fix-not-a-directory-error.patch @@ -0,0 +1,38 @@ +From: Stefan Seyfried +Subject: Makefile.am: makes sure that ext2/3/4 is tried before minix +References: boo#1161641 + +I recently came across a strange grub2-install error when building kiwi images +in OBS. The reason is a bug in the minix file system detection. I filed +upstream bug [1]. + +Note I experienced this on SLES15-SP1. The bug is still present in current +Tumbleweed [2]. This bug thus needs fixing in all supported openSUSE releases. + +The reproducer-script is called as root like + + bash ./grub-bug-57652-reproduce-suse.sh /tmp/grub-test.img /mnt + +/tmp needs 1GB of free storage to store the image. + +Maybe this would be good enough as a minimal-intrusive fix. It does not fix the +minix detection code, but instead makes sure that ext[234] is tried before +minix. + +[1] https://savannah.gnu.org/bugs/index.php?57652 +[2] https://bugzilla.opensuse.org/attachment.cgi?id=828118 + +--- a/Makefile.am ++++ b/Makefile.am +@@ -51,8 +51,11 @@ + -D'GRUB_MOD_INIT(x)=@MARKER@x@' $^ > $@ || (rm -f $@; exit 1) + CLEANFILES += libgrub.pp + ++# the grep/sed ensures that ext2 gets initialized before minix* ++# see https://savannah.gnu.org/bugs/?57652 + libgrub_a_init.lst: libgrub.pp + cat $< | grep '^@MARKER@' | sed 's/@MARKER@\(.*\)@/\1/g' | sort -u > $@ || (rm -f $@; exit 1) ++ if grep ^ext2 $@ >/dev/null; then sed '/ext2/d;/newc/iext2' < $@ > $@.tmp && mv $@.tmp $@; fi + CLEANFILES += libgrub_a_init.lst + + libgrub_a_init.c: libgrub_a_init.lst $(top_srcdir)/geninit.sh diff --git a/grub2-install-remove-useless-check-PReP-partition-is-empty.patch b/grub2-install-remove-useless-check-PReP-partition-is-empty.patch new file mode 100644 index 0000000..02190ca --- /dev/null +++ b/grub2-install-remove-useless-check-PReP-partition-is-empty.patch @@ -0,0 +1,77 @@ +From b57af595c94db6d7babb7623c1530ee4f5b956f0 Mon Sep 17 00:00:00 2001 +From: Michal Suchanek +Date: Tue, 31 Oct 2017 14:28:54 +0100 +Subject: [PATCH] grub-install: remove useless check PReP partition is empty. + +References: bsc#1065738 + +The grub-install rewrite in commit cd46aa6cefab checks that the PPeP +partition does not install an ELF binary before writing grub to it. This +causes regression in installer scripts that expect to be able to +reinstall bootloaders without first witping the partition by hand. + +Fixes: cd46aa6cefab ("Rewrite grub-install, grub-mkrescue, + grub-mkstandalone and grub-mknetdir ") +--- + util/grub-install.c | 39 ++------------------------------------- + 1 file changed, 2 insertions(+), 37 deletions(-) + +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -759,34 +759,6 @@ + return 0; + } + +-static int +-is_prep_empty (grub_device_t dev) +-{ +- grub_disk_addr_t dsize, addr; +- grub_uint32_t buffer[32768]; +- +- dsize = grub_disk_native_sectors (dev->disk); +- for (addr = 0; addr < dsize; +- addr += sizeof (buffer) / GRUB_DISK_SECTOR_SIZE) +- { +- grub_size_t sz = sizeof (buffer); +- grub_uint32_t *ptr; +- +- if (sizeof (buffer) / GRUB_DISK_SECTOR_SIZE > dsize - addr) +- sz = (dsize - addr) * GRUB_DISK_SECTOR_SIZE; +- grub_disk_read (dev->disk, addr, 0, sz, buffer); +- +- if (addr == 0 && grub_memcmp (buffer, ELFMAG, SELFMAG) == 0) +- return 1; +- +- for (ptr = buffer; ptr < buffer + sz / sizeof (*buffer); ptr++) +- if (*ptr) +- return 0; +- } +- +- return 1; +-} +- + static void + bless (grub_device_t dev, const char *path, int x86) + { +@@ -1980,18 +1952,9 @@ + { + grub_util_error ("%s", _("the chosen partition is not a PReP partition")); + } +- if (is_prep_empty (ins_dev)) +- { +- if (write_to_disk (ins_dev, imgfile)) +- grub_util_error ("%s", _("failed to copy Grub to the PReP partition")); +- grub_set_install_backup_ponr (); +- } +- else +- { +- char *s = xasprintf ("dd if=/dev/zero of=%s", install_device); +- grub_util_error (_("the PReP partition is not empty. If you are sure you want to use it, run dd to clear it: `%s'"), +- s); +- } ++ if (write_to_disk (ins_dev, imgfile)) ++ grub_util_error ("%s", _("failed to copy Grub to the PReP partition")); ++ grub_set_install_backup_ponr (); + grub_device_close (ins_dev); + if (update_nvram) + grub_install_register_ieee1275 (1, grub_util_get_os_disk (install_device), diff --git a/grub2-instdev-fixup.pl b/grub2-instdev-fixup.pl new file mode 100644 index 0000000..dfd809b --- /dev/null +++ b/grub2-instdev-fixup.pl @@ -0,0 +1,336 @@ +#!/usr/bin/perl + +use strict; +use integer; +use bytes; +eval 'use File::Copy qw(copy move)'; +eval 'use File::Temp qw(mkstemp mktemp)'; +eval 'use POSIX qw(uname)'; +eval 'use Cwd qw(realpath)'; + +my $device; +my $diskboot; +my $instdev; +my $diskboot_start; +my $default_backup; +my $default = "/etc/default/grub_installdevice"; +my $debug = 0; + +$debug = 1 if ($ARGV[0] =~ m/^(--debug|-d)$/); + +sub is_part ($) { + my ($dev) = @_; + my $ret; + + $dev = realpath($dev); + if ($dev =~ qr{/dev/(.+)}) { + $ret = 1 if (-e "/sys/class/block/$1/partition"); + } + $ret; +} + +sub is_abstraction ($) { + my ($path) = @_; + my @abs; + + chomp( @abs = qx{grub2-probe --target=abstraction $path} ); + die "Failed to probe $path for target abstraction\n" if ($? != 0); + @abs; +} + +sub default_installdevice () { + my $ret; + + if ( -w $default ) { + open( IN, "< $default") || return; + while ( ) { + chomp; + (m{^/dev}) && ($ret = $_, last); + } + close ( IN ); + } + $ret; +} + +sub new_installdevice ($) { + my ($dev) = @_; + my $cfg; + + die unless (open( IN, "< $default")); + + while ( ) { + if (m{^/dev}) { + $cfg .= "${dev}\n"; + } else { + $cfg .= $_; + } + } + close ( IN ); + + my ($out, $newf) = mkstemp('/tmp/grub.installdevice.XXXXX'); + die unless (print ( $out $cfg)); + close ( $out ); + + $default_backup = mktemp("${default}.old.XXXXX"); + copy($default, $default_backup); + move($newf, $default); +} + +sub is_grub_drive ($$$) { + my ( $prefix, $path, $isdev ) = @_; + my $tgt; + my ($td, $tp); + my ($pd, $pp); + my $pattern = qr{\((hd[0-9]+)?,?((?:gpt|msdos)[0-9]+)?\)}; + + if ($isdev) { + chomp( $tgt = qx{grub2-probe --target=drive -d $path} ); + } else { + chomp( $tgt = qx{grub2-probe --target=drive $path} ); + } + + die "Failed to probe $path for target drive\n" if ($? != 0); + ( $tgt =~ $pattern ) && (($td, $tp) = ($1, $2)) || return ; + ( $prefix =~ $pattern ) && (($pd, $pp) = ($1, $2)) || return ; + return if ($pd && $pd ne $td); + return 1 unless ($tp); + ($pp eq $tp) ? 1 : 0; +} + +sub embed_part_start ($){ + my ($dev) = @_; + my @blk; + my $ret; + + chomp (@blk = qx{lsblk --list --ascii --noheadings --output PATH,PTTYPE,PARTTYPE $dev}); + die "Failed to get block device information for $dev\n" if ($? != 0); + foreach (@blk) { + my ($path, $pttype, $parttype) = split /\s+/; + if ($pttype eq 'dos') { + $ret = 1; + last; + } elsif ($pttype eq 'gpt' && $parttype eq '21686148-6449-6e6f-744e-656564454649') { + if ($path =~ qr{/dev/(.+)}) { + if ( -r "/sys/class/block/$1/start" ) { + chomp ($ret = qx{cat /sys/class/block/$1/start}); + last; + } + } + } + } + + $ret; +} + +sub check_mbr ($) { + my ($dev) = @_; + my $devh; + my $mbr; + + open( $devh, "< $dev" ) or die "$0: cannot open $dev: $!\n"; + sysread( $devh, $mbr, 512 ) == 512 or die "$0: $dev: read error\n"; + close( $devh ); + my( $magic ) = unpack('H4', $mbr); + return if ($magic ne 'eb63'); + + my( $version ) = unpack('x128H4', $mbr); + return if ($version ne '0020'); + + my( $sector_nr ) = unpack('x92L<', $mbr); + return if ($sector_nr ne embed_part_start($dev)); + + my( $drive_nr ) = unpack('x100H2', $mbr); + return if ($drive_nr ne 'ff'); + + $sector_nr; +} + +sub check_diskboot ($$) { + my ($dev, $sector_nr) = @_; + my $devh; + my $diskboot; + my @ret; + + open($devh, "< $dev" ) or die "$0: cannot open $dev: $!\n"; + # print "looks at sector $sector_nr of the same hard drive for core.img\n"; + sysseek($devh, $sector_nr*512, 0) or die "$0: $dev: $!\n"; + # grub-core/boot/i386/pc/diskboot.S + sysread($devh, $diskboot, 512 ) == 512 or die "$0: $dev: read error\n"; + close($devh); + + my( $magic ) = unpack('H8', $diskboot); + # print $magic , "\n"; + + # 5256be1b - upstream diskboot.S + # 5256be63 - trustedgrub2 1.4 + # 5256be56 - diskboot.S with mjg TPM patches (e.g. in openSUSE Tumbleweed) + return if ($magic !~ m/(5256be1b|5256be63|5256be56)/); + + for (1..3) { + my $nr; + my $s = 512 - 12 * $_; + my( $nr_low, $nr_high, $size ) = unpack("x${s}L 8192) ? 8192 : $size; + # Find the last 6 bytes of lzma_decode to find the offset of the lzma_stream: + $off = index( unpack( "H".($r<<1), $core ), 'd1e9dffeffff' ); + if ($off != -1) { + $off >>= 1; + $off += 8; + $off = (($off + 0b1111) >> 4) << 4; + } +} + +sub decomp_lzma ($$) { + my ($core, $off) = @_; + my $comp_size; + my $decomp_size; + my $lzma; + my $lzmah; + my $unlzma; + + # grub-core/boot/i386/pc/startup_raw.S + my $tmpf = "/tmp/lzma_grub.lzma"; + ($comp_size, $decomp_size) = unpack ("x8VV", $core); + $lzma = pack( "CVVx4", 0x5d, 0x00010000, $decomp_size ); + $lzma .= substr( $core, $off, $comp_size ); + + open($lzmah, "> $tmpf") or die "$0: cannot open $tmpf : $!\n"; + binmode $lzmah; + print $lzmah $lzma; + close($lzmah); + + $unlzma = qx{lzcat $tmpf}; + die if ($? != 0); + die "decompressed size mismatch\n" if (length($unlzma) != $decomp_size); + + ($unlzma, $decomp_size); +} + +sub search_prefix (@) { + my ($unlzma, $decomp_size) = @_; + + my ($mod_base) = unpack("x19V", $unlzma); + my ($mod_magic, $mod_off, $mod_sz) = unpack("x$mod_base A4 L< L<", $unlzma); + die "module magic mismatch\n" if ( $mod_magic ne "mimg" ); + die "module out of bound" if ($mod_base + $mod_sz > $decomp_size); + my $mod_start = $mod_base + $mod_off; + my $mod_end = $mod_base + $mod_sz; + my $embed; + my $prefix; + while ($mod_start < ($mod_end - 8)) { + my ($type, $sz) = unpack("x${mod_start} L< L<", $unlzma); + last if ($mod_start + $sz > $mod_end); + last if ($sz < 8); + if ($type == 2) { + ($embed) = unpack(join('', 'x', $mod_start + 8, 'A', $sz - 8), $unlzma); + } elsif ($type == 3) { + ($prefix) = unpack(join('', 'x', $mod_start + 8, 'A', $sz - 8), $unlzma); + } + $sz = (($sz + 0b11) >> 2) << 2; + $mod_start += $sz; + } + + $prefix; +} + +sub part_to_disk ($) { + my ($dev) = @_; + my $ret; + + if ($dev =~ m{/dev/disk/by-uuid/}) { + $dev = realpath($dev); + } + + my @regexp = ( + qr{(/dev/disk/(?:by-id|by-path)/.+)-part[0-9]+}, + qr{(/dev/[a-z]+d[a-z])[0-9]+}, + qr{(/dev/nvme[0-9]+n[0-9]+)p[0-9]+} + ); + + foreach (@regexp) { + if ($dev =~ $_) { + $ret = $1; + last; + } + } + + $ret; +} + +sub get_prefix ($@) { + my ($dev, ($sector_nr, $size)) = @_; + my $devh; + my $core; + my $off; + my $prefix; + + $size <<= 9; + $sector_nr <<= 9; + + open( $devh, "< $dev" ) or die "$0: cannot open $dev: $!\n"; + sysseek( $devh, $sector_nr, 0) or die "$0: $dev: $!\n"; + sysread( $devh, $core, $size ) == $size or die "$0: $dev: read error\n"; + close( $devh ); + + $off = lzma_start($core, $size); + return if ($off == -1); + + $prefix = search_prefix( decomp_lzma($core, $off) ); +} + +eval { + +my @uname = uname(); +die "machine hardware is not x86_64\n" if ($uname[4] ne 'x86_64'); + +die "no install device config or no permission to alter it\n" unless ($instdev = default_installdevice()); +die "/boot is abstraction\n" if (is_abstraction("/boot")); +die "$instdev is NOT partition\n" unless (is_part($instdev)); + +chomp ( $device = qx{grub2-probe --target=disk /boot} ); +die "no disk for /boot\n" unless ( $device ); + +my $sector_nr = check_mbr($device); + +die "$device mbr is not used for suse grub embedding\n" unless ($sector_nr); + +my @core_sectors = check_diskboot($device, $sector_nr); + +die "core image is not single continuous chunk\n" if (@core_sectors != 2); + +die "starting sector of startup_raw $core_sectors[0]" . +" did not follow diskboot $sector_nr\n" if ($core_sectors[0] != $sector_nr + 1); + +my $prefix = get_prefix($device, @core_sectors); + +die "$prefix is not pointing to /boot" unless ($prefix && is_grub_drive ($prefix, '/boot', 0)); + +my $instdisk = part_to_disk($instdev); + +die "cannot determine disk device for $instdev" unless ($instdisk); +die "$instdisk is not grub disk" unless (is_grub_drive($prefix, $instdisk, 1)); + +new_installdevice($instdisk); + +print "The system has been detected using grub in master boot record for booting this updated system with \$prefix=$prefix. However the $default has the install device set to the partition, $instdev. To avoid potential breakage in the application binary interface between grub image and modules, the install device of grub has been changed to use the disk device, $instdisk, to update the master boot record with new grub in order to keep up with the new binary.\n"; + +print "The backup of the original file is $default_backup\n"; + +}; + +print "No fixup required: $@" if ($debug && $@); diff --git a/grub2-iterate-and-hook-for-extended-partition.patch b/grub2-iterate-and-hook-for-extended-partition.patch new file mode 100644 index 0000000..e7dabc4 --- /dev/null +++ b/grub2-iterate-and-hook-for-extended-partition.patch @@ -0,0 +1,48 @@ +From: Michael Chang + +The same as in the previous patch, add a support for installing grub +into an extended partition. + +Here, we do not ignore extended partitions anymore. Instead we call a +hook that makes sure we have the partition when installing. + +Signed-off-by: Jiri Slaby +References: https://bugzilla.novell.com/show_bug.cgi?id=750897 + +From: Andrey Borzenkov + +Apply this logic only to primary extended partition. Ignore extended +partitions that are used to link together logical partitions. + +References: https://bugzilla.novell.com/show_bug.cgi?id=785341 +--- +Index: grub-2.00/grub-core/partmap/msdos.c +=================================================================== +--- grub-2.00.orig/grub-core/partmap/msdos.c ++++ grub-2.00/grub-core/partmap/msdos.c +@@ -188,13 +188,20 @@ grub_partition_msdos_iterate (grub_disk_ + (unsigned long long) p.len); + + /* If this partition is a normal one, call the hook. */ +- if (! grub_msdos_partition_is_empty (e->type) +- && ! grub_msdos_partition_is_extended (e->type)) ++ if (! grub_msdos_partition_is_empty (e->type)) + { +- p.number++; ++ if (!grub_msdos_partition_is_extended (e->type) || p.number < 3) ++ { ++ p.number++; + +- if (hook (disk, &p, hook_data)) +- return grub_errno; ++ /* prevent someone doing mkfs or mkswap on an ++ extended partition, but leave room for LILO */ ++ if (grub_msdos_partition_is_extended (e->type)) ++ p.len = 2; ++ ++ if (hook (disk, &p, hook_data)) ++ return grub_errno; ++ } + } + else if (p.number < 3) + /* If this partition is a logical one, shouldn't increase the diff --git a/grub2-linguas.sh-no-rsync.patch b/grub2-linguas.sh-no-rsync.patch new file mode 100644 index 0000000..8cc702e --- /dev/null +++ b/grub2-linguas.sh-no-rsync.patch @@ -0,0 +1,21 @@ +From: Andrey Borzenkov +Subject: disable rsync to make it possible to use in RPM build + +We need to create po/LINGUAS to generate message catalogs. Use +linguas.sh to ensure we always use the same rules as upstream, but +disable rsync. +Index: grub-2.02~rc2/linguas.sh +=================================================================== +--- grub-2.02~rc2.orig/linguas.sh ++++ grub-2.02~rc2/linguas.sh +@@ -1,8 +1,8 @@ + #!/bin/sh + +-rsync -Lrtvz translationproject.org::tp/latest/grub/ po ++#rsync -Lrtvz translationproject.org::tp/latest/grub/ po + +-autogenerated="en@quot en@hebrew de@hebrew en@cyrillic en@greek en@arabic en@piglatin de_CH" ++autogenerated="en@quot" # en@hebrew de@hebrew en@cyrillic en@greek en@arabic en@piglatin de_CH" + + + for x in $autogenerated; do diff --git a/grub2-linux.patch b/grub2-linux.patch new file mode 100644 index 0000000..c5b9f2e --- /dev/null +++ b/grub2-linux.patch @@ -0,0 +1,40 @@ +--- a/util/grub.d/10_linux.in ++++ b/util/grub.d/10_linux.in +@@ -31,7 +31,7 @@ + if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then + OS=GNU/Linux + else +- OS="${GRUB_DISTRIBUTOR} GNU/Linux" ++ OS="${GRUB_DISTRIBUTOR}" + CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}" + fi + +@@ -143,7 +143,7 @@ + message="$(gettext_printf "Loading Linux %s ..." ${version})" + sed "s/^/$submenu_indentation/" << EOF + echo '$(echo "$message" | grub_quote)' +- linux ${rel_dirname}/${basename} root=${linux_root_device_thisversion} ro ${args} ++ linux ${rel_dirname}/${basename} root=${linux_root_device_thisversion} ${args} + EOF + if test -n "${initrd}" ; then + # TRANSLATORS: ramdisk isn't identifier. Should be translated. +--- a/util/grub.d/20_linux_xen.in ++++ b/util/grub.d/20_linux_xen.in +@@ -31,7 +31,7 @@ + if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then + OS=GNU/Linux + else +- OS="${GRUB_DISTRIBUTOR} GNU/Linux" ++ OS="${GRUB_DISTRIBUTOR}" + CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}" + fi + +@@ -154,7 +154,7 @@ + fi + ${xen_loader} ${rel_xen_dirname}/${xen_basename} placeholder ${xen_args} \${xen_rm_opts} + echo '$(echo "$lmessage" | grub_quote)' +- ${module_loader} ${rel_dirname}/${basename} placeholder root=${linux_root_device_thisversion} ro ${args} ++ ${module_loader} ${rel_dirname}/${basename} placeholder root=${linux_root_device_thisversion} ${args} + EOF + if test -n "${initrd}" ; then + # TRANSLATORS: ramdisk isn't identifier. Should be translated. diff --git a/grub2-linuxefi-fix-boot-params.patch b/grub2-linuxefi-fix-boot-params.patch new file mode 100644 index 0000000..8c9e616 --- /dev/null +++ b/grub2-linuxefi-fix-boot-params.patch @@ -0,0 +1,18 @@ +--- a/grub-core/loader/i386/efi/linux.c ++++ b/grub-core/loader/i386/efi/linux.c +@@ -298,7 +298,14 @@ + lh.code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem; + } + +- grub_memcpy(params, &lh, 2 * 512); ++ /* Grub linuxefi erroneously initialize linux's boot_params with non-zero values. (bsc#1025563) ++ ++ From https://www.kernel.org/doc/Documentation/x86/boot.txt: ++ The memory for struct boot_params could be allocated anywhere (even above 4G) ++ and initialized to all zero. ++ Then, the setup header at offset 0x01f1 of kernel image on should be ++ loaded into struct boot_params and examined. */ ++ grub_memcpy (¶ms->setup_sects, &lh.setup_sects, sizeof (lh) - 0x01f1); + + params->type_of_loader = 0x21; + diff --git a/grub2-lvm-allocate-metadata-buffer-from-raw-contents.patch b/grub2-lvm-allocate-metadata-buffer-from-raw-contents.patch new file mode 100644 index 0000000..be34d12 --- /dev/null +++ b/grub2-lvm-allocate-metadata-buffer-from-raw-contents.patch @@ -0,0 +1,148 @@ +From: Michael Chang +Date: Fri, 9 Apr 2021 19:58:24 +0800 +Subject: [PATCH] Allocate LVM metadata buffer from raw contents + +The size reserved for on disk LVM metadata area can be exceedingly large that +may trigger out of memory error for allocating buffer based on it. Refine the +buffer allocation to use size of raw LVM metadata contents and read them from +within the metadata area as we only need to parse the JSON formatted contents +rather than the entire metadata area. This reduced the size significantly and +the likelihood to out of memory error. +--- + grub-core/disk/lvm.c | 79 ++++++++++++++++++++++++-------------------- + 1 file changed, 43 insertions(+), 36 deletions(-) + +diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c +index 8257159b3..1d1a3dcad 100644 +--- a/grub-core/disk/lvm.c ++++ b/grub-core/disk/lvm.c +@@ -140,9 +140,11 @@ grub_lvm_detect (grub_disk_t disk, + grub_err_t err; + grub_uint64_t mda_offset, mda_size; + grub_size_t ptr; ++ grub_uint64_t mda_raw_offset, mda_raw_size; + char buf[GRUB_LVM_LABEL_SIZE]; + char vg_id[GRUB_LVM_ID_STRLEN+1]; + char pv_id[GRUB_LVM_ID_STRLEN+1]; ++ char mdah_buf[sizeof (struct grub_lvm_mda_header) + sizeof (struct grub_lvm_raw_locn)]; + char *metadatabuf, *mda_end, *vgname; + const char *p, *q; + struct grub_lvm_label_header *lh = (struct grub_lvm_label_header *) buf; +@@ -220,21 +222,15 @@ grub_lvm_detect (grub_disk_t disk, + + dlocn++; + mda_offset = grub_le_to_cpu64 (dlocn->offset); +- mda_size = grub_le_to_cpu64 (dlocn->size); + + /* It's possible to have multiple copies of metadata areas, we just use the + first one. */ +- +- /* Allocate buffer space for the circular worst-case scenario. */ +- metadatabuf = grub_calloc (2, mda_size); +- if (! metadatabuf) ++ err = grub_disk_read (disk, 0, mda_offset, sizeof (mdah_buf), mdah_buf); ++ if (err) + goto fail; + +- err = grub_disk_read (disk, 0, mda_offset, mda_size, metadatabuf); +- if (err) +- goto fail2; ++ mdah = (struct grub_lvm_mda_header *) mdah_buf; + +- mdah = (struct grub_lvm_mda_header *) metadatabuf; + if ((grub_strncmp ((char *)mdah->magic, GRUB_LVM_FMTT_MAGIC, + sizeof (mdah->magic))) + || (grub_le_to_cpu32 (mdah->version) != GRUB_LVM_FMTT_VERSION)) +@@ -244,42 +240,58 @@ grub_lvm_detect (grub_disk_t disk, + #ifdef GRUB_UTIL + grub_util_info ("unknown LVM metadata header"); + #endif +- goto fail2; ++ goto fail; + } + + rlocn = mdah->raw_locns; +- if (grub_le_to_cpu64 (rlocn->offset) >= grub_le_to_cpu64 (mda_size)) ++ ++ mda_size = grub_le_to_cpu64 (mdah->size); ++ mda_raw_size = grub_le_to_cpu64 (rlocn->size); ++ mda_raw_offset = grub_le_to_cpu64 (rlocn->offset); ++ ++ if (mda_raw_offset >= mda_size) + { + #ifdef GRUB_UTIL + grub_util_info ("metadata offset is beyond end of metadata area"); + #endif +- goto fail2; ++ goto fail; + } + +- if (grub_le_to_cpu64 (rlocn->offset) + grub_le_to_cpu64 (rlocn->size) > +- grub_le_to_cpu64 (mdah->size)) ++ metadatabuf = grub_malloc (mda_raw_size); ++ ++ if (! metadatabuf) ++ goto fail; ++ ++ if (mda_raw_offset + mda_raw_size > mda_size) + { +- if (2 * mda_size < GRUB_LVM_MDA_HEADER_SIZE || +- (grub_le_to_cpu64 (rlocn->offset) + grub_le_to_cpu64 (rlocn->size) - +- grub_le_to_cpu64 (mdah->size) > mda_size - GRUB_LVM_MDA_HEADER_SIZE)) +- { +-#ifdef GRUB_UTIL +- grub_util_info ("cannot copy metadata wrap in circular buffer"); +-#endif +- goto fail2; +- } ++ err = grub_disk_read (disk, 0, ++ mda_offset + mda_raw_offset, ++ mda_size - mda_raw_offset, ++ metadatabuf); ++ if (err) ++ goto fail2; + + /* Metadata is circular. Copy the wrap in place. */ +- grub_memcpy (metadatabuf + mda_size, +- metadatabuf + GRUB_LVM_MDA_HEADER_SIZE, +- grub_le_to_cpu64 (rlocn->offset) + +- grub_le_to_cpu64 (rlocn->size) - +- grub_le_to_cpu64 (mdah->size)); ++ err = grub_disk_read (disk, 0, ++ mda_offset + GRUB_LVM_MDA_HEADER_SIZE, ++ mda_raw_offset + mda_raw_size - mda_size, ++ metadatabuf + mda_size - mda_raw_offset); ++ if (err) ++ goto fail2; ++ } ++ else ++ { ++ err = grub_disk_read (disk, 0, ++ mda_offset + mda_raw_offset, ++ mda_raw_size, ++ metadatabuf); ++ if (err) ++ goto fail2; + } + +- if (grub_add ((grub_size_t)metadatabuf, +- (grub_size_t)grub_le_to_cpu64 (rlocn->offset), +- &ptr)) ++ p = q = metadatabuf; ++ ++ if (grub_add ((grub_size_t)metadatabuf, (grub_size_t)mda_raw_size, &ptr)) + { + error_parsing_metadata: + #ifdef GRUB_UTIL +@@ -288,11 +300,6 @@ grub_lvm_detect (grub_disk_t disk, + goto fail2; + } + +- p = q = (char *)ptr; +- +- if (grub_add ((grub_size_t)metadatabuf, (grub_size_t)mda_size, &ptr)) +- goto error_parsing_metadata; +- + mda_end = (char *)ptr; + + while (*q != ' ' && q < mda_end) diff --git a/grub2-menu-unrestricted.patch b/grub2-menu-unrestricted.patch new file mode 100644 index 0000000..b0810e7 --- /dev/null +++ b/grub2-menu-unrestricted.patch @@ -0,0 +1,21 @@ +--- a/grub-core/normal/menu.c ++++ b/grub-core/normal/menu.c +@@ -212,7 +212,17 @@ + grub_size_t sz = 0; + + if (entry->restricted) +- err = grub_auth_check_authentication (entry->users); ++ { ++ int auth_check = 1; ++ if (entry->users && entry->users[0] == 0) ++ { ++ const char *unr = grub_env_get ("unrestricted_menu"); ++ if (unr && (unr[0] == '1' || unr[0] == 'y')) ++ auth_check = 0; ++ } ++ if (auth_check) ++ err = grub_auth_check_authentication (entry->users); ++ } + + if (err) + { diff --git a/grub2-mkconfig-aarch64.patch b/grub2-mkconfig-aarch64.patch new file mode 100644 index 0000000..949b21f --- /dev/null +++ b/grub2-mkconfig-aarch64.patch @@ -0,0 +1,12 @@ +grub-mkonfig: Look for Image-* on aarch64 + +--- a/util/grub.d/10_linux.in ++++ b/util/grub.d/10_linux.in +@@ -194,6 +194,7 @@ + machine=`uname -m` + case "x$machine" in + xi?86 | xx86_64) klist="/boot/vmlinuz-* /vmlinuz-* /boot/kernel-*" ;; ++ xaarch64) klist="/boot/Image-* /Image-* /boot/kernel-*" ;; + xs390 | xs390x) klist="/boot/image-* /boot/kernel-*" ;; + *) klist="/boot/vmlinuz-* /boot/vmlinux-* /vmlinuz-* /vmlinux-* \ + /boot/kernel-*" ;; diff --git a/grub2-mkconfig-arm.patch b/grub2-mkconfig-arm.patch new file mode 100644 index 0000000..3193b72 --- /dev/null +++ b/grub2-mkconfig-arm.patch @@ -0,0 +1,10 @@ +--- a/util/grub.d/10_linux.in ++++ b/util/grub.d/10_linux.in +@@ -195,6 +195,7 @@ + case "x$machine" in + xi?86 | xx86_64) klist="/boot/vmlinuz-* /vmlinuz-* /boot/kernel-*" ;; + xaarch64) klist="/boot/Image-* /Image-* /boot/kernel-*" ;; ++ xarm*) klist="/boot/zImage-* /zImage-* /boot/kernel-*" ;; + xs390 | xs390x) klist="/boot/image-* /boot/kernel-*" ;; + *) klist="/boot/vmlinuz-* /boot/vmlinux-* /vmlinuz-* /vmlinux-* \ + /boot/kernel-*" ;; diff --git a/grub2-mkconfig-default-entry-correction.patch b/grub2-mkconfig-default-entry-correction.patch new file mode 100644 index 0000000..da322c9 --- /dev/null +++ b/grub2-mkconfig-default-entry-correction.patch @@ -0,0 +1,14 @@ +--- a/util/grub-mkconfig.in ++++ b/util/grub-mkconfig.in +@@ -363,6 +363,11 @@ + cat ${grub_cfg}.new > ${grub_cfg} + umask $oldumask + rm -f ${grub_cfg}.new ++ # check if default entry need to be corrected for updated distributor version ++ # and/or use fallback entry if default kernel entry removed ++ if test -x /usr/sbin/grub2-check-default; then ++ /usr/sbin/grub2-check-default >&2 ++ fi + sync_fs_journal || true + fi + fi diff --git a/grub2-mkconfig-riscv64.patch b/grub2-mkconfig-riscv64.patch new file mode 100644 index 0000000..c9bd334 --- /dev/null +++ b/grub2-mkconfig-riscv64.patch @@ -0,0 +1,12 @@ +Index: grub-2.04/util/grub.d/10_linux.in +=================================================================== +--- grub-2.04.orig/util/grub.d/10_linux.in ++++ grub-2.04/util/grub.d/10_linux.in +@@ -216,6 +216,7 @@ case "x$machine" in + xi?86 | xx86_64) klist="/boot/vmlinuz-* /vmlinuz-* /boot/kernel-*" ;; + xaarch64) klist="/boot/Image-* /Image-* /boot/kernel-*" ;; + xarm*) klist="/boot/zImage-* /zImage-* /boot/kernel-*" ;; ++ xriscv64) klist="/boot/Image-* /Image-* /boot/kernel-*" ;; + xs390 | xs390x) klist="/boot/image-* /boot/kernel-*" ;; + *) klist="/boot/vmlinuz-* /boot/vmlinux-* /vmlinuz-* /vmlinux-* \ + /boot/kernel-*" ;; diff --git a/grub2-once b/grub2-once new file mode 100644 index 0000000..5cd60cc --- /dev/null +++ b/grub2-once @@ -0,0 +1,309 @@ +#!/usr/bin/perl +# +# (C) 2014 mchang@suse.com +# +# 2014-02-20 jw@suse.de + +use strict; + +my $grub2_dir; +my $grub2_reboot; +my $grub2_editenv; +my $show_mapped; +my $id_name; +my @menuentry; +my @enumentry; +my %E; + +sub dPrint($) { + #print( STDERR @_[0]); +} + +sub sh_test($) { + my ( $exp ) = @_; + + dPrint( "?? '$exp' "); + + # Don't test grub command return status from linux shell, this often results + # in command not found error. In such case the expression often has no + # opening bracket and just returning false here to signify -ENOCMD error. + return 0 if ( $exp =~ m{^\s*[^\[]}); + + $exp .= " ]" if ( $exp =~ m{^\[.*[^\]]\s*$} ); # gnaaa + #my $t = qx{set -x; $exp}; + my $t = qx{$exp}; + my $ret = $? >> 8; + $ret = ($ret == 0) ? 1 : 0; + dPrint("=> $ret ($t)\n"); + return $ret; +} + +sub read_cfg($$) { + my ($dir, $cfg) = @_; + + my $fh; + my $m = ""; + my $state = 1; # 1 == normal, 010 == if-false, 011 == if-true, 110 == else-false, 111 == else-true + my @State = (); + + if ($dir) { + %E = ( "config_directory" => $dir ); + dPrint("# VE: 'cd'='$dir'\n"); + $dir .= "/"; + if ($> == 0) { + open($fh, "$grub2_editenv - list |") || die "cannot read grub2 environment: $!\n"; + while (<$fh>) { + chomp; + if ( m{^([^\s=]+?)=(.*)$} ) { + my ($k, $v) = ($1, $2); + $v =~ s{^"([^"]*)"$}{$1}; + dPrint("# VE: '$k'='$v'\n"); + $E{$k} = $v; + } + } + close($fh); + } + } + + dPrint("# open($dir$cfg)\n"); + open($fh, "<$dir$cfg") || die "cannot read $cfg in $dir: $!\n"; + + LINE: while ( <$fh> ) { + s{^#.*$}{}; # get rid of trailing comments, + s{\s+$}{}; # trailing whitespace + s{\s*;$}{}; # including semicolons + next if (m{^\s*$}); # and empty lines. + s{^\s*}{ }; # force leading whitespace to one + + dPrint(sprintf("#%d: '%s' [%s]%04b\n", $., $_, join(",",@State), $state)); + if ( m{^ fi$} ) { + $state = pop( @State); + $m .= "$_\n"; + dPrint(sprintf(">FI: [%s]0b%04b\n", join(",",@State), $state)); + next; + } + if ($state & 0b10) { # {if,else}-* + if ( m{^ elif\s+(.*?)\s*; then$} && !($state & 0b1000)) { + if ($state & 0b1) { + $state = 0b110; # else-false + } else { + $state = 0b010 + sh_test( $1); # if-? + dPrint(sprintf("=EI: 0b%03b\n", $state)); + $m .= "$_\n"; + next; + } + } elsif ( m{^ else$} && !($state & 0b1000)) { + if (($state & 0b111) == 0b010) { # in 'if' but neither 'else' nor 'true' + $state = 0b111; # else-true + } else { + $state = 0b110; # else-false + } + $m .= "$_\n"; + dPrint(sprintf("=EL: 0b%03b\n", $state)); + next; + } + } + if ($state & 0b1) { # *-true or normal + dPrint("-I1: $_\n"); + } else { # *-false + dPrint("-I0: $_\n"); + if ( m{^ if (.*?)\s*; then$} ) { + push( @State, $state); + $state = 0b1000; + $m .= "$_\n"; + } + next; + } + + while ( m'(?:[^\\])(\$(?:{([^}]+?)}|([A-Za-z0-9_]+)))' ) { + my ($s, $k1, $k2) = ($1, $2, $3); + my $k = (defined($k1)) ? $k1 : $k2; + dPrint("# VT: '$k'\n"); + if (exists( $E{$k})) { + $s =~ s{([\$\{\}\"])}{\\$1}g; + dPrint("# VB: '$_'\n"); + s{$s}{$E{$k}} || die; + dPrint("# VR: '$_'\n"); + } else { + $s =~ s{([\$\{\}\"])}{\\$1}g; + s{$s}{} || die; + dPrint("# VR: '$_'\n"); + } + } + + if ( m{^ if (.*?)\s*; then$} ) { + push( @State, $state); + $state = 0b010 + sh_test( $1); + dPrint(sprintf("$title" : "$title"; + my $eId = (($pId ne "") ? "$pId>" : "") . $c++; + + if ($type eq "menuentry") { + push @menuentry, $name; + push @enumentry, [$name, $eId]; + } elsif ($type eq "submenu") { + parse_menuentry ($name, $eId, $data); + } + } +} + +# Enable restore grubenv service (bnc#892358) +# Restore grubenv settings for booting default entry to workaround the grub2-once cannot +# work and function properly on lvm, md and s390. +sub enable_restore_grubenv_service { + + my $systemctl = "/usr/bin/systemctl"; + my $cleanup = "/var/lib/misc/grub2-cleanup-once"; + + unless (-e $cleanup) { + open(my $fh, ">", $cleanup) or die "open: $cleanup $!\n"; + close($fh); + } + + return 0 if (system("$systemctl --quiet is-enabled grub2-once") == 0); + system "$systemctl --no-reload enable grub2-once >/dev/null 2>&1"; +} + +$id_name = ""; +if (@ARGV == 2 && ($ARGV[0] eq "--show-mapped")) { + $show_mapped = 1; + $id_name = $ARGV[1]; +} elsif (@ARGV == 1) { + $show_mapped = 0; + $id_name = $ARGV[0]; +} + +die "wrong command line options, try --help\n" if ($id_name eq ""); + +open(SYSCONF, ") { + chomp; + next if ( /^\s*#/ ); + if ( /LOADER_TYPE=(\'|\"|)([^\'\"\s]+)\1(\s*|\s+#.*)$/ ) { + dPrint("OK : $2\n"); + if ($2 eq "grub2" || $2 eq "grub2-efi") { + # Found grub2 to be the incumbent loader ... + $grub2_dir = "/boot/grub2"; + $grub2_reboot = "/usr/sbin/grub2-reboot"; + $grub2_editenv = "/usr/bin/grub2-editenv"; + # Note : Here we continues rather than exiting the loop, which + # results in different behavior than previous "the first wins". Now + # the latest defined LOADER_TYPE can be used to override any + # previous one, which is identical to the result of regular shell + # variable expansion to meet most people's expectation. + } + } else { + next if ( /^\s*$/ ); + dPrint("SKIP: <$_>\n"); + } +} + +close (SYSCONF); + +if ($id_name eq "--help" or $id_name eq "-h") + { + print "Usage: grub2-once [--show-mapped ID | --list | ID | NAME_SUBSTRING ]\n"; + system "$grub2_reboot \"--help\""; + exit 0; + } + +die "no grub2_dir" if ($grub2_dir eq ""); + +my $m = read_cfg( $grub2_dir, "grub.cfg"); +# Note: only *one* top-level call to parse_menuentry() is possible +# or else it will start again with 0 (and no parent)! +parse_menuentry ("", "", $m); + +my $ret = ""; +my $name = ""; +my $id = -1; + +if ($id_name eq '--enum') { + foreach my $e (@enumentry) { + printf "%-7s %s\n", $e->[1], $e->[0]; + } + exit 0; +} + +if ($id_name eq '--list') + { + my $c = 0; + foreach my $e (@menuentry) + { + printf "%6d %s\n", $c, $e; + $c++; + } + exit 0; + } + +if ($id_name =~ m!^[0-9]+$!) { + + if ($id_name < @menuentry) { + $id = $id_name; + $name = $menuentry[$id]; + $ret = $name; + } + +} else { + + my $i = -1; + my $c = 0; + + $name = $id_name; + + foreach my $e (@menuentry) { + if ($e =~ qr!\Q$name\E!) { + $i = $c; + last; + } + } continue { + ++$c; + } + + if ($i >= 0) { + $id = $i; + $name = $menuentry[$id]; + $ret = "$id"; + } +} + +if ($show_mapped > 0) { + print $ret; +} else { + system "$grub2_reboot \"$name\""; + enable_restore_grubenv_service; +} + diff --git a/grub2-once.service b/grub2-once.service new file mode 100644 index 0000000..1832b17 --- /dev/null +++ b/grub2-once.service @@ -0,0 +1,17 @@ +[Unit] +Description=Restore grubenv +DefaultDependencies=no +After=local-fs.target +Before=sysinit.target shutdown.target +Conflicts=shutdown.target +ConditionPathIsReadWrite=/boot/grub2/grubenv +ConditionPathExists=/var/lib/misc/grub2-cleanup-once + +[Service] +Type=oneshot +ExecStart=-/usr/bin/grub2-editenv /boot/grub2/grubenv unset next_entry +ExecStartPost=-/usr/bin/rm -f /var/lib/misc/grub2-cleanup-once +StandardOutput=journal + +[Install] +WantedBy=sysinit.target diff --git a/grub2-pass-corret-root-for-nfsroot.patch b/grub2-pass-corret-root-for-nfsroot.patch new file mode 100644 index 0000000..d6cbc07 --- /dev/null +++ b/grub2-pass-corret-root-for-nfsroot.patch @@ -0,0 +1,135 @@ +From 340fd0c8717c2bf33163a18bfec72243b0e51862 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 30 Aug 2012 15:43:17 +0800 +Subject: [PATCH] Pass corret root= for nfsroot + +References: bnc#774548, bsc#1069094 +Patch-Mainline: no + +Fix / is mounted on nfs. The fix is to pass kernel parameters +with correct root= for nfs. However since grub2 doesn't support +nfs file system module, the /boot on nfs is not possible and +grub2-probe not work in probing nfs mounted path. The fix is merely +on the script level and not use grub2-probe for above reasons. + +v2: Filter out autofs and securityfs from /proc/self/mountinfo (bsc#1069094) + +--- + util/grub-mkconfig.in | 37 ++++++++++++++++++++++++++++++------- + 1 files changed, 30 insertions(+), 7 deletions(-) + +--- a/util/grub-mkconfig.in ++++ b/util/grub-mkconfig.in +@@ -131,26 +131,54 @@ + exit 1 + fi + +-# Device containing our userland. Typically used for root= parameter. +-GRUB_DEVICE="`${grub_probe} --target=device /`" +-GRUB_DEVICE_UUID="`${grub_probe} --device ${GRUB_DEVICE} --target=fs_uuid 2> /dev/null`" || true +-GRUB_DEVICE_PARTUUID="`${grub_probe} --device ${GRUB_DEVICE} --target=partuuid 2> /dev/null`" || true ++probe_nfsroot_device () { ++ while read line ; do ++ part1=`echo $line | sed -e 's! - .*$!!'` ++ part2=`echo $line | sed -n -e 's! - \(.*\)$!\n\1!p' | sed 1d` ++ ++ set -- $part1 ++ path=$5 ++ ++ set -- $part2 ++ fstype=$1 ++ device=$2 ++ ++ if [ "x${path}" = "x/" ] && ++ [ "x${fstype}" = "xnfs" -o "x${fstype}" = "xnfs4" ] ; then ++ echo "${fstype}:$device" ++ return ++ fi ++ done ++} + +-# Device containing our /boot partition. Usually the same as GRUB_DEVICE. +-GRUB_DEVICE_BOOT="`${grub_probe} --target=device /boot`" +-GRUB_DEVICE_BOOT_UUID="`${grub_probe} --device ${GRUB_DEVICE_BOOT} --target=fs_uuid 2> /dev/null`" || true ++NFSROOT_DEVICE="`awk '($9!="autofs")&&($9!="securityfs")' /proc/self/mountinfo | probe_nfsroot_device`" + + # Disable os-prober by default due to security reasons. + GRUB_DISABLE_OS_PROBER="true" + +-# Filesystem for the device containing our userland. Used for stuff like +-# choosing Hurd filesystem module. +-GRUB_FS="`${grub_probe} --device ${GRUB_DEVICE} --target=fs 2> /dev/null || echo unknown`" ++if [ "x${NFSROOT_DEVICE}" != "x" ]; then ++ GRUB_DEVICE="" ++ GRUB_DEVICE_UUID="" ++ GRUB_DEVICE_PARTUUID="" ++ GRUB_FS="unknown" ++else ++ # Filesystem for the device containing our userland. Used for stuff like ++ # choosing Hurd filesystem module. ++ GRUB_FS="`${grub_probe} --device ${GRUB_DEVICE} --target=fs 2> /dev/null || echo unknown`" ++ # Device containing our userland. Typically used for root= parameter. ++ GRUB_DEVICE="`${grub_probe} --target=device /`" ++ GRUB_DEVICE_UUID="`${grub_probe} --device ${GRUB_DEVICE} --target=fs_uuid 2> /dev/null`" || true ++ GRUB_DEVICE_PARTUUID="`${grub_probe} --device ${GRUB_DEVICE} --target=partuuid 2> /dev/null`" || true ++fi + +-if [ x"$GRUB_FS" = xunknown ]; then ++if [ x"$GRUB_FS" = x ] || [ x"$GRUB_FS" = xunknown ]; then + GRUB_FS="$(stat -f -c %T / || echo unknown)" + fi + ++# Device containing our /boot partition. Usually the same as GRUB_DEVICE. ++GRUB_DEVICE_BOOT="`${grub_probe} --target=device /boot`" ++GRUB_DEVICE_BOOT_UUID="`${grub_probe} --device ${GRUB_DEVICE_BOOT} --target=fs_uuid 2> /dev/null`" || true ++ + # Provide a default set of stock linux early initrd images. + # Define here so the list can be modified in the sourced config file. + if [ "x${GRUB_EARLY_INITRD_LINUX_STOCK}" = "x" ]; then +--- a/util/grub.d/10_linux.in ++++ b/util/grub.d/10_linux.in +@@ -88,6 +88,12 @@ + type="$3" + args="$4" + ++ if [ -n "${linux_root_device_thisversion}" ]; then ++ root_device="root=${linux_root_device_thisversion}" ++ else ++ root_device="" ++ fi ++ + if [ -z "$boot_device_id" ]; then + boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" + fi +@@ -143,7 +149,7 @@ + message="$(gettext_printf "Loading Linux %s ..." ${version})" + sed "s/^/$submenu_indentation/" << EOF + echo '$(echo "$message" | grub_quote)' +- linux ${rel_dirname}/${basename} root=${linux_root_device_thisversion} ${args} ++ linux ${rel_dirname}/${basename} ${root_device} ${args} + EOF + if test -n "${initrd}" ; then + # TRANSLATORS: ramdisk isn't identifier. Should be translated. +--- a/util/grub.d/20_linux_xen.in ++++ b/util/grub.d/20_linux_xen.in +@@ -109,6 +109,11 @@ + args="$5" + xen_args="$6" + xsm="$7" ++ if [ -n "${linux_root_device_thisversion}" ]; then ++ root_device="root=${linux_root_device_thisversion}" ++ else ++ root_device="" ++ fi + # If user wants to enable XSM support, make sure there's + # corresponding policy file. + xenpolicy= +@@ -160,7 +165,7 @@ + fi + ${xen_loader} ${rel_xen_dirname}/${xen_basename} placeholder ${xen_args} \${xen_rm_opts} + echo '$(echo "$lmessage" | grub_quote)' +- ${module_loader} ${rel_dirname}/${basename} placeholder root=${linux_root_device_thisversion} ${args} ++ ${module_loader} ${rel_dirname}/${basename} placeholder ${root_device} ${args} + EOF + if test -n "${initrd}" ; then + # TRANSLATORS: ramdisk isn't identifier. Should be translated. diff --git a/grub2-ppc-terminfo.patch b/grub2-ppc-terminfo.patch new file mode 100644 index 0000000..5725a3b --- /dev/null +++ b/grub2-ppc-terminfo.patch @@ -0,0 +1,147 @@ +From e263907f50e496e602edd9bd846ccb6e0565a085 Mon Sep 17 00:00:00 2001 +From: Mark Hamzy +Date: Wed, 28 Mar 2012 14:46:41 -0500 +Subject: [PATCH] Migrate PPC from Yaboot to Grub2 + +Add configuration support for serial terminal consoles. This will set the +maximum screen size so that text is not overwritten. + +--- + Makefile.util.def | 7 +++ + util/grub.d/20_ppc_terminfo.in | 114 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 121 insertions(+), 0 deletions(-) + create mode 100644 util/grub.d/20_ppc_terminfo.in + +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -512,6 +512,13 @@ + }; + + script = { ++ name = '20_ppc_terminfo'; ++ common = util/grub.d/20_ppc_terminfo.in; ++ installdir = grubconf; ++ condition = COND_HOST_LINUX; ++}; ++ ++script = { + name = '25_bli'; + common = util/grub.d/25_bli.in; + installdir = grubconf; +--- /dev/null ++++ b/util/grub.d/20_ppc_terminfo.in +@@ -0,0 +1,114 @@ ++#! /bin/sh ++set -e ++ ++# grub-mkconfig helper script. ++# Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc. ++# ++# GRUB is free software: you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# GRUB is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with GRUB. If not, see . ++ ++prefix=@prefix@ ++exec_prefix=@exec_prefix@ ++bindir=@bindir@ ++libdir=@libdir@ ++. "@datadir@/@PACKAGE@/grub-mkconfig_lib" ++ ++export TEXTDOMAIN=@PACKAGE@ ++export TEXTDOMAINDIR=@localedir@ ++ ++X=80 ++Y=24 ++TERMINAL=ofconsole ++ ++argument () { ++ opt=$1 ++ shift ++ ++ if test $# -eq 0; then ++ echo "$0: option requires an argument -- '$opt'" 1>&2 ++ exit 1 ++ fi ++ echo $1 ++} ++ ++check_terminfo () { ++ ++ while test $# -gt 0 ++ do ++ option=$1 ++ shift ++ ++ case "$option" in ++ terminfo | TERMINFO) ++ ;; ++ ++ -g) ++ NEWXY=`argument $option "$@"` ++ NEWX=`echo $NEWXY | cut -d x -f 1` ++ NEWY=`echo $NEWXY | cut -d x -f 2` ++ ++ if [ ${NEWX} -ge 80 ] ; then ++ X=${NEWX} ++ else ++ echo "Warning: ${NEWX} is less than the minimum size of 80" ++ fi ++ ++ if [ ${NEWY} -ge 24 ] ; then ++ Y=${NEWY} ++ else ++ echo "Warning: ${NEWY} is less than the minimum size of 24" ++ fi ++ ++ shift ++ ;; ++ ++ *) ++# # accept console or ofconsole ++# if [ "$option" != "console" -a "$option" != "ofconsole" ] ; then ++# echo "Error: GRUB_TERMINFO unknown console: $option" ++# exit 1 ++# fi ++# # perfer console ++# TERMINAL=console ++ # accept ofconsole ++ if [ "$option" != "ofconsole" ] ; then ++ echo "Error: GRUB_TERMINFO unknown console: $option" ++ exit 1 ++ fi ++ # perfer console ++ TERMINAL=ofconsole ++ ;; ++ esac ++ ++ done ++ ++} ++ ++if ! uname -m | grep -q ppc ; then ++ exit 0 ++fi ++ ++if [ "x${GRUB_TERMINFO}" != "x" ] ; then ++ F1=`echo ${GRUB_TERMINFO} | cut -d " " -f 1` ++ ++ if [ "${F1}" != "terminfo" ] ; then ++ echo "Error: GRUB_TERMINFO is set to \"${GRUB_TERMINFO}\" The first word should be terminfo." ++ exit 1 ++ fi ++ ++ check_terminfo ${GRUB_TERMINFO} ++fi ++ ++cat << EOF ++ terminfo -g ${X}x${Y} ${TERMINAL} ++EOF diff --git a/grub2-ppc64-cas-fix-double-free.patch b/grub2-ppc64-cas-fix-double-free.patch new file mode 100644 index 0000000..8be8120 --- /dev/null +++ b/grub2-ppc64-cas-fix-double-free.patch @@ -0,0 +1,94 @@ +--- a/grub-core/kern/ieee1275/openfw.c ++++ b/grub-core/kern/ieee1275/openfw.c +@@ -595,7 +595,7 @@ + + /* Check if it's a CAS reboot. If so, set the script to be executed. */ + int +-grub_ieee1275_cas_reboot (char *script) ++grub_ieee1275_cas_reboot (char **script) + { + grub_uint32_t ibm_ca_support_reboot; + grub_uint32_t ibm_fw_nbr_reboots; +@@ -628,16 +628,37 @@ + + if (ibm_ca_support_reboot || ibm_fw_nbr_reboots) + { +- if (! grub_ieee1275_get_property_length (options, "boot-last-label", &actual)) +- { +- if (actual > 1024) +- script = grub_realloc (script, actual + 1); +- grub_ieee1275_get_property (options, "boot-last-label", script, actual, +- &actual); +- return 0; +- } ++ grub_ssize_t len; ++ char *buf; ++ ++ if (grub_ieee1275_get_property_length (options, "boot-last-label", &len) ++ || len <= 0) ++ { ++ grub_dprintf ("ieee1275", "boot-last-label missing or invalid\n"); ++ goto out; ++ } ++ /* The returned property string length may not include terminating null byte, and in ++ a bid to avoid out of bound access we allocate one more byte to add it back */ ++ buf = grub_malloc ((grub_size_t)len + 1); ++ if (!buf) ++ { ++ grub_print_error (); ++ goto out; ++ } ++ if (grub_ieee1275_get_property (options, "boot-last-label", buf, (grub_size_t)len + 1, &actual) ++ || actual < 0) ++ { ++ grub_dprintf ("ieee1275", "error while get boot-last-label property\n"); ++ grub_free (buf); ++ goto out; ++ } ++ /* Add terminating null byte */ ++ buf[len] = '\0'; ++ *script = buf; ++ return 0; + } + ++out: + grub_ieee1275_set_boot_last_label (""); + + return -1; +@@ -651,8 +672,9 @@ + grub_dprintf("ieee1275", "set boot_last_label (size: %" PRIxGRUB_SIZE ")\n", grub_strlen(text)); + if (! grub_ieee1275_finddevice ("/options", &options) && + options != (grub_ieee1275_ihandle_t) -1) ++ /* To be on the safe side, set the property string with terminating null byte */ + grub_ieee1275_set_property (options, "boot-last-label", text, +- grub_strlen (text), &actual); ++ grub_strlen (text) + 1, &actual); + return 0; + } + +--- a/grub-core/normal/main.c ++++ b/grub-core/normal/main.c +@@ -282,10 +282,9 @@ + #ifdef GRUB_MACHINE_IEEE1275 + int boot; + boot = 0; +- char *script; ++ char *script = NULL; + char *dummy[1] = { NULL }; +- script = grub_malloc (1024); +- if (! grub_ieee1275_cas_reboot (script)) ++ if (! grub_ieee1275_cas_reboot (&script) && script) + { + if (! grub_script_execute_new_scope (script, 0, dummy)) + boot = 1; +--- a/include/grub/ieee1275/ieee1275.h ++++ b/include/grub/ieee1275/ieee1275.h +@@ -256,7 +256,7 @@ + void EXPORT_FUNC(grub_ieee1275_children_peer) (struct grub_ieee1275_devalias *alias); + void EXPORT_FUNC(grub_ieee1275_children_first) (const char *devpath, + struct grub_ieee1275_devalias *alias); +-int EXPORT_FUNC(grub_ieee1275_cas_reboot) (char *script); ++int EXPORT_FUNC(grub_ieee1275_cas_reboot) (char **script); + int EXPORT_FUNC(grub_ieee1275_set_boot_last_label) (const char *text); + + char *EXPORT_FUNC(grub_ieee1275_get_boot_dev) (void); diff --git a/grub2-ppc64-cas-new-scope.patch b/grub2-ppc64-cas-new-scope.patch new file mode 100644 index 0000000..b37b656 --- /dev/null +++ b/grub2-ppc64-cas-new-scope.patch @@ -0,0 +1,15 @@ +--- a/grub-core/normal/main.c ++++ b/grub-core/normal/main.c +@@ -283,10 +283,11 @@ + int boot; + boot = 0; + char *script; ++ char *dummy[1] = { NULL }; + script = grub_malloc (1024); + if (! grub_ieee1275_cas_reboot (script)) + { +- if (! grub_script_execute_sourcecode (script)) ++ if (! grub_script_execute_new_scope (script, 0, dummy)) + boot = 1; + } + grub_free (script); diff --git a/grub2-ppc64-cas-reboot-support.patch b/grub2-ppc64-cas-reboot-support.patch new file mode 100644 index 0000000..7b09aaa --- /dev/null +++ b/grub2-ppc64-cas-reboot-support.patch @@ -0,0 +1,166 @@ +From 9d1411ffa7290c1cbdc9ee95bb5fcc5506e63e0f Mon Sep 17 00:00:00 2001 +From: Paulo Flabiano Smorigo +Date: Thu, 20 Sep 2012 18:07:39 -0300 +Subject: [PATCH 096/152] IBM client architecture (CAS) reboot support + +This is an implementation of IBM client architecture (CAS) reboot for GRUB. + +There are cases where the POWER firmware must reboot in order to support +specific features requested by a kernel. The kernel calls +ibm,client-architecture-support and it may either return or reboot with the new +feature set. eg: + +Calling ibm,client-architecture-support.../ +Elapsed time since release of system processors: 70959 mins 50 secs +Welcome to GRUB! + +Instead of return to the GRUB menu, it will check if the flag for CAS reboot is +set. If so, grub will automatically boot the last booted kernel using the same +parameters +--- + grub-core/kern/ieee1275/openfw.c | 62 ++++++++++++++++++++++++++++++++++++++++ + grub-core/normal/main.c | 19 ++++++++++++ + grub-core/script/execute.c | 7 +++++ + include/grub/ieee1275/ieee1275.h | 2 ++ + 4 files changed, 90 insertions(+) + +--- a/grub-core/kern/ieee1275/openfw.c ++++ b/grub-core/kern/ieee1275/openfw.c +@@ -593,6 +593,69 @@ + return NULL; + } + ++/* Check if it's a CAS reboot. If so, set the script to be executed. */ ++int ++grub_ieee1275_cas_reboot (char *script) ++{ ++ grub_uint32_t ibm_ca_support_reboot; ++ grub_uint32_t ibm_fw_nbr_reboots; ++ char property_value[10]; ++ grub_ssize_t actual; ++ grub_ieee1275_ihandle_t options; ++ ++ if (grub_ieee1275_finddevice ("/options", &options) < 0) ++ return -1; ++ ++ /* Check two properties, one is enough to get cas reboot value */ ++ ibm_ca_support_reboot = 0; ++ if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, ++ "ibm,client-architecture-support-reboot", ++ &ibm_ca_support_reboot, ++ sizeof (ibm_ca_support_reboot), ++ &actual) >= 0) ++ grub_dprintf("ieee1275", "ibm,client-architecture-support-reboot: %u\n", ++ ibm_ca_support_reboot); ++ ++ ibm_fw_nbr_reboots = 0; ++ if (grub_ieee1275_get_property (options, "ibm,fw-nbr-reboots", ++ property_value, sizeof (property_value), ++ &actual) >= 0) ++ { ++ property_value[sizeof (property_value) - 1] = 0; ++ ibm_fw_nbr_reboots = (grub_uint8_t) grub_strtoul (property_value, 0, 10); ++ grub_dprintf("ieee1275", "ibm,fw-nbr-reboots: %u\n", ibm_fw_nbr_reboots); ++ } ++ ++ if (ibm_ca_support_reboot || ibm_fw_nbr_reboots) ++ { ++ if (! grub_ieee1275_get_property_length (options, "boot-last-label", &actual)) ++ { ++ if (actual > 1024) ++ script = grub_realloc (script, actual + 1); ++ grub_ieee1275_get_property (options, "boot-last-label", script, actual, ++ &actual); ++ return 0; ++ } ++ } ++ ++ grub_ieee1275_set_boot_last_label (""); ++ ++ return -1; ++} ++ ++int grub_ieee1275_set_boot_last_label (const char *text) ++{ ++ grub_ieee1275_ihandle_t options; ++ grub_ssize_t actual; ++ ++ grub_dprintf("ieee1275", "set boot_last_label (size: %" PRIxGRUB_SIZE ")\n", grub_strlen(text)); ++ if (! grub_ieee1275_finddevice ("/options", &options) && ++ options != (grub_ieee1275_ihandle_t) -1) ++ grub_ieee1275_set_property (options, "boot-last-label", text, ++ grub_strlen (text), &actual); ++ return 0; ++} ++ + char * + grub_ieee1275_get_boot_dev (void) + { +--- a/grub-core/normal/main.c ++++ b/grub-core/normal/main.c +@@ -34,6 +34,9 @@ + #include + #include + #include ++#ifdef GRUB_MACHINE_IEEE1275 ++#include ++#endif + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -276,6 +279,21 @@ + { + menu = read_config_file (config); + ++#ifdef GRUB_MACHINE_IEEE1275 ++ int boot; ++ boot = 0; ++ char *script; ++ script = grub_malloc (1024); ++ if (! grub_ieee1275_cas_reboot (script)) ++ { ++ if (! grub_script_execute_sourcecode (script)) ++ boot = 1; ++ } ++ grub_free (script); ++ if (boot) ++ grub_command_execute ("boot", 0, 0); ++#endif ++ + /* Ignore any error. */ + grub_errno = GRUB_ERR_NONE; + } +--- a/grub-core/script/execute.c ++++ b/grub-core/script/execute.c +@@ -28,6 +28,9 @@ + #include + #include + #include ++#ifdef GRUB_MACHINE_IEEE1275 ++#include ++#endif + + /* Max digits for a char is 3 (0xFF is 255), similarly for an int it + is sizeof (int) * 3, and one extra for a possible -ve sign. */ +@@ -883,6 +886,10 @@ + grub_err_t ret = 0; + struct grub_script *parsed_script; + ++#ifdef GRUB_MACHINE_IEEE1275 ++ grub_ieee1275_set_boot_last_label (source); ++#endif ++ + while (source) + { + char *line; +--- a/include/grub/ieee1275/ieee1275.h ++++ b/include/grub/ieee1275/ieee1275.h +@@ -256,6 +256,8 @@ + void EXPORT_FUNC(grub_ieee1275_children_peer) (struct grub_ieee1275_devalias *alias); + void EXPORT_FUNC(grub_ieee1275_children_first) (const char *devpath, + struct grub_ieee1275_devalias *alias); ++int EXPORT_FUNC(grub_ieee1275_cas_reboot) (char *script); ++int EXPORT_FUNC(grub_ieee1275_set_boot_last_label) (const char *text); + + char *EXPORT_FUNC(grub_ieee1275_get_boot_dev) (void); + diff --git a/grub2-ppc64le-disable-video.patch b/grub2-ppc64le-disable-video.patch new file mode 100644 index 0000000..1934de3 --- /dev/null +++ b/grub2-ppc64le-disable-video.patch @@ -0,0 +1,43 @@ +--- a/grub-core/kern/ieee1275/cmain.c ++++ b/grub-core/kern/ieee1275/cmain.c +@@ -89,7 +89,10 @@ + } + + if (rc >= 0 && grub_strncmp (tmp, "IBM", 3) == 0) +- grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_TREE_SCANNING_FOR_DISKS); ++ { ++ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_TREE_SCANNING_FOR_DISKS); ++ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT); ++ } + + /* Old Macs have no key repeat, newer ones have fully working one. + The ones inbetween when repeated key generates an escaoe sequence +--- a/grub-core/video/ieee1275.c ++++ b/grub-core/video/ieee1275.c +@@ -351,9 +351,12 @@ + + GRUB_MOD_INIT(ieee1275_fb) + { +- find_display (); +- if (display) +- grub_video_register (&grub_video_ieee1275_adapter); ++ if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT)) ++ { ++ find_display (); ++ if (display) ++ grub_video_register (&grub_video_ieee1275_adapter); ++ } + } + + GRUB_MOD_FINI(ieee1275_fb) +--- a/include/grub/ieee1275/ieee1275.h ++++ b/include/grub/ieee1275/ieee1275.h +@@ -141,6 +141,8 @@ + */ + GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY, + #endif ++ ++ GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT + }; + + extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag); diff --git a/grub2-ppc64le-memory-map.patch b/grub2-ppc64le-memory-map.patch new file mode 100644 index 0000000..abe6004 --- /dev/null +++ b/grub2-ppc64le-memory-map.patch @@ -0,0 +1,78 @@ +--- a/grub-core/kern/ieee1275/openfw.c ++++ b/grub-core/kern/ieee1275/openfw.c +@@ -302,6 +302,34 @@ + return args.catch_result; + } + ++/* Preallocate IEEE1275_MAX_MAP_RESOURCE map tracks to track the ++ * map regions allocated to us by the firmware. Cannot ++ * dynamically allocate them, since the heap is not set ++ * yet. ++ */ ++struct grub_map_track grub_map_track[IEEE1275_MAX_MAP_RESOURCE]; ++int grub_map_track_index=0; ++ ++void ++grub_releasemap () ++{ ++ int i=0; ++ for (i=grub_map_track_index-1; i >= 0; i--) ++ grub_ieee1275_release(grub_map_track[i].addr, grub_map_track[i].size); ++ grub_map_track_index = 0; ++ return; ++} ++ ++static void ++grub_track_map (grub_addr_t addr, grub_size_t size) ++{ ++ if (grub_map_track_index >= IEEE1275_MAX_MAP_RESOURCE) ++ return; ++ grub_map_track[grub_map_track_index].addr = addr; ++ grub_map_track[grub_map_track_index++].size = size; ++ return; ++} ++ + grub_err_t + grub_claimmap (grub_addr_t addr, grub_size_t size) + { +@@ -317,6 +345,7 @@ + return grub_errno; + } + ++ grub_track_map (addr, size); + return GRUB_ERR_NONE; + } + +--- a/include/grub/ieee1275/ieee1275.h ++++ b/include/grub/ieee1275/ieee1275.h +@@ -33,6 +33,12 @@ + unsigned int size; + }; + ++#define IEEE1275_MAX_MAP_RESOURCE 10 ++struct grub_map_track { ++ grub_addr_t addr; ++ grub_size_t size; ++}; ++ + #define IEEE1275_MAX_PROP_LEN 8192 + #define IEEE1275_MAX_PATH_LEN 256 + +@@ -228,6 +234,7 @@ + int EXPORT_FUNC(grub_ieee1275_get_block_size) (grub_ieee1275_ihandle_t ihandle); + + grub_err_t EXPORT_FUNC(grub_claimmap) (grub_addr_t addr, grub_size_t size); ++void EXPORT_FUNC(grub_releasemap) (void); + + int + EXPORT_FUNC(grub_ieee1275_map) (grub_addr_t phys, grub_addr_t virt, +--- a/grub-core/kern/ieee1275/init.c ++++ b/grub-core/kern/ieee1275/init.c +@@ -111,6 +111,7 @@ + void + grub_exit (void) + { ++ grub_releasemap(); + grub_ieee1275_exit (); + } + diff --git a/grub2-s390x-01-Changes-made-and-files-added-in-order-to-allow-s390x.patch b/grub2-s390x-01-Changes-made-and-files-added-in-order-to-allow-s390x.patch new file mode 100644 index 0000000..287b28a --- /dev/null +++ b/grub2-s390x-01-Changes-made-and-files-added-in-order-to-allow-s390x.patch @@ -0,0 +1,296 @@ +From f38ada424e7d991a0121253ba1abc430b86a990b Mon Sep 17 00:00:00 2001 +From: John Jolly +Date: Wed, 22 Jan 2014 01:18:10 -0700 +Subject: [PATCH 1/3] - Changes made and files added in order to allow s390x + build + +--- + grub-core/kern/emu/cache_s.S | 1 + + grub-core/kern/emu/lite.c | 2 ++ + grub-core/kern/s390x/dl.c | 37 +++++++++++++++++++++++++++++++++++ + grub-core/lib/s390x/setjmp.S | 46 ++++++++++++++++++++++++++++++++++++++++++++ + grub-core/lib/setjmp.S | 2 ++ + include/grub/cache.h | 2 +- + include/grub/s390x/setjmp.h | 29 ++++++++++++++++++++++++++++ + include/grub/s390x/time.h | 27 ++++++++++++++++++++++++++ + include/grub/s390x/types.h | 32 ++++++++++++++++++++++++++++++ + 9 files changed, 177 insertions(+), 1 deletion(-) + create mode 100644 grub-core/kern/s390x/dl.c + create mode 100644 grub-core/lib/s390x/setjmp.S + create mode 100644 include/grub/s390x/setjmp.h + create mode 100644 include/grub/s390x/time.h + create mode 100644 include/grub/s390x/types.h + +--- a/grub-core/kern/emu/cache_s.S ++++ b/grub-core/kern/emu/cache_s.S +@@ -15,6 +15,7 @@ + #include "../powerpc/cache.S" + #elif defined(__ia64__) || defined(__arm__) || defined(__aarch64__) || \ + defined(__mips__) || defined(__riscv) ++#elif defined(__s390x__) + #else + #error "No target cpu type is defined" + #endif +--- a/grub-core/kern/emu/lite.c ++++ b/grub-core/kern/emu/lite.c +@@ -26,6 +26,8 @@ + #include "../arm64/dl.c" + #elif defined(__riscv) + #include "../riscv/dl.c" ++#elif defined(__s390x__) ++#include "../s390x/dl.c" + #else + #error "No target cpu type is defined" + #endif +--- a/grub-core/kern/dl.c ++++ b/grub-core/kern/dl.c +@@ -230,7 +230,7 @@ + const Elf_Shdr *s; + grub_size_t tsize = 0, talign = 1; + #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ +- !defined (__loongarch__) ++ !defined (__loongarch__) && !defined (__s390x__) + grub_size_t tramp; + grub_size_t got; + grub_err_t err; +@@ -247,7 +247,7 @@ + } + + #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ +- !defined (__loongarch__) ++ !defined (__loongarch__) && !defined (__s390x__) + err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got); + if (err) + return err; +@@ -311,7 +311,7 @@ + } + } + #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ +- !defined (__loongarch__) ++ !defined (__loongarch__) && !defined (__s390x__) + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN); + mod->tramp = ptr; + mod->trampptr = ptr; +--- /dev/null ++++ b/grub-core/kern/s390x/dl.c +@@ -0,0 +1,40 @@ ++/* dl.c - arch-dependent part of loadable module support */ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2002,2004,2005,2007,2009 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++ ++/* Check if EHDR is a valid ELF header. */ ++grub_err_t ++grub_arch_dl_check_header (void *ehdr) ++{ ++ (void)(ehdr); ++ return GRUB_ERR_BUG; ++} ++ ++/* Relocate symbols. */ ++grub_err_t ++grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, ++ Elf_Shdr *s, grub_dl_segment_t seg) ++{ ++ (void)(mod); ++ (void)(ehdr); ++ (void)(s); ++ (void)(seg); ++ return GRUB_ERR_BUG; ++} +--- /dev/null ++++ b/grub-core/lib/s390x/setjmp.S +@@ -0,0 +1,46 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2005,2007,2009 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++ ++ .file "setjmp.S" ++ ++GRUB_MOD_LICENSE "GPLv3+" ++ ++ .text ++ ++/* ++ * int grub_setjmp (grub_jmp_buf env) ++ */ ++FUNCTION(grub_setjmp) ++ stmg %r11,%r15,0(%r2) ++ lghi %r2,0 ++ br %r14 ++ ++/* ++ * int grub_longjmp (grub_jmp_buf env, int val) ++ */ ++FUNCTION(grub_longjmp) ++ chi %r3,0 ++ jne .L2 ++ lghi %r3,1 ++.L2: ++ lmg %r11,%r15,0(%r2) ++ lgr %r2,%r3 ++ br %r14 +--- a/grub-core/lib/setjmp.S ++++ b/grub-core/lib/setjmp.S +@@ -23,6 +23,8 @@ + #include "./loongarch64/setjmp.S" + #elif defined(__riscv) + #include "./riscv/setjmp.S" ++#elif defined(__s390x__) ++#include "./s390x/setjmp.S" + #else + #error "Unknown target cpu type" + #endif +--- a/include/grub/cache.h ++++ b/include/grub/cache.h +@@ -23,7 +23,7 @@ + #include + #include + +-#if defined (__i386__) || defined (__x86_64__) ++#if defined (__i386__) || defined (__x86_64__) || defined (__s390x__) + static inline void + grub_arch_sync_caches (void *address __attribute__ ((unused)), + grub_size_t len __attribute__ ((unused))) +--- /dev/null ++++ b/include/grub/s390x/setjmp.h +@@ -0,0 +1,29 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2002,2004,2006,2007,2009 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_SETJMP_CPU_HEADER ++#define GRUB_SETJMP_CPU_HEADER 1 ++ ++#include ++ ++typedef grub_uint64_t grub_jmp_buf[5]; ++ ++int grub_setjmp (grub_jmp_buf env) __attribute__ ((returns_twice)); ++void grub_longjmp (grub_jmp_buf env, int val) __attribute__ ((noreturn)); ++ ++#endif /* ! GRUB_SETJMP_CPU_HEADER */ +--- /dev/null ++++ b/include/grub/s390x/time.h +@@ -0,0 +1,27 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2007 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef KERNEL_CPU_TIME_HEADER ++#define KERNEL_CPU_TIME_HEADER 1 ++ ++static __inline void ++grub_cpu_idle (void) ++{ ++} ++ ++#endif /* ! KERNEL_CPU_TIME_HEADER */ +--- /dev/null ++++ b/include/grub/s390x/types.h +@@ -0,0 +1,32 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2002,2004,2006,2007 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_TYPES_CPU_HEADER ++#define GRUB_TYPES_CPU_HEADER 1 ++ ++/* The size of void *. */ ++#define GRUB_TARGET_SIZEOF_VOID_P 8 ++ ++/* The size of long. */ ++#define GRUB_TARGET_SIZEOF_LONG 8 ++ ++/* s390x is big-endian. */ ++#define GRUB_TARGET_WORDS_BIGENDIAN 1 ++ ++ ++#endif /* ! GRUB_TYPES_CPU_HEADER */ +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -407,6 +407,9 @@ + extra_dist = kern/i386/realmode.S; + extra_dist = boot/i386/pc/lzma_decode.S; + extra_dist = kern/mips/cache_flush.S; ++ ++ extra_dist = kern/s390x/dl.c; ++ extra_dist = lib/s390x/setjmp.S; + }; + + program = { diff --git a/grub2-s390x-03-output-7-bit-ascii.patch b/grub2-s390x-03-output-7-bit-ascii.patch new file mode 100644 index 0000000..4bb4ff0 --- /dev/null +++ b/grub2-s390x-03-output-7-bit-ascii.patch @@ -0,0 +1,530 @@ +Vn: + * recognize 'dev/sclp_line0' as 3215-look-alike. [bnc#876743] +Vn+1: + * revamp readkey_dumb(). +Vn+2: + * support hotkeys on all line-mode terminals, not only 3215. [bnc#885668] + +--- + grub-core/kern/emu/main.c | 8 + + grub-core/normal/menu_text.c | 54 +++++++- + grub-core/normal/term.c | 2 + grub-core/osdep/unix/emuconsole.c | 238 +++++++++++++++++++++++++++++++++++++- + include/grub/term.h | 4 + 5 files changed, 294 insertions(+), 12 deletions(-) + +--- a/grub-core/osdep/unix/emuconsole.c ++++ b/grub-core/osdep/unix/emuconsole.c +@@ -39,17 +39,61 @@ + + #include + ++#include ++#include ++ + extern struct grub_terminfo_output_state grub_console_terminfo_output; + static int original_fl; + static int saved_orig; + static struct termios orig_tty; + static struct termios new_tty; ++static int console_mode = 0; ++ ++#define MAX_LEN 1023 ++#if defined(__s390x__) ++static int ++dummy (void) ++{ ++ return 0; ++} ++#endif ++#if 0 ++static char msg[MAX_LEN+1]; ++static void ++dprint (int len) ++{ ++ if (len < 0) ++ return; ++ if (len > MAX_LEN) ++ len = MAX_LEN; ++ write (2, msg, len); ++} ++#define dprintf(fmt, vargs...) dprint(snprintf(msg, MAX_LEN, fmt, ## vargs)) ++#else ++#define dprintf(fmt, vargs...) {} ++#endif + + static void +-put (struct grub_term_output *term __attribute__ ((unused)), const int c) ++put (struct grub_term_output *term, const int c) + { + char chr = c; + ssize_t actual; ++ struct grub_terminfo_output_state *data ++ = (struct grub_terminfo_output_state *) term->data; ++ ++ if (term->flags & GRUB_TERM_DUMB) { ++ if (c == '\n') { ++ data->pos.y++; ++ data->pos.x = 0; ++ } else { ++ data->pos.x++; ++ } ++ if (0) { ++ if (c == ' ') chr = '_'; ++ if (c == GRUB_TERM_BACKSPACE) chr = '{'; ++ if (c == '\b') chr = '<'; ++ } ++ } + + actual = write (STDOUT_FILENO, &chr, 1); + if (actual < 1) +@@ -60,17 +104,152 @@ + } + + static int +-readkey (struct grub_term_input *term __attribute__ ((unused))) ++readkey (struct grub_term_input *term) + { + grub_uint8_t c; + ssize_t actual; + ++ fd_set readfds; ++ struct timeval timeout; ++ int sel; ++ FD_SET (0, &readfds); ++ timeout.tv_sec = 0; ++ timeout.tv_usec = 500000; ++ if ((sel=select (1, &readfds, (fd_set *)0, (fd_set *)0, &timeout)) <= 0) ++ { ++ if (sel < 0 && errno == EINTR) ++ return 0x03; /* '^C' */ ++ return -1; ++ } ++ + actual = read (STDIN_FILENO, &c, 1); + if (actual > 0) + return c; + return -1; + } + ++#define NO_KEY ((grub_uint8_t)-1) ++static int ++readkey_dumb (struct grub_term_input *term) ++{ ++ grub_uint8_t c; ++ static grub_uint8_t p = NO_KEY; ++ ++ c = readkey (term); ++ if (c == NO_KEY) ++ return -1; ++ if ((p == '^' || p == '\n') && c == '\n') /* solitary '^' or '\n'? */ ++ { ++ c = p; /* use immediately! */ ++ p = '\n'; ++ } ++ else if ((c == '\n' || c == '^') && p != c) /* non-duplicate specials? */ ++ { ++ p = c; /* remember! */ ++ c = NO_KEY; ++ } ++ else if (p == '^') ++ { ++ if (c != '^') ++ c &= 0x1F; ++ p = NO_KEY; ++ } ++ else ++ p = c; ++ return c; ++} ++ ++static void ++grub_dumb_putchar (struct grub_term_output *term, ++ const struct grub_unicode_glyph *c) ++{ ++ unsigned i; ++ ++ /* For now, do not try to use a surrogate pair. */ ++ if (c->base > 0xffff) ++ put (term, '?'); ++ else ++ put (term, (c->base & 0xffff)); ++ ++ if (0) { ++ for (i = 0; i < c->ncomb; i++) ++ if (c->base < 0xffff) ++ put (term, grub_unicode_get_comb (c)[i].code); ++ } ++} ++ ++static struct grub_term_coordinate ++grub_dumb_getxy (struct grub_term_output *term) ++{ ++ struct grub_terminfo_output_state *data ++ = (struct grub_terminfo_output_state *) term->data; ++ ++ dprintf ("<%d,%d>", data->pos.x, data->pos.y); ++ return data->pos; ++} ++ ++static struct grub_term_coordinate ++grub_dumb_getwh (struct grub_term_output *term) ++{ ++ static int once = 0; ++ struct grub_terminfo_output_state *data ++ = (struct grub_terminfo_output_state *) term->data; ++ ++ if (!once++) ++ dprintf ("dumb_getwh: w=%d h=%d\n", data->size.x, data->size.y); ++ return data->size; ++} ++ ++static void ++grub_dumb_gotoxy (struct grub_term_output *term, ++ struct grub_term_coordinate pos) ++{ ++ struct grub_terminfo_output_state *data ++ = (struct grub_terminfo_output_state *) term->data; ++ ++ if (pos.x > grub_term_width (term) || pos.y > grub_term_height (term)) ++ { ++ grub_error (GRUB_ERR_BUG, "invalid point (%u,%u)", pos.x, pos.y); ++ return; ++ } ++ ++ dprintf("goto(%d,%d)", pos.x, pos.y); ++ if (pos.x > (grub_term_width (term) - 4)) { ++ dprintf (" really?"); ++ //return; ++ } ++ ++ if (data->gotoxy) ++ { ++ int i; ++ dprintf ("data-gotoxy"); ++ if (data->pos.y != pos.y) { ++ put (term, '\n'); ++ ++ for (i = 1; i < pos.x; i++ ) ++ put (term, ' '); ++ } ++ } ++ else ++ { ++ int i = 0; ++ if (data->pos.y != pos.y || data->pos.x > pos.x) { ++ if (data->pos.y >= pos.y) data->pos.y = pos.y - 1; ++ if (pos.y - data->pos.y > 3) data->pos.y = pos.y - 2; ++ dprintf (" <%dnl>+%d", (pos.y - data->pos.y), pos.x); ++ for (i = data->pos.y; i < pos.y; i++ ) ++ put (term, '\n'); ++ } ++ for (i = data->pos.x; i < pos.x; i++ ) ++ put (term, ' '); ++ dprintf ("#%d", i); ++ grub_dumb_getxy (term); ++ } ++ ++ dprintf ("\n"); ++ data->pos = pos; ++} ++ + static grub_err_t + grub_console_init_input (struct grub_term_input *term) + { +@@ -105,7 +284,8 @@ + grub_console_init_output (struct grub_term_output *term) + { + struct winsize size; +- if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &size) >= 0) ++ if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &size) >= 0 && ++ size.ws_col > 0 && size.ws_row > 0) + { + grub_console_terminfo_output.size.x = size.ws_col; + grub_console_terminfo_output.size.y = size.ws_row; +@@ -115,6 +295,8 @@ + grub_console_terminfo_output.size.x = 80; + grub_console_terminfo_output.size.y = 24; + } ++ if (console_mode == 3215) ++ grub_console_terminfo_output.size.x -= 1; + + grub_terminfo_output_init (term); + +@@ -161,24 +343,72 @@ + void + grub_console_init (void) + { ++#if ! defined(__s390x__) + const char *cs = nl_langinfo (CODESET); + if (cs && grub_strcasecmp (cs, "UTF-8")) + grub_console_term_output.flags = GRUB_TERM_CODE_TYPE_UTF8_LOGICAL; + else + grub_console_term_output.flags = GRUB_TERM_CODE_TYPE_ASCII; ++#else ++ char link[MAX_LEN+1]; ++ ssize_t len = readlink ("/proc/self/fd/0", link, MAX_LEN); ++ ++ if (len > 0) ++ link[len] = 0; ++ else ++ link[0] = 0; ++ if (grub_strncmp ("/dev/ttyS", link, 9) == 0 ) ++ console_mode = 3215; ++ else if (grub_strncmp ("/dev/3270/tty", link, 13) == 0 ) ++ console_mode = 3270; ++ else if (grub_strncmp ("/dev/sclp_line", link, 14) == 0 ) ++ console_mode = 3215; ++ grub_console_term_output.flags = GRUB_TERM_CODE_TYPE_ASCII; ++ switch (console_mode) ++ { ++ case 3215: ++ grub_console_term_output.flags |= GRUB_TERM_DUMB; ++ /* FALLTHROUGH */ ++ case 3270: ++ grub_console_term_output.flags |= GRUB_TERM_LINE; ++ grub_console_term_output.flags |= GRUB_TERM_NO_ECHO; ++ grub_console_terminfo_input.readkey = readkey_dumb; ++ break; ++ default: ++ break; ++ } ++#endif ++ if (grub_console_term_output.flags & GRUB_TERM_DUMB) ++ { ++ grub_console_term_output.putchar = grub_dumb_putchar, ++ grub_console_term_output.getxy = grub_dumb_getxy; ++ grub_console_term_output.getwh = grub_dumb_getwh; ++ grub_console_term_output.gotoxy = grub_dumb_gotoxy; ++ grub_console_term_output.cls = (void *) dummy; ++ grub_console_term_output.setcolorstate = (void *) dummy; ++ grub_console_term_output.setcursor = (void *) dummy; ++ grub_console_term_output.progress_update_divisor = GRUB_PROGRESS_NO_UPDATE; ++ } + grub_term_register_input ("console", &grub_console_term_input); + grub_term_register_output ("console", &grub_console_term_output); + grub_terminfo_init (); +- grub_terminfo_output_register (&grub_console_term_output, "vt100-color"); ++ grub_terminfo_output_register (&grub_console_term_output, ++ (grub_console_term_output.flags & GRUB_TERM_DUMB) ? "dumb":"vt100-color"); + } + + void + grub_console_fini (void) + { ++ dprintf( "grub_console_fini: %d\n", grub_console_term_output.flags & GRUB_TERM_DUMB); + if (saved_orig) + { + fcntl (STDIN_FILENO, F_SETFL, original_fl); + tcsetattr(STDIN_FILENO, TCSANOW, &orig_tty); + } ++ if (!(grub_console_term_output.flags & GRUB_TERM_DUMB)) ++ { ++ const char clear[] = { 0x1b, 'c', 0 }; ++ write (STDOUT_FILENO, clear, 2); ++ } + saved_orig = 0; + } +--- a/grub-core/normal/menu_text.c ++++ b/grub-core/normal/menu_text.c +@@ -113,6 +113,7 @@ + { + int i; + ++ if (! (term->flags & GRUB_TERM_DUMB)) { + grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL); + + grub_term_gotoxy (term, (struct grub_term_coordinate) { geo->first_entry_x - 1, +@@ -142,7 +143,7 @@ + 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 +@@ -155,6 +156,15 @@ + int ret = 0; + grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL); + ++ if (edit && (term->flags & GRUB_TERM_LINE)) ++ { ++ ret += grub_print_message_indented_real ++ (_("Minimum Emacs-like screen editing is supported. '^i' lists " ++ "completions. Type '^x' to boot, '^c' for a command-line " ++ "or '^[' to discard edits and return to the GRUB menu."), ++ STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run); ++ } ++ else + if (edit) + { + ret += grub_print_message_indented_real (_("Minimum Emacs-like screen editing is \ +@@ -165,10 +175,15 @@ + } + else + { ++#if defined(__s390x__hotkey) ++ ret += grub_print_message_indented_real ++ (_("Select a menu option by pressing the hotkey specified. "), ++ STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run); ++#else + char *msg_translated; + + msg_translated = grub_xasprintf (_("Use the %C and %C keys to select which " +- "entry is highlighted."), ++ "entry is highlighted. "), + GRUB_UNICODE_UPARROW, + GRUB_UNICODE_DOWNARROW); + if (!msg_translated) +@@ -177,6 +192,7 @@ + STANDARD_MARGIN, term, dry_run); + + grub_free (msg_translated); ++#endif + + if (nested) + { +@@ -211,6 +227,10 @@ + + title = entry ? entry->title : ""; + title_len = grub_strlen (title); ++ ++ if ((data->term->flags & GRUB_TERM_DUMB) && title[0] == '\0') ++ return; ++ + unicode_title = grub_calloc (title_len, sizeof (*unicode_title)); + if (! unicode_title) + /* XXX How to show this error? */ +@@ -244,6 +264,14 @@ + if (data->geo.num_entries > 1) + grub_putcode (highlight ? '*' : ' ', data->term); + ++ if ((data->term->flags & GRUB_TERM_LINE) && title[0] != '\0') { ++ grub_putcode('(', data->term); ++ grub_putcode((entry && entry->hotkey >= '0' && entry->hotkey <= 'z') ? ++ entry->hotkey : ' ', data->term); ++ grub_putcode(')', data->term); ++ grub_putcode(' ', data->term); ++ } ++ + grub_print_ucs4_menu (unicode_title, + unicode_title + len, + 0, +@@ -416,6 +444,8 @@ + grub_term_highlight_color = old_color_highlight; + geo->timeout_y = geo->first_entry_y + geo->num_entries + + geo->border + empty_lines; ++ if (term->flags & GRUB_TERM_DUMB) ++ geo->timeout_y = 1; + if (bottom_message) + { + grub_term_gotoxy (term, +@@ -425,6 +455,8 @@ + print_message (nested, edit, term, 0); + geo->timeout_y += msg_num_lines; + } ++ if (term->flags & GRUB_TERM_DUMB) ++ geo->timeout_y = 1; + geo->right_margin = grub_term_width (term) + - geo->first_entry_x + - geo->entry_width - 1; +@@ -436,12 +468,19 @@ + struct menu_viewer_data *data = dataptr; + char *msg_translated = 0; + +- grub_term_gotoxy (data->term, ++ if (data->geo.timeout_y) ++ grub_term_gotoxy (data->term, + (struct grub_term_coordinate) { 0, data->geo.timeout_y }); + ++ if (data->term->flags & GRUB_TERM_DUMB) ++ { ++ if (! data->geo.timeout_y) ++ data->timeout_msg = TIMEOUT_TERSE; ++ data->geo.timeout_y = 0; ++ } + if (data->timeout_msg == TIMEOUT_TERSE + || data->timeout_msg == TIMEOUT_TERSE_NO_MARGIN) +- msg_translated = grub_xasprintf (_("%ds"), timeout); ++ msg_translated = grub_xasprintf (_(" %ds"), timeout); + else + msg_translated = grub_xasprintf (_("The highlighted entry will be executed automatically in %ds."), timeout); + if (!msg_translated) +@@ -471,6 +510,8 @@ + data->term); + grub_free (msg_translated); + ++ if (data->term->flags & GRUB_TERM_DUMB) ++ return; + grub_term_gotoxy (data->term, + (struct grub_term_coordinate) { + grub_term_cursor_x (&data->geo), +@@ -498,7 +539,7 @@ + data->first = entry; + complete_redraw = 1; + } +- if (complete_redraw) ++ if (complete_redraw || (data->term->flags & GRUB_TERM_DUMB)) + print_entries (data->menu, data); + else + { +@@ -528,6 +569,9 @@ + struct menu_viewer_data *data = dataptr; + int i; + ++ if ((data->term->flags & GRUB_TERM_DUMB)) ++ return; ++ + for (i = 0; i < data->geo.timeout_lines;i++) + { + grub_term_gotoxy (data->term, (struct grub_term_coordinate) { +--- a/grub-core/normal/term.c ++++ b/grub-core/normal/term.c +@@ -981,7 +981,7 @@ + { + print_ucs4_real (str, last_position, margin_left, margin_right, + term, 0, 0, 1, skip_lines, max_lines, +- contchar, 1, pos); ++ contchar, (term->flags & GRUB_TERM_DUMB)? 0 : 1, pos); + } + + void +--- a/grub-core/kern/emu/main.c ++++ b/grub-core/kern/emu/main.c +@@ -190,6 +190,12 @@ + NULL, help_filter, NULL + }; + ++void ++ignore (int num __attribute__ ((unused))) ++{ ++ return; ++} ++ + + + #pragma GCC diagnostic ignored "-Wmissing-prototypes" +@@ -259,7 +265,7 @@ + sleep (1); + } + +- signal (SIGINT, SIG_IGN); ++ signal (SIGINT, (sighandler_t) &ignore); + grub_console_init (); + grub_host_init (); + +--- a/include/grub/term.h ++++ b/include/grub/term.h +@@ -102,8 +102,10 @@ + #define GRUB_TERM_NO_EDIT (1 << 1) + /* Set when the terminal cannot do fancy things. */ + #define GRUB_TERM_DUMB (1 << 2) ++/* Set when the terminal is line oriented. */ ++#define GRUB_TERM_LINE (1 << 3) + /* Which encoding does terminal expect stream to be. */ +-#define GRUB_TERM_CODE_TYPE_SHIFT 3 ++#define GRUB_TERM_CODE_TYPE_SHIFT 4 + #define GRUB_TERM_CODE_TYPE_MASK (7 << GRUB_TERM_CODE_TYPE_SHIFT) + /* Only ASCII characters accepted. */ + #define GRUB_TERM_CODE_TYPE_ASCII (0 << GRUB_TERM_CODE_TYPE_SHIFT) diff --git a/grub2-s390x-04-grub2-install.patch b/grub2-s390x-04-grub2-install.patch new file mode 100644 index 0000000..dab949e --- /dev/null +++ b/grub2-s390x-04-grub2-install.patch @@ -0,0 +1,1276 @@ +From: Raymund Will +Subject: Allow s390x-emu to be "installed" +References: fate#314213, bnc#866867, bnc#868909, bnc#874155, bnc#876743, + bnc#879136, bnc#889562, bnc#889572 +Patch-Mainline: no + +V2: + * try harder to find root filesystem (incl. subvol-handling). + * read /etc/sysconfig/bootloader as final fallback. +V3: + * refresh initrd by default, prefer running kernel and + re-zipl despite minor issues. [bnc#866867, fate#314213] +V4: + * append 'quiet splash=silent' for 'initgrub'-boot. + * properly check for dracut script during 'grub2-install'. + * move 'zipl2grub.pl' to '/usr/sbin/grub2-zipl-setup'. +V5: + * actually call 'grub2-zipl-setup' from 'grub2-install'. + * handle 'GRUB{,_EMU}_CONMODE'. [bnc#868909] +V6: + * grub2-zipl-setup: support 'xz' initrd compression. [bnc#874155] +V7: + * dracut-grub2.sh: use 'showconsole' to determine console device. [bnc#876743] + * dracut-grub2.sh: and fall back to '/dev/console' (instead of 'tty1'). + * dracut-grub2.sh: introduce "debug()" helper function. +V8: + * grub2-zipl-setup: replace poor choice in '/sysroot/sys' check. [bnc#879136] + * grub2-zipl-setup: fix typo in '/sysroot/proc' check. +V9: + * grub2-zipl-setup: honor GRUB_DISABLE_LINUX_UUID. [bnc#885854] +V10: + * grub2-zipl-setup: fix stupid typo in previous fix. [bnc#889562, bnc#889572] +V11: + * grub2-zipl-setup: disable 'grub-mkrelpath' acrobatics. [bnc#889572] +V12: + * dracut-grub2.sh: try to mount '/.snapshots' if missing. [bnc#892014] + * dracut-grub2.sh: use '/dev/shm' for debug output, so it's accessible + from grub2-emu's shell. +V13: + * grub2-zipl-setup: make initrd mount '/boot', if needed. [bnc#873951, bnc#892088] + * dracut-grub2.sh: provide /boot from above to grub2-emu in chroot. +V14: + * grub2-zipl-setup: actually remove obsolete kernel/initrds. [bnc#892810] +V15: + * zipl2grub.conf: turn of zipl-prompt and quiescent plymouth. [bsc#898198] +V16: + * dracut-grub2.sh: force read-only '/usr' for kexec. [bsc#932951] +V17: + * grub2-zipl-setup: remove arybase dependency by not referencing $[. [bsc#1055280] +V18: + * dracut-zipl-refresh.sh.in: initial submission. [bsc#1127293] + * dracut-grub2.sh: try to call zipl-refresh on failed kexec and drop + to an emergency shell otherwise +V19: + * dracut-grub2.sh: use 'grep -P' instead of '-E'. [bsc#1136970] +V20: + * dracut-grub2.sh: add support for '/boot/writable'. [bsc#1190395] + + +--- + Makefile.util.def | 46 +++ + configure.ac | 9 + grub-core/Makefile.core.def | 7 + grub-core/osdep/basic/no_platform.c | 7 + grub-core/osdep/unix/platform.c | 11 + grub-core/osdep/windows/platform.c | 6 + include/grub/util/install.h | 4 + util/grub-install-common.c | 1 + util/grub-install.c | 43 +++ + util/s390x/dracut-grub2.sh.in | 141 +++++++++++ + util/s390x/dracut-module-setup.sh.in | 19 + + util/s390x/dracut-zipl-refresh.sh.in | 183 ++++++++++++++ + util/s390x/zipl2grub.conf.in | 26 ++ + util/s390x/zipl2grub.pl.in | 423 +++++++++++++++++++++++++++++++++ + 14 files changed, 923 insertions(+), 3 deletions(-) + +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -377,6 +377,7 @@ + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + cppflags = '-DGRUB_SETUP_FUNC=grub_util_bios_setup'; ++ emu_condition = COND_NOT_s390x; + }; + + program = { +@@ -397,6 +398,7 @@ + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + cppflags = '-DGRUB_SETUP_FUNC=grub_util_sparc_setup'; ++ emu_condition = COND_NOT_s390x; + }; + + program = { +@@ -412,6 +414,7 @@ + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; ++ emu_condition = COND_NOT_s390x; + }; + + program = { +@@ -442,6 +445,7 @@ + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; ++ emu_condition = COND_NOT_s390x; + }; + + data = { +@@ -665,6 +669,7 @@ + common = grub-core/disk/host.c; + + common = util/resolve.c; ++ emu_condition = COND_s390x; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + +@@ -734,6 +739,46 @@ + }; + + script = { ++ name = grub-zipl-setup; ++ installdir = sbin; ++ common = util/s390x/zipl2grub.pl.in; ++ enable = emu; ++ emu_condition = COND_s390x; ++}; ++ ++data = { ++ name = zipl2grub.conf.in; ++ common = util/s390x/zipl2grub.conf.in; ++ installdir = grubconf; ++ enable = emu; ++ emu_condition = COND_s390x; ++}; ++ ++script = { ++ name = dracut-module-setup.sh; ++ common = util/s390x/dracut-module-setup.sh.in; ++ enable = emu; ++ emu_condition = COND_s390x; ++ installdir = platform; ++}; ++ ++script = { ++ name = dracut-grub.sh; ++ common = util/s390x/dracut-grub2.sh.in; ++ enable = emu; ++ emu_condition = COND_s390x; ++ installdir = platform; ++}; ++ ++script = { ++ name = dracut-zipl-refresh; ++ common = util/s390x/dracut-zipl-refresh.sh.in; ++ enable = emu; ++ emu_condition = COND_s390x; ++ installdir = platform; ++}; ++ ++script = { + name = grub-mkconfig_lib; + common = util/grub-mkconfig_lib.in; + installdir = noinst; +@@ -1375,6 +1420,7 @@ + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; ++ emu_condition = COND_NOT_s390x; + }; + + program = { +--- a/configure.ac ++++ b/configure.ac +@@ -211,9 +211,9 @@ + esac + fi + +-if test x"$target_cpu-$platform" = xsparc64-emu ; then +- target_m64=1 +-fi ++case x"$target_cpu-$platform" in ++ xsparc64-emu | xs390x-emu) target_m64=1 ;; ++esac + + case "$target_os" in + windows* | mingw32*) target_os=cygwin ;; +@@ -2093,6 +2093,9 @@ + AM_CONDITIONAL([COND_sparc64_emu], [test x$target_cpu = xsparc64 -a x$platform = xemu]) + AM_CONDITIONAL([COND_x86_64_efi], [test x$target_cpu = xx86_64 -a x$platform = xefi]) + AM_CONDITIONAL([COND_x86_64_xen], [test x$target_cpu = xx86_64 -a x$platform = xxen]) ++AM_CONDITIONAL([COND_s390x], [test x$target_cpu = xs390x ]) ++AM_CONDITIONAL([COND_NOT_s390x], [test x$target_cpu != xs390x ]) ++AM_CONDITIONAL([COND_s390x_emu], [test x$target_cpu = xs390x -a x$platform = xemu]) + + AM_CONDITIONAL([COND_HOST_HURD], [test x$host_kernel = xhurd]) + AM_CONDITIONAL([COND_HOST_LINUX], [test x$host_kernel = xlinux]) +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -1183,6 +1183,7 @@ + module = { + name = videotest; + common = commands/videotest.c; ++ emu_condition = COND_NOT_s390x; + }; + + module = { +@@ -1637,6 +1638,7 @@ + common = gfxmenu/gui_progress_bar.c; + common = gfxmenu/gui_util.c; + common = gfxmenu/gui_string_util.c; ++ emu_condition = COND_NOT_s390x; + }; + + module = { +@@ -2075,11 +2077,13 @@ + name = gfxterm; + common = term/gfxterm.c; + enable = videomodules; ++ emu_condition = COND_NOT_s390x; + }; + + module = { + name = gfxterm_background; + common = term/gfxterm_background.c; ++ emu_condition = COND_NOT_s390x; + }; + + module = { +@@ -2202,6 +2206,7 @@ + enable = x86_64_efi; + enable = emu; + enable = xen; ++ emu_condition = COND_NOT_s390x; + }; + + module = { +@@ -2248,6 +2253,7 @@ + module = { + name = gfxterm_menu; + common = tests/gfxterm_menu.c; ++ emu_condition = COND_NOT_s390x; + }; + + module = { +@@ -2409,6 +2415,7 @@ + enable = x86_64_efi; + enable = emu; + enable = xen; ++ emu_condition = COND_NOT_s390x; + }; + + module = { +--- a/grub-core/osdep/basic/no_platform.c ++++ b/grub-core/osdep/basic/no_platform.c +@@ -44,3 +44,10 @@ + { + grub_util_error ("%s", _("no SGI routines are available for your platform")); + } ++ ++void ++grub_install_zipl (const char *d, int i, int f) ++{ ++ grub_util_error ("%s", _("no zIPL routines are available for your platform")); ++} ++ +--- a/grub-core/osdep/unix/platform.c ++++ b/grub-core/osdep/unix/platform.c +@@ -239,3 +239,14 @@ + imgfile, destname, NULL }); + grub_util_warn ("%s", _("You will have to set `SystemPartition' and `OSLoader' manually.")); + } ++ ++void ++grub_install_zipl (const char *dest, int install, int force) ++{ ++ if (grub_util_exec ((const char * []){ PACKAGE"-zipl-setup", ++ verbosity ? "-v" : "", ++ install ? "" : "--debug", ++ !force ? "" : "--force", ++ "-z", dest, NULL })) ++ grub_util_error (_("`%s' failed.\n"), PACKAGE"-zipl-setup"); ++} +--- a/grub-core/osdep/windows/platform.c ++++ b/grub-core/osdep/windows/platform.c +@@ -434,3 +434,9 @@ + { + grub_util_error ("%s", _("no SGI routines are available for your platform")); + } ++ ++void ++grub_install_zipl (const char *d, int i, int f) ++{ ++ grub_util_error ("%s", _("no zIPL routines are available for your platform")); ++} +--- a/include/grub/util/install.h ++++ b/include/grub/util/install.h +@@ -110,6 +110,7 @@ + GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI, + GRUB_INSTALL_PLATFORM_RISCV32_EFI, + GRUB_INSTALL_PLATFORM_RISCV64_EFI, ++ GRUB_INSTALL_PLATFORM_S390X_EMU, + GRUB_INSTALL_PLATFORM_MAX + }; + +@@ -237,6 +238,9 @@ + grub_install_sgi_setup (const char *install_device, + const char *imgfile, const char *destname); + ++void ++grub_install_zipl (const char *d, int i, int f); ++ + int + grub_install_compress_gzip (const char *src, const char *dest); + int +--- a/util/grub-install-common.c ++++ b/util/grub-install-common.c +@@ -911,6 +911,7 @@ + [GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI] = { "loongarch64", "efi" }, + [GRUB_INSTALL_PLATFORM_RISCV32_EFI] = { "riscv32", "efi" }, + [GRUB_INSTALL_PLATFORM_RISCV64_EFI] = { "riscv64", "efi" }, ++ [GRUB_INSTALL_PLATFORM_S390X_EMU] = { "s390x", "emu" }, + }; + + char * +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -66,6 +66,7 @@ + static char *disk_module = NULL; + static char *efidir = NULL; + static char *macppcdir = NULL; ++static char *zipldir = NULL; + static int force = 0; + static int have_abstractions = 0; + static int have_cryptodisk = 0; +@@ -106,6 +107,7 @@ + OPTION_NO_BOOTSECTOR, + OPTION_NO_RS_CODES, + OPTION_MACPPC_DIRECTORY, ++ OPTION_ZIPL_DIRECTORY, + OPTION_LABEL_FONT, + OPTION_LABEL_COLOR, + OPTION_LABEL_BGCOLOR, +@@ -181,6 +183,11 @@ + efidir = xstrdup (arg); + return 0; + ++ case OPTION_ZIPL_DIRECTORY: ++ free (zipldir); ++ zipldir = xstrdup (arg); ++ return 0; ++ + case OPTION_DISK_MODULE: + free (disk_module); + disk_module = xstrdup (arg); +@@ -298,6 +305,8 @@ + N_("use DIR as the EFI System Partition root."), 2}, + {"macppc-directory", OPTION_MACPPC_DIRECTORY, N_("DIR"), 0, + N_("use DIR for PPC MAC install."), 2}, ++ {"zipl-directory", OPTION_ZIPL_DIRECTORY, N_("DIR"), 0, ++ N_("use DIR as the zIPL Boot Partition root."), 2}, + {"label-font", OPTION_LABEL_FONT, N_("FILE"), 0, N_("use FILE as font for label"), 2}, + {"label-color", OPTION_LABEL_COLOR, N_("COLOR"), 0, N_("use COLOR for label"), 2}, + {"label-bgcolor", OPTION_LABEL_BGCOLOR, N_("COLOR"), 0, N_("use COLOR for label background"), 2}, +@@ -334,6 +343,8 @@ + #else + return NULL; + #endif ++#elif defined (__s390x__) ++ return "s390x-emu"; + #else + return NULL; + #endif +@@ -510,6 +521,8 @@ + case GRUB_INSTALL_PLATFORM_I386_XEN: + case GRUB_INSTALL_PLATFORM_X86_64_XEN: + case GRUB_INSTALL_PLATFORM_I386_XEN_PVH: ++ ++ case GRUB_INSTALL_PLATFORM_S390X_EMU: + return 0; + + /* pacify warning. */ +@@ -939,6 +952,7 @@ + case GRUB_INSTALL_PLATFORM_I386_XEN: + case GRUB_INSTALL_PLATFORM_X86_64_XEN: + case GRUB_INSTALL_PLATFORM_I386_XEN_PVH: ++ case GRUB_INSTALL_PLATFORM_S390X_EMU: + break; + + case GRUB_INSTALL_PLATFORM_I386_QEMU: +@@ -990,6 +1004,7 @@ + case GRUB_INSTALL_PLATFORM_I386_XEN: + case GRUB_INSTALL_PLATFORM_X86_64_XEN: + case GRUB_INSTALL_PLATFORM_I386_XEN_PVH: ++ case GRUB_INSTALL_PLATFORM_S390X_EMU: + free (install_device); + install_device = NULL; + break; +@@ -1291,6 +1306,20 @@ + } + } + ++ if (platform == GRUB_INSTALL_PLATFORM_S390X_EMU) ++ { ++ if (!zipldir) ++ { ++ char *d = grub_util_path_concat (2, bootdir, "zipl"); ++ if (!grub_util_is_directory (d)) ++ { ++ free (d); ++ grub_util_error ("%s", _("cannot find zIPL directory")); ++ } ++ zipldir = d; ++ } ++ } ++ + grub_install_copy_files (grub_install_source_directory, + grubdir, platform); + +@@ -1541,6 +1570,7 @@ + case GRUB_INSTALL_PLATFORM_I386_XEN: + case GRUB_INSTALL_PLATFORM_X86_64_XEN: + case GRUB_INSTALL_PLATFORM_I386_XEN_PVH: ++ case GRUB_INSTALL_PLATFORM_S390X_EMU: + grub_util_warn ("%s", _("no hints available for your platform. Expect reduced performance")); + break; + /* pacify warning. */ +@@ -1659,6 +1689,10 @@ + strcpy (mkimage_target, "sparc64-ieee1275-raw"); + core_name = "core.img"; + break; ++ case GRUB_INSTALL_PLATFORM_S390X_EMU: ++ strcpy (mkimage_target, "grub2-emu"); ++ core_name = mkimage_target; ++ break; + /* pacify warning. */ + case GRUB_INSTALL_PLATFORM_MAX: + break; +@@ -1674,6 +1708,7 @@ + core_name); + char *prefix = xasprintf ("%s%s", prefix_drive ? : "", + relative_grubdir); ++ if (core_name != mkimage_target) + grub_install_make_image_wrap (/* source dir */ grub_install_source_directory, + /*prefix */ prefix, + /* output */ imgfile, +@@ -1712,6 +1747,10 @@ + /* image target */ mkimage_target, 0); + } + break; ++ ++ case GRUB_INSTALL_PLATFORM_S390X_EMU: ++ break; ++ + case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: + case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI: +@@ -2011,6 +2050,10 @@ + } + break; + ++ case GRUB_INSTALL_PLATFORM_S390X_EMU: ++ grub_install_zipl (zipldir, install_bootsector, force); ++ break; ++ + case GRUB_INSTALL_PLATFORM_MIPSEL_LOONGSON: + case GRUB_INSTALL_PLATFORM_MIPSEL_QEMU_MIPS: + case GRUB_INSTALL_PLATFORM_MIPS_QEMU_MIPS: +--- /dev/null ++++ b/util/s390x/dracut-grub2.sh.in +@@ -0,0 +1,141 @@ ++#!/bin/sh ++# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- ++# ex: ts=8 sw=4 sts=4 et filetype=sh ++#getargbool() { true; } ++ ++if getargbool 0 initgrub && [ ! -e /grub2skip ] || [ -e /grub2force ]; then ++ #type getarg >/dev/null 2>&1 || . /lib/dracut-lib.sh ++ checkro() { ++ local tgt="$1" ++ local dev mp fs opts dc ++ local rofs=true ++ while read dev mp fs opts dc; do ++ [ "$mp" = "$tgt" ] || continue ++ case ",$opts," in ++ (*,ro,*) rofs=true;; ++ (*) rofs=false;; ++ esac ++ done < /proc/mounts ++ echo $rofs ++ } ++ checkd() { ++ [ -d $1 ] && echo true || echo false ++ } ++ checke() { ++ [ -e $1 ] && echo true || echo false ++ } ++ checksubvol() { ++ local tgt="$1" ++ local tst="$2" ++ if [ -n "$tst" -a -e "/sysroot$tgt/$tst" ]; then ++ echo true ++ elif grep -qP '^[^#\s]+\s+'"$tgt"'\s+' /sysroot/etc/fstab; then ++ echo false ++ else ++ echo true ++ fi ++ } ++ checksnap() { ++ # checksubvol /.snapshots grub-snapshot.cfg ++ if [ -e /sysroot/.snapshots/grub-snapshot.cfg ]; then ++ echo true ++ elif grep -qP '^[^#\s]+\s+/.snapshots\s+' /sysroot/etc/fstab; then ++ echo false ++ else ++ echo true ++ fi ++ } ++ checkboot() { ++ [ -d /boot/grub2 ] && echo false || echo true ++ } ++ getterm() { ++ local term="$(getarg TERM)" ++ [ -z "$term" ] && term=dumb ++ echo $term ++ } ++ debug() { ++ if [ -n "$1" ]; then ++ echo "$1" >> /dev/.grub2.debug ++ fi ++ shift; ++ [ -n "$*" ] || return 0 ++ echo "+ $*" >> /dev/.grub2.debug ++ "$@" >> /dev/.grub2.debug ++ } ++ ++ exec_prefix=@exec_prefix@ ++ bindir=@bindir@ ++ if [ -e /sysroot$bindir/grub2-emu ]; then ++ ++ export TERM=$(getterm) ++ export grub2rofs=$(checkro /sysroot) ++ export grub2roufs=$(checkro /sysroot/usr) ++ export grub2sysfs=$(checkd /sysroot/sys/devices/system/memory) ++ export grub2procfs=$(checkd /sysroot/proc/self) ++ export grub2bootfs=$(checkboot) ++ export grub2bootw=$(checksubvol /boot/writable) ++ export grub2devfs=$(checkd /sysroot/dev/disk) ++ export grub2snap=$(checksnap) ++ debug "" export -p ++ ++ _ctty="$(RD_DEBUG= getarg rd.ctty=)" && _ctty="/dev/${_ctty##*/}" ++ if [ -z "$_ctty" ]; then ++ _ctty=$(showconsole) ++ fi ++ if [ -z "$_ctty" ]; then ++ _ctty=console ++ while [ -f /sys/class/tty/$_ctty/active ]; do ++ _ctty=$(cat /sys/class/tty/$_ctty/active) ++ _ctty=${_ctty##* } # last one in the list ++ done ++ _ctty=/dev/$_ctty ++ fi ++ [ -c "$_ctty" ] || _ctty=/dev/console ++ case "$(/usr/bin/setsid --help 2>&1)" in *--ctty*) CTTY="--ctty";; esac ++ ++ CTTY="$CTTY --wait" ++ $grub2rofs || mount -o remount,ro /sysroot ++ $grub2roufs || mount -o remount,ro /sysroot/usr ++ $grub2sysfs || mount --bind {,/sysroot}/sys ++ $grub2procfs || mount --bind {,/sysroot}/proc ++ $grub2bootfs || mount --bind {,/sysroot}/boot ++ $grub2devfs || mount --bind {,/sysroot}/dev ++ $grub2snap || chroot /sysroot mount -rn /.snapshots ++ $grub2bootw || chroot /sysroot mount -rn /boot/writable ++ debug "" cat /proc/mounts ++ ++ debug "Trying grub2-emu (ro=$grub2rofs, TERM=$TERM, ctty=$_ctty)..." ++ setsid $CTTY -- chroot /sysroot $bindir/grub2-emu -X -X 0<>$_ctty 1>&0 2>&0 ++ ++ if [ -x /sysroot@libdir@/grub2/zipl-refresh ]; then ++ setsid $CTTY -- /sysroot@libdir@/grub2/zipl-refresh 0<>$_ctty 1>&0 2>&0 ++ if [ $? != 0 ]; then ++ warn "Not continuing" ++ emergency_shell -n grub2-emu-zipl-refresh ++ else ++ echo "+ reboot" >& $_ctty ++ sleep 3 ++ reboot ++ fi ++ else ++ echo " ++ Attention: 'grub2' failed to start the target kernel and 'zipl-refresh' ++ is not available. This should never happen. Please contact support." >& $_ctty ++ warn "Not continuing" ++ emergency_shell -n grub2-emu-kexec ++ fi ++ ++ $grub2snap || umount /sysroot/.snapshots ++ $grub2devfs || umount /sysroot/dev ++ $grub2bootw || umount /sysroot/boot/writable ++ $grub2bootfs || umount /sysroot/boot ++ $grub2procfs || umount /sysroot/proc ++ $grub2sysfs || umount /sysroot/sys ++ $grub2roufs || mount -o remount,rw /sysroot/usr ++ $grub2rofs || mount -o remount,rw /sysroot ++ else ++ warn "No $bindir/grub2-emu in /sysroot--dropping to emergency shell..." ++ emergency_shell -n no-grub2-emu ++ fi ++fi ++ +--- /dev/null ++++ b/util/s390x/dracut-module-setup.sh.in +@@ -0,0 +1,19 @@ ++#!/bin/bash ++# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- ++# ex: ts=8 sw=4 sts=4 et filetype=sh ++ ++# called by dracut ++check() { ++ local _arch=$(uname -m) ++ [ "$_arch" = "s390" -o "$_arch" = "s390x" ] || return 1 ++ return 0 ++} ++ ++# called by dracut ++install() { ++ inst_hook cleanup 99 "$moddir/grub2.sh" ++ inst_multiple showconsole ++ inst_multiple mount umount chroot cat grep /usr/bin/setsid ++ #inst_multiple grub2-emu kexec ++} ++ +--- /dev/null ++++ b/util/s390x/zipl2grub.conf.in +@@ -0,0 +1,26 @@ ++## This is the template for '@zipldir@/config' and is subject to ++## rpm's %config file handling in case of grub2-s390x-emu package update. ++ ++[defaultboot] ++defaultmenu = menu ++ ++[grub2] ++ target = @zipldir@ ++ ramdisk = @zipldir@/initrd,0x2000000 ++ image = @zipldir@/image ++ parameters = "root=@GRUB_DEVICE@ @GRUB_EMU_CONMODE@ @GRUB_CMDLINE_LINUX@ @GRUB_CMDLINE_LINUX_DEFAULT@ initgrub quiet splash=silent plymouth.enable=0 " ++ ++[skip-grub2] ++ target = @zipldir@ ++ ramdisk = @zipldir@/initrd,0x2000000 ++ image = @zipldir@/image ++ parameters = "root=@GRUB_DEVICE@ @GRUB_CONMODE@ @GRUB_CMDLINE_LINUX@ @GRUB_CMDLINE_LINUX_DEFAULT@ " ++ ++:menu ++ target = @zipldir@ ++ timeout = 16 ++ default = 1 ++ prompt = 0 ++ 1 = grub2 ++ 2 = skip-grub2 ++ +--- /dev/null ++++ b/util/s390x/zipl2grub.pl.in +@@ -0,0 +1,423 @@ ++#!/usr/bin/perl ++use strict; ++ ++my $C = $0; $C =~ s{^.*/}{}; ++ ++my $in = '@sysconfdir@/default/zipl2grub.conf.in'; ++my $default = '@sysconfdir@/default/grub'; ++my $fallback = '@sysconfdir@/zipl.conf'; ++my $sysconfbl = '@sysconfdir@/sysconfig/bootloader'; ++my $zipldir = ""; ++my $running = ""; ++my $refresh = 1; # needs to default to "on" until most bugs are shaken out! ++my $force = 0; ++my $verbose = 0; ++my $debug = 0; ++my $miss = 0; ++my $cfg = ""; ++my %fsdev = (); ++my %fstype = (); ++ ++my %C = ( ++ GRUB_CMDLINE_LINUX_DEFAULT => "quiet splash=silent", ++ GRUB_DISABLE_LINUX_UUID => "false", ++); ++ ++my %Mandatory = ( ++ GRUB_CMDLINE_LINUX_DEFAULT => 1, ++ GRUB_DEVICE => 1, ++); ++ ++sub Panic($$) { ++ printf( STDERR "%s", $_[1]); ++ exit( $_[0]); ++} ++sub Info($$) { ++ printf( STDERR "%s", $_[1]) if ($_[0] <= $verbose); ++} ++sub System(@) { ++ my (@C) =@_; ++ Info( 1, "+ " . join( " ", @C) . "\n"); ++ return 0 if ($debug); ++ system( @C); ++ if ($? == -1) { ++ Panic( $?, "$C[0]: Failed to execute: $!\n"); ++ } elsif ($? & 127) { ++ Panic( $?, sprintf( "$C[0]: Died with signal %d with%s coredump\n", ++ ($? & 127), ($? & 128) ? '' : 'out')); ++ } elsif ( $? >> 8 != 0 ) { ++ Panic( $?, "$C[0]: Failed\n"); ++ } ++ return( 0); ++} ++sub cp($$) { ++ my @C = ( "cp", "-p", $_[0], $_[1]); ++ System( @C); ++} ++sub rm($) { ++ return( 0) unless ( -l $_[0] || -e $_[0]); ++ Info( 2, "+ rm $_[0]\n"); ++ return 0 if ($debug); ++ unlink( $_[0]) || Panic( 1, "$C: unlink: $!.\n"); ++} ++sub mv($$) { ++ Info( 1, "+ mv $_[0] $_[1]\n"); ++ return 0 if ($debug); ++ rename($_[0], $_[1]) || Panic( 1, "$C: rename: $!.\n"); ++} ++sub ln($$) { ++ Info( 1, "+ ln -sf $_[0] $_[1]\n"); ++ return 0 if ($debug); ++ unlink( $_[1]) || Panic( 1, "$C: unlink: $!.\n") if ( -e $_[1]); ++ symlink($_[0], $_[1]) || Panic( 1, "$C: symlink: $!.\n"); ++} ++sub BootCopy($$$) { ++ my( $file, $dir, $tgt) = @_; ++ my $curr = "$dir/$tgt"; ++ my $prev = "$dir/$tgt.prev"; ++ Info(4, "Copy /boot/$file $dir $tgt\n"); ++ if ( -l $curr ) { ++ my $curf = readlink( $curr); ++ if ( $curf ne $file ) { ++ if ( -l $prev ) { ++ my $pref = readlink( $prev); ++ $pref = "$dir/$pref" unless ($pref =~ m{^/}); ++ rm( $pref); ++ } ++ mv( $curr, $prev); ++ } ++ } ++ cp( "/boot/$file", "$dir/$file"); ++ ln( $file, $curr); ++} ++sub MkInitrd($$$) { ++ my( $initrd, $dir, $version) = @_; ++ my @C = ( "dracut", "--hostonly", "--force"); ++ my $uuid; ++ if ( exists( $fsdev{"/boot"}) ) { ++ chomp( $uuid = qx{grub2-probe --target=fs_uuid /boot}); ++ my ($dev, $type) = ($fsdev{"/boot"}, $fstype{"/boot"}); ++ if ( $type eq "auto" ) { ++ chomp( $type = qx{grub2-probe --target=fs /boot}); ++ } ++ if ($C{GRUB_DISABLE_LINUX_UUID} eq "true" && ++ $dev =~ m{^(UUID=|/dev/disk/by-uuid/)}) { ++ chomp( $dev = qx{grub2-probe --target=device /boot}); ++ } ++ push @C, "--mount", "$dev /boot $type ro"; ++ } ++ push @C, "$dir/$initrd", $version; ++ System( @C); ++ ln( $initrd, "$dir/initrd"); ++} ++sub ChkInitrd($$) { ++ my( $dir, $initrd) = @_; ++ my $found = -1; ++ my $d = $dir; ++ my $pattern = qr{lib/dracut/hooks/cleanup/99-grub2.sh}; ++ my $show = "cleanup/99-grub2.sh"; ++ my $cat = undef; ++ my $magic; ++ ++ return $found unless (-r "$dir/$initrd"); ++ open( IN, "< $dir/$initrd") || return $found; ++ my $rd = sysread( IN, $magic, 6); ++ close( IN); ++ return $found unless (defined( $rd) && $rd == 6); ++ $cat = "xzcat" if ($magic eq "\xFD7zXZ\x00"); ++ $cat = "zcat" if (substr($magic, 0, 2) eq "\037\213"); ++ $cat = "cat" if (substr($magic, 0, 5) eq "07070"); ++ return $found unless (defined($cat)); ++ ++ my $modinst = "/usr/lib/dracut/modules.d/99grub2/module-setup.sh"; ++ if ( -r $modinst ) { ++ my( $hook, $ord, $script); ++ my $pat = qr{^\s*inst_hook\s+(\S+)\s+([0-9]+)\s+\"\$moddir/(grub2\.sh)\"}; ++ open( IN, "< $modinst") || die; ++ while ( ) { ++ next unless ($_ =~ $pat); ++ $show = "$1/$2-$3"; ++ $pattern = qr{lib/dracut/hooks/$show}o; ++ last; ++ } ++ close( IN); ++ } ++ ++ $found = 0; ++ Info( 3, "+ $cat $d/$initrd | cpio -it | grep '$show'\n"); ++ open( IN, "$cat $d/$initrd | cpio -it 2>/dev/null |") || ++ Panic( 1, "$C: cpio: $!.\n"); ++ while ( ) { ++ $found = 1 if ($_ =~ $pattern); ++ } ++ close( IN); ++ return $found; ++} ++ ++sub Usage($) { ++ my @cat = ("", ++ "Parameter error.", ++ "zIPL directory missing.", ++ "Configuration template missing.", ++ "Configuration template unreadable.", ++ "zIPL directory not accesible.", ++ "" ++ ); ++ my $msg = ""; ++ ++ $msg .= sprintf( "%s: %s\n", $C, $cat[$_[0]]) if ($_[0] > 0); ++ $msg .= "Usage: $C [-v] [-d] [-f] [-T template] [-z ZIPLDIR]\n"; ++ Panic( $_[0], $msg . "\n"); ++} ++ ++while ( $#ARGV >= 0 ) { ++ $_ = shift; ++ next if /^$/; ++ last if /^--$/; ++ (/^--verbose$/ || /^-v$/) && ($verbose++, next); ++ (/^--quiet$/ || /^-q$/) && ($verbose = 0, next); ++ (/^--debug$/ || /^-d$/) && ($debug = 1, $verbose++, next); ++ (/^--force$/ || /^-f$/) && ($force = $refresh = 1, next); ++ (/^--refresh$/ || /^-r$/) && ($refresh = 1, next); ++ (/^--keep$/ || /^-k$/) && ($refresh = 0, next); ++ (/^--?help/ || /^-h/) && (Usage(0)); ++ (/^--zipldir$/ || /^-z$/) && ($zipldir = shift || Usage(2), next); ++ (/^--template$/ || /^-T$/) && ($in = shift || Usage(3), next); ++ (/^-/) && (Usage(1)); ++ Usage(1); ++} ++Usage(4) if (! -r $in); ++ ++if ($zipldir) { ++ $C{zipldir} = $zipldir; # command-line wins ++} elsif ( exists( $C{zipldir}) ) { ++ $zipldir = $C{zipldir}; # otherwise fall back to config ++} else { ++ $zipldir = $C{zipldir} = "/boot/zipl"; # but don't proceed without... ++} ++Usage(5) if (! -d $zipldir); ++if ( $zipldir eq "/boot" ) { ++ Panic( 5, "$C: zIPL directory '/boot' not supported!\n"); ++} ++ ++if ( ! -r $default && ! -r $fallback && ! -r $sysconfbl ) { ++ Panic( 0, "$C: No configuration files found. Retry later!\n"); ++} ++if ( -r $default ) { ++ open( IN, "< $default") || die; ++ while ( ) { ++ chomp; ++ s{^\s*#.*$}{}; ++ next if m{^\s*$}; ++ s{x}{\x01xx\x01}g; ++ s{\\\"}{\x01x1\x01}g; ++ s{\\\'}{\x01x2\x01}g; ++ Info( 5, "<$_>\n"); ++ if ( m{^([^\s=]+)='([^']*)'\s*(?:#.*)?$} || ++ m{^([^\s=]+)="([^"]*)"\s*(?:#.*)?$} || ++ m{^([^\s=]+)=(\S*)\s*(?:#.*)?$} ) { ++ my ( $k, $v) = ($1, $2); ++ $v =~ s{\x01x2\x01}{\\'}g; ++ $v =~ s{\x01x1\x01}{\\"}g; ++ $v =~ s{\x01xx\x01}{x}g; ++ $C{$k} = $v; ++ next; ++ } ++ print( STDERR "$default:$.: parse error ignored.\n"); ++ } ++ close( IN); ++} ++if ( -r "/etc/fstab" ) { ++ my $regex = qr{^(\S+)\s+(\S+)\s+(\S+)\s+\S+\s+\S+\s+\S+\s*(?:#.*)?$}; ++ open( IN, "< /etc/fstab") || die; ++ while ( ) { ++ next if ( m{^\s*#} ); ++ my ($dev, $mp, $type) = (m{$regex}o); ++ $fsdev{$mp} = $dev; ++ $fstype{$mp} = $type; ++ } ++ close( IN); ++} ++ ++if ( ! exists( $C{GRUB_DEVICE}) && ++ $C{GRUB_CMDLINE_LINUX_DEFAULT} eq "quiet splash=silent" && ++ -r $fallback ) { ++ # configuration incomplete, let's try fallback ++ open( IN, "< $fallback") || die; ++ my $section = ""; ++ while( ){ ++ if ( m{^\[([^\]]+)\]\s*$} ) { ++ $section = $1; ++ } ++ if ( $section eq "ipl" && ++ m{^\s*parameters\s*=\s*\"root=(\S+)(?:\s*|\s+([^\"]+))\"} ) { ++ $C{GRUB_DEVICE} = $1; ++ $C{GRUB_CMDLINE_LINUX_DEFAULT} = $2 if (defined($2) && $2 !~ m{^\s*$}); ++ last; ++ } ++ } ++ close( IN); ++ $default = $fallback; ++} ++ ++if ( ! exists( $C{GRUB_DEVICE}) && exists( $fsdev{"/"}) ) { ++ my( $dev, $type, $subvol) = ( $fsdev{"/"}, $fstype{"/"}, ""); ++ if ( $dev !~ m{^(UUID=|/dev/disk/by-uuid/)} || ++ $C{GRUB_DISABLE_LINUX_UUID} ne "true" ) { ++ $C{GRUB_DEVICE} = $dev; ++ # grub2-mkrelpath fails on rollback -- and provides no known merit... ++ #chomp( $subvol = qx{grub2-mkrelpath /}) if ( $type eq "btrfs" ); ++ #$subvol =~ s{^/}{}; ++ #$C{GRUB_DEVICE} .= " rootflags=subvol=$subvol" if ($subvol); ++ } ++} ++if ( ! exists( $C{GRUB_DEVICE}) ) { ++ my( $dev, $uuid, $type, $subvol) = ("", "", "", ""); ++ chomp( $dev = qx{grub2-probe --target=device /}); ++ chomp( $uuid = qx{grub2-probe --device $dev --target=fs_uuid}); ++ if ( $dev ) { ++ if ( $uuid && $C{GRUB_DISABLE_LINUX_UUID} ne "true" ) { ++ $C{GRUB_DEVICE} = "UUID=$uuid"; ++ } else { ++ $C{GRUB_DEVICE} = "$dev"; ++ } ++ chomp( $type = qx{stat -f --printf='%T' /}); ++ # grub2-mkrelpath fails on rollback -- and provides no known merit... ++ #chomp( $subvol = qx{grub2-mkrelpath /}) if ( $type eq "btrfs" ); ++ #$subvol =~ s{^/}{}; ++ #$C{GRUB_DEVICE} .= " rootflags=subvol=$subvol" if ($subvol); ++ } ++} ++if ( $C{GRUB_CMDLINE_LINUX_DEFAULT} eq "quiet splash=silent" && ++ -r $sysconfbl) { ++ open( IN, "< $sysconfbl") || die; ++ while ( ) { ++ next if ( m{^\s*#} ); ++ if ( m{^DEFAULT_APPEND=".*"(?:\s*|\s+#.*)$} ) { ++ $C{GRUB_CMDLINE_LINUX_DEFAULT} = $1; ++ } ++ } ++ close( IN); ++} ++ ++if ( ! exists( $C{GRUB_DEVICE})) { ++ Panic( 0, "$C: Default not ready and no fallback. Please retry later!\n"); ++} ++ ++if ( ! exists( $C{GRUB_EMU_CONMODE}) && exists( $C{GRUB_CONMODE}) ) { ++ # GRUB_CONMODE is used for 'grub2-emu' as well ++ $C{GRUB_EMU_CONMODE} = $C{GRUB_CONMODE}; ++} ++if ( exists( $C{GRUB_EMU_CONMODE}) && !exists( $C{GRUB_CONMODE}) ) { ++ # pick up 'conmode=' from CMDLINE ++ my $found = ""; ++ foreach ( "GRUB_CMDLINE_LINUX", "GRUB_CMDLINE_LINUX_DEFAULT" ) { ++ next unless ($C{$_} =~ m{ ?conmode=(\S+) ?}); ++ $C{GRUB_CONMODE} = $1; ++ last; ++ } ++ if ( !exists( $C{GRUB_CONMODE}) && $C{GRUB_EMU_CONMODE} eq "3270" ) { ++ # force GRUB_CONMODE to 3215 for least surprise ++ $C{GRUB_CONMODE}="3215"; ++ } ++} ++if ( exists( $C{GRUB_EMU_CONMODE}) && exists( $C{GRUB_CONMODE})) { ++ # strip "conmode=" from GRUB_CMDLINE{,_LINUX}_DEFAULT ++ foreach ( "GRUB_CMDLINE_LINUX", "GRUB_CMDLINE_LINUX_DEFAULT" ) { ++ $C{$_} =~ s{( ?)conmode=\S+ ?}{$1}g; ++ } ++} ++foreach ("GRUB_EMU_CONMODE", "GRUB_CONMODE") { ++ next unless( exists( $C{$_}) ); ++ $C{$_} = "conmode=" . $C{$_}; ++} ++ ++if ( $debug && $verbose > 2 ) { ++ foreach ( sort( keys( %C)) ) { ++ printf( "%s=\"%s\"\n", $_, $C{$_}); ++ } ++} ++ ++open( IN, "< $in") || ++ Panic( 1, "$C: Failed to open 'zipl.conf' template: $!.\n"); ++while ( ) { ++ Info( 3, "$.. <$_$.. >"); ++ if ( $. == 1 && m{^## This} ) { ++ $_ = "## This file was written by 'grub2-install/$C'\n" . ++ "## filling '$in' as template\n"; ++ } elsif ( $. == 2 && m{^## rpm's} ) { ++ $_ = "## with values from '$default'.\n" . ++ "## In-place modifications will eventually go missing!\n"; ++ } ++ while ( m{\@([^\@\s]+)\@} ) { ++ my $k = $1; ++ my $v; ++ if ( exists( $C{$k}) ) { ++ $v = $C{$k}; ++ } elsif ( exists( $Mandatory{$k}) ) { ++ $v = "$k"; ++ $miss++; ++ } else { ++ $v = ""; ++ } ++ s{\@$k\@}{$v}g; ++ } ++ Info( 2, $_); ++ $cfg .= $_; ++} ++if ( $miss ) { ++ Info( 1, "Partially filled config:\n===\n$cfg===\n"); ++ Panic( 1, "$C: 'zipl.conf' template could not be filled. \n"); ++} ++ ++my $ziplconf = "$zipldir/config"; ++if ( ! $debug ) { ++ open( OUT, "> $ziplconf") || die; ++ print( OUT $cfg) || die; ++ close( OUT); ++} ++ ++# copy out kernel and initrd ++my $defimage = "/boot/image"; ++my $definitrd = "/boot/initrd"; ++my $ziplimage = "$zipldir/image"; ++my $ziplinitrd = "$zipldir/initrd"; ++my $Image = "$defimage"; ++ ++if ( ! $running && ! $force ) { ++ chomp( $running = qx{uname -r}); ++ Info( 1, "preferred kernel: '$running'\n"); ++ $Image .= "-$running"; ++} ++if ( ! -r $Image ) { ++ $Image = $defimage; ++} ++Panic( 1, "$C: kernel '$Image' not readable!?\n") unless (-r $Image); ++ ++if ( -l $Image ) { ++ $Image = readlink( $Image); ++} ++my ($image, $version) = ($Image =~ m{^(?:/boot/)?([^-]+-(.+))$}); ++my $initrd = "initrd-$version"; ++ ++if ( !defined($image) || !defined($version) || ! -r "/boot/$image" ) { ++ Panic( 1, "$C: weird $Image. This should never happen!\n"); ++} ++ ++if ( ! -r $ziplimage || ! -r $ziplinitrd || $refresh ) { ++ BootCopy( $image, $zipldir, "image"); ++ BootCopy( $initrd, $zipldir, "initrd") if (-r "/boot/$initrd"); ++} ++if ( $refresh || ChkInitrd( $zipldir, "initrd") <= 0 ) { ++ MkInitrd( $initrd, $zipldir, $version); ++} ++if ( ChkInitrd( $zipldir, "initrd") == 0 ) { ++ Info( 0, "$C: dracut does not work as expected! Help needed!\n"); ++ $miss++; ++} ++ ++# now: go for it! ++my @C = ( "/sbin/zipl", (($verbose) ? "-Vnc" : "-nc"), "$ziplconf" ); ++System( @C); ++exit( $miss); ++ +--- /dev/null ++++ b/util/s390x/dracut-zipl-refresh.sh.in +@@ -0,0 +1,183 @@ ++#!/bin/bash ++# ex: ts=8 sw=4 sts=4 et filetype=sh syntax=off ++ ++debug=false ++TIMEOUT=300 ++[ -n "$SYSROOT" ] || ++SYSROOT=/sysroot ++[ -d $SYSROOT/boot ] || SYSROOT= ++ ++sync() { $SYSROOT/usr/bin/sync "$@"; } ++readlink() { $SYSROOT/usr/bin/readlink "$@"; } ++newline() { echo ""; } ++verbose() { ++ local a ++ local m ++ [ -n "$*" ] || return 0 ++ m="+" ++ for a in "$@"; do ++ case "$a" in ++ (*" "*|*" "*|"") m="$m '$a'";; ++ (*) m="$m $a";; ++ esac ++ done ++ echo "$m" ++ [ -n "$SYSROOT" -o "$1" = "chroot" ] || return 0 ++ "$@" ++} ++ ++SYSK="$(readlink $SYSROOT/boot/image)" ++SYSK="${SYSK#image-}" ++ZIPK="$(readlink $SYSROOT/boot/zipl/image)" ++ZIPK="${ZIPK#image-}" ++PRVK="$(readlink $SYSROOT/boot/zipl/image.prev 2> /dev/null)" ++PRVK="${PRVK#image-}" ++RUNK="$(uname -r)" ++# if /boot/zipl is not accessible ZIPK will be empty, assume running kernel ++[ -n "$ZIPK" ] || ZIPK="$RUNK" ++ ++[ -n "$SYSROOT" ] || { ++ echo "$0 is not intended for interactive use!" ++ $debug || ++ exit 0 ++ ## test: ++ TIMEOUT=6 ++ RUNK=110; ZIPK=110; PRVK=101; SYSK=194 ++ ##RUNK=$PRVK; ZIPK=$SYSK # previous booted, newest is default ++ ##t=$ZIPK; ZIPK=$PRVK; PRVK=$t; SYSK=$PRVK # unknown booted ++ ##ZIPK=$SYSK; PRVK="" # no update ++ ##ZIPK=$SYSK # try previous ++ echo "R=$RUNK S=$SYSK Z=$ZIPK P=$PRVK" ++ #verbose echo "a b" z ++ #verbose echo "^h ^j" ^z ++} ++ ++trap newline EXIT ++ ++echo -n " ++ Attention: 'grub2' failed to start the target kernel" ++ ++if [ "$ZIPK" != "$RUNK" -a "$RUNK" != "$SYSK" ]; then ++ # i.e. "previous" has been selected via zipl, but it fails!? ++ [ "$RUNK" = "$PRVK" ] && ++ echo " from previous" || ++ echo " from unknown" ++ ++ echo " ZIPL kernel ($RUNK). If default ($ZIPK) ++ fails as well, please contact support." ++ exit 1 ++fi ++echo "." ++if [ "$ZIPK" = "$SYSK" ]; then ++ [ -z "$PRVK" ] && ++ echo " ++ No kernel update readily available/installed. Please contact support." || ++ echo " ++ You may want to try the previous kernel ($PRVK) with ++ 'IPL ... LOADPARM 4', as no update kernel is readily available/installed." ++ exit 1 ++fi ++ ++echo " ++ A newer kernel ($SYSK) is available and will be deployed ++ in $TIMEOUT seconds. To facilitate this, the affected file-systems have ++ to be made writable, then 'grub2-install --force' needs to be run, ++ and, on success, a 'reboot' will be initiated. ++ ++ Press 'c[Enter]' to interrupt, any other input will proceed... " ++ ++trap interrupted=1 INT ++interrupted=0 ++input="" ++read -t $TIMEOUT input ++case "$input" in ++ ([Cc]) interrupted=2 ;; ++esac ++if [ $interrupted != 0 ]; then ++ echo " ++ Automatic update cancelled..." ++ exit 1 ++fi ++trap - INT ++echo " ++ Attempting automatic update..." ++ ++ismounted() { ++ local mode="$1" ++ local tgt="$2" ++ local dev mp fs opts dc ++ while read dev mp fs opts dc; do ++ [ "$mp" = "$tgt" ] || continue ++ case ",$opts," in ++ (*,$mode,*) return 0;; ++ esac ++ done < /proc/mounts ++ return 1 ++} ++ismp() { ++ local sysr="$1" ++ local tgt="$2" ++ local dev mp fs opts dc ++ while read dev mp fs opts dc; do ++ case "$dev" in ++ ("#"*) continue;; ++ esac ++ [ "$mp" = "$tgt" ] || continue ++ return 0 ++ done < $sysr/etc/fstab ++ return 1 ++} ++chroot() { ++ local tgt="$1"; shift ++ if [ -z "$tgt" ]; then ++ echo -n "+" ++ verbose "$@" ++ else ++ /usr/bin/chroot "$tgt" "$@" ++ fi ++} ++cleanup() { ++ local mp ++ echo " # cleanup" ++ for mp in $UMOUNT; do ++ verbose chroot "$SYSROOT" umount $mp ++ done ++ for mp in $WMOUNT; do ++ verbose mount -o remount,ro $mp ++ done ++ sync; sync ++ [ -z "$EXIT" ] || echo "$EXIT" ++ echo "" ++} ++trap cleanup EXIT ++UMOUNT="" ++WMOUNT="" ++EXIT="" ++ ++echo " # prepare" ++# remount $SYSROOT{,/boot{,/zipl}} read-write ++for mp in {"",/boot{,/zipl}}; do ++ [ -n "$SYSROOT$mp" ] || continue ++ if ismounted rw $SYSROOT$mp; then ++ echo " # $mp: already read-write: ignore" ++ elif ismounted ro $SYSROOT$mp; then ++ verbose mount -o remount,rw $SYSROOT$mp ++ WMOUNT="$SYSROOT$mp $WMOUNT" ++ elif ismp "$SYSROOT" $mp; then ++ verbose chroot "$SYSROOT" mount -w $mp || exit 1 ++ UMOUNT="$mp $UMOUNT" ++ fi ++done ++if [ ! -w $SYSROOT/boot/zipl/config ]; then ++ EXIT="ERROR: $SYSROOT/boot/zipl/config not writable! Aborting..." ++ exit 1 ++fi ++echo " # action" ++verbose chroot "$SYSROOT" grub2-zipl-setup --force ++ret=$? ++if [ $ret != 0 ]; then ++ EXIT=" # failed ($ret)" ++else ++ EXIT=" # done" ++fi ++exit $ret diff --git a/grub2-s390x-05-grub2-mkconfig.patch b/grub2-s390x-05-grub2-mkconfig.patch new file mode 100644 index 0000000..328d57d --- /dev/null +++ b/grub2-s390x-05-grub2-mkconfig.patch @@ -0,0 +1,145 @@ +From: Raymund Will +Subject: Enable grub2-mkconfig for s390x-emu +References: fate#314213, bnc#868909 +Patch-Mainline: no + +V2: + * omit subvolume-prefix for platform "emu" +V3: + * add 'conmode=' to command-line if GRUB_CONMODE exists. [bnc#868909] +V4: + * remove 's' from possible hot-keys for "bootable snapshots". [bnc#885668] + +--- + util/grub.d/10_linux.in | 63 ++++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 51 insertions(+), 12 deletions(-) + +--- a/util/grub.d/10_linux.in ++++ b/util/grub.d/10_linux.in +@@ -65,6 +65,10 @@ + LINUX_ROOT_DEVICE=UUID=${GRUB_DEVICE_UUID} + fi + ++if [ "x$GRUB_CONMODE" != "x" ]; then ++ GRUB_CMDLINE_LINUX="conmode=${GRUB_CONMODE} ${GRUB_CMDLINE_LINUX}" ++fi ++ + case x"$GRUB_FS" in + xbtrfs) + rootsubvol="`make_system_path_relative_to_its_root /`" +@@ -81,6 +85,21 @@ + + title_correction_code= + ++hotkey=1 ++incr_hotkey() ++{ ++ [ -z "$hotkey" ] && return ++ expr $hotkey + 1 ++} ++print_hotkey() ++{ ++ keys="123456789abdfgijklmnoprtuvwyz" ++ if [ -z "$hotkey" ]||[ $hotkey -eq 0 ]||[ $hotkey -gt 30 ]; then ++ return ++ fi ++ echo "--hotkey=$(expr substr $keys $hotkey 1)" ++} ++ + linux_entry () + { + os="$1" +@@ -110,9 +129,11 @@ + title_correction_code="${title_correction_code}if [ \"x\$default\" = '$quoted' ]; then default='$(echo "$replacement_title" | grub_quote)'; fi;" + grub_warn "$(gettext_printf "Please don't use old title \`%s' for GRUB_DEFAULT, use \`%s' (for versions before 2.00) or \`%s' (for 2.00 or later)" "$GRUB_ACTUAL_DEFAULT" "$replacement_title" "gnulinux-advanced-$boot_device_id>gnulinux-$version-$type-$boot_device_id")" + fi +- echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/" ++ echo "menuentry '$(echo "$title" | grub_quote)' $(print_hotkey) ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/" ++ hotkey=$(incr_hotkey) + else +- echo "menuentry '$(echo "$os" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/" ++ echo "menuentry '$(echo "$os" | grub_quote)' $(print_hotkey) ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/" ++ hotkey=$(incr_hotkey) + fi + if [ x$type != xrecovery ] ; then + save_default_entry | grub_add_tab +@@ -135,6 +156,7 @@ + + echo " insmod gzio" | sed "s/^/$submenu_indentation/" + ++ if [ $PLATFORM != emu ]; then # 'search' does not work for now + if [ x$dirname = x/ ]; then + if [ -z "${prepare_root_cache}" ]; then + prepare_root_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE} | grub_add_tab)" +@@ -146,6 +168,7 @@ + fi + printf '%s\n' "${prepare_boot_cache}" | sed "s/^/$submenu_indentation/" + fi ++ fi + message="$(gettext_printf "Loading Linux %s ..." ${version})" + sed "s/^/$submenu_indentation/" << EOF + echo '$(echo "$message" | grub_quote)' +@@ -170,17 +193,15 @@ + + machine=`uname -m` + case "x$machine" in +- xi?86 | xx86_64) +- list= +- for i in /boot/vmlinuz-* /vmlinuz-* /boot/kernel-* ; do +- if grub_file_is_not_garbage "$i" ; then list="$list $i" ; fi +- done ;; +- *) +- list= +- for i in /boot/vmlinuz-* /boot/vmlinux-* /vmlinuz-* /vmlinux-* /boot/kernel-* ; do +- if grub_file_is_not_garbage "$i" ; then list="$list $i" ; fi +- done ;; ++ xi?86 | xx86_64) klist="/boot/vmlinuz-* /vmlinuz-* /boot/kernel-*" ;; ++ xs390 | xs390x) klist="/boot/image-* /boot/kernel-*" ;; ++ *) klist="/boot/vmlinuz-* /boot/vmlinux-* /vmlinuz-* /vmlinux-* \ ++ /boot/kernel-*" ;; + esac ++list= ++for i in $klist ; do ++ if grub_file_is_not_garbage "$i" ; then list="$list $i" ; fi ++done + + case "$machine" in + i?86) GENKERNEL_ARCH="x86" ;; +@@ -190,6 +211,15 @@ + *) GENKERNEL_ARCH="$machine" ;; + esac + ++PLATFORM="native" ++if [ -d /sys/firmware/efi ]&&[ "x${GRUB_USE_LINUXEFI}" = "xtrue" ]; then ++ PLATFORM="efi" ++else ++ case "$machine" in ++ s390*) PLATFORM="emu" ;; ++ esac ++fi ++ + prepare_boot_cache= + prepare_root_cache= + boot_device_id= +@@ -216,6 +246,11 @@ + basename=`basename $linux` + dirname=`dirname $linux` + rel_dirname=`make_system_path_relative_to_its_root $dirname` ++ if [ $PLATFORM != "emu" ]; then ++ hotkey=0 ++ else ++ rel_dirname=$dirname ++ fi + version=`echo $basename | sed -e "s,^[^0-9]*-,,g"` + alt_version=`echo $version | sed -e "s,\.old$,,g"` + linux_root_device_thisversion="${LINUX_ROOT_DEVICE}" +@@ -333,7 +368,8 @@ + boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" + fi + # TRANSLATORS: %s is replaced with an OS name +- echo "submenu '$(gettext_printf "Advanced options for %s" "${OS}" | grub_quote)' \$menuentry_id_option 'gnulinux-advanced-$boot_device_id' {" ++ echo "submenu '$(gettext_printf "Advanced options for %s" "${OS}" | grub_quote)' $(print_hotkey) \$menuentry_id_option 'gnulinux-advanced-$boot_device_id' {" ++ hotkey=$(incr_hotkey) + is_top_level=false + fi + diff --git a/grub2-s390x-06-loadparm.patch b/grub2-s390x-06-loadparm.patch new file mode 100644 index 0000000..ae4f973 --- /dev/null +++ b/grub2-s390x-06-loadparm.patch @@ -0,0 +1,45 @@ +From: Raymund Will +Subject: Allow s390x-emu to telecontrolled by LOADPARM +References: bsc#892852, bsc#891946 +Patch-Mainline: no + +--- + util/grub.d/00_header.in | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +--- a/util/grub.d/00_header.in ++++ b/util/grub.d/00_header.in +@@ -54,6 +54,33 @@ + fi + + EOF ++if [ "`uname -m`" = "s390x" ]; then ++ cat <' ++ regexp -s 1:z_1 -s 2:z_2 '^([0-9][0-9>]*)\.([0-9][0-9.]*)$' "\$z_gp" ++ if [ ! "\$z_1" -o ! "\$z_2" ]; then break; fi ++ set z_gp="\$z_1>\$z_2" ++ done ++ if [ ! "\$z_gp" ]; then break; fi ++ set next_entry="\$z_gp" ++ unset z_gp ++ unset loadparm ++ break ++done ++EOF ++fi + if [ "x$GRUB_BUTTON_CMOS_ADDRESS" != "x" ]; then + cat < 0); +- $msg .= "Usage: $C [-v] [-d] [-f] [-T template] [-z ZIPLDIR]\n"; ++ $msg .= "Usage: $C [-v] [-d] [-f] [-T template] [-z ZIPLDIR] [-i imagepath]\n"; + Panic( $_[0], $msg . "\n"); + } + +@@ -183,6 +186,7 @@ + (/^--?help/ || /^-h/) && (Usage(0)); + (/^--zipldir$/ || /^-z$/) && ($zipldir = shift || Usage(2), next); + (/^--template$/ || /^-T$/) && ($in = shift || Usage(3), next); ++ (/^--image$/ || /^-i$/) && ($Image = shift || Usage(5), $force = 1, next); + (/^-/) && (Usage(1)); + Usage(1); + } +@@ -378,11 +382,8 @@ + } + + # copy out kernel and initrd +-my $defimage = "/boot/image"; +-my $definitrd = "/boot/initrd"; + my $ziplimage = "$zipldir/image"; + my $ziplinitrd = "$zipldir/initrd"; +-my $Image = "$defimage"; + + if ( ! $running && ! $force ) { + chomp( $running = qx{uname -r}); diff --git a/grub2-s390x-08-workaround-part-to-disk.patch b/grub2-s390x-08-workaround-part-to-disk.patch new file mode 100644 index 0000000..29d1ac1 --- /dev/null +++ b/grub2-s390x-08-workaround-part-to-disk.patch @@ -0,0 +1,13 @@ +--- a/grub-core/osdep/linux/getroot.c ++++ b/grub-core/osdep/linux/getroot.c +@@ -740,6 +740,10 @@ + if (! realpath (os_dev, path)) + return NULL; + ++#ifdef __s390x__ ++ return path; ++#endif ++ + if (strncmp ("/dev/", path, 5) == 0) + { + char *p = path + 5; diff --git a/grub2-s390x-09-improve-zipl-setup.patch b/grub2-s390x-09-improve-zipl-setup.patch new file mode 100644 index 0000000..c664831 --- /dev/null +++ b/grub2-s390x-09-improve-zipl-setup.patch @@ -0,0 +1,209 @@ +--- + util/s390x/zipl2grub.conf.in | 30 +++++++++++++++++++- + util/s390x/zipl2grub.pl.in | 64 +++++++++++++++++++++++++++++-------------- + 2 files changed, 73 insertions(+), 21 deletions(-) + +--- a/util/s390x/zipl2grub.conf.in ++++ b/util/s390x/zipl2grub.conf.in +@@ -10,17 +10,45 @@ defaultmenu = menu + image = @zipldir@/image + parameters = "root=@GRUB_DEVICE@ @GRUB_EMU_CONMODE@ @GRUB_CMDLINE_LINUX@ @GRUB_CMDLINE_LINUX_DEFAULT@ initgrub quiet splash=silent plymouth.enable=0 " + ++[grub2-mem1G] ++ target = @zipldir@ ++ image = @zipldir@/image ++ ramdisk = @zipldir@/initrd,0x2000000 ++ parameters = "root=@GRUB_DEVICE@ @GRUB_EMU_CONMODE@ @GRUB_CMDLINE_LINUX@ @GRUB_CMDLINE_LINUX_DEFAULT@ initgrub quiet splash=silent plymouth.enable=0 mem=1G " ++ + [skip-grub2] + target = @zipldir@ + ramdisk = @zipldir@/initrd,0x2000000 + image = @zipldir@/image + parameters = "root=@GRUB_DEVICE@ @GRUB_CONMODE@ @GRUB_CMDLINE_LINUX@ @GRUB_CMDLINE_LINUX_DEFAULT@ " ++#@ ++#@[grub2-previous] ++#@ target = @zipldir@ ++#@ image = @zipldir@/image.prev ++#@ ramdisk = @zipldir@/initrd.prev,0x2000000 ++#@ parameters = "root=@GRUB_DEVICE@ @GRUB_EMU_CONMODE@ @GRUB_CMDLINE_LINUX@ @GRUB_CMDLINE_LINUX_DEFAULT@ initgrub quiet splash=silent plymouth.enable=0 " ++#@ ++#@[grub2-mem1G-previous] ++#@ target = @zipldir@ ++#@ image = @zipldir@/image.prev ++#@ ramdisk = @zipldir@/initrd.prev,0x2000000 ++#@ parameters = "root=@GRUB_DEVICE@ @GRUB_EMU_CONMODE@ @GRUB_CMDLINE_LINUX@ @GRUB_CMDLINE_LINUX_DEFAULT@ initgrub quiet splash=silent plymouth.enable=0 mem=1G " ++#@ ++#@[skip-grub2-previous] ++#@ target = @zipldir@ ++#@ image = @zipldir@/image.prev ++#@ ramdisk = @zipldir@/initrd.prev,0x2000000 ++#@ parameters = "root=@GRUB_DEVICE@ @GRUB_CONMODE@ @GRUB_CMDLINE_LINUX@ @GRUB_CMDLINE_LINUX_DEFAULT@ " + + :menu + target = @zipldir@ +- timeout = 16 ++ timeout = 60 + default = 1 + prompt = 0 + 1 = grub2 + 2 = skip-grub2 ++ 3 = grub2-mem1G ++#@ 4 = grub2-previous ++#@ 5 = skip-grub2-previous ++#@ 6 = grub2-mem1G-previous + +--- a/util/s390x/zipl2grub.pl.in ++++ b/util/s390x/zipl2grub.pl.in +@@ -10,6 +10,7 @@ my $sysconfbl = '@sysconfdir@/sysconfig/ + my $defimage = "/boot/image"; + my $definitrd = "/boot/initrd"; + my $Image = "$defimage"; ++my $previous = ".prev"; + my $zipldir = ""; + my $running = ""; + my $refresh = 1; # needs to default to "on" until most bugs are shaken out! +@@ -44,12 +45,12 @@ sub System(@) { + return 0 if ($debug); + system( @C); + if ($? == -1) { +- Panic( $?, "$C[0]: Failed to execute: $!\n"); ++ Panic( 1, "$C[0]: Failed to execute: $!\n"); + } elsif ($? & 127) { +- Panic( $?, sprintf( "$C[0]: Died with signal %d with%s coredump\n", ++ Panic( 1, sprintf( "$C[0]: Died with signal %d with%s coredump\n", + ($? & 127), ($? & 128) ? '' : 'out')); + } elsif ( $? >> 8 != 0 ) { +- Panic( $?, "$C[0]: Failed\n"); ++ Panic( $? >> 8, "$C[0]: Failed\n"); + } + return( 0); + } +@@ -74,11 +75,13 @@ sub ln($$) { + unlink( $_[1]) || Panic( 1, "$C: unlink: $!.\n") if ( -e $_[1]); + symlink($_[0], $_[1]) || Panic( 1, "$C: symlink: $!.\n"); + } +-sub BootCopy($$$) { ++ ++sub ManagePrev($$$){ + my( $file, $dir, $tgt) = @_; + my $curr = "$dir/$tgt"; +- my $prev = "$dir/$tgt.prev"; +- Info(4, "Copy /boot/$file $dir $tgt\n"); ++ my $prev = "$dir/$tgt$previous"; ++ my $ret = 0; ++ Info(2, "Manage $prev\n"); + if ( -l $curr ) { + my $curf = readlink( $curr); + if ( $curf ne $file ) { +@@ -88,7 +91,21 @@ sub BootCopy($$$) { + rm( $pref); + } + mv( $curr, $prev); ++ $ret = 1; ++ } else { ++ Info(2, " nothing to do ($curr -> $file).\n"); + } ++ } else { ++ Info(2, " nothing to do ($curr no sym-link).\n"); ++ } ++ return $ret; ++} ++sub BootCopy($$$) { ++ my( $file, $dir, $tgt) = @_; ++ my $curr = "$dir/$tgt"; ++ Info(4, "Copy /boot/$file $dir $tgt\n"); ++ if ( $tgt eq "image" && ManagePrev( $file, $dir, $tgt)) { ++ ManagePrev( $file, $dir, "initrd") + } + cp( "/boot/$file", "$dir/$file"); + ln( $file, $curr); +@@ -163,7 +180,9 @@ sub Usage($) { + "zIPL directory missing.", + "Configuration template missing.", + "Configuration template unreadable.", +- "zIPL directory not accesible.", ++ "zIPL directory not accessible.", ++ "kernel image parameter missing.", ++ "kernel image unreadable.", + "" + ); + my $msg = ""; +@@ -186,7 +205,8 @@ while ( $#ARGV >= 0 ) { + (/^--?help/ || /^-h/) && (Usage(0)); + (/^--zipldir$/ || /^-z$/) && ($zipldir = shift || Usage(2), next); + (/^--template$/ || /^-T$/) && ($in = shift || Usage(3), next); +- (/^--image$/ || /^-i$/) && ($Image = shift || Usage(5), $force = 1, next); ++ (/^--image$/ || /^-i$/) && ($Image = shift || Usage(6), ++ -r "$Image" || Usage(7), $force = 1, next); + (/^-/) && (Usage(1)); + Usage(1); + } +@@ -345,7 +365,7 @@ if ( $debug && $verbose > 2 ) { + open( IN, "< $in") || + Panic( 1, "$C: Failed to open 'zipl.conf' template: $!.\n"); + while ( ) { +- Info( 3, "$.. <$_$.. >"); ++ Info( 4, "$.. <$_$.. >"); + if ( $. == 1 && m{^## This} ) { + $_ = "## This file was written by 'grub2-install/$C'\n" . + "## filling '$in' as template\n"; +@@ -366,7 +386,7 @@ while ( ) { + } + s{\@$k\@}{$v}g; + } +- Info( 2, $_); ++ Info( 3, $_); + $cfg .= $_; + } + if ( $miss ) { +@@ -374,13 +394,6 @@ if ( $miss ) { + Panic( 1, "$C: 'zipl.conf' template could not be filled. \n"); + } + +-my $ziplconf = "$zipldir/config"; +-if ( ! $debug ) { +- open( OUT, "> $ziplconf") || die; +- print( OUT $cfg) || die; +- close( OUT); +-} +- + # copy out kernel and initrd + my $ziplimage = "$zipldir/image"; + my $ziplinitrd = "$zipldir/initrd"; +@@ -399,15 +412,15 @@ if ( -l $Image ) { + $Image = readlink( $Image); + } + my ($image, $version) = ($Image =~ m{^(?:/boot/)?([^-]+-(.+))$}); +-my $initrd = "initrd-$version"; +- + if ( !defined($image) || !defined($version) || ! -r "/boot/$image" ) { + Panic( 1, "$C: weird $Image. This should never happen!\n"); + } ++my $initrd = "initrd-$version"; + + if ( ! -r $ziplimage || ! -r $ziplinitrd || $refresh ) { + BootCopy( $image, $zipldir, "image"); +- BootCopy( $initrd, $zipldir, "initrd") if (-r "/boot/$initrd"); ++ BootCopy( $initrd, $zipldir, "initrd") ++ if (-r "/boot/$initrd" && ! exists( $fsdev{"/boot"})); + } + if ( $refresh || ChkInitrd( $zipldir, "initrd") <= 0 ) { + MkInitrd( $initrd, $zipldir, $version); +@@ -417,6 +430,17 @@ if ( ChkInitrd( $zipldir, "initrd") == 0 + $miss++; + } + ++# write zipl config file ++my $ziplconf = "$zipldir/config"; ++$cfg =~ s{#@}{}g if ( -r "$ziplimage$previous" && -r "$ziplinitrd$previous" ); ++if ( ! $debug ) { ++ open( OUT, "> $ziplconf") || die; ++ print( OUT $cfg) || die; ++ close( OUT); ++} else { ++ print( STDERR $cfg); ++} ++ + # now: go for it! + my @C = ( "/sbin/zipl", (($verbose) ? "-Vnc" : "-nc"), "$ziplconf" ); + System( @C); diff --git a/grub2-s390x-11-secureboot.patch b/grub2-s390x-11-secureboot.patch new file mode 100644 index 0000000..0d8f25a --- /dev/null +++ b/grub2-s390x-11-secureboot.patch @@ -0,0 +1,133 @@ +--- + grub-core/loader/emu/linux.c | 4 ++-- + util/s390x/dracut-grub2.sh.in | 14 ++++++++++++-- + util/s390x/zipl2grub.conf.in | 1 + + util/s390x/zipl2grub.pl.in | 31 ++++++++++++++++++++++--------- + 4 files changed, 37 insertions(+), 13 deletions(-) + +--- a/util/s390x/dracut-grub2.sh.in ++++ b/util/s390x/dracut-grub2.sh.in +@@ -18,6 +18,9 @@ + done < /proc/mounts + echo $rofs + } ++ checkcat() { ++ [ -r $1 ] && cat $1 ++ } + checkd() { + [ -d $1 ] && echo true || echo false + } +@@ -76,6 +79,7 @@ + export grub2bootw=$(checksubvol /boot/writable) + export grub2devfs=$(checkd /sysroot/dev/disk) + export grub2snap=$(checksnap) ++ export grub2secure=$(checkcat /sys/firmware/ipl/secure) + debug "" export -p + + _ctty="$(RD_DEBUG= getarg rd.ctty=)" && _ctty="/dev/${_ctty##*/}" +@@ -107,7 +111,7 @@ + debug "Trying grub2-emu (ro=$grub2rofs, TERM=$TERM, ctty=$_ctty)..." + setsid $CTTY -- chroot /sysroot $bindir/grub2-emu -X -X 0<>$_ctty 1>&0 2>&0 + +- if [ -x /sysroot@libdir@/grub2/zipl-refresh ]; then ++ if [ "$grub2secure" != 1 ]&&[ -x /sysroot@libdir@/grub2/zipl-refresh ]; then + setsid $CTTY -- /sysroot@libdir@/grub2/zipl-refresh 0<>$_ctty 1>&0 2>&0 + if [ $? != 0 ]; then + warn "Not continuing" +@@ -117,12 +121,18 @@ + sleep 3 + reboot + fi +- else ++ elif [ "$grub2secure" != 1 ]; then + echo " + Attention: 'grub2' failed to start the target kernel and 'zipl-refresh' + is not available. This should never happen. Please contact support." >& $_ctty + warn "Not continuing" + emergency_shell -n grub2-emu-kexec ++ else ++ echo " ++ Attention: 'grub2' failed to start the target kernel and secure boot seems ++ active. Automatic recovery not available. Please contact support." >& $_ctty ++ warn "Not continuing" ++ emergency_shell -n grub2-emu-kexec + fi + + $grub2snap || umount /sysroot/.snapshots +--- a/util/s390x/zipl2grub.conf.in ++++ b/util/s390x/zipl2grub.conf.in +@@ -45,6 +45,7 @@ + timeout = 60 + default = 1 + prompt = 0 ++ secure = @SUSE_SECURE_BOOT@ + 1 = grub2 + 2 = skip-grub2 + 3 = grub2-mem1G +--- a/util/s390x/zipl2grub.pl.in ++++ b/util/s390x/zipl2grub.pl.in +@@ -21,6 +21,7 @@ + my $cfg = ""; + my %fsdev = (); + my %fstype = (); ++my %SBL = (); # key/value of $sysconfbl + + my %C = ( + GRUB_CMDLINE_LINUX_DEFAULT => "quiet splash=silent", +@@ -251,6 +252,15 @@ + } + close( IN); + } ++if ( -r $sysconfbl ) { ++ open( IN, "< $sysconfbl") || die; ++ while ( ) { ++ next if ( m{^\s*#} ); ++ next unless ( m{^\s*([^=#\s]+)="(.*)"(?:\s*|\s+#.*)$} ); ++ $SBL{$1} = $2; ++ } ++ close( IN); ++} + if ( -r "/etc/fstab" ) { + my $regex = qr{^(\S+)\s+(\S+)\s+(\S+)\s+\S+\s+\S+\s+\S+\s*(?:#.*)?$}; + open( IN, "< /etc/fstab") || die; +@@ -313,21 +323,21 @@ + } + } + if ( $C{GRUB_CMDLINE_LINUX_DEFAULT} eq "quiet splash=silent" && +- -r $sysconfbl) { +- open( IN, "< $sysconfbl") || die; +- while ( ) { +- next if ( m{^\s*#} ); +- if ( m{^DEFAULT_APPEND=".*"(?:\s*|\s+#.*)$} ) { +- $C{GRUB_CMDLINE_LINUX_DEFAULT} = $1; +- } +- } +- close( IN); ++ exists( $SBL{DEFAULT_APPEND}) ) { ++ $C{GRUB_CMDLINE_LINUX_DEFAULT} = $SBL{DEFAULT_APPEND}; + } + + if ( ! exists( $C{GRUB_DEVICE})) { + Panic( 0, "$C: Default not ready and no fallback. Please retry later!\n"); + } + ++if ( !exists( $C{SUSE_SECURE_BOOT}) ) { ++ $C{SUSE_SECURE_BOOT} = "0"; ++ if ( exists( $SBL{SECURE_BOOT}) && $SBL{SECURE_BOOT} =~ m{^(yes|true|1)$} ) { ++ $C{SUSE_SECURE_BOOT} = "1"; ++ } ++} ++ + if ( ! exists( $C{GRUB_EMU_CONMODE}) && exists( $C{GRUB_CONMODE}) ) { + # GRUB_CONMODE is used for 'grub2-emu' as well + $C{GRUB_EMU_CONMODE} = $C{GRUB_CONMODE}; +@@ -360,6 +370,9 @@ + foreach ( sort( keys( %C)) ) { + printf( "%s=\"%s\"\n", $_, $C{$_}); + } ++ foreach ( sort( keys( %SBL)) ) { ++ printf( "SBL: %s=\"%s\"\n", $_, $SBL{$_}); ++ } + } + + open( IN, "< $in") || diff --git a/grub2-s390x-12-zipl-setup-usrmerge.patch b/grub2-s390x-12-zipl-setup-usrmerge.patch new file mode 100644 index 0000000..db4d76f --- /dev/null +++ b/grub2-s390x-12-zipl-setup-usrmerge.patch @@ -0,0 +1,84 @@ +--- + util/s390x/zipl2grub.pl.in | 41 ++++++++++++++++++++++++++++------------- + 1 file changed, 28 insertions(+), 13 deletions(-) + +Index: grub-2.06/util/s390x/zipl2grub.pl.in +=================================================================== +--- grub-2.06.orig/util/s390x/zipl2grub.pl.in ++++ grub-2.06/util/s390x/zipl2grub.pl.in +@@ -101,20 +101,22 @@ sub ManagePrev($$$){ + } + return $ret; + } +-sub BootCopy($$$) { +- my( $file, $dir, $tgt) = @_; ++sub BootCopy($$$$) { ++ my( $src, $file, $dir, $tgt) = @_; + my $curr = "$dir/$tgt"; +- Info(4, "Copy /boot/$file $dir $tgt\n"); ++ $src = "/boot/$src" unless ( -r $src ); ++ Info(4, "Copy $src $dir $tgt\n"); + if ( $tgt eq "image" && ManagePrev( $file, $dir, $tgt)) { + ManagePrev( $file, $dir, "initrd") + } +- cp( "/boot/$file", "$dir/$file"); ++ cp( $src, "$dir/$file"); + ln( $file, $curr); + } + sub MkInitrd($$$) { + my( $initrd, $dir, $version) = @_; + my @C = ( "dracut", "--hostonly", "--force"); + my $uuid; ++ push @C, "--quiet" unless ($verbose > 1); + if ( exists( $fsdev{"/boot"}) ) { + chomp( $uuid = qx{grub2-probe --target=fs_uuid /boot}); + my ($dev, $type) = ($fsdev{"/boot"}, $fstype{"/boot"}); +@@ -429,18 +431,31 @@ if ( ! -r $Image ) { + } + Panic( 1, "$C: kernel '$Image' not readable!?\n") unless (-r $Image); + +-if ( -l $Image ) { +- $Image = readlink( $Image); +-} +-my ($image, $version) = ($Image =~ m{^(?:/boot/)?([^-]+-(.+))$}); +-if ( !defined($image) || !defined($version) || ! -r "/boot/$image" ) { +- Panic( 1, "$C: weird $Image. This should never happen!\n"); ++my ($image, $version) = ($Image, undef); ++while ( !defined( $version) ) { ++ my ($i, $vr, $f) = ($image =~ m{^(?:/boot/)?([^-/]+)-([^/]+)-([^-/]+)$}); ++ Info( 4, "image='$image': "); ++ if ( defined($i) && defined($vr) && defined( $f) && -r "/boot/$i-$vr-$f" ) { ++ Info( 4, "matches pattern ('$vr'-'$f')\n"); ++ $version = "$vr-$f"; ++ last; ++ } ++ if ( -l $image ) { ++ Info( 4, "readlink...\n"); ++ $image = readlink( $image); ++ next; ++ } ++ Info( 4, "last resort: get_kernel_version from original '$Image'...\n"); ++ chomp( $version = qx{get_kernel_version $Image}); ++ Panic( 1, "$C: failed to get kernel version for '$Image'!\n") ++ unless ( defined( $version) && $version ); + } + my $initrd = "initrd-$version"; ++$image = "image-$version"; + + if ( ! -r $ziplimage || ! -r $ziplinitrd || $refresh ) { +- BootCopy( $image, $zipldir, "image"); +- BootCopy( $initrd, $zipldir, "initrd") ++ BootCopy( $Image, $image, $zipldir, "image"); ++ BootCopy( $initrd, $initrd, $zipldir, "initrd") + if (-r "/boot/$initrd" && ! exists( $fsdev{"/boot"})); + } + if ( $refresh || ChkInitrd( $zipldir, "initrd") <= 0 ) { +@@ -463,7 +478,7 @@ if ( ! $debug ) { + } + + # now: go for it! +-my @C = ( "/sbin/zipl", (($verbose) ? "-Vnc" : "-nc"), "$ziplconf" ); ++my @C = ( "/sbin/zipl", (($verbose > 1) ? "-Vnc" : "-nc"), "$ziplconf" ); + System( @C); + exit( $miss); + diff --git a/grub2-s390x-skip-zfcpdump-image.patch b/grub2-s390x-skip-zfcpdump-image.patch new file mode 100644 index 0000000..1295e8f --- /dev/null +++ b/grub2-s390x-skip-zfcpdump-image.patch @@ -0,0 +1,15 @@ +--- a/util/grub-mkconfig_lib.in ++++ b/util/grub-mkconfig_lib.in +@@ -193,6 +193,12 @@ + *.rpmsave|*.rpmnew) return 1 ;; + README*|*/README*) return 1 ;; # documentation + *.sig) return 1 ;; # signatures ++ # Skip zfcpdump kernel from the grub boot menu (bsc#1166513) The zfcpdump ++ # kernel image is used by zipl to prepare a SCSI dump disc and is only ++ # intended to boot from that disk for creating kernel crash dumps, ++ # therefore booting it from grub is not making sense and also will result ++ # in unbootable system. ++ *-zfcpdump) return 1 ;; # s390 zfcpdump image + esac + else + return 1 diff --git a/grub2-secureboot-add-linuxefi.patch b/grub2-secureboot-add-linuxefi.patch new file mode 100644 index 0000000..fe389d3 --- /dev/null +++ b/grub2-secureboot-add-linuxefi.patch @@ -0,0 +1,450 @@ +From: Matthew Garrett +Date: 2012-07-10 11:58:52 EDT +Subject: [PATCH] Add support for linuxefi + +References: fate#314485 +Patch-Mainline: no + +Signed-off-by: Michael Chang + +v2: Adjust patch according to new upstream commits +4d4a8c96e verifiers: Add possibility to verify kernel and modules command lines +ca0a4f689 verifiers: File type for fine-grained signature-verification controlling +7d36709d5 i386: make struct linux_kernel_header architecture specific +4bc909bf8 Remove grub_efi_allocate_pages. +v3: +The upstream commit + +df84d6e94 efi: Print error messages to grub_efi_allocate_pages_real() + +adds grub_error() to set error message and return grub_errno. We have to +unset the grub_errno if we want to ignore the error and proceed, or +the inadvertently provoked error handler would lead to unspecified +consequence. + +--- + grub-core/Makefile.core.def | 8 + + grub-core/kern/efi/mm.c | 32 ++++ + grub-core/loader/i386/efi/linux.c | 371 +++++++++++++++++++++++++++++++++++++ + include/grub/efi/efi.h | 3 + + include/grub/i386/linux.h | 1 + + 5 files changed, 415 insertions(+), 0 deletions(-) + create mode 100644 grub-core/loader/i386/efi/linux.c + +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -1920,6 +1920,13 @@ + }; + + module = { ++ name = linuxefi; ++ efi = loader/i386/efi/linux.c; ++ enable = i386_efi; ++ enable = x86_64_efi; ++}; ++ ++module = { + name = chain; + efi = loader/efi/chainloader.c; + i386_pc = loader/i386/pc/chainloader.c; +--- a/grub-core/kern/efi/mm.c ++++ b/grub-core/kern/efi/mm.c +@@ -112,6 +112,38 @@ + } + } + ++/* Allocate pages below a specified address */ ++void * ++grub_efi_allocate_pages_max (grub_efi_physical_address_t max, ++ grub_efi_uintn_t pages) ++{ ++ grub_efi_status_t status; ++ grub_efi_boot_services_t *b; ++ grub_efi_physical_address_t address = max; ++ ++ if (max > 0xffffffff) ++ return 0; ++ ++ b = grub_efi_system_table->boot_services; ++ status = b->allocate_pages (GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return 0; ++ ++ if (address == 0) ++ { ++ /* Uggh, the address 0 was allocated... This is too annoying, ++ so reallocate another one. */ ++ address = max; ++ status = b->allocate_pages (GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address); ++ grub_efi_free_pages (0, pages); ++ if (status != GRUB_EFI_SUCCESS) ++ return 0; ++ } ++ ++ return (void *) ((grub_addr_t) address); ++} ++ + /* Allocate pages. Return the pointer to the first of allocated pages. */ + void * + grub_efi_allocate_pages_real (grub_efi_physical_address_t address, +--- /dev/null ++++ b/grub-core/loader/i386/efi/linux.c +@@ -0,0 +1,345 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2012 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++static grub_dl_t my_mod; ++static int loaded; ++static void *kernel_mem; ++static grub_uint64_t kernel_size; ++static grub_uint8_t *initrd_mem; ++static grub_uint32_t handover_offset; ++struct linux_kernel_params *params; ++static char *linux_cmdline; ++ ++#define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) ++ ++typedef void(*handover_func)(void *, grub_efi_system_table_t *, struct linux_kernel_params *); ++ ++static grub_err_t ++grub_linuxefi_boot (void) ++{ ++ handover_func hf; ++ int offset = 0; ++ ++#ifdef __x86_64__ ++ offset = 512; ++#endif ++ ++ hf = (handover_func)((char *)kernel_mem + handover_offset + offset); ++ ++ asm volatile ("cli"); ++ ++ hf (grub_efi_image_handle, grub_efi_system_table, params); ++ ++ /* Not reached */ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_linuxefi_unload (void) ++{ ++ grub_dl_unref (my_mod); ++ loaded = 0; ++ if (initrd_mem) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)initrd_mem, BYTES_TO_PAGES(params->ramdisk_size)); ++ if (linux_cmdline) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)linux_cmdline, BYTES_TO_PAGES(params->cmdline_size + 1)); ++ if (kernel_mem) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); ++ if (params) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)params, BYTES_TO_PAGES(16384)); ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), ++ int argc, char *argv[]) ++{ ++ grub_file_t *files = 0; ++ int i, nfiles = 0; ++ grub_size_t size = 0; ++ grub_uint8_t *ptr; ++ ++ if (argc == 0) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); ++ goto fail; ++ } ++ ++ if (!loaded) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); ++ goto fail; ++ } ++ ++ files = grub_zalloc (argc * sizeof (files[0])); ++ if (!files) ++ goto fail; ++ ++ for (i = 0; i < argc; i++) ++ { ++ files[i] = grub_file_open (argv[i], GRUB_FILE_TYPE_LINUX_INITRD ++ | GRUB_FILE_TYPE_NO_DECOMPRESS); ++ if (! files[i]) ++ goto fail; ++ nfiles++; ++ size += ALIGN_UP (grub_file_size (files[i]), 4); ++ } ++ ++ initrd_mem = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(size)); ++ ++ if (!initrd_mem) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd")); ++ goto fail; ++ } ++ ++ params->ramdisk_size = size; ++ params->ramdisk_image = (grub_uint32_t)(grub_addr_t) initrd_mem; ++ ++ ptr = initrd_mem; ++ ++ for (i = 0; i < nfiles; i++) ++ { ++ grub_ssize_t cursize = grub_file_size (files[i]); ++ if (grub_file_read (files[i], ptr, cursize) != cursize) ++ { ++ if (!grub_errno) ++ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), ++ argv[i]); ++ goto fail; ++ } ++ ptr += cursize; ++ grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); ++ ptr += ALIGN_UP_OVERHEAD (cursize, 4); ++ } ++ ++ params->ramdisk_size = size; ++ ++ fail: ++ for (i = 0; i < nfiles; i++) ++ grub_file_close (files[i]); ++ grub_free (files); ++ ++ if (initrd_mem && grub_errno) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)initrd_mem, BYTES_TO_PAGES(size)); ++ ++ return grub_errno; ++} ++ ++static grub_err_t ++grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), ++ int argc, char *argv[]) ++{ ++ grub_file_t file = 0; ++ struct linux_i386_kernel_header lh; ++ grub_ssize_t len, start, filelen; ++ void *kernel; ++ grub_err_t err; ++ ++ grub_dl_ref (my_mod); ++ ++ if (argc == 0) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); ++ goto fail; ++ } ++ ++ file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL); ++ if (! file) ++ goto fail; ++ ++ filelen = grub_file_size (file); ++ ++ kernel = grub_malloc(filelen); ++ ++ if (!kernel) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); ++ goto fail; ++ } ++ ++ if (grub_file_read (file, kernel, filelen) != filelen) ++ { ++ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), argv[0]); ++ goto fail; ++ } ++ ++ grub_file_seek (file, 0); ++ ++ grub_free(kernel); ++ ++ params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384)); ++ ++ if (! params) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); ++ goto fail; ++ } ++ ++ grub_memset (params, 0, 16384); ++ ++ if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) ++ { ++ if (!grub_errno) ++ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), ++ argv[0]); ++ goto fail; ++ } ++ ++ if (lh.boot_flag != grub_cpu_to_le16 (0xaa55)) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number")); ++ goto fail; ++ } ++ ++ if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors")); ++ goto fail; ++ } ++ ++ if (lh.version < grub_cpu_to_le16 (0x020b)) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("kernel too old")); ++ goto fail; ++ } ++ ++ if (!lh.handover_offset) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover")); ++ goto fail; ++ } ++ ++ linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff, ++ BYTES_TO_PAGES(lh.cmdline_size + 1)); ++ ++ if (!linux_cmdline) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline")); ++ goto fail; ++ } ++ ++ grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); ++ err = grub_create_loader_cmdline (argc, argv, ++ linux_cmdline + sizeof (LINUX_IMAGE) - 1, ++ lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1), ++ GRUB_VERIFY_KERNEL_CMDLINE); ++ if (err) ++ goto fail; ++ ++ lh.cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline; ++ ++ handover_offset = lh.handover_offset; ++ ++ start = (lh.setup_sects + 1) * 512; ++ len = grub_file_size(file) - start; ++ ++ kernel_mem = grub_efi_allocate_fixed (lh.pref_address, ++ BYTES_TO_PAGES(lh.init_size)); ++ ++ if (!kernel_mem) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ kernel_mem = grub_efi_allocate_pages_max(0x3fffffff, ++ BYTES_TO_PAGES(lh.init_size)); ++ } ++ ++ if (!kernel_mem) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel")); ++ goto fail; ++ } ++ ++ if (grub_file_seek (file, start) == (grub_off_t) -1) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), ++ argv[0]); ++ goto fail; ++ } ++ ++ if (grub_file_read (file, kernel_mem, len) != len && !grub_errno) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), ++ argv[0]); ++ } ++ ++ if (grub_errno == GRUB_ERR_NONE) ++ { ++ grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); ++ loaded = 1; ++ lh.code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem; ++ } ++ ++ grub_memcpy(params, &lh, 2 * 512); ++ ++ params->type_of_loader = 0x21; ++ ++ fail: ++ ++ if (file) ++ grub_file_close (file); ++ ++ if (grub_errno != GRUB_ERR_NONE) ++ { ++ grub_dl_unref (my_mod); ++ loaded = 0; ++ } ++ ++ if (linux_cmdline && !loaded) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)linux_cmdline, BYTES_TO_PAGES(lh.cmdline_size + 1)); ++ ++ if (kernel_mem && !loaded) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); ++ ++ if (params && !loaded) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)params, BYTES_TO_PAGES(16384)); ++ ++ return grub_errno; ++} ++ ++static grub_command_t cmd_linux, cmd_initrd; ++ ++GRUB_MOD_INIT(linuxefi) ++{ ++ cmd_linux = ++ grub_register_command ("linuxefi", grub_cmd_linux, ++ 0, N_("Load Linux.")); ++ cmd_initrd = ++ grub_register_command ("initrdefi", grub_cmd_initrd, ++ 0, N_("Load initrd.")); ++ my_mod = mod; ++} ++ ++GRUB_MOD_FINI(linuxefi) ++{ ++ grub_unregister_command (cmd_linux); ++ grub_unregister_command (cmd_initrd); ++} +--- a/include/grub/efi/efi.h ++++ b/include/grub/efi/efi.h +@@ -61,6 +61,9 @@ + grub_efi_uintn_t pages); + void * + EXPORT_FUNC(grub_efi_allocate_any_pages) (grub_efi_uintn_t pages); ++void * ++EXPORT_FUNC(grub_efi_allocate_pages_max) (grub_efi_physical_address_t max, ++ grub_efi_uintn_t pages); + void EXPORT_FUNC(grub_efi_free_pages) (grub_efi_physical_address_t address, + grub_efi_uintn_t pages); + grub_efi_uintn_t EXPORT_FUNC(grub_efi_find_mmap_size) (void); diff --git a/grub2-secureboot-chainloader.patch b/grub2-secureboot-chainloader.patch new file mode 100644 index 0000000..8f24a65 --- /dev/null +++ b/grub2-secureboot-chainloader.patch @@ -0,0 +1,596 @@ +From 06ff1079788fedac5e3f1f12ed7bbe69228a7ae0 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Tue, 18 Dec 2012 16:54:03 +0800 +Subject: [PATCH] Add secureboot support on efi chainloader + +References: fate#314485 +Patch-Mainline: no + +Expand the chainloader to be able to verify the image by means of shim +lock protocol. The PE/COFF image is loaded and relocated by the +chainloader instead of calling LoadImage and StartImage UEFI boot +Service as they require positive verification result from keys enrolled +in KEK or DB. The shim will use MOK in addition to firmware enrolled +keys to verify the image. + +The chainloader module could be used to load other UEFI bootloaders, +such as xen.efi, and could be signed by any of MOK, KEK or DB. + +v1: +Use grub_efi_get_secureboot to get secure boot status + +Signed-off-by: Michael Chang +--- + grub-core/loader/efi/chainloader.c | 538 +++++++++++++++++++++++++++++++++-- + 1 files changed, 507 insertions(+), 31 deletions(-) + +--- a/grub-core/loader/efi/chainloader.c ++++ b/grub-core/loader/efi/chainloader.c +@@ -41,10 +41,24 @@ + #include + #endif + ++#ifdef __x86_64__ ++#define SUPPORT_SECURE_BOOT ++#endif ++ ++#ifdef SUPPORT_SECURE_BOOT ++#include ++#include ++#endif ++ + GRUB_MOD_LICENSE ("GPLv3+"); + + static grub_dl_t my_mod; + ++#ifdef SUPPORT_SECURE_BOOT ++static grub_efi_boolean_t debug_secureboot = 0; ++static grub_efi_status_t (__grub_efi_api *entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table); ++#endif ++ + static grub_err_t + grub_chainloader_unload (void *context) + { +@@ -209,6 +223,421 @@ + return file_path; + } + ++#ifdef SUPPORT_SECURE_BOOT ++#define SHIM_LOCK_GUID \ ++ { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} } ++ ++struct grub_pe32_header_no_msdos_stub ++{ ++ char signature[GRUB_PE32_SIGNATURE_SIZE]; ++ struct grub_pe32_coff_header coff_header; ++ struct grub_pe64_optional_header optional_header; ++}; ++ ++struct pe_coff_loader_image_context ++{ ++ grub_efi_uint64_t image_address; ++ grub_efi_uint64_t image_size; ++ grub_efi_uint64_t entry_point; ++ grub_efi_uintn_t size_of_headers; ++ grub_efi_uint16_t image_type; ++ grub_efi_uint16_t number_of_sections; ++ struct grub_pe32_section_table *first_section; ++ struct grub_pe32_data_directory *reloc_dir; ++ struct grub_pe32_data_directory *sec_dir; ++ grub_efi_uint64_t number_of_rva_and_sizes; ++ struct grub_pe32_header_no_msdos_stub *pe_hdr; ++}; ++ ++struct grub_secureboot_chainloader_context ++{ ++ grub_efi_physical_address_t address; ++ grub_efi_uintn_t pages; ++ grub_ssize_t fsize; ++ grub_efi_device_path_t *file_path; ++ grub_efi_char16_t *cmdline; ++ grub_ssize_t cmdline_len; ++ grub_efi_handle_t dev_handle; ++}; ++ ++typedef struct pe_coff_loader_image_context pe_coff_loader_image_context_t; ++ ++struct grub_efi_shim_lock ++{ ++ grub_efi_status_t (*verify)(void *buffer, ++ grub_efi_uint32_t size); ++ grub_efi_status_t (*hash)(void *data, ++ grub_efi_int32_t datasize, ++ pe_coff_loader_image_context_t *context, ++ grub_efi_uint8_t *sha256hash, ++ grub_efi_uint8_t *sha1hash); ++ grub_efi_status_t (*context)(void *data, ++ grub_efi_uint32_t size, ++ pe_coff_loader_image_context_t *context); ++}; ++ ++typedef struct grub_efi_shim_lock grub_efi_shim_lock_t; ++ ++static grub_efi_boolean_t ++grub_secure_validate (void *data, grub_efi_uint32_t size) ++{ ++ grub_guid_t guid = SHIM_LOCK_GUID; ++ grub_efi_shim_lock_t *shim_lock; ++ ++ shim_lock = grub_efi_locate_protocol (&guid, NULL); ++ ++ if (!shim_lock) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "no shim lock protocol"); ++ return 0; ++ } ++ ++ if (shim_lock->verify (data, size) == GRUB_EFI_SUCCESS) ++ { ++ grub_dprintf ("chain", "verify success\n"); ++ return 1; ++ } ++ ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "verify failed"); ++ return 0; ++} ++ ++static grub_efi_boolean_t ++read_header (void *data, grub_efi_uint32_t size, pe_coff_loader_image_context_t *context) ++{ ++ grub_efi_guid_t guid = SHIM_LOCK_GUID; ++ grub_efi_shim_lock_t *shim_lock; ++ grub_efi_status_t status; ++ ++ shim_lock = grub_efi_locate_protocol (&guid, NULL); ++ ++ if (!shim_lock) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "no shim lock protocol"); ++ return 0; ++ } ++ ++ status = shim_lock->context (data, size, context); ++ ++ if (status == GRUB_EFI_SUCCESS) ++ { ++ grub_dprintf ("chain", "context success\n"); ++ return 1; ++ } ++ ++ switch (status) ++ { ++ case GRUB_EFI_UNSUPPORTED: ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "context error unsupported"); ++ break; ++ case GRUB_EFI_INVALID_PARAMETER: ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "context error invalid parameter"); ++ break; ++ default: ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "context error code"); ++ break; ++ } ++ ++ return 0; ++} ++ ++static void* ++image_address (void *image, grub_efi_uint64_t sz, grub_efi_uint64_t adr) ++{ ++ if (adr > sz) ++ return NULL; ++ ++ return ((grub_uint8_t*)image + adr); ++} ++ ++static grub_efi_status_t ++relocate_coff (pe_coff_loader_image_context_t *context, void *data) ++{ ++ struct grub_pe32_data_directory *reloc_base, *reloc_base_end; ++ grub_efi_uint64_t adjust; ++ grub_efi_uint16_t *reloc, *reloc_end; ++ char *fixup, *fixup_base, *fixup_data = NULL; ++ grub_efi_uint16_t *fixup_16; ++ grub_efi_uint32_t *fixup_32; ++ grub_efi_uint64_t *fixup_64; ++ ++ grub_efi_uint64_t size = context->image_size; ++ void *image_end = (char *)data + size; ++ ++ context->pe_hdr->optional_header.image_base = (grub_uint64_t)data; ++ ++ if (context->number_of_rva_and_sizes <= 5 || context->reloc_dir->size == 0) ++ { ++ grub_dprintf ("chain", "no need to reloc, we are done\n"); ++ return GRUB_EFI_SUCCESS; ++ } ++ ++ reloc_base = image_address (data, size, context->reloc_dir->rva); ++ reloc_base_end = image_address (data, size, context->reloc_dir->rva + context->reloc_dir->size -1); ++ ++ grub_dprintf ("chain", "reloc_base %p reloc_base_end %p\n", reloc_base, reloc_base_end); ++ ++ if (!reloc_base || !reloc_base_end) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc table overflows binary"); ++ return GRUB_EFI_UNSUPPORTED; ++ } ++ ++ adjust = (grub_uint64_t)data - context->image_address; ++ ++ while (reloc_base < reloc_base_end) ++ { ++ reloc = (grub_uint16_t *)((char*)reloc_base + sizeof (struct grub_pe32_data_directory)); ++ reloc_end = (grub_uint16_t *)((char*)reloc_base + reloc_base->size); ++ ++ if ((void *)reloc_end < data || (void *)reloc_end > image_end) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc table overflows binary"); ++ return GRUB_EFI_UNSUPPORTED; ++ } ++ ++ fixup_base = image_address(data, size, reloc_base->rva); ++ ++ if (!fixup_base) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid fixupbase"); ++ return GRUB_EFI_UNSUPPORTED; ++ } ++ ++ while (reloc < reloc_end) ++ { ++ fixup = fixup_base + (*reloc & 0xFFF); ++ switch ((*reloc) >> 12) ++ { ++ case GRUB_PE32_REL_BASED_ABSOLUTE: ++ break; ++ case GRUB_PE32_REL_BASED_HIGH: ++ fixup_16 = (grub_uint16_t *)fixup; ++ *fixup_16 = (grub_uint16_t) (*fixup_16 + ((grub_uint16_t)((grub_uint32_t)adjust >> 16))); ++ if (fixup_data != NULL) ++ { ++ *(grub_uint16_t *) fixup_data = *fixup_16; ++ fixup_data = fixup_data + sizeof (grub_uint16_t); ++ } ++ break; ++ case GRUB_PE32_REL_BASED_LOW: ++ fixup_16 = (grub_uint16_t *)fixup; ++ *fixup_16 = (grub_uint16_t) (*fixup_16 + (grub_uint16_t)adjust ); ++ if (fixup_data != NULL) ++ { ++ *(grub_uint16_t *) fixup_data = *fixup_16; ++ fixup_data = fixup_data + sizeof (grub_uint16_t); ++ } ++ break; ++ case GRUB_PE32_REL_BASED_HIGHLOW: ++ fixup_32 = (grub_uint32_t *)fixup; ++ *fixup_32 = *fixup_32 + (grub_uint32_t)adjust; ++ if (fixup_data != NULL) ++ { ++ fixup_data = (char *)ALIGN_UP ((grub_addr_t)fixup_data, sizeof (grub_uint32_t)); ++ *(grub_uint32_t *) fixup_data = *fixup_32; ++ fixup_data += sizeof (grub_uint32_t); ++ } ++ break; ++ case GRUB_PE32_REL_BASED_DIR64: ++ fixup_64 = (grub_uint64_t *)fixup; ++ *fixup_64 = *fixup_64 + (grub_uint64_t)adjust; ++ if (fixup_data != NULL) ++ { ++ fixup_data = (char *)ALIGN_UP ((grub_addr_t)fixup_data, sizeof (grub_uint64_t)); ++ *(grub_uint64_t *) fixup_data = *fixup_64; ++ fixup_data += sizeof (grub_uint64_t); ++ } ++ break; ++ default: ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown relocation"); ++ return GRUB_EFI_UNSUPPORTED; ++ } ++ reloc += 1; ++ } ++ reloc_base = (struct grub_pe32_data_directory *)reloc_end; ++ } ++ ++ return GRUB_EFI_SUCCESS; ++} ++ ++static grub_efi_device_path_t * ++grub_efi_get_media_file_path (grub_efi_device_path_t *dp) ++{ ++ while (1) ++ { ++ grub_efi_uint8_t type; ++ grub_efi_uint8_t subtype; ++ ++ if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) ++ break; ++ ++ type = GRUB_EFI_DEVICE_PATH_TYPE (dp); ++ subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); ++ ++ if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE && subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE) ++ return dp; ++ ++ dp = GRUB_EFI_NEXT_DEVICE_PATH (dp); ++ } ++ ++ return NULL; ++} ++ ++static grub_efi_boolean_t ++handle_image (struct grub_secureboot_chainloader_context *load_context) ++{ ++ grub_efi_boot_services_t *b; ++ grub_efi_loaded_image_t *li, li_bak; ++ grub_efi_status_t efi_status; ++ void *data = (void *)(unsigned long)load_context->address; ++ grub_efi_uint32_t datasize = load_context->fsize; ++ char *buffer = NULL; ++ char *buffer_aligned = NULL; ++ grub_efi_uint32_t i, size; ++ struct grub_pe32_section_table *section; ++ char *base, *end; ++ pe_coff_loader_image_context_t context; ++ grub_uint32_t section_alignment; ++ grub_uint32_t buffer_size; ++ ++ b = grub_efi_system_table->boot_services; ++ ++ if (read_header (data, datasize, &context)) ++ { ++ grub_dprintf ("chain", "Succeed to read header\n"); ++ } ++ else ++ { ++ grub_dprintf ("chain", "Failed to read header\n"); ++ goto error_exit; ++ } ++ ++ section_alignment = context.pe_hdr->optional_header.section_alignment; ++ buffer_size = context.image_size + section_alignment; ++ ++ efi_status = b->allocate_pool (GRUB_EFI_LOADER_DATA, ++ buffer_size, (void**)&buffer); ++ ++ if (efi_status != GRUB_EFI_SUCCESS) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); ++ goto error_exit; ++ } ++ ++ buffer_aligned = (char *)ALIGN_UP ((grub_addr_t)buffer, section_alignment); ++ ++ grub_memcpy (buffer_aligned, data, context.size_of_headers); ++ ++ section = context.first_section; ++ for (i = 0; i < context.number_of_sections; i++) ++ { ++ size = section->virtual_size; ++ if (size > section->raw_data_size) ++ size = section->raw_data_size; ++ ++ base = image_address (buffer_aligned, context.image_size, section->virtual_address); ++ end = image_address (buffer_aligned, context.image_size, section->virtual_address + size - 1); ++ ++ if (!base || !end) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid section size"); ++ goto error_exit; ++ } ++ ++ if (section->raw_data_size > 0) ++ grub_memcpy (base, (grub_efi_uint8_t*)data + section->raw_data_offset, size); ++ ++ if (size < section->virtual_size) ++ grub_memset (base + size, 0, section->virtual_size - size); ++ ++ grub_dprintf ("chain", "copied section %s\n", section->name); ++ section += 1; ++ } ++ ++ efi_status = relocate_coff (&context, buffer_aligned); ++ ++ if (efi_status != GRUB_EFI_SUCCESS) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "relocation failed"); ++ goto error_exit; ++ } ++ ++ entry_point = image_address (buffer_aligned, context.image_size, context.entry_point); ++ ++ if (!entry_point) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid entry point"); ++ goto error_exit; ++ } ++ ++ li = grub_efi_get_loaded_image (grub_efi_image_handle); ++ if (!li) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, "no loaded image available"); ++ goto error_exit; ++ } ++ ++ grub_memcpy (&li_bak, li, sizeof (grub_efi_loaded_image_t)); ++ li->image_base = buffer_aligned; ++ li->image_size = context.image_size; ++ li->load_options = load_context->cmdline; ++ li->load_options_size = load_context->cmdline_len; ++ li->file_path = grub_efi_get_media_file_path (load_context->file_path); ++ li->device_handle = load_context->dev_handle; ++ if (li->file_path) ++ { ++ grub_printf ("file path: "); ++ grub_efi_print_device_path (li->file_path); ++ } ++ else ++ { ++ grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching file path found"); ++ goto error_exit; ++ } ++ ++ efi_status = entry_point (grub_efi_image_handle, grub_efi_system_table); ++ ++ grub_memcpy (li, &li_bak, sizeof (grub_efi_loaded_image_t)); ++ efi_status = b->free_pool (buffer); ++ ++ return 1; ++ ++error_exit: ++ if (buffer) ++ b->free_pool (buffer); ++ ++ return 0; ++ ++} ++ ++static grub_err_t ++grub_secureboot_chainloader_unload (void* context) ++{ ++ grub_efi_boot_services_t *b; ++ struct grub_secureboot_chainloader_context *sb_context = (struct grub_secureboot_chainloader_context *)context; ++ ++ b = grub_efi_system_table->boot_services; ++ b->free_pages (sb_context->address, sb_context->pages); ++ grub_free (sb_context->file_path); ++ grub_free (sb_context->cmdline); ++ grub_free (sb_context); ++ ++ grub_dl_unref (my_mod); ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_secureboot_chainloader_boot (void *context) ++{ ++ struct grub_secureboot_chainloader_context *sb_context = (struct grub_secureboot_chainloader_context *)context; ++ ++ handle_image (sb_context); ++ grub_loader_unset (); ++ return grub_errno; ++} ++#endif ++ + static grub_err_t + grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +@@ -222,11 +651,12 @@ + grub_efi_loaded_image_t *loaded_image; + char *filename; + void *boot_image = 0; +- grub_efi_handle_t dev_handle = 0; + grub_efi_physical_address_t address = 0; + grub_efi_uintn_t pages = 0; + grub_efi_char16_t *cmdline = NULL; + grub_efi_handle_t image_handle = NULL; ++ grub_ssize_t cmdline_len = 0; ++ grub_efi_handle_t dev_handle = 0; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); +@@ -236,12 +666,39 @@ + + b = grub_efi_system_table->boot_services; + ++ if (argc > 1) ++ { ++ int i; ++ grub_efi_char16_t *p16; ++ ++ for (i = 1, cmdline_len = 0; i < argc; i++) ++ cmdline_len += grub_strlen (argv[i]) + 1; ++ ++ cmdline_len *= sizeof (grub_efi_char16_t); ++ cmdline = p16 = grub_malloc (cmdline_len); ++ if (! cmdline) ++ goto fail; ++ ++ for (i = 1; i < argc; i++) ++ { ++ char *p8; ++ ++ p8 = argv[i]; ++ while (*p8) ++ *(p16++) = *(p8++); ++ ++ *(p16++) = ' '; ++ } ++ *(--p16) = 0; ++ } ++ + file = grub_file_open (filename, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE); + if (! file) + goto fail; + +- /* Get the root device's device path. */ +- dev = grub_device_open (0); ++ /* Get the device path from filename. */ ++ char *devname = grub_file_get_device_name (filename); ++ dev = grub_device_open (devname); + if (dev == NULL) + ; + else if (dev->disk) +@@ -343,6 +800,28 @@ + } + #endif + ++#ifdef SUPPORT_SECURE_BOOT ++ /* FIXME is secure boot possible also with universal binaries? */ ++ if (debug_secureboot || (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED && grub_secure_validate ((void *)address, size))) ++ { ++ struct grub_secureboot_chainloader_context *sb_context; ++ ++ sb_context = grub_malloc (sizeof (*sb_context)); ++ if (!sb_context) ++ goto fail; ++ sb_context->cmdline = cmdline; ++ sb_context->cmdline_len = cmdline_len; ++ sb_context->fsize = size; ++ sb_context->dev_handle = dev_handle; ++ sb_context->address = address; ++ sb_context->pages = pages; ++ sb_context->file_path = file_path; ++ grub_file_close (file); ++ grub_loader_set_ex (grub_secureboot_chainloader_boot, grub_secureboot_chainloader_unload, sb_context, 0); ++ return 0; ++ } ++#endif ++ + status = b->load_image (0, grub_efi_image_handle, file_path, + boot_image, size, + &image_handle); +@@ -368,33 +847,10 @@ + loaded_image->device_handle = dev_handle; + + /* Build load options with arguments from chainloader command line. */ +- if (argc > 1) ++ if (cmdline) + { +- int i, len; +- grub_efi_char16_t *p16; +- +- for (i = 1, len = 0; i < argc; i++) +- len += grub_strlen (argv[i]) + 1; +- +- len *= sizeof (grub_efi_char16_t); +- cmdline = p16 = grub_malloc (len); +- if (! cmdline) +- goto fail; +- +- for (i = 1; i < argc; i++) +- { +- char *p8; +- +- p8 = argv[i]; +- while (*p8) +- *(p16++) = *(p8++); +- +- *(p16++) = ' '; +- } +- *(--p16) = 0; +- + loaded_image->load_options = cmdline; +- loaded_image->load_options_size = len; ++ loaded_image->load_options_size = cmdline_len; + } + + grub_file_close (file); diff --git a/grub2-secureboot-install-signed-grub.patch b/grub2-secureboot-install-signed-grub.patch new file mode 100644 index 0000000..97eaa86 --- /dev/null +++ b/grub2-secureboot-install-signed-grub.patch @@ -0,0 +1,225 @@ +From 1ff2f31d12f7235423a1eb8a117e0c6f8b2f41c7 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Tue, 4 Jun 2019 12:32:35 +0800 +Subject: [PATCH] grub-install: handle signed grub installation on arm64-efi + +Use grub2-install to handle signed grub installation for arm64 UEFI secure +boot, the default behavior is auto, which will install signed grub whenever +detected. + +Two options, --suse-force-signed and --suse-inhibit-signed, can be used to +override the default auto detecting behavior. The former will force to use +prebuilt signed image and thus will fail if missing, the latter will always use +'mkimage' to create unsigned core image per the user's running environment. + +Signed-off-by: Michael Chang +--- + util/grub-install.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 85 insertions(+), 1 deletion(-) + +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -85,6 +85,15 @@ + + enum + { ++ SIGNED_GRUB_INHIBIT, ++ SIGNED_GRUB_AUTO, ++ SIGNED_GRUB_FORCE ++ }; ++ ++static int signed_grub_mode = SIGNED_GRUB_AUTO; ++ ++enum ++ { + OPTION_BOOT_DIRECTORY = 0x301, + OPTION_ROOT_DIRECTORY, + OPTION_TARGET, +@@ -109,6 +118,8 @@ + OPTION_NO_BOOTSECTOR, + OPTION_NO_RS_CODES, + OPTION_SUSE_ENABLE_TPM, ++ OPTION_SUSE_FORCE_SIGNED, ++ OPTION_SUSE_INHIBIT_SIGNED, + OPTION_MACPPC_DIRECTORY, + OPTION_ZIPL_DIRECTORY, + OPTION_LABEL_FONT, +@@ -238,6 +249,14 @@ + suse_enable_tpm = 1; + return 0; + ++ case OPTION_SUSE_FORCE_SIGNED: ++ signed_grub_mode = SIGNED_GRUB_FORCE; ++ return 0; ++ ++ case OPTION_SUSE_INHIBIT_SIGNED: ++ signed_grub_mode = SIGNED_GRUB_INHIBIT; ++ return 0; ++ + case OPTION_DEBUG: + verbosity++; + return 0; +@@ -300,7 +319,12 @@ + N_("Do not apply any reed-solomon codes when embedding core.img. " + "This option is only available on x86 BIOS targets."), 0}, + {"suse-enable-tpm", OPTION_SUSE_ENABLE_TPM, 0, 0, N_("install TPM modules"), 0}, +- ++ {"suse-force-signed", OPTION_SUSE_FORCE_SIGNED, 0, 0, ++ N_("force installation of signed grub" "%s." ++ "This option is only available on ARM64 EFI targets."), 0}, ++ {"suse-inhibit-signed", OPTION_SUSE_INHIBIT_SIGNED, 0, 0, ++ N_("inhibit installation of signed grub. " ++ "This option is only available on ARM64 EFI targets."), 0}, + {"debug", OPTION_DEBUG, 0, OPTION_HIDDEN, 0, 2}, + {"no-floppy", OPTION_NO_FLOPPY, 0, OPTION_HIDDEN, 0, 2}, + {"debug-image", OPTION_DEBUG_IMAGE, N_("STRING"), OPTION_HIDDEN, 0, 2}, +@@ -375,6 +399,22 @@ + free (plats); + return ret; + } ++ case OPTION_SUSE_FORCE_SIGNED: ++ { ++ const char *t = get_default_platform (); ++ char *ret; ++ if (grub_strcmp (t, "arm64-efi") == 0) ++ { ++ char *s = grub_util_path_concat (3, grub_util_get_pkglibdir (), t, "grub.efi"); ++ char *text2 = xasprintf (" [default=%s]", s); ++ ret = xasprintf (text, text2); ++ free (text2); ++ free (s); ++ } ++ else ++ ret = xasprintf (text, ""); ++ return ret; ++ } + case ARGP_KEY_HELP_POST_DOC: + return xasprintf (text, program_name, GRUB_BOOT_DIR_NAME "/" GRUB_DIR_NAME); + default: +@@ -1681,13 +1721,34 @@ + + char mkimage_target[200]; + const char *core_name = NULL; ++ char *signed_imgfile = NULL; + + switch (platform) + { ++ case GRUB_INSTALL_PLATFORM_ARM64_EFI: ++ ++ if (signed_grub_mode > SIGNED_GRUB_INHIBIT) ++ { ++ signed_imgfile = grub_util_path_concat (2, grub_install_source_directory, "grub.efi"); ++ if (!grub_util_is_regular (signed_imgfile)) ++ { ++ if (signed_grub_mode >= SIGNED_GRUB_FORCE) ++ grub_util_error ("signed image `%s' does not exist\n", signed_imgfile); ++ else ++ { ++ free (signed_imgfile); ++ signed_imgfile = NULL; ++ } ++ } ++ } ++ ++ if (signed_imgfile) ++ fprintf (stderr, _("Use signed file in %s for installation.\n"), signed_imgfile); ++ ++ /* fallthrough. */ + case GRUB_INSTALL_PLATFORM_I386_EFI: + case GRUB_INSTALL_PLATFORM_X86_64_EFI: + case GRUB_INSTALL_PLATFORM_ARM_EFI: +- case GRUB_INSTALL_PLATFORM_ARM64_EFI: + case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI: + case GRUB_INSTALL_PLATFORM_RISCV32_EFI: + case GRUB_INSTALL_PLATFORM_RISCV64_EFI: +@@ -1758,13 +1819,75 @@ + core_name); + char *prefix = xasprintf ("%s%s", prefix_drive ? : "", + relative_grubdir); +- if (core_name != mkimage_target) ++ char *grub_efi_cfg = NULL; ++ ++ if ((core_name != mkimage_target) && !signed_imgfile) + grub_install_make_image_wrap (/* source dir */ grub_install_source_directory, + /*prefix */ prefix, + /* output */ imgfile, + /* memdisk */ NULL, + have_load_cfg ? load_cfg : NULL, + /* image target */ mkimage_target, 0); ++ else if (signed_imgfile) ++ { ++ FILE *grub_cfg_f; ++ ++ grub_install_copy_file (signed_imgfile, imgfile, 1); ++ grub_efi_cfg = grub_util_path_concat (2, platdir, "grub.cfg"); ++ grub_cfg_f = grub_util_fopen (grub_efi_cfg, "wb"); ++ if (!grub_cfg_f) ++ grub_util_error (_("Can't create file: %s"), strerror (errno)); ++ ++ if (have_abstractions) ++ { ++ fprintf (grub_cfg_f, "set prefix=(%s)%s\n", grub_drives[0], relative_grubdir); ++ fprintf (grub_cfg_f, "set root=%s\n", grub_drives[0]); ++ } ++ else if (prefix_drive) ++ { ++ char *uuid = NULL; ++ if (grub_fs->fs_uuid && grub_fs->fs_uuid (grub_dev, &uuid)) ++ { ++ grub_print_error (); ++ grub_errno = 0; ++ uuid = NULL; ++ } ++ if (!uuid) ++ grub_util_error ("cannot find fs uuid for %s", grub_fs->name); ++ ++ fprintf (grub_cfg_f, "search --fs-uuid --set=root %s\n", uuid); ++ fprintf (grub_cfg_f, "set prefix=($root)%s\n", relative_grubdir); ++ } ++ ++ if (have_load_cfg) ++ { ++ size_t len; ++ char *buf; ++ ++ FILE *fp = grub_util_fopen (load_cfg, "rb"); ++ if (!fp) ++ grub_util_error (_("Can't read file: %s"), strerror (errno)); ++ ++ fseek (fp, 0, SEEK_END); ++ len = ftell (fp); ++ fseek (fp, 0, SEEK_SET); ++ buf = xmalloc (len); ++ ++ if (fread (buf, 1, len, fp) != len) ++ grub_util_error (_("cannot read `%s': %s"), load_cfg, strerror (errno)); ++ ++ if (fwrite (buf, 1, len, grub_cfg_f) != len) ++ grub_util_error (_("cannot write `%s': %s"), grub_efi_cfg, strerror (errno)); ++ ++ free (buf); ++ fclose (fp); ++ } ++ ++ fprintf (grub_cfg_f, "source ${prefix}/grub.cfg\n"); ++ fclose (grub_cfg_f); ++ free (signed_imgfile); ++ signed_imgfile = NULL; ++ } + /* Backward-compatibility kludges. */ + switch (platform) + { +@@ -2061,6 +2184,13 @@ + grub_set_install_backup_ponr (); + + free (dst); ++ if (grub_efi_cfg) ++ { ++ dst = grub_util_path_concat (2, efidir, "grub.cfg"); ++ grub_install_copy_file (grub_efi_cfg, dst, 1); ++ free (dst); ++ free (grub_efi_cfg); ++ } + } + if (!removable && update_nvram) + { diff --git a/grub2-secureboot-no-insmod-on-sb.patch b/grub2-secureboot-no-insmod-on-sb.patch new file mode 100644 index 0000000..5779650 --- /dev/null +++ b/grub2-secureboot-no-insmod-on-sb.patch @@ -0,0 +1,51 @@ +From 29c89e27805f7a6a22bce11ed9bb430e19c972a9 Mon Sep 17 00:00:00 2001 +From: Colin Watson +Date: Tue, 23 Oct 2012 10:40:49 -0400 +Subject: [PATCH 449/482] Don't allow insmod when secure boot is enabled. + +References: fate#314485 +Patch-Mainline: no + +v2: +Use grub_efi_get_secureboot to get secure boot status + +Signed-off-by: Michael Chang +--- + grub-core/kern/dl.c | 17 +++++++++++++++++ + grub-core/kern/efi/efi.c | 28 ++++++++++++++++++++++++++++ + include/grub/efi/efi.h | 1 + + 3 files changed, 46 insertions(+) + +--- a/grub-core/kern/dl.c ++++ b/grub-core/kern/dl.c +@@ -38,6 +38,10 @@ + #define GRUB_MODULES_MACHINE_READONLY + #endif + ++#ifdef GRUB_MACHINE_EFI ++#include ++#endif ++ + + + #pragma GCC diagnostic ignored "-Wcast-align" +@@ -708,6 +712,19 @@ + + grub_boot_time ("Loading module %s", filename); + ++#ifdef GRUB_MACHINE_EFI ++ if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) ++ { ++#if 0 ++ /* This is an error, but grub2-mkconfig still generates a pile of ++ * insmod commands, so emitting it would be mostly just obnoxious. */ ++ grub_error (GRUB_ERR_ACCESS_DENIED, ++ "Secure Boot forbids loading module from %s", filename); ++#endif ++ return 0; ++ } ++#endif ++ + file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE); + if (! file) + return 0; diff --git a/grub2-setup-try-fs-embed-if-mbr-gap-too-small.patch b/grub2-setup-try-fs-embed-if-mbr-gap-too-small.patch new file mode 100644 index 0000000..5b97f27 --- /dev/null +++ b/grub2-setup-try-fs-embed-if-mbr-gap-too-small.patch @@ -0,0 +1,61 @@ + +V2: Add fs_ prefix to fs functions by upstream commit ad4bfee + +Index: grub-2.06~rc1/util/setup.c +=================================================================== +--- grub-2.06~rc1.orig/util/setup.c ++++ grub-2.06~rc1/util/setup.c +@@ -530,8 +530,42 @@ SETUP (const char *dir, + err = grub_util_ldm_embed (dest_dev->disk, &nsec, maxsec, + GRUB_EMBED_PCBIOS, §ors); + else if (ctx.dest_partmap) +- err = ctx.dest_partmap->embed (dest_dev->disk, &nsec, maxsec, +- GRUB_EMBED_PCBIOS, §ors, warn_small); ++ { ++ err = ctx.dest_partmap->embed (dest_dev->disk, &nsec, maxsec, ++ GRUB_EMBED_PCBIOS, §ors, warn_small); ++#ifdef GRUB_SETUP_BIOS ++ if ((err == GRUB_ERR_OUT_OF_RANGE || err == GRUB_ERR_FILE_NOT_FOUND) ++ && dest_dev->disk->id == root_dev->disk->id ++ && dest_dev->disk->dev->id == root_dev->disk->dev->id) ++ { ++ grub_fs_t root_fs; ++ ++ root_fs = grub_fs_probe (root_dev); ++ if (root_fs && root_fs->fs_embed) ++ { ++ grub_disk_addr_t *fs_sectors; ++ unsigned int fs_nsec; ++ ++ fs_sectors = NULL; ++ fs_nsec = core_sectors; ++ err = root_fs->fs_embed (root_dev, &fs_nsec, maxsec, ++ GRUB_EMBED_PCBIOS, &fs_sectors); ++ if (!err && fs_nsec >= core_sectors) ++ { ++ sectors = fs_sectors; ++ nsec = fs_nsec; ++ ctx.container = root_dev->disk->partition; ++ core_dev = root_dev; ++ } ++ else ++ { ++ if (fs_sectors) ++ grub_free (fs_sectors); ++ } ++ } ++ } ++#endif ++ } + else + err = fs->fs_embed (dest_dev, &nsec, maxsec, + GRUB_EMBED_PCBIOS, §ors); +@@ -643,7 +677,7 @@ SETUP (const char *dir, + + /* Write the core image onto the disk. */ + for (i = 0; i < nsec; i++) +- grub_disk_write (dest_dev->disk, sectors[i], 0, ++ grub_disk_write (core_dev->disk, sectors[i], 0, + GRUB_DISK_SECTOR_SIZE, + core_img + i * GRUB_DISK_SECTOR_SIZE); + #endif diff --git a/grub2-simplefb.patch b/grub2-simplefb.patch new file mode 100644 index 0000000..f624abd --- /dev/null +++ b/grub2-simplefb.patch @@ -0,0 +1,13 @@ + + +--- a/util/grub.d/10_linux.in ++++ b/util/grub.d/10_linux.in +@@ -116,7 +116,7 @@ + # FIXME: We need an interface to select vesafb in case efifb can't be used. + if [ "x$GRUB_GFXPAYLOAD_LINUX" = x ]; then + echo " load_video" | sed "s/^/$submenu_indentation/" +- if grep -qx "CONFIG_FB_EFI=y" "${config}" 2> /dev/null \ ++ if grep -qx "CONFIG_\(FB_EFI\|SYSFB_SIMPLEFB\)=y" "${config}" 2> /dev/null \ + && grep -qx "CONFIG_VT_HW_CONSOLE_BINDING=y" "${config}" 2> /dev/null; then + echo " set gfxpayload=keep" | sed "s/^/$submenu_indentation/" + fi diff --git a/grub2-snapper-plugin.sh b/grub2-snapper-plugin.sh new file mode 100644 index 0000000..355fb25 --- /dev/null +++ b/grub2-snapper-plugin.sh @@ -0,0 +1,281 @@ +#!/bin/sh +set -e + +# Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc. +# +# GRUB is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# GRUB is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GRUB. If not, see . + +grub_mkconfig="/usr/sbin/grub2-mkconfig" +grub_mkrelpath="/usr/bin/grub2-mkrelpath" +grub_script_check="/usr/bin/grub2-script-check" +grub_setting="/etc/default/grub" +grub_cfg="/boot/grub2/grub.cfg" +grub_snapshot_cfg="/boot/grub2/snapshot_submenu.cfg" + +snapper_snapshot_path="/.snapshots" +snapshot_submenu_name="grub-snapshot.cfg" +snapper_snapshots_cfg="${snapper_snapshot_path}/${snapshot_submenu_name}" + +# add hotkeys for s390. (bnc#885668) +hotkey= +incr_hotkey() +{ + [ -n "$hotkey" ] || return + expr $hotkey + 1 +} +print_hotkey() +{ + keys="123456789abdfgijklmnoprstuvwyz" + if [ -z "$hotkey" ]||[ $hotkey -eq 0 ]||[ $hotkey -gt 30 ]; then + return + fi + echo "--hotkey=$(expr substr $keys $hotkey 1)" +} + + +snapshot_submenu () { + + s_dir="$1" + + snapshot="${s_dir}/snapshot" + num="`basename $s_dir`" + + # bnc#864842 Important snapshots are not marked as such in grub2 menu + # the format is "important distribution version (kernel_version, timestamp, pre/post)" + date=`xmllint --xpath '/snapshot/date/text()' "${s_dir}/info.xml" || echo ""` + date=`echo $date | sed 's/\(.*\) \(.*\):.*/\1T\2/'` + important=`xmllint --xpath "/snapshot/userdata[key='important']/value/text()" "${s_dir}/info.xml" 2>/dev/null || echo ""` + stype=`xmllint --xpath '/snapshot/type/text()' "${s_dir}/info.xml" || echo ""` + kernel_ver=`readlink ${snapshot}/boot/vmlinuz | sed -e 's/^vmlinuz-//' -e 's/-default$//'` + if [ -z "$kernel_ver" -a -L ${snapshot}/boot/image ]; then + kernel_ver=`readlink ${snapshot}/boot/image | sed -e 's/^image-//' -e 's/-default$//'` + fi + eval `cat ${snapshot}/etc/os-release` + # bsc#934252 - Replace SLES 12.1 with SLES12-SP1 for the list of snapshots + if test "${NAME}" = "SLES" -o "${NAME}" = "SLED"; then + VERSION=`echo ${VERSION} | sed -e 's!^\([0-9]\{1,\}\)\.\([0-9]\{1,\}\)$!\1-SP\2!'` + fi + + # FATE#318101 + # Show user defined comments in grub2 menu for snapshots + # Use userdata tag "bootloader=[user defined text]" + full_desc=`xmllint --xpath "/snapshot/userdata[key='bootloader']/value/text()" "${s_dir}/info.xml" 2>/dev/null || echo ""` + test -z "$full_desc" && desc=`xmllint --xpath '/snapshot/description/text()' "${s_dir}/info.xml" 2>/dev/null || echo ""` + + # FATE#317972 + # If we have a post entry and the description field is empty, + # we should use the "Pre" number and add that description to the post entry. + if test -z "$full_desc" -a -z "$desc" -a "$stype" = "post"; then + pre_num=`xmllint --xpath '/snapshot/pre_num/text()' "${s_dir}/info.xml" 2>/dev/null || echo ""` + if test -n "$pre_num"; then + if test -f "${snapper_snapshot_path}/${pre_num}/info.xml" ; then + desc=`xmllint --xpath '/snapshot/description/text()' "${snapper_snapshot_path}/${pre_num}/info.xml" 2>/dev/null || echo ""` + fi + fi + fi + + test "$important" = "yes" && important="*" || important=" " + test "$stype" = "single" && stype="" + test -z "$stype" || stype=",$stype" + test -z "$desc" || desc=",$desc" + test -z "$full_desc" && full_desc="$kernel_ver,$date$stype$desc" + + if test "${NAME}" = "SLES" -o "${NAME}" = "SLED"; then + title="${important}${NAME}${VERSION} ($full_desc)" + else + title="${important}${NAME} ${VERSION} ($full_desc)" + fi + + if test "$s390" = "1"; then + subvol="\$2" + else + subvol="\$3" + fi + + cat </dev/null || true + fi + continue + fi + + done + + hk="" + [ -z "$hotkey" ] || hk="--hotkey=s" + + for c in $(printf '%s' "${cs}" | sort -Vr); do + if ! snapshot_submenu "$c" > "${c}/${snapshot_submenu_name}"; then + rm -f "${c}/${snapshot_submenu_name}" + continue + fi + snapshot_cfg="${snapshot_cfg} + if [ -f \"$c/${snapshot_submenu_name}\" ]; then + source \"$c/${snapshot_submenu_name}\" + fi" + done + + cat <"${snapper_snapshots_cfg}.new" +if [ -z "\$extra_cmdline" ]; then + submenu $hk "Start bootloader from a read-only snapshot" {${snapshot_cfg} + if [ x\$snapshot_found != xtrue ]; then + submenu "Not Found" { true; } + fi + } +fi +EOF + + if ${grub_script_check} "${snapper_snapshots_cfg}.new"; then + mv -f "${snapper_snapshots_cfg}.new" "${snapper_snapshots_cfg}" + fi + +} + + +snapshot_submenu_clean () { + + for s_dir in ${snapper_snapshot_path}/*; do + + snapper_cfg="${s_dir}/${snapshot_submenu_name}" + + if [ -f "$snapper_cfg" ]; then + rm -f "$snapper_cfg" + rmdir "$s_dir" 2>/dev/null || true + fi + + done + + if [ -f "${snapper_snapshot_path}/${snapshot_submenu_name}" ]; then + rm -f "${snapper_snapshot_path}/${snapshot_submenu_name}" + fi + +} + +set_grub_setting () { + + name=$1 + val=$2 + + if grep -q "$name" "$grub_setting"; then + sed -i -e "s!.*\($name\)=.*!\1=\"$val\"!" "$grub_setting" + else + echo "$name=\"$val\"" >> "$grub_setting" + fi +} + +enable_grub_settings () { + set_grub_setting SUSE_BTRFS_SNAPSHOT_BOOTING "true" +} + +disable_grub_settings () { + set_grub_setting SUSE_BTRFS_SNAPSHOT_BOOTING "false" +} + +update_grub () { + "${grub_mkconfig}" -o "${grub_cfg}" +} + +machine=`uname -m` +case "$machine" in +(s390|s390x) + hotkey=1 + s390=1 + ;; +esac +cmdline="$0 $* hotkey='$hotkey'" + +# Check the arguments. +while test $# -gt 0 +do + option=$1 + shift + + case "$option" in + -e | --enable) + opt_enable=true + ;; + -d | --disable) + opt_enable=false + ;; + -r | --refresh) + opt_refresh=true + ;; + -c | --clean) + opt_clean=true + ;; + -*) + ;; + esac +done + +if [ "x${opt_enable}" = "xtrue" ]; then + #enable_grub_settings + #update_grub + snapper_snapshots_cfg_refresh +elif [ "x${opt_enable}" = "xfalse" ]; then + #disable_grub_settings + update_grub + snapshot_submenu_clean +fi + +if [ x${opt_refresh} = "xtrue" ]; then + snapper_snapshots_cfg_refresh +fi + +if [ x${opt_clean} = "xtrue" ]; then + snapshot_submenu_clean +fi + diff --git a/grub2-suse-remove-linux-root-param.patch b/grub2-suse-remove-linux-root-param.patch new file mode 100644 index 0000000..28a218a --- /dev/null +++ b/grub2-suse-remove-linux-root-param.patch @@ -0,0 +1,75 @@ +--- a/util/grub-mkconfig.in ++++ b/util/grub-mkconfig.in +@@ -299,7 +299,8 @@ + GRUB_OS_PROBER_SKIP_LIST \ + GRUB_DISABLE_SUBMENU \ + SUSE_BTRFS_SNAPSHOT_BOOTING \ +- SUSE_CMDLINE_XENEFI ++ SUSE_CMDLINE_XENEFI \ ++ SUSE_REMOVE_LINUX_ROOT_PARAM + + if test "x${grub_cfg}" != "x"; then + rm -f "${grub_cfg}.new" +--- a/util/grub.d/10_linux.in ++++ b/util/grub.d/10_linux.in +@@ -76,7 +76,7 @@ + else + rootsubvol="`make_system_path_relative_to_its_root /`" + rootsubvol="${rootsubvol#/}" +- if [ "x${rootsubvol}" != x ]; then ++ if [ "x${rootsubvol}" != x ] && [ "x$SUSE_REMOVE_LINUX_ROOT_PARAM" != "xtrue" ]; then + GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}" + fi + fi;; +@@ -87,6 +87,10 @@ + ;; + esac + ++if [ "x$SUSE_REMOVE_LINUX_ROOT_PARAM" = "xtrue" ]; then ++ LINUX_ROOT_DEVICE="" ++fi ++ + title_correction_code= + + hotkey=1 +--- a/util/grub.d/20_linux_xen.in ++++ b/util/grub.d/20_linux_xen.in +@@ -100,7 +100,7 @@ + else + rootsubvol="`make_system_path_relative_to_its_root /`" + rootsubvol="${rootsubvol#/}" +- if [ "x${rootsubvol}" != x ]; then ++ if [ "x${rootsubvol}" != x ] && [ "x$SUSE_REMOVE_LINUX_ROOT_PARAM" != "xtrue" ]; then + GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}" + fi + fi;; +@@ -111,6 +111,10 @@ + ;; + esac + ++if [ "x$SUSE_REMOVE_LINUX_ROOT_PARAM" = "xtrue" ]; then ++ LINUX_ROOT_DEVICE="" ++fi ++ + title_correction_code= + + grub2_unquote () +--- a/util/s390x/zipl2grub.pl.in ++++ b/util/s390x/zipl2grub.pl.in +@@ -384,9 +384,13 @@ + } else { + $v = ""; + } +- if ($k eq "GRUB_DEVICE" && $v !~ /^UUID/ && ! -e $v) { +- s{root=\@$k\@}{}g; +- next; ++ if ($k eq "GRUB_DEVICE") { ++ if (($v !~ /^UUID/ && ! -e $v) || ++ (exists( $C{SUSE_REMOVE_LINUX_ROOT_PARAM}) && ++ $C{SUSE_REMOVE_LINUX_ROOT_PARAM} eq "true")) { ++ s{root=\@$k\@}{}g; ++ next; ++ } + } + s{\@$k\@}{$v}g; + } diff --git a/grub2-systemd-sleep.sh b/grub2-systemd-sleep.sh new file mode 100644 index 0000000..42c15fb --- /dev/null +++ b/grub2-systemd-sleep.sh @@ -0,0 +1,265 @@ +#!/bin/bash +# +# Copyright (c) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany. + +set -e + +GRUB_ONCE="/usr/sbin/grub2-once" +GRUB_ENV="/boot/grub2/grubenv" +GRUB_EDITENV="/usr/bin/grub2-editenv" +GRUB_CONF="/boot/grub2/grub.cfg" +GRUB_SETUP= +BLKID="/usr/sbin/blkid" +LSBLK="/usr/bin/lsblk" +ARCH=`uname -m` +VMLINUZ="vmlinuz" +case $ARCH in + ppc*) VMLINUZ="vmlinux" ;; + s390*) VMLINUZ="image"; GRUB_SETUP="/usr/sbin/grub2-zipl-setup" ;; +esac + +error_quit() +{ + echo "$1" >&2 + exit 1 +} + +check-system() +{ + [ -x "${GRUB_ONCE}" ] || error_quit "ERROR: cannot find or execute ${GRUB_ONCE}" + [ -x "${GRUB_EDITENV}" ] || error_quit "ERROR: cannot find or execute ${GRUB_EDITENV}" + [ -x "${BLKID}" ] || error_quit "ERROR: cannot find or execute ${BLKID}" + [ -r "${GRUB_CONF}" ] || error_quit "ERROR: cannot find or read ${GRUB_CONF}" +} + +compare-fsuuid() +{ + local uuids=($($LSBLK -n -o UUID $1 $2 2>/dev/null)) + if [ ${#uuids[@]} -eq 2 ] && [ "${uuids[0]}" = "${uuids[1]}" ]; then + return 0 + fi + return 1 +} + +##################################################################### +# gets a list of available kernels from /boot/grub2/grub.cfg +# kernels are in the array $KERNELS +get-kernels() +{ + local I DUMMY MNT ROOTDEV + declare -i I=0 + + # we need the root partition later to decide if this is the kernel to select + while read ROOTDEV MNT DUMMY; do + [ "$ROOTDEV" = "rootfs" ] && continue # not what we are searching for + if [ "$MNT" = "/" ]; then + break + fi + done < /proc/mounts + + + while read -r LINE; do + case $LINE in + menuentry\ *) + local PATTERN="^\\s*menuentry\\s\\+\\(.\\+\\)\\s*{.*\$" + MENUENTRY_OPTS=`echo "$LINE" | sed -n -e "s/${PATTERN}/\\1/p"` + MENU_ENTRIES[$I]=`eval "printf \"%s\n\" $MENUENTRY_OPTS | head -1"` + ;; + set\ default*) + local DEFAULT=${LINE#*default=} + + if echo $DEFAULT | grep -q saved_entry ; then + local SAVED=`$GRUB_EDITENV $GRUB_ENV list | sed -n s/^saved_entry=//p` + if [ -n "$SAVED" ]; then + DEFAULT_BOOT=$($GRUB_ONCE --show-mapped "$SAVED") + fi + fi + + ;; + linux*noresume*|module*xen*noresume*) + echo " Skipping ${MENU_ENTRIES[$I]}, because it has the noresume option" >&2 + ;; + linux*root=*|module*xen*root=*) + local ROOT + ROOT=${LINE#*root=} + DUMMY=($ROOT) + ROOT=${DUMMY[0]} + + if [ x"${ROOT:0:5}" = "xUUID=" ]; then + UUID=${ROOT#UUID=} + if [ -n "$UUID" ]; then + ROOT=$($BLKID -U $UUID || true) + if [ -z "$ROOT" ]; then + echo " Skipping ${MENU_ENTRIES[$I]}, because its root device $UUID is not found" >&2 + continue + fi + fi + fi + + if [ "$(stat -Lc '%t:%T' $ROOT || true)" != "$(stat -Lc '%t:%T' $ROOTDEV || true)" ]; then + if compare-fsuuid "$ROOT" "$ROOTDEV" ; then + echo " $ROOTDEV and $ROOT have the same filesystem UUID" + else + echo " Skipping ${MENU_ENTRIES[$I]}, because its root= parameter ($ROOT)" >&2 + echo " does not match the current root device ($ROOTDEV)." >&2 + continue + fi + fi + + DUMMY=($LINE) # kernel (hd0,1)/boot/vmlinuz-ABC root=/dev/hda2 + KERNELS[$I]=${DUMMY[1]##*/} # vmlinuz-ABC + # DEBUG "Found kernel entry #${I}: '${DUMMY[1]##*/}'" INFO + let ++I + ;; + linux*|module*xen*) + # a kernel without "root="? We better skip that one... + echo " Skipping ${MENU_ENTRIES[$I]}, because it has no root= option" >&2 + ;; + *) ;; + esac + done < "$GRUB_CONF" +} + +############################################################# +# restore grub default after (eventually failed) resume +grub-once-restore() +{ + echo "INFO: Running grub-once-restore .." + check-system + $GRUB_EDITENV $GRUB_ENV unset next_entry + echo "INFO: Done." +} + +############################################################################# +# try to find a kernel image that matches the actually running kernel. +# We need this, if more than one kernel is installed. This works reasonably +# well with grub, if all kernels are named "vmlinuz-`uname -r`" and are +# located in /boot. If they are not, good luck ;-) +# for 2021-style usrmerged kernels, the location in /usr/lib/modules/ \ +# `uname -r`/vmlinuz is resolved to match... +find-kernel-entry() +{ + NEXT_BOOT="" + declare -i I=0 + # DEBUG "running kernel: $RUNNING" DIAG + while [ -n "${KERNELS[$I]}" ]; do + BOOTING="${KERNELS[$I]}" + if IMAGE=$(readlink /boot/"$BOOTING"); then + if [[ $IMAGE == */vmlinuz ]]; then # new usrmerged setup + BOOTING=${IMAGE%/vmlinuz} # the directory name is what counts + BOOTING=${BOOTING##*/} + elif [ -e "/boot/${IMAGE##*/}" ]; then + # DEBUG "Found kernel symlink $BOOTING => $IMAGE" INFO + BOOTING=$IMAGE + fi + fi + BOOTING="${BOOTING#*${VMLINUZ}-}" + if [ "$RUNNING" == "$BOOTING" -a -n "${MENU_ENTRIES[$I]}" ]; then + NEXT_BOOT="${MENU_ENTRIES[$I]}" + echo " running kernel is grub menu entry $NEXT_BOOT (${KERNELS[$I]})" + break + fi + let ++I + done + # if we have not found a kernel, issue a warning. + # if we have found a kernel, we'll do "grub-once" later, after + # prepare_suspend finished. + if [ -z "$NEXT_BOOT" ]; then + echo "WARNING: no kernelfile matching the running kernel found" + fi +} + +############################################################################# +# if we did not find a kernel (or BOOT_LOADER is not GRUB) check, +# if the running kernel is still the one that will (probably) be booted for +# resume (default entry in menu.lst or, if there is none, the kernel file +# /boot/${VMLINUZ} points to.) +# This will only work, if you use "original" SUSE kernels. +# you can always override with the config variable set to "yes" +prepare-grub() +{ + echo "INFO: Running prepare-grub .." + check-system + get-kernels + RUNNING=`uname -r` + find-kernel-entry + + if [ -z "$NEXT_BOOT" ]; then + # which kernel is booted with the default entry? + BOOTING="${KERNELS[$DEFAULT_BOOT]}" + # if there is no default entry (no menu.lst?) we fall back to + # the default of /boot/${VMLINUZ}. + [ -z "$BOOTING" ] && BOOTING="${VMLINUZ}" + if IMAGE=$(readlink /boot/"$BOOTING"); then + if [[ $IMAGE == */vmlinuz ]]; then # new usrmerged setup + BOOTING=${IMAGE%/vmlinuz} # the directory name is what counts + BOOTING=${BOOTING##*/} + elif [ -e "/boot/${IMAGE##*/}" ]; then + BOOTING=$IMAGE + fi + fi + BOOTING="${BOOTING#*${VMLINUZ}-}" + echo "running kernel: '$RUNNING', probably booting kernel: '$BOOTING'" + check-setup "$RUNNING" + if [ "$BOOTING" != "$RUNNING" ]; then + error_quit "ERROR: kernel version mismatch, cannot suspend to disk" + fi + else + # set the bootloader to the running kernel + echo " preparing boot-loader: selecting entry $NEXT_BOOT, kernel /boot/$BOOTING" + T1=`date +"%s%N"` + sync; sync; sync # this is needed to speed up grub-once on reiserfs + T2=`date +"%s%N"` + check-setup "$RUNNING" + echo " running $GRUB_ONCE \"${NEXT_BOOT}\"" + ${GRUB_ONCE} "$NEXT_BOOT" + T3=`date +"%s%N"` + S=$(((T2-T1)/100000000)); S="$((S/10)).${S:0-1}" + G=$(((T3-T2)/100000000)); G="$((G/10)).${G:0-1}" + echo " time needed for sync: $S seconds, time needed for grub: $G seconds." + fi + + echo "INFO: Done." +} + +############################################################################# +check-setup() +{ + local WANT="$VMLINUZ-$1" + + [ -n "$GRUB_SETUP" ] || return 0 + # implementation below is s390x-only (for now) + echo "INFO: check-setup \"$WANT\" .." + HAVE="/boot/zipl/$VMLINUZ" + [ -r "$HAVE" ] || + error_quit "ERROR: no zipl kernel, cannot suspend to disk" + HAVE=$(readlink $HAVE) || + error_quit "ERROR: zipl kernel no sym-link, cannot suspend to disk" + [ "$HAVE" != "$WANT" ] || + { echo " zipl kernel already in sync, nothing to do"; return; } + echo " running $GRUB_SETUP # (incl. dracut!)" # no --image as running is preferred! + ${GRUB_SETUP} > /dev/null 2>&1 +} + +###### main() + +eval `grep LOADER_TYPE= /etc/sysconfig/bootloader` + +if [ x"$LOADER_TYPE" != "xgrub2" -a x"$LOADER_TYPE" != "xgrub2-efi" ]; then + echo "INFO: Skip running $0 for bootloader: $LOADER_TYPE" + exit 0 +fi + +if [ "$2" = suspend ]; then + echo "INFO: Skip running $0 for $2" + exit 0 +else + echo "INFO: running $0 for $2" +fi + +if [ "$1" = pre ] ; then + prepare-grub +fi +if [ "$1" = post ] ; then + grub-once-restore +fi diff --git a/grub2-use-Unifont-for-starfield-theme-terminal.patch b/grub2-use-Unifont-for-starfield-theme-terminal.patch new file mode 100644 index 0000000..4e4de24 --- /dev/null +++ b/grub2-use-Unifont-for-starfield-theme-terminal.patch @@ -0,0 +1,15 @@ +DejaVu Sans is proportional font and looks pretty bad in terminal +window. Use GNU Unifont instead. +Index: grub-2.02~beta2/themes/starfield/theme.txt +=================================================================== +--- grub-2.02~beta2.orig/themes/starfield/theme.txt ++++ grub-2.02~beta2/themes/starfield/theme.txt +@@ -25,7 +25,7 @@ message-font: "DejaVu Sans Regular 12" + message-color: "#000" + message-bg-color: "#fff" + terminal-box: "terminal_box_*.png" +-terminal-font: "DejaVu Sans Regular 12" ++terminal-font: "Gnu Unifont Mono Regular 16" + desktop-image: "starfield.png" + + #help bar at the bottom diff --git a/grub2-use-rpmsort-for-version-sorting.patch b/grub2-use-rpmsort-for-version-sorting.patch new file mode 100644 index 0000000..02fea75 --- /dev/null +++ b/grub2-use-rpmsort-for-version-sorting.patch @@ -0,0 +1,143 @@ +v2: +Fix wrong sorting order if version contains "-" delimiter + +--- a/util/grub-mkconfig_lib.in ++++ b/util/grub-mkconfig_lib.in +@@ -203,12 +203,17 @@ + version_sort () + { + case $version_sort_sort_has_v in ++ rpmsort) ++ LC_ALL=C /usr/lib/rpm/rpmsort "$@";; + yes) + LC_ALL=C sort -V "$@";; + no) + LC_ALL=C sort -n "$@";; + *) +- if sort -V /dev/null 2>&1; then ++ if test -x /usr/lib/rpm/rpmsort; then ++ version_sort_sort_has_v=rpmsort ++ LC_ALL=C /usr/lib/rpm/rpmsort "$@" ++ elif sort -V /dev/null 2>&1; then + version_sort_sort_has_v=yes + LC_ALL=C sort -V "$@" + else +--- a/util/grub.d/10_linux.in ++++ b/util/grub.d/10_linux.in +@@ -229,12 +229,56 @@ + # yet, so it's empty. In a submenu it will be equal to '\t' (one tab). + submenu_indentation="" + ++listvrf="" ++pre_sort () { ++ local l="" ++ ++ for f in $list; do ++ vr="`echo $f | sed -e 's/[^-]*-//' -e 's/-\([^0-9]*\)$/\.\1/' -e 's/-/~/g' -e 's/~\([^~]*\)$/-\1/'`" ++ l="$l $vr" ++ listvrf="$listvrf $vr:$f" ++ done ++ ++ list=$l ++} ++ ++post_sort () { ++ local l="" ++ local vr="" ++ local f="" ++ local found="" ++ ++ for i in $reverse_sorted_list; do ++ found="" ++ for vrf in $listvrf; do ++ vr=${vrf%%:*} ++ f=${vrf#*:} ++ if test x"$vr" = x"$i"; then ++ l="$l $f" ++ found=$vrf ++ break ++ fi ++ done ++ if test -n "$found"; then ++ listvrf="`echo $listvrf | (sed -e 's!'$found'!!' 2>/dev/null || echo $listvrf)`" ++ fi ++ done ++ ++ for vrf in $listvrf; do ++ f=${vrf#*:} ++ l="$l $f" ++ done ++ ++ reverse_sorted_list=$l ++} ++pre_sort + # Perform a reverse version sort on the entire list. + # Temporarily replace the '.old' suffix by ' 1' and append ' 2' for all + # other files to order the '.old' files after their non-old counterpart + # in reverse-sorted order. + + reverse_sorted_list=$(echo $list | tr ' ' '\n' | sed -e 's/\.old$/ 1/; / 1$/! s/$/ 2/' | version_sort -r | sed -e 's/ 1$/.old/; s/ 2$//') ++post_sort + + if [ "x$GRUB_TOP_LEVEL" != x ]; then + reverse_sorted_list=$(grub_move_to_front "$GRUB_TOP_LEVEL" ${reverse_sorted_list}) +--- a/util/grub.d/20_linux_xen.in ++++ b/util/grub.d/20_linux_xen.in +@@ -255,13 +255,57 @@ + # yet, so it's empty. In a submenu it will be equal to '\t' (one tab). + submenu_indentation="" + ++listvrf="" ++pre_sort () { ++ local l="" ++ ++ for f in $linux_list; do ++ vr="`echo $f | sed -e 's/[^-]*-//' -e 's/-\([^0-9]*\)$/\.\1/' -e 's/-/~/g' -e 's/~\([^~]*\)$/-\1/'`" ++ l="$l $vr" ++ listvrf="$listvrf $vr:$f" ++ done ++ ++ linux_list=$l ++} ++ ++post_sort () { ++ local l="" ++ local vr="" ++ local f="" ++ local found="" ++ ++ for i in $reverse_sorted_linux_list; do ++ found="" ++ for vrf in $listvrf; do ++ vr=${vrf%%:*} ++ f=${vrf#*:} ++ if test x"$vr" = x"$i"; then ++ l="$l $f" ++ found=$vrf ++ break ++ fi ++ done ++ if test -n "$found"; then ++ listvrf="`echo $listvrf | (sed -e 's!'$found'!!' 2>/dev/null || echo $listvrf)`" ++ fi ++ done ++ ++ for vrf in $listvrf; do ++ f=${vrf#*:} ++ l="$l $f" ++ done ++ ++ reverse_sorted_linux_list=$l ++} + # Perform a reverse version sort on the entire xen_list and linux_list. + # Temporarily replace the '.old' suffix by ' 1' and append ' 2' for all + # other files to order the '.old' files after their non-old counterpart + # in reverse-sorted order. + + reverse_sorted_xen_list=$(echo ${xen_list} | tr ' ' '\n' | sed -e 's/\.old$/ 1/; / 1$/! s/$/ 2/' | version_sort -r | sed -e 's/ 1$/.old/; s/ 2$//') ++pre_sort + reverse_sorted_linux_list=$(echo ${linux_list} | tr ' ' '\n' | sed -e 's/\.old$/ 1/; / 1$/! s/$/ 2/' | version_sort -r | sed -e 's/ 1$/.old/; s/ 2$//') ++post_sort + + if [ "x$GRUB_TOP_LEVEL_XEN" != x ]; then + reverse_sorted_xen_list=$(grub_move_to_front "$GRUB_TOP_LEVEL_XEN" ${reverse_sorted_xen_list}) diff --git a/grub2-util-30_os-prober-multiple-initrd.patch b/grub2-util-30_os-prober-multiple-initrd.patch new file mode 100644 index 0000000..7f1a49e --- /dev/null +++ b/grub2-util-30_os-prober-multiple-initrd.patch @@ -0,0 +1,11 @@ +--- a/util/grub.d/30_os-prober.in ++++ b/util/grub.d/30_os-prober.in +@@ -223,7 +223,7 @@ + + if [ "${LROOT}" != "${LBOOT}" ]; then + LKERNEL="${LKERNEL#/boot}" +- LINITRD="${LINITRD#/boot}" ++ LINITRD="$(echo $LINITRD | sed -e 's!^/boot!!' -e 's!\(\s\)/boot!\1!g')" + fi + + onstr="$(gettext_printf "(on %s)" "${DEVICE}")" diff --git a/grub2-vbe-blacklist-preferred-1440x900x32.patch b/grub2-vbe-blacklist-preferred-1440x900x32.patch new file mode 100644 index 0000000..1595992 --- /dev/null +++ b/grub2-vbe-blacklist-preferred-1440x900x32.patch @@ -0,0 +1,18 @@ +--- a/grub-core/video/i386/pc/vbe.c ++++ b/grub-core/video/i386/pc/vbe.c +@@ -1054,6 +1054,15 @@ + || vbe_mode_info.y_resolution > height) + /* Resolution exceeds that of preferred mode. */ + continue; ++ ++ /* Blacklist 1440x900x32 from preferred mode handling until a ++ better solution is available. This mode causes problems on ++ many Thinkpads. ++ */ ++ if (vbe_mode_info.x_resolution == 1440 && ++ vbe_mode_info.y_resolution == 900 && ++ vbe_mode_info.bits_per_pixel == 32) ++ continue; + } + else + { diff --git a/grub2-video-limit-the-resolution-for-fixed-bimap-font.patch b/grub2-video-limit-the-resolution-for-fixed-bimap-font.patch new file mode 100644 index 0000000..350cc28 --- /dev/null +++ b/grub2-video-limit-the-resolution-for-fixed-bimap-font.patch @@ -0,0 +1,86 @@ +From 5f7f27d1198ef425f4943cc10132509415bbaf55 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Thu, 24 Jan 2019 16:41:04 +0800 +Subject: [PATCH] video: limit the resolution for fixed bimap font + +As grub uses fixed bitmap font and also its size is a fixed property, it is not +possible to accommodate to all resolutions, therefore we raise some limit to +the preferred resolution as most themes are designed on popular device and the +resolution in its prime, which is supposedly Full HD. + +This change also makes grub font readable on hiDPI device without going through +the steps in. + +https://wiki.archlinux.org/index.php/HiDPI#GRUB + +v2: efi_gop: Avoid high resolution when trying to keep current mode. + +--- + grub-core/video/efi_gop.c | 7 +++++++ + grub-core/video/i386/pc/vbe.c | 8 +++++++- + 2 files changed, 14 insertions(+), 1 deletion(-) + +--- a/grub-core/video/efi_gop.c ++++ b/grub-core/video/efi_gop.c +@@ -358,7 +358,7 @@ + grub_err_t err; + unsigned bpp; + int found = 0; +- int avoid_low_resolution = 1; ++ int avoid_extreme_resolution = 1; + unsigned long long best_volume = 0; + unsigned int preferred_width = 0, preferred_height = 0; + grub_uint8_t *buffer; +@@ -375,13 +375,21 @@ + preferred_height = 600; + grub_errno = GRUB_ERR_NONE; + } ++ else ++ { ++ /* Limit the range of preferred resolution not exceeding FHD ++ to keep the fixed bitmap font readable */ ++ preferred_width = (preferred_width < 1920) ? preferred_width : 1920; ++ preferred_height = (preferred_height < 1080) ? preferred_height : 1080; ++ } + } + + again: + /* Keep current mode if possible. */ + if (gop->mode->info && +- (!avoid_low_resolution || +- (gop->mode->info->width >= 800 && gop->mode->info->height >= 600))) ++ (!avoid_extreme_resolution || ++ ((gop->mode->info->width >= 800 && gop->mode->info->height >= 600) && ++ (gop->mode->info->width <= 1920 && gop->mode->info->height <= 1080)))) + { + bpp = grub_video_gop_get_bpp (gop->mode->info); + if (bpp && ((width == gop->mode->info->width +@@ -454,9 +462,9 @@ + + if (!found) + { +- if (avoid_low_resolution && gop->mode->info) ++ if (avoid_extreme_resolution && gop->mode->info) + { +- avoid_low_resolution = 0; ++ avoid_extreme_resolution = 0; + goto again; + } + grub_dprintf ("video", "GOP: no mode found\n"); +--- a/grub-core/video/i386/pc/vbe.c ++++ b/grub-core/video/i386/pc/vbe.c +@@ -994,7 +994,13 @@ + { + grub_vbe_get_preferred_mode (&width, &height); + if (grub_errno == GRUB_ERR_NONE) +- preferred_mode = 1; ++ { ++ preferred_mode = 1; ++ /* Limit the range of preferred resolution not exceeding FHD ++ to keep the fixed bitmap font readable */ ++ width = (width < 1920) ? width : 1920; ++ height = (height < 1080) ? height : 1080; ++ } + else + { + /* Fall back to 640x480. This is conservative, but the largest diff --git a/grub2-xen-linux16.patch b/grub2-xen-linux16.patch new file mode 100644 index 0000000..bb8caa7 --- /dev/null +++ b/grub2-xen-linux16.patch @@ -0,0 +1,29 @@ +--- a/grub-core/loader/i386/xen.c ++++ b/grub-core/loader/i386/xen.c +@@ -961,7 +961,7 @@ + return grub_errno; + } + +-static grub_command_t cmd_xen, cmd_initrd, cmd_module, cmd_multiboot; ++static grub_command_t cmd_xen, cmd_initrd, cmd_module, cmd_multiboot, cmd_xen16, cmd_initrd16; + + GRUB_MOD_INIT (xen) + { +@@ -973,6 +973,10 @@ + 0, N_("Load initrd.")); + cmd_module = grub_register_command ("module", grub_cmd_module, + 0, N_("Load module.")); ++ cmd_xen16 = grub_register_command ("linux16", grub_cmd_xen, ++ 0, N_("Load Linux.")); ++ cmd_initrd16 = grub_register_command ("initrd16", grub_cmd_initrd, ++ 0, N_("Load initrd.")); + my_mod = mod; + } + +@@ -982,4 +986,6 @@ + grub_unregister_command (cmd_initrd); + grub_unregister_command (cmd_multiboot); + grub_unregister_command (cmd_module); ++ grub_unregister_command (cmd_xen16); ++ grub_unregister_command (cmd_initrd16); + } diff --git a/grub2-xen-pv-firmware.cfg b/grub2-xen-pv-firmware.cfg new file mode 100644 index 0000000..e8e02f7 --- /dev/null +++ b/grub2-xen-pv-firmware.cfg @@ -0,0 +1,158 @@ +insmod part_msdos +insmod part_gpt +insmod search +insmod configfile +insmod legacy_configfile +insmod lvm +insmod mdraid09 +insmod mdraid1x + +set debian_cddev="" +set debian_cdarch="" +if [ "${grub_cpu}" = "x86_64" ]; then + debian_cdarch="amd" +fi +if [ "${grub_cpu}" = "i386" ]; then + debian_cdarch="i386" +fi +if [ -n "${debian_cdarch}" ]; then + set debian_kern="/install.${debian_cdarch}/xen/vmlinuz" + set debian_initrd="/install.${debian_cdarch}/xen/initrd.gz" + search -s debian_domUcfg -f "/install.${debian_cdarch}/xen/debian.cfg" + search -s debian_cdkern -f "${debian_kern}" + search -s debian_cdinitrd -f "${debian_initrd}" + if [ -n "${debian_domUcfg}" -a -n "${debian_cdinitrd}" -a -n "${debian_cdkern}" -a "${debian_domUcfg}" = "${debian_cdinitrd}" -a "${debian_domUcfg}" = "${debian_cdkern}" ]; then + debian_cddev="${debian_domUcfg}" + fi +fi + +set fedora_cddev="" +if [ "${grub_cpu}" = "x86_64" ]; then + set fedora_kern="/images/pxeboot/vmlinuz" + set fedora_initrd="/images/pxeboot/initrd.img" + search -s fedora_cdkern -f "${fedora_kern}" + search -s fedora_cdinitrd -f "${fedora_initrd}" + if [ -n "${fedora_cdkern}" -a -n "${fedora_cdinitrd}" -a "${fedora_cdkern}" = "${fedora_cdinitrd}" ]; then + set fedora_cddev="${fedora_cdkern}" + fi +fi + +set suse_cddev="" +if [ "${grub_cpu}" = "i386" ]; then + set suse_cdarch="i586" +else + set suse_cdarch="${grub_cpu}" +fi +if [ -n "${suse_cdarch}" ]; then + set suse_kern="/boot/${suse_cdarch}/loader/linux" + set suse_initrd="/boot/${suse_cdarch}/loader/initrd" + search -s suse_cdkern -f "${suse_kern}" + search -s suse_cdinitrd -f "${suse_initrd}" + if [ -n "${suse_cdkern}" -a -n "${suse_cdinitrd}" -a "${suse_cdkern}" = "${suse_cdinitrd}" ]; then + set suse_cddev="${suse_cdkern}" + fi +fi + +set hdcfg_list="\ +/boot/grub2/grub.cfg \ +/grub2/grub.cfg\ +" + +set hdlst_list="\ +/boot/grub/menu.lst \ +/grub/menu.lst\ +" + +for c in ${hdcfg_list}; do + btrfs_relative_path=1 + if search -s hddev -f "${c}"; then + btrfs_relative_path=0 + menuentry "${hddev} Boot From Hard Disk (${c})" "${hddev}" "${c}" { + set root="${2}" + set cfg="${3}" + btrfs-get-default-subvol -p -o btrfs_default_subvol ($root) + if [ -n "${btrfs_default_subvol}" ]; then + configfile "${btrfs_default_subvol}${cfg}" + else + configfile "${cfg}" + fi + } + break + fi + btrfs_relative_path=0 +done + +for c in ${hdlst_list}; do + btrfs_relative_path=1 + if search -s hddev -f "${c}"; then + btrfs_relative_path=0 + menuentry "${hddev} Boot From Hard Disk (${c})" "${hddev}" "${c}" { + set root="${2}" + set cfg="${3}" + btrfs-get-default-subvol -p -o btrfs_default_subvol ($root) + if [ -n "${btrfs_default_subvol}" ]; then + legacy_configfile "${btrfs_default_subvol}${cfg}" + else + legacy_configfile "${cfg}" + fi + } + break + fi + btrfs_relative_path=0 +done + +set timeout=0 +if [ -n "${debian_cddev}" ]; then + set timeout=8 + menuentry "${debian_cddev} Debian Install" { + set root="${debian_cddev}" + linux "${debian_kern}" ignore_loglevel + initrd "${debian_initrd}" + } +fi + +if [ -n "${fedora_cddev}" ]; then + set timeout=8 + menuentry "${fedora_cddev} Fedora Install" { + set root="${fedora_cddev}" + linux "${fedora_kern}" ignore_loglevel + initrd "${fedora_initrd}" + } + menuentry "${fedora_cddev} Fedora Rescue" { + set root="${fedora_cddev}" + linux "${fedora_kern}" ignore_loglevel rescue + initrd "${fedora_initrd}" + } +fi + +if [ -n "${suse_cddev}" ]; then + set timeout=8 + set root="${suse_cddev}" + set suse_cdcfg="/boot/${suse_cdarch}/grub2-xen/grub.cfg" + if [ -e "/boot/${suse_cdarch}/vmlinuz-xen" ]; then + set suse_kern="/boot/${suse_cdarch}/vmlinuz-xen" + set suse_initrd="/boot/${suse_cdarch}/initrd-xen" + fi + if [ -f "${suse_cdcfg}" ]; then + menuentry "${suse_cddev} SUSE Install menu" { + set root="${suse_cddev}" + configfile "${suse_cdcfg}" + } + elif [ -f "${suse_kern}" -a -f "$suse_initrd" ]; then + menuentry "${suse_cddev} SUSE Install" { + linux "${suse_kern}" xencons=hvc0 + initrd "${suse_initrd}" + } + menuentry "${suse_cddev} SUSE Rescue" { + linux "${suse_kern}" xencons=hvc0 rescue=1 + initrd "${suse_initrd}" + } + menuentry "${suse_cddev} SUSE Upgrade" { + linux "${suse_kern}" xencons=hvc0 upgrade=1 + initrd "${suse_initrd}" + } + else + echo "the device ${suse_cddev} is not xen pv bootable" + fi +fi + diff --git a/grub2-zipl-setup-fix-btrfs-multipledev.patch b/grub2-zipl-setup-fix-btrfs-multipledev.patch new file mode 100644 index 0000000..d185727 --- /dev/null +++ b/grub2-zipl-setup-fix-btrfs-multipledev.patch @@ -0,0 +1,17 @@ +--- + util/s390x/zipl2grub.pl.in | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/util/s390x/zipl2grub.pl.in ++++ b/util/s390x/zipl2grub.pl.in +@@ -384,6 +384,10 @@ while ( ) { + } else { + $v = ""; + } ++ if ($k eq "GRUB_DEVICE" && $v !~ /^UUID/ && ! -e $v) { ++ s{root=\@$k\@}{}g; ++ next; ++ } + s{\@$k\@}{$v}g; + } + Info( 3, $_); diff --git a/grub2.changes b/grub2.changes new file mode 100644 index 0000000..9d0fcba --- /dev/null +++ b/grub2.changes @@ -0,0 +1,4289 @@ +------------------------------------------------------------------- +Thu Nov 30 09:41:10 UTC 2023 - Michael Chang + +- Fix reproducible build for grub.xen (bsc#1217619) + * 0001-mkstandalone-ensure-stable-timestamps-for-generated-.patch + * 0002-mkstandalone-ensure-deterministic-tar-file-creation-.patch + +------------------------------------------------------------------- +Wed Nov 22 09:25:23 UTC 2023 - Michael Chang + +- Fix unattended boot with TPM2 allows downgrading kernel and rootfs, also + enhancing the overall security posture (bsc#1216680) + * 0001-Improve-TPM-key-protection-on-boot-interruptions.patch + * 0002-Restrict-file-access-on-cryptodisk-print.patch + * 0003-Restrict-ls-and-auto-file-completion-on-cryptodisk-p.patch + * 0004-Key-revocation-on-out-of-bound-file-access.patch + +------------------------------------------------------------------- +Tue Nov 21 06:52:08 UTC 2023 - Michael Chang + +- grub2.spec: Fix openQA test failure in SLE-15-SP6 due to missing + font in memdisk + +------------------------------------------------------------------- +Thu Nov 16 06:39:46 UTC 2023 - Gary Ching-Pang Lin + +- Update the TPM2 patches to skip the persistent SRK handle if not + specified and improve the error messages + + 0003-protectors-Add-TPM2-Key-Protector.patch + + 0005-util-grub-protect-Add-new-tool.patch + + 0004-tpm2-Support-authorized-policy.patch + +------------------------------------------------------------------- +Tue Nov 14 07:52:41 UTC 2023 - Michael Chang + +- Fix XFS regression in 2.12~rc1 and support large extent counters + * 0001-fs-xfs-Incorrect-short-form-directory-data-boundary-.patch + * 0002-fs-xfs-Fix-XFS-directory-extent-parsing.patch + * 0003-fs-xfs-add-large-extent-counters-incompat-feature-su.patch + +------------------------------------------------------------------- +Mon Oct 30 07:15:17 UTC 2023 - Michael Chang + +- Fix fadump not working with 1GB/2GB/4GB LMB[P10] (bsc#1216253) + * 0001-kern-ieee1275-init-Restrict-high-memory-in-presence-.patch + +------------------------------------------------------------------- +Thu Oct 26 06:04:54 UTC 2023 - Gary Ching-Pang Lin + +- Fix a potential error when appending multiple keys into the + synthesized initrd + * Fix-the-size-calculation-for-the-synthesized-initrd.patch + +------------------------------------------------------------------- +Wed Oct 25 01:56:09 UTC 2023 - Michael Chang + +- Fix Xen chainloding error of no matching file path found (bsc#1216081) + * grub2-efi-chainload-harder.patch + +------------------------------------------------------------------- +Mon Oct 23 13:11:45 UTC 2023 - Michael Chang + +- Use grub-tpm2 token to unlock keyslots to make the unsealing process more + efficient and secure. + * 0001-luks2-Use-grub-tpm2-token-for-TPM2-protected-volume-.patch + +------------------------------------------------------------------- +Mon Oct 16 08:05:03 UTC 2023 - Michael Chang + +- Fix detection of encrypted disk's uuid in powerpc to cope with logical disks + when signed image installation is specified (bsc#1216075) + * 0003-grub-install-support-prep-environment-block.patch +- grub2.spec: Add support to unlocking multiple encrypted disks in signed + grub.elf image for logical disks + +------------------------------------------------------------------- +Fri Oct 6 05:06:59 UTC 2023 - Michael Chang + +- Fix CVE-2023-4692 (bsc#1215935) +- Fix CVE-2023-4693 (bsc#1215936) + * 0001-fs-ntfs-Fix-an-OOB-write-when-parsing-the-ATTRIBUTE_.patch + * 0002-fs-ntfs-Fix-an-OOB-read-when-reading-data-from-the-r.patch + * 0003-fs-ntfs-Fix-an-OOB-read-when-parsing-directory-entri.patch + * 0004-fs-ntfs-Fix-an-OOB-read-when-parsing-bitmaps-for-ind.patch + * 0005-fs-ntfs-Fix-an-OOB-read-when-parsing-a-volume-label.patch + * 0006-fs-ntfs-Make-code-more-readable.patch +- Bump upstream SBAT generation to 4 + +------------------------------------------------------------------- +Thu Oct 5 09:49:54 UTC 2023 - Fabian Vogt + +- Add patch to fix reading files from btrfs with "implicit" holes: + * 0001-fs-btrfs-Zero-file-data-not-backed-by-extents.patch + +------------------------------------------------------------------- +Mon Oct 2 14:30:49 UTC 2023 - Gary Ching-Pang Lin + +- Update the TPM 2.0 patches to support more RSA and ECC algorithms + * 0002-tpm2-Add-TPM-Software-Stack-TSS.patch + * 0003-protectors-Add-TPM2-Key-Protector.patch + * 0005-util-grub-protect-Add-new-tool.patch + +------------------------------------------------------------------- +Mon Oct 2 08:11:56 UTC 2023 - Michael Chang + +- Remove build require for gcc-32bit, target platform didn't rely on libgcc + function shipped with compiler but rather using functions supplied in grub + directly. + +------------------------------------------------------------------- +Fri Sep 29 08:38:13 UTC 2023 - Fabian Vogt + +- Add BuildIgnore to break cycle with the branding package + +------------------------------------------------------------------- +Wed Sep 27 03:37:10 UTC 2023 - Gary Ching-Pang Lin + +- Only build with fde-tpm-helper-rpm-macros for the architectures + supporting the newer UEFI and TPM 2.0. + * Also correct the location of %fde_tpm_update_requires + +------------------------------------------------------------------- +Wed Sep 20 07:54:05 UTC 2023 - Michael Chang + +- Fix a boot delay regression in PowerPC PXE boot (bsc#1201300) + * 0001-ieee1275-ofdisk-retry-on-open-and-read-failure.patch + +------------------------------------------------------------------- +Tue Sep 19 06:31:43 UTC 2023 - Gary Ching-Pang Lin + +- Add the new BuildRequires for EFI builds for the better FDE + support: fde-tpm-helper-rpm-macros + + Also add the the macros to %post and %posttrans + +------------------------------------------------------------------- +Mon Sep 11 13:17:20 UTC 2023 - Chester Lin + +- Correct the type of allocated EFI pages for ARM64 kernel (bsc#1215151) + * arm64-Use-proper-memory-type-for-kernel-allocation.patch + +------------------------------------------------------------------- +Thu Aug 31 19:09:33 UTC 2023 - Andreas Schwab + +- grub2-mkconfig-riscv64.patch: Handle riscv64 in mkconfig + +------------------------------------------------------------------- +Wed Aug 16 06:59:35 UTC 2023 - Gary Ching-Pang Lin + +- Implement NV index mode for TPM 2.0 key protector + 0001-protectors-Implement-NV-index.patch +- Fall back to passphrase mode when the key protector fails to + unlock the disk + 0002-cryptodisk-Fallback-to-passphrase.patch +- Wipe out the cached key cleanly + 0003-cryptodisk-wipe-out-the-cached-keys-from-protectors.patch +- Make diskfiler to look up cryptodisk devices first + 0004-diskfilter-look-up-cryptodisk-devices-first.patch + +------------------------------------------------------------------- +Thu Aug 3 03:24:41 UTC 2023 - Gary Ching-Pang Lin + +- Change the bash-completion directory (bsc#1213855) + * grub2-change-bash-completion-dir.patch + +------------------------------------------------------------------- +Thu Jul 27 06:16:36 UTC 2023 - Michael Chang + +- Version bump to 2.12~rc1 (PED-5589) + * Added: + - grub-2.12~rc1.tar.xz + * Removed: + - grub-2.06.tar.xz + * Patch dropped merged by new version: + - grub2-GRUB_CMDLINE_LINUX_RECOVERY-for-recovery-mode.patch + - grub2-s390x-02-kexec-module-added-to-emu.patch + - grub2-efi-chainloader-root.patch + - grub2-Fix-incorrect-netmask-on-ppc64.patch + - 0001-osdep-Introduce-include-grub-osdep-major.h-and-use-i.patch + - 0002-osdep-linux-hostdisk-Use-stat-instead-of-udevadm-for.patch + - 0002-net-read-bracketed-ipv6-addrs-and-port-numbers.patch + - grub2-s390x-10-keep-network-at-kexec.patch + - 0001-Fix-build-error-in-binutils-2.36.patch + - 0001-emu-fix-executable-stack-marking.patch + - 0046-squash-verifiers-Move-verifiers-API-to-kernel-image.patch + - 0001-30_uefi-firmware-fix-printf-format-with-null-byte.patch + - 0001-tpm-Pass-unknown-error-as-non-fatal-but-debug-print-.patch + - 0001-Filter-out-POSIX-locale-for-translation.patch + - 0001-disk-diskfilter-Use-nodes-in-logical-volume-s-segmen.patch + - 0001-fs-xfs-Fix-unreadable-filesystem-with-v4-superblock.patch + - 0001-fs-btrfs-Make-extent-item-iteration-to-handle-gaps.patch + - 0001-grub-mkconfig-restore-umask-for-grub.cfg.patch + - 0001-ieee1275-Drop-HEAP_MAX_ADDR-and-HEAP_MIN_SIZE-consta.patch + - 0002-ieee1275-claim-more-memory.patch + - 0003-ieee1275-request-memory-with-ibm-client-architecture.patch + - 0001-RISC-V-Adjust-march-flags-for-binutils-2.38.patch + - 0001-mkimage-Fix-dangling-pointer-may-be-used-error.patch + - 0002-Fix-Werror-array-bounds-array-subscript-0-is-outside.patch + - 0003-reed_solomon-Fix-array-subscript-0-is-outside-array-.patch + - 0001-powerpc-do-CAS-in-a-more-compatible-way.patch + - 0001-libc-config-merge-from-glibc.patch + - 0001-video-Remove-trailing-whitespaces.patch + - 0002-loader-efi-chainloader-Simplify-the-loader-state.patch + - 0003-commands-boot-Add-API-to-pass-context-to-loader.patch + - 0004-loader-efi-chainloader-Use-grub_loader_set_ex.patch + - 0005-kern-efi-sb-Reject-non-kernel-files-in-the-shim_lock.patch + - 0006-kern-file-Do-not-leak-device_name-on-error-in-grub_f.patch + - 0007-video-readers-png-Abort-sooner-if-a-read-operation-f.patch + - 0008-video-readers-png-Refuse-to-handle-multiple-image-he.patch + - 0009-video-readers-png-Drop-greyscale-support-to-fix-heap.patch + - 0010-video-readers-png-Avoid-heap-OOB-R-W-inserting-huff-.patch + - 0011-video-readers-png-Sanity-check-some-huffman-codes.patch + - 0012-video-readers-jpeg-Abort-sooner-if-a-read-operation-.patch + - 0013-video-readers-jpeg-Do-not-reallocate-a-given-huff-ta.patch + - 0014-video-readers-jpeg-Refuse-to-handle-multiple-start-o.patch + - 0015-video-readers-jpeg-Block-int-underflow-wild-pointer-.patch + - 0016-normal-charset-Fix-array-out-of-bounds-formatting-un.patch + - 0017-net-ip-Do-IP-fragment-maths-safely.patch + - 0018-net-netbuff-Block-overly-large-netbuff-allocs.patch + - 0019-net-dns-Fix-double-free-addresses-on-corrupt-DNS-res.patch + - 0020-net-dns-Don-t-read-past-the-end-of-the-string-we-re-.patch + - 0021-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch + - 0022-net-tftp-Avoid-a-trivial-UAF.patch + - 0023-net-http-Do-not-tear-down-socket-if-it-s-already-bee.patch + - 0024-net-http-Fix-OOB-write-for-split-http-headers.patch + - 0025-net-http-Error-out-on-headers-with-LF-without-CR.patch + - 0026-fs-f2fs-Do-not-read-past-the-end-of-nat-journal-entr.patch + - 0027-fs-f2fs-Do-not-read-past-the-end-of-nat-bitmap.patch + - 0028-fs-f2fs-Do-not-copy-file-names-that-are-too-long.patch + - 0029-fs-btrfs-Fix-several-fuzz-issues-with-invalid-dir-it.patch + - 0030-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch + - 0031-fs-btrfs-Fix-more-fuzz-issues-related-to-chunks.patch + - 0032-Use-grub_loader_set_ex-for-secureboot-chainloader.patch + - 0001-luks2-Add-debug-message-to-align-with-luks-and-geli-.patch + - 0002-cryptodisk-Refactor-to-discard-have_it-global.patch + - 0003-cryptodisk-Return-failure-in-cryptomount-when-no-cry.patch + - 0004-cryptodisk-Improve-error-messaging-in-cryptomount-in.patch + - 0005-cryptodisk-Improve-cryptomount-u-error-message.patch + - 0006-cryptodisk-Add-infrastructure-to-pass-data-from-cryp.patch + - 0007-cryptodisk-Refactor-password-input-out-of-crypto-dev.patch + - 0008-cryptodisk-Move-global-variables-into-grub_cryptomou.patch + - 0009-cryptodisk-Improve-handling-of-partition-name-in-cry.patch + - 0001-crytodisk-fix-cryptodisk-module-looking-up.patch + - 0001-devmapper-getroot-Have-devmapper-recognize-LUKS2.patch + - 0002-devmapper-getroot-Set-up-cheated-LUKS2-cryptodisk-mo.patch + - 0003-disk-cryptodisk-When-cheatmounting-use-the-sector-in.patch + - 0004-normal-menu-Don-t-show-Booting-s-msg-when-auto-booti.patch + - 0005-EFI-suppress-the-Welcome-to-GRUB-message-in-EFI-buil.patch + - 0006-EFI-console-Do-not-set-colorstate-until-the-first-te.patch + - 0007-EFI-console-Do-not-set-cursor-until-the-first-text-o.patch + - efi-set-variable-with-attrs.patch + - 0001-mm-Allow-dynamically-requesting-additional-memory-re.patch + - 0002-kern-efi-mm-Always-request-a-fixed-number-of-pages-o.patch + - 0003-kern-efi-mm-Extract-function-to-add-memory-regions.patch + - 0004-kern-efi-mm-Pass-up-errors-from-add_memory_regions.patch + - 0005-kern-efi-mm-Implement-runtime-addition-of-pages.patch + - 0001-kern-efi-mm-Enlarge-the-default-heap-size.patch + - 0002-mm-Defer-the-disk-cache-invalidation.patch + - 0001-grub-install-set-point-of-no-return-for-powerpc-ieee1275.patch + - 0001-commands-efi-tpm-Refine-the-status-of-log-event.patch + - 0002-commands-efi-tpm-Use-grub_strcpy-instead-of-grub_mem.patch + - 0003-efi-tpm-Add-EFI_CC_MEASUREMENT_PROTOCOL-support.patch + - 0001-ibmvtpm-Add-support-for-trusted-boot-using-a-vTPM-2..patch + - 0002-ieee1275-implement-vec5-for-cas-negotiation.patch + - 0001-font-Reject-glyphs-exceeds-font-max_glyph_width-or-f.patch + - 0002-font-Fix-size-overflow-in-grub_font_get_glyph_intern.patch + - 0003-font-Fix-several-integer-overflows-in-grub_font_cons.patch + - 0004-font-Remove-grub_font_dup_glyph.patch + - 0005-font-Fix-integer-overflow-in-ensure_comb_space.patch + - 0006-font-Fix-integer-overflow-in-BMP-index.patch + - 0007-font-Fix-integer-underflow-in-binary-search-of-char-.patch + - 0008-fbutil-Fix-integer-overflow.patch + - 0009-font-Fix-an-integer-underflow-in-blit_comb.patch + - 0010-font-Harden-grub_font_blit_glyph-and-grub_font_blit_.patch + - 0011-font-Assign-null_font-to-glyphs-in-ascii_font_glyph.patch + - 0012-normal-charset-Fix-an-integer-overflow-in-grub_unico.patch + - 0001-fs-btrfs-Use-full-btrfs-bootloader-area.patch + - 0001-ieee1275-Increase-initially-allocated-heap-from-1-4-.patch + - 0001-grub-core-modify-sector-by-sysfs-as-disk-sector.patch + - grub2-add-module-for-boot-loader-interface.patch + - 0001-ieee1275-Further-increase-initially-allocated-heap-f.patch + - 0002-tpm-Disable-tpm-verifier-if-tpm-is-not-present.patch + - 0001-RISC-V-Handle-R_RISCV_CALL_PLT-reloc.patch + - 0001-loader-linux-Ensure-the-newc-pathname-is-NULL-termin.patch + - 0001-kern-ieee1275-init-Convert-plain-numbers-to-constant.patch + - 0002-kern-ieee1275-init-Extended-support-in-Vec5.patch + - 0001-fs-ext2-Ignore-checksum-seed-incompat-feature.patch + - 0001-fs-ext2-Ignore-the-large_dir-incompat-feature.patch + * Patch modified to new base version: + - use-grub2-as-a-package-name.patch + - grub2-fix-menu-in-xen-host-server.patch + - grub2-secureboot-add-linuxefi.patch + - grub2-secureboot-chainloader.patch + - grub2-s390x-01-Changes-made-and-files-added-in-order-to-allow-s390x.patch + - grub2-s390x-03-output-7-bit-ascii.patch + - grub2-s390x-04-grub2-install.patch + - grub2-use-rpmsort-for-version-sorting.patch + - grub2-getroot-treat-mdadm-ddf-as-simple-device.patch + - grub2-grubenv-in-btrfs-header.patch + - grub2-commands-introduce-read_file-subcommand.patch + - grub2-efi-chainload-harder.patch + - grub2-emu-4-all.patch + - grub2-util-30_os-prober-multiple-initrd.patch + - grub2-install-fix-not-a-directory-error.patch + - grub-install-force-journal-draining-to-ensure-data-i.patch + - grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch + - grub2-btrfs-04-grub2-install.patch + - grub2-btrfs-05-grub2-mkconfig.patch + - grub2-btrfs-06-subvol-mount.patch + - grub2-efi-xen-chainload.patch + - grub2-efi-xen-cmdline.patch + - grub2-efi-xen-removable.patch + - grub2-suse-remove-linux-root-param.patch + - grub2-ppc64le-disable-video.patch + - grub2-install-remove-useless-check-PReP-partition-is-empty.patch + - 0004-efinet-UEFI-IPv6-PXE-support.patch + - 0007-efinet-Setting-network-from-UEFI-device-path.patch + - 0008-efinet-Setting-DNS-server-from-UEFI-protocol.patch + - 0001-add-support-for-UEFI-network-protocols.patch + - grub2-mkconfig-default-entry-correction.patch + - grub2-s390x-11-secureboot.patch + - grub2-secureboot-install-signed-grub.patch + - grub2-gfxmenu-support-scrolling-menu-entry-s-text.patch + - 0002-cmdline-Provide-cmdline-functions-as-module.patch + - 0001-efi-linux-provide-linux-command.patch + - 0001-Add-support-for-Linux-EFI-stub-loading-on-aarch64.patch + - 0004-arm-arm64-loader-Better-memory-allocation-and-error-.patch + - 0002-Arm-check-for-the-PE-magic-for-the-compiled-arch.patch + - 0001-Factor-out-grub_efi_linux_boot.patch + - 0003-Handle-multi-arch-64-on-32-boot-in-linuxefi-loader.patch + - 0015-test_asn1-test-module-for-libtasn1.patch + - 0021-appended-signatures-documentation.patch + - 0022-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch + - 0003-grub-install-support-prep-environment-block.patch + - 0004-Introduce-prep_load_env-command.patch + - 0001-grub-install-bailout-root-device-probing.patch + - 0001-install-fix-software-raid1-on-esp.patch + - 0001-ofdisk-improve-boot-time-by-lookup-boot-disk-first.patch + - 0001-protectors-Add-key-protectors-framework.patch + - 0002-tpm2-Add-TPM-Software-Stack-TSS.patch + - 0004-cryptodisk-Support-key-protectors.patch + - 0008-linuxefi-Use-common-grub_initrd_load.patch + - 0009-Add-crypttab_entry-to-obviate-the-need-to-input-pass.patch + - grub-read-pcr.patch + - tpm-record-pcrs.patch + - 0001-clean-up-crypttab-and-linux-modules-dependency.patch + * Patch refreshed: + - rename-grub-info-file-to-grub2.patch + - grub2-linux.patch + - grub2-simplefb.patch + - grub2-ppc-terminfo.patch + - grub2-pass-corret-root-for-nfsroot.patch + - grub2-efi-HP-workaround.patch + - grub2-secureboot-no-insmod-on-sb.patch + - grub2-linuxefi-fix-boot-params.patch + - grub2-s390x-05-grub2-mkconfig.patch + - grub2-xen-linux16.patch + - grub2-efi-disable-video-cirrus-and-bochus.patch + - grub2-vbe-blacklist-preferred-1440x900x32.patch + - grub2-mkconfig-aarch64.patch + - grub2-menu-unrestricted.patch + - grub2-mkconfig-arm.patch + - grub2-s390x-06-loadparm.patch + - grub2-s390x-07-add-image-param-for-zipl-setup.patch + - grub2-s390x-08-workaround-part-to-disk.patch + - grub2-diskfilter-support-pv-without-metadatacopies.patch + - grub2-getroot-support-nvdimm.patch + - grub2-s390x-skip-zfcpdump-image.patch + - grub2-btrfs-02-export-subvolume-envvars.patch + - grub2-btrfs-03-follow_default.patch + - grub2-btrfs-07-subvol-fallback.patch + - grub2-btrfs-08-workaround-snapshot-menu-default-entry.patch + - grub2-btrfs-09-get-default-subvolume.patch + - grub2-btrfs-10-config-directory.patch + - grub2-efi-xen-cfg-unquote.patch + - grub2-Add-hidden-menu-entries.patch + - grub2-SUSE-Add-the-t-hotkey.patch + - grub2-ppc64le-memory-map.patch + - grub2-ppc64-cas-reboot-support.patch + - grub2-ppc64-cas-new-scope.patch + - grub2-ppc64-cas-fix-double-free.patch + - 0003-bootp-New-net_bootp6-command.patch + - 0005-grub.texi-Add-net_bootp6-doument.patch + - 0006-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch + - 0012-tpm-Build-tpm-as-module.patch + - 0002-AUDIT-0-http-boot-tracker-bug.patch + - grub2-btrfs-help-on-snapper-rollback.patch + - grub2-video-limit-the-resolution-for-fixed-bimap-font.patch + - 0001-kern-mm.c-Make-grub_calloc-inline.patch + - 0001-Unify-the-check-to-enable-btrfs-relative-path.patch + - 0002-arm64-make-sure-fdt-has-address-cells-and-size-cells.patch + - 0003-Make-grub_error-more-verbose.patch + - 0001-ieee1275-Avoiding-many-unecessary-open-close.patch + - 0001-Workaround-volatile-efi-boot-variable.patch + - 0001-templates-Follow-the-path-of-usr-merged-kernel-confi.patch + - 0004-Try-to-pick-better-locations-for-kernel-and-initrd.patch + - 0004-Add-suport-for-signing-grub-with-an-appended-signatu.patch + - 0005-docs-grub-Document-signing-grub-under-UEFI.patch + - 0006-docs-grub-Document-signing-grub-with-an-appended-sig.patch + - 0007-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch + - 0008-pgp-factor-out-rsa_pad.patch + - 0010-posix_wrap-tweaks-in-preparation-for-libtasn1.patch + - 0011-libtasn1-import-libtasn1-4.18.0.patch + - 0014-libtasn1-compile-into-asn1-module.patch + - 0016-grub-install-support-embedding-x509-certificates.patch + - 0017-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch + - 0018-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch + - 0019-appended-signatures-support-verifying-appended-signa.patch + - 0020-appended-signatures-verification-tests.patch + - 0001-grub-install-Add-SUSE-signed-image-support-for-power.patch + - 0002-Add-grub_disk_write_tail-helper-function.patch + - 0005-export-environment-at-start-up.patch + - 0001-Fix-infinite-boot-loop-on-headless-system-in-qemu.patch + - 0003-protectors-Add-TPM2-Key-Protector.patch + - 0005-util-grub-protect-Add-new-tool.patch + - 0010-templates-import-etc-crypttab-to-grub.cfg.patch + - grub-install-record-pcrs.patch + - safe_tpm_pcr_snapshot.patch + - 0002-Mark-environmet-blocks-as-used-for-image-embedding.patch + - 0001-grub2-Set-multiple-device-path-for-a-nvmf-boot-devic.patch + - 0002-discard-cached-key-before-entering-grub-shell-and-ed.patch + - 0001-ieee1275-ofdisk-retry-on-open-and-read-failure.patch + - 0002-Restrict-cryptsetup-key-file-permission-for-better-s.patch + * New: + - 0001-xen_boot-add-missing-grub_arch_efi_linux_load_image_.patch + - 0001-font-Try-memdisk-fonts-with-the-same-name.patch + - 0001-Make-grub.cfg-compatible-to-old-binaries.patch + - 0001-disk-cryptodisk-Fix-missing-change-when-updating-to-.patch + * Embedding fonts in the grub.efi to get signed for secure boot + +------------------------------------------------------------------- +Wed Jul 26 03:04:25 UTC 2023 - Michael Chang + +- Fix error message "unknown command tpm_record_pcrs" with encrypted boot and + no tpm device present (bsc#1213547) + * 0002-tpm-Disable-tpm-verifier-if-tpm-is-not-present.patch + +------------------------------------------------------------------- +Tue May 30 11:03:54 UTC 2023 - Dirk Müller + +- add 0001-fs-ext2-Ignore-checksum-seed-incompat-feature.patch, + 0001-fs-ext2-Ignore-the-large_dir-incompat-feature.patch: + * support more featureful extX filesystems (backport from + upstream git) + +------------------------------------------------------------------- +Thu May 4 06:58:12 UTC 2023 - Michael Chang + +- grub2-once: Fix 'sh: terminal_output: command not found' error (bsc#1204563) + +------------------------------------------------------------------- +Wed Apr 26 07:22:03 UTC 2023 - Gary Ching-Pang Lin + +- Exclude the deprecated EFI location, /usr/lib64/efi/, from + Tumbleweed and ALP + +------------------------------------------------------------------- +Fri Apr 21 07:53:30 UTC 2023 - Gary Ching-Pang Lin + +- Update TPM 2.0 key unsealing patches + * Add the new upstreaming patches + 0001-protectors-Add-key-protectors-framework.patch + 0002-tpm2-Add-TPM-Software-Stack-TSS.patch + 0003-protectors-Add-TPM2-Key-Protector.patch + 0004-cryptodisk-Support-key-protectors.patch + 0005-util-grub-protect-Add-new-tool.patch + * Add the authorized policy patches based on the upstreaming + patches + 0001-tpm2-Add-TPM2-types-structures-and-command-constants.patch + 0002-tpm2-Add-more-marshal-unmarshal-functions.patch + 0003-tpm2-Implement-more-TPM2-commands.patch + 0004-tpm2-Support-authorized-policy.patch + * Drop the old patches + 0010-protectors-Add-key-protectors-framework.patch + 0011-tpm2-Add-TPM-Software-Stack-TSS.patch + 0012-protectors-Add-TPM2-Key-Protector.patch + 0013-cryptodisk-Support-key-protectors.patch + 0014-util-grub-protect-Add-new-tool.patch + fix-tpm2-build.patch + tpm-protector-dont-measure-sealed-key.patch + tpm-protector-export-secret-key.patch + grub-unseal-debug.patch + 0001-tpm2-adjust-the-input-parameters-of-TPM2_EvictContro.patch + 0002-tpm2-declare-the-input-arguments-of-TPM2-functions-a.patch + 0003-tpm2-resend-the-command-on-TPM_RC_RETRY.patch + 0004-tpm2-add-new-TPM2-types-structures-and-command-const.patch + 0005-tpm2-add-more-marshal-unmarshal-functions.patch + 0006-tpm2-check-the-command-parameters-of-TPM2-commands.patch + 0007-tpm2-pack-the-missing-authorization-command-for-TPM2.patch + 0008-tpm2-allow-some-command-parameters-to-be-NULL.patch + 0009-tpm2-remove-the-unnecessary-variables.patch + 0010-tpm2-add-TPM2-commands-to-support-authorized-policy.patch + 0011-tpm2-make-the-file-reading-unmarshal-functions-gener.patch + 0012-tpm2-initialize-the-PCR-selection-list-early.patch + 0013-tpm2-support-unsealing-key-with-authorized-policy.patch + * Refresh grub-read-pcr.patch + * Introduce a new build requirement: libtasn1-devel +- Only package grub2-protect for the architectures with EFI support + +------------------------------------------------------------------- +Fri Apr 21 04:53:54 UTC 2023 - Michael Chang + +- Fix PowerVS deployment fails to boot with 90 cores (bsc#1208581) + * 0001-kern-ieee1275-init-Convert-plain-numbers-to-constant.patch + * 0002-kern-ieee1275-init-Extended-support-in-Vec5.patch + +------------------------------------------------------------------- +Tue Apr 18 02:42:23 UTC 2023 - Michael Chang + +- Fix no prep partition error on non-PReP architectures by making the + prep_loadenv module exclusive to powerpc_ieee1275 platform (bsc#1210489) + * 0004-Introduce-prep_load_env-command.patch +- Fix the issue of freeing an uninitialized pointer + * 0002-prep_loadenv-Fix-regex-for-Open-Firmware-device-spec.patch +- Rediff + * 0005-export-environment-at-start-up.patch + * 0009-Add-crypttab_entry-to-obviate-the-need-to-input-pass.patch + +------------------------------------------------------------------- +Tue Apr 11 11:13:26 UTC 2023 - Michael Chang + +- Resolve some issues with OS boot failure on PPC NVMe-oF disks and made + enhancements to PPC secure boot's root device discovery config (bsc#1207230) +- Ensure get_devargs and get_devname functions are consistent + * 0001-openfw-Ensure-get_devargs-and-get_devname-functions-.patch +- Fix regex for Open Firmware device specifier with encoded commas + * 0002-prep_loadenv-Fix-regex-for-Open-Firmware-device-spec.patch +- Fix regular expression in PPC secure boot config to prevent escaped commas + from being treated as delimiters when retrieving partition substrings. +- Use prep_load_env in PPC secure boot config to handle unset host-specific + environment variables and ensure successful command execution. + * 0004-Introduce-prep_load_env-command.patch +- Refreshed + * 0005-export-environment-at-start-up.patch + +------------------------------------------------------------------- +Thu Mar 23 05:25:50 UTC 2023 - Michael Chang + +- Fix aarch64 kiwi image's file not found due to '/@' prepended to path in + btrfs filesystem. (bsc#1209165) + * grub2-btrfs-05-grub2-mkconfig.patch + +------------------------------------------------------------------- +Mon Mar 20 05:02:01 UTC 2023 - Michael Chang + +- Restrict cryptsetup key file permission for better security (bsc#1207499) + * 0001-loader-linux-Ensure-the-newc-pathname-is-NULL-termin.patch + * 0002-Restrict-cryptsetup-key-file-permission-for-better-s.patch + +------------------------------------------------------------------- +Wed Mar 15 21:46:00 UTC 2023 - Hans-Peter Jansen + +- Meanwhile, memtest86+ gained EFI support, but using the grub + command line to run it manually is quite tedious... + Adapt 20_memtest86+ to provide a proper menu entry. Executing + memtest requires to turn security off in BIOS: (Boot Mode: Other OS). + +------------------------------------------------------------------- +Mon Mar 13 15:43:01 UTC 2023 - rw@suse.com + +- Tolerate kernel moved out of /boot. (bsc#1184804) + * grub2-s390x-12-zipl-setup-usrmerge.patch + +------------------------------------------------------------------- +Mon Mar 6 06:31:09 UTC 2023 - Michael Chang + +- Discard cached key from grub shell and editor mode + * 0001-clean-up-crypttab-and-linux-modules-dependency.patch + * 0002-discard-cached-key-before-entering-grub-shell-and-ed.patch + +------------------------------------------------------------------- +Fri Mar 3 07:48:56 UTC 2023 - Michael Chang + +- Make grub more robust against storage race condition causing system boot + failures (bsc#1189036) + * 0001-ieee1275-ofdisk-retry-on-open-and-read-failure.patch + +------------------------------------------------------------------- +Wed Mar 1 02:58:07 UTC 2023 - Michael Chang + +- Fix riscv64 error for relocation 0x13 is not implemented yet + * 0001-RISC-V-Handle-R_RISCV_CALL_PLT-reloc.patch + +------------------------------------------------------------------- +Wed Feb 22 07:08:44 UTC 2023 - Michael Chang + +- Fix out of memory error on lpar installation from virtual cdrom (bsc#1208024) + * 0001-ieee1275-Further-increase-initially-allocated-heap-f.patch + * 0002-tpm-Disable-tpm-verifier-if-tpm-is-not-present.patch +- Fix lpar got hung at grub after inactive migration (bsc#1207684) + * 0002-ieee1275-implement-vec5-for-cas-negotiation.patch +- Rediff + * safe_tpm_pcr_snapshot.patch +- Patch supersceded + * 0001-tpm-Disable-tpm-verifier-if-tpm-is-not-present.patch + +------------------------------------------------------------------- +Wed Feb 15 07:09:39 UTC 2023 - Gary Ching-Pang Lin + +- Refresh 0003-tpm2-resend-the-command-on-TPM_RC_RETRY.patch to + handle the TPM2 responseCode correctly. + +------------------------------------------------------------------- +Fri Feb 10 14:54:35 UTC 2023 - Valentin Lefebvre + +- Add module for boot loader interface. Needed for load Unified Kernel + Image (UKI) + * grub2-add-module-for-boot-loader-interface.patch + +------------------------------------------------------------------- +Thu Feb 9 08:42:26 UTC 2023 - Gary Ching-Pang Lin + +- Amend the TPM2 stack and add authorized policy mode to + tpm2_key_protector + * 0001-tpm2-adjust-the-input-parameters-of-TPM2_EvictContro.patch + * 0002-tpm2-declare-the-input-arguments-of-TPM2-functions-a.patch + * 0003-tpm2-resend-the-command-on-TPM_RC_RETRY.patch + * 0004-tpm2-add-new-TPM2-types-structures-and-command-const.patch + * 0005-tpm2-add-more-marshal-unmarshal-functions.patch + * 0006-tpm2-check-the-command-parameters-of-TPM2-commands.patch + * 0007-tpm2-pack-the-missing-authorization-command-for-TPM2.patch + * 0008-tpm2-allow-some-command-parameters-to-be-NULL.patch + * 0009-tpm2-remove-the-unnecessary-variables.patch + * 0010-tpm2-add-TPM2-commands-to-support-authorized-policy.patch + * 0011-tpm2-make-the-file-reading-unmarshal-functions-gener.patch + * 0012-tpm2-initialize-the-PCR-selection-list-early.patch + * 0013-tpm2-support-unsealing-key-with-authorized-policy.patch + +------------------------------------------------------------------- +Wed Feb 8 02:24:16 UTC 2023 - Michael Chang + +- Fix nvmf boot device setup (bsc#1207811) + * 0001-grub2-Can-t-setup-a-default-boot-device-correctly-on.patch + +------------------------------------------------------------------- +Tue Feb 7 02:11:47 UTC 2023 - Michael Chang + +- Fix unknown filesystem error on disks with 4096 sector size (bsc#1207064) + * 0001-grub-core-modify-sector-by-sysfs-as-disk-sector.patch + +------------------------------------------------------------------- +Sat Feb 4 05:57:02 UTC 2023 - Michael Chang + +- Fix GCC 13 build failure (bsc#1201089) + * 0002-AUDIT-0-http-boot-tracker-bug.patch + +------------------------------------------------------------------- +Tue Jan 3 02:48:05 UTC 2023 - Gary Ching-Pang Lin + +- Move unsupported zfs modules into 'extras' packages + (bsc#1205554) (PED-2947) + +------------------------------------------------------------------- +Fri Dec 30 07:58:54 UTC 2022 - Michael Chang + +- Fix inappropriately including commented lines in crypttab (bsc#1206279) + * 0010-templates-import-etc-crypttab-to-grub.cfg.patch + +------------------------------------------------------------------- +Fri Dec 23 09:50:42 UTC 2022 - Michael Chang + +- Make grub.cfg invariant to efi and legacy platforms (bsc#1205200) +- Removed patch linuxefi + * grub2-secureboot-provide-linuxefi-config.patch + * grub2-secureboot-use-linuxefi-on-uefi-in-os-prober.patch + * grub2-secureboot-use-linuxefi-on-uefi.patch +- Rediff + * grub2-btrfs-05-grub2-mkconfig.patch + * grub2-efi-xen-cmdline.patch + * grub2-s390x-05-grub2-mkconfig.patch + * grub2-suse-remove-linux-root-param.patch + +------------------------------------------------------------------- +Mon Dec 19 08:39:05 UTC 2022 - Michael Chang + +- Setup multiple device paths for a nvmf boot device (bsc#1205666) + * 0001-grub2-Set-multiple-device-path-for-a-nvmf-boot-devic.patch + +------------------------------------------------------------------- +Fri Dec 16 01:51:45 UTC 2022 - Gary Ching-Pang Lin + +- Increase the path buffer in the crypttab command for the long + volume name (bsc#1206333) + * grub2-increase-crypttab-path-buffer.patch + +------------------------------------------------------------------- +Mon Dec 5 08:47:06 UTC 2022 - Michael Chang + +- Add tpm to signed grub.elf image (PED-1990) (bsc#1205912) +- Increase initial heap size from 1/4 to 1/3 + * 0001-ieee1275-Increase-initially-allocated-heap-from-1-4-.patch + +------------------------------------------------------------------- +Tue Nov 22 08:11:17 UTC 2022 - Michael Chang + +- Make full utilization of btrfs bootloader area (bsc#1161823) + * 0001-fs-btrfs-Use-full-btrfs-bootloader-area.patch + * 0002-Mark-environmet-blocks-as-used-for-image-embedding.patch +- Patch removed + * 0001-i386-pc-build-btrfs-zstd-support-into-separate-modul.patch + +------------------------------------------------------------------- +Mon Nov 21 02:10:28 UTC 2022 - Michael Chang + +- Fix regression of reverting back to asking password twice when a keyfile is + already used (bsc#1205309) + * 0010-templates-import-etc-crypttab-to-grub.cfg.patch + +------------------------------------------------------------------- +Wed Nov 16 02:36:23 UTC 2022 - Michael Chang + +- Security fixes and hardenings + * 0001-font-Reject-glyphs-exceeds-font-max_glyph_width-or-f.patch + * 0002-font-Fix-size-overflow-in-grub_font_get_glyph_intern.patch +- Fix CVE-2022-2601 (bsc#1205178) + * 0003-font-Fix-several-integer-overflows-in-grub_font_cons.patch + * 0004-font-Remove-grub_font_dup_glyph.patch + * 0005-font-Fix-integer-overflow-in-ensure_comb_space.patch + * 0006-font-Fix-integer-overflow-in-BMP-index.patch + * 0007-font-Fix-integer-underflow-in-binary-search-of-char-.patch + * 0008-fbutil-Fix-integer-overflow.patch +- Fix CVE-2022-3775 (bsc#1205182) + * 0009-font-Fix-an-integer-underflow-in-blit_comb.patch + * 0010-font-Harden-grub_font_blit_glyph-and-grub_font_blit_.patch + * 0011-font-Assign-null_font-to-glyphs-in-ascii_font_glyph.patch + * 0012-normal-charset-Fix-an-integer-overflow-in-grub_unico.patch +- Bump upstream SBAT generation to 3 + +------------------------------------------------------------------- +Mon Nov 14 09:54:16 UTC 2022 - Michael Chang + +- Removed 0001-linux-fix-efi_relocate_kernel-failure.patch as reported + regression in some hardware being stuck in initrd loading (bsc#1205380) + +------------------------------------------------------------------- +Mon Nov 14 03:03:35 UTC 2022 - Michael Chang + +- Fix password asked twice if third field in crypttab not present (bsc#1205312) + * 0009-Add-crypttab_entry-to-obviate-the-need-to-input-pass.patch + +------------------------------------------------------------------- +Fri Oct 28 04:58:28 UTC 2022 - Michael Chang + +- NVMeoFC support on grub (jsc#PED-996) + * 0001-ieee1275-add-support-for-NVMeoFC.patch + * 0002-ieee1275-ofpath-enable-NVMeoF-logical-device-transla.patch + * 0003-ieee1275-change-the-logic-of-ieee1275_get_devargs.patch + * 0004-ofpath-controller-name-update.patch +- TDX: Enhance grub2 measurement to TD RTMR (jsc#PED-1265) + * 0001-commands-efi-tpm-Refine-the-status-of-log-event.patch + * 0002-commands-efi-tpm-Use-grub_strcpy-instead-of-grub_mem.patch + * 0003-efi-tpm-Add-EFI_CC_MEASUREMENT_PROTOCOL-support.patch +- Measure the kernel on POWER10 and extend TPM PCRs (PED-1990) + * 0001-ibmvtpm-Add-support-for-trusted-boot-using-a-vTPM-2..patch + * 0002-ieee1275-implement-vec5-for-cas-negotiation.patch +- Fix efi pcr snapshot related funtion is defined but not used on powerpc + platform. + * safe_tpm_pcr_snapshot.patch + +------------------------------------------------------------------- +Mon Oct 24 01:58:08 UTC 2022 - Michael Chang + +- Include loopback into signed grub2 image (jsc#PED-2150) + +------------------------------------------------------------------- +Thu Oct 6 07:13:23 UTC 2022 - Michael Chang + +- Fix firmware oops after disk decrypting failure (bsc#1204037) + * 0009-Add-crypttab_entry-to-obviate-the-need-to-input-pass.patch + +------------------------------------------------------------------- +Fri Sep 23 10:13:54 UTC 2022 - Michael Chang + +- Add patch to fix kernel relocation error in low memory + * 0001-linux-fix-efi_relocate_kernel-failure.patch + +------------------------------------------------------------------- +Mon Sep 19 04:07:36 UTC 2022 - Michael Chang + +- Add safety measure to pcr snapshot by checking platform and tpm status + * safe_tpm_pcr_snapshot.patch + +------------------------------------------------------------------- +Fri Sep 16 03:56:14 UTC 2022 - Michael Chang + +- Fix installation failure due to unavailable nvram device on + ppc64le (bsc#1201361) + * 0001-grub-install-set-point-of-no-return-for-powerpc-ieee1275.patch + +------------------------------------------------------------------- +Fri Sep 16 03:12:36 UTC 2022 - Gary Ching-Pang Lin + +- Add patches to dynamically allocate additional memory regions for + EFI systems (bsc#1202438) + * 0001-mm-Allow-dynamically-requesting-additional-memory-re.patch + * 0002-kern-efi-mm-Always-request-a-fixed-number-of-pages-o.patch + * 0003-kern-efi-mm-Extract-function-to-add-memory-regions.patch + * 0004-kern-efi-mm-Pass-up-errors-from-add_memory_regions.patch + * 0005-kern-efi-mm-Implement-runtime-addition-of-pages.patch +- Enlarge the default heap size and defer the disk cache + invalidation (bsc#1202438) + * 0001-kern-efi-mm-Enlarge-the-default-heap-size.patch + * 0002-mm-Defer-the-disk-cache-invalidation.patch + +------------------------------------------------------------------- +Thu Sep 15 09:51:07 UTC 2022 - Michael Chang + +- Add patches for ALP FDE support + * 0001-devmapper-getroot-Have-devmapper-recognize-LUKS2.patch + * 0002-devmapper-getroot-Set-up-cheated-LUKS2-cryptodisk-mo.patch + * 0003-disk-cryptodisk-When-cheatmounting-use-the-sector-in.patch + * 0004-normal-menu-Don-t-show-Booting-s-msg-when-auto-booti.patch + * 0005-EFI-suppress-the-Welcome-to-GRUB-message-in-EFI-buil.patch + * 0006-EFI-console-Do-not-set-colorstate-until-the-first-te.patch + * 0007-EFI-console-Do-not-set-cursor-until-the-first-text-o.patch + * 0008-linuxefi-Use-common-grub_initrd_load.patch + * 0009-Add-crypttab_entry-to-obviate-the-need-to-input-pass.patch + * 0010-templates-import-etc-crypttab-to-grub.cfg.patch + * grub-read-pcr.patch + * efi-set-variable-with-attrs.patch + * tpm-record-pcrs.patch + * tpm-protector-dont-measure-sealed-key.patch + * tpm-protector-export-secret-key.patch + * grub-install-record-pcrs.patch + * grub-unseal-debug.patch + +------------------------------------------------------------------- +Mon Aug 29 03:48:55 UTC 2022 - Michael Chang + +- Fix out of memory error cannot be prevented via disabling tpm (bsc#1202438) + * 0001-tpm-Disable-tpm-verifier-if-tpm-is-not-present.patch + +------------------------------------------------------------------- +Thu Aug 18 02:47:28 UTC 2022 - Michael Chang + +- Fix tpm error stop tumbleweed from booting (bsc#1202374) + * 0001-tpm-Pass-unknown-error-as-non-fatal-but-debug-print-.patch +- Patch Removed + * 0001-tpm-Log-EFI_VOLUME_FULL-and-continue.patch + +------------------------------------------------------------------- +Wed Jun 8 03:25:26 UTC 2022 - Michael Chang + +- Add tpm, tpm2, luks2 and gcry_sha512 to default grub.efi (bsc#1197625) +- Make grub-tpm.efi a symlink to grub.efi + * grub2.spec +- Log error when tpm event log is full and continue + * 0001-tpm-Log-EFI_VOLUME_FULL-and-continue.patch +- Patch superseded + * 0001-tpm-Pass-unknown-error-as-non-fatal-but-debug-print-.patch + +------------------------------------------------------------------- +Wed Jun 8 03:17:29 UTC 2022 - Michael Chang + +- Add patches for automatic TPM disk unlock (jsc#SLE-24018) (bsc#1196668) (jsc#PED-1276) + * 0001-luks2-Add-debug-message-to-align-with-luks-and-geli-.patch + * 0002-cryptodisk-Refactor-to-discard-have_it-global.patch + * 0003-cryptodisk-Return-failure-in-cryptomount-when-no-cry.patch + * 0004-cryptodisk-Improve-error-messaging-in-cryptomount-in.patch + * 0005-cryptodisk-Improve-cryptomount-u-error-message.patch + * 0006-cryptodisk-Add-infrastructure-to-pass-data-from-cryp.patch + * 0007-cryptodisk-Refactor-password-input-out-of-crypto-dev.patch + * 0008-cryptodisk-Move-global-variables-into-grub_cryptomou.patch + * 0009-cryptodisk-Improve-handling-of-partition-name-in-cry.patch + * 0010-protectors-Add-key-protectors-framework.patch + * 0011-tpm2-Add-TPM-Software-Stack-TSS.patch + * 0012-protectors-Add-TPM2-Key-Protector.patch + * 0013-cryptodisk-Support-key-protectors.patch + * 0014-util-grub-protect-Add-new-tool.patch +- Fix no disk unlocking happen (bsc#1196668) + * 0001-crytodisk-fix-cryptodisk-module-looking-up.patch +- Fix build error + * fix-tpm2-build.patch + +------------------------------------------------------------------- +Tue May 31 04:44:18 UTC 2022 - Michael Chang + +- Security fixes and hardenings for boothole 3 / boothole 2022 (bsc#1198581) + * 0001-video-Remove-trailing-whitespaces.patch + * 0002-loader-efi-chainloader-Simplify-the-loader-state.patch + * 0003-commands-boot-Add-API-to-pass-context-to-loader.patch +- Fix CVE-2022-28736 (bsc#1198496) + * 0004-loader-efi-chainloader-Use-grub_loader_set_ex.patch +- Fix CVE-2022-28735 (bsc#1198495) + * 0005-kern-efi-sb-Reject-non-kernel-files-in-the-shim_lock.patch + * 0006-kern-file-Do-not-leak-device_name-on-error-in-grub_f.patch + * 0007-video-readers-png-Abort-sooner-if-a-read-operation-f.patch + * 0008-video-readers-png-Refuse-to-handle-multiple-image-he.patch +- Fix CVE-2021-3695 (bsc#1191184) + * 0009-video-readers-png-Drop-greyscale-support-to-fix-heap.patch +- Fix CVE-2021-3696 (bsc#1191185) + * 0010-video-readers-png-Avoid-heap-OOB-R-W-inserting-huff-.patch + * 0011-video-readers-png-Sanity-check-some-huffman-codes.patch + * 0012-video-readers-jpeg-Abort-sooner-if-a-read-operation-.patch + * 0013-video-readers-jpeg-Do-not-reallocate-a-given-huff-ta.patch + * 0014-video-readers-jpeg-Refuse-to-handle-multiple-start-o.patch +- Fix CVE-2021-3697 (bsc#1191186) + * 0015-video-readers-jpeg-Block-int-underflow-wild-pointer-.patch + * 0016-normal-charset-Fix-array-out-of-bounds-formatting-un.patch +- Fix CVE-2022-28733 (bsc#1198460) + * 0017-net-ip-Do-IP-fragment-maths-safely.patch + * 0018-net-netbuff-Block-overly-large-netbuff-allocs.patch + * 0019-net-dns-Fix-double-free-addresses-on-corrupt-DNS-res.patch + * 0020-net-dns-Don-t-read-past-the-end-of-the-string-we-re-.patch + * 0021-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch + * 0022-net-tftp-Avoid-a-trivial-UAF.patch + * 0023-net-http-Do-not-tear-down-socket-if-it-s-already-bee.patch +- Fix CVE-2022-28734 (bsc#1198493) + * 0024-net-http-Fix-OOB-write-for-split-http-headers.patch +- Fix CVE-2022-28734 (bsc#1198493) + * 0025-net-http-Error-out-on-headers-with-LF-without-CR.patch + * 0026-fs-f2fs-Do-not-read-past-the-end-of-nat-journal-entr.patch + * 0027-fs-f2fs-Do-not-read-past-the-end-of-nat-bitmap.patch + * 0028-fs-f2fs-Do-not-copy-file-names-that-are-too-long.patch + * 0029-fs-btrfs-Fix-several-fuzz-issues-with-invalid-dir-it.patch + * 0030-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch + * 0031-fs-btrfs-Fix-more-fuzz-issues-related-to-chunks.patch + * 0032-Use-grub_loader_set_ex-for-secureboot-chainloader.patch +- Bump grub's SBAT generation to 2 + +------------------------------------------------------------------- +Tue May 31 04:41:44 UTC 2022 - Michael Chang + +- Use boot disks in OpenFirmware, fixing regression caused by + 0001-ieee1275-implement-FCP-methods-for-WWPN-and-LUNs.patch, when + the root LV is completely in the boot LUN (bsc#1197948) + * 0001-ofdisk-improve-boot-time-by-lookup-boot-disk-first.patch + +------------------------------------------------------------------- +Thu May 26 10:10:56 UTC 2022 - Michael Chang + +- Fix error message in displaying help on bootable snapshot (bsc#1199609) + +------------------------------------------------------------------- +Tue May 17 10:46:38 UTC 2022 - Michael Chang + +- Fix installation over serial console ends up in infinite boot loop + (bsc#1187810) (bsc#1209667) (bsc#1209372) + * 0001-Fix-infinite-boot-loop-on-headless-system-in-qemu.patch +- Fix ppc64le build error for new IEEE long double ABI + * 0001-libc-config-merge-from-glibc.patch + +------------------------------------------------------------------- +Thu Apr 21 09:35:15 UTC 2022 - Michael Chang + +- Fix Power10 LPAR error "The partition fails to activate as partition went + into invalid state" (bsc#1198714) + * 0001-powerpc-do-CAS-in-a-more-compatible-way.patch + +------------------------------------------------------------------- +Mon Apr 11 11:50:04 UTC 2022 - Ludwig Nussel + +- use common SBAT values (boo#1193282) + +------------------------------------------------------------------- +Fri Mar 25 03:46:55 UTC 2022 - Michael Chang + +- Fix wrong order in kernel sorting of listing rc before final release + (bsc#1197376) + * grub2-use-rpmsort-for-version-sorting.patch + +------------------------------------------------------------------- +Fri Mar 18 09:10:07 UTC 2022 - Michael Chang + +- Fix duplicated insmod part_gpt lines in grub.cfg (bsc#1197186) + * 0001-grub-probe-Deduplicate-probed-partmap-output.patch + +------------------------------------------------------------------- +Wed Mar 16 14:57:02 UTC 2022 - Michael Chang + +- Fix GCC 12 build failure (bsc#1196546) + * 0001-mkimage-Fix-dangling-pointer-may-be-used-error.patch + * 0002-Fix-Werror-array-bounds-array-subscript-0-is-outside.patch + * 0003-reed_solomon-Fix-array-subscript-0-is-outside-array-.patch +- Revised + * grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch + * 0002-ieee1275-powerpc-enables-device-mapper-discovery.patch + +------------------------------------------------------------------- +Fri Mar 11 09:30:05 UTC 2022 - Michael Chang + +- Fix grub-install error when efi system partition is created as mdadm software + raid1 device (bsc#1179981) (bsc#1195204) + * 0001-install-fix-software-raid1-on-esp.patch + +------------------------------------------------------------------- +Thu Mar 10 09:10:23 UTC 2022 - Michael Chang + +- Fix riscv64 build error + * 0001-RISC-V-Adjust-march-flags-for-binutils-2.38.patch + +------------------------------------------------------------------- +Thu Mar 10 07:08:52 UTC 2022 - Michael Chang + +- Fix error in grub-install when linux root device is on lvm thin volume + (bsc#1192622) (bsc#1191974) + * 0001-grub-install-bailout-root-device-probing.patch + +------------------------------------------------------------------- +Fri Mar 4 03:37:40 UTC 2022 - Michael Chang + +- Support saving grub environment for POWER signed grub images (jsc#SLE-23854) + * 0001-Add-grub_envblk_buf-helper-function.patch + * 0002-Add-grub_disk_write_tail-helper-function.patch + * 0003-grub-install-support-prep-environment-block.patch + * 0004-Introduce-prep_load_env-command.patch + * 0005-export-environment-at-start-up.patch +- Use enviroment variable in early boot config to looking up root device + * grub2.spec + +------------------------------------------------------------------- +Tue Mar 1 08:55:57 UTC 2022 - Michal Suchanek + +- Remove obsolete openSUSE 12.2 conditionals in spec file +- Clean up powerpc certificate handling. + +------------------------------------------------------------------- +Thu Feb 10 16:20:24 UTC 2022 - Bjørn Lie + +- Set grub2-check-default shebang to "#!/bin/bash", as the the code + uses many instructions which are undefined for a POSIX sh. + (boo#1195794). + +------------------------------------------------------------------- +Fri Jan 14 08:39:36 UTC 2022 - Michael Chang + +- Power guest secure boot with static keys: GRUB2 signing portion + (jsc#SLE-18271) (bsc#1192764) + * 0001-grub-install-Add-SUSE-signed-image-support-for-power.patch + +------------------------------------------------------------------- +Thu Jan 13 06:36:44 UTC 2022 - Michael Chang + +- Fix wrong default entry when booting snapshot (bsc#1159205) + * grub2-btrfs-08-workaround-snapshot-menu-default-entry.patch + +------------------------------------------------------------------- +Tue Jan 11 03:49:15 UTC 2022 - Michael Chang + +- Power guest secure boot with static keys: GRUB2 signing portion + (jsc#SLE-18271) (bsc#1192764) + * grub2.spec +- Power guest secure boot with static keys: GRUB2 portion (jsc#SLE-18144) + (bsc#1192686) + * 0001-ieee1275-Drop-HEAP_MAX_ADDR-and-HEAP_MIN_SIZE-consta.patch + * 0002-ieee1275-claim-more-memory.patch + * 0003-ieee1275-request-memory-with-ibm-client-architecture.patch + * 0004-Add-suport-for-signing-grub-with-an-appended-signatu.patch + * 0005-docs-grub-Document-signing-grub-under-UEFI.patch + * 0006-docs-grub-Document-signing-grub-with-an-appended-sig.patch + * 0007-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch + * 0008-pgp-factor-out-rsa_pad.patch + * 0009-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch + * 0010-posix_wrap-tweaks-in-preparation-for-libtasn1.patch + * 0011-libtasn1-import-libtasn1-4.18.0.patch + * 0012-libtasn1-disable-code-not-needed-in-grub.patch + * 0013-libtasn1-changes-for-grub-compatibility.patch + * 0014-libtasn1-compile-into-asn1-module.patch + * 0015-test_asn1-test-module-for-libtasn1.patch + * 0016-grub-install-support-embedding-x509-certificates.patch + * 0017-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch + * 0018-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch + * 0019-appended-signatures-support-verifying-appended-signa.patch + * 0020-appended-signatures-verification-tests.patch + * 0021-appended-signatures-documentation.patch + * 0022-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch + * 0023-x509-allow-Digitial-Signature-plus-other-Key-Usages.patch + +------------------------------------------------------------------- +Mon Jan 10 09:38:46 UTC 2022 - Michael Chang + +- Fix no menuentry is found if hibernation on btrfs RAID1 (bsc#1193090) + * grub2-systemd-sleep-plugin + +------------------------------------------------------------------- +Tue Dec 21 03:03:47 UTC 2021 - Michael Chang + +- Fix CVE-2021-3981 (bsc#1189644) + * 0001-grub-mkconfig-restore-umask-for-grub.cfg.patch + +------------------------------------------------------------------- +Fri Dec 17 10:42:33 UTC 2021 - Michael Chang + +- Fix can't allocate initrd error (bsc#1191378) + * 0001-Factor-out-grub_efi_linux_boot.patch + * 0002-Fix-race-in-EFI-validation.patch + * 0003-Handle-multi-arch-64-on-32-boot-in-linuxefi-loader.patch + * 0004-Try-to-pick-better-locations-for-kernel-and-initrd.patch + * 0005-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch + * 0006-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch + * 0007-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch + * 0008-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch + * 0009-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch + * 0010-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch + * 0011-Also-define-GRUB_EFI_MAX_ALLOCATION_ADDRESS-for-RISC.patch + +------------------------------------------------------------------- +Wed Dec 8 14:16:58 UTC 2021 - Michal Suchanek + +- Add support for simplefb (boo#1193532). + + grub2-simplefb.patch + +------------------------------------------------------------------- +Mon Dec 6 01:21:07 UTC 2021 - Michael Chang + +- Fix extent not found when initramfs contains shared extents (bsc#1190982) + * 0001-fs-btrfs-Make-extent-item-iteration-to-handle-gaps.patch + +------------------------------------------------------------------- +Thu Nov 11 07:45:11 UTC 2021 - Michael Chang + +- Fix arm64 kernel image not aligned on 64k boundary (bsc#1192522) + * 0001-arm64-Fix-EFI-loader-kernel-image-allocation.patch + * 0002-Arm-check-for-the-PE-magic-for-the-compiled-arch.patch + +------------------------------------------------------------------- +Thu Oct 21 12:51:46 UTC 2021 - Michael Chang + +- Remove openSUSE Tumbleweed specific handling for default grub + distributor (bsc#1191198) +- Use /usr/lib/os-release as fallback (bsc#1191196) + * grub2-default-distributor.patch + * grub2-check-default.sh +- VUL-0: grub2: grub2-once uses fixed file name in /var/tmp (bsc#1190474) (CVE-2021-46705) + * grub2-once + * grub2-once.service +- Fix unknown TPM error on buggy uefi firmware (bsc#1191504) + * 0001-tpm-Pass-unknown-error-as-non-fatal-but-debug-print-.patch +- Fix error /boot/grub2/locale/POSIX.gmo not found (bsc#1189769) + * 0001-Filter-out-POSIX-locale-for-translation.patch +- Fix error lvmid disk cannot be found after second disk added to the root + volume group (bsc#1189874) (bsc#1071559) + * 0001-ieee1275-implement-FCP-methods-for-WWPN-and-LUNs.patch +- Fix error in grub installation due to unnecessary requirement to support + excessive device for the root logical volume (bsc#1184135) + * 0001-disk-diskfilter-Use-nodes-in-logical-volume-s-segmen.patch +- Fix regression in reading xfs v4 + *0001-fs-xfs-Fix-unreadable-filesystem-with-v4-superblock.patch + +------------------------------------------------------------------- +Tue Oct 19 08:26:50 UTC 2021 - Fabian Vogt + +- Fix installation on usrmerged s390x + +------------------------------------------------------------------- +Wed Sep 22 14:29:12 UTC 2021 - rw@suse.com + +- Improve support for SLE Micro 5.1 on s390x. (bsc#1190395) + * amend grub2-s390x-04-grub2-install.patch + * refresh grub2-s390x-11-secureboot.patch + +------------------------------------------------------------------- +Tue Sep 7 02:32:30 UTC 2021 - Michael Chang + +- Follow usr merge for looking up kernel config (bsc#1189782) (bsc#1190061) + * 0001-templates-Follow-the-path-of-usr-merged-kernel-confi.patch + +------------------------------------------------------------------- +Wed Sep 1 05:49:47 UTC 2021 - Michael Chang + +- Add btrfs zstd compression on i386-pc and also make sure it won't break + existing grub installations (bsc#1161823) + * deleted 0001-btrfs-disable-zstd-support-for-i386-pc.patch + * added 0001-i386-pc-build-btrfs-zstd-support-into-separate-modul.patch + +------------------------------------------------------------------- +Tue Aug 31 05:56:56 UTC 2021 - Petr Vorel + +- Delete the author list from %description (the %description section is + literally for package descriptions (only) these days, encoding was also + problematic). +- Add %doc AUTHORS to get packaged that info + +------------------------------------------------------------------- +Wed Aug 4 10:28:49 UTC 2021 - Stefan Seyfried + +- update grub2-systemd-sleep.sh to fix hibernation by avoiding the + error "no kernelfile matching the running kernel found" on + usrmerged setup + +------------------------------------------------------------------- +Wed Aug 4 08:36:25 UTC 2021 - Fabian Vogt + +- Use %autosetup + +------------------------------------------------------------------- +Thu Jul 22 16:43:20 UTC 2021 - Petr Vorel + +- Replace grub2-use-stat-instead-of-udevadm-for-partition-lookup.patch and + fix-grub2-use-stat-instead-of-udevadm-for-partition-lookup-with-new-glibc.patch + with upstream backport: + 0001-osdep-Introduce-include-grub-osdep-major.h-and-use-i.patch and + 0002-osdep-linux-hostdisk-Use-stat-instead-of-udevadm-for.patch. + +------------------------------------------------------------------- +Mon Jun 28 10:14:26 UTC 2021 - Michael Chang + +- Fix error not a btrfs filesystem on s390x (bsc#1187645) + * 80_suse_btrfs_snapshot + +------------------------------------------------------------------- +Wed Jun 23 07:41:57 UTC 2021 - Michael Chang + +- Fix error gfxterm isn't found with multiple terminals (bsc#1187565) + * grub2-fix-error-terminal-gfxterm-isn-t-found.patch + +------------------------------------------------------------------- +Mon Jun 21 10:45:40 UTC 2021 - Michael Chang + +- Fix boot failure after kdump due to the content of grub.cfg is not + completed with pending modificaton in xfs journal (bsc#1186975) + * grub-install-force-journal-draining-to-ensure-data-i.patch +- Patch refreshed + * grub2-mkconfig-default-entry-correction.patch + +------------------------------------------------------------------- +Thu Jun 3 11:17:49 UTC 2021 - Michael Chang + +- Version bump to 2.06 + * rediff + - 0001-add-support-for-UEFI-network-protocols.patch + - 0002-net-read-bracketed-ipv6-addrs-and-port-numbers.patch + - 0003-Make-grub_error-more-verbose.patch + - 0003-bootp-New-net_bootp6-command.patch + - 0005-grub.texi-Add-net_bootp6-doument.patch + - 0006-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch + - 0006-efi-Set-image-base-address-before-jumping-to-the-PE-.patch + - 0008-efinet-Setting-DNS-server-from-UEFI-protocol.patch + - 0046-squash-verifiers-Move-verifiers-API-to-kernel-image.patch + - grub-install-force-journal-draining-to-ensure-data-i.patch + - grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch + - grub2-diskfilter-support-pv-without-metadatacopies.patch + - grub2-efi-HP-workaround.patch + - grub2-efi-xen-cfg-unquote.patch + - grub2-efi-xen-chainload.patch + - grub2-fix-menu-in-xen-host-server.patch + - grub2-gfxmenu-support-scrolling-menu-entry-s-text.patch + - grub2-install-remove-useless-check-PReP-partition-is-empty.patch + - grub2-lvm-allocate-metadata-buffer-from-raw-contents.patch + - grub2-mkconfig-default-entry-correction.patch + - grub2-pass-corret-root-for-nfsroot.patch + - grub2-s390x-03-output-7-bit-ascii.patch + - grub2-s390x-04-grub2-install.patch + - grub2-secureboot-install-signed-grub.patch + - grub2-setup-try-fs-embed-if-mbr-gap-too-small.patch + - use-grub2-as-a-package-name.patch + * update by patch squashed: + - 0001-Add-support-for-Linux-EFI-stub-loading-on-aarch64.patch + - grub2-efi-chainload-harder.patch + - grub2-secureboot-no-insmod-on-sb.patch + - grub2-secureboot-chainloader.patch + - grub2-secureboot-add-linuxefi.patch + * remove squashed patches: + - 0008-squash-Add-support-for-Linux-EFI-stub-loading-on-aar.patch + - 0009-squash-Add-support-for-linuxefi.patch + - 0041-squash-Add-secureboot-support-on-efi-chainloader.patch + - 0042-squash-grub2-efi-chainload-harder.patch + - 0043-squash-Don-t-allow-insmod-when-secure-boot-is-enable.patch + - 0045-squash-Add-support-for-Linux-EFI-stub-loading-on-aar.patch + * drop upstream patches: + - 0001-Warn-if-MBR-gap-is-small-and-user-uses-advanced-modu.patch + - 0001-include-grub-i386-linux.h-Include-missing-grub-types.patch + - 0001-kern-efi-sb-Add-chainloaded-image-as-shim-s-verifiab.patch + - 0001-mdraid1x_linux-Fix-gcc10-error-Werror-array-bounds.patch + - 0001-normal-Move-common-datetime-functions-out-of-the-nor.patch + - 0001-yylex-Make-lexer-fatal-errors-actually-be-fatal.patch + - 0002-efi-Make-shim_lock-GUID-and-protocol-type-public.patch + - 0002-grub-install-Avoid-incompleted-install-on-i386-pc.patch + - 0002-kern-Add-X-option-to-printf-functions.patch + - 0002-safemath-Add-some-arithmetic-primitives-that-check-f.patch + - 0002-zfs-Fix-gcc10-error-Werror-zero-length-bounds.patch + - 0003-calloc-Make-sure-we-always-have-an-overflow-checking.patch + - 0003-efi-Return-grub_efi_status_t-from-grub_efi_get_varia.patch + - 0003-normal-main-Search-for-specific-config-files-for-net.patch + - 0004-calloc-Use-calloc-at-most-places.patch + - 0004-datetime-Enable-the-datetime-module-for-the-emu-plat.patch + - 0004-efi-Add-a-function-to-read-EFI-variables-with-attrib.patch + - 0005-Make-linux_arm_kernel_header.hdr_offset-be-at-the-ri.patch + - 0005-efi-Add-secure-boot-detection.patch + - 0005-malloc-Use-overflow-checking-primitives-where-we-do-.patch + - 0006-efi-Only-register-shim_lock-verifier-if-shim_lock-pr.patch + - 0006-iso9660-Don-t-leak-memory-on-realloc-failures.patch + - 0007-font-Do-not-load-more-than-one-NAME-section.patch + - 0007-verifiers-Move-verifiers-API-to-kernel-image.patch + - 0008-efi-Move-the-shim_lock-verifier-to-the-GRUB-core.patch + - 0008-script-Remove-unused-fields-from-grub_script_functio.patch + - 0009-kern-Add-lockdown-support.patch + - 0009-script-Avoid-a-use-after-free-when-redefining-a-func.patch + - 0010-kern-lockdown-Set-a-variable-if-the-GRUB-is-locked-d.patch + - 0010-linux-Fix-integer-overflows-in-initrd-size-handling.patch + - 0011-efi-Lockdown-the-GRUB-when-the-UEFI-Secure-Boot-is-e.patch + - 0012-efi-Use-grub_is_lockdown-instead-of-hardcoding-a-dis.patch + - 0013-acpi-Don-t-register-the-acpi-command-when-locked-dow.patch + - 0014-mmap-Don-t-register-cutmem-and-badram-commands-when-.patch + - 0015-commands-Restrict-commands-that-can-load-BIOS-or-DT-.patch + - 0016-commands-setpci-Restrict-setpci-command-when-locked-.patch + - 0017-commands-hdparm-Restrict-hdparm-command-when-locked-.patch + - 0018-gdb-Restrict-GDB-access-when-locked-down.patch + - 0019-loader-xnu-Don-t-allow-loading-extension-and-package.patch + - 0020-dl-Only-allow-unloading-modules-that-are-not-depende.patch + - 0021-usb-Avoid-possible-out-of-bound-accesses-caused-by-m.patch + - 0022-lib-arg-Block-repeated-short-options-that-require-an.patch + - 0023-commands-menuentry-Fix-quoting-in-setparams_prefix.patch + - 0024-kern-parser-Fix-resource-leak-if-argc-0.patch + - 0025-kern-parser-Fix-a-memory-leak.patch + - 0026-kern-parser-Introduce-process_char-helper.patch + - 0027-kern-parser-Introduce-terminate_arg-helper.patch + - 0028-kern-parser-Refactor-grub_parser_split_cmdline-clean.patch + - 0029-kern-buffer-Add-variable-sized-heap-buffer.patch + - 0030-kern-parser-Fix-a-stack-buffer-overflow.patch + - 0031-util-mkimage-Remove-unused-code-to-add-BSS-section.patch + - 0032-util-mkimage-Use-grub_host_to_target32-instead-of-gr.patch + - 0033-util-mkimage-Always-use-grub_host_to_target32-to-ini.patch + - 0034-util-mkimage-Unify-more-of-the-PE32-and-PE32-header-.patch + - 0035-util-mkimage-Reorder-PE-optional-header-fields-set-u.patch + - 0036-util-mkimage-Improve-data_size-value-calculation.patch + - 0037-util-mkimage-Refactor-section-setup-to-use-a-helper.patch + - 0038-util-mkimage-Add-an-option-to-import-SBAT-metadata-i.patch + - 0039-grub-install-common-Add-sbat-option.patch + - 0040-shim_lock-Only-skip-loading-shim_lock-verifier-with-.patch + - grub-install-define-default-platform-for-risc-v.patch + - grub2-editenv-add-warning-message.patch + - grub2-efi-gop-add-blt.patch + - grub2-efi-uga-64bit-fb.patch + - grub2-verifiers-fix-system-freeze-if-verify-failed.patch + - risc-v-add-clzdi2-symbol.patch + - risc-v-fix-computation-of-pc-relative-relocation-offset.patch +- Add grub2-instdev-fixup.pl for correcting /etc/default/grub_installdevice to + use disk devie if grub has been installed to it +- Add 0001-30_uefi-firmware-fix-printf-format-with-null-byte.patch to fix + detection of efi fwsetup support + +------------------------------------------------------------------- +Mon May 31 07:18:56 UTC 2021 - Michael Chang + +- Fix running grub2-once leads to failure of starting systemd service in the + boot sequence (bsc#1169460) + * grub2-once + * grub2-once.service + +------------------------------------------------------------------- +Fri May 28 15:16:37 UTC 2021 - Michael Chang + +- Fix crash in launching gfxmenu without theme file (bsc#1186481) + * grub2-gfxmenu-support-scrolling-menu-entry-s-text.patch + +------------------------------------------------------------------- +Tue May 11 02:14:06 UTC 2021 - Michael Chang + +- Fix plaintext password in grub config didn't work to unlock menu entry if + enabling secure boot in UEFI (bsc#1181892) + +------------------------------------------------------------------- +Fri Apr 23 03:27:37 UTC 2021 - Michael Chang + +- Fix obsolete syslog in systemd unit file and updating to use journal as + StandardOutput (bsc#1185149) + * grub2-once.service + +------------------------------------------------------------------- +Mon Apr 19 09:53:43 UTC 2021 - Michael Chang + +- Fix build error on armv6/armv7 (bsc#1184712) + * 0001-emu-fix-executable-stack-marking.patch + +------------------------------------------------------------------- +Thu Apr 8 12:32:52 UTC 2021 - Michael Chang + +- Fix error grub_file_filters not found in Azure virtual machine (bsc#1182012) + * 0001-Workaround-volatile-efi-boot-variable.patch + +------------------------------------------------------------------- +Tue Mar 16 02:57:12 UTC 2021 - Michael Chang + +- Fix powerpc-ieee1275 lpar takes long time to boot with increasing number of + nvme namespace (bsc#1177751) + 0001-ieee1275-Avoiding-many-unecessary-open-close.patch + +------------------------------------------------------------------- +Thu Mar 11 02:00:15 UTC 2021 - Michael Chang + +- Fix chainloading windows on dual boot machine (bsc#1183073) + * 0001-kern-efi-sb-Add-chainloaded-image-as-shim-s-verifiab.patch + +------------------------------------------------------------------- +Fri Feb 26 06:52:18 UTC 2021 - Michael Chang + +- VUL-0: grub2,shim: implement new SBAT method (bsc#1182057) + * 0031-util-mkimage-Remove-unused-code-to-add-BSS-section.patch + * 0032-util-mkimage-Use-grub_host_to_target32-instead-of-gr.patch + * 0033-util-mkimage-Always-use-grub_host_to_target32-to-ini.patch + * 0034-util-mkimage-Unify-more-of-the-PE32-and-PE32-header-.patch + * 0035-util-mkimage-Reorder-PE-optional-header-fields-set-u.patch + * 0036-util-mkimage-Improve-data_size-value-calculation.patch + * 0037-util-mkimage-Refactor-section-setup-to-use-a-helper.patch + * 0038-util-mkimage-Add-an-option-to-import-SBAT-metadata-i.patch + * 0039-grub-install-common-Add-sbat-option.patch +- Fix CVE-2021-20225 (bsc#1182262) + * 0022-lib-arg-Block-repeated-short-options-that-require-an.patch +- Fix CVE-2020-27749 (bsc#1179264) + * 0024-kern-parser-Fix-resource-leak-if-argc-0.patch + * 0025-kern-parser-Fix-a-memory-leak.patch + * 0026-kern-parser-Introduce-process_char-helper.patch + * 0027-kern-parser-Introduce-terminate_arg-helper.patch + * 0028-kern-parser-Refactor-grub_parser_split_cmdline-clean.patch + * 0029-kern-buffer-Add-variable-sized-heap-buffer.patch + * 0030-kern-parser-Fix-a-stack-buffer-overflow.patch +- Fix CVE-2021-20233 (bsc#1182263) + * 0023-commands-menuentry-Fix-quoting-in-setparams_prefix.patch +- Fix CVE-2020-25647 (bsc#1177883) + * 0021-usb-Avoid-possible-out-of-bound-accesses-caused-by-m.patch +- Fix CVE-2020-25632 (bsc#1176711) + * 0020-dl-Only-allow-unloading-modules-that-are-not-depende.patch +- Fix CVE-2020-27779, CVE-2020-14372 (bsc#1179265) (bsc#1175970) + * 0001-include-grub-i386-linux.h-Include-missing-grub-types.patch + * 0002-efi-Make-shim_lock-GUID-and-protocol-type-public.patch + * 0003-efi-Return-grub_efi_status_t-from-grub_efi_get_varia.patch + * 0004-efi-Add-a-function-to-read-EFI-variables-with-attrib.patch + * 0005-efi-Add-secure-boot-detection.patch + * 0006-efi-Only-register-shim_lock-verifier-if-shim_lock-pr.patch + * 0007-verifiers-Move-verifiers-API-to-kernel-image.patch + * 0008-efi-Move-the-shim_lock-verifier-to-the-GRUB-core.patch + * 0009-kern-Add-lockdown-support.patch + * 0010-kern-lockdown-Set-a-variable-if-the-GRUB-is-locked-d.patch + * 0011-efi-Lockdown-the-GRUB-when-the-UEFI-Secure-Boot-is-e.patch + * 0012-efi-Use-grub_is_lockdown-instead-of-hardcoding-a-dis.patch + * 0013-acpi-Don-t-register-the-acpi-command-when-locked-dow.patch + * 0014-mmap-Don-t-register-cutmem-and-badram-commands-when-.patch + * 0015-commands-Restrict-commands-that-can-load-BIOS-or-DT-.patch + * 0016-commands-setpci-Restrict-setpci-command-when-locked-.patch + * 0017-commands-hdparm-Restrict-hdparm-command-when-locked-.patch + * 0018-gdb-Restrict-GDB-access-when-locked-down.patch + * 0019-loader-xnu-Don-t-allow-loading-extension-and-package.patch + * 0040-shim_lock-Only-skip-loading-shim_lock-verifier-with-.patch + * 0041-squash-Add-secureboot-support-on-efi-chainloader.patch + * 0042-squash-grub2-efi-chainload-harder.patch + * 0043-squash-Don-t-allow-insmod-when-secure-boot-is-enable.patch + * 0044-squash-kern-Add-lockdown-support.patch + * 0045-squash-Add-support-for-Linux-EFI-stub-loading-on-aar.patch + * 0046-squash-verifiers-Move-verifiers-API-to-kernel-image.patch +- Drop patch supersceded by the new backport + * 0001-linuxefi-fail-kernel-validation-without-shim-protoco.patch + * 0001-shim_lock-Disable-GRUB_VERIFY_FLAGS_DEFER_AUTH-if-se.patch + * 0007-linuxefi-fail-kernel-validation-without-shim-protoco.patch +- Add SBAT metadata section to grub.efi +- Drop shim_lock module as it is part of core of grub.efi + * grub2.spec + +------------------------------------------------------------------- +Mon Feb 22 12:49:48 UTC 2021 - Michael Chang + +- Fix build error in binutils 2.36 (bsc#1181741) + * 0001-Fix-build-error-in-binutils-2.36.patch +- Fix executable stack in grub-emu (bsc#1181696) + * 0001-emu-fix-executable-stack-marking.patch + +------------------------------------------------------------------- +Thu Feb 18 05:21:29 UTC 2021 - Michael Chang + +- Restore compatibilty sym-links + * grub2.spec +- Use rpmlintrc to filter out rpmlint 2.0 error (bsc#1179044) + * grub2.rpmlintrc + +------------------------------------------------------------------- +Wed Jan 27 04:13:32 UTC 2021 - Michael Chang + +- Complete Secure Boot support on aarch64 (jsc#SLE-15020) + * 0001-Add-support-for-Linux-EFI-stub-loading-on-aarch64.patch + * 0002-arm64-make-sure-fdt-has-address-cells-and-size-cells.patch + * 0003-Make-grub_error-more-verbose.patch + * 0004-arm-arm64-loader-Better-memory-allocation-and-error-.patch + * 0005-Make-linux_arm_kernel_header.hdr_offset-be-at-the-ri.patch + * 0006-efi-Set-image-base-address-before-jumping-to-the-PE-.patch + * 0007-linuxefi-fail-kernel-validation-without-shim-protoco.patch + * 0008-squash-Add-support-for-Linux-EFI-stub-loading-on-aar.patch + * 0009-squash-Add-support-for-linuxefi.patch + +------------------------------------------------------------------- +Thu Jan 21 07:59:39 UTC 2021 - Michael Chang + +- Fix rpmlint 2.0 error for having arch specific path in noarch package aiming + for compatibility with old package (bsc#1179044) + * grub2.spec +- Fix non POSIX sed argument which failed in sed from busybox (bsc#1181091) + * grub2-check-default.sh + +------------------------------------------------------------------- +Mon Nov 2 06:42:04 UTC 2020 - Michael Chang + +- Fix boot failure in blocklist installation (bsc#1178278) + * Modified 0002-grub-install-Avoid-incompleted-install-on-i386-pc.patch + +------------------------------------------------------------------- +Thu Oct 22 06:19:13 UTC 2020 - Michael Chang + +- Fix grub2-install error with "failed to get canonical path of + `/boot/grub2/i386-pc'." (bsc#1177957) + * Modified 0002-grub-install-Avoid-incompleted-install-on-i386-pc.patch + +------------------------------------------------------------------- +Wed Oct 14 08:46:46 UTC 2020 - Michael Chang + +- Fix https boot interrupted by unrecognised network address error message + (bsc#1172952) + * 0001-add-support-for-UEFI-network-protocols.patch + +------------------------------------------------------------------- +Tue Oct 13 08:54:10 UTC 2020 - Michael Chang + +- grub2.spec: Fix bare words used as string in expression which is no longer + allowed in rpm 4.16 + +------------------------------------------------------------------- +Fri Sep 25 07:13:27 UTC 2020 - Michael Chang + +- Improve the error handling when grub2-install fails with short mbr gap + (bsc#1176062) + * 0001-Warn-if-MBR-gap-is-small-and-user-uses-advanced-modu.patch + * 0002-grub-install-Avoid-incompleted-install-on-i386-pc.patch + +------------------------------------------------------------------- +Wed Sep 9 08:10:45 UTC 2020 - Michael Chang + +- Make efi hand off the default entry point of the linux command (bsc#1176134) + * 0001-efi-linux-provide-linux-command.patch + +------------------------------------------------------------------- +Thu Aug 27 06:58:37 UTC 2020 - Michael Chang + +- Fix verification requested but nobody cares error when loading external + module in secure boot off (bsc#1175766) + * 0001-shim_lock-Disable-GRUB_VERIFY_FLAGS_DEFER_AUTH-if-se.patch + +------------------------------------------------------------------- +Sat Aug 22 02:41:49 UTC 2020 - Michael Chang + +- Make consistent check to enable relative path on btrfs (bsc#1174567) + * 0001-Unify-the-check-to-enable-btrfs-relative-path.patch + +------------------------------------------------------------------- +Fri Aug 21 04:40:48 UTC 2020 - Michael Chang + +- Add fibre channel device's ofpath support to grub-ofpathname and search hint + to speed up root device discovery (bsc#1172745) + * 0001-ieee1275-powerpc-implements-fibre-channel-discovery-.patch + * 0002-ieee1275-powerpc-enables-device-mapper-discovery.patch + +------------------------------------------------------------------- +Tue Aug 18 06:02:21 UTC 2020 - Michael Chang + +- Fix for CVE-2020-15705 (bsc#1174421) + * 0001-linuxefi-fail-kernel-validation-without-shim-protoco.patch + * 0002-cmdline-Provide-cmdline-functions-as-module.patch + +------------------------------------------------------------------- +Thu Aug 13 06:41:16 UTC 2020 - Michael Chang + +- Make grub-calloc inline to avoid symbol not found error as the system may not + use updated grub to boot the system (bsc#1174782) (bsc#1175060) (bsc#1175036) + * 0001-kern-mm.c-Make-grub_calloc-inline.patch + +------------------------------------------------------------------- +Mon Jul 27 10:04:49 UTC 2020 - Michael Chang + +- Fix for CVE-2020-10713 (bsc#1168994) + * 0001-yylex-Make-lexer-fatal-errors-actually-be-fatal.patch +- Fix for CVE-2020-14308 CVE-2020-14309, CVE-2020-14310, CVE-2020-14311 + (bsc#1173812) + * 0002-safemath-Add-some-arithmetic-primitives-that-check-f.patch + * 0003-calloc-Make-sure-we-always-have-an-overflow-checking.patch + * 0004-calloc-Use-calloc-at-most-places.patch + * 0005-malloc-Use-overflow-checking-primitives-where-we-do-.patch + * 0006-iso9660-Don-t-leak-memory-on-realloc-failures.patch + * 0007-font-Do-not-load-more-than-one-NAME-section.patch +- Fix CVE-2020-15706 (bsc#1174463) + * 0008-script-Remove-unused-fields-from-grub_script_functio.patch + * 0009-script-Avoid-a-use-after-free-when-redefining-a-func.patch +- Fix CVE-2020-15707 (bsc#1174570) + * 0010-linux-Fix-integer-overflows-in-initrd-size-handling.patch +- Use overflow checking primitives where the arithmetic expression for buffer + allocations may include unvalidated data +- Use grub_calloc for overflow check and return NULL when it would occur + * 0001-add-support-for-UEFI-network-protocols.patch + * 0003-bootp-New-net_bootp6-command.patch + * grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch + * grub2-btrfs-09-get-default-subvolume.patch + * grub2-gfxmenu-support-scrolling-menu-entry-s-text.patch + * grub2-grubenv-in-btrfs-header.patch + +------------------------------------------------------------------- +Thu Jul 16 13:15:27 UTC 2020 - Michel Normand + +- No 95_textmode for PowerPC (boo#1174166) + +------------------------------------------------------------------- +Mon May 18 08:15:01 UTC 2020 - Michael Chang + +- Skip zfcpdump kernel from the grub boot menu (bsc#1166513) + * grub2-s390x-skip-zfcpdump-image.patch + +------------------------------------------------------------------- +Tue May 5 06:48:55 UTC 2020 - Michael Chang + +- Fix boot failure as journaled data not get drained due to abrupt power + off after grub-install (bsc#1167756) + * grub-install-force-journal-draining-to-ensure-data-i.patch + +------------------------------------------------------------------- +Thu Apr 16 13:35:10 UTC 2020 - Michael Chang + +- Fix executable stack in grub-probe and other grub utility (bsc#1169137) + * grub2-btrfs-06-subvol-mount.patch + +------------------------------------------------------------------- +Tue Mar 24 08:17:33 UTC 2020 - Michael Chang + +- Fix GCC 10 build fail (bsc#1158189) + * 0001-mdraid1x_linux-Fix-gcc10-error-Werror-array-bounds.patch + * 0002-zfs-Fix-gcc10-error-Werror-zero-length-bounds.patch + +------------------------------------------------------------------- +Fri Mar 20 10:36:54 UTC 2020 - Michael Chang + +- Backport to support searching for specific config files for netboot + (bsc#1166409) + * 0001-normal-Move-common-datetime-functions-out-of-the-nor.patch + * 0002-kern-Add-X-option-to-printf-functions.patch + * 0003-normal-main-Search-for-specific-config-files-for-net.patch + * 0004-datetime-Enable-the-datetime-module-for-the-emu-plat.patch + +------------------------------------------------------------------- +Mon Mar 16 11:42:08 UTC 2020 - Ludwig Nussel + +- move *.module files to separate -debug subpackage (boo#1166578) + +------------------------------------------------------------------- +Thu Mar 12 08:29:55 UTC 2020 - Fabian Vogt + +- Fix EFI console detection to make it a runtime decision (bsc#1164385) + * grub2-SUSE-Add-the-t-hotkey.patch + +------------------------------------------------------------------- +Tue Mar 10 11:59:23 UTC 2020 - Ludwig Nussel + +- Downgrade mtools to Suggests for consistency with xorriso (boo#1165839) +- remove info requirements, file triggers are used now (boo#1152105) + +------------------------------------------------------------------- +Fri Feb 28 16:36:57 UTC 2020 - rw@suse.com + +- Add secure boot support for s390x. (jsc#SLE-9425) + * grub2-s390x-11-secureboot.patch + +------------------------------------------------------------------- +Tue Feb 18 08:43:30 UTC 2020 - Michael Chang + +- Fix grub hangs after loading rogue image without valid signature for uefi + secure boot (bsc#1159102) + * grub2-verifiers-fix-system-freeze-if-verify-failed.patch + +------------------------------------------------------------------- +Tue Feb 4 07:59:40 UTC 2020 - Michael Chang + +- From Stefan Seyfried : Fix grub2-install fails + with "not a directory" error (boo#1161641, bsc#1162403) + * grub2-install-fix-not-a-directory-error.patch + +------------------------------------------------------------------- +Wed Nov 27 17:09:42 UTC 2019 - olaf@aepfle.de + +- Correct awk pattern in 20_linux_xen (bsc#900418, bsc#1157912) +- Correct linux and initrd handling in 20_linux_xen (bsc#1157912) + M grub2-efi-xen-cfg-unquote.patch + M grub2-efi-xen-chainload.patch + M grub2-efi-xen-cmdline.patch + M grub2-efi-xen-removable.patch + +------------------------------------------------------------------- +Wed Oct 30 06:26:33 UTC 2019 - Michael Chang + +- Disable btrfs zstd support for i386-pc to workaround core.img too large to be + embedded in btrfs bootloader area or MBR gap (boo#1154809) + * 0001-btrfs-disable-zstd-support-for-i386-pc.patch + +------------------------------------------------------------------- +Mon Oct 28 11:52:19 UTC 2019 - Bernhard Wiedemann + +- Fix grub2.sleep to load old kernel after hibernation (boo#1154783) + +------------------------------------------------------------------- +Tue Oct 22 08:05:19 UTC 2019 - Andreas Schwab + +- Enable support for riscv64 +- Backports from upstream: + * risc-v-fix-computation-of-pc-relative-relocation-offset.patch + * risc-v-add-clzdi2-symbol.patch + * grub-install-define-default-platform-for-risc-v.patch + +------------------------------------------------------------------- +Thu Oct 17 06:34:52 UTC 2019 - Michael Chang + +- Version bump to 2.04 + * removed + - translations-20170427.tar.xz + * grub2.spec + - Make signed grub-tpm.efi specific to x86_64-efi build, the platform + currently shipped with tpm module from upstream codebase + - Add shim_lock to signed grub.efi in x86_64-efi build + - x86_64: linuxefi now depends on linux, both will verify kernel via + shim_lock + - Remove translation tarball and po file hacks as it's been included in + upstream tarball + * rediff + - grub2-setup-try-fs-embed-if-mbr-gap-too-small.patch + - grub2-commands-introduce-read_file-subcommand.patch + - grub2-secureboot-add-linuxefi.patch + - 0001-add-support-for-UEFI-network-protocols.patch + - grub2-efi-HP-workaround.patch + - grub2-secureboot-install-signed-grub.patch + - grub2-linux.patch + - use-grub2-as-a-package-name.patch + - grub2-pass-corret-root-for-nfsroot.patch + - grub2-secureboot-use-linuxefi-on-uefi.patch + - grub2-secureboot-no-insmod-on-sb.patch + - grub2-secureboot-provide-linuxefi-config.patch + - grub2-secureboot-chainloader.patch + - grub2-s390x-01-Changes-made-and-files-added-in-order-to-allow-s390x.patch + - grub2-s390x-02-kexec-module-added-to-emu.patch + - grub2-s390x-04-grub2-install.patch + - grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch + - grub2-efi-chainloader-root.patch + - grub2-ppc64le-disable-video.patch + - grub2-ppc64-cas-reboot-support.patch + - grub2-Fix-incorrect-netmask-on-ppc64.patch + - 0003-bootp-New-net_bootp6-command.patch + - 0006-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch + - 0012-tpm-Build-tpm-as-module.patch + - grub2-emu-4-all.patch + - grub2-btrfs-09-get-default-subvolume.patch + - grub2-ppc64le-memory-map.patch + - grub2-ppc64-cas-fix-double-free.patch + - 0008-efinet-Setting-DNS-server-from-UEFI-protocol.patch + * drop upstream patches + - grub2-fix-locale-en.mo.gz-not-found-error-message.patch + - grub2-fix-build-with-flex-2.6.4.patch + - grub2-accept-empty-module.patch + - 0001-Fix-packed-not-aligned-error-on-GCC-8.patch + - 0001-Fix-PCIe-LER-when-GRUB2-accesses-non-enabled-MMIO-da.patch + - unix-exec-avoid-atexit-handlers-when-child-exits.patch + - 0001-xfs-Accept-filesystem-with-sparse-inodes.patch + - grub2-binutils2.31.patch + - grub2-msdos-fix-overflow.patch + - 0001-tsc-Change-default-tsc-calibration-method-to-pmtimer.patch + - grub2-efi-Move-grub_reboot-into-kernel.patch + - grub2-efi-Free-malloc-regions-on-exit.patch + - grub2-move-initrd-upper.patch + - 0002-Add-Virtual-LAN-support.patch + - 0001-ofnet-Initialize-structs-in-bootpath-parser.patch + - 0001-misc-fix-invalid-character-recongition-in-strto-l.patch + - 0001-tpm-Core-TPM-support.patch + - 0002-tpm-Measure-kernel-initrd.patch + - 0003-tpm-Add-BIOS-boot-measurement.patch + - 0004-tpm-Rework-linux-command.patch + - 0005-tpm-Rework-linux16-command.patch + - 0006-tpm-Measure-kernel-and-initrd-on-BIOS-systems.patch + - 0007-tpm-Measure-the-kernel-commandline.patch + - 0008-tpm-Measure-commands.patch + - 0009-tpm-Measure-multiboot-images-and-modules.patch + - 0010-tpm-Fix-boot-when-there-s-no-TPM.patch + - 0011-tpm-Fix-build-error.patch + - 0013-tpm-i386-pc-diskboot-img.patch + - grub2-freetype-pkgconfig.patch + - 0001-cpio-Disable-gcc9-Waddress-of-packed-member.patch + - 0002-jfs-Disable-gcc9-Waddress-of-packed-member.patch + - 0003-hfs-Fix-gcc9-error-Waddress-of-packed-member.patch + - 0004-hfsplus-Fix-gcc9-error-with-Waddress-of-packed-membe.patch + - 0005-acpi-Fix-gcc9-error-Waddress-of-packed-member.patch + - 0006-usbtest-Disable-gcc9-Waddress-of-packed-member.patch + - 0007-chainloader-Fix-gcc9-error-Waddress-of-packed-member.patch + - 0008-efi-Fix-gcc9-error-Waddress-of-packed-member.patch + +------------------------------------------------------------------- +Tue Oct 15 13:29:14 UTC 2019 - rw@suse.com + +- Consistently find btrfs snapshots on s390x. (bsc#1136970) + * grub2-s390x-04-grub2-install.patch + +------------------------------------------------------------------- +Fri Aug 16 04:51:16 UTC 2019 - Michael Chang + +- Fix fallback embed doesn't work when no post mbr gap at all (boo#1142229) + * Refresh grub2-setup-try-fs-embed-if-mbr-gap-too-small.patch + +------------------------------------------------------------------- +Thu Jul 18 09:54:14 UTC 2019 - mchang@suse.com + +- Revert grub2-ieee1275-FCP-methods-for-WWPN-and-LUNs.patch until merged by + upstream (bsc#1134287, bsc#1139345, LTC#177836, LTC#174229). + +------------------------------------------------------------------- +Mon Jun 24 17:56:12 UTC 2019 - Michal Suchanek + +- Fix iteration of FCP LUNs (bsc#1134287, bsc#1139345, LTC#177836, LTC#174229). + * Refresh grub2-ieee1275-FCP-methods-for-WWPN-and-LUNs.patch + +------------------------------------------------------------------- +Mon Jun 17 09:45:49 UTC 2019 - mchang@suse.com + +- Use grub2-install to handle signed grub installation for UEFI secure + boot and also provide options to override default (bsc#1136601) + * grub2-secureboot-install-signed-grub.patch +- Remove arm64 linuxefi patches as it's not needed for secure boot + * 0001-efi-refactor-grub_efi_allocate_pages.patch + * 0002-Remove-grub_efi_allocate_pages.patch + * 0003-arm64-efi-move-EFI_PAGE-definitions-to-efi-memory.h.patch + * 0004-efi-Add-central-copy-of-grub_efi_find_mmap_size.patch + * 0005-efi-Add-grub_efi_get_ram_base-function-for-arm64.patch + * 0006-Add-support-for-EFI-handover-on-ARM64.patch + +------------------------------------------------------------------- +Fri Jun 14 06:13:58 UTC 2019 - mchang@suse.com + +- Avoid high resolution when trying to keep current mode (bsc#1133842) + * grub2-video-limit-the-resolution-for-fixed-bimap-font.patch +- Make GRUB_SAVEDEFAULT working with btrfs (bsc#1128592) + * grub2-grubenv-in-btrfs-header.patch + +------------------------------------------------------------------- +Fri May 17 13:57:29 UTC 2019 - rw@suse.com + +- Check/refresh zipl-kernel before hibernate on s390x. (bsc#940457) + (Getting rid of hardcoded 'vmlinuz', which failed on PPC as well.) + * grub2-systemd-sleep.sh + +------------------------------------------------------------------- +Fri May 17 12:22:55 UTC 2019 - rw@suse.com + +- Try to refresh zipl-kernel on failed kexec. (bsc#1127293) + * grub2-s390x-04-grub2-install.patch +- Fully support "previous" zipl-kernel, + with 'mem=1G' being available on dedicated entries. (bsc#928131) + * grub2-s390x-09-improve-zipl-setup.patch +- Refresh + * grub2-zipl-setup-fix-btrfs-multipledev.patch + +------------------------------------------------------------------- +Fri May 3 02:42:27 UTC 2019 - mchang + +- Fix GCC 9 build failure (bsc#1121208) + * 0001-cpio-Disable-gcc9-Waddress-of-packed-member.patch + * 0002-jfs-Disable-gcc9-Waddress-of-packed-member.patch + * 0003-hfs-Fix-gcc9-error-Waddress-of-packed-member.patch + * 0004-hfsplus-Fix-gcc9-error-with-Waddress-of-packed-membe.patch + * 0005-acpi-Fix-gcc9-error-Waddress-of-packed-member.patch + * 0006-usbtest-Disable-gcc9-Waddress-of-packed-member.patch + * 0007-chainloader-Fix-gcc9-error-Waddress-of-packed-member.patch + * 0008-efi-Fix-gcc9-error-Waddress-of-packed-member.patch + +------------------------------------------------------------------- +Tue Mar 19 06:14:47 UTC 2019 - mchang + +- Use %doc for older products for compatibility, or may end up with + unsuccessful build result + * grub2.spec + +------------------------------------------------------------------- +Tue Mar 19 04:18:46 UTC 2019 - mchang + +- Revert grub2-ieee1275-open-raw-mode.patch for regression of crashing lvm on + multipath SAN (bsc#1113702) + * deleted grub2-ieee1275-open-raw-mode.patch +- Add exception handling to FCP lun enumeration (bsc#1113702) + * grub2-ieee1275-FCP-methods-for-WWPN-and-LUNs.patch + +------------------------------------------------------------------- +Wed Feb 20 08:38:55 UTC 2019 - mchang@suse.com + +- Fix LOADER_TYPE parsing in grub2-once (boo#1122569) + +------------------------------------------------------------------- +Tue Feb 12 08:57:03 UTC 2019 - mchang@suse.com + +- Create compatibility sym-link of grub.xen in the old location to which + old VM definition is pointing (bsc#1123942) + +------------------------------------------------------------------- +Mon Jan 28 14:12:05 UTC 2019 - Guillaume GARDET + +- Add patch to fix ARM boot, when kernel become too big: + * grub2-move-initrd-upper.patch (boo#1123350) + +------------------------------------------------------------------- +Fri Jan 25 22:20:36 UTC 2019 - Jan Engelhardt + +- Replace old $RPM_* shell vars. + +------------------------------------------------------------------- +Fri Jan 25 07:08:32 UTC 2019 - mchang@suse.com + +- Support long menu entry by scrolling its text left and right through + the key stroke ctrl+l and ctrl+r (FATE#325760) + * grub2-gfxmenu-support-scrolling-menu-entry-s-text.patch + +------------------------------------------------------------------- +Thu Jan 24 09:26:09 UTC 2019 - mchang@suse.com + +- Improved hiDPI device support (FATE#326680) + * grub2-video-limit-the-resolution-for-fixed-bimap-font.patch + +------------------------------------------------------------------- +Wed Jan 23 10:44:09 UTC 2019 - rw@suse.com + +- Build platform-packages 'noarch' and move to '/usr/share/efi' + for SUSE Manager. (FATE#326960) + * grub2-efi-xen-chainload.patch (bsc#1122563) + * grub2-efi-xen-removable.patch (refresh) + +------------------------------------------------------------------- +Thu Dec 20 09:21:27 UTC 2018 - mchang@suse.com + +- Support for UEFI Secure Boot on AArch64 (FATE#326541) + * 0001-efi-refactor-grub_efi_allocate_pages.patch + * 0002-Remove-grub_efi_allocate_pages.patch + * 0003-arm64-efi-move-EFI_PAGE-definitions-to-efi-memory.h.patch + * 0004-efi-Add-central-copy-of-grub_efi_find_mmap_size.patch + * 0005-efi-Add-grub_efi_get_ram_base-function-for-arm64.patch + * 0006-Add-support-for-EFI-handover-on-ARM64.patch + +------------------------------------------------------------------- +Mon Nov 26 06:54:34 UTC 2018 - mchang@suse.com + +- Change default tsc calibration method to pmtimer on EFI (bsc#1114754) + * 0001-tsc-Change-default-tsc-calibration-method-to-pmtimer.patch + +------------------------------------------------------------------- +Fri Oct 19 07:17:34 UTC 2018 - mchang@suse.com + +- ieee1275: Fix double free in CAS reboot (bsc#1111955) + * grub2-ppc64-cas-fix-double-free.patch + +------------------------------------------------------------------- +Thu Oct 4 06:52:58 UTC 2018 - glin@suse.com + +- Support NVDIMM device names (bsc#1110073) + * grub2-getroot-support-nvdimm.patch + +------------------------------------------------------------------- +Wed Oct 3 07:08:14 UTC 2018 - mchang@suse.com + +- Translate caret back to space as the initrd stanza could use space to + delimit multiple files loaded (bsc#1101942) + * grub2-util-30_os-prober-multiple-initrd.patch + +------------------------------------------------------------------- +Wed Sep 26 08:01:05 UTC 2018 - mchang@suse.com + +- ieee1275: implement FCP methods for WWPN and LUNs (bsc#1093145) + * grub2-ieee1275-FCP-methods-for-WWPN-and-LUNs.patch + +------------------------------------------------------------------- +Thu Sep 13 08:38:01 UTC 2018 - mchang@suse.com + +- Fix broken network interface with random address and same name (bsc#1084508) + * 0001-ofnet-Initialize-structs-in-bootpath-parser.patch + +------------------------------------------------------------------- +Fri Aug 31 10:06:56 UTC 2018 - mchang@suse.com + +- Fix outputting invalid btrfs subvol path on non btrfs filesystem due to bogus + return code handling. (bsc#1106381) + * modified grub2-btrfs-10-config-directory.patch + +------------------------------------------------------------------- +Thu Aug 23 08:37:15 UTC 2018 - mchang@suse.com + +- Fix overflow in sector count calculation (bsc#1105163) + * grub2-msdos-fix-overflow.patch + +------------------------------------------------------------------- +Thu Aug 9 02:48:18 UTC 2018 - mchang@suse.com + +- Downgrade libburnia-tools to suggest as minimal system can't afford pulling + in tcl/tk and half of the x11 stack (bsc#1102515) + * modified grub2.spec + +------------------------------------------------------------------- +Wed Aug 8 15:17:13 UTC 2018 - dimstar@opensuse.org + +- Add grub2-binutils2.31.patch: x86-64: Treat R_X86_64_PLT32 as + R_X86_64_PC32. Starting from binutils commit bd7ab16b x86-64 + assembler generates R_X86_64_PLT32, instead of R_X86_64_PC32, for + 32-bit PC-relative branches. Grub2 should treat R_X86_64_PLT32 + as R_X86_64_PC32. + +------------------------------------------------------------------- +Mon Aug 6 09:02:45 UTC 2018 - josef.moellers@suse.com + +- The grubxenarch packages are now architecture-independent. + [bsc#953297, grub2.spec, grub2-rpmlintrc] + +------------------------------------------------------------------- +Tue Jul 24 07:56:02 UTC 2018 - mchang@suse.com + +- Fix config_directory on btrfs to follow path scheme (bsc#1063443) + * grub2-btrfs-10-config-directory.patch +- Fix grub2-install --root-directory does not work for /boot/grub2/ on + separate btrfs subvolume (boo#1098420) + * grub2-btrfs-06-subvol-mount.patch +- Fix setparams doesn't work as expected from boot-last-label NVRAM var, after + inital CAS reboot on ieee1275 (bsc#1088830) + * grub2-ppc64-cas-new-scope.patch + +------------------------------------------------------------------- +Mon Jul 16 07:53:19 UTC 2018 - mchang@suse.com + +- Fix install on xfs error (bsc#1101283) + * 0001-xfs-Accept-filesystem-with-sparse-inodes.patch + +------------------------------------------------------------------- +Tue Jul 10 15:57:50 UTC 2018 - jbohac@suse.cz + +- grub2.spec: change %config to %config(noreplace) + Don't overwrite user changes to config files on upgrades. + +------------------------------------------------------------------- +Wed Jul 4 12:55:30 UTC 2018 - josef.moellers@suse.com + +- Marked %{_sysconfdir}/grub.d/40_custom as (noreplace) + [bsc#1079332, grub2.spec] + +------------------------------------------------------------------- +Wed Jun 27 08:39:07 UTC 2018 - josef.moellers@suse.com + +- Replace "GRUB_DISABLE_LINUX_RECOVERY" by "GRUB_DISABLE_RECOVERY" + in /etc/default/grub and remove test from s390x install + section in upec file. + [bsc#1042433, grub.default, grub2.spec] + +------------------------------------------------------------------- +Wed Jun 20 09:21:48 UTC 2018 - josef.moellers@suse.com + +- Added "# needssslcertforbuild", which got lost somewhere, + to spec file + [grub2.spec] + +------------------------------------------------------------------- +Fri Jun 15 09:33:17 UTC 2018 - josef.moellers@suse.com + +- Replace confusing menu on btrfs "snapper rollback" by help text. + [bsc#1027588, grub2-btrfs-help-on-snapper-rollback.patch] + +------------------------------------------------------------------- +Thu May 24 11:02:04 CEST 2018 - kukuk@suse.de + +- Use %license instead of %doc [bsc#1082318] + +------------------------------------------------------------------- +Wed May 16 09:07:08 UTC 2018 - Thomas.Blume@suse.com + +- grub2-emu on s390 keep network during kexec boot (bsc#1089493) + * grub2-s390x-10-keep-network-at-kexec.patch + +------------------------------------------------------------------- +Fri May 4 08:07:09 UTC 2018 - idonmez@suse.com + +- Add grub2-freetype-pkgconfig.patch to fix build with new freetype + use pkgconfig to find Freetype libraries. + +------------------------------------------------------------------- +Tue Apr 17 07:54:15 UTC 2018 - mchang@suse.com + +- Fallback to raw mode if Open Firmware returns invalid ihandler (bsc#1071559) + * grub2-ieee1275-open-raw-mode.patch + +------------------------------------------------------------------- +Thu Apr 12 08:41:39 UTC 2018 - mchang@suse.com + +- Fix error of essential directory not found on UEFI Xen host (bsc#1085842) + * add grub2-efi-xen-removable.patch + * rediff grub2-suse-remove-linux-root-param.patch + +------------------------------------------------------------------- +Tue Apr 10 15:12:28 CEST 2018 - jdelvare@suse.de + +- Fix corruption of "grub2-install --help" and grub2-install manual + page (bsc#1086670) + * unix-exec-avoid-atexit-handlers-when-child-exits.patch + +------------------------------------------------------------------- +Mon Apr 2 08:30:05 UTC 2018 - mchang@suse.com + +- Fix Nvidia GPU in legacy I/O slot 2 disappears during system + startup (bsc#1082914) + * 0001-Fix-PCIe-LER-when-GRUB2-accesses-non-enabled-MMIO-da.patch + +------------------------------------------------------------------- +Fri Mar 30 09:16:06 UTC 2018 - mchang@suse.com + +- Fix packed-not-aligned error on GCC 8 (bsc#1084632) + * 0001-Fix-packed-not-aligned-error-on-GCC-8.patch + +------------------------------------------------------------------- +Mon Mar 26 11:37:13 UTC 2018 - msuchanek@suse.com + +- Fix incorrect netmask on ppc64 (bsc#1085419) + * grub2-Fix-incorrect-netmask-on-ppc64.patch + +------------------------------------------------------------------- +Mon Mar 12 07:31:10 UTC 2018 - mchang@suse.com + +- Fix UEFI HTTPS Boot from ISO installation image (bsc#1076132) + * 0001-add-support-for-UEFI-network-protocols.patch + +------------------------------------------------------------------- +Tue Mar 6 08:21:43 UTC 2018 - mchang@suse.com + +- fix wrong command output when default subvolume is toplevel tree with + id 5 (bsc#1078775) + * grub2-btrfs-09-get-default-subvolume.patch +- insert mdraid modules to support software RAID (bsc#1078775) + * grub2-xen-pv-firmware.cfg + +------------------------------------------------------------------- +Thu Mar 1 18:36:33 UTC 2018 - iforster@suse.com + +- Rename grub2-btrfs-workaround-grub2-once.patch to + grub2-grubenv-in-btrfs-header.patch +- Store GRUB environment variable health_checker_flag in Btrfs header + +------------------------------------------------------------------- +Tue Feb 13 09:05:45 UTC 2018 - mchang@suse.com + +- Fix incorrect check preventing the script from running (bsc#1078481) + * 80_suse_btrfs_snapshot + +------------------------------------------------------------------- +Wed Feb 7 09:58:26 UTC 2018 - mchang@suse.com + +- Fix disappeared snapshot menu entry (bsc#1078481) + * 80_suse_btrfs_snapshot + +------------------------------------------------------------------- +Tue Feb 6 09:44:26 UTC 2018 - mchang@suse.com + +- Fix unquoted string error and add some more checks (bsc#1079330) + * grub2-check-default.sh + +------------------------------------------------------------------- +Mon Feb 5 08:52:20 UTC 2018 - olaf@aepfle.de + +- The %prep section applies patches, the %build section builds. + Remove mixup of patching and building from %prep for quilt setup + Related to bsc#1065703 + +------------------------------------------------------------------- +Tue Jan 23 04:41:22 UTC 2018 - mchang@suse.com + +- Check if default entry need to be corrected for updated distributor version + and/or use fallback entry if default kernel entry removed (bsc#1065349) + * grub2-check-default.sh + * grub2-mkconfig-default-entry-correction.patch +- Fix grub2-mkconfig warning when disk is LVM PV (bsc#1071239) + * grub2-getroot-scan-disk-pv.patch + +------------------------------------------------------------------- +Fri Dec 8 09:30:46 UTC 2017 - mchang@suse.com + +- Filter out autofs and securityfs from /proc/self/mountinfo to speed + up nfsroot test in large number of autofs mounts (bsc#1069094) + * modified grub2-pass-corret-root-for-nfsroot.patch + +------------------------------------------------------------------- +Tue Nov 28 09:35:48 UTC 2017 - mchang@suse.com + +- Fix http(s) boot security review (bsc#1058090) + * 0002-AUDIT-0-http-boot-tracker-bug.patch + +------------------------------------------------------------------- +Tue Nov 14 09:02:19 UTC 2017 - mchang@suse.com + +- 0001-add-support-for-UEFI-network-protocols.patch: + * Workaround http data access in firmware + * Fix DNS device path parsing for efinet device + * Relaxed UEFI Protocol requirement + * Support Intel OPA (Omni-Path Architecture) PXE Boot (bsc#1015589) + +------------------------------------------------------------------- +Wed Nov 8 09:37:12 UTC 2017 - olaf@aepfle.de + +- grub2-xen-pv-firmware.cfg: remove linemode=1 from cmdline for + SUSE installer. openQA expects ncurses interface. (bsc#1066919) + +------------------------------------------------------------------- +Mon Nov 6 15:34:03 UTC 2017 - jmatejek@suse.com + +- use python3 for autogen.sh (fate#323526) + +------------------------------------------------------------------- +Tue Oct 31 13:36:02 UTC 2017 - msuchanek@suse.com + +- Do not check that PReP partition does not contain an ELF during installation + (bsc#1065738). + * grub2-install-remove-useless-check-PReP-partition-is-empty.patch + +------------------------------------------------------------------- +Tue Sep 26 06:35:50 UTC 2017 - mchang@suse.com + +- Build diskboot_tpm.img as separate image to diskboot.img to prevent failure + in booting on some bogus firmware. To use the TPM image you have to use + suse-enable-tpm option of grub2-install (bsc#1052401) + * 0013-tpm-i386-pc-diskboot-img.patch + +------------------------------------------------------------------- +Wed Sep 20 22:17:10 UTC 2017 - mlatimer@suse.com + +- Use /boot//loader/linux to determine if install media + is SUSE instead of /contents file (bsc#1054453) + +------------------------------------------------------------------- +Tue Sep 19 22:52:45 UTC 2017 - mlatimer@suse.com + +- Use the pvops-enabled default kernel if the traditional xen + pv kernel and initrd are not found (bsc#1054453) + +------------------------------------------------------------------- +Fri Sep 8 08:04:41 UTC 2017 - agraf@suse.com + +- Fix reboot in UEFI environments (bsc#1047331) + * Add grub2-efi-Move-grub_reboot-into-kernel.patch + * Refresh grub2-efi-Free-malloc-regions-on-exit.patch + +------------------------------------------------------------------- +Sun Sep 3 12:12:21 UTC 2017 - mchang@suse.com + +- Add preliminary patch for UEFI HTTPS and related network protocol support + (fate#320130) + * 0001-add-support-for-UEFI-network-protocols.patch + +------------------------------------------------------------------- +Sun Sep 3 11:41:42 UTC 2017 - mchang@suse.com + +- grub2-s390x-04-grub2-install.patch : remove arybase dependency in + grub2-zipl-setup by not referencing to $[ (bsc#1055280) + +------------------------------------------------------------------- +Wed Aug 23 17:52:32 UTC 2017 - rw@suse.com + +- Fix minor oversights in and the exit value of the grub2-install + helper on s390x. (bsc#1055343, fate#323298) + * grub2-s390x-09-improve-zipl-setup.patch + +------------------------------------------------------------------- +Mon Jul 24 13:39:25 UTC 2017 - bwiedemann@suse.com + +- Make grub2.info build reproducible (boo#1047218) + +------------------------------------------------------------------- +Tue Jul 4 16:56:33 UTC 2017 - arvidjaar@gmail.com + +- add grub2-fix-build-with-flex-2.6.4.patch - fix build with flex 2.6.4+ + that removed explicit (void) cast from fprintf call in yy_fatal_error. + +------------------------------------------------------------------- +Thu Jun 1 09:45:44 UTC 2017 - mchang@suse.com + +- Support LVM physical volume created without metadatacopies (bsc#1027526) + * grub2-diskfilter-support-pv-without-metadatacopies.patch +- Fix page fault exception when grub loads with Nvidia cards (bsc#1038533) + * grub2-efi-uga-64bit-fb.patch +- Require 'kexec-tools' for System z. (bsc#944358) + * modified grub2.spec + +------------------------------------------------------------------- +Thu May 11 08:56:57 UTC 2017 - mchang@suse.com + +- grub2-xen-pv-firmware.cfg: insmod lvm module as it's not auto-loaded + to support booting from lvm volume (bsc#1004324) +- Grub not working correctly with xen and btrfs snapshots (bsc#1026511) + * Add grub2-btrfs-09-get-default-subvolume.patch + * grub2-xen-pv-firmware.cfg : search path in default subvolume + +------------------------------------------------------------------- +Thu Apr 27 18:39:21 UTC 2017 - arvidjaar@gmail.com + +- new upstream version 2.02 + * rediff + - use-grub2-as-a-package-name.patch + * drop upstream patches + - grub2-fix-uninitialized-variable-in-btrfs-with-GCC7.patch + - grub2-add-FALLTHROUGH-annotations.patch +- update translations + +------------------------------------------------------------------- +Sun Mar 26 18:08:20 UTC 2017 - arvidjaar@gmail.com + +- update grub2-btrfs-workaround-grub2-once.patch to also store saved_entry + in additional environment block (boo#1031025) + +------------------------------------------------------------------- +Wed Mar 22 17:39:52 UTC 2017 - arvidjaar@gmail.com + +- fix building with GCC (bsc#1030247) + * add grub2-fix-uninitialized-variable-in-btrfs-with-GCC7.patch + * grub2-add-FALLTHROUGH-annotations.patch + +------------------------------------------------------------------- +Mon Mar 20 09:43:58 UTC 2017 - mchang@suse.com + +- Fix out of memory error on lvm detection (bsc#1016536) (bsc#1027401) + * grub2-lvm-allocate-metadata-buffer-from-raw-contents.patch +- Fix boot failure if /boot is separate btrfs partition (bsc#1023160) + * grub2-btrfs-06-subvol-mount.patch + +------------------------------------------------------------------- +Fri Mar 17 06:22:42 UTC 2017 - mchang@suse.com + +- 0004-tpm-Rework-linux-command.patch : Fix out of bound memory copy + (bsc#1029187) + +------------------------------------------------------------------- +Thu Mar 16 16:32:54 UTC 2017 - arvidjaar@gmail.com + +- new upstream version 2.02~rc2 + * rediff + - use-grub2-as-a-package-name.patch + - grub2-linguas.sh-no-rsync.patch + * drop upstream patches + - 0001-efi-strip-off-final-NULL-from-File-Path-in-grub_efi_.patch + +------------------------------------------------------------------- +Mon Mar 6 06:34:01 UTC 2017 - mchang@suse.com + +- TPM Support (FATE#315831) + * 0001-tpm-Core-TPM-support.patch + * 0002-tpm-Measure-kernel-initrd.patch + * 0003-tpm-Add-BIOS-boot-measurement.patch + * 0004-tpm-Rework-linux-command.patch + * 0005-tpm-Rework-linux16-command.patch + * 0006-tpm-Measure-kernel-and-initrd-on-BIOS-systems.patch + * 0007-tpm-Measure-the-kernel-commandline.patch + * 0008-tpm-Measure-commands.patch + * 0009-tpm-Measure-multiboot-images-and-modules.patch + * 0010-tpm-Fix-boot-when-there-s-no-TPM.patch + * 0011-tpm-Fix-build-error.patch + * 0012-tpm-Build-tpm-as-module.patch +- grub2.spec : Add grub-tpm.efi for Secure Boot + +------------------------------------------------------------------- +Fri Mar 3 10:26:10 UTC 2017 - mchang@suse.com + +- Fix invalid Xen EFI config files if xen_args include GRUB2 quoting + (bsc#900418) (bsc#951748) + * grub2-efi-xen-cfg-unquote.patch +- Fix linuxefi erroneously initialize linux's boot_params with non-zero + values. (bsc#1025563) + * grub2-linuxefi-fix-boot-params.patch +- Removed grub2-fix-multi-device-root-kernel-argument.patch as it has + regression on how GRUB_DISABLE_LINUX_UUID=true interpreted (bsc#1015138) + +------------------------------------------------------------------- +Wed Mar 1 10:29:46 UTC 2017 - mchang@suse.com + +- Fix for openQA UEFI USB Boot failure with upstream patch (bsc#1026344) + * added 0001-efi-strip-off-final-NULL-from-File-Path-in-grub_efi_.patch + * removed 0001-Revert-efi-properly-terminate-filepath-with-NULL-in-.patch + +------------------------------------------------------------------- +Thu Feb 23 15:06:44 UTC 2017 - mchang@suse.com + +- Temporary fix for openQA UEFI USB Boot failure (bsc#1026344) + * 0001-Revert-efi-properly-terminate-filepath-with-NULL-in-.patch + +------------------------------------------------------------------- +Fri Feb 17 06:46:11 UTC 2017 - mchang@suse.com + +- grub2.spec: fix s390x file list. + +------------------------------------------------------------------- +Thu Feb 16 12:55:29 UTC 2017 - msuchanek@suse.com + +- require efibootmgr in efi package (boo#1025520) + +------------------------------------------------------------------- +Wed Feb 15 07:25:02 UTC 2017 - mchang@suse.com + +- Merge changes from SLE12 +- add grub2-emu-4-all.patch + * Build 'grub2-emu' wherever possible, to allow a better + implementation of that feature. +- add grub2-s390x-06-loadparm.patch, +- add grub2-commands-introduce-read_file-subcommand.patch: + * allow s390x to telecontrol grub2. (bsc#891946, bsc#892852) +- add grub2-s390x-06-loadparm.patch: + * ignore case and fix transliteration of parameter. (bsc#891946) +- add grub2-s390x-07-add-image-param-for-zipl-setup.patch + * Add --image switch to force zipl update to specific kernel + (bsc#928131) +- add grub2-s390x-08-workaround-part-to-disk.patch + * Ignore partition tables on s390x. (bsc#935127) +- add grub2-efi-chainload-harder.patch: + * allow XEN to be chain-loaded despite firmware flaws. (bnc#887793) + * Do not use shim lock protocol for reading pe header, it won't be + available when secure boot disabled (bsc#943380) + * Make firmware flaw condition be more precisely detected and add + debug message for the case + * Check msdos header to find PE file header (bsc#954126) +- grub2-s390x-04-grub2-install.patch: + * streamline boot to grub menu. (bsc#898198) + * Force '/usr' to read-only before calling kexec. (bsc#932951) +- grub2-once: + * add '--enum' option to enumerate boot-entries in a way + actually understood by 'grub2'. (bsc#892852, bsc#892811) + * Examine variables from grub environment in 'grub2-once'. (fate#319632) + +------------------------------------------------------------------- +Fri Feb 10 17:58:22 UTC 2017 - arvidjaar@gmail.com + +- new upstream version 2.02~rc1 + * rediff + - use-grub2-as-a-package-name.patch + - grub2-s390x-04-grub2-install.patch + - grub2-accept-empty-module.patch + - grub2-btrfs-04-grub2-install.patch + - grub2-btrfs-06-subvol-mount.patch + * drop upstream patches + - 0001-dns-fix-buffer-overflow-for-data-addresses-in-recv_h.patch + - 0001-build-Use-AC_HEADER_MAJOR-to-find-device-macros.patch + - 0002-configure-fix-check-for-sys-sysmacros.h-under-glibc-.patch + - 0001-Fix-fwpath-in-efi-netboot.patch + - 0001-arm64-Move-firmware-fdt-search-into-global-function.patch + - 0002-arm-efi-Use-fdt-from-firmware-when-available.patch + - grub2-arm64-mknetdir-add-suport-for-arm64-efi.patch + - 0001-10_linux-Fix-grouping-of-tests-for-GRUB_DEVICE.patch + - 0002-20_linux_xen-fix-test-for-GRUB_DEVICE.patch + - 0001-xen-make-xen-loader-callable-multiple-times.patch + - 0002-xen-avoid-memleaks-on-error.patch + - 0003-xen-reduce-number-of-global-variables-in-xen-loader.patch + - 0004-xen-add-elfnote.h-to-avoid-using-numbers-instead-of-.patch + - 0005-xen-synchronize-xen-header.patch + - 0006-xen-factor-out-p2m-list-allocation-into-separate-fun.patch + - 0007-xen-factor-out-allocation-of-special-pages-into-sepa.patch + - 0008-xen-factor-out-allocation-of-page-tables-into-separa.patch + - 0009-xen-add-capability-to-load-initrd-outside-of-initial.patch + - 0010-xen-modify-page-table-construction.patch + - 0011-xen-add-capability-to-load-p2m-list-outside-of-kerne.patch + * add + - fix-grub2-use-stat-instead-of-udevadm-for-partition-lookup-with-new-glibc.patch + fix compilation with new glibc + +------------------------------------------------------------------- +Thu Feb 9 03:45:16 UTC 2017 - mchang@suse.com + +- Fix build error on glibc-2.25 + * 0001-build-Use-AC_HEADER_MAJOR-to-find-device-macros.patch + * 0002-configure-fix-check-for-sys-sysmacros.h-under-glibc-.patch +- Fix fwpath in efi netboot (fate#321993) (bsc#1022294) + * 0001-Fix-fwpath-in-efi-netboot.patch + +------------------------------------------------------------------- +Fri Feb 3 08:18:30 UTC 2017 - mchang@suse.com + +- grub2-systemd-sleep.sh: Fix prematurely abort by commands error return code + and skip the offending menu entry (bsc#1022880) + +------------------------------------------------------------------- +Wed Feb 1 21:42:49 UTC 2017 - agraf@suse.com + +- Add support for BLT only EFI GOP adapters (FATE#322332) + * grub2-efi-gop-add-blt.patch + +------------------------------------------------------------------- +Wed Jan 25 09:58:20 UTC 2017 - schwab@linux-m68k.org + +- info-dir-entry.patch: Update info dir entry to follow renaming to grub2 + +------------------------------------------------------------------- +Mon Jan 16 10:15:52 UTC 2017 - matwey.kornilov@gmail.com + +- Add serial module to efi image. + Serial terminal is still useful even with EFI Secure Boot + +------------------------------------------------------------------- +Wed Jan 11 06:58:56 UTC 2017 - mchang@suse.com + +- Support %posttrans with marcos provided by update-bootloader-rpm-macros + package (bsc#997317) + +------------------------------------------------------------------- +Wed Jan 4 08:48:56 UTC 2017 - mchang@suse.com + +- Remove outdated README.openSUSE (bsc#907693) + +------------------------------------------------------------------- +Fri Dec 30 14:47:39 UTC 2016 - sor.alexei@meowr.ru + +- 20_memtest86+: avoid adding memtest86+ to the list with UEFI + booting. + +------------------------------------------------------------------- +Fri Oct 28 04:08:19 UTC 2016 - mchang@suse.com + +- Fix new line character in distributor (bsc#1007212) + * modified grub2-default-distributor.patch + +------------------------------------------------------------------- +Fri Oct 21 09:34:58 UTC 2016 - mchang@suse.com + +- From Juergen Gross : grub-xen: support booting huge + pv-domains (bsc#1004398) (bsc#899465) + * 0001-xen-make-xen-loader-callable-multiple-times.patch + * 0002-xen-avoid-memleaks-on-error.patch + * 0003-xen-reduce-number-of-global-variables-in-xen-loader.patch + * 0004-xen-add-elfnote.h-to-avoid-using-numbers-instead-of-.patch + * 0005-xen-synchronize-xen-header.patch + * 0006-xen-factor-out-p2m-list-allocation-into-separate-fun.patch + * 0007-xen-factor-out-allocation-of-special-pages-into-sepa.patch + * 0008-xen-factor-out-allocation-of-page-tables-into-separa.patch + * 0009-xen-add-capability-to-load-initrd-outside-of-initial.patch + * 0010-xen-modify-page-table-construction.patch + * 0011-xen-add-capability-to-load-p2m-list-outside-of-kerne.patch + +------------------------------------------------------------------- +Tue Oct 11 20:59:40 UTC 2016 - dmueller@suse.com + +- add support for netboot on arm64-efi platforms (bsc#998097) + * grub2-arm64-mknetdir-add-suport-for-arm64-efi.patch + +------------------------------------------------------------------- +Fri Sep 2 03:24:19 UTC 2016 - mchang@suse.com + +- use $PRETTY_NAME instead of $NAME $VERSION for $GRUB_DISTRIBUTOR + in openSUSE Tumbleweed (bsc#995549) + * modified grub2-default-distributor.patch +- grub2.spec: add http module to grub.efi (fate#320129) + +------------------------------------------------------------------- +Wed Aug 31 15:40:28 UTC 2016 - matz@suse.com + +- binutils 2.27 creates empty modules without a symtab. + Add patch grub2-accept-empty-module.patch to not reject them. + +------------------------------------------------------------------- +Sat Aug 20 05:42:12 UTC 2016 - arvidjaar@gmail.com + +- since version 1.7 cryptsetup defaults to SHA256 for LUKS - include + gcry_sha256 in signed EFI image + +------------------------------------------------------------------- +Fri Aug 12 08:32:05 UTC 2016 - mchang@suse.com + +- Workaround default entry in snapshot menu (bsc#956046) + * grub2-btrfs-08-workaround-snapshot-menu-default-entry.patch +- grub2.spec: Add true command to grub.efi (bsc#993274) + +------------------------------------------------------------------- +Wed Aug 3 04:45:51 UTC 2016 - mchang@suse.com + +- grub.default: Empty GRUB_CMDLINE_LINUX_DEFAULT, the value will be fully + taken from YaST settings. (bsc#989803) + +------------------------------------------------------------------- +Wed Aug 3 04:45:34 UTC 2016 - mchang@suse.com + +- Add patches from Roberto Sassu +- Fix grub2-10_linux-avoid-multi-device-root-kernel-argument.patch, + device path is not tested if GRUB_DISABLE_LINUX_UUID="true" + - added grub2-fix-multi-device-root-kernel-argument.patch + (bsc#960776) +- grub2-zipl-setup: avoid multi-device root= kernel argument + * added grub2-zipl-setup-fix-btrfs-multipledev.patch + (bsc#960776) +- Add SUSE_REMOVE_LINUX_ROOT_PARAM configuration option + to /etc/default/grub, to remove root= and rootflags= from the + kernel command line in /boot/grub2/grub.cfg and /boot/zipl/config + - added grub2-suse-remove-linux-root-param.patch + (bsc#962585) + +------------------------------------------------------------------- +Tue Aug 2 09:05:11 UTC 2016 - mchang@suse.com + +- Support HTTP Boot IPv4 and IPv6 (fate#320129) + * 0001-misc-fix-invalid-character-recongition-in-strto-l.patch + * 0002-net-read-bracketed-ipv6-addrs-and-port-numbers.patch + * 0003-bootp-New-net_bootp6-command.patch + * 0004-efinet-UEFI-IPv6-PXE-support.patch + * 0005-grub.texi-Add-net_bootp6-doument.patch + * 0006-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch + * 0007-efinet-Setting-network-from-UEFI-device-path.patch + * 0008-efinet-Setting-DNS-server-from-UEFI-protocol.patch +- Fix heap corruption after dns lookup + * 0001-dns-fix-buffer-overflow-for-data-addresses-in-recv_h.patch + +------------------------------------------------------------------- +Tue Jun 28 00:31:47 CEST 2016 - ro@suse.de + +- fix filelist for s390x + +------------------------------------------------------------------- +Tue Jun 21 06:19:27 UTC 2016 - mchang@suse.com + +- Fix grub2-editenv error on encrypted lvm installation (bsc#981621) + * modified grub2-btrfs-workaround-grub2-once.patch +- Add missing closing bracket in 'grub2-snapper-plugin.sh'. +- Fix snapshot booting on s390x (bsc#955115) + * modified grub2-snapper-plugin.sh +- Fallback to old subvol name scheme to support old snapshot config + (bsc#953538) + * added grub2-btrfs-07-subvol-fallback.patch + +------------------------------------------------------------------- +Thu Jun 2 19:25:58 UTC 2016 - arvidjaar@gmail.com + +- update grub2-once with patch from Björn Voigt - skip comments in + /etc/sysconfig/bootloader (boo#963610) + +------------------------------------------------------------------- +Fri May 20 09:28:16 UTC 2016 - jengelh@inai.de + +- Make sure all systemd unit files are passed to %service_ macros. + +------------------------------------------------------------------- +Thu May 19 14:56:53 UTC 2016 - agraf@suse.com + +- Add patch to free memory on exit in efi environments (bsc#980739) + * grub2-efi-Free-malloc-regions-on-exit.patch + +------------------------------------------------------------------- +Mon May 2 13:25:02 UTC 2016 - olaf@aepfle.de + +- Remove xen-devel from BuildRequires + required headers are included in grub-2.0.2 + +------------------------------------------------------------------- +Thu Apr 28 09:06:11 UTC 2016 - agraf@suse.com + +- Add support for "t" hotkey to switch to text mode (bsc#976836) + * added grub2-SUSE-Add-the-t-hotkey.patch +- Add support for hidden menu entries (bsc#976836) + * added grub2-Add-hidden-menu-entries.patch + +------------------------------------------------------------------- +Tue Apr 19 08:21:24 UTC 2016 - mchang@suse.com + +- Correct show user defined comments in menu for snapshots (bsc#956698) + * modified grub2-snapper-plugin.sh + +------------------------------------------------------------------- +Mon Mar 21 11:27:54 UTC 2016 - mchang@suse.com + +- Fix GRUB_DISABLE_LINUX_UUID to be ignore and also fallback kernel device + won't be used if fs uuid not detected (bsc#971867) + * added 0001-10_linux-Fix-grouping-of-tests-for-GRUB_DEVICE.patch + * added 0002-20_linux_xen-fix-test-for-GRUB_DEVICE.patch + +------------------------------------------------------------------- +Tue Mar 1 18:53:17 UTC 2016 - arvidjaar@gmail.com + +- new upstream version 2.02~beta3 + * highlights of user visible changes not yet present in openSUSE package + - arm-uboot now generates position independent self relocating image, so + single binary should run on all supported systems + - loader for Xen on aarch64. grub-mkconfig support was not in time for + beta3 yet. + - improved ZFS support (extensible_dataset, large_blocks, embedded_data, + hole_birth features) + - support for IPv6 Router Advertisements + - support for persistent memory (we do not overwrite it and pass correct + information to OS) + - try to display more specific icons for os-prober generated menu entries + - grub-install detects EFI bit size and selects correct platform (x86_64-efi + or i386-efi) independent of OS bit size; needs kernel 4.0 or higher. + - LVM RAID1 support + - xnu loader fixes which should make OS X menu entry generated by os-prober + work again + - key modifiers (Ctrl-X etc) should work on EFI too + - ... and lot of fixes over entire tree + * rediff + - rename-grub-info-file-to-grub2.patch + - use-grub2-as-a-package-name.patch + - grub2-GRUB_CMDLINE_LINUX_RECOVERY-for-recovery-mode.patch + - grub2-fix-menu-in-xen-host-server.patch + - grub2-efi-HP-workaround.patch + - grub2-secureboot-chainloader.patch + - grub2-s390x-02-kexec-module-added-to-emu.patch + - grub2-s390x-04-grub2-install.patch + - grub2-s390x-05-grub2-mkconfig.patch + - grub2-efi-xen-chainload.patch + - grub2-mkconfig-aarch64.patch + - grub2-btrfs-04-grub2-install.patch + - grub2-ppc64-cas-reboot-support.patch + - 0002-Add-Virtual-LAN-support.patch + * fix grub2-secureboot-add-linuxefi.patch - use grub_memset and + grub_memcpy instead of memset and memcpy (caused errors due to + compiler warning) + * drop upstream patches + - 0001-grub-core-kern-efi-efi.c-Ensure-that-the-result-star.patch + - 0001-look-for-DejaVu-also-in-usr-share-fonts-truetype.patch + - 0001-efidisk-move-device-path-helpers-in-core-for-efinet.patch + - 0002-efinet-skip-virtual-IPv4-and-IPv6-devices-when-enume.patch + - 0003-efinet-open-Simple-Network-Protocol-exclusively.patch + - 0001-efinet-Check-for-immediate-completition.patch + - 0001-efinet-enable-hardware-filters-when-opening-interfac.patch + - grub2-xen-legacy-config-device-name.patch + - grub2-getroot-support-NVMe-device-names.patch + - grub2-netboot-hang.patch + - grub2-btrfs-fix-incorrect-address-reference.patch + - aarch64-reloc.patch + - grub2-glibc-2.20.patch (related code dropped upstream) + - grub2-Initialized-initrd_ctx-so-we-don-t-free-a-random-poi.patch + - grub2-btrfs-fix-get_root-key-comparison-failures-due-to-en.patch + - grub2-getroot-fix-get-btrfs-fs-prefix-big-endian.patch + - grub2-ppc64-qemu.patch + - grub2-xfs-Add-helper-for-inode-size.patch + - grub2-xfs-Fix-termination-loop-for-directory-iteration.patch + - grub2-xfs-Convert-inode-numbers-to-cpu-endianity-immediate.patch + - grub2-xfs-V5-filesystem-format-support.patch + - 0001-Add-bootargs-parser-for-open-firmware.patch + - grub2-arm64-set-correct-length.patch + - grub2-arm64-setjmp-Add-missing-license-macro.patch + - grub2-arm64-efinet-handle-get_status-on-buggy-firmware-properly.patch + - 0001-unix-password-Fix-file-descriptor-leak.patch + - 0002-linux-getroot-fix-descriptor-leak.patch + - 0003-util-grub-mount-fix-descriptor-leak.patch + - 0004-linux-ofpath-fix-descriptor-leak.patch + - 0005-grub-fstest-fix-descriptor-leak.patch + - ppc64le.patch + - libgcc-prereq.patch + - libgcc.patch + - 0001-Fix-security-issue-when-reading-username-and-passwor.patch + - 0001-menu-fix-line-count-calculation-for-long-lines.patch + - grub2-arm64-Reduce-timer-event-frequency-by-10.patch + - 0001-unix-do-not-close-stdin-in-grub_passwd_get.patch + - 0001-grub-core-kern-i386-tsc.c-calibrate_tsc-Ensure-that.patch + - 0002-i386-tsc-Fix-unused-function-warning-on-xen.patch + - 0003-acpi-do-not-skip-BIOS-scan-if-EBDA-length-is-zero.patch + - 0004-tsc-Use-alternative-delay-sources-whenever-appropria.patch + - 0005-i386-fix-TSC-calibration-using-PIT.patch + - biendian.patch + - ppc64_opt.patch + * drop workarounds for gdb_grub and grub.chrp, they are now installed under fixed name + * do not patch docs/Makefile.in, it is regenerated anyway + +------------------------------------------------------------------- +Tue Mar 1 11:06:34 UTC 2016 - agraf@suse.com + +- Make mkconfig search for zImage on arm + * grub2-mkconfig-arm.patch + +------------------------------------------------------------------- +Sun Feb 28 23:10:06 UTC 2016 - agraf@suse.com + +- Add support to directly pass an EFI FDT table to a kernel on 32bit arm + * 0001-arm64-Move-firmware-fdt-search-into-global-function.patch + * 0002-arm-efi-Use-fdt-from-firmware-when-available.patch + +------------------------------------------------------------------- +Fri Jan 29 03:54:15 UTC 2016 - mchang@suse.com + +- Add config option to set efi xen loader command line option (bsc#957383) + * added grub2-efi-xen-cmdline.patch + +------------------------------------------------------------------- +Thu Jan 28 12:27:27 UTC 2016 - dvaleev@suse.com + +- Drop ppc64le patches. Build stage1 as BE for Power + Droped patches: + - grub2-ppc64le-01-Add-Little-Endian-support-for-Power64-to-the-build.patch + - grub2-ppc64le-02-Build-grub-as-O1-until-we-add-savegpr-and-restgpr-ro.patch + - grub2-ppc64le-03-disable-creation-of-vsx-and-altivec-instructions.patch + - grub2-ppc64le-04-powerpc64-LE-s-linker-knows-how-to-handle-the-undefi.patch + - grub2-ppc64le-05-grub-install-can-now-recognize-and-install-a-LE-grub.patch + - grub2-ppc64le-06-set-the-ABI-version-to-0x02-in-the-e_flag-of-the-PPC.patch + - grub2-ppc64le-07-Add-IEEE1275_ADDR-helper.patch + - grub2-ppc64le-08-Fix-some-more-warnings-when-casting.patch + - grub2-ppc64le-09-Add-powerpc64-types.patch + - grub2-ppc64le-10-powerpc64-is-not-necessarily-BigEndian-anymore.patch + - grub2-ppc64le-11-Fix-warnings-when-building-powerpc-linux-loader-64bi.patch + - grub2-ppc64le-12-GRUB_ELF_R_PPC_-processing-is-applicable-only-for-32.patch + - grub2-ppc64le-13-Fix-powerpc-setjmp-longjmp-64bit-issues.patch + - grub2-ppc64le-14-Add-powerpc64-ieee1275-trampoline.patch + - grub2-ppc64le-15-Add-64bit-support-to-powerpc-startup-code.patch + - grub2-ppc64le-16-Add-grub_dl_find_section_addr.patch + - grub2-ppc64le-17-Add-ppc64-relocations.patch + - grub2-ppc64le-18-ppc64-doesn-t-need-libgcc-routines.patch + - grub2-ppc64le-19-Use-FUNC_START-FUNC_END-for-powerpc-function-definit.patch + - grub2-ppc64le-20-.TOC.-symbol-is-special-in-ppc64le-.-It-maps-to-the-.patch + - grub2-ppc64le-21-the-.toc-section-in-powerpc64le-modules-are-sometime.patch + - grub2-ppc64le-22-all-parameter-to-firmware-calls-should-to-be-BigEndi.patch + - grub2-ppc64le-fix-64bit-trampoline-in-dyn-linker.patch + - grub2-ppc64le-timeout.patch + - grub2-ppc64-build-ppc64-32bit.patch +- Added patches: + - biendian.patch + - grub2-ppc64-cas-reboot-support.patch + - libgcc-prereq.patch + - libgcc.patch + - ppc64_opt.patch + - ppc64le.patch + +------------------------------------------------------------------- +Wed Jan 20 11:44:27 UTC 2016 - mchang@suse.com + +- Backport upstream patches for HyperV gen2 TSC timer calbration without + RTC (bsc#904647) + * added 0001-grub-core-kern-i386-tsc.c-calibrate_tsc-Ensure-that.patch + * added 0002-i386-tsc-Fix-unused-function-warning-on-xen.patch + * added 0003-acpi-do-not-skip-BIOS-scan-if-EBDA-length-is-zero.patch + * added 0004-tsc-Use-alternative-delay-sources-whenever-appropria.patch + * added 0005-i386-fix-TSC-calibration-using-PIT.patch + +------------------------------------------------------------------- +Wed Dec 28 16:53:54 UTC 2015 - arvidjaar@gmail.com + +- Add 0001-menu-fix-line-count-calculation-for-long-lines.patch (bsc#943585) + +------------------------------------------------------------------- +Thu Dec 17 11:04:06 UTC 2015 - olaf@aepfle.de + +- grub2-xen-pv-firmware.cfg: fix hd boot (boo#926795) + +------------------------------------------------------------------- +Wed Dec 16 05:04:37 UTC 2015 - arvidjaar@gmail.com + +- Add 0001-Fix-security-issue-when-reading-username-and-passwor.patch + Fix for CVE-2015-8370 [boo#956631] + +------------------------------------------------------------------- +Wed Dec 9 18:13:27 UTC 2015 - arvidjaar@gmail.com + +- Update grub2-efi-xen-chainload.patch - fix copying of Linux kernel + and initrd to ESP (boo#958193) + +------------------------------------------------------------------- +Mon Dec 7 08:03:41 UTC 2015 - olaf@aepfle.de + +- Rename grub2-xen.cfg to grub2-xen-pv-firmware.cfg (boo#926795) + +------------------------------------------------------------------- +Fri Dec 4 17:06:17 UTC 2015 - olaf@aepfle.de + +- grub2-xen.cfg: to handle grub1 menu.lst in PV guest (boo#926795) + +------------------------------------------------------------------- +Thu Nov 26 10:22:28 UTC 2015 - mchang@suse.com + +- Expand list of grub.cfg search path in PV Xen guest for systems + installed to btrfs snapshot. (bsc#946148) (bsc#952539) + * modified grub2-xen.cfg +- drop grub2-fix-Grub2-with-SUSE-Xen-package-install.patch (bsc#774666) + +------------------------------------------------------------------- +Wed Nov 18 19:33:42 UTC 2015 - arvidjaar@gmail.com + +- Add 0001-unix-do-not-close-stdin-in-grub_passwd_get.patch + Fix reading password by grub2-mkpasswd-pbdk2 without controlling + tty, e.g. when called from Xfce menu (boo#954519) + +------------------------------------------------------------------- +Mon Nov 1 21:30:02 UTC 2015 - arvidjaar@gmail.com + +- Modify grub2-linguas.sh-no-rsync.patch to re-enable en@quot catalog + (boo#953022). Other autogenerated catalogs still fail to build due + to missing C.UTF-8 locale. + +------------------------------------------------------------------- +Fri Oct 30 10:09:02 UTC 2015 - mchang@suse.com + +- Allow to execute menuentry unrestricted as default (fate#318574) + * added grub2-menu-unrestricted.patch + +------------------------------------------------------------------- +Thu Oct 29 04:17:08 UTC 2015 - mchang@suse.com + +- Add missing quoting for linuxefi (bsc#951962) + * modified grub2-secureboot-use-linuxefi-on-uefi.patch + * refreshed grub2-secureboot-provide-linuxefi-config.patch + +------------------------------------------------------------------- +Sun Oct 18 11:45:10 UTC 2015 - eich@suse.com + +- Include custom.cfg into the files scanned by grub2-once. + Allows to chose manually added entries as well (FATE#319632). + +------------------------------------------------------------------- +Wed Oct 7 09:01:37 UTC 2015 - mchang@suse.com + +- Upstream patches for fixing file descriptor leakage (bsc#943784) + * added 0001-unix-password-Fix-file-descriptor-leak.patch + * added 0002-linux-getroot-fix-descriptor-leak.patch + * added 0003-util-grub-mount-fix-descriptor-leak.patch + * added 0004-linux-ofpath-fix-descriptor-leak.patch + * added 0005-grub-fstest-fix-descriptor-leak.patch + +------------------------------------------------------------------- +Tue Oct 6 07:59:47 UTC 2015 - mchang@suse.com + +- Do not force ro option in linuxefi patch (bsc#948555) + * modified grub2-secureboot-use-linuxefi-on-uefi.patch + * refrehed grub2-secureboot-provide-linuxefi-config.patch + +------------------------------------------------------------------- +Wed Sep 23 20:02:47 UTC 2015 - dmueller@suse.com + +- add 0001-efinet-Check-for-immediate-completition.patch, + 0001-efinet-enable-hardware-filters-when-opening-interfac.patch, + grub2-arm64-efinet-handle-get_status-on-buggy-firmware-properly.patch + (bsc#947203) + +------------------------------------------------------------------- +Mon Sep 14 06:36:04 UTC 2015 - mchang@suse.com + +- Set default GRUB_DISTRIBUTOR from /etc/os-release if it is empty + or not set by user (bsc#942519) + * added grub2-default-distributor.patch + * modified grub.default + +------------------------------------------------------------------- +Tue Aug 18 09:53:54 UTC 2015 - mchang@suse.com + +- add systemd-sleep-plugin subpackage (bsc#941758) +- evaluate the menu entry's title string by printf + * modified grub2-once + * added grub2-systemd-sleep.sh + +------------------------------------------------------------------- +Fri Jul 31 03:55:32 UTC 2015 - mchang@suse.com + +- fix for 'rollback' hint (bsc#901487) + * modified grub2-btrfs-05-grub2-mkconfig.patch: + +------------------------------------------------------------------- +Fri Jul 17 08:44:24 UTC 2015 - mchang@suse.com + +- Replace 12.1 with 12 SP1 for the list of snapshots (bsc#934252) + * modified grub2-snapper-plugin.sh + +------------------------------------------------------------------- +Thu Jun 18 08:43:07 UTC 2015 - mchang@suse.com + +- Fix btrfs subvol detection on BigEndian systems (bsc#933541) + * modified grub2-btrfs-06-subvol-mount.patch +- Fix grub2-mkrelpath outputs wrong path on BigEndian system + * added grub2-getroot-fix-get-btrfs-fs-prefix-big-endian.patch + +------------------------------------------------------------------- +Fri Jun 12 07:20:00 UTC 2015 - mchang@suse.com + +- If we have a post entry and the description field is empty, we should use the + "Pre" number and add that description to the post entry. (fate#317972) +- Show user defined comments in grub2 menu for snapshots (fate#318101) + * modified grub2-snapper-plugin.sh + +------------------------------------------------------------------- +Sun Jun 7 04:00:56 UTC 2015 - arvidjaar@gmail.com + +- add 0001-grub-core-kern-efi-efi.c-Ensure-that-the-result-star.patch + make sure firmware path starts with '/' (boo#902982) + +------------------------------------------------------------------- +Fri Jun 5 03:46:33 UTC 2015 - mchang@suse.com + +- Fix btrfs patch on BigEndian systems (bsc#933541) + * modified grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch + * modified grub2-btrfs-06-subvol-mount.patch + +------------------------------------------------------------------- +Wed Jun 3 20:07:33 UTC 2015 - agraf@suse.com + +- Fix license for setjmp module + * added grub2-arm64-setjmp-Add-missing-license-macro.patch + +------------------------------------------------------------------- +Thu May 21 09:23:52 UTC 2015 - mchang@suse.com + +- Fix install into snapper controlled btrfs subvolume and can't + load grub modules from separate subvolume (fate#318392) + * added grub2-btrfs-06-subvol-mount.patch + * grub2-snapper-plugin.sh: use absolute subvol name + +------------------------------------------------------------------- +Tue May 19 17:47:33 UTC 2015 - arvidjaar@gmail.com + +- also Recommends mtools for grub2-mkrescue (used to create EFI + boot image) in addition to libburnia-tools. + +------------------------------------------------------------------- +Mon May 11 08:50:14 UTC 2015 - mchang@suse.com + +- Support booting opensuse installer as PV DomU (boo#926795) + * added grub2-xen.cfg for tracking default pvgrub2 xen configs rather than + generating it from spec file + * grub2-xen.cfg: from Olaf Hering + +------------------------------------------------------------------- +Sun May 10 19:38:00 UTC 2015 - arvidjaar@gmail.com + +- replace grub2-efinet-reopen-SNP-protocol-for-exclusive-use-by-grub.patch + with upstream version: + * 0001-efidisk-move-device-path-helpers-in-core-for-efinet.patch + * 0002-efinet-skip-virtual-IPv4-and-IPv6-devices-when-enume.patch + * 0003-efinet-open-Simple-Network-Protocol-exclusively.patch + Fixes EFI network boot in some QEMU configurations. + +------------------------------------------------------------------- +Wed Apr 29 13:20:20 UTC 2015 - dmueller@suse.com + +- fix grub2-mkconfig-aarch64.patch: fix arch detection broken + by malformed patch rediffing + +------------------------------------------------------------------- +Wed Apr 15 06:02:36 UTC 2015 - mchang@suse.com + +- Cleanup patch not applied + * remove grub2-enable-theme-for-terminal-window.patch + * grub2.rpmlintrc: remove addFilter("patch-not-applied") + +------------------------------------------------------------------- +Thu Apr 2 04:25:52 UTC 2015 - mchang@suse.com + +- Merge changes from SLE12 +- Do not pass root= when root is on nfs (bnc#894374) + * modified grub2-pass-corret-root-for-nfsroot.patch + * modified grub2-secureboot-provide-linuxefi-config.patch + * modified grub2-secureboot-use-linuxefi-on-uefi.patch +- Fix xen pvops kernel not appear on menu (bnc#895286) + * modified grub2-fix-menu-in-xen-host-server.patch +- Workaround grub2-once (bnc#892358) + * added grub2-btrfs-workaround-grub2-once.patch + * added grub2-once.service + * modified grub2-once +- Fix busy-loop and hang while network booting (bnc#870613) + * added grub2-netboot-hang.patch +- Add warning in grubenv file about editing it directly (bnc#887008) + * added grub2-editenv-add-warning-message.patch +- Fix broken graphics with efifb on QEMU/KVM and nomodeset (bnc#884558) + * added grub2-efi-disable-video-cirrus-and-bochus.patch +- Disable video support on Power (bnc#877142) + * added grub2-ppc64le-disable-video.patch +- Track occupied memory so it can be released on exit (bnc#885026) + * added grub2-ppc64le-memory-map.patch +- Fix grub.xen config searching path on boot partition (bnc#884828) +- Add linux16 and initrd16 to grub.xen (bnc#884830) + * added grub2-xen-linux16.patch +- VLAN tag support (fate#315753) + * added 0001-Add-bootargs-parser-for-open-firmware.patch + * added 0002-Add-Virtual-LAN-support.patch +- Use chainloader to boot xen.efi under UEFI (bnc#871857) + * added grub2-efi-xen-chainload.patch +- Use device part of chainloader target, if present (bnc#871857) + * added grub2-efi-chainloader-root.patch +- Create only hypervisor pointed by /boot/xen.gz symlink (bnc#877040) + * modified grub2-fix-Grub2-with-SUSE-Xen-package-install.patch +- Fix xen and native entries differ in grub.cfg (bnc#872014) + * modified grub2-linux.patch +- Fix install error on ddf md device (bnc#872360) + * added grub2-getroot-treat-mdadm-ddf-as-simple-device.patch +- Fix booting from NVMe device (bnc#873132) + * added grub2-getroot-support-NVMe-device-names.patch +- Document peculiarities of s390 terminals + * added README.ibm3215 +- Grub2 for System z (fate#314213) + * added grub2-s390x-02-kexec-module-added-to-emu.patch + * added grub2-s390x-03-output-7-bit-ascii.patch + * added grub2-s390x-04-grub2-install.patch + * added grub2-s390x-05-grub2-mkconfig.patch + +------------------------------------------------------------------- +Mon Mar 16 08:08:32 UTC 2015 - schwab@suse.de + +- grub2-arm64-set-correct-length.patch: arm64: set correct length of + device path end entry + +------------------------------------------------------------------- +Wed Mar 4 04:03:37 UTC 2015 - mchang@suse.com + +- grub2-efi-HP-workaround.patch: + * try to read config from all-uppercase prefix as last resort. + (bnc#872503) (boo#902982) + +------------------------------------------------------------------- +Mon Feb 16 16:25:50 UTC 2015 - arvidjaar@gmail.com + +- add luks, gcry_rijndael, gcry_sha1 to signed EFI image to support + LUKS partition in default setup (boo#917427) + +------------------------------------------------------------------- +Thu Feb 5 09:37:46 UTC 2015 - mchang@suse.com + +- enable i386-xen (boo#891043) + +------------------------------------------------------------------- +Wed Feb 4 07:43:27 UTC 2015 - mchang@suse.com + +- Downgrade os-prober dependency to Recommends (boo#898610) + +------------------------------------------------------------------- +Thu Dec 25 08:52:12 UTC 2014 - mchang@suse.com + +- grub2-snapper-plugin.sh: cleanup grub-snapshot.cfg not referring + to any snapshot (boo#909359) + +------------------------------------------------------------------- +Thu Dec 25 08:34:49 UTC 2014 - mpluskal@suse.com + +- Require efibootmgr also on i586 + +------------------------------------------------------------------- +Tue Dec 16 10:41:08 UTC 2014 - schwab@suse.de + +- Require efibootmgr also on aarch64 + +------------------------------------------------------------------- +Thu Dec 11 11:20:13 UTC 2014 - schwab@suse.de + +- grub2-snapper-plugin.sh: fix use of printf without format string; fix + quoting + +------------------------------------------------------------------- +Wed Dec 10 09:12:47 UTC 2014 - schwab@suse.de + +- grub2-arm64-Reduce-timer-event-frequency-by-10.patch: fix periodic timer + on arm64 + +------------------------------------------------------------------- +Thu Dec 4 01:42:39 UTC 2014 - agraf@suse.com + +- enable 32bit arm targets for uboot and efi + +------------------------------------------------------------------- +Sat Nov 29 18:26:00 UTC 2014 - Led + +- Replace 'echo -e' command in grub2-snapper-plugin.sh script to + 'printf' command. '-e' option of 'echo' command may be + unsupported in some POSIX-complete shells. + +------------------------------------------------------------------- +Fri Nov 14 16:06:00 UTC 2014 - Led + +- fix bashism in post script + +------------------------------------------------------------------- +Thu Oct 30 15:18:16 CET 2014 - jdelvare@suse.de + +- grub2.spec: Fix conditional construct which wasn't supported by + older versions of rpmbuild (caused error message + "parseExpressionBoolean returns -1".) + +------------------------------------------------------------------- +Thu Oct 30 07:36:15 UTC 2014 - mchang@suse.com + +- fix errors when boot is btrfs with Windows partition scheme. The + first partition is created on cylinder boundary that can't offer + enough room for core.img and also the installation has to be in + logical paritition which made MBR the only location to install. + (bnc#841247) + * add grub2-setup-try-fs-embed-if-mbr-gap-too-small.patch + +------------------------------------------------------------------- +Tue Sep 30 03:45:04 UTC 2014 - mchang@suse.com + +- packaging 20_memtest86+ and 20_ppc_terminfo in corresponing grubarch + package + +------------------------------------------------------------------- +Mon Sep 29 07:22:29 UTC 2014 - fcastelli@suse.com + +- Add '80_suse_btrfs_snapshot' required to show btrfs snapshots inside + of the boot menu. + +------------------------------------------------------------------- +Sun Sep 28 06:16:35 UTC 2014 - arvidjaar@gmail.com + +- fix btrfs on big endian systems (ppc/ppc64) + * add grub2-btrfs-fix-get_root-key-comparison-failures-due-to-en.patch + +------------------------------------------------------------------- +Sun Sep 21 06:47:12 UTC 2014 - arvidjaar@gmail.com + +- update translations +- fix possible access to uninitialized pointer in linux loader + * add grub2-Initialized-initrd_ctx-so-we-don-t-free-a-random-poi.patch + * drop superceded grub2-ppc64le-23-grub-segfaults-if-initrd-is-specified-before-specify.patch + +------------------------------------------------------------------- +Thu Sep 18 09:55:57 UTC 2014 - mchang@suse.com + +- fix grub.xen not able to handle legacy menu.lst hdX names (bnc#863821) + * add grub2-xen-legacy-config-device-name.patch from arvidjaar +- fix the performance of grub2 uefi pxe is bad (bnc#871555) + * add grub2-efinet-reopen-SNP-protocol-for-exclusive-use-by-grub.patch + +------------------------------------------------------------------- +Tue Sep 16 07:08:18 UTC 2014 - schwab@suse.de + +- grub2-mkconfig-aarch64.patch: Look for Image-* instead of vmlinuz-* on + aarch64 + +------------------------------------------------------------------- +Mon Sep 15 15:30:03 UTC 2014 - arvidjaar@gmail.com + +- add grub2-glibc-2.20.patch - fix build with glibc 2.20+ + (use _DEFAULT_SOURCE to avoid warning) + +------------------------------------------------------------------- +Fri Sep 12 04:14:38 UTC 2014 - mchang@suse.com + +- fix xen pvops kernel not appear on menu (bnc#895286) + * refresh grub2-fix-menu-in-xen-host-server.patch + +------------------------------------------------------------------- +Wed Sep 10 10:34:47 UTC 2014 - mchang@suse.com + +- fix extraneous comma in printf shell command (bnc#895884) + * refresh grub2-btrfs-04-grub2-install.patch + +------------------------------------------------------------------- +Wed Aug 27 07:53:35 UTC 2014 - schwab@suse.de + +- aarch64-reloc.patch: replace with upstream solution + +------------------------------------------------------------------- +Mon Aug 25 03:10:18 UTC 2014 - mchang@suse.com + +- remove unused patch, which's supersceded by new snapper rollback + support patches + * 0001-script-provide-overridable-root-by-subvol.patch + * 0002-script-create-menus-for-btrfs-snapshot.patch + +------------------------------------------------------------------- +Fri Aug 22 10:05:13 UTC 2014 - mchang@suse.com + +- fix openqa boot error on separate boot partition + * refresh grub2-btrfs-05-grub2-mkconfig.patch + +------------------------------------------------------------------- +Thu Aug 21 06:10:07 UTC 2014 - mchang@suse.com + +- update snapper plugin for rollback support + * refresh grub2-snapper-plugin.sh + +------------------------------------------------------------------- +Fri Aug 15 07:55:54 UTC 2014 - mchang@suse.com + +- snapper rollback support patches. +- rename patch + * 0002-btrfs-add-ability-to-boot-from-subvolumes.patch to + grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch + * 0004-btrfs-export-subvolume-envvars.patch to + grub2-btrfs-02-export-subvolume-envvars.patch +- added patches + * grub2-btrfs-03-follow_default.patch + * grub2-btrfs-04-grub2-install.patch + * grub2-btrfs-05-grub2-mkconfig.patch +- remove patch + * 0003-cmdline-add-envvar-loader_cmdline_append.patch + +------------------------------------------------------------------- +Thu Aug 14 06:35:58 UTC 2014 - mchang@suse.com + +- grub2-btrfs-fix-incorrect-address-reference.patch + * Fix incorrect address reference in GRUB_BTRFS_EXTENT_REGULAR + range check (bnc#869748) + +------------------------------------------------------------------- +Wed Aug 13 02:56:22 UTC 2014 - mchang@suse.com + +- grub2-vbe-blacklist-preferred-1440x900x32.patch + * Blacklist preferred resolution 1440x900x32 which is broken on + many Thinkpads (bnc#888727) + +------------------------------------------------------------------- +Tue Aug 12 12:56:29 UTC 2014 - schwab@suse.de + +- Enable building on aarch64 +- aarch64-reloc.patch: support R_AARCH64_PREL32 relocation +- Build host tools with RPM_OPT_FLAGS + +------------------------------------------------------------------- +Mon Aug 11 14:34:55 UTC 2014 - dvaleev@suse.com + +- Fix the 64-bit trampoline code in dynamic linker (bnc#890999) + grub2-ppc64le-fix-64bit-trampoline-in-dyn-linker.patch + +------------------------------------------------------------------- +Tue Jul 29 11:46:54 CEST 2014 - tiwai@suse.de + +- Prefer a higher resolution in efi_gop driver if the mode taking + over is too small like 640x480 (bnc#887972): + grub2-efi_gop-avoid-low-resolution.patch + +------------------------------------------------------------------- +Wed Jul 9 16:26:35 UTC 2014 - dvlaeev@suse.com + +- Fix ppc64le build by fixing + grub2-xfs-V5-filesystem-format-support.patch + +------------------------------------------------------------------- +Wed Jun 25 09:20:16 UTC 2014 - jack@suse.cz + +- xfs V5 superblock support (bnc#880166 bnc#883942) + +- added patches: + * grub2-xfs-Add-helper-for-inode-size.patch + * grub2-xfs-Fix-termination-loop-for-directory-iteration.patch + * grub2-xfs-Convert-inode-numbers-to-cpu-endianity-immediate.patch + * grub2-xfs-V5-filesystem-format-support.patch + +------------------------------------------------------------------- +Fri Jun 20 19:50:28 UTC 2014 - jeffm@suse.com + +- grub2: use stat instead of udevadm for partition lookup (bnc#883635) + * Added grub2-use-stat-instead-of-udevadm-for-partition-lookup.patch + +------------------------------------------------------------------- +Tue Apr 15 08:36:46 UTC 2014 - tchvatal@suse.com + +- Fix sorting of RC kernels to be older than first regular of the + series. Fixes bnc#827531. + +- added patches: + * grub2-use-rpmsort-for-version-sorting.patch + +------------------------------------------------------------------- +Thu Apr 10 16:35:08 UTC 2014 - dvaleev@suse.com + +- Build GRUB2 for ppc64le as LittleEndian and 64bit +- Fix timeout issue on ppc64le (bnc#869166) +- Add powerpc-utils requires to grub2-powerpc-ieee1275 + +- added patches: + * grub2-ppc64-build-ppc64-32bit.patch + * grub2-ppc64-qemu.patch + * grub2-ppc64le-01-Add-Little-Endian-support-for-Power64-to-the-build.patch + * grub2-ppc64le-02-Build-grub-as-O1-until-we-add-savegpr-and-restgpr-ro.patch + * grub2-ppc64le-03-disable-creation-of-vsx-and-altivec-instructions.patch + * grub2-ppc64le-04-powerpc64-LE-s-linker-knows-how-to-handle-the-undefi.patch + * grub2-ppc64le-05-grub-install-can-now-recognize-and-install-a-LE-grub.patch + * grub2-ppc64le-06-set-the-ABI-version-to-0x02-in-the-e_flag-of-the-PPC.patch + * grub2-ppc64le-07-Add-IEEE1275_ADDR-helper.patch + * grub2-ppc64le-08-Fix-some-more-warnings-when-casting.patch + * grub2-ppc64le-09-Add-powerpc64-types.patch + * grub2-ppc64le-10-powerpc64-is-not-necessarily-BigEndian-anymore.patch + * grub2-ppc64le-11-Fix-warnings-when-building-powerpc-linux-loader-64bi.patch + * grub2-ppc64le-12-GRUB_ELF_R_PPC_-processing-is-applicable-only-for-32.patch + * grub2-ppc64le-13-Fix-powerpc-setjmp-longjmp-64bit-issues.patch + * grub2-ppc64le-14-Add-powerpc64-ieee1275-trampoline.patch + * grub2-ppc64le-15-Add-64bit-support-to-powerpc-startup-code.patch + * grub2-ppc64le-16-Add-grub_dl_find_section_addr.patch + * grub2-ppc64le-17-Add-ppc64-relocations.patch + * grub2-ppc64le-18-ppc64-doesn-t-need-libgcc-routines.patch + * grub2-ppc64le-19-Use-FUNC_START-FUNC_END-for-powerpc-function-definit.patch + * grub2-ppc64le-20-.TOC.-symbol-is-special-in-ppc64le-.-It-maps-to-the-.patch + * grub2-ppc64le-21-the-.toc-section-in-powerpc64le-modules-are-sometime.patch + * grub2-ppc64le-22-all-parameter-to-firmware-calls-should-to-be-BigEndi.patch + * grub2-ppc64le-23-grub-segfaults-if-initrd-is-specified-before-specify.patch + * grub2-ppc64le-timeout.patch +- removed patches: + * grub2-powerpc-libgcc.patch + * grub2-ppc64le-core-bigendian.patch + * grub2-ppc64le-platform.patch +------------------------------------------------------------------- +Thu Apr 10 07:39:30 UTC 2014 - mchang@suse.com + +- add grub2-x86_64-xen subpackage (bnc#863821) + +------------------------------------------------------------------- +Sat Apr 5 14:27:45 UTC 2014 - arvidjaar@gmail.com + +- rename grub2.chrp back into grub.chrp, otherwise it is not found by + grub tools +- replace grub2-use-DejaVuSansMono-for-starfield-theme.patch with + grub2-use-Unifont-for-starfield-theme-terminal.patch - use Unifont + font for terminal window + +------------------------------------------------------------------- +Thu Feb 27 04:30:07 UTC 2014 - mchang@suse.com + +- grub2-snapper-plugin: fix important snapshots are not marked as such + in grub2 menu, also display the snapshot entries in the format + "important distribution version (kernel_version, timestamp, pre/post)" + (bnc#864842) + +------------------------------------------------------------------- +Mon Feb 24 07:28:42 UTC 2014 - mchang@suse.com + +- refresh grub2-fix-menu-in-xen-host-server.patch (bnc#859361) + * prevent 10_linux from booting xen kernel without pv_opt support + on systems other than xen PV domU guest + * prevent 20_linux_xen.in from setting up nested virt running from + Xen domU +- refresh grub2-fix-Grub2-with-SUSE-Xen-package-install.patch + * adjust accordingly + +------------------------------------------------------------------- +Thu Feb 20 14:43:21 UTC 2014 - jw@suse.com + +- updating grub2-once + - added --list switch. + - improved --help and error handling. + +------------------------------------------------------------------- +Tue Feb 11 03:02:21 UTC 2014 - mchang@suse.com + +- add Supplements: packageand(snapper:grub2) in grub2-snapper-plugin + to install it while both snapper and grub2 are installed + +------------------------------------------------------------------- +Wed Feb 5 04:33:55 UTC 2014 - mchang@suse.com + +- add grub2-snapper-plugin.sh (fate#316232) + * grub2's snapper plugin for advanced btrfs snapshot menu management + * package as grub2-snapper-plugin.noarch +- refresh 0002-script-create-menus-for-btrfs-snapshot.patch + * when booting btrfs snapshots disabled, deleting snapshot master config + if it's not customized + +------------------------------------------------------------------- +Fri Jan 31 14:42:26 UTC 2014 - dvaleev@suse.com + +- Enable grub2 for PowerPC LE (ppc64le) +- Add ppc64le to exclusive arches +- Don't require gcc-32bit (PowerLE don't have 32bit toolchain) + +- added patches: + * grub2-powerpc-libgcc.patch + Provide 32bit libgcc functions for PowerLE + + * grub2-ppc64le-core-bigendian.patch + Build grub kernel and images as BE on ppc64le (BL is BE there) + + * grub2-ppc64le-platform.patch + Enable ppc64le platform + +------------------------------------------------------------------- +Fri Jan 24 13:48:13 CET 2014 - jjolly@suse.com + +- Add changes to allow build for s390x arch: added + grub2-s390x-01-Changes-made-and-files-added-in-order-to-allow-s390x.patch + +------------------------------------------------------------------- +Wed Jan 22 05:19:35 UTC 2014 - mchang@suse.com + +- refresh 0002-script-create-menus-for-btrfs-snapshot.patch +* Fix bootable snapshots not found while root is on Btrfs subvolume + (bnc#859587) +* Create missing slave config in /.snapshots// +* Prefix with SUSE_ for related options + +------------------------------------------------------------------- +Fri Jan 17 06:23:04 UTC 2014 - mchang@suse.com + +- refresh 0001-script-provide-overridable-root-by-subvol.patch +* Introduce $boot_prefix for setting prefix on seeking other /boot + directory. +- refresh 0002-script-create-menus-for-btrfs-snapshot.patch +* Support existing snapshots by creating their missing slave configs. +* Temporarily default to disable this feature until receiving more + tests from QA. +* Introduce GRUB_ENABLE_CUSTOM_SNAPSHOT_SUBMENU to allow custom + submenu for listing snapshots rather than the default one. + +------------------------------------------------------------------- +Wed Jan 15 15:46:31 UTC 2014 - arvidjaar@gmail.com + +- package autoiso.cfg and osdetect.cfg as documentation +- add 0001-look-for-DejaVu-also-in-usr-share-fonts-truetype.patch - + fix configure test for DejaVu font +- add dejavu-fonts to BR (needed to build starfield theme) +- package starfield theme as grub2-branding-upstream +- add grub2-use-DejaVuSansMono-for-starfield-theme.patch - use fixed width + font for starfield theme +- clarify that grub2 subpackage contains only user space tools + +------------------------------------------------------------------- +Wed Jan 15 06:18:10 UTC 2014 - mchang@suse.com + +- add new patches for booting btrfs snapshot (fate#316522) (fate#316232) + * 0001-script-provide-overridable-root-by-subvol.patch + * 0002-script-create-menus-for-btrfs-snapshot.patch + +------------------------------------------------------------------- +Fri Dec 27 16:36:40 UTC 2013 - arvidjaar@gmail.com + +- update to grub-2.02 beta2 + * drop upstream patches + - grub2-fix-unquoted-string-in-class.patch (different) + - grub2-cdpath.patch (modified) + - grub2-fix-parsing-of-short-LVM-PV-names.patch + - grub2-fix-descriptor-leak-in-grub_util_is_imsm.patch + - grub2-install-opt-skip-fs-probe.patch (file it patched no more exists, + functionality included upstream) + - grub2-fix-x86_64-efi-startup-stack-alignment.patch + - grub2-fix-x86_64-efi-callwrap-stack-alignment.patch + - 0001-Fix-build-with-FreeType-2.5.1.patch + * rediff + - grub2-linux.patch + - use-grub2-as-a-package-name.patch (do not patch generated configure) + - grub2-GRUB_CMDLINE_LINUX_RECOVERY-for-recovery-mode.patch + - grub2-fix-locale-en.mo.gz-not-found-error-message.patch (upstream added + explicit exclusion for en_* language only; I do not see reason to stop + with error in this case for any language). + - not-display-menu-when-boot-once.patch + - grub2-secureboot-provide-linuxefi-config.patch + - grub2-pass-corret-root-for-nfsroot.patch + - 0002-btrfs-add-ability-to-boot-from-subvolumes.patch + - grub2-fix-menu-in-xen-host-server.patch + - grub2-fix-Grub2-with-SUSE-Xen-package-install.patch + - grub2-secureboot-add-linuxefi.patch + - grub2-secureboot-no-insmod-on-sb.patch + - rename-grub-info-file-to-grub2.patch + * drop Makefile.util.am and Makefile.core.am, they are now generated + during build + * call ./autogen.sh again now when it does not need autogen anymore; drop + autoreconf call, it is called by autogen.sh + * drop 0001-btrfs-rename-skip_default-to-follow_default.patch - is not + needed anymore due to upstream changes + * package /usr/bin/grub2-file, /usr/bin/grub2-syslinux2cfg and + /usr/sbin/grub2-macbless + * use grub-install --no-bootsector instead of --grub-setup=/bin/true + in postinstall script + +------------------------------------------------------------------- +Tue Dec 17 07:20:33 UTC 2013 - mchang@suse.com + +- add new patches for booting btrfs snapshot (fate#316522) (fate#316232) + * 0001-btrfs-rename-skip_default-to-follow_default.patch + * 0002-btrfs-add-ability-to-boot-from-subvolumes.patch + * 0003-cmdline-add-envvar-loader_cmdline_append.patch + * 0004-btrfs-export-subvolume-envvars.patch + +------------------------------------------------------------------- +Tue Dec 10 19:13:53 UTC 2013 - arvidjaar@gmail.com + +- add patch 0001-Fix-build-with-FreeType-2.5.1.patch - fix build with + freetype2 >= 2.5.1 (backport from fd0df6d098b1e6a4f60275c48a3ec88d15ba1fbb) + +------------------------------------------------------------------- +Sun Dec 1 13:10:23 UTC 2013 - arvidjaar@gmail.com + +- reset executable bits on *module, *.exec and *.image files. They are not + executable. + +------------------------------------------------------------------- +Fri Nov 22 07:12:16 UTC 2013 - glin@suse.com + +- add grub2-fix-x86_64-efi-startup-stack-alignment.patch and + grub2-fix-x86_64-efi-callwrap-stack-alignment.patch: fix the + stack alignment of x86_64 efi. (bnc#841426) + +------------------------------------------------------------------- +Wed Sep 11 07:17:07 UTC 2013 - mchang@suse.com + +- use new update-bootloader option --reinit to install and update + bootloader config +- refresh grub2-secureboot-no-insmod-on-sb.patch to fobid module + loading completely. + +------------------------------------------------------------------- +Mon Sep 9 09:22:34 UTC 2013 - lnussel@suse.de + +- replace openSUSE UEFI certificate with new 2048 bit certificate. + +------------------------------------------------------------------- +Sat Jul 27 10:12:36 UTC 2013 - arvidjaar@gmail.com + +- add grub2-fix-parsing-of-short-LVM-PV-names.patch - fix PV detection in + grub-probe when PV name is less than 10 charaters +- add grub2-fix-descriptor-leak-in-grub_util_is_imsm.patch - fix decriptor + leak which later caused LVM warnings during grub-probe invocation +- remove --enable-grub-emu-usb - it is not needed on physical platform + +------------------------------------------------------------------- +Tue Jul 9 10:54:41 UTC 2013 - mchang@suse.com + +- refresh grub2-fix-menu-in-xen-host-server.patch: In domU we + have to add xen kernel to config. (bnc#825528) + +------------------------------------------------------------------- +Wed Jun 26 17:02:08 UTC 2013 - elchevive@opensuse.org + +- updated existent translations and include new ones + (es, lt, pt_BR, sl, tr) + +------------------------------------------------------------------- +Sun Jun 16 12:42:33 UTC 2013 - arvidjaar@gmail.com + +- update to current upstream trunk rev 5042 + * drop upstream patches + - grub2-correct-font-path.patch + - grub2-fix-mo-not-copied-to-grubdir-locale.patch + - grub2-stdio.in.patch + - grub2-fix-build-error-on-flex-2.5.37.patch + - grub2-quote-messages-in-grub.cfg.patch + - 30_os-prober_UEFI_support.patch + - grub2-fix-enumeration-of-extended-partition.patch + - grub2-add-device-to-os_prober-linux-menuentry.patch + - grub2-fix-tftp-endianness.patch + - efidisk-ahci-workaround + - grub2-grub-mount-return-failure-if-FUSE-failed.patch + * rediff + - rename-grub-info-file-to-grub2.patch + - grub2-linux.patch + - use-grub2-as-a-package-name.patch + - grub2-iterate-and-hook-for-extended-partition.patch + - grub2-secureboot-add-linuxefi.patch + - grub2-secureboot-no-insmod-on-sb.patch + - grub2-secureboot-chainloader.patch + * add + - grub2-linguas.sh-no-rsync.patch + + disable rsync in linguas.sh so it can be used during RPM build + + disable auto-generated catalogs, they fail at the moment due to + missing C.UTF-8 locale + * update Makefile.util.am and Makefile.core.am + * grub2-mknetdir is now in /usr/bin + * generate po/LINGUAS for message catalogs using distributed linguas.sh + * remove po/stamp-po during setup to trigger message catalogs rebuild + * package bootinfo.txt on PPC (used by grub2-mkrescue) + +------------------------------------------------------------------- +Sat Apr 13 08:48:52 UTC 2013 - arvidjaar@gmail.com + +- BuildRequires: help2man to generate man pages and package them too + +------------------------------------------------------------------- +Fri Apr 5 17:01:42 UTC 2013 - arvidjaar@gmail.com + +- add grub2-secureboot-use-linuxefi-on-uefi-in-os-prober.patch (bnc#810912) + * use linuxefi in 30_os-prober if secure boot is enabled + +------------------------------------------------------------------- +Wed Apr 3 17:56:20 UTC 2013 - arvidjaar@gmail.com + +- update rename-grub-info-file-to-grub2.patch + * do not rename docs/grub2.texi here, do it in %%prep (we do it there + conditionally already). It simplifies patch refreshing using quilt + which does not support file rename. + +------------------------------------------------------------------- +Wed Apr 3 06:55:52 UTC 2013 - mchang@suse.com + +- refresh grub2-secureboot-chainloader.patch: Fix wrongly aligned + buffer address (bnc#811608) + +------------------------------------------------------------------- +Thu Mar 28 02:57:47 UTC 2013 - mchang@suse.com + +- package Secure Boot CA file as /usr/lib64/efi/grub.der which + could be used to verify signed image from build server +- add openSUSE-UEFI-CA-Certificate.crt, openSUSE Secure Boot CA +- add SLES-UEFI-CA-Certificate.crt, SUSE Linux Enterprise Secure + Boot CA + +------------------------------------------------------------------- +Mon Mar 25 17:37:59 UTC 2013 - dvaleev@suse.com + +- extraconfigure macro is not defined on ppc + +------------------------------------------------------------------- +Sat Mar 23 18:31:07 UTC 2013 - arvidjaar@gmail.com + +- corretly set chainloaded image device handle in secure boot mode (bnc#809038) + +------------------------------------------------------------------- +Wed Mar 13 11:30:52 UTC 2013 - mchang@suse.com + +- remove all compatible links in grub2-efi as now all concerned + utilities are fixed +- superseding grub2-efi by grub2-x86_64-efi and grub2-i386-efi on + x86_64 and ix86 respectively +- make grub2-x86_64-efi and grub2-i386-efi providing grub2-efi + capability to not break package dependency +- handle upgrade from 12.2 by preseving grubenv and custom.cfg to + new directory /boot/grub2, rename /boot/grub2-efi to + /boot/grub2-efi.rpmsave to avoid confusion. + +------------------------------------------------------------------- +Mon Mar 11 16:02:26 UTC 2013 - arvidjaar@gmail.com + +- move post scripts into corresponding subpackages to ensure they are + run after updated binaries are installed. Currently it may happen + that update-bootlader picks up old binaries. +- move requires for perl-Bootloader to target subpackages. Make sure + efi requires minimal version that supports /boot/grub2. +- add requires(post) to force order of installation: grub2 => grub2-arch + => grub2-efi +- split efi post in two parts. One that updates configuration and is part + of grub2-efiarch and second that migrates settings and is part of + grub2-efi. Only custom.cfg and grubenv may need migration. device.map + is not relevant for EFI and new grub.cfg had been created at this point. + +------------------------------------------------------------------- +Mon Mar 11 06:52:58 UTC 2013 - mchang@suse.com + +- add grub2-fix-tftp-endianness.patch from upstream (bnc#808582) +- add efinet and tftp to grub.efi (bnc#808582) + +------------------------------------------------------------------- +Thu Mar 7 15:39:50 UTC 2013 - seife+obs@b1-systems.com + +- convert spec file to UTF-8 + +------------------------------------------------------------------- +Thu Mar 7 08:12:57 UTC 2013 - mchang@suse.com + +- add lvm to grub.efi (bnc#807989) +- add loadenv to grub.efi (bnc#807992) + +------------------------------------------------------------------- +Mon Mar 4 16:48:32 UTC 2013 - arvidjaar@gmail.com + +- grub2-grub-mount-return-failure-if-FUSE-failed.patch - return error + if fuse_main failed (bnc#802983) + +------------------------------------------------------------------- +Mon Feb 25 13:31:31 UTC 2013 - fcrozat@suse.com + +- Fix build for SLES 11. + +------------------------------------------------------------------- +Tue Feb 19 15:38:04 UTC 2013 - duwe@suse.com + + Fix up bogus items from the previous merge: + - efi_libdir = _libdir = /usr/lib + - package /usr/lib/grub2 dir only once + - move grub.efi to /usr/lib/grub2/%{grubefiarch}/ + - create a symlink so that scripts can find it there. + +------------------------------------------------------------------- +Thu Feb 14 11:42:40 UTC 2013 - duwe@suse.com + +- merge internal+external BS changes into superset spec file, + remove obsolete dependencies +- merge SLES+openSUSE patches, restrict "grub-efi" to 12.2 +- add efidisk-ahci-workaround (bnc#794674) +- fix unquoted-string-in-class.patch (bnc#788322) + +------------------------------------------------------------------- +Fri Feb 8 01:58:22 UTC 2013 - mchang@suse.com + +- adapt to pesign-obs-integration changes + +------------------------------------------------------------------- +Thu Feb 7 10:38:42 UTC 2013 - mchang@suse.com + +- grub.efi signing on build server. + +------------------------------------------------------------------- +Thu Jan 31 16:18:56 UTC 2013 - duwe@suse.com + +- switch to out of source / subdir build + +------------------------------------------------------------------- +Wed Jan 30 07:29:29 UTC 2013 - mchang@suse.com + +- sync from SLE-11 SP3 to date +- set empty prefix to grub.efi for looking up in current directory +- grub2-cdpath.patch: fix the grub.cfg not found when booting from + optical disk +- put grub.efi in grub2's source module directory +- create links in system's efi directory to grub.efi +- arvidjaar: do not overwrite device path in grub2-cdpath.patch + +------------------------------------------------------------------- +Wed Jan 30 04:36:45 UTC 2013 - arvidjaar@gmail.com + +- remove obsolete reference to /boot/grub2-efi and /usr/sbin/grub2-efi + from grub2-once +- add GRUB_SAVEDFAULT description to /etc/default/grub + +------------------------------------------------------------------- +Tue Jan 29 02:42:28 UTC 2013 - mchang@suse.com + +- set empty prefix to grub.efi for looking up in current directory +- remove grubcd.efi, as grub.efi can now be used for cdrom booting + +------------------------------------------------------------------- +Mon Jan 28 08:05:52 CET 2013 - snwint@suse.de + +- add fat module to grubcd +- explicitly set empty prefix to get grub to set $prefix to the currrent + directory + +------------------------------------------------------------------- +Fri Jan 18 07:39:18 UTC 2013 - mchang@suse.com + +- ship a Secure Boot UEFI compatible bootloader (fate#314485) +- add grub2-secureboot-chainloader.patch, which expands the efi + chainloader to be able to verify images via shim lock protocol. + +------------------------------------------------------------------- +Fri Jan 18 06:24:57 UTC 2013 - mchang@suse.com + +- ship a Secure Boot UEFI compatible bootloader (fate#314485). +- update for cdrom boot support. +- grub2-cdpath.patch: fix the grub.cfg not found when booting from + optical disk. +- grubcd.efi: the efi image used for optial disk booting, with + reduced size and $prefix set to /EFI/BOOT. + +------------------------------------------------------------------- +Tue Jan 8 08:09:01 UTC 2013 - mchang@suse.com + +- add grub2-fix-unquoted-string-in-class.patch (bnc#788322) + +------------------------------------------------------------------- +Tue Jan 8 07:09:47 UTC 2013 - arvidjaar@gmail.com + +- add grub2-add-device-to-os_prober-linux-menuentry.patch (bnc#796919) + +------------------------------------------------------------------- +Sun Jan 6 18:54:54 UTC 2013 - arvidjaar@gmail.com + +- add patch grub2-fix-enumeration-of-extended-partition.patch to + fix enumeration of extended partitions with non-standard EBR (bnc#779534) + +------------------------------------------------------------------- +Fri Jan 4 10:29:58 UTC 2013 - arvidjaar@gmail.com + +- add support for chainloading another UEFI bootloader to + 30_os-prober (bnc#775610) + +------------------------------------------------------------------- +Fri Dec 21 04:18:06 UTC 2012 - mchang@suse.com + +- put 32-bit grub2 modules to /usr/lib/grub2 +- put 64-bit grub2 modules to /usr/lib64/grub2 (x86_64-efi) +- put grub.efi to /usr/lib64/efi(x86_64) or /usr/lib/efi(i586) + +------------------------------------------------------------------- +Tue Dec 18 03:43:38 UTC 2012 - mchang@suse.com + +- ship a Secure Boot UEFI compatible bootloader (fate#314485) +- add grub2-secureboot-chainloader.patch, which expands the efi + chainloader to be able to verify images via shim lock protocol. + +------------------------------------------------------------------- +Fri Nov 30 06:39:15 UTC 2012 - mchang@suse.com + +- replace %{sles_version} by %{suse_version} +- use correct product name + +------------------------------------------------------------------- +Mon Nov 26 08:26:10 UTC 2012 - mchang@suse.com + +- ship a Secure Boot UEFI compatible bootloader (fate#314485) +- added secureboot patches which introduces new linuxefi module + that is able to perform verifying signed images via exported + protocol from shim. The insmod command will not function if + secure boot enabled (as all modules should built in grub.efi + and signed). + - grub2-secureboot-add-linuxefi.patch + - grub2-secureboot-use-linuxefi-on-uefi.patch + - grub2-secureboot-no-insmod-on-sb.patch + - grub2-secureboot-provide-linuxefi-config.patch +- Makefile.core.am : support building linuxefi module +- Make grub.efi image that is with all relevant modules incorporated + and signed, it will be the second stage to the shim loader which + will verified it when secureboot enabled. +- Make grub.efi's path to align with shim loader's default loader + lookup path. +- The changes has been verified not affecting any factory instalation, + but will allow us to run & test secure boot setup manually with shim. + +------------------------------------------------------------------- +Thu Nov 22 07:01:31 UTC 2012 - mchang@suse.com + +- ship a Secure Boot UEFI compatible bootloader (fate#314485) +- In SLE-11 SP3, don't include any other architecture binaries + except EFI, so we split packages by architecture binaries to + meet the requirement. + - grub2 : common utilties and config etc + - grub2-efi : provide compatibilty to grub2-efi package + - grub2-i386-pc : binaries for x86 legacy pc firmware + - grub2-i386-efi : binaries for ia32 EFI firmware + - grub2-x86_64-efi : binaries for x86_64 firmware + - grub2-powerpc-ieee1275: binaries for powerpc open firmware + +------------------------------------------------------------------- +Tue Nov 20 16:14:50 UTC 2012 - arvidjaar@gmail.com + +- update grub2-quote-messages-in-grub.cfg.patch to use upstream commit + +------------------------------------------------------------------- +Mon Nov 19 16:40:25 UTC 2012 - arvidjaar@gmail.com + +- quote localized "Loading ..." messages in grub.cfg (bnc#790195) + +------------------------------------------------------------------- +Mon Nov 5 08:17:26 UTC 2012 - aj@suse.de + +- We really only need makeinfo, so require that one where it exists. + +------------------------------------------------------------------- +Thu Nov 1 08:10:12 UTC 2012 - mchang@suse.com + +- ship a Secure Boot UEFI compatible bootloader (fate#314485) +- Secure boot support in installer DVD (fate#314489) +- prime support for package on SLE-11 (SP3) + - remove buildrequire to libuse and ncurses 32-bit devel packages + as they are needed by grub-emu which we don't support + - remove buildrequire to freetype2-devel-32bit as it's not need + by grub2-mkfont and others + - buildrequire to xz instead of lzma + - buildrequire to texinfo instead of makeinfo + - remove buildrequire to autogen as it's not available in SLE-11 + - add Makefile.util.am Makefile.core.am generated by autogen + - run autoreconf -vi instead of ./autogen.sh + - For SLE-11 remove buildrequire to gnu-unifont as it's not + yet available. Also do not package pf fonts created from it. + - workaround SLE-11 patch utility not rename file for us + - add -fno-inline-functions-called-once to CFLAGS to fix build + error on gcc 4.3.x + - not require os-prober for SLE-11, as package not yet ready + +------------------------------------------------------------------- +Sat Oct 27 05:27:42 UTC 2012 - arvidjaar@gmail.com + +- grub2-efi now depends on exact grub2 version + +------------------------------------------------------------------- +Fri Oct 25 17:00:35 UTC 2012 - arvidjaar@gmail.com + +- build grub2-efi with standard "grub2" prefix (bnc#782891) + - remove use-grub2-efi-as-a-package-name.patch + - migrate settings from /boot/grub2-efi to /boot/grub2 in efi post + - provide some compatibility links grub2-efi-xxx for perl-Bootloader + - workaround for /boot/grub2-efi linkk and /boot/grub2/grub.cfg + missing on update from older versions + +------------------------------------------------------------------- +Thu Oct 25 05:56:59 UTC 2012 - mchang@suse.com + +- add grub2-fix-build-error-on-flex-2.5.37.patch + +------------------------------------------------------------------- +Thu Oct 18 16:10:02 UTC 2012 - arvidjaar@gmail.com + +- modify patch grub2-iterate-and-hook-for-extended-partition.patch to + ignore extended partitions other then primary (bnc#785341) + +------------------------------------------------------------------- +Wed Sep 26 08:04:48 UTC 2012 - mchang@suse.com + +- refresh grub2-fix-locale-en.mo.gz-not-found-error-message.patch + with the correct fix in upstream bugzilla #35880 by Colin Watson + (bnc#771393) + +------------------------------------------------------------------- +Fri Sep 21 07:37:53 UTC 2012 - mchang@suse.com + +- grub2-fix-locale-en.mo.gz-not-found-error-message.patch (bnc#771393) + +------------------------------------------------------------------- +Wed Sep 19 18:54:34 UTC 2012 - arvidjaar@gmail.com + +- add 20_memtest86+ (bnc#780622) + +------------------------------------------------------------------- +Tue Sep 18 09:26:29 UTC 2012 - mchang@suse.com + +- Fix un-bootable grub2 testing entry in grub's menu.lst (bnc#779370) +- Not add new grub2 testing entry if it's not found in menu.lst +- Update grub2 stuff and config if there's grub2 entry in menu.lst +- Check for current bootloader as update-bootloader acts on it + +------------------------------------------------------------------- +Thu Aug 30 08:00:54 UTC 2012 - mchang@suse.com + +- add grub2-fix-Grub2-with-SUSE-Xen-package-install.patch (bnc#774666) +- add grub2-pass-corret-root-for-nfsroot.patch (bnc#774548) + +------------------------------------------------------------------- +Mon Aug 20 06:27:23 UTC 2012 - mchang@suse.com + +- disable grub2-enable-theme-for-terminal-window.patch to use + default black background due to current background has poor + contrast to the font color (bnc#776244). + +------------------------------------------------------------------- +Fri Aug 10 19:31:40 UTC 2012 - jslaby@suse.de + +- rename grub2once to grub2-once + +------------------------------------------------------------------- +Wed Aug 1 08:01:41 UTC 2012 - mchang@suse.com + +- add grub2once (bnc#771587) +- add not-display-menu-when-boot-once.patch + +------------------------------------------------------------------- +Sat Jul 28 14:17:56 UTC 2012 - aj@suse.de + +- Fix build with missing gets declaration (glibc 2.16) + +------------------------------------------------------------------- +Fri Jul 27 13:22:24 UTC 2012 - tittiatcoke@gmail.com + +- Add grub2-enable-theme-for-terminal-window.patch (bnc#770107) + +------------------------------------------------------------------- +Thu Jul 19 11:03:37 UTC 2012 - mchang@suse.com + +- add grub2-fix-menu-in-xen-host-server.patch (bnc#757895) + +------------------------------------------------------------------- +Wed Jul 18 08:29:53 UTC 2012 - mchang@suse.com + +- add grub2-fix-error-terminal-gfxterm-isn-t-found.patch +- add grub2-fix-mo-not-copied-to-grubdir-locale.patch + +------------------------------------------------------------------- +Wed Jul 18 08:12:19 UTC 2012 - aj@suse.de + +- We only need makeinfo, not texinfo for building. + +------------------------------------------------------------------- +Tue Jul 17 21:12:26 CEST 2012 - jslaby@suse.de + +- fix build by adding texinfo to buildrequires. + +------------------------------------------------------------------- +Fri Jul 6 08:09:16 UTC 2012 - mchang@suse.com + +- grub2-GRUB_CMDLINE_LINUX_RECOVERY-for-recovery-mode.patch. We + don't run in sigle user mode for recovery, instead use different + set kernel command line options which could be specified by this + GRUB_CMDLINE_LINUX_RECOVERY setting. + +------------------------------------------------------------------- +Wed Jul 4 06:20:23 UTC 2012 - mchang@suse.com + +- add use-grub2-efi-as-a-package-name.patch (bnc#769916) + +------------------------------------------------------------------- +Fri Jun 29 10:02:08 UTC 2012 - dvaleev@suse.com + +- Add configuration support for serial terminal consoles. This will + set the maximum screen size so that text is not overwritten. + +------------------------------------------------------------------- +Fri Jun 29 09:51:59 UTC 2012 - dvaleev@suse.com + +- don't enable grub-emu-usb on ppc ppc641 + +------------------------------------------------------------------- +Thu Jun 28 09:33:26 CEST 2012 - jslaby@suse.de + +- update to 2.0 final + * see ChangeLog for changes + +------------------------------------------------------------------- +Mon Jun 25 11:10:27 UTC 2012 - adrian@suse.de + +- enable xz/lzma support for image file generation + +------------------------------------------------------------------- +Sun Jun 24 18:10:27 UTC 2012 - jslaby@suse.de + +- update to 2.0 beta6, a snapshot from today + * see ChangeLog for changes + +------------------------------------------------------------------- +Fri Jun 22 08:50:12 UTC 2012 - mchang@suse.com + +- do not package grub.cfg, as it's generated at runtime and the + presence of it would confuse pygrub (bnc#768063) + +------------------------------------------------------------------- +Wed May 16 06:38:05 UTC 2012 - mchang@suse.com + +- fix build error on 12.1 caused by autogen aborts because of + absence of guile package + +------------------------------------------------------------------- +Wed May 2 03:17:21 UTC 2012 - mchang@suse.com + +- grub2-automake-1-11-2.patch : fix grub2 build error on newer + autotools (automake >= 1.11.2) +- call ./autogen.sh + +------------------------------------------------------------------- +Thu Apr 19 11:28:44 UTC 2012 - mchang@suse.com + +- grub2-probe-disk-mountby.patch : fix grub2-probe fails on + probing mount-by devices under /dev/disk/by-(id|uuid|path). + (bnc#757746) + +------------------------------------------------------------------- +Thu Mar 29 07:08:38 UTC 2012 - mchang@suse.com + +- Add Requires to os-prober as script depends on it for probing + foreign os (bnc#753229) + +------------------------------------------------------------------- +Wed Mar 21 06:58:43 UTC 2012 - mchang@suse.com + +- Mark %config(noreplace) to /etc/default/grub (bnc#753246) + +------------------------------------------------------------------- +Fri Mar 16 09:21:40 UTC 2012 - aj@suse.de + +- Fix build with gcc 4.7 (needs -fno-strict-aliasing for zfs code). + +------------------------------------------------------------------- +Tue Mar 13 04:06:06 UTC 2012 - mchang@suse.com + +- Fix error in installation to extended partition (bnc#750897) + add grub2-iterate-and-hook-for-extended-partition.patch + add grub2-install-opt-skip-fs-probe.patch + +------------------------------------------------------------------- +Mon Mar 12 09:34:40 UTC 2012 - tittiatcoke@gmail.com + +- Added BuildRequires for gnu-unifont in order to create the + necessary fonts for a graphical boot menu. + +------------------------------------------------------------------- +Mon Feb 20 13:04:51 UTC 2012 - andrea.turrini@gmail.com + +- fixed typos in grub2.spec + +------------------------------------------------------------------- +Mon Jan 2 03:16:13 UTC 2012 - mchang@suse.com + +- platforms without efi should not specify exclusion of it + +------------------------------------------------------------------- +Thu Dec 29 02:31:23 UTC 2011 - mchang@suse.com + +- set --target=%{_target_plaform) explicitly to %configure in case + it wouldn't do that for us implicitly +- when making x86_64-efi image not use i386 target build and keep + use of x86_64. otherwise it would have error "invalid ELF header" + +------------------------------------------------------------------- +Fri Dec 2 16:31:14 UTC 2011 - coolo@suse.com + +- add automake as buildrequire to avoid implicit dependency + +------------------------------------------------------------------- +Mon Nov 28 09:40:44 CET 2011 - jslaby@suse.de + +- remove doubly packaged files +- remove INSTALL from docs +- handle duplicate bindir files + +------------------------------------------------------------------- +Mon Oct 31 13:08:21 CET 2011 - meissner@suse.de + +- make efi exclusion more complete + +------------------------------------------------------------------- +Thu Oct 27 08:16:58 UTC 2011 - aj@suse.de + +- efibootmgr only exists on x86-64 and ia64. + +------------------------------------------------------------------- +Tue Oct 25 08:38:19 UTC 2011 - aj@suse.de + +- Add requires from efi subpackage to main package (bnc#72596) + +------------------------------------------------------------------- +Mon Oct 24 13:11:39 CEST 2011 - jslaby@suse.de + +- update it and pl translations +- cleanup spec file + * don't package efi files to non-efi package + +------------------------------------------------------------------- +Thu Aug 25 14:46:04 UTC 2011 - aj@suse.de + +- Fix directory ownership. + +------------------------------------------------------------------- +Tue Aug 23 12:46:43 UTC 2011 - aj@suse.de + +- Build an efi subpackage [bnc#713595]. + +------------------------------------------------------------------- +Tue Aug 2 12:10:39 UTC 2011 - dvaleev@novell.com + +- enable ppc build +- patch unused-but-set-variable + +------------------------------------------------------------------- +Tue Jul 12 14:03:05 UTC 2011 - aj@suse.de + +- Create submenu for all besides primary Linux kernels. +- Only run preun section during package install but not during + upgrade. + +------------------------------------------------------------------- +Tue Jul 12 11:48:08 UTC 2011 - aj@suse.de + +- Update README.openSUSE + +------------------------------------------------------------------- +Tue May 31 10:42:29 CEST 2011 - jslaby@suse.de + +- update translations +- update to 1.99 final + * See NEWS file for changes + +------------------------------------------------------------------- +Sat May 7 12:33:43 CEST 2011 - jslaby@suse.de + +- fix build with gcc 4.6 +- build in parallel (fixed finally in 1.99) +- add translations from translations project +- update to 1.99-rc2 + * See NEWS file for changes + +------------------------------------------------------------------- +Wed Oct 27 16:57:13 CEST 2010 - jslaby@suse.de + +- fix vanishing of /boot/grub2/* if /boot/grub/device.map + doesn't exist + +------------------------------------------------------------------- +Mon Oct 25 12:39:11 UTC 2010 - jslaby@suse.de + +- add missing " in the default file; add "fi" to grub2-linux.patch + +------------------------------------------------------------------- +Mon Oct 11 22:29:27 CEST 2010 - jslaby@suse.de + +- repack gz to bz2 (0.5M saving) + +------------------------------------------------------------------- +Sat Oct 9 19:16:51 UTC 2010 - aj@suse.de + +- Do not output vmlinux if vmlinuz of same version exists. +- Update default grub file. + +------------------------------------------------------------------- +Sat Oct 9 13:58:31 UTC 2010 - aj@suse.de + +- Add patch grub-1.98-follow-dev-mapper-symlinks.patch from Fedora + for grub2-probe to detect lvm devices correctly + +------------------------------------------------------------------- +Sat Sep 11 23:55:54 CEST 2010 - jslaby@suse.de + +- add gettext "requires" + +------------------------------------------------------------------- +Sun Mar 14 12:11:53 UTC 2010 - aj@suse.de + +- Fix build on x86-64. + +------------------------------------------------------------------- +Fri Mar 12 20:39:25 UTC 2010 - aj@suse.de + +- Don't build parallel. +- Update to grub 1.98 including: + * Multiboot on EFI support. + * Saved default menu entry support, with new utilities `grub-reboot' and + `grub-set-default'. + * Encrypted password support, with a new utility `grub-mkpasswd-pbkdf2'. + * `grub-mkfloppy' removed; use `grub-mkrescue' to create floppy images. + +------------------------------------------------------------------- +Fri Feb 12 08:21:10 UTC 2010 - aj@suse.de + +- Update to grub 1.97.2: + * Fix a few 4 GiB limits. + * Fix license problems with a few BSD headers. + * Lots of misc bugfixes. + +------------------------------------------------------------------- +Wed Dec 9 12:37:18 UTC 2009 - aj@suse.de + +- Fix requires. + +------------------------------------------------------------------- +Wed Dec 9 11:45:57 UTC 2009 - aj@suse.de + +- Mark /etc/default/grub as config file. + +------------------------------------------------------------------- +Wed Dec 9 10:45:33 UTC 2009 - aj@suse.de + +- Mark root partition rw + +------------------------------------------------------------------- +Wed Dec 9 09:11:33 UTC 2009 - aj@suse.de + +- New package grub2. + diff --git a/grub2.rpmlintrc b/grub2.rpmlintrc new file mode 100644 index 0000000..23c9652 --- /dev/null +++ b/grub2.rpmlintrc @@ -0,0 +1,14 @@ +addFilter("zero-length /boot/grub2/grub.cfg") +addFilter("non-etc-or-var-file-marked-as-conffile /boot/grub2/grub.cfg") +addFilter("non-conffile-in-etc /etc/bash_completion.d/grub") +addFilter("non-conffile-in-etc /etc/grub.d/README") +addFilter("statically-linked-binary .*/grub2/*/kernel.img") +# We need to supply unstripped files for grub +addFilter("unstripped-binary-or-object .*/grub2/*/.*.mod") +# TODO: s390 Experts: is this sensible?! +addFilter("s390x: W: executable-stack") +# We need to provide compatibility sym-links in noarch package +addFilter("suse-filelist-forbidden-noarch") +addFilter("filelist-forbidden-noarch") +# +addFilter('arch-independent-package-contains-binary-or-object') diff --git a/grub2.spec b/grub2.spec new file mode 100644 index 0000000..809f22c --- /dev/null +++ b/grub2.spec @@ -0,0 +1,1492 @@ +# +# spec file for package grub2 +# +# Copyright (c) 2023 SUSE LLC +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# +# needssslcertforbuild + + +%define _binaries_in_noarch_package_terminate_build 0 + +%if %{defined sbat_distro} +# SBAT metadata +%define sbat_generation 1 +%define sbat_generation_grub 4 +%else +%{error please define sbat_distro, sbat_distro_summary and sbat_distro_url} +%endif + +Name: grub2 +BuildRequires: automake +BuildRequires: bison +BuildRequires: device-mapper-devel +BuildRequires: fdupes +BuildRequires: flex +BuildRequires: freetype2-devel +BuildRequires: fuse-devel +BuildRequires: gcc +BuildRequires: glibc-devel +%if 0%{?suse_version} >= 1140 +BuildRequires: dejavu-fonts +BuildRequires: gnu-unifont +%endif +BuildRequires: help2man +BuildRequires: libtasn1-devel +BuildRequires: xz +%if 0%{?suse_version} >= 1210 +BuildRequires: makeinfo +%else +BuildRequires: texinfo +%endif +%if %{defined pythons} +BuildRequires: %{pythons} +%else +BuildRequires: python +%endif +BuildRequires: xz-devel +%ifarch x86_64 aarch64 ppc ppc64 ppc64le +BuildRequires: openssl >= 0.9.8 +BuildRequires: pesign-obs-integration +%endif +%if 0%{?suse_version} >= 1210 +# Package systemd services files grub2-once.service +BuildRequires: systemd-rpm-macros +%define has_systemd 1 +%endif +%if 0%{?suse_version} > 1320 +BuildRequires: update-bootloader-rpm-macros +%endif + +# Modules code is dynamically loaded and collected from a _fixed_ path. +%define _libdir %{_exec_prefix}/lib + +# Build grub2-emu everywhere (it may be "required" by 'grub2-once') +%define emu 1 + +%ifarch ppc ppc64 ppc64le +%define grubcpu powerpc +%define platform ieee1275 +%define brp_pesign_reservation 65536 +# emu does not build here yet... :-( +%define emu 0 +%endif + +%ifarch %{ix86} x86_64 +%define grubcpu i386 +%define platform pc +%endif + +%ifarch s390x +%define grubcpu s390x +%define platform emu +%endif + +%ifarch %{arm} +%define grubcpu arm +%define platform uboot +%endif + +%ifarch aarch64 +%define grubcpu arm64 +%define platform efi +%define only_efi 1 +%endif + +%ifarch riscv64 +%define grubcpu riscv64 +%define platform efi +%define only_efi 1 +%endif + +%define grubarch %{grubcpu}-%{platform} + +# build efi bootloader on some platforms only: +%if ! 0%{?efi:1} +%global efi %{ix86} x86_64 ia64 aarch64 %{arm} riscv64 +%endif + +%ifarch %{efi} +%ifarch %{ix86} +%define grubefiarch i386-efi +%else +%ifarch aarch64 +%define grubefiarch arm64-efi +%else +%ifarch %{arm} +%define grubefiarch arm-efi +%else +%define grubefiarch %{_target_cpu}-efi +%endif +%endif +%endif +%endif + +%ifarch %{ix86} +%define grubxenarch i386-xen +%endif + +%ifarch x86_64 +%define grubxenarch x86_64-xen +%endif + +%if "%{platform}" == "emu" +# force %%{emu} to 1, e.g. for s390 +%define emu 1 +%endif + +%if 0%{?suse_version} == 1110 +%define only_efi %{nil} +%define only_x86_64 %{nil} +%endif + +%ifarch %{efi} +# The branding package requires grub2. It's not necessary here, +# so break the dep to avoid a cycle. +#!BuildIgnore: grub2 +BuildRequires: grub2-branding +BuildRequires: squashfs +%endif + +# For ALP and Tumbleweed +%if 0%{?suse_version} >= 1600 +# Only include the macros for the architectures with the newer UEFI and TCG protocol +%ifarch x86_64 aarch64 riscv64 +BuildRequires: fde-tpm-helper-rpm-macros +%endif +%endif + +Version: 2.12~rc1 +Release: 0 +Summary: Bootloader with support for Linux, Multiboot and more +License: GPL-3.0-or-later +Group: System/Boot +URL: http://www.gnu.org/software/grub/ +Source0: https://alpha.gnu.org/gnu/grub/grub-%{version}.tar.xz +Source1: 90_persistent +Source2: grub.default +Source4: grub2.rpmlintrc +Source6: grub2-once +Source7: 20_memtest86+ +Source8: README.ibm3215 +Source10: openSUSE-UEFI-CA-Certificate.crt +Source11: SLES-UEFI-CA-Certificate.crt +Source12: grub2-snapper-plugin.sh +Source14: 80_suse_btrfs_snapshot +Source15: grub2-once.service +Source16: grub2-xen-pv-firmware.cfg +# required hook for systemd-sleep (bsc#941758) +Source17: grub2-systemd-sleep.sh +Source18: grub2-check-default.sh +Source19: grub2-instdev-fixup.pl +Source1000: PATCH_POLICY +Patch1: rename-grub-info-file-to-grub2.patch +Patch2: grub2-linux.patch +Patch3: use-grub2-as-a-package-name.patch +Patch4: info-dir-entry.patch +Patch5: grub2-simplefb.patch +Patch6: grub2-iterate-and-hook-for-extended-partition.patch +Patch7: grub2-ppc-terminfo.patch +Patch8: grub2-fix-error-terminal-gfxterm-isn-t-found.patch +Patch9: grub2-fix-menu-in-xen-host-server.patch +Patch10: not-display-menu-when-boot-once.patch +Patch11: grub2-pass-corret-root-for-nfsroot.patch +Patch12: grub2-efi-HP-workaround.patch +Patch13: grub2-secureboot-add-linuxefi.patch +Patch14: grub2-secureboot-no-insmod-on-sb.patch +Patch15: grub2-secureboot-chainloader.patch +Patch16: grub2-linuxefi-fix-boot-params.patch +Patch17: grub2-linguas.sh-no-rsync.patch +Patch18: grub2-use-Unifont-for-starfield-theme-terminal.patch +Patch19: grub2-s390x-01-Changes-made-and-files-added-in-order-to-allow-s390x.patch +Patch20: grub2-s390x-03-output-7-bit-ascii.patch +Patch21: grub2-s390x-04-grub2-install.patch +Patch22: grub2-s390x-05-grub2-mkconfig.patch +Patch23: grub2-use-rpmsort-for-version-sorting.patch +Patch24: grub2-getroot-treat-mdadm-ddf-as-simple-device.patch +Patch25: grub2-setup-try-fs-embed-if-mbr-gap-too-small.patch +Patch26: grub2-xen-linux16.patch +Patch27: grub2-efi-disable-video-cirrus-and-bochus.patch +Patch28: grub2-vbe-blacklist-preferred-1440x900x32.patch +Patch29: grub2-grubenv-in-btrfs-header.patch +Patch30: grub2-mkconfig-aarch64.patch +Patch31: grub2-default-distributor.patch +Patch32: grub2-menu-unrestricted.patch +Patch33: grub2-mkconfig-arm.patch +Patch34: grub2-s390x-06-loadparm.patch +Patch35: grub2-s390x-07-add-image-param-for-zipl-setup.patch +Patch36: grub2-s390x-08-workaround-part-to-disk.patch +Patch37: grub2-commands-introduce-read_file-subcommand.patch +Patch38: grub2-efi-chainload-harder.patch +Patch39: grub2-emu-4-all.patch +Patch40: grub2-lvm-allocate-metadata-buffer-from-raw-contents.patch +Patch41: grub2-diskfilter-support-pv-without-metadatacopies.patch +Patch42: grub2-s390x-09-improve-zipl-setup.patch +Patch43: grub2-getroot-scan-disk-pv.patch +Patch44: grub2-util-30_os-prober-multiple-initrd.patch +Patch45: grub2-getroot-support-nvdimm.patch +Patch46: grub2-install-fix-not-a-directory-error.patch +Patch47: grub-install-force-journal-draining-to-ensure-data-i.patch +Patch48: grub2-s390x-skip-zfcpdump-image.patch +Patch49: grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch +Patch50: grub2-btrfs-02-export-subvolume-envvars.patch +Patch51: grub2-btrfs-03-follow_default.patch +Patch52: grub2-btrfs-04-grub2-install.patch +Patch53: grub2-btrfs-05-grub2-mkconfig.patch +Patch54: grub2-btrfs-06-subvol-mount.patch +Patch55: grub2-btrfs-07-subvol-fallback.patch +Patch56: grub2-btrfs-08-workaround-snapshot-menu-default-entry.patch +Patch57: grub2-btrfs-09-get-default-subvolume.patch +Patch58: grub2-btrfs-10-config-directory.patch +Patch59: grub2-efi-xen-chainload.patch +Patch60: grub2-efi-xen-cmdline.patch +Patch61: grub2-efi-xen-cfg-unquote.patch +Patch62: grub2-efi-xen-removable.patch +Patch63: grub2-Add-hidden-menu-entries.patch +Patch64: grub2-SUSE-Add-the-t-hotkey.patch +Patch65: grub2-zipl-setup-fix-btrfs-multipledev.patch +Patch66: grub2-suse-remove-linux-root-param.patch +Patch67: grub2-ppc64le-disable-video.patch +Patch68: grub2-ppc64le-memory-map.patch +Patch69: grub2-ppc64-cas-reboot-support.patch +Patch70: grub2-install-remove-useless-check-PReP-partition-is-empty.patch +Patch71: grub2-ppc64-cas-new-scope.patch +Patch72: grub2-ppc64-cas-fix-double-free.patch +Patch73: grub2-efi_gop-avoid-low-resolution.patch +Patch74: 0003-bootp-New-net_bootp6-command.patch +Patch75: 0004-efinet-UEFI-IPv6-PXE-support.patch +Patch76: 0005-grub.texi-Add-net_bootp6-doument.patch +Patch77: 0006-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch +Patch78: 0007-efinet-Setting-network-from-UEFI-device-path.patch +Patch79: 0008-efinet-Setting-DNS-server-from-UEFI-protocol.patch +Patch80: 0012-tpm-Build-tpm-as-module.patch +Patch81: 0001-add-support-for-UEFI-network-protocols.patch +Patch82: 0002-AUDIT-0-http-boot-tracker-bug.patch +Patch83: grub2-mkconfig-default-entry-correction.patch +Patch84: grub2-s390x-11-secureboot.patch +Patch85: grub2-s390x-12-zipl-setup-usrmerge.patch +Patch86: grub2-secureboot-install-signed-grub.patch +Patch87: grub2-btrfs-help-on-snapper-rollback.patch +Patch88: grub2-video-limit-the-resolution-for-fixed-bimap-font.patch +Patch89: grub2-gfxmenu-support-scrolling-menu-entry-s-text.patch +Patch90: 0001-kern-mm.c-Make-grub_calloc-inline.patch +Patch91: 0002-cmdline-Provide-cmdline-functions-as-module.patch +Patch92: 0001-ieee1275-powerpc-implements-fibre-channel-discovery-.patch +Patch93: 0002-ieee1275-powerpc-enables-device-mapper-discovery.patch +Patch94: 0001-Unify-the-check-to-enable-btrfs-relative-path.patch +Patch95: 0001-efi-linux-provide-linux-command.patch +Patch96: 0001-Add-support-for-Linux-EFI-stub-loading-on-aarch64.patch +Patch97: 0002-arm64-make-sure-fdt-has-address-cells-and-size-cells.patch +Patch98: 0003-Make-grub_error-more-verbose.patch +Patch99: 0004-arm-arm64-loader-Better-memory-allocation-and-error-.patch +Patch100: 0006-efi-Set-image-base-address-before-jumping-to-the-PE-.patch +Patch101: 0044-squash-kern-Add-lockdown-support.patch +Patch102: 0001-ieee1275-Avoiding-many-unecessary-open-close.patch +Patch103: 0001-Workaround-volatile-efi-boot-variable.patch +Patch104: 0001-templates-Follow-the-path-of-usr-merged-kernel-confi.patch +Patch105: 0001-ieee1275-implement-FCP-methods-for-WWPN-and-LUNs.patch +Patch106: 0001-arm64-Fix-EFI-loader-kernel-image-allocation.patch +Patch107: 0002-Arm-check-for-the-PE-magic-for-the-compiled-arch.patch +Patch108: 0001-Factor-out-grub_efi_linux_boot.patch +Patch109: 0002-Fix-race-in-EFI-validation.patch +Patch110: 0003-Handle-multi-arch-64-on-32-boot-in-linuxefi-loader.patch +Patch111: 0004-Try-to-pick-better-locations-for-kernel-and-initrd.patch +Patch112: 0005-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch +Patch113: 0006-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch +Patch114: 0007-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch +Patch115: 0008-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch +Patch116: 0009-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch +Patch117: 0010-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch +Patch118: 0011-Also-define-GRUB_EFI_MAX_ALLOCATION_ADDRESS-for-RISC.patch +Patch119: 0004-Add-suport-for-signing-grub-with-an-appended-signatu.patch +Patch120: 0005-docs-grub-Document-signing-grub-under-UEFI.patch +Patch121: 0006-docs-grub-Document-signing-grub-with-an-appended-sig.patch +Patch122: 0007-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch +Patch123: 0008-pgp-factor-out-rsa_pad.patch +Patch124: 0009-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch +Patch125: 0010-posix_wrap-tweaks-in-preparation-for-libtasn1.patch +Patch126: 0011-libtasn1-import-libtasn1-4.18.0.patch +Patch127: 0012-libtasn1-disable-code-not-needed-in-grub.patch +Patch128: 0013-libtasn1-changes-for-grub-compatibility.patch +Patch129: 0014-libtasn1-compile-into-asn1-module.patch +Patch130: 0015-test_asn1-test-module-for-libtasn1.patch +Patch131: 0016-grub-install-support-embedding-x509-certificates.patch +Patch132: 0017-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch +Patch133: 0018-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch +Patch134: 0019-appended-signatures-support-verifying-appended-signa.patch +Patch135: 0020-appended-signatures-verification-tests.patch +Patch136: 0021-appended-signatures-documentation.patch +Patch137: 0022-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch +Patch138: 0023-x509-allow-Digitial-Signature-plus-other-Key-Usages.patch +Patch139: 0001-grub-install-Add-SUSE-signed-image-support-for-power.patch +Patch140: 0001-Add-grub_envblk_buf-helper-function.patch +Patch141: 0002-Add-grub_disk_write_tail-helper-function.patch +Patch142: 0003-grub-install-support-prep-environment-block.patch +Patch143: 0004-Introduce-prep_load_env-command.patch +Patch144: 0005-export-environment-at-start-up.patch +Patch145: 0001-grub-install-bailout-root-device-probing.patch +Patch146: 0001-install-fix-software-raid1-on-esp.patch +Patch147: 0001-grub-probe-Deduplicate-probed-partmap-output.patch +Patch148: 0001-Fix-infinite-boot-loop-on-headless-system-in-qemu.patch +Patch149: 0001-ofdisk-improve-boot-time-by-lookup-boot-disk-first.patch +Patch150: 0001-protectors-Add-key-protectors-framework.patch +Patch151: 0002-tpm2-Add-TPM-Software-Stack-TSS.patch +Patch152: 0003-protectors-Add-TPM2-Key-Protector.patch +Patch153: 0004-cryptodisk-Support-key-protectors.patch +Patch154: 0005-util-grub-protect-Add-new-tool.patch +Patch155: 0008-linuxefi-Use-common-grub_initrd_load.patch +Patch156: 0009-Add-crypttab_entry-to-obviate-the-need-to-input-pass.patch +Patch157: 0010-templates-import-etc-crypttab-to-grub.cfg.patch +Patch158: grub-read-pcr.patch +Patch159: tpm-record-pcrs.patch +Patch160: grub-install-record-pcrs.patch +Patch161: safe_tpm_pcr_snapshot.patch +Patch162: 0001-ieee1275-add-support-for-NVMeoFC.patch +Patch163: 0002-ieee1275-ofpath-enable-NVMeoF-logical-device-transla.patch +Patch164: 0003-ieee1275-change-the-logic-of-ieee1275_get_devargs.patch +Patch165: 0004-ofpath-controller-name-update.patch +Patch166: 0002-Mark-environmet-blocks-as-used-for-image-embedding.patch +Patch167: grub2-increase-crypttab-path-buffer.patch +Patch168: 0001-grub2-Set-multiple-device-path-for-a-nvmf-boot-devic.patch +Patch169: 0001-grub2-Can-t-setup-a-default-boot-device-correctly-on.patch +Patch170: 0001-tpm2-Add-TPM2-types-structures-and-command-constants.patch +Patch171: 0002-tpm2-Add-more-marshal-unmarshal-functions.patch +Patch172: 0003-tpm2-Implement-more-TPM2-commands.patch +Patch173: 0004-tpm2-Support-authorized-policy.patch +Patch174: 0001-clean-up-crypttab-and-linux-modules-dependency.patch +Patch175: 0002-discard-cached-key-before-entering-grub-shell-and-ed.patch +Patch176: 0001-ieee1275-ofdisk-retry-on-open-and-read-failure.patch +Patch177: 0002-Restrict-cryptsetup-key-file-permission-for-better-s.patch +Patch178: 0001-openfw-Ensure-get_devargs-and-get_devname-functions-.patch +Patch179: 0002-prep_loadenv-Fix-regex-for-Open-Firmware-device-spec.patch +Patch180: 0001-xen_boot-add-missing-grub_arch_efi_linux_load_image_.patch +Patch181: 0001-font-Try-memdisk-fonts-with-the-same-name.patch +Patch182: 0001-Make-grub.cfg-compatible-to-old-binaries.patch +Patch183: 0001-disk-cryptodisk-Fix-missing-change-when-updating-to-.patch +Patch184: grub2-change-bash-completion-dir.patch +Patch185: 0001-protectors-Implement-NV-index.patch +Patch186: 0002-cryptodisk-Fallback-to-passphrase.patch +Patch187: 0003-cryptodisk-wipe-out-the-cached-keys-from-protectors.patch +Patch188: 0004-diskfilter-look-up-cryptodisk-devices-first.patch +Patch189: grub2-mkconfig-riscv64.patch +Patch190: arm64-Use-proper-memory-type-for-kernel-allocation.patch +Patch191: 0001-fs-btrfs-Zero-file-data-not-backed-by-extents.patch +Patch192: 0001-fs-ntfs-Fix-an-OOB-write-when-parsing-the-ATTRIBUTE_.patch +Patch193: 0002-fs-ntfs-Fix-an-OOB-read-when-reading-data-from-the-r.patch +Patch194: 0003-fs-ntfs-Fix-an-OOB-read-when-parsing-directory-entri.patch +Patch195: 0004-fs-ntfs-Fix-an-OOB-read-when-parsing-bitmaps-for-ind.patch +Patch196: 0005-fs-ntfs-Fix-an-OOB-read-when-parsing-a-volume-label.patch +Patch197: 0006-fs-ntfs-Make-code-more-readable.patch +Patch198: 0001-luks2-Use-grub-tpm2-token-for-TPM2-protected-volume-.patch +Patch199: Fix-the-size-calculation-for-the-synthesized-initrd.patch +Patch200: 0001-kern-ieee1275-init-Restrict-high-memory-in-presence-.patch +Patch201: 0001-fs-xfs-Incorrect-short-form-directory-data-boundary-.patch +Patch202: 0002-fs-xfs-Fix-XFS-directory-extent-parsing.patch +Patch203: 0003-fs-xfs-add-large-extent-counters-incompat-feature-su.patch +Patch204: 0001-Improve-TPM-key-protection-on-boot-interruptions.patch +Patch205: 0002-Restrict-file-access-on-cryptodisk-print.patch +Patch206: 0003-Restrict-ls-and-auto-file-completion-on-cryptodisk-p.patch +Patch207: 0004-Key-revocation-on-out-of-bound-file-access.patch +Patch208: 0001-mkstandalone-ensure-stable-timestamps-for-generated-.patch +Patch209: 0002-mkstandalone-ensure-deterministic-tar-file-creation-.patch + +Requires: gettext-runtime +%if 0%{?suse_version} >= 1140 +%ifnarch s390x +Recommends: os-prober +%endif +# xorriso not available using grub2-mkrescue (bnc#812681) +# downgrade to suggest as minimal system can't afford pulling in tcl/tk and half of the x11 stack (bsc#1102515) +Suggests: libburnia-tools +Suggests: mtools +%endif +%if ! 0%{?only_efi:1} +Requires: grub2-%{grubarch} = %{version}-%{release} +%endif +%ifarch s390x +# required utilities by grub2-s390x-04-grub2-install.patch +# use 'showconsole' to determine console device. (bnc#876743) +Requires: kexec-tools +Requires: (/sbin/showconsole or /usr/sbin/showconsole) +# for /sbin/zipl used by grub2-zipl-setup +Requires: s390-tools +%endif +%ifarch ppc64 ppc64le +Requires: powerpc-utils +%endif +%ifarch %{ix86} +# meanwhile, memtest is available as EFI executable +Recommends: memtest86+ +%endif + +%if 0%{?only_x86_64:1} +ExclusiveArch: x86_64 +%else +ExclusiveArch: %{ix86} x86_64 ppc ppc64 ppc64le s390x aarch64 %{arm} riscv64 +%endif + +%description +This is the second version of the GRUB (Grand Unified Bootloader), a +highly configurable and customizable bootloader with modular +architecture. It support rich scale of kernel formats, file systems, +computer architectures and hardware devices. + +This package includes user space utlities to manage GRUB on your system. + +%package branding-upstream + +Summary: Upstream branding for GRUB2's graphical console +Group: System/Fhs +Requires: %{name} = %{version} + +%description branding-upstream +Upstream branding for GRUB2's graphical console + +%if ! 0%{?only_efi:1} +%package %{grubarch} + +Summary: Bootloader with support for Linux, Multiboot and more +Group: System/Boot +%if "%{platform}" != "emu" +BuildArch: noarch +%endif +Requires: %{name} = %{version} +Requires(post): %{name} = %{version} +%if 0%{?update_bootloader_requires:1} +%update_bootloader_requires +%else +Requires: perl-Bootloader +Requires(post): perl-Bootloader +%endif + +%description %{grubarch} +The GRand Unified Bootloader (GRUB) is a highly configurable and customizable +bootloader with modular architecture. It supports rich variety of kernel formats, +file systems, computer architectures and hardware devices. This subpackage +provides support for %{platform} systems. + +%package %{grubarch}-extras +Summary: Unsupported modules for %{grubarch} +Group: System/Boot +BuildArch: noarch +Requires: %{name}-%{grubarch} = %{version} +Provides: %{name}-%{grubarch}:%{_datadir}/%{name}/%{grubarch}/zfs.mod +Provides: %{name}-%{grubarch}:%{_datadir}/%{name}/%{grubarch}/zfscrypt.mod +Provides: %{name}-%{grubarch}:%{_datadir}/%{name}/%{grubarch}/zfsinfo.mod + +%description %{grubarch}-extras +Unsupported modules for %{name}-%{grubarch} + +%package %{grubarch}-debug +Summary: Debug symbols for %{grubarch} +Group: System/Boot +%if "%{platform}" != "emu" +BuildArch: noarch +%endif +Requires: %{name}-%{grubarch} = %{version} + +%description %{grubarch}-debug +Debug information for %{name}-%{grubarch} + +Information on how to debug grub can be found online: +https://www.cnblogs.com/coryxie/archive/2013/03/12/2956807.html + +%endif + +%ifarch %{efi} + +%package %{grubefiarch} + +Summary: Bootloader with support for Linux, Multiboot and more +Group: System/Boot +BuildArch: noarch +# Require efibootmgr +# Without it grub-install is broken so break the package as well if unavailable +Requires: efibootmgr +Requires(post): efibootmgr +Requires: %{name} = %{version} +Requires(post): %{name} = %{version} +%if 0%{?update_bootloader_requires:1} +%update_bootloader_requires +%else +Requires: perl-Bootloader >= 0.706 +Requires(post): perl-Bootloader >= 0.706 +%endif +%{?fde_tpm_update_requires} +Provides: %{name}-efi = %{version}-%{release} +Obsoletes: %{name}-efi < %{version}-%{release} + +%description %{grubefiarch} +The GRand Unified Bootloader (GRUB) is a highly configurable and customizable +bootloader with modular architecture. It supports rich variety of kernel formats, +file systems, computer architectures and hardware devices. This subpackage +provides support for EFI systems. + +%package %{grubefiarch}-extras + +Summary: Unsupported modules for %{grubefiarch} +Group: System/Boot +BuildArch: noarch +Requires: %{name}-%{grubefiarch} = %{version} +Provides: %{name}-%{grubefiarch}:%{_datadir}/%{name}/%{grubefiarch}/zfs.mod +Provides: %{name}-%{grubefiarch}:%{_datadir}/%{name}/%{grubefiarch}/zfscrypt.mod +Provides: %{name}-%{grubefiarch}:%{_datadir}/%{name}/%{grubefiarch}/zfsinfo.mod + +%description %{grubefiarch}-extras +Unsupported modules for %{name}-%{grubefiarch} + +%package %{grubefiarch}-debug +Summary: Debug symbols for %{grubefiarch} +Group: System/Boot +%if "%{platform}" != "emu" +BuildArch: noarch +%endif +Requires: %{name}-%{grubefiarch} = %{version} + +%description %{grubefiarch}-debug +Debug symbols for %{name}-%{grubefiarch} + +Information on how to debug grub can be found online: +https://www.cnblogs.com/coryxie/archive/2013/03/12/2956807.html + +%endif + +%ifarch %{ix86} x86_64 + +%package %{grubxenarch} + +Summary: Bootloader with support for Linux, Multiboot and more +Group: System/Boot +Provides: %{name}-xen = %{version}-%{release} +Obsoletes: %{name}-xen < %{version}-%{release} +BuildArch: noarch + +%description %{grubxenarch} +The GRand Unified Bootloader (GRUB) is a highly configurable and customizable +bootloader with modular architecture. It supports rich variety of kernel formats, +file systems, computer architectures and hardware devices. This subpackage +provides support for XEN systems. + +%package %{grubxenarch}-extras +Summary: Unsupported modules for %{grubxenarch} +Group: System/Boot +BuildArch: noarch +Requires: %{name}-%{grubxenarch} = %{version} +Provides: %{name}-%{grubxenarch}:%{_datadir}/%{name}/%{grubxenarch}/zfs.mod +Provides: %{name}-%{grubxenarch}:%{_datadir}/%{name}/%{grubxenarch}/zfscrypt.mod +Provides: %{name}-%{grubxenarch}:%{_datadir}/%{name}/%{grubxenarch}/zfsinfo.mod + +%description %{grubxenarch}-extras +Unsupported modules for %{name}-%{grubxenarch} + +%endif + +%package snapper-plugin + +Summary: Grub2's snapper plugin +Group: System/Fhs +Requires: %{name} = %{version} +Requires: libxml2-tools +Supplements: packageand(snapper:grub2) +BuildArch: noarch + +%description snapper-plugin +Grub2's snapper plugin for advanced btrfs snapshot boot menu management + +%if 0%{?has_systemd:1} +%package systemd-sleep-plugin + +Summary: Grub2's systemd-sleep plugin +Group: System/Fhs +Requires: grub2 +Requires: util-linux +Supplements: packageand(systemd:grub2) +BuildArch: noarch + +%description systemd-sleep-plugin +Grub2's systemd-sleep plugin for directly booting hibernated kernel image in +swap partition while in resuming +%endif + +%prep +# We create (if we build for efi) two copies of the sources in the Builddir +%autosetup -p1 -n grub-%{version} + +%build +# collect evidence to debug spurious build failure on SLE15 +ulimit -a +# patches above may update the timestamp of grub.texi +# and via build-aux/mdate-sh they end up in grub2.info, breaking build-compare +[ -z "$SOURCE_DATE_EPOCH" ] ||\ + [ `stat -c %Y docs/grub.texi` -lt $SOURCE_DATE_EPOCH ] ||\ + touch -d@$SOURCE_DATE_EPOCH docs/grub.texi + +# This simplifies patch handling without need to use git to create patch +# that renames file +mv docs/grub.texi docs/grub2.texi + +cp %{SOURCE8} . +mkdir build +%ifarch %{efi} +mkdir build-efi +%endif +%ifarch %{ix86} x86_64 +mkdir build-xen +%endif +%if %{emu} +mkdir build-emu +%endif + +export PYTHON=%{_bindir}/python3 +[ -x $PYTHON ] || unset PYTHON # try 'python', if 'python3' is unavailable +# autogen calls autoreconf -vi +./autogen.sh +# Not yet: +%define common_conf_options TARGET_LDFLAGS=-static --program-transform-name=s,grub,%{name}, +# This does NOT work on SLE11: +%define _configure ../configure + +# We don't want to let rpm override *FLAGS with default a.k.a bogus values. +CFLAGS="-fno-strict-aliasing -fno-inline-functions-called-once " +CXXFLAGS=" " +FFLAGS=" " +export CFLAGS CXXFLAGS FFLAGS + +%if %{emu} +cd build-emu +%define arch_specific --enable-device-mapper --disable-grub-mount +TLFLAGS="-fPIC" + +# -static is needed so that autoconf script is able to link +# test that looks for _start symbol on 64 bit platforms +../configure TARGET_LDFLAGS=$TLFLAGS \ + --prefix=%{_prefix} \ + --libdir=%{_datadir} \ + --sysconfdir=%{_sysconfdir} \ + --target=%{_target_platform} \ + --with-platform=emu \ + %{arch_specific} \ + --program-transform-name=s,grub,%{name}, +make %{?_smp_mflags} +cd .. +if [ "%{platform}" = "emu" ]; then + rmdir build + mv build-emu build +fi +%endif + +%ifarch %{ix86} x86_64 +cd build-xen +../configure \ + TARGET_LDFLAGS=-static \ + --prefix=%{_prefix} \ + --libdir=%{_datadir} \ + --sysconfdir=%{_sysconfdir} \ + --target=%{_target_platform} \ + --with-platform=xen \ + --program-transform-name=s,grub,%{name}, +make %{?_smp_mflags} + +./grub-mkstandalone --grub-mkimage=./grub-mkimage -o grub.xen -O %{grubxenarch} -d grub-core/ "/boot/grub/grub.cfg=%{SOURCE16}" + +cd .. +%endif + +FS_MODULES="btrfs ext2 xfs jfs reiserfs" +CD_MODULES="all_video boot cat configfile echo true \ + font gfxmenu gfxterm gzio halt iso9660 \ + jpeg minicmd normal part_apple part_msdos part_gpt \ + password password_pbkdf2 png reboot search search_fs_uuid \ + search_fs_file search_label sleep test video fat loadenv loopback" +PXE_MODULES="tftp http" +CRYPTO_MODULES="luks luks2 gcry_rijndael gcry_sha1 gcry_sha256 gcry_sha512 crypttab" +%ifarch %{efi} +CD_MODULES="${CD_MODULES} chain efifwsetup efinet read tpm tpm2 memdisk tar squash4 xzio" +PXE_MODULES="${PXE_MODULES} efinet" +%else +CD_MODULES="${CD_MODULES} net" +PXE_MODULES="${PXE_MODULES} net" +%endif + +%ifarch x86_64 +CD_MODULES="${CD_MODULES} linuxefi" +%else +CD_MODULES="${CD_MODULES} linux" +%endif + +GRUB_MODULES="${CD_MODULES} ${FS_MODULES} ${PXE_MODULES} ${CRYPTO_MODULES} mdraid09 mdraid1x lvm serial" +%ifarch ppc ppc64 ppc64le +GRUB_MODULES="${GRUB_MODULES} appendedsig memdisk tar regexp prep_loadenv tpm" +%endif + +%ifarch %{efi} +cd build-efi +../configure \ + TARGET_LDFLAGS=-static \ + --prefix=%{_prefix} \ + --libdir=%{_datadir} \ + --sysconfdir=%{_sysconfdir} \ + --target=%{_target_platform} \ + --with-platform=efi \ + --program-transform-name=s,grub,%{name}, +make %{?_smp_mflags} + +%if 0%{?sbat_generation} +echo "sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md" > sbat.csv +echo "grub,%{sbat_generation_grub},Free Software Foundation,grub,%{version},https://www.gnu.org/software/grub/" >> sbat.csv +echo "grub.%{sbat_distro},%{sbat_generation},%{sbat_distro_summary},%{name},%{version},%{sbat_distro_url}" >> sbat.csv +%endif + +mkdir -p ./fonts +cp %{_datadir}/%{name}/themes/*/*.pf2 ./fonts +cp ./unicode.pf2 ./fonts +%if 0%{?suse_version} > 1500 +tar -cf - ./fonts | mksquashfs - memdisk.sqsh -tar -comp xz -quiet -no-progress +%else +mksquashfs ./fonts memdisk.sqsh -keep-as-directory -comp xz -quiet -no-progress +%endif + +./grub-mkimage -O %{grubefiarch} -o grub.efi --memdisk=./memdisk.sqsh --prefix= %{?sbat_generation:--sbat sbat.csv} \ + -d grub-core ${GRUB_MODULES} + +%ifarch x86_64 aarch64 +if test -e %{_sourcedir}/_projectcert.crt ; then + prjsubject=$(openssl x509 -in %{_sourcedir}/_projectcert.crt -noout -subject_hash) + prjissuer=$(openssl x509 -in %{_sourcedir}/_projectcert.crt -noout -issuer_hash) + opensusesubject=$(openssl x509 -in %{SOURCE10} -noout -subject_hash) + slessubject=$(openssl x509 -in %{SOURCE11} -noout -subject_hash) + if test "$prjissuer" = "$opensusesubject" ; then + cert=%{SOURCE10} + fi + if test "$prjissuer" = "$slessubject" ; then + cert=%{SOURCE11} + fi + if test "$prjsubject" = "$prjissuer" ; then + cert=%{_sourcedir}/_projectcert.crt + fi +fi +if test -z "$cert" ; then + echo "cannot identify project, assuming openSUSE signing" + cert=%{SOURCE10} +fi + +openssl x509 -in $cert -outform DER -out grub.der +%endif + +cd .. +%endif + +%if ! 0%{?only_efi:1} +cd build + +# 64-bit x86-64 machines use 32-bit boot loader +# (We cannot just redefine _target_cpu, as we'd get i386.rpm packages then) +%ifarch x86_64 +%define _target_platform i386-%{_vendor}-%{_target_os}%{?_gnu} +%endif + +%if "%{platform}" != "emu" +%define arch_specific --enable-device-mapper +TLFLAGS="-static" + +# -static is needed so that autoconf script is able to link +# test that looks for _start symbol on 64 bit platforms +../configure TARGET_LDFLAGS="$TLFLAGS" \ + --prefix=%{_prefix} \ + --libdir=%{_datadir} \ + --sysconfdir=%{_sysconfdir} \ + --target=%{_target_platform} \ + --with-platform=%{platform} \ + %{arch_specific} \ + --program-transform-name=s,grub,%{name}, +make %{?_smp_mflags} + +if [ "%{platform}" = "ieee1275" ]; then + # So far neither OpenFirmware nor grub support CA chain, only certificate pinning + # Use project certificate always in the shipped informational file and + # for kernel verification + projectcert="%{_sourcedir}/_projectcert.crt" + openssl x509 -in "$projectcert" -outform DER -out grub.der + cat > %{platform}-config <<'EOF' +set root=memdisk +set prefix=($root)/ +echo "earlycfg: root=$root prefix=$prefix" +EOF + cat > ./grub.cfg <<'EOF' + +regexp --set 1:bdev --set 2:bpath '\((.*)\)(.*)' "$cmdpath" +regexp --set 1:bdev --set 2:bpart '(.*[^\])(,.*)' "$bdev" + +echo "bdev=$bdev" +echo "bpart=$bpart" +echo "bpath=$bpath" + +if [ -z "$ENV_FS_UUID" ]; then + echo "Reading vars from ($bdev)" + prep_load_env "($bdev)" +fi + +echo "ENV_HINT=$ENV_HINT" +echo "ENV_GRUB_DIR=$ENV_GRUB_DIR" +echo "ENV_FS_UUID=$ENV_FS_UUID" +echo "ENV_CRYPTO_UUID=$ENV_CRYPTO_UUID" + +if [ "$btrfs_relative_path" = xy ]; then + btrfs_relative_path=1 +fi + +if [ "$bdev" -a "$bpart" -a "$bpath" ]; then + set hints="--hint $bdev$bpart" + set cfg_dir="$bpath" +elif [ "$bdev" -a "$bpart" ]; then + set hints="--hint $bdev$bpart" + set cfg_dir="/boot/grub2 /grub2" + set btrfs_relative_path=1 +elif [ "$bdev" ]; then + if [ "$ENV_HINT" ]; then + set hints="--hint $ENV_HINT" + else + set hints="--hint ${bdev}," + fi + if [ "$ENV_GRUB_DIR" ]; then + set cfg_dir="$ENV_GRUB_DIR" + else + set cfg_dir="/boot/grub2 /grub2" + set btrfs_relative_path=1 + fi +else + set hints="" + set cfg_dir="/boot/grub2 /grub2" + set btrfs_relative_path=1 +fi + +set prefix="" +set root="" +set cfg="grub.cfg" + +for uuid in $ENV_CRYPTO_UUID; do + cryptomount -u $uuid +done + +if [ "$ENV_FS_UUID" ]; then + echo "searching for $ENV_FS_UUID with $hints" + if search --fs-uuid --set=root "$ENV_FS_UUID" $hints; then + echo "$ENV_FS_UUID is on $root" + fi +fi + +for d in ${cfg_dir}; do + if [ -z "$root" ]; then + echo "searching for ${d}/${cfg}" + if search --file --set=root "${d}/${cfg}" $hints; then + echo "${d}/${cfg} is on $root" + prefix="($root)${d}" + fi + elif [ -f "${d}/${cfg}" ]; then + echo "${d}/${cfg} is on $root" + prefix="($root)${d}" + else + echo "${d}/${cfg} not found in $root" + fi + + if [ "$prefix" -a x"$btrfs_relative_path" = x1 ]; then + btrfs_relative_path=0 + if [ -f /@"${d}"/powerpc-ieee1275/command.lst ]; then + btrfs_relative_path=1 + echo "mounting subvolume @${d}/powerpc-ieee1275 on ${d}/powerpc-ieee1275" + btrfs-mount-subvol ($root) "${d}"/powerpc-ieee1275 @"${d}"/powerpc-ieee1275 + fi + btrfs_relative_path=1 + break + fi +done + +echo "prefix=$prefix root=$root" +if [ -n "$prefix" ]; then + source "${prefix}/${cfg}" +fi +EOF + %{__tar} cvf memdisk.tar ./grub.cfg + ./grub-mkimage -O %{grubarch} -o grub.elf -d grub-core -x grub.der -m memdisk.tar \ + -c %{platform}-config --appended-signature-size %brp_pesign_reservation ${GRUB_MODULES} + ls -l "grub.elf" + truncate -s -%brp_pesign_reservation "grub.elf" +fi +%endif +cd .. +%endif + +%install + +%ifarch %{ix86} x86_64 +cd build-xen +%make_install +install -m 644 grub.xen %{buildroot}/%{_datadir}/%{name}/%{grubxenarch}/. +# provide compatibility sym-link for VM definitions pointing to old location +install -d %{buildroot}%{_libdir}/%{name}/%{grubxenarch} +ln -srf %{buildroot}%{_datadir}/%{name}/%{grubxenarch}/grub.xen %{buildroot}%{_libdir}/%{name}/%{grubxenarch}/grub.xen +cat <<-EoM >%{buildroot}%{_libdir}/%{name}/%{grubxenarch}/DEPRECATED + This directory and its contents was moved to %{_datadir}/%{name}/%{grubxenarch}. + Individual symbolic links are provided for a smooth transition. + Please update your VM definition files to use the new location! +EoM +cd .. +%endif + +%ifarch %{efi} +cd build-efi +%make_install +install -m 644 grub.efi %{buildroot}/%{_datadir}/%{name}/%{grubefiarch}/. +%ifarch x86_64 +ln -srf %{buildroot}/%{_datadir}/%{name}/%{grubefiarch}/grub.efi %{buildroot}/%{_datadir}/%{name}/%{grubefiarch}/grub-tpm.efi +%endif + +# Create grub.efi link to system efi directory +# This is for tools like kiwi not fiddling with the path +%define sysefibasedir %{_datadir}/efi +%define sysefidir %{sysefibasedir}/%{_target_cpu} +install -d %{buildroot}/%{sysefidir} +ln -sr %{buildroot}/%{_datadir}/%{name}/%{grubefiarch}/grub.efi %{buildroot}%{sysefidir}/grub.efi +%if 0%{?suse_version} < 1600 +%ifarch x86_64 +# provide compatibility sym-link for previous shim-install and the like +install -d %{buildroot}/usr/lib64/efi +ln -srf %{buildroot}/%{_datadir}/%{name}/%{grubefiarch}/grub.efi %{buildroot}/usr/lib64/efi/grub.efi +cat <<-EoM >%{buildroot}/usr/lib64/efi/DEPRECATED + This directory and its contents was moved to %{_datadir}/efi/x86_64. + Individual symbolic links are provided for a smooth transition and + may vanish at any point in time. Please use the new location! +EoM +%endif +%endif + +%ifarch x86_64 aarch64 +export BRP_PESIGN_FILES="%{_datadir}/%{name}/%{grubefiarch}/grub.efi" +install -m 444 grub.der %{buildroot}/%{sysefidir}/ +%endif + +cd .. +%endif + +%if ! 0%{?only_efi:1} +cd build +%make_install +if [ "%{platform}" = "ieee1275" ]; then + export BRP_PESIGN_FILES="%{_datadir}/%{name}/%{grubarch}/grub.elf" + export BRP_PESIGN_GRUB_RESERVATION=%brp_pesign_reservation + install -m 444 grub.der %{buildroot}%{_datadir}/%{name}/%{grubarch}/ + install -m 644 grub.elf %{buildroot}%{_datadir}/%{name}/%{grubarch}/ +fi +cd .. +%endif + +if [ "%{platform}" = "emu" ]; then + # emu-lite is currently broken (and not needed), don't install! + rm -f %{buildroot}/%{_bindir}/%{name}-emu-lite +elif [ -d build-emu/grub-core ]; then + cd build-emu/grub-core + install -m 755 grub-emu %{buildroot}/%{_bindir}/%{name}-emu + if false; then + # this needs to go to '-emu'-package; until that is ready, don't install! + install -m 755 grub-emu-lite %{buildroot}/%{_bindir}/%{name}-emu-lite + else + rm -f %{buildroot}/%{_bindir}/%{name}-emu-lite + fi + install -m 644 grub-emu.1 %{buildroot}/%{_mandir}/man1/%{name}-emu.1 + cd ../.. +fi + +# *.module files are installed with executable bits due to the way grub2 build +# system works. Clear executable bits to not confuse find-debuginfo.sh +find %{buildroot}/%{_datadir}/%{name} \ + \( -name '*.module' -o -name '*.image' -o -name '*.exec' \) -print0 | \ + xargs --no-run-if-empty -0 chmod a-x + +# Script that makes part of grub.cfg persist across updates +install -m 755 %{SOURCE1} %{buildroot}/%{_sysconfdir}/grub.d/ + +# Script to generate memtest86+ menu entry +install -m 755 %{SOURCE7} %{buildroot}/%{_sysconfdir}/grub.d/ + +# Ghost config file +install -d %{buildroot}/boot/%{name} +touch %{buildroot}/boot/%{name}/grub.cfg + +# Remove devel files +rm %{buildroot}/%{_datadir}/%{name}/*/*.h +%if 0%{?suse_version} >= 1140 +rm %{buildroot}/%{_datadir}/%{name}/*.h +%endif + +# Defaults +install -m 644 -D %{SOURCE2} %{buildroot}/%{_sysconfdir}/default/grub +install -m 755 -D %{SOURCE6} %{buildroot}/%{_sbindir}/grub2-once +install -m 755 -D %{SOURCE12} %{buildroot}/%{_libdir}/snapper/plugins/grub +install -m 755 -D %{SOURCE14} %{buildroot}/%{_sysconfdir}/grub.d/80_suse_btrfs_snapshot +%if 0%{?has_systemd:1} +install -m 644 -D %{SOURCE15} %{buildroot}/%{_unitdir}/grub2-once.service +install -m 755 -D %{SOURCE17} %{buildroot}/%{_libdir}/systemd/system-sleep/grub2.sleep +%endif +install -m 755 -D %{SOURCE18} %{buildroot}/%{_sbindir}/grub2-check-default +%ifarch %{ix86} x86_64 +install -m 755 -D %{SOURCE19} %{buildroot}/%{_libexecdir}/grub2-instdev-fixup.pl +%endif + +R="%{buildroot}" +%ifarch %{ix86} x86_64 +%else +rm -f $R%{_sysconfdir}/grub.d/20_memtest86+ +%endif + +%ifarch ppc ppc64 ppc64le +rm -f $R%{_sysconfdir}/grub.d/95_textmode +%else +rm -f $R%{_sysconfdir}/grub.d/20_ppc_terminfo +%endif + +%ifarch s390x +mv $R%{_sysconfdir}/{grub.d,default}/zipl2grub.conf.in +chmod 600 $R%{_sysconfdir}/default/zipl2grub.conf.in + +%define dracutlibdir %{_prefix}/lib/dracut +%define dracutgrubmoddir %{dracutlibdir}/modules.d/99grub2 +install -m 755 -d $R%{dracutgrubmoddir} +for f in module-setup.sh grub2.sh; do + mv $R%{_datadir}/%{name}/%{grubarch}/dracut-$f $R%{dracutgrubmoddir}/$f +done +mv $R%{_datadir}/%{name}/%{grubarch}/dracut-zipl-refresh \ + $R%{_datadir}/%{name}/zipl-refresh +rm -f $R%{_sysconfdir}/grub.d/30_os-prober + +perl -ni -e ' + sub END() { + print "\n# on s390x always:\nGRUB_DISABLE_OS_PROBER=true\n"; + } + if ( s{^#?(GRUB_TERMINAL)=(console|gfxterm)}{$1=console} ) { + $_ .= "GRUB_GFXPAYLOAD_LINUX=text\n"; + } + if ( m{^# The resolution used on graphical} || + m{^# # note that you can use only modes} || + m{^# you can see them in real GRUB} || + m{^#?GRUB_GFXMODE=} ) { + next; + } + s{openSUSE}{SUSE Linux Enterprise Server} if (m{^GRUB_DISTRIBUTOR}); + print; +' %{buildroot}/%{_sysconfdir}/default/grub +%else +%endif + +# bsc#1205554 move the zfs modules into extras packages +# EXTRA_PATTERN='pattern1|pattern2|pattern3|...' +EXTRA_PATTERN="zfs" +%ifarch %{ix86} x86_64 +find %{buildroot}/%{_datadir}/%{name}/%{grubxenarch}/ -type f | sed 's,%{buildroot},,' > %{grubxenarch}-all.lst +grep -v -E ${EXTRA_PATTERN} %{grubxenarch}-all.lst > %{grubxenarch}.lst +grep -E ${EXTRA_PATTERN} %{grubxenarch}-all.lst > %{grubxenarch}-extras.lst +%endif + +%ifarch %{efi} +find %{buildroot}/%{_datadir}/%{name}/%{grubefiarch}/ -name '*.mod' | sed 's,%{buildroot},,' > %{grubefiarch}-mod-all.lst +grep -v -E ${EXTRA_PATTERN} %{grubefiarch}-mod-all.lst > %{grubefiarch}-mod.lst +grep -E ${EXTRA_PATTERN} %{grubefiarch}-mod-all.lst > %{grubefiarch}-mod-extras.lst +%endif + +find %{buildroot}/%{_datadir}/%{name}/%{grubarch}/ -name '*.mod' | sed 's,%{buildroot},,' > %{grubarch}-mod-all.lst +grep -v -E ${EXTRA_PATTERN} %{grubarch}-mod-all.lst > %{grubarch}-mod.lst +grep -E ${EXTRA_PATTERN} %{grubarch}-mod-all.lst > %{grubarch}-mod-extras.lst + +%find_lang %{name} +%fdupes %buildroot%{_bindir} +%fdupes %buildroot%{_libdir} +%fdupes %buildroot%{_datadir} + +%pre +%service_add_pre grub2-once.service + +%post +%service_add_post grub2-once.service + +%if ! 0%{?only_efi:1} + +%post %{grubarch} +%if 0%{?update_bootloader_check_type_reinit_post:1} +%update_bootloader_check_type_reinit_post grub2 +%else +# To check by current loader settings +if [ -f %{_sysconfdir}/sysconfig/bootloader ]; then + . %{_sysconfdir}/sysconfig/bootloader +fi + +# If the grub is the current loader, we'll handle the grub2 testing entry +if [ "x${LOADER_TYPE}" = "xgrub" ]; then + + exec >/dev/null 2>&1 + + # check if entry for grub2's core.img exists in the config + # if yes, we will correct obsoleted path and update grub2 stuff and config to make it work + # if no, do nothing + if [ -f /boot/grub/menu.lst ]; then + + # If grub config contains obsolete core.img path, remove and use the new one + if /usr/bin/grep -l "^\s*kernel\s*.*/boot/%{name}/core.img" /boot/grub/menu.lst; then + /sbin/update-bootloader --remove --image /boot/%{name}/core.img || true + /sbin/update-bootloader --add --image /boot/%{name}/i386-pc/core.img --name "GNU GRUB 2" || true + fi + + # Install grub2 stuff and config to make the grub2 testing entry to work with updated version + if /usr/bin/grep -l "^\s*kernel\s*.*/boot/%{name}/i386-pc/core.img" /boot/grub/menu.lst; then + # Determine the partition with /boot + BOOT_PARTITION=$(df -h /boot | sed -n '2s/[[:blank:]].*//p') + # Generate core.img, but don't let it be installed in boot sector + %{name}-install --no-bootsector $BOOT_PARTITION || true + # Create a working grub2 config, otherwise that entry is un-bootable + /usr/sbin/grub2-mkconfig -o /boot/%{name}/grub.cfg + fi + fi + +elif [ "x${LOADER_TYPE}" = "xgrub2" ]; then + + # It's enought to call update-bootloader to install grub2 and update it's config + # Use new --reinit, if not available use --refresh + # --reinit: install and update bootloader config + # --refresh: update bootloader config + /sbin/update-bootloader --reinit 2>&1 | grep -q 'Unknown option: reinit' && + /sbin/update-bootloader --refresh || true +fi +%endif + +%posttrans %{grubarch} +%{?update_bootloader_posttrans} + +%endif + +%ifarch %{efi} + +%post %{grubefiarch} +%if 0%{?fde_tpm_update_post:1} +%fde_tpm_update_post grub2-efi +%endif + +%if 0%{?update_bootloader_check_type_reinit_post:1} +%update_bootloader_check_type_reinit_post grub2-efi +%else +# To check by current loader settings +if [ -f %{_sysconfdir}/sysconfig/bootloader ]; then + . %{_sysconfdir}/sysconfig/bootloader +fi + +if [ "x${LOADER_TYPE}" = "xgrub2-efi" ]; then + + if [ -d /boot/%{name}-efi ]; then + # Migrate settings to standard prefix /boot/grub2 + for i in custom.cfg grubenv; do + [ -f /boot/%{name}-efi/$i ] && cp -a /boot/%{name}-efi/$i /boot/%{name} || : + done + + fi + + # It's enough to call update-bootloader to install grub2 and update it's config + # Use new --reinit, if not available use --refresh + # --reinit: install and update bootloader config + # --refresh: update bootloader config + /sbin/update-bootloader --reinit 2>&1 | grep -q 'Unknown option: reinit' && + /sbin/update-bootloader --refresh || true +fi + +if [ -d /boot/%{name}-efi ]; then + mv /boot/%{name}-efi /boot/%{name}-efi.rpmsave +fi + +exit 0 +%endif + +%posttrans %{grubefiarch} +%{?update_bootloader_posttrans} +%{?fde_tpm_update_posttrans} + +%endif + +%preun +%service_del_preun grub2-once.service +# We did not add core.img to grub1 menu.lst in new update-bootloader macro as what +# the old %%post ever did, then the %%preun counterpart which removed the added core.img +# entry from old %%post can be skipped entirely if having new macro in use. +%if ! 0%{?update_bootloader_posttrans:1}%{?only_efi:1} +if [ $1 = 0 ]; then + # To check by current loader settings + if [ -f %{_sysconfdir}/sysconfig/bootloader ]; then + . %{_sysconfdir}/sysconfig/bootloader + fi + + if [ "x${LOADER_TYPE}" = "xgrub" ]; then + + exec >/dev/null 2>&1 + + if [ -f /boot/grub/menu.lst ]; then + + # Remove grub2 testing entry in menu.lst if has any + for i in /boot/%{name}/core.img /boot/%{name}/i386-pc/core.img; do + if /usr/bin/grep -l "^\s*kernel\s*.*$i" /boot/grub/menu.lst; then + /sbin/update-bootloader --remove --image "$i" || true + fi + done + fi + + # Cleanup config, to not confuse some tools determining bootloader in use + rm -f /boot/%{name}/grub.cfg + + # Cleanup installed files + # Unless grub2 provides grub2-uninstall, we don't remove any file because + # we have no idea what's been installed. (And a blind remove is dangerous + # to remove user's or other package's file accidently ..) + fi +fi +%endif + +%postun +%service_del_postun grub2-once.service + +%files -f %{name}.lang +%defattr(-,root,root,-) +%if 0%{?suse_version} < 1500 +%doc COPYING +%else +%license COPYING +%endif +%doc AUTHORS +%doc NEWS README +%doc THANKS TODO ChangeLog +%doc docs/autoiso.cfg docs/osdetect.cfg +%ifarch s390x +%doc README.ibm3215 +%endif +%dir /boot/%{name} +%ghost %attr(600, root, root) /boot/%{name}/grub.cfg +%{_datadir}/bash-completion/completions/grub +%config(noreplace) %{_sysconfdir}/default/grub +%dir %{_sysconfdir}/grub.d +%{_sysconfdir}/grub.d/README +%config(noreplace) %{_sysconfdir}/grub.d/00_header +%config(noreplace) %{_sysconfdir}/grub.d/05_crypttab +%config(noreplace) %{_sysconfdir}/grub.d/10_linux +%config(noreplace) %{_sysconfdir}/grub.d/20_linux_xen +%config(noreplace) %{_sysconfdir}/grub.d/25_bli +%config(noreplace) %{_sysconfdir}/grub.d/30_uefi-firmware +%config(noreplace) %{_sysconfdir}/grub.d/40_custom +%config(noreplace) %{_sysconfdir}/grub.d/41_custom +%config(noreplace) %{_sysconfdir}/grub.d/90_persistent +%ifnarch ppc ppc64 ppc64le +%config(noreplace) %{_sysconfdir}/grub.d/95_textmode +%endif +%ifarch %{ix86} x86_64 +%config(noreplace) %{_sysconfdir}/grub.d/20_memtest86+ +%endif +%ifarch ppc ppc64 ppc64le +%config(noreplace) %{_sysconfdir}/grub.d/20_ppc_terminfo +%endif +%ifarch s390x +%config(noreplace) %{_sysconfdir}/default/zipl2grub.conf.in +%{dracutlibdir} +%{_sbindir}/%{name}-zipl-setup +%{_datadir}/%{name}/zipl-refresh +%endif +%{_sbindir}/%{name}-install +%{_sbindir}/%{name}-mkconfig +%{_sbindir}/%{name}-once +%{_sbindir}/%{name}-probe +%{_sbindir}/%{name}-reboot +%{_sbindir}/%{name}-set-default +%{_sbindir}/%{name}-check-default +%{_bindir}/%{name}-editenv +%{_bindir}/%{name}-file +%{_bindir}/%{name}-fstest +%{_bindir}/%{name}-kbdcomp +%{_bindir}/%{name}-menulst2cfg +%{_bindir}/%{name}-mkfont +%{_bindir}/%{name}-mkimage +%{_bindir}/%{name}-mklayout +%{_bindir}/%{name}-mknetdir +%{_bindir}/%{name}-mkpasswd-pbkdf2 +%{_bindir}/%{name}-mkrelpath +%{_bindir}/%{name}-mkrescue +%{_bindir}/%{name}-mkstandalone +%{_bindir}/%{name}-render-label +%{_bindir}/%{name}-script-check +%{_bindir}/%{name}-syslinux2cfg +%ifarch %{efi} +%{_bindir}/%{name}-protect +%endif +%if 0%{?has_systemd:1} +%{_unitdir}/grub2-once.service +%endif +%dir %{_datadir}/%{name} +%dir %{_datadir}/%{name}/themes +%if 0%{?suse_version} >= 1140 +%{_datadir}/%{name}/*.pf2 +%endif +%{_datadir}/%{name}/grub-mkconfig_lib +%{_infodir}/grub-dev.info* +%{_infodir}/%{name}.info* +%{_mandir}/man1/%{name}-editenv.1.* +%{_mandir}/man1/%{name}-file.1.* +%{_mandir}/man1/%{name}-fstest.1.* +%{_mandir}/man1/%{name}-kbdcomp.1.* +%{_mandir}/man1/%{name}-menulst2cfg.1.* +%{_mandir}/man1/%{name}-mkfont.1.* +%{_mandir}/man1/%{name}-mkimage.1.* +%{_mandir}/man1/%{name}-mklayout.1.* +%{_mandir}/man1/%{name}-mknetdir.1.* +%{_mandir}/man1/%{name}-mkpasswd-pbkdf2.1.* +%{_mandir}/man1/%{name}-mkrelpath.1.* +%{_mandir}/man1/%{name}-mkrescue.1.* +%{_mandir}/man1/%{name}-mkstandalone.1.* +%{_mandir}/man1/%{name}-render-label.1.* +%{_mandir}/man1/%{name}-script-check.1.* +%{_mandir}/man1/%{name}-syslinux2cfg.1.* +%{_mandir}/man8/%{name}-install.8.* +%{_mandir}/man8/%{name}-mkconfig.8.* +%{_mandir}/man8/%{name}-probe.8.* +%{_mandir}/man8/%{name}-reboot.8.* +%{_mandir}/man8/%{name}-set-default.8.* +%if %{emu} +%{_bindir}/%{name}-emu +%{_mandir}/man1/%{name}-emu.1.* +%endif +%ifnarch s390x +%config(noreplace) %{_sysconfdir}/grub.d/30_os-prober +%{_bindir}/%{name}-glue-efi +%{_bindir}/%{name}-mount +%{_sbindir}/%{name}-bios-setup +%{_sbindir}/%{name}-macbless +%{_sbindir}/%{name}-ofpathname +%{_sbindir}/%{name}-sparc64-setup +%{_mandir}/man1/%{name}-glue-efi.1.* +%{_mandir}/man1/%{name}-mount.1.* +%{_mandir}/man8/%{name}-bios-setup.8.* +%{_mandir}/man8/%{name}-macbless.8.* +%{_mandir}/man8/%{name}-ofpathname.8.* +%{_mandir}/man8/%{name}-sparc64-setup.8.* +%endif + +%files branding-upstream +%defattr(-,root,root,-) +%{_datadir}/%{name}/themes/starfield + +%if ! 0%{?only_efi:1} + +%files %{grubarch} -f %{grubarch}-mod.lst +%defattr(-,root,root,-) +%dir %{_datadir}/%{name}/%{grubarch} +%ifarch ppc ppc64 ppc64le +# This is intentionally "grub.chrp" and not "%%{name}.chrp" +%{_datadir}/%{name}/%{grubarch}/grub.chrp +%{_datadir}/%{name}/%{grubarch}/grub.elf +%{_datadir}/%{name}/%{grubarch}/grub.der +%{_datadir}/%{name}/%{grubarch}/bootinfo.txt +%endif +%ifnarch ppc ppc64 ppc64le s390x %{arm} +%{_datadir}/%{name}/%{grubarch}/*.image +%endif +%{_datadir}/%{name}/%{grubarch}/*.img +%{_datadir}/%{name}/%{grubarch}/*.lst +%ifarch x86_64 +%{_datadir}/%{name}/%{grubarch}/efiemu*.o +%endif +%{_datadir}/%{name}/%{grubarch}/kernel.exec +%{_datadir}/%{name}/%{grubarch}/modinfo.sh +%ifarch %{ix86} x86_64 +%{_libexecdir}/%{name}-instdev-fixup.pl +%endif + +%files %{grubarch}-extras -f %{grubarch}-mod-extras.lst +%defattr(-,root,root,-) +%dir %{_datadir}/%{name}/%{grubarch} + +%files %{grubarch}-debug +%defattr(-,root,root,-) +%{_datadir}/%{name}/%{grubarch}/gdb_grub +%{_datadir}/%{name}/%{grubarch}/gdb_helper.py +%{_datadir}/%{name}/%{grubarch}/*.module + +%endif + +%ifarch %{efi} + +%files %{grubefiarch} -f %{grubefiarch}-mod.lst +%defattr(-,root,root,-) +%dir %{_datadir}/%{name}/%{grubefiarch} +%{_datadir}/%{name}/%{grubefiarch}/grub.efi +%ifarch x86_64 +%{_datadir}/%{name}/%{grubefiarch}/grub-tpm.efi +%endif +%{_datadir}/%{name}/%{grubefiarch}/*.img +%{_datadir}/%{name}/%{grubefiarch}/*.lst +%{_datadir}/%{name}/%{grubefiarch}/kernel.exec +%{_datadir}/%{name}/%{grubefiarch}/modinfo.sh +%dir %{sysefibasedir} +%dir %{sysefidir} +%{sysefidir}/grub.efi +%if 0%{?suse_version} < 1600 +%ifarch x86_64 +# provide compatibility sym-link for previous shim-install and kiwi +%dir /usr/lib64/efi +/usr/lib64/efi/DEPRECATED +/usr/lib64/efi/grub.efi +%endif +%endif + +%ifarch x86_64 aarch64 +%{sysefidir}/grub.der +%endif + +%files %{grubefiarch}-extras -f %{grubefiarch}-mod-extras.lst +%defattr(-,root,root,-) +%dir %{_datadir}/%{name}/%{grubefiarch} + +%files %{grubefiarch}-debug +%defattr(-,root,root,-) +%{_datadir}/%{name}/%{grubefiarch}/gdb_grub +%{_datadir}/%{name}/%{grubefiarch}/gdb_helper.py +%{_datadir}/%{name}/%{grubefiarch}/*.module + +%endif + +%files snapper-plugin +%defattr(-,root,root,-) +%dir %{_libdir}/snapper +%dir %{_libdir}/snapper/plugins +%config(noreplace) %{_sysconfdir}/grub.d/80_suse_btrfs_snapshot +%{_libdir}/snapper/plugins/grub + +%ifarch %{ix86} x86_64 +%files %{grubxenarch} -f %{grubxenarch}.lst +%defattr(-,root,root,-) +%dir %{_datadir}/%{name}/%{grubxenarch} +# provide compatibility sym-link for VM definitions pointing to old location +%dir %{_libdir}/%{name} +%{_libdir}/%{name}/%{grubxenarch} + +%files %{grubxenarch}-extras -f %{grubxenarch}-extras.lst +%defattr(-,root,root,-) +%dir %{_datadir}/%{name}/%{grubxenarch} +%endif + +%if 0%{?has_systemd:1} +%files systemd-sleep-plugin +%defattr(-,root,root,-) +%dir %{_libdir}/systemd/system-sleep +%{_libdir}/systemd/system-sleep/grub2.sleep +%endif + +%changelog diff --git a/info-dir-entry.patch b/info-dir-entry.patch new file mode 100644 index 0000000..4a9a9af --- /dev/null +++ b/info-dir-entry.patch @@ -0,0 +1,29 @@ +Index: grub-2.02~beta3/docs/grub.texi +=================================================================== +--- grub-2.02~beta3.orig/docs/grub.texi ++++ grub-2.02~beta3/docs/grub.texi +@@ -32,15 +32,15 @@ Invariant Sections. + + @dircategory Kernel + @direntry +-* GRUB: (grub). The GRand Unified Bootloader +-* grub-install: (grub)Invoking grub-install. Install GRUB on your drive +-* grub-mkconfig: (grub)Invoking grub-mkconfig. Generate GRUB configuration +-* grub-mkpasswd-pbkdf2: (grub)Invoking grub-mkpasswd-pbkdf2. +-* grub-mkrelpath: (grub)Invoking grub-mkrelpath. +-* grub-mkrescue: (grub)Invoking grub-mkrescue. Make a GRUB rescue image +-* grub-mount: (grub)Invoking grub-mount. Mount a file system using GRUB +-* grub-probe: (grub)Invoking grub-probe. Probe device information +-* grub-script-check: (grub)Invoking grub-script-check. ++* GRUB2: (grub2). The GRand Unified Bootloader ++* grub2-install: (grub2)Invoking grub-install. Install GRUB on your drive ++* grub2-mkconfig: (grub2)Invoking grub-mkconfig. Generate GRUB configuration ++* grub2-mkpasswd-pbkdf2: (grub2)Invoking grub-mkpasswd-pbkdf2. ++* grub2-mkrelpath: (grub2)Invoking grub-mkrelpath. ++* grub2-mkrescue: (grub2)Invoking grub-mkrescue. Make a GRUB rescue image ++* grub2-mount: (grub2)Invoking grub-mount. Mount a file system using GRUB ++* grub2-probe: (grub2)Invoking grub-probe. Probe device information ++* grub2-script-check: (grub2)Invoking grub-script-check. + @end direntry + + @setchapternewpage odd diff --git a/not-display-menu-when-boot-once.patch b/not-display-menu-when-boot-once.patch new file mode 100644 index 0000000..c264c19 --- /dev/null +++ b/not-display-menu-when-boot-once.patch @@ -0,0 +1,31 @@ +From 78270522e8b8c0674941e0752c245dd8468e5bf8 Mon Sep 17 00:00:00 2001 +From: Michael Chang +Date: Wed, 1 Aug 2012 15:46:34 +0800 +Subject: [PATCH] not display menu when boot once + +References: bnc#771587 +Patch-Mainline: no + +We should prevent the menu from being displayed if boot once is +specified. This is in order to compliant with Grub1's behavior +and is better than current as it's not make any sense to bother +user to make decision when decision has been made. +--- + util/grub.d/00_header.in | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +Index: grub-2.02~beta2/util/grub.d/00_header.in +=================================================================== +--- grub-2.02~beta2.orig/util/grub.d/00_header.in ++++ grub-2.02~beta2/util/grub.d/00_header.in +@@ -304,7 +304,9 @@ make_timeout () + style="menu" + fi + cat << EOF +-if [ x\$feature_timeout_style = xy ] ; then ++if [ x\${boot_once} = xtrue ]; then ++ set timeout=0 ++elif [ x\$feature_timeout_style = xy ] ; then + set timeout_style=${style} + set timeout=${timeout} + EOF diff --git a/openSUSE-UEFI-CA-Certificate.crt b/openSUSE-UEFI-CA-Certificate.crt new file mode 100644 index 0000000..eb49085 --- /dev/null +++ b/openSUSE-UEFI-CA-Certificate.crt @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgTEgMB4GA1UEAwwXb3Bl +blNVU0UgU2VjdXJlIEJvb3QgQ0ExCzAJBgNVBAYTAkRFMRIwEAYDVQQHDAlOdXJl +bWJlcmcxGTAXBgNVBAoMEG9wZW5TVVNFIFByb2plY3QxITAfBgkqhkiG9w0BCQEW +EmJ1aWxkQG9wZW5zdXNlLm9yZzAeFw0xMzA4MjYxNjEyMDdaFw0zNTA3MjIxNjEy +MDdaMIGBMSAwHgYDVQQDDBdvcGVuU1VTRSBTZWN1cmUgQm9vdCBDQTELMAkGA1UE +BhMCREUxEjAQBgNVBAcMCU51cmVtYmVyZzEZMBcGA1UECgwQb3BlblNVU0UgUHJv +amVjdDEhMB8GCSqGSIb3DQEJARYSYnVpbGRAb3BlbnN1c2Uub3JnMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3t9hknqk/oPRfTtoDrGn8E6Sk/xHPnAt +Tojcmp76M7Sm2w4jwQ2owdVlBIQE/zpIGE85MuTKTvkEnp8PzSBdYaunANil/yt/ +vuhHwy9bAsi73o4a6UbThu//iJmQ6xCJuIs/PqgHxlV6btNf/IM8PRbtJsUTc5Kx +cB4ilcgAbCV2RvGi2dCwmGgPpy2xDWeJypRK6hLFkVV2f2x6LvkYiZ/49CRD1TVq +ywAOLu1L4l0J2BuXcJmeWm+mgaidqVh2fWlxgtO6OpZDm/DaFcZO6cgVuenLx+Rx +zuoQG2vEKnABqVK0F94AUs995P0PTQMYspAo1G/Erla8NmBJRotrCwIDAQABo4H0 +MIHxMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFGhCYA3iLExHfpW+I9/qlRPl +lxdiMIGuBgNVHSMEgaYwgaOAFGhCYA3iLExHfpW+I9/qlRPllxdioYGHpIGEMIGB +MSAwHgYDVQQDDBdvcGVuU1VTRSBTZWN1cmUgQm9vdCBDQTELMAkGA1UEBhMCREUx +EjAQBgNVBAcMCU51cmVtYmVyZzEZMBcGA1UECgwQb3BlblNVU0UgUHJvamVjdDEh +MB8GCSqGSIb3DQEJARYSYnVpbGRAb3BlbnN1c2Uub3JnggEBMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAQEAiqOJwo7Z+YIL8zPO6RkXF6NlgM0zrgZR +Vim2OId79J38KI6q4FMSDjpgxwbYOmF2O3cI9JSkjHxHOpnYhJsXzCBiLuJ25MY2 +DSbpLlM1Cvs6NZNFw5OCwQvzCOlXH1k3qdBsafto6n87r9P3WSeO1MeWc/QMCvc+ +5K9sjMd6bwl59EEf428R+z5ssaB75JK3yvky9d7DsHN947OCXc3sYdz+DD7Gteds +LV2Sc//tqmqpm2aeXjptcLAxwM7fLyEQaAyH83egMzEKDxX27jKIxZpTcc0NGqEo +idC/9lasSzs2BisBxevl3HKDPZSsKIMT+8FdJ5wT9jJf9h9Ktz5Tig== +-----END CERTIFICATE----- diff --git a/rename-grub-info-file-to-grub2.patch b/rename-grub-info-file-to-grub2.patch new file mode 100644 index 0000000..7962594 --- /dev/null +++ b/rename-grub-info-file-to-grub2.patch @@ -0,0 +1,36 @@ +From 031abf80020b2fa75850d6e09f4489b687a5cb19 Mon Sep 17 00:00:00 2001 +From: Jiri Slaby +Date: Sun, 24 Jun 2012 15:40:40 +0200 +Subject: [PATCH] rename grub info file to grub2 + +Signed-off-by: Jiri Slaby + +From: Andrey Borzenkov +Do not rename file here. quilt does not support it and creates the +whole file if patch needs refreshing. It means that to regenerate two +files - Makefile.core.am and Makefile.util.am - it may be necessary to +manually rename it. +--- + +--- a/docs/Makefile.am ++++ b/docs/Makefile.am +@@ -1,7 +1,7 @@ + AUTOMAKE_OPTIONS = subdir-objects + + # AM_MAKEINFOFLAGS = --no-split --no-validate +-info_TEXINFOS = grub.texi grub-dev.texi ++info_TEXINFOS = grub2.texi grub-dev.texi + grub_TEXINFOS = fdl.texi + + EXTRA_DIST = font_char_metrics.png font_char_metrics.txt +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -1,7 +1,7 @@ + \input texinfo + @c -*-texinfo-*- + @c %**start of header +-@setfilename grub.info ++@setfilename grub2.info + @include version.texi + @settitle GNU GRUB Manual @value{VERSION} + @c Unify all our little indices for now. diff --git a/safe_tpm_pcr_snapshot.patch b/safe_tpm_pcr_snapshot.patch new file mode 100644 index 0000000..b57523f --- /dev/null +++ b/safe_tpm_pcr_snapshot.patch @@ -0,0 +1,99 @@ +--- + grub-core/commands/tpm.c | 46 ++++++++++++++++++++++++++++++++++++---------- + util/grub-install.c | 6 ++++-- + 2 files changed, 40 insertions(+), 12 deletions(-) + +--- a/grub-core/commands/tpm.c ++++ b/grub-core/commands/tpm.c +@@ -27,8 +27,10 @@ + #include + #include + #include ++#ifdef GRUB_MACHINE_EFI + #include + #include ++#endif + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -97,12 +99,6 @@ + .verify_string = grub_tpm_verify_string, + }; + +-/* +- * Preserve current PCR values and record them to an EFI variable +- */ +-#define GRUB2_PCR_BITMASK_DEFAULT ((1 << 16) - 1) +-#define GRUB2_PCR_BITMASK_ALL ((1 << 24) - 1) +- + static const struct grub_arg_option grub_tpm_record_pcrs_options[] = + { + { +@@ -118,6 +114,14 @@ + {0, 0, 0, 0, 0, 0} + }; + ++#ifdef GRUB_MACHINE_EFI ++ ++/* ++ * Preserve current PCR values and record them to an EFI variable ++ */ ++#define GRUB2_PCR_BITMASK_DEFAULT ((1 << 16) - 1) ++#define GRUB2_PCR_BITMASK_ALL ((1 << 24) - 1) ++ + static grub_err_t + grub_tpm_parse_pcr_index (const char *word, const char **end_ret, unsigned int *index) + { +@@ -269,6 +273,10 @@ + grub_size_t size = 0; + int n, rv = 1; + ++ /* To prevent error: unable to read PCR from TPM, if no TPM device available */ ++ if (!grub_tpm_present()) ++ return GRUB_ERR_NONE; ++ + if (argc == 0) + pcr_bitmask = GRUB2_PCR_BITMASK_DEFAULT; + else +@@ -297,6 +305,18 @@ + return rv; + } + ++#else ++ ++static grub_err_t ++grub_tpm_record_pcrs (grub_extcmd_context_t ctxt __attribute__((unused)), ++ int argc __attribute__((unused)), ++ char **args __attribute__((unused))) ++{ ++ return GRUB_ERR_NONE; ++} ++ ++#endif ++ + static grub_extcmd_t cmd; + + GRUB_MOD_INIT (tpm) +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -1501,8 +1501,9 @@ + + grub_util_unlink (load_cfg); + +- if (1) ++ if (platform == GRUB_INSTALL_PLATFORM_X86_64_EFI && have_cryptodisk) + { ++ grub_install_push_module ("tpm"); + load_cfg_f = grub_util_fopen (load_cfg, "wb"); + have_load_cfg = 1; + fprintf (load_cfg_f, "tpm_record_pcrs 0-9\n"); +@@ -1510,7 +1511,8 @@ + + if (debug_image && debug_image[0]) + { +- load_cfg_f = grub_util_fopen (load_cfg, "wb"); ++ if (!load_cfg_f) ++ load_cfg_f = grub_util_fopen (load_cfg, "wb"); + have_load_cfg = 1; + fprintf (load_cfg_f, "set debug='%s'\n", + debug_image); diff --git a/tpm-record-pcrs.patch b/tpm-record-pcrs.patch new file mode 100644 index 0000000..40bbb71 --- /dev/null +++ b/tpm-record-pcrs.patch @@ -0,0 +1,235 @@ +--- a/grub-core/commands/tpm.c ++++ b/grub-core/commands/tpm.c +@@ -26,6 +26,9 @@ + #include + #include + #include ++#include ++#include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -94,8 +97,214 @@ + .verify_string = grub_tpm_verify_string, + }; + ++/* ++ * Preserve current PCR values and record them to an EFI variable ++ */ ++#define GRUB2_PCR_BITMASK_DEFAULT ((1 << 16) - 1) ++#define GRUB2_PCR_BITMASK_ALL ((1 << 24) - 1) ++ ++static const struct grub_arg_option grub_tpm_record_pcrs_options[] = ++ { ++ { ++ .longarg = "efivar", ++ .shortarg = 'E', ++ .flags = 0, ++ .arg = NULL, ++ .type = ARG_TYPE_STRING, ++ .doc = ++ N_("The EFI variable to publish the PCRs to (default GrubPcrSnapshot)"), ++ }, ++ ++ {0, 0, 0, 0, 0, 0} ++ }; ++ ++static grub_err_t ++grub_tpm_parse_pcr_index (const char *word, const char **end_ret, unsigned int *index) ++{ ++ const char *end; ++ ++ if (!grub_isdigit (word[0])) ++ return GRUB_ERR_BAD_NUMBER; ++ ++ *index = grub_strtoul(word, &end, 0); ++ if (*index > 32) ++ return GRUB_ERR_BAD_NUMBER; ++ ++ *end_ret = end; ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_tpm_parse_pcr_list (const char *arg, grub_uint32_t *bitmask) ++{ ++ const char *word, *end; ++ unsigned int index, last_index = 0; ++ ++ if (!grub_strcmp (arg, "all")) ++ { ++ *bitmask = GRUB2_PCR_BITMASK_ALL; ++ return GRUB_ERR_NONE; ++ } ++ ++ word = arg; ++ while (1) ++ { ++ if (grub_tpm_parse_pcr_index (word, &end, &index)) ++ goto bad_pcr_index; ++ ++ if (*end == '-') ++ { ++ if (grub_tpm_parse_pcr_index (end + 1, &end, &last_index) || last_index < index) ++ goto bad_pcr_index; ++ ++ while (index <= last_index) ++ *bitmask |= (1 << (index++)); ++ } ++ else ++ *bitmask |= (1 << index); ++ ++ if (*end == '\0') ++ break; ++ ++ if (*end != ',') ++ goto bad_pcr_index; ++ ++ word = end + 1; ++ } ++ ++ return GRUB_ERR_NONE; ++ ++bad_pcr_index: ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("cannot parse PCR list \"%s\""), arg); ++} ++ ++static inline unsigned int ++nbits(grub_uint32_t mask) ++{ ++ unsigned int r = 0; ++ ++ for (; mask != 0; mask >>= 1) ++ r += (mask & 1); ++ return r; ++} ++ ++static grub_err_t ++grub_tpm_snapshot_pcrs (grub_uint32_t pcr_bitmask, const char *algo, ++ void **buffer_ret, grub_size_t *size_ret) ++{ ++ char *buffer; ++ grub_size_t size = 65536; ++ unsigned int wpos = 0; ++ grub_uint8_t pcr; ++ ++ buffer = grub_malloc (size); ++ for (pcr = 0; pcr < 32; ++pcr) ++ { ++ struct grub_tpm_digest *d; ++ unsigned int need, k; ++ ++ if (!(pcr_bitmask & (1 << pcr))) ++ continue; ++ ++ d = grub_tpm_read_pcr (pcr, algo); ++ if (d == NULL) ++ { ++ grub_error (GRUB_ERR_BAD_DEVICE, N_("unable to read PCR %d from TPM"), pcr); ++ continue; ++ } ++ ++ /* We need room for the PCR index, 2 spaces, newline, NUL. 16 should be enough. */ ++ need = 16 + grub_strlen(d->algorithm) + 2 * d->size; ++ if (wpos + need > size) ++ { ++ buffer = grub_realloc (buffer, size + need); ++ if (buffer == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("Not enough memory when dumping PCR registers")); ++ } ++ ++ grub_snprintf (buffer + wpos, size - wpos, "%02d %s ", pcr, d->algorithm); ++ wpos = grub_strlen(buffer); ++ ++ for (k = 0; k < d->size; ++k) ++ { ++ grub_snprintf (buffer + wpos, size - wpos, "%02x", d->value[k]); ++ wpos += 2; ++ } ++ ++ buffer[wpos++] = '\n'; ++ buffer[wpos] = '\0'; ++ ++ grub_tpm_digest_free (d); ++ } ++ ++ *buffer_ret = buffer; ++ *size_ret = wpos; ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_tpm_write_pcrs_to_efi (void *data, grub_size_t size, const char *var_name) ++{ ++ grub_guid_t vendor_guid = { 0x7ce323f2, 0xb841, 0x4d30, { 0xa0, 0xe9, 0x54, 0x74, 0xa7, 0x6c, 0x9a, 0x3f }}; ++ grub_err_t rc; ++ ++ rc = grub_efi_set_variable_with_attributes(var_name, &vendor_guid, ++ data, size, ++ GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | GRUB_EFI_VARIABLE_RUNTIME_ACCESS); ++ ++ if (rc) ++ return grub_error (GRUB_ERR_BAD_DEVICE, N_("Failed to publish PCR snapshot to UEFI variable %s"), var_name); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_tpm_record_pcrs (grub_extcmd_context_t ctxt, int argc, char **args) ++{ ++ struct grub_arg_list *state = ctxt->state; ++ grub_uint32_t pcr_bitmask = 0; ++ const char *efivar; ++ void *buffer = NULL; ++ grub_size_t size = 0; ++ int n, rv = 1; ++ ++ if (argc == 0) ++ pcr_bitmask = GRUB2_PCR_BITMASK_DEFAULT; ++ else ++ { ++ for (n = 0; n < argc; ++n) ++ if (grub_tpm_parse_pcr_list (args[n], &pcr_bitmask)) ++ return 1; ++ } ++ ++ if (grub_tpm_snapshot_pcrs (pcr_bitmask, NULL, &buffer, &size)) ++ goto out; ++ ++ if (state[0].set) ++ efivar = state[0].arg; ++ else ++ efivar = "GrubPcrSnapshot"; ++ ++ if (grub_tpm_write_pcrs_to_efi (buffer, size, efivar)) ++ goto out; ++ ++ rv = 0; ++ ++out: ++ if (buffer) ++ grub_free (buffer); ++ return rv; ++} ++ ++static grub_extcmd_t cmd; ++ + GRUB_MOD_INIT (tpm) + { ++ cmd = grub_register_extcmd ("tpm_record_pcrs", grub_tpm_record_pcrs, 0, ++ N_("LIST_OF_PCRS"), ++ N_("Snapshot one or more PCR values and record them in an EFI variable."), ++ grub_tpm_record_pcrs_options); + /* + * Even though this now calls ibmvtpm's grub_tpm_present() from GRUB_MOD_INIT(), + * it does seem to call it late enough in the initialization sequence so +@@ -109,6 +318,7 @@ + + GRUB_MOD_FINI (tpm) + { ++ grub_unregister_extcmd (cmd); + if (!grub_tpm_present()) + return; + grub_verifier_unregister (&grub_tpm_verifier); diff --git a/use-grub2-as-a-package-name.patch b/use-grub2-as-a-package-name.patch new file mode 100644 index 0000000..25e2237 --- /dev/null +++ b/use-grub2-as-a-package-name.patch @@ -0,0 +1,25 @@ +From 3729b131ef1dcaa043242e8074418249695d381b Mon Sep 17 00:00:00 2001 +From: Jiri Slaby +Date: Sun, 24 Jun 2012 20:51:52 +0200 +Subject: [PATCH] use grub2 as a package name + +This will ease all of the renaming of directories and all the pkgdata +hacks. + +Signed-off-by: Jiri Slaby +--- + configure | 24 ++++++++++++------------ + configure.ac | 2 +- + 2 files changed, 13 insertions(+), 13 deletions(-) + +--- a/configure.ac ++++ b/configure.ac +@@ -34,7 +34,7 @@ + dnl the target type. See INSTALL for full list of variables and + dnl description of the relationships between them. + +-AC_INIT([GRUB],[2.12~rc1],[bug-grub@gnu.org]) ++AC_INIT([GRUB2],[2.12~rc1],[bug-grub@gnu.org]) + + AS_CASE(["$ERROR_PLATFORM_NOT_SUPPORT_SSP"], + [n | no | nO | N | No | NO], [ERROR_PLATFORM_NOT_SUPPORT_SSP=no],