From: Laszlo Ersek <lersek@redhat.com>
To: Phil Dennis-Jordan <lists@philjordan.eu>
Cc: edk2-devel-01 <edk2-devel@lists.01.org>,
Phil Dennis-Jordan <phil@philjordan.eu>
Subject: Re: [PATCH v1 1/1] OvmfPkg: Allow multiple add-pointer linker commands to same ACPI table
Date: Thu, 30 Mar 2017 17:58:26 +0200 [thread overview]
Message-ID: <afac98ca-6d2b-9bca-dc39-48d36640239c@redhat.com> (raw)
In-Reply-To: <CAGCz3vs7AvkmGK+E6Vw_KF-VNYVo=jL5Y0jirKsmenaOeU1XOA@mail.gmail.com>
On 03/30/17 12:41, Phil Dennis-Jordan wrote:
> On Thu, Mar 30, 2017 at 2:06 PM, Laszlo Ersek <lersek@redhat.com> wrote:
>> This is a very good first patch!
>>
>> I have a few requests. I'll generally rehash the points from
>> <https://bugzilla.tianocore.org/show_bug.cgi?id=368#c0>.
>>
>> On 03/29/17 09:50, Phil Dennis-Jordan wrote:
>>> From: Phil Dennis-Jordan <phil@philjordan.eu>
>>>
>>> ACPI tables may contain multiple pointer fields to the same destination
>>> table. For example, in some revisions, the FADT contains both DSDT and
>>> X_DSDT fields, and they may both point to the DSDT. Indeed, some operating
>>> systems demand this to be the case.
>>>
>>> Previously, if Qemu created "add pointer" linker commands for both fields,
>>> the linking process would fail, as AcpiProtocol->InstallAcpiTable() may
>>> only be called once for each destination table and otherwise returns an
>>> error.
>>
>> (1) Please be a bit more precise here: passing the exact same table
>> twice to InstallAcpiTable() is wrong, but for two different reasons.
>>
>> Reason #1 is what you named -- some, but not all, ACPI table types are
>> not allowed to have multiple instances, and that aborts the linking
>> process. Reason #2 is that successfully installing the exact same table,
>> of a type that does allow multiple instances (such as an SSDT, there
>> could be others) is just as wrong, although it doesn't abort the linking.
>
> Thanks - I know you've explained this distinction a bunch of times
> now, but it's finally clicked with me.
>
>>>
>>> This change adds a memoisation data structure which tracks the table
>>> pointers that have already been installed; even if the same pointer is
>>> encountered multiple times, it is only installed once.
>>
>> (2) Please s/installed/processed/g above -- see for more below.
>>
>>>
>>> Contributed-under: TianoCore Contribution Agreement 1.0
>>> Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
>>> ---
>>> OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c | 109 ++++++++++++++++----
>>> 1 file changed, 89 insertions(+), 20 deletions(-)
>>>
>>> diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
>>> index 7bb2e3f21821..cffa838623cc 100644
>>> --- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
>>> +++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
>>> @@ -100,6 +100,39 @@ BlobCompare (
>>>
>>>
>>> /**
>>> + Comparator function for two opaque pointers, ordering on pointer value
>>> + itself.
>>> + Can be used as both Key and UserStruct comparator.
>>> +
>>> + @param[in] Pointer1 First pointer.
>>> +
>>> + @param[in] Pointer2 Second pointer.
>>> +
>>> + @retval <0 If Pointer1 compares less than Pointer2.
>>> +
>>> + @retval 0 If Pointer1 compares equal to Pointer2.
>>> +
>>> + @retval >0 If Pointer1 compares greater than Pointer2.
>>> +**/
>>> +STATIC
>>> +INTN
>>> +EFIAPI
>>> +PointerCompare (
>>> + IN CONST VOID *Pointer1,
>>> + IN CONST VOID *Pointer2
>>> + )
>>> +{
>>> + if (Pointer1 == Pointer2) {
>>> + return 0;
>>> + } else if ((INTN)Pointer1 < (INTN)Pointer2) {
>>
>> (3) Please use UINTN here, not INTN.
>>
>> Also, I slightly dislike an "else" after a "return", but I guess it's up
>> to you. :)
>>
>>> + return -1;
>>> + } else {
>>> + return 1;
>>> + }
>>> +}
>>> +
>>> +
>>> +/**
>>> Process a QEMU_LOADER_ALLOCATE command.
>>>
>>> @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process.
>>> @@ -535,27 +568,32 @@ UndoCmdWritePointer (
>>> This function assumes that the entire QEMU linker/loader command file has
>>> been processed successfully in a prior first pass.
>>>
>>> - @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
>>> + @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
>>>
>>> - @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
>>> - structures.
>>> + @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
>>> + structures.
>>>
>>> - @param[in] AcpiProtocol The ACPI table protocol used to install tables.
>>> + @param[in] AcpiProtocol The ACPI table protocol used to install tables.
>>>
>>> - @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
>>> - elements, allocated by the caller. On output,
>>> - the function will have stored (appended) the
>>> - AcpiProtocol-internal key of the ACPI table that
>>> - the function has installed, if the AddPointer
>>> - command identified an ACPI table that is
>>> - different from RSDT and XSDT.
>>> + @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
>>> + elements, allocated by the caller. On output,
>>> + the function will have stored (appended) the
>>> + AcpiProtocol-internal key of the ACPI table that
>>> + the function has installed, if the AddPointer
>>> + command identified an ACPI table that is
>>> + different from RSDT and XSDT.
>>>
>>> - @param[in,out] NumInstalled On input, the number of entries already used in
>>> - InstalledKey; it must be in [0,
>>> - INSTALLED_TABLES_MAX] inclusive. On output, the
>>> - parameter is incremented if the AddPointer
>>> - command identified an ACPI table that is
>>> - different from RSDT and XSDT.
>>> + @param[in,out] NumInstalled On input, the number of entries already used in
>>> + InstalledKey; it must be in [0,
>>> + INSTALLED_TABLES_MAX] inclusive. On output, the
>>> + parameter is incremented if the AddPointer
>>> + command identified an ACPI table that is
>>> + different from RSDT and XSDT.
>>> +
>>> + @param[in,out] InstalledTables The ORDERED_COLLECTION tracking the ACPI tables
>>> + which have already been installed. If a new
>>> + table is encountered by the function, it is
>>> + added; existing ones will not be installed again.
>>
>> (4) Please rename "InstalledTables" to "SeenPointers", and update its
>> comment block accordingly:
>>
>> "The ORDERED_COLLECTION tracking the absolute target addresses that have
>> been pointed-to by QEMU_LOADER_ADD_POINTER commands thus far. If a
>> target address is encountered for the first time, and it identifies an
>> ACPI table that is different from RDST and DSDT, the table is installed.
>> If a target address is seen for the second or later times, it is skipped
>> without taking any action."
>>
>> (5) An added benefit of using the name "SeenPointers" is that it is
>> exactly as long as "NumInstalled", and you won't have to reindent the
>> other comment blocks.
>>
>> (6) Please also update the @retval EFI_SUCCESS comment:
>>
>> "AddPointer has been processed. Either its absolute target address has
>> been encountered before, or an ACPI table different from RSDT and XSDT
>> has been installed (reflected by InstalledKey and NumInstalled), or RSDT
>> or XSDT has been identified but not installed, or the fw_cfg blob
>> pointed-into by AddPointer has been marked as hosting something else
>> than just direct ACPI table contents."
>>
>>>
>>> @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
>>> input.
>>> @@ -584,7 +622,8 @@ Process2ndPassCmdAddPointer (
>>> IN CONST ORDERED_COLLECTION *Tracker,
>>> IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
>>> IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],
>>> - IN OUT INT32 *NumInstalled
>>> + IN OUT INT32 *NumInstalled,
>>> + IN OUT ORDERED_COLLECTION *InstalledTables
>>> )
>>> {
>>> CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;
>>> @@ -679,6 +718,21 @@ Process2ndPassCmdAddPointer (
>>> return EFI_SUCCESS;
>>> }
>>>
>>> + Status = OrderedCollectionInsert (
>>> + InstalledTables, NULL, (VOID *)(UINTN)PointerValue);
>>
>> (7) For function calls (and function-like macro invocations) that don't
>> fit on a single line, we should now follow the style described in
>> <https://bugzilla.tianocore.org/show_bug.cgi?id=425>.
>>
>> (8) The main point of renaming InstalledTables to SeenPointers:
>>
>> please move this logic higher up in the function,
>>
>> ASSERT (PointerValue < Blob2Remaining);
>> <------- right here
>> Blob2Remaining -= (UINTN) PointerValue;
>>
>> The reason why is explained in
>> <https://bugzilla.tianocore.org/show_bug.cgi?id=368#c0>, in the part
>> that starts with "This is justified because all actions after that point
>> would be..."
>
> I've now moved it as requested for patch v2, but my reasoning for the
> original ordering of the logic was precisely to avoid the complication
> of having to roll back in the error case. It seemed a valid code
> transformation as the other early-out paths also return EFI_SUCCESS,
> the RSDT/XSDT one has no side effects, and the opaque blob one's side
> effect is surely idempotent.
Yes, that's a valid argument, but I prefer the proposed (and,
thankfully, v2 :) ) approach for two reasons:
- I would like to benefit from the memoization also in order to save on
the idempotent (but still superfluous) code paths,
- I prefer a clean rollback-on-error because it is more future proof. If
we omit it now, later extensions might forget about it completely, which
at that point might not be so innocent any longer.
>
>> (9) Near the two late error exits in the function (when we run out of
>> INSTALLED_TABLES_MAX, or InstallAcpiTable() fails), please replace the
>> direct returns with a goto to an error handling label. This error
>> handling label should remove the just added PointerValue from SeenPointers.
>>
>> For this, you can use the second parameter of OrderedCollectionInsert().
>> On success, that parameter is set to the node of the newly inserted
>> item, and then you can pass that to OrderedCollectionDelete() directly
>> on the error path.
>>
>>> + if (EFI_ERROR (Status)) {
>>> + if (Status == RETURN_ALREADY_STARTED) {
>>> + //
>>> + // Already installed this table, don't try to do so again.
>>> + //
>>> + DEBUG ((DEBUG_VERBOSE, "%a: AcpiProtocol->InstallAcpiTable reports table "
>>> + "already installed, skipping. PointerValue=0x%Lx\n",
>>> + __FUNCTION__, PointerValue));
>>> + Status = EFI_SUCCESS;
>>> + }
>>> + return Status;
>>> + }
>>> +
>>> if (*NumInstalled == INSTALLED_TABLES_MAX) {
>>> DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n",
>>> __FUNCTION__, INSTALLED_TABLES_MAX));
>>> @@ -739,6 +793,8 @@ InstallQemuFwCfgTables (
>>> UINTN *InstalledKey;
>>> INT32 Installed;
>>> ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;
>>> + ORDERED_COLLECTION *InstalledTables;
>>> + ORDERED_COLLECTION_ENTRY *InstalledTableEntry;
>>
>> (10) please rename InstalledTables here too.
>>
>>>
>>> Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);
>>> if (EFI_ERROR (Status)) {
>>> @@ -827,14 +883,21 @@ InstallQemuFwCfgTables (
>>> goto RollbackWritePointersAndFreeTracker;
>>> }
>>>
>>> + InstalledTables = OrderedCollectionInit (PointerCompare, PointerCompare);
>>> + if (InstalledTables == NULL) {
>>> + Status = EFI_OUT_OF_RESOURCES;
>>> + goto FreeKeys;
>>> + }
>>> +
>>
>> Looks valid.
>>
>>> //
>>> // second pass: identify and install ACPI tables
>>> //
>>> Installed = 0;
>>> for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
>>> if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {
>>> - Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer,
>>> - Tracker, AcpiProtocol, InstalledKey, &Installed);
>>> + Status = Process2ndPassCmdAddPointer (
>>> + &LoaderEntry->Command.AddPointer, Tracker, AcpiProtocol,
>>> + InstalledKey, &Installed, InstalledTables);
>>> if (EFI_ERROR (Status)) {
>>> goto UninstallAcpiTables;
>>> }
>>> @@ -870,6 +933,12 @@ UninstallAcpiTables:
>>> DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
>>> }
>>>
>>> + while (((InstalledTableEntry = OrderedCollectionMax(InstalledTables))) != NULL) {
>>> + OrderedCollectionDelete (InstalledTables, InstalledTableEntry, NULL);
>>> + }
>>> + OrderedCollectionUninit (InstalledTables);
>>> +
>>
>> (11) Please implement this loop instead like the one that tears down
>> "Tracker". Using an assignment in the controlling expression of the
>> "while" is powerful and I like it too, but it is never used in edk2
>> code, to my knowledge.
>
> There's a third option, which is to also use a for loop, but avoids
> the second running entry variable by calling OrderedCollectionMin()
> (or Max) on each iteration. This might even be slightly more efficient
> than OrderedCollectionNext(), but only by a constant factor, so I've
> gone with the same teardown as for Tracker for the sake of
> consistency.
Thanks!
>
>>> +FreeKeys:
>>> FreePool (InstalledKey);
>>
>> Yes, this looks correct too.
>>
>> (I apologize if I made any confusing requests in my review, I'm very tired.)
>>
>> Thank you,
>> Laszlo
>>
>>>
>>> RollbackWritePointersAndFreeTracker:
>>>
>>
>
> Thanks for the quick review and detailed explanations, Laszlo! I've
> submitted v2 of the patch with the changes.
>
Thanks, I'll try to look at it sooner or later.
Laszlo
prev parent reply other threads:[~2017-03-30 15:58 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-03-29 7:50 [PATCH v1 0/1] OvmfPkg/AcpiPlatformDxe: Fix bug 368, multiply pointed-to tables Phil Dennis-Jordan
2017-03-29 7:50 ` [PATCH v1 1/1] OvmfPkg: Allow multiple add-pointer linker commands to same ACPI table Phil Dennis-Jordan
2017-03-30 1:06 ` Laszlo Ersek
2017-03-30 10:41 ` Phil Dennis-Jordan
2017-03-30 15:58 ` Laszlo Ersek [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=afac98ca-6d2b-9bca-dc39-48d36640239c@redhat.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox