[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v2 10/16] xen/riscv: imsic_init() implementation
On 06.05.2025 18:51, Oleksii Kurochko wrote: > 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_cpuid() 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> > --- > Changes in V2: > - Drop years in copyrights. Did you, really? > - s/riscv_of_processor_hartid/dt_processor_cpuid. > - s/imsic_get_parent_hartid/imsic_get_parent_cpuid. > Rename argument hartid to cpuid. > Make node argument const. > Return res instead of -EINVAL for the failure case of dt_processor_cpuid(). > Drop local variable hart and use cpuid argument instead. > Drop useless return res; > - imsic_parse_node() changes: > - Make node argument const. > - Check the return value of dt_property_read_u32() directly instead of > saving it to rc variable. > - Update tmp usage, use short form "-=". > - Update a check (imsic_cfg.nr_ids >= IMSIC_MAX_ID) to (imsic_cfg.nr_ids > > IMSIC_MAX_ID) > as IMSIC_MAX_ID is changed to maximum valid value, not just the firsr > out-of-range. > - Use `rc` to return value instead of explicitly use -EINVAL. > - Use do {} while() to find number of MMIO register sets. > - Set IMSIC_MAX_ID to 2047 (maximum possible IRQ number). > - imsic_init() changes: > - Use unsigned int in for's expression1. > - s/xfree/XFEE. > - Allocate msi and cpus array dynamically. > - Drop forward declaration before declaration of imsic_get_config() in > asm/imsic.h > as it is not used as parameter type. > - Align declaration of imisic_init with defintion. > - s/harts/cpus in imisic_mmios. > Also, change type from bool harts[NR_CPUS] to unsigned long *cpus. > - Allocate msi member of imsic_config dynamically to save some memory. > - Code style fixes. > - Update the commit message. > --- > xen/arch/riscv/Makefile | 1 + > xen/arch/riscv/imsic.c | 286 +++++++++++++++++++++++++++++ > xen/arch/riscv/include/asm/imsic.h | 65 +++++++ > 3 files changed, 352 insertions(+) > create mode 100644 xen/arch/riscv/imsic.c > create mode 100644 xen/arch/riscv/include/asm/imsic.h > > 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..43d0c92cbd > --- /dev/null > +++ b/xen/arch/riscv/imsic.c > @@ -0,0 +1,286 @@ > +/* SPDX-License-Identifier: MIT */ > + > +/* > + * xen/arch/riscv/imsic.c > + * > + * RISC-V Incoming MSI Controller support > + * > + * (c) Microchip Technology Inc. > + * (c) Vates > + */ > + > +#include <xen/const.h> > +#include <xen/device_tree.h> > +#include <xen/errno.h> > +#include <xen/init.h> > +#include <xen/macros.h> > +#include <xen/xmalloc.h> > + > +#include <asm/imsic.h> > + > +static struct imsic_config imsic_cfg; > + > +/* Callers aren't expected to changed imsic_cfg so return const. */ > +const struct imsic_config *imsic_get_config(void) > +{ > + return &imsic_cfg; > +} > + > +static int __init imsic_get_parent_cpuid(const struct dt_device_node *node, > + unsigned int index, > + unsigned long *cpuid) > +{ > + 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_cpuid(args.np->parent, cpuid); > + > + return res; > +} > + > +static int imsic_parse_node(const struct dt_device_node *node, > + unsigned int *nr_parent_irqs) > +{ > + int rc; > + unsigned int tmp; > + paddr_t base_addr; > + > + /* Find number of parent interrupts */ > + *nr_parent_irqs = dt_number_of_irq(node); > + if ( !*nr_parent_irqs ) > + { > + printk(XENLOG_ERR "%s: no parent irqs available\n", node->name); > + return -ENOENT; > + } > + > + 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", node->name); > + 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); > + if ( BIT(imsic_cfg.hart_index_bits, UL) < *nr_parent_irqs ) > + imsic_cfg.hart_index_bits++; > + } > + tmp -= imsic_cfg.guest_index_bits; > + if ( tmp < imsic_cfg.hart_index_bits ) > + { > + printk(XENLOG_ERR "%s: HART index bits too big\n", node->name); > + 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", node->name); > + 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", node->name); > + 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", node->name); > + return -EINVAL; > + } > + > + /* 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) || > + ((imsic_cfg.nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID) ) > + { > + printk(XENLOG_ERR "%s: invalid number of interrupt identities\n", > + node->name); > + return -EINVAL; > + } > + > + /* Compute base address */ > + imsic_cfg.nr_mmios = 0; > + rc = dt_device_get_address(node, imsic_cfg.nr_mmios, &base_addr, NULL); > + if ( rc ) > + { > + printk(XENLOG_ERR "%s: first MMIO resource not found\n", node->name); > + 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); Nit: indentation, similarly ... > + imsic_cfg.base_addr &= ~((BIT(imsic_cfg.group_index_bits, UL) - 1) << > + imsic_cfg.group_index_shift); ... here. > + /* Find number of MMIO register sets */ > + do { > + imsic_cfg.nr_mmios++; > + } while ( !dt_device_get_address(node, imsic_cfg.nr_mmios, &base_addr, > NULL) ); > + > + return 0; > +} > + > +int __init imsic_init(const struct dt_device_node *node) > +{ > + int rc; > + unsigned long reloff, cpuid; > + uint32_t nr_parent_irqs, index, nr_handlers = 0; I can't spot why unsigned int wouldn't be suitable here. In fact e.g. ... > + paddr_t base_addr; > + unsigned int nr_mmios; > + > + /* Parse IMSIC node */ > + rc = imsic_parse_node(node, &nr_parent_irqs); ... this one wants to yield unsigned int * according to the function parameter's type. > + if ( rc ) > + return rc; > + > + nr_mmios = imsic_cfg.nr_mmios; > + > + /* Allocate MMIO resource array */ > + imsic_cfg.mmios = xzalloc_array(struct imsic_mmios, nr_mmios); > + if ( !imsic_cfg.mmios ) > + return -ENOMEM; > + > + imsic_cfg.msi = xzalloc_array(struct imsic_msi, nr_parent_irqs); > + if ( !imsic_cfg.msi ) > + return -ENOMEM; Leaking the earlier successful allocation? > + /* Check MMIO register sets */ > + for ( unsigned int i = 0; i < nr_mmios; i++ ) > + { > + imsic_cfg.mmios[i].cpus = xzalloc_array(unsigned long, > + > BITS_TO_LONGS(nr_parent_irqs)); > + if ( !imsic_cfg.mmios[i].cpus ) > + return -ENOMEM; Leaking all earlier successful allocations? > + rc = dt_device_get_address(node, i, &imsic_cfg.mmios[i].base_addr, > + &imsic_cfg.mmios[i].size); > + if ( rc ) > + { > + printk(XENLOG_ERR "%s: unable to parse MMIO regset %d\n", Nit: Excess blank. > + node->name, i); > + goto imsic_init_err; > + } > + > + base_addr = imsic_cfg.mmios[i].base_addr; > + base_addr &= ~(BIT(imsic_cfg.guest_index_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); As above, indentation again. > + if ( base_addr != imsic_cfg.base_addr ) > + { > + rc = -EINVAL; > + printk(XENLOG_ERR "%s: address mismatch for regset %d\n", > + node->name, i); > + goto imsic_init_err; > + } > + } > + > + /* Configure handlers for target CPUs */ > + for ( unsigned int i = 0; i < nr_parent_irqs; i++ ) > + { > + rc = imsic_get_parent_cpuid(node, i, &cpuid); Coming back to a comment I gave on the respective earlier patch: What you get back here is a DT value, aiui. There's no translation to Xen CPU numbering space, as would be required for e.g. ... > + if ( rc ) > + { > + printk(XENLOG_WARNING "%s: cpu ID for parent irq%d not found\n", > + node->name, i); > + continue; > + } > + > + if ( cpuid >= num_possible_cpus() ) ... this. Are you using DT numbering without any translation, no matter that it (I suppose) could be very sparse? > + { > + printk(XENLOG_WARNING "%s: unsupported cpu ID=%lu for parent > irq%d\n", > + node->name, cpuid, i); Nit: i is unsigned int, so wants formatting with %u (also applicable elsewhere). > + continue; > + } > + > + /* Find MMIO location of MSI page */ > + index = nr_mmios; > + reloff = i * BIT(imsic_cfg.guest_index_bits, UL) * > IMSIC_MMIO_PAGE_SZ; > + for ( unsigned int j = 0; nr_mmios; j++ ) DYM "j < nr_mmios"? > + { > + if ( reloff < imsic_cfg.mmios[j].size ) > + { > + index = j; > + break; > + } > + > + /* > + * MMIO region size may not be aligned to > + * BIT(global->guest_index_bits) * IMSIC_MMIO_PAGE_SZ > + * if holes are present. > + */ > + reloff -= ROUNDUP(imsic_cfg.mmios[j].size, > + BIT(imsic_cfg.guest_index_bits, UL) * IMSIC_MMIO_PAGE_SZ); > + } > + > + if ( index >= nr_mmios ) Why is it that you need both "index" and "j"? > + { > + printk(XENLOG_WARNING "%s: MMIO not found for parent irq%d\n", > + node->name, i); > + continue; > + } > + > + if ( !IS_ALIGNED(imsic_cfg.msi[cpuid].base_addr + reloff, PAGE_SIZE) > ) > + { > + printk(XENLOG_WARNING "%s: MMIO address 0x%lx is not aligned on > a page\n", Please prefer to use %#lx, as we do elsewhere. > + node->name, imsic_cfg.msi[cpuid].base_addr + reloff); > + imsic_cfg.msi[cpuid].offset = 0; > + imsic_cfg.msi[cpuid].base_addr = 0; > + continue; > + } > + > + bitmap_set(imsic_cfg.mmios[index].cpus, cpuid, 1); Depending on clarification on the number space used, this may want to be cpumask_set_cpu() instead. Else I think this is simply __set_bit(). > + imsic_cfg.msi[cpuid].base_addr = imsic_cfg.mmios[index].base_addr; > + imsic_cfg.msi[cpuid].offset = reloff; How come it's cpuid that's used as array index here? Shouldn't this be i, seeing that the array allocation is using nr_parent_irqs? > + nr_handlers++; > + } > + > + if ( !nr_handlers ) > + { > + printk(XENLOG_ERR "%s: No CPU handlers found\n", node->name); > + rc = -ENODEV; > + goto imsic_init_err; > + } > + > + return 0; > + > + imsic_init_err: > + for ( unsigned int i = 0; i < nr_mmios; i++ ) > + XFREE(imsic_cfg.mmios[i].cpus); This can be just xfree(), as the array itself ... > + XFREE(imsic_cfg.mmios); ... is then also freed. > + XFREE(imsic_cfg.msi); > + > + return rc; > +} > --- /dev/null > +++ b/xen/arch/riscv/include/asm/imsic.h > @@ -0,0 +1,65 @@ > +/* SPDX-License-Identifier: MIT */ > + > +/* > + * xen/arch/riscv/imsic.h > + * > + * RISC-V Incoming MSI Controller support > + * > + * (c) 2023 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 This isn't the "minimum ID", but the "minimum permissible number of IDs" as per its first use in imsic_parse_node(). Further uses there consider it a mask, which makes me wonder whether the imsic_cfg.nr_ids field name is actually correct: Isn't this the maximum valid ID rather than their count/number? Jan
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |