Discussion:
[edk2] Virtio disk not bootable in OVMF
David Woodhouse
2013-01-22 00:17:57 UTC
Permalink
When I have a virtio disk attached to a qemu VM, it doesn't appear in
the OVMF boot menu, so I can't boot from it. This is because the
BdsGetBootTypeFromDevicePath() function in GenericBdsLib.c only works on
the device path and isn't able to infer that 'PciRoot(0x0)/Pci(0x5,0x0)'
is a virtio disk. It could be *any* PCI device, if the path is all
you're looking at.

Should the VirtioBlk driver actually be registering a *child* of the Pci
device node, such as PciRoot(0x0)/Pci(0x5,0x0)/Virtio(0), so that we can
automatically work out what it is from the path? Or is there a better
way?

I expected to see it appear *twice* in my boot menu; once for EFI
booting and once for the CSM. But neither of them appear...
--
dwmw2
Andrew Fish
2013-01-22 00:59:57 UTC
Permalink
Post by David Woodhouse
When I have a virtio disk attached to a qemu VM, it doesn't appear in
the OVMF boot menu, so I can't boot from it. This is because the
BdsGetBootTypeFromDevicePath() function in GenericBdsLib.c only works on
the device path and isn't able to infer that 'PciRoot(0x0)/Pci(0x5,0x0)'
is a virtio disk. It could be *any* PCI device, if the path is all
you're looking at.
Should the VirtioBlk driver actually be registering a *child* of the Pci
device node, such as PciRoot(0x0)/Pci(0x5,0x0)/Virtio(0), so that we can
automatically work out what it is from the path? Or is there a better
way?
Not sure if BdsGetBootTypeFromDevicePath() is broken, or functioning as intended. Remember in EFI there is not a concept of a bootable non-removable block IO device. The OS installs the OS loader as a file and updates a variable to contain the path to the OS loader. This allows an arbitrary number of operating systems to boot from a single disk. So if you add a "bootable" disk into a system you still have to manually config the file that is the OS loader.

It is possible to add heuristics to know about well known loader names, etc, but the open source code does not do any of this.

If BdsGetBootTypeFromDevicePath() is broken it will be to not treat any device that produces Block IO as potentially bootable.
Post by David Woodhouse
I expected to see it appear *twice* in my boot menu; once for EFI
booting and once for the CSM. But neither of them appear...
I've not thought about legacy BIOS for a long time, but you boot from C: or A:. It is up to the BIOS to define what is A: or what is C: The BBS spec allows selection of what is C: but that is a CSM and not EFI thing. You should be able to add a BBS device path node for C: and let the CSM figure out what is C:.

Thanks,

Andrew
David Woodhouse
2013-01-22 01:40:12 UTC
Permalink
Post by Andrew Fish
Not sure if BdsGetBootTypeFromDevicePath() is broken, or functioning
as intended. Remember in EFI there is not a concept of a bootable
non-removable block IO device. The OS installs the OS loader as a
file and updates a variable to contain the path to the OS loader. This
allows an arbitrary number of operating systems to boot from a single
disk. So if you add a "bootable" disk into a system you still have to
manually config the file that is the OS loader.
It is possible to add heuristics to know about well known loader
names, etc, but the open source code does not do any of this.
Yes, absolutely. Except that as discussed on the mailing list in the
not-so-distant past, it's very hard to tell what is "removable" and what
isn't these days, and currently there *is* a well-known loader name
of /etc/boot/boot${ARCHITECTURE}.efi which OVMF *will* attempt to boot.

If I invoke qemu with a certain disk image as IDE, it appears in the
boot menu and I can boot from it. If I invoke it with the same image a
virtio, it doesn't appear at all. Whatever the behaviour, it should be
*consistent* and not vary according to how the device is attached.
Post by Andrew Fish
If BdsGetBootTypeFromDevicePath() is broken it will be to not treat
any device that produces Block IO as potentially bootable.
I'm not sure I can parse that sentence correctly. I've experimented with
treating every device that produces block IO as potentially bootable.
We'd certainly want them to get saner names than 'EFI Hard Disk 1'...
'EFI Hard Disk 37' if we're going to do that :)

And we probably *don't* want to register every *partition*.
Post by Andrew Fish
Post by David Woodhouse
I expected to see it appear *twice* in my boot menu; once for EFI
booting and once for the CSM. But neither of them appear...
I've not thought about legacy BIOS for a long time, but you boot from
C: or A:. It is up to the BIOS to define what is A: or what is C: The
BBS spec allows selection of what is C: but that is a CSM and not EFI
thing. You should be able to add a BBS device path node for C: and let
the CSM figure out what is C:.
Except that in the CSM case, the "BIOS" isn't really in control; it's
supposed to do EFI's bidding. EFI passes the BBS_TABLE to the CSM which
controls the BCV priority. If the BBS_TABLE doesn't even *mention* the
virtio disk, then CSM doesn't know what priority it should have. To be
specific: the patch at
http://git.infradead.org/users/dwmw2/seabios.git/commitdiff/756273b1e8ed135a3178c5c2529c337471d460f5 would work fine on the CSM side for virtio disks, and would correctly select which device is C: — if the EFI side actually gave me 'Legacy boot from VirtIO disk' as an option in the menu and filled in the BBS_TABLE with it correctly. Which it doesn't.

I suspect that the LegacyBiosBuildBbs() function *also* needs to list
all the devices that produce the Block IO protocol, and add them all to
the table?

And we also need to add a LUN field to BBS_TABLE, ideally, because we
might have more than one device behind each PCI function, and we need to
distinguish between them. Could we use some of the reserved bits in
BBS_STATUS_FLAGS for that?
--
dwmw2
Andrew Fish
2013-01-22 02:59:19 UTC
Permalink
Post by David Woodhouse
Post by Andrew Fish
Not sure if BdsGetBootTypeFromDevicePath() is broken, or functioning
as intended. Remember in EFI there is not a concept of a bootable
non-removable block IO device. The OS installs the OS loader as a
file and updates a variable to contain the path to the OS loader. This
allows an arbitrary number of operating systems to boot from a single
disk. So if you add a "bootable" disk into a system you still have to
manually config the file that is the OS loader.
It is possible to add heuristics to know about well known loader
names, etc, but the open source code does not do any of this.
Yes, absolutely. Except that as discussed on the mailing list in the
not-so-distant past, it's very hard to tell what is "removable" and what
isn't these days,
EFI_BLOCK_IO_PROTOCOL *BlkIo;

BlkIo->Media->RemovableMedia

Keys the EFI behavior. Other than that it is up to the person writing the EFI driver.
Post by David Woodhouse
and currently there *is* a well-known loader name
of /etc/boot/boot${ARCHITECTURE}.efi which OVMF *will* attempt to boot.
Thought it was /EFI/BOOT/BOOT{arch}.EFI as the spec....
Post by David Woodhouse
If I invoke qemu with a certain disk image as IDE, it appears in the
boot menu and I can boot from it. If I invoke it with the same image a
virtio, it doesn't appear at all. Whatever the behaviour, it should be
*consistent* and not vary according to how the device is attached.
Post by Andrew Fish
If BdsGetBootTypeFromDevicePath() is broken it will be to not treat
any device that produces Block IO as potentially bootable.
I'm not sure I can parse that sentence correctly. I've experimented with
treating every device that produces block IO as potentially bootable.
We'd certainly want them to get saner names than 'EFI Hard Disk 1'...
'EFI Hard Disk 37' if we're going to do that :)
My point is the device path should not matter, and it seems to be used to mostly generate a string.
Post by David Woodhouse
And we probably *don't* want to register every *partition*.
Well if you use the removable media rules then you only need to show partitions with the magic file. If you are not using the removable media rules the user can pick.
Post by David Woodhouse
Post by Andrew Fish
Post by David Woodhouse
I expected to see it appear *twice* in my boot menu; once for EFI
booting and once for the CSM. But neither of them appear...
I've not thought about legacy BIOS for a long time, but you boot from
C: or A:. It is up to the BIOS to define what is A: or what is C: The
BBS spec allows selection of what is C: but that is a CSM and not EFI
thing. You should be able to add a BBS device path node for C: and let
the CSM figure out what is C:.
Except that in the CSM case, the "BIOS" isn't really in control; it's
supposed to do EFI's bidding. EFI passes the BBS_TABLE to the CSM which
controls the BCV priority. If the BBS_TABLE doesn't even *mention* the
virtio disk, then CSM doesn't know what priority it should have. To be
specific: the patch at
http://git.infradead.org/users/dwmw2/seabios.git/commitdiff/756273b1e8ed135a3178c5c2529c337471d460f5 would work fine on the CSM side for virtio disks, and would correctly select which device is C: — if the EFI side actually gave me 'Legacy boot from VirtIO disk' as an option in the menu and filled in the BBS_TABLE with it correctly. Which it doesn't.
I suspect that the LegacyBiosBuildBbs() function *also* needs to list
all the devices that produce the Block IO protocol, and add them all to
the table?
So I'm confused don't you want to list PnP BCV devices? The PnP BIOS ROM should create an entry per LUN so it can be selected? This all has nothing to do with EFI or Bock IO.
Post by David Woodhouse
And we also need to add a LUN field to BBS_TABLE, ideally, because we
might have more than one device behind each PCI function, and we need to
distinguish between them. Could we use some of the reserved bits in
BBS_STATUS_FLAGS for that?
Bad idea to use reserved bits. Again the PCI BIOS ROM should produce multiple BCV entries to let you pick the LUN.

It seem from the edk2 side you just want to pick the BCV....

Got to get on a plane....

Thanks,

Andrew
Post by David Woodhouse
--
dwmw2
David Woodhouse
2013-01-22 03:30:18 UTC
Permalink
Post by Andrew Fish
EFI_BLOCK_IO_PROTOCOL *BlkIo;
BlkIo->Media->RemovableMedia
Keys the EFI behavior.
It does, slightly. EFI puts removable devices before non-removable ones
in the list. That's it, AFAICT.

It *isn't* relevant to the question of whether a device shows up in the
list or not.
Post by Andrew Fish
Other than that it is up to the person writing the EFI driver.
I would love to find that this is the case, and 'fix' the VirtioBlk
device to ensure that its disks do appear in the menu. I just don't see
how.
Post by Andrew Fish
Post by David Woodhouse
and currently there *is* a well-known loader name
of /etc/boot/boot${ARCHITECTURE}.efi which OVMF *will* attempt to boot.
Thought it was /EFI/BOOT/BOOT{arch}.EFI as the spec....
Sorry, yes. A typo.
Post by Andrew Fish
My point is the device path should not matter, and it seems to be used
to mostly generate a string.
Are we looking at the same code base? Between the
BdsGetBootTypeFromDevicePath() and BdsLibEnumerateAllBootOption()
functions in IntelFrameworkModulePkg/Library/GenericBdsLib/BdsBoot.c,
it definitely seems to be using the device path as the *only* criterion
for determining whether to list the given device as a boot option or
not.
Post by Andrew Fish
Post by David Woodhouse
And we probably *don't* want to register every *partition*.
Well if you use the removable media rules then you only need to show
partitions with the magic file. If you are not using the removable
media rules the user can pick.
Post by David Woodhouse
Post by Andrew Fish
Post by David Woodhouse
I expected to see it appear *twice* in my boot menu; once for EFI
booting and once for the CSM. But neither of them appear...
I've not thought about legacy BIOS for a long time, but you boot from
C: or A:. It is up to the BIOS to define what is A: or what is C: The
BBS spec allows selection of what is C: but that is a CSM and not EFI
thing. You should be able to add a BBS device path node for C: and let
the CSM figure out what is C:.
Except that in the CSM case, the "BIOS" isn't really in control; it's
supposed to do EFI's bidding. EFI passes the BBS_TABLE to the CSM which
controls the BCV priority. If the BBS_TABLE doesn't even *mention* the
virtio disk, then CSM doesn't know what priority it should have. To be
specific: the patch at
http://git.infradead.org/users/dwmw2/seabios.git/commitdiff/756273b1e8ed135a3178c5c2529c337471d460f5 would work fine on the CSM side for virtio disks, and would correctly select which device is C: — if the EFI side actually gave me 'Legacy boot from VirtIO disk' as an option in the menu and filled in the BBS_TABLE with it correctly. Which it doesn't.
I suspect that the LegacyBiosBuildBbs() function *also* needs to list
all the devices that produce the Block IO protocol, and add them all to
the table?
So I'm confused don't you want to list PnP BCV devices? The PnP BIOS
ROM should create an entry per LUN so it can be selected? This all has
nothing to do with EFI or Bock IO.
The top-level view: When I boot a machine with a virtio block device, I
want it to behave like an IDE block device. I want the boot menu in EFI
to offer me *both* the option to boot from it as an EFI device
(using /EFI/BOOT/BOOT${ARCHITECTURE}.EFI), or to boot from it using the
CSM as a legacy BIOS boot. I currently get *neither*.
Post by Andrew Fish
Post by David Woodhouse
And we also need to add a LUN field to BBS_TABLE, ideally, because we
might have more than one device behind each PCI function, and we need to
distinguish between them. Could we use some of the reserved bits in
BBS_STATUS_FLAGS for that?
Bad idea to use reserved bits.
I mean, of course, that a future version of the spec would no longer
call them 'reserved'.
Post by Andrew Fish
Again the PCI BIOS ROM should produce multiple BCV entries to let you
pick the LUN.
But EFI hasn't even *invoked* the BIOS ROMs at the time the boot menu is
displayed to the user. It doesn't do that until it's actually decided to
do a legacy boot. The most interesting case is that of devices which
have EFI drivers, and for which we *assume* the CSM will have drivers
too; either natively or via option roms. Like the IDE. Which *does* kind
of work via BBS_TABLE, albeit only when the CSM makes horrid assumptions
and infers meaning from the *index* into the BBS_TABLE of each entry.
--
dwmw2
Laszlo Ersek
2013-01-22 16:18:27 UTC
Permalink
Post by Andrew Fish
Post by David Woodhouse
Post by Andrew Fish
Not sure if BdsGetBootTypeFromDevicePath() is broken, or functioning
as intended. Remember in EFI there is not a concept of a bootable
non-removable block IO device.
Ah, that explains the intent.
Post by Andrew Fish
Post by David Woodhouse
Post by Andrew Fish
The OS installs the OS loader as a
file and updates a variable to contain the path to the OS loader. This
allows an arbitrary number of operating systems to boot from a single
disk.
OK, this is where the problem is introduced.

When one installs the OS of choice with the OS's UEFI installer, at some
point the installer will update the BootOrder non-volatile variable.

At this point the installer has of course already called the
gBS->ExitBootServices() boot service, so only runtime services are
available to it (one of which is gRT->SetVariable(), so trying to update
the BootOrder variable itself is fine). Non-volatile variables would be
stored in flash (nvram) on a physical platform.

qemu (without KVM) supports flash emulation.

qemu (with KVM) doesn't -- AFAIK it would take a type of memory that
reads as ROM/RAM but writes as MMIO, see "memory.c" in qemu-kvm --, so
OVMF works it around:

- before gBS->ExitBootServices() is called, non-volatile variables are
stored on the (virtual) disk, in the \NvVars file on the boot partition
/ vfat fs,

- after gBS->ExitBootServices() has been called -- that is, when the
installer would set up the boot option for the OS just installed onto
the non-removable disk -- the resident set of OVMF cannot know if the
same boot filesystem holding \NvVars is accessible to (mounted by) the
OS, and therefore it skips the write (gRT->SetVariable() has no effect).

