hv: Implement emulated guest CPU shutdown

Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
Hector Martin 2023-04-19 01:00:59 +09:00
parent 4d7b18d382
commit 4638632657
7 changed files with 74 additions and 11 deletions

View file

@ -107,7 +107,7 @@ class HV(Reloadable):
self.wdt_cpu = None self.wdt_cpu = None
self.smp = True self.smp = True
self.hook_exceptions = False self.hook_exceptions = False
self.started_cpus = set() self.started_cpus = {}
self.started = False self.started = False
self.ctx = None self.ctx = None
self.hvcall_handlers = {} self.hvcall_handlers = {}
@ -658,7 +658,6 @@ class HV(Reloadable):
MDSCR_EL1, MDSCR_EL1,
} }
ro = { ro = {
CYC_OVRD_EL1,
ACC_CFG_EL1, ACC_CFG_EL1,
ACC_OVRD_EL1, ACC_OVRD_EL1,
} }
@ -673,7 +672,15 @@ class HV(Reloadable):
shadow.add(DBGWVRn_EL1(i)) shadow.add(DBGWVRn_EL1(i))
value = 0 value = 0
if enc in shadow: if enc == CYC_OVRD_EL1 and iss.DIR == MSR_DIR.WRITE:
if iss.Rt != 31:
value = ctx.regs[iss.Rt]
self.log(f"Skip: msr {name}, x{iss.Rt} = {value:x}")
if value & 1:
self.log("Guest is shutting down CPU")
self.p.hv_exit_cpu()
del self.started_cpus[self.ctx.cpu_id]
elif enc in shadow:
if iss.DIR == MSR_DIR.READ: if iss.DIR == MSR_DIR.READ:
value = self.sysreg[self.ctx.cpu_id].setdefault(enc, 0) value = self.sysreg[self.ctx.cpu_id].setdefault(enc, 0)
self.log(f"Shadow: mrs x{iss.Rt}, {name} = {value:x}") self.log(f"Shadow: mrs x{iss.Rt}, {name} = {value:x}")
@ -1503,6 +1510,34 @@ class HV(Reloadable):
self.map_hook(addr, 4, read=lambda base, off, width: pg_overrides[base + off]) self.map_hook(addr, 4, read=lambda base, off, width: pg_overrides[base + off])
self.add_tracer(irange(addr, 4), "PMGR HACK", TraceMode.RESERVED) self.add_tracer(irange(addr, 4), "PMGR HACK", TraceMode.RESERVED)
cpu_hack = [
# 0x210e20020,
# 0x211e20020,
# 0x212e20020,
]
def wh(base, off, data, width):
if isinstance(data, list):
data = data[0]
self.log(f"CPU W {base:x}+{off:x}:{width} = 0x{data:x}: Dangerous write")
for addr in cpu_hack:
self.map_hook(addr, 8, write=wh)
self.add_tracer(irange(addr, 8), "CPU HACK", TraceMode.RESERVED)
def cpu_state_rh(base, off, width):
data = ret = self.p.read64(base + off)
die = base // 0x20_0000_0000
cluster = (base >> 24) & 0xf
cpu = (base >> 20) & 0xf
for i, j in self.started_cpus.items():
if j == (die, cluster, cpu):
break
else:
ret &= ~0xff
self.log(f"CPU STATE R {base:x}+{off:x}:{width} = 0x{data:x} -> 0x{ret:x}")
return ret
def cpustart_wh(base, off, data, width): def cpustart_wh(base, off, data, width):
self.log(f"CPUSTART W {base:x}+{off:x}:{width} = 0x{data:x}") self.log(f"CPUSTART W {base:x}+{off:x}:{width} = 0x{data:x}")
if off >= 8: if off >= 8:
@ -1512,6 +1547,9 @@ class HV(Reloadable):
for i in range(32): for i in range(32):
if data & (1 << i): if data & (1 << i):
self.start_secondary(die, cluster, i) self.start_secondary(die, cluster, i)
cpu_state = 0x210050100 | (die << 27) | (cluster << 24) | (i << 20)
self.map_hook(cpu_state, 8, read=cpu_state_rh)
self.add_tracer(irange(addr, 8), "CPU STATE HACK", TraceMode.RESERVED)
die_count = self.adt["/arm-io"].die_count if hasattr(self.adt["/arm-io"], "die-count") else 1 die_count = self.adt["/arm-io"].die_count if hasattr(self.adt["/arm-io"], "die-count") else 1
@ -1546,7 +1584,7 @@ class HV(Reloadable):
self.log(f" CPU #{index}: RVBAR = {entry:#x}") self.log(f" CPU #{index}: RVBAR = {entry:#x}")
self.sysreg[index] = {} self.sysreg[index] = {}
self.started_cpus.add(index) self.started_cpus[index] = (die, cluster, cpu)
self.p.hv_start_secondary(index, entry) self.p.hv_start_secondary(index, entry)
def setup_adt(self): def setup_adt(self):
@ -1867,7 +1905,7 @@ class HV(Reloadable):
# Does not return # Does not return
self.started = True self.started = True
self.started_cpus.add(0) self.started_cpus[0] = (0, 0, 0)
self.p.hv_start(self.entry, self.guest_base + self.bootargs_off) self.p.hv_start(self.entry, self.guest_base + self.bootargs_off)
from .. import trace from .. import trace

View file

@ -597,6 +597,7 @@ class M1N1Proxy(Reloadable):
P_HV_WRITE_HCR = 0xc0c P_HV_WRITE_HCR = 0xc0c
P_HV_MAP_VIRTIO = 0xc0d P_HV_MAP_VIRTIO = 0xc0d
P_VIRTIO_PUT_BUFFER = 0xc0e P_VIRTIO_PUT_BUFFER = 0xc0e
P_HV_EXIT_CPU = 0xc0f
P_FB_INIT = 0xd00 P_FB_INIT = 0xd00
P_FB_SHUTDOWN = 0xd01 P_FB_SHUTDOWN = 0xd01
@ -1038,6 +1039,8 @@ class M1N1Proxy(Reloadable):
return self.request(self.P_HV_MAP_VIRTIO, base, config) return self.request(self.P_HV_MAP_VIRTIO, base, config)
def virtio_put_buffer(self, base, qu, idx, length): def virtio_put_buffer(self, base, qu, idx, length):
return self.request(self.P_VIRTIO_PUT_BUFFER, base, qu, idx, length) return self.request(self.P_VIRTIO_PUT_BUFFER, base, qu, idx, length)
def hv_exit_cpu(self, cpu=-1):
return self.request(self.P_HV_EXIT_CPU, cpu)
def fb_init(self): def fb_init(self):
return self.request(self.P_FB_INIT) return self.request(self.P_FB_INIT)

View file

