00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include <pch.h>
00033 #include "Emu/Utility/System/Memory/MemorySystem.h"
00034 #include "Emu/Utility/System/Memory/MemoryUtility.h"
00035 #include "SysDeps/Endian.h"
00036
00037 using namespace std;
00038 using namespace Onikiri;
00039 using namespace EmulatorUtility;
00040
00041
00042 #define ENABLE_MEMORY_SYSTEM_COPY_ON_WRITE
00043
00044
00045
00046
00047 MemorySystem::MemorySystem( int pid, bool bigEndian, SystemIF* simSystem ) :
00048 m_virtualMemory( pid, bigEndian, simSystem ),
00049 m_heapAlloc( m_virtualMemory.GetPageSize() ),
00050 m_currentBrk(0),
00051 m_simSystem(simSystem),
00052 m_pid(pid),
00053 m_bigEndian(bigEndian)
00054 {
00055 u64 pageSize = GetPageSize();
00056
00057
00058 u64 zeroPageAddr = RESERVED_PAGE_ZERO_FILLED * pageSize;
00059 m_virtualMemory.AssignPhysicalMemory( zeroPageAddr, VIRTUAL_MEMORY_ATTR_READ );
00060 m_virtualMemory.TargetMemset( zeroPageAddr, 0, pageSize );
00061
00062 if( ( pageSize & (pageSize - 1) ) != 0 ){
00063 THROW_RUNTIME_ERROR( "The page size is not power-of-two." );
00064 }
00065 }
00066
00067 MemorySystem::~MemorySystem()
00068 {
00069
00070 u64 zeroPageAddr = RESERVED_PAGE_ZERO_FILLED * GetPageSize();
00071 m_virtualMemory.FreePhysicalMemory( zeroPageAddr );
00072 }
00073
00074
00075
00076
00077
00078 u64 MemorySystem::GetPageSize()
00079 {
00080 return m_heapAlloc.GetPageSize();
00081 }
00082
00083 void MemorySystem::AddHeapBlock(u64 addr, u64 size)
00084 {
00085 m_heapAlloc.AddMemoryBlock(addr, size);
00086 }
00087
00088 void MemorySystem::SetInitialBrk(u64 initialBrk)
00089 {
00090 m_currentBrk = initialBrk;
00091 }
00092
00093 static u64 MaskPageOffset( u64 addr, u64 pageSize )
00094 {
00095 return addr & ~(pageSize - 1);
00096 }
00097
00098 u64 MemorySystem::Brk(u64 addr)
00099 {
00100
00101 if (addr == 0)
00102 return m_currentBrk;
00103
00104 u64 pageSize = GetPageSize();
00105
00106
00107 if( addr < m_currentBrk ){
00108
00109 u64 endPageAddr = MaskPageOffset( addr, pageSize );
00110 u64 pageAddr = MaskPageOffset( m_currentBrk, pageSize );
00111 while( pageAddr != endPageAddr ){
00112 m_virtualMemory.FreePhysicalMemory( pageAddr, pageSize );
00113 pageAddr -= pageSize;
00114 }
00115 m_currentBrk = addr;
00116 return 0;
00117 }
00118 else{
00119
00120 u64 endPageAddr = MaskPageOffset( addr, pageSize );
00121 u64 pageAddr = MaskPageOffset( m_currentBrk, pageSize ) + pageSize;
00122
00123
00124 while( pageAddr <= endPageAddr ){
00125
00126 m_virtualMemory.AssignPhysicalMemory( pageAddr, VIRTUAL_MEMORY_ATTR_READ|VIRTUAL_MEMORY_ATTR_WRITE );
00127 pageAddr += pageSize;
00128 }
00129 TargetMemset(m_currentBrk, 0, addr-m_currentBrk);
00130 m_currentBrk = addr;
00131 }
00132
00133
00134 return 0;
00135 }
00136
00137
00138 u64 MemorySystem::MMap(u64 addr, u64 length)
00139 {
00140 if (addr != 0)
00141 return (u64)-1;
00142 if (length <= 0)
00143 return (u64)-1;
00144
00145 u64 result = m_heapAlloc.Alloc(addr, length);
00146 CheckValueOnPageBoundary( result, "mmap:address" );
00147
00148 if (result) {
00149 #ifdef ENABLE_MEMORY_SYSTEM_COPY_ON_WRITE
00150 u64 zeroPageAddr = RESERVED_PAGE_ZERO_FILLED * GetPageSize();
00151 m_virtualMemory.SetPhysicalMemoryMapping( result, zeroPageAddr, length, VIRTUAL_MEMORY_ATTR_READ|VIRTUAL_MEMORY_ATTR_WRITE );
00152 #else
00153 m_virtualMemory.AssignPhysicalMemory(result, length, VIRTUAL_MEMORY_ATTR_READ);
00154 TargetMemset(result, 0, length);
00155 #endif
00156 return result;
00157 }
00158 else{
00159 return (u64)-1;
00160 }
00161 }
00162
00163 u64 MemorySystem::MRemap(u64 old_addr, u64 old_size, u64 new_size, bool mayMove)
00164 {
00165
00166
00167
00168 CheckValueOnPageBoundary( old_addr, "mremap:old_addr" );
00169
00170 if (new_size <= 0)
00171 return (u64)-1;
00172
00173 if (m_heapAlloc.GetBlockSize(old_addr) != old_size)
00174 return (u64)-1;
00175
00176 u64 result = m_heapAlloc.ReAlloc(old_addr, old_size, new_size);
00177 CheckValueOnPageBoundary( result, "mremap/not move" );
00178
00179 if (result) {
00180 CheckValueOnPageBoundary( old_size, "mremap:old_size" );
00181 CheckValueOnPageBoundary( new_size, "mremap:new_size" );
00182 if( new_size > old_size ){
00183
00184 u64 pageMask = GetPageSize() - 1;
00185 if( ( ( result + old_size ) & pageMask) != 0 ){
00186
00187 s64 length = (s64)new_size - (s64)(( old_size + pageMask ) & ~pageMask);
00188 u64 addr = result + new_size - length;
00189 if( length > 0 ){
00190 m_virtualMemory.AssignPhysicalMemory( addr, length, VIRTUAL_MEMORY_ATTR_READ|VIRTUAL_MEMORY_ATTR_WRITE );
00191 }
00192 }
00193 else{
00194 m_virtualMemory.AssignPhysicalMemory(result+old_size, new_size - old_size, VIRTUAL_MEMORY_ATTR_READ|VIRTUAL_MEMORY_ATTR_WRITE );
00195 }
00196 TargetMemset(result+old_size, 0, new_size-old_size);
00197 }
00198 else if( new_size < old_size ){
00199 u64 pageMask = GetPageSize() - 1;
00200 if( ( ( result + new_size ) & pageMask ) != 0 ){
00201
00202
00203 s64 length = (s64)old_size - (s64)(( new_size + pageMask ) & ~pageMask);
00204 u64 addr = result + old_size - length;
00205 if( length > 0 ){
00206 m_virtualMemory.FreePhysicalMemory( addr, length );
00207 }
00208 }
00209 else{
00210 m_virtualMemory.FreePhysicalMemory(result+new_size, old_size - new_size);
00211 }
00212 }
00213
00214 return result;
00215 }
00216 else if (mayMove) {
00217
00218 ASSERT(old_size < new_size, "HeapAlloc::ReAlloc Logic Error");
00219 result = m_heapAlloc.Alloc(0, new_size);
00220 CheckValueOnPageBoundary( result, "mremap/move" );
00221
00222 if (result) {
00223 m_virtualMemory.AssignPhysicalMemory(result, new_size, VIRTUAL_MEMORY_ATTR_READ|VIRTUAL_MEMORY_ATTR_WRITE );
00224
00225
00226
00227 {
00228 TargetBuffer buf(this, old_addr, static_cast<size_t>(old_size));
00229 MemCopyToTarget(result, buf.Get(), old_size);
00230 TargetMemset(result+old_size, 0, new_size-old_size);
00231 }
00232
00233 MUnmap(old_addr, old_size);
00234 return result;
00235 }
00236 else {
00237 return (u64)-1;
00238 }
00239 }
00240 else {
00241 return (u64)-1;
00242 }
00243 }
00244
00245 int MemorySystem::MUnmap(u64 addr, u64 length)
00246 {
00247 CheckValueOnPageBoundary( addr, "munmap:address" );
00248 CheckValueOnPageBoundary( length, "munmap:length" );
00249
00250 if (length <= 0)
00251 return -1;
00252
00253 if (m_heapAlloc.Free(addr, length)) {
00254
00255 u64 pageMask = GetPageSize() - 1;
00256 if( ( ( addr + length ) & pageMask ) != 0 ){
00257 length = length & ~pageMask;
00258 }
00259 m_virtualMemory.FreePhysicalMemory( addr, length );
00260 return 0;
00261 }
00262 else
00263 return -1;
00264 }
00265
00266
00267
00268 void MemorySystem::CheckValueOnPageBoundary( u64 addr, const char* signature )
00269 {
00270 if( ( addr & (GetPageSize() - 1) ) != 0 ){
00271 RUNTIME_WARNING(
00272 "The address is not aligned to a page boundary in '%s'\n"
00273 "Address : %08x%08x",
00274 (u32)(addr >> 32), (u32)addr,
00275 signature
00276 );
00277 }
00278 }
00279