Jordan had pointed out the file that implements this check; see
PlatformFvbDataWritten() in
"OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.c". (It doesn't do
anything if EfiAtRuntime() returns "true".)
Post by Andrew Fish
Post by David Woodhouse
Post by Andrew Fish
So if you add a "bootable" disk into a system you still have to
manually config the file that is the OS loader.
Yes, this fallback works, as I attempted to describe in my previous
email. Even SetBootOrderFromQemu() runs before ExitBootServices() is
called, so the reordering it does gets actually stored in \NvVars.
Post by Andrew Fish
Post by David Woodhouse
Post by Andrew Fish
It is possible to add heuristics to know about well known loader
names, etc, but the open source code does not do any of this.
Yes, absolutely. Except that as discussed on the mailing list in the
not-so-distant past, it's very hard to tell what is "removable" and what
isn't these days,
EFI_BLOCK_IO_PROTOCOL *BlkIo;
BlkIo->Media->RemovableMedia
Keys the EFI behavior. Other than that it is up to the person writing the EFI driver.
Yes, this field is set to FALSE in VirtioBlkInit()
[OvmfPkg/VirtioBlkDxe/VirtioBlk.c]; for many good reasons (it's even
mentioned in the file's leading comment):
- detaching/attaching a virtio-blk disk is maybe not the most critical
functionality,
- block io protocol exported for removable media has to implement a
bunch of extra callbacks,
- it would require OVMF to handle PCI hotplug/hot-unplug (people would
attemt to add/remove virtio-blk disks in virt-manager eg.), which would
necessitate severe improvements in at least the ACPI stuff in OVMF.

Too little bang for the buck.

Laszlo
David Woodhouse
2013-01-22 17:59:27 UTC
Permalink
Post by Laszlo Ersek
Post by Andrew Fish
Not sure if BdsGetBootTypeFromDevicePath() is broken, or functioning
as intended. Remember in EFI there is not a concept of a bootable
non-removable block IO device.
Ah, that explains the intent.
Yes, but AFAICT it's irrelevant. In OVMF the presence of the Removable
flag is not at all relevant to whether the device appears in the default
boot menu or not (ignoring NVRAM configuration, qv.). It affects the
*sorting*, with removable devices appearing before non-removable ones.
But that's it.

The question "why does a virtio disk not appear as a bootable device in
the list in OVMF" has *nothing* to do with whether it's removable or
not. Unless I'm very much mistaken.
Post by Laszlo Ersek
Post by Andrew Fish
The OS installs the OS loader as a
file and updates a variable to contain the path to the OS loader. This
allows an arbitrary number of operating systems to boot from a single
disk.
OK, this is where the problem is introduced.
No, I was avoiding the nvram variables altogether. This is an
"appliance" disk, with Grub installed at /efi/boot/bootx64.efi, with no
NvVars file on its EFI partition, which works fine when I just run 'qemu
-hda efidisk.img'. I go into the boot menu, elect to boot from the 'EFI
Hard Drive' option that appears there, and it works. It does not appear
in the menu when it's attached as virtio, and that is based *purely* on
the device path of the block device. Nothing else.

But since we're speaking of nvram, my plan for fixing that is probably
to use qemu's fw_cfg. I believe that fw_cfg "files" are actually
writeable from the guest, so we could use *that* for storing nvram
variables instead of the NvVars file on the EFI partition which, as
discussed, only works before the OS might have it mounted.
--
dwmw2
Andrew Fish
2013-01-22 18:06:59 UTC
Permalink
Post by David Woodhouse
Post by Laszlo Ersek
Post by Andrew Fish
Not sure if BdsGetBootTypeFromDevicePath() is broken, or functioning
as intended. Remember in EFI there is not a concept of a bootable
non-removable block IO device.
Ah, that explains the intent.
Yes, but AFAICT it's irrelevant. In OVMF the presence of the Removable
flag is not at all relevant to whether the device appears in the default
boot menu or not (ignoring NVRAM configuration, qv.). It affects the
*sorting*, with removable devices appearing before non-removable ones.
But that's it.
The question "why does a virtio disk not appear as a bootable device in
the list in OVMF" has *nothing* to do with whether it's removable or
not. Unless I'm very much mistaken.
This is really a platform policy question. The generic edk2 BDS punts platform policy to the PlatformBdsLib.

Seems like you either need to follow the algorithm in SetBootOrderFromQemu() or fix it to do what you need.

https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2/OvmfPkg/Library/PlatformBdsLib/QemuBootOrder.c

Thanks,

Andrew Fish
Laszlo Ersek
2013-01-22 19:25:16 UTC
Permalink
Post by David Woodhouse
No, I was avoiding the nvram variables altogether. This is an
"appliance" disk, with Grub installed at /efi/boot/bootx64.efi, with
no NvVars file on its EFI partition, which works fine when I just run
'qemu -hda efidisk.img'. I go into the boot menu, elect to boot from
the 'EFI Hard Drive' option that appears there, and it works. It does
not appear in the menu when it's attached as virtio, and that is based
*purely* on the device path of the block device. Nothing else.
It's getting clearer. The device path (excluding directories and the
filename) for IDE looks like:

PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)/HD(...)

whereas for virtio-blk it is

PciRoot(0x0)/Pci(0x6,0x3)/HD(...)

In the IDE case, there's an Ata(...) DP node:
- type: MESSAGING_DEVICE_PATH,
- subtype: MSG_ATAPI_DP,

for which BdsGetBootTypeFromDevicePath() returns
BDS_EFI_MESSAGE_ATAPI_BOOT (before reaching the HD(...) node).

That in turn makes BdsLibEnumerateAllBootOption() auto-generate a boot
option (= call to BdsLibBuildOptionFromHandle()).

... I'm not sure how to fix this (if at all):

(a) Is it a bug in the virtio-blk driver, not to produce such a child
device path node? When I was writing the driver and reading the driver
writers' guide & the UEFI spec, it seemed like producing an extra child
node was not necessary.

(b) If it *is* a bug in the virtio-blk driver, then a node of what
type/subtype should be appended, so that BdsLibEnumerateAllBootOption()
is happy with it? It is

- neither BDS_EFI_ACPI_FLOPPY_BOOT,
- nor BDS_EFI_MESSAGE_ATAPI_BOOT,
- nor BDS_EFI_MESSAGE_SATA_BOOT,
- nor BDS_EFI_MESSAGE_USB_DEVICE_BOOT,
- nor BDS_EFI_MESSAGE_SCSI_BOOT.

BDS_EFI_MESSAGE_MISC_BOOT would be an option. For that a child node of
type MESSAGING_DEVICE_PATH would be necessary. Available subtypes are:

