Merge branch 'ppc-next' of git://repo.or.cz/qemu/agraf

* 'ppc-next' of git://repo.or.cz/qemu/agraf: (24 commits)
  pseries: Add partial support for PCI
  ppc: Alter CPU state to mask out TCG unimplemented instructions as appropriate
  pseries: Allow writes to KVM accelerated TCE table
  KVM: PPC: Override host vmx/vsx/dfp only when information known
  ppc: Fix up usermode only builds
  pseries: Correct vmx/dfp handling in both KVM and TCG cases
  PPC: Fail configure when libfdt is not available
  ppc: Avoid decrementer related kvm exits
  PPC: Disable non-440 CPUs for ppcemb target
  PPC: Bump qemu-system-ppc to 64-bit physical address space
  pseries: Under kvm use guest cpu = host cpu by default
  ppc: Add cpu defs for POWER7 revisions 2.1 and 2.3
  ppc: First cut implementation of -cpu host
  ppc: Remove broken partial PVR matching
  pseries: Update SLOF firmware image
  pseries: Add device tree properties for VMX/VSX and DFP under kvm
  ppc: Generalize the kvmppc_get_clockfreq() function
  Set an invalid-bits mask for each SPE instructions
  pseries: Update SLOF firmware image
  pseries: Use Book3S-HV TCE acceleration capabilities
  ...
This commit is contained in:
Blue Swirl 2011-11-01 20:57:01 +00:00
commit e927dab1fd
19 changed files with 1391 additions and 325 deletions

View File

@ -248,6 +248,7 @@ obj-ppc-y += ppc_newworld.o
# IBM pSeries (sPAPR)
obj-ppc-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
obj-ppc-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
obj-ppc-$(CONFIG_PSERIES) += spapr_pci.o device-hotplug.o pci-hotplug.o
# PowerPC 4xx boards
obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
obj-ppc-y += ppc440.o ppc440_bamboo.o

13
configure vendored
View File

@ -3341,7 +3341,7 @@ case "$target_arch2" in
;;
ppc)
gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
target_phys_bits=32
target_phys_bits=64
target_nptl="yes"
target_libs_softmmu="$fdt_libs"
;;
@ -3454,7 +3454,16 @@ case "$target_arch2" in
fi
fi
esac
if test "$target_arch2" = "ppc64" -a "$fdt" = "yes"; then
if test "$fdt" != "yes" && test "$target_arch2" = "ppc" -o \
"$target_arch2" = "ppc64" -o "$target_arch2" = "ppcemb"; then
echo
echo "Error: libfdt missing"
echo "The PowerPC target requires libfdt to work properly."
echo "Please make sure to have it and its development packages installed."
echo
exit 1
fi
if test "$target_arch2" = "ppc64"; then
echo "CONFIG_PSERIES=y" >> $config_target_mak
fi
if test "$target_bigendian" = "yes" ; then

View File

