[SeaBIOS] allocation zone extensions for the firmware linker/loader

Laszlo Ersek lersek at redhat.com
Sat Jun 3 01:20:54 CEST 2017


On 06/02/17 18:30, Michael S. Tsirkin wrote:
> On Fri, Jun 02, 2017 at 05:45:21PM +0200, Laszlo Ersek wrote:
>> Hi,
>>
>> this message is cross-posted to three lists (qemu, seabios, edk2). I'll
>> follow up with three patch series, one series for each project. I'll
>> cross-post all of the patches as well, but I'll add the project name in
>> the "bag of tags" in the subject lines.
>>
>> The QEMU series introduces two extensions to the ALLOCATE firmware
>> linker/loader command.
>>
>> One extension is a new allocation zone, with value 3, for allowing the
>> firmware to allocate the fw_cfg blobs in 64-bit address space.
> 
> Seems to make sense. I guess it's safe to do this if no
> pointers to this table are 32 bit, right?

That's right. For example, the TCPA patch (6 of 7) in the QEMU series
does this, because the ACPI_BUILD_TPMLOG_FILE is only referenced by a
64-bit pointer.

> Is there a chance we'll ever be able to use this on PC
> assuming the need to support 32 bit guests?

Well, sticking with the TCPA example, if an ACPI table defines *only* an
8-byte pointer to some memory area, that seems to preclude support for
32-bit guests already, generally speaking, no?

But otherwise I agree, requiring support for 32-bit guests makes this
allocation zone a lot less useful.

>> The other extension is a repurposing of the most significant bit (bit 7)
>> in the zone field. This bit becomes orthogonal to the rest of the zone
>> field. If the bit is set, it means that QEMU promises the firmware that
>> the blob referenced by the ALLOCATE command contains no ACPI tables at all.
> 
> This one is a bit strange in that it does not seem to be about
> allocations - it seems to be about content.

Sure, I only stuffed it in the Zone field because that's where I found a
free bit :)

> 
> I'd like to better understand what makes ACPI special.
> 
> I see two other things that make acpi special, but I'd like to make sure
> 1. I think that RSDT from qemu is more or less ignored by OVMF, it
>    builds it from tables supplied. Thus pointers from RSDT only serve to
>    find beginning of tables - they are not really patched in. So ACPI
>    tables are special in that their actual addresses are unused. As a
>    result they can be moved at will after linker runs.

Sort of. OVMF performs two passes on the linker/loader script. The first
pass is fully identical to what SeaBIOS does, so the RSDT too is
allocated (as part of its containing fw_cfg blob, of course) and its
fields are patched like anything else.

In the second pass, the (now relocated) pointers are checked again,
based on the ADD_POINTER commands. Wherever the (now relocated) pointers
point, we probe for ACPI table headers. If the target passes the probe
(i.e., it looks like an ACPI table), we call
EFI_ACPI_TABLE_PROTOCOL.InstallAcpiTable() with it. This installs a
separate copy of that table, maintaining RSDT and XSDT internally.

Now, the RSD PTR table has a pointer to the RSDT, so normally we'd
invoke InstallAcpiTable() with the RSDT as well, in the second pass. To
prevent that, we have a quirk that recognizes RSDT and XSDT, and skips them.

So you can say that the RSDT from QEMU is *ultimately* ignored in OVMF,
but for the first pass to complete (which is identical to SeaBIOS's only
pass), OVMF uses the RSDT (as embedded in its containing fw_cfg blob) too.

There are more details about the second pass. If a (relocated) pointer
points to some stuff that doesn't look like an ACPI table header, then
we don't install that thing with InstallAcpiTable(). Instead, we think
that at that location the containing fw_cfg blob contains an
opregion-like area, so its actual absolute address is relevant (remember
we are past the first pass, which completed all the relocations). So in
this case, the containing fw_cfg blob is marked as "non-releasable", and
we'll keep it forever (in AcpiNVS memory). Otherwise, if at the end of
processing a blob is marked as releasable (the default), because all
pointers into it pointed only at ACPI table headers, we free the blob.

There are more details about the second pass. There can be several
pointers that point to the same address (= same offset of the same
pointed-to fw_cfg blob). In such cases, only the first encounter is
honored with an InstallAcpiTable() call, further surfacings of the same
target address are skipped.

Now, the heuristics to determine whether a pointed-to location is an
ACPI table header or not, is not fool-proof. It recognizes all valid
headers (so no false negatives), I think, but it could also
mis-recognize random garbage in an opregion-like blob as an ACPI table
header (so there's a chance for false positives). In order to suppress
these false positives deterministically, there are two methods:

- prefix all such areas in the pointed-to blobs with a sufficient number
of zeroes so that the probe would fail, guaranteed. However, this means
that you have to keep the ADD_POINTER commands pointed at the zeroes,
not at the real opregion, so you need to add the offset in the AML code
at runtime, to actually access the opregion.

- the other method is the "no ACPI content" hint, proposed here. In this
case whenever an ADD_POINTER command is found, in the 2nd pass, to point
into a blob that had this "no ACPI content" hint at ALLOCATE time, the
probing is skipped immediately.

> 2. Some tables can go into AddressRangeACPI, some into AddressRangeNVS,
>    but they never need to be in AddressRangeReserved.
>    It would be simple if we could just say "allocate this table
>    from AddressRangeACPI". But I am not sure this is true
>    since e.g. stuff used for power management can't go into
>    AddressRangeACPI.  Thoughts?

For the first pass, OVMF allocates all the blobs in AcpiNVS memory. Most
of these blobs are ultimately released (because they only contain ACPI
tables, of which EFI_ACPI_TABLE_PROTOCOL.InstallAcpiTable() creates deep
copies). The blobs that are marked as non-releasable (see above) are
preserved in AcpiNVS memory.

Regarding the tables that EFI_ACPI_TABLE_PROTOCOL.InstallAcpiTable()
installs -- this protocol member function has internal knowledge about
what table should be allocated in what kind of memory. It cannot be
influenced from the outside. Years ago I proposed and
EFI_ACPI_TABLE_PROTOCOL2 on the USWG list, in which the
InstallAcpiTable() member would additionally take the memory type to
place the copied / installed table into. I received zero feedback.

Thanks
Laszlo

>> After introducing these, the QEMU series puts them to use, covering all
>> of the currently generated ALLOCATE commands, as appropriate. Among the
>> benefits we can mention
>> - the removal of the OVMF ACPI SDT Header Probe suppressor from VMGENID
>> (and from any similar future devices),
>> - and the fact that the "virt" machine type (and maybe other machine
>> types) of the arm/aarch64 target will no longer require RAM under 4GB
>> for ACPI to work.
>>
>> Both of these extensions are irrelevant for SeaBIOS, therefore the
>> SeaBIOS patches simply mask out bit 7 (for ignoring the "no ACPI
>> content" hint), and fall back to the HIGH zone (= 32-bit address space)
>> when the 64-bit zone is permitted.
>>
>> In other words, SeaBIOS needs some patches to recognize the new zone
>> values, but beyond that, the behavior is unchanged.
>>
>> Both extensions are important for virtual UEFI firmware (OVMF in x86
>> guests and ArmVirtQemu in aarch64 guests). The edk2 patches add support
>> to OvmfPkg/AcpiPlatformDxe for the extensions. Please see the commit
>> messages for details (all the extensions are explained in detail in the
>> relevant patches for all of the projects).
>>
>> The patches can cause linker/loader breakage when old firmware is booted
>> on new QEMU. However, that's no problem (it's nothing new), the next
>> release of QEMU should bundle the new firmware binaries as always.
>>
>> New firmware will continue running on old QEMU without issues.
>>
>> (In case you have sent me emails about this in the last few tens of
>> hours, please know that I'm not ignoring them, I just haven't seen /
>> read them. Reading emails every five minutes makes focused work
>> impossible, so when I'm busy, I tend to read email once per day.)
>>
>> Thanks
>> Laszlo




More information about the SeaBIOS mailing list