public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Laszlo Ersek <lersek@redhat.com>
To: spam collector <spamcollector@cableone.net>
Cc: edk2-devel@ml01.01.org
Subject: Re: OVMF.fd and placement of EfiBootServicesData
Date: Tue, 4 Oct 2016 19:22:34 +0200	[thread overview]
Message-ID: <94a76bd7-732c-c8f6-b8be-8175dd061b86@redhat.com> (raw)
In-Reply-To: <1747186652.93714260.1475599157436.JavaMail.zimbra@cableone.net>

On 10/04/16 18:39, spam collector wrote:

> [...] I was surprised to see that QEMU and/or
> OVMF requires the NvVars file to exist on the partition, else OVMF
> does not even show the shell, even on a perfectly valid partitioned
> image.  I don't know if this is QEMU's fault or OVMF's fault.  If 
> the NvVars file does not exist, is it the desired option to fail 
> the loading of the shell? If the file does not exist, can OVMF use 
> default values and still load the shell?

OVMF does not require the NvVars file to exist. The NvVars file is only
used / relied on when the QEMU command line is incorrect, that is, when
it does not configure the pflash chip(s) for storing the firmware binary
and/or the variable store.

* The command line that you are likely used to goes like this:

  qemu-system-x86_64 -bios OVMF.fd ...

This is broken. Don't use it. The OVMF.fd file in the above usage is a
unified file (1MB or 2MB in size), containing both the firmware binary
and an empty (pre-formatted) variable store. The entire file is mapped
into guest memory as ROM. The variable store area is not writeable,
hence OVMF falls back to the NvVars based emulation. That emulation is
not entirely compatible with the UEFI spec. Don't use the above command
line.

* The first stage improvement is the following command line:

  qemu-system-x86_64 -pflash OVMF.fd

The OVMF.fd file has the same characteristics as above, but the same
stuff is mapped as a programmable pflash chip into guest memory. It
means that the guest memory area in question *normally* reads and
executes like normal RAM, but if you write to it (that is, if OVMF's
flash driver writes to it), it is flipped into "programming mode", where
you can read and write the device using various "commands". One of those
commands flips the device back to "normal" mode.

In this mode, the NvVars-based emulation is not used, and the variable
store / variable services will work as defined by the UEFI spec.

The problem with this approach is that the OVMF.fd file contains both a
firmware binary and a live variable store. You cannot upgrade the
firmware binary without losing the VM's long-term, non-volatile variables.

* The second stage improvement is the following command line:

  # create the virtual machine's private variable store from the
  # pristine variable store template, if the former doesn't exist yet,
  # or has been lost for some reason
  if ! [ -e GUEST_1_VARS.fd ]; then
    cp OVMF_VARS.fd GUEST_1_VARS.fd
  fi

  qemu-system-x86_64 \
    -drive if=pflash,format=raw,unit=0,readonly,file=OVMF_CODE.fd \
    -drive if=pflash,format=raw,unit=1,file=GUEST_1_VARS.fd \
    ...

