chainload: Support old-school call based chainloading

This is useful in the middle of the HV exception handler to reboot m1n1
entirely, since we can't do a clean exit the way we would for normal
chainloading.

Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
Hector Martin 2021-05-04 22:45:18 +09:00
parent 826bdb709c
commit 954408cc65
2 changed files with 28 additions and 9 deletions

View file

@ -1,10 +1,11 @@
#!/usr/bin/env python3
import argparse, pathlib
import argparse, pathlib, time
parser = argparse.ArgumentParser(description='Mach-O loader for m1n1')
parser.add_argument('payload', type=pathlib.Path)
parser.add_argument('-s', '--sepfw', action="store_true")
parser.add_argument('-c', '--call', action="store_true")
args = parser.parse_args()
from setup import *
@ -76,9 +77,19 @@ p.dc_cvau(stub.addr, stub.len)
p.ic_ivau(stub.addr, stub.len)
print(f"Entry point: 0x{entry:x}")
print(f"Jumping to stub at 0x{stub.addr:x}")
p.reboot(stub.addr, new_base + bootargs_off, image_addr, new_base, image_size)
if args.call:
print(f"Shutting down MMU...")
try:
p.mmu_shutdown()
except ProxyCommandError:
pass
print(f"Jumping to stub at 0x{stub.addr:x}")
p.call(stub.addr, new_base + bootargs_off, image_addr, new_base, image_size, reboot=True)
else:
print(f"Rebooting into stub at 0x{stub.addr:x}")
p.reboot(stub.addr, new_base + bootargs_off, image_addr, new_base, image_size)
time.sleep(1)
iface.nop()
print("Proxy is alive again")

View file

@ -53,6 +53,14 @@ def chexdump32(s, st=0, abbreviate=True):
last = val
skip = False
# Hack to disable input buffer flushing
class Serial(serial.Serial):
def _reset_input_buffer(self):
return
def reset_input_buffer(self):
super()._reset_input_buffer()
class UartError(RuntimeError):
pass
@ -122,7 +130,7 @@ class UartInterface:
self.devpath = device
self.baudrate = baud
device = serial.Serial(self.devpath, baud)
device = Serial(self.devpath, baud)
self.dev = device
self.dev.timeout = 0
@ -268,7 +276,7 @@ class UartInterface:
def wait_boot(self):
try:
self.reply(self.REQ_BOOT)
return self.reply(self.REQ_BOOT)
except:
# Over USB, reboots cause a reconnect
self.dev.close()
@ -296,7 +304,7 @@ class UartInterface:
if no_reply:
return
elif reboot:
return self.reply(self.REQ_BOOT)
return self.wait_boot()
else:
return self.reply(self.REQ_PROXY)
@ -485,7 +493,7 @@ class M1N1Proxy:
if self.debug:
print("<<<< %08x: %08x %08x %08x %08x %08x %08x"%tuple([opcode] + args))
reply = self.iface.proxyreq(req, reboot=reboot, no_reply=no_reply, pre_reply=None)
if no_reply:
if no_reply or reboot and reply is None:
return
ret_fmt = "q" if signed else "Q"
rop, status, retval = struct.unpack("<Qq" + ret_fmt, reply)
@ -527,10 +535,10 @@ class M1N1Proxy:
self.request(self.P_NOP)
def exit(self, retval=0):
self.request(self.P_EXIT, retval)
def call(self, addr, *args):
def call(self, addr, *args, reboot=False):
if len(args) > 4:
raise ValueError("Too many arguments")
return self.request(self.P_CALL, addr, *args)
return self.request(self.P_CALL, addr, *args, reboot=reboot)
def reboot(self, addr, *args, el1=False):
if len(args) > 4:
raise ValueError("Too many arguments")