| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2021 Western Digital Corporation or its affiliates.
- * Copyright (C) 2022 Ventana Micro Systems Inc.
- *
- * Authors:
- * Anup Patel <apatel@ventanamicro.com>
- */
- #include <linux/atomic.h>
- #include <linux/bitmap.h>
- #include <linux/irqchip/riscv-imsic.h>
- #include <linux/kvm_host.h>
- #include <linux/math.h>
- #include <linux/spinlock.h>
- #include <linux/swab.h>
- #include <kvm/iodev.h>
- #include <asm/csr.h>
- #define IMSIC_MAX_EIX (IMSIC_MAX_ID / BITS_PER_TYPE(u64))
- struct imsic_mrif_eix {
- unsigned long eip[BITS_PER_TYPE(u64) / BITS_PER_LONG];
- unsigned long eie[BITS_PER_TYPE(u64) / BITS_PER_LONG];
- };
- struct imsic_mrif {
- struct imsic_mrif_eix eix[IMSIC_MAX_EIX];
- unsigned long eithreshold;
- unsigned long eidelivery;
- };
- struct imsic {
- struct kvm_io_device iodev;
- u32 nr_msis;
- u32 nr_eix;
- u32 nr_hw_eix;
- /*
- * At any point in time, the register state is in
- * one of the following places:
- *
- * 1) Hardware: IMSIC VS-file (vsfile_cpu >= 0)
- * 2) Software: IMSIC SW-file (vsfile_cpu < 0)
- */
- /* IMSIC VS-file */
- rwlock_t vsfile_lock;
- int vsfile_cpu;
- int vsfile_hgei;
- void __iomem *vsfile_va;
- phys_addr_t vsfile_pa;
- /* IMSIC SW-file */
- struct imsic_mrif *swfile;
- phys_addr_t swfile_pa;
- raw_spinlock_t swfile_extirq_lock;
- };
- #define imsic_vs_csr_read(__c) \
- ({ \
- unsigned long __r; \
- csr_write(CSR_VSISELECT, __c); \
- __r = csr_read(CSR_VSIREG); \
- __r; \
- })
- #define imsic_read_switchcase(__ireg) \
- case __ireg: \
- return imsic_vs_csr_read(__ireg);
- #define imsic_read_switchcase_2(__ireg) \
- imsic_read_switchcase(__ireg + 0) \
- imsic_read_switchcase(__ireg + 1)
- #define imsic_read_switchcase_4(__ireg) \
- imsic_read_switchcase_2(__ireg + 0) \
- imsic_read_switchcase_2(__ireg + 2)
- #define imsic_read_switchcase_8(__ireg) \
- imsic_read_switchcase_4(__ireg + 0) \
- imsic_read_switchcase_4(__ireg + 4)
- #define imsic_read_switchcase_16(__ireg) \
- imsic_read_switchcase_8(__ireg + 0) \
- imsic_read_switchcase_8(__ireg + 8)
- #define imsic_read_switchcase_32(__ireg) \
- imsic_read_switchcase_16(__ireg + 0) \
- imsic_read_switchcase_16(__ireg + 16)
- #define imsic_read_switchcase_64(__ireg) \
- imsic_read_switchcase_32(__ireg + 0) \
- imsic_read_switchcase_32(__ireg + 32)
- static unsigned long imsic_eix_read(int ireg)
- {
- switch (ireg) {
- imsic_read_switchcase_64(IMSIC_EIP0)
- imsic_read_switchcase_64(IMSIC_EIE0)
- }
- return 0;
- }
- #define imsic_vs_csr_swap(__c, __v) \
- ({ \
- unsigned long __r; \
- csr_write(CSR_VSISELECT, __c); \
- __r = csr_swap(CSR_VSIREG, __v); \
- __r; \
- })
- #define imsic_swap_switchcase(__ireg, __v) \
- case __ireg: \
- return imsic_vs_csr_swap(__ireg, __v);
- #define imsic_swap_switchcase_2(__ireg, __v) \
- imsic_swap_switchcase(__ireg + 0, __v) \
- imsic_swap_switchcase(__ireg + 1, __v)
- #define imsic_swap_switchcase_4(__ireg, __v) \
- imsic_swap_switchcase_2(__ireg + 0, __v) \
- imsic_swap_switchcase_2(__ireg + 2, __v)
- #define imsic_swap_switchcase_8(__ireg, __v) \
- imsic_swap_switchcase_4(__ireg + 0, __v) \
- imsic_swap_switchcase_4(__ireg + 4, __v)
- #define imsic_swap_switchcase_16(__ireg, __v) \
- imsic_swap_switchcase_8(__ireg + 0, __v) \
- imsic_swap_switchcase_8(__ireg + 8, __v)
- #define imsic_swap_switchcase_32(__ireg, __v) \
- imsic_swap_switchcase_16(__ireg + 0, __v) \
- imsic_swap_switchcase_16(__ireg + 16, __v)
- #define imsic_swap_switchcase_64(__ireg, __v) \
- imsic_swap_switchcase_32(__ireg + 0, __v) \
- imsic_swap_switchcase_32(__ireg + 32, __v)
- static unsigned long imsic_eix_swap(int ireg, unsigned long val)
- {
- switch (ireg) {
- imsic_swap_switchcase_64(IMSIC_EIP0, val)
- imsic_swap_switchcase_64(IMSIC_EIE0, val)
- }
- return 0;
- }
- #define imsic_vs_csr_write(__c, __v) \
- do { \
- csr_write(CSR_VSISELECT, __c); \
- csr_write(CSR_VSIREG, __v); \
- } while (0)
- #define imsic_write_switchcase(__ireg, __v) \
- case __ireg: \
- imsic_vs_csr_write(__ireg, __v); \
- break;
- #define imsic_write_switchcase_2(__ireg, __v) \
- imsic_write_switchcase(__ireg + 0, __v) \
- imsic_write_switchcase(__ireg + 1, __v)
- #define imsic_write_switchcase_4(__ireg, __v) \
- imsic_write_switchcase_2(__ireg + 0, __v) \
- imsic_write_switchcase_2(__ireg + 2, __v)
- #define imsic_write_switchcase_8(__ireg, __v) \
- imsic_write_switchcase_4(__ireg + 0, __v) \
- imsic_write_switchcase_4(__ireg + 4, __v)
- #define imsic_write_switchcase_16(__ireg, __v) \
- imsic_write_switchcase_8(__ireg + 0, __v) \
- imsic_write_switchcase_8(__ireg + 8, __v)
- #define imsic_write_switchcase_32(__ireg, __v) \
- imsic_write_switchcase_16(__ireg + 0, __v) \
- imsic_write_switchcase_16(__ireg + 16, __v)
- #define imsic_write_switchcase_64(__ireg, __v) \
- imsic_write_switchcase_32(__ireg + 0, __v) \
- imsic_write_switchcase_32(__ireg + 32, __v)
- static void imsic_eix_write(int ireg, unsigned long val)
- {
- switch (ireg) {
- imsic_write_switchcase_64(IMSIC_EIP0, val)
- imsic_write_switchcase_64(IMSIC_EIE0, val)
- }
- }
- #define imsic_vs_csr_set(__c, __v) \
- do { \
- csr_write(CSR_VSISELECT, __c); \
- csr_set(CSR_VSIREG, __v); \
- } while (0)
- #define imsic_set_switchcase(__ireg, __v) \
- case __ireg: \
- imsic_vs_csr_set(__ireg, __v); \
- break;
- #define imsic_set_switchcase_2(__ireg, __v) \
- imsic_set_switchcase(__ireg + 0, __v) \
- imsic_set_switchcase(__ireg + 1, __v)
- #define imsic_set_switchcase_4(__ireg, __v) \
- imsic_set_switchcase_2(__ireg + 0, __v) \
- imsic_set_switchcase_2(__ireg + 2, __v)
- #define imsic_set_switchcase_8(__ireg, __v) \
- imsic_set_switchcase_4(__ireg + 0, __v) \
- imsic_set_switchcase_4(__ireg + 4, __v)
- #define imsic_set_switchcase_16(__ireg, __v) \
- imsic_set_switchcase_8(__ireg + 0, __v) \
- imsic_set_switchcase_8(__ireg + 8, __v)
- #define imsic_set_switchcase_32(__ireg, __v) \
- imsic_set_switchcase_16(__ireg + 0, __v) \
- imsic_set_switchcase_16(__ireg + 16, __v)
- #define imsic_set_switchcase_64(__ireg, __v) \
- imsic_set_switchcase_32(__ireg + 0, __v) \
- imsic_set_switchcase_32(__ireg + 32, __v)
- static void imsic_eix_set(int ireg, unsigned long val)
- {
- switch (ireg) {
- imsic_set_switchcase_64(IMSIC_EIP0, val)
- imsic_set_switchcase_64(IMSIC_EIE0, val)
- }
- }
- static unsigned long imsic_mrif_atomic_rmw(struct imsic_mrif *mrif,
- unsigned long *ptr,
- unsigned long new_val,
- unsigned long wr_mask)
- {
- unsigned long old_val = 0, tmp = 0;
- __asm__ __volatile__ (
- "0: lr.w.aq %1, %0\n"
- " and %2, %1, %3\n"
- " or %2, %2, %4\n"
- " sc.w.rl %2, %2, %0\n"
- " bnez %2, 0b"
- : "+A" (*ptr), "+r" (old_val), "+r" (tmp)
- : "r" (~wr_mask), "r" (new_val & wr_mask)
- : "memory");
- return old_val;
- }
- static unsigned long imsic_mrif_atomic_or(struct imsic_mrif *mrif,
- unsigned long *ptr,
- unsigned long val)
- {
- return atomic_long_fetch_or(val, (atomic_long_t *)ptr);
- }
- #define imsic_mrif_atomic_write(__mrif, __ptr, __new_val) \
- imsic_mrif_atomic_rmw(__mrif, __ptr, __new_val, -1UL)
- #define imsic_mrif_atomic_read(__mrif, __ptr) \
- imsic_mrif_atomic_or(__mrif, __ptr, 0)
- static u32 imsic_mrif_topei(struct imsic_mrif *mrif, u32 nr_eix, u32 nr_msis)
- {
- struct imsic_mrif_eix *eix;
- u32 i, imin, imax, ei, max_msi;
- unsigned long eipend[BITS_PER_TYPE(u64) / BITS_PER_LONG];
- unsigned long eithreshold = imsic_mrif_atomic_read(mrif,
- &mrif->eithreshold);
- max_msi = (eithreshold && (eithreshold <= nr_msis)) ?
- eithreshold : nr_msis;
- for (ei = 0; ei < nr_eix; ei++) {
- eix = &mrif->eix[ei];
- eipend[0] = imsic_mrif_atomic_read(mrif, &eix->eie[0]) &
- imsic_mrif_atomic_read(mrif, &eix->eip[0]);
- #ifdef CONFIG_32BIT
- eipend[1] = imsic_mrif_atomic_read(mrif, &eix->eie[1]) &
- imsic_mrif_atomic_read(mrif, &eix->eip[1]);
- if (!eipend[0] && !eipend[1])
- #else
- if (!eipend[0])
- #endif
- continue;
- imin = ei * BITS_PER_TYPE(u64);
- imax = ((imin + BITS_PER_TYPE(u64)) < max_msi) ?
- imin + BITS_PER_TYPE(u64) : max_msi;
- for (i = (!imin) ? 1 : imin; i < imax; i++) {
- if (test_bit(i - imin, eipend))
- return (i << TOPEI_ID_SHIFT) | i;
- }
- }
- return 0;
- }
- static int imsic_mrif_isel_check(u32 nr_eix, unsigned long isel)
- {
- u32 num = 0;
- switch (isel) {
- case IMSIC_EIDELIVERY:
- case IMSIC_EITHRESHOLD:
- break;
- case IMSIC_EIP0 ... IMSIC_EIP63:
- num = isel - IMSIC_EIP0;
- break;
- case IMSIC_EIE0 ... IMSIC_EIE63:
- num = isel - IMSIC_EIE0;
- break;
- default:
- return -ENOENT;
- }
- #ifndef CONFIG_32BIT
- if (num & 0x1)
- return -EINVAL;
- #endif
- if ((num / 2) >= nr_eix)
- return -EINVAL;
- return 0;
- }
- static int imsic_mrif_rmw(struct imsic_mrif *mrif, u32 nr_eix,
- unsigned long isel, unsigned long *val,
- unsigned long new_val, unsigned long wr_mask)
- {
- bool pend;
- struct imsic_mrif_eix *eix;
- unsigned long *ei, num, old_val = 0;
- switch (isel) {
- case IMSIC_EIDELIVERY:
- old_val = imsic_mrif_atomic_rmw(mrif, &mrif->eidelivery,
- new_val, wr_mask & 0x1);
- break;
- case IMSIC_EITHRESHOLD:
- old_val = imsic_mrif_atomic_rmw(mrif, &mrif->eithreshold,
- new_val, wr_mask & (IMSIC_MAX_ID - 1));
- break;
- case IMSIC_EIP0 ... IMSIC_EIP63:
- case IMSIC_EIE0 ... IMSIC_EIE63:
- if (isel >= IMSIC_EIP0 && isel <= IMSIC_EIP63) {
- pend = true;
- num = isel - IMSIC_EIP0;
- } else {
- pend = false;
- num = isel - IMSIC_EIE0;
- }
- if ((num / 2) >= nr_eix)
- return -EINVAL;
- eix = &mrif->eix[num / 2];
- #ifndef CONFIG_32BIT
- if (num & 0x1)
- return -EINVAL;
- ei = (pend) ? &eix->eip[0] : &eix->eie[0];
- #else
- ei = (pend) ? &eix->eip[num & 0x1] : &eix->eie[num & 0x1];
- #endif
- /* Bit0 of EIP0 or EIE0 is read-only */
- if (!num)
- wr_mask &= ~BIT(0);
- old_val = imsic_mrif_atomic_rmw(mrif, ei, new_val, wr_mask);
- break;
- default:
- return -ENOENT;
- }
- if (val)
- *val = old_val;
- return 0;
- }
- struct imsic_vsfile_read_data {
- int hgei;
- u32 nr_eix;
- bool clear;
- struct imsic_mrif *mrif;
- };
- static void imsic_vsfile_local_read(void *data)
- {
- u32 i;
- struct imsic_mrif_eix *eix;
- struct imsic_vsfile_read_data *idata = data;
- struct imsic_mrif *mrif = idata->mrif;
- unsigned long new_hstatus, old_hstatus, old_vsiselect;
- old_vsiselect = csr_read(CSR_VSISELECT);
- old_hstatus = csr_read(CSR_HSTATUS);
- new_hstatus = old_hstatus & ~HSTATUS_VGEIN;
- new_hstatus |= ((unsigned long)idata->hgei) << HSTATUS_VGEIN_SHIFT;
- csr_write(CSR_HSTATUS, new_hstatus);
- /*
- * We don't use imsic_mrif_atomic_xyz() functions to store
- * values in MRIF because imsic_vsfile_read() is always called
- * with pointer to temporary MRIF on stack.
- */
- if (idata->clear) {
- mrif->eidelivery = imsic_vs_csr_swap(IMSIC_EIDELIVERY, 0);
- mrif->eithreshold = imsic_vs_csr_swap(IMSIC_EITHRESHOLD, 0);
- for (i = 0; i < idata->nr_eix; i++) {
- eix = &mrif->eix[i];
- eix->eip[0] = imsic_eix_swap(IMSIC_EIP0 + i * 2, 0);
- eix->eie[0] = imsic_eix_swap(IMSIC_EIE0 + i * 2, 0);
- #ifdef CONFIG_32BIT
- eix->eip[1] = imsic_eix_swap(IMSIC_EIP0 + i * 2 + 1, 0);
- eix->eie[1] = imsic_eix_swap(IMSIC_EIE0 + i * 2 + 1, 0);
- #endif
- }
- } else {
- mrif->eidelivery = imsic_vs_csr_read(IMSIC_EIDELIVERY);
- mrif->eithreshold = imsic_vs_csr_read(IMSIC_EITHRESHOLD);
- for (i = 0; i < idata->nr_eix; i++) {
- eix = &mrif->eix[i];
- eix->eip[0] = imsic_eix_read(IMSIC_EIP0 + i * 2);
- eix->eie[0] = imsic_eix_read(IMSIC_EIE0 + i * 2);
- #ifdef CONFIG_32BIT
- eix->eip[1] = imsic_eix_read(IMSIC_EIP0 + i * 2 + 1);
- eix->eie[1] = imsic_eix_read(IMSIC_EIE0 + i * 2 + 1);
- #endif
- }
- }
- csr_write(CSR_HSTATUS, old_hstatus);
- csr_write(CSR_VSISELECT, old_vsiselect);
- }
- static void imsic_vsfile_read(int vsfile_hgei, int vsfile_cpu, u32 nr_eix,
- bool clear, struct imsic_mrif *mrif)
- {
- struct imsic_vsfile_read_data idata;
- /* We can only read clear if we have a IMSIC VS-file */
- if (vsfile_cpu < 0 || vsfile_hgei <= 0)
- return;
- /* We can only read clear on local CPU */
- idata.hgei = vsfile_hgei;
- idata.nr_eix = nr_eix;
- idata.clear = clear;
- idata.mrif = mrif;
- on_each_cpu_mask(cpumask_of(vsfile_cpu),
- imsic_vsfile_local_read, &idata, 1);
- }
- struct imsic_vsfile_rw_data {
- int hgei;
- int isel;
- bool write;
- unsigned long val;
- };
- static void imsic_vsfile_local_rw(void *data)
- {
- struct imsic_vsfile_rw_data *idata = data;
- unsigned long new_hstatus, old_hstatus, old_vsiselect;
- old_vsiselect = csr_read(CSR_VSISELECT);
- old_hstatus = csr_read(CSR_HSTATUS);
- new_hstatus = old_hstatus & ~HSTATUS_VGEIN;
- new_hstatus |= ((unsigned long)idata->hgei) << HSTATUS_VGEIN_SHIFT;
- csr_write(CSR_HSTATUS, new_hstatus);
- switch (idata->isel) {
- case IMSIC_EIDELIVERY:
- if (idata->write)
- imsic_vs_csr_write(IMSIC_EIDELIVERY, idata->val);
- else
- idata->val = imsic_vs_csr_read(IMSIC_EIDELIVERY);
- break;
- case IMSIC_EITHRESHOLD:
- if (idata->write)
- imsic_vs_csr_write(IMSIC_EITHRESHOLD, idata->val);
- else
- idata->val = imsic_vs_csr_read(IMSIC_EITHRESHOLD);
- break;
- case IMSIC_EIP0 ... IMSIC_EIP63:
- case IMSIC_EIE0 ... IMSIC_EIE63:
- #ifndef CONFIG_32BIT
- if (idata->isel & 0x1)
- break;
- #endif
- if (idata->write)
- imsic_eix_write(idata->isel, idata->val);
- else
- idata->val = imsic_eix_read(idata->isel);
- break;
- default:
- break;
- }
- csr_write(CSR_HSTATUS, old_hstatus);
- csr_write(CSR_VSISELECT, old_vsiselect);
- }
- static int imsic_vsfile_rw(int vsfile_hgei, int vsfile_cpu, u32 nr_eix,
- unsigned long isel, bool write,
- unsigned long *val)
- {
- int rc;
- struct imsic_vsfile_rw_data rdata;
- /* We can only access register if we have a IMSIC VS-file */
- if (vsfile_cpu < 0 || vsfile_hgei <= 0)
- return -EINVAL;
- /* Check IMSIC register iselect */
- rc = imsic_mrif_isel_check(nr_eix, isel);
- if (rc)
- return rc;
- /* We can only access register on local CPU */
- rdata.hgei = vsfile_hgei;
- rdata.isel = isel;
- rdata.write = write;
- rdata.val = (write) ? *val : 0;
- on_each_cpu_mask(cpumask_of(vsfile_cpu),
- imsic_vsfile_local_rw, &rdata, 1);
- if (!write)
- *val = rdata.val;
- return 0;
- }
- static void imsic_vsfile_local_clear(int vsfile_hgei, u32 nr_eix)
- {
- u32 i;
- unsigned long new_hstatus, old_hstatus, old_vsiselect;
- /* We can only zero-out if we have a IMSIC VS-file */
- if (vsfile_hgei <= 0)
- return;
- old_vsiselect = csr_read(CSR_VSISELECT);
- old_hstatus = csr_read(CSR_HSTATUS);
- new_hstatus = old_hstatus & ~HSTATUS_VGEIN;
- new_hstatus |= ((unsigned long)vsfile_hgei) << HSTATUS_VGEIN_SHIFT;
- csr_write(CSR_HSTATUS, new_hstatus);
- imsic_vs_csr_write(IMSIC_EIDELIVERY, 0);
- imsic_vs_csr_write(IMSIC_EITHRESHOLD, 0);
- for (i = 0; i < nr_eix; i++) {
- imsic_eix_write(IMSIC_EIP0 + i * 2, 0);
- imsic_eix_write(IMSIC_EIE0 + i * 2, 0);
- #ifdef CONFIG_32BIT
- imsic_eix_write(IMSIC_EIP0 + i * 2 + 1, 0);
- imsic_eix_write(IMSIC_EIE0 + i * 2 + 1, 0);
- #endif
- }
- csr_write(CSR_HSTATUS, old_hstatus);
- csr_write(CSR_VSISELECT, old_vsiselect);
- }
- static void imsic_vsfile_local_update(int vsfile_hgei, u32 nr_eix,
- struct imsic_mrif *mrif)
- {
- u32 i;
- struct imsic_mrif_eix *eix;
- unsigned long new_hstatus, old_hstatus, old_vsiselect;
- /* We can only update if we have a HW IMSIC context */
- if (vsfile_hgei <= 0)
- return;
- /*
- * We don't use imsic_mrif_atomic_xyz() functions to read values
- * from MRIF in this function because it is always called with
- * pointer to temporary MRIF on stack.
- */
- old_vsiselect = csr_read(CSR_VSISELECT);
- old_hstatus = csr_read(CSR_HSTATUS);
- new_hstatus = old_hstatus & ~HSTATUS_VGEIN;
- new_hstatus |= ((unsigned long)vsfile_hgei) << HSTATUS_VGEIN_SHIFT;
- csr_write(CSR_HSTATUS, new_hstatus);
- for (i = 0; i < nr_eix; i++) {
- eix = &mrif->eix[i];
- imsic_eix_set(IMSIC_EIP0 + i * 2, eix->eip[0]);
- imsic_eix_set(IMSIC_EIE0 + i * 2, eix->eie[0]);
- #ifdef CONFIG_32BIT
- imsic_eix_set(IMSIC_EIP0 + i * 2 + 1, eix->eip[1]);
- imsic_eix_set(IMSIC_EIE0 + i * 2 + 1, eix->eie[1]);
- #endif
- }
- imsic_vs_csr_write(IMSIC_EITHRESHOLD, mrif->eithreshold);
- imsic_vs_csr_write(IMSIC_EIDELIVERY, mrif->eidelivery);
- csr_write(CSR_HSTATUS, old_hstatus);
- csr_write(CSR_VSISELECT, old_vsiselect);
- }
- static void imsic_vsfile_cleanup(struct imsic *imsic)
- {
- int old_vsfile_hgei, old_vsfile_cpu;
- unsigned long flags;
- /*
- * We don't use imsic_mrif_atomic_xyz() functions to clear the
- * SW-file in this function because it is always called when the
- * VCPU is being destroyed.
- */
- write_lock_irqsave(&imsic->vsfile_lock, flags);
- old_vsfile_hgei = imsic->vsfile_hgei;
- old_vsfile_cpu = imsic->vsfile_cpu;
- imsic->vsfile_cpu = imsic->vsfile_hgei = -1;
- imsic->vsfile_va = NULL;
- imsic->vsfile_pa = 0;
- write_unlock_irqrestore(&imsic->vsfile_lock, flags);
- memset(imsic->swfile, 0, sizeof(*imsic->swfile));
- if (old_vsfile_cpu >= 0)
- kvm_riscv_aia_free_hgei(old_vsfile_cpu, old_vsfile_hgei);
- }
- static void imsic_swfile_extirq_update(struct kvm_vcpu *vcpu)
- {
- struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
- struct imsic_mrif *mrif = imsic->swfile;
- unsigned long flags;
- /*
- * The critical section is necessary during external interrupt
- * updates to avoid the risk of losing interrupts due to potential
- * interruptions between reading topei and updating pending status.
- */
- raw_spin_lock_irqsave(&imsic->swfile_extirq_lock, flags);
- if (imsic_mrif_atomic_read(mrif, &mrif->eidelivery) &&
- imsic_mrif_topei(mrif, imsic->nr_eix, imsic->nr_msis))
- kvm_riscv_vcpu_set_interrupt(vcpu, IRQ_VS_EXT);
- else
- kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_EXT);
- raw_spin_unlock_irqrestore(&imsic->swfile_extirq_lock, flags);
- }
- static void imsic_swfile_read(struct kvm_vcpu *vcpu, bool clear,
- struct imsic_mrif *mrif)
- {
- struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
- /*
- * We don't use imsic_mrif_atomic_xyz() functions to read and
- * write SW-file and MRIF in this function because it is always
- * called when VCPU is not using SW-file and the MRIF points to
- * a temporary MRIF on stack.
- */
- memcpy(mrif, imsic->swfile, sizeof(*mrif));
- if (clear) {
- memset(imsic->swfile, 0, sizeof(*imsic->swfile));
- kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_EXT);
- }
- }
- static void imsic_swfile_update(struct kvm_vcpu *vcpu,
- struct imsic_mrif *mrif)
- {
- u32 i;
- struct imsic_mrif_eix *seix, *eix;
- struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
- struct imsic_mrif *smrif = imsic->swfile;
- imsic_mrif_atomic_write(smrif, &smrif->eidelivery, mrif->eidelivery);
- imsic_mrif_atomic_write(smrif, &smrif->eithreshold, mrif->eithreshold);
- for (i = 0; i < imsic->nr_eix; i++) {
- seix = &smrif->eix[i];
- eix = &mrif->eix[i];
- imsic_mrif_atomic_or(smrif, &seix->eip[0], eix->eip[0]);
- imsic_mrif_atomic_or(smrif, &seix->eie[0], eix->eie[0]);
- #ifdef CONFIG_32BIT
- imsic_mrif_atomic_or(smrif, &seix->eip[1], eix->eip[1]);
- imsic_mrif_atomic_or(smrif, &seix->eie[1], eix->eie[1]);
- #endif
- }
- imsic_swfile_extirq_update(vcpu);
- }
- void kvm_riscv_vcpu_aia_imsic_release(struct kvm_vcpu *vcpu)
- {
- unsigned long flags;
- struct imsic_mrif tmrif;
- int old_vsfile_hgei, old_vsfile_cpu;
- struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
- /* Read and clear IMSIC VS-file details */
- write_lock_irqsave(&imsic->vsfile_lock, flags);
- old_vsfile_hgei = imsic->vsfile_hgei;
- old_vsfile_cpu = imsic->vsfile_cpu;
- imsic->vsfile_cpu = imsic->vsfile_hgei = -1;
- imsic->vsfile_va = NULL;
- imsic->vsfile_pa = 0;
- write_unlock_irqrestore(&imsic->vsfile_lock, flags);
- /* Do nothing, if no IMSIC VS-file to release */
- if (old_vsfile_cpu < 0)
- return;
- /*
- * At this point, all interrupt producers are still using
- * the old IMSIC VS-file so we first re-direct all interrupt
- * producers.
- */
- /* Purge the G-stage mapping */
- kvm_riscv_gstage_iounmap(vcpu->kvm,
- vcpu->arch.aia_context.imsic_addr,
- IMSIC_MMIO_PAGE_SZ);
- /* TODO: Purge the IOMMU mapping ??? */
- /*
- * At this point, all interrupt producers have been re-directed
- * to somewhere else so we move register state from the old IMSIC
- * VS-file to the IMSIC SW-file.
- */
- /* Read and clear register state from old IMSIC VS-file */
- memset(&tmrif, 0, sizeof(tmrif));
- imsic_vsfile_read(old_vsfile_hgei, old_vsfile_cpu, imsic->nr_hw_eix,
- true, &tmrif);
- /* Update register state in IMSIC SW-file */
- imsic_swfile_update(vcpu, &tmrif);
- /* Free-up old IMSIC VS-file */
- kvm_riscv_aia_free_hgei(old_vsfile_cpu, old_vsfile_hgei);
- }
- int kvm_riscv_vcpu_aia_imsic_update(struct kvm_vcpu *vcpu)
- {
- unsigned long flags;
- phys_addr_t new_vsfile_pa;
- struct imsic_mrif tmrif;
- void __iomem *new_vsfile_va;
- struct kvm *kvm = vcpu->kvm;
- struct kvm_run *run = vcpu->run;
- struct kvm_vcpu_aia *vaia = &vcpu->arch.aia_context;
- struct imsic *imsic = vaia->imsic_state;
- int ret = 0, new_vsfile_hgei = -1, old_vsfile_hgei, old_vsfile_cpu;
- /* Do nothing for emulation mode */
- if (kvm->arch.aia.mode == KVM_DEV_RISCV_AIA_MODE_EMUL)
- return 1;
- /* Read old IMSIC VS-file details */
- read_lock_irqsave(&imsic->vsfile_lock, flags);
- old_vsfile_hgei = imsic->vsfile_hgei;
- old_vsfile_cpu = imsic->vsfile_cpu;
- read_unlock_irqrestore(&imsic->vsfile_lock, flags);
- /* Do nothing if we are continuing on same CPU */
- if (old_vsfile_cpu == vcpu->cpu)
- return 1;
- /* Allocate new IMSIC VS-file */
- ret = kvm_riscv_aia_alloc_hgei(vcpu->cpu, vcpu,
- &new_vsfile_va, &new_vsfile_pa);
- if (ret <= 0) {
- /* For HW acceleration mode, we can't continue */
- if (kvm->arch.aia.mode == KVM_DEV_RISCV_AIA_MODE_HWACCEL) {
- run->fail_entry.hardware_entry_failure_reason =
- CSR_HSTATUS;
- run->fail_entry.cpu = vcpu->cpu;
- run->exit_reason = KVM_EXIT_FAIL_ENTRY;
- return 0;
- }
- /* Release old IMSIC VS-file */
- if (old_vsfile_cpu >= 0)
- kvm_riscv_vcpu_aia_imsic_release(vcpu);
- /* For automatic mode, we continue */
- goto done;
- }
- new_vsfile_hgei = ret;
- /*
- * At this point, all interrupt producers are still using
- * to the old IMSIC VS-file so we first move all interrupt
- * producers to the new IMSIC VS-file.
- */
- /* Zero-out new IMSIC VS-file */
- imsic_vsfile_local_clear(new_vsfile_hgei, imsic->nr_hw_eix);
- /* Update G-stage mapping for the new IMSIC VS-file */
- ret = kvm_riscv_gstage_ioremap(kvm, vcpu->arch.aia_context.imsic_addr,
- new_vsfile_pa, IMSIC_MMIO_PAGE_SZ,
- true, true);
- if (ret)
- goto fail_free_vsfile_hgei;
- /* TODO: Update the IOMMU mapping ??? */
- /* Update new IMSIC VS-file details in IMSIC context */
- write_lock_irqsave(&imsic->vsfile_lock, flags);
- imsic->vsfile_hgei = new_vsfile_hgei;
- imsic->vsfile_cpu = vcpu->cpu;
- imsic->vsfile_va = new_vsfile_va;
- imsic->vsfile_pa = new_vsfile_pa;
- write_unlock_irqrestore(&imsic->vsfile_lock, flags);
- /*
- * At this point, all interrupt producers have been moved
- * to the new IMSIC VS-file so we move register state from
- * the old IMSIC VS/SW-file to the new IMSIC VS-file.
- */
- memset(&tmrif, 0, sizeof(tmrif));
- if (old_vsfile_cpu >= 0) {
- /* Read and clear register state from old IMSIC VS-file */
- imsic_vsfile_read(old_vsfile_hgei, old_vsfile_cpu,
- imsic->nr_hw_eix, true, &tmrif);
- /* Free-up old IMSIC VS-file */
- kvm_riscv_aia_free_hgei(old_vsfile_cpu, old_vsfile_hgei);
- } else {
- /* Read and clear register state from IMSIC SW-file */
- imsic_swfile_read(vcpu, true, &tmrif);
- }
- /* Restore register state in the new IMSIC VS-file */
- imsic_vsfile_local_update(new_vsfile_hgei, imsic->nr_hw_eix, &tmrif);
- done:
- /* Set VCPU HSTATUS.VGEIN to new IMSIC VS-file */
- vcpu->arch.guest_context.hstatus &= ~HSTATUS_VGEIN;
- if (new_vsfile_hgei > 0)
- vcpu->arch.guest_context.hstatus |=
- ((unsigned long)new_vsfile_hgei) << HSTATUS_VGEIN_SHIFT;
- /* Continue run-loop */
- return 1;
- fail_free_vsfile_hgei:
- kvm_riscv_aia_free_hgei(vcpu->cpu, new_vsfile_hgei);
- return ret;
- }
- int kvm_riscv_vcpu_aia_imsic_rmw(struct kvm_vcpu *vcpu, unsigned long isel,
- unsigned long *val, unsigned long new_val,
- unsigned long wr_mask)
- {
- u32 topei;
- struct imsic_mrif_eix *eix;
- int r, rc = KVM_INSN_CONTINUE_NEXT_SEPC;
- struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
- if (isel == KVM_RISCV_AIA_IMSIC_TOPEI) {
- /* Read pending and enabled interrupt with highest priority */
- topei = imsic_mrif_topei(imsic->swfile, imsic->nr_eix,
- imsic->nr_msis);
- if (val)
- *val = topei;
- /* Writes ignore value and clear top pending interrupt */
- if (topei && wr_mask) {
- topei >>= TOPEI_ID_SHIFT;
- if (topei) {
- eix = &imsic->swfile->eix[topei /
- BITS_PER_TYPE(u64)];
- clear_bit(topei & (BITS_PER_TYPE(u64) - 1),
- eix->eip);
- }
- }
- } else {
- r = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix, isel,
- val, new_val, wr_mask);
- /* Forward unknown IMSIC register to user-space */
- if (r)
- rc = (r == -ENOENT) ? 0 : KVM_INSN_ILLEGAL_TRAP;
- }
- if (wr_mask)
- imsic_swfile_extirq_update(vcpu);
- return rc;
- }
- int kvm_riscv_aia_imsic_rw_attr(struct kvm *kvm, unsigned long type,
- bool write, unsigned long *val)
- {
- u32 isel, vcpu_id;
- unsigned long flags;
- struct imsic *imsic;
- struct kvm_vcpu *vcpu;
- int rc, vsfile_hgei, vsfile_cpu;
- if (!kvm_riscv_aia_initialized(kvm))
- return -ENODEV;
- vcpu_id = KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(type);
- vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id);
- if (!vcpu)
- return -ENODEV;
- isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
- imsic = vcpu->arch.aia_context.imsic_state;
- read_lock_irqsave(&imsic->vsfile_lock, flags);
- rc = 0;
- vsfile_hgei = imsic->vsfile_hgei;
- vsfile_cpu = imsic->vsfile_cpu;
- if (vsfile_cpu < 0) {
- if (write) {
- rc = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix,
- isel, NULL, *val, -1UL);
- imsic_swfile_extirq_update(vcpu);
- } else
- rc = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix,
- isel, val, 0, 0);
- }
- read_unlock_irqrestore(&imsic->vsfile_lock, flags);
- if (!rc && vsfile_cpu >= 0)
- rc = imsic_vsfile_rw(vsfile_hgei, vsfile_cpu, imsic->nr_eix,
- isel, write, val);
- return rc;
- }
- int kvm_riscv_aia_imsic_has_attr(struct kvm *kvm, unsigned long type)
- {
- u32 isel, vcpu_id;
- struct imsic *imsic;
- struct kvm_vcpu *vcpu;
- if (!kvm_riscv_aia_initialized(kvm))
- return -ENODEV;
- vcpu_id = KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(type);
- vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id);
- if (!vcpu)
- return -ENODEV;
- isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
- imsic = vcpu->arch.aia_context.imsic_state;
- return imsic_mrif_isel_check(imsic->nr_eix, isel);
- }
- void kvm_riscv_vcpu_aia_imsic_reset(struct kvm_vcpu *vcpu)
- {
- struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
- if (!imsic)
- return;
- kvm_riscv_vcpu_aia_imsic_release(vcpu);
- memset(imsic->swfile, 0, sizeof(*imsic->swfile));
- }
- int kvm_riscv_vcpu_aia_imsic_inject(struct kvm_vcpu *vcpu,
- u32 guest_index, u32 offset, u32 iid)
- {
- unsigned long flags;
- struct imsic_mrif_eix *eix;
- struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
- /* We only emulate one IMSIC MMIO page for each Guest VCPU */
- if (!imsic || !iid || guest_index ||
- (offset != IMSIC_MMIO_SETIPNUM_LE &&
- offset != IMSIC_MMIO_SETIPNUM_BE))
- return -ENODEV;
- iid = (offset == IMSIC_MMIO_SETIPNUM_BE) ? __swab32(iid) : iid;
- if (imsic->nr_msis <= iid)
- return -EINVAL;
- read_lock_irqsave(&imsic->vsfile_lock, flags);
- if (imsic->vsfile_cpu >= 0) {
- writel(iid, imsic->vsfile_va + IMSIC_MMIO_SETIPNUM_LE);
- kvm_vcpu_kick(vcpu);
- } else {
- eix = &imsic->swfile->eix[iid / BITS_PER_TYPE(u64)];
- set_bit(iid & (BITS_PER_TYPE(u64) - 1), eix->eip);
- imsic_swfile_extirq_update(vcpu);
- }
- read_unlock_irqrestore(&imsic->vsfile_lock, flags);
- return 0;
- }
- static int imsic_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
- gpa_t addr, int len, void *val)
- {
- if (len != 4 || (addr & 0x3) != 0)
- return -EOPNOTSUPP;
- *((u32 *)val) = 0;
- return 0;
- }
- static int imsic_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
- gpa_t addr, int len, const void *val)
- {
- struct kvm_msi msi = { 0 };
- if (len != 4 || (addr & 0x3) != 0)
- return -EOPNOTSUPP;
- msi.address_hi = addr >> 32;
- msi.address_lo = (u32)addr;
- msi.data = *((const u32 *)val);
- kvm_riscv_aia_inject_msi(vcpu->kvm, &msi);
- return 0;
- };
- static struct kvm_io_device_ops imsic_iodoev_ops = {
- .read = imsic_mmio_read,
- .write = imsic_mmio_write,
- };
- int kvm_riscv_vcpu_aia_imsic_init(struct kvm_vcpu *vcpu)
- {
- int ret = 0;
- struct imsic *imsic;
- struct page *swfile_page;
- struct kvm *kvm = vcpu->kvm;
- /* Fail if we have zero IDs */
- if (!kvm->arch.aia.nr_ids)
- return -EINVAL;
- /* Allocate IMSIC context */
- imsic = kzalloc(sizeof(*imsic), GFP_KERNEL);
- if (!imsic)
- return -ENOMEM;
- vcpu->arch.aia_context.imsic_state = imsic;
- /* Setup IMSIC context */
- imsic->nr_msis = kvm->arch.aia.nr_ids + 1;
- rwlock_init(&imsic->vsfile_lock);
- imsic->nr_eix = BITS_TO_U64(imsic->nr_msis);
- imsic->nr_hw_eix = BITS_TO_U64(kvm_riscv_aia_max_ids);
- imsic->vsfile_hgei = imsic->vsfile_cpu = -1;
- /* Setup IMSIC SW-file */
- swfile_page = alloc_pages(GFP_KERNEL | __GFP_ZERO,
- get_order(sizeof(*imsic->swfile)));
- if (!swfile_page) {
- ret = -ENOMEM;
- goto fail_free_imsic;
- }
- imsic->swfile = page_to_virt(swfile_page);
- imsic->swfile_pa = page_to_phys(swfile_page);
- raw_spin_lock_init(&imsic->swfile_extirq_lock);
- /* Setup IO device */
- kvm_iodevice_init(&imsic->iodev, &imsic_iodoev_ops);
- mutex_lock(&kvm->slots_lock);
- ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS,
- vcpu->arch.aia_context.imsic_addr,
- KVM_DEV_RISCV_IMSIC_SIZE,
- &imsic->iodev);
- mutex_unlock(&kvm->slots_lock);
- if (ret)
- goto fail_free_swfile;
- return 0;
- fail_free_swfile:
- free_pages((unsigned long)imsic->swfile,
- get_order(sizeof(*imsic->swfile)));
- fail_free_imsic:
- vcpu->arch.aia_context.imsic_state = NULL;
- kfree(imsic);
- return ret;
- }
- void kvm_riscv_vcpu_aia_imsic_cleanup(struct kvm_vcpu *vcpu)
- {
- struct kvm *kvm = vcpu->kvm;
- struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
- if (!imsic)
- return;
- imsic_vsfile_cleanup(imsic);
- mutex_lock(&kvm->slots_lock);
- kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &imsic->iodev);
- mutex_unlock(&kvm->slots_lock);
- free_pages((unsigned long)imsic->swfile,
- get_order(sizeof(*imsic->swfile)));
- vcpu->arch.aia_context.imsic_state = NULL;
- kfree(imsic);
- }
|