In c/source/emulation/softmmu/soft_memory.h the emulated memory is stored in a structure called memory which has the following members
struct Memory {
U8 flags[NUMBER_OF_PAGES];
struct KProcess* process;
struct Page* mmu[NUMBER_OF_PAGES];
U32 read[NUMBER_OF_PAGES];
U32 write[NUMBER_OF_PAGES];
U32 ramPage[NUMBER_OF_PAGES];
};
- flags: tells us if the memory has read, write or executable permission
- mmu: is an array of struct Page which contains function pointers that will read and write to an address
- read/write: this is an offset to physical memory, this is a cached calculation that allows better performance. If the cache isn’t there then it will call mmu[page]->read(address) / mmu[page]->write(address)
- ramPage: the index into a block of memory that has been reserved for emulation
struct Page is defined as
struct Page {
U8 (*readb)(struct KThread* thread, U32 address);
void (*writeb)(struct KThread* thread, U32 address, U8 value);
U16 (*readw)(struct KThread* thread, U32 address);
void (*writew)(struct KThread* thread, U32 address, U16 value);
U32 (*readd)(struct KThread* thread, U32 address);
void (*writed)(struct KThread* thread, U32 address, U32 value);
void (*clear)(struct Memory* memory, U32 page);
U8* (*physicalAddress)(struct KThread* thread, U32 address);
};
When initialized, all of the values in mmu will point to invalidPage. invalidPage is a Page structure where all of its functions will throw an exception
U8 invalid_readb(struct KThread* thread, U32 address) {
U8 invalid_readb(struct KThread* thread, U32 address) {
seg_mapper(thread, address);
return 0;
}
struct Page invalidPage = {invalid_readb, invalid_writeb, invalid_readw, invalid_writew, invalid_readd, invalid_writed, pf_clear, invalid_physicalAddress};
When memory is allocated, see allocPages in c/source/emulation/softmmu/soft_memory.c , the mmu and flags will be updated
pageType = &ramOnDemandPage;
for (i=0;i<pageCount;i++) {
if (memory->mmu[page]!=&invalidPage) {
memory->mmu[page]->clear(memory, page);
}
memory->mmu[page] = pageType;
memory->flags[page] = permissions;
memory->ramPage[page] = 0;
memory->read[page] = 0;
memory->write[page] = 0;
page++;
}
Notice that the memory hasn’t actually been allocated, it has just been reserved. Now when the app reads/writes to this page it will call into the ramOnDemmandPage which will then grab a free ram page and assign it to this emulated page.
Here is the on demmand code from c/source/emulation/softmmu/soft_ram.h
static void ondemmand(struct KThread* thread, U32 address) {
struct Memory* memory = thread->process->memory;
U32 ram = allocRamPage();
U32 page = address >> PAGE_SHIFT;
U32 flags = memory->flags[page];
BOOL read = IS_PAGE_READ(flags) | IS_PAGE_EXEC(flags);
BOOL write = IS_PAGE_WRITE(flags);
if (read || write) {
memory->ramPage[page] = ram;
memory->flags[page] |= PAGE_IN_RAM;
}
if (read && write) {
memory->mmu[page] = &ramPageWR;
memory->read[page] = TO_TLB(ram, address);
memory->write[page] = TO_TLB(ram, address);
} else if (write) {
memory->mmu[page] = &ramPageWO;
memory->write[page] = TO_TLB(ram, address);
} else if (read) {
memory->mmu[page] = &ramPageRO;
memory->read[page] = TO_TLB(ram, address);
} else {
memory->mmu[page] = &invalidPage;
}
}
static U8 ondemand_ram_readb(struct KThread* thread, U32 address) {
ondemmand(thread, address);
return ram_readb(thread, address);
}