lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date:   Wed, 20 Jan 2021 18:11:31 -0600
From:   Jeremy Linton <jeremy.linton@....com>
To:     linux-arm-kernel@...ts.infradead.org
Cc:     catalin.marinas@....com, will@...nel.org, mark.rutland@....com,
        lorenzo.pieralisi@....com, sudeep.holla@....com,
        bhelgaas@...gle.com, robh@...nel.org, vidyas@...dia.com,
        linux-kernel@...r.kernel.org
Subject: Re: [PATCH v2] arm64: PCI: Enable SMC conduit

Hi,

On 1/13/21 4:40 PM, Jeremy Linton wrote:
> Given that most arm64 platforms' PCI implementations need quirks
> to deal with problematic config accesses, this is a good place
> to apply a firmware abstraction. The ARM PCI Configuration Space
> Access Firmware Interface specification details a standard SMC
> conduit designed to provide a simple PCI config accessor. This
> specification enhances the existing ACPI/PCI abstraction and
> expects power, config, etc., is handled by the platform. It also
> is very explicit that the resulting config space registers must
> behave as is specified by the PCI specification.
> 
> Hook the ACPI/PCI config path, and when missing MCFG data is
> detected, attempt to probe the SMC conduit. If the conduit
> exists and responds to the requested segment,  provided by the
> ACPI namespace, attach a custom pci_ecam_ops which redirects
> all config read/write requests to the firmware.
> 
> The Arm PCI Configuration Space Access Firmware Interface:
> https://developer.arm.com/documentation/den0115/latest
> 
> Signed-off-by: Jeremy Linton <jeremy.linton@....com>
> ---
>   arch/arm64/kernel/pci.c   | 109 ++++++++++++++++++++++++++++++++++++++
>   include/linux/arm-smccc.h |  29 ++++++++++
>   2 files changed, 138 insertions(+)
> 
> diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
> index 1006ed2d7c60..bcbca70ef219 100644
> --- a/arch/arm64/kernel/pci.c
> +++ b/arch/arm64/kernel/pci.c
> @@ -7,6 +7,7 @@
>    */
>   
>   #include <linux/acpi.h>
> +#include <linux/arm-smccc.h>
>   #include <linux/init.h>
>   #include <linux/io.h>
>   #include <linux/kernel.h>
> @@ -107,6 +108,112 @@ static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci)
>   	return status;
>   }
>   
> +static int smccc_pcie_has_conduit(void)
> +{
> +	struct arm_smccc_res res;
> +
> +	if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE)
> +		return -EOPNOTSUPP;
> +
> +	arm_smccc_smc(SMCCC_PCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);

Its been pointed out to me that there is a better choice here than 
arm_smccc_smc() given the spec calls out HVC too.

So i'm retesting this with arm_smccc_1_1_invoke() replacing all the 
arm_smccc_smc() call sites.

Reposting in the near future.

Thanks,


