drivers/irqchip/irq-gic-v3-its.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
From: Arnd Bergmann <arnd@arndb.de>
On 32-bit machines with CONFIG_ARM_LPAE, it is possible for lowmem
allocations to be backed by addresses physical memory above the 32-bit
address limit, as I found while experimenting with larger VMSPLIT
configurations.
This caused the qemu virt model to crash in the GICv3 driver, which
allocates the 'itt' object using GFP_KERNEL. Since all memory below
the 4GB physical address limit is in ZONE_DMA in this configuration,
kmalloc defaults to higher addresses for ZONE_NORMAL, and the
its driver stores the physical address in a 32-bit 'unsigned long'
variable.
Change the itt_addr variable to the correct phys_addr_t type instead,
along with all other variables in this driver that hold a physical
address.
I checked the gicv5 driver for the same problem, and it correctly
uses u64 variables, while all other irqchip drivers don't call
virt_to_phys or similar interfaces. I expect other drivers to
have similar issues, but fixing this one is sufficient for
booting a virtio based guest.
Fixes: cc2d3216f53c ("irqchip: GICv3: ITS command queue")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
drivers/irqchip/irq-gic-v3-its.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index ada585bfa451..2988def30972 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -709,7 +709,7 @@ static struct its_collection *its_build_mapd_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
- unsigned long itt_addr;
+ phys_addr_t itt_addr;
u8 size = ilog2(desc->its_mapd_cmd.dev->nr_ites);
itt_addr = virt_to_phys(desc->its_mapd_cmd.dev->itt);
@@ -879,7 +879,7 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,
struct its_cmd_desc *desc)
{
struct its_vpe *vpe = valid_vpe(its, desc->its_vmapp_cmd.vpe);
- unsigned long vpt_addr, vconf_addr;
+ phys_addr_t vpt_addr, vconf_addr;
u64 target;
bool alloc;
@@ -2477,10 +2477,10 @@ static int its_setup_baser(struct its_node *its, struct its_baser *baser,
baser->psz = psz;
tmp = indirect ? GITS_LVL1_ENTRY_SIZE : esz;
- pr_info("ITS@%pa: allocated %d %s @%lx (%s, esz %d, psz %dK, shr %d)\n",
+ pr_info("ITS@%pa: allocated %d %s @%llx (%s, esz %d, psz %dK, shr %d)\n",
&its->phys_base, (int)(PAGE_ORDER_TO_SIZE(order) / (int)tmp),
its_base_type_string[type],
- (unsigned long)virt_to_phys(base),
+ (u64)virt_to_phys(base),
indirect ? "indirect" : "flat", (int)esz,
psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT);
--
2.39.5
On Mon, 19 Jan 2026 20:15:12 +0000,
Arnd Bergmann <arnd@kernel.org> wrote:
>
> From: Arnd Bergmann <arnd@arndb.de>
>
> On 32-bit machines with CONFIG_ARM_LPAE, it is possible for lowmem
> allocations to be backed by addresses physical memory above the 32-bit
> address limit, as I found while experimenting with larger VMSPLIT
> configurations.
>
> This caused the qemu virt model to crash in the GICv3 driver, which
> allocates the 'itt' object using GFP_KERNEL. Since all memory below
> the 4GB physical address limit is in ZONE_DMA in this configuration,
> kmalloc defaults to higher addresses for ZONE_NORMAL, and the
> its driver stores the physical address in a 32-bit 'unsigned long'
> variable.
>
> Change the itt_addr variable to the correct phys_addr_t type instead,
> along with all other variables in this driver that hold a physical
> address.
Ah, nice catch. It's amazing that we've managed for so long with such
a glaring bug. I guess most 32bit VMs don't have much memory above the
4GB limit.
>
> I checked the gicv5 driver for the same problem, and it correctly
> uses u64 variables,
GICv5 is strictly 64bit, and cannot be plugged on a system that
supports AArch32.
> while all other irqchip drivers don't call
> virt_to_phys or similar interfaces. I expect other drivers to
> have similar issues, but fixing this one is sufficient for
> booting a virtio based guest.
>
> Fixes: cc2d3216f53c ("irqchip: GICv3: ITS command queue")
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: stable@vger.kernel.org
Reviewed-by: Marc Zyngier <maz@kernel.org>
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
On Tue, Jan 20, 2026, at 09:51, Marc Zyngier wrote: > On Mon, 19 Jan 2026 20:15:12 +0000, Arnd Bergmann <arnd@kernel.org> wrote: >> >> Change the itt_addr variable to the correct phys_addr_t type instead, >> along with all other variables in this driver that hold a physical >> address. > > Ah, nice catch. It's amazing that we've managed for so long with such > a glaring bug. I guess most 32bit VMs don't have much memory above the > 4GB limit. Right, the upstream qemu model specifically has no lowmem above the limit, as its RAM starts as 0x40000000 and the most lowmem we support (with CONFIG_VMSPLIT_1G) is 3.75GiB, ending at 0xEFFFFFFF physical. There are some SoCs that start physical RAM higher up, but I suppose these were either never tested with CONFIG_VMSPLIT_1G, or they don't have a GICv3. The patch I'm testing with allows lowmem to be at physically discontinuous addresses, so this can happen on any SoC that has its second DRAM controller mapped to a high address, potentially even with the regular VMSPLIT_3G or VMSPLIT_2G. >> I checked the gicv5 driver for the same problem, and it correctly >> uses u64 variables, > > GICv5 is strictly 64bit, and cannot be plugged on a system that > supports AArch32. Right, makes sense. Arnd
The following commit has been merged into the irq/urgent branch of tip:
Commit-ID: 8d76a7d89c12d08382b66e2f21f20d0627d14859
Gitweb: https://git.kernel.org/tip/8d76a7d89c12d08382b66e2f21f20d0627d14859
Author: Arnd Bergmann <arnd@arndb.de>
AuthorDate: Mon, 19 Jan 2026 21:15:12 +01:00
Committer: Thomas Gleixner <tglx@kernel.org>
CommitterDate: Tue, 20 Jan 2026 10:11:29 +01:00
irqchip/gic-v3-its: Avoid truncating memory addresses
On 32-bit machines with CONFIG_ARM_LPAE, it is possible for lowmem
allocations to be backed by addresses physical memory above the 32-bit
address limit, as found while experimenting with larger VMSPLIT
configurations.
This caused the qemu virt model to crash in the GICv3 driver, which
allocates the 'itt' object using GFP_KERNEL. Since all memory below
the 4GB physical address limit is in ZONE_DMA in this configuration,
kmalloc() defaults to higher addresses for ZONE_NORMAL, and the
ITS driver stores the physical address in a 32-bit 'unsigned long'
variable.
Change the itt_addr variable to the correct phys_addr_t type instead,
along with all other variables in this driver that hold a physical
address.
The gicv5 driver correctly uses u64 variables, while all other irqchip
drivers don't call virt_to_phys or similar interfaces. It's expected that
other device drivers have similar issues, but fixing this one is
sufficient for booting a virtio based guest.
Fixes: cc2d3216f53c ("irqchip: GICv3: ITS command queue")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Reviewed-by: Marc Zyngier <maz@kernel.org>
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260119201603.2713066-1-arnd@kernel.org
---
drivers/irqchip/irq-gic-v3-its.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index ada585b..2988def 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -709,7 +709,7 @@ static struct its_collection *its_build_mapd_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
- unsigned long itt_addr;
+ phys_addr_t itt_addr;
u8 size = ilog2(desc->its_mapd_cmd.dev->nr_ites);
itt_addr = virt_to_phys(desc->its_mapd_cmd.dev->itt);
@@ -879,7 +879,7 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,
struct its_cmd_desc *desc)
{
struct its_vpe *vpe = valid_vpe(its, desc->its_vmapp_cmd.vpe);
- unsigned long vpt_addr, vconf_addr;
+ phys_addr_t vpt_addr, vconf_addr;
u64 target;
bool alloc;
@@ -2477,10 +2477,10 @@ retry_baser:
baser->psz = psz;
tmp = indirect ? GITS_LVL1_ENTRY_SIZE : esz;
- pr_info("ITS@%pa: allocated %d %s @%lx (%s, esz %d, psz %dK, shr %d)\n",
+ pr_info("ITS@%pa: allocated %d %s @%llx (%s, esz %d, psz %dK, shr %d)\n",
&its->phys_base, (int)(PAGE_ORDER_TO_SIZE(order) / (int)tmp),
its_base_type_string[type],
- (unsigned long)virt_to_phys(base),
+ (u64)virt_to_phys(base),
indirect ? "indirect" : "flat", (int)esz,
psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT);
© 2016 - 2026 Red Hat, Inc.