Patch queue for ppc - 2015-06-03

Highlights this time around:
 
   - sPAPR: endian fixes, speedups, bug fixes, hotplug basics
   - add default ram size capability for machines (sPAPR defaults to 512MB now)
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.19 (GNU/Linux)
 
 iQIcBAABAgAGBQJVb3itAAoJECszeR4D/txgGncQAIz7tPRvMlCJyaGdYIkySUh4
 vbwAf4Z2Ddjv/gA/3G3oY1lC5RnhOJucxCbobzdayKecrdkdAJa/O/6RbKij4zMD
 svXseSpk8aKr4yrfNItxrjysJsp4cMS7APim7HcF5mOBJJqp0COkr1q97VteTfY1
 AdiSfBU5IEj0RZ+J1pSnMVf837gLiKSv+L2gTyGkb66VBMqZOZzu5UuoUhIOfa+R
 /tlm2VMRKe7vrU7Q4TL8Syn9UZnB03aNrKIXYN0VJy5WTePSMWPSQ6fbImTELEQB
 En87DGYt/QVs0eB7XNwzhF0REFblHECOzFhbOovCrvGZIa4xai8HJaJHMeaxQfkx
 4Aiby7Kv8wJgjn13OuBTvG7YWtw3hJcO1i0ePs2MmGz9sJNzhz0tyRSRglc3xN1Q
 RBrqyl3lOsnvNRzj/py7kYxCKtG8xlkaTSkO6FfXmt9UMW91pqWo4/2LCTON0zkx
 +gd2UW7JPw2u6ttzCu+b8BZv1ATovHoj2wXPP4iEYpe1sGT6qp4moZZ6CtWex/O3
 4Lhd9jJVJurMZl6e1pn/4bkcEhNvT2B484GmmerrZXrtlKm9wcepqMJC2bVCtzjT
 JBLNGTk6z8QKN5WRD+LWD3LgEjAEqV6nvqrmiwovMUtC0lJSHJTTAoeurM3h6jJn
 eaR4tzdEqHgDhzkOCHux
 =zWZp
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/agraf/tags/signed-ppc-for-upstream' into staging

Patch queue for ppc - 2015-06-03

Highlights this time around:

  - sPAPR: endian fixes, speedups, bug fixes, hotplug basics
  - add default ram size capability for machines (sPAPR defaults to 512MB now)

# gpg: Signature made Wed Jun  3 22:59:09 2015 BST using RSA key ID 03FEDC60
# gpg: Good signature from "Alexander Graf <agraf@suse.de>"
# gpg:                 aka "Alexander Graf <alex@csgraf.de>"

* remotes/agraf/tags/signed-ppc-for-upstream: (40 commits)
  softmmu: support up to 12 MMU modes
  tcg: add TCG_TARGET_TLB_DISPLACEMENT_BITS
  tci: do not use CPUArchState in tcg-target.h
  Add David Gibson for sPAPR in MAINTAINERS file
  pseries: Enable in-kernel H_LOGICAL_CI_{LOAD, STORE} implementations
  spapr: override default ram size to 512MB
  machine: add default_ram_size to machine class
  spapr_pci: emit hotplug add/remove events during hotplug
  spapr_pci: enable basic hotplug operations
  pci: make pci_bar useable outside pci.c
  spapr_pci: create DRConnectors for each PCI slot during PHB realize
  spapr_pci: add dynamic-reconfiguration option for spapr-pci-host-bridge
  spapr_drc: add spapr_drc_populate_dt()
  spapr_events: event-scan RTAS interface
  spapr_events: re-use EPOW event infrastructure for hotplug events
  spapr_rtas: add ibm, configure-connector RTAS interface
  spapr: add rtas_st_buffer_direct() helper
  spapr_rtas: add get-sensor-state RTAS interface
  spapr_rtas: add set-indicator RTAS interface
  spapr_rtas: add get/set-power-level RTAS interfaces
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2015-06-04 14:04:14 +01:00
commit 3b730f570c
37 changed files with 2708 additions and 227 deletions

View file

@ -486,7 +486,8 @@ F: hw/ppc/prep.c
F: hw/pci-host/prep.[hc]
F: hw/isa/pc87312.[hc]
sPAPR
sPAPR (pseries)
M: David Gibson <david@gibson.dropbear.id.au>
M: Alexander Graf <agraf@suse.de>
L: qemu-ppc@nongnu.org
S: Supported

6
configure vendored
View file

@ -3115,9 +3115,11 @@ fi
if test "$fdt" != "no" ; then
fdt_libs="-lfdt"
# explicitly check for libfdt_env.h as it is missing in some stable installs
# and test for required functions to make sure we are on a version >= 1.4.0
cat > $TMPC << EOF
#include <libfdt.h>
#include <libfdt_env.h>
int main(void) { return 0; }
int main(void) { fdt_get_property_by_offset(0, 0, 0); return 0; }
EOF
if compile_prog "" "$fdt_libs" ; then
# system DTC is good - use it
@ -3135,7 +3137,7 @@ EOF
fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs"
elif test "$fdt" = "yes" ; then
# have neither and want - prompt for system/submodule install
error_exit "DTC (libfdt) not present. Your options:" \
error_exit "DTC (libfdt) version >= 1.4.0 not present. Your options:" \
" (1) Preferred: Install the DTC (libfdt) devel package" \
" (2) Fetch the DTC submodule, using:" \
" git submodule update --init dtc"

View file

@ -0,0 +1,287 @@
= sPAPR Dynamic Reconfiguration =
sPAPR/"pseries" guests make use of a facility called dynamic-reconfiguration
to handle hotplugging of dynamic "physical" resources like PCI cards, or
"logical"/paravirtual resources like memory, CPUs, and "physical"
host-bridges, which are generally managed by the host/hypervisor and provided
to guests as virtualized resources. The specifics of dynamic-reconfiguration
are documented extensively in PAPR+ v2.7, Section 13.1. This document
provides a summary of that information as it applies to the implementation
within QEMU.
== Dynamic-reconfiguration Connectors ==
To manage hotplug/unplug of these resources, a firmware abstraction known as
a Dynamic Resource Connector (DRC) is used to assign a particular dynamic
resource to the guest, and provide an interface for the guest to manage
configuration/removal of the resource associated with it.
== Device-tree description of DRCs ==
A set of 4 Open Firmware device tree array properties are used to describe
the name/index/power-domain/type of each DRC allocated to a guest at
boot-time. There may be multiple sets of these arrays, rooted at different
paths in the device tree depending on the type of resource the DRCs manage.
In some cases, the DRCs themselves may be provided by a dynamic resource,
such as the DRCs managing PCI slots on a hotplugged PHB. In this case the
arrays would be fetched as part of the device tree retrieval interfaces
for hotplugged resources described under "Guest->Host interface".
The array properties are described below. Each entry/element in an array
describes the DRC identified by the element in the corresponding position
of ibm,drc-indexes:
ibm,drc-names:
first 4-bytes: BE-encoded integer denoting the number of entries
each entry: a NULL-terminated <name> string encoded as a byte array
<name> values for logical/virtual resources are defined in PAPR+ v2.7,
Section 13.5.2.4, and basically consist of the type of the resource
followed by a space and a numerical value that's unique across resources
of that type.
<name> values for "physical" resources such as PCI or VIO devices are
defined as being "location codes", which are the "location labels" of
each encapsulating device, starting from the chassis down to the
individual slot for the device, concatenated by a hyphen. This provides
a mapping of resources to a physical location in a chassis for debugging
purposes. For QEMU, this mapping is less important, so we assign a
location code that conforms to naming specifications, but is simply a
location label for the slot by itself to simplify the implementation.
The naming convention for location labels is documented in detail in
PAPR+ v2.7, Section 12.3.1.5, and in our case amounts to using "C<n>"
for PCI/VIO device slots, where <n> is unique across all PCI/VIO
device slots.
ibm,drc-indexes:
first 4-bytes: BE-encoded integer denoting the number of entries
each 4-byte entry: BE-encoded <index> integer that is unique across all DRCs
in the machine
<index> is arbitrary, but in the case of QEMU we try to maintain the
convention used to assign them to pSeries guests on pHyp:
bit[31:28]: integer encoding of <type>, where <type> is:
1 for CPU resource
2 for PHB resource
3 for VIO resource
4 for PCI resource
8 for Memory resource
bit[27:0]: integer encoding of <id>, where <id> is unique across
all resources of specified type
ibm,drc-power-domains:
first 4-bytes: BE-encoded integer denoting the number of entries
each 4-byte entry: 32-bit, BE-encoded <index> integer that specifies the
power domain the resource will be assigned to. In the case of QEMU
we associated all resources with a "live insertion" domain, where the
power is assumed to be managed automatically. The integer value for
this domain is a special value of -1.
ibm,drc-types:
first 4-bytes: BE-encoded integer denoting the number of entries
each entry: a NULL-terminated <type> string encoded as a byte array
<type> is assigned as follows:
"CPU" for a CPU
"PHB" for a physical host-bridge
"SLOT" for a VIO slot
"28" for a PCI slot
"MEM" for memory resource
== Guest->Host interface to manage dynamic resources ==
Each DRC is given a globally unique DRC Index, and resources associated with
a particular DRC are configured/managed by the guest via a number of RTAS
calls which reference individual DRCs based on the DRC index. This can be
considered the guest->host interface.
rtas-set-power-level:
arg[0]: integer identifying power domain
arg[1]: new power level for the domain, 0-100
output[0]: status, 0 on success
output[1]: power level after command
Set the power level for a specified power domain
rtas-get-power-level:
arg[0]: integer identifying power domain
output[0]: status, 0 on success
output[1]: current power level
Get the power level for a specified power domain
rtas-set-indicator:
arg[0]: integer identifying sensor/indicator type
arg[1]: index of sensor, for DR-related sensors this is generally the
DRC index
arg[2]: desired sensor value
output[0]: status, 0 on success
Set the state of an indicator or sensor. For the purpose of this document we
focus on the indicator/sensor types associated with a DRC. The types are:
9001: isolation-state, controls/indicates whether a device has been made
accessible to a guest
supported sensor values:
0: isolate, device is made unaccessible by guest OS
1: unisolate, device is made available to guest OS
9002: dr-indicator, controls "visual" indicator associated with device
supported sensor values:
0: inactive, resource may be safely removed
1: active, resource is in use and cannot be safely removed
2: identify, used to visually identify slot for interactive hotplug
3: action, in most cases, used in the same manner as identify
9003: allocation-state, generally only used for "logical" DR resources to
request the allocation/deallocation of a resource prior to acquiring
it via isolation-state->unisolate, or after releasing it via
isolation-state->isolate, respectively. for "physical" DR (like PCI
hotplug/unplug) the pre-allocation of the resource is implied and
this sensor is unused.
supported sensor values:
0: unusable, tell firmware/system the resource can be
unallocated/reclaimed and added back to the system resource pool
1: usable, request the resource be allocated/reserved for use by
guest OS
2: exchange, used to allocate a spare resource to use for fail-over
in certain situations. unused in QEMU
3: recover, used to reclaim a previously allocated resource that's
not currently allocated to the guest OS. unused in QEMU
rtas-get-sensor-state:
arg[0]: integer identifying sensor/indicator type
arg[1]: index of sensor, for DR-related sensors this is generally the
DRC index
output[0]: status, 0 on success
Used to read an indicator or sensor value.
For DR-related operations, the only noteworthy sensor is dr-entity-sense,
which has a type value of 9003, as allocation-state does in the case of
rtas-set-indicator. The semantics/encodings of the sensor values are distinct
however:
supported sensor values for dr-entity-sense (9003) sensor:
0: empty,
for physical resources: DRC/slot is empty
for logical resources: unused
1: present,
for physical resources: DRC/slot is populated with a device/resource
for logical resources: resource has been allocated to the DRC
2: unusable,
for physical resources: unused
for logical resources: DRC has no resource allocated to it
3: exchange,
for physical resources: unused
for logical resources: resource available for exchange (see
allocation-state sensor semantics above)
4: recovery,
for physical resources: unused
for logical resources: resource available for recovery (see
allocation-state sensor semantics above)
rtas-ibm-configure-connector:
arg[0]: guest physical address of 4096-byte work area buffer
arg[1]: 0, or address of additional 4096-byte work area buffer. only non-zero
if a prior RTAS response indicated a need for additional memory
output[0]: status:
0: completed transmittal of device-tree node
1: instruct guest to prepare for next DT sibling node
2: instruct guest to prepare for next DT child node
3: instruct guest to prepare for next DT property
4: instruct guest to ascend to parent DT node
5: instruct guest to provide additional work-area buffer
via arg[1]
990x: instruct guest that operation took too long and to try
again later
Used to fetch an OF device-tree description of the resource associated with
a particular DRC. The DRC index is encoded in the first 4-bytes of the first
work area buffer.
Work area layout, using 4-byte offsets:
wa[0]: DRC index of the DRC to fetch device-tree nodes from
wa[1]: 0 (hard-coded)
wa[2]: for next-sibling/next-child response:
wa offset of null-terminated string denoting the new node's name
for next-property response:
wa offset of null-terminated string denoting new property's name
wa[3]: for next-property response (unused otherwise):
byte-length of new property's value
wa[4]: for next-property response (unused otherwise):
new property's value, encoded as an OFDT-compatible byte array
== hotplug/unplug events ==
For most DR operations, the hypervisor will issue host->guest add/remove events
using the EPOW/check-exception notification framework, where the host issues a
check-exception interrupt, then provides an RTAS event log via an
rtas-check-exception call issued by the guest in response. This framework is
documented by PAPR+ v2.7, and already use in by QEMU for generating powerdown
requests via EPOW events.
For DR, this framework has been extended to include hotplug events, which were
previously unneeded due to direct manipulation of DR-related guest userspace
tools by host-level management such as an HMC. This level of management is not
applicable to PowerKVM, hence the reason for extending the notification
framework to support hotplug events.
Note that these events are not yet formally part of the PAPR+ specification,
but support for this format has already been implemented in DR-related
guest tools such as powerpc-utils/librtas, as well as kernel patches that have
been submitted to handle in-kernel processing of memory/cpu-related hotplug
events[1], and is planned for formal inclusion is PAPR+ specification. The
hotplug-specific payload is QEMU implemented as follows (with all values
encoded in big-endian format):
struct rtas_event_log_v6_hp {
#define SECTION_ID_HOTPLUG 0x4850 /* HP */
struct section_header {
uint16_t section_id; /* set to SECTION_ID_HOTPLUG */
uint16_t section_length; /* sizeof(rtas_event_log_v6_hp),
* plus the length of the DRC name
* if a DRC name identifier is
* specified for hotplug_identifier
*/
uint8_t section_version; /* version 1 */
uint8_t section_subtype; /* unused */
uint16_t creator_component_id; /* unused */
} hdr;
#define RTAS_LOG_V6_HP_TYPE_CPU 1
#define RTAS_LOG_V6_HP_TYPE_MEMORY 2
#define RTAS_LOG_V6_HP_TYPE_SLOT 3
#define RTAS_LOG_V6_HP_TYPE_PHB 4
#define RTAS_LOG_V6_HP_TYPE_PCI 5
uint8_t hotplug_type; /* type of resource/device */
#define RTAS_LOG_V6_HP_ACTION_ADD 1
#define RTAS_LOG_V6_HP_ACTION_REMOVE 2
uint8_t hotplug_action; /* action (add/remove) */
#define RTAS_LOG_V6_HP_ID_DRC_NAME 1
#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2
#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3
uint8_t hotplug_identifier; /* type of the resource identifier,
* which serves as the discriminator
* for the 'drc' union field below
*/
uint8_t reserved;
union {
uint32_t index; /* DRC index of resource to take action
* on
*/
uint32_t count; /* number of DR resources to take
* action on (guest chooses which)
*/
char name[1]; /* string representing the name of the
* DRC to take action on
*/
} drc;
} QEMU_PACKED;
[1] http://thread.gmane.org/gmane.linux.ports.ppc.embedded/75350/focus=106867

2
dtc

@ -1 +1 @@
Subproject commit bc895d6d09695d05ceb8b52486ffe861d6cfbdde
Subproject commit 65cc4d2748a2c2e6f27f1cf39e07a5dbabd80ebf

View file

@ -294,6 +294,14 @@ static void machine_init_notify(Notifier *notifier, void *data)
foreach_dynamic_sysbus_device(error_on_sysbus_device, NULL);
}
static void machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
/* Default 128 MB as guest ram size */
mc->default_ram_size = 128 * M_BYTE;
}
static void machine_initfn(Object *obj)
{
MachineState *ms = MACHINE(obj);
@ -463,6 +471,7 @@ static const TypeInfo machine_info = {
.parent = TYPE_OBJECT,
.abstract = true,
.class_size = sizeof(MachineClass),
.class_init = machine_class_init,
.instance_size = sizeof(MachineState),
.instance_init = machine_initfn,
.instance_finalize = machine_finalize,

View file

@ -126,17 +126,18 @@ static void macio_bar_setup(MacIOState *macio_state)
}
}
static int macio_common_initfn(PCIDevice *d)
static void macio_common_realize(PCIDevice *d, Error **errp)
{
MacIOState *s = MACIO(d);
SysBusDevice *sysbus_dev;
int ret;
Error *err = NULL;
d->config[0x3d] = 0x01; // interrupt on pin 1
ret = qdev_init(DEVICE(&s->cuda));
if (ret < 0) {
return ret;
object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
memory_region_add_subregion(&s->bar, 0x16000,
@ -144,12 +145,11 @@ static int macio_common_initfn(PCIDevice *d)
macio_bar_setup(s);
pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar);
return 0;
}
static int macio_initfn_ide(MacIOState *s, MACIOIDEState *ide, qemu_irq irq0,
qemu_irq irq1, int dmaid)
static void macio_realize_ide(MacIOState *s, MACIOIDEState *ide,
qemu_irq irq0, qemu_irq irq1, int dmaid,
Error **errp)
{
SysBusDevice *sysbus_dev;
@ -157,27 +157,31 @@ static int macio_initfn_ide(MacIOState *s, MACIOIDEState *ide, qemu_irq irq0,
sysbus_connect_irq(sysbus_dev, 0, irq0);
sysbus_connect_irq(sysbus_dev, 1, irq1);
macio_ide_register_dma(ide, s->dbdma, dmaid);
return qdev_init(DEVICE(ide));
object_property_set_bool(OBJECT(ide), true, "realized", errp);
}
static int macio_oldworld_initfn(PCIDevice *d)
static void macio_oldworld_realize(PCIDevice *d, Error **errp)
{
MacIOState *s = MACIO(d);
OldWorldMacIOState *os = OLDWORLD_MACIO(d);
Error *err = NULL;
SysBusDevice *sysbus_dev;
int i;
int cur_irq = 0;
int ret = macio_common_initfn(d);
if (ret < 0) {
return ret;
macio_common_realize(d, &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]);
ret = qdev_init(DEVICE(&os->nvram));
if (ret < 0) {
return ret;
object_property_set_bool(OBJECT(&os->nvram), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_dev = SYS_BUS_DEVICE(&os->nvram);
memory_region_add_subregion(&s->bar, 0x60000,
@ -194,13 +198,12 @@ static int macio_oldworld_initfn(PCIDevice *d)
qemu_irq irq0 = os->irqs[cur_irq++];
qemu_irq irq1 = os->irqs[cur_irq++];
ret = macio_initfn_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4));
if (ret < 0) {
return ret;
macio_realize_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4), &err);
if (err) {
error_propagate(errp, err);
return;
}
}
return 0;
}
static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, size_t ide_size,
@ -268,17 +271,20 @@ static const MemoryRegionOps timer_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
static int macio_newworld_initfn(PCIDevice *d)
static void macio_newworld_realize(PCIDevice *d, Error **errp)
{
MacIOState *s = MACIO(d);
NewWorldMacIOState *ns = NEWWORLD_MACIO(d);
Error *err = NULL;
SysBusDevice *sysbus_dev;
MemoryRegion *timer_memory = NULL;
int i;
int cur_irq = 0;
int ret = macio_common_initfn(d);
if (ret < 0) {
return ret;
macio_common_realize(d, &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
@ -294,9 +300,10 @@ static int macio_newworld_initfn(PCIDevice *d)
qemu_irq irq0 = ns->irqs[cur_irq++];
qemu_irq irq1 = ns->irqs[cur_irq++];
ret = macio_initfn_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4));
if (ret < 0) {
return ret;
macio_realize_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4), &err);
if (err) {
error_propagate(errp, err);
return;
}
}
@ -305,8 +312,6 @@ static int macio_newworld_initfn(PCIDevice *d)
memory_region_init_io(timer_memory, OBJECT(s), &timer_ops, NULL, "timer",
0x1000);
memory_region_add_subregion(&s->bar, 0x15000, timer_memory);
return 0;
}
static void macio_newworld_init(Object *obj)
@ -352,7 +357,7 @@ static void macio_oldworld_class_init(ObjectClass *oc, void *data)
PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
pdc->init = macio_oldworld_initfn;
pdc->realize = macio_oldworld_realize;
pdc->device_id = PCI_DEVICE_ID_APPLE_343S1201;
dc->vmsd = &vmstate_macio_oldworld;
}
@ -372,7 +377,7 @@ static void macio_newworld_class_init(ObjectClass *oc, void *data)
PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
pdc->init = macio_newworld_initfn;
pdc->realize = macio_newworld_realize;
pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL;
dc->vmsd = &vmstate_macio_newworld;
}

View file

@ -123,7 +123,7 @@ static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU;
static QLIST_HEAD(, PCIHostState) pci_host_bridges;
static int pci_bar(PCIDevice *d, int reg)
int pci_bar(PCIDevice *d, int reg)
{
uint8_t type;

View file

@ -3,7 +3,7 @@ obj-y += ppc.o ppc_booke.o
# IBM pSeries (sPAPR)
obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o
obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
obj-y += spapr_pci_vfio.o
endif

View file

@ -533,6 +533,8 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
refpoints, sizeof(refpoints))));
_FDT((fdt_property_cell(fdt, "rtas-error-log-max", RTAS_ERROR_LOG_MAX)));
_FDT((fdt_property_cell(fdt, "rtas-event-scan-rate",
RTAS_EVENT_SCAN_RATE)));
/*
* According to PAPR, rtas ibm,os-term does not guarantee a return
@ -794,8 +796,8 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr,
_FDT((fdt_pack(fdt)));
if (fdt_totalsize(fdt) > FDT_MAX_SIZE) {
hw_error("FDT too big ! 0x%x bytes (max is 0x%x)\n",
fdt_totalsize(fdt), FDT_MAX_SIZE);
error_report("FDT too big ! 0x%x bytes (max is 0x%x)",
fdt_totalsize(fdt), FDT_MAX_SIZE);
exit(1);
}
@ -899,7 +901,7 @@ static int spapr_check_htab_fd(sPAPREnvironment *spapr)
spapr->htab_fd = kvmppc_get_htab_fd(false);
if (spapr->htab_fd < 0) {
error_report("Unable to open fd for reading hash table from KVM: "
"%s", strerror(errno));
"%s", strerror(errno));
rc = -1;
}
spapr->htab_fd_stale = false;
@ -1419,7 +1421,7 @@ static void ppc_spapr_init(MachineState *machine)
rma_alloc_size = kvmppc_alloc_rma(&rma);
if (rma_alloc_size == -1) {
hw_error("qemu: Unable to create RMA\n");
error_report("Unable to create RMA");
exit(1);
}
@ -1504,6 +1506,11 @@ static void ppc_spapr_init(MachineState *machine)
qemu_register_reset(spapr_cpu_reset, cpu);
}
if (kvm_enabled()) {
/* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */
kvmppc_enable_logical_ci_hcalls();
}
/* allocate RAM */
spapr->ram_limit = ram_size;
memory_region_allocate_system_memory(ram, NULL, "ppc_spapr.ram",
@ -1520,18 +1527,18 @@ static void ppc_spapr_init(MachineState *machine)
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
if (!filename) {
hw_error("Could not find LPAR rtas '%s'\n", "spapr-rtas.bin");
error_report("Could not find LPAR rtas '%s'", "spapr-rtas.bin");
exit(1);
}
spapr->rtas_size = get_image_size(filename);
spapr->rtas_blob = g_malloc(spapr->rtas_size);
if (load_image_size(filename, spapr->rtas_blob, spapr->rtas_size) < 0) {
hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
error_report("Could not load LPAR rtas '%s'", filename);
exit(1);
}
if (spapr->rtas_size > RTAS_MAX_SIZE) {
hw_error("RTAS too big ! 0x%zx bytes (max is 0x%x)\n",
(size_t)spapr->rtas_size, RTAS_MAX_SIZE);
error_report("RTAS too big ! 0x%zx bytes (max is 0x%x)",
(size_t)spapr->rtas_size, RTAS_MAX_SIZE);
exit(1);
}
g_free(filename);
@ -1641,12 +1648,12 @@ static void ppc_spapr_init(MachineState *machine)
}
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (!filename) {
hw_error("Could not find LPAR rtas '%s'\n", bios_name);
error_report("Could not find LPAR firmware '%s'", bios_name);
exit(1);
}
fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
if (fw_size < 0) {
hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
if (fw_size <= 0) {
error_report("Could not load LPAR firmware '%s'", filename);
exit(1);
}
g_free(filename);
@ -1660,9 +1667,14 @@ static void ppc_spapr_init(MachineState *machine)
/* Prepare the device tree */
spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size,
kernel_size, kernel_le,
kernel_cmdline, spapr->epow_irq);
kernel_cmdline,
spapr->check_exception_irq);
assert(spapr->fdt_skel != NULL);
/* used by RTAS */
QTAILQ_INIT(&spapr->ccs_list);
qemu_register_reset(spapr_ccs_reset_hook, spapr);
qemu_register_boot_set(spapr_boot_set, spapr);
}
@ -1794,6 +1806,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
mc->max_cpus = MAX_CPUS;
mc->no_parallel = 1;
mc->default_boot_order = "";
mc->default_ram_size = 512 * M_BYTE;
mc->kvm_type = spapr_kvm_type;
mc->has_dynamic_sysbus = true;
@ -1816,7 +1829,12 @@ static const TypeInfo spapr_machine_info = {
};
#define SPAPR_COMPAT_2_3 \
HW_COMPAT_2_3
HW_COMPAT_2_3 \
{\
.driver = "spapr-pci-host-bridge",\
.property = "dynamic-reconfiguration",\
.value = "off",\
},
#define SPAPR_COMPAT_2_2 \
SPAPR_COMPAT_2_3 \
@ -1905,10 +1923,15 @@ static const TypeInfo spapr_machine_2_2_info = {
static void spapr_machine_2_3_class_init(ObjectClass *oc, void *data)
{
static GlobalProperty compat_props[] = {
SPAPR_COMPAT_2_3
{ /* end of list */ }
};
MachineClass *mc = MACHINE_CLASS(oc);
mc->name = "pseries-2.3";
mc->desc = "pSeries Logical Partition (PAPR compliant) v2.3";
mc->compat_props = compat_props;
}
static const TypeInfo spapr_machine_2_3_info = {

744
hw/ppc/spapr_drc.c Normal file
View file

@ -0,0 +1,744 @@
/*
* QEMU SPAPR Dynamic Reconfiguration Connector Implementation
*
* Copyright IBM Corp. 2014
*
* Authors:
* Michael Roth <mdroth@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "hw/ppc/spapr_drc.h"
#include "qom/object.h"
#include "hw/qdev.h"
#include "qapi/visitor.h"
#include "qemu/error-report.h"
/* #define DEBUG_SPAPR_DRC */
#ifdef DEBUG_SPAPR_DRC
#define DPRINTF(fmt, ...) \
do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
#define DPRINTFN(fmt, ...) \
do { DPRINTF(fmt, ## __VA_ARGS__); fprintf(stderr, "\n"); } while (0)
#else
#define DPRINTF(fmt, ...) \
do { } while (0)
#define DPRINTFN(fmt, ...) \
do { } while (0)
#endif
#define DRC_CONTAINER_PATH "/dr-connector"
#define DRC_INDEX_TYPE_SHIFT 28
#define DRC_INDEX_ID_MASK (~(~0 << DRC_INDEX_TYPE_SHIFT))
static sPAPRDRConnectorTypeShift get_type_shift(sPAPRDRConnectorType type)
{
uint32_t shift = 0;
/* make sure this isn't SPAPR_DR_CONNECTOR_TYPE_ANY, or some
* other wonky value.
*/
g_assert(is_power_of_2(type));
while (type != (1 << shift)) {
shift++;
}
return shift;
}
static uint32_t get_index(sPAPRDRConnector *drc)
{
/* no set format for a drc index: it only needs to be globally
* unique. this is how we encode the DRC type on bare-metal
* however, so might as well do that here
*/
return (get_type_shift(drc->type) << DRC_INDEX_TYPE_SHIFT) |
(drc->id & DRC_INDEX_ID_MASK);
}
static int set_isolation_state(sPAPRDRConnector *drc,
sPAPRDRIsolationState state)
{
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
DPRINTFN("drc: %x, set_isolation_state: %x", get_index(drc), state);
drc->isolation_state = state;
if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) {
/* if we're awaiting release, but still in an unconfigured state,
* it's likely the guest is still in the process of configuring
* the device and is transitioning the devices to an ISOLATED
* state as a part of that process. so we only complete the
* removal when this transition happens for a device in a
* configured state, as suggested by the state diagram from
* PAPR+ 2.7, 13.4
*/
if (drc->awaiting_release) {
if (drc->configured) {
DPRINTFN("finalizing device removal");
drck->detach(drc, DEVICE(drc->dev), drc->detach_cb,
drc->detach_cb_opaque, NULL);
} else {
DPRINTFN("deferring device removal on unconfigured device\n");
}
}
drc->configured = false;
}
return 0;
}
static int set_indicator_state(sPAPRDRConnector *drc,
sPAPRDRIndicatorState state)
{
DPRINTFN("drc: %x, set_indicator_state: %x", get_index(drc), state);
drc->indicator_state = state;
return 0;
}
static int set_allocation_state(sPAPRDRConnector *drc,
sPAPRDRAllocationState state)
{
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
DPRINTFN("drc: %x, set_allocation_state: %x", get_index(drc), state);
if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) {
drc->allocation_state = state;
if (drc->awaiting_release &&
drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
DPRINTFN("finalizing device removal");
drck->detach(drc, DEVICE(drc->dev), drc->detach_cb,
drc->detach_cb_opaque, NULL);
}
}
return 0;
}
static uint32_t get_type(sPAPRDRConnector *drc)
{
return drc->type;
}
static const char *get_name(sPAPRDRConnector *drc)
{
return drc->name;
}
static const void *get_fdt(sPAPRDRConnector *drc, int *fdt_start_offset)
{
if (fdt_start_offset) {
*fdt_start_offset = drc->fdt_start_offset;
}
return drc->fdt;
}
static void set_configured(sPAPRDRConnector *drc)
{
DPRINTFN("drc: %x, set_configured", get_index(drc));
if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_UNISOLATED) {
/* guest should be not configuring an isolated device */
DPRINTFN("drc: %x, set_configured: skipping isolated device",
get_index(drc));
return;
}
drc->configured = true;
}
/*
* dr-entity-sense sensor value
* returned via get-sensor-state RTAS calls
* as expected by state diagram in PAPR+ 2.7, 13.4
* based on the current allocation/indicator/power states
* for the DR connector.
*/
static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc)
{
sPAPRDREntitySense state;
if (drc->dev) {
if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI &&
drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
/* for logical DR, we return a state of UNUSABLE
* iff the allocation state UNUSABLE.
* Otherwise, report the state as USABLE/PRESENT,
* as we would for PCI.
*/
state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
} else {
/* this assumes all PCI devices are assigned to
* a 'live insertion' power domain, where QEMU
* manages power state automatically as opposed
* to the guest. present, non-PCI resources are
* unaffected by power state.
*/
state = SPAPR_DR_ENTITY_SENSE_PRESENT;
}
} else {
if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
/* PCI devices, and only PCI devices, use EMPTY
* in cases where we'd otherwise use UNUSABLE
*/
state = SPAPR_DR_ENTITY_SENSE_EMPTY;
} else {
state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
}
}
DPRINTFN("drc: %x, entity_sense: %x", get_index(drc), state);
return state;
}
static void prop_get_index(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
uint32_t value = (uint32_t)drck->get_index(drc);
visit_type_uint32(v, &value, name, errp);
}
static void prop_get_type(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
uint32_t value = (uint32_t)drck->get_type(drc);
visit_type_uint32(v, &value, name, errp);
}
static char *prop_get_name(Object *obj, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
return g_strdup(drck->get_name(drc));
}
static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
uint32_t value = (uint32_t)drck->entity_sense(drc);
visit_type_uint32(v, &value, name, errp);
}
static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
int fdt_offset_next, fdt_offset, fdt_depth;
void *fdt;
if (!drc->fdt) {
return;
}
fdt = drc->fdt;
fdt_offset = drc->fdt_start_offset;
fdt_depth = 0;
do {
const char *name = NULL;
const struct fdt_property *prop = NULL;
int prop_len = 0, name_len = 0;
uint32_t tag;
tag = fdt_next_tag(fdt, fdt_offset, &fdt_offset_next);
switch (tag) {
case FDT_BEGIN_NODE:
fdt_depth++;
name = fdt_get_name(fdt, fdt_offset, &name_len);
visit_start_struct(v, NULL, NULL, name, 0, NULL);
break;
case FDT_END_NODE:
/* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */
g_assert(fdt_depth > 0);
visit_end_struct(v, NULL);
fdt_depth--;
break;
case FDT_PROP: {
int i;
prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len);
name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
visit_start_list(v, name, NULL);
for (i = 0; i < prop_len; i++) {
visit_type_uint8(v, (uint8_t *)&prop->data[i], NULL, NULL);
}
visit_end_list(v, NULL);
break;
}
default:
error_setg(&error_abort, "device FDT in unexpected state: %d", tag);
}
fdt_offset = fdt_offset_next;
} while (fdt_depth != 0);
}
static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
int fdt_start_offset, bool coldplug, Error **errp)
{
DPRINTFN("drc: %x, attach", get_index(drc));
if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) {
error_setg(errp, "an attached device is still awaiting release");
return;
}
if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
g_assert(drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_USABLE);
}
g_assert(fdt || coldplug);
/* NOTE: setting initial isolation state to UNISOLATED means we can't
* detach unless guest has a userspace/kernel that moves this state
* back to ISOLATED in response to an unplug event, or this is done
* manually by the admin prior. if we force things while the guest
* may be accessing the device, we can easily crash the guest, so we
* we defer completion of removal in such cases to the reset() hook.
*/
if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
drc->isolation_state = SPAPR_DR_ISOLATION_STATE_UNISOLATED;
}
drc->indicator_state = SPAPR_DR_INDICATOR_STATE_ACTIVE;
drc->dev = d;
drc->fdt = fdt;
drc->fdt_start_offset = fdt_start_offset;
drc->configured = false;
object_property_add_link(OBJECT(drc), "device",
object_get_typename(OBJECT(drc->dev)),
(Object **)(&drc->dev),
NULL, 0, NULL);
}
static void detach(sPAPRDRConnector *drc, DeviceState *d,
spapr_drc_detach_cb *detach_cb,
void *detach_cb_opaque, Error **errp)
{
DPRINTFN("drc: %x, detach", get_index(drc));
drc->detach_cb = detach_cb;
drc->detach_cb_opaque = detach_cb_opaque;
if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) {
DPRINTFN("awaiting transition to isolated state before removal");
drc->awaiting_release = true;
return;
}
if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI &&
drc->allocation_state != SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
DPRINTFN("awaiting transition to unusable state before removal");
drc->awaiting_release = true;
return;
}
drc->indicator_state = SPAPR_DR_INDICATOR_STATE_INACTIVE;
if (drc->detach_cb) {
drc->detach_cb(drc->dev, drc->detach_cb_opaque);
}
drc->awaiting_release = false;
g_free(drc->fdt);
drc->fdt = NULL;
drc->fdt_start_offset = 0;
object_property_del(OBJECT(drc), "device", NULL);
drc->dev = NULL;
drc->detach_cb = NULL;
drc->detach_cb_opaque = NULL;
}
static bool release_pending(sPAPRDRConnector *drc)
{
return drc->awaiting_release;
}
static void reset(DeviceState *d)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
DPRINTFN("drc reset: %x", drck->get_index(drc));
/* immediately upon reset we can safely assume DRCs whose devices
* are pending removal can be safely removed, and that they will
* subsequently be left in an ISOLATED state. move the DRC to this
* state in these cases (which will in turn complete any pending
* device removals)
*/
if (drc->awaiting_release) {
drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_ISOLATED);
/* generally this should also finalize the removal, but if the device
* hasn't yet been configured we normally defer removal under the
* assumption that this transition is taking place as part of device
* configuration. so check if we're still waiting after this, and
* force removal if we are
*/
if (drc->awaiting_release) {
drck->detach(drc, DEVICE(drc->dev), drc->detach_cb,
drc->detach_cb_opaque, NULL);
}
/* non-PCI devices may be awaiting a transition to UNUSABLE */
if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI &&
drc->awaiting_release) {
drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_UNUSABLE);
}
}
}
static void realize(DeviceState *d, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
Object *root_container;
char link_name[256];
gchar *child_name;
Error *err = NULL;
DPRINTFN("drc realize: %x", drck->get_index(drc));
/* NOTE: we do this as part of realize/unrealize due to the fact
* that the guest will communicate with the DRC via RTAS calls
* referencing the global DRC index. By unlinking the DRC
* from DRC_CONTAINER_PATH/<drc_index> we effectively make it
* inaccessible by the guest, since lookups rely on this path
* existing in the composition tree
*/
root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
snprintf(link_name, sizeof(link_name), "%x", drck->get_index(drc));
child_name = object_get_canonical_path_component(OBJECT(drc));
DPRINTFN("drc child name: %s", child_name);
object_property_add_alias(root_container, link_name,
drc->owner, child_name, &err);
if (err) {
error_report("%s", error_get_pretty(err));
error_free(err);
object_unref(OBJECT(drc));
}
DPRINTFN("drc realize complete");
}
static void unrealize(DeviceState *d, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
Object *root_container;
char name[256];
Error *err = NULL;
DPRINTFN("drc unrealize: %x", drck->get_index(drc));
root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
snprintf(name, sizeof(name), "%x", drck->get_index(drc));
object_property_del(root_container, name, &err);
if (err) {
error_report("%s", error_get_pretty(err));
error_free(err);
object_unref(OBJECT(drc));
}
}
sPAPRDRConnector *spapr_dr_connector_new(Object *owner,
sPAPRDRConnectorType type,
uint32_t id)
{
sPAPRDRConnector *drc =
SPAPR_DR_CONNECTOR(object_new(TYPE_SPAPR_DR_CONNECTOR));
g_assert(type);
drc->type = type;
drc->id = id;
drc->owner = owner;
object_property_add_child(owner, "dr-connector[*]", OBJECT(drc), NULL);
object_property_set_bool(OBJECT(drc), true, "realized", NULL);
/* human-readable name for a DRC to encode into the DT
* description. this is mainly only used within a guest in place
* of the unique DRC index.
*
* in the case of VIO/PCI devices, it corresponds to a
* "location code" that maps a logical device/function (DRC index)
* to a physical (or virtual in the case of VIO) location in the
* system by chaining together the "location label" for each
* encapsulating component.
*
* since this is more to do with diagnosing physical hardware
* issues than guest compatibility, we choose location codes/DRC
* names that adhere to the documented format, but avoid encoding
* the entire topology information into the label/code, instead
* just using the location codes based on the labels for the
* endpoints (VIO/PCI adaptor connectors), which is basically
* just "C" followed by an integer ID.
*
* DRC names as documented by PAPR+ v2.7, 13.5.2.4
* location codes as documented by PAPR+ v2.7, 12.3.1.5
*/
switch (drc->type) {
case SPAPR_DR_CONNECTOR_TYPE_CPU:
drc->name = g_strdup_printf("CPU %d", id);
break;
case SPAPR_DR_CONNECTOR_TYPE_PHB:
drc->name = g_strdup_printf("PHB %d", id);
break;
case SPAPR_DR_CONNECTOR_TYPE_VIO:
case SPAPR_DR_CONNECTOR_TYPE_PCI:
drc->name = g_strdup_printf("C%d", id);
break;
case SPAPR_DR_CONNECTOR_TYPE_LMB:
drc->name = g_strdup_printf("LMB %d", id);
break;
default:
g_assert(false);
}
/* PCI slot always start in a USABLE state, and stay there */
if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
drc->allocation_state = SPAPR_DR_ALLOCATION_STATE_USABLE;
}
return drc;
}
static void spapr_dr_connector_instance_init(Object *obj)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
object_property_add_uint32_ptr(obj, "isolation-state",
&drc->isolation_state, NULL);
object_property_add_uint32_ptr(obj, "indicator-state",
&drc->indicator_state, NULL);
object_property_add_uint32_ptr(obj, "allocation-state",
&drc->allocation_state, NULL);
object_property_add_uint32_ptr(obj, "id", &drc->id, NULL);
object_property_add(obj, "index", "uint32", prop_get_index,
NULL, NULL, NULL, NULL);
object_property_add(obj, "connector_type", "uint32", prop_get_type,
NULL, NULL, NULL, NULL);
object_property_add_str(obj, "name", prop_get_name, NULL, NULL);
object_property_add(obj, "entity-sense", "uint32", prop_get_entity_sense,
NULL, NULL, NULL, NULL);
object_property_add(obj, "fdt", "struct", prop_get_fdt,
NULL, NULL, NULL, NULL);
}
static void spapr_dr_connector_class_init(ObjectClass *k, void *data)
{
DeviceClass *dk = DEVICE_CLASS(k);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
dk->reset = reset;
dk->realize = realize;
dk->unrealize = unrealize;
drck->set_isolation_state = set_isolation_state;
drck->set_indicator_state = set_indicator_state;
drck->set_allocation_state = set_allocation_state;
drck->get_index = get_index;
drck->get_type = get_type;
drck->get_name = get_name;
drck->get_fdt = get_fdt;
drck->set_configured = set_configured;
drck->entity_sense = entity_sense;
drck->attach = attach;
drck->detach = detach;
drck->release_pending = release_pending;
}
static const TypeInfo spapr_dr_connector_info = {
.name = TYPE_SPAPR_DR_CONNECTOR,
.parent = TYPE_DEVICE,
.instance_size = sizeof(sPAPRDRConnector),
.instance_init = spapr_dr_connector_instance_init,
.class_size = sizeof(sPAPRDRConnectorClass),
.class_init = spapr_dr_connector_class_init,
};
static void spapr_drc_register_types(void)
{
type_register_static(&spapr_dr_connector_info);
}
type_init(spapr_drc_register_types)
/* helper functions for external users */
sPAPRDRConnector *spapr_dr_connector_by_index(uint32_t index)
{
Object *obj;
char name[256];
snprintf(name, sizeof(name), "%s/%x", DRC_CONTAINER_PATH, index);
obj = object_resolve_path(name, NULL);
return !obj ? NULL : SPAPR_DR_CONNECTOR(obj);
}
sPAPRDRConnector *spapr_dr_connector_by_id(sPAPRDRConnectorType type,
uint32_t id)
{
return spapr_dr_connector_by_index(
(get_type_shift(type) << DRC_INDEX_TYPE_SHIFT) |
(id & DRC_INDEX_ID_MASK));
}
/* generate a string the describes the DRC to encode into the
* device tree.
*
* as documented by PAPR+ v2.7, 13.5.2.6 and C.6.1
*/
static const char *spapr_drc_get_type_str(sPAPRDRConnectorType type)
{
switch (type) {
case SPAPR_DR_CONNECTOR_TYPE_CPU:
return "CPU";
case SPAPR_DR_CONNECTOR_TYPE_PHB:
return "PHB";
case SPAPR_DR_CONNECTOR_TYPE_VIO:
return "SLOT";
case SPAPR_DR_CONNECTOR_TYPE_PCI:
return "28";
case SPAPR_DR_CONNECTOR_TYPE_LMB:
return "MEM";
default:
g_assert(false);
}
return NULL;
}
/**
* spapr_drc_populate_dt
*
* @fdt: libfdt device tree
* @path: path in the DT to generate properties
* @owner: parent Object/DeviceState for which to generate DRC
* descriptions for
* @drc_type_mask: mask of sPAPRDRConnectorType values corresponding
* to the types of DRCs to generate entries for
*
* generate OF properties to describe DRC topology/indices to guests
*
* as documented in PAPR+ v2.1, 13.5.2
*/
int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
uint32_t drc_type_mask)
{
Object *root_container;
ObjectProperty *prop;
uint32_t drc_count = 0;
GArray *drc_indexes, *drc_power_domains;
GString *drc_names, *drc_types;
int ret;
/* the first entry of each properties is a 32-bit integer encoding
* the number of elements in the array. we won't know this until
* we complete the iteration through all the matching DRCs, but
* reserve the space now and set the offsets accordingly so we
* can fill them in later.
*/
drc_indexes = g_array_new(false, true, sizeof(uint32_t));
drc_indexes = g_array_set_size(drc_indexes, 1);
drc_power_domains = g_array_new(false, true, sizeof(uint32_t));
drc_power_domains = g_array_set_size(drc_power_domains, 1);
drc_names = g_string_set_size(g_string_new(NULL), sizeof(uint32_t));
drc_types = g_string_set_size(g_string_new(NULL), sizeof(uint32_t));
/* aliases for all DRConnector objects will be rooted in QOM
* composition tree at DRC_CONTAINER_PATH
*/
root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
QTAILQ_FOREACH(prop, &root_container->properties, node) {
Object *obj;
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
uint32_t drc_index, drc_power_domain;
if (!strstart(prop->type, "link<", NULL)) {
continue;
}
obj = object_property_get_link(root_container, prop->name, NULL);
drc = SPAPR_DR_CONNECTOR(obj);
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
if (owner && (drc->owner != owner)) {
continue;
}
if ((drc->type & drc_type_mask) == 0) {
continue;
}
drc_count++;
/* ibm,drc-indexes */
drc_index = cpu_to_be32(drck->get_index(drc));
g_array_append_val(drc_indexes, drc_index);
/* ibm,drc-power-domains */
drc_power_domain = cpu_to_be32(-1);
g_array_append_val(drc_power_domains, drc_power_domain);
/* ibm,drc-names */
drc_names = g_string_append(drc_names, drck->get_name(drc));
drc_names = g_string_insert_len(drc_names, -1, "\0", 1);
/* ibm,drc-types */
drc_types = g_string_append(drc_types,
spapr_drc_get_type_str(drc->type));
drc_types = g_string_insert_len(drc_types, -1, "\0", 1);
}
/* now write the drc count into the space we reserved at the
* beginning of the arrays previously
*/
*(uint32_t *)drc_indexes->data = cpu_to_be32(drc_count);
*(uint32_t *)drc_power_domains->data = cpu_to_be32(drc_count);
*(uint32_t *)drc_names->str = cpu_to_be32(drc_count);
*(uint32_t *)drc_types->str = cpu_to_be32(drc_count);
ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-indexes",
drc_indexes->data,
drc_indexes->len * sizeof(uint32_t));
if (ret) {
fprintf(stderr, "Couldn't create ibm,drc-indexes property\n");
goto out;
}
ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-power-domains",
drc_power_domains->data,
drc_power_domains->len * sizeof(uint32_t));
if (ret) {
fprintf(stderr, "Couldn't finalize ibm,drc-power-domains property\n");
goto out;
}
ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-names",
drc_names->str, drc_names->len);
if (ret) {
fprintf(stderr, "Couldn't finalize ibm,drc-names property\n");
goto out;
}
ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-types",
drc_types->str, drc_types->len);
if (ret) {
fprintf(stderr, "Couldn't finalize ibm,drc-types property\n");
goto out;
}
out:
g_array_free(drc_indexes, true);
g_array_free(drc_power_domains, true);
g_string_free(drc_names, true);
g_string_free(drc_types, true);
return ret;
}

View file

@ -32,6 +32,9 @@
#include "hw/ppc/spapr.h"
#include "hw/ppc/spapr_vio.h"
#include "hw/pci/pci.h"
#include "hw/pci-host/spapr.h"
#include "hw/ppc/spapr_drc.h"
#include <libfdt.h>
@ -77,6 +80,7 @@ struct rtas_error_log {
#define RTAS_LOG_TYPE_ECC_UNCORR 0x00000009
#define RTAS_LOG_TYPE_ECC_CORR 0x0000000a
#define RTAS_LOG_TYPE_EPOW 0x00000040
#define RTAS_LOG_TYPE_HOTPLUG 0x000000e5
uint32_t extended_length;
} QEMU_PACKED;
@ -166,6 +170,38 @@ struct epow_log_full {
struct rtas_event_log_v6_epow epow;
} QEMU_PACKED;
struct rtas_event_log_v6_hp {
#define RTAS_LOG_V6_SECTION_ID_HOTPLUG 0x4850 /* HP */
struct rtas_event_log_v6_section_header hdr;
uint8_t hotplug_type;
#define RTAS_LOG_V6_HP_TYPE_CPU 1
#define RTAS_LOG_V6_HP_TYPE_MEMORY 2
#define RTAS_LOG_V6_HP_TYPE_SLOT 3
#define RTAS_LOG_V6_HP_TYPE_PHB 4
#define RTAS_LOG_V6_HP_TYPE_PCI 5
uint8_t hotplug_action;
#define RTAS_LOG_V6_HP_ACTION_ADD 1
#define RTAS_LOG_V6_HP_ACTION_REMOVE 2
uint8_t hotplug_identifier;
#define RTAS_LOG_V6_HP_ID_DRC_NAME 1
#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2
#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3
uint8_t reserved;
union {
uint32_t index;
uint32_t count;
char name[1];
} drc;
} QEMU_PACKED;
struct hp_log_full {
struct rtas_error_log hdr;
struct rtas_event_log_v6 v6hdr;
struct rtas_event_log_v6_maina maina;
struct rtas_event_log_v6_mainb mainb;
struct rtas_event_log_v6_hp hp;
} QEMU_PACKED;
#define EVENT_MASK_INTERNAL_ERRORS 0x80000000
#define EVENT_MASK_EPOW 0x40000000
#define EVENT_MASK_HOTPLUG 0x10000000
@ -181,67 +217,105 @@ struct epow_log_full {
} \
} while (0)
void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq)
void spapr_events_fdt_skel(void *fdt, uint32_t check_exception_irq)
{
uint32_t epow_irq_ranges[] = {cpu_to_be32(epow_irq), cpu_to_be32(1)};
uint32_t epow_interrupts[] = {cpu_to_be32(epow_irq), 0};
uint32_t irq_ranges[] = {cpu_to_be32(check_exception_irq), cpu_to_be32(1)};
uint32_t interrupts[] = {cpu_to_be32(check_exception_irq), 0};
_FDT((fdt_begin_node(fdt, "event-sources")));
_FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
_FDT((fdt_property_cell(fdt, "#interrupt-cells", 2)));
_FDT((fdt_property(fdt, "interrupt-ranges",
epow_irq_ranges, sizeof(epow_irq_ranges))));
irq_ranges, sizeof(irq_ranges))));
_FDT((fdt_begin_node(fdt, "epow-events")));
_FDT((fdt_property(fdt, "interrupts",
epow_interrupts, sizeof(epow_interrupts))));
_FDT((fdt_property(fdt, "interrupts", interrupts, sizeof(interrupts))));
_FDT((fdt_end_node(fdt)));
_FDT((fdt_end_node(fdt)));
}
static struct epow_log_full *pending_epow;
static void rtas_event_log_queue(int log_type, void *data, bool exception)
{
sPAPREventLogEntry *entry = g_new(sPAPREventLogEntry, 1);
g_assert(data);
entry->log_type = log_type;
entry->exception = exception;
entry->data = data;
QTAILQ_INSERT_TAIL(&spapr->pending_events, entry, next);
}
static sPAPREventLogEntry *rtas_event_log_dequeue(uint32_t event_mask,
bool exception)
{
sPAPREventLogEntry *entry = NULL;
/* we only queue EPOW events atm. */
if ((event_mask & EVENT_MASK_EPOW) == 0) {
return NULL;
}
QTAILQ_FOREACH(entry, &spapr->pending_events, next) {
if (entry->exception != exception) {
continue;
}
/* EPOW and hotplug events are surfaced in the same manner */
if (entry->log_type == RTAS_LOG_TYPE_EPOW ||
entry->log_type == RTAS_LOG_TYPE_HOTPLUG) {
break;
}
}
if (entry) {
QTAILQ_REMOVE(&spapr->pending_events, entry, next);
}
return entry;
}
static bool rtas_event_log_contains(uint32_t event_mask, bool exception)
{
sPAPREventLogEntry *entry = NULL;
/* we only queue EPOW events atm. */
if ((event_mask & EVENT_MASK_EPOW) == 0) {
return false;
}
QTAILQ_FOREACH(entry, &spapr->pending_events, next) {
if (entry->exception != exception) {
continue;
}
/* EPOW and hotplug events are surfaced in the same manner */
if (entry->log_type == RTAS_LOG_TYPE_EPOW ||
entry->log_type == RTAS_LOG_TYPE_HOTPLUG) {
return true;
}
}
return false;
}
static uint32_t next_plid;
static void spapr_powerdown_req(Notifier *n, void *opaque)
static void spapr_init_v6hdr(struct rtas_event_log_v6 *v6hdr)
{
sPAPREnvironment *spapr = container_of(n, sPAPREnvironment, epow_notifier);
struct rtas_error_log *hdr;
struct rtas_event_log_v6 *v6hdr;
struct rtas_event_log_v6_maina *maina;
struct rtas_event_log_v6_mainb *mainb;
struct rtas_event_log_v6_epow *epow;
struct tm tm;
int year;
if (pending_epow) {
/* For now, we just throw away earlier events if two come
* along before any are consumed. This is sufficient for our
* powerdown messages, but we'll need more if we do more
* general error/event logging */
g_free(pending_epow);
}
pending_epow = g_malloc0(sizeof(*pending_epow));
hdr = &pending_epow->hdr;
v6hdr = &pending_epow->v6hdr;
maina = &pending_epow->maina;
mainb = &pending_epow->mainb;
epow = &pending_epow->epow;
hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
| RTAS_LOG_SEVERITY_EVENT
| RTAS_LOG_DISPOSITION_NOT_RECOVERED
| RTAS_LOG_OPTIONAL_PART_PRESENT
| RTAS_LOG_TYPE_EPOW);
hdr->extended_length = cpu_to_be32(sizeof(*pending_epow)
- sizeof(pending_epow->hdr));
v6hdr->b0 = RTAS_LOG_V6_B0_VALID | RTAS_LOG_V6_B0_NEW_LOG
| RTAS_LOG_V6_B0_BIGENDIAN;
v6hdr->b2 = RTAS_LOG_V6_B2_POWERPC_FORMAT
| RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT;
v6hdr->company = cpu_to_be32(RTAS_LOG_V6_COMPANY_IBM);
}
static void spapr_init_maina(struct rtas_event_log_v6_maina *maina,
int section_count)
{
struct tm tm;
int year;
maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA);
maina->hdr.section_length = cpu_to_be16(sizeof(*maina));
@ -256,8 +330,37 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
| (to_bcd(tm.tm_min) << 16)
| (to_bcd(tm.tm_sec) << 8));
maina->creator_id = 'H'; /* Hypervisor */
maina->section_count = 3; /* Main-A, Main-B and EPOW */
maina->section_count = section_count;
maina->plid = next_plid++;
}
static void spapr_powerdown_req(Notifier *n, void *opaque)
{
sPAPREnvironment *spapr = container_of(n, sPAPREnvironment, epow_notifier);
struct rtas_error_log *hdr;
struct rtas_event_log_v6 *v6hdr;
struct rtas_event_log_v6_maina *maina;
struct rtas_event_log_v6_mainb *mainb;
struct rtas_event_log_v6_epow *epow;
struct epow_log_full *new_epow;
new_epow = g_malloc0(sizeof(*new_epow));
hdr = &new_epow->hdr;
v6hdr = &new_epow->v6hdr;
maina = &new_epow->maina;
mainb = &new_epow->mainb;
epow = &new_epow->epow;
hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
| RTAS_LOG_SEVERITY_EVENT
| RTAS_LOG_DISPOSITION_NOT_RECOVERED
| RTAS_LOG_OPTIONAL_PART_PRESENT
| RTAS_LOG_TYPE_EPOW);
hdr->extended_length = cpu_to_be32(sizeof(*new_epow)
- sizeof(new_epow->hdr));
spapr_init_v6hdr(v6hdr);
spapr_init_maina(maina, 3 /* Main-A, Main-B and EPOW */);
mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
@ -274,7 +377,80 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
epow->event_modifier = RTAS_LOG_V6_EPOW_MODIFIER_NORMAL;
epow->extended_modifier = RTAS_LOG_V6_EPOW_XMODIFIER_PARTITION_SPECIFIC;
qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->epow_irq));
rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true);
qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
}
static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
{
struct hp_log_full *new_hp;
struct rtas_error_log *hdr;
struct rtas_event_log_v6 *v6hdr;
struct rtas_event_log_v6_maina *maina;
struct rtas_event_log_v6_mainb *mainb;
struct rtas_event_log_v6_hp *hp;
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
sPAPRDRConnectorType drc_type = drck->get_type(drc);
new_hp = g_malloc0(sizeof(struct hp_log_full));
hdr = &new_hp->hdr;
v6hdr = &new_hp->v6hdr;
maina = &new_hp->maina;
mainb = &new_hp->mainb;
hp = &new_hp->hp;
hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
| RTAS_LOG_SEVERITY_EVENT
| RTAS_LOG_DISPOSITION_NOT_RECOVERED
| RTAS_LOG_OPTIONAL_PART_PRESENT
| RTAS_LOG_INITIATOR_HOTPLUG
| RTAS_LOG_TYPE_HOTPLUG);
hdr->extended_length = cpu_to_be32(sizeof(*new_hp)
- sizeof(new_hp->hdr));
spapr_init_v6hdr(v6hdr);
spapr_init_maina(maina, 3 /* Main-A, Main-B, HP */);
mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
mainb->subsystem_id = 0x80; /* External environment */
mainb->event_severity = 0x00; /* Informational / non-error */
mainb->event_subtype = 0x00; /* Normal shutdown */
hp->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_HOTPLUG);
hp->hdr.section_length = cpu_to_be16(sizeof(*hp));
hp->hdr.section_version = 1; /* includes extended modifier */
hp->hotplug_action = hp_action;
switch (drc_type) {
case SPAPR_DR_CONNECTOR_TYPE_PCI:
hp->drc.index = cpu_to_be32(drck->get_index(drc));
hp->hotplug_identifier = RTAS_LOG_V6_HP_ID_DRC_INDEX;
hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI;
break;
default:
/* we shouldn't be signaling hotplug events for resources
* that don't support them
*/
g_assert(false);
return;
}
rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true);
qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
}
void spapr_hotplug_req_add_event(sPAPRDRConnector *drc)
{
spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_ADD);
}
void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc)
{
spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_REMOVE);
}
static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@ -282,8 +458,10 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
target_ulong args,
uint32_t nret, target_ulong rets)
{
uint32_t mask, buf, len;
uint32_t mask, buf, len, event_len;
uint64_t xinfo;
sPAPREventLogEntry *event;
struct rtas_error_log *hdr;
if ((nargs < 6) || (nargs > 7) || nret != 1) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@ -298,25 +476,85 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
xinfo |= (uint64_t)rtas_ld(args, 6) << 32;
}
if ((mask & EVENT_MASK_EPOW) && pending_epow) {
if (sizeof(*pending_epow) < len) {
len = sizeof(*pending_epow);
}
cpu_physical_memory_write(buf, pending_epow, len);
g_free(pending_epow);
pending_epow = NULL;
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
} else {
rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
event = rtas_event_log_dequeue(mask, true);
if (!event) {
goto out_no_events;
}
hdr = event->data;
event_len = be32_to_cpu(hdr->extended_length) + sizeof(*hdr);
if (event_len < len) {
len = event_len;
}
cpu_physical_memory_write(buf, event->data, len);
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
g_free(event->data);
g_free(event);
/* according to PAPR+, the IRQ must be left asserted, or re-asserted, if
* there are still pending events to be fetched via check-exception. We
* do the latter here, since our code relies on edge-triggered
* interrupts.
*/
if (rtas_event_log_contains(mask, true)) {
qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
}
return;
out_no_events:
rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
}
static void event_scan(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
{
uint32_t mask, buf, len, event_len;
sPAPREventLogEntry *event;
struct rtas_error_log *hdr;
if (nargs != 4 || nret != 1) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
mask = rtas_ld(args, 0);
buf = rtas_ld(args, 2);
len = rtas_ld(args, 3);
event = rtas_event_log_dequeue(mask, false);
if (!event) {
goto out_no_events;
}
hdr = event->data;
event_len = be32_to_cpu(hdr->extended_length) + sizeof(*hdr);
if (event_len < len) {
len = event_len;
}
cpu_physical_memory_write(buf, event->data, len);
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
g_free(event->data);
g_free(event);
return;
out_no_events:
rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
}
void spapr_events_init(sPAPREnvironment *spapr)
{
spapr->epow_irq = xics_alloc(spapr->icp, 0, 0, false);
QTAILQ_INIT(&spapr->pending_events);
spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false);
spapr->epow_notifier.notify = spapr_powerdown_req;
qemu_register_powerdown_notifier(&spapr->epow_notifier);
spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception",
check_exception);
spapr_rtas_register(RTAS_EVENT_SCAN, "event-scan", event_scan);
}

View file

@ -41,7 +41,7 @@ enum sPAPRTCEAccess {
static QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables;
static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn)
sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn)
{
sPAPRTCETable *tcet;
@ -52,7 +52,7 @@ static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn)
}
QLIST_FOREACH(tcet, &spapr_tce_tables, list) {
if (tcet->liobn == liobn) {
if (tcet->liobn == (uint32_t)liobn) {
return tcet;
}
}
@ -126,11 +126,11 @@ static MemoryRegionIOMMUOps spapr_iommu_ops = {
static int spapr_tce_table_realize(DeviceState *dev)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
uint64_t window_size = (uint64_t)tcet->nb_table << tcet->page_shift;
if (kvm_enabled()) {
if (kvm_enabled() && !(window_size >> 32)) {
tcet->table = kvmppc_create_spapr_tce(tcet->liobn,
tcet->nb_table <<
tcet->page_shift,
window_size,
&tcet->fd,
tcet->vfio_accel);
}
@ -161,6 +161,7 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
bool vfio_accel)
{
sPAPRTCETable *tcet;
char tmp[64];
if (spapr_tce_find_by_liobn(liobn)) {
fprintf(stderr, "Attempted to create TCE table with duplicate"
@ -179,7 +180,8 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
tcet->nb_table = nb_table;
tcet->vfio_accel = vfio_accel;
object_property_add_child(OBJECT(owner), "tce-table", OBJECT(tcet), NULL);
snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn);
object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL);
object_property_set_bool(OBJECT(tcet), true, "realized", NULL);
@ -247,7 +249,7 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu,
target_ulong ioba1 = ioba;
target_ulong tce_list = args[2];
target_ulong npages = args[3];
target_ulong ret = H_PARAMETER;
target_ulong ret = H_PARAMETER, tce = 0;
sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn);
CPUState *cs = CPU(cpu);
hwaddr page_mask, page_size;
@ -267,7 +269,7 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu,
for (i = 0; i < npages; ++i, ioba += page_size) {
target_ulong off = (tce_list & ~SPAPR_TCE_RW) +
i * sizeof(target_ulong);
target_ulong tce = ldq_phys(cs->as, off);
tce = ldq_be_phys(cs->as, off);
ret = put_tce_emu(tcet, ioba, tce);
if (ret) {
@ -277,11 +279,11 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu,
/* Trace last successful or the first problematic entry */
i = i ? (i - 1) : 0;
trace_spapr_iommu_indirect(liobn, ioba1, tce_list, i,
ldq_phys(cs->as,
tce_list + i * sizeof(target_ulong)),
ret);
if (SPAPR_IS_PCI_LIOBN(liobn)) {
trace_spapr_iommu_pci_indirect(liobn, ioba1, tce_list, i, tce, ret);
} else {
trace_spapr_iommu_indirect(liobn, ioba1, tce_list, i, tce, ret);
}
return ret;
}
@ -315,7 +317,11 @@ static target_ulong h_stuff_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
break;
}
}
trace_spapr_iommu_stuff(liobn, ioba, tce_value, npages, ret);
if (SPAPR_IS_PCI_LIOBN(liobn)) {
trace_spapr_iommu_pci_stuff(liobn, ioba, tce_value, npages, ret);
} else {
trace_spapr_iommu_stuff(liobn, ioba, tce_value, npages, ret);
}
return ret;
}
@ -336,7 +342,11 @@ static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
ret = put_tce_emu(tcet, ioba, tce);
}
trace_spapr_iommu_put(liobn, ioba, tce, ret);
if (SPAPR_IS_PCI_LIOBN(liobn)) {
trace_spapr_iommu_pci_put(liobn, ioba, tce, ret);
} else {
trace_spapr_iommu_put(liobn, ioba, tce, ret);
}
return ret;
}
@ -376,7 +386,11 @@ static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
args[0] = tce;
}
}
trace_spapr_iommu_get(liobn, ioba, ret, tce);
if (SPAPR_IS_PCI_LIOBN(liobn)) {
trace_spapr_iommu_pci_get(liobn, ioba, ret, tce);
} else {
trace_spapr_iommu_get(liobn, ioba, ret, tce);
}
return ret;
}

