target-arm:

* hw/intc/arm_gicv3_its: Fix various minor bugs
  * hw/arm/aspeed: Add the i3c device to the AST2600 SoC
  * hw/arm: kudo: add lm75s behind bus 1 switch at 75
  * hw/arm/virt: Fix support for running guests on hosts
    with restricted IPA ranges
  * hw/intc/arm_gic: Allow reset of the running priority
  * hw/intc/arm_gic: Implement read of GICC_IIDR
  * hw/arm/virt: Support for virtio-mem-pci
  * hw/arm/virt: Support CPU cluster on ARM virt machine
  * docs/can: convert to restructuredText
  * hw/net: Move MV88W8618 network device out of hw/arm/ directory
  * hw/arm/virt: KVM: Enable PAuth when supported by the host
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmHpidwZHHBldGVyLm1h
 eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3svDD/4l3R0WXneEQ7mQrf9ZtW+T
 Jx/JgvYYRAQAXQyUa3Nf+1OqdVciuoHTRD1RQ3kIYcTaIF7V9+h7be4PMrjRNoS6
 7UassskKk2RFNa+UaU8d8qB4HeTm0w8u2+NEPM8qcxU71ItsjSNy7ZfRKxNLhsAt
 1jjaFEAJmi6T4YGzaF/78sg61X2hxkJwPolVBvOgFvamp77oQ0SZxp5iwWlPEy80
 NOypq4NX1qrTvB4Wnyx/5I8/4RXi6ecJZ2SkZGkPxN0b6c57ef6EjNm+ejf7zg+1
 BHvHfNIdw5c27ew0dwR9sHZ9XipE2MNjguAgpKTQzeBmybg0Jj9anoa7viPEO3yQ
 R+IFPSkAJQrAIDZjeC7XKuypFBmsgEHOmYvesp/lw6N3JZMH6aV+wInnDc8JrDvq
 T7pz+Kr2QTFkam2zdlOp5mJbdQ1RxJHADhPcNbmbLznnwApoBlO8Piaw3NRNM/jT
 WdFA/3j3m1HX9ibc1m58LzExkfGk7Gca2U0hLrfiz7/jiSQzuHmVmxqQQVYvpc+6
 AQqKuv104rqCsvjtsRwHGA3vHc/9ya0RzCHzzd4HoNtSXrGVcmqKTeif1zT8xus6
 eTRnwfXl3hmzNWM7/Vb7t+QbFlaSTf6fy/c15kkaWIm41PrbM8PMcmEXvJnkkVev
 Pvqw3POMiDl8NxK8SMkIzA==
 =hPal
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20220120-1' into staging

target-arm:
 * hw/intc/arm_gicv3_its: Fix various minor bugs
 * hw/arm/aspeed: Add the i3c device to the AST2600 SoC
 * hw/arm: kudo: add lm75s behind bus 1 switch at 75
 * hw/arm/virt: Fix support for running guests on hosts
   with restricted IPA ranges
 * hw/intc/arm_gic: Allow reset of the running priority
 * hw/intc/arm_gic: Implement read of GICC_IIDR
 * hw/arm/virt: Support for virtio-mem-pci
 * hw/arm/virt: Support CPU cluster on ARM virt machine
 * docs/can: convert to restructuredText
 * hw/net: Move MV88W8618 network device out of hw/arm/ directory
 * hw/arm/virt: KVM: Enable PAuth when supported by the host

# gpg: Signature made Thu 20 Jan 2022 16:12:12 GMT
# gpg:                using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg:                issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20220120-1: (38 commits)
  hw/intc/arm_gicv3: Check for !MEMTX_OK instead of MEMTX_ERROR
  hw/intc/arm_gicv3_its: Range-check ICID before indexing into collection table
  hw/intc/arm_gicv3_its: Check indexes before use, not after
  hw/intc/arm_gicv3_its: Factor out "find address of table entry" code
  hw/intc/arm_gicv3_its: Fix return codes in process_mapd()
  hw/intc/arm_gicv3_its: Fix return codes in process_mapc()
  hw/intc/arm_gicv3_its: Fix return codes in process_mapti()
  hw/intc/arm_gicv3_its: Refactor process_its_cmd() to reduce nesting
  hw/intc/arm_gicv3_its: Fix return codes in process_its_cmd()
  hw/intc/arm_gicv3_its: Use enum for return value of process_* functions
  hw/intc/arm_gicv3_its: Don't use data if reading command failed
  hw/intc/arm_gicv3_its: Fix handling of process_its_cmd() return value
  hw/intc/arm_gicv3_its: Convert int ID check to num_intids convention
  hw/intc/arm_gicv3_its: Fix event ID bounds checks
  hw/arm/aspeed: Add the i3c device to the AST2600 SoC
  hw/misc/aspeed_i3c.c: Introduce a dummy AST2600 I3C model.
  hw/arm: kudo add lm75s behind bus 1 switch at 75
  hw/arm/virt: Drop superfluous checks against highmem
  hw/arm/virt: Disable highmem devices that don't fit in the PA range
  hw/arm/virt: Use the PA range to compute the memory map
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
staging
Peter Maydell 2022-01-20 16:13:17 +00:00
commit 2c89b5af5e
31 changed files with 1476 additions and 782 deletions

View File

@ -774,6 +774,8 @@ M: Peter Maydell <peter.maydell@linaro.org>
L: qemu-arm@nongnu.org
S: Odd Fixes
F: hw/arm/musicpal.c
F: hw/net/mv88w8618_eth.c
F: include/hw/net/mv88w8618_eth.h
F: docs/system/arm/musicpal.rst
Nuvoton NPCM7xx

View File

@ -217,10 +217,6 @@ TCG VCPU Features
TCG VCPU features are CPU features that are specific to TCG.
Below is the list of TCG VCPU features and their descriptions.
pauth Enable or disable ``FEAT_Pauth``, pointer
authentication. By default, the feature is
enabled with ``-cpu max``.
pauth-impdef When ``FEAT_Pauth`` is enabled, either the
*impdef* (Implementation Defined) algorithm
is enabled or the *architected* QARMA algorithm

View File

@ -82,6 +82,7 @@ Emulated Devices
.. toctree::
:maxdepth: 1
devices/can.rst
devices/ivshmem.rst
devices/net.rst
devices/nvme.rst

View File

@ -1,6 +1,5 @@
QEMU CAN bus emulation support
==============================
CAN Bus Emulation Support
=========================
The CAN bus emulation provides mechanism to connect multiple
emulated CAN controller chips together by one or multiple CAN busses
(the controller device "canbus" parameter). The individual busses
@ -32,34 +31,39 @@ emulated environment for testing and RTEMS GSoC slot has been donated
to work on CAN hardware emulation on QEMU.
Examples how to use CAN emulation for SJA1000 based boards
==========================================================
----------------------------------------------------------
When QEMU with CAN PCI support is compiled then one of the next
CAN boards can be selected
(1) CAN bus Kvaser PCI CAN-S (single SJA1000 channel) boad. QEMU startup options
(1) CAN bus Kvaser PCI CAN-S (single SJA1000 channel) board. QEMU startup options::
-object can-bus,id=canbus0
-device kvaser_pci,canbus=canbus0
Add "can-host-socketcan" object to connect device to host system CAN bus
Add "can-host-socketcan" object to connect device to host system CAN bus::
-object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0
(2) CAN bus PCM-3680I PCI (dual SJA1000 channel) emulation
(2) CAN bus PCM-3680I PCI (dual SJA1000 channel) emulation::
-object can-bus,id=canbus0
-device pcm3680_pci,canbus0=canbus0,canbus1=canbus0
another example:
Another example::
-object can-bus,id=canbus0
-object can-bus,id=canbus1
-device pcm3680_pci,canbus0=canbus0,canbus1=canbus1
(3) CAN bus MIOe-3680 PCI (dual SJA1000 channel) emulation
(3) CAN bus MIOe-3680 PCI (dual SJA1000 channel) emulation::
-device mioe3680_pci,canbus0=canbus0
The ''kvaser_pci'' board/device model is compatible with and has been tested with
''kvaser_pci'' driver included in mainline Linux kernel.
the ''kvaser_pci'' driver included in mainline Linux kernel.
The tested setup was Linux 4.9 kernel on the host and guest side.
Example for qemu-system-x86_64:
Example for qemu-system-x86_64::
qemu-system-x86_64 -accel kvm -kernel /boot/vmlinuz-4.9.0-4-amd64 \
-initrd ramdisk.cpio \
@ -69,7 +73,7 @@ Example for qemu-system-x86_64:
-device kvaser_pci,canbus=canbus0 \
-nographic -append "console=ttyS0"
Example for qemu-system-arm:
Example for qemu-system-arm::
qemu-system-arm -cpu arm1176 -m 256 -M versatilepb \
-kernel kernel-qemu-arm1176-versatilepb \
@ -84,24 +88,23 @@ Example for qemu-system-arm:
The CAN interface of the host system has to be configured for proper
bitrate and set up. Configuration is not propagated from emulated
devices through bus to the physical host device. Example configuration
for 1 Mbit/s
for 1 Mbit/s::
ip link set can0 type can bitrate 1000000
ip link set can0 up
Virtual (host local only) can interface can be used on the host
side instead of physical interface
side instead of physical interface::
ip link add dev can0 type vcan
The CAN interface on the host side can be used to analyze CAN
traffic with "candump" command which is included in "can-utils".
traffic with "candump" command which is included in "can-utils"::
candump can0
CTU CAN FD support examples
===========================
---------------------------
This open-source core provides CAN FD support. CAN FD drames are
delivered even to the host systems when SocketCAN interface is found
CAN FD capable.
@ -113,7 +116,7 @@ on the board.
Example how to connect the canbus0-bus (virtual wire) to the host
Linux system (SocketCAN used) and to both CTU CAN FD cores emulated
on the corresponding PCI card expects that host system CAN bus
is setup according to the previous SJA1000 section.
is setup according to the previous SJA1000 section::
qemu-system-x86_64 -enable-kvm -kernel /boot/vmlinuz-4.19.52+ \
-initrd ramdisk.cpio \
@ -125,7 +128,7 @@ is setup according to the previous SJA1000 section.
-device ctucan_pci,canbus0=canbus0-bus,canbus1=canbus0-bus \
-nographic
Setup of CTU CAN FD controller in a guest Linux system
Setup of CTU CAN FD controller in a guest Linux system::
insmod ctucanfd.ko || modprobe ctucanfd
insmod ctucanfd_pci.ko || modprobe ctucanfd_pci
@ -150,19 +153,19 @@ Setup of CTU CAN FD controller in a guest Linux system
/bin/ip link set $ifc up
done
The test can run for example
The test can run for example::
candump can1
in the guest system and next commands in the host system for basic CAN
in the guest system and next commands in the host system for basic CAN::
cangen can0
for CAN FD without bitrate switch
for CAN FD without bitrate switch::
cangen can0 -f
and with bitrate switch
and with bitrate switch::
cangen can0 -b
@ -170,29 +173,16 @@ The test can be run viceversa, generate messages in the guest system and capture
in the host one and much more combinations.
Links to other resources
========================
------------------------
(1) CAN related projects at Czech Technical University, Faculty of Electrical Engineering
http://canbus.pages.fel.cvut.cz/
(2) Repository with development can-pci branch at Czech Technical University
https://gitlab.fel.cvut.cz/canbus/qemu-canbus
(3) RTEMS page describing project
https://devel.rtems.org/wiki/Developer/Simulators/QEMU/CANEmulation
(4) RTLWS 2015 article about the project and its use with CANopen emulation
http://cmp.felk.cvut.cz/~pisa/can/doc/rtlws-17-pisa-qemu-can.pdf
(5) GNU/Linux, CAN and CANopen in Real-time Control Applications
Slides from LinuxDays 2017 (include updated RTLWS 2015 content)
https://www.linuxdays.cz/2017/video/Pavel_Pisa-CAN_canopen.pdf
(6) Linux SocketCAN utilities
https://github.com/linux-can/can-utils/
(7) CTU CAN FD project including core VHDL design, Linux driver,
test utilities etc.
https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core
(8) CTU CAN FD Core Datasheet Documentation
http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/Progdokum.pdf
(9) CTU CAN FD Core System Architecture Documentation
http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/ctu_can_fd_architecture.pdf
(10) CTU CAN FD Driver Documentation
http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/driver_doc/ctucanfd-driver.html
(11) Integration with PCIe interfacing for Intel/Altera Cyclone IV based board
https://gitlab.fel.cvut.cz/canbus/pcie-ctu_can_fd
(1) `CAN related projects at Czech Technical University, Faculty of Electrical Engineering <http://canbus.pages.fel.cvut.cz>`_
(2) `Repository with development can-pci branch at Czech Technical University <https://gitlab.fel.cvut.cz/canbus/qemu-canbus>`_
(3) `RTEMS page describing project <https://devel.rtems.org/wiki/Developer/Simulators/QEMU/CANEmulation>`_
(4) `RTLWS 2015 article about the project and its use with CANopen emulation <http://cmp.felk.cvut.cz/~pisa/can/doc/rtlws-17-pisa-qemu-can.pdf>`_
(5) `GNU/Linux, CAN and CANopen in Real-time Control Applications Slides from LinuxDays 2017 (include updated RTLWS 2015 content) <https://www.linuxdays.cz/2017/video/Pavel_Pisa-CAN_canopen.pdf>`_
(6) `Linux SocketCAN utilities <https://github.com/linux-can/can-utils>`_
(7) `CTU CAN FD project including core VHDL design, Linux driver, test utilities etc. <https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core>`_
(8) `CTU CAN FD Core Datasheet Documentation <http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/Progdokum.pdf>`_
(9) `CTU CAN FD Core System Architecture Documentation <http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/ctu_can_fd_architecture.pdf>`_
(10) `CTU CAN FD Driver Documentation <http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/driver_doc/ctucanfd-driver.html>`_
(11) `Integration with PCIe interfacing for Intel/Altera Cyclone IV based board <https://gitlab.fel.cvut.cz/canbus/pcie-ctu_can_fd>`_

