mirror of
https://github.com/qemu/qemu.git
synced 2024-12-15 15:33:29 +08:00
Merge branch 'master' of git://git.qemu.org/qemu
This commit is contained in:
commit
dd7b25b5b4
@ -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
|
||||
|
2
configure
vendored
2
configure
vendored
@ -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"
|
||||
;;
|
||||
|
6
hw/ppc.c
6
hw/ppc.c
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
135
hw/spapr.c
135
hw/spapr.c
@ -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);
|
||||
|
@ -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
508
hw/spapr_pci.c
Normal 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
61
hw/spapr_pci.h
Normal 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__ */
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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:
|
||||
|
BIN
pc-bios/slof.bin
BIN
pc-bios/slof.bin
Binary file not shown.
@ -1 +1 @@
|
||||
Subproject commit d1d6b53b713a2b7c2c25685268fa932d28a4b4c0
|
||||
Subproject commit 32e3430c018ceb8413cb808477449d1968c42497
|
@ -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)
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -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);
|
||||
|
||||
|
197
target-ppc/kvm.c
197
target-ppc/kvm.c
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user