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
lenbytes of physical memory starting atphys.Returns a virtual address through
out_va. On UEFI this is the same asphys(identity-mapped); on a future Linux backend it is ammapreturn value. Either way the caller dereferences the VA directly withvolatile uint{N}_t *casts to read or write, and pairs every successful map with anaxl_mem_phys_unmapusing the samelen.- 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. NULLvais 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.
physmust 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.
-
int axl_mem_phys_search(const void *va, size_t len, const void *needle, size_t needle_len, const void **out_match)
Find the first occurrence of
needlein 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_matchis set to a pointer insideva), AXL_ERR ifneedleis 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).
-
enumerator AXL_MEM_REGION_RAM
-
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
-
enumerator AXL_IO_REGION_IO
Functions
-
int axl_mem_phys_region_at(uintptr_t phys, AxlMemRegion *out)
Classify the region containing
phys.Fills
outwith the contiguous region that containsphys. 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
outpopulated; AXL_ERR only on a NULLoutor 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_countset); AXL_ERR on NULLout_countor a firmware-query failure.
-
int axl_mem_phys_region_get(size_t index, AxlMemRegion *out)
Fetch the region at
indexin the cached map.indexis 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
outpopulated; AXL_ERR on NULLout,indexout 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
outfilled); AXL_ERR on NULLout.
-
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_RAMfor a “RAM only” safe mode, or clearwritable_typesfor 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, haslen== 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
lenbytes fromphysintobufusingaccess_width-byteaccesses.Issues
len / access_widthnatural-width accesses (eachaxl_mem_phys_read{8,16,32,64}), copying intobufin ascending order.access_width == 1is 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_widthis not one of 1/2/4/8;physis not a multiple ofaccess_width(AArch64 would fault);lenis not a multiple ofaccess_width;len== 0;bufis 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 (>=
lenbytes)access_width – per-access width in bytes: 1, 2, 4, or 8
- Returns:
AXL_OK on success (
buffilled), AXL_ERR otherwise.
-
int axl_mem_phys_write_range(uintptr_t phys, size_t len, const void *buf, uint32_t access_width)
Write
lenbytes frombuftophysusingaccess_width-byteaccesses.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 (>=
lenbytes)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
outpopulated; AXL_ERR on NULLoutor 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_countset); AXL_ERR on NULLout_countor a firmware-query failure.
-
int axl_io_region_get(size_t index, AxlIoRegion *out)
Fetch the I/O range at
indexin the cached map ([0, count)).- Parameters:
index – range index in [0, count)
out – [out] receives the range (non-NULL)
- Returns:
AXL_OK with
outpopulated; AXL_ERR on NULLout,indexout 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_writeis 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
lenports fromportintobufusingaccess_width-byteport reads (1/2/4).Issues
len / access_widthaxl_io_port_read{8,16,32}reads. Refuses (AXL_ERR) a bad width (not 1/2/4), alennot a multiple of it, a NULLbuf, 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 (>=
lenbytes)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
lenports frombuftoport(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 (>=
lenbytes)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.
-
uintptr_t base
-
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.
-
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.
-
uintptr_t base