On 09/05/14 17:42, Jordan Justen wrote:
> On Fri, Sep 5, 2014 at 2:03 AM, Laszlo Ersek <ler...@redhat.com> wrote:
>> On 09/05/14 10:52, Laszlo Ersek wrote:
>>> On 08/26/14 02:12, Jordan Justen wrote:
>>>> On Mon, Aug 25, 2014 at 4:27 PM, Laszlo Ersek <ler...@redhat.com> wrote:
>>>>> On 08/26/14 00:24, Jordan Justen wrote:
>>>>>> On Fri, Aug 8, 2014 at 7:08 PM, Laszlo Ersek <ler...@redhat.com> wrote:
>>>>>>> Recent changes in the QEMU ACPI table generator have shown that our
>>>>>>> limited client for that interface is insufficient and/or brittle.
>>>>>>>
>>>>>>> Implement the full interface utilizing OrderedCollectionLib for 
>>>>>>> addressing
>>>>>>> fw_cfg blobs by name.
>>>>>>>
>>>>>>> In addition, we stop using EFI_ACPI_TABLE_PROTOCOL in the QEMU loader
>>>>>>> client, because:
>>>>>>> - splitting the blobs for EFI_ACPI_TABLE_PROTOCOL is just untenable 
>>>>>>> short
>>>>>>>   of a complete ACPI parser,
>>>>>>> - and it is fully sufficient to install the RSD PTR as an EFI
>>>>>>>   configuration table for the guest OS to see everything.
>>>>>>>
>>>>>>> In short, for this interface, EFI_ACPI_TABLE_PROTOCOL is an unwanted and
>>>>>>> restrictive convenience; let's stop using it.
>>>>>>
>>>>>> Hmm. Been through a few rounds of this for Xen. It's QEMU's turn now? :)
>>>>>>
>>>>>> Is this required?
>>>>>
>>>>> Depends on how good (how accurate) ACPI tables you want to have in your
>>>>> VMs. For a few qemu releases now, qemu has been the "master" of ACPI
>>>>> payload generation. SeaBIOS too has kept its own builtin tables, yes,
>>>>> but when the QEMU generated payload is available, it interprets this
>>>>> linker/loader script just the same.
>>>>>
>>>>> If you want PCI hotplug, pvpanic support; or, going forward, memory
>>>>> hotplug, then yes, those things need ACPI support, and the ACPI stuff
>>>>> for them is now developed in QEMU (and dynamically generated by it,
>>>>> dependent on the actual hardware config).
>>>>>
>>>>>> What happens if another driver uses EFI_ACPI_TABLE_PROTOCOL? We now
>>>>>> have two drivers that want to install the pointer.
>>>>>
>>>>> Yes, I checked that. The UEFI spec says that whenever you install a
>>>>> configuration table that's already present (by GUID), the reference is
>>>>> updated.
>>>>>
>>>>>   InstallConfigurationTable()
>>>>>
>>>>>   [...]
>>>>>   * If Guid is present in the System Table, and Table is not NULL, then
>>>>>     the (Guid, Table) pair is updated with the new Table value.
>>>>>   [...]
>>>>>
>>>>> For this reason, the first thing InstallAllQemuLinkedTables() does is
>>>>> check, with EfiGetSystemConfigurationTable(), for the presence of any of
>>>>> the ACPI GUIDs in the config table.
>>>>>
>>>>> There's no "absolute" cure against another driver in OVMF just grabbing
>>>>> EFI_ACPI_TABLE_PROTOCOL and installing its own stuff (which would then
>>>>> completely hide the tables installed by this patchset, because
>>>>> "MdeModulePkg/Universal/Acpi/AcpiTableDxe" would simply replace QEMU's
>>>>> RSD PTR's address in the UEFI config table with its own).
>>>>>
>>>>> But ACPI tables are not randomly installed in OVMF, we keep it
>>>>> centralized in AcpiTableDxe.
>>>>
>>>> I don't agree with this statement. Rather, I would say that OVMF
>>>> follows the EDK II method of publishing tables, which means
>>>> EFI_ACPI_TABLE_PROTOCOL.
>>>>
>>>> In the past we were a good little sample platform, and provided the
>>>> ACPI tables directly. Well, that is less and less the case. But, is it
>>>> a good idea to stop using EFI_ACPI_TABLE_PROTOCOL?
>>>>
>>>> What about MdeModulePkg/Universal/Network/IScsiDxe/IScsiIbft.c, which
>>>> uses EFI_ACPI_TABLE_PROTOCOL?
>>>>
>>>> Do we need to keep monitoring which EDK II drivers decide to use
>>>> EFI_ACPI_TABLE_PROTOCOL, or can we find a way to make these work
>>>> together? If the answer is no, then I wonder if this is something that
>>>> UEFI or EDK II needs to consider.
>>>
>>> I think I might have found a solution -- at this point it's just an
>>> idea, but I want to run it by you guys first before I start implementing
>>> it. The idea is based on this patchset, in an incremental fashion.
>>>
>>> Where the current code calls gBS->InstallConfigurationTable(), we shall
>>> replace that call with further processing.
>>>
>>> Michael has proposed before to check the *targets* of all the pointer
>>> commands. Namely, a pointed-to "something" in the blob that hosts it is
>>> with a good chance an ACPI table. Not guaranteed, but likely.
>>>
>>> Now, after all is done by the current patchset *except* linking the RSDP
>>> into the UEFI config table, the payload we have in AcpiNVS memory is a
>>> nicely cross-linked, checksummed forest of ACPI stuff. We re-scan the
>>> QEMU commands. We ignore everything different from AddPointer. When an
>>> AddPointer call is found, the following statements are true:
>>>
>>> (a) the pointer field in the blob that the AddPointer call would
>>> originally patch *now* contains a valid, absolute physical address.
>>>
>>> (b) the pointed-to byte-array may or may not be an ACPI table. It could
>>> be an ACPI table, or it could be some funny area that QEMU has
>>> preallocated, like the target of the TCPA.LASA field, or the target of
>>> the BGRT.ImageAddress field, or something else.
>>>
>>> (c) *if* the pointed-to byte-array is an ACPI table, then we'll have it
>>> already checksummed, since we're past the first complete pass of the
>>> processing.
>>>
>>> So the idea is, look at the target area,
>>> - determine if the remaining size in that blob (the pointed-to blob)
>>>   could still contain an ACPI table header,
>>> - if so, check the presumed "length" field in that header, and see if
>>>   it's self-consistent (ie. >= sizeof(header), and <= remaining size in
>>>   blob)
>>> - if so, run a checksum on the bytes that presumably constitute the
>>>   ACPI table
>>> - if it sums to zero, we attempt to install the target area with
>>>   EFI_ACPI_TABLE_PROTOCOL, otherwise we leave it alone.
>>>
>>> Now, EFI_ACPI_TABLE_PROTOCOL makes deep copies of the tables it gets,
>>> but we'll leave the entire forest that we've build during the first
>>> processing in place. Why? Because this way all the stuff that *didn't*
>>> look like an ACPI table during the procedure above will remain in place,
>>> and the pointers *to* them will remain valid, in those ACPI table copies
>>> that EFI_ACPI_TABLE_PROTOCOL creates.
>>>
>>> For example, consider a BGRT table, where QEMU places both the BGRT in
>>> the blob, and the image data right after it (and sets BGRT.ImageAddress
>>> to the relative address inside the blob where the image data starts).
>>> The above procedure will result in:
>>>
>>> - the BGRT table itself being allocated twice, once by our first pass,
>>> and once by EFI_ACPI_TABLE_PROTOCOL itself (from the second pass). The
>>> first instance will be leaked (it won't be reachable from the system
>>> table that EFI_ACPI_TABLE_PROTOCOL now manages), but it's not grave.
>>>
>>> - the BGRT image data will be allocated only once, from our first pass,
>>> and it will be referenced *both* from our first-pass BGRT table (which
>>> is irrelevant) *and* from the BGRT copy that EFI_ACPI_TABLE_PROTOCOL
>>> installs (which is relevant).
>>>
>>> This approach would leak a few pages of AcpiNVS memory, and it has a
>>> slight chance to call EFI_ACPI_TABLE_PROTOCOL.InstallTable() with
>>> garbage (that has a valid-looking Length field and checksums to zero).
>>> But it would remain compatible with EFI_ACPI_TABLE_PROTOCOL, and it
>>> wouldn't miss anything from QEMU's payload that in fact *is* an ACPI table.
>>
>> In addition, I could tighten the Length + checksum validation with
>> ACPI_BUILD_APPNAME6 and ACPI_BUILD_APPNAME4 checks, according to qemu's
>> build_header() function -- if Michael agrees that these are stable. IOW,
>> the OEMID would have to be "BOCHS ", and the first four bytes of
>> OEMTableID would have to be "BXPC". I think these four checks together
>> are pretty strong: a static check for a *10-byte* signature (in effect),
>> and a dynamic check for length + checksum.
>>
>> I think this should be acceptable.
> 
> It sounds like it would solve my concern.
> 
> It doesn't sound like it would be a huge amount of extra effort or code.
> 
> The part about having to identify tables from the blob is not great,
> but it does seem like your plan is unlikely to mistakenly identify a
> non-table as a table.
> 
> Is it possible to get more confirmation that the few wasted pages
> would remain small. I guess maybe the easiest argument to this would
> be that QEMU is not going to have a large AcpiNVS blob in the first
> place because it would always mean taking some RAM from the OS.

I've written the code such that it tracks, per fw_cfg blob, if the blob
had any pointers into it that turned out to be non-tables. If it didn't,
then the blob is released in the end. In my current testing, there are
two blobs in total, /etc/acpi/rsdp and /etc/acpi/tables, and both are
released. Hence no pages are wasted at all.

In general, as far as I understand, QEMU developers don't intend to push
any blobs > 64K, and an idea was raised to keep a minimal number of
tables per blob. So this should limit the leakage even if a few blobs
remain with "opaque" contents in them.

I'll go through the patches again and post them soon.

Thanks
Laszlo


------------------------------------------------------------------------------
Slashdot TV.  
Video for Nerds.  Stuff that matters.
http://tv.slashdot.org/
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/edk2-devel

Reply via email to