@ -662,6 +662,12 @@ static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp,
LOG_TB("%s: %08" PRIx32 " => %08" PRIx32 "\n", __func__,
decr, value);
if (kvm_enabled()) {
/* KVM handles decrementer exceptions, we don't need our own timer */
return;
}
now = qemu_get_clock_ns(vm_clock);
next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq);
if (is_excp) {

View File

@ -89,6 +89,7 @@ static uint32_t pci_reg_read4(void *opaque, target_phys_addr_t addr)
PPCE500PCIState *pci = opaque;
unsigned long win;
uint32_t value = 0;
int idx;
win = addr & 0xfe0;
@ -97,24 +98,44 @@ static uint32_t pci_reg_read4(void *opaque, target_phys_addr_t addr)
case PPCE500_PCI_OW2:
case PPCE500_PCI_OW3:
case PPCE500_PCI_OW4:
idx = (addr >> 5) & 0x7;
switch (addr & 0xC) {
case PCI_POTAR: value = pci->pob[(addr >> 5) & 0x7].potar; break;
case PCI_POTEAR: value = pci->pob[(addr >> 5) & 0x7].potear; break;
case PCI_POWBAR: value = pci->pob[(addr >> 5) & 0x7].powbar; break;
case PCI_POWAR: value = pci->pob[(addr >> 5) & 0x7].powar; break;
default: break;
case PCI_POTAR:
value = pci->pob[idx].potar;
break;
case PCI_POTEAR:
value = pci->pob[idx].potear;
break;
case PCI_POWBAR:
value = pci->pob[idx].powbar;
break;
case PCI_POWAR:
value = pci->pob[idx].powar;
break;
default:
break;
}
break;
case PPCE500_PCI_IW3:
case PPCE500_PCI_IW2:
case PPCE500_PCI_IW1:
idx = ((addr >> 5) & 0x3) - 1;
switch (addr & 0xC) {
case PCI_PITAR: value = pci->pib[(addr >> 5) & 0x3].pitar; break;
case PCI_PIWBAR: value = pci->pib[(addr >> 5) & 0x3].piwbar; break;
case PCI_PIWBEAR: value = pci->pib[(addr >> 5) & 0x3].piwbear; break;
case PCI_PIWAR: value = pci->pib[(addr >> 5) & 0x3].piwar; break;
default: break;
case PCI_PITAR:
value = pci->pib[idx].pitar;
break;
case PCI_PIWBAR:
value = pci->pib[idx].piwbar;
break;
case PCI_PIWBEAR:
value = pci->pib[idx].piwbear;
break;
case PCI_PIWAR:
value = pci->pib[idx].piwar;
break;
default:
break;
};
break;
@ -142,6 +163,7 @@ static void pci_reg_write4(void *opaque, target_phys_addr_t addr,
{
PPCE500PCIState *pci = opaque;
unsigned long win;
int idx;
win = addr & 0xfe0;
@ -153,24 +175,44 @@ static void pci_reg_write4(void *opaque, target_phys_addr_t addr,
case PPCE500_PCI_OW2:
case PPCE500_PCI_OW3:
case PPCE500_PCI_OW4:
idx = (addr >> 5) & 0x7;
switch (addr & 0xC) {
case PCI_POTAR: pci->pob[(addr >> 5) & 0x7].potar = value; break;
case PCI_POTEAR: pci->pob[(addr >> 5) & 0x7].potear = value; break;
case PCI_POWBAR: pci->pob[(addr >> 5) & 0x7].powbar = value; break;
case PCI_POWAR: pci->pob[(addr >> 5) & 0x7].powar = value; break;
default: break;
case PCI_POTAR:
pci->pob[idx].potar = value;
break;
case PCI_POTEAR:
pci->pob[idx].potear = value;
break;
case PCI_POWBAR:
pci->pob[idx].powbar = value;
break;
case PCI_POWAR:
pci->pob[idx].powar = value;
break;
default:
break;
};
break;
case PPCE500_PCI_IW3:
case PPCE500_PCI_IW2:
case PPCE500_PCI_IW1:
idx = ((addr >> 5) & 0x3) - 1;
switch (addr & 0xC) {
case PCI_PITAR: pci->pib[(addr >> 5) & 0x3].pitar = value; break;
case PCI_PIWBAR: pci->pib[(addr >> 5) & 0x3].piwbar = value; break;
case PCI_PIWBEAR: pci->pib[(addr >> 5) & 0x3].piwbear = value; break;
case PCI_PIWAR: pci->pib[(addr >> 5) & 0x3].piwar = value; break;
default: break;
case PCI_PITAR:
pci->pib[idx].pitar = value;
break;
case PCI_PIWBAR:
pci->pib[idx].piwbar = value;
break;
case PCI_PIWBEAR:
pci->pib[idx].piwbear = value;
break;
case PCI_PIWAR:
pci->pib[idx].piwar = value;
break;
default:
break;
};
break;

View File

@ -29,6 +29,9 @@
#include "elf.h"
#include "net.h"
#include "blockdev.h"
#include "cpus.h"
#include "kvm.h"
#include "kvm_ppc.h"
#include "hw/boards.h"
#include "hw/ppc.h"
@ -36,10 +39,12 @@
#include "hw/spapr.h"
#include "hw/spapr_vio.h"
#include "hw/spapr_pci.h"
#include "hw/xics.h"
#include "kvm.h"
#include "kvm_ppc.h"
#include "pci.h"
#include "exec-memory.h"
@ -59,6 +64,11 @@
#define MAX_CPUS 256
#define XICS_IRQS 1024
#define SPAPR_PCI_BUID 0x800000020000001ULL
#define SPAPR_PCI_MEM_WIN_ADDR (0x10000000000ULL + 0xA0000000)
#define SPAPR_PCI_MEM_WIN_SIZE 0x20000000
#define SPAPR_PCI_IO_WIN_ADDR (0x10000000000ULL + 0x80000000)
#define PHANDLE_XICP 0x00001111
sPAPREnvironment *spapr;
@ -88,6 +98,7 @@ qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num)
}
static void *spapr_create_fdt_skel(const char *cpu_model,
target_phys_addr_t rma_size,
target_phys_addr_t initrd_base,
target_phys_addr_t initrd_size,
const char *boot_device,
@ -96,7 +107,9 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
{
void *fdt;
CPUState *env;
uint64_t mem_reg_property[] = { 0, cpu_to_be64(ram_size) };
uint64_t mem_reg_property_rma[] = { 0, cpu_to_be64(rma_size) };
uint64_t mem_reg_property_nonrma[] = { cpu_to_be64(rma_size),
cpu_to_be64(ram_size - rma_size) };
uint32_t start_prop = cpu_to_be32(initrd_base);
uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
@ -105,6 +118,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
int i;
char *modelname;
int smt = kvmppc_smt_threads();
#define _FDT(exp) \
do { \
@ -139,17 +153,35 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
&end_prop, sizeof(end_prop))));
_FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device)));
/*
* Because we don't always invoke any firmware, we can't rely on
* that to do BAR allocation. Long term, we should probably do
* that ourselves, but for now, this setting (plus advertising the
* current BARs as 0) causes sufficiently recent kernels to to the
* BAR assignment themselves */
_FDT((fdt_property_cell(fdt, "linux,pci-probe-only", 0)));
_FDT((fdt_end_node(fdt)));
/* memory node */
/* memory node(s) */
_FDT((fdt_begin_node(fdt, "memory@0")));
_FDT((fdt_property_string(fdt, "device_type", "memory")));
_FDT((fdt_property(fdt, "reg",
mem_reg_property, sizeof(mem_reg_property))));
_FDT((fdt_property(fdt, "reg", mem_reg_property_rma,
sizeof(mem_reg_property_rma))));
_FDT((fdt_end_node(fdt)));
if (ram_size > rma_size) {
char mem_name[32];
sprintf(mem_name, "memory@%" PRIx64, (uint64_t)rma_size);
_FDT((fdt_begin_node(fdt, mem_name)));
_FDT((fdt_property_string(fdt, "device_type", "memory")));
_FDT((fdt_property(fdt, "reg", mem_reg_property_nonrma,
sizeof(mem_reg_property_nonrma))));
_FDT((fdt_end_node(fdt)));
}
/* cpus */
_FDT((fdt_begin_node(fdt, "cpus")));
@ -164,13 +196,18 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
for (env = first_cpu; env != NULL; env = env->next_cpu) {
int index = env->cpu_index;
uint32_t gserver_prop[] = {cpu_to_be32(index), 0}; /* HACK! */
uint32_t servers_prop[smp_threads];
uint32_t gservers_prop[smp_threads * 2];
char *nodename;
uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
0xffffffff, 0xffffffff};
uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ;
uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000;
if ((index % smt) != 0) {
continue;
}
if (asprintf(&nodename, "%s@%x", modelname, index) < 0) {
fprintf(stderr, "Allocation failure\n");
exit(1);
@ -195,15 +232,41 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
pft_size_prop, sizeof(pft_size_prop))));
_FDT((fdt_property_string(fdt, "status", "okay")));
_FDT((fdt_property(fdt, "64-bit", NULL, 0)));
_FDT((fdt_property_cell(fdt, "ibm,ppc-interrupt-server#s", index)));
/* Build interrupt servers and gservers properties */
for (i = 0; i < smp_threads; i++) {
servers_prop[i] = cpu_to_be32(index + i);
/* Hack, direct the group queues back to cpu 0 */
gservers_prop[i*2] = cpu_to_be32(index + i);
gservers_prop[i*2 + 1] = 0;
}
_FDT((fdt_property(fdt, "ibm,ppc-interrupt-server#s",
servers_prop, sizeof(servers_prop))));
_FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s",
gserver_prop, sizeof(gserver_prop))));
gservers_prop, sizeof(gservers_prop))));
if (env->mmu_model & POWERPC_MMU_1TSEG) {
_FDT((fdt_property(fdt, "ibm,processor-segment-sizes",
segs, sizeof(segs))));
}
/* Advertise VMX/VSX (vector extensions) if available
* 0 / no property == no vector extensions
* 1 == VMX / Altivec available
* 2 == VSX available */
if (env->insns_flags & PPC_ALTIVEC) {
uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1;
_FDT((fdt_property_cell(fdt, "ibm,vmx", vmx)));
}
/* Advertise DFP (Decimal Floating Point) if available
* 0 / no property == no DFP
* 1 == DFP available */
if (env->insns_flags2 & PPC2_DFP) {
_FDT((fdt_property_cell(fdt, "ibm,dfp", 1)));
}
_FDT((fdt_end_node(fdt)));
}
@ -260,6 +323,7 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr,
{
int ret;
void *fdt;
sPAPRPHBState *phb;
fdt = g_malloc(FDT_MAX_SIZE);
@ -272,6 +336,15 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr,
exit(1);
}
QLIST_FOREACH(phb, &spapr->phbs, list) {
ret = spapr_populate_pci_devices(phb, PHANDLE_XICP, fdt);
}
if (ret < 0) {
fprintf(stderr, "couldn't setup PCI devices in fdt\n");
exit(1);
}
/* RTAS */
ret = spapr_rtas_device_tree_setup(fdt, rtas_addr, rtas_size);
if (ret < 0) {
@ -328,6 +401,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
int i;
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1);
target_phys_addr_t rma_alloc_size, rma_size;
uint32_t initrd_base;
long kernel_size, initrd_size, fw_size;
long pteg_shift = 17;
@ -336,15 +410,28 @@ static void ppc_spapr_init(ram_addr_t ram_size,
spapr = g_malloc(sizeof(*spapr));
cpu_ppc_hypercall = emulate_spapr_hypercall;
/* We place the device tree just below either the top of RAM, or
* 2GB, so that it can be processed with 32-bit code if
* necessary */
spapr->fdt_addr = MIN(ram_size, 0x80000000) - FDT_MAX_SIZE;
/* Allocate RMA if necessary */
rma_alloc_size = kvmppc_alloc_rma("ppc_spapr.rma", sysmem);
if (rma_alloc_size == -1) {
hw_error("qemu: Unable to create RMA\n");
exit(1);
}
if (rma_alloc_size && (rma_alloc_size < ram_size)) {
rma_size = rma_alloc_size;
} else {
rma_size = ram_size;
}
/* We place the device tree just below either the top of the RMA,
* or just below 2GB, whichever is lowere, so that it can be
* processed with 32-bit real mode code if necessary */
spapr->fdt_addr = MIN(rma_size, 0x80000000) - FDT_MAX_SIZE;
spapr->rtas_addr = spapr->fdt_addr - RTAS_MAX_SIZE;
/* init CPUs */
if (cpu_model == NULL) {
cpu_model = "POWER7";
cpu_model = kvm_enabled() ? "host" : "POWER7";
}
for (i = 0; i < smp_cpus; i++) {
env = cpu_init(cpu_model);
@ -364,8 +451,13 @@ static void ppc_spapr_init(ram_addr_t ram_size,
/* allocate RAM */
spapr->ram_limit = ram_size;
memory_region_init_ram(ram, NULL, "ppc_spapr.ram", spapr->ram_limit);
memory_region_add_subregion(sysmem, 0, ram);
if (spapr->ram_limit > rma_alloc_size) {
ram_addr_t nonrma_base = rma_alloc_size;
ram_addr_t nonrma_size = spapr->ram_limit - rma_alloc_size;
memory_region_init_ram(ram, NULL, "ppc_spapr.ram", nonrma_size);
memory_region_add_subregion(sysmem, nonrma_base, ram);
}
/* allocate hash page table. For now we always make this 16mb,
* later we should probably make it scale to the size of guest
@ -411,6 +503,12 @@ static void ppc_spapr_init(ram_addr_t ram_size,
}
}
/* Set up PCI */
spapr_create_phb(spapr, "pci", SPAPR_PCI_BUID,
SPAPR_PCI_MEM_WIN_ADDR,
SPAPR_PCI_MEM_WIN_SIZE,
SPAPR_PCI_IO_WIN_ADDR);
for (i = 0; i < nb_nics; i++) {
NICInfo *nd = &nd_table[i];
@ -421,10 +519,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
if (strcmp(nd->model, "ibmveth") == 0) {
spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd);
} else {
fprintf(stderr, "pSeries (sPAPR) platform does not support "
"NIC model '%s' (only ibmveth is supported)\n",
nd->model);
exit(1);
pci_nic_init_nofail(&nd_table[i], nd->model, NULL);
}
}
@ -489,7 +584,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
}
/* Prepare the device tree */
spapr->fdt_skel = spapr_create_fdt_skel(cpu_model,
spapr->fdt_skel = spapr_create_fdt_skel(cpu_model, rma_size,
initrd_base, initrd_size,
boot_device, kernel_cmdline,
pteg_shift + 7);

View File

@ -4,10 +4,12 @@
#include "hw/xics.h"
struct VIOsPAPRBus;
struct sPAPRPHBState;
struct icp_state;
typedef struct sPAPREnvironment {
struct VIOsPAPRBus *vio_bus;
QLIST_HEAD(, sPAPRPHBState) phbs;
struct icp_state *icp;
target_phys_addr_t ram_limit;

508
hw/spapr_pci.c Normal file
View File

@ -0,0 +1,508 @@
/*
* QEMU sPAPR PCI host originated from Uninorth PCI host
*
* Copyright (c) 2011 Alexey Kardashevskiy, IBM Corporation.
* Copyright (C) 2011 David Gibson, IBM Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hw.h"
#include "pci.h"
#include "pci_host.h"
#include "hw/spapr.h"
#include "hw/spapr_pci.h"
#include "exec-memory.h"
#include <libfdt.h>
#include "hw/pci_internals.h"
static const uint32_t bars[] = {
PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1,
PCI_BASE_ADDRESS_2, PCI_BASE_ADDRESS_3,
PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5
/*, PCI_ROM_ADDRESS*/
};
static PCIDevice *find_dev(sPAPREnvironment *spapr,
uint64_t buid, uint32_t config_addr)
{
DeviceState *qdev;
int devfn = (config_addr >> 8) & 0xFF;
sPAPRPHBState *phb;
QLIST_FOREACH(phb, &spapr->phbs, list) {
if (phb->buid != buid) {
continue;
}
QLIST_FOREACH(qdev, &phb->host_state.bus->qbus.children, sibling) {
PCIDevice *dev = (PCIDevice *)qdev;
if (dev->devfn == devfn) {
return dev;
}
}
}
return NULL;
}
static void rtas_ibm_read_pci_config(sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
{
uint32_t val, size, addr;
uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
PCIDevice *dev = find_dev(spapr, buid, rtas_ld(args, 0));
if (!dev) {
rtas_st(rets, 0, -1);
return;
}
size = rtas_ld(args, 3);
addr = rtas_ld(args, 0) & 0xFF;
val = pci_default_read_config(dev, addr, size);
rtas_st(rets, 0, 0);
rtas_st(rets, 1, val);
}
static void rtas_read_pci_config(sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
{
uint32_t val, size, addr;
PCIDevice *dev = find_dev(spapr, 0, rtas_ld(args, 0));
if (!dev) {
rtas_st(rets, 0, -1);
return;
}
size = rtas_ld(args, 1);
addr = rtas_ld(args, 0) & 0xFF;
val = pci_default_read_config(dev, addr, size);
rtas_st(rets, 0, 0);
rtas_st(rets, 1, val);
}
static void rtas_ibm_write_pci_config(sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
{
uint32_t val, size, addr;
uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
PCIDevice *dev = find_dev(spapr, buid, rtas_ld(args, 0));
if (!dev) {
rtas_st(rets, 0, -1);
return;
}
val = rtas_ld(args, 4);
size = rtas_ld(args, 3);
addr = rtas_ld(args, 0) & 0xFF;
pci_default_write_config(dev, addr, val, size);
rtas_st(rets, 0, 0);
}
static void rtas_write_pci_config(sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
{
uint32_t val, size, addr;
PCIDevice *dev = find_dev(spapr, 0, rtas_ld(args, 0));
if (!dev) {
rtas_st(rets, 0, -1);
return;
}
val = rtas_ld(args, 2);
size = rtas_ld(args, 1);
addr = rtas_ld(args, 0) & 0xFF;
pci_default_write_config(dev, addr, val, size);
rtas_st(rets, 0, 0);
}
static int pci_spapr_map_irq(PCIDevice *pci_dev, int irq_num)
{
/*
* Here we need to convert pci_dev + irq_num to some unique value
* which is less than number of IRQs on the specific bus (now it
* is 16). At the moment irq_num == device_id (number of the
* slot?)
* FIXME: we should swizzle in fn and irq_num
*/
return (pci_dev->devfn >> 3) % SPAPR_PCI_NUM_LSI;
}
static void pci_spapr_set_irq(void *opaque, int irq_num, int level)
{
/*
* Here we use the number returned by pci_spapr_map_irq to find a
* corresponding qemu_irq.
*/
sPAPRPHBState *phb = opaque;
qemu_set_irq(phb->lsi_table[irq_num].qirq, level);
}
static int spapr_phb_init(SysBusDevice *s)
{
sPAPRPHBState *phb = FROM_SYSBUS(sPAPRPHBState, s);
int i;
/* Initialize the LSI table */
for (i = 0; i < SPAPR_PCI_NUM_LSI; i++) {
qemu_irq qirq;
uint32_t num;
qirq = spapr_allocate_irq(0, &num);
if (!qirq) {
return -1;
}
phb->lsi_table[i].dt_irq = num;
phb->lsi_table[i].qirq = qirq;
}
return 0;
}
static int spapr_main_pci_host_init(PCIDevice *d)
{
return 0;
}
static PCIDeviceInfo spapr_main_pci_host_info = {
.qdev.name = "spapr-pci-host-bridge",
.qdev.size = sizeof(PCIDevice),
.init = spapr_main_pci_host_init,
};
static void spapr_register_devices(void)
{
sysbus_register_dev("spapr-pci-host-bridge", sizeof(sPAPRPHBState),
spapr_phb_init);
pci_qdev_register(&spapr_main_pci_host_info);
}
device_init(spapr_register_devices)
static uint64_t spapr_io_read(void *opaque, target_phys_addr_t addr,
unsigned size)
{
switch (size) {
case 1:
return cpu_inb(addr);
case 2:
return cpu_inw(addr);
case 4:
return cpu_inl(addr);
}
assert(0);
}
static void spapr_io_write(void *opaque, target_phys_addr_t addr,
uint64_t data, unsigned size)
{
switch (size) {
case 1:
cpu_outb(addr, data);
return;
case 2:
cpu_outw(addr, data);
return;
case 4:
cpu_outl(addr, data);
return;
}
assert(0);
}
static MemoryRegionOps spapr_io_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
.read = spapr_io_read,
.write = spapr_io_write
};
void spapr_create_phb(sPAPREnvironment *spapr,
const char *busname, uint64_t buid,
uint64_t mem_win_addr, uint64_t mem_win_size,
uint64_t io_win_addr)
{
DeviceState *dev;
SysBusDevice *s;
sPAPRPHBState *phb;
PCIBus *bus;
char namebuf[strlen(busname)+11];
dev = qdev_create(NULL, "spapr-pci-host-bridge");
qdev_init_nofail(dev);
s = sysbus_from_qdev(dev);
phb = FROM_SYSBUS(sPAPRPHBState, s);
phb->mem_win_addr = mem_win_addr;
sprintf(namebuf, "%s-mem", busname);
memory_region_init(&phb->memspace, namebuf, INT64_MAX);
sprintf(namebuf, "%s-memwindow", busname);
memory_region_init_alias(&phb->memwindow, namebuf, &phb->memspace,
SPAPR_PCI_MEM_WIN_BUS_OFFSET, mem_win_size);
memory_region_add_subregion(get_system_memory(), mem_win_addr,
&phb->memwindow);
phb->io_win_addr = io_win_addr;
/* On ppc, we only have MMIO no specific IO space from the CPU
* perspective. In theory we ought to be able to embed the PCI IO
* memory region direction in the system memory space. However,
* if any of the IO BAR subregions use the old_portio mechanism,
* that won't be processed properly unless accessed from the
* system io address space. This hack to bounce things via
* system_io works around the problem until all the users of
* old_portion are updated */
sprintf(namebuf, "%s-io", busname);
memory_region_init(&phb->iospace, namebuf, SPAPR_PCI_IO_WIN_SIZE);
/* FIXME: fix to support multiple PHBs */
memory_region_add_subregion(get_system_io(), 0, &phb->iospace);
sprintf(namebuf, "%s-iowindow", busname);
memory_region_init_io(&phb->iowindow, &spapr_io_ops, phb,
namebuf, SPAPR_PCI_IO_WIN_SIZE);
memory_region_add_subregion(get_system_memory(), io_win_addr,
&phb->iowindow);
phb->host_state.bus = bus = pci_register_bus(&phb->busdev.qdev, busname,
pci_spapr_set_irq,
pci_spapr_map_irq,
phb,
&phb->memspace, &phb->iospace,
PCI_DEVFN(0, 0),
SPAPR_PCI_NUM_LSI);
spapr_rtas_register("read-pci-config", rtas_read_pci_config);
spapr_rtas_register("write-pci-config", rtas_write_pci_config);
spapr_rtas_register("ibm,read-pci-config", rtas_ibm_read_pci_config);
spapr_rtas_register("ibm,write-pci-config", rtas_ibm_write_pci_config);
QLIST_INSERT_HEAD(&spapr->phbs, phb, list);
/* pci_bus_set_mem_base(bus, mem_va_start - SPAPR_PCI_MEM_BAR_START); */
}
/* Macros to operate with address in OF binding to PCI */
#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p))
#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */
#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */
#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */
#define b_ss(x) b_x((x), 24, 2) /* the space code */
#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */
#define b_ddddd(x) b_x((x), 11, 5) /* device number */
#define b_fff(x) b_x((x), 8, 3) /* function number */
#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */
static uint32_t regtype_to_ss(uint8_t type)
{
if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
return 3;
}
if (type == PCI_BASE_ADDRESS_SPACE_IO) {
return 1;
}
return 2;
}
int spapr_populate_pci_devices(sPAPRPHBState *phb,
uint32_t xics_phandle,
void *fdt)
{
PCIBus *bus = phb->host_state.bus;
int bus_off, node_off = 0, devid, fn, i, n, devices;
DeviceState *qdev;
char nodename[256];
struct {
uint32_t hi;
uint64_t addr;
uint64_t size;
} __attribute__((packed)) reg[PCI_NUM_REGIONS + 1],
assigned_addresses[PCI_NUM_REGIONS];
uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) };
struct {
uint32_t hi;
uint64_t child;
uint64_t parent;
uint64_t size;
} __attribute__((packed)) ranges[] = {
{
cpu_to_be32(b_ss(1)), cpu_to_be64(0),
cpu_to_be64(phb->io_win_addr),
cpu_to_be64(memory_region_size(&phb->iospace)),
},
{
cpu_to_be32(b_ss(2)), cpu_to_be64(SPAPR_PCI_MEM_WIN_BUS_OFFSET),
cpu_to_be64(phb->mem_win_addr),
cpu_to_be64(memory_region_size(&phb->memwindow)),
},
};
uint64_t bus_reg[] = { cpu_to_be64(phb->buid), 0 };
uint32_t interrupt_map_mask[] = {
cpu_to_be32(b_ddddd(-1)|b_fff(-1)), 0x0, 0x0, 0x0};
uint32_t interrupt_map[bus->nirq][7];
/* Start populating the FDT */
sprintf(nodename, "pci@%" PRIx64, phb->buid);
bus_off = fdt_add_subnode(fdt, 0, nodename);
if (bus_off < 0) {
return bus_off;
}
#define _FDT(exp) \
do { \
int ret = (exp); \
if (ret < 0) { \
return ret; \
} \
} while (0)
/* Write PHB properties */
_FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci"));
_FDT(fdt_setprop_string(fdt, bus_off, "compatible", "IBM,Logical_PHB"));
_FDT(fdt_setprop_cell(fdt, bus_off, "#address-cells", 0x3));
_FDT(fdt_setprop_cell(fdt, bus_off, "#size-cells", 0x2));
_FDT(fdt_setprop_cell(fdt, bus_off, "#interrupt-cells", 0x1));
_FDT(fdt_setprop(fdt, bus_off, "used-by-rtas", NULL, 0));
_FDT(fdt_setprop(fdt, bus_off, "bus-range", &bus_range, sizeof(bus_range)));
_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, "interrupt-map-mask",
&interrupt_map_mask, sizeof(interrupt_map_mask)));
/* Populate PCI devices and allocate IRQs */
devices = 0;
QLIST_FOREACH(qdev, &bus->qbus.children, sibling) {
PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
int irq_index = pci_spapr_map_irq(dev, 0);
uint32_t *irqmap = interrupt_map[devices];
uint8_t *config = dev->config;
devid = dev->devfn >> 3;
fn = dev->devfn & 7;
sprintf(nodename, "pci@%u,%u", devid, fn);
/* Allocate interrupt from the map */
if (devid > bus->nirq) {
printf("Unexpected behaviour in spapr_populate_pci_devices,"
"wrong devid %u\n", devid);
exit(-1);
}
irqmap[0] = cpu_to_be32(b_ddddd(devid)|b_fff(fn));
irqmap[1] = 0;
irqmap[2] = 0;
irqmap[3] = 0;
irqmap[4] = cpu_to_be32(xics_phandle);
irqmap[5] = cpu_to_be32(phb->lsi_table[irq_index].dt_irq);
irqmap[6] = cpu_to_be32(0x8);
/* Add node to FDT */
node_off = fdt_add_subnode(fdt, bus_off, nodename);
if (node_off < 0) {
return node_off;
}
_FDT(fdt_setprop_cell(fdt, node_off, "vendor-id",
pci_get_word(&config[PCI_VENDOR_ID])));
_FDT(fdt_setprop_cell(fdt, node_off, "device-id",
pci_get_word(&config[PCI_DEVICE_ID])));
_FDT(fdt_setprop_cell(fdt, node_off, "revision-id",
pci_get_byte(&config[PCI_REVISION_ID])));
_FDT(fdt_setprop_cell(fdt, node_off, "class-code",
pci_get_long(&config[PCI_CLASS_REVISION]) >> 8));
_FDT(fdt_setprop_cell(fdt, node_off, "subsystem-id",
pci_get_word(&config[PCI_SUBSYSTEM_ID])));
_FDT(fdt_setprop_cell(fdt, node_off, "subsystem-vendor-id",
pci_get_word(&config[PCI_SUBSYSTEM_VENDOR_ID])));
/* Config space region comes first */
reg[0].hi = cpu_to_be32(
b_n(0) |
b_p(0) |
b_t(0) |
b_ss(0/*config*/) |
b_bbbbbbbb(0) |
b_ddddd(devid) |
b_fff(fn));
reg[0].addr = 0;
reg[0].size = 0;
n = 0;
for (i = 0; i < PCI_NUM_REGIONS; ++i) {
if (0 == dev->io_regions[i].size) {
continue;
}
reg[n+1].hi = cpu_to_be32(
b_n(0) |
b_p(0) |
b_t(0) |
b_ss(regtype_to_ss(dev->io_regions[i].type)) |
b_bbbbbbbb(0) |
b_ddddd(devid) |
b_fff(fn) |
b_rrrrrrrr(bars[i]));
reg[n+1].addr = 0;
reg[n+1].size = cpu_to_be64(dev->io_regions[i].size);
assigned_addresses[n].hi = cpu_to_be32(
b_n(1) |
b_p(0) |
b_t(0) |
b_ss(regtype_to_ss(dev->io_regions[i].type)) |
b_bbbbbbbb(0) |
b_ddddd(devid) |
b_fff(fn) |
b_rrrrrrrr(bars[i]));
/*
* Writing zeroes to assigned_addresses causes the guest kernel to
* reassign BARs
*/
assigned_addresses[n].addr = cpu_to_be64(dev->io_regions[i].addr);
assigned_addresses[n].size = reg[n+1].size;
++n;
}
_FDT(fdt_setprop(fdt, node_off, "reg", reg, sizeof(reg[0])*(n+1)));
_FDT(fdt_setprop(fdt, node_off, "assigned-addresses",
assigned_addresses,
sizeof(assigned_addresses[0])*(n)));
_FDT(fdt_setprop_cell(fdt, node_off, "interrupts",
pci_get_byte(&config[PCI_INTERRUPT_PIN])));
++devices;
}
/* Write interrupt map */
_FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map,
devices * sizeof(interrupt_map[0])));
return 0;
}

