ppc patch queue 2018-08-21
Here's my first ppc & spapr pull request for qemu-3.1. This contains a bunch of things that have accumulated while 3.0 was in freeze. Highlights are: * SLOF firmware update * A number of floating point cleanups from Richard Henderson and Yasmin Beatriz * A new model for assigning irq numbers on spapr, this is an important preliminary step towards implementing the POWER9 "XIVE" interrupt controller -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEdfRlhq5hpmzETofcbDjKyiDZs5IFAlt7lewACgkQbDjKyiDZ s5K+TA//QIMtlm59lR1G68Bwj656WEMgi/f+HN3FL419XtOZ/UkgprPmvBzWvoVP r7EgyktRw9qSyCsOe5OOST12rkP8s4RwyjxOPak8opRBEgXRFYk9q8micCCOv/94 X7dtxh7sqDYvWVC4Gky1SvmNbrPtaFqSWAp7ZC/+OYnN5jOg9g+nQloPTko++GKp hNEKoS5I/5Q/OvtkaxGy6+G5oShi3in9gpC/nE5vtfJOnZ/ukIJcW5Niate6INpF WoKg5LPEF3/f0GGCDxumpoOQ7odVcBIFrtbeoeEDIK91f0l3H7+n75b8xgWE1Y51 WelLNgdD2n0Z1pxhKwxUljIg5CnJamVSBhd6zELXDc5cx8CcOBLuNBSqtpriyRPn 0Or3E4xfq3EbD+fNVcqHNVBC8M5mN18iplx+sOjmNTbBtwAiB/IGpVVfJkhc83Ed 85Rlu4FxDdwBdeeE21PwdLhkRrRrtYpgobiWU2Mw0l20YYflhnQ20XS80AVQiVBa H/NflZbkEM93rqt/sKwenlx0bAUKt1HjZpE3mDuhSkLMRL4Sdg4hsulFEMT7QpPW QSZs+AntJpC6znRmZfE0Cavq1GNk5j4j9O5MBSKD8fbSNv7UR6Muu4SABIhjEZ0m 7wG7qfqfLVEO/cnFph4nKgSAPnCE8mNiIyE0VowpkjhUWFSDTGE= =viH7 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-3.1-20180821' into staging ppc patch queue 2018-08-21 Here's my first ppc & spapr pull request for qemu-3.1. This contains a bunch of things that have accumulated while 3.0 was in freeze. Highlights are: * SLOF firmware update * A number of floating point cleanups from Richard Henderson and Yasmin Beatriz * A new model for assigning irq numbers on spapr, this is an important preliminary step towards implementing the POWER9 "XIVE" interrupt controller # gpg: Signature made Tue 21 Aug 2018 05:32:44 BST # gpg: using RSA key 6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dgibson/tags/ppc-for-3.1-20180821: (26 commits) ppc: add DBCR based debugging spapr_pci: factorize the use of SPAPR_MACHINE_GET_CLASS() mac_newworld: don't use legacy fw_cfg_init_mem() function mac_oldworld: don't use legacy fw_cfg_init_mem() function 40p: don't use legacy fw_cfg_init_mem() function qemu-doc: mark ppc/prep machine as deprecated hw/ppc: deprecate the machine type 'prep', replaced by '40p' spapr: introduce a IRQ controller backend to the machine hw/ppc/ppc405_uc: Convert away from old_mmio hw/ppc/ppc_boards: Don't use old_mmio for ref405ep_fpga hw/ppc/prep: Remove ifdeffed-out stub of XCSR code spapr: introduce a fixed IRQ number space spapr: Add a pseries-3.1 machine type target/ppc: simplify bcdadd/sub functions xics: don't include "target/ppc/cpu-qom.h" in "hw/ppc/xics.h" vfio/spapr: Allow backing bigger guest IOMMU pages with smaller physical pages target/ppc: bcdsub fix sign when result is zero target/ppc: Use non-arithmetic conversions for fp load/store target/ppc: Honor fpscr_ze semantics and tidy fre, fresqrt target/ppc: Tidy helper_fsqrt ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
ee135aa042
|
@ -4,7 +4,7 @@ obj-y += ppc.o ppc_booke.o fdt.o
|
||||||
obj-$(CONFIG_PSERIES) += spapr.o spapr_caps.o spapr_vio.o spapr_events.o
|
obj-$(CONFIG_PSERIES) += spapr.o spapr_caps.o spapr_vio.o spapr_events.o
|
||||||
obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
|
obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
|
||||||
obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
|
obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
|
||||||
obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o
|
obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o spapr_irq.o
|
||||||
# IBM PowerNV
|
# IBM PowerNV
|
||||||
obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o
|
obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o
|
||||||
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
|
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
|
||||||
|
|
|
@ -454,7 +454,17 @@ static void ppc_core99_init(MachineState *machine)
|
||||||
pmac_format_nvram_partition(nvr, 0x2000);
|
pmac_format_nvram_partition(nvr, 0x2000);
|
||||||
/* No PCI init: the BIOS will do it */
|
/* No PCI init: the BIOS will do it */
|
||||||
|
|
||||||
fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2);
|
dev = qdev_create(NULL, TYPE_FW_CFG_MEM);
|
||||||
|
fw_cfg = FW_CFG(dev);
|
||||||
|
qdev_prop_set_uint32(dev, "data_width", 1);
|
||||||
|
qdev_prop_set_bit(dev, "dma_enabled", false);
|
||||||
|
object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG,
|
||||||
|
OBJECT(fw_cfg), NULL);
|
||||||
|
qdev_init_nofail(dev);
|
||||||
|
s = SYS_BUS_DEVICE(dev);
|
||||||
|
sysbus_mmio_map(s, 0, CFG_ADDR);
|
||||||
|
sysbus_mmio_map(s, 1, CFG_ADDR + 2);
|
||||||
|
|
||||||
fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
|
fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
|
||||||
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
|
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
|
||||||
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
|
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
|
||||||
|
|
|
@ -309,7 +309,17 @@ static void ppc_heathrow_init(MachineState *machine)
|
||||||
|
|
||||||
/* No PCI init: the BIOS will do it */
|
/* No PCI init: the BIOS will do it */
|
||||||
|
|
||||||
fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2);
|
dev = qdev_create(NULL, TYPE_FW_CFG_MEM);
|
||||||
|
fw_cfg = FW_CFG(dev);
|
||||||
|
qdev_prop_set_uint32(dev, "data_width", 1);
|
||||||
|
qdev_prop_set_bit(dev, "dma_enabled", false);
|
||||||
|
object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG,
|
||||||
|
OBJECT(fw_cfg), NULL);
|
||||||
|
qdev_init_nofail(dev);
|
||||||
|
s = SYS_BUS_DEVICE(dev);
|
||||||
|
sysbus_mmio_map(s, 0, CFG_ADDR);
|
||||||
|
sysbus_mmio_map(s, 1, CFG_ADDR + 2);
|
||||||
|
|
||||||
fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
|
fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
|
||||||
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
|
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
|
||||||
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
|
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
|
||||||
|
|
|
@ -66,7 +66,7 @@ struct ref405ep_fpga_t {
|
||||||
uint8_t reg1;
|
uint8_t reg1;
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint32_t ref405ep_fpga_readb (void *opaque, hwaddr addr)
|
static uint64_t ref405ep_fpga_readb(void *opaque, hwaddr addr, unsigned size)
|
||||||
{
|
{
|
||||||
ref405ep_fpga_t *fpga;
|
ref405ep_fpga_t *fpga;
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
|
@ -87,8 +87,8 @@ static uint32_t ref405ep_fpga_readb (void *opaque, hwaddr addr)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ref405ep_fpga_writeb (void *opaque,
|
static void ref405ep_fpga_writeb(void *opaque, hwaddr addr, uint64_t value,
|
||||||
hwaddr addr, uint32_t value)
|
unsigned size)
|
||||||
{
|
{
|
||||||
ref405ep_fpga_t *fpga;
|
ref405ep_fpga_t *fpga;
|
||||||
|
|
||||||
|
@ -105,54 +105,14 @@ static void ref405ep_fpga_writeb (void *opaque,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t ref405ep_fpga_readw (void *opaque, hwaddr addr)
|
|
||||||
{
|
|
||||||
uint32_t ret;
|
|
||||||
|
|
||||||
ret = ref405ep_fpga_readb(opaque, addr) << 8;
|
|
||||||
ret |= ref405ep_fpga_readb(opaque, addr + 1);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ref405ep_fpga_writew (void *opaque,
|
|
||||||
hwaddr addr, uint32_t value)
|
|
||||||
{
|
|
||||||
ref405ep_fpga_writeb(opaque, addr, (value >> 8) & 0xFF);
|
|
||||||
ref405ep_fpga_writeb(opaque, addr + 1, value & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t ref405ep_fpga_readl (void *opaque, hwaddr addr)
|
|
||||||
{
|
|
||||||
uint32_t ret;
|
|
||||||
|
|
||||||
ret = ref405ep_fpga_readb(opaque, addr) << 24;
|
|
||||||
ret |= ref405ep_fpga_readb(opaque, addr + 1) << 16;
|
|
||||||
ret |= ref405ep_fpga_readb(opaque, addr + 2) << 8;
|
|
||||||
ret |= ref405ep_fpga_readb(opaque, addr + 3);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ref405ep_fpga_writel (void *opaque,
|
|
||||||
hwaddr addr, uint32_t value)
|
|
||||||
{
|
|
||||||
ref405ep_fpga_writeb(opaque, addr, (value >> 24) & 0xFF);
|
|
||||||
ref405ep_fpga_writeb(opaque, addr + 1, (value >> 16) & 0xFF);
|
|
||||||
ref405ep_fpga_writeb(opaque, addr + 2, (value >> 8) & 0xFF);
|
|
||||||
ref405ep_fpga_writeb(opaque, addr + 3, value & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const MemoryRegionOps ref405ep_fpga_ops = {
|
static const MemoryRegionOps ref405ep_fpga_ops = {
|
||||||
.old_mmio = {
|
.read = ref405ep_fpga_readb,
|
||||||
.read = {
|
.write = ref405ep_fpga_writeb,
|
||||||
ref405ep_fpga_readb, ref405ep_fpga_readw, ref405ep_fpga_readl,
|
.impl.min_access_size = 1,
|
||||||
},
|
.impl.max_access_size = 1,
|
||||||
.write = {
|
.valid.min_access_size = 1,
|
||||||
ref405ep_fpga_writeb, ref405ep_fpga_writew, ref405ep_fpga_writel,
|
.valid.max_access_size = 4,
|
||||||
},
|
.endianness = DEVICE_BIG_ENDIAN,
|
||||||
},
|
|
||||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ref405ep_fpga_reset (void *opaque)
|
static void ref405ep_fpga_reset (void *opaque)
|
||||||
|
|
|
@ -283,7 +283,7 @@ struct ppc4xx_opba_t {
|
||||||
uint8_t pr;
|
uint8_t pr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint32_t opba_readb (void *opaque, hwaddr addr)
|
static uint64_t opba_readb(void *opaque, hwaddr addr, unsigned size)
|
||||||
{
|
{
|
||||||
ppc4xx_opba_t *opba;
|
ppc4xx_opba_t *opba;
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
|
@ -307,8 +307,8 @@ static uint32_t opba_readb (void *opaque, hwaddr addr)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void opba_writeb (void *opaque,
|
static void opba_writeb(void *opaque, hwaddr addr, uint64_t value,
|
||||||
hwaddr addr, uint32_t value)
|
unsigned size)
|
||||||
{
|
{
|
||||||
ppc4xx_opba_t *opba;
|
ppc4xx_opba_t *opba;
|
||||||
|
|
||||||
|
@ -328,61 +328,14 @@ static void opba_writeb (void *opaque,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t opba_readw (void *opaque, hwaddr addr)
|
|
||||||
{
|
|
||||||
uint32_t ret;
|
|
||||||
|
|
||||||
#ifdef DEBUG_OPBA
|
|
||||||
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
|
|
||||||
#endif
|
|
||||||
ret = opba_readb(opaque, addr) << 8;
|
|
||||||
ret |= opba_readb(opaque, addr + 1);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void opba_writew (void *opaque,
|
|
||||||
hwaddr addr, uint32_t value)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_OPBA
|
|
||||||
printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
|
|
||||||
value);
|
|
||||||
#endif
|
|
||||||
opba_writeb(opaque, addr, value >> 8);
|
|
||||||
opba_writeb(opaque, addr + 1, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t opba_readl (void *opaque, hwaddr addr)
|
|
||||||
{
|
|
||||||
uint32_t ret;
|
|
||||||
|
|
||||||
#ifdef DEBUG_OPBA
|
|
||||||
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
|
|
||||||
#endif
|
|
||||||
ret = opba_readb(opaque, addr) << 24;
|
|
||||||
ret |= opba_readb(opaque, addr + 1) << 16;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void opba_writel (void *opaque,
|
|
||||||
hwaddr addr, uint32_t value)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_OPBA
|
|
||||||
printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
|
|
||||||
value);
|
|
||||||
#endif
|
|
||||||
opba_writeb(opaque, addr, value >> 24);
|
|
||||||
opba_writeb(opaque, addr + 1, value >> 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const MemoryRegionOps opba_ops = {
|
static const MemoryRegionOps opba_ops = {
|
||||||
.old_mmio = {
|
.read = opba_readb,
|
||||||
.read = { opba_readb, opba_readw, opba_readl, },
|
.write = opba_writeb,
|
||||||
.write = { opba_writeb, opba_writew, opba_writel, },
|
.impl.min_access_size = 1,
|
||||||
},
|
.impl.max_access_size = 1,
|
||||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
.valid.min_access_size = 1,
|
||||||
|
.valid.max_access_size = 4,
|
||||||
|
.endianness = DEVICE_BIG_ENDIAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ppc4xx_opba_reset (void *opaque)
|
static void ppc4xx_opba_reset (void *opaque)
|
||||||
|
@ -750,65 +703,27 @@ struct ppc405_gpio_t {
|
||||||
uint32_t isr1l;
|
uint32_t isr1l;
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint32_t ppc405_gpio_readb (void *opaque, hwaddr addr)
|
static uint64_t ppc405_gpio_read(void *opaque, hwaddr addr, unsigned size)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_GPIO
|
#ifdef DEBUG_GPIO
|
||||||
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
|
printf("%s: addr " TARGET_FMT_plx " size %d\n", __func__, addr, size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ppc405_gpio_writeb (void *opaque,
|
static void ppc405_gpio_write(void *opaque, hwaddr addr, uint64_t value,
|
||||||
hwaddr addr, uint32_t value)
|
unsigned size)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_GPIO
|
#ifdef DEBUG_GPIO
|
||||||
printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
|
printf("%s: addr " TARGET_FMT_plx " size %d val %08" PRIx32 "\n",
|
||||||
value);
|
__func__, addr, size, value);
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t ppc405_gpio_readw (void *opaque, hwaddr addr)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_GPIO
|
|
||||||
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ppc405_gpio_writew (void *opaque,
|
|
||||||
hwaddr addr, uint32_t value)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_GPIO
|
|
||||||
printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
|
|
||||||
value);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t ppc405_gpio_readl (void *opaque, hwaddr addr)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_GPIO
|
|
||||||
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ppc405_gpio_writel (void *opaque,
|
|
||||||
hwaddr addr, uint32_t value)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_GPIO
|
|
||||||
printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
|
|
||||||
value);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static const MemoryRegionOps ppc405_gpio_ops = {
|
static const MemoryRegionOps ppc405_gpio_ops = {
|
||||||
.old_mmio = {
|
.read = ppc405_gpio_read,
|
||||||
.read = { ppc405_gpio_readb, ppc405_gpio_readw, ppc405_gpio_readl, },
|
.write = ppc405_gpio_write,
|
||||||
.write = { ppc405_gpio_writeb, ppc405_gpio_writew, ppc405_gpio_writel, },
|
|
||||||
},
|
|
||||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1017,44 +932,6 @@ struct ppc4xx_gpt_t {
|
||||||
uint32_t mask[5];
|
uint32_t mask[5];
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint32_t ppc4xx_gpt_readb (void *opaque, hwaddr addr)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_GPT
|
|
||||||
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
|
|
||||||
#endif
|
|
||||||
/* XXX: generate a bus fault */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ppc4xx_gpt_writeb (void *opaque,
|
|
||||||
hwaddr addr, uint32_t value)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_I2C
|
|
||||||
printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
|
|
||||||
value);
|
|
||||||
#endif
|
|
||||||
/* XXX: generate a bus fault */
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t ppc4xx_gpt_readw (void *opaque, hwaddr addr)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_GPT
|
|
||||||
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
|
|
||||||
#endif
|
|
||||||
/* XXX: generate a bus fault */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ppc4xx_gpt_writew (void *opaque,
|
|
||||||
hwaddr addr, uint32_t value)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_I2C
|
|
||||||
printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
|
|
||||||
value);
|
|
||||||
#endif
|
|
||||||
/* XXX: generate a bus fault */
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ppc4xx_gpt_compare (ppc4xx_gpt_t *gpt, int n)
|
static int ppc4xx_gpt_compare (ppc4xx_gpt_t *gpt, int n)
|
||||||
{
|
{
|
||||||
/* XXX: TODO */
|
/* XXX: TODO */
|
||||||
|
@ -1107,7 +984,7 @@ static void ppc4xx_gpt_compute_timer (ppc4xx_gpt_t *gpt)
|
||||||
/* XXX: TODO */
|
/* XXX: TODO */
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t ppc4xx_gpt_readl (void *opaque, hwaddr addr)
|
static uint64_t ppc4xx_gpt_read(void *opaque, hwaddr addr, unsigned size)
|
||||||
{
|
{
|
||||||
ppc4xx_gpt_t *gpt;
|
ppc4xx_gpt_t *gpt;
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
|
@ -1162,8 +1039,8 @@ static uint32_t ppc4xx_gpt_readl (void *opaque, hwaddr addr)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ppc4xx_gpt_writel (void *opaque,
|
static void ppc4xx_gpt_write(void *opaque, hwaddr addr, uint64_t value,
|
||||||
hwaddr addr, uint32_t value)
|
unsigned size)
|
||||||
{
|
{
|
||||||
ppc4xx_gpt_t *gpt;
|
ppc4xx_gpt_t *gpt;
|
||||||
int idx;
|
int idx;
|
||||||
|
@ -1225,10 +1102,10 @@ static void ppc4xx_gpt_writel (void *opaque,
|
||||||
}
|
}
|
||||||
|
|
||||||
static const MemoryRegionOps gpt_ops = {
|
static const MemoryRegionOps gpt_ops = {
|
||||||
.old_mmio = {
|
.read = ppc4xx_gpt_read,
|
||||||
.read = { ppc4xx_gpt_readb, ppc4xx_gpt_readw, ppc4xx_gpt_readl, },
|
.write = ppc4xx_gpt_write,
|
||||||
.write = { ppc4xx_gpt_writeb, ppc4xx_gpt_writew, ppc4xx_gpt_writel, },
|
.valid.min_access_size = 4,
|
||||||
},
|
.valid.max_access_size = 4,
|
||||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
111
hw/ppc/prep.c
111
hw/ppc/prep.c
|
@ -78,94 +78,6 @@ static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
|
||||||
/* ISA IO ports bridge */
|
/* ISA IO ports bridge */
|
||||||
#define PPC_IO_BASE 0x80000000
|
#define PPC_IO_BASE 0x80000000
|
||||||
|
|
||||||
/* PowerPC control and status registers */
|
|
||||||
#if 0 // Not used
|
|
||||||
static struct {
|
|
||||||
/* IDs */
|
|
||||||
uint32_t veni_devi;
|
|
||||||
uint32_t revi;
|
|
||||||
/* Control and status */
|
|
||||||
uint32_t gcsr;
|
|
||||||
uint32_t xcfr;
|
|
||||||
uint32_t ct32;
|
|
||||||
uint32_t mcsr;
|
|
||||||
/* General purpose registers */
|
|
||||||
uint32_t gprg[6];
|
|
||||||
/* Exceptions */
|
|
||||||
uint32_t feen;
|
|
||||||
uint32_t fest;
|
|
||||||
uint32_t fema;
|
|
||||||
uint32_t fecl;
|
|
||||||
uint32_t eeen;
|
|
||||||
uint32_t eest;
|
|
||||||
uint32_t eecl;
|
|
||||||
uint32_t eeint;
|
|
||||||
uint32_t eemck0;
|
|
||||||
uint32_t eemck1;
|
|
||||||
/* Error diagnostic */
|
|
||||||
} XCSR;
|
|
||||||
|
|
||||||
static void PPC_XCSR_writeb (void *opaque,
|
|
||||||
hwaddr addr, uint32_t value)
|
|
||||||
{
|
|
||||||
printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr,
|
|
||||||
value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PPC_XCSR_writew (void *opaque,
|
|
||||||
hwaddr addr, uint32_t value)
|
|
||||||
{
|
|
||||||
printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr,
|
|
||||||
value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PPC_XCSR_writel (void *opaque,
|
|
||||||
hwaddr addr, uint32_t value)
|
|
||||||
{
|
|
||||||
printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr,
|
|
||||||
value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t PPC_XCSR_readb (void *opaque, hwaddr addr)
|
|
||||||
{
|
|
||||||
uint32_t retval = 0;
|
|
||||||
|
|
||||||
printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr,
|
|
||||||
retval);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t PPC_XCSR_readw (void *opaque, hwaddr addr)
|
|
||||||
{
|
|
||||||
uint32_t retval = 0;
|
|
||||||
|
|
||||||
printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr,
|
|
||||||
retval);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t PPC_XCSR_readl (void *opaque, hwaddr addr)
|
|
||||||
{
|
|
||||||
uint32_t retval = 0;
|
|
||||||
|
|
||||||
printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr,
|
|
||||||
retval);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const MemoryRegionOps PPC_XCSR_ops = {
|
|
||||||
.old_mmio = {
|
|
||||||
.read = { PPC_XCSR_readb, PPC_XCSR_readw, PPC_XCSR_readl, },
|
|
||||||
.write = { PPC_XCSR_writeb, PPC_XCSR_writew, PPC_XCSR_writel, },
|
|
||||||
},
|
|
||||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Fake super-io ports for PREP platform (Intel 82378ZB) */
|
/* Fake super-io ports for PREP platform (Intel 82378ZB) */
|
||||||
typedef struct sysctrl_t {
|
typedef struct sysctrl_t {
|
||||||
qemu_irq reset_irq;
|
qemu_irq reset_irq;
|
||||||
|
@ -648,11 +560,10 @@ static void ppc_prep_init(MachineState *machine)
|
||||||
portio_list_init(&prep_port_list, NULL, prep_portio_list, sysctrl, "prep");
|
portio_list_init(&prep_port_list, NULL, prep_portio_list, sysctrl, "prep");
|
||||||
portio_list_add(&prep_port_list, isa_address_space_io(isa), 0x0);
|
portio_list_add(&prep_port_list, isa_address_space_io(isa), 0x0);
|
||||||
|
|
||||||
/* PowerPC control and status register group */
|
/*
|
||||||
#if 0
|
* PowerPC control and status register group: unimplemented,
|
||||||
memory_region_init_io(xcsr, NULL, &PPC_XCSR_ops, NULL, "ppc-xcsr", 0x1000);
|
* would be at address 0xFEFF0000.
|
||||||
memory_region_add_subregion(sysmem, 0xFEFF0000, xcsr);
|
*/
|
||||||
#endif
|
|
||||||
|
|
||||||
if (machine_usb(machine)) {
|
if (machine_usb(machine)) {
|
||||||
pci_create_simple(pci_bus, -1, "pci-ohci");
|
pci_create_simple(pci_bus, -1, "pci-ohci");
|
||||||
|
@ -676,6 +587,7 @@ static void ppc_prep_init(MachineState *machine)
|
||||||
|
|
||||||
static void prep_machine_init(MachineClass *mc)
|
static void prep_machine_init(MachineClass *mc)
|
||||||
{
|
{
|
||||||
|
mc->deprecation_reason = "use 40p machine type instead";
|
||||||
mc->desc = "PowerPC PREP platform";
|
mc->desc = "PowerPC PREP platform";
|
||||||
mc->init = ppc_prep_init;
|
mc->init = ppc_prep_init;
|
||||||
mc->block_default_type = IF_IDE;
|
mc->block_default_type = IF_IDE;
|
||||||
|
@ -706,7 +618,7 @@ static void ibm_40p_init(MachineState *machine)
|
||||||
uint16_t cmos_checksum;
|
uint16_t cmos_checksum;
|
||||||
PowerPCCPU *cpu;
|
PowerPCCPU *cpu;
|
||||||
DeviceState *dev;
|
DeviceState *dev;
|
||||||
SysBusDevice *pcihost;
|
SysBusDevice *pcihost, *s;
|
||||||
Nvram *m48t59 = NULL;
|
Nvram *m48t59 = NULL;
|
||||||
PCIBus *pci_bus;
|
PCIBus *pci_bus;
|
||||||
ISABus *isa_bus;
|
ISABus *isa_bus;
|
||||||
|
@ -799,7 +711,16 @@ static void ibm_40p_init(MachineState *machine)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare firmware configuration for OpenBIOS */
|
/* Prepare firmware configuration for OpenBIOS */
|
||||||
fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2);
|
dev = qdev_create(NULL, TYPE_FW_CFG_MEM);
|
||||||
|
fw_cfg = FW_CFG(dev);
|
||||||
|
qdev_prop_set_uint32(dev, "data_width", 1);
|
||||||
|
qdev_prop_set_bit(dev, "dma_enabled", false);
|
||||||
|
object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG,
|
||||||
|
OBJECT(fw_cfg), NULL);
|
||||||
|
qdev_init_nofail(dev);
|
||||||
|
s = SYS_BUS_DEVICE(dev);
|
||||||
|
sysbus_mmio_map(s, 0, CFG_ADDR);
|
||||||
|
sysbus_mmio_map(s, 1, CFG_ADDR + 2);
|
||||||
|
|
||||||
if (machine->kernel_filename) {
|
if (machine->kernel_filename) {
|
||||||
/* load kernel */
|
/* load kernel */
|
||||||
|
|
229
hw/ppc/spapr.c
229
hw/ppc/spapr.c
|
@ -54,7 +54,6 @@
|
||||||
#include "hw/ppc/spapr.h"
|
#include "hw/ppc/spapr.h"
|
||||||
#include "hw/ppc/spapr_vio.h"
|
#include "hw/ppc/spapr_vio.h"
|
||||||
#include "hw/pci-host/spapr.h"
|
#include "hw/pci-host/spapr.h"
|
||||||
#include "hw/ppc/xics.h"
|
|
||||||
#include "hw/pci/msi.h"
|
#include "hw/pci/msi.h"
|
||||||
|
|
||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
|
@ -117,33 +116,6 @@ static bool spapr_is_thread0_in_vcore(sPAPRMachineState *spapr,
|
||||||
return spapr_get_vcpu_id(cpu) % spapr->vsmt == 0;
|
return spapr_get_vcpu_id(cpu) % spapr->vsmt == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ICSState *spapr_ics_create(sPAPRMachineState *spapr,
|
|
||||||
const char *type_ics,
|
|
||||||
int nr_irqs, Error **errp)
|
|
||||||
{
|
|
||||||
Error *local_err = NULL;
|
|
||||||
Object *obj;
|
|
||||||
|
|
||||||
obj = object_new(type_ics);
|
|
||||||
object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort);
|
|
||||||
object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr),
|
|
||||||
&error_abort);
|
|
||||||
object_property_set_int(obj, nr_irqs, "nr-irqs", &local_err);
|
|
||||||
if (local_err) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
object_property_set_bool(obj, true, "realized", &local_err);
|
|
||||||
if (local_err) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ICS_BASE(obj);
|
|
||||||
|
|
||||||
error:
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool pre_2_10_vmstate_dummy_icp_needed(void *opaque)
|
static bool pre_2_10_vmstate_dummy_icp_needed(void *opaque)
|
||||||
{
|
{
|
||||||
/* Dummy entries correspond to unused ICPState objects in older QEMUs,
|
/* Dummy entries correspond to unused ICPState objects in older QEMUs,
|
||||||
|
@ -184,38 +156,6 @@ static int xics_max_server_number(sPAPRMachineState *spapr)
|
||||||
return DIV_ROUND_UP(max_cpus * spapr->vsmt, smp_threads);
|
return DIV_ROUND_UP(max_cpus * spapr->vsmt, smp_threads);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp)
|
|
||||||
{
|
|
||||||
sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
|
|
||||||
Error *local_err = NULL;
|
|
||||||
|
|
||||||
if (kvm_enabled()) {
|
|
||||||
if (machine_kernel_irqchip_allowed(machine) &&
|
|
||||||
!xics_kvm_init(spapr, &local_err)) {
|
|
||||||
spapr->icp_type = TYPE_KVM_ICP;
|
|
||||||
spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs,
|
|
||||||
&local_err);
|
|
||||||
}
|
|
||||||
if (machine_kernel_irqchip_required(machine) && !spapr->ics) {
|
|
||||||
error_prepend(&local_err,
|
|
||||||
"kernel_irqchip requested but unavailable: ");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
error_free(local_err);
|
|
||||||
local_err = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!spapr->ics) {
|
|
||||||
xics_spapr_init(spapr);
|
|
||||||
spapr->icp_type = TYPE_ICP;
|
|
||||||
spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs,
|
|
||||||
&local_err);
|
|
||||||
}
|
|
||||||
|
|
||||||
error:
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu,
|
static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu,
|
||||||
int smt_threads)
|
int smt_threads)
|
||||||
{
|
{
|
||||||
|
@ -1636,6 +1576,10 @@ static void spapr_machine_reset(void)
|
||||||
ppc_set_compat(first_ppc_cpu, spapr->max_compat_pvr, &error_fatal);
|
ppc_set_compat(first_ppc_cpu, spapr->max_compat_pvr, &error_fatal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) {
|
||||||
|
spapr_irq_msi_reset(spapr);
|
||||||
|
}
|
||||||
|
|
||||||
qemu_devices_reset();
|
qemu_devices_reset();
|
||||||
|
|
||||||
/* DRC reset may cause a device to be unplugged. This will cause troubles
|
/* DRC reset may cause a device to be unplugged. This will cause troubles
|
||||||
|
@ -1910,6 +1854,24 @@ static const VMStateDescription vmstate_spapr_patb_entry = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool spapr_irq_map_needed(void *opaque)
|
||||||
|
{
|
||||||
|
sPAPRMachineState *spapr = opaque;
|
||||||
|
|
||||||
|
return spapr->irq_map && !bitmap_empty(spapr->irq_map, spapr->irq_map_nr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_spapr_irq_map = {
|
||||||
|
.name = "spapr_irq_map",
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.needed = spapr_irq_map_needed,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_BITMAP(irq_map, sPAPRMachineState, 0, irq_map_nr),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
static const VMStateDescription vmstate_spapr = {
|
static const VMStateDescription vmstate_spapr = {
|
||||||
.name = "spapr",
|
.name = "spapr",
|
||||||
.version_id = 3,
|
.version_id = 3,
|
||||||
|
@ -1937,6 +1899,7 @@ static const VMStateDescription vmstate_spapr = {
|
||||||
&vmstate_spapr_cap_cfpc,
|
&vmstate_spapr_cap_cfpc,
|
||||||
&vmstate_spapr_cap_sbbc,
|
&vmstate_spapr_cap_sbbc,
|
||||||
&vmstate_spapr_cap_ibs,
|
&vmstate_spapr_cap_ibs,
|
||||||
|
&vmstate_spapr_irq_map,
|
||||||
NULL
|
NULL
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2590,7 +2553,7 @@ static void spapr_machine_init(MachineState *machine)
|
||||||
load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD;
|
load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD;
|
||||||
|
|
||||||
/* Set up Interrupt Controller before we create the VCPUs */
|
/* Set up Interrupt Controller before we create the VCPUs */
|
||||||
xics_system_init(machine, XICS_IRQS_SPAPR, &error_fatal);
|
smc->irq->init(spapr, &error_fatal);
|
||||||
|
|
||||||
/* Set up containers for ibm,client-architecture-support negotiated options
|
/* Set up containers for ibm,client-architecture-support negotiated options
|
||||||
*/
|
*/
|
||||||
|
@ -3782,121 +3745,13 @@ static ICPState *spapr_icp_get(XICSFabric *xi, int vcpu_id)
|
||||||
return cpu ? ICP(cpu->intc) : NULL;
|
return cpu ? ICP(cpu->intc) : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ICS_IRQ_FREE(ics, srcno) \
|
|
||||||
(!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK)))
|
|
||||||
|
|
||||||
static int ics_find_free_block(ICSState *ics, int num, int alignnum)
|
|
||||||
{
|
|
||||||
int first, i;
|
|
||||||
|
|
||||||
for (first = 0; first < ics->nr_irqs; first += alignnum) {
|
|
||||||
if (num > (ics->nr_irqs - first)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
for (i = first; i < first + num; ++i) {
|
|
||||||
if (!ICS_IRQ_FREE(ics, i)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i == (first + num)) {
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, Error **errp)
|
|
||||||
{
|
|
||||||
ICSState *ics = spapr->ics;
|
|
||||||
int first = -1;
|
|
||||||
|
|
||||||
assert(ics);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* MSIMesage::data is used for storing VIRQ so
|
|
||||||
* it has to be aligned to num to support multiple
|
|
||||||
* MSI vectors. MSI-X is not affected by this.
|
|
||||||
* The hint is used for the first IRQ, the rest should
|
|
||||||
* be allocated continuously.
|
|
||||||
*/
|
|
||||||
if (align) {
|
|
||||||
assert((num == 1) || (num == 2) || (num == 4) ||
|
|
||||||
(num == 8) || (num == 16) || (num == 32));
|
|
||||||
first = ics_find_free_block(ics, num, num);
|
|
||||||
} else {
|
|
||||||
first = ics_find_free_block(ics, num, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first < 0) {
|
|
||||||
error_setg(errp, "can't find a free %d-IRQ block", num);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return first + ics->offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp)
|
|
||||||
{
|
|
||||||
ICSState *ics = spapr->ics;
|
|
||||||
|
|
||||||
assert(ics);
|
|
||||||
|
|
||||||
if (!ics_valid_irq(ics, irq)) {
|
|
||||||
error_setg(errp, "IRQ %d is invalid", irq);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ICS_IRQ_FREE(ics, irq - ics->offset)) {
|
|
||||||
error_setg(errp, "IRQ %d is not free", irq);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ics_set_irq_type(ics, irq - ics->offset, lsi);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num)
|
|
||||||
{
|
|
||||||
ICSState *ics = spapr->ics;
|
|
||||||
int srcno = irq - ics->offset;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (ics_valid_irq(ics, irq)) {
|
|
||||||
trace_spapr_irq_free(0, irq, num);
|
|
||||||
for (i = srcno; i < srcno + num; ++i) {
|
|
||||||
if (ICS_IRQ_FREE(ics, i)) {
|
|
||||||
trace_spapr_irq_free_warn(0, i + ics->offset);
|
|
||||||
}
|
|
||||||
memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq)
|
|
||||||
{
|
|
||||||
ICSState *ics = spapr->ics;
|
|
||||||
|
|
||||||
if (ics_valid_irq(ics, irq)) {
|
|
||||||
return ics->qirqs[irq - ics->offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void spapr_pic_print_info(InterruptStatsProvider *obj,
|
static void spapr_pic_print_info(InterruptStatsProvider *obj,
|
||||||
Monitor *mon)
|
Monitor *mon)
|
||||||
{
|
{
|
||||||
sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
|
sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
|
||||||
CPUState *cs;
|
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
|
||||||
|
|
||||||
CPU_FOREACH(cs) {
|
smc->irq->print_info(spapr, mon);
|
||||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
|
||||||
|
|
||||||
icp_pic_print_info(ICP(cpu->intc), mon);
|
|
||||||
}
|
|
||||||
|
|
||||||
ics_pic_print_info(spapr->ics, mon);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int spapr_get_vcpu_id(PowerPCCPU *cpu)
|
int spapr_get_vcpu_id(PowerPCCPU *cpu)
|
||||||
|
@ -4009,6 +3864,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
|
||||||
smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN;
|
smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN;
|
||||||
smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 16; /* 64kiB */
|
smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 16; /* 64kiB */
|
||||||
spapr_caps_add_properties(smc, &error_abort);
|
spapr_caps_add_properties(smc, &error_abort);
|
||||||
|
smc->irq = &spapr_irq_xics;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo spapr_machine_info = {
|
static const TypeInfo spapr_machine_info = {
|
||||||
|
@ -4060,18 +3916,41 @@ static const TypeInfo spapr_machine_info = {
|
||||||
type_init(spapr_machine_register_##suffix)
|
type_init(spapr_machine_register_##suffix)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pseries-3.0
|
* pseries-3.1
|
||||||
*/
|
*/
|
||||||
static void spapr_machine_3_0_instance_options(MachineState *machine)
|
static void spapr_machine_3_1_instance_options(MachineState *machine)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spapr_machine_3_0_class_options(MachineClass *mc)
|
static void spapr_machine_3_1_class_options(MachineClass *mc)
|
||||||
{
|
{
|
||||||
/* Defaults for the latest behaviour inherited from the base class */
|
/* Defaults for the latest behaviour inherited from the base class */
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_SPAPR_MACHINE(3_0, "3.0", true);
|
DEFINE_SPAPR_MACHINE(3_1, "3.1", true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pseries-3.0
|
||||||
|
*/
|
||||||
|
#define SPAPR_COMPAT_3_0 \
|
||||||
|
HW_COMPAT_3_0
|
||||||
|
|
||||||
|
static void spapr_machine_3_0_instance_options(MachineState *machine)
|
||||||
|
{
|
||||||
|
spapr_machine_3_1_instance_options(machine);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spapr_machine_3_0_class_options(MachineClass *mc)
|
||||||
|
{
|
||||||
|
sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
|
||||||
|
|
||||||
|
spapr_machine_3_1_class_options(mc);
|
||||||
|
SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_3_0);
|
||||||
|
|
||||||
|
smc->legacy_irq_allocation = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_SPAPR_MACHINE(3_0, "3.0", false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pseries-2.12
|
* pseries-2.12
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "hw/ppc/spapr_cpu_core.h"
|
#include "hw/ppc/spapr_cpu_core.h"
|
||||||
#include "target/ppc/cpu.h"
|
#include "target/ppc/cpu.h"
|
||||||
#include "hw/ppc/spapr.h"
|
#include "hw/ppc/spapr.h"
|
||||||
|
#include "hw/ppc/xics.h" /* for icp_create() - to be removed */
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "sysemu/cpus.h"
|
#include "sysemu/cpus.h"
|
||||||
|
@ -113,26 +114,6 @@ const char *spapr_get_cpu_core_type(const char *cpu_type)
|
||||||
return object_class_get_name(oc);
|
return object_class_get_name(oc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spapr_unrealize_vcpu(PowerPCCPU *cpu)
|
|
||||||
{
|
|
||||||
qemu_unregister_reset(spapr_cpu_reset, cpu);
|
|
||||||
object_unparent(cpu->intc);
|
|
||||||
cpu_remove_sync(CPU(cpu));
|
|
||||||
object_unparent(OBJECT(cpu));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void spapr_cpu_core_unrealize(DeviceState *dev, Error **errp)
|
|
||||||
{
|
|
||||||
sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev));
|
|
||||||
CPUCore *cc = CPU_CORE(dev);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < cc->nr_threads; i++) {
|
|
||||||
spapr_unrealize_vcpu(sc->threads[i]);
|
|
||||||
}
|
|
||||||
g_free(sc->threads);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool slb_shadow_needed(void *opaque)
|
static bool slb_shadow_needed(void *opaque)
|
||||||
{
|
{
|
||||||
sPAPRCPUState *spapr_cpu = opaque;
|
sPAPRCPUState *spapr_cpu = opaque;
|
||||||
|
@ -207,10 +188,34 @@ static const VMStateDescription vmstate_spapr_cpu_state = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void spapr_unrealize_vcpu(PowerPCCPU *cpu, sPAPRCPUCore *sc)
|
||||||
|
{
|
||||||
|
if (!sc->pre_3_0_migration) {
|
||||||
|
vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data);
|
||||||
|
}
|
||||||
|
qemu_unregister_reset(spapr_cpu_reset, cpu);
|
||||||
|
object_unparent(cpu->intc);
|
||||||
|
cpu_remove_sync(CPU(cpu));
|
||||||
|
object_unparent(OBJECT(cpu));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spapr_cpu_core_unrealize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev));
|
||||||
|
CPUCore *cc = CPU_CORE(dev);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cc->nr_threads; i++) {
|
||||||
|
spapr_unrealize_vcpu(sc->threads[i], sc);
|
||||||
|
}
|
||||||
|
g_free(sc->threads);
|
||||||
|
}
|
||||||
|
|
||||||
static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||||
Error **errp)
|
sPAPRCPUCore *sc, Error **errp)
|
||||||
{
|
{
|
||||||
CPUPPCState *env = &cpu->env;
|
CPUPPCState *env = &cpu->env;
|
||||||
|
CPUState *cs = CPU(cpu);
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
|
object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
|
||||||
|
@ -233,6 +238,11 @@ static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||||
goto error_unregister;
|
goto error_unregister;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!sc->pre_3_0_migration) {
|
||||||
|
vmstate_register(NULL, cs->cpu_index, &vmstate_spapr_cpu_state,
|
||||||
|
cpu->machine_data);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error_unregister:
|
error_unregister:
|
||||||
|
@ -272,10 +282,6 @@ static PowerPCCPU *spapr_create_vcpu(sPAPRCPUCore *sc, int i, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu->machine_data = g_new0(sPAPRCPUState, 1);
|
cpu->machine_data = g_new0(sPAPRCPUState, 1);
|
||||||
if (!sc->pre_3_0_migration) {
|
|
||||||
vmstate_register(NULL, cs->cpu_index, &vmstate_spapr_cpu_state,
|
|
||||||
cpu->machine_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
object_unref(obj);
|
object_unref(obj);
|
||||||
return cpu;
|
return cpu;
|
||||||
|
@ -290,9 +296,6 @@ static void spapr_delete_vcpu(PowerPCCPU *cpu, sPAPRCPUCore *sc)
|
||||||
{
|
{
|
||||||
sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu);
|
sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu);
|
||||||
|
|
||||||
if (!sc->pre_3_0_migration) {
|
|
||||||
vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data);
|
|
||||||
}
|
|
||||||
cpu->machine_data = NULL;
|
cpu->machine_data = NULL;
|
||||||
g_free(spapr_cpu);
|
g_free(spapr_cpu);
|
||||||
object_unparent(OBJECT(cpu));
|
object_unparent(OBJECT(cpu));
|
||||||
|
@ -325,7 +328,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0; j < cc->nr_threads; j++) {
|
for (j = 0; j < cc->nr_threads; j++) {
|
||||||
spapr_realize_vcpu(sc->threads[j], spapr, &local_err);
|
spapr_realize_vcpu(sc->threads[j], spapr, sc, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
goto err_unrealize;
|
goto err_unrealize;
|
||||||
}
|
}
|
||||||
|
@ -334,7 +337,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
|
||||||
|
|
||||||
err_unrealize:
|
err_unrealize:
|
||||||
while (--j >= 0) {
|
while (--j >= 0) {
|
||||||
spapr_unrealize_vcpu(sc->threads[j]);
|
spapr_unrealize_vcpu(sc->threads[j], sc);
|
||||||
}
|
}
|
||||||
err:
|
err:
|
||||||
while (--i >= 0) {
|
while (--i >= 0) {
|
||||||
|
|
|
@ -707,9 +707,11 @@ void spapr_clear_pending_events(sPAPRMachineState *spapr)
|
||||||
|
|
||||||
void spapr_events_init(sPAPRMachineState *spapr)
|
void spapr_events_init(sPAPRMachineState *spapr)
|
||||||
{
|
{
|
||||||
int epow_irq;
|
int epow_irq = SPAPR_IRQ_EPOW;
|
||||||
|
|
||||||
|
if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) {
|
||||||
epow_irq = spapr_irq_findone(spapr, &error_fatal);
|
epow_irq = spapr_irq_findone(spapr, &error_fatal);
|
||||||
|
}
|
||||||
|
|
||||||
spapr_irq_claim(spapr, epow_irq, false, &error_fatal);
|
spapr_irq_claim(spapr, epow_irq, false, &error_fatal);
|
||||||
|
|
||||||
|
@ -729,9 +731,11 @@ void spapr_events_init(sPAPRMachineState *spapr)
|
||||||
* checking that it's enabled.
|
* checking that it's enabled.
|
||||||
*/
|
*/
|
||||||
if (spapr->use_hotplug_event_source) {
|
if (spapr->use_hotplug_event_source) {
|
||||||
int hp_irq;
|
int hp_irq = SPAPR_IRQ_HOTPLUG;
|
||||||
|
|
||||||
|
if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) {
|
||||||
hp_irq = spapr_irq_findone(spapr, &error_fatal);
|
hp_irq = spapr_irq_findone(spapr, &error_fatal);
|
||||||
|
}
|
||||||
|
|
||||||
spapr_irq_claim(spapr, hp_irq, false, &error_fatal);
|
spapr_irq_claim(spapr, hp_irq, false, &error_fatal);
|
||||||
|
|
||||||
|
|
286
hw/ppc/spapr_irq.c
Normal file
286
hw/ppc/spapr_irq.c
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
/*
|
||||||
|
* QEMU PowerPC sPAPR IRQ interface
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018, IBM Corporation.
|
||||||
|
*
|
||||||
|
* This code is licensed under the GPL version 2 or later. See the
|
||||||
|
* COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "hw/ppc/spapr.h"
|
||||||
|
#include "hw/ppc/xics.h"
|
||||||
|
#include "sysemu/kvm.h"
|
||||||
|
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
void spapr_irq_msi_init(sPAPRMachineState *spapr, uint32_t nr_msis)
|
||||||
|
{
|
||||||
|
spapr->irq_map_nr = nr_msis;
|
||||||
|
spapr->irq_map = bitmap_new(spapr->irq_map_nr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int spapr_irq_msi_alloc(sPAPRMachineState *spapr, uint32_t num, bool align,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The 'align_mask' parameter of bitmap_find_next_zero_area()
|
||||||
|
* should be one less than a power of 2; 0 means no
|
||||||
|
* alignment. Adapt the 'align' value of the former allocator
|
||||||
|
* to fit the requirements of bitmap_find_next_zero_area()
|
||||||
|
*/
|
||||||
|
align -= 1;
|
||||||
|
|
||||||
|
irq = bitmap_find_next_zero_area(spapr->irq_map, spapr->irq_map_nr, 0, num,
|
||||||
|
align);
|
||||||
|
if (irq == spapr->irq_map_nr) {
|
||||||
|
error_setg(errp, "can't find a free %d-IRQ block", num);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap_set(spapr->irq_map, irq, num);
|
||||||
|
|
||||||
|
return irq + SPAPR_IRQ_MSI;
|
||||||
|
}
|
||||||
|
|
||||||
|
void spapr_irq_msi_free(sPAPRMachineState *spapr, int irq, uint32_t num)
|
||||||
|
{
|
||||||
|
bitmap_clear(spapr->irq_map, irq - SPAPR_IRQ_MSI, num);
|
||||||
|
}
|
||||||
|
|
||||||
|
void spapr_irq_msi_reset(sPAPRMachineState *spapr)
|
||||||
|
{
|
||||||
|
bitmap_clear(spapr->irq_map, 0, spapr->irq_map_nr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XICS IRQ backend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static ICSState *spapr_ics_create(sPAPRMachineState *spapr,
|
||||||
|
const char *type_ics,
|
||||||
|
int nr_irqs, Error **errp)
|
||||||
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
|
Object *obj;
|
||||||
|
|
||||||
|
obj = object_new(type_ics);
|
||||||
|
object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort);
|
||||||
|
object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr),
|
||||||
|
&error_abort);
|
||||||
|
object_property_set_int(obj, nr_irqs, "nr-irqs", &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
object_property_set_bool(obj, true, "realized", &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ICS_BASE(obj);
|
||||||
|
|
||||||
|
error:
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spapr_irq_init_xics(sPAPRMachineState *spapr, Error **errp)
|
||||||
|
{
|
||||||
|
MachineState *machine = MACHINE(spapr);
|
||||||
|
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
|
||||||
|
int nr_irqs = smc->irq->nr_irqs;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
/* Initialize the MSI IRQ allocator. */
|
||||||
|
if (!SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) {
|
||||||
|
spapr_irq_msi_init(spapr, XICS_IRQ_BASE + nr_irqs - SPAPR_IRQ_MSI);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kvm_enabled()) {
|
||||||
|
if (machine_kernel_irqchip_allowed(machine) &&
|
||||||
|
!xics_kvm_init(spapr, &local_err)) {
|
||||||
|
spapr->icp_type = TYPE_KVM_ICP;
|
||||||
|
spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs,
|
||||||
|
&local_err);
|
||||||
|
}
|
||||||
|
if (machine_kernel_irqchip_required(machine) && !spapr->ics) {
|
||||||
|
error_prepend(&local_err,
|
||||||
|
"kernel_irqchip requested but unavailable: ");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
error_free(local_err);
|
||||||
|
local_err = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!spapr->ics) {
|
||||||
|
xics_spapr_init(spapr);
|
||||||
|
spapr->icp_type = TYPE_ICP;
|
||||||
|
spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs,
|
||||||
|
&local_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ICS_IRQ_FREE(ics, srcno) \
|
||||||
|
(!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK)))
|
||||||
|
|
||||||
|
static int spapr_irq_claim_xics(sPAPRMachineState *spapr, int irq, bool lsi,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
ICSState *ics = spapr->ics;
|
||||||
|
|
||||||
|
assert(ics);
|
||||||
|
|
||||||
|
if (!ics_valid_irq(ics, irq)) {
|
||||||
|
error_setg(errp, "IRQ %d is invalid", irq);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ICS_IRQ_FREE(ics, irq - ics->offset)) {
|
||||||
|
error_setg(errp, "IRQ %d is not free", irq);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ics_set_irq_type(ics, irq - ics->offset, lsi);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spapr_irq_free_xics(sPAPRMachineState *spapr, int irq, int num)
|
||||||
|
{
|
||||||
|
ICSState *ics = spapr->ics;
|
||||||
|
uint32_t srcno = irq - ics->offset;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (ics_valid_irq(ics, irq)) {
|
||||||
|
trace_spapr_irq_free(0, irq, num);
|
||||||
|
for (i = srcno; i < srcno + num; ++i) {
|
||||||
|
if (ICS_IRQ_FREE(ics, i)) {
|
||||||
|
trace_spapr_irq_free_warn(0, i);
|
||||||
|
}
|
||||||
|
memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static qemu_irq spapr_qirq_xics(sPAPRMachineState *spapr, int irq)
|
||||||
|
{
|
||||||
|
ICSState *ics = spapr->ics;
|
||||||
|
uint32_t srcno = irq - ics->offset;
|
||||||
|
|
||||||
|
if (ics_valid_irq(ics, irq)) {
|
||||||
|
return ics->qirqs[srcno];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spapr_irq_print_info_xics(sPAPRMachineState *spapr, Monitor *mon)
|
||||||
|
{
|
||||||
|
CPUState *cs;
|
||||||
|
|
||||||
|
CPU_FOREACH(cs) {
|
||||||
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
|
||||||
|
icp_pic_print_info(ICP(cpu->intc), mon);
|
||||||
|
}
|
||||||
|
|
||||||
|
ics_pic_print_info(spapr->ics, mon);
|
||||||
|
}
|
||||||
|
|
||||||
|
sPAPRIrq spapr_irq_xics = {
|
||||||
|
.nr_irqs = XICS_IRQS_SPAPR,
|
||||||
|
|
||||||
|
.init = spapr_irq_init_xics,
|
||||||
|
.claim = spapr_irq_claim_xics,
|
||||||
|
.free = spapr_irq_free_xics,
|
||||||
|
.qirq = spapr_qirq_xics,
|
||||||
|
.print_info = spapr_irq_print_info_xics,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sPAPR IRQ frontend routines for devices
|
||||||
|
*/
|
||||||
|
|
||||||
|
int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp)
|
||||||
|
{
|
||||||
|
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
|
||||||
|
|
||||||
|
return smc->irq->claim(spapr, irq, lsi, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num)
|
||||||
|
{
|
||||||
|
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
|
||||||
|
|
||||||
|
smc->irq->free(spapr, irq, num);
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq)
|
||||||
|
{
|
||||||
|
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
|
||||||
|
|
||||||
|
return smc->irq->qirq(spapr, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XICS legacy routines - to deprecate one day
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int ics_find_free_block(ICSState *ics, int num, int alignnum)
|
||||||
|
{
|
||||||
|
int first, i;
|
||||||
|
|
||||||
|
for (first = 0; first < ics->nr_irqs; first += alignnum) {
|
||||||
|
if (num > (ics->nr_irqs - first)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (i = first; i < first + num; ++i) {
|
||||||
|
if (!ICS_IRQ_FREE(ics, i)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == (first + num)) {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, Error **errp)
|
||||||
|
{
|
||||||
|
ICSState *ics = spapr->ics;
|
||||||
|
int first = -1;
|
||||||
|
|
||||||
|
assert(ics);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MSIMesage::data is used for storing VIRQ so
|
||||||
|
* it has to be aligned to num to support multiple
|
||||||
|
* MSI vectors. MSI-X is not affected by this.
|
||||||
|
* The hint is used for the first IRQ, the rest should
|
||||||
|
* be allocated continuously.
|
||||||
|
*/
|
||||||
|
if (align) {
|
||||||
|
assert((num == 1) || (num == 2) || (num == 4) ||
|
||||||
|
(num == 8) || (num == 16) || (num == 32));
|
||||||
|
first = ics_find_free_block(ics, num, num);
|
||||||
|
} else {
|
||||||
|
first = ics_find_free_block(ics, num, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first < 0) {
|
||||||
|
error_setg(errp, "can't find a free %d-IRQ block", num);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return first + ics->offset;
|
||||||
|
}
|
|
@ -267,6 +267,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||||
target_ulong args, uint32_t nret,
|
target_ulong args, uint32_t nret,
|
||||||
target_ulong rets)
|
target_ulong rets)
|
||||||
{
|
{
|
||||||
|
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
|
||||||
uint32_t config_addr = rtas_ld(args, 0);
|
uint32_t config_addr = rtas_ld(args, 0);
|
||||||
uint64_t buid = rtas_ldq(args, 1);
|
uint64_t buid = rtas_ldq(args, 1);
|
||||||
unsigned int func = rtas_ld(args, 3);
|
unsigned int func = rtas_ld(args, 3);
|
||||||
|
@ -334,6 +335,9 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!smc->legacy_irq_allocation) {
|
||||||
|
spapr_irq_msi_free(spapr, msi->first_irq, msi->num);
|
||||||
|
}
|
||||||
spapr_irq_free(spapr, msi->first_irq, msi->num);
|
spapr_irq_free(spapr, msi->first_irq, msi->num);
|
||||||
if (msi_present(pdev)) {
|
if (msi_present(pdev)) {
|
||||||
spapr_msi_setmsg(pdev, 0, false, 0, 0);
|
spapr_msi_setmsg(pdev, 0, false, 0, 0);
|
||||||
|
@ -372,7 +376,13 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate MSIs */
|
/* Allocate MSIs */
|
||||||
irq = spapr_irq_find(spapr, req_num, ret_intr_type == RTAS_TYPE_MSI, &err);
|
if (smc->legacy_irq_allocation) {
|
||||||
|
irq = spapr_irq_find(spapr, req_num, ret_intr_type == RTAS_TYPE_MSI,
|
||||||
|
&err);
|
||||||
|
} else {
|
||||||
|
irq = spapr_irq_msi_alloc(spapr, req_num,
|
||||||
|
ret_intr_type == RTAS_TYPE_MSI, &err);
|
||||||
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
error_reportf_err(err, "Can't allocate MSIs for device %x: ",
|
error_reportf_err(err, "Can't allocate MSIs for device %x: ",
|
||||||
config_addr);
|
config_addr);
|
||||||
|
@ -392,6 +402,9 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||||
|
|
||||||
/* Release previous MSIs */
|
/* Release previous MSIs */
|
||||||
if (msi) {
|
if (msi) {
|
||||||
|
if (!smc->legacy_irq_allocation) {
|
||||||
|
spapr_irq_msi_free(spapr, msi->first_irq, msi->num);
|
||||||
|
}
|
||||||
spapr_irq_free(spapr, msi->first_irq, msi->num);
|
spapr_irq_free(spapr, msi->first_irq, msi->num);
|
||||||
g_hash_table_remove(phb->msi, &config_addr);
|
g_hash_table_remove(phb->msi, &config_addr);
|
||||||
}
|
}
|
||||||
|
@ -1546,6 +1559,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
|
||||||
sPAPRMachineState *spapr =
|
sPAPRMachineState *spapr =
|
||||||
(sPAPRMachineState *) object_dynamic_cast(qdev_get_machine(),
|
(sPAPRMachineState *) object_dynamic_cast(qdev_get_machine(),
|
||||||
TYPE_SPAPR_MACHINE);
|
TYPE_SPAPR_MACHINE);
|
||||||
|
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
|
||||||
SysBusDevice *s = SYS_BUS_DEVICE(dev);
|
SysBusDevice *s = SYS_BUS_DEVICE(dev);
|
||||||
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s);
|
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s);
|
||||||
PCIHostState *phb = PCI_HOST_BRIDGE(s);
|
PCIHostState *phb = PCI_HOST_BRIDGE(s);
|
||||||
|
@ -1563,7 +1577,6 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sphb->index != (uint32_t)-1) {
|
if (sphb->index != (uint32_t)-1) {
|
||||||
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
|
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
smc->phb_placement(spapr, sphb->index,
|
smc->phb_placement(spapr, sphb->index,
|
||||||
|
@ -1705,15 +1718,17 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
|
||||||
|
|
||||||
/* Initialize the LSI table */
|
/* Initialize the LSI table */
|
||||||
for (i = 0; i < PCI_NUM_PINS; i++) {
|
for (i = 0; i < PCI_NUM_PINS; i++) {
|
||||||
uint32_t irq;
|
uint32_t irq = SPAPR_IRQ_PCI_LSI + sphb->index * PCI_NUM_PINS + i;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
if (smc->legacy_irq_allocation) {
|
||||||
irq = spapr_irq_findone(spapr, &local_err);
|
irq = spapr_irq_findone(spapr, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
error_prepend(errp, "can't allocate LSIs: ");
|
error_prepend(errp, "can't allocate LSIs: ");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spapr_irq_claim(spapr, irq, true, &local_err);
|
spapr_irq_claim(spapr, irq, true, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
|
@ -2123,6 +2138,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
|
||||||
_FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof_ranges));
|
_FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof_ranges));
|
||||||
_FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg)));
|
_FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg)));
|
||||||
_FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1));
|
_FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1));
|
||||||
|
/* TODO: fine tune the total count of allocatable MSIs per PHB */
|
||||||
_FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS_SPAPR));
|
_FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS_SPAPR));
|
||||||
|
|
||||||
/* Dynamic DMA window */
|
/* Dynamic DMA window */
|
||||||
|
|
|
@ -37,12 +37,13 @@
|
||||||
|
|
||||||
#include "hw/ppc/spapr.h"
|
#include "hw/ppc/spapr.h"
|
||||||
#include "hw/ppc/spapr_vio.h"
|
#include "hw/ppc/spapr_vio.h"
|
||||||
#include "hw/ppc/xics.h"
|
|
||||||
#include "hw/ppc/fdt.h"
|
#include "hw/ppc/fdt.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
#include <libfdt.h>
|
#include <libfdt.h>
|
||||||
|
|
||||||
|
#define SPAPR_VIO_REG_BASE 0x71000000
|
||||||
|
|
||||||
static void spapr_vio_get_irq(Object *obj, Visitor *v, const char *name,
|
static void spapr_vio_get_irq(Object *obj, Visitor *v, const char *name,
|
||||||
void *opaque, Error **errp)
|
void *opaque, Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -445,6 +446,55 @@ static void spapr_vio_busdev_reset(DeviceState *qdev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The register property of a VIO device is defined in livirt using
|
||||||
|
* 0x1000 as a base register number plus a 0x1000 increment. For the
|
||||||
|
* VIO tty device, the base number is changed to 0x30000000. QEMU uses
|
||||||
|
* a base register number of 0x71000000 and then a simple increment.
|
||||||
|
*
|
||||||
|
* The formula below tries to compute a unique index number from the
|
||||||
|
* register value that will be used to define the IRQ number of the
|
||||||
|
* VIO device.
|
||||||
|
*
|
||||||
|
* A maximum of 256 VIO devices is covered. Collisions are possible
|
||||||
|
* but they will be detected when the IRQ is claimed.
|
||||||
|
*/
|
||||||
|
static inline uint32_t spapr_vio_reg_to_irq(uint32_t reg)
|
||||||
|
{
|
||||||
|
uint32_t irq;
|
||||||
|
|
||||||
|
if (reg >= SPAPR_VIO_REG_BASE) {
|
||||||
|
/*
|
||||||
|
* VIO device register values when allocated by QEMU. For
|
||||||
|
* these, we simply mask the high bits to fit the overall
|
||||||
|
* range: [0x00 - 0xff].
|
||||||
|
*
|
||||||
|
* The nvram VIO device (reg=0x71000000) is a static device of
|
||||||
|
* the pseries machine and so is always allocated by QEMU. Its
|
||||||
|
* IRQ number is 0x0.
|
||||||
|
*/
|
||||||
|
irq = reg & 0xff;
|
||||||
|
|
||||||
|
} else if (reg >= 0x30000000) {
|
||||||
|
/*
|
||||||
|
* VIO tty devices register values, when allocated by livirt,
|
||||||
|
* are mapped in range [0xf0 - 0xff], gives us a maximum of 16
|
||||||
|
* vtys.
|
||||||
|
*/
|
||||||
|
irq = 0xf0 | ((reg >> 12) & 0xf);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Other VIO devices register values, when allocated by
|
||||||
|
* livirt, should be mapped in range [0x00 - 0xef]. Conflicts
|
||||||
|
* will be detected when IRQ is claimed.
|
||||||
|
*/
|
||||||
|
irq = (reg >> 12) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SPAPR_IRQ_VIO | irq;
|
||||||
|
}
|
||||||
|
|
||||||
static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
|
static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
|
||||||
{
|
{
|
||||||
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||||
|
@ -485,12 +535,16 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dev->irq) {
|
if (!dev->irq) {
|
||||||
|
dev->irq = spapr_vio_reg_to_irq(dev->reg);
|
||||||
|
|
||||||
|
if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) {
|
||||||
dev->irq = spapr_irq_findone(spapr, &local_err);
|
dev->irq = spapr_irq_findone(spapr, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spapr_irq_claim(spapr, dev->irq, false, &local_err);
|
spapr_irq_claim(spapr, dev->irq, false, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
|
@ -557,7 +611,7 @@ VIOsPAPRBus *spapr_vio_bus_init(void)
|
||||||
/* Create bus on bridge device */
|
/* Create bus on bridge device */
|
||||||
qbus = qbus_create(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio");
|
qbus = qbus_create(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio");
|
||||||
bus = SPAPR_VIO_BUS(qbus);
|
bus = SPAPR_VIO_BUS(qbus);
|
||||||
bus->next_reg = 0x71000000;
|
bus->next_reg = SPAPR_VIO_REG_BASE;
|
||||||
|
|
||||||
/* hcall-vio */
|
/* hcall-vio */
|
||||||
spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal);
|
spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal);
|
||||||
|
|
|
@ -1136,6 +1136,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
|
||||||
info.iova_pgsizes = 4096;
|
info.iova_pgsizes = 4096;
|
||||||
}
|
}
|
||||||
vfio_host_win_add(container, 0, (hwaddr)-1, info.iova_pgsizes);
|
vfio_host_win_add(container, 0, (hwaddr)-1, info.iova_pgsizes);
|
||||||
|
container->pgsizes = info.iova_pgsizes;
|
||||||
} else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU) ||
|
} else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU) ||
|
||||||
ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU)) {
|
ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU)) {
|
||||||
struct vfio_iommu_spapr_tce_info info;
|
struct vfio_iommu_spapr_tce_info info;
|
||||||
|
@ -1200,6 +1201,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2) {
|
if (v2) {
|
||||||
|
container->pgsizes = info.ddw.pgsizes;
|
||||||
/*
|
/*
|
||||||
* There is a default window in just created container.
|
* There is a default window in just created container.
|
||||||
* To make region_add/del simpler, we better remove this
|
* To make region_add/del simpler, we better remove this
|
||||||
|
@ -1214,6 +1216,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* The default table uses 4K pages */
|
/* The default table uses 4K pages */
|
||||||
|
container->pgsizes = 0x1000;
|
||||||
vfio_host_win_add(container, info.dma32_window_start,
|
vfio_host_win_add(container, info.dma32_window_start,
|
||||||
info.dma32_window_start +
|
info.dma32_window_start +
|
||||||
info.dma32_window_size - 1,
|
info.dma32_window_size - 1,
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include "hw/vfio/vfio-common.h"
|
#include "hw/vfio/vfio-common.h"
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
|
#include "exec/ram_addr.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
@ -144,9 +145,27 @@ int vfio_spapr_create_window(VFIOContainer *container,
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr);
|
IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr);
|
||||||
unsigned pagesize = memory_region_iommu_get_min_page_size(iommu_mr);
|
uint64_t pagesize = memory_region_iommu_get_min_page_size(iommu_mr);
|
||||||
unsigned entries, pages;
|
unsigned entries, pages;
|
||||||
struct vfio_iommu_spapr_tce_create create = { .argsz = sizeof(create) };
|
struct vfio_iommu_spapr_tce_create create = { .argsz = sizeof(create) };
|
||||||
|
long systempagesize = qemu_getrampagesize();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The host might not support the guest supported IOMMU page size,
|
||||||
|
* so we will use smaller physical IOMMU pages to back them.
|
||||||
|
*/
|
||||||
|
if (pagesize > systempagesize) {
|
||||||
|
pagesize = systempagesize;
|
||||||
|
}
|
||||||
|
pagesize = 1ULL << (63 - clz64(container->pgsizes &
|
||||||
|
(pagesize | (pagesize - 1))));
|
||||||
|
if (!pagesize) {
|
||||||
|
error_report("Host doesn't support page size 0x%"PRIx64
|
||||||
|
", the supported mask is 0x%lx",
|
||||||
|
memory_region_iommu_get_min_page_size(iommu_mr),
|
||||||
|
container->pgsizes);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FIXME: For VFIO iommu types which have KVM acceleration to
|
* FIXME: For VFIO iommu types which have KVM acceleration to
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
#include "qemu/units.h"
|
#include "qemu/units.h"
|
||||||
#include "sysemu/dma.h"
|
#include "sysemu/dma.h"
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "hw/ppc/xics.h"
|
|
||||||
#include "hw/ppc/spapr_drc.h"
|
#include "hw/ppc/spapr_drc.h"
|
||||||
#include "hw/mem/pc-dimm.h"
|
#include "hw/mem/pc-dimm.h"
|
||||||
#include "hw/ppc/spapr_ovec.h"
|
#include "hw/ppc/spapr_ovec.h"
|
||||||
|
#include "hw/ppc/spapr_irq.h"
|
||||||
|
|
||||||
struct VIOsPAPRBus;
|
struct VIOsPAPRBus;
|
||||||
struct sPAPRPHBState;
|
struct sPAPRPHBState;
|
||||||
|
@ -15,6 +15,7 @@ struct sPAPRNVRAM;
|
||||||
typedef struct sPAPREventLogEntry sPAPREventLogEntry;
|
typedef struct sPAPREventLogEntry sPAPREventLogEntry;
|
||||||
typedef struct sPAPREventSource sPAPREventSource;
|
typedef struct sPAPREventSource sPAPREventSource;
|
||||||
typedef struct sPAPRPendingHPT sPAPRPendingHPT;
|
typedef struct sPAPRPendingHPT sPAPRPendingHPT;
|
||||||
|
typedef struct ICSState ICSState;
|
||||||
|
|
||||||
#define HPTE64_V_HPTE_DIRTY 0x0000000000000040ULL
|
#define HPTE64_V_HPTE_DIRTY 0x0000000000000040ULL
|
||||||
#define SPAPR_ENTRY_POINT 0x100
|
#define SPAPR_ENTRY_POINT 0x100
|
||||||
|
@ -101,12 +102,15 @@ struct sPAPRMachineClass {
|
||||||
bool dr_lmb_enabled; /* enable dynamic-reconfig/hotplug of LMBs */
|
bool dr_lmb_enabled; /* enable dynamic-reconfig/hotplug of LMBs */
|
||||||
bool use_ohci_by_default; /* use USB-OHCI instead of XHCI */
|
bool use_ohci_by_default; /* use USB-OHCI instead of XHCI */
|
||||||
bool pre_2_10_has_unused_icps;
|
bool pre_2_10_has_unused_icps;
|
||||||
|
bool legacy_irq_allocation;
|
||||||
|
|
||||||
void (*phb_placement)(sPAPRMachineState *spapr, uint32_t index,
|
void (*phb_placement)(sPAPRMachineState *spapr, uint32_t index,
|
||||||
uint64_t *buid, hwaddr *pio,
|
uint64_t *buid, hwaddr *pio,
|
||||||
hwaddr *mmio32, hwaddr *mmio64,
|
hwaddr *mmio32, hwaddr *mmio64,
|
||||||
unsigned n_dma, uint32_t *liobns, Error **errp);
|
unsigned n_dma, uint32_t *liobns, Error **errp);
|
||||||
sPAPRResizeHPT resize_hpt_default;
|
sPAPRResizeHPT resize_hpt_default;
|
||||||
sPAPRCapabilities default_caps;
|
sPAPRCapabilities default_caps;
|
||||||
|
sPAPRIrq *irq;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -167,6 +171,8 @@ struct sPAPRMachineState {
|
||||||
char *kvm_type;
|
char *kvm_type;
|
||||||
|
|
||||||
const char *icp_type;
|
const char *icp_type;
|
||||||
|
int32_t irq_map_nr;
|
||||||
|
unsigned long *irq_map;
|
||||||
|
|
||||||
bool cmd_line_caps[SPAPR_CAP_NUM];
|
bool cmd_line_caps[SPAPR_CAP_NUM];
|
||||||
sPAPRCapabilities def, eff, mig;
|
sPAPRCapabilities def, eff, mig;
|
||||||
|
@ -775,14 +781,6 @@ int spapr_get_vcpu_id(PowerPCCPU *cpu);
|
||||||
void spapr_set_vcpu_id(PowerPCCPU *cpu, int cpu_index, Error **errp);
|
void spapr_set_vcpu_id(PowerPCCPU *cpu, int cpu_index, Error **errp);
|
||||||
PowerPCCPU *spapr_find_cpu(int vcpu_id);
|
PowerPCCPU *spapr_find_cpu(int vcpu_id);
|
||||||
|
|
||||||
int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align,
|
|
||||||
Error **errp);
|
|
||||||
#define spapr_irq_findone(spapr, errp) spapr_irq_find(spapr, 1, false, errp)
|
|
||||||
int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp);
|
|
||||||
void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num);
|
|
||||||
qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq);
|
|
||||||
|
|
||||||
|
|
||||||
int spapr_caps_pre_load(void *opaque);
|
int spapr_caps_pre_load(void *opaque);
|
||||||
int spapr_caps_pre_save(void *opaque);
|
int spapr_caps_pre_save(void *opaque);
|
||||||
|
|
||||||
|
|
54
include/hw/ppc/spapr_irq.h
Normal file
54
include/hw/ppc/spapr_irq.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* QEMU PowerPC sPAPR IRQ backend definitions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018, IBM Corporation.
|
||||||
|
*
|
||||||
|
* This code is licensed under the GPL version 2 or later. See the
|
||||||
|
* COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HW_SPAPR_IRQ_H
|
||||||
|
#define HW_SPAPR_IRQ_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IRQ range offsets per device type
|
||||||
|
*/
|
||||||
|
#define SPAPR_IRQ_EPOW 0x1000 /* XICS_IRQ_BASE offset */
|
||||||
|
#define SPAPR_IRQ_HOTPLUG 0x1001
|
||||||
|
#define SPAPR_IRQ_VIO 0x1100 /* 256 VIO devices */
|
||||||
|
#define SPAPR_IRQ_PCI_LSI 0x1200 /* 32+ PHBs devices */
|
||||||
|
|
||||||
|
#define SPAPR_IRQ_MSI 0x1300 /* Offset of the dynamic range covered
|
||||||
|
* by the bitmap allocator */
|
||||||
|
|
||||||
|
typedef struct sPAPRMachineState sPAPRMachineState;
|
||||||
|
|
||||||
|
void spapr_irq_msi_init(sPAPRMachineState *spapr, uint32_t nr_msis);
|
||||||
|
int spapr_irq_msi_alloc(sPAPRMachineState *spapr, uint32_t num, bool align,
|
||||||
|
Error **errp);
|
||||||
|
void spapr_irq_msi_free(sPAPRMachineState *spapr, int irq, uint32_t num);
|
||||||
|
void spapr_irq_msi_reset(sPAPRMachineState *spapr);
|
||||||
|
|
||||||
|
typedef struct sPAPRIrq {
|
||||||
|
uint32_t nr_irqs;
|
||||||
|
|
||||||
|
void (*init)(sPAPRMachineState *spapr, Error **errp);
|
||||||
|
int (*claim)(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp);
|
||||||
|
void (*free)(sPAPRMachineState *spapr, int irq, int num);
|
||||||
|
qemu_irq (*qirq)(sPAPRMachineState *spapr, int irq);
|
||||||
|
void (*print_info)(sPAPRMachineState *spapr, Monitor *mon);
|
||||||
|
} sPAPRIrq;
|
||||||
|
|
||||||
|
extern sPAPRIrq spapr_irq_xics;
|
||||||
|
|
||||||
|
int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp);
|
||||||
|
void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num);
|
||||||
|
qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XICS legacy routines
|
||||||
|
*/
|
||||||
|
int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, Error **errp);
|
||||||
|
#define spapr_irq_findone(spapr, errp) spapr_irq_find(spapr, 1, false, errp)
|
||||||
|
|
||||||
|
#endif
|
|
@ -29,7 +29,6 @@
|
||||||
#define XICS_H
|
#define XICS_H
|
||||||
|
|
||||||
#include "hw/qdev.h"
|
#include "hw/qdev.h"
|
||||||
#include "target/ppc/cpu-qom.h"
|
|
||||||
|
|
||||||
#define XICS_IPI 0x2
|
#define XICS_IPI 0x2
|
||||||
#define XICS_BUID 0x1
|
#define XICS_BUID 0x1
|
||||||
|
|
|
@ -73,6 +73,7 @@ typedef struct VFIOContainer {
|
||||||
unsigned iommu_type;
|
unsigned iommu_type;
|
||||||
int error;
|
int error;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
|
unsigned long pgsizes;
|
||||||
/*
|
/*
|
||||||
* This assumes the host IOMMU can support only a single
|
* This assumes the host IOMMU can support only a single
|
||||||
* contiguous IOVA window. We may need to generalize that in
|
* contiguous IOVA window. We may need to generalize that in
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
|
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
|
||||||
implementation for certain IBM POWER hardware. The sources are at
|
implementation for certain IBM POWER hardware. The sources are at
|
||||||
https://github.com/aik/SLOF, and the image currently in qemu is
|
https://github.com/aik/SLOF, and the image currently in qemu is
|
||||||
built from git tag qemu-slof-20180621.
|
built from git tag qemu-slof-20180702.
|
||||||
|
|
||||||
- sgabios (the Serial Graphics Adapter option ROM) provides a means for
|
- sgabios (the Serial Graphics Adapter option ROM) provides a means for
|
||||||
legacy x86 software to communicate with an attached serial console as
|
legacy x86 software to communicate with an attached serial console as
|
||||||
|
|
BIN
pc-bios/slof.bin
BIN
pc-bios/slof.bin
Binary file not shown.
|
@ -195,6 +195,12 @@ support page sizes < 4096 any longer.
|
||||||
These machine types are very old and likely can not be used for live migration
|
These machine types are very old and likely can not be used for live migration
|
||||||
from old QEMU versions anymore. A newer machine type should be used instead.
|
from old QEMU versions anymore. A newer machine type should be used instead.
|
||||||
|
|
||||||
|
@subsection prep (PowerPC) (since 3.1)
|
||||||
|
|
||||||
|
This machine type uses an unmaintained firmware, broken in lots of ways,
|
||||||
|
and unable to start post-2004 operating systems. 40p machine type should be
|
||||||
|
used instead.
|
||||||
|
|
||||||
@section Device options
|
@section Device options
|
||||||
|
|
||||||
@subsection Block device options
|
@subsection Block device options
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7d37babcfa48a6eb08e726a8d13b745cb2eebe1c
|
Subproject commit 9b7ab2fa020341dee8bf9df6c9cf40003e0136df
|
|
@ -481,6 +481,11 @@ struct ppc_slb_t {
|
||||||
#define msr_ts ((env->msr >> MSR_TS1) & 3)
|
#define msr_ts ((env->msr >> MSR_TS1) & 3)
|
||||||
#define msr_tm ((env->msr >> MSR_TM) & 1)
|
#define msr_tm ((env->msr >> MSR_TM) & 1)
|
||||||
|
|
||||||
|
#define DBCR0_ICMP (1 << 27)
|
||||||
|
#define DBCR0_BRT (1 << 26)
|
||||||
|
#define DBSR_ICMP (1 << 27)
|
||||||
|
#define DBSR_BRT (1 << 26)
|
||||||
|
|
||||||
/* Hypervisor bit is more specific */
|
/* Hypervisor bit is more specific */
|
||||||
#if defined(TARGET_PPC64)
|
#if defined(TARGET_PPC64)
|
||||||
#define MSR_HVB (1ULL << MSR_SHV)
|
#define MSR_HVB (1ULL << MSR_SHV)
|
||||||
|
|
|
@ -348,19 +348,16 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||||
case POWERPC_EXCP_ITLB: /* Instruction TLB error */
|
case POWERPC_EXCP_ITLB: /* Instruction TLB error */
|
||||||
break;
|
break;
|
||||||
case POWERPC_EXCP_DEBUG: /* Debug interrupt */
|
case POWERPC_EXCP_DEBUG: /* Debug interrupt */
|
||||||
switch (excp_model) {
|
if (env->flags & POWERPC_FLAG_DE) {
|
||||||
case POWERPC_EXCP_BOOKE:
|
|
||||||
/* FIXME: choose one or the other based on CPU type */
|
/* FIXME: choose one or the other based on CPU type */
|
||||||
srr0 = SPR_BOOKE_DSRR0;
|
srr0 = SPR_BOOKE_DSRR0;
|
||||||
srr1 = SPR_BOOKE_DSRR1;
|
srr1 = SPR_BOOKE_DSRR1;
|
||||||
asrr0 = SPR_BOOKE_CSRR0;
|
asrr0 = SPR_BOOKE_CSRR0;
|
||||||
asrr1 = SPR_BOOKE_CSRR1;
|
asrr1 = SPR_BOOKE_CSRR1;
|
||||||
break;
|
/* DBSR already modified by caller */
|
||||||
default:
|
} else {
|
||||||
break;
|
cpu_abort(cs, "Debug exception triggered on unsupported model\n");
|
||||||
}
|
}
|
||||||
/* XXX: TODO */
|
|
||||||
cpu_abort(cs, "Debug exception is not implemented yet !\n");
|
|
||||||
break;
|
break;
|
||||||
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */
|
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */
|
||||||
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
|
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
|
||||||
|
|
|
@ -36,26 +36,72 @@ static inline float128 float128_snan_to_qnan(float128 x)
|
||||||
#define float32_snan_to_qnan(x) ((x) | 0x00400000)
|
#define float32_snan_to_qnan(x) ((x) | 0x00400000)
|
||||||
#define float16_snan_to_qnan(x) ((x) | 0x0200)
|
#define float16_snan_to_qnan(x) ((x) | 0x0200)
|
||||||
|
|
||||||
/*****************************************************************************/
|
static inline bool fp_exceptions_enabled(CPUPPCState *env)
|
||||||
/* Floating point operations helpers */
|
|
||||||
uint64_t helper_float32_to_float64(CPUPPCState *env, uint32_t arg)
|
|
||||||
{
|
{
|
||||||
CPU_FloatU f;
|
#ifdef CONFIG_USER_ONLY
|
||||||
CPU_DoubleU d;
|
return true;
|
||||||
|
#else
|
||||||
f.l = arg;
|
return (env->msr & ((1U << MSR_FE0) | (1U << MSR_FE1))) != 0;
|
||||||
d.d = float32_to_float64(f.f, &env->fp_status);
|
#endif
|
||||||
return d.ll;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t helper_float64_to_float32(CPUPPCState *env, uint64_t arg)
|
/*****************************************************************************/
|
||||||
{
|
/* Floating point operations helpers */
|
||||||
CPU_FloatU f;
|
|
||||||
CPU_DoubleU d;
|
|
||||||
|
|
||||||
d.ll = arg;
|
/*
|
||||||
f.f = float64_to_float32(d.d, &env->fp_status);
|
* This is the non-arithmatic conversion that happens e.g. on loads.
|
||||||
return f.l;
|
* In the Power ISA pseudocode, this is called DOUBLE.
|
||||||
|
*/
|
||||||
|
uint64_t helper_todouble(uint32_t arg)
|
||||||
|
{
|
||||||
|
uint32_t abs_arg = arg & 0x7fffffff;
|
||||||
|
uint64_t ret;
|
||||||
|
|
||||||
|
if (likely(abs_arg >= 0x00800000)) {
|
||||||
|
/* Normalized operand, or Inf, or NaN. */
|
||||||
|
ret = (uint64_t)extract32(arg, 30, 2) << 62;
|
||||||
|
ret |= ((extract32(arg, 30, 1) ^ 1) * (uint64_t)7) << 59;
|
||||||
|
ret |= (uint64_t)extract32(arg, 0, 30) << 29;
|
||||||
|
} else {
|
||||||
|
/* Zero or Denormalized operand. */
|
||||||
|
ret = (uint64_t)extract32(arg, 31, 1) << 63;
|
||||||
|
if (unlikely(abs_arg != 0)) {
|
||||||
|
/* Denormalized operand. */
|
||||||
|
int shift = clz32(abs_arg) - 9;
|
||||||
|
int exp = -126 - shift + 1023;
|
||||||
|
ret |= (uint64_t)exp << 52;
|
||||||
|
ret |= abs_arg << (shift + 29);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the non-arithmatic conversion that happens e.g. on stores.
|
||||||
|
* In the Power ISA pseudocode, this is called SINGLE.
|
||||||
|
*/
|
||||||
|
uint32_t helper_tosingle(uint64_t arg)
|
||||||
|
{
|
||||||
|
int exp = extract64(arg, 52, 11);
|
||||||
|
uint32_t ret;
|
||||||
|
|
||||||
|
if (likely(exp > 896)) {
|
||||||
|
/* No denormalization required (includes Inf, NaN). */
|
||||||
|
ret = extract64(arg, 62, 2) << 30;
|
||||||
|
ret |= extract64(arg, 29, 30);
|
||||||
|
} else {
|
||||||
|
/* Zero or Denormal result. If the exponent is in bounds for
|
||||||
|
* a single-precision denormal result, extract the proper bits.
|
||||||
|
* If the input is not zero, and the exponent is out of bounds,
|
||||||
|
* then the result is undefined; this underflows to zero.
|
||||||
|
*/
|
||||||
|
ret = extract64(arg, 63, 1) << 31;
|
||||||
|
if (unlikely(exp >= 874)) {
|
||||||
|
/* Denormal result. */
|
||||||
|
ret |= ((1ULL << 52) | extract64(arg, 0, 52)) >> (896 + 30 - exp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int ppc_float32_get_unbiased_exp(float32 f)
|
static inline int ppc_float32_get_unbiased_exp(float32 f)
|
||||||
|
@ -207,7 +253,7 @@ uint64_t float_invalid_op_excp(CPUPPCState *env, int op, int set_fpcc)
|
||||||
if (ve != 0) {
|
if (ve != 0) {
|
||||||
/* Update the floating-point enabled exception summary */
|
/* Update the floating-point enabled exception summary */
|
||||||
env->fpscr |= 1 << FPSCR_FEX;
|
env->fpscr |= 1 << FPSCR_FEX;
|
||||||
if (msr_fe0 != 0 || msr_fe1 != 0) {
|
if (fp_exceptions_enabled(env)) {
|
||||||
/* GETPC() works here because this is inline */
|
/* GETPC() works here because this is inline */
|
||||||
raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM,
|
raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM,
|
||||||
POWERPC_EXCP_FP | op, GETPC());
|
POWERPC_EXCP_FP | op, GETPC());
|
||||||
|
@ -225,7 +271,7 @@ static inline void float_zero_divide_excp(CPUPPCState *env, uintptr_t raddr)
|
||||||
if (fpscr_ze != 0) {
|
if (fpscr_ze != 0) {
|
||||||
/* Update the floating-point enabled exception summary */
|
/* Update the floating-point enabled exception summary */
|
||||||
env->fpscr |= 1 << FPSCR_FEX;
|
env->fpscr |= 1 << FPSCR_FEX;
|
||||||
if (msr_fe0 != 0 || msr_fe1 != 0) {
|
if (fp_exceptions_enabled(env)) {
|
||||||
raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM,
|
raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM,
|
||||||
POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX,
|
POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX,
|
||||||
raddr);
|
raddr);
|
||||||
|
@ -536,9 +582,7 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr)
|
||||||
int status = get_float_exception_flags(&env->fp_status);
|
int status = get_float_exception_flags(&env->fp_status);
|
||||||
bool inexact_happened = false;
|
bool inexact_happened = false;
|
||||||
|
|
||||||
if (status & float_flag_divbyzero) {
|
if (status & float_flag_overflow) {
|
||||||
float_zero_divide_excp(env, raddr);
|
|
||||||
} else if (status & float_flag_overflow) {
|
|
||||||
float_overflow_excp(env);
|
float_overflow_excp(env);
|
||||||
} else if (status & float_flag_underflow) {
|
} else if (status & float_flag_underflow) {
|
||||||
float_underflow_excp(env);
|
float_underflow_excp(env);
|
||||||
|
@ -555,7 +599,7 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr)
|
||||||
if (cs->exception_index == POWERPC_EXCP_PROGRAM &&
|
if (cs->exception_index == POWERPC_EXCP_PROGRAM &&
|
||||||
(env->error_code & POWERPC_EXCP_FP)) {
|
(env->error_code & POWERPC_EXCP_FP)) {
|
||||||
/* Differred floating-point exception after target FPR update */
|
/* Differred floating-point exception after target FPR update */
|
||||||
if (msr_fe0 != 0 || msr_fe1 != 0) {
|
if (fp_exceptions_enabled(env)) {
|
||||||
raise_exception_err_ra(env, cs->exception_index,
|
raise_exception_err_ra(env, cs->exception_index,
|
||||||
env->error_code, raddr);
|
env->error_code, raddr);
|
||||||
}
|
}
|
||||||
|
@ -580,102 +624,93 @@ void helper_reset_fpstatus(CPUPPCState *env)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fadd - fadd. */
|
/* fadd - fadd. */
|
||||||
uint64_t helper_fadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2)
|
float64 helper_fadd(CPUPPCState *env, float64 arg1, float64 arg2)
|
||||||
{
|
{
|
||||||
CPU_DoubleU farg1, farg2;
|
float64 ret = float64_add(arg1, arg2, &env->fp_status);
|
||||||
|
int status = get_float_exception_flags(&env->fp_status);
|
||||||
|
|
||||||
farg1.ll = arg1;
|
if (unlikely(status & float_flag_invalid)) {
|
||||||
farg2.ll = arg2;
|
if (float64_is_infinity(arg1) && float64_is_infinity(arg2)) {
|
||||||
|
|
||||||
if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) &&
|
|
||||||
float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) {
|
|
||||||
/* Magnitude subtraction of infinities */
|
/* Magnitude subtraction of infinities */
|
||||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1);
|
||||||
} else {
|
} else if (float64_is_signaling_nan(arg1, &env->fp_status) ||
|
||||||
if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) ||
|
float64_is_signaling_nan(arg2, &env->fp_status)) {
|
||||||
float64_is_signaling_nan(farg2.d, &env->fp_status))) {
|
|
||||||
/* sNaN addition */
|
/* sNaN addition */
|
||||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||||
}
|
}
|
||||||
farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return farg1.ll;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fsub - fsub. */
|
/* fsub - fsub. */
|
||||||
uint64_t helper_fsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2)
|
float64 helper_fsub(CPUPPCState *env, float64 arg1, float64 arg2)
|
||||||
{
|
{
|
||||||
CPU_DoubleU farg1, farg2;
|
float64 ret = float64_sub(arg1, arg2, &env->fp_status);
|
||||||
|
int status = get_float_exception_flags(&env->fp_status);
|
||||||
|
|
||||||
farg1.ll = arg1;
|
if (unlikely(status & float_flag_invalid)) {
|
||||||
farg2.ll = arg2;
|
if (float64_is_infinity(arg1) && float64_is_infinity(arg2)) {
|
||||||
|
|
||||||
if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) &&
|
|
||||||
float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) {
|
|
||||||
/* Magnitude subtraction of infinities */
|
/* Magnitude subtraction of infinities */
|
||||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1);
|
||||||
} else {
|
} else if (float64_is_signaling_nan(arg1, &env->fp_status) ||
|
||||||
if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) ||
|
float64_is_signaling_nan(arg2, &env->fp_status)) {
|
||||||
float64_is_signaling_nan(farg2.d, &env->fp_status))) {
|
/* sNaN addition */
|
||||||
/* sNaN subtraction */
|
|
||||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||||
}
|
}
|
||||||
farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return farg1.ll;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fmul - fmul. */
|
/* fmul - fmul. */
|
||||||
uint64_t helper_fmul(CPUPPCState *env, uint64_t arg1, uint64_t arg2)
|
float64 helper_fmul(CPUPPCState *env, float64 arg1, float64 arg2)
|
||||||
{
|
{
|
||||||
CPU_DoubleU farg1, farg2;
|
float64 ret = float64_mul(arg1, arg2, &env->fp_status);
|
||||||
|
int status = get_float_exception_flags(&env->fp_status);
|
||||||
|
|
||||||
farg1.ll = arg1;
|
if (unlikely(status & float_flag_invalid)) {
|
||||||
farg2.ll = arg2;
|
if ((float64_is_infinity(arg1) && float64_is_zero(arg2)) ||
|
||||||
|
(float64_is_zero(arg1) && float64_is_infinity(arg2))) {
|
||||||
if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) ||
|
|
||||||
(float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) {
|
|
||||||
/* Multiplication of zero by infinity */
|
/* Multiplication of zero by infinity */
|
||||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1);
|
||||||
} else {
|
} else if (float64_is_signaling_nan(arg1, &env->fp_status) ||
|
||||||
if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) ||
|
float64_is_signaling_nan(arg2, &env->fp_status)) {
|
||||||
float64_is_signaling_nan(farg2.d, &env->fp_status))) {
|
|
||||||
/* sNaN multiplication */
|
/* sNaN multiplication */
|
||||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||||
}
|
}
|
||||||
farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return farg1.ll;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fdiv - fdiv. */
|
/* fdiv - fdiv. */
|
||||||
uint64_t helper_fdiv(CPUPPCState *env, uint64_t arg1, uint64_t arg2)
|
float64 helper_fdiv(CPUPPCState *env, float64 arg1, float64 arg2)
|
||||||
{
|
{
|
||||||
CPU_DoubleU farg1, farg2;
|
float64 ret = float64_div(arg1, arg2, &env->fp_status);
|
||||||
|
int status = get_float_exception_flags(&env->fp_status);
|
||||||
|
|
||||||
farg1.ll = arg1;
|
if (unlikely(status)) {
|
||||||
farg2.ll = arg2;
|
if (status & float_flag_invalid) {
|
||||||
|
/* Determine what kind of invalid operation was seen. */
|
||||||
if (unlikely(float64_is_infinity(farg1.d) &&
|
if (float64_is_infinity(arg1) && float64_is_infinity(arg2)) {
|
||||||
float64_is_infinity(farg2.d))) {
|
|
||||||
/* Division of infinity by infinity */
|
/* Division of infinity by infinity */
|
||||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, 1);
|
||||||
} else if (unlikely(float64_is_zero(farg1.d) && float64_is_zero(farg2.d))) {
|
} else if (float64_is_zero(arg1) && float64_is_zero(arg2)) {
|
||||||
/* Division of zero by zero */
|
/* Division of zero by zero */
|
||||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, 1);
|
||||||
} else {
|
} else if (float64_is_signaling_nan(arg1, &env->fp_status) ||
|
||||||
if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) ||
|
float64_is_signaling_nan(arg2, &env->fp_status)) {
|
||||||
float64_is_signaling_nan(farg2.d, &env->fp_status))) {
|
|
||||||
/* sNaN division */
|
/* sNaN division */
|
||||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||||
}
|
}
|
||||||
farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status);
|
}
|
||||||
|
if (status & float_flag_divbyzero) {
|
||||||
|
float_zero_divide_excp(env, GETPC());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return farg1.ll;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -860,40 +895,48 @@ uint64_t helper_frsp(CPUPPCState *env, uint64_t arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fsqrt - fsqrt. */
|
/* fsqrt - fsqrt. */
|
||||||
uint64_t helper_fsqrt(CPUPPCState *env, uint64_t arg)
|
float64 helper_fsqrt(CPUPPCState *env, float64 arg)
|
||||||
{
|
{
|
||||||
CPU_DoubleU farg;
|
float64 ret = float64_sqrt(arg, &env->fp_status);
|
||||||
|
int status = get_float_exception_flags(&env->fp_status);
|
||||||
|
|
||||||
farg.ll = arg;
|
if (unlikely(status & float_flag_invalid)) {
|
||||||
|
if (unlikely(float64_is_any_nan(arg))) {
|
||||||
if (unlikely(float64_is_any_nan(farg.d))) {
|
if (unlikely(float64_is_signaling_nan(arg, &env->fp_status))) {
|
||||||
if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) {
|
/* sNaN square root */
|
||||||
/* sNaN reciprocal square root */
|
|
||||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||||
farg.ll = float64_snan_to_qnan(farg.ll);
|
|
||||||
}
|
}
|
||||||
} else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) {
|
|
||||||
/* Square root of a negative nonzero number */
|
|
||||||
farg.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1);
|
|
||||||
} else {
|
} else {
|
||||||
farg.d = float64_sqrt(farg.d, &env->fp_status);
|
/* Square root of a negative nonzero number */
|
||||||
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1);
|
||||||
}
|
}
|
||||||
return farg.ll;
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fre - fre. */
|
/* fre - fre. */
|
||||||
uint64_t helper_fre(CPUPPCState *env, uint64_t arg)
|
float64 helper_fre(CPUPPCState *env, float64 arg)
|
||||||
{
|
{
|
||||||
CPU_DoubleU farg;
|
/* "Estimate" the reciprocal with actual division. */
|
||||||
|
float64 ret = float64_div(float64_one, arg, &env->fp_status);
|
||||||
|
int status = get_float_exception_flags(&env->fp_status);
|
||||||
|
|
||||||
farg.ll = arg;
|
if (unlikely(status)) {
|
||||||
|
if (status & float_flag_invalid) {
|
||||||
if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) {
|
if (float64_is_signaling_nan(arg, &env->fp_status)) {
|
||||||
/* sNaN reciprocal */
|
/* sNaN reciprocal */
|
||||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||||
}
|
}
|
||||||
farg.d = float64_div(float64_one, farg.d, &env->fp_status);
|
}
|
||||||
return farg.d;
|
if (status & float_flag_divbyzero) {
|
||||||
|
float_zero_divide_excp(env, GETPC());
|
||||||
|
/* For FPSCR.ZE == 0, the result is 1/2. */
|
||||||
|
ret = float64_set_sign(float64_half, float64_is_neg(arg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fres - fres. */
|
/* fres - fres. */
|
||||||
|
@ -916,27 +959,30 @@ uint64_t helper_fres(CPUPPCState *env, uint64_t arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* frsqrte - frsqrte. */
|
/* frsqrte - frsqrte. */
|
||||||
uint64_t helper_frsqrte(CPUPPCState *env, uint64_t arg)
|
float64 helper_frsqrte(CPUPPCState *env, float64 arg)
|
||||||
{
|
{
|
||||||
CPU_DoubleU farg;
|
/* "Estimate" the reciprocal with actual division. */
|
||||||
|
float64 rets = float64_sqrt(arg, &env->fp_status);
|
||||||
|
float64 retd = float64_div(float64_one, rets, &env->fp_status);
|
||||||
|
int status = get_float_exception_flags(&env->fp_status);
|
||||||
|
|
||||||
farg.ll = arg;
|
if (unlikely(status)) {
|
||||||
|
if (status & float_flag_invalid) {
|
||||||
if (unlikely(float64_is_any_nan(farg.d))) {
|
if (float64_is_signaling_nan(arg, &env->fp_status)) {
|
||||||
if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) {
|
/* sNaN reciprocal */
|
||||||
/* sNaN reciprocal square root */
|
|
||||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||||
farg.ll = float64_snan_to_qnan(farg.ll);
|
|
||||||
}
|
|
||||||
} else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) {
|
|
||||||
/* Reciprocal square root of a negative nonzero number */
|
|
||||||
farg.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1);
|
|
||||||
} else {
|
} else {
|
||||||
farg.d = float64_sqrt(farg.d, &env->fp_status);
|
/* Square root of a negative nonzero number */
|
||||||
farg.d = float64_div(float64_one, farg.d, &env->fp_status);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (status & float_flag_divbyzero) {
|
||||||
|
/* Reciprocal of (square root of) zero. */
|
||||||
|
float_zero_divide_excp(env, GETPC());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return farg.ll;
|
return retd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fsel - fsel. */
|
/* fsel - fsel. */
|
||||||
|
@ -1919,6 +1965,9 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
|
||||||
tp##_is_signaling_nan(xb.fld, &tstat)) { \
|
tp##_is_signaling_nan(xb.fld, &tstat)) { \
|
||||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \
|
||||||
} \
|
} \
|
||||||
|
} \
|
||||||
|
if (unlikely(tstat.float_exception_flags & float_flag_divbyzero)) { \
|
||||||
|
float_zero_divide_excp(env, GETPC()); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
if (r2sp) { \
|
if (r2sp) { \
|
||||||
|
@ -1969,6 +2018,9 @@ void helper_xsdivqp(CPUPPCState *env, uint32_t opcode)
|
||||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (unlikely(tstat.float_exception_flags & float_flag_divbyzero)) {
|
||||||
|
float_zero_divide_excp(env, GETPC());
|
||||||
|
}
|
||||||
|
|
||||||
helper_compute_fprf_float128(env, xt.f128);
|
helper_compute_fprf_float128(env, xt.f128);
|
||||||
putVSR(rD(opcode) + 32, &xt, env);
|
putVSR(rD(opcode) + 32, &xt, env);
|
||||||
|
|
|
@ -61,8 +61,8 @@ DEF_HELPER_2(compute_fprf_float64, void, env, i64)
|
||||||
DEF_HELPER_3(store_fpscr, void, env, i64, i32)
|
DEF_HELPER_3(store_fpscr, void, env, i64, i32)
|
||||||
DEF_HELPER_2(fpscr_clrbit, void, env, i32)
|
DEF_HELPER_2(fpscr_clrbit, void, env, i32)
|
||||||
DEF_HELPER_2(fpscr_setbit, void, env, i32)
|
DEF_HELPER_2(fpscr_setbit, void, env, i32)
|
||||||
DEF_HELPER_2(float64_to_float32, i32, env, i64)
|
DEF_HELPER_FLAGS_1(todouble, TCG_CALL_NO_RWG_SE, i64, i32)
|
||||||
DEF_HELPER_2(float32_to_float64, i64, env, i32)
|
DEF_HELPER_FLAGS_1(tosingle, TCG_CALL_NO_RWG_SE, i32, i64)
|
||||||
|
|
||||||
DEF_HELPER_4(fcmpo, void, env, i64, i64, i32)
|
DEF_HELPER_4(fcmpo, void, env, i64, i64, i32)
|
||||||
DEF_HELPER_4(fcmpu, void, env, i64, i64, i32)
|
DEF_HELPER_4(fcmpu, void, env, i64, i64, i32)
|
||||||
|
@ -85,15 +85,15 @@ DEF_HELPER_2(friz, i64, env, i64)
|
||||||
DEF_HELPER_2(frip, i64, env, i64)
|
DEF_HELPER_2(frip, i64, env, i64)
|
||||||
DEF_HELPER_2(frim, i64, env, i64)
|
DEF_HELPER_2(frim, i64, env, i64)
|
||||||
|
|
||||||
DEF_HELPER_3(fadd, i64, env, i64, i64)
|
DEF_HELPER_3(fadd, f64, env, f64, f64)
|
||||||
DEF_HELPER_3(fsub, i64, env, i64, i64)
|
DEF_HELPER_3(fsub, f64, env, f64, f64)
|
||||||
DEF_HELPER_3(fmul, i64, env, i64, i64)
|
DEF_HELPER_3(fmul, f64, env, f64, f64)
|
||||||
DEF_HELPER_3(fdiv, i64, env, i64, i64)
|
DEF_HELPER_3(fdiv, f64, env, f64, f64)
|
||||||
DEF_HELPER_4(fmadd, i64, env, i64, i64, i64)
|
DEF_HELPER_4(fmadd, i64, env, i64, i64, i64)
|
||||||
DEF_HELPER_4(fmsub, i64, env, i64, i64, i64)
|
DEF_HELPER_4(fmsub, i64, env, i64, i64, i64)
|
||||||
DEF_HELPER_4(fnmadd, i64, env, i64, i64, i64)
|
DEF_HELPER_4(fnmadd, i64, env, i64, i64, i64)
|
||||||
DEF_HELPER_4(fnmsub, i64, env, i64, i64, i64)
|
DEF_HELPER_4(fnmsub, i64, env, i64, i64, i64)
|
||||||
DEF_HELPER_2(fsqrt, i64, env, i64)
|
DEF_HELPER_2(fsqrt, f64, env, f64)
|
||||||
DEF_HELPER_2(fre, i64, env, i64)
|
DEF_HELPER_2(fre, i64, env, i64)
|
||||||
DEF_HELPER_2(fres, i64, env, i64)
|
DEF_HELPER_2(fres, i64, env, i64)
|
||||||
DEF_HELPER_2(frsqrte, i64, env, i64)
|
DEF_HELPER_2(frsqrte, i64, env, i64)
|
||||||
|
|
|
@ -2671,16 +2671,14 @@ static int bcd_cmp_mag(ppc_avr_t *a, ppc_avr_t *b)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid,
|
static void bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid,
|
||||||
int *overflow)
|
int *overflow)
|
||||||
{
|
{
|
||||||
int carry = 0;
|
int carry = 0;
|
||||||
int i;
|
int i;
|
||||||
int is_zero = 1;
|
|
||||||
for (i = 1; i <= 31; i++) {
|
for (i = 1; i <= 31; i++) {
|
||||||
uint8_t digit = bcd_get_digit(a, i, invalid) +
|
uint8_t digit = bcd_get_digit(a, i, invalid) +
|
||||||
bcd_get_digit(b, i, invalid) + carry;
|
bcd_get_digit(b, i, invalid) + carry;
|
||||||
is_zero &= (digit == 0);
|
|
||||||
if (digit > 9) {
|
if (digit > 9) {
|
||||||
carry = 1;
|
carry = 1;
|
||||||
digit -= 10;
|
digit -= 10;
|
||||||
|
@ -2689,26 +2687,20 @@ static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid,
|
||||||
}
|
}
|
||||||
|
|
||||||
bcd_put_digit(t, digit, i);
|
bcd_put_digit(t, digit, i);
|
||||||
|
|
||||||
if (unlikely(*invalid)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*overflow = carry;
|
*overflow = carry;
|
||||||
return is_zero;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid,
|
static void bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid,
|
||||||
int *overflow)
|
int *overflow)
|
||||||
{
|
{
|
||||||
int carry = 0;
|
int carry = 0;
|
||||||
int i;
|
int i;
|
||||||
int is_zero = 1;
|
|
||||||
for (i = 1; i <= 31; i++) {
|
for (i = 1; i <= 31; i++) {
|
||||||
uint8_t digit = bcd_get_digit(a, i, invalid) -
|
uint8_t digit = bcd_get_digit(a, i, invalid) -
|
||||||
bcd_get_digit(b, i, invalid) + carry;
|
bcd_get_digit(b, i, invalid) + carry;
|
||||||
is_zero &= (digit == 0);
|
|
||||||
if (digit & 0x80) {
|
if (digit & 0x80) {
|
||||||
carry = -1;
|
carry = -1;
|
||||||
digit += 10;
|
digit += 10;
|
||||||
|
@ -2717,14 +2709,9 @@ static int bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid,
|
||||||
}
|
}
|
||||||
|
|
||||||
bcd_put_digit(t, digit, i);
|
bcd_put_digit(t, digit, i);
|
||||||
|
|
||||||
if (unlikely(*invalid)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*overflow = carry;
|
*overflow = carry;
|
||||||
return is_zero;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
|
uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
|
||||||
|
@ -2734,23 +2721,28 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
|
||||||
int sgnb = bcd_get_sgn(b);
|
int sgnb = bcd_get_sgn(b);
|
||||||
int invalid = (sgna == 0) || (sgnb == 0);
|
int invalid = (sgna == 0) || (sgnb == 0);
|
||||||
int overflow = 0;
|
int overflow = 0;
|
||||||
int zero = 0;
|
|
||||||
uint32_t cr = 0;
|
uint32_t cr = 0;
|
||||||
ppc_avr_t result = { .u64 = { 0, 0 } };
|
ppc_avr_t result = { .u64 = { 0, 0 } };
|
||||||
|
|
||||||
if (!invalid) {
|
if (!invalid) {
|
||||||
if (sgna == sgnb) {
|
if (sgna == sgnb) {
|
||||||
result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps);
|
result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps);
|
||||||
zero = bcd_add_mag(&result, a, b, &invalid, &overflow);
|
bcd_add_mag(&result, a, b, &invalid, &overflow);
|
||||||
cr = (sgna > 0) ? CRF_GT : CRF_LT;
|
cr = bcd_cmp_zero(&result);
|
||||||
} else if (bcd_cmp_mag(a, b) > 0) {
|
|
||||||
result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps);
|
|
||||||
zero = bcd_sub_mag(&result, a, b, &invalid, &overflow);
|
|
||||||
cr = (sgna > 0) ? CRF_GT : CRF_LT;
|
|
||||||
} else {
|
} else {
|
||||||
|
int magnitude = bcd_cmp_mag(a, b);
|
||||||
|
if (magnitude > 0) {
|
||||||
|
result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps);
|
||||||
|
bcd_sub_mag(&result, a, b, &invalid, &overflow);
|
||||||
|
cr = (sgna > 0) ? CRF_GT : CRF_LT;
|
||||||
|
} else if (magnitude < 0) {
|
||||||
result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgnb, ps);
|
result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgnb, ps);
|
||||||
zero = bcd_sub_mag(&result, b, a, &invalid, &overflow);
|
bcd_sub_mag(&result, b, a, &invalid, &overflow);
|
||||||
cr = (sgnb > 0) ? CRF_GT : CRF_LT;
|
cr = (sgnb > 0) ? CRF_GT : CRF_LT;
|
||||||
|
} else {
|
||||||
|
result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(0, ps);
|
||||||
|
cr = CRF_EQ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2759,8 +2751,6 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
|
||||||
cr = CRF_SO;
|
cr = CRF_SO;
|
||||||
} else if (overflow) {
|
} else if (overflow) {
|
||||||
cr |= CRF_SO;
|
cr |= CRF_SO;
|
||||||
} else if (zero) {
|
|
||||||
cr = CRF_EQ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*r = result;
|
*r = result;
|
||||||
|
|
|
@ -211,6 +211,7 @@ struct DisasContext {
|
||||||
bool gtse;
|
bool gtse;
|
||||||
ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
|
ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
|
||||||
int singlestep_enabled;
|
int singlestep_enabled;
|
||||||
|
uint32_t flags;
|
||||||
uint64_t insns_flags;
|
uint64_t insns_flags;
|
||||||
uint64_t insns_flags2;
|
uint64_t insns_flags2;
|
||||||
};
|
};
|
||||||
|
@ -251,6 +252,17 @@ struct opc_handler_t {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* SPR load/store helpers */
|
||||||
|
static inline void gen_load_spr(TCGv t, int reg)
|
||||||
|
{
|
||||||
|
tcg_gen_ld_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg]));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gen_store_spr(int reg, TCGv t)
|
||||||
|
{
|
||||||
|
tcg_gen_st_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg]));
|
||||||
|
}
|
||||||
|
|
||||||
static inline void gen_set_access_type(DisasContext *ctx, int access_type)
|
static inline void gen_set_access_type(DisasContext *ctx, int access_type)
|
||||||
{
|
{
|
||||||
if (ctx->need_access_type && ctx->access_type != access_type) {
|
if (ctx->need_access_type && ctx->access_type != access_type) {
|
||||||
|
@ -313,6 +325,38 @@ static void gen_exception_nip(DisasContext *ctx, uint32_t excp,
|
||||||
ctx->exception = (excp);
|
ctx->exception = (excp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Translates the EXCP_TRACE/BRANCH exceptions used on most PowerPCs to
|
||||||
|
* EXCP_DEBUG, if we are running on cores using the debug enable bit (e.g.
|
||||||
|
* BookE).
|
||||||
|
*/
|
||||||
|
static uint32_t gen_prep_dbgex(DisasContext *ctx, uint32_t excp)
|
||||||
|
{
|
||||||
|
if ((ctx->singlestep_enabled & CPU_SINGLE_STEP)
|
||||||
|
&& (excp == POWERPC_EXCP_BRANCH)) {
|
||||||
|
/* Trace excpt. has priority */
|
||||||
|
excp = POWERPC_EXCP_TRACE;
|
||||||
|
}
|
||||||
|
if (ctx->flags & POWERPC_FLAG_DE) {
|
||||||
|
target_ulong dbsr = 0;
|
||||||
|
switch (excp) {
|
||||||
|
case POWERPC_EXCP_TRACE:
|
||||||
|
dbsr = DBCR0_ICMP;
|
||||||
|
break;
|
||||||
|
case POWERPC_EXCP_BRANCH:
|
||||||
|
dbsr = DBCR0_BRT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
TCGv t0 = tcg_temp_new();
|
||||||
|
gen_load_spr(t0, SPR_BOOKE_DBSR);
|
||||||
|
tcg_gen_ori_tl(t0, t0, dbsr);
|
||||||
|
gen_store_spr(SPR_BOOKE_DBSR, t0);
|
||||||
|
tcg_temp_free(t0);
|
||||||
|
return POWERPC_EXCP_DEBUG;
|
||||||
|
} else {
|
||||||
|
return excp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void gen_debug_exception(DisasContext *ctx)
|
static void gen_debug_exception(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
TCGv_i32 t0;
|
TCGv_i32 t0;
|
||||||
|
@ -575,17 +619,6 @@ typedef struct opcode_t {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* SPR load/store helpers */
|
|
||||||
static inline void gen_load_spr(TCGv t, int reg)
|
|
||||||
{
|
|
||||||
tcg_gen_ld_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg]));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void gen_store_spr(int reg, TCGv t)
|
|
||||||
{
|
|
||||||
tcg_gen_st_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invalid instruction */
|
/* Invalid instruction */
|
||||||
static void gen_invalid(DisasContext *ctx)
|
static void gen_invalid(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
|
@ -3602,6 +3635,24 @@ static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gen_lookup_and_goto_ptr(DisasContext *ctx)
|
||||||
|
{
|
||||||
|
int sse = ctx->singlestep_enabled;
|
||||||
|
if (unlikely(sse)) {
|
||||||
|
if (sse & GDBSTUB_SINGLE_STEP) {
|
||||||
|
gen_debug_exception(ctx);
|
||||||
|
} else if (sse & (CPU_SINGLE_STEP | CPU_BRANCH_STEP)) {
|
||||||
|
uint32_t excp = gen_prep_dbgex(ctx, POWERPC_EXCP_BRANCH);
|
||||||
|
if (excp != POWERPC_EXCP_NONE) {
|
||||||
|
gen_exception(ctx, excp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tcg_gen_exit_tb(NULL, 0);
|
||||||
|
} else {
|
||||||
|
tcg_gen_lookup_and_goto_ptr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*** Branch ***/
|
/*** Branch ***/
|
||||||
static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
|
static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
|
||||||
{
|
{
|
||||||
|
@ -3614,18 +3665,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
|
||||||
tcg_gen_exit_tb(ctx->base.tb, n);
|
tcg_gen_exit_tb(ctx->base.tb, n);
|
||||||
} else {
|
} else {
|
||||||
tcg_gen_movi_tl(cpu_nip, dest & ~3);
|
tcg_gen_movi_tl(cpu_nip, dest & ~3);
|
||||||
if (unlikely(ctx->singlestep_enabled)) {
|
gen_lookup_and_goto_ptr(ctx);
|
||||||
if ((ctx->singlestep_enabled &
|
|
||||||
(CPU_BRANCH_STEP | CPU_SINGLE_STEP)) &&
|
|
||||||
(ctx->exception == POWERPC_EXCP_BRANCH ||
|
|
||||||
ctx->exception == POWERPC_EXCP_TRACE)) {
|
|
||||||
gen_exception_nip(ctx, POWERPC_EXCP_TRACE, dest);
|
|
||||||
}
|
|
||||||
if (ctx->singlestep_enabled & GDBSTUB_SINGLE_STEP) {
|
|
||||||
gen_debug_exception(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tcg_gen_lookup_and_goto_ptr();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3668,8 +3708,8 @@ static void gen_bcond(DisasContext *ctx, int type)
|
||||||
uint32_t bo = BO(ctx->opcode);
|
uint32_t bo = BO(ctx->opcode);
|
||||||
TCGLabel *l1;
|
TCGLabel *l1;
|
||||||
TCGv target;
|
TCGv target;
|
||||||
|
|
||||||
ctx->exception = POWERPC_EXCP_BRANCH;
|
ctx->exception = POWERPC_EXCP_BRANCH;
|
||||||
|
|
||||||
if (type == BCOND_LR || type == BCOND_CTR || type == BCOND_TAR) {
|
if (type == BCOND_LR || type == BCOND_CTR || type == BCOND_TAR) {
|
||||||
target = tcg_temp_local_new();
|
target = tcg_temp_local_new();
|
||||||
if (type == BCOND_CTR)
|
if (type == BCOND_CTR)
|
||||||
|
@ -3733,10 +3773,11 @@ static void gen_bcond(DisasContext *ctx, int type)
|
||||||
} else {
|
} else {
|
||||||
tcg_gen_andi_tl(cpu_nip, target, ~3);
|
tcg_gen_andi_tl(cpu_nip, target, ~3);
|
||||||
}
|
}
|
||||||
tcg_gen_lookup_and_goto_ptr();
|
gen_lookup_and_goto_ptr(ctx);
|
||||||
tcg_temp_free(target);
|
tcg_temp_free(target);
|
||||||
}
|
}
|
||||||
if ((bo & 0x14) != 0x14) {
|
if ((bo & 0x14) != 0x14) {
|
||||||
|
/* fallthrough case */
|
||||||
gen_set_label(l1);
|
gen_set_label(l1);
|
||||||
gen_goto_tb(ctx, 1, ctx->base.pc_next);
|
gen_goto_tb(ctx, 1, ctx->base.pc_next);
|
||||||
}
|
}
|
||||||
|
@ -7419,6 +7460,7 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
|
||||||
ctx->need_access_type = !(env->mmu_model & POWERPC_MMU_64B);
|
ctx->need_access_type = !(env->mmu_model & POWERPC_MMU_64B);
|
||||||
ctx->le_mode = !!(env->hflags & (1 << MSR_LE));
|
ctx->le_mode = !!(env->hflags & (1 << MSR_LE));
|
||||||
ctx->default_tcg_memop_mask = ctx->le_mode ? MO_LE : MO_BE;
|
ctx->default_tcg_memop_mask = ctx->le_mode ? MO_LE : MO_BE;
|
||||||
|
ctx->flags = env->flags;
|
||||||
#if defined(TARGET_PPC64)
|
#if defined(TARGET_PPC64)
|
||||||
ctx->sf_mode = msr_is_64bit(env, env->msr);
|
ctx->sf_mode = msr_is_64bit(env, env->msr);
|
||||||
ctx->has_cfar = !!(env->flags & POWERPC_FLAG_CFAR);
|
ctx->has_cfar = !!(env->flags & POWERPC_FLAG_CFAR);
|
||||||
|
@ -7455,6 +7497,17 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
|
||||||
ctx->singlestep_enabled = 0;
|
ctx->singlestep_enabled = 0;
|
||||||
if ((env->flags & POWERPC_FLAG_BE) && msr_be)
|
if ((env->flags & POWERPC_FLAG_BE) && msr_be)
|
||||||
ctx->singlestep_enabled |= CPU_BRANCH_STEP;
|
ctx->singlestep_enabled |= CPU_BRANCH_STEP;
|
||||||
|
if ((env->flags & POWERPC_FLAG_DE) && msr_de) {
|
||||||
|
ctx->singlestep_enabled = 0;
|
||||||
|
target_ulong dbcr0 = env->spr[SPR_BOOKE_DBCR0];
|
||||||
|
if (dbcr0 & DBCR0_ICMP) {
|
||||||
|
ctx->singlestep_enabled |= CPU_SINGLE_STEP;
|
||||||
|
}
|
||||||
|
if (dbcr0 & DBCR0_BRT) {
|
||||||
|
ctx->singlestep_enabled |= CPU_BRANCH_STEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
if (unlikely(ctx->base.singlestep_enabled)) {
|
if (unlikely(ctx->base.singlestep_enabled)) {
|
||||||
ctx->singlestep_enabled |= GDBSTUB_SINGLE_STEP;
|
ctx->singlestep_enabled |= GDBSTUB_SINGLE_STEP;
|
||||||
}
|
}
|
||||||
|
@ -7565,7 +7618,9 @@ static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
|
||||||
ctx->exception != POWERPC_SYSCALL &&
|
ctx->exception != POWERPC_SYSCALL &&
|
||||||
ctx->exception != POWERPC_EXCP_TRAP &&
|
ctx->exception != POWERPC_EXCP_TRAP &&
|
||||||
ctx->exception != POWERPC_EXCP_BRANCH)) {
|
ctx->exception != POWERPC_EXCP_BRANCH)) {
|
||||||
gen_exception_nip(ctx, POWERPC_EXCP_TRACE, ctx->base.pc_next);
|
uint32_t excp = gen_prep_dbgex(ctx, POWERPC_EXCP_TRACE);
|
||||||
|
if (excp != POWERPC_EXCP_NONE)
|
||||||
|
gen_exception_nip(ctx, excp, ctx->base.pc_next);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tcg_check_temp_count()) {
|
if (tcg_check_temp_count()) {
|
||||||
|
|
|
@ -660,15 +660,12 @@ GEN_LDUF(name, ldop, op | 0x21, type); \
|
||||||
GEN_LDUXF(name, ldop, op | 0x01, type); \
|
GEN_LDUXF(name, ldop, op | 0x01, type); \
|
||||||
GEN_LDXF(name, ldop, 0x17, op | 0x00, type)
|
GEN_LDXF(name, ldop, 0x17, op | 0x00, type)
|
||||||
|
|
||||||
static inline void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2)
|
static void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 dest, TCGv addr)
|
||||||
{
|
{
|
||||||
TCGv t0 = tcg_temp_new();
|
TCGv_i32 tmp = tcg_temp_new_i32();
|
||||||
TCGv_i32 t1 = tcg_temp_new_i32();
|
tcg_gen_qemu_ld_i32(tmp, addr, ctx->mem_idx, DEF_MEMOP(MO_UL));
|
||||||
gen_qemu_ld32u(ctx, t0, arg2);
|
gen_helper_todouble(dest, tmp);
|
||||||
tcg_gen_trunc_tl_i32(t1, t0);
|
tcg_temp_free_i32(tmp);
|
||||||
tcg_temp_free(t0);
|
|
||||||
gen_helper_float32_to_float64(arg1, cpu_env, t1);
|
|
||||||
tcg_temp_free_i32(t1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* lfd lfdu lfdux lfdx */
|
/* lfd lfdu lfdux lfdx */
|
||||||
|
@ -836,15 +833,12 @@ GEN_STUF(name, stop, op | 0x21, type); \
|
||||||
GEN_STUXF(name, stop, op | 0x01, type); \
|
GEN_STUXF(name, stop, op | 0x01, type); \
|
||||||
GEN_STXF(name, stop, 0x17, op | 0x00, type)
|
GEN_STXF(name, stop, 0x17, op | 0x00, type)
|
||||||
|
|
||||||
static inline void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2)
|
static void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 src, TCGv addr)
|
||||||
{
|
{
|
||||||
TCGv_i32 t0 = tcg_temp_new_i32();
|
TCGv_i32 tmp = tcg_temp_new_i32();
|
||||||
TCGv t1 = tcg_temp_new();
|
gen_helper_tosingle(tmp, src);
|
||||||
gen_helper_float64_to_float32(t0, cpu_env, arg1);
|
tcg_gen_qemu_st_i32(tmp, addr, ctx->mem_idx, DEF_MEMOP(MO_UL));
|
||||||
tcg_gen_extu_i32_tl(t1, t0);
|
tcg_temp_free_i32(tmp);
|
||||||
tcg_temp_free_i32(t0);
|
|
||||||
gen_qemu_st32(ctx, t1, arg2);
|
|
||||||
tcg_temp_free(t1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* stfd stfdu stfdux stfdx */
|
/* stfd stfdu stfdux stfdx */
|
||||||
|
|
|
@ -498,6 +498,7 @@ static void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn)
|
||||||
|
|
||||||
static void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn)
|
static void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn)
|
||||||
{
|
{
|
||||||
|
gen_store_spr(sprn, cpu_gpr[gprn]);
|
||||||
gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]);
|
gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]);
|
||||||
/* We must stop translation as we may have rebooted */
|
/* We must stop translation as we may have rebooted */
|
||||||
gen_stop_exception(ctx);
|
gen_stop_exception(ctx);
|
||||||
|
@ -1769,6 +1770,14 @@ static void gen_spr_BookE(CPUPPCState *env, uint64_t ivor_mask)
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, &spr_write_generic,
|
&spr_read_generic, &spr_write_generic,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
|
spr_register(env, SPR_BOOKE_DSRR0, "DSRR0",
|
||||||
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
|
&spr_read_generic, &spr_write_generic,
|
||||||
|
0x00000000);
|
||||||
|
spr_register(env, SPR_BOOKE_DSRR1, "DSRR1",
|
||||||
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
|
&spr_read_generic, &spr_write_generic,
|
||||||
|
0x00000000);
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_BOOKE_DBSR, "DBSR",
|
spr_register(env, SPR_BOOKE_DBSR, "DBSR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
|
@ -1841,6 +1850,14 @@ static void gen_spr_BookE(CPUPPCState *env, uint64_t ivor_mask)
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, &spr_write_generic,
|
&spr_read_generic, &spr_write_generic,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
|
spr_register(env, SPR_BOOKE_SPRG8, "SPRG8",
|
||||||
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
|
&spr_read_generic, &spr_write_generic,
|
||||||
|
0x00000000);
|
||||||
|
spr_register(env, SPR_BOOKE_SPRG9, "SPRG9",
|
||||||
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
|
&spr_read_generic, &spr_write_generic,
|
||||||
|
0x00000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint32_t gen_tlbncfg(uint32_t assoc, uint32_t minsize,
|
static inline uint32_t gen_tlbncfg(uint32_t assoc, uint32_t minsize,
|
||||||
|
@ -10278,6 +10295,8 @@ static void ppc_cpu_reset(CPUState *s)
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_USER_ONLY)
|
#if defined(CONFIG_USER_ONLY)
|
||||||
msr |= (target_ulong)1 << MSR_FP; /* Allow floating point usage */
|
msr |= (target_ulong)1 << MSR_FP; /* Allow floating point usage */
|
||||||
|
msr |= (target_ulong)1 << MSR_FE0; /* Allow floating point exceptions */
|
||||||
|
msr |= (target_ulong)1 << MSR_FE1;
|
||||||
msr |= (target_ulong)1 << MSR_VR; /* Allow altivec usage */
|
msr |= (target_ulong)1 << MSR_VR; /* Allow altivec usage */
|
||||||
msr |= (target_ulong)1 << MSR_VSX; /* Allow VSX usage */
|
msr |= (target_ulong)1 << MSR_VSX; /* Allow VSX usage */
|
||||||
msr |= (target_ulong)1 << MSR_SPE; /* Allow SPE usage */
|
msr |= (target_ulong)1 << MSR_SPE; /* Allow SPE usage */
|
||||||
|
|
|
@ -75,13 +75,11 @@ typedef struct testdef {
|
||||||
static testdef_t tests[] = {
|
static testdef_t tests[] = {
|
||||||
{ "alpha", "clipper", "", "PCI:" },
|
{ "alpha", "clipper", "", "PCI:" },
|
||||||
{ "ppc", "ppce500", "", "U-Boot" },
|
{ "ppc", "ppce500", "", "U-Boot" },
|
||||||
{ "ppc", "prep", "-m 96", "Memory size: 96 MB" },
|
|
||||||
{ "ppc", "40p", "-boot d", "Booting from device d" },
|
{ "ppc", "40p", "-boot d", "Booting from device d" },
|
||||||
{ "ppc", "g3beige", "", "PowerPC,750" },
|
{ "ppc", "g3beige", "", "PowerPC,750" },
|
||||||
{ "ppc", "mac99", "", "PowerPC,G4" },
|
{ "ppc", "mac99", "", "PowerPC,G4" },
|
||||||
{ "ppc", "sam460ex", "-m 256", "DRAM: 256 MiB" },
|
{ "ppc", "sam460ex", "-m 256", "DRAM: 256 MiB" },
|
||||||
{ "ppc64", "ppce500", "", "U-Boot" },
|
{ "ppc64", "ppce500", "", "U-Boot" },
|
||||||
{ "ppc64", "prep", "-boot e", "Booting from device e" },
|
|
||||||
{ "ppc64", "40p", "-m 192", "Memory size: 192 MB" },
|
{ "ppc64", "40p", "-m 192", "Memory size: 192 MB" },
|
||||||
{ "ppc64", "mac99", "", "PowerPC,970FX" },
|
{ "ppc64", "mac99", "", "PowerPC,970FX" },
|
||||||
{ "ppc64", "pseries", "", "Open Firmware" },
|
{ "ppc64", "pseries", "", "Open Firmware" },
|
||||||
|
|
Loading…
Reference in a new issue