View File

@ -2001,7 +2001,11 @@ static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags,
void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
const char *oem_id, const char *oem_table_id)
{
int pptt_start = table_data->len;
MachineClass *mc = MACHINE_GET_CLASS(ms);
GQueue *list = g_queue_new();
guint pptt_start = table_data->len;
guint parent_offset;
guint length, i;
int uid = 0;
int socket;
AcpiTable table = { .sig = "PPTT", .rev = 2,
@ -2010,9 +2014,8 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
acpi_table_begin(&table, table_data);
for (socket = 0; socket < ms->smp.sockets; socket++) {
uint32_t socket_offset = table_data->len - pptt_start;
int core;
g_queue_push_tail(list,
GUINT_TO_POINTER(table_data->len - pptt_start));
build_processor_hierarchy_node(
table_data,
/*
@ -2021,35 +2024,64 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
*/
(1 << 0),
0, socket, NULL, 0);
}
for (core = 0; core < ms->smp.cores; core++) {
uint32_t core_offset = table_data->len - pptt_start;
int thread;
if (mc->smp_props.clusters_supported) {
length = g_queue_get_length(list);
for (i = 0; i < length; i++) {
int cluster;
if (ms->smp.threads > 1) {
parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list));
for (cluster = 0; cluster < ms->smp.clusters; cluster++) {
g_queue_push_tail(list,
GUINT_TO_POINTER(table_data->len - pptt_start));
build_processor_hierarchy_node(
table_data,
(0 << 0), /* not a physical package */
socket_offset, core, NULL, 0);
parent_offset, cluster, NULL, 0);
}
}
}
for (thread = 0; thread < ms->smp.threads; thread++) {
build_processor_hierarchy_node(
table_data,
(1 << 1) | /* ACPI Processor ID valid */
(1 << 2) | /* Processor is a Thread */
(1 << 3), /* Node is a Leaf */
core_offset, uid++, NULL, 0);
}
length = g_queue_get_length(list);
for (i = 0; i < length; i++) {
int core;
parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list));
for (core = 0; core < ms->smp.cores; core++) {
if (ms->smp.threads > 1) {
g_queue_push_tail(list,
GUINT_TO_POINTER(table_data->len - pptt_start));
build_processor_hierarchy_node(
table_data,
(0 << 0), /* not a physical package */
parent_offset, core, NULL, 0);
} else {
build_processor_hierarchy_node(
table_data,
(1 << 1) | /* ACPI Processor ID valid */
(1 << 3), /* Node is a Leaf */
socket_offset, uid++, NULL, 0);
parent_offset, uid++, NULL, 0);
}
}
}
length = g_queue_get_length(list);
for (i = 0; i < length; i++) {
int thread;
parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list));
for (thread = 0; thread < ms->smp.threads; thread++) {
build_processor_hierarchy_node(
table_data,
(1 << 1) | /* ACPI Processor ID valid */
(1 << 2) | /* Processor is a Thread */
(1 << 3), /* Node is a Leaf */
parent_offset, uid++, NULL, 0);
}
}
g_queue_free(list);
acpi_table_end(linker, &table);
}

View File

@ -28,6 +28,7 @@ config ARM_VIRT
select ACPI_HW_REDUCED
select ACPI_APEI
select ACPI_VIOT
select VIRTIO_MEM_SUPPORTED
config CHEETAH
bool
@ -94,6 +95,9 @@ config MUSCA
select SPLIT_IRQ
select UNIMP
config MARVELL_88W8618
bool
config MUSICPAL
bool
select OR_IRQ

View File

@ -61,6 +61,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = {
[ASPEED_DEV_UART1] = 0x1E783000,
[ASPEED_DEV_UART5] = 0x1E784000,
[ASPEED_DEV_VUART] = 0x1E787000,
[ASPEED_DEV_I3C] = 0x1E7A0000,
[ASPEED_DEV_SDRAM] = 0x80000000,
};
@ -108,6 +109,7 @@ static const int aspeed_soc_ast2600_irqmap[] = {
[ASPEED_DEV_ETH4] = 33,
[ASPEED_DEV_KCS] = 138, /* 138 -> 142 */
[ASPEED_DEV_DP] = 62,
[ASPEED_DEV_I3C] = 102, /* 102 -> 107 */
};
static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl)
@ -223,6 +225,8 @@ static void aspeed_soc_ast2600_init(Object *obj)
snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname);
object_initialize_child(obj, "hace", &s->hace, typename);
object_initialize_child(obj, "i3c", &s->i3c, TYPE_ASPEED_I3C);
}
/*
@ -523,6 +527,18 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
sysbus_mmio_map(SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0,
aspeed_soc_get_irq(s, ASPEED_DEV_HACE));
/* I3C */
if (!sysbus_realize(SYS_BUS_DEVICE(&s->i3c), errp)) {
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->i3c), 0, sc->memmap[ASPEED_DEV_I3C]);
for (i = 0; i < ASPEED_I3C_NR_DEVICES; i++) {
qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore),
sc->irqmap[ASPEED_DEV_I3C] + i);
/* The AST2600 I3C controller has one IRQ per bus. */
sysbus_connect_irq(SYS_BUS_DEVICE(&s->i3c.devices[i]), 0, irq);
}
}
static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data)

View File

@ -34,12 +34,12 @@
#include "ui/pixel_ops.h"
#include "qemu/cutils.h"
#include "qom/object.h"
#include "hw/net/mv88w8618_eth.h"
#define MP_MISC_BASE 0x80002000
#define MP_MISC_SIZE 0x00001000
#define MP_ETH_BASE 0x80008000
#define MP_ETH_SIZE 0x00001000
#define MP_WLAN_BASE 0x8000C000
#define MP_WLAN_SIZE 0x00000800
@ -84,384 +84,6 @@
/* Wolfson 8750 I2C address */
#define MP_WM_ADDR 0x1A
/* Ethernet register offsets */
#define MP_ETH_SMIR 0x010
#define MP_ETH_PCXR 0x408
#define MP_ETH_SDCMR 0x448
#define MP_ETH_ICR 0x450
#define MP_ETH_IMR 0x458
#define MP_ETH_FRDP0 0x480
#define MP_ETH_FRDP1 0x484
#define MP_ETH_FRDP2 0x488
#define MP_ETH_FRDP3 0x48C
#define MP_ETH_CRDP0 0x4A0
#define MP_ETH_CRDP1 0x4A4
#define MP_ETH_CRDP2 0x4A8
#define MP_ETH_CRDP3 0x4AC
#define MP_ETH_CTDP0 0x4E0
#define MP_ETH_CTDP1 0x4E4
/* MII PHY access */
#define MP_ETH_SMIR_DATA 0x0000FFFF
#define MP_ETH_SMIR_ADDR 0x03FF0000
#define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */
#define MP_ETH_SMIR_RDVALID (1 << 27)
/* PHY registers */
#define MP_ETH_PHY1_BMSR 0x00210000
#define MP_ETH_PHY1_PHYSID1 0x00410000
#define MP_ETH_PHY1_PHYSID2 0x00610000
#define MP_PHY_BMSR_LINK 0x0004
#define MP_PHY_BMSR_AUTONEG 0x0008
#define MP_PHY_88E3015 0x01410E20
/* TX descriptor status */
#define MP_ETH_TX_OWN (1U << 31)
/* RX descriptor status */
#define MP_ETH_RX_OWN (1U << 31)
/* Interrupt cause/mask bits */
#define MP_ETH_IRQ_RX_BIT 0
#define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT)
#define MP_ETH_IRQ_TXHI_BIT 2
#define MP_ETH_IRQ_TXLO_BIT 3
/* Port config bits */
#define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */
/* SDMA command bits */
#define MP_ETH_CMD_TXHI (1 << 23)
#define MP_ETH_CMD_TXLO (1 << 22)
typedef struct mv88w8618_tx_desc {
uint32_t cmdstat;
uint16_t res;
uint16_t bytes;
uint32_t buffer;
uint32_t next;
} mv88w8618_tx_desc;
typedef struct mv88w8618_rx_desc {
uint32_t cmdstat;
uint16_t bytes;
uint16_t buffer_size;
uint32_t buffer;
uint32_t next;
} mv88w8618_rx_desc;
#define TYPE_MV88W8618_ETH "mv88w8618_eth"
OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH)
struct mv88w8618_eth_state {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion iomem;
qemu_irq irq;
MemoryRegion *dma_mr;
AddressSpace dma_as;
uint32_t smir;
uint32_t icr;
uint32_t imr;
int mmio_index;
uint32_t vlan_header;
uint32_t tx_queue[2];
uint32_t rx_queue[4];
uint32_t frx_queue[4];
uint32_t cur_rx[4];
NICState *nic;
NICConf conf;
};
static void eth_rx_desc_put(AddressSpace *dma_as, uint32_t addr,
mv88w8618_rx_desc *desc)
{
cpu_to_le32s(&desc->cmdstat);
cpu_to_le16s(&desc->bytes);
cpu_to_le16s(&desc->buffer_size);
cpu_to_le32s(&desc->buffer);
cpu_to_le32s(&desc->next);
dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
}
static void eth_rx_desc_get(AddressSpace *dma_as, uint32_t addr,
mv88w8618_rx_desc *desc)
{
dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
le32_to_cpus(&desc->cmdstat);
le16_to_cpus(&desc->bytes);
le16_to_cpus(&desc->buffer_size);
le32_to_cpus(&desc->buffer);
le32_to_cpus(&desc->next);
}
static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
uint32_t desc_addr;
mv88w8618_rx_desc desc;
int i;
for (i = 0; i < 4; i++) {
desc_addr = s->cur_rx[i];
if (!desc_addr) {
continue;
}
do {
eth_rx_desc_get(&s->dma_as, desc_addr, &desc);
if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) {
dma_memory_write(&s->dma_as, desc.buffer + s->vlan_header,
buf, size, MEMTXATTRS_UNSPECIFIED);
desc.bytes = size + s->vlan_header;
desc.cmdstat &= ~MP_ETH_RX_OWN;
s->cur_rx[i] = desc.next;
s->icr |= MP_ETH_IRQ_RX;
if (s->icr & s->imr) {
qemu_irq_raise(s->irq);
}
eth_rx_desc_put(&s->dma_as, desc_addr, &desc);
return size;
}
desc_addr = desc.next;
} while (desc_addr != s->rx_queue[i]);
}
return size;
}
static void eth_tx_desc_put(AddressSpace *dma_as, uint32_t addr,
mv88w8618_tx_desc *desc)
{
cpu_to_le32s(&desc->cmdstat);
cpu_to_le16s(&desc->res);
cpu_to_le16s(&desc->bytes);
cpu_to_le32s(&desc->buffer);
cpu_to_le32s(&desc->next);
dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
}
static void eth_tx_desc_get(AddressSpace *dma_as, uint32_t addr,
mv88w8618_tx_desc *desc)
{
dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
le32_to_cpus(&desc->cmdstat);
le16_to_cpus(&desc->res);
le16_to_cpus(&desc->bytes);
le32_to_cpus(&desc->buffer);
le32_to_cpus(&desc->next);
}
static void eth_send(mv88w8618_eth_state *s, int queue_index)
{
uint32_t desc_addr = s->tx_queue[queue_index];
mv88w8618_tx_desc desc;
uint32_t next_desc;
uint8_t buf[2048];
int len;
do {
eth_tx_desc_get(&s->dma_as, desc_addr, &desc);
next_desc = desc.next;
if (desc.cmdstat & MP_ETH_TX_OWN) {
len = desc.bytes;
if (len < 2048) {
dma_memory_read(&s->dma_as, desc.buffer, buf, len,
MEMTXATTRS_UNSPECIFIED);
qemu_send_packet(qemu_get_queue(s->nic), buf, len);
}
desc.cmdstat &= ~MP_ETH_TX_OWN;
s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
eth_tx_desc_put(&s->dma_as, desc_addr, &desc);
}
desc_addr = next_desc;
} while (desc_addr != s->tx_queue[queue_index]);
}
static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
unsigned size)
{
mv88w8618_eth_state *s = opaque;
switch (offset) {
case MP_ETH_SMIR:
if (s->smir & MP_ETH_SMIR_OPCODE) {
switch (s->smir & MP_ETH_SMIR_ADDR) {
case MP_ETH_PHY1_BMSR:
return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG |
MP_ETH_SMIR_RDVALID;
case MP_ETH_PHY1_PHYSID1:
return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID;
case MP_ETH_PHY1_PHYSID2:
return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID;
default:
return MP_ETH_SMIR_RDVALID;
}
}
return 0;
case MP_ETH_ICR:
return s->icr;
case MP_ETH_IMR:
return s->imr;
case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
return s->frx_queue[(offset - MP_ETH_FRDP0)/4];
case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
return s->rx_queue[(offset - MP_ETH_CRDP0)/4];
case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
return s->tx_queue[(offset - MP_ETH_CTDP0)/4];
default:
return 0;
}
}
static void mv88w8618_eth_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
mv88w8618_eth_state *s = opaque;
switch (offset) {
case MP_ETH_SMIR:
s->smir = value;
break;
case MP_ETH_PCXR:
s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
break;
case MP_ETH_SDCMR:
if (value & MP_ETH_CMD_TXHI) {
eth_send(s, 1);
}
if (value & MP_ETH_CMD_TXLO) {
eth_send(s, 0);
}
if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
qemu_irq_raise(s->irq);
}
break;
case MP_ETH_ICR:
s->icr &= value;
break;
case MP_ETH_IMR:
s->imr = value;
if (s->icr & s->imr) {
qemu_irq_raise(s->irq);
}
break;
case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value;
break;
case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
s->rx_queue[(offset - MP_ETH_CRDP0)/4] =
s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value;
break;
case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value;
break;
}
}
static const MemoryRegionOps mv88w8618_eth_ops = {
.read = mv88w8618_eth_read,
.write = mv88w8618_eth_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void eth_cleanup(NetClientState *nc)
{
mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
static NetClientInfo net_mv88w8618_info = {
.type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = eth_receive,
.cleanup = eth_cleanup,
};
static void mv88w8618_eth_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
mv88w8618_eth_state *s = MV88W8618_ETH(dev);
sysbus_init_irq(sbd, &s->irq);
memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
"mv88w8618-eth", MP_ETH_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
}
static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
{
mv88w8618_eth_state *s = MV88W8618_ETH(dev);
if (!s->dma_mr) {
error_setg(errp, TYPE_MV88W8618_ETH " 'dma-memory' link not set");
return;
}
address_space_init(&s->dma_as, s->dma_mr, "emac-dma");
s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
object_get_typename(OBJECT(dev)), dev->id, s);
}
static const VMStateDescription mv88w8618_eth_vmsd = {
.name = "mv88w8618_eth",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(smir, mv88w8618_eth_state),
VMSTATE_UINT32(icr, mv88w8618_eth_state),
VMSTATE_UINT32(imr, mv88w8618_eth_state),
VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
VMSTATE_END_OF_LIST()
}
};
static Property mv88w8618_eth_properties[] = {
DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr,
TYPE_MEMORY_REGION, MemoryRegion *),
DEFINE_PROP_END_OF_LIST(),
};
static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &mv88w8618_eth_vmsd;
device_class_set_props(dc, mv88w8618_eth_properties);
dc->realize = mv88w8618_eth_realize;
}
static const TypeInfo mv88w8618_eth_info = {
.name = TYPE_MV88W8618_ETH,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(mv88w8618_eth_state),
.instance_init = mv88w8618_eth_init,
.class_init = mv88w8618_eth_class_init,
};
/* LCD register offsets */
#define MP_LCD_IRQCTRL 0x180
#define MP_LCD_IRQSTAT 0x184
@ -1746,7 +1368,6 @@ static void musicpal_register_types(void)
type_register_static(&mv88w8618_pic_info);
type_register_static(&mv88w8618_pit_info);
type_register_static(&mv88w8618_flashcfg_info);
type_register_static(&mv88w8618_eth_info);
type_register_static(&mv88w8618_wlan_info);
type_register_static(&musicpal_lcd_info);
type_register_static(&musicpal_gpio_info);