In this case, the OVMF_CODE.fd and OVMF_VARS.fd are used separately.
(You can find both files under the Build directory; plus Gerd Hoffmann's
RPM files <https://www.kraxel.org/repos/> also package them.)

The OVMF_CODE.fd file is the read-only firmware binary (1920KB in size),
whereas the OVMF_VARS.fd file is an empty, preformatted variable store
*template* (128KB in size). The VM never uses OVMF_VARS.fd directly, you
(or the management layer, see below) uses that file only to instantiate
VM-specific varstores.

This pattern allows you to upgrade your firmware binary without messing
up each VM's own variable store.

The issue with this method is that a malicious guest OS can still poke
at the pflash chip (with unit=1) directly. If you have Secure Boot
enabled, you don't want the guest OS to tamper with the authenticated
UEFI variables in the varstore that contain certificates and such.

* The third stage improvement is SMM. For that, build OVMF like this
(I'm simplifying a lot, but this email should come to an end sometime):

  build -a IA32 -a X64 -p OvmfPkg/OvmfPkgIa32X64.dsc \
    -D SMM_REQUIRE -D SECURE_BOOT_ENABLE \
    ...

and run OVMF like this:

  # create the virtual machine's private variable store from the
  # pristine variable store template, if the former doesn't exist yet,
  # or has been lost for some reason
  if ! [ -e GUEST_1_VARS.fd ]; then
    cp OVMF_VARS.fd GUEST_1_VARS.fd
  fi

  qemu-system-x86_64 \
    -machine q35,smm=on \
    -global driver=cfi.pflash01,property=secure,value=on \
    -drive if=pflash,format=raw,unit=0,readonly,file=OVMF_CODE.fd \
    -drive if=pflash,format=raw,unit=1,file=GUEST_1_VARS.fd \

In this case, the guest OS won't be able to write to the variable store
directly. It will have to invoke the runtime UEFI variable services,
which will raise an SMI / enter SMM. The SMI handler (part of the
firmware) will validate / authenticate the request parameters, and
perform the modifications if they are valid.

* The fourth stage improvement is to use libvirt, which will handle all
of the above for you, assuming you give it the right domain XML.
(Virt-manager / virt-install can create domain XMLs that need no or
minimal further customizations.)

* In some builds / distributions of OVMF, the UEFI shell is excluded
from the firmware image, and it is provided on a small ISO image. This
is justified by two things:

(a) first, this way you can use QEMU's

  -device virtio-scsi-pci,id=scsi0 \
  -drive if=none,format=raw,readonly,file=UefiShell.iso \
  -device scsi-hd,bus=scsi0.0,bootindex=N \

options to boot the UEFI shell directly -- bootindex is not usable for
modules built into the firmware binary; QEMU's "bootorder" fw_cfg
doesn't have enough expressive power for that --,

(b) second, we've been advised that the UEFI shell can be used to
circumvent Secure Boot. By moving the shell binary out of the firmware
image, to a CD-ROM, as an external *unsigned* EFI application, it cannot
be launched when Secure Boot is enabled.

* Should your installed guest (such as a UEFI GNU/Linux distro, or a
UEFI Windows edition) lose the variable store (for example because you
unwittingly delete GUEST_1_VARS.fd), the UEFI spec defines a method for
the (guest) OS to recreate a sane set of boot options / boot order,
after you (or libvirt) recreate GUEST_1_VARS.fd from OVMF_VARS.fd again.
For Linux at least, the keyword here is "fallback.efi"; you can read
about it at
<http://blog.uncooperative.org/blog/2014/02/06/the-efi-system-partition/>.

> 
> I ask because I spent a little while thinking that my GPT partitioned
> image was in error since I could not get a shell to load.  Once I
> added the NvVars file to the root directory, the shell loaded as
> expected.  If NvVars is not compatible with UEFI, which I don't
> believe it is,

(which you are correct about,)

> should OVMF still require it to boot to the shell?

No, and it doesn't. Use one of the above pflash-based methods please.

Since we're into boot option land anyway, I should mention the following
section of the OVMF whitepaper:

  Select features |
    Platform-specific boot policy

If you never use the "bootindex=N" QEMU device property (or libvirt's
equivalent <boot order='N'/> element), then OVMF will not touch your
UEFI boot options (Boot####, BootOrder), beyond regenerating options for
all possible bootable devices at the end of your current BootOrder.

However, if you want to influence the UEFI boot order from the QEMU
command line (or the libvirt domain XML), you can. There are a few
caveats; please read the above whitepaper section for understanding them.

> Maybe if I study more on the pflash option and use it, OVMF will
> not require the NvVars file.

Exactly.

> 
> I ask with the intent of better understanding the reason it
> requires the NvVars file, as opposed to being sarcastic as it may
> sound. :-)

It doesn't sound sarcastic, don't worry. ;)

NvVars dates back to a time when QEMU didn't have pflash yet, and
(consequently) OVMF didn't have a driver for QEMU's pflash. Now that
pflash can be used, it (and only it) should be used too.

OVMF also runs on Xen. Xen doesn't provide pflash emulation, so the
NvVars fallback is still in active use on Xen.

Thanks
Laszlo


  reply	other threads:[~2016-10-04 17:22 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <1063482308.87469389.1475464272414.JavaMail.zimbra@cableone.net>
2016-10-03  3:16 ` OVMF.fd and placement of EfiBootServicesData spam collector
2016-10-03  9:42   ` Laszlo Ersek
2016-10-04  2:45     ` spam collector
2016-10-04  7:52       ` Laszlo Ersek
2016-10-04 16:39         ` spam collector
2016-10-04 17:22           ` Laszlo Ersek [this message]
2016-10-05  2:47             ` spam collector
2016-10-05 15:28               ` Laszlo Ersek
2016-10-06  3:05                 ` spam collector
2016-10-06  7:39                   ` Laszlo Ersek
2016-10-07  3:11                     ` spam collector
2016-10-07 15:08                       ` Laszlo Ersek
2016-10-06  5:45                 ` spam collector
2016-10-06  7:47                   ` Laszlo Ersek

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=94a76bd7-732c-c8f6-b8be-8175dd061b86@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