mirror of https://github.com/xemu-project/xemu.git
target-arm queue:
* i.MX CCM patches * support guest debug for AArch64 KVM * support power button on virt board via GPIO * clean up AArch32 singlestep code * raise exception on misaligned LDREX operands * soc-dma: use hwaddr instead of target_ulong in printf * explicitly mark some ARM device loads as little-endian * i.MX: add support for lower and upper interrupt in GPIO -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJWcrrBAAoJEDwlJe0UNgzeSZIP/1agio7CJiIAG+wW2OMJclcL 6bsHWwcLRxhDYZZcHraEEJY2Zn/iJdhoZcI5aRlmXz0zcrKZ0zNb/9YPGgbnohyk X01YpWdEoeyowDx1ThbS/Tug5qpLk0evQfSUr1dPmGrz6pyBRKyhUNc2AjDWiUJ6 qNFc++rLDv59p7V8AshfplRwbC6ARIeQCn20dbtgqkNNC/fHVf4BeXgNbjfuzHxp jE8rj+CR6Hcv0t0ddGWF7a3da1w1n8FsuuNnPRzHVzL3JvXLHsrQx1fn3CTFZP9V ZSBy+Lh0zel77oeFSZNVePSVe5WHVy8nsNYnjjJSfmOIR6dM98wWUfxoswxtuUHt mqzj+1qwp7e+HjHMVLfLp0DeC66Rzz/fzMUlS4CyJRN0JAij5WOucxgTBTWmosln XbObusAh/lrQsLLTyF1evf3iBwARcpXdLTy5IoMzOdwnO25Z1fsJpaBLK+JroABt 0jL8mvGtRDExkbZyByV/KBuXxF47uIfdFze8DdgIEm+RWcqEZZghs/RR123+CqF9 Sdszju8NMsZas5wBmvZUfLO5lQkAxaQn5z8KsPfaMgoUsXgiJnmwjPQScGeJciQD e7tgIBH2EvcqZPcmL+q4k7tBHIyih62sqhNFw9yHC05SCFF1qTPoJXlSOcSQZoAy 7a0igayGdeEedXLfXZ1O =L4Fl -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20151217-1' into staging target-arm queue: * i.MX CCM patches * support guest debug for AArch64 KVM * support power button on virt board via GPIO * clean up AArch32 singlestep code * raise exception on misaligned LDREX operands * soc-dma: use hwaddr instead of target_ulong in printf * explicitly mark some ARM device loads as little-endian * i.MX: add support for lower and upper interrupt in GPIO # gpg: Signature made Thu 17 Dec 2015 13:38:09 GMT using RSA key ID 14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" * remotes/pmaydell/tags/pull-target-arm-20151217-1: (25 commits) i.MX: Add an i.MX25 specific CCM class/instance i.MX: Split the CCM class into an abstract base class and a concrete class i.MX: rename i.MX CCM get_clock() function and CLK ID enum names i.MX: Fix i.MX31 default/reset configuration tests/guest-debug: introduce basic gdbstub tests target-arm: kvm - re-inject guest debug exceptions target-arm: kvm - add support for HW assisted debug target-arm: kvm - support for single step target-arm: kvm - implement software breakpoints target-arm: kvm64 - introduce kvm_arm_init_debug() ARM: Virt: Add gpio-keys node for Poweroff using DT ARM: Virt: Add QEMU powerdown notifier and hook it to GPIO Pin 3 ARM: ACPI: Add _E03 for Power Button ACPI: Add aml_gpio_int() wrapper for GPIO Interrupt Connection ACPI: Add GPIO Connection Descriptor ARM: ACPI: Add power button device in ACPI DSDT table ARM: ACPI: Add GPIO controller in ACPI DSDT table ARM: Virt: Add a GPIO controller acpi: extend aml_interrupt() to support multiple irqs acpi: support serialized method ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
e5fbe28e54
|
@ -564,6 +564,94 @@ Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4)
|
|||
return var;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
||||
*/
|
||||
DPRINTF("(pllreg = 0x%08x, base_freq = %d) = %d\n", pllreg, base_freq,
|
||||
freq);
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -59,6 +59,7 @@ enum {
|
|||
VIRT_PCIE_ECAM,
|
||||
VIRT_PLATFORM_BUS,
|
||||
VIRT_PCIE_MMIO_HIGH,
|
||||
VIRT_GPIO,
|
||||
};
|
||||
|
||||
typedef struct MemMapEntry {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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. */
|
||||
/* Unconditional and "condition passed" instruction codepath. */
|
||||
gen_set_condexec(dc);
|
||||
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));
|
||||
break;
|
||||
case DISAS_HVC:
|
||||
gen_ss_advance(dc);
|
||||
gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2);
|
||||
break;
|
||||
case DISAS_SMC:
|
||||
gen_ss_advance(dc);
|
||||
gen_exception(EXCP_SMC, syn_aa32_smc(), 3);
|
||||
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);
|
||||
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_set_pc_im(dc, dc->pc);
|
||||
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;
|
||||
}
|
||||
gen_set_condexec(dc);
|
||||
if (dc->is_jmp == DISAS_SWI && !dc->condjmp) {
|
||||
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) {
|
||||
gen_ss_advance(dc);
|
||||
gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2);
|
||||
} else if (dc->is_jmp == DISAS_SMC && !dc->condjmp) {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
/* While branches must always occur at the end of an IT block,
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue