summaryrefslogtreecommitdiff
path: root/memattrs.c
diff options
context:
space:
mode:
authorPeter Jones <pjones@redhat.com>2024-06-27 15:19:38 -0400
committerPeter Jones <pjones@redhat.com>2025-02-24 15:58:16 -0500
commitc41b1f066b9f279b70d933095f277eddbd7c6433 (patch)
treea00429147551e01583d9128ff76ed7f536a8d824 /memattrs.c
parenteeda3fab069a194fe812e4e4065f4138d4017ba4 (diff)
downloadefi-boot-shim-c41b1f066b9f279b70d933095f277eddbd7c6433.tar.gz
efi-boot-shim-c41b1f066b9f279b70d933095f277eddbd7c6433.zip
Add support for DXE memory attribute updates.
This adds DXE implementations of get_mem_attrs() and update_mem_attrs() for machines that implement DXE but don't yet have the EFI_MEMORY_ATTRIBUTE_PROTOCOL. Signed-off-by: Peter Jones <pjones@redhat.com>
Diffstat (limited to 'memattrs.c')
-rw-r--r--memattrs.c318
1 files changed, 280 insertions, 38 deletions
diff --git a/memattrs.c b/memattrs.c
index 988459c3..a2c1777c 100644
--- a/memattrs.c
+++ b/memattrs.c
@@ -44,21 +44,227 @@ uefi_mem_attrs_to_shim_mem_attrs (uint64_t attrs)
return ret;
}
-EFI_STATUS
-get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs)
+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;
- efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID,
- (VOID **)&proto);
- if (EFI_ERROR(efi_status) || !proto) {
- dprint(L"No memory attribute protocol found: %r\n", efi_status);
- if (!EFI_ERROR(efi_status))
- efi_status = EFI_UNSUPPORTED;
- return 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",
@@ -79,23 +285,22 @@ get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs)
return efi_status;
}
-EFI_STATUS
-update_mem_attrs(uintptr_t addr, uint64_t size,
- uint64_t set_attrs, uint64_t clear_attrs)
+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;
- efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID,
- (VOID **)&proto);
- if (EFI_ERROR(efi_status) || !proto)
- return efi_status;
+ get_efi_mem_attr_protocol(&proto);
+ if (!proto)
+ return EFI_UNSUPPORTED;
- efi_status = get_mem_attrs (addr, size, &before);
+ efi_status = efi_get_mem_attrs(addr, size, &before);
if (EFI_ERROR(efi_status))
- dprint(L"get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n",
+ 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);
@@ -116,7 +321,7 @@ update_mem_attrs(uintptr_t addr, uint64_t size,
perror(L" size is not page aligned\n");
if (size == 0)
perror(L" size is 0\n");
- return 0;
+ return EFI_SUCCESS;
}
uefi_set_attrs = shim_mem_attrs_to_uefi_mem_attrs (set_attrs);
@@ -140,9 +345,9 @@ update_mem_attrs(uintptr_t addr, uint64_t size,
}
ret = efi_status;
- efi_status = get_mem_attrs (addr, size, &after);
+ efi_status = efi_get_mem_attrs(addr, size, &after);
if (EFI_ERROR(efi_status))
- dprint(L"get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n",
+ 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);
@@ -164,6 +369,37 @@ update_mem_attrs(uintptr_t addr, uint64_t size,
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;
+}
+
void
get_hsi_mem_info(void)
{
@@ -171,22 +407,10 @@ get_hsi_mem_info(void)
uintptr_t addr;
uint64_t attrs = 0;
uint32_t *tmp_alloc;
+ EFI_MEMORY_ATTRIBUTE_PROTOCOL *efiproto = NULL;
- 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)) {
-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", hsi_status, efi_status);
- return;
- } else {
+ get_efi_mem_attr_protocol(&efiproto);
+ if (efiproto) {
hsi_status = SHIM_HSI_STATUS_HASMAP;
dprint(L"Setting HSI to 0x%lx\n", hsi_status);
}
@@ -196,7 +420,13 @@ error:
return;
}
- hsi_status = SHIM_HSI_STATUS_HASMAP;
+ 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: 0x%x->0x%x\n",
hsi_status, hsi_status | SHIM_HSI_STATUS_ROW);
@@ -234,6 +464,18 @@ error:
hsi_status, 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", hsi_status, efi_status);
}
// vim:fenc=utf-8:tw=75:noet