802 changes: 792 additions & 10 deletions hw/nvme/ctrl.c

Large diffs are not rendered by default.

147 changes: 147 additions & 0 deletions hw/nvme/ns.c
Expand Up @@ -14,8 +14,10 @@

#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qemu/bitops.h"
#include "sysemu/sysemu.h"
#include "sysemu/block-backend.h"

Expand Down Expand Up @@ -377,6 +379,130 @@ static void nvme_zoned_ns_shutdown(NvmeNamespace *ns)
assert(ns->nr_open_zones == 0);
}

static NvmeRuHandle *nvme_find_ruh_by_attr(NvmeEnduranceGroup *endgrp,
uint8_t ruha, uint16_t *ruhid)
{
for (uint16_t i = 0; i < endgrp->fdp.nruh; i++) {
NvmeRuHandle *ruh = &endgrp->fdp.ruhs[i];

if (ruh->ruha == ruha) {
*ruhid = i;
return ruh;
}
}

return NULL;
}

static bool nvme_ns_init_fdp(NvmeNamespace *ns, Error **errp)
{
NvmeEnduranceGroup *endgrp = ns->endgrp;
NvmeRuHandle *ruh;
uint8_t lbafi = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas);
unsigned int *ruhid, *ruhids;
char *r, *p, *token;
uint16_t *ph;

if (!ns->params.fdp.ruhs) {
ns->fdp.nphs = 1;
ph = ns->fdp.phs = g_new(uint16_t, 1);

ruh = nvme_find_ruh_by_attr(endgrp, NVME_RUHA_CTRL, ph);
if (!ruh) {
ruh = nvme_find_ruh_by_attr(endgrp, NVME_RUHA_UNUSED, ph);
if (!ruh) {
error_setg(errp, "no unused reclaim unit handles left");
return false;
}

ruh->ruha = NVME_RUHA_CTRL;
ruh->lbafi = lbafi;
ruh->ruamw = endgrp->fdp.runs >> ns->lbaf.ds;

for (uint16_t rg = 0; rg < endgrp->fdp.nrg; rg++) {
ruh->rus[rg].ruamw = ruh->ruamw;
}
} else if (ruh->lbafi != lbafi) {
error_setg(errp, "lba format index of controller assigned "
"reclaim unit handle does not match namespace lba "
"format index");
return false;
}

return true;
}

ruhid = ruhids = g_new0(unsigned int, endgrp->fdp.nruh);
r = p = strdup(ns->params.fdp.ruhs);

/* parse the placement handle identifiers */
while ((token = qemu_strsep(&p, ";")) != NULL) {
ns->fdp.nphs += 1;
if (ns->fdp.nphs > NVME_FDP_MAXPIDS ||
ns->fdp.nphs == endgrp->fdp.nruh) {
error_setg(errp, "too many placement handles");
free(r);
return false;
}

if (qemu_strtoui(token, NULL, 0, ruhid++) < 0) {
error_setg(errp, "cannot parse reclaim unit handle identifier");
free(r);
return false;
}
}

free(r);

ph = ns->fdp.phs = g_new(uint16_t, ns->fdp.nphs);

ruhid = ruhids;

/* verify the identifiers */
for (unsigned int i = 0; i < ns->fdp.nphs; i++, ruhid++, ph++) {
if (*ruhid >= endgrp->fdp.nruh) {
error_setg(errp, "invalid reclaim unit handle identifier");
return false;
}

ruh = &endgrp->fdp.ruhs[*ruhid];

switch (ruh->ruha) {
case NVME_RUHA_UNUSED:
ruh->ruha = NVME_RUHA_HOST;
ruh->lbafi = lbafi;
ruh->ruamw = endgrp->fdp.runs >> ns->lbaf.ds;

for (uint16_t rg = 0; rg < endgrp->fdp.nrg; rg++) {
ruh->rus[rg].ruamw = ruh->ruamw;
}

break;

case NVME_RUHA_HOST:
if (ruh->lbafi != lbafi) {
error_setg(errp, "lba format index of host assigned"
"reclaim unit handle does not match namespace "
"lba format index");
return false;
}

break;

case NVME_RUHA_CTRL:
error_setg(errp, "reclaim unit handle is controller assigned");
return false;

default:
abort();
}

*ph = *ruhid;
}

return true;
}

static int nvme_ns_check_constraints(NvmeNamespace *ns, Error **errp)
{
unsigned int pi_size;
Expand Down Expand Up @@ -417,6 +543,11 @@ static int nvme_ns_check_constraints(NvmeNamespace *ns, Error **errp)
return -1;
}

if (ns->params.zoned && ns->endgrp && ns->endgrp->fdp.enabled) {
error_setg(errp, "cannot be a zoned- in an FDP configuration");
return -1;
}

if (ns->params.zoned) {
if (ns->params.max_active_zones) {
if (ns->params.max_open_zones > ns->params.max_active_zones) {
Expand Down Expand Up @@ -502,6 +633,12 @@ int nvme_ns_setup(NvmeNamespace *ns, Error **errp)
nvme_ns_init_zoned(ns);
}

if (ns->endgrp && ns->endgrp->fdp.enabled) {
if (!nvme_ns_init_fdp(ns, errp)) {
return -1;
}
}

return 0;
}

Expand All @@ -525,6 +662,10 @@ void nvme_ns_cleanup(NvmeNamespace *ns)
g_free(ns->zone_array);
g_free(ns->zd_extensions);
}

if (ns->endgrp && ns->endgrp->fdp.enabled) {
g_free(ns->fdp.phs);
}
}

static void nvme_ns_unrealize(DeviceState *dev)
Expand Down Expand Up @@ -561,6 +702,8 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
if (!qdev_set_parent_bus(dev, &subsys->bus.parent_bus, errp)) {
return;
}
ns->subsys = subsys;
ns->endgrp = &subsys->endgrp;
}

if (nvme_ns_setup(ns, errp)) {
Expand Down Expand Up @@ -591,6 +734,8 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
if (subsys) {
subsys->namespaces[nsid] = ns;

ns->id_ns.endgid = cpu_to_le16(0x1);

if (ns->params.detached) {
return;
}
Expand All @@ -606,6 +751,7 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)

return;
}

}

nvme_attach_ns(n, ns);
Expand Down Expand Up @@ -644,6 +790,7 @@ static Property nvme_ns_props[] = {
DEFINE_PROP_SIZE("zoned.zrwafg", NvmeNamespace, params.zrwafg, -1),
DEFINE_PROP_BOOL("eui64-default", NvmeNamespace, params.eui64_default,
false),
DEFINE_PROP_STRING("fdp.ruhs", NvmeNamespace, params.fdp.ruhs),
DEFINE_PROP_END_OF_LIST(),
};

Expand Down
92 changes: 90 additions & 2 deletions hw/nvme/nvme.h
Expand Up @@ -27,6 +27,8 @@
#define NVME_MAX_CONTROLLERS 256
#define NVME_MAX_NAMESPACES 256
#define NVME_EUI64_DEFAULT ((uint64_t)0x5254000000000000)
#define NVME_FDP_MAX_EVENTS 63
#define NVME_FDP_MAXPIDS 128

QEMU_BUILD_BUG_ON(NVME_MAX_NAMESPACES > NVME_NSID_BROADCAST - 1);

Expand All @@ -45,17 +47,68 @@ typedef struct NvmeBus {
OBJECT_CHECK(NvmeSubsystem, (obj), TYPE_NVME_SUBSYS)
#define SUBSYS_SLOT_RSVD (void *)0xFFFF

typedef struct NvmeReclaimUnit {
uint64_t ruamw;
} NvmeReclaimUnit;

typedef struct NvmeRuHandle {
uint8_t ruht;
uint8_t ruha;
uint64_t event_filter;
uint8_t lbafi;
uint64_t ruamw;

/* reclaim units indexed by reclaim group */
NvmeReclaimUnit *rus;
} NvmeRuHandle;

typedef struct NvmeFdpEventBuffer {
NvmeFdpEvent events[NVME_FDP_MAX_EVENTS];
unsigned int nelems;
unsigned int start;
unsigned int next;
} NvmeFdpEventBuffer;

typedef struct NvmeEnduranceGroup {
uint8_t event_conf;

struct {
NvmeFdpEventBuffer host_events, ctrl_events;

uint16_t nruh;
uint16_t nrg;
uint8_t rgif;
uint64_t runs;

uint64_t hbmw;
uint64_t mbmw;
uint64_t mbe;

bool enabled;

NvmeRuHandle *ruhs;
} fdp;
} NvmeEnduranceGroup;

typedef struct NvmeSubsystem {
DeviceState parent_obj;
NvmeBus bus;
uint8_t subnqn[256];
char *serial;

NvmeCtrl *ctrls[NVME_MAX_CONTROLLERS];
NvmeNamespace *namespaces[NVME_MAX_NAMESPACES + 1];
NvmeCtrl *ctrls[NVME_MAX_CONTROLLERS];
NvmeNamespace *namespaces[NVME_MAX_NAMESPACES + 1];
NvmeEnduranceGroup endgrp;

struct {
char *nqn;

struct {
bool enabled;
uint64_t runs;
uint16_t nruh;
uint32_t nrg;
} fdp;
} params;
} NvmeSubsystem;

Expand Down Expand Up @@ -96,6 +149,21 @@ typedef struct NvmeZone {
QTAILQ_ENTRY(NvmeZone) entry;
} NvmeZone;

#define FDP_EVT_MAX 0xff
#define NVME_FDP_MAX_NS_RUHS 32u
#define FDPVSS 0

static const uint8_t nvme_fdp_evf_shifts[FDP_EVT_MAX] = {
/* Host events */
[FDP_EVT_RU_NOT_FULLY_WRITTEN] = 0,
[FDP_EVT_RU_ATL_EXCEEDED] = 1,
[FDP_EVT_CTRL_RESET_RUH] = 2,
[FDP_EVT_INVALID_PID] = 3,
/* CTRL events */
[FDP_EVT_MEDIA_REALLOC] = 32,
[FDP_EVT_RUH_IMPLICIT_RU_CHANGE] = 33,
};

typedef struct NvmeNamespaceParams {
bool detached;
bool shared;
Expand Down Expand Up @@ -125,6 +193,10 @@ typedef struct NvmeNamespaceParams {
uint32_t numzrwa;
uint64_t zrwas;
uint64_t zrwafg;

struct {
char *ruhs;
} fdp;
} NvmeNamespaceParams;

typedef struct NvmeNamespace {
Expand Down Expand Up @@ -167,10 +239,18 @@ typedef struct NvmeNamespace {
int32_t nr_active_zones;

NvmeNamespaceParams params;
NvmeSubsystem *subsys;
NvmeEnduranceGroup *endgrp;

struct {
uint32_t err_rec;
} features;

struct {
uint16_t nphs;
/* reclaim unit handle identifiers indexed by placement handle */
uint16_t *phs;
} fdp;
} NvmeNamespace;

static inline uint32_t nvme_nsid(NvmeNamespace *ns)
Expand Down Expand Up @@ -274,6 +354,12 @@ static inline void nvme_aor_dec_active(NvmeNamespace *ns)
assert(ns->nr_active_zones >= 0);
}

static inline void nvme_fdp_stat_inc(uint64_t *a, uint64_t b)
{
uint64_t ret = *a + b;
*a = ret < *a ? UINT64_MAX : ret;
}

void nvme_ns_init_format(NvmeNamespace *ns);
int nvme_ns_setup(NvmeNamespace *ns, Error **errp);
void nvme_ns_drain(NvmeNamespace *ns);
Expand Down Expand Up @@ -340,7 +426,9 @@ static inline const char *nvme_adm_opc_str(uint8_t opc)
case NVME_ADM_CMD_GET_FEATURES: return "NVME_ADM_CMD_GET_FEATURES";
case NVME_ADM_CMD_ASYNC_EV_REQ: return "NVME_ADM_CMD_ASYNC_EV_REQ";
case NVME_ADM_CMD_NS_ATTACHMENT: return "NVME_ADM_CMD_NS_ATTACHMENT";
case NVME_ADM_CMD_DIRECTIVE_SEND: return "NVME_ADM_CMD_DIRECTIVE_SEND";
case NVME_ADM_CMD_VIRT_MNGMT: return "NVME_ADM_CMD_VIRT_MNGMT";
case NVME_ADM_CMD_DIRECTIVE_RECV: return "NVME_ADM_CMD_DIRECTIVE_RECV";
case NVME_ADM_CMD_DBBUF_CONFIG: return "NVME_ADM_CMD_DBBUF_CONFIG";
case NVME_ADM_CMD_FORMAT_NVM: return "NVME_ADM_CMD_FORMAT_NVM";
default: return "NVME_ADM_CMD_UNKNOWN";
Expand Down
94 changes: 92 additions & 2 deletions hw/nvme/subsys.c
Expand Up @@ -7,10 +7,13 @@
*/

#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qapi/error.h"

#include "nvme.h"

#define NVME_DEFAULT_RU_SIZE (96 * MiB)

static int nvme_subsys_reserve_cntlids(NvmeCtrl *n, int start, int num)
{
NvmeSubsystem *subsys = n->subsys;
Expand Down Expand Up @@ -109,13 +112,95 @@ void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n)
n->cntlid = -1;
}

static void nvme_subsys_setup(NvmeSubsystem *subsys)
static bool nvme_calc_rgif(uint16_t nruh, uint16_t nrg, uint8_t *rgif)
{
uint16_t val;
unsigned int i;

if (unlikely(nrg == 1)) {
/* PIDRG_NORGI scenario, all of pid is used for PHID */
*rgif = 0;
return true;
}

val = nrg;
i = 0;
while (val) {
val >>= 1;
i++;
}
*rgif = i;

/* ensure remaining bits suffice to represent number of phids in a RG */
if (unlikely((UINT16_MAX >> i) < nruh)) {
*rgif = 0;
return false;
}

return true;
}

static bool nvme_subsys_setup_fdp(NvmeSubsystem *subsys, Error **errp)
{
NvmeEnduranceGroup *endgrp = &subsys->endgrp;

if (!subsys->params.fdp.runs) {
error_setg(errp, "fdp.runs must be non-zero");
return false;
}

endgrp->fdp.runs = subsys->params.fdp.runs;

if (!subsys->params.fdp.nrg) {
error_setg(errp, "fdp.nrg must be non-zero");
return false;
}

endgrp->fdp.nrg = subsys->params.fdp.nrg;

if (!subsys->params.fdp.nruh) {
error_setg(errp, "fdp.nruh must be non-zero");
return false;
}

endgrp->fdp.nruh = subsys->params.fdp.nruh;

if (!nvme_calc_rgif(endgrp->fdp.nruh, endgrp->fdp.nrg, &endgrp->fdp.rgif)) {
error_setg(errp,
"cannot derive a valid rgif (nruh %"PRIu16" nrg %"PRIu32")",
endgrp->fdp.nruh, endgrp->fdp.nrg);
return false;
}

endgrp->fdp.ruhs = g_new(NvmeRuHandle, endgrp->fdp.nruh);

for (uint16_t ruhid = 0; ruhid < endgrp->fdp.nruh; ruhid++) {
endgrp->fdp.ruhs[ruhid] = (NvmeRuHandle) {
.ruht = NVME_RUHT_INITIALLY_ISOLATED,
.ruha = NVME_RUHA_UNUSED,
};

endgrp->fdp.ruhs[ruhid].rus = g_new(NvmeReclaimUnit, endgrp->fdp.nrg);
}

endgrp->fdp.enabled = true;

return true;
}

static bool nvme_subsys_setup(NvmeSubsystem *subsys, Error **errp)
{
const char *nqn = subsys->params.nqn ?
subsys->params.nqn : subsys->parent_obj.id;

snprintf((char *)subsys->subnqn, sizeof(subsys->subnqn),
"nqn.2019-08.org.qemu:%s", nqn);

if (subsys->params.fdp.enabled && !nvme_subsys_setup_fdp(subsys, errp)) {
return false;
}

return true;
}

static void nvme_subsys_realize(DeviceState *dev, Error **errp)
Expand All @@ -124,11 +209,16 @@ static void nvme_subsys_realize(DeviceState *dev, Error **errp)

qbus_init(&subsys->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, dev->id);

nvme_subsys_setup(subsys);
nvme_subsys_setup(subsys, errp);
}

