AxlMemPhys — physical-memory access

Held _map/_unmap mappings plus one-shot _read{8,16,32,64} / _write{8,16,32,64} helpers and a byte-pattern _search. UEFI is identity-mapped so map is effectively a no-op; the abstraction is preserved for portability to backends where physical access requires explicit mapping (Linux mmap("/dev/mem"), AXL kernel POC explicit page-table mapping).

API Reference

Physical-memory access — map / unmap and one-shot read / write.

UEFI runs with the physical memory map identity-mapped, so the map step is effectively a no-op there: the returned VA equals the physical address. The map / unmap shape exists for portability — a future Linux backend would mmap("/dev/mem") on the way in and munmap on the way out, and consumer code that holds a mapping over multiple accesses keeps working.

For the common case where a tool reads (or writes) a single address one time, the one-shot helpers do the map / access / unmap trio in one call. They keep the typical *(volatile uint32_t *)0xFEE00000 style readable without forcing every call site to manage a mapping.

// Held mapping over multiple accesses.
void *va;
if (axl_mem_phys_map(0xFED00000, 4096, &va) == AXL_OK) {
    for (size_t i = 0; i < 4096; i += 4) {
        uint32_t w = *(volatile uint32_t *)((uint8_t *)va + i);
        // ...
    }
    axl_mem_phys_unmap(va, 4096);
}

// One-shot.
uint32_t signature;
axl_mem_phys_read32(0xE0000, &signature);

Functions

int axl_mem_phys_map(uintptr_t phys, size_t len, void **out_va)

Map len bytes of physical memory starting at phys.

Returns a virtual address through out_va. On UEFI this is the same as phys (identity-mapped); on a future Linux backend it is a mmap return value. Either way the caller dereferences the VA directly with volatile uint{N}_t * casts to read or write, and pairs every successful map with an axl_mem_phys_unmap using the same len.

Parameters:
  • phys – physical address (alignment is consumer’s responsibility)

  • len – number of bytes to map

  • out_va – [out] receives the mapped VA

Returns:

AXL_OK on success, AXL_ERR if the mapping cannot be established.

void axl_mem_phys_unmap(void *va, size_t len)

Release a mapping established by axl_mem_phys_map.

On UEFI this is a no-op; on a future Linux backend it is munmap. NULL va is tolerated (no-op).

Parameters:
  • va – VA returned by axl_mem_phys_map

  • len – same length passed to map

int axl_mem_phys_read8(uintptr_t phys, uint8_t *out)

Read a byte from physical memory.

Maps the smallest aligned region covering phys, dereferences, and unmaps. On UEFI all three steps are direct; on backends that actually need to map, this is one mmap + read + munmap per call, so the held-mapping API above is the better choice for hot loops.

Returns:

AXL_OK on success, AXL_ERR if the mapping cannot be established.

int axl_mem_phys_read16(uintptr_t phys, uint16_t *out)

16-bit variant. phys must be 16-bit aligned on AArch64 (a misaligned access raises a synchronous Data Abort and terminates the image); x86 tolerates misaligned reads at a performance penalty.

int axl_mem_phys_read32(uintptr_t phys, uint32_t *out)

32-bit variant. Same alignment caveat as axl_mem_phys_read16 — required on AArch64, advisory on x86.

int axl_mem_phys_read64(uintptr_t phys, uint64_t *out)

64-bit variant. Same alignment caveat as axl_mem_phys_read16 — required on AArch64, advisory on x86.

int axl_mem_phys_write8(uintptr_t phys, uint8_t value)

One-shot byte write.

int axl_mem_phys_write16(uintptr_t phys, uint16_t value)

One-shot 16-bit write.

int axl_mem_phys_write32(uintptr_t phys, uint32_t value)

One-shot 32-bit write.

int axl_mem_phys_write64(uintptr_t phys, uint64_t value)

One-shot 64-bit write.

Find the first occurrence of needle in a mapped region.

Operates on a VA (typically returned by axl_mem_phys_map). Useful for scanning ROMs, firmware tables, or signature blocks. Linear byte-by-byte scan; use sparingly on multi-megabyte regions.

Parameters:
  • va – mapped region base

  • len – region length in bytes

  • needle – pattern to find

  • needle_len – pattern length in bytes

  • out_match – [out] pointer inside va

Returns:

AXL_OK on hit (and *out_match is set to a pointer inside va), AXL_ERR if needle is not present.

AxlMemRegion — region map + fault-safe access

