[PATCH] lib: utils: irqchip: handle IMSIC mask boundaries
Pengpeng Hou
pengpeng at iscas.ac.cn
Thu Aug 27 04:55:34 PDT 2026
imsic_data_check() accepts field geometries that reach the native word
width, but then constructs masks with 1UL << bits. A full-width low field
therefore shifts by BITS_PER_LONG. The group endpoint calculation also
underflows when group_index_bits is zero, even though an absent group field
has no shift semantics.
Build all geometry masks through a checked helper. Represent a full-width
field as ~0UL, keep zero-width fields as a zero mask, and validate the group
shift only when a group field exists. Reuse the resulting masks for base
canonicalization and regset-size alignment.
Fixes: 9f73669959dd ("lib: utils/irqchip: Add IMSIC library")
Signed-off-by: Pengpeng Hou <pengpeng at iscas.ac.cn>
---
Base-commit: 4e79fd7de59f1b2899092c1a84ce68c8ebc68f93
lib/utils/irqchip/imsic.c | 72 +++++++++++++++++++++++++++------------
1 file changed, 50 insertions(+), 22 deletions(-)
diff --git a/lib/utils/irqchip/imsic.c b/lib/utils/irqchip/imsic.c
index f6da9f7..5f6eed5 100644
--- a/lib/utils/irqchip/imsic.c
+++ b/lib/utils/irqchip/imsic.c
@@ -300,10 +300,30 @@ static int imsic_warm_irqchip_init(struct sbi_irqchip_device *dev)
return 0;
}
+static int imsic_make_field_mask(u32 bits, u32 shift,
+ unsigned long *mask)
+{
+ if (!bits) {
+ *mask = 0;
+ return 0;
+ }
+
+ if (shift >= BITS_PER_LONG || bits > BITS_PER_LONG - shift)
+ return SBI_EINVAL;
+
+ if (bits == BITS_PER_LONG)
+ *mask = ~0UL;
+ else
+ *mask = ((1UL << bits) - 1) << shift;
+
+ return 0;
+}
+
int imsic_data_check(struct imsic_data *imsic)
{
- u32 i, tmp;
- unsigned long base_addr, addr, mask;
+ u32 i, low_index_bits, tmp;
+ unsigned long addr, base_addr, group_mask, low_mask, regset_mask;
+ int rc;
/* Sanity checks */
if (!imsic ||
@@ -325,13 +345,29 @@ int imsic_data_check(struct imsic_data *imsic)
if (tmp < imsic->group_index_bits)
return SBI_EINVAL;
- tmp = IMSIC_MMIO_PAGE_SHIFT + imsic->guest_index_bits +
- imsic->hart_index_bits;
- if (imsic->group_index_shift < tmp)
- return SBI_EINVAL;
- tmp = imsic->group_index_bits + imsic->group_index_shift - 1;
- if (tmp >= BITS_PER_LONG)
- return SBI_EINVAL;
+ low_index_bits = IMSIC_MMIO_PAGE_SHIFT + imsic->guest_index_bits +
+ imsic->hart_index_bits;
+ rc = imsic_make_field_mask(low_index_bits, 0, &low_mask);
+ if (rc)
+ return rc;
+
+ rc = imsic_make_field_mask(imsic->guest_index_bits +
+ IMSIC_MMIO_PAGE_SHIFT, 0, ®set_mask);
+ if (rc)
+ return rc;
+
+ if (imsic->group_index_bits) {
+ if (imsic->group_index_shift < low_index_bits)
+ return SBI_EINVAL;
+
+ rc = imsic_make_field_mask(imsic->group_index_bits,
+ imsic->group_index_shift,
+ &group_mask);
+ if (rc)
+ return rc;
+ } else {
+ group_mask = 0;
+ }
/*
* Number of interrupt identities should be 1 less than
@@ -346,23 +382,15 @@ int imsic_data_check(struct imsic_data *imsic)
/* Match patter of each regset */
base_addr = imsic->regs[0].addr;
- base_addr &= ~((1UL << (imsic->guest_index_bits +
- imsic->hart_index_bits +
- IMSIC_MMIO_PAGE_SHIFT)) - 1);
- base_addr &= ~(((1UL << imsic->group_index_bits) - 1) <<
- imsic->group_index_shift);
+ base_addr &= ~low_mask;
+ base_addr &= ~group_mask;
for (i = 0; i < IMSIC_MAX_REGS && imsic->regs[i].size; i++) {
- mask = (1UL << imsic->guest_index_bits) * IMSIC_MMIO_PAGE_SZ;
- mask -= 1UL;
- if (imsic->regs[i].size & mask)
+ if (imsic->regs[i].size & regset_mask)
return SBI_EINVAL;
addr = imsic->regs[i].addr;
- addr &= ~((1UL << (imsic->guest_index_bits +
- imsic->hart_index_bits +
- IMSIC_MMIO_PAGE_SHIFT)) - 1);
- addr &= ~(((1UL << imsic->group_index_bits) - 1) <<
- imsic->group_index_shift);
+ addr &= ~low_mask;
+ addr &= ~group_mask;
if (base_addr != addr)
return SBI_EINVAL;
}
--
2.50.1 (Apple Git-155)
More information about the opensbi
mailing list