2121#include "cpu-csr.h"
2222#include "tcg/tcg_loongarch.h"
2323
24+ typedef bool (* tlb_match )(bool global , int asid , int tlb_asid );
25+
26+ static bool tlb_match_any (bool global , int asid , int tlb_asid )
27+ {
28+ return global || tlb_asid == asid ;
29+ }
30+
31+ static bool tlb_match_asid (bool global , int asid , int tlb_asid )
32+ {
33+ return !global && tlb_asid == asid ;
34+ }
35+
2436bool check_ps (CPULoongArchState * env , uint8_t tlb_ps )
2537{
2638 if (tlb_ps >= 64 ) {
@@ -101,8 +113,7 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index)
101113 target_ulong addr , mask , pagesize ;
102114 uint8_t tlb_ps ;
103115 LoongArchTLB * tlb = & env -> tlb [index ];
104-
105- int mmu_idx = cpu_mmu_index (env_cpu (env ), false);
116+ int idxmap = BIT (MMU_KERNEL_IDX ) | BIT (MMU_USER_IDX );
106117 uint8_t tlb_v0 = FIELD_EX64 (tlb -> tlb_entry0 , TLBENTRY , V );
107118 uint8_t tlb_v1 = FIELD_EX64 (tlb -> tlb_entry1 , TLBENTRY , V );
108119 uint64_t tlb_vppn = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , VPPN );
@@ -112,6 +123,7 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index)
112123 return ;
113124 }
114125
126+ tlb -> tlb_misc = FIELD_DP64 (tlb -> tlb_misc , TLB_MISC , E , 0 );
115127 tlb_ps = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , PS );
116128 pagesize = MAKE_64BIT_MASK (tlb_ps , 1 );
117129 mask = MAKE_64BIT_MASK (0 , tlb_ps + 1 );
@@ -120,12 +132,12 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index)
120132
121133 if (tlb_v0 ) {
122134 tlb_flush_range_by_mmuidx (env_cpu (env ), addr , pagesize ,
123- mmu_idx , TARGET_LONG_BITS );
135+ idxmap , TARGET_LONG_BITS );
124136 }
125137
126138 if (tlb_v1 ) {
127139 tlb_flush_range_by_mmuidx (env_cpu (env ), addr + pagesize , pagesize ,
128- mmu_idx , TARGET_LONG_BITS );
140+ idxmap , TARGET_LONG_BITS );
129141 }
130142}
131143
@@ -144,9 +156,8 @@ static void invalidate_tlb(CPULoongArchState *env, int index)
144156 invalidate_tlb_entry (env , index );
145157}
146158
147- static void fill_tlb_entry (CPULoongArchState * env , int index )
159+ static void fill_tlb_entry (CPULoongArchState * env , LoongArchTLB * tlb )
148160{
149- LoongArchTLB * tlb = & env -> tlb [index ];
150161 uint64_t lo0 , lo1 , csr_vppn ;
151162 uint16_t csr_asid ;
152163 uint8_t csr_ps ;
@@ -198,17 +209,18 @@ static uint32_t get_random_tlb(uint32_t low, uint32_t high)
198209 * field in tlb entry contains bit[47:13], so need adjust.
199210 * virt_vpn = vaddr[47:13]
200211 */
201- static bool loongarch_tlb_search (CPULoongArchState * env , vaddr vaddr ,
202- int * index )
212+ static LoongArchTLB * loongarch_tlb_search_cb (CPULoongArchState * env ,
213+ vaddr vaddr , int csr_asid ,
214+ tlb_match func )
203215{
204216 LoongArchTLB * tlb ;
205- uint16_t csr_asid , tlb_asid , stlb_idx ;
206- uint8_t tlb_e , tlb_ps , tlb_g , stlb_ps ;
217+ uint16_t tlb_asid , stlb_idx ;
218+ uint8_t tlb_e , tlb_ps , stlb_ps ;
219+ bool tlb_g ;
207220 int i , compare_shift ;
208221 uint64_t vpn , tlb_vppn ;
209222
210- csr_asid = FIELD_EX64 (env -> CSR_ASID , CSR_ASID , ASID );
211- stlb_ps = FIELD_EX64 (env -> CSR_STLBPS , CSR_STLBPS , PS );
223+ stlb_ps = FIELD_EX64 (env -> CSR_STLBPS , CSR_STLBPS , PS );
212224 vpn = (vaddr & TARGET_VIRT_MASK ) >> (stlb_ps + 1 );
213225 stlb_idx = vpn & 0xff ; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */
214226 compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT ;
@@ -220,12 +232,11 @@ static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr,
220232 if (tlb_e ) {
221233 tlb_vppn = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , VPPN );
222234 tlb_asid = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , ASID );
223- tlb_g = FIELD_EX64 (tlb -> tlb_entry0 , TLBENTRY , G );
235+ tlb_g = !! FIELD_EX64 (tlb -> tlb_entry0 , TLBENTRY , G );
224236
225- if ((tlb_g == 1 || tlb_asid == csr_asid ) &&
237+ if (func (tlb_g , csr_asid , tlb_asid ) &&
226238 (vpn == (tlb_vppn >> compare_shift ))) {
227- * index = i * 256 + stlb_idx ;
228- return true;
239+ return tlb ;
229240 }
230241 }
231242 }
@@ -241,13 +252,30 @@ static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr,
241252 tlb_g = FIELD_EX64 (tlb -> tlb_entry0 , TLBENTRY , G );
242253 compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT ;
243254 vpn = (vaddr & TARGET_VIRT_MASK ) >> (tlb_ps + 1 );
244- if ((tlb_g == 1 || tlb_asid == csr_asid ) &&
255+ if (func (tlb_g , csr_asid , tlb_asid ) &&
245256 (vpn == (tlb_vppn >> compare_shift ))) {
246- * index = i ;
247- return true;
257+ return tlb ;
248258 }
249259 }
250260 }
261+ return NULL ;
262+ }
263+
264+ static bool loongarch_tlb_search (CPULoongArchState * env , vaddr vaddr ,
265+ int * index )
266+ {
267+ int csr_asid ;
268+ tlb_match func ;
269+ LoongArchTLB * tlb ;
270+
271+ func = tlb_match_any ;
272+ csr_asid = FIELD_EX64 (env -> CSR_ASID , CSR_ASID , ASID );
273+ tlb = loongarch_tlb_search_cb (env , vaddr , csr_asid , func );
274+ if (tlb ) {
275+ * index = tlb - env -> tlb ;
276+ return true;
277+ }
278+
251279 return false;
252280}
253281
@@ -304,23 +332,44 @@ void helper_tlbrd(CPULoongArchState *env)
304332void helper_tlbwr (CPULoongArchState * env )
305333{
306334 int index = FIELD_EX64 (env -> CSR_TLBIDX , CSR_TLBIDX , INDEX );
335+ LoongArchTLB * old , new = {};
336+ bool skip_inv = false;
337+ uint8_t tlb_v0 , tlb_v1 ;
307338
308- invalidate_tlb (env , index );
309-
339+ old = env -> tlb + index ;
310340 if (FIELD_EX64 (env -> CSR_TLBIDX , CSR_TLBIDX , NE )) {
311- env -> tlb [index ].tlb_misc = FIELD_DP64 (env -> tlb [index ].tlb_misc ,
312- TLB_MISC , E , 0 );
341+ invalidate_tlb (env , index );
313342 return ;
314343 }
315344
316- fill_tlb_entry (env , index );
345+ fill_tlb_entry (env , & new );
346+ /* Check whether ASID/VPPN is the same */
347+ if (old -> tlb_misc == new .tlb_misc ) {
348+ /* Check whether both even/odd pages is the same or invalid */
349+ tlb_v0 = FIELD_EX64 (old -> tlb_entry0 , TLBENTRY , V );
350+ tlb_v1 = FIELD_EX64 (old -> tlb_entry1 , TLBENTRY , V );
351+ if ((!tlb_v0 || new .tlb_entry0 == old -> tlb_entry0 ) &&
352+ (!tlb_v1 || new .tlb_entry1 == old -> tlb_entry1 )) {
353+ skip_inv = true;
354+ }
355+ }
356+
357+ /* flush tlb before updating the entry */
358+ if (!skip_inv ) {
359+ invalidate_tlb (env , index );
360+ }
361+
362+ * old = new ;
317363}
318364
319365void helper_tlbfill (CPULoongArchState * env )
320366{
321367 uint64_t address , entryhi ;
322- int index , set , stlb_idx ;
368+ int index , set , i , stlb_idx ;
323369 uint16_t pagesize , stlb_ps ;
370+ uint16_t asid , tlb_asid ;
371+ LoongArchTLB * tlb ;
372+ uint8_t tlb_e ;
324373
325374 if (FIELD_EX64 (env -> CSR_TLBRERA , CSR_TLBRERA , ISTLBR )) {
326375 entryhi = env -> CSR_TLBREHI ;
@@ -334,24 +383,56 @@ void helper_tlbfill(CPULoongArchState *env)
334383
335384 /* Validity of stlb_ps is checked in helper_csrwr_stlbps() */
336385 stlb_ps = FIELD_EX64 (env -> CSR_STLBPS , CSR_STLBPS , PS );
386+ asid = FIELD_EX64 (env -> CSR_ASID , CSR_ASID , ASID );
337387 if (pagesize == stlb_ps ) {
338388 /* Only write into STLB bits [47:13] */
339389 address = entryhi & ~MAKE_64BIT_MASK (0 , R_CSR_TLBEHI_64_VPPN_SHIFT );
340-
341- /* Choose one set ramdomly */
342- set = get_random_tlb (0 , 7 );
343-
344- /* Index in one set */
390+ set = -1 ;
345391 stlb_idx = (address >> (stlb_ps + 1 )) & 0xff ; /* [0,255] */
392+ for (i = 0 ; i < 8 ; ++ i ) {
393+ tlb = & env -> tlb [i * 256 + stlb_idx ];
394+ tlb_e = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , E );
395+ if (!tlb_e ) {
396+ set = i ;
397+ break ;
398+ }
399+
400+ tlb_asid = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , ASID );
401+ if (asid != tlb_asid ) {
402+ set = i ;
403+ }
404+ }
346405
406+ /* Choose one set randomly */
407+ if (set < 0 ) {
408+ set = get_random_tlb (0 , 7 );
409+ }
347410 index = set * 256 + stlb_idx ;
348411 } else {
349412 /* Only write into MTLB */
350- index = get_random_tlb (LOONGARCH_STLB , LOONGARCH_TLB_MAX - 1 );
413+ index = -1 ;
414+ for (i = LOONGARCH_STLB ; i < LOONGARCH_TLB_MAX ; i ++ ) {
415+ tlb = & env -> tlb [i ];
416+ tlb_e = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , E );
417+
418+ if (!tlb_e ) {
419+ index = i ;
420+ break ;
421+ }
422+
423+ tlb_asid = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , ASID );
424+ if (asid != tlb_asid ) {
425+ index = i ;
426+ }
427+ }
428+
429+ if (index < 0 ) {
430+ index = get_random_tlb (LOONGARCH_STLB , LOONGARCH_TLB_MAX - 1 );
431+ }
351432 }
352433
353434 invalidate_tlb (env , index );
354- fill_tlb_entry (env , index );
435+ fill_tlb_entry (env , env -> tlb + index );
355436}
356437
357438void helper_tlbclr (CPULoongArchState * env )
@@ -453,61 +534,29 @@ void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info)
453534void helper_invtlb_page_asid (CPULoongArchState * env , target_ulong info ,
454535 target_ulong addr )
455536{
456- uint16_t asid = info & 0x3ff ;
457-
458- for (int i = 0 ; i < LOONGARCH_TLB_MAX ; i ++ ) {
459- LoongArchTLB * tlb = & env -> tlb [i ];
460- uint8_t tlb_g = FIELD_EX64 (tlb -> tlb_entry0 , TLBENTRY , G );
461- uint16_t tlb_asid = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , ASID );
462- uint64_t vpn , tlb_vppn ;
463- uint8_t tlb_ps , compare_shift ;
464- uint8_t tlb_e = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , E );
465-
466- if (!tlb_e ) {
467- continue ;
468- }
469-
470- tlb_ps = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , PS );
471- tlb_vppn = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , VPPN );
472- vpn = (addr & TARGET_VIRT_MASK ) >> (tlb_ps + 1 );
473- compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT ;
537+ int asid = info & 0x3ff ;
538+ LoongArchTLB * tlb ;
539+ tlb_match func ;
474540
475- if (! tlb_g && ( tlb_asid == asid ) &&
476- ( vpn == ( tlb_vppn >> compare_shift ))) {
477- tlb -> tlb_misc = FIELD_DP64 (tlb -> tlb_misc , TLB_MISC , E , 0 );
478- }
541+ func = tlb_match_asid ;
542+ tlb = loongarch_tlb_search_cb ( env , addr , asid , func );
543+ if (tlb ) {
544+ invalidate_tlb ( env , tlb - env -> tlb );
479545 }
480- tlb_flush (env_cpu (env ));
481546}
482547
483548void helper_invtlb_page_asid_or_g (CPULoongArchState * env ,
484549 target_ulong info , target_ulong addr )
485550{
486- uint16_t asid = info & 0x3ff ;
487-
488- for (int i = 0 ; i < LOONGARCH_TLB_MAX ; i ++ ) {
489- LoongArchTLB * tlb = & env -> tlb [i ];
490- uint8_t tlb_g = FIELD_EX64 (tlb -> tlb_entry0 , TLBENTRY , G );
491- uint16_t tlb_asid = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , ASID );
492- uint64_t vpn , tlb_vppn ;
493- uint8_t tlb_ps , compare_shift ;
494- uint8_t tlb_e = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , E );
495-
496- if (!tlb_e ) {
497- continue ;
498- }
499-
500- tlb_ps = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , PS );
501- tlb_vppn = FIELD_EX64 (tlb -> tlb_misc , TLB_MISC , VPPN );
502- vpn = (addr & TARGET_VIRT_MASK ) >> (tlb_ps + 1 );
503- compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT ;
551+ int asid = info & 0x3ff ;
552+ LoongArchTLB * tlb ;
553+ tlb_match func ;
504554
505- if (( tlb_g || ( tlb_asid == asid )) &&
506- ( vpn == ( tlb_vppn >> compare_shift ))) {
507- tlb -> tlb_misc = FIELD_DP64 (tlb -> tlb_misc , TLB_MISC , E , 0 );
508- }
555+ func = tlb_match_any ;
556+ tlb = loongarch_tlb_search_cb ( env , addr , asid , func );
557+ if (tlb ) {
558+ invalidate_tlb ( env , tlb - env -> tlb );
509559 }
510- tlb_flush (env_cpu (env ));
511560}
512561
513562bool loongarch_cpu_tlb_fill (CPUState * cs , vaddr address , int size ,
0 commit comments