- MSG_ATAPI_DP (mapped to BDS_EFI_MESSAGE_ATAPI_BOOT)
- MSG_SCSI_DP (mapped to BDS_EFI_MESSAGE_SCSI_BOOT)
- MSG_FIBRECHANNEL_DP
- MSG_FIBRECHANNELEX_DP
- MSG_1394_DP
- MSG_USB_DP (mapped to BDS_EFI_MESSAGE_USB_DEVICE_BOOT)
- MSG_USB_CLASS_DP
- MSG_USB_WWID_DP
- MSG_DEVICE_LOGICAL_UNIT_DP
- MSG_SATA_DP (mapped to BDS_EFI_MESSAGE_SATA_BOOT)
- MSG_I2O_DP
- MSG_MAC_ADDR_DP (mapped to BDS_EFI_MESSAGE_MAC_BOOT)
- MSG_IPv4_DP (mapped to BDS_EFI_MESSAGE_MAC_BOOT)
- MSG_IPv6_DP (mapped to BDS_EFI_MESSAGE_MAC_BOOT)
- MSG_INFINIBAND_DP
- MSG_UART_DP
- MSG_VENDOR_DP
- MSG_SASEX_DP
- MSG_ISCSI_DP
- MSG_VLAN_DP (mapped to BDS_EFI_MESSAGE_MAC_BOOT)

The not yet mapped subtypes are:
- MSG_FIBRECHANNEL_DP
- MSG_FIBRECHANNELEX_DP
- MSG_1394_DP
- MSG_USB_CLASS_DP
- MSG_USB_WWID_DP
- MSG_DEVICE_LOGICAL_UNIT_DP
- MSG_I2O_DP
- MSG_INFINIBAND_DP
- MSG_UART_DP
- MSG_VENDOR_DP
- MSG_SASEX_DP
- MSG_ISCSI_DP

The only subtype I can imagine is MSG_VENDOR_DP.


So, we could be tempted to answer question (b) with "append a node with
type/subtype = MESSAGING_DEVICE_PATH/MSG_VENDOR_DP". However, from UEFI
2.3.1+errC:

9.3.5.17 Vendor-Defined Messaging Device Path:

--------v--------
The following GUIDs are used with a Vendor-Defined Messaging Device Path
to describe the transport protocol for use with PC-ANSI, VT-100,
VT-100+, and VT-UTF8 terminals. Device paths can be constructed with
this node as the last node in the device path. The rest of the device
path describes the physical device that is being used to transmit and
receive data. The PC-ANSI, VT- 100, VT-100+, and VT-UTF8 GUIDs define
the format of the data that is being sent though the physical device.
Additional GUIDs can be generated to describe additional transport
protocols.
--------^--------

I don't think this fits virtio-blk.


Regarding question (a), I'll have to check in the specs (help
appreciated!) whether virtio-blk is required to produce a messaging
device path node at all. I can't (yet?) see why a messaging DP node
before the final HD(...) is a requirement.

... The UEFI 2.3.1+errC spec says in 9.3.5 Messaging Device Path:

--------v--------
This Device Path is used to describe the connection of devices outside
the resource domain of the system. This Device Path can describe
physical messaging information like SCSI ID, or abstract information
like networking protocol IP addresses.
--------^--------

In the DWG-2.3.1, section 3.9 says:

--------v--------
Messaging device path node: Used to describe devices on
industry-standard buses that are not directly accessible through
processor memory or processor I/O cycles. These devices are accessed by
the processor through one or more hardware bridge devices that translate
one industry-standard bus type to another industry-standard bus type.
This type of device path is used to describe devices such as SCSI, Fibre
Channel, 1394, USB, I2O, InfiniBand®, UARTs, and network agents.
--------^--------

These don't hold for virtio-blk. The virtio-blk device is in fact a PCI
device, and I recall that I approached the driver as one for an
individual PCI bus/dev/func that provides the block IO interface. (The
related DP node type is HARDWARE_DEVICE_PATH/HW_PCI_DP, which is printed
as Pci(...).) According to my comments in the driver, I was following
the following specs:

--------v--------
Specs relevant in the specific sense:
- UEFI Spec 2.3.1 + Errata C, 13.4 EFI PCI I/O Protocol
- Driver Writer's Guide for UEFI 2.3.1 v1.01, 18 PCI Driver Design
Guidelines, 18.3 PCI drivers.
--------^--------

UEFI 2.3.1+errC, 9.4.4 Hardware vs. Messaging Device Path Rules:

--------v--------
Hardware Device Paths are used to define paths on buses that have a
standard enumeration algorithm and that relate directly to the coherency
domain of the system. The coherency domain is defined as a global set of
resources that is visible to at least one processor in the system. In a
typical system this would include the processor memory space, IO space,
and PCI configuration space.

Messaging Device Paths are used to define paths on buses that have a
standard enumeration algorithm, but are not part of the global coherency
domain of the system. SCSI and Fibre Channel are examples of this kind
of bus. The Messaging Device Path can also be used to describe virtual
connections over network-style devices. An example would be the TCPI/IP
address of an internet connection.

Thus Hardware Device Path is used if the bus produces resources that
show up in the coherency resource domain of the system. A Message Device
Path is used if the bus consumes resources from the coherency domain and
produces resources out side the coherency domain of the system.
--------^--------

This puts virtio-blk firmly in hardware device path domain, as it is
accessed via PCI IO space and (pseudo-)physical memory.


... So why does BdsLibEnumerateAllBootOption() insist on a messaging
device path node? I guess the idea captured in there is that a device
hanging off a messaging device path is *conceptually* "removable", and
BdsLibEnumerateAllBootOption() wants to auto-generate boot options for
removable devices.

I think this isn't a bug in virtio-blk. I believe
BdsLibEnumerateAllBootOption() and BdsGetBootTypeFromDevicePath() face
an unexpected case where a system can be booted purely through PCI IO
space access.
Post by David Woodhouse
But since we're speaking of nvram, my plan for fixing that is probably
to use qemu's fw_cfg. I believe that fw_cfg "files" are actually
writeable from the guest, so we could use *that* for storing nvram
variables instead of the NvVars file on the EFI partition which, as
discussed, only works before the OS might have it mounted.
(fw_cfg entries seem to be writable indeed, with an optional qemu
callback to be invoked once the last byte in the entry is written.
Resizing of entries doesn't appear possible from a cursory look.)

Laszlo
Andrew Fish
2013-01-22 20:16:51 UTC
Permalink
Post by Laszlo Ersek
Post by David Woodhouse
No, I was avoiding the nvram variables altogether. This is an
"appliance" disk, with Grub installed at /efi/boot/bootx64.efi, with
no NvVars file on its EFI partition, which works fine when I just run
'qemu -hda efidisk.img'. I go into the boot menu, elect to boot from
the 'EFI Hard Drive' option that appears there, and it works. It does
not appear in the menu when it's attached as virtio, and that is based
*purely* on the device path of the block device. Nothing else.
It's getting clearer. The device path (excluding directories and the
PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)/HD(...)
whereas for virtio-blk it is
PciRoot(0x0)/Pci(0x6,0x3)/HD(...)
- type: MESSAGING_DEVICE_PATH,
- subtype: MSG_ATAPI_DP,
for which BdsGetBootTypeFromDevicePath() returns
BDS_EFI_MESSAGE_ATAPI_BOOT (before reaching the HD(...) node).
That in turn makes BdsLibEnumerateAllBootOption() auto-generate a boot
option (= call to BdsLibBuildOptionFromHandle()).
(a) Is it a bug in the virtio-blk driver, not to produce such a child
device path node? When I was writing the driver and reading the driver
writers' guide & the UEFI spec, it seemed like producing an extra child
node was not necessary.
You are correct there is nothing wrong with your driver.
Post by Laszlo Ersek
(b) If it *is* a bug in the virtio-blk driver, then a node of what
type/subtype should be appended, so that BdsLibEnumerateAllBootOption()
is happy with it? It is
- neither BDS_EFI_ACPI_FLOPPY_BOOT,
- nor BDS_EFI_MESSAGE_ATAPI_BOOT,
- nor BDS_EFI_MESSAGE_SATA_BOOT,
- nor BDS_EFI_MESSAGE_USB_DEVICE_BOOT,
- nor BDS_EFI_MESSAGE_SCSI_BOOT.
BDS_EFI_MESSAGE_MISC_BOOT would be an option. For that a child node of
- MSG_ATAPI_DP (mapped to BDS_EFI_MESSAGE_ATAPI_BOOT)
- MSG_SCSI_DP (mapped to BDS_EFI_MESSAGE_SCSI_BOOT)
- MSG_FIBRECHANNEL_DP
- MSG_FIBRECHANNELEX_DP
- MSG_1394_DP
- MSG_USB_DP (mapped to BDS_EFI_MESSAGE_USB_DEVICE_BOOT)
- MSG_USB_CLASS_DP
- MSG_USB_WWID_DP
- MSG_DEVICE_LOGICAL_UNIT_DP
- MSG_SATA_DP (mapped to BDS_EFI_MESSAGE_SATA_BOOT)
- MSG_I2O_DP
- MSG_MAC_ADDR_DP (mapped to BDS_EFI_MESSAGE_MAC_BOOT)
- MSG_IPv4_DP (mapped to BDS_EFI_MESSAGE_MAC_BOOT)
- MSG_IPv6_DP (mapped to BDS_EFI_MESSAGE_MAC_BOOT)
- MSG_INFINIBAND_DP
- MSG_UART_DP
- MSG_VENDOR_DP
- MSG_SASEX_DP
- MSG_ISCSI_DP
- MSG_VLAN_DP (mapped to BDS_EFI_MESSAGE_MAC_BOOT)
- MSG_FIBRECHANNEL_DP
- MSG_FIBRECHANNELEX_DP
- MSG_1394_DP
- MSG_USB_CLASS_DP
- MSG_USB_WWID_DP
- MSG_DEVICE_LOGICAL_UNIT_DP
- MSG_I2O_DP
- MSG_INFINIBAND_DP
- MSG_UART_DP
- MSG_VENDOR_DP
- MSG_SASEX_DP
- MSG_ISCSI_DP
The only subtype I can imagine is MSG_VENDOR_DP.
So, we could be tempted to answer question (b) with "append a node with
type/subtype = MESSAGING_DEVICE_PATH/MSG_VENDOR_DP". However, from UEFI
--------v--------
The following GUIDs are used with a Vendor-Defined Messaging Device Path
to describe the transport protocol for use with PC-ANSI, VT-100,
VT-100+, and VT-UTF8 terminals. Device paths can be constructed with
this node as the last node in the device path. The rest of the device
path describes the physical device that is being used to transmit and
receive data. The PC-ANSI, VT- 100, VT-100+, and VT-UTF8 GUIDs define
the format of the data that is being sent though the physical device.
Additional GUIDs can be generated to describe additional transport
protocols.
--------^--------
I don't think this fits virtio-blk.
Regarding question (a), I'll have to check in the specs (help
appreciated!) whether virtio-blk is required to produce a messaging
device path node at all. I can't (yet?) see why a messaging DP node
before the final HD(...) is a requirement.
--------v--------
This Device Path is used to describe the connection of devices outside
the resource domain of the system. This Device Path can describe
physical messaging information like SCSI ID, or abstract information
like networking protocol IP addresses.
--------^--------
--------v--------
Messaging device path node: Used to describe devices on
industry-standard buses that are not directly accessible through
processor memory or processor I/O cycles. These devices are accessed by
the processor through one or more hardware bridge devices that translate
one industry-standard bus type to another industry-standard bus type.
This type of device path is used to describe devices such as SCSI, Fibre
Channel, 1394, USB, I2O, InfiniBand®, UARTs, and network agents.
--------^--------
These don't hold for virtio-blk. The virtio-blk device is in fact a PCI
device, and I recall that I approached the driver as one for an
individual PCI bus/dev/func that provides the block IO interface. (The
related DP node type is HARDWARE_DEVICE_PATH/HW_PCI_DP, which is printed
as Pci(...).) According to my comments in the driver, I was following
--------v--------
- UEFI Spec 2.3.1 + Errata C, 13.4 EFI PCI I/O Protocol
- Driver Writer's Guide for UEFI 2.3.1 v1.01, 18 PCI Driver Design
Guidelines, 18.3 PCI drivers.
--------^--------
--------v--------
Hardware Device Paths are used to define paths on buses that have a
standard enumeration algorithm and that relate directly to the coherency
domain of the system. The coherency domain is defined as a global set of
resources that is visible to at least one processor in the system. In a
typical system this would include the processor memory space, IO space,
and PCI configuration space.
Messaging Device Paths are used to define paths on buses that have a
standard enumeration algorithm, but are not part of the global coherency
domain of the system. SCSI and Fibre Channel are examples of this kind
of bus. The Messaging Device Path can also be used to describe virtual
connections over network-style devices. An example would be the TCPI/IP
address of an internet connection.
Thus Hardware Device Path is used if the bus produces resources that
show up in the coherency resource domain of the system. A Message Device
Path is used if the bus consumes resources from the coherency domain and
produces resources out side the coherency domain of the system.
--------^--------
This puts virtio-blk firmly in hardware device path domain, as it is
accessed via PCI IO space and (pseudo-)physical memory.
... So why does BdsLibEnumerateAllBootOption() insist on a messaging
device path node?
It is a bug in the BDS. The BDS should do something smarter in the switch default case if the heuristic fails.
Post by Laszlo Ersek
I guess the idea captured in there is that a device
hanging off a messaging device path is *conceptually* "removable", and
BdsLibEnumerateAllBootOption() wants to auto-generate boot options for
removable devices.
I think this isn't a bug in virtio-blk. I believe
BdsLibEnumerateAllBootOption() and BdsGetBootTypeFromDevicePath() face
an unexpected case where a system can be booted purely through PCI IO
space access.
Post by David Woodhouse
But since we're speaking of nvram, my plan for fixing that is probably
to use qemu's fw_cfg. I believe that fw_cfg "files" are actually
writeable from the guest, so we could use *that* for storing nvram
variables instead of the NvVars file on the EFI partition which, as
discussed, only works before the OS might have it mounted.
(fw_cfg entries seem to be writable indeed, with an optional qemu
callback to be invoked once the last byte in the entry is written.
Resizing of entries doesn't appear possible from a cursory look.)
Laszlo
------------------------------------------------------------------------------
Master Visual Studio, SharePoint, SQL, ASP.NET, C# 2012, HTML5, CSS,
MVC, Windows 8 Apps, JavaScript and much more. Keep your skills current
with LearnDevNow - 3,200 step-by-step video tutorials by Microsoft
http://p.sf.net/sfu/learnnow-d2d
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
David Woodhouse
2013-01-22 20:42:58 UTC
Permalink
Post by Andrew Fish
Post by Laszlo Ersek
... So why does BdsLibEnumerateAllBootOption() insist on a messaging
device path node?
It is a bug in the BDS. The BDS should do something smarter in the
switch default case if the heuristic fails.
Thanks. That is the answer to my original question in the mail which
started this thread.

I may attempt to fix the BDS code. But then again, I also now realise
that this has *nothing* to do with the selection of devices for legacy
boot, which is a completely *separate* iteration over block devices,
with completely *different* bugs regarding which devices get included
and which don't :)

The legacy boot case is much worse, in fact. It *only* offers IDE
devices on the native IDE controllers, and the floppy. Nothing else gets
listed at all.
--
dwmw2
Andrew Fish
2013-01-22 21:04:53 UTC
Permalink
Post by David Woodhouse
Post by Andrew Fish
Post by Laszlo Ersek
... So why does BdsLibEnumerateAllBootOption() insist on a messaging
device path node?
It is a bug in the BDS. The BDS should do something smarter in the
switch default case if the heuristic fails.
Thanks. That is the answer to my original question in the mail which
started this thread.
I may attempt to fix the BDS code. But then again, I also now realise
that this has *nothing* to do with the selection of devices for legacy
boot, which is a completely *separate* iteration over block devices,
with completely *different* bugs regarding which devices get included
and which don't :)
The legacy boot case is much worse, in fact. It *only* offers IDE
devices on the native IDE controllers, and the floppy. Nothing else gets
listed at all.
Sounds like more "bad" heuristics.

I'm guessing you need a UI to select the BBS IPL Table (and that would cover BEV devices) and another UI to select BCV priority.

If you want to avoid the UI craziness it may be possible to detect all BEV devices and make boot options for each one. That way the BEV devices would at least show up in the boot selection menus.

The EFI device path node is basically BBS Device Type + BBS Status Flag + BBS String.

Thanks,

Andrew
David Woodhouse
2013-01-22 21:17:07 UTC
Permalink
Post by Andrew Fish
Sounds like more "bad" heuristics.
Yes. It's completely shagged.

When I first started putting the CSM bits together, the OVMF side mostly
seemed to Just Work™. So when things started to show problems, it was
reasonable to assume that in my ignorance, I had done something wrong on
the CSM side.

Having dug into it some more and with help from yourself and others, I
am perfectly comfortable now with the diagnosis that it's OVMF (and even
the CSM spec) which is broken in various ways, and I shall set about
trying to fix it properly, rather than staring at my CSM code and
wondering why the gods hate me.