View File

@ -332,7 +332,15 @@ static void kudo_bmc_i2c_init(NPCM7xxState *soc)
{
I2CSlave *i2c_mux;
i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 1), TYPE_PCA9548, 0x75);
i2c_mux = i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 1),
TYPE_PCA9548, 0x75);
/* tmp105 is compatible with the lm75 */
i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 4), "tmp105", 0x5c);
i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 5), "tmp105", 0x5c);
i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 6), "tmp105", 0x5c);
i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 7), "tmp105", 0x5c);
i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 1), TYPE_PCA9548, 0x77);
i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 4), TYPE_PCA9548, 0x77);

View File

@ -158,10 +158,9 @@ static void acpi_dsdt_add_virtio(Aml *scope,
}
static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
uint32_t irq, bool use_highmem, bool highmem_ecam,
VirtMachineState *vms)
uint32_t irq, VirtMachineState *vms)
{
int ecam_id = VIRT_ECAM_ID(highmem_ecam);
int ecam_id = VIRT_ECAM_ID(vms->highmem_ecam);
struct GPEXConfig cfg = {
.mmio32 = memmap[VIRT_PCIE_MMIO],
.pio = memmap[VIRT_PCIE_PIO],
@ -170,7 +169,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
.bus = vms->bus,
};
if (use_highmem) {
if (vms->highmem_mmio) {
cfg.mmio64 = memmap[VIRT_HIGH_PCIE_MMIO];
}
@ -869,8 +868,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]);
acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
vms->highmem, vms->highmem_ecam, vms);
acpi_dsdt_add_pci(scope, memmap, irqmap[VIRT_PCIE] + ARM_SPI_BASE, vms);
if (vms->acpi_dev) {
build_ged_aml(scope, "\\_SB."GED_DEVICE,
HOTPLUG_HANDLER(vms->acpi_dev),

View File

@ -71,9 +71,11 @@
#include "hw/arm/smmuv3.h"
#include "hw/acpi/acpi.h"
#include "target/arm/internals.h"
#include "hw/mem/memory-device.h"
#include "hw/mem/pc-dimm.h"
#include "hw/mem/nvdimm.h"
#include "hw/acpi/generic_event_device.h"
#include "hw/virtio/virtio-mem-pci.h"
#include "hw/virtio/virtio-iommu.h"
#include "hw/char/pl011.h"
#include "qemu/guest-random.h"
@ -434,9 +436,8 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms)
* can contain several layers of clustering within a single physical
* package and cluster nodes can be contained in parent cluster nodes.
*
* Given that cluster is not yet supported in the vCPU topology,
* we currently generate one cluster node within each socket node
* by default.
* Note: currently we only support one layer of clustering within
* each physical package.
*/
qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map");
@ -446,14 +447,16 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms)
if (ms->smp.threads > 1) {
map_path = g_strdup_printf(
"/cpus/cpu-map/socket%d/cluster0/core%d/thread%d",
cpu / (ms->smp.cores * ms->smp.threads),
"/cpus/cpu-map/socket%d/cluster%d/core%d/thread%d",
cpu / (ms->smp.clusters * ms->smp.cores * ms->smp.threads),
(cpu / (ms->smp.cores * ms->smp.threads)) % ms->smp.clusters,
(cpu / ms->smp.threads) % ms->smp.cores,
cpu % ms->smp.threads);
} else {
map_path = g_strdup_printf(
"/cpus/cpu-map/socket%d/cluster0/core%d",
cpu / ms->smp.cores,
"/cpus/cpu-map/socket%d/cluster%d/core%d",
cpu / (ms->smp.clusters * ms->smp.cores),
(cpu / ms->smp.cores) % ms->smp.clusters,
cpu % ms->smp.cores);
}
qemu_fdt_add_path(ms->fdt, map_path);
@ -1416,7 +1419,7 @@ static void create_pcie(VirtMachineState *vms)
mmio_reg, base_mmio, size_mmio);
memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias);
if (vms->highmem) {
if (vms->highmem_mmio) {
/* Map high MMIO space */
MemoryRegion *high_mmio_alias = g_new0(MemoryRegion, 1);
@ -1470,7 +1473,7 @@ static void create_pcie(VirtMachineState *vms)
qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
2, base_ecam, 2, size_ecam);
if (vms->highmem) {
if (vms->highmem_mmio) {
qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges",
1, FDT_PCI_RANGE_IOPORT, 2, 0,
2, base_pio, 2, size_pio,
@ -1664,10 +1667,10 @@ static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx)
return arm_cpu_mp_affinity(idx, clustersz);
}
static void virt_set_memmap(VirtMachineState *vms)
static void virt_set_memmap(VirtMachineState *vms, int pa_bits)
{
MachineState *ms = MACHINE(vms);
hwaddr base, device_memory_base, device_memory_size;
hwaddr base, device_memory_base, device_memory_size, memtop;
int i;
vms->memmap = extended_memmap;
@ -1682,6 +1685,14 @@ static void virt_set_memmap(VirtMachineState *vms)
exit(EXIT_FAILURE);
}
/*
* !highmem is exactly the same as limiting the PA space to 32bit,
* irrespective of the underlying capabilities of the HW.
*/
if (!vms->highmem) {
pa_bits = 32;
}
/*
* We compute the base of the high IO region depending on the
* amount of initial and device memory. The device memory start/size
@ -1694,7 +1705,12 @@ static void virt_set_memmap(VirtMachineState *vms)
device_memory_size = ms->maxram_size - ms->ram_size + ms->ram_slots * GiB;
/* Base address of the high IO region */
base = device_memory_base + ROUND_UP(device_memory_size, GiB);
memtop = base = device_memory_base + ROUND_UP(device_memory_size, GiB);
if (memtop > BIT_ULL(pa_bits)) {
error_report("Addressing limited to %d bits, but memory exceeds it by %llu bytes\n",
pa_bits, memtop - BIT_ULL(pa_bits));
exit(EXIT_FAILURE);
}
if (base < device_memory_base) {
error_report("maxmem/slots too huge");
exit(EXIT_FAILURE);
@ -1703,15 +1719,43 @@ static void virt_set_memmap(VirtMachineState *vms)
base = vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES;
}
/* We know for sure that at least the memory fits in the PA space */
vms->highest_gpa = memtop - 1;
for (i = VIRT_LOWMEMMAP_LAST; i < ARRAY_SIZE(extended_memmap); i++) {
hwaddr size = extended_memmap[i].size;
bool fits;
base = ROUND_UP(base, size);
vms->memmap[i].base = base;
vms->memmap[i].size = size;
/*
* Check each device to see if they fit in the PA space,
* moving highest_gpa as we go.
*
* For each device that doesn't fit, disable it.
*/
fits = (base + size) <= BIT_ULL(pa_bits);
if (fits) {
vms->highest_gpa = base + size - 1;
}
switch (i) {
case VIRT_HIGH_GIC_REDIST2:
vms->highmem_redists &= fits;
break;
case VIRT_HIGH_PCIE_ECAM:
vms->highmem_ecam &= fits;
break;
case VIRT_HIGH_PCIE_MMIO:
vms->highmem_mmio &= fits;
break;
}
base += size;
}
vms->highest_gpa = base - 1;
if (device_memory_size > 0) {
ms->device_memory = g_malloc0(sizeof(*ms->device_memory));
ms->device_memory->base = device_memory_base;
@ -1902,12 +1946,43 @@ static void machvirt_init(MachineState *machine)
unsigned int smp_cpus = machine->smp.cpus;
unsigned int max_cpus = machine->smp.max_cpus;
if (!cpu_type_valid(machine->cpu_type)) {
error_report("mach-virt: CPU type %s not supported", machine->cpu_type);
exit(1);
}
possible_cpus = mc->possible_cpu_arch_ids(machine);
/*
* In accelerated mode, the memory map is computed earlier in kvm_type()
* to create a VM with the right number of IPA bits.
*/
if (!vms->memmap) {
virt_set_memmap(vms);
Object *cpuobj;
ARMCPU *armcpu;
int pa_bits;
/*
* Instanciate a temporary CPU object to find out about what
* we are about to deal with. Once this is done, get rid of
* the object.
*/
cpuobj = object_new(possible_cpus->cpus[0].type);
armcpu = ARM_CPU(cpuobj);
if (object_property_get_bool(cpuobj, "aarch64", NULL)) {
pa_bits = arm_pamax(armcpu);
} else if (arm_feature(&armcpu->env, ARM_FEATURE_LPAE)) {
/* v7 with LPAE */
pa_bits = 40;
} else {
/* Anything else */
pa_bits = 32;
}
object_unref(cpuobj);
virt_set_memmap(vms, pa_bits);
}
/* We can probe only here because during property set
@ -1915,11 +1990,6 @@ static void machvirt_init(MachineState *machine)
*/
finalize_gic_version(vms);
if (!cpu_type_valid(machine->cpu_type)) {
error_report("mach-virt: CPU type %s not supported", machine->cpu_type);
exit(1);
}
if (vms->secure) {
/*
* The Secure view of the world is the same as the NonSecure,
@ -1989,7 +2059,6 @@ static void machvirt_init(MachineState *machine)
create_fdt(vms);
possible_cpus = mc->possible_cpu_arch_ids(machine);
assert(possible_cpus->len == max_cpus);
for (n = 0; n < possible_cpus->len; n++) {
Object *cpuobj;
@ -2127,7 +2196,7 @@ static void machvirt_init(MachineState *machine)
machine->ram_size, "mach-virt.tag");
}
vms->highmem_ecam &= vms->highmem && (!firmware_loaded || aarch64);
vms->highmem_ecam &= (!firmware_loaded || aarch64);
create_rtc(vms);
@ -2500,6 +2569,64 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev,
dev, &error_abort);
}
static void virt_virtio_md_pci_pre_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
HotplugHandler *hotplug_dev2 = qdev_get_bus_hotplug_handler(dev);
Error *local_err = NULL;
if (!hotplug_dev2 && dev->hotplugged) {
/*
* Without a bus hotplug handler, we cannot control the plug/unplug
* order. We should never reach this point when hotplugging on ARM.
* However, it's nice to add a safety net, similar to what we have
* on x86.
*/
error_setg(errp, "hotplug of virtio based memory devices not supported"
" on this bus.");
return;
}
/*
* First, see if we can plug this memory device at all. If that
* succeeds, branch of to the actual hotplug handler.
*/
memory_device_pre_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev), NULL,
&local_err);
if (!local_err && hotplug_dev2) {
hotplug_handler_pre_plug(hotplug_dev2, dev, &local_err);
}
error_propagate(errp, local_err);
}
static void virt_virtio_md_pci_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
HotplugHandler *hotplug_dev2 = qdev_get_bus_hotplug_handler(dev);
Error *local_err = NULL;
/*
* Plug the memory device first and then branch off to the actual
* hotplug handler. If that one fails, we can easily undo the memory
* device bits.
*/
memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev));
if (hotplug_dev2) {
hotplug_handler_plug(hotplug_dev2, dev, &local_err);
if (local_err) {
memory_device_unplug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev));
}
}
error_propagate(errp, local_err);
}
static void virt_virtio_md_pci_unplug_request(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
/* We don't support hot unplug of virtio based memory devices */
error_setg(errp, "virtio based memory devices cannot be unplugged.");
}
static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@ -2507,6 +2634,8 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
virt_memory_pre_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {
virt_virtio_md_pci_pre_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
hwaddr db_start = 0, db_end = 0;
char *resv_prop_str;
@ -2558,6 +2687,11 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
virt_memory_plug(hotplug_dev, dev, errp);
}
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {
virt_virtio_md_pci_plug(hotplug_dev, dev, errp);
}
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
PCIDevice *pdev = PCI_DEVICE(dev);
@ -2614,6 +2748,8 @@ static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
virt_dimm_unplug_request(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {
virt_virtio_md_pci_unplug_request(hotplug_dev, dev, errp);
} else {
error_setg(errp, "device unplug request for unsupported device"
" type: %s", object_get_typename(OBJECT(dev)));
@ -2638,6 +2774,7 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
if (device_is_dynamic_sysbus(mc, dev) ||
object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
return HOTPLUG_HANDLER(machine);
}
@ -2657,7 +2794,7 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms, &fixed_ipa);
/* we freeze the memory map to compute the highest gpa */
virt_set_memmap(vms);
virt_set_memmap(vms, max_vm_pa_size);
requested_pa_size = 64 - clz64(vms->highest_gpa);
@ -2718,6 +2855,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
hc->unplug_request = virt_machine_device_unplug_request_cb;
hc->unplug = virt_machine_device_unplug_cb;
mc->nvdimm_supported = true;
mc->smp_props.clusters_supported = true;
mc->auto_enable_numa_with_memhp = true;
mc->auto_enable_numa_with_memdev = true;
mc->default_ram_id = "mach-virt.ram";
@ -2827,6 +2965,8 @@ static void virt_instance_init(Object *obj)
vms->gic_version = VIRT_GIC_VERSION_NOSEL;
vms->highmem_ecam = !vmc->no_highmem_ecam;
vms->highmem_mmio = true;
vms->highmem_redists = true;
if (vmc->no_its) {
vms->its = false;

View File

@ -47,6 +47,3 @@ config PL041
config CS4231
bool
config MARVELL_88W8618
bool

View File

@ -1662,6 +1662,15 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
}
break;
}
case 0xfc:
if (s->revision == REV_11MPCORE) {
/* Reserved on 11MPCore */
*data = 0;
} else {
/* GICv1 or v2; Arm implementation */
*data = (s->revision << 16) | 0x43b;
}
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_read: Bad offset %x\n", (int)offset);
@ -1727,6 +1736,7 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset,
} else {
s->apr[regno][cpu] = value;
}
s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu);
break;
}
case 0xe0: case 0xe4: case 0xe8: case 0xec:
@ -1743,6 +1753,7 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset,
return MEMTX_OK;
}
s->nsapr[regno][cpu] = value;
s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu);
break;
}
case 0x1000:

View File

@ -45,6 +45,23 @@ typedef struct {
uint64_t itel;
} IteEntry;
/*
* The ITS spec permits a range of CONSTRAINED UNPREDICTABLE options
* if a command parameter is not correct. These include both "stall
* processing of the command queue" and "ignore this command, and
* keep processing the queue". In our implementation we choose that
* memory transaction errors reading the command packet provoke a
* stall, but errors in parameters cause us to ignore the command
* and continue processing.
* The process_* functions which handle individual ITS commands all
* return an ItsCmdResult which tells process_cmdq() whether it should
* stall or keep going.
*/
typedef enum ItsCmdResult {
CMD_STALL = 0,
CMD_CONTINUE = 1,
} ItsCmdResult;
static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
{
uint64_t result = 0;
@ -66,44 +83,62 @@ static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
return result;
}
static uint64_t table_entry_addr(GICv3ITSState *s, TableDesc *td,
uint32_t idx, MemTxResult *res)
{
/*
* Given a TableDesc describing one of the ITS in-guest-memory
* tables and an index into it, return the guest address
* corresponding to that table entry.
* If there was a memory error reading the L1 table of an
* indirect table, *res is set accordingly, and we return -1.
* If the L1 table entry is marked not valid, we return -1 with
* *res set to MEMTX_OK.
*
* The specification defines the format of level 1 entries of a
* 2-level table, but the format of level 2 entries and the format
* of flat-mapped tables is IMPDEF.
*/
AddressSpace *as = &s->gicv3->dma_as;
uint32_t l2idx;
uint64_t l2;
uint32_t num_l2_entries;
*res = MEMTX_OK;
if (!td->indirect) {
/* Single level table */
return td->base_addr + idx * td->entry_sz;
}
/* Two level table */
l2idx = idx / (td->page_sz / L1TABLE_ENTRY_SIZE);
l2 = address_space_ldq_le(as,
td->base_addr + (l2idx * L1TABLE_ENTRY_SIZE),
MEMTXATTRS_UNSPECIFIED, res);
if (*res != MEMTX_OK) {
return -1;
}
if (!(l2 & L2_TABLE_VALID_MASK)) {
return -1;
}
num_l2_entries = td->page_sz / td->entry_sz;
return (l2 & ((1ULL << 51) - 1)) + (idx % num_l2_entries) * td->entry_sz;
}
static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t *cte,
MemTxResult *res)
{
AddressSpace *as = &s->gicv3->dma_as;
uint64_t l2t_addr;
uint64_t value;
bool valid_l2t;
uint32_t l2t_id;
uint32_t num_l2_entries;
uint64_t entry_addr = table_entry_addr(s, &s->ct, icid, res);
if (s->ct.indirect) {
l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
value = address_space_ldq_le(as,
s->ct.base_addr +
(l2t_id * L1TABLE_ENTRY_SIZE),
MEMTXATTRS_UNSPECIFIED, res);
if (*res == MEMTX_OK) {
valid_l2t = (value & L2_TABLE_VALID_MASK) != 0;
if (valid_l2t) {
num_l2_entries = s->ct.page_sz / s->ct.entry_sz;
l2t_addr = value & ((1ULL << 51) - 1);
*cte = address_space_ldq_le(as, l2t_addr +
((icid % num_l2_entries) * GITS_CTE_SIZE),
MEMTXATTRS_UNSPECIFIED, res);
}
}
} else {
/* Flat level table */
*cte = address_space_ldq_le(as, s->ct.base_addr +
(icid * GITS_CTE_SIZE),
MEMTXATTRS_UNSPECIFIED, res);
if (entry_addr == -1) {
return false; /* not valid */
}
*cte = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, res);
return FIELD_EX64(*cte, CTE, VALID);
}
@ -172,41 +207,12 @@ static bool get_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte,
static uint64_t get_dte(GICv3ITSState *s, uint32_t devid, MemTxResult *res)
{
AddressSpace *as = &s->gicv3->dma_as;
uint64_t l2t_addr;
uint64_t value;
bool valid_l2t;
uint32_t l2t_id;
uint32_t num_l2_entries;
uint64_t entry_addr = table_entry_addr(s, &s->dt, devid, res);
if (s->dt.indirect) {
l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
value = address_space_ldq_le(as,
s->dt.base_addr +
(l2t_id * L1TABLE_ENTRY_SIZE),
MEMTXATTRS_UNSPECIFIED, res);
if (*res == MEMTX_OK) {
valid_l2t = (value & L2_TABLE_VALID_MASK) != 0;
if (valid_l2t) {
num_l2_entries = s->dt.page_sz / s->dt.entry_sz;
l2t_addr = value & ((1ULL << 51) - 1);
value = address_space_ldq_le(as, l2t_addr +
((devid % num_l2_entries) * GITS_DTE_SIZE),
MEMTXATTRS_UNSPECIFIED, res);
}
}
} else {
/* Flat level table */
value = address_space_ldq_le(as, s->dt.base_addr +
(devid * GITS_DTE_SIZE),
MEMTXATTRS_UNSPECIFIED, res);
if (entry_addr == -1) {
return 0; /* a DTE entry with the Valid bit clear */
}
return value;
return address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, res);
}
/*
@ -217,21 +223,20 @@ static uint64_t get_dte(GICv3ITSState *s, uint32_t devid, MemTxResult *res)
* 3. handling of ITS CLEAR command
* 4. handling of ITS DISCARD command
*/
static bool process_its_cmd(GICv3ITSState *s, uint64_t value, uint32_t offset,
ItsCmdType cmd)
static ItsCmdResult process_its_cmd(GICv3ITSState *s, uint64_t value,
uint32_t offset, ItsCmdType cmd)
{
AddressSpace *as = &s->gicv3->dma_as;
uint32_t devid, eventid;
MemTxResult res = MEMTX_OK;
bool dte_valid;
uint64_t dte = 0;
uint32_t max_eventid;
uint64_t num_eventids;
uint16_t icid = 0;
uint32_t pIntid = 0;
bool ite_valid = false;
uint64_t cte = 0;
bool cte_valid = false;
bool result = false;
uint64_t rdbase;
if (cmd == NONE) {
@ -245,103 +250,111 @@ static bool process_its_cmd(GICv3ITSState *s, uint64_t value, uint32_t offset,
}
if (res != MEMTX_OK) {
return result;
return CMD_STALL;
}
eventid = (value & EVENTID_MASK);
dte = get_dte(s, devid, &res);
if (res != MEMTX_OK) {
return result;
}
dte_valid = FIELD_EX64(dte, DTE, VALID);
if (dte_valid) {
max_eventid = 1UL << (FIELD_EX64(dte, DTE, SIZE) + 1);
ite_valid = get_ite(s, eventid, dte, &icid, &pIntid, &res);
if (res != MEMTX_OK) {
return result;
}
if (ite_valid) {
cte_valid = get_cte(s, icid, &cte, &res);
}
if (res != MEMTX_OK) {
return result;
}
} else {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: "
"invalid dte: %"PRIx64" for %d (MEM_TX: %d)\n",
__func__, dte, devid, res);
return result;
}
/*
* In this implementation, in case of guest errors we ignore the
* command and move onto the next command in the queue.
*/
if (devid >= s->dt.num_ids) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: devid %d>=%d",
__func__, devid, s->dt.num_ids);
} else if (!dte_valid || !ite_valid || !cte_valid) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: "
"dte: %s, ite: %s, cte: %s\n",
__func__,
dte_valid ? "valid" : "invalid",
ite_valid ? "valid" : "invalid",
cte_valid ? "valid" : "invalid");
} else if (eventid > max_eventid) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: eventid %d > %d\n",
__func__, eventid, max_eventid);
} else {
/*
* Current implementation only supports rdbase == procnum
* Hence rdbase physical address is ignored
*/
rdbase = FIELD_EX64(cte, CTE, RDBASE);
if (rdbase >= s->gicv3->num_cpu) {
return result;
}
if ((cmd == CLEAR) || (cmd == DISCARD)) {
gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 0);
} else {
gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 1);
}
if (cmd == DISCARD) {
IteEntry ite = {};
/* remove mapping from interrupt translation table */
result = update_ite(s, eventid, dte, ite);
}
return CMD_CONTINUE;
}
return result;
dte = get_dte(s, devid, &res);
if (res != MEMTX_OK) {
return CMD_STALL;
}
dte_valid = FIELD_EX64(dte, DTE, VALID);
if (!dte_valid) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: "
"invalid dte: %"PRIx64" for %d\n",
__func__, dte, devid);
return CMD_CONTINUE;
}
num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1);
if (eventid >= num_eventids) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: eventid %d >= %"
PRId64 "\n",
__func__, eventid, num_eventids);
return CMD_CONTINUE;
}
ite_valid = get_ite(s, eventid, dte, &icid, &pIntid, &res);
if (res != MEMTX_OK) {
return CMD_STALL;
}
if (!ite_valid) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: invalid ITE\n",
__func__);
return CMD_CONTINUE;
}
if (icid >= s->ct.num_ids) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid ICID 0x%x in ITE (table corrupted?)\n",
__func__, icid);
return CMD_CONTINUE;
}
cte_valid = get_cte(s, icid, &cte, &res);
if (res != MEMTX_OK) {
return CMD_STALL;
}
if (!cte_valid) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: "
"invalid cte: %"PRIx64"\n",
__func__, cte);
return CMD_CONTINUE;
}
/*
* Current implementation only supports rdbase == procnum
* Hence rdbase physical address is ignored
*/
rdbase = FIELD_EX64(cte, CTE, RDBASE);
if (rdbase >= s->gicv3->num_cpu) {
return CMD_CONTINUE;
}
if ((cmd == CLEAR) || (cmd == DISCARD)) {
gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 0);
} else {
gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 1);
}
if (cmd == DISCARD) {
IteEntry ite = {};
/* remove mapping from interrupt translation table */
return update_ite(s, eventid, dte, ite) ? CMD_CONTINUE : CMD_STALL;
}
return CMD_CONTINUE;
}
static bool process_mapti(GICv3ITSState *s, uint64_t value, uint32_t offset,
bool ignore_pInt)
static ItsCmdResult process_mapti(GICv3ITSState *s, uint64_t value,
uint32_t offset, bool ignore_pInt)
{
AddressSpace *as = &s->gicv3->dma_as;
uint32_t devid, eventid;
uint32_t pIntid = 0;
uint32_t max_eventid, max_Intid;
uint64_t num_eventids;
uint32_t num_intids;
bool dte_valid;
MemTxResult res = MEMTX_OK;
uint16_t icid = 0;
uint64_t dte = 0;
bool result = false;
IteEntry ite = {};
devid = ((value & DEVID_MASK) >> DEVID_SHIFT);
offset += NUM_BYTES_IN_DW;
@ -349,7 +362,7 @@ static bool process_mapti(GICv3ITSState *s, uint64_t value, uint32_t offset,
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
return result;
return CMD_STALL;
}
eventid = (value & EVENTID_MASK);
@ -365,58 +378,59 @@ static bool process_mapti(GICv3ITSState *s, uint64_t value, uint32_t offset,
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
return result;
return CMD_STALL;
}
icid = value & ICID_MASK;
if (devid >= s->dt.num_ids) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: devid %d>=%d",
__func__, devid, s->dt.num_ids);
return CMD_CONTINUE;
}
dte = get_dte(s, devid, &res);
if (res != MEMTX_OK) {
return result;
return CMD_STALL;
}
dte_valid = FIELD_EX64(dte, DTE, VALID);
max_eventid = 1UL << (FIELD_EX64(dte, DTE, SIZE) + 1);
max_Intid = (1ULL << (GICD_TYPER_IDBITS + 1)) - 1;
num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1);
num_intids = 1ULL << (GICD_TYPER_IDBITS + 1);
if ((devid >= s->dt.num_ids) || (icid >= s->ct.num_ids)
|| !dte_valid || (eventid > max_eventid) ||
(((pIntid < GICV3_LPI_INTID_START) || (pIntid > max_Intid)) &&
if ((icid >= s->ct.num_ids)
|| !dte_valid || (eventid >= num_eventids) ||
(((pIntid < GICV3_LPI_INTID_START) || (pIntid >= num_intids)) &&
(pIntid != INTID_SPURIOUS))) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes "
"devid %d or icid %d or eventid %d or pIntid %d or"
"unmapped dte %d\n", __func__, devid, icid, eventid,
"icid %d or eventid %d or pIntid %d or"
"unmapped dte %d\n", __func__, icid, eventid,
pIntid, dte_valid);
/*
* in this implementation, in case of error
* we ignore this command and move onto the next
* command in the queue
*/
} else {
/* add ite entry to interrupt translation table */
IteEntry ite = {};
ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, dte_valid);
ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL);
ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, pIntid);
ite.itel = FIELD_DP64(ite.itel, ITE_L, DOORBELL, INTID_SPURIOUS);
ite.iteh = FIELD_DP32(ite.iteh, ITE_H, ICID, icid);
result = update_ite(s, eventid, dte, ite);
return CMD_CONTINUE;
}
return result;
/* add ite entry to interrupt translation table */
ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, dte_valid);
ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL);
ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, pIntid);
ite.itel = FIELD_DP64(ite.itel, ITE_L, DOORBELL, INTID_SPURIOUS);
ite.iteh = FIELD_DP32(ite.iteh, ITE_H, ICID, icid);
return update_ite(s, eventid, dte, ite) ? CMD_CONTINUE : CMD_STALL;
}
static bool update_cte(GICv3ITSState *s, uint16_t icid, bool valid,
uint64_t rdbase)
{
AddressSpace *as = &s->gicv3->dma_as;
uint64_t value;
uint64_t l2t_addr;
bool valid_l2t;
uint32_t l2t_id;
uint32_t num_l2_entries;
uint64_t entry_addr;
uint64_t cte = 0;
MemTxResult res = MEMTX_OK;
@ -430,54 +444,27 @@ static bool update_cte(GICv3ITSState *s, uint16_t icid, bool valid,
cte = FIELD_DP64(cte, CTE, RDBASE, rdbase);
}
/*
* The specification defines the format of level 1 entries of a
* 2-level table, but the format of level 2 entries and the format
* of flat-mapped tables is IMPDEF.
*/
if (s->ct.indirect) {
l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
value = address_space_ldq_le(as,
s->ct.base_addr +
(l2t_id * L1TABLE_ENTRY_SIZE),
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
return false;
}
valid_l2t = (value & L2_TABLE_VALID_MASK) != 0;
if (valid_l2t) {
num_l2_entries = s->ct.page_sz / s->ct.entry_sz;
l2t_addr = value & ((1ULL << 51) - 1);
address_space_stq_le(as, l2t_addr +
((icid % num_l2_entries) * GITS_CTE_SIZE),
cte, MEMTXATTRS_UNSPECIFIED, &res);
}
} else {
/* Flat level table */
address_space_stq_le(as, s->ct.base_addr + (icid * GITS_CTE_SIZE),
cte, MEMTXATTRS_UNSPECIFIED, &res);
}
entry_addr = table_entry_addr(s, &s->ct, icid, &res);
if (res != MEMTX_OK) {
/* memory access error: stall */
return false;
} else {
}
if (entry_addr == -1) {
/* No L2 table for this index: discard write and continue */
return true;
}
address_space_stq_le(as, entry_addr, cte, MEMTXATTRS_UNSPECIFIED, &res);
return res == MEMTX_OK;
}
static bool process_mapc(GICv3ITSState *s, uint32_t offset)
static ItsCmdResult process_mapc(GICv3ITSState *s, uint32_t offset)
{
AddressSpace *as = &s->gicv3->dma_as;
uint16_t icid;
uint64_t rdbase;
bool valid;
MemTxResult res = MEMTX_OK;
bool result = false;
uint64_t value;
offset += NUM_BYTES_IN_DW;
@ -487,7 +474,7 @@ static bool process_mapc(GICv3ITSState *s, uint32_t offset)
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
return result;
return CMD_STALL;
}
icid = value & ICID_MASK;
@ -506,22 +493,17 @@ static bool process_mapc(GICv3ITSState *s, uint32_t offset)
* we ignore this command and move onto the next
* command in the queue
*/
} else {
result = update_cte(s, icid, valid, rdbase);
return CMD_CONTINUE;
}
return result;
return update_cte(s, icid, valid, rdbase) ? CMD_CONTINUE : CMD_STALL;
}
static bool update_dte(GICv3ITSState *s, uint32_t devid, bool valid,
uint8_t size, uint64_t itt_addr)
{
AddressSpace *as = &s->gicv3->dma_as;
uint64_t value;
uint64_t l2t_addr;
bool valid_l2t;
uint32_t l2t_id;
uint32_t num_l2_entries;
uint64_t entry_addr;
uint64_t dte = 0;
MemTxResult res = MEMTX_OK;
@ -536,47 +518,21 @@ static bool update_dte(GICv3ITSState *s, uint32_t devid, bool valid,
return true;
}
/*
* The specification defines the format of level 1 entries of a
* 2-level table, but the format of level 2 entries and the format
* of flat-mapped tables is IMPDEF.
*/
if (s->dt.indirect) {
l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
value = address_space_ldq_le(as,
s->dt.base_addr +
(l2t_id * L1TABLE_ENTRY_SIZE),
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
return false;
}
valid_l2t = (value & L2_TABLE_VALID_MASK) != 0;
if (valid_l2t) {
num_l2_entries = s->dt.page_sz / s->dt.entry_sz;
l2t_addr = value & ((1ULL << 51) - 1);
address_space_stq_le(as, l2t_addr +
((devid % num_l2_entries) * GITS_DTE_SIZE),
dte, MEMTXATTRS_UNSPECIFIED, &res);
}
} else {
/* Flat level table */
address_space_stq_le(as, s->dt.base_addr + (devid * GITS_DTE_SIZE),
dte, MEMTXATTRS_UNSPECIFIED, &res);
}
entry_addr = table_entry_addr(s, &s->dt, devid, &res);
if (res != MEMTX_OK) {
/* memory access error: stall */
return false;
} else {
}
if (entry_addr == -1) {
/* No L2 table for this index: discard write and continue */
return true;
}
address_space_stq_le(as, entry_addr, dte, MEMTXATTRS_UNSPECIFIED, &res);
return res == MEMTX_OK;
}
static bool process_mapd(GICv3ITSState *s, uint64_t value, uint32_t offset)
static ItsCmdResult process_mapd(GICv3ITSState *s, uint64_t value,
uint32_t offset)
{
AddressSpace *as = &s->gicv3->dma_as;
uint32_t devid;
@ -584,7 +540,6 @@ static bool process_mapd(GICv3ITSState *s, uint64_t value, uint32_t offset)
uint64_t itt_addr;
bool valid;
MemTxResult res = MEMTX_OK;
bool result = false;
devid = ((value & DEVID_MASK) >> DEVID_SHIFT);
@ -593,7 +548,7 @@ static bool process_mapd(GICv3ITSState *s, uint64_t value, uint32_t offset)
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
return result;
return CMD_STALL;
}
size = (value & SIZE_MASK);
@ -603,7 +558,7 @@ static bool process_mapd(GICv3ITSState *s, uint64_t value, uint32_t offset)
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
return result;
return CMD_STALL;
}
itt_addr = (value & ITTADDR_MASK) >> ITTADDR_SHIFT;
@ -620,11 +575,10 @@ static bool process_mapd(GICv3ITSState *s, uint64_t value, uint32_t offset)
* we ignore this command and move onto the next
* command in the queue
*/
} else {
result = update_dte(s, devid, valid, size, itt_addr);
return CMD_CONTINUE;
}
return result;
return update_dte(s, devid, valid, size, itt_addr) ? CMD_CONTINUE : CMD_STALL;
}
/*
@ -639,7 +593,6 @@ static void process_cmdq(GICv3ITSState *s)
uint64_t data;
AddressSpace *as = &s->gicv3->dma_as;
MemTxResult res = MEMTX_OK;
bool result = true;
uint8_t cmd;
int i;
@ -666,20 +619,27 @@ static void process_cmdq(GICv3ITSState *s)
}
while (wr_offset != rd_offset) {
ItsCmdResult result = CMD_CONTINUE;
cq_offset = (rd_offset * GITS_CMDQ_ENTRY_SIZE);
data = address_space_ldq_le(as, s->cq.base_addr + cq_offset,
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
result = false;
s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, STALLED, 1);
qemu_log_mask(LOG_GUEST_ERROR,
"%s: could not read command at 0x%" PRIx64 "\n",
__func__, s->cq.base_addr + cq_offset);
break;
}
cmd = (data & CMD_MASK);
switch (cmd) {
case GITS_CMD_INT:
res = process_its_cmd(s, data, cq_offset, INTERRUPT);
result = process_its_cmd(s, data, cq_offset, INTERRUPT);
break;
case GITS_CMD_CLEAR:
res = process_its_cmd(s, data, cq_offset, CLEAR);
result = process_its_cmd(s, data, cq_offset, CLEAR);
break;
case GITS_CMD_SYNC:
/*
@ -719,18 +679,16 @@ static void process_cmdq(GICv3ITSState *s)
default:
break;
}
if (result) {
if (result == CMD_CONTINUE) {
rd_offset++;
rd_offset %= s->cq.num_entries;
s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, OFFSET, rd_offset);
} else {
/*
* in this implementation, in case of dma read/write error
* we stall the command processing
*/
/* CMD_STALL */
s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, STALLED, 1);
qemu_log_mask(LOG_GUEST_ERROR,
"%s: %x cmd processing failed\n", __func__, cmd);
"%s: 0x%x cmd processing failed, stalling\n",
__func__, cmd);
break;
}
}

