-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Support MIPS64 - write correct PC register width on uc_emu_start #2111
Conversation
uc.c
Outdated
uc_reg_write(uc, UC_MIPS_REG_PC, &begin_pc32); | ||
if (uc->mode & UC_MODE_64) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not an expert of unicorn, but, I'd think this should be UC_MODE_MIPS64
, no? And based on the TODO, it was just never accounted for...
See here for an example
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose UC_MODE_MIPS64
is more correct and forward-compatible. Right now, it is the same value as UC_MODE_64
(the constant value 8).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see - I assumed UC_MODE_64 was a generic 64-bit mode flag, but on further look it is only meant to apply for x86 based on the code and just so happens to line up with the MIPS64 flag
unicorn/include/unicorn/unicorn.h
Lines 139 to 142 in 8d52ece
// x86 / x64 | |
UC_MODE_16 = 1 << 1, // 16-bit mode | |
UC_MODE_32 = 1 << 2, // 32-bit mode | |
UC_MODE_64 = 1 << 3, // 64-bit mode |
I made this change.
Thanks for giving a try. Looks good to me and will merge once CI passed. |
I found an edge case related to MIPS64 TLB that needs to be considered. Posting an update in a few. |
Please also consider adding a (maybe simple) test case so that we won't break it in our future release. |
There seems to be limited functionality even with UC_TLB_VIRTUAL enabled in MIPS64. Some instructions work, others do not. Unfortunately, there seems to be more at play here. I'm sometimes getting exception #!/usr/bin/env python3
from unicorn import *
from unicorn.mips_const import *
print(unicorn.uc_version())
emu = Uc(UC_ARCH_MIPS, UC_MODE_64 | UC_MODE_BIG_ENDIAN)
emu.ctl_set_tlb_mode(UC_TLB_VIRTUAL)
# def hook(uc, vaddr, a, b, c):
# print(hex(vaddr), a, b, c)
# return True
# emu.hook_add(UC_HOOK_TLB_FILL, hook)
# daddu $gp, $gp, $ra
code = b"\x03\x9f\xe0\x2d" * 2
ADDRESS = 0x1000
ADDRESS = 0x555555d56370 & ~0xFFF
emu.mem_map(ADDRESS, 4 * 1024)
emu.mem_write(ADDRESS, code)
# emu.reg_write(UC_MIPS_REG_CP0_STATUS, 0b000)
emu.reg_write(UC_MIPS_REG_PC, ADDRESS)
emu.reg_write(UC_MIPS_REG_GP, 0x1)
emu.reg_write(UC_MIPS_REG_RA, 0x1)
try:
emu.emu_start(ADDRESS, 0, count=2)
except Exception as e:
print(e)
print(f"PC: {hex(emu.reg_read(UC_MIPS_REG_PC))}") Additionally, for future people who might try to get this to work, here are other things I tried: I tried to get this to work without UC_TLB_VIRTUAL, however addresses over 0x80000000 (the 32-bit usermode limit) will still give an exception, and I was unable to find the root cause of this. Basically, in 64-bit mode, the TLB has the following translation scheme: You can find qemu doing these translations/memory checks here https://github.com/qemu/qemu/blob/495de0fd82d8bb2d7035f82d9869cfeb48de2f9e/target/mips/system/physaddr.c#L145 I assumed that the following location was doing a final step of the mapping logic, but I was incorrect. unicorn/qemu/target/mips/helper.c Lines 48 to 53 in f2e80ff
|
For accessing high address, you need to switch to supervised levels? Not an MIPS expert unfortunately =( |
I played around with bits of the status register, and didn't see any difference in behavior. It appears to throw exceptions on certain types of instructions, even ones that do not read/write from memory Here is the output of the Unicorn/qemu logging with Python script in my previous comment, running the single instruction
|
So the execption was thrown during translation. I will have a look shortly. |
I found the bug that was preventing certain instructions from being executed - the CPU model was still being set to a 32-bit MIPS model, instead of a 64-bit one. With the latest commit, and with UC_TLB_VIRTUAL enabled, MIPS64 appears to be working great now! I'll make another commit to add a test to Unicorn. This setup now works great. If you use MIPS64, make sure to enable Notes on further fixing MIPS64 memory lookupsWithout TLB_VIRTUAL, there is some more work needed to get memory accesses functioning for addresses
(see earlier comment on MIPS64 mappings #2111 (comment)) The # 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) Reference - "MIPS Architecture For Programmers Volume III: MIPS64..."
The call to |
include/unicorn/mips.h
Outdated
UC_CPU_MIPS64_R4000 = 0, | ||
// This is used as an index into the array defined in "qemu/target/mips/translate_init.inc.c". | ||
// 64-bit CPU models are defined in the array directly after 32-bit models | ||
UC_CPU_MIPS64_R4000 = UC_CPU_MIPS32_ENDING, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather to index the cpu like UC_CPU_MIPS32_ENDING + uc->cpu_model
instead if chaning the enum value here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made this change 👍
Thanks for the valuable analysis. I was busy with distributions issues and didn't have a look yet sorry. For the VTLB issue, it is definitely a bug and I will have a look. |
tests/regress/mips64.py
Outdated
# daddu $gp, $gp, $ra # a 64-bit instruction. This is important - it ensures the selected CPU model is 64-bit, otherwise it would crash | ||
# move $t1, $v0 | ||
|
||
code = b"\x03\x9f\xe0\x2d" + b"\x00\x40\x48\x25" |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
The CI is failing with a strange error:
The Python package in the CI/CD is not having the |
Since we run tests for the Python 2.7 wheel too, we need to add a decorator to skip the test when the test itself is using a feature not available for the Python 2.
|
tests/regress/mips64.py
Outdated
# This will raise an exception if MIPS64 fails | ||
uc.emu_start(ADDRESS, 0, count=2) | ||
|
||
assert uc.reg_read(UC_MIPS_REG_PC) == 0x0120003000 + 8 |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call - done
@OBarronCS I'm going to merge this, thanks! Do you have a PoC for the VTLB bug or would you like to raise another standalone issue for that? |
Thanks! Also, please raise another issue about the virtual TLB at your convenience. |
Great! I will make an issue related to the MIPS64 memory mappings so that it's searchable for future contributors. |
This PR allows MIPS64 to work - previously MIPS64 would crash immediately after starting the emulator - see #2109.
The current
uc_emu_start
code makes the assumptions that MIPS has a 32-bit program counter. Adding a check to see if it's 64-bit and writing the correct value to PC makes MIPS64 run.unicorn/uc.c
Lines 1021 to 1026 in 8d52ece