static Property nvme_subsystem_props[] = {
DEFINE_PROP_STRING("nqn", NvmeSubsystem, params.nqn),
DEFINE_PROP_BOOL("fdp", NvmeSubsystem, params.fdp.enabled, false),
DEFINE_PROP_SIZE("fdp.runs", NvmeSubsystem, params.fdp.runs,
NVME_DEFAULT_RU_SIZE),
DEFINE_PROP_UINT32("fdp.nrg", NvmeSubsystem, params.fdp.nrg, 1),
DEFINE_PROP_UINT16("fdp.nruh", NvmeSubsystem, params.fdp.nruh, 0),
DEFINE_PROP_END_OF_LIST(),
};

Expand Down
1 change: 1 addition & 0 deletions hw/nvme/trace-events
Expand Up @@ -117,6 +117,7 @@ pci_nvme_clear_ns_reset(uint32_t state, uint64_t slba) "zone state=%"PRIu32", sl
pci_nvme_zoned_zrwa_implicit_flush(uint64_t zslba, uint32_t nlb) "zslba 0x%"PRIx64" nlb %"PRIu32""
pci_nvme_pci_reset(void) "PCI Function Level Reset"
pci_nvme_virt_mngmt(uint16_t cid, uint16_t act, uint16_t cntlid, const char* rt, uint16_t nr) "cid %"PRIu16", act=0x%"PRIx16", ctrlid=%"PRIu16" %s nr=%"PRIu16""
pci_nvme_fdp_ruh_change(uint16_t rgid, uint16_t ruhid) "change RU on RUH rgid=%"PRIu16", ruhid=%"PRIu16""

# error conditions
pci_nvme_err_mdts(size_t len) "len %zu"
Expand Down
64 changes: 53 additions & 11 deletions hw/xen/xen_pt.c
Expand Up @@ -57,6 +57,7 @@
#include <sys/ioctl.h>

#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/xen/xen.h"
Expand Down Expand Up @@ -780,15 +781,6 @@ static void xen_pt_realize(PCIDevice *d, Error **errp)
s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function,
s->dev.devfn);

xen_host_pci_device_get(&s->real_device,
s->hostaddr.domain, s->hostaddr.bus,
s->hostaddr.slot, s->hostaddr.function,
errp);
if (*errp) {
error_append_hint(errp, "Failed to \"open\" the real pci device");
return;
}

