summaryrefslogtreecommitdiff
path: root/memattrs.c
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2025-07-06 21:59:18 +0200
committerChristian Breunig <christian@breunig.cc>2025-07-06 21:59:18 +0200
commit02acad285c74015e8120ade2b41d51b39ae66b63 (patch)
tree980533ac963ac23bc9e090e3e4212bdb9e225a05 /memattrs.c
parent1c1d50da810e6c49e804a74719c2675b88b033a6 (diff)
parent18d98bfb34be583a5fe2987542e4b15e0db9cb61 (diff)
downloadefi-boot-shim-02acad285c74015e8120ade2b41d51b39ae66b63.tar.gz
efi-boot-shim-02acad285c74015e8120ade2b41d51b39ae66b63.zip
Merge tag '16.0' into vyos/current
shim-16.0 What's Changed * Validate that a supplied vendor cert is not in PEM format by @steve-mcintyre in https://github.com/rhboot/shim/pull/646 * sbat: Add grub.peimage,2 to latest (CVE-2024-2312) by @julian-klode in https://github.com/rhboot/shim/pull/651 * sbat: Also bump latest for grub,4 (and to todays date) by @julian-klode in https://github.com/rhboot/shim/pull/653 * undo change that limits certificate files to a single file by @jsetje in https://github.com/rhboot/shim/pull/659 * shim: don't set second_stage to the empty string by @jjd27 in https://github.com/rhboot/shim/pull/640 * Fix SBAT.md for today's consensus about numbers by @aronowski in https://github.com/rhboot/shim/pull/672 * Update Code of Conduct contact address by @aronowski in https://github.com/rhboot/shim/pull/683 * make-certs: Handle missing OpenSSL installation by @aronowski in https://github.com/rhboot/shim/pull/595 * Update MokVars.txt by @mikebeaton in https://github.com/rhboot/shim/pull/598 * export DEFINES for sub makefile by @bryteise in https://github.com/rhboot/shim/pull/600 * Drop unused EFI_IMAGE_SECURITY_DATABASE_GUID definition by @vittyvk in https://github.com/rhboot/shim/pull/609 * Null-terminate 'arguments' in fallback by @vittyvk in https://github.com/rhboot/shim/pull/611 * Fix "Verifiying" typo in error message by @chrisbainbridge in https://github.com/rhboot/shim/pull/706 * Update Fedora CI targets by @vathpela in https://github.com/rhboot/shim/pull/708 * Force gcc to produce DWARF4 so that gdb can use it by @mikebeaton in https://github.com/rhboot/shim/pull/607 * Minor housekeeping 2024121700 by @vathpela in https://github.com/rhboot/shim/pull/709 * Discard load-options that start with WINDOWS by @Metabolix in https://github.com/rhboot/shim/pull/621 * Fix the issue that the gBS->LoadImage pointer was empty. by @15058718379 in https://github.com/rhboot/shim/pull/703 * shim: Allow data after the end of device path node in load options by @dbnicholson in https://github.com/rhboot/shim/pull/694 * Handle network file not found like disks by @dbnicholson in https://github.com/rhboot/shim/pull/695 * Update gnu-efi submodule for EFI_HTTP_ERROR by @vathpela in https://github.com/rhboot/shim/pull/674 * Increase EFI file alignment by @lumag in https://github.com/rhboot/shim/pull/673 * avoid EFIv2 runtime services on Apple x86 machines by @eduardacatrinei in https://github.com/rhboot/shim/pull/690 * Improve shortcut performance when comparing two boolean expressions by @dennis-tseng99 in https://github.com/rhboot/shim/pull/667 * Provide better error message when MokManager is not found by @rmetrich in https://github.com/rhboot/shim/pull/663 * tpm: Boot with a warning if the event log is full by @kukrimate in https://github.com/rhboot/shim/pull/657 * MokManager: remove redundant logical constraints by @xypron in https://github.com/rhboot/shim/pull/409 * Test import_mok_state() when MokListRT would be bigger than available size by @vathpela in https://github.com/rhboot/shim/pull/417 * test-mok-mirror: minor bug fix by @vathpela in https://github.com/rhboot/shim/pull/715 * Fix file system browser hang when enrolling MOK from disk by @miczyg1 in https://github.com/rhboot/shim/pull/622 * Ignore a minor clang-tidy nit by @vathpela in https://github.com/rhboot/shim/pull/716 * Allow fallback to default loader when encountering errors on network boot by @nathan-omeara in https://github.com/rhboot/shim/pull/666 * test.mk: don't use a temporary random.bin by @vathpela in https://github.com/rhboot/shim/pull/718 * pe: Enhance debug report for update_mem_attrs by @jongwu in https://github.com/rhboot/shim/pull/594 * Multiple certificate handling improvements by @rosslagerwall in https://github.com/rhboot/shim/pull/644 * Generate SbatLevel Metadata from SbatLevel_Variable.txt by @jsetje in https://github.com/rhboot/shim/pull/711 * Apply EKU check with compile option by @dennis-tseng99 in https://github.com/rhboot/shim/pull/664 * Add configuration option to boot an alternative 2nd stage by @esnowberg in https://github.com/rhboot/shim/pull/608 * Loader protocol (with Device Path resolution support) by @kukrimate in https://github.com/rhboot/shim/pull/656 * netboot cleanup for additional files by @jsetje in https://github.com/rhboot/shim/pull/686 * Document how revocations can be delivered by @jsetje in https://github.com/rhboot/shim/pull/722 * post-process-pe: add tests to validate NX compliance by @vathpela in https://github.com/rhboot/shim/pull/705 * regression: CopyMem() in ad8692e copies out of bounds by @jsetje in https://github.com/rhboot/shim/pull/725 * Save the debug and error logs in mok-variables by @vathpela in https://github.com/rhboot/shim/pull/726 * Add features for the Host Security ID program by @vathpela in https://github.com/rhboot/shim/pull/660 * Mirror some more efi variables to mok-variables by @vathpela in https://github.com/rhboot/shim/pull/723 * This adds DXE Services measurements to HSI and uses them for NX by @vathpela in https://github.com/rhboot/shim/pull/724 * Add shim's current NX_COMPAT status to HSIStatus by @vathpela in https://github.com/rhboot/shim/pull/727 * README.tpm: reflect that vendor_db is in fact logged as "vendor_db" by @jsetje in https://github.com/rhboot/shim/pull/728 * Reject HTTP message with duplicate Content-Length header fields by @dennis-tseng99 in https://github.com/rhboot/shim/pull/637 * Disable log saving by @vathpela in https://github.com/rhboot/shim/pull/729 * fallback: don't add new boot order entries backwards by @vathpela in https://github.com/rhboot/shim/pull/730 * Misc fixes... by @vathpela in https://github.com/rhboot/shim/pull/735 * README.tpm: Update MokList entry to MokListRT by @trungams in https://github.com/rhboot/shim/pull/732 * SBAT Level update for February 2025 GRUB CVEs by @jsetje in https://github.com/rhboot/shim/pull/736 New Contributors * @jjd27 made their first contribution in https://github.com/rhboot/shim/pull/640 * @mikebeaton made their first contribution in https://github.com/rhboot/shim/pull/598 * @bryteise made their first contribution in https://github.com/rhboot/shim/pull/600 * @vittyvk made their first contribution in https://github.com/rhboot/shim/pull/609 * @chrisbainbridge made their first contribution in https://github.com/rhboot/shim/pull/706 * @Metabolix made their first contribution in https://github.com/rhboot/shim/pull/621 * @15058718379 made their first contribution in https://github.com/rhboot/shim/pull/703 * @dbnicholson made their first contribution in https://github.com/rhboot/shim/pull/694 * @lumag made their first contribution in https://github.com/rhboot/shim/pull/673 * @eduardacatrinei made their first contribution in https://github.com/rhboot/shim/pull/690 * @kukrimate made their first contribution in https://github.com/rhboot/shim/pull/657 * @miczyg1 made their first contribution in https://github.com/rhboot/shim/pull/622 * @nathan-omeara made their first contribution in https://github.com/rhboot/shim/pull/666 * @jongwu made their first contribution in https://github.com/rhboot/shim/pull/594 * @rosslagerwall made their first contribution in https://github.com/rhboot/shim/pull/644 * @trungams made their first contribution in https://github.com/rhboot/shim/pull/732 **Full Changelog**: https://github.com/rhboot/shim/compare/15.8...16.0 * tag '16.0': (451 commits) Update version to 16.0 SBAT Level update for February 2025 GRUB CVEs README.tpm: Update MokList entry to MokListRT Make 'make fanalyzer' work again. simple_dir_filter(): test our 'next' pointer shim_load_image(): initialize the buffer fully mirror_mok_db(): Free our mok variable name correctly mirror_one_mok_variable(): fix a memory leak on TPM log error. mirror_mok_db(): get rid of an unused variable+allocation generate_sbat_var_defs: Ensure revlistentry->revocations is initialized. generate_sbat_var_defs: Fix memory leak on realloc failure and fd leak. generate_sbat_var_defs: run clang-format on readfile() SetSecureVariable(): free Cert on failure Update version to 16.0~rc1 make-archive: some minor housekeeping makefiles: Make GITTAG swizzle tildes to dashes fallback: don't add new boot order entries backwards Disable log saving for now. Some save_logs() improvements. reject message with different values in multiple Content-Length header field ...
Diffstat (limited to 'memattrs.c')
-rw-r--r--memattrs.c550
1 files changed, 550 insertions, 0 deletions
diff --git a/memattrs.c b/memattrs.c
new file mode 100644
index 00000000..ed8a3ae2
--- /dev/null
+++ b/memattrs.c
@@ -0,0 +1,550 @@
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+/*
+ * memattrs.c - EFI and DXE memory attribute helpers
+ * Copyright Peter Jones <pjones@redhat.com>
+ */
+
+#include "shim.h"
+
+static inline uint64_t
+shim_mem_attrs_to_uefi_mem_attrs (uint64_t attrs)
+{
+ uint64_t ret = EFI_MEMORY_RP |
+ EFI_MEMORY_RO |
+ EFI_MEMORY_XP;
+
+ if (attrs & MEM_ATTR_R)
+ ret &= ~EFI_MEMORY_RP;
+
+ if (attrs & MEM_ATTR_W)
+ ret &= ~EFI_MEMORY_RO;
+
+ if (attrs & MEM_ATTR_X)
+ ret &= ~EFI_MEMORY_XP;
+
+ return ret;
+}
+
+static inline uint64_t
+uefi_mem_attrs_to_shim_mem_attrs (uint64_t attrs)
+{
+ uint64_t ret = MEM_ATTR_R |
+ MEM_ATTR_W |
+ MEM_ATTR_X;
+
+ if (attrs & EFI_MEMORY_RP)
+ ret &= ~MEM_ATTR_R;
+
+ if (attrs & EFI_MEMORY_RO)
+ ret &= ~MEM_ATTR_W;
+
+ if (attrs & EFI_MEMORY_XP)
+ ret &= ~MEM_ATTR_X;
+
+ return ret;
+}
+
+static void
+get_dxe_services_table(EFI_DXE_SERVICES_TABLE **dstp)
+{
+ static EFI_DXE_SERVICES_TABLE *dst = NULL;
+
+ if (dst == NULL) {
+ dprint(L"Looking for configuration table " LGUID_FMT L"\n", GUID_ARGS(gEfiDxeServicesTableGuid));
+
+ for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) {
+ EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i];
+ dprint(L"Testing configuration table " LGUID_FMT L"\n", GUID_ARGS(ct->VendorGuid));
+ if (CompareMem(&ct->VendorGuid, &gEfiDxeServicesTableGuid, sizeof(EFI_GUID)) != 0)
+ continue;
+
+ dst = (EFI_DXE_SERVICES_TABLE *)ct->VendorTable;
+ dprint(L"Looking for DXE Services Signature 0x%16llx, found signature 0x%16llx\n",
+ EFI_DXE_SERVICES_TABLE_SIGNATURE, dst->Hdr.Signature);
+ if (dst->Hdr.Signature != EFI_DXE_SERVICES_TABLE_SIGNATURE)
+ continue;
+
+ if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes) {
+ /*
+ * purposefully not treating this as an error so that HSIStatus
+ * can tell us about it later.
+ */
+ dprint(L"DXE Services lacks Get/SetMemorySpace* functions\n");
+ }
+
+ dprint(L"Setting dxe_services_table to 0x%llx\n", dst);
+ *dstp = dst;
+ return;
+ }
+ } else {
+ *dstp = dst;
+ return;
+ }
+
+ dst = NULL;
+ dprint(L"Couldn't find DXE services\n");
+}
+
+static EFI_STATUS
+dxe_get_mem_attrs(uintptr_t physaddr, size_t size, uint64_t *attrs)
+{
+ EFI_STATUS status;
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR desc;
+ EFI_PHYSICAL_ADDRESS start, end, next;
+ EFI_DXE_SERVICES_TABLE *dst = NULL;
+
+ get_dxe_services_table(&dst);
+ if (!dst)
+ return EFI_UNSUPPORTED;
+
+ if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes)
+ return EFI_UNSUPPORTED;
+
+ if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) {
+ dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n",
+ __func__, (unsigned long long)physaddr,
+ (unsigned long long)(physaddr+size-1),
+ attrs);
+ return EFI_SUCCESS;
+ }
+
+ start = ALIGN_DOWN(physaddr, EFI_PAGE_SIZE);
+ end = ALIGN_UP(physaddr + size, EFI_PAGE_SIZE);
+
+ for (; start < end; start = next) {
+ status = dst->GetMemorySpaceDescriptor(start, &desc);
+ if (EFI_ERROR(status)) {
+ dprint(L"GetMemorySpaceDescriptor(0x%llx, ...): %r\n",
+ start, status);
+ return status;
+ }
+
+ next = desc.BaseAddress + desc.Length;
+
+ if (desc.GcdMemoryType != EFI_GCD_MEMORY_TYPE_SYSTEM_MEMORY)
+ continue;
+
+ *attrs = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes);
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+static EFI_STATUS
+dxe_update_mem_attrs(uintptr_t addr, size_t size,
+ uint64_t set_attrs, uint64_t clear_attrs)
+{
+#if 0
+ EFI_STATUS status;
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR desc;
+ EFI_PHYSICAL_ADDRESS start, end, next;
+ uint64_t before = 0, after = 0, dxe_set_attrs, dxe_clear_attrs;
+#endif
+ EFI_DXE_SERVICES_TABLE *dst = NULL;
+
+ get_dxe_services_table(&dst);
+ if (!dst)
+ return EFI_UNSUPPORTED;
+
+ if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes)
+ return EFI_UNSUPPORTED;
+
+ if (!IS_PAGE_ALIGNED(addr) || !IS_PAGE_ALIGNED(size) || size == 0) {
+ perror(L"Invalid call %a(addr:0x%llx-0x%llx, size:0x%llx, +%a%a%a, -%a%a%a)\n",
+ __func__, (unsigned long long)addr,
+ (unsigned long long)(addr + size - 1),
+ (unsigned long long)size,
+ (set_attrs & MEM_ATTR_R) ? "r" : "",
+ (set_attrs & MEM_ATTR_W) ? "w" : "",
+ (set_attrs & MEM_ATTR_X) ? "x" : "",
+ (clear_attrs & MEM_ATTR_R) ? "r" : "",
+ (clear_attrs & MEM_ATTR_W) ? "w" : "",
+ (clear_attrs & MEM_ATTR_X) ? "x" : "");
+ if (!IS_PAGE_ALIGNED(addr))
+ perror(L" addr is not page aligned\n");
+ if (!IS_PAGE_ALIGNED(size))
+ perror(L" size is not page aligned\n");
+ if (size == 0)
+ perror(L" size is 0\n");
+ return EFI_SUCCESS;
+ }
+
+ /*
+ * We know this only works coincidentally, so nerfing it for now
+ * until we have a chance to debug more thoroughly on these niche
+ * systems.
+ */
+#if 0
+ start = ALIGN_DOWN(addr, EFI_PAGE_SIZE);
+ end = ALIGN_UP(addr + size, EFI_PAGE_SIZE);
+
+ for (; start < end; start = next) {
+ EFI_PHYSICAL_ADDRESS mod_start;
+ UINT64 mod_size;
+
+ status = dst->GetMemorySpaceDescriptor(start, &desc);
+ if (EFI_ERROR(status)) {
+ dprint(L"GetMemorySpaceDescriptor(0x%llx, ...): %r\n",
+ start, status);
+ return status;
+ }
+
+ next = desc.BaseAddress + desc.Length;
+
+ if (desc.GcdMemoryType != EFI_GCD_MEMORY_TYPE_SYSTEM_MEMORY)
+ continue;
+
+ mod_start = MAX(start, desc.BaseAddress);
+ mod_size = MIN(end, next) - mod_start;
+
+ before = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes);
+ dxe_set_attrs = shim_mem_attrs_to_uefi_mem_attrs(set_attrs);
+ dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, dxe_set_attrs);
+ dxe_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs(clear_attrs);
+ dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, dxe_clear_attrs);
+ desc.Attributes |= dxe_set_attrs;
+ desc.Attributes &= ~dxe_clear_attrs;
+ after = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes);
+
+ status = dst->SetMemorySpaceAttributes(mod_start, mod_size, desc.Attributes);
+ if (EFI_ERROR(status)) {
+ dprint(L"Failed to update memory attrs:0x%0x addr:0x%llx size:0x%0lx status:%r\n",
+ desc.Attributes, mod_start, mod_size, status);
+ return status;
+ }
+
+ break;
+ }
+
+ dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n",
+ (set_attrs & MEM_ATTR_R) ? "r" : "",
+ (set_attrs & MEM_ATTR_W) ? "w" : "",
+ (set_attrs & MEM_ATTR_X) ? "x" : "",
+ (clear_attrs & MEM_ATTR_R) ? "r" : "",
+ (clear_attrs & MEM_ATTR_W) ? "w" : "",
+ (clear_attrs & MEM_ATTR_X) ? "x" : "",
+ (unsigned long long)addr, (unsigned long long)(addr + size - 1),
+ (before & MEM_ATTR_R) ? 'r' : '-',
+ (before & MEM_ATTR_W) ? 'w' : '-',
+ (before & MEM_ATTR_X) ? 'x' : '-',
+ (after & MEM_ATTR_R) ? 'r' : '-',
+ (after & MEM_ATTR_W) ? 'w' : '-',
+ (after & MEM_ATTR_X) ? 'x' : '-');
+#endif
+
+ return EFI_SUCCESS;
+}
+
+static void
+get_efi_mem_attr_protocol(EFI_MEMORY_ATTRIBUTE_PROTOCOL **protop)
+{
+ static EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL;
+ static bool has_mem_access_proto = true;
+
+ if (proto == NULL && has_mem_access_proto == true) {
+ EFI_STATUS efi_status;
+ efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID,
+ (VOID **)&proto);
+ if (EFI_ERROR(efi_status) || !proto) {
+ has_mem_access_proto = false;
+ *protop = NULL;
+ }
+ }
+
+ *protop = proto;
+}
+
+static EFI_STATUS
+efi_get_mem_attrs(uintptr_t addr, size_t size, uint64_t *attrs)
+{
+ EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL;
+ EFI_PHYSICAL_ADDRESS physaddr = addr;
+ EFI_STATUS efi_status;
+
+ get_efi_mem_attr_protocol(&proto);
+ if (!proto)
+ return EFI_UNSUPPORTED;
+
+ if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) {
+ dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n",
+ __func__, (unsigned long long)physaddr,
+ (unsigned long long)(physaddr+size-1),
+ attrs);
+ return EFI_SUCCESS;
+ }
+
+ efi_status = proto->GetMemoryAttributes(proto, physaddr, size, attrs);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"GetMemoryAttributes(..., 0x%llx, 0x%x, 0x%x): %r\n",
+ physaddr, size, attrs, efi_status);
+ } else {
+ *attrs = uefi_mem_attrs_to_shim_mem_attrs (*attrs);
+ }
+
+ return efi_status;
+}
+
+static EFI_STATUS
+efi_update_mem_attrs(uintptr_t addr, uint64_t size,
+ uint64_t set_attrs, uint64_t clear_attrs)
+{
+ EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL;
+ EFI_PHYSICAL_ADDRESS physaddr = addr;
+ EFI_STATUS efi_status, ret;
+ uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs;
+
+ get_efi_mem_attr_protocol(&proto);
+ if (!proto)
+ return EFI_UNSUPPORTED;
+
+ efi_status = efi_get_mem_attrs(addr, size, &before);
+ if (EFI_ERROR(efi_status))
+ dprint(L"efi_get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n",
+ (unsigned long long)addr, (unsigned long long)size,
+ &before, efi_status);
+
+ if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0) {
+ perror(L"Invalid call %a(addr:0x%llx-0x%llx, size:0x%llx, +%a%a%a, -%a%a%a)\n",
+ __func__, (unsigned long long)physaddr,
+ (unsigned long long)(physaddr + size - 1),
+ (unsigned long long)size,
+ (set_attrs & MEM_ATTR_R) ? "r" : "",
+ (set_attrs & MEM_ATTR_W) ? "w" : "",
+ (set_attrs & MEM_ATTR_X) ? "x" : "",
+ (clear_attrs & MEM_ATTR_R) ? "r" : "",
+ (clear_attrs & MEM_ATTR_W) ? "w" : "",
+ (clear_attrs & MEM_ATTR_X) ? "x" : "");
+ if (!IS_PAGE_ALIGNED(physaddr))
+ perror(L" addr is not page aligned\n");
+ if (!IS_PAGE_ALIGNED(size))
+ perror(L" size is not page aligned\n");
+ if (size == 0)
+ perror(L" size is 0\n");
+ return EFI_SUCCESS;
+ }
+
+ uefi_set_attrs = shim_mem_attrs_to_uefi_mem_attrs (set_attrs);
+ dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs);
+ uefi_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs (clear_attrs);
+ dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs);
+ efi_status = EFI_SUCCESS;
+ if (uefi_set_attrs) {
+ efi_status = proto->SetMemoryAttributes(proto, physaddr, size, uefi_set_attrs);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"Failed to set memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n",
+ uefi_set_attrs, physaddr, size, efi_status);
+ }
+ }
+ if (!EFI_ERROR(efi_status) && uefi_clear_attrs) {
+ efi_status = proto->ClearMemoryAttributes(proto, physaddr, size, uefi_clear_attrs);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"Failed to clear memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n",
+ uefi_clear_attrs, physaddr, size, efi_status);
+ }
+ }
+ ret = efi_status;
+
+ efi_status = efi_get_mem_attrs(addr, size, &after);
+ if (EFI_ERROR(efi_status))
+ dprint(L"efi_get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n",
+ (unsigned long long)addr, (unsigned long long)size,
+ &after, efi_status);
+
+ dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n",
+ (set_attrs & MEM_ATTR_R) ? "r" : "",
+ (set_attrs & MEM_ATTR_W) ? "w" : "",
+ (set_attrs & MEM_ATTR_X) ? "x" : "",
+ (clear_attrs & MEM_ATTR_R) ? "r" : "",
+ (clear_attrs & MEM_ATTR_W) ? "w" : "",
+ (clear_attrs & MEM_ATTR_X) ? "x" : "",
+ (unsigned long long)addr, (unsigned long long)(addr + size - 1),
+ (before & MEM_ATTR_R) ? 'r' : '-',
+ (before & MEM_ATTR_W) ? 'w' : '-',
+ (before & MEM_ATTR_X) ? 'x' : '-',
+ (after & MEM_ATTR_R) ? 'r' : '-',
+ (after & MEM_ATTR_W) ? 'w' : '-',
+ (after & MEM_ATTR_X) ? 'x' : '-');
+
+ return ret;
+}
+
+EFI_STATUS
+update_mem_attrs(uintptr_t addr, uint64_t size,
+ uint64_t set_attrs, uint64_t clear_attrs)
+{
+ EFI_STATUS efi_status;
+
+ efi_status = efi_update_mem_attrs(addr, size, set_attrs, clear_attrs);
+ if (!EFI_ERROR(efi_status))
+ return efi_status;
+
+ if (efi_status == EFI_UNSUPPORTED)
+ efi_status = dxe_update_mem_attrs(addr, size, set_attrs, clear_attrs);
+
+ return efi_status;
+}
+
+EFI_STATUS
+get_mem_attrs(uintptr_t addr, size_t size, uint64_t *attrs)
+{
+ EFI_STATUS efi_status;
+
+ efi_status = efi_get_mem_attrs(addr, size, attrs);
+ if (!EFI_ERROR(efi_status))
+ return efi_status;
+
+ if (efi_status == EFI_UNSUPPORTED)
+ efi_status = dxe_get_mem_attrs(addr, size, attrs);
+
+ return efi_status;
+}
+
+char *
+decode_hsi_bits(UINTN hsi)
+{
+ static const struct {
+ UINTN bit;
+ char name[16];
+ } bits[] = {
+ {.bit = SHIM_HSI_STATUS_HEAPX, .name = "HEAPX"},
+ {.bit = SHIM_HSI_STATUS_STACKX, .name = "STACKX"},
+ {.bit = SHIM_HSI_STATUS_ROW, .name = "ROW"},
+ {.bit = SHIM_HSI_STATUS_HASMAP, .name = "HASMAP"},
+ {.bit = SHIM_HSI_STATUS_HASDST, .name = "HASDST"},
+ {.bit = SHIM_HSI_STATUS_HASDSTGMSD, .name = "HASDSTGMSD"},
+ {.bit = SHIM_HSI_STATUS_HASDSTSMSA, .name = "HASDSTSMSA"},
+ {.bit = SHIM_HSI_STATUS_NX, .name = "NX"},
+ {.bit = 0, .name = ""},
+ };
+ static int x = 0;
+ static char retbufs[2][sizeof(bits)];
+ char *retbuf = &retbufs[x % 2][0];
+ char *prev = &retbuf[0];
+ char *pos = &retbuf[0];
+
+ x = ( x + 1 ) % 2;
+
+ ZeroMem(retbuf, sizeof(bits));
+
+ if (hsi == 0) {
+ prev = stpcpy(retbuf, "0");
+ } else {
+ for (UINTN i = 0; bits[i].bit != 0; i++) {
+ if (hsi & bits[i].bit) {
+ prev = stpcpy(pos, bits[i].name);
+ pos = stpcpy(prev, "|");
+ }
+ }
+ }
+ prev[0] = '\0';
+ return retbuf;
+}
+
+void
+get_hsi_mem_info(void)
+{
+ EFI_STATUS efi_status;
+ uintptr_t addr;
+ uint64_t attrs = 0;
+ uint32_t *tmp_alloc;
+ EFI_MEMORY_ATTRIBUTE_PROTOCOL *efiproto = NULL;
+ EFI_DXE_SERVICES_TABLE *dst = NULL;
+
+ get_efi_mem_attr_protocol(&efiproto);
+ if (efiproto) {
+ dprint(L"Setting HSI from %a to %a\n",
+ decode_hsi_bits(hsi_status),
+ decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASMAP));
+ hsi_status |= SHIM_HSI_STATUS_HASMAP;
+ }
+
+ get_dxe_services_table(&dst);
+ if (dst) {
+ dprint(L"Setting HSI from %a to %a\n",
+ decode_hsi_bits(hsi_status),
+ decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDST));
+ hsi_status |= SHIM_HSI_STATUS_HASDST;
+ if (dst->GetMemorySpaceDescriptor) {
+ dprint(L"Setting HSI from %a to %a\n",
+ decode_hsi_bits(hsi_status),
+ decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDSTGMSD));
+ hsi_status |= SHIM_HSI_STATUS_HASDSTGMSD;
+ }
+ if (dst->SetMemorySpaceAttributes) {
+ dprint(L"Setting HSI from %a to %a\n",
+ decode_hsi_bits(hsi_status),
+ decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDSTSMSA));
+ hsi_status |= SHIM_HSI_STATUS_HASDSTSMSA;
+ }
+ }
+
+ if (!(hsi_status & SHIM_HSI_STATUS_HASMAP) &&
+ !(hsi_status & SHIM_HSI_STATUS_HASDSTGMSD &&
+ hsi_status & SHIM_HSI_STATUS_HASDSTSMSA)) {
+ dprint(L"No memory protocol, not testing further\n");
+ return;
+ }
+
+ addr = ((uintptr_t)&get_hsi_mem_info) & ~EFI_PAGE_MASK;
+ efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE);
+ goto error;
+ }
+
+ if (attrs & MEM_ATTR_W) {
+ dprint(L"get_hsi_mem_info() is on a writable page: %a->%a\n",
+ decode_hsi_bits(hsi_status),
+ decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_ROW));
+ hsi_status |= SHIM_HSI_STATUS_ROW;
+ }
+
+ addr = ((uintptr_t)&addr) & ~EFI_PAGE_MASK;
+ efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE);
+ goto error;
+ }
+
+ if (attrs & MEM_ATTR_X) {
+ dprint(L"Stack variable is on an executable page: %a->%a\n",
+ decode_hsi_bits(hsi_status),
+ decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_STACKX));
+ hsi_status |= SHIM_HSI_STATUS_STACKX;
+ }
+
+ tmp_alloc = AllocatePool(EFI_PAGE_SIZE);
+ if (!tmp_alloc) {
+ dprint(L"Failed to allocate heap variable.\n");
+ goto error;
+ }
+
+ addr = ((uintptr_t)tmp_alloc) & ~EFI_PAGE_MASK;
+ efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs);
+ FreePool(tmp_alloc);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE);
+ goto error;
+ }
+ if (attrs & MEM_ATTR_X) {
+ dprint(L"Heap variable is on an executable page: %a->%a\n",
+ decode_hsi_bits(hsi_status),
+ decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HEAPX));
+ hsi_status |= SHIM_HSI_STATUS_HEAPX;
+ }
+
+ return;
+
+error:
+ /*
+ * In this case we can't actually tell anything, so assume
+ * and report the worst case scenario.
+ */
+ hsi_status = SHIM_HSI_STATUS_HEAPX |
+ SHIM_HSI_STATUS_STACKX |
+ SHIM_HSI_STATUS_ROW;
+ dprint(L"Setting HSI to 0x%lx due to error: %r\n", decode_hsi_bits(hsi_status), efi_status);
+}
+
+// vim:fenc=utf-8:tw=75:noet