[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [xen staging] xen/riscv: imsic_init() implementation
commit c9bd8b322ecbb486bc4487ee54c260ace9965f25 Author: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx> AuthorDate: Wed Jul 9 10:57:15 2025 +0200 Commit: Jan Beulich <jbeulich@xxxxxxxx> CommitDate: Wed Jul 9 10:57:15 2025 +0200 xen/riscv: imsic_init() implementation imsic_init() is introduced to parse device tree node, which has the following bindings [2], and based on the parsed information update IMSIC configuration which is stored in imsic_cfg. The following helpers are introduces for imsic_init() usage: - imsic_parse_node() parses IMSIC node from DTS - imsic_get_parent_hartid() returns the hart ( CPU ) ID of the given device tree node. This patch is based on the code from [1]. Since Microchip originally developed imsic.{c,h}, an internal discussion with them led to the decision to use the MIT license. [1] https://gitlab.com/xen-project/people/olkur/xen/-/commit/0b1a94f2bc3bb1a81cd26bb75f0bf578f84cb4d4 [2] https://elixir.bootlin.com/linux/v6.12/source/Documentation/devicetree/bindings/interrupt-controller/riscv,imsics.yaml Co-developed-by: Romain Caritey <Romain.Caritey@xxxxxxxxxxxxx> Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx> Acked-by: Jan Beulich <jbeulich@xxxxxxxx> --- xen/arch/riscv/Makefile | 1 + xen/arch/riscv/imsic.c | 369 +++++++++++++++++++++++++++++++++++++ xen/arch/riscv/include/asm/imsic.h | 55 ++++++ xen/arch/riscv/include/asm/smp.h | 13 ++ 4 files changed, 438 insertions(+) diff --git a/xen/arch/riscv/Makefile b/xen/arch/riscv/Makefile index a1c145c506..e2b8aa42c8 100644 --- a/xen/arch/riscv/Makefile +++ b/xen/arch/riscv/Makefile @@ -2,6 +2,7 @@ obj-y += aplic.o obj-y += cpufeature.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-y += entry.o +obj-y += imsic.o obj-y += intc.o obj-y += irq.o obj-y += mm.o diff --git a/xen/arch/riscv/imsic.c b/xen/arch/riscv/imsic.c new file mode 100644 index 0000000000..63f4233035 --- /dev/null +++ b/xen/arch/riscv/imsic.c @@ -0,0 +1,369 @@ +/* SPDX-License-Identifier: MIT */ + +/* + * xen/arch/riscv/imsic.c + * + * RISC-V Incoming MSI Controller support + * + * (c) Microchip Technology Inc. + * (c) Vates + */ + +#include <xen/bitops.h> +#include <xen/const.h> +#include <xen/cpumask.h> +#include <xen/device_tree.h> +#include <xen/errno.h> +#include <xen/init.h> +#include <xen/macros.h> +#include <xen/smp.h> +#include <xen/spinlock.h> +#include <xen/xvmalloc.h> + +#include <asm/imsic.h> + +#define IMSIC_HART_SIZE(guest_bits) (BIT(guest_bits, U) * IMSIC_MMIO_PAGE_SZ) + +struct imsic_mmios { + paddr_t base_addr; + unsigned long size; +}; + +static struct imsic_config imsic_cfg; + +/* Callers aren't intended to changed imsic_cfg so return const. */ +const struct imsic_config *imsic_get_config(void) +{ + return &imsic_cfg; +} + +static int __init imsic_get_parent_hartid(const struct dt_device_node *node, + unsigned int index, + unsigned long *hartid) +{ + int res; + struct dt_phandle_args args; + + res = dt_parse_phandle_with_args(node, "interrupts-extended", + "#interrupt-cells", index, &args); + if ( !res ) + res = dt_processor_hartid(args.np->parent, hartid); + + return res; +} + +/* + * Parses IMSIC DT node. + * + * Returns 0 if initialization is successful, a negative value on failure, + * or IRQ_M_EXT if the IMSIC node corresponds to a machine-mode IMSIC, + * which should be ignored by the hypervisor. + */ +static int imsic_parse_node(const struct dt_device_node *node, + unsigned int *nr_parent_irqs, + unsigned int *nr_mmios) +{ + int rc; + unsigned int tmp; + paddr_t base_addr; + uint32_t *irq_range; + + *nr_parent_irqs = dt_number_of_irq(node); + if ( !*nr_parent_irqs ) + panic("%s: irq_num can't be 0. Check %s node\n", __func__, + dt_node_full_name(node)); + + irq_range = xvzalloc_array(uint32_t, *nr_parent_irqs * 2); + if ( !irq_range ) + panic("%s: irq_range[] allocation failed\n", __func__); + + if ( (rc = dt_property_read_u32_array(node, "interrupts-extended", + irq_range, *nr_parent_irqs * 2)) ) + panic("%s: unable to find interrupt-extended in %s node: %d\n", + __func__, dt_node_full_name(node), rc); + + /* Check that interrupts-extended property is well-formed. */ + for ( unsigned int i = 2; i < (*nr_parent_irqs * 2); i += 2 ) + { + if ( irq_range[i + 1] != irq_range[1] ) + panic("%s: mode[%u] != %u\n", __func__, i + 1, irq_range[1]); + } + + if ( irq_range[1] == IRQ_M_EXT ) + { + /* Machine mode imsic node, ignore it. */ + xvfree(irq_range); + + return IRQ_M_EXT; + } + + xvfree(irq_range); + + if ( !dt_property_read_u32(node, "riscv,guest-index-bits", + &imsic_cfg.guest_index_bits) ) + imsic_cfg.guest_index_bits = 0; + tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT; + if ( tmp < imsic_cfg.guest_index_bits ) + { + printk(XENLOG_ERR "%s: guest index bits too big\n", + dt_node_name(node)); + return -ENOENT; + } + + /* Find number of HART index bits */ + if ( !dt_property_read_u32(node, "riscv,hart-index-bits", + &imsic_cfg.hart_index_bits) ) + /* Assume default value */ + imsic_cfg.hart_index_bits = fls(*nr_parent_irqs - 1); + tmp -= imsic_cfg.guest_index_bits; + if ( tmp < imsic_cfg.hart_index_bits ) + { + printk(XENLOG_ERR "%s: HART index bits too big\n", + dt_node_name(node)); + return -ENOENT; + } + + /* Find number of group index bits */ + if ( !dt_property_read_u32(node, "riscv,group-index-bits", + &imsic_cfg.group_index_bits) ) + imsic_cfg.group_index_bits = 0; + tmp -= imsic_cfg.hart_index_bits; + if ( tmp < imsic_cfg.group_index_bits ) + { + printk(XENLOG_ERR "%s: group index bits too big\n", + dt_node_name(node)); + return -ENOENT; + } + + /* Find first bit position of group index */ + tmp = IMSIC_MMIO_PAGE_SHIFT * 2; + if ( !dt_property_read_u32(node, "riscv,group-index-shift", + &imsic_cfg.group_index_shift) ) + imsic_cfg.group_index_shift = tmp; + if ( imsic_cfg.group_index_shift < tmp ) + { + printk(XENLOG_ERR "%s: group index shift too small\n", + dt_node_name(node)); + return -ENOENT; + } + tmp = imsic_cfg.group_index_bits + imsic_cfg.group_index_shift - 1; + if ( tmp >= BITS_PER_LONG ) + { + printk(XENLOG_ERR "%s: group index shift too big\n", + dt_node_name(node)); + return -ENOENT; + } + + /* Find number of interrupt identities */ + if ( !dt_property_read_u32(node, "riscv,num-ids", &imsic_cfg.nr_ids) ) + { + printk(XENLOG_ERR "%s: number of interrupt identities not found\n", + node->name); + return -ENOENT; + } + + if ( (imsic_cfg.nr_ids < IMSIC_MIN_ID) || + (imsic_cfg.nr_ids > IMSIC_MAX_ID) ) + { + printk(XENLOG_ERR "%s: invalid number of interrupt identities\n", + node->name); + return -ENOENT; + } + + /* Compute base address */ + *nr_mmios = 0; + rc = dt_device_get_address(node, *nr_mmios, &base_addr, NULL); + if ( rc ) + { + printk(XENLOG_ERR "%s: first MMIO resource not found: %d\n", + dt_node_name(node), rc); + return rc; + } + + imsic_cfg.base_addr = base_addr; + imsic_cfg.base_addr &= ~(BIT(imsic_cfg.guest_index_bits + + imsic_cfg.hart_index_bits + + IMSIC_MMIO_PAGE_SHIFT, UL) - 1); + imsic_cfg.base_addr &= ~((BIT(imsic_cfg.group_index_bits, UL) - 1) << + imsic_cfg.group_index_shift); + + /* Find number of MMIO register sets */ + do { + ++*nr_mmios; + } while ( !dt_device_get_address(node, *nr_mmios, &base_addr, NULL) ); + + return 0; +} + +/* + * Initialize the imsic_cfg structure based on the IMSIC DT node. + * + * Returns 0 if initialization is successful, a negative value on failure, + * or IRQ_M_EXT if the IMSIC node corresponds to a machine-mode IMSIC, + * which should be ignored by the hypervisor. + */ +int __init imsic_init(const struct dt_device_node *node) +{ + int rc; + unsigned long reloff, hartid; + unsigned int nr_parent_irqs, index, nr_handlers = 0; + paddr_t base_addr; + unsigned int nr_mmios; + struct imsic_mmios *mmios; + struct imsic_msi *msi = NULL; + + /* Parse IMSIC node */ + rc = imsic_parse_node(node, &nr_parent_irqs, &nr_mmios); + /* + * If machine mode imsic node => ignore it. + * If rc < 0 => parsing of IMSIC DT node failed. + */ + if ( (rc == IRQ_M_EXT) || (rc < 0) ) + return rc; + + /* Allocate MMIO resource array */ + mmios = xvzalloc_array(struct imsic_mmios, nr_mmios); + if ( !mmios ) + { + rc = -ENOMEM; + goto imsic_init_err; + } + + msi = xvzalloc_array(struct imsic_msi, nr_parent_irqs); + if ( !msi ) + { + rc = -ENOMEM; + goto imsic_init_err; + } + + /* Check MMIO register sets */ + for ( unsigned int i = 0; i < nr_mmios; i++ ) + { + unsigned int guest_bits = imsic_cfg.guest_index_bits; + unsigned long expected_mmio_size = + IMSIC_HART_SIZE(guest_bits) * nr_parent_irqs; + + rc = dt_device_get_address(node, i, &mmios[i].base_addr, + &mmios[i].size); + if ( rc ) + { + printk(XENLOG_ERR "%s: unable to parse MMIO regset %u\n", + node->name, i); + goto imsic_init_err; + } + + base_addr = mmios[i].base_addr; + base_addr &= ~(BIT(guest_bits + + imsic_cfg.hart_index_bits + + IMSIC_MMIO_PAGE_SHIFT, UL) - 1); + base_addr &= ~((BIT(imsic_cfg.group_index_bits, UL) - 1) << + imsic_cfg.group_index_shift); + if ( base_addr != imsic_cfg.base_addr ) + { + rc = -EINVAL; + printk(XENLOG_ERR "%s: address mismatch for regset %u\n", + node->name, i); + goto imsic_init_err; + } + + if ( mmios[i].size != expected_mmio_size ) + { + rc = -EINVAL; + printk(XENLOG_ERR "%s: IMSIC MMIO size is incorrect %ld, expected MMIO size: %ld\n", + node->name, mmios[i].size, expected_mmio_size); + goto imsic_init_err; + } + } + + /* Configure handlers for target CPUs */ + for ( unsigned int i = 0; i < nr_parent_irqs; i++ ) + { + unsigned int cpu; + + rc = imsic_get_parent_hartid(node, i, &hartid); + if ( rc ) + { + printk(XENLOG_WARNING "%s: cpu ID for parent irq%u not found\n", + node->name, i); + continue; + } + + cpu = hartid_to_cpuid(hartid); + + /* + * If .base_addr is not 0, it indicates that the CPU has already been + * found. + * In this case, skip re-initialization to avoid duplicate setup. + * Also, print a warning message to signal that the DTS should be + * reviewed for possible duplication. + */ + if ( msi[cpu].base_addr ) + { + printk("%s: cpu%u is found twice in interrupts-extended prop\n", + node->name, cpu); + continue; + } + + if ( cpu >= num_possible_cpus() ) + { + printk(XENLOG_WARNING "%s: unsupported hart ID=%#lx for parent irq%u\n", + node->name, hartid, i); + continue; + } + + /* Find MMIO location of MSI page */ + reloff = i * IMSIC_HART_SIZE(imsic_cfg.guest_index_bits); + for ( index = 0; index < nr_mmios; index++ ) + { + if ( reloff < mmios[index].size ) + break; + + /* + * MMIO region size may not be aligned to + * IMSIC_HART_SIZE(guest_index_bits) if + * holes are present. + */ + reloff -= ROUNDUP(mmios[index].size, + IMSIC_HART_SIZE(imsic_cfg.guest_index_bits)); + } + + if ( index == nr_mmios ) + { + printk(XENLOG_WARNING "%s: MMIO not found for parent irq%u\n", + node->name, i); + continue; + } + + if ( !IS_ALIGNED(mmios[cpu].base_addr + reloff, + IMSIC_MMIO_PAGE_SZ) ) + { + printk(XENLOG_WARNING "%s: MMIO address %#lx is not aligned on a page\n", + node->name, msi[cpu].base_addr + reloff); + continue; + } + + msi[cpu].base_addr = mmios[index].base_addr; + msi[cpu].offset = reloff; + + nr_handlers++; + } + + if ( !nr_handlers ) + { + printk(XENLOG_ERR "%s: No CPU handlers found\n", node->name); + rc = -ENODEV; + goto imsic_init_err; + } + + imsic_cfg.msi = msi; + + xvfree(mmios); + + return 0; + + imsic_init_err: + xvfree(mmios); + xvfree(msi); + + return rc; +} diff --git a/xen/arch/riscv/include/asm/imsic.h b/xen/arch/riscv/include/asm/imsic.h new file mode 100644 index 0000000000..9cd12365b1 --- /dev/null +++ b/xen/arch/riscv/include/asm/imsic.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: MIT */ + +/* + * xen/arch/riscv/include/asm/imsic.h + * + * RISC-V Incoming MSI Controller support + * + * (c) Microchip Technology Inc. + */ + +#ifndef ASM_RISCV_IMSIC_H +#define ASM_RISCV_IMSIC_H + +#include <xen/types.h> + +#define IMSIC_MMIO_PAGE_SHIFT 12 +#define IMSIC_MMIO_PAGE_SZ (1UL << IMSIC_MMIO_PAGE_SHIFT) + +#define IMSIC_MIN_ID 63 +#define IMSIC_MAX_ID 2047 + +struct imsic_msi { + paddr_t base_addr; + unsigned long offset; +}; + +struct imsic_config { + /* Base address */ + paddr_t base_addr; + + /* Bits representing Guest index, HART index, and Group index */ + unsigned int guest_index_bits; + unsigned int hart_index_bits; + unsigned int group_index_bits; + unsigned int group_index_shift; + + /* IMSIC phandle */ + unsigned int phandle; + + /* Number of parent irq */ + unsigned int nr_parent_irqs; + + /* Number off interrupt identities */ + unsigned int nr_ids; + + /* MSI */ + const struct imsic_msi *msi; +}; + +struct dt_device_node; +int imsic_init(const struct dt_device_node *node); + +const struct imsic_config *imsic_get_config(void); + +#endif /* ASM_RISCV_IMSIC_H */ diff --git a/xen/arch/riscv/include/asm/smp.h b/xen/arch/riscv/include/asm/smp.h index eb58b6576b..5dbacf1f9d 100644 --- a/xen/arch/riscv/include/asm/smp.h +++ b/xen/arch/riscv/include/asm/smp.h @@ -3,6 +3,7 @@ #define ASM__RISCV__SMP_H #include <xen/cpumask.h> +#include <xen/macros.h> #include <xen/percpu.h> #include <asm/current.h> @@ -18,6 +19,18 @@ static inline unsigned long cpuid_to_hartid(unsigned long cpuid) return pcpu_info[cpuid].hart_id; } +static inline unsigned int hartid_to_cpuid(unsigned long hartid) +{ + for ( unsigned int cpu = 0; cpu < ARRAY_SIZE(pcpu_info); cpu++ ) + { + if ( hartid == cpuid_to_hartid(cpu) ) + return cpu; + } + + /* hartid isn't valid for some reason */ + return NR_CPUS; +} + static inline void set_cpuid_to_hartid(unsigned long cpuid, unsigned long hartid) { -- generated by git-patchbot for /home/xen/git/xen.git#staging
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |