提交 e5fbe28e 编写于 作者: P Peter Maydell

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: NPeter Maydell <peter.maydell@linaro.org>
......@@ -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
......
/*
* 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)
/*
* 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
*/
if ((s->ccmr & CCMR_PRCS) == 2) {
s->pll_refclk_freq = CKIL_FREQ * 1024;
} else {
s->pll_refclk_freq = CKIH_FREQ;
}
/* ipg_clk_arm aka MCU clock */
if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
s->mcu_clk_freq = s->pll_refclk_freq;
} else {
s->mcu_clk_freq = calc_pll(s->mpctl, s->pll_refclk_freq);
}
/* High-speed clock */
s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP));
s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG));
DPRINTF("mcu %uMHz, HSP %uMHz, IPG %uHz\n",
s->mcu_clk_freq / 1000000,
s->hsp_clk_freq / 1000000,
s->ipg_clk_freq);
}
static void imx_ccm_reset(DeviceState *dev)
{
IMXCCMState *s = IMX_CCM(dev);
s->ccmr = 0x074b0b7b;
s->pdr0 = 0xff870b48;
s->pdr1 = 0x49fcfe7f;
s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0);
s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
s->spctl = PLL_PD(1) | PLL_MFD(4) | PLL_MFI(0xc) | PLL_MFN(1);
s->pmcr0 = 0x80209828;
update_clocks(s);
}
static uint64_t imx_ccm_read(void *opaque, hwaddr offset,
unsigned size)
{
IMXCCMState *s = (IMXCCMState *)opaque;
DPRINTF("(offset=0x%" HWADDR_PRIx ")\n", offset);
switch (offset >> 2) {
case 0: /* CCMR */
DPRINTF(" ccmr = 0x%x\n", s->ccmr);
return s->ccmr;
case 1:
DPRINTF(" pdr0 = 0x%x\n", s->pdr0);
return s->pdr0;
case 2:
DPRINTF(" pdr1 = 0x%x\n", s->pdr1);
return s->pdr1;
case 4:
DPRINTF(" mpctl = 0x%x\n", s->mpctl);
return s->mpctl;
case 6:
DPRINTF(" spctl = 0x%x\n", s->spctl);
return s->spctl;
case 8:
DPRINTF(" cgr0 = 0x%x\n", s->cgr[0]);
return s->cgr[0];
case 9:
DPRINTF(" cgr1 = 0x%x\n", s->cgr[1]);
return s->cgr[1];
case 10:
DPRINTF(" cgr2 = 0x%x\n", s->cgr[2]);
return s->cgr[2];
case 18: /* LTR1 */
return 0x00004040;
case 23:
DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0);
return s->pmcr0;
default:
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset);
return 0;
}
}
static void imx_ccm_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
IMXCCMState *s = (IMXCCMState *)opaque;
DPRINTF("(offset=0x%" HWADDR_PRIx ", value = 0x%x)\n",
offset, (unsigned int)value);
switch (offset >> 2) {
case 0:
s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
break;
case 1:
s->pdr0 = value & 0xff9f3fff;
break;
case 2:
s->pdr1 = value;
break;
case 4:
s->mpctl = value & 0xbfff3fff;
break;
case 6:
s->spctl = value & 0xbfff3fff;
break;
case 8:
s->cgr[0] = value;
return;
case 9:
s->cgr[1] = value;
return;
case 10:
s->cgr[2] = value;
return;
default:
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset);
return;
}
update_clocks(s);
}
static const struct MemoryRegionOps imx_ccm_ops = {
.read = imx_ccm_read,
.write = imx_ccm_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static int imx_ccm_init(SysBusDevice *dev)
{
IMXCCMState *s = IMX_CCM(dev);
memory_region_init_io(&s->iomem, OBJECT(dev), &imx_ccm_ops, s,
TYPE_IMX_CCM, 0x1000);
sysbus_init_mmio(dev, &s->iomem);
return 0;
}
static int imx_ccm_post_load(void *opaque, int version_id)
{
IMXCCMState *s = (IMXCCMState *)opaque;
update_clocks(s);
return 0;
}
static void imx_ccm_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
DPRINTF("(pllreg = 0x%08x, base_freq = %d) = %d\n", pllreg, base_freq,
freq);
sbc->init = imx_ccm_init;
dc->reset = imx_ccm_reset;
dc->vmsd = &vmstate_imx_ccm;
dc->desc = "i.MX Clock Control Module";
return freq;
}
static const TypeInfo imx_ccm_info = {
.name = TYPE_IMX_CCM,
.parent = TYPE_SYS_BUS_DEVICE,
.name = TYPE_IMX_CCM,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IMXCCMState),
.class_init = imx_ccm_class_init,
.class_size = sizeof(IMXCCMClass),
.abstract = true,
};
static void imx_ccm_register_types(void)
......
......@@ -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;
......
/*
* 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 */
/*
* 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 */
/*
* 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. */
if (dc->condjmp) {
gen_set_condexec(dc);
if (dc->is_jmp == DISAS_SWI) {
gen_ss_advance(dc);
gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb),
default_exception_el(dc));
} else if (dc->is_jmp == DISAS_HVC) {
gen_ss_advance(dc);
gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2);
} else if (dc->is_jmp == DISAS_SMC) {
gen_ss_advance(dc);
gen_exception(EXCP_SMC, syn_aa32_smc(), 3);
} else if (dc->ss_active) {
gen_step_complete_exception(dc);
} else {
gen_exception_internal(EXCP_DEBUG);
}
gen_set_label(dc->condlabel);
}
if (dc->condjmp || dc->is_jmp == DISAS_NEXT ||
dc->is_jmp == DISAS_UPDATE) {
gen_set_pc_im(dc, dc->pc);
dc->condjmp = 0;
}
/* Unconditional and "condition passed" instruction codepath. */
gen_set_condexec(dc);
if (dc->is_jmp == DISAS_SWI && !dc->condjmp) {
switch (dc->is_jmp) {
case DISAS_SWI:
gen_ss_advance(dc);
gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb),
default_exception_el(dc));
} else if (dc->is_jmp == DISAS_HVC && !dc->condjmp) {
break;
case DISAS_HVC:
gen_ss_advance(dc);
gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2);
} else if (dc->is_jmp == DISAS_SMC && !dc->condjmp) {
break;
case DISAS_SMC:
gen_ss_advance(dc);
gen_exception(EXCP_SMC, syn_aa32_smc(), 3);
} else if (dc->ss_active) {
gen_step_complete_exception(dc);
} else {
/* FIXME: Single stepping a WFI insn will not halt
the CPU. */
gen_exception_internal(EXCP_DEBUG);
break;
case DISAS_NEXT:
case DISAS_UPDATE:
gen_set_pc_im(dc, dc->pc);
/* fall through */
default:
if (dc->ss_active) {
gen_step_complete_exception(dc);
} else {
/* FIXME: Single stepping a WFI insn will not halt
the CPU. */
gen_exception_internal(EXCP_DEBUG);
}
}
if (dc->condjmp) {
/* "Condition failed" instruction codepath. */
gen_set_label(dc->condlabel);
gen_set_condexec(dc);
gen_set_pc_im(dc, dc->pc);
if (dc->ss_active) {
gen_step_complete_exception(dc);
} else {
gen_exception_internal(EXCP_DEBUG);
}
}
} else {
/* While branches must always occur at the end of an IT block,
......
#
# 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)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册