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  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20190114200120.GA33971@google.com>
Date:   Mon, 14 Jan 2019 14:01:20 -0600
From:   Bjorn Helgaas <helgaas@...nel.org>
To:     Alexandru Gagniuc <mr.nuke.me@...il.com>
Cc:     austin_bolen@...l.com, alex_gagniuc@...lteam.com,
        keith.busch@...el.com, Shyam_Iyer@...l.com, lukas@...ner.de,
        okaya@...nel.org, "Rafael J. Wysocki" <rjw@...ysocki.net>,
        Len Brown <lenb@...nel.org>, linux-acpi@...r.kernel.org,
        linux-pci@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [RFC] PCI / ACPI: Implementing Type 3 _HPX records

On Thu, Jan 10, 2019 at 05:11:27PM -0600, Alexandru Gagniuc wrote:
> _HPX Type 3 is intended to be more generic and allow configuration of
> settings not possible with Type 2 tables. For example, FW could ensure
> that the completion timeout value is set accordingly throughout the PCI
> tree; some switches require very special handling.
> 
> Type 3 can come in an arbitrary number of ACPI packages. With the older
> types we only ever had one descriptor. We could get lazy and store it
> on the stack. The current flow is to parse the HPX table
> --pci_get_hp_params()-- and then program the PCIe device registers.
> 
> In the current flow, we'll need to malloc(). Here's what I tried:
>  1) devm_kmalloc() on the pci_dev
>   This ended up giving me NULL dereference at boot time.

Yeah, I don't think devm_*() is going to work because that's part of the
driver binding model; this is in the PCI core and this use is not related
to the lifetime of a driver's binding to a device.

>  2) Add a cleanup function to be called after writing the PCIe registers
> 
> This RFC implements method 2. The HPX3 stuff is still NDA'd, but the
> housekeeping parts are in here. Is this a good way to do things? I'm not
> too sure about the need to call cleanup() on a stack variable. But if
> not that, then what else?

pci_get_hp_params() is currently exported, but only used inside
drivers/pci, so I think it could be unexported.

I don't remember if there's a reason why things are split between
pci_get_hp_params(), which runs _HPX or _HPP, and the program_hpp_type*()
functions.  If we could get rid of that split, we could get rid of struct
hotplug_params and do the configuration directly in the pci_get_hp_params()
path.  I think that would simplify the memory management.

Bjorn

