Skip to content

MIPS64 MMU Emulation Bug #2119

Open
Open
@OBarronCS

Description

@OBarronCS

This issue is made to track/document how MIPS64 will fail to resolve memory address accesses larger than 0x7FFFFFFFUL without UC_TLB_VIRTUAL. Without UC_TLB_VIRTUAL, the code will use an emulated MMU which is described below.

#2111 allowed Unicorn to be instantiated with 64-bit MIPS architecture with the caveat that UC_TLB_VIRTUAL must be enabled so that all virtual addresses map directly to physical addresses, and that it doesn't use any emulated MMU logic. Otherwise, the emulator will use an emulated MMU, which currently doesn't resolve memory addresses correctly and instead returns an error.

For some background, MIPS64 has some virtual address segmentation architecturally defined:

Image

This is from page 29 in this MIPS reference: https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00091-2B-MIPS64PRA-AFP-05.04.pdf

Note that addresses in the inclusive range [0,0x3FFF_FFFF_FFFF_FFFF] are defined for user space, and other mappings are reserved for kernel/supervisor mode with specific functionality described in the doc linked above.

Currently, attempts to access memory addresses above USEG_LIMIT (which is 0x7FFFFFFFUL) will call a function that emulates an MMU. The memory lookup starts here:

if (address <= USEG_LIMIT) {
/* useg */
uint16_t segctl;
if (address >= 0x40000000UL) {
segctl = env->CP0_SegCtl2;
} else {
segctl = env->CP0_SegCtl2 >> 16;
}
ret = get_segctl_physical_address(env, physical, prot,
real_address, rw, access_type,
mmu_idx, segctl, 0x3FFFFFFF);
#if defined(TARGET_MIPS64)
} else if (address < 0x4000000000000000ULL) {
/* xuseg */
if (UX && address <= (0x3FFFFFFFFFFFFFFFULL & env->SEGMask)) {
ret = env->tlb->map_address(env, physical, prot,
real_address, rw, access_type);
} else {
ret = TLBRET_BADADDR;
}

The most relevant part starts at the #if defined(TARGET_MIPS64). Note that the UX variable comes from the MIPS status flag which indicates "user mode", which must be explicitly set via writing to the register (it is not enabled by default).

For example, you have to set the status register manually such as with the following snippet:

# The `1` at the 5th bit position indicates access to 64-bit User Segments (which go from address 0 to 0x3FFF_FFFF_FFFF_FFFF
status_register = 0b0100_0000_0000_0000_0010_0100
# I found the other `1` bits in this string to be necessary as well
emu.reg_write(UC_MIPS_REG_CP0_STATUS, status_register)

For more details on the MIPS 64 status register, check out https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00091-2B-MIPS64PRA-AFP-06.03.pdf where page 213 (section 9.33 Status Register) describes the bits of the register.

The env->tlb->map_address is a function pointer to r4k_map_address.

int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
target_ulong address, int rw, int access_type)
{
uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
uint32_t MMID = env->CP0_MemoryMapID;

This is where the MMU emulation currently fails. The function doesn't return a valid address, and returns TLBRET_NOMATCH.

Possible fixes/changes:

  • It's possible we just need to do some more setup to make this emulated MMU work (set some registers?)
  • We should change the default value of the CP0_STATUS register so user memory is accessible by default.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions