--- hypervisor/arch/riscv/control.c | 213 ++++++++++++++++++-- hypervisor/arch/riscv/include/asm/control.h | 13 ++ 2 files changed, 209 insertions(+), 17 deletions(-)
diff --git a/hypervisor/arch/riscv/control.c b/hypervisor/arch/riscv/control.c index 263708a7..fa9921e6 100644 --- a/hypervisor/arch/riscv/control.c +++ b/hypervisor/arch/riscv/control.c @@ -2,70 +2,230 @@ * Jailhouse, a Linux-based partitioning hypervisor * * Copyright (c) Siemens AG, 2020 + * Copyright (c) OTH Regensburg, 2022 * * Authors: - * Jan Kiszka <[email protected]> + * Konrad Schwarz <[email protected]> + * Ralf Ramsauer <[email protected]> * * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. */ #include <jailhouse/control.h> +#include <jailhouse/paging.h> #include <jailhouse/printk.h> +#include <jailhouse/string.h> +#include <asm/control.h> +#include <asm/csr64.h> #include <asm/sbi.h> -int arch_cell_create(struct cell *cell) +extern void __attribute__((noreturn)) riscv_park_loop(void); + +static void riscv_cpu_reset(unsigned long pc, unsigned long a0, unsigned long a1) +{ + union registers *regs; + + regs = &this_cpu_data()->guest_regs; + + memset(regs, 0, sizeof(union registers)); + regs->pc = pc; + regs->a0 = a0; + regs->a1 = a1; + + /* TBD: Check if we need to clear more registers */ + csr_write(sepc, regs->pc); + csr_write(CSR_VSATP, 0); + csr_write(CSR_HIE, 0); + csr_write(CSR_HIP, 0); + csr_write(CSR_VSSTATUS, 0); + + csr_set(sstatus, SR_SPP); /* Return to VS-Mode */ +} + +void arch_check_events(void) { - return -ENOSYS; + struct public_per_cpu *pcpu; + + pcpu = this_cpu_public(); + + spin_lock(&pcpu->control_lock); + + if (pcpu->suspend_cpu) { + pcpu->cpu_suspended = true; + + spin_unlock(&pcpu->control_lock); + + while (pcpu->suspend_cpu) + cpu_relax(); + + spin_lock(&pcpu->control_lock); + } + + pcpu->cpu_suspended = false; + + if (pcpu->wait_for_power_on) { + pcpu->wait_for_power_on = false; + goto out; + } + + if (pcpu->hsm.state == START_PENDING) { /* We could also check if START_PENDING is set... */ + pcpu->reset = false; + riscv_cpu_reset(pcpu->hsm.start_addr, pcpu->phys_id, pcpu->hsm.opaque); + riscv_paging_vcpu_init(&this_cell()->arch.mm); + pcpu->hsm.state = STARTED; + } else if (pcpu->park) { + riscv_park_cpu(); + } + + if (pcpu->reset) { + pcpu->reset = false; + riscv_cpu_reset(0x0, pcpu->phys_id, 0); + riscv_paging_vcpu_init(&this_cell()->arch.mm); + } + +out: + spin_unlock(&pcpu->control_lock); } int arch_map_memory_region(struct cell *cell, const struct jailhouse_memory *mem) { - return -ENOSYS; + int result = paging_create( + &cell->arch.mm, + mem->flags & JAILHOUSE_MEM_COMM_REGION ? + paging_hvirt2phys (&cell->comm_page) : mem->phys_start, + mem->size, mem->virt_start, + /* guests require U access */ + RISCV_PTE_FLAG(V) | RISCV_PTE_FLAG(U) | + (mem->flags & JAILHOUSE_MEM_READ ? RISCV_PTE_FLAG(R) : 0) | + (mem->flags & JAILHOUSE_MEM_WRITE ? RISCV_PTE_FLAG(W) : 0) | + (mem->flags & JAILHOUSE_MEM_EXECUTE ? RISCV_PTE_FLAG(X) : 0), + PAGING_COHERENT | + (mem->flags & JAILHOUSE_MEM_NO_HUGEPAGES ? 0 : PAGING_HUGE)); + + return result; } -int arch_unmap_memory_region(struct cell *cell, - const struct jailhouse_memory *mem) +int arch_unmap_memory_region(struct cell *const cell, + const struct jailhouse_memory *mem_original) { - return -ENOSYS; + return paging_destroy (&cell->arch.mm, + mem_original->virt_start, + mem_original->size, + PAGING_COHERENT); } -void arch_check_events(void) +void arch_flush_cell_vcpu_caches(struct cell *const cell) { + /* the necessary TLB invalidation has already been performed + in the map/unmap routines */ + /* doing it here would require the entire cell's TLB to be + flushed, because this function does not receive information + about the memory segment to invalidate. That would be overkill + (although the current unmap_region implementation does this + nonetheless, owing to other API shortcomings). + */ } -void arch_flush_cell_vcpu_caches(struct cell *cell) +int arch_cell_create(struct cell *const cell) { + struct public_per_cpu *ppc; + unsigned int cpu; + + cell->arch.mm.root_paging = riscv_Sv39x4; + cell->arch.mm.root_table = page_alloc_aligned(&mem_pool, CELL_ROOT_PT_PAGES); + + for_each_cpu(cpu, &cell->cpu_set) { + ppc = public_per_cpu(cpu); + ppc->wait_for_power_on = false; + ppc->park = true; + ppc->reset = false; + } + + if (!cell->arch.mm.root_table) + return -ENOMEM; + + return 0; } -void arch_cell_destroy(struct cell *cell) +void arch_cell_destroy(struct cell *const cell) { + unsigned int cpu; + struct public_per_cpu *ppc; + + page_free(&mem_pool, cell->arch.mm.root_table, CELL_ROOT_PT_PAGES); + + for_each_cpu(cpu, &cell->cpu_set) { + ppc = public_per_cpu(cpu); + ppc->wait_for_power_on = false; + ppc->park = true; + ppc->reset = false; + } } -void arch_cell_reset(struct cell *cell) +void arch_cell_reset(struct cell *const cell) { + unsigned int first = first_cpu(&cell->cpu_set); + struct public_per_cpu *pcpu; + unsigned int cpu; + + /* + * All CPUs except the first one shall not be started, they shall park + */ + pcpu = public_per_cpu(first); + pcpu->wait_for_power_on = false; + pcpu->park = false; + for_each_cpu_except(cpu, &cell->cpu_set, first) + public_per_cpu(cpu)->wait_for_power_on = true; } -void arch_prepare_shutdown(void) +void arch_reset_cpu(unsigned int const cpu_id) { + public_per_cpu(cpu_id)->reset = true; + + resume_cpu(cpu_id); } -void __attribute__((noreturn)) arch_panic_stop(void) +void arch_park_cpu(unsigned int const cpu_id) { - while (1); + struct public_per_cpu *pc = public_per_cpu(cpu_id); + + if (pc->hsm.state == STOPPED) + return; + + if (pc->cpu_suspended == true) + return; + + spin_lock(&pc->control_lock); + pc->park = true; + arch_send_event(pc); + spin_unlock(&pc->control_lock); + + while (pc->hsm.state != STOPPED) + cpu_relax(); + } -void arch_panic_park(void) +void arch_prepare_shutdown(void) { } -void arch_reset_cpu(unsigned int const cpu_id) +void __attribute__((noreturn)) arch_panic_stop(void) { + /* No need to check return code here */ + sbi_hart_stop(); + + /* + * If this happens, which should never be the case, then let the CPU + * execute the park loop. + */ + riscv_park_loop(); } -void arch_park_cpu(unsigned int cpu_id) +void arch_panic_park(void) { + riscv_park_cpu(); } void arch_send_event(struct public_per_cpu *target_data) @@ -83,3 +243,22 @@ void arch_send_event(struct public_per_cpu *target_data) panic_stop(); } } + +void riscv_park_cpu(void) +{ + struct public_per_cpu *pcpu = this_cpu_public(); + + pcpu->hsm.state = STOPPED; + /* + * BUG FIXME: The Timer IRQ might be pending, and we're + * effectively busy waiting here. Apparently, Linux doesn't + * shut down the timer before offlining the CPU. Check this! + * + * Second, do we need to manually disable all external PLIC IRQs of the + * cell? Actually, yes. + */ + sbi_set_timer(-1); + + riscv_paging_vcpu_init(&parking_pt); + riscv_cpu_reset(0, 0, 0); +} diff --git a/hypervisor/arch/riscv/include/asm/control.h b/hypervisor/arch/riscv/include/asm/control.h index e69de29b..466311f8 100644 --- a/hypervisor/arch/riscv/include/asm/control.h +++ b/hypervisor/arch/riscv/include/asm/control.h @@ -0,0 +1,13 @@ +/* + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright (c) OTH Regensburg, 2022 + * + * Authors: + * Ralf Ramsauer <[email protected]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +void riscv_park_cpu(void); -- 2.36.1 -- You received this message because you are subscribed to the Google Groups "Jailhouse" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/jailhouse-dev/20220627132905.4338-25-ralf.ramsauer%40oth-regensburg.de.