> ---
>  drivers/pci/pci-acpi.c      | 88 +++++++++++++++++++++++++++++++++++++
>  drivers/pci/probe.c         | 29 ++++++++++++
>  include/linux/pci_hotplug.h | 17 +++++++
>  3 files changed, 134 insertions(+)
> 
> diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
> index e1949f7efd9c..2ce1b68ce688 100644
> --- a/drivers/pci/pci-acpi.c
> +++ b/drivers/pci/pci-acpi.c
> @@ -219,6 +219,65 @@ static acpi_status decode_type2_hpx_record(union acpi_object *record,
>  	return AE_OK;
>  }
>  
> +static acpi_status parse_hpx3_register(struct hpp_type3 *hpx3,
> +				       union acpi_object *reg_fields)
> +{
> +	struct hpp_type3_register *hpx3_reg;
> +
> +	hpx3_reg = kzalloc(sizeof(*hpx3_reg), GFP_KERNEL);
> +	if (!hpx3_reg)
> +		return AE_NO_MEMORY;
> +
> +	/* Parse fields blurb */
> +
> +	list_add_tail(&hpx3_reg->list, &hpx3->regs);
> +	return AE_OK;
> +}
> +
> +static acpi_status decode_type3_hpx_record(union acpi_object *record,
> +					   struct hotplug_params *hpx)
> +{
> +	union acpi_object *fields = record->package.elements;
> +	u32 desc_count, expected_length, revision;
> +	union acpi_object *reg_fields;
> +	acpi_status ret;
> +	int i;
> +
> +	revision = fields[1].integer.value;
> +	switch (revision) {
> +	case 1:
> +		desc_count = fields[2].integer.value;
> +		expected_length = 3 + desc_count * 14;
> +
> +		if (record->package.count != expected_length)
> +			return AE_ERROR;
> +
> +		for (i = 2; i < expected_length; i++)
> +			if (fields[i].type != ACPI_TYPE_INTEGER)
> +				return AE_ERROR;
> +
> +		if (!hpx->t3) {
> +			INIT_LIST_HEAD(&hpx->type3_data.regs);
> +			hpx->t3 = &hpx->type3_data;
> +		}
> +
> +		for (i = 0; i < desc_count; i++) {
> +			reg_fields = fields + 3 + i * 14;
> +			ret = parse_hpx3_register(hpx->t3, reg_fields);
> +			if (ret != AE_OK)
> +				return ret;
> +		}
> +
> +		break;
> +	default:
> +		printk(KERN_WARNING
> +			"%s: Type 3 Revision %d record not supported\n",
> +			__func__, revision);
> +		return AE_ERROR;
> +	}
> +	return AE_OK;
> +}
> +
>  static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx)
>  {
>  	acpi_status status;
> @@ -271,6 +330,11 @@ static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx)
>  			if (ACPI_FAILURE(status))
>  				goto exit;
>  			break;
> +		case 3:
> +			status = decode_type3_hpx_record(record, hpx);
> +			if (ACPI_FAILURE(status))
> +				goto exit;
> +			break;
>  		default:
>  			printk(KERN_ERR "%s: Type %d record not supported\n",
>  			       __func__, type);
> @@ -368,6 +432,30 @@ int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
>  }
>  EXPORT_SYMBOL_GPL(pci_get_hp_params);
>  
> +static int cleantup_type3_hpx_record(struct hpp_type3 *hpx3)
> +{
> +	struct hpp_type3_register *reg;
> +	struct list_head *entry, *temp;
> +
> +	list_for_each_safe(entry, temp, &hpp->t3->regs){
> +		reg = list_entry(entry, typeof(*reg), list);
> +		list_del(entry);
> +		kfree(reg);
> +	}
> +}
> +
> +/* pci_cleanup_hp_params
> + *
> + * @hpp - allocated by the caller
> + */
> +void pci_cleanup_hp_params(struct hotplug_params *hpp)
> +{
> +
> +	if (hpp->t3)
> +		cleanup_type3_hpx_record(hpp->t3);
> +}
> +EXPORT_SYMBOL_GPL(pci_cleanup_hp_params);
> +
>  /**
>   * pciehp_is_native - Check whether a hotplug port is handled by the OS
>   * @bridge: Hotplug port to check
> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
> index 257b9f6f2ebb..35ef7d1f4f3b 100644
> --- a/drivers/pci/probe.c
> +++ b/drivers/pci/probe.c
> @@ -1979,6 +1979,32 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
>  	 */
>  }
>  
> +static void program_hpp_type3_register(struct pci_dev *dev,
> +				       const struct hpp_type3_register *reg)
> +{
> +	/* Complicated ACPI-mandated blurb */
> +}
> +
> +static void program_hpp_type3(struct pci_dev *dev, struct hpp_type3 *hpp)
> +{
> +	struct hpp_type3_register *reg;
> +
> +	if (!hpp)
> +		return;
> +
> +	if (!pci_is_pcie(dev))
> +		return;
> +
> +	if (hpp->revision > 1) {
> +		pci_warn(dev, "PCIe settings rev %d not supported\n",
> +			 hpp->revision);
> +		return;
> +	}
> +
> +	list_for_each_entry(reg, &hpp->regs, list)
> +		program_hpp_type3_register(dev, reg);
> +}
> +
>  int pci_configure_extended_tags(struct pci_dev *dev, void *ign)
>  {
>  	struct pci_host_bridge *host;
> @@ -2145,9 +2171,12 @@ static void pci_configure_device(struct pci_dev *dev)
>  	if (ret)
>  		return;
>  
> +	program_hpp_type3(dev, hpp.t3);
>  	program_hpp_type2(dev, hpp.t2);
>  	program_hpp_type1(dev, hpp.t1);
>  	program_hpp_type0(dev, hpp.t0);
> +
> +	pci_cleanup_hp_params(&hpp);
>  }
>  
>  static void pci_release_capabilities(struct pci_dev *dev)
> diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h
> index 7acc9f91e72b..479da87a3774 100644
> --- a/include/linux/pci_hotplug.h
> +++ b/include/linux/pci_hotplug.h
> @@ -124,18 +124,35 @@ struct hpp_type2 {
>  	u32 sec_unc_err_mask_or;
>  };
>  
> +/*
> + * PCI Express Setting Record (Type 3)
> + * The ACPI overlords can never get enough
> + */
> +struct hpp_type3_register {
> +	u32 crap_describing_entry_contents;
> +	struct list_head list;
> +};
> +
> +struct hpp_type3 {
> +	u32 revision;
> +	struct list_head regs;
> +};
> +
>  struct hotplug_params {
>  	struct hpp_type0 *t0;		/* Type0: NULL if not available */
>  	struct hpp_type1 *t1;		/* Type1: NULL if not available */
>  	struct hpp_type2 *t2;		/* Type2: NULL if not available */
> +	struct hpp_type3 *t3;		/* Type3: NULL if not available */
>  	struct hpp_type0 type0_data;
>  	struct hpp_type1 type1_data;
>  	struct hpp_type2 type2_data;
> +	struct hpp_type3 type3_data;
>  };
>  
>  #ifdef CONFIG_ACPI
>  #include <linux/acpi.h>
>  int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp);
> +void pci_cleanup_hp_params(struct hotplug_params *hpp);
>  bool pciehp_is_native(struct pci_dev *bridge);
>  int acpi_get_hp_hw_control_from_firmware(struct pci_dev *bridge);
>  bool shpchp_is_native(struct pci_dev *bridge);
> -- 
> 2.19.2
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