* CAN bus (will be under network maintainner)

* scsi-block opblockers (myself)
 * Dirty log bitmap cleanup (myself)
 * SDHCI improvements and tests (Philippe)
 * HAX support for larger guest sizese (Yu Ning)
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQEcBAABAgAGBQJagwYqAAoJEL/70l94x66Dy54H+weJG15nv+ihR7diKmelsmAA
 HQCYGImo8xKzvSwHOqLW3pisPq0Xey6Zz/h48H/JpWVLwGQ4cIenWoSIHRY9lJzy
 25+Yxa4omXCq1pDuMqfMlu+OB6/0rBTvWHolmnCeRfpuLj3vY5sQ+iEz33cSJaL6
 J/NsUGJX0zy9VKM5Fpu02ZN0EMmjDFMQYaGiKKv/G1kGmJgj4VeMBJJEkqjS0qiC
 xYQ4coRDOSdtkAvMS4k/2oMZGjPC6r9FBay/3MbyTqAzsSKKAuBs5HuTjJ6bQF+R
 9xj0dqTNEHQteXtMAijAIhSHN2uGqRc+ZvBk5wyboj6heWabYQECzY05aolulf8=
 =KWrC
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging

* CAN bus (will be under network maintainner)
* scsi-block opblockers (myself)
* Dirty log bitmap cleanup (myself)
* SDHCI improvements and tests (Philippe)
* HAX support for larger guest sizese (Yu Ning)

# gpg: Signature made Tue 13 Feb 2018 15:37:14 GMT
# gpg:                using RSA key BFFBD25F78C7AE83
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>"
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>"
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* remotes/bonzini/tags/for-upstream: (48 commits)
  travis: use libgcc-4.8-dev (libgcc-6-dev is not available on Ubuntu 14.04)
  memory: unify loops to sync dirty log bitmap
  memory: hide memory_region_sync_dirty_bitmap behind DirtyBitmapSnapshot
  memory: remove memory_region_test_and_clear_dirty
  g364fb: switch to using DirtyBitmapSnapshot
  sdhci: add Spec v4.2 register definitions
  sdhci: add a check_capab_v3() qtest
  sdhci: check Spec v3 capabilities qtest
  hw/arm/xilinx_zynqmp: enable the UHS-I mode
  hw/arm/xilinx_zynqmp: fix the capabilities/spec version to match the datasheet
  hw/arm/fsl-imx6: implement SDHCI Spec. v3
  hw/arm/bcm2835_peripherals: change maximum block size to 1kB
  hw/arm/bcm2835_peripherals: implement SDHCI Spec v3
  sdhci: implement CMD/DAT[] fields in the Present State register
  sdhci: implement UHS-I voltage switch
  sdbus: add trace events
  sdhci: implement the Host Control 2 register (tuning sequence)
  sdhci: rename the hostctl1 register
  sdhci: add support for v3 capabilities
  hw/arm/xilinx_zynq: fix the capabilities register to match the datasheet
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-02-13 18:24:07 +00:00
commit bec9c64ef7
52 changed files with 3706 additions and 295 deletions

View File

@ -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

View File

@ -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)'

16
configure vendored
View File

@ -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

View File

@ -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

107
docs/can.txt Normal file
View File

@ -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/

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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]);
for (n = 0; n < 2; n++) {
int hci_irq = n ? 79 : 56;
hwaddr hci_addr = n ? 0xE0101000 : 0xE0100000;
DriveInfo *di;
BlockBackend *blk;
DeviceState *carddev;
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);
/* 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]);
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);
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);

View File

@ -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;
char *bus_name = g_strdup_printf("sd-bus%d", i);
SysBusDevice *sbd = SYS_BUS_DEVICE(&s->sdhci[i]);
Object *sdhci = OBJECT(&s->sdhci[i]);
object_property_set_bool(OBJECT(&s->sdhci[i]), true,
"realized", &err);
/* 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);
}

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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) {

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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/

4
hw/net/can/Makefile.objs Normal file
View File

@ -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

319
hw/net/can/can_kvaser_pci.c Normal file
View File

@ -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)

View File

@ -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)

View File

@ -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)

953
hw/net/can/can_sja1000.c Normal file
View File

@ -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()
}
};

146
hw/net/can/can_sja1000.h Normal file
View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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)

View File

@ -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
#define SDHC_CAPAB_REG_DEFAULT 0x057834b4
/* 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
static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
{
return 1 << (9 + FIELD_EX32(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH));
}
#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
/* 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;
}
#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
static void sdhci_check_capareg(SDHCIState *s, Error **errp)
{
uint64_t msk = s->capareg;
uint32_t val;
bool y;
#if SDHC_CAPAB_TOCLKFREQ > 63
#error Timeout clock frequency can have value in range 0-63 only!
#endif
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);
#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))
val = FIELD_EX64(s->capareg, SDHC_CAPAB, UHS_II);
trace_sdhci_capareg("UHS-II", val);
msk = FIELD_DP64(msk, SDHC_CAPAB, UHS_II, 0);
#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
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);

View File

@ -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"

View File

@ -1090,33 +1090,15 @@ bool memory_region_get_dirty(MemoryRegion *mr, hwaddr addr,
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
@ -1153,17 +1135,6 @@ bool memory_region_snapshot_get_dirty(MemoryRegion *mr,
DirtyBitmapSnapshot *snap,
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.

View File

@ -55,6 +55,20 @@
#define AKE_SEQ_ERROR (1 << 3)
#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 */
@ -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);

View File

@ -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;
/*

123
include/net/can_emu.h Normal file
View File

@ -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

55
include/net/can_host.h Normal file
View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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/

2
net/can/Makefile.objs Normal file
View File

@ -0,0 +1,2 @@
common-obj-y += can_core.o can_host.o
common-obj-$(CONFIG_LINUX) += can_socketcan.o

138
net/can/can_core.c Normal file
View File

@ -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);

112
net/can/can_host.c Normal file
View File

@ -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);

286
net/can/can_socketcan.c Normal file
View File

@ -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);

View File

@ -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)

View File

@ -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");

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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();
}
}

View File

@ -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;
if (hax_global.supports_64bit_ramblock) {
struct hax_ramblock_info ramblock = {
.start_va = va,
.size = size,
.reserved = 0
};
ret = DeviceIoControl(hDeviceVM,
HAX_VM_IOCTL_ALLOC_RAM,
&info, sizeof(info), NULL, 0, &dSize,
(LPOVERLAPPED) NULL);
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;
}

View File

@ -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)

View File

@ -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)$@")

250
tests/sdhci-test.c Normal file
View File

@ -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();
}