61
hw/spapr_pci.h Normal file
View File

@ -0,0 +1,61 @@
/*
* QEMU SPAPR PCI BUS definitions
*
* Copyright (c) 2011 Alexey Kardashevskiy <aik@au1.ibm.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(__HW_SPAPR_H__)
#error Please include spapr.h before this file!
#endif
#if !defined(__HW_SPAPR_PCI_H__)
#define __HW_SPAPR_PCI_H__
#include "hw/pci_host.h"
#include "hw/xics.h"
#define SPAPR_PCI_NUM_LSI 16
typedef struct sPAPRPHBState {
SysBusDevice busdev;
PCIHostState host_state;
uint64_t buid;
MemoryRegion memspace, iospace;
target_phys_addr_t mem_win_addr, io_win_addr;
MemoryRegion memwindow, iowindow;
struct {
uint32_t dt_irq;
qemu_irq qirq;
} lsi_table[SPAPR_PCI_NUM_LSI];
QLIST_ENTRY(sPAPRPHBState) list;
} sPAPRPHBState;
#define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL
#define SPAPR_PCI_IO_WIN_SIZE 0x10000
void spapr_create_phb(sPAPREnvironment *spapr,
const char *busname, uint64_t buid,
uint64_t mem_win_addr, uint64_t mem_win_size,
uint64_t io_win_addr);
int spapr_populate_pci_devices(sPAPRPHBState *phb,
uint32_t xics_phandle,
void *fdt);
#endif /* __HW_SPAPR_PCI_H__ */

View File

@ -165,7 +165,13 @@ static void rtce_init(VIOsPAPRDevice *dev)
* sizeof(VIOsPAPR_RTCE);
if (size) {
dev->rtce_table = g_malloc0(size);
dev->rtce_table = kvmppc_create_spapr_tce(dev->reg,
dev->rtce_window_size,
&dev->kvmtce_fd);
if (!dev->rtce_table) {
dev->rtce_table = g_malloc0(size);
}
}
}

View File

@ -57,6 +57,7 @@ typedef struct VIOsPAPRDevice {
target_ulong signal_state;
uint32_t rtce_window_size;
VIOsPAPR_RTCE *rtce_table;
int kvmtce_fd;
VIOsPAPR_CRQ crq;
} VIOsPAPRDevice;

View File

@ -17,7 +17,7 @@
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
implementation for certain IBM POWER hardware. The sources are at
https://github.com/dgibson/SLOF, and the image currently in qemu is
built from git tag qemu-slof-20110323.
built from git tag qemu-slof-20111013.
- The PXE roms come from the iPXE project. Built with BANNER_TIME 0.
Sources available at http://ipxe.org. Vendor:Device ID -> ROM mapping:

Binary file not shown.

@ -1 +1 @@
Subproject commit d1d6b53b713a2b7c2c25685268fa932d28a4b4c0
Subproject commit 32e3430c018ceb8413cb808477449d1968c42497

View File

