[PATCH v2 37/39] xen/riscv: map IMSIC interrupt file for vCPUs
Oleksii Kurochko <[email protected]>
| Newsgroups | gmane.comp.emulators.xen.devel |
|---|---|
| Message-ID | <ae5965ba43eb691b078f4a6bcf8a87a1f7a5aac5.1787838835.git.oleksii.kurochko@gmail.com> |
A guest running in VS-mode expects its own IMSIC S-file at offset 0 of its guest-physical IMSIC block. Physically, the guest-file (G-file) assigned to this vCPU lives at a hart-relative offset given by guest_file_id (assigned via the vGEIN allocator). Therefore, imsic_map_guest_file() uses stage-2 translation to redirect the guest's fixed per-vCPU GPA page (offset 0) to the specific physical guest-file page. Signed-off-by: Oleksii Kurochko <[email protected]> --- Changes in v2: - Use GUEST_IMSIC_S_BASE instead of imsic_cfg.base_addr as the base of the guest address to map to, and change the type of gaddr to paddr_t as it holds a guest physical address. - Rename guest_stride to guest_offset: it is an offset of the VS-file inside the pCPU's IMSIC block, not a stride. - Use PRIpaddr for physical addresses and %u for unsigned values in the debug/error messages. - Switch the mapping failure message from printk() to dprintk(XENLOG_ERR, ...). - Update the comment above imsic_map_guest_file(): vCPUs aren't pinned, they run on the pCPU chosen by the scheduler, and mention that on migration a VS-file is acquired on the new pCPU and mapped at the same GFN, so the stale mapping is replaced rather than explicitly torn down. --- --- xen/arch/riscv/imsic.c | 66 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/xen/arch/riscv/imsic.c b/xen/arch/riscv/imsic.c index 07152066116a..374a21ace15f 100644 --- a/xen/arch/riscv/imsic.c +++ b/xen/arch/riscv/imsic.c @@ -29,6 +29,7 @@ #include <asm/aia.h> #include <asm/aplic.h> #include <asm/imsic.h> +#include <asm/p2m.h> #define IMSIC_HART_SIZE(guest_bits) (BIT(guest_bits, U) * IMSIC_MMIO_PAGE_SZ) @@ -537,9 +538,72 @@ void cf_check imsic_ctxt_switch_to(struct vcpu *v) read_unlock_irqrestore(&imsic_state->vsfile_lock, flags); } +/* + * Map the physical IMSIC guest interrupt file (G-file) assigned to vCPU + * into the domain's stage-2 guest-physical address space. + * + * In the machine's physical address space (SPA), each hart's IMSIC + * supervisor-level file (S-file) is located at offset 0 of its address block, + * followed contiguously by GEILEN guest files at offsets of 1, 2, ..., N pages. + * + * Because a guest OS running in VS-mode expects its own supervisor-level + * interrupt file to be at offset 0 of its guest-physical IMSIC block, the + * hypervisor must use stage-2 address translation to map the vCPU's + * guest-physical "supervisor" page (GPA offset 0) to the specific + * physical guest file page (SPA offset guest_file_id) on the physical hart. + * + * A vCPU runs on the pCPU the scheduler picked for it (v->processor), and + * the guest file it is given (guest_file_id, from the vGEIN allocator) + * belongs to that very pCPU's IMSIC. A guest_file_id of 0 indicates that no + * hardware guest file is selected (matching the architectural behavior where + * vGEIN = 0 in the hstatus CSR selects no guest external interrupt source), + * requiring the VS-file to be emulated in software. + * + * Consequently the mapping installed here is only valid as long as the vCPU + * stays on that pCPU. When it migrates, a VS-file is acquired on the new + * pCPU and mapped at the very same GFN, so the stale mapping needs no + * explicit tear-down: it is simply replaced. + * + * The base guest-physical address advertised to the guest in the device + * tree matches offset 0 of the vCPU's virtual IMSIC block. Stage-2 + * translation ensures that guest supervisor accesses to this page are + * transparently routed to the real hardware VS-file granted to it on + * the pCPU it currently runs on. + */ int imsic_map_guest_file(struct vcpu *v, unsigned int vsfile_id) { - return -EOPNOTSUPP; + struct domain *d = v->domain; + unsigned int cpu = v->processor; + paddr_t gaddr = GUEST_IMSIC_S_BASE + (IMSIC_MMIO_PAGE_SZ * v->vcpu_id); + paddr_t paddr, guest_offset; + int res; + + /* Nothing to map in the case of sw interrupt file. */ + if ( !vsfile_id ) + return 0; + + guest_offset = vsfile_id * IMSIC_MMIO_PAGE_SZ; + + paddr = imsic_cfg.msi[cpu].base_addr + imsic_cfg.msi[cpu].offset + + guest_offset; + +#ifdef IMSIC_DEBUG + printk(XENLOG_DEBUG + "%s: %pv: ga(%#"PRIpaddr") -> pa(%#"PRIpaddr"), cpu(%u), " + "guest_file_id(%u) base_addr(%#"PRIpaddr") offset(%#lx)\n", + __func__, v, gaddr, paddr, cpu, vsfile_id, + imsic_cfg.msi[cpu].base_addr, imsic_cfg.msi[cpu].offset); +#endif + + res = map_regions_p2mt(d, gaddr_to_gfn(gaddr), + PFN_DOWN(IMSIC_MMIO_PAGE_SZ), maddr_to_mfn(paddr), + arch_dt_passthrough_p2m_type()); + if ( res ) + dprintk(XENLOG_ERR, + "%s: Failed to map %#"PRIpaddr" to the guest at %#"PRIpaddr"\n", + __func__, paddr, gaddr); + + return res; } int cf_check vcpu_imsic_init(struct vcpu *v) -- 2.55.0