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-2.9-20170303' into staging
ppc patch queuye for 2017-03-03
This will probably be my last pull request before the hard freeze. It
has some new work, but that has all been posted in draft before the
soft freeze, so I think it's reasonable to include in qemu-2.9.
This batch has:
* A substantial amount of POWER9 work
* Implements the legacy (hash) MMU for POWER9
* Some more preliminaries for implementing the POWER9 radix
MMU
* POWER9 has_work
* Basic POWER9 compatibility mode handling
* Removal of some premature tests
* Some cleanups and fixes to the existing MMU code to make the
POWER9 work simpler
* A bugfix for TCG multiply adds on power
* Allow pseries guests to access PCIe extended config space
This also includes a code-motion not strictly in ppc code - moving
getrampagesize() from ppc code to exec.c. This will make some future
VFIO improvements easier, Paolo said it was ok to merge via my tree.
# gpg: Signature made Fri 03 Mar 2017 03:20:36 GMT
# gpg: using RSA key 0x6C38CACA20D9B392
# gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>"
# gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>"
# gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>"
# gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>"
# Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392
* remotes/dgibson/tags/ppc-for-2.9-20170303:
target/ppc: rewrite f[n]m[add,sub] using float64_muladd
spapr: Small cleanup of PPC MMU enums
spapr_pci: Advertise access to PCIe extended config space
target/ppc: Rework hash mmu page fault code and add defines for clarity
target/ppc: Move no-execute and guarded page checking into new function
target/ppc: Add execute permission checking to access authority check
target/ppc: Add Instruction Authority Mask Register Check
hw/ppc/spapr: Add POWER9 to pseries cpu models
target/ppc/POWER9: Add cpu_has_work function for POWER9
target/ppc/POWER9: Add POWER9 pa-features definition
target/ppc/POWER9: Add POWER9 mmu fault handler
target/ppc: Don't gen an SDR1 on POWER9 and rework register creation
target/ppc: Add patb_entry to sPAPRMachineState
target/ppc/POWER9: Add POWERPC_MMU_V3 bit
powernv: Don't test POWER9 CPU yet
exec, kvm, target-ppc: Move getrampagesize() to common code
target/ppc: Add POWER9/ISAv3.00 to compat_table
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -42,6 +42,7 @@
|
||||
#include "exec/memory.h"
|
||||
#include "exec/ioport.h"
|
||||
#include "sysemu/dma.h"
|
||||
#include "sysemu/numa.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "sysemu/xen-mapcache.h"
|
||||
#include "trace-root.h"
|
||||
@@ -1256,6 +1257,87 @@ void qemu_mutex_unlock_ramlist(void)
|
||||
qemu_mutex_unlock(&ram_list.mutex);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
/*
|
||||
* FIXME TOCTTOU: this iterates over memory backends' mem-path, which
|
||||
* may or may not name the same files / on the same filesystem now as
|
||||
* when we actually open and map them. Iterate over the file
|
||||
* descriptors instead, and use qemu_fd_getpagesize().
|
||||
*/
|
||||
static int find_max_supported_pagesize(Object *obj, void *opaque)
|
||||
{
|
||||
char *mem_path;
|
||||
long *hpsize_min = opaque;
|
||||
|
||||
if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) {
|
||||
mem_path = object_property_get_str(obj, "mem-path", NULL);
|
||||
if (mem_path) {
|
||||
long hpsize = qemu_mempath_getpagesize(mem_path);
|
||||
if (hpsize < *hpsize_min) {
|
||||
*hpsize_min = hpsize;
|
||||
}
|
||||
} else {
|
||||
*hpsize_min = getpagesize();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
long qemu_getrampagesize(void)
|
||||
{
|
||||
long hpsize = LONG_MAX;
|
||||
long mainrampagesize;
|
||||
Object *memdev_root;
|
||||
|
||||
if (mem_path) {
|
||||
mainrampagesize = qemu_mempath_getpagesize(mem_path);
|
||||
} else {
|
||||
mainrampagesize = getpagesize();
|
||||
}
|
||||
|
||||
/* it's possible we have memory-backend objects with
|
||||
* hugepage-backed RAM. these may get mapped into system
|
||||
* address space via -numa parameters or memory hotplug
|
||||
* hooks. we want to take these into account, but we
|
||||
* also want to make sure these supported hugepage
|
||||
* sizes are applicable across the entire range of memory
|
||||
* we may boot from, so we take the min across all
|
||||
* backends, and assume normal pages in cases where a
|
||||
* backend isn't backed by hugepages.
|
||||
*/
|
||||
memdev_root = object_resolve_path("/objects", NULL);
|
||||
if (memdev_root) {
|
||||
object_child_foreach(memdev_root, find_max_supported_pagesize, &hpsize);
|
||||
}
|
||||
if (hpsize == LONG_MAX) {
|
||||
/* No additional memory regions found ==> Report main RAM page size */
|
||||
return mainrampagesize;
|
||||
}
|
||||
|
||||
/* If NUMA is disabled or the NUMA nodes are not backed with a
|
||||
* memory-backend, then there is at least one node using "normal" RAM,
|
||||
* so if its page size is smaller we have got to report that size instead.
|
||||
*/
|
||||
if (hpsize > mainrampagesize &&
|
||||
(nb_numa_nodes == 0 || numa_info[0].node_memdev == NULL)) {
|
||||
static bool warned;
|
||||
if (!warned) {
|
||||
error_report("Huge page support disabled (n/a for main memory).");
|
||||
warned = true;
|
||||
}
|
||||
return mainrampagesize;
|
||||
}
|
||||
|
||||
return hpsize;
|
||||
}
|
||||
#else
|
||||
long qemu_getrampagesize(void)
|
||||
{
|
||||
return getpagesize();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
static int64_t get_file_size(int fd)
|
||||
{
|
||||
|
||||
+50
-5
@@ -390,20 +390,36 @@ static void spapr_populate_pa_features(CPUPPCState *env, void *fdt, int offset)
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x00, 0x00 };
|
||||
/* Currently we don't advertise any of the "new" ISAv3.00 functionality */
|
||||
uint8_t pa_features_300[] = { 64, 0,
|
||||
0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, /* 0 - 5 */
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */
|
||||
0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 18 - 23 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24 - 29 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 - 35 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 36 - 41 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 42 - 47 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 48 - 53 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 54 - 59 */
|
||||
0x00, 0x00, 0x00, 0x00 }; /* 60 - 63 */
|
||||
|
||||
uint8_t *pa_features;
|
||||
size_t pa_size;
|
||||
|
||||
switch (env->mmu_model) {
|
||||
case POWERPC_MMU_2_06:
|
||||
case POWERPC_MMU_2_06a:
|
||||
switch (POWERPC_MMU_VER(env->mmu_model)) {
|
||||
case POWERPC_MMU_VER_2_06:
|
||||
pa_features = pa_features_206;
|
||||
pa_size = sizeof(pa_features_206);
|
||||
break;
|
||||
case POWERPC_MMU_2_07:
|
||||
case POWERPC_MMU_2_07a:
|
||||
case POWERPC_MMU_VER_2_07:
|
||||
pa_features = pa_features_207;
|
||||
pa_size = sizeof(pa_features_207);
|
||||
break;
|
||||
case POWERPC_MMU_VER_3_00:
|
||||
pa_features = pa_features_300;
|
||||
pa_size = sizeof(pa_features_300);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
@@ -1055,6 +1071,13 @@ static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t spapr_get_patbe(PPCVirtualHypervisor *vhyp)
|
||||
{
|
||||
sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp);
|
||||
|
||||
return spapr->patb_entry;
|
||||
}
|
||||
|
||||
#define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2))
|
||||
#define HPTE_VALID(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_VALID)
|
||||
#define HPTE_DIRTY(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_HPTE_DIRTY)
|
||||
@@ -1234,6 +1257,8 @@ static void ppc_spapr_reset(void)
|
||||
/* Check for unknown sysbus devices */
|
||||
foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL);
|
||||
|
||||
spapr->patb_entry = 0;
|
||||
|
||||
/* Allocate and/or reset the hash page table */
|
||||
spapr_reallocate_hpt(spapr,
|
||||
spapr_hpt_shift_for_ramsize(machine->maxram_size),
|
||||
@@ -1427,6 +1452,24 @@ static const VMStateDescription vmstate_spapr_ov5_cas = {
|
||||
},
|
||||
};
|
||||
|
||||
static bool spapr_patb_entry_needed(void *opaque)
|
||||
{
|
||||
sPAPRMachineState *spapr = opaque;
|
||||
|
||||
return !!spapr->patb_entry;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_spapr_patb_entry = {
|
||||
.name = "spapr_patb_entry",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = spapr_patb_entry_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(patb_entry, sPAPRMachineState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_spapr = {
|
||||
.name = "spapr",
|
||||
.version_id = 3,
|
||||
@@ -1444,6 +1487,7 @@ static const VMStateDescription vmstate_spapr = {
|
||||
},
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_spapr_ov5_cas,
|
||||
&vmstate_spapr_patb_entry,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
@@ -3049,6 +3093,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
|
||||
vhc->map_hptes = spapr_map_hptes;
|
||||
vhc->unmap_hptes = spapr_unmap_hptes;
|
||||
vhc->store_hpte = spapr_store_hpte;
|
||||
vhc->get_patbe = spapr_get_patbe;
|
||||
xic->ics_get = spapr_ics_get;
|
||||
xic->ics_resend = spapr_ics_resend;
|
||||
xic->icp_get = spapr_icp_get;
|
||||
|
||||
@@ -238,6 +238,9 @@ static const char *spapr_core_models[] = {
|
||||
|
||||
/* POWER8NVL */
|
||||
"POWER8NVL_v1.0",
|
||||
|
||||
/* POWER9 */
|
||||
"POWER9_v1.0",
|
||||
};
|
||||
|
||||
void spapr_cpu_core_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
@@ -1321,6 +1321,10 @@ static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset,
|
||||
_FDT(fdt_setprop(fdt, offset, "assigned-addresses",
|
||||
(uint8_t *)rp.assigned, rp.assigned_len));
|
||||
|
||||
if (pci_is_express(dev)) {
|
||||
_FDT(fdt_setprop_cell(fdt, offset, "ibm,pci-config-space-type", 0x1));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ static inline void *ramblock_ptr(RAMBlock *block, ram_addr_t offset)
|
||||
return (char *)block->host + offset;
|
||||
}
|
||||
|
||||
long qemu_getrampagesize(void);
|
||||
ram_addr_t last_ram_offset(void);
|
||||
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||
bool share, const char *mem_path,
|
||||
|
||||
@@ -62,6 +62,7 @@ struct sPAPRMachineState {
|
||||
|
||||
void *htab;
|
||||
uint32_t htab_shift;
|
||||
uint64_t patb_entry; /* Process tbl registed in H_REGISTER_PROCESS_TABLE */
|
||||
hwaddr rma_size;
|
||||
int vrma_adjust;
|
||||
ssize_t rtas_size;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
size_t qemu_fd_getpagesize(int fd);
|
||||
|
||||
size_t qemu_mempath_getpagesize(const char *mem_path);
|
||||
|
||||
void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared);
|
||||
|
||||
void qemu_ram_munmap(void *ptr, size_t size);
|
||||
|
||||
@@ -3,7 +3,7 @@ obj-y += cpu.o
|
||||
obj-y += translate.o
|
||||
ifeq ($(CONFIG_SOFTMMU),y)
|
||||
obj-y += machine.o mmu_helper.o mmu-hash32.o monitor.o arch_dump.o
|
||||
obj-$(TARGET_PPC64) += mmu-hash64.o compat.o
|
||||
obj-$(TARGET_PPC64) += mmu-hash64.o mmu-book3s-v3.o compat.o
|
||||
endif
|
||||
obj-$(CONFIG_KVM) += kvm.o
|
||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
||||
|
||||
+11
-5
@@ -39,29 +39,35 @@ static const CompatInfo compat_table[] = {
|
||||
*/
|
||||
{ /* POWER6, ISA2.05 */
|
||||
.pvr = CPU_POWERPC_LOGICAL_2_05,
|
||||
.pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05
|
||||
| PCR_TM_DIS | PCR_VSX_DIS,
|
||||
.pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 |
|
||||
PCR_COMPAT_2_05 | PCR_TM_DIS | PCR_VSX_DIS,
|
||||
.pcr_level = PCR_COMPAT_2_05,
|
||||
.max_threads = 2,
|
||||
},
|
||||
{ /* POWER7, ISA2.06 */
|
||||
.pvr = CPU_POWERPC_LOGICAL_2_06,
|
||||
.pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS,
|
||||
.pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS,
|
||||
.pcr_level = PCR_COMPAT_2_06,
|
||||
.max_threads = 4,
|
||||
},
|
||||
{
|
||||
.pvr = CPU_POWERPC_LOGICAL_2_06_PLUS,
|
||||
.pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS,
|
||||
.pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS,
|
||||
.pcr_level = PCR_COMPAT_2_06,
|
||||
.max_threads = 4,
|
||||
},
|
||||
{ /* POWER8, ISA2.07 */
|
||||
.pvr = CPU_POWERPC_LOGICAL_2_07,
|
||||
.pcr = PCR_COMPAT_2_07,
|
||||
.pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07,
|
||||
.pcr_level = PCR_COMPAT_2_07,
|
||||
.max_threads = 8,
|
||||
},
|
||||
{ /* POWER9, ISA3.00 */
|
||||
.pvr = CPU_POWERPC_LOGICAL_3_00,
|
||||
.pcr = PCR_COMPAT_3_00,
|
||||
.pcr_level = PCR_COMPAT_3_00,
|
||||
.max_threads = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static const CompatInfo *compat_by_pvr(uint32_t pvr)
|
||||
|
||||
@@ -71,6 +71,7 @@ enum powerpc_mmu_t {
|
||||
#define POWERPC_MMU_1TSEG 0x00020000
|
||||
#define POWERPC_MMU_AMR 0x00040000
|
||||
#define POWERPC_MMU_64K 0x00080000
|
||||
#define POWERPC_MMU_V3 0x00100000 /* ISA V3.00 MMU Support */
|
||||
/* 64 bits PowerPC MMU */
|
||||
POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001,
|
||||
/* Architecture 2.03 and later (has LPCR) */
|
||||
@@ -79,21 +80,22 @@ enum powerpc_mmu_t {
|
||||
POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG
|
||||
| POWERPC_MMU_64K
|
||||
| POWERPC_MMU_AMR | 0x00000003,
|
||||
/* Architecture 2.06 "degraded" (no 1T segments) */
|
||||
POWERPC_MMU_2_06a = POWERPC_MMU_64 | POWERPC_MMU_AMR
|
||||
| 0x00000003,
|
||||
/* Architecture 2.07 variant */
|
||||
POWERPC_MMU_2_07 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG
|
||||
| POWERPC_MMU_64K
|
||||
| POWERPC_MMU_AMR | 0x00000004,
|
||||
/* Architecture 2.07 "degraded" (no 1T segments) */
|
||||
POWERPC_MMU_2_07a = POWERPC_MMU_64 | POWERPC_MMU_AMR
|
||||
| 0x00000004,
|
||||
/* Architecture 3.00 variant */
|
||||
POWERPC_MMU_3_00 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG
|
||||
| POWERPC_MMU_64K
|
||||
| POWERPC_MMU_AMR | 0x00000005,
|
||||
| POWERPC_MMU_AMR | POWERPC_MMU_V3
|
||||
| 0x00000005,
|
||||
};
|
||||
#define POWERPC_MMU_VER(x) ((x) & (POWERPC_MMU_64 | 0xFFFF))
|
||||
#define POWERPC_MMU_VER_64B POWERPC_MMU_VER(POWERPC_MMU_64B)
|
||||
#define POWERPC_MMU_VER_2_03 POWERPC_MMU_VER(POWERPC_MMU_2_03)
|
||||
#define POWERPC_MMU_VER_2_06 POWERPC_MMU_VER(POWERPC_MMU_2_06)
|
||||
#define POWERPC_MMU_VER_2_07 POWERPC_MMU_VER(POWERPC_MMU_2_07)
|
||||
#define POWERPC_MMU_VER_3_00 POWERPC_MMU_VER(POWERPC_MMU_3_00)
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Exception model */
|
||||
|
||||
@@ -473,6 +473,22 @@ struct ppc_slb_t {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* DSISR */
|
||||
#define DSISR_NOPTE 0x40000000
|
||||
/* Not permitted by access authority of encoded access authority */
|
||||
#define DSISR_PROTFAULT 0x08000000
|
||||
#define DSISR_ISSTORE 0x02000000
|
||||
/* Not permitted by virtual page class key protection */
|
||||
#define DSISR_AMR 0x00200000
|
||||
|
||||
/* SRR1 error code fields */
|
||||
|
||||
#define SRR1_NOPTE DSISR_NOPTE
|
||||
/* Not permitted due to no-execute or guard bit set */
|
||||
#define SRR1_NOEXEC_GUARD 0x10000000
|
||||
#define SRR1_PROTFAULT DSISR_PROTFAULT
|
||||
#define SRR1_IAMR DSISR_AMR
|
||||
|
||||
/* Facility Status and Control (FSCR) bits */
|
||||
#define FSCR_EBB (63 - 56) /* Event-Based Branch Facility */
|
||||
#define FSCR_TAR (63 - 55) /* Target Address Register */
|
||||
@@ -1216,6 +1232,7 @@ struct PPCVirtualHypervisorClass {
|
||||
hwaddr ptex, int n);
|
||||
void (*store_hpte)(PPCVirtualHypervisor *vhyp, hwaddr ptex,
|
||||
uint64_t pte0, uint64_t pte1);
|
||||
uint64_t (*get_patbe)(PPCVirtualHypervisor *vhyp);
|
||||
};
|
||||
|
||||
#define TYPE_PPC_VIRTUAL_HYPERVISOR "ppc-virtual-hypervisor"
|
||||
|
||||
+46
-167
@@ -743,178 +743,62 @@ uint64_t helper_frim(CPUPPCState *env, uint64_t arg)
|
||||
return do_fri(env, arg, float_round_down);
|
||||
}
|
||||
|
||||
/* fmadd - fmadd. */
|
||||
uint64_t helper_fmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2,
|
||||
uint64_t arg3)
|
||||
static void float64_maddsub_update_excp(CPUPPCState *env, float64 arg1,
|
||||
float64 arg2, float64 arg3,
|
||||
unsigned int madd_flags)
|
||||
{
|
||||
CPU_DoubleU farg1, farg2, farg3;
|
||||
|
||||
farg1.ll = arg1;
|
||||
farg2.ll = arg2;
|
||||
farg3.ll = arg3;
|
||||
|
||||
if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) ||
|
||||
(float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) {
|
||||
if (unlikely((float64_is_infinity(arg1) && float64_is_zero(arg2)) ||
|
||||
(float64_is_zero(arg1) && float64_is_infinity(arg2)))) {
|
||||
/* Multiplication of zero by infinity */
|
||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1);
|
||||
} else {
|
||||
if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) ||
|
||||
float64_is_signaling_nan(farg2.d, &env->fp_status) ||
|
||||
float64_is_signaling_nan(farg3.d, &env->fp_status))) {
|
||||
/* sNaN operation */
|
||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||
}
|
||||
/* This is the way the PowerPC specification defines it */
|
||||
float128 ft0_128, ft1_128;
|
||||
arg1 = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1);
|
||||
} else if (unlikely(float64_is_signaling_nan(arg1, &env->fp_status) ||
|
||||
float64_is_signaling_nan(arg2, &env->fp_status) ||
|
||||
float64_is_signaling_nan(arg3, &env->fp_status))) {
|
||||
/* sNaN operation */
|
||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||
} else if ((float64_is_infinity(arg1) || float64_is_infinity(arg2)) &&
|
||||
float64_is_infinity(arg3)) {
|
||||
uint8_t aSign, bSign, cSign;
|
||||
|
||||
ft0_128 = float64_to_float128(farg1.d, &env->fp_status);
|
||||
ft1_128 = float64_to_float128(farg2.d, &env->fp_status);
|
||||
ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
|
||||
if (unlikely(float128_is_infinity(ft0_128) &&
|
||||
float64_is_infinity(farg3.d) &&
|
||||
float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) {
|
||||
/* Magnitude subtraction of infinities */
|
||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1);
|
||||
} else {
|
||||
ft1_128 = float64_to_float128(farg3.d, &env->fp_status);
|
||||
ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status);
|
||||
farg1.d = float128_to_float64(ft0_128, &env->fp_status);
|
||||
aSign = float64_is_neg(arg1);
|
||||
bSign = float64_is_neg(arg2);
|
||||
cSign = float64_is_neg(arg3);
|
||||
if (madd_flags & float_muladd_negate_c) {
|
||||
cSign ^= 1;
|
||||
}
|
||||
if (aSign ^ bSign ^ cSign) {
|
||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return farg1.ll;
|
||||
}
|
||||
|
||||
/* fmsub - fmsub. */
|
||||
uint64_t helper_fmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2,
|
||||
uint64_t arg3)
|
||||
{
|
||||
CPU_DoubleU farg1, farg2, farg3;
|
||||
|
||||
farg1.ll = arg1;
|
||||
farg2.ll = arg2;
|
||||
farg3.ll = arg3;
|
||||
|
||||
if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) ||
|
||||
(float64_is_zero(farg1.d) &&
|
||||
float64_is_infinity(farg2.d)))) {
|
||||
/* Multiplication of zero by infinity */
|
||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1);
|
||||
} else {
|
||||
if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) ||
|
||||
float64_is_signaling_nan(farg2.d, &env->fp_status) ||
|
||||
float64_is_signaling_nan(farg3.d, &env->fp_status))) {
|
||||
/* sNaN operation */
|
||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||
}
|
||||
/* This is the way the PowerPC specification defines it */
|
||||
float128 ft0_128, ft1_128;
|
||||
|
||||
ft0_128 = float64_to_float128(farg1.d, &env->fp_status);
|
||||
ft1_128 = float64_to_float128(farg2.d, &env->fp_status);
|
||||
ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
|
||||
if (unlikely(float128_is_infinity(ft0_128) &&
|
||||
float64_is_infinity(farg3.d) &&
|
||||
float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) {
|
||||
/* Magnitude subtraction of infinities */
|
||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1);
|
||||
} else {
|
||||
ft1_128 = float64_to_float128(farg3.d, &env->fp_status);
|
||||
ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status);
|
||||
farg1.d = float128_to_float64(ft0_128, &env->fp_status);
|
||||
}
|
||||
}
|
||||
return farg1.ll;
|
||||
#define FPU_FMADD(op, madd_flags) \
|
||||
uint64_t helper_##op(CPUPPCState *env, uint64_t arg1, \
|
||||
uint64_t arg2, uint64_t arg3) \
|
||||
{ \
|
||||
uint32_t flags; \
|
||||
float64 ret = float64_muladd(arg1, arg2, arg3, madd_flags, \
|
||||
&env->fp_status); \
|
||||
flags = get_float_exception_flags(&env->fp_status); \
|
||||
if (flags) { \
|
||||
if (flags & float_flag_invalid) { \
|
||||
float64_maddsub_update_excp(env, arg1, arg2, arg3, \
|
||||
madd_flags); \
|
||||
} \
|
||||
float_check_status(env); \
|
||||
} \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
/* fnmadd - fnmadd. */
|
||||
uint64_t helper_fnmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2,
|
||||
uint64_t arg3)
|
||||
{
|
||||
CPU_DoubleU farg1, farg2, farg3;
|
||||
#define MADD_FLGS 0
|
||||
#define MSUB_FLGS float_muladd_negate_c
|
||||
#define NMADD_FLGS float_muladd_negate_result
|
||||
#define NMSUB_FLGS (float_muladd_negate_c | float_muladd_negate_result)
|
||||
|
||||
farg1.ll = arg1;
|
||||
farg2.ll = arg2;
|
||||
farg3.ll = arg3;
|
||||
|
||||
if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) ||
|
||||
(float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) {
|
||||
/* Multiplication of zero by infinity */
|
||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1);
|
||||
} else {
|
||||
if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) ||
|
||||
float64_is_signaling_nan(farg2.d, &env->fp_status) ||
|
||||
float64_is_signaling_nan(farg3.d, &env->fp_status))) {
|
||||
/* sNaN operation */
|
||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||
}
|
||||
/* This is the way the PowerPC specification defines it */
|
||||
float128 ft0_128, ft1_128;
|
||||
|
||||
ft0_128 = float64_to_float128(farg1.d, &env->fp_status);
|
||||
ft1_128 = float64_to_float128(farg2.d, &env->fp_status);
|
||||
ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
|
||||
if (unlikely(float128_is_infinity(ft0_128) &&
|
||||
float64_is_infinity(farg3.d) &&
|
||||
float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) {
|
||||
/* Magnitude subtraction of infinities */
|
||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1);
|
||||
} else {
|
||||
ft1_128 = float64_to_float128(farg3.d, &env->fp_status);
|
||||
ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status);
|
||||
farg1.d = float128_to_float64(ft0_128, &env->fp_status);
|
||||
}
|
||||
if (likely(!float64_is_any_nan(farg1.d))) {
|
||||
farg1.d = float64_chs(farg1.d);
|
||||
}
|
||||
}
|
||||
return farg1.ll;
|
||||
}
|
||||
|
||||
/* fnmsub - fnmsub. */
|
||||
uint64_t helper_fnmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2,
|
||||
uint64_t arg3)
|
||||
{
|
||||
CPU_DoubleU farg1, farg2, farg3;
|
||||
|
||||
farg1.ll = arg1;
|
||||
farg2.ll = arg2;
|
||||
farg3.ll = arg3;
|
||||
|
||||
if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) ||
|
||||
(float64_is_zero(farg1.d) &&
|
||||
float64_is_infinity(farg2.d)))) {
|
||||
/* Multiplication of zero by infinity */
|
||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1);
|
||||
} else {
|
||||
if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) ||
|
||||
float64_is_signaling_nan(farg2.d, &env->fp_status) ||
|
||||
float64_is_signaling_nan(farg3.d, &env->fp_status))) {
|
||||
/* sNaN operation */
|
||||
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
|
||||
}
|
||||
/* This is the way the PowerPC specification defines it */
|
||||
float128 ft0_128, ft1_128;
|
||||
|
||||
ft0_128 = float64_to_float128(farg1.d, &env->fp_status);
|
||||
ft1_128 = float64_to_float128(farg2.d, &env->fp_status);
|
||||
ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
|
||||
if (unlikely(float128_is_infinity(ft0_128) &&
|
||||
float64_is_infinity(farg3.d) &&
|
||||
float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) {
|
||||
/* Magnitude subtraction of infinities */
|
||||
farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1);
|
||||
} else {
|
||||
ft1_128 = float64_to_float128(farg3.d, &env->fp_status);
|
||||
ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status);
|
||||
farg1.d = float128_to_float64(ft0_128, &env->fp_status);
|
||||
}
|
||||
if (likely(!float64_is_any_nan(farg1.d))) {
|
||||
farg1.d = float64_chs(farg1.d);
|
||||
}
|
||||
}
|
||||
return farg1.ll;
|
||||
}
|
||||
FPU_FMADD(fmadd, MADD_FLGS)
|
||||
FPU_FMADD(fnmadd, NMADD_FLGS)
|
||||
FPU_FMADD(fmsub, MSUB_FLGS)
|
||||
FPU_FMADD(fnmsub, NMSUB_FLGS)
|
||||
|
||||
/* frsp - frsp. */
|
||||
uint64_t helper_frsp(CPUPPCState *env, uint64_t arg)
|
||||
@@ -2384,11 +2268,6 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
|
||||
float_check_status(env); \
|
||||
}
|
||||
|
||||
#define MADD_FLGS 0
|
||||
#define MSUB_FLGS float_muladd_negate_c
|
||||
#define NMADD_FLGS float_muladd_negate_result
|
||||
#define NMSUB_FLGS (float_muladd_negate_c | float_muladd_negate_result)
|
||||
|
||||
VSX_MADD(xsmaddadp, 1, float64, VsrD(0), MADD_FLGS, 1, 1, 0)
|
||||
VSX_MADD(xsmaddmdp, 1, float64, VsrD(0), MADD_FLGS, 0, 1, 0)
|
||||
VSX_MADD(xsmsubadp, 1, float64, VsrD(0), MSUB_FLGS, 1, 1, 0)
|
||||
|
||||
+9
-108
@@ -28,7 +28,6 @@
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/hw_accel.h"
|
||||
#include "sysemu/numa.h"
|
||||
#include "kvm_ppc.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
@@ -43,8 +42,10 @@
|
||||
#include "trace.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "exec/memattrs.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/mmap-alloc.h"
|
||||
#if defined(TARGET_PPC64)
|
||||
#include "hw/ppc/spapr_cpu_core.h"
|
||||
#endif
|
||||
@@ -282,8 +283,8 @@ static void kvm_get_fallback_smmu_info(PowerPCCPU *cpu,
|
||||
info->flags |= KVM_PPC_1T_SEGMENTS;
|
||||
}
|
||||
|
||||
if (env->mmu_model == POWERPC_MMU_2_06 ||
|
||||
env->mmu_model == POWERPC_MMU_2_07) {
|
||||
if (POWERPC_MMU_VER(env->mmu_model) == POWERPC_MMU_VER_2_06 ||
|
||||
POWERPC_MMU_VER(env->mmu_model) == POWERPC_MMU_VER_2_07) {
|
||||
info->slb_size = 32;
|
||||
} else {
|
||||
info->slb_size = 64;
|
||||
@@ -297,8 +298,8 @@ static void kvm_get_fallback_smmu_info(PowerPCCPU *cpu,
|
||||
i++;
|
||||
|
||||
/* 64K on MMU 2.06 and later */
|
||||
if (env->mmu_model == POWERPC_MMU_2_06 ||
|
||||
env->mmu_model == POWERPC_MMU_2_07) {
|
||||
if (POWERPC_MMU_VER(env->mmu_model) == POWERPC_MMU_VER_2_06 ||
|
||||
POWERPC_MMU_VER(env->mmu_model) == POWERPC_MMU_VER_2_07) {
|
||||
info->sps[i].page_shift = 16;
|
||||
info->sps[i].slb_enc = 0x110;
|
||||
info->sps[i].enc[0].page_shift = 16;
|
||||
@@ -329,106 +330,6 @@ static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info)
|
||||
kvm_get_fallback_smmu_info(cpu, info);
|
||||
}
|
||||
|
||||
static long gethugepagesize(const char *mem_path)
|
||||
{
|
||||
struct statfs fs;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = statfs(mem_path, &fs);
|
||||
} while (ret != 0 && errno == EINTR);
|
||||
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Couldn't statfs() memory path: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#define HUGETLBFS_MAGIC 0x958458f6
|
||||
|
||||
if (fs.f_type != HUGETLBFS_MAGIC) {
|
||||
/* Explicit mempath, but it's ordinary pages */
|
||||
return getpagesize();
|
||||
}
|
||||
|
||||
/* It's hugepage, return the huge page size */
|
||||
return fs.f_bsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME TOCTTOU: this iterates over memory backends' mem-path, which
|
||||
* may or may not name the same files / on the same filesystem now as
|
||||
* when we actually open and map them. Iterate over the file
|
||||
* descriptors instead, and use qemu_fd_getpagesize().
|
||||
*/
|
||||
static int find_max_supported_pagesize(Object *obj, void *opaque)
|
||||
{
|
||||
char *mem_path;
|
||||
long *hpsize_min = opaque;
|
||||
|
||||
if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) {
|
||||
mem_path = object_property_get_str(obj, "mem-path", NULL);
|
||||
if (mem_path) {
|
||||
long hpsize = gethugepagesize(mem_path);
|
||||
if (hpsize < *hpsize_min) {
|
||||
*hpsize_min = hpsize;
|
||||
}
|
||||
} else {
|
||||
*hpsize_min = getpagesize();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long getrampagesize(void)
|
||||
{
|
||||
long hpsize = LONG_MAX;
|
||||
long mainrampagesize;
|
||||
Object *memdev_root;
|
||||
|
||||
if (mem_path) {
|
||||
mainrampagesize = gethugepagesize(mem_path);
|
||||
} else {
|
||||
mainrampagesize = getpagesize();
|
||||
}
|
||||
|
||||
/* it's possible we have memory-backend objects with
|
||||
* hugepage-backed RAM. these may get mapped into system
|
||||
* address space via -numa parameters or memory hotplug
|
||||
* hooks. we want to take these into account, but we
|
||||
* also want to make sure these supported hugepage
|
||||
* sizes are applicable across the entire range of memory
|
||||
* we may boot from, so we take the min across all
|
||||
* backends, and assume normal pages in cases where a
|
||||
* backend isn't backed by hugepages.
|
||||
*/
|
||||
memdev_root = object_resolve_path("/objects", NULL);
|
||||
if (memdev_root) {
|
||||
object_child_foreach(memdev_root, find_max_supported_pagesize, &hpsize);
|
||||
}
|
||||
if (hpsize == LONG_MAX) {
|
||||
/* No additional memory regions found ==> Report main RAM page size */
|
||||
return mainrampagesize;
|
||||
}
|
||||
|
||||
/* If NUMA is disabled or the NUMA nodes are not backed with a
|
||||
* memory-backend, then there is at least one node using "normal" RAM,
|
||||
* so if its page size is smaller we have got to report that size instead.
|
||||
*/
|
||||
if (hpsize > mainrampagesize &&
|
||||
(nb_numa_nodes == 0 || numa_info[0].node_memdev == NULL)) {
|
||||
static bool warned;
|
||||
if (!warned) {
|
||||
error_report("Huge page support disabled (n/a for main memory).");
|
||||
warned = true;
|
||||
}
|
||||
return mainrampagesize;
|
||||
}
|
||||
|
||||
return hpsize;
|
||||
}
|
||||
|
||||
static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift)
|
||||
{
|
||||
if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) {
|
||||
@@ -460,7 +361,7 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu)
|
||||
}
|
||||
|
||||
if (!max_cpu_page_size) {
|
||||
max_cpu_page_size = getrampagesize();
|
||||
max_cpu_page_size = qemu_getrampagesize();
|
||||
}
|
||||
|
||||
/* Convert to QEMU form */
|
||||
@@ -521,7 +422,7 @@ bool kvmppc_is_mem_backend_page_size_ok(char *obj_path)
|
||||
long pagesize;
|
||||
|
||||
if (mempath) {
|
||||
pagesize = gethugepagesize(mempath);
|
||||
pagesize = qemu_mempath_getpagesize(mempath);
|
||||
} else {
|
||||
pagesize = getpagesize();
|
||||
}
|
||||
@@ -2205,7 +2106,7 @@ uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift)
|
||||
/* Find the largest hardware supported page size that's less than
|
||||
* or equal to the (logical) backing page size of guest RAM */
|
||||
kvm_get_smmu_info(POWERPC_CPU(first_cpu), &info);
|
||||
rampagesize = getrampagesize();
|
||||
rampagesize = qemu_getrampagesize();
|
||||
best_page_shift = 0;
|
||||
|
||||
for (i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* PowerPC ISAV3 BookS emulation generic mmu helpers for qemu.
|
||||
*
|
||||
* Copyright (c) 2017 Suraj Jitindar Singh, IBM Corporation
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "cpu.h"
|
||||
#include "mmu-hash64.h"
|
||||
#include "mmu-book3s-v3.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
|
||||
int mmu_idx)
|
||||
{
|
||||
if (ppc64_radix_guest(cpu)) { /* Guest uses radix */
|
||||
/* TODO - Unsupported */
|
||||
error_report("Guest Radix Support Unimplemented");
|
||||
exit(1);
|
||||
} else { /* Guest uses hash */
|
||||
return ppc_hash64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* PowerPC ISAV3 BookS emulation generic mmu definitions for qemu.
|
||||
*
|
||||
* Copyright (c) 2017 Suraj Jitindar Singh, IBM Corporation
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef MMU_H
|
||||
#define MMU_H
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
||||
/* Partition Table Entry Fields */
|
||||
#define PATBE1_GR 0x8000000000000000
|
||||
|
||||
#ifdef TARGET_PPC64
|
||||
|
||||
static inline bool ppc64_use_proc_tbl(PowerPCCPU *cpu)
|
||||
{
|
||||
return !!(cpu->env.spr[SPR_LPCR] & LPCR_UPRT);
|
||||
}
|
||||
|
||||
static inline bool ppc64_radix_guest(PowerPCCPU *cpu)
|
||||
{
|
||||
PPCVirtualHypervisorClass *vhc =
|
||||
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
|
||||
|
||||
return !!(vhc->get_patbe(cpu->vhyp) & PATBE1_GR);
|
||||
}
|
||||
|
||||
int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
|
||||
int mmu_idx);
|
||||
|
||||
#endif /* TARGET_PPC64 */
|
||||
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
#endif /* MMU_H */
|
||||
+85
-32
@@ -28,6 +28,7 @@
|
||||
#include "mmu-hash64.h"
|
||||
#include "exec/log.h"
|
||||
#include "hw/hw.h"
|
||||
#include "mmu-book3s-v3.h"
|
||||
|
||||
//#define DEBUG_SLB
|
||||
|
||||
@@ -289,6 +290,16 @@ target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb)
|
||||
return rt;
|
||||
}
|
||||
|
||||
/* Check No-Execute or Guarded Storage */
|
||||
static inline int ppc_hash64_pte_noexec_guard(PowerPCCPU *cpu,
|
||||
ppc_hash_pte64_t pte)
|
||||
{
|
||||
/* Exec permissions CANNOT take away read or write permissions */
|
||||
return (pte.pte1 & HPTE64_R_N) || (pte.pte1 & HPTE64_R_G) ?
|
||||
PAGE_READ | PAGE_WRITE : PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
}
|
||||
|
||||
/* Check Basic Storage Protection */
|
||||
static int ppc_hash64_pte_prot(PowerPCCPU *cpu,
|
||||
ppc_slb_t *slb, ppc_hash_pte64_t pte)
|
||||
{
|
||||
@@ -307,41 +318,51 @@ static int ppc_hash64_pte_prot(PowerPCCPU *cpu,
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
prot = PAGE_READ | PAGE_WRITE;
|
||||
prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
break;
|
||||
|
||||
case 0x3:
|
||||
case 0x6:
|
||||
prot = PAGE_READ;
|
||||
prot = PAGE_READ | PAGE_EXEC;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (pp) {
|
||||
case 0x0:
|
||||
case 0x6:
|
||||
prot = 0;
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
case 0x3:
|
||||
prot = PAGE_READ;
|
||||
prot = PAGE_READ | PAGE_EXEC;
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
prot = PAGE_READ | PAGE_WRITE;
|
||||
prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* No execute if either noexec or guarded bits set */
|
||||
if (!(pte.pte1 & HPTE64_R_N) || (pte.pte1 & HPTE64_R_G)
|
||||
|| (slb->vsid & SLB_VSID_N)) {
|
||||
prot |= PAGE_EXEC;
|
||||
}
|
||||
|
||||
return prot;
|
||||
}
|
||||
|
||||
/* Check the instruction access permissions specified in the IAMR */
|
||||
static int ppc_hash64_iamr_prot(PowerPCCPU *cpu, int key)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
int iamr_bits = (env->spr[SPR_IAMR] >> 2 * (31 - key)) & 0x3;
|
||||
|
||||
/*
|
||||
* An instruction fetch is permitted if the IAMR bit is 0.
|
||||
* If the bit is set, return PAGE_READ | PAGE_WRITE because this bit
|
||||
* can only take away EXEC permissions not READ or WRITE permissions.
|
||||
* If bit is cleared return PAGE_READ | PAGE_WRITE | PAGE_EXEC since
|
||||
* EXEC permissions are allowed.
|
||||
*/
|
||||
return (iamr_bits & 0x1) ? PAGE_READ | PAGE_WRITE :
|
||||
PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
}
|
||||
|
||||
static int ppc_hash64_amr_prot(PowerPCCPU *cpu, ppc_hash_pte64_t pte)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
@@ -374,6 +395,21 @@ static int ppc_hash64_amr_prot(PowerPCCPU *cpu, ppc_hash_pte64_t pte)
|
||||
prot &= ~PAGE_READ;
|
||||
}
|
||||
|
||||
switch (env->mmu_model) {
|
||||
/*
|
||||
* MMU version 2.07 and later support IAMR
|
||||
* Check if the IAMR allows the instruction access - it will return
|
||||
* PAGE_EXEC if it doesn't (and thus that bit will be cleared) or 0
|
||||
* if it does (and prot will be unchanged indicating execution support).
|
||||
*/
|
||||
case POWERPC_MMU_2_07:
|
||||
case POWERPC_MMU_3_00:
|
||||
prot &= ppc_hash64_iamr_prot(cpu, key);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return prot;
|
||||
}
|
||||
|
||||
@@ -664,8 +700,8 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
|
||||
unsigned apshift;
|
||||
hwaddr ptex;
|
||||
ppc_hash_pte64_t pte;
|
||||
int pp_prot, amr_prot, prot;
|
||||
uint64_t new_pte1, dsisr;
|
||||
int exec_prot, pp_prot, amr_prot, prot;
|
||||
uint64_t new_pte1;
|
||||
const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC};
|
||||
hwaddr raddr;
|
||||
|
||||
@@ -706,11 +742,11 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
|
||||
} else {
|
||||
/* The access failed, generate the approriate interrupt */
|
||||
if (rwx == 2) {
|
||||
ppc_hash64_set_isi(cs, env, 0x08000000);
|
||||
ppc_hash64_set_isi(cs, env, SRR1_PROTFAULT);
|
||||
} else {
|
||||
dsisr = 0x08000000;
|
||||
int dsisr = DSISR_PROTFAULT;
|
||||
if (rwx == 1) {
|
||||
dsisr |= 0x02000000;
|
||||
dsisr |= DSISR_ISSTORE;
|
||||
}
|
||||
ppc_hash64_set_dsi(cs, env, eaddr, dsisr);
|
||||
}
|
||||
@@ -726,6 +762,13 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
|
||||
/* 2. Translation is on, so look up the SLB */
|
||||
slb = slb_lookup(cpu, eaddr);
|
||||
if (!slb) {
|
||||
/* No entry found, check if in-memory segment tables are in use */
|
||||
if ((env->mmu_model & POWERPC_MMU_V3) && ppc64_use_proc_tbl(cpu)) {
|
||||
/* TODO - Unsupported */
|
||||
error_report("Segment Table Support Unimplemented");
|
||||
exit(1);
|
||||
}
|
||||
/* Segment still not found, generate the appropriate interrupt */
|
||||
if (rwx == 2) {
|
||||
cs->exception_index = POWERPC_EXCP_ISEG;
|
||||
env->error_code = 0;
|
||||
@@ -741,19 +784,19 @@ skip_slb_search:
|
||||
|
||||
/* 3. Check for segment level no-execute violation */
|
||||
if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) {
|
||||
ppc_hash64_set_isi(cs, env, 0x10000000);
|
||||
ppc_hash64_set_isi(cs, env, SRR1_NOEXEC_GUARD);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 4. Locate the PTE in the hash table */
|
||||
ptex = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte, &apshift);
|
||||
if (ptex == -1) {
|
||||
dsisr = 0x40000000;
|
||||
if (rwx == 2) {
|
||||
ppc_hash64_set_isi(cs, env, dsisr);
|
||||
ppc_hash64_set_isi(cs, env, SRR1_NOPTE);
|
||||
} else {
|
||||
int dsisr = DSISR_NOPTE;
|
||||
if (rwx == 1) {
|
||||
dsisr |= 0x02000000;
|
||||
dsisr |= DSISR_ISSTORE;
|
||||
}
|
||||
ppc_hash64_set_dsi(cs, env, eaddr, dsisr);
|
||||
}
|
||||
@@ -764,25 +807,35 @@ skip_slb_search:
|
||||
|
||||
/* 5. Check access permissions */
|
||||
|
||||
exec_prot = ppc_hash64_pte_noexec_guard(cpu, pte);
|
||||
pp_prot = ppc_hash64_pte_prot(cpu, slb, pte);
|
||||
amr_prot = ppc_hash64_amr_prot(cpu, pte);
|
||||
prot = pp_prot & amr_prot;
|
||||
prot = exec_prot & pp_prot & amr_prot;
|
||||
|
||||
if ((need_prot[rwx] & ~prot) != 0) {
|
||||
/* Access right violation */
|
||||
qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n");
|
||||
if (rwx == 2) {
|
||||
ppc_hash64_set_isi(cs, env, 0x08000000);
|
||||
int srr1 = 0;
|
||||
if (PAGE_EXEC & ~exec_prot) {
|
||||
srr1 |= SRR1_NOEXEC_GUARD; /* Access violates noexec or guard */
|
||||
} else if (PAGE_EXEC & ~pp_prot) {
|
||||
srr1 |= SRR1_PROTFAULT; /* Access violates access authority */
|
||||
}
|
||||
if (PAGE_EXEC & ~amr_prot) {
|
||||
srr1 |= SRR1_IAMR; /* Access violates virt pg class key prot */
|
||||
}
|
||||
ppc_hash64_set_isi(cs, env, srr1);
|
||||
} else {
|
||||
dsisr = 0;
|
||||
int dsisr = 0;
|
||||
if (need_prot[rwx] & ~pp_prot) {
|
||||
dsisr |= 0x08000000;
|
||||
dsisr |= DSISR_PROTFAULT;
|
||||
}
|
||||
if (rwx == 1) {
|
||||
dsisr |= 0x02000000;
|
||||
dsisr |= DSISR_ISSTORE;
|
||||
}
|
||||
if (need_prot[rwx] & ~amr_prot) {
|
||||
dsisr |= 0x00200000;
|
||||
dsisr |= DSISR_AMR;
|
||||
}
|
||||
ppc_hash64_set_dsi(cs, env, eaddr, dsisr);
|
||||
}
|
||||
@@ -979,8 +1032,8 @@ void helper_store_lpcr(CPUPPCState *env, target_ulong val)
|
||||
uint64_t lpcr = 0;
|
||||
|
||||
/* Filter out bits */
|
||||
switch (env->mmu_model) {
|
||||
case POWERPC_MMU_64B: /* 970 */
|
||||
switch (POWERPC_MMU_VER(env->mmu_model)) {
|
||||
case POWERPC_MMU_VER_64B: /* 970 */
|
||||
if (val & 0x40) {
|
||||
lpcr |= LPCR_LPES0;
|
||||
}
|
||||
@@ -1006,26 +1059,26 @@ void helper_store_lpcr(CPUPPCState *env, target_ulong val)
|
||||
* to dig HRMOR out of HID5
|
||||
*/
|
||||
break;
|
||||
case POWERPC_MMU_2_03: /* P5p */
|
||||
case POWERPC_MMU_VER_2_03: /* P5p */
|
||||
lpcr = val & (LPCR_RMLS | LPCR_ILE |
|
||||
LPCR_LPES0 | LPCR_LPES1 |
|
||||
LPCR_RMI | LPCR_HDICE);
|
||||
break;
|
||||
case POWERPC_MMU_2_06: /* P7 */
|
||||
case POWERPC_MMU_VER_2_06: /* P7 */
|
||||
lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD |
|
||||
LPCR_VRMASD | LPCR_RMLS | LPCR_ILE |
|
||||
LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 |
|
||||
LPCR_MER | LPCR_TC |
|
||||
LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE);
|
||||
break;
|
||||
case POWERPC_MMU_2_07: /* P8 */
|
||||
case POWERPC_MMU_VER_2_07: /* P8 */
|
||||
lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV |
|
||||
LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE |
|
||||
LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 |
|
||||
LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 |
|
||||
LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE);
|
||||
break;
|
||||
case POWERPC_MMU_3_00: /* P9 */
|
||||
case POWERPC_MMU_VER_3_00: /* P9 */
|
||||
lpcr = val & (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD |
|
||||
(LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL |
|
||||
LPCR_UPRT | LPCR_EVIRT | LPCR_ONL |
|
||||
|
||||
+42
-40
@@ -29,6 +29,7 @@
|
||||
#include "exec/log.h"
|
||||
#include "helper_regs.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "mmu-book3s-v3.h"
|
||||
|
||||
//#define DEBUG_MMU
|
||||
//#define DEBUG_BATS
|
||||
@@ -1265,7 +1266,7 @@ static void mmu6xx_dump_mmu(FILE *f, fprintf_function cpu_fprintf,
|
||||
|
||||
void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env)
|
||||
{
|
||||
switch (env->mmu_model) {
|
||||
switch (POWERPC_MMU_VER(env->mmu_model)) {
|
||||
case POWERPC_MMU_BOOKE:
|
||||
mmubooke_dump_mmu(f, cpu_fprintf, env);
|
||||
break;
|
||||
@@ -1277,14 +1278,19 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env)
|
||||
mmu6xx_dump_mmu(f, cpu_fprintf, env);
|
||||
break;
|
||||
#if defined(TARGET_PPC64)
|
||||
case POWERPC_MMU_64B:
|
||||
case POWERPC_MMU_2_03:
|
||||
case POWERPC_MMU_2_06:
|
||||
case POWERPC_MMU_2_06a:
|
||||
case POWERPC_MMU_2_07:
|
||||
case POWERPC_MMU_2_07a:
|
||||
case POWERPC_MMU_VER_64B:
|
||||
case POWERPC_MMU_VER_2_03:
|
||||
case POWERPC_MMU_VER_2_06:
|
||||
case POWERPC_MMU_VER_2_07:
|
||||
dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env));
|
||||
break;
|
||||
case POWERPC_MMU_VER_3_00:
|
||||
if (ppc64_radix_guest(ppc_env_get_cpu(env))) {
|
||||
/* TODO - Unsupported */
|
||||
} else {
|
||||
dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: unimplemented\n", __func__);
|
||||
@@ -1417,15 +1423,20 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
|
||||
CPUPPCState *env = &cpu->env;
|
||||
mmu_ctx_t ctx;
|
||||
|
||||
switch (env->mmu_model) {
|
||||
switch (POWERPC_MMU_VER(env->mmu_model)) {
|
||||
#if defined(TARGET_PPC64)
|
||||
case POWERPC_MMU_64B:
|
||||
case POWERPC_MMU_2_03:
|
||||
case POWERPC_MMU_2_06:
|
||||
case POWERPC_MMU_2_06a:
|
||||
case POWERPC_MMU_2_07:
|
||||
case POWERPC_MMU_2_07a:
|
||||
case POWERPC_MMU_VER_64B:
|
||||
case POWERPC_MMU_VER_2_03:
|
||||
case POWERPC_MMU_VER_2_06:
|
||||
case POWERPC_MMU_VER_2_07:
|
||||
return ppc_hash64_get_phys_page_debug(cpu, addr);
|
||||
case POWERPC_MMU_VER_3_00:
|
||||
if (ppc64_radix_guest(ppc_env_get_cpu(env))) {
|
||||
/* TODO - Unsupported */
|
||||
} else {
|
||||
return ppc_hash64_get_phys_page_debug(cpu, addr);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case POWERPC_MMU_32B:
|
||||
@@ -1909,6 +1920,12 @@ void ppc_tlb_invalidate_all(CPUPPCState *env)
|
||||
{
|
||||
PowerPCCPU *cpu = ppc_env_get_cpu(env);
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
if (env->mmu_model & POWERPC_MMU_64) {
|
||||
env->tlb_need_flush = 0;
|
||||
tlb_flush(CPU(cpu));
|
||||
} else
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
switch (env->mmu_model) {
|
||||
case POWERPC_MMU_SOFT_6xx:
|
||||
case POWERPC_MMU_SOFT_74xx:
|
||||
@@ -1933,21 +1950,12 @@ void ppc_tlb_invalidate_all(CPUPPCState *env)
|
||||
break;
|
||||
case POWERPC_MMU_32B:
|
||||
case POWERPC_MMU_601:
|
||||
#if defined(TARGET_PPC64)
|
||||
case POWERPC_MMU_64B:
|
||||
case POWERPC_MMU_2_03:
|
||||
case POWERPC_MMU_2_06:
|
||||
case POWERPC_MMU_2_06a:
|
||||
case POWERPC_MMU_2_07:
|
||||
case POWERPC_MMU_2_07a:
|
||||
case POWERPC_MMU_3_00:
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
env->tlb_need_flush = 0;
|
||||
tlb_flush(CPU(cpu));
|
||||
break;
|
||||
default:
|
||||
/* XXX: TODO */
|
||||
cpu_abort(CPU(cpu), "Unknown MMU model %d\n", env->mmu_model);
|
||||
cpu_abort(CPU(cpu), "Unknown MMU model %x\n", env->mmu_model);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1956,6 +1964,16 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr)
|
||||
{
|
||||
#if !defined(FLUSH_ALL_TLBS)
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
#if defined(TARGET_PPC64)
|
||||
if (env->mmu_model & POWERPC_MMU_64) {
|
||||
/* tlbie invalidate TLBs for all segments */
|
||||
/* XXX: given the fact that there are too many segments to invalidate,
|
||||
* and we still don't have a tlb_flush_mask(env, n, mask) in QEMU,
|
||||
* we just invalidate all TLBs
|
||||
*/
|
||||
env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH;
|
||||
} else
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
switch (env->mmu_model) {
|
||||
case POWERPC_MMU_SOFT_6xx:
|
||||
case POWERPC_MMU_SOFT_74xx:
|
||||
@@ -1973,22 +1991,6 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr)
|
||||
*/
|
||||
env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH;
|
||||
break;
|
||||
#if defined(TARGET_PPC64)
|
||||
case POWERPC_MMU_64B:
|
||||
case POWERPC_MMU_2_03:
|
||||
case POWERPC_MMU_2_06:
|
||||
case POWERPC_MMU_2_06a:
|
||||
case POWERPC_MMU_2_07:
|
||||
case POWERPC_MMU_2_07a:
|
||||
case POWERPC_MMU_3_00:
|
||||
/* tlbie invalidate TLBs for all segments */
|
||||
/* XXX: given the fact that there are too many segments to invalidate,
|
||||
* and we still don't have a tlb_flush_mask(env, n, mask) in QEMU,
|
||||
* we just invalidate all TLBs
|
||||
*/
|
||||
env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH;
|
||||
break;
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
default:
|
||||
/* Should never reach here with other MMU models */
|
||||
assert(0);
|
||||
|
||||
+10
-9
@@ -7078,21 +7078,22 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
|
||||
if (env->spr_cb[SPR_LPCR].name)
|
||||
cpu_fprintf(f, " LPCR " TARGET_FMT_lx "\n", env->spr[SPR_LPCR]);
|
||||
|
||||
switch (env->mmu_model) {
|
||||
switch (POWERPC_MMU_VER(env->mmu_model)) {
|
||||
case POWERPC_MMU_32B:
|
||||
case POWERPC_MMU_601:
|
||||
case POWERPC_MMU_SOFT_6xx:
|
||||
case POWERPC_MMU_SOFT_74xx:
|
||||
#if defined(TARGET_PPC64)
|
||||
case POWERPC_MMU_64B:
|
||||
case POWERPC_MMU_2_03:
|
||||
case POWERPC_MMU_2_06:
|
||||
case POWERPC_MMU_2_06a:
|
||||
case POWERPC_MMU_2_07:
|
||||
case POWERPC_MMU_2_07a:
|
||||
case POWERPC_MMU_VER_64B:
|
||||
case POWERPC_MMU_VER_2_03:
|
||||
case POWERPC_MMU_VER_2_06:
|
||||
case POWERPC_MMU_VER_2_07:
|
||||
case POWERPC_MMU_VER_3_00:
|
||||
#endif
|
||||
cpu_fprintf(f, " SDR1 " TARGET_FMT_lx " DAR " TARGET_FMT_lx
|
||||
" DSISR " TARGET_FMT_lx "\n", env->spr[SPR_SDR1],
|
||||
if (env->spr_cb[SPR_SDR1].name) { /* SDR1 Exists */
|
||||
cpu_fprintf(f, " SDR1 " TARGET_FMT_lx " ", env->spr[SPR_SDR1]);
|
||||
}
|
||||
cpu_fprintf(f, " DAR " TARGET_FMT_lx " DSISR " TARGET_FMT_lx "\n",
|
||||
env->spr[SPR_DAR], env->spr[SPR_DSISR]);
|
||||
break;
|
||||
case POWERPC_MMU_BOOKE206:
|
||||
|
||||
+247
-121
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@ static testdef_t tests[] = {
|
||||
{ "ppc64", "ppce500", "", "U-Boot" },
|
||||
{ "ppc64", "prep", "", "Open Hack'Ware BIOS" },
|
||||
{ "ppc64", "pseries", "", "Open Firmware" },
|
||||
{ "ppc64", "powernv", "-cpu POWER9", "SkiBoot" },
|
||||
{ "ppc64", "powernv", "-cpu POWER8", "SkiBoot" },
|
||||
{ "i386", "isapc", "-cpu qemu32 -device sga", "SGABIOS" },
|
||||
{ "i386", "pc", "-device sga", "SGABIOS" },
|
||||
{ "i386", "q35", "-device sga", "SGABIOS" },
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user