axl-mem-region.h layers a safe, navigable view over the raw axl-mem-phys primitives, for a tool that lets a user type an arbitrary physical address. It classifies the physical address space into typed regions (RAM / reserved / ACPI / MMIO / unmapped) by overlaying the UEFI memory map with the PI GCD memory-space map (so MMIO the EFI map omits — PCI BARs — is classified), gates every access behind axl_mem_phys_is_accessible (best-effort, since there is no recoverable pre-boot fault handler), and offers width- and alignment-aware _read_range / _write_range so a misaligned or unmapped address returns an error instead of faulting. An AxlMemAccessPolicy permits every mapped type by default and can be tightened (RAM-only, read-only) as an opt-in guard.

Physical-memory region map + fault-safe range access.

<axl/axl-mem-phys.h> provides the raw physical access primitives (map / one-shot read & write). They are fast and unguarded: reading an unmapped or misaligned address can raise a synchronous fault and terminate the image. This header is the layer that makes arbitrary- address access safe and navigable — what a memory / hex editor needs when a user can type any address:

  • Classify the physical address space into typed regions (AxlMemRegion: RAM / reserved / ACPI / MMIO / unmapped), sourced from the UEFI memory map merged with the PI GCD memory-space map (so MMIO ranges the EFI map omits — e.g. PCI BARs — are classified).

  • Gate every access behind axl_mem_phys_is_accessible

    , which refuses anything not entirely backed by a mapped region. This is a best-effort guard, not a true fault handler (see the caveat on that function) — but it catches the common “typed an address in a huge

    unmapped gap” case that would otherwise fault.

  • Bulk width- and alignment-aware read_range / write_range, centralizing the AArch64 natural-alignment rule so the consumer never hand-rolls a faulting access.

Region data is cached on first query and re-walked by axl_mem_phys_region_refresh. UEFI is single-process / single-threaded (BSP), so the cache and the access policy are plain process-global state.

Defines

AXL_MEM_ACCESS_ALL_MAPPED

All mapped region types (everything except UNMAPPED) — the default mask.

Enums

enum AxlMemRegionType

How a physical region is classified. The bit (1u << type) is used in the access-policy masks (see AxlMemAccessPolicy).

Values:

enumerator AXL_MEM_REGION_RAM

Usable system RAM (EfiConventionalMemory / Loader* / BootServices*; GCD SystemMemory).

enumerator AXL_MEM_REGION_RESERVED

Reserved / runtime / GCD Reserved / Persistent — mapped but not general-use RAM.

enumerator AXL_MEM_REGION_ACPI

ACPI reclaim memory or ACPI NVS.

enumerator AXL_MEM_REGION_MMIO

Memory-mapped I/O (EfiMemoryMappedIO[PortSpace]

  • GCD MemoryMappedIo). Access may have side effects; byte width is often wrong.

enumerator AXL_MEM_REGION_UNMAPPED

Not described by any map (GCD NonExistent / a gap). Touching it would fault — never accessible regardless of policy.

enumerator AXL_MEM_REGION_TYPE_COUNT

Number of region types (for mask sizing).

enum AxlIoRegionType

Classification of an I/O port range.

Values:

enumerator AXL_IO_REGION_IO

usable I/O port range (GCD Io).

enumerator AXL_IO_REGION_RESERVED

reserved I/O range (GCD Reserved).

enumerator AXL_IO_REGION_UNMAPPED

non-existent / not described (GCD NonExistent).

enumerator AXL_IO_REGION_TYPE_COUNT

Functions

int axl_mem_phys_region_at(uintptr_t phys, AxlMemRegion *out)

Classify the region containing phys.

Fills out with the contiguous region that contains phys. An address in a gap not described by any map is reported as AXL_MEM_REGION_UNMAPPED with best-effort bounds (the gap between the surrounding described regions), so a caller can always render “you are at 0x… in an UNMAPPED region” rather than getting an error. The region map is built on first use and cached; call axl_mem_phys_region_refresh to rebuild it.

Parameters:
  • phys – physical address to classify

  • out – [out] receives the containing region (non-NULL)

Returns:

AXL_OK with out populated; AXL_ERR only on a NULL out or if the region map could not be built (firmware query failed).

int axl_mem_phys_region_count(size_t *out_count)

Number of regions in the (cached) physical region map.

Regions are ordered by ascending base, non-overlapping, and adjacent same-type regions are coalesced. Pair with axl_mem_phys_region_get to enumerate (a region-picker UI / “jump to next region”). Built on first use.

Parameters:
  • out_count – [out] number of regions (non-NULL)