View file

@ -33,8 +33,11 @@
#include <libfdt.h>
#include "trace.h"
#include "qemu/error-report.h"
#include "qapi/qmp/qerror.h"
#include "hw/pci/pci_bus.h"
#include "hw/ppc/spapr_drc.h"
#include "sysemu/device_tree.h"
/* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */
#define RTAS_QUERY_FN 0
@ -47,7 +50,15 @@
#define RTAS_TYPE_MSI 1
#define RTAS_TYPE_MSIX 2
static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid)
#define _FDT(exp) \
do { \
int ret = (exp); \
if (ret < 0) { \
return ret; \
} \
} while (0)
sPAPRPHBState *spapr_pci_find_phb(sPAPREnvironment *spapr, uint64_t buid)
{
sPAPRPHBState *sphb;
@ -61,10 +72,10 @@ static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid)
return NULL;
}
static PCIDevice *find_dev(sPAPREnvironment *spapr, uint64_t buid,
uint32_t config_addr)
PCIDevice *spapr_pci_find_dev(sPAPREnvironment *spapr, uint64_t buid,
uint32_t config_addr)
{
sPAPRPHBState *sphb = find_phb(spapr, buid);
sPAPRPHBState *sphb = spapr_pci_find_phb(spapr, buid);
PCIHostState *phb = PCI_HOST_BRIDGE(sphb);
int bus_num = (config_addr >> 16) & 0xFF;
int devfn = (config_addr >> 8) & 0xFF;
@ -95,7 +106,7 @@ static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid,
return;
}
pci_dev = find_dev(spapr, buid, addr);
pci_dev = spapr_pci_find_dev(spapr, buid, addr);
addr = rtas_pci_cfgaddr(addr);
if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) {
@ -162,7 +173,7 @@ static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid,
return;
}
pci_dev = find_dev(spapr, buid, addr);
pci_dev = spapr_pci_find_dev(spapr, buid, addr);
addr = rtas_pci_cfgaddr(addr);
if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) {
@ -280,9 +291,9 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
}
/* Fins sPAPRPHBState */
phb = find_phb(spapr, buid);
phb = spapr_pci_find_phb(spapr, buid);
if (phb) {
pdev = find_dev(spapr, buid, config_addr);
pdev = spapr_pci_find_dev(spapr, buid, config_addr);
}
if (!phb || !pdev) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@ -381,9 +392,9 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
spapr_pci_msi *msi;
/* Find sPAPRPHBState */
phb = find_phb(spapr, buid);
phb = spapr_pci_find_phb(spapr, buid);
if (phb) {
pdev = find_dev(spapr, buid, config_addr);
pdev = spapr_pci_find_dev(spapr, buid, config_addr);
}
if (!phb || !pdev) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@ -426,7 +437,7 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
addr = rtas_ld(args, 0);
option = rtas_ld(args, 3);
sphb = find_phb(spapr, buid);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@ -461,7 +472,7 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
}
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
sphb = find_phb(spapr, buid);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@ -479,7 +490,7 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
switch (option) {
case RTAS_GET_PE_ADDR:
addr = rtas_ld(args, 0);
pdev = find_dev(spapr, buid, addr);
pdev = spapr_pci_find_dev(spapr, buid, addr);
if (!pdev) {
goto param_error_exit;
}
@ -516,7 +527,7 @@ static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu,
}
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
sphb = find_phb(spapr, buid);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@ -562,7 +573,7 @@ static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu,
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
option = rtas_ld(args, 3);
sphb = find_phb(spapr, buid);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@ -596,7 +607,7 @@ static void rtas_ibm_configure_pe(PowerPCCPU *cpu,
}
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
sphb = find_phb(spapr, buid);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@ -631,7 +642,7 @@ static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu,
}
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
sphb = find_phb(spapr, buid);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@ -731,6 +742,372 @@ static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn)
return &phb->iommu_as;
}
/* Macros to operate with address in OF binding to PCI */
#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p))
#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */
#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */
#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */
#define b_ss(x) b_x((x), 24, 2) /* the space code */
#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */
#define b_ddddd(x) b_x((x), 11, 5) /* device number */
#define b_fff(x) b_x((x), 8, 3) /* function number */
#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */
/* for 'reg'/'assigned-addresses' OF properties */
#define RESOURCE_CELLS_SIZE 2
#define RESOURCE_CELLS_ADDRESS 3
typedef struct ResourceFields {
uint32_t phys_hi;
uint32_t phys_mid;
uint32_t phys_lo;
uint32_t size_hi;
uint32_t size_lo;
} QEMU_PACKED ResourceFields;
typedef struct ResourceProps {
ResourceFields reg[8];
ResourceFields assigned[7];
uint32_t reg_len;
uint32_t assigned_len;
} ResourceProps;
/* fill in the 'reg'/'assigned-resources' OF properties for
* a PCI device. 'reg' describes resource requirements for a
* device's IO/MEM regions, 'assigned-addresses' describes the
* actual resource assignments.
*
* the properties are arrays of ('phys-addr', 'size') pairs describing
* the addressable regions of the PCI device, where 'phys-addr' is a
* RESOURCE_CELLS_ADDRESS-tuple of 32-bit integers corresponding to
* (phys.hi, phys.mid, phys.lo), and 'size' is a
* RESOURCE_CELLS_SIZE-tuple corresponding to (size.hi, size.lo).
*
* phys.hi = 0xYYXXXXZZ, where:
* 0xYY = npt000ss
* ||| |
* ||| +-- space code: 1 if IO region, 2 if MEM region
* ||+------ for non-relocatable IO: 1 if aliased
* || for relocatable IO: 1 if below 64KB
* || for MEM: 1 if below 1MB
* |+------- 1 if region is prefetchable
* +-------- 1 if region is non-relocatable
* 0xXXXX = bbbbbbbb dddddfff, encoding bus, slot, and function
* bits respectively
* 0xZZ = rrrrrrrr, the register number of the BAR corresponding
* to the region
*
* phys.mid and phys.lo correspond respectively to the hi/lo portions
* of the actual address of the region.
*
* how the phys-addr/size values are used differ slightly between
* 'reg' and 'assigned-addresses' properties. namely, 'reg' has
* an additional description for the config space region of the
* device, and in the case of QEMU has n=0 and phys.mid=phys.lo=0
* to describe the region as relocatable, with an address-mapping
* that corresponds directly to the PHB's address space for the
* resource. 'assigned-addresses' always has n=1 set with an absolute
* address assigned for the resource. in general, 'assigned-addresses'
* won't be populated, since addresses for PCI devices are generally
* unmapped initially and left to the guest to assign.
*
* note also that addresses defined in these properties are, at least
* for PAPR guests, relative to the PHBs IO/MEM windows, and
* correspond directly to the addresses in the BARs.
*
* in accordance with PCI Bus Binding to Open Firmware,
* IEEE Std 1275-1994, section 4.1.1, as implemented by PAPR+ v2.7,
* Appendix C.
*/
static void populate_resource_props(PCIDevice *d, ResourceProps *rp)
{
int bus_num = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(d))));
uint32_t dev_id = (b_bbbbbbbb(bus_num) |
b_ddddd(PCI_SLOT(d->devfn)) |
b_fff(PCI_FUNC(d->devfn)));
ResourceFields *reg, *assigned;
int i, reg_idx = 0, assigned_idx = 0;
/* config space region */
reg = &rp->reg[reg_idx++];
reg->phys_hi = cpu_to_be32(dev_id);
reg->phys_mid = 0;
reg->phys_lo = 0;
reg->size_hi = 0;
reg->size_lo = 0;
for (i = 0; i < PCI_NUM_REGIONS; i++) {
if (!d->io_regions[i].size) {
continue;
}
reg = &rp->reg[reg_idx++];
reg->phys_hi = cpu_to_be32(dev_id | b_rrrrrrrr(pci_bar(d, i)));
if (d->io_regions[i].type & PCI_BASE_ADDRESS_SPACE_IO) {
reg->phys_hi |= cpu_to_be32(b_ss(1));
} else {
reg->phys_hi |= cpu_to_be32(b_ss(2));
}
reg->phys_mid = 0;
reg->phys_lo = 0;
reg->size_hi = cpu_to_be32(d->io_regions[i].size >> 32);
reg->size_lo = cpu_to_be32(d->io_regions[i].size);
if (d->io_regions[i].addr == PCI_BAR_UNMAPPED) {
continue;
}
assigned = &rp->assigned[assigned_idx++];
assigned->phys_hi = cpu_to_be32(reg->phys_hi | b_n(1));
assigned->phys_mid = cpu_to_be32(d->io_regions[i].addr >> 32);
assigned->phys_lo = cpu_to_be32(d->io_regions[i].addr);
assigned->size_hi = reg->size_hi;
assigned->size_lo = reg->size_lo;
}
rp->reg_len = reg_idx * sizeof(ResourceFields);
rp->assigned_len = assigned_idx * sizeof(ResourceFields);
}
static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset,
int phb_index, int drc_index,
const char *drc_name)
{
ResourceProps rp;
bool is_bridge = false;
int pci_status;
if (pci_default_read_config(dev, PCI_HEADER_TYPE, 1) ==
PCI_HEADER_TYPE_BRIDGE) {
is_bridge = true;
}
/* in accordance with PAPR+ v2.7 13.6.3, Table 181 */
_FDT(fdt_setprop_cell(fdt, offset, "vendor-id",
pci_default_read_config(dev, PCI_VENDOR_ID, 2)));
_FDT(fdt_setprop_cell(fdt, offset, "device-id",
pci_default_read_config(dev, PCI_DEVICE_ID, 2)));
_FDT(fdt_setprop_cell(fdt, offset, "revision-id",
pci_default_read_config(dev, PCI_REVISION_ID, 1)));
_FDT(fdt_setprop_cell(fdt, offset, "class-code",
pci_default_read_config(dev, PCI_CLASS_DEVICE, 2)
<< 8));
if (pci_default_read_config(dev, PCI_INTERRUPT_PIN, 1)) {
_FDT(fdt_setprop_cell(fdt, offset, "interrupts",
pci_default_read_config(dev, PCI_INTERRUPT_PIN, 1)));
}
if (!is_bridge) {
_FDT(fdt_setprop_cell(fdt, offset, "min-grant",
pci_default_read_config(dev, PCI_MIN_GNT, 1)));
_FDT(fdt_setprop_cell(fdt, offset, "max-latency",
pci_default_read_config(dev, PCI_MAX_LAT, 1)));
}
if (pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2)) {
_FDT(fdt_setprop_cell(fdt, offset, "subsystem-id",
pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2)));
}
if (pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2)) {
_FDT(fdt_setprop_cell(fdt, offset, "subsystem-vendor-id",
pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2)));
}
_FDT(fdt_setprop_cell(fdt, offset, "cache-line-size",
pci_default_read_config(dev, PCI_CACHE_LINE_SIZE, 1)));
/* the following fdt cells are masked off the pci status register */
pci_status = pci_default_read_config(dev, PCI_STATUS, 2);
_FDT(fdt_setprop_cell(fdt, offset, "devsel-speed",
PCI_STATUS_DEVSEL_MASK & pci_status));
if (pci_status & PCI_STATUS_FAST_BACK) {
_FDT(fdt_setprop(fdt, offset, "fast-back-to-back", NULL, 0));
}
if (pci_status & PCI_STATUS_66MHZ) {
_FDT(fdt_setprop(fdt, offset, "66mhz-capable", NULL, 0));
}
if (pci_status & PCI_STATUS_UDF) {
_FDT(fdt_setprop(fdt, offset, "udf-supported", NULL, 0));
}
/* NOTE: this is normally generated by firmware via path/unit name,
* but in our case we must set it manually since it does not get
* processed by OF beforehand
*/
_FDT(fdt_setprop_string(fdt, offset, "name", "pci"));
_FDT(fdt_setprop(fdt, offset, "ibm,loc-code", drc_name, strlen(drc_name)));
_FDT(fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index));
_FDT(fdt_setprop_cell(fdt, offset, "#address-cells",
RESOURCE_CELLS_ADDRESS));
_FDT(fdt_setprop_cell(fdt, offset, "#size-cells",
RESOURCE_CELLS_SIZE));
_FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x",
RESOURCE_CELLS_SIZE));
populate_resource_props(dev, &rp);
_FDT(fdt_setprop(fdt, offset, "reg", (uint8_t *)rp.reg, rp.reg_len));
_FDT(fdt_setprop(fdt, offset, "assigned-addresses",
(uint8_t *)rp.assigned, rp.assigned_len));
return 0;
}
/* create OF node for pci device and required OF DT properties */
static void *spapr_create_pci_child_dt(sPAPRPHBState *phb, PCIDevice *dev,
int drc_index, const char *drc_name,
int *dt_offset)
{
void *fdt;
int offset, ret, fdt_size;
int slot = PCI_SLOT(dev->devfn);
int func = PCI_FUNC(dev->devfn);
char nodename[512];
fdt = create_device_tree(&fdt_size);
if (func != 0) {
sprintf(nodename, "pci@%d,%d", slot, func);
} else {
sprintf(nodename, "pci@%d", slot);
}
offset = fdt_add_subnode(fdt, 0, nodename);
ret = spapr_populate_pci_child_dt(dev, fdt, offset, phb->index, drc_index,
drc_name);
g_assert(!ret);
*dt_offset = offset;
return fdt;
}
static void spapr_phb_add_pci_device(sPAPRDRConnector *drc,
sPAPRPHBState *phb,
PCIDevice *pdev,
Error **errp)
{
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
DeviceState *dev = DEVICE(pdev);
int drc_index = drck->get_index(drc);
const char *drc_name = drck->get_name(drc);
void *fdt = NULL;
int fdt_start_offset = 0;
/* boot-time devices get their device tree node created by SLOF, but for
* hotplugged devices we need QEMU to generate it so the guest can fetch
* it via RTAS
*/
if (dev->hotplugged) {
fdt = spapr_create_pci_child_dt(phb, pdev, drc_index, drc_name,
&fdt_start_offset);
}
drck->attach(drc, DEVICE(pdev),
fdt, fdt_start_offset, !dev->hotplugged, errp);
if (*errp) {
g_free(fdt);
}
}
static void spapr_phb_remove_pci_device_cb(DeviceState *dev, void *opaque)
{
/* some version guests do not wait for completion of a device
* cleanup (generally done asynchronously by the kernel) before
* signaling to QEMU that the device is safe, but instead sleep
* for some 'safe' period of time. unfortunately on a busy host
* this sleep isn't guaranteed to be long enough, resulting in
* bad things like IRQ lines being left asserted during final
* device removal. to deal with this we call reset just prior
* to finalizing the device, which will put the device back into
* an 'idle' state, as the device cleanup code expects.
*/
pci_device_reset(PCI_DEVICE(dev));
object_unparent(OBJECT(dev));
}
static void spapr_phb_remove_pci_device(sPAPRDRConnector *drc,
sPAPRPHBState *phb,
PCIDevice *pdev,
Error **errp)
{
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
drck->detach(drc, DEVICE(pdev), spapr_phb_remove_pci_device_cb, phb, errp);
}
static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb,
PCIDevice *pdev)
{
uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))));
return spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_PCI,
(phb->index << 16) |
(busnr << 8) |
pdev->devfn);
}
static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler,
DeviceState *plugged_dev, Error **errp)
{
sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler));
PCIDevice *pdev = PCI_DEVICE(plugged_dev);
sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev);
Error *local_err = NULL;
/* if DR is disabled we don't need to do anything in the case of
* hotplug or coldplug callbacks
*/
if (!phb->dr_enabled) {
/* if this is a hotplug operation initiated by the user
* we need to let them know it's not enabled
*/
if (plugged_dev->hotplugged) {
error_set(errp, QERR_BUS_NO_HOTPLUG,
object_get_typename(OBJECT(phb)));
}
return;
}
g_assert(drc);
spapr_phb_add_pci_device(drc, phb, pdev, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
if (plugged_dev->hotplugged) {
spapr_hotplug_req_add_event(drc);
}
}
static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler,
DeviceState *plugged_dev, Error **errp)
{
sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler));
PCIDevice *pdev = PCI_DEVICE(plugged_dev);
sPAPRDRConnectorClass *drck;
sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev);
Error *local_err = NULL;
if (!phb->dr_enabled) {
error_set(errp, QERR_BUS_NO_HOTPLUG,
object_get_typename(OBJECT(phb)));
return;
}
g_assert(drc);
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
if (!drck->release_pending(drc)) {
spapr_phb_remove_pci_device(drc, phb, pdev, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
spapr_hotplug_req_remove_event(drc);
}
}
static void spapr_phb_realize(DeviceState *dev, Error **errp)
{
SysBusDevice *s = SYS_BUS_DEVICE(dev);
@ -742,12 +1119,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
PCIBus *bus;
uint64_t msi_window_size = 4096;
if (sphb->index != -1) {
if (sphb->index != (uint32_t)-1) {
hwaddr windows_base;
if ((sphb->buid != -1) || (sphb->dma_liobn != -1)
|| (sphb->mem_win_addr != -1)
|| (sphb->io_win_addr != -1)) {
if ((sphb->buid != (uint64_t)-1) || (sphb->dma_liobn != (uint32_t)-1)
|| (sphb->mem_win_addr != (hwaddr)-1)
|| (sphb->io_win_addr != (hwaddr)-1)) {
error_setg(errp, "Either \"index\" or other parameters must"
" be specified for PAPR PHB, not both");
return;
@ -760,7 +1137,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
}
sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index;
sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN + sphb->index;
sphb->dma_liobn = SPAPR_PCI_LIOBN(sphb->index, 0);
windows_base = SPAPR_PCI_WINDOW_BASE
+ sphb->index * SPAPR_PCI_WINDOW_SPACING;
@ -768,27 +1145,27 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
sphb->io_win_addr = windows_base + SPAPR_PCI_IO_WIN_OFF;
}
if (sphb->buid == -1) {
if (sphb->buid == (uint64_t)-1) {
error_setg(errp, "BUID not specified for PHB");
return;
}
if (sphb->dma_liobn == -1) {
if (sphb->dma_liobn == (uint32_t)-1) {
error_setg(errp, "LIOBN not specified for PHB");
return;
}
if (sphb->mem_win_addr == -1) {
if (sphb->mem_win_addr == (hwaddr)-1) {
error_setg(errp, "Memory window address not specified for PHB");
return;
}
if (sphb->io_win_addr == -1) {
if (sphb->io_win_addr == (hwaddr)-1) {
error_setg(errp, "IO window address not specified for PHB");
return;
}
if (find_phb(spapr, sphb->buid)) {
if (spapr_pci_find_phb(spapr, sphb->buid)) {
error_setg(errp, "PCI host bridges must have unique BUIDs");
return;
}
@ -824,6 +1201,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
&sphb->memspace, &sphb->iospace,
PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS);
phb->bus = bus;
qbus_set_hotplug_handler(BUS(phb->bus), DEVICE(sphb), NULL);
/*
* Initialize PHB address space.
@ -880,6 +1258,15 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
sphb->lsi_table[i].irq = irq;
}
/* allocate connectors for child PCI devices */
if (sphb->dr_enabled) {
for (i = 0; i < PCI_SLOT_MAX * 8; i++) {
spapr_dr_connector_new(OBJECT(phb),
SPAPR_DR_CONNECTOR_TYPE_PCI,
(sphb->index << 16) | i);
}
}
if (!info->finish_realize) {
error_setg(errp, "finish_realize not defined");
return;
@ -893,11 +1280,11 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp)
{
sPAPRTCETable *tcet;
uint32_t nb_table;
nb_table = SPAPR_PCI_DMA32_SIZE >> SPAPR_TCE_PAGE_SHIFT;
tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn,
0,
SPAPR_TCE_PAGE_SHIFT,
0x40000000 >> SPAPR_TCE_PAGE_SHIFT, false);
0, SPAPR_TCE_PAGE_SHIFT, nb_table, false);
if (!tcet) {
error_setg(errp, "Unable to create TCE table for %s",
sphb->dtbusname);
@ -936,6 +1323,8 @@ static Property spapr_phb_properties[] = {
DEFINE_PROP_UINT64("io_win_addr", sPAPRPHBState, io_win_addr, -1),
DEFINE_PROP_UINT64("io_win_size", sPAPRPHBState, io_win_size,
SPAPR_PCI_IO_WIN_SIZE),
DEFINE_PROP_BOOL("dynamic-reconfiguration", sPAPRPHBState, dr_enabled,
true),
DEFINE_PROP_END_OF_LIST(),
};
@ -1049,6 +1438,7 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_CLASS(klass);
HotplugHandlerClass *hp = HOTPLUG_HANDLER_CLASS(klass);
hc->root_bus_path = spapr_phb_root_bus_path;
dc->realize = spapr_phb_realize;
@ -1058,6 +1448,8 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->cannot_instantiate_with_device_add_yet = false;
spc->finish_realize = spapr_phb_finish_realize;
hp->plug = spapr_phb_hot_plug_child;
hp->unplug = spapr_phb_hot_unplug_child;
}
static const TypeInfo spapr_phb_info = {
@ -1066,6 +1458,10 @@ static const TypeInfo spapr_phb_info = {
.instance_size = sizeof(sPAPRPHBState),
.class_init = spapr_phb_class_init,
.class_size = sizeof(sPAPRPHBClass),
.interfaces = (InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
{ }
}
};
PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index)
@ -1079,45 +1475,11 @@ PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index)
return PCI_HOST_BRIDGE(dev);
}
/* Macros to operate with address in OF binding to PCI */
#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p))
#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */
#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */
#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */
#define b_ss(x) b_x((x), 24, 2) /* the space code */
#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */
#define b_ddddd(x) b_x((x), 11, 5) /* device number */
#define b_fff(x) b_x((x), 8, 3) /* function number */
#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */
typedef struct sPAPRTCEDT {
void *fdt;
int node_off;
} sPAPRTCEDT;
static int spapr_phb_children_dt(Object *child, void *opaque)
{
sPAPRTCEDT *p = opaque;
sPAPRTCETable *tcet;
tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE);
if (!tcet) {
return 0;
}
spapr_dma_dt(p->fdt, p->node_off, "ibm,dma-window",
tcet->liobn, tcet->bus_offset,
tcet->nb_table << tcet->page_shift);
/* Stop after the first window */
return 1;
}
int spapr_populate_pci_dt(sPAPRPHBState *phb,
uint32_t xics_phandle,
void *fdt)
{
int bus_off, i, j;
int bus_off, i, j, ret;
char nodename[256];
uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) };
const uint64_t mmiosize = memory_region_size(&phb->memwindow);
@ -1151,6 +1513,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
uint32_t interrupt_map_mask[] = {
cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)};
uint32_t interrupt_map[PCI_SLOT_MAX * PCI_NUM_PINS][7];
sPAPRTCETable *tcet;
/* Start populating the FDT */
sprintf(nodename, "pci@%" PRIx64, phb->buid);
@ -1159,14 +1522,6 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
return bus_off;
}
#define _FDT(exp) \
do { \
int ret = (exp); \
if (ret < 0) { \
return ret; \
} \
} while (0)
/* Write PHB properties */
_FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci"));
_FDT(fdt_setprop_string(fdt, bus_off, "compatible", "IBM,Logical_PHB"));
@ -1203,8 +1558,16 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
_FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map,
sizeof(interrupt_map)));
object_child_foreach(OBJECT(phb), spapr_phb_children_dt,
&((sPAPRTCEDT){ .fdt = fdt, .node_off = bus_off }));
tcet = spapr_tce_find_by_liobn(SPAPR_PCI_LIOBN(phb->index, 0));
spapr_dma_dt(fdt, bus_off, "ibm,dma-window",
tcet->liobn, tcet->bus_offset,
tcet->nb_table << tcet->page_shift);
ret = spapr_drc_populate_dt(fdt, bus_off, OBJECT(phb),
SPAPR_DR_CONNECTOR_TYPE_PCI);
if (ret) {
return ret;
}
return 0;
}