Thanks :)
--
dwmw2
Laszlo Ersek
2013-01-22 15:21:24 UTC
Permalink
Post by David Woodhouse
When I have a virtio disk attached to a qemu VM, it doesn't appear in
the OVMF boot menu, so I can't boot from it. This is because the
BdsGetBootTypeFromDevicePath() function in GenericBdsLib.c only works on
the device path and isn't able to infer that 'PciRoot(0x0)/Pci(0x5,0x0)'
is a virtio disk. It could be *any* PCI device, if the path is all
you're looking at.
Should the VirtioBlk driver actually be registering a *child* of the Pci
device node, such as PciRoot(0x0)/Pci(0x5,0x0)/Virtio(0), so that we can
automatically work out what it is from the path? Or is there a better
way?
The virtio-blk driver implements the EFI_BLOCK_IO_PROTOCOL for
virtio-blk devices; that should be enough for enumerating the disk.

If, instead of selecting Boot Manager in the OVMF boot menu, you go to

Boot Maintenance Manager
Boot Options
Add Boot Option

then in the File Explorer appearing there, something like

PciRoot(0x0)/Pci(0x5,0x0)/HD(...)

shows up. (It can be navigated down to \EFI\BOOT\BOOTX64.EFI.)

The device path node that corresponds to the HD(...) substring is
constructed by code outside of the virtio-blk driver (on top of the
block IO protocol that virtio-blk produces). This HD(...) DP node is of
type MEDIA_DEVICE_PATH, subtype MEDIA_HARDDRIVE_DP (see
"DevPathToTextTable" and DevPathToTextHardDrive() in
"MdeModulePkg/Universal/DevicePathDxe/DevicePathToText.c").

