// SPDX-License-Identifier: GPL-2.0 /* * PCI Resizable BAR Extended Capability handling. */ #include #include #include #include #include #include #include #include #include #include #include "pci.h" #define PCI_REBAR_MIN_SIZE ((resource_size_t)SZ_1M) /** * pci_rebar_bytes_to_size - Convert size in bytes to PCI BAR Size * @bytes: size in bytes * * Convert size in bytes to encoded BAR Size in Resizable BAR Capability * (PCIe r6.2, sec. 7.8.6.3). * * Return: encoded BAR Size as defined in the PCIe spec (0=1MB, 31=128TB) */ int pci_rebar_bytes_to_size(u64 bytes) { int rebar_minsize = ilog2(PCI_REBAR_MIN_SIZE); bytes = roundup_pow_of_two(bytes); return max(ilog2(bytes), rebar_minsize) - rebar_minsize; } EXPORT_SYMBOL_GPL(pci_rebar_bytes_to_size); /** * pci_rebar_size_to_bytes - Convert encoded BAR Size to size in bytes * @size: encoded BAR Size as defined in the PCIe spec (0=1MB, 31=128TB) * * Return: BAR size in bytes */ resource_size_t pci_rebar_size_to_bytes(int size) { return 1ULL << (size + ilog2(PCI_REBAR_MIN_SIZE)); } EXPORT_SYMBOL_GPL(pci_rebar_size_to_bytes); void pci_rebar_init(struct pci_dev *pdev) { pdev->rebar_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); } /** * pci_rebar_find_pos - find position of resize control reg for BAR * @pdev: PCI device * @bar: BAR to find * * Helper to find the position of the control register for a BAR. * * Return: * * %-ENOTSUPP if resizable BARs are not supported at all, * * %-ENOENT if no control register for the BAR could be found. */ static int pci_rebar_find_pos(struct pci_dev *pdev, int bar) { unsigned int pos, nbars, i; u32 ctrl; if (pci_resource_is_iov(bar)) { pos = pci_iov_vf_rebar_cap(pdev); bar = pci_resource_num_to_vf_bar(bar); } else { pos = pdev->rebar_cap; } if (!pos) return -ENOTSUPP; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl); for (i = 0; i < nbars; i++, pos += 8) { int bar_idx; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); bar_idx = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, ctrl); if (bar_idx == bar) return pos; } return -ENOENT; } /** * pci_rebar_get_possible_sizes - get possible sizes for Resizable BAR * @pdev: PCI device * @bar: BAR to query * * Get the possible sizes of a resizable BAR as bitmask. * * Return: A bitmask of possible sizes (bit 0=1MB, bit 31=128TB), or %0 if * BAR isn't resizable. */ u64 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar) { int pos; u32 cap; pos = pci_rebar_find_pos(pdev, bar); if (pos < 0) return 0; pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap); cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap); /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */ if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f && bar == 0 && cap == 0x700) return 0x3f00; return cap; } EXPORT_SYMBOL(pci_rebar_get_possible_sizes); /** * pci_rebar_size_supported - check if size is supported for BAR * @pdev: PCI device * @bar: BAR to check * @size: encoded size as defined in the PCIe spec (0=1MB, 31=128TB) * * Return: %true if @bar is resizable and @size is supported, otherwise * %false. */ bool pci_rebar_size_supported(struct pci_dev *pdev, int bar, int size) { u64 sizes = pci_rebar_get_possible_sizes(pdev, bar); if (size < 0 || size > ilog2(SZ_128T) - ilog2(PCI_REBAR_MIN_SIZE)) return false; return BIT(size) & sizes; } EXPORT_SYMBOL_GPL(pci_rebar_size_supported); /** * pci_rebar_get_max_size - get the maximum supported size of a BAR * @pdev: PCI device * @bar: BAR to query * * Get the largest supported size of a resizable BAR as a size. * * Return: the encoded maximum BAR size as defined in the PCIe spec * (0=1MB, 31=128TB), or %-NOENT on error. */ int pci_rebar_get_max_size(struct pci_dev *pdev, int bar) { u64 sizes; sizes = pci_rebar_get_possible_sizes(pdev, bar); if (!sizes) return -ENOENT; return __fls(sizes); } EXPORT_SYMBOL_GPL(pci_rebar_get_max_size); /** * pci_rebar_get_current_size - get the current size of a Resizable BAR * @pdev: PCI device * @bar: BAR to get the size from * * Read the current size of a BAR from the Resizable BAR config. * * Return: BAR Size if @bar is resizable (0=1MB, 31=128TB), or negative on * error. */ int pci_rebar_get_current_size(struct pci_dev *pdev, int bar) { int pos; u32 ctrl; pos = pci_rebar_find_pos(pdev, bar); if (pos < 0) return pos; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); return FIELD_GET(PCI_REBAR_CTRL_BAR_SIZE, ctrl); } /** * pci_rebar_set_size - set a new size for a Resizable BAR * @pdev: PCI device * @bar: BAR to set size to * @size: new size as defined in the PCIe spec (0=1MB, 31=128TB) * * Set the new size of a BAR as defined in the spec. * * Return: %0 if resizing was successful, or negative on error. */ int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size) { int pos; u32 ctrl; pos = pci_rebar_find_pos(pdev, bar); if (pos < 0) return pos; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size); pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); if (pci_resource_is_iov(bar)) pci_iov_resource_set_size(pdev, bar, size); return 0; } void pci_restore_rebar_state(struct pci_dev *pdev) { unsigned int pos, nbars, i; u32 ctrl; pos = pdev->rebar_cap; if (!pos) return; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl); for (i = 0; i < nbars; i++, pos += 8) { struct resource *res; int bar_idx, size; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; res = pci_resource_n(pdev, bar_idx); size = pci_rebar_bytes_to_size(resource_size(res)); ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size); pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); } } static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev, int resno) { u16 cmd; if (pci_resource_is_iov(resno)) return pci_iov_is_memory_decoding_enabled(dev); pci_read_config_word(dev, PCI_COMMAND, &cmd); return cmd & PCI_COMMAND_MEMORY; } void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size) { resource_size_t res_size = pci_rebar_size_to_bytes(size); struct resource *res = pci_resource_n(dev, resno); if (pci_resource_is_iov(resno)) res_size *= pci_sriov_get_totalvfs(dev); resource_set_size(res, res_size); } /** * pci_resize_resource - reconfigure a Resizable BAR and resources * @dev: the PCI device * @resno: index of the BAR to be resized * @size: new size as defined in the spec (0=1MB, 31=128TB) * @exclude_bars: a mask of BARs that should not be released * * Reconfigure @resno to @size and re-run resource assignment algorithm * with the new size. * * Prior to resize, release @dev resources that share a bridge window with * @resno. This unpins the bridge window resource to allow changing it. * * The caller may prevent releasing a particular BAR by providing * @exclude_bars mask, but this may result in the resize operation failing * due to insufficient space. * * Return: 0 on success, or negative on error. In case of an error, the * resources are restored to their original places. */ int pci_resize_resource(struct pci_dev *dev, int resno, int size, int exclude_bars) { struct pci_host_bridge *host; int old, ret; /* Check if we must preserve the firmware's resource assignment */ host = pci_find_host_bridge(dev->bus); if (host->preserve_config) return -ENOTSUPP; if (pci_resize_is_memory_decoding_enabled(dev, resno)) return -EBUSY; if (!pci_rebar_size_supported(dev, resno, size)) return -EINVAL; old = pci_rebar_get_current_size(dev, resno); if (old < 0) return old; ret = pci_rebar_set_size(dev, resno, size); if (ret) return ret; ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars); if (ret) goto error_resize; return 0; error_resize: pci_rebar_set_size(dev, resno, old); return ret; } EXPORT_SYMBOL(pci_resize_resource);