@ -66,7 +66,7 @@
#define TARGET_PAGE_BITS 12
#endif /* defined(TARGET_PPCEMB) */
#define TARGET_PHYS_ADDR_SPACE_BITS 32
#define TARGET_PHYS_ADDR_SPACE_BITS 36
#define TARGET_VIRT_ADDR_SPACE_BITS 32
#endif /* defined (TARGET_PPC64) */
@ -858,6 +858,22 @@ enum {
/* The whole PowerPC CPU context */
#define NB_MMU_MODES 3
struct ppc_def_t {
const char *name;
uint32_t pvr;
uint32_t svr;
uint64_t insns_flags;
uint64_t insns_flags2;
uint64_t msr_mask;
powerpc_mmu_t mmu_model;
powerpc_excp_t excp_model;
powerpc_input_t bus_model;
uint32_t flags;
int bfd_mach;
void (*init_proc)(CPUPPCState *env);
int (*check_pow)(CPUPPCState *env);
};
struct CPUPPCState {
/* First are the most commonly used resources
* during translated code execution
@ -1107,6 +1123,7 @@ void ppc_store_msr (CPUPPCState *env, target_ulong value);
void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf);
const ppc_def_t *ppc_find_by_pvr(uint32_t pvr);
const ppc_def_t *cpu_ppc_find_by_name (const char *name);
int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def);
@ -1839,10 +1856,40 @@ enum {
/* popcntw and popcntd instructions */
PPC_POPCNTWD = 0x8000000000000000ULL,
#define PPC_TCG_INSNS (PPC_INSNS_BASE | PPC_POWER | PPC_POWER2 \
| PPC_POWER_RTC | PPC_POWER_BR | PPC_64B \
| PPC_64BX | PPC_64H | PPC_WAIT | PPC_MFTB \
| PPC_602_SPEC | PPC_ISEL | PPC_POPCNTB \
| PPC_STRING | PPC_FLOAT | PPC_FLOAT_EXT \
| PPC_FLOAT_FSQRT | PPC_FLOAT_FRES \
| PPC_FLOAT_FRSQRTE | PPC_FLOAT_FRSQRTES \
| PPC_FLOAT_FSEL | PPC_FLOAT_STFIWX \
| PPC_ALTIVEC | PPC_SPE | PPC_SPE_SINGLE \
| PPC_SPE_DOUBLE | PPC_MEM_TLBIA \
| PPC_MEM_TLBIE | PPC_MEM_TLBSYNC \
| PPC_MEM_SYNC | PPC_MEM_EIEIO \
| PPC_CACHE | PPC_CACHE_ICBI \
| PPC_CACHE_DCBZ | PPC_CACHE_DCBZT \
| PPC_CACHE_DCBA | PPC_CACHE_LOCK \
| PPC_EXTERN | PPC_SEGMENT | PPC_6xx_TLB \
| PPC_74xx_TLB | PPC_40x_TLB | PPC_SEGMENT_64B \
| PPC_SLBI | PPC_WRTEE | PPC_40x_EXCP \
| PPC_405_MAC | PPC_440_SPEC | PPC_BOOKE \
| PPC_MFAPIDI | PPC_TLBIVA | PPC_TLBIVAX \
| PPC_4xx_COMMON | PPC_40x_ICBT | PPC_RFMCI \
| PPC_RFDI | PPC_DCR | PPC_DCRX | PPC_DCRUX \
| PPC_POPCNTWD)
/* extended type values */
/* BookE 2.06 PowerPC specification */
PPC2_BOOKE206 = 0x0000000000000001ULL,
/* VSX (extensions to Altivec / VMX) */
PPC2_VSX = 0x0000000000000002ULL,
/* Decimal Floating Point (DFP) */
PPC2_DFP = 0x0000000000000004ULL,
#define PPC_TCG_INSNS2 (PPC2_BOOKE206)
};
/*****************************************************************************/

View File

@ -26,6 +26,8 @@
#include "helper_regs.h"
#include "qemu-common.h"
#include "kvm.h"
#include "kvm_ppc.h"
#include "cpus.h"
//#define DEBUG_MMU
//#define DEBUG_BATS
@ -3189,6 +3191,15 @@ CPUPPCState *cpu_ppc_init (const char *cpu_model)
if (tcg_enabled()) {
ppc_translate_init();
}
/* Adjust cpu index for SMT */
#if !defined(CONFIG_USER_ONLY)
if (kvm_enabled()) {
int smt = kvmppc_smt_threads();
env->cpu_index = (env->cpu_index / smp_threads)*smt
+ (env->cpu_index % smp_threads);
}
#endif /* !CONFIG_USER_ONLY */
env->cpu_model_str = cpu_model;
cpu_ppc_register_internal(env, def);

View File

@ -28,6 +28,8 @@
#include "kvm_ppc.h"
#include "cpu.h"
#include "device_tree.h"
#include "hw/sysbus.h"
#include "hw/spapr.h"
#include "hw/sysbus.h"
#include "hw/spapr.h"
@ -53,6 +55,9 @@ static int cap_interrupt_unset = false;
static int cap_interrupt_level = false;
static int cap_segstate;
static int cap_booke_sregs;
static int cap_ppc_smt;
static int cap_ppc_rma;
static int cap_spapr_tce;
/* XXX We have a race condition where we actually have a level triggered
* interrupt, but the infrastructure can't expose that yet, so the guest
@ -76,6 +81,9 @@ int kvm_arch_init(KVMState *s)
cap_interrupt_level = kvm_check_extension(s, KVM_CAP_PPC_IRQ_LEVEL);
cap_segstate = kvm_check_extension(s, KVM_CAP_PPC_SEGSTATE);
cap_booke_sregs = kvm_check_extension(s, KVM_CAP_PPC_BOOKE_SREGS);
cap_ppc_smt = kvm_check_extension(s, KVM_CAP_PPC_SMT);
cap_ppc_rma = kvm_check_extension(s, KVM_CAP_PPC_RMA);
cap_spapr_tce = kvm_check_extension(s, KVM_CAP_SPAPR_TCE);
if (!cap_interrupt_level) {
fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the "
@ -642,37 +650,60 @@ static int kvmppc_find_cpu_dt(char *buf, int buf_len)
return 0;
}
uint64_t kvmppc_get_clockfreq(void)
/* Read a CPU node property from the host device tree that's a single
* integer (32-bit or 64-bit). Returns 0 if anything goes wrong
* (can't find or open the property, or doesn't understand the
* format) */
static uint64_t kvmppc_read_int_cpu_dt(const char *propname)
{
char buf[512];
uint32_t tb[2];
char buf[PATH_MAX];
union {
uint32_t v32;
uint64_t v64;
} u;
FILE *f;
int len;
if (kvmppc_find_cpu_dt(buf, sizeof(buf))) {
return 0;
return -1;
}
strncat(buf, "/clock-frequency", sizeof(buf) - strlen(buf));
strncat(buf, "/", sizeof(buf) - strlen(buf));
strncat(buf, propname, sizeof(buf) - strlen(buf));
f = fopen(buf, "rb");
if (!f) {
return -1;
}
len = fread(tb, sizeof(tb[0]), 2, f);
len = fread(&u, 1, sizeof(u), f);
fclose(f);
switch (len) {
case 1:
/* freq is only a single cell */
return tb[0];
case 2:
return *(uint64_t*)tb;
case 4:
/* property is a 32-bit quantity */
return be32_to_cpu(u.v32);
case 8:
return be64_to_cpu(u.v64);
}
return 0;
}
uint64_t kvmppc_get_clockfreq(void)
{
return kvmppc_read_int_cpu_dt("clock-frequency");
}
uint32_t kvmppc_get_vmx(void)
{
return kvmppc_read_int_cpu_dt("ibm,vmx");
}
uint32_t kvmppc_get_dfp(void)
{
return kvmppc_read_int_cpu_dt("ibm,dfp");
}
int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len)
{
uint32_t *hc = (uint32_t*)buf;
@ -750,6 +781,150 @@ fail:
cpu_abort(env, "This KVM version does not support PAPR\n");
}
int kvmppc_smt_threads(void)
{
return cap_ppc_smt ? cap_ppc_smt : 1;
}
off_t kvmppc_alloc_rma(const char *name, MemoryRegion *sysmem)
{
void *rma;
off_t size;
int fd;
struct kvm_allocate_rma ret;
MemoryRegion *rma_region;
/* If cap_ppc_rma == 0, contiguous RMA allocation is not supported
* if cap_ppc_rma == 1, contiguous RMA allocation is supported, but
* not necessary on this hardware
* if cap_ppc_rma == 2, contiguous RMA allocation is needed on this hardware
*
* FIXME: We should allow the user to force contiguous RMA
* allocation in the cap_ppc_rma==1 case.
*/
if (cap_ppc_rma < 2) {
return 0;
}
fd = kvm_vm_ioctl(kvm_state, KVM_ALLOCATE_RMA, &ret);
if (fd < 0) {
fprintf(stderr, "KVM: Error on KVM_ALLOCATE_RMA: %s\n",
strerror(errno));
return -1;
}
size = MIN(ret.rma_size, 256ul << 20);
rma = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (rma == MAP_FAILED) {
fprintf(stderr, "KVM: Error mapping RMA: %s\n", strerror(errno));
return -1;
};
rma_region = g_new(MemoryRegion, 1);
memory_region_init_ram_ptr(rma_region, NULL, name, size, rma);
memory_region_add_subregion(sysmem, 0, rma_region);
return size;
}
void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd)
{
struct kvm_create_spapr_tce args = {
.liobn = liobn,
.window_size = window_size,
};
long len;
int fd;
void *table;
if (!cap_spapr_tce) {
return NULL;
}
fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_SPAPR_TCE, &args);
if (fd < 0) {
return NULL;
}
len = (window_size / SPAPR_VIO_TCE_PAGE_SIZE) * sizeof(VIOsPAPR_RTCE);
/* FIXME: round this up to page size */
table = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (table == MAP_FAILED) {
close(fd);
return NULL;
}
*pfd = fd;
return table;
}
int kvmppc_remove_spapr_tce(void *table, int fd, uint32_t window_size)
{
long len;
if (fd < 0) {
return -1;
}
len = (window_size / SPAPR_VIO_TCE_PAGE_SIZE)*sizeof(VIOsPAPR_RTCE);
if ((munmap(table, len) < 0) ||
(close(fd) < 0)) {
fprintf(stderr, "KVM: Unexpected error removing KVM SPAPR TCE "
"table: %s", strerror(errno));
/* Leak the table */
}
return 0;
}
static inline uint32_t mfpvr(void)
{
uint32_t pvr;
asm ("mfpvr %0"
: "=r"(pvr));
return pvr;
}
static void alter_insns(uint64_t *word, uint64_t flags, bool on)
{
if (on) {
*word |= flags;
} else {
*word &= ~flags;
}
}
const ppc_def_t *kvmppc_host_cpu_def(void)
{
uint32_t host_pvr = mfpvr();
const ppc_def_t *base_spec;
ppc_def_t *spec;
uint32_t vmx = kvmppc_get_vmx();
uint32_t dfp = kvmppc_get_dfp();
base_spec = ppc_find_by_pvr(host_pvr);
spec = g_malloc0(sizeof(*spec));
memcpy(spec, base_spec, sizeof(*spec));
/* Now fix up the spec with information we can query from the host */
if (vmx != -1) {
/* Only override when we know what the host supports */
alter_insns(&spec->insns_flags, PPC_ALTIVEC, vmx > 0);
alter_insns(&spec->insns_flags2, PPC2_VSX, vmx > 1);
}
if (dfp != -1) {
/* Only override when we know what the host supports */
alter_insns(&spec->insns_flags2, PPC2_DFP, dfp);
}
return spec;
}
bool kvm_arch_stop_on_emulation_error(CPUState *env)
{
return true;

View File

@ -9,15 +9,26 @@
#ifndef __KVM_PPC_H__
#define __KVM_PPC_H__
#include "memory.h"
void kvmppc_init(void);
#ifdef CONFIG_KVM
uint32_t kvmppc_get_tbfreq(void);
uint64_t kvmppc_get_clockfreq(void);
uint32_t kvmppc_get_vmx(void);
uint32_t kvmppc_get_dfp(void);
int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len);
int kvmppc_set_interrupt(CPUState *env, int irq, int level);
void kvmppc_set_papr(CPUState *env);
int kvmppc_smt_threads(void);
#ifndef CONFIG_USER_ONLY
off_t kvmppc_alloc_rma(const char *name, MemoryRegion *sysmem);
void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd);
int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size);
#endif /* !CONFIG_USER_ONLY */
const ppc_def_t *kvmppc_host_cpu_def(void);
#else
@ -31,6 +42,16 @@ static inline uint64_t kvmppc_get_clockfreq(void)
return 0;
}
static inline uint32_t kvmppc_get_vmx(void)
{
return 0;
}
static inline uint32_t kvmppc_get_dfp(void)
{
return 0;
}
static inline int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len)
{
return -1;
@ -45,6 +66,35 @@ static inline void kvmppc_set_papr(CPUState *env)
{
}
static inline int kvmppc_smt_threads(void)
{
return 1;
}
#ifndef CONFIG_USER_ONLY
static inline off_t kvmppc_alloc_rma(const char *name, MemoryRegion *sysmem)
{
return 0;
}
static inline void *kvmppc_create_spapr_tce(uint32_t liobn,
uint32_t window_size, int *fd)
{
return NULL;
}
static inline int kvmppc_remove_spapr_tce(void *table, int pfd,
uint32_t window_size)
{
return -1;
}
#endif /* !CONFIG_USER_ONLY */
static inline const ppc_def_t *kvmppc_host_cpu_def(void)
{
return NULL;
}
#endif
#ifndef CONFIG_KVM

View File

@ -205,8 +205,10 @@ typedef struct DisasContext {
} DisasContext;
struct opc_handler_t {
/* invalid bits */
uint32_t inval;
/* invalid bits for instruction 1 (Rc(opcode) == 0) */
uint32_t inval1;
/* invalid bits for instruction 2 (Rc(opcode) == 1) */
uint32_t inval2;
/* instruction type */
uint64_t type;
/* extended instruction type */
@ -478,7 +480,23 @@ static inline target_ulong MASK(uint32_t start, uint32_t end)
.opc3 = op3, \
.pad = { 0, }, \
.handler = { \
.inval = invl, \
.inval1 = invl, \
.type = _typ, \
.type2 = _typ2, \
.handler = &gen_##name, \
.oname = stringify(name), \
}, \
.oname = stringify(name), \
}
#define GEN_OPCODE_DUAL(name, op1, op2, op3, invl1, invl2, _typ, _typ2) \
{ \
.opc1 = op1, \
.opc2 = op2, \
.opc3 = op3, \
.pad = { 0, }, \
.handler = { \
.inval1 = invl1, \
.inval2 = invl2, \
.type = _typ, \
.type2 = _typ2, \
.handler = &gen_##name, \
@ -493,7 +511,7 @@ static inline target_ulong MASK(uint32_t start, uint32_t end)
.opc3 = op3, \
.pad = { 0, }, \
.handler = { \
.inval = invl, \
.inval1 = invl, \
.type = _typ, \
.type2 = _typ2, \
.handler = &gen_##name, \
@ -509,7 +527,22 @@ static inline target_ulong MASK(uint32_t start, uint32_t end)
.opc3 = op3, \
.pad = { 0, }, \
.handler = { \
.inval = invl, \
.inval1 = invl, \
.type = _typ, \
.type2 = _typ2, \
.handler = &gen_##name, \
}, \
.oname = stringify(name), \
}
#define GEN_OPCODE_DUAL(name, op1, op2, op3, invl1, invl2, _typ, _typ2) \
{ \
.opc1 = op1, \
.opc2 = op2, \
.opc3 = op3, \
.pad = { 0, }, \
.handler = { \
.inval1 = invl1, \
.inval2 = invl2, \
.type = _typ, \
.type2 = _typ2, \
.handler = &gen_##name, \
@ -523,7 +556,7 @@ static inline target_ulong MASK(uint32_t start, uint32_t end)
.opc3 = op3, \
.pad = { 0, }, \
.handler = { \
.inval = invl, \
.inval1 = invl, \
.type = _typ, \
.type2 = _typ2, \
.handler = &gen_##name, \
@ -550,7 +583,8 @@ static void gen_invalid(DisasContext *ctx)
}
static opc_handler_t invalid_handler = {
.inval = 0xFFFFFFFF,
.inval1 = 0xFFFFFFFF,
.inval2 = 0xFFFFFFFF,
.type = PPC_NONE,
.type2 = PPC_NONE,
.handler = gen_invalid,
@ -6693,7 +6727,7 @@ static inline void gen_store_gpr64(int reg, TCGv_i64 t)
#endif
}
#define GEN_SPE(name0, name1, opc2, opc3, inval, type) \
#define GEN_SPE(name0, name1, opc2, opc3, inval0, inval1, type) \
static void glue(gen_, name0##_##name1)(DisasContext *ctx) \
{ \
if (Rc(ctx->opcode)) \
@ -7416,35 +7450,35 @@ static inline void gen_evmwsmiaa(DisasContext *ctx)
tcg_temp_free_i64(tmp);
}
GEN_SPE(evaddw, speundef, 0x00, 0x08, 0x00000000, PPC_SPE); ////
GEN_SPE(evaddiw, speundef, 0x01, 0x08, 0x00000000, PPC_SPE);
GEN_SPE(evsubfw, speundef, 0x02, 0x08, 0x00000000, PPC_SPE); ////
GEN_SPE(evsubifw, speundef, 0x03, 0x08, 0x00000000, PPC_SPE);
GEN_SPE(evabs, evneg, 0x04, 0x08, 0x0000F800, PPC_SPE); ////
GEN_SPE(evextsb, evextsh, 0x05, 0x08, 0x0000F800, PPC_SPE); ////
GEN_SPE(evrndw, evcntlzw, 0x06, 0x08, 0x0000F800, PPC_SPE); ////
GEN_SPE(evcntlsw, brinc, 0x07, 0x08, 0x00000000, PPC_SPE); //
GEN_SPE(evmra, speundef, 0x02, 0x13, 0x0000F800, PPC_SPE);
GEN_SPE(speundef, evand, 0x08, 0x08, 0x00000000, PPC_SPE); ////
GEN_SPE(evandc, speundef, 0x09, 0x08, 0x00000000, PPC_SPE); ////
GEN_SPE(evxor, evor, 0x0B, 0x08, 0x00000000, PPC_SPE); ////
GEN_SPE(evnor, eveqv, 0x0C, 0x08, 0x00000000, PPC_SPE); ////
GEN_SPE(evmwumi, evmwsmi, 0x0C, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(evmwumia, evmwsmia, 0x1C, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(evmwumiaa, evmwsmiaa, 0x0C, 0x15, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evorc, 0x0D, 0x08, 0x00000000, PPC_SPE); ////
GEN_SPE(evnand, speundef, 0x0F, 0x08, 0x00000000, PPC_SPE); ////
GEN_SPE(evsrwu, evsrws, 0x10, 0x08, 0x00000000, PPC_SPE); ////
GEN_SPE(evsrwiu, evsrwis, 0x11, 0x08, 0x00000000, PPC_SPE);
GEN_SPE(evslw, speundef, 0x12, 0x08, 0x00000000, PPC_SPE); ////
GEN_SPE(evslwi, speundef, 0x13, 0x08, 0x00000000, PPC_SPE);
GEN_SPE(evrlw, evsplati, 0x14, 0x08, 0x00000000, PPC_SPE); //
GEN_SPE(evrlwi, evsplatfi, 0x15, 0x08, 0x00000000, PPC_SPE);
GEN_SPE(evmergehi, evmergelo, 0x16, 0x08, 0x00000000, PPC_SPE); ////
GEN_SPE(evmergehilo, evmergelohi, 0x17, 0x08, 0x00000000, PPC_SPE); ////
GEN_SPE(evcmpgtu, evcmpgts, 0x18, 0x08, 0x00600000, PPC_SPE); ////
GEN_SPE(evcmpltu, evcmplts, 0x19, 0x08, 0x00600000, PPC_SPE); ////
GEN_SPE(evcmpeq, speundef, 0x1A, 0x08, 0x00600000, PPC_SPE); ////
GEN_SPE(evaddw, speundef, 0x00, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); ////
GEN_SPE(evaddiw, speundef, 0x01, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE);
GEN_SPE(evsubfw, speundef, 0x02, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); ////
GEN_SPE(evsubifw, speundef, 0x03, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE);
GEN_SPE(evabs, evneg, 0x04, 0x08, 0x0000F800, 0x0000F800, PPC_SPE); ////
GEN_SPE(evextsb, evextsh, 0x05, 0x08, 0x0000F800, 0x0000F800, PPC_SPE); ////
GEN_SPE(evrndw, evcntlzw, 0x06, 0x08, 0x0000F800, 0x0000F800, PPC_SPE); ////
GEN_SPE(evcntlsw, brinc, 0x07, 0x08, 0x0000F800, 0x00000000, PPC_SPE); //
GEN_SPE(evmra, speundef, 0x02, 0x13, 0x0000F800, 0xFFFFFFFF, PPC_SPE);
GEN_SPE(speundef, evand, 0x08, 0x08, 0xFFFFFFFF, 0x00000000, PPC_SPE); ////
GEN_SPE(evandc, speundef, 0x09, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); ////
GEN_SPE(evxor, evor, 0x0B, 0x08, 0x00000000, 0x00000000, PPC_SPE); ////
GEN_SPE(evnor, eveqv, 0x0C, 0x08, 0x00000000, 0x00000000, PPC_SPE); ////
GEN_SPE(evmwumi, evmwsmi, 0x0C, 0x11, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(evmwumia, evmwsmia, 0x1C, 0x11, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(evmwumiaa, evmwsmiaa, 0x0C, 0x15, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evorc, 0x0D, 0x08, 0xFFFFFFFF, 0x00000000, PPC_SPE); ////
GEN_SPE(evnand, speundef, 0x0F, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); ////
GEN_SPE(evsrwu, evsrws, 0x10, 0x08, 0x00000000, 0x00000000, PPC_SPE); ////
GEN_SPE(evsrwiu, evsrwis, 0x11, 0x08, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(evslw, speundef, 0x12, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); ////
GEN_SPE(evslwi, speundef, 0x13, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE);
GEN_SPE(evrlw, evsplati, 0x14, 0x08, 0x00000000, 0x0000F800, PPC_SPE); //
GEN_SPE(evrlwi, evsplatfi, 0x15, 0x08, 0x00000000, 0x0000F800, PPC_SPE);
GEN_SPE(evmergehi, evmergelo, 0x16, 0x08, 0x00000000, 0x00000000, PPC_SPE); ////
GEN_SPE(evmergehilo, evmergelohi, 0x17, 0x08, 0x00000000, 0x00000000, PPC_SPE); ////
GEN_SPE(evcmpgtu, evcmpgts, 0x18, 0x08, 0x00600000, 0x00600000, PPC_SPE); ////
GEN_SPE(evcmpltu, evcmplts, 0x19, 0x08, 0x00600000, 0x00600000, PPC_SPE); ////
GEN_SPE(evcmpeq, speundef, 0x1A, 0x08, 0x00600000, 0xFFFFFFFF, PPC_SPE); ////
/* SPE load and stores */
static inline void gen_addr_spe_imm_index(DisasContext *ctx, TCGv EA, int sh)
@ -7803,74 +7837,74 @@ GEN_SPEOP_LDST(evstwwo, 0x1E, 2);
/* Multiply and add - TODO */
#if 0
GEN_SPE(speundef, evmhessf, 0x01, 0x10, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhossf, 0x03, 0x10, 0x00000000, PPC_SPE);
GEN_SPE(evmheumi, evmhesmi, 0x04, 0x10, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhesmf, 0x05, 0x10, 0x00000000, PPC_SPE);
GEN_SPE(evmhoumi, evmhosmi, 0x06, 0x10, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhosmf, 0x07, 0x10, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhessfa, 0x11, 0x10, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhossfa, 0x13, 0x10, 0x00000000, PPC_SPE);
GEN_SPE(evmheumia, evmhesmia, 0x14, 0x10, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhesmfa, 0x15, 0x10, 0x00000000, PPC_SPE);
GEN_SPE(evmhoumia, evmhosmia, 0x16, 0x10, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhosmfa, 0x17, 0x10, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhessf, 0x01, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE);//
GEN_SPE(speundef, evmhossf, 0x03, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmheumi, evmhesmi, 0x04, 0x10, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhesmf, 0x05, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmhoumi, evmhosmi, 0x06, 0x10, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhosmf, 0x07, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhessfa, 0x11, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhossfa, 0x13, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmheumia, evmhesmia, 0x14, 0x10, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhesmfa, 0x15, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmhoumia, evmhosmia, 0x16, 0x10, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhosmfa, 0x17, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwhssf, 0x03, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(evmwlumi, speundef, 0x04, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(evmwhumi, evmwhsmi, 0x06, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwhsmf, 0x07, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwssf, 0x09, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwsmf, 0x0D, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwhssfa, 0x13, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(evmwlumia, speundef, 0x14, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(evmwhumia, evmwhsmia, 0x16, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwhsmfa, 0x17, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwssfa, 0x19, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwsmfa, 0x1D, 0x11, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwhssf, 0x03, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmwlumi, speundef, 0x04, 0x11, 0x00000000, 0xFFFFFFFF, PPC_SPE);
GEN_SPE(evmwhumi, evmwhsmi, 0x06, 0x11, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwhsmf, 0x07, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwssf, 0x09, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwsmf, 0x0D, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwhssfa, 0x13, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmwlumia, speundef, 0x14, 0x11, 0x00000000, 0xFFFFFFFF, PPC_SPE);
GEN_SPE(evmwhumia, evmwhsmia, 0x16, 0x11, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwhsmfa, 0x17, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwssfa, 0x19, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwsmfa, 0x1D, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evadduiaaw, evaddsiaaw, 0x00, 0x13, 0x0000F800, PPC_SPE);
GEN_SPE(evsubfusiaaw, evsubfssiaaw, 0x01, 0x13, 0x0000F800, PPC_SPE);
GEN_SPE(evaddumiaaw, evaddsmiaaw, 0x04, 0x13, 0x0000F800, PPC_SPE);
GEN_SPE(evsubfumiaaw, evsubfsmiaaw, 0x05, 0x13, 0x0000F800, PPC_SPE);
GEN_SPE(evdivws, evdivwu, 0x06, 0x13, 0x00000000, PPC_SPE);
GEN_SPE(evadduiaaw, evaddsiaaw, 0x00, 0x13, 0x0000F800, 0x0000F800, PPC_SPE);
GEN_SPE(evsubfusiaaw, evsubfssiaaw, 0x01, 0x13, 0x0000F800, 0x0000F800, PPC_SPE);
GEN_SPE(evaddumiaaw, evaddsmiaaw, 0x04, 0x13, 0x0000F800, 0x0000F800, PPC_SPE);
GEN_SPE(evsubfumiaaw, evsubfsmiaaw, 0x05, 0x13, 0x0000F800, 0x0000F800, PPC_SPE);
GEN_SPE(evdivws, evdivwu, 0x06, 0x13, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(evmheusiaaw, evmhessiaaw, 0x00, 0x14, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhessfaaw, 0x01, 0x14, 0x00000000, PPC_SPE);
GEN_SPE(evmhousiaaw, evmhossiaaw, 0x02, 0x14, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhossfaaw, 0x03, 0x14, 0x00000000, PPC_SPE);
GEN_SPE(evmheumiaaw, evmhesmiaaw, 0x04, 0x14, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhesmfaaw, 0x05, 0x14, 0x00000000, PPC_SPE);
GEN_SPE(evmhoumiaaw, evmhosmiaaw, 0x06, 0x14, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhosmfaaw, 0x07, 0x14, 0x00000000, PPC_SPE);
GEN_SPE(evmhegumiaa, evmhegsmiaa, 0x14, 0x14, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhegsmfaa, 0x15, 0x14, 0x00000000, PPC_SPE);
GEN_SPE(evmhogumiaa, evmhogsmiaa, 0x16, 0x14, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhogsmfaa, 0x17, 0x14, 0x00000000, PPC_SPE);
GEN_SPE(evmheusiaaw, evmhessiaaw, 0x00, 0x14, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhessfaaw, 0x01, 0x14, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmhousiaaw, evmhossiaaw, 0x02, 0x14, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhossfaaw, 0x03, 0x14, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmheumiaaw, evmhesmiaaw, 0x04, 0x14, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhesmfaaw, 0x05, 0x14, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmhoumiaaw, evmhosmiaaw, 0x06, 0x14, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhosmfaaw, 0x07, 0x14, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmhegumiaa, evmhegsmiaa, 0x14, 0x14, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhegsmfaa, 0x15, 0x14, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmhogumiaa, evmhogsmiaa, 0x16, 0x14, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhogsmfaa, 0x17, 0x14, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmwlusiaaw, evmwlssiaaw, 0x00, 0x15, 0x00000000, PPC_SPE);
GEN_SPE(evmwlumiaaw, evmwlsmiaaw, 0x04, 0x15, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwssfaa, 0x09, 0x15, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwsmfaa, 0x0D, 0x15, 0x00000000, PPC_SPE);
GEN_SPE(evmwlusiaaw, evmwlssiaaw, 0x00, 0x15, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(evmwlumiaaw, evmwlsmiaaw, 0x04, 0x15, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwssfaa, 0x09, 0x15, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwsmfaa, 0x0D, 0x15, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmheusianw, evmhessianw, 0x00, 0x16, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhessfanw, 0x01, 0x16, 0x00000000, PPC_SPE);
GEN_SPE(evmhousianw, evmhossianw, 0x02, 0x16, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhossfanw, 0x03, 0x16, 0x00000000, PPC_SPE);
GEN_SPE(evmheumianw, evmhesmianw, 0x04, 0x16, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhesmfanw, 0x05, 0x16, 0x00000000, PPC_SPE);
GEN_SPE(evmhoumianw, evmhosmianw, 0x06, 0x16, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhosmfanw, 0x07, 0x16, 0x00000000, PPC_SPE);
GEN_SPE(evmhegumian, evmhegsmian, 0x14, 0x16, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhegsmfan, 0x15, 0x16, 0x00000000, PPC_SPE);
GEN_SPE(evmhigumian, evmhigsmian, 0x16, 0x16, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhogsmfan, 0x17, 0x16, 0x00000000, PPC_SPE);
GEN_SPE(evmheusianw, evmhessianw, 0x00, 0x16, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhessfanw, 0x01, 0x16, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmhousianw, evmhossianw, 0x02, 0x16, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhossfanw, 0x03, 0x16, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmheumianw, evmhesmianw, 0x04, 0x16, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhesmfanw, 0x05, 0x16, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmhoumianw, evmhosmianw, 0x06, 0x16, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhosmfanw, 0x07, 0x16, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmhegumian, evmhegsmian, 0x14, 0x16, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhegsmfan, 0x15, 0x16, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmhigumian, evmhigsmian, 0x16, 0x16, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmhogsmfan, 0x17, 0x16, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmwlusianw, evmwlssianw, 0x00, 0x17, 0x00000000, PPC_SPE);
GEN_SPE(evmwlumianw, evmwlsmianw, 0x04, 0x17, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwssfan, 0x09, 0x17, 0x00000000, PPC_SPE);
GEN_SPE(evmwumian, evmwsmian, 0x0C, 0x17, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwsmfan, 0x0D, 0x17, 0x00000000, PPC_SPE);
GEN_SPE(evmwlusianw, evmwlssianw, 0x00, 0x17, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(evmwlumianw, evmwlsmianw, 0x04, 0x17, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwssfan, 0x09, 0x17, 0xFFFFFFFF, 0x00000000, PPC_SPE);
GEN_SPE(evmwumian, evmwsmian, 0x0C, 0x17, 0x00000000, 0x00000000, PPC_SPE);
GEN_SPE(speundef, evmwsmfan, 0x0D, 0x17, 0xFFFFFFFF, 0x00000000, PPC_SPE);
#endif
/*** SPE floating-point extension ***/
@ -8131,20 +8165,20 @@ GEN_SPEFPUOP_COMP_64(evfststlt);
GEN_SPEFPUOP_COMP_64(evfststeq);
/* Opcodes definitions */
GEN_SPE(evfsadd, evfssub, 0x00, 0x0A, 0x00000000, PPC_SPE_SINGLE); //
GEN_SPE(evfsabs, evfsnabs, 0x02, 0x0A, 0x0000F800, PPC_SPE_SINGLE); //
GEN_SPE(evfsneg, speundef, 0x03, 0x0A, 0x0000F800, PPC_SPE_SINGLE); //
GEN_SPE(evfsmul, evfsdiv, 0x04, 0x0A, 0x00000000, PPC_SPE_SINGLE); //
GEN_SPE(evfscmpgt, evfscmplt, 0x06, 0x0A, 0x00600000, PPC_SPE_SINGLE); //
GEN_SPE(evfscmpeq, speundef, 0x07, 0x0A, 0x00600000, PPC_SPE_SINGLE); //
GEN_SPE(evfscfui, evfscfsi, 0x08, 0x0A, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(evfscfuf, evfscfsf, 0x09, 0x0A, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(evfsctui, evfsctsi, 0x0A, 0x0A, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(evfsctuf, evfsctsf, 0x0B, 0x0A, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(evfsctuiz, speundef, 0x0C, 0x0A, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(evfsctsiz, speundef, 0x0D, 0x0A, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(evfststgt, evfststlt, 0x0E, 0x0A, 0x00600000, PPC_SPE_SINGLE); //
GEN_SPE(evfststeq, speundef, 0x0F, 0x0A, 0x00600000, PPC_SPE_SINGLE); //
GEN_SPE(evfsadd, evfssub, 0x00, 0x0A, 0x00000000, 0x00000000, PPC_SPE_SINGLE); //
GEN_SPE(evfsabs, evfsnabs, 0x02, 0x0A, 0x0000F800, 0x0000F800, PPC_SPE_SINGLE); //
GEN_SPE(evfsneg, speundef, 0x03, 0x0A, 0x0000F800, 0xFFFFFFFF, PPC_SPE_SINGLE); //
GEN_SPE(evfsmul, evfsdiv, 0x04, 0x0A, 0x00000000, 0x00000000, PPC_SPE_SINGLE); //
GEN_SPE(evfscmpgt, evfscmplt, 0x06, 0x0A, 0x00600000, 0x00600000, PPC_SPE_SINGLE); //
GEN_SPE(evfscmpeq, speundef, 0x07, 0x0A, 0x00600000, 0xFFFFFFFF, PPC_SPE_SINGLE); //
GEN_SPE(evfscfui, evfscfsi, 0x08, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(evfscfuf, evfscfsf, 0x09, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(evfsctui, evfsctsi, 0x0A, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(evfsctuf, evfsctsf, 0x0B, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(evfsctuiz, speundef, 0x0C, 0x0A, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE); //
GEN_SPE(evfsctsiz, speundef, 0x0D, 0x0A, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE); //
GEN_SPE(evfststgt, evfststlt, 0x0E, 0x0A, 0x00600000, 0x00600000, PPC_SPE_SINGLE); //
GEN_SPE(evfststeq, speundef, 0x0F, 0x0A, 0x00600000, 0xFFFFFFFF, PPC_SPE_SINGLE); //
/* Single precision floating-point operations */
/* Arithmetic */
@ -8199,20 +8233,20 @@ GEN_SPEFPUOP_COMP_32(efststlt);
GEN_SPEFPUOP_COMP_32(efststeq);
/* Opcodes definitions */
GEN_SPE(efsadd, efssub, 0x00, 0x0B, 0x00000000, PPC_SPE_SINGLE); //
GEN_SPE(efsabs, efsnabs, 0x02, 0x0B, 0x0000F800, PPC_SPE_SINGLE); //
GEN_SPE(efsneg, speundef, 0x03, 0x0B, 0x0000F800, PPC_SPE_SINGLE); //
GEN_SPE(efsmul, efsdiv, 0x04, 0x0B, 0x00000000, PPC_SPE_SINGLE); //
GEN_SPE(efscmpgt, efscmplt, 0x06, 0x0B, 0x00600000, PPC_SPE_SINGLE); //
GEN_SPE(efscmpeq, efscfd, 0x07, 0x0B, 0x00600000, PPC_SPE_SINGLE); //
GEN_SPE(efscfui, efscfsi, 0x08, 0x0B, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(efscfuf, efscfsf, 0x09, 0x0B, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(efsctui, efsctsi, 0x0A, 0x0B, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(efsctuf, efsctsf, 0x0B, 0x0B, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(efsctuiz, speundef, 0x0C, 0x0B, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(efsctsiz, speundef, 0x0D, 0x0B, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(efststgt, efststlt, 0x0E, 0x0B, 0x00600000, PPC_SPE_SINGLE); //
GEN_SPE(efststeq, speundef, 0x0F, 0x0B, 0x00600000, PPC_SPE_SINGLE); //
GEN_SPE(efsadd, efssub, 0x00, 0x0B, 0x00000000, 0x00000000, PPC_SPE_SINGLE); //
GEN_SPE(efsabs, efsnabs, 0x02, 0x0B, 0x0000F800, 0x0000F800, PPC_SPE_SINGLE); //
GEN_SPE(efsneg, speundef, 0x03, 0x0B, 0x0000F800, 0xFFFFFFFF, PPC_SPE_SINGLE); //
GEN_SPE(efsmul, efsdiv, 0x04, 0x0B, 0x00000000, 0x00000000, PPC_SPE_SINGLE); //
GEN_SPE(efscmpgt, efscmplt, 0x06, 0x0B, 0x00600000, 0x00600000, PPC_SPE_SINGLE); //
GEN_SPE(efscmpeq, efscfd, 0x07, 0x0B, 0x00600000, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(efscfui, efscfsi, 0x08, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(efscfuf, efscfsf, 0x09, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(efsctui, efsctsi, 0x0A, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(efsctuf, efsctsf, 0x0B, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE); //
GEN_SPE(efsctuiz, speundef, 0x0C, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE); //
GEN_SPE(efsctsiz, speundef, 0x0D, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE); //
GEN_SPE(efststgt, efststlt, 0x0E, 0x0B, 0x00600000, 0x00600000, PPC_SPE_SINGLE); //
GEN_SPE(efststeq, speundef, 0x0F, 0x0B, 0x00600000, 0xFFFFFFFF, PPC_SPE_SINGLE); //
/* Double precision floating-point operations */
/* Arithmetic */
@ -8286,22 +8320,22 @@ GEN_SPEFPUOP_COMP_64(efdtstlt);
GEN_SPEFPUOP_COMP_64(efdtsteq);
/* Opcodes definitions */
GEN_SPE(efdadd, efdsub, 0x10, 0x0B, 0x00000000, PPC_SPE_DOUBLE); //
GEN_SPE(efdcfuid, efdcfsid, 0x11, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdabs, efdnabs, 0x12, 0x0B, 0x0000F800, PPC_SPE_DOUBLE); //
GEN_SPE(efdneg, speundef, 0x13, 0x0B, 0x0000F800, PPC_SPE_DOUBLE); //
GEN_SPE(efdmul, efddiv, 0x14, 0x0B, 0x00000000, PPC_SPE_DOUBLE); //
GEN_SPE(efdctuidz, efdctsidz, 0x15, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdcmpgt, efdcmplt, 0x16, 0x0B, 0x00600000, PPC_SPE_DOUBLE); //
GEN_SPE(efdcmpeq, efdcfs, 0x17, 0x0B, 0x00600000, PPC_SPE_DOUBLE); //
GEN_SPE(efdcfui, efdcfsi, 0x18, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdcfuf, efdcfsf, 0x19, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdctui, efdctsi, 0x1A, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdctuf, efdctsf, 0x1B, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdctuiz, speundef, 0x1C, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdctsiz, speundef, 0x1D, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdtstgt, efdtstlt, 0x1E, 0x0B, 0x00600000, PPC_SPE_DOUBLE); //
GEN_SPE(efdtsteq, speundef, 0x1F, 0x0B, 0x00600000, PPC_SPE_DOUBLE); //
GEN_SPE(efdadd, efdsub, 0x10, 0x0B, 0x00000000, 0x00000000, PPC_SPE_DOUBLE); //
GEN_SPE(efdcfuid, efdcfsid, 0x11, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdabs, efdnabs, 0x12, 0x0B, 0x0000F800, 0x0000F800, PPC_SPE_DOUBLE); //
GEN_SPE(efdneg, speundef, 0x13, 0x0B, 0x0000F800, 0xFFFFFFFF, PPC_SPE_DOUBLE); //
GEN_SPE(efdmul, efddiv, 0x14, 0x0B, 0x00000000, 0x00000000, PPC_SPE_DOUBLE); //
GEN_SPE(efdctuidz, efdctsidz, 0x15, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdcmpgt, efdcmplt, 0x16, 0x0B, 0x00600000, 0x00600000, PPC_SPE_DOUBLE); //
GEN_SPE(efdcmpeq, efdcfs, 0x17, 0x0B, 0x00600000, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdcfui, efdcfsi, 0x18, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdcfuf, efdcfsf, 0x19, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdctui, efdctsi, 0x1A, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdctuf, efdctsf, 0x1B, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE); //
GEN_SPE(efdctuiz, speundef, 0x1C, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_DOUBLE); //
GEN_SPE(efdctsiz, speundef, 0x1D, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_DOUBLE); //
GEN_SPE(efdtstgt, efdtstlt, 0x1E, 0x0B, 0x00600000, 0x00600000, PPC_SPE_DOUBLE); //
GEN_SPE(efdtsteq, speundef, 0x1F, 0x0B, 0x00600000, 0xFFFFFFFF, PPC_SPE_DOUBLE); //
static opcode_t opcodes[] = {
GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE),
@ -9070,84 +9104,84 @@ GEN_VAFORM_PAIRED(vsel, vperm, 21),
GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23),
#undef GEN_SPE
#define GEN_SPE(name0, name1, opc2, opc3, inval, type) \
GEN_HANDLER(name0##_##name1, 0x04, opc2, opc3, inval, type)
GEN_SPE(evaddw, speundef, 0x00, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evaddiw, speundef, 0x01, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evsubfw, speundef, 0x02, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evsubifw, speundef, 0x03, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evabs, evneg, 0x04, 0x08, 0x0000F800, PPC_SPE),
GEN_SPE(evextsb, evextsh, 0x05, 0x08, 0x0000F800, PPC_SPE),
GEN_SPE(evrndw, evcntlzw, 0x06, 0x08, 0x0000F800, PPC_SPE),
GEN_SPE(evcntlsw, brinc, 0x07, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evmra, speundef, 0x02, 0x13, 0x0000F800, PPC_SPE),
GEN_SPE(speundef, evand, 0x08, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evandc, speundef, 0x09, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evxor, evor, 0x0B, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evnor, eveqv, 0x0C, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evmwumi, evmwsmi, 0x0C, 0x11, 0x00000000, PPC_SPE),
GEN_SPE(evmwumia, evmwsmia, 0x1C, 0x11, 0x00000000, PPC_SPE),
GEN_SPE(evmwumiaa, evmwsmiaa, 0x0C, 0x15, 0x00000000, PPC_SPE),
GEN_SPE(speundef, evorc, 0x0D, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evnand, speundef, 0x0F, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evsrwu, evsrws, 0x10, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evsrwiu, evsrwis, 0x11, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evslw, speundef, 0x12, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evslwi, speundef, 0x13, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evrlw, evsplati, 0x14, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evrlwi, evsplatfi, 0x15, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evmergehi, evmergelo, 0x16, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evmergehilo, evmergelohi, 0x17, 0x08, 0x00000000, PPC_SPE),
GEN_SPE(evcmpgtu, evcmpgts, 0x18, 0x08, 0x00600000, PPC_SPE),
GEN_SPE(evcmpltu, evcmplts, 0x19, 0x08, 0x00600000, PPC_SPE),
GEN_SPE(evcmpeq, speundef, 0x1A, 0x08, 0x00600000, PPC_SPE),
#define GEN_SPE(name0, name1, opc2, opc3, inval0, inval1, type) \
GEN_OPCODE_DUAL(name0##_##name1, 0x04, opc2, opc3, inval0, inval1, type, PPC_NONE)
GEN_SPE(evaddw, speundef, 0x00, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE),
GEN_SPE(evaddiw, speundef, 0x01, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE),
GEN_SPE(evsubfw, speundef, 0x02, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE),
GEN_SPE(evsubifw, speundef, 0x03, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE),
GEN_SPE(evabs, evneg, 0x04, 0x08, 0x0000F800, 0x0000F800, PPC_SPE),
GEN_SPE(evextsb, evextsh, 0x05, 0x08, 0x0000F800, 0x0000F800, PPC_SPE),
GEN_SPE(evrndw, evcntlzw, 0x06, 0x08, 0x0000F800, 0x0000F800, PPC_SPE),
GEN_SPE(evcntlsw, brinc, 0x07, 0x08, 0x0000F800, 0x00000000, PPC_SPE),
GEN_SPE(evmra, speundef, 0x02, 0x13, 0x0000F800, 0xFFFFFFFF, PPC_SPE),
GEN_SPE(speundef, evand, 0x08, 0x08, 0xFFFFFFFF, 0x00000000, PPC_SPE),
GEN_SPE(evandc, speundef, 0x09, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE),
GEN_SPE(evxor, evor, 0x0B, 0x08, 0x00000000, 0x00000000, PPC_SPE),
GEN_SPE(evnor, eveqv, 0x0C, 0x08, 0x00000000, 0x00000000, PPC_SPE),
GEN_SPE(evmwumi, evmwsmi, 0x0C, 0x11, 0x00000000, 0x00000000, PPC_SPE),
GEN_SPE(evmwumia, evmwsmia, 0x1C, 0x11, 0x00000000, 0x00000000, PPC_SPE),
GEN_SPE(evmwumiaa, evmwsmiaa, 0x0C, 0x15, 0x00000000, 0x00000000, PPC_SPE),
GEN_SPE(speundef, evorc, 0x0D, 0x08, 0xFFFFFFFF, 0x00000000, PPC_SPE),
GEN_SPE(evnand, speundef, 0x0F, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE),
GEN_SPE(evsrwu, evsrws, 0x10, 0x08, 0x00000000, 0x00000000, PPC_SPE),
GEN_SPE(evsrwiu, evsrwis, 0x11, 0x08, 0x00000000, 0x00000000, PPC_SPE),
GEN_SPE(evslw, speundef, 0x12, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE),
GEN_SPE(evslwi, speundef, 0x13, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE),
GEN_SPE(evrlw, evsplati, 0x14, 0x08, 0x00000000, 0x0000F800, PPC_SPE),
GEN_SPE(evrlwi, evsplatfi, 0x15, 0x08, 0x00000000, 0x0000F800, PPC_SPE),
GEN_SPE(evmergehi, evmergelo, 0x16, 0x08, 0x00000000, 0x00000000, PPC_SPE),
GEN_SPE(evmergehilo, evmergelohi, 0x17, 0x08, 0x00000000, 0x00000000, PPC_SPE),
GEN_SPE(evcmpgtu, evcmpgts, 0x18, 0x08, 0x00600000, 0x00600000, PPC_SPE),
GEN_SPE(evcmpltu, evcmplts, 0x19, 0x08, 0x00600000, 0x00600000, PPC_SPE),
GEN_SPE(evcmpeq, speundef, 0x1A, 0x08, 0x00600000, 0xFFFFFFFF, PPC_SPE),
GEN_SPE(evfsadd, evfssub, 0x00, 0x0A, 0x00000000, PPC_SPE_SINGLE),
GEN_SPE(evfsabs, evfsnabs, 0x02, 0x0A, 0x0000F800, PPC_SPE_SINGLE),
GEN_SPE(evfsneg, speundef, 0x03, 0x0A, 0x0000F800, PPC_SPE_SINGLE),
GEN_SPE(evfsmul, evfsdiv, 0x04, 0x0A, 0x00000000, PPC_SPE_SINGLE),
GEN_SPE(evfscmpgt, evfscmplt, 0x06, 0x0A, 0x00600000, PPC_SPE_SINGLE),
GEN_SPE(evfscmpeq, speundef, 0x07, 0x0A, 0x00600000, PPC_SPE_SINGLE),
GEN_SPE(evfscfui, evfscfsi, 0x08, 0x0A, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(evfscfuf, evfscfsf, 0x09, 0x0A, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(evfsctui, evfsctsi, 0x0A, 0x0A, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(evfsctuf, evfsctsf, 0x0B, 0x0A, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(evfsctuiz, speundef, 0x0C, 0x0A, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(evfsctsiz, speundef, 0x0D, 0x0A, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(evfststgt, evfststlt, 0x0E, 0x0A, 0x00600000, PPC_SPE_SINGLE),
GEN_SPE(evfststeq, speundef, 0x0F, 0x0A, 0x00600000, PPC_SPE_SINGLE),
GEN_SPE(evfsadd, evfssub, 0x00, 0x0A, 0x00000000, 0x00000000, PPC_SPE_SINGLE),
GEN_SPE(evfsabs, evfsnabs, 0x02, 0x0A, 0x0000F800, 0x0000F800, PPC_SPE_SINGLE),
GEN_SPE(evfsneg, speundef, 0x03, 0x0A, 0x0000F800, 0xFFFFFFFF, PPC_SPE_SINGLE),
GEN_SPE(evfsmul, evfsdiv, 0x04, 0x0A, 0x00000000, 0x00000000, PPC_SPE_SINGLE),
GEN_SPE(evfscmpgt, evfscmplt, 0x06, 0x0A, 0x00600000, 0x00600000, PPC_SPE_SINGLE),
GEN_SPE(evfscmpeq, speundef, 0x07, 0x0A, 0x00600000, 0xFFFFFFFF, PPC_SPE_SINGLE),
GEN_SPE(evfscfui, evfscfsi, 0x08, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(evfscfuf, evfscfsf, 0x09, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(evfsctui, evfsctsi, 0x0A, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(evfsctuf, evfsctsf, 0x0B, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(evfsctuiz, speundef, 0x0C, 0x0A, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE),
GEN_SPE(evfsctsiz, speundef, 0x0D, 0x0A, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE),
GEN_SPE(evfststgt, evfststlt, 0x0E, 0x0A, 0x00600000, 0x00600000, PPC_SPE_SINGLE),
GEN_SPE(evfststeq, speundef, 0x0F, 0x0A, 0x00600000, 0xFFFFFFFF, PPC_SPE_SINGLE),
GEN_SPE(efsadd, efssub, 0x00, 0x0B, 0x00000000, PPC_SPE_SINGLE),
GEN_SPE(efsabs, efsnabs, 0x02, 0x0B, 0x0000F800, PPC_SPE_SINGLE),
GEN_SPE(efsneg, speundef, 0x03, 0x0B, 0x0000F800, PPC_SPE_SINGLE),
GEN_SPE(efsmul, efsdiv, 0x04, 0x0B, 0x00000000, PPC_SPE_SINGLE),
GEN_SPE(efscmpgt, efscmplt, 0x06, 0x0B, 0x00600000, PPC_SPE_SINGLE),
GEN_SPE(efscmpeq, efscfd, 0x07, 0x0B, 0x00600000, PPC_SPE_SINGLE),
GEN_SPE(efscfui, efscfsi, 0x08, 0x0B, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(efscfuf, efscfsf, 0x09, 0x0B, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(efsctui, efsctsi, 0x0A, 0x0B, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(efsctuf, efsctsf, 0x0B, 0x0B, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(efsctuiz, speundef, 0x0C, 0x0B, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(efsctsiz, speundef, 0x0D, 0x0B, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(efststgt, efststlt, 0x0E, 0x0B, 0x00600000, PPC_SPE_SINGLE),
GEN_SPE(efststeq, speundef, 0x0F, 0x0B, 0x00600000, PPC_SPE_SINGLE),
GEN_SPE(efsadd, efssub, 0x00, 0x0B, 0x00000000, 0x00000000, PPC_SPE_SINGLE),
GEN_SPE(efsabs, efsnabs, 0x02, 0x0B, 0x0000F800, 0x0000F800, PPC_SPE_SINGLE),
GEN_SPE(efsneg, speundef, 0x03, 0x0B, 0x0000F800, 0xFFFFFFFF, PPC_SPE_SINGLE),
GEN_SPE(efsmul, efsdiv, 0x04, 0x0B, 0x00000000, 0x00000000, PPC_SPE_SINGLE),
GEN_SPE(efscmpgt, efscmplt, 0x06, 0x0B, 0x00600000, 0x00600000, PPC_SPE_SINGLE),
GEN_SPE(efscmpeq, efscfd, 0x07, 0x0B, 0x00600000, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(efscfui, efscfsi, 0x08, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(efscfuf, efscfsf, 0x09, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(efsctui, efsctsi, 0x0A, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(efsctuf, efsctsf, 0x0B, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE),
GEN_SPE(efsctuiz, speundef, 0x0C, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE),
GEN_SPE(efsctsiz, speundef, 0x0D, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE),
GEN_SPE(efststgt, efststlt, 0x0E, 0x0B, 0x00600000, 0x00600000, PPC_SPE_SINGLE),
GEN_SPE(efststeq, speundef, 0x0F, 0x0B, 0x00600000, 0xFFFFFFFF, PPC_SPE_SINGLE),
GEN_SPE(efdadd, efdsub, 0x10, 0x0B, 0x00000000, PPC_SPE_DOUBLE),
GEN_SPE(efdcfuid, efdcfsid, 0x11, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdabs, efdnabs, 0x12, 0x0B, 0x0000F800, PPC_SPE_DOUBLE),
GEN_SPE(efdneg, speundef, 0x13, 0x0B, 0x0000F800, PPC_SPE_DOUBLE),
GEN_SPE(efdmul, efddiv, 0x14, 0x0B, 0x00000000, PPC_SPE_DOUBLE),
GEN_SPE(efdctuidz, efdctsidz, 0x15, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdcmpgt, efdcmplt, 0x16, 0x0B, 0x00600000, PPC_SPE_DOUBLE),
GEN_SPE(efdcmpeq, efdcfs, 0x17, 0x0B, 0x00600000, PPC_SPE_DOUBLE),
GEN_SPE(efdcfui, efdcfsi, 0x18, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdcfuf, efdcfsf, 0x19, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdctui, efdctsi, 0x1A, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdctuf, efdctsf, 0x1B, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdctuiz, speundef, 0x1C, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdctsiz, speundef, 0x1D, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdtstgt, efdtstlt, 0x1E, 0x0B, 0x00600000, PPC_SPE_DOUBLE),
GEN_SPE(efdtsteq, speundef, 0x1F, 0x0B, 0x00600000, PPC_SPE_DOUBLE),
GEN_SPE(efdadd, efdsub, 0x10, 0x0B, 0x00000000, 0x00000000, PPC_SPE_DOUBLE),
GEN_SPE(efdcfuid, efdcfsid, 0x11, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdabs, efdnabs, 0x12, 0x0B, 0x0000F800, 0x0000F800, PPC_SPE_DOUBLE),
GEN_SPE(efdneg, speundef, 0x13, 0x0B, 0x0000F800, 0xFFFFFFFF, PPC_SPE_DOUBLE),
GEN_SPE(efdmul, efddiv, 0x14, 0x0B, 0x00000000, 0x00000000, PPC_SPE_DOUBLE),
GEN_SPE(efdctuidz, efdctsidz, 0x15, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdcmpgt, efdcmplt, 0x16, 0x0B, 0x00600000, 0x00600000, PPC_SPE_DOUBLE),
GEN_SPE(efdcmpeq, efdcfs, 0x17, 0x0B, 0x00600000, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdcfui, efdcfsi, 0x18, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdcfuf, efdcfsf, 0x19, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdctui, efdctsi, 0x1A, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdctuf, efdctsf, 0x1B, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE),
GEN_SPE(efdctuiz, speundef, 0x1C, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_DOUBLE),
GEN_SPE(efdctsiz, speundef, 0x1D, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_DOUBLE),
GEN_SPE(efdtstgt, efdtstlt, 0x1E, 0x0B, 0x00600000, 0x00600000, PPC_SPE_DOUBLE),
GEN_SPE(efdtsteq, speundef, 0x1F, 0x0B, 0x00600000, 0xFFFFFFFF, PPC_SPE_DOUBLE),
#undef GEN_SPEOP_LDST
#define GEN_SPEOP_LDST(name, opc2, sh) \
@ -9484,11 +9518,19 @@ static inline void gen_intermediate_code_internal(CPUState *env,
opc3(ctx.opcode), ctx.opcode, ctx.nip - 4, (int)msr_ir);
}
} else {
if (unlikely((ctx.opcode & handler->inval) != 0)) {
uint32_t inval;
if (unlikely(handler->type & (PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE) && Rc(ctx.opcode))) {
inval = handler->inval2;
} else {
inval = handler->inval1;
}
if (unlikely((ctx.opcode & inval) != 0)) {
if (qemu_log_enabled()) {
qemu_log("invalid bits: %08x for opcode: "
"%02x - %02x - %02x (%08x) " TARGET_FMT_lx "\n",
ctx.opcode & handler->inval, opc1(ctx.opcode),
ctx.opcode & inval, opc1(ctx.opcode),
opc2(ctx.opcode), opc3(ctx.opcode),
ctx.opcode, ctx.nip - 4);
}

View File

@ -24,6 +24,8 @@
#include "dis-asm.h"
#include "gdbstub.h"
#include <kvm.h>
#include "kvm_ppc.h"
//#define PPC_DUMP_CPU
//#define PPC_DEBUG_SPR
@ -32,22 +34,6 @@
#define TODO_USER_ONLY 1
#endif
struct ppc_def_t {
const char *name;
uint32_t pvr;
uint32_t svr;
uint64_t insns_flags;
uint64_t insns_flags2;
uint64_t msr_mask;
powerpc_mmu_t mmu_model;
powerpc_excp_t excp_model;
powerpc_input_t bus_model;
uint32_t flags;
int bfd_mach;
void (*init_proc)(CPUPPCState *env);
int (*check_pow)(CPUPPCState *env);
};
/* For user-mode emulation, we don't emulate any IRQ controller */
#if defined(CONFIG_USER_ONLY)
#define PPC_IRQ_INIT_FN(name) \
@ -6533,7 +6519,7 @@ static void init_proc_970MP (CPUPPCState *env)
PPC_64B | PPC_ALTIVEC | \
PPC_SEGMENT_64B | PPC_SLBI | \
PPC_POPCNTB | PPC_POPCNTWD)
#define POWERPC_INSNS2_POWER7 (PPC_NONE)
#define POWERPC_INSNS2_POWER7 (PPC2_VSX | PPC2_DFP)
#define POWERPC_MSRM_POWER7 (0x800000000204FF36ULL)
#define POWERPC_MMU_POWER7 (POWERPC_MMU_2_06)
#define POWERPC_EXCP_POWER7 (POWERPC_EXCP_POWER7)
@ -7331,6 +7317,8 @@ enum {
CPU_POWERPC_POWER6A = 0x0F000002,
#define CPU_POWERPC_POWER7 CPU_POWERPC_POWER7_v20
CPU_POWERPC_POWER7_v20 = 0x003F0200,
CPU_POWERPC_POWER7_v21 = 0x003F0201,
CPU_POWERPC_POWER7_v23 = 0x003F0203,
CPU_POWERPC_970 = 0x00390202,
#define CPU_POWERPC_970FX CPU_POWERPC_970FX_v31
CPU_POWERPC_970FX_v10 = 0x00391100,
@ -9137,6 +9125,8 @@ static const ppc_def_t ppc_defs[] = {
/* POWER7 */
POWERPC_DEF("POWER7", CPU_POWERPC_POWER7, POWER7),
POWERPC_DEF("POWER7_v2.0", CPU_POWERPC_POWER7_v20, POWER7),
POWERPC_DEF("POWER7_v2.1", CPU_POWERPC_POWER7_v21, POWER7),
POWERPC_DEF("POWER7_v2.3", CPU_POWERPC_POWER7_v23, POWER7),
/* PowerPC 970 */
POWERPC_DEF("970", CPU_POWERPC_970, 970),
/* PowerPC 970FX (G5) */
@ -9856,6 +9846,22 @@ int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def)
env->bus_model = def->bus_model;
env->insns_flags = def->insns_flags;
env->insns_flags2 = def->insns_flags2;
if (!kvm_enabled()) {
/* TCG doesn't (yet) emulate some groups of instructions that
* are implemented on some otherwise supported CPUs (e.g. VSX
* and decimal floating point instructions on POWER7). We
* remove unsupported instruction groups from the cpu state's
* instruction masks and hope the guest can cope. For at
* least the pseries machine, the unavailability of these
* instructions can be advertise to the guest via the device
* tree.
*
* FIXME: we should have a similar masking for CPU features
* not accessible under KVM, but so far, there aren't any of
* those. */
env->insns_flags &= PPC_TCG_INSNS;
env->insns_flags2 &= PPC_TCG_INSNS2;
}
env->flags = def->flags;
env->bfd_mach = def->bfd_mach;
env->check_pow = def->check_pow;
@ -10041,42 +10047,34 @@ int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def)
return 0;
}
static const ppc_def_t *ppc_find_by_pvr (uint32_t pvr)
static bool ppc_cpu_usable(const ppc_def_t *def)
{
const ppc_def_t *ret;
uint32_t pvr_rev;
int i, best, match, best_match, max;
#if defined(TARGET_PPCEMB)
/* When using the ppcemb target, we only support 440 style cores */
if (def->mmu_model != POWERPC_MMU_BOOKE) {
return false;
}
#endif
ret = NULL;
max = ARRAY_SIZE(ppc_defs);
best = -1;
pvr_rev = pvr & 0xFFFF;
/* We want all specified bits to match */
best_match = 32 - ctz32(pvr_rev);
for (i = 0; i < max; i++) {
/* We check that the 16 higher bits are the same to ensure the CPU
* model will be the choosen one.
*/
if (((pvr ^ ppc_defs[i].pvr) >> 16) == 0) {
/* We want as much as possible of the low-level 16 bits
* to be the same but we allow inexact matches.
*/
match = clz32(pvr_rev ^ (ppc_defs[i].pvr & 0xFFFF));
/* We check '>=' instead of '>' because the PPC_defs table
* is ordered by increasing revision.
* Then, we will match the higher revision compatible
* with the requested PVR
*/
if (match >= best_match) {
best = i;
best_match = match;
}
return true;
}
const ppc_def_t *ppc_find_by_pvr(uint32_t pvr)
{
int i;
for (i = 0; i < ARRAY_SIZE(ppc_defs); i++) {
if (!ppc_cpu_usable(&ppc_defs[i])) {
continue;
}
/* If we have an exact match, we're done */
if (pvr == ppc_defs[i].pvr) {
return &ppc_defs[i];
}
}
if (best != -1)
ret = &ppc_defs[best];
return ret;
return NULL;
}
#include <ctype.h>
@ -10087,6 +10085,10 @@ const ppc_def_t *cpu_ppc_find_by_name (const char *name)
const char *p;
int i, max, len;
if (kvm_enabled() && (strcasecmp(name, "host") == 0)) {
return kvmppc_host_cpu_def();
}
/* Check if the given name is a PVR */
len = strlen(name);
if (len == 10 && name[0] == '0' && name[1] == 'x') {
@ -10105,6 +10107,10 @@ const ppc_def_t *cpu_ppc_find_by_name (const char *name)
ret = NULL;
max = ARRAY_SIZE(ppc_defs);
for (i = 0; i < max; i++) {
if (!ppc_cpu_usable(&ppc_defs[i])) {
continue;
}
if (strcasecmp(name, ppc_defs[i].name) == 0) {
ret = &ppc_defs[i];
break;
@ -10120,6 +10126,10 @@ void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf)
max = ARRAY_SIZE(ppc_defs);
for (i = 0; i < max; i++) {
if (!ppc_cpu_usable(&ppc_defs[i])) {
continue;
}
(*cpu_fprintf)(f, "PowerPC %-16s PVR %08x\n",
ppc_defs[i].name, ppc_defs[i].pvr);
}