Merge branch 'ppc-for-upstream' of git://github.com/agraf/qemu
* 'ppc-for-upstream' of git://github.com/agraf/qemu: (58 commits) target-ppc: Use NARROW_MODE macro for tlbie target-ppc: Use NARROW_MODE macro for addresses target-ppc: Use NARROW_MODE macro for comparisons target-ppc: Use NARROW_MODE macro for branches target-ppc: Fix add and subf carry generation in narrow mode target-ppc: Use QOM method dispatch for MMU fault handling target-ppc: Move ppc tlb_fill implementation into mmu_helper.c target-ppc: Split user only code out of mmu_helper.c mmu-hash64: Implement Virtual Page Class Key Protection mmu-hash*: Merge translate and fault handling functions mmu-hash*: Don't use full ppc_hash{32, 64}_translate() path for get_phys_page_debug() mmu-hash*: Correctly mask RPN from hash PTE mmu-hash*: Clean up real address calculation mmu-hash*: Clean up PTE flags update mmu-hash64: Factor SLB N bit into permissions bits mmu-hash*: Clean up permission checking mmu-hash32: Remove nx from context structure mmu-hash*: Don't update PTE flags when permission is denied mmu-hash32: Don't look up page tables on BAT permission error mmu-hash32: Cleanup BAT lookup ...
This commit is contained in:
commit
d76bb73549
@ -781,7 +781,8 @@ static int cpu_gdb_write_register(CPUPPCState *env, uint8_t *mem_buf, int n)
|
||||
/* fpscr */
|
||||
if (gdb_has_xml)
|
||||
return 0;
|
||||
return 4;
|
||||
store_fpscr(env, ldtul_p(mem_buf), 0xffffffff);
|
||||
return sizeof(target_ulong);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -629,7 +629,7 @@ static void ppc_spapr_reset(void)
|
||||
spapr->rtas_size);
|
||||
|
||||
/* Set up the entry state */
|
||||
first_cpu_cpu = CPU(first_cpu);
|
||||
first_cpu_cpu = ENV_GET_CPU(first_cpu);
|
||||
first_cpu->gpr[3] = spapr->fdt_addr;
|
||||
first_cpu->gpr[5] = 0;
|
||||
first_cpu_cpu->halted = 0;
|
||||
@ -779,6 +779,11 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
|
||||
spapr->htab_shift++;
|
||||
}
|
||||
|
||||
/* Set up Interrupt Controller before we create the VCPUs */
|
||||
spapr->icp = xics_system_init(smp_cpus * kvmppc_smt_threads() / smp_threads,
|
||||
XICS_IRQS);
|
||||
spapr->next_irq = XICS_IRQ_BASE;
|
||||
|
||||
/* init CPUs */
|
||||
if (cpu_model == NULL) {
|
||||
cpu_model = kvm_enabled() ? "host" : "POWER7";
|
||||
@ -791,6 +796,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
|
||||
}
|
||||
env = &cpu->env;
|
||||
|
||||
xics_cpu_setup(spapr->icp, cpu);
|
||||
|
||||
/* Set time-base frequency to 512 MHz */
|
||||
cpu_ppc_tb_init(env, TIMEBASE_FREQ);
|
||||
|
||||
@ -830,11 +837,6 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
|
||||
}
|
||||
g_free(filename);
|
||||
|
||||
|
||||
/* Set up Interrupt Controller */
|
||||
spapr->icp = xics_system_init(XICS_IRQS);
|
||||
spapr->next_irq = XICS_IRQ_BASE;
|
||||
|
||||
/* Set up EPOW events infrastructure */
|
||||
spapr_events_init(spapr);
|
||||
|
||||
@ -856,7 +858,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
|
||||
/* Set up PCI */
|
||||
spapr_pci_rtas_init();
|
||||
|
||||
phb = spapr_create_phb(spapr, 0, "pci");
|
||||
phb = spapr_create_phb(spapr, 0);
|
||||
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
NICInfo *nd = &nd_table[i];
|
||||
|
@ -3,39 +3,7 @@
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "helper_regs.h"
|
||||
#include "hw/spapr.h"
|
||||
|
||||
#define HPTES_PER_GROUP 8
|
||||
|
||||
#define HPTE_V_SSIZE_SHIFT 62
|
||||
#define HPTE_V_AVPN_SHIFT 7
|
||||
#define HPTE_V_AVPN 0x3fffffffffffff80ULL
|
||||
#define HPTE_V_AVPN_VAL(x) (((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT)
|
||||
#define HPTE_V_COMPARE(x, y) (!(((x) ^ (y)) & 0xffffffffffffff80UL))
|
||||
#define HPTE_V_BOLTED 0x0000000000000010ULL
|
||||
#define HPTE_V_LOCK 0x0000000000000008ULL
|
||||
#define HPTE_V_LARGE 0x0000000000000004ULL
|
||||
#define HPTE_V_SECONDARY 0x0000000000000002ULL
|
||||
#define HPTE_V_VALID 0x0000000000000001ULL
|
||||
|
||||
#define HPTE_R_PP0 0x8000000000000000ULL
|
||||
#define HPTE_R_TS 0x4000000000000000ULL
|
||||
#define HPTE_R_KEY_HI 0x3000000000000000ULL
|
||||
#define HPTE_R_RPN_SHIFT 12
|
||||
#define HPTE_R_RPN 0x3ffffffffffff000ULL
|
||||
#define HPTE_R_FLAGS 0x00000000000003ffULL
|
||||
#define HPTE_R_PP 0x0000000000000003ULL
|
||||
#define HPTE_R_N 0x0000000000000004ULL
|
||||
#define HPTE_R_G 0x0000000000000008ULL
|
||||
#define HPTE_R_M 0x0000000000000010ULL
|
||||
#define HPTE_R_I 0x0000000000000020ULL
|
||||
#define HPTE_R_W 0x0000000000000040ULL
|
||||
#define HPTE_R_WIMG 0x0000000000000078ULL
|
||||
#define HPTE_R_C 0x0000000000000080ULL
|
||||
#define HPTE_R_R 0x0000000000000100ULL
|
||||
#define HPTE_R_KEY_LO 0x0000000000000e00ULL
|
||||
|
||||
#define HPTE_V_1TB_SEG 0x4000000000000000ULL
|
||||
#define HPTE_V_VRMA_MASK 0x4001ffffff000000ULL
|
||||
#include "mmu-hash64.h"
|
||||
|
||||
static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
|
||||
target_ulong pte_index)
|
||||
@ -44,17 +12,17 @@ static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
|
||||
|
||||
rb = (v & ~0x7fULL) << 16; /* AVA field */
|
||||
va_low = pte_index >> 3;
|
||||
if (v & HPTE_V_SECONDARY) {
|
||||
if (v & HPTE64_V_SECONDARY) {
|
||||
va_low = ~va_low;
|
||||
}
|
||||
/* xor vsid from AVA */
|
||||
if (!(v & HPTE_V_1TB_SEG)) {
|
||||
if (!(v & HPTE64_V_1TB_SEG)) {
|
||||
va_low ^= v >> 12;
|
||||
} else {
|
||||
va_low ^= v >> 24;
|
||||
}
|
||||
va_low &= 0x7ff;
|
||||
if (v & HPTE_V_LARGE) {
|
||||
if (v & HPTE64_V_LARGE) {
|
||||
rb |= 1; /* L field */
|
||||
#if 0 /* Disable that P7 specific bit for now */
|
||||
if (r & 0xff000) {
|
||||
@ -84,10 +52,10 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
target_ulong page_shift = 12;
|
||||
target_ulong raddr;
|
||||
target_ulong i;
|
||||
uint8_t *hpte;
|
||||
hwaddr hpte;
|
||||
|
||||
/* only handle 4k and 16M pages for now */
|
||||
if (pteh & HPTE_V_LARGE) {
|
||||
if (pteh & HPTE64_V_LARGE) {
|
||||
#if 0 /* We don't support 64k pages yet */
|
||||
if ((ptel & 0xf000) == 0x1000) {
|
||||
/* 64k page */
|
||||
@ -105,11 +73,11 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
}
|
||||
}
|
||||
|
||||
raddr = (ptel & HPTE_R_RPN) & ~((1ULL << page_shift) - 1);
|
||||
raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << page_shift) - 1);
|
||||
|
||||
if (raddr < spapr->ram_limit) {
|
||||
/* Regular RAM - should have WIMG=0010 */
|
||||
if ((ptel & HPTE_R_WIMG) != HPTE_R_M) {
|
||||
if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
} else {
|
||||
@ -117,7 +85,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
/* FIXME: What WIMG combinations could be sensible for IO?
|
||||
* For now we allow WIMG=010x, but are there others? */
|
||||
/* FIXME: Should we check against registered IO addresses? */
|
||||
if ((ptel & (HPTE_R_W | HPTE_R_I | HPTE_R_M)) != HPTE_R_I) {
|
||||
if ((ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)) != HPTE64_R_I) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
}
|
||||
@ -129,26 +97,26 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
}
|
||||
if (likely((flags & H_EXACT) == 0)) {
|
||||
pte_index &= ~7ULL;
|
||||
hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
|
||||
hpte = pte_index * HASH_PTE_SIZE_64;
|
||||
for (i = 0; ; ++i) {
|
||||
if (i == 8) {
|
||||
return H_PTEG_FULL;
|
||||
}
|
||||
if ((ldq_p(hpte) & HPTE_V_VALID) == 0) {
|
||||
if ((ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) == 0) {
|
||||
break;
|
||||
}
|
||||
hpte += HASH_PTE_SIZE_64;
|
||||
}
|
||||
} else {
|
||||
i = 0;
|
||||
hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
|
||||
if (ldq_p(hpte) & HPTE_V_VALID) {
|
||||
hpte = pte_index * HASH_PTE_SIZE_64;
|
||||
if (ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) {
|
||||
return H_PTEG_FULL;
|
||||
}
|
||||
}
|
||||
stq_p(hpte + (HASH_PTE_SIZE_64/2), ptel);
|
||||
ppc_hash64_store_hpte1(env, hpte, ptel);
|
||||
/* eieio(); FIXME: need some sort of barrier for smp? */
|
||||
stq_p(hpte, pteh);
|
||||
ppc_hash64_store_hpte0(env, hpte, pteh);
|
||||
|
||||
args[0] = pte_index + i;
|
||||
return H_SUCCESS;
|
||||
@ -166,26 +134,26 @@ static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex,
|
||||
target_ulong flags,
|
||||
target_ulong *vp, target_ulong *rp)
|
||||
{
|
||||
uint8_t *hpte;
|
||||
hwaddr hpte;
|
||||
target_ulong v, r, rb;
|
||||
|
||||
if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) {
|
||||
return REMOVE_PARM;
|
||||
}
|
||||
|
||||
hpte = env->external_htab + (ptex * HASH_PTE_SIZE_64);
|
||||
hpte = ptex * HASH_PTE_SIZE_64;
|
||||
|
||||
v = ldq_p(hpte);
|
||||
r = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
|
||||
v = ppc_hash64_load_hpte0(env, hpte);
|
||||
r = ppc_hash64_load_hpte1(env, hpte);
|
||||
|
||||
if ((v & HPTE_V_VALID) == 0 ||
|
||||
if ((v & HPTE64_V_VALID) == 0 ||
|
||||
((flags & H_AVPN) && (v & ~0x7fULL) != avpn) ||
|
||||
((flags & H_ANDCOND) && (v & avpn) != 0)) {
|
||||
return REMOVE_NOT_FOUND;
|
||||
}
|
||||
*vp = v;
|
||||
*rp = r;
|
||||
stq_p(hpte, 0);
|
||||
ppc_hash64_store_hpte0(env, hpte, 0);
|
||||
rb = compute_tlbie_rb(v, r, ptex);
|
||||
ppc_tlb_invalidate_one(env, rb);
|
||||
return REMOVE_SUCCESS;
|
||||
@ -271,7 +239,7 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
|
||||
switch (ret) {
|
||||
case REMOVE_SUCCESS:
|
||||
*tsh |= (r & (HPTE_R_C | HPTE_R_R)) << 43;
|
||||
*tsh |= (r & (HPTE64_R_C | HPTE64_R_R)) << 43;
|
||||
break;
|
||||
|
||||
case REMOVE_PARM:
|
||||
@ -292,34 +260,34 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
target_ulong flags = args[0];
|
||||
target_ulong pte_index = args[1];
|
||||
target_ulong avpn = args[2];
|
||||
uint8_t *hpte;
|
||||
hwaddr hpte;
|
||||
target_ulong v, r, rb;
|
||||
|
||||
if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
|
||||
hpte = pte_index * HASH_PTE_SIZE_64;
|
||||
|
||||
v = ldq_p(hpte);
|
||||
r = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
|
||||
v = ppc_hash64_load_hpte0(env, hpte);
|
||||
r = ppc_hash64_load_hpte1(env, hpte);
|
||||
|
||||
if ((v & HPTE_V_VALID) == 0 ||
|
||||
if ((v & HPTE64_V_VALID) == 0 ||
|
||||
((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) {
|
||||
return H_NOT_FOUND;
|
||||
}
|
||||
|
||||
r &= ~(HPTE_R_PP0 | HPTE_R_PP | HPTE_R_N |
|
||||
HPTE_R_KEY_HI | HPTE_R_KEY_LO);
|
||||
r |= (flags << 55) & HPTE_R_PP0;
|
||||
r |= (flags << 48) & HPTE_R_KEY_HI;
|
||||
r |= flags & (HPTE_R_PP | HPTE_R_N | HPTE_R_KEY_LO);
|
||||
r &= ~(HPTE64_R_PP0 | HPTE64_R_PP | HPTE64_R_N |
|
||||
HPTE64_R_KEY_HI | HPTE64_R_KEY_LO);
|
||||
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);
|
||||
rb = compute_tlbie_rb(v, r, pte_index);
|
||||
stq_p(hpte, v & ~HPTE_V_VALID);
|
||||
ppc_hash64_store_hpte0(env, hpte, v & ~HPTE64_V_VALID);
|
||||
ppc_tlb_invalidate_one(env, rb);
|
||||
stq_p(hpte + (HASH_PTE_SIZE_64/2), r);
|
||||
ppc_hash64_store_hpte1(env, hpte, r);
|
||||
/* Don't need a memory barrier, due to qemu's global lock */
|
||||
stq_p(hpte, v);
|
||||
ppc_hash64_store_hpte0(env, hpte, v);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -521,46 +521,39 @@ static void xics_reset(void *opaque)
|
||||
}
|
||||
}
|
||||
|
||||
struct icp_state *xics_system_init(int nr_irqs)
|
||||
void xics_cpu_setup(struct icp_state *icp, PowerPCCPU *cpu)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
struct icp_server_state *ss = &icp->ss[cs->cpu_index];
|
||||
|
||||
assert(cs->cpu_index < icp->nr_servers);
|
||||
|
||||
switch (PPC_INPUT(env)) {
|
||||
case PPC_FLAGS_INPUT_POWER7:
|
||||
ss->output = env->irq_inputs[POWER7_INPUT_INT];
|
||||
break;
|
||||
|
||||
case PPC_FLAGS_INPUT_970:
|
||||
ss->output = env->irq_inputs[PPC970_INPUT_INT];
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "XICS interrupt controller does not support this CPU "
|
||||
"bus model\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
struct icp_state *xics_system_init(int nr_servers, int nr_irqs)
|
||||
{
|
||||
CPUPPCState *env;
|
||||
CPUState *cpu;
|
||||
int max_server_num;
|
||||
struct icp_state *icp;
|
||||
struct ics_state *ics;
|
||||
|
||||
max_server_num = -1;
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
cpu = CPU(ppc_env_get_cpu(env));
|
||||
if (cpu->cpu_index > max_server_num) {
|
||||
max_server_num = cpu->cpu_index;
|
||||
}
|
||||
}
|
||||
|
||||
icp = g_malloc0(sizeof(*icp));
|
||||
icp->nr_servers = max_server_num + 1;
|
||||
icp->nr_servers = nr_servers;
|
||||
icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state));
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
cpu = CPU(ppc_env_get_cpu(env));
|
||||
struct icp_server_state *ss = &icp->ss[cpu->cpu_index];
|
||||
|
||||
switch (PPC_INPUT(env)) {
|
||||
case PPC_FLAGS_INPUT_POWER7:
|
||||
ss->output = env->irq_inputs[POWER7_INPUT_INT];
|
||||
break;
|
||||
|
||||
case PPC_FLAGS_INPUT_970:
|
||||
ss->output = env->irq_inputs[PPC970_INPUT_INT];
|
||||
break;
|
||||
|
||||
default:
|
||||
hw_error("XICS interrupt model does not support this CPU bus "
|
||||
"model\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
ics = g_malloc0(sizeof(*ics));
|
||||
ics->nr_irqs = nr_irqs;
|
||||
ics->offset = XICS_IRQ_BASE;
|
||||
|
@ -518,6 +518,7 @@ static int spapr_phb_init(SysBusDevice *s)
|
||||
{
|
||||
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s);
|
||||
PCIHostState *phb = PCI_HOST_BRIDGE(s);
|
||||
const char *busname;
|
||||
char *namebuf;
|
||||
int i;
|
||||
PCIBus *bus;
|
||||
@ -575,9 +576,6 @@ static int spapr_phb_init(SysBusDevice *s)
|
||||
}
|
||||
|
||||
sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid);
|
||||
if (!sphb->busname) {
|
||||
sphb->busname = sphb->dtbusname;
|
||||
}
|
||||
|
||||
namebuf = alloca(strlen(sphb->dtbusname) + 32);
|
||||
|
||||
@ -621,7 +619,26 @@ static int spapr_phb_init(SysBusDevice *s)
|
||||
&sphb->msiwindow);
|
||||
}
|
||||
|
||||
bus = pci_register_bus(DEVICE(s), sphb->busname,
|
||||
/*
|
||||
* Selecting a busname is more complex than you'd think, due to
|
||||
* interacting constraints. If the user has specified an id
|
||||
* explicitly for the phb , then we want to use the qdev default
|
||||
* of naming the bus based on the bridge device (so the user can
|
||||
* then assign devices to it in the way they expect). For the
|
||||
* first / default PCI bus (index=0) we want to use just "pci"
|
||||
* because libvirt expects there to be a bus called, simply,
|
||||
* "pci". Otherwise, we use the same name as in the device tree,
|
||||
* since it's unique by construction, and makes the guest visible
|
||||
* BUID clear.
|
||||
*/
|
||||
if (s->qdev.id) {
|
||||
busname = NULL;
|
||||
} else if (sphb->index == 0) {
|
||||
busname = "pci";
|
||||
} else {
|
||||
busname = sphb->dtbusname;
|
||||
}
|
||||
bus = pci_register_bus(DEVICE(s), busname,
|
||||
pci_spapr_set_irq, pci_spapr_map_irq, sphb,
|
||||
&sphb->memspace, &sphb->iospace,
|
||||
PCI_DEVFN(0, 0), PCI_NUM_PINS);
|
||||
@ -663,7 +680,6 @@ static void spapr_phb_reset(DeviceState *qdev)
|
||||
}
|
||||
|
||||
static Property spapr_phb_properties[] = {
|
||||
DEFINE_PROP_STRING("busname", sPAPRPHBState, busname),
|
||||
DEFINE_PROP_INT32("index", sPAPRPHBState, index, -1),
|
||||
DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, -1),
|
||||
DEFINE_PROP_HEX32("liobn", sPAPRPHBState, dma_liobn, -1),
|
||||
@ -694,14 +710,12 @@ static const TypeInfo spapr_phb_info = {
|
||||
.class_init = spapr_phb_class_init,
|
||||
};
|
||||
|
||||
PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index,
|
||||
const char *busname)
|
||||
PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index)
|
||||
{
|
||||
DeviceState *dev;
|
||||
|
||||
dev = qdev_create(NULL, TYPE_SPAPR_PCI_HOST_BRIDGE);
|
||||
qdev_prop_set_uint32(dev, "index", index);
|
||||
qdev_prop_set_string(dev, "busname", busname);
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
return PCI_HOST_BRIDGE(dev);
|
||||
|
@ -39,7 +39,6 @@ typedef struct sPAPRPHBState {
|
||||
|
||||
int32_t index;
|
||||
uint64_t buid;
|
||||
char *busname;
|
||||
char *dtbusname;
|
||||
|
||||
MemoryRegion memspace, iospace;
|
||||
@ -82,8 +81,7 @@ static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin)
|
||||
return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq);
|
||||
}
|
||||
|
||||
PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index,
|
||||
const char *busname);
|
||||
PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index);
|
||||
|
||||
int spapr_populate_pci_dt(sPAPRPHBState *phb,
|
||||
uint32_t xics_phandle,
|
||||
|
@ -35,6 +35,7 @@ struct icp_state;
|
||||
qemu_irq xics_get_qirq(struct icp_state *icp, int irq);
|
||||
void xics_set_irq_type(struct icp_state *icp, int irq, bool lsi);
|
||||
|
||||
struct icp_state *xics_system_init(int nr_irqs);
|
||||
struct icp_state *xics_system_init(int nr_servers, int nr_irqs);
|
||||
void xics_cpu_setup(struct icp_state *icp, PowerPCCPU *cpu);
|
||||
|
||||
#endif /* __XICS_H__ */
|
||||
|
@ -2960,10 +2960,6 @@ static const MonitorDef monitor_defs[] = {
|
||||
{ "xer", 0, &monitor_get_xer, },
|
||||
{ "tbu", 0, &monitor_get_tbu, },
|
||||
{ "tbl", 0, &monitor_get_tbl, },
|
||||
#if defined(TARGET_PPC64)
|
||||
/* Address space register */
|
||||
{ "asr", offsetof(CPUPPCState, asr) },
|
||||
#endif
|
||||
/* Segment registers */
|
||||
{ "sdr1", offsetof(CPUPPCState, spr[SPR_SDR1]) },
|
||||
{ "sr0", offsetof(CPUPPCState, sr[0]) },
|
||||
|
@ -1,11 +1,14 @@
|
||||
obj-y += cpu-models.o
|
||||
obj-y += translate.o
|
||||
obj-$(CONFIG_SOFTMMU) += machine.o
|
||||
ifeq ($(CONFIG_SOFTMMU),y)
|
||||
obj-y += machine.o mmu_helper.o mmu-hash32.o
|
||||
obj-$(TARGET_PPC64) += mmu-hash64.o
|
||||
endif
|
||||
obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o
|
||||
obj-y += excp_helper.o
|
||||
obj-y += fpu_helper.o
|
||||
obj-y += int_helper.o
|
||||
obj-y += mmu_helper.o
|
||||
obj-y += timebase_helper.o
|
||||
obj-y += misc_helper.o
|
||||
obj-y += mem_helper.o
|
||||
obj-$(CONFIG_USER_ONLY) += user_only_helper.o
|
||||
|
@ -1101,9 +1101,9 @@
|
||||
"PowerPC 7457A v1.2 (G4)")
|
||||
/* 64 bits PowerPC */
|
||||
#if defined (TARGET_PPC64)
|
||||
#if defined(TODO)
|
||||
POWERPC_DEF("620", CPU_POWERPC_620, 620,
|
||||
"PowerPC 620")
|
||||
#if defined(TODO)
|
||||
POWERPC_DEF("630", CPU_POWERPC_630, 630,
|
||||
"PowerPC 630 (POWER3)")
|
||||
#endif
|
||||
|
@ -68,6 +68,10 @@ typedef struct PowerPCCPUClass {
|
||||
#endif
|
||||
void (*init_proc)(CPUPPCState *env);
|
||||
int (*check_pow)(CPUPPCState *env);
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
int (*handle_mmu_fault)(CPUPPCState *env, target_ulong eaddr, int rwx,
|
||||
int mmu_idx);
|
||||
#endif
|
||||
} PowerPCCPUClass;
|
||||
|
||||
/**
|
||||
|
@ -113,13 +113,13 @@ enum powerpc_mmu_t {
|
||||
#if defined(TARGET_PPC64)
|
||||
#define POWERPC_MMU_64 0x00010000
|
||||
#define POWERPC_MMU_1TSEG 0x00020000
|
||||
#define POWERPC_MMU_AMR 0x00040000
|
||||
/* 64 bits PowerPC MMU */
|
||||
POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001,
|
||||
/* 620 variant (no segment exceptions) */
|
||||
POWERPC_MMU_620 = POWERPC_MMU_64 | 0x00000002,
|
||||
/* Architecture 2.06 variant */
|
||||
POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG | 0x00000003,
|
||||
/* Architecture 2.06 "degraded" (no 1T segments) */
|
||||
POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG
|
||||
| POWERPC_MMU_AMR | 0x00000003,
|
||||
/* Architecture 2.06 "degraded" (no 1T segments or AMR) */
|
||||
POWERPC_MMU_2_06d = POWERPC_MMU_64 | 0x00000003,
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
};
|
||||
@ -396,36 +396,12 @@ union ppc_tlb_t {
|
||||
#define SDR_64_HTABSIZE 0x000000000000001FULL
|
||||
#endif /* defined(TARGET_PPC64 */
|
||||
|
||||
#define HASH_PTE_SIZE_32 8
|
||||
#define HASH_PTE_SIZE_64 16
|
||||
|
||||
typedef struct ppc_slb_t ppc_slb_t;
|
||||
struct ppc_slb_t {
|
||||
uint64_t esid;
|
||||
uint64_t vsid;
|
||||
};
|
||||
|
||||
/* Bits in the SLB ESID word */
|
||||
#define SLB_ESID_ESID 0xFFFFFFFFF0000000ULL
|
||||
#define SLB_ESID_V 0x0000000008000000ULL /* valid */
|
||||
|
||||
/* Bits in the SLB VSID word */
|
||||
#define SLB_VSID_SHIFT 12
|
||||
#define SLB_VSID_SHIFT_1T 24
|
||||
#define SLB_VSID_SSIZE_SHIFT 62
|
||||
#define SLB_VSID_B 0xc000000000000000ULL
|
||||
#define SLB_VSID_B_256M 0x0000000000000000ULL
|
||||
#define SLB_VSID_B_1T 0x4000000000000000ULL
|
||||
#define SLB_VSID_VSID 0x3FFFFFFFFFFFF000ULL
|
||||
#define SLB_VSID_PTEM (SLB_VSID_B | SLB_VSID_VSID)
|
||||
#define SLB_VSID_KS 0x0000000000000800ULL
|
||||
#define SLB_VSID_KP 0x0000000000000400ULL
|
||||
#define SLB_VSID_N 0x0000000000000200ULL /* no-execute */
|
||||
#define SLB_VSID_L 0x0000000000000100ULL
|
||||
#define SLB_VSID_C 0x0000000000000080ULL /* class */
|
||||
#define SLB_VSID_LP 0x0000000000000030ULL
|
||||
#define SLB_VSID_ATTR 0x0000000000000FFFULL
|
||||
|
||||
#define SEGMENT_SHIFT_256M 28
|
||||
#define SEGMENT_MASK_256M (~((1ULL << SEGMENT_SHIFT_256M) - 1))
|
||||
|
||||
@ -965,8 +941,6 @@ struct CPUPPCState {
|
||||
/* MMU context - only relevant for full system emulation */
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
#if defined(TARGET_PPC64)
|
||||
/* Address space register */
|
||||
target_ulong asr;
|
||||
/* PowerPC 64 SLB area */
|
||||
ppc_slb_t slb[64];
|
||||
int slb_nr;
|
||||
@ -1105,20 +1079,6 @@ do { \
|
||||
env->wdt_period[3] = (d_); \
|
||||
} while (0)
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* Context used internally during MMU translations */
|
||||
typedef struct mmu_ctx_t mmu_ctx_t;
|
||||
struct mmu_ctx_t {
|
||||
hwaddr raddr; /* Real address */
|
||||
hwaddr eaddr; /* Effective address */
|
||||
int prot; /* Protection bits */
|
||||
hwaddr hash[2]; /* Pagetable hash values */
|
||||
target_ulong ptem; /* Virtual segment ID | API */
|
||||
int key; /* Access key */
|
||||
int nx; /* Non-execute area */
|
||||
};
|
||||
#endif
|
||||
|
||||
#include "cpu-qom.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -1130,17 +1090,14 @@ int cpu_ppc_exec (CPUPPCState *s);
|
||||
is returned if the signal was handled by the virtual CPU. */
|
||||
int cpu_ppc_signal_handler (int host_signum, void *pinfo,
|
||||
void *puc);
|
||||
int cpu_ppc_handle_mmu_fault (CPUPPCState *env, target_ulong address, int rw,
|
||||
int mmu_idx);
|
||||
#define cpu_handle_mmu_fault cpu_ppc_handle_mmu_fault
|
||||
void ppc_hw_interrupt (CPUPPCState *env);
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
int cpu_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
|
||||
int mmu_idx);
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
void ppc_store_sdr1 (CPUPPCState *env, target_ulong value);
|
||||
#if defined(TARGET_PPC64)
|
||||
void ppc_store_asr (CPUPPCState *env, target_ulong value);
|
||||
int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
#endif /* !defined(CONFIG_USER_ONLY) */
|
||||
void ppc_store_msr (CPUPPCState *env, target_ulong value);
|
||||
|
||||
@ -1172,14 +1129,13 @@ 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);
|
||||
int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb,
|
||||
hwaddr *raddrp, target_ulong address,
|
||||
uint32_t pid);
|
||||
void ppc_tlb_invalidate_all (CPUPPCState *env);
|
||||
void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask);
|
||||
|
||||
static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn)
|
||||
{
|
||||
uint64_t gprv;
|
||||
@ -1270,6 +1226,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
|
||||
#define SPR_601_UDECR (0x006)
|
||||
#define SPR_LR (0x008)
|
||||
#define SPR_CTR (0x009)
|
||||
#define SPR_UAMR (0x00C)
|
||||
#define SPR_DSCR (0x011)
|
||||
#define SPR_DSISR (0x012)
|
||||
#define SPR_DAR (0x013) /* DAE for PowerPC 601 */
|
||||
@ -1307,6 +1264,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
|
||||
#define SPR_MPC_CMPH (0x09B)
|
||||
#define SPR_MPC_LCTRL1 (0x09C)
|
||||
#define SPR_MPC_LCTRL2 (0x09D)
|
||||
#define SPR_UAMOR (0x09D)
|
||||
#define SPR_MPC_ICTRL (0x09E)
|
||||
#define SPR_MPC_BAR (0x09F)
|
||||
#define SPR_VRSAVE (0x100)
|
||||
@ -1489,11 +1447,9 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
|
||||
#define SPR_RCPU_MI_RBA2 (0x302)
|
||||
#define SPR_MPC_MI_AP (0x302)
|
||||
#define SPR_PERF3 (0x303)
|
||||
#define SPR_620_PMC1R (0x303)
|
||||
#define SPR_RCPU_MI_RBA3 (0x303)
|
||||
#define SPR_MPC_MI_EPN (0x303)
|
||||
#define SPR_PERF4 (0x304)
|
||||
#define SPR_620_PMC2R (0x304)
|
||||
#define SPR_PERF5 (0x305)
|
||||
#define SPR_MPC_MI_TWC (0x305)
|
||||
#define SPR_PERF6 (0x306)
|
||||
@ -1509,7 +1465,6 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
|
||||
#define SPR_RCPU_L2U_RBA2 (0x30A)
|
||||
#define SPR_MPC_MD_AP (0x30A)
|
||||
#define SPR_PERFB (0x30B)
|
||||
#define SPR_620_MMCR0R (0x30B)
|
||||
#define SPR_RCPU_L2U_RBA3 (0x30B)
|
||||
#define SPR_MPC_MD_EPN (0x30B)
|
||||
#define SPR_PERFC (0x30C)
|
||||
@ -1524,9 +1479,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
|
||||
#define SPR_UPERF1 (0x311)
|
||||
#define SPR_UPERF2 (0x312)
|
||||
#define SPR_UPERF3 (0x313)
|
||||
#define SPR_620_PMC1W (0x313)
|
||||
#define SPR_UPERF4 (0x314)
|
||||
#define SPR_620_PMC2W (0x314)
|
||||
#define SPR_UPERF5 (0x315)
|
||||
#define SPR_UPERF6 (0x316)
|
||||
#define SPR_UPERF7 (0x317)
|
||||
@ -1534,7 +1487,6 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
|
||||
#define SPR_UPERF9 (0x319)
|
||||
#define SPR_UPERFA (0x31A)
|
||||
#define SPR_UPERFB (0x31B)
|
||||
#define SPR_620_MMCR0W (0x31B)
|
||||
#define SPR_UPERFC (0x31C)
|
||||
#define SPR_UPERFD (0x31D)
|
||||
#define SPR_UPERFE (0x31E)
|
||||
@ -1606,49 +1558,33 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
|
||||
#define SPR_USDA (0x3AF)
|
||||
#define SPR_40x_ZPR (0x3B0)
|
||||
#define SPR_BOOKE_MAS7 (0x3B0)
|
||||
#define SPR_620_PMR0 (0x3B0)
|
||||
#define SPR_MMCR2 (0x3B0)
|
||||
#define SPR_PMC5 (0x3B1)
|
||||
#define SPR_40x_PID (0x3B1)
|
||||
#define SPR_620_PMR1 (0x3B1)
|
||||
#define SPR_PMC6 (0x3B2)
|
||||
#define SPR_440_MMUCR (0x3B2)
|
||||
#define SPR_620_PMR2 (0x3B2)
|
||||
#define SPR_4xx_CCR0 (0x3B3)
|
||||
#define SPR_BOOKE_EPLC (0x3B3)
|
||||
#define SPR_620_PMR3 (0x3B3)
|
||||
#define SPR_405_IAC3 (0x3B4)
|
||||
#define SPR_BOOKE_EPSC (0x3B4)
|
||||
#define SPR_620_PMR4 (0x3B4)
|
||||
#define SPR_405_IAC4 (0x3B5)
|
||||
#define SPR_620_PMR5 (0x3B5)
|
||||
#define SPR_405_DVC1 (0x3B6)
|
||||
#define SPR_620_PMR6 (0x3B6)
|
||||
#define SPR_405_DVC2 (0x3B7)
|
||||
#define SPR_620_PMR7 (0x3B7)
|
||||
#define SPR_BAMR (0x3B7)
|
||||
#define SPR_MMCR0 (0x3B8)
|
||||
#define SPR_620_PMR8 (0x3B8)
|
||||
#define SPR_PMC1 (0x3B9)
|
||||
#define SPR_40x_SGR (0x3B9)
|
||||
#define SPR_620_PMR9 (0x3B9)
|
||||
#define SPR_PMC2 (0x3BA)
|
||||
#define SPR_40x_DCWR (0x3BA)
|
||||
#define SPR_620_PMRA (0x3BA)
|
||||
#define SPR_SIAR (0x3BB)
|
||||
#define SPR_405_SLER (0x3BB)
|
||||
#define SPR_620_PMRB (0x3BB)
|
||||
#define SPR_MMCR1 (0x3BC)
|
||||
#define SPR_405_SU0R (0x3BC)
|
||||
#define SPR_620_PMRC (0x3BC)
|
||||
#define SPR_401_SKR (0x3BC)
|
||||
#define SPR_PMC3 (0x3BD)
|
||||
#define SPR_405_DBCR1 (0x3BD)
|
||||
#define SPR_620_PMRD (0x3BD)
|
||||
#define SPR_PMC4 (0x3BE)
|
||||
#define SPR_620_PMRE (0x3BE)
|
||||
#define SPR_SDA (0x3BF)
|
||||
#define SPR_620_PMRF (0x3BF)
|
||||
#define SPR_403_VTBL (0x3CC)
|
||||
#define SPR_403_VTBU (0x3CD)
|
||||
#define SPR_DMISS (0x3D0)
|
||||
@ -1716,15 +1652,12 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
|
||||
#define SPR_LDSTCR (0x3F8)
|
||||
#define SPR_L2PMCR (0x3F8)
|
||||
#define SPR_750FX_HID2 (0x3F8)
|
||||
#define SPR_620_BUSCSR (0x3F8)
|
||||
#define SPR_Exxx_L1FINV0 (0x3F8)
|
||||
#define SPR_L2CR (0x3F9)
|
||||
#define SPR_620_L2CR (0x3F9)
|
||||
#define SPR_L3CR (0x3FA)
|
||||
#define SPR_750_TDCH (0x3FA)
|
||||
#define SPR_IABR2 (0x3FA)
|
||||
#define SPR_40x_DCCR (0x3FA)
|
||||
#define SPR_620_L2SR (0x3FA)
|
||||
#define SPR_ICTC (0x3FB)
|
||||
#define SPR_40x_ICCR (0x3FB)
|
||||
#define SPR_THRM1 (0x3FC)
|
||||
|
@ -463,6 +463,11 @@ void helper_store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask)
|
||||
fpscr_set_rounding_mode(env);
|
||||
}
|
||||
|
||||
void store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask)
|
||||
{
|
||||
helper_store_fpscr(env, arg, mask);
|
||||
}
|
||||
|
||||
void helper_float_check_status(CPUPPCState *env)
|
||||
{
|
||||
if (env->exception_index == POWERPC_EXCP_PROGRAM &&
|
||||
|
@ -382,7 +382,6 @@ DEF_HELPER_1(load_601_rtcl, tl, env)
|
||||
DEF_HELPER_1(load_601_rtcu, tl, env)
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
#if defined(TARGET_PPC64)
|
||||
DEF_HELPER_2(store_asr, void, env, tl)
|
||||
DEF_HELPER_1(load_purr, tl, env)
|
||||
#endif
|
||||
DEF_HELPER_2(store_sdr1, void, env, tl)
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/spapr.h"
|
||||
#include "mmu-hash64.h"
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/spapr.h"
|
||||
@ -1077,7 +1078,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
||||
dprintf("handle halt\n");
|
||||
ret = kvmppc_handle_halt(cpu);
|
||||
break;
|
||||
#ifdef CONFIG_PSERIES
|
||||
#if defined(TARGET_PPC64)
|
||||
case KVM_EXIT_PAPR_HCALL:
|
||||
dprintf("handle PAPR hypercall\n");
|
||||
run->papr_hcall.ret = spapr_hypercall(cpu,
|
||||
|
@ -37,7 +37,7 @@ void cpu_save(QEMUFile *f, void *opaque)
|
||||
qemu_put_be32s(f, &fpscr);
|
||||
qemu_put_sbe32s(f, &env->access_type);
|
||||
#if defined(TARGET_PPC64)
|
||||
qemu_put_betls(f, &env->asr);
|
||||
qemu_put_betls(f, &env->spr[SPR_ASR]);
|
||||
qemu_put_sbe32s(f, &env->slb_nr);
|
||||
#endif
|
||||
qemu_put_betls(f, &env->spr[SPR_SDR1]);
|
||||
@ -125,7 +125,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
|
||||
env->fpscr = fpscr;
|
||||
qemu_get_sbe32s(f, &env->access_type);
|
||||
#if defined(TARGET_PPC64)
|
||||
qemu_get_betls(f, &env->asr);
|
||||
qemu_get_betls(f, &env->spr[SPR_ASR]);
|
||||
qemu_get_sbe32s(f, &env->slb_nr);
|
||||
#endif
|
||||
qemu_get_betls(f, &sdr1);
|
||||
|
@ -252,41 +252,3 @@ STVE(stvewx, cpu_stl_data, bswap32, u32)
|
||||
|
||||
#undef HI_IDX
|
||||
#undef LO_IDX
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Softmmu support */
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
#define MMUSUFFIX _mmu
|
||||
|
||||
#define SHIFT 0
|
||||
#include "exec/softmmu_template.h"
|
||||
|
||||
#define SHIFT 1
|
||||
#include "exec/softmmu_template.h"
|
||||
|
||||
#define SHIFT 2
|
||||
#include "exec/softmmu_template.h"
|
||||
|
||||
#define SHIFT 3
|
||||
#include "exec/softmmu_template.h"
|
||||
|
||||
/* try to fill the TLB and return an exception if error. If retaddr is
|
||||
NULL, it means that the function was called in C code (i.e. not
|
||||
from generated code or from helper.c) */
|
||||
/* XXX: fix it to restore all registers */
|
||||
void tlb_fill(CPUPPCState *env, target_ulong addr, int is_write, int mmu_idx,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx);
|
||||
if (unlikely(ret != 0)) {
|
||||
if (likely(retaddr)) {
|
||||
/* now we have a real cpu fault */
|
||||
cpu_restore_state(env, retaddr);
|
||||
}
|
||||
helper_raise_exception_err(env, env->exception_index, env->error_code);
|
||||
}
|
||||
}
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
@ -35,12 +35,6 @@ void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn)
|
||||
env->spr[sprn]);
|
||||
}
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
#if defined(TARGET_PPC64)
|
||||
void helper_store_asr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
ppc_store_asr(env, val);
|
||||
}
|
||||
#endif
|
||||
|
||||
void helper_store_sdr1(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
|
560
target-ppc/mmu-hash32.c
Normal file
560
target-ppc/mmu-hash32.c
Normal file
@ -0,0 +1,560 @@
|
||||
/*
|
||||
* PowerPC MMU, TLB and BAT emulation helpers for QEMU.
|
||||
*
|
||||
* Copyright (c) 2003-2007 Jocelyn Mayer
|
||||
* Copyright (c) 2013 David Gibson, 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 "cpu.h"
|
||||
#include "helper.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "kvm_ppc.h"
|
||||
#include "mmu-hash32.h"
|
||||
|
||||
//#define DEBUG_MMU
|
||||
//#define DEBUG_BAT
|
||||
|
||||
#ifdef DEBUG_MMU
|
||||
# define LOG_MMU(...) qemu_log(__VA_ARGS__)
|
||||
# define LOG_MMU_STATE(env) log_cpu_state((env), 0)
|
||||
#else
|
||||
# define LOG_MMU(...) do { } while (0)
|
||||
# define LOG_MMU_STATE(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_BATS
|
||||
# define LOG_BATS(...) qemu_log(__VA_ARGS__)
|
||||
#else
|
||||
# define LOG_BATS(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
struct mmu_ctx_hash32 {
|
||||
hwaddr raddr; /* Real address */
|
||||
int prot; /* Protection bits */
|
||||
int key; /* Access key */
|
||||
};
|
||||
|
||||
static int ppc_hash32_pp_prot(int key, int pp, int nx)
|
||||
{
|
||||
int prot;
|
||||
|
||||
if (key == 0) {
|
||||
switch (pp) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
prot = PAGE_READ | PAGE_WRITE;
|
||||
break;
|
||||
|
||||
case 0x3:
|
||||
prot = PAGE_READ;
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
switch (pp) {
|
||||
case 0x0:
|
||||
prot = 0;
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
case 0x3:
|
||||
prot = PAGE_READ;
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
prot = PAGE_READ | PAGE_WRITE;
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
if (nx == 0) {
|
||||
prot |= PAGE_EXEC;
|
||||
}
|
||||
|
||||
return prot;
|
||||
}
|
||||
|
||||
static int ppc_hash32_pte_prot(CPUPPCState *env,
|
||||
target_ulong sr, ppc_hash_pte32_t pte)
|
||||
{
|
||||
unsigned pp, key;
|
||||
|
||||
key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS));
|
||||
pp = pte.pte1 & HPTE32_R_PP;
|
||||
|
||||
return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX));
|
||||
}
|
||||
|
||||
static target_ulong hash32_bat_size(CPUPPCState *env,
|
||||
target_ulong batu, target_ulong batl)
|
||||
{
|
||||
if ((msr_pr && !(batu & BATU32_VP))
|
||||
|| (!msr_pr && !(batu & BATU32_VS))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return BATU32_BEPI & ~((batu & BATU32_BL) << 15);
|
||||
}
|
||||
|
||||
static int hash32_bat_prot(CPUPPCState *env,
|
||||
target_ulong batu, target_ulong batl)
|
||||
{
|
||||
int pp, prot;
|
||||
|
||||
prot = 0;
|
||||
pp = batl & BATL32_PP;
|
||||
if (pp != 0) {
|
||||
prot = PAGE_READ | PAGE_EXEC;
|
||||
if (pp == 0x2) {
|
||||
prot |= PAGE_WRITE;
|
||||
}
|
||||
}
|
||||
return prot;
|
||||
}
|
||||
|
||||
static target_ulong hash32_bat_601_size(CPUPPCState *env,
|
||||
target_ulong batu, target_ulong batl)
|
||||
{
|
||||
if (!(batl & BATL32_601_V)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17);
|
||||
}
|
||||
|
||||
static int hash32_bat_601_prot(CPUPPCState *env,
|
||||
target_ulong batu, target_ulong batl)
|
||||
{
|
||||
int key, pp;
|
||||
|
||||
pp = batu & BATU32_601_PP;
|
||||
if (msr_pr == 0) {
|
||||
key = !!(batu & BATU32_601_KS);
|
||||
} else {
|
||||
key = !!(batu & BATU32_601_KP);
|
||||
}
|
||||
return ppc_hash32_pp_prot(key, pp, 0);
|
||||
}
|
||||
|
||||
static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx,
|
||||
int *prot)
|
||||
{
|
||||
target_ulong *BATlt, *BATut;
|
||||
int i;
|
||||
|
||||
LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__,
|
||||
rwx == 2 ? 'I' : 'D', ea);
|
||||
if (rwx == 2) {
|
||||
BATlt = env->IBAT[1];
|
||||
BATut = env->IBAT[0];
|
||||
} else {
|
||||
BATlt = env->DBAT[1];
|
||||
BATut = env->DBAT[0];
|
||||
}
|
||||
for (i = 0; i < env->nb_BATs; i++) {
|
||||
target_ulong batu = BATut[i];
|
||||
target_ulong batl = BATlt[i];
|
||||
target_ulong mask;
|
||||
|
||||
if (unlikely(env->mmu_model == POWERPC_MMU_601)) {
|
||||
mask = hash32_bat_601_size(env, batu, batl);
|
||||
} else {
|
||||
mask = hash32_bat_size(env, batu, batl);
|
||||
}
|
||||
LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
|
||||
" BATl " TARGET_FMT_lx "\n", __func__,
|
||||
type == ACCESS_CODE ? 'I' : 'D', i, ea, batu, batl);
|
||||
|
||||
if (mask && ((ea & mask) == (batu & BATU32_BEPI))) {
|
||||
hwaddr raddr = (batl & mask) | (ea & ~mask);
|
||||
|
||||
if (unlikely(env->mmu_model == POWERPC_MMU_601)) {
|
||||
*prot = hash32_bat_601_prot(env, batu, batl);
|
||||
} else {
|
||||
*prot = hash32_bat_prot(env, batu, batl);
|
||||
}
|
||||
|
||||
return raddr & TARGET_PAGE_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
/* No hit */
|
||||
#if defined(DEBUG_BATS)
|
||||
if (qemu_log_enabled()) {
|
||||
LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea);
|
||||
for (i = 0; i < 4; i++) {
|
||||
BATu = &BATut[i];
|
||||
BATl = &BATlt[i];
|
||||
BEPIu = *BATu & BATU32_BEPIU;
|
||||
BEPIl = *BATu & BATU32_BEPIL;
|
||||
bl = (*BATu & 0x00001FFC) << 15;
|
||||
LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
|
||||
" BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " "
|
||||
TARGET_FMT_lx " " TARGET_FMT_lx "\n",
|
||||
__func__, type == ACCESS_CODE ? 'I' : 'D', i, ea,
|
||||
*BATu, *BATl, BEPIu, BEPIl, bl);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr,
|
||||
target_ulong eaddr, int rwx,
|
||||
hwaddr *raddr, int *prot)
|
||||
{
|
||||
int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS));
|
||||
|
||||
LOG_MMU("direct store...\n");
|
||||
|
||||
if ((sr & 0x1FF00000) >> 20 == 0x07f) {
|
||||
/* Memory-forced I/O controller interface access */
|
||||
/* If T=1 and BUID=x'07F', the 601 performs a memory access
|
||||
* to SR[28-31] LA[4-31], bypassing all protection mechanisms.
|
||||
*/
|
||||
*raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF);
|
||||
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rwx == 2) {
|
||||
/* No code fetch is allowed in direct-store areas */
|
||||
env->exception_index = POWERPC_EXCP_ISI;
|
||||
env->error_code = 0x10000000;
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (env->access_type) {
|
||||
case ACCESS_INT:
|
||||
/* Integer load/store : only access allowed */
|
||||
break;
|
||||
case ACCESS_FLOAT:
|
||||
/* Floating point load/store */
|
||||
env->exception_index = POWERPC_EXCP_ALIGN;
|
||||
env->error_code = POWERPC_EXCP_ALIGN_FP;
|
||||
env->spr[SPR_DAR] = eaddr;
|
||||
return 1;
|
||||
case ACCESS_RES:
|
||||
/* lwarx, ldarx or srwcx. */
|
||||
env->error_code = 0;
|
||||
env->spr[SPR_DAR] = eaddr;
|
||||
if (rwx == 1) {
|
||||
env->spr[SPR_DSISR] = 0x06000000;
|
||||
} else {
|
||||
env->spr[SPR_DSISR] = 0x04000000;
|
||||
}
|
||||
return 1;
|
||||
case ACCESS_CACHE:
|
||||
/* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */
|
||||
/* Should make the instruction do no-op.
|
||||
* As it already do no-op, it's quite easy :-)
|
||||
*/
|
||||
*raddr = eaddr;
|
||||
return 0;
|
||||
case ACCESS_EXT:
|
||||
/* eciwx or ecowx */
|
||||
env->exception_index = POWERPC_EXCP_DSI;
|
||||
env->error_code = 0;
|
||||
env->spr[SPR_DAR] = eaddr;
|
||||
if (rwx == 1) {
|
||||
env->spr[SPR_DSISR] = 0x06100000;
|
||||
} else {
|
||||
env->spr[SPR_DSISR] = 0x04100000;
|
||||
}
|
||||
return 1;
|
||||
default:
|
||||
qemu_log("ERROR: instruction should not need "
|
||||
"address translation\n");
|
||||
abort();
|
||||
}
|
||||
if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) {
|
||||
*raddr = eaddr;
|
||||
return 0;
|
||||
} else {
|
||||
env->exception_index = POWERPC_EXCP_DSI;
|
||||
env->error_code = 0;
|
||||
env->spr[SPR_DAR] = eaddr;
|
||||
if (rwx == 1) {
|
||||
env->spr[SPR_DSISR] = 0x0a000000;
|
||||
} else {
|
||||
env->spr[SPR_DSISR] = 0x08000000;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash)
|
||||
{
|
||||
return (hash * HASH_PTEG_SIZE_32) & env->htab_mask;
|
||||
}
|
||||
|
||||
static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off,
|
||||
bool secondary, target_ulong ptem,
|
||||
ppc_hash_pte32_t *pte)
|
||||
{
|
||||
hwaddr pte_offset = pteg_off;
|
||||
target_ulong pte0, pte1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HPTES_PER_GROUP; i++) {
|
||||
pte0 = ppc_hash32_load_hpte0(env, pte_offset);
|
||||
pte1 = ppc_hash32_load_hpte1(env, pte_offset);
|
||||
|
||||
if ((pte0 & HPTE32_V_VALID)
|
||||
&& (secondary == !!(pte0 & HPTE32_V_SECONDARY))
|
||||
&& HPTE32_V_COMPARE(pte0, ptem)) {
|
||||
pte->pte0 = pte0;
|
||||
pte->pte1 = pte1;
|
||||
return pte_offset;
|
||||
}
|
||||
|
||||
pte_offset += HASH_PTE_SIZE_32;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env,
|
||||
target_ulong sr, target_ulong eaddr,
|
||||
ppc_hash_pte32_t *pte)
|
||||
{
|
||||
hwaddr pteg_off, pte_offset;
|
||||
hwaddr hash;
|
||||
uint32_t vsid, pgidx, ptem;
|
||||
|
||||
vsid = sr & SR32_VSID;
|
||||
pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS;
|
||||
hash = vsid ^ pgidx;
|
||||
ptem = (vsid << 7) | (pgidx >> 10);
|
||||
|
||||
/* Page address translation */
|
||||
LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
|
||||
" hash " TARGET_FMT_plx "\n",
|
||||
env->htab_base, env->htab_mask, hash);
|
||||
|
||||
/* Primary PTEG lookup */
|
||||
LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
|
||||
" vsid=%" PRIx32 " ptem=%" PRIx32
|
||||
" hash=" TARGET_FMT_plx "\n",
|
||||
env->htab_base, env->htab_mask, vsid, ptem, hash);
|
||||
pteg_off = get_pteg_offset32(env, hash);
|
||||
pte_offset = ppc_hash32_pteg_search(env, pteg_off, 0, ptem, pte);
|
||||
if (pte_offset == -1) {
|
||||
/* Secondary PTEG lookup */
|
||||
LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
|
||||
" vsid=%" PRIx32 " api=%" PRIx32
|
||||
" hash=" TARGET_FMT_plx "\n", env->htab_base,
|
||||
env->htab_mask, vsid, ptem, ~hash);
|
||||
pteg_off = get_pteg_offset32(env, ~hash);
|
||||
pte_offset = ppc_hash32_pteg_search(env, pteg_off, 1, ptem, pte);
|
||||
}
|
||||
|
||||
return pte_offset;
|
||||
}
|
||||
|
||||
static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte,
|
||||
target_ulong eaddr)
|
||||
{
|
||||
hwaddr rpn = pte.pte1 & HPTE32_R_RPN;
|
||||
hwaddr mask = ~TARGET_PAGE_MASK;
|
||||
|
||||
return (rpn & ~mask) | (eaddr & mask);
|
||||
}
|
||||
|
||||
int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, int rwx,
|
||||
int mmu_idx)
|
||||
{
|
||||
target_ulong sr;
|
||||
hwaddr pte_offset;
|
||||
ppc_hash_pte32_t pte;
|
||||
int prot;
|
||||
uint32_t new_pte1;
|
||||
const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC};
|
||||
hwaddr raddr;
|
||||
|
||||
assert((rwx == 0) || (rwx == 1) || (rwx == 2));
|
||||
|
||||
/* 1. Handle real mode accesses */
|
||||
if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
|
||||
/* Translation is off */
|
||||
raddr = eaddr;
|
||||
tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
|
||||
PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
|
||||
TARGET_PAGE_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 2. Check Block Address Translation entries (BATs) */
|
||||
if (env->nb_BATs != 0) {
|
||||
raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &prot);
|
||||
if (raddr != -1) {
|
||||
if (need_prot[rwx] & ~prot) {
|
||||
if (rwx == 2) {
|
||||
env->exception_index = POWERPC_EXCP_ISI;
|
||||
env->error_code = 0x08000000;
|
||||
} else {
|
||||
env->exception_index = POWERPC_EXCP_DSI;
|
||||
env->error_code = 0;
|
||||
env->spr[SPR_DAR] = eaddr;
|
||||
if (rwx == 1) {
|
||||
env->spr[SPR_DSISR] = 0x0a000000;
|
||||
} else {
|
||||
env->spr[SPR_DSISR] = 0x08000000;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
tlb_set_page(env, eaddr & TARGET_PAGE_MASK,
|
||||
raddr & TARGET_PAGE_MASK, prot, mmu_idx,
|
||||
TARGET_PAGE_SIZE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 3. Look up the Segment Register */
|
||||
sr = env->sr[eaddr >> 28];
|
||||
|
||||
/* 4. Handle direct store segments */
|
||||
if (sr & SR32_T) {
|
||||
if (ppc_hash32_direct_store(env, sr, eaddr, rwx,
|
||||
&raddr, &prot) == 0) {
|
||||
tlb_set_page(env, eaddr & TARGET_PAGE_MASK,
|
||||
raddr & TARGET_PAGE_MASK, prot, mmu_idx,
|
||||
TARGET_PAGE_SIZE);
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 5. Check for segment level no-execute violation */
|
||||
if ((rwx == 2) && (sr & SR32_NX)) {
|
||||
env->exception_index = POWERPC_EXCP_ISI;
|
||||
env->error_code = 0x10000000;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 6. Locate the PTE in the hash table */
|
||||
pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte);
|
||||
if (pte_offset == -1) {
|
||||
if (rwx == 2) {
|
||||
env->exception_index = POWERPC_EXCP_ISI;
|
||||
env->error_code = 0x40000000;
|
||||
} else {
|
||||
env->exception_index = POWERPC_EXCP_DSI;
|
||||
env->error_code = 0;
|
||||
env->spr[SPR_DAR] = eaddr;
|
||||
if (rwx == 1) {
|
||||
env->spr[SPR_DSISR] = 0x42000000;
|
||||
} else {
|
||||
env->spr[SPR_DSISR] = 0x40000000;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
|
||||
|
||||
/* 7. Check access permissions */
|
||||
|
||||
prot = ppc_hash32_pte_prot(env, sr, pte);
|
||||
|
||||
if (need_prot[rwx] & ~prot) {
|
||||
/* Access right violation */
|
||||
LOG_MMU("PTE access rejected\n");
|
||||
if (rwx == 2) {
|
||||
env->exception_index = POWERPC_EXCP_ISI;
|
||||
env->error_code = 0x08000000;
|
||||
} else {
|
||||
env->exception_index = POWERPC_EXCP_DSI;
|
||||
env->error_code = 0;
|
||||
env->spr[SPR_DAR] = eaddr;
|
||||
if (rwx == 1) {
|
||||
env->spr[SPR_DSISR] = 0x0a000000;
|
||||
} else {
|
||||
env->spr[SPR_DSISR] = 0x08000000;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOG_MMU("PTE access granted !\n");
|
||||
|
||||
/* 8. Update PTE referenced and changed bits if necessary */
|
||||
|
||||
new_pte1 = pte.pte1 | HPTE32_R_R; /* set referenced bit */
|
||||
if (rwx == 1) {
|
||||
new_pte1 |= HPTE32_R_C; /* set changed (dirty) bit */
|
||||
} else {
|
||||
/* Treat the page as read-only for now, so that a later write
|
||||
* will pass through this function again to set the C bit */
|
||||
prot &= ~PAGE_WRITE;
|
||||
}
|
||||
|
||||
if (new_pte1 != pte.pte1) {
|
||||
ppc_hash32_store_hpte1(env, pte_offset, new_pte1);
|
||||
}
|
||||
|
||||
/* 9. Determine the real address from the PTE */
|
||||
|
||||
raddr = ppc_hash32_pte_raddr(sr, pte, eaddr);
|
||||
|
||||
tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
|
||||
prot, mmu_idx, TARGET_PAGE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong eaddr)
|
||||
{
|
||||
target_ulong sr;
|
||||
hwaddr pte_offset;
|
||||
ppc_hash_pte32_t pte;
|
||||
int prot;
|
||||
|
||||
if (msr_dr == 0) {
|
||||
/* Translation is off */
|
||||
return eaddr;
|
||||
}
|
||||
|
||||
if (env->nb_BATs != 0) {
|
||||
hwaddr raddr = ppc_hash32_bat_lookup(env, eaddr, 0, &prot);
|
||||
if (raddr != -1) {
|
||||
return raddr;
|
||||
}
|
||||
}
|
||||
|
||||
sr = env->sr[eaddr >> 28];
|
||||
|
||||
if (sr & SR32_T) {
|
||||
/* FIXME: Add suitable debug support for Direct Store segments */
|
||||
return -1;
|
||||
}
|
||||
|
||||
pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte);
|
||||
if (pte_offset == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ppc_hash32_pte_raddr(sr, pte, eaddr) & TARGET_PAGE_MASK;
|
||||
}
|
102
target-ppc/mmu-hash32.h
Normal file
102
target-ppc/mmu-hash32.h
Normal file
@ -0,0 +1,102 @@
|
||||
#if !defined (__MMU_HASH32_H__)
|
||||
#define __MMU_HASH32_H__
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
||||
hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash);
|
||||
hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr);
|
||||
int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
|
||||
int mmu_idx);
|
||||
|
||||
/*
|
||||
* Segment register definitions
|
||||
*/
|
||||
|
||||
#define SR32_T 0x80000000
|
||||
#define SR32_KS 0x40000000
|
||||
#define SR32_KP 0x20000000
|
||||
#define SR32_NX 0x10000000
|
||||
#define SR32_VSID 0x00ffffff
|
||||
|
||||
/*
|
||||
* Block Address Translation (BAT) definitions
|
||||
*/
|
||||
|
||||
#define BATU32_BEPI 0xfffe0000
|
||||
#define BATU32_BL 0x00001ffc
|
||||
#define BATU32_VS 0x00000002
|
||||
#define BATU32_VP 0x00000001
|
||||
|
||||
|
||||
#define BATL32_BRPN 0xfffe0000
|
||||
#define BATL32_WIMG 0x00000078
|
||||
#define BATL32_PP 0x00000003
|
||||
|
||||
/* PowerPC 601 has slightly different BAT registers */
|
||||
|
||||
#define BATU32_601_KS 0x00000008
|
||||
#define BATU32_601_KP 0x00000004
|
||||
#define BATU32_601_PP 0x00000003
|
||||
|
||||
#define BATL32_601_V 0x00000040
|
||||
#define BATL32_601_BL 0x0000003f
|
||||
|
||||
/*
|
||||
* Hash page table definitions
|
||||
*/
|
||||
|
||||
#define HPTES_PER_GROUP 8
|
||||
#define HASH_PTE_SIZE_32 8
|
||||
#define HASH_PTEG_SIZE_32 (HASH_PTE_SIZE_32 * HPTES_PER_GROUP)
|
||||
|
||||
#define HPTE32_V_VALID 0x80000000
|
||||
#define HPTE32_V_VSID 0x7fffff80
|
||||
#define HPTE32_V_SECONDARY 0x00000040
|
||||
#define HPTE32_V_API 0x0000003f
|
||||
#define HPTE32_V_COMPARE(x, y) (!(((x) ^ (y)) & 0x7fffffbf))
|
||||
|
||||
#define HPTE32_R_RPN 0xfffff000
|
||||
#define HPTE32_R_R 0x00000100
|
||||
#define HPTE32_R_C 0x00000080
|
||||
#define HPTE32_R_W 0x00000040
|
||||
#define HPTE32_R_I 0x00000020
|
||||
#define HPTE32_R_M 0x00000010
|
||||
#define HPTE32_R_G 0x00000008
|
||||
#define HPTE32_R_WIMG 0x00000078
|
||||
#define HPTE32_R_PP 0x00000003
|
||||
|
||||
static inline target_ulong ppc_hash32_load_hpte0(CPUPPCState *env,
|
||||
hwaddr pte_offset)
|
||||
{
|
||||
assert(!env->external_htab); /* Not supported on 32-bit for now */
|
||||
return ldl_phys(env->htab_base + pte_offset);
|
||||
}
|
||||
|
||||
static inline target_ulong ppc_hash32_load_hpte1(CPUPPCState *env,
|
||||
hwaddr pte_offset)
|
||||
{
|
||||
assert(!env->external_htab); /* Not supported on 32-bit for now */
|
||||
return ldl_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_32/2);
|
||||
}
|
||||
|
||||
static inline void ppc_hash32_store_hpte0(CPUPPCState *env,
|
||||
hwaddr pte_offset, target_ulong pte0)
|
||||
{
|
||||
assert(!env->external_htab); /* Not supported on 32-bit for now */
|
||||
stl_phys(env->htab_base + pte_offset, pte0);
|
||||
}
|
||||
|
||||
static inline void ppc_hash32_store_hpte1(CPUPPCState *env,
|
||||
hwaddr pte_offset, target_ulong pte1)
|
||||
{
|
||||
assert(!env->external_htab); /* Not supported on 32-bit for now */
|
||||
stl_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_32/2, pte1);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t pte0, pte1;
|
||||
} ppc_hash_pte32_t;
|
||||
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
#endif /* __MMU_HASH32_H__ */
|
546
target-ppc/mmu-hash64.c
Normal file
546
target-ppc/mmu-hash64.c
Normal file
@ -0,0 +1,546 @@
|
||||
/*
|
||||
* PowerPC MMU, TLB, SLB and BAT emulation helpers for QEMU.
|
||||
*
|
||||
* Copyright (c) 2003-2007 Jocelyn Mayer
|
||||
* Copyright (c) 2013 David Gibson, 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 "cpu.h"
|
||||
#include "helper.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "kvm_ppc.h"
|
||||
#include "mmu-hash64.h"
|
||||
|
||||
//#define DEBUG_MMU
|
||||
//#define DEBUG_SLB
|
||||
|
||||
#ifdef DEBUG_MMU
|
||||
# define LOG_MMU(...) qemu_log(__VA_ARGS__)
|
||||
# define LOG_MMU_STATE(env) log_cpu_state((env), 0)
|
||||
#else
|
||||
# define LOG_MMU(...) do { } while (0)
|
||||
# define LOG_MMU_STATE(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_SLB
|
||||
# define LOG_SLB(...) qemu_log(__VA_ARGS__)
|
||||
#else
|
||||
# define LOG_SLB(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SLB handling
|
||||
*/
|
||||
|
||||
static ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr)
|
||||
{
|
||||
uint64_t esid_256M, esid_1T;
|
||||
int n;
|
||||
|
||||
LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr);
|
||||
|
||||
esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V;
|
||||
esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V;
|
||||
|
||||
for (n = 0; n < env->slb_nr; n++) {
|
||||
ppc_slb_t *slb = &env->slb[n];
|
||||
|
||||
LOG_SLB("%s: slot %d %016" PRIx64 " %016"
|
||||
PRIx64 "\n", __func__, n, slb->esid, slb->vsid);
|
||||
/* We check for 1T matches on all MMUs here - if the MMU
|
||||
* doesn't have 1T segment support, we will have prevented 1T
|
||||
* entries from being inserted in the slbmte code. */
|
||||
if (((slb->esid == esid_256M) &&
|
||||
((slb->vsid & SLB_VSID_B) == SLB_VSID_B_256M))
|
||||
|| ((slb->esid == esid_1T) &&
|
||||
((slb->vsid & SLB_VSID_B) == SLB_VSID_B_1T))) {
|
||||
return slb;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env)
|
||||
{
|
||||
int i;
|
||||
uint64_t slbe, slbv;
|
||||
|
||||
cpu_synchronize_state(env);
|
||||
|
||||
cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n");
|
||||
for (i = 0; i < env->slb_nr; i++) {
|
||||
slbe = env->slb[i].esid;
|
||||
slbv = env->slb[i].vsid;
|
||||
if (slbe == 0 && slbv == 0) {
|
||||
continue;
|
||||
}
|
||||
cpu_fprintf(f, "%d\t0x%016" PRIx64 "\t0x%016" PRIx64 "\n",
|
||||
i, slbe, slbv);
|
||||
}
|
||||
}
|
||||
|
||||
void helper_slbia(CPUPPCState *env)
|
||||
{
|
||||
int n, do_invalidate;
|
||||
|
||||
do_invalidate = 0;
|
||||
/* XXX: Warning: slbia never invalidates the first segment */
|
||||
for (n = 1; n < env->slb_nr; n++) {
|
||||
ppc_slb_t *slb = &env->slb[n];
|
||||
|
||||
if (slb->esid & SLB_ESID_V) {
|
||||
slb->esid &= ~SLB_ESID_V;
|
||||
/* XXX: given the fact that segment size is 256 MB or 1TB,
|
||||
* and we still don't have a tlb_flush_mask(env, n, mask)
|
||||
* in QEMU, we just invalidate all TLBs
|
||||
*/
|
||||
do_invalidate = 1;
|
||||
}
|
||||
}
|
||||
if (do_invalidate) {
|
||||
tlb_flush(env, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void helper_slbie(CPUPPCState *env, target_ulong addr)
|
||||
{
|
||||
ppc_slb_t *slb;
|
||||
|
||||
slb = slb_lookup(env, addr);
|
||||
if (!slb) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (slb->esid & SLB_ESID_V) {
|
||||
slb->esid &= ~SLB_ESID_V;
|
||||
|
||||
/* XXX: given the fact that segment size is 256 MB or 1TB,
|
||||
* and we still don't have a tlb_flush_mask(env, n, mask)
|
||||
* in QEMU, we just invalidate all TLBs
|
||||
*/
|
||||
tlb_flush(env, 1);
|
||||
}
|
||||
}
|
||||
|
||||
int ppc_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs)
|
||||
{
|
||||
int slot = rb & 0xfff;
|
||||
ppc_slb_t *slb = &env->slb[slot];
|
||||
|
||||
if (rb & (0x1000 - env->slb_nr)) {
|
||||
return -1; /* Reserved bits set or slot too high */
|
||||
}
|
||||
if (rs & (SLB_VSID_B & ~SLB_VSID_B_1T)) {
|
||||
return -1; /* Bad segment size */
|
||||
}
|
||||
if ((rs & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) {
|
||||
return -1; /* 1T segment on MMU that doesn't support it */
|
||||
}
|
||||
|
||||
/* Mask out the slot number as we store the entry */
|
||||
slb->esid = rb & (SLB_ESID_ESID | SLB_ESID_V);
|
||||
slb->vsid = rs;
|
||||
|
||||
LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64
|
||||
" %016" PRIx64 "\n", __func__, slot, rb, rs,
|
||||
slb->esid, slb->vsid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ppc_load_slb_esid(CPUPPCState *env, target_ulong rb,
|
||||
target_ulong *rt)
|
||||
{
|
||||
int slot = rb & 0xfff;
|
||||
ppc_slb_t *slb = &env->slb[slot];
|
||||
|
||||
if (slot >= env->slb_nr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*rt = slb->esid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ppc_load_slb_vsid(CPUPPCState *env, target_ulong rb,
|
||||
target_ulong *rt)
|
||||
{
|
||||
int slot = rb & 0xfff;
|
||||
ppc_slb_t *slb = &env->slb[slot];
|
||||
|
||||
if (slot >= env->slb_nr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*rt = slb->vsid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs)
|
||||
{
|
||||
if (ppc_store_slb(env, rb, rs) < 0) {
|
||||
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
|
||||
POWERPC_EXCP_INVAL);
|
||||
}
|
||||
}
|
||||
|
||||
target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb)
|
||||
{
|
||||
target_ulong rt = 0;
|
||||
|
||||
if (ppc_load_slb_esid(env, rb, &rt) < 0) {
|
||||
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
|
||||
POWERPC_EXCP_INVAL);
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
|
||||
target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb)
|
||||
{
|
||||
target_ulong rt = 0;
|
||||
|
||||
if (ppc_load_slb_vsid(env, rb, &rt) < 0) {
|
||||
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
|
||||
POWERPC_EXCP_INVAL);
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
|
||||
/*
|
||||
* 64-bit hash table MMU handling
|
||||
*/
|
||||
|
||||
static int ppc_hash64_pte_prot(CPUPPCState *env,
|
||||
ppc_slb_t *slb, ppc_hash_pte64_t pte)
|
||||
{
|
||||
unsigned pp, key;
|
||||
/* Some pp bit combinations have undefined behaviour, so default
|
||||
* to no access in those cases */
|
||||
int prot = 0;
|
||||
|
||||
key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP)
|
||||
: (slb->vsid & SLB_VSID_KS));
|
||||
pp = (pte.pte1 & HPTE64_R_PP) | ((pte.pte1 & HPTE64_R_PP0) >> 61);
|
||||
|
||||
if (key == 0) {
|
||||
switch (pp) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
prot = PAGE_READ | PAGE_WRITE;
|
||||
break;
|
||||
|
||||
case 0x3:
|
||||
case 0x6:
|
||||
prot = PAGE_READ;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (pp) {
|
||||
case 0x0:
|
||||
case 0x6:
|
||||
prot = 0;
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
case 0x3:
|
||||
prot = PAGE_READ;
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
prot = PAGE_READ | PAGE_WRITE;
|
||||
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;
|
||||
}
|
||||
|
||||
static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte)
|
||||
{
|
||||
int key, amrbits;
|
||||
int prot = PAGE_EXEC;
|
||||
|
||||
|
||||
/* Only recent MMUs implement Virtual Page Class Key Protection */
|
||||
if (!(env->mmu_model & POWERPC_MMU_AMR)) {
|
||||
return PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
}
|
||||
|
||||
key = HPTE64_R_KEY(pte.pte1);
|
||||
amrbits = (env->spr[SPR_AMR] >> 2*(31 - key)) & 0x3;
|
||||
|
||||
/* fprintf(stderr, "AMR protection: key=%d AMR=0x%" PRIx64 "\n", key, */
|
||||
/* env->spr[SPR_AMR]); */
|
||||
|
||||
if (amrbits & 0x2) {
|
||||
prot |= PAGE_WRITE;
|
||||
}
|
||||
if (amrbits & 0x1) {
|
||||
prot |= PAGE_READ;
|
||||
}
|
||||
|
||||
return prot;
|
||||
}
|
||||
|
||||
static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off,
|
||||
bool secondary, target_ulong ptem,
|
||||
ppc_hash_pte64_t *pte)
|
||||
{
|
||||
hwaddr pte_offset = pteg_off;
|
||||
target_ulong pte0, pte1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HPTES_PER_GROUP; i++) {
|
||||
pte0 = ppc_hash64_load_hpte0(env, pte_offset);
|
||||
pte1 = ppc_hash64_load_hpte1(env, pte_offset);
|
||||
|
||||
if ((pte0 & HPTE64_V_VALID)
|
||||
&& (secondary == !!(pte0 & HPTE64_V_SECONDARY))
|
||||
&& HPTE64_V_COMPARE(pte0, ptem)) {
|
||||
pte->pte0 = pte0;
|
||||
pte->pte1 = pte1;
|
||||
return pte_offset;
|
||||
}
|
||||
|
||||
pte_offset += HASH_PTE_SIZE_64;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env,
|
||||
ppc_slb_t *slb, target_ulong eaddr,
|
||||
ppc_hash_pte64_t *pte)
|
||||
{
|
||||
hwaddr pteg_off, pte_offset;
|
||||
hwaddr hash;
|
||||
uint64_t vsid, epnshift, epnmask, epn, ptem;
|
||||
|
||||
/* Page size according to the SLB, which we use to generate the
|
||||
* EPN for hash table lookup.. When we implement more recent MMU
|
||||
* extensions this might be different from the actual page size
|
||||
* encoded in the PTE */
|
||||
epnshift = (slb->vsid & SLB_VSID_L)
|
||||
? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS;
|
||||
epnmask = ~((1ULL << epnshift) - 1);
|
||||
|
||||
if (slb->vsid & SLB_VSID_B) {
|
||||
/* 1TB segment */
|
||||
vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T;
|
||||
epn = (eaddr & ~SEGMENT_MASK_1T) & epnmask;
|
||||
hash = vsid ^ (vsid << 25) ^ (epn >> epnshift);
|
||||
} else {
|
||||
/* 256M segment */
|
||||
vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
|
||||
epn = (eaddr & ~SEGMENT_MASK_256M) & epnmask;
|
||||
hash = vsid ^ (epn >> epnshift);
|
||||
}
|
||||
ptem = (slb->vsid & SLB_VSID_PTEM) | ((epn >> 16) & HPTE64_V_AVPN);
|
||||
|
||||
/* Page address translation */
|
||||
LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
|
||||
" hash " TARGET_FMT_plx "\n",
|
||||
env->htab_base, env->htab_mask, hash);
|
||||
|
||||
/* Primary PTEG lookup */
|
||||
LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
|
||||
" vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx
|
||||
" hash=" TARGET_FMT_plx "\n",
|
||||
env->htab_base, env->htab_mask, vsid, ptem, hash);
|
||||
pteg_off = (hash * HASH_PTEG_SIZE_64) & env->htab_mask;
|
||||
pte_offset = ppc_hash64_pteg_search(env, pteg_off, 0, ptem, pte);
|
||||
|
||||
if (pte_offset == -1) {
|
||||
/* Secondary PTEG lookup */
|
||||
LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
|
||||
" vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
|
||||
" hash=" TARGET_FMT_plx "\n", env->htab_base,
|
||||
env->htab_mask, vsid, ptem, ~hash);
|
||||
|
||||
pteg_off = (~hash * HASH_PTEG_SIZE_64) & env->htab_mask;
|
||||
pte_offset = ppc_hash64_pteg_search(env, pteg_off, 1, ptem, pte);
|
||||
}
|
||||
|
||||
return pte_offset;
|
||||
}
|
||||
|
||||
static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte,
|
||||
target_ulong eaddr)
|
||||
{
|
||||
hwaddr rpn = pte.pte1 & HPTE64_R_RPN;
|
||||
/* FIXME: Add support for SLLP extended page sizes */
|
||||
int target_page_bits = (slb->vsid & SLB_VSID_L)
|
||||
? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS;
|
||||
hwaddr mask = (1ULL << target_page_bits) - 1;
|
||||
|
||||
return (rpn & ~mask) | (eaddr & mask);
|
||||
}
|
||||
|
||||
int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr,
|
||||
int rwx, int mmu_idx)
|
||||
{
|
||||
ppc_slb_t *slb;
|
||||
hwaddr pte_offset;
|
||||
ppc_hash_pte64_t pte;
|
||||
int pp_prot, amr_prot, prot;
|
||||
uint64_t new_pte1;
|
||||
const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC};
|
||||
hwaddr raddr;
|
||||
|
||||
assert((rwx == 0) || (rwx == 1) || (rwx == 2));
|
||||
|
||||
/* 1. Handle real mode accesses */
|
||||
if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
|
||||
/* Translation is off */
|
||||
/* In real mode the top 4 effective address bits are ignored */
|
||||
raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
|
||||
tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
|
||||
PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
|
||||
TARGET_PAGE_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 2. Translation is on, so look up the SLB */
|
||||
slb = slb_lookup(env, eaddr);
|
||||
|
||||
if (!slb) {
|
||||
if (rwx == 2) {
|
||||
env->exception_index = POWERPC_EXCP_ISEG;
|
||||
env->error_code = 0;
|
||||
} else {
|
||||
env->exception_index = POWERPC_EXCP_DSEG;
|
||||
env->error_code = 0;
|
||||
env->spr[SPR_DAR] = eaddr;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 3. Check for segment level no-execute violation */
|
||||
if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) {
|
||||
env->exception_index = POWERPC_EXCP_ISI;
|
||||
env->error_code = 0x10000000;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 4. Locate the PTE in the hash table */
|
||||
pte_offset = ppc_hash64_htab_lookup(env, slb, eaddr, &pte);
|
||||
if (pte_offset == -1) {
|
||||
if (rwx == 2) {
|
||||
env->exception_index = POWERPC_EXCP_ISI;
|
||||
env->error_code = 0x40000000;
|
||||
} else {
|
||||
env->exception_index = POWERPC_EXCP_DSI;
|
||||
env->error_code = 0;
|
||||
env->spr[SPR_DAR] = eaddr;
|
||||
if (rwx == 1) {
|
||||
env->spr[SPR_DSISR] = 0x42000000;
|
||||
} else {
|
||||
env->spr[SPR_DSISR] = 0x40000000;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
|
||||
|
||||
/* 5. Check access permissions */
|
||||
|
||||
pp_prot = ppc_hash64_pte_prot(env, slb, pte);
|
||||
amr_prot = ppc_hash64_amr_prot(env, pte);
|
||||
prot = pp_prot & amr_prot;
|
||||
|
||||
if ((need_prot[rwx] & ~prot) != 0) {
|
||||
/* Access right violation */
|
||||
LOG_MMU("PTE access rejected\n");
|
||||
if (rwx == 2) {
|
||||
env->exception_index = POWERPC_EXCP_ISI;
|
||||
env->error_code = 0x08000000;
|
||||
} else {
|
||||
target_ulong dsisr = 0;
|
||||
|
||||
env->exception_index = POWERPC_EXCP_DSI;
|
||||
env->error_code = 0;
|
||||
env->spr[SPR_DAR] = eaddr;
|
||||
if (need_prot[rwx] & ~pp_prot) {
|
||||
dsisr |= 0x08000000;
|
||||
}
|
||||
if (rwx == 1) {
|
||||
dsisr |= 0x02000000;
|
||||
}
|
||||
if (need_prot[rwx] & ~amr_prot) {
|
||||
dsisr |= 0x00200000;
|
||||
}
|
||||
env->spr[SPR_DSISR] = dsisr;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOG_MMU("PTE access granted !\n");
|
||||
|
||||
/* 6. Update PTE referenced and changed bits if necessary */
|
||||
|
||||
new_pte1 = pte.pte1 | HPTE64_R_R; /* set referenced bit */
|
||||
if (rwx == 1) {
|
||||
new_pte1 |= HPTE64_R_C; /* set changed (dirty) bit */
|
||||
} else {
|
||||
/* Treat the page as read-only for now, so that a later write
|
||||
* will pass through this function again to set the C bit */
|
||||
prot &= ~PAGE_WRITE;
|
||||
}
|
||||
|
||||
if (new_pte1 != pte.pte1) {
|
||||
ppc_hash64_store_hpte1(env, pte_offset, new_pte1);
|
||||
}
|
||||
|
||||
/* 7. Determine the real address from the PTE */
|
||||
|
||||
raddr = ppc_hash64_pte_raddr(slb, pte, eaddr);
|
||||
|
||||
tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
|
||||
prot, mmu_idx, TARGET_PAGE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr)
|
||||
{
|
||||
ppc_slb_t *slb;
|
||||
hwaddr pte_offset;
|
||||
ppc_hash_pte64_t pte;
|
||||
|
||||
if (msr_dr == 0) {
|
||||
/* In real mode the top 4 effective address bits are ignored */
|
||||
return addr & 0x0FFFFFFFFFFFFFFFULL;
|
||||
}
|
||||
|
||||
slb = slb_lookup(env, addr);
|
||||
if (!slb) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pte_offset = ppc_hash64_htab_lookup(env, slb, addr, &pte);
|
||||
if (pte_offset == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ppc_hash64_pte_raddr(slb, pte, addr) & TARGET_PAGE_MASK;
|
||||
}
|
124
target-ppc/mmu-hash64.h
Normal file
124
target-ppc/mmu-hash64.h
Normal file
@ -0,0 +1,124 @@
|
||||
#if !defined (__MMU_HASH64_H__)
|
||||
#define __MMU_HASH64_H__
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
||||
#ifdef TARGET_PPC64
|
||||
void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env);
|
||||
int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
|
||||
hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr);
|
||||
int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
|
||||
int mmu_idx);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SLB definitions
|
||||
*/
|
||||
|
||||
/* Bits in the SLB ESID word */
|
||||
#define SLB_ESID_ESID 0xFFFFFFFFF0000000ULL
|
||||
#define SLB_ESID_V 0x0000000008000000ULL /* valid */
|
||||
|
||||
/* Bits in the SLB VSID word */
|
||||
#define SLB_VSID_SHIFT 12
|
||||
#define SLB_VSID_SHIFT_1T 24
|
||||
#define SLB_VSID_SSIZE_SHIFT 62
|
||||
#define SLB_VSID_B 0xc000000000000000ULL
|
||||
#define SLB_VSID_B_256M 0x0000000000000000ULL
|
||||
#define SLB_VSID_B_1T 0x4000000000000000ULL
|
||||
#define SLB_VSID_VSID 0x3FFFFFFFFFFFF000ULL
|
||||
#define SLB_VSID_PTEM (SLB_VSID_B | SLB_VSID_VSID)
|
||||
#define SLB_VSID_KS 0x0000000000000800ULL
|
||||
#define SLB_VSID_KP 0x0000000000000400ULL
|
||||
#define SLB_VSID_N 0x0000000000000200ULL /* no-execute */
|
||||
#define SLB_VSID_L 0x0000000000000100ULL
|
||||
#define SLB_VSID_C 0x0000000000000080ULL /* class */
|
||||
#define SLB_VSID_LP 0x0000000000000030ULL
|
||||
#define SLB_VSID_ATTR 0x0000000000000FFFULL
|
||||
|
||||
/*
|
||||
* Hash page table definitions
|
||||
*/
|
||||
|
||||
#define HPTES_PER_GROUP 8
|
||||
#define HASH_PTE_SIZE_64 16
|
||||
#define HASH_PTEG_SIZE_64 (HASH_PTE_SIZE_64 * HPTES_PER_GROUP)
|
||||
|
||||
#define HPTE64_V_SSIZE_SHIFT 62
|
||||
#define HPTE64_V_AVPN_SHIFT 7
|
||||
#define HPTE64_V_AVPN 0x3fffffffffffff80ULL
|
||||
#define HPTE64_V_AVPN_VAL(x) (((x) & HPTE64_V_AVPN) >> HPTE64_V_AVPN_SHIFT)
|
||||
#define HPTE64_V_COMPARE(x, y) (!(((x) ^ (y)) & 0xffffffffffffff80ULL))
|
||||
#define HPTE64_V_LARGE 0x0000000000000004ULL
|
||||
#define HPTE64_V_SECONDARY 0x0000000000000002ULL
|
||||
#define HPTE64_V_VALID 0x0000000000000001ULL
|
||||
|
||||
#define HPTE64_R_PP0 0x8000000000000000ULL
|
||||
#define HPTE64_R_TS 0x4000000000000000ULL
|
||||
#define HPTE64_R_KEY_HI 0x3000000000000000ULL
|
||||
#define HPTE64_R_RPN_SHIFT 12
|
||||
#define HPTE64_R_RPN 0x0ffffffffffff000ULL
|
||||
#define HPTE64_R_FLAGS 0x00000000000003ffULL
|
||||
#define HPTE64_R_PP 0x0000000000000003ULL
|
||||
#define HPTE64_R_N 0x0000000000000004ULL
|
||||
#define HPTE64_R_G 0x0000000000000008ULL
|
||||
#define HPTE64_R_M 0x0000000000000010ULL
|
||||
#define HPTE64_R_I 0x0000000000000020ULL
|
||||
#define HPTE64_R_W 0x0000000000000040ULL
|
||||
#define HPTE64_R_WIMG 0x0000000000000078ULL
|
||||
#define HPTE64_R_C 0x0000000000000080ULL
|
||||
#define HPTE64_R_R 0x0000000000000100ULL
|
||||
#define HPTE64_R_KEY_LO 0x0000000000000e00ULL
|
||||
#define HPTE64_R_KEY(x) ((((x) & HPTE64_R_KEY_HI) >> 60) | \
|
||||
(((x) & HPTE64_R_KEY_LO) >> 9))
|
||||
|
||||
#define HPTE64_V_1TB_SEG 0x4000000000000000ULL
|
||||
#define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL
|
||||
|
||||
static inline target_ulong ppc_hash64_load_hpte0(CPUPPCState *env,
|
||||
hwaddr pte_offset)
|
||||
{
|
||||
if (env->external_htab) {
|
||||
return ldq_p(env->external_htab + pte_offset);
|
||||
} else {
|
||||
return ldq_phys(env->htab_base + pte_offset);
|
||||
}
|
||||
}
|
||||
|
||||
static inline target_ulong ppc_hash64_load_hpte1(CPUPPCState *env,
|
||||
hwaddr pte_offset)
|
||||
{
|
||||
if (env->external_htab) {
|
||||
return ldq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2);
|
||||
} else {
|
||||
return ldq_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_64/2);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ppc_hash64_store_hpte0(CPUPPCState *env,
|
||||
hwaddr pte_offset, target_ulong pte0)
|
||||
{
|
||||
if (env->external_htab) {
|
||||
stq_p(env->external_htab + pte_offset, pte0);
|
||||
} else {
|
||||
stq_phys(env->htab_base + pte_offset, pte0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ppc_hash64_store_hpte1(CPUPPCState *env,
|
||||
hwaddr pte_offset, target_ulong pte1)
|
||||
{
|
||||
if (env->external_htab) {
|
||||
stq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2, pte1);
|
||||
} else {
|
||||
stq_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_64/2, pte1);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint64_t pte0, pte1;
|
||||
} ppc_hash_pte64_t;
|
||||
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
#endif /* !defined (__MMU_HASH64_H__) */
|
File diff suppressed because it is too large
Load Diff
@ -204,6 +204,13 @@ typedef struct DisasContext {
|
||||
int singlestep_enabled;
|
||||
} DisasContext;
|
||||
|
||||
/* True when active word size < size of target_long. */
|
||||
#ifdef TARGET_PPC64
|
||||
# define NARROW_MODE(C) (!(C)->sf_mode)
|
||||
#else
|
||||
# define NARROW_MODE(C) 0
|
||||
#endif
|
||||
|
||||
struct opc_handler_t {
|
||||
/* invalid bits for instruction 1 (Rc(opcode) == 0) */
|
||||
uint32_t inval1;
|
||||
@ -260,12 +267,10 @@ static inline void gen_set_access_type(DisasContext *ctx, int access_type)
|
||||
|
||||
static inline void gen_update_nip(DisasContext *ctx, target_ulong nip)
|
||||
{
|
||||
#if defined(TARGET_PPC64)
|
||||
if (ctx->sf_mode)
|
||||
tcg_gen_movi_tl(cpu_nip, nip);
|
||||
else
|
||||
#endif
|
||||
tcg_gen_movi_tl(cpu_nip, (uint32_t)nip);
|
||||
if (NARROW_MODE(ctx)) {
|
||||
nip = (uint32_t)nip;
|
||||
}
|
||||
tcg_gen_movi_tl(cpu_nip, nip);
|
||||
}
|
||||
|
||||
static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error)
|
||||
@ -627,7 +632,6 @@ static inline void gen_op_cmpi(TCGv arg0, target_ulong arg1, int s, int crf)
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
static inline void gen_op_cmp32(TCGv arg0, TCGv arg1, int s, int crf)
|
||||
{
|
||||
TCGv t0, t1;
|
||||
@ -651,68 +655,62 @@ static inline void gen_op_cmpi32(TCGv arg0, target_ulong arg1, int s, int crf)
|
||||
gen_op_cmp32(arg0, t0, s, crf);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void gen_set_Rc0(DisasContext *ctx, TCGv reg)
|
||||
{
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!(ctx->sf_mode))
|
||||
if (NARROW_MODE(ctx)) {
|
||||
gen_op_cmpi32(reg, 0, 1, 0);
|
||||
else
|
||||
#endif
|
||||
} else {
|
||||
gen_op_cmpi(reg, 0, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* cmp */
|
||||
static void gen_cmp(DisasContext *ctx)
|
||||
{
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!(ctx->sf_mode && (ctx->opcode & 0x00200000)))
|
||||
if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) {
|
||||
gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
|
||||
1, crfD(ctx->opcode));
|
||||
else
|
||||
#endif
|
||||
} else {
|
||||
gen_op_cmp(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
|
||||
1, crfD(ctx->opcode));
|
||||
}
|
||||
}
|
||||
|
||||
/* cmpi */
|
||||
static void gen_cmpi(DisasContext *ctx)
|
||||
{
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!(ctx->sf_mode && (ctx->opcode & 0x00200000)))
|
||||
if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) {
|
||||
gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode),
|
||||
1, crfD(ctx->opcode));
|
||||
else
|
||||
#endif
|
||||
} else {
|
||||
gen_op_cmpi(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode),
|
||||
1, crfD(ctx->opcode));
|
||||
}
|
||||
}
|
||||
|
||||
/* cmpl */
|
||||
static void gen_cmpl(DisasContext *ctx)
|
||||
{
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!(ctx->sf_mode && (ctx->opcode & 0x00200000)))
|
||||
if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) {
|
||||
gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
|
||||
0, crfD(ctx->opcode));
|
||||
else
|
||||
#endif
|
||||
} else {
|
||||
gen_op_cmp(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
|
||||
0, crfD(ctx->opcode));
|
||||
}
|
||||
}
|
||||
|
||||
/* cmpli */
|
||||
static void gen_cmpli(DisasContext *ctx)
|
||||
{
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!(ctx->sf_mode && (ctx->opcode & 0x00200000)))
|
||||
if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) {
|
||||
gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode),
|
||||
0, crfD(ctx->opcode));
|
||||
else
|
||||
#endif
|
||||
} else {
|
||||
gen_op_cmpi(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode),
|
||||
0, crfD(ctx->opcode));
|
||||
}
|
||||
}
|
||||
|
||||
/* isel (PowerPC 2.03 specification) */
|
||||
@ -756,11 +754,9 @@ static inline void gen_op_arith_compute_ov(DisasContext *ctx, TCGv arg0,
|
||||
tcg_gen_andc_tl(cpu_ov, cpu_ov, t0);
|
||||
}
|
||||
tcg_temp_free(t0);
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!ctx->sf_mode) {
|
||||
if (NARROW_MODE(ctx)) {
|
||||
tcg_gen_ext32s_tl(cpu_ov, cpu_ov);
|
||||
}
|
||||
#endif
|
||||
tcg_gen_shri_tl(cpu_ov, cpu_ov, TARGET_LONG_BITS - 1);
|
||||
tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov);
|
||||
}
|
||||
@ -778,14 +774,26 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1,
|
||||
}
|
||||
|
||||
if (compute_ca) {
|
||||
TCGv zero = tcg_const_tl(0);
|
||||
if (add_ca) {
|
||||
tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, cpu_ca, zero);
|
||||
tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, arg2, zero);
|
||||
if (NARROW_MODE(ctx)) {
|
||||
TCGv t1 = tcg_temp_new();
|
||||
tcg_gen_ext32u_tl(t1, arg2);
|
||||
tcg_gen_ext32u_tl(t0, arg1);
|
||||
tcg_gen_add_tl(t0, t0, t1);
|
||||
tcg_temp_free(t1);
|
||||
if (add_ca) {
|
||||
tcg_gen_add_tl(t0, t0, cpu_ca);
|
||||
}
|
||||
tcg_gen_shri_tl(cpu_ca, t0, 32);
|
||||
} else {
|
||||
tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, arg2, zero);
|
||||
TCGv zero = tcg_const_tl(0);
|
||||
if (add_ca) {
|
||||
tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, cpu_ca, zero);
|
||||
tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, arg2, zero);
|
||||
} else {
|
||||
tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, arg2, zero);
|
||||
}
|
||||
tcg_temp_free(zero);
|
||||
}
|
||||
tcg_temp_free(zero);
|
||||
} else {
|
||||
tcg_gen_add_tl(t0, arg1, arg2);
|
||||
if (add_ca) {
|
||||
@ -1114,14 +1122,25 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1,
|
||||
{
|
||||
TCGv t0 = ret;
|
||||
|
||||
if (((add_ca && compute_ca) || compute_ov)
|
||||
&& (TCGV_EQUAL(ret, arg1) || TCGV_EQUAL(ret, arg2))) {
|
||||
if (compute_ov && (TCGV_EQUAL(ret, arg1) || TCGV_EQUAL(ret, arg2))) {
|
||||
t0 = tcg_temp_new();
|
||||
}
|
||||
|
||||
if (add_ca) {
|
||||
/* dest = ~arg1 + arg2 + ca. */
|
||||
if (compute_ca) {
|
||||
if (compute_ca) {
|
||||
/* dest = ~arg1 + arg2 [+ ca]. */
|
||||
if (NARROW_MODE(ctx)) {
|
||||
TCGv inv1 = tcg_temp_new();
|
||||
tcg_gen_not_tl(inv1, arg1);
|
||||
tcg_gen_ext32u_tl(t0, arg2);
|
||||
tcg_gen_ext32u_tl(inv1, inv1);
|
||||
if (add_ca) {
|
||||
tcg_gen_add_tl(t0, t0, cpu_ca);
|
||||
} else {
|
||||
tcg_gen_addi_tl(t0, t0, 1);
|
||||
}
|
||||
tcg_gen_add_tl(t0, t0, inv1);
|
||||
tcg_gen_shri_tl(cpu_ca, t0, 32);
|
||||
} else if (add_ca) {
|
||||
TCGv zero, inv1 = tcg_temp_new();
|
||||
tcg_gen_not_tl(inv1, arg1);
|
||||
zero = tcg_const_tl(0);
|
||||
@ -1130,14 +1149,16 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1,
|
||||
tcg_temp_free(zero);
|
||||
tcg_temp_free(inv1);
|
||||
} else {
|
||||
tcg_gen_sub_tl(t0, arg2, arg1);
|
||||
tcg_gen_add_tl(t0, t0, cpu_ca);
|
||||
tcg_gen_subi_tl(t0, t0, 1);
|
||||
}
|
||||
} else {
|
||||
if (compute_ca) {
|
||||
tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1);
|
||||
tcg_gen_sub_tl(t0, arg2, arg1);
|
||||
}
|
||||
} else if (add_ca) {
|
||||
/* Since we're ignoring carry-out, we can simplify the
|
||||
standard ~arg1 + arg2 + ca to arg2 - arg1 + ca - 1. */
|
||||
tcg_gen_sub_tl(t0, arg2, arg1);
|
||||
tcg_gen_add_tl(t0, t0, cpu_ca);
|
||||
tcg_gen_subi_tl(t0, t0, 1);
|
||||
} else {
|
||||
tcg_gen_sub_tl(t0, arg2, arg1);
|
||||
}
|
||||
|
||||
@ -2311,45 +2332,37 @@ static inline void gen_addr_imm_index(DisasContext *ctx, TCGv EA,
|
||||
|
||||
simm &= ~maskl;
|
||||
if (rA(ctx->opcode) == 0) {
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!ctx->sf_mode) {
|
||||
tcg_gen_movi_tl(EA, (uint32_t)simm);
|
||||
} else
|
||||
#endif
|
||||
if (NARROW_MODE(ctx)) {
|
||||
simm = (uint32_t)simm;
|
||||
}
|
||||
tcg_gen_movi_tl(EA, simm);
|
||||
} else if (likely(simm != 0)) {
|
||||
tcg_gen_addi_tl(EA, cpu_gpr[rA(ctx->opcode)], simm);
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!ctx->sf_mode) {
|
||||
if (NARROW_MODE(ctx)) {
|
||||
tcg_gen_ext32u_tl(EA, EA);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!ctx->sf_mode) {
|
||||
if (NARROW_MODE(ctx)) {
|
||||
tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]);
|
||||
} else
|
||||
#endif
|
||||
tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]);
|
||||
} else {
|
||||
tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void gen_addr_reg_index(DisasContext *ctx, TCGv EA)
|
||||
{
|
||||
if (rA(ctx->opcode) == 0) {
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!ctx->sf_mode) {
|
||||
if (NARROW_MODE(ctx)) {
|
||||
tcg_gen_ext32u_tl(EA, cpu_gpr[rB(ctx->opcode)]);
|
||||
} else
|
||||
#endif
|
||||
tcg_gen_mov_tl(EA, cpu_gpr[rB(ctx->opcode)]);
|
||||
} else {
|
||||
tcg_gen_mov_tl(EA, cpu_gpr[rB(ctx->opcode)]);
|
||||
}
|
||||
} else {
|
||||
tcg_gen_add_tl(EA, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!ctx->sf_mode) {
|
||||
if (NARROW_MODE(ctx)) {
|
||||
tcg_gen_ext32u_tl(EA, EA);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -2357,13 +2370,10 @@ static inline void gen_addr_register(DisasContext *ctx, TCGv EA)
|
||||
{
|
||||
if (rA(ctx->opcode) == 0) {
|
||||
tcg_gen_movi_tl(EA, 0);
|
||||
} else if (NARROW_MODE(ctx)) {
|
||||
tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]);
|
||||
} else {
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!ctx->sf_mode) {
|
||||
tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]);
|
||||
} else
|
||||
#endif
|
||||
tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]);
|
||||
tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2371,11 +2381,9 @@ static inline void gen_addr_add(DisasContext *ctx, TCGv ret, TCGv arg1,
|
||||
target_long val)
|
||||
{
|
||||
tcg_gen_addi_tl(ret, arg1, val);
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!ctx->sf_mode) {
|
||||
if (NARROW_MODE(ctx)) {
|
||||
tcg_gen_ext32u_tl(ret, ret);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void gen_check_align(DisasContext *ctx, TCGv EA, int mask)
|
||||
@ -3320,10 +3328,9 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
tb = ctx->tb;
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!ctx->sf_mode)
|
||||
if (NARROW_MODE(ctx)) {
|
||||
dest = (uint32_t) dest;
|
||||
#endif
|
||||
}
|
||||
if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) &&
|
||||
likely(!ctx->singlestep_enabled)) {
|
||||
tcg_gen_goto_tb(n);
|
||||
@ -3351,12 +3358,10 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
|
||||
|
||||
static inline void gen_setlr(DisasContext *ctx, target_ulong nip)
|
||||
{
|
||||
#if defined(TARGET_PPC64)
|
||||
if (ctx->sf_mode == 0)
|
||||
tcg_gen_movi_tl(cpu_lr, (uint32_t)nip);
|
||||
else
|
||||
#endif
|
||||
tcg_gen_movi_tl(cpu_lr, nip);
|
||||
if (NARROW_MODE(ctx)) {
|
||||
nip = (uint32_t)nip;
|
||||
}
|
||||
tcg_gen_movi_tl(cpu_lr, nip);
|
||||
}
|
||||
|
||||
/* b ba bl bla */
|
||||
@ -3366,18 +3371,16 @@ static void gen_b(DisasContext *ctx)
|
||||
|
||||
ctx->exception = POWERPC_EXCP_BRANCH;
|
||||
/* sign extend LI */
|
||||
#if defined(TARGET_PPC64)
|
||||
if (ctx->sf_mode)
|
||||
li = ((int64_t)LI(ctx->opcode) << 38) >> 38;
|
||||
else
|
||||
#endif
|
||||
li = ((int32_t)LI(ctx->opcode) << 6) >> 6;
|
||||
if (likely(AA(ctx->opcode) == 0))
|
||||
li = LI(ctx->opcode);
|
||||
li = (li ^ 0x02000000) - 0x02000000;
|
||||
if (likely(AA(ctx->opcode) == 0)) {
|
||||
target = ctx->nip + li - 4;
|
||||
else
|
||||
} else {
|
||||
target = li;
|
||||
if (LK(ctx->opcode))
|
||||
}
|
||||
if (LK(ctx->opcode)) {
|
||||
gen_setlr(ctx, ctx->nip);
|
||||
}
|
||||
gen_update_cfar(ctx, ctx->nip);
|
||||
gen_goto_tb(ctx, 0, target);
|
||||
}
|
||||
@ -3413,12 +3416,11 @@ static inline void gen_bcond(DisasContext *ctx, int type)
|
||||
return;
|
||||
}
|
||||
tcg_gen_subi_tl(cpu_ctr, cpu_ctr, 1);
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!ctx->sf_mode)
|
||||
if (NARROW_MODE(ctx)) {
|
||||
tcg_gen_ext32u_tl(temp, cpu_ctr);
|
||||
else
|
||||
#endif
|
||||
} else {
|
||||
tcg_gen_mov_tl(temp, cpu_ctr);
|
||||
}
|
||||
if (bo & 0x2) {
|
||||
tcg_gen_brcondi_tl(TCG_COND_NE, temp, 0, l1);
|
||||
} else {
|
||||
@ -3452,20 +3454,14 @@ static inline void gen_bcond(DisasContext *ctx, int type)
|
||||
gen_set_label(l1);
|
||||
gen_goto_tb(ctx, 1, ctx->nip);
|
||||
} else {
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!(ctx->sf_mode))
|
||||
if (NARROW_MODE(ctx)) {
|
||||
tcg_gen_andi_tl(cpu_nip, target, (uint32_t)~3);
|
||||
else
|
||||
#endif
|
||||
} else {
|
||||
tcg_gen_andi_tl(cpu_nip, target, ~3);
|
||||
}
|
||||
tcg_gen_exit_tb(0);
|
||||
gen_set_label(l1);
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!(ctx->sf_mode))
|
||||
tcg_gen_movi_tl(cpu_nip, (uint32_t)ctx->nip);
|
||||
else
|
||||
#endif
|
||||
tcg_gen_movi_tl(cpu_nip, ctx->nip);
|
||||
gen_update_nip(ctx, ctx->nip);
|
||||
tcg_gen_exit_tb(0);
|
||||
}
|
||||
}
|
||||
@ -4324,15 +4320,14 @@ static void gen_tlbie(DisasContext *ctx)
|
||||
gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
|
||||
return;
|
||||
}
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!ctx->sf_mode) {
|
||||
if (NARROW_MODE(ctx)) {
|
||||
TCGv t0 = tcg_temp_new();
|
||||
tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]);
|
||||
gen_helper_tlbie(cpu_env, t0);
|
||||
tcg_temp_free(t0);
|
||||
} else
|
||||
#endif
|
||||
} else {
|
||||
gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -7577,11 +7572,9 @@ static inline void gen_addr_spe_imm_index(DisasContext *ctx, TCGv EA, int sh)
|
||||
tcg_gen_movi_tl(EA, uimm << sh);
|
||||
} else {
|
||||
tcg_gen_addi_tl(EA, cpu_gpr[rA(ctx->opcode)], uimm << sh);
|
||||
#if defined(TARGET_PPC64)
|
||||
if (!ctx->sf_mode) {
|
||||
if (NARROW_MODE(ctx)) {
|
||||
tcg_gen_ext32u_tl(EA, EA);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -9428,7 +9421,6 @@ void cpu_dump_state (CPUPPCState *env, FILE *f, fprintf_function cpu_fprintf,
|
||||
case POWERPC_MMU_SOFT_6xx:
|
||||
case POWERPC_MMU_SOFT_74xx:
|
||||
#if defined(TARGET_PPC64)
|
||||
case POWERPC_MMU_620:
|
||||
case POWERPC_MMU_64B:
|
||||
#endif
|
||||
cpu_fprintf(f, " SDR1 " TARGET_FMT_lx "\n", env->spr[SPR_SDR1]);
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "cpu-models.h"
|
||||
#include "mmu-hash32.h"
|
||||
#include "mmu-hash64.h"
|
||||
|
||||
//#define PPC_DUMP_CPU
|
||||
//#define PPC_DEBUG_SPR
|
||||
@ -365,7 +367,6 @@ static void spr_write_sdr1 (void *opaque, int sprn, int gprn)
|
||||
}
|
||||
|
||||
/* 64 bits PowerPC specific SPRs */
|
||||
/* ASR */
|
||||
#if defined(TARGET_PPC64)
|
||||
static void spr_read_hior (void *opaque, int gprn, int sprn)
|
||||
{
|
||||
@ -379,16 +380,6 @@ static void spr_write_hior (void *opaque, int sprn, int gprn)
|
||||
tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix));
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void spr_read_asr (void *opaque, int gprn, int sprn)
|
||||
{
|
||||
tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, asr));
|
||||
}
|
||||
|
||||
static void spr_write_asr (void *opaque, int sprn, int gprn)
|
||||
{
|
||||
gen_helper_store_asr(cpu_env, cpu_gpr[gprn]);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -1028,6 +1019,54 @@ static void gen_spr_7xx (CPUPPCState *env)
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
#ifdef TARGET_PPC64
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static void spr_read_uamr (void *opaque, int gprn, int sprn)
|
||||
{
|
||||
gen_load_spr(cpu_gpr[gprn], SPR_AMR);
|
||||
spr_load_dump_spr(SPR_AMR);
|
||||
}
|
||||
|
||||
static void spr_write_uamr (void *opaque, int sprn, int gprn)
|
||||
{
|
||||
gen_store_spr(SPR_AMR, cpu_gpr[gprn]);
|
||||
spr_store_dump_spr(SPR_AMR);
|
||||
}
|
||||
|
||||
static void spr_write_uamr_pr (void *opaque, int sprn, int gprn)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
|
||||
gen_load_spr(t0, SPR_UAMOR);
|
||||
tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]);
|
||||
gen_store_spr(SPR_AMR, t0);
|
||||
spr_store_dump_spr(SPR_AMR);
|
||||
}
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
static void gen_spr_amr (CPUPPCState *env)
|
||||
{
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
/* Virtual Page Class Key protection */
|
||||
/* The AMR is accessible either via SPR 13 or SPR 29. 13 is
|
||||
* userspace accessible, 29 is privileged. So we only need to set
|
||||
* the kvm ONE_REG id on one of them, we use 29 */
|
||||
spr_register(env, SPR_UAMR, "UAMR",
|
||||
&spr_read_uamr, &spr_write_uamr_pr,
|
||||
&spr_read_uamr, &spr_write_uamr,
|
||||
0);
|
||||
spr_register_kvm(env, SPR_AMR, "AMR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
KVM_REG_PPC_AMR, 0xffffffffffffffffULL);
|
||||
spr_register_kvm(env, SPR_UAMOR, "UAMOR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
KVM_REG_PPC_UAMOR, 0);
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
}
|
||||
#endif /* TARGET_PPC64 */
|
||||
|
||||
static void gen_spr_thrm (CPUPPCState *env)
|
||||
{
|
||||
/* Thermal management */
|
||||
@ -2151,173 +2190,6 @@ static void gen_spr_compress (CPUPPCState *env)
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
#if defined (TARGET_PPC64)
|
||||
/* SPR specific to PowerPC 620 */
|
||||
static void gen_spr_620 (CPUPPCState *env)
|
||||
{
|
||||
/* Processor identification */
|
||||
spr_register(env, SPR_PIR, "PIR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_pir,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_ASR, "ASR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_asr, &spr_write_asr,
|
||||
0x00000000);
|
||||
/* Breakpoints */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_IABR, "IABR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_DABR, "DABR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_SIAR, "SIAR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_SDA, "SDA",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMC1R, "PMC1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_620_PMC1W, "PMC1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMC2R, "PMC2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_620_PMC2W, "PMC2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_MMCR0R, "MMCR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_620_MMCR0W, "MMCR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* External access control */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_EAR, "EAR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
#if 0 // XXX: check this
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMR0, "PMR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMR1, "PMR1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMR2, "PMR2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMR3, "PMR3",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMR4, "PMR4",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMR5, "PMR5",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMR6, "PMR6",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMR7, "PMR7",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMR8, "PMR8",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMR9, "PMR9",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMRA, "PMR10",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMRB, "PMR11",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMRC, "PMR12",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMRD, "PMR13",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMRE, "PMR14",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_PMRF, "PMR15",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
#endif
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_BUSCSR, "BUSCSR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_L2CR, "L2CR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_620_L2SR, "L2SR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
}
|
||||
#endif /* defined (TARGET_PPC64) */
|
||||
|
||||
static void gen_spr_5xx_8xx (CPUPPCState *env)
|
||||
{
|
||||
/* Exception processing */
|
||||
@ -2993,31 +2865,6 @@ static void init_excp_604 (CPUPPCState *env)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
static void init_excp_620 (CPUPPCState *env)
|
||||
{
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
|
||||
env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
|
||||
env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
|
||||
env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
|
||||
env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
|
||||
env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
|
||||
env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
|
||||
env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
|
||||
env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
|
||||
env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
|
||||
env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
|
||||
env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
|
||||
env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
|
||||
env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
|
||||
env->hreset_excp_prefix = 0xFFF00000UL;
|
||||
/* Hardware reset vector */
|
||||
env->hreset_vector = 0x0000000000000100ULL;
|
||||
#endif
|
||||
}
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
|
||||
static void init_excp_7x0 (CPUPPCState *env)
|
||||
{
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
@ -4951,6 +4798,9 @@ POWERPC_FAMILY(601)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x000000000000FD70ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_601;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_601;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_601;
|
||||
@ -4985,7 +4835,9 @@ POWERPC_FAMILY(601v)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x000000000000FD70ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_601;
|
||||
pcc->excp_model = POWERPC_EXCP_601;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_601;
|
||||
pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK;
|
||||
@ -5192,6 +5044,9 @@ POWERPC_FAMILY(604)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x000000000005FF77ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_604;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_604;
|
||||
@ -5258,6 +5113,9 @@ POWERPC_FAMILY(604E)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x000000000005FF77ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_604;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_604;
|
||||
@ -5311,6 +5169,9 @@ POWERPC_FAMILY(740)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x000000000005FF77ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_7x0;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_750;
|
||||
@ -5372,6 +5233,9 @@ POWERPC_FAMILY(750)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x000000000005FF77ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_7x0;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_750;
|
||||
@ -5556,6 +5420,9 @@ POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x000000000005FF77ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_7x0;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_750;
|
||||
@ -5621,6 +5488,9 @@ POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x000000000005FF77ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_7x0;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_750;
|
||||
@ -5691,6 +5561,9 @@ POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x000000000005FF77ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_7x0;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_750;
|
||||
@ -5761,6 +5634,9 @@ POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x000000000005FF77ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_7x0;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_750;
|
||||
@ -5953,6 +5829,9 @@ POWERPC_FAMILY(7400)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x000000000205FF77ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_74xx;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_7400;
|
||||
@ -6019,6 +5898,9 @@ POWERPC_FAMILY(7410)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x000000000205FF77ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_74xx;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_7400;
|
||||
@ -6725,6 +6607,9 @@ POWERPC_FAMILY(970)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x900000000204FF36ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_64B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_970;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_970;
|
||||
pcc->bfd_mach = bfd_mach_ppc64;
|
||||
@ -6835,6 +6720,9 @@ POWERPC_FAMILY(970FX)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x800000000204FF36ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_64B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_970;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_970;
|
||||
pcc->bfd_mach = bfd_mach_ppc64;
|
||||
@ -6933,6 +6821,9 @@ POWERPC_FAMILY(970GX)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x800000000204FF36ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_64B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_970;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_970;
|
||||
pcc->bfd_mach = bfd_mach_ppc64;
|
||||
@ -7031,6 +6922,9 @@ POWERPC_FAMILY(970MP)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x900000000204FF36ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_64B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_970;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_970;
|
||||
pcc->bfd_mach = bfd_mach_ppc64;
|
||||
@ -7075,6 +6969,7 @@ static void init_proc_POWER7 (CPUPPCState *env)
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000); /* TOFIX */
|
||||
gen_spr_amr(env);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_CTRL, "SPR_CTRLT",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
@ -7122,6 +7017,9 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX;
|
||||
pcc->msr_mask = 0x800000000204FF36ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_2_06;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_POWER7;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_POWER7;
|
||||
pcc->bfd_mach = bfd_mach_ppc64;
|
||||
@ -7129,55 +7027,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
|
||||
POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
|
||||
POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR;
|
||||
}
|
||||
|
||||
static void init_proc_620 (CPUPPCState *env)
|
||||
{
|
||||
gen_spr_ne_601(env);
|
||||
gen_spr_620(env);
|
||||
/* Time base */
|
||||
gen_tbl(env);
|
||||
/* Hardware implementation registers */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_HID0, "HID0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* Memory management */
|
||||
gen_low_BATs(env);
|
||||
init_excp_620(env);
|
||||
env->dcache_line_size = 64;
|
||||
env->icache_line_size = 64;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc6xx_irq_init(env);
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(620)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
|
||||
dc->desc = "PowerPC 620";
|
||||
pcc->init_proc = init_proc_620;
|
||||
pcc->check_pow = check_pow_nocheck; /* Check this */
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB |
|
||||
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
|
||||
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
|
||||
PPC_FLOAT_STFIWX |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
|
||||
PPC_SEGMENT | PPC_EXTERN |
|
||||
PPC_64B | PPC_SLBI;
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
pcc->msr_mask = 0x800000000005FF77ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_620;
|
||||
pcc->excp_model = POWERPC_EXCP_970;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc64;
|
||||
pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE |
|
||||
POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK;
|
||||
}
|
||||
|
||||
#endif /* defined (TARGET_PPC64) */
|
||||
|
||||
|
||||
@ -7693,7 +7542,7 @@ static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
|
||||
return 8;
|
||||
}
|
||||
if (n == 32) {
|
||||
/* FPSCR not implemented */
|
||||
helper_store_fpscr(env, ldl_p(mem_buf), 0xffffffff);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
@ -7915,9 +7764,6 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
case POWERPC_MMU_64B:
|
||||
mmu_model = "PowerPC 64";
|
||||
break;
|
||||
case POWERPC_MMU_620:
|
||||
mmu_model = "PowerPC 620";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
mmu_model = "Unknown or invalid";
|
||||
|
44
target-ppc/user_only_helper.c
Normal file
44
target-ppc/user_only_helper.c
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* PowerPC MMU stub handling for user mode emulation
|
||||
*
|
||||
* Copyright (c) 2003-2007 Jocelyn Mayer
|
||||
* Copyright (c) 2013 David Gibson, 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 "cpu.h"
|
||||
|
||||
int cpu_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
|
||||
int mmu_idx)
|
||||
{
|
||||
int exception, error_code;
|
||||
|
||||
if (rw == 2) {
|
||||
exception = POWERPC_EXCP_ISI;
|
||||
error_code = 0x40000000;
|
||||
} else {
|
||||
exception = POWERPC_EXCP_DSI;
|
||||
error_code = 0x40000000;
|
||||
if (rw) {
|
||||
error_code |= 0x02000000;
|
||||
}
|
||||
env->spr[SPR_DAR] = address;
|
||||
env->spr[SPR_DSISR] = error_code;
|
||||
}
|
||||
env->exception_index = exception;
|
||||
env->error_code = error_code;
|
||||
|
||||
return 1;
|
||||
}
|
Loading…
Reference in New Issue
Block a user