View file

@ -35,6 +35,55 @@
#include "qapi-event.h"
#include <libfdt.h>
#include "hw/ppc/spapr_drc.h"
/* #define DEBUG_SPAPR */
#ifdef DEBUG_SPAPR
#define DPRINTF(fmt, ...) \
do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) \
do { } while (0)
#endif
static sPAPRConfigureConnectorState *spapr_ccs_find(sPAPREnvironment *spapr,
uint32_t drc_index)
{
sPAPRConfigureConnectorState *ccs = NULL;
QTAILQ_FOREACH(ccs, &spapr->ccs_list, next) {
if (ccs->drc_index == drc_index) {
break;
}
}
return ccs;
}
static void spapr_ccs_add(sPAPREnvironment *spapr,
sPAPRConfigureConnectorState *ccs)
{
g_assert(!spapr_ccs_find(spapr, ccs->drc_index));
QTAILQ_INSERT_HEAD(&spapr->ccs_list, ccs, next);
}
static void spapr_ccs_remove(sPAPREnvironment *spapr,
sPAPRConfigureConnectorState *ccs)
{
QTAILQ_REMOVE(&spapr->ccs_list, ccs, next);
g_free(ccs);
}
void spapr_ccs_reset_hook(void *opaque)
{
sPAPREnvironment *spapr = opaque;
sPAPRConfigureConnectorState *ccs, *ccs_tmp;
QTAILQ_FOREACH_SAFE(ccs, &spapr->ccs_list, next, ccs_tmp) {
spapr_ccs_remove(spapr, ccs);
}
}
static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
@ -245,6 +294,308 @@ static void rtas_ibm_os_term(PowerPCCPU *cpu,
rtas_st(rets, 0, ret);
}
static void rtas_set_power_level(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
target_ulong rets)
{
int32_t power_domain;
if (nargs != 2 || nret != 2) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
/* we currently only use a single, "live insert" powerdomain for
* hotplugged/dlpar'd resources, so the power is always live/full (100)
*/
power_domain = rtas_ld(args, 0);
if (power_domain != -1) {
rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
return;
}
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, 100);
}
static void rtas_get_power_level(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
target_ulong rets)
{
int32_t power_domain;
if (nargs != 1 || nret != 2) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
/* we currently only use a single, "live insert" powerdomain for
* hotplugged/dlpar'd resources, so the power is always live/full (100)
*/
power_domain = rtas_ld(args, 0);
if (power_domain != -1) {
rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
return;
}
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, 100);
}
static bool sensor_type_is_dr(uint32_t sensor_type)
{
switch (sensor_type) {
case RTAS_SENSOR_TYPE_ISOLATION_STATE:
case RTAS_SENSOR_TYPE_DR:
case RTAS_SENSOR_TYPE_ALLOCATION_STATE:
return true;
}
return false;
}
static void rtas_set_indicator(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
target_ulong rets)
{
uint32_t sensor_type;
uint32_t sensor_index;
uint32_t sensor_state;
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
if (nargs != 3 || nret != 1) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
sensor_type = rtas_ld(args, 0);
sensor_index = rtas_ld(args, 1);
sensor_state = rtas_ld(args, 2);
if (!sensor_type_is_dr(sensor_type)) {
goto out_unimplemented;
}
/* if this is a DR sensor we can assume sensor_index == drc_index */
drc = spapr_dr_connector_by_index(sensor_index);
if (!drc) {
DPRINTF("rtas_set_indicator: invalid sensor/DRC index: %xh\n",
sensor_index);
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
switch (sensor_type) {
case RTAS_SENSOR_TYPE_ISOLATION_STATE:
/* if the guest is configuring a device attached to this
* DRC, we should reset the configuration state at this
* point since it may no longer be reliable (guest released
* device and needs to start over, or unplug occurred so
* the FDT is no longer valid)
*/
if (sensor_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) {
sPAPRConfigureConnectorState *ccs = spapr_ccs_find(spapr,
sensor_index);
if (ccs) {
spapr_ccs_remove(spapr, ccs);
}
}
drck->set_isolation_state(drc, sensor_state);
break;
case RTAS_SENSOR_TYPE_DR:
drck->set_indicator_state(drc, sensor_state);
break;
case RTAS_SENSOR_TYPE_ALLOCATION_STATE:
drck->set_allocation_state(drc, sensor_state);
break;
default:
goto out_unimplemented;
}
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
return;
out_unimplemented:
/* currently only DR-related sensors are implemented */
DPRINTF("rtas_set_indicator: sensor/indicator not implemented: %d\n",
sensor_type);
rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
}
static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
target_ulong rets)
{
uint32_t sensor_type;
uint32_t sensor_index;
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
uint32_t entity_sense;
if (nargs != 2 || nret != 2) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
sensor_type = rtas_ld(args, 0);
sensor_index = rtas_ld(args, 1);
if (sensor_type != RTAS_SENSOR_TYPE_ENTITY_SENSE) {
/* currently only DR-related sensors are implemented */
DPRINTF("rtas_get_sensor_state: sensor/indicator not implemented: %d\n",
sensor_type);
rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
return;
}
drc = spapr_dr_connector_by_index(sensor_index);
if (!drc) {
DPRINTF("rtas_get_sensor_state: invalid sensor/DRC index: %xh\n",
sensor_index);
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
entity_sense = drck->entity_sense(drc);
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, entity_sense);
}
/* configure-connector work area offsets, int32_t units for field
* indexes, bytes for field offset/len values.
*
* as documented by PAPR+ v2.7, 13.5.3.5
*/
#define CC_IDX_NODE_NAME_OFFSET 2
#define CC_IDX_PROP_NAME_OFFSET 2
#define CC_IDX_PROP_LEN 3
#define CC_IDX_PROP_DATA_OFFSET 4
#define CC_VAL_DATA_OFFSET ((CC_IDX_PROP_DATA_OFFSET + 1) * 4)
#define CC_WA_LEN 4096
static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
target_ulong rets)
{
uint64_t wa_addr;
uint64_t wa_offset;
uint32_t drc_index;
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
sPAPRConfigureConnectorState *ccs;
sPAPRDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE;
int rc;
const void *fdt;
if (nargs != 2 || nret != 1) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
wa_addr = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 0);
drc_index = rtas_ld(wa_addr, 0);
drc = spapr_dr_connector_by_index(drc_index);
if (!drc) {
DPRINTF("rtas_ibm_configure_connector: invalid DRC index: %xh\n",
drc_index);
rc = RTAS_OUT_PARAM_ERROR;
goto out;
}
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
fdt = drck->get_fdt(drc, NULL);
ccs = spapr_ccs_find(spapr, drc_index);
if (!ccs) {
ccs = g_new0(sPAPRConfigureConnectorState, 1);
(void)drck->get_fdt(drc, &ccs->fdt_offset);
ccs->drc_index = drc_index;
spapr_ccs_add(spapr, ccs);
}
do {
uint32_t tag;
const char *name;
const struct fdt_property *prop;
int fdt_offset_next, prop_len;
tag = fdt_next_tag(fdt, ccs->fdt_offset, &fdt_offset_next);
switch (tag) {
case FDT_BEGIN_NODE:
ccs->fdt_depth++;
name = fdt_get_name(fdt, ccs->fdt_offset, NULL);
/* provide the name of the next OF node */
wa_offset = CC_VAL_DATA_OFFSET;
rtas_st(wa_addr, CC_IDX_NODE_NAME_OFFSET, wa_offset);
rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset,
(uint8_t *)name, strlen(name) + 1);
resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD;
break;
case FDT_END_NODE:
ccs->fdt_depth--;
if (ccs->fdt_depth == 0) {
/* done sending the device tree, don't need to track
* the state anymore
*/
drck->set_configured(drc);
spapr_ccs_remove(spapr, ccs);
ccs = NULL;
resp = SPAPR_DR_CC_RESPONSE_SUCCESS;
} else {
resp = SPAPR_DR_CC_RESPONSE_PREV_PARENT;
}
break;
case FDT_PROP:
prop = fdt_get_property_by_offset(fdt, ccs->fdt_offset,
&prop_len);
name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
/* provide the name of the next OF property */
wa_offset = CC_VAL_DATA_OFFSET;
rtas_st(wa_addr, CC_IDX_PROP_NAME_OFFSET, wa_offset);
rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset,
(uint8_t *)name, strlen(name) + 1);
/* provide the length and value of the OF property. data gets
* placed immediately after NULL terminator of the OF property's
* name string
*/
wa_offset += strlen(name) + 1,
rtas_st(wa_addr, CC_IDX_PROP_LEN, prop_len);
rtas_st(wa_addr, CC_IDX_PROP_DATA_OFFSET, wa_offset);
rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset,
(uint8_t *)((struct fdt_property *)prop)->data,
prop_len);
resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY;
break;
case FDT_END:
resp = SPAPR_DR_CC_RESPONSE_ERROR;
default:
/* keep seeking for an actionable tag */
break;
}
if (ccs) {
ccs->fdt_offset = fdt_offset_next;
}
} while (resp == SPAPR_DR_CC_RESPONSE_CONTINUE);
rc = resp;
out:
rtas_st(rets, 0, rc);
}
static struct rtas_call {
const char *name;
spapr_rtas_fn fn;
@ -370,6 +721,16 @@ static void core_rtas_register_types(void)
rtas_ibm_set_system_parameter);
spapr_rtas_register(RTAS_IBM_OS_TERM, "ibm,os-term",
rtas_ibm_os_term);
spapr_rtas_register(RTAS_SET_POWER_LEVEL, "set-power-level",
rtas_set_power_level);
spapr_rtas_register(RTAS_GET_POWER_LEVEL, "get-power-level",
rtas_get_power_level);
spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator",
rtas_set_indicator);
spapr_rtas_register(RTAS_GET_SENSOR_STATE, "get-sensor-state",
rtas_get_sensor_state);
spapr_rtas_register(RTAS_IBM_CONFIGURE_CONNECTOR, "ibm,configure-connector",
rtas_ibm_configure_connector);
}
type_init(core_rtas_register_types)

