diff --git a/Documentation/devicetree/bindings/arm/firmware/linaro,optee-tz.yaml b/Documentation/devicetree/bindings/arm/firmware/linaro,optee-tz.yaml index d4dc0749f9fd1f..ff7e1292c803bc 100644 --- a/Documentation/devicetree/bindings/arm/firmware/linaro,optee-tz.yaml +++ b/Documentation/devicetree/bindings/arm/firmware/linaro,optee-tz.yaml @@ -40,6 +40,16 @@ properties: HVC #0, register assignments register assignments are specified in drivers/tee/optee/optee_smc.h + interrupt-controller: true + + "#interrupt-cells": + const: 1 + description: | + OP-TEE exposes irq for irp chip controllers from OP-TEE world. Each + irq is assigned a single line number identifier used as first argument. + Line number identifiers and their meaning shall be found in the OP-TEE + firmware platform documentation. + required: - compatible - method @@ -64,3 +74,31 @@ examples: method = "hvc"; }; }; + + - | + #include + firmware { + optee: optee { + compatible = "linaro,optee-tz"; + method = "smc"; + interrupt-parent = <&gic>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <1>; + }; + + scmi { + compatible = "linaro,scmi-optee"; + linaro,optee-channel-id = <0>; + shmem = <&scmi_shm_tx>, <&scmi_shm_rx>; + interrupts-extended = <&optee 5>; + interrupt-names = "a2p"; + #address-cells = <1>; + #size-cells = <0>; + + scmi_cpu_dvfs: protocol@13 { + reg = <0x13>; + #clock-cells = <1>; + }; + }; + }; diff --git a/arch/arm/boot/dts/stm32mp151.dtsi b/arch/arm/boot/dts/stm32mp151.dtsi index 5491b6c4dec203..97dfae09c09edb 100644 --- a/arch/arm/boot/dts/stm32mp151.dtsi +++ b/arch/arm/boot/dts/stm32mp151.dtsi @@ -35,6 +35,14 @@ method = "smc"; }; + firmware { + optee: optee { + compatible = "linaro,optee-tz"; + method = "smc"; + status = "disabled"; + }; + }; + intc: interrupt-controller@a0021000 { compatible = "arm,cortex-a7-gic"; #interrupt-cells = <3>; diff --git a/arch/arm/boot/dts/stm32mp157c-dk2.dts b/arch/arm/boot/dts/stm32mp157c-dk2.dts index 2bc92ef3aeb957..60075aa1d6a85d 100644 --- a/arch/arm/boot/dts/stm32mp157c-dk2.dts +++ b/arch/arm/boot/dts/stm32mp157c-dk2.dts @@ -16,6 +16,13 @@ model = "STMicroelectronics STM32MP157C-DK2 Discovery Board"; compatible = "st,stm32mp157c-dk2", "st,stm32mp157"; + reserved-memory { + optee_memory: optee@0xde000000 { + reg = <0xde000000 0x02000000>; + no-map; + }; + }; + aliases { ethernet0 = ðernet0; serial0 = &uart4; @@ -99,3 +106,7 @@ pinctrl-2 = <&usart2_idle_pins_c>; status = "disabled"; }; + +&optee { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/stm32mp157c-ed1.dts b/arch/arm/boot/dts/stm32mp157c-ed1.dts index b1eb688a278ac9..88886c1d190e7b 100644 --- a/arch/arm/boot/dts/stm32mp157c-ed1.dts +++ b/arch/arm/boot/dts/stm32mp157c-ed1.dts @@ -70,6 +70,11 @@ reg = <0xe8000000 0x8000000>; no-map; }; + + optee_memory: optee@fe000000 { + reg = <0xfe000000 0x2000000>; + no-map; + }; }; aliases { @@ -320,6 +325,10 @@ status = "okay"; }; +&optee { + status = "okay"; +}; + &pwr_regulators { vdd-supply = <&vdd>; vdd_3v3_usbfs-supply = <&vdd_usb>; diff --git a/arch/arm/boot/dts/stm32mp15xx-dhcom-som.dtsi b/arch/arm/boot/dts/stm32mp15xx-dhcom-som.dtsi index 002f221f1694c7..1accb5cf9bced3 100644 --- a/arch/arm/boot/dts/stm32mp15xx-dhcom-som.dtsi +++ b/arch/arm/boot/dts/stm32mp15xx-dhcom-som.dtsi @@ -61,6 +61,11 @@ reg = <0x38000000 0x10000>; no-map; }; + + optee_memory: optee@fe000000 { + reg = <0xfe000000 0x2000000>; + no-map; + }; }; ethernet_vio: vioregulator { @@ -421,6 +426,10 @@ status = "okay"; }; +&optee { + status = "okay"; +}; + &pwr_regulators { vdd-supply = <&vdd>; vdd_3v3_usbfs-supply = <&vdd_usb>; diff --git a/arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi b/arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi index 134a798ad3f23f..65ae46e891da15 100644 --- a/arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi +++ b/arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi @@ -60,6 +60,11 @@ reg = <0x38000000 0x10000>; no-map; }; + + optee_memory: optee@fe000000 { + reg = <0xfe000000 0x2000000>; + no-map; + }; }; }; @@ -240,6 +245,10 @@ status = "okay"; }; +&optee { + status = "okay"; +}; + &pwr_regulators { vdd-supply = <&vdd>; vdd_3v3_usbfs-supply = <&vdd_usb>; diff --git a/arch/arm64/boot/dts/arm/foundation-v8.dtsi b/arch/arm64/boot/dts/arm/foundation-v8.dtsi index 029578072d8fb4..e8ef75fab03cf0 100644 --- a/arch/arm64/boot/dts/arm/foundation-v8.dtsi +++ b/arch/arm64/boot/dts/arm/foundation-v8.dtsi @@ -22,11 +22,14 @@ aliases { serial0 = &v2m_serial0; - serial1 = &v2m_serial1; serial2 = &v2m_serial2; serial3 = &v2m_serial3; }; + ftpm { + compatible = "microsoft,ftpm"; + }; + cpus { #address-cells = <2>; #size-cells = <0>; @@ -68,6 +71,17 @@ <0x00000008 0x80000000 0 0x80000000>; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + optee@0x83000000 { + reg = <0x00000000 0x83000000 0 0x01000000>; + no-map; + }; + }; + timer { compatible = "arm,armv8-timer"; interrupts = , @@ -202,14 +216,6 @@ clock-names = "uartclk", "apb_pclk"; }; - v2m_serial1: serial@a0000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0x0a0000 0x1000>; - interrupts = <6>; - clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; - clock-names = "uartclk", "apb_pclk"; - }; - v2m_serial2: serial@b0000 { compatible = "arm,pl011", "arm,primecell"; reg = <0x0b0000 0x1000>; @@ -233,4 +239,12 @@ }; }; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; + }; diff --git a/arch/arm64/boot/dts/arm/fvp-base-revc.dts b/arch/arm64/boot/dts/arm/fvp-base-revc.dts index 60472d65a35574..7be12731c38d98 100644 --- a/arch/arm64/boot/dts/arm/fvp-base-revc.dts +++ b/arch/arm64/boot/dts/arm/fvp-base-revc.dts @@ -183,6 +183,11 @@ reg = <0x00000000 0x18000000 0 0x00800000>; no-map; }; + + optee@83000000 { + reg = <0x00000000 0x83000000 0 0x01000000>; + no-map; + }; }; gic: interrupt-controller@2f000000 { @@ -317,4 +322,11 @@ <0 0 44 &gic 0 0 GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>, <0 0 46 &gic 0 0 GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; }; diff --git a/arch/arm64/boot/dts/arm/juno-base.dtsi b/arch/arm64/boot/dts/arm/juno-base.dtsi index 8b4d280b1e7e78..5e5083078fe236 100644 --- a/arch/arm64/boot/dts/arm/juno-base.dtsi +++ b/arch/arm64/boot/dts/arm/juno-base.dtsi @@ -967,6 +967,18 @@ <0x00000008 0x80000000 0x1 0x80000000>; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* Shared memory between secure and non-secure world */ + optee@0xfee00000 { + reg = <0x00000000 0xfee00000 0 0x00200000>; + no-map; + }; + }; + bus@8000000 { #interrupt-cells = <1>; interrupt-map-mask = <0 0 15>; @@ -994,4 +1006,11 @@ interrupt-map-mask = <0 0>; interrupt-map = <0 0 &gic 0 GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; }; diff --git a/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts b/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts index 7d370dac4c8571..3bb161655313c4 100644 --- a/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts +++ b/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts @@ -6,6 +6,7 @@ */ /dts-v1/; +/memreserve/ 0x00000000 0x04080000; #include #include "hi3798cv200.dtsi" @@ -70,6 +71,13 @@ gpio = <&gpio6 7 0>; enable-active-high; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; }; &ehci { diff --git a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts index 0b5f154007be8f..b27e7ae7bcb4d8 100644 --- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts +++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts @@ -60,6 +60,13 @@ gpio = <&pio 9 GPIO_ACTIVE_HIGH>; enable-active-high; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; }; &mfg_async { diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi index 7640b5158ff9d3..dccfc1f6baf53d 100644 --- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi @@ -526,15 +526,6 @@ reg = <0 0x10007000 0 0x100>; }; - timer: timer@10008000 { - compatible = "mediatek,mt8173-timer", - "mediatek,mt6577-timer"; - reg = <0 0x10008000 0 0x1000>; - interrupts = ; - clocks = <&infracfg CLK_INFRA_CLK_13M>, - <&topckgen CLK_TOP_RTC_SEL>; - }; - pwrap: pwrap@1000d000 { compatible = "mediatek,mt8173-pwrap"; reg = <0 0x1000d000 0 0x1000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dtsi index 1f76d3501bda31..d849bd93fa7572 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dtsi @@ -167,6 +167,24 @@ regulator-min-microvolt = <800000>; regulator-max-microvolt = <1400000>; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + optee@30000000 { + reg = <0x0 0x30000000 0x0 0x2400000>; + no-map; + }; + }; }; &cpu_l0 { @@ -765,7 +783,7 @@ }; &usbdrd_dwc3_0 { - status = "okay"; + status = "disabled"; dr_mode = "host"; }; diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 04ae58892608c6..f467409e02e91d 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -94,11 +94,21 @@ struct optee_supp { struct completion reqs_c; }; +/* + * struct optee_smc - optee smc communication struct + * @invoke_fn handler function to invoke secure monitor + * @memremaped_shm virtual address of memory in shared memory pool + * @sec_caps: secure world capabilities defined by + * OPTEE_SMC_SEC_CAP_* in optee_smc.h + * @notif_irq interrupt used as async notification by OP-TEE or 0 + * @domain interrupt domain registered by OP-TEE driver + */ struct optee_smc { optee_invoke_fn *invoke_fn; void *memremaped_shm; u32 sec_caps; unsigned int notif_irq; + struct irq_domain *domain; }; /** diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index 73b5e7760d102d..f2aa45dfe1d246 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -226,7 +226,8 @@ struct optee_smc_get_shm_config_result { * a3 Bit[7:0]: Number of parameters needed for RPC to be supplied * as the second MSG arg struct for * OPTEE_SMC_CALL_WITH_ARG - * Bit[31:8]: Reserved (MBZ) + * Bit[23:8]: The maximum interrupt event notification number + * Bit[31:24]: Reserved (MBZ) * a4-7 Preserved * * Error return register usage: @@ -254,6 +255,11 @@ struct optee_smc_get_shm_config_result { #define OPTEE_SMC_SEC_CAP_ASYNC_NOTIF BIT(5) /* Secure world supports pre-allocating RPC arg struct */ #define OPTEE_SMC_SEC_CAP_RPC_ARG BIT(6) +/* Secure world supports interrupt events notification to normal world */ +#define OPTEE_SMC_SEC_CAP_ITR_NOTIF BIT(7) + +#define OPTEE_SMC_SEC_CAP_MAX_NOTIF_ITR_MASK GENMASK(23, 8) +#define OPTEE_SMC_SEC_CAP_MAX_NOTIF_ITR_SHIFT 8 #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ @@ -426,6 +432,131 @@ struct optee_smc_disable_shm_cache_result { /* See OPTEE_SMC_CALL_WITH_REGD_ARG above */ #define OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG 19 +/* + * Retrieve up to 5 pending interrupt events notified by OP-TEE world, + * whether bottom half is to be scheduled and if there are pending + * async event for waiting threads, all this since the last call of + * this function. + * + * Interrupts notified by OP-TEE are identified by a number from 0 to + * the interrupt number max value for that platform. Values for each + * interrupt number are platform specific and shall be found in the + * OP-TEE platform documentation. + * + * OP-TEE keeps a record of all posted interrupt notification events. + * When the async notif interrupt is received by normal world, + * this function should be called until all pended interrupt events + * have been retrieved. When an interrupt event is retrieved, it is + * cleared from the record in OP-TEE world. When do bottom half event + * is retrieved (async value 0), it is also cleared from its related + * record in OP-TEE world. + * + * It is expected that this function is called from an interrupt handler + * in normal world. + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_GET_NOTIF_ITR + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 Bit[7:0]: Number of pending interrupt carried in a1..a5 + * Bit[8]: OPTEE_SMC_NOTIF_ITR_PENDING if other interrupt(s) are pending + * Bit[9]: OPTEE_SMC_NOTIF_ASYNC_PENDING if a threaded event is pending + * excluding bottom half notification that is retrieved in Bit[10]. + * Bit[10]: OPTEE_SMC_NOTIF_DO_BOTTOM_HALF if retrieved bottom half notif + * Bit[15:11]: Reserved for future use, MBZ + * Bit[31:16]: Pending interrupt line value if a1 & 0xFF >= 1 + * a2 Bit[15:0]: Pending interrupt line value if a1 & 0xFF >= 2 + * Bit[31:16]: Pending interrupt line value if a1 & 0xFF >= 3 + * a3 Bit[15:0]: Pending interrupt line value if a1 & 0xFF >= 4 + * Bit[31:16]: Pending interrupt line value if a1 & 0xFF == 5 + * a4-7 Preserved + * + * Not supported return register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL + * a1-7 Preserved + */ +#define OPTEE_SMC_NOTIF_ITR_COUNT_MASK GENMASK(7, 0) +#define OPTEE_SMC_NOTIF_ITR_PENDING BIT(8) +#define OPTEE_SMC_NOTIF_VALUE_PENDING BIT(9) +#define OPTEE_SMC_NOTIF_DO_BOTTOM_HALF BIT(10) + +#define OPTEE_SMC_FUNCID_GET_NOTIF_ITR 20 +#define OPTEE_SMC_GET_NOTIF_ITR \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_NOTIF_ITR) + +/* + * Mask/unmask an interrupt notification + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_NOTIF_ITR_SET_MASK + * a1 Interrupt number identifier value + * a2 1 to mask, 0 to unmask the interrupt notification. + * a3-6 Reserved for future use, MBZ + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1-7 Preserved + * + * Invalid command with provided arguments return usage: + * a0 OPTEE_SMC_RETURN_EBADCMD + * a1-7 Preserved + * + * Not supported return register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL + * a1-7 Preserved + */ +#define OPTEE_SMC_FUNCID_NOTIF_ITR_SET_MASK 21 +#define OPTEE_SMC_NOTIF_ITR_SET_MASK \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_NOTIF_ITR_SET_MASK) + +/* + * Enable/disable an interrupt notification + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_NOTIF_ITR_SET_STATE + * a1 Interrupt number identifier value + * a2 1 to enable, 0 to disable the interrupt notification + * a3-6 Reserved for future use, MBZ + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1-7 Preserved + * + * Invalid command with provided arguments return usage: + * a0 OPTEE_SMC_RETURN_EBADCMD + * a1-7 Preserved + */ +#define OPTEE_SMC_FUNCID_NOTIF_ITR_SET_STATE 22 +#define OPTEE_SMC_NOTIF_ITR_SET_STATE \ + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_NOTIF_ITR_SET_STATE) + +/* + * Enable/disable the wake up from low power feature of an interrupt event + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_NOTIF_ITR_SET_WAKEUP + * a1 Interrupt number identifier value + * a2 1 to enable, 0 to disable the interrupt wake up capability + * a3-6 Reserved for future use, MBZ + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1-7 Preserved + * + * Invalid command with provided arguments return usage: + * a0 OPTEE_SMC_RETURN_EBADCMD + * a1-7 Preserved + */ +#define OPTEE_SMC_FUNCID_NOTIF_ITR_SET_WAKEUP 23 +#define OPTEE_SMC_NOTIF_ITR_SET_WAKEUP \ + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_NOTIF_ITR_SET_WAKEUP) + /* * Resume from RPC (for example after processing a foreign interrupt) * diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index a1c1fa1a9c28a7..f90816df36994f 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -977,6 +977,99 @@ static int optee_smc_stop_async_notif(struct tee_context *ctx) * 5. Asynchronous notification */ +static void optee_itr_notif_mask(struct irq_data *d) +{ + struct optee *optee = d->domain->host_data; + struct arm_smccc_res res = { }; + + optee->smc.invoke_fn(OPTEE_SMC_NOTIF_ITR_SET_MASK, d->hwirq, 1, + 0, 0, 0, 0, 0, &res); +} + +static void optee_itr_notif_unmask(struct irq_data *d) +{ + struct optee *optee = d->domain->host_data; + struct arm_smccc_res res = { }; + + optee->smc.invoke_fn(OPTEE_SMC_NOTIF_ITR_SET_MASK, d->hwirq, 0, + 0, 0, 0, 0, 0, &res); +} + +static void optee_itr_notif_enable(struct irq_data *d) +{ + struct optee *optee = d->domain->host_data; + struct arm_smccc_res res; + + optee->smc.invoke_fn(OPTEE_SMC_NOTIF_ITR_SET_STATE, d->hwirq, 1, + 0, 0, 0, 0, 0, &res); +} + +static void optee_itr_notif_disable(struct irq_data *d) +{ + struct optee *optee = d->domain->host_data; + struct arm_smccc_res res; + + optee->smc.invoke_fn(OPTEE_SMC_NOTIF_ITR_SET_STATE, d->hwirq, 0, + 0, 0, 0, 0, 0, &res); +} + +static int optee_itr_notif_set_wake(struct irq_data *d, unsigned int on) +{ + struct optee *optee = d->domain->host_data; + struct arm_smccc_res res; + + optee->smc.invoke_fn(OPTEE_SMC_NOTIF_ITR_SET_WAKEUP, d->hwirq, !!on, + 0, 0, 0, 0, 0, &res); + + if (res.a0) + return -EINVAL; + return 0; +} + +static struct irq_chip optee_irq_chip = { + .name = "optee-itr-notif", + .irq_mask = optee_itr_notif_mask, + .irq_unmask = optee_itr_notif_unmask, + .irq_disable = optee_itr_notif_disable, + .irq_enable = optee_itr_notif_enable, + .irq_set_wake = optee_itr_notif_set_wake, +}; + +static int optee_itr_alloc(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct irq_fwspec *fwspec = data; + irq_hw_number_t hwirq; + + hwirq = fwspec->param[0]; + + irq_domain_set_hwirq_and_chip(d, virq, hwirq, &optee_irq_chip, + d->host_data); + + return 0; +} + +static const struct irq_domain_ops optee_irq_domain_ops = { + .alloc = optee_itr_alloc, + .free = irq_domain_free_irqs_common, +}; + +static int optee_irq_domain_init(struct platform_device *pdev, + struct optee *optee, u_int max_it) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + optee->smc.domain = irq_domain_add_linear(np, max_it, + &optee_irq_domain_ops, optee); + if (!optee->smc.domain) { + dev_err(dev, "Unable to add irq domain\n"); + return -ENOMEM; + } + + return 0; +} + static u32 get_async_notif_value(optee_invoke_fn *invoke_fn, bool *value_valid, bool *value_pending) { @@ -991,6 +1084,61 @@ static u32 get_async_notif_value(optee_invoke_fn *invoke_fn, bool *value_valid, return res.a1; } +static void forward_irq(struct optee *optee, unsigned int itr_num) +{ + if (generic_handle_domain_irq(optee->smc.domain, itr_num)) { + struct arm_smccc_res res = { }; + + pr_err("No consumer for optee irq %u, masked\n", itr_num); + optee->smc.invoke_fn(OPTEE_SMC_NOTIF_ITR_SET_MASK, itr_num, 1, + 0, 0, 0, 0, 0, &res); + } +} + +static void retrieve_pending_irqs(struct optee *optee, bool *async_pending, + bool *do_bottom_half) + +{ + struct arm_smccc_res res; + bool irq_pending; + ssize_t cnt; + const unsigned int lsb_mask = GENMASK(15, 0); + const unsigned int msb_shift = 16; + + *do_bottom_half = false; + + do { + optee->smc.invoke_fn(OPTEE_SMC_GET_NOTIF_ITR, 0, 0, 0, 0, 0, 0, + 0, &res); + + if (res.a0) + return; + + if (res.a1 & OPTEE_SMC_NOTIF_DO_BOTTOM_HALF) + *do_bottom_half = true; + + irq_pending = res.a1 & OPTEE_SMC_NOTIF_ITR_PENDING; + cnt = res.a1 & OPTEE_SMC_NOTIF_ITR_COUNT_MASK; + if (cnt > 5 || (!cnt && irq_pending)) { + WARN_ONCE(0, "Unexpected irq notif count %zi\n", cnt); + break; + } + + if (cnt > 0) + forward_irq(optee, res.a1 >> msb_shift); + if (cnt > 1) + forward_irq(optee, res.a2 & lsb_mask); + if (cnt > 2) + forward_irq(optee, res.a2 >> msb_shift); + if (cnt > 3) + forward_irq(optee, res.a3 & lsb_mask); + if (cnt == 5) + forward_irq(optee, res.a3 >> msb_shift); + } while (irq_pending); + + *async_pending = res.a1 & OPTEE_SMC_NOTIF_VALUE_PENDING; +} + static irqreturn_t notif_irq_handler(int irq, void *dev_id) { struct optee *optee = dev_id; @@ -999,9 +1147,14 @@ static irqreturn_t notif_irq_handler(int irq, void *dev_id) bool value_pending; u32 value; - do { - value = get_async_notif_value(optee->smc.invoke_fn, - &value_valid, &value_pending); + if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_ITR_NOTIF) + retrieve_pending_irqs(optee, &value_pending, &do_bottom_half); + else + value_pending = true; + + while (value_pending) { + value = get_async_notif_value(optee->smc.invoke_fn, &value_valid, + &value_pending); if (!value_valid) break; @@ -1009,10 +1162,11 @@ static irqreturn_t notif_irq_handler(int irq, void *dev_id) do_bottom_half = true; else optee_notif_send(optee, value); - } while (value_pending); + }; if (do_bottom_half) return IRQ_WAKE_THREAD; + return IRQ_HANDLED; } @@ -1048,6 +1202,9 @@ static void optee_smc_notif_uninit_irq(struct optee *optee) free_irq(optee->smc.notif_irq, optee); irq_dispose_mapping(optee->smc.notif_irq); } + + if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_ITR_NOTIF) + irq_domain_remove(optee->smc.domain); } } @@ -1187,6 +1344,7 @@ static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, u32 *sec_caps, u32 *max_notif_value, + u32 *max_notif_it, unsigned int *rpc_param_count) { union { @@ -1219,6 +1377,13 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, else *rpc_param_count = 0; + if (*sec_caps & OPTEE_SMC_SEC_CAP_ITR_NOTIF) + *max_notif_it = (res.result.data & + OPTEE_SMC_SEC_CAP_MAX_NOTIF_ITR_MASK) >> + OPTEE_SMC_SEC_CAP_MAX_NOTIF_ITR_SHIFT; + else + *max_notif_it = 0; + return true; } @@ -1364,6 +1529,7 @@ static int optee_probe(struct platform_device *pdev) struct tee_device *teedev; struct tee_context *ctx; u32 max_notif_value; + u32 max_notif_it; u32 arg_cache_flags; u32 sec_caps; int rc; @@ -1385,7 +1551,7 @@ static int optee_probe(struct platform_device *pdev) } if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps, - &max_notif_value, + &max_notif_value, &max_notif_it, &rpc_param_count)) { pr_warn("capabilities mismatch\n"); return -EINVAL; @@ -1506,6 +1672,16 @@ static int optee_probe(struct platform_device *pdev) irq_dispose_mapping(irq); goto err_notif_uninit; } + + if (sec_caps & OPTEE_SMC_SEC_CAP_ITR_NOTIF) { + rc = optee_irq_domain_init(pdev, optee, max_notif_it); + if (rc) { + free_irq(optee->smc.notif_irq, optee); + irq_dispose_mapping(irq); + goto err_notif_uninit; + } + } + enable_async_notif(optee->smc.invoke_fn); pr_info("Asynchronous notifications enabled\n"); }