Soft MMU

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