mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-4.1-20190426' into staging
ppc patch queue 2019-04-26 Here's the first ppc target pull request for qemu-4.1. This has a number of things that have accumulated while qemu-4.0 was frozen. * A number of emulated MMU improvements from Ben Herrenschmidt * Assorted cleanups fro Greg Kurz * A large set of mostly mechanical cleanups from me to make target/ppc much closer to compliant with the modern coding style * Support for passthrough of NVIDIA GPUs using NVLink2 As well as some other assorted fixes. # gpg: Signature made Fri 26 Apr 2019 07:02:19 BST # gpg: using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full] # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full] # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full] # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown] # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dgibson/tags/ppc-for-4.1-20190426: (36 commits) target/ppc: improve performance of large BAT invalidations ppc/hash32: Rework R and C bit updates ppc/hash64: Rework R and C bit updates ppc/spapr: Use proper HPTE accessors for H_READ target/ppc: Don't check UPRT in radix mode when in HV real mode target/ppc/kvm: Convert DPRINTF to traces target/ppc/trace-events: Fix trivial typo spapr: Drop duplicate PCI swizzle code spapr_pci: Get rid of duplicate code for node name creation target/ppc: Style fixes for translate/spe-impl.inc.c target/ppc: Style fixes for translate/vmx-impl.inc.c target/ppc: Style fixes for translate/vsx-impl.inc.c target/ppc: Style fixes for translate/fp-impl.inc.c target/ppc: Style fixes for translate.c target/ppc: Style fixes for translate_init.inc.c target/ppc: Style fixes for monitor.c target/ppc: Style fixes for mmu_helper.c target/ppc: Style fixes for mmu-hash64.[ch] target/ppc: Style fixes for mmu-hash32.[ch] target/ppc: Style fixes for misc_helper.c ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+1
-1
@@ -1556,7 +1556,7 @@ void pci_device_set_intx_routing_notifier(PCIDevice *dev,
|
||||
*/
|
||||
int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin)
|
||||
{
|
||||
return (pin + PCI_SLOT(pci_dev->devfn)) % PCI_NUM_PINS;
|
||||
return pci_swizzle(PCI_SLOT(pci_dev->devfn), pin);
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
|
||||
@@ -9,7 +9,7 @@ obj-$(CONFIG_SPAPR_RNG) += spapr_rng.o
|
||||
# IBM PowerNV
|
||||
obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o
|
||||
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
|
||||
obj-y += spapr_pci_vfio.o
|
||||
obj-y += spapr_pci_vfio.o spapr_pci_nvlink2.o
|
||||
endif
|
||||
obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o
|
||||
# PowerPC 4xx boards
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#include "hw/ide.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/timer/mc146818rtc.h"
|
||||
#include "hw/input/i8042.h"
|
||||
#include "hw/isa/pc87312.h"
|
||||
#include "hw/net/ne2000-isa.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
|
||||
+78
-11
@@ -1034,12 +1034,13 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt)
|
||||
0, cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE),
|
||||
cpu_to_be32(max_cpus / smp_threads),
|
||||
};
|
||||
uint32_t maxdomain = cpu_to_be32(spapr->gpu_numa_id > 1 ? 1 : 0);
|
||||
uint32_t maxdomains[] = {
|
||||
cpu_to_be32(4),
|
||||
cpu_to_be32(0),
|
||||
cpu_to_be32(0),
|
||||
cpu_to_be32(0),
|
||||
cpu_to_be32(nb_numa_nodes ? nb_numa_nodes : 1),
|
||||
maxdomain,
|
||||
maxdomain,
|
||||
maxdomain,
|
||||
cpu_to_be32(spapr->gpu_numa_id),
|
||||
};
|
||||
|
||||
_FDT(rtas = fdt_add_subnode(fdt, 0, "rtas"));
|
||||
@@ -1519,10 +1520,10 @@ static void spapr_unmap_hptes(PPCVirtualHypervisor *vhyp,
|
||||
/* Nothing to do for qemu managed HPT */
|
||||
}
|
||||
|
||||
static void spapr_store_hpte(PPCVirtualHypervisor *vhyp, hwaddr ptex,
|
||||
uint64_t pte0, uint64_t pte1)
|
||||
void spapr_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
|
||||
uint64_t pte0, uint64_t pte1)
|
||||
{
|
||||
SpaprMachineState *spapr = SPAPR_MACHINE(vhyp);
|
||||
SpaprMachineState *spapr = SPAPR_MACHINE(cpu->vhyp);
|
||||
hwaddr offset = ptex * HASH_PTE_SIZE_64;
|
||||
|
||||
if (!spapr->htab) {
|
||||
@@ -1550,6 +1551,38 @@ static void spapr_store_hpte(PPCVirtualHypervisor *vhyp, hwaddr ptex,
|
||||
}
|
||||
}
|
||||
|
||||
static void spapr_hpte_set_c(PPCVirtualHypervisor *vhyp, hwaddr ptex,
|
||||
uint64_t pte1)
|
||||
{
|
||||
hwaddr offset = ptex * HASH_PTE_SIZE_64 + 15;
|
||||
SpaprMachineState *spapr = SPAPR_MACHINE(vhyp);
|
||||
|
||||
if (!spapr->htab) {
|
||||
/* There should always be a hash table when this is called */
|
||||
error_report("spapr_hpte_set_c called with no hash table !");
|
||||
return;
|
||||
}
|
||||
|
||||
/* The HW performs a non-atomic byte update */
|
||||
stb_p(spapr->htab + offset, (pte1 & 0xff) | 0x80);
|
||||
}
|
||||
|
||||
static void spapr_hpte_set_r(PPCVirtualHypervisor *vhyp, hwaddr ptex,
|
||||
uint64_t pte1)
|
||||
{
|
||||
hwaddr offset = ptex * HASH_PTE_SIZE_64 + 14;
|
||||
SpaprMachineState *spapr = SPAPR_MACHINE(vhyp);
|
||||
|
||||
if (!spapr->htab) {
|
||||
/* There should always be a hash table when this is called */
|
||||
error_report("spapr_hpte_set_r called with no hash table !");
|
||||
return;
|
||||
}
|
||||
|
||||
/* The HW performs a non-atomic byte update */
|
||||
stb_p(spapr->htab + offset, ((pte1 >> 8) & 0xff) | 0x01);
|
||||
}
|
||||
|
||||
int spapr_hpt_shift_for_ramsize(uint64_t ramsize)
|
||||
{
|
||||
int shift;
|
||||
@@ -1698,6 +1731,16 @@ static void spapr_machine_reset(void)
|
||||
spapr_irq_msi_reset(spapr);
|
||||
}
|
||||
|
||||
/*
|
||||
* NVLink2-connected GPU RAM needs to be placed on a separate NUMA node.
|
||||
* We assign a new numa ID per GPU in spapr_pci_collect_nvgpu() which is
|
||||
* called from vPHB reset handler so we initialize the counter here.
|
||||
* If no NUMA is configured from the QEMU side, we start from 1 as GPU RAM
|
||||
* must be equally distant from any other node.
|
||||
* The final value of spapr->gpu_numa_id is going to be written to
|
||||
* max-associativity-domains in spapr_build_fdt().
|
||||
*/
|
||||
spapr->gpu_numa_id = MAX(1, nb_numa_nodes);
|
||||
qemu_devices_reset();
|
||||
|
||||
/*
|
||||
@@ -3907,7 +3950,9 @@ static void spapr_phb_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
smc->phb_placement(spapr, sphb->index,
|
||||
&sphb->buid, &sphb->io_win_addr,
|
||||
&sphb->mem_win_addr, &sphb->mem64_win_addr,
|
||||
windows_supported, sphb->dma_liobn, errp);
|
||||
windows_supported, sphb->dma_liobn,
|
||||
&sphb->nv2_gpa_win_addr, &sphb->nv2_atsd_win_addr,
|
||||
errp);
|
||||
}
|
||||
|
||||
static void spapr_phb_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
@@ -4108,7 +4153,8 @@ static const CPUArchIdList *spapr_possible_cpu_arch_ids(MachineState *machine)
|
||||
static void spapr_phb_placement(SpaprMachineState *spapr, uint32_t index,
|
||||
uint64_t *buid, hwaddr *pio,
|
||||
hwaddr *mmio32, hwaddr *mmio64,
|
||||
unsigned n_dma, uint32_t *liobns, Error **errp)
|
||||
unsigned n_dma, uint32_t *liobns,
|
||||
hwaddr *nv2gpa, hwaddr *nv2atsd, Error **errp)
|
||||
{
|
||||
/*
|
||||
* New-style PHB window placement.
|
||||
@@ -4153,6 +4199,9 @@ static void spapr_phb_placement(SpaprMachineState *spapr, uint32_t index,
|
||||
*pio = SPAPR_PCI_BASE + index * SPAPR_PCI_IO_WIN_SIZE;
|
||||
*mmio32 = SPAPR_PCI_BASE + (index + 1) * SPAPR_PCI_MEM32_WIN_SIZE;
|
||||
*mmio64 = SPAPR_PCI_BASE + (index + 1) * SPAPR_PCI_MEM64_WIN_SIZE;
|
||||
|
||||
*nv2gpa = SPAPR_PCI_NV2RAM64_WIN_BASE + index * SPAPR_PCI_NV2RAM64_WIN_SIZE;
|
||||
*nv2atsd = SPAPR_PCI_NV2ATSD_WIN_BASE + index * SPAPR_PCI_NV2ATSD_WIN_SIZE;
|
||||
}
|
||||
|
||||
static ICSState *spapr_ics_get(XICSFabric *dev, int irq)
|
||||
@@ -4274,7 +4323,8 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
|
||||
vhc->hpt_mask = spapr_hpt_mask;
|
||||
vhc->map_hptes = spapr_map_hptes;
|
||||
vhc->unmap_hptes = spapr_unmap_hptes;
|
||||
vhc->store_hpte = spapr_store_hpte;
|
||||
vhc->hpte_set_c = spapr_hpte_set_c;
|
||||
vhc->hpte_set_r = spapr_hpte_set_r;
|
||||
vhc->get_pate = spapr_get_pate;
|
||||
vhc->encode_hpt_for_kvm_pr = spapr_encode_hpt_for_kvm_pr;
|
||||
xic->ics_get = spapr_ics_get;
|
||||
@@ -4368,6 +4418,18 @@ DEFINE_SPAPR_MACHINE(4_0, "4.0", false);
|
||||
/*
|
||||
* pseries-3.1
|
||||
*/
|
||||
static void phb_placement_3_1(SpaprMachineState *spapr, uint32_t index,
|
||||
uint64_t *buid, hwaddr *pio,
|
||||
hwaddr *mmio32, hwaddr *mmio64,
|
||||
unsigned n_dma, uint32_t *liobns,
|
||||
hwaddr *nv2gpa, hwaddr *nv2atsd, Error **errp)
|
||||
{
|
||||
spapr_phb_placement(spapr, index, buid, pio, mmio32, mmio64, n_dma, liobns,
|
||||
nv2gpa, nv2atsd, errp);
|
||||
*nv2gpa = 0;
|
||||
*nv2atsd = 0;
|
||||
}
|
||||
|
||||
static void spapr_machine_3_1_class_options(MachineClass *mc)
|
||||
{
|
||||
SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
|
||||
@@ -4383,6 +4445,7 @@ static void spapr_machine_3_1_class_options(MachineClass *mc)
|
||||
smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN;
|
||||
smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN;
|
||||
smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_OFF;
|
||||
smc->phb_placement = phb_placement_3_1;
|
||||
}
|
||||
|
||||
DEFINE_SPAPR_MACHINE(3_1, "3.1", false);
|
||||
@@ -4514,7 +4577,8 @@ DEFINE_SPAPR_MACHINE(2_8, "2.8", false);
|
||||
static void phb_placement_2_7(SpaprMachineState *spapr, uint32_t index,
|
||||
uint64_t *buid, hwaddr *pio,
|
||||
hwaddr *mmio32, hwaddr *mmio64,
|
||||
unsigned n_dma, uint32_t *liobns, Error **errp)
|
||||
unsigned n_dma, uint32_t *liobns,
|
||||
hwaddr *nv2gpa, hwaddr *nv2atsd, Error **errp)
|
||||
{
|
||||
/* Legacy PHB placement for pseries-2.7 and earlier machine types */
|
||||
const uint64_t base_buid = 0x800000020000000ULL;
|
||||
@@ -4558,6 +4622,9 @@ static void phb_placement_2_7(SpaprMachineState *spapr, uint32_t index,
|
||||
* fallback behaviour of automatically splitting a large "32-bit"
|
||||
* window into contiguous 32-bit and 64-bit windows
|
||||
*/
|
||||
|
||||
*nv2gpa = 0;
|
||||
*nv2atsd = 0;
|
||||
}
|
||||
|
||||
static void spapr_machine_2_7_class_options(MachineClass *mc)
|
||||
|
||||
+12
-12
@@ -118,7 +118,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1);
|
||||
}
|
||||
|
||||
ppc_hash64_store_hpte(cpu, ptex + slot, pteh | HPTE64_V_HPTE_DIRTY, ptel);
|
||||
spapr_store_hpte(cpu, ptex + slot, pteh | HPTE64_V_HPTE_DIRTY, ptel);
|
||||
|
||||
args[0] = ptex + slot;
|
||||
return H_SUCCESS;
|
||||
@@ -131,7 +131,8 @@ typedef enum {
|
||||
REMOVE_HW = 3,
|
||||
} RemoveResult;
|
||||
|
||||
static RemoveResult remove_hpte(PowerPCCPU *cpu, target_ulong ptex,
|
||||
static RemoveResult remove_hpte(PowerPCCPU *cpu
|
||||
, target_ulong ptex,
|
||||
target_ulong avpn,
|
||||
target_ulong flags,
|
||||
target_ulong *vp, target_ulong *rp)
|
||||
@@ -155,7 +156,7 @@ static RemoveResult remove_hpte(PowerPCCPU *cpu, target_ulong ptex,
|
||||
}
|
||||
*vp = v;
|
||||
*rp = r;
|
||||
ppc_hash64_store_hpte(cpu, ptex, HPTE64_V_HPTE_DIRTY, 0);
|
||||
spapr_store_hpte(cpu, ptex, HPTE64_V_HPTE_DIRTY, 0);
|
||||
ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r);
|
||||
return REMOVE_SUCCESS;
|
||||
}
|
||||
@@ -289,13 +290,13 @@ static target_ulong h_protect(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
r |= (flags << 55) & HPTE64_R_PP0;
|
||||
r |= (flags << 48) & HPTE64_R_KEY_HI;
|
||||
r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO);
|
||||
ppc_hash64_store_hpte(cpu, ptex,
|
||||
(v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0);
|
||||
spapr_store_hpte(cpu, ptex,
|
||||
(v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0);
|
||||
ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r);
|
||||
/* Flush the tlb */
|
||||
check_tlb_flush(env, true);
|
||||
/* Don't need a memory barrier, due to qemu's global lock */
|
||||
ppc_hash64_store_hpte(cpu, ptex, v | HPTE64_V_HPTE_DIRTY, r);
|
||||
spapr_store_hpte(cpu, ptex, v | HPTE64_V_HPTE_DIRTY, r);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -304,8 +305,8 @@ static target_ulong h_read(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
{
|
||||
target_ulong flags = args[0];
|
||||
target_ulong ptex = args[1];
|
||||
uint8_t *hpte;
|
||||
int i, ridx, n_entries = 1;
|
||||
const ppc_hash_pte64_t *hptes;
|
||||
|
||||
if (!valid_ptex(cpu, ptex)) {
|
||||
return H_PARAMETER;
|
||||
@@ -317,13 +318,12 @@ static target_ulong h_read(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
n_entries = 4;
|
||||
}
|
||||
|
||||
hpte = spapr->htab + (ptex * HASH_PTE_SIZE_64);
|
||||
|
||||
hptes = ppc_hash64_map_hptes(cpu, ptex, n_entries);
|
||||
for (i = 0, ridx = 0; i < n_entries; i++) {
|
||||
args[ridx++] = ldq_p(hpte);
|
||||
args[ridx++] = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
|
||||
hpte += HASH_PTE_SIZE_64;
|
||||
args[ridx++] = ppc_hash64_hpte0(cpu, hptes, i);
|
||||
args[ridx++] = ppc_hash64_hpte1(cpu, hptes, i);
|
||||
}
|
||||
ppc_hash64_unmap_hptes(cpu, hptes, ptex, n_entries);
|
||||
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
+14
-30
@@ -67,36 +67,11 @@ void spapr_irq_msi_reset(SpaprMachineState *spapr)
|
||||
* XICS IRQ backend.
|
||||
*/
|
||||
|
||||
static ICSState *spapr_ics_create(SpaprMachineState *spapr,
|
||||
int nr_irqs, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
Object *obj;
|
||||
|
||||
obj = object_new(TYPE_ICS_SIMPLE);
|
||||
object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort);
|
||||
object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr),
|
||||
&error_abort);
|
||||
object_property_set_int(obj, nr_irqs, "nr-irqs", &local_err);
|
||||
if (local_err) {
|
||||
goto error;
|
||||
}
|
||||
object_property_set_bool(obj, true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return ICS_BASE(obj);
|
||||
|
||||
error:
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void spapr_irq_init_xics(SpaprMachineState *spapr, int nr_irqs,
|
||||
Error **errp)
|
||||
{
|
||||
MachineState *machine = MACHINE(spapr);
|
||||
Object *obj;
|
||||
Error *local_err = NULL;
|
||||
bool xics_kvm = false;
|
||||
|
||||
@@ -108,7 +83,8 @@ static void spapr_irq_init_xics(SpaprMachineState *spapr, int nr_irqs,
|
||||
if (machine_kernel_irqchip_required(machine) && !xics_kvm) {
|
||||
error_prepend(&local_err,
|
||||
"kernel_irqchip requested but unavailable: ");
|
||||
goto error;
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
error_free(local_err);
|
||||
local_err = NULL;
|
||||
@@ -118,10 +94,18 @@ static void spapr_irq_init_xics(SpaprMachineState *spapr, int nr_irqs,
|
||||
xics_spapr_init(spapr);
|
||||
}
|
||||
|
||||
spapr->ics = spapr_ics_create(spapr, nr_irqs, &local_err);
|
||||
obj = object_new(TYPE_ICS_SIMPLE);
|
||||
object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort);
|
||||
object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr),
|
||||
&error_fatal);
|
||||
object_property_set_int(obj, nr_irqs, "nr-irqs", &error_fatal);
|
||||
object_property_set_bool(obj, true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
error:
|
||||
error_propagate(errp, local_err);
|
||||
spapr->ics = ICS_BASE(obj);
|
||||
}
|
||||
|
||||
#define ICS_IRQ_FREE(ics, srcno) \
|
||||
|
||||
+24
-24
@@ -719,26 +719,10 @@ param_error_exit:
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
}
|
||||
|
||||
static int pci_spapr_swizzle(int slot, int pin)
|
||||
{
|
||||
return (slot + pin) % PCI_NUM_PINS;
|
||||
}
|
||||
|
||||
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 (4). We
|
||||
* use standard PCI swizzling, that is (slot number + pin number)
|
||||
* % 4.
|
||||
*/
|
||||
return pci_spapr_swizzle(PCI_SLOT(pci_dev->devfn), irq_num);
|
||||
}
|
||||
|
||||
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
|
||||
* Here we use the number returned by pci_swizzle_map_irq_fn to find a
|
||||
* corresponding qemu_irq.
|
||||
*/
|
||||
SpaprPhbState *phb = opaque;
|
||||
@@ -1355,6 +1339,8 @@ static void spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset,
|
||||
if (sphb->pcie_ecs && pci_is_express(dev)) {
|
||||
_FDT(fdt_setprop_cell(fdt, offset, "ibm,pci-config-space-type", 0x1));
|
||||
}
|
||||
|
||||
spapr_phb_nvgpu_populate_pcidev_dt(dev, fdt, offset, sphb);
|
||||
}
|
||||
|
||||
/* create OF node for pci device and required OF DT properties */
|
||||
@@ -1587,6 +1573,8 @@ static void spapr_phb_unrealize(DeviceState *dev, Error **errp)
|
||||
int i;
|
||||
const unsigned windows_supported = spapr_phb_windows_supported(sphb);
|
||||
|
||||
spapr_phb_nvgpu_free(sphb);
|
||||
|
||||
if (sphb->msi) {
|
||||
g_hash_table_unref(sphb->msi);
|
||||
sphb->msi = NULL;
|
||||
@@ -1762,7 +1750,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
|
||||
&sphb->iowindow);
|
||||
|
||||
bus = pci_register_root_bus(dev, NULL,
|
||||
pci_spapr_set_irq, pci_spapr_map_irq, sphb,
|
||||
pci_spapr_set_irq, pci_swizzle_map_irq_fn, sphb,
|
||||
&sphb->memspace, &sphb->iospace,
|
||||
PCI_DEVFN(0, 0), PCI_NUM_PINS,
|
||||
TYPE_SPAPR_PHB_ROOT_BUS);
|
||||
@@ -1898,8 +1886,14 @@ void spapr_phb_dma_reset(SpaprPhbState *sphb)
|
||||
static void spapr_phb_reset(DeviceState *qdev)
|
||||
{
|
||||
SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(qdev);
|
||||
Error *errp = NULL;
|
||||
|
||||
spapr_phb_dma_reset(sphb);
|
||||
spapr_phb_nvgpu_free(sphb);
|
||||
spapr_phb_nvgpu_setup(sphb, &errp);
|
||||
if (errp) {
|
||||
error_report_err(errp);
|
||||
}
|
||||
|
||||
/* Reset the IOMMU state */
|
||||
object_child_foreach(OBJECT(qdev), spapr_phb_children_reset, NULL);
|
||||
@@ -1932,6 +1926,8 @@ static Property spapr_phb_properties[] = {
|
||||
pre_2_8_migration, false),
|
||||
DEFINE_PROP_BOOL("pcie-extended-configuration-space", SpaprPhbState,
|
||||
pcie_ecs, true),
|
||||
DEFINE_PROP_UINT64("gpa", SpaprPhbState, nv2_gpa_win_addr, 0),
|
||||
DEFINE_PROP_UINT64("atsd", SpaprPhbState, nv2_atsd_win_addr, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@@ -2164,7 +2160,6 @@ int spapr_populate_pci_dt(SpaprPhbState *phb, uint32_t intc_phandle, void *fdt,
|
||||
uint32_t nr_msis, int *node_offset)
|
||||
{
|
||||
int bus_off, i, j, ret;
|
||||
gchar *nodename;
|
||||
uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) };
|
||||
struct {
|
||||
uint32_t hi;
|
||||
@@ -2212,11 +2207,10 @@ int spapr_populate_pci_dt(SpaprPhbState *phb, uint32_t intc_phandle, void *fdt,
|
||||
PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus;
|
||||
SpaprFdt s_fdt;
|
||||
SpaprDrc *drc;
|
||||
Error *errp = NULL;
|
||||
|
||||
/* Start populating the FDT */
|
||||
nodename = g_strdup_printf("pci@%" PRIx64, phb->buid);
|
||||
_FDT(bus_off = fdt_add_subnode(fdt, 0, nodename));
|
||||
g_free(nodename);
|
||||
_FDT(bus_off = fdt_add_subnode(fdt, 0, phb->dtbusname));
|
||||
if (node_offset) {
|
||||
*node_offset = bus_off;
|
||||
}
|
||||
@@ -2249,14 +2243,14 @@ int spapr_populate_pci_dt(SpaprPhbState *phb, uint32_t intc_phandle, void *fdt,
|
||||
}
|
||||
|
||||
/* Build the interrupt-map, this must matches what is done
|
||||
* in pci_spapr_map_irq
|
||||
* in pci_swizzle_map_irq_fn
|
||||
*/
|
||||
_FDT(fdt_setprop(fdt, bus_off, "interrupt-map-mask",
|
||||
&interrupt_map_mask, sizeof(interrupt_map_mask)));
|
||||
for (i = 0; i < PCI_SLOT_MAX; i++) {
|
||||
for (j = 0; j < PCI_NUM_PINS; j++) {
|
||||
uint32_t *irqmap = interrupt_map[i*PCI_NUM_PINS + j];
|
||||
int lsi_num = pci_spapr_swizzle(i, j);
|
||||
int lsi_num = pci_swizzle(i, j);
|
||||
|
||||
irqmap[0] = cpu_to_be32(b_ddddd(i)|b_fff(0));
|
||||
irqmap[1] = 0;
|
||||
@@ -2304,6 +2298,12 @@ int spapr_populate_pci_dt(SpaprPhbState *phb, uint32_t intc_phandle, void *fdt,
|
||||
return ret;
|
||||
}
|
||||
|
||||
spapr_phb_nvgpu_populate_dt(phb, fdt, bus_off, &errp);
|
||||
if (errp) {
|
||||
error_report_err(errp);
|
||||
}
|
||||
spapr_phb_nvgpu_ram_populate_dt(phb, fdt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* QEMU sPAPR PCI for NVLink2 pass through
|
||||
*
|
||||
* Copyright (c) 2019 Alexey Kardashevskiy, 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 "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci-host/spapr.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/ppc/fdt.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
|
||||
#define PHANDLE_PCIDEV(phb, pdev) (0x12000000 | \
|
||||
(((phb)->index) << 16) | ((pdev)->devfn))
|
||||
#define PHANDLE_GPURAM(phb, n) (0x110000FF | ((n) << 8) | \
|
||||
(((phb)->index) << 16))
|
||||
#define PHANDLE_NVLINK(phb, gn, nn) (0x00130000 | (((phb)->index) << 8) | \
|
||||
((gn) << 4) | (nn))
|
||||
|
||||
#define SPAPR_GPU_NUMA_ID (cpu_to_be32(1))
|
||||
|
||||
struct spapr_phb_pci_nvgpu_config {
|
||||
uint64_t nv2_ram_current;
|
||||
uint64_t nv2_atsd_current;
|
||||
int num; /* number of non empty (i.e. tgt!=0) entries in slots[] */
|
||||
struct spapr_phb_pci_nvgpu_slot {
|
||||
uint64_t tgt;
|
||||
uint64_t gpa;
|
||||
unsigned numa_id;
|
||||
PCIDevice *gpdev;
|
||||
int linknum;
|
||||
struct {
|
||||
uint64_t atsd_gpa;
|
||||
PCIDevice *npdev;
|
||||
uint32_t link_speed;
|
||||
} links[NVGPU_MAX_LINKS];
|
||||
} slots[NVGPU_MAX_NUM];
|
||||
Error *errp;
|
||||
};
|
||||
|
||||
static struct spapr_phb_pci_nvgpu_slot *
|
||||
spapr_nvgpu_get_slot(struct spapr_phb_pci_nvgpu_config *nvgpus, uint64_t tgt)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Search for partially collected "slot" */
|
||||
for (i = 0; i < nvgpus->num; ++i) {
|
||||
if (nvgpus->slots[i].tgt == tgt) {
|
||||
return &nvgpus->slots[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (nvgpus->num == ARRAY_SIZE(nvgpus->slots)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i = nvgpus->num;
|
||||
nvgpus->slots[i].tgt = tgt;
|
||||
++nvgpus->num;
|
||||
|
||||
return &nvgpus->slots[i];
|
||||
}
|
||||
|
||||
static void spapr_pci_collect_nvgpu(struct spapr_phb_pci_nvgpu_config *nvgpus,
|
||||
PCIDevice *pdev, uint64_t tgt,
|
||||
MemoryRegion *mr, Error **errp)
|
||||
{
|
||||
MachineState *machine = MACHINE(qdev_get_machine());
|
||||
SpaprMachineState *spapr = SPAPR_MACHINE(machine);
|
||||
struct spapr_phb_pci_nvgpu_slot *nvslot = spapr_nvgpu_get_slot(nvgpus, tgt);
|
||||
|
||||
if (!nvslot) {
|
||||
error_setg(errp, "Found too many GPUs per vPHB");
|
||||
return;
|
||||
}
|
||||
g_assert(!nvslot->gpdev);
|
||||
nvslot->gpdev = pdev;
|
||||
|
||||
nvslot->gpa = nvgpus->nv2_ram_current;
|
||||
nvgpus->nv2_ram_current += memory_region_size(mr);
|
||||
nvslot->numa_id = spapr->gpu_numa_id;
|
||||
++spapr->gpu_numa_id;
|
||||
}
|
||||
|
||||
static void spapr_pci_collect_nvnpu(struct spapr_phb_pci_nvgpu_config *nvgpus,
|
||||
PCIDevice *pdev, uint64_t tgt,
|
||||
MemoryRegion *mr, Error **errp)
|
||||
{
|
||||
struct spapr_phb_pci_nvgpu_slot *nvslot = spapr_nvgpu_get_slot(nvgpus, tgt);
|
||||
int j;
|
||||
|
||||
if (!nvslot) {
|
||||
error_setg(errp, "Found too many NVLink bridges per vPHB");
|
||||
return;
|
||||
}
|
||||
|
||||
j = nvslot->linknum;
|
||||
if (j == ARRAY_SIZE(nvslot->links)) {
|
||||
error_setg(errp, "Found too many NVLink bridges per GPU");
|
||||
return;
|
||||
}
|
||||
++nvslot->linknum;
|
||||
|
||||
g_assert(!nvslot->links[j].npdev);
|
||||
nvslot->links[j].npdev = pdev;
|
||||
nvslot->links[j].atsd_gpa = nvgpus->nv2_atsd_current;
|
||||
nvgpus->nv2_atsd_current += memory_region_size(mr);
|
||||
nvslot->links[j].link_speed =
|
||||
object_property_get_uint(OBJECT(pdev), "nvlink2-link-speed", NULL);
|
||||
}
|
||||
|
||||
static void spapr_phb_pci_collect_nvgpu(PCIBus *bus, PCIDevice *pdev,
|
||||
void *opaque)
|
||||
{
|
||||
PCIBus *sec_bus;
|
||||
Object *po = OBJECT(pdev);
|
||||
uint64_t tgt = object_property_get_uint(po, "nvlink2-tgt", NULL);
|
||||
|
||||
if (tgt) {
|
||||
Error *local_err = NULL;
|
||||
struct spapr_phb_pci_nvgpu_config *nvgpus = opaque;
|
||||
Object *mr_gpu = object_property_get_link(po, "nvlink2-mr[0]", NULL);
|
||||
Object *mr_npu = object_property_get_link(po, "nvlink2-atsd-mr[0]",
|
||||
NULL);
|
||||
|
||||
g_assert(mr_gpu || mr_npu);
|
||||
if (mr_gpu) {
|
||||
spapr_pci_collect_nvgpu(nvgpus, pdev, tgt, MEMORY_REGION(mr_gpu),
|
||||
&local_err);
|
||||
} else {
|
||||
spapr_pci_collect_nvnpu(nvgpus, pdev, tgt, MEMORY_REGION(mr_npu),
|
||||
&local_err);
|
||||
}
|
||||
error_propagate(&nvgpus->errp, local_err);
|
||||
}
|
||||
if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) !=
|
||||
PCI_HEADER_TYPE_BRIDGE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev));
|
||||
if (!sec_bus) {
|
||||
return;
|
||||
}
|
||||
|
||||
pci_for_each_device(sec_bus, pci_bus_num(sec_bus),
|
||||
spapr_phb_pci_collect_nvgpu, opaque);
|
||||
}
|
||||
|
||||
void spapr_phb_nvgpu_setup(SpaprPhbState *sphb, Error **errp)
|
||||
{
|
||||
int i, j, valid_gpu_num;
|
||||
PCIBus *bus;
|
||||
|
||||
/* Search for GPUs and NPUs */
|
||||
if (!sphb->nv2_gpa_win_addr || !sphb->nv2_atsd_win_addr) {
|
||||
return;
|
||||
}
|
||||
|
||||
sphb->nvgpus = g_new0(struct spapr_phb_pci_nvgpu_config, 1);
|
||||
sphb->nvgpus->nv2_ram_current = sphb->nv2_gpa_win_addr;
|
||||
sphb->nvgpus->nv2_atsd_current = sphb->nv2_atsd_win_addr;
|
||||
|
||||
bus = PCI_HOST_BRIDGE(sphb)->bus;
|
||||
pci_for_each_device(bus, pci_bus_num(bus),
|
||||
spapr_phb_pci_collect_nvgpu, sphb->nvgpus);
|
||||
|
||||
if (sphb->nvgpus->errp) {
|
||||
error_propagate(errp, sphb->nvgpus->errp);
|
||||
sphb->nvgpus->errp = NULL;
|
||||
goto cleanup_exit;
|
||||
}
|
||||
|
||||
/* Add found GPU RAM and ATSD MRs if found */
|
||||
for (i = 0, valid_gpu_num = 0; i < sphb->nvgpus->num; ++i) {
|
||||
Object *nvmrobj;
|
||||
struct spapr_phb_pci_nvgpu_slot *nvslot = &sphb->nvgpus->slots[i];
|
||||
|
||||
if (!nvslot->gpdev) {
|
||||
continue;
|
||||
}
|
||||
nvmrobj = object_property_get_link(OBJECT(nvslot->gpdev),
|
||||
"nvlink2-mr[0]", NULL);
|
||||
/* ATSD is pointless without GPU RAM MR so skip those */
|
||||
if (!nvmrobj) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++valid_gpu_num;
|
||||
memory_region_add_subregion(get_system_memory(), nvslot->gpa,
|
||||
MEMORY_REGION(nvmrobj));
|
||||
|
||||
for (j = 0; j < nvslot->linknum; ++j) {
|
||||
Object *atsdmrobj;
|
||||
|
||||
atsdmrobj = object_property_get_link(OBJECT(nvslot->links[j].npdev),
|
||||
"nvlink2-atsd-mr[0]", NULL);
|
||||
if (!atsdmrobj) {
|
||||
continue;
|
||||
}
|
||||
memory_region_add_subregion(get_system_memory(),
|
||||
nvslot->links[j].atsd_gpa,
|
||||
MEMORY_REGION(atsdmrobj));
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_gpu_num) {
|
||||
return;
|
||||
}
|
||||
/* We did not find any interesting GPU */
|
||||
cleanup_exit:
|
||||
g_free(sphb->nvgpus);
|
||||
sphb->nvgpus = NULL;
|
||||
}
|
||||
|
||||
void spapr_phb_nvgpu_free(SpaprPhbState *sphb)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if (!sphb->nvgpus) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < sphb->nvgpus->num; ++i) {
|
||||
struct spapr_phb_pci_nvgpu_slot *nvslot = &sphb->nvgpus->slots[i];
|
||||
Object *nv_mrobj = object_property_get_link(OBJECT(nvslot->gpdev),
|
||||
"nvlink2-mr[0]", NULL);
|
||||
|
||||
if (nv_mrobj) {
|
||||
memory_region_del_subregion(get_system_memory(),
|
||||
MEMORY_REGION(nv_mrobj));
|
||||
}
|
||||
for (j = 0; j < nvslot->linknum; ++j) {
|
||||
PCIDevice *npdev = nvslot->links[j].npdev;
|
||||
Object *atsd_mrobj;
|
||||
atsd_mrobj = object_property_get_link(OBJECT(npdev),
|
||||
"nvlink2-atsd-mr[0]", NULL);
|
||||
if (atsd_mrobj) {
|
||||
memory_region_del_subregion(get_system_memory(),
|
||||
MEMORY_REGION(atsd_mrobj));
|
||||
}
|
||||
}
|
||||
}
|
||||
g_free(sphb->nvgpus);
|
||||
sphb->nvgpus = NULL;
|
||||
}
|
||||
|
||||
void spapr_phb_nvgpu_populate_dt(SpaprPhbState *sphb, void *fdt, int bus_off,
|
||||
Error **errp)
|
||||
{
|
||||
int i, j, atsdnum = 0;
|
||||
uint64_t atsd[8]; /* The existing limitation of known guests */
|
||||
|
||||
if (!sphb->nvgpus) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; (i < sphb->nvgpus->num) && (atsdnum < ARRAY_SIZE(atsd)); ++i) {
|
||||
struct spapr_phb_pci_nvgpu_slot *nvslot = &sphb->nvgpus->slots[i];
|
||||
|
||||
if (!nvslot->gpdev) {
|
||||
continue;
|
||||
}
|
||||
for (j = 0; j < nvslot->linknum; ++j) {
|
||||
if (!nvslot->links[j].atsd_gpa) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (atsdnum == ARRAY_SIZE(atsd)) {
|
||||
error_report("Only %"PRIuPTR" ATSD registers supported",
|
||||
ARRAY_SIZE(atsd));
|
||||
break;
|
||||
}
|
||||
atsd[atsdnum] = cpu_to_be64(nvslot->links[j].atsd_gpa);
|
||||
++atsdnum;
|
||||
}
|
||||
}
|
||||
|
||||
if (!atsdnum) {
|
||||
error_setg(errp, "No ATSD registers found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!spapr_phb_eeh_available(sphb)) {
|
||||
/*
|
||||
* ibm,mmio-atsd contains ATSD registers; these belong to an NPU PHB
|
||||
* which we do not emulate as a separate device. Instead we put
|
||||
* ibm,mmio-atsd to the vPHB with GPU and make sure that we do not
|
||||
* put GPUs from different IOMMU groups to the same vPHB to ensure
|
||||
* that the guest will use ATSDs from the corresponding NPU.
|
||||
*/
|
||||
error_setg(errp, "ATSD requires separate vPHB per GPU IOMMU group");
|
||||
return;
|
||||
}
|
||||
|
||||
_FDT((fdt_setprop(fdt, bus_off, "ibm,mmio-atsd", atsd,
|
||||
atsdnum * sizeof(atsd[0]))));
|
||||
}
|
||||
|
||||
void spapr_phb_nvgpu_ram_populate_dt(SpaprPhbState *sphb, void *fdt)
|
||||
{
|
||||
int i, j, linkidx, npuoff;
|
||||
char *npuname;
|
||||
|
||||
if (!sphb->nvgpus) {
|
||||
return;
|
||||
}
|
||||
|
||||
npuname = g_strdup_printf("npuphb%d", sphb->index);
|
||||
npuoff = fdt_add_subnode(fdt, 0, npuname);
|
||||
_FDT(npuoff);
|
||||
_FDT(fdt_setprop_cell(fdt, npuoff, "#address-cells", 1));
|
||||
_FDT(fdt_setprop_cell(fdt, npuoff, "#size-cells", 0));
|
||||
/* Advertise NPU as POWER9 so the guest can enable NPU2 contexts */
|
||||
_FDT((fdt_setprop_string(fdt, npuoff, "compatible", "ibm,power9-npu")));
|
||||
g_free(npuname);
|
||||
|
||||
for (i = 0, linkidx = 0; i < sphb->nvgpus->num; ++i) {
|
||||
for (j = 0; j < sphb->nvgpus->slots[i].linknum; ++j) {
|
||||
char *linkname = g_strdup_printf("link@%d", linkidx);
|
||||
int off = fdt_add_subnode(fdt, npuoff, linkname);
|
||||
|
||||
_FDT(off);
|
||||
/* _FDT((fdt_setprop_cell(fdt, off, "reg", linkidx))); */
|
||||
_FDT((fdt_setprop_string(fdt, off, "compatible",
|
||||
"ibm,npu-link")));
|
||||
_FDT((fdt_setprop_cell(fdt, off, "phandle",
|
||||
PHANDLE_NVLINK(sphb, i, j))));
|
||||
_FDT((fdt_setprop_cell(fdt, off, "ibm,npu-link-index", linkidx)));
|
||||
g_free(linkname);
|
||||
++linkidx;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add memory nodes for GPU RAM and mark them unusable */
|
||||
for (i = 0; i < sphb->nvgpus->num; ++i) {
|
||||
struct spapr_phb_pci_nvgpu_slot *nvslot = &sphb->nvgpus->slots[i];
|
||||
Object *nv_mrobj = object_property_get_link(OBJECT(nvslot->gpdev),
|
||||
"nvlink2-mr[0]", NULL);
|
||||
uint32_t associativity[] = {
|
||||
cpu_to_be32(0x4),
|
||||
SPAPR_GPU_NUMA_ID,
|
||||
SPAPR_GPU_NUMA_ID,
|
||||
SPAPR_GPU_NUMA_ID,
|
||||
cpu_to_be32(nvslot->numa_id)
|
||||
};
|
||||
uint64_t size = object_property_get_uint(nv_mrobj, "size", NULL);
|
||||
uint64_t mem_reg[2] = { cpu_to_be64(nvslot->gpa), cpu_to_be64(size) };
|
||||
char *mem_name = g_strdup_printf("memory@%"PRIx64, nvslot->gpa);
|
||||
int off = fdt_add_subnode(fdt, 0, mem_name);
|
||||
|
||||
_FDT(off);
|
||||
_FDT((fdt_setprop_string(fdt, off, "device_type", "memory")));
|
||||
_FDT((fdt_setprop(fdt, off, "reg", mem_reg, sizeof(mem_reg))));
|
||||
_FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity,
|
||||
sizeof(associativity))));
|
||||
|
||||
_FDT((fdt_setprop_string(fdt, off, "compatible",
|
||||
"ibm,coherent-device-memory")));
|
||||
|
||||
mem_reg[1] = cpu_to_be64(0);
|
||||
_FDT((fdt_setprop(fdt, off, "linux,usable-memory", mem_reg,
|
||||
sizeof(mem_reg))));
|
||||
_FDT((fdt_setprop_cell(fdt, off, "phandle",
|
||||
PHANDLE_GPURAM(sphb, i))));
|
||||
g_free(mem_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void spapr_phb_nvgpu_populate_pcidev_dt(PCIDevice *dev, void *fdt, int offset,
|
||||
SpaprPhbState *sphb)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if (!sphb->nvgpus) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < sphb->nvgpus->num; ++i) {
|
||||
struct spapr_phb_pci_nvgpu_slot *nvslot = &sphb->nvgpus->slots[i];
|
||||
|
||||
/* Skip "slot" without attached GPU */
|
||||
if (!nvslot->gpdev) {
|
||||
continue;
|
||||
}
|
||||
if (dev == nvslot->gpdev) {
|
||||
uint32_t npus[nvslot->linknum];
|
||||
|
||||
for (j = 0; j < nvslot->linknum; ++j) {
|
||||
PCIDevice *npdev = nvslot->links[j].npdev;
|
||||
|
||||
npus[j] = cpu_to_be32(PHANDLE_PCIDEV(sphb, npdev));
|
||||
}
|
||||
_FDT(fdt_setprop(fdt, offset, "ibm,npu", npus,
|
||||
j * sizeof(npus[0])));
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "phandle",
|
||||
PHANDLE_PCIDEV(sphb, dev))));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < nvslot->linknum; ++j) {
|
||||
if (dev != nvslot->links[j].npdev) {
|
||||
continue;
|
||||
}
|
||||
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "phandle",
|
||||
PHANDLE_PCIDEV(sphb, dev))));
|
||||
_FDT(fdt_setprop_cell(fdt, offset, "ibm,gpu",
|
||||
PHANDLE_PCIDEV(sphb, nvslot->gpdev)));
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "ibm,nvlink",
|
||||
PHANDLE_NVLINK(sphb, i, j))));
|
||||
/*
|
||||
* If we ever want to emulate GPU RAM at the same location as on
|
||||
* the host - here is the encoding GPA->TGT:
|
||||
*
|
||||
* gta = ((sphb->nv2_gpa >> 42) & 0x1) << 42;
|
||||
* gta |= ((sphb->nv2_gpa >> 45) & 0x3) << 43;
|
||||
* gta |= ((sphb->nv2_gpa >> 49) & 0x3) << 45;
|
||||
* gta |= sphb->nv2_gpa & ((1UL << 43) - 1);
|
||||
*/
|
||||
_FDT(fdt_setprop_cell(fdt, offset, "memory-region",
|
||||
PHANDLE_GPURAM(sphb, i)));
|
||||
_FDT(fdt_setprop_u64(fdt, offset, "ibm,device-tgt-addr",
|
||||
nvslot->tgt));
|
||||
_FDT(fdt_setprop_cell(fdt, offset, "ibm,nvlink-speed",
|
||||
nvslot->links[j].link_speed));
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -404,7 +404,7 @@ void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn)
|
||||
|
||||
token -= RTAS_TOKEN_BASE;
|
||||
|
||||
assert(!rtas_table[token].name);
|
||||
assert(!name || !rtas_table[token].name);
|
||||
|
||||
rtas_table[token].name = name;
|
||||
rtas_table[token].fn = fn;
|
||||
|
||||
@@ -2180,3 +2180,134 @@ int vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vfio_pci_nvlink2_get_tgt(Object *obj, Visitor *v,
|
||||
const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
uint64_t tgt = (uintptr_t) opaque;
|
||||
visit_type_uint64(v, name, &tgt, errp);
|
||||
}
|
||||
|
||||
static void vfio_pci_nvlink2_get_link_speed(Object *obj, Visitor *v,
|
||||
const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
uint32_t link_speed = (uint32_t)(uintptr_t) opaque;
|
||||
visit_type_uint32(v, name, &link_speed, errp);
|
||||
}
|
||||
|
||||
int vfio_pci_nvidia_v100_ram_init(VFIOPCIDevice *vdev, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
void *p;
|
||||
struct vfio_region_info *nv2reg = NULL;
|
||||
struct vfio_info_cap_header *hdr;
|
||||
struct vfio_region_info_cap_nvlink2_ssatgt *cap;
|
||||
VFIOQuirk *quirk;
|
||||
|
||||
ret = vfio_get_dev_region_info(&vdev->vbasedev,
|
||||
VFIO_REGION_TYPE_PCI_VENDOR_TYPE |
|
||||
PCI_VENDOR_ID_NVIDIA,
|
||||
VFIO_REGION_SUBTYPE_NVIDIA_NVLINK2_RAM,
|
||||
&nv2reg);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
hdr = vfio_get_region_info_cap(nv2reg, VFIO_REGION_INFO_CAP_NVLINK2_SSATGT);
|
||||
if (!hdr) {
|
||||
ret = -ENODEV;
|
||||
goto free_exit;
|
||||
}
|
||||
cap = (void *) hdr;
|
||||
|
||||
p = mmap(NULL, nv2reg->size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_SHARED, vdev->vbasedev.fd, nv2reg->offset);
|
||||
if (p == MAP_FAILED) {
|
||||
ret = -errno;
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
quirk = vfio_quirk_alloc(1);
|
||||
memory_region_init_ram_ptr(&quirk->mem[0], OBJECT(vdev), "nvlink2-mr",
|
||||
nv2reg->size, p);
|
||||
QLIST_INSERT_HEAD(&vdev->bars[0].quirks, quirk, next);
|
||||
|
||||
object_property_add(OBJECT(vdev), "nvlink2-tgt", "uint64",
|
||||
vfio_pci_nvlink2_get_tgt, NULL, NULL,
|
||||
(void *) (uintptr_t) cap->tgt, NULL);
|
||||
trace_vfio_pci_nvidia_gpu_setup_quirk(vdev->vbasedev.name, cap->tgt,
|
||||
nv2reg->size);
|
||||
free_exit:
|
||||
g_free(nv2reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vfio_pci_nvlink2_init(VFIOPCIDevice *vdev, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
void *p;
|
||||
struct vfio_region_info *atsdreg = NULL;
|
||||
struct vfio_info_cap_header *hdr;
|
||||
struct vfio_region_info_cap_nvlink2_ssatgt *captgt;
|
||||
struct vfio_region_info_cap_nvlink2_lnkspd *capspeed;
|
||||
VFIOQuirk *quirk;
|
||||
|
||||
ret = vfio_get_dev_region_info(&vdev->vbasedev,
|
||||
VFIO_REGION_TYPE_PCI_VENDOR_TYPE |
|
||||
PCI_VENDOR_ID_IBM,
|
||||
VFIO_REGION_SUBTYPE_IBM_NVLINK2_ATSD,
|
||||
&atsdreg);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
hdr = vfio_get_region_info_cap(atsdreg,
|
||||
VFIO_REGION_INFO_CAP_NVLINK2_SSATGT);
|
||||
if (!hdr) {
|
||||
ret = -ENODEV;
|
||||
goto free_exit;
|
||||
}
|
||||
captgt = (void *) hdr;
|
||||
|
||||
hdr = vfio_get_region_info_cap(atsdreg,
|
||||
VFIO_REGION_INFO_CAP_NVLINK2_LNKSPD);
|
||||
if (!hdr) {
|
||||
ret = -ENODEV;
|
||||
goto free_exit;
|
||||
}
|
||||
capspeed = (void *) hdr;
|
||||
|
||||
/* Some NVLink bridges may not have assigned ATSD */
|
||||
if (atsdreg->size) {
|
||||
p = mmap(NULL, atsdreg->size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_SHARED, vdev->vbasedev.fd, atsdreg->offset);
|
||||
if (p == MAP_FAILED) {
|
||||
ret = -errno;
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
quirk = vfio_quirk_alloc(1);
|
||||
memory_region_init_ram_device_ptr(&quirk->mem[0], OBJECT(vdev),
|
||||
"nvlink2-atsd-mr", atsdreg->size, p);
|
||||
QLIST_INSERT_HEAD(&vdev->bars[0].quirks, quirk, next);
|
||||
}
|
||||
|
||||
object_property_add(OBJECT(vdev), "nvlink2-tgt", "uint64",
|
||||
vfio_pci_nvlink2_get_tgt, NULL, NULL,
|
||||
(void *) (uintptr_t) captgt->tgt, NULL);
|
||||
trace_vfio_pci_nvlink2_setup_quirk_ssatgt(vdev->vbasedev.name, captgt->tgt,
|
||||
atsdreg->size);
|
||||
|
||||
object_property_add(OBJECT(vdev), "nvlink2-link-speed", "uint32",
|
||||
vfio_pci_nvlink2_get_link_speed, NULL, NULL,
|
||||
(void *) (uintptr_t) capspeed->link_speed, NULL);
|
||||
trace_vfio_pci_nvlink2_setup_quirk_lnkspd(vdev->vbasedev.name,
|
||||
capspeed->link_speed);
|
||||
free_exit:
|
||||
g_free(atsdreg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3086,6 +3086,20 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
if (vdev->vendor_id == PCI_VENDOR_ID_NVIDIA) {
|
||||
ret = vfio_pci_nvidia_v100_ram_init(vdev, errp);
|
||||
if (ret && ret != -ENODEV) {
|
||||
error_report("Failed to setup NVIDIA V100 GPU RAM");
|
||||
}
|
||||
}
|
||||
|
||||
if (vdev->vendor_id == PCI_VENDOR_ID_IBM) {
|
||||
ret = vfio_pci_nvlink2_init(vdev, errp);
|
||||
if (ret && ret != -ENODEV) {
|
||||
error_report("Failed to setup NVlink2 bridge");
|
||||
}
|
||||
}
|
||||
|
||||
vfio_register_err_notifier(vdev);
|
||||
vfio_register_req_notifier(vdev);
|
||||
vfio_setup_resetfn_quirk(vdev);
|
||||
|
||||
@@ -196,6 +196,8 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp);
|
||||
int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
|
||||
struct vfio_region_info *info,
|
||||
Error **errp);
|
||||
int vfio_pci_nvidia_v100_ram_init(VFIOPCIDevice *vdev, Error **errp);
|
||||
int vfio_pci_nvlink2_init(VFIOPCIDevice *vdev, Error **errp);
|
||||
|
||||
void vfio_display_reset(VFIOPCIDevice *vdev);
|
||||
int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp);
|
||||
|
||||
@@ -86,6 +86,10 @@ vfio_pci_igd_opregion_enabled(const char *name) "%s"
|
||||
vfio_pci_igd_host_bridge_enabled(const char *name) "%s"
|
||||
vfio_pci_igd_lpc_bridge_enabled(const char *name) "%s"
|
||||
|
||||
vfio_pci_nvidia_gpu_setup_quirk(const char *name, uint64_t tgt, uint64_t size) "%s tgt=0x%"PRIx64" size=0x%"PRIx64
|
||||
vfio_pci_nvlink2_setup_quirk_ssatgt(const char *name, uint64_t tgt, uint64_t size) "%s tgt=0x%"PRIx64" size=0x%"PRIx64
|
||||
vfio_pci_nvlink2_setup_quirk_lnkspd(const char *name, uint32_t link_speed) "%s link_speed=0x%x"
|
||||
|
||||
# common.c
|
||||
vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)"
|
||||
vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64
|
||||
|
||||
@@ -87,6 +87,9 @@ struct SpaprPhbState {
|
||||
uint32_t mig_liobn;
|
||||
hwaddr mig_mem_win_addr, mig_mem_win_size;
|
||||
hwaddr mig_io_win_addr, mig_io_win_size;
|
||||
hwaddr nv2_gpa_win_addr;
|
||||
hwaddr nv2_atsd_win_addr;
|
||||
struct spapr_phb_pci_nvgpu_config *nvgpus;
|
||||
};
|
||||
|
||||
#define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL
|
||||
@@ -105,6 +108,22 @@ struct SpaprPhbState {
|
||||
|
||||
#define SPAPR_PCI_MSI_WINDOW 0x40000000000ULL
|
||||
|
||||
#define SPAPR_PCI_NV2RAM64_WIN_BASE SPAPR_PCI_LIMIT
|
||||
#define SPAPR_PCI_NV2RAM64_WIN_SIZE (2 * TiB) /* For up to 6 GPUs 256GB each */
|
||||
|
||||
/* Max number of these GPUsper a physical box */
|
||||
#define NVGPU_MAX_NUM 6
|
||||
/* Max number of NVLinks per GPU in any physical box */
|
||||
#define NVGPU_MAX_LINKS 3
|
||||
|
||||
/*
|
||||
* GPU RAM starts at 64TiB so huge DMA window to cover it all ends at 128TiB
|
||||
* which is enough. We do not need DMA for ATSD so we put them at 128TiB.
|
||||
*/
|
||||
#define SPAPR_PCI_NV2ATSD_WIN_BASE (128 * TiB)
|
||||
#define SPAPR_PCI_NV2ATSD_WIN_SIZE (NVGPU_MAX_NUM * NVGPU_MAX_LINKS * \
|
||||
64 * KiB)
|
||||
|
||||
static inline qemu_irq spapr_phb_lsi_qirq(struct SpaprPhbState *phb, int pin)
|
||||
{
|
||||
SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||
@@ -135,6 +154,13 @@ int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, int *state);
|
||||
int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option);
|
||||
int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb);
|
||||
void spapr_phb_vfio_reset(DeviceState *qdev);
|
||||
void spapr_phb_nvgpu_setup(SpaprPhbState *sphb, Error **errp);
|
||||
void spapr_phb_nvgpu_free(SpaprPhbState *sphb);
|
||||
void spapr_phb_nvgpu_populate_dt(SpaprPhbState *sphb, void *fdt, int bus_off,
|
||||
Error **errp);
|
||||
void spapr_phb_nvgpu_ram_populate_dt(SpaprPhbState *sphb, void *fdt);
|
||||
void spapr_phb_nvgpu_populate_pcidev_dt(PCIDevice *dev, void *fdt, int offset,
|
||||
SpaprPhbState *sphb);
|
||||
#else
|
||||
static inline bool spapr_phb_eeh_available(SpaprPhbState *sphb)
|
||||
{
|
||||
@@ -161,6 +187,25 @@ static inline int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb)
|
||||
static inline void spapr_phb_vfio_reset(DeviceState *qdev)
|
||||
{
|
||||
}
|
||||
static inline void spapr_phb_nvgpu_setup(SpaprPhbState *sphb, Error **errp)
|
||||
{
|
||||
}
|
||||
static inline void spapr_phb_nvgpu_free(SpaprPhbState *sphb)
|
||||
{
|
||||
}
|
||||
static inline void spapr_phb_nvgpu_populate_dt(SpaprPhbState *sphb, void *fdt,
|
||||
int bus_off, Error **errp)
|
||||
{
|
||||
}
|
||||
static inline void spapr_phb_nvgpu_ram_populate_dt(SpaprPhbState *sphb,
|
||||
void *fdt)
|
||||
{
|
||||
}
|
||||
static inline void spapr_phb_nvgpu_populate_pcidev_dt(PCIDevice *dev, void *fdt,
|
||||
int offset,
|
||||
SpaprPhbState *sphb)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void spapr_phb_dma_reset(SpaprPhbState *sphb);
|
||||
|
||||
@@ -413,6 +413,10 @@ void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
|
||||
void pci_bus_irqs_cleanup(PCIBus *bus);
|
||||
int pci_bus_get_irq_level(PCIBus *bus, int irq_num);
|
||||
/* 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD */
|
||||
static inline int pci_swizzle(int slot, int pin)
|
||||
{
|
||||
return (slot + pin) % PCI_NUM_PINS;
|
||||
}
|
||||
int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin);
|
||||
PCIBus *pci_register_root_bus(DeviceState *parent, const char *name,
|
||||
pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
|
||||
|
||||
+10
-1
@@ -123,7 +123,8 @@ struct SpaprMachineClass {
|
||||
void (*phb_placement)(SpaprMachineState *spapr, uint32_t index,
|
||||
uint64_t *buid, hwaddr *pio,
|
||||
hwaddr *mmio32, hwaddr *mmio64,
|
||||
unsigned n_dma, uint32_t *liobns, Error **errp);
|
||||
unsigned n_dma, uint32_t *liobns, hwaddr *nv2gpa,
|
||||
hwaddr *nv2atsd, Error **errp);
|
||||
SpaprResizeHpt resize_hpt_default;
|
||||
SpaprCapabilities default_caps;
|
||||
SpaprIrq *irq;
|
||||
@@ -199,6 +200,8 @@ struct SpaprMachineState {
|
||||
|
||||
bool cmd_line_caps[SPAPR_CAP_NUM];
|
||||
SpaprCapabilities def, eff, mig;
|
||||
|
||||
unsigned gpu_numa_id;
|
||||
};
|
||||
|
||||
#define H_SUCCESS 0
|
||||
@@ -672,6 +675,10 @@ typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, SpaprMachineState *sm,
|
||||
uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets);
|
||||
void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn);
|
||||
static inline void spapr_rtas_unregister(int token)
|
||||
{
|
||||
spapr_rtas_register(token, NULL, NULL);
|
||||
}
|
||||
target_ulong spapr_rtas_call(PowerPCCPU *cpu, SpaprMachineState *sm,
|
||||
uint32_t token, uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets);
|
||||
@@ -777,6 +784,8 @@ void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift,
|
||||
Error **errp);
|
||||
void spapr_clear_pending_events(SpaprMachineState *spapr);
|
||||
int spapr_max_server_number(SpaprMachineState *spapr);
|
||||
void spapr_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
|
||||
uint64_t pte0, uint64_t pte1);
|
||||
|
||||
/* DRC callbacks. */
|
||||
void spapr_core_release(DeviceState *dev);
|
||||
|
||||
@@ -740,7 +740,7 @@
|
||||
POWERPC_DEF("7457a_v1.2", CPU_POWERPC_74x7A_v12, 7455,
|
||||
"PowerPC 7457A v1.2 (G4)")
|
||||
/* 64 bits PowerPC */
|
||||
#if defined (TARGET_PPC64)
|
||||
#if defined(TARGET_PPC64)
|
||||
POWERPC_DEF("970_v2.2", CPU_POWERPC_970_v22, 970,
|
||||
"PowerPC 970 v2.2")
|
||||
POWERPC_DEF("970fx_v1.0", CPU_POWERPC_970FX_v10, 970,
|
||||
|
||||
@@ -393,7 +393,8 @@ enum {
|
||||
CPU_POWERPC_RS64IV = 0x00370000,
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
/* Original POWER */
|
||||
/* XXX: should be POWER (RIOS), RSC3308, RSC4608,
|
||||
/*
|
||||
* XXX: should be POWER (RIOS), RSC3308, RSC4608,
|
||||
* POWER2 (RIOS2) & RSC2 (P2SC) here
|
||||
*/
|
||||
/* PA Semi core */
|
||||
|
||||
+131
-110
@@ -23,23 +23,28 @@
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/int128.h"
|
||||
|
||||
//#define PPC_EMULATE_32BITS_HYPV
|
||||
/* #define PPC_EMULATE_32BITS_HYPV */
|
||||
|
||||
#if defined (TARGET_PPC64)
|
||||
#if defined(TARGET_PPC64)
|
||||
/* PowerPC 64 definitions */
|
||||
#define TARGET_LONG_BITS 64
|
||||
#define TARGET_PAGE_BITS 12
|
||||
|
||||
#define TCG_GUEST_DEFAULT_MO 0
|
||||
|
||||
/* Note that the official physical address space bits is 62-M where M
|
||||
is implementation dependent. I've not looked up M for the set of
|
||||
cpus we emulate at the system level. */
|
||||
/*
|
||||
* Note that the official physical address space bits is 62-M where M
|
||||
* is implementation dependent. I've not looked up M for the set of
|
||||
* cpus we emulate at the system level.
|
||||
*/
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 62
|
||||
|
||||
/* Note that the PPC environment architecture talks about 80 bit virtual
|
||||
addresses, with segmentation. Obviously that's not all visible to a
|
||||
single process, which is all we're concerned with here. */
|
||||
/*
|
||||
* Note that the PPC environment architecture talks about 80 bit
|
||||
* virtual addresses, with segmentation. Obviously that's not all
|
||||
* visible to a single process, which is all we're concerned with
|
||||
* here.
|
||||
*/
|
||||
#ifdef TARGET_ABI32
|
||||
# define TARGET_VIRT_ADDR_SPACE_BITS 32
|
||||
#else
|
||||
@@ -49,7 +54,7 @@
|
||||
#define TARGET_PAGE_BITS_64K 16
|
||||
#define TARGET_PAGE_BITS_16M 24
|
||||
|
||||
#else /* defined (TARGET_PPC64) */
|
||||
#else /* defined(TARGET_PPC64) */
|
||||
/* PowerPC 32 definitions */
|
||||
#define TARGET_LONG_BITS 32
|
||||
#define TARGET_PAGE_BITS 12
|
||||
@@ -57,14 +62,14 @@
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 36
|
||||
#define TARGET_VIRT_ADDR_SPACE_BITS 32
|
||||
|
||||
#endif /* defined (TARGET_PPC64) */
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
|
||||
#define CPUArchState struct CPUPPCState
|
||||
|
||||
#include "exec/cpu-defs.h"
|
||||
#include "cpu-qom.h"
|
||||
|
||||
#if defined (TARGET_PPC64)
|
||||
#if defined(TARGET_PPC64)
|
||||
#define PPC_ELF_MACHINE EM_PPC64
|
||||
#else
|
||||
#define PPC_ELF_MACHINE EM_PPC
|
||||
@@ -237,9 +242,11 @@ struct ppc_spr_t {
|
||||
const char *name;
|
||||
target_ulong default_value;
|
||||
#ifdef CONFIG_KVM
|
||||
/* We (ab)use the fact that all the SPRs will have ids for the
|
||||
/*
|
||||
* We (ab)use the fact that all the SPRs will have ids for the
|
||||
* ONE_REG interface will have KVM_REG_PPC to use 0 as meaning,
|
||||
* don't sync this */
|
||||
* don't sync this
|
||||
*/
|
||||
uint64_t one_reg_id;
|
||||
#endif
|
||||
};
|
||||
@@ -656,39 +663,39 @@ enum {
|
||||
#define fpscr_eex (((env->fpscr) >> FPSCR_XX) & ((env->fpscr) >> FPSCR_XE) & \
|
||||
0x1F)
|
||||
|
||||
#define FP_FX (1ull << FPSCR_FX)
|
||||
#define FP_FEX (1ull << FPSCR_FEX)
|
||||
#define FP_VX (1ull << FPSCR_VX)
|
||||
#define FP_OX (1ull << FPSCR_OX)
|
||||
#define FP_UX (1ull << FPSCR_UX)
|
||||
#define FP_ZX (1ull << FPSCR_ZX)
|
||||
#define FP_XX (1ull << FPSCR_XX)
|
||||
#define FP_VXSNAN (1ull << FPSCR_VXSNAN)
|
||||
#define FP_VXISI (1ull << FPSCR_VXISI)
|
||||
#define FP_VXIDI (1ull << FPSCR_VXIDI)
|
||||
#define FP_VXZDZ (1ull << FPSCR_VXZDZ)
|
||||
#define FP_VXIMZ (1ull << FPSCR_VXIMZ)
|
||||
#define FP_VXVC (1ull << FPSCR_VXVC)
|
||||
#define FP_FR (1ull << FSPCR_FR)
|
||||
#define FP_FI (1ull << FPSCR_FI)
|
||||
#define FP_C (1ull << FPSCR_C)
|
||||
#define FP_FL (1ull << FPSCR_FL)
|
||||
#define FP_FG (1ull << FPSCR_FG)
|
||||
#define FP_FE (1ull << FPSCR_FE)
|
||||
#define FP_FU (1ull << FPSCR_FU)
|
||||
#define FP_FPCC (FP_FL | FP_FG | FP_FE | FP_FU)
|
||||
#define FP_FPRF (FP_C | FP_FL | FP_FG | FP_FE | FP_FU)
|
||||
#define FP_VXSOFT (1ull << FPSCR_VXSOFT)
|
||||
#define FP_VXSQRT (1ull << FPSCR_VXSQRT)
|
||||
#define FP_VXCVI (1ull << FPSCR_VXCVI)
|
||||
#define FP_VE (1ull << FPSCR_VE)
|
||||
#define FP_OE (1ull << FPSCR_OE)
|
||||
#define FP_UE (1ull << FPSCR_UE)
|
||||
#define FP_ZE (1ull << FPSCR_ZE)
|
||||
#define FP_XE (1ull << FPSCR_XE)
|
||||
#define FP_NI (1ull << FPSCR_NI)
|
||||
#define FP_RN1 (1ull << FPSCR_RN1)
|
||||
#define FP_RN (1ull << FPSCR_RN)
|
||||
#define FP_FX (1ull << FPSCR_FX)
|
||||
#define FP_FEX (1ull << FPSCR_FEX)
|
||||
#define FP_VX (1ull << FPSCR_VX)
|
||||
#define FP_OX (1ull << FPSCR_OX)
|
||||
#define FP_UX (1ull << FPSCR_UX)
|
||||
#define FP_ZX (1ull << FPSCR_ZX)
|
||||
#define FP_XX (1ull << FPSCR_XX)
|
||||
#define FP_VXSNAN (1ull << FPSCR_VXSNAN)
|
||||
#define FP_VXISI (1ull << FPSCR_VXISI)
|
||||
#define FP_VXIDI (1ull << FPSCR_VXIDI)
|
||||
#define FP_VXZDZ (1ull << FPSCR_VXZDZ)
|
||||
#define FP_VXIMZ (1ull << FPSCR_VXIMZ)
|
||||
#define FP_VXVC (1ull << FPSCR_VXVC)
|
||||
#define FP_FR (1ull << FSPCR_FR)
|
||||
#define FP_FI (1ull << FPSCR_FI)
|
||||
#define FP_C (1ull << FPSCR_C)
|
||||
#define FP_FL (1ull << FPSCR_FL)
|
||||
#define FP_FG (1ull << FPSCR_FG)
|
||||
#define FP_FE (1ull << FPSCR_FE)
|
||||
#define FP_FU (1ull << FPSCR_FU)
|
||||
#define FP_FPCC (FP_FL | FP_FG | FP_FE | FP_FU)
|
||||
#define FP_FPRF (FP_C | FP_FL | FP_FG | FP_FE | FP_FU)
|
||||
#define FP_VXSOFT (1ull << FPSCR_VXSOFT)
|
||||
#define FP_VXSQRT (1ull << FPSCR_VXSQRT)
|
||||
#define FP_VXCVI (1ull << FPSCR_VXCVI)
|
||||
#define FP_VE (1ull << FPSCR_VE)
|
||||
#define FP_OE (1ull << FPSCR_OE)
|
||||
#define FP_UE (1ull << FPSCR_UE)
|
||||
#define FP_ZE (1ull << FPSCR_ZE)
|
||||
#define FP_XE (1ull << FPSCR_XE)
|
||||
#define FP_NI (1ull << FPSCR_NI)
|
||||
#define FP_RN1 (1ull << FPSCR_RN1)
|
||||
#define FP_RN (1ull << FPSCR_RN)
|
||||
|
||||
/* the exception bits which can be cleared by mcrfs - includes FX */
|
||||
#define FP_EX_CLEAR_BITS (FP_FX | FP_OX | FP_UX | FP_ZX | \
|
||||
@@ -698,8 +705,8 @@ enum {
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Vector status and control register */
|
||||
#define VSCR_NJ 16 /* Vector non-java */
|
||||
#define VSCR_SAT 0 /* Vector saturation */
|
||||
#define VSCR_NJ 16 /* Vector non-java */
|
||||
#define VSCR_SAT 0 /* Vector saturation */
|
||||
|
||||
/*****************************************************************************/
|
||||
/* BookE e500 MMU registers */
|
||||
@@ -962,9 +969,10 @@ struct ppc_radix_page_info {
|
||||
/*****************************************************************************/
|
||||
/* The whole PowerPC CPU context */
|
||||
|
||||
/* PowerPC needs eight modes for different hypervisor/supervisor/guest +
|
||||
* real/paged mode combinations. The other two modes are for external PID
|
||||
* load/store.
|
||||
/*
|
||||
* PowerPC needs eight modes for different hypervisor/supervisor/guest
|
||||
* + real/paged mode combinations. The other two modes are for
|
||||
* external PID load/store.
|
||||
*/
|
||||
#define NB_MMU_MODES 10
|
||||
#define MMU_MODE8_SUFFIX _epl
|
||||
@@ -976,8 +984,9 @@ struct ppc_radix_page_info {
|
||||
#define PPC_CPU_INDIRECT_OPCODES_LEN 0x20
|
||||
|
||||
struct CPUPPCState {
|
||||
/* First are the most commonly used resources
|
||||
* during translated code execution
|
||||
/*
|
||||
* First are the most commonly used resources during translated
|
||||
* code execution
|
||||
*/
|
||||
/* general purpose registers */
|
||||
target_ulong gpr[32];
|
||||
@@ -1023,8 +1032,8 @@ struct CPUPPCState {
|
||||
/* High part of 128-bit helper return. */
|
||||
uint64_t retxh;
|
||||
|
||||
int access_type; /* when a memory exception occurs, the access
|
||||
type is stored here */
|
||||
/* when a memory exception occurs, the access type is stored here */
|
||||
int access_type;
|
||||
|
||||
CPU_COMMON
|
||||
|
||||
@@ -1072,8 +1081,10 @@ struct CPUPPCState {
|
||||
/* SPE registers */
|
||||
uint64_t spe_acc;
|
||||
uint32_t spe_fscr;
|
||||
/* SPE and Altivec can share a status since they will never be used
|
||||
* simultaneously */
|
||||
/*
|
||||
* SPE and Altivec can share a status since they will never be
|
||||
* used simultaneously
|
||||
*/
|
||||
float_status vec_status;
|
||||
|
||||
/* Internal devices resources */
|
||||
@@ -1103,7 +1114,8 @@ struct CPUPPCState {
|
||||
int error_code;
|
||||
uint32_t pending_interrupts;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* This is the IRQ controller, which is implementation dependent
|
||||
/*
|
||||
* This is the IRQ controller, which is implementation dependent
|
||||
* and only relevant when emulating a complete machine.
|
||||
*/
|
||||
uint32_t irq_input_state;
|
||||
@@ -1117,7 +1129,8 @@ struct CPUPPCState {
|
||||
hwaddr mpic_iack;
|
||||
/* true when the external proxy facility mode is enabled */
|
||||
bool mpic_proxy;
|
||||
/* set when the processor has an HV mode, thus HV priv
|
||||
/*
|
||||
* set when the processor has an HV mode, thus HV priv
|
||||
* instructions and SPRs are diallowed if MSR:HV is 0
|
||||
*/
|
||||
bool has_hv_mode;
|
||||
@@ -1149,8 +1162,10 @@ struct CPUPPCState {
|
||||
|
||||
/* booke timers */
|
||||
|
||||
/* Specifies bit locations of the Time Base used to signal a fixed timer
|
||||
* exception on a transition from 0 to 1. (watchdog or fixed-interval timer)
|
||||
/*
|
||||
* Specifies bit locations of the Time Base used to signal a fixed
|
||||
* timer exception on a transition from 0 to 1. (watchdog or
|
||||
* fixed-interval timer)
|
||||
*
|
||||
* 0 selects the least significant bit.
|
||||
* 63 selects the most significant bit.
|
||||
@@ -1250,8 +1265,8 @@ struct PPCVirtualHypervisorClass {
|
||||
void (*unmap_hptes)(PPCVirtualHypervisor *vhyp,
|
||||
const ppc_hash_pte64_t *hptes,
|
||||
hwaddr ptex, int n);
|
||||
void (*store_hpte)(PPCVirtualHypervisor *vhyp, hwaddr ptex,
|
||||
uint64_t pte0, uint64_t pte1);
|
||||
void (*hpte_set_c)(PPCVirtualHypervisor *vhyp, hwaddr ptex, uint64_t pte1);
|
||||
void (*hpte_set_r)(PPCVirtualHypervisor *vhyp, hwaddr ptex, uint64_t pte1);
|
||||
void (*get_pate)(PPCVirtualHypervisor *vhyp, ppc_v3_pate_t *entry);
|
||||
target_ulong (*encode_hpt_for_kvm_pr)(PPCVirtualHypervisor *vhyp);
|
||||
};
|
||||
@@ -1290,53 +1305,54 @@ extern const struct VMStateDescription vmstate_ppc_cpu;
|
||||
|
||||
/*****************************************************************************/
|
||||
void ppc_translate_init(void);
|
||||
/* you can call this signal handler from your SIGBUS and SIGSEGV
|
||||
signal handlers to inform the virtual CPU of exceptions. non zero
|
||||
is returned if the signal was handled by the virtual CPU. */
|
||||
int cpu_ppc_signal_handler (int host_signum, void *pinfo,
|
||||
void *puc);
|
||||
/*
|
||||
* you can call this signal handler from your SIGBUS and SIGSEGV
|
||||
* signal handlers to inform the virtual CPU of exceptions. non zero
|
||||
* is returned if the signal was handled by the virtual CPU.
|
||||
*/
|
||||
int cpu_ppc_signal_handler(int host_signum, void *pinfo, void *puc);
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
int ppc_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw,
|
||||
int mmu_idx);
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
void ppc_store_sdr1 (CPUPPCState *env, target_ulong value);
|
||||
void ppc_store_sdr1(CPUPPCState *env, target_ulong value);
|
||||
void ppc_store_ptcr(CPUPPCState *env, target_ulong value);
|
||||
#endif /* !defined(CONFIG_USER_ONLY) */
|
||||
void ppc_store_msr (CPUPPCState *env, target_ulong value);
|
||||
void ppc_store_msr(CPUPPCState *env, target_ulong value);
|
||||
|
||||
void ppc_cpu_list(void);
|
||||
|
||||
/* Time-base and decrementer management */
|
||||
#ifndef NO_CPU_IO_DEFS
|
||||
uint64_t cpu_ppc_load_tbl (CPUPPCState *env);
|
||||
uint32_t cpu_ppc_load_tbu (CPUPPCState *env);
|
||||
void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value);
|
||||
void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value);
|
||||
uint64_t cpu_ppc_load_atbl (CPUPPCState *env);
|
||||
uint32_t cpu_ppc_load_atbu (CPUPPCState *env);
|
||||
void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value);
|
||||
void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value);
|
||||
uint64_t cpu_ppc_load_tbl(CPUPPCState *env);
|
||||
uint32_t cpu_ppc_load_tbu(CPUPPCState *env);
|
||||
void cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value);
|
||||
void cpu_ppc_store_tbl(CPUPPCState *env, uint32_t value);
|
||||
uint64_t cpu_ppc_load_atbl(CPUPPCState *env);
|
||||
uint32_t cpu_ppc_load_atbu(CPUPPCState *env);
|
||||
void cpu_ppc_store_atbl(CPUPPCState *env, uint32_t value);
|
||||
void cpu_ppc_store_atbu(CPUPPCState *env, uint32_t value);
|
||||
bool ppc_decr_clear_on_delivery(CPUPPCState *env);
|
||||
target_ulong cpu_ppc_load_decr(CPUPPCState *env);
|
||||
void cpu_ppc_store_decr(CPUPPCState *env, target_ulong value);
|
||||
target_ulong cpu_ppc_load_hdecr(CPUPPCState *env);
|
||||
void cpu_ppc_store_hdecr(CPUPPCState *env, target_ulong value);
|
||||
uint64_t cpu_ppc_load_purr (CPUPPCState *env);
|
||||
uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env);
|
||||
uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env);
|
||||
uint64_t cpu_ppc_load_purr(CPUPPCState *env);
|
||||
uint32_t cpu_ppc601_load_rtcl(CPUPPCState *env);
|
||||
uint32_t cpu_ppc601_load_rtcu(CPUPPCState *env);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
void cpu_ppc601_store_rtcl (CPUPPCState *env, uint32_t value);
|
||||
void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value);
|
||||
target_ulong load_40x_pit (CPUPPCState *env);
|
||||
void store_40x_pit (CPUPPCState *env, target_ulong val);
|
||||
void store_40x_dbcr0 (CPUPPCState *env, uint32_t val);
|
||||
void store_40x_sler (CPUPPCState *env, uint32_t val);
|
||||
void store_booke_tcr (CPUPPCState *env, target_ulong val);
|
||||
void store_booke_tsr (CPUPPCState *env, target_ulong val);
|
||||
void ppc_tlb_invalidate_all (CPUPPCState *env);
|
||||
void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr);
|
||||
void cpu_ppc601_store_rtcl(CPUPPCState *env, uint32_t value);
|
||||
void cpu_ppc601_store_rtcu(CPUPPCState *env, uint32_t value);
|
||||
target_ulong load_40x_pit(CPUPPCState *env);
|
||||
void store_40x_pit(CPUPPCState *env, target_ulong val);
|
||||
void store_40x_dbcr0(CPUPPCState *env, uint32_t val);
|
||||
void store_40x_sler(CPUPPCState *env, uint32_t val);
|
||||
void store_booke_tcr(CPUPPCState *env, target_ulong val);
|
||||
void store_booke_tsr(CPUPPCState *env, target_ulong val);
|
||||
void ppc_tlb_invalidate_all(CPUPPCState *env);
|
||||
void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr);
|
||||
void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp);
|
||||
#endif
|
||||
#endif
|
||||
@@ -1349,7 +1365,8 @@ static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn)
|
||||
|
||||
gprv = env->gpr[gprn];
|
||||
if (env->flags & POWERPC_FLAG_SPE) {
|
||||
/* If the CPU implements the SPE extension, we have to get the
|
||||
/*
|
||||
* If the CPU implements the SPE extension, we have to get the
|
||||
* high bits of the GPR from the gprh storage area
|
||||
*/
|
||||
gprv &= 0xFFFFFFFFULL;
|
||||
@@ -1360,8 +1377,8 @@ static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn)
|
||||
}
|
||||
|
||||
/* Device control registers */
|
||||
int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp);
|
||||
int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val);
|
||||
int ppc_dcr_read(ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp);
|
||||
int ppc_dcr_write(ppc_dcr_t *dcr_env, int dcrn, uint32_t val);
|
||||
|
||||
#define POWERPC_CPU_TYPE_SUFFIX "-" TYPE_POWERPC_CPU
|
||||
#define POWERPC_CPU_TYPE_NAME(model) model POWERPC_CPU_TYPE_SUFFIX
|
||||
@@ -1372,7 +1389,7 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val);
|
||||
|
||||
/* MMU modes definitions */
|
||||
#define MMU_USER_IDX 0
|
||||
static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
|
||||
static inline int cpu_mmu_index(CPUPPCState *env, bool ifetch)
|
||||
{
|
||||
return ifetch ? env->immu_idx : env->dmmu_idx;
|
||||
}
|
||||
@@ -1990,17 +2007,17 @@ void ppc_compat_add_property(Object *obj, const char *name,
|
||||
/* External Input Interrupt Directed to Guest State */
|
||||
#define EPCR_EXTGS (1 << 31)
|
||||
|
||||
#define L1CSR0_CPE 0x00010000 /* Data Cache Parity Enable */
|
||||
#define L1CSR0_CUL 0x00000400 /* (D-)Cache Unable to Lock */
|
||||
#define L1CSR0_DCLFR 0x00000100 /* D-Cache Lock Flash Reset */
|
||||
#define L1CSR0_DCFI 0x00000002 /* Data Cache Flash Invalidate */
|
||||
#define L1CSR0_DCE 0x00000001 /* Data Cache Enable */
|
||||
#define L1CSR0_CPE 0x00010000 /* Data Cache Parity Enable */
|
||||
#define L1CSR0_CUL 0x00000400 /* (D-)Cache Unable to Lock */
|
||||
#define L1CSR0_DCLFR 0x00000100 /* D-Cache Lock Flash Reset */
|
||||
#define L1CSR0_DCFI 0x00000002 /* Data Cache Flash Invalidate */
|
||||
#define L1CSR0_DCE 0x00000001 /* Data Cache Enable */
|
||||
|
||||
#define L1CSR1_CPE 0x00010000 /* Instruction Cache Parity Enable */
|
||||
#define L1CSR1_ICUL 0x00000400 /* I-Cache Unable to Lock */
|
||||
#define L1CSR1_ICLFR 0x00000100 /* I-Cache Lock Flash Reset */
|
||||
#define L1CSR1_ICFI 0x00000002 /* Instruction Cache Flash Invalidate */
|
||||
#define L1CSR1_ICE 0x00000001 /* Instruction Cache Enable */
|
||||
#define L1CSR1_CPE 0x00010000 /* Instruction Cache Parity Enable */
|
||||
#define L1CSR1_ICUL 0x00000400 /* I-Cache Unable to Lock */
|
||||
#define L1CSR1_ICLFR 0x00000100 /* I-Cache Lock Flash Reset */
|
||||
#define L1CSR1_ICFI 0x00000002 /* Instruction Cache Flash Invalidate */
|
||||
#define L1CSR1_ICE 0x00000001 /* Instruction Cache Enable */
|
||||
|
||||
/* HID0 bits */
|
||||
#define HID0_DEEPNAP (1 << 24) /* pre-2.06 */
|
||||
@@ -2226,7 +2243,8 @@ enum {
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Memory access type :
|
||||
/*
|
||||
* Memory access type :
|
||||
* may be needed for precise access rights control and precise exceptions.
|
||||
*/
|
||||
enum {
|
||||
@@ -2242,8 +2260,9 @@ enum {
|
||||
ACCESS_CACHE = 0x60, /* Cache manipulation */
|
||||
};
|
||||
|
||||
/* Hardware interruption sources:
|
||||
* all those exception can be raised simulteaneously
|
||||
/*
|
||||
* Hardware interrupt sources:
|
||||
* all those exception can be raised simulteaneously
|
||||
*/
|
||||
/* Input pins definitions */
|
||||
enum {
|
||||
@@ -2325,9 +2344,11 @@ enum {
|
||||
enum {
|
||||
/* POWER7 input pins */
|
||||
POWER7_INPUT_INT = 0,
|
||||
/* POWER7 probably has other inputs, but we don't care about them
|
||||
/*
|
||||
* POWER7 probably has other inputs, but we don't care about them
|
||||
* for any existing machine. We can wire these up when we need
|
||||
* them */
|
||||
* them
|
||||
*/
|
||||
POWER7_INPUT_NB,
|
||||
};
|
||||
|
||||
|
||||
@@ -1104,19 +1104,19 @@ void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b, uint32_t s) \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
while (offset < (size)/4) { \
|
||||
while (offset < (size) / 4) { \
|
||||
n++; \
|
||||
digits[(size)/4-n] = dfp_get_bcd_digit_##size(dfp.b64, offset++); \
|
||||
if (digits[(size)/4-n] > 10) { \
|
||||
digits[(size) / 4 - n] = dfp_get_bcd_digit_##size(dfp.b64, offset++); \
|
||||
if (digits[(size) / 4 - n] > 10) { \
|
||||
dfp_set_FPSCR_flag(&dfp, FP_VX | FP_VXCVI, FPSCR_VE); \
|
||||
return; \
|
||||
} else { \
|
||||
nonzero |= (digits[(size)/4-n] > 0); \
|
||||
nonzero |= (digits[(size) / 4 - n] > 0); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
if (nonzero) { \
|
||||
decNumberSetBCD(&dfp.t, digits+((size)/4)-n, n); \
|
||||
decNumberSetBCD(&dfp.t, digits + ((size) / 4) - n, n); \
|
||||
} \
|
||||
\
|
||||
if (s && sgn) { \
|
||||
@@ -1170,13 +1170,13 @@ DFP_HELPER_XEX(dxexq, 128)
|
||||
static void dfp_set_raw_exp_64(uint64_t *t, uint64_t raw)
|
||||
{
|
||||
*t &= 0x8003ffffffffffffULL;
|
||||
*t |= (raw << (63-13));
|
||||
*t |= (raw << (63 - 13));
|
||||
}
|
||||
|
||||
static void dfp_set_raw_exp_128(uint64_t *t, uint64_t raw)
|
||||
{
|
||||
t[HI_IDX] &= 0x80003fffffffffffULL;
|
||||
t[HI_IDX] |= (raw << (63-17));
|
||||
t[HI_IDX] |= (raw << (63 - 17));
|
||||
}
|
||||
|
||||
#define DFP_HELPER_IEX(op, size) \
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user