s->is_virtfn = s->real_device.is_virtfn;
if (s->is_virtfn) {
XEN_PT_LOG(d, "%04x:%02x:%02x.%d is a SR-IOV Virtual Function\n",
Expand All @@ -803,8 +795,10 @@ static void xen_pt_realize(PCIDevice *d, Error **errp)
s->io_listener = xen_pt_io_listener;

/* Setup VGA bios for passthrough GFX */
if ((s->real_device.domain == 0) && (s->real_device.bus == 0) &&
(s->real_device.dev == 2) && (s->real_device.func == 0)) {
if ((s->real_device.domain == XEN_PCI_IGD_DOMAIN) &&
(s->real_device.bus == XEN_PCI_IGD_BUS) &&
(s->real_device.dev == XEN_PCI_IGD_DEV) &&
(s->real_device.func == XEN_PCI_IGD_FN)) {
if (!is_igd_vga_passthrough(&s->real_device)) {
error_setg(errp, "Need to enable igd-passthru if you're trying"
" to passthrough IGD GFX");
Expand Down Expand Up @@ -950,11 +944,58 @@ static void xen_pci_passthrough_instance_init(Object *obj)
PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS;
}

void xen_igd_reserve_slot(PCIBus *pci_bus)
{
if (!xen_igd_gfx_pt_enabled()) {
return;
}

XEN_PT_LOG(0, "Reserving PCI slot 2 for IGD\n");
pci_bus->slot_reserved_mask |= XEN_PCI_IGD_SLOT_MASK;
}

static void xen_igd_clear_slot(DeviceState *qdev, Error **errp)
{
ERRP_GUARD();
PCIDevice *pci_dev = (PCIDevice *)qdev;
XenPCIPassthroughState *s = XEN_PT_DEVICE(pci_dev);
XenPTDeviceClass *xpdc = XEN_PT_DEVICE_GET_CLASS(s);
PCIBus *pci_bus = pci_get_bus(pci_dev);

xen_host_pci_device_get(&s->real_device,
s->hostaddr.domain, s->hostaddr.bus,
s->hostaddr.slot, s->hostaddr.function,
errp);
if (*errp) {
error_append_hint(errp, "Failed to \"open\" the real pci device");
return;
}

if (!(pci_bus->slot_reserved_mask & XEN_PCI_IGD_SLOT_MASK)) {
xpdc->pci_qdev_realize(qdev, errp);
return;
}

if (is_igd_vga_passthrough(&s->real_device) &&
s->real_device.domain == XEN_PCI_IGD_DOMAIN &&
s->real_device.bus == XEN_PCI_IGD_BUS &&
s->real_device.dev == XEN_PCI_IGD_DEV &&
s->real_device.func == XEN_PCI_IGD_FN &&
s->real_device.vendor_id == PCI_VENDOR_ID_INTEL) {
pci_bus->slot_reserved_mask &= ~XEN_PCI_IGD_SLOT_MASK;
XEN_PT_LOG(pci_dev, "Intel IGD found, using slot 2\n");
}
xpdc->pci_qdev_realize(qdev, errp);
}

static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);

XenPTDeviceClass *xpdc = XEN_PT_DEVICE_CLASS(klass);
xpdc->pci_qdev_realize = dc->realize;
dc->realize = xen_igd_clear_slot;
k->realize = xen_pt_realize;
k->exit = xen_pt_unregister_device;
k->config_read = xen_pt_pci_read_config;
Expand All @@ -977,6 +1018,7 @@ static const TypeInfo xen_pci_passthrough_info = {
.instance_size = sizeof(XenPCIPassthroughState),
.instance_finalize = xen_pci_passthrough_finalize,
.class_init = xen_pci_passthrough_class_init,
.class_size = sizeof(XenPTDeviceClass),
.instance_init = xen_pci_passthrough_instance_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
Expand Down
20 changes: 20 additions & 0 deletions hw/xen/xen_pt.h
Expand Up @@ -40,7 +40,20 @@ typedef struct XenPTReg XenPTReg;
#define TYPE_XEN_PT_DEVICE "xen-pci-passthrough"
OBJECT_DECLARE_SIMPLE_TYPE(XenPCIPassthroughState, XEN_PT_DEVICE)

#define XEN_PT_DEVICE_CLASS(klass) \
OBJECT_CLASS_CHECK(XenPTDeviceClass, klass, TYPE_XEN_PT_DEVICE)
#define XEN_PT_DEVICE_GET_CLASS(obj) \
OBJECT_GET_CLASS(XenPTDeviceClass, obj, TYPE_XEN_PT_DEVICE)

typedef void (*XenPTQdevRealize)(DeviceState *qdev, Error **errp);

typedef struct XenPTDeviceClass {
PCIDeviceClass parent_class;
XenPTQdevRealize pci_qdev_realize;
} XenPTDeviceClass;

uint32_t igd_read_opregion(XenPCIPassthroughState *s);
void xen_igd_reserve_slot(PCIBus *pci_bus);
void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val);
void xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s,
XenHostPCIDevice *dev);
Expand Down Expand Up @@ -75,6 +88,13 @@ typedef int (*xen_pt_conf_byte_read)

#define XEN_PCI_INTEL_OPREGION 0xfc

#define XEN_PCI_IGD_DOMAIN 0
#define XEN_PCI_IGD_BUS 0
#define XEN_PCI_IGD_DEV 2
#define XEN_PCI_IGD_FN 0
#define XEN_PCI_IGD_SLOT_MASK \
(1UL << PCI_SLOT(PCI_DEVFN(XEN_PCI_IGD_DEV, XEN_PCI_IGD_FN)))

typedef enum {
XEN_PT_GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */
XEN_PT_GRP_TYPE_EMU, /* emul reg group */
Expand Down
2 changes: 1 addition & 1 deletion hw/xen/xen_pt_config_init.c
Expand Up @@ -1924,7 +1924,7 @@ static void xen_pt_config_reg_init(XenPCIPassthroughState *s,
if (reg->init) {
uint32_t host_mask, size_mask;
unsigned int offset;
uint32_t val;
uint32_t val = 0;

/* initialize emulate register */
rc = reg->init(s, reg_entry->reg,
Expand Down
4 changes: 4 additions & 0 deletions hw/xen/xen_pt_stub.c
Expand Up @@ -20,3 +20,7 @@ void xen_igd_gfx_pt_set(bool value, Error **errp)
error_setg(errp, "Xen PCI passthrough support not built in");
}
}

void xen_igd_reserve_slot(PCIBus *pci_bus)
{
}
236 changes: 223 additions & 13 deletions include/block/nvme.h
@@ -1,6 +1,8 @@
#ifndef BLOCK_NVME_H
#define BLOCK_NVME_H

#include "hw/registerfields.h"

typedef struct QEMU_PACKED NvmeBar {
uint64_t cap;
uint32_t vs;
Expand Down Expand Up @@ -58,6 +60,24 @@ enum NvmeBarRegs {
NVME_REG_PMRMSCU = offsetof(NvmeBar, pmrmscu),
};

typedef struct QEMU_PACKED NvmeEndGrpLog {
uint8_t critical_warning;
uint8_t rsvd[2];
uint8_t avail_spare;
uint8_t avail_spare_thres;
uint8_t percet_used;
uint8_t rsvd1[26];
uint64_t end_estimate[2];
uint64_t data_units_read[2];
uint64_t data_units_written[2];
uint64_t media_units_written[2];
uint64_t host_read_commands[2];
uint64_t host_write_commands[2];
uint64_t media_integrity_errors[2];
uint64_t no_err_info_log_entries[2];
uint8_t rsvd2[352];
} NvmeEndGrpLog;

enum NvmeCapShift {
CAP_MQES_SHIFT = 0,
CAP_CQR_SHIFT = 16,
Expand Down Expand Up @@ -595,7 +615,9 @@ enum NvmeAdminCommands {
NVME_ADM_CMD_ACTIVATE_FW = 0x10,
NVME_ADM_CMD_DOWNLOAD_FW = 0x11,
NVME_ADM_CMD_NS_ATTACHMENT = 0x15,
NVME_ADM_CMD_DIRECTIVE_SEND = 0x19,
NVME_ADM_CMD_VIRT_MNGMT = 0x1c,
NVME_ADM_CMD_DIRECTIVE_RECV = 0x1a,
NVME_ADM_CMD_DBBUF_CONFIG = 0x7c,
NVME_ADM_CMD_FORMAT_NVM = 0x80,
NVME_ADM_CMD_SECURITY_SEND = 0x81,
Expand All @@ -611,7 +633,9 @@ enum NvmeIoCommands {
NVME_CMD_WRITE_ZEROES = 0x08,
NVME_CMD_DSM = 0x09,
NVME_CMD_VERIFY = 0x0c,
NVME_CMD_IO_MGMT_RECV = 0x12,
NVME_CMD_COPY = 0x19,
NVME_CMD_IO_MGMT_SEND = 0x1d,
NVME_CMD_ZONE_MGMT_SEND = 0x79,
NVME_CMD_ZONE_MGMT_RECV = 0x7a,
NVME_CMD_ZONE_APPEND = 0x7d,
Expand Down Expand Up @@ -704,7 +728,9 @@ typedef struct QEMU_PACKED NvmeRwCmd {
uint64_t slba;
uint16_t nlb;
uint16_t control;
uint32_t dsmgmt;
uint8_t dsmgmt;
uint8_t rsvd;
uint16_t dspec;
uint32_t reftag;
uint16_t apptag;
uint16_t appmask;
Expand Down Expand Up @@ -875,6 +901,8 @@ enum NvmeStatusCodes {
NVME_INVALID_PRP_OFFSET = 0x0013,
NVME_CMD_SET_CMB_REJECTED = 0x002b,
NVME_INVALID_CMD_SET = 0x002c,
NVME_FDP_DISABLED = 0x0029,
NVME_INVALID_PHID_LIST = 0x002a,
NVME_LBA_RANGE = 0x0080,
NVME_CAP_EXCEEDED = 0x0081,
NVME_NS_NOT_READY = 0x0082,
Expand Down Expand Up @@ -1005,11 +1033,16 @@ enum {
};

enum NvmeLogIdentifier {
NVME_LOG_ERROR_INFO = 0x01,
NVME_LOG_SMART_INFO = 0x02,
NVME_LOG_FW_SLOT_INFO = 0x03,
NVME_LOG_CHANGED_NSLIST = 0x04,
NVME_LOG_CMD_EFFECTS = 0x05,
NVME_LOG_ERROR_INFO = 0x01,
NVME_LOG_SMART_INFO = 0x02,
NVME_LOG_FW_SLOT_INFO = 0x03,
NVME_LOG_CHANGED_NSLIST = 0x04,
NVME_LOG_CMD_EFFECTS = 0x05,
NVME_LOG_ENDGRP = 0x09,
NVME_LOG_FDP_CONFS = 0x20,
NVME_LOG_FDP_RUH_USAGE = 0x21,
NVME_LOG_FDP_STATS = 0x22,
NVME_LOG_FDP_EVENTS = 0x23,
};

typedef struct QEMU_PACKED NvmePSD {
Expand Down Expand Up @@ -1091,7 +1124,10 @@ typedef struct QEMU_PACKED NvmeIdCtrl {
uint16_t mntmt;
uint16_t mxtmt;
uint32_t sanicap;
uint8_t rsvd332[180];
uint8_t rsvd332[6];
uint16_t nsetidmax;
uint16_t endgidmax;
uint8_t rsvd342[170];
uint8_t sqes;
uint8_t cqes;
uint16_t maxcmd;
Expand Down Expand Up @@ -1134,15 +1170,18 @@ enum NvmeIdCtrlOaes {
};

enum NvmeIdCtrlCtratt {
NVME_CTRATT_ENDGRPS = 1 << 4,
NVME_CTRATT_ELBAS = 1 << 15,
NVME_CTRATT_FDPS = 1 << 19,
};

enum NvmeIdCtrlOacs {
NVME_OACS_SECURITY = 1 << 0,
NVME_OACS_FORMAT = 1 << 1,
NVME_OACS_FW = 1 << 2,
NVME_OACS_NS_MGMT = 1 << 3,
NVME_OACS_DBBUF = 1 << 8,
NVME_OACS_SECURITY = 1 << 0,
NVME_OACS_FORMAT = 1 << 1,
NVME_OACS_FW = 1 << 2,
NVME_OACS_NS_MGMT = 1 << 3,
NVME_OACS_DIRECTIVES = 1 << 5,
NVME_OACS_DBBUF = 1 << 8,
};

enum NvmeIdCtrlOncs {
Expand Down Expand Up @@ -1227,6 +1266,7 @@ enum NvmeNsAttachmentOperation {
#define NVME_AEC_SMART(aec) (aec & 0xff)
#define NVME_AEC_NS_ATTR(aec) ((aec >> 8) & 0x1)
#define NVME_AEC_FW_ACTIVATION(aec) ((aec >> 9) & 0x1)
#define NVME_AEC_ENDGRP_NOTICE(aec) ((aec >> 14) & 0x1)

#define NVME_ERR_REC_TLER(err_rec) (err_rec & 0xffff)
#define NVME_ERR_REC_DULBE(err_rec) (err_rec & 0x10000)
Expand All @@ -1246,6 +1286,8 @@ enum NvmeFeatureIds {
NVME_TIMESTAMP = 0xe,
NVME_HOST_BEHAVIOR_SUPPORT = 0x16,
NVME_COMMAND_SET_PROFILE = 0x19,
NVME_FDP_MODE = 0x1d,
NVME_FDP_EVENTS = 0x1e,
NVME_SOFTWARE_PROGRESS_MARKER = 0x80,
NVME_FID_MAX = 0x100,
};
Expand Down Expand Up @@ -1338,7 +1380,10 @@ typedef struct QEMU_PACKED NvmeIdNs {
uint16_t mssrl;
uint32_t mcl;
uint8_t msrc;
uint8_t rsvd81[23];
uint8_t rsvd81[18];
uint8_t nsattr;
uint16_t nvmsetid;
uint16_t endgid;
uint8_t nguid[16];
uint64_t eui64;
NvmeLBAF lbaf[NVME_MAX_NLBAF];
Expand Down Expand Up @@ -1617,6 +1662,169 @@ typedef enum NvmeVirtualResourceType {
NVME_VIRT_RES_INTERRUPT = 0x01,
} NvmeVirtualResourceType;

typedef struct NvmeDirectiveIdentify {
uint8_t supported;
uint8_t unused1[31];
uint8_t enabled;
uint8_t unused33[31];
uint8_t persistent;
uint8_t unused65[31];
uint8_t rsvd64[4000];
} NvmeDirectiveIdentify;

enum NvmeDirectiveTypes {
NVME_DIRECTIVE_IDENTIFY = 0x0,
NVME_DIRECTIVE_DATA_PLACEMENT = 0x2,
};

enum NvmeDirectiveOperations {
NVME_DIRECTIVE_RETURN_PARAMS = 0x1,
};

typedef struct QEMU_PACKED NvmeFdpConfsHdr {
uint16_t num_confs;
uint8_t version;
uint8_t rsvd3;
uint32_t size;
uint8_t rsvd8[8];
} NvmeFdpConfsHdr;

REG8(FDPA, 0x0)
FIELD(FDPA, RGIF, 0, 4)
FIELD(FDPA, VWC, 4, 1)
FIELD(FDPA, VALID, 7, 1);

typedef struct QEMU_PACKED NvmeFdpDescrHdr {
uint16_t descr_size;
uint8_t fdpa;
uint8_t vss;
uint32_t nrg;
uint16_t nruh;
uint16_t maxpids;
uint32_t nnss;
uint64_t runs;
uint32_t erutl;
uint8_t rsvd28[36];
} NvmeFdpDescrHdr;

enum NvmeRuhType {
NVME_RUHT_INITIALLY_ISOLATED = 1,
NVME_RUHT_PERSISTENTLY_ISOLATED = 2,
};

typedef struct QEMU_PACKED NvmeRuhDescr {
uint8_t ruht;
uint8_t rsvd1[3];
} NvmeRuhDescr;

typedef struct QEMU_PACKED NvmeRuhuLog {
uint16_t nruh;
uint8_t rsvd2[6];
} NvmeRuhuLog;

enum NvmeRuhAttributes {
NVME_RUHA_UNUSED = 0,
NVME_RUHA_HOST = 1,
NVME_RUHA_CTRL = 2,
};

typedef struct QEMU_PACKED NvmeRuhuDescr {
uint8_t ruha;
uint8_t rsvd1[7];
} NvmeRuhuDescr;

typedef struct QEMU_PACKED NvmeFdpStatsLog {
uint64_t hbmw[2];
uint64_t mbmw[2];
uint64_t mbe[2];
uint8_t rsvd48[16];
} NvmeFdpStatsLog;

typedef struct QEMU_PACKED NvmeFdpEventsLog {
uint32_t num_events;
uint8_t rsvd4[60];
} NvmeFdpEventsLog;

enum NvmeFdpEventType {
FDP_EVT_RU_NOT_FULLY_WRITTEN = 0x0,
FDP_EVT_RU_ATL_EXCEEDED = 0x1,
FDP_EVT_CTRL_RESET_RUH = 0x2,
FDP_EVT_INVALID_PID = 0x3,
FDP_EVT_MEDIA_REALLOC = 0x80,
FDP_EVT_RUH_IMPLICIT_RU_CHANGE = 0x81,
};

enum NvmeFdpEventFlags {
FDPEF_PIV = 1 << 0,
FDPEF_NSIDV = 1 << 1,
FDPEF_LV = 1 << 2,
};

typedef struct QEMU_PACKED NvmeFdpEvent {
uint8_t type;
uint8_t flags;
uint16_t pid;
uint64_t timestamp;
uint32_t nsid;
uint64_t type_specific[2];
uint16_t rgid;
uint8_t ruhid;
uint8_t rsvd35[5];
uint64_t vendor[3];
} NvmeFdpEvent;

typedef struct QEMU_PACKED NvmePhidList {
uint16_t nnruhd;
uint8_t rsvd2[6];
} NvmePhidList;

typedef struct QEMU_PACKED NvmePhidDescr {
uint8_t ruht;
uint8_t rsvd1;
uint16_t ruhid;
} NvmePhidDescr;

REG32(FEAT_FDP, 0x0)
FIELD(FEAT_FDP, FDPE, 0, 1)
FIELD(FEAT_FDP, CONF_NDX, 8, 8);

typedef struct QEMU_PACKED NvmeFdpEventDescr {
uint8_t evt;
uint8_t evta;
} NvmeFdpEventDescr;

REG32(NVME_IOMR, 0x0)
FIELD(NVME_IOMR, MO, 0, 8)
FIELD(NVME_IOMR, MOS, 16, 16);

enum NvmeIomr2Mo {
NVME_IOMR_MO_NOP = 0x0,
NVME_IOMR_MO_RUH_STATUS = 0x1,
NVME_IOMR_MO_VENDOR_SPECIFIC = 0x255,
};

typedef struct QEMU_PACKED NvmeRuhStatus {
uint8_t rsvd0[14];
uint16_t nruhsd;
} NvmeRuhStatus;

typedef struct QEMU_PACKED NvmeRuhStatusDescr {
uint16_t pid;
uint16_t ruhid;
uint32_t earutr;
uint64_t ruamw;
uint8_t rsvd16[16];
} NvmeRuhStatusDescr;

REG32(NVME_IOMS, 0x0)
FIELD(NVME_IOMS, MO, 0, 8)
FIELD(NVME_IOMS, MOS, 16, 16);

enum NvmeIoms2Mo {
NVME_IOMS_MO_NOP = 0x0,
NVME_IOMS_MO_RUH_UPDATE = 0x1,
};

static inline void _nvme_check_size(void)
{
QEMU_BUILD_BUG_ON(sizeof(NvmeBar) != 4096);
Expand Down Expand Up @@ -1655,5 +1863,7 @@ static inline void _nvme_check_size(void)
QEMU_BUILD_BUG_ON(sizeof(NvmePriCtrlCap) != 4096);
QEMU_BUILD_BUG_ON(sizeof(NvmeSecCtrlEntry) != 32);
QEMU_BUILD_BUG_ON(sizeof(NvmeSecCtrlList) != 4096);
QEMU_BUILD_BUG_ON(sizeof(NvmeEndGrpLog) != 512);
QEMU_BUILD_BUG_ON(sizeof(NvmeDirectiveIdentify) != 4096);
}
#endif
2 changes: 0 additions & 2 deletions include/hw/i386/x86.h
Expand Up @@ -18,10 +18,8 @@
#define HW_I386_X86_H

#include "exec/hwaddr.h"
#include "qemu/notify.h"

#include "hw/boards.h"
#include "hw/nmi.h"
#include "hw/intc/ioapic.h"
#include "hw/isa/isa.h"
#include "qom/object.h"
Expand Down
Binary file modified pc-bios/s390-ccw.img
Binary file not shown.
157 changes: 121 additions & 36 deletions pc-bios/s390-ccw/bootmap.c
Expand Up @@ -72,42 +72,74 @@ static inline void verify_boot_info(BootInfo *bip)
"Bad block size in zIPL section of the 1st record.");
}

static block_number_t eckd_block_num(EckdCHS *chs)
static void eckd_format_chs(ExtEckdBlockPtr *ptr, bool ldipl,
uint64_t *c,
uint64_t *h,
uint64_t *s)
{
if (ldipl) {
*c = ptr->ldptr.chs.cylinder;
*h = ptr->ldptr.chs.head;
*s = ptr->ldptr.chs.sector;
} else {
*c = ptr->bptr.chs.cylinder;
*h = ptr->bptr.chs.head;
*s = ptr->bptr.chs.sector;
}
}

static block_number_t eckd_chs_to_block(uint64_t c, uint64_t h, uint64_t s)
{
const uint64_t sectors = virtio_get_sectors();
const uint64_t heads = virtio_get_heads();
const uint64_t cylinder = chs->cylinder
+ ((chs->head & 0xfff0) << 12);
const uint64_t head = chs->head & 0x000f;
const uint64_t cylinder = c + ((h & 0xfff0) << 12);
const uint64_t head = h & 0x000f;
const block_number_t block = sectors * heads * cylinder
+ sectors * head
+ chs->sector
- 1; /* block nr starts with zero */
+ s - 1; /* block nr starts with zero */
return block;
}

static bool eckd_valid_address(BootMapPointer *p)
static block_number_t eckd_block_num(EckdCHS *chs)
{
const uint64_t head = p->eckd.chs.head & 0x000f;
return eckd_chs_to_block(chs->cylinder, chs->head, chs->sector);
}

static block_number_t gen_eckd_block_num(ExtEckdBlockPtr *ptr, bool ldipl)
{
uint64_t cyl, head, sec;
eckd_format_chs(ptr, ldipl, &cyl, &head, &sec);
return eckd_chs_to_block(cyl, head, sec);
}

static bool eckd_valid_chs(uint64_t cyl, uint64_t head, uint64_t sector)
{
if (head >= virtio_get_heads()
|| p->eckd.chs.sector > virtio_get_sectors()
|| p->eckd.chs.sector <= 0) {
|| sector > virtio_get_sectors()
|| sector <= 0) {
return false;
}

if (!virtio_guessed_disk_nature() &&
eckd_block_num(&p->eckd.chs) >= virtio_get_blocks()) {
eckd_chs_to_block(cyl, head, sector) >= virtio_get_blocks()) {
return false;
}

return true;
}

static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address)
static bool eckd_valid_address(ExtEckdBlockPtr *ptr, bool ldipl)
{
uint64_t cyl, head, sec;
eckd_format_chs(ptr, ldipl, &cyl, &head, &sec);
return eckd_valid_chs(cyl, head, sec);
}

static block_number_t load_eckd_segments(block_number_t blk, bool ldipl,
uint64_t *address)
{
block_number_t block_nr;
int j, rc;
int j, rc, count;
BootMapPointer *bprs = (void *)_bprs;
bool more_data;

Expand All @@ -117,7 +149,7 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address)
do {
more_data = false;
for (j = 0;; j++) {
block_nr = eckd_block_num(&bprs[j].xeckd.bptr.chs);
block_nr = gen_eckd_block_num(&bprs[j].xeckd, ldipl);
if (is_null_block_number(block_nr)) { /* end of chunk */
break;
}
Expand All @@ -129,11 +161,26 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address)
break;
}

IPL_assert(block_size_ok(bprs[j].xeckd.bptr.size),
/* List directed pointer does not store block size */
IPL_assert(ldipl || block_size_ok(bprs[j].xeckd.bptr.size),
"bad chunk block size");
IPL_assert(eckd_valid_address(&bprs[j]), "bad chunk ECKD addr");

if ((bprs[j].xeckd.bptr.count == 0) && unused_space(&(bprs[j+1]),
if (!eckd_valid_address(&bprs[j].xeckd, ldipl)) {
/*
* If an invalid address is found during LD-IPL then break and
* retry as CCW
*/
IPL_assert(ldipl, "bad chunk ECKD addr");
break;
}

if (ldipl) {
count = bprs[j].xeckd.ldptr.count;
} else {
count = bprs[j].xeckd.bptr.count;
}

if (count == 0 && unused_space(&bprs[j + 1],
sizeof(EckdBlockPtr))) {
/* This is a "continue" pointer.
* This ptr should be the last one in the current
Expand All @@ -149,11 +196,10 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address)
/* Load (count+1) blocks of code at (block_nr)
* to memory (address).
*/
rc = virtio_read_many(block_nr, (void *)(*address),
bprs[j].xeckd.bptr.count+1);
rc = virtio_read_many(block_nr, (void *)(*address), count + 1);
IPL_assert(rc == 0, "code chunk read failed");

*address += (bprs[j].xeckd.bptr.count+1) * virtio_get_block_size();
*address += (count + 1) * virtio_get_block_size();
}
} while (more_data);
return block_nr;
Expand Down Expand Up @@ -237,8 +283,10 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr,
uint64_t address;
BootMapTable *bmt = (void *)sec;
BootMapScript *bms = (void *)sec;
/* The S1B block number is NULL_BLOCK_NR if and only if it's an LD-IPL */
bool ldipl = (s1b_block_nr == NULL_BLOCK_NR);

if (menu_is_enabled_zipl()) {
if (menu_is_enabled_zipl() && !ldipl) {
loadparm = eckd_get_boot_menu_index(s1b_block_nr);
}

Expand All @@ -249,7 +297,7 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr,
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
read_block(bmt_block_nr, sec, "Cannot read Boot Map Table");

block_nr = eckd_block_num(&bmt->entry[loadparm].xeckd.bptr.chs);
block_nr = gen_eckd_block_num(&bmt->entry[loadparm].xeckd, ldipl);
IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry");

memset(sec, FREE_SPACE_FILLER, sizeof(sec));
Expand All @@ -264,13 +312,18 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr,
}

address = bms->entry[i].address.load_address;
block_nr = eckd_block_num(&bms->entry[i].blkptr.xeckd.bptr.chs);
block_nr = gen_eckd_block_num(&bms->entry[i].blkptr.xeckd, ldipl);

do {
block_nr = load_eckd_segments(block_nr, &address);
block_nr = load_eckd_segments(block_nr, ldipl, &address);
} while (block_nr != -1);
}

if (ldipl && bms->entry[i].type != BOOT_SCRIPT_EXEC) {
/* Abort LD-IPL and retry as CCW-IPL */
return;
}

IPL_assert(bms->entry[i].type == BOOT_SCRIPT_EXEC,
"Unknown script entry type");
write_reset_psw(bms->entry[i].address.load_address); /* no return */
Expand Down Expand Up @@ -380,6 +433,23 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
/* no return */
}

static block_number_t eckd_find_bmt(ExtEckdBlockPtr *ptr)
{
block_number_t blockno;
uint8_t tmp_sec[MAX_SECTOR_SIZE];
BootRecord *br;

blockno = gen_eckd_block_num(ptr, 0);
read_block(blockno, tmp_sec, "Cannot read boot record");
br = (BootRecord *)tmp_sec;
if (!magic_match(br->magic, ZIPL_MAGIC)) {
/* If the boot record is invalid, return and try CCW-IPL instead */
return NULL_BLOCK_NR;
}

return gen_eckd_block_num(&br->pgt.xeckd, 1);
}

static void print_eckd_msg(void)
{
char msg[] = "Using ECKD scheme (block size *****), ";
Expand All @@ -401,28 +471,43 @@ static void print_eckd_msg(void)

static void ipl_eckd(void)
{
XEckdMbr *mbr = (void *)sec;
LDL_VTOC *vlbl = (void *)sec;
IplVolumeLabel *vlbl = (void *)sec;
LDL_VTOC *vtoc = (void *)sec;
block_number_t ldipl_bmt; /* Boot Map Table for List-Directed IPL */

print_eckd_msg();

/* Grab the MBR again */
/* Block 2 can contain either the CDL VOL1 label or the LDL VTOC */
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
read_block(0, mbr, "Cannot read block 0 on DASD");
read_block(2, vlbl, "Cannot read block 2");

if (magic_match(mbr->magic, IPL1_MAGIC)) {
ipl_eckd_cdl(); /* only returns in case of error */
return;
/*
* First check for a list-directed-format pointer which would
* supersede the CCW pointer.
*/
if (eckd_valid_address((ExtEckdBlockPtr *)&vlbl->f.br, 0)) {
ldipl_bmt = eckd_find_bmt((ExtEckdBlockPtr *)&vlbl->f.br);
if (ldipl_bmt) {
sclp_print("List-Directed\n");
/* LD-IPL does not use the S1B bock, just make it NULL */
run_eckd_boot_script(ldipl_bmt, NULL_BLOCK_NR);
/* Only return in error, retry as CCW-IPL */
sclp_print("Retrying IPL ");
print_eckd_msg();
}
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
read_block(2, vtoc, "Cannot read block 2");
}

/* LDL/CMS? */
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
read_block(2, vlbl, "Cannot read block 2");
/* Not list-directed */
if (magic_match(vtoc->magic, VOL1_MAGIC)) {
ipl_eckd_cdl(); /* may return in error */
}

if (magic_match(vlbl->magic, CMS1_MAGIC)) {
if (magic_match(vtoc->magic, CMS1_MAGIC)) {
ipl_eckd_ldl(ECKD_CMS); /* no return */
}
if (magic_match(vlbl->magic, LNX1_MAGIC)) {
if (magic_match(vtoc->magic, LNX1_MAGIC)) {
ipl_eckd_ldl(ECKD_LDL); /* no return */
}

Expand Down
30 changes: 27 additions & 3 deletions pc-bios/s390-ccw/bootmap.h
Expand Up @@ -45,9 +45,23 @@ typedef struct EckdBlockPtr {
* it's 0 for TablePtr, ScriptPtr, and SectionPtr */
} __attribute__ ((packed)) EckdBlockPtr;

typedef struct ExtEckdBlockPtr {
typedef struct LdEckdCHS {
uint32_t cylinder;
uint8_t head;
uint8_t sector;
} __attribute__ ((packed)) LdEckdCHS;

typedef struct LdEckdBlockPtr {
LdEckdCHS chs; /* cylinder/head/sector is an address of the block */
uint8_t reserved[4];
uint16_t count;
uint32_t pad;
} __attribute__ ((packed)) LdEckdBlockPtr;

/* bptr is used for CCW type IPL, while ldptr is for list-directed IPL */
typedef union ExtEckdBlockPtr {
EckdBlockPtr bptr;
uint8_t reserved[8];
LdEckdBlockPtr ldptr;
} __attribute__ ((packed)) ExtEckdBlockPtr;

typedef union BootMapPointer {
Expand All @@ -57,6 +71,15 @@ typedef union BootMapPointer {
ExtEckdBlockPtr xeckd;
} __attribute__ ((packed)) BootMapPointer;

typedef struct BootRecord {
uint8_t magic[4];
uint32_t version;
uint64_t res1;
BootMapPointer pgt;
uint8_t reserved[510 - 32];
uint16_t os_id;
} __attribute__ ((packed)) BootRecord;

/* aka Program Table */
typedef struct BootMapTable {
uint8_t magic[4];
Expand Down Expand Up @@ -292,7 +315,8 @@ typedef struct IplVolumeLabel {
struct {
unsigned char key[4]; /* == "VOL1" */
unsigned char volser[6];
unsigned char reserved[6];
unsigned char reserved[64];
EckdCHS br; /* Location of Boot Record for list-directed IPL */
} f;
};
} __attribute__((packed)) IplVolumeLabel;
Expand Down
2 changes: 1 addition & 1 deletion target/hexagon/meson.build
Expand Up @@ -183,7 +183,7 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs
)

bison = generator(
find_program('bison'),
find_program('bison', version: '>=3.0'),
output: ['@BASENAME@.tab.c', '@BASENAME@.tab.h'],
arguments: ['@INPUT@', '--defines=@OUTPUT1@', '--output=@OUTPUT0@']
)
Expand Down
49 changes: 1 addition & 48 deletions tests/qtest/libqos/libqos.c
Expand Up @@ -137,56 +137,9 @@ void migrate(QOSState *from, QOSState *to, const char *uri)
migrate_allocator(&from->alloc, &to->alloc);
}

bool have_qemu_img(void)
{
char *rpath;
const char *path = getenv("QTEST_QEMU_IMG");
if (!path) {
return false;
}

rpath = realpath(path, NULL);
if (!rpath) {
return false;
} else {
free(rpath);
return true;
}
}

void mkimg(const char *file, const char *fmt, unsigned size_mb)
{
gchar *cli;
bool ret;
int rc;
GError *err = NULL;
char *qemu_img_path;
gchar *out, *out2;
char *qemu_img_abs_path;

qemu_img_path = getenv("QTEST_QEMU_IMG");
g_assert(qemu_img_path);
qemu_img_abs_path = realpath(qemu_img_path, NULL);
g_assert(qemu_img_abs_path);

cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path,
fmt, file, size_mb);
ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err);
if (err || !g_spawn_check_exit_status(rc, &err)) {
fprintf(stderr, "%s\n", err->message);
g_error_free(err);
}
g_assert(ret && !err);

g_free(out);
g_free(out2);
g_free(cli);
free(qemu_img_abs_path);
}

void mkqcow2(const char *file, unsigned size_mb)
{
return mkimg(file, "qcow2", size_mb);
g_assert_true(mkimg(file, "qcow2", size_mb));
}

void prepare_blkdebug_script(const char *debug_fn, const char *event)
Expand Down
2 changes: 0 additions & 2 deletions tests/qtest/libqos/libqos.h
Expand Up @@ -27,8 +27,6 @@ QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...)
G_GNUC_PRINTF(2, 3);
void qtest_common_shutdown(QOSState *qs);
void qtest_shutdown(QOSState *qs);
bool have_qemu_img(void);
void mkimg(const char *file, const char *fmt, unsigned size_mb);
void mkqcow2(const char *file, unsigned size_mb);
void migrate(QOSState *from, QOSState *to, const char *uri);
void prepare_blkdebug_script(const char *debug_fn, const char *event);
Expand Down
52 changes: 52 additions & 0 deletions tests/qtest/libqtest.c
Expand Up @@ -1625,3 +1625,55 @@ bool qtest_qom_get_bool(QTestState *s, const char *path, const char *property)

return b;
}

bool have_qemu_img(void)
{
char *rpath;
const char *path = getenv("QTEST_QEMU_IMG");
if (!path) {
return false;
}

rpath = realpath(path, NULL);
if (!rpath) {
return false;
} else {
free(rpath);
return true;
}
}

bool mkimg(const char *file, const char *fmt, unsigned size_mb)
{
gchar *cli;
bool ret;
int rc;
GError *err = NULL;
char *qemu_img_path;
gchar *out, *out2;
char *qemu_img_abs_path;

qemu_img_path = getenv("QTEST_QEMU_IMG");
if (!qemu_img_path) {
return false;
}
qemu_img_abs_path = realpath(qemu_img_path, NULL);
if (!qemu_img_abs_path) {
return false;
}

cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path,
fmt, file, size_mb);
ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err);
if (err || !g_spawn_check_exit_status(rc, &err)) {
fprintf(stderr, "%s\n", err->message);
g_error_free(err);
}

g_free(out);
g_free(out2);
g_free(cli);
free(qemu_img_abs_path);

return ret && !err;
}
21 changes: 21 additions & 0 deletions tests/qtest/libqtest.h
Expand Up @@ -832,4 +832,25 @@ void qtest_qom_set_bool(QTestState *s, const char *path, const char *property,
* Returns: Value retrieved from property.
*/
bool qtest_qom_get_bool(QTestState *s, const char *path, const char *property);

/**
* have_qemu_img:
*
* Returns: true if "qemu-img" is available.
*/
bool have_qemu_img(void);

/**
* mkimg:
* @file: File name of the image that should be created
* @fmt: Format, e.g. "qcow2" or "raw"
* @size_mb: Size of the image in megabytes
*
* Create a disk image with qemu-img. Note that the QTEST_QEMU_IMG
* environment variable must point to the qemu-img file.
*
* Returns: true if the image has been created successfully.
*/
bool mkimg(const char *file, const char *fmt, unsigned size_mb);

#endif
10 changes: 6 additions & 4 deletions tests/qtest/meson.build
Expand Up @@ -309,10 +309,12 @@ qtests = {
'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'),
}

gvnc = dependency('gvnc-1.0', required: false)
if gvnc.found()
qtests += {'vnc-display-test': [gvnc]}
qtests_generic += [ 'vnc-display-test' ]
if vnc.found()
gvnc = dependency('gvnc-1.0', required: false)
if gvnc.found()
qtests += {'vnc-display-test': [gvnc]}
qtests_generic += [ 'vnc-display-test' ]
endif
endif

if dbus_display
Expand Down
175 changes: 164 additions & 11 deletions tests/qtest/readconfig-test.c
Expand Up @@ -48,7 +48,7 @@ static QTestState *qtest_init_with_config(const char *cfgdata)
return qts;
}

static void test_x86_memdev_resp(QObject *res)
static void test_x86_memdev_resp(QObject *res, const char *mem_id, int size)
{
Visitor *v;
g_autoptr(MemdevList) memdevs = NULL;
Expand All @@ -63,8 +63,8 @@ static void test_x86_memdev_resp(QObject *res)
g_assert(!memdevs->next);

memdev = memdevs->value;
g_assert_cmpstr(memdev->id, ==, "ram");
g_assert_cmpint(memdev->size, ==, 200 * MiB);
g_assert_cmpstr(memdev->id, ==, mem_id);
g_assert_cmpint(memdev->size, ==, size * MiB);

visit_free(v);
}
Expand All @@ -80,7 +80,7 @@ static void test_x86_memdev(void)
qts = qtest_init_with_config(cfgdata);
/* Test valid command */
resp = qtest_qmp(qts, "{ 'execute': 'query-memdev' }");
test_x86_memdev_resp(qdict_get(resp, "return"));
test_x86_memdev_resp(qdict_get(resp, "return"), "ram", 200);
qobject_unref(resp);

qtest_quit(qts);
Expand Down Expand Up @@ -124,13 +124,15 @@ static void test_spice(void)
}
#endif

static void test_object_rng_resp(QObject *res)
static void test_object_available(QObject *res, const char *name,
const char *type)
{
Visitor *v;
g_autoptr(ObjectPropertyInfoList) objs = NULL;
ObjectPropertyInfoList *tmp;
ObjectPropertyInfo *obj;
bool seen_rng = false;
bool object_available = false;
g_autofree char *childtype = g_strdup_printf("child<%s>", type);

g_assert(res);
v = qobject_input_visitor_new(res);
Expand All @@ -142,16 +144,15 @@ static void test_object_rng_resp(QObject *res)
g_assert(tmp->value);

obj = tmp->value;
if (g_str_equal(obj->name, "rng0") &&
g_str_equal(obj->type, "child<rng-builtin>")) {
seen_rng = true;
if (g_str_equal(obj->name, name) && g_str_equal(obj->type, childtype)) {
object_available = true;
break;
}

tmp = tmp->next;
}

g_assert(seen_rng);
g_assert(object_available);

visit_free(v);
}
Expand All @@ -170,12 +171,158 @@ static void test_object_rng(void)
resp = qtest_qmp(qts,
"{ 'execute': 'qom-list',"
" 'arguments': {'path': '/objects' }}");
test_object_rng_resp(qdict_get(resp, "return"));
test_object_available(qdict_get(resp, "return"), "rng0", "rng-builtin");
qobject_unref(resp);

qtest_quit(qts);
}

static void test_docs_config_ich9(void)
{
QTestState *qts;
QDict *resp;
QObject *qobj;

qts = qtest_initf("-nodefaults -readconfig docs/config/ich9-ehci-uhci.cfg");

resp = qtest_qmp(qts, "{ 'execute': 'qom-list',"
" 'arguments': {'path': '/machine/peripheral' }}");
qobj = qdict_get(resp, "return");
test_object_available(qobj, "ehci", "ich9-usb-ehci1");
test_object_available(qobj, "uhci-1", "ich9-usb-uhci1");
test_object_available(qobj, "uhci-2", "ich9-usb-uhci2");
test_object_available(qobj, "uhci-3", "ich9-usb-uhci3");
qobject_unref(resp);

qtest_quit(qts);
}

#ifdef CONFIG_LINUX

static char *make_temp_img(const char *template, const char *format, int size)
{
GError *error = NULL;
char *temp_name;
int fd;

/* Create a temporary image names */
fd = g_file_open_tmp(template, &temp_name, &error);
if (fd == -1) {
fprintf(stderr, "unable to create file: %s\n", error->message);
g_error_free(error);
return NULL;
}
close(fd);

if (!mkimg(temp_name, format, size)) {
fprintf(stderr, "qemu-img failed to create %s\n", temp_name);
g_free(temp_name);
return NULL;
}

return temp_name;
}

static void test_docs_q35_emulated(void)
{
QTestState *qts;
QDict *resp;
QObject *qobj;
int ret, i;
g_autofree char *cfg_file = NULL, *sedcmd = NULL;
g_autofree char *hd_file = NULL, *cd_file = NULL;

struct {
const char *name;
const char *type;
} devices[] = {
{ "ich9-pcie-port-1", "ioh3420" },
{ "ich9-pcie-port-2", "ioh3420" },
{ "ich9-pcie-port-3", "ioh3420" },
{ "ich9-pcie-port-4", "ioh3420" },
{ "ich9-pci-bridge", "i82801b11-bridge" },
{ "ich9-ehci-1", "ich9-usb-ehci1" },
{ "ich9-ehci-2", "ich9-usb-ehci2" },
{ "ich9-uhci-1", "ich9-usb-uhci1" },
{ "ich9-uhci-2", "ich9-usb-uhci2" },
{ "ich9-uhci-3", "ich9-usb-uhci3" },
{ "ich9-uhci-4", "ich9-usb-uhci4" },
{ "ich9-uhci-5", "ich9-usb-uhci5" },
{ "ich9-uhci-6", "ich9-usb-uhci6" },
{ "sata-disk", "ide-hd" },
{ "sata-optical-disk", "ide-cd" },
{ "net", "e1000" },
{ "video", "VGA" },
{ "ich9-hda-audio", "ich9-intel-hda" },
{ "ich9-hda-duplex", "hda-duplex" },
};

/* Check that all the devices are available in the QEMU binary */
for (i = 0; i < ARRAY_SIZE(devices); i++) {
if (!qtest_has_device(devices[i].type)) {
g_test_skip("one of the required devices is not available");
return;
}
}

hd_file = make_temp_img("qtest_disk_XXXXXX.qcow2", "qcow2", 1);
cd_file = make_temp_img("qtest_cdrom_XXXXXX.iso", "raw", 1);
if (!hd_file || !cd_file) {
g_test_skip("could not create disk images");
goto cleanup;
}

/* Create a temporary config file where we replace the disk image names */
ret = g_file_open_tmp("q35-emulated-XXXXXX.cfg", &cfg_file, NULL);
if (ret == -1) {
g_test_skip("could not create temporary config file");
goto cleanup;
}
close(ret);

sedcmd = g_strdup_printf("sed -e 's,guest.qcow2,%s,' -e 's,install.iso,%s,'"
" docs/config/q35-emulated.cfg > '%s'",
hd_file, cd_file, cfg_file);
ret = system(sedcmd);
if (ret) {
g_test_skip("could not modify temporary config file");
goto cleanup;
}

qts = qtest_initf("-machine none -nodefaults -readconfig %s", cfg_file);

/* Check memory size */
resp = qtest_qmp(qts, "{ 'execute': 'query-memdev' }");
test_x86_memdev_resp(qdict_get(resp, "return"), "pc.ram", 1024);
qobject_unref(resp);

resp = qtest_qmp(qts, "{ 'execute': 'qom-list',"
" 'arguments': {'path': '/machine/peripheral' }}");
qobj = qdict_get(resp, "return");

/* Check that all the devices have been created */
for (i = 0; i < ARRAY_SIZE(devices); i++) {
test_object_available(qobj, devices[i].name, devices[i].type);
}

qobject_unref(resp);

qtest_quit(qts);

cleanup:
if (hd_file) {
unlink(hd_file);
}
if (cd_file) {
unlink(cd_file);
}
if (cfg_file) {
unlink(cfg_file);
}
}

#endif /* CONFIG_LINUX */

int main(int argc, char *argv[])
{
const char *arch;
Expand All @@ -186,6 +333,12 @@ int main(int argc, char *argv[])
if (g_str_equal(arch, "i386") ||
g_str_equal(arch, "x86_64")) {
qtest_add_func("readconfig/x86/memdev", test_x86_memdev);
qtest_add_func("readconfig/x86/ich9-ehci-uhci", test_docs_config_ich9);
#ifdef CONFIG_LINUX
if (qtest_has_accel("kvm")) {
qtest_add_func("readconfig/x86/q35-emulated", test_docs_q35_emulated);
}
#endif
}
#ifdef CONFIG_SPICE
qtest_add_func("readconfig/spice", test_spice);
Expand Down