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

target-arm queue:
 * Netduino 2 improvements (SPI, ADC devices)
 * fix some Mainstone key mappings
 * vmstateify tsc210x, tsc2005
 * virt: add 2.8 machine type
 * virt: support in-kernel GICv3 ITS
 * generic-loader device
 * A64: fix iss_sf decoding in disas_ld_lit
 * correctly handle 'sub pc, pc, 1' for ARMv6

# gpg: Signature made Tue 04 Oct 2016 13:41:34 BST
# gpg:                using RSA key 0x3C2525ED14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>"
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20161004: (27 commits)
  target-arm: Correctly handle 'sub pc, pc, 1' for ARMv6
  target-arm: A64: Fix decoding of iss_sf in disas_ld_lit
  cadence_gem: Fix priority queue out of bounds access
  docs: Add a generic loader explanation document
  generic-loader: Add a generic loader
  ARM: Virt: ACPI: Add GIC ITS description in ACPI MADT table
  ACPI: Add GIC Interrupt Translation Service Structure definition
  arm/virt: Add ITS to the virt board
  hw/intc/arm_gicv3_its: Implement support for in-kernel ITS emulation
  kvm-all: Pass requester ID to MSI routing functions
  target-arm: move gicv3_class_name from machine to kvm_arm.h
  hw/intc/arm_gicv3_its: Implement ITS base class
  hw/intc/arm_gic(v3)_kvm: Initialize gsi routing
  hw/arm/virt: add 2.8 machine type
  vmstateify tsc210x
  vmstateify tsc2005
  hw/arm: Fix Integrator/CM initialization
  mainstone: Add mapping for dot, slash and backspace.
  mainstone: Fix incorrect key mapping for Enter key.
  MAINTAINERS: Add Alistair to the maintainers list
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2016-10-04 13:48:25 +01:00
39 changed files with 2025 additions and 314 deletions
+21
View File
@@ -479,6 +479,21 @@ S: Maintained
F: hw/arm/virt-acpi-build.c
F: include/hw/arm/virt-acpi-build.h
STM32F205
M: Alistair Francis <alistair@alistair23.me>
S: Maintained
F: hw/arm/stm32f205_soc.c
F: hw/misc/stm32f2xx_syscfg.c
F: hw/char/stm32f2xx_usart.c
F: hw/timer/stm32f2xx_timer.c
F: hw/adc/*
F: hw/ssi/stm32f2xx_spi.c
Netduino 2
M: Alistair Francis <alistair@alistair23.me>
S: Maintained
F: hw/arm/netduino2.c
CRIS Machines
-------------
Axis Dev88
@@ -1014,6 +1029,12 @@ M: Dmitry Fleytman <dmitry@daynix.com>
S: Maintained
F: hw/net/e1000e*
Generic Loader
M: Alistair Francis <alistair.francis@xilinx.com>
S: Maintained
F: hw/core/generic-loader.c
F: include/hw/core/generic-loader.h
Subsystems
----------
Audio
+2
View File
@@ -86,6 +86,8 @@ CONFIG_ZYNQ=y
CONFIG_STM32F2XX_TIMER=y
CONFIG_STM32F2XX_USART=y
CONFIG_STM32F2XX_SYSCFG=y
CONFIG_STM32F2XX_ADC=y
CONFIG_STM32F2XX_SPI=y
CONFIG_STM32F205_SOC=y
CONFIG_VERSATILE_PCI=y
+84
View File
@@ -0,0 +1,84 @@
Copyright (c) 2016 Xilinx Inc.
This work is licensed under the terms of the GNU GPL, version 2 or later. See
the COPYING file in the top-level directory.
The 'loader' device allows the user to load multiple images or values into
QEMU at startup.
Loading Data into Memory Values
---------------------
The loader device allows memory values to be set from the command line. This
can be done by following the syntax below:
-device loader,addr=<addr>,data=<data>,data-len=<data-len>
[,data-be=<data-be>][,cpu-num=<cpu-num>]
<addr> - The address to store the data in.
<data> - The value to be written to the address. The maximum size of
the data is 8 bytes.
<data-len> - The length of the data in bytes. This argument must be
included if the data argument is.
<data-be> - Set to true if the data to be stored on the guest should be
written as big endian data. The default is to write little
endian data.
<cpu-num> - The number of the CPU's address space where the data should
be loaded. If not specified the address space of the first
CPU is used.
All values are parsed using the standard QemuOps parsing. This allows the user
to specify any values in any format supported. By default the values
will be parsed as decimal. To use hex values the user should prefix the number
with a '0x'.
An example of loading value 0x8000000e to address 0xfd1a0104 is:
-device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4
Setting a CPU's Program Counter
---------------------
The loader device allows the CPU's PC to be set from the command line. This
can be done by following the syntax below:
-device loader,addr=<addr>,cpu-num=<cpu-num>
<addr> - The value to use as the CPU's PC.
<cpu-num> - The number of the CPU whose PC should be set to the
specified value.
All values are parsed using the standard QemuOps parsing. This allows the user
to specify any values in any format supported. By default the values
will be parsed as decimal. To use hex values the user should prefix the number
with a '0x'.
An example of setting CPU 0's PC to 0x8000 is:
-device loader,addr=0x8000,cpu-num=0
Loading Files
---------------------
The loader device also allows files to be loaded into memory. This can be done
similarly to setting memory values. The syntax is shown below:
-device loader,file=<file>[,addr=<addr>][,cpu-num=<cpu-num>][,force-raw=<raw>]
<file> - A file to be loaded into memory
<addr> - The addr in memory that the file should be loaded. This is
ignored if you are using an ELF (unless force-raw is true).
This is required if you aren't loading an ELF.
<cpu-num> - This specifies the CPU that should be used. This is an
optional argument and will cause the CPU's PC to be set to
where the image is stored or in the case of an ELF file to
the value in the header. This option should only be used
for the boot image.
This will also cause the image to be written to the specified
CPU's address space. If not specified, the default is CPU 0.
<force-raw> - Forces the file to be treated as a raw image. This can be
used to specify the load address of ELF files.
All values are parsed using the standard QemuOps parsing. This allows the user
to specify any values in any format supported. By default the values
will be parsed as decimal. To use hex values the user should prefix the number
with a '0x'.
An example of loading an ELF file which CPU0 will boot is shown below:
-device loader,file=./images/boot.elf,cpu-num=0
+1
View File
@@ -1,5 +1,6 @@
devices-dirs-$(call land, $(CONFIG_VIRTIO),$(call land,$(CONFIG_VIRTFS),$(CONFIG_PCI))) += 9pfs/
devices-dirs-$(CONFIG_ACPI) += acpi/
devices-dirs-$(CONFIG_SOFTMMU) += adc/
devices-dirs-$(CONFIG_SOFTMMU) += audio/
devices-dirs-$(CONFIG_SOFTMMU) += block/
devices-dirs-$(CONFIG_SOFTMMU) += bt/
+1
View File
@@ -0,0 +1 @@
obj-$(CONFIG_STM32F2XX_ADC) += stm32f2xx_adc.o
+306
View File
@@ -0,0 +1,306 @@
/*
* STM32F2XX ADC
*
* Copyright (c) 2014 Alistair Francis <alistair@alistair23.me>
*
* 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 "hw/sysbus.h"
#include "hw/hw.h"
#include "qapi/error.h"
#include "qemu/log.h"
#include "hw/adc/stm32f2xx_adc.h"
#ifndef STM_ADC_ERR_DEBUG
#define STM_ADC_ERR_DEBUG 0
#endif
#define DB_PRINT_L(lvl, fmt, args...) do { \
if (STM_ADC_ERR_DEBUG >= lvl) { \
qemu_log("%s: " fmt, __func__, ## args); \
} \
} while (0);
#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
static void stm32f2xx_adc_reset(DeviceState *dev)
{
STM32F2XXADCState *s = STM32F2XX_ADC(dev);
s->adc_sr = 0x00000000;
s->adc_cr1 = 0x00000000;
s->adc_cr2 = 0x00000000;
s->adc_smpr1 = 0x00000000;
s->adc_smpr2 = 0x00000000;
s->adc_jofr[0] = 0x00000000;
s->adc_jofr[1] = 0x00000000;
s->adc_jofr[2] = 0x00000000;
s->adc_jofr[3] = 0x00000000;
s->adc_htr = 0x00000FFF;
s->adc_ltr = 0x00000000;
s->adc_sqr1 = 0x00000000;
s->adc_sqr2 = 0x00000000;
s->adc_sqr3 = 0x00000000;
s->adc_jsqr = 0x00000000;
s->adc_jdr[0] = 0x00000000;
s->adc_jdr[1] = 0x00000000;
s->adc_jdr[2] = 0x00000000;
s->adc_jdr[3] = 0x00000000;
s->adc_dr = 0x00000000;
}
static uint32_t stm32f2xx_adc_generate_value(STM32F2XXADCState *s)
{
/* Attempts to fake some ADC values */
s->adc_dr = s->adc_dr + 7;
switch ((s->adc_cr1 & ADC_CR1_RES) >> 24) {
case 0:
/* 12-bit */
s->adc_dr &= 0xFFF;
break;
case 1:
/* 10-bit */
s->adc_dr &= 0x3FF;
break;
case 2:
/* 8-bit */
s->adc_dr &= 0xFF;
break;
default:
/* 6-bit */
s->adc_dr &= 0x3F;
}
if (s->adc_cr2 & ADC_CR2_ALIGN) {
return (s->adc_dr << 1) & 0xFFF0;
} else {
return s->adc_dr;
}
}
static uint64_t stm32f2xx_adc_read(void *opaque, hwaddr addr,
unsigned int size)
{
STM32F2XXADCState *s = opaque;
DB_PRINT("Address: 0x%" HWADDR_PRIx "\n", addr);
if (addr >= ADC_COMMON_ADDRESS) {
qemu_log_mask(LOG_UNIMP,
"%s: ADC Common Register Unsupported\n", __func__);
}
switch (addr) {
case ADC_SR:
return s->adc_sr;
case ADC_CR1:
return s->adc_cr1;
case ADC_CR2:
return s->adc_cr2 & 0xFFFFFFF;
case ADC_SMPR1:
return s->adc_smpr1;
case ADC_SMPR2:
return s->adc_smpr2;
case ADC_JOFR1:
case ADC_JOFR2:
case ADC_JOFR3:
case ADC_JOFR4:
qemu_log_mask(LOG_UNIMP, "%s: " \
"Injection ADC is not implemented, the registers are " \
"included for compatibility\n", __func__);
return s->adc_jofr[(addr - ADC_JOFR1) / 4];
case ADC_HTR:
return s->adc_htr;
case ADC_LTR:
return s->adc_ltr;
case ADC_SQR1:
return s->adc_sqr1;
case ADC_SQR2:
return s->adc_sqr2;
case ADC_SQR3:
return s->adc_sqr3;
case ADC_JSQR:
qemu_log_mask(LOG_UNIMP, "%s: " \
"Injection ADC is not implemented, the registers are " \
"included for compatibility\n", __func__);
return s->adc_jsqr;
case ADC_JDR1:
case ADC_JDR2:
case ADC_JDR3:
case ADC_JDR4:
qemu_log_mask(LOG_UNIMP, "%s: " \
"Injection ADC is not implemented, the registers are " \
"included for compatibility\n", __func__);
return s->adc_jdr[(addr - ADC_JDR1) / 4] -
s->adc_jofr[(addr - ADC_JDR1) / 4];
case ADC_DR:
if ((s->adc_cr2 & ADC_CR2_ADON) && (s->adc_cr2 & ADC_CR2_SWSTART)) {
s->adc_cr2 ^= ADC_CR2_SWSTART;
return stm32f2xx_adc_generate_value(s);
} else {
return 0;
}
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
}
return 0;
}
static void stm32f2xx_adc_write(void *opaque, hwaddr addr,
uint64_t val64, unsigned int size)
{
STM32F2XXADCState *s = opaque;
uint32_t value = (uint32_t) val64;
DB_PRINT("Address: 0x%" HWADDR_PRIx ", Value: 0x%x\n",
addr, value);
if (addr >= 0x100) {
qemu_log_mask(LOG_UNIMP,
"%s: ADC Common Register Unsupported\n", __func__);
}
switch (addr) {
case ADC_SR:
s->adc_sr &= (value & 0x3F);
break;
case ADC_CR1:
s->adc_cr1 = value;
break;
case ADC_CR2:
s->adc_cr2 = value;
break;
case ADC_SMPR1:
s->adc_smpr1 = value;
break;
case ADC_SMPR2:
s->adc_smpr2 = value;
break;
case ADC_JOFR1:
case ADC_JOFR2:
case ADC_JOFR3:
case ADC_JOFR4:
s->adc_jofr[(addr - ADC_JOFR1) / 4] = (value & 0xFFF);
qemu_log_mask(LOG_UNIMP, "%s: " \
"Injection ADC is not implemented, the registers are " \
"included for compatibility\n", __func__);
break;
case ADC_HTR:
s->adc_htr = value;
break;
case ADC_LTR:
s->adc_ltr = value;
break;
case ADC_SQR1:
s->adc_sqr1 = value;
break;
case ADC_SQR2:
s->adc_sqr2 = value;
break;
case ADC_SQR3:
s->adc_sqr3 = value;
break;
case ADC_JSQR:
s->adc_jsqr = value;
qemu_log_mask(LOG_UNIMP, "%s: " \
"Injection ADC is not implemented, the registers are " \
"included for compatibility\n", __func__);
break;
case ADC_JDR1:
case ADC_JDR2:
case ADC_JDR3:
case ADC_JDR4:
s->adc_jdr[(addr - ADC_JDR1) / 4] = value;
qemu_log_mask(LOG_UNIMP, "%s: " \
"Injection ADC is not implemented, the registers are " \
"included for compatibility\n", __func__);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
}
}
static const MemoryRegionOps stm32f2xx_adc_ops = {
.read = stm32f2xx_adc_read,
.write = stm32f2xx_adc_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription vmstate_stm32f2xx_adc = {
.name = TYPE_STM32F2XX_ADC,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(adc_sr, STM32F2XXADCState),
VMSTATE_UINT32(adc_cr1, STM32F2XXADCState),
VMSTATE_UINT32(adc_cr2, STM32F2XXADCState),
VMSTATE_UINT32(adc_smpr1, STM32F2XXADCState),
VMSTATE_UINT32(adc_smpr2, STM32F2XXADCState),
VMSTATE_UINT32_ARRAY(adc_jofr, STM32F2XXADCState, 4),
VMSTATE_UINT32(adc_htr, STM32F2XXADCState),
VMSTATE_UINT32(adc_ltr, STM32F2XXADCState),
VMSTATE_UINT32(adc_sqr1, STM32F2XXADCState),
VMSTATE_UINT32(adc_sqr2, STM32F2XXADCState),
VMSTATE_UINT32(adc_sqr3, STM32F2XXADCState),
VMSTATE_UINT32(adc_jsqr, STM32F2XXADCState),
VMSTATE_UINT32_ARRAY(adc_jdr, STM32F2XXADCState, 4),
VMSTATE_UINT32(adc_dr, STM32F2XXADCState),
VMSTATE_END_OF_LIST()
}
};
static void stm32f2xx_adc_init(Object *obj)
{
STM32F2XXADCState *s = STM32F2XX_ADC(obj);
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
memory_region_init_io(&s->mmio, obj, &stm32f2xx_adc_ops, s,
TYPE_STM32F2XX_ADC, 0xFF);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
}
static void stm32f2xx_adc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = stm32f2xx_adc_reset;
dc->vmsd = &vmstate_stm32f2xx_adc;
}
static const TypeInfo stm32f2xx_adc_info = {
.name = TYPE_STM32F2XX_ADC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(STM32F2XXADCState),
.instance_init = stm32f2xx_adc_init,
.class_init = stm32f2xx_adc_class_init,
};
static void stm32f2xx_adc_register_types(void)
{
type_register_static(&stm32f2xx_adc_info);
}
type_init(stm32f2xx_adc_register_types)
+21 -14
View File
@@ -252,6 +252,26 @@ static void integratorcm_init(Object *obj)
/* ??? What should the high bits of this value be? */
s->cm_auxosc = 0x0007feff;
s->cm_sdram = 0x00011122;
memcpy(integrator_spd + 73, "QEMU-MEMORY", 11);
s->cm_init = 0x00000112;
s->cm_refcnt_offset = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24,
1000);
memory_region_init_ram(&s->flash, obj, "integrator.flash", 0x100000,
&error_fatal);
vmstate_register_ram_global(&s->flash);
memory_region_init_io(&s->iomem, obj, &integratorcm_ops, s,
"integratorcm", 0x00800000);
sysbus_init_mmio(dev, &s->iomem);
integratorcm_do_remap(s);
/* ??? Save/restore. */
}
static void integratorcm_realize(DeviceState *d, Error **errp)
{
IntegratorCMState *s = INTEGRATOR_CM(d);
if (s->memsz >= 256) {
integrator_spd[31] = 64;
s->cm_sdram |= 0x10;
@@ -267,20 +287,6 @@ static void integratorcm_init(Object *obj)
} else {
integrator_spd[31] = 2;
}
memcpy(integrator_spd + 73, "QEMU-MEMORY", 11);
s->cm_init = 0x00000112;
s->cm_refcnt_offset = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24,
1000);
memory_region_init_ram(&s->flash, obj, "integrator.flash", 0x100000,
&error_fatal);
vmstate_register_ram_global(&s->flash);
memory_region_init_io(&s->iomem, obj, &integratorcm_ops, s,
"integratorcm", 0x00800000);
sysbus_init_mmio(dev, &s->iomem);
integratorcm_do_remap(s);
/* ??? Save/restore. */
}
/* Integrator/CP hardware emulation. */
@@ -633,6 +639,7 @@ static void core_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->props = core_properties;
dc->realize = integratorcm_realize;
}
static const TypeInfo core_info = {
+4 -1
View File
@@ -73,8 +73,10 @@ static const struct keymap map[0xE0] = {
[0x2f] = {3,3}, /* v */
[0x11] = {3,4}, /* w */
[0x2d] = {3,5}, /* x */
[0x34] = {4,0}, /* . */
[0x15] = {4,2}, /* y */
[0x2c] = {4,3}, /* z */
[0x35] = {4,4}, /* / */
[0xc7] = {5,0}, /* Home */
[0x2a] = {5,1}, /* shift */
/*
@@ -88,7 +90,8 @@ static const struct keymap map[0xE0] = {
* Matrix position {5,4} and other keys are missing here.
* TODO: Compare with Linux code and test real hardware.
*/
[0x1c] = {5,5}, /* enter (TODO: might be wrong) */
[0x1c] = {5,4}, /* enter */
[0x0e] = {5,5}, /* backspace */
[0xc8] = {6,0}, /* up */
[0xd0] = {6,1}, /* down */
[0xcb] = {6,2}, /* left */
+74 -18
View File
@@ -34,9 +34,15 @@ static const uint32_t timer_addr[STM_NUM_TIMERS] = { 0x40000000, 0x40000400,
0x40000800, 0x40000C00 };
static const uint32_t usart_addr[STM_NUM_USARTS] = { 0x40011000, 0x40004400,
0x40004800, 0x40004C00, 0x40005000, 0x40011400 };
static const uint32_t adc_addr[STM_NUM_ADCS] = { 0x40012000, 0x40012100,
0x40012200 };
static const uint32_t spi_addr[STM_NUM_SPIS] = { 0x40013000, 0x40003800,
0x40003C00 };
static const int timer_irq[STM_NUM_TIMERS] = {28, 29, 30, 50};
static const int usart_irq[STM_NUM_USARTS] = {37, 38, 39, 52, 53, 71};
#define ADC_IRQ 18
static const int spi_irq[STM_NUM_SPIS] = {35, 36, 51};
static void stm32f205_soc_initfn(Object *obj)
{
@@ -57,13 +63,27 @@ static void stm32f205_soc_initfn(Object *obj)
TYPE_STM32F2XX_TIMER);
qdev_set_parent_bus(DEVICE(&s->timer[i]), sysbus_get_default());
}
s->adc_irqs = OR_IRQ(object_new(TYPE_OR_IRQ));
for (i = 0; i < STM_NUM_ADCS; i++) {
object_initialize(&s->adc[i], sizeof(s->adc[i]),
TYPE_STM32F2XX_ADC);
qdev_set_parent_bus(DEVICE(&s->adc[i]), sysbus_get_default());
}
for (i = 0; i < STM_NUM_SPIS; i++) {
object_initialize(&s->spi[i], sizeof(s->spi[i]),
TYPE_STM32F2XX_SPI);
qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
}
}
static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
{
STM32F205State *s = STM32F205_SOC(dev_soc);
DeviceState *syscfgdev, *usartdev, *timerdev, *nvic;
SysBusDevice *syscfgbusdev, *usartbusdev, *timerbusdev;
DeviceState *dev, *nvic;
SysBusDevice *busdev;
Error *err = NULL;
int i;
@@ -94,44 +114,80 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
s->kernel_filename, s->cpu_model);
/* System configuration controller */
syscfgdev = DEVICE(&s->syscfg);
dev = DEVICE(&s->syscfg);
object_property_set_bool(OBJECT(&s->syscfg), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
syscfgbusdev = SYS_BUS_DEVICE(syscfgdev);
sysbus_mmio_map(syscfgbusdev, 0, 0x40013800);
sysbus_connect_irq(syscfgbusdev, 0, qdev_get_gpio_in(nvic, 71));
busdev = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(busdev, 0, 0x40013800);
sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(nvic, 71));
/* Attach UART (uses USART registers) and USART controllers */
for (i = 0; i < STM_NUM_USARTS; i++) {
usartdev = DEVICE(&(s->usart[i]));
qdev_prop_set_chr(usartdev, "chardev", i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL);
dev = DEVICE(&(s->usart[i]));
qdev_prop_set_chr(dev, "chardev",
i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL);
object_property_set_bool(OBJECT(&s->usart[i]), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
usartbusdev = SYS_BUS_DEVICE(usartdev);
sysbus_mmio_map(usartbusdev, 0, usart_addr[i]);
sysbus_connect_irq(usartbusdev, 0,
qdev_get_gpio_in(nvic, usart_irq[i]));
busdev = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(busdev, 0, usart_addr[i]);
sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(nvic, usart_irq[i]));
}
/* Timer 2 to 5 */
for (i = 0; i < STM_NUM_TIMERS; i++) {
timerdev = DEVICE(&(s->timer[i]));
qdev_prop_set_uint64(timerdev, "clock-frequency", 1000000000);
dev = DEVICE(&(s->timer[i]));
qdev_prop_set_uint64(dev, "clock-frequency", 1000000000);
object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
timerbusdev = SYS_BUS_DEVICE(timerdev);
sysbus_mmio_map(timerbusdev, 0, timer_addr[i]);
sysbus_connect_irq(timerbusdev, 0,
qdev_get_gpio_in(nvic, timer_irq[i]));
busdev = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(busdev, 0, timer_addr[i]);
sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(nvic, timer_irq[i]));
}
/* ADC 1 to 3 */
object_property_set_int(OBJECT(s->adc_irqs), STM_NUM_ADCS,
"num-lines", &err);
object_property_set_bool(OBJECT(s->adc_irqs), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
qdev_connect_gpio_out(DEVICE(s->adc_irqs), 0,
qdev_get_gpio_in(nvic, ADC_IRQ));
for (i = 0; i < STM_NUM_ADCS; i++) {
dev = DEVICE(&(s->adc[i]));
object_property_set_bool(OBJECT(&s->adc[i]), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
busdev = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(busdev, 0, adc_addr[i]);
sysbus_connect_irq(busdev, 0,
qdev_get_gpio_in(DEVICE(s->adc_irqs), i));
}
/* SPI 1 and 2 */
for (i = 0; i < STM_NUM_SPIS; i++) {
dev = DEVICE(&(s->spi[i]));
object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
busdev = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(busdev, 0, spi_addr[i]);
sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(nvic, spi_irq[i]));
}
}
+12
View File
@@ -44,6 +44,7 @@
#include "hw/pci/pcie_host.h"
#include "hw/pci/pci.h"
#include "sysemu/numa.h"
#include "kvm_arm.h"
#define ARM_SPI_BASE 32
#define ACPI_POWER_BUTTON_DEVICE "PWRB"
@@ -546,6 +547,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
}
if (guest_info->gic_version == 3) {
AcpiMadtGenericTranslator *gic_its;
AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data,
sizeof *gicr);
@@ -553,6 +555,16 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
gicr->length = sizeof(*gicr);
gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base);
gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size);
if (!its_class_name()) {
return;
}
gic_its = acpi_data_push(table_data, sizeof *gic_its);
gic_its->type = ACPI_APIC_GENERIC_TRANSLATOR;
gic_its->length = sizeof(*gic_its);
gic_its->translation_id = 0;
gic_its->base_address = cpu_to_le64(memmap[VIRT_GIC_ITS].base);
} else {
gic_msi = acpi_data_push(table_data, sizeof *gic_msi);
gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME;
+59 -9
View File
@@ -76,7 +76,7 @@ typedef struct VirtBoardInfo {
int fdt_size;
uint32_t clock_phandle;
uint32_t gic_phandle;
uint32_t v2m_phandle;
uint32_t msi_phandle;
bool using_psci;
} VirtBoardInfo;
@@ -423,9 +423,22 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
}
}
static void fdt_add_its_gic_node(VirtBoardInfo *vbi)
{
vbi->msi_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
qemu_fdt_add_subnode(vbi->fdt, "/intc/its");
qemu_fdt_setprop_string(vbi->fdt, "/intc/its", "compatible",
"arm,gic-v3-its");
qemu_fdt_setprop(vbi->fdt, "/intc/its", "msi-controller", NULL, 0);
qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc/its", "reg",
2, vbi->memmap[VIRT_GIC_ITS].base,
2, vbi->memmap[VIRT_GIC_ITS].size);
qemu_fdt_setprop_cell(vbi->fdt, "/intc/its", "phandle", vbi->msi_phandle);
}
static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi)
{
vbi->v2m_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
vbi->msi_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
qemu_fdt_add_subnode(vbi->fdt, "/intc/v2m");
qemu_fdt_setprop_string(vbi->fdt, "/intc/v2m", "compatible",
"arm,gic-v2m-frame");
@@ -433,7 +446,7 @@ static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi)
qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc/v2m", "reg",
2, vbi->memmap[VIRT_GIC_V2M].base,
2, vbi->memmap[VIRT_GIC_V2M].size);
qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->v2m_phandle);
qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->msi_phandle);
}
static void fdt_add_gic_node(VirtBoardInfo *vbi, int type)
@@ -500,6 +513,26 @@ static void fdt_add_pmu_nodes(const VirtBoardInfo *vbi, int gictype)
}
}
static void create_its(VirtBoardInfo *vbi, DeviceState *gicdev)
{
const char *itsclass = its_class_name();
DeviceState *dev;
if (!itsclass) {
/* Do nothing if not supported */
return;
}
dev = qdev_create(NULL, itsclass);
object_property_set_link(OBJECT(dev), OBJECT(gicdev), "parent-gicv3",
&error_abort);
qdev_init_nofail(dev);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vbi->memmap[VIRT_GIC_ITS].base);
fdt_add_its_gic_node(vbi);
}
static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic)
{
int i;
@@ -583,7 +616,9 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, bool secure)
fdt_add_gic_node(vbi, type);
if (type == 2) {
if (type == 3) {
create_its(vbi, gicdev);
} else {
create_v2m(vbi, pic);
}
}
@@ -1025,9 +1060,9 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
nr_pcie_buses - 1);
qemu_fdt_setprop(vbi->fdt, nodename, "dma-coherent", NULL, 0);
if (vbi->v2m_phandle) {
if (vbi->msi_phandle) {
qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent",
vbi->v2m_phandle);
vbi->msi_phandle);
}
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
@@ -1479,7 +1514,7 @@ static void machvirt_machine_init(void)
}
type_init(machvirt_machine_init);
static void virt_2_7_instance_init(Object *obj)
static void virt_2_8_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
@@ -1512,10 +1547,25 @@ static void virt_2_7_instance_init(Object *obj)
"Valid values are 2, 3 and host", NULL);
}
static void virt_machine_2_7_options(MachineClass *mc)
static void virt_machine_2_8_options(MachineClass *mc)
{
}
DEFINE_VIRT_MACHINE_AS_LATEST(2, 7)
DEFINE_VIRT_MACHINE_AS_LATEST(2, 8)
#define VIRT_COMPAT_2_7 \
HW_COMPAT_2_7
static void virt_2_7_instance_init(Object *obj)
{
virt_2_8_instance_init(obj);
}
static void virt_machine_2_7_options(MachineClass *mc)
{
virt_machine_2_8_options(mc);
SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_7);
}
DEFINE_VIRT_MACHINE(2, 7)
#define VIRT_COMPAT_2_6 \
HW_COMPAT_2_6
+3
View File
@@ -16,4 +16,7 @@ common-obj-$(CONFIG_SOFTMMU) += null-machine.o
common-obj-$(CONFIG_SOFTMMU) += loader.o
common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
common-obj-$(CONFIG_SOFTMMU) += register.o
common-obj-$(CONFIG_SOFTMMU) += or-irq.o
common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o
obj-$(CONFIG_SOFTMMU) += generic-loader.o
+211
View File
@@ -0,0 +1,211 @@
/*
* Generic Loader
*
* Copyright (C) 2014 Li Guang
* Copyright (C) 2016 Xilinx Inc.
* Written by Li Guang <lig.fnst@cn.fujitsu.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
*/
/*
* Internally inside QEMU this is a device. It is a strange device that
* provides no hardware interface but allows QEMU to monkey patch memory
* specified when it is created. To be able to do this it has a reset
* callback that does the memory operations.
* This device allows the user to monkey patch memory. To be able to do
* this it needs a backend to manage the datas, the same as other
* memory-related devices. In this case as the backend is so trivial we
* have merged it with the frontend instead of creating and maintaining a
* seperate backend.
*/
#include "qemu/osdep.h"
#include "qom/cpu.h"
#include "hw/sysbus.h"
#include "sysemu/dma.h"
#include "hw/loader.h"
#include "qapi/error.h"
#include "hw/core/generic-loader.h"
#define CPU_NONE 0xFFFFFFFF
static void generic_loader_reset(void *opaque)
{
GenericLoaderState *s = GENERIC_LOADER(opaque);
if (s->set_pc) {
CPUClass *cc = CPU_GET_CLASS(s->cpu);
cpu_reset(s->cpu);
if (cc) {
cc->set_pc(s->cpu, s->addr);
}
}
if (s->data_len) {
assert(s->data_len < sizeof(s->data));
dma_memory_write(s->cpu->as, s->addr, &s->data, s->data_len);
}
}
static void generic_loader_realize(DeviceState *dev, Error **errp)
{
GenericLoaderState *s = GENERIC_LOADER(dev);
hwaddr entry;
int big_endian;
int size = 0;
s->set_pc = false;
/* Perform some error checking on the user's options */
if (s->data || s->data_len || s->data_be) {
/* User is loading memory values */
if (s->file) {
error_setg(errp, "Specifying a file is not supported when loading "
"memory values");
return;
} else if (s->force_raw) {
error_setg(errp, "Specifying force-raw is not supported when "
"loading memory values");
return;
} else if (!s->data_len) {
/* We cant' check for !data here as a value of 0 is still valid. */
error_setg(errp, "Both data and data-len must be specified");
return;
} else if (s->data_len > 8) {
error_setg(errp, "data-len cannot be greater then 8 bytes");
return;
}
} else if (s->file || s->force_raw) {
/* User is loading an image */
if (s->data || s->data_len || s->data_be) {
error_setg(errp, "data can not be specified when loading an "
"image");
return;
}
s->set_pc = true;
} else if (s->addr) {
/* User is setting the PC */
if (s->data || s->data_len || s->data_be) {
error_setg(errp, "data can not be specified when setting a "
"program counter");
return;
} else if (!s->cpu_num) {
error_setg(errp, "cpu_num must be specified when setting a "
"program counter");
return;
}
s->set_pc = true;
} else {
/* Did the user specify anything? */
error_setg(errp, "please include valid arguments");
return;
}
qemu_register_reset(generic_loader_reset, dev);
if (s->cpu_num != CPU_NONE) {
s->cpu = qemu_get_cpu(s->cpu_num);
if (!s->cpu) {
error_setg(errp, "Specified boot CPU#%d is nonexistent",
s->cpu_num);
return;
}
} else {
s->cpu = first_cpu;
}
#ifdef TARGET_WORDS_BIGENDIAN
big_endian = 1;
#else
big_endian = 0;
#endif
if (s->file) {
if (!s->force_raw) {
size = load_elf_as(s->file, NULL, NULL, &entry, NULL, NULL,
big_endian, 0, 0, 0, s->cpu->as);
if (size < 0) {
size = load_uimage_as(s->file, &entry, NULL, NULL, NULL, NULL,
s->cpu->as);
}
}
if (size < 0 || s->force_raw) {
/* Default to the maximum size being the machine's ram size */
size = load_image_targphys_as(s->file, s->addr, ram_size,
s->cpu->as);
} else {
s->addr = entry;
}
if (size < 0) {
error_setg(errp, "Cannot load specified image %s", s->file);
return;
}
}
/* Convert the data endiannes */
if (s->data_be) {
s->data = cpu_to_be64(s->data);
} else {
s->data = cpu_to_le64(s->data);
}
}
static void generic_loader_unrealize(DeviceState *dev, Error **errp)
{
qemu_unregister_reset(generic_loader_reset, dev);
}
static Property generic_loader_props[] = {
DEFINE_PROP_UINT64("addr", GenericLoaderState, addr, 0),
DEFINE_PROP_UINT64("data", GenericLoaderState, data, 0),
DEFINE_PROP_UINT8("data-len", GenericLoaderState, data_len, 0),
DEFINE_PROP_BOOL("data-be", GenericLoaderState, data_be, false),
DEFINE_PROP_UINT32("cpu-num", GenericLoaderState, cpu_num, CPU_NONE),
DEFINE_PROP_BOOL("force-raw", GenericLoaderState, force_raw, false),
DEFINE_PROP_STRING("file", GenericLoaderState, file),
DEFINE_PROP_END_OF_LIST(),
};
static void generic_loader_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
/* The reset function is not registered here and is instead registered in
* the realize function to allow this device to be added via the device_add
* command in the QEMU monitor.
* TODO: Improve the device_add functionality to allow resets to be
* connected
*/
dc->realize = generic_loader_realize;
dc->unrealize = generic_loader_unrealize;
dc->props = generic_loader_props;
dc->desc = "Generic Loader";
}
static TypeInfo generic_loader_info = {
.name = TYPE_GENERIC_LOADER,
.parent = TYPE_DEVICE,
.instance_size = sizeof(GenericLoaderState),
.class_init = generic_loader_class_init,
};
static void generic_loader_register_type(void)
{
type_register_static(&generic_loader_info);
}
type_init(generic_loader_register_type)
+107
View File
@@ -0,0 +1,107 @@
/*
* QEMU IRQ/GPIO common code.
*
* Copyright (c) 2016 Alistair Francis <alistair@alistair23.me>.
*
* 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 "hw/or-irq.h"
static void or_irq_handler(void *opaque, int n, int level)
{
qemu_or_irq *s = OR_IRQ(opaque);
int or_level = 0;
int i;
s->levels[n] = level;
for (i = 0; i < s->num_lines; i++) {
or_level |= s->levels[i];
}
qemu_set_irq(s->out_irq, or_level);
}
static void or_irq_reset(DeviceState *dev)
{
qemu_or_irq *s = OR_IRQ(dev);
int i;
for (i = 0; i < MAX_OR_LINES; i++) {
s->levels[i] = false;
}
}
static void or_irq_realize(DeviceState *dev, Error **errp)
{
qemu_or_irq *s = OR_IRQ(dev);
assert(s->num_lines < MAX_OR_LINES);
qdev_init_gpio_in(dev, or_irq_handler, s->num_lines);
}
static void or_irq_init(Object *obj)
{
qemu_or_irq *s = OR_IRQ(obj);
qdev_init_gpio_out(DEVICE(obj), &s->out_irq, 1);
}
static const VMStateDescription vmstate_or_irq = {
.name = TYPE_OR_IRQ,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_BOOL_ARRAY(levels, qemu_or_irq, MAX_OR_LINES),
VMSTATE_END_OF_LIST(),
}
};
static Property or_irq_properties[] = {
DEFINE_PROP_UINT16("num-lines", qemu_or_irq, num_lines, 1),
DEFINE_PROP_END_OF_LIST(),
};
static void or_irq_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = or_irq_reset;
dc->props = or_irq_properties;
dc->realize = or_irq_realize;
dc->vmsd = &vmstate_or_irq;
}
static const TypeInfo or_irq_type_info = {
.name = TYPE_OR_IRQ,
.parent = TYPE_DEVICE,
.instance_size = sizeof(qemu_or_irq),
.instance_init = or_irq_init,
.class_init = or_irq_class_init,
};
static void or_irq_register_types(void)
{
type_register_static(&or_irq_type_info);
}
type_init(or_irq_register_types)
+75 -115
View File
@@ -31,30 +31,31 @@ typedef struct {
QEMUTimer *timer;
uint16_t model;
int x, y;
int pressure;
int32_t x, y;
bool pressure;
int state, reg, irq, command;
uint8_t reg, state;
bool irq, command;
uint16_t data, dav;
int busy;
int enabled;
int host_mode;
int function;
int nextfunction;
int precision;
int nextprecision;
int filter;
int pin_func;
int timing[2];
int noise;
int reset;
int pdst;
int pnd0;
bool busy;
bool enabled;
bool host_mode;
int8_t function;
int8_t nextfunction;
bool precision;
bool nextprecision;
uint16_t filter;
uint8_t pin_func;
uint16_t timing[2];
uint8_t noise;
bool reset;
bool pdst;
bool pnd0;
uint16_t temp_thr[2];
uint16_t aux_thr[2];
int tr[8];
int32_t tr[8];
} TSC2005State;
enum {
@@ -149,7 +150,7 @@ static uint16_t tsc2005_read(TSC2005State *s, int reg)
ret = s->dav | (s->reset << 7) | (s->pdst << 2) | 0x0;
s->dav &= ~(mode_regs[TSC_MODE_X_TEST] | mode_regs[TSC_MODE_Y_TEST] |
mode_regs[TSC_MODE_TS_TEST]);
s->reset = 1;
s->reset = true;
return ret;
case 0x8: /* AUX high treshold */
@@ -196,14 +197,14 @@ static void tsc2005_write(TSC2005State *s, int reg, uint16_t data)
break;
case 0xc: /* CFR0 */
s->host_mode = data >> 15;
s->host_mode = (data >> 15) != 0;
if (s->enabled != !(data & 0x4000)) {
s->enabled = !(data & 0x4000);
fprintf(stderr, "%s: touchscreen sense %sabled\n",
__FUNCTION__, s->enabled ? "en" : "dis");
if (s->busy && !s->enabled)
timer_del(s->timer);
s->busy &= s->enabled;
s->busy = s->busy && s->enabled;
}
s->nextprecision = (data >> 13) & 1;
s->timing[0] = data & 0x1fff;
@@ -229,7 +230,7 @@ static void tsc2005_write(TSC2005State *s, int reg, uint16_t data)
static void tsc2005_pin_update(TSC2005State *s)
{
int64_t expires;
int pin_state;
bool pin_state;
switch (s->pin_func) {
case 0:
@@ -253,7 +254,7 @@ static void tsc2005_pin_update(TSC2005State *s)
case TSC_MODE_XYZ_SCAN:
case TSC_MODE_XY_SCAN:
if (!s->host_mode && s->dav)
s->enabled = 0;
s->enabled = false;
if (!s->pressure)
return;
/* Fall through */
@@ -273,7 +274,7 @@ static void tsc2005_pin_update(TSC2005State *s)
case TSC_MODE_Y_TEST:
case TSC_MODE_TS_TEST:
if (s->dav)
s->enabled = 0;
s->enabled = false;
break;
case TSC_MODE_RESERVED:
@@ -287,7 +288,7 @@ static void tsc2005_pin_update(TSC2005State *s)
if (!s->enabled || s->busy)
return;
s->busy = 1;
s->busy = true;
s->precision = s->nextprecision;
s->function = s->nextfunction;
s->pdst = !s->pnd0; /* Synchronised on internal clock */
@@ -300,17 +301,17 @@ static void tsc2005_reset(TSC2005State *s)
{
s->state = 0;
s->pin_func = 0;
s->enabled = 0;
s->busy = 0;
s->nextprecision = 0;
s->enabled = false;
s->busy = false;
s->nextprecision = false;
s->nextfunction = 0;
s->timing[0] = 0;
s->timing[1] = 0;
s->irq = 0;
s->irq = false;
s->dav = 0;
s->reset = 0;
s->pdst = 1;
s->pnd0 = 0;
s->reset = false;
s->pdst = true;
s->pnd0 = false;
s->function = -1;
s->temp_thr[0] = 0x000;
s->temp_thr[1] = 0xfff;
@@ -340,7 +341,7 @@ static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value)
__FUNCTION__, s->enabled ? "en" : "dis");
if (s->busy && !s->enabled)
timer_del(s->timer);
s->busy &= s->enabled;
s->busy = s->busy && s->enabled;
}
tsc2005_pin_update(s);
}
@@ -407,7 +408,7 @@ static void tsc2005_timer_tick(void *opaque)
if (!s->busy)
return;
s->busy = 0;
s->busy = false;
s->dav |= mode_regs[s->function];
s->function = -1;
tsc2005_pin_update(s);
@@ -434,86 +435,9 @@ static void tsc2005_touchscreen_event(void *opaque,
tsc2005_pin_update(s);
}
static void tsc2005_save(QEMUFile *f, void *opaque)
static int tsc2005_post_load(void *opaque, int version_id)
{
TSC2005State *s = (TSC2005State *) opaque;
int i;
qemu_put_be16(f, s->x);
qemu_put_be16(f, s->y);
qemu_put_byte(f, s->pressure);
qemu_put_byte(f, s->state);
qemu_put_byte(f, s->reg);
qemu_put_byte(f, s->command);
qemu_put_byte(f, s->irq);
qemu_put_be16s(f, &s->dav);
qemu_put_be16s(f, &s->data);
timer_put(f, s->timer);
qemu_put_byte(f, s->enabled);
qemu_put_byte(f, s->host_mode);
qemu_put_byte(f, s->function);
qemu_put_byte(f, s->nextfunction);
qemu_put_byte(f, s->precision);
qemu_put_byte(f, s->nextprecision);
qemu_put_be16(f, s->filter);
qemu_put_byte(f, s->pin_func);
qemu_put_be16(f, s->timing[0]);
qemu_put_be16(f, s->timing[1]);
qemu_put_be16s(f, &s->temp_thr[0]);
qemu_put_be16s(f, &s->temp_thr[1]);
qemu_put_be16s(f, &s->aux_thr[0]);
qemu_put_be16s(f, &s->aux_thr[1]);
qemu_put_be32(f, s->noise);
qemu_put_byte(f, s->reset);
qemu_put_byte(f, s->pdst);
qemu_put_byte(f, s->pnd0);
for (i = 0; i < 8; i ++)
qemu_put_be32(f, s->tr[i]);
}
static int tsc2005_load(QEMUFile *f, void *opaque, int version_id)
{
TSC2005State *s = (TSC2005State *) opaque;
int i;
s->x = qemu_get_be16(f);
s->y = qemu_get_be16(f);
s->pressure = qemu_get_byte(f);
s->state = qemu_get_byte(f);
s->reg = qemu_get_byte(f);
s->command = qemu_get_byte(f);
s->irq = qemu_get_byte(f);
qemu_get_be16s(f, &s->dav);
qemu_get_be16s(f, &s->data);
timer_get(f, s->timer);
s->enabled = qemu_get_byte(f);
s->host_mode = qemu_get_byte(f);
s->function = qemu_get_byte(f);
s->nextfunction = qemu_get_byte(f);
s->precision = qemu_get_byte(f);
s->nextprecision = qemu_get_byte(f);
s->filter = qemu_get_be16(f);
s->pin_func = qemu_get_byte(f);
s->timing[0] = qemu_get_be16(f);
s->timing[1] = qemu_get_be16(f);
qemu_get_be16s(f, &s->temp_thr[0]);
qemu_get_be16s(f, &s->temp_thr[1]);
qemu_get_be16s(f, &s->aux_thr[0]);
qemu_get_be16s(f, &s->aux_thr[1]);
s->noise = qemu_get_be32(f);
s->reset = qemu_get_byte(f);
s->pdst = qemu_get_byte(f);
s->pnd0 = qemu_get_byte(f);
for (i = 0; i < 8; i ++)
s->tr[i] = qemu_get_be32(f);
s->busy = timer_pending(s->timer);
tsc2005_pin_update(s);
@@ -521,6 +445,42 @@ static int tsc2005_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
static const VMStateDescription vmstate_tsc2005 = {
.name = "tsc2005",
.version_id = 2,
.minimum_version_id = 2,
.post_load = tsc2005_post_load,
.fields = (VMStateField []) {
VMSTATE_BOOL(pressure, TSC2005State),
VMSTATE_BOOL(irq, TSC2005State),
VMSTATE_BOOL(command, TSC2005State),
VMSTATE_BOOL(enabled, TSC2005State),
VMSTATE_BOOL(host_mode, TSC2005State),
VMSTATE_BOOL(reset, TSC2005State),
VMSTATE_BOOL(pdst, TSC2005State),
VMSTATE_BOOL(pnd0, TSC2005State),
VMSTATE_BOOL(precision, TSC2005State),
VMSTATE_BOOL(nextprecision, TSC2005State),
VMSTATE_UINT8(reg, TSC2005State),
VMSTATE_UINT8(state, TSC2005State),
VMSTATE_UINT16(data, TSC2005State),
VMSTATE_UINT16(dav, TSC2005State),
VMSTATE_UINT16(filter, TSC2005State),
VMSTATE_INT8(nextfunction, TSC2005State),
VMSTATE_INT8(function, TSC2005State),
VMSTATE_INT32(x, TSC2005State),
VMSTATE_INT32(y, TSC2005State),
VMSTATE_TIMER_PTR(timer, TSC2005State),
VMSTATE_UINT8(pin_func, TSC2005State),
VMSTATE_UINT16_ARRAY(timing, TSC2005State, 2),
VMSTATE_UINT8(noise, TSC2005State),
VMSTATE_UINT16_ARRAY(temp_thr, TSC2005State, 2),
VMSTATE_UINT16_ARRAY(aux_thr, TSC2005State, 2),
VMSTATE_INT32_ARRAY(tr, TSC2005State, 8),
VMSTATE_END_OF_LIST()
}
};
void *tsc2005_init(qemu_irq pintdav)
{
TSC2005State *s;
@@ -529,8 +489,8 @@ void *tsc2005_init(qemu_irq pintdav)
g_malloc0(sizeof(TSC2005State));
s->x = 400;
s->y = 240;
s->pressure = 0;
s->precision = s->nextprecision = 0;
s->pressure = false;
s->precision = s->nextprecision = false;
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tsc2005_timer_tick, s);
s->pint = pintdav;
s->model = 0x2005;
@@ -550,7 +510,7 @@ void *tsc2005_init(qemu_irq pintdav)
"QEMU TSC2005-driven Touchscreen");
qemu_register_reset((void *) tsc2005_reset, s);
register_savevm(NULL, "tsc2005", -1, 0, tsc2005_save, tsc2005_load, s);
vmstate_register(NULL, 0, &vmstate_tsc2005, s);
return s;
}
+101 -120
View File
@@ -47,24 +47,25 @@ typedef struct {
uint8_t out_fifo[16384];
uint16_t model;
int x, y;
int pressure;
int32_t x, y;
bool pressure;
int state, page, offset, irq;
uint16_t command, dav;
uint8_t page, offset;
uint16_t dav;
int busy;
int enabled;
int host_mode;
int function;
int nextfunction;
int precision;
int nextprecision;
int filter;
int pin_func;
int ref;
int timing;
int noise;
bool state;
bool irq;
bool command;
bool busy;
bool enabled;
bool host_mode;
uint8_t function, nextfunction;
uint8_t precision, nextprecision;
uint8_t filter;
uint8_t pin_func;
uint8_t ref;
uint8_t timing;
uint8_t noise;
uint16_t audio_ctrl1;
uint16_t audio_ctrl2;
@@ -72,7 +73,7 @@ typedef struct {
uint16_t pll[3];
uint16_t volume;
int64_t volume_change;
int softstep;
bool softstep;
uint16_t dac_power;
int64_t powerdown;
uint16_t filter_data[0x14];
@@ -93,6 +94,7 @@ typedef struct {
int mode;
int intr;
} kb;
int64_t now; /* Time at migration */
} TSC210xState;
static const int resolution[4] = { 12, 8, 10, 12 };
@@ -154,14 +156,14 @@ static const uint16_t mode_regs[16] = {
static void tsc210x_reset(TSC210xState *s)
{
s->state = 0;
s->state = false;
s->pin_func = 2;
s->enabled = 0;
s->busy = 0;
s->enabled = false;
s->busy = false;
s->nextfunction = 0;
s->ref = 0;
s->timing = 0;
s->irq = 0;
s->irq = false;
s->dav = 0;
s->audio_ctrl1 = 0x0000;
@@ -172,7 +174,7 @@ static void tsc210x_reset(TSC210xState *s)
s->pll[2] = 0x1fff;
s->volume = 0xffff;
s->dac_power = 0x8540;
s->softstep = 1;
s->softstep = true;
s->volume_change = 0;
s->powerdown = 0;
s->filter_data[0x00] = 0x6be3;
@@ -566,7 +568,7 @@ static void tsc2102_control_register_write(
s->enabled = !(value & 0x4000);
if (s->busy && !s->enabled)
timer_del(s->timer);
s->busy &= s->enabled;
s->busy = s->busy && s->enabled;
s->nextfunction = (value >> 10) & 0xf;
s->nextprecision = (value >> 8) & 3;
s->filter = value & 0xff;
@@ -773,7 +775,7 @@ static void tsc2102_audio_register_write(
static void tsc210x_pin_update(TSC210xState *s)
{
int64_t expires;
int pin_state;
bool pin_state;
switch (s->pin_func) {
case 0:
@@ -788,7 +790,7 @@ static void tsc210x_pin_update(TSC210xState *s)
}
if (!s->enabled)
pin_state = 0;
pin_state = false;
if (pin_state != s->irq) {
s->irq = pin_state;
@@ -814,7 +816,7 @@ static void tsc210x_pin_update(TSC210xState *s)
case TSC_MODE_TEMP1:
case TSC_MODE_TEMP2:
if (s->dav)
s->enabled = 0;
s->enabled = false;
break;
case TSC_MODE_AUX_SCAN:
@@ -832,7 +834,7 @@ static void tsc210x_pin_update(TSC210xState *s)
if (!s->enabled || s->busy || s->dav)
return;
s->busy = 1;
s->busy = true;
s->precision = s->nextprecision;
s->function = s->nextfunction;
expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
@@ -867,7 +869,7 @@ static uint16_t tsc210x_read(TSC210xState *s)
/* Allow sequential reads. */
s->offset ++;
s->state = 0;
s->state = false;
return ret;
}
@@ -878,10 +880,10 @@ static void tsc210x_write(TSC210xState *s, uint16_t value)
* command and data every second time.
*/
if (!s->state) {
s->command = value >> 15;
s->command = (value >> 15) != 0;
s->page = (value >> 11) & 0x0f;
s->offset = (value >> 5) & 0x3f;
s->state = 1;
s->state = true;
} else {
if (s->command)
fprintf(stderr, "tsc210x_write: SPI overrun!\n");
@@ -901,7 +903,7 @@ static void tsc210x_write(TSC210xState *s, uint16_t value)
}
tsc210x_pin_update(s);
s->state = 0;
s->state = false;
}
}
@@ -933,7 +935,7 @@ static void tsc210x_timer_tick(void *opaque)
if (!s->busy)
return;
s->busy = 0;
s->busy = false;
s->dav |= mode_regs[s->function];
tsc210x_pin_update(s);
qemu_irq_lower(s->davint);
@@ -974,108 +976,34 @@ static void tsc210x_i2s_set_rate(TSC210xState *s, int in, int out)
s->i2s_rx_rate = in;
}
static void tsc210x_save(QEMUFile *f, void *opaque)
static void tsc210x_pre_save(void *opaque)
{
TSC210xState *s = (TSC210xState *) opaque;
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int i;
qemu_put_be16(f, s->x);
qemu_put_be16(f, s->y);
qemu_put_byte(f, s->pressure);
qemu_put_byte(f, s->state);
qemu_put_byte(f, s->page);
qemu_put_byte(f, s->offset);
qemu_put_byte(f, s->command);
qemu_put_byte(f, s->irq);
qemu_put_be16s(f, &s->dav);
timer_put(f, s->timer);
qemu_put_byte(f, s->enabled);
qemu_put_byte(f, s->host_mode);
qemu_put_byte(f, s->function);
qemu_put_byte(f, s->nextfunction);
qemu_put_byte(f, s->precision);
qemu_put_byte(f, s->nextprecision);
qemu_put_byte(f, s->filter);
qemu_put_byte(f, s->pin_func);
qemu_put_byte(f, s->ref);
qemu_put_byte(f, s->timing);
qemu_put_be32(f, s->noise);
qemu_put_be16s(f, &s->audio_ctrl1);
qemu_put_be16s(f, &s->audio_ctrl2);
qemu_put_be16s(f, &s->audio_ctrl3);
qemu_put_be16s(f, &s->pll[0]);
qemu_put_be16s(f, &s->pll[1]);
qemu_put_be16s(f, &s->volume);
qemu_put_sbe64(f, (s->volume_change - now));
qemu_put_sbe64(f, (s->powerdown - now));
qemu_put_byte(f, s->softstep);
qemu_put_be16s(f, &s->dac_power);
for (i = 0; i < 0x14; i ++)
qemu_put_be16s(f, &s->filter_data[i]);
s->now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
}
static int tsc210x_load(QEMUFile *f, void *opaque, int version_id)
static int tsc210x_post_load(void *opaque, int version_id)
{
TSC210xState *s = (TSC210xState *) opaque;
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int i;
s->x = qemu_get_be16(f);
s->y = qemu_get_be16(f);
s->pressure = qemu_get_byte(f);
s->state = qemu_get_byte(f);
s->page = qemu_get_byte(f);
s->offset = qemu_get_byte(f);
s->command = qemu_get_byte(f);
s->irq = qemu_get_byte(f);
qemu_get_be16s(f, &s->dav);
timer_get(f, s->timer);
s->enabled = qemu_get_byte(f);
s->host_mode = qemu_get_byte(f);
s->function = qemu_get_byte(f);
if (s->function < 0 || s->function >= ARRAY_SIZE(mode_regs)) {
if (s->function >= ARRAY_SIZE(mode_regs)) {
return -EINVAL;
}
s->nextfunction = qemu_get_byte(f);
if (s->nextfunction < 0 || s->nextfunction >= ARRAY_SIZE(mode_regs)) {
if (s->nextfunction >= ARRAY_SIZE(mode_regs)) {
return -EINVAL;
}
s->precision = qemu_get_byte(f);
if (s->precision < 0 || s->precision >= ARRAY_SIZE(resolution)) {
if (s->precision >= ARRAY_SIZE(resolution)) {
return -EINVAL;
}
s->nextprecision = qemu_get_byte(f);
if (s->nextprecision < 0 || s->nextprecision >= ARRAY_SIZE(resolution)) {
if (s->nextprecision >= ARRAY_SIZE(resolution)) {
return -EINVAL;
}
s->filter = qemu_get_byte(f);
s->pin_func = qemu_get_byte(f);
s->ref = qemu_get_byte(f);
s->timing = qemu_get_byte(f);
s->noise = qemu_get_be32(f);
qemu_get_be16s(f, &s->audio_ctrl1);
qemu_get_be16s(f, &s->audio_ctrl2);
qemu_get_be16s(f, &s->audio_ctrl3);
qemu_get_be16s(f, &s->pll[0]);
qemu_get_be16s(f, &s->pll[1]);
qemu_get_be16s(f, &s->volume);
s->volume_change = qemu_get_sbe64(f) + now;
s->powerdown = qemu_get_sbe64(f) + now;
s->softstep = qemu_get_byte(f);
qemu_get_be16s(f, &s->dac_power);
for (i = 0; i < 0x14; i ++)
qemu_get_be16s(f, &s->filter_data[i]);
s->volume_change -= s->now;
s->volume_change += now;
s->powerdown -= s->now;
s->powerdown += now;
s->busy = timer_pending(s->timer);
qemu_set_irq(s->pint, !s->irq);
@@ -1084,6 +1012,60 @@ static int tsc210x_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
static VMStateField vmstatefields_tsc210x[] = {
VMSTATE_BOOL(enabled, TSC210xState),
VMSTATE_BOOL(host_mode, TSC210xState),
VMSTATE_BOOL(irq, TSC210xState),
VMSTATE_BOOL(command, TSC210xState),
VMSTATE_BOOL(pressure, TSC210xState),
VMSTATE_BOOL(softstep, TSC210xState),
VMSTATE_BOOL(state, TSC210xState),
VMSTATE_UINT16(dav, TSC210xState),
VMSTATE_INT32(x, TSC210xState),
VMSTATE_INT32(y, TSC210xState),
VMSTATE_UINT8(offset, TSC210xState),
VMSTATE_UINT8(page, TSC210xState),
VMSTATE_UINT8(filter, TSC210xState),
VMSTATE_UINT8(pin_func, TSC210xState),
VMSTATE_UINT8(ref, TSC210xState),
VMSTATE_UINT8(timing, TSC210xState),
VMSTATE_UINT8(noise, TSC210xState),
VMSTATE_UINT8(function, TSC210xState),
VMSTATE_UINT8(nextfunction, TSC210xState),
VMSTATE_UINT8(precision, TSC210xState),
VMSTATE_UINT8(nextprecision, TSC210xState),
VMSTATE_UINT16(audio_ctrl1, TSC210xState),
VMSTATE_UINT16(audio_ctrl2, TSC210xState),
VMSTATE_UINT16(audio_ctrl3, TSC210xState),
VMSTATE_UINT16_ARRAY(pll, TSC210xState, 3),
VMSTATE_UINT16(volume, TSC210xState),
VMSTATE_UINT16(dac_power, TSC210xState),
VMSTATE_INT64(volume_change, TSC210xState),
VMSTATE_INT64(powerdown, TSC210xState),
VMSTATE_INT64(now, TSC210xState),
VMSTATE_UINT16_ARRAY(filter_data, TSC210xState, 0x14),
VMSTATE_TIMER_PTR(timer, TSC210xState),
VMSTATE_END_OF_LIST()
};
static const VMStateDescription vmstate_tsc2102 = {
.name = "tsc2102",
.version_id = 1,
.minimum_version_id = 1,
.pre_save = tsc210x_pre_save,
.post_load = tsc210x_post_load,
.fields = vmstatefields_tsc210x,
};
static const VMStateDescription vmstate_tsc2301 = {
.name = "tsc2301",
.version_id = 1,
.minimum_version_id = 1,
.pre_save = tsc210x_pre_save,
.post_load = tsc210x_post_load,
.fields = vmstatefields_tsc210x,
};
uWireSlave *tsc2102_init(qemu_irq pint)
{
TSC210xState *s;
@@ -1125,8 +1107,7 @@ uWireSlave *tsc2102_init(qemu_irq pint)
AUD_register_card(s->name, &s->card);
qemu_register_reset((void *) tsc210x_reset, s);
register_savevm(NULL, s->name, -1, 0,
tsc210x_save, tsc210x_load, s);
vmstate_register(NULL, 0, &vmstate_tsc2102, s);
return &s->chip;
}
@@ -1174,7 +1155,7 @@ uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav)
AUD_register_card(s->name, &s->card);
qemu_register_reset((void *) tsc210x_reset, s);
register_savevm(NULL, s->name, -1, 0, tsc210x_save, tsc210x_load, s);
vmstate_register(NULL, 0, &vmstate_tsc2301, s);
return &s->chip;
}
+2
View File
@@ -16,11 +16,13 @@ common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gicv3.o
common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_dist.o
common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_redist.o
common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_its_common.o
common-obj-$(CONFIG_OPENPIC) += openpic.o
obj-$(CONFIG_APIC) += apic.o apic_common.o
obj-$(CONFIG_ARM_GIC_KVM) += arm_gic_kvm.o
obj-$(call land,$(CONFIG_ARM_GIC_KVM),$(TARGET_AARCH64)) += arm_gicv3_kvm.o
obj-$(call land,$(CONFIG_ARM_GIC_KVM),$(TARGET_AARCH64)) += arm_gicv3_its_kvm.o
obj-$(CONFIG_STELLARIS) += armv7m_nvic.o
obj-$(CONFIG_EXYNOS4) += exynos4210_gic.o exynos4210_combiner.o
obj-$(CONFIG_GRLIB) += grlib_irqmp.o
+12
View File
@@ -577,6 +577,18 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
"not support vGICv2 migration");
migrate_add_blocker(s->migration_blocker);
}
if (kvm_has_gsi_routing()) {
/* set up irq routing */
kvm_init_irq_routing(kvm_state);
for (i = 0; i < s->num_irq - GIC_INTERNAL; ++i) {
kvm_irqchip_add_irq_route(kvm_state, i, 0, i);
}
kvm_gsi_routing_allowed = true;
kvm_irqchip_commit_routes(kvm_state);
}
}
static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
+148
View File
@@ -0,0 +1,148 @@
/*
* ITS base class for a GICv3-based system
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* Written by Pavel Fedin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "hw/pci/msi.h"
#include "hw/intc/arm_gicv3_its_common.h"
#include "qemu/log.h"
static void gicv3_its_pre_save(void *opaque)
{
GICv3ITSState *s = (GICv3ITSState *)opaque;
GICv3ITSCommonClass *c = ARM_GICV3_ITS_COMMON_GET_CLASS(s);
if (c->pre_save) {
c->pre_save(s);
}
}
static int gicv3_its_post_load(void *opaque, int version_id)
{
GICv3ITSState *s = (GICv3ITSState *)opaque;
GICv3ITSCommonClass *c = ARM_GICV3_ITS_COMMON_GET_CLASS(s);
if (c->post_load) {
c->post_load(s);
}
return 0;
}
static const VMStateDescription vmstate_its = {
.name = "arm_gicv3_its",
.pre_save = gicv3_its_pre_save,
.post_load = gicv3_its_post_load,
.unmigratable = true,
};
static MemTxResult gicv3_its_trans_read(void *opaque, hwaddr offset,
uint64_t *data, unsigned size,
MemTxAttrs attrs)
{
qemu_log_mask(LOG_GUEST_ERROR, "ITS read at offset 0x%"PRIx64"\n", offset);
return MEMTX_ERROR;
}
static MemTxResult gicv3_its_trans_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size,
MemTxAttrs attrs)
{
if (offset == 0x0040 && ((size == 2) || (size == 4))) {
GICv3ITSState *s = ARM_GICV3_ITS_COMMON(opaque);
GICv3ITSCommonClass *c = ARM_GICV3_ITS_COMMON_GET_CLASS(s);
int ret = c->send_msi(s, le64_to_cpu(value), attrs.requester_id);
if (ret <= 0) {
qemu_log_mask(LOG_GUEST_ERROR,
"ITS: Error sending MSI: %s\n", strerror(-ret));
return MEMTX_DECODE_ERROR;
}
return MEMTX_OK;
} else {
qemu_log_mask(LOG_GUEST_ERROR,
"ITS write at bad offset 0x%"PRIx64"\n", offset);
return MEMTX_DECODE_ERROR;
}
}
static const MemoryRegionOps gicv3_its_trans_ops = {
.read_with_attrs = gicv3_its_trans_read,
.write_with_attrs = gicv3_its_trans_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(s);
memory_region_init_io(&s->iomem_its_cntrl, OBJECT(s), ops, s,
"control", ITS_CONTROL_SIZE);
memory_region_init_io(&s->iomem_its_translation, OBJECT(s),
&gicv3_its_trans_ops, s,
"translation", ITS_TRANS_SIZE);
/* Our two regions are always adjacent, therefore we now combine them
* into a single one in order to make our users' life easier.
*/
memory_region_init(&s->iomem_main, OBJECT(s), "gicv3_its", ITS_SIZE);
memory_region_add_subregion(&s->iomem_main, 0, &s->iomem_its_cntrl);
memory_region_add_subregion(&s->iomem_main, ITS_CONTROL_SIZE,
&s->iomem_its_translation);
sysbus_init_mmio(sbd, &s->iomem_main);
msi_nonbroken = true;
}
static void gicv3_its_common_reset(DeviceState *dev)
{
GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
s->ctlr = 0;
s->cbaser = 0;
s->cwriter = 0;
s->creadr = 0;
memset(&s->baser, 0, sizeof(s->baser));
gicv3_its_post_load(s, 0);
}
static void gicv3_its_common_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = gicv3_its_common_reset;
dc->vmsd = &vmstate_its;
}
static const TypeInfo gicv3_its_common_info = {
.name = TYPE_ARM_GICV3_ITS_COMMON,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(GICv3ITSState),
.class_size = sizeof(GICv3ITSCommonClass),
.class_init = gicv3_its_common_class_init,
.abstract = true,
};
static void gicv3_its_common_register_types(void)
{
type_register_static(&gicv3_its_common_info);
}
type_init(gicv3_its_common_register_types)
+121
View File
@@ -0,0 +1,121 @@
/*
* KVM-based ITS implementation for a GICv3-based system
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* Written by Pavel Fedin <p.fedin@samsung.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/intc/arm_gicv3_its_common.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
#include "migration/migration.h"
#define TYPE_KVM_ARM_ITS "arm-its-kvm"
#define KVM_ARM_ITS(obj) OBJECT_CHECK(GICv3ITSState, (obj), TYPE_KVM_ARM_ITS)
static int kvm_its_send_msi(GICv3ITSState *s, uint32_t value, uint16_t devid)
{
struct kvm_msi msi;
if (unlikely(!s->translater_gpa_known)) {
MemoryRegion *mr = &s->iomem_its_translation;
MemoryRegionSection mrs;
mrs = memory_region_find(mr, 0, 1);
memory_region_unref(mrs.mr);
s->gits_translater_gpa = mrs.offset_within_address_space + 0x40;
s->translater_gpa_known = true;
}
msi.address_lo = extract64(s->gits_translater_gpa, 0, 32);
msi.address_hi = extract64(s->gits_translater_gpa, 32, 32);
msi.data = le32_to_cpu(value);
msi.flags = KVM_MSI_VALID_DEVID;
msi.devid = devid;
memset(msi.pad, 0, sizeof(msi.pad));
return kvm_vm_ioctl(kvm_state, KVM_SIGNAL_MSI, &msi);
}
static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
{
GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
s->dev_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_ITS, false);
if (s->dev_fd < 0) {
error_setg_errno(errp, -s->dev_fd, "error creating in-kernel ITS");
return;
}
/* explicit init of the ITS */
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
/* register the base address */
kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd);
gicv3_its_init_mmio(s, NULL);
/*
* Block migration of a KVM GICv3 ITS device: the API for saving and
* restoring the state in the kernel is not yet available
*/
error_setg(&s->migration_blocker, "vITS migration is not implemented");
migrate_add_blocker(s->migration_blocker);
kvm_msi_use_devid = true;
kvm_gsi_direct_mapping = false;
kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
}
static void kvm_arm_its_init(Object *obj)
{
GICv3ITSState *s = KVM_ARM_ITS(obj);
object_property_add_link(obj, "parent-gicv3",
"kvm-arm-gicv3", (Object **)&s->gicv3,
object_property_allow_set_link,
OBJ_PROP_LINK_UNREF_ON_RELEASE,
&error_abort);
}
static void kvm_arm_its_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
GICv3ITSCommonClass *icc = ARM_GICV3_ITS_COMMON_CLASS(klass);
dc->realize = kvm_arm_its_realize;
icc->send_msi = kvm_its_send_msi;
}
static const TypeInfo kvm_arm_its_info = {
.name = TYPE_KVM_ARM_ITS,
.parent = TYPE_ARM_GICV3_ITS_COMMON,
.instance_size = sizeof(GICv3ITSState),
.instance_init = kvm_arm_its_init,
.class_init = kvm_arm_its_class_init,
};
static void kvm_arm_its_register_types(void)
{
type_register_static(&kvm_arm_its_info);
}
type_init(kvm_arm_its_register_types)

Some files were not shown because too many files have changed in this diff Show More