qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] How to reserve guest physical region for ACPI


From: Laszlo Ersek
Subject: Re: [Qemu-devel] How to reserve guest physical region for ACPI
Date: Tue, 5 Jan 2016 18:22:33 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.5.0

On 01/05/16 18:08, Igor Mammedov wrote:
> On Mon, 4 Jan 2016 21:17:31 +0100
> Laszlo Ersek <address@hidden> wrote:
> 
>> Michael CC'd me on the grandparent of the email below. I'll try to add
>> my thoughts in a single go, with regard to OVMF.
>>
>> On 12/30/15 20:52, Michael S. Tsirkin wrote:
>>> On Wed, Dec 30, 2015 at 04:55:54PM +0100, Igor Mammedov wrote:  
>>>> On Mon, 28 Dec 2015 14:50:15 +0200
>>>> "Michael S. Tsirkin" <address@hidden> wrote:
>>>>  
>>>>> On Mon, Dec 28, 2015 at 10:39:04AM +0800, Xiao Guangrong wrote:  
>>>>>>
>>>>>> Hi Michael, Paolo,
>>>>>>
>>>>>> Now it is the time to return to the challenge that how to reserve guest
>>>>>> physical region internally used by ACPI.
>>>>>>
>>>>>> Igor suggested that:
>>>>>> | An alternative place to allocate reserve from could be high memory.
>>>>>> | For pc we have "reserved-memory-end" which currently makes sure
>>>>>> | that hotpluggable memory range isn't used by firmware
>>>>>> (https://lists.nongnu.org/archive/html/qemu-devel/2015-11/msg00926.html) 
>>>>>>  
>>
>> OVMF has no support for the "reserved-memory-end" fw_cfg file. The
>> reason is that nobody wrote that patch, nor asked for the patch to be
>> written. (Not implying that just requesting the patch would be
>> sufficient for the patch to be written.)
>>
>>>>> I don't want to tie things to reserved-memory-end because this
>>>>> does not scale: next time we need to reserve memory,
>>>>> we'll need to find yet another way to figure out what is where.  
>>>> Could you elaborate a bit more on a problem you're seeing?
>>>>
>>>> To me it looks like it scales rather well.
>>>> For example lets imagine that we adding a device
>>>> that has some on device memory that should be mapped into GPA
>>>> code to do so would look like:
>>>>
>>>>   pc_machine_device_plug_cb(dev)
>>>>   {
>>>>    ...
>>>>    if (dev == OUR_NEW_DEVICE_TYPE) {
>>>>        memory_region_add_subregion(as, current_reserved_end, &dev->mr);
>>>>        set_new_reserved_end(current_reserved_end + 
>>>> memory_region_size(&dev->mr));
>>>>    }
>>>>   }
>>>>
>>>> we can practically add any number of new devices that way.  
>>>
>>> Yes but we'll have to build a host side allocator for these, and that's
>>> nasty. We'll also have to maintain these addresses indefinitely (at
>>> least per machine version) as they are guest visible.
>>> Not only that, there's no way for guest to know if we move things
>>> around, so basically we'll never be able to change addresses.
>>>
>>>   
>>>>    
>>>>> I would like ./hw/acpi/bios-linker-loader.c interface to be extended to
>>>>> support 64 bit RAM instead  
>>
>> This looks quite doable in OVMF, as long as the blob to allocate from
>> high memory contains *zero* ACPI tables.
>>
>> (
>> Namely, each ACPI table is installed from the containing fw_cfg blob
>> with EFI_ACPI_TABLE_PROTOCOL.InstallAcpiTable(), and the latter has its
>> own allocation policy for the *copies* of ACPI tables it installs.
>>
>> This allocation policy is left unspecified in the section of the UEFI
>> spec that governs EFI_ACPI_TABLE_PROTOCOL.
>>
>> The current policy in edk2 (= the reference implementation) seems to be
>> "allocate from under 4GB". It is currently being changed to "try to
>> allocate from under 4GB, and if that fails, retry from high memory". (It
>> is motivated by Aarch64 machines that may have no DRAM at all under 4GB.)
>> )
>>
>>>>> (and maybe a way to allocate and
>>>>> zero-initialize buffer without loading it through fwcfg),  
>>
>> Sounds reasonable.
>>
>>>>> this way bios
>>>>> does the allocation, and addresses can be patched into acpi.  
>>>> and then guest side needs to parse/execute some AML that would
>>>> initialize QEMU side so it would know where to write data.  
>>>
>>> Well not really - we can put it in a data table, by itself
>>> so it's easy to find.  
>>
>> Do you mean acpi_tb_find_table(), acpi_get_table_by_index() /
>> acpi_get_table_with_size()?
>>
>>>
>>> AML is only needed if access from ACPI is desired.
>>>
>>>   
>>>> bios-linker-loader is a great interface for initializing some
>>>> guest owned data and linking it together but I think it adds
>>>> unnecessary complexity and is misused if it's used to handle
>>>> device owned data/on device memory in this and VMGID cases.  
>>>
>>> I want a generic interface for guest to enumerate these things.  linker
>>> seems quite reasonable but if you see a reason why it won't do, or want
>>> to propose a better interface, fine.  
>>
>> * The guest could do the following:
>> - while processing the ALLOCATE commands, it would make a note where in
>> GPA space each fw_cfg blob gets allocated
>> - at the end the guest would prepare a temporary array with a predefined
>> record format, that associates each fw_cfg blob's name with the concrete
>> allocation address
>> - it would create an FWCfgDmaAccess stucture pointing at this array,
>> with a new "control" bit set (or something similar)
>> - the guest could write the address of the FWCfgDmaAccess struct to the
>> appropriate register, as always.
>>
>> * Another idea would be a GET_ALLOCATION_ADDRESS linker/loader command,
>> specifying:
>> - the fw_cfg blob's name, for which to retrieve the guest-allocated
>>   address (this command could only follow the matching ALLOCATE
>>   command, never precede it)
>> - a flag whether the address should be written to IO or MMIO space
>>   (would be likely IO on x86, MMIO on ARM)
>> - a unique uint64_t key (could be the 16-bit fw_cfg selector value that
>>   identifies the blob, actually!)
>> - a uint64_t (IO or MMIO) address to write the unique key and then the
>>   allocation address to.
>>
>> Either way, QEMU could learn about all the relevant guest-side
>> allocation addresses in a low number of traps. In addition, AML code
>> wouldn't have to reflect any allocation addresses to QEMU, ever.

> That would be nice trick. I see 2 issues here:
>  1. ACPI tables blob is build atomically when one guest tries to read it
>     from fw_cfg so patched addresses have to be communicated
>     to QEMU before that.

I don't understand issue #1. I think it is okay if the allocation
happens strictly after QEMU refreshes / regenerates the ACPI payload.
Namely, the guest-allocated addresses have two uses:
- references from within the ACPI payload
- references from the QEMU side, for device operation.

The first purpose is covered by the linker/loader itself (that is,
GET_ALLOCATION_ADDRESS would be used *in addition* to ADD_POINTER). The
second purpose would be covered by GET_ALLOCATION_ADDRESS.

>  2. Mo important I think that we are miss-using linker-loader
>     interface here, trying to from allocate buffer in guest RAM
>     an so consuming it while all we need a window into device
>     memory mapped somewhere outside of RAM occupied  address space.

But, more importantly, I definitely see your point with issue #2. I'm
neutral on the question whether this should be solved with the ACPI
linker/loader or with something else. I'm perfectly fine with "something
else", as long as it is generic enough. The above GET_ALLOCATION_ADDRESS
idea is relevant *only if* the ACPI linker/loader is deemed the best
solution here.

(Heck, if the linker/loader avenue is rejected here, that's the least
work for me! :))

Thanks
Laszlo

> 
>>
>>>
>>> PCI would do, too - though windows guys had concerns about
>>> returning PCI BARs from ACPI.
>>>
>>>   
>>>> There was RFC on list to make BIOS boot from NVDIMM already
>>>> doing some ACPI table lookup/parsing. Now if they were forced
>>>> to also parse and execute AML to initialize QEMU with guest
>>>> allocated address that would complicate them quite a bit.  
>>>
>>> If they just need to find a table by name, it won't be
>>> too bad, would it?
>>>   
>>>> While with NVDIMM control memory region mapped directly by QEMU,
>>>> respective patches don't need in any way to initialize QEMU,
>>>> all they would need just read necessary data from control region.
>>>>
>>>> Also using bios-linker-loader takes away some usable RAM
>>>> from guest and in the end that doesn't scale,
>>>> the more devices I add the less usable RAM is left for guest OS
>>>> while all the device needs is a piece of GPA address space
>>>> that would belong to it.  
>>>
>>> I don't get this comment. I don't think it's MMIO that is wanted.
>>> If it's backed by qemu virtual memory then it's RAM.
>>>   
>>>>>
>>>>> See patch at the bottom that might be handy.  
>>
>> I've given up on Microsoft implementing DataTableRegion. (It's sad, really.)
>>
>> From last year I have a WIP version of "docs/vmgenid.txt" that is based
>> on Michael's build_append_named_dword() function. If
>> GET_ALLOCATION_ADDRESS above looks good, then I could simplify the ACPI
>> stuff in that text file (and hopefully post it soon after for comments?)
>>
>>>>>  
>>>>>> he also innovated a way to use 64-bit address in DSDT/SSDT.rev = 1:
>>>>>> | when writing ASL one shall make sure that only XP supported
>>>>>> | features are in global scope, which is evaluated when tables
>>>>>> | are loaded and features of rev2 and higher are inside methods.
>>>>>> | That way XP doesn't crash as far as it doesn't evaluate unsupported
>>>>>> | opcode and one can guard those opcodes checking _REV object if 
>>>>>> neccesary.
>>>>>> (https://lists.nongnu.org/archive/html/qemu-devel/2015-11/msg01010.html) 
>>>>>>  
>>>>>
>>>>> Yes, this technique works.  
>>
>> Agreed.
>>
>>>>>
>>>>> An alternative is to add an XSDT, XP ignores that.
>>>>> XSDT at the moment breaks OVMF (because it loads both
>>>>> the RSDT and the XSDT, which is wrong), but I think
>>>>> Laszlo was working on a fix for that.  
>>
>> We have to distinguish two use cases here.
>>
>> * The first is the case when QEMU prepares both an XSDT and an RSDT, and
>> links at least one common ACPI table from both. This would cause OVMF to
>> pass the same source (= to-be-copied) table to
>> EFI_ACPI_TABLE_PROTOCOL.InstallAcpiTable() twice, with one of the
>> following outcomes:
>>
>> - there would be two instances of the same table (think e.g. SSDT)
>> - the second attempt would be rejected (e.g. FADT) and that error would
>>   terminate the linker-loader procedure.
>>
>> This issue would not be too hard to overcome, with a simple "memoization
>> technique". After the initial loading & linking of the tables, OVMF
>> could remember the addresses of the "source" ACPI tables, and could
>> avoid passing already installed source tables to
>> EFI_ACPI_TABLE_PROTOCOL.InstallAcpiTable() for a second time.
>>
>> * The second use case is when an ACPI table is linked *only* from QEMU's
>> XSDT. This is much harder to fix, because
>> EFI_ACPI_TABLE_PROTOCOL.InstallAcpiTable() in edk2 links the copy of the
>> passed-in table into *both* RSDT and XSDT, automatically. And, again,
>> the UEFI spec doesn't provide a way to control this from the caller
>> (i.e. from within OVMF).
>>
>> I have tried earlier to effect a change in the specification of
>> EFI_ACPI_TABLE_PROTOCOL.InstallAcpiTable(), on the ASWG and USWG mailing
>> lists. (At that time I was trying to expose UEFI memory *type* to the
>> caller, from which the copy of the ACPI table being installed should be
>> allocated from.) Alas, I received no answers at all.
>>
>> All in all I strongly recommend the "place rev2+ objects in method
>> scope" trick, over the "link it from the XSDT only" trick.
>>
>>>> Using XSDT would increase ACPI tables occupied RAM
>>>> as it would duplicate DSDT + non XP supported AML
>>>> at global namespace.  
>>>
>>> Not at all - I posted patches linking to same
>>> tables from both RSDT and XSDT at some point.  
>>
>> Yes, at <http://thread.gmane.org/gmane.comp.emulators.qemu/342559>. This
>> could be made work in OVMF with the above mentioned memoization stuff.
>>
>>> Only the list of pointers would be different.  
>>
>> I don't recommend that, see the second case above.
>>
>> Thanks
>> Laszlo
>>
>>>> So far we've managed keep DSDT compatible with XP while
>>>> introducing features from v2 and higher ACPI revisions as
>>>> AML that is only evaluated on demand.
>>>> We can continue doing so unless we have to unconditionally
>>>> add incompatible AML at global scope.
>>>>  
>>>
>>> Yes.
>>>   
>>>>>  
>>>>>> Michael, Paolo, what do you think about these ideas?
>>>>>>
>>>>>> Thanks!  
>>>>>
>>>>>
>>>>>
>>>>> So using a patch below, we can add Name(PQRS, 0x0) at the top of the
>>>>> SSDT (or bottom, or add a separate SSDT just for that).  It returns the
>>>>> current offset so we can add that to the linker.
>>>>>
>>>>> Won't work if you append the Name to the Aml structure (these can be
>>>>> nested to arbitrary depth using aml_append), so using plain GArray for
>>>>> this API makes sense to me.
>>>>>  
>>>>> --->  
>>>>>
>>>>> acpi: add build_append_named_dword, returning an offset in buffer
>>>>>
>>>>> This is a very limited form of support for runtime patching -
>>>>> similar in functionality to what we can do with ACPI_EXTRACT
>>>>> macros in python, but implemented in C.
>>>>>
>>>>> This is to allow ACPI code direct access to data tables -
>>>>> which is exactly what DataTableRegion is there for, except
>>>>> no known windows release so far implements DataTableRegion.  
>>>> unsupported means Windows will BSOD, so it's practically
>>>> unusable unless MS will patch currently existing Windows
>>>> versions.  
>>>
>>> Yes. That's why my patch allows patching SSDT without using
>>> DataTableRegion.
>>>   
>>>> Another thing about DataTableRegion is that ACPI tables are
>>>> supposed to have static content which matches checksum in
>>>> table the header while you are trying to use it for dynamic
>>>> data. It would be cleaner/more compatible to teach
>>>> bios-linker-loader to just allocate memory and patch AML
>>>> with the allocated address.  
>>>
>>> Yes - if address is static, you need to put it outside
>>> the table. Can come right before or right after this.
>>>   
>>>> Also if OperationRegion() is used, then one has to patch
>>>> DefOpRegion directly as RegionOffset must be Integer,
>>>> using variable names is not permitted there.  
>>>
>>> I am not sure the comment was understood correctly.
>>> The comment says really "we can't use DataTableRegion
>>> so here is an alternative".
>>>   
>>>>  
>>>>>
>>>>> Signed-off-by: Michael S. Tsirkin <address@hidden>
>>>>>
>>>>> ---
>>>>>
>>>>> diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h
>>>>> index 1b632dc..f8998ea 100644
>>>>> --- a/include/hw/acpi/aml-build.h
>>>>> +++ b/include/hw/acpi/aml-build.h
>>>>> @@ -286,4 +286,7 @@ void acpi_build_tables_cleanup(AcpiBuildTables 
>>>>> *tables, bool mfre);
>>>>>  void
>>>>>  build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets);
>>>>>  
>>>>> +int
>>>>> +build_append_named_dword(GArray *array, const char *name_format, ...);
>>>>> +
>>>>>  #endif
>>>>> diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
>>>>> index 0d4b324..7f9fa65 100644
>>>>> --- a/hw/acpi/aml-build.c
>>>>> +++ b/hw/acpi/aml-build.c
>>>>> @@ -262,6 +262,32 @@ static void build_append_int(GArray *table, uint64_t 
>>>>> value)
>>>>>      }
>>>>>  }
>>>>>  
>>>>> +/* Build NAME(XXXX, 0x0) where 0x0 is encoded as a qword,
>>>>> + * and return the offset to 0x0 for runtime patching.
>>>>> + *
>>>>> + * Warning: runtime patching is best avoided. Only use this as
>>>>> + * a replacement for DataTableRegion (for guests that don't
>>>>> + * support it).
>>>>> + */
>>>>> +int
>>>>> +build_append_named_qword(GArray *array, const char *name_format, ...)
>>>>> +{
>>>>> +    int offset;
>>>>> +    va_list ap;
>>>>> +
>>>>> +    va_start(ap, name_format);
>>>>> +    build_append_namestringv(array, name_format, ap);
>>>>> +    va_end(ap);
>>>>> +
>>>>> +    build_append_byte(array, 0x0E); /* QWordPrefix */
>>>>> +
>>>>> +    offset = array->len;
>>>>> +    build_append_int_noprefix(array, 0x0, 8);
>>>>> +    assert(array->len == offset + 8);
>>>>> +
>>>>> +    return offset;
>>>>> +}
>>>>> +
>>>>>  static GPtrArray *alloc_list;
>>>>>  
>>>>>  static Aml *aml_alloc(void)
>>>>>
>>>>>  
>>
> 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]