OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
mem32.cpp
Go to the documentation of this file.
1#include "mem32.h"
2
3#include <Tempest/Log>
4#include <cassert>
5#include <cstring>
6#include <algorithm>
7#include <cstddef>
8
9using namespace Tempest;
10
12 /*
13 * [0x00001000 .. 0x80000000] - (2GB) user space
14 * [0x80000000 .. 0xc0000000] - (1GB) extra space(reserved for opengothic use; pinned memory)
15 * [0xc0000000 .. 0xffffffff] - (1GB) kernel space
16 */
17 region.emplace_back(Region(0x1000,0x80000000));
18 }
19
21 for(auto& rgn:region) {
22 if(rgn.status==S_Allocated && rgn.real!=nullptr) {
23 std::free(rgn.real);
24 rgn.real = nullptr;
25 }
26 }
27 }
28
29void Mem32::implSetCallbackR(Type t, std::function<void(void*, uint32_t)> fn, size_t elt) {
30 auto& m = memMap[t];
31 m.read = std::move(fn);
32 m.elementSize = uint32_t(elt);
33 }
34
35void Mem32::implSetCallbackW(Type t, std::function<void(void*, uint32_t)> fn, size_t elt) {
36 auto& m = memMap[t];
37 m.write = std::move(fn);
38 m.elementSize = uint32_t(elt);
39 }
40
41Mem32::ptr32_t Mem32::pin(void* mem, ptr32_t address, uint32_t size, const char* comment) {
42 if(auto rgn = implAllocAt(address,size)) {
43 rgn->size = size;
44 rgn->real = mem;
45 rgn->status = S_Pin;
46 rgn->comment = comment;
47 return address;
48 }
49 throw std::bad_alloc();
50 }
51
52Mem32::ptr32_t Mem32::pin(void* mem, uint32_t size, const char* comment) {
53 if(auto rgn = implAlloc(size)) {
54 rgn->size = size;
55 rgn->real = mem;
56 rgn->status = S_Pin;
57 rgn->comment = comment;
58 return rgn->address;
59 }
60 throw std::bad_alloc();
61 }
62
63Mem32::ptr32_t Mem32::pin(ptr32_t address, uint32_t size, Type type) {
64 if(auto rgn = implAllocAt(address,size)) {
65 rgn->type = type;
66 rgn->size = size;
67 rgn->real = nullptr; // allocated on demand
68 rgn->status = S_Callback;
69 return address;
70 }
71 throw std::bad_alloc();
72 }
73
74Mem32::ptr32_t Mem32::alloc(ptr32_t address, uint32_t size, const char* comment) {
75 if(auto rgn = implAllocAt(address,size)) {
76 rgn->real = std::calloc(rgn->size,1);
77 if(rgn->real==nullptr) {
78 compactage();
79 return 0;
80 }
81 rgn->size = size;
82 rgn->status = S_Allocated;
83 rgn->comment = comment;
84 return address;
85 }
86 throw std::bad_alloc();
87 }
88
89Mem32::ptr32_t Mem32::alloc(uint32_t size, const char* comment) {
90 if(auto rgn = implAlloc(size)) {
91 rgn->real = std::calloc(rgn->size,1);
92 if(rgn->real==nullptr) {
93 compactage();
94 return 0;
95 }
96 rgn->status = S_Allocated;
97 rgn->comment = comment;
98 return rgn->address;
99 }
100 return 0;
101 }
102
103Mem32::ptr32_t Mem32::alloc(uint32_t size, Type type, const char* comment) {
104 if(auto rgn = implAlloc(size)) {
105 rgn->type = type;
106 rgn->status = S_Callback;
107 rgn->comment = comment;
108 return rgn->address;
109 }
110 return 0;
111 }
112
113void Mem32::free(ptr32_t address) {
114 if(address==0)
115 return;
116 for(size_t i=0; i<region.size(); ++i) {
117 if(region[i].address!=address)
118 continue;
119 std::free(region[i].real);
120 region[i].real = nullptr;
121 region[i].status = S_Unused;
122 compactage();
123 return;
124 }
125 Log::e("mem_free: heap block wan't allocated by script: ", reinterpret_cast<void*>(uint64_t(address)));
126 }
127
128const void* Mem32::deref(ptr32_t address, uint32_t size) {
129 auto r = translate(address);
130 if(r==nullptr) {
131 Log::e("deref: address translation failure: ", reinterpret_cast<void*>(uint64_t(address)));
132 return nullptr;
133 }
134 if(r->address+r->size < address+size) {
135 Log::e("deref: memmory block is too small: ", reinterpret_cast<void*>(uint64_t(address)), " ", size);
136 return nullptr;
137 }
138 const auto rgn = *r;
139 memSyncRead(rgn.type, rgn, address, size);
140 address -= rgn.address;
141 return reinterpret_cast<uint8_t*>(rgn.real)+address;
142 }
143
144void* Mem32::derefv(ptr32_t address, uint32_t size) {
145 auto rgn = translate(address);
146 if(rgn==nullptr) {
147 Log::e("deref: address translation failure: ", reinterpret_cast<void*>(uint64_t(address)));
148 return nullptr;
149 }
150 if(rgn->address+rgn->size < address+size) {
151 Log::e("deref: memmory block is too small: ", reinterpret_cast<void*>(uint64_t(address)), " ", size);
152 return nullptr;
153 }
154 if(rgn->status==S_Callback) {
155 Log::e("deref: unable to deref callback-memory: ", reinterpret_cast<void*>(uint64_t(address)));
156 return nullptr;
157 }
158 // memSyncRead(rgn->type, rgn, address, size);
159 address -= rgn->address;
160 return reinterpret_cast<uint8_t*>(rgn->real)+address;
161 }
162
163auto Mem32::deref(ptr32_t address) -> std::tuple<void*, uint32_t> {
164 auto r = translate(address);
165 if(r==nullptr) {
166 Log::e("deref: address translation failure: ", reinterpret_cast<void*>(uint64_t(address)));
167 return {nullptr,0};
168 }
169
170 const auto rgn = *r;
171 const ptr32_t dAddr = address - rgn.address;
172 const uint32_t size = rgn.size - dAddr;
173 memSyncRead(rgn.type, rgn, address, size);
174 return {reinterpret_cast<uint8_t*>(rgn.real)+dAddr, size};
175 }
176
177void Mem32::compactage() {
178 for(size_t i=0; i+1<region.size(); ) {
179 auto& a = region[i+0];
180 auto& b = region[i+1];
181 if(a.status==S_Unused && a.status==b.status && a.address+a.size==b.address) {
182 a.size += b.size;
183 b.size = 0;
184 region.erase(region.begin()+ptrdiff_t(i+1));
185 } else {
186 ++i;
187 }
188 }
189 }
190
191void Mem32::writeInt(ptr32_t address, int32_t v) {
192 auto rgn = translate(address);
193 if(rgn==nullptr) {
194 Log::e("mem_writeint: address translation failure: ", reinterpret_cast<void*>(uint64_t(address)));
195 return;
196 }
197
198 memSyncRead(rgn->type, *rgn, address, 4); // memory hazzards othwerwise!
199 const ptr32_t dAddr = address-rgn->address;
200 auto ptr = reinterpret_cast<uint8_t*>(rgn->real)+dAddr;
201 std::memcpy(ptr,&v,4);
202 memSyncWrite(rgn->type, *rgn, address, 4);
203 }
204
205int32_t Mem32::readInt(ptr32_t address) {
206 const auto r = translate(address);
207 if(r==nullptr) {
208 Log::e("mem_readint: address translation failure: ", reinterpret_cast<void*>(uint64_t(address)));
209 return 0;
210 }
211
212 const auto rgn = *r;
213 const ptr32_t dAddr = address-rgn.address;
214 memSyncRead(rgn.type, rgn, address, 4);
215
216 auto ptr = reinterpret_cast<uint8_t*>(rgn.real)+dAddr;
217 int32_t ret = 0;
218 std::memcpy(&ret,ptr,4);
219 return ret;
220 }
221
222void Mem32::copyBytes(ptr32_t psrc, ptr32_t pdst, uint32_t size) {
223 auto s = translate(psrc);
224 auto d = translate(pdst);
225 if(s==nullptr || s->status==S_Unused) {
226 Log::e("mem_copybytes: address translation failure: ", reinterpret_cast<void*>(uint64_t(psrc)));
227 return;
228 }
229 if(d==nullptr || d->status==S_Unused) {
230 Log::e("mem_copybytes: address translation failure: ", reinterpret_cast<void*>(uint64_t(pdst)));
231 return;
232 }
233 const auto src = *s;
234 const auto dst = *d;
235
236 size_t sOff = psrc - src.address;
237 size_t dOff = pdst - dst.address;
238 size_t sz = size;
239 if(src.size<sOff+size) {
240 Log::e("mem_copybytes: copy-size exceed source block size: ", size);
241 sz = std::min(src.size-sOff,sz);
242 }
243 if(dst.size<dOff+size) {
244 Log::e("mem_copybytes: copy-size exceed destination block size: ", size);
245 sz = std::min(dst.size-dOff,sz);
246 }
247 memSyncRead(src.type, src, ptr32_t(src.address+sOff), uint32_t(sz));
248 memSyncRead(dst.type, dst, ptr32_t(dst.address+dOff), uint32_t(sz));
249 std::memcpy(reinterpret_cast<uint8_t*>(dst.real)+dOff,
250 reinterpret_cast<uint8_t*>(src.real)+sOff,
251 sz);
252 memSyncWrite(dst.type, dst, ptr32_t(dst.address+dOff), uint32_t(sz));
253 }
254
255Mem32::Region* Mem32::implAlloc(uint32_t size) {
256 size = ((size+memAlign-1)/memAlign)*memAlign;
257
258 for(size_t i=0; i<region.size(); ++i) {
259 if(region[i].status!=S_Unused)
260 continue;
261 const uint32_t off = region[i].address % memAlign;
262 if(region[i].size<size+off)
263 continue;
264 if(size!=region[i].size) {
265 auto p2 = region[i];
266 p2.address+=size;
267 p2.size -=size;
268 region.insert(region.begin()+ptrdiff_t(i+1),p2);
269 }
270 region[i].size = size;
271 return &region[i];
272 }
273
274 return nullptr;
275 }
276
277Mem32::ptr32_t Mem32::realloc(ptr32_t address, uint32_t size) {
278 size = ((size+memAlign-1)/memAlign)*memAlign;
279 if(implRealloc(address,size))
280 return address;
281
282 auto next = implAlloc(size);
283 if(next==nullptr)
284 return 0;
285
286 auto src = translate(address);
287 if(src==nullptr) {
288 if(address!=0) {
289 Log::e("realloc: address translation failure: ", reinterpret_cast<void*>(uint64_t(address)));
290 compactage();
291 return 0;
292 }
293 }
294
295 auto ret = next->address;
296 next->status = S_Allocated;
297
298 if(src!=nullptr) {
299 next->type = src->type;
300 next->real = std::realloc(src->real, next->size);
301 next->comment = src->comment;
302 if(size>src->size)
303 std::memset(reinterpret_cast<uint8_t*>(next->real)+src->size, 0, size-src->size);
304
305 src->real = nullptr;
306 src->status = S_Unused;
307 compactage();
308 } else {
309 next->real = std::calloc(next->size, 1);
310 }
311
312 return ret;
313 }
314
315Mem32::Region* Mem32::implAllocAt(ptr32_t address, uint32_t size) {
316 for(size_t i=0; i<region.size(); ++i) {
317 auto& rgn = region[i];
318
319 if(address!=0) {
320 if(!(rgn.address<=address && address+size<=rgn.address+rgn.size))
321 continue;
322 } else {
323 if(rgn.size<size)
324 continue;
325 }
326
327 if(rgn.status!=S_Unused) {
328 Log::e("DMA: failed to pin a ",size," bytes of memory: block is in use");
329 return 0;
330 }
331
332 if(address==0)
333 address = region[i].address;
334
335 if(region[i].address<address) {
336 uint32_t off = (address-region[i].address);
337 auto p2 = region[i];
338 p2.address+=off;
339 p2.size -=off;
340 region.insert(region.begin()+ptrdiff_t(i+1),p2);
341 region[i].size = off;
342 ++i;
343 }
344 if(size!=region[i].size) {
345 auto p2 = region[i];
346 p2.address+=size;
347 p2.size -=size;
348 region.insert(region.begin()+ptrdiff_t(i+1),p2);
349 }
350
351 return &region[i];
352 }
353 return nullptr;
354 }
355
356bool Mem32::implRealloc(ptr32_t address, uint32_t nsize) {
357 if(address==0)
358 return false;
359 // NOTE: in place only
360 for(size_t i=0; i<region.size(); ++i) {
361 auto& rgn = region[i];
362 if(rgn.address!=address)
363 continue; //TODO: binary-search
364
365 if(nsize==rgn.size)
366 return true;
367
368 if(nsize<rgn.size) {
369 auto next = std::realloc(rgn.real, nsize);
370 if(next==nullptr)
371 return false;
372 rgn.real = next;
373 Region frgn(address+nsize, rgn.size-nsize);
374 rgn.size = nsize;
375 region.insert(region.begin() + intptr_t(i + 1), frgn);
376 return true;
377 }
378
379 if(i+1==region.size())
380 return false; // can't expand
381
382 auto& rgn1 = region[i+1];
383 if(rgn1.status==S_Unused && rgn.size + rgn1.size>=nsize) {
384 auto next = std::realloc(rgn.real, nsize);
385 if(next==nullptr)
386 return false;
387 std::memset(reinterpret_cast<uint8_t*>(next)+rgn.size, 0, nsize-rgn.size);
388 rgn1.address += (nsize-rgn.size);
389 rgn1.size -= (nsize-rgn.size);
390 rgn.real = next;
391 rgn.size = nsize;
392 if(rgn1.size==0)
393 compactage();
394 return true;
395 }
396
397 return false;
398 }
399 return false;
400 }
401
402Mem32::Region* Mem32::implTranslate(ptr32_t address) {
403 // TODO: binary-search
404 for(auto& rgn:region) {
405 if(rgn.address<=address && address<rgn.address+rgn.size && rgn.status!=S_Unused)
406 return &rgn;
407 }
408 return nullptr;
409 }
410
411Mem32::Region* Mem32::translate(ptr32_t address) {
412 if(address==0)
413 return nullptr;
414
415 auto ret = implTranslate(address);
416 if(ret==nullptr)
417 return nullptr;
418
419 auto& rgn = *ret;
420 if(rgn.status!=S_Callback)
421 return ret;
422
423 if(rgn.real==nullptr) {
424 rgn.real = std::calloc(rgn.size, 1);
425 if(rgn.real==nullptr)
426 throw std::bad_alloc();
427 }
428 return ret;
429 }
430
431void Mem32::memSyncRead(Type type, const Region& rgn, ptr32_t address, uint32_t size) {
432 if(type==Type::plain)
433 return;
434 auto m = memMap.find(type);
435 if(m==memMap.end()) {
436 throw std::logic_error("DMA: callback is not provided for callback memory!");
437 }
438 memSyncRead(m->second, rgn, address, size);
439 }
440
441void Mem32::memSyncRead(Callback& cb, const Region& rgn, ptr32_t address, uint32_t size) {
442 address -= rgn.address;
443
444 const uint32_t begin = address/cb.elementSize;
445 const uint32_t end = (address+size+cb.elementSize-1)/cb.elementSize;
446
447 auto ptr = reinterpret_cast<uint8_t*>(rgn.real);
448 for(size_t i=begin; i<end; ++i) {
449 cb.read(ptr+i*cb.elementSize, uint32_t(i));
450 }
451 }
452
453void Mem32::memSyncWrite(Type type, const Region& rgn, ptr32_t address, uint32_t size) {
454 if(type==Type::plain)
455 return;
456 auto m = memMap.find(type);
457 if(m==memMap.end()) {
458 Log::e("memSyncWrite: unable to write to mapped memory: ", reinterpret_cast<void*>(uint64_t(address)));
459 return;
460 }
461 memSyncWrite(m->second, rgn, address, size);
462 }
463
464void Mem32::memSyncWrite(Callback& cb, const Region& rgn, ptr32_t address, uint32_t size) {
465 if(!cb.write) {
466 Log::e("memSyncWrite: unable to write to mapped memory: ", reinterpret_cast<void*>(uint64_t(address)));
467 return;
468 }
469
470 address -= rgn.address;
471 const uint32_t begin = address/cb.elementSize;
472 const uint32_t end = (address+size+cb.elementSize-1)/cb.elementSize;
473
474 auto ptr = reinterpret_cast<uint8_t*>(rgn.real);
475 for(size_t i=begin; i<end; ++i) {
476 cb.write(ptr+i*cb.elementSize, uint32_t(i));
477 }
478 }
Compatibility::ptr32_t ptr32_t
Definition mem32.h:27
~Mem32()
Definition mem32.cpp:20
int32_t readInt(ptr32_t address)
Definition mem32.cpp:205
void * derefv(ptr32_t address, uint32_t size)
Definition mem32.cpp:144
ptr32_t alloc(uint32_t size, const char *comment=nullptr)
Definition mem32.cpp:89
ptr32_t realloc(ptr32_t address, uint32_t size)
Definition mem32.cpp:277
ptr32_t pin(void *mem, ptr32_t address, uint32_t size, const char *comment=nullptr)
Definition mem32.cpp:41
static constexpr uint32_t memAlign
Definition mem32.h:28
void copyBytes(ptr32_t src, ptr32_t dst, uint32_t size)
Definition mem32.cpp:222
auto deref(ptr32_t address) -> std::tuple< void *, uint32_t >
Definition mem32.cpp:163
void free(ptr32_t at)
Definition mem32.cpp:113
Type
Definition mem32.h:20
void writeInt(ptr32_t address, int32_t v)
Definition mem32.cpp:191
Mem32()
Definition mem32.cpp:11