On 03/27/15 07:48, Gerd Hoffmann wrote:
> Hi,
>
>> The relevant ACPI SSDT is here:
>> http://pastebin.com/Q2qeBdkb
>
>> I was told on the nouveau IRC channel that the important method
>> is _ROM (line 2847).
>
> Hmm, my apci-foo isn't good enough to figure what exactly this thing is
> doing. Seems to somehow calculate and return address of the rom.
>
> I suspect just cut+paste the method isn't going to fly.
Not that my ACPI-fu is better, but I'm curious :)
So, according to ACPI 5.1, "B.3.3 _ROM (Get ROM Data)":
> This method is used to get a copy of the display devices' ROM data.
> This method is required when the ROM image is stored in a proprietary
> format such as stored in the system BIOS ROM. This method is not
> necessary if the ROM image can be read through a standard PCI
> interface (using ROM BAR). If _ROM is present, it is preferred over
> the image read through the standard PCI interface, in order to allow
> system BIOS to provide re-configured ROM data via the method.
>
> The video driver can use the data returned by this method to program
> the device. The format of the data returned by this function is a
> large linear buffer limited to 4 KB. The content of the buffer is
> defined by the graphics independent hardware vendor (IHV) that builds
> this device. The format of this ROM data will traditionally be
> compatible with the ROM format of the normal PCI video card, which
> will allow the video driver to program its device, independently of
> motherboard versus add-in card issues.
>
> The data returned by the _ROM method is implementation-specific data
> that the video driver needs to program the device. This method is
> defined to provide this data as motherboard devices typically don't
> have a dedicated option ROM. This method will allow a video driver to
> get the key implementation specific data it needs so that it can fully
> control and program the device without BIOS support.
>
> Arguments: (2)
>
> Arg0 -- An Integer containing the offset of the display device ROM
> data
> Arg1 -- An Integer containing the size of the buffer to fill in (up to
> 4K).
>
> Return Value:
>
> A Buffer containing the requested ROM data
This way we can rename some objects in the decompiled SSDT:
- Arg0 -> ArgRomDataOffset
- Arg1 -> ArgOutbufSize
Via further analysis:
- Local0 -> RomDataOffset
- Local1 -> OutbufSize
- Local2 -> EndOffset
Then the trimmed down SSDT looks like:
> DefinitionBlock ("ssdt5.aml", "SSDT", 1, "LENOVO", "CB-01 ", 0x00000001)
> {
> Scope (\_SB.PCI0.PEG0.PEGP)
> {
> OperationRegion (VBOR, SystemMemory, 0x9CF9C018, 0x00019604)
> Field (VBOR, DWordAcc, Lock, Preserve)
> {
> RVBS, 32,
> VBS1, 262144,
> VBS2, 262144,
> VBS3, 262144,
> VBS4, 45056
> }
> }
This gives the name "VBOR" to the system memory region @ 0x9CF9C018, for
length 0x00019604. In addition,
- the first UINT32 at its beginning gets the member name "RVBS",
- the next 32K byte array gets the name VBS1,
- the next 32K byte array gets the name VBS2,
- the next 32K byte array gets the name VBS3,
- the last five and half K byte array gets the name VBS4.
I'm not exactly sure what RVBS stands for, but from the below, "Rom
Video Buffer Size" would be justified.
That is, the UINT32 @ 0x9CF9C018 holds the actual ROM size.
>
> Scope (\_SB.PCI0.PEG0.PEGP)
> {
>
> Method (_ROM, 2, NotSerialized) // _ROM: Read-Only Memory
> {
> Store (ArgRomDataOffset, RomDataOffset)
> Store (ArgOutbufSize, OutbufSize)
Copies the arguments to local variables. "RomDataOffset" is where the
caller wants to start reading from, "OutbufSize" is the number of bytes
it wants to get.
> Name (VROM, Buffer (OutbufSize)
> {
> 0x00
> })
The VROM object (our future return value) is initialized as a
zero-filled array of the requested size.
> If (LGreater (OutbufSize, 0x1000))
> {
> Store (0x1000, OutbufSize)
> }
If the caller wants to read more than 4K (which is not allowed by the
spec, see above), the code clamps it down to 4K.
>
> If (LGreater (ArgRomDataOffset, RVBS))
> {
> Return (VROM)
> }
If the caller wants to read from an offset that is past the actual ROM
with more than 1 bytes, we return the zero-filled buffer.
(Note that reading *right past* the ROM, ie. at the end of the ROM, is
valid; see below.)
>
> Add (ArgRomDataOffset, ArgOutbufSize, EndOffset)
We compute the end offset, that is, the offset of the first byte *past*
the read portion.
> If (LGreater (EndOffset, RVBS))
> {
> Subtract (RVBS, RomDataOffset, OutbufSize)
> }
If the end offset (ie. the requested buffer size) would cause us to read
past beyond the actual ROM size, we re-set OutbufSize so that the read
ends exactly at the end of the actual ROM.
The (RVBS - RomDataOffset) subtraction is safe, because we have already
ensured above that RomDataOffset <= RVBS. If the read request is right
at the end of the ROM, then OutbufSize will be set to zero here.
In addition, the initial comparison is also safe; it compares exclusive
limits (apples to apples).
EndOffset is not recomputed, but it won't be used later anyway.
>
> If (LLess (RomDataOffset, 0x8000))
> {
> Mid (VBS1, RomDataOffset, OutbufSize, VROM)
> }
Now this is interesting. If the start offset falls under 32K, then we
satisfy the read from VBS1. (Note that this can easily "overflow" into
the later VBSx members, but that shouldn't matter, because they are all
consecutive, and RVBS probably holds the *full* video ROM size, and
we've already checked the range against that.)
> Else
> {
> Subtract (RomDataOffset, 0x8000, RomDataOffset)
> If (LLess (RomDataOffset, 0x8000))
> {
> Mid (VBS2, RomDataOffset, OutbufSize, VROM)
> }
> Else
> {
> Subtract (RomDataOffset, 0x8000, RomDataOffset)
> If (LLess (RomDataOffset, 0x8000))
> {
> Mid (VBS3, RomDataOffset, OutbufSize, VROM)
> }
> Else
> {
> Subtract (RomDataOffset, 0x8000, RomDataOffset)
> If (LLess (RomDataOffset, 0x8000))
> {
> Mid (VBS4, RomDataOffset, OutbufSize, VROM)
> }
> }
> }
> }
Otherwise, we locate, in 32KB steps, the exact VBSx field into which the
start offset falls, and actually initiate the read by referencing that
VBSx field.
Functionally, this entire exercise with the VBSx fields seems
superfluous; the actual start offset computed this way will not change.
(Every time RomDataOffset is decreased by 32KB, we jump to the next VBSx
field, which constitutes an increment of 32KB.) So I'm thinking this
must be a workaround for some AML interpreter limitation.
>
> Return (VROM)
> }
> }
> }
The end result is that the caller should call _ROM in a loop, updating
the start offset, and stop when the returned buffer size is smaller
(potentially: zero) than the requested read size. This _ROM loop will be
served from the memory range starting at (0x9CF9C018 + 4), for the
number of bytes that can be read from the UINT32 at 0x9CF9C018.
If a copy of the actual ROM in question is available, then one might be
able reimplement the _ROM method: define a Buffer of bytes, listing the
actual bytes comprising the ROM image, and then serve the _ROM
invocations from *that*, instead of the VBOR operation region's VBSx
fields. And the RVBS references can be replaced with a known constant.
>> Is there a way to modify the OVMF firmware to make it present
>> the BIOS payload via the _ROM method?
>
> acpi tables are provides by qemu, edk2/seabios only fetch them and store
> them in guest ram. So for starters have a look at the -apcitable switch
> qemu has.
Agreed.
(Although, the guest driver might want to call many more ACPI methods
than just _ROM...)
Thanks
Laszlo
------------------------------------------------------------------------------
Dive into the World of Parallel Programming The Go Parallel Website, sponsored
by Intel and developed in partnership with Slashdot Media, is your hub for all
things parallel software development, from weekly thought leadership blogs to
news, videos, case studies, tutorials and more. Take a look and join the
conversation now. http://goparallel.sourceforge.net/
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/edk2-devel