Returns:

AXL_OK (and out_count set); AXL_ERR on NULL out_count or a firmware-query failure.

int axl_mem_phys_region_get(size_t index, AxlMemRegion *out)

Fetch the region at index in the cached map.

index is in [0, count) per axl_mem_phys_region_count.

Parameters:
  • index – region index in [0, count)

  • out – [out] receives the region (non-NULL)

Returns:

AXL_OK with out populated; AXL_ERR on NULL out, index out of range, or a firmware-query failure.

int axl_mem_phys_region_refresh(void)

Rebuild the cached physical region map from current firmware state.

The map is otherwise built once on first query and cached. Call this to pick up changes (e.g. after driver enumeration alters the GCD MMIO map). Cheap to call; the editor may refresh on demand.

Returns:

AXL_OK on success, AXL_ERR if the firmware query failed (the cache is left marked stale and is rebuilt on the next query).

int axl_mem_phys_get_policy(AxlMemAccessPolicy *out)

Read the current access policy.

Parameters:
  • out – [out] current policy (non-NULL)

Returns:

AXL_OK (and out filled); AXL_ERR on NULL out.

int axl_mem_phys_set_policy(const AxlMemAccessPolicy *policy)

Replace the access policy used by is_accessible / read_range / write_range.

Use this to restrict access — e.g. set both masks to 1u << AXL_MEM_REGION_RAM for a “RAM only” safe mode, or clear writable_types for read-only. Passing NULL restores the permissive default (AXL_MEM_ACCESS_ALL_MAPPED for both).

Parameters:
  • policy – new policy; NULL = restore default

Returns:

AXL_OK.

bool axl_mem_phys_is_accessible(uintptr_t phys, size_t len, bool want_write)

Whether [phys, phys+len) is safe to access under the current policy.

Returns true iff the entire span is covered by mapped regions whose type is permitted by the current policy (writable_types when want_write, else readable_types). A span that touches an AXL_MEM_REGION_UNMAPPED region, crosses out of the permitted set, has len == 0, or overflows the address space returns false. Call this before every read / write so an arbitrary user-typed address cannot terminate the image.

Caveat — best-effort, not a fault handler. Pre-boot there is no recoverable fault handler, so this gates through the region map only. It cannot guarantee no fault: firmware may mark a page within a “RAM” region as protected, and reads of AXL_MEM_REGION_MMIO ranges may have side effects on the device. It reduces, but does not eliminate, the risk.

Parameters:
  • phys – start physical address

  • len – span length in bytes

  • want_write – true to check write access, false for read

Returns:

true if the span is accessible under the current policy, false otherwise.

int axl_mem_phys_read_range(uintptr_t phys, size_t len, void *buf, uint32_t access_width)

Read len bytes from phys into buf using access_width-byte accesses.

Issues len / access_width natural-width accesses (each axl_mem_phys_read{8,16,32,64}), copying into buf in ascending order. access_width == 1 is the plain byte read for RAM; 4 is the common width for MMIO registers (where byte access is often wrong or has side effects). Centralizes the AArch64 alignment rule so the consumer never issues a faulting misaligned access.

Refuses (AXL_ERR, no access performed) if: access_width is not one of 1/2/4/8; phys is not a multiple of access_width (AArch64 would fault); len is not a multiple of access_width; len == 0; buf is NULL; or the span is not accessible for read per axl_mem_phys_is_accessible.

Parameters:
  • phys – source physical address (multiple of access_width)

  • len – bytes to read (multiple of access_width, > 0)

  • buf – [out] destination buffer (>= len bytes)

  • access_width – per-access width in bytes: 1, 2, 4, or 8

Returns:

AXL_OK on success (buf filled), AXL_ERR otherwise.

int axl_mem_phys_write_range(uintptr_t phys, size_t len, const void *buf, uint32_t access_width)

Write len bytes from buf to phys using access_width-byte accesses.

Write counterpart to axl_mem_phys_read_range, with the same width / alignment / length rules, gated on write accessibility (axl_mem_phys_is_accessible with want_write = true). Issues axl_mem_phys_write{8,16,32,64} in ascending order.

Parameters:
  • phys – destination physical address (multiple of access_width)

  • len – bytes to write (multiple of access_width, > 0)

  • buf – source buffer (>= len bytes)

  • access_width – per-access width in bytes: 1, 2, 4, or 8

Returns:

AXL_OK on success, AXL_ERR on a bad argument or an inaccessible span.

int axl_io_region_at(uintptr_t port, AxlIoRegion *out)

