1 // Copyright 2008 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 // NOTE:
6 // These functions are primarily used by the interpreter versions of the LoadStore instructions.
7 // However, if a JITed instruction (for example lwz) wants to access a bad memory area that call
8 // may be redirected here (for example to Read_U32()).
9
10 #include "Core/HW/Memmap.h"
11
12 #include <algorithm>
13 #include <array>
14 #include <cstring>
15 #include <memory>
16
17 #include "Common/ChunkFile.h"
18 #include "Common/CommonTypes.h"
19 #include "Common/Logging/Log.h"
20 #include "Common/MemArena.h"
21 #include "Common/Swap.h"
22 #include "Core/Config/MainSettings.h"
23 #include "Core/ConfigManager.h"
24 #include "Core/HW/AudioInterface.h"
25 #include "Core/HW/DSP.h"
26 #include "Core/HW/DVD/DVDInterface.h"
27 #include "Core/HW/EXI/EXI.h"
28 #include "Core/HW/MMIO.h"
29 #include "Core/HW/MemoryInterface.h"
30 #include "Core/HW/ProcessorInterface.h"
31 #include "Core/HW/SI/SI.h"
32 #include "Core/HW/VideoInterface.h"
33 #include "Core/HW/WII_IPC.h"
34 #include "Core/PowerPC/JitCommon/JitBase.h"
35 #include "Core/PowerPC/PowerPC.h"
36 #include "VideoCommon/CommandProcessor.h"
37 #include "VideoCommon/PixelEngine.h"
38
39 namespace Memory
40 {
41 // =================================
42 // Init() declarations
43 // ----------------
44 // Store the MemArena here
45 u8* physical_base = nullptr;
46 u8* logical_base = nullptr;
47 static bool is_fastmem_arena_initialized = false;
48
49 // The MemArena class
50 static Common::MemArena g_arena;
51 // ==============
52
53 // STATE_TO_SAVE
54 static bool m_IsInitialized = false; // Save the Init(), Shutdown() state
55 // END STATE_TO_SAVE
56
57 u8* m_pRAM;
58 u8* m_pL1Cache;
59 u8* m_pEXRAM;
60 u8* m_pFakeVMEM;
61
62 // s_ram_size is the amount allocated by the emulator, whereas s_ram_size_real
63 // is what will be reported in lowmem, and thus used by emulated software.
64 // Note: Writing to lowmem is done by IPL. If using retail IPL, it will
65 // always be set to 24MB.
66 static u32 s_ram_size_real;
67 static u32 s_ram_size;
68 static u32 s_ram_mask;
69 static u32 s_fakevmem_size;
70 static u32 s_fakevmem_mask;
71 static u32 s_L1_cache_size;
72 static u32 s_L1_cache_mask;
73 static u32 s_io_size;
74 // s_exram_size is the amount allocated by the emulator, whereas s_exram_size_real
75 // is what gets used by emulated software. If using retail IOS, it will
76 // always be set to 64MB.
77 static u32 s_exram_size_real;
78 static u32 s_exram_size;
79 static u32 s_exram_mask;
80
GetRamSizeReal()81 u32 GetRamSizeReal()
82 {
83 return s_ram_size_real;
84 }
GetRamSize()85 u32 GetRamSize()
86 {
87 return s_ram_size;
88 }
GetRamMask()89 u32 GetRamMask()
90 {
91 return s_ram_mask;
92 }
GetFakeVMemSize()93 u32 GetFakeVMemSize()
94 {
95 return s_fakevmem_size;
96 }
GetFakeVMemMask()97 u32 GetFakeVMemMask()
98 {
99 return s_fakevmem_mask;
100 }
GetL1CacheSize()101 u32 GetL1CacheSize()
102 {
103 return s_L1_cache_size;
104 }
GetL1CacheMask()105 u32 GetL1CacheMask()
106 {
107 return s_L1_cache_mask;
108 }
GetIOSize()109 u32 GetIOSize()
110 {
111 return s_io_size;
112 }
GetExRamSizeReal()113 u32 GetExRamSizeReal()
114 {
115 return s_exram_size_real;
116 }
GetExRamSize()117 u32 GetExRamSize()
118 {
119 return s_exram_size;
120 }
GetExRamMask()121 u32 GetExRamMask()
122 {
123 return s_exram_mask;
124 }
125
126 // MMIO mapping object.
127 std::unique_ptr<MMIO::Mapping> mmio_mapping;
128
InitMMIO()129 static std::unique_ptr<MMIO::Mapping> InitMMIO()
130 {
131 auto mmio = std::make_unique<MMIO::Mapping>();
132
133 CommandProcessor::RegisterMMIO(mmio.get(), 0x0C000000);
134 PixelEngine::RegisterMMIO(mmio.get(), 0x0C001000);
135 VideoInterface::RegisterMMIO(mmio.get(), 0x0C002000);
136 ProcessorInterface::RegisterMMIO(mmio.get(), 0x0C003000);
137 MemoryInterface::RegisterMMIO(mmio.get(), 0x0C004000);
138 DSP::RegisterMMIO(mmio.get(), 0x0C005000);
139 DVDInterface::RegisterMMIO(mmio.get(), 0x0C006000);
140 SerialInterface::RegisterMMIO(mmio.get(), 0x0C006400);
141 ExpansionInterface::RegisterMMIO(mmio.get(), 0x0C006800);
142 AudioInterface::RegisterMMIO(mmio.get(), 0x0C006C00);
143
144 return mmio;
145 }
146
InitMMIOWii()147 static std::unique_ptr<MMIO::Mapping> InitMMIOWii()
148 {
149 auto mmio = InitMMIO();
150
151 IOS::RegisterMMIO(mmio.get(), 0x0D000000);
152 DVDInterface::RegisterMMIO(mmio.get(), 0x0D006000);
153 SerialInterface::RegisterMMIO(mmio.get(), 0x0D006400);
154 ExpansionInterface::RegisterMMIO(mmio.get(), 0x0D006800);
155 AudioInterface::RegisterMMIO(mmio.get(), 0x0D006C00);
156
157 return mmio;
158 }
159
IsInitialized()160 bool IsInitialized()
161 {
162 return m_IsInitialized;
163 }
164
165 struct PhysicalMemoryRegion
166 {
167 u8** out_pointer;
168 u32 physical_address;
169 u32 size;
170 enum : u32
171 {
172 ALWAYS = 0,
173 FAKE_VMEM = 1,
174 WII_ONLY = 2,
175 } flags;
176 u32 shm_position;
177 };
178
179 struct LogicalMemoryView
180 {
181 void* mapped_pointer;
182 u32 mapped_size;
183 };
184
185 // Dolphin allocates memory to represent four regions:
186 // - 32MB RAM (actually 24MB on hardware), available on Gamecube and Wii
187 // - 64MB "EXRAM", RAM only available on Wii
188 // - 32MB FakeVMem, allocated in GameCube mode when MMU support is turned off.
189 // This is used to approximate the behavior of a common library which pages
190 // memory to and from the DSP's dedicated RAM. The DSP's RAM (ARAM) isn't
191 // directly addressable on GameCube.
192 // - 256KB Locked L1, to represent cache lines allocated out of the L1 data
193 // cache in Locked L1 mode. Dolphin does not emulate this hardware feature
194 // accurately; it just pretends there is extra memory at 0xE0000000.
195 //
196 // The 4GB starting at physical_base represents access from the CPU
197 // with address translation turned off. (This is only used by the CPU;
198 // other devices, like the GPU, use other rules, approximated by
199 // Memory::GetPointer.) This memory is laid out as follows:
200 // [0x00000000, 0x02000000) - 32MB RAM
201 // [0x02000000, 0x08000000) - Mirrors of 32MB RAM (not handled here)
202 // [0x08000000, 0x0C000000) - EFB "mapping" (not handled here)
203 // [0x0C000000, 0x0E000000) - MMIO etc. (not handled here)
204 // [0x10000000, 0x14000000) - 64MB RAM (Wii-only; slightly slower)
205 // [0x7E000000, 0x80000000) - FakeVMEM
206 // [0xE0000000, 0xE0040000) - 256KB locked L1
207 //
208 // The 4GB starting at logical_base represents access from the CPU
209 // with address translation turned on. This mapping is computed based
210 // on the BAT registers.
211 //
212 // Each of these 4GB regions is followed by 4GB of empty space so overflows
213 // in address computation in the JIT don't access the wrong memory.
214 //
215 // The neighboring mirrors of RAM ([0x02000000, 0x08000000), etc.) exist because
216 // the bus masks off the bits in question for RAM accesses; using them is a
217 // terrible idea because the CPU cache won't handle them correctly, but a
218 // few buggy games (notably Rogue Squadron 2) use them by accident. They
219 // aren't backed by memory mappings because they are used very rarely.
220 //
221 // Dolphin doesn't emulate the difference between cached and uncached access.
222 //
223 // TODO: The actual size of RAM is 24MB; the other 8MB shouldn't be backed by actual memory.
224 // TODO: Do we want to handle the mirrors of the GC RAM?
225 static std::array<PhysicalMemoryRegion, 4> physical_regions;
226
227 static std::vector<LogicalMemoryView> logical_mapped_entries;
228
GetFlags()229 static u32 GetFlags()
230 {
231 bool wii = SConfig::GetInstance().bWii;
232 bool bMMU = SConfig::GetInstance().bMMU;
233 bool bFakeVMEM = false;
234 #ifndef _ARCH_32
235 // If MMU is turned off in GameCube mode, turn on fake VMEM hack.
236 // The fake VMEM hack's address space is above the memory space that we
237 // allocate on 32bit targets, so disable it there.
238 bFakeVMEM = !wii && !bMMU;
239 #endif
240
241 u32 flags = 0;
242 if (wii)
243 flags |= PhysicalMemoryRegion::WII_ONLY;
244 if (bFakeVMEM)
245 flags |= PhysicalMemoryRegion::FAKE_VMEM;
246
247 return flags;
248 }
249
Init()250 void Init()
251 {
252 const auto get_mem1_size = [] {
253 if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE))
254 return Config::Get(Config::MAIN_MEM1_SIZE);
255 return Memory::MEM1_SIZE_RETAIL;
256 };
257 const auto get_mem2_size = [] {
258 if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE))
259 return Config::Get(Config::MAIN_MEM2_SIZE);
260 return Memory::MEM2_SIZE_RETAIL;
261 };
262 s_ram_size_real = get_mem1_size();
263 s_ram_size = MathUtil::NextPowerOf2(GetRamSizeReal());
264 s_ram_mask = GetRamSize() - 1;
265 s_fakevmem_size = 0x02000000;
266 s_fakevmem_mask = GetFakeVMemSize() - 1;
267 s_L1_cache_size = 0x00040000;
268 s_L1_cache_mask = GetL1CacheSize() - 1;
269 s_io_size = 0x00010000;
270 s_exram_size_real = get_mem2_size();
271 s_exram_size = MathUtil::NextPowerOf2(GetExRamSizeReal());
272 s_exram_mask = GetExRamSize() - 1;
273
274 physical_regions[0] = {&m_pRAM, 0x00000000, GetRamSize(), PhysicalMemoryRegion::ALWAYS};
275 physical_regions[1] = {&m_pL1Cache, 0xE0000000, GetL1CacheSize(), PhysicalMemoryRegion::ALWAYS};
276 physical_regions[2] = {&m_pFakeVMEM, 0x7E000000, GetFakeVMemSize(),
277 PhysicalMemoryRegion::FAKE_VMEM};
278 physical_regions[3] = {&m_pEXRAM, 0x10000000, GetExRamSize(), PhysicalMemoryRegion::WII_ONLY};
279
280 bool wii = SConfig::GetInstance().bWii;
281 u32 flags = GetFlags();
282 u32 mem_size = 0;
283 for (PhysicalMemoryRegion& region : physical_regions)
284 {
285 if ((flags & region.flags) != region.flags)
286 continue;
287 region.shm_position = mem_size;
288 mem_size += region.size;
289 }
290 g_arena.GrabSHMSegment(mem_size);
291
292 // Create an anonymous view of the physical memory
293 for (PhysicalMemoryRegion& region : physical_regions)
294 {
295 if ((flags & region.flags) != region.flags)
296 continue;
297
298 *region.out_pointer = (u8*)g_arena.CreateView(region.shm_position, region.size);
299
300 if (!*region.out_pointer)
301 {
302 PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
303 exit(0);
304 }
305 }
306
307 if (wii)
308 mmio_mapping = InitMMIOWii();
309 else
310 mmio_mapping = InitMMIO();
311
312 Clear();
313
314 INFO_LOG(MEMMAP, "Memory system initialized. RAM at %p", m_pRAM);
315 m_IsInitialized = true;
316 }
317
InitFastmemArena()318 bool InitFastmemArena()
319 {
320 u32 flags = GetFlags();
321 physical_base = Common::MemArena::FindMemoryBase();
322
323 if (!physical_base)
324 return false;
325
326 for (PhysicalMemoryRegion& region : physical_regions)
327 {
328 if ((flags & region.flags) != region.flags)
329 continue;
330
331 u8* base = physical_base + region.physical_address;
332 u8* view = (u8*)g_arena.CreateView(region.shm_position, region.size, base);
333
334 if (base != view)
335 {
336 return false;
337 }
338 }
339
340 #ifndef _ARCH_32
341 logical_base = physical_base + 0x200000000;
342 #endif
343
344 is_fastmem_arena_initialized = true;
345 return true;
346 }
347
UpdateLogicalMemory(const PowerPC::BatTable & dbat_table)348 void UpdateLogicalMemory(const PowerPC::BatTable& dbat_table)
349 {
350 if (!is_fastmem_arena_initialized)
351 return;
352
353 for (auto& entry : logical_mapped_entries)
354 {
355 g_arena.ReleaseView(entry.mapped_pointer, entry.mapped_size);
356 }
357 logical_mapped_entries.clear();
358 for (u32 i = 0; i < dbat_table.size(); ++i)
359 {
360 if (dbat_table[i] & PowerPC::BAT_PHYSICAL_BIT)
361 {
362 u32 logical_address = i << PowerPC::BAT_INDEX_SHIFT;
363 // TODO: Merge adjacent mappings to make this faster.
364 u32 logical_size = PowerPC::BAT_PAGE_SIZE;
365 u32 translated_address = dbat_table[i] & PowerPC::BAT_RESULT_MASK;
366 for (const auto& physical_region : physical_regions)
367 {
368 u32 mapping_address = physical_region.physical_address;
369 u32 mapping_end = mapping_address + physical_region.size;
370 u32 intersection_start = std::max(mapping_address, translated_address);
371 u32 intersection_end = std::min(mapping_end, translated_address + logical_size);
372 if (intersection_start < intersection_end)
373 {
374 // Found an overlapping region; map it.
375 u32 position = physical_region.shm_position + intersection_start - mapping_address;
376 u8* base = logical_base + logical_address + intersection_start - translated_address;
377 u32 mapped_size = intersection_end - intersection_start;
378
379 void* mapped_pointer = g_arena.CreateView(position, mapped_size, base);
380 if (!mapped_pointer)
381 {
382 PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
383 exit(0);
384 }
385 logical_mapped_entries.push_back({mapped_pointer, mapped_size});
386 }
387 }
388 }
389 }
390 }
391
DoState(PointerWrap & p)392 void DoState(PointerWrap& p)
393 {
394 bool wii = SConfig::GetInstance().bWii;
395 p.DoArray(m_pRAM, GetRamSize());
396 p.DoArray(m_pL1Cache, GetL1CacheSize());
397 p.DoMarker("Memory RAM");
398 if (m_pFakeVMEM)
399 p.DoArray(m_pFakeVMEM, GetFakeVMemSize());
400 p.DoMarker("Memory FakeVMEM");
401 if (wii)
402 p.DoArray(m_pEXRAM, GetExRamSize());
403 p.DoMarker("Memory EXRAM");
404 }
405
Shutdown()406 void Shutdown()
407 {
408 ShutdownFastmemArena();
409
410 m_IsInitialized = false;
411 u32 flags = GetFlags();
412 for (PhysicalMemoryRegion& region : physical_regions)
413 {
414 if ((flags & region.flags) != region.flags)
415 continue;
416 g_arena.ReleaseView(*region.out_pointer, region.size);
417 *region.out_pointer = nullptr;
418 }
419 g_arena.ReleaseSHMSegment();
420 mmio_mapping.reset();
421 INFO_LOG(MEMMAP, "Memory system shut down.");
422 }
423
ShutdownFastmemArena()424 void ShutdownFastmemArena()
425 {
426 if (!is_fastmem_arena_initialized)
427 return;
428
429 u32 flags = GetFlags();
430 for (PhysicalMemoryRegion& region : physical_regions)
431 {
432 if ((flags & region.flags) != region.flags)
433 continue;
434
435 u8* base = physical_base + region.physical_address;
436 g_arena.ReleaseView(base, region.size);
437 }
438
439 for (auto& entry : logical_mapped_entries)
440 {
441 g_arena.ReleaseView(entry.mapped_pointer, entry.mapped_size);
442 }
443 logical_mapped_entries.clear();
444
445 physical_base = nullptr;
446 logical_base = nullptr;
447
448 is_fastmem_arena_initialized = false;
449 }
450
Clear()451 void Clear()
452 {
453 if (m_pRAM)
454 memset(m_pRAM, 0, GetRamSize());
455 if (m_pL1Cache)
456 memset(m_pL1Cache, 0, GetL1CacheSize());
457 if (m_pFakeVMEM)
458 memset(m_pFakeVMEM, 0, GetFakeVMemSize());
459 if (m_pEXRAM)
460 memset(m_pEXRAM, 0, GetExRamSize());
461 }
462
GetPointerForRange(u32 address,size_t size)463 static inline u8* GetPointerForRange(u32 address, size_t size)
464 {
465 // Make sure we don't have a range spanning 2 separate banks
466 if (size >= GetExRamSizeReal())
467 return nullptr;
468
469 // Check that the beginning and end of the range are valid
470 u8* pointer = GetPointer(address);
471 if (!pointer || !GetPointer(address + u32(size) - 1))
472 return nullptr;
473
474 return pointer;
475 }
476
CopyFromEmu(void * data,u32 address,size_t size)477 void CopyFromEmu(void* data, u32 address, size_t size)
478 {
479 if (size == 0)
480 return;
481
482 void* pointer = GetPointerForRange(address, size);
483 if (!pointer)
484 {
485 PanicAlert("Invalid range in CopyFromEmu. %zx bytes from 0x%08x", size, address);
486 return;
487 }
488 memcpy(data, pointer, size);
489 }
490
CopyToEmu(u32 address,const void * data,size_t size)491 void CopyToEmu(u32 address, const void* data, size_t size)
492 {
493 if (size == 0)
494 return;
495
496 void* pointer = GetPointerForRange(address, size);
497 if (!pointer)
498 {
499 PanicAlert("Invalid range in CopyToEmu. %zx bytes to 0x%08x", size, address);
500 return;
501 }
502 memcpy(pointer, data, size);
503 }
504
Memset(u32 address,u8 value,size_t size)505 void Memset(u32 address, u8 value, size_t size)
506 {
507 if (size == 0)
508 return;
509
510 void* pointer = GetPointerForRange(address, size);
511 if (!pointer)
512 {
513 PanicAlert("Invalid range in Memset. %zx bytes at 0x%08x", size, address);
514 return;
515 }
516 memset(pointer, value, size);
517 }
518
GetString(u32 em_address,size_t size)519 std::string GetString(u32 em_address, size_t size)
520 {
521 const char* ptr = reinterpret_cast<const char*>(GetPointer(em_address));
522 if (ptr == nullptr)
523 return "";
524
525 if (size == 0) // Null terminated string.
526 {
527 return std::string(ptr);
528 }
529 else // Fixed size string, potentially null terminated or null padded.
530 {
531 size_t length = strnlen(ptr, size);
532 return std::string(ptr, length);
533 }
534 }
535
GetPointer(u32 address)536 u8* GetPointer(u32 address)
537 {
538 // TODO: Should we be masking off more bits here? Can all devices access
539 // EXRAM?
540 address &= 0x3FFFFFFF;
541 if (address < GetRamSizeReal())
542 return m_pRAM + address;
543
544 if (m_pEXRAM)
545 {
546 if ((address >> 28) == 0x1 && (address & 0x0fffffff) < GetExRamSizeReal())
547 return m_pEXRAM + (address & GetExRamMask());
548 }
549
550 PanicAlert("Unknown Pointer 0x%08x PC 0x%08x LR 0x%08x", address, PC, LR);
551
552 return nullptr;
553 }
554
Read_U8(u32 address)555 u8 Read_U8(u32 address)
556 {
557 return *GetPointer(address);
558 }
559
Read_U16(u32 address)560 u16 Read_U16(u32 address)
561 {
562 return Common::swap16(GetPointer(address));
563 }
564
Read_U32(u32 address)565 u32 Read_U32(u32 address)
566 {
567 return Common::swap32(GetPointer(address));
568 }
569
Read_U64(u32 address)570 u64 Read_U64(u32 address)
571 {
572 return Common::swap64(GetPointer(address));
573 }
574
Write_U8(u8 value,u32 address)575 void Write_U8(u8 value, u32 address)
576 {
577 *GetPointer(address) = value;
578 }
579
Write_U16(u16 value,u32 address)580 void Write_U16(u16 value, u32 address)
581 {
582 u16 swapped_value = Common::swap16(value);
583 std::memcpy(GetPointer(address), &swapped_value, sizeof(u16));
584 }
585
Write_U32(u32 value,u32 address)586 void Write_U32(u32 value, u32 address)
587 {
588 u32 swapped_value = Common::swap32(value);
589 std::memcpy(GetPointer(address), &swapped_value, sizeof(u32));
590 }
591
Write_U64(u64 value,u32 address)592 void Write_U64(u64 value, u32 address)
593 {
594 u64 swapped_value = Common::swap64(value);
595 std::memcpy(GetPointer(address), &swapped_value, sizeof(u64));
596 }
597
Write_U32_Swap(u32 value,u32 address)598 void Write_U32_Swap(u32 value, u32 address)
599 {
600 std::memcpy(GetPointer(address), &value, sizeof(u32));
601 }
602
Write_U64_Swap(u64 value,u32 address)603 void Write_U64_Swap(u64 value, u32 address)
604 {
605 std::memcpy(GetPointer(address), &value, sizeof(u64));
606 }
607
608 } // namespace Memory
609