The calls seem to advance as in:

PlatformBdsPolicyBehavior() [OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c]
BdsLibEnumerateAllBootOption() [IntelFrameworkModulePkg/Library/GenericBdsLib/BdsBoot.c]
BdsGetBootTypeFromDevicePath()
SetBootOrderFromQemu() [OvmfPkg/Library/PlatformBdsLib/QemuBootOrder.c]

BdsGetBootTypeFromDevicePath() seems to handle MEDIA_HARDDRIVE_DP. It
progresses through PciRoot(...) and Pci(...), and returns
BDS_EFI_MEDIA_HD_BOOT for type MEDIA_DEVICE_PATH, subtype
MEDIA_HARDDRIVE_DP.

However BdsLibEnumerateAllBootOption() chooses to ignore
BDS_EFI_MEDIA_HD_BOOT on purpose (unlike BDS_EFI_MESSAGE_SCSI_BOOT,
which BdsGetBootTypeFromDevicePath() returns for virtio-scsi devices).
See the comment on BdsLibEnumerateAllBootOption():

--------*--------
4. Fixed BlockIo - The boot option only points to a Fixed blockIo device,
like HardDisk.
These device should contain a *fixed* blockIo
protocol in their device handle.
BDS will skip fixed blockIo devices, and NOT
automatically create boot option for them. But BDS
will help to delete those fixed blockIo boot option,
whose description rule conflict with other auto-created
boot options.

[...]

This function will enumerate all possible boot device in the system, and
automatically create boot options for Network, Shell, Removable BlockIo,
and Non-BlockIo Simplefile devices.
--------*--------

Therefore the problem is not a missing child device path node; code
outside of virtio-blk appends the appropriate MEDIA_HARDDRIVE_DP node to
the device path. The problem is that BdsLibEnumerateAllBootOption()
skips MEDIA_HARDDRIVE_DP / BDS_EFI_MEDIA_HD_BOOT deliberately. I'm not
sure what the intent was.

(

Later on, SetBootOrderFromQemu() would retrieve the fw_cfg boot order,
translate the OpenFirmware device paths to UEFI device path prefixes,
reorder the list of enumerated boot options against matching device
path prefixes, and kill the rest of UEFI boot options. However
BdsLibEnumerateAllBootOption() doesn't return a boot option for
virtio-blk automatically, hence it doesn't matter if the fw_cfg boot
order would favor it.

Once a boot option for a virtio-blk device has been established
manually (down to the BOOTX64.EFI file), and if it doesn't conflict
with an auto-enumerated boot option, then it will be available to
SetBootOrderFromQemu().

(

If you set DEBUG_VERBOSE / 0x00400000 in
"gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel" in file
"OvmfPkg/OvmfPkgX64.dsc", then the Match() function in
"OvmfPkg/Library/PlatformBdsLib/QemuBootOrder.c" will log the boot
options enumerated by BdsLibEnumerateAllBootOption() as it tries to
match each OpenFirmware device path against them. Something like:

SetBootOrderFromQemu: FwCfg:
/***@i0cf8/***@7/***@0,0
SetBootOrderFromQemu: FwCfg: <end>
ParseOfwNode: DriverName="pci" UnitAddress="i0cf8" DeviceArguments=""
ParseOfwNode: DriverName="scsi" UnitAddress="7" DeviceArguments=""
ParseOfwNode: DriverName="disk" UnitAddress="0,0" DeviceArguments=""
TranslateOfwPath: success: "PciRoot(0x0)/Pci(0x7,0x0)/HD("
Match: against "PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)": no match
Match: against "PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x1)": no match
Match: against "MemoryMapped(0xB,0xDFC0E000,0xDFFBDFFF)/FvFile(C57AD6B7-0515-40A8-9D21-551652854E37)": no match
Match: against "PciRoot(0x0)/Pci(0x7,0x0)/HD(2,GPT,A2146CB9-2947-4342-9528-D9F4369CBEF4,0x96800,0x31800)/\EFI\Boot\bootx64.efi": match
TranslateOfwPath: no more nodes
EmuVariablesUpdatedCallback
Saved NV Variables to NvVars file
SetBootOrderFromQemu: setting BootOrder: success

)

)

Laszlo
Loading...