diff mbox

[edk2] OvmfPkg: split the variable store to a separate file

Message ID 1385072499-31367-1-git-send-email-lersek@redhat.com
State New
Headers show

Commit Message

Laszlo Ersek Nov. 21, 2013, 10:21 p.m. UTC
Split the variable store off to a separate file when SPLIT_VARSTORE is
defined.

Even in this case, the preexistent PCDs' values don't change. Qemu must
take care of contiguously mapping NVVARSTORE.fd + OVMF.fd so that when
concatenated they end exactly at 4GB.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
 Generated with 8 lines of context for easier in-patch verification with
 the help of the clipboard.

 OvmfPkg/OvmfPkgIa32.fdf    | 48 ++++++++++++++++++++++++++++++++++++++++++++++
 OvmfPkg/OvmfPkgIa32X64.fdf | 48 ++++++++++++++++++++++++++++++++++++++++++++++
 OvmfPkg/OvmfPkgX64.fdf     | 48 ++++++++++++++++++++++++++++++++++++++++++++++
 OvmfPkg/README             | 16 ++++++++++++++++
 4 files changed, 160 insertions(+)

Comments

Paolo Bonzini Nov. 22, 2013, 9:27 a.m. UTC | #1
Il 21/11/2013 23:21, Laszlo Ersek ha scritto:
> Split the variable store off to a separate file when SPLIT_VARSTORE is
> defined.
> 
> Even in this case, the preexistent PCDs' values don't change. Qemu must
> take care of contiguously mapping NVVARSTORE.fd + OVMF.fd so that when
> concatenated they end exactly at 4GB.
> 
> Contributed-under: TianoCore Contribution Agreement 1.0
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>

It's good that this is so easy to do.

The obvious question is, what happens if you only pass only OVMF.fd
(which would be less than 2MB in size, right)?

Also, I see a command line compatibility problem, especially if one
wants OVMF.fd to become the default firmware.  Then, having to specify
it again on the command line would be strange.

Paolo
Laszlo Ersek Nov. 22, 2013, 11:46 a.m. UTC | #2
On 11/22/13 10:27, Paolo Bonzini wrote:
> Il 21/11/2013 23:21, Laszlo Ersek ha scritto:
>> Split the variable store off to a separate file when SPLIT_VARSTORE is
>> defined.
>>
>> Even in this case, the preexistent PCDs' values don't change. Qemu must
>> take care of contiguously mapping NVVARSTORE.fd + OVMF.fd so that when
>> concatenated they end exactly at 4GB.
>>
>> Contributed-under: TianoCore Contribution Agreement 1.0
>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> 
> It's good that this is so easy to do.
> 
> The obvious question is, what happens if you only pass only OVMF.fd
> (which would be less than 2MB in size, right)?

