1 // Copyright 2003 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "Core/PowerPC/MMU.h"
6 
7 #include <cstddef>
8 #include <cstring>
9 #include <string>
10 
11 #include "Common/BitUtils.h"
12 #include "Common/CommonTypes.h"
13 
14 #include "Core/ConfigManager.h"
15 #include "Core/HW/CPU.h"
16 #include "Core/HW/GPFifo.h"
17 #include "Core/HW/MMIO.h"
18 #include "Core/HW/Memmap.h"
19 #include "Core/PowerPC/JitInterface.h"
20 #include "Core/PowerPC/PowerPC.h"
21 
22 #include "VideoCommon/VideoBackendBase.h"
23 
24 #ifdef USE_GDBSTUB
25 #include "Core/PowerPC/GDBStub.h"
26 #endif
27 
28 namespace PowerPC
29 {
30 constexpr size_t HW_PAGE_SIZE = 4096;
31 constexpr u32 HW_PAGE_INDEX_SHIFT = 12;
32 constexpr u32 HW_PAGE_INDEX_MASK = 0x3f;
33 
34 // EFB RE
35 /*
36 GXPeekZ
37 80322de8: rlwinm    r0, r3, 2, 14, 29 (0003fffc)   a =  x << 2 & 0x3fffc
38 80322dec: oris      r0, r0, 0xC800                 a |= 0xc8000000
39 80322df0: rlwinm    r3, r0, 0, 20, 9 (ffc00fff)    x = a & 0xffc00fff
40 80322df4: rlwinm    r0, r4, 12, 4, 19 (0ffff000)   a = (y << 12) & 0x0ffff000;
41 80322df8: or        r0, r3, r0                     a |= x;
42 80322dfc: rlwinm    r0, r0, 0, 10, 7 (ff3fffff)    a &= 0xff3fffff
43 80322e00: oris      r3, r0, 0x0040                 x = a | 0x00400000
44 80322e04: lwz       r0, 0 (r3)                     r0 = *r3
45 80322e08: stw       r0, 0 (r5)                     z =
46 80322e0c: blr
47 */
48 
49 // =================================
50 // From Memmap.cpp
51 // ----------------
52 
53 // Overloaded byteswap functions, for use within the templated functions below.
bswap(u8 val)54 inline u8 bswap(u8 val)
55 {
56   return val;
57 }
bswap(s8 val)58 inline s8 bswap(s8 val)
59 {
60   return val;
61 }
bswap(u16 val)62 inline u16 bswap(u16 val)
63 {
64   return Common::swap16(val);
65 }
bswap(s16 val)66 inline s16 bswap(s16 val)
67 {
68   return Common::swap16(val);
69 }
bswap(u32 val)70 inline u32 bswap(u32 val)
71 {
72   return Common::swap32(val);
73 }
bswap(u64 val)74 inline u64 bswap(u64 val)
75 {
76   return Common::swap64(val);
77 }
78 // =================
79 
80 enum class XCheckTLBFlag
81 {
82   NoException,
83   Read,
84   Write,
85   Opcode,
86   OpcodeNoException
87 };
88 
IsOpcodeFlag(XCheckTLBFlag flag)89 static bool IsOpcodeFlag(XCheckTLBFlag flag)
90 {
91   return flag == XCheckTLBFlag::Opcode || flag == XCheckTLBFlag::OpcodeNoException;
92 }
93 
IsNoExceptionFlag(XCheckTLBFlag flag)94 static bool IsNoExceptionFlag(XCheckTLBFlag flag)
95 {
96   return flag == XCheckTLBFlag::NoException || flag == XCheckTLBFlag::OpcodeNoException;
97 }
98 
99 struct TranslateAddressResult
100 {
101   enum
102   {
103     BAT_TRANSLATED,
104     PAGE_TABLE_TRANSLATED,
105     DIRECT_STORE_SEGMENT,
106     PAGE_FAULT
107   } result;
108   u32 address;
SuccessPowerPC::TranslateAddressResult109   bool Success() const { return result <= PAGE_TABLE_TRANSLATED; }
110 };
111 template <const XCheckTLBFlag flag>
112 static TranslateAddressResult TranslateAddress(u32 address);
113 
114 // Nasty but necessary. Super Mario Galaxy pointer relies on this stuff.
EFB_Read(const u32 addr)115 static u32 EFB_Read(const u32 addr)
116 {
117   u32 var = 0;
118   // Convert address to coordinates. It's possible that this should be done
119   // differently depending on color depth, especially regarding PeekColor.
120   const u32 x = (addr & 0xfff) >> 2;
121   const u32 y = (addr >> 12) & 0x3ff;
122 
123   if (addr & 0x00800000)
124   {
125     ERROR_LOG(MEMMAP, "Unimplemented Z+Color EFB read @ 0x%08x", addr);
126   }
127   else if (addr & 0x00400000)
128   {
129     var = g_video_backend->Video_AccessEFB(EFBAccessType::PeekZ, x, y, 0);
130     DEBUG_LOG(MEMMAP, "EFB Z Read @ %u, %u\t= 0x%08x", x, y, var);
131   }
132   else
133   {
134     var = g_video_backend->Video_AccessEFB(EFBAccessType::PeekColor, x, y, 0);
135     DEBUG_LOG(MEMMAP, "EFB Color Read @ %u, %u\t= 0x%08x", x, y, var);
136   }
137 
138   return var;
139 }
140 
EFB_Write(u32 data,u32 addr)141 static void EFB_Write(u32 data, u32 addr)
142 {
143   const u32 x = (addr & 0xfff) >> 2;
144   const u32 y = (addr >> 12) & 0x3ff;
145 
146   if (addr & 0x00800000)
147   {
148     // It's possible to do a z-tested write to EFB by writing a 64bit value to this address range.
149     // Not much is known, but let's at least get some loging.
150     ERROR_LOG(MEMMAP, "Unimplemented Z+Color EFB write. %08x @ 0x%08x", data, addr);
151   }
152   else if (addr & 0x00400000)
153   {
154     g_video_backend->Video_AccessEFB(EFBAccessType::PokeZ, x, y, data);
155     DEBUG_LOG(MEMMAP, "EFB Z Write %08x @ %u, %u", data, x, y);
156   }
157   else
158   {
159     g_video_backend->Video_AccessEFB(EFBAccessType::PokeColor, x, y, data);
160     DEBUG_LOG(MEMMAP, "EFB Color Write %08x @ %u, %u", data, x, y);
161   }
162 }
163 
164 BatTable ibat_table;
165 BatTable dbat_table;
166 
167 static void GenerateDSIException(u32 effective_address, bool write);
168 
169 template <XCheckTLBFlag flag, typename T, bool never_translate = false>
ReadFromHardware(u32 em_address)170 static T ReadFromHardware(u32 em_address)
171 {
172   if (!never_translate && MSR.DR)
173   {
174     auto translated_addr = TranslateAddress<flag>(em_address);
175     if (!translated_addr.Success())
176     {
177       if (flag == XCheckTLBFlag::Read)
178         GenerateDSIException(em_address, false);
179       return 0;
180     }
181     if ((em_address & (HW_PAGE_SIZE - 1)) > HW_PAGE_SIZE - sizeof(T))
182     {
183       // This could be unaligned down to the byte level... hopefully this is rare, so doing it this
184       // way isn't too terrible.
185       // TODO: floats on non-word-aligned boundaries should technically cause alignment exceptions.
186       // Note that "word" means 32-bit, so paired singles or doubles might still be 32-bit aligned!
187       u32 em_address_next_page = (em_address + sizeof(T) - 1) & ~(HW_PAGE_SIZE - 1);
188       auto addr_next_page = TranslateAddress<flag>(em_address_next_page);
189       if (!addr_next_page.Success())
190       {
191         if (flag == XCheckTLBFlag::Read)
192           GenerateDSIException(em_address_next_page, false);
193         return 0;
194       }
195       T var = 0;
196       u32 addr_translated = translated_addr.address;
197       for (u32 addr = em_address; addr < em_address + sizeof(T); addr++, addr_translated++)
198       {
199         if (addr == em_address_next_page)
200           addr_translated = addr_next_page.address;
201         var = (var << 8) | ReadFromHardware<flag, u8, true>(addr_translated);
202       }
203       return var;
204     }
205     em_address = translated_addr.address;
206   }
207 
208   // TODO: Make sure these are safe for unaligned addresses.
209 
210   if ((em_address & 0xF8000000) == 0x00000000)
211   {
212     // Handle RAM; the masking intentionally discards bits (essentially creating
213     // mirrors of memory).
214     // TODO: Only the first GetRamSizeReal() is supposed to be backed by actual memory.
215     T value;
216     std::memcpy(&value, &Memory::m_pRAM[em_address & Memory::GetRamMask()], sizeof(T));
217     return bswap(value);
218   }
219 
220   if (Memory::m_pEXRAM && (em_address >> 28) == 0x1 &&
221       (em_address & 0x0FFFFFFF) < Memory::GetExRamSizeReal())
222   {
223     T value;
224     std::memcpy(&value, &Memory::m_pEXRAM[em_address & 0x0FFFFFFF], sizeof(T));
225     return bswap(value);
226   }
227 
228   // Locked L1 technically doesn't have a fixed address, but games all use 0xE0000000.
229   if ((em_address >> 28) == 0xE && (em_address < (0xE0000000 + Memory::GetL1CacheSize())))
230   {
231     T value;
232     std::memcpy(&value, &Memory::m_pL1Cache[em_address & 0x0FFFFFFF], sizeof(T));
233     return bswap(value);
234   }
235   // In Fake-VMEM mode, we need to map the memory somewhere into
236   // physical memory for BAT translation to work; we currently use
237   // [0x7E000000, 0x80000000).
238   if (Memory::m_pFakeVMEM && ((em_address & 0xFE000000) == 0x7E000000))
239   {
240     T value;
241     std::memcpy(&value, &Memory::m_pFakeVMEM[em_address & Memory::GetRamMask()], sizeof(T));
242     return bswap(value);
243   }
244 
245   if (flag == XCheckTLBFlag::Read && (em_address & 0xF8000000) == 0x08000000)
246   {
247     if (em_address < 0x0c000000)
248       return EFB_Read(em_address);
249     else
250       return (T)Memory::mmio_mapping->Read<typename std::make_unsigned<T>::type>(em_address);
251   }
252 
253   PanicAlert("Unable to resolve read address %x PC %x", em_address, PC);
254   return 0;
255 }
256 
257 template <XCheckTLBFlag flag, typename T, bool never_translate = false>
WriteToHardware(u32 em_address,const T data)258 static void WriteToHardware(u32 em_address, const T data)
259 {
260   if (!never_translate && MSR.DR)
261   {
262     auto translated_addr = TranslateAddress<flag>(em_address);
263     if (!translated_addr.Success())
264     {
265       if (flag == XCheckTLBFlag::Write)
266         GenerateDSIException(em_address, true);
267       return;
268     }
269     if ((em_address & (sizeof(T) - 1)) &&
270         (em_address & (HW_PAGE_SIZE - 1)) > HW_PAGE_SIZE - sizeof(T))
271     {
272       // This could be unaligned down to the byte level... hopefully this is rare, so doing it this
273       // way isn't too terrible.
274       // TODO: floats on non-word-aligned boundaries should technically cause alignment exceptions.
275       // Note that "word" means 32-bit, so paired singles or doubles might still be 32-bit aligned!
276       u32 em_address_next_page = (em_address + sizeof(T) - 1) & ~(HW_PAGE_SIZE - 1);
277       auto addr_next_page = TranslateAddress<flag>(em_address_next_page);
278       if (!addr_next_page.Success())
279       {
280         if (flag == XCheckTLBFlag::Write)
281           GenerateDSIException(em_address_next_page, true);
282         return;
283       }
284       T val = bswap(data);
285       u32 addr_translated = translated_addr.address;
286       for (size_t i = 0; i < sizeof(T); i++, addr_translated++)
287       {
288         if (em_address + i == em_address_next_page)
289           addr_translated = addr_next_page.address;
290         WriteToHardware<flag, u8, true>(addr_translated, static_cast<u8>(val >> (i * 8)));
291       }
292       return;
293     }
294     em_address = translated_addr.address;
295   }
296 
297   // TODO: Make sure these are safe for unaligned addresses.
298 
299   if ((em_address & 0xF8000000) == 0x00000000)
300   {
301     // Handle RAM; the masking intentionally discards bits (essentially creating
302     // mirrors of memory).
303     // TODO: Only the first GetRamSizeReal() is supposed to be backed by actual memory.
304     const T swapped_data = bswap(data);
305     std::memcpy(&Memory::m_pRAM[em_address & Memory::GetRamMask()], &swapped_data, sizeof(T));
306     return;
307   }
308 
309   if (Memory::m_pEXRAM && (em_address >> 28) == 0x1 &&
310       (em_address & 0x0FFFFFFF) < Memory::GetExRamSizeReal())
311   {
312     const T swapped_data = bswap(data);
313     std::memcpy(&Memory::m_pEXRAM[em_address & 0x0FFFFFFF], &swapped_data, sizeof(T));
314     return;
315   }
316 
317   // Locked L1 technically doesn't have a fixed address, but games all use 0xE0000000.
318   if ((em_address >> 28 == 0xE) && (em_address < (0xE0000000 + Memory::GetL1CacheSize())))
319   {
320     const T swapped_data = bswap(data);
321     std::memcpy(&Memory::m_pL1Cache[em_address & 0x0FFFFFFF], &swapped_data, sizeof(T));
322     return;
323   }
324 
325   // In Fake-VMEM mode, we need to map the memory somewhere into
326   // physical memory for BAT translation to work; we currently use
327   // [0x7E000000, 0x80000000).
328   if (Memory::m_pFakeVMEM && ((em_address & 0xFE000000) == 0x7E000000))
329   {
330     const T swapped_data = bswap(data);
331     std::memcpy(&Memory::m_pFakeVMEM[em_address & Memory::GetRamMask()], &swapped_data, sizeof(T));
332     return;
333   }
334 
335   // Check for a gather pipe write.
336   // Note that we must mask the address to correctly emulate certain games;
337   // Pac-Man World 3 in particular is affected by this.
338   if (flag == XCheckTLBFlag::Write && (em_address & 0xFFFFF000) == 0x0C008000)
339   {
340     switch (sizeof(T))
341     {
342     case 1:
343       GPFifo::Write8((u8)data);
344       return;
345     case 2:
346       GPFifo::Write16((u16)data);
347       return;
348     case 4:
349       GPFifo::Write32((u32)data);
350       return;
351     case 8:
352       GPFifo::Write64((u64)data);
353       return;
354     }
355   }
356 
357   if (flag == XCheckTLBFlag::Write && (em_address & 0xF8000000) == 0x08000000)
358   {
359     if (em_address < 0x0c000000)
360     {
361       EFB_Write((u32)data, em_address);
362       return;
363     }
364     else
365     {
366       Memory::mmio_mapping->Write(em_address, data);
367       return;
368     }
369   }
370 
371   PanicAlert("Unable to resolve write address %x PC %x", em_address, PC);
372   return;
373 }
374 // =====================
375 
376 // =================================
377 /* These functions are primarily called by the Interpreter functions and are routed to the correct
378    location through ReadFromHardware and WriteToHardware */
379 // ----------------
380 
381 static void GenerateISIException(u32 effective_address);
382 
Read_Opcode(u32 address)383 u32 Read_Opcode(u32 address)
384 {
385   TryReadInstResult result = TryReadInstruction(address);
386   if (!result.valid)
387   {
388     GenerateISIException(address);
389     return 0;
390   }
391   return result.hex;
392 }
393 
TryReadInstruction(u32 address)394 TryReadInstResult TryReadInstruction(u32 address)
395 {
396   bool from_bat = true;
397   if (MSR.IR)
398   {
399     auto tlb_addr = TranslateAddress<XCheckTLBFlag::Opcode>(address);
400     if (!tlb_addr.Success())
401     {
402       return TryReadInstResult{false, false, 0, 0};
403     }
404     else
405     {
406       address = tlb_addr.address;
407       from_bat = tlb_addr.result == TranslateAddressResult::BAT_TRANSLATED;
408     }
409   }
410 
411   u32 hex;
412   // TODO: Refactor this. This icache implementation is totally wrong if used with the fake vmem.
413   if (Memory::m_pFakeVMEM && ((address & 0xFE000000) == 0x7E000000))
414   {
415     hex = Common::swap32(&Memory::m_pFakeVMEM[address & Memory::GetFakeVMemMask()]);
416   }
417   else
418   {
419     hex = PowerPC::ppcState.iCache.ReadInstruction(address);
420   }
421   return TryReadInstResult{true, from_bat, hex, address};
422 }
423 
HostRead_Instruction(const u32 address)424 u32 HostRead_Instruction(const u32 address)
425 {
426   return ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(address);
427 }
428 
Memcheck(u32 address,u32 var,bool write,size_t size)429 static void Memcheck(u32 address, u32 var, bool write, size_t size)
430 {
431   if (PowerPC::memchecks.HasAny())
432   {
433     TMemCheck* mc = PowerPC::memchecks.GetMemCheck(address, size);
434     if (mc)
435     {
436       if (CPU::IsStepping())
437       {
438         // Disable when stepping so that resume works.
439         return;
440       }
441       mc->num_hits++;
442       bool pause = mc->Action(&PowerPC::debug_interface, var, address, write, size, PC);
443       if (pause)
444       {
445         CPU::Break();
446         // Fake a DSI so that all the code that tests for it in order to skip
447         // the rest of the instruction will apply.  (This means that
448         // watchpoints will stop the emulator before the offending load/store,
449         // not after like GDB does, but that's better anyway.  Just need to
450         // make sure resuming after that works.)
451         // It doesn't matter if ReadFromHardware triggers its own DSI because
452         // we'll take it after resuming.
453         PowerPC::ppcState.Exceptions |= EXCEPTION_DSI | EXCEPTION_FAKE_MEMCHECK_HIT;
454       }
455     }
456   }
457 }
458 
Read_U8(const u32 address)459 u8 Read_U8(const u32 address)
460 {
461   u8 var = ReadFromHardware<XCheckTLBFlag::Read, u8>(address);
462   Memcheck(address, var, false, 1);
463   return var;
464 }
465 
Read_U16(const u32 address)466 u16 Read_U16(const u32 address)
467 {
468   u16 var = ReadFromHardware<XCheckTLBFlag::Read, u16>(address);
469   Memcheck(address, var, false, 2);
470   return var;
471 }
472 
Read_U32(const u32 address)473 u32 Read_U32(const u32 address)
474 {
475   u32 var = ReadFromHardware<XCheckTLBFlag::Read, u32>(address);
476   Memcheck(address, var, false, 4);
477   return var;
478 }
479 
Read_U64(const u32 address)480 u64 Read_U64(const u32 address)
481 {
482   u64 var = ReadFromHardware<XCheckTLBFlag::Read, u64>(address);
483   Memcheck(address, (u32)var, false, 8);
484   return var;
485 }
486 
Read_F64(const u32 address)487 double Read_F64(const u32 address)
488 {
489   const u64 integral = Read_U64(address);
490 
491   return Common::BitCast<double>(integral);
492 }
493 
Read_F32(const u32 address)494 float Read_F32(const u32 address)
495 {
496   const u32 integral = Read_U32(address);
497 
498   return Common::BitCast<float>(integral);
499 }
500 
Read_U8_ZX(const u32 address)501 u32 Read_U8_ZX(const u32 address)
502 {
503   return Read_U8(address);
504 }
505 
Read_U16_ZX(const u32 address)506 u32 Read_U16_ZX(const u32 address)
507 {
508   return Read_U16(address);
509 }
510 
Write_U8(const u8 var,const u32 address)511 void Write_U8(const u8 var, const u32 address)
512 {
513   Memcheck(address, var, true, 1);
514   WriteToHardware<XCheckTLBFlag::Write, u8>(address, var);
515 }
516 
Write_U16(const u16 var,const u32 address)517 void Write_U16(const u16 var, const u32 address)
518 {
519   Memcheck(address, var, true, 2);
520   WriteToHardware<XCheckTLBFlag::Write, u16>(address, var);
521 }
Write_U16_Swap(const u16 var,const u32 address)522 void Write_U16_Swap(const u16 var, const u32 address)
523 {
524   Memcheck(address, var, true, 2);
525   Write_U16(Common::swap16(var), address);
526 }
527 
Write_U32(const u32 var,const u32 address)528 void Write_U32(const u32 var, const u32 address)
529 {
530   Memcheck(address, var, true, 4);
531   WriteToHardware<XCheckTLBFlag::Write, u32>(address, var);
532 }
Write_U32_Swap(const u32 var,const u32 address)533 void Write_U32_Swap(const u32 var, const u32 address)
534 {
535   Memcheck(address, var, true, 4);
536   Write_U32(Common::swap32(var), address);
537 }
538 
Write_U64(const u64 var,const u32 address)539 void Write_U64(const u64 var, const u32 address)
540 {
541   Memcheck(address, (u32)var, true, 8);
542   WriteToHardware<XCheckTLBFlag::Write, u64>(address, var);
543 }
Write_U64_Swap(const u64 var,const u32 address)544 void Write_U64_Swap(const u64 var, const u32 address)
545 {
546   Memcheck(address, (u32)var, true, 8);
547   Write_U64(Common::swap64(var), address);
548 }
549 
Write_F64(const double var,const u32 address)550 void Write_F64(const double var, const u32 address)
551 {
552   const u64 integral = Common::BitCast<u64>(var);
553 
554   Write_U64(integral, address);
555 }
556 
HostRead_U8(const u32 address)557 u8 HostRead_U8(const u32 address)
558 {
559   return ReadFromHardware<XCheckTLBFlag::NoException, u8>(address);
560 }
561 
HostRead_U16(const u32 address)562 u16 HostRead_U16(const u32 address)
563 {
564   return ReadFromHardware<XCheckTLBFlag::NoException, u16>(address);
565 }
566 
HostRead_U32(const u32 address)567 u32 HostRead_U32(const u32 address)
568 {
569   return ReadFromHardware<XCheckTLBFlag::NoException, u32>(address);
570 }
571 
HostRead_U64(const u32 address)572 u64 HostRead_U64(const u32 address)
573 {
574   return ReadFromHardware<XCheckTLBFlag::NoException, u64>(address);
575 }
576 
HostRead_F32(const u32 address)577 float HostRead_F32(const u32 address)
578 {
579   const u32 integral = HostRead_U32(address);
580 
581   return Common::BitCast<float>(integral);
582 }
583 
HostRead_F64(const u32 address)584 double HostRead_F64(const u32 address)
585 {
586   const u64 integral = HostRead_U64(address);
587 
588   return Common::BitCast<double>(integral);
589 }
590 
HostWrite_U8(const u8 var,const u32 address)591 void HostWrite_U8(const u8 var, const u32 address)
592 {
593   WriteToHardware<XCheckTLBFlag::NoException, u8>(address, var);
594 }
595 
HostWrite_U16(const u16 var,const u32 address)596 void HostWrite_U16(const u16 var, const u32 address)
597 {
598   WriteToHardware<XCheckTLBFlag::NoException, u16>(address, var);
599 }
600 
HostWrite_U32(const u32 var,const u32 address)601 void HostWrite_U32(const u32 var, const u32 address)
602 {
603   WriteToHardware<XCheckTLBFlag::NoException, u32>(address, var);
604 }
605 
HostWrite_U64(const u64 var,const u32 address)606 void HostWrite_U64(const u64 var, const u32 address)
607 {
608   WriteToHardware<XCheckTLBFlag::NoException, u64>(address, var);
609 }
610 
HostWrite_F32(const float var,const u32 address)611 void HostWrite_F32(const float var, const u32 address)
612 {
613   const u32 integral = Common::BitCast<u32>(var);
614 
615   HostWrite_U32(integral, address);
616 }
617 
HostWrite_F64(const double var,const u32 address)618 void HostWrite_F64(const double var, const u32 address)
619 {
620   const u64 integral = Common::BitCast<u64>(var);
621 
622   HostWrite_U64(integral, address);
623 }
624 
HostGetString(u32 address,size_t size)625 std::string HostGetString(u32 address, size_t size)
626 {
627   std::string s;
628   do
629   {
630     if (!HostIsRAMAddress(address))
631       break;
632     u8 res = HostRead_U8(address);
633     if (!res)
634       break;
635     s += static_cast<char>(res);
636     ++address;
637   } while (size == 0 || s.length() < size);
638   return s;
639 }
640 
IsOptimizableRAMAddress(const u32 address)641 bool IsOptimizableRAMAddress(const u32 address)
642 {
643   if (PowerPC::memchecks.HasAny())
644     return false;
645 
646   if (!MSR.DR)
647     return false;
648 
649   // TODO: This API needs to take an access size
650   //
651   // We store whether an access can be optimized to an unchecked access
652   // in dbat_table.
653   u32 bat_result = dbat_table[address >> BAT_INDEX_SHIFT];
654   return (bat_result & BAT_PHYSICAL_BIT) != 0;
655 }
656 
657 template <XCheckTLBFlag flag>
IsRAMAddress(u32 address,bool translate)658 static bool IsRAMAddress(u32 address, bool translate)
659 {
660   if (translate)
661   {
662     auto translate_address = TranslateAddress<flag>(address);
663     if (!translate_address.Success())
664       return false;
665     address = translate_address.address;
666   }
667 
668   u32 segment = address >> 28;
669   if (segment == 0x0 && (address & 0x0FFFFFFF) < Memory::GetRamSizeReal())
670     return true;
671   else if (Memory::m_pEXRAM && segment == 0x1 &&
672            (address & 0x0FFFFFFF) < Memory::GetExRamSizeReal())
673     return true;
674   else if (Memory::m_pFakeVMEM && ((address & 0xFE000000) == 0x7E000000))
675     return true;
676   else if (segment == 0xE && (address < (0xE0000000 + Memory::GetL1CacheSize())))
677     return true;
678   return false;
679 }
680 
HostIsRAMAddress(u32 address)681 bool HostIsRAMAddress(u32 address)
682 {
683   return IsRAMAddress<XCheckTLBFlag::NoException>(address, MSR.DR);
684 }
685 
HostIsInstructionRAMAddress(u32 address)686 bool HostIsInstructionRAMAddress(u32 address)
687 {
688   // Instructions are always 32bit aligned.
689   return !(address & 3) && IsRAMAddress<XCheckTLBFlag::OpcodeNoException>(address, MSR.IR);
690 }
691 
DMA_LCToMemory(const u32 mem_address,const u32 cache_address,const u32 num_blocks)692 void DMA_LCToMemory(const u32 mem_address, const u32 cache_address, const u32 num_blocks)
693 {
694   // TODO: It's not completely clear this is the right spot for this code;
695   // what would happen if, for example, the DVD drive tried to write to the EFB?
696   // TODO: This is terribly slow.
697   // TODO: Refactor.
698   // Avatar: The Last Airbender (GC) uses this for videos.
699   if ((mem_address & 0x0F000000) == 0x08000000)
700   {
701     for (u32 i = 0; i < 32 * num_blocks; i += 4)
702     {
703       const u32 data = Common::swap32(Memory::m_pL1Cache + ((cache_address + i) & 0x3FFFF));
704       EFB_Write(data, mem_address + i);
705     }
706     return;
707   }
708 
709   // No known game uses this; here for completeness.
710   // TODO: Refactor.
711   if ((mem_address & 0x0F000000) == 0x0C000000)
712   {
713     for (u32 i = 0; i < 32 * num_blocks; i += 4)
714     {
715       const u32 data = Common::swap32(Memory::m_pL1Cache + ((cache_address + i) & 0x3FFFF));
716       Memory::mmio_mapping->Write(mem_address + i, data);
717     }
718     return;
719   }
720 
721   const u8* src = Memory::m_pL1Cache + (cache_address & 0x3FFFF);
722   u8* dst = Memory::GetPointer(mem_address);
723   if (dst == nullptr)
724     return;
725 
726   memcpy(dst, src, 32 * num_blocks);
727 }
728 
DMA_MemoryToLC(const u32 cache_address,const u32 mem_address,const u32 num_blocks)729 void DMA_MemoryToLC(const u32 cache_address, const u32 mem_address, const u32 num_blocks)
730 {
731   const u8* src = Memory::GetPointer(mem_address);
732   u8* dst = Memory::m_pL1Cache + (cache_address & 0x3FFFF);
733 
734   // No known game uses this; here for completeness.
735   // TODO: Refactor.
736   if ((mem_address & 0x0F000000) == 0x08000000)
737   {
738     for (u32 i = 0; i < 32 * num_blocks; i += 4)
739     {
740       const u32 data = Common::swap32(EFB_Read(mem_address + i));
741       std::memcpy(Memory::m_pL1Cache + ((cache_address + i) & 0x3FFFF), &data, sizeof(u32));
742     }
743     return;
744   }
745 
746   // No known game uses this.
747   // TODO: Refactor.
748   if ((mem_address & 0x0F000000) == 0x0C000000)
749   {
750     for (u32 i = 0; i < 32 * num_blocks; i += 4)
751     {
752       const u32 data = Common::swap32(Memory::mmio_mapping->Read<u32>(mem_address + i));
753       std::memcpy(Memory::m_pL1Cache + ((cache_address + i) & 0x3FFFF), &data, sizeof(u32));
754     }
755     return;
756   }
757 
758   if (src == nullptr)
759     return;
760 
761   memcpy(dst, src, 32 * num_blocks);
762 }
763 
ClearCacheLine(u32 address)764 void ClearCacheLine(u32 address)
765 {
766   DEBUG_ASSERT((address & 0x1F) == 0);
767   if (MSR.DR)
768   {
769     auto translated_address = TranslateAddress<XCheckTLBFlag::Write>(address);
770     if (translated_address.result == TranslateAddressResult::DIRECT_STORE_SEGMENT)
771     {
772       // dcbz to direct store segments is ignored. This is a little
773       // unintuitive, but this is consistent with both console and the PEM.
774       // Advance Game Port crashes if we don't emulate this correctly.
775       return;
776     }
777     if (translated_address.result == TranslateAddressResult::PAGE_FAULT)
778     {
779       // If translation fails, generate a DSI.
780       GenerateDSIException(address, true);
781       return;
782     }
783     address = translated_address.address;
784   }
785 
786   // TODO: This isn't precisely correct for non-RAM regions, but the difference
787   // is unlikely to matter.
788   for (u32 i = 0; i < 32; i += 8)
789     WriteToHardware<XCheckTLBFlag::Write, u64, true>(address + i, 0);
790 }
791 
IsOptimizableMMIOAccess(u32 address,u32 access_size)792 u32 IsOptimizableMMIOAccess(u32 address, u32 access_size)
793 {
794   if (PowerPC::memchecks.HasAny())
795     return 0;
796 
797   if (!MSR.DR)
798     return 0;
799 
800   // Translate address
801   // If we also optimize for TLB mappings, we'd have to clear the
802   // JitCache on each TLB invalidation.
803   if (!TranslateBatAddess(dbat_table, &address))
804     return 0;
805 
806   // Check whether the address is an aligned address of an MMIO register.
807   const bool aligned = (address & ((access_size >> 3) - 1)) == 0;
808   if (!aligned || !MMIO::IsMMIOAddress(address))
809     return 0;
810 
811   return address;
812 }
813 
IsOptimizableGatherPipeWrite(u32 address)814 bool IsOptimizableGatherPipeWrite(u32 address)
815 {
816   if (PowerPC::memchecks.HasAny())
817     return false;
818 
819   if (!MSR.DR)
820     return false;
821 
822   // Translate address, only check BAT mapping.
823   // If we also optimize for TLB mappings, we'd have to clear the
824   // JitCache on each TLB invalidation.
825   if (!TranslateBatAddess(dbat_table, &address))
826     return false;
827 
828   // Check whether the translated address equals the address in WPAR.
829   return address == 0x0C008000;
830 }
831 
JitCache_TranslateAddress(u32 address)832 TranslateResult JitCache_TranslateAddress(u32 address)
833 {
834   if (!MSR.IR)
835     return TranslateResult{true, true, address};
836 
837   // TODO: We shouldn't use FLAG_OPCODE if the caller is the debugger.
838   auto tlb_addr = TranslateAddress<XCheckTLBFlag::Opcode>(address);
839   if (!tlb_addr.Success())
840   {
841     return TranslateResult{false, false, 0};
842   }
843 
844   bool from_bat = tlb_addr.result == TranslateAddressResult::BAT_TRANSLATED;
845   return TranslateResult{true, from_bat, tlb_addr.address};
846 }
847 
848 // *********************************************************************************
849 // Warning: Test Area
850 //
851 // This code is for TESTING and it works in interpreter mode ONLY. Some games (like
852 // COD iirc) work thanks to this basic TLB emulation.
853 // It is just a small hack and we have never spend enough time to finalize it.
854 // Cheers PearPC!
855 //
856 // *********************************************************************************
857 
858 /*
859  * PearPC
860  * ppc_mmu.cc
861  *
862  * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net)
863  *
864  * This program is free software; you can redistribute it and/or modify
865  * it under the terms of the GNU General Public License version 2 as
866  * published by the Free Software Foundation.
867  *
868  * This program is distributed in the hope that it will be useful,
869  * but WITHOUT ANY WARRANTY; without even the implied warranty of
870  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
871  * GNU General Public License for more details.
872  *
873  * You should have received a copy of the GNU General Public License
874  * along with this program; if not, write to the Free Software
875  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
876  */
877 
878 #define PPC_EXC_DSISR_PAGE (1 << 30)
879 #define PPC_EXC_DSISR_PROT (1 << 27)
880 #define PPC_EXC_DSISR_STORE (1 << 25)
881 
882 #define SDR1_HTABORG(v) (((v) >> 16) & 0xffff)
883 #define SDR1_HTABMASK(v) ((v)&0x1ff)
884 #define SDR1_PAGETABLE_BASE(v) ((v)&0xffff)
885 #define SR_T (1 << 31)
886 #define SR_Ks (1 << 30)
887 #define SR_Kp (1 << 29)
888 #define SR_N (1 << 28)
889 #define SR_VSID(v) ((v)&0xffffff)
890 #define SR_BUID(v) (((v) >> 20) & 0x1ff)
891 #define SR_CNTRL_SPEC(v) ((v)&0xfffff)
892 
893 #define EA_SR(v) (((v) >> 28) & 0xf)
894 #define EA_PageIndex(v) (((v) >> 12) & 0xffff)
895 #define EA_Offset(v) ((v)&0xfff)
896 #define EA_API(v) (((v) >> 22) & 0x3f)
897 
898 #define PA_RPN(v) (((v) >> 12) & 0xfffff)
899 #define PA_Offset(v) ((v)&0xfff)
900 
901 #define PTE1_V (1 << 31)
902 #define PTE1_VSID(v) (((v) >> 7) & 0xffffff)
903 #define PTE1_H (1 << 6)
904 #define PTE1_API(v) ((v)&0x3f)
905 
906 #define PTE2_RPN(v) ((v)&0xfffff000)
907 #define PTE2_R (1 << 8)
908 #define PTE2_C (1 << 7)
909 #define PTE2_WIMG(v) (((v) >> 3) & 0xf)
910 #define PTE2_PP(v) ((v)&3)
911 
912 // Hey! these duplicate a structure in Gekko.h
913 union UPTE1
914 {
915   struct
916   {
917     u32 API : 6;
918     u32 H : 1;
919     u32 VSID : 24;
920     u32 V : 1;
921   };
922   u32 Hex;
923 };
924 
925 union UPTE2
926 {
927   struct
928   {
929     u32 PP : 2;
930     u32 : 1;
931     u32 WIMG : 4;
932     u32 C : 1;
933     u32 R : 1;
934     u32 : 3;
935     u32 RPN : 20;
936   };
937   u32 Hex;
938 };
939 
GenerateDSIException(u32 effective_address,bool write)940 static void GenerateDSIException(u32 effective_address, bool write)
941 {
942   // DSI exceptions are only supported in MMU mode.
943   if (!SConfig::GetInstance().bMMU)
944   {
945     PanicAlert("Invalid %s 0x%08x, PC = 0x%08x ", write ? "write to" : "read from",
946                effective_address, PC);
947     return;
948   }
949 
950   if (effective_address)
951     PowerPC::ppcState.spr[SPR_DSISR] = PPC_EXC_DSISR_PAGE | PPC_EXC_DSISR_STORE;
952   else
953     PowerPC::ppcState.spr[SPR_DSISR] = PPC_EXC_DSISR_PAGE;
954 
955   PowerPC::ppcState.spr[SPR_DAR] = effective_address;
956 
957   PowerPC::ppcState.Exceptions |= EXCEPTION_DSI;
958 }
959 
GenerateISIException(u32 effective_address)960 static void GenerateISIException(u32 effective_address)
961 {
962   // Address of instruction could not be translated
963   NPC = effective_address;
964 
965   PowerPC::ppcState.Exceptions |= EXCEPTION_ISI;
966   WARN_LOG(POWERPC, "ISI exception at 0x%08x", PC);
967 }
968 
SDRUpdated()969 void SDRUpdated()
970 {
971   u32 htabmask = SDR1_HTABMASK(PowerPC::ppcState.spr[SPR_SDR]);
972   if (!Common::IsValidLowMask(htabmask))
973   {
974     return;
975   }
976   u32 htaborg = SDR1_HTABORG(PowerPC::ppcState.spr[SPR_SDR]);
977   if (htaborg & htabmask)
978   {
979     return;
980   }
981   PowerPC::ppcState.pagetable_base = htaborg << 16;
982   PowerPC::ppcState.pagetable_hashmask = ((htabmask << 10) | 0x3ff);
983 }
984 
985 enum class TLBLookupResult
986 {
987   Found,
988   NotFound,
989   UpdateC
990 };
991 
LookupTLBPageAddress(const XCheckTLBFlag flag,const u32 vpa,u32 * paddr)992 static TLBLookupResult LookupTLBPageAddress(const XCheckTLBFlag flag, const u32 vpa, u32* paddr)
993 {
994   const u32 tag = vpa >> HW_PAGE_INDEX_SHIFT;
995   TLBEntry& tlbe = ppcState.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK];
996 
997   if (tlbe.tag[0] == tag)
998   {
999     // Check if C bit requires updating
1000     if (flag == XCheckTLBFlag::Write)
1001     {
1002       UPTE2 PTE2;
1003       PTE2.Hex = tlbe.pte[0];
1004       if (PTE2.C == 0)
1005       {
1006         PTE2.C = 1;
1007         tlbe.pte[0] = PTE2.Hex;
1008         return TLBLookupResult::UpdateC;
1009       }
1010     }
1011 
1012     if (!IsNoExceptionFlag(flag))
1013       tlbe.recent = 0;
1014 
1015     *paddr = tlbe.paddr[0] | (vpa & 0xfff);
1016 
1017     return TLBLookupResult::Found;
1018   }
1019   if (tlbe.tag[1] == tag)
1020   {
1021     // Check if C bit requires updating
1022     if (flag == XCheckTLBFlag::Write)
1023     {
1024       UPTE2 PTE2;
1025       PTE2.Hex = tlbe.pte[1];
1026       if (PTE2.C == 0)
1027       {
1028         PTE2.C = 1;
1029         tlbe.pte[1] = PTE2.Hex;
1030         return TLBLookupResult::UpdateC;
1031       }
1032     }
1033 
1034     if (!IsNoExceptionFlag(flag))
1035       tlbe.recent = 1;
1036 
1037     *paddr = tlbe.paddr[1] | (vpa & 0xfff);
1038 
1039     return TLBLookupResult::Found;
1040   }
1041   return TLBLookupResult::NotFound;
1042 }
1043 
UpdateTLBEntry(const XCheckTLBFlag flag,UPTE2 PTE2,const u32 address)1044 static void UpdateTLBEntry(const XCheckTLBFlag flag, UPTE2 PTE2, const u32 address)
1045 {
1046   if (IsNoExceptionFlag(flag))
1047     return;
1048 
1049   const int tag = address >> HW_PAGE_INDEX_SHIFT;
1050   TLBEntry& tlbe = ppcState.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK];
1051   const int index = tlbe.recent == 0 && tlbe.tag[0] != TLBEntry::INVALID_TAG;
1052   tlbe.recent = index;
1053   tlbe.paddr[index] = PTE2.RPN << HW_PAGE_INDEX_SHIFT;
1054   tlbe.pte[index] = PTE2.Hex;
1055   tlbe.tag[index] = tag;
1056 }
1057 
InvalidateTLBEntry(u32 address)1058 void InvalidateTLBEntry(u32 address)
1059 {
1060   const u32 entry_index = (address >> HW_PAGE_INDEX_SHIFT) & HW_PAGE_INDEX_MASK;
1061 
1062   TLBEntry& tlbe = ppcState.tlb[0][entry_index];
1063   tlbe.tag[0] = TLBEntry::INVALID_TAG;
1064   tlbe.tag[1] = TLBEntry::INVALID_TAG;
1065 
1066   TLBEntry& tlbe_i = ppcState.tlb[1][entry_index];
1067   tlbe_i.tag[0] = TLBEntry::INVALID_TAG;
1068   tlbe_i.tag[1] = TLBEntry::INVALID_TAG;
1069 }
1070 
1071 // Page Address Translation
TranslatePageAddress(const u32 address,const XCheckTLBFlag flag)1072 static TranslateAddressResult TranslatePageAddress(const u32 address, const XCheckTLBFlag flag)
1073 {
1074   // TLB cache
1075   // This catches 99%+ of lookups in practice, so the actual page table entry code below doesn't
1076   // benefit
1077   // much from optimization.
1078   u32 translatedAddress = 0;
1079   TLBLookupResult res = LookupTLBPageAddress(flag, address, &translatedAddress);
1080   if (res == TLBLookupResult::Found)
1081     return TranslateAddressResult{TranslateAddressResult::PAGE_TABLE_TRANSLATED, translatedAddress};
1082 
1083   u32 sr = PowerPC::ppcState.sr[EA_SR(address)];
1084 
1085   if (sr & 0x80000000)
1086     return TranslateAddressResult{TranslateAddressResult::DIRECT_STORE_SEGMENT, 0};
1087 
1088   // TODO: Handle KS/KP segment register flags.
1089 
1090   // No-execute segment register flag.
1091   if ((flag == XCheckTLBFlag::Opcode || flag == XCheckTLBFlag::OpcodeNoException) &&
1092       (sr & 0x10000000))
1093   {
1094     return TranslateAddressResult{TranslateAddressResult::PAGE_FAULT, 0};
1095   }
1096 
1097   u32 offset = EA_Offset(address);         // 12 bit
1098   u32 page_index = EA_PageIndex(address);  // 16 bit
1099   u32 VSID = SR_VSID(sr);                  // 24 bit
1100   u32 api = EA_API(address);               //  6 bit (part of page_index)
1101 
1102   // hash function no 1 "xor" .360
1103   u32 hash = (VSID ^ page_index);
1104   u32 pte1 = Common::swap32((VSID << 7) | api | PTE1_V);
1105 
1106   for (int hash_func = 0; hash_func < 2; hash_func++)
1107   {
1108     // hash function no 2 "not" .360
1109     if (hash_func == 1)
1110     {
1111       hash = ~hash;
1112       pte1 |= PTE1_H << 24;
1113     }
1114 
1115     u32 pteg_addr =
1116         ((hash & PowerPC::ppcState.pagetable_hashmask) << 6) | PowerPC::ppcState.pagetable_base;
1117 
1118     for (int i = 0; i < 8; i++, pteg_addr += 8)
1119     {
1120       u32 pteg = Common::swap32(Memory::Read_U32(pteg_addr));
1121 
1122       if (pte1 == pteg)
1123       {
1124         UPTE2 PTE2;
1125         PTE2.Hex = Memory::Read_U32(pteg_addr + 4);
1126 
1127         // set the access bits
1128         switch (flag)
1129         {
1130         case XCheckTLBFlag::NoException:
1131         case XCheckTLBFlag::OpcodeNoException:
1132           break;
1133         case XCheckTLBFlag::Read:
1134           PTE2.R = 1;
1135           break;
1136         case XCheckTLBFlag::Write:
1137           PTE2.R = 1;
1138           PTE2.C = 1;
1139           break;
1140         case XCheckTLBFlag::Opcode:
1141           PTE2.R = 1;
1142           break;
1143         }
1144 
1145         if (!IsNoExceptionFlag(flag))
1146         {
1147           Memory::Write_U32(PTE2.Hex, pteg_addr + 4);
1148         }
1149 
1150         // We already updated the TLB entry if this was caused by a C bit.
1151         if (res != TLBLookupResult::UpdateC)
1152           UpdateTLBEntry(flag, PTE2, address);
1153 
1154         return TranslateAddressResult{TranslateAddressResult::PAGE_TABLE_TRANSLATED,
1155                                       (PTE2.RPN << 12) | offset};
1156       }
1157     }
1158   }
1159   return TranslateAddressResult{TranslateAddressResult::PAGE_FAULT, 0};
1160 }
1161 
UpdateBATs(BatTable & bat_table,u32 base_spr)1162 static void UpdateBATs(BatTable& bat_table, u32 base_spr)
1163 {
1164   // TODO: Separate BATs for MSR.PR==0 and MSR.PR==1
1165   // TODO: Handle PP/WIMG settings.
1166   // TODO: Check how hardware reacts to overlapping BATs (including
1167   // BATs which should cause a DSI).
1168   // TODO: Check how hardware reacts to invalid BATs (bad mask etc).
1169   for (int i = 0; i < 4; ++i)
1170   {
1171     const u32 spr = base_spr + i * 2;
1172     const UReg_BAT_Up batu{ppcState.spr[spr]};
1173     const UReg_BAT_Lo batl{ppcState.spr[spr + 1]};
1174     if (batu.VS == 0 && batu.VP == 0)
1175       continue;
1176 
1177     if ((batu.BEPI & batu.BL) != 0)
1178     {
1179       // With a valid BAT, the simplest way to match is
1180       // (input & ~BL_mask) == BEPI. For now, assume it's
1181       // implemented this way for invalid BATs as well.
1182       WARN_LOG(POWERPC, "Bad BAT setup: BEPI overlaps BL");
1183       continue;
1184     }
1185     if ((batl.BRPN & batu.BL) != 0)
1186     {
1187       // With a valid BAT, the simplest way to translate is
1188       // (input & BL_mask) | BRPN_address. For now, assume it's
1189       // implemented this way for invalid BATs as well.
1190       WARN_LOG(POWERPC, "Bad BAT setup: BPRN overlaps BL");
1191     }
1192     if (!Common::IsValidLowMask((u32)batu.BL))
1193     {
1194       // With a valid BAT, the simplest way of masking is
1195       // (input & ~BL_mask) for matching and (input & BL_mask) for
1196       // translation. For now, assume it's implemented this way for
1197       // invalid BATs as well.
1198       WARN_LOG(POWERPC, "Bad BAT setup: invalid mask in BL");
1199     }
1200     for (u32 j = 0; j <= batu.BL; ++j)
1201     {
1202       // Enumerate all bit-patterns which fit within the given mask.
1203       if ((j & batu.BL) == j)
1204       {
1205         // This bit is a little weird: if BRPN & j != 0, we end up with
1206         // a strange mapping. Need to check on hardware.
1207         u32 physical_address = (batl.BRPN | j) << BAT_INDEX_SHIFT;
1208         u32 virtual_address = (batu.BEPI | j) << BAT_INDEX_SHIFT;
1209 
1210         // The bottom bit is whether the translation is valid; the second
1211         // bit from the bottom is whether we can use the fastmem arena.
1212         u32 valid_bit = BAT_MAPPED_BIT;
1213         if (Memory::m_pFakeVMEM && (physical_address & 0xFE000000) == 0x7E000000)
1214           valid_bit |= BAT_PHYSICAL_BIT;
1215         else if (physical_address < Memory::GetRamSizeReal())
1216           valid_bit |= BAT_PHYSICAL_BIT;
1217         else if (Memory::m_pEXRAM && physical_address >> 28 == 0x1 &&
1218                  (physical_address & 0x0FFFFFFF) < Memory::GetExRamSizeReal())
1219           valid_bit |= BAT_PHYSICAL_BIT;
1220         else if (physical_address >> 28 == 0xE &&
1221                  physical_address < 0xE0000000 + Memory::GetL1CacheSize())
1222           valid_bit |= BAT_PHYSICAL_BIT;
1223 
1224         // Fastmem doesn't support memchecks, so disable it for all overlapping virtual pages.
1225         if (PowerPC::memchecks.OverlapsMemcheck(virtual_address, BAT_PAGE_SIZE))
1226           valid_bit &= ~BAT_PHYSICAL_BIT;
1227 
1228         // (BEPI | j) == (BEPI & ~BL) | (j & BL).
1229         bat_table[virtual_address >> BAT_INDEX_SHIFT] = physical_address | valid_bit;
1230       }
1231     }
1232   }
1233 }
1234 
UpdateFakeMMUBat(BatTable & bat_table,u32 start_addr)1235 static void UpdateFakeMMUBat(BatTable& bat_table, u32 start_addr)
1236 {
1237   for (u32 i = 0; i < (0x10000000 >> BAT_INDEX_SHIFT); ++i)
1238   {
1239     // Map from 0x4XXXXXXX or 0x7XXXXXXX to the range
1240     // [0x7E000000,0x80000000).
1241     u32 e_address = i + (start_addr >> BAT_INDEX_SHIFT);
1242     u32 p_address = 0x7E000000 | (i << BAT_INDEX_SHIFT & Memory::GetFakeVMemMask());
1243     u32 flags = BAT_MAPPED_BIT | BAT_PHYSICAL_BIT;
1244 
1245     if (PowerPC::memchecks.OverlapsMemcheck(e_address << BAT_INDEX_SHIFT, BAT_PAGE_SIZE))
1246       flags &= ~BAT_PHYSICAL_BIT;
1247 
1248     bat_table[e_address] = p_address | flags;
1249   }
1250 }
1251 
DBATUpdated()1252 void DBATUpdated()
1253 {
1254   dbat_table = {};
1255   UpdateBATs(dbat_table, SPR_DBAT0U);
1256   bool extended_bats = SConfig::GetInstance().bWii && HID4.SBE;
1257   if (extended_bats)
1258     UpdateBATs(dbat_table, SPR_DBAT4U);
1259   if (Memory::m_pFakeVMEM)
1260   {
1261     // In Fake-MMU mode, insert some extra entries into the BAT tables.
1262     UpdateFakeMMUBat(dbat_table, 0x40000000);
1263     UpdateFakeMMUBat(dbat_table, 0x70000000);
1264   }
1265 
1266 #ifndef _ARCH_32
1267   Memory::UpdateLogicalMemory(dbat_table);
1268 #endif
1269 
1270   // IsOptimizable*Address and dcbz depends on the BAT mapping, so we need a flush here.
1271   JitInterface::ClearSafe();
1272 }
1273 
IBATUpdated()1274 void IBATUpdated()
1275 {
1276   ibat_table = {};
1277   UpdateBATs(ibat_table, SPR_IBAT0U);
1278   bool extended_bats = SConfig::GetInstance().bWii && HID4.SBE;
1279   if (extended_bats)
1280     UpdateBATs(ibat_table, SPR_IBAT4U);
1281   if (Memory::m_pFakeVMEM)
1282   {
1283     // In Fake-MMU mode, insert some extra entries into the BAT tables.
1284     UpdateFakeMMUBat(ibat_table, 0x40000000);
1285     UpdateFakeMMUBat(ibat_table, 0x70000000);
1286   }
1287   JitInterface::ClearSafe();
1288 }
1289 
1290 // Translate effective address using BAT or PAT.  Returns 0 if the address cannot be translated.
1291 // Through the hardware looks up BAT and TLB in parallel, BAT is used first if available.
1292 // So we first check if there is a matching BAT entry, else we look for the TLB in
1293 // TranslatePageAddress().
1294 template <const XCheckTLBFlag flag>
TranslateAddress(u32 address)1295 static TranslateAddressResult TranslateAddress(u32 address)
1296 {
1297   if (TranslateBatAddess(IsOpcodeFlag(flag) ? ibat_table : dbat_table, &address))
1298     return TranslateAddressResult{TranslateAddressResult::BAT_TRANSLATED, address};
1299 
1300   return TranslatePageAddress(address, flag);
1301 }
1302 
GetTranslatedAddress(u32 address)1303 std::optional<u32> GetTranslatedAddress(u32 address)
1304 {
1305   auto result = TranslateAddress<XCheckTLBFlag::NoException>(address);
1306   if (!result.Success())
1307   {
1308     return std::nullopt;
1309   }
1310   return std::optional<u32>(result.address);
1311 }
1312 
1313 }  // namespace PowerPC
1314