aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/aspeed.c27
-rw-r--r--hw/arm/virt-acpi-build.c4
-rw-r--r--hw/arm/virt.c21
-rw-r--r--hw/core/loader.c3
-rw-r--r--hw/i2c/aspeed_i2c.c65
-rw-r--r--hw/intc/arm_gicv3_cpuif.c50
-rw-r--r--hw/intc/armv7m_nvic.c104
7 files changed, 258 insertions, 16 deletions
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index 283c038814..e824ea87a9 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -39,6 +39,7 @@ typedef struct AspeedBoardConfig {
const char *fmc_model;
const char *spi_model;
uint32_t num_cs;
+ void (*i2c_init)(AspeedBoardState *bmc);
} AspeedBoardConfig;
enum {
@@ -82,6 +83,9 @@ enum {
SCU_AST2500_HW_STRAP_ACPI_ENABLE | \
SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER))
+static void palmetto_bmc_i2c_init(AspeedBoardState *bmc);
+static void ast2500_evb_i2c_init(AspeedBoardState *bmc);
+
static const AspeedBoardConfig aspeed_boards[] = {
[PALMETTO_BMC] = {
.soc_name = "ast2400-a1",
@@ -89,6 +93,7 @@ static const AspeedBoardConfig aspeed_boards[] = {
.fmc_model = "n25q256a",
.spi_model = "mx25l25635e",
.num_cs = 1,
+ .i2c_init = palmetto_bmc_i2c_init,
},
[AST2500_EVB] = {
.soc_name = "ast2500-a1",
@@ -96,6 +101,7 @@ static const AspeedBoardConfig aspeed_boards[] = {
.fmc_model = "n25q256a",
.spi_model = "mx25l25635e",
.num_cs = 1,
+ .i2c_init = ast2500_evb_i2c_init,
},
[ROMULUS_BMC] = {
.soc_name = "ast2500-a1",
@@ -223,9 +229,22 @@ static void aspeed_board_init(MachineState *machine,
aspeed_board_binfo.ram_size = ram_size;
aspeed_board_binfo.loader_start = sc->info->sdram_base;
+ if (cfg->i2c_init) {
+ cfg->i2c_init(bmc);
+ }
+
arm_load_kernel(ARM_CPU(first_cpu), &aspeed_board_binfo);
}
+static void palmetto_bmc_i2c_init(AspeedBoardState *bmc)
+{
+ AspeedSoCState *soc = &bmc->soc;
+
+ /* The palmetto platform expects a ds3231 RTC but a ds1338 is
+ * enough to provide basic RTC features. Alarms will be missing */
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 0), "ds1338", 0x68);
+}
+
static void palmetto_bmc_init(MachineState *machine)
{
aspeed_board_init(machine, &aspeed_boards[PALMETTO_BMC]);
@@ -250,6 +269,14 @@ static const TypeInfo palmetto_bmc_type = {
.class_init = palmetto_bmc_class_init,
};
+static void ast2500_evb_i2c_init(AspeedBoardState *bmc)
+{
+ AspeedSoCState *soc = &bmc->soc;
+
+ /* The AST2500 EVB expects a LM75 but a TMP105 is compatible */
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 7), "tmp105", 0x4d);
+}
+
static void ast2500_evb_init(MachineState *machine)
{
aspeed_board_init(machine, &aspeed_boards[AST2500_EVB]);
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index e5852067f5..2079828c22 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -776,6 +776,10 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
if (nb_numa_nodes > 0) {
acpi_add_table(table_offsets, tables_blob);
build_srat(tables_blob, tables->linker, vms);
+ if (have_numa_distance) {
+ acpi_add_table(table_offsets, tables_blob);
+ build_slit(tables_blob, tables->linker);
+ }
}
if (its_class_name() && !vmc->no_its) {
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index c7c8159dfd..4db2d4207c 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -219,6 +219,27 @@ static void create_fdt(VirtMachineState *vms)
"clk24mhz");
qemu_fdt_setprop_cell(fdt, "/apb-pclk", "phandle", vms->clock_phandle);
+ if (have_numa_distance) {
+ int size = nb_numa_nodes * nb_numa_nodes * 3 * sizeof(uint32_t);
+ uint32_t *matrix = g_malloc0(size);
+ int idx, i, j;
+
+ for (i = 0; i < nb_numa_nodes; i++) {
+ for (j = 0; j < nb_numa_nodes; j++) {
+ idx = (i * nb_numa_nodes + j) * 3;
+ matrix[idx + 0] = cpu_to_be32(i);
+ matrix[idx + 1] = cpu_to_be32(j);
+ matrix[idx + 2] = cpu_to_be32(numa_info[i].distance[j]);
+ }
+ }
+
+ qemu_fdt_add_subnode(fdt, "/distance-map");
+ qemu_fdt_setprop_string(fdt, "/distance-map", "compatible",
+ "numa-distance-map-v1");
+ qemu_fdt_setprop(fdt, "/distance-map", "distance-matrix",
+ matrix, size);
+ g_free(matrix);
+ }
}
static void fdt_add_psci_node(const VirtMachineState *vms)
diff --git a/hw/core/loader.c b/hw/core/loader.c
index bf17b42cbe..f72930ca4a 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -611,8 +611,9 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr,
return -1;
size = read(fd, hdr, sizeof(uboot_image_header_t));
- if (size < 0)
+ if (size < sizeof(uboot_image_header_t)) {
goto out;
+ }
bswap_uboot_header(hdr);
diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c
index ce5b1f0fa4..c762c7366a 100644
--- a/hw/i2c/aspeed_i2c.c
+++ b/hw/i2c/aspeed_i2c.c
@@ -169,12 +169,33 @@ static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset,
}
}
+static void aspeed_i2c_set_state(AspeedI2CBus *bus, uint8_t state)
+{
+ bus->cmd &= ~(I2CD_TX_STATE_MASK << I2CD_TX_STATE_SHIFT);
+ bus->cmd |= (state & I2CD_TX_STATE_MASK) << I2CD_TX_STATE_SHIFT;
+}
+
+static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus)
+{
+ return (bus->cmd >> I2CD_TX_STATE_SHIFT) & I2CD_TX_STATE_MASK;
+}
+
+/*
+ * The state machine needs some refinement. It is only used to track
+ * invalid STOP commands for the moment.
+ */
static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value)
{
+ bus->cmd &= ~0xFFFF;
bus->cmd |= value & 0xFFFF;
bus->intr_status = 0;
if (bus->cmd & I2CD_M_START_CMD) {
+ uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ?
+ I2CD_MSTARTR : I2CD_MSTART;
+
+ aspeed_i2c_set_state(bus, state);
+
if (i2c_start_transfer(bus->bus, extract32(bus->buf, 1, 7),
extract32(bus->buf, 0, 1))) {
bus->intr_status |= I2CD_INTR_TX_NAK;
@@ -182,16 +203,34 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value)
bus->intr_status |= I2CD_INTR_TX_ACK;
}
- } else if (bus->cmd & I2CD_M_TX_CMD) {
+ /* START command is also a TX command, as the slave address is
+ * sent on the bus */
+ bus->cmd &= ~(I2CD_M_START_CMD | I2CD_M_TX_CMD);
+
+ /* No slave found */
+ if (!i2c_bus_busy(bus->bus)) {
+ return;
+ }
+ aspeed_i2c_set_state(bus, I2CD_MACTIVE);
+ }
+
+ if (bus->cmd & I2CD_M_TX_CMD) {
+ aspeed_i2c_set_state(bus, I2CD_MTXD);
if (i2c_send(bus->bus, bus->buf)) {
- bus->intr_status |= (I2CD_INTR_TX_NAK | I2CD_INTR_ABNORMAL);
+ bus->intr_status |= (I2CD_INTR_TX_NAK);
i2c_end_transfer(bus->bus);
} else {
bus->intr_status |= I2CD_INTR_TX_ACK;
}
+ bus->cmd &= ~I2CD_M_TX_CMD;
+ aspeed_i2c_set_state(bus, I2CD_MACTIVE);
+ }
- } else if (bus->cmd & I2CD_M_RX_CMD) {
- int ret = i2c_recv(bus->bus);
+ if (bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST)) {
+ int ret;
+
+ aspeed_i2c_set_state(bus, I2CD_MRXD);
+ ret = i2c_recv(bus->bus);
if (ret < 0) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: read failed\n", __func__);
ret = 0xff;
@@ -199,20 +238,25 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value)
bus->intr_status |= I2CD_INTR_RX_DONE;
}
bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT;
+ if (bus->cmd & I2CD_M_S_RX_CMD_LAST) {
+ i2c_nack(bus->bus);
+ }
+ bus->cmd &= ~(I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST);
+ aspeed_i2c_set_state(bus, I2CD_MACTIVE);
}
- if (bus->cmd & (I2CD_M_STOP_CMD | I2CD_M_S_RX_CMD_LAST)) {
- if (!i2c_bus_busy(bus->bus)) {
+ if (bus->cmd & I2CD_M_STOP_CMD) {
+ if (!(aspeed_i2c_get_state(bus) & I2CD_MACTIVE)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: abnormal stop\n", __func__);
bus->intr_status |= I2CD_INTR_ABNORMAL;
} else {
+ aspeed_i2c_set_state(bus, I2CD_MSTOP);
i2c_end_transfer(bus->bus);
bus->intr_status |= I2CD_INTR_NORMAL_STOP;
}
+ bus->cmd &= ~I2CD_M_STOP_CMD;
+ aspeed_i2c_set_state(bus, I2CD_IDLE);
}
-
- /* command is handled, reset it and check for interrupts */
- bus->cmd &= ~0xFFFF;
- aspeed_i2c_bus_raise_interrupt(bus);
}
static void aspeed_i2c_bus_write(void *opaque, hwaddr offset,
@@ -262,6 +306,7 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset,
}
aspeed_i2c_bus_handle_cmd(bus, value);
+ aspeed_i2c_bus_raise_interrupt(bus);
break;
default:
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index 0b208560bd..09d8ba0547 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -216,18 +216,35 @@ static uint32_t icv_gprio_mask(GICv3CPUState *cs, int group)
{
/* Return a mask word which clears the subpriority bits from
* a priority value for a virtual interrupt in the specified group.
- * This depends on the VBPR value:
+ * This depends on the VBPR value.
+ * If using VBPR0 then:
* a BPR of 0 means the group priority bits are [7:1];
* a BPR of 1 means they are [7:2], and so on down to
* a BPR of 7 meaning no group priority bits at all.
+ * If using VBPR1 then:
+ * a BPR of 0 is impossible (the minimum value is 1)
+ * a BPR of 1 means the group priority bits are [7:1];
+ * a BPR of 2 means they are [7:2], and so on down to
+ * a BPR of 7 meaning the group priority is [7].
+ *
* Which BPR to use depends on the group of the interrupt and
* the current ICH_VMCR_EL2.VCBPR settings.
+ *
+ * This corresponds to the VGroupBits() pseudocode.
*/
+ int bpr;
+
if (group == GICV3_G1NS && cs->ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR) {
group = GICV3_G0;
}
- return ~0U << (read_vbpr(cs, group) + 1);
+ bpr = read_vbpr(cs, group);
+ if (group == GICV3_G1NS) {
+ assert(bpr > 0);
+ bpr--;
+ }
+
+ return ~0U << (bpr + 1);
}
static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr)
@@ -674,20 +691,37 @@ static uint32_t icc_gprio_mask(GICv3CPUState *cs, int group)
{
/* Return a mask word which clears the subpriority bits from
* a priority value for an interrupt in the specified group.
- * This depends on the BPR value:
+ * This depends on the BPR value. For CBPR0 (S or NS):
* a BPR of 0 means the group priority bits are [7:1];
* a BPR of 1 means they are [7:2], and so on down to
* a BPR of 7 meaning no group priority bits at all.
+ * For CBPR1 NS:
+ * a BPR of 0 is impossible (the minimum value is 1)
+ * a BPR of 1 means the group priority bits are [7:1];
+ * a BPR of 2 means they are [7:2], and so on down to
+ * a BPR of 7 meaning the group priority is [7].
+ *
* Which BPR to use depends on the group of the interrupt and
* the current ICC_CTLR.CBPR settings.
+ *
+ * This corresponds to the GroupBits() pseudocode.
*/
+ int bpr;
+
if ((group == GICV3_G1 && cs->icc_ctlr_el1[GICV3_S] & ICC_CTLR_EL1_CBPR) ||
(group == GICV3_G1NS &&
cs->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_CBPR)) {
group = GICV3_G0;
}
- return ~0U << ((cs->icc_bpr[group] & 7) + 1);
+ bpr = cs->icc_bpr[group] & 7;
+
+ if (group == GICV3_G1NS) {
+ assert(bpr > 0);
+ bpr--;
+ }
+
+ return ~0U << (bpr + 1);
}
static bool icc_no_enabled_hppi(GICv3CPUState *cs)
@@ -1388,6 +1422,7 @@ static void icc_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri,
{
GICv3CPUState *cs = icc_cs_from_env(env);
int grp = (ri->crm == 8) ? GICV3_G0 : GICV3_G1;
+ uint64_t minval;
if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) {
icv_bpr_write(env, ri, value);
@@ -1415,6 +1450,11 @@ static void icc_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri,
return;
}
+ minval = (grp == GICV3_G1NS) ? GIC_MIN_BPR_NS : GIC_MIN_BPR;
+ if (value < minval) {
+ value = minval;
+ }
+
cs->icc_bpr[grp] = value & 7;
gicv3_cpuif_update(cs);
}
@@ -2014,7 +2054,7 @@ static void icc_reset(CPUARMState *env, const ARMCPRegInfo *ri)
cs->ich_hcr_el2 = 0;
memset(cs->ich_lr_el2, 0, sizeof(cs->ich_lr_el2));
cs->ich_vmcr_el2 = ICH_VMCR_EL2_VFIQEN |
- (icv_min_vbpr(cs) << ICH_VMCR_EL2_VBPR1_SHIFT) |
+ ((icv_min_vbpr(cs) + 1) << ICH_VMCR_EL2_VBPR1_SHIFT) |
(icv_min_vbpr(cs) << ICH_VMCR_EL2_VBPR0_SHIFT);
}
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index 32ffa0bf35..26a4b2dcb5 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -19,6 +19,7 @@
#include "hw/arm/arm.h"
#include "hw/arm/armv7m_nvic.h"
#include "target/arm/cpu.h"
+#include "exec/exec-all.h"
#include "qemu/log.h"
#include "trace.h"
@@ -528,6 +529,39 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset)
case 0xd70: /* ISAR4. */
return 0x01310102;
/* TODO: Implement debug registers. */
+ case 0xd90: /* MPU_TYPE */
+ /* Unified MPU; if the MPU is not present this value is zero */
+ return cpu->pmsav7_dregion << 8;
+ break;
+ case 0xd94: /* MPU_CTRL */
+ return cpu->env.v7m.mpu_ctrl;
+ case 0xd98: /* MPU_RNR */
+ return cpu->env.cp15.c6_rgnr;
+ case 0xd9c: /* MPU_RBAR */
+ case 0xda4: /* MPU_RBAR_A1 */
+ case 0xdac: /* MPU_RBAR_A2 */
+ case 0xdb4: /* MPU_RBAR_A3 */
+ {
+ int region = cpu->env.cp15.c6_rgnr;
+
+ if (region >= cpu->pmsav7_dregion) {
+ return 0;
+ }
+ return (cpu->env.pmsav7.drbar[region] & 0x1f) | (region & 0xf);
+ }
+ case 0xda0: /* MPU_RASR */
+ case 0xda8: /* MPU_RASR_A1 */
+ case 0xdb0: /* MPU_RASR_A2 */
+ case 0xdb8: /* MPU_RASR_A3 */
+ {
+ int region = cpu->env.cp15.c6_rgnr;
+
+ if (region >= cpu->pmsav7_dregion) {
+ return 0;
+ }
+ return ((cpu->env.pmsav7.dracr[region] & 0xffff) << 16) |
+ (cpu->env.pmsav7.drsr[region] & 0xffff);
+ }
default:
qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset);
return 0;
@@ -627,6 +661,76 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value)
qemu_log_mask(LOG_UNIMP,
"NVIC: Aux fault status registers unimplemented\n");
break;
+ case 0xd90: /* MPU_TYPE */
+ return; /* RO */
+ case 0xd94: /* MPU_CTRL */
+ if ((value &
+ (R_V7M_MPU_CTRL_HFNMIENA_MASK | R_V7M_MPU_CTRL_ENABLE_MASK))
+ == R_V7M_MPU_CTRL_HFNMIENA_MASK) {
+ qemu_log_mask(LOG_GUEST_ERROR, "MPU_CTRL: HFNMIENA and !ENABLE is "
+ "UNPREDICTABLE\n");
+ }
+ cpu->env.v7m.mpu_ctrl = value & (R_V7M_MPU_CTRL_ENABLE_MASK |
+ R_V7M_MPU_CTRL_HFNMIENA_MASK |
+ R_V7M_MPU_CTRL_PRIVDEFENA_MASK);
+ tlb_flush(CPU(cpu));
+ break;
+ case 0xd98: /* MPU_RNR */
+ if (value >= cpu->pmsav7_dregion) {
+ qemu_log_mask(LOG_GUEST_ERROR, "MPU region out of range %"
+ PRIu32 "/%" PRIu32 "\n",
+ value, cpu->pmsav7_dregion);
+ } else {
+ cpu->env.cp15.c6_rgnr = value;
+ }
+ break;
+ case 0xd9c: /* MPU_RBAR */
+ case 0xda4: /* MPU_RBAR_A1 */
+ case 0xdac: /* MPU_RBAR_A2 */
+ case 0xdb4: /* MPU_RBAR_A3 */
+ {
+ int region;
+
+ if (value & (1 << 4)) {
+ /* VALID bit means use the region number specified in this
+ * value and also update MPU_RNR.REGION with that value.
+ */
+ region = extract32(value, 0, 4);
+ if (region >= cpu->pmsav7_dregion) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "MPU region out of range %u/%" PRIu32 "\n",
+ region, cpu->pmsav7_dregion);
+ return;
+ }
+ cpu->env.cp15.c6_rgnr = region;
+ } else {
+ region = cpu->env.cp15.c6_rgnr;
+ }
+
+ if (region >= cpu->pmsav7_dregion) {
+ return;
+ }
+
+ cpu->env.pmsav7.drbar[region] = value & ~0x1f;
+ tlb_flush(CPU(cpu));
+ break;
+ }
+ case 0xda0: /* MPU_RASR */
+ case 0xda8: /* MPU_RASR_A1 */
+ case 0xdb0: /* MPU_RASR_A2 */
+ case 0xdb8: /* MPU_RASR_A3 */
+ {
+ int region = cpu->env.cp15.c6_rgnr;
+
+ if (region >= cpu->pmsav7_dregion) {
+ return;
+ }
+
+ cpu->env.pmsav7.drsr[region] = value & 0xff3f;
+ cpu->env.pmsav7.dracr[region] = (value >> 16) & 0x173f;
+ tlb_flush(CPU(cpu));
+ break;
+ }
case 0xf00: /* Software Triggered Interrupt Register */
{
/* user mode can only write to STIR if CCR.USERSETMPEND permits it */