Keep SLB in-CPU
Real 970 CPUs have the SLB not memory backed, but inside the CPU. This breaks bridge mode for 970 for now, but at least keeps us from overwriting physical addresses 0x0 - 0x300, rendering our interrupt handlers useless. I put in a stub for bridge mode operation that could be enabled easily, but for now it's safer to leave that off I guess (970fx doesn't have bridge mode AFAIK). Signed-off-by: Alexander Graf <alex@csgraf.de> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6757 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
e47ce3f244
commit
8eee0af947
@ -344,6 +344,12 @@ union ppc_tlb_t {
|
|||||||
ppcemb_tlb_t tlbe;
|
ppcemb_tlb_t tlbe;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct ppc_slb_t ppc_slb_t;
|
||||||
|
struct ppc_slb_t {
|
||||||
|
uint64_t tmp64;
|
||||||
|
uint32_t tmp;
|
||||||
|
};
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Machine state register bits definition */
|
/* Machine state register bits definition */
|
||||||
#define MSR_SF 63 /* Sixty-four-bit mode hflags */
|
#define MSR_SF 63 /* Sixty-four-bit mode hflags */
|
||||||
@ -584,6 +590,7 @@ struct CPUPPCState {
|
|||||||
/* Address space register */
|
/* Address space register */
|
||||||
target_ulong asr;
|
target_ulong asr;
|
||||||
/* PowerPC 64 SLB area */
|
/* PowerPC 64 SLB area */
|
||||||
|
ppc_slb_t slb[64];
|
||||||
int slb_nr;
|
int slb_nr;
|
||||||
#endif
|
#endif
|
||||||
/* segment registers */
|
/* segment registers */
|
||||||
|
@ -693,14 +693,44 @@ static always_inline int find_pte (CPUState *env, mmu_ctx_t *ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(TARGET_PPC64)
|
#if defined(TARGET_PPC64)
|
||||||
static always_inline int slb_is_valid (uint64_t slb64)
|
static ppc_slb_t *slb_get_entry(CPUPPCState *env, int nr)
|
||||||
{
|
{
|
||||||
return slb64 & 0x0000000008000000ULL ? 1 : 0;
|
ppc_slb_t *retval = &env->slb[nr];
|
||||||
|
|
||||||
|
#if 0 // XXX implement bridge mode?
|
||||||
|
if (env->spr[SPR_ASR] & 1) {
|
||||||
|
target_phys_addr_t sr_base;
|
||||||
|
|
||||||
|
sr_base = env->spr[SPR_ASR] & 0xfffffffffffff000;
|
||||||
|
sr_base += (12 * nr);
|
||||||
|
|
||||||
|
retval->tmp64 = ldq_phys(sr_base);
|
||||||
|
retval->tmp = ldl_phys(sr_base + 8);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static always_inline void slb_invalidate (uint64_t *slb64)
|
static void slb_set_entry(CPUPPCState *env, int nr, ppc_slb_t *slb)
|
||||||
{
|
{
|
||||||
*slb64 &= ~0x0000000008000000ULL;
|
ppc_slb_t *entry = &env->slb[nr];
|
||||||
|
|
||||||
|
if (slb == entry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entry->tmp64 = slb->tmp64;
|
||||||
|
entry->tmp = slb->tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline int slb_is_valid (ppc_slb_t *slb)
|
||||||
|
{
|
||||||
|
return (int)(slb->tmp64 & 0x0000000008000000ULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline void slb_invalidate (ppc_slb_t *slb)
|
||||||
|
{
|
||||||
|
slb->tmp64 &= ~0x0000000008000000ULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static always_inline int slb_lookup (CPUPPCState *env, target_ulong eaddr,
|
static always_inline int slb_lookup (CPUPPCState *env, target_ulong eaddr,
|
||||||
@ -708,25 +738,20 @@ static always_inline int slb_lookup (CPUPPCState *env, target_ulong eaddr,
|
|||||||
target_ulong *page_mask, int *attr,
|
target_ulong *page_mask, int *attr,
|
||||||
int *target_page_bits)
|
int *target_page_bits)
|
||||||
{
|
{
|
||||||
target_phys_addr_t sr_base;
|
|
||||||
target_ulong mask;
|
target_ulong mask;
|
||||||
uint64_t tmp64;
|
|
||||||
uint32_t tmp;
|
|
||||||
int n, ret;
|
int n, ret;
|
||||||
|
|
||||||
ret = -5;
|
ret = -5;
|
||||||
sr_base = env->spr[SPR_ASR];
|
LOG_SLB("%s: eaddr " ADDRX "\n", __func__, eaddr);
|
||||||
LOG_SLB("%s: eaddr " ADDRX " base " PADDRX "\n",
|
|
||||||
__func__, eaddr, sr_base);
|
|
||||||
mask = 0x0000000000000000ULL; /* Avoid gcc warning */
|
mask = 0x0000000000000000ULL; /* Avoid gcc warning */
|
||||||
for (n = 0; n < env->slb_nr; n++) {
|
for (n = 0; n < env->slb_nr; n++) {
|
||||||
tmp64 = ldq_phys(sr_base);
|
ppc_slb_t *slb = slb_get_entry(env, n);
|
||||||
tmp = ldl_phys(sr_base + 8);
|
|
||||||
LOG_SLB("%s: seg %d " PADDRX " %016" PRIx64 " %08"
|
LOG_SLB("%s: seg %d %016" PRIx64 " %08"
|
||||||
PRIx32 "\n", __func__, n, sr_base, tmp64, tmp);
|
PRIx32 "\n", __func__, n, slb->tmp64, slb->tmp);
|
||||||
if (slb_is_valid(tmp64)) {
|
if (slb_is_valid(slb)) {
|
||||||
/* SLB entry is valid */
|
/* SLB entry is valid */
|
||||||
if (tmp & 0x8) {
|
if (slb->tmp & 0x8) {
|
||||||
/* 1 TB Segment */
|
/* 1 TB Segment */
|
||||||
mask = 0xFFFF000000000000ULL;
|
mask = 0xFFFF000000000000ULL;
|
||||||
if (target_page_bits)
|
if (target_page_bits)
|
||||||
@ -737,16 +762,15 @@ static always_inline int slb_lookup (CPUPPCState *env, target_ulong eaddr,
|
|||||||
if (target_page_bits)
|
if (target_page_bits)
|
||||||
*target_page_bits = TARGET_PAGE_BITS;
|
*target_page_bits = TARGET_PAGE_BITS;
|
||||||
}
|
}
|
||||||
if ((eaddr & mask) == (tmp64 & mask)) {
|
if ((eaddr & mask) == (slb->tmp64 & mask)) {
|
||||||
/* SLB match */
|
/* SLB match */
|
||||||
*vsid = ((tmp64 << 24) | (tmp >> 8)) & 0x0003FFFFFFFFFFFFULL;
|
*vsid = ((slb->tmp64 << 24) | (slb->tmp >> 8)) & 0x0003FFFFFFFFFFFFULL;
|
||||||
*page_mask = ~mask;
|
*page_mask = ~mask;
|
||||||
*attr = tmp & 0xFF;
|
*attr = slb->tmp & 0xFF;
|
||||||
ret = n;
|
ret = n;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sr_base += 12;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -754,25 +778,22 @@ static always_inline int slb_lookup (CPUPPCState *env, target_ulong eaddr,
|
|||||||
|
|
||||||
void ppc_slb_invalidate_all (CPUPPCState *env)
|
void ppc_slb_invalidate_all (CPUPPCState *env)
|
||||||
{
|
{
|
||||||
target_phys_addr_t sr_base;
|
|
||||||
uint64_t tmp64;
|
|
||||||
int n, do_invalidate;
|
int n, do_invalidate;
|
||||||
|
|
||||||
do_invalidate = 0;
|
do_invalidate = 0;
|
||||||
sr_base = env->spr[SPR_ASR];
|
|
||||||
/* XXX: Warning: slbia never invalidates the first segment */
|
/* XXX: Warning: slbia never invalidates the first segment */
|
||||||
for (n = 1; n < env->slb_nr; n++) {
|
for (n = 1; n < env->slb_nr; n++) {
|
||||||
tmp64 = ldq_phys(sr_base);
|
ppc_slb_t *slb = slb_get_entry(env, n);
|
||||||
if (slb_is_valid(tmp64)) {
|
|
||||||
slb_invalidate(&tmp64);
|
if (slb_is_valid(slb)) {
|
||||||
stq_phys(sr_base, tmp64);
|
slb_invalidate(slb);
|
||||||
|
slb_set_entry(env, n, slb);
|
||||||
/* XXX: given the fact that segment size is 256 MB or 1TB,
|
/* 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)
|
* and we still don't have a tlb_flush_mask(env, n, mask)
|
||||||
* in Qemu, we just invalidate all TLBs
|
* in Qemu, we just invalidate all TLBs
|
||||||
*/
|
*/
|
||||||
do_invalidate = 1;
|
do_invalidate = 1;
|
||||||
}
|
}
|
||||||
sr_base += 12;
|
|
||||||
}
|
}
|
||||||
if (do_invalidate)
|
if (do_invalidate)
|
||||||
tlb_flush(env, 1);
|
tlb_flush(env, 1);
|
||||||
@ -780,20 +801,17 @@ void ppc_slb_invalidate_all (CPUPPCState *env)
|
|||||||
|
|
||||||
void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0)
|
void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0)
|
||||||
{
|
{
|
||||||
target_phys_addr_t sr_base;
|
|
||||||
target_ulong vsid, page_mask;
|
target_ulong vsid, page_mask;
|
||||||
uint64_t tmp64;
|
|
||||||
int attr;
|
int attr;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
n = slb_lookup(env, T0, &vsid, &page_mask, &attr, NULL);
|
n = slb_lookup(env, T0, &vsid, &page_mask, &attr, NULL);
|
||||||
if (n >= 0) {
|
if (n >= 0) {
|
||||||
sr_base = env->spr[SPR_ASR];
|
ppc_slb_t *slb = slb_get_entry(env, n);
|
||||||
sr_base += 12 * n;
|
|
||||||
tmp64 = ldq_phys(sr_base);
|
if (slb_is_valid(slb)) {
|
||||||
if (slb_is_valid(tmp64)) {
|
slb_invalidate(slb);
|
||||||
slb_invalidate(&tmp64);
|
slb_set_entry(env, n, slb);
|
||||||
stq_phys(sr_base, tmp64);
|
|
||||||
/* XXX: given the fact that segment size is 256 MB or 1TB,
|
/* 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)
|
* and we still don't have a tlb_flush_mask(env, n, mask)
|
||||||
* in Qemu, we just invalidate all TLBs
|
* in Qemu, we just invalidate all TLBs
|
||||||
@ -805,36 +823,28 @@ void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0)
|
|||||||
|
|
||||||
target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr)
|
target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr)
|
||||||
{
|
{
|
||||||
target_phys_addr_t sr_base;
|
|
||||||
target_ulong rt;
|
target_ulong rt;
|
||||||
uint64_t tmp64;
|
ppc_slb_t *slb = slb_get_entry(env, slb_nr);
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
sr_base = env->spr[SPR_ASR];
|
if (slb_is_valid(slb)) {
|
||||||
sr_base += 12 * slb_nr;
|
|
||||||
tmp64 = ldq_phys(sr_base);
|
|
||||||
tmp = ldl_phys(sr_base + 8);
|
|
||||||
if (tmp64 & 0x0000000008000000ULL) {
|
|
||||||
/* SLB entry is valid */
|
/* SLB entry is valid */
|
||||||
/* Copy SLB bits 62:88 to Rt 37:63 (VSID 23:49) */
|
/* Copy SLB bits 62:88 to Rt 37:63 (VSID 23:49) */
|
||||||
rt = tmp >> 8; /* 65:88 => 40:63 */
|
rt = slb->tmp >> 8; /* 65:88 => 40:63 */
|
||||||
rt |= (tmp64 & 0x7) << 24; /* 62:64 => 37:39 */
|
rt |= (slb->tmp64 & 0x7) << 24; /* 62:64 => 37:39 */
|
||||||
/* Copy SLB bits 89:92 to Rt 33:36 (KsKpNL) */
|
/* Copy SLB bits 89:92 to Rt 33:36 (KsKpNL) */
|
||||||
rt |= ((tmp >> 4) & 0xF) << 27;
|
rt |= ((slb->tmp >> 4) & 0xF) << 27;
|
||||||
} else {
|
} else {
|
||||||
rt = 0;
|
rt = 0;
|
||||||
}
|
}
|
||||||
LOG_SLB("%s: " PADDRX " %016" PRIx64 " %08" PRIx32 " => %d "
|
LOG_SLB("%s: %016" PRIx64 " %08" PRIx32 " => %d "
|
||||||
ADDRX "\n", __func__, sr_base, tmp64, tmp, slb_nr, rt);
|
ADDRX "\n", __func__, slb->tmp64, slb->tmp, slb_nr, rt);
|
||||||
|
|
||||||
return rt;
|
return rt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs)
|
void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs)
|
||||||
{
|
{
|
||||||
target_phys_addr_t sr_base;
|
ppc_slb_t *slb;
|
||||||
uint64_t tmp64;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
uint64_t vsid;
|
uint64_t vsid;
|
||||||
uint64_t esid;
|
uint64_t esid;
|
||||||
@ -847,19 +857,15 @@ void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs)
|
|||||||
valid = (rb & (1 << 27));
|
valid = (rb & (1 << 27));
|
||||||
slb_nr = rb & 0xfff;
|
slb_nr = rb & 0xfff;
|
||||||
|
|
||||||
tmp64 = (esid << 28) | valid | (vsid >> 24);
|
slb = slb_get_entry(env, slb_nr);
|
||||||
tmp = (vsid << 8) | (flags << 3);
|
slb->tmp64 = (esid << 28) | valid | (vsid >> 24);
|
||||||
|
slb->tmp = (vsid << 8) | (flags << 3);
|
||||||
|
|
||||||
/* Write SLB entry to memory */
|
LOG_SLB("%s: %d " ADDRX " - " ADDRX " => %016" PRIx64
|
||||||
sr_base = env->spr[SPR_ASR];
|
|
||||||
sr_base += 12 * slb_nr;
|
|
||||||
|
|
||||||
LOG_SLB("%s: %d " ADDRX " - " ADDRX " => " PADDRX " %016" PRIx64
|
|
||||||
" %08" PRIx32 "\n", __func__,
|
" %08" PRIx32 "\n", __func__,
|
||||||
slb_nr, rb, rs, sr_base, tmp64, tmp);
|
slb_nr, rb, rs, tmp64, tmp);
|
||||||
|
|
||||||
stq_phys(sr_base, tmp64);
|
slb_set_entry(env, slb_nr, slb);
|
||||||
stl_phys(sr_base + 8, tmp);
|
|
||||||
}
|
}
|
||||||
#endif /* defined(TARGET_PPC64) */
|
#endif /* defined(TARGET_PPC64) */
|
||||||
|
|
||||||
|
@ -6056,7 +6056,7 @@ static void init_proc_970FX (CPUPPCState *env)
|
|||||||
&spr_read_generic, &spr_write_generic,
|
&spr_read_generic, &spr_write_generic,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
env->slb_nr = 32;
|
env->slb_nr = 64;
|
||||||
#endif
|
#endif
|
||||||
init_excp_970(env);
|
init_excp_970(env);
|
||||||
env->dcache_line_size = 128;
|
env->dcache_line_size = 128;
|
||||||
|
Loading…
Reference in New Issue
Block a user