summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/acpi/aml-build.c127
-rw-r--r--hw/arm/fsl-imx25.c6
-rw-r--r--hw/arm/fsl-imx31.c6
-rw-r--r--hw/arm/virt-acpi-build.c77
-rw-r--r--hw/arm/virt.c60
-rw-r--r--hw/display/omap_lcd_template.h4
-rw-r--r--hw/display/pxa2xx_lcd.c8
-rw-r--r--hw/dma/soc_dma.c39
-rw-r--r--hw/gpio/imx_gpio.c12
-rw-r--r--hw/i386/acpi-build.c41
-rw-r--r--hw/misc/Makefile.objs2
-rw-r--r--hw/misc/imx25_ccm.c341
-rw-r--r--hw/misc/imx31_ccm.c392
-rw-r--r--hw/misc/imx_ccm.c231
-rw-r--r--hw/timer/imx_epit.c20
-rw-r--r--hw/timer/imx_gpt.c16
-rw-r--r--include/hw/acpi/aml-build.h37
-rw-r--r--include/hw/arm/fsl-imx25.h4
-rw-r--r--include/hw/arm/fsl-imx31.h4
-rw-r--r--include/hw/arm/virt.h1
-rw-r--r--include/hw/gpio/imx_gpio.h3
-rw-r--r--include/hw/misc/imx25_ccm.h79
-rw-r--r--include/hw/misc/imx31_ccm.h66
-rw-r--r--include/hw/misc/imx_ccm.h75
-rw-r--r--include/hw/timer/imx_epit.h5
-rw-r--r--include/hw/timer/imx_gpt.h5
-rw-r--r--target-arm/cpu.c1
-rw-r--r--target-arm/helper-a64.c12
-rw-r--r--target-arm/helper.c8
-rw-r--r--target-arm/internals.h7
-rw-r--r--target-arm/kvm.c65
-rw-r--r--target-arm/kvm32.c47
-rw-r--r--target-arm/kvm64.c464
-rw-r--r--target-arm/kvm_arm.h30
-rw-r--r--target-arm/op_helper.c40
-rw-r--r--target-arm/translate.c76
-rw-r--r--tests/guest-debug/test-gdbstub.py176
37 files changed, 2141 insertions, 446 deletions
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index a00a0abe83..302cf01e5c 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -565,6 +565,94 @@ Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4)
}
/*
+ * ACPI 5.0: 6.4.3.8.1 GPIO Connection Descriptor
+ * Type 1, Large Item Name 0xC
+ */
+
+static Aml *aml_gpio_connection(AmlGpioConnectionType type,
+ AmlConsumerAndProducer con_and_pro,
+ uint8_t flags, AmlPinConfig pin_config,
+ uint16_t output_drive,
+ uint16_t debounce_timeout,
+ const uint32_t pin_list[], uint32_t pin_count,
+ const char *resource_source_name,
+ const uint8_t *vendor_data,
+ uint16_t vendor_data_len)
+{
+ Aml *var = aml_alloc();
+ const uint16_t min_desc_len = 0x16;
+ uint16_t resource_source_name_len, length;
+ uint16_t pin_table_offset, resource_source_name_offset, vendor_data_offset;
+ uint32_t i;
+
+ assert(resource_source_name);
+ resource_source_name_len = strlen(resource_source_name) + 1;
+ length = min_desc_len + resource_source_name_len + vendor_data_len;
+ pin_table_offset = min_desc_len + 1;
+ resource_source_name_offset = pin_table_offset + pin_count * 2;
+ vendor_data_offset = resource_source_name_offset + resource_source_name_len;
+
+ build_append_byte(var->buf, 0x8C); /* GPIO Connection Descriptor */
+ build_append_int_noprefix(var->buf, length, 2); /* Length */
+ build_append_byte(var->buf, 1); /* Revision ID */
+ build_append_byte(var->buf, type); /* GPIO Connection Type */
+ /* General Flags (2 bytes) */
+ build_append_int_noprefix(var->buf, con_and_pro, 2);
+ /* Interrupt and IO Flags (2 bytes) */
+ build_append_int_noprefix(var->buf, flags, 2);
+ /* Pin Configuration 0 = Default 1 = Pull-up 2 = Pull-down 3 = No Pull */
+ build_append_byte(var->buf, pin_config);
+ /* Output Drive Strength (2 bytes) */
+ build_append_int_noprefix(var->buf, output_drive, 2);
+ /* Debounce Timeout (2 bytes) */
+ build_append_int_noprefix(var->buf, debounce_timeout, 2);
+ /* Pin Table Offset (2 bytes) */
+ build_append_int_noprefix(var->buf, pin_table_offset, 2);
+ build_append_byte(var->buf, 0); /* Resource Source Index */
+ /* Resource Source Name Offset (2 bytes) */
+ build_append_int_noprefix(var->buf, resource_source_name_offset, 2);
+ /* Vendor Data Offset (2 bytes) */
+ build_append_int_noprefix(var->buf, vendor_data_offset, 2);
+ /* Vendor Data Length (2 bytes) */
+ build_append_int_noprefix(var->buf, vendor_data_len, 2);
+ /* Pin Number (2n bytes)*/
+ for (i = 0; i < pin_count; i++) {
+ build_append_int_noprefix(var->buf, pin_list[i], 2);
+ }
+
+ /* Resource Source Name */
+ build_append_namestring(var->buf, "%s", resource_source_name);
+ build_append_byte(var->buf, '\0');
+
+ /* Vendor-defined Data */
+ if (vendor_data != NULL) {
+ g_array_append_vals(var->buf, vendor_data, vendor_data_len);
+ }
+
+ return var;
+}
+
+/*
+ * ACPI 5.0: 19.5.53
+ * GpioInt(GPIO Interrupt Connection Resource Descriptor Macro)
+ */
+Aml *aml_gpio_int(AmlConsumerAndProducer con_and_pro,
+ AmlLevelAndEdge edge_level,
+ AmlActiveHighAndLow active_level, AmlShared shared,
+ AmlPinConfig pin_config, uint16_t debounce_timeout,
+ const uint32_t pin_list[], uint32_t pin_count,
+ const char *resource_source_name,
+ const uint8_t *vendor_data, uint16_t vendor_data_len)
+{
+ uint8_t flags = edge_level | (active_level << 1) | (shared << 3);
+
+ return aml_gpio_connection(AML_INTERRUPT_CONNECTION, con_and_pro, flags,
+ pin_config, 0, debounce_timeout, pin_list,
+ pin_count, resource_source_name, vendor_data,
+ vendor_data_len);
+}
+
+/*
* ACPI 1.0b: 6.4.3.4 32-Bit Fixed Location Memory Range Descriptor
* (Type 1, Large Item Name 0x6)
*/
@@ -598,23 +686,27 @@ Aml *aml_memory32_fixed(uint32_t addr, uint32_t size,
Aml *aml_interrupt(AmlConsumerAndProducer con_and_pro,
AmlLevelAndEdge level_and_edge,
AmlActiveHighAndLow high_and_low, AmlShared shared,
- uint32_t irq)
+ uint32_t *irq_list, uint8_t irq_count)
{
+ int i;
Aml *var = aml_alloc();
uint8_t irq_flags = con_and_pro | (level_and_edge << 1)
| (high_and_low << 2) | (shared << 3);
+ const int header_bytes_in_len = 2;
+ uint16_t len = header_bytes_in_len + irq_count * sizeof(uint32_t);
+
+ assert(irq_count > 0);
build_append_byte(var->buf, 0x89); /* Extended irq descriptor */
- build_append_byte(var->buf, 6); /* Length, bits[7:0] minimum value = 6 */
- build_append_byte(var->buf, 0); /* Length, bits[15:8] minimum value = 0 */
+ build_append_byte(var->buf, len & 0xFF); /* Length, bits[7:0] */
+ build_append_byte(var->buf, len >> 8); /* Length, bits[15:8] */
build_append_byte(var->buf, irq_flags); /* Interrupt Vector Information. */
- build_append_byte(var->buf, 0x01); /* Interrupt table length = 1 */
+ build_append_byte(var->buf, irq_count); /* Interrupt table length */
- /* Interrupt Number */
- build_append_byte(var->buf, extract32(irq, 0, 8)); /* bits[7:0] */
- build_append_byte(var->buf, extract32(irq, 8, 8)); /* bits[15:8] */
- build_append_byte(var->buf, extract32(irq, 16, 8)); /* bits[23:16] */
- build_append_byte(var->buf, extract32(irq, 24, 8)); /* bits[31:24] */
+ /* Interrupt Number List */
+ for (i = 0; i < irq_count; i++) {
+ build_append_int_noprefix(var->buf, irq_list[i], 4);
+ }
return var;
}
@@ -696,11 +788,24 @@ Aml *aml_while(Aml *predicate)
}
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMethod */
-Aml *aml_method(const char *name, int arg_count)
+Aml *aml_method(const char *name, int arg_count, AmlSerializeFlag sflag)
{
Aml *var = aml_bundle(0x14 /* MethodOp */, AML_PACKAGE);
+ int methodflags;
+
+ /*
+ * MethodFlags:
+ * bit 0-2: ArgCount (0-7)
+ * bit 3: SerializeFlag
+ * 0: NotSerialized
+ * 1: Serialized
+ * bit 4-7: reserved (must be 0)
+ */
+ assert(arg_count < 8);
+ methodflags = arg_count | (sflag << 3);
+
build_append_namestring(var->buf, "%s", name);
- build_append_byte(var->buf, arg_count); /* MethodFlags: ArgCount */
+ build_append_byte(var->buf, methodflags); /* MethodFlags: ArgCount */
return var;
}
diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index e1cadac997..36818ee025 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -38,7 +38,7 @@ static void fsl_imx25_init(Object *obj)
object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
- object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM);
+ object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX25_CCM);
qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) {
@@ -150,7 +150,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
{ FSL_IMX25_GPT4_ADDR, FSL_IMX25_GPT4_IRQ }
};
- s->gpt[i].ccm = DEVICE(&s->ccm);
+ s->gpt[i].ccm = IMX_CCM(&s->ccm);
object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized", &err);
if (err) {
@@ -173,7 +173,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
{ FSL_IMX25_EPIT2_ADDR, FSL_IMX25_EPIT2_IRQ }
};
- s->epit[i].ccm = DEVICE(&s->ccm);
+ s->epit[i].ccm = IMX_CCM(&s->ccm);
object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
if (err) {
diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c
index 53d4473250..abdea0683a 100644
--- a/hw/arm/fsl-imx31.c
+++ b/hw/arm/fsl-imx31.c
@@ -35,7 +35,7 @@ static void fsl_imx31_init(Object *obj)
object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
- object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM);
+ object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM);
qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) {
@@ -128,7 +128,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
serial_table[i].irq));
}
- s->gpt.ccm = DEVICE(&s->ccm);
+ s->gpt.ccm = IMX_CCM(&s->ccm);
object_property_set_bool(OBJECT(&s->gpt), true, "realized", &err);
if (err) {
@@ -150,7 +150,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
{ FSL_IMX31_EPIT2_ADDR, FSL_IMX31_EPIT2_IRQ },
};
- s->epit[i].ccm = DEVICE(&s->ccm);
+ s->epit[i].ccm = IMX_CCM(&s->ccm);
object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
if (err) {
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 8fd0b1c57b..32ec19e884 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -43,6 +43,7 @@
#include "hw/pci/pci.h"
#define ARM_SPI_BASE 32
+#define ACPI_POWER_BUTTON_DEVICE "PWRB"
typedef struct VirtAcpiCpuInfo {
DECLARE_BITMAP(found_cpus, VIRT_ACPI_CPU_ID_LIMIT);
@@ -71,7 +72,7 @@ static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus)
}
static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
- int uart_irq)
+ uint32_t uart_irq)
{
Aml *dev = aml_device("COM0");
aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0011")));
@@ -82,7 +83,7 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
uart_memmap->size, AML_READ_WRITE));
aml_append(crs,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
- AML_EXCLUSIVE, uart_irq));
+ AML_EXCLUSIVE, &uart_irq, 1));
aml_append(dev, aml_name_decl("_CRS", crs));
/* The _ADR entry is used to link this device to the UART described
@@ -94,7 +95,7 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
}
static void acpi_dsdt_add_rtc(Aml *scope, const MemMapEntry *rtc_memmap,
- int rtc_irq)
+ uint32_t rtc_irq)
{
Aml *dev = aml_device("RTC0");
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0013")));
@@ -105,7 +106,7 @@ static void acpi_dsdt_add_rtc(Aml *scope, const MemMapEntry *rtc_memmap,
rtc_memmap->size, AML_READ_WRITE));
aml_append(crs,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
- AML_EXCLUSIVE, rtc_irq));
+ AML_EXCLUSIVE, &rtc_irq, 1));
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
}
@@ -136,14 +137,14 @@ static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
static void acpi_dsdt_add_virtio(Aml *scope,
const MemMapEntry *virtio_mmio_memmap,
- int mmio_irq, int num)
+ uint32_t mmio_irq, int num)
{
hwaddr base = virtio_mmio_memmap->base;
hwaddr size = virtio_mmio_memmap->size;
- int irq = mmio_irq;
int i;
for (i = 0; i < num; i++) {
+ uint32_t irq = mmio_irq + i;
Aml *dev = aml_device("VR%02u", i);
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005")));
aml_append(dev, aml_name_decl("_UID", aml_int(i)));
@@ -152,15 +153,15 @@ static void acpi_dsdt_add_virtio(Aml *scope,
aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE));
aml_append(crs,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
- AML_EXCLUSIVE, irq + i));
+ AML_EXCLUSIVE, &irq, 1));
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
base += size;
}
}
-static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
- bool use_highmem)
+static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
+ uint32_t irq, bool use_highmem)
{
Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf;
int i, bus_no;
@@ -199,29 +200,30 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
/* Create GSI link device */
for (i = 0; i < PCI_NUM_PINS; i++) {
+ uint32_t irqs = irq + i;
Aml *dev_gsi = aml_device("GSI%d", i);
aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F")));
aml_append(dev_gsi, aml_name_decl("_UID", aml_int(0)));
crs = aml_resource_template();
aml_append(crs,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
- AML_EXCLUSIVE, irq + i));
+ AML_EXCLUSIVE, &irqs, 1));
aml_append(dev_gsi, aml_name_decl("_PRS", crs));
crs = aml_resource_template();
aml_append(crs,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
- AML_EXCLUSIVE, irq + i));
+ AML_EXCLUSIVE, &irqs, 1));
aml_append(dev_gsi, aml_name_decl("_CRS", crs));
- method = aml_method("_SRS", 1);
+ method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
aml_append(dev_gsi, method);
aml_append(dev, dev_gsi);
}
- method = aml_method("_CBA", 0);
+ method = aml_method("_CBA", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_int(base_ecam)));
aml_append(dev, method);
- method = aml_method("_CRS", 0);
+ method = aml_method("_CRS", 0, AML_NOTSERIALIZED);
Aml *rbuf = aml_resource_template();
aml_append(rbuf,
aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
@@ -254,7 +256,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
/* Declare an _OSC (OS Control Handoff) method */
aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
- method = aml_method("_OSC", 4);
+ method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
aml_append(method,
aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
@@ -296,7 +298,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
aml_append(method, elsectx);
aml_append(dev, method);
- method = aml_method("_DSM", 4);
+ method = aml_method("_DSM", 4, AML_NOTSERIALIZED);
/* PCI Firmware Specification 3.0
* 4.6.1. _DSM for PCI Express Slot Information
@@ -323,6 +325,46 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
aml_append(scope, dev);
}
+static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap,
+ uint32_t gpio_irq)
+{
+ Aml *dev = aml_device("GPO0");
+ aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0061")));
+ aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
+ aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+
+ Aml *crs = aml_resource_template();
+ aml_append(crs, aml_memory32_fixed(gpio_memmap->base, gpio_memmap->size,
+ AML_READ_WRITE));
+ aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, &gpio_irq, 1));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ Aml *aei = aml_resource_template();
+ /* Pin 3 for power button */
+ const uint32_t pin_list[1] = {3};
+ aml_append(aei, aml_gpio_int(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, AML_PULL_UP, 0, pin_list, 1,
+ "GPO0", NULL, 0));
+ aml_append(dev, aml_name_decl("_AEI", aei));
+
+ /* _E03 is handle for power button */
+ Aml *method = aml_method("_E03", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
+ aml_int(0x80)));
+ aml_append(dev, method);
+ aml_append(scope, dev);
+}
+
+static void acpi_dsdt_add_power_button(Aml *scope)
+{
+ Aml *dev = aml_device(ACPI_POWER_BUTTON_DEVICE);
+ aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0C")));
+ aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
+ aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+ aml_append(scope, dev);
+}
+
/* RSDP */
static GArray *
build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
@@ -539,6 +581,9 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
guest_info->use_highmem);
+ acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
+ (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
+ acpi_dsdt_add_power_button(scope);
aml_append(dsdt, scope);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 9c6792cea1..acc1fcbdd1 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -52,6 +52,7 @@
#include "kvm_arm.h"
#include "hw/smbios/smbios.h"
#include "qapi/visitor.h"
+#include "standard-headers/linux/input.h"
/* Number of external interrupt lines to configure the GIC with */
#define NUM_IRQS 256
@@ -120,6 +121,7 @@ static const MemMapEntry a15memmap[] = {
[VIRT_UART] = { 0x09000000, 0x00001000 },
[VIRT_RTC] = { 0x09010000, 0x00001000 },
[VIRT_FW_CFG] = { 0x09020000, 0x00000018 },
+ [VIRT_GPIO] = { 0x09030000, 0x00001000 },
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
@@ -135,6 +137,7 @@ static const int a15irqmap[] = {
[VIRT_UART] = 1,
[VIRT_RTC] = 2,
[VIRT_PCIE] = 3, /* ... to 6 */
+ [VIRT_GPIO] = 7,
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
[VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
[VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
@@ -538,6 +541,61 @@ static void create_rtc(const VirtBoardInfo *vbi, qemu_irq *pic)
g_free(nodename);
}
+static DeviceState *pl061_dev;
+static void virt_powerdown_req(Notifier *n, void *opaque)
+{
+ /* use gpio Pin 3 for power button event */
+ qemu_set_irq(qdev_get_gpio_in(pl061_dev, 3), 1);
+}
+
+static Notifier virt_system_powerdown_notifier = {
+ .notify = virt_powerdown_req
+};
+
+static void create_gpio(const VirtBoardInfo *vbi, qemu_irq *pic)
+{
+ char *nodename;
+ hwaddr base = vbi->memmap[VIRT_GPIO].base;
+ hwaddr size = vbi->memmap[VIRT_GPIO].size;
+ int irq = vbi->irqmap[VIRT_GPIO];
+ const char compat[] = "arm,pl061\0arm,primecell";
+
+ pl061_dev = sysbus_create_simple("pl061", base, pic[irq]);
+
+ uint32_t phandle = qemu_fdt_alloc_phandle(vbi->fdt);
+ nodename = g_strdup_printf("/pl061@%" PRIx64, base);
+ qemu_fdt_add_subnode(vbi->fdt, nodename);
+ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
+ 2, base, 2, size);
+ qemu_fdt_setprop(vbi->fdt, nodename, "compatible", compat, sizeof(compat));
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "#gpio-cells", 2);
+ qemu_fdt_setprop(vbi->fdt, nodename, "gpio-controller", NULL, 0);
+ qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts",
+ GIC_FDT_IRQ_TYPE_SPI, irq,
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "clocks", vbi->clock_phandle);
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "clock-names", "apb_pclk");
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "phandle", phandle);
+
+ qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys");
+ qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys", "compatible", "gpio-keys");
+ qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#size-cells", 0);
+ qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#address-cells", 1);
+
+ qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys/poweroff");
+ qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys/poweroff",
+ "label", "GPIO Key Poweroff");
+ qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys/poweroff", "linux,code",
+ KEY_POWER);
+ qemu_fdt_setprop_cells(vbi->fdt, "/gpio-keys/poweroff",
+ "gpios", phandle, 3, 0);
+
+ /* connect powerdown request */
+ qemu_register_powerdown_notifier(&virt_system_powerdown_notifier);
+
+ g_free(nodename);
+}
+
static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic)
{
int i;
@@ -1041,6 +1099,8 @@ static void machvirt_init(MachineState *machine)
create_pcie(vbi, pic, vms->highmem);
+ create_gpio(vbi, pic);
+
/* Create mmio transports, so the user can create virtio backends
* (which will be automatically plugged in to the transports). If
* no backend is created the transport will just sit harmlessly idle.
diff --git a/hw/display/omap_lcd_template.h b/hw/display/omap_lcd_template.h
index e5dd447167..f0ce71fd66 100644
--- a/hw/display/omap_lcd_template.h
+++ b/hw/display/omap_lcd_template.h
@@ -136,7 +136,7 @@ static void glue(draw_line12_, DEPTH)(void *opaque,
uint8_t r, g, b;
do {
- v = lduw_p((void *) s);
+ v = lduw_le_p((void *) s);
r = (v >> 4) & 0xf0;
g = v & 0xf0;
b = (v << 4) & 0xf0;
@@ -159,7 +159,7 @@ static void glue(draw_line16_, DEPTH)(void *opaque,
uint8_t r, g, b;
do {
- v = lduw_p((void *) s);
+ v = lduw_le_p((void *) s);
r = (v >> 8) & 0xf8;
g = (v >> 3) & 0xfc;
b = (v << 3) & 0xf8;
diff --git a/hw/display/pxa2xx_lcd.c b/hw/display/pxa2xx_lcd.c
index 494700d07a..4d36c94707 100644
--- a/hw/display/pxa2xx_lcd.c
+++ b/hw/display/pxa2xx_lcd.c
@@ -309,10 +309,10 @@ static void pxa2xx_descriptor_load(PXA2xxLCDState *s)
}
cpu_physical_memory_read(descptr, &desc, sizeof(desc));
- s->dma_ch[i].descriptor = tswap32(desc.fdaddr);
- s->dma_ch[i].source = tswap32(desc.fsaddr);
- s->dma_ch[i].id = tswap32(desc.fidr);
- s->dma_ch[i].command = tswap32(desc.ldcmd);
+ s->dma_ch[i].descriptor = le32_to_cpu(desc.fdaddr);
+ s->dma_ch[i].source = le32_to_cpu(desc.fsaddr);
+ s->dma_ch[i].id = le32_to_cpu(desc.fidr);
+ s->dma_ch[i].command = le32_to_cpu(desc.ldcmd);
}
}
diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c
index c06aabb406..94be376127 100644
--- a/hw/dma/soc_dma.c
+++ b/hw/dma/soc_dma.c
@@ -269,11 +269,10 @@ void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base,
if (entry->type == soc_dma_port_mem) {
if (entry->addr <= virt_base &&
entry->addr + entry->u.mem.size > virt_base) {
- fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx
- " collides with RAM region at " TARGET_FMT_lx
- "-" TARGET_FMT_lx "\n", __FUNCTION__,
- (target_ulong) virt_base,
- (target_ulong) entry->addr, (target_ulong)
+ fprintf(stderr, "%s: FIFO at %"PRIx64
+ " collides with RAM region at %"PRIx64
+ "-%"PRIx64 "\n", __func__,
+ virt_base, entry->addr,
(entry->addr + entry->u.mem.size));
exit(-1);
}
@@ -284,10 +283,9 @@ void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base,
while (entry < dma->memmap + dma->memmap_size &&
entry->addr <= virt_base) {
if (entry->addr == virt_base && entry->u.fifo.out == out) {
- fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx
- " collides FIFO at " TARGET_FMT_lx "\n",
- __FUNCTION__, (target_ulong) virt_base,
- (target_ulong) entry->addr);
+ fprintf(stderr, "%s: FIFO at %"PRIx64
+ " collides FIFO at %"PRIx64 "\n",
+ __func__, virt_base, entry->addr);
exit(-1);
}
@@ -322,13 +320,11 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
if ((entry->addr >= virt_base && entry->addr < virt_base + size) ||
(entry->addr <= virt_base &&
entry->addr + entry->u.mem.size > virt_base)) {
- fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx
- " collides with RAM region at " TARGET_FMT_lx
- "-" TARGET_FMT_lx "\n", __FUNCTION__,
- (target_ulong) virt_base,
- (target_ulong) (virt_base + size),
- (target_ulong) entry->addr, (target_ulong)
- (entry->addr + entry->u.mem.size));
+ fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64
+ " collides with RAM region at %"PRIx64
+ "-%"PRIx64 "\n", __func__,
+ virt_base, virt_base + size,
+ entry->addr, entry->addr + entry->u.mem.size);
exit(-1);
}
@@ -337,12 +333,11 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
} else {
if (entry->addr >= virt_base &&
entry->addr < virt_base + size) {
- fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx
- " collides with FIFO at " TARGET_FMT_lx
- "\n", __FUNCTION__,
- (target_ulong) virt_base,
- (target_ulong) (virt_base + size),
- (target_ulong) entry->addr);
+ fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64
+ " collides with FIFO at %"PRIx64
+ "\n", __func__,
+ virt_base, virt_base + size,
+ entry->addr);
exit(-1);
}
diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c
index 3170585a27..a6d7cab7dc 100644
--- a/hw/gpio/imx_gpio.c
+++ b/hw/gpio/imx_gpio.c
@@ -62,7 +62,12 @@ static const char *imx_gpio_reg_name(uint32_t reg)
static void imx_gpio_update_int(IMXGPIOState *s)
{
- qemu_set_irq(s->irq, (s->isr & s->imr) ? 1 : 0);
+ if (s->has_upper_pin_irq) {
+ qemu_set_irq(s->irq[0], (s->isr & s->imr & 0x0000FFFF) ? 1 : 0);
+ qemu_set_irq(s->irq[1], (s->isr & s->imr & 0xFFFF0000) ? 1 : 0);
+ } else {
+ qemu_set_irq(s->irq[0], (s->isr & s->imr) ? 1 : 0);
+ }
}
static void imx_gpio_set_int_line(IMXGPIOState *s, int line, IMXGPIOLevel level)
@@ -282,6 +287,8 @@ static const VMStateDescription vmstate_imx_gpio = {
static Property imx_gpio_properties[] = {
DEFINE_PROP_BOOL("has-edge-sel", IMXGPIOState, has_edge_sel, true),
+ DEFINE_PROP_BOOL("has-upper-pin-irq", IMXGPIOState, has_upper_pin_irq,
+ false),
DEFINE_PROP_END_OF_LIST(),
};
@@ -311,7 +318,8 @@ static void imx_gpio_realize(DeviceState *dev, Error **errp)
qdev_init_gpio_in(DEVICE(s), imx_gpio_set, IMX_GPIO_PIN_COUNT);
qdev_init_gpio_out(DEVICE(s), s->output, IMX_GPIO_PIN_COUNT);
- sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[0]);
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[1]);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
}
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 29e30ce79a..fa866f6378 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -487,7 +487,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
int64_t bsel_val = qint_get_int(qobject_to_qint(bsel));
aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val)));
- notify_method = aml_method("DVNT", 2);
+ notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED);
}
for (i = 0; i < ARRAY_SIZE(bus->devices); i += PCI_FUNC_MAX) {
@@ -503,7 +503,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
dev = aml_device("S%.02X", PCI_DEVFN(slot, 0));
aml_append(dev, aml_name_decl("_SUN", aml_int(slot)));
aml_append(dev, aml_name_decl("_ADR", aml_int(slot << 16)));
- method = aml_method("_EJ0", 1);
+ method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
aml_append(method,
aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN"))
);
@@ -546,22 +546,22 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
s3d = 0;
}
- method = aml_method("_S1D", 0);
+ method = aml_method("_S1D", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_int(0)));
aml_append(dev, method);
- method = aml_method("_S2D", 0);
+ method = aml_method("_S2D", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_int(0)));
aml_append(dev, method);
- method = aml_method("_S3D", 0);
+ method = aml_method("_S3D", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_int(s3d)));
aml_append(dev, method);
} else if (hotplug_enabled_dev) {
/* add _SUN/_EJ0 to make slot hotpluggable */
aml_append(dev, aml_name_decl("_SUN", aml_int(slot)));
- method = aml_method("_EJ0", 1);
+ method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
aml_append(method,
aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN"))
);
@@ -590,7 +590,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
/* Append PCNT method to notify about events on local and child buses.
* Add unconditionally for root since DSDT expects it.
*/
- method = aml_method("PCNT", 0);
+ method = aml_method("PCNT", 0, AML_NOTSERIALIZED);
/* If bus supports hotplug select it and notify about local events */
if (bsel) {
@@ -651,7 +651,7 @@ static Aml *build_prt(void)
{
Aml *method, *while_ctx, *pin, *res;
- method = aml_method("_PRT", 0);
+ method = aml_method("_PRT", 0, AML_NOTSERIALIZED);
res = aml_local(0);
pin = aml_local(1);
aml_append(method, aml_store(aml_package(128), res));
@@ -1112,12 +1112,12 @@ build_ssdt(GArray *table_data, GArray *linker,
/* device present, functioning, decoding, shown in UI */
aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
- method = aml_method("RDPT", 0);
+ method = aml_method("RDPT", 0, AML_NOTSERIALIZED);
aml_append(method, aml_store(aml_name("PEPT"), aml_local(0)));
aml_append(method, aml_return(aml_local(0)));
aml_append(dev, method);
- method = aml_method("WRPT", 1);
+ method = aml_method("WRPT", 1, AML_NOTSERIALIZED);
aml_append(method, aml_store(aml_arg(0), aml_name("PEPT")));
aml_append(dev, method);
@@ -1153,15 +1153,15 @@ build_ssdt(GArray *table_data, GArray *linker,
for (i = 0; i < acpi_cpus; i++) {
dev = aml_processor(i, 0, 0, "CP%.02X", i);
- method = aml_method("_MAT", 0);
+ method = aml_method("_MAT", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_call1("CPMA", aml_int(i))));
aml_append(dev, method);
- method = aml_method("_STA", 0);
+ method = aml_method("_STA", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_call1("CPST", aml_int(i))));
aml_append(dev, method);
- method = aml_method("_EJ0", 1);
+ method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
aml_append(method,
aml_return(aml_call2("CPEJ", aml_int(i), aml_arg(0)))
);
@@ -1174,7 +1174,7 @@ build_ssdt(GArray *table_data, GArray *linker,
* Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}
*/
/* Arg0 = Processor ID = APIC ID */
- method = aml_method("NTFY", 2);
+ method = aml_method("NTFY", 2, AML_NOTSERIALIZED);
for (i = 0; i < acpi_cpus; i++) {
ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
aml_append(ifctx,
@@ -1269,29 +1269,29 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_append(dev, aml_name_decl("_UID", aml_string("0x%02X", i)));
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C80")));
- method = aml_method("_CRS", 0);
+ method = aml_method("_CRS", 0, AML_NOTSERIALIZED);
s = BASEPATH stringify(MEMORY_SLOT_CRS_METHOD);
aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
aml_append(dev, method);
- method = aml_method("_STA", 0);
+ method = aml_method("_STA", 0, AML_NOTSERIALIZED);
s = BASEPATH stringify(MEMORY_SLOT_STATUS_METHOD);
aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
aml_append(dev, method);
- method = aml_method("_PXM", 0);
+ method = aml_method("_PXM", 0, AML_NOTSERIALIZED);
s = BASEPATH stringify(MEMORY_SLOT_PROXIMITY_METHOD);
aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
aml_append(dev, method);
- method = aml_method("_OST", 3);
+ method = aml_method("_OST", 3, AML_NOTSERIALIZED);
s = BASEPATH stringify(MEMORY_SLOT_OST_METHOD);
aml_append(method, aml_return(aml_call4(
s, aml_name("_UID"), aml_arg(0), aml_arg(1), aml_arg(2)
)));
aml_append(dev, method);
- method = aml_method("_EJ0", 1);
+ method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
s = BASEPATH stringify(MEMORY_SLOT_EJECT_METHOD);
aml_append(method, aml_return(aml_call2(
s, aml_name("_UID"), aml_arg(0))));
@@ -1303,7 +1303,8 @@ build_ssdt(GArray *table_data, GArray *linker,
/* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) {
* If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... }
*/
- method = aml_method(stringify(MEMORY_SLOT_NOTIFY_METHOD), 2);
+ method = aml_method(stringify(MEMORY_SLOT_NOTIFY_METHOD), 2,
+ AML_NOTSERIALIZED);
for (i = 0; i < nr_mem; i++) {
ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
aml_append(ifctx,
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index aeb6b7da74..8a235dfb57 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -26,6 +26,8 @@ obj-$(CONFIG_NSERIES) += cbus.o
obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
obj-$(CONFIG_IMX) += imx_ccm.o
+obj-$(CONFIG_IMX) += imx31_ccm.o
+obj-$(CONFIG_IMX) += imx25_ccm.o
obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
obj-$(CONFIG_MAINSTONE) += mst_fpga.o
diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c
new file mode 100644
index 0000000000..23759ca648
--- /dev/null
+++ b/hw/misc/imx25_ccm.c
@@ -0,0 +1,341 @@
+/*
+ * IMX25 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the CCM.
+ */
+
+#include "hw/misc/imx25_ccm.h"
+
+#ifndef DEBUG_IMX25_CCM
+#define DEBUG_IMX25_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX25_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX25_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static char const *imx25_ccm_reg_name(uint32_t reg)
+{
+ static char unknown[20];
+
+ switch (reg) {
+ case IMX25_CCM_MPCTL_REG:
+ return "mpctl";
+ case IMX25_CCM_UPCTL_REG:
+ return "upctl";
+ case IMX25_CCM_CCTL_REG:
+ return "cctl";
+ case IMX25_CCM_CGCR0_REG:
+ return "cgcr0";
+ case IMX25_CCM_CGCR1_REG:
+ return "cgcr1";
+ case IMX25_CCM_CGCR2_REG:
+ return "cgcr2";
+ case IMX25_CCM_PCDR0_REG:
+ return "pcdr0";
+ case IMX25_CCM_PCDR1_REG:
+ return "pcdr1";
+ case IMX25_CCM_PCDR2_REG:
+ return "pcdr2";
+ case IMX25_CCM_PCDR3_REG:
+ return "pcdr3";
+ case IMX25_CCM_RCSR_REG:
+ return "rcsr";
+ case IMX25_CCM_CRDR_REG:
+ return "crdr";
+ case IMX25_CCM_DCVR0_REG:
+ return "dcvr0";
+ case IMX25_CCM_DCVR1_REG:
+ return "dcvr1";
+ case IMX25_CCM_DCVR2_REG:
+ return "dcvr2";
+ case IMX25_CCM_DCVR3_REG:
+ return "dcvr3";
+ case IMX25_CCM_LTR0_REG:
+ return "ltr0";
+ case IMX25_CCM_LTR1_REG:
+ return "ltr1";
+ case IMX25_CCM_LTR2_REG:
+ return "ltr2";
+ case IMX25_CCM_LTR3_REG:
+ return "ltr3";
+ case IMX25_CCM_LTBR0_REG:
+ return "ltbr0";
+ case IMX25_CCM_LTBR1_REG:
+ return "ltbr1";
+ case IMX25_CCM_PMCR0_REG:
+ return "pmcr0";
+ case IMX25_CCM_PMCR1_REG:
+ return "pmcr1";
+ case IMX25_CCM_PMCR2_REG:
+ return "pmcr2";
+ case IMX25_CCM_MCR_REG:
+ return "mcr";
+ case IMX25_CCM_LPIMR0_REG:
+ return "lpimr0";
+ case IMX25_CCM_LPIMR1_REG:
+ return "lpimr1";
+ default:
+ sprintf(unknown, "[%d ?]", reg);
+ return unknown;
+ }
+}
+#define CKIH_FREQ 24000000 /* 24MHz crystal input */
+
+static const VMStateDescription vmstate_imx25_ccm = {
+ .name = TYPE_IMX25_CCM,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(reg, IMX25CCMState, IMX25_CCM_MAX_REG),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], MPLL_BYPASS)) {
+ freq = CKIH_FREQ;
+ } else {
+ freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_MPCTL_REG], CKIH_FREQ);
+ }
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_upll_clk(IMXCCMState *dev)
+{
+ uint32_t freq = 0;
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ if (!EXTRACT(s->reg[IMX25_CCM_CCTL_REG], UPLL_DIS)) {
+ freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_UPCTL_REG], CKIH_FREQ);
+ }
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ freq = imx25_ccm_get_mpll_clk(dev);
+
+ if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_SRC)) {
+ freq = (freq * 3 / 4);
+ }
+
+ freq = freq / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_CLK_DIV));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_ahb_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ freq = imx25_ccm_get_mcu_clk(dev)
+ / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], AHB_CLK_DIV));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_ipg_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+
+ freq = imx25_ccm_get_ahb_clk(dev) / 2;
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+ uint32_t freq = 0;
+ DPRINTF("Clock = %d)\n", clock);
+
+ switch (clock) {
+ case NOCLK:
+ break;
+ case CLK_MPLL:
+ freq = imx25_ccm_get_mpll_clk(dev);
+ break;
+ case CLK_UPLL:
+ freq = imx25_ccm_get_upll_clk(dev);
+ break;
+ case CLK_MCU:
+ freq = imx25_ccm_get_mcu_clk(dev);
+ break;
+ case CLK_AHB:
+ freq = imx25_ccm_get_ahb_clk(dev);
+ break;
+ case CLK_IPG:
+ freq = imx25_ccm_get_ipg_clk(dev);
+ break;
+ case CLK_32k:
+ freq = CKIL_FREQ;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+ TYPE_IMX25_CCM, __func__, clock);
+ break;
+ }
+
+ DPRINTF("Clock = %d) = %d\n", clock, freq);
+
+ return freq;
+}
+
+static void imx25_ccm_reset(DeviceState *dev)
+{
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ DPRINTF("\n");
+
+ memset(s->reg, 0, IMX25_CCM_MAX_REG * sizeof(uint32_t));
+ s->reg[IMX25_CCM_MPCTL_REG] = 0x800b2c01;
+ s->reg[IMX25_CCM_UPCTL_REG] = 0x84042800;
+ /*
+ * The value below gives:
+ * CPU = 133 MHz, AHB = 66,5 MHz, IPG = 33 MHz.
+ */
+ s->reg[IMX25_CCM_CCTL_REG] = 0xd0030000;
+ s->reg[IMX25_CCM_CGCR0_REG] = 0x028A0100;
+ s->reg[IMX25_CCM_CGCR1_REG] = 0x04008100;
+ s->reg[IMX25_CCM_CGCR2_REG] = 0x00000438;
+ s->reg[IMX25_CCM_PCDR0_REG] = 0x01010101;
+ s->reg[IMX25_CCM_PCDR1_REG] = 0x01010101;
+ s->reg[IMX25_CCM_PCDR2_REG] = 0x01010101;
+ s->reg[IMX25_CCM_PCDR3_REG] = 0x01010101;
+ s->reg[IMX25_CCM_PMCR0_REG] = 0x00A00000;
+ s->reg[IMX25_CCM_PMCR1_REG] = 0x0000A030;
+ s->reg[IMX25_CCM_PMCR2_REG] = 0x0000A030;
+ s->reg[IMX25_CCM_MCR_REG] = 0x43000000;
+
+ /*
+ * default boot will change the reset values to allow:
+ * CPU = 399 MHz, AHB = 133 MHz, IPG = 66,5 MHz.
+ * For some reason, this doesn't work. With the value below, linux
+ * detects a 88 MHz IPG CLK instead of 66,5 MHz.
+ s->reg[IMX25_CCM_CCTL_REG] = 0x20032000;
+ */
+}
+
+static uint64_t imx25_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32 value = 0;
+ IMX25CCMState *s = (IMX25CCMState *)opaque;
+
+ if (offset < 0x70) {
+ value = s->reg[offset >> 2];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset);
+ }
+
+ DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2),
+ value);
+
+ return value;
+}
+
+static void imx25_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ IMX25CCMState *s = (IMX25CCMState *)opaque;
+
+ DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2),
+ (uint32_t)value);
+
+ if (offset < 0x70) {
+ /*
+ * We will do a better implementation later. In particular some bits
+ * cannot be written to.
+ */
+ s->reg[offset >> 2] = value;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset);
+ }
+}
+
+static const struct MemoryRegionOps imx25_ccm_ops = {
+ .read = imx25_ccm_read,
+ .write = imx25_ccm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static void imx25_ccm_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+ IMX25CCMState *s = IMX25_CCM(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &imx25_ccm_ops, s,
+ TYPE_IMX25_CCM, 0x1000);
+ sysbus_init_mmio(sd, &s->iomem);
+}
+
+static void imx25_ccm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+ dc->reset = imx25_ccm_reset;
+ dc->vmsd = &vmstate_imx25_ccm;
+ dc->desc = "i.MX25 Clock Control Module";
+
+ ccm->get_clock_frequency = imx25_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx25_ccm_info = {
+ .name = TYPE_IMX25_CCM,
+ .parent = TYPE_IMX_CCM,
+ .instance_size = sizeof(IMX25CCMState),
+ .instance_init = imx25_ccm_init,
+ .class_init = imx25_ccm_class_init,
+};
+
+static void imx25_ccm_register_types(void)
+{
+ type_register_static(&imx25_ccm_info);
+}
+
+type_init(imx25_ccm_register_types)
diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c
new file mode 100644
index 0000000000..b92d2e0392
--- /dev/null
+++ b/hw/misc/imx31_ccm.c
@@ -0,0 +1,392 @@
+/*
+ * IMX31 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the i.MX31 CCM.
+ */
+
+#include "hw/misc/imx31_ccm.h"
+
+#define CKIH_FREQ 26000000 /* 26MHz crystal input */
+
+#ifndef DEBUG_IMX31_CCM
+#define DEBUG_IMX31_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX31_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX31_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static char const *imx31_ccm_reg_name(uint32_t reg)
+{
+ switch (reg) {
+ case 0:
+ return "CCMR";
+ case 1:
+ return "PDR0";
+ case 2:
+ return "PDR1";
+ case 3:
+ return "RCSR";
+ case 4:
+ return "MPCTL";
+ case 5:
+ return "UPCTL";
+ case 6:
+ return "SPCTL";
+ case 7:
+ return "COSR";
+ case 8:
+ return "CGR0";
+ case 9:
+ return "CGR1";
+ case 10:
+ return "CGR2";
+ case 11:
+ return "WIMR";
+ case 12:
+ return "LDC";
+ case 13:
+ return "DCVR0";
+ case 14:
+ return "DCVR1";
+ case 15:
+ return "DCVR2";
+ case 16:
+ return "DCVR3";
+ case 17:
+ return "LTR0";
+ case 18:
+ return "LTR1";
+ case 19:
+ return "LTR2";
+ case 20:
+ return "LTR3";
+ case 21:
+ return "LTBR0";
+ case 22:
+ return "LTBR1";
+ case 23:
+ return "PMCR0";
+ case 24:
+ return "PMCR1";
+ case 25:
+ return "PDR2";
+ default:
+ return "???";
+ }
+}
+
+static const VMStateDescription vmstate_imx31_ccm = {
+ .name = TYPE_IMX31_CCM,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ccmr, IMX31CCMState),
+ VMSTATE_UINT32(pdr0, IMX31CCMState),
+ VMSTATE_UINT32(pdr1, IMX31CCMState),
+ VMSTATE_UINT32(mpctl, IMX31CCMState),
+ VMSTATE_UINT32(spctl, IMX31CCMState),
+ VMSTATE_UINT32_ARRAY(cgr, IMX31CCMState, 3),
+ VMSTATE_UINT32(pmcr0, IMX31CCMState),
+ VMSTATE_UINT32(pmcr1, IMX31CCMState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static uint32_t imx31_ccm_get_pll_ref_clk(IMXCCMState *dev)
+{
+ uint32_t freq = 0;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ if ((s->ccmr & CCMR_PRCS) == 2) {
+ if (s->ccmr & CCMR_FPME) {
+ freq = CKIL_FREQ;
+ if (s->ccmr & CCMR_FPMF) {
+ freq *= 1024;
+ }
+ }
+ } else {
+ freq = CKIH_FREQ;
+ }
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_mpll_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ freq = imx_ccm_calc_pll(s->mpctl, imx31_ccm_get_pll_ref_clk(dev));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
+ freq = imx31_ccm_get_pll_ref_clk(dev);
+ } else {
+ freq = imx31_ccm_get_mpll_clk(dev);
+ }
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_mcu_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, MCU));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_hsp_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, HSP));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, MAX));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_ipg_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ freq = imx31_ccm_get_hclk_clk(dev) / (1 + EXTRACT(s->pdr0, IPG));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+ uint32_t freq = 0;
+
+ switch (clock) {
+ case NOCLK:
+ break;
+ case CLK_MCU:
+ freq = imx31_ccm_get_mcu_clk(dev);
+ break;
+ case CLK_HSP:
+ freq = imx31_ccm_get_hsp_clk(dev);
+ break;
+ case CLK_IPG:
+ freq = imx31_ccm_get_ipg_clk(dev);
+ break;
+ case CLK_32k:
+ freq = CKIL_FREQ;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+ TYPE_IMX31_CCM, __func__, clock);
+ break;
+ }
+
+ DPRINTF("Clock = %d) = %d\n", clock, freq);
+
+ return freq;
+}
+
+static void imx31_ccm_reset(DeviceState *dev)
+{
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ DPRINTF("()\n");
+
+ s->ccmr = 0x074b0b7d;
+ s->pdr0 = 0xff870b48;
+ s->pdr1 = 0x49fcfe7f;
+ s->mpctl = 0x04001800;
+ s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
+ s->spctl = 0x04043001;
+ s->pmcr0 = 0x80209828;
+ s->pmcr1 = 0x00aa0000;
+}
+
+static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32 value = 0;
+ IMX31CCMState *s = (IMX31CCMState *)opaque;
+
+ switch (offset >> 2) {
+ case 0: /* CCMR */
+ value = s->ccmr;
+ break;
+ case 1:
+ value = s->pdr0;
+ break;
+ case 2:
+ value = s->pdr1;
+ break;
+ case 4:
+ value = s->mpctl;
+ break;
+ case 6:
+ value = s->spctl;
+ break;
+ case 8:
+ value = s->cgr[0];
+ break;
+ case 9:
+ value = s->cgr[1];
+ break;
+ case 10:
+ value = s->cgr[2];
+ break;
+ case 18: /* LTR1 */
+ value = 0x00004040;
+ break;
+ case 23:
+ value = s->pmcr0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
+ break;
+ }
+
+ DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
+ value);
+
+ return (uint64_t)value;
+}
+
+static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ IMX31CCMState *s = (IMX31CCMState *)opaque;
+
+ DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
+ (uint32_t)value);
+
+ switch (offset >> 2) {
+ case 0:
+ s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
+ break;
+ case 1:
+ s->pdr0 = value & 0xff9f3fff;
+ break;
+ case 2:
+ s->pdr1 = value;
+ break;
+ case 4:
+ s->mpctl = value & 0xbfff3fff;
+ break;
+ case 6:
+ s->spctl = value & 0xbfff3fff;
+ break;
+ case 8:
+ s->cgr[0] = value;
+ break;
+ case 9:
+ s->cgr[1] = value;
+ break;
+ case 10:
+ s->cgr[2] = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
+ break;
+ }
+}
+
+static const struct MemoryRegionOps imx31_ccm_ops = {
+ .read = imx31_ccm_read,
+ .write = imx31_ccm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+
+};
+
+static void imx31_ccm_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+ IMX31CCMState *s = IMX31_CCM(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &imx31_ccm_ops, s,
+ TYPE_IMX31_CCM, 0x1000);
+ sysbus_init_mmio(sd, &s->iomem);
+}
+
+static void imx31_ccm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+ dc->reset = imx31_ccm_reset;
+ dc->vmsd = &vmstate_imx31_ccm;
+ dc->desc = "i.MX31 Clock Control Module";
+
+ ccm->get_clock_frequency = imx31_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx31_ccm_info = {
+ .name = TYPE_IMX31_CCM,
+ .parent = TYPE_IMX_CCM,
+ .instance_size = sizeof(IMX31CCMState),
+ .instance_init = imx31_ccm_init,
+ .class_init = imx31_ccm_class_init,
+};
+
+static void imx31_ccm_register_types(void)
+{
+ type_register_static(&imx31_ccm_info);
+}
+
+type_init(imx31_ccm_register_types)
diff --git a/hw/misc/imx_ccm.c b/hw/misc/imx_ccm.c
index 4cc2bbc0ed..0c9740bd7a 100644
--- a/hw/misc/imx_ccm.c
+++ b/hw/misc/imx_ccm.c
@@ -7,15 +7,12 @@
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
- * To get the timer frequencies right, we need to emulate at least part of
- * the CCM.
+ * This is an abstract base class used to get a common interface to
+ * retrieve the CCM frequencies from the various i.MX SOC.
*/
#include "hw/misc/imx_ccm.h"
-#define CKIH_FREQ 26000000 /* 26MHz crystal input */
-#define CKIL_FREQ 32768 /* nominal 32khz clock */
-
#ifndef DEBUG_IMX_CCM
#define DEBUG_IMX_CCM 0
#endif
@@ -28,51 +25,27 @@
} \
} while (0)
-static int imx_ccm_post_load(void *opaque, int version_id);
-
-static const VMStateDescription vmstate_imx_ccm = {
- .name = TYPE_IMX_CCM,
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(ccmr, IMXCCMState),
- VMSTATE_UINT32(pdr0, IMXCCMState),
- VMSTATE_UINT32(pdr1, IMXCCMState),
- VMSTATE_UINT32(mpctl, IMXCCMState),
- VMSTATE_UINT32(spctl, IMXCCMState),
- VMSTATE_UINT32_ARRAY(cgr, IMXCCMState, 3),
- VMSTATE_UINT32(pmcr0, IMXCCMState),
- VMSTATE_UINT32(pmcr1, IMXCCMState),
- VMSTATE_UINT32(pll_refclk_freq, IMXCCMState),
- VMSTATE_END_OF_LIST()
- },
- .post_load = imx_ccm_post_load,
-};
-uint32_t imx_clock_frequency(DeviceState *dev, IMXClk clock)
+uint32_t imx_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
{
- IMXCCMState *s = IMX_CCM(dev);
+ uint32_t freq = 0;
+ IMXCCMClass *klass = IMX_GET_CLASS(dev);
- switch (clock) {
- case NOCLK:
- return 0;
- case MCU:
- return s->mcu_clk_freq;
- case HSP:
- return s->hsp_clk_freq;
- case IPG:
- return s->ipg_clk_freq;
- case CLK_32k:
- return CKIL_FREQ;
+ if (klass->get_clock_frequency) {
+ freq = klass->get_clock_frequency(dev, clock);
}
- return 0;
+
+ DPRINTF("(clock = %d) = %d\n", clock, freq);
+
+ return freq;
}
/*
* Calculate PLL output frequency
*/
-static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
+uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq)
{
+ int32_t freq;
int32_t mfn = MFN(pllreg); /* Numerator */
uint32_t mfi = MFI(pllreg); /* Integer part */
uint32_t mfd = 1 + MFD(pllreg); /* Denominator */
@@ -81,186 +54,26 @@ static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
if (mfi < 5) {
mfi = 5;
}
+
/* mfn is 10-bit signed twos-complement */
mfn <<= 32 - 10;
mfn >>= 32 - 10;
- return ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
+ freq = ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
(mfd * pd)) << 10;
-}
-
-static void update_clocks(IMXCCMState *s)
-{
- /*
- * If we ever emulate more clocks, this should switch to a data-driven
- * approach
- */
-
- if ((s->ccmr & CCMR_PRCS) == 2) {
- s->pll_refclk_freq = CKIL_FREQ * 1024;
- } else {
- s->pll_refclk_freq = CKIH_FREQ;
- }
-
- /* ipg_clk_arm aka MCU clock */
- if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
- s->mcu_clk_freq = s->pll_refclk_freq;
- } else {
- s->mcu_clk_freq = calc_pll(s->mpctl, s->pll_refclk_freq);
- }
-
- /* High-speed clock */
- s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP));
- s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG));
-
- DPRINTF("mcu %uMHz, HSP %uMHz, IPG %uHz\n",
- s->mcu_clk_freq / 1000000,
- s->hsp_clk_freq / 1000000,
- s->ipg_clk_freq);
-}
-
-static void imx_ccm_reset(DeviceState *dev)
-{
- IMXCCMState *s = IMX_CCM(dev);
-
- s->ccmr = 0x074b0b7b;
- s->pdr0 = 0xff870b48;
- s->pdr1 = 0x49fcfe7f;
- s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0);
- s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
- s->spctl = PLL_PD(1) | PLL_MFD(4) | PLL_MFI(0xc) | PLL_MFN(1);
- s->pmcr0 = 0x80209828;
-
- update_clocks(s);
-}
-
-static uint64_t imx_ccm_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- IMXCCMState *s = (IMXCCMState *)opaque;
- DPRINTF("(offset=0x%" HWADDR_PRIx ")\n", offset);
-
- switch (offset >> 2) {
- case 0: /* CCMR */
- DPRINTF(" ccmr = 0x%x\n", s->ccmr);
- return s->ccmr;
- case 1:
- DPRINTF(" pdr0 = 0x%x\n", s->pdr0);
- return s->pdr0;
- case 2:
- DPRINTF(" pdr1 = 0x%x\n", s->pdr1);
- return s->pdr1;
- case 4:
- DPRINTF(" mpctl = 0x%x\n", s->mpctl);
- return s->mpctl;
- case 6:
- DPRINTF(" spctl = 0x%x\n", s->spctl);
- return s->spctl;
- case 8:
- DPRINTF(" cgr0 = 0x%x\n", s->cgr[0]);
- return s->cgr[0];
- case 9:
- DPRINTF(" cgr1 = 0x%x\n", s->cgr[1]);
- return s->cgr[1];
- case 10:
- DPRINTF(" cgr2 = 0x%x\n", s->cgr[2]);
- return s->cgr[2];
- case 18: /* LTR1 */
- return 0x00004040;
- case 23:
- DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0);
- return s->pmcr0;
- default:
- qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
- HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset);
- return 0;
- }
-}
-
-static void imx_ccm_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- IMXCCMState *s = (IMXCCMState *)opaque;
-
- DPRINTF("(offset=0x%" HWADDR_PRIx ", value = 0x%x)\n",
- offset, (unsigned int)value);
-
- switch (offset >> 2) {
- case 0:
- s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
- break;
- case 1:
- s->pdr0 = value & 0xff9f3fff;
- break;
- case 2:
- s->pdr1 = value;
- break;
- case 4:
- s->mpctl = value & 0xbfff3fff;
- break;
- case 6:
- s->spctl = value & 0xbfff3fff;
- break;
- case 8:
- s->cgr[0] = value;
- return;
- case 9:
- s->cgr[1] = value;
- return;
- case 10:
- s->cgr[2] = value;
- return;
-
- default:
- qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
- HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset);
- return;
- }
- update_clocks(s);
-}
-
-static const struct MemoryRegionOps imx_ccm_ops = {
- .read = imx_ccm_read,
- .write = imx_ccm_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int imx_ccm_init(SysBusDevice *dev)
-{
- IMXCCMState *s = IMX_CCM(dev);
-
- memory_region_init_io(&s->iomem, OBJECT(dev), &imx_ccm_ops, s,
- TYPE_IMX_CCM, 0x1000);
- sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
-}
-
-static int imx_ccm_post_load(void *opaque, int version_id)
-{
- IMXCCMState *s = (IMXCCMState *)opaque;
-
- update_clocks(s);
- return 0;
-}
-
-static void imx_ccm_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+ DPRINTF("(pllreg = 0x%08x, base_freq = %d) = %d\n", pllreg, base_freq,
+ freq);
- sbc->init = imx_ccm_init;
- dc->reset = imx_ccm_reset;
- dc->vmsd = &vmstate_imx_ccm;
- dc->desc = "i.MX Clock Control Module";
+ return freq;
}
static const TypeInfo imx_ccm_info = {
- .name = TYPE_IMX_CCM,
- .parent = TYPE_SYS_BUS_DEVICE,
+ .name = TYPE_IMX_CCM,
+ .parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IMXCCMState),
- .class_init = imx_ccm_class_init,
+ .class_size = sizeof(IMXCCMClass),
+ .abstract = true,
};
static void imx_ccm_register_types(void)
diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c
index 967be4ad27..50bf83c253 100644
--- a/hw/timer/imx_epit.c
+++ b/hw/timer/imx_epit.c
@@ -51,9 +51,9 @@ static char const *imx_epit_reg_name(uint32_t reg)
* These are typical.
*/
static const IMXClk imx_epit_clocks[] = {
- 0, /* 00 disabled */
- IPG, /* 01 ipg_clk, ~532MHz */
- IPG, /* 10 ipg_clk_highfreq */
+ NOCLK, /* 00 disabled */
+ CLK_IPG, /* 01 ipg_clk, ~532MHz */
+ CLK_IPG, /* 10 ipg_clk_highfreq */
CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */
};
@@ -73,20 +73,18 @@ static void imx_epit_set_freq(IMXEPITState *s)
{
uint32_t clksrc;
uint32_t prescaler;
- uint32_t freq;
clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, 2);
prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, 12);
- freq = imx_clock_frequency(s->ccm, imx_epit_clocks[clksrc]) / prescaler;
+ s->freq = imx_ccm_get_clock_frequency(s->ccm,
+ imx_epit_clocks[clksrc]) / prescaler;
- s->freq = freq;
+ DPRINTF("Setting ptimer frequency to %u\n", s->freq);
- DPRINTF("Setting ptimer frequency to %u\n", freq);
-
- if (freq) {
- ptimer_set_freq(s->timer_reload, freq);
- ptimer_set_freq(s->timer_cmp, freq);
+ if (s->freq) {
+ ptimer_set_freq(s->timer_reload, s->freq);
+ ptimer_set_freq(s->timer_cmp, s->freq);
}
}
diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c
index 7257f4201a..b1893b8350 100644
--- a/hw/timer/imx_gpt.c
+++ b/hw/timer/imx_gpt.c
@@ -81,8 +81,8 @@ static const VMStateDescription vmstate_imx_timer_gpt = {
static const IMXClk imx_gpt_clocks[] = {
NOCLK, /* 000 No clock source */
- IPG, /* 001 ipg_clk, 532MHz*/
- IPG, /* 010 ipg_clk_highfreq */
+ CLK_IPG, /* 001 ipg_clk, 532MHz*/
+ CLK_IPG, /* 010 ipg_clk_highfreq */
NOCLK, /* 011 not defined */
CLK_32k, /* 100 ipg_clk_32k */
NOCLK, /* 101 not defined */
@@ -93,14 +93,14 @@ static const IMXClk imx_gpt_clocks[] = {
static void imx_gpt_set_freq(IMXGPTState *s)
{
uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3);
- uint32_t freq = imx_clock_frequency(s->ccm, imx_gpt_clocks[clksrc])
- / (1 + s->pr);
- s->freq = freq;
- DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, freq);
+ s->freq = imx_ccm_get_clock_frequency(s->ccm,
+ imx_gpt_clocks[clksrc]) / (1 + s->pr);
- if (freq) {
- ptimer_set_freq(s->timer, freq);
+ DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, s->freq);
+
+ if (s->freq) {
+ ptimer_set_freq(s->timer, s->freq);
}
}
diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h
index 1b632dc542..0d3645322c 100644
--- a/include/hw/acpi/aml-build.h
+++ b/include/hw/acpi/aml-build.h
@@ -148,6 +148,32 @@ typedef enum {
AML_SHARED_AND_WAKE = 3,
} AmlShared;
+/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: MethodFlags */
+typedef enum {
+ AML_NOTSERIALIZED = 0,
+ AML_SERIALIZED = 1,
+} AmlSerializeFlag;
+
+/*
+ * ACPI 5.0: Table 6-189 GPIO Connection Descriptor Definition
+ * GPIO Connection Type
+ */
+typedef enum {
+ AML_INTERRUPT_CONNECTION = 0,
+ AML_IO_CONNECTION = 1,
+} AmlGpioConnectionType;
+
+/*
+ * ACPI 5.0: Table 6-189 GPIO Connection Descriptor Definition
+ * _PPI field definition
+ */
+typedef enum {
+ AML_PULL_DEFAULT = 0,
+ AML_PULL_UP = 1,
+ AML_PULL_DOWN = 2,
+ AML_PULL_NONE = 3,
+} AmlPinConfig;
+
typedef
struct AcpiBuildTables {
GArray *table_data;
@@ -212,12 +238,19 @@ Aml *aml_call1(const char *method, Aml *arg1);
Aml *aml_call2(const char *method, Aml *arg1, Aml *arg2);
Aml *aml_call3(const char *method, Aml *arg1, Aml *arg2, Aml *arg3);
Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4);
+Aml *aml_gpio_int(AmlConsumerAndProducer con_and_pro,
+ AmlLevelAndEdge edge_level,
+ AmlActiveHighAndLow active_level, AmlShared shared,
+ AmlPinConfig pin_config, uint16_t debounce_timeout,
+ const uint32_t pin_list[], uint32_t pin_count,
+ const char *resource_source_name,
+ const uint8_t *vendor_data, uint16_t vendor_data_len);
Aml *aml_memory32_fixed(uint32_t addr, uint32_t size,
AmlReadAndWrite read_and_write);
Aml *aml_interrupt(AmlConsumerAndProducer con_and_pro,
AmlLevelAndEdge level_and_edge,
AmlActiveHighAndLow high_and_low, AmlShared shared,
- uint32_t irq);
+ uint32_t *irq_list, uint8_t irq_count);
Aml *aml_io(AmlIODecode dec, uint16_t min_base, uint16_t max_base,
uint8_t aln, uint8_t len);
Aml *aml_operation_region(const char *name, AmlRegionSpace rs,
@@ -262,7 +295,7 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed,
/* Block AML object primitives */
Aml *aml_scope(const char *name_format, ...) GCC_FMT_ATTR(1, 2);
Aml *aml_device(const char *name_format, ...) GCC_FMT_ATTR(1, 2);
-Aml *aml_method(const char *name, int arg_count);
+Aml *aml_method(const char *name, int arg_count, AmlSerializeFlag sflag);
Aml *aml_if(Aml *predicate);
Aml *aml_else(void);
Aml *aml_while(Aml *predicate);
diff --git a/include/hw/arm/fsl-imx25.h b/include/hw/arm/fsl-imx25.h
index 73f50c64d8..d0e8e9d956 100644
--- a/include/hw/arm/fsl-imx25.h
+++ b/include/hw/arm/fsl-imx25.h
@@ -19,7 +19,7 @@
#include "hw/arm/arm.h"
#include "hw/intc/imx_avic.h"
-#include "hw/misc/imx_ccm.h"
+#include "hw/misc/imx25_ccm.h"
#include "hw/char/imx_serial.h"
#include "hw/timer/imx_gpt.h"
#include "hw/timer/imx_epit.h"
@@ -44,7 +44,7 @@ typedef struct FslIMX25State {
/*< public >*/
ARMCPU cpu;
IMXAVICState avic;
- IMXCCMState ccm;
+ IMX25CCMState ccm;
IMXSerialState uart[FSL_IMX25_NUM_UARTS];
IMXGPTState gpt[FSL_IMX25_NUM_GPTS];
IMXEPITState epit[FSL_IMX25_NUM_EPITS];
diff --git a/include/hw/arm/fsl-imx31.h b/include/hw/arm/fsl-imx31.h
index 5e8f795f98..d408abbba0 100644
--- a/include/hw/arm/fsl-imx31.h
+++ b/include/hw/arm/fsl-imx31.h
@@ -19,7 +19,7 @@
#include "hw/arm/arm.h"
#include "hw/intc/imx_avic.h"
-#include "hw/misc/imx_ccm.h"
+#include "hw/misc/imx31_ccm.h"
#include "hw/char/imx_serial.h"
#include "hw/timer/imx_gpt.h"
#include "hw/timer/imx_epit.h"
@@ -42,7 +42,7 @@ typedef struct FslIMX31State {
/*< public >*/
ARMCPU cpu;
IMXAVICState avic;
- IMXCCMState ccm;
+ IMX31CCMState ccm;
IMXSerialState uart[FSL_IMX31_NUM_UARTS];
IMXGPTState gpt;
IMXEPITState epit[FSL_IMX31_NUM_EPITS];
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index f464586304..925faa7249 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -59,6 +59,7 @@ enum {
VIRT_PCIE_ECAM,
VIRT_PLATFORM_BUS,
VIRT_PCIE_MMIO_HIGH,
+ VIRT_GPIO,
};
typedef struct MemMapEntry {
diff --git a/include/hw/gpio/imx_gpio.h b/include/hw/gpio/imx_gpio.h
index 517b2618cb..b15a09fbca 100644
--- a/include/hw/gpio/imx_gpio.h
+++ b/include/hw/gpio/imx_gpio.h
@@ -54,8 +54,9 @@ typedef struct IMXGPIOState {
uint32_t isr;
bool has_edge_sel;
uint32_t edge_sel;
+ bool has_upper_pin_irq;
- qemu_irq irq;
+ qemu_irq irq[2];
qemu_irq output[IMX_GPIO_PIN_COUNT];
} IMXGPIOState;
diff --git a/include/hw/misc/imx25_ccm.h b/include/hw/misc/imx25_ccm.h
new file mode 100644
index 0000000000..296321c612
--- /dev/null
+++ b/include/hw/misc/imx25_ccm.h
@@ -0,0 +1,79 @@
+/*
+ * IMX25 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef IMX25_CCM_H
+#define IMX25_CCM_H
+
+#include "hw/misc/imx_ccm.h"
+
+#define IMX25_CCM_MPCTL_REG 0
+#define IMX25_CCM_UPCTL_REG 1
+#define IMX25_CCM_CCTL_REG 2
+#define IMX25_CCM_CGCR0_REG 3
+#define IMX25_CCM_CGCR1_REG 4
+#define IMX25_CCM_CGCR2_REG 5
+#define IMX25_CCM_PCDR0_REG 6
+#define IMX25_CCM_PCDR1_REG 7
+#define IMX25_CCM_PCDR2_REG 8
+#define IMX25_CCM_PCDR3_REG 9
+#define IMX25_CCM_RCSR_REG 10
+#define IMX25_CCM_CRDR_REG 11
+#define IMX25_CCM_DCVR0_REG 12
+#define IMX25_CCM_DCVR1_REG 13
+#define IMX25_CCM_DCVR2_REG 14
+#define IMX25_CCM_DCVR3_REG 15
+#define IMX25_CCM_LTR0_REG 16
+#define IMX25_CCM_LTR1_REG 17
+#define IMX25_CCM_LTR2_REG 18
+#define IMX25_CCM_LTR3_REG 19
+#define IMX25_CCM_LTBR0_REG 20
+#define IMX25_CCM_LTBR1_REG 21
+#define IMX25_CCM_PMCR0_REG 22
+#define IMX25_CCM_PMCR1_REG 23
+#define IMX25_CCM_PMCR2_REG 24
+#define IMX25_CCM_MCR_REG 25
+#define IMX25_CCM_LPIMR0_REG 26
+#define IMX25_CCM_LPIMR1_REG 27
+#define IMX25_CCM_MAX_REG 28
+
+/* CCTL */
+#define CCTL_ARM_CLK_DIV_SHIFT (30)
+#define CCTL_ARM_CLK_DIV_MASK (0x3)
+#define CCTL_AHB_CLK_DIV_SHIFT (28)
+#define CCTL_AHB_CLK_DIV_MASK (0x3)
+#define CCTL_MPLL_BYPASS_SHIFT (22)
+#define CCTL_MPLL_BYPASS_MASK (0x1)
+#define CCTL_USB_DIV_SHIFT (16)
+#define CCTL_USB_DIV_MASK (0x3F)
+#define CCTL_ARM_SRC_SHIFT (13)
+#define CCTL_ARM_SRC_MASK (0x1)
+#define CCTL_UPLL_DIS_SHIFT (23)
+#define CCTL_UPLL_DIS_MASK (0x1)
+
+#define EXTRACT(value, name) (((value) >> CCTL_##name##_SHIFT) \
+ & CCTL_##name##_MASK)
+#define INSERT(value, name) (((value) & CCTL_##name##_MASK) << \
+ CCTL_##name##_SHIFT)
+
+#define TYPE_IMX25_CCM "imx25.ccm"
+#define IMX25_CCM(obj) OBJECT_CHECK(IMX25CCMState, (obj), TYPE_IMX25_CCM)
+
+typedef struct IMX25CCMState {
+ /* <private> */
+ IMXCCMState parent_obj;
+
+ /* <public> */
+ MemoryRegion iomem;
+
+ uint32_t reg[IMX25_CCM_MAX_REG];
+
+} IMX25CCMState;
+
+#endif /* IMX25_CCM_H */
diff --git a/include/hw/misc/imx31_ccm.h b/include/hw/misc/imx31_ccm.h
new file mode 100644
index 0000000000..fcae36d426
--- /dev/null
+++ b/include/hw/misc/imx31_ccm.h
@@ -0,0 +1,66 @@
+/*
+ * IMX31 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef IMX31_CCM_H
+#define IMX31_CCM_H
+
+#include "hw/misc/imx_ccm.h"
+
+/* CCMR */
+#define CCMR_FPME (1<<0)
+#define CCMR_MPE (1<<3)
+#define CCMR_MDS (1<<7)
+#define CCMR_FPMF (1<<26)
+#define CCMR_PRCS (3<<1)
+
+#define PMCR0_DFSUP1 (1<<31)
+
+/* PDR0 */
+#define PDR0_MCU_PODF_SHIFT (0)
+#define PDR0_MCU_PODF_MASK (0x7)
+#define PDR0_MAX_PODF_SHIFT (3)
+#define PDR0_MAX_PODF_MASK (0x7)
+#define PDR0_IPG_PODF_SHIFT (6)
+#define PDR0_IPG_PODF_MASK (0x3)
+#define PDR0_NFC_PODF_SHIFT (8)
+#define PDR0_NFC_PODF_MASK (0x7)
+#define PDR0_HSP_PODF_SHIFT (11)
+#define PDR0_HSP_PODF_MASK (0x7)
+#define PDR0_PER_PODF_SHIFT (16)
+#define PDR0_PER_PODF_MASK (0x1f)
+#define PDR0_CSI_PODF_SHIFT (23)
+#define PDR0_CSI_PODF_MASK (0x1ff)
+
+#define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \
+ & PDR0_##name##_PODF_MASK)
+#define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \
+ PDR0_##name##_PODF_SHIFT)
+
+#define TYPE_IMX31_CCM "imx31.ccm"
+#define IMX31_CCM(obj) OBJECT_CHECK(IMX31CCMState, (obj), TYPE_IMX31_CCM)
+
+typedef struct IMX31CCMState {
+ /* <private> */
+ IMXCCMState parent_obj;
+
+ /* <public> */
+ MemoryRegion iomem;
+
+ uint32_t ccmr;
+ uint32_t pdr0;
+ uint32_t pdr1;
+ uint32_t mpctl;
+ uint32_t spctl;
+ uint32_t cgr[3];
+ uint32_t pmcr0;
+ uint32_t pmcr1;
+} IMX31CCMState;
+
+#endif /* IMX31_CCM_H */
diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h
index 0f2e469b23..5c4b7958bd 100644
--- a/include/hw/misc/imx_ccm.h
+++ b/include/hw/misc/imx_ccm.h
@@ -1,5 +1,5 @@
/*
- * IMX31 Clock Control Module
+ * IMX Clock Control Module base class
*
* Copyright (C) 2012 NICTA
* Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
@@ -13,33 +13,7 @@
#include "hw/sysbus.h"
-/* CCMR */
-#define CCMR_FPME (1<<0)
-#define CCMR_MPE (1<<3)
-#define CCMR_MDS (1<<7)
-#define CCMR_FPMF (1<<26)
-#define CCMR_PRCS (3<<1)
-
-/* PDR0 */
-#define PDR0_MCU_PODF_SHIFT (0)
-#define PDR0_MCU_PODF_MASK (0x7)
-#define PDR0_MAX_PODF_SHIFT (3)
-#define PDR0_MAX_PODF_MASK (0x7)
-#define PDR0_IPG_PODF_SHIFT (6)
-#define PDR0_IPG_PODF_MASK (0x3)
-#define PDR0_NFC_PODF_SHIFT (8)
-#define PDR0_NFC_PODF_MASK (0x7)
-#define PDR0_HSP_PODF_SHIFT (11)
-#define PDR0_HSP_PODF_MASK (0x7)
-#define PDR0_PER_PODF_SHIFT (16)
-#define PDR0_PER_PODF_MASK (0x1f)
-#define PDR0_CSI_PODF_SHIFT (23)
-#define PDR0_CSI_PODF_MASK (0x1ff)
-
-#define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \
- & PDR0_##name##_PODF_MASK)
-#define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \
- PDR0_##name##_PODF_SHIFT)
+#define CKIL_FREQ 32768 /* nominal 32khz clock */
/* PLL control registers */
#define PD(v) (((v) >> 26) & 0xf)
@@ -53,39 +27,44 @@
#define PLL_MFN(x) (((x) & 0x3ff) << 0)
#define TYPE_IMX_CCM "imx.ccm"
-#define IMX_CCM(obj) OBJECT_CHECK(IMXCCMState, (obj), TYPE_IMX_CCM)
+#define IMX_CCM(obj) \
+ OBJECT_CHECK(IMXCCMState, (obj), TYPE_IMX_CCM)
+#define IMX_CCM_CLASS(klass) \
+ OBJECT_CLASS_CHECK(IMXCCMClass, (klass), TYPE_IMX_CCM)
+#define IMX_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(IMXCCMClass, (obj), TYPE_IMX_CCM)
typedef struct IMXCCMState {
/* <private> */
SysBusDevice parent_obj;
/* <public> */
- MemoryRegion iomem;
- uint32_t ccmr;
- uint32_t pdr0;
- uint32_t pdr1;
- uint32_t mpctl;
- uint32_t spctl;
- uint32_t cgr[3];
- uint32_t pmcr0;
- uint32_t pmcr1;
-
- /* Frequencies precalculated on register changes */
- uint32_t pll_refclk_freq;
- uint32_t mcu_clk_freq;
- uint32_t hsp_clk_freq;
- uint32_t ipg_clk_freq;
} IMXCCMState;
typedef enum {
NOCLK,
- MCU,
- HSP,
- IPG,
+ CLK_MPLL,
+ CLK_UPLL,
+ CLK_MCU,
+ CLK_HSP,
+ CLK_MAX,
+ CLK_AHB,
+ CLK_IPG,
+ CLK_PER,
CLK_32k
} IMXClk;
-uint32_t imx_clock_frequency(DeviceState *s, IMXClk clock);
+typedef struct IMXCCMClass {
+ /* <private> */
+ SysBusDeviceClass parent_class;
+
+ /* <public> */
+ uint32_t (*get_clock_frequency)(IMXCCMState *s, IMXClk clk);
+} IMXCCMClass;
+
+uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq);
+
+uint32_t imx_ccm_get_clock_frequency(IMXCCMState *s, IMXClk clock);
#endif /* IMX_CCM_H */
diff --git a/include/hw/timer/imx_epit.h b/include/hw/timer/imx_epit.h
index c5328aefe1..0730ac35e6 100644
--- a/include/hw/timer/imx_epit.h
+++ b/include/hw/timer/imx_epit.h
@@ -31,6 +31,7 @@
#include "hw/sysbus.h"
#include "hw/ptimer.h"
+#include "hw/misc/imx_ccm.h"
/*
* EPIT: Enhanced periodic interrupt timer
@@ -63,8 +64,8 @@ typedef struct IMXEPITState{
/*< public >*/
ptimer_state *timer_reload;
ptimer_state *timer_cmp;
- MemoryRegion iomem;
- DeviceState *ccm;
+ MemoryRegion iomem;
+ IMXCCMState *ccm;
uint32_t cr;
uint32_t sr;
diff --git a/include/hw/timer/imx_gpt.h b/include/hw/timer/imx_gpt.h
index 3f02d3b337..461adbe53f 100644
--- a/include/hw/timer/imx_gpt.h
+++ b/include/hw/timer/imx_gpt.h
@@ -31,6 +31,7 @@
#include "hw/sysbus.h"
#include "hw/ptimer.h"
+#include "hw/misc/imx_ccm.h"
/*
* GPT : General purpose timer
@@ -82,8 +83,8 @@ typedef struct IMXGPTState{
/*< public >*/
ptimer_state *timer;
- MemoryRegion iomem;
- DeviceState *ccm;
+ MemoryRegion iomem;
+ IMXCCMState *ccm;
uint32_t cr;
uint32_t pr;
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 30739fc0df..35a1f12661 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -1417,6 +1417,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
cc->handle_mmu_fault = arm_cpu_handle_mmu_fault;
#else
cc->do_interrupt = arm_cpu_do_interrupt;
+ cc->do_unaligned_access = arm_cpu_do_unaligned_access;
cc->get_phys_page_debug = arm_cpu_get_phys_page_debug;
cc->vmsd = &vmstate_arm_cpu;
cc->virtio_is_big_endian = arm_cpu_is_big_endian;
diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c
index deb8dbe489..fc3ccdf2a7 100644
--- a/target-arm/helper-a64.c
+++ b/target-arm/helper-a64.c
@@ -25,6 +25,7 @@
#include "qemu/bitops.h"
#include "internals.h"
#include "qemu/crc32c.h"
+#include "sysemu/kvm.h"
#include <zlib.h> /* For crc32 */
/* C2.4.7 Multiply and divide */
@@ -469,7 +470,8 @@ void aarch64_cpu_do_interrupt(CPUState *cs)
new_el);
if (qemu_loglevel_mask(CPU_LOG_INT)
&& !excp_is_internal(cs->exception_index)) {
- qemu_log_mask(CPU_LOG_INT, "...with ESR 0x%" PRIx32 "\n",
+ qemu_log_mask(CPU_LOG_INT, "...with ESR %x/0x%" PRIx32 "\n",
+ env->exception.syndrome >> ARM_EL_EC_SHIFT,
env->exception.syndrome);
}
@@ -535,6 +537,12 @@ void aarch64_cpu_do_interrupt(CPUState *cs)
aarch64_restore_sp(env, new_el);
env->pc = addr;
- cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
+
+ qemu_log_mask(CPU_LOG_INT, "...to EL%d PC 0x%" PRIx64 " PSTATE 0x%x\n",
+ new_el, env->pc, pstate_read(env));
+
+ if (!kvm_enabled()) {
+ cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
+ }
}
#endif
diff --git a/target-arm/helper.c b/target-arm/helper.c
index afc4163342..59d5a41b58 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -5996,6 +5996,14 @@ static inline bool regime_using_lpae_format(CPUARMState *env,
return false;
}
+/* Returns true if the translation regime is using LPAE format page tables.
+ * Used when raising alignment exceptions, whose FSR changes depending on
+ * whether the long or short descriptor format is in use. */
+bool arm_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx)
+{
+ return regime_using_lpae_format(env, mmu_idx);
+}
+
static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx)
{
switch (mmu_idx) {
diff --git a/target-arm/internals.h b/target-arm/internals.h
index 347998c8be..b925aaaa45 100644
--- a/target-arm/internals.h
+++ b/target-arm/internals.h
@@ -441,4 +441,11 @@ struct ARMMMUFaultInfo {
bool arm_tlb_fill(CPUState *cpu, vaddr address, int rw, int mmu_idx,
uint32_t *fsr, ARMMMUFaultInfo *fi);
+/* Return true if the translation regime is using LPAE format page tables */
+bool arm_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx);
+
+/* Raise a data fault alignment exception for the specified virtual address */
+void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write,
+ int is_user, uintptr_t retaddr);
+
#endif
diff --git a/target-arm/kvm.c b/target-arm/kvm.c
index 79ef4c61de..84974bbd32 100644
--- a/target-arm/kvm.c
+++ b/target-arm/kvm.c
@@ -17,6 +17,7 @@
#include "qemu-common.h"
#include "qemu/timer.h"
+#include "qemu/error-report.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
@@ -516,9 +517,23 @@ MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run)
return MEMTXATTRS_UNSPECIFIED;
}
+
int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
{
- return 0;
+ int ret = 0;
+
+ switch (run->exit_reason) {
+ case KVM_EXIT_DEBUG:
+ if (kvm_arm_handle_debug(cs, &run->debug.arch)) {
+ ret = EXCP_DEBUG;
+ } /* otherwise return to guest */
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n",
+ __func__, run->exit_reason);
+ break;
+ }
+ return ret;
}
bool kvm_arch_stop_on_emulation_error(CPUState *cs)
@@ -541,42 +556,22 @@ int kvm_arch_on_sigbus(int code, void *addr)
return 1;
}
+/* The #ifdef protections are until 32bit headers are imported and can
+ * be removed once both 32 and 64 bit reach feature parity.
+ */
void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg)
{
- qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
-}
-
-int kvm_arch_insert_sw_breakpoint(CPUState *cs,
- struct kvm_sw_breakpoint *bp)
-{
- qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
- return -EINVAL;
-}
-
-int kvm_arch_insert_hw_breakpoint(target_ulong addr,
- target_ulong len, int type)
-{
- qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
- return -EINVAL;
-}
-
-int kvm_arch_remove_hw_breakpoint(target_ulong addr,
- target_ulong len, int type)
-{
- qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
- return -EINVAL;
-}
-
-int kvm_arch_remove_sw_breakpoint(CPUState *cs,
- struct kvm_sw_breakpoint *bp)
-{
- qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
- return -EINVAL;
-}
-
-void kvm_arch_remove_all_hw_breakpoints(void)
-{
- qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
+#ifdef KVM_GUESTDBG_USE_SW_BP
+ if (kvm_sw_breakpoints_active(cs)) {
+ dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
+ }
+#endif
+#ifdef KVM_GUESTDBG_USE_HW
+ if (kvm_arm_hw_debug_active(cs)) {
+ dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW;
+ kvm_arm_copy_hw_debug_data(&dbg->arch);
+ }
+#endif
}
void kvm_arch_init_irq_routing(KVMState *s)
diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c
index df1e2b0ebf..ff83ce6757 100644
--- a/target-arm/kvm32.c
+++ b/target-arm/kvm32.c
@@ -475,3 +475,50 @@ int kvm_arch_get_registers(CPUState *cs)
return 0;
}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: guest debug not yet implemented\n", __func__);
+ return -EINVAL;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: guest debug not yet implemented\n", __func__);
+ return -EINVAL;
+}
+
+bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: guest debug not yet implemented\n", __func__);
+ return false;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
+ return -EINVAL;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
+ return -EINVAL;
+}
+
+void kvm_arch_remove_all_hw_breakpoints(void)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
+}
+
+void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
+}
+
+bool kvm_arm_hw_debug_active(CPUState *cs)
+{
+ return false;
+}
diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c
index ceebfeb774..bb9531f33c 100644
--- a/target-arm/kvm64.c
+++ b/target-arm/kvm64.c
@@ -2,6 +2,7 @@
* ARM implementation of KVM hooks, 64 bit specific code
*
* Copyright Mian-M. Hamayun 2013, Virtual Open Systems
+ * Copyright Alex Bennée 2014, Linaro
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
@@ -12,12 +13,17 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <linux/elf.h>
#include <linux/kvm.h>
#include "config-host.h"
#include "qemu-common.h"
#include "qemu/timer.h"
+#include "qemu/error-report.h"
+#include "qemu/host-utils.h"
+#include "exec/gdbstub.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
@@ -25,6 +31,360 @@
#include "internals.h"
#include "hw/arm/arm.h"
+static bool have_guest_debug;
+
+/*
+ * Although the ARM implementation of hardware assisted debugging
+ * allows for different breakpoints per-core, the current GDB
+ * interface treats them as a global pool of registers (which seems to
+ * be the case for x86, ppc and s390). As a result we store one copy
+ * of registers which is used for all active cores.
+ *
+ * Write access is serialised by virtue of the GDB protocol which
+ * updates things. Read access (i.e. when the values are copied to the
+ * vCPU) is also gated by GDB's run control.
+ *
+ * This is not unreasonable as most of the time debugging kernels you
+ * never know which core will eventually execute your function.
+ */
+
+typedef struct {
+ uint64_t bcr;
+ uint64_t bvr;
+} HWBreakpoint;
+
+/* The watchpoint registers can cover more area than the requested
+ * watchpoint so we need to store the additional information
+ * somewhere. We also need to supply a CPUWatchpoint to the GDB stub
+ * when the watchpoint is hit.
+ */
+typedef struct {
+ uint64_t wcr;
+ uint64_t wvr;
+ CPUWatchpoint details;
+} HWWatchpoint;
+
+/* Maximum and current break/watch point counts */
+int max_hw_bps, max_hw_wps;
+GArray *hw_breakpoints, *hw_watchpoints;
+
+#define cur_hw_wps (hw_watchpoints->len)
+#define cur_hw_bps (hw_breakpoints->len)
+#define get_hw_bp(i) (&g_array_index(hw_breakpoints, HWBreakpoint, i))
+#define get_hw_wp(i) (&g_array_index(hw_watchpoints, HWWatchpoint, i))
+
+/**
+ * kvm_arm_init_debug() - check for guest debug capabilities
+ * @cs: CPUState
+ *
+ * kvm_check_extension returns the number of debug registers we have
+ * or 0 if we have none.
+ *
+ */
+static void kvm_arm_init_debug(CPUState *cs)
+{
+ have_guest_debug = kvm_check_extension(cs->kvm_state,
+ KVM_CAP_SET_GUEST_DEBUG);
+
+ max_hw_wps = kvm_check_extension(cs->kvm_state, KVM_CAP_GUEST_DEBUG_HW_WPS);
+ hw_watchpoints = g_array_sized_new(true, true,
+ sizeof(HWWatchpoint), max_hw_wps);
+
+ max_hw_bps = kvm_check_extension(cs->kvm_state, KVM_CAP_GUEST_DEBUG_HW_BPS);
+ hw_breakpoints = g_array_sized_new(true, true,
+ sizeof(HWBreakpoint), max_hw_bps);
+ return;
+}
+
+/**
+ * insert_hw_breakpoint()
+ * @addr: address of breakpoint
+ *
+ * See ARM ARM D2.9.1 for details but here we are only going to create
+ * simple un-linked breakpoints (i.e. we don't chain breakpoints
+ * together to match address and context or vmid). The hardware is
+ * capable of fancier matching but that will require exposing that
+ * fanciness to GDB's interface
+ *
+ * D7.3.2 DBGBCR<n>_EL1, Debug Breakpoint Control Registers
+ *
+ * 31 24 23 20 19 16 15 14 13 12 9 8 5 4 3 2 1 0
+ * +------+------+-------+-----+----+------+-----+------+-----+---+
+ * | RES0 | BT | LBN | SSC | HMC| RES0 | BAS | RES0 | PMC | E |
+ * +------+------+-------+-----+----+------+-----+------+-----+---+
+ *
+ * BT: Breakpoint type (0 = unlinked address match)
+ * LBN: Linked BP number (0 = unused)
+ * SSC/HMC/PMC: Security, Higher and Priv access control (Table D-12)
+ * BAS: Byte Address Select (RES1 for AArch64)
+ * E: Enable bit
+ */
+static int insert_hw_breakpoint(target_ulong addr)
+{
+ HWBreakpoint brk = {
+ .bcr = 0x1, /* BCR E=1, enable */
+ .bvr = addr
+ };
+
+ if (cur_hw_bps >= max_hw_bps) {
+ return -ENOBUFS;
+ }
+
+ brk.bcr = deposit32(brk.bcr, 1, 2, 0x3); /* PMC = 11 */
+ brk.bcr = deposit32(brk.bcr, 5, 4, 0xf); /* BAS = RES1 */
+
+ g_array_append_val(hw_breakpoints, brk);
+
+ return 0;
+}
+
+/**
+ * delete_hw_breakpoint()
+ * @pc: address of breakpoint
+ *
+ * Delete a breakpoint and shuffle any above down
+ */
+
+static int delete_hw_breakpoint(target_ulong pc)
+{
+ int i;
+ for (i = 0; i < hw_breakpoints->len; i++) {
+ HWBreakpoint *brk = get_hw_bp(i);
+ if (brk->bvr == pc) {
+ g_array_remove_index(hw_breakpoints, i);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+/**
+ * insert_hw_watchpoint()
+ * @addr: address of watch point
+ * @len: size of area
+ * @type: type of watch point
+ *
+ * See ARM ARM D2.10. As with the breakpoints we can do some advanced
+ * stuff if we want to. The watch points can be linked with the break
+ * points above to make them context aware. However for simplicity
+ * currently we only deal with simple read/write watch points.
+ *
+ * D7.3.11 DBGWCR<n>_EL1, Debug Watchpoint Control Registers
+ *
+ * 31 29 28 24 23 21 20 19 16 15 14 13 12 5 4 3 2 1 0
+ * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+
+ * | RES0 | MASK | RES0 | WT | LBN | SSC | HMC | BAS | LSC | PAC | E |
+ * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+
+ *
+ * MASK: num bits addr mask (0=none,01/10=res,11=3 bits (8 bytes))
+ * WT: 0 - unlinked, 1 - linked (not currently used)
+ * LBN: Linked BP number (not currently used)
+ * SSC/HMC/PAC: Security, Higher and Priv access control (Table D2-11)
+ * BAS: Byte Address Select
+ * LSC: Load/Store control (01: load, 10: store, 11: both)
+ * E: Enable
+ *
+ * The bottom 2 bits of the value register are masked. Therefore to
+ * break on any sizes smaller than an unaligned word you need to set
+ * MASK=0, BAS=bit per byte in question. For larger regions (^2) you
+ * need to ensure you mask the address as required and set BAS=0xff
+ */
+
+static int insert_hw_watchpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ HWWatchpoint wp = {
+ .wcr = 1, /* E=1, enable */
+ .wvr = addr & (~0x7ULL),
+ .details = { .vaddr = addr, .len = len }
+ };
+
+ if (cur_hw_wps >= max_hw_wps) {
+ return -ENOBUFS;
+ }
+
+ /*
+ * HMC=0 SSC=0 PAC=3 will hit EL0 or EL1, any security state,
+ * valid whether EL3 is implemented or not
+ */
+ wp.wcr = deposit32(wp.wcr, 1, 2, 3);
+
+ switch (type) {
+ case GDB_WATCHPOINT_READ:
+ wp.wcr = deposit32(wp.wcr, 3, 2, 1);
+ wp.details.flags = BP_MEM_READ;
+ break;
+ case GDB_WATCHPOINT_WRITE:
+ wp.wcr = deposit32(wp.wcr, 3, 2, 2);
+ wp.details.flags = BP_MEM_WRITE;
+ break;
+ case GDB_WATCHPOINT_ACCESS:
+ wp.wcr = deposit32(wp.wcr, 3, 2, 3);
+ wp.details.flags = BP_MEM_ACCESS;
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ if (len <= 8) {
+ /* we align the address and set the bits in BAS */
+ int off = addr & 0x7;
+ int bas = (1 << len) - 1;
+
+ wp.wcr = deposit32(wp.wcr, 5 + off, 8 - off, bas);
+ } else {
+ /* For ranges above 8 bytes we need to be a power of 2 */
+ if (is_power_of_2(len)) {
+ int bits = ctz64(len);
+
+ wp.wvr &= ~((1 << bits) - 1);
+ wp.wcr = deposit32(wp.wcr, 24, 4, bits);
+ wp.wcr = deposit32(wp.wcr, 5, 8, 0xff);
+ } else {
+ return -ENOBUFS;
+ }
+ }
+
+ g_array_append_val(hw_watchpoints, wp);
+ return 0;
+}
+
+
+static bool check_watchpoint_in_range(int i, target_ulong addr)
+{
+ HWWatchpoint *wp = get_hw_wp(i);
+ uint64_t addr_top, addr_bottom = wp->wvr;
+ int bas = extract32(wp->wcr, 5, 8);
+ int mask = extract32(wp->wcr, 24, 4);
+
+ if (mask) {
+ addr_top = addr_bottom + (1 << mask);
+ } else {
+ /* BAS must be contiguous but can offset against the base
+ * address in DBGWVR */
+ addr_bottom = addr_bottom + ctz32(bas);
+ addr_top = addr_bottom + clo32(bas);
+ }
+
+ if (addr >= addr_bottom && addr <= addr_top) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * delete_hw_watchpoint()
+ * @addr: address of breakpoint
+ *
+ * Delete a breakpoint and shuffle any above down
+ */
+
+static int delete_hw_watchpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ int i;
+ for (i = 0; i < cur_hw_wps; i++) {
+ if (check_watchpoint_in_range(i, addr)) {
+ g_array_remove_index(hw_watchpoints, i);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ switch (type) {
+ case GDB_BREAKPOINT_HW:
+ return insert_hw_breakpoint(addr);
+ break;
+ case GDB_WATCHPOINT_READ:
+ case GDB_WATCHPOINT_WRITE:
+ case GDB_WATCHPOINT_ACCESS:
+ return insert_hw_watchpoint(addr, len, type);
+ default:
+ return -ENOSYS;
+ }
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ switch (type) {
+ case GDB_BREAKPOINT_HW:
+ return delete_hw_breakpoint(addr);
+ break;
+ case GDB_WATCHPOINT_READ:
+ case GDB_WATCHPOINT_WRITE:
+ case GDB_WATCHPOINT_ACCESS:
+ return delete_hw_watchpoint(addr, len, type);
+ default:
+ return -ENOSYS;
+ }
+}
+
+
+void kvm_arch_remove_all_hw_breakpoints(void)
+{
+ if (cur_hw_wps > 0) {
+ g_array_remove_range(hw_watchpoints, 0, cur_hw_wps);
+ }
+ if (cur_hw_bps > 0) {
+ g_array_remove_range(hw_breakpoints, 0, cur_hw_bps);
+ }
+}
+
+void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr)
+{
+ int i;
+ memset(ptr, 0, sizeof(struct kvm_guest_debug_arch));
+
+ for (i = 0; i < max_hw_wps; i++) {
+ HWWatchpoint *wp = get_hw_wp(i);
+ ptr->dbg_wcr[i] = wp->wcr;
+ ptr->dbg_wvr[i] = wp->wvr;
+ }
+ for (i = 0; i < max_hw_bps; i++) {
+ HWBreakpoint *bp = get_hw_bp(i);
+ ptr->dbg_bcr[i] = bp->bcr;
+ ptr->dbg_bvr[i] = bp->bvr;
+ }
+}
+
+bool kvm_arm_hw_debug_active(CPUState *cs)
+{
+ return ((cur_hw_wps > 0) || (cur_hw_bps > 0));
+}
+
+static bool find_hw_breakpoint(CPUState *cpu, target_ulong pc)
+{
+ int i;
+
+ for (i = 0; i < cur_hw_bps; i++) {
+ HWBreakpoint *bp = get_hw_bp(i);
+ if (bp->bvr == pc) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr)
+{
+ int i;
+
+ for (i = 0; i < cur_hw_wps; i++) {
+ if (check_watchpoint_in_range(i, addr)) {
+ return &get_hw_wp(i)->details;
+ }
+ }
+ return NULL;
+}
+
+
static inline void set_feature(uint64_t *features, int feature)
{
*features |= 1ULL << feature;
@@ -121,6 +481,8 @@ int kvm_arch_init_vcpu(CPUState *cs)
}
cpu->mp_affinity = mpidr & ARM64_AFFINITY_MASK;
+ kvm_arm_init_debug(cs);
+
return kvm_arm_init_cpreg_list(cpu);
}
@@ -463,3 +825,105 @@ int kvm_arch_get_registers(CPUState *cs)
/* TODO: other registers */
return ret;
}
+
+/* C6.6.29 BRK instruction */
+static const uint32_t brk_insn = 0xd4200000;
+
+int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
+{
+ if (have_guest_debug) {
+ if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) ||
+ cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk_insn, 4, 1)) {
+ return -EINVAL;
+ }
+ return 0;
+ } else {
+ error_report("guest debug not supported on this kernel");
+ return -EINVAL;
+ }
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
+{
+ static uint32_t brk;
+
+ if (have_guest_debug) {
+ if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk, 4, 0) ||
+ brk != brk_insn ||
+ cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) {
+ return -EINVAL;
+ }
+ return 0;
+ } else {
+ error_report("guest debug not supported on this kernel");
+ return -EINVAL;
+ }
+}
+
+/* See v8 ARM ARM D7.2.27 ESR_ELx, Exception Syndrome Register
+ *
+ * To minimise translating between kernel and user-space the kernel
+ * ABI just provides user-space with the full exception syndrome
+ * register value to be decoded in QEMU.
+ */
+
+bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit)
+{
+ int hsr_ec = debug_exit->hsr >> ARM_EL_EC_SHIFT;
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUClass *cc = CPU_GET_CLASS(cs);
+ CPUARMState *env = &cpu->env;
+
+ /* Ensure PC is synchronised */
+ kvm_cpu_synchronize_state(cs);
+
+ switch (hsr_ec) {
+ case EC_SOFTWARESTEP:
+ if (cs->singlestep_enabled) {
+ return true;
+ } else {
+ /*
+ * The kernel should have suppressed the guest's ability to
+ * single step at this point so something has gone wrong.
+ */
+ error_report("%s: guest single-step while debugging unsupported"
+ " (%"PRIx64", %"PRIx32")\n",
+ __func__, env->pc, debug_exit->hsr);
+ return false;
+ }
+ break;
+ case EC_AA64_BKPT:
+ if (kvm_find_sw_breakpoint(cs, env->pc)) {
+ return true;
+ }
+ break;
+ case EC_BREAKPOINT:
+ if (find_hw_breakpoint(cs, env->pc)) {
+ return true;
+ }
+ break;
+ case EC_WATCHPOINT:
+ {
+ CPUWatchpoint *wp = find_hw_watchpoint(cs, debug_exit->far);
+ if (wp) {
+ cs->watchpoint_hit = wp;
+ return true;
+ }
+ break;
+ }
+ default:
+ error_report("%s: unhandled debug exit (%"PRIx32", %"PRIx64")\n",
+ __func__, debug_exit->hsr, env->pc);
+ }
+
+ /* If we are not handling the debug exception it must belong to
+ * the guest. Let's re-use the existing TCG interrupt code to set
+ * everything up properly.
+ */
+ cs->exception_index = EXCP_BKPT;
+ env->exception.syndrome = debug_exit->hsr;
+ env->exception.vaddress = debug_exit->far;
+ cc->do_interrupt(cs);
+
+ return false;
+}
diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h
index b516041779..07f0c72199 100644
--- a/target-arm/kvm_arm.h
+++ b/target-arm/kvm_arm.h
@@ -215,4 +215,34 @@ static inline const char *gic_class_name(void)
*/
const char *gicv3_class_name(void);
+/**
+ * kvm_arm_handle_debug:
+ * @cs: CPUState
+ * @debug_exit: debug part of the KVM exit structure
+ *
+ * Returns: TRUE if the debug exception was handled.
+ */
+bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit);
+
+/**
+ * kvm_arm_hw_debug_active:
+ * @cs: CPU State
+ *
+ * Return: TRUE if any hardware breakpoints in use.
+ */
+
+bool kvm_arm_hw_debug_active(CPUState *cs);
+
+/**
+ * kvm_arm_copy_hw_debug_data:
+ *
+ * @ptr: kvm_guest_debug_arch structure
+ *
+ * Copy the architecture specific debug registers into the
+ * kvm_guest_debug ioctl structure.
+ */
+struct kvm_guest_debug_arch;
+
+void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr);
+
#endif
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index 6cd54c8f7a..e42d287d9c 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -126,7 +126,45 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
raise_exception(env, exc, syn, target_el);
}
}
-#endif
+
+/* Raise a data fault alignment exception for the specified virtual address */
+void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write,
+ int is_user, uintptr_t retaddr)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+ int target_el;
+ bool same_el;
+
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ cpu_restore_state(cs, retaddr);
+ }
+
+ target_el = exception_target_el(env);
+ same_el = (arm_current_el(env) == target_el);
+
+ env->exception.vaddress = vaddr;
+
+ /* the DFSR for an alignment fault depends on whether we're using
+ * the LPAE long descriptor format, or the short descriptor format
+ */
+ if (arm_regime_using_lpae_format(env, cpu_mmu_index(env, false))) {
+ env->exception.fsr = 0x21;
+ } else {
+ env->exception.fsr = 0x1;
+ }
+
+ if (is_write == 1 && arm_feature(env, ARM_FEATURE_V6)) {
+ env->exception.fsr |= (1 << 11);
+ }
+
+ raise_exception(env, EXCP_DATA_ABORT,
+ syn_data_abort(same_el, 0, 0, 0, is_write == 1, 0x21),
+ target_el);
+}
+
+#endif /* !defined(CONFIG_USER_ONLY) */
uint32_t HELPER(add_setq)(CPUARMState *env, uint32_t a, uint32_t b)
{
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 5d22879755..d485e7d9c3 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -926,13 +926,13 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var)
#define DO_GEN_LD(SUFF, OPC) \
static inline void gen_aa32_ld##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \
{ \
- tcg_gen_qemu_ld_i32(val, addr, index, OPC); \
+ tcg_gen_qemu_ld_i32(val, addr, index, (OPC)); \
}
#define DO_GEN_ST(SUFF, OPC) \
static inline void gen_aa32_st##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \
{ \
- tcg_gen_qemu_st_i32(val, addr, index, OPC); \
+ tcg_gen_qemu_st_i32(val, addr, index, (OPC)); \
}
static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index)
@@ -988,6 +988,9 @@ DO_GEN_LD(8u, MO_UB)
DO_GEN_LD(16s, MO_TESW)
DO_GEN_LD(16u, MO_TEUW)
DO_GEN_LD(32u, MO_TEUL)
+/* 'a' variants include an alignment check */
+DO_GEN_LD(16ua, MO_TEUW | MO_ALIGN)
+DO_GEN_LD(32ua, MO_TEUL | MO_ALIGN)
DO_GEN_ST(8, MO_UB)
DO_GEN_ST(16, MO_TEUW)
DO_GEN_ST(32, MO_TEUL)
@@ -7435,11 +7438,11 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2,
gen_aa32_ld8u(tmp, addr, get_mem_index(s));
break;
case 1:
- gen_aa32_ld16u(tmp, addr, get_mem_index(s));
+ gen_aa32_ld16ua(tmp, addr, get_mem_index(s));
break;
case 2:
case 3:
- gen_aa32_ld32u(tmp, addr, get_mem_index(s));
+ gen_aa32_ld32ua(tmp, addr, get_mem_index(s));
break;
default:
abort();
@@ -11480,48 +11483,45 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb)
instruction was a conditional branch or trap, and the PC has
already been written. */
if (unlikely(cs->singlestep_enabled || dc->ss_active)) {
- /* Make sure the pc is updated, and raise a debug exception. */
- if (dc->condjmp) {
- gen_set_condexec(dc);
- if (dc->is_jmp == DISAS_SWI) {
- gen_ss_advance(dc);
- gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb),
- default_exception_el(dc));
- } else if (dc->is_jmp == DISAS_HVC) {
- gen_ss_advance(dc);
- gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2);
- } else if (dc->is_jmp == DISAS_SMC) {
- gen_ss_advance(dc);
- gen_exception(EXCP_SMC, syn_aa32_smc(), 3);
- } else if (dc->ss_active) {
- gen_step_complete_exception(dc);
- } else {
- gen_exception_internal(EXCP_DEBUG);
- }
- gen_set_label(dc->condlabel);
- }
- if (dc->condjmp || dc->is_jmp == DISAS_NEXT ||
- dc->is_jmp == DISAS_UPDATE) {
- gen_set_pc_im(dc, dc->pc);
- dc->condjmp = 0;
- }
+ /* Unconditional and "condition passed" instruction codepath. */
gen_set_condexec(dc);
- if (dc->is_jmp == DISAS_SWI && !dc->condjmp) {
+ switch (dc->is_jmp) {
+ case DISAS_SWI:
gen_ss_advance(dc);
gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb),
default_exception_el(dc));
- } else if (dc->is_jmp == DISAS_HVC && !dc->condjmp) {
+ break;
+ case DISAS_HVC:
gen_ss_advance(dc);
gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2);
- } else if (dc->is_jmp == DISAS_SMC && !dc->condjmp) {
+ break;
+ case DISAS_SMC:
gen_ss_advance(dc);
gen_exception(EXCP_SMC, syn_aa32_smc(), 3);
- } else if (dc->ss_active) {
- gen_step_complete_exception(dc);
- } else {
- /* FIXME: Single stepping a WFI insn will not halt
- the CPU. */
- gen_exception_internal(EXCP_DEBUG);
+ break;
+ case DISAS_NEXT:
+ case DISAS_UPDATE:
+ gen_set_pc_im(dc, dc->pc);
+ /* fall through */
+ default:
+ if (dc->ss_active) {
+ gen_step_complete_exception(dc);
+ } else {
+ /* FIXME: Single stepping a WFI insn will not halt
+ the CPU. */
+ gen_exception_internal(EXCP_DEBUG);
+ }
+ }
+ if (dc->condjmp) {
+ /* "Condition failed" instruction codepath. */
+ gen_set_label(dc->condlabel);
+ gen_set_condexec(dc);
+ gen_set_pc_im(dc, dc->pc);
+ if (dc->ss_active) {
+ gen_step_complete_exception(dc);
+ } else {
+ gen_exception_internal(EXCP_DEBUG);
+ }
}
} else {
/* While branches must always occur at the end of an IT block,
diff --git a/tests/guest-debug/test-gdbstub.py b/tests/guest-debug/test-gdbstub.py
new file mode 100644
index 0000000000..31ba6c943a
--- /dev/null
+++ b/tests/guest-debug/test-gdbstub.py
@@ -0,0 +1,176 @@
+#
+# This script needs to be run on startup
+# qemu -kernel ${KERNEL} -s -S
+# and then:
+# gdb ${KERNEL}.vmlinux -x ${QEMU_SRC}/tests/guest-debug/test-gdbstub.py
+
+import gdb
+
+failcount = 0
+
+
+def report(cond, msg):
+ "Report success/fail of test"
+ if cond:
+ print ("PASS: %s" % (msg))
+ else:
+ print ("FAIL: %s" % (msg))
+ failcount += 1
+
+
+def check_step():
+ "Step an instruction, check it moved."
+ start_pc = gdb.parse_and_eval('$pc')
+ gdb.execute("si")
+ end_pc = gdb.parse_and_eval('$pc')
+
+ return not (start_pc == end_pc)
+
+
+def check_break(sym_name):
+ "Setup breakpoint, continue and check we stopped."
+ sym, ok = gdb.lookup_symbol(sym_name)
+ bp = gdb.Breakpoint(sym_name)
+
+ gdb.execute("c")
+
+ # hopefully we came back
+ end_pc = gdb.parse_and_eval('$pc')
+ print ("%s == %s %d" % (end_pc, sym.value(), bp.hit_count))
+ bp.delete()
+
+ # can we test we hit bp?
+ return end_pc == sym.value()
+
+
+# We need to do hbreak manually as the python interface doesn't export it
+def check_hbreak(sym_name):
+ "Setup hardware breakpoint, continue and check we stopped."
+ sym, ok = gdb.lookup_symbol(sym_name)
+ gdb.execute("hbreak %s" % (sym_name))
+ gdb.execute("c")
+
+ # hopefully we came back
+ end_pc = gdb.parse_and_eval('$pc')
+ print ("%s == %s" % (end_pc, sym.value()))
+
+ if end_pc == sym.value():
+ gdb.execute("d 1")
+ return True
+ else:
+ return False
+
+
+class WatchPoint(gdb.Breakpoint):
+
+ def get_wpstr(self, sym_name):
+ "Setup sym and wp_str for given symbol."
+ self.sym, ok = gdb.lookup_symbol(sym_name)
+ wp_addr = gdb.parse_and_eval(sym_name).address
+ self.wp_str = '*(%(type)s)(&%(address)s)' % dict(
+ type = wp_addr.type, address = sym_name)
+
+ return(self.wp_str)
+
+ def __init__(self, sym_name, type):
+ wp_str = self.get_wpstr(sym_name)
+ super(WatchPoint, self).__init__(wp_str, gdb.BP_WATCHPOINT, type)
+
+ def stop(self):
+ end_pc = gdb.parse_and_eval('$pc')
+ print ("HIT WP @ %s" % (end_pc))
+ return True
+
+
+def do_one_watch(sym, wtype, text):
+
+ wp = WatchPoint(sym, wtype)
+ gdb.execute("c")
+ report_str = "%s for %s (%s)" % (text, sym, wp.sym.value())
+
+ if wp.hit_count > 0:
+ report(True, report_str)
+ wp.delete()
+ else:
+ report(False, report_str)
+
+
+def check_watches(sym_name):
+ "Watch a symbol for any access."
+
+ # Should hit for any read
+ do_one_watch(sym_name, gdb.WP_ACCESS, "awatch")
+
+ # Again should hit for reads
+ do_one_watch(sym_name, gdb.WP_READ, "rwatch")
+
+ # Finally when it is written
+ do_one_watch(sym_name, gdb.WP_WRITE, "watch")
+
+
+class CatchBreakpoint(gdb.Breakpoint):
+ def __init__(self, sym_name):
+ super(CatchBreakpoint, self).__init__(sym_name)
+ self.sym, ok = gdb.lookup_symbol(sym_name)
+
+ def stop(self):
+ end_pc = gdb.parse_and_eval('$pc')
+ print ("CB: %s == %s" % (end_pc, self.sym.value()))
+ if end_pc == self.sym.value():
+ report(False, "Hit final catchpoint")
+
+
+def run_test():
+ "Run throught the tests one by one"
+
+ print ("Checking we can step the first few instructions")
+ step_ok = 0
+ for i in range(3):
+ if check_step():
+ step_ok += 1
+
+ report(step_ok == 3, "single step in boot code")
+
+ print ("Checking HW breakpoint works")
+ break_ok = check_hbreak("kernel_init")
+ report(break_ok, "hbreak @ kernel_init")
+
+ # Can't set this up until we are in the kernel proper
+ # if we make it to run_init_process we've over-run and
+ # one of the tests failed
+ print ("Setup catch-all for run_init_process")
+ cbp = CatchBreakpoint("run_init_process")
+ cpb2 = CatchBreakpoint("try_to_run_init_process")
+
+ print ("Checking Normal breakpoint works")
+ break_ok = check_break("wait_for_completion")
+ report(break_ok, "break @ wait_for_completion")
+
+ print ("Checking watchpoint works")
+ check_watches("system_state")
+
+#
+# This runs as the script it sourced (via -x)
+#
+
+try:
+ print ("Connecting to remote")
+ gdb.execute("target remote localhost:1234")
+
+ # These are not very useful in scripts
+ gdb.execute("set pagination off")
+ gdb.execute("set confirm off")
+
+ # Run the actual tests
+ run_test()
+
+except:
+ print ("GDB Exception: %s" % (sys.exc_info()[0]))
+ failcount += 1
+ import code
+ code.InteractiveConsole(locals=globals()).interact()
+ raise
+
+# Finally kill the inferior and exit gdb with a count of failures
+gdb.execute("kill")
+exit(failcount)