Skip to content
FreeDOSDeep Dive Published Updated 5 min readViews unavailable

FreeDOS ACPI RSDP Discovery: A Bounded Real-Mode Scan

Locate and checksum-check the ACPI Root System Description Pointer from legacy PC memory without following arbitrary physical addresses from DOS.

The ACPI Root System Description Pointer (RSDP) is a small firmware structure that names the RSDT and, for newer revisions, the XSDT. Finding and validating that pointer is a narrow diagnostic task. It is not the same as validating every ACPI table, discovering usable RAM, or safely mapping arbitrary physical addresses from a DOS application.

This guide is deliberately limited to a native 16-bit real-mode utility on a legacy PC-compatible boot path. It performs read-only byte checks in two specification-defined low-memory windows, reports the RSDP address and root-pointer fields, and stops. It does not dereference the RSDT or XSDT. Do not use the segment-to-physical addressing sketch below from a DPMI client, protected-mode DOS extender, or other environment where a far pointer is not a real-mode address.

The scan windows are fixed, not guessed

The UEFI Forum’s ACPI 6.5 specification source defines IA-PC discovery as a search for the RSDP on 16-byte boundaries in the first 1 KiB of the Extended BIOS Data Area (EBDA) and in BIOS read-only memory from physical 0xE0000 through 0xFFFFF. For EISA or MCA systems, the specification identifies the EBDA segment word at BIOS Data Area address 0040:000E. A UEFI loader follows a different contract: it obtains the pointer from the EFI System Table before handing control to the operating system. A DOS program must not assume that scanning legacy BIOS windows is a substitute for the UEFI handoff path.

Where the BIOS exposes the EBDA segment at that BDA location, read the word as data, convert it to a physical base with segment * 16, and validate it before using it. A conservative scanner should skip the EBDA window if the segment is zero, below conventional memory, or places the full 1 KiB window at or above 0xA0000; do not guess an EBDA location when the pointer is unavailable. The ROM search range is a fixed half-open interval, [0xE0000, 0x100000). Keep the end exclusive so a candidate is never read through the one-megabyte boundary.

The following pseudocode shows the bounds. read8() and read_le16() are read-only helpers implemented using the selected compiler’s native real-mode far-pointer model. They must not be reused in a protected-mode DOS extender without an explicit physical-memory mapping API.

ebda_segment = read_le16(physical 0x40E)
ebda_base = ebda_segment * 16

if ebda_segment != 0 and 0x400 <= ebda_base <= 0xA0000 - 0x400:
    scan_rsdp(ebda_base, ebda_base + 0x400)

scan_rsdp(0xE0000, 0x100000)

The 0x400 window length is the first 1 KiB only. The range test uses a subtraction before forming the end address, so a corrupt segment value cannot wrap a 16-bit calculation into an apparently valid region. In a real-mode implementation, normalize each physical address into a segment and offset, then ensure each read remains within the already-approved window. Do not use a 16-bit segment << 4 expression if it can overflow before being widened.

Validate the complete structure before reporting it

An RSDP candidate begins with the exact eight-byte signature RSD PTR , including the trailing space. The ACPI 1.0-compatible portion is 20 bytes long. Its checksum is the 8-bit sum of bytes 0 through 19, including the checksum byte, and must be zero modulo 256. A signature match without that checksum is not a valid result.

Revision zero ends at byte 19 and contains a 32-bit physical RSDT address at offset 16. For revision 2 or later, offset 20 contains a length, offset 24 contains the 64-bit XSDT address, and offset 32 contains the extended checksum. Before reading those fields, verify that the length field itself is inside the scan window. Then require a length of at least 36 bytes, require the whole declared length to fit in the remaining approved range, and sum exactly that many bytes. Reject unsupported revisions rather than guessing which fields they add. ACPI defines larger revisions as backward compatible, but compatibility does not authorize a DOS scanner to read past its validated memory window.

scan_rsdp(begin, end):
    for candidate = begin; end - candidate >= 20; candidate += 16:
        if read8(candidate .. candidate + 7) != "RSD PTR ": continue
        if sum8(candidate, 20) != 0: continue

        revision = read8(candidate + 15)
        if revision == 0:
            report_validated_rsdp(candidate, revision, read_le32(candidate + 16))
            continue

        if revision < 2 or end - candidate < 24: continue
        length = read_le32(candidate + 20)
        if length < 36 or length > end - candidate: continue
        if sum8(candidate, length) != 0: continue
        report_validated_rsdp(candidate, revision,
                              read_le32(candidate + 16),
                              read_bytes(candidate + 24, 8))

The pseudocode uses end - candidate only after proving that the candidate is inside the half-open range. A real implementation should use a widened integer type for the physical addresses, little-endian readers with explicit byte order, and a checksum accumulator whose behavior is defined modulo 256. The report for an extended RSDP prints the RSDT and XSDT bytes as addresses; it does not turn either value into a pointer.

Stop before following firmware pointers

The RSDP checksum establishes structural consistency for the bytes read. It does not prove that the root table address is mapped into the DOS process, that the table is within RAM, or that the table has a valid signature, length, and checksum. A 64-bit XSDT address is especially unsuitable for direct dereference from a 16-bit DOS program. Even the 32-bit RSDT address can lie above the low-memory region this scanner can inspect.

Keep this first utility diagnostic and non-invasive: print the physical RSDP location, revision, validated length, and root-address values; make no writes, BIOS mode changes, or port I/O. A future table reader would need a separate, explicit mapping and validation design for each table, including overflow-safe length checks and address-range policy. Reusing a low-memory scan helper to follow a firmware-provided pointer would cross the safety boundary this example is designed to enforce.

The result is intentionally modest. It can tell an operator whether a legacy BIOS RSDP was found and whether its checksums passed in a bounded range. It cannot diagnose ACPI namespace behavior, prove that every firmware table is safe, or replace the EFI System Table handoff on a UEFI boot path.

Related:

Sources:

Comments