Classify the I/O range containing port port.

I/O-space analogue of axl_mem_phys_region_at — a port not described by the GCD I/O map is reported as AXL_IO_REGION_UNMAPPED with best-effort bounds. Built on first use and cached; axl_io_region_refresh rebuilds it.

Parameters:
  • port – I/O port address to classify

  • out – [out] receives the containing range (non-NULL)

Returns:

AXL_OK with out populated; AXL_ERR on NULL out or a firmware-query failure.

int axl_io_region_count(size_t *out_count)

Number of ranges in the (cached) I/O region map.

Parameters:
  • out_count – [out] number of ranges (non-NULL)

Returns:

AXL_OK (and out_count set); AXL_ERR on NULL out_count or a firmware-query failure.

int axl_io_region_get(size_t index, AxlIoRegion *out)

Fetch the I/O range at index in the cached map ([0, count)).

Parameters:
  • index – range index in [0, count)

  • out – [out] receives the range (non-NULL)

Returns:

AXL_OK with out populated; AXL_ERR on NULL out, index out of range, or a firmware-query failure.

int axl_io_region_refresh(void)

Rebuild the cached I/O region map from current firmware state.

Returns:

AXL_OK on success, AXL_ERR if the firmware query failed (the cache is left marked stale and rebuilt on the next query).

bool axl_io_is_accessible(uintptr_t port, size_t len, bool want_write)

Whether the I/O range [port, port+len) is safe to access.

True iff the whole range is classified AXL_IO_REGION_IO. want_write is accepted for symmetry with the memory gate; the I/O policy is the same for read and write. Note an I/O read may still have device side effects.

Parameters:
  • port – start I/O port

  • len – number of ports

  • want_write – accepted for symmetry; same policy as read

Returns:

true if the range is entirely usable I/O space, false otherwise.

int axl_io_read_range(uintptr_t port, size_t len, void *buf, uint32_t access_width)

Read len ports from port into buf using access_width-byte port reads (1/2/4).

Issues len / access_width axl_io_port_read{8,16,32} reads. Refuses (AXL_ERR) a bad width (not 1/2/4), a len not a multiple of it, a NULL buf, a range not accessible per axl_io_is_accessible, a span beyond the 64 KiB port space, or any access on a non-x86 build (no port I/O). Unlike memory, I/O ports have no alignment requirement.

Parameters:
  • port – source I/O port

  • len – ports to read (multiple of access_width, > 0)

  • buf – [out] destination buffer (>= len bytes)

  • access_width – per-access width: 1, 2, or 4

Returns:

AXL_OK on success, AXL_ERR otherwise.

int axl_io_write_range(uintptr_t port, size_t len, const void *buf, uint32_t access_width)

Write len ports from buf to port (1/2/4-byte port writes).

Write counterpart to axl_io_read_range, same width / length / range / arch rules, issuing axl_io_port_write{8,16,32}.

Parameters:
  • port – destination I/O port

  • len – ports to write (multiple of access_width, > 0)

  • buf – source buffer (>= len bytes)

  • access_width – per-access width: 1, 2, or 4

Returns:

AXL_OK on success, AXL_ERR on a bad argument, inaccessible range, or a non-x86 build.

struct AxlMemRegion
#include <axl-mem-region.h>

A classified, contiguous physical region.

Public Members

uintptr_t base

Region start (physical address).

uint64_t len

Region length in bytes (> 0).

AxlMemRegionType type

Classification.

uint64_t attr

Firmware-reported EFI_MEMORY_* attribute bits (UC/WC/WT/WB/RO/…); 0 when not applicable (e.g. UNMAPPED) or unknown. Informational.

struct AxlMemAccessPolicy
#include <axl-mem-region.h>

Which region types may be read / written, as bitmasks of (1u << type). The default policy (see axl_mem_phys_get_policy) permits every mapped type (RAM | RESERVED | ACPI | MMIO) for both read and write — least limiting. Tighten it (e.g. RAM-only, or read-only) with axl_mem_phys_set_policy. AXL_MEM_REGION_UNMAPPED is never accessible regardless of the masks (it would fault); the policy only gates among mapped types.

Public Members

uint32_t readable_types

bits (1u << AxlMemRegionType) allowed for read

uint32_t writable_types

bits allowed for write

struct AxlIoRegion
#include <axl-mem-region.h>

A classified, contiguous I/O port range.

Public Members

uintptr_t base

range start (I/O port address).

uint64_t len

length in ports (> 0).

AxlIoRegionType type

classification.