Yes, when -D SPLIT_VARSTORE is passed, then NVVARSTORE.fd is built in
addition, and is 128KB in size, and OVMF.fd becomes 2MB-128KB == 1920KB
in size (unless you also passed -D FD_SIZE_1MB, in which case it's 896KB).

If you only pass the split OVMF.fd with -pflash to qemu, then it will be
mapped into the same place: [4GB-1920KB .. 4GB[.

It will scan the first 4KB (the first PcdOvmfFirmwareBlockSize bytes) at
4GB-2048KB -- ie. where NVVARSTORE would have been mapped had you not
forgotten to pass it.

OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c, QemuFlashDetected():

  for (Offset = 0; Offset < mFdBlockSize; Offset++) {
    Ptr = QemuFlashPtr (0, Offset);
    ProbeUint8 = *Ptr;
    if (ProbeUint8 != CLEAR_STATUS_CMD &&
        ProbeUint8 != READ_STATUS_CMD &&
        ProbeUint8 != CLEARED_ARRAY_STATUS) {
      break;
    }
  }

  if (Offset >= mFdBlockSize) {
    DEBUG ((EFI_D_INFO, "QEMU Flash: Failed to find probe location\n"));
    return FALSE;
  }

It looks for a byte in [4GB-2048KB .. 4GB-2044KB[ that's different from
all of those values. CLEARED_ARRAY_STATUS is zero. The flash driver will
not install, and the on-disk NvVars emulation will be enabled. The guest
should then boot with this original NvVars emulation.

It does in my testing anyway; this is the OVMF log:

  QEMU Flash: Failed to find probe location
  QEMU flash was not detected. Writable FVB is not being installed.
  [...]
  FsAccess.c: LoadNvVarsFromFs
  [...]

> Also, I see a command line compatibility problem, especially if one
> wants OVMF.fd to become the default firmware.

I don't understand. If you use the un-split build, you use the original
command line (single -pflash or -drive if=pflash option).

If you use the split build, then you:
- extend the first -drive if=pflash option with ",readonly" -- this is
optional but recommended,
- you add a second option after the first, pointing it to NVVARSTORE.fd
(ie. its VM-specific, private copy).

> Then, having to specify
> it again on the command line would be strange.

You don't specify OVMF.fd twice. The second option refers to NVVARSTORE.fd.

I think I don't fully understand your point.

Do you want any switching between un-split OVMF.fd and split
(OVMF.fd+NVVARSTORE.fd) to be transparent to the qemu command line user?
(Be it a person or libvirt?)

Laszlo
Paolo Bonzini Nov. 22, 2013, 11:56 a.m. UTC | #3
Il 22/11/2013 12:46, Laszlo Ersek ha scritto:
>> Also, I see a command line compatibility problem, especially if one
>> > wants OVMF.fd to become the default firmware.
> I don't understand. If you use the un-split build, you use the original
> command line (single -pflash or -drive if=pflash option).
> 
> If you use the split build, then you:
> - extend the first -drive if=pflash option with ",readonly" -- this is
> optional but recommended,
> - you add a second option after the first, pointing it to NVVARSTORE.fd
> (ie. its VM-specific, private copy).

Suppose OVMF.fd is already the default.  To add a non-volatile store,
you would have to do one of the following:

* -pflash /path/to/OVMF.fd -pflash NVVARSTORE.fd


Or alternatively, pc and q35 could use the current semantics forever.
UEFI-by-default will be tied to a separate machine type (pc-uefi, or
q35-uefi, or a different chipset) where -bios will also create a
cfi_pflash01 device and all pflash drives will be mapped below the
BIOS's.  So you would have one of the following:

* -M pc -pflash /path/to/OVMF.fd -pflash NVVARSTORE.fd

* -M pc-uefi -pflash NVVARSTORE.fd

> You don't specify OVMF.fd twice.

I meant the first time is inside QEMU, the second is on the command line.

> I think I don't fully understand your point.

I probably didn't express it well, also because I have no real idea to
offer (I don't like the "-M pc-uefi" either).

Paolo
Laszlo Ersek Nov. 22, 2013, 12:12 p.m. UTC | #4
On 11/22/13 12:56, Paolo Bonzini wrote:
> Il 22/11/2013 12:46, Laszlo Ersek ha scritto:
>>> Also, I see a command line compatibility problem, especially if one
>>>> wants OVMF.fd to become the default firmware.
>> I don't understand. If you use the un-split build, you use the original
>> command line (single -pflash or -drive if=pflash option).
>>
>> If you use the split build, then you:
>> - extend the first -drive if=pflash option with ",readonly" -- this is
>> optional but recommended,
>> - you add a second option after the first, pointing it to NVVARSTORE.fd
>> (ie. its VM-specific, private copy).
> 
> Suppose OVMF.fd is already the default.  To add a non-volatile store,
> you would have to do one of the following:
> 
> * -pflash /path/to/OVMF.fd -pflash NVVARSTORE.fd
> 
> 
> Or alternatively, pc and q35 could use the current semantics forever.
> UEFI-by-default will be tied to a separate machine type (pc-uefi, or
> q35-uefi, or a different chipset) where -bios will also create a
> cfi_pflash01 device and all pflash drives will be mapped below the
> BIOS's.  So you would have one of the following:
> 
> * -M pc -pflash /path/to/OVMF.fd -pflash NVVARSTORE.fd
> 
> * -M pc-uefi -pflash NVVARSTORE.fd
> 
>> You don't specify OVMF.fd twice.
> 
> I meant the first time is inside QEMU, the second is on the command line.
> 
>> I think I don't fully understand your point.
> 
> I probably didn't express it well, also because I have no real idea to
> offer (I don't like the "-M pc-uefi" either).

Ah sorry, I get it now. You'd change the default bios in qemu itself
(that would be the first specification), to OVMF.fd, and then adding
NVVARSTORE.fd on the command line would be the second specification.

I didn't think of this because I didn't consider changing the bios
default in qemu at all. Now that you mentioned it, my first reaction was
"use a new machine type" :)

I realize we're currently changing (refreshing) SeaBIOS builds without
introducing new machine types all the time. Maybe OVMF would justify a
change (if you indeed want to change the in-qemu default).

Hm... If you just change the default bios file name in qemu, that will
still result in an (implicit) -bios option. Whereas for
OVMF.fd+NVVARSTORE.fd, you need zero -bios options, and two -pflash
options. (Considering even a single OVMF.fd, you'd still pass it with
one -pflash option and zero -bios options, otherwise you'd have no
chance at all at a flash variable store.)

Currently, adding (one or more) -pflash option(s) on the command line,
for the PCI-enabled pc machine type(s), makes any -bios option a no-op
-- old_pc_system_rom_init() is not called.

So, I think if you want to change the default in qemu, changing just the
bios filename wouldn't be enough, it might have to include changing from
-bios to -pflash as well. But this would certainly need a new machine
type, no?

(What I managed (not) to describe with oh so many words is basically "pc
and q35 could use the current semantics forever", ie. your 2nd and 3rd
alternatives.)

Thanks,
Laszlo
Jordan Justen Nov. 22, 2013, 5:37 p.m. UTC | #5
On Thu, Nov 21, 2013 at 2:21 PM, Laszlo Ersek <lersek@redhat.com> wrote:
> Split the variable store off to a separate file when SPLIT_VARSTORE is
> defined.

Is the goal to allow the central OVMF to be updated so the VM will
automatically be running the newest firmware? (While preserving
variables)

I think in your scenario, you are assuming some VM manager will build
the command line parameters. But, couldn't the VM manager also merge
flash updates into the *single* VM specific flash image? Then QEMU
would not need to be changed.

This might also enable a 'feature' where the particular VM instance
can choose to not update the firmware when the central OVMF is
updated.

If we decided splitting was a good thing, then it would be nice to
just always build the split and full images. I'm not sure .fdf can
handle this though. Seems like build.sh could be tweaked if .fdf is
not up to the task.

-Jordan

> Even in this case, the preexistent PCDs' values don't change. Qemu must
> take care of contiguously mapping NVVARSTORE.fd + OVMF.fd so that when
> concatenated they end exactly at 4GB.
>
> Contributed-under: TianoCore Contribution Agreement 1.0
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> ---
>  Generated with 8 lines of context for easier in-patch verification with
>  the help of the clipboard.
>
>  OvmfPkg/OvmfPkgIa32.fdf    | 48 ++++++++++++++++++++++++++++++++++++++++++++++
>  OvmfPkg/OvmfPkgIa32X64.fdf | 48 ++++++++++++++++++++++++++++++++++++++++++++++
>  OvmfPkg/OvmfPkgX64.fdf     | 48 ++++++++++++++++++++++++++++++++++++++++++++++
>  OvmfPkg/README             | 16 ++++++++++++++++
>  4 files changed, 160 insertions(+)
>
> diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
> index c50709c..310d6a9 100644
> --- a/OvmfPkg/OvmfPkgIa32.fdf
> +++ b/OvmfPkg/OvmfPkgIa32.fdf
> @@ -23,31 +23,51 @@
>  #
>  [Defines]
>  !if $(TARGET) == RELEASE
>  !ifndef $(FD_SIZE_2MB)
>  DEFINE FD_SIZE_1MB=
>  !endif
>  !endif
>
> +!ifdef $(SPLIT_VARSTORE)
> +!ifdef $(FD_SIZE_1MB)
> +[FD.NvVarStore]
> +BaseAddress   = 0xFFF00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
> +SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = 0x00100000
> +Size          = 0x20000
> +ErasePolarity = 1
> +BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
> +NumBlocks     = 0x20
> +!else
> +[FD.NvVarStore]
> +BaseAddress   = 0xFFE00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
> +SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = 0x00200000
> +Size          = 0x20000
> +ErasePolarity = 1
> +BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
> +NumBlocks     = 0x20
> +!endif
> +!else
>  !ifdef $(FD_SIZE_1MB)
>  [FD.OVMF]
>  BaseAddress   = 0xFFF00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
>  Size          = 0x00100000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize
>  ErasePolarity = 1
>  BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
>  NumBlocks     = 0x100
>  !else
>  [FD.OVMF]
>  BaseAddress   = 0xFFE00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
>  Size          = 0x00200000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize
>  ErasePolarity = 1
>  BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
>  NumBlocks     = 0x200
>  !endif
> +!endif
>
>  0x00000000|0x0000e000
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
>  #NV_VARIABLE_STORE
>  DATA = {
>    ## This is the EFI_FIRMWARE_VOLUME_HEADER
>    # ZeroVector []
>    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> @@ -106,30 +126,58 @@ DATA = {
>    # WriteQueueSize: UINT64
>    0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
>  }
>
>  0x00010000|0x00010000
>  #NV_FTW_SPARE
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
>
> +!ifdef $(SPLIT_VARSTORE)
> +!ifdef $(FD_SIZE_1MB)
> +[FD.OVMF]
> +BaseAddress   = 0xFFF20000
> +Size          = 0x000E0000
> +ErasePolarity = 1
> +BlockSize     = 0x1000
> +NumBlocks     = 0xE0
> +
> +0x00000000|0x000CC000
> +FV = FVMAIN_COMPACT
> +0x000CC000|0x14000
> +FV = SECFV
> +!else
> +[FD.OVMF]
> +BaseAddress   = 0xFFE20000
> +Size          = 0x001E0000
> +ErasePolarity = 1
> +BlockSize     = 0x1000
> +NumBlocks     = 0x1E0
> +
> +0x00000000|0x001AC000
> +FV = FVMAIN_COMPACT
> +0x001AC000|0x34000
> +FV = SECFV
> +!endif
> +!else
>  !ifdef $(FD_SIZE_1MB)
>  0x00020000|0x000CC000
>  FV = FVMAIN_COMPACT
>
>  0x000EC000|0x14000
>  FV = SECFV
>
>  !else
>  0x00020000|0x001AC000
>  FV = FVMAIN_COMPACT
>
>  0x001CC000|0x34000
>  FV = SECFV
>  !endif
> +!endif
>
>  ################################################################################
>
>  [FD.MEMFD]
>  BaseAddress   = 0x800000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfMemFvBase
>  Size          = 0x800000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfMemFvSize
>  ErasePolarity = 1
>  BlockSize     = 0x10000
> diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
> index d65f40f..8877a89 100644
> --- a/OvmfPkg/OvmfPkgIa32X64.fdf
> +++ b/OvmfPkg/OvmfPkgIa32X64.fdf
> @@ -23,31 +23,51 @@
>  #
>  [Defines]
>  !if $(TARGET) == RELEASE
>  !ifndef $(FD_SIZE_2MB)
>  DEFINE FD_SIZE_1MB=
>  !endif
>  !endif
>
> +!ifdef $(SPLIT_VARSTORE)
> +!ifdef $(FD_SIZE_1MB)
> +[FD.NvVarStore]
> +BaseAddress   = 0xFFF00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
> +SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = 0x00100000
> +Size          = 0x20000
> +ErasePolarity = 1
> +BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
> +NumBlocks     = 0x20
> +!else
> +[FD.NvVarStore]
> +BaseAddress   = 0xFFE00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
> +SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = 0x00200000
> +Size          = 0x20000
> +ErasePolarity = 1
> +BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
> +NumBlocks     = 0x20
> +!endif
> +!else
>  !ifdef $(FD_SIZE_1MB)
>  [FD.OVMF]
>  BaseAddress   = 0xFFF00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
>  Size          = 0x00100000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize
>  ErasePolarity = 1
>  BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
>  NumBlocks     = 0x100
>  !else
>  [FD.OVMF]
>  BaseAddress   = 0xFFE00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
>  Size          = 0x00200000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize
>  ErasePolarity = 1
>  BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
>  NumBlocks     = 0x200
>  !endif
> +!endif
>
>  0x00000000|0x0000e000
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
>  #NV_VARIABLE_STORE
>  DATA = {
>    ## This is the EFI_FIRMWARE_VOLUME_HEADER
>    # ZeroVector []
>    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> @@ -106,30 +126,58 @@ DATA = {
>    # WriteQueueSize: UINT64
>    0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
>  }
>
>  0x00010000|0x00010000
>  #NV_FTW_SPARE
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
>
> +!ifdef $(SPLIT_VARSTORE)
> +!ifdef $(FD_SIZE_1MB)
> +[FD.OVMF]
> +BaseAddress   = 0xFFF20000
> +Size          = 0x000E0000
> +ErasePolarity = 1
> +BlockSize     = 0x1000
> +NumBlocks     = 0xE0
> +
> +0x00000000|0x000CC000
> +FV = FVMAIN_COMPACT
> +0x000CC000|0x14000
> +FV = SECFV
> +!else
> +[FD.OVMF]
> +BaseAddress   = 0xFFE20000
> +Size          = 0x001E0000
> +ErasePolarity = 1
> +BlockSize     = 0x1000
> +NumBlocks     = 0x1E0
> +
> +0x00000000|0x001AC000
> +FV = FVMAIN_COMPACT
> +0x001AC000|0x34000
> +FV = SECFV
> +!endif
> +!else
>  !ifdef $(FD_SIZE_1MB)
>  0x00020000|0x000CC000
>  FV = FVMAIN_COMPACT
>
>  0x000EC000|0x14000
>  FV = SECFV
>
>  !else
>  0x00020000|0x001AC000
>  FV = FVMAIN_COMPACT
>
>  0x001CC000|0x34000
>  FV = SECFV
>  !endif
> +!endif
>
>  ################################################################################
>
>  [FD.MEMFD]
>  BaseAddress   = 0x800000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfMemFvBase
>  Size          = 0x800000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfMemFvSize
>  ErasePolarity = 1
>  BlockSize     = 0x10000
> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
> index 751b94b..81cfff6 100644
> --- a/OvmfPkg/OvmfPkgX64.fdf
> +++ b/OvmfPkg/OvmfPkgX64.fdf
> @@ -23,31 +23,51 @@
>  #
>  [Defines]
>  !if $(TARGET) == RELEASE
>  !ifndef $(FD_SIZE_2MB)
>  DEFINE FD_SIZE_1MB=
>  !endif
>  !endif
>
> +!ifdef $(SPLIT_VARSTORE)
> +!ifdef $(FD_SIZE_1MB)
> +[FD.NvVarStore]
> +BaseAddress   = 0xFFF00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
> +SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = 0x00100000
> +Size          = 0x20000
> +ErasePolarity = 1
> +BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
> +NumBlocks     = 0x20
> +!else
> +[FD.NvVarStore]
> +BaseAddress   = 0xFFE00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
> +SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = 0x00200000
> +Size          = 0x20000
> +ErasePolarity = 1
> +BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
> +NumBlocks     = 0x20
> +!endif
> +!else
>  !ifdef $(FD_SIZE_1MB)
>  [FD.OVMF]
>  BaseAddress   = 0xFFF00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
>  Size          = 0x00100000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize
>  ErasePolarity = 1
>  BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
>  NumBlocks     = 0x100
>  !else
>  [FD.OVMF]
>  BaseAddress   = 0xFFE00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
>  Size          = 0x00200000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize
>  ErasePolarity = 1
>  BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
>  NumBlocks     = 0x200
>  !endif
> +!endif
>
>  0x00000000|0x0000e000
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
>  #NV_VARIABLE_STORE
>  DATA = {
>    ## This is the EFI_FIRMWARE_VOLUME_HEADER
>    # ZeroVector []
>    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> @@ -106,30 +126,58 @@ DATA = {
>    # WriteQueueSize: UINT64
>    0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
>  }
>
>  0x00010000|0x00010000
>  #NV_FTW_SPARE
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
>
> +!ifdef $(SPLIT_VARSTORE)
> +!ifdef $(FD_SIZE_1MB)
> +[FD.OVMF]
> +BaseAddress   = 0xFFF20000
> +Size          = 0x000E0000
> +ErasePolarity = 1
> +BlockSize     = 0x1000
> +NumBlocks     = 0xE0
> +
> +0x00000000|0x000CC000
> +FV = FVMAIN_COMPACT
> +0x000CC000|0x14000
> +FV = SECFV
> +!else
> +[FD.OVMF]
> +BaseAddress   = 0xFFE20000
> +Size          = 0x001E0000
> +ErasePolarity = 1
> +BlockSize     = 0x1000
> +NumBlocks     = 0x1E0
> +
> +0x00000000|0x001AC000
> +FV = FVMAIN_COMPACT
> +0x001AC000|0x34000
> +FV = SECFV
> +!endif
> +!else
>  !ifdef $(FD_SIZE_1MB)
>  0x00020000|0x000CC000
>  FV = FVMAIN_COMPACT
>
>  0x000EC000|0x14000
>  FV = SECFV
>
>  !else
>  0x00020000|0x001AC000
>  FV = FVMAIN_COMPACT
>
>  0x001CC000|0x34000
>  FV = SECFV
>  !endif
> +!endif
>
>  ################################################################################
>
>  [FD.MEMFD]
>  BaseAddress   = 0x800000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfMemFvBase
>  Size          = 0x800000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfMemFvSize
>  ErasePolarity = 1
>  BlockSize     = 0x10000
> diff --git a/OvmfPkg/README b/OvmfPkg/README
> index f2c2fc7..6f7dc38 100644
> --- a/OvmfPkg/README
> +++ b/OvmfPkg/README
> @@ -48,16 +48,19 @@ Update Conf/target.txt TARGET_ARCH based on the .dsc file:
>  Following the edk2 build process, you will find the OVMF binaries
>  under the $WORKSPACE/Build/*/*/FV directory.  The actual path will
>  depend on how your build is configured.  You can expect to find
>  these binary outputs:
>  * OVMF.FD
>    - Please note!  This filename has changed.  Older releases used OVMF.Fv.
>  * OvmfVideo.rom
>    - This file is not built separately any longer, starting with svn r13520.
> +* NVVARSTORE.fd
> +  - This file is only produced when -D SPLIT_VARSTORE is passed to the build
> +    utility. (See more under "OVMF Flash Layout".)
>
>  More information on building OVMF can be found at:
>
>  http://sourceforge.net/apps/mediawiki/tianocore/index.php?title=How_to_build_OVMF
>
>  === RUNNING OVMF on QEMU ===
>
>  * QEMU 0.9.1 or later is required.
> @@ -211,16 +214,29 @@ OVMF supports building a 1MB or a 2MB flash image. The base address for
>  a 1MB image in QEMU physical memory is 0xfff00000. The base address for
>  a 2MB image is 0xffe00000.
>
>  The code in SECFV locates FVMAIN_COMPACT, and decompresses the
>  main firmware (MAINFV) into RAM memory at address 0x800000. The
>  remaining OVMF firmware then uses this decompressed firmware
>  volume image.
>
> +If -D SPLIT_VARSTORE has been passed to the build utility, then the address
> +(base + 0x20000) splits the build output in two parts. The address range
> +starting at (base + 0x20000) is covered by OVMF.fd (up to 4GB), while the range
> +below it (from (base) to (base + 0x20000)) is covered by NVVARSTORE.fd.
> +
> +This is useful for centrally managing the binary part (OVMF.fd) on a host
> +system, while allowing virtual machines to keep their private variable stores.
> +Qemu-1.8 is expected to support this use case with the following options
> +(specified in this order):
> +
> +      -drive file=/central/OVMF.fd,if=pflash,format=raw,readonly \
> +      -drive file=/images/NVVARSTORE.fd.vm1,if=pflash,format=raw
> +
>  === UNIXGCC Debug ===
>
>  If you build with the UNIXGCC toolchain, then debugging will be disabled
>  due to larger image sizes being produced by the UNIXGCC toolchain. The
>  first choice recommendation is to use GCC44 or newer instead.
>
>  If you must use UNIXGCC, then you can override the build options for
>  particular libraries and modules in the .dsc to re-enable debugging
> --
> 1.8.3.1
>
>
Laszlo Ersek Nov. 22, 2013, 6:43 p.m. UTC | #6
On 11/22/13 18:37, Jordan Justen wrote:
> On Thu, Nov 21, 2013 at 2:21 PM, Laszlo Ersek <lersek@redhat.com> wrote:
>> Split the variable store off to a separate file when SPLIT_VARSTORE is
>> defined.
> 
> Is the goal to allow the central OVMF to be updated so the VM will
> automatically be running the newest firmware? (While preserving
> variables)

Yes.

In some distros this is what happens (*) when you uprage the
seabios(-bin) package on the host system. When you reboot any VM on it,
it will see the upgraded bios.

(*) Except of course you have no variable store.

The bios binary (the file belonging to its containing package) is also
owned by root and has some nice file mode bits and SELinux permissions:

$ ls -lL /usr/share/qemu-kvm/bios.bin
-rw-r--r--. 1 root root 131072 2013-11-20 14:55:29 +0100
/usr/share/qemu-kvm/bios.bin

$ ls -lLZ /usr/share/qemu-kvm/bios.bin
-rw-r--r--. root root system_u:object_r:usr_t:s0
/usr/share/qemu-kvm/bios.bin

These are distinctly different from what libvirt sets for the private
image files that underneath the VM's disks.

OVMF.fd would correspond to "bios.bin" above, and NVVARSTORE.fd is a
private disk file.

> I think in your scenario, you are assuming some VM manager will build
> the command line parameters. But, couldn't the VM manager also merge
> flash updates into the *single* VM specific flash image? Then QEMU
> would not need to be changed.

Correct. I floated this idea to the libvirt guys and Cole (of
virt-manager fame). The merging proposal was frowned upon. (Also, if
we're talking explicit reflashing, maybe the user should click a button
on virt-manager to request the update... Not sure.)

So basically these patches are just the non-merging alternative that is
perceived as more suitable for some distros.

> This might also enable a 'feature' where the particular VM instance
> can choose to not update the firmware when the central OVMF is
> updated.

Correct. (See the click the button thing above.) However right now VMs
have no choice, and auto-upgrading their OVMF wouldn't be a step back.
But, your remark is 100% valid.

> If we decided splitting was a good thing, then it would be nice to
> just always build the split and full images. I'm not sure .fdf can
> handle this though.

I think it can, if we add all three FDs with different names (like
OVMF.fd, OVMF_SPLIT.fd, NVVARSTORE.fd). I think the build process reuses
the FV files if there are multiple referring FDs -- I seem to recall
that's already happening between MEMFD.fd and OVMF.fd, no?

> Seems like build.sh could be tweaked if .fdf is
> not up to the task.

Certainly -- just invoke it twice with different params.

OTOH building all three FDs per default might be confusing for
end-users. We know for a fact that they usually don't read the README
(or not thoroughly enough). If we only give them one output file per
default, that's less potential for confusion.

But I can certainly post a version where all three files are built per
default, if that's what you prefer (and aren't opposed to the idea in
general).

Thanks!
Laszlo
Jordan Justen Nov. 22, 2013, 8:51 p.m. UTC | #7
On Fri, Nov 22, 2013 at 10:43 AM, Laszlo Ersek <lersek@redhat.com> wrote:
> OTOH building all three FDs per default might be confusing for
> end-users. We know for a fact that they usually don't read the README
> (or not thoroughly enough). If we only give them one output file per
> default, that's less potential for confusion.

If it will just add confusion, then perhaps it is something best left
out of the README. And at that point, since splitting it is so easy,
the value of having it in OVMF is debatable.

> But I can certainly post a version where all three files are built per
> default, if that's what you prefer (and aren't opposed to the idea in
> general).

I'm not opposed to it, but I would like to wait to see what QEMU does.

Many of these scenarios were discussed in the past on qemu-devel, but
a single -pflash was the only thing that stuck. This has made me just
focus on making the single file pflash work. You also can't deny that
the QEMU command line gets ugly real fast.

Tangentially related idea: if the user specifies -pflash to a
non-existent file, then QEMU could copy 'pflash-$(arch).bin' from the
roms folder into that file, and then the use it for the flash. It
could make using a flash almost as easy as using the default bios.

-Jordan
Eric Blake Nov. 22, 2013, 8:54 p.m. UTC | #8
On 11/22/2013 01:51 PM, Jordan Justen wrote:

> Tangentially related idea: if the user specifies -pflash to a
> non-existent file, then QEMU could copy 'pflash-$(arch).bin' from the
> roms folder into that file, and then the use it for the flash. It
> could make using a flash almost as easy as using the default bios.

But that image won't be auto-updated the next time the master is
updated.  Besides, under sVirt protections of libvirt, libvirt would
have to pre-create the file (because libvirt intentionally denies qemu
the ability to create files), so libvirt would have to be aware of the
default.  It's not much harder to make libvirt aware of handling a split
image, and a split image is easier to handle than having to copy the
default into a destination.
Jordan Justen Nov. 22, 2013, 9:18 p.m. UTC | #9
On Fri, Nov 22, 2013 at 12:54 PM, Eric Blake <eblake@redhat.com> wrote:
> On 11/22/2013 01:51 PM, Jordan Justen wrote:
>
>> Tangentially related idea: if the user specifies -pflash to a
>> non-existent file, then QEMU could copy 'pflash-$(arch).bin' from the
>> roms folder into that file, and then the use it for the flash. It
>> could make using a flash almost as easy as using the default bios.
>
> But that image won't be auto-updated the next time the master is
> updated.

Yes. We've decided to call that a feature. :)
https://lists.gnu.org/archive/html/qemu-devel/2011-07/msg02507.html

> Besides, under sVirt protections of libvirt, libvirt would
> have to pre-create the file (because libvirt intentionally denies qemu
> the ability to create files), so libvirt would have to be aware of the
> default. It's not much harder to make libvirt aware of handling a split
> image, and a split image is easier to handle than having to copy the
> default into a destination.

Well, admittedly, this idea is to optimize for qemu command line
ease-of-use. So I seem to be targeting a niche market. :) But I still
like to run qemu directly.

-Jordan
Laszlo Ersek Nov. 22, 2013, 9:40 p.m. UTC | #10
On 11/22/13 21:51, Jordan Justen wrote:
> On Fri, Nov 22, 2013 at 10:43 AM, Laszlo Ersek <lersek@redhat.com> wrote:
>> OTOH building all three FDs per default might be confusing for
>> end-users. We know for a fact that they usually don't read the README
>> (or not thoroughly enough). If we only give them one output file per
>> default, that's less potential for confusion.
> 
> If it will just add confusion, then perhaps it is something best left
> out of the README. And at that point, since splitting it is so easy,
> the value of having it in OVMF is debatable.
> 
>> But I can certainly post a version where all three files are built per
>> default, if that's what you prefer (and aren't opposed to the idea in
>> general).
> 
> I'm not opposed to it, but I would like to wait to see what QEMU does.

OK. Let's see where the qemu patch goes (I'll have to fix the comment
typo that Eric found), and if it is accepted (maybe with modifications),
we can return to the OVMF patch.

> Many of these scenarios were discussed in the past on qemu-devel, but
> a single -pflash was the only thing that stuck.

But (thanks to you) we now have a flash driver in OVMF that works *in
practice*. The discussion about multiple -pflash flags is not academic
any longer. (Hm, sorry, that's maybe too strong -- I can't really say if
the discussion used to be academic before. But it cannot have been this
practical.)

We also know that the libvirt developers prefer the split file. Plus:

> This has made me just
> focus on making the single file pflash work. You also can't deny that
> the QEMU command line gets ugly real fast.

We're in violent agreement on that -- which emphasizes the need for good
libvirt support all the more.

Seriously, I refuse to work with the qemu command line day to day. I
have an elaborate wrapper script that I insert between qemu and libvirt
(specified in the <emulator> libvirt domain XML) that post-processes the
arguments composed by libvirt. I need this to test out features that
libvirt doesn't yet support.

For example, I can set various filenames in the <loader> element -- it
specifies the argument to the -bios option. In the script I check the
argument against various patterns, and I can turn it into:
- -bios "$ARG"
- -pflash "$ARG"
- -pflash "$ARG" -pflash "$(manipulate "$ARG")"

The script handles CSM vs. pure-UEFI for Windows 2008, it can honor
upstream vs. RHEL qemu differences, it can turn off the iPXE virtio-net
driver. In the single -pflash option case, it does refresh the code part
in the VM's copy of OVMF (with dd) from my most recent build. It sets a
name-dependent output file for the debug port. Etc.

Maintaining this script over months has paid off orders of magnitude
better than working with the raw qemu command line would have. (I can
compare: on my (occasionally used) AMD desktop that runs Debian I have
not bothered to install libvirt, and each time I need to modify the
forty line qemu command that I keep in a script file, I cringe. And I
can't bring myself to install a second guest.)

The convenience/efficiency libvirt gives me is critical. Changing boot
order, adding or removing devices, starting & stopping -- these are very
fast in virt-manager. Editing the configuration with virsh is nice (and
it has some level of automatic verification when you save and exit).
Comparing guest configs against each other (using the XMLs) is very
convenient. And so on. I depend on these every day, but I need to modify
the wrapper script only occasionally.

Libvirt still allows me to send custom monitor commands, and I can set
it to log all of its *own* commands that it sends to the monitor or the
guest agent.

I've always wondered how other developers (for example, you :)) can
exist without libvirt at all!

That is why "keep the qemu command line simple" (== approx. "two -pflash
options are inconvenient") is no concern of mine. That ship has sailed.

Thanks,
Laszlo
Laszlo Ersek Nov. 22, 2013, 9:45 p.m. UTC | #11
On 11/22/13 21:51, Jordan Justen wrote:

> Many of these scenarios were discussed in the past on qemu-devel, but
> a single -pflash was the only thing that stuck. This has made me just
> focus on making the single file pflash work.

I almost forgot to reflect on this -- I'm extremely grateful to you that
you implemented the flash driver.

I tried to be as non-intrusive in my OVMF patch as possible. Keeping the
PCD values and the original memory layout was crucial -- I wanted the
driver to continue working as-is. Everything else (the qemu patch, for
example) came from that. In fact I wrote the OVMF patch first, and then
said "let's see how we can accommodate this in qemu".

(Sorry about answering twice.)

Laszlo
diff mbox

Patch

diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
index c50709c..310d6a9 100644
--- a/OvmfPkg/OvmfPkgIa32.fdf
+++ b/OvmfPkg/OvmfPkgIa32.fdf
@@ -23,31 +23,51 @@ 
 #
 [Defines]
 !if $(TARGET) == RELEASE
 !ifndef $(FD_SIZE_2MB)
 DEFINE FD_SIZE_1MB=
 !endif
 !endif
 
+!ifdef $(SPLIT_VARSTORE)
+!ifdef $(FD_SIZE_1MB)
+[FD.NvVarStore]
+BaseAddress   = 0xFFF00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
+SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = 0x00100000
+Size          = 0x20000
+ErasePolarity = 1
+BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
+NumBlocks     = 0x20
+!else
+[FD.NvVarStore]
+BaseAddress   = 0xFFE00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
+SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = 0x00200000
+Size          = 0x20000
+ErasePolarity = 1
+BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
+NumBlocks     = 0x20
+!endif
+!else
 !ifdef $(FD_SIZE_1MB)
 [FD.OVMF]
 BaseAddress   = 0xFFF00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
 Size          = 0x00100000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize
 ErasePolarity = 1
 BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
 NumBlocks     = 0x100
 !else
 [FD.OVMF]
 BaseAddress   = 0xFFE00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
 Size          = 0x00200000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize
 ErasePolarity = 1
 BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
 NumBlocks     = 0x200
 !endif
+!endif
 
 0x00000000|0x0000e000
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
 #NV_VARIABLE_STORE
 DATA = {
   ## This is the EFI_FIRMWARE_VOLUME_HEADER
   # ZeroVector []
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -106,30 +126,58 @@  DATA = {
   # WriteQueueSize: UINT64
   0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 }
 
 0x00010000|0x00010000
 #NV_FTW_SPARE
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
 
+!ifdef $(SPLIT_VARSTORE)
+!ifdef $(FD_SIZE_1MB)
+[FD.OVMF]
+BaseAddress   = 0xFFF20000
+Size          = 0x000E0000
+ErasePolarity = 1
+BlockSize     = 0x1000
+NumBlocks     = 0xE0
+
+0x00000000|0x000CC000
+FV = FVMAIN_COMPACT
+0x000CC000|0x14000
+FV = SECFV
+!else
+[FD.OVMF]
+BaseAddress   = 0xFFE20000
+Size          = 0x001E0000
+ErasePolarity = 1
+BlockSize     = 0x1000
+NumBlocks     = 0x1E0
+
+0x00000000|0x001AC000
+FV = FVMAIN_COMPACT
+0x001AC000|0x34000
+FV = SECFV
+!endif
+!else
 !ifdef $(FD_SIZE_1MB)
 0x00020000|0x000CC000
 FV = FVMAIN_COMPACT
 
 0x000EC000|0x14000
 FV = SECFV
 
 !else
 0x00020000|0x001AC000
 FV = FVMAIN_COMPACT
 
 0x001CC000|0x34000
 FV = SECFV
 !endif
+!endif
 
 ################################################################################
 
 [FD.MEMFD]
 BaseAddress   = 0x800000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfMemFvBase
 Size          = 0x800000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfMemFvSize
 ErasePolarity = 1
 BlockSize     = 0x10000
diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
index d65f40f..8877a89 100644
--- a/OvmfPkg/OvmfPkgIa32X64.fdf
+++ b/OvmfPkg/OvmfPkgIa32X64.fdf
@@ -23,31 +23,51 @@ 
 #
 [Defines]
 !if $(TARGET) == RELEASE
 !ifndef $(FD_SIZE_2MB)
 DEFINE FD_SIZE_1MB=
 !endif
 !endif
 
+!ifdef $(SPLIT_VARSTORE)
+!ifdef $(FD_SIZE_1MB)
+[FD.NvVarStore]
+BaseAddress   = 0xFFF00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
+SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = 0x00100000
+Size          = 0x20000
+ErasePolarity = 1
+BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
+NumBlocks     = 0x20
+!else
+[FD.NvVarStore]
+BaseAddress   = 0xFFE00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
+SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = 0x00200000
+Size          = 0x20000
+ErasePolarity = 1
+BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
+NumBlocks     = 0x20
+!endif
+!else
 !ifdef $(FD_SIZE_1MB)
 [FD.OVMF]
 BaseAddress   = 0xFFF00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
 Size          = 0x00100000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize
 ErasePolarity = 1
 BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
 NumBlocks     = 0x100
 !else
 [FD.OVMF]
 BaseAddress   = 0xFFE00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
 Size          = 0x00200000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize
 ErasePolarity = 1
 BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
 NumBlocks     = 0x200
 !endif
+!endif
 
 0x00000000|0x0000e000
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
 #NV_VARIABLE_STORE
 DATA = {
   ## This is the EFI_FIRMWARE_VOLUME_HEADER
   # ZeroVector []
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -106,30 +126,58 @@  DATA = {
   # WriteQueueSize: UINT64
   0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 }
 
 0x00010000|0x00010000
 #NV_FTW_SPARE
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
 
+!ifdef $(SPLIT_VARSTORE)
+!ifdef $(FD_SIZE_1MB)
+[FD.OVMF]
+BaseAddress   = 0xFFF20000
+Size          = 0x000E0000
+ErasePolarity = 1
+BlockSize     = 0x1000
+NumBlocks     = 0xE0
+
+0x00000000|0x000CC000
+FV = FVMAIN_COMPACT
+0x000CC000|0x14000
+FV = SECFV
+!else
+[FD.OVMF]
+BaseAddress   = 0xFFE20000
+Size          = 0x001E0000
+ErasePolarity = 1
+BlockSize     = 0x1000
+NumBlocks     = 0x1E0
+
+0x00000000|0x001AC000
+FV = FVMAIN_COMPACT
+0x001AC000|0x34000
+FV = SECFV
+!endif
+!else
 !ifdef $(FD_SIZE_1MB)
 0x00020000|0x000CC000
 FV = FVMAIN_COMPACT
 
 0x000EC000|0x14000
 FV = SECFV
 
 !else
 0x00020000|0x001AC000
 FV = FVMAIN_COMPACT
 
 0x001CC000|0x34000
 FV = SECFV
 !endif
+!endif
 
 ################################################################################
 
 [FD.MEMFD]
 BaseAddress   = 0x800000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfMemFvBase
 Size          = 0x800000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfMemFvSize
 ErasePolarity = 1
 BlockSize     = 0x10000
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index 751b94b..81cfff6 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -23,31 +23,51 @@ 
 #
 [Defines]
 !if $(TARGET) == RELEASE
 !ifndef $(FD_SIZE_2MB)
 DEFINE FD_SIZE_1MB=
 !endif
 !endif
 
+!ifdef $(SPLIT_VARSTORE)
+!ifdef $(FD_SIZE_1MB)
+[FD.NvVarStore]
+BaseAddress   = 0xFFF00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
+SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = 0x00100000
+Size          = 0x20000
+ErasePolarity = 1
+BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
+NumBlocks     = 0x20
+!else
+[FD.NvVarStore]
+BaseAddress   = 0xFFE00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
+SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = 0x00200000
+Size          = 0x20000
+ErasePolarity = 1
+BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
+NumBlocks     = 0x20
+!endif
+!else
 !ifdef $(FD_SIZE_1MB)
 [FD.OVMF]
 BaseAddress   = 0xFFF00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
 Size          = 0x00100000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize
 ErasePolarity = 1
 BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
 NumBlocks     = 0x100
 !else
 [FD.OVMF]
 BaseAddress   = 0xFFE00000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
 Size          = 0x00200000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize
 ErasePolarity = 1
 BlockSize     = 0x1000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize
 NumBlocks     = 0x200
 !endif
+!endif
 
 0x00000000|0x0000e000
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
 #NV_VARIABLE_STORE
 DATA = {
   ## This is the EFI_FIRMWARE_VOLUME_HEADER
   # ZeroVector []
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -106,30 +126,58 @@  DATA = {
   # WriteQueueSize: UINT64
   0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 }
 
 0x00010000|0x00010000
 #NV_FTW_SPARE
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
 
+!ifdef $(SPLIT_VARSTORE)
+!ifdef $(FD_SIZE_1MB)
+[FD.OVMF]
+BaseAddress   = 0xFFF20000
+Size          = 0x000E0000
+ErasePolarity = 1
+BlockSize     = 0x1000
+NumBlocks     = 0xE0
+
+0x00000000|0x000CC000
+FV = FVMAIN_COMPACT
+0x000CC000|0x14000
+FV = SECFV
+!else
+[FD.OVMF]
+BaseAddress   = 0xFFE20000
+Size          = 0x001E0000
+ErasePolarity = 1
+BlockSize     = 0x1000
+NumBlocks     = 0x1E0
+
+0x00000000|0x001AC000
+FV = FVMAIN_COMPACT
+0x001AC000|0x34000
+FV = SECFV
+!endif
+!else
 !ifdef $(FD_SIZE_1MB)
 0x00020000|0x000CC000
 FV = FVMAIN_COMPACT
 
 0x000EC000|0x14000
 FV = SECFV
 
 !else
 0x00020000|0x001AC000
 FV = FVMAIN_COMPACT
 
 0x001CC000|0x34000
 FV = SECFV
 !endif
+!endif
 
 ################################################################################
 
 [FD.MEMFD]
 BaseAddress   = 0x800000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfMemFvBase
 Size          = 0x800000|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfMemFvSize
 ErasePolarity = 1
 BlockSize     = 0x10000
diff --git a/OvmfPkg/README b/OvmfPkg/README
index f2c2fc7..6f7dc38 100644
--- a/OvmfPkg/README
+++ b/OvmfPkg/README
@@ -48,16 +48,19 @@  Update Conf/target.txt TARGET_ARCH based on the .dsc file:
 Following the edk2 build process, you will find the OVMF binaries
 under the $WORKSPACE/Build/*/*/FV directory.  The actual path will
 depend on how your build is configured.  You can expect to find
 these binary outputs:
 * OVMF.FD
   - Please note!  This filename has changed.  Older releases used OVMF.Fv.
 * OvmfVideo.rom
   - This file is not built separately any longer, starting with svn r13520.
+* NVVARSTORE.fd
+  - This file is only produced when -D SPLIT_VARSTORE is passed to the build
+    utility. (See more under "OVMF Flash Layout".)
 
 More information on building OVMF can be found at:
 
 http://sourceforge.net/apps/mediawiki/tianocore/index.php?title=How_to_build_OVMF
 
 === RUNNING OVMF on QEMU ===
 
 * QEMU 0.9.1 or later is required.
@@ -211,16 +214,29 @@  OVMF supports building a 1MB or a 2MB flash image. The base address for
 a 1MB image in QEMU physical memory is 0xfff00000. The base address for
 a 2MB image is 0xffe00000.
 
 The code in SECFV locates FVMAIN_COMPACT, and decompresses the
 main firmware (MAINFV) into RAM memory at address 0x800000. The
 remaining OVMF firmware then uses this decompressed firmware
 volume image.
 
+If -D SPLIT_VARSTORE has been passed to the build utility, then the address
+(base + 0x20000) splits the build output in two parts. The address range
+starting at (base + 0x20000) is covered by OVMF.fd (up to 4GB), while the range
+below it (from (base) to (base + 0x20000)) is covered by NVVARSTORE.fd.
+
+This is useful for centrally managing the binary part (OVMF.fd) on a host
+system, while allowing virtual machines to keep their private variable stores.
+Qemu-1.8 is expected to support this use case with the following options
+(specified in this order):
+
+      -drive file=/central/OVMF.fd,if=pflash,format=raw,readonly \
+      -drive file=/images/NVVARSTORE.fd.vm1,if=pflash,format=raw
+
 === UNIXGCC Debug ===
 
 If you build with the UNIXGCC toolchain, then debugging will be disabled
 due to larger image sizes being produced by the UNIXGCC toolchain. The
 first choice recommendation is to use GCC44 or newer instead.
 
 If you must use UNIXGCC, then you can override the build options for
 particular libraries and modules in the .dsc to re-enable debugging