View File

@ -462,7 +462,7 @@ MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data,
break;
}
if (r == MEMTX_ERROR) {
if (r != MEMTX_OK) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid guest read at offset " TARGET_FMT_plx
" size %u\n", __func__, offset, size);
@ -521,7 +521,7 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
break;
}
if (r == MEMTX_ERROR) {
if (r != MEMTX_OK) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid guest write at offset " TARGET_FMT_plx
" size %u\n", __func__, offset, size);

View File

@ -0,0 +1,384 @@
/*
* ASPEED I3C Controller
*
* Copyright (C) 2021 ASPEED Technology Inc.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/error-report.h"
#include "hw/misc/aspeed_i3c.h"
#include "hw/registerfields.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
#include "migration/vmstate.h"
#include "trace.h"
/* I3C Controller Registers */
REG32(I3C1_REG0, 0x10)
REG32(I3C1_REG1, 0x14)
FIELD(I3C1_REG1, I2C_MODE, 0, 1)
FIELD(I3C1_REG1, SA_EN, 15, 1)
REG32(I3C2_REG0, 0x20)
REG32(I3C2_REG1, 0x24)
FIELD(I3C2_REG1, I2C_MODE, 0, 1)
FIELD(I3C2_REG1, SA_EN, 15, 1)
REG32(I3C3_REG0, 0x30)
REG32(I3C3_REG1, 0x34)
FIELD(I3C3_REG1, I2C_MODE, 0, 1)
FIELD(I3C3_REG1, SA_EN, 15, 1)
REG32(I3C4_REG0, 0x40)
REG32(I3C4_REG1, 0x44)
FIELD(I3C4_REG1, I2C_MODE, 0, 1)
FIELD(I3C4_REG1, SA_EN, 15, 1)
REG32(I3C5_REG0, 0x50)
REG32(I3C5_REG1, 0x54)
FIELD(I3C5_REG1, I2C_MODE, 0, 1)
FIELD(I3C5_REG1, SA_EN, 15, 1)
REG32(I3C6_REG0, 0x60)
REG32(I3C6_REG1, 0x64)
FIELD(I3C6_REG1, I2C_MODE, 0, 1)
FIELD(I3C6_REG1, SA_EN, 15, 1)
/* I3C Device Registers */
REG32(DEVICE_CTRL, 0x00)
REG32(DEVICE_ADDR, 0x04)
REG32(HW_CAPABILITY, 0x08)
REG32(COMMAND_QUEUE_PORT, 0x0c)
REG32(RESPONSE_QUEUE_PORT, 0x10)
REG32(RX_TX_DATA_PORT, 0x14)
REG32(IBI_QUEUE_STATUS, 0x18)
REG32(IBI_QUEUE_DATA, 0x18)
REG32(QUEUE_THLD_CTRL, 0x1c)
REG32(DATA_BUFFER_THLD_CTRL, 0x20)
REG32(IBI_QUEUE_CTRL, 0x24)
REG32(IBI_MR_REQ_REJECT, 0x2c)
REG32(IBI_SIR_REQ_REJECT, 0x30)
REG32(RESET_CTRL, 0x34)
REG32(SLV_EVENT_CTRL, 0x38)
REG32(INTR_STATUS, 0x3c)
REG32(INTR_STATUS_EN, 0x40)
REG32(INTR_SIGNAL_EN, 0x44)
REG32(INTR_FORCE, 0x48)
REG32(QUEUE_STATUS_LEVEL, 0x4c)
REG32(DATA_BUFFER_STATUS_LEVEL, 0x50)
REG32(PRESENT_STATE, 0x54)
REG32(CCC_DEVICE_STATUS, 0x58)
REG32(DEVICE_ADDR_TABLE_POINTER, 0x5c)
FIELD(DEVICE_ADDR_TABLE_POINTER, DEPTH, 16, 16)
FIELD(DEVICE_ADDR_TABLE_POINTER, ADDR, 0, 16)
REG32(DEV_CHAR_TABLE_POINTER, 0x60)
REG32(VENDOR_SPECIFIC_REG_POINTER, 0x6c)
REG32(SLV_MIPI_PID_VALUE, 0x70)
REG32(SLV_PID_VALUE, 0x74)
REG32(SLV_CHAR_CTRL, 0x78)
REG32(SLV_MAX_LEN, 0x7c)
REG32(MAX_READ_TURNAROUND, 0x80)
REG32(MAX_DATA_SPEED, 0x84)
REG32(SLV_DEBUG_STATUS, 0x88)
REG32(SLV_INTR_REQ, 0x8c)
REG32(DEVICE_CTRL_EXTENDED, 0xb0)
REG32(SCL_I3C_OD_TIMING, 0xb4)
REG32(SCL_I3C_PP_TIMING, 0xb8)
REG32(SCL_I2C_FM_TIMING, 0xbc)
REG32(SCL_I2C_FMP_TIMING, 0xc0)
REG32(SCL_EXT_LCNT_TIMING, 0xc8)
REG32(SCL_EXT_TERMN_LCNT_TIMING, 0xcc)
REG32(BUS_FREE_TIMING, 0xd4)
REG32(BUS_IDLE_TIMING, 0xd8)
REG32(I3C_VER_ID, 0xe0)
REG32(I3C_VER_TYPE, 0xe4)
REG32(EXTENDED_CAPABILITY, 0xe8)
REG32(SLAVE_CONFIG, 0xec)
static const uint32_t ast2600_i3c_device_resets[ASPEED_I3C_DEVICE_NR_REGS] = {
[R_HW_CAPABILITY] = 0x000e00bf,
[R_QUEUE_THLD_CTRL] = 0x01000101,
[R_I3C_VER_ID] = 0x3130302a,
[R_I3C_VER_TYPE] = 0x6c633033,
[R_DEVICE_ADDR_TABLE_POINTER] = 0x00080280,
[R_DEV_CHAR_TABLE_POINTER] = 0x00020200,
[A_VENDOR_SPECIFIC_REG_POINTER] = 0x000000b0,
[R_SLV_MAX_LEN] = 0x00ff00ff,
};
static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset,
unsigned size)
{
AspeedI3CDevice *s = ASPEED_I3C_DEVICE(opaque);
uint32_t addr = offset >> 2;
uint64_t value;
switch (addr) {
case R_COMMAND_QUEUE_PORT:
value = 0;
break;
default:
value = s->regs[addr];
break;
}
trace_aspeed_i3c_device_read(s->id, offset, value);
return value;
}
static void aspeed_i3c_device_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
AspeedI3CDevice *s = ASPEED_I3C_DEVICE(opaque);
uint32_t addr = offset >> 2;
trace_aspeed_i3c_device_write(s->id, offset, value);
switch (addr) {
case R_HW_CAPABILITY:
case R_RESPONSE_QUEUE_PORT:
case R_IBI_QUEUE_DATA:
case R_QUEUE_STATUS_LEVEL:
case R_PRESENT_STATE:
case R_CCC_DEVICE_STATUS:
case R_DEVICE_ADDR_TABLE_POINTER:
case R_VENDOR_SPECIFIC_REG_POINTER:
case R_SLV_CHAR_CTRL:
case R_SLV_MAX_LEN:
case R_MAX_READ_TURNAROUND:
case R_I3C_VER_ID:
case R_I3C_VER_TYPE:
case R_EXTENDED_CAPABILITY:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: write to readonly register[0x%02" HWADDR_PRIx
"] = 0x%08" PRIx64 "\n",
__func__, offset, value);
break;
case R_RX_TX_DATA_PORT:
break;
case R_RESET_CTRL:
break;
default:
s->regs[addr] = value;
break;
}
}
static const VMStateDescription aspeed_i3c_device_vmstate = {
.name = TYPE_ASPEED_I3C,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]){
VMSTATE_UINT32_ARRAY(regs, AspeedI3CDevice, ASPEED_I3C_DEVICE_NR_REGS),
VMSTATE_END_OF_LIST(),
}
};
static const MemoryRegionOps aspeed_i3c_device_ops = {
.read = aspeed_i3c_device_read,
.write = aspeed_i3c_device_write,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void aspeed_i3c_device_reset(DeviceState *dev)
{
AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev);
memcpy(s->regs, ast2600_i3c_device_resets, sizeof(s->regs));
}
static void aspeed_i3c_device_realize(DeviceState *dev, Error **errp)
{
AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev);
g_autofree char *name = g_strdup_printf(TYPE_ASPEED_I3C_DEVICE ".%d",
s->id);
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i3c_device_ops,
s, name, ASPEED_I3C_DEVICE_NR_REGS << 2);
}
static uint64_t aspeed_i3c_read(void *opaque, hwaddr addr, unsigned int size)
{
AspeedI3CState *s = ASPEED_I3C(opaque);
uint64_t val = 0;
val = s->regs[addr >> 2];
trace_aspeed_i3c_read(addr, val);
return val;
}
static void aspeed_i3c_write(void *opaque,
hwaddr addr,
uint64_t data,
unsigned int size)
{
AspeedI3CState *s = ASPEED_I3C(opaque);
trace_aspeed_i3c_write(addr, data);
addr >>= 2;
/* I3C controller register */
switch (addr) {
case R_I3C1_REG1:
case R_I3C2_REG1:
case R_I3C3_REG1:
case R_I3C4_REG1:
case R_I3C5_REG1:
case R_I3C6_REG1:
if (data & R_I3C1_REG1_I2C_MODE_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: Unsupported I2C mode [0x%08" HWADDR_PRIx
"]=%08" PRIx64 "\n",
__func__, addr << 2, data);
break;
}
if (data & R_I3C1_REG1_SA_EN_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: Unsupported slave mode [%08" HWADDR_PRIx
"]=0x%08" PRIx64 "\n",
__func__, addr << 2, data);
break;
}
s->regs[addr] = data;
break;
default:
s->regs[addr] = data;
break;
}
}
static const MemoryRegionOps aspeed_i3c_ops = {
.read = aspeed_i3c_read,
.write = aspeed_i3c_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 4,
}
};
static void aspeed_i3c_reset(DeviceState *dev)
{
AspeedI3CState *s = ASPEED_I3C(dev);
memset(s->regs, 0, sizeof(s->regs));
}
static void aspeed_i3c_instance_init(Object *obj)
{
AspeedI3CState *s = ASPEED_I3C(obj);
int i;
for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) {
object_initialize_child(obj, "device[*]", &s->devices[i],
TYPE_ASPEED_I3C_DEVICE);
}
}
static void aspeed_i3c_realize(DeviceState *dev, Error **errp)
{
int i;
AspeedI3CState *s = ASPEED_I3C(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
memory_region_init(&s->iomem_container, OBJECT(s),
TYPE_ASPEED_I3C ".container", 0x8000);
sysbus_init_mmio(sbd, &s->iomem_container);
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i3c_ops, s,
TYPE_ASPEED_I3C ".regs", ASPEED_I3C_NR_REGS << 2);
memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem);
for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) {
Object *dev = OBJECT(&s->devices[i]);
if (!object_property_set_uint(dev, "device-id", i, errp)) {
return;
}
if (!sysbus_realize(SYS_BUS_DEVICE(dev), errp)) {
return;
}
/*
* Register Address of I3CX Device =
* (Base Address of Global Register) + (Offset of I3CX) + Offset
* X = 0, 1, 2, 3, 4, 5
* Offset of I3C0 = 0x2000
* Offset of I3C1 = 0x3000
* Offset of I3C2 = 0x4000
* Offset of I3C3 = 0x5000
* Offset of I3C4 = 0x6000
* Offset of I3C5 = 0x7000
*/
memory_region_add_subregion(&s->iomem_container,
0x2000 + i * 0x1000, &s->devices[i].mr);
}
}
static Property aspeed_i3c_device_properties[] = {
DEFINE_PROP_UINT8("device-id", AspeedI3CDevice, id, 0),
DEFINE_PROP_END_OF_LIST(),
};
static void aspeed_i3c_device_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->desc = "Aspeed I3C Device";
dc->realize = aspeed_i3c_device_realize;
dc->reset = aspeed_i3c_device_reset;
device_class_set_props(dc, aspeed_i3c_device_properties);
}
static const TypeInfo aspeed_i3c_device_info = {
.name = TYPE_ASPEED_I3C_DEVICE,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AspeedI3CDevice),
.class_init = aspeed_i3c_device_class_init,
};
static const VMStateDescription vmstate_aspeed_i3c = {
.name = TYPE_ASPEED_I3C,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, AspeedI3CState, ASPEED_I3C_NR_REGS),
VMSTATE_STRUCT_ARRAY(devices, AspeedI3CState, ASPEED_I3C_NR_DEVICES, 1,
aspeed_i3c_device_vmstate, AspeedI3CDevice),
VMSTATE_END_OF_LIST(),
}
};
static void aspeed_i3c_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = aspeed_i3c_realize;
dc->reset = aspeed_i3c_reset;
dc->desc = "Aspeed I3C Controller";
dc->vmsd = &vmstate_aspeed_i3c;
}
static const TypeInfo aspeed_i3c_info = {
.name = TYPE_ASPEED_I3C,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_init = aspeed_i3c_instance_init,
.instance_size = sizeof(AspeedI3CState),
.class_init = aspeed_i3c_class_init,
};
static void aspeed_i3c_register_types(void)
{
type_register_static(&aspeed_i3c_device_info);
type_register_static(&aspeed_i3c_info);
}
type_init(aspeed_i3c_register_types);

