On 10/31/25 11:50 AM, Shameer Kolothum wrote:
> QEMU SMMUv3 currently sets the output address size (OAS) to 44 bits. With
> accelerator mode enabled, a guest device may use SVA where CPU page tables
> are shared with SMMUv3, requiring OAS at least equal to the CPU OAS. Add
> a user option to set this.
>
> Note: Linux kernel docs currently state the OAS field in the IDR register
> is not meaningful for users. But looks like we need this information.
I would explain why this is actually needed instead of quoting the linux
kernel docs. I guess in practice the vSMMU can't advertise an OAS
greater than the one support by the host SMMU otherwise the guest might
fail to use the range exposed by the vSMMU.
>
> Tested-by: Zhangfei Gao <zhangfei.gao@linaro.org>
> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com>
> ---
> hw/arm/smmuv3-accel.c | 22 ++++++++++++++++++++++
> hw/arm/smmuv3-internal.h | 3 ++-
> hw/arm/smmuv3.c | 16 +++++++++++++++-
> include/hw/arm/smmuv3.h | 1 +
> 4 files changed, 40 insertions(+), 2 deletions(-)
>
> diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c
> index 5b0ef3804a..c46510150e 100644
> --- a/hw/arm/smmuv3-accel.c
> +++ b/hw/arm/smmuv3-accel.c
> @@ -28,6 +28,12 @@ MemoryRegion root;
> MemoryRegion sysmem;
> static AddressSpace *shared_as_sysmem;
>
> +static int smmuv3_oas_bits(uint32_t oas)
> +{
> + static const int map[] = { 32, 36, 40, 42, 44, 48, 52, 56 };
> + return (oas < ARRAY_SIZE(map)) ? map[oas] : -EINVAL;
> +}
> +
> static bool
> smmuv3_accel_check_hw_compatible(SMMUv3State *s,
> struct iommu_hw_info_arm_smmuv3 *info,
> @@ -70,6 +76,18 @@ smmuv3_accel_check_hw_compatible(SMMUv3State *s,
> return false;
> }
>
> + /*
> + * TODO: OAS is not something Linux kernel doc says meaningful for user.
> + * But looks like OAS needs to be compatible for accelerator support. Please
> + * check.
would remove that comment. Either it is requested or not.
> + */
> + if (FIELD_EX32(info->idr[5], IDR5, OAS) <
> + FIELD_EX32(s->idr[5], IDR5, OAS)) {
> + error_setg(errp, "Host SMMUv3 OAS(%d) bits not compatible",
> + smmuv3_oas_bits(FIELD_EX32(info->idr[5], IDR5, OAS)));
let's be more explicit then and say
Host SMMUv3 OAS (%d bits) is less that OAS bits advertised by SMMU (%d)
> + return false;
> + }
> +
> /* QEMU SMMUv3 supports GRAN4K/GRAN16K/GRAN64K translation granules */
> if (FIELD_EX32(info->idr[5], IDR5, GRAN4K) !=
> FIELD_EX32(s->idr[5], IDR5, GRAN4K)) {
> @@ -649,6 +667,10 @@ void smmuv3_accel_idr_override(SMMUv3State *s)
> if (s->ats) {
> s->idr[0] = FIELD_DP32(s->idr[0], IDR0, ATS, 1); /* ATS */
> }
> + /* QEMU SMMUv3 has OAS set 44. Update IDR5 if user has it set to 48 bits*/
vSMMUv3 advertises by default a 44 bit wide OAS
> + if (s->oas == 48) {
> + s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, SMMU_IDR5_OAS_48);
> + }
> }
>
> /* Based on SMUUv3 GBPA configuration, attach a corresponding HWPT */
> diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h
> index 5fd88b4257..cfc5897569 100644
> --- a/hw/arm/smmuv3-internal.h
> +++ b/hw/arm/smmuv3-internal.h
> @@ -111,7 +111,8 @@ REG32(IDR5, 0x14)
> FIELD(IDR5, VAX, 10, 2);
> FIELD(IDR5, STALL_MAX, 16, 16);
>
> -#define SMMU_IDR5_OAS 4
> +#define SMMU_IDR5_OAS_44 4
> +#define SMMU_IDR5_OAS_48 5
>
> REG32(IIDR, 0x18)
> REG32(AIDR, 0x1c)
> diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
> index d95279a733..c4d28a3786 100644
> --- a/hw/arm/smmuv3.c
> +++ b/hw/arm/smmuv3.c
> @@ -299,7 +299,8 @@ static void smmuv3_init_id_regs(SMMUv3State *s)
> s->idr[3] = FIELD_DP32(s->idr[3], IDR3, RIL, 1);
> s->idr[3] = FIELD_DP32(s->idr[3], IDR3, BBML, 2);
>
> - s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, SMMU_IDR5_OAS); /* 44 bits */
> + /* OAS: 44 bits */
> + s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, SMMU_IDR5_OAS_44);
> /* 4K, 16K and 64K granule support */
> s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN4K, 1);
> s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN16K, 1);
> @@ -1961,6 +1962,15 @@ static bool smmu_validate_property(SMMUv3State *s, Error **errp)
> error_setg(errp, "ats can only be enabled if accel=on");
> return false;
> }
> + if (s->oas != 44) {
> + error_setg(errp, "OAS can only be set to 44 bits if accel=off");
> + return false;
> + }
> + return false;
> + }
> +
> + if (s->oas != 44 && s->oas != 48) {
> + error_setg(errp, "OAS can only be set to 44 or 48 bits");
> return false;
> }
> return true;
> @@ -2087,6 +2097,7 @@ static const Property smmuv3_properties[] = {
> /* RIL can be turned off for accel cases */
> DEFINE_PROP_BOOL("ril", SMMUv3State, ril, true),
> DEFINE_PROP_BOOL("ats", SMMUv3State, ats, false),
> + DEFINE_PROP_UINT8("oas", SMMUv3State, oas, 44),
> };
>
> static void smmuv3_instance_init(Object *obj)
> @@ -2119,6 +2130,9 @@ static void smmuv3_class_init(ObjectClass *klass, const void *data)
> object_class_property_set_description(klass, "ats",
> "Enable/disable ATS support (for accel=on). Please ensure host "
> "platform has ATS support before enabling this");
> + object_class_property_set_description(klass, "oas",
> + "Specify Output Address Size (for accel =on). Supported values "
> + "are 44 or 48 bits. Defaults to 44 bits");
> }
>
> static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu,
> diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h
> index 5fd5ec7b49..e4226b66f3 100644
> --- a/include/hw/arm/smmuv3.h
> +++ b/include/hw/arm/smmuv3.h
> @@ -70,6 +70,7 @@ struct SMMUv3State {
> Error *migration_blocker;
> bool ril;
> bool ats;
> + uint8_t oas;
> };
>
> typedef enum {