summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--Makefile5
-rwxr-xr-xconfigure16
-rw-r--r--contrib/libvhost-user/libvhost-user.c147
-rw-r--r--contrib/libvhost-user/libvhost-user.h3
-rw-r--r--default-configs/pci.mak3
-rw-r--r--docs/can.txt107
-rw-r--r--docs/pcie_pci_bridge.txt2
-rw-r--r--hw/arm/bcm2835_peripherals.c23
-rw-r--r--hw/arm/exynos4210.c14
-rw-r--r--hw/arm/fsl-imx6.c7
-rw-r--r--hw/arm/xilinx_zynq.c53
-rw-r--r--hw/arm/xlnx-zynqmp.c30
-rw-r--r--hw/block/dataplane/virtio-blk.c2
-rw-r--r--hw/block/nvme.c1
-rw-r--r--hw/display/cg3.c1
-rw-r--r--hw/display/exynos4210_fimd.c1
-rw-r--r--hw/display/framebuffer.c1
-rw-r--r--hw/display/g364fb.c11
-rw-r--r--hw/display/sm501.c1
-rw-r--r--hw/display/tcx.c2
-rw-r--r--hw/display/vga.c6
-rw-r--r--hw/isa/lpc_ich9.c1
-rw-r--r--hw/net/Makefile.objs2
-rw-r--r--hw/net/can/Makefile.objs4
-rw-r--r--hw/net/can/can_kvaser_pci.c319
-rw-r--r--hw/net/can/can_mioe3680_pci.c262
-rw-r--r--hw/net/can/can_pcm3680_pci.c263
-rw-r--r--hw/net/can/can_sja1000.c953
-rw-r--r--hw/net/can/can_sja1000.h146
-rw-r--r--hw/net/e1000e.c1
-rw-r--r--hw/pci-bridge/gen_pcie_root_port.c1
-rw-r--r--hw/pci-bridge/i82801b11.c2
-rw-r--r--hw/pci-bridge/ioh3420.c1
-rw-r--r--hw/pci-bridge/pci_bridge_dev.c1
-rw-r--r--hw/pci-bridge/pcie_pci_bridge.c3
-rw-r--r--hw/pci-bridge/pcie_root_port.c1
-rw-r--r--hw/pci-bridge/xio3130_downstream.c2
-rw-r--r--hw/pci-bridge/xio3130_upstream.c2
-rw-r--r--hw/pci-host/xilinx-pcie.c1
-rw-r--r--hw/pci/pci.c8
-rw-r--r--hw/pci/pci_bridge.c24
-rw-r--r--hw/scsi/megasas.c4
-rw-r--r--hw/scsi/virtio-scsi-dataplane.c2
-rw-r--r--hw/sd/core.c61
-rw-r--r--hw/sd/sd.c29
-rw-r--r--hw/sd/sdhci-internal.h71
-rw-r--r--hw/sd/sdhci.c397
-rw-r--r--hw/sd/trace-events9
-rw-r--r--hw/usb/hcd-xhci.c9
-rw-r--r--hw/vfio/pci.c5
-rw-r--r--hw/virtio/trace-events12
-rw-r--r--hw/virtio/vhost.c504
-rw-r--r--hw/virtio/virtio-balloon.c2
-rw-r--r--hw/virtio/virtio-bus.c14
-rw-r--r--hw/virtio/virtio-pci.c14
-rw-r--r--hw/virtio/virtio.c22
-rw-r--r--hw/xen/xen_pt.c9
-rw-r--r--include/exec/memory.h35
-rw-r--r--include/hw/compat.h8
-rw-r--r--include/hw/pci/pci.h3
-rw-r--r--include/hw/pci/pci_bridge.h4
-rw-r--r--include/hw/sd/sd.h20
-rw-r--r--include/hw/sd/sdhci.h6
-rw-r--r--include/hw/virtio/vhost.h5
-rw-r--r--include/hw/virtio/virtio-bus.h2
-rw-r--r--include/migration/vmstate.h1
-rw-r--r--include/net/can_emu.h123
-rw-r--r--include/net/can_host.h55
-rw-r--r--include/standard-headers/linux/virtio_balloon.h3
-rw-r--r--include/sysemu/hax.h2
-rw-r--r--memory.c70
-rw-r--r--net/Makefile.objs2
-rw-r--r--net/can/Makefile.objs2
-rw-r--r--net/can/can_core.c138
-rw-r--r--net/can/can_host.c112
-rw-r--r--net/can/can_socketcan.c286
-rw-r--r--rules.mak2
-rw-r--r--target/i386/hax-all.c2
-rw-r--r--target/i386/hax-darwin.c27
-rw-r--r--target/i386/hax-darwin.h1
-rw-r--r--target/i386/hax-i386.h1
-rw-r--r--target/i386/hax-interface.h8
-rw-r--r--target/i386/hax-mem.c34
-rw-r--r--target/i386/hax-windows.c38
-rw-r--r--target/i386/hax-windows.h2
-rw-r--r--tests/Makefile.include4
-rw-r--r--tests/acpi-test-data/pc/FACPbin116 -> 116 bytes
-rw-r--r--tests/acpi-test-data/q35/FACPbin116 -> 244 bytes
-rw-r--r--tests/bios-tables-test.c35
-rw-r--r--tests/sdhci-test.c250
91 files changed, 4183 insertions, 697 deletions
diff --git a/.travis.yml b/.travis.yml
index 0dd5020552..79377c8de0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,7 +13,7 @@ addons:
- libattr1-dev
- libbrlapi-dev
- libcap-ng-dev
- - libgcc-6-dev
+ - libgcc-4.8-dev
- libgnutls-dev
- libgtk-3-dev
- libiscsi-dev
diff --git a/Makefile b/Makefile
index 4ec7a3cb82..b5a6d602b2 100644
--- a/Makefile
+++ b/Makefile
@@ -294,7 +294,7 @@ else
DOCS=
endif
-SUBDIR_MAKEFLAGS=BUILD_DIR=$(BUILD_DIR)
+SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR)
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS))
@@ -958,4 +958,5 @@ ifdef QEMU_GA_MSI_ENABLED
endif
@echo ''
endif
- @echo ' $(MAKE) V=0|1 [targets] 0 => quiet build (default), 1 => verbose build'
+ @echo ' $(MAKE) [targets] (quiet build, default)'
+ @echo ' $(MAKE) V=1 [targets] (verbose build)'
diff --git a/configure b/configure
index fe9eea9218..913e14839d 100755
--- a/configure
+++ b/configure
@@ -471,10 +471,8 @@ for opt do
--cpu=*) cpu="$optarg"
;;
--extra-cflags=*) QEMU_CFLAGS="$QEMU_CFLAGS $optarg"
- EXTRA_CFLAGS="$optarg"
;;
--extra-cxxflags=*) QEMU_CXXFLAGS="$QEMU_CXXFLAGS $optarg"
- EXTRA_CXXFLAGS="$optarg"
;;
--extra-ldflags=*) LDFLAGS="$LDFLAGS $optarg"
EXTRA_LDFLAGS="$optarg"
@@ -1424,7 +1422,6 @@ case "$cpu" in
esac
QEMU_CFLAGS="$CPU_CFLAGS $QEMU_CFLAGS"
-EXTRA_CFLAGS="$CPU_CFLAGS $EXTRA_CFLAGS"
# For user-mode emulation the host arch has to be one we explicitly
# support, even if we're using TCI.
@@ -5309,7 +5306,15 @@ fi
##########################################
# checks for sanitizers
-write_c_skeleton
+# we could use a simple skeleton for flags checks, but this also
+# detect the static linking issue of ubsan, see also:
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84285
+cat > $TMPC << EOF
+#include <stdint.h>
+int main(void) {
+ return INT32_MIN / -1;
+}
+EOF
have_asan=no
have_ubsan=no
@@ -5877,9 +5882,6 @@ if test "$mingw32" = "no" ; then
echo "qemu_localstatedir=$local_statedir" >> $config_host_mak
fi
echo "qemu_helperdir=$libexecdir" >> $config_host_mak
-echo "extra_cflags=$EXTRA_CFLAGS" >> $config_host_mak
-echo "extra_cxxflags=$EXTRA_CXXFLAGS" >> $config_host_mak
-echo "extra_ldflags=$EXTRA_LDFLAGS" >> $config_host_mak
echo "qemu_localedir=$qemu_localedir" >> $config_host_mak
echo "libs_softmmu=$libs_softmmu" >> $config_host_mak
echo "GIT=$git" >> $config_host_mak
diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c
index 27cc59791b..2e358b5bce 100644
--- a/contrib/libvhost-user/libvhost-user.c
+++ b/contrib/libvhost-user/libvhost-user.c
@@ -118,15 +118,22 @@ vu_panic(VuDev *dev, const char *msg, ...)
/* Translate guest physical address to our virtual address. */
void *
-vu_gpa_to_va(VuDev *dev, uint64_t guest_addr)
+vu_gpa_to_va(VuDev *dev, uint64_t *plen, uint64_t guest_addr)
{
int i;
+ if (*plen == 0) {
+ return NULL;
+ }
+
/* Find matching memory region. */
for (i = 0; i < dev->nregions; i++) {
VuDevRegion *r = &dev->regions[i];
if ((guest_addr >= r->gpa) && (guest_addr < (r->gpa + r->size))) {
+ if ((guest_addr + *plen) > (r->gpa + r->size)) {
+ *plen = r->gpa + r->size - guest_addr;
+ }
return (void *)(uintptr_t)
guest_addr - r->gpa + r->mmap_addr + r->mmap_offset;
}
@@ -407,6 +414,15 @@ vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg)
{
int i;
VhostUserMemory *memory = &vmsg->payload.memory;
+
+ for (i = 0; i < dev->nregions; i++) {
+ VuDevRegion *r = &dev->regions[i];
+ void *m = (void *) (uintptr_t) r->mmap_addr;
+
+ if (m) {
+ munmap(m, r->size + r->mmap_offset);
+ }
+ }
dev->nregions = memory->nregions;
DPRINT("Nregions: %d\n", memory->nregions);
@@ -472,9 +488,14 @@ vu_set_log_base_exec(VuDev *dev, VhostUserMsg *vmsg)
rc = mmap(0, log_mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
log_mmap_offset);
+ close(fd);
if (rc == MAP_FAILED) {
perror("log mmap error");
}
+
+ if (dev->log_table) {
+ munmap(dev->log_table, dev->log_size);
+ }
dev->log_table = rc;
dev->log_size = log_mmap_size;
@@ -1102,6 +1123,37 @@ virtqueue_get_head(VuDev *dev, VuVirtq *vq,
return true;
}
+static int
+virtqueue_read_indirect_desc(VuDev *dev, struct vring_desc *desc,
+ uint64_t addr, size_t len)
+{
+ struct vring_desc *ori_desc;
+ uint64_t read_len;
+
+ if (len > (VIRTQUEUE_MAX_SIZE * sizeof(struct vring_desc))) {
+ return -1;
+ }
+
+ if (len == 0) {
+ return -1;
+ }
+
+ while (len) {
+ read_len = len;
+ ori_desc = vu_gpa_to_va(dev, &read_len, addr);
+ if (!ori_desc) {
+ return -1;
+ }
+
+ memcpy(desc, ori_desc, read_len);
+ len -= read_len;
+ addr += read_len;
+ desc += read_len;
+ }
+
+ return 0;
+}
+
enum {
VIRTQUEUE_READ_DESC_ERROR = -1,
VIRTQUEUE_READ_DESC_DONE = 0, /* end of chain */
@@ -1148,8 +1200,10 @@ vu_queue_get_avail_bytes(VuDev *dev, VuVirtq *vq, unsigned int *in_bytes,
}
while ((rc = virtqueue_num_heads(dev, vq, idx)) > 0) {
- unsigned int max, num_bufs, indirect = 0;
+ unsigned int max, desc_len, num_bufs, indirect = 0;
+ uint64_t desc_addr, read_len;
struct vring_desc *desc;
+ struct vring_desc desc_buf[VIRTQUEUE_MAX_SIZE];
unsigned int i;
max = vq->vring.num;
@@ -1173,8 +1227,24 @@ vu_queue_get_avail_bytes(VuDev *dev, VuVirtq *vq, unsigned int *in_bytes,
/* loop over the indirect descriptor table */
indirect = 1;
- max = desc[i].len / sizeof(struct vring_desc);
- desc = vu_gpa_to_va(dev, desc[i].addr);
+ desc_addr = desc[i].addr;
+ desc_len = desc[i].len;
+ max = desc_len / sizeof(struct vring_desc);
+ read_len = desc_len;
+ desc = vu_gpa_to_va(dev, &read_len, desc_addr);
+ if (unlikely(desc && read_len != desc_len)) {
+ /* Failed to use zero copy */
+ desc = NULL;
+ if (!virtqueue_read_indirect_desc(dev, desc_buf,
+ desc_addr,
+ desc_len)) {
+ desc = desc_buf;
+ }
+ }
+ if (!desc) {
+ vu_panic(dev, "Invalid indirect buffer table");
+ goto err;
+ }
num_bufs = i = 0;
}
@@ -1372,9 +1442,24 @@ virtqueue_map_desc(VuDev *dev,
return;
}
- iov[num_sg].iov_base = vu_gpa_to_va(dev, pa);
- iov[num_sg].iov_len = sz;
- num_sg++;
+ while (sz) {
+ uint64_t len = sz;
+
+ if (num_sg == max_num_sg) {
+ vu_panic(dev, "virtio: too many descriptors in indirect table");
+ return;
+ }
+
+ iov[num_sg].iov_base = vu_gpa_to_va(dev, &len, pa);
+ if (iov[num_sg].iov_base == NULL) {
+ vu_panic(dev, "virtio: invalid address for buffers");
+ return;
+ }
+ iov[num_sg].iov_len = len;
+ num_sg++;
+ sz -= len;
+ pa += len;
+ }
*p_num_sg = num_sg;
}
@@ -1406,10 +1491,12 @@ virtqueue_alloc_element(size_t sz,
void *
vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz)
{
- unsigned int i, head, max;
+ unsigned int i, head, max, desc_len;
+ uint64_t desc_addr, read_len;
VuVirtqElement *elem;
unsigned out_num, in_num;
struct iovec iov[VIRTQUEUE_MAX_SIZE];
+ struct vring_desc desc_buf[VIRTQUEUE_MAX_SIZE];
struct vring_desc *desc;
int rc;
@@ -1450,8 +1537,24 @@ vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz)
}
/* loop over the indirect descriptor table */
- max = desc[i].len / sizeof(struct vring_desc);
- desc = vu_gpa_to_va(dev, desc[i].addr);
+ desc_addr = desc[i].addr;
+ desc_len = desc[i].len;
+ max = desc_len / sizeof(struct vring_desc);
+ read_len = desc_len;
+ desc = vu_gpa_to_va(dev, &read_len, desc_addr);
+ if (unlikely(desc && read_len != desc_len)) {
+ /* Failed to use zero copy */
+ desc = NULL;
+ if (!virtqueue_read_indirect_desc(dev, desc_buf,
+ desc_addr,
+ desc_len)) {
+ desc = desc_buf;
+ }
+ }
+ if (!desc) {
+ vu_panic(dev, "Invalid indirect buffer table");
+ return NULL;
+ }
i = 0;
}
@@ -1527,7 +1630,9 @@ vu_log_queue_fill(VuDev *dev, VuVirtq *vq,
unsigned int len)
{
struct vring_desc *desc = vq->vring.desc;
- unsigned int i, max, min;
+ unsigned int i, max, min, desc_len;
+ uint64_t desc_addr, read_len;
+ struct vring_desc desc_buf[VIRTQUEUE_MAX_SIZE];
unsigned num_bufs = 0;
max = vq->vring.num;
@@ -1539,8 +1644,24 @@ vu_log_queue_fill(VuDev *dev, VuVirtq *vq,
}
/* loop over the indirect descriptor table */
- max = desc[i].len / sizeof(struct vring_desc);
- desc = vu_gpa_to_va(dev, desc[i].addr);
+ desc_addr = desc[i].addr;
+ desc_len = desc[i].len;
+ max = desc_len / sizeof(struct vring_desc);
+ read_len = desc_len;
+ desc = vu_gpa_to_va(dev, &read_len, desc_addr);
+ if (unlikely(desc && read_len != desc_len)) {
+ /* Failed to use zero copy */
+ desc = NULL;
+ if (!virtqueue_read_indirect_desc(dev, desc_buf,
+ desc_addr,
+ desc_len)) {
+ desc = desc_buf;
+ }
+ }
+ if (!desc) {
+ vu_panic(dev, "Invalid indirect buffer table");
+ return;
+ }
i = 0;
}
diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h
index f8a730b725..18f95f65d7 100644
--- a/contrib/libvhost-user/libvhost-user.h
+++ b/contrib/libvhost-user/libvhost-user.h
@@ -327,11 +327,12 @@ bool vu_dispatch(VuDev *dev);
/**
* vu_gpa_to_va:
* @dev: a VuDev context
+ * @plen: guest memory size
* @guest_addr: guest address
*
* Translate a guest address to a pointer. Returns NULL on failure.
*/
-void *vu_gpa_to_va(VuDev *dev, uint64_t guest_addr);
+void *vu_gpa_to_va(VuDev *dev, uint64_t *plen, uint64_t guest_addr);
/**
* vu_get_queue:
diff --git a/default-configs/pci.mak b/default-configs/pci.mak
index 49a0f285ac..35e7596949 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -31,6 +31,9 @@ CONFIG_ESP_PCI=y
CONFIG_SERIAL=y
CONFIG_SERIAL_ISA=y
CONFIG_SERIAL_PCI=y
+CONFIG_CAN_BUS=y
+CONFIG_CAN_SJA1000=y
+CONFIG_CAN_PCI=y
CONFIG_IPACK=y
CONFIG_WDT_IB6300ESB=y
CONFIG_PCI_TESTDEV=y
diff --git a/docs/can.txt b/docs/can.txt
new file mode 100644
index 0000000000..a357105762
--- /dev/null
+++ b/docs/can.txt
@@ -0,0 +1,107 @@
+QEMU CAN bus emulation support
+==============================
+
+The CAN bus emulation provides mechanism to connect multiple
+emulated CAN controller chips together by one or multiple CAN busses
+(the controller device "canbus" parameter). The individual busses
+can be connected to host system CAN API (at this time only Linux
+SocketCAN is supported).
+
+The concept of busses is generic and different CAN controllers
+can be implemented for it but at this time only SJA1000 chip
+controller is implemented.
+
+The PCI addon card hardware has been selected as the first CAN
+interface to implement because such device can be easily connected
+to systems with different CPU architectures (x86, PowerPC, ARM, etc.).
+
+The project has been initially started in frame of RTEMS GSoC 2013
+slot by Jin Yang under our mentoring The initial idea was to provide generic
+CAN subsystem for RTEMS. But lack of common environment for code and RTEMS
+testing lead to goal change to provide environment which provides complete
+emulated environment for testing and RTEMS GSoC slot has been donated
+to work on CAN hardware emulation on QEMU.
+
+Examples how to use CAN emulation
+=================================
+
+When QEMU with CAN PCI support is compiled then one of the next
+CAN boards can be selected
+
+ (1) CAN bus Kvaser PCI CAN-S (single SJA1000 channel) boad. QEMU startup options
+ -object can-bus,id=canbus0
+ -device kvaser_pci,canbus=canbus0
+ Add "can-host-socketcan" object to connect device to host system CAN bus
+ -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0
+
+ (2) CAN bus PCM-3680I PCI (dual SJA1000 channel) emulation
+ -object can-bus,id=canbus0
+ -device pcm3680_pci,canbus0=canbus0,canbus1=canbus0
+
+ another example:
+ -object can-bus,id=canbus0
+ -object can-bus,id=canbus1
+ -device pcm3680_pci,canbus0=canbus0,canbus1=canbus1
+
+ (3) CAN bus MIOe-3680 PCI (dual SJA1000 channel) emulation
+ -device mioe3680_pci,canbus0=canbus0
+
+
+The ''kvaser_pci'' board/device model is compatible with and has been tested with
+''kvaser_pci'' driver included in mainline Linux kernel.
+The tested setup was Linux 4.9 kernel on the host and guest side.
+Example for qemu-system-x86_64:
+
+ qemu-system-x86_64 -enable-kvm -kernel /boot/vmlinuz-4.9.0-4-amd64 \
+ -initrd ramdisk.cpio \
+ -virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \
+ -object can-bus,id=canbus0 \
+ -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 \
+ -device kvaser_pci,canbus=canbus0 \
+ -nographic -append "console=ttyS0"
+
+Example for qemu-system-arm:
+
+ qemu-system-arm -cpu arm1176 -m 256 -M versatilepb \
+ -kernel kernel-qemu-arm1176-versatilepb \
+ -hda rpi-wheezy-overlay \
+ -append "console=ttyAMA0 root=/dev/sda2 ro init=/sbin/init-overlay" \
+ -nographic \
+ -virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \
+ -object can-bus,id=canbus0 \
+ -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 \
+ -device kvaser_pci,canbus=canbus0,host=can0 \
+
+The CAN interface of the host system has to be configured for proper
+bitrate and set up. Configuration is not propagated from emulated
+devices through bus to the physical host device. Example configuration
+for 1 Mbit/s
+
+ ip link set can0 type can bitrate 1000000
+ ip link set can0 up
+
+Virtual (host local only) can interface can be used on the host
+side instead of physical interface
+
+ ip link add dev can0 type vcan
+
+The CAN interface on the host side can be used to analyze CAN
+traffic with "candump" command which is included in "can-utils".
+
+ candump can0
+
+Links to other resources
+========================
+
+ (1) Repository with development branch can-pci at Czech Technical University
+ https://gitlab.fel.cvut.cz/canbus/qemu-canbus
+ (2) GitHub repository with can-pci and our other changes included
+ https://gitlab.fel.cvut.cz/canbus/qemu-canbus
+ (3) RTEMS page describing project
+ https://devel.rtems.org/wiki/Developer/Simulators/QEMU/CANEmulation
+ (4) RTLWS 2015 article about the projevt and its use with CANopen emulation
+ http://rtime.felk.cvut.cz/publications/public/rtlws2015-qemu-can.pdf
+ Slides
+ http://rtime.felk.cvut.cz/publications/public/rtlws2015-qemu-can-slides.pdf
+ (5) Linux SocketCAN utilities
+ https://github.com/linux-can/can-utils/ \ No newline at end of file
diff --git a/docs/pcie_pci_bridge.txt b/docs/pcie_pci_bridge.txt
index 5a4203f97c..ab35ebf3ca 100644
--- a/docs/pcie_pci_bridge.txt
+++ b/docs/pcie_pci_bridge.txt
@@ -110,5 +110,5 @@ To enable device hot-plug into the bridge on Linux there're 3 ways:
Implementation
==============
The PCIE-PCI bridge is based on PCI-PCI bridge, but also accumulates PCI Express
-features as a PCI Express device (is_express=1).
+features as a PCI Express device.
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index 12e0dd11af..13b63970d7 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -19,7 +19,7 @@
#define BCM2835_VC_PERI_BASE 0x7e000000
/* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */
-#define BCM2835_SDHC_CAPAREG 0x52034b4
+#define BCM2835_SDHC_CAPAREG 0x52134b4
static void bcm2835_peripherals_init(Object *obj)
{
@@ -254,14 +254,19 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(&s->peri_mr, RNG_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0));
- /* Extended Mass Media Controller */
- object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
- &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
+ /* Extended Mass Media Controller
+ *
+ * Compatible with:
+ * - SD Host Controller Specification Version 3.0 Draft 1.0
+ * - SDIO Specification Version 3.0
+ * - MMC Specification Version 4.4
+ *
+ * For the exact details please refer to the Arasan documentation:
+ * SD3.0_Host_AHB_eMMC4.4_Usersguide_ver5.9_jan11_10.pdf
+ */
+ object_property_set_uint(OBJECT(&s->sdhci), 3, "sd-spec-version", &err);
+ object_property_set_uint(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
+ &err);
object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk",
&err);
if (err) {
diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c
index e8e1d81e62..06f9d1ffa4 100644
--- a/hw/arm/exynos4210.c
+++ b/hw/arm/exynos4210.c
@@ -377,8 +377,20 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem)
BlockBackend *blk;
DriveInfo *di;
+ /* Compatible with:
+ * - SD Host Controller Specification Version 2.0
+ * - SDIO Specification Version 2.0
+ * - MMC Specification Version 4.3
+ * - SDMA
+ * - ADMA2
+ *
+ * As this part of the Exynos4210 is not publically available,
+ * we used the "HS-MMC Controller S3C2416X RISC Microprocessor"
+ * public datasheet which is very similar (implementing
+ * MMC Specification Version 4.0 being the only difference noted)
+ */
dev = qdev_create(NULL, TYPE_SYSBUS_SDHCI);
- qdev_prop_set_uint32(dev, "capareg", EXYNOS4210_SDHCI_CAPABILITIES);
+ qdev_prop_set_uint64(dev, "capareg", EXYNOS4210_SDHCI_CAPABILITIES);
qdev_init_nofail(dev);
busdev = SYS_BUS_DEVICE(dev);
diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c
index e6559a8b12..b6ac72de27 100644
--- a/hw/arm/fsl-imx6.c
+++ b/hw/arm/fsl-imx6.c
@@ -27,6 +27,8 @@
#include "chardev/char.h"
#include "qemu/error-report.h"
+#define IMX6_ESDHC_CAPABILITIES 0x057834b4
+
#define NAME_SIZE 20
static void fsl_imx6_init(Object *obj)
@@ -348,6 +350,11 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
{ FSL_IMX6_uSDHC4_ADDR, FSL_IMX6_uSDHC4_IRQ },
};
+ /* UHS-I SDIO3.0 SDR104 1.8V ADMA */
+ object_property_set_uint(OBJECT(&s->esdhc[i]), 3, "sd-spec-version",
+ &err);
+ object_property_set_uint(OBJECT(&s->esdhc[i]), IMX6_ESDHC_CAPABILITIES,
+ "capareg", &err);
object_property_set_bool(OBJECT(&s->esdhc[i]), true, "realized", &err);
if (err) {
error_propagate(errp, err);
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 1836a4ed45..0f76333770 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -61,6 +61,8 @@ static const int dma_irqs[8] = {
#define SLCR_XILINX_UNLOCK_KEY 0xdf0d
#define SLCR_XILINX_LOCK_KEY 0x767b
+#define ZYNQ_SDHCI_CAPABILITIES 0x69ec0080 /* Datasheet: UG585 (v1.12.1) */
+
#define ARMV7_IMM16(x) (extract32((x), 0, 12) | \
extract32((x), 12, 4) << 16)
@@ -165,10 +167,8 @@ static void zynq_init(MachineState *machine)
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *ext_ram = g_new(MemoryRegion, 1);
MemoryRegion *ocm_ram = g_new(MemoryRegion, 1);
- DeviceState *dev, *carddev;
+ DeviceState *dev;
SysBusDevice *busdev;
- DriveInfo *di;
- BlockBackend *blk;
qemu_irq pic[64];
int n;
@@ -247,27 +247,32 @@ static void zynq_init(MachineState *machine)
gem_init(&nd_table[0], 0xE000B000, pic[54-IRQ_OFFSET]);
gem_init(&nd_table[1], 0xE000C000, pic[77-IRQ_OFFSET]);
- dev = qdev_create(NULL, TYPE_SYSBUS_SDHCI);
- qdev_init_nofail(dev);
- sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0100000);
- sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[56-IRQ_OFFSET]);
-
- di = drive_get_next(IF_SD);
- blk = di ? blk_by_legacy_dinfo(di) : NULL;
- carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
- qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
- object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
-
- dev = qdev_create(NULL, TYPE_SYSBUS_SDHCI);
- qdev_init_nofail(dev);
- sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0101000);
- sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[79-IRQ_OFFSET]);
-
- di = drive_get_next(IF_SD);
- blk = di ? blk_by_legacy_dinfo(di) : NULL;
- carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
- qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
- object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
+ for (n = 0; n < 2; n++) {
+ int hci_irq = n ? 79 : 56;
+ hwaddr hci_addr = n ? 0xE0101000 : 0xE0100000;
+ DriveInfo *di;
+ BlockBackend *blk;
+ DeviceState *carddev;
+
+ /* Compatible with:
+ * - SD Host Controller Specification Version 2.0 Part A2
+ * - SDIO Specification Version 2.0
+ * - MMC Specification Version 3.31
+ */
+ dev = qdev_create(NULL, TYPE_SYSBUS_SDHCI);
+ qdev_prop_set_uint8(dev, "sd-spec-version", 2);
+ qdev_prop_set_uint64(dev, "capareg", ZYNQ_SDHCI_CAPABILITIES);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, hci_addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[hci_irq - IRQ_OFFSET]);
+
+ di = drive_get_next(IF_SD);
+ blk = di ? blk_by_legacy_dinfo(di) : NULL;
+ carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
+ qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
+ object_property_set_bool(OBJECT(carddev), true, "realized",
+ &error_fatal);
+ }
dev = qdev_create(NULL, TYPE_ZYNQ_XADC);
qdev_init_nofail(dev);
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index ca398c4159..4b93a3abd2 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -53,6 +53,8 @@
#define IPI_ADDR 0xFF300000
#define IPI_IRQ 64
+#define SDHCI_CAPABILITIES 0x280737ec6481 /* Datasheet: UG1085 (v1.7) */
+
static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = {
0xFF0B0000, 0xFF0C0000, 0xFF0D0000, 0xFF0E0000,
};
@@ -387,22 +389,28 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, gic_spi[SATA_INTR]);
for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) {
- char *bus_name;
-
- object_property_set_bool(OBJECT(&s->sdhci[i]), true,
- "realized", &err);
+ char *bus_name = g_strdup_printf("sd-bus%d", i);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(&s->sdhci[i]);
+ Object *sdhci = OBJECT(&s->sdhci[i]);
+
+ /* Compatible with:
+ * - SD Host Controller Specification Version 3.00
+ * - SDIO Specification Version 3.0
+ * - eMMC Specification Version 4.51
+ */
+ object_property_set_uint(sdhci, 3, "sd-spec-version", &err);
+ object_property_set_uint(sdhci, SDHCI_CAPABILITIES, "capareg", &err);
+ object_property_set_uint(sdhci, UHS_I, "uhs", &err);
+ object_property_set_bool(sdhci, true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci[i]), 0,
- sdhci_addr[i]);
- sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci[i]), 0,
- gic_spi[sdhci_intr[i]]);
+ sysbus_mmio_map(sbd, 0, sdhci_addr[i]);
+ sysbus_connect_irq(sbd, 0, gic_spi[sdhci_intr[i]]);
+
/* Alias controller SD bus to the SoC itself */
- bus_name = g_strdup_printf("sd-bus%d", i);
- object_property_add_alias(OBJECT(s), bus_name,
- OBJECT(&s->sdhci[i]), "sd-bus",
+ object_property_add_alias(OBJECT(s), bus_name, sdhci, "sd-bus",
&error_abort);
g_free(bus_name);
}
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
index f6fc639e88..2cb990997e 100644
--- a/hw/block/dataplane/virtio-blk.c
+++ b/hw/block/dataplane/virtio-blk.c
@@ -192,6 +192,7 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev)
fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r);
while (i--) {
virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
+ virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
}
goto fail_guest_notifiers;
}
@@ -267,6 +268,7 @@ void virtio_blk_data_plane_stop(VirtIODevice *vdev)
for (i = 0; i < nvqs; i++) {
virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
+ virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
}
/* Clean up guest notifier (irq) */
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index 51a58fefba..85d2406400 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -1360,7 +1360,6 @@ static void nvme_class_init(ObjectClass *oc, void *data)
pc->vendor_id = PCI_VENDOR_ID_INTEL;
pc->device_id = 0x5845;
pc->revision = 2;
- pc->is_express = 1;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
dc->desc = "Non-Volatile Memory Express";
diff --git a/hw/display/cg3.c b/hw/display/cg3.c
index cafd9f47ef..6fff4852c5 100644
--- a/hw/display/cg3.c
+++ b/hw/display/cg3.c
@@ -108,7 +108,6 @@ static void cg3_update_display(void *opaque)
data = (uint32_t *)surface_data(surface);
if (!s->full_update) {
- memory_region_sync_dirty_bitmap(&s->vram_mem);
snap = memory_region_snapshot_and_clear_dirty(&s->vram_mem, 0x0,
memory_region_size(&s->vram_mem),
DIRTY_MEMORY_VGA);
diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c
index 86e37e93e9..f011ea5b00 100644
--- a/hw/display/exynos4210_fimd.c
+++ b/hw/display/exynos4210_fimd.c
@@ -1289,7 +1289,6 @@ static void exynos4210_fimd_update(void *opaque)
scrn_width = w->virtpage_width;
/* Total width of virtual screen page in bytes */
inc_size = scrn_width + w->virtpage_offsize;
- memory_region_sync_dirty_bitmap(w->mem_section.mr);
host_fb_addr = w->host_fb_addr;
fb_line_addr = w->mem_section.offset_within_region;
snap = memory_region_snapshot_and_clear_dirty(w->mem_section.mr,
diff --git a/hw/display/framebuffer.c b/hw/display/framebuffer.c
index d7310d25f2..36e3db189a 100644
--- a/hw/display/framebuffer.c
+++ b/hw/display/framebuffer.c
@@ -83,7 +83,6 @@ void framebuffer_update_display(
if (!mem) {
return;
}
- memory_region_sync_dirty_bitmap(mem);
addr = mem_section->offset_within_region;
src = memory_region_get_ram_ptr(mem) + addr;
diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c
index 86557d14a9..819f8be05d 100644
--- a/hw/display/g364fb.c
+++ b/hw/display/g364fb.c
@@ -62,15 +62,15 @@ typedef struct G364State {
#define G364_PAGE_SIZE 4096
-static inline int check_dirty(G364State *s, ram_addr_t page)
+static inline int check_dirty(G364State *s, DirtyBitmapSnapshot *snap, ram_addr_t page)
{
- return memory_region_test_and_clear_dirty(&s->mem_vram, page, G364_PAGE_SIZE,
- DIRTY_MEMORY_VGA);
+ return memory_region_snapshot_get_dirty(&s->mem_vram, snap, page, G364_PAGE_SIZE);
}
static void g364fb_draw_graphic8(G364State *s)
{
DisplaySurface *surface = qemu_console_surface(s->con);
+ DirtyBitmapSnapshot *snap;
int i, w;
uint8_t *vram;
uint8_t *data_display, *dd;
@@ -122,8 +122,10 @@ static void g364fb_draw_graphic8(G364State *s)
vram = s->vram + s->top_of_screen;
/* XXX: out of range in vram? */
data_display = dd = surface_data(surface);
+ snap = memory_region_snapshot_and_clear_dirty(&s->mem_vram, 0, s->vram_size,
+ DIRTY_MEMORY_VGA);
while (y < s->height) {
- if (check_dirty(s, page)) {
+ if (check_dirty(s, snap, page)) {
if (y < ymin)
ymin = ymax = y;
if (x < xmin)
@@ -244,7 +246,6 @@ static void g364fb_update_display(void *opaque)
qemu_console_resize(s->con, s->width, s->height);
}
- memory_region_sync_dirty_bitmap(&s->mem_vram);
if (s->ctla & CTLA_FORCE_BLANK) {
g364fb_draw_blank(s);
} else if (s->depth == 8) {
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index 134cbed607..f4bb33c279 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1508,7 +1508,6 @@ static void sm501_update_display(void *opaque)
}
/* draw each line according to conditions */
- memory_region_sync_dirty_bitmap(&s->local_mem_region);
snap = memory_region_snapshot_and_clear_dirty(&s->local_mem_region,
offset, width * height * src_bpp, DIRTY_MEMORY_VGA);
for (y = 0, offset = 0; y < height; y++, offset += width * src_bpp) {
diff --git a/hw/display/tcx.c b/hw/display/tcx.c
index daa93e0929..b2786ee8d0 100644
--- a/hw/display/tcx.c
+++ b/hw/display/tcx.c
@@ -236,7 +236,6 @@ static void tcx_update_display(void *opaque)
dd = surface_stride(surface);
ds = 1024;
- memory_region_sync_dirty_bitmap(&ts->vram_mem);
snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
memory_region_size(&ts->vram_mem),
DIRTY_MEMORY_VGA);
@@ -292,7 +291,6 @@ static void tcx24_update_display(void *opaque)
dd = surface_stride(surface);
ds = 1024;
- memory_region_sync_dirty_bitmap(&ts->vram_mem);
snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
memory_region_size(&ts->vram_mem),
DIRTY_MEMORY_VGA);
diff --git a/hw/display/vga.c b/hw/display/vga.c
index 6e78a4e156..28f298b342 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -1444,11 +1444,6 @@ static bool vga_scanline_invalidated(VGACommonState *s, int y)
return s->invalidated_y_table[y >> 5] & (1 << (y & 0x1f));
}
-void vga_sync_dirty_bitmap(VGACommonState *s)
-{
- memory_region_sync_dirty_bitmap(&s->vram);
-}
-
void vga_dirty_log_start(VGACommonState *s)
{
memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA);
@@ -1638,7 +1633,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
y1 = 0;
if (!full_update) {
- vga_sync_dirty_bitmap(s);
if (s->line_compare < height) {
/* split screen mode */
region_start = 0;
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index adcf077fa5..e692b9fdc1 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -39,7 +39,6 @@
#include "hw/isa/apm.h"
#include "hw/i386/ioapic.h"
#include "hw/pci/pci.h"
-#include "hw/pci/pcie_host.h"
#include "hw/pci/pci_bridge.h"
#include "hw/i386/ich9.h"
#include "hw/acpi/acpi.h"
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index 4171af0b5d..ab22968641 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -46,3 +46,5 @@ common-obj-$(CONFIG_ROCKER) += rocker/rocker.o rocker/rocker_fp.o \
rocker/rocker_desc.o rocker/rocker_world.o \
rocker/rocker_of_dpa.o
obj-$(call lnot,$(CONFIG_ROCKER)) += rocker/qmp-norocker.o
+
+common-obj-$(CONFIG_CAN_BUS) += can/
diff --git a/hw/net/can/Makefile.objs b/hw/net/can/Makefile.objs
new file mode 100644
index 0000000000..9f0c4ee332
--- /dev/null
+++ b/hw/net/can/Makefile.objs
@@ -0,0 +1,4 @@
+common-obj-$(CONFIG_CAN_SJA1000) += can_sja1000.o
+common-obj-$(CONFIG_CAN_PCI) += can_kvaser_pci.o
+common-obj-$(CONFIG_CAN_PCI) += can_pcm3680_pci.o
+common-obj-$(CONFIG_CAN_PCI) += can_mioe3680_pci.o
diff --git a/hw/net/can/can_kvaser_pci.c b/hw/net/can/can_kvaser_pci.c
new file mode 100644
index 0000000000..5f82f4359a
--- /dev/null
+++ b/hw/net/can/can_kvaser_pci.c
@@ -0,0 +1,319 @@
+/*
+ * Kvaser PCI CAN device (SJA1000 based) emulation
+ *
+ * Copyright (c) 2013-2014 Jin Yang
+ * Copyright (c) 2014-2018 Pavel Pisa
+ *
+ * Partially based on educational PCIexpress APOHW hardware
+ * emulator used fro class A0B36APO at CTU FEE course by
+ * Rostislav Lisovy and Pavel Pisa
+ *
+ * Initial development supported by Google GSoC 2013 from RTEMS project slot
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/event_notifier.h"
+#include "qemu/thread.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "net/can_emu.h"
+
+#include "can_sja1000.h"
+
+#define TYPE_CAN_PCI_DEV "kvaser_pci"
+
+#define KVASER_PCI_DEV(obj) \
+ OBJECT_CHECK(KvaserPCIState, (obj), TYPE_CAN_PCI_DEV)
+
+#ifndef KVASER_PCI_VENDOR_ID1
+#define KVASER_PCI_VENDOR_ID1 0x10e8 /* the PCI device and vendor IDs */
+#endif
+
+#ifndef KVASER_PCI_DEVICE_ID1
+#define KVASER_PCI_DEVICE_ID1 0x8406
+#endif
+
+#define KVASER_PCI_S5920_RANGE 0x80
+#define KVASER_PCI_SJA_RANGE 0x80
+#define KVASER_PCI_XILINX_RANGE 0x8
+
+#define KVASER_PCI_BYTES_PER_SJA 0x20
+
+#define S5920_OMB 0x0C
+#define S5920_IMB 0x1C
+#define S5920_MBEF 0x34
+#define S5920_INTCSR 0x38
+#define S5920_RCR 0x3C
+#define S5920_PTCR 0x60
+
+#define S5920_INTCSR_ADDON_INTENABLE_M 0x2000
+#define S5920_INTCSR_INTERRUPT_ASSERTED_M 0x800000
+
+#define KVASER_PCI_XILINX_VERINT 7 /* Lower nibble simulate interrupts,
+ high nibble version number. */
+
+#define KVASER_PCI_XILINX_VERSION_NUMBER 13
+
+typedef struct KvaserPCIState {
+ /*< private >*/
+ PCIDevice dev;
+ /*< public >*/
+ MemoryRegion s5920_io;
+ MemoryRegion sja_io;
+ MemoryRegion xilinx_io;
+
+ CanSJA1000State sja_state;
+ qemu_irq irq;
+
+ uint32_t s5920_intcsr;
+ uint32_t s5920_irqstate;
+
+ CanBusState *canbus;
+} KvaserPCIState;
+
+static void kvaser_pci_irq_handler(void *opaque, int irq_num, int level)
+{
+ KvaserPCIState *d = (KvaserPCIState *)opaque;
+
+ d->s5920_irqstate = level;
+ if (d->s5920_intcsr & S5920_INTCSR_ADDON_INTENABLE_M) {
+ pci_set_irq(&d->dev, level);
+ }
+}
+
+static void kvaser_pci_reset(DeviceState *dev)
+{
+ KvaserPCIState *d = KVASER_PCI_DEV(dev);
+ CanSJA1000State *s = &d->sja_state;
+
+ can_sja_hardware_reset(s);
+}
+
+static uint64_t kvaser_pci_s5920_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ KvaserPCIState *d = opaque;
+ uint64_t val;
+
+ switch (addr) {
+ case S5920_INTCSR:
+ val = d->s5920_intcsr;
+ val &= ~S5920_INTCSR_INTERRUPT_ASSERTED_M;
+ if (d->s5920_irqstate) {
+ val |= S5920_INTCSR_INTERRUPT_ASSERTED_M;
+ }
+ return val;
+ }
+ return 0;
+}
+
+static void kvaser_pci_s5920_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ KvaserPCIState *d = opaque;
+
+ switch (addr) {
+ case S5920_INTCSR:
+ if (d->s5920_irqstate &&
+ ((d->s5920_intcsr ^ data) & S5920_INTCSR_ADDON_INTENABLE_M)) {
+ pci_set_irq(&d->dev, !!(data & S5920_INTCSR_ADDON_INTENABLE_M));
+ }
+ d->s5920_intcsr = data;
+ break;
+ }
+}
+
+static uint64_t kvaser_pci_sja_io_read(void *opaque, hwaddr addr, unsigned size)
+{
+ KvaserPCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state;
+
+ if (addr >= KVASER_PCI_BYTES_PER_SJA) {
+ return 0;
+ }
+
+ return can_sja_mem_read(s, addr, size);
+}
+
+static void kvaser_pci_sja_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ KvaserPCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state;
+
+ if (addr >= KVASER_PCI_BYTES_PER_SJA) {
+ return;
+ }
+
+ can_sja_mem_write(s, addr, data, size);
+}
+
+static uint64_t kvaser_pci_xilinx_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ switch (addr) {
+ case KVASER_PCI_XILINX_VERINT:
+ return (KVASER_PCI_XILINX_VERSION_NUMBER << 4) | 0;
+ }
+
+ return 0;
+}
+
+static void kvaser_pci_xilinx_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+
+}
+
+static const MemoryRegionOps kvaser_pci_s5920_io_ops = {
+ .read = kvaser_pci_s5920_io_read,
+ .write = kvaser_pci_s5920_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static const MemoryRegionOps kvaser_pci_sja_io_ops = {
+ .read = kvaser_pci_sja_io_read,
+ .write = kvaser_pci_sja_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static const MemoryRegionOps kvaser_pci_xilinx_io_ops = {
+ .read = kvaser_pci_xilinx_io_read,
+ .write = kvaser_pci_xilinx_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static void kvaser_pci_realize(PCIDevice *pci_dev, Error **errp)
+{
+ KvaserPCIState *d = KVASER_PCI_DEV(pci_dev);
+ CanSJA1000State *s = &d->sja_state;
+ uint8_t *pci_conf;
+
+ pci_conf = pci_dev->config;
+ pci_conf[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
+
+ d->irq = qemu_allocate_irq(kvaser_pci_irq_handler, d, 0);
+
+ can_sja_init(s, d->irq);
+
+ if (can_sja_connect_to_bus(s, d->canbus) < 0) {
+ error_setg(errp, "can_sja_connect_to_bus failed");
+ return;
+ }
+
+ memory_region_init_io(&d->s5920_io, OBJECT(d), &kvaser_pci_s5920_io_ops,
+ d, "kvaser_pci-s5920", KVASER_PCI_S5920_RANGE);
+ memory_region_init_io(&d->sja_io, OBJECT(d), &kvaser_pci_sja_io_ops,
+ d, "kvaser_pci-sja", KVASER_PCI_SJA_RANGE);
+ memory_region_init_io(&d->xilinx_io, OBJECT(d), &kvaser_pci_xilinx_io_ops,
+ d, "kvaser_pci-xilinx", KVASER_PCI_XILINX_RANGE);
+
+ pci_register_bar(&d->dev, /*BAR*/ 0, PCI_BASE_ADDRESS_SPACE_IO,
+ &d->s5920_io);
+ pci_register_bar(&d->dev, /*BAR*/ 1, PCI_BASE_ADDRESS_SPACE_IO,
+ &d->sja_io);
+ pci_register_bar(&d->dev, /*BAR*/ 2, PCI_BASE_ADDRESS_SPACE_IO,
+ &d->xilinx_io);
+}
+
+static void kvaser_pci_exit(PCIDevice *pci_dev)
+{
+ KvaserPCIState *d = KVASER_PCI_DEV(pci_dev);
+ CanSJA1000State *s = &d->sja_state;
+
+ can_sja_disconnect(s);
+
+ qemu_free_irq(d->irq);
+}
+
+static const VMStateDescription vmstate_kvaser_pci = {
+ .name = "kvaser_pci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, KvaserPCIState),
+ /* Load this before sja_state. */
+ VMSTATE_UINT32(s5920_intcsr, KvaserPCIState),
+ VMSTATE_STRUCT(sja_state, KvaserPCIState, 0, vmstate_can_sja,
+ CanSJA1000State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void kvaser_pci_instance_init(Object *obj)
+{
+ KvaserPCIState *d = KVASER_PCI_DEV(obj);
+
+ object_property_add_link(obj, "canbus", TYPE_CAN_BUS,
+ (Object **)&d->canbus,
+ qdev_prop_allow_set_link_before_realize,
+ 0, &error_abort);
+}
+
+static void kvaser_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = kvaser_pci_realize;
+ k->exit = kvaser_pci_exit;
+ k->vendor_id = KVASER_PCI_VENDOR_ID1;
+ k->device_id = KVASER_PCI_DEVICE_ID1;
+ k->revision = 0x00;
+ k->class_id = 0x00ff00;
+ dc->desc = "Kvaser PCICANx";
+ dc->vmsd = &vmstate_kvaser_pci;
+ dc->reset = kvaser_pci_reset;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo kvaser_pci_info = {
+ .name = TYPE_CAN_PCI_DEV,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(KvaserPCIState),
+ .class_init = kvaser_pci_class_init,
+ .instance_init = kvaser_pci_instance_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void kvaser_pci_register_types(void)
+{
+ type_register_static(&kvaser_pci_info);
+}
+
+type_init(kvaser_pci_register_types)
diff --git a/hw/net/can/can_mioe3680_pci.c b/hw/net/can/can_mioe3680_pci.c
new file mode 100644
index 0000000000..fd20b88955
--- /dev/null
+++ b/hw/net/can/can_mioe3680_pci.c
@@ -0,0 +1,262 @@
+/*
+ * MIOe-3680 PCI CAN device (SJA1000 based) emulation
+ *
+ * Copyright (c) 2016 Deniz Eren (deniz.eren@icloud.com)
+ *
+ * Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by
+ * Jin Yang and Pavel Pisa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/event_notifier.h"
+#include "qemu/thread.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "net/can_emu.h"
+
+#include "can_sja1000.h"
+
+#define TYPE_CAN_PCI_DEV "mioe3680_pci"
+
+#define MIOe3680_PCI_DEV(obj) \
+ OBJECT_CHECK(Mioe3680PCIState, (obj), TYPE_CAN_PCI_DEV)
+
+/* the PCI device and vendor IDs */
+#ifndef MIOe3680_PCI_VENDOR_ID1
+#define MIOe3680_PCI_VENDOR_ID1 0x13fe
+#endif
+
+#ifndef MIOe3680_PCI_DEVICE_ID1
+#define MIOe3680_PCI_DEVICE_ID1 0xc302
+#endif
+
+#define MIOe3680_PCI_SJA_COUNT 2
+#define MIOe3680_PCI_SJA_RANGE 0x400
+
+#define MIOe3680_PCI_BYTES_PER_SJA 0x80
+
+typedef struct Mioe3680PCIState {
+ /*< private >*/
+ PCIDevice dev;
+ /*< public >*/
+ MemoryRegion sja_io[MIOe3680_PCI_SJA_COUNT];
+
+ CanSJA1000State sja_state[MIOe3680_PCI_SJA_COUNT];
+ qemu_irq irq;
+
+ char *model; /* The model that support, only SJA1000 now. */
+ CanBusState *canbus[MIOe3680_PCI_SJA_COUNT];
+} Mioe3680PCIState;
+
+static void mioe3680_pci_reset(DeviceState *dev)
+{
+ Mioe3680PCIState *d = MIOe3680_PCI_DEV(dev);
+ int i;
+
+ for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
+ can_sja_hardware_reset(&d->sja_state[i]);
+ }
+}
+
+static uint64_t mioe3680_pci_sja1_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ Mioe3680PCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[0];
+
+ if (addr >= MIOe3680_PCI_BYTES_PER_SJA) {
+ return 0;
+ }
+
+ return can_sja_mem_read(s, addr >> 2, size);
+}
+
+static void mioe3680_pci_sja1_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ Mioe3680PCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[0];
+
+ if (addr >= MIOe3680_PCI_BYTES_PER_SJA) {
+ return;
+ }
+
+ can_sja_mem_write(s, addr >> 2, data, size);
+}
+
+static uint64_t mioe3680_pci_sja2_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ Mioe3680PCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[1];
+
+ if (addr >= MIOe3680_PCI_BYTES_PER_SJA) {
+ return 0;
+ }
+
+ return can_sja_mem_read(s, addr >> 2, size);
+}
+
+static void mioe3680_pci_sja2_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ Mioe3680PCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[1];
+
+ if (addr >= MIOe3680_PCI_BYTES_PER_SJA) {
+ return;
+ }
+
+ can_sja_mem_write(s, addr >> 2, data, size);
+}
+
+static const MemoryRegionOps mioe3680_pci_sja1_io_ops = {
+ .read = mioe3680_pci_sja1_io_read,
+ .write = mioe3680_pci_sja1_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static const MemoryRegionOps mioe3680_pci_sja2_io_ops = {
+ .read = mioe3680_pci_sja2_io_read,
+ .write = mioe3680_pci_sja2_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static void mioe3680_pci_realize(PCIDevice *pci_dev, Error **errp)
+{
+ Mioe3680PCIState *d = MIOe3680_PCI_DEV(pci_dev);
+ uint8_t *pci_conf;
+ int i;
+
+ pci_conf = pci_dev->config;
+ pci_conf[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
+
+ d->irq = pci_allocate_irq(&d->dev);
+
+ for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
+ can_sja_init(&d->sja_state[i], d->irq);
+ }
+
+ for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
+ if (can_sja_connect_to_bus(&d->sja_state[i], d->canbus[i]) < 0) {
+ error_setg(errp, "can_sja_connect_to_bus failed");
+ return;
+ }
+ }
+
+ memory_region_init_io(&d->sja_io[0], OBJECT(d), &mioe3680_pci_sja1_io_ops,
+ d, "mioe3680_pci-sja1", MIOe3680_PCI_SJA_RANGE);
+ memory_region_init_io(&d->sja_io[1], OBJECT(d), &mioe3680_pci_sja2_io_ops,
+ d, "mioe3680_pci-sja2", MIOe3680_PCI_SJA_RANGE);
+
+ for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
+ pci_register_bar(&d->dev, /*BAR*/ i, PCI_BASE_ADDRESS_SPACE_IO,
+ &d->sja_io[i]);
+ }
+}
+
+static void mioe3680_pci_exit(PCIDevice *pci_dev)
+{
+ Mioe3680PCIState *d = MIOe3680_PCI_DEV(pci_dev);
+ int i;
+
+ for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
+ can_sja_disconnect(&d->sja_state[i]);
+ }
+
+ qemu_free_irq(d->irq);
+}
+
+static const VMStateDescription vmstate_mioe3680_pci = {
+ .name = "mioe3680_pci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, Mioe3680PCIState),
+ VMSTATE_STRUCT(sja_state[0], Mioe3680PCIState, 0, vmstate_can_sja,
+ CanSJA1000State),
+ VMSTATE_STRUCT(sja_state[1], Mioe3680PCIState, 0, vmstate_can_sja,
+ CanSJA1000State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void mioe3680_pci_instance_init(Object *obj)
+{
+ Mioe3680PCIState *d = MIOe3680_PCI_DEV(obj);
+
+ object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
+ (Object **)&d->canbus[0],
+ qdev_prop_allow_set_link_before_realize,
+ 0, &error_abort);
+ object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
+ (Object **)&d->canbus[1],
+ qdev_prop_allow_set_link_before_realize,
+ 0, &error_abort);
+}
+
+static void mioe3680_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = mioe3680_pci_realize;
+ k->exit = mioe3680_pci_exit;
+ k->vendor_id = MIOe3680_PCI_VENDOR_ID1;
+ k->device_id = MIOe3680_PCI_DEVICE_ID1;
+ k->revision = 0x00;
+ k->class_id = 0x000c09;
+ k->subsystem_vendor_id = MIOe3680_PCI_VENDOR_ID1;
+ k->subsystem_id = MIOe3680_PCI_DEVICE_ID1;
+ dc->desc = "Mioe3680 PCICANx";
+ dc->vmsd = &vmstate_mioe3680_pci;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->reset = mioe3680_pci_reset;
+}
+
+static const TypeInfo mioe3680_pci_info = {
+ .name = TYPE_CAN_PCI_DEV,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(Mioe3680PCIState),
+ .class_init = mioe3680_pci_class_init,
+ .instance_init = mioe3680_pci_instance_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void mioe3680_pci_register_types(void)
+{
+ type_register_static(&mioe3680_pci_info);
+}
+
+type_init(mioe3680_pci_register_types)
diff --git a/hw/net/can/can_pcm3680_pci.c b/hw/net/can/can_pcm3680_pci.c
new file mode 100644
index 0000000000..23f7ff45a3
--- /dev/null
+++ b/hw/net/can/can_pcm3680_pci.c
@@ -0,0 +1,263 @@
+/*
+ * PCM-3680i PCI CAN device (SJA1000 based) emulation
+ *
+ * Copyright (c) 2016 Deniz Eren (deniz.eren@icloud.com)
+ *
+ * Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by
+ * Jin Yang and Pavel Pisa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/event_notifier.h"
+#include "qemu/thread.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "net/can_emu.h"
+
+#include "can_sja1000.h"
+
+#define TYPE_CAN_PCI_DEV "pcm3680_pci"
+
+#define PCM3680i_PCI_DEV(obj) \
+ OBJECT_CHECK(Pcm3680iPCIState, (obj), TYPE_CAN_PCI_DEV)
+
+/* the PCI device and vendor IDs */
+#ifndef PCM3680i_PCI_VENDOR_ID1
+#define PCM3680i_PCI_VENDOR_ID1 0x13fe
+#endif
+
+#ifndef PCM3680i_PCI_DEVICE_ID1
+#define PCM3680i_PCI_DEVICE_ID1 0xc002
+#endif
+
+#define PCM3680i_PCI_SJA_COUNT 2
+#define PCM3680i_PCI_SJA_RANGE 0x100
+
+#define PCM3680i_PCI_BYTES_PER_SJA 0x20
+
+typedef struct Pcm3680iPCIState {
+ /*< private >*/
+ PCIDevice dev;
+ /*< public >*/
+ MemoryRegion sja_io[PCM3680i_PCI_SJA_COUNT];
+
+ CanSJA1000State sja_state[PCM3680i_PCI_SJA_COUNT];
+ qemu_irq irq;
+
+ char *model; /* The model that support, only SJA1000 now. */
+ CanBusState *canbus[PCM3680i_PCI_SJA_COUNT];
+} Pcm3680iPCIState;
+
+static void pcm3680i_pci_reset(DeviceState *dev)
+{
+ Pcm3680iPCIState *d = PCM3680i_PCI_DEV(dev);
+ int i;
+
+ for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
+ can_sja_hardware_reset(&d->sja_state[i]);
+ }
+}
+
+static uint64_t pcm3680i_pci_sja1_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ Pcm3680iPCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[0];
+
+ if (addr >= PCM3680i_PCI_BYTES_PER_SJA) {
+ return 0;
+ }
+
+ return can_sja_mem_read(s, addr, size);
+}
+
+static void pcm3680i_pci_sja1_io_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ Pcm3680iPCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[0];
+
+ if (addr >= PCM3680i_PCI_BYTES_PER_SJA) {
+ return;
+ }
+
+ can_sja_mem_write(s, addr, data, size);
+}
+
+static uint64_t pcm3680i_pci_sja2_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ Pcm3680iPCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[1];
+
+ if (addr >= PCM3680i_PCI_BYTES_PER_SJA) {
+ return 0;
+ }
+
+ return can_sja_mem_read(s, addr, size);
+}
+
+static void pcm3680i_pci_sja2_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ Pcm3680iPCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[1];
+
+ if (addr >= PCM3680i_PCI_BYTES_PER_SJA) {
+ return;
+ }
+
+ can_sja_mem_write(s, addr, data, size);
+}
+
+static const MemoryRegionOps pcm3680i_pci_sja1_io_ops = {
+ .read = pcm3680i_pci_sja1_io_read,
+ .write = pcm3680i_pci_sja1_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static const MemoryRegionOps pcm3680i_pci_sja2_io_ops = {
+ .read = pcm3680i_pci_sja2_io_read,
+ .write = pcm3680i_pci_sja2_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static void pcm3680i_pci_realize(PCIDevice *pci_dev, Error **errp)
+{
+ Pcm3680iPCIState *d = PCM3680i_PCI_DEV(pci_dev);
+ uint8_t *pci_conf;
+ int i;
+
+ pci_conf = pci_dev->config;
+ pci_conf[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
+
+ d->irq = pci_allocate_irq(&d->dev);
+
+ for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
+ can_sja_init(&d->sja_state[i], d->irq);
+ }
+
+ for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
+ if (can_sja_connect_to_bus(&d->sja_state[i], d->canbus[i]) < 0) {
+ error_setg(errp, "can_sja_connect_to_bus failed");
+ return;
+ }
+ }
+
+ memory_region_init_io(&d->sja_io[0], OBJECT(d), &pcm3680i_pci_sja1_io_ops,
+ d, "pcm3680i_pci-sja1", PCM3680i_PCI_SJA_RANGE);
+
+ memory_region_init_io(&d->sja_io[1], OBJECT(d), &pcm3680i_pci_sja2_io_ops,
+ d, "pcm3680i_pci-sja2", PCM3680i_PCI_SJA_RANGE);
+
+ for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
+ pci_register_bar(&d->dev, /*BAR*/ i, PCI_BASE_ADDRESS_SPACE_IO,
+ &d->sja_io[i]);
+ }
+}
+
+static void pcm3680i_pci_exit(PCIDevice *pci_dev)
+{
+ Pcm3680iPCIState *d = PCM3680i_PCI_DEV(pci_dev);
+ int i;
+
+ for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
+ can_sja_disconnect(&d->sja_state[i]);
+ }
+
+ qemu_free_irq(d->irq);
+}
+
+static const VMStateDescription vmstate_pcm3680i_pci = {
+ .name = "pcm3680i_pci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, Pcm3680iPCIState),
+ VMSTATE_STRUCT(sja_state[0], Pcm3680iPCIState, 0,
+ vmstate_can_sja, CanSJA1000State),
+ VMSTATE_STRUCT(sja_state[1], Pcm3680iPCIState, 0,
+ vmstate_can_sja, CanSJA1000State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pcm3680i_pci_instance_init(Object *obj)
+{
+ Pcm3680iPCIState *d = PCM3680i_PCI_DEV(obj);
+
+ object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
+ (Object **)&d->canbus[0],
+ qdev_prop_allow_set_link_before_realize,
+ 0, &error_abort);
+ object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
+ (Object **)&d->canbus[1],
+ qdev_prop_allow_set_link_before_realize,
+ 0, &error_abort);
+}
+
+static void pcm3680i_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = pcm3680i_pci_realize;
+ k->exit = pcm3680i_pci_exit;
+ k->vendor_id = PCM3680i_PCI_VENDOR_ID1;
+ k->device_id = PCM3680i_PCI_DEVICE_ID1;
+ k->revision = 0x00;
+ k->class_id = 0x000c09;
+ k->subsystem_vendor_id = PCM3680i_PCI_VENDOR_ID1;
+ k->subsystem_id = PCM3680i_PCI_DEVICE_ID1;
+ dc->desc = "Pcm3680i PCICANx";
+ dc->vmsd = &vmstate_pcm3680i_pci;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->reset = pcm3680i_pci_reset;
+}
+
+static const TypeInfo pcm3680i_pci_info = {
+ .name = TYPE_CAN_PCI_DEV,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(Pcm3680iPCIState),
+ .class_init = pcm3680i_pci_class_init,
+ .instance_init = pcm3680i_pci_instance_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void pcm3680i_pci_register_types(void)
+{
+ type_register_static(&pcm3680i_pci_info);
+}
+
+type_init(pcm3680i_pci_register_types)
diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c
new file mode 100644
index 0000000000..629323312c
--- /dev/null
+++ b/hw/net/can/can_sja1000.c
@@ -0,0 +1,953 @@
+/*
+ * CAN device - SJA1000 chip emulation for QEMU
+ *
+ * Copyright (c) 2013-2014 Jin Yang
+ * Copyright (c) 2014-2018 Pavel Pisa
+ *
+ * Initial development supported by Google GSoC 2013 from RTEMS project slot
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "chardev/char.h"
+#include "hw/hw.h"
+#include "net/can_emu.h"
+
+#include "can_sja1000.h"
+
+#ifndef DEBUG_FILTER
+#define DEBUG_FILTER 0
+#endif /*DEBUG_FILTER*/
+
+#ifndef DEBUG_CAN
+#define DEBUG_CAN 0
+#endif /*DEBUG_CAN*/
+
+#define DPRINTF(fmt, ...) \
+ do { \
+ if (DEBUG_CAN) { \
+ qemu_log("[cansja]: " fmt , ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+static void can_sja_software_reset(CanSJA1000State *s)
+{
+ s->mode &= ~0x31;
+ s->mode |= 0x01;
+ s->status_pel &= ~0x37;
+ s->status_pel |= 0x34;
+
+ s->rxbuf_start = 0x00;
+ s->rxmsg_cnt = 0x00;
+ s->rx_cnt = 0x00;
+}
+
+void can_sja_hardware_reset(CanSJA1000State *s)
+{
+ /* Reset by hardware, p10 */
+ s->mode = 0x01;
+ s->status_pel = 0x3c;
+ s->interrupt_pel = 0x00;
+ s->clock = 0x00;
+ s->rxbuf_start = 0x00;
+ s->rxmsg_cnt = 0x00;
+ s->rx_cnt = 0x00;
+
+ s->control = 0x01;
+ s->status_bas = 0x0c;
+ s->interrupt_bas = 0x00;
+
+ qemu_irq_lower(s->irq);
+}
+
+static
+void can_sja_single_filter(struct qemu_can_filter *filter,
+ const uint8_t *acr, const uint8_t *amr, int extended)
+{
+ if (extended) {
+ filter->can_id = (uint32_t)acr[0] << 21;
+ filter->can_id |= (uint32_t)acr[1] << 13;
+ filter->can_id |= (uint32_t)acr[2] << 5;
+ filter->can_id |= (uint32_t)acr[3] >> 3;
+ if (acr[3] & 4) {
+ filter->can_id |= QEMU_CAN_RTR_FLAG;
+ }
+
+ filter->can_mask = (uint32_t)amr[0] << 21;
+ filter->can_mask |= (uint32_t)amr[1] << 13;
+ filter->can_mask |= (uint32_t)amr[2] << 5;
+ filter->can_mask |= (uint32_t)amr[3] >> 3;
+ filter->can_mask = ~filter->can_mask & QEMU_CAN_EFF_MASK;
+ if (!(amr[3] & 4)) {
+ filter->can_mask |= QEMU_CAN_RTR_FLAG;
+ }
+ } else {
+ filter->can_id = (uint32_t)acr[0] << 3;
+ filter->can_id |= (uint32_t)acr[1] >> 5;
+ if (acr[1] & 0x10) {
+ filter->can_id |= QEMU_CAN_RTR_FLAG;
+ }
+
+ filter->can_mask = (uint32_t)amr[0] << 3;
+ filter->can_mask |= (uint32_t)amr[1] << 5;
+ filter->can_mask = ~filter->can_mask & QEMU_CAN_SFF_MASK;
+ if (!(amr[1] & 0x10)) {
+ filter->can_mask |= QEMU_CAN_RTR_FLAG;
+ }
+ }
+}
+
+static
+void can_sja_dual_filter(struct qemu_can_filter *filter,
+ const uint8_t *acr, const uint8_t *amr, int extended)
+{
+ if (extended) {
+ filter->can_id = (uint32_t)acr[0] << 21;
+ filter->can_id |= (uint32_t)acr[1] << 13;
+
+ filter->can_mask = (uint32_t)amr[0] << 21;
+ filter->can_mask |= (uint32_t)amr[1] << 13;
+ filter->can_mask = ~filter->can_mask & QEMU_CAN_EFF_MASK & ~0x1fff;
+ } else {
+ filter->can_id = (uint32_t)acr[0] << 3;
+ filter->can_id |= (uint32_t)acr[1] >> 5;
+ if (acr[1] & 0x10) {
+ filter->can_id |= QEMU_CAN_RTR_FLAG;
+ }
+
+ filter->can_mask = (uint32_t)amr[0] << 3;
+ filter->can_mask |= (uint32_t)amr[1] >> 5;
+ filter->can_mask = ~filter->can_mask & QEMU_CAN_SFF_MASK;
+ if (!(amr[1] & 0x10)) {
+ filter->can_mask |= QEMU_CAN_RTR_FLAG;
+ }
+ }
+}
+
+/* Details in DS-p22, what we need to do here is to test the data. */
+static
+int can_sja_accept_filter(CanSJA1000State *s,
+ const qemu_can_frame *frame)
+{
+
+ struct qemu_can_filter filter;
+
+ if (s->clock & 0x80) { /* PeliCAN Mode */
+ if (s->mode & (1 << 3)) { /* Single mode. */
+ if (frame->can_id & QEMU_CAN_EFF_FLAG) { /* EFF */
+ can_sja_single_filter(&filter,
+ s->code_mask + 0, s->code_mask + 4, 1);
+
+ if (!can_bus_filter_match(&filter, frame->can_id)) {
+ return 0;
+ }
+ } else { /* SFF */
+ can_sja_single_filter(&filter,
+ s->code_mask + 0, s->code_mask + 4, 0);
+
+ if (!can_bus_filter_match(&filter, frame->can_id)) {
+ return 0;
+ }
+
+ if (frame->can_id & QEMU_CAN_RTR_FLAG) { /* RTR */
+ return 1;
+ }
+
+ if (frame->can_dlc == 0) {
+ return 1;
+ }
+
+ if ((frame->data[0] & ~(s->code_mask[6])) !=
+ (s->code_mask[2] & ~(s->code_mask[6]))) {
+ return 0;
+ }
+
+ if (frame->can_dlc < 2) {
+ return 1;
+ }
+
+ if ((frame->data[1] & ~(s->code_mask[7])) ==
+ (s->code_mask[3] & ~(s->code_mask[7]))) {
+ return 1;
+ }
+
+ return 0;
+ }
+ } else { /* Dual mode */
+ if (frame->can_id & QEMU_CAN_EFF_FLAG) { /* EFF */
+ can_sja_dual_filter(&filter,
+ s->code_mask + 0, s->code_mask + 4, 1);
+
+ if (can_bus_filter_match(&filter, frame->can_id)) {
+ return 1;
+ }
+
+ can_sja_dual_filter(&filter,
+ s->code_mask + 2, s->code_mask + 6, 1);
+
+ if (can_bus_filter_match(&filter, frame->can_id)) {
+ return 1;
+ }
+
+ return 0;
+ } else {
+ can_sja_dual_filter(&filter,
+ s->code_mask + 0, s->code_mask + 4, 0);
+
+ if (can_bus_filter_match(&filter, frame->can_id)) {
+ uint8_t expect;
+ uint8_t mask;
+ expect = s->code_mask[1] << 4;
+ expect |= s->code_mask[3] & 0x0f;
+
+ mask = s->code_mask[5] << 4;
+ mask |= s->code_mask[7] & 0x0f;
+ mask = ~mask & 0xff;
+
+ if ((frame->data[0] & mask) ==
+ (expect & mask)) {
+ return 1;
+ }
+ }
+
+ can_sja_dual_filter(&filter,
+ s->code_mask + 2, s->code_mask + 6, 0);
+
+ if (can_bus_filter_match(&filter, frame->can_id)) {
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static void can_display_msg(const char *prefix, const qemu_can_frame *msg)
+{
+ int i;
+
+ qemu_log_lock();
+ qemu_log("%s%03X [%01d] %s %s",
+ prefix,
+ msg->can_id & QEMU_CAN_EFF_MASK,
+ msg->can_dlc,
+ msg->can_id & QEMU_CAN_EFF_FLAG ? "EFF" : "SFF",
+ msg->can_id & QEMU_CAN_RTR_FLAG ? "RTR" : "DAT");
+
+ for (i = 0; i < msg->can_dlc; i++) {
+ qemu_log(" %02X", msg->data[i]);
+ }
+ qemu_log("\n");
+ qemu_log_flush();
+ qemu_log_unlock();
+}
+
+static void buff2frame_pel(const uint8_t *buff, qemu_can_frame *frame)
+{
+ uint8_t i;
+
+ frame->can_id = 0;
+ if (buff[0] & 0x40) { /* RTR */
+ frame->can_id = QEMU_CAN_RTR_FLAG;
+ }
+ frame->can_dlc = buff[0] & 0x0f;
+
+ if (buff[0] & 0x80) { /* Extended */
+ frame->can_id |= QEMU_CAN_EFF_FLAG;
+ frame->can_id |= buff[1] << 21; /* ID.28~ID.21 */
+ frame->can_id |= buff[2] << 13; /* ID.20~ID.13 */
+ frame->can_id |= buff[3] << 5;
+ frame->can_id |= buff[4] >> 3;
+ for (i = 0; i < frame->can_dlc; i++) {
+ frame->data[i] = buff[5 + i];
+ }
+ for (; i < 8; i++) {
+ frame->data[i] = 0;
+ }
+ } else {
+ frame->can_id |= buff[1] << 3;
+ frame->can_id |= buff[2] >> 5;
+ for (i = 0; i < frame->can_dlc; i++) {
+ frame->data[i] = buff[3 + i];
+ }
+ for (; i < 8; i++) {
+ frame->data[i] = 0;
+ }
+ }
+}
+
+
+static void buff2frame_bas(const uint8_t *buff, qemu_can_frame *frame)
+{
+ uint8_t i;
+
+ frame->can_id = ((buff[0] << 3) & (0xff << 3)) + ((buff[1] >> 5) & 0x07);
+ if (buff[1] & 0x10) { /* RTR */
+ frame->can_id = QEMU_CAN_RTR_FLAG;
+ }
+ frame->can_dlc = buff[1] & 0x0f;
+
+ for (i = 0; i < frame->can_dlc; i++) {
+ frame->data[i] = buff[2 + i];
+ }
+ for (; i < 8; i++) {
+ frame->data[i] = 0;
+ }
+}
+
+
+static int frame2buff_pel(const qemu_can_frame *frame, uint8_t *buff)
+{
+ int i;
+
+ if (frame->can_id & QEMU_CAN_ERR_FLAG) { /* error frame, NOT support now. */
+ return -1;
+ }
+
+ buff[0] = 0x0f & frame->can_dlc; /* DLC */
+ if (frame->can_id & QEMU_CAN_RTR_FLAG) { /* RTR */
+ buff[0] |= (1 << 6);
+ }
+ if (frame->can_id & QEMU_CAN_EFF_FLAG) { /* EFF */
+ buff[0] |= (1 << 7);
+ buff[1] = extract32(frame->can_id, 21, 8); /* ID.28~ID.21 */
+ buff[2] = extract32(frame->can_id, 13, 8); /* ID.20~ID.13 */
+ buff[3] = extract32(frame->can_id, 5, 8); /* ID.12~ID.05 */
+ buff[4] = extract32(frame->can_id, 0, 5) << 3; /* ID.04~ID.00,xxx */
+ for (i = 0; i < frame->can_dlc; i++) {
+ buff[5 + i] = frame->data[i];
+ }
+ return frame->can_dlc + 5;
+ } else { /* SFF */
+ buff[1] = extract32(frame->can_id, 3, 8); /* ID.10~ID.03 */
+ buff[2] = extract32(frame->can_id, 0, 3) << 5; /* ID.02~ID.00,xxxxx */
+ for (i = 0; i < frame->can_dlc; i++) {
+ buff[3 + i] = frame->data[i];
+ }
+
+ return frame->can_dlc + 3;
+ }
+
+ return -1;
+}
+
+static int frame2buff_bas(const qemu_can_frame *frame, uint8_t *buff)
+{
+ int i;
+
+ /*
+ * EFF, no support for BasicMode
+ * No use for Error frames now,
+ * they could be used in future to update SJA1000 error state
+ */
+ if ((frame->can_id & QEMU_CAN_EFF_FLAG) ||
+ (frame->can_id & QEMU_CAN_ERR_FLAG)) {
+ return -1;
+ }
+
+ buff[0] = extract32(frame->can_id, 3, 8); /* ID.10~ID.03 */
+ buff[1] = extract32(frame->can_id, 0, 3) << 5; /* ID.02~ID.00,xxxxx */
+ if (frame->can_id & QEMU_CAN_RTR_FLAG) { /* RTR */
+ buff[1] |= (1 << 4);
+ }
+ buff[1] |= frame->can_dlc & 0x0f;
+ for (i = 0; i < frame->can_dlc; i++) {
+ buff[2 + i] = frame->data[i];
+ }
+
+ return frame->can_dlc + 2;
+}
+
+static void can_sja_update_pel_irq(CanSJA1000State *s)
+{
+ if (s->interrupt_en & s->interrupt_pel) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void can_sja_update_bas_irq(CanSJA1000State *s)
+{
+ if ((s->control >> 1) & s->interrupt_bas) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ qemu_can_frame frame;
+ uint32_t tmp;
+ uint8_t tmp8, count;
+
+
+ DPRINTF("write 0x%02llx addr 0x%02x\n",
+ (unsigned long long)val, (unsigned int)addr);
+
+ if (addr > CAN_SJA_MEM_SIZE) {
+ return ;
+ }
+
+ if (s->clock & 0x80) { /* PeliCAN Mode */
+ switch (addr) {
+ case SJA_MOD: /* Mode register */
+ s->mode = 0x1f & val;
+ if ((s->mode & 0x01) && ((val & 0x01) == 0)) {
+ /* Go to operation mode from reset mode. */
+ if (s->mode & (1 << 3)) { /* Single mode. */
+ /* For EFF */
+ can_sja_single_filter(&s->filter[0],
+ s->code_mask + 0, s->code_mask + 4, 1);
+
+ /* For SFF */
+ can_sja_single_filter(&s->filter[1],
+ s->code_mask + 0, s->code_mask + 4, 0);
+
+ can_bus_client_set_filters(&s->bus_client, s->filter, 2);
+ } else { /* Dual mode */
+ /* For EFF */
+ can_sja_dual_filter(&s->filter[0],
+ s->code_mask + 0, s->code_mask + 4, 1);
+
+ can_sja_dual_filter(&s->filter[1],
+ s->code_mask + 2, s->code_mask + 6, 1);
+
+ /* For SFF */
+ can_sja_dual_filter(&s->filter[2],
+ s->code_mask + 0, s->code_mask + 4, 0);
+
+ can_sja_dual_filter(&s->filter[3],
+ s->code_mask + 2, s->code_mask + 6, 0);
+
+ can_bus_client_set_filters(&s->bus_client, s->filter, 4);
+ }
+
+ s->rxmsg_cnt = 0;
+ s->rx_cnt = 0;
+ }
+ break;
+
+ case SJA_CMR: /* Command register. */
+ if (0x01 & val) { /* Send transmission request. */
+ buff2frame_pel(s->tx_buff, &frame);
+ if (DEBUG_FILTER) {
+ can_display_msg("[cansja]: Tx request " , &frame);
+ }
+
+ /*
+ * Clear transmission complete status,
+ * and Transmit Buffer Status.
+ * write to the backends.
+ */
+ s->status_pel &= ~(3 << 2);
+
+ can_bus_client_send(&s->bus_client, &frame, 1);
+
+ /*
+ * Set transmission complete status
+ * and Transmit Buffer Status.
+ */
+ s->status_pel |= (3 << 2);
+
+ /* Clear transmit status. */
+ s->status_pel &= ~(1 << 5);
+ s->interrupt_pel |= 0x02;
+ can_sja_update_pel_irq(s);
+ }
+ if (0x04 & val) { /* Release Receive Buffer */
+ if (s->rxmsg_cnt <= 0) {
+ break;
+ }
+
+ tmp8 = s->rx_buff[s->rxbuf_start]; count = 0;
+ if (tmp8 & (1 << 7)) { /* EFF */
+ count += 2;
+ }
+ count += 3;
+ if (!(tmp8 & (1 << 6))) { /* DATA */
+ count += (tmp8 & 0x0f);
+ }
+
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: message released from "
+ "Rx FIFO cnt=%d, count=%d\n", s->rx_cnt, count);
+ }
+
+ s->rxbuf_start += count;
+ s->rxbuf_start %= SJA_RCV_BUF_LEN;
+
+ s->rx_cnt -= count;
+ s->rxmsg_cnt--;
+ if (s->rxmsg_cnt == 0) {
+ s->status_pel &= ~(1 << 0);
+ s->interrupt_pel &= ~(1 << 0);
+ can_sja_update_pel_irq(s);
+ }
+ }
+ if (0x08 & val) { /* Clear data overrun */
+ s->status_pel &= ~(1 << 1);
+ s->interrupt_pel &= ~(1 << 3);
+ can_sja_update_pel_irq(s);
+ }
+ break;
+ case SJA_SR: /* Status register */
+ case SJA_IR: /* Interrupt register */
+ break; /* Do nothing */
+ case SJA_IER: /* Interrupt enable register */
+ s->interrupt_en = val;
+ break;
+ case 16: /* RX frame information addr16-28. */
+ s->status_pel |= (1 << 5); /* Set transmit status. */
+ case 17 ... 28:
+ if (s->mode & 0x01) { /* Reset mode */
+ if (addr < 24) {
+ s->code_mask[addr - 16] = val;
+ }
+ } else { /* Operation mode */
+ s->tx_buff[addr - 16] = val; /* Store to TX buffer directly. */
+ }
+ break;
+ case SJA_CDR:
+ s->clock = val;
+ break;
+ }
+ } else { /* Basic Mode */
+ switch (addr) {
+ case SJA_BCAN_CTR: /* Control register, addr 0 */
+ if ((s->control & 0x01) && ((val & 0x01) == 0)) {
+ /* Go to operation mode from reset mode. */
+ s->filter[0].can_id = (s->code << 3) & (0xff << 3);
+ tmp = (~(s->mask << 3)) & (0xff << 3);
+ tmp |= QEMU_CAN_EFF_FLAG; /* Only Basic CAN Frame. */
+ s->filter[0].can_mask = tmp;
+ can_bus_client_set_filters(&s->bus_client, s->filter, 1);
+
+ s->rxmsg_cnt = 0;
+ s->rx_cnt = 0;
+ } else if (!(s->control & 0x01) && !(val & 0x01)) {
+ can_sja_software_reset(s);
+ }
+
+ s->control = 0x1f & val;
+ break;
+ case SJA_BCAN_CMR: /* Command register, addr 1 */
+ if (0x01 & val) { /* Send transmission request. */
+ buff2frame_bas(s->tx_buff, &frame);
+ if (DEBUG_FILTER) {
+ can_display_msg("[cansja]: Tx request " , &frame);
+ }
+
+ /*
+ * Clear transmission complete status,
+ * and Transmit Buffer Status.
+ */
+ s->status_bas &= ~(3 << 2);
+
+ /* write to the backends. */
+ can_bus_client_send(&s->bus_client, &frame, 1);
+
+ /*
+ * Set transmission complete status,
+ * and Transmit Buffer Status.
+ */
+ s->status_bas |= (3 << 2);
+
+ /* Clear transmit status. */
+ s->status_bas &= ~(1 << 5);
+ s->interrupt_bas |= 0x02;
+ can_sja_update_bas_irq(s);
+ }
+ if (0x04 & val) { /* Release Receive Buffer */
+ if (s->rxmsg_cnt <= 0) {
+ break;
+ }
+
+ tmp8 = s->rx_buff[(s->rxbuf_start + 1) % SJA_RCV_BUF_LEN];
+ count = 2 + (tmp8 & 0x0f);
+
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: message released from "
+ "Rx FIFO cnt=%d, count=%d\n", s->rx_cnt, count);
+ }
+
+ s->rxbuf_start += count;
+ s->rxbuf_start %= SJA_RCV_BUF_LEN;
+ s->rx_cnt -= count;
+ s->rxmsg_cnt--;
+
+ if (s->rxmsg_cnt == 0) {
+ s->status_bas &= ~(1 << 0);
+ s->interrupt_bas &= ~(1 << 0);
+ can_sja_update_bas_irq(s);
+ }
+ }
+ if (0x08 & val) { /* Clear data overrun */
+ s->status_bas &= ~(1 << 1);
+ s->interrupt_bas &= ~(1 << 3);
+ can_sja_update_bas_irq(s);
+ }
+ break;
+ case 4:
+ s->code = val;
+ break;
+ case 5:
+ s->mask = val;
+ break;
+ case 10:
+ s->status_bas |= (1 << 5); /* Set transmit status. */
+ case 11 ... 19:
+ if ((s->control & 0x01) == 0) { /* Operation mode */
+ s->tx_buff[addr - 10] = val; /* Store to TX buffer directly. */
+ }
+ break;
+ case SJA_CDR:
+ s->clock = val;
+ break;
+ }
+ }
+}
+
+uint64_t can_sja_mem_read(CanSJA1000State *s, hwaddr addr, unsigned size)
+{
+ uint64_t temp = 0;
+
+ DPRINTF("read addr 0x%02x ...\n", (unsigned int)addr);
+
+ if (addr > CAN_SJA_MEM_SIZE) {
+ return 0;
+ }
+
+ if (s->clock & 0x80) { /* PeliCAN Mode */
+ switch (addr) {
+ case SJA_MOD: /* Mode register, addr 0 */
+ temp = s->mode;
+ break;
+ case SJA_CMR: /* Command register, addr 1 */
+ temp = 0x00; /* Command register, cannot be read. */
+ break;
+ case SJA_SR: /* Status register, addr 2 */
+ temp = s->status_pel;
+ break;
+ case SJA_IR: /* Interrupt register, addr 3 */
+ temp = s->interrupt_pel;
+ s->interrupt_pel = 0;
+ if (s->rxmsg_cnt) {
+ s->interrupt_pel |= (1 << 0); /* Receive interrupt. */
+ }
+ can_sja_update_pel_irq(s);
+ break;
+ case SJA_IER: /* Interrupt enable register, addr 4 */
+ temp = s->interrupt_en;
+ break;
+ case 5: /* Reserved */
+ case 6: /* Bus timing 0, hardware related, not support now. */
+ case 7: /* Bus timing 1, hardware related, not support now. */
+ case 8: /*
+ * Output control register, hardware related,
+ * not supported for now.
+ */
+ case 9: /* Test. */
+ case 10 ... 15: /* Reserved */
+ temp = 0x00;
+ break;
+
+ case 16 ... 28:
+ if (s->mode & 0x01) { /* Reset mode */
+ if (addr < 24) {
+ temp = s->code_mask[addr - 16];
+ } else {
+ temp = 0x00;
+ }
+ } else { /* Operation mode */
+ temp = s->rx_buff[(s->rxbuf_start + addr - 16) %
+ SJA_RCV_BUF_LEN];
+ }
+ break;
+ case SJA_CDR:
+ temp = s->clock;
+ break;
+ default:
+ temp = 0xff;
+ }
+ } else { /* Basic Mode */
+ switch (addr) {
+ case SJA_BCAN_CTR: /* Control register, addr 0 */
+ temp = s->control;
+ break;
+ case SJA_BCAN_SR: /* Status register, addr 2 */
+ temp = s->status_bas;
+ break;
+ case SJA_BCAN_IR: /* Interrupt register, addr 3 */
+ temp = s->interrupt_bas;
+ s->interrupt_bas = 0;
+ if (s->rxmsg_cnt) {
+ s->interrupt_bas |= (1 << 0); /* Receive interrupt. */
+ }
+ can_sja_update_bas_irq(s);
+ break;
+ case 4:
+ temp = s->code;
+ break;
+ case 5:
+ temp = s->mask;
+ break;
+ case 20 ... 29:
+ temp = s->rx_buff[(s->rxbuf_start + addr - 20) % SJA_RCV_BUF_LEN];
+ break;
+ case 31:
+ temp = s->clock;
+ break;
+ default:
+ temp = 0xff;
+ break;
+ }
+ }
+ DPRINTF("read addr 0x%02x, %d bytes, content 0x%02lx\n",
+ (int)addr, size, (long unsigned int)temp);
+
+ return temp;
+}
+
+int can_sja_can_receive(CanBusClientState *client)
+{
+ CanSJA1000State *s = container_of(client, CanSJA1000State, bus_client);
+
+ if (s->clock & 0x80) { /* PeliCAN Mode */
+ if (s->mode & 0x01) { /* reset mode. */
+ return 0;
+ }
+ } else { /* BasicCAN mode */
+ if (s->control & 0x01) {
+ return 0;
+ }
+ }
+
+ return 1; /* always return 1, when operation mode */
+}
+
+ssize_t can_sja_receive(CanBusClientState *client, const qemu_can_frame *frames,
+ size_t frames_cnt)
+{
+ CanSJA1000State *s = container_of(client, CanSJA1000State, bus_client);
+ static uint8_t rcv[SJA_MSG_MAX_LEN];
+ int i;
+ int ret = -1;
+ const qemu_can_frame *frame = frames;
+
+ if (frames_cnt <= 0) {
+ return 0;
+ }
+ if (DEBUG_FILTER) {
+ can_display_msg("[cansja]: receive ", frame);
+ }
+
+ if (s->clock & 0x80) { /* PeliCAN Mode */
+
+ /* the CAN controller is receiving a message */
+ s->status_pel |= (1 << 4);
+
+ if (can_sja_accept_filter(s, frame) == 0) {
+ s->status_pel &= ~(1 << 4);
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: filter rejects message\n");
+ }
+ return ret;
+ }
+
+ ret = frame2buff_pel(frame, rcv);
+ if (ret < 0) {
+ s->status_pel &= ~(1 << 4);
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: message store failed\n");
+ }
+ return ret; /* maybe not support now. */
+ }
+
+ if (s->rx_cnt + ret > SJA_RCV_BUF_LEN) { /* Data overrun. */
+ s->status_pel |= (1 << 1); /* Overrun status */
+ s->interrupt_pel |= (1 << 3);
+ s->status_pel &= ~(1 << 4);
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: receive FIFO overrun\n");
+ }
+ can_sja_update_pel_irq(s);
+ return ret;
+ }
+ s->rx_cnt += ret;
+ s->rxmsg_cnt++;
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: message stored in receive FIFO\n");
+ }
+
+ for (i = 0; i < ret; i++) {
+ s->rx_buff[(s->rx_ptr++) % SJA_RCV_BUF_LEN] = rcv[i];
+ }
+ s->rx_ptr %= SJA_RCV_BUF_LEN; /* update the pointer. */
+
+ s->status_pel |= 0x01; /* Set the Receive Buffer Status. DS-p23 */
+ s->interrupt_pel |= 0x01;
+ s->status_pel &= ~(1 << 4);
+ s->status_pel |= (1 << 0);
+ can_sja_update_pel_irq(s);
+ } else { /* BasicCAN mode */
+
+ /* the CAN controller is receiving a message */
+ s->status_bas |= (1 << 4);
+
+ ret = frame2buff_bas(frame, rcv);
+ if (ret < 0) {
+ s->status_bas &= ~(1 << 4);
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: message store failed\n");
+ }
+ return ret; /* maybe not support now. */
+ }
+
+ if (s->rx_cnt + ret > SJA_RCV_BUF_LEN) { /* Data overrun. */
+ s->status_bas |= (1 << 1); /* Overrun status */
+ s->status_bas &= ~(1 << 4);
+ s->interrupt_bas |= (1 << 3);
+ can_sja_update_bas_irq(s);
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: receive FIFO overrun\n");
+ }
+ return ret;
+ }
+ s->rx_cnt += ret;
+ s->rxmsg_cnt++;
+
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: message stored\n");
+ }
+
+ for (i = 0; i < ret; i++) {
+ s->rx_buff[(s->rx_ptr++) % SJA_RCV_BUF_LEN] = rcv[i];
+ }
+ s->rx_ptr %= SJA_RCV_BUF_LEN; /* update the pointer. */
+
+ s->status_bas |= 0x01; /* Set the Receive Buffer Status. DS-p15 */
+ s->status_bas &= ~(1 << 4);
+ s->interrupt_bas |= (1 << 0);
+ can_sja_update_bas_irq(s);
+ }
+ return 1;
+}
+
+static CanBusClientInfo can_sja_bus_client_info = {
+ .can_receive = can_sja_can_receive,
+ .receive = can_sja_receive,
+};
+
+
+int can_sja_connect_to_bus(CanSJA1000State *s, CanBusState *bus)
+{
+ s->bus_client.info = &can_sja_bus_client_info;
+
+ if (can_bus_insert_client(bus, &s->bus_client) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void can_sja_disconnect(CanSJA1000State *s)
+{
+ can_bus_remove_client(&s->bus_client);
+}
+
+int can_sja_init(CanSJA1000State *s, qemu_irq irq)
+{
+ s->irq = irq;
+
+ qemu_irq_lower(s->irq);
+
+ can_sja_hardware_reset(s);
+
+ return 0;
+}
+
+const VMStateDescription vmstate_qemu_can_filter = {
+ .name = "qemu_can_filter",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(can_id, qemu_can_filter),
+ VMSTATE_UINT32(can_mask, qemu_can_filter),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int can_sja_post_load(void *opaque, int version_id)
+{
+ CanSJA1000State *s = opaque;
+ if (s->clock & 0x80) { /* PeliCAN Mode */
+ can_sja_update_pel_irq(s);
+ } else {
+ can_sja_update_bas_irq(s);
+ }
+ return 0;
+}
+
+/* VMState is needed for live migration of QEMU images */
+const VMStateDescription vmstate_can_sja = {
+ .name = "can_sja",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = can_sja_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(mode, CanSJA1000State),
+
+ VMSTATE_UINT8(status_pel, CanSJA1000State),
+ VMSTATE_UINT8(interrupt_pel, CanSJA1000State),
+ VMSTATE_UINT8(interrupt_en, CanSJA1000State),
+ VMSTATE_UINT8(rxmsg_cnt, CanSJA1000State),
+ VMSTATE_UINT8(rxbuf_start, CanSJA1000State),
+ VMSTATE_UINT8(clock, CanSJA1000State),
+
+ VMSTATE_BUFFER(code_mask, CanSJA1000State),
+ VMSTATE_BUFFER(tx_buff, CanSJA1000State),
+
+ VMSTATE_BUFFER(rx_buff, CanSJA1000State),
+
+ VMSTATE_UINT32(rx_ptr, CanSJA1000State),
+ VMSTATE_UINT32(rx_cnt, CanSJA1000State),
+
+ VMSTATE_UINT8(control, CanSJA1000State),
+
+ VMSTATE_UINT8(status_bas, CanSJA1000State),
+ VMSTATE_UINT8(interrupt_bas, CanSJA1000State),
+ VMSTATE_UINT8(code, CanSJA1000State),
+ VMSTATE_UINT8(mask, CanSJA1000State),
+
+ VMSTATE_STRUCT_ARRAY(filter, CanSJA1000State, 4, 0,
+ vmstate_qemu_can_filter, qemu_can_filter),
+
+
+ VMSTATE_END_OF_LIST()
+ }
+};
diff --git a/hw/net/can/can_sja1000.h b/hw/net/can/can_sja1000.h
new file mode 100644
index 0000000000..4731cbbd2a
--- /dev/null
+++ b/hw/net/can/can_sja1000.h
@@ -0,0 +1,146 @@
+/*
+ * CAN device - SJA1000 chip emulation for QEMU
+ *
+ * Copyright (c) 2013-2014 Jin Yang
+ * Copyright (c) 2014-2018 Pavel Pisa
+ *
+ * Initial development supported by Google GSoC 2013 from RTEMS project slot
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef HW_CAN_SJA1000_H
+#define HW_CAN_SJA1000_H
+
+#include "net/can_emu.h"
+
+#define CAN_SJA_MEM_SIZE 128
+
+/* The max size for a message buffer, EFF and DLC=8, DS-p39 */
+#define SJA_MSG_MAX_LEN 13
+/* The receive buffer size. */
+#define SJA_RCV_BUF_LEN 64
+
+typedef struct CanSJA1000State {
+ /* PeliCAN state and registers sorted by address */
+ uint8_t mode; /* 0 .. Mode register, DS-p26 */
+ /* 1 .. Command register */
+ uint8_t status_pel; /* 2 .. Status register, p15 */
+ uint8_t interrupt_pel; /* 3 .. Interrupt register */
+ uint8_t interrupt_en; /* 4 .. Interrupt Enable register */
+ uint8_t rxmsg_cnt; /* 29 .. RX message counter. DS-p49 */
+ uint8_t rxbuf_start; /* 30 .. RX buffer start address, DS-p49 */
+ uint8_t clock; /* 31 .. Clock Divider register, DS-p55 */
+
+ uint8_t code_mask[8]; /* 16~23 */
+ uint8_t tx_buff[13]; /* 96~108 .. transmit buffer */
+ /* 10~19 .. transmit buffer for BasicCAN */
+
+ uint8_t rx_buff[SJA_RCV_BUF_LEN]; /* 32~95 .. 64bytes Rx FIFO */
+ uint32_t rx_ptr; /* Count by bytes. */
+ uint32_t rx_cnt; /* Count by bytes. */
+
+ /* PeliCAN state and registers sorted by address */
+ uint8_t control; /* 0 .. Control register */
+ /* 1 .. Command register */
+ uint8_t status_bas; /* 2 .. Status register */
+ uint8_t interrupt_bas; /* 3 .. Interrupt register */
+ uint8_t code; /* 4 .. Acceptance code register */
+ uint8_t mask; /* 5 .. Acceptance mask register */
+
+ qemu_can_filter filter[4];
+
+ qemu_irq irq;
+ CanBusClientState bus_client;
+} CanSJA1000State;
+
+/* PeliCAN mode */
+enum SJA1000_PeliCAN_regs {
+ SJA_MOD = 0x00, /* Mode control register */
+ SJA_CMR = 0x01, /* Command register */
+ SJA_SR = 0x02, /* Status register */
+ SJA_IR = 0x03, /* Interrupt register */
+ SJA_IER = 0x04, /* Interrupt Enable */
+ SJA_BTR0 = 0x06, /* Bus Timing register 0 */
+ SJA_BTR1 = 0x07, /* Bus Timing register 1 */
+ SJA_OCR = 0x08, /* Output Control register */
+ SJA_ALC = 0x0b, /* Arbitration Lost Capture */
+ SJA_ECC = 0x0c, /* Error Code Capture */
+ SJA_EWLR = 0x0d, /* Error Warning Limit */
+ SJA_RXERR = 0x0e, /* RX Error Counter */
+ SJA_TXERR0 = 0x0e, /* TX Error Counter */
+ SJA_TXERR1 = 0x0f,
+ SJA_RMC = 0x1d, /* Rx Message Counter
+ * number of messages in RX FIFO
+ */
+ SJA_RBSA = 0x1e, /* Rx Buffer Start Addr
+ * address of current message
+ */
+ SJA_FRM = 0x10, /* Transmit Buffer
+ * write: Receive Buffer
+ * read: Frame Information
+ */
+/*
+ * ID bytes (11 bits in 0 and 1 for standard message or
+ * 16 bits in 0,1 and 13 bits in 2,3 for extended message)
+ * The most significant bit of ID is placed in MSB
+ * position of ID0 register.
+ */
+ SJA_ID0 = 0x11, /* ID for standard and extended frames */
+ SJA_ID1 = 0x12,
+ SJA_ID2 = 0x13, /* ID cont. for extended frames */
+ SJA_ID3 = 0x14,
+
+ SJA_DATS = 0x13, /* Data start standard frame */
+ SJA_DATE = 0x15, /* Data start extended frame */
+ SJA_ACR0 = 0x10, /* Acceptance Code (4 bytes) in RESET mode */
+ SJA_AMR0 = 0x14, /* Acceptance Mask (4 bytes) in RESET mode */
+ SJA_PeliCAN_AC_LEN = 4, /* 4 bytes */
+ SJA_CDR = 0x1f /* Clock Divider */
+};
+
+
+/* BasicCAN mode */
+enum SJA1000_BasicCAN_regs {
+ SJA_BCAN_CTR = 0x00, /* Control register */
+ SJA_BCAN_CMR = 0x01, /* Command register */
+ SJA_BCAN_SR = 0x02, /* Status register */
+ SJA_BCAN_IR = 0x03 /* Interrupt register */
+};
+
+void can_sja_hardware_reset(CanSJA1000State *s);
+
+void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val,
+ unsigned size);
+
+uint64_t can_sja_mem_read(CanSJA1000State *s, hwaddr addr, unsigned size);
+
+int can_sja_connect_to_bus(CanSJA1000State *s, CanBusState *bus);
+
+void can_sja_disconnect(CanSJA1000State *s);
+
+int can_sja_init(CanSJA1000State *s, qemu_irq irq);
+
+int can_sja_can_receive(CanBusClientState *client);
+
+ssize_t can_sja_receive(CanBusClientState *client,
+ const qemu_can_frame *frames, size_t frames_cnt);
+
+extern const VMStateDescription vmstate_can_sja;
+
+#endif
diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
index 191398a3d5..16a9417a85 100644
--- a/hw/net/e1000e.c
+++ b/hw/net/e1000e.c
@@ -675,7 +675,6 @@ static void e1000e_class_init(ObjectClass *class, void *data)
c->revision = 0;
c->romfile = "efi-e1000e.rom";
c->class_id = PCI_CLASS_NETWORK_ETHERNET;
- c->is_express = 1;
dc->desc = "Intel 82574L GbE Controller";
dc->reset = e1000e_qdev_reset;
diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c
index 3dbacc6cea..d117e20325 100644
--- a/hw/pci-bridge/gen_pcie_root_port.c
+++ b/hw/pci-bridge/gen_pcie_root_port.c
@@ -101,6 +101,7 @@ static void gen_rp_realize(DeviceState *dev, Error **errp)
static const VMStateDescription vmstate_rp_dev = {
.name = "pcie-root-port",
+ .priority = MIG_PRI_PCI_BUS,
.version_id = 1,
.minimum_version_id = 1,
.post_load = pcie_cap_slot_post_load,
diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c
index f557b12f90..10e590e5c6 100644
--- a/hw/pci-bridge/i82801b11.c
+++ b/hw/pci-bridge/i82801b11.c
@@ -78,6 +78,7 @@ err_bridge:
static const VMStateDescription i82801b11_bridge_dev_vmstate = {
.name = "i82801b11_bridge",
+ .priority = MIG_PRI_PCI_BUS,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
VMSTATE_END_OF_LIST()
@@ -96,6 +97,7 @@ static void i82801b11_bridge_class_init(ObjectClass *klass, void *data)
k->realize = i82801b11_bridge_realize;
k->config_write = pci_bridge_write_config;
dc->vmsd = &i82801b11_bridge_dev_vmstate;
+ dc->reset = pci_bridge_reset;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c
index 79fa84d7b9..a451d74ee6 100644
--- a/hw/pci-bridge/ioh3420.c
+++ b/hw/pci-bridge/ioh3420.c
@@ -82,6 +82,7 @@ static void ioh3420_interrupts_uninit(PCIDevice *d)
static const VMStateDescription vmstate_ioh3420 = {
.name = "ioh-3240-express-root-port",
+ .priority = MIG_PRI_PCI_BUS,
.version_id = 1,
.minimum_version_id = 1,
.post_load = pcie_cap_slot_post_load,
diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c
index d56f6638c2..b2d861d216 100644
--- a/hw/pci-bridge/pci_bridge_dev.c
+++ b/hw/pci-bridge/pci_bridge_dev.c
@@ -174,6 +174,7 @@ static bool pci_device_shpc_present(void *opaque, int version_id)
static const VMStateDescription pci_bridge_dev_vmstate = {
.name = "pci_bridge",
+ .priority = MIG_PRI_PCI_BUS,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
SHPC_VMSTATE(shpc, PCIDevice, pci_device_shpc_present),
diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c
index a4d827c99d..04cf5a6a92 100644
--- a/hw/pci-bridge/pcie_pci_bridge.c
+++ b/hw/pci-bridge/pcie_pci_bridge.c
@@ -129,6 +129,7 @@ static Property pcie_pci_bridge_dev_properties[] = {
static const VMStateDescription pcie_pci_bridge_dev_vmstate = {
.name = TYPE_PCIE_PCI_BRIDGE_DEV,
+ .priority = MIG_PRI_PCI_BUS,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
SHPC_VMSTATE(shpc, PCIDevice, NULL),
@@ -169,7 +170,6 @@ static void pcie_pci_bridge_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
- k->is_express = 1;
k->is_bridge = 1;
k->vendor_id = PCI_VENDOR_ID_REDHAT;
k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE;
@@ -178,7 +178,6 @@ static void pcie_pci_bridge_class_init(ObjectClass *klass, void *data)
k->config_write = pcie_pci_bridge_write_config;
dc->vmsd = &pcie_pci_bridge_dev_vmstate;
dc->props = pcie_pci_bridge_dev_properties;
- dc->vmsd = &pcie_pci_bridge_dev_vmstate;
dc->reset = &pcie_pci_bridge_reset;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
hc->plug = pcie_pci_bridge_hotplug_cb;
diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c
index 9b6e4ce512..45f9e8cd4a 100644
--- a/hw/pci-bridge/pcie_root_port.c
+++ b/hw/pci-bridge/pcie_root_port.c
@@ -145,7 +145,6 @@ static void rp_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->is_express = 1;
k->is_bridge = 1;
k->config_write = rp_write_config;
k->realize = rp_realize;
diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c
index 1e09d2afb7..b202657954 100644
--- a/hw/pci-bridge/xio3130_downstream.c
+++ b/hw/pci-bridge/xio3130_downstream.c
@@ -161,6 +161,7 @@ static Property xio3130_downstream_props[] = {
static const VMStateDescription vmstate_xio3130_downstream = {
.name = "xio3130-express-downstream-port",
+ .priority = MIG_PRI_PCI_BUS,
.version_id = 1,
.minimum_version_id = 1,
.post_load = pcie_cap_slot_post_load,
@@ -177,7 +178,6 @@ static void xio3130_downstream_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->is_express = 1;
k->is_bridge = 1;
k->config_write = xio3130_downstream_write_config;
k->realize = xio3130_downstream_realize;
diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c
index df5692501b..bca2f9a5ea 100644
--- a/hw/pci-bridge/xio3130_upstream.c
+++ b/hw/pci-bridge/xio3130_upstream.c
@@ -132,6 +132,7 @@ PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction,
static const VMStateDescription vmstate_xio3130_upstream = {
.name = "xio3130-express-upstream-port",
+ .priority = MIG_PRI_PCI_BUS,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
@@ -147,7 +148,6 @@ static void xio3130_upstream_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->is_express = 1;
k->is_bridge = 1;
k->config_write = xio3130_upstream_write_config;
k->realize = xio3130_upstream_realize;
diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c
index 53b561f81f..044e312dc1 100644
--- a/hw/pci-host/xilinx-pcie.c
+++ b/hw/pci-host/xilinx-pcie.c
@@ -297,7 +297,6 @@ static void xilinx_pcie_root_class_init(ObjectClass *klass, void *data)
k->device_id = 0x7021;
k->revision = 0;
k->class_id = PCI_CLASS_BRIDGE_HOST;
- k->is_express = true;
k->is_bridge = true;
k->realize = xilinx_pcie_root_realize;
k->exit = pci_bridge_exitfn;
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 239f73d711..e006b6ac71 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -2007,11 +2007,15 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp)
{
PCIDevice *pci_dev = (PCIDevice *)qdev;
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev);
+ ObjectClass *klass = OBJECT_CLASS(pc);
Error *local_err = NULL;
bool is_default_rom;
- /* initialize cap_present for pci_is_express() and pci_config_size() */
- if (pc->is_express) {
+ /* initialize cap_present for pci_is_express() and pci_config_size(),
+ * Note that hybrid PCIs are not set automatically and need to manage
+ * QEMU_PCI_CAP_EXPRESS manually */
+ if (object_class_dynamic_cast(klass, INTERFACE_PCIE_DEVICE) &&
+ !object_class_dynamic_cast(klass, INTERFACE_CONVENTIONAL_PCI_DEVICE)) {
pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
}
diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c
index b2e50c36a0..40a39f57cb 100644
--- a/hw/pci/pci_bridge.c
+++ b/hw/pci/pci_bridge.c
@@ -412,22 +412,36 @@ void pci_bridge_map_irq(PCIBridge *br, const char* bus_name,
int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset,
uint32_t bus_reserve, uint64_t io_reserve,
- uint32_t mem_non_pref_reserve,
- uint32_t mem_pref_32_reserve,
+ uint64_t mem_non_pref_reserve,
+ uint64_t mem_pref_32_reserve,
uint64_t mem_pref_64_reserve,
Error **errp)
{
- if (mem_pref_32_reserve != (uint32_t)-1 &&
+ if (mem_pref_32_reserve != (uint64_t)-1 &&
mem_pref_64_reserve != (uint64_t)-1) {
error_setg(errp,
"PCI resource reserve cap: PREF32 and PREF64 conflict");
return -EINVAL;
}
+ if (mem_non_pref_reserve != (uint64_t)-1 &&
+ mem_non_pref_reserve >= (1ULL << 32)) {
+ error_setg(errp,
+ "PCI resource reserve cap: mem-reserve must be less than 4G");
+ return -EINVAL;
+ }
+
+ if (mem_pref_32_reserve != (uint64_t)-1 &&
+ mem_pref_32_reserve >= (1ULL << 32)) {
+ error_setg(errp,
+ "PCI resource reserve cap: pref32-reserve must be less than 4G");
+ return -EINVAL;
+ }
+
if (bus_reserve == (uint32_t)-1 &&
io_reserve == (uint64_t)-1 &&
- mem_non_pref_reserve == (uint32_t)-1 &&
- mem_pref_32_reserve == (uint32_t)-1 &&
+ mem_non_pref_reserve == (uint64_t)-1 &&
+ mem_pref_32_reserve == (uint64_t)-1 &&
mem_pref_64_reserve == (uint64_t)-1) {
return 0;
}
diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
index 3e38e9e8aa..ba1afa3c1e 100644
--- a/hw/scsi/megasas.c
+++ b/hw/scsi/megasas.c
@@ -2447,7 +2447,6 @@ typedef struct MegasasInfo {
uint16_t subsystem_id;
int ioport_bar;
int mmio_bar;
- bool is_express;
int osts;
const VMStateDescription *vmsd;
Property *props;
@@ -2465,7 +2464,6 @@ static struct MegasasInfo megasas_devices[] = {
.ioport_bar = 2,
.mmio_bar = 0,
.osts = MFI_1078_RM | 1,
- .is_express = false,
.vmsd = &vmstate_megasas_gen1,
.props = megasas_properties_gen1,
.interfaces = (InterfaceInfo[]) {
@@ -2482,7 +2480,6 @@ static struct MegasasInfo megasas_devices[] = {
.ioport_bar = 0,
.mmio_bar = 1,
.osts = MFI_GEN2_RM,
- .is_express = true,
.vmsd = &vmstate_megasas_gen2,
.props = megasas_properties_gen2,
.interfaces = (InterfaceInfo[]) {
@@ -2506,7 +2503,6 @@ static void megasas_class_init(ObjectClass *oc, void *data)
pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
pc->subsystem_id = info->subsystem_id;
pc->class_id = PCI_CLASS_STORAGE_RAID;
- pc->is_express = info->is_express;
e->mmio_bar = info->mmio_bar;
e->ioport_bar = info->ioport_bar;
e->osts = info->osts;
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index add4b3f4a4..1c33322ba6 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -175,6 +175,7 @@ fail_vrings:
aio_context_release(s->ctx);
for (i = 0; i < vs->conf.num_queues + 2; i++) {
virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
+ virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
}
k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
fail_guest_notifiers:
@@ -213,6 +214,7 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev)
for (i = 0; i < vs->conf.num_queues + 2; i++) {
virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
+ virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
}
/* Clean up guest notifier (irq) */
diff --git a/hw/sd/core.c b/hw/sd/core.c
index 295dc44ab7..3c6eae6c88 100644
--- a/hw/sd/core.c
+++ b/hw/sd/core.c
@@ -23,6 +23,12 @@
#include "hw/qdev-core.h"
#include "sysemu/block-backend.h"
#include "hw/sd/sd.h"
+#include "trace.h"
+
+static inline const char *sdbus_name(SDBus *sdbus)
+{
+ return sdbus->qbus.name;
+}
static SDState *get_card(SDBus *sdbus)
{
@@ -35,10 +41,58 @@ static SDState *get_card(SDBus *sdbus)
return SD_CARD(kid->child);
}
+uint8_t sdbus_get_dat_lines(SDBus *sdbus)
+{
+ SDState *slave = get_card(sdbus);
+ uint8_t dat_lines = 0b1111; /* 4 bit bus width */
+
+ if (slave) {
+ SDCardClass *sc = SD_CARD_GET_CLASS(slave);
+
+ if (sc->get_dat_lines) {
+ dat_lines = sc->get_dat_lines(slave);
+ }
+ }
+ trace_sdbus_get_dat_lines(sdbus_name(sdbus), dat_lines);
+
+ return dat_lines;
+}
+
+bool sdbus_get_cmd_line(SDBus *sdbus)
+{
+ SDState *slave = get_card(sdbus);
+ bool cmd_line = true;
+
+ if (slave) {
+ SDCardClass *sc = SD_CARD_GET_CLASS(slave);
+
+ if (sc->get_cmd_line) {
+ cmd_line = sc->get_cmd_line(slave);
+ }
+ }
+ trace_sdbus_get_cmd_line(sdbus_name(sdbus), cmd_line);
+
+ return cmd_line;
+}
+
+void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts)
+{
+ SDState *card = get_card(sdbus);
+
+ trace_sdbus_set_voltage(sdbus_name(sdbus), millivolts);
+ if (card) {
+ SDCardClass *sc = SD_CARD_GET_CLASS(card);
+
+ assert(sc->set_voltage);
+ sc->set_voltage(card, millivolts);
+ }
+}
+
int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response)
{
SDState *card = get_card(sdbus);
+ trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg, req->crc);
if (card) {
SDCardClass *sc = SD_CARD_GET_CLASS(card);
@@ -52,6 +106,7 @@ void sdbus_write_data(SDBus *sdbus, uint8_t value)
{
SDState *card = get_card(sdbus);
+ trace_sdbus_write(sdbus_name(sdbus), value);
if (card) {
SDCardClass *sc = SD_CARD_GET_CLASS(card);
@@ -62,14 +117,16 @@ void sdbus_write_data(SDBus *sdbus, uint8_t value)
uint8_t sdbus_read_data(SDBus *sdbus)
{
SDState *card = get_card(sdbus);
+ uint8_t value = 0;
if (card) {
SDCardClass *sc = SD_CARD_GET_CLASS(card);
- return sc->read_data(card);
+ value = sc->read_data(card);
}
+ trace_sdbus_read(sdbus_name(sdbus), value);
- return 0;
+ return value;
}
bool sdbus_data_ready(SDBus *sdbus)
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 73e405a04f..9ac9b63ff8 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -126,8 +126,32 @@ struct SDState {
BlockBackend *blk;
bool enable;
+ uint8_t dat_lines;
+ bool cmd_line;
};
+static uint8_t sd_get_dat_lines(SDState *sd)
+{
+ return sd->enable ? sd->dat_lines : 0;
+}
+
+static bool sd_get_cmd_line(SDState *sd)
+{
+ return sd->enable ? sd->cmd_line : false;
+}
+
+static void sd_set_voltage(SDState *sd, uint16_t millivolts)
+{
+ switch (millivolts) {
+ case 3001 ... 3600: /* SD_VOLTAGE_3_3V */
+ case 2001 ... 3000: /* SD_VOLTAGE_3_0V */
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "SD card voltage not supported: %.3fV",
+ millivolts / 1000.f);
+ }
+}
+
static void sd_set_mode(SDState *sd)
{
switch (sd->state) {
@@ -445,6 +469,8 @@ static void sd_reset(DeviceState *dev)
sd->blk_len = 0x200;
sd->pwd_len = 0;
sd->expecting_acmd = false;
+ sd->dat_lines = 0xf;
+ sd->cmd_line = true;
sd->multi_blk_cnt = 0;
}
@@ -1926,6 +1952,9 @@ static void sd_class_init(ObjectClass *klass, void *data)
dc->reset = sd_reset;
dc->bus_type = TYPE_SD_BUS;
+ sc->set_voltage = sd_set_voltage;
+ sc->get_dat_lines = sd_get_dat_lines;
+ sc->get_cmd_line = sd_get_cmd_line;
sc->do_command = sd_do_command;
sc->write_data = sd_write_data;
sc->read_data = sd_read_data;
diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h
index 0991acd724..756ef3f3c2 100644
--- a/hw/sd/sdhci-internal.h
+++ b/hw/sd/sdhci-internal.h
@@ -24,6 +24,8 @@
#ifndef SDHCI_INTERNAL_H
#define SDHCI_INTERNAL_H
+#include "hw/registerfields.h"
+
/* R/W SDMA System Address register 0x0 */
#define SDHC_SYSAD 0x00
@@ -41,6 +43,7 @@
#define SDHC_TRNS_DMA 0x0001
#define SDHC_TRNS_BLK_CNT_EN 0x0002
#define SDHC_TRNS_ACMD12 0x0004
+#define SDHC_TRNS_ACMD23 0x0008 /* since v3 */
#define SDHC_TRNS_READ 0x0010
#define SDHC_TRNS_MULTI 0x0020
#define SDHC_TRNMOD_MASK 0x0037
@@ -79,15 +82,19 @@
#define SDHC_CARD_PRESENT 0x00010000
#define SDHC_CARD_DETECT 0x00040000
#define SDHC_WRITE_PROTECT 0x00080000
+FIELD(SDHC_PRNSTS, DAT_LVL, 20, 4);
+FIELD(SDHC_PRNSTS, CMD_LVL, 24, 1);
#define TRANSFERRING_DATA(x) \
((x) & (SDHC_DOING_READ | SDHC_DOING_WRITE))
/* R/W Host control Register 0x0 */
#define SDHC_HOSTCTL 0x28
#define SDHC_CTRL_LED 0x01
+#define SDHC_CTRL_DATATRANSFERWIDTH 0x02 /* SD mode only */
+#define SDHC_CTRL_HIGH_SPEED 0x04
#define SDHC_CTRL_DMA_CHECK_MASK 0x18
#define SDHC_CTRL_SDMA 0x00
-#define SDHC_CTRL_ADMA1_32 0x08
+#define SDHC_CTRL_ADMA1_32 0x08 /* NOT ALLOWED since v2 */
#define SDHC_CTRL_ADMA2_32 0x10
#define SDHC_CTRL_ADMA2_64 0x18
#define SDHC_DMA_TYPE(x) ((x) & SDHC_CTRL_DMA_CHECK_MASK)
@@ -96,10 +103,10 @@
#define SDHC_CTRL_CDTEST_INS 0x40
#define SDHC_CTRL_CDTEST_EN 0x80
-
/* R/W Power Control Register 0x0 */
#define SDHC_PWRCON 0x29
#define SDHC_POWER_ON (1 << 0)
+FIELD(SDHC_PWRCON, BUS_VOLTAGE, 1, 3);
/* R/W Block Gap Control Register 0x0 */
#define SDHC_BLKGAP 0x2A
@@ -122,6 +129,7 @@
/* R/W Timeout Control Register 0x0 */
#define SDHC_TIMEOUTCON 0x2E
+FIELD(SDHC_TIMEOUTCON, COUNTER, 0, 4);
/* R/W Software Reset Register 0x0 */
#define SDHC_SWRST 0x2F
@@ -178,17 +186,62 @@
/* ROC Auto CMD12 error status register 0x0 */
#define SDHC_ACMD12ERRSTS 0x3C
+FIELD(SDHC_ACMD12ERRSTS, TIMEOUT_ERR, 1, 1);
+FIELD(SDHC_ACMD12ERRSTS, CRC_ERR, 2, 1);
+FIELD(SDHC_ACMD12ERRSTS, INDEX_ERR, 4, 1);
+
+/* Host Control Register 2 (since v3) */
+#define SDHC_HOSTCTL2 0x3E
+FIELD(SDHC_HOSTCTL2, UHS_MODE_SEL, 0, 3);
+FIELD(SDHC_HOSTCTL2, V18_ENA, 3, 1); /* UHS-I only */
+FIELD(SDHC_HOSTCTL2, DRIVER_STRENGTH, 4, 2); /* UHS-I only */
+FIELD(SDHC_HOSTCTL2, EXECUTE_TUNING, 6, 1); /* UHS-I only */
+FIELD(SDHC_HOSTCTL2, SAMPLING_CLKSEL, 7, 1); /* UHS-I only */
+FIELD(SDHC_HOSTCTL2, UHS_II_ENA, 8, 1); /* since v4 */
+FIELD(SDHC_HOSTCTL2, ADMA2_LENGTH, 10, 1); /* since v4 */
+FIELD(SDHC_HOSTCTL2, CMD23_ENA, 11, 1); /* since v4 */
+FIELD(SDHC_HOSTCTL2, VERSION4, 12, 1); /* since v4 */
+FIELD(SDHC_HOSTCTL2, ASYNC_INT, 14, 1);
+FIELD(SDHC_HOSTCTL2, PRESET_ENA, 15, 1);
/* HWInit Capabilities Register 0x05E80080 */
#define SDHC_CAPAB 0x40
-#define SDHC_CAN_DO_DMA 0x00400000
-#define SDHC_CAN_DO_ADMA2 0x00080000
-#define SDHC_CAN_DO_ADMA1 0x00100000
-#define SDHC_64_BIT_BUS_SUPPORT (1 << 28)
-#define SDHC_CAPAB_BLOCKSIZE(x) (((x) >> 16) & 0x3)
+FIELD(SDHC_CAPAB, TOCLKFREQ, 0, 6);
+FIELD(SDHC_CAPAB, TOUNIT, 7, 1);
+FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8);
+FIELD(SDHC_CAPAB, MAXBLOCKLENGTH, 16, 2);
+FIELD(SDHC_CAPAB, EMBEDDED_8BIT, 18, 1); /* since v3 */
+FIELD(SDHC_CAPAB, ADMA2, 19, 1); /* since v2 */
+FIELD(SDHC_CAPAB, ADMA1, 20, 1); /* v1 only? */
+FIELD(SDHC_CAPAB, HIGHSPEED, 21, 1);
+FIELD(SDHC_CAPAB, SDMA, 22, 1);
+FIELD(SDHC_CAPAB, SUSPRESUME, 23, 1);
+FIELD(SDHC_CAPAB, V33, 24, 1);
+FIELD(SDHC_CAPAB, V30, 25, 1);
+FIELD(SDHC_CAPAB, V18, 26, 1);
+FIELD(SDHC_CAPAB, BUS64BIT_V4, 27, 1); /* since v4.10 */
+FIELD(SDHC_CAPAB, BUS64BIT, 28, 1); /* since v2 */
+FIELD(SDHC_CAPAB, ASYNC_INT, 29, 1); /* since v3 */
+FIELD(SDHC_CAPAB, SLOT_TYPE, 30, 2); /* since v3 */
+FIELD(SDHC_CAPAB, BUS_SPEED, 32, 3); /* since v3 */
+FIELD(SDHC_CAPAB, UHS_II, 35, 8); /* since v4.20 */
+FIELD(SDHC_CAPAB, DRIVER_STRENGTH, 36, 3); /* since v3 */
+FIELD(SDHC_CAPAB, DRIVER_TYPE_A, 36, 1); /* since v3 */
+FIELD(SDHC_CAPAB, DRIVER_TYPE_C, 37, 1); /* since v3 */
+FIELD(SDHC_CAPAB, DRIVER_TYPE_D, 38, 1); /* since v3 */
+FIELD(SDHC_CAPAB, TIMER_RETUNING, 40, 4); /* since v3 */
+FIELD(SDHC_CAPAB, SDR50_TUNING, 45, 1); /* since v3 */
+FIELD(SDHC_CAPAB, RETUNING_MODE, 46, 2); /* since v3 */
+FIELD(SDHC_CAPAB, CLOCK_MULT, 48, 8); /* since v3 */
+FIELD(SDHC_CAPAB, ADMA3, 59, 1); /* since v4.20 */
+FIELD(SDHC_CAPAB, V18_VDD2, 60, 1); /* since v4.20 */
/* HWInit Maximum Current Capabilities Register 0x0 */
#define SDHC_MAXCURR 0x48
+FIELD(SDHC_MAXCURR, V33_VDD1, 0, 8);
+FIELD(SDHC_MAXCURR, V30_VDD1, 8, 8);
+FIELD(SDHC_MAXCURR, V18_VDD1, 16, 8);
+FIELD(SDHC_MAXCURR, V18_VDD2, 32, 8); /* since v4.20 */
/* W Force Event Auto CMD12 Error Interrupt Register 0x0000 */
#define SDHC_FEAER 0x50
@@ -216,9 +269,9 @@
/* Slot interrupt status */
#define SDHC_SLOT_INT_STATUS 0xFC
-/* HWInit Host Controller Version Register 0x0401 */
+/* HWInit Host Controller Version Register */
#define SDHC_HCVER 0xFE
-#define SD_HOST_SPECv2_VERS 0x2401
+#define SDHC_HCVER_VENDOR 0x24
#define SDHC_REGISTERS_MAP_SIZE 0x100
#define SDHC_INSERTION_DELAY (NANOSECONDS_PER_SECOND)
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index ee95e78aeb..97b4a473c8 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -23,6 +23,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "sysemu/block-backend.h"
@@ -33,72 +34,194 @@
#include "hw/sd/sdhci.h"
#include "sdhci-internal.h"
#include "qemu/log.h"
+#include "qemu/cutils.h"
#include "trace.h"
#define TYPE_SDHCI_BUS "sdhci-bus"
#define SDHCI_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_SDHCI_BUS)
+#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
+
/* Default SD/MMC host controller features information, which will be
* presented in CAPABILITIES register of generic SD host controller at reset.
- * If not stated otherwise:
- * 0 - not supported, 1 - supported, other - prohibited.
+ *
+ * support:
+ * - 3.3v and 1.8v voltages
+ * - SDMA/ADMA1/ADMA2
+ * - high-speed
+ * max host controller R/W buffers size: 512B
+ * max clock frequency for SDclock: 52 MHz
+ * timeout clock frequency: 52 MHz
+ *
+ * does not support:
+ * - 3.0v voltage
+ * - 64-bit system bus
+ * - suspend/resume
*/
-#define SDHC_CAPAB_64BITBUS 0ul /* 64-bit System Bus Support */
-#define SDHC_CAPAB_18V 1ul /* Voltage support 1.8v */
-#define SDHC_CAPAB_30V 0ul /* Voltage support 3.0v */
-#define SDHC_CAPAB_33V 1ul /* Voltage support 3.3v */
-#define SDHC_CAPAB_SUSPRESUME 0ul /* Suspend/resume support */
-#define SDHC_CAPAB_SDMA 1ul /* SDMA support */
-#define SDHC_CAPAB_HIGHSPEED 1ul /* High speed support */
-#define SDHC_CAPAB_ADMA1 1ul /* ADMA1 support */
-#define SDHC_CAPAB_ADMA2 1ul /* ADMA2 support */
-/* Maximum host controller R/W buffers size
- * Possible values: 512, 1024, 2048 bytes */
-#define SDHC_CAPAB_MAXBLOCKLENGTH 512ul
-/* Maximum clock frequency for SDclock in MHz
- * value in range 10-63 MHz, 0 - not defined */
-#define SDHC_CAPAB_BASECLKFREQ 52ul
-#define SDHC_CAPAB_TOUNIT 1ul /* Timeout clock unit 0 - kHz, 1 - MHz */
-/* Timeout clock frequency 1-63, 0 - not defined */
-#define SDHC_CAPAB_TOCLKFREQ 52ul
-
-/* Now check all parameters and calculate CAPABILITIES REGISTER value */
-#if SDHC_CAPAB_64BITBUS > 1 || SDHC_CAPAB_18V > 1 || SDHC_CAPAB_30V > 1 || \
- SDHC_CAPAB_33V > 1 || SDHC_CAPAB_SUSPRESUME > 1 || SDHC_CAPAB_SDMA > 1 || \
- SDHC_CAPAB_HIGHSPEED > 1 || SDHC_CAPAB_ADMA2 > 1 || SDHC_CAPAB_ADMA1 > 1 ||\
- SDHC_CAPAB_TOUNIT > 1
-#error Capabilities features can have value 0 or 1 only!
-#endif
-
-#if SDHC_CAPAB_MAXBLOCKLENGTH == 512
-#define MAX_BLOCK_LENGTH 0ul
-#elif SDHC_CAPAB_MAXBLOCKLENGTH == 1024
-#define MAX_BLOCK_LENGTH 1ul
-#elif SDHC_CAPAB_MAXBLOCKLENGTH == 2048
-#define MAX_BLOCK_LENGTH 2ul
-#else
-#error Max host controller block size can have value 512, 1024 or 2048 only!
-#endif
-
-#if (SDHC_CAPAB_BASECLKFREQ > 0 && SDHC_CAPAB_BASECLKFREQ < 10) || \
- SDHC_CAPAB_BASECLKFREQ > 63
-#error SDclock frequency can have value in range 0, 10-63 only!
-#endif
-
-#if SDHC_CAPAB_TOCLKFREQ > 63
-#error Timeout clock frequency can have value in range 0-63 only!
-#endif
-
-#define SDHC_CAPAB_REG_DEFAULT \
- ((SDHC_CAPAB_64BITBUS << 28) | (SDHC_CAPAB_18V << 26) | \
- (SDHC_CAPAB_30V << 25) | (SDHC_CAPAB_33V << 24) | \
- (SDHC_CAPAB_SUSPRESUME << 23) | (SDHC_CAPAB_SDMA << 22) | \
- (SDHC_CAPAB_HIGHSPEED << 21) | (SDHC_CAPAB_ADMA1 << 20) | \
- (SDHC_CAPAB_ADMA2 << 19) | (MAX_BLOCK_LENGTH << 16) | \
- (SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \
- (SDHC_CAPAB_TOCLKFREQ))
+#define SDHC_CAPAB_REG_DEFAULT 0x057834b4
-#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
+static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
+{
+ return 1 << (9 + FIELD_EX32(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH));
+}
+
+/* return true on error */
+static bool sdhci_check_capab_freq_range(SDHCIState *s, const char *desc,
+ uint8_t freq, Error **errp)
+{
+ if (s->sd_spec_version >= 3) {
+ return false;
+ }
+ switch (freq) {
+ case 0:
+ case 10 ... 63:
+ break;
+ default:
+ error_setg(errp, "SD %s clock frequency can have value"
+ "in range 0-63 only", desc);
+ return true;
+ }
+ return false;
+}
+
+static void sdhci_check_capareg(SDHCIState *s, Error **errp)
+{
+ uint64_t msk = s->capareg;
+ uint32_t val;
+ bool y;
+
+ switch (s->sd_spec_version) {
+ case 4:
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, BUS64BIT_V4);
+ trace_sdhci_capareg("64-bit system bus (v4)", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, BUS64BIT_V4, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, UHS_II);
+ trace_sdhci_capareg("UHS-II", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, UHS_II, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, ADMA3);
+ trace_sdhci_capareg("ADMA3", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, ADMA3, 0);
+
+ /* fallthrough */
+ case 3:
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, ASYNC_INT);
+ trace_sdhci_capareg("async interrupt", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, ASYNC_INT, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, SLOT_TYPE);
+ if (val) {
+ error_setg(errp, "slot-type not supported");
+ return;
+ }
+ trace_sdhci_capareg("slot type", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, SLOT_TYPE, 0);
+
+ if (val != 2) {
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, EMBEDDED_8BIT);
+ trace_sdhci_capareg("8-bit bus", val);
+ }
+ msk = FIELD_DP64(msk, SDHC_CAPAB, EMBEDDED_8BIT, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, BUS_SPEED);
+ trace_sdhci_capareg("bus speed mask", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, BUS_SPEED, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, DRIVER_STRENGTH);
+ trace_sdhci_capareg("driver strength mask", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, DRIVER_STRENGTH, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, TIMER_RETUNING);
+ trace_sdhci_capareg("timer re-tuning", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, TIMER_RETUNING, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, SDR50_TUNING);
+ trace_sdhci_capareg("use SDR50 tuning", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, SDR50_TUNING, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, RETUNING_MODE);
+ trace_sdhci_capareg("re-tuning mode", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, RETUNING_MODE, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, CLOCK_MULT);
+ trace_sdhci_capareg("clock multiplier", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, CLOCK_MULT, 0);
+
+ /* fallthrough */
+ case 2: /* default version */
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, ADMA2);
+ trace_sdhci_capareg("ADMA2", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, ADMA2, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, ADMA1);
+ trace_sdhci_capareg("ADMA1", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, ADMA1, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, BUS64BIT);
+ trace_sdhci_capareg("64-bit system bus (v3)", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, BUS64BIT, 0);
+
+ /* fallthrough */
+ case 1:
+ y = FIELD_EX64(s->capareg, SDHC_CAPAB, TOUNIT);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, TOUNIT, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, TOCLKFREQ);
+ trace_sdhci_capareg(y ? "timeout (MHz)" : "Timeout (KHz)", val);
+ if (sdhci_check_capab_freq_range(s, "timeout", val, errp)) {
+ return;
+ }
+ msk = FIELD_DP64(msk, SDHC_CAPAB, TOCLKFREQ, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, BASECLKFREQ);
+ trace_sdhci_capareg(y ? "base (MHz)" : "Base (KHz)", val);
+ if (sdhci_check_capab_freq_range(s, "base", val, errp)) {
+ return;
+ }
+ msk = FIELD_DP64(msk, SDHC_CAPAB, BASECLKFREQ, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH);
+ if (val >= 3) {
+ error_setg(errp, "block size can be 512, 1024 or 2048 only");
+ return;
+ }
+ trace_sdhci_capareg("max block length", sdhci_get_fifolen(s));
+ msk = FIELD_DP64(msk, SDHC_CAPAB, MAXBLOCKLENGTH, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, HIGHSPEED);
+ trace_sdhci_capareg("high speed", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, HIGHSPEED, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, SDMA);
+ trace_sdhci_capareg("SDMA", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, SDMA, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, SUSPRESUME);
+ trace_sdhci_capareg("suspend/resume", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, SUSPRESUME, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, V33);
+ trace_sdhci_capareg("3.3v", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, V33, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, V30);
+ trace_sdhci_capareg("3.0v", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, V30, 0);
+
+ val = FIELD_EX64(s->capareg, SDHC_CAPAB, V18);
+ trace_sdhci_capareg("1.8v", val);
+ msk = FIELD_DP64(msk, SDHC_CAPAB, V18, 0);
+ break;
+
+ default:
+ error_setg(errp, "Unsupported spec version: %u", s->sd_spec_version);
+ }
+ if (msk) {
+ qemu_log_mask(LOG_UNIMP,
+ "SDHCI: unknown CAPAB mask: 0x%016" PRIx64 "\n", msk);
+ }
+}
static uint8_t sdhci_slotint(SDHCIState *s)
{
@@ -173,7 +296,8 @@ static void sdhci_reset(SDHCIState *s)
timer_del(s->insert_timer);
timer_del(s->transfer_timer);
- /* Set all registers to 0. Capabilities registers are not cleared
+
+ /* Set all registers to 0. Capabilities/Version registers are not cleared
* and assumed to always preserve their value, given to them during
* initialization */
memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad);
@@ -292,19 +416,35 @@ static void sdhci_end_transfer(SDHCIState *s)
/*
* Programmed i/o data transfer
*/
+#define BLOCK_SIZE_MASK (4 * K_BYTE - 1)
/* Fill host controller's read buffer with BLKSIZE bytes of data from card */
static void sdhci_read_block_from_card(SDHCIState *s)
{
int index = 0;
+ uint8_t data;
+ const uint16_t blk_size = s->blksize & BLOCK_SIZE_MASK;
if ((s->trnmod & SDHC_TRNS_MULTI) &&
(s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0)) {
return;
}
- for (index = 0; index < (s->blksize & 0x0fff); index++) {
- s->fifo_buffer[index] = sdbus_read_data(&s->sdbus);
+ for (index = 0; index < blk_size; index++) {
+ data = sdbus_read_data(&s->sdbus);
+ if (!FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
+ /* Device is not in tunning */
+ s->fifo_buffer[index] = data;
+ }
+ }
+
+ if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
+ /* Device is in tunning */
+ s->hostctl2 &= ~R_SDHC_HOSTCTL2_EXECUTE_TUNING_MASK;
+ s->hostctl2 |= R_SDHC_HOSTCTL2_SAMPLING_CLKSEL_MASK;
+ s->prnsts &= ~(SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ |
+ SDHC_DATA_INHIBIT);
+ goto read_done;
}
/* New data now available for READ through Buffer Port Register */
@@ -329,6 +469,7 @@ static void sdhci_read_block_from_card(SDHCIState *s)
}
}
+read_done:
sdhci_update_irq(s);
}
@@ -348,7 +489,7 @@ static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size)
value |= s->fifo_buffer[s->data_count] << i * 8;
s->data_count++;
/* check if we've read all valid data (blksize bytes) from buffer */
- if ((s->data_count) >= (s->blksize & 0x0fff)) {
+ if ((s->data_count) >= (s->blksize & BLOCK_SIZE_MASK)) {
trace_sdhci_read_dataport(s->data_count);
s->prnsts &= ~SDHC_DATA_AVAILABLE; /* no more data in a buffer */
s->data_count = 0; /* next buff read must start at position [0] */
@@ -395,7 +536,7 @@ static void sdhci_write_block_to_card(SDHCIState *s)
}
}
- for (index = 0; index < (s->blksize & 0x0fff); index++) {
+ for (index = 0; index < (s->blksize & BLOCK_SIZE_MASK); index++) {
sdbus_write_data(&s->sdbus, s->fifo_buffer[index]);
}
@@ -440,7 +581,7 @@ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size)
s->fifo_buffer[s->data_count] = value & 0xFF;
s->data_count++;
value >>= 8;
- if (s->data_count >= (s->blksize & 0x0fff)) {
+ if (s->data_count >= (s->blksize & BLOCK_SIZE_MASK)) {
trace_sdhci_write_dataport(s->data_count);
s->data_count = 0;
s->prnsts &= ~SDHC_SPACE_AVAILABLE;
@@ -460,8 +601,8 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
{
bool page_aligned = false;
unsigned int n, begin;
- const uint16_t block_size = s->blksize & 0x0fff;
- uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12);
+ const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
+ uint32_t boundary_chk = 1 << (((s->blksize & ~BLOCK_SIZE_MASK) >> 12) + 12);
uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk);
if (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || !s->blkcnt) {
@@ -550,7 +691,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
static void sdhci_sdma_transfer_single_block(SDHCIState *s)
{
int n;
- uint32_t datacnt = s->blksize & 0x0fff;
+ uint32_t datacnt = s->blksize & BLOCK_SIZE_MASK;
if (s->trnmod & SDHC_TRNS_READ) {
for (n = 0; n < datacnt; n++) {
@@ -580,7 +721,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
uint32_t adma1 = 0;
uint64_t adma2 = 0;
hwaddr entry_addr = (hwaddr)s->admasysaddr;
- switch (SDHC_DMA_TYPE(s->hostctl)) {
+ switch (SDHC_DMA_TYPE(s->hostctl1)) {
case SDHC_CTRL_ADMA2_32:
dma_memory_read(s->dma_as, entry_addr, (uint8_t *)&adma2,
sizeof(adma2));
@@ -614,8 +755,8 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
dscr->length = le16_to_cpu(dscr->length);
dma_memory_read(s->dma_as, entry_addr + 4,
(uint8_t *)(&dscr->addr), 8);
- dscr->attr = le64_to_cpu(dscr->attr);
- dscr->attr &= 0xfffffff8;
+ dscr->addr = le64_to_cpu(dscr->addr);
+ dscr->attr &= (uint8_t) ~0xC0;
dscr->incr = 12;
break;
}
@@ -626,7 +767,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
static void sdhci_do_adma(SDHCIState *s)
{
unsigned int n, begin, length;
- const uint16_t block_size = s->blksize & 0x0fff;
+ const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
ADMADescr dscr = {};
int i;
@@ -769,7 +910,7 @@ static void sdhci_data_transfer(void *opaque)
SDHCIState *s = (SDHCIState *)opaque;
if (s->trnmod & SDHC_TRNS_DMA) {
- switch (SDHC_DMA_TYPE(s->hostctl)) {
+ switch (SDHC_DMA_TYPE(s->hostctl1)) {
case SDHC_CTRL_SDMA:
if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) {
sdhci_sdma_transfer_single_block(s);
@@ -779,7 +920,7 @@ static void sdhci_data_transfer(void *opaque)
break;
case SDHC_CTRL_ADMA1_32:
- if (!(s->capareg & SDHC_CAN_DO_ADMA1)) {
+ if (!(s->capareg & R_SDHC_CAPAB_ADMA1_MASK)) {
trace_sdhci_error("ADMA1 not supported");
break;
}
@@ -787,7 +928,7 @@ static void sdhci_data_transfer(void *opaque)
sdhci_do_adma(s);
break;
case SDHC_CTRL_ADMA2_32:
- if (!(s->capareg & SDHC_CAN_DO_ADMA2)) {
+ if (!(s->capareg & R_SDHC_CAPAB_ADMA2_MASK)) {
trace_sdhci_error("ADMA2 not supported");
break;
}
@@ -795,8 +936,8 @@ static void sdhci_data_transfer(void *opaque)
sdhci_do_adma(s);
break;
case SDHC_CTRL_ADMA2_64:
- if (!(s->capareg & SDHC_CAN_DO_ADMA2) ||
- !(s->capareg & SDHC_64_BIT_BUS_SUPPORT)) {
+ if (!(s->capareg & R_SDHC_CAPAB_ADMA2_MASK) ||
+ !(s->capareg & R_SDHC_CAPAB_BUS64BIT_MASK)) {
trace_sdhci_error("64 bit ADMA not supported");
break;
}
@@ -876,9 +1017,13 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size)
break;
case SDHC_PRNSTS:
ret = s->prnsts;
+ ret = FIELD_DP32(ret, SDHC_PRNSTS, DAT_LVL,
+ sdbus_get_dat_lines(&s->sdbus));
+ ret = FIELD_DP32(ret, SDHC_PRNSTS, CMD_LVL,
+ sdbus_get_cmd_line(&s->sdbus));
break;
case SDHC_HOSTCTL:
- ret = s->hostctl | (s->pwrcon << 8) | (s->blkgap << 16) |
+ ret = s->hostctl1 | (s->pwrcon << 8) | (s->blkgap << 16) |
(s->wakcon << 24);
break;
case SDHC_CLKCON:
@@ -894,7 +1039,7 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size)
ret = s->norintsigen | (s->errintsigen << 16);
break;
case SDHC_ACMD12ERRSTS:
- ret = s->acmd12errsts;
+ ret = s->acmd12errsts | (s->hostctl2 << 16);
break;
case SDHC_CAPAB:
ret = (uint32_t)s->capareg;
@@ -918,7 +1063,7 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size)
ret = (uint32_t)(s->admasysaddr >> 32);
break;
case SDHC_SLOT_INT_STATUS:
- ret = (SD_HOST_SPECv2_VERS << 16) | sdhci_slotint(s);
+ ret = (s->version << 16) | sdhci_slotint(s);
break;
default:
qemu_log_mask(LOG_UNIMP, "SDHC rd_%ub @0x%02" HWADDR_PRIx " "
@@ -996,7 +1141,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
MASKED_WRITE(s->sdmasysad, mask, value);
/* Writing to last byte of sdmasysad might trigger transfer */
if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt &&
- s->blksize && SDHC_DMA_TYPE(s->hostctl) == SDHC_CTRL_SDMA) {
+ s->blksize && SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) {
if (s->trnmod & SDHC_TRNS_MULTI) {
sdhci_sdma_transfer_multi_blocks(s);
} else {
@@ -1026,7 +1171,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
case SDHC_TRNMOD:
/* DMA can be enabled only if it is supported as indicated by
* capabilities register */
- if (!(s->capareg & SDHC_CAN_DO_DMA)) {
+ if (!(s->capareg & R_SDHC_CAPAB_SDMA_MASK)) {
value &= ~SDHC_TRNS_DMA;
}
MASKED_WRITE(s->trnmod, mask, value & SDHC_TRNMOD_MASK);
@@ -1048,7 +1193,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
if (!(mask & 0xFF0000)) {
sdhci_blkgap_write(s, value >> 16);
}
- MASKED_WRITE(s->hostctl, mask, value);
+ MASKED_WRITE(s->hostctl1, mask, value);
MASKED_WRITE(s->pwrcon, mask >> 8, value >> 8);
MASKED_WRITE(s->wakcon, mask >> 24, value >> 24);
if (!(s->prnsts & SDHC_CARD_PRESENT) || ((s->pwrcon >> 1) & 0x7) < 5 ||
@@ -1128,7 +1273,16 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
sdhci_update_irq(s);
break;
case SDHC_ACMD12ERRSTS:
- MASKED_WRITE(s->acmd12errsts, mask, value);
+ MASKED_WRITE(s->acmd12errsts, mask, value & UINT16_MAX);
+ if (s->uhs_mode >= UHS_I) {
+ MASKED_WRITE(s->hostctl2, mask >> 16, value >> 16);
+
+ if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, V18_ENA)) {
+ sdbus_set_voltage(&s->sdbus, SD_VOLTAGE_1_8V);
+ } else {
+ sdbus_set_voltage(&s->sdbus, SD_VOLTAGE_3_3V);
+ }
+ }
break;
case SDHC_CAPAB:
@@ -1159,26 +1313,34 @@ static const MemoryRegionOps sdhci_mmio_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
+static void sdhci_init_readonly_registers(SDHCIState *s, Error **errp)
{
- switch (SDHC_CAPAB_BLOCKSIZE(s->capareg)) {
- case 0:
- return 512;
- case 1:
- return 1024;
- case 2:
- return 2048;
+ Error *local_err = NULL;
+
+ switch (s->sd_spec_version) {
+ case 2 ... 3:
+ break;
default:
- hw_error("SDHC: unsupported value for maximum block size\n");
- return 0;
+ error_setg(errp, "Only Spec v2/v3 are supported");
+ return;
+ }
+ s->version = (SDHC_HCVER_VENDOR << 8) | (s->sd_spec_version - 1);
+
+ sdhci_check_capareg(s, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
}
}
/* --- qdev common --- */
#define DEFINE_SDHCI_COMMON_PROPERTIES(_state) \
- /* Capabilities registers provide information on supported features
- * of this specific host controller implementation */ \
+ DEFINE_PROP_UINT8("sd-spec-version", _state, sd_spec_version, 2), \
+ DEFINE_PROP_UINT8("uhs", _state, uhs_mode, UHS_NOT_SUPPORTED), \
+ \
+ /* Capabilities registers provide information on supported
+ * features of this specific host controller implementation */ \
DEFINE_PROP_UINT64("capareg", _state, capareg, SDHC_CAPAB_REG_DEFAULT), \
DEFINE_PROP_UINT64("maxcurr", _state, maxcurr, 0)
@@ -1206,6 +1368,13 @@ static void sdhci_uninitfn(SDHCIState *s)
static void sdhci_common_realize(SDHCIState *s, Error **errp)
{
+ Error *local_err = NULL;
+
+ sdhci_init_readonly_registers(s, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
s->buf_maxsz = sdhci_get_fifolen(s);
s->fifo_buffer = g_malloc0(s->buf_maxsz);
@@ -1255,7 +1424,7 @@ const VMStateDescription sdhci_vmstate = {
VMSTATE_UINT16(cmdreg, SDHCIState),
VMSTATE_UINT32_ARRAY(rspreg, SDHCIState, 4),
VMSTATE_UINT32(prnsts, SDHCIState),
- VMSTATE_UINT8(hostctl, SDHCIState),
+ VMSTATE_UINT8(hostctl1, SDHCIState),
VMSTATE_UINT8(pwrcon, SDHCIState),
VMSTATE_UINT8(blkgap, SDHCIState),
VMSTATE_UINT8(wakcon, SDHCIState),
@@ -1302,10 +1471,12 @@ static Property sdhci_pci_properties[] = {
static void sdhci_pci_realize(PCIDevice *dev, Error **errp)
{
SDHCIState *s = PCI_SDHCI(dev);
+ Error *local_err = NULL;
sdhci_initfn(s);
sdhci_common_realize(s, errp);
- if (errp && *errp) {
+ if (local_err) {
+ error_propagate(errp, local_err);
return;
}
@@ -1383,9 +1554,11 @@ static void sdhci_sysbus_realize(DeviceState *dev, Error ** errp)
{
SDHCIState *s = SYSBUS_SDHCI(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ Error *local_err = NULL;
sdhci_common_realize(s, errp);
- if (errp && *errp) {
+ if (local_err) {
+ error_propagate(errp, local_err);
return;
}
@@ -1457,7 +1630,7 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
{
SDHCIState *s = SYSBUS_SDHCI(opaque);
uint32_t ret;
- uint16_t hostctl;
+ uint16_t hostctl1;
switch (offset) {
default:
@@ -1469,17 +1642,17 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
* manipulation code see comments in a similar part of
* usdhc_write()
*/
- hostctl = SDHC_DMA_TYPE(s->hostctl) << (8 - 3);
+ hostctl1 = SDHC_DMA_TYPE(s->hostctl1) << (8 - 3);
- if (s->hostctl & SDHC_CTRL_8BITBUS) {
- hostctl |= ESDHC_CTRL_8BITBUS;
+ if (s->hostctl1 & SDHC_CTRL_8BITBUS) {
+ hostctl1 |= ESDHC_CTRL_8BITBUS;
}
- if (s->hostctl & SDHC_CTRL_4BITBUS) {
- hostctl |= ESDHC_CTRL_4BITBUS;
+ if (s->hostctl1 & SDHC_CTRL_4BITBUS) {
+ hostctl1 |= ESDHC_CTRL_4BITBUS;
}
- ret = hostctl;
+ ret = hostctl1;
ret |= (uint32_t)s->blkgap << 16;
ret |= (uint32_t)s->wakcon << 24;
@@ -1503,7 +1676,7 @@ static void
usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
SDHCIState *s = SYSBUS_SDHCI(opaque);
- uint8_t hostctl;
+ uint8_t hostctl1;
uint32_t value = (uint32_t)val;
switch (offset) {
@@ -1566,25 +1739,25 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
/*
* First, save bits 7 6 and 0 since they are identical
*/
- hostctl = value & (SDHC_CTRL_LED |
- SDHC_CTRL_CDTEST_INS |
- SDHC_CTRL_CDTEST_EN);
+ hostctl1 = value & (SDHC_CTRL_LED |
+ SDHC_CTRL_CDTEST_INS |
+ SDHC_CTRL_CDTEST_EN);
/*
* Second, split "Data Transfer Width" from bits 2 and 1 in to
* bits 5 and 1
*/
if (value & ESDHC_CTRL_8BITBUS) {
- hostctl |= SDHC_CTRL_8BITBUS;
+ hostctl1 |= SDHC_CTRL_8BITBUS;
}
if (value & ESDHC_CTRL_4BITBUS) {
- hostctl |= ESDHC_CTRL_4BITBUS;
+ hostctl1 |= ESDHC_CTRL_4BITBUS;
}
/*
* Third, move DMA select from bits 9 and 8 to bits 4 and 3
*/
- hostctl |= SDHC_DMA_TYPE(value >> (8 - 3));
+ hostctl1 |= SDHC_DMA_TYPE(value >> (8 - 3));
/*
* Now place the corrected value into low 16-bit of the value
@@ -1595,7 +1768,7 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
* kernel
*/
value &= ~UINT16_MAX;
- value |= hostctl;
+ value |= hostctl1;
value |= (uint16_t)s->pwrcon << 8;
sdhci_write(opaque, offset, value, size);
diff --git a/hw/sd/trace-events b/hw/sd/trace-events
index 0a121156a3..0f8536db32 100644
--- a/hw/sd/trace-events
+++ b/hw/sd/trace-events
@@ -1,5 +1,13 @@
# See docs/devel/tracing.txt for syntax documentation.
+# hw/sd/core.c
+sdbus_command(const char *bus_name, uint8_t cmd, uint32_t arg, uint8_t crc) "@%s CMD%02d arg 0x%08x crc 0x%02x"
+sdbus_read(const char *bus_name, uint8_t value) "@%s value 0x%02x"
+sdbus_write(const char *bus_name, uint8_t value) "@%s value 0x%02x"
+sdbus_set_voltage(const char *bus_name, uint16_t millivolts) "@%s %u (mV)"
+sdbus_get_dat_lines(const char *bus_name, uint8_t dat_lines) "@%s dat_lines: %u"
+sdbus_get_cmd_line(const char *bus_name, bool cmd_line) "@%s cmd_line: %u"
+
# hw/sd/sdhci.c
sdhci_set_inserted(const char *level) "card state changed: %s"
sdhci_send_command(uint8_t cmd, uint32_t arg) "CMD%02u ARG[0x%08x]"
@@ -13,6 +21,7 @@ sdhci_adma_transfer_completed(void) ""
sdhci_access(const char *access, unsigned int size, uint64_t offset, const char *dir, uint64_t val, uint64_t val2) "%s%u: addr[0x%04" PRIx64 "] %s 0x%08" PRIx64 " (%" PRIu64 ")"
sdhci_read_dataport(uint16_t data_count) "all %u bytes of data have been read from input buffer"
sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of data"
+sdhci_capareg(const char *desc, uint16_t val) "%s: %u"
# hw/sd/milkymist-memcard.c
milkymist_memcard_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 228e82b3fb..721beb5486 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -3649,6 +3649,13 @@ static Property xhci_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
+static void xhci_instance_init(Object *obj)
+{
+ /* QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command
+ * line, therefore, no need to wait to realize like other devices */
+ PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS;
+}
+
static void xhci_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
@@ -3661,7 +3668,6 @@ static void xhci_class_init(ObjectClass *klass, void *data)
k->realize = usb_xhci_realize;
k->exit = usb_xhci_exit;
k->class_id = PCI_CLASS_SERIAL_USB;
- k->is_express = 1;
}
static const TypeInfo xhci_info = {
@@ -3669,6 +3675,7 @@ static const TypeInfo xhci_info = {
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XHCIState),
.class_init = xhci_class_init,
+ .instance_init = xhci_instance_init,
.abstract = true,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_PCIE_DEVICE },
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index f02b3aa541..033cc8dea1 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -3114,6 +3114,10 @@ static void vfio_instance_init(Object *obj)
vdev->host.function = ~0U;
vdev->nv_gpudirect_clique = 0xFF;
+
+ /* QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command
+ * line, therefore, no need to wait to realize like other devices */
+ pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
}
static Property vfio_pci_dev_properties[] = {
@@ -3172,7 +3176,6 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, void *data)
pdc->exit = vfio_exitfn;
pdc->config_read = vfio_pci_read_config;
pdc->config_write = vfio_pci_write_config;
- pdc->is_express = 1; /* We might be */
}
static const TypeInfo vfio_pci_dev_info = {
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index 2b8f81eb25..742ff0f90b 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -1,5 +1,11 @@
# See docs/devel/tracing.txt for syntax documentation.
+# hw/virtio/vhost.c
+vhost_commit(bool started, bool changed) "Started: %d Changed: %d"
+vhost_region_add_section(const char *name, uint64_t gpa, uint64_t size, uint64_t host) "%s: 0x%"PRIx64"+0x%"PRIx64" @ 0x%"PRIx64
+vhost_region_add_section_abut(const char *name, uint64_t new_size) "%s: 0x%"PRIx64
+vhost_section(const char *name, int r) "%s:%d"
+
# hw/virtio/virtio.c
virtqueue_alloc_element(void *elem, size_t sz, unsigned in_num, unsigned out_num) "elem %p size %zd in_num %u out_num %u"
virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) "vq %p elem %p len %u idx %u"
@@ -25,9 +31,3 @@ virtio_balloon_handle_output(const char *name, uint64_t gpa) "section name: %s g
virtio_balloon_get_config(uint32_t num_pages, uint32_t actual) "num_pages: %d actual: %d"
virtio_balloon_set_config(uint32_t actual, uint32_t oldactual) "actual: %d oldactual: %d"
virtio_balloon_to_target(uint64_t target, uint32_t num_pages) "balloon target: 0x%"PRIx64" num_pages: %d"
-
-# hw/virtio/vhost.c
-vhost_region_add(void *p, const char *mr) "dev %p mr %s"
-vhost_region_del(void *p, const char *mr) "dev %p mr %s"
-vhost_iommu_region_add(void *p, const char *mr) "dev %p mr %s"
-vhost_iommu_region_del(void *p, const char *mr) "dev %p mr %s"
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 338e4395b7..4a44e6e6bf 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -156,160 +156,6 @@ static void vhost_log_sync_range(struct vhost_dev *dev,
}
}
-/* Assign/unassign. Keep an unsorted array of non-overlapping
- * memory regions in dev->mem. */
-static void vhost_dev_unassign_memory(struct vhost_dev *dev,
- uint64_t start_addr,
- uint64_t size)
-{
- int from, to, n = dev->mem->nregions;
- /* Track overlapping/split regions for sanity checking. */
- int overlap_start = 0, overlap_end = 0, overlap_middle = 0, split = 0;
-
- for (from = 0, to = 0; from < n; ++from, ++to) {
- struct vhost_memory_region *reg = dev->mem->regions + to;
- uint64_t reglast;
- uint64_t memlast;
- uint64_t change;
-
- /* clone old region */
- if (to != from) {
- memcpy(reg, dev->mem->regions + from, sizeof *reg);
- }
-
- /* No overlap is simple */
- if (!ranges_overlap(reg->guest_phys_addr, reg->memory_size,
- start_addr, size)) {
- continue;
- }
-
- /* Split only happens if supplied region
- * is in the middle of an existing one. Thus it can not
- * overlap with any other existing region. */
- assert(!split);
-
- reglast = range_get_last(reg->guest_phys_addr, reg->memory_size);
- memlast = range_get_last(start_addr, size);
-
- /* Remove whole region */
- if (start_addr <= reg->guest_phys_addr && memlast >= reglast) {
- --dev->mem->nregions;
- --to;
- ++overlap_middle;
- continue;
- }
-
- /* Shrink region */
- if (memlast >= reglast) {
- reg->memory_size = start_addr - reg->guest_phys_addr;
- assert(reg->memory_size);
- assert(!overlap_end);
- ++overlap_end;
- continue;
- }
-
- /* Shift region */
- if (start_addr <= reg->guest_phys_addr) {
- change = memlast + 1 - reg->guest_phys_addr;
- reg->memory_size -= change;
- reg->guest_phys_addr += change;
- reg->userspace_addr += change;
- assert(reg->memory_size);
- assert(!overlap_start);
- ++overlap_start;
- continue;
- }
-
- /* This only happens if supplied region
- * is in the middle of an existing one. Thus it can not
- * overlap with any other existing region. */
- assert(!overlap_start);
- assert(!overlap_end);
- assert(!overlap_middle);
- /* Split region: shrink first part, shift second part. */
- memcpy(dev->mem->regions + n, reg, sizeof *reg);
- reg->memory_size = start_addr - reg->guest_phys_addr;
- assert(reg->memory_size);
- change = memlast + 1 - reg->guest_phys_addr;
- reg = dev->mem->regions + n;
- reg->memory_size -= change;
- assert(reg->memory_size);
- reg->guest_phys_addr += change;
- reg->userspace_addr += change;
- /* Never add more than 1 region */
- assert(dev->mem->nregions == n);
- ++dev->mem->nregions;
- ++split;
- }
-}
-
-/* Called after unassign, so no regions overlap the given range. */
-static void vhost_dev_assign_memory(struct vhost_dev *dev,
- uint64_t start_addr,
- uint64_t size,
- uint64_t uaddr)
-{
- int from, to;
- struct vhost_memory_region *merged = NULL;
- for (from = 0, to = 0; from < dev->mem->nregions; ++from, ++to) {
- struct vhost_memory_region *reg = dev->mem->regions + to;
- uint64_t prlast, urlast;
- uint64_t pmlast, umlast;
- uint64_t s, e, u;
-
- /* clone old region */
- if (to != from) {
- memcpy(reg, dev->mem->regions + from, sizeof *reg);
- }
- prlast = range_get_last(reg->guest_phys_addr, reg->memory_size);
- pmlast = range_get_last(start_addr, size);
- urlast = range_get_last(reg->userspace_addr, reg->memory_size);
- umlast = range_get_last(uaddr, size);
-
- /* check for overlapping regions: should never happen. */
- assert(prlast < start_addr || pmlast < reg->guest_phys_addr);
- /* Not an adjacent or overlapping region - do not merge. */
- if ((prlast + 1 != start_addr || urlast + 1 != uaddr) &&
- (pmlast + 1 != reg->guest_phys_addr ||
- umlast + 1 != reg->userspace_addr)) {
- continue;
- }
-
- if (dev->vhost_ops->vhost_backend_can_merge &&
- !dev->vhost_ops->vhost_backend_can_merge(dev, uaddr, size,
- reg->userspace_addr,
- reg->memory_size)) {
- continue;
- }
-
- if (merged) {
- --to;
- assert(to >= 0);
- } else {
- merged = reg;
- }
- u = MIN(uaddr, reg->userspace_addr);
- s = MIN(start_addr, reg->guest_phys_addr);
- e = MAX(pmlast, prlast);
- uaddr = merged->userspace_addr = u;
- start_addr = merged->guest_phys_addr = s;
- size = merged->memory_size = e - s + 1;
- assert(merged->memory_size);
- }
-
- if (!merged) {
- struct vhost_memory_region *reg = dev->mem->regions + to;
- memset(reg, 0, sizeof *reg);
- reg->memory_size = size;
- assert(reg->memory_size);
- reg->guest_phys_addr = start_addr;
- reg->userspace_addr = uaddr;
- ++to;
- }
- assert(to <= dev->mem->nregions + 1);
- dev->mem->nregions = to;
-}
-
static uint64_t vhost_get_log_size(struct vhost_dev *dev)
{
uint64_t log_size = 0;
@@ -456,35 +302,37 @@ static void vhost_memory_unmap(struct vhost_dev *dev, void *buffer,
}
}
-static int vhost_verify_ring_part_mapping(struct vhost_dev *dev,
- void *part,
- uint64_t part_addr,
- uint64_t part_size,
- uint64_t start_addr,
- uint64_t size)
+static int vhost_verify_ring_part_mapping(void *ring_hva,
+ uint64_t ring_gpa,
+ uint64_t ring_size,
+ void *reg_hva,
+ uint64_t reg_gpa,
+ uint64_t reg_size)
{
- hwaddr l;
- void *p;
- int r = 0;
+ uint64_t hva_ring_offset;
+ uint64_t ring_last = range_get_last(ring_gpa, ring_size);
+ uint64_t reg_last = range_get_last(reg_gpa, reg_size);
- if (!ranges_overlap(start_addr, size, part_addr, part_size)) {
+ if (ring_last < reg_gpa || ring_gpa > reg_last) {
return 0;
}
- l = part_size;
- p = vhost_memory_map(dev, part_addr, &l, 1);
- if (!p || l != part_size) {
- r = -ENOMEM;
+ /* check that whole ring's is mapped */
+ if (ring_last > reg_last) {
+ return -ENOMEM;
}
- if (p != part) {
- r = -EBUSY;
+ /* check that ring's MemoryRegion wasn't replaced */
+ hva_ring_offset = ring_gpa - reg_gpa;
+ if (ring_hva != reg_hva + hva_ring_offset) {
+ return -EBUSY;
}
- vhost_memory_unmap(dev, p, l, 0, 0);
- return r;
+
+ return 0;
}
static int vhost_verify_ring_mappings(struct vhost_dev *dev,
- uint64_t start_addr,
- uint64_t size)
+ void *reg_hva,
+ uint64_t reg_gpa,
+ uint64_t reg_size)
{
int i, j;
int r = 0;
@@ -498,22 +346,25 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev,
struct vhost_virtqueue *vq = dev->vqs + i;
j = 0;
- r = vhost_verify_ring_part_mapping(dev, vq->desc, vq->desc_phys,
- vq->desc_size, start_addr, size);
+ r = vhost_verify_ring_part_mapping(
+ vq->desc, vq->desc_phys, vq->desc_size,
+ reg_hva, reg_gpa, reg_size);
if (r) {
break;
}
j++;
- r = vhost_verify_ring_part_mapping(dev, vq->avail, vq->avail_phys,
- vq->avail_size, start_addr, size);
+ r = vhost_verify_ring_part_mapping(
+ vq->desc, vq->desc_phys, vq->desc_size,
+ reg_hva, reg_gpa, reg_size);
if (r) {
break;
}
j++;
- r = vhost_verify_ring_part_mapping(dev, vq->used, vq->used_phys,
- vq->used_size, start_addr, size);
+ r = vhost_verify_ring_part_mapping(
+ vq->desc, vq->desc_phys, vq->desc_size,
+ reg_hva, reg_gpa, reg_size);
if (r) {
break;
}
@@ -527,134 +378,95 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev,
return r;
}
-static struct vhost_memory_region *vhost_dev_find_reg(struct vhost_dev *dev,
- uint64_t start_addr,
- uint64_t size)
-{
- int i, n = dev->mem->nregions;
- for (i = 0; i < n; ++i) {
- struct vhost_memory_region *reg = dev->mem->regions + i;
- if (ranges_overlap(reg->guest_phys_addr, reg->memory_size,
- start_addr, size)) {
- return reg;
- }
- }
- return NULL;
-}
-
-static bool vhost_dev_cmp_memory(struct vhost_dev *dev,
- uint64_t start_addr,
- uint64_t size,
- uint64_t uaddr)
-{
- struct vhost_memory_region *reg = vhost_dev_find_reg(dev, start_addr, size);
- uint64_t reglast;
- uint64_t memlast;
-
- if (!reg) {
- return true;
- }
-
- reglast = range_get_last(reg->guest_phys_addr, reg->memory_size);
- memlast = range_get_last(start_addr, size);
-
- /* Need to extend region? */
- if (start_addr < reg->guest_phys_addr || memlast > reglast) {
- return true;
- }
- /* userspace_addr changed? */
- return uaddr != reg->userspace_addr + start_addr - reg->guest_phys_addr;
-}
-
-static void vhost_set_memory(MemoryListener *listener,
- MemoryRegionSection *section,
- bool add)
-{
- struct vhost_dev *dev = container_of(listener, struct vhost_dev,
- memory_listener);
- hwaddr start_addr = section->offset_within_address_space;
- ram_addr_t size = int128_get64(section->size);
- bool log_dirty =
- memory_region_get_dirty_log_mask(section->mr) & ~(1 << DIRTY_MEMORY_MIGRATION);
- int s = offsetof(struct vhost_memory, regions) +
- (dev->mem->nregions + 1) * sizeof dev->mem->regions[0];
- void *ram;
-
- dev->mem = g_realloc(dev->mem, s);
-
- if (log_dirty) {
- add = false;
- }
-
- assert(size);
-
- /* Optimize no-change case. At least cirrus_vga does this a lot at this time. */
- ram = memory_region_get_ram_ptr(section->mr) + section->offset_within_region;
- if (add) {
- if (!vhost_dev_cmp_memory(dev, start_addr, size, (uintptr_t)ram)) {
- /* Region exists with same address. Nothing to do. */
- return;
- }
- } else {
- if (!vhost_dev_find_reg(dev, start_addr, size)) {
- /* Removing region that we don't access. Nothing to do. */
- return;
- }
- }
-
- vhost_dev_unassign_memory(dev, start_addr, size);
- if (add) {
- /* Add given mapping, merging adjacent regions if any */
- vhost_dev_assign_memory(dev, start_addr, size, (uintptr_t)ram);
- } else {
- /* Remove old mapping for this memory, if any. */
- vhost_dev_unassign_memory(dev, start_addr, size);
- }
- dev->mem_changed_start_addr = MIN(dev->mem_changed_start_addr, start_addr);
- dev->mem_changed_end_addr = MAX(dev->mem_changed_end_addr, start_addr + size - 1);
- dev->memory_changed = true;
- used_memslots = dev->mem->nregions;
-}
-
static bool vhost_section(MemoryRegionSection *section)
{
- return memory_region_is_ram(section->mr) &&
+ bool result;
+ bool log_dirty = memory_region_get_dirty_log_mask(section->mr) &
+ ~(1 << DIRTY_MEMORY_MIGRATION);
+ result = memory_region_is_ram(section->mr) &&
!memory_region_is_rom(section->mr);
+
+ /* Vhost doesn't handle any block which is doing dirty-tracking other
+ * than migration; this typically fires on VGA areas.
+ */
+ result &= !log_dirty;
+
+ trace_vhost_section(section->mr->name, result);
+ return result;
}
static void vhost_begin(MemoryListener *listener)
{
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
memory_listener);
- dev->mem_changed_end_addr = 0;
- dev->mem_changed_start_addr = -1;
+ dev->tmp_sections = NULL;
+ dev->n_tmp_sections = 0;
}
static void vhost_commit(MemoryListener *listener)
{
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
memory_listener);
- hwaddr start_addr = 0;
- ram_addr_t size = 0;
+ MemoryRegionSection *old_sections;
+ int n_old_sections;
uint64_t log_size;
+ size_t regions_size;
int r;
+ int i;
+ bool changed = false;
- if (!dev->memory_changed) {
- return;
+ /* Note we can be called before the device is started, but then
+ * starting the device calls set_mem_table, so we need to have
+ * built the data structures.
+ */
+ old_sections = dev->mem_sections;
+ n_old_sections = dev->n_mem_sections;
+ dev->mem_sections = dev->tmp_sections;
+ dev->n_mem_sections = dev->n_tmp_sections;
+
+ if (dev->n_mem_sections != n_old_sections) {
+ changed = true;
+ } else {
+ /* Same size, lets check the contents */
+ changed = n_old_sections && memcmp(dev->mem_sections, old_sections,
+ n_old_sections * sizeof(old_sections[0])) != 0;
}
- if (!dev->started) {
- return;
+
+ trace_vhost_commit(dev->started, changed);
+ if (!changed) {
+ goto out;
}
- if (dev->mem_changed_start_addr > dev->mem_changed_end_addr) {
- return;
+
+ /* Rebuild the regions list from the new sections list */
+ regions_size = offsetof(struct vhost_memory, regions) +
+ dev->n_mem_sections * sizeof dev->mem->regions[0];
+ dev->mem = g_realloc(dev->mem, regions_size);
+ dev->mem->nregions = dev->n_mem_sections;
+ used_memslots = dev->mem->nregions;
+ for (i = 0; i < dev->n_mem_sections; i++) {
+ struct vhost_memory_region *cur_vmr = dev->mem->regions + i;
+ struct MemoryRegionSection *mrs = dev->mem_sections + i;
+
+ cur_vmr->guest_phys_addr = mrs->offset_within_address_space;
+ cur_vmr->memory_size = int128_get64(mrs->size);
+ cur_vmr->userspace_addr =
+ (uintptr_t)memory_region_get_ram_ptr(mrs->mr) +
+ mrs->offset_within_region;
+ cur_vmr->flags_padding = 0;
}
- if (dev->started) {
- start_addr = dev->mem_changed_start_addr;
- size = dev->mem_changed_end_addr - dev->mem_changed_start_addr + 1;
+ if (!dev->started) {
+ goto out;
+ }
- r = vhost_verify_ring_mappings(dev, start_addr, size);
- assert(r >= 0);
+ for (i = 0; i < dev->mem->nregions; i++) {
+ if (vhost_verify_ring_mappings(dev,
+ (void *)(uintptr_t)dev->mem->regions[i].userspace_addr,
+ dev->mem->regions[i].guest_phys_addr,
+ dev->mem->regions[i].memory_size)) {
+ error_report("Verify ring failure on region %d", i);
+ abort();
+ }
}
if (!dev->log_enabled) {
@@ -662,8 +474,7 @@ static void vhost_commit(MemoryListener *listener)
if (r < 0) {
VHOST_OPS_DEBUG("vhost_set_mem_table failed");
}
- dev->memory_changed = false;
- return;
+ goto out;
}
log_size = vhost_get_log_size(dev);
/* We allocate an extra 4K bytes to log,
@@ -681,51 +492,91 @@ static void vhost_commit(MemoryListener *listener)
if (dev->log_size > log_size + VHOST_LOG_BUFFER) {
vhost_dev_log_resize(dev, log_size);
}
- dev->memory_changed = false;
+
+out:
+ /* Deref the old list of sections, this must happen _after_ the
+ * vhost_set_mem_table to ensure the client isn't still using the
+ * section we're about to unref.
+ */
+ while (n_old_sections--) {
+ memory_region_unref(old_sections[n_old_sections].mr);
+ }
+ g_free(old_sections);
+ return;
}
-static void vhost_region_add(MemoryListener *listener,
- MemoryRegionSection *section)
+/* Adds the section data to the tmp_section structure.
+ * It relies on the listener calling us in memory address order
+ * and for each region (via the _add and _nop methods) to
+ * join neighbours.
+ */
+static void vhost_region_add_section(struct vhost_dev *dev,
+ MemoryRegionSection *section)
{
- struct vhost_dev *dev = container_of(listener, struct vhost_dev,
- memory_listener);
-
- if (!vhost_section(section)) {
- return;
+ bool need_add = true;
+ uint64_t mrs_size = int128_get64(section->size);
+ uint64_t mrs_gpa = section->offset_within_address_space;
+ uintptr_t mrs_host = (uintptr_t)memory_region_get_ram_ptr(section->mr) +
+ section->offset_within_region;
+
+ trace_vhost_region_add_section(section->mr->name, mrs_gpa, mrs_size,
+ mrs_host);
+
+ if (dev->n_tmp_sections) {
+ /* Since we already have at least one section, lets see if
+ * this extends it; since we're scanning in order, we only
+ * have to look at the last one, and the FlatView that calls
+ * us shouldn't have overlaps.
+ */
+ MemoryRegionSection *prev_sec = dev->tmp_sections +
+ (dev->n_tmp_sections - 1);
+ uint64_t prev_gpa_start = prev_sec->offset_within_address_space;
+ uint64_t prev_size = int128_get64(prev_sec->size);
+ uint64_t prev_gpa_end = range_get_last(prev_gpa_start, prev_size);
+ uint64_t prev_host_start =
+ (uintptr_t)memory_region_get_ram_ptr(prev_sec->mr) +
+ prev_sec->offset_within_region;
+ uint64_t prev_host_end = range_get_last(prev_host_start, prev_size);
+
+ if (prev_gpa_end + 1 == mrs_gpa &&
+ prev_host_end + 1 == mrs_host &&
+ section->mr == prev_sec->mr &&
+ (!dev->vhost_ops->vhost_backend_can_merge ||
+ dev->vhost_ops->vhost_backend_can_merge(dev,
+ mrs_host, mrs_size,
+ prev_host_start, prev_size))) {
+ /* The two sections abut */
+ need_add = false;
+ prev_sec->size = int128_add(prev_sec->size, section->size);
+ trace_vhost_region_add_section_abut(section->mr->name,
+ mrs_size + prev_size);
+ }
}
- trace_vhost_region_add(dev, section->mr->name ?: NULL);
- ++dev->n_mem_sections;
- dev->mem_sections = g_renew(MemoryRegionSection, dev->mem_sections,
- dev->n_mem_sections);
- dev->mem_sections[dev->n_mem_sections - 1] = *section;
- memory_region_ref(section->mr);
- vhost_set_memory(listener, section, true);
+ if (need_add) {
+ ++dev->n_tmp_sections;
+ dev->tmp_sections = g_renew(MemoryRegionSection, dev->tmp_sections,
+ dev->n_tmp_sections);
+ dev->tmp_sections[dev->n_tmp_sections - 1] = *section;
+ /* The flatview isn't stable and we don't use it, making it NULL
+ * means we can memcmp the list.
+ */
+ dev->tmp_sections[dev->n_tmp_sections - 1].fv = NULL;
+ memory_region_ref(section->mr);
+ }
}
-static void vhost_region_del(MemoryListener *listener,
- MemoryRegionSection *section)
+/* Used for both add and nop callbacks */
+static void vhost_region_addnop(MemoryListener *listener,
+ MemoryRegionSection *section)
{
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
memory_listener);
- int i;
if (!vhost_section(section)) {
return;
}
-
- trace_vhost_region_del(dev, section->mr->name ?: NULL);
- vhost_set_memory(listener, section, false);
- memory_region_unref(section->mr);
- for (i = 0; i < dev->n_mem_sections; ++i) {
- if (dev->mem_sections[i].offset_within_address_space
- == section->offset_within_address_space) {
- --dev->n_mem_sections;
- memmove(&dev->mem_sections[i], &dev->mem_sections[i+1],
- (dev->n_mem_sections - i) * sizeof(*dev->mem_sections));
- break;
- }
- }
+ vhost_region_add_section(dev, section);
}
static void vhost_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
@@ -752,8 +603,6 @@ static void vhost_iommu_region_add(MemoryListener *listener,
return;
}
- trace_vhost_iommu_region_add(dev, section->mr->name ?: NULL);
-
iommu = g_malloc0(sizeof(*iommu));
end = int128_add(int128_make64(section->offset_within_region),
section->size);
@@ -782,8 +631,6 @@ static void vhost_iommu_region_del(MemoryListener *listener,
return;
}
- trace_vhost_iommu_region_del(dev, section->mr->name ?: NULL);
-
QLIST_FOREACH(iommu, &dev->iommu_list, iommu_next) {
if (iommu->mr == section->mr &&
iommu->n.start == section->offset_within_region) {
@@ -796,11 +643,6 @@ static void vhost_iommu_region_del(MemoryListener *listener,
}
}
-static void vhost_region_nop(MemoryListener *listener,
- MemoryRegionSection *section)
-{
-}
-
static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
struct vhost_virtqueue *vq,
unsigned idx, bool enable_log)
@@ -1305,9 +1147,8 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
hdev->memory_listener = (MemoryListener) {
.begin = vhost_begin,
.commit = vhost_commit,
- .region_add = vhost_region_add,
- .region_del = vhost_region_del,
- .region_nop = vhost_region_nop,
+ .region_add = vhost_region_addnop,
+ .region_nop = vhost_region_addnop,
.log_start = vhost_log_start,
.log_stop = vhost_log_stop,
.log_sync = vhost_log_sync,
@@ -1349,7 +1190,6 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
hdev->log_size = 0;
hdev->log_enabled = false;
hdev->started = false;
- hdev->memory_changed = false;
memory_listener_register(&hdev->memory_listener, &address_space_memory);
QLIST_INSERT_HEAD(&vhost_devices, hdev, entry);
return 0;
@@ -1425,6 +1265,7 @@ fail_vq:
error_report("vhost VQ %d notifier cleanup error: %d", i, -r);
}
assert (e >= 0);
+ virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i);
}
virtio_device_release_ioeventfd(vdev);
fail:
@@ -1448,6 +1289,7 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
error_report("vhost VQ %d notifier cleanup failed: %d", i, -r);
}
assert (r >= 0);
+ virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i);
}
virtio_device_release_ioeventfd(vdev);
}
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index e05df206fc..48224493a0 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -51,6 +51,7 @@ static const char *balloon_stat_names[] = {
[VIRTIO_BALLOON_S_MEMFREE] = "stat-free-memory",
[VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory",
[VIRTIO_BALLOON_S_AVAIL] = "stat-available-memory",
+ [VIRTIO_BALLOON_S_CACHES] = "stat-disk-caches",
[VIRTIO_BALLOON_S_NR] = NULL
};
@@ -235,6 +236,7 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
memory_region_is_rom(section.mr) ||
memory_region_is_romd(section.mr)) {
trace_virtio_balloon_bad_addr(pa);
+ memory_region_unref(section.mr);
continue;
}
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
index 3042232daf..f9bc9ea46d 100644
--- a/hw/virtio/virtio-bus.c
+++ b/hw/virtio/virtio-bus.c
@@ -283,20 +283,26 @@ int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign)
r = k->ioeventfd_assign(proxy, notifier, n, true);
if (r < 0) {
error_report("%s: unable to assign ioeventfd: %d", __func__, r);
- goto cleanup_event_notifier;
+ virtio_bus_cleanup_host_notifier(bus, n);
}
- return 0;
} else {
k->ioeventfd_assign(proxy, notifier, n, false);
}
-cleanup_event_notifier:
+ return r;
+}
+
+void virtio_bus_cleanup_host_notifier(VirtioBusState *bus, int n)
+{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
+ EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
+
/* Test and clear notifier after disabling event,
* in case poll callback didn't have time to run.
*/
virtio_queue_host_notifier_read(notifier);
event_notifier_cleanup(notifier);
- return r;
}
static char *virtio_bus_get_dev_path(DeviceState *dev)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index c20537f31d..b55dfcf05c 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1932,7 +1932,8 @@ static Property virtio_blk_pci_properties[] = {
DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+ DEV_NVECTORS_UNSPECIFIED),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1941,6 +1942,10 @@ static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(vpci_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+ vpci_dev->nvectors = dev->vdev.conf.num_queues + 1;
+ }
+
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
object_property_set_bool(OBJECT(vdev), true, "realized", errp);
}
@@ -1983,7 +1988,8 @@ static const TypeInfo virtio_blk_pci_info = {
static Property vhost_user_blk_pci_properties[] = {
DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+ DEV_NVECTORS_UNSPECIFIED),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1992,6 +1998,10 @@ static void vhost_user_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(vpci_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+ vpci_dev->nvectors = dev->vdev.num_queues + 1;
+ }
+
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
object_property_set_bool(OBJECT(vdev), true, "realized", errp);
}
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index d6002ee550..006d3d1148 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -2572,8 +2572,9 @@ static Property virtio_properties[] = {
static int virtio_device_start_ioeventfd_impl(VirtIODevice *vdev)
{
VirtioBusState *qbus = VIRTIO_BUS(qdev_get_parent_bus(DEVICE(vdev)));
- int n, r, err;
+ int i, n, r, err;
+ memory_region_transaction_begin();
for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
VirtQueue *vq = &vdev->vq[n];
if (!virtio_queue_get_num(vdev, n)) {
@@ -2596,9 +2597,11 @@ static int virtio_device_start_ioeventfd_impl(VirtIODevice *vdev)
}
event_notifier_set(&vq->host_notifier);
}
+ memory_region_transaction_commit();
return 0;
assign_error:
+ i = n; /* save n for a second iteration after transaction is committed. */
while (--n >= 0) {
VirtQueue *vq = &vdev->vq[n];
if (!virtio_queue_get_num(vdev, n)) {
@@ -2609,6 +2612,14 @@ assign_error:
r = virtio_bus_set_host_notifier(qbus, n, false);
assert(r >= 0);
}
+ memory_region_transaction_commit();
+
+ while (--i >= 0) {
+ if (!virtio_queue_get_num(vdev, i)) {
+ continue;
+ }
+ virtio_bus_cleanup_host_notifier(qbus, i);
+ }
return err;
}
@@ -2625,6 +2636,7 @@ static void virtio_device_stop_ioeventfd_impl(VirtIODevice *vdev)
VirtioBusState *qbus = VIRTIO_BUS(qdev_get_parent_bus(DEVICE(vdev)));
int n, r;
+ memory_region_transaction_begin();
for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
VirtQueue *vq = &vdev->vq[n];
@@ -2635,6 +2647,14 @@ static void virtio_device_stop_ioeventfd_impl(VirtIODevice *vdev)
r = virtio_bus_set_host_notifier(qbus, n, false);
assert(r >= 0);
}
+ memory_region_transaction_commit();
+
+ for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
+ if (!virtio_queue_get_num(vdev, n)) {
+ continue;
+ }
+ virtio_bus_cleanup_host_notifier(qbus, n);
+ }
}
void virtio_device_stop_ioeventfd(VirtIODevice *vdev)
diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c
index f662f30370..9b7a960de1 100644
--- a/hw/xen/xen_pt.c
+++ b/hw/xen/xen_pt.c
@@ -937,6 +937,13 @@ static Property xen_pci_passthrough_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
+static void xen_pci_passthrough_instance_init(Object *obj)
+{
+ /* QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command
+ * line, therefore, no need to wait to realize like other devices */
+ PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS;
+}
+
static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -946,7 +953,6 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data)
k->exit = xen_pt_unregister_device;
k->config_read = xen_pt_pci_read_config;
k->config_write = xen_pt_pci_write_config;
- k->is_express = 1; /* We might be */
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->desc = "Assign an host PCI device with Xen";
dc->props = xen_pci_passthrough_properties;
@@ -965,6 +971,7 @@ static const TypeInfo xen_pci_passthrough_info = {
.instance_size = sizeof(XenPCIPassthroughState),
.instance_finalize = xen_pci_passthrough_finalize,
.class_init = xen_pci_passthrough_class_init,
+ .instance_init = xen_pci_passthrough_instance_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ INTERFACE_PCIE_DEVICE },
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 783ef64570..fff9b1d871 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -1091,32 +1091,14 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr size);
/**
- * memory_region_test_and_clear_dirty: Check whether a range of bytes is dirty
- * for a specified client. It clears them.
- *
- * Checks whether a range of bytes has been written to since the last
- * call to memory_region_reset_dirty() with the same @client. Dirty logging
- * must be enabled.
- *
- * @mr: the memory region being queried.
- * @addr: the address (relative to the start of the region) being queried.
- * @size: the size of the range being queried.
- * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or
- * %DIRTY_MEMORY_VGA.
- */
-bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
- hwaddr size, unsigned client);
-
-/**
* memory_region_snapshot_and_clear_dirty: Get a snapshot of the dirty
* bitmap and clear it.
*
* Creates a snapshot of the dirty bitmap, clears the dirty bitmap and
* returns the snapshot. The snapshot can then be used to query dirty
- * status, using memory_region_snapshot_get_dirty. Unlike
- * memory_region_test_and_clear_dirty this allows to query the same
- * page multiple times, which is especially useful for display updates
- * where the scanlines often are not page aligned.
+ * status, using memory_region_snapshot_get_dirty. Snapshotting allows
+ * querying the same page multiple times, which is especially useful for
+ * display updates where the scanlines often are not page aligned.
*
* The dirty bitmap region which gets copyed into the snapshot (and
* cleared afterwards) can be larger than requested. The boundaries
@@ -1154,17 +1136,6 @@ bool memory_region_snapshot_get_dirty(MemoryRegion *mr,
hwaddr addr, hwaddr size);
/**
- * memory_region_sync_dirty_bitmap: Synchronize a region's dirty bitmap with
- * any external TLBs (e.g. kvm)
- *
- * Flushes dirty information from accelerators such as kvm and vhost-net
- * and makes it available to users of the memory API.
- *
- * @mr: the region being flushed.
- */
-void memory_region_sync_dirty_bitmap(MemoryRegion *mr);
-
-/**
* memory_region_reset_dirty: Mark a range of pages as clean, for a specified
* client.
*
diff --git a/include/hw/compat.h b/include/hw/compat.h
index 7f31850dfa..bc9e3a6627 100644
--- a/include/hw/compat.h
+++ b/include/hw/compat.h
@@ -6,6 +6,14 @@
.driver = "hpet",\
.property = "hpet-offset-saved",\
.value = "false",\
+ },{\
+ .driver = "virtio-blk-pci",\
+ .property = "vectors",\
+ .value = "2",\
+ },{\
+ .driver = "vhost-user-blk-pci",\
+ .property = "vectors",\
+ .value = "2",\
},
#define HW_COMPAT_2_10 \
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 15ced9648c..d8c18c7fa4 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -236,9 +236,6 @@ typedef struct PCIDeviceClass {
*/
int is_bridge;
- /* pcie stuff */
- int is_express; /* is this device pci express? */
-
/* rom bar */
const char *romfile;
} PCIDeviceClass;
diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h
index 9b44ffd22a..0347da52d2 100644
--- a/include/hw/pci/pci_bridge.h
+++ b/include/hw/pci/pci_bridge.h
@@ -135,8 +135,8 @@ typedef struct PCIBridgeQemuCap {
int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset,
uint32_t bus_reserve, uint64_t io_reserve,
- uint32_t mem_non_pref_reserve,
- uint32_t mem_pref_32_reserve,
+ uint64_t mem_non_pref_reserve,
+ uint64_t mem_pref_32_reserve,
uint64_t mem_pref_64_reserve,
Error **errp);
diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h
index 96caefe373..bf1eb0713c 100644
--- a/include/hw/sd/sd.h
+++ b/include/hw/sd/sd.h
@@ -56,6 +56,20 @@
#define OCR_CCS_BITN 30
typedef enum {
+ SD_VOLTAGE_0_4V = 400, /* currently not supported */
+ SD_VOLTAGE_1_8V = 1800,
+ SD_VOLTAGE_3_0V = 3000,
+ SD_VOLTAGE_3_3V = 3300,
+} sd_voltage_mv_t;
+
+typedef enum {
+ UHS_NOT_SUPPORTED = 0,
+ UHS_I = 1,
+ UHS_II = 2, /* currently not supported */
+ UHS_III = 3, /* currently not supported */
+} sd_uhs_mode_t;
+
+typedef enum {
sd_none = -1,
sd_bc = 0, /* broadcast -- no response */
sd_bcr, /* broadcast with response */
@@ -88,6 +102,9 @@ typedef struct {
void (*write_data)(SDState *sd, uint8_t value);
uint8_t (*read_data)(SDState *sd);
bool (*data_ready)(SDState *sd);
+ void (*set_voltage)(SDState *sd, uint16_t millivolts);
+ uint8_t (*get_dat_lines)(SDState *sd);
+ bool (*get_cmd_line)(SDState *sd);
void (*enable)(SDState *sd, bool enable);
bool (*get_inserted)(SDState *sd);
bool (*get_readonly)(SDState *sd);
@@ -134,6 +151,9 @@ void sd_enable(SDState *sd, bool enable);
/* Functions to be used by qdevified callers (working via
* an SDBus rather than directly with SDState)
*/
+void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts);
+uint8_t sdbus_get_dat_lines(SDBus *sdbus);
+bool sdbus_get_cmd_line(SDBus *sdbus);
int sdbus_do_command(SDBus *sd, SDRequest *req, uint8_t *response);
void sdbus_write_data(SDBus *sd, uint8_t value);
uint8_t sdbus_read_data(SDBus *sd);
diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h
index f8d1ba3538..f321767c56 100644
--- a/include/hw/sd/sdhci.h
+++ b/include/hw/sd/sdhci.h
@@ -59,7 +59,7 @@ typedef struct SDHCIState {
uint16_t cmdreg; /* Command Register */
uint32_t rspreg[4]; /* Response Registers 0-3 */
uint32_t prnsts; /* Present State Register */
- uint8_t hostctl; /* Host Control Register */
+ uint8_t hostctl1; /* Host Control Register */
uint8_t pwrcon; /* Power control Register */
uint8_t blkgap; /* Block Gap Control Register */
uint8_t wakcon; /* WakeUp Control Register */
@@ -73,11 +73,13 @@ typedef struct SDHCIState {
uint16_t norintsigen; /* Normal Interrupt Signal Enable Register */
uint16_t errintsigen; /* Error Interrupt Signal Enable Register */
uint16_t acmd12errsts; /* Auto CMD12 error status register */
+ uint16_t hostctl2; /* Host Control 2 */
uint64_t admasysaddr; /* ADMA System Address Register */
/* Read-only registers */
uint64_t capareg; /* Capabilities Register */
uint64_t maxcurr; /* Maximum Current Capabilities Register */
+ uint16_t version; /* Host Controller Version Register */
uint8_t *fifo_buffer; /* SD host i/o FIFO buffer */
uint32_t buf_maxsz;
@@ -93,6 +95,8 @@ typedef struct SDHCIState {
/* Configurable properties */
bool pending_insert_quirk; /* Quirk for Raspberry Pi card insert int */
uint32_t quirks;
+ uint8_t sd_spec_version;
+ uint8_t uhs_mode;
} SDHCIState;
/*
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index 1dc2d73d76..a7f449fa87 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -60,6 +60,8 @@ struct vhost_dev {
struct vhost_memory *mem;
int n_mem_sections;
MemoryRegionSection *mem_sections;
+ int n_tmp_sections;
+ MemoryRegionSection *tmp_sections;
struct vhost_virtqueue *vqs;
int nvqs;
/* the first virtqueue which would be used by this vhost dev */
@@ -73,9 +75,6 @@ struct vhost_dev {
bool log_enabled;
uint64_t log_size;
Error *migration_blocker;
- bool memory_changed;
- hwaddr mem_changed_start_addr;
- hwaddr mem_changed_end_addr;
const VhostOps *vhost_ops;
void *opaque;
struct vhost_log *log;
diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h
index a63c1d216d..ced3d2d2b0 100644
--- a/include/hw/virtio/virtio-bus.h
+++ b/include/hw/virtio/virtio-bus.h
@@ -148,5 +148,7 @@ int virtio_bus_grab_ioeventfd(VirtioBusState *bus);
void virtio_bus_release_ioeventfd(VirtioBusState *bus);
/* Switch from/to the generic ioeventfd handler */
int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign);
+/* Tell the bus that the ioeventfd handler is no longer required. */
+void virtio_bus_cleanup_host_notifier(VirtioBusState *bus, int n);
#endif /* VIRTIO_BUS_H */
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 8c3889433c..df463fd33d 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -148,6 +148,7 @@ enum VMStateFlags {
typedef enum {
MIG_PRI_DEFAULT = 0,
MIG_PRI_IOMMU, /* Must happen before PCI devices */
+ MIG_PRI_PCI_BUS, /* Must happen before IOMMU */
MIG_PRI_GICV3_ITS, /* Must happen before PCI devices */
MIG_PRI_GICV3, /* Must happen before the ITS */
MIG_PRI_MAX,
diff --git a/include/net/can_emu.h b/include/net/can_emu.h
new file mode 100644
index 0000000000..1da4d01b95
--- /dev/null
+++ b/include/net/can_emu.h
@@ -0,0 +1,123 @@
+/*
+ * CAN common CAN bus emulation support
+ *
+ * Copyright (c) 2013-2014 Jin Yang
+ * Copyright (c) 2014-2018 Pavel Pisa
+ *
+ * Initial development supported by Google GSoC 2013 from RTEMS project slot
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef NET_CAN_EMU_H
+#define NET_CAN_EMU_H
+
+#include "qom/object.h"
+
+/* NOTE: the following two structures is copied from <linux/can.h>. */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef uint32_t qemu_canid_t;
+
+typedef struct qemu_can_frame {
+ qemu_canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ uint8_t can_dlc; /* data length code: 0 .. 8 */
+ uint8_t data[8] QEMU_ALIGNED(8);
+} qemu_can_frame;
+
+/* Keep defines for QEMU separate from Linux ones for now */
+
+#define QEMU_CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define QEMU_CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define QEMU_CAN_ERR_FLAG 0x20000000U /* error message frame */
+
+#define QEMU_CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define QEMU_CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+
+/**
+ * struct qemu_can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * A filter matches, when
+ *
+ * <received_can_id> & mask == can_id & mask
+ *
+ * The filter can be inverted (QEMU_CAN_INV_FILTER bit set in can_id) or it can
+ * filter for error message frames (QEMU_CAN_ERR_FLAG bit set in mask).
+ */
+typedef struct qemu_can_filter {
+ qemu_canid_t can_id;
+ qemu_canid_t can_mask;
+} qemu_can_filter;
+
+/* QEMU_CAN_INV_FILTER can be set in qemu_can_filter.can_id */
+#define QEMU_CAN_INV_FILTER 0x20000000U
+
+typedef struct CanBusClientState CanBusClientState;
+typedef struct CanBusState CanBusState;
+
+typedef struct CanBusClientInfo {
+ int (*can_receive)(CanBusClientState *);
+ ssize_t (*receive)(CanBusClientState *,
+ const struct qemu_can_frame *frames, size_t frames_cnt);
+} CanBusClientInfo;
+
+struct CanBusClientState {
+ CanBusClientInfo *info;
+ CanBusState *bus;
+ int link_down;
+ QTAILQ_ENTRY(CanBusClientState) next;
+ CanBusClientState *peer;
+ char *model;
+ char *name;
+ void (*destructor)(CanBusClientState *);
+};
+
+#define TYPE_CAN_BUS "can-bus"
+#define CAN_BUS_CLASS(klass) \
+ OBJECT_CLASS_CHECK(CanBusClass, (klass), TYPE_CAN_BUS)
+#define CAN_BUS_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(CanBusClass, (obj), TYPE_CAN_BUS)
+#define CAN_BUS(obj) \
+ OBJECT_CHECK(CanBusState, (obj), TYPE_CAN_BUS)
+
+int can_bus_filter_match(struct qemu_can_filter *filter, qemu_canid_t can_id);
+
+int can_bus_insert_client(CanBusState *bus, CanBusClientState *client);
+
+int can_bus_remove_client(CanBusClientState *client);
+
+ssize_t can_bus_client_send(CanBusClientState *,
+ const struct qemu_can_frame *frames,
+ size_t frames_cnt);
+
+int can_bus_client_set_filters(CanBusClientState *,
+ const struct qemu_can_filter *filters,
+ size_t filters_cnt);
+
+#endif
diff --git a/include/net/can_host.h b/include/net/can_host.h
new file mode 100644
index 0000000000..d79676746b
--- /dev/null
+++ b/include/net/can_host.h
@@ -0,0 +1,55 @@
+/*
+ * CAN common CAN bus emulation support
+ *
+ * Copyright (c) 2013-2014 Jin Yang
+ * Copyright (c) 2014-2018 Pavel Pisa
+ *
+ * Initial development supported by Google GSoC 2013 from RTEMS project slot
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef NET_CAN_HOST_H
+#define NET_CAN_HOST_H
+
+#include "net/can_emu.h"
+
+#define TYPE_CAN_HOST "can-host"
+#define CAN_HOST_CLASS(klass) \
+ OBJECT_CLASS_CHECK(CanHostClass, (klass), TYPE_CAN_HOST)
+#define CAN_HOST_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(CanHostClass, (obj), TYPE_CAN_HOST)
+#define CAN_HOST(obj) \
+ OBJECT_CHECK(CanHostState, (obj), TYPE_CAN_HOST)
+
+typedef struct CanHostState {
+ ObjectClass oc;
+
+ CanBusState *bus;
+ CanBusClientState bus_client;
+} CanHostState;
+
+typedef struct CanHostClass {
+ ObjectClass oc;
+
+ void (*connect)(CanHostState *ch, Error **errp);
+ void (*disconnect)(CanHostState *ch);
+} CanHostClass;
+
+#endif
diff --git a/include/standard-headers/linux/virtio_balloon.h b/include/standard-headers/linux/virtio_balloon.h
index 9d06ccd066..7b0a41b8fc 100644
--- a/include/standard-headers/linux/virtio_balloon.h
+++ b/include/standard-headers/linux/virtio_balloon.h
@@ -52,7 +52,8 @@ struct virtio_balloon_config {
#define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */
#define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */
#define VIRTIO_BALLOON_S_AVAIL 6 /* Available memory as in /proc */
-#define VIRTIO_BALLOON_S_NR 7
+#define VIRTIO_BALLOON_S_CACHES 7 /* Disk caches */
+#define VIRTIO_BALLOON_S_NR 8
/*
* Memory statistics structure.
diff --git a/include/sysemu/hax.h b/include/sysemu/hax.h
index f252399623..1f6c46186d 100644
--- a/include/sysemu/hax.h
+++ b/include/sysemu/hax.h
@@ -27,7 +27,7 @@
int hax_sync_vcpus(void);
int hax_init_vcpu(CPUState *cpu);
int hax_smp_cpu_exec(CPUState *cpu);
-int hax_populate_ram(uint64_t va, uint32_t size);
+int hax_populate_ram(uint64_t va, uint64_t size);
void hax_cpu_synchronize_state(CPUState *cpu);
void hax_cpu_synchronize_post_reset(CPUState *cpu);
diff --git a/memory.c b/memory.c
index 93258a6655..c7f6588452 100644
--- a/memory.c
+++ b/memory.c
@@ -1971,33 +1971,7 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr,
memory_region_get_dirty_log_mask(mr));
}
-bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
- hwaddr size, unsigned client)
-{
- assert(mr->ram_block);
- return cpu_physical_memory_test_and_clear_dirty(
- memory_region_get_ram_addr(mr) + addr, size, client);
-}
-
-DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr,
- hwaddr addr,
- hwaddr size,
- unsigned client)
-{
- assert(mr->ram_block);
- return cpu_physical_memory_snapshot_and_clear_dirty(
- memory_region_get_ram_addr(mr) + addr, size, client);
-}
-
-bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snap,
- hwaddr addr, hwaddr size)
-{
- assert(mr->ram_block);
- return cpu_physical_memory_snapshot_get_dirty(snap,
- memory_region_get_ram_addr(mr) + addr, size);
-}
-
-void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
+static void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
{
MemoryListener *listener;
AddressSpace *as;
@@ -2016,7 +1990,7 @@ void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
as = listener->address_space;
view = address_space_get_flatview(as);
FOR_EACH_FLAT_RANGE(fr, view) {
- if (fr->mr == mr) {
+ if (fr->dirty_log_mask && (!mr || fr->mr == mr)) {
MemoryRegionSection mrs = section_from_flat_range(fr, view);
listener->log_sync(listener, &mrs);
}
@@ -2025,6 +1999,25 @@ void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
}
}
+DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr,
+ hwaddr addr,
+ hwaddr size,
+ unsigned client)
+{
+ assert(mr->ram_block);
+ memory_region_sync_dirty_bitmap(mr);
+ return cpu_physical_memory_snapshot_and_clear_dirty(
+ memory_region_get_ram_addr(mr) + addr, size, client);
+}
+
+bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snap,
+ hwaddr addr, hwaddr size)
+{
+ assert(mr->ram_block);
+ return cpu_physical_memory_snapshot_get_dirty(snap,
+ memory_region_get_ram_addr(mr) + addr, size);
+}
+
void memory_region_set_readonly(MemoryRegion *mr, bool readonly)
{
if (mr->readonly != readonly) {
@@ -2513,26 +2506,7 @@ bool memory_region_present(MemoryRegion *container, hwaddr addr)
void memory_global_dirty_log_sync(void)
{
- MemoryListener *listener;
- AddressSpace *as;
- FlatView *view;
- FlatRange *fr;
-
- QTAILQ_FOREACH(listener, &memory_listeners, link) {
- if (!listener->log_sync) {
- continue;
- }
- as = listener->address_space;
- view = address_space_get_flatview(as);
- FOR_EACH_FLAT_RANGE(fr, view) {
- if (fr->dirty_log_mask) {
- MemoryRegionSection mrs = section_from_flat_range(fr, view);
-
- listener->log_sync(listener, &mrs);
- }
- }
- flatview_unref(view);
- }
+ memory_region_sync_dirty_bitmap(NULL);
}
static VMChangeStateEntry *vmstate_change;
diff --git a/net/Makefile.objs b/net/Makefile.objs
index 64adf32f40..b2bf88a0ef 100644
--- a/net/Makefile.objs
+++ b/net/Makefile.objs
@@ -23,3 +23,5 @@ common-obj-$(CONFIG_POSIX) += tap.o $(tap-obj-y)
common-obj-$(CONFIG_WIN32) += tap-win32.o
vde.o-libs = $(VDE_LIBS)
+
+common-obj-$(CONFIG_CAN_BUS) += can/
diff --git a/net/can/Makefile.objs b/net/can/Makefile.objs
new file mode 100644
index 0000000000..9f35dc5c87
--- /dev/null
+++ b/net/can/Makefile.objs
@@ -0,0 +1,2 @@
+common-obj-y += can_core.o can_host.o
+common-obj-$(CONFIG_LINUX) += can_socketcan.o
diff --git a/net/can/can_core.c b/net/can/can_core.c
new file mode 100644
index 0000000000..2a83cadfc5
--- /dev/null
+++ b/net/can/can_core.c
@@ -0,0 +1,138 @@
+/*
+ * CAN common CAN bus emulation support
+ *
+ * Copyright (c) 2013-2014 Jin Yang
+ * Copyright (c) 2014-2018 Pavel Pisa
+ *
+ * Initial development supported by Google GSoC 2013 from RTEMS project slot
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "chardev/char.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "net/can_emu.h"
+#include "qom/object_interfaces.h"
+
+struct CanBusState {
+ Object object;
+
+ QTAILQ_HEAD(, CanBusClientState) clients;
+};
+
+static void can_bus_instance_init(Object *object)
+{
+ CanBusState *bus = (CanBusState *)object;
+
+ QTAILQ_INIT(&bus->clients);
+}
+
+int can_bus_insert_client(CanBusState *bus, CanBusClientState *client)
+{
+ client->bus = bus;
+ QTAILQ_INSERT_TAIL(&bus->clients, client, next);
+ return 0;
+}
+
+int can_bus_remove_client(CanBusClientState *client)
+{
+ CanBusState *bus = client->bus;
+ if (bus == NULL) {
+ return 0;
+ }
+
+ QTAILQ_REMOVE(&bus->clients, client, next);
+ client->bus = NULL;
+ return 1;
+}
+
+ssize_t can_bus_client_send(CanBusClientState *client,
+ const struct qemu_can_frame *frames, size_t frames_cnt)
+{
+ int ret = 0;
+ CanBusState *bus = client->bus;
+ CanBusClientState *peer;
+ if (bus == NULL) {
+ return -1;
+ }
+
+ QTAILQ_FOREACH(peer, &bus->clients, next) {
+ if (peer->info->can_receive(peer)) {
+ if (peer == client) {
+ /* No loopback support for now */
+ continue;
+ }
+ if (peer->info->receive(peer, frames, frames_cnt) > 0) {
+ ret = 1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int can_bus_filter_match(struct qemu_can_filter *filter, qemu_canid_t can_id)
+{
+ int m;
+ if (((can_id | filter->can_mask) & QEMU_CAN_ERR_FLAG)) {
+ return (filter->can_mask & QEMU_CAN_ERR_FLAG) != 0;
+ }
+ m = (can_id & filter->can_mask) == (filter->can_id & filter->can_mask);
+ return filter->can_id & QEMU_CAN_INV_FILTER ? !m : m;
+}
+
+int can_bus_client_set_filters(CanBusClientState *client,
+ const struct qemu_can_filter *filters, size_t filters_cnt)
+{
+ return 0;
+}
+
+
+static bool can_bus_can_be_deleted(UserCreatable *uc)
+{
+ return false;
+}
+
+static void can_bus_class_init(ObjectClass *klass,
+ void *class_data G_GNUC_UNUSED)
+{
+ UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass);
+
+ uc_klass->can_be_deleted = can_bus_can_be_deleted;
+}
+
+static const TypeInfo can_bus_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_CAN_BUS,
+ .instance_size = sizeof(CanBusState),
+ .instance_init = can_bus_instance_init,
+ .class_init = can_bus_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+static void can_bus_register_types(void)
+{
+ type_register_static(&can_bus_info);
+}
+
+type_init(can_bus_register_types);
diff --git a/net/can/can_host.c b/net/can/can_host.c
new file mode 100644
index 0000000000..c3d26521cd
--- /dev/null
+++ b/net/can/can_host.c
@@ -0,0 +1,112 @@
+/*
+ * CAN generic CAN host connection support
+ *
+ * Copyright (c) 2013-2014 Jin Yang
+ * Copyright (c) 2014-2018 Pavel Pisa
+ *
+ * Initial development supported by Google GSoC 2013 from RTEMS project slot
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "chardev/char.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "net/can_emu.h"
+#include "net/can_host.h"
+
+struct CanBusState {
+ Object object;
+
+ QTAILQ_HEAD(, CanBusClientState) clients;
+};
+
+static void can_host_disconnect(CanHostState *ch)
+{
+ CanHostClass *chc = CAN_HOST_GET_CLASS(ch);
+
+ can_bus_remove_client(&ch->bus_client);
+ chc->disconnect(ch);
+}
+
+static void can_host_connect(CanHostState *ch, Error **errp)
+{
+ CanHostClass *chc = CAN_HOST_GET_CLASS(ch);
+ Error *local_err = NULL;
+
+ chc->connect(ch, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ can_bus_insert_client(ch->bus, &ch->bus_client);
+}
+
+static void can_host_unparent(Object *obj)
+{
+ can_host_disconnect(CAN_HOST(obj));
+}
+
+static void can_host_complete(UserCreatable *uc, Error **errp)
+{
+ can_host_connect(CAN_HOST(uc), errp);
+}
+
+static void can_host_instance_init(Object *obj)
+{
+ CanHostState *ch = CAN_HOST(obj);
+
+ object_property_add_link(obj, "canbus", TYPE_CAN_BUS,
+ (Object **)&ch->bus,
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &error_abort);
+}
+
+static void can_host_class_init(ObjectClass *klass,
+ void *class_data G_GNUC_UNUSED)
+{
+ UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass);
+
+ klass->unparent = can_host_unparent;
+ uc_klass->complete = can_host_complete;
+}
+
+static const TypeInfo can_host_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_CAN_HOST,
+ .instance_size = sizeof(CanHostState),
+ .class_size = sizeof(CanHostClass),
+ .abstract = true,
+ .instance_init = can_host_instance_init,
+ .class_init = can_host_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+static void can_host_register_types(void)
+{
+ type_register_static(&can_host_info);
+}
+
+type_init(can_host_register_types);
diff --git a/net/can/can_socketcan.c b/net/can/can_socketcan.c
new file mode 100644
index 0000000000..39865e28e0
--- /dev/null
+++ b/net/can/can_socketcan.c
@@ -0,0 +1,286 @@
+/*
+ * CAN c support to connect to the Linux host SocketCAN interfaces
+ *
+ * Copyright (c) 2013-2014 Jin Yang
+ * Copyright (c) 2014-2018 Pavel Pisa
+ *
+ * Initial development supported by Google GSoC 2013 from RTEMS project slot
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "qemu/sockets.h"
+#include "qemu/error-report.h"
+#include "net/can_emu.h"
+#include "net/can_host.h"
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/can.h>
+#include <linux/can/raw.h>
+
+#ifndef DEBUG_CAN
+#define DEBUG_CAN 0
+#endif /*DEBUG_CAN*/
+
+#define TYPE_CAN_HOST_SOCKETCAN "can-host-socketcan"
+#define CAN_HOST_SOCKETCAN(obj) \
+ OBJECT_CHECK(CanHostSocketCAN, (obj), TYPE_CAN_HOST_SOCKETCAN)
+
+#define CAN_READ_BUF_LEN 5
+typedef struct CanHostSocketCAN {
+ CanHostState parent;
+ char *ifname;
+
+ qemu_can_filter *rfilter;
+ int rfilter_num;
+ can_err_mask_t err_mask;
+
+ qemu_can_frame buf[CAN_READ_BUF_LEN];
+ int bufcnt;
+ int bufptr;
+
+ int fd;
+} CanHostSocketCAN;
+
+/* Check that QEMU and Linux kernel flags encoding and structure matches */
+QEMU_BUILD_BUG_ON(QEMU_CAN_EFF_FLAG != CAN_EFF_FLAG);
+QEMU_BUILD_BUG_ON(QEMU_CAN_RTR_FLAG != CAN_RTR_FLAG);
+QEMU_BUILD_BUG_ON(QEMU_CAN_ERR_FLAG != CAN_ERR_FLAG);
+QEMU_BUILD_BUG_ON(QEMU_CAN_INV_FILTER != CAN_INV_FILTER);
+QEMU_BUILD_BUG_ON(offsetof(qemu_can_frame, data)
+ != offsetof(struct can_frame, data));
+
+static void can_host_socketcan_display_msg(struct qemu_can_frame *msg)
+{
+ int i;
+
+ qemu_log_lock();
+ qemu_log("[cansocketcan]: %03X [%01d] %s %s",
+ msg->can_id & QEMU_CAN_EFF_MASK,
+ msg->can_dlc,
+ msg->can_id & QEMU_CAN_EFF_FLAG ? "EFF" : "SFF",
+ msg->can_id & QEMU_CAN_RTR_FLAG ? "RTR" : "DAT");
+
+ for (i = 0; i < msg->can_dlc; i++) {
+ qemu_log(" %02X", msg->data[i]);
+ }
+ qemu_log("\n");
+ qemu_log_flush();
+ qemu_log_unlock();
+}
+
+static void can_host_socketcan_read(void *opaque)
+{
+ CanHostSocketCAN *c = opaque;
+ CanHostState *ch = CAN_HOST(c);
+
+ /* CAN_READ_BUF_LEN for multiple messages syscall is possible for future */
+ c->bufcnt = read(c->fd, c->buf, sizeof(qemu_can_frame));
+ if (c->bufcnt < 0) {
+ warn_report("CAN bus host read failed (%s)", strerror(errno));
+ return;
+ }
+
+ can_bus_client_send(&ch->bus_client, c->buf, 1);
+
+ if (DEBUG_CAN) {
+ can_host_socketcan_display_msg(c->buf);
+ }
+}
+
+static int can_host_socketcan_can_receive(CanBusClientState *client)
+{
+ return 1;
+}
+
+static ssize_t can_host_socketcan_receive(CanBusClientState *client,
+ const qemu_can_frame *frames, size_t frames_cnt)
+{
+ CanHostState *ch = container_of(client, CanHostState, bus_client);
+ CanHostSocketCAN *c = CAN_HOST_SOCKETCAN(ch);
+
+ size_t len = sizeof(qemu_can_frame);
+ int res;
+
+ if (c->fd < 0) {
+ return -1;
+ }
+
+ res = write(c->fd, frames, len);
+
+ if (!res) {
+ warn_report("[cansocketcan]: write message to host returns zero");
+ return -1;
+ }
+
+ if (res != len) {
+ if (res < 0) {
+ warn_report("[cansocketcan]: write to host failed (%s)",
+ strerror(errno));
+ } else {
+ warn_report("[cansocketcan]: write to host truncated");
+ }
+ return -1;
+ }
+
+ return 1;
+}
+
+static void can_host_socketcan_disconnect(CanHostState *ch)
+{
+ CanHostSocketCAN *c = CAN_HOST_SOCKETCAN(ch);
+
+ if (c->fd >= 0) {
+ qemu_set_fd_handler(c->fd, NULL, NULL, c);
+ close(c->fd);
+ c->fd = -1;
+ }
+
+ g_free(c->rfilter);
+ c->rfilter = NULL;
+ c->rfilter_num = 0;
+}
+
+static CanBusClientInfo can_host_socketcan_bus_client_info = {
+ .can_receive = can_host_socketcan_can_receive,
+ .receive = can_host_socketcan_receive,
+};
+
+static void can_host_socketcan_connect(CanHostState *ch, Error **errp)
+{
+ CanHostSocketCAN *c = CAN_HOST_SOCKETCAN(ch);
+ int s; /* can raw socket */
+ struct sockaddr_can addr;
+ struct ifreq ifr;
+
+ /* open socket */
+ s = qemu_socket(PF_CAN, SOCK_RAW, CAN_RAW);
+ if (s < 0) {
+ error_setg_errno(errp, errno, "failed to create CAN_RAW socket");
+ return;
+ }
+
+ addr.can_family = AF_CAN;
+ memset(&ifr.ifr_name, 0, sizeof(ifr.ifr_name));
+ strcpy(ifr.ifr_name, c->ifname);
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+ error_setg_errno(errp, errno,
+ "SocketCAN host interface %s not available", c->ifname);
+ goto fail;
+ }
+ addr.can_ifindex = ifr.ifr_ifindex;
+
+ c->err_mask = 0xffffffff; /* Receive error frame. */
+ setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
+ &c->err_mask, sizeof(c->err_mask));
+
+ c->rfilter_num = 1;
+ c->rfilter = g_new(struct qemu_can_filter, c->rfilter_num);
+
+ /* Receive all data frame. If |= CAN_INV_FILTER no data. */
+ c->rfilter[0].can_id = 0;
+ c->rfilter[0].can_mask = 0;
+ c->rfilter[0].can_mask &= ~CAN_ERR_FLAG;
+
+ setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, c->rfilter,
+ c->rfilter_num * sizeof(struct qemu_can_filter));
+
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ error_setg_errno(errp, errno, "failed to bind to host interface %s",
+ c->ifname);
+ goto fail;
+ }
+
+ c->fd = s;
+ ch->bus_client.info = &can_host_socketcan_bus_client_info;
+ qemu_set_fd_handler(c->fd, can_host_socketcan_read, NULL, c);
+ return;
+
+fail:
+ close(s);
+ g_free(c->rfilter);
+ c->rfilter = NULL;
+ c->rfilter_num = 0;
+}
+
+static char *can_host_socketcan_get_if(Object *obj, Error **errp)
+{
+ CanHostSocketCAN *c = CAN_HOST_SOCKETCAN(obj);
+
+ return g_strdup(c->ifname);
+}
+
+static void can_host_socketcan_set_if(Object *obj, const char *value, Error **errp)
+{
+ CanHostSocketCAN *c = CAN_HOST_SOCKETCAN(obj);
+ struct ifreq ifr;
+
+ if (strlen(value) >= sizeof(ifr.ifr_name)) {
+ error_setg(errp, "CAN interface name longer than %zd characters",
+ sizeof(ifr.ifr_name) - 1);
+ return;
+ }
+
+ if (c->fd != -1) {
+ error_setg(errp, "CAN interface already connected");
+ return;
+ }
+
+ g_free(c->ifname);
+ c->ifname = g_strdup(value);
+}
+
+static void can_host_socketcan_instance_init(Object *obj)
+{
+ CanHostSocketCAN *c = CAN_HOST_SOCKETCAN(obj);
+
+ c->fd = -1;
+}
+
+static void can_host_socketcan_class_init(ObjectClass *klass,
+ void *class_data G_GNUC_UNUSED)
+{
+ CanHostClass *chc = CAN_HOST_CLASS(klass);
+
+ object_class_property_add_str(klass, "if",
+ can_host_socketcan_get_if,
+ can_host_socketcan_set_if,
+ &error_abort);
+ chc->connect = can_host_socketcan_connect;
+ chc->disconnect = can_host_socketcan_disconnect;
+}
+
+static const TypeInfo can_host_socketcan_info = {
+ .parent = TYPE_CAN_HOST,
+ .name = TYPE_CAN_HOST_SOCKETCAN,
+ .instance_size = sizeof(CanHostSocketCAN),
+ .instance_init = can_host_socketcan_instance_init,
+ .class_init = can_host_socketcan_class_init,
+};
+
+static void can_host_register_types(void)
+{
+ type_register_static(&can_host_socketcan_info);
+}
+
+type_init(can_host_register_types);
diff --git a/rules.mak b/rules.mak
index 5fb4951561..6e943335f3 100644
--- a/rules.mak
+++ b/rules.mak
@@ -131,8 +131,6 @@ modules:
# If called with only a single argument, will print nothing in quiet mode.
quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1))
-MAKEFLAGS += $(if $(V),,--no-print-directory --quiet)
-
# cc-option
# Usage: CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0)
diff --git a/target/i386/hax-all.c b/target/i386/hax-all.c
index bc9a12c1ee..cad7531406 100644
--- a/target/i386/hax-all.c
+++ b/target/i386/hax-all.c
@@ -103,6 +103,8 @@ static int hax_get_capability(struct hax_state *hax)
return -ENOTSUP;
}
+ hax->supports_64bit_ramblock = !!(cap->winfo & HAX_CAP_64BIT_RAMBLOCK);
+
if (cap->wstatus & HAX_CAP_MEMQUOTA) {
if (cap->mem_quota < hax->mem_quota) {
fprintf(stderr, "The VM memory needed exceeds the driver limit.\n");
diff --git a/target/i386/hax-darwin.c b/target/i386/hax-darwin.c
index ee9417454c..acdde476a0 100644
--- a/target/i386/hax-darwin.c
+++ b/target/i386/hax-darwin.c
@@ -28,21 +28,36 @@ hax_fd hax_mod_open(void)
return fd;
}
-int hax_populate_ram(uint64_t va, uint32_t size)
+int hax_populate_ram(uint64_t va, uint64_t size)
{
int ret;
- struct hax_alloc_ram_info info;
if (!hax_global.vm || !hax_global.vm->fd) {
fprintf(stderr, "Allocate memory before vm create?\n");
return -EINVAL;
}
- info.size = size;
- info.va = va;
- ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_ALLOC_RAM, &info);
+ if (hax_global.supports_64bit_ramblock) {
+ struct hax_ramblock_info ramblock = {
+ .start_va = va,
+ .size = size,
+ .reserved = 0
+ };
+
+ ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_ADD_RAMBLOCK, &ramblock);
+ } else {
+ struct hax_alloc_ram_info info = {
+ .size = (uint32_t)size,
+ .pad = 0,
+ .va = va
+ };
+
+ ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_ALLOC_RAM, &info);
+ }
if (ret < 0) {
- fprintf(stderr, "Failed to allocate %x memory\n", size);
+ fprintf(stderr, "Failed to register RAM block: ret=%d, va=0x%" PRIx64
+ ", size=0x%" PRIx64 ", method=%s\n", ret, va, size,
+ hax_global.supports_64bit_ramblock ? "new" : "legacy");
return ret;
}
return 0;
diff --git a/target/i386/hax-darwin.h b/target/i386/hax-darwin.h
index fb8e25a096..51af0e8c88 100644
--- a/target/i386/hax-darwin.h
+++ b/target/i386/hax-darwin.h
@@ -44,6 +44,7 @@ static inline void hax_close_fd(hax_fd fd)
#define HAX_VM_IOCTL_SET_RAM _IOWR(0, 0x82, struct hax_set_ram_info)
#define HAX_VM_IOCTL_VCPU_DESTROY _IOW(0, 0x83, uint32_t)
#define HAX_VM_IOCTL_NOTIFY_QEMU_VERSION _IOW(0, 0x84, struct hax_qemu_version)
+#define HAX_VM_IOCTL_ADD_RAMBLOCK _IOW(0, 0x85, struct hax_ramblock_info)
#define HAX_VCPU_IOCTL_RUN _IO(0, 0xc0)
#define HAX_VCPU_IOCTL_SET_MSRS _IOWR(0, 0xc1, struct hax_msr_data)
diff --git a/target/i386/hax-i386.h b/target/i386/hax-i386.h
index 8ffe91fcb5..6abc156f88 100644
--- a/target/i386/hax-i386.h
+++ b/target/i386/hax-i386.h
@@ -37,6 +37,7 @@ struct hax_state {
uint32_t version;
struct hax_vm *vm;
uint64_t mem_quota;
+ bool supports_64bit_ramblock;
};
#define HAX_MAX_VCPU 0x10
diff --git a/target/i386/hax-interface.h b/target/i386/hax-interface.h
index d141308831..93d5fcb1dc 100644
--- a/target/i386/hax-interface.h
+++ b/target/i386/hax-interface.h
@@ -308,6 +308,13 @@ struct hax_alloc_ram_info {
uint32_t pad;
uint64_t va;
} __attribute__ ((__packed__));
+
+struct hax_ramblock_info {
+ uint64_t start_va;
+ uint64_t size;
+ uint64_t reserved;
+} __attribute__ ((__packed__));
+
#define HAX_RAM_INFO_ROM 0x01 /* Read-Only */
#define HAX_RAM_INFO_INVALID 0x80 /* Unmapped, usually used for MMIO */
struct hax_set_ram_info {
@@ -327,6 +334,7 @@ struct hax_set_ram_info {
#define HAX_CAP_MEMQUOTA 0x2
#define HAX_CAP_UG 0x4
+#define HAX_CAP_64BIT_RAMBLOCK 0x8
struct hax_capabilityinfo {
/* bit 0: 1 - working
diff --git a/target/i386/hax-mem.c b/target/i386/hax-mem.c
index 27a0d214f2..f46e85544d 100644
--- a/target/i386/hax-mem.c
+++ b/target/i386/hax-mem.c
@@ -174,6 +174,7 @@ static void hax_process_section(MemoryRegionSection *section, uint8_t flags)
ram_addr_t size = int128_get64(section->size);
unsigned int delta;
uint64_t host_va;
+ uint32_t max_mapping_size;
/* We only care about RAM and ROM regions */
if (!memory_region_is_ram(mr)) {
@@ -206,10 +207,23 @@ static void hax_process_section(MemoryRegionSection *section, uint8_t flags)
flags |= HAX_RAM_INFO_ROM;
}
- /* the kernel module interface uses 32-bit sizes (but we could split...) */
- g_assert(size <= UINT32_MAX);
-
- hax_update_mapping(start_pa, size, host_va, flags);
+ /*
+ * The kernel module interface uses 32-bit sizes:
+ * https://github.com/intel/haxm/blob/master/API.md#hax_vm_ioctl_set_ram
+ *
+ * If the mapping size is longer than 32 bits, we can't process it in one
+ * call into the kernel. Instead, we split the mapping into smaller ones,
+ * and call hax_update_mapping() on each.
+ */
+ max_mapping_size = UINT32_MAX & qemu_real_host_page_mask;
+ while (size > max_mapping_size) {
+ hax_update_mapping(start_pa, max_mapping_size, host_va, flags);
+ start_pa += max_mapping_size;
+ size -= max_mapping_size;
+ host_va += max_mapping_size;
+ }
+ /* Now size <= max_mapping_size */
+ hax_update_mapping(start_pa, (uint32_t)size, host_va, flags);
}
static void hax_region_add(MemoryListener *listener,
@@ -283,12 +297,16 @@ static MemoryListener hax_memory_listener = {
static void hax_ram_block_added(RAMBlockNotifier *n, void *host, size_t size)
{
/*
- * In HAX, QEMU allocates the virtual address, and HAX kernel
- * populates the memory with physical memory. Currently we have no
- * paging, so user should make sure enough free memory in advance.
+ * We must register each RAM block with the HAXM kernel module, or
+ * hax_set_ram() will fail for any mapping into the RAM block:
+ * https://github.com/intel/haxm/blob/master/API.md#hax_vm_ioctl_alloc_ram
+ *
+ * Old versions of the HAXM kernel module (< 6.2.0) used to preallocate all
+ * host physical pages for the RAM block as part of this registration
+ * process, hence the name hax_populate_ram().
*/
if (hax_populate_ram((uint64_t)(uintptr_t)host, size) < 0) {
- fprintf(stderr, "HAX failed to populate RAM");
+ fprintf(stderr, "HAX failed to populate RAM\n");
abort();
}
}
diff --git a/target/i386/hax-windows.c b/target/i386/hax-windows.c
index 15a180b646..b1ac737ae4 100644
--- a/target/i386/hax-windows.c
+++ b/target/i386/hax-windows.c
@@ -58,10 +58,9 @@ static int hax_open_device(hax_fd *fd)
return fd;
}
-int hax_populate_ram(uint64_t va, uint32_t size)
+int hax_populate_ram(uint64_t va, uint64_t size)
{
int ret;
- struct hax_alloc_ram_info info;
HANDLE hDeviceVM;
DWORD dSize = 0;
@@ -70,18 +69,35 @@ int hax_populate_ram(uint64_t va, uint32_t size)
return -EINVAL;
}
- info.size = size;
- info.va = va;
-
hDeviceVM = hax_global.vm->fd;
-
- ret = DeviceIoControl(hDeviceVM,
- HAX_VM_IOCTL_ALLOC_RAM,
- &info, sizeof(info), NULL, 0, &dSize,
- (LPOVERLAPPED) NULL);
+ if (hax_global.supports_64bit_ramblock) {
+ struct hax_ramblock_info ramblock = {
+ .start_va = va,
+ .size = size,
+ .reserved = 0
+ };
+
+ ret = DeviceIoControl(hDeviceVM,
+ HAX_VM_IOCTL_ADD_RAMBLOCK,
+ &ramblock, sizeof(ramblock), NULL, 0, &dSize,
+ (LPOVERLAPPED) NULL);
+ } else {
+ struct hax_alloc_ram_info info = {
+ .size = (uint32_t) size,
+ .pad = 0,
+ .va = va
+ };
+
+ ret = DeviceIoControl(hDeviceVM,
+ HAX_VM_IOCTL_ALLOC_RAM,
+ &info, sizeof(info), NULL, 0, &dSize,
+ (LPOVERLAPPED) NULL);
+ }
if (!ret) {
- fprintf(stderr, "Failed to allocate %x memory\n", size);
+ fprintf(stderr, "Failed to register RAM block: va=0x%" PRIx64
+ ", size=0x%" PRIx64 ", method=%s\n", va, size,
+ hax_global.supports_64bit_ramblock ? "new" : "legacy");
return ret;
}
diff --git a/target/i386/hax-windows.h b/target/i386/hax-windows.h
index 20e2f85407..12cbd813dc 100644
--- a/target/i386/hax-windows.h
+++ b/target/i386/hax-windows.h
@@ -57,6 +57,8 @@ static inline int hax_invalid_fd(hax_fd fd)
METHOD_BUFFERED, FILE_ANY_ACCESS)
#define HAX_VM_IOCTL_VCPU_DESTROY CTL_CODE(HAX_DEVICE_TYPE, 0x905, \
METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_VM_IOCTL_ADD_RAMBLOCK CTL_CODE(HAX_DEVICE_TYPE, 0x913, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
#define HAX_VCPU_IOCTL_RUN CTL_CODE(HAX_DEVICE_TYPE, 0x906, \
METHOD_BUFFERED, FILE_ANY_ACCESS)
diff --git a/tests/Makefile.include b/tests/Makefile.include
index f41da235ae..278c13aa93 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -294,6 +294,7 @@ check-qtest-i386-y += tests/migration-test$(EXESUF)
check-qtest-i386-y += tests/test-x86-cpuid-compat$(EXESUF)
check-qtest-i386-y += tests/numa-test$(EXESUF)
check-qtest-x86_64-y += $(check-qtest-i386-y)
+check-qtest-x86_64-y += tests/sdhci-test$(EXESUF)
gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c
gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y))
@@ -367,8 +368,10 @@ gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c
check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF)
gcov-files-arm-y += hw/timer/arm_mptimer.c
check-qtest-arm-y += tests/boot-serial-test$(EXESUF)
+check-qtest-arm-y += tests/sdhci-test$(EXESUF)
check-qtest-aarch64-y = tests/numa-test$(EXESUF)
+check-qtest-aarch64-y += tests/sdhci-test$(EXESUF)
check-qtest-microblazeel-y = $(check-qtest-microblaze-y)
@@ -822,6 +825,7 @@ tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y)
tests/numa-test$(EXESUF): tests/numa-test.o
tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o
+tests/sdhci-test$(EXESUF): tests/sdhci-test.o $(libqos-pc-obj-y)
tests/migration/stress$(EXESUF): tests/migration/stress.o
$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
diff --git a/tests/acpi-test-data/pc/FACP b/tests/acpi-test-data/pc/FACP
index 0639999ed1..261ebdc5d1 100644
--- a/tests/acpi-test-data/pc/FACP
+++ b/tests/acpi-test-data/pc/FACP
Binary files differ
diff --git a/tests/acpi-test-data/q35/FACP b/tests/acpi-test-data/q35/FACP
index 19f3ac3ce6..72c9d97902 100644
--- a/tests/acpi-test-data/q35/FACP
+++ b/tests/acpi-test-data/q35/FACP
Binary files differ
diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c
index b354aaafe6..2b332ed3c1 100644
--- a/tests/bios-tables-test.c
+++ b/tests/bios-tables-test.c
@@ -194,6 +194,35 @@ static void test_acpi_fadt_table(test_data *data)
le32_to_cpu(fadt_table->length)));
}
+static void sanitize_fadt_ptrs(test_data *data)
+{
+ /* fixup pointers in FADT */
+ int i;
+
+ for (i = 0; i < data->tables->len; i++) {
+ AcpiSdtTable *sdt = &g_array_index(data->tables, AcpiSdtTable, i);
+
+ if (memcmp(&sdt->header.signature, "FACP", 4)) {
+ continue;
+ }
+
+ /* sdt->aml field offset := spec offset - header size */
+ memset(sdt->aml + 0, 0, 4); /* sanitize FIRMWARE_CTRL(36) ptr */
+ memset(sdt->aml + 4, 0, 4); /* sanitize DSDT(40) ptr */
+ if (sdt->header.revision >= 3) {
+ memset(sdt->aml + 96, 0, 8); /* sanitize X_FIRMWARE_CTRL(132) ptr */
+ memset(sdt->aml + 104, 0, 8); /* sanitize X_DSDT(140) ptr */
+ }
+
+ /* update checksum */
+ sdt->header.checksum = 0;
+ sdt->header.checksum -=
+ acpi_calc_checksum((uint8_t *)sdt, sizeof(AcpiTableHeader)) +
+ acpi_calc_checksum((uint8_t *)sdt->aml, sdt->aml_len);
+ break;
+ }
+}
+
static void test_acpi_facs_table(test_data *data)
{
AcpiFacsDescriptorRev1 *facs_table = &data->facs_table;
@@ -248,14 +277,14 @@ static void test_acpi_dsdt_table(test_data *data)
/* Load all tables and add to test list directly RSDT referenced tables */
static void fetch_rsdt_referenced_tables(test_data *data)
{
- int tables_nr = data->rsdt_tables_nr - 1; /* fadt is first */
+ int tables_nr = data->rsdt_tables_nr;
int i;
for (i = 0; i < tables_nr; i++) {
AcpiSdtTable ssdt_table;
uint32_t addr;
- addr = le32_to_cpu(data->rsdt_tables_addr[i + 1]); /* fadt is first */
+ addr = le32_to_cpu(data->rsdt_tables_addr[i]);
fetch_table(&ssdt_table, addr);
/* Add table to ASL test tables list */
@@ -650,6 +679,8 @@ static void test_acpi_one(const char *params, test_data *data)
test_acpi_dsdt_table(data);
fetch_rsdt_referenced_tables(data);
+ sanitize_fadt_ptrs(data);
+
if (iasl) {
if (getenv(ACPI_REBUILD_EXPECTED_AML)) {
dump_aml_files(data, true);
diff --git a/tests/sdhci-test.c b/tests/sdhci-test.c
new file mode 100644
index 0000000000..493023fd0c
--- /dev/null
+++ b/tests/sdhci-test.c
@@ -0,0 +1,250 @@
+/*
+ * QTest testcase for SDHCI controllers
+ *
+ * Written by Philippe Mathieu-Daudé <f4bug@amsat.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "hw/registerfields.h"
+#include "libqtest.h"
+#include "libqos/pci-pc.h"
+#include "hw/pci/pci.h"
+
+#define SDHC_CAPAB 0x40
+FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8); /* since v2 */
+FIELD(SDHC_CAPAB, SDMA, 22, 1);
+FIELD(SDHC_CAPAB, SDR, 32, 3); /* since v3 */
+FIELD(SDHC_CAPAB, DRIVER, 36, 3); /* since v3 */
+#define SDHC_HCVER 0xFE
+
+static const struct sdhci_t {
+ const char *arch, *machine;
+ struct {
+ uintptr_t addr;
+ uint8_t version;
+ uint8_t baseclock;
+ struct {
+ bool sdma;
+ uint64_t reg;
+ } capab;
+ } sdhci;
+ struct {
+ uint16_t vendor_id, device_id;
+ } pci;
+} models[] = {
+ /* PC via PCI */
+ { "x86_64", "pc",
+ {-1, 2, 0, {1, 0x057834b4} },
+ .pci = { PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_SDHCI } },
+
+ /* Exynos4210 */
+ { "arm", "smdkc210",
+ {0x12510000, 2, 0, {1, 0x5e80080} } },
+
+ /* i.MX 6 */
+ { "arm", "sabrelite",
+ {0x02190000, 3, 0, {1, 0x057834b4} } },
+
+ /* BCM2835 */
+ { "arm", "raspi2",
+ {0x3f300000, 3, 52, {0, 0x052134b4} } },
+
+ /* Zynq-7000 */
+ { "arm", "xilinx-zynq-a9", /* Datasheet: UG585 (v1.12.1) */
+ {0xe0100000, 2, 0, {1, 0x69ec0080} } },
+
+ /* ZynqMP */
+ { "aarch64", "xlnx-zcu102", /* Datasheet: UG1085 (v1.7) */
+ {0xff160000, 3, 0, {1, 0x280737ec6481} } },
+
+};
+
+typedef struct QSDHCI {
+ struct {
+ QPCIBus *bus;
+ QPCIDevice *dev;
+ } pci;
+ union {
+ QPCIBar mem_bar;
+ uint64_t addr;
+ };
+} QSDHCI;
+
+static uint16_t sdhci_readw(QSDHCI *s, uint32_t reg)
+{
+ uint16_t val;
+
+ if (s->pci.dev) {
+ val = qpci_io_readw(s->pci.dev, s->mem_bar, reg);
+ } else {
+ val = qtest_readw(global_qtest, s->addr + reg);
+ }
+
+ return val;
+}
+
+static uint64_t sdhci_readq(QSDHCI *s, uint32_t reg)
+{
+ uint64_t val;
+
+ if (s->pci.dev) {
+ val = qpci_io_readq(s->pci.dev, s->mem_bar, reg);
+ } else {
+ val = qtest_readq(global_qtest, s->addr + reg);
+ }
+
+ return val;
+}
+
+static void sdhci_writeq(QSDHCI *s, uint32_t reg, uint64_t val)
+{
+ if (s->pci.dev) {
+ qpci_io_writeq(s->pci.dev, s->mem_bar, reg, val);
+ } else {
+ qtest_writeq(global_qtest, s->addr + reg, val);
+ }
+}
+
+static void check_specs_version(QSDHCI *s, uint8_t version)
+{
+ uint32_t v;
+
+ v = sdhci_readw(s, SDHC_HCVER);
+ v &= 0xff;
+ v += 1;
+ g_assert_cmpuint(v, ==, version);
+}
+
+static void check_capab_capareg(QSDHCI *s, uint64_t expec_capab)
+{
+ uint64_t capab;
+
+ capab = sdhci_readq(s, SDHC_CAPAB);
+ g_assert_cmphex(capab, ==, expec_capab);
+}
+
+static void check_capab_readonly(QSDHCI *s)
+{
+ const uint64_t vrand = 0x123456789abcdef;
+ uint64_t capab0, capab1;
+
+ capab0 = sdhci_readq(s, SDHC_CAPAB);
+ g_assert_cmpuint(capab0, !=, vrand);
+
+ sdhci_writeq(s, SDHC_CAPAB, vrand);
+ capab1 = sdhci_readq(s, SDHC_CAPAB);
+ g_assert_cmpuint(capab1, !=, vrand);
+ g_assert_cmpuint(capab1, ==, capab0);
+}
+
+static void check_capab_baseclock(QSDHCI *s, uint8_t expec_freq)
+{
+ uint64_t capab, capab_freq;
+
+ if (!expec_freq) {
+ return;
+ }
+ capab = sdhci_readq(s, SDHC_CAPAB);
+ capab_freq = FIELD_EX64(capab, SDHC_CAPAB, BASECLKFREQ);
+ g_assert_cmpuint(capab_freq, ==, expec_freq);
+}
+
+static void check_capab_sdma(QSDHCI *s, bool supported)
+{
+ uint64_t capab, capab_sdma;
+
+ capab = sdhci_readq(s, SDHC_CAPAB);
+ capab_sdma = FIELD_EX64(capab, SDHC_CAPAB, SDMA);
+ g_assert_cmpuint(capab_sdma, ==, supported);
+}
+
+static void check_capab_v3(QSDHCI *s, uint8_t version)
+{
+ uint64_t capab, capab_v3;
+
+ if (version < 3) {
+ /* before v3 those fields are RESERVED */
+ capab = sdhci_readq(s, SDHC_CAPAB);
+ capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, SDR);
+ g_assert_cmpuint(capab_v3, ==, 0);
+ capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, DRIVER);
+ g_assert_cmpuint(capab_v3, ==, 0);
+ }
+}
+
+static QSDHCI *machine_start(const struct sdhci_t *test)
+{
+ QSDHCI *s = g_new0(QSDHCI, 1);
+
+ if (test->pci.vendor_id) {
+ /* PCI */
+ uint16_t vendor_id, device_id;
+ uint64_t barsize;
+
+ global_qtest = qtest_startf("-machine %s -device sdhci-pci",
+ test->machine);
+
+ s->pci.bus = qpci_init_pc(NULL);
+
+ /* Find PCI device and verify it's the right one */
+ s->pci.dev = qpci_device_find(s->pci.bus, QPCI_DEVFN(4, 0));
+ g_assert_nonnull(s->pci.dev);
+ vendor_id = qpci_config_readw(s->pci.dev, PCI_VENDOR_ID);
+ device_id = qpci_config_readw(s->pci.dev, PCI_DEVICE_ID);
+ g_assert(vendor_id == test->pci.vendor_id);
+ g_assert(device_id == test->pci.device_id);
+ s->mem_bar = qpci_iomap(s->pci.dev, 0, &barsize);
+ qpci_device_enable(s->pci.dev);
+ } else {
+ /* SysBus */
+ global_qtest = qtest_startf("-machine %s", test->machine);
+ s->addr = test->sdhci.addr;
+ }
+
+ return s;
+}
+
+static void machine_stop(QSDHCI *s)
+{
+ g_free(s->pci.dev);
+ qtest_quit(global_qtest);
+}
+
+static void test_machine(const void *data)
+{
+ const struct sdhci_t *test = data;
+ QSDHCI *s;
+
+ s = machine_start(test);
+
+ check_specs_version(s, test->sdhci.version);
+ check_capab_capareg(s, test->sdhci.capab.reg);
+ check_capab_readonly(s);
+ check_capab_v3(s, test->sdhci.version);
+ check_capab_sdma(s, test->sdhci.capab.sdma);
+ check_capab_baseclock(s, test->sdhci.baseclock);
+
+ machine_stop(s);
+}
+
+int main(int argc, char *argv[])
+{
+ const char *arch = qtest_get_arch();
+ char *name;
+ int i;
+
+ g_test_init(&argc, &argv, NULL);
+ for (i = 0; i < ARRAY_SIZE(models); i++) {
+ if (strcmp(arch, models[i].arch)) {
+ continue;
+ }
+ name = g_strdup_printf("sdhci/%s", models[i].machine);
+ qtest_add_data_func(name, &models[i], test_machine);
+ g_free(name);
+ }
+
+ return g_test_run();
+}