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); }