View file

@ -469,7 +469,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
}
if (pc->rtce_window_size) {
uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
uint32_t liobn = SPAPR_VIO_LIOBN(dev->reg);
memory_region_init(&dev->mrroot, OBJECT(dev), "iommu-spapr-root",
ram_size);

View file

@ -27,6 +27,7 @@
#include <inttypes.h>
#include "qemu/osdep.h"
#include "qemu/queue.h"
#include "tcg-target.h"
#ifndef CONFIG_USER_ONLY
#include "exec/hwaddr.h"
#endif
@ -70,8 +71,6 @@ typedef uint64_t target_ulong;
#define TB_JMP_PAGE_MASK (TB_JMP_CACHE_SIZE - TB_JMP_PAGE_SIZE)
#if !defined(CONFIG_USER_ONLY)
#define CPU_TLB_BITS 8
#define CPU_TLB_SIZE (1 << CPU_TLB_BITS)
/* use a fully associative victim tlb of 8 entries */
#define CPU_VTLB_SIZE 8
@ -81,6 +80,38 @@ typedef uint64_t target_ulong;
#define CPU_TLB_ENTRY_BITS 5
#endif
/* TCG_TARGET_TLB_DISPLACEMENT_BITS is used in CPU_TLB_BITS to ensure that
* the TLB is not unnecessarily small, but still small enough for the
* TLB lookup instruction sequence used by the TCG target.
*
* TCG will have to generate an operand as large as the distance between
* env and the tlb_table[NB_MMU_MODES - 1][0].addend. For simplicity,
* the TCG targets just round everything up to the next power of two, and
* count bits. This works because: 1) the size of each TLB is a largish
* power of two, 2) and because the limit of the displacement is really close
* to a power of two, 3) the offset of tlb_table[0][0] inside env is smaller
* than the size of a TLB.
*
* For example, the maximum displacement 0xFFF0 on PPC and MIPS, but TCG
* just says "the displacement is 16 bits". TCG_TARGET_TLB_DISPLACEMENT_BITS
* then ensures that tlb_table at least 0x8000 bytes large ("not unnecessarily
* small": 2^15). The operand then will come up smaller than 0xFFF0 without
* any particular care, because the TLB for a single MMU mode is larger than
* 0x10000-0xFFF0=16 bytes. In the end, the maximum value of the operand
* could be something like 0xC000 (the offset of the last TLB table) plus
* 0x18 (the offset of the addend field in each TLB entry) plus the offset
* of tlb_table inside env (which is non-trivial but not huge).
*/
#define CPU_TLB_BITS \
MIN(8, \
TCG_TARGET_TLB_DISPLACEMENT_BITS - CPU_TLB_ENTRY_BITS - \
(NB_MMU_MODES <= 1 ? 0 : \
NB_MMU_MODES <= 2 ? 1 : \
NB_MMU_MODES <= 4 ? 2 : \
NB_MMU_MODES <= 8 ? 3 : 4))
#define CPU_TLB_SIZE (1 << CPU_TLB_BITS)
typedef struct CPUTLBEntry {
/* bit TARGET_LONG_BITS to TARGET_PAGE_BITS : virtual address
bit TARGET_PAGE_BITS-1..4 : Nonzero for accesses that should not

View file

@ -263,12 +263,104 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx);
#undef MEMSUFFIX
#endif /* (NB_MMU_MODES >= 7) */
#if (NB_MMU_MODES > 7)
/* Note that supporting NB_MMU_MODES == 9 would require
* changes to at least the ARM TCG backend.
*/
#error "NB_MMU_MODES > 7 is not supported for now"
#endif /* (NB_MMU_MODES > 7) */
#if (NB_MMU_MODES >= 8) && defined(MMU_MODE7_SUFFIX)
#define CPU_MMU_INDEX 7
#define MEMSUFFIX MMU_MODE7_SUFFIX
#define DATA_SIZE 1
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 2
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 4
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 8
#include "exec/cpu_ldst_template.h"
#undef CPU_MMU_INDEX
#undef MEMSUFFIX
#endif /* (NB_MMU_MODES >= 8) */
#if (NB_MMU_MODES >= 9) && defined(MMU_MODE8_SUFFIX)
#define CPU_MMU_INDEX 8
#define MEMSUFFIX MMU_MODE8_SUFFIX
#define DATA_SIZE 1
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 2
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 4
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 8
#include "exec/cpu_ldst_template.h"
#undef CPU_MMU_INDEX
#undef MEMSUFFIX
#endif /* (NB_MMU_MODES >= 9) */
#if (NB_MMU_MODES >= 10) && defined(MMU_MODE9_SUFFIX)
#define CPU_MMU_INDEX 9
#define MEMSUFFIX MMU_MODE9_SUFFIX
#define DATA_SIZE 1
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 2
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 4
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 8
#include "exec/cpu_ldst_template.h"
#undef CPU_MMU_INDEX
#undef MEMSUFFIX
#endif /* (NB_MMU_MODES >= 10) */
#if (NB_MMU_MODES >= 11) && defined(MMU_MODE10_SUFFIX)
#define CPU_MMU_INDEX 10
#define MEMSUFFIX MMU_MODE10_SUFFIX
#define DATA_SIZE 1
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 2
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 4
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 8
#include "exec/cpu_ldst_template.h"
#undef CPU_MMU_INDEX
#undef MEMSUFFIX
#endif /* (NB_MMU_MODES >= 11) */
#if (NB_MMU_MODES >= 12) && defined(MMU_MODE11_SUFFIX)
#define CPU_MMU_INDEX 11
#define MEMSUFFIX MMU_MODE11_SUFFIX
#define DATA_SIZE 1
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 2
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 4
#include "exec/cpu_ldst_template.h"
#define DATA_SIZE 8
#include "exec/cpu_ldst_template.h"
#undef CPU_MMU_INDEX
#undef MEMSUFFIX
#endif /* (NB_MMU_MODES >= 12) */
#if (NB_MMU_MODES > 12)
#error "NB_MMU_MODES > 12 is not supported for now"
#endif /* (NB_MMU_MODES > 12) */
/* these access are slower, they must be as rare as possible */
#define CPU_MMU_INDEX (cpu_mmu_index(env))

View file

@ -106,6 +106,7 @@ struct MachineClass {
const char *default_display;
GlobalProperty *compat_props;
const char *hw_version;
ram_addr_t default_ram_size;
HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
DeviceState *dev);

View file

@ -71,6 +71,7 @@ struct sPAPRPHBState {
uint32_t index;
uint64_t buid;
char *dtbusname;
bool dr_enabled;
MemoryRegion memspace, iospace;
hwaddr mem_win_addr, mem_win_size, io_win_addr, io_win_size;
@ -114,6 +115,8 @@ struct sPAPRPHBVFIOState {
#define SPAPR_PCI_MSI_WINDOW 0x40000000000ULL
#define SPAPR_PCI_DMA32_SIZE 0x40000000
static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin)
{
return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq);
@ -129,4 +132,8 @@ void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr);
void spapr_pci_rtas_init(void);
sPAPRPHBState *spapr_pci_find_phb(sPAPREnvironment *spapr, uint64_t buid);
PCIDevice *spapr_pci_find_dev(sPAPREnvironment *spapr, uint64_t buid,
uint32_t config_addr);
#endif /* __HW_SPAPR_PCI_H__ */

View file

@ -334,6 +334,12 @@ int pci_device_load(PCIDevice *s, QEMUFile *f);
MemoryRegion *pci_address_space(PCIDevice *dev);
MemoryRegion *pci_address_space_io(PCIDevice *dev);
/*
* Should not normally be used by devices. For use by sPAPR target
* where QEMU emulates firmware.
*/
int pci_bar(PCIDevice *d, int reg);
typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level);
typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num);
typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin);

View file

@ -3,10 +3,13 @@
#include "sysemu/dma.h"
#include "hw/ppc/xics.h"
#include "hw/ppc/spapr_drc.h"
struct VIOsPAPRBus;
struct sPAPRPHBState;
struct sPAPRNVRAM;
typedef struct sPAPRConfigureConnectorState sPAPRConfigureConnectorState;
typedef struct sPAPREventLogEntry sPAPREventLogEntry;
#define HPTE64_V_HPTE_DIRTY 0x0000000000000040ULL
@ -31,14 +34,18 @@ typedef struct sPAPREnvironment {
struct PPCTimebase tb;
bool has_graphics;
uint32_t epow_irq;
uint32_t check_exception_irq;
Notifier epow_notifier;
QTAILQ_HEAD(, sPAPREventLogEntry) pending_events;
/* Migration state */
int htab_save_index;
bool htab_first_pass;
int htab_fd;
bool htab_fd_stale;
/* RTAS state */
QTAILQ_HEAD(, sPAPRConfigureConnectorState) ccs_list;
} sPAPREnvironment;
#define H_SUCCESS 0
@ -430,6 +437,17 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi);
#define RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE 42
#define RTAS_SYSPARM_UUID 48
/* RTAS indicator/sensor types
*
* as defined by PAPR+ 2.7 7.3.5.4, Table 41
*
* NOTE: currently only DR-related sensors are implemented here
*/
#define RTAS_SENSOR_TYPE_ISOLATION_STATE 9001
#define RTAS_SENSOR_TYPE_DR 9002
#define RTAS_SENSOR_TYPE_ALLOCATION_STATE 9003
#define RTAS_SENSOR_TYPE_ENTITY_SENSE RTAS_SENSOR_TYPE_ALLOCATION_STATE
/* Possible values for the platform-processor-diagnostics-run-mode parameter
* of the RTAS ibm,get-system-parameter call.
*/
@ -453,6 +471,13 @@ static inline void rtas_st(target_ulong phys, int n, uint32_t val)
stl_be_phys(&address_space_memory, ppc64_phys_to_real(phys + 4*n), val);
}
static inline void rtas_st_buffer_direct(target_ulong phys,
target_ulong phys_len,
uint8_t *buffer, uint16_t buffer_len)
{
cpu_physical_memory_write(ppc64_phys_to_real(phys), buffer,
MIN(buffer_len, phys_len));
}
static inline void rtas_st_buffer(target_ulong phys, target_ulong phys_len,
uint8_t *buffer, uint16_t buffer_len)
@ -462,8 +487,7 @@ static inline void rtas_st_buffer(target_ulong phys, target_ulong phys_len,
}
stw_be_phys(&address_space_memory,
ppc64_phys_to_real(phys), buffer_len);
cpu_physical_memory_write(ppc64_phys_to_real(phys + 2),
buffer, MIN(buffer_len, phys_len - 2));
rtas_st_buffer_direct(phys + 2, phys_len - 2, buffer, buffer_len);
}
typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@ -482,10 +506,16 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr,
#define SPAPR_TCE_PAGE_MASK (SPAPR_TCE_PAGE_SIZE - 1)
#define SPAPR_VIO_BASE_LIOBN 0x00000000
#define SPAPR_PCI_BASE_LIOBN 0x80000000
#define SPAPR_VIO_LIOBN(reg) (0x00000000 | (reg))
#define SPAPR_PCI_LIOBN(phb_index, window_num) \
(0x80000000 | ((phb_index) << 8) | (window_num))
#define SPAPR_IS_PCI_LIOBN(liobn) (!!((liobn) & 0x80000000))
#define SPAPR_PCI_DMA_WINDOW_NUM(liobn) ((liobn) & 0xff)
#define RTAS_ERROR_LOG_MAX 2048
#define RTAS_EVENT_SCAN_RATE 1
typedef struct sPAPRTCETable sPAPRTCETable;
#define TYPE_SPAPR_TCE_TABLE "spapr-tce-table"
@ -507,6 +537,15 @@ struct sPAPRTCETable {
QLIST_ENTRY(sPAPRTCETable) list;
};
sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn);
struct sPAPREventLogEntry {
int log_type;
bool exception;
void *data;
QTAILQ_ENTRY(sPAPREventLogEntry) next;
};
void spapr_events_init(sPAPREnvironment *spapr);
void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq);
int spapr_h_cas_compose_response(target_ulong addr, target_ulong size);
@ -521,6 +560,18 @@ int spapr_dma_dt(void *fdt, int node_off, const char *propname,
int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname,
sPAPRTCETable *tcet);
void spapr_pci_switch_vga(bool big_endian);
void spapr_hotplug_req_add_event(sPAPRDRConnector *drc);
void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc);
/* rtas-configure-connector state */
struct sPAPRConfigureConnectorState {
uint32_t drc_index;
int fdt_offset;
int fdt_depth;
QTAILQ_ENTRY(sPAPRConfigureConnectorState) next;
};
void spapr_ccs_reset_hook(void *opaque);
#define TYPE_SPAPR_RTC "spapr-rtc"

201
include/hw/ppc/spapr_drc.h Normal file
View file

@ -0,0 +1,201 @@
/*
* QEMU SPAPR Dynamic Reconfiguration Connector Implementation
*
* Copyright IBM Corp. 2014
*
* Authors:
* Michael Roth <mdroth@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#if !defined(__HW_SPAPR_DRC_H__)
#define __HW_SPAPR_DRC_H__
#include "qom/object.h"
#include "hw/qdev.h"
#include "libfdt.h"
#define TYPE_SPAPR_DR_CONNECTOR "spapr-dr-connector"
#define SPAPR_DR_CONNECTOR_GET_CLASS(obj) \
OBJECT_GET_CLASS(sPAPRDRConnectorClass, obj, TYPE_SPAPR_DR_CONNECTOR)
#define SPAPR_DR_CONNECTOR_CLASS(klass) \
OBJECT_CLASS_CHECK(sPAPRDRConnectorClass, klass, \
TYPE_SPAPR_DR_CONNECTOR)
#define SPAPR_DR_CONNECTOR(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \
TYPE_SPAPR_DR_CONNECTOR)
/*
* Various hotplug types managed by sPAPRDRConnector
*
* these are somewhat arbitrary, but to make things easier
* when generating DRC indexes later we've aligned the bit
* positions with the values used to assign DRC indexes on
* pSeries. we use those values as bit shifts to allow for
* the OR'ing of these values in various QEMU routines, but
* for values exposed to the guest (via DRC indexes for
* instance) we will use the shift amounts.
*/
typedef enum {
SPAPR_DR_CONNECTOR_TYPE_SHIFT_CPU = 1,
SPAPR_DR_CONNECTOR_TYPE_SHIFT_PHB = 2,
SPAPR_DR_CONNECTOR_TYPE_SHIFT_VIO = 3,
SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI = 4,
SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB = 8,
} sPAPRDRConnectorTypeShift;
typedef enum {
SPAPR_DR_CONNECTOR_TYPE_ANY = ~0,
SPAPR_DR_CONNECTOR_TYPE_CPU = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_CPU,
SPAPR_DR_CONNECTOR_TYPE_PHB = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_PHB,
SPAPR_DR_CONNECTOR_TYPE_VIO = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_VIO,
SPAPR_DR_CONNECTOR_TYPE_PCI = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI,
SPAPR_DR_CONNECTOR_TYPE_LMB = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB,
} sPAPRDRConnectorType;
/*
* set via set-indicator RTAS calls
* as documented by PAPR+ 2.7 13.5.3.4, Table 177
*
* isolated: put device under firmware control
* unisolated: claim OS control of device (may or may not be in use)
*/
typedef enum {
SPAPR_DR_ISOLATION_STATE_ISOLATED = 0,
SPAPR_DR_ISOLATION_STATE_UNISOLATED = 1
} sPAPRDRIsolationState;
/*
* set via set-indicator RTAS calls
* as documented by PAPR+ 2.7 13.5.3.4, Table 177
*
* unusable: mark device as unavailable to OS
* usable: mark device as available to OS
* exchange: (currently unused)
* recover: (currently unused)
*/
typedef enum {
SPAPR_DR_ALLOCATION_STATE_UNUSABLE = 0,
SPAPR_DR_ALLOCATION_STATE_USABLE = 1,
SPAPR_DR_ALLOCATION_STATE_EXCHANGE = 2,
SPAPR_DR_ALLOCATION_STATE_RECOVER = 3
} sPAPRDRAllocationState;
/*
* LED/visual indicator state
*
* set via set-indicator RTAS calls
* as documented by PAPR+ 2.7 13.5.3.4, Table 177,
* and PAPR+ 2.7 13.5.4.1, Table 180
*
* inactive: hotpluggable entity inactive and safely removable
* active: hotpluggable entity in use and not safely removable
* identify: (currently unused)
* action: (currently unused)
*/
typedef enum {
SPAPR_DR_INDICATOR_STATE_INACTIVE = 0,
SPAPR_DR_INDICATOR_STATE_ACTIVE = 1,
SPAPR_DR_INDICATOR_STATE_IDENTIFY = 2,
SPAPR_DR_INDICATOR_STATE_ACTION = 3,
} sPAPRDRIndicatorState;
/*
* returned via get-sensor-state RTAS calls
* as documented by PAPR+ 2.7 13.5.3.3, Table 175:
*
* empty: connector slot empty (e.g. empty hotpluggable PCI slot)
* present: connector slot populated and device available to OS
* unusable: device not currently available to OS
* exchange: (currently unused)
* recover: (currently unused)
*/
typedef enum {
SPAPR_DR_ENTITY_SENSE_EMPTY = 0,
SPAPR_DR_ENTITY_SENSE_PRESENT = 1,
SPAPR_DR_ENTITY_SENSE_UNUSABLE = 2,
SPAPR_DR_ENTITY_SENSE_EXCHANGE = 3,
SPAPR_DR_ENTITY_SENSE_RECOVER = 4,
} sPAPRDREntitySense;
typedef enum {
SPAPR_DR_CC_RESPONSE_NEXT_SIB = 1, /* currently unused */
SPAPR_DR_CC_RESPONSE_NEXT_CHILD = 2,
SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY = 3,
SPAPR_DR_CC_RESPONSE_PREV_PARENT = 4,
SPAPR_DR_CC_RESPONSE_SUCCESS = 0,
SPAPR_DR_CC_RESPONSE_ERROR = -1,
SPAPR_DR_CC_RESPONSE_CONTINUE = -2,
} sPAPRDRCCResponse;
typedef void (spapr_drc_detach_cb)(DeviceState *d, void *opaque);
typedef struct sPAPRDRConnector {
/*< private >*/
DeviceState parent;
sPAPRDRConnectorType type;
uint32_t id;
Object *owner;
const char *name;
/* sensor/indicator states */
uint32_t isolation_state;
uint32_t allocation_state;
uint32_t indicator_state;
/* configure-connector state */
void *fdt;
int fdt_start_offset;
bool configured;
bool awaiting_release;
/* device pointer, via link property */
DeviceState *dev;
spapr_drc_detach_cb *detach_cb;
void *detach_cb_opaque;
} sPAPRDRConnector;
typedef struct sPAPRDRConnectorClass {
/*< private >*/
DeviceClass parent;
/*< public >*/
/* accessors for guest-visible (generally via RTAS) DR state */
int (*set_isolation_state)(sPAPRDRConnector *drc,
sPAPRDRIsolationState state);
int (*set_indicator_state)(sPAPRDRConnector *drc,
sPAPRDRIndicatorState state);
int (*set_allocation_state)(sPAPRDRConnector *drc,
sPAPRDRAllocationState state);
uint32_t (*get_index)(sPAPRDRConnector *drc);
uint32_t (*get_type)(sPAPRDRConnector *drc);
const char *(*get_name)(sPAPRDRConnector *drc);
sPAPRDREntitySense (*entity_sense)(sPAPRDRConnector *drc);
/* QEMU interfaces for managing FDT/configure-connector */
const void *(*get_fdt)(sPAPRDRConnector *drc, int *fdt_start_offset);
void (*set_configured)(sPAPRDRConnector *drc);
/* QEMU interfaces for managing hotplug operations */
void (*attach)(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
int fdt_start_offset, bool coldplug, Error **errp);
void (*detach)(sPAPRDRConnector *drc, DeviceState *d,
spapr_drc_detach_cb *detach_cb,
void *detach_cb_opaque, Error **errp);
bool (*release_pending)(sPAPRDRConnector *drc);
} sPAPRDRConnectorClass;
sPAPRDRConnector *spapr_dr_connector_new(Object *owner,
sPAPRDRConnectorType type,
uint32_t id);
sPAPRDRConnector *spapr_dr_connector_by_index(uint32_t index);
sPAPRDRConnector *spapr_dr_connector_by_id(sPAPRDRConnectorType type,
uint32_t id);
int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
uint32_t drc_type_mask);
#endif /* __HW_SPAPR_DRC_H__ */

View file

@ -186,6 +186,12 @@ int64_t strtosz(const char *nptr, char **end);
int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix);
int64_t strtosz_suffix_unit(const char *nptr, char **end,
const char default_suffix, int64_t unit);
#define K_BYTE (1ULL << 10)
#define M_BYTE (1ULL << 20)
#define G_BYTE (1ULL << 30)
#define T_BYTE (1ULL << 40)
#define P_BYTE (1ULL << 50)
#define E_BYTE (1ULL << 60)
/* used to print char* safely */
#define STR_OR_NULL(str) ((str) ? (str) : "null")

View file

@ -1884,6 +1884,23 @@ int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len)
return 0;
}
static inline int kvmppc_enable_hcall(KVMState *s, target_ulong hcall)
{
return kvm_vm_enable_cap(s, KVM_CAP_PPC_ENABLE_HCALL, 0, hcall, 1);
}
void kvmppc_enable_logical_ci_hcalls(void)
{
/*
* FIXME: it would be nice if we could detect the cases where
* we're using a device which requires the in kernel
* implementation of these hcalls, but the kernel lacks them and
* produce a warning.
*/
kvmppc_enable_hcall(kvm_state, H_LOGICAL_CI_LOAD);
kvmppc_enable_hcall(kvm_state, H_LOGICAL_CI_STORE);
}
void kvmppc_set_papr(PowerPCCPU *cpu)
{
CPUState *cs = CPU(cpu);

View file

@ -24,6 +24,7 @@ bool kvmppc_get_host_serial(char **buf);
int kvmppc_get_hasidle(CPUPPCState *env);
int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len);
int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level);
void kvmppc_enable_logical_ci_hcalls(void);
void kvmppc_set_papr(PowerPCCPU *cpu);
int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version);
void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy);
@ -107,6 +108,10 @@ static inline int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level)
return -1;
}
static inline void kvmppc_enable_logical_ci_hcalls(void)
{
}
static inline void kvmppc_set_papr(PowerPCCPU *cpu)
{
}

View file

@ -14,6 +14,7 @@
#define TCG_TARGET_AARCH64 1
#define TCG_TARGET_INSN_UNIT_SIZE 4
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 24
#undef TCG_TARGET_STACK_GROWSUP
typedef enum {

View file

@ -27,6 +27,7 @@
#undef TCG_TARGET_STACK_GROWSUP
#define TCG_TARGET_INSN_UNIT_SIZE 4
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16
typedef enum {
TCG_REG_R0 = 0,

View file

@ -25,6 +25,7 @@
#define TCG_TARGET_I386 1
#define TCG_TARGET_INSN_UNIT_SIZE 1
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 31
#ifdef __x86_64__
# define TCG_TARGET_REG_BITS 64

View file

@ -26,6 +26,8 @@
#define TCG_TARGET_IA64 1
#define TCG_TARGET_INSN_UNIT_SIZE 16
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 21
typedef struct {
uint64_t lo __attribute__((aligned(16)));
uint64_t hi;

View file

@ -27,6 +27,7 @@
#define TCG_TARGET_MIPS 1
#define TCG_TARGET_INSN_UNIT_SIZE 4
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16
#define TCG_TARGET_NB_REGS 32
typedef enum {

View file

@ -32,6 +32,7 @@
#define TCG_TARGET_NB_REGS 32
#define TCG_TARGET_INSN_UNIT_SIZE 4
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16
typedef enum {
TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3,

View file

@ -25,6 +25,7 @@
#define TCG_TARGET_S390 1
#define TCG_TARGET_INSN_UNIT_SIZE 2
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 19
typedef enum TCGReg {
TCG_REG_R0 = 0,

View file

@ -27,6 +27,7 @@
#define TCG_TARGET_REG_BITS 64
#define TCG_TARGET_INSN_UNIT_SIZE 4
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 32
#define TCG_TARGET_NB_REGS 32
typedef enum {

View file

@ -927,7 +927,9 @@ static inline unsigned get_mmuidx(TCGMemOpIdx oi)
#define TB_EXIT_ICOUNT_EXPIRED 2
#define TB_EXIT_REQUESTED 3
#if !defined(tcg_qemu_tb_exec)
#ifdef HAVE_TCG_QEMU_TB_EXEC
uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr);
#else
# define tcg_qemu_tb_exec(env, tb_ptr) \
((uintptr_t (*)(void *, void *))tcg_ctx.code_gen_prologue)(env, tb_ptr)
#endif

View file

@ -44,6 +44,7 @@
#define TCG_TARGET_INTERPRETER 1
#define TCG_TARGET_INSN_UNIT_SIZE 1
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 32
#if UINTPTR_MAX == UINT32_MAX
# define TCG_TARGET_REG_BITS 32
@ -175,8 +176,7 @@ typedef enum {
void tci_disas(uint8_t opc);
uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr);
#define tcg_qemu_tb_exec tcg_qemu_tb_exec
#define HAVE_TCG_QEMU_TB_EXEC
static inline void flush_icache_range(uintptr_t start, uintptr_t stop)
{

View file

@ -1338,6 +1338,10 @@ spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liob
spapr_iommu_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64
spapr_iommu_indirect(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t iobaN, uint64_t tceN, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcelist=0x%"PRIx64" iobaN=0x%"PRIx64" tceN=0x%"PRIx64" ret=%"PRId64
spapr_iommu_stuff(uint64_t liobn, uint64_t ioba, uint64_t tce_value, uint64_t npages, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcevalue=0x%"PRIx64" npages=%"PRId64" ret=%"PRId64
spapr_iommu_pci_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64
spapr_iommu_pci_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64
spapr_iommu_pci_indirect(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t iobaN, uint64_t tceN, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcelist=0x%"PRIx64" iobaN=0x%"PRIx64" tceN=0x%"PRIx64" ret=%"PRId64
spapr_iommu_pci_stuff(uint64_t liobn, uint64_t ioba, uint64_t tce_value, uint64_t npages, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcevalue=0x%"PRIx64" npages=%"PRId64" ret=%"PRId64
spapr_iommu_xlate(uint64_t liobn, uint64_t ioba, uint64_t tce, unsigned perm, unsigned pgsize) "liobn=%"PRIx64" 0x%"PRIx64" -> 0x%"PRIx64" perm=%u mask=%x"
spapr_iommu_new_table(uint64_t liobn, void *tcet, void *table, int fd) "liobn=%"PRIx64" tcet=%p table=%p fd=%d"

30
vl.c
View file

@ -120,8 +120,6 @@ int main(int argc, char **argv)
#include "qom/object_interfaces.h"
#include "qapi-event.h"
#define DEFAULT_RAM_SIZE 128
#define MAX_VIRTIO_CONSOLES 1
#define MAX_SCLP_CONSOLES 1
@ -1310,7 +1308,11 @@ void hmp_usb_del(Monitor *mon, const QDict *qdict)
MachineState *current_machine;
static void machine_class_init(ObjectClass *oc, void *data)
/*
* Transitional class registration/init used for converting from
* legacy QEMUMachine to MachineClass.
*/
static void qemu_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
QEMUMachine *qm = data;
@ -1333,7 +1335,7 @@ int qemu_register_machine(QEMUMachine *m)
TypeInfo ti = {
.name = name,
.parent = TYPE_MACHINE,
.class_init = machine_class_init,
.class_init = qemu_machine_class_init,
.class_data = (void *)m,
};
@ -2647,13 +2649,13 @@ out:
return 0;
}
static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size)
static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size,
MachineClass *mc)
{
uint64_t sz;
const char *mem_str;
const char *maxmem_str, *slots_str;
const ram_addr_t default_ram_size = (ram_addr_t)DEFAULT_RAM_SIZE *
1024 * 1024;
const ram_addr_t default_ram_size = mc->default_ram_size;
QemuOpts *opts = qemu_find_opts_singleton("memory");
sz = 0;
@ -3769,7 +3771,13 @@ int main(int argc, char **argv, char **envp)
machine_class = machine_parse(optarg);
}
set_memory_options(&ram_slots, &maxram_size);
if (machine_class == NULL) {
fprintf(stderr, "No machine specified, and there is no default.\n"
"Use -machine help to list supported machines!\n");
exit(1);
}
set_memory_options(&ram_slots, &maxram_size, machine_class);
loc_set_none();
@ -3798,12 +3806,6 @@ int main(int argc, char **argv, char **envp)
}
#endif
if (machine_class == NULL) {
fprintf(stderr, "No machine specified, and there is no default.\n"
"Use -machine help to list supported machines!\n");
exit(1);
}
current_machine = MACHINE(object_new(object_class_get_name(
OBJECT_CLASS(machine_class))));
if (machine_help_func(qemu_get_machine_opts(), current_machine)) {