mirror of https://gitee.com/openkylin/qemu.git
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>
This commit is contained in:
commit
2c89b5af5e
|
@ -774,6 +774,8 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
||||||
L: qemu-arm@nongnu.org
|
L: qemu-arm@nongnu.org
|
||||||
S: Odd Fixes
|
S: Odd Fixes
|
||||||
F: hw/arm/musicpal.c
|
F: hw/arm/musicpal.c
|
||||||
|
F: hw/net/mv88w8618_eth.c
|
||||||
|
F: include/hw/net/mv88w8618_eth.h
|
||||||
F: docs/system/arm/musicpal.rst
|
F: docs/system/arm/musicpal.rst
|
||||||
|
|
||||||
Nuvoton NPCM7xx
|
Nuvoton NPCM7xx
|
||||||
|
|
|
@ -217,10 +217,6 @@ TCG VCPU Features
|
||||||
TCG VCPU features are CPU features that are specific to TCG.
|
TCG VCPU features are CPU features that are specific to TCG.
|
||||||
Below is the list of TCG VCPU features and their descriptions.
|
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
|
pauth-impdef When ``FEAT_Pauth`` is enabled, either the
|
||||||
*impdef* (Implementation Defined) algorithm
|
*impdef* (Implementation Defined) algorithm
|
||||||
is enabled or the *architected* QARMA algorithm
|
is enabled or the *architected* QARMA algorithm
|
||||||
|
|
|
@ -82,6 +82,7 @@ Emulated Devices
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
|
devices/can.rst
|
||||||
devices/ivshmem.rst
|
devices/ivshmem.rst
|
||||||
devices/net.rst
|
devices/net.rst
|
||||||
devices/nvme.rst
|
devices/nvme.rst
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
QEMU CAN bus emulation support
|
CAN Bus Emulation Support
|
||||||
==============================
|
=========================
|
||||||
|
|
||||||
The CAN bus emulation provides mechanism to connect multiple
|
The CAN bus emulation provides mechanism to connect multiple
|
||||||
emulated CAN controller chips together by one or multiple CAN busses
|
emulated CAN controller chips together by one or multiple CAN busses
|
||||||
(the controller device "canbus" parameter). The individual 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.
|
to work on CAN hardware emulation on QEMU.
|
||||||
|
|
||||||
Examples how to use CAN emulation for SJA1000 based boards
|
Examples how to use CAN emulation for SJA1000 based boards
|
||||||
==========================================================
|
----------------------------------------------------------
|
||||||
|
|
||||||
When QEMU with CAN PCI support is compiled then one of the next
|
When QEMU with CAN PCI support is compiled then one of the next
|
||||||
CAN boards can be selected
|
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
|
-object can-bus,id=canbus0
|
||||||
-device kvaser_pci,canbus=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
|
-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
|
-object can-bus,id=canbus0
|
||||||
-device pcm3680_pci,canbus0=canbus0,canbus1=canbus0
|
-device pcm3680_pci,canbus0=canbus0,canbus1=canbus0
|
||||||
|
|
||||||
another example:
|
Another example::
|
||||||
|
|
||||||
-object can-bus,id=canbus0
|
-object can-bus,id=canbus0
|
||||||
-object can-bus,id=canbus1
|
-object can-bus,id=canbus1
|
||||||
-device pcm3680_pci,canbus0=canbus0,canbus1=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
|
-device mioe3680_pci,canbus0=canbus0
|
||||||
|
|
||||||
|
|
||||||
The ''kvaser_pci'' board/device model is compatible with and has been tested with
|
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.
|
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 \
|
qemu-system-x86_64 -accel kvm -kernel /boot/vmlinuz-4.9.0-4-amd64 \
|
||||||
-initrd ramdisk.cpio \
|
-initrd ramdisk.cpio \
|
||||||
|
@ -69,7 +73,7 @@ Example for qemu-system-x86_64:
|
||||||
-device kvaser_pci,canbus=canbus0 \
|
-device kvaser_pci,canbus=canbus0 \
|
||||||
-nographic -append "console=ttyS0"
|
-nographic -append "console=ttyS0"
|
||||||
|
|
||||||
Example for qemu-system-arm:
|
Example for qemu-system-arm::
|
||||||
|
|
||||||
qemu-system-arm -cpu arm1176 -m 256 -M versatilepb \
|
qemu-system-arm -cpu arm1176 -m 256 -M versatilepb \
|
||||||
-kernel kernel-qemu-arm1176-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
|
The CAN interface of the host system has to be configured for proper
|
||||||
bitrate and set up. Configuration is not propagated from emulated
|
bitrate and set up. Configuration is not propagated from emulated
|
||||||
devices through bus to the physical host device. Example configuration
|
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 type can bitrate 1000000
|
||||||
ip link set can0 up
|
ip link set can0 up
|
||||||
|
|
||||||
Virtual (host local only) can interface can be used on the host
|
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
|
ip link add dev can0 type vcan
|
||||||
|
|
||||||
The CAN interface on the host side can be used to analyze CAN
|
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
|
candump can0
|
||||||
|
|
||||||
CTU CAN FD support examples
|
CTU CAN FD support examples
|
||||||
===========================
|
---------------------------
|
||||||
|
|
||||||
This open-source core provides CAN FD support. CAN FD drames are
|
This open-source core provides CAN FD support. CAN FD drames are
|
||||||
delivered even to the host systems when SocketCAN interface is found
|
delivered even to the host systems when SocketCAN interface is found
|
||||||
CAN FD capable.
|
CAN FD capable.
|
||||||
|
@ -113,7 +116,7 @@ on the board.
|
||||||
Example how to connect the canbus0-bus (virtual wire) to the host
|
Example how to connect the canbus0-bus (virtual wire) to the host
|
||||||
Linux system (SocketCAN used) and to both CTU CAN FD cores emulated
|
Linux system (SocketCAN used) and to both CTU CAN FD cores emulated
|
||||||
on the corresponding PCI card expects that host system CAN bus
|
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+ \
|
qemu-system-x86_64 -enable-kvm -kernel /boot/vmlinuz-4.19.52+ \
|
||||||
-initrd ramdisk.cpio \
|
-initrd ramdisk.cpio \
|
||||||
|
@ -125,7 +128,7 @@ is setup according to the previous SJA1000 section.
|
||||||
-device ctucan_pci,canbus0=canbus0-bus,canbus1=canbus0-bus \
|
-device ctucan_pci,canbus0=canbus0-bus,canbus1=canbus0-bus \
|
||||||
-nographic
|
-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.ko || modprobe ctucanfd
|
||||||
insmod ctucanfd_pci.ko || modprobe ctucanfd_pci
|
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
|
/bin/ip link set $ifc up
|
||||||
done
|
done
|
||||||
|
|
||||||
The test can run for example
|
The test can run for example::
|
||||||
|
|
||||||
candump can1
|
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
|
cangen can0
|
||||||
|
|
||||||
for CAN FD without bitrate switch
|
for CAN FD without bitrate switch::
|
||||||
|
|
||||||
cangen can0 -f
|
cangen can0 -f
|
||||||
|
|
||||||
and with bitrate switch
|
and with bitrate switch::
|
||||||
|
|
||||||
cangen can0 -b
|
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.
|
in the host one and much more combinations.
|
||||||
|
|
||||||
Links to other resources
|
Links to other resources
|
||||||
========================
|
------------------------
|
||||||
|
|
||||||
(1) CAN related projects at Czech Technical University, Faculty of Electrical Engineering
|
(1) `CAN related projects at Czech Technical University, Faculty of Electrical Engineering <http://canbus.pages.fel.cvut.cz>`_
|
||||||
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>`_
|
||||||
(2) Repository with development can-pci branch at Czech Technical University
|
(3) `RTEMS page describing project <https://devel.rtems.org/wiki/Developer/Simulators/QEMU/CANEmulation>`_
|
||||||
https://gitlab.fel.cvut.cz/canbus/qemu-canbus
|
(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>`_
|
||||||
(3) RTEMS page describing project
|
(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>`_
|
||||||
https://devel.rtems.org/wiki/Developer/Simulators/QEMU/CANEmulation
|
(6) `Linux SocketCAN utilities <https://github.com/linux-can/can-utils>`_
|
||||||
(4) RTLWS 2015 article about the project and its use with CANopen emulation
|
(7) `CTU CAN FD project including core VHDL design, Linux driver, test utilities etc. <https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core>`_
|
||||||
http://cmp.felk.cvut.cz/~pisa/can/doc/rtlws-17-pisa-qemu-can.pdf
|
(8) `CTU CAN FD Core Datasheet Documentation <http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/Progdokum.pdf>`_
|
||||||
(5) GNU/Linux, CAN and CANopen in Real-time Control Applications
|
(9) `CTU CAN FD Core System Architecture Documentation <http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/ctu_can_fd_architecture.pdf>`_
|
||||||
Slides from LinuxDays 2017 (include updated RTLWS 2015 content)
|
(10) `CTU CAN FD Driver Documentation <http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/driver_doc/ctucanfd-driver.html>`_
|
||||||
https://www.linuxdays.cz/2017/video/Pavel_Pisa-CAN_canopen.pdf
|
(11) `Integration with PCIe interfacing for Intel/Altera Cyclone IV based board <https://gitlab.fel.cvut.cz/canbus/pcie-ctu_can_fd>`_
|
||||||
(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
|
|
|
@ -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,
|
void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
|
||||||
const char *oem_id, const char *oem_table_id)
|
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 uid = 0;
|
||||||
int socket;
|
int socket;
|
||||||
AcpiTable table = { .sig = "PPTT", .rev = 2,
|
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);
|
acpi_table_begin(&table, table_data);
|
||||||
|
|
||||||
for (socket = 0; socket < ms->smp.sockets; socket++) {
|
for (socket = 0; socket < ms->smp.sockets; socket++) {
|
||||||
uint32_t socket_offset = table_data->len - pptt_start;
|
g_queue_push_tail(list,
|
||||||
int core;
|
GUINT_TO_POINTER(table_data->len - pptt_start));
|
||||||
|
|
||||||
build_processor_hierarchy_node(
|
build_processor_hierarchy_node(
|
||||||
table_data,
|
table_data,
|
||||||
/*
|
/*
|
||||||
|
@ -2021,35 +2024,64 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
|
||||||
*/
|
*/
|
||||||
(1 << 0),
|
(1 << 0),
|
||||||
0, socket, NULL, 0);
|
0, socket, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
for (core = 0; core < ms->smp.cores; core++) {
|
if (mc->smp_props.clusters_supported) {
|
||||||
uint32_t core_offset = table_data->len - pptt_start;
|
length = g_queue_get_length(list);
|
||||||
int thread;
|
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(
|
build_processor_hierarchy_node(
|
||||||
table_data,
|
table_data,
|
||||||
(0 << 0), /* not a physical package */
|
(0 << 0), /* not a physical package */
|
||||||
socket_offset, core, NULL, 0);
|
parent_offset, cluster, NULL, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (thread = 0; thread < ms->smp.threads; thread++) {
|
length = g_queue_get_length(list);
|
||||||
build_processor_hierarchy_node(
|
for (i = 0; i < length; i++) {
|
||||||
table_data,
|
int core;
|
||||||
(1 << 1) | /* ACPI Processor ID valid */
|
|
||||||
(1 << 2) | /* Processor is a Thread */
|
parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list));
|
||||||
(1 << 3), /* Node is a Leaf */
|
for (core = 0; core < ms->smp.cores; core++) {
|
||||||
core_offset, uid++, NULL, 0);
|
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 {
|
} else {
|
||||||
build_processor_hierarchy_node(
|
build_processor_hierarchy_node(
|
||||||
table_data,
|
table_data,
|
||||||
(1 << 1) | /* ACPI Processor ID valid */
|
(1 << 1) | /* ACPI Processor ID valid */
|
||||||
(1 << 3), /* Node is a Leaf */
|
(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);
|
acpi_table_end(linker, &table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ config ARM_VIRT
|
||||||
select ACPI_HW_REDUCED
|
select ACPI_HW_REDUCED
|
||||||
select ACPI_APEI
|
select ACPI_APEI
|
||||||
select ACPI_VIOT
|
select ACPI_VIOT
|
||||||
|
select VIRTIO_MEM_SUPPORTED
|
||||||
|
|
||||||
config CHEETAH
|
config CHEETAH
|
||||||
bool
|
bool
|
||||||
|
@ -94,6 +95,9 @@ config MUSCA
|
||||||
select SPLIT_IRQ
|
select SPLIT_IRQ
|
||||||
select UNIMP
|
select UNIMP
|
||||||
|
|
||||||
|
config MARVELL_88W8618
|
||||||
|
bool
|
||||||
|
|
||||||
config MUSICPAL
|
config MUSICPAL
|
||||||
bool
|
bool
|
||||||
select OR_IRQ
|
select OR_IRQ
|
||||||
|
|
|
@ -61,6 +61,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = {
|
||||||
[ASPEED_DEV_UART1] = 0x1E783000,
|
[ASPEED_DEV_UART1] = 0x1E783000,
|
||||||
[ASPEED_DEV_UART5] = 0x1E784000,
|
[ASPEED_DEV_UART5] = 0x1E784000,
|
||||||
[ASPEED_DEV_VUART] = 0x1E787000,
|
[ASPEED_DEV_VUART] = 0x1E787000,
|
||||||
|
[ASPEED_DEV_I3C] = 0x1E7A0000,
|
||||||
[ASPEED_DEV_SDRAM] = 0x80000000,
|
[ASPEED_DEV_SDRAM] = 0x80000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -108,6 +109,7 @@ static const int aspeed_soc_ast2600_irqmap[] = {
|
||||||
[ASPEED_DEV_ETH4] = 33,
|
[ASPEED_DEV_ETH4] = 33,
|
||||||
[ASPEED_DEV_KCS] = 138, /* 138 -> 142 */
|
[ASPEED_DEV_KCS] = 138, /* 138 -> 142 */
|
||||||
[ASPEED_DEV_DP] = 62,
|
[ASPEED_DEV_DP] = 62,
|
||||||
|
[ASPEED_DEV_I3C] = 102, /* 102 -> 107 */
|
||||||
};
|
};
|
||||||
|
|
||||||
static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl)
|
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);
|
snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname);
|
||||||
object_initialize_child(obj, "hace", &s->hace, typename);
|
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_mmio_map(SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]);
|
||||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0,
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0,
|
||||||
aspeed_soc_get_irq(s, ASPEED_DEV_HACE));
|
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)
|
static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data)
|
||||||
|
|
|
@ -34,12 +34,12 @@
|
||||||
#include "ui/pixel_ops.h"
|
#include "ui/pixel_ops.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
|
#include "hw/net/mv88w8618_eth.h"
|
||||||
|
|
||||||
#define MP_MISC_BASE 0x80002000
|
#define MP_MISC_BASE 0x80002000
|
||||||
#define MP_MISC_SIZE 0x00001000
|
#define MP_MISC_SIZE 0x00001000
|
||||||
|
|
||||||
#define MP_ETH_BASE 0x80008000
|
#define MP_ETH_BASE 0x80008000
|
||||||
#define MP_ETH_SIZE 0x00001000
|
|
||||||
|
|
||||||
#define MP_WLAN_BASE 0x8000C000
|
#define MP_WLAN_BASE 0x8000C000
|
||||||
#define MP_WLAN_SIZE 0x00000800
|
#define MP_WLAN_SIZE 0x00000800
|
||||||
|
@ -84,384 +84,6 @@
|
||||||
/* Wolfson 8750 I2C address */
|
/* Wolfson 8750 I2C address */
|
||||||
#define MP_WM_ADDR 0x1A
|
#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 */
|
/* LCD register offsets */
|
||||||
#define MP_LCD_IRQCTRL 0x180
|
#define MP_LCD_IRQCTRL 0x180
|
||||||
#define MP_LCD_IRQSTAT 0x184
|
#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_pic_info);
|
||||||
type_register_static(&mv88w8618_pit_info);
|
type_register_static(&mv88w8618_pit_info);
|
||||||
type_register_static(&mv88w8618_flashcfg_info);
|
type_register_static(&mv88w8618_flashcfg_info);
|
||||||
type_register_static(&mv88w8618_eth_info);
|
|
||||||
type_register_static(&mv88w8618_wlan_info);
|
type_register_static(&mv88w8618_wlan_info);
|
||||||
type_register_static(&musicpal_lcd_info);
|
type_register_static(&musicpal_lcd_info);
|
||||||
type_register_static(&musicpal_gpio_info);
|
type_register_static(&musicpal_gpio_info);
|
||||||
|
|
|
@ -332,7 +332,15 @@ static void kudo_bmc_i2c_init(NPCM7xxState *soc)
|
||||||
{
|
{
|
||||||
I2CSlave *i2c_mux;
|
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, 1), TYPE_PCA9548, 0x77);
|
||||||
|
|
||||||
i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 4), TYPE_PCA9548, 0x77);
|
i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 4), TYPE_PCA9548, 0x77);
|
||||||
|
|
|
@ -158,10 +158,9 @@ static void acpi_dsdt_add_virtio(Aml *scope,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
|
static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
|
||||||
uint32_t irq, bool use_highmem, bool highmem_ecam,
|
uint32_t irq, VirtMachineState *vms)
|
||||||
VirtMachineState *vms)
|
|
||||||
{
|
{
|
||||||
int ecam_id = VIRT_ECAM_ID(highmem_ecam);
|
int ecam_id = VIRT_ECAM_ID(vms->highmem_ecam);
|
||||||
struct GPEXConfig cfg = {
|
struct GPEXConfig cfg = {
|
||||||
.mmio32 = memmap[VIRT_PCIE_MMIO],
|
.mmio32 = memmap[VIRT_PCIE_MMIO],
|
||||||
.pio = memmap[VIRT_PCIE_PIO],
|
.pio = memmap[VIRT_PCIE_PIO],
|
||||||
|
@ -170,7 +169,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
|
||||||
.bus = vms->bus,
|
.bus = vms->bus,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (use_highmem) {
|
if (vms->highmem_mmio) {
|
||||||
cfg.mmio64 = memmap[VIRT_HIGH_PCIE_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_fw_cfg(scope, &memmap[VIRT_FW_CFG]);
|
||||||
acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
|
acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
|
||||||
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
|
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
|
||||||
acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
|
acpi_dsdt_add_pci(scope, memmap, irqmap[VIRT_PCIE] + ARM_SPI_BASE, vms);
|
||||||
vms->highmem, vms->highmem_ecam, vms);
|
|
||||||
if (vms->acpi_dev) {
|
if (vms->acpi_dev) {
|
||||||
build_ged_aml(scope, "\\_SB."GED_DEVICE,
|
build_ged_aml(scope, "\\_SB."GED_DEVICE,
|
||||||
HOTPLUG_HANDLER(vms->acpi_dev),
|
HOTPLUG_HANDLER(vms->acpi_dev),
|
||||||
|
|
184
hw/arm/virt.c
184
hw/arm/virt.c
|
@ -71,9 +71,11 @@
|
||||||
#include "hw/arm/smmuv3.h"
|
#include "hw/arm/smmuv3.h"
|
||||||
#include "hw/acpi/acpi.h"
|
#include "hw/acpi/acpi.h"
|
||||||
#include "target/arm/internals.h"
|
#include "target/arm/internals.h"
|
||||||
|
#include "hw/mem/memory-device.h"
|
||||||
#include "hw/mem/pc-dimm.h"
|
#include "hw/mem/pc-dimm.h"
|
||||||
#include "hw/mem/nvdimm.h"
|
#include "hw/mem/nvdimm.h"
|
||||||
#include "hw/acpi/generic_event_device.h"
|
#include "hw/acpi/generic_event_device.h"
|
||||||
|
#include "hw/virtio/virtio-mem-pci.h"
|
||||||
#include "hw/virtio/virtio-iommu.h"
|
#include "hw/virtio/virtio-iommu.h"
|
||||||
#include "hw/char/pl011.h"
|
#include "hw/char/pl011.h"
|
||||||
#include "qemu/guest-random.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
|
* can contain several layers of clustering within a single physical
|
||||||
* package and cluster nodes can be contained in parent cluster nodes.
|
* package and cluster nodes can be contained in parent cluster nodes.
|
||||||
*
|
*
|
||||||
* Given that cluster is not yet supported in the vCPU topology,
|
* Note: currently we only support one layer of clustering within
|
||||||
* we currently generate one cluster node within each socket node
|
* each physical package.
|
||||||
* by default.
|
|
||||||
*/
|
*/
|
||||||
qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map");
|
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) {
|
if (ms->smp.threads > 1) {
|
||||||
map_path = g_strdup_printf(
|
map_path = g_strdup_printf(
|
||||||
"/cpus/cpu-map/socket%d/cluster0/core%d/thread%d",
|
"/cpus/cpu-map/socket%d/cluster%d/core%d/thread%d",
|
||||||
cpu / (ms->smp.cores * ms->smp.threads),
|
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) % ms->smp.cores,
|
||||||
cpu % ms->smp.threads);
|
cpu % ms->smp.threads);
|
||||||
} else {
|
} else {
|
||||||
map_path = g_strdup_printf(
|
map_path = g_strdup_printf(
|
||||||
"/cpus/cpu-map/socket%d/cluster0/core%d",
|
"/cpus/cpu-map/socket%d/cluster%d/core%d",
|
||||||
cpu / ms->smp.cores,
|
cpu / (ms->smp.clusters * ms->smp.cores),
|
||||||
|
(cpu / ms->smp.cores) % ms->smp.clusters,
|
||||||
cpu % ms->smp.cores);
|
cpu % ms->smp.cores);
|
||||||
}
|
}
|
||||||
qemu_fdt_add_path(ms->fdt, map_path);
|
qemu_fdt_add_path(ms->fdt, map_path);
|
||||||
|
@ -1416,7 +1419,7 @@ static void create_pcie(VirtMachineState *vms)
|
||||||
mmio_reg, base_mmio, size_mmio);
|
mmio_reg, base_mmio, size_mmio);
|
||||||
memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias);
|
memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias);
|
||||||
|
|
||||||
if (vms->highmem) {
|
if (vms->highmem_mmio) {
|
||||||
/* Map high MMIO space */
|
/* Map high MMIO space */
|
||||||
MemoryRegion *high_mmio_alias = g_new0(MemoryRegion, 1);
|
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",
|
qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
|
||||||
2, base_ecam, 2, size_ecam);
|
2, base_ecam, 2, size_ecam);
|
||||||
|
|
||||||
if (vms->highmem) {
|
if (vms->highmem_mmio) {
|
||||||
qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges",
|
qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges",
|
||||||
1, FDT_PCI_RANGE_IOPORT, 2, 0,
|
1, FDT_PCI_RANGE_IOPORT, 2, 0,
|
||||||
2, base_pio, 2, size_pio,
|
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);
|
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);
|
MachineState *ms = MACHINE(vms);
|
||||||
hwaddr base, device_memory_base, device_memory_size;
|
hwaddr base, device_memory_base, device_memory_size, memtop;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
vms->memmap = extended_memmap;
|
vms->memmap = extended_memmap;
|
||||||
|
@ -1682,6 +1685,14 @@ static void virt_set_memmap(VirtMachineState *vms)
|
||||||
exit(EXIT_FAILURE);
|
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
|
* We compute the base of the high IO region depending on the
|
||||||
* amount of initial and device memory. The device memory start/size
|
* 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;
|
device_memory_size = ms->maxram_size - ms->ram_size + ms->ram_slots * GiB;
|
||||||
|
|
||||||
/* Base address of the high IO region */
|
/* 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) {
|
if (base < device_memory_base) {
|
||||||
error_report("maxmem/slots too huge");
|
error_report("maxmem/slots too huge");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
|
@ -1703,15 +1719,43 @@ static void virt_set_memmap(VirtMachineState *vms)
|
||||||
base = vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES;
|
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++) {
|
for (i = VIRT_LOWMEMMAP_LAST; i < ARRAY_SIZE(extended_memmap); i++) {
|
||||||
hwaddr size = extended_memmap[i].size;
|
hwaddr size = extended_memmap[i].size;
|
||||||
|
bool fits;
|
||||||
|
|
||||||
base = ROUND_UP(base, size);
|
base = ROUND_UP(base, size);
|
||||||
vms->memmap[i].base = base;
|
vms->memmap[i].base = base;
|
||||||
vms->memmap[i].size = size;
|
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;
|
base += size;
|
||||||
}
|
}
|
||||||
vms->highest_gpa = base - 1;
|
|
||||||
if (device_memory_size > 0) {
|
if (device_memory_size > 0) {
|
||||||
ms->device_memory = g_malloc0(sizeof(*ms->device_memory));
|
ms->device_memory = g_malloc0(sizeof(*ms->device_memory));
|
||||||
ms->device_memory->base = device_memory_base;
|
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 smp_cpus = machine->smp.cpus;
|
||||||
unsigned int max_cpus = machine->smp.max_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()
|
* In accelerated mode, the memory map is computed earlier in kvm_type()
|
||||||
* to create a VM with the right number of IPA bits.
|
* to create a VM with the right number of IPA bits.
|
||||||
*/
|
*/
|
||||||
if (!vms->memmap) {
|
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
|
/* We can probe only here because during property set
|
||||||
|
@ -1915,11 +1990,6 @@ static void machvirt_init(MachineState *machine)
|
||||||
*/
|
*/
|
||||||
finalize_gic_version(vms);
|
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) {
|
if (vms->secure) {
|
||||||
/*
|
/*
|
||||||
* The Secure view of the world is the same as the NonSecure,
|
* 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);
|
create_fdt(vms);
|
||||||
|
|
||||||
possible_cpus = mc->possible_cpu_arch_ids(machine);
|
|
||||||
assert(possible_cpus->len == max_cpus);
|
assert(possible_cpus->len == max_cpus);
|
||||||
for (n = 0; n < possible_cpus->len; n++) {
|
for (n = 0; n < possible_cpus->len; n++) {
|
||||||
Object *cpuobj;
|
Object *cpuobj;
|
||||||
|
@ -2127,7 +2196,7 @@ static void machvirt_init(MachineState *machine)
|
||||||
machine->ram_size, "mach-virt.tag");
|
machine->ram_size, "mach-virt.tag");
|
||||||
}
|
}
|
||||||
|
|
||||||
vms->highmem_ecam &= vms->highmem && (!firmware_loaded || aarch64);
|
vms->highmem_ecam &= (!firmware_loaded || aarch64);
|
||||||
|
|
||||||
create_rtc(vms);
|
create_rtc(vms);
|
||||||
|
|
||||||
|
@ -2500,6 +2569,64 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev,
|
||||||
dev, &error_abort);
|
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,
|
static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
|
||||||
DeviceState *dev, Error **errp)
|
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)) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
||||||
virt_memory_pre_plug(hotplug_dev, dev, errp);
|
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)) {
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
|
||||||
hwaddr db_start = 0, db_end = 0;
|
hwaddr db_start = 0, db_end = 0;
|
||||||
char *resv_prop_str;
|
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)) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
||||||
virt_memory_plug(hotplug_dev, dev, errp);
|
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)) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
|
||||||
PCIDevice *pdev = PCI_DEVICE(dev);
|
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)) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
||||||
virt_dimm_unplug_request(hotplug_dev, dev, errp);
|
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 {
|
} else {
|
||||||
error_setg(errp, "device unplug request for unsupported device"
|
error_setg(errp, "device unplug request for unsupported device"
|
||||||
" type: %s", object_get_typename(OBJECT(dev)));
|
" 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) ||
|
if (device_is_dynamic_sysbus(mc, dev) ||
|
||||||
object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
|
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)) {
|
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
|
||||||
return HOTPLUG_HANDLER(machine);
|
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);
|
max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms, &fixed_ipa);
|
||||||
|
|
||||||
/* we freeze the memory map to compute the highest gpa */
|
/* 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);
|
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_request = virt_machine_device_unplug_request_cb;
|
||||||
hc->unplug = virt_machine_device_unplug_cb;
|
hc->unplug = virt_machine_device_unplug_cb;
|
||||||
mc->nvdimm_supported = true;
|
mc->nvdimm_supported = true;
|
||||||
|
mc->smp_props.clusters_supported = true;
|
||||||
mc->auto_enable_numa_with_memhp = true;
|
mc->auto_enable_numa_with_memhp = true;
|
||||||
mc->auto_enable_numa_with_memdev = true;
|
mc->auto_enable_numa_with_memdev = true;
|
||||||
mc->default_ram_id = "mach-virt.ram";
|
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->gic_version = VIRT_GIC_VERSION_NOSEL;
|
||||||
|
|
||||||
vms->highmem_ecam = !vmc->no_highmem_ecam;
|
vms->highmem_ecam = !vmc->no_highmem_ecam;
|
||||||
|
vms->highmem_mmio = true;
|
||||||
|
vms->highmem_redists = true;
|
||||||
|
|
||||||
if (vmc->no_its) {
|
if (vmc->no_its) {
|
||||||
vms->its = false;
|
vms->its = false;
|
||||||
|
|
|
@ -47,6 +47,3 @@ config PL041
|
||||||
|
|
||||||
config CS4231
|
config CS4231
|
||||||
bool
|
bool
|
||||||
|
|
||||||
config MARVELL_88W8618
|
|
||||||
bool
|
|
||||||
|
|
|
@ -1662,6 +1662,15 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
"gic_cpu_read: Bad offset %x\n", (int)offset);
|
"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 {
|
} else {
|
||||||
s->apr[regno][cpu] = value;
|
s->apr[regno][cpu] = value;
|
||||||
}
|
}
|
||||||
|
s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0xe0: case 0xe4: case 0xe8: case 0xec:
|
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;
|
return MEMTX_OK;
|
||||||
}
|
}
|
||||||
s->nsapr[regno][cpu] = value;
|
s->nsapr[regno][cpu] = value;
|
||||||
|
s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x1000:
|
case 0x1000:
|
||||||
|
|
|
@ -45,6 +45,23 @@ typedef struct {
|
||||||
uint64_t itel;
|
uint64_t itel;
|
||||||
} IteEntry;
|
} 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)
|
static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
|
||||||
{
|
{
|
||||||
uint64_t result = 0;
|
uint64_t result = 0;
|
||||||
|
@ -66,44 +83,62 @@ static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
|
||||||
return result;
|
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,
|
static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t *cte,
|
||||||
MemTxResult *res)
|
MemTxResult *res)
|
||||||
{
|
{
|
||||||
AddressSpace *as = &s->gicv3->dma_as;
|
AddressSpace *as = &s->gicv3->dma_as;
|
||||||
uint64_t l2t_addr;
|
uint64_t entry_addr = table_entry_addr(s, &s->ct, icid, res);
|
||||||
uint64_t value;
|
|
||||||
bool valid_l2t;
|
|
||||||
uint32_t l2t_id;
|
|
||||||
uint32_t num_l2_entries;
|
|
||||||
|
|
||||||
if (s->ct.indirect) {
|
if (entry_addr == -1) {
|
||||||
l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
|
return false; /* not valid */
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*cte = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, res);
|
||||||
return FIELD_EX64(*cte, CTE, VALID);
|
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)
|
static uint64_t get_dte(GICv3ITSState *s, uint32_t devid, MemTxResult *res)
|
||||||
{
|
{
|
||||||
AddressSpace *as = &s->gicv3->dma_as;
|
AddressSpace *as = &s->gicv3->dma_as;
|
||||||
uint64_t l2t_addr;
|
uint64_t entry_addr = table_entry_addr(s, &s->dt, devid, res);
|
||||||
uint64_t value;
|
|
||||||
bool valid_l2t;
|
|
||||||
uint32_t l2t_id;
|
|
||||||
uint32_t num_l2_entries;
|
|
||||||
|
|
||||||
if (s->dt.indirect) {
|
if (entry_addr == -1) {
|
||||||
l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
|
return 0; /* a DTE entry with the Valid bit clear */
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
return address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, res);
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -217,21 +223,20 @@ static uint64_t get_dte(GICv3ITSState *s, uint32_t devid, MemTxResult *res)
|
||||||
* 3. handling of ITS CLEAR command
|
* 3. handling of ITS CLEAR command
|
||||||
* 4. handling of ITS DISCARD command
|
* 4. handling of ITS DISCARD command
|
||||||
*/
|
*/
|
||||||
static bool process_its_cmd(GICv3ITSState *s, uint64_t value, uint32_t offset,
|
static ItsCmdResult process_its_cmd(GICv3ITSState *s, uint64_t value,
|
||||||
ItsCmdType cmd)
|
uint32_t offset, ItsCmdType cmd)
|
||||||
{
|
{
|
||||||
AddressSpace *as = &s->gicv3->dma_as;
|
AddressSpace *as = &s->gicv3->dma_as;
|
||||||
uint32_t devid, eventid;
|
uint32_t devid, eventid;
|
||||||
MemTxResult res = MEMTX_OK;
|
MemTxResult res = MEMTX_OK;
|
||||||
bool dte_valid;
|
bool dte_valid;
|
||||||
uint64_t dte = 0;
|
uint64_t dte = 0;
|
||||||
uint32_t max_eventid;
|
uint64_t num_eventids;
|
||||||
uint16_t icid = 0;
|
uint16_t icid = 0;
|
||||||
uint32_t pIntid = 0;
|
uint32_t pIntid = 0;
|
||||||
bool ite_valid = false;
|
bool ite_valid = false;
|
||||||
uint64_t cte = 0;
|
uint64_t cte = 0;
|
||||||
bool cte_valid = false;
|
bool cte_valid = false;
|
||||||
bool result = false;
|
|
||||||
uint64_t rdbase;
|
uint64_t rdbase;
|
||||||
|
|
||||||
if (cmd == NONE) {
|
if (cmd == NONE) {
|
||||||
|
@ -245,103 +250,111 @@ static bool process_its_cmd(GICv3ITSState *s, uint64_t value, uint32_t offset,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res != MEMTX_OK) {
|
if (res != MEMTX_OK) {
|
||||||
return result;
|
return CMD_STALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
eventid = (value & EVENTID_MASK);
|
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) {
|
if (devid >= s->dt.num_ids) {
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
"%s: invalid command attributes: devid %d>=%d",
|
"%s: invalid command attributes: devid %d>=%d",
|
||||||
__func__, devid, s->dt.num_ids);
|
__func__, devid, s->dt.num_ids);
|
||||||
|
return CMD_CONTINUE;
|
||||||
} 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 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,
|
static ItsCmdResult process_mapti(GICv3ITSState *s, uint64_t value,
|
||||||
bool ignore_pInt)
|
uint32_t offset, bool ignore_pInt)
|
||||||
{
|
{
|
||||||
AddressSpace *as = &s->gicv3->dma_as;
|
AddressSpace *as = &s->gicv3->dma_as;
|
||||||
uint32_t devid, eventid;
|
uint32_t devid, eventid;
|
||||||
uint32_t pIntid = 0;
|
uint32_t pIntid = 0;
|
||||||
uint32_t max_eventid, max_Intid;
|
uint64_t num_eventids;
|
||||||
|
uint32_t num_intids;
|
||||||
bool dte_valid;
|
bool dte_valid;
|
||||||
MemTxResult res = MEMTX_OK;
|
MemTxResult res = MEMTX_OK;
|
||||||
uint16_t icid = 0;
|
uint16_t icid = 0;
|
||||||
uint64_t dte = 0;
|
uint64_t dte = 0;
|
||||||
bool result = false;
|
IteEntry ite = {};
|
||||||
|
|
||||||
devid = ((value & DEVID_MASK) >> DEVID_SHIFT);
|
devid = ((value & DEVID_MASK) >> DEVID_SHIFT);
|
||||||
offset += NUM_BYTES_IN_DW;
|
offset += NUM_BYTES_IN_DW;
|
||||||
|
@ -349,7 +362,7 @@ static bool process_mapti(GICv3ITSState *s, uint64_t value, uint32_t offset,
|
||||||
MEMTXATTRS_UNSPECIFIED, &res);
|
MEMTXATTRS_UNSPECIFIED, &res);
|
||||||
|
|
||||||
if (res != MEMTX_OK) {
|
if (res != MEMTX_OK) {
|
||||||
return result;
|
return CMD_STALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
eventid = (value & EVENTID_MASK);
|
eventid = (value & EVENTID_MASK);
|
||||||
|
@ -365,58 +378,59 @@ static bool process_mapti(GICv3ITSState *s, uint64_t value, uint32_t offset,
|
||||||
MEMTXATTRS_UNSPECIFIED, &res);
|
MEMTXATTRS_UNSPECIFIED, &res);
|
||||||
|
|
||||||
if (res != MEMTX_OK) {
|
if (res != MEMTX_OK) {
|
||||||
return result;
|
return CMD_STALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
icid = value & ICID_MASK;
|
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);
|
dte = get_dte(s, devid, &res);
|
||||||
|
|
||||||
if (res != MEMTX_OK) {
|
if (res != MEMTX_OK) {
|
||||||
return result;
|
return CMD_STALL;
|
||||||
}
|
}
|
||||||
dte_valid = FIELD_EX64(dte, DTE, VALID);
|
dte_valid = FIELD_EX64(dte, DTE, VALID);
|
||||||
max_eventid = 1UL << (FIELD_EX64(dte, DTE, SIZE) + 1);
|
num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1);
|
||||||
max_Intid = (1ULL << (GICD_TYPER_IDBITS + 1)) - 1;
|
num_intids = 1ULL << (GICD_TYPER_IDBITS + 1);
|
||||||
|
|
||||||
if ((devid >= s->dt.num_ids) || (icid >= s->ct.num_ids)
|
if ((icid >= s->ct.num_ids)
|
||||||
|| !dte_valid || (eventid > max_eventid) ||
|
|| !dte_valid || (eventid >= num_eventids) ||
|
||||||
(((pIntid < GICV3_LPI_INTID_START) || (pIntid > max_Intid)) &&
|
(((pIntid < GICV3_LPI_INTID_START) || (pIntid >= num_intids)) &&
|
||||||
(pIntid != INTID_SPURIOUS))) {
|
(pIntid != INTID_SPURIOUS))) {
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
"%s: invalid command attributes "
|
"%s: invalid command attributes "
|
||||||
"devid %d or icid %d or eventid %d or pIntid %d or"
|
"icid %d or eventid %d or pIntid %d or"
|
||||||
"unmapped dte %d\n", __func__, devid, icid, eventid,
|
"unmapped dte %d\n", __func__, icid, eventid,
|
||||||
pIntid, dte_valid);
|
pIntid, dte_valid);
|
||||||
/*
|
/*
|
||||||
* in this implementation, in case of error
|
* in this implementation, in case of error
|
||||||
* we ignore this command and move onto the next
|
* we ignore this command and move onto the next
|
||||||
* command in the queue
|
* command in the queue
|
||||||
*/
|
*/
|
||||||
} else {
|
return CMD_CONTINUE;
|
||||||
/* 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 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,
|
static bool update_cte(GICv3ITSState *s, uint16_t icid, bool valid,
|
||||||
uint64_t rdbase)
|
uint64_t rdbase)
|
||||||
{
|
{
|
||||||
AddressSpace *as = &s->gicv3->dma_as;
|
AddressSpace *as = &s->gicv3->dma_as;
|
||||||
uint64_t value;
|
uint64_t entry_addr;
|
||||||
uint64_t l2t_addr;
|
|
||||||
bool valid_l2t;
|
|
||||||
uint32_t l2t_id;
|
|
||||||
uint32_t num_l2_entries;
|
|
||||||
uint64_t cte = 0;
|
uint64_t cte = 0;
|
||||||
MemTxResult res = MEMTX_OK;
|
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);
|
cte = FIELD_DP64(cte, CTE, RDBASE, rdbase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
entry_addr = table_entry_addr(s, &s->ct, icid, &res);
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
if (res != MEMTX_OK) {
|
if (res != MEMTX_OK) {
|
||||||
|
/* memory access error: stall */
|
||||||
return false;
|
return false;
|
||||||
} else {
|
}
|
||||||
|
if (entry_addr == -1) {
|
||||||
|
/* No L2 table for this index: discard write and continue */
|
||||||
return true;
|
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;
|
AddressSpace *as = &s->gicv3->dma_as;
|
||||||
uint16_t icid;
|
uint16_t icid;
|
||||||
uint64_t rdbase;
|
uint64_t rdbase;
|
||||||
bool valid;
|
bool valid;
|
||||||
MemTxResult res = MEMTX_OK;
|
MemTxResult res = MEMTX_OK;
|
||||||
bool result = false;
|
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
|
|
||||||
offset += NUM_BYTES_IN_DW;
|
offset += NUM_BYTES_IN_DW;
|
||||||
|
@ -487,7 +474,7 @@ static bool process_mapc(GICv3ITSState *s, uint32_t offset)
|
||||||
MEMTXATTRS_UNSPECIFIED, &res);
|
MEMTXATTRS_UNSPECIFIED, &res);
|
||||||
|
|
||||||
if (res != MEMTX_OK) {
|
if (res != MEMTX_OK) {
|
||||||
return result;
|
return CMD_STALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
icid = value & ICID_MASK;
|
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
|
* we ignore this command and move onto the next
|
||||||
* command in the queue
|
* command in the queue
|
||||||
*/
|
*/
|
||||||
} else {
|
return CMD_CONTINUE;
|
||||||
result = update_cte(s, icid, valid, rdbase);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return update_cte(s, icid, valid, rdbase) ? CMD_CONTINUE : CMD_STALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool update_dte(GICv3ITSState *s, uint32_t devid, bool valid,
|
static bool update_dte(GICv3ITSState *s, uint32_t devid, bool valid,
|
||||||
uint8_t size, uint64_t itt_addr)
|
uint8_t size, uint64_t itt_addr)
|
||||||
{
|
{
|
||||||
AddressSpace *as = &s->gicv3->dma_as;
|
AddressSpace *as = &s->gicv3->dma_as;
|
||||||
uint64_t value;
|
uint64_t entry_addr;
|
||||||
uint64_t l2t_addr;
|
|
||||||
bool valid_l2t;
|
|
||||||
uint32_t l2t_id;
|
|
||||||
uint32_t num_l2_entries;
|
|
||||||
uint64_t dte = 0;
|
uint64_t dte = 0;
|
||||||
MemTxResult res = MEMTX_OK;
|
MemTxResult res = MEMTX_OK;
|
||||||
|
|
||||||
|
@ -536,47 +518,21 @@ static bool update_dte(GICv3ITSState *s, uint32_t devid, bool valid,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
entry_addr = table_entry_addr(s, &s->dt, devid, &res);
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
if (res != MEMTX_OK) {
|
if (res != MEMTX_OK) {
|
||||||
|
/* memory access error: stall */
|
||||||
return false;
|
return false;
|
||||||
} else {
|
}
|
||||||
|
if (entry_addr == -1) {
|
||||||
|
/* No L2 table for this index: discard write and continue */
|
||||||
return true;
|
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;
|
AddressSpace *as = &s->gicv3->dma_as;
|
||||||
uint32_t devid;
|
uint32_t devid;
|
||||||
|
@ -584,7 +540,6 @@ static bool process_mapd(GICv3ITSState *s, uint64_t value, uint32_t offset)
|
||||||
uint64_t itt_addr;
|
uint64_t itt_addr;
|
||||||
bool valid;
|
bool valid;
|
||||||
MemTxResult res = MEMTX_OK;
|
MemTxResult res = MEMTX_OK;
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
devid = ((value & DEVID_MASK) >> DEVID_SHIFT);
|
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);
|
MEMTXATTRS_UNSPECIFIED, &res);
|
||||||
|
|
||||||
if (res != MEMTX_OK) {
|
if (res != MEMTX_OK) {
|
||||||
return result;
|
return CMD_STALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size = (value & SIZE_MASK);
|
size = (value & SIZE_MASK);
|
||||||
|
@ -603,7 +558,7 @@ static bool process_mapd(GICv3ITSState *s, uint64_t value, uint32_t offset)
|
||||||
MEMTXATTRS_UNSPECIFIED, &res);
|
MEMTXATTRS_UNSPECIFIED, &res);
|
||||||
|
|
||||||
if (res != MEMTX_OK) {
|
if (res != MEMTX_OK) {
|
||||||
return result;
|
return CMD_STALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
itt_addr = (value & ITTADDR_MASK) >> ITTADDR_SHIFT;
|
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
|
* we ignore this command and move onto the next
|
||||||
* command in the queue
|
* command in the queue
|
||||||
*/
|
*/
|
||||||
} else {
|
return CMD_CONTINUE;
|
||||||
result = update_dte(s, devid, valid, size, itt_addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
uint64_t data;
|
||||||
AddressSpace *as = &s->gicv3->dma_as;
|
AddressSpace *as = &s->gicv3->dma_as;
|
||||||
MemTxResult res = MEMTX_OK;
|
MemTxResult res = MEMTX_OK;
|
||||||
bool result = true;
|
|
||||||
uint8_t cmd;
|
uint8_t cmd;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -666,20 +619,27 @@ static void process_cmdq(GICv3ITSState *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
while (wr_offset != rd_offset) {
|
while (wr_offset != rd_offset) {
|
||||||
|
ItsCmdResult result = CMD_CONTINUE;
|
||||||
|
|
||||||
cq_offset = (rd_offset * GITS_CMDQ_ENTRY_SIZE);
|
cq_offset = (rd_offset * GITS_CMDQ_ENTRY_SIZE);
|
||||||
data = address_space_ldq_le(as, s->cq.base_addr + cq_offset,
|
data = address_space_ldq_le(as, s->cq.base_addr + cq_offset,
|
||||||
MEMTXATTRS_UNSPECIFIED, &res);
|
MEMTXATTRS_UNSPECIFIED, &res);
|
||||||
if (res != MEMTX_OK) {
|
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);
|
cmd = (data & CMD_MASK);
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case GITS_CMD_INT:
|
case GITS_CMD_INT:
|
||||||
res = process_its_cmd(s, data, cq_offset, INTERRUPT);
|
result = process_its_cmd(s, data, cq_offset, INTERRUPT);
|
||||||
break;
|
break;
|
||||||
case GITS_CMD_CLEAR:
|
case GITS_CMD_CLEAR:
|
||||||
res = process_its_cmd(s, data, cq_offset, CLEAR);
|
result = process_its_cmd(s, data, cq_offset, CLEAR);
|
||||||
break;
|
break;
|
||||||
case GITS_CMD_SYNC:
|
case GITS_CMD_SYNC:
|
||||||
/*
|
/*
|
||||||
|
@ -719,18 +679,16 @@ static void process_cmdq(GICv3ITSState *s)
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (result) {
|
if (result == CMD_CONTINUE) {
|
||||||
rd_offset++;
|
rd_offset++;
|
||||||
rd_offset %= s->cq.num_entries;
|
rd_offset %= s->cq.num_entries;
|
||||||
s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, OFFSET, rd_offset);
|
s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, OFFSET, rd_offset);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/* CMD_STALL */
|
||||||
* in this implementation, in case of dma read/write error
|
|
||||||
* we stall the command processing
|
|
||||||
*/
|
|
||||||
s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, STALLED, 1);
|
s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, STALLED, 1);
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -462,7 +462,7 @@ MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r == MEMTX_ERROR) {
|
if (r != MEMTX_OK) {
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
"%s: invalid guest read at offset " TARGET_FMT_plx
|
"%s: invalid guest read at offset " TARGET_FMT_plx
|
||||||
" size %u\n", __func__, offset, size);
|
" size %u\n", __func__, offset, size);
|
||||||
|
@ -521,7 +521,7 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r == MEMTX_ERROR) {
|
if (r != MEMTX_OK) {
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
"%s: invalid guest write at offset " TARGET_FMT_plx
|
"%s: invalid guest write at offset " TARGET_FMT_plx
|
||||||
" size %u\n", __func__, offset, size);
|
" size %u\n", __func__, offset, size);
|
||||||
|
|
|
@ -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);
|
|
@ -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_AUX', if_true: files('auxbus.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
|
softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
|
||||||
'aspeed_hace.c',
|
'aspeed_hace.c',
|
||||||
|
'aspeed_i3c.c',
|
||||||
'aspeed_lpc.c',
|
'aspeed_lpc.c',
|
||||||
'aspeed_scu.c',
|
'aspeed_scu.c',
|
||||||
'aspeed_sdmc.c',
|
'aspeed_sdmc.c',
|
||||||
|
|
|
@ -199,6 +199,12 @@ armsse_mhu_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU wri
|
||||||
# aspeed_xdma.c
|
# aspeed_xdma.c
|
||||||
aspeed_xdma_write(uint64_t offset, uint64_t data) "XDMA write: offset 0x%" PRIx64 " data 0x%" PRIx64
|
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_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"
|
bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu"
|
||||||
|
|
||||||
|
|
|
@ -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_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_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_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_CADENCE', if_true: files('cadence_gem.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_STELLARIS_ENET', if_true: files('stellaris_enet.c'))
|
softmmu_ss.add(when: 'CONFIG_STELLARIS_ENET', if_true: files('stellaris_enet.c'))
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -46,14 +46,25 @@
|
||||||
*/
|
*/
|
||||||
#define VIRTIO_MEM_MIN_BLOCK_SIZE ((uint32_t)(1 * MiB))
|
#define VIRTIO_MEM_MIN_BLOCK_SIZE ((uint32_t)(1 * MiB))
|
||||||
|
|
||||||
#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \
|
static uint32_t virtio_mem_default_thp_size(void)
|
||||||
defined(__powerpc64__)
|
{
|
||||||
#define VIRTIO_MEM_DEFAULT_THP_SIZE ((uint32_t)(2 * MiB))
|
uint32_t default_thp_size = VIRTIO_MEM_MIN_BLOCK_SIZE;
|
||||||
#else
|
|
||||||
/* fallback to 1 MiB (e.g., the THP size on s390x) */
|
#if defined(__x86_64__) || defined(__arm__) || defined(__powerpc64__)
|
||||||
#define VIRTIO_MEM_DEFAULT_THP_SIZE VIRTIO_MEM_MIN_BLOCK_SIZE
|
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
|
#endif
|
||||||
|
|
||||||
|
return default_thp_size;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We want to have a reasonable default block size such that
|
* We want to have a reasonable default block size such that
|
||||||
* 1. We avoid splitting THPs when unplugging memory, which degrades
|
* 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) &&
|
if (g_file_get_contents(HPAGE_PMD_SIZE_PATH, &content, NULL, NULL) &&
|
||||||
!qemu_strtou64(content, &endptr, 0, &tmp) &&
|
!qemu_strtou64(content, &endptr, 0, &tmp) &&
|
||||||
(!endptr || *endptr == '\n')) {
|
(!endptr || *endptr == '\n')) {
|
||||||
/*
|
/* Sanity-check the value and fallback to something reasonable. */
|
||||||
* Sanity-check the value, if it's too big (e.g., aarch64 with 64k base
|
if (!tmp || !is_power_of_2(tmp)) {
|
||||||
* pages) or weird, fallback to something smaller.
|
|
||||||
*/
|
|
||||||
if (!tmp || !is_power_of_2(tmp) || tmp > 16 * MiB) {
|
|
||||||
warn_report("Read unsupported THP size: %" PRIx64, tmp);
|
warn_report("Read unsupported THP size: %" PRIx64, tmp);
|
||||||
} else {
|
} else {
|
||||||
thp_size = tmp;
|
thp_size = tmp;
|
||||||
|
@ -98,7 +106,7 @@ static uint32_t virtio_mem_thp_size(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!thp_size) {
|
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
|
warn_report("Could not detect THP size, falling back to %" PRIx64
|
||||||
" MiB.", thp_size / MiB);
|
" 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.
|
* 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
|
* 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).
|
* aligned, similar to ordinary DIMMs).
|
||||||
*
|
*
|
||||||
* We can change this at any time and maybe even make it configurable if
|
* 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)
|
#if defined(TARGET_X86_64) || defined(TARGET_I386)
|
||||||
#define VIRTIO_MEM_USABLE_EXTENT (2 * (128 * MiB))
|
#define VIRTIO_MEM_USABLE_EXTENT (2 * (128 * MiB))
|
||||||
|
#elif defined(TARGET_ARM)
|
||||||
|
#define VIRTIO_MEM_USABLE_EXTENT (2 * (512 * MiB))
|
||||||
#else
|
#else
|
||||||
#error VIRTIO_MEM_USABLE_EXTENT not defined
|
#error VIRTIO_MEM_USABLE_EXTENT not defined
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "hw/timer/aspeed_timer.h"
|
#include "hw/timer/aspeed_timer.h"
|
||||||
#include "hw/rtc/aspeed_rtc.h"
|
#include "hw/rtc/aspeed_rtc.h"
|
||||||
#include "hw/i2c/aspeed_i2c.h"
|
#include "hw/i2c/aspeed_i2c.h"
|
||||||
|
#include "hw/misc/aspeed_i3c.h"
|
||||||
#include "hw/ssi/aspeed_smc.h"
|
#include "hw/ssi/aspeed_smc.h"
|
||||||
#include "hw/misc/aspeed_hace.h"
|
#include "hw/misc/aspeed_hace.h"
|
||||||
#include "hw/watchdog/wdt_aspeed.h"
|
#include "hw/watchdog/wdt_aspeed.h"
|
||||||
|
@ -51,6 +52,7 @@ struct AspeedSoCState {
|
||||||
AspeedRtcState rtc;
|
AspeedRtcState rtc;
|
||||||
AspeedTimerCtrlState timerctrl;
|
AspeedTimerCtrlState timerctrl;
|
||||||
AspeedI2CState i2c;
|
AspeedI2CState i2c;
|
||||||
|
AspeedI3CState i3c;
|
||||||
AspeedSCUState scu;
|
AspeedSCUState scu;
|
||||||
AspeedHACEState hace;
|
AspeedHACEState hace;
|
||||||
AspeedXDMAState xdma;
|
AspeedXDMAState xdma;
|
||||||
|
@ -141,6 +143,7 @@ enum {
|
||||||
ASPEED_DEV_HACE,
|
ASPEED_DEV_HACE,
|
||||||
ASPEED_DEV_DPMCU,
|
ASPEED_DEV_DPMCU,
|
||||||
ASPEED_DEV_DP,
|
ASPEED_DEV_DP,
|
||||||
|
ASPEED_DEV_I3C,
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* ASPEED_SOC_H */
|
#endif /* ASPEED_SOC_H */
|
||||||
|
|
|
@ -143,6 +143,8 @@ struct VirtMachineState {
|
||||||
bool secure;
|
bool secure;
|
||||||
bool highmem;
|
bool highmem;
|
||||||
bool highmem_ecam;
|
bool highmem_ecam;
|
||||||
|
bool highmem_mmio;
|
||||||
|
bool highmem_redists;
|
||||||
bool its;
|
bool its;
|
||||||
bool tcg_its;
|
bool tcg_its;
|
||||||
bool virt;
|
bool virt;
|
||||||
|
@ -190,7 +192,8 @@ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms)
|
||||||
|
|
||||||
assert(vms->gic_version == VIRT_GIC_VERSION_3);
|
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 */
|
#endif /* QEMU_ARM_VIRT_H */
|
||||||
|
|
|
@ -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 */
|
|
@ -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
|
|
@ -277,6 +277,16 @@ SRST
|
||||||
|
|
||||||
-smp 16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16
|
-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
|
Historically preference was given to the coarsest topology parameters
|
||||||
when computing missing values (ie sockets preferred over cores, which
|
when computing missing values (ie sockets preferred over cores, which
|
||||||
were preferred over threads), however, this behaviour is considered
|
were preferred over threads), however, this behaviour is considered
|
||||||
|
|
|
@ -1380,17 +1380,10 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
arm_cpu_pauth_finalize(cpu, &local_err);
|
||||||
* KVM does not support modifications to this feature.
|
if (local_err != NULL) {
|
||||||
* We have not registered the cpu properties when KVM
|
error_propagate(errp, local_err);
|
||||||
* is in use, so the user will not be able to set them.
|
return;
|
||||||
*/
|
|
||||||
if (!kvm_enabled()) {
|
|
||||||
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);
|
kvm_arm_set_cpu_features_from_host(cpu);
|
||||||
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
||||||
aarch64_add_sve_properties(obj);
|
aarch64_add_sve_properties(obj);
|
||||||
|
aarch64_add_pauth_properties(obj);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
hvf_arm_set_cpu_features_from_host(cpu);
|
hvf_arm_set_cpu_features_from_host(cpu);
|
||||||
|
|
|
@ -1076,6 +1076,7 @@ void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq);
|
||||||
void aarch64_sve_change_el(CPUARMState *env, int old_el,
|
void aarch64_sve_change_el(CPUARMState *env, int old_el,
|
||||||
int new_el, bool el0_a64);
|
int new_el, bool el0_a64);
|
||||||
void aarch64_add_sve_properties(Object *obj);
|
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.
|
* SVE registers are encoded in KVM's memory in an endianness-invariant format.
|
||||||
|
|
|
@ -630,6 +630,15 @@ void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp)
|
||||||
int arch_val = 0, impdef_val = 0;
|
int arch_val = 0, impdef_val = 0;
|
||||||
uint64_t t;
|
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. */
|
/* TODO: Handle HaveEnhancedPAC, HaveEnhancedPAC2, HaveFPAC. */
|
||||||
if (cpu->prop_pauth) {
|
if (cpu->prop_pauth) {
|
||||||
if (cpu->prop_pauth_impdef) {
|
if (cpu->prop_pauth_impdef) {
|
||||||
|
@ -655,6 +664,23 @@ static Property arm_cpu_pauth_property =
|
||||||
static Property arm_cpu_pauth_impdef_property =
|
static Property arm_cpu_pauth_impdef_property =
|
||||||
DEFINE_PROP_BOOL("pauth-impdef", ARMCPU, prop_pauth_impdef, false);
|
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);
|
/* -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.
|
* 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;
|
* 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 */
|
cpu->dcz_blocksize = 7; /* 512 bytes */
|
||||||
#endif
|
#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);
|
bitmap_fill(cpu->sve_vq_supported, ARM_MAX_VQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aarch64_add_pauth_properties(obj);
|
||||||
aarch64_add_sve_properties(obj);
|
aarch64_add_sve_properties(obj);
|
||||||
object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq,
|
object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq,
|
||||||
cpu_max_set_sve_max_vq, NULL, NULL);
|
cpu_max_set_sve_max_vq, NULL, NULL);
|
||||||
|
|
|
@ -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);
|
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)
|
bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
|
||||||
{
|
{
|
||||||
/* Identify the feature bits corresponding to the host CPU, and
|
/* 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, };
|
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)) {
|
if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -865,6 +882,10 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
||||||
assert(kvm_arm_sve_supported());
|
assert(kvm_arm_sve_supported());
|
||||||
cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_SVE;
|
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 */
|
/* Do KVM_ARM_VCPU_INIT ioctl */
|
||||||
ret = kvm_arm_vcpu_init(cs);
|
ret = kvm_arm_vcpu_init(cs);
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue