2 * MIPS TLB (Translation lookaside buffer) helpers.
4 * Copyright (c) 2004-2005 Jocelyn Mayer
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 #include "qemu/osdep.h"
21 #include "exec/exec-all.h"
22 #include "../internal.h"
24 static int is_seg_am_mapped(unsigned int am
, bool eu
, int mmu_idx
)
27 * Interpret access control mode and mmu_idx.
30 * UK 0 0 1 1 0 0 - - 0
31 * MK 1 0 1 1 0 1 - - !eu
32 * MSK 2 0 0 1 0 1 1 - !eu
33 * MUSK 3 0 0 0 0 1 1 1 !eu
34 * MUSUK 4 0 0 0 0 0 1 1 0
35 * USK 5 0 0 1 0 0 0 - 0
37 * UUSK 7 0 0 0 0 0 0 0 0
43 /* If EU is set, always unmapped */
49 /* Never AdE, TLB mapped if AM={1,2,3} */
50 adetlb_mask
= 0x70000000;
54 /* AdE if AM={0,1}, TLB mapped if AM={2,3,4} */
55 adetlb_mask
= 0xc0380000;
59 /* AdE if AM={0,1,2,5}, TLB mapped if AM={3,4} */
60 adetlb_mask
= 0xe4180000;
63 /* does this AM cause AdE in current execution mode */
64 if ((adetlb_mask
<< am
) < 0) {
65 return TLBRET_BADADDR
;
70 /* is this AM mapped in current execution mode */
71 return ((adetlb_mask
<< am
) < 0);
74 return TLBRET_BADADDR
;
78 static int get_seg_physical_address(CPUMIPSState
*env
, hwaddr
*physical
,
79 int *prot
, target_ulong real_address
,
80 MMUAccessType access_type
, int mmu_idx
,
81 unsigned int am
, bool eu
,
85 int mapped
= is_seg_am_mapped(am
, eu
, mmu_idx
);
88 /* is_seg_am_mapped can report TLBRET_BADADDR */
91 /* The segment is TLB mapped */
92 return env
->tlb
->map_address(env
, physical
, prot
, real_address
,
95 /* The segment is unmapped */
96 *physical
= physical_base
| (real_address
& segmask
);
97 *prot
= PAGE_READ
| PAGE_WRITE
| PAGE_EXEC
;
102 static int get_segctl_physical_address(CPUMIPSState
*env
, hwaddr
*physical
,
103 int *prot
, target_ulong real_address
,
104 MMUAccessType access_type
, int mmu_idx
,
105 uint16_t segctl
, target_ulong segmask
)
107 unsigned int am
= (segctl
& CP0SC_AM_MASK
) >> CP0SC_AM
;
108 bool eu
= (segctl
>> CP0SC_EU
) & 1;
109 hwaddr pa
= ((hwaddr
)segctl
& CP0SC_PA_MASK
) << 20;
111 return get_seg_physical_address(env
, physical
, prot
, real_address
,
112 access_type
, mmu_idx
, am
, eu
, segmask
,
113 pa
& ~(hwaddr
)segmask
);
116 int get_physical_address(CPUMIPSState
*env
, hwaddr
*physical
,
117 int *prot
, target_ulong real_address
,
118 MMUAccessType access_type
, int mmu_idx
)
120 /* User mode can only access useg/xuseg */
121 #if defined(TARGET_MIPS64)
122 int user_mode
= mmu_idx
== MIPS_HFLAG_UM
;
123 int supervisor_mode
= mmu_idx
== MIPS_HFLAG_SM
;
124 int kernel_mode
= !user_mode
&& !supervisor_mode
;
125 int UX
= (env
->CP0_Status
& (1 << CP0St_UX
)) != 0;
126 int SX
= (env
->CP0_Status
& (1 << CP0St_SX
)) != 0;
127 int KX
= (env
->CP0_Status
& (1 << CP0St_KX
)) != 0;
129 int ret
= TLBRET_MATCH
;
130 /* effective address (modified for KVM T&E kernel segments) */
131 target_ulong address
= real_address
;
133 if (address
<= USEG_LIMIT
) {
137 if (address
>= 0x40000000UL
) {
138 segctl
= env
->CP0_SegCtl2
;
140 segctl
= env
->CP0_SegCtl2
>> 16;
142 ret
= get_segctl_physical_address(env
, physical
, prot
,
143 real_address
, access_type
,
144 mmu_idx
, segctl
, 0x3FFFFFFF);
145 #if defined(TARGET_MIPS64)
146 } else if (address
< 0x4000000000000000ULL
) {
148 if (UX
&& address
<= (0x3FFFFFFFFFFFFFFFULL
& env
->SEGMask
)) {
149 ret
= env
->tlb
->map_address(env
, physical
, prot
,
150 real_address
, access_type
);
152 ret
= TLBRET_BADADDR
;
154 } else if (address
< 0x8000000000000000ULL
) {
156 if ((supervisor_mode
|| kernel_mode
) &&
157 SX
&& address
<= (0x7FFFFFFFFFFFFFFFULL
& env
->SEGMask
)) {
158 ret
= env
->tlb
->map_address(env
, physical
, prot
,
159 real_address
, access_type
);
161 ret
= TLBRET_BADADDR
;
163 } else if (address
< 0xC000000000000000ULL
) {
165 if ((address
& 0x07FFFFFFFFFFFFFFULL
) <= env
->PAMask
) {
166 /* KX/SX/UX bit to check for each xkphys EVA access mode */
167 static const uint8_t am_ksux
[8] = {
168 [CP0SC_AM_UK
] = (1u << CP0St_KX
),
169 [CP0SC_AM_MK
] = (1u << CP0St_KX
),
170 [CP0SC_AM_MSK
] = (1u << CP0St_SX
),
171 [CP0SC_AM_MUSK
] = (1u << CP0St_UX
),
172 [CP0SC_AM_MUSUK
] = (1u << CP0St_UX
),
173 [CP0SC_AM_USK
] = (1u << CP0St_SX
),
174 [6] = (1u << CP0St_KX
),
175 [CP0SC_AM_UUSK
] = (1u << CP0St_UX
),
177 unsigned int am
= CP0SC_AM_UK
;
178 unsigned int xr
= (env
->CP0_SegCtl2
& CP0SC2_XR_MASK
) >> CP0SC2_XR
;
180 if (xr
& (1 << ((address
>> 59) & 0x7))) {
181 am
= (env
->CP0_SegCtl1
& CP0SC1_XAM_MASK
) >> CP0SC1_XAM
;
183 /* Does CP0_Status.KX/SX/UX permit the access mode (am) */
184 if (env
->CP0_Status
& am_ksux
[am
]) {
185 ret
= get_seg_physical_address(env
, physical
, prot
,
186 real_address
, access_type
,
187 mmu_idx
, am
, false, env
->PAMask
,
190 ret
= TLBRET_BADADDR
;
193 ret
= TLBRET_BADADDR
;
195 } else if (address
< 0xFFFFFFFF80000000ULL
) {
197 if (kernel_mode
&& KX
&&
198 address
<= (0xFFFFFFFF7FFFFFFFULL
& env
->SEGMask
)) {
199 ret
= env
->tlb
->map_address(env
, physical
, prot
,
200 real_address
, access_type
);
202 ret
= TLBRET_BADADDR
;
205 } else if (address
< KSEG1_BASE
) {
207 ret
= get_segctl_physical_address(env
, physical
, prot
, real_address
,
208 access_type
, mmu_idx
,
209 env
->CP0_SegCtl1
>> 16, 0x1FFFFFFF);
210 } else if (address
< KSEG2_BASE
) {
212 ret
= get_segctl_physical_address(env
, physical
, prot
, real_address
,
213 access_type
, mmu_idx
,
214 env
->CP0_SegCtl1
, 0x1FFFFFFF);
215 } else if (address
< KSEG3_BASE
) {
217 ret
= get_segctl_physical_address(env
, physical
, prot
, real_address
,
218 access_type
, mmu_idx
,
219 env
->CP0_SegCtl0
>> 16, 0x1FFFFFFF);
223 * XXX: debug segment is not emulated
225 ret
= get_segctl_physical_address(env
, physical
, prot
, real_address
,
226 access_type
, mmu_idx
,
227 env
->CP0_SegCtl0
, 0x1FFFFFFF);
232 hwaddr
mips_cpu_get_phys_page_debug(CPUState
*cs
, vaddr addr
)
234 MIPSCPU
*cpu
= MIPS_CPU(cs
);
235 CPUMIPSState
*env
= &cpu
->env
;
239 if (get_physical_address(env
, &phys_addr
, &prot
, addr
, MMU_DATA_LOAD
,
240 cpu_mmu_index(env
, false)) != 0) {