> +	if ((int)res.a0 < 0)
> +		return -EOPNOTSUPP;
> +
> +	arm_smccc_smc(SMCCC_PCI_FEATURES,
> +		      SMCCC_PCI_WRITE, 0, 0, 0, 0, 0, 0, &res);
> +	if ((int)res.a0 < 0)
> +		return -EOPNOTSUPP;
> +
> +	arm_smccc_smc(SMCCC_PCI_FEATURES,
> +		      SMCCC_PCI_READ, 0, 0, 0, 0, 0, 0, &res);
> +	if ((int)res.a0 < 0)
> +		return -EOPNOTSUPP;
> +
> +	arm_smccc_smc(SMCCC_PCI_FEATURES,
> +		      SMCCC_PCI_SEG_INFO, 0, 0, 0, 0, 0, 0, &res);
> +	if ((int)res.a0 < 0)
> +		return -EOPNOTSUPP;
> +
> +	return 0;
> +}
> +
> +static int smccc_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
> +				  int where, int size, u32 *val)
> +{
> +	struct arm_smccc_res res;
> +
> +	devfn |= bus->number << 8;
> +	devfn |= bus->domain_nr << 16;
> +
> +	arm_smccc_smc(SMCCC_PCI_READ, devfn, where, size, 0, 0, 0, 0, &res);
> +	if (res.a0) {
> +		*val = ~0;
> +		return -PCIBIOS_BAD_REGISTER_NUMBER;
> +	}
> +
> +	*val = res.a1;
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int smccc_pcie_config_write(struct pci_bus *bus, unsigned int devfn,
> +				   int where, int size, u32 val)
> +{
> +	struct arm_smccc_res res;
> +
> +	devfn |= bus->number << 8;
> +	devfn |= bus->domain_nr << 16;
> +
> +	arm_smccc_smc(SMCCC_PCI_WRITE, devfn, where, size, val, 0, 0, 0, &res);
> +	if (res.a0)
> +		return -PCIBIOS_BAD_REGISTER_NUMBER;
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static const struct pci_ecam_ops smccc_pcie_ops = {
> +	.pci_ops	= {
> +		.read		= smccc_pcie_config_read,
> +		.write		= smccc_pcie_config_write,
> +	}
> +};
> +
> +static struct pci_config_window *
> +pci_acpi_setup_smccc_mapping(struct acpi_pci_root *root)
> +{
> +	struct device *dev = &root->device->dev;
> +	struct arm_smccc_res res;
> +	struct resource *bus_res = &root->secondary;
> +	struct pci_config_window *cfg;
> +	u16 seg = root->segment;
> +
> +	arm_smccc_smc(SMCCC_PCI_SEG_INFO, seg, 0, 0, 0, 0, 0, 0, &res);
> +	if ((int)res.a0 < 0) {
> +		pr_warn("PCI: SMC segment %d doesn't exist\n", seg);
> +		return NULL;
> +	}
> +
> +	if (FIELD_GET(SMCCC_PCI_SEG_INFO_START_BUS, res.a1) != bus_res->start ||
> +	    FIELD_GET(SMCCC_PCI_SEG_INFO_END_BUS, res.a1) != bus_res->end) {
> +		pr_warn("PCI: SMC segment %d doesn't match ACPI description\n", seg);
> +		return NULL;
> +	}
> +
> +	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
> +	if (!cfg)
> +		return NULL;
> +
> +	cfg->parent = dev;
> +	cfg->ops = &smccc_pcie_ops;
> +	cfg->busr.start = bus_res->start;
> +	cfg->busr.end = bus_res->end;
> +	cfg->busr.flags = IORESOURCE_BUS;
> +	cfg->res.name = "PCI SMCCC";
> +
> +	pr_info("PCI: SMC conduit attached to segment %d\n", seg);
> +
> +	return cfg;
> +}
> +
>   /*
>    * Lookup the bus range for the domain in MCFG, and set up config space
>    * mapping.
> @@ -125,6 +232,8 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
>   
>   	ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops);
>   	if (ret) {
> +		if (!smccc_pcie_has_conduit())
> +			return pci_acpi_setup_smccc_mapping(root);
>   		dev_err(dev, "%04x:%pR ECAM region not found\n", seg, bus_res);
>   		return NULL;
>   	}
> diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
> index f860645f6512..a1a8fe0ea5aa 100644
> --- a/include/linux/arm-smccc.h
> +++ b/include/linux/arm-smccc.h
> @@ -89,6 +89,35 @@
>   
>   #define SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED	1
>   
> +/* PCI ECAM conduit (defined by ARM DEN0115A) */
> +#define SMCCC_PCI_VERSION						\
> +	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
> +			   ARM_SMCCC_SMC_32,				\
> +			   ARM_SMCCC_OWNER_STANDARD, 0x0130)
> +
> +#define SMCCC_PCI_FEATURES						\
> +	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
> +			   ARM_SMCCC_SMC_32,				\
> +			   ARM_SMCCC_OWNER_STANDARD, 0x0131)
> +
> +#define SMCCC_PCI_READ							\
> +	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
> +			   ARM_SMCCC_SMC_32,				\
> +			   ARM_SMCCC_OWNER_STANDARD, 0x0132)
> +
> +#define SMCCC_PCI_WRITE							\
> +	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
> +			   ARM_SMCCC_SMC_32,				\
> +			   ARM_SMCCC_OWNER_STANDARD, 0x0133)
> +
> +#define SMCCC_PCI_SEG_INFO						\
> +	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
> +			   ARM_SMCCC_SMC_32,				\
> +			   ARM_SMCCC_OWNER_STANDARD, 0x0134)
> +
> +#define SMCCC_PCI_SEG_INFO_START_BUS  GENMASK(7, 0)
> +#define SMCCC_PCI_SEG_INFO_END_BUS    GENMASK(15, 8)
> +
>   /* Paravirtualised time calls (defined by ARM DEN0057A) */
>   #define ARM_SMCCC_HV_PV_TIME_FEATURES				\
>   	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
> 

Powered by blists - more mailing lists