View File

@ -105,6 +105,7 @@ softmmu_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c'))
softmmu_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c'))
softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
'aspeed_hace.c',
'aspeed_i3c.c',
'aspeed_lpc.c',
'aspeed_scu.c',
'aspeed_sdmc.c',

View File

@ -199,6 +199,12 @@ armsse_mhu_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU wri
# aspeed_xdma.c
aspeed_xdma_write(uint64_t offset, uint64_t data) "XDMA write: offset 0x%" PRIx64 " data 0x%" PRIx64
# aspeed_i3c.c
aspeed_i3c_read(uint64_t offset, uint64_t data) "I3C read: offset 0x%" PRIx64 " data 0x%" PRIx64
aspeed_i3c_write(uint64_t offset, uint64_t data) "I3C write: offset 0x%" PRIx64 " data 0x%" PRIx64
aspeed_i3c_device_read(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] read: offset 0x%" PRIx64 " data 0x%" PRIx64
aspeed_i3c_device_write(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] write: offset 0x%" PRIx64 " data 0x%" PRIx64
# bcm2835_property.c
bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu"

View File

@ -26,6 +26,7 @@ softmmu_ss.add(when: 'CONFIG_ALLWINNER_EMAC', if_true: files('allwinner_emac.c')
softmmu_ss.add(when: 'CONFIG_ALLWINNER_SUN8I_EMAC', if_true: files('allwinner-sun8i-emac.c'))
softmmu_ss.add(when: 'CONFIG_IMX_FEC', if_true: files('imx_fec.c'))
softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-emac.c'))
softmmu_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('mv88w8618_eth.c'))
softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_gem.c'))
softmmu_ss.add(when: 'CONFIG_STELLARIS_ENET', if_true: files('stellaris_enet.c'))

