Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MIPS64 MMU Emulation Bug #2119

Open
OBarronCS opened this issue Feb 22, 2025 · 1 comment
Open

MIPS64 MMU Emulation Bug #2119

OBarronCS opened this issue Feb 22, 2025 · 1 comment

Comments

@OBarronCS
Copy link
Contributor

OBarronCS commented Feb 22, 2025

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.
@PhilippTakacs
Copy link
Contributor

As far as I understand your observation and the code is correct. The UX bit of the C0 Status register controlles if the 64 bit user segment is enabled or not and defaults undefined. When you want to emulate some code which assumes this is already done correctly you need to set this bits manual. I would keep the current behavior of setting it to 0, because this is most likely the state on a cold boot/reset.

For a correct working mips mmu you might also need to check the KX and SX bits of the Status register.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants