162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2021 Western Digital Corporation or its affiliates. 462306a36Sopenharmony_ci * Copyright (C) 2022 Ventana Micro Systems Inc. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Anup Patel <apatel@ventanamicro.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/atomic.h> 1162306a36Sopenharmony_ci#include <linux/bitmap.h> 1262306a36Sopenharmony_ci#include <linux/kvm_host.h> 1362306a36Sopenharmony_ci#include <linux/math.h> 1462306a36Sopenharmony_ci#include <linux/spinlock.h> 1562306a36Sopenharmony_ci#include <linux/swab.h> 1662306a36Sopenharmony_ci#include <kvm/iodev.h> 1762306a36Sopenharmony_ci#include <asm/csr.h> 1862306a36Sopenharmony_ci#include <asm/kvm_aia_imsic.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define IMSIC_MAX_EIX (IMSIC_MAX_ID / BITS_PER_TYPE(u64)) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct imsic_mrif_eix { 2362306a36Sopenharmony_ci unsigned long eip[BITS_PER_TYPE(u64) / BITS_PER_LONG]; 2462306a36Sopenharmony_ci unsigned long eie[BITS_PER_TYPE(u64) / BITS_PER_LONG]; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct imsic_mrif { 2862306a36Sopenharmony_ci struct imsic_mrif_eix eix[IMSIC_MAX_EIX]; 2962306a36Sopenharmony_ci unsigned long eithreshold; 3062306a36Sopenharmony_ci unsigned long eidelivery; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct imsic { 3462306a36Sopenharmony_ci struct kvm_io_device iodev; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci u32 nr_msis; 3762306a36Sopenharmony_ci u32 nr_eix; 3862306a36Sopenharmony_ci u32 nr_hw_eix; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* 4162306a36Sopenharmony_ci * At any point in time, the register state is in 4262306a36Sopenharmony_ci * one of the following places: 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * 1) Hardware: IMSIC VS-file (vsfile_cpu >= 0) 4562306a36Sopenharmony_ci * 2) Software: IMSIC SW-file (vsfile_cpu < 0) 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* IMSIC VS-file */ 4962306a36Sopenharmony_ci rwlock_t vsfile_lock; 5062306a36Sopenharmony_ci int vsfile_cpu; 5162306a36Sopenharmony_ci int vsfile_hgei; 5262306a36Sopenharmony_ci void __iomem *vsfile_va; 5362306a36Sopenharmony_ci phys_addr_t vsfile_pa; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* IMSIC SW-file */ 5662306a36Sopenharmony_ci struct imsic_mrif *swfile; 5762306a36Sopenharmony_ci phys_addr_t swfile_pa; 5862306a36Sopenharmony_ci spinlock_t swfile_extirq_lock; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define imsic_vs_csr_read(__c) \ 6262306a36Sopenharmony_ci({ \ 6362306a36Sopenharmony_ci unsigned long __r; \ 6462306a36Sopenharmony_ci csr_write(CSR_VSISELECT, __c); \ 6562306a36Sopenharmony_ci __r = csr_read(CSR_VSIREG); \ 6662306a36Sopenharmony_ci __r; \ 6762306a36Sopenharmony_ci}) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define imsic_read_switchcase(__ireg) \ 7062306a36Sopenharmony_ci case __ireg: \ 7162306a36Sopenharmony_ci return imsic_vs_csr_read(__ireg); 7262306a36Sopenharmony_ci#define imsic_read_switchcase_2(__ireg) \ 7362306a36Sopenharmony_ci imsic_read_switchcase(__ireg + 0) \ 7462306a36Sopenharmony_ci imsic_read_switchcase(__ireg + 1) 7562306a36Sopenharmony_ci#define imsic_read_switchcase_4(__ireg) \ 7662306a36Sopenharmony_ci imsic_read_switchcase_2(__ireg + 0) \ 7762306a36Sopenharmony_ci imsic_read_switchcase_2(__ireg + 2) 7862306a36Sopenharmony_ci#define imsic_read_switchcase_8(__ireg) \ 7962306a36Sopenharmony_ci imsic_read_switchcase_4(__ireg + 0) \ 8062306a36Sopenharmony_ci imsic_read_switchcase_4(__ireg + 4) 8162306a36Sopenharmony_ci#define imsic_read_switchcase_16(__ireg) \ 8262306a36Sopenharmony_ci imsic_read_switchcase_8(__ireg + 0) \ 8362306a36Sopenharmony_ci imsic_read_switchcase_8(__ireg + 8) 8462306a36Sopenharmony_ci#define imsic_read_switchcase_32(__ireg) \ 8562306a36Sopenharmony_ci imsic_read_switchcase_16(__ireg + 0) \ 8662306a36Sopenharmony_ci imsic_read_switchcase_16(__ireg + 16) 8762306a36Sopenharmony_ci#define imsic_read_switchcase_64(__ireg) \ 8862306a36Sopenharmony_ci imsic_read_switchcase_32(__ireg + 0) \ 8962306a36Sopenharmony_ci imsic_read_switchcase_32(__ireg + 32) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic unsigned long imsic_eix_read(int ireg) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci switch (ireg) { 9462306a36Sopenharmony_ci imsic_read_switchcase_64(IMSIC_EIP0) 9562306a36Sopenharmony_ci imsic_read_switchcase_64(IMSIC_EIE0) 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define imsic_vs_csr_swap(__c, __v) \ 10262306a36Sopenharmony_ci({ \ 10362306a36Sopenharmony_ci unsigned long __r; \ 10462306a36Sopenharmony_ci csr_write(CSR_VSISELECT, __c); \ 10562306a36Sopenharmony_ci __r = csr_swap(CSR_VSIREG, __v); \ 10662306a36Sopenharmony_ci __r; \ 10762306a36Sopenharmony_ci}) 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define imsic_swap_switchcase(__ireg, __v) \ 11062306a36Sopenharmony_ci case __ireg: \ 11162306a36Sopenharmony_ci return imsic_vs_csr_swap(__ireg, __v); 11262306a36Sopenharmony_ci#define imsic_swap_switchcase_2(__ireg, __v) \ 11362306a36Sopenharmony_ci imsic_swap_switchcase(__ireg + 0, __v) \ 11462306a36Sopenharmony_ci imsic_swap_switchcase(__ireg + 1, __v) 11562306a36Sopenharmony_ci#define imsic_swap_switchcase_4(__ireg, __v) \ 11662306a36Sopenharmony_ci imsic_swap_switchcase_2(__ireg + 0, __v) \ 11762306a36Sopenharmony_ci imsic_swap_switchcase_2(__ireg + 2, __v) 11862306a36Sopenharmony_ci#define imsic_swap_switchcase_8(__ireg, __v) \ 11962306a36Sopenharmony_ci imsic_swap_switchcase_4(__ireg + 0, __v) \ 12062306a36Sopenharmony_ci imsic_swap_switchcase_4(__ireg + 4, __v) 12162306a36Sopenharmony_ci#define imsic_swap_switchcase_16(__ireg, __v) \ 12262306a36Sopenharmony_ci imsic_swap_switchcase_8(__ireg + 0, __v) \ 12362306a36Sopenharmony_ci imsic_swap_switchcase_8(__ireg + 8, __v) 12462306a36Sopenharmony_ci#define imsic_swap_switchcase_32(__ireg, __v) \ 12562306a36Sopenharmony_ci imsic_swap_switchcase_16(__ireg + 0, __v) \ 12662306a36Sopenharmony_ci imsic_swap_switchcase_16(__ireg + 16, __v) 12762306a36Sopenharmony_ci#define imsic_swap_switchcase_64(__ireg, __v) \ 12862306a36Sopenharmony_ci imsic_swap_switchcase_32(__ireg + 0, __v) \ 12962306a36Sopenharmony_ci imsic_swap_switchcase_32(__ireg + 32, __v) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic unsigned long imsic_eix_swap(int ireg, unsigned long val) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci switch (ireg) { 13462306a36Sopenharmony_ci imsic_swap_switchcase_64(IMSIC_EIP0, val) 13562306a36Sopenharmony_ci imsic_swap_switchcase_64(IMSIC_EIE0, val) 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#define imsic_vs_csr_write(__c, __v) \ 14262306a36Sopenharmony_cido { \ 14362306a36Sopenharmony_ci csr_write(CSR_VSISELECT, __c); \ 14462306a36Sopenharmony_ci csr_write(CSR_VSIREG, __v); \ 14562306a36Sopenharmony_ci} while (0) 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#define imsic_write_switchcase(__ireg, __v) \ 14862306a36Sopenharmony_ci case __ireg: \ 14962306a36Sopenharmony_ci imsic_vs_csr_write(__ireg, __v); \ 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci#define imsic_write_switchcase_2(__ireg, __v) \ 15262306a36Sopenharmony_ci imsic_write_switchcase(__ireg + 0, __v) \ 15362306a36Sopenharmony_ci imsic_write_switchcase(__ireg + 1, __v) 15462306a36Sopenharmony_ci#define imsic_write_switchcase_4(__ireg, __v) \ 15562306a36Sopenharmony_ci imsic_write_switchcase_2(__ireg + 0, __v) \ 15662306a36Sopenharmony_ci imsic_write_switchcase_2(__ireg + 2, __v) 15762306a36Sopenharmony_ci#define imsic_write_switchcase_8(__ireg, __v) \ 15862306a36Sopenharmony_ci imsic_write_switchcase_4(__ireg + 0, __v) \ 15962306a36Sopenharmony_ci imsic_write_switchcase_4(__ireg + 4, __v) 16062306a36Sopenharmony_ci#define imsic_write_switchcase_16(__ireg, __v) \ 16162306a36Sopenharmony_ci imsic_write_switchcase_8(__ireg + 0, __v) \ 16262306a36Sopenharmony_ci imsic_write_switchcase_8(__ireg + 8, __v) 16362306a36Sopenharmony_ci#define imsic_write_switchcase_32(__ireg, __v) \ 16462306a36Sopenharmony_ci imsic_write_switchcase_16(__ireg + 0, __v) \ 16562306a36Sopenharmony_ci imsic_write_switchcase_16(__ireg + 16, __v) 16662306a36Sopenharmony_ci#define imsic_write_switchcase_64(__ireg, __v) \ 16762306a36Sopenharmony_ci imsic_write_switchcase_32(__ireg + 0, __v) \ 16862306a36Sopenharmony_ci imsic_write_switchcase_32(__ireg + 32, __v) 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void imsic_eix_write(int ireg, unsigned long val) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci switch (ireg) { 17362306a36Sopenharmony_ci imsic_write_switchcase_64(IMSIC_EIP0, val) 17462306a36Sopenharmony_ci imsic_write_switchcase_64(IMSIC_EIE0, val) 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci#define imsic_vs_csr_set(__c, __v) \ 17962306a36Sopenharmony_cido { \ 18062306a36Sopenharmony_ci csr_write(CSR_VSISELECT, __c); \ 18162306a36Sopenharmony_ci csr_set(CSR_VSIREG, __v); \ 18262306a36Sopenharmony_ci} while (0) 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci#define imsic_set_switchcase(__ireg, __v) \ 18562306a36Sopenharmony_ci case __ireg: \ 18662306a36Sopenharmony_ci imsic_vs_csr_set(__ireg, __v); \ 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci#define imsic_set_switchcase_2(__ireg, __v) \ 18962306a36Sopenharmony_ci imsic_set_switchcase(__ireg + 0, __v) \ 19062306a36Sopenharmony_ci imsic_set_switchcase(__ireg + 1, __v) 19162306a36Sopenharmony_ci#define imsic_set_switchcase_4(__ireg, __v) \ 19262306a36Sopenharmony_ci imsic_set_switchcase_2(__ireg + 0, __v) \ 19362306a36Sopenharmony_ci imsic_set_switchcase_2(__ireg + 2, __v) 19462306a36Sopenharmony_ci#define imsic_set_switchcase_8(__ireg, __v) \ 19562306a36Sopenharmony_ci imsic_set_switchcase_4(__ireg + 0, __v) \ 19662306a36Sopenharmony_ci imsic_set_switchcase_4(__ireg + 4, __v) 19762306a36Sopenharmony_ci#define imsic_set_switchcase_16(__ireg, __v) \ 19862306a36Sopenharmony_ci imsic_set_switchcase_8(__ireg + 0, __v) \ 19962306a36Sopenharmony_ci imsic_set_switchcase_8(__ireg + 8, __v) 20062306a36Sopenharmony_ci#define imsic_set_switchcase_32(__ireg, __v) \ 20162306a36Sopenharmony_ci imsic_set_switchcase_16(__ireg + 0, __v) \ 20262306a36Sopenharmony_ci imsic_set_switchcase_16(__ireg + 16, __v) 20362306a36Sopenharmony_ci#define imsic_set_switchcase_64(__ireg, __v) \ 20462306a36Sopenharmony_ci imsic_set_switchcase_32(__ireg + 0, __v) \ 20562306a36Sopenharmony_ci imsic_set_switchcase_32(__ireg + 32, __v) 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic void imsic_eix_set(int ireg, unsigned long val) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci switch (ireg) { 21062306a36Sopenharmony_ci imsic_set_switchcase_64(IMSIC_EIP0, val) 21162306a36Sopenharmony_ci imsic_set_switchcase_64(IMSIC_EIE0, val) 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic unsigned long imsic_mrif_atomic_rmw(struct imsic_mrif *mrif, 21662306a36Sopenharmony_ci unsigned long *ptr, 21762306a36Sopenharmony_ci unsigned long new_val, 21862306a36Sopenharmony_ci unsigned long wr_mask) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci unsigned long old_val = 0, tmp = 0; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci __asm__ __volatile__ ( 22362306a36Sopenharmony_ci "0: lr.w.aq %1, %0\n" 22462306a36Sopenharmony_ci " and %2, %1, %3\n" 22562306a36Sopenharmony_ci " or %2, %2, %4\n" 22662306a36Sopenharmony_ci " sc.w.rl %2, %2, %0\n" 22762306a36Sopenharmony_ci " bnez %2, 0b" 22862306a36Sopenharmony_ci : "+A" (*ptr), "+r" (old_val), "+r" (tmp) 22962306a36Sopenharmony_ci : "r" (~wr_mask), "r" (new_val & wr_mask) 23062306a36Sopenharmony_ci : "memory"); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return old_val; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic unsigned long imsic_mrif_atomic_or(struct imsic_mrif *mrif, 23662306a36Sopenharmony_ci unsigned long *ptr, 23762306a36Sopenharmony_ci unsigned long val) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci return atomic_long_fetch_or(val, (atomic_long_t *)ptr); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci#define imsic_mrif_atomic_write(__mrif, __ptr, __new_val) \ 24362306a36Sopenharmony_ci imsic_mrif_atomic_rmw(__mrif, __ptr, __new_val, -1UL) 24462306a36Sopenharmony_ci#define imsic_mrif_atomic_read(__mrif, __ptr) \ 24562306a36Sopenharmony_ci imsic_mrif_atomic_or(__mrif, __ptr, 0) 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic u32 imsic_mrif_topei(struct imsic_mrif *mrif, u32 nr_eix, u32 nr_msis) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct imsic_mrif_eix *eix; 25062306a36Sopenharmony_ci u32 i, imin, imax, ei, max_msi; 25162306a36Sopenharmony_ci unsigned long eipend[BITS_PER_TYPE(u64) / BITS_PER_LONG]; 25262306a36Sopenharmony_ci unsigned long eithreshold = imsic_mrif_atomic_read(mrif, 25362306a36Sopenharmony_ci &mrif->eithreshold); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci max_msi = (eithreshold && (eithreshold <= nr_msis)) ? 25662306a36Sopenharmony_ci eithreshold : nr_msis; 25762306a36Sopenharmony_ci for (ei = 0; ei < nr_eix; ei++) { 25862306a36Sopenharmony_ci eix = &mrif->eix[ei]; 25962306a36Sopenharmony_ci eipend[0] = imsic_mrif_atomic_read(mrif, &eix->eie[0]) & 26062306a36Sopenharmony_ci imsic_mrif_atomic_read(mrif, &eix->eip[0]); 26162306a36Sopenharmony_ci#ifdef CONFIG_32BIT 26262306a36Sopenharmony_ci eipend[1] = imsic_mrif_atomic_read(mrif, &eix->eie[1]) & 26362306a36Sopenharmony_ci imsic_mrif_atomic_read(mrif, &eix->eip[1]); 26462306a36Sopenharmony_ci if (!eipend[0] && !eipend[1]) 26562306a36Sopenharmony_ci#else 26662306a36Sopenharmony_ci if (!eipend[0]) 26762306a36Sopenharmony_ci#endif 26862306a36Sopenharmony_ci continue; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci imin = ei * BITS_PER_TYPE(u64); 27162306a36Sopenharmony_ci imax = ((imin + BITS_PER_TYPE(u64)) < max_msi) ? 27262306a36Sopenharmony_ci imin + BITS_PER_TYPE(u64) : max_msi; 27362306a36Sopenharmony_ci for (i = (!imin) ? 1 : imin; i < imax; i++) { 27462306a36Sopenharmony_ci if (test_bit(i - imin, eipend)) 27562306a36Sopenharmony_ci return (i << TOPEI_ID_SHIFT) | i; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return 0; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int imsic_mrif_isel_check(u32 nr_eix, unsigned long isel) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci u32 num = 0; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci switch (isel) { 28762306a36Sopenharmony_ci case IMSIC_EIDELIVERY: 28862306a36Sopenharmony_ci case IMSIC_EITHRESHOLD: 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case IMSIC_EIP0 ... IMSIC_EIP63: 29162306a36Sopenharmony_ci num = isel - IMSIC_EIP0; 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case IMSIC_EIE0 ... IMSIC_EIE63: 29462306a36Sopenharmony_ci num = isel - IMSIC_EIE0; 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci default: 29762306a36Sopenharmony_ci return -ENOENT; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci#ifndef CONFIG_32BIT 30062306a36Sopenharmony_ci if (num & 0x1) 30162306a36Sopenharmony_ci return -EINVAL; 30262306a36Sopenharmony_ci#endif 30362306a36Sopenharmony_ci if ((num / 2) >= nr_eix) 30462306a36Sopenharmony_ci return -EINVAL; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int imsic_mrif_rmw(struct imsic_mrif *mrif, u32 nr_eix, 31062306a36Sopenharmony_ci unsigned long isel, unsigned long *val, 31162306a36Sopenharmony_ci unsigned long new_val, unsigned long wr_mask) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci bool pend; 31462306a36Sopenharmony_ci struct imsic_mrif_eix *eix; 31562306a36Sopenharmony_ci unsigned long *ei, num, old_val = 0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci switch (isel) { 31862306a36Sopenharmony_ci case IMSIC_EIDELIVERY: 31962306a36Sopenharmony_ci old_val = imsic_mrif_atomic_rmw(mrif, &mrif->eidelivery, 32062306a36Sopenharmony_ci new_val, wr_mask & 0x1); 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci case IMSIC_EITHRESHOLD: 32362306a36Sopenharmony_ci old_val = imsic_mrif_atomic_rmw(mrif, &mrif->eithreshold, 32462306a36Sopenharmony_ci new_val, wr_mask & (IMSIC_MAX_ID - 1)); 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci case IMSIC_EIP0 ... IMSIC_EIP63: 32762306a36Sopenharmony_ci case IMSIC_EIE0 ... IMSIC_EIE63: 32862306a36Sopenharmony_ci if (isel >= IMSIC_EIP0 && isel <= IMSIC_EIP63) { 32962306a36Sopenharmony_ci pend = true; 33062306a36Sopenharmony_ci num = isel - IMSIC_EIP0; 33162306a36Sopenharmony_ci } else { 33262306a36Sopenharmony_ci pend = false; 33362306a36Sopenharmony_ci num = isel - IMSIC_EIE0; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if ((num / 2) >= nr_eix) 33762306a36Sopenharmony_ci return -EINVAL; 33862306a36Sopenharmony_ci eix = &mrif->eix[num / 2]; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci#ifndef CONFIG_32BIT 34162306a36Sopenharmony_ci if (num & 0x1) 34262306a36Sopenharmony_ci return -EINVAL; 34362306a36Sopenharmony_ci ei = (pend) ? &eix->eip[0] : &eix->eie[0]; 34462306a36Sopenharmony_ci#else 34562306a36Sopenharmony_ci ei = (pend) ? &eix->eip[num & 0x1] : &eix->eie[num & 0x1]; 34662306a36Sopenharmony_ci#endif 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Bit0 of EIP0 or EIE0 is read-only */ 34962306a36Sopenharmony_ci if (!num) 35062306a36Sopenharmony_ci wr_mask &= ~BIT(0); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci old_val = imsic_mrif_atomic_rmw(mrif, ei, new_val, wr_mask); 35362306a36Sopenharmony_ci break; 35462306a36Sopenharmony_ci default: 35562306a36Sopenharmony_ci return -ENOENT; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (val) 35962306a36Sopenharmony_ci *val = old_val; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistruct imsic_vsfile_read_data { 36562306a36Sopenharmony_ci int hgei; 36662306a36Sopenharmony_ci u32 nr_eix; 36762306a36Sopenharmony_ci bool clear; 36862306a36Sopenharmony_ci struct imsic_mrif *mrif; 36962306a36Sopenharmony_ci}; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic void imsic_vsfile_local_read(void *data) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci u32 i; 37462306a36Sopenharmony_ci struct imsic_mrif_eix *eix; 37562306a36Sopenharmony_ci struct imsic_vsfile_read_data *idata = data; 37662306a36Sopenharmony_ci struct imsic_mrif *mrif = idata->mrif; 37762306a36Sopenharmony_ci unsigned long new_hstatus, old_hstatus, old_vsiselect; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci old_vsiselect = csr_read(CSR_VSISELECT); 38062306a36Sopenharmony_ci old_hstatus = csr_read(CSR_HSTATUS); 38162306a36Sopenharmony_ci new_hstatus = old_hstatus & ~HSTATUS_VGEIN; 38262306a36Sopenharmony_ci new_hstatus |= ((unsigned long)idata->hgei) << HSTATUS_VGEIN_SHIFT; 38362306a36Sopenharmony_ci csr_write(CSR_HSTATUS, new_hstatus); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * We don't use imsic_mrif_atomic_xyz() functions to store 38762306a36Sopenharmony_ci * values in MRIF because imsic_vsfile_read() is always called 38862306a36Sopenharmony_ci * with pointer to temporary MRIF on stack. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (idata->clear) { 39262306a36Sopenharmony_ci mrif->eidelivery = imsic_vs_csr_swap(IMSIC_EIDELIVERY, 0); 39362306a36Sopenharmony_ci mrif->eithreshold = imsic_vs_csr_swap(IMSIC_EITHRESHOLD, 0); 39462306a36Sopenharmony_ci for (i = 0; i < idata->nr_eix; i++) { 39562306a36Sopenharmony_ci eix = &mrif->eix[i]; 39662306a36Sopenharmony_ci eix->eip[0] = imsic_eix_swap(IMSIC_EIP0 + i * 2, 0); 39762306a36Sopenharmony_ci eix->eie[0] = imsic_eix_swap(IMSIC_EIE0 + i * 2, 0); 39862306a36Sopenharmony_ci#ifdef CONFIG_32BIT 39962306a36Sopenharmony_ci eix->eip[1] = imsic_eix_swap(IMSIC_EIP0 + i * 2 + 1, 0); 40062306a36Sopenharmony_ci eix->eie[1] = imsic_eix_swap(IMSIC_EIE0 + i * 2 + 1, 0); 40162306a36Sopenharmony_ci#endif 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci } else { 40462306a36Sopenharmony_ci mrif->eidelivery = imsic_vs_csr_read(IMSIC_EIDELIVERY); 40562306a36Sopenharmony_ci mrif->eithreshold = imsic_vs_csr_read(IMSIC_EITHRESHOLD); 40662306a36Sopenharmony_ci for (i = 0; i < idata->nr_eix; i++) { 40762306a36Sopenharmony_ci eix = &mrif->eix[i]; 40862306a36Sopenharmony_ci eix->eip[0] = imsic_eix_read(IMSIC_EIP0 + i * 2); 40962306a36Sopenharmony_ci eix->eie[0] = imsic_eix_read(IMSIC_EIE0 + i * 2); 41062306a36Sopenharmony_ci#ifdef CONFIG_32BIT 41162306a36Sopenharmony_ci eix->eip[1] = imsic_eix_read(IMSIC_EIP0 + i * 2 + 1); 41262306a36Sopenharmony_ci eix->eie[1] = imsic_eix_read(IMSIC_EIE0 + i * 2 + 1); 41362306a36Sopenharmony_ci#endif 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci csr_write(CSR_HSTATUS, old_hstatus); 41862306a36Sopenharmony_ci csr_write(CSR_VSISELECT, old_vsiselect); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void imsic_vsfile_read(int vsfile_hgei, int vsfile_cpu, u32 nr_eix, 42262306a36Sopenharmony_ci bool clear, struct imsic_mrif *mrif) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct imsic_vsfile_read_data idata; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* We can only read clear if we have a IMSIC VS-file */ 42762306a36Sopenharmony_ci if (vsfile_cpu < 0 || vsfile_hgei <= 0) 42862306a36Sopenharmony_ci return; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* We can only read clear on local CPU */ 43162306a36Sopenharmony_ci idata.hgei = vsfile_hgei; 43262306a36Sopenharmony_ci idata.nr_eix = nr_eix; 43362306a36Sopenharmony_ci idata.clear = clear; 43462306a36Sopenharmony_ci idata.mrif = mrif; 43562306a36Sopenharmony_ci on_each_cpu_mask(cpumask_of(vsfile_cpu), 43662306a36Sopenharmony_ci imsic_vsfile_local_read, &idata, 1); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistruct imsic_vsfile_rw_data { 44062306a36Sopenharmony_ci int hgei; 44162306a36Sopenharmony_ci int isel; 44262306a36Sopenharmony_ci bool write; 44362306a36Sopenharmony_ci unsigned long val; 44462306a36Sopenharmony_ci}; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void imsic_vsfile_local_rw(void *data) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct imsic_vsfile_rw_data *idata = data; 44962306a36Sopenharmony_ci unsigned long new_hstatus, old_hstatus, old_vsiselect; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci old_vsiselect = csr_read(CSR_VSISELECT); 45262306a36Sopenharmony_ci old_hstatus = csr_read(CSR_HSTATUS); 45362306a36Sopenharmony_ci new_hstatus = old_hstatus & ~HSTATUS_VGEIN; 45462306a36Sopenharmony_ci new_hstatus |= ((unsigned long)idata->hgei) << HSTATUS_VGEIN_SHIFT; 45562306a36Sopenharmony_ci csr_write(CSR_HSTATUS, new_hstatus); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci switch (idata->isel) { 45862306a36Sopenharmony_ci case IMSIC_EIDELIVERY: 45962306a36Sopenharmony_ci if (idata->write) 46062306a36Sopenharmony_ci imsic_vs_csr_write(IMSIC_EIDELIVERY, idata->val); 46162306a36Sopenharmony_ci else 46262306a36Sopenharmony_ci idata->val = imsic_vs_csr_read(IMSIC_EIDELIVERY); 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci case IMSIC_EITHRESHOLD: 46562306a36Sopenharmony_ci if (idata->write) 46662306a36Sopenharmony_ci imsic_vs_csr_write(IMSIC_EITHRESHOLD, idata->val); 46762306a36Sopenharmony_ci else 46862306a36Sopenharmony_ci idata->val = imsic_vs_csr_read(IMSIC_EITHRESHOLD); 46962306a36Sopenharmony_ci break; 47062306a36Sopenharmony_ci case IMSIC_EIP0 ... IMSIC_EIP63: 47162306a36Sopenharmony_ci case IMSIC_EIE0 ... IMSIC_EIE63: 47262306a36Sopenharmony_ci#ifndef CONFIG_32BIT 47362306a36Sopenharmony_ci if (idata->isel & 0x1) 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci#endif 47662306a36Sopenharmony_ci if (idata->write) 47762306a36Sopenharmony_ci imsic_eix_write(idata->isel, idata->val); 47862306a36Sopenharmony_ci else 47962306a36Sopenharmony_ci idata->val = imsic_eix_read(idata->isel); 48062306a36Sopenharmony_ci break; 48162306a36Sopenharmony_ci default: 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci csr_write(CSR_HSTATUS, old_hstatus); 48662306a36Sopenharmony_ci csr_write(CSR_VSISELECT, old_vsiselect); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int imsic_vsfile_rw(int vsfile_hgei, int vsfile_cpu, u32 nr_eix, 49062306a36Sopenharmony_ci unsigned long isel, bool write, 49162306a36Sopenharmony_ci unsigned long *val) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci int rc; 49462306a36Sopenharmony_ci struct imsic_vsfile_rw_data rdata; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* We can only access register if we have a IMSIC VS-file */ 49762306a36Sopenharmony_ci if (vsfile_cpu < 0 || vsfile_hgei <= 0) 49862306a36Sopenharmony_ci return -EINVAL; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Check IMSIC register iselect */ 50162306a36Sopenharmony_ci rc = imsic_mrif_isel_check(nr_eix, isel); 50262306a36Sopenharmony_ci if (rc) 50362306a36Sopenharmony_ci return rc; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* We can only access register on local CPU */ 50662306a36Sopenharmony_ci rdata.hgei = vsfile_hgei; 50762306a36Sopenharmony_ci rdata.isel = isel; 50862306a36Sopenharmony_ci rdata.write = write; 50962306a36Sopenharmony_ci rdata.val = (write) ? *val : 0; 51062306a36Sopenharmony_ci on_each_cpu_mask(cpumask_of(vsfile_cpu), 51162306a36Sopenharmony_ci imsic_vsfile_local_rw, &rdata, 1); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (!write) 51462306a36Sopenharmony_ci *val = rdata.val; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci return 0; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic void imsic_vsfile_local_clear(int vsfile_hgei, u32 nr_eix) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci u32 i; 52262306a36Sopenharmony_ci unsigned long new_hstatus, old_hstatus, old_vsiselect; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* We can only zero-out if we have a IMSIC VS-file */ 52562306a36Sopenharmony_ci if (vsfile_hgei <= 0) 52662306a36Sopenharmony_ci return; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci old_vsiselect = csr_read(CSR_VSISELECT); 52962306a36Sopenharmony_ci old_hstatus = csr_read(CSR_HSTATUS); 53062306a36Sopenharmony_ci new_hstatus = old_hstatus & ~HSTATUS_VGEIN; 53162306a36Sopenharmony_ci new_hstatus |= ((unsigned long)vsfile_hgei) << HSTATUS_VGEIN_SHIFT; 53262306a36Sopenharmony_ci csr_write(CSR_HSTATUS, new_hstatus); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci imsic_vs_csr_write(IMSIC_EIDELIVERY, 0); 53562306a36Sopenharmony_ci imsic_vs_csr_write(IMSIC_EITHRESHOLD, 0); 53662306a36Sopenharmony_ci for (i = 0; i < nr_eix; i++) { 53762306a36Sopenharmony_ci imsic_eix_write(IMSIC_EIP0 + i * 2, 0); 53862306a36Sopenharmony_ci imsic_eix_write(IMSIC_EIE0 + i * 2, 0); 53962306a36Sopenharmony_ci#ifdef CONFIG_32BIT 54062306a36Sopenharmony_ci imsic_eix_write(IMSIC_EIP0 + i * 2 + 1, 0); 54162306a36Sopenharmony_ci imsic_eix_write(IMSIC_EIE0 + i * 2 + 1, 0); 54262306a36Sopenharmony_ci#endif 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci csr_write(CSR_HSTATUS, old_hstatus); 54662306a36Sopenharmony_ci csr_write(CSR_VSISELECT, old_vsiselect); 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic void imsic_vsfile_local_update(int vsfile_hgei, u32 nr_eix, 55062306a36Sopenharmony_ci struct imsic_mrif *mrif) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci u32 i; 55362306a36Sopenharmony_ci struct imsic_mrif_eix *eix; 55462306a36Sopenharmony_ci unsigned long new_hstatus, old_hstatus, old_vsiselect; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* We can only update if we have a HW IMSIC context */ 55762306a36Sopenharmony_ci if (vsfile_hgei <= 0) 55862306a36Sopenharmony_ci return; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* 56162306a36Sopenharmony_ci * We don't use imsic_mrif_atomic_xyz() functions to read values 56262306a36Sopenharmony_ci * from MRIF in this function because it is always called with 56362306a36Sopenharmony_ci * pointer to temporary MRIF on stack. 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci old_vsiselect = csr_read(CSR_VSISELECT); 56762306a36Sopenharmony_ci old_hstatus = csr_read(CSR_HSTATUS); 56862306a36Sopenharmony_ci new_hstatus = old_hstatus & ~HSTATUS_VGEIN; 56962306a36Sopenharmony_ci new_hstatus |= ((unsigned long)vsfile_hgei) << HSTATUS_VGEIN_SHIFT; 57062306a36Sopenharmony_ci csr_write(CSR_HSTATUS, new_hstatus); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci for (i = 0; i < nr_eix; i++) { 57362306a36Sopenharmony_ci eix = &mrif->eix[i]; 57462306a36Sopenharmony_ci imsic_eix_set(IMSIC_EIP0 + i * 2, eix->eip[0]); 57562306a36Sopenharmony_ci imsic_eix_set(IMSIC_EIE0 + i * 2, eix->eie[0]); 57662306a36Sopenharmony_ci#ifdef CONFIG_32BIT 57762306a36Sopenharmony_ci imsic_eix_set(IMSIC_EIP0 + i * 2 + 1, eix->eip[1]); 57862306a36Sopenharmony_ci imsic_eix_set(IMSIC_EIE0 + i * 2 + 1, eix->eie[1]); 57962306a36Sopenharmony_ci#endif 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci imsic_vs_csr_write(IMSIC_EITHRESHOLD, mrif->eithreshold); 58262306a36Sopenharmony_ci imsic_vs_csr_write(IMSIC_EIDELIVERY, mrif->eidelivery); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci csr_write(CSR_HSTATUS, old_hstatus); 58562306a36Sopenharmony_ci csr_write(CSR_VSISELECT, old_vsiselect); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic void imsic_vsfile_cleanup(struct imsic *imsic) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci int old_vsfile_hgei, old_vsfile_cpu; 59162306a36Sopenharmony_ci unsigned long flags; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* 59462306a36Sopenharmony_ci * We don't use imsic_mrif_atomic_xyz() functions to clear the 59562306a36Sopenharmony_ci * SW-file in this function because it is always called when the 59662306a36Sopenharmony_ci * VCPU is being destroyed. 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci write_lock_irqsave(&imsic->vsfile_lock, flags); 60062306a36Sopenharmony_ci old_vsfile_hgei = imsic->vsfile_hgei; 60162306a36Sopenharmony_ci old_vsfile_cpu = imsic->vsfile_cpu; 60262306a36Sopenharmony_ci imsic->vsfile_cpu = imsic->vsfile_hgei = -1; 60362306a36Sopenharmony_ci imsic->vsfile_va = NULL; 60462306a36Sopenharmony_ci imsic->vsfile_pa = 0; 60562306a36Sopenharmony_ci write_unlock_irqrestore(&imsic->vsfile_lock, flags); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci memset(imsic->swfile, 0, sizeof(*imsic->swfile)); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (old_vsfile_cpu >= 0) 61062306a36Sopenharmony_ci kvm_riscv_aia_free_hgei(old_vsfile_cpu, old_vsfile_hgei); 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic void imsic_swfile_extirq_update(struct kvm_vcpu *vcpu) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct imsic *imsic = vcpu->arch.aia_context.imsic_state; 61662306a36Sopenharmony_ci struct imsic_mrif *mrif = imsic->swfile; 61762306a36Sopenharmony_ci unsigned long flags; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* 62062306a36Sopenharmony_ci * The critical section is necessary during external interrupt 62162306a36Sopenharmony_ci * updates to avoid the risk of losing interrupts due to potential 62262306a36Sopenharmony_ci * interruptions between reading topei and updating pending status. 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci spin_lock_irqsave(&imsic->swfile_extirq_lock, flags); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (imsic_mrif_atomic_read(mrif, &mrif->eidelivery) && 62862306a36Sopenharmony_ci imsic_mrif_topei(mrif, imsic->nr_eix, imsic->nr_msis)) 62962306a36Sopenharmony_ci kvm_riscv_vcpu_set_interrupt(vcpu, IRQ_VS_EXT); 63062306a36Sopenharmony_ci else 63162306a36Sopenharmony_ci kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_EXT); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci spin_unlock_irqrestore(&imsic->swfile_extirq_lock, flags); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic void imsic_swfile_read(struct kvm_vcpu *vcpu, bool clear, 63762306a36Sopenharmony_ci struct imsic_mrif *mrif) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct imsic *imsic = vcpu->arch.aia_context.imsic_state; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* 64262306a36Sopenharmony_ci * We don't use imsic_mrif_atomic_xyz() functions to read and 64362306a36Sopenharmony_ci * write SW-file and MRIF in this function because it is always 64462306a36Sopenharmony_ci * called when VCPU is not using SW-file and the MRIF points to 64562306a36Sopenharmony_ci * a temporary MRIF on stack. 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci memcpy(mrif, imsic->swfile, sizeof(*mrif)); 64962306a36Sopenharmony_ci if (clear) { 65062306a36Sopenharmony_ci memset(imsic->swfile, 0, sizeof(*imsic->swfile)); 65162306a36Sopenharmony_ci kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_EXT); 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic void imsic_swfile_update(struct kvm_vcpu *vcpu, 65662306a36Sopenharmony_ci struct imsic_mrif *mrif) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci u32 i; 65962306a36Sopenharmony_ci struct imsic_mrif_eix *seix, *eix; 66062306a36Sopenharmony_ci struct imsic *imsic = vcpu->arch.aia_context.imsic_state; 66162306a36Sopenharmony_ci struct imsic_mrif *smrif = imsic->swfile; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci imsic_mrif_atomic_write(smrif, &smrif->eidelivery, mrif->eidelivery); 66462306a36Sopenharmony_ci imsic_mrif_atomic_write(smrif, &smrif->eithreshold, mrif->eithreshold); 66562306a36Sopenharmony_ci for (i = 0; i < imsic->nr_eix; i++) { 66662306a36Sopenharmony_ci seix = &smrif->eix[i]; 66762306a36Sopenharmony_ci eix = &mrif->eix[i]; 66862306a36Sopenharmony_ci imsic_mrif_atomic_or(smrif, &seix->eip[0], eix->eip[0]); 66962306a36Sopenharmony_ci imsic_mrif_atomic_or(smrif, &seix->eie[0], eix->eie[0]); 67062306a36Sopenharmony_ci#ifdef CONFIG_32BIT 67162306a36Sopenharmony_ci imsic_mrif_atomic_or(smrif, &seix->eip[1], eix->eip[1]); 67262306a36Sopenharmony_ci imsic_mrif_atomic_or(smrif, &seix->eie[1], eix->eie[1]); 67362306a36Sopenharmony_ci#endif 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci imsic_swfile_extirq_update(vcpu); 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_imsic_release(struct kvm_vcpu *vcpu) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci unsigned long flags; 68262306a36Sopenharmony_ci struct imsic_mrif tmrif; 68362306a36Sopenharmony_ci int old_vsfile_hgei, old_vsfile_cpu; 68462306a36Sopenharmony_ci struct imsic *imsic = vcpu->arch.aia_context.imsic_state; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Read and clear IMSIC VS-file details */ 68762306a36Sopenharmony_ci write_lock_irqsave(&imsic->vsfile_lock, flags); 68862306a36Sopenharmony_ci old_vsfile_hgei = imsic->vsfile_hgei; 68962306a36Sopenharmony_ci old_vsfile_cpu = imsic->vsfile_cpu; 69062306a36Sopenharmony_ci imsic->vsfile_cpu = imsic->vsfile_hgei = -1; 69162306a36Sopenharmony_ci imsic->vsfile_va = NULL; 69262306a36Sopenharmony_ci imsic->vsfile_pa = 0; 69362306a36Sopenharmony_ci write_unlock_irqrestore(&imsic->vsfile_lock, flags); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* Do nothing, if no IMSIC VS-file to release */ 69662306a36Sopenharmony_ci if (old_vsfile_cpu < 0) 69762306a36Sopenharmony_ci return; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* 70062306a36Sopenharmony_ci * At this point, all interrupt producers are still using 70162306a36Sopenharmony_ci * the old IMSIC VS-file so we first re-direct all interrupt 70262306a36Sopenharmony_ci * producers. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* Purge the G-stage mapping */ 70662306a36Sopenharmony_ci kvm_riscv_gstage_iounmap(vcpu->kvm, 70762306a36Sopenharmony_ci vcpu->arch.aia_context.imsic_addr, 70862306a36Sopenharmony_ci IMSIC_MMIO_PAGE_SZ); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* TODO: Purge the IOMMU mapping ??? */ 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* 71362306a36Sopenharmony_ci * At this point, all interrupt producers have been re-directed 71462306a36Sopenharmony_ci * to somewhere else so we move register state from the old IMSIC 71562306a36Sopenharmony_ci * VS-file to the IMSIC SW-file. 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* Read and clear register state from old IMSIC VS-file */ 71962306a36Sopenharmony_ci memset(&tmrif, 0, sizeof(tmrif)); 72062306a36Sopenharmony_ci imsic_vsfile_read(old_vsfile_hgei, old_vsfile_cpu, imsic->nr_hw_eix, 72162306a36Sopenharmony_ci true, &tmrif); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* Update register state in IMSIC SW-file */ 72462306a36Sopenharmony_ci imsic_swfile_update(vcpu, &tmrif); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* Free-up old IMSIC VS-file */ 72762306a36Sopenharmony_ci kvm_riscv_aia_free_hgei(old_vsfile_cpu, old_vsfile_hgei); 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_imsic_update(struct kvm_vcpu *vcpu) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci unsigned long flags; 73362306a36Sopenharmony_ci phys_addr_t new_vsfile_pa; 73462306a36Sopenharmony_ci struct imsic_mrif tmrif; 73562306a36Sopenharmony_ci void __iomem *new_vsfile_va; 73662306a36Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 73762306a36Sopenharmony_ci struct kvm_run *run = vcpu->run; 73862306a36Sopenharmony_ci struct kvm_vcpu_aia *vaia = &vcpu->arch.aia_context; 73962306a36Sopenharmony_ci struct imsic *imsic = vaia->imsic_state; 74062306a36Sopenharmony_ci int ret = 0, new_vsfile_hgei = -1, old_vsfile_hgei, old_vsfile_cpu; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* Do nothing for emulation mode */ 74362306a36Sopenharmony_ci if (kvm->arch.aia.mode == KVM_DEV_RISCV_AIA_MODE_EMUL) 74462306a36Sopenharmony_ci return 1; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Read old IMSIC VS-file details */ 74762306a36Sopenharmony_ci read_lock_irqsave(&imsic->vsfile_lock, flags); 74862306a36Sopenharmony_ci old_vsfile_hgei = imsic->vsfile_hgei; 74962306a36Sopenharmony_ci old_vsfile_cpu = imsic->vsfile_cpu; 75062306a36Sopenharmony_ci read_unlock_irqrestore(&imsic->vsfile_lock, flags); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* Do nothing if we are continuing on same CPU */ 75362306a36Sopenharmony_ci if (old_vsfile_cpu == vcpu->cpu) 75462306a36Sopenharmony_ci return 1; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* Allocate new IMSIC VS-file */ 75762306a36Sopenharmony_ci ret = kvm_riscv_aia_alloc_hgei(vcpu->cpu, vcpu, 75862306a36Sopenharmony_ci &new_vsfile_va, &new_vsfile_pa); 75962306a36Sopenharmony_ci if (ret <= 0) { 76062306a36Sopenharmony_ci /* For HW acceleration mode, we can't continue */ 76162306a36Sopenharmony_ci if (kvm->arch.aia.mode == KVM_DEV_RISCV_AIA_MODE_HWACCEL) { 76262306a36Sopenharmony_ci run->fail_entry.hardware_entry_failure_reason = 76362306a36Sopenharmony_ci CSR_HSTATUS; 76462306a36Sopenharmony_ci run->fail_entry.cpu = vcpu->cpu; 76562306a36Sopenharmony_ci run->exit_reason = KVM_EXIT_FAIL_ENTRY; 76662306a36Sopenharmony_ci return 0; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Release old IMSIC VS-file */ 77062306a36Sopenharmony_ci if (old_vsfile_cpu >= 0) 77162306a36Sopenharmony_ci kvm_riscv_vcpu_aia_imsic_release(vcpu); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* For automatic mode, we continue */ 77462306a36Sopenharmony_ci goto done; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci new_vsfile_hgei = ret; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* 77962306a36Sopenharmony_ci * At this point, all interrupt producers are still using 78062306a36Sopenharmony_ci * to the old IMSIC VS-file so we first move all interrupt 78162306a36Sopenharmony_ci * producers to the new IMSIC VS-file. 78262306a36Sopenharmony_ci */ 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* Zero-out new IMSIC VS-file */ 78562306a36Sopenharmony_ci imsic_vsfile_local_clear(new_vsfile_hgei, imsic->nr_hw_eix); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* Update G-stage mapping for the new IMSIC VS-file */ 78862306a36Sopenharmony_ci ret = kvm_riscv_gstage_ioremap(kvm, vcpu->arch.aia_context.imsic_addr, 78962306a36Sopenharmony_ci new_vsfile_pa, IMSIC_MMIO_PAGE_SZ, 79062306a36Sopenharmony_ci true, true); 79162306a36Sopenharmony_ci if (ret) 79262306a36Sopenharmony_ci goto fail_free_vsfile_hgei; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* TODO: Update the IOMMU mapping ??? */ 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Update new IMSIC VS-file details in IMSIC context */ 79762306a36Sopenharmony_ci write_lock_irqsave(&imsic->vsfile_lock, flags); 79862306a36Sopenharmony_ci imsic->vsfile_hgei = new_vsfile_hgei; 79962306a36Sopenharmony_ci imsic->vsfile_cpu = vcpu->cpu; 80062306a36Sopenharmony_ci imsic->vsfile_va = new_vsfile_va; 80162306a36Sopenharmony_ci imsic->vsfile_pa = new_vsfile_pa; 80262306a36Sopenharmony_ci write_unlock_irqrestore(&imsic->vsfile_lock, flags); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* 80562306a36Sopenharmony_ci * At this point, all interrupt producers have been moved 80662306a36Sopenharmony_ci * to the new IMSIC VS-file so we move register state from 80762306a36Sopenharmony_ci * the old IMSIC VS/SW-file to the new IMSIC VS-file. 80862306a36Sopenharmony_ci */ 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci memset(&tmrif, 0, sizeof(tmrif)); 81162306a36Sopenharmony_ci if (old_vsfile_cpu >= 0) { 81262306a36Sopenharmony_ci /* Read and clear register state from old IMSIC VS-file */ 81362306a36Sopenharmony_ci imsic_vsfile_read(old_vsfile_hgei, old_vsfile_cpu, 81462306a36Sopenharmony_ci imsic->nr_hw_eix, true, &tmrif); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* Free-up old IMSIC VS-file */ 81762306a36Sopenharmony_ci kvm_riscv_aia_free_hgei(old_vsfile_cpu, old_vsfile_hgei); 81862306a36Sopenharmony_ci } else { 81962306a36Sopenharmony_ci /* Read and clear register state from IMSIC SW-file */ 82062306a36Sopenharmony_ci imsic_swfile_read(vcpu, true, &tmrif); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci /* Restore register state in the new IMSIC VS-file */ 82462306a36Sopenharmony_ci imsic_vsfile_local_update(new_vsfile_hgei, imsic->nr_hw_eix, &tmrif); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cidone: 82762306a36Sopenharmony_ci /* Set VCPU HSTATUS.VGEIN to new IMSIC VS-file */ 82862306a36Sopenharmony_ci vcpu->arch.guest_context.hstatus &= ~HSTATUS_VGEIN; 82962306a36Sopenharmony_ci if (new_vsfile_hgei > 0) 83062306a36Sopenharmony_ci vcpu->arch.guest_context.hstatus |= 83162306a36Sopenharmony_ci ((unsigned long)new_vsfile_hgei) << HSTATUS_VGEIN_SHIFT; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* Continue run-loop */ 83462306a36Sopenharmony_ci return 1; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cifail_free_vsfile_hgei: 83762306a36Sopenharmony_ci kvm_riscv_aia_free_hgei(vcpu->cpu, new_vsfile_hgei); 83862306a36Sopenharmony_ci return ret; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_imsic_rmw(struct kvm_vcpu *vcpu, unsigned long isel, 84262306a36Sopenharmony_ci unsigned long *val, unsigned long new_val, 84362306a36Sopenharmony_ci unsigned long wr_mask) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci u32 topei; 84662306a36Sopenharmony_ci struct imsic_mrif_eix *eix; 84762306a36Sopenharmony_ci int r, rc = KVM_INSN_CONTINUE_NEXT_SEPC; 84862306a36Sopenharmony_ci struct imsic *imsic = vcpu->arch.aia_context.imsic_state; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (isel == KVM_RISCV_AIA_IMSIC_TOPEI) { 85162306a36Sopenharmony_ci /* Read pending and enabled interrupt with highest priority */ 85262306a36Sopenharmony_ci topei = imsic_mrif_topei(imsic->swfile, imsic->nr_eix, 85362306a36Sopenharmony_ci imsic->nr_msis); 85462306a36Sopenharmony_ci if (val) 85562306a36Sopenharmony_ci *val = topei; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* Writes ignore value and clear top pending interrupt */ 85862306a36Sopenharmony_ci if (topei && wr_mask) { 85962306a36Sopenharmony_ci topei >>= TOPEI_ID_SHIFT; 86062306a36Sopenharmony_ci if (topei) { 86162306a36Sopenharmony_ci eix = &imsic->swfile->eix[topei / 86262306a36Sopenharmony_ci BITS_PER_TYPE(u64)]; 86362306a36Sopenharmony_ci clear_bit(topei & (BITS_PER_TYPE(u64) - 1), 86462306a36Sopenharmony_ci eix->eip); 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci } else { 86862306a36Sopenharmony_ci r = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix, isel, 86962306a36Sopenharmony_ci val, new_val, wr_mask); 87062306a36Sopenharmony_ci /* Forward unknown IMSIC register to user-space */ 87162306a36Sopenharmony_ci if (r) 87262306a36Sopenharmony_ci rc = (r == -ENOENT) ? 0 : KVM_INSN_ILLEGAL_TRAP; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if (wr_mask) 87662306a36Sopenharmony_ci imsic_swfile_extirq_update(vcpu); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci return rc; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ciint kvm_riscv_aia_imsic_rw_attr(struct kvm *kvm, unsigned long type, 88262306a36Sopenharmony_ci bool write, unsigned long *val) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci u32 isel, vcpu_id; 88562306a36Sopenharmony_ci unsigned long flags; 88662306a36Sopenharmony_ci struct imsic *imsic; 88762306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 88862306a36Sopenharmony_ci int rc, vsfile_hgei, vsfile_cpu; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci if (!kvm_riscv_aia_initialized(kvm)) 89162306a36Sopenharmony_ci return -ENODEV; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci vcpu_id = KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(type); 89462306a36Sopenharmony_ci vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id); 89562306a36Sopenharmony_ci if (!vcpu) 89662306a36Sopenharmony_ci return -ENODEV; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type); 89962306a36Sopenharmony_ci imsic = vcpu->arch.aia_context.imsic_state; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci read_lock_irqsave(&imsic->vsfile_lock, flags); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci rc = 0; 90462306a36Sopenharmony_ci vsfile_hgei = imsic->vsfile_hgei; 90562306a36Sopenharmony_ci vsfile_cpu = imsic->vsfile_cpu; 90662306a36Sopenharmony_ci if (vsfile_cpu < 0) { 90762306a36Sopenharmony_ci if (write) { 90862306a36Sopenharmony_ci rc = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix, 90962306a36Sopenharmony_ci isel, NULL, *val, -1UL); 91062306a36Sopenharmony_ci imsic_swfile_extirq_update(vcpu); 91162306a36Sopenharmony_ci } else 91262306a36Sopenharmony_ci rc = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix, 91362306a36Sopenharmony_ci isel, val, 0, 0); 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci read_unlock_irqrestore(&imsic->vsfile_lock, flags); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (!rc && vsfile_cpu >= 0) 91962306a36Sopenharmony_ci rc = imsic_vsfile_rw(vsfile_hgei, vsfile_cpu, imsic->nr_eix, 92062306a36Sopenharmony_ci isel, write, val); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci return rc; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ciint kvm_riscv_aia_imsic_has_attr(struct kvm *kvm, unsigned long type) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci u32 isel, vcpu_id; 92862306a36Sopenharmony_ci struct imsic *imsic; 92962306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (!kvm_riscv_aia_initialized(kvm)) 93262306a36Sopenharmony_ci return -ENODEV; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci vcpu_id = KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(type); 93562306a36Sopenharmony_ci vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id); 93662306a36Sopenharmony_ci if (!vcpu) 93762306a36Sopenharmony_ci return -ENODEV; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type); 94062306a36Sopenharmony_ci imsic = vcpu->arch.aia_context.imsic_state; 94162306a36Sopenharmony_ci return imsic_mrif_isel_check(imsic->nr_eix, isel); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_imsic_reset(struct kvm_vcpu *vcpu) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci struct imsic *imsic = vcpu->arch.aia_context.imsic_state; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (!imsic) 94962306a36Sopenharmony_ci return; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci kvm_riscv_vcpu_aia_imsic_release(vcpu); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci memset(imsic->swfile, 0, sizeof(*imsic->swfile)); 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_imsic_inject(struct kvm_vcpu *vcpu, 95762306a36Sopenharmony_ci u32 guest_index, u32 offset, u32 iid) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci unsigned long flags; 96062306a36Sopenharmony_ci struct imsic_mrif_eix *eix; 96162306a36Sopenharmony_ci struct imsic *imsic = vcpu->arch.aia_context.imsic_state; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* We only emulate one IMSIC MMIO page for each Guest VCPU */ 96462306a36Sopenharmony_ci if (!imsic || !iid || guest_index || 96562306a36Sopenharmony_ci (offset != IMSIC_MMIO_SETIPNUM_LE && 96662306a36Sopenharmony_ci offset != IMSIC_MMIO_SETIPNUM_BE)) 96762306a36Sopenharmony_ci return -ENODEV; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci iid = (offset == IMSIC_MMIO_SETIPNUM_BE) ? __swab32(iid) : iid; 97062306a36Sopenharmony_ci if (imsic->nr_msis <= iid) 97162306a36Sopenharmony_ci return -EINVAL; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci read_lock_irqsave(&imsic->vsfile_lock, flags); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (imsic->vsfile_cpu >= 0) { 97662306a36Sopenharmony_ci writel(iid, imsic->vsfile_va + IMSIC_MMIO_SETIPNUM_LE); 97762306a36Sopenharmony_ci kvm_vcpu_kick(vcpu); 97862306a36Sopenharmony_ci } else { 97962306a36Sopenharmony_ci eix = &imsic->swfile->eix[iid / BITS_PER_TYPE(u64)]; 98062306a36Sopenharmony_ci set_bit(iid & (BITS_PER_TYPE(u64) - 1), eix->eip); 98162306a36Sopenharmony_ci imsic_swfile_extirq_update(vcpu); 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci read_unlock_irqrestore(&imsic->vsfile_lock, flags); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci return 0; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_cistatic int imsic_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, 99062306a36Sopenharmony_ci gpa_t addr, int len, void *val) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci if (len != 4 || (addr & 0x3) != 0) 99362306a36Sopenharmony_ci return -EOPNOTSUPP; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci *((u32 *)val) = 0; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci return 0; 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_cistatic int imsic_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, 100162306a36Sopenharmony_ci gpa_t addr, int len, const void *val) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci struct kvm_msi msi = { 0 }; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci if (len != 4 || (addr & 0x3) != 0) 100662306a36Sopenharmony_ci return -EOPNOTSUPP; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci msi.address_hi = addr >> 32; 100962306a36Sopenharmony_ci msi.address_lo = (u32)addr; 101062306a36Sopenharmony_ci msi.data = *((const u32 *)val); 101162306a36Sopenharmony_ci kvm_riscv_aia_inject_msi(vcpu->kvm, &msi); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci return 0; 101462306a36Sopenharmony_ci}; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic struct kvm_io_device_ops imsic_iodoev_ops = { 101762306a36Sopenharmony_ci .read = imsic_mmio_read, 101862306a36Sopenharmony_ci .write = imsic_mmio_write, 101962306a36Sopenharmony_ci}; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_imsic_init(struct kvm_vcpu *vcpu) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci int ret = 0; 102462306a36Sopenharmony_ci struct imsic *imsic; 102562306a36Sopenharmony_ci struct page *swfile_page; 102662306a36Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci /* Fail if we have zero IDs */ 102962306a36Sopenharmony_ci if (!kvm->arch.aia.nr_ids) 103062306a36Sopenharmony_ci return -EINVAL; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci /* Allocate IMSIC context */ 103362306a36Sopenharmony_ci imsic = kzalloc(sizeof(*imsic), GFP_KERNEL); 103462306a36Sopenharmony_ci if (!imsic) 103562306a36Sopenharmony_ci return -ENOMEM; 103662306a36Sopenharmony_ci vcpu->arch.aia_context.imsic_state = imsic; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci /* Setup IMSIC context */ 103962306a36Sopenharmony_ci imsic->nr_msis = kvm->arch.aia.nr_ids + 1; 104062306a36Sopenharmony_ci rwlock_init(&imsic->vsfile_lock); 104162306a36Sopenharmony_ci imsic->nr_eix = BITS_TO_U64(imsic->nr_msis); 104262306a36Sopenharmony_ci imsic->nr_hw_eix = BITS_TO_U64(kvm_riscv_aia_max_ids); 104362306a36Sopenharmony_ci imsic->vsfile_hgei = imsic->vsfile_cpu = -1; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci /* Setup IMSIC SW-file */ 104662306a36Sopenharmony_ci swfile_page = alloc_pages(GFP_KERNEL | __GFP_ZERO, 104762306a36Sopenharmony_ci get_order(sizeof(*imsic->swfile))); 104862306a36Sopenharmony_ci if (!swfile_page) { 104962306a36Sopenharmony_ci ret = -ENOMEM; 105062306a36Sopenharmony_ci goto fail_free_imsic; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci imsic->swfile = page_to_virt(swfile_page); 105362306a36Sopenharmony_ci imsic->swfile_pa = page_to_phys(swfile_page); 105462306a36Sopenharmony_ci spin_lock_init(&imsic->swfile_extirq_lock); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci /* Setup IO device */ 105762306a36Sopenharmony_ci kvm_iodevice_init(&imsic->iodev, &imsic_iodoev_ops); 105862306a36Sopenharmony_ci mutex_lock(&kvm->slots_lock); 105962306a36Sopenharmony_ci ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, 106062306a36Sopenharmony_ci vcpu->arch.aia_context.imsic_addr, 106162306a36Sopenharmony_ci KVM_DEV_RISCV_IMSIC_SIZE, 106262306a36Sopenharmony_ci &imsic->iodev); 106362306a36Sopenharmony_ci mutex_unlock(&kvm->slots_lock); 106462306a36Sopenharmony_ci if (ret) 106562306a36Sopenharmony_ci goto fail_free_swfile; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci return 0; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cifail_free_swfile: 107062306a36Sopenharmony_ci free_pages((unsigned long)imsic->swfile, 107162306a36Sopenharmony_ci get_order(sizeof(*imsic->swfile))); 107262306a36Sopenharmony_cifail_free_imsic: 107362306a36Sopenharmony_ci vcpu->arch.aia_context.imsic_state = NULL; 107462306a36Sopenharmony_ci kfree(imsic); 107562306a36Sopenharmony_ci return ret; 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_imsic_cleanup(struct kvm_vcpu *vcpu) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 108162306a36Sopenharmony_ci struct imsic *imsic = vcpu->arch.aia_context.imsic_state; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (!imsic) 108462306a36Sopenharmony_ci return; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci imsic_vsfile_cleanup(imsic); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci mutex_lock(&kvm->slots_lock); 108962306a36Sopenharmony_ci kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &imsic->iodev); 109062306a36Sopenharmony_ci mutex_unlock(&kvm->slots_lock); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci free_pages((unsigned long)imsic->swfile, 109362306a36Sopenharmony_ci get_order(sizeof(*imsic->swfile))); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci vcpu->arch.aia_context.imsic_state = NULL; 109662306a36Sopenharmony_ci kfree(imsic); 109762306a36Sopenharmony_ci} 1098