Message ID | 9057a85806e92c0cf6e0f5df93f44d894523c263.1473159543.git-series.james.hogan@imgtec.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 06/09/2016 12:03, James Hogan wrote: > Implement the optional segmentation control feature in the virtual to > physical address translation code. > > The fixed legacy segment and XKPhys handling is replaced with a dynamic > layout based on the segmentation control registers (which should be set > up even when the feature is not exposed to the guest). > > Signed-off-by: James Hogan <james.hogan@imgtec.com> > Cc: Leon Alrae <leon.alrae@imgtec.com> > Cc: Aurelien Jarno <aurelien@aurel32.net> > --- > target-mips/helper.c | 154 +++++++++++++++++++++++++++++++++++--------- > 1 file changed, 123 insertions(+), 31 deletions(-) > > diff --git a/target-mips/helper.c b/target-mips/helper.c > index 2065fc3ec119..63d709bd620f 100644 > --- a/target-mips/helper.c > +++ b/target-mips/helper.c > @@ -107,15 +107,107 @@ int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, > return TLBRET_NOMATCH; > } > > +static int is_seg_am_mapped(unsigned int am, bool eu, int mmu_idx) > +{ Nice :) I like this simplified logic to decode access controls. > + /* > + * Interpret access control mode and mmu_idx. > + * AdE? TLB? > + * AM K S U E K S U E > + * UK 0 0 1 1 0 0 - - 0 > + * MK 1 0 1 1 0 1 - - !eu > + * MSK 2 0 0 1 0 1 1 - !eu > + * MUSK 3 0 0 0 0 1 1 1 !eu > + * MUSUK 4 0 0 0 0 0 1 1 0 > + * USK 5 0 0 1 0 0 0 - 0 > + * - 6 - - - - - - - - > + * UUSK 7 0 0 0 0 0 0 0 0 > + */ > + int32_t adetlb_mask; > + > + switch (mmu_idx) { > + case 3 /* ERL */: > + /* If EU is set, always unmapped */ > + if (eu) { > + return 0; > + } I guess checking ERL and EU has to be outside of the switch case. If ERL = 1 and EU = 0 then it has to check with the current Privilege level but in this case it falls through and always checks against Kernel mode. > + /* fall through */ > + case MIPS_HFLAG_KM: > + /* Never AdE, TLB mapped if AM={1,2,3} */ > + adetlb_mask = 0x70000000; > + goto check_tlb; > + > + case MIPS_HFLAG_SM: > + /* AdE if AM={0,1}, TLB mapped if AM={2,3,4} */ > + adetlb_mask = 0xc0380000; > + goto check_ade; > + > + case MIPS_HFLAG_UM: > + /* AdE if AM={0,1,2,5}, TLB mapped if AM={3,4} */ > + adetlb_mask = 0xe4180000; > + /* fall through */ > + check_ade: > + /* does this AM cause AdE in current execution mode */ > + if ((adetlb_mask << am) < 0) { > + return TLBRET_BADADDR; > + } > + adetlb_mask <<= 8; > + /* fall through */ > + check_tlb: > + /* is this AM mapped in current execution mode */ > + return ((adetlb_mask << am) < 0); > + default: > + assert(0); > + return TLBRET_BADADDR; > + }; > +} > + > +static int get_seg_physical_address(CPUMIPSState *env, hwaddr *physical, > + int *prot, target_ulong real_address, > + int rw, int access_type, int mmu_idx, > + unsigned int am, bool eu, > + target_ulong segmask, > + target_ulong physical_base) uint64_t for physical_base. see below comment. > +{ > + int mapped = is_seg_am_mapped(am, eu, mmu_idx); > + > + if (mapped < 0) { > + /* is_seg_am_mapped can report TLBRET_BADADDR */ > + return mapped; > + } else if (mapped) { > + /* The segment is TLB mapped */ > + return env->tlb->map_address(env, physical, prot, real_address, rw, > + access_type); > + } else { > + /* The segment is unmapped */ > + *physical = physical_base | (real_address & segmask); > + *prot = PAGE_READ | PAGE_WRITE; > + return TLBRET_MATCH; > + } > +} > + > +static int get_segctl_physical_address(CPUMIPSState *env, hwaddr *physical, > + int *prot, target_ulong real_address, > + int rw, int access_type, int mmu_idx, > + uint16_t segctl, target_ulong segmask) > +{ > + unsigned int am = (segctl & CP0SC_AM_MASK) >> CP0SC_AM; > + bool eu = (segctl >> CP0SC_EU) & 1; > + target_ulong pa = ((target_ulong)segctl & CP0SC_PA_MASK) << 20; According to the architecture spec, PA supports mapping of up to a 36-bit physical address. uint64_t would be better for PA and physical_base. > + > + return get_seg_physical_address(env, physical, prot, real_address, rw, > + access_type, mmu_idx, am, eu, segmask, > + pa & ~segmask); > +} > + > static int get_physical_address (CPUMIPSState *env, hwaddr *physical, > int *prot, target_ulong real_address, > int rw, int access_type, int mmu_idx) > { > /* User mode can only access useg/xuseg */ > +#if defined(TARGET_MIPS64) > int user_mode = mmu_idx == MIPS_HFLAG_UM; > int supervisor_mode = mmu_idx == MIPS_HFLAG_SM; > int kernel_mode = !user_mode && !supervisor_mode; > -#if defined(TARGET_MIPS64) > int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; > int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; > int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; > @@ -148,12 +240,16 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical, > > if (address <= USEG_LIMIT) { > /* useg */ > - if (env->CP0_Status & (1 << CP0St_ERL)) { > - *physical = address & 0xFFFFFFFF; > - *prot = PAGE_READ | PAGE_WRITE; > + uint16_t segctl; > + > + if (address >= 0x40000000UL) { > + segctl = env->CP0_SegCtl2; > } else { > - ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); > + segctl = env->CP0_SegCtl2 >> 16; > } > + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, > + access_type, mmu_idx, segctl, > + 0x3FFFFFFF); > #if defined(TARGET_MIPS64) > } else if (address < 0x4000000000000000ULL) { > /* xuseg */ > @@ -172,10 +268,16 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical, > } > } else if (address < 0xC000000000000000ULL) { > /* xkphys */ > - if (kernel_mode && KX && > - (address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) { > - *physical = address & env->PAMask; > - *prot = PAGE_READ | PAGE_WRITE; > + if (KX && (address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) { KX should be dropped from here. More over, we need to check KX, SX and UX whether it meets minimum privilege level according to AM. > + unsigned int am = CP0SC_AM_UK; When the region is disabled, it is valid for Kernel mode only. > + unsigned int xr = (env->CP0_SegCtl2 & CP0SC2_XR_MASK) >> CP0SC2_XR; > + > + if (xr & (1 << ((address >> 59) & 0x7))) { > + am = (env->CP0_SegCtl1 & CP0SC1_XAM_MASK) >> CP0SC1_XAM; > + } > + ret = get_seg_physical_address(env, physical, prot, real_address, > + rw, access_type, mmu_idx, am, false, > + env->PAMask, 0); > } else { > ret = TLBRET_BADADDR; > } > @@ -190,35 +292,25 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical, > #endif > } else if (address < (int32_t)KSEG1_BASE) { > /* kseg0 */ > - if (kernel_mode) { > - *physical = address - (int32_t)KSEG0_BASE; > - *prot = PAGE_READ | PAGE_WRITE; > - } else { > - ret = TLBRET_BADADDR; > - } > + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, > + access_type, mmu_idx, > + env->CP0_SegCtl1 >> 16, 0x1FFFFFFF); > } else if (address < (int32_t)KSEG2_BASE) { > /* kseg1 */ > - if (kernel_mode) { > - *physical = address - (int32_t)KSEG1_BASE; > - *prot = PAGE_READ | PAGE_WRITE; > - } else { > - ret = TLBRET_BADADDR; > - } > + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, > + access_type, mmu_idx, > + env->CP0_SegCtl1, 0x1FFFFFFF); > } else if (address < (int32_t)KSEG3_BASE) { > /* sseg (kseg2) */ > - if (supervisor_mode || kernel_mode) { > - ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); > - } else { > - ret = TLBRET_BADADDR; > - } > + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, > + access_type, mmu_idx, > + env->CP0_SegCtl0 >> 16, 0x1FFFFFFF); > } else { > /* kseg3 */ > /* XXX: debug segment is not emulated */ > - if (kernel_mode) { > - ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); > - } else { > - ret = TLBRET_BADADDR; > - } > + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, > + access_type, mmu_idx, > + env->CP0_SegCtl0, 0x1FFFFFFF); > } > return ret; > } > Regards, Yongbok
On Thu, Oct 13, 2016 at 02:06:25PM +0100, Yongbok Kim wrote: > > > On 06/09/2016 12:03, James Hogan wrote: > > Implement the optional segmentation control feature in the virtual to > > physical address translation code. > > > > The fixed legacy segment and XKPhys handling is replaced with a dynamic > > layout based on the segmentation control registers (which should be set > > up even when the feature is not exposed to the guest). > > > > Signed-off-by: James Hogan <james.hogan@imgtec.com> > > Cc: Leon Alrae <leon.alrae@imgtec.com> > > Cc: Aurelien Jarno <aurelien@aurel32.net> > > --- > > target-mips/helper.c | 154 +++++++++++++++++++++++++++++++++++--------- > > 1 file changed, 123 insertions(+), 31 deletions(-) > > > > diff --git a/target-mips/helper.c b/target-mips/helper.c > > index 2065fc3ec119..63d709bd620f 100644 > > --- a/target-mips/helper.c > > +++ b/target-mips/helper.c > > @@ -107,15 +107,107 @@ int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, > > return TLBRET_NOMATCH; > > } > > > > +static int is_seg_am_mapped(unsigned int am, bool eu, int mmu_idx) > > +{ > > Nice :) I like this simplified logic to decode access controls. > > > + /* > > + * Interpret access control mode and mmu_idx. > > + * AdE? TLB? > > + * AM K S U E K S U E > > + * UK 0 0 1 1 0 0 - - 0 > > + * MK 1 0 1 1 0 1 - - !eu > > + * MSK 2 0 0 1 0 1 1 - !eu > > + * MUSK 3 0 0 0 0 1 1 1 !eu > > + * MUSUK 4 0 0 0 0 0 1 1 0 > > + * USK 5 0 0 1 0 0 0 - 0 > > + * - 6 - - - - - - - - > > + * UUSK 7 0 0 0 0 0 0 0 0 > > + */ > > + int32_t adetlb_mask; > > + > > + switch (mmu_idx) { > > + case 3 /* ERL */: > > + /* If EU is set, always unmapped */ > > + if (eu) { > > + return 0; > > + } > > I guess checking ERL and EU has to be outside of the switch case. > If ERL = 1 and EU = 0 then it has to check with the current Privilege level > but in this case it falls through and always checks against Kernel mode. No, "The processor is in Kernel Mode when the DM bit in the Debug register is 0... and any of the following is true... The ERL bit in the Status register is 1" See how compute_hflags() leaves hflags & KSU == KM in the specified cases. > > > + /* fall through */ > > + case MIPS_HFLAG_KM: > > + /* Never AdE, TLB mapped if AM={1,2,3} */ > > + adetlb_mask = 0x70000000; > > + goto check_tlb; > > + > > + case MIPS_HFLAG_SM: > > + /* AdE if AM={0,1}, TLB mapped if AM={2,3,4} */ > > + adetlb_mask = 0xc0380000; > > + goto check_ade; > > + > > + case MIPS_HFLAG_UM: > > + /* AdE if AM={0,1,2,5}, TLB mapped if AM={3,4} */ > > + adetlb_mask = 0xe4180000; > > + /* fall through */ > > + check_ade: > > + /* does this AM cause AdE in current execution mode */ > > + if ((adetlb_mask << am) < 0) { > > + return TLBRET_BADADDR; > > + } > > + adetlb_mask <<= 8; > > + /* fall through */ > > + check_tlb: > > + /* is this AM mapped in current execution mode */ > > + return ((adetlb_mask << am) < 0); > > + default: > > + assert(0); > > + return TLBRET_BADADDR; > > + }; > > +} > > + > > +static int get_seg_physical_address(CPUMIPSState *env, hwaddr *physical, > > + int *prot, target_ulong real_address, > > + int rw, int access_type, int mmu_idx, > > + unsigned int am, bool eu, > > + target_ulong segmask, > > + target_ulong physical_base) > > uint64_t for physical_base. see below comment. > > > +{ > > + int mapped = is_seg_am_mapped(am, eu, mmu_idx); > > + > > + if (mapped < 0) { > > + /* is_seg_am_mapped can report TLBRET_BADADDR */ > > + return mapped; > > + } else if (mapped) { > > + /* The segment is TLB mapped */ > > + return env->tlb->map_address(env, physical, prot, real_address, rw, > > + access_type); > > + } else { > > + /* The segment is unmapped */ > > + *physical = physical_base | (real_address & segmask); > > + *prot = PAGE_READ | PAGE_WRITE; > > + return TLBRET_MATCH; > > + } > > +} > > + > > +static int get_segctl_physical_address(CPUMIPSState *env, hwaddr *physical, > > + int *prot, target_ulong real_address, > > + int rw, int access_type, int mmu_idx, > > + uint16_t segctl, target_ulong segmask) > > +{ > > + unsigned int am = (segctl & CP0SC_AM_MASK) >> CP0SC_AM; > > + bool eu = (segctl >> CP0SC_EU) & 1; > > + target_ulong pa = ((target_ulong)segctl & CP0SC_PA_MASK) << 20; > > According to the architecture spec, PA supports mapping of up to a 36-bit > physical address. uint64_t would be better for PA and physical_base. Yes, good catch. > > > + > > + return get_seg_physical_address(env, physical, prot, real_address, rw, > > + access_type, mmu_idx, am, eu, segmask, > > + pa & ~segmask); > > +} > > + > > static int get_physical_address (CPUMIPSState *env, hwaddr *physical, > > int *prot, target_ulong real_address, > > int rw, int access_type, int mmu_idx) > > { > > /* User mode can only access useg/xuseg */ > > +#if defined(TARGET_MIPS64) > > int user_mode = mmu_idx == MIPS_HFLAG_UM; > > int supervisor_mode = mmu_idx == MIPS_HFLAG_SM; > > int kernel_mode = !user_mode && !supervisor_mode; > > -#if defined(TARGET_MIPS64) > > int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; > > int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; > > int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; > > @@ -148,12 +240,16 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical, > > > > if (address <= USEG_LIMIT) { > > /* useg */ > > - if (env->CP0_Status & (1 << CP0St_ERL)) { > > - *physical = address & 0xFFFFFFFF; > > - *prot = PAGE_READ | PAGE_WRITE; > > + uint16_t segctl; > > + > > + if (address >= 0x40000000UL) { > > + segctl = env->CP0_SegCtl2; > > } else { > > - ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); > > + segctl = env->CP0_SegCtl2 >> 16; > > } > > + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, > > + access_type, mmu_idx, segctl, > > + 0x3FFFFFFF); > > #if defined(TARGET_MIPS64) > > } else if (address < 0x4000000000000000ULL) { > > /* xuseg */ > > @@ -172,10 +268,16 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical, > > } > > } else if (address < 0xC000000000000000ULL) { > > /* xkphys */ > > - if (kernel_mode && KX && > > - (address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) { > > - *physical = address & env->PAMask; > > - *prot = PAGE_READ | PAGE_WRITE; > > + if (KX && (address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) { > > KX should be dropped from here. > More over, we need to check KX, SX and UX whether it meets minimum > privilege level according to AM. Oh, hmm... good point! Cheers James > > > + unsigned int am = CP0SC_AM_UK; > > When the region is disabled, it is valid for Kernel mode only. > > > + unsigned int xr = (env->CP0_SegCtl2 & CP0SC2_XR_MASK) >> CP0SC2_XR; > > + > > + if (xr & (1 << ((address >> 59) & 0x7))) { > > + am = (env->CP0_SegCtl1 & CP0SC1_XAM_MASK) >> CP0SC1_XAM; > > + } > > + ret = get_seg_physical_address(env, physical, prot, real_address, > > + rw, access_type, mmu_idx, am, false, > > + env->PAMask, 0); > > } else { > > ret = TLBRET_BADADDR; > > } > > @@ -190,35 +292,25 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical, > > #endif > > } else if (address < (int32_t)KSEG1_BASE) { > > /* kseg0 */ > > - if (kernel_mode) { > > - *physical = address - (int32_t)KSEG0_BASE; > > - *prot = PAGE_READ | PAGE_WRITE; > > - } else { > > - ret = TLBRET_BADADDR; > > - } > > + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, > > + access_type, mmu_idx, > > + env->CP0_SegCtl1 >> 16, 0x1FFFFFFF); > > } else if (address < (int32_t)KSEG2_BASE) { > > /* kseg1 */ > > - if (kernel_mode) { > > - *physical = address - (int32_t)KSEG1_BASE; > > - *prot = PAGE_READ | PAGE_WRITE; > > - } else { > > - ret = TLBRET_BADADDR; > > - } > > + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, > > + access_type, mmu_idx, > > + env->CP0_SegCtl1, 0x1FFFFFFF); > > } else if (address < (int32_t)KSEG3_BASE) { > > /* sseg (kseg2) */ > > - if (supervisor_mode || kernel_mode) { > > - ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); > > - } else { > > - ret = TLBRET_BADADDR; > > - } > > + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, > > + access_type, mmu_idx, > > + env->CP0_SegCtl0 >> 16, 0x1FFFFFFF); > > } else { > > /* kseg3 */ > > /* XXX: debug segment is not emulated */ > > - if (kernel_mode) { > > - ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); > > - } else { > > - ret = TLBRET_BADADDR; > > - } > > + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, > > + access_type, mmu_idx, > > + env->CP0_SegCtl0, 0x1FFFFFFF); > > } > > return ret; > > } > > > > > Regards, > Yongbok
diff --git a/target-mips/helper.c b/target-mips/helper.c index 2065fc3ec119..63d709bd620f 100644 --- a/target-mips/helper.c +++ b/target-mips/helper.c @@ -107,15 +107,107 @@ int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, return TLBRET_NOMATCH; } +static int is_seg_am_mapped(unsigned int am, bool eu, int mmu_idx) +{ + /* + * Interpret access control mode and mmu_idx. + * AdE? TLB? + * AM K S U E K S U E + * UK 0 0 1 1 0 0 - - 0 + * MK 1 0 1 1 0 1 - - !eu + * MSK 2 0 0 1 0 1 1 - !eu + * MUSK 3 0 0 0 0 1 1 1 !eu + * MUSUK 4 0 0 0 0 0 1 1 0 + * USK 5 0 0 1 0 0 0 - 0 + * - 6 - - - - - - - - + * UUSK 7 0 0 0 0 0 0 0 0 + */ + int32_t adetlb_mask; + + switch (mmu_idx) { + case 3 /* ERL */: + /* If EU is set, always unmapped */ + if (eu) { + return 0; + } + /* fall through */ + case MIPS_HFLAG_KM: + /* Never AdE, TLB mapped if AM={1,2,3} */ + adetlb_mask = 0x70000000; + goto check_tlb; + + case MIPS_HFLAG_SM: + /* AdE if AM={0,1}, TLB mapped if AM={2,3,4} */ + adetlb_mask = 0xc0380000; + goto check_ade; + + case MIPS_HFLAG_UM: + /* AdE if AM={0,1,2,5}, TLB mapped if AM={3,4} */ + adetlb_mask = 0xe4180000; + /* fall through */ + check_ade: + /* does this AM cause AdE in current execution mode */ + if ((adetlb_mask << am) < 0) { + return TLBRET_BADADDR; + } + adetlb_mask <<= 8; + /* fall through */ + check_tlb: + /* is this AM mapped in current execution mode */ + return ((adetlb_mask << am) < 0); + default: + assert(0); + return TLBRET_BADADDR; + }; +} + +static int get_seg_physical_address(CPUMIPSState *env, hwaddr *physical, + int *prot, target_ulong real_address, + int rw, int access_type, int mmu_idx, + unsigned int am, bool eu, + target_ulong segmask, + target_ulong physical_base) +{ + int mapped = is_seg_am_mapped(am, eu, mmu_idx); + + if (mapped < 0) { + /* is_seg_am_mapped can report TLBRET_BADADDR */ + return mapped; + } else if (mapped) { + /* The segment is TLB mapped */ + return env->tlb->map_address(env, physical, prot, real_address, rw, + access_type); + } else { + /* The segment is unmapped */ + *physical = physical_base | (real_address & segmask); + *prot = PAGE_READ | PAGE_WRITE; + return TLBRET_MATCH; + } +} + +static int get_segctl_physical_address(CPUMIPSState *env, hwaddr *physical, + int *prot, target_ulong real_address, + int rw, int access_type, int mmu_idx, + uint16_t segctl, target_ulong segmask) +{ + unsigned int am = (segctl & CP0SC_AM_MASK) >> CP0SC_AM; + bool eu = (segctl >> CP0SC_EU) & 1; + target_ulong pa = ((target_ulong)segctl & CP0SC_PA_MASK) << 20; + + return get_seg_physical_address(env, physical, prot, real_address, rw, + access_type, mmu_idx, am, eu, segmask, + pa & ~segmask); +} + static int get_physical_address (CPUMIPSState *env, hwaddr *physical, int *prot, target_ulong real_address, int rw, int access_type, int mmu_idx) { /* User mode can only access useg/xuseg */ +#if defined(TARGET_MIPS64) int user_mode = mmu_idx == MIPS_HFLAG_UM; int supervisor_mode = mmu_idx == MIPS_HFLAG_SM; int kernel_mode = !user_mode && !supervisor_mode; -#if defined(TARGET_MIPS64) int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; @@ -148,12 +240,16 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical, if (address <= USEG_LIMIT) { /* useg */ - if (env->CP0_Status & (1 << CP0St_ERL)) { - *physical = address & 0xFFFFFFFF; - *prot = PAGE_READ | PAGE_WRITE; + uint16_t segctl; + + if (address >= 0x40000000UL) { + segctl = env->CP0_SegCtl2; } else { - ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); + segctl = env->CP0_SegCtl2 >> 16; } + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, + access_type, mmu_idx, segctl, + 0x3FFFFFFF); #if defined(TARGET_MIPS64) } else if (address < 0x4000000000000000ULL) { /* xuseg */ @@ -172,10 +268,16 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical, } } else if (address < 0xC000000000000000ULL) { /* xkphys */ - if (kernel_mode && KX && - (address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) { - *physical = address & env->PAMask; - *prot = PAGE_READ | PAGE_WRITE; + if (KX && (address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) { + unsigned int am = CP0SC_AM_UK; + unsigned int xr = (env->CP0_SegCtl2 & CP0SC2_XR_MASK) >> CP0SC2_XR; + + if (xr & (1 << ((address >> 59) & 0x7))) { + am = (env->CP0_SegCtl1 & CP0SC1_XAM_MASK) >> CP0SC1_XAM; + } + ret = get_seg_physical_address(env, physical, prot, real_address, + rw, access_type, mmu_idx, am, false, + env->PAMask, 0); } else { ret = TLBRET_BADADDR; } @@ -190,35 +292,25 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical, #endif } else if (address < (int32_t)KSEG1_BASE) { /* kseg0 */ - if (kernel_mode) { - *physical = address - (int32_t)KSEG0_BASE; - *prot = PAGE_READ | PAGE_WRITE; - } else { - ret = TLBRET_BADADDR; - } + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, + access_type, mmu_idx, + env->CP0_SegCtl1 >> 16, 0x1FFFFFFF); } else if (address < (int32_t)KSEG2_BASE) { /* kseg1 */ - if (kernel_mode) { - *physical = address - (int32_t)KSEG1_BASE; - *prot = PAGE_READ | PAGE_WRITE; - } else { - ret = TLBRET_BADADDR; - } + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, + access_type, mmu_idx, + env->CP0_SegCtl1, 0x1FFFFFFF); } else if (address < (int32_t)KSEG3_BASE) { /* sseg (kseg2) */ - if (supervisor_mode || kernel_mode) { - ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); - } else { - ret = TLBRET_BADADDR; - } + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, + access_type, mmu_idx, + env->CP0_SegCtl0 >> 16, 0x1FFFFFFF); } else { /* kseg3 */ /* XXX: debug segment is not emulated */ - if (kernel_mode) { - ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); - } else { - ret = TLBRET_BADADDR; - } + ret = get_segctl_physical_address(env, physical, prot, real_address, rw, + access_type, mmu_idx, + env->CP0_SegCtl0, 0x1FFFFFFF); } return ret; }
Implement the optional segmentation control feature in the virtual to physical address translation code. The fixed legacy segment and XKPhys handling is replaced with a dynamic layout based on the segmentation control registers (which should be set up even when the feature is not exposed to the guest). Signed-off-by: James Hogan <james.hogan@imgtec.com> Cc: Leon Alrae <leon.alrae@imgtec.com> Cc: Aurelien Jarno <aurelien@aurel32.net> --- target-mips/helper.c | 154 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 123 insertions(+), 31 deletions(-)