Commit 50de920b authored by Laszlo Ersek's avatar Laszlo Ersek Committed by Paolo Bonzini

hw/isa/lpc_ich9: add SMI feature negotiation via fw_cfg

Introduce the following fw_cfg files:

- "etc/smi/supported-features": a little endian uint64_t feature bitmap,
  presenting the features known by the host to the guest. Read-only for
  the guest.

  The content of this file will be determined via bit-granularity ICH9-LPC
  device properties, to be introduced later. For now, the bitmask is left
  zeroed. The bits will be set from machine type compat properties and on
  the QEMU command line, hence this file is not migrated.

- "etc/smi/requested-features": a little endian uint64_t feature bitmap,
  representing the features the guest would like to request. Read-write
  for the guest.

  The guest can freely (re)write this file, it has no direct consequence.
  Initial value is zero. A nonzero value causes the SMI-related fw_cfg
  files and fields that are under guest influence to be migrated.

- "etc/smi/features-ok": contains a uint8_t value, and it is read-only for
  the guest. When the guest selects the associated fw_cfg key, the guest
  features are validated against the host features. In case of error, the
  negotiation doesn't proceed, and the "features-ok" file remains zero. In
  case of success, the "features-ok" file becomes (uint8_t)1, and the
  negotiated features are locked down internally (to which no further
  changes are possible until reset).

  The initial value is zero.  A nonzero value causes the SMI-related
  fw_cfg files and fields that are under guest influence to be migrated.

The C-language fields backing the "supported-features" and
"requested-features" files are uint8_t arrays. This is because they carry
guest-side representation (our choice is little endian), while
VMSTATE_UINT64() assumes / implies host-side endianness for any uint64_t
fields. If we migrate a guest between hosts with different endiannesses
(which is possible with TCG), then the host-side value is preserved, and
the host-side representation is translated. This would be visible to the
guest through fw_cfg, unless we used plain byte arrays. So we do.

Cc: "Michael S. Tsirkin" <>
Cc: Gerd Hoffmann <>
Cc: Igor Mammedov <>
Cc: Paolo Bonzini <>
Signed-off-by: 's avatarLaszlo Ersek <>
Reviewed-by: 's avatarMichael S. Tsirkin <>
Reviewed-by: 's avatarIgor Mammedov <>
Message-Id: <>
Signed-off-by: 's avatarPaolo Bonzini <>
parent 57bb40c9
......@@ -48,6 +48,8 @@
#include "exec/address-spaces.h"
#include "sysemu/sysemu.h"
#include "qom/cpu.h"
#include "hw/nvram/fw_cfg.h"
#include "qemu/cutils.h"
/* ICH9 LPC PCI to ISA bridge */
......@@ -360,13 +362,62 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
static void smi_features_ok_callback(void *opaque)
ICH9LPCState *lpc = opaque;
uint64_t guest_features;
if (lpc->smi_features_ok) {
/* negotiation already complete, features locked */
memcpy(&guest_features, lpc->smi_guest_features_le, sizeof guest_features);
if (guest_features & ~lpc->smi_host_features) {
/* guest requests invalid features, leave @features_ok at zero */
/* valid feature subset requested, lock it down, report success */
lpc->smi_negotiated_features = guest_features;
lpc->smi_features_ok = 1;
void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled)
ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
qemu_irq sci_irq;
FWCfgState *fw_cfg = fw_cfg_find();
sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0);
ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, sci_irq);
if (lpc->smi_host_features && fw_cfg) {
uint64_t host_features_le;
host_features_le = cpu_to_le64(lpc->smi_host_features);
memcpy(lpc->smi_host_features_le, &host_features_le,
sizeof host_features_le);
fw_cfg_add_file(fw_cfg, "etc/smi/supported-features",
sizeof lpc->smi_host_features_le);
/* The other two guest-visible fields are cleared on device reset, we
* just link them into fw_cfg here.
fw_cfg_add_file_callback(fw_cfg, "etc/smi/requested-features",
sizeof lpc->smi_guest_features_le,
fw_cfg_add_file_callback(fw_cfg, "etc/smi/features-ok",
smi_features_ok_callback, lpc,
sizeof lpc->smi_features_ok,
......@@ -507,6 +558,10 @@ static void ich9_lpc_reset(DeviceState *qdev)
lpc->sci_level = 0;
lpc->rst_cnt = 0;
memset(lpc->smi_guest_features_le, 0, sizeof lpc->smi_guest_features_le);
lpc->smi_features_ok = 0;
lpc->smi_negotiated_features = 0;
/* root complex register block is mapped into memory space */
......@@ -668,6 +723,29 @@ static const VMStateDescription vmstate_ich9_rst_cnt = {
static bool ich9_smi_feat_needed(void *opaque)
ICH9LPCState *lpc = opaque;
return !buffer_is_zero(lpc->smi_guest_features_le,
sizeof lpc->smi_guest_features_le) ||
static const VMStateDescription vmstate_ich9_smi_feat = {
.name = "ICH9LPC/smi_feat",
.version_id = 1,
.minimum_version_id = 1,
.needed = ich9_smi_feat_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8_ARRAY(smi_guest_features_le, ICH9LPCState,
VMSTATE_UINT8(smi_features_ok, ICH9LPCState),
VMSTATE_UINT64(smi_negotiated_features, ICH9LPCState),
static const VMStateDescription vmstate_ich9_lpc = {
.name = "ICH9LPC",
.version_id = 1,
......@@ -683,6 +761,7 @@ static const VMStateDescription vmstate_ich9_lpc = {
.subsections = (const VMStateDescription*[]) {
......@@ -64,6 +64,16 @@ typedef struct ICH9LPCState {
uint8_t rst_cnt;
MemoryRegion rst_cnt_mem;
/* SMI feature negotiation via fw_cfg */
uint64_t smi_host_features; /* guest-invisible, host endian */
uint8_t smi_host_features_le[8]; /* guest-visible, read-only, little
* endian uint64_t */
uint8_t smi_guest_features_le[8]; /* guest-visible, read-write, little
* endian uint64_t */
uint8_t smi_features_ok; /* guest-visible, read-only; selecting it
* triggers feature lockdown */
uint64_t smi_negotiated_features; /* guest-invisible, host endian */
/* isa bus */
ISABus *isa_bus;
MemoryRegion rcrb_mem; /* root complex register block */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment