aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dump.c14
-rw-r--r--hw/arm/bcm2836.c87
-rw-r--r--hw/arm/boot.c12
-rw-r--r--hw/arm/raspi.c77
-rw-r--r--hw/char/imx_serial.c44
-rw-r--r--hw/net/imx_fec.c28
-rw-r--r--include/hw/arm/bcm2836.h31
-rw-r--r--include/hw/arm/fsl-imx6.h4
-rw-r--r--include/hw/char/imx_serial.h3
9 files changed, 237 insertions, 63 deletions
diff --git a/dump.c b/dump.c
index 097e60b2b3..6bdb0dbe23 100644
--- a/dump.c
+++ b/dump.c
@@ -1609,10 +1609,18 @@ static void vmcoreinfo_update_phys_base(DumpState *s)
lines = g_strsplit((char *)vmci, "\n", -1);
for (i = 0; lines[i]; i++) {
- if (g_str_has_prefix(lines[i], "NUMBER(phys_base)=")) {
- if (qemu_strtou64(lines[i] + 18, NULL, 16,
+ const char *prefix = NULL;
+
+ if (s->dump_info.d_machine == EM_X86_64) {
+ prefix = "NUMBER(phys_base)=";
+ } else if (s->dump_info.d_machine == EM_AARCH64) {
+ prefix = "NUMBER(PHYS_OFFSET)=";
+ }
+
+ if (prefix && g_str_has_prefix(lines[i], prefix)) {
+ if (qemu_strtou64(lines[i] + strlen(prefix), NULL, 16,
&phys_base) < 0) {
- warn_report("Failed to read NUMBER(phys_base)=");
+ warn_report("Failed to read %s", prefix);
} else {
s->dump_info.phys_base = phys_base;
}
diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c
index 40e8b25a46..3e7e8ca791 100644
--- a/hw/arm/bcm2836.c
+++ b/hw/arm/bcm2836.c
@@ -23,9 +23,40 @@
/* "QA7" (Pi2) interrupt controller and mailboxes etc. */
#define BCM2836_CONTROL_BASE 0x40000000
+struct BCM283XInfo {
+ const char *name;
+ const char *cpu_type;
+ int clusterid;
+};
+
+static const BCM283XInfo bcm283x_socs[] = {
+ {
+ .name = TYPE_BCM2836,
+ .cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"),
+ .clusterid = 0xf,
+ },
+#ifdef TARGET_AARCH64
+ {
+ .name = TYPE_BCM2837,
+ .cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"),
+ .clusterid = 0x0,
+ },
+#endif
+};
+
static void bcm2836_init(Object *obj)
{
- BCM2836State *s = BCM2836(obj);
+ BCM283XState *s = BCM283X(obj);
+ BCM283XClass *bc = BCM283X_GET_CLASS(obj);
+ const BCM283XInfo *info = bc->info;
+ int n;
+
+ for (n = 0; n < BCM283X_NCPUS; n++) {
+ object_initialize(&s->cpus[n], sizeof(s->cpus[n]),
+ info->cpu_type);
+ object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]),
+ &error_abort);
+ }
object_initialize(&s->control, sizeof(s->control), TYPE_BCM2836_CONTROL);
object_property_add_child(obj, "control", OBJECT(&s->control), NULL);
@@ -44,21 +75,15 @@ static void bcm2836_init(Object *obj)
static void bcm2836_realize(DeviceState *dev, Error **errp)
{
- BCM2836State *s = BCM2836(dev);
+ BCM283XState *s = BCM283X(dev);
+ BCM283XClass *bc = BCM283X_GET_CLASS(dev);
+ const BCM283XInfo *info = bc->info;
Object *obj;
Error *err = NULL;
int n;
/* common peripherals from bcm2835 */
- obj = OBJECT(dev);
- for (n = 0; n < BCM2836_NCPUS; n++) {
- object_initialize(&s->cpus[n], sizeof(s->cpus[n]),
- s->cpu_type);
- object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]),
- &error_abort);
- }
-
obj = object_property_get_link(OBJECT(dev), "ram", &err);
if (obj == NULL) {
error_setg(errp, "%s: required ram link not found: %s",
@@ -102,11 +127,9 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1,
qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-fiq", 0));
- for (n = 0; n < BCM2836_NCPUS; n++) {
- /* Mirror bcm2836, which has clusterid set to 0xf
- * TODO: this should be converted to a property of ARM_CPU
- */
- s->cpus[n].mp_affinity = 0xF00 | n;
+ for (n = 0; n < BCM283X_NCPUS; n++) {
+ /* TODO: this should be converted to a property of ARM_CPU */
+ s->cpus[n].mp_affinity = (info->clusterid << 8) | n;
/* set periphbase/CBAR value for CPU-local registers */
object_property_set_int(OBJECT(&s->cpus[n]),
@@ -150,30 +173,44 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
}
static Property bcm2836_props[] = {
- DEFINE_PROP_STRING("cpu-type", BCM2836State, cpu_type),
- DEFINE_PROP_UINT32("enabled-cpus", BCM2836State, enabled_cpus, BCM2836_NCPUS),
+ DEFINE_PROP_UINT32("enabled-cpus", BCM283XState, enabled_cpus,
+ BCM283X_NCPUS),
DEFINE_PROP_END_OF_LIST()
};
-static void bcm2836_class_init(ObjectClass *oc, void *data)
+static void bcm283x_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ BCM283XClass *bc = BCM283X_CLASS(oc);
- dc->props = bcm2836_props;
+ bc->info = data;
dc->realize = bcm2836_realize;
+ dc->props = bcm2836_props;
}
-static const TypeInfo bcm2836_type_info = {
- .name = TYPE_BCM2836,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(BCM2836State),
+static const TypeInfo bcm283x_type_info = {
+ .name = TYPE_BCM283X,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(BCM283XState),
.instance_init = bcm2836_init,
- .class_init = bcm2836_class_init,
+ .class_size = sizeof(BCM283XClass),
+ .abstract = true,
};
static void bcm2836_register_types(void)
{
- type_register_static(&bcm2836_type_info);
+ int i;
+
+ type_register_static(&bcm283x_type_info);
+ for (i = 0; i < ARRAY_SIZE(bcm283x_socs); i++) {
+ TypeInfo ti = {
+ .name = bcm283x_socs[i].name,
+ .parent = TYPE_BCM283X,
+ .class_init = bcm283x_class_init,
+ .class_data = (void *) &bcm283x_socs[i],
+ };
+ type_register(&ti);
+ }
}
type_init(bcm2836_register_types)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 196c7fb242..9319b12fcd 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -720,6 +720,18 @@ static void do_cpu_reset(void *opaque)
} else {
env->pstate = PSTATE_MODE_EL1h;
}
+ /* AArch64 kernels never boot in secure mode */
+ assert(!info->secure_boot);
+ /* This hook is only supported for AArch32 currently:
+ * bootloader_aarch64[] will not call the hook, and
+ * the code above has already dropped us into EL2 or EL1.
+ */
+ assert(!info->secure_board_setup);
+ }
+
+ if (arm_feature(env, ARM_FEATURE_EL2)) {
+ /* If we have EL2 then Linux expects the HVC insn to work */
+ env->cp15.scr_el3 |= SCR_HCE;
}
/* Set to non-secure if not a secure boot */
diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c
index a37881433c..06f1e08ca9 100644
--- a/hw/arm/raspi.c
+++ b/hw/arm/raspi.c
@@ -27,12 +27,13 @@
#define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */
#define FIRMWARE_ADDR_2 0x8000 /* Pi 2 loads kernel.img here by default */
#define FIRMWARE_ADDR_3 0x80000 /* Pi 3 loads kernel.img here by default */
+#define SPINTABLE_ADDR 0xd8 /* Pi 3 bootloader spintable */
/* Table of Linux board IDs for different Pi versions */
static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43, [3] = 0xc44};
typedef struct RasPiState {
- BCM2836State soc;
+ BCM283XState soc;
MemoryRegion ram;
} RasPiState;
@@ -63,6 +64,40 @@ static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info)
info->smp_loader_start);
}
+static void write_smpboot64(ARMCPU *cpu, const struct arm_boot_info *info)
+{
+ /* Unlike the AArch32 version we don't need to call the board setup hook.
+ * The mechanism for doing the spin-table is also entirely different.
+ * We must have four 64-bit fields at absolute addresses
+ * 0xd8, 0xe0, 0xe8, 0xf0 in RAM, which are the flag variables for
+ * our CPUs, and which we must ensure are zero initialized before
+ * the primary CPU goes into the kernel. We put these variables inside
+ * a rom blob, so that the reset for ROM contents zeroes them for us.
+ */
+ static const uint32_t smpboot[] = {
+ 0xd2801b05, /* mov x5, 0xd8 */
+ 0xd53800a6, /* mrs x6, mpidr_el1 */
+ 0x924004c6, /* and x6, x6, #0x3 */
+ 0xd503205f, /* spin: wfe */
+ 0xf86678a4, /* ldr x4, [x5,x6,lsl #3] */
+ 0xb4ffffc4, /* cbz x4, spin */
+ 0xd2800000, /* mov x0, #0x0 */
+ 0xd2800001, /* mov x1, #0x0 */
+ 0xd2800002, /* mov x2, #0x0 */
+ 0xd2800003, /* mov x3, #0x0 */
+ 0xd61f0080, /* br x4 */
+ };
+
+ static const uint64_t spintables[] = {
+ 0, 0, 0, 0
+ };
+
+ rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot),
+ info->smp_loader_start);
+ rom_add_blob_fixed("raspi_spintables", spintables, sizeof(spintables),
+ SPINTABLE_ADDR);
+}
+
static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info)
{
arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR);
@@ -82,15 +117,28 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size)
binfo.board_id = raspi_boardid[version];
binfo.ram_size = ram_size;
binfo.nb_cpus = smp_cpus;
- binfo.board_setup_addr = BOARDSETUP_ADDR;
- binfo.write_board_setup = write_board_setup;
- binfo.secure_board_setup = true;
- binfo.secure_boot = true;
+
+ if (version <= 2) {
+ /* The rpi1 and 2 require some custom setup code to run in Secure
+ * mode before booting a kernel (to set up the SMC vectors so
+ * that we get a no-op SMC; this is used by Linux to call the
+ * firmware for some cache maintenance operations.
+ * The rpi3 doesn't need this.
+ */
+ binfo.board_setup_addr = BOARDSETUP_ADDR;
+ binfo.write_board_setup = write_board_setup;
+ binfo.secure_board_setup = true;
+ binfo.secure_boot = true;
+ }
/* Pi2 and Pi3 requires SMP setup */
if (version >= 2) {
binfo.smp_loader_start = SMPBOOT_ADDR;
- binfo.write_secondary_boot = write_smpboot;
+ if (version == 2) {
+ binfo.write_secondary_boot = write_smpboot;
+ } else {
+ binfo.write_secondary_boot = write_smpboot64;
+ }
binfo.secondary_cpu_reset_hook = reset_secondary;
}
@@ -127,7 +175,8 @@ static void raspi_init(MachineState *machine, int version)
BusState *bus;
DeviceState *carddev;
- object_initialize(&s->soc, sizeof(s->soc), TYPE_BCM2836);
+ object_initialize(&s->soc, sizeof(s->soc),
+ version == 3 ? TYPE_BCM2837 : TYPE_BCM2836);
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
&error_abort);
@@ -140,8 +189,6 @@ static void raspi_init(MachineState *machine, int version)
/* Setup the SOC */
object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(&s->ram),
&error_abort);
- object_property_set_str(OBJECT(&s->soc), machine->cpu_type, "cpu-type",
- &error_abort);
object_property_set_int(OBJECT(&s->soc), smp_cpus, "enabled-cpus",
&error_abort);
int board_rev = version == 3 ? 0xa02082 : 0xa21041;
@@ -180,9 +227,9 @@ static void raspi2_machine_init(MachineClass *mc)
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15");
- mc->max_cpus = BCM2836_NCPUS;
- mc->min_cpus = BCM2836_NCPUS;
- mc->default_cpus = BCM2836_NCPUS;
+ mc->max_cpus = BCM283X_NCPUS;
+ mc->min_cpus = BCM283X_NCPUS;
+ mc->default_cpus = BCM283X_NCPUS;
mc->default_ram_size = 1024 * 1024 * 1024;
mc->ignore_memory_transaction_failures = true;
};
@@ -203,9 +250,9 @@ static void raspi3_machine_init(MachineClass *mc)
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a53");
- mc->max_cpus = BCM2836_NCPUS;
- mc->min_cpus = BCM2836_NCPUS;
- mc->default_cpus = BCM2836_NCPUS;
+ mc->max_cpus = BCM283X_NCPUS;
+ mc->min_cpus = BCM283X_NCPUS;
+ mc->default_cpus = BCM283X_NCPUS;
mc->default_ram_size = 1024 * 1024 * 1024;
}
DEFINE_MACHINE("raspi3", raspi3_machine_init)
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index 70405ccf8b..1e5540472b 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -37,8 +37,8 @@
static const VMStateDescription vmstate_imx_serial = {
.name = TYPE_IMX_SERIAL,
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
VMSTATE_INT32(readbuff, IMXSerialState),
VMSTATE_UINT32(usr1, IMXSerialState),
@@ -50,22 +50,36 @@ static const VMStateDescription vmstate_imx_serial = {
VMSTATE_UINT32(ubmr, IMXSerialState),
VMSTATE_UINT32(ubrc, IMXSerialState),
VMSTATE_UINT32(ucr3, IMXSerialState),
+ VMSTATE_UINT32(ucr4, IMXSerialState),
VMSTATE_END_OF_LIST()
},
};
static void imx_update(IMXSerialState *s)
{
- uint32_t flags;
+ uint32_t usr1;
+ uint32_t usr2;
+ uint32_t mask;
- flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY);
- if (s->ucr1 & UCR1_TXMPTYEN) {
- flags |= (s->uts1 & UTS1_TXEMPTY);
- } else {
- flags &= ~USR1_TRDY;
- }
+ /*
+ * Lucky for us TRDY and RRDY has the same offset in both USR1 and
+ * UCR1, so we can get away with something as simple as the
+ * following:
+ */
+ usr1 = s->usr1 & s->ucr1 & (USR1_TRDY | USR1_RRDY);
+ /*
+ * Bits that we want in USR2 are not as conveniently laid out,
+ * unfortunately.
+ */
+ mask = (s->ucr1 & UCR1_TXMPTYEN) ? USR2_TXFE : 0;
+ /*
+ * TCEN and TXDC are both bit 3
+ */
+ mask |= s->ucr4 & UCR4_TCEN;
- qemu_set_irq(s->irq, !!flags);
+ usr2 = s->usr2 & mask;
+
+ qemu_set_irq(s->irq, usr1 || usr2);
}
static void imx_serial_reset(IMXSerialState *s)
@@ -155,6 +169,8 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
return s->ucr3;
case 0x23: /* UCR4 */
+ return s->ucr4;
+
case 0x29: /* BRM Incremental */
return 0x0; /* TODO */
@@ -183,8 +199,10 @@ static void imx_serial_write(void *opaque, hwaddr offset,
* qemu_chr_fe_write and background I/O callbacks */
qemu_chr_fe_write_all(&s->chr, &ch, 1);
s->usr1 &= ~USR1_TRDY;
+ s->usr2 &= ~USR2_TXDC;
imx_update(s);
s->usr1 |= USR1_TRDY;
+ s->usr2 |= USR2_TXDC;
imx_update(s);
}
break;
@@ -257,8 +275,12 @@ static void imx_serial_write(void *opaque, hwaddr offset,
s->ucr3 = value & 0xffff;
break;
- case 0x2d: /* UTS1 */
case 0x23: /* UCR4 */
+ s->ucr4 = value & 0xffff;
+ imx_update(s);
+ break;
+
+ case 0x2d: /* UTS1 */
qemu_log_mask(LOG_UNIMP, "[%s]%s: Unimplemented reg 0x%"
HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset);
/* TODO */
diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
index 9506f9b69f..6e297c5480 100644
--- a/hw/net/imx_fec.c
+++ b/hw/net/imx_fec.c
@@ -417,7 +417,33 @@ static void imx_enet_write_bd(IMXENETBufDesc *bd, dma_addr_t addr)
static void imx_eth_update(IMXFECState *s)
{
- if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & ENET_INT_TS_TIMER) {
+ /*
+ * Previous versions of qemu had the ENET_INT_MAC and ENET_INT_TS_TIMER
+ * interrupts swapped. This worked with older versions of Linux (4.14
+ * and older) since Linux associated both interrupt lines with Ethernet
+ * MAC interrupts. Specifically,
+ * - Linux 4.15 and later have separate interrupt handlers for the MAC and
+ * timer interrupts. Those versions of Linux fail with versions of QEMU
+ * with swapped interrupt assignments.
+ * - In linux 4.14, both interrupt lines were registered with the Ethernet
+ * MAC interrupt handler. As a result, all versions of qemu happen to
+ * work, though that is accidental.
+ * - In Linux 4.9 and older, the timer interrupt was registered directly
+ * with the Ethernet MAC interrupt handler. The MAC interrupt was
+ * redirected to a GPIO interrupt to work around erratum ERR006687.
+ * This was implemented using the SOC's IOMUX block. In qemu, this GPIO
+ * interrupt never fired since IOMUX is currently not supported in qemu.
+ * Linux instead received MAC interrupts on the timer interrupt.
+ * As a result, qemu versions with the swapped interrupt assignment work,
+ * albeit accidentally, but qemu versions with the correct interrupt
+ * assignment fail.
+ *
+ * To ensure that all versions of Linux work, generate ENET_INT_MAC
+ * interrrupts on both interrupt lines. This should be changed if and when
+ * qemu supports IOMUX.
+ */
+ if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] &
+ (ENET_INT_MAC | ENET_INT_TS_TIMER)) {
qemu_set_irq(s->irq[1], 1);
} else {
qemu_set_irq(s->irq[1], 0);
diff --git a/include/hw/arm/bcm2836.h b/include/hw/arm/bcm2836.h
index 4758b4ae54..93248399ba 100644
--- a/include/hw/arm/bcm2836.h
+++ b/include/hw/arm/bcm2836.h
@@ -15,12 +15,19 @@
#include "hw/arm/bcm2835_peripherals.h"
#include "hw/intc/bcm2836_control.h"
-#define TYPE_BCM2836 "bcm2836"
-#define BCM2836(obj) OBJECT_CHECK(BCM2836State, (obj), TYPE_BCM2836)
+#define TYPE_BCM283X "bcm283x"
+#define BCM283X(obj) OBJECT_CHECK(BCM283XState, (obj), TYPE_BCM283X)
+
+#define BCM283X_NCPUS 4
-#define BCM2836_NCPUS 4
+/* These type names are for specific SoCs; other than instantiating
+ * them, code using these devices should always handle them via the
+ * BCM283x base class, so they have no BCM2836(obj) etc macros.
+ */
+#define TYPE_BCM2836 "bcm2836"
+#define TYPE_BCM2837 "bcm2837"
-typedef struct BCM2836State {
+typedef struct BCM283XState {
/*< private >*/
DeviceState parent_obj;
/*< public >*/
@@ -28,9 +35,21 @@ typedef struct BCM2836State {
char *cpu_type;
uint32_t enabled_cpus;
- ARMCPU cpus[BCM2836_NCPUS];
+ ARMCPU cpus[BCM283X_NCPUS];
BCM2836ControlState control;
BCM2835PeripheralState peripherals;
-} BCM2836State;
+} BCM283XState;
+
+typedef struct BCM283XInfo BCM283XInfo;
+
+typedef struct BCM283XClass {
+ DeviceClass parent_class;
+ const BCM283XInfo *info;
+} BCM283XClass;
+
+#define BCM283X_CLASS(klass) \
+ OBJECT_CLASS_CHECK(BCM283XClass, (klass), TYPE_BCM283X)
+#define BCM283X_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(BCM283XClass, (obj), TYPE_BCM283X)
#endif /* BCM2836_H */
diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h
index ec6c509d74..06f8aaeda4 100644
--- a/include/hw/arm/fsl-imx6.h
+++ b/include/hw/arm/fsl-imx6.h
@@ -438,8 +438,8 @@ typedef struct FslIMX6State {
#define FSL_IMX6_HDMI_MASTER_IRQ 115
#define FSL_IMX6_HDMI_CEC_IRQ 116
#define FSL_IMX6_MLB150_LOW_IRQ 117
-#define FSL_IMX6_ENET_MAC_1588_IRQ 118
-#define FSL_IMX6_ENET_MAC_IRQ 119
+#define FSL_IMX6_ENET_MAC_IRQ 118
+#define FSL_IMX6_ENET_MAC_1588_IRQ 119
#define FSL_IMX6_PCIE1_IRQ 120
#define FSL_IMX6_PCIE2_IRQ 121
#define FSL_IMX6_PCIE3_IRQ 122
diff --git a/include/hw/char/imx_serial.h b/include/hw/char/imx_serial.h
index baeec3183f..5b99cee7cf 100644
--- a/include/hw/char/imx_serial.h
+++ b/include/hw/char/imx_serial.h
@@ -67,6 +67,8 @@
#define UCR2_RXEN (1<<1) /* Receiver enable */
#define UCR2_SRST (1<<0) /* Reset complete */
+#define UCR4_TCEN BIT(3) /* TX complete interrupt enable */
+
#define UTS1_TXEMPTY (1<<6)
#define UTS1_RXEMPTY (1<<5)
#define UTS1_TXFULL (1<<4)
@@ -95,6 +97,7 @@ typedef struct IMXSerialState {
uint32_t ubmr;
uint32_t ubrc;
uint32_t ucr3;
+ uint32_t ucr4;
qemu_irq irq;
CharBackend chr;