diff options
| author | Alexei Starovoitov <ast@kernel.org> | 2026-10-01 14:52:55 +0000 |
|---|---|---|
| committer | Kumar Kartikeya Dwivedi <memxor@gmail.com> | 2026-10-01 18:39:27 +0200 |
| commit | 33a154a96e71a34a1bcca9f40da343dbbf7b38b4 (patch) | |
| tree | 543be8029ab3d8a3a63994ae23385a053ed30029 /drivers/soundwire/amd_init.c | |
| download | linux-stable-33a154a96e71a34a1bcca9f40da343dbbf7b38b4.tar.gz linux-stable-33a154a96e71a34a1bcca9f40da343dbbf7b38b4.zip | |
selftests/bpf: Test packet range of pointers sharing an idgrafted
Add tests where two packet pointers share an id and tightening one
pointer's umax from its var_off would put it less than their constant
distance from the other's umax: with an index & 0x38 capped at 50, the
base pointer keeps umax 50, so the pointer 8 bytes further on must keep
umax 58, even though its known bits allow at most 56.
These refused a valid program or accepted an out-of-bounds access before
the fix:
- check the advanced copy, load through the base: valid, was refused;
- check the base, load the byte at base + 1 through a copy advanced by
8: was accepted;
- check base + 4, load 4 bytes at base + 2 through base + 8: reads two
bytes past the checked range, was accepted;
- the same as the second with data_meta pointers checked against data:
was accepted.
These pass with and without the fix and cover nearby paths:
- subtract an unknown scalar from a checked pointer and load below it
(the range is kept across a new id);
- reach a load through two paths whose checks cover 8 and 7 bytes after
the loaded pointer; the second path must not be pruned by the first;
- spill a copy of a pointer, check the pointer, fill the copy and load
one byte past the checked range: the load is refused, and the copy
has the range of the check.
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20261001145255.855630-2-alexei.starovoitov@gmail.com
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Diffstat (limited to 'drivers/soundwire/amd_init.c')
| -rw-r--r-- | drivers/soundwire/amd_init.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/drivers/soundwire/amd_init.c b/drivers/soundwire/amd_init.c new file mode 100644 index 000000000..8e419ddfa --- /dev/null +++ b/drivers/soundwire/amd_init.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * SoundWire AMD Manager Initialize routines + * + * Initializes and creates SDW devices based on ACPI and Hardware values + * + * Copyright 2024 Advanced Micro Devices, Inc. + */ + +#include <linux/acpi.h> +#include <linux/cleanup.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "amd_init.h" + +#define ACP_PAD_PULLDOWN_CTRL 0x0001448 +#define ACP_SW_PAD_KEEPER_EN 0x0001454 +#define AMD_SDW0_PAD_CTRL_MASK 0x60 +#define AMD_SDW1_PAD_CTRL_MASK 5 +#define AMD_SDW_PAD_CTRL_MASK (AMD_SDW0_PAD_CTRL_MASK | AMD_SDW1_PAD_CTRL_MASK) +#define AMD_SDW0_PAD_EN 1 +#define AMD_SDW1_PAD_EN 0x10 +#define AMD_SDW_PAD_EN (AMD_SDW0_PAD_EN | AMD_SDW1_PAD_EN) + +static int amd_enable_sdw_pads(void __iomem *mmio, u32 link_mask, struct device *dev) +{ + u32 pad_keeper_en, pad_pulldown_ctrl_mask; + + switch (link_mask) { + case 1: + pad_keeper_en = AMD_SDW0_PAD_EN; + pad_pulldown_ctrl_mask = AMD_SDW0_PAD_CTRL_MASK; + break; + case 2: + pad_keeper_en = AMD_SDW1_PAD_EN; + pad_pulldown_ctrl_mask = AMD_SDW1_PAD_CTRL_MASK; + break; + case 3: + pad_keeper_en = AMD_SDW_PAD_EN; + pad_pulldown_ctrl_mask = AMD_SDW_PAD_CTRL_MASK; + break; + default: + dev_err(dev, "No SDW Links are enabled\n"); + return -ENODEV; + } + + amd_updatel(mmio, ACP_SW_PAD_KEEPER_EN, pad_keeper_en, pad_keeper_en); + amd_updatel(mmio, ACP_PAD_PULLDOWN_CTRL, pad_pulldown_ctrl_mask, 0); + + return 0; +} + +static int sdw_amd_cleanup(struct sdw_amd_ctx *ctx) +{ + int i; + + for (i = 0; i < ctx->count; i++) { + if (!(ctx->link_mask & BIT(i))) + continue; + platform_device_unregister(ctx->pdev[i]); + } + + return 0; +} + +static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res) +{ + struct sdw_amd_ctx *ctx; + struct acpi_device *adev; + struct acp_sdw_pdata sdw_pdata[2]; + struct platform_device_info pdevinfo[2]; + u32 link_mask; + int count, index; + int ret; + + if (!res) + return NULL; + + adev = acpi_fetch_acpi_dev(res->handle); + if (!adev) + return NULL; + + if (!res->count) + return NULL; + + count = res->count; + dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); + ret = amd_enable_sdw_pads(res->mmio_base, res->link_mask, res->parent); + if (ret) + return NULL; + + /* + * we need to alloc/free memory manually and can't use devm: + * this routine may be called from a workqueue, and not from + * the parent .probe. + * If devm_ was used, the memory might never be freed on errors. + */ + ctx = kzalloc_obj(*ctx); + if (!ctx) + return NULL; + + ctx->count = count; + ctx->link_mask = res->link_mask; + struct resource *sdw_res __free(kfree) = kzalloc_obj(*sdw_res); + if (!sdw_res) { + kfree(ctx); + return NULL; + } + sdw_res->flags = IORESOURCE_MEM; + sdw_res->start = res->addr; + sdw_res->end = res->addr + res->reg_range; + memset(&pdevinfo, 0, sizeof(pdevinfo)); + link_mask = ctx->link_mask; + for (index = 0; index < count; index++) { + if (!(link_mask & BIT(index))) + continue; + + sdw_pdata[index].instance = index; + sdw_pdata[index].acp_sdw_lock = res->acp_lock; + sdw_pdata[index].acp_rev = res->acp_rev; + pdevinfo[index].name = "amd_sdw_manager"; + pdevinfo[index].id = index; + pdevinfo[index].parent = res->parent; + pdevinfo[index].num_res = 1; + pdevinfo[index].res = sdw_res; + pdevinfo[index].data = &sdw_pdata[index]; + pdevinfo[index].size_data = sizeof(struct acp_sdw_pdata); + pdevinfo[index].fwnode = acpi_fwnode_handle(adev); + ctx->pdev[index] = platform_device_register_full(&pdevinfo[index]); + if (IS_ERR(ctx->pdev[index])) + goto err; + } + return ctx; +err: + while (index--) { + if (!(link_mask & BIT(index))) + continue; + + platform_device_unregister(ctx->pdev[index]); + } + + kfree(ctx); + return NULL; +} + +static int sdw_amd_startup(struct sdw_amd_ctx *ctx) +{ + struct amd_sdw_manager *amd_manager; + int i, ret; + + /* Startup SDW Manager devices */ + for (i = 0; i < ctx->count; i++) { + if (!(ctx->link_mask & BIT(i))) + continue; + amd_manager = dev_get_drvdata(&ctx->pdev[i]->dev); + ret = amd_sdw_manager_start(amd_manager); + if (ret) + return ret; + } + + return 0; +} + +int sdw_amd_probe(struct sdw_amd_res *res, struct sdw_amd_ctx **sdw_ctx) +{ + *sdw_ctx = sdw_amd_probe_controller(res); + if (!*sdw_ctx) + return -ENODEV; + + return sdw_amd_startup(*sdw_ctx); +} +EXPORT_SYMBOL_NS(sdw_amd_probe, "SOUNDWIRE_AMD_INIT"); + +void sdw_amd_exit(struct sdw_amd_ctx *ctx) +{ + sdw_amd_cleanup(ctx); + kfree(ctx->peripherals); + kfree(ctx); +} +EXPORT_SYMBOL_NS(sdw_amd_exit, "SOUNDWIRE_AMD_INIT"); + +int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx) +{ + struct amd_sdw_manager *amd_manager; + struct sdw_bus *bus; + struct sdw_slave *slave; + struct list_head *node; + int index; + int i = 0; + int num_slaves = 0; + + for (index = 0; index < ctx->count; index++) { + if (!(ctx->link_mask & BIT(index))) + continue; + amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev); + if (!amd_manager) + return -ENODEV; + bus = &amd_manager->bus; + /* Calculate number of slaves */ + list_for_each(node, &bus->slaves) + num_slaves++; + } + + ctx->peripherals = kmalloc_flex(*ctx->peripherals, array, num_slaves); + if (!ctx->peripherals) + return -ENOMEM; + ctx->peripherals->num_peripherals = num_slaves; + for (index = 0; index < ctx->count; index++) { + if (!(ctx->link_mask & BIT(index))) + continue; + amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev); + if (amd_manager) { + bus = &amd_manager->bus; + list_for_each_entry(slave, &bus->slaves, node) { + ctx->peripherals->array[i] = slave; + i++; + } + } + } + return 0; +} +EXPORT_SYMBOL_NS(sdw_amd_get_slave_info, "SOUNDWIRE_AMD_INIT"); + +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD SoundWire Init Library"); +MODULE_LICENSE("Dual BSD/GPL"); |