@ -29,7 +29,7 @@ int hv_pinned_cpu;
int hv_want_cpu; int hv_want_cpu;
static bool hv_has_ecv; static bool hv_has_ecv;
static bool hv_should_exit; static bool hv_should_exit[MAX_CPUS];
bool hv_started_cpus[MAX_CPUS]; bool hv_started_cpus[MAX_CPUS];
u64 hv_cpus_in_guest; u64 hv_cpus_in_guest;
u64 hv_saved_sp[MAX_CPUS]; u64 hv_saved_sp[MAX_CPUS];
@ -111,7 +111,7 @@ static void hv_set_gxf_vbar(void)
void hv_start(void *entry, u64 regs[4]) void hv_start(void *entry, u64 regs[4])
{ {
hv_should_exit = false; memset(hv_should_exit, 0, sizeof(hv_should_exit));
memset(hv_started_cpus, 0, sizeof(hv_started_cpus)); memset(hv_started_cpus, 0, sizeof(hv_started_cpus));
hv_started_cpus[0] = 1; hv_started_cpus[0] = 1;
@ -148,10 +148,17 @@ void hv_start(void *entry, u64 regs[4])
hv_wdt_stop(); hv_wdt_stop();
hv_should_exit = true;
printf("HV: Exiting hypervisor (main CPU)\n"); printf("HV: Exiting hypervisor (main CPU)\n");
for (int i = 0; i < MAX_CPUS; i++) { spin_unlock(&bhl);
// Wait a bit for the guest CPUs to exit on their own if they are in the process.
udelay(200000);
spin_lock(&bhl);
hv_started_cpus[0] = false;
for (int i = 1; i < MAX_CPUS; i++) {
hv_should_exit[i] = true;
if (hv_started_cpus[i]) { if (hv_started_cpus[i]) {
printf("HV: Waiting for CPU %d to exit\n", i); printf("HV: Waiting for CPU %d to exit\n", i);
spin_unlock(&bhl); spin_unlock(&bhl);
@ -201,11 +208,11 @@ static void hv_enter_secondary(void *entry, u64 regs[4])
spin_lock(&bhl); spin_lock(&bhl);
hv_should_exit = true;
printf("HV: Exiting from CPU %d\n", smp_id()); printf("HV: Exiting from CPU %d\n", smp_id());
__atomic_and_fetch(&hv_cpus_in_guest, ~BIT(smp_id()), __ATOMIC_ACQUIRE); __atomic_and_fetch(&hv_cpus_in_guest, ~BIT(smp_id()), __ATOMIC_ACQUIRE);
hv_started_cpus[smp_id()] = false;
spin_unlock(&bhl); spin_unlock(&bhl);
} }
@ -228,6 +235,15 @@ void hv_start_secondary(int cpu, void *entry, u64 regs[4])
smp_call4(cpu, hv_enter_secondary, (u64)entry, (u64)regs, 0, 0); smp_call4(cpu, hv_enter_secondary, (u64)entry, (u64)regs, 0, 0);
} }
void hv_exit_cpu(int cpu)
{
if (cpu == -1)
cpu = smp_id();
printf("HV: Requesting exit of CPU#%d from the guest\n", cpu);
hv_should_exit[cpu] = true;
}
void hv_rendezvous(void) void hv_rendezvous(void)
{ {
int timeout = 1000000; int timeout = 1000000;
@ -343,7 +359,7 @@ void hv_arm_tick(bool secondary)
void hv_maybe_exit(void) void hv_maybe_exit(void)
{ {
if (hv_should_exit) { if (hv_should_exit[smp_id()]) {
hv_exit_guest(); hv_exit_guest();
} }
} }

View file

@ -101,6 +101,7 @@ void hv_set_elr(u64 val);
void hv_init(void); void hv_init(void);
void hv_start(void *entry, u64 regs[4]); void hv_start(void *entry, u64 regs[4]);
void hv_start_secondary(int cpu, void *entry, u64 regs[4]); void hv_start_secondary(int cpu, void *entry, u64 regs[4]);
void hv_exit_cpu(int cpu);
void hv_rendezvous(void); void hv_rendezvous(void);
bool hv_switch_cpu(int cpu); bool hv_switch_cpu(int cpu);
void hv_pin_cpu(int cpu); void hv_pin_cpu(int cpu);

View file

@ -385,6 +385,7 @@ static void hv_exc_exit(struct exc_info *ctx)
reg_set(SYS_IMP_APL_PMCR0, PERCPU(exc_entry_pmcr0_cnt)); reg_set(SYS_IMP_APL_PMCR0, PERCPU(exc_entry_pmcr0_cnt));
msr(CNTVOFF_EL2, stolen_time); msr(CNTVOFF_EL2, stolen_time);
spin_unlock(&bhl); spin_unlock(&bhl);
hv_maybe_exit();
__atomic_or_fetch(&hv_cpus_in_guest, BIT(smp_id()), __ATOMIC_ACQUIRE); __atomic_or_fetch(&hv_cpus_in_guest, BIT(smp_id()), __ATOMIC_ACQUIRE);
hv_set_spsr(ctx->spsr); hv_set_spsr(ctx->spsr);

View file

@ -487,6 +487,9 @@ int proxy_process(ProxyRequest *request, ProxyReply *reply)
case P_HV_WRITE_HCR: case P_HV_WRITE_HCR:
hv_write_hcr(request->args[0]); hv_write_hcr(request->args[0]);
break; break;
case P_HV_EXIT_CPU:
hv_exit_cpu(request->args[0]);
break;
case P_FB_INIT: case P_FB_INIT:
fb_init(request->args[0]); fb_init(request->args[0]);

View file

@ -133,6 +133,7 @@ typedef enum {
P_HV_WRITE_HCR, P_HV_WRITE_HCR,
P_HV_MAP_VIRTIO, P_HV_MAP_VIRTIO,
P_VIRTIO_PUT_BUFFER, P_VIRTIO_PUT_BUFFER,
P_HV_EXIT_CPU,
P_FB_INIT = 0xd00, P_FB_INIT = 0xd00,
P_FB_SHUTDOWN, P_FB_SHUTDOWN,