View File

@ -0,0 +1,403 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Marvell MV88W8618 / Freecom MusicPal emulation.
*
* Copyright (c) 2008 Jan Kiszka
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/qdev-properties.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/net/mv88w8618_eth.h"
#include "migration/vmstate.h"
#include "sysemu/dma.h"
#include "net/net.h"
#define MP_ETH_SIZE 0x00001000
/* Ethernet register offsets */
#define MP_ETH_SMIR 0x010
#define MP_ETH_PCXR 0x408
#define MP_ETH_SDCMR 0x448
#define MP_ETH_ICR 0x450
#define MP_ETH_IMR 0x458
#define MP_ETH_FRDP0 0x480
#define MP_ETH_FRDP1 0x484
#define MP_ETH_FRDP2 0x488
#define MP_ETH_FRDP3 0x48C
#define MP_ETH_CRDP0 0x4A0
#define MP_ETH_CRDP1 0x4A4
#define MP_ETH_CRDP2 0x4A8
#define MP_ETH_CRDP3 0x4AC
#define MP_ETH_CTDP0 0x4E0
#define MP_ETH_CTDP1 0x4E4
/* MII PHY access */
#define MP_ETH_SMIR_DATA 0x0000FFFF
#define MP_ETH_SMIR_ADDR 0x03FF0000
#define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */
#define MP_ETH_SMIR_RDVALID (1 << 27)
/* PHY registers */
#define MP_ETH_PHY1_BMSR 0x00210000
#define MP_ETH_PHY1_PHYSID1 0x00410000
#define MP_ETH_PHY1_PHYSID2 0x00610000
#define MP_PHY_BMSR_LINK 0x0004
#define MP_PHY_BMSR_AUTONEG 0x0008
#define MP_PHY_88E3015 0x01410E20
/* TX descriptor status */
#define MP_ETH_TX_OWN (1U << 31)
/* RX descriptor status */
#define MP_ETH_RX_OWN (1U << 31)
/* Interrupt cause/mask bits */
#define MP_ETH_IRQ_RX_BIT 0
#define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT)
#define MP_ETH_IRQ_TXHI_BIT 2
#define MP_ETH_IRQ_TXLO_BIT 3
/* Port config bits */
#define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */
/* SDMA command bits */
#define MP_ETH_CMD_TXHI (1 << 23)
#define MP_ETH_CMD_TXLO (1 << 22)
typedef struct mv88w8618_tx_desc {
uint32_t cmdstat;
uint16_t res;
uint16_t bytes;
uint32_t buffer;
uint32_t next;
} mv88w8618_tx_desc;
typedef struct mv88w8618_rx_desc {
uint32_t cmdstat;
uint16_t bytes;
uint16_t buffer_size;
uint32_t buffer;
uint32_t next;
} mv88w8618_rx_desc;
OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH)
struct mv88w8618_eth_state {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion iomem;
qemu_irq irq;
MemoryRegion *dma_mr;
AddressSpace dma_as;
uint32_t smir;
uint32_t icr;
uint32_t imr;
int mmio_index;
uint32_t vlan_header;
uint32_t tx_queue[2];
uint32_t rx_queue[4];
uint32_t frx_queue[4];
uint32_t cur_rx[4];
NICState *nic;
NICConf conf;
};
static void eth_rx_desc_put(AddressSpace *dma_as, uint32_t addr,
mv88w8618_rx_desc *desc)
{
cpu_to_le32s(&desc->cmdstat);
cpu_to_le16s(&desc->bytes);
cpu_to_le16s(&desc->buffer_size);
cpu_to_le32s(&desc->buffer);
cpu_to_le32s(&desc->next);
dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
}
static void eth_rx_desc_get(AddressSpace *dma_as, uint32_t addr,
mv88w8618_rx_desc *desc)
{
dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
le32_to_cpus(&desc->cmdstat);
le16_to_cpus(&desc->bytes);
le16_to_cpus(&desc->buffer_size);
le32_to_cpus(&desc->buffer);
le32_to_cpus(&desc->next);
}
static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
uint32_t desc_addr;
mv88w8618_rx_desc desc;
int i;
for (i = 0; i < 4; i++) {
desc_addr = s->cur_rx[i];
if (!desc_addr) {
continue;
}
do {
eth_rx_desc_get(&s->dma_as, desc_addr, &desc);
if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) {
dma_memory_write(&s->dma_as, desc.buffer + s->vlan_header,
buf, size, MEMTXATTRS_UNSPECIFIED);
desc.bytes = size + s->vlan_header;
desc.cmdstat &= ~MP_ETH_RX_OWN;
s->cur_rx[i] = desc.next;
s->icr |= MP_ETH_IRQ_RX;
if (s->icr & s->imr) {
qemu_irq_raise(s->irq);
}
eth_rx_desc_put(&s->dma_as, desc_addr, &desc);
return size;
}
desc_addr = desc.next;
} while (desc_addr != s->rx_queue[i]);
}
return size;
}
static void eth_tx_desc_put(AddressSpace *dma_as, uint32_t addr,
mv88w8618_tx_desc *desc)
{
cpu_to_le32s(&desc->cmdstat);
cpu_to_le16s(&desc->res);
cpu_to_le16s(&desc->bytes);
cpu_to_le32s(&desc->buffer);
cpu_to_le32s(&desc->next);
dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
}
static void eth_tx_desc_get(AddressSpace *dma_as, uint32_t addr,
mv88w8618_tx_desc *desc)
{
dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
le32_to_cpus(&desc->cmdstat);
le16_to_cpus(&desc->res);
le16_to_cpus(&desc->bytes);
le32_to_cpus(&desc->buffer);
le32_to_cpus(&desc->next);
}
static void eth_send(mv88w8618_eth_state *s, int queue_index)
{
uint32_t desc_addr = s->tx_queue[queue_index];
mv88w8618_tx_desc desc;
uint32_t next_desc;
uint8_t buf[2048];
int len;
do {
eth_tx_desc_get(&s->dma_as, desc_addr, &desc);
next_desc = desc.next;
if (desc.cmdstat & MP_ETH_TX_OWN) {
len = desc.bytes;
if (len < 2048) {
dma_memory_read(&s->dma_as, desc.buffer, buf, len,
MEMTXATTRS_UNSPECIFIED);
qemu_send_packet(qemu_get_queue(s->nic), buf, len);
}
desc.cmdstat &= ~MP_ETH_TX_OWN;
s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
eth_tx_desc_put(&s->dma_as, desc_addr, &desc);
}
desc_addr = next_desc;
} while (desc_addr != s->tx_queue[queue_index]);
}
static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
unsigned size)
{
mv88w8618_eth_state *s = opaque;
switch (offset) {
case MP_ETH_SMIR:
if (s->smir & MP_ETH_SMIR_OPCODE) {
switch (s->smir & MP_ETH_SMIR_ADDR) {
case MP_ETH_PHY1_BMSR:
return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG |
MP_ETH_SMIR_RDVALID;
case MP_ETH_PHY1_PHYSID1:
return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID;
case MP_ETH_PHY1_PHYSID2:
return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID;
default:
return MP_ETH_SMIR_RDVALID;
}
}
return 0;
case MP_ETH_ICR:
return s->icr;
case MP_ETH_IMR:
return s->imr;
case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
return s->frx_queue[(offset - MP_ETH_FRDP0) / 4];
case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
return s->rx_queue[(offset - MP_ETH_CRDP0) / 4];
case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
return s->tx_queue[(offset - MP_ETH_CTDP0) / 4];
default:
return 0;
}
}
static void mv88w8618_eth_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
mv88w8618_eth_state *s = opaque;
switch (offset) {
case MP_ETH_SMIR:
s->smir = value;
break;
case MP_ETH_PCXR:
s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
break;
case MP_ETH_SDCMR:
if (value & MP_ETH_CMD_TXHI) {
eth_send(s, 1);
}
if (value & MP_ETH_CMD_TXLO) {
eth_send(s, 0);
}
if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
qemu_irq_raise(s->irq);
}
break;
case MP_ETH_ICR:
s->icr &= value;
break;
case MP_ETH_IMR:
s->imr = value;
if (s->icr & s->imr) {
qemu_irq_raise(s->irq);
}
break;
case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
s->frx_queue[(offset - MP_ETH_FRDP0) / 4] = value;
break;
case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
s->rx_queue[(offset - MP_ETH_CRDP0) / 4] =
s->cur_rx[(offset - MP_ETH_CRDP0) / 4] = value;
break;
case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
s->tx_queue[(offset - MP_ETH_CTDP0) / 4] = value;
break;
}
}
static const MemoryRegionOps mv88w8618_eth_ops = {
.read = mv88w8618_eth_read,
.write = mv88w8618_eth_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void eth_cleanup(NetClientState *nc)
{
mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
static NetClientInfo net_mv88w8618_info = {
.type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = eth_receive,
.cleanup = eth_cleanup,
};
static void mv88w8618_eth_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
mv88w8618_eth_state *s = MV88W8618_ETH(dev);
sysbus_init_irq(sbd, &s->irq);
memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
"mv88w8618-eth", MP_ETH_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
}
static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
{
mv88w8618_eth_state *s = MV88W8618_ETH(dev);
if (!s->dma_mr) {
error_setg(errp, TYPE_MV88W8618_ETH " 'dma-memory' link not set");
return;
}
address_space_init(&s->dma_as, s->dma_mr, "emac-dma");
s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
object_get_typename(OBJECT(dev)), dev->id, s);
}
static const VMStateDescription mv88w8618_eth_vmsd = {
.name = "mv88w8618_eth",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(smir, mv88w8618_eth_state),
VMSTATE_UINT32(icr, mv88w8618_eth_state),
VMSTATE_UINT32(imr, mv88w8618_eth_state),
VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
VMSTATE_END_OF_LIST()
}
};
static Property mv88w8618_eth_properties[] = {
DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr,
TYPE_MEMORY_REGION, MemoryRegion *),
DEFINE_PROP_END_OF_LIST(),
};
static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &mv88w8618_eth_vmsd;
device_class_set_props(dc, mv88w8618_eth_properties);
dc->realize = mv88w8618_eth_realize;
}
static const TypeInfo mv88w8618_eth_info = {
.name = TYPE_MV88W8618_ETH,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(mv88w8618_eth_state),
.instance_init = mv88w8618_eth_init,
.class_init = mv88w8618_eth_class_init,
};
static void musicpal_register_types(void)
{
type_register_static(&mv88w8618_eth_info);
}
type_init(musicpal_register_types)

View File

@ -46,14 +46,25 @@
*/
#define VIRTIO_MEM_MIN_BLOCK_SIZE ((uint32_t)(1 * MiB))
#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \
defined(__powerpc64__)
#define VIRTIO_MEM_DEFAULT_THP_SIZE ((uint32_t)(2 * MiB))
#else
/* fallback to 1 MiB (e.g., the THP size on s390x) */
#define VIRTIO_MEM_DEFAULT_THP_SIZE VIRTIO_MEM_MIN_BLOCK_SIZE
static uint32_t virtio_mem_default_thp_size(void)
{
uint32_t default_thp_size = VIRTIO_MEM_MIN_BLOCK_SIZE;
#if defined(__x86_64__) || defined(__arm__) || defined(__powerpc64__)
default_thp_size = 2 * MiB;
#elif defined(__aarch64__)
if (qemu_real_host_page_size == 4 * KiB) {
default_thp_size = 2 * MiB;
} else if (qemu_real_host_page_size == 16 * KiB) {
default_thp_size = 32 * MiB;
} else if (qemu_real_host_page_size == 64 * KiB) {
default_thp_size = 512 * MiB;
}
#endif
return default_thp_size;
}
/*
* We want to have a reasonable default block size such that
* 1. We avoid splitting THPs when unplugging memory, which degrades
@ -86,11 +97,8 @@ static uint32_t virtio_mem_thp_size(void)
if (g_file_get_contents(HPAGE_PMD_SIZE_PATH, &content, NULL, NULL) &&
!qemu_strtou64(content, &endptr, 0, &tmp) &&
(!endptr || *endptr == '\n')) {
/*
* Sanity-check the value, if it's too big (e.g., aarch64 with 64k base
* pages) or weird, fallback to something smaller.
*/
if (!tmp || !is_power_of_2(tmp) || tmp > 16 * MiB) {
/* Sanity-check the value and fallback to something reasonable. */
if (!tmp || !is_power_of_2(tmp)) {
warn_report("Read unsupported THP size: %" PRIx64, tmp);
} else {
thp_size = tmp;
@ -98,7 +106,7 @@ static uint32_t virtio_mem_thp_size(void)
}
if (!thp_size) {
thp_size = VIRTIO_MEM_DEFAULT_THP_SIZE;
thp_size = virtio_mem_default_thp_size();
warn_report("Could not detect THP size, falling back to %" PRIx64
" MiB.", thp_size / MiB);
}
@ -138,7 +146,7 @@ static bool virtio_mem_has_shared_zeropage(RAMBlock *rb)
* The memory block size corresponds mostly to the section size.
*
* This allows e.g., to add 20MB with a section size of 128MB on x86_64, and
* a section size of 1GB on arm64 (as long as the start address is properly
* a section size of 512MB on arm64 (as long as the start address is properly
* aligned, similar to ordinary DIMMs).
*
* We can change this at any time and maybe even make it configurable if
@ -147,6 +155,8 @@ static bool virtio_mem_has_shared_zeropage(RAMBlock *rb)
*/
#if defined(TARGET_X86_64) || defined(TARGET_I386)
#define VIRTIO_MEM_USABLE_EXTENT (2 * (128 * MiB))
#elif defined(TARGET_ARM)
#define VIRTIO_MEM_USABLE_EXTENT (2 * (512 * MiB))
#else
#error VIRTIO_MEM_USABLE_EXTENT not defined
#endif

View File

@ -21,6 +21,7 @@
#include "hw/timer/aspeed_timer.h"
#include "hw/rtc/aspeed_rtc.h"
#include "hw/i2c/aspeed_i2c.h"
#include "hw/misc/aspeed_i3c.h"
#include "hw/ssi/aspeed_smc.h"
#include "hw/misc/aspeed_hace.h"
#include "hw/watchdog/wdt_aspeed.h"
@ -51,6 +52,7 @@ struct AspeedSoCState {
AspeedRtcState rtc;
AspeedTimerCtrlState timerctrl;
AspeedI2CState i2c;
AspeedI3CState i3c;
AspeedSCUState scu;
AspeedHACEState hace;
AspeedXDMAState xdma;
@ -141,6 +143,7 @@ enum {
ASPEED_DEV_HACE,
ASPEED_DEV_DPMCU,
ASPEED_DEV_DP,
ASPEED_DEV_I3C,
};
#endif /* ASPEED_SOC_H */

View File

@ -143,6 +143,8 @@ struct VirtMachineState {
bool secure;
bool highmem;
bool highmem_ecam;
bool highmem_mmio;
bool highmem_redists;
bool its;
bool tcg_its;
bool virt;
@ -190,7 +192,8 @@ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms)
assert(vms->gic_version == VIRT_GIC_VERSION_3);
return MACHINE(vms)->smp.cpus > redist0_capacity ? 2 : 1;
return (MACHINE(vms)->smp.cpus > redist0_capacity &&
vms->highmem_redists) ? 2 : 1;
}
#endif /* QEMU_ARM_VIRT_H */

View File

@ -0,0 +1,48 @@
/*
* ASPEED I3C Controller
*
* Copyright (C) 2021 ASPEED Technology Inc.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#ifndef ASPEED_I3C_H
#define ASPEED_I3C_H
#include "hw/sysbus.h"
#define TYPE_ASPEED_I3C "aspeed.i3c"
#define TYPE_ASPEED_I3C_DEVICE "aspeed.i3c.device"
OBJECT_DECLARE_TYPE(AspeedI3CState, AspeedI3CClass, ASPEED_I3C)
#define ASPEED_I3C_NR_REGS (0x70 >> 2)
#define ASPEED_I3C_DEVICE_NR_REGS (0x300 >> 2)
#define ASPEED_I3C_NR_DEVICES 6
OBJECT_DECLARE_SIMPLE_TYPE(AspeedI3CDevice, ASPEED_I3C_DEVICE)
typedef struct AspeedI3CDevice {
/* <private> */
SysBusDevice parent;
/* <public> */
MemoryRegion mr;
qemu_irq irq;
uint8_t id;
uint32_t regs[ASPEED_I3C_DEVICE_NR_REGS];
} AspeedI3CDevice;
typedef struct AspeedI3CState {
/* <private> */
SysBusDevice parent;
/* <public> */
MemoryRegion iomem;
MemoryRegion iomem_container;
qemu_irq irq;
uint32_t regs[ASPEED_I3C_NR_REGS];
AspeedI3CDevice devices[ASPEED_I3C_NR_DEVICES];
} AspeedI3CState;
#endif /* ASPEED_I3C_H */

View File

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Marvell MV88W8618 / Freecom MusicPal emulation.
*
* Copyright (c) 2008-2021 QEMU contributors
*/
#ifndef HW_NET_MV88W8618_H
#define HW_NET_MV88W8618_H
#define TYPE_MV88W8618_ETH "mv88w8618_eth"
#endif

View File

@ -277,6 +277,16 @@ SRST
-smp 16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16
The following sub-option defines a CPU topology hierarchy (2 sockets
totally on the machine, 2 clusters per socket, 2 cores per cluster,
2 threads per core) for ARM virt machines which support sockets/clusters
/cores/threads. Some members of the option can be omitted but their values
will be automatically computed:
::
-smp 16,sockets=2,clusters=2,cores=2,threads=2,maxcpus=16
Historically preference was given to the coarsest topology parameters
when computing missing values (ie sockets preferred over cores, which
were preferred over threads), however, this behaviour is considered

View File

@ -1380,17 +1380,10 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp)
return;
}
/*
* KVM does not support modifications to this feature.
* We have not registered the cpu properties when KVM
* is in use, so the user will not be able to set them.
*/
if (!kvm_enabled()) {
arm_cpu_pauth_finalize(cpu, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
return;
}
arm_cpu_pauth_finalize(cpu, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
return;
}
}
@ -2091,6 +2084,7 @@ static void arm_host_initfn(Object *obj)
kvm_arm_set_cpu_features_from_host(cpu);
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
aarch64_add_sve_properties(obj);
aarch64_add_pauth_properties(obj);
}
#else
hvf_arm_set_cpu_features_from_host(cpu);

View File

@ -1076,6 +1076,7 @@ void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq);
void aarch64_sve_change_el(CPUARMState *env, int old_el,
int new_el, bool el0_a64);
void aarch64_add_sve_properties(Object *obj);
void aarch64_add_pauth_properties(Object *obj);
/*
* SVE registers are encoded in KVM's memory in an endianness-invariant format.

View File

@ -630,6 +630,15 @@ void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp)
int arch_val = 0, impdef_val = 0;
uint64_t t;
/* Exit early if PAuth is enabled, and fall through to disable it */
if (kvm_enabled() && cpu->prop_pauth) {
if (!cpu_isar_feature(aa64_pauth, cpu)) {
error_setg(errp, "'pauth' feature not supported by KVM on this host");
}
return;
}
/* TODO: Handle HaveEnhancedPAC, HaveEnhancedPAC2, HaveFPAC. */
if (cpu->prop_pauth) {
if (cpu->prop_pauth_impdef) {
@ -655,6 +664,23 @@ static Property arm_cpu_pauth_property =
static Property arm_cpu_pauth_impdef_property =
DEFINE_PROP_BOOL("pauth-impdef", ARMCPU, prop_pauth_impdef, false);
void aarch64_add_pauth_properties(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
/* Default to PAUTH on, with the architected algorithm on TCG. */
qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_property);
if (kvm_enabled()) {
/*
* Mirror PAuth support from the probed sysregs back into the
* property for KVM. Is it just a bit backward? Yes it is!
*/
cpu->prop_pauth = cpu_isar_feature(aa64_pauth, cpu);
} else {
qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_impdef_property);
}
}
/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host);
* otherwise, a CPU with as many features enabled as our emulation supports.
* The version of '-cpu max' for qemu-system-arm is defined in cpu.c;
@ -829,13 +855,10 @@ static void aarch64_max_initfn(Object *obj)
cpu->dcz_blocksize = 7; /* 512 bytes */
#endif
/* Default to PAUTH on, with the architected algorithm. */
qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_property);
qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_impdef_property);
bitmap_fill(cpu->sve_vq_supported, ARM_MAX_VQ);
}
aarch64_add_pauth_properties(obj);
aarch64_add_sve_properties(obj);
object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq,
cpu_max_set_sve_max_vq, NULL, NULL);

View File

@ -491,6 +491,12 @@ static int read_sys_reg64(int fd, uint64_t *pret, uint64_t id)
return ioctl(fd, KVM_GET_ONE_REG, &idreg);
}
static bool kvm_arm_pauth_supported(void)
{
return (kvm_check_extension(kvm_state, KVM_CAP_ARM_PTRAUTH_ADDRESS) &&
kvm_check_extension(kvm_state, KVM_CAP_ARM_PTRAUTH_GENERIC));
}
bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
{
/* Identify the feature bits corresponding to the host CPU, and
@ -521,6 +527,17 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
*/
struct kvm_vcpu_init init = { .target = -1, };
/*
* Ask for Pointer Authentication if supported. We can't play the
* SVE trick of synthesising the ID reg as KVM won't tell us
* whether we have the architected or IMPDEF version of PAuth, so
* we have to use the actual ID regs.
*/
if (kvm_arm_pauth_supported()) {
init.features[0] |= (1 << KVM_ARM_VCPU_PTRAUTH_ADDRESS |
1 << KVM_ARM_VCPU_PTRAUTH_GENERIC);
}
if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
return false;
}
@ -865,6 +882,10 @@ int kvm_arch_init_vcpu(CPUState *cs)
assert(kvm_arm_sve_supported());
cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_SVE;
}
if (cpu_isar_feature(aa64_pauth, cpu)) {
cpu->kvm_init_features[0] |= (1 << KVM_ARM_VCPU_PTRAUTH_ADDRESS |
1 << KVM_ARM_VCPU_PTRAUTH_GENERIC);
}
/* Do KVM_ARM_VCPU_INIT ioctl */
ret = kvm_arm_vcpu_init(cs);

Binary file not shown.