From: Sasha Levin Date: Wed, 2 Oct 2024 05:17:36 +0000 (-0400) Subject: Fixes for 5.4 X-Git-Tag: v6.6.54~45 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=dce803ae6c8c3c505f8dfb9bb572704c0fb67897;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 5.4 Signed-off-by: Sasha Levin --- diff --git a/queue-5.4/asoc-meson-axg-card-fix-use-after-free.patch b/queue-5.4/asoc-meson-axg-card-fix-use-after-free.patch new file mode 100644 index 00000000000..4f8cec4e88e --- /dev/null +++ b/queue-5.4/asoc-meson-axg-card-fix-use-after-free.patch @@ -0,0 +1,87 @@ +From b86366b5b927e9ff0fd2bfe02b65aaa3accabbf3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 11 Sep 2024 17:24:25 +0300 +Subject: ASoC: meson: axg-card: fix 'use-after-free' + +From: Arseniy Krasnov + +[ Upstream commit 4f9a71435953f941969a4f017e2357db62d85a86 ] + +Buffer 'card->dai_link' is reallocated in 'meson_card_reallocate_links()', +so move 'pad' pointer initialization after this function when memory is +already reallocated. + +Kasan bug report: + +================================================================== +BUG: KASAN: slab-use-after-free in axg_card_add_link+0x76c/0x9bc +Read of size 8 at addr ffff000000e8b260 by task modprobe/356 + +CPU: 0 PID: 356 Comm: modprobe Tainted: G O 6.9.12-sdkernel #1 +Call trace: + dump_backtrace+0x94/0xec + show_stack+0x18/0x24 + dump_stack_lvl+0x78/0x90 + print_report+0xfc/0x5c0 + kasan_report+0xb8/0xfc + __asan_load8+0x9c/0xb8 + axg_card_add_link+0x76c/0x9bc [snd_soc_meson_axg_sound_card] + meson_card_probe+0x344/0x3b8 [snd_soc_meson_card_utils] + platform_probe+0x8c/0xf4 + really_probe+0x110/0x39c + __driver_probe_device+0xb8/0x18c + driver_probe_device+0x108/0x1d8 + __driver_attach+0xd0/0x25c + bus_for_each_dev+0xe0/0x154 + driver_attach+0x34/0x44 + bus_add_driver+0x134/0x294 + driver_register+0xa8/0x1e8 + __platform_driver_register+0x44/0x54 + axg_card_pdrv_init+0x20/0x1000 [snd_soc_meson_axg_sound_card] + do_one_initcall+0xdc/0x25c + do_init_module+0x10c/0x334 + load_module+0x24c4/0x26cc + init_module_from_file+0xd4/0x128 + __arm64_sys_finit_module+0x1f4/0x41c + invoke_syscall+0x60/0x188 + el0_svc_common.constprop.0+0x78/0x13c + do_el0_svc+0x30/0x40 + el0_svc+0x38/0x78 + el0t_64_sync_handler+0x100/0x12c + el0t_64_sync+0x190/0x194 + +Fixes: 7864a79f37b5 ("ASoC: meson: add axg sound card support") +Cc: Stable@vger.kernel.org +Signed-off-by: Arseniy Krasnov +Reviewed-by: Jerome Brunet +Link: https://patch.msgid.link/20240911142425.598631-1-avkrasnov@salutedevices.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/meson/axg-card.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c +index 6bb3a8f0a788d..a369c18531876 100644 +--- a/sound/soc/meson/axg-card.c ++++ b/sound/soc/meson/axg-card.c +@@ -104,7 +104,7 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, + int *index) + { + struct meson_card *priv = snd_soc_card_get_drvdata(card); +- struct snd_soc_dai_link *pad = &card->dai_link[*index]; ++ struct snd_soc_dai_link *pad; + struct snd_soc_dai_link *lb; + struct snd_soc_dai_link_component *dlc; + int ret; +@@ -114,6 +114,7 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, + if (ret) + return ret; + ++ pad = &card->dai_link[*index]; + lb = &card->dai_link[*index + 1]; + + lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name); +-- +2.43.0 + diff --git a/queue-5.4/asoc-meson-axg-extract-sound-card-utils.patch b/queue-5.4/asoc-meson-axg-extract-sound-card-utils.patch new file mode 100644 index 00000000000..ab9bebc306b --- /dev/null +++ b/queue-5.4/asoc-meson-axg-extract-sound-card-utils.patch @@ -0,0 +1,1048 @@ +From b5acf3f40a49beb3d71fe3e1c074ff6105f3fbcc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 13 Feb 2020 16:51:57 +0100 +Subject: ASoC: meson: axg: extract sound card utils + +From: Jerome Brunet + +[ Upstream commit aa9c3b7273a58b5d9b2c1161b76b5fc8ea8c159b ] + +This prepares the addition of the GX SoC family sound card driver. +The GX sound card, while slightly different, will be similar to the +AXG one. The purpose of this change is to share the utils common to +both sound card driver. + +Signed-off-by: Jerome Brunet +Link: https://lore.kernel.org/r/20200213155159.3235792-8-jbrunet@baylibre.com +Signed-off-by: Mark Brown +Stable-dep-of: 4f9a71435953 ("ASoC: meson: axg-card: fix 'use-after-free'") +Signed-off-by: Sasha Levin +--- + sound/soc/meson/Kconfig | 4 + + sound/soc/meson/Makefile | 2 + + sound/soc/meson/axg-card.c | 403 ++--------------------------- + sound/soc/meson/meson-card-utils.c | 385 +++++++++++++++++++++++++++ + sound/soc/meson/meson-card.h | 55 ++++ + 5 files changed, 473 insertions(+), 376 deletions(-) + create mode 100644 sound/soc/meson/meson-card-utils.c + create mode 100644 sound/soc/meson/meson-card.h + +diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig +index f9188274f6b0f..ce15de42945b5 100644 +--- a/sound/soc/meson/Kconfig ++++ b/sound/soc/meson/Kconfig +@@ -50,6 +50,7 @@ config SND_MESON_AXG_TDMOUT + config SND_MESON_AXG_SOUND_CARD + tristate "Amlogic AXG Sound Card Support" + select SND_MESON_AXG_TDM_INTERFACE ++ select SND_MESON_CARD_UTILS + imply SND_MESON_AXG_FRDDR + imply SND_MESON_AXG_TODDR + imply SND_MESON_AXG_TDMIN +@@ -85,6 +86,9 @@ config SND_MESON_AXG_PDM + Select Y or M to add support for PDM input embedded + in the Amlogic AXG SoC family + ++config SND_MESON_CARD_UTILS ++ tristate ++ + config SND_MESON_CODEC_GLUE + tristate + +diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile +index 529a807b3f376..6e248260617fe 100644 +--- a/sound/soc/meson/Makefile ++++ b/sound/soc/meson/Makefile +@@ -11,6 +11,7 @@ snd-soc-meson-axg-sound-card-objs := axg-card.o + snd-soc-meson-axg-spdifin-objs := axg-spdifin.o + snd-soc-meson-axg-spdifout-objs := axg-spdifout.o + snd-soc-meson-axg-pdm-objs := axg-pdm.o ++snd-soc-meson-card-utils-objs := meson-card-utils.o + snd-soc-meson-codec-glue-objs := meson-codec-glue.o + snd-soc-meson-g12a-tohdmitx-objs := g12a-tohdmitx.o + +@@ -25,5 +26,6 @@ obj-$(CONFIG_SND_MESON_AXG_SOUND_CARD) += snd-soc-meson-axg-sound-card.o + obj-$(CONFIG_SND_MESON_AXG_SPDIFIN) += snd-soc-meson-axg-spdifin.o + obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o + obj-$(CONFIG_SND_MESON_AXG_PDM) += snd-soc-meson-axg-pdm.o ++obj-$(CONFIG_SND_MESON_CARD_UTILS) += snd-soc-meson-card-utils.o + obj-$(CONFIG_SND_MESON_CODEC_GLUE) += snd-soc-meson-codec-glue.o + obj-$(CONFIG_SND_MESON_G12A_TOHDMITX) += snd-soc-meson-g12a-tohdmitx.o +diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c +index 7126344017fa6..6bb3a8f0a788d 100644 +--- a/sound/soc/meson/axg-card.c ++++ b/sound/soc/meson/axg-card.c +@@ -9,11 +9,7 @@ + #include + + #include "axg-tdm.h" +- +-struct axg_card { +- struct snd_soc_card card; +- void **link_data; +-}; ++#include "meson-card.h" + + struct axg_dai_link_tdm_mask { + u32 tx; +@@ -41,161 +37,15 @@ static const struct snd_soc_pcm_stream codec_params = { + .channels_max = 8, + }; + +-#define PREFIX "amlogic," +- +-static int axg_card_reallocate_links(struct axg_card *priv, +- unsigned int num_links) +-{ +- struct snd_soc_dai_link *links; +- void **ldata; +- +- links = krealloc(priv->card.dai_link, +- num_links * sizeof(*priv->card.dai_link), +- GFP_KERNEL | __GFP_ZERO); +- ldata = krealloc(priv->link_data, +- num_links * sizeof(*priv->link_data), +- GFP_KERNEL | __GFP_ZERO); +- +- if (!links || !ldata) { +- dev_err(priv->card.dev, "failed to allocate links\n"); +- return -ENOMEM; +- } +- +- priv->card.dai_link = links; +- priv->link_data = ldata; +- priv->card.num_links = num_links; +- return 0; +-} +- +-static int axg_card_parse_dai(struct snd_soc_card *card, +- struct device_node *node, +- struct device_node **dai_of_node, +- const char **dai_name) +-{ +- struct of_phandle_args args; +- int ret; +- +- if (!dai_name || !dai_of_node || !node) +- return -EINVAL; +- +- ret = of_parse_phandle_with_args(node, "sound-dai", +- "#sound-dai-cells", 0, &args); +- if (ret) { +- if (ret != -EPROBE_DEFER) +- dev_err(card->dev, "can't parse dai %d\n", ret); +- return ret; +- } +- *dai_of_node = args.np; +- +- return snd_soc_get_dai_name(&args, dai_name); +-} +- +-static int axg_card_set_link_name(struct snd_soc_card *card, +- struct snd_soc_dai_link *link, +- struct device_node *node, +- const char *prefix) +-{ +- char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", +- prefix, node->full_name); +- if (!name) +- return -ENOMEM; +- +- link->name = name; +- link->stream_name = name; +- +- return 0; +-} +- +-static void axg_card_clean_references(struct axg_card *priv) +-{ +- struct snd_soc_card *card = &priv->card; +- struct snd_soc_dai_link *link; +- struct snd_soc_dai_link_component *codec; +- struct snd_soc_aux_dev *aux; +- int i, j; +- +- if (card->dai_link) { +- for_each_card_prelinks(card, i, link) { +- if (link->cpus) +- of_node_put(link->cpus->of_node); +- for_each_link_codecs(link, j, codec) +- of_node_put(codec->of_node); +- } +- } +- +- if (card->aux_dev) { +- for_each_card_pre_auxs(card, i, aux) +- of_node_put(aux->dlc.of_node); +- } +- +- kfree(card->dai_link); +- kfree(priv->link_data); +-} +- +-static int axg_card_add_aux_devices(struct snd_soc_card *card) +-{ +- struct device_node *node = card->dev->of_node; +- struct snd_soc_aux_dev *aux; +- int num, i; +- +- num = of_count_phandle_with_args(node, "audio-aux-devs", NULL); +- if (num == -ENOENT) { +- /* +- * It is ok to have no auxiliary devices but for this card it +- * is a strange situtation. Let's warn the about it. +- */ +- dev_warn(card->dev, "card has no auxiliary devices\n"); +- return 0; +- } else if (num < 0) { +- dev_err(card->dev, "error getting auxiliary devices: %d\n", +- num); +- return num; +- } +- +- aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); +- if (!aux) +- return -ENOMEM; +- card->aux_dev = aux; +- card->num_aux_devs = num; +- +- for_each_card_pre_auxs(card, i, aux) { +- aux->dlc.of_node = +- of_parse_phandle(node, "audio-aux-devs", i); +- if (!aux->dlc.of_node) +- return -EINVAL; +- } +- +- return 0; +-} +- + static int axg_card_tdm_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) + { + struct snd_soc_pcm_runtime *rtd = substream->private_data; +- struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); ++ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct axg_dai_link_tdm_data *be = + (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; +- struct snd_soc_dai *codec_dai; +- unsigned int mclk; +- int ret, i; +- +- if (be->mclk_fs) { +- mclk = params_rate(params) * be->mclk_fs; +- +- for_each_rtd_codec_dai(rtd, i, codec_dai) { +- ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, +- SND_SOC_CLOCK_IN); +- if (ret && ret != -ENOTSUPP) +- return ret; +- } +- +- ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, +- SND_SOC_CLOCK_OUT); +- if (ret && ret != -ENOTSUPP) +- return ret; +- } + +- return 0; ++ return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs); + } + + static const struct snd_soc_ops axg_card_tdm_be_ops = { +@@ -204,7 +54,7 @@ static const struct snd_soc_ops axg_card_tdm_be_ops = { + + static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd) + { +- struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); ++ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct axg_dai_link_tdm_data *be = + (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; + struct snd_soc_dai *codec_dai; +@@ -234,7 +84,7 @@ static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd) + + static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd) + { +- struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); ++ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct axg_dai_link_tdm_data *be = + (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; + int ret; +@@ -253,14 +103,14 @@ static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd) + static int axg_card_add_tdm_loopback(struct snd_soc_card *card, + int *index) + { +- struct axg_card *priv = snd_soc_card_get_drvdata(card); ++ struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *pad = &card->dai_link[*index]; + struct snd_soc_dai_link *lb; + struct snd_soc_dai_link_component *dlc; + int ret; + + /* extend links */ +- ret = axg_card_reallocate_links(priv, card->num_links + 1); ++ ret = meson_card_reallocate_links(card, card->num_links + 1); + if (ret) + return ret; + +@@ -304,32 +154,6 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, + return 0; + } + +-static unsigned int axg_card_parse_daifmt(struct device_node *node, +- struct device_node *cpu_node) +-{ +- struct device_node *bitclkmaster = NULL; +- struct device_node *framemaster = NULL; +- unsigned int daifmt; +- +- daifmt = snd_soc_of_parse_daifmt(node, PREFIX, +- &bitclkmaster, &framemaster); +- daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; +- +- /* If no master is provided, default to cpu master */ +- if (!bitclkmaster || bitclkmaster == cpu_node) { +- daifmt |= (!framemaster || framemaster == cpu_node) ? +- SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM; +- } else { +- daifmt |= (!framemaster || framemaster == cpu_node) ? +- SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM; +- } +- +- of_node_put(bitclkmaster); +- of_node_put(framemaster); +- +- return daifmt; +-} +- + static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node, +@@ -424,7 +248,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, + struct device_node *node, + int *index) + { +- struct axg_card *priv = snd_soc_card_get_drvdata(card); ++ struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *link = &card->dai_link[*index]; + struct axg_dai_link_tdm_data *be; + int ret; +@@ -438,7 +262,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, + /* Setup tdm link */ + link->ops = &axg_card_tdm_be_ops; + link->init = axg_card_tdm_dai_init; +- link->dai_fmt = axg_card_parse_daifmt(node, link->cpus->of_node); ++ link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node); + + of_property_read_u32(node, "mclk-fs", &be->mclk_fs); + +@@ -462,97 +286,24 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, + return 0; + } + +-static int axg_card_set_be_link(struct snd_soc_card *card, +- struct snd_soc_dai_link *link, +- struct device_node *node) +-{ +- struct snd_soc_dai_link_component *codec; +- struct device_node *np; +- int ret, num_codecs; +- +- link->no_pcm = 1; +- link->dpcm_playback = 1; +- link->dpcm_capture = 1; +- +- num_codecs = of_get_child_count(node); +- if (!num_codecs) { +- dev_err(card->dev, "be link %s has no codec\n", +- node->full_name); +- return -EINVAL; +- } +- +- codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); +- if (!codec) +- return -ENOMEM; +- +- link->codecs = codec; +- link->num_codecs = num_codecs; +- +- for_each_child_of_node(node, np) { +- ret = axg_card_parse_dai(card, np, &codec->of_node, +- &codec->dai_name); +- if (ret) { +- of_node_put(np); +- return ret; +- } +- +- codec++; +- } +- +- ret = axg_card_set_link_name(card, link, node, "be"); +- if (ret) +- dev_err(card->dev, "error setting %pOFn link name\n", np); +- +- return ret; +-} +- +-static int axg_card_set_fe_link(struct snd_soc_card *card, +- struct snd_soc_dai_link *link, +- struct device_node *node, +- bool is_playback) +-{ +- struct snd_soc_dai_link_component *codec; +- +- codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL); +- if (!codec) +- return -ENOMEM; +- +- link->codecs = codec; +- link->num_codecs = 1; +- +- link->dynamic = 1; +- link->dpcm_merged_format = 1; +- link->dpcm_merged_chan = 1; +- link->dpcm_merged_rate = 1; +- link->codecs->dai_name = "snd-soc-dummy-dai"; +- link->codecs->name = "snd-soc-dummy"; +- +- if (is_playback) +- link->dpcm_playback = 1; +- else +- link->dpcm_capture = 1; +- +- return axg_card_set_link_name(card, link, node, "fe"); +-} +- + static int axg_card_cpu_is_capture_fe(struct device_node *np) + { +- return of_device_is_compatible(np, PREFIX "axg-toddr"); ++ return of_device_is_compatible(np, DT_PREFIX "axg-toddr"); + } + + static int axg_card_cpu_is_playback_fe(struct device_node *np) + { +- return of_device_is_compatible(np, PREFIX "axg-frddr"); ++ return of_device_is_compatible(np, DT_PREFIX "axg-frddr"); + } + + static int axg_card_cpu_is_tdm_iface(struct device_node *np) + { +- return of_device_is_compatible(np, PREFIX "axg-tdm-iface"); ++ return of_device_is_compatible(np, DT_PREFIX "axg-tdm-iface"); + } + + static int axg_card_cpu_is_codec(struct device_node *np) + { +- return of_device_is_compatible(np, PREFIX "g12a-tohdmitx"); ++ return of_device_is_compatible(np, DT_PREFIX "g12a-tohdmitx"); + } + + static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, +@@ -569,17 +320,17 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, + dai_link->cpus = cpu; + dai_link->num_cpus = 1; + +- ret = axg_card_parse_dai(card, np, &dai_link->cpus->of_node, +- &dai_link->cpus->dai_name); ++ ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node, ++ &dai_link->cpus->dai_name); + if (ret) + return ret; + + if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node)) +- ret = axg_card_set_fe_link(card, dai_link, np, true); ++ ret = meson_card_set_fe_link(card, dai_link, np, true); + else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node)) +- ret = axg_card_set_fe_link(card, dai_link, np, false); ++ ret = meson_card_set_fe_link(card, dai_link, np, false); + else +- ret = axg_card_set_be_link(card, dai_link, np); ++ ret = meson_card_set_be_link(card, dai_link, np); + + if (ret) + return ret; +@@ -592,121 +343,21 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, + return ret; + } + +-static int axg_card_add_links(struct snd_soc_card *card) +-{ +- struct axg_card *priv = snd_soc_card_get_drvdata(card); +- struct device_node *node = card->dev->of_node; +- struct device_node *np; +- int num, i, ret; +- +- num = of_get_child_count(node); +- if (!num) { +- dev_err(card->dev, "card has no links\n"); +- return -EINVAL; +- } +- +- ret = axg_card_reallocate_links(priv, num); +- if (ret) +- return ret; +- +- i = 0; +- for_each_child_of_node(node, np) { +- ret = axg_card_add_link(card, np, &i); +- if (ret) { +- of_node_put(np); +- return ret; +- } +- +- i++; +- } +- +- return 0; +-} +- +-static int axg_card_parse_of_optional(struct snd_soc_card *card, +- const char *propname, +- int (*func)(struct snd_soc_card *c, +- const char *p)) +-{ +- /* If property is not provided, don't fail ... */ +- if (!of_property_read_bool(card->dev->of_node, propname)) +- return 0; +- +- /* ... but do fail if it is provided and the parsing fails */ +- return func(card, propname); +-} ++static const struct meson_card_match_data axg_card_match_data = { ++ .add_link = axg_card_add_link, ++}; + + static const struct of_device_id axg_card_of_match[] = { +- { .compatible = "amlogic,axg-sound-card", }, +- {} ++ { ++ .compatible = "amlogic,axg-sound-card", ++ .data = &axg_card_match_data, ++ }, {} + }; + MODULE_DEVICE_TABLE(of, axg_card_of_match); + +-static int axg_card_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct axg_card *priv; +- int ret; +- +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- platform_set_drvdata(pdev, priv); +- snd_soc_card_set_drvdata(&priv->card, priv); +- +- priv->card.owner = THIS_MODULE; +- priv->card.dev = dev; +- +- ret = snd_soc_of_parse_card_name(&priv->card, "model"); +- if (ret < 0) +- return ret; +- +- ret = axg_card_parse_of_optional(&priv->card, "audio-routing", +- snd_soc_of_parse_audio_routing); +- if (ret) { +- dev_err(dev, "error while parsing routing\n"); +- return ret; +- } +- +- ret = axg_card_parse_of_optional(&priv->card, "audio-widgets", +- snd_soc_of_parse_audio_simple_widgets); +- if (ret) { +- dev_err(dev, "error while parsing widgets\n"); +- return ret; +- } +- +- ret = axg_card_add_links(&priv->card); +- if (ret) +- goto out_err; +- +- ret = axg_card_add_aux_devices(&priv->card); +- if (ret) +- goto out_err; +- +- ret = devm_snd_soc_register_card(dev, &priv->card); +- if (ret) +- goto out_err; +- +- return 0; +- +-out_err: +- axg_card_clean_references(priv); +- return ret; +-} +- +-static int axg_card_remove(struct platform_device *pdev) +-{ +- struct axg_card *priv = platform_get_drvdata(pdev); +- +- axg_card_clean_references(priv); +- +- return 0; +-} +- + static struct platform_driver axg_card_pdrv = { +- .probe = axg_card_probe, +- .remove = axg_card_remove, ++ .probe = meson_card_probe, ++ .remove = meson_card_remove, + .driver = { + .name = "axg-sound-card", + .of_match_table = axg_card_of_match, +diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c +new file mode 100644 +index 0000000000000..a70d244ef88b6 +--- /dev/null ++++ b/sound/soc/meson/meson-card-utils.c +@@ -0,0 +1,385 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// Copyright (c) 2020 BayLibre, SAS. ++// Author: Jerome Brunet ++ ++#include ++#include ++#include ++ ++#include "meson-card.h" ++ ++int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ unsigned int mclk_fs) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai *codec_dai; ++ unsigned int mclk; ++ int ret, i; ++ ++ if (!mclk_fs) ++ return 0; ++ ++ mclk = params_rate(params) * mclk_fs; ++ ++ for_each_rtd_codec_dai(rtd, i, codec_dai) { ++ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, ++ SND_SOC_CLOCK_IN); ++ if (ret && ret != -ENOTSUPP) ++ return ret; ++ } ++ ++ ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, ++ SND_SOC_CLOCK_OUT); ++ if (ret && ret != -ENOTSUPP) ++ return ret; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(meson_card_i2s_set_sysclk); ++ ++int meson_card_reallocate_links(struct snd_soc_card *card, ++ unsigned int num_links) ++{ ++ struct meson_card *priv = snd_soc_card_get_drvdata(card); ++ struct snd_soc_dai_link *links; ++ void **ldata; ++ ++ links = krealloc(priv->card.dai_link, ++ num_links * sizeof(*priv->card.dai_link), ++ GFP_KERNEL | __GFP_ZERO); ++ ldata = krealloc(priv->link_data, ++ num_links * sizeof(*priv->link_data), ++ GFP_KERNEL | __GFP_ZERO); ++ ++ if (!links || !ldata) { ++ dev_err(priv->card.dev, "failed to allocate links\n"); ++ return -ENOMEM; ++ } ++ ++ priv->card.dai_link = links; ++ priv->link_data = ldata; ++ priv->card.num_links = num_links; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(meson_card_reallocate_links); ++ ++int meson_card_parse_dai(struct snd_soc_card *card, ++ struct device_node *node, ++ struct device_node **dai_of_node, ++ const char **dai_name) ++{ ++ struct of_phandle_args args; ++ int ret; ++ ++ if (!dai_name || !dai_of_node || !node) ++ return -EINVAL; ++ ++ ret = of_parse_phandle_with_args(node, "sound-dai", ++ "#sound-dai-cells", 0, &args); ++ if (ret) { ++ if (ret != -EPROBE_DEFER) ++ dev_err(card->dev, "can't parse dai %d\n", ret); ++ return ret; ++ } ++ *dai_of_node = args.np; ++ ++ return snd_soc_get_dai_name(&args, dai_name); ++} ++EXPORT_SYMBOL_GPL(meson_card_parse_dai); ++ ++static int meson_card_set_link_name(struct snd_soc_card *card, ++ struct snd_soc_dai_link *link, ++ struct device_node *node, ++ const char *prefix) ++{ ++ char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", ++ prefix, node->full_name); ++ if (!name) ++ return -ENOMEM; ++ ++ link->name = name; ++ link->stream_name = name; ++ ++ return 0; ++} ++ ++unsigned int meson_card_parse_daifmt(struct device_node *node, ++ struct device_node *cpu_node) ++{ ++ struct device_node *bitclkmaster = NULL; ++ struct device_node *framemaster = NULL; ++ unsigned int daifmt; ++ ++ daifmt = snd_soc_of_parse_daifmt(node, DT_PREFIX, ++ &bitclkmaster, &framemaster); ++ daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; ++ ++ /* If no master is provided, default to cpu master */ ++ if (!bitclkmaster || bitclkmaster == cpu_node) { ++ daifmt |= (!framemaster || framemaster == cpu_node) ? ++ SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM; ++ } else { ++ daifmt |= (!framemaster || framemaster == cpu_node) ? ++ SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM; ++ } ++ ++ of_node_put(bitclkmaster); ++ of_node_put(framemaster); ++ ++ return daifmt; ++} ++EXPORT_SYMBOL_GPL(meson_card_parse_daifmt); ++ ++int meson_card_set_be_link(struct snd_soc_card *card, ++ struct snd_soc_dai_link *link, ++ struct device_node *node) ++{ ++ struct snd_soc_dai_link_component *codec; ++ struct device_node *np; ++ int ret, num_codecs; ++ ++ link->no_pcm = 1; ++ link->dpcm_playback = 1; ++ link->dpcm_capture = 1; ++ ++ num_codecs = of_get_child_count(node); ++ if (!num_codecs) { ++ dev_err(card->dev, "be link %s has no codec\n", ++ node->full_name); ++ return -EINVAL; ++ } ++ ++ codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); ++ if (!codec) ++ return -ENOMEM; ++ ++ link->codecs = codec; ++ link->num_codecs = num_codecs; ++ ++ for_each_child_of_node(node, np) { ++ ret = meson_card_parse_dai(card, np, &codec->of_node, ++ &codec->dai_name); ++ if (ret) { ++ of_node_put(np); ++ return ret; ++ } ++ ++ codec++; ++ } ++ ++ ret = meson_card_set_link_name(card, link, node, "be"); ++ if (ret) ++ dev_err(card->dev, "error setting %pOFn link name\n", np); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(meson_card_set_be_link); ++ ++int meson_card_set_fe_link(struct snd_soc_card *card, ++ struct snd_soc_dai_link *link, ++ struct device_node *node, ++ bool is_playback) ++{ ++ struct snd_soc_dai_link_component *codec; ++ ++ codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL); ++ if (!codec) ++ return -ENOMEM; ++ ++ link->codecs = codec; ++ link->num_codecs = 1; ++ ++ link->dynamic = 1; ++ link->dpcm_merged_format = 1; ++ link->dpcm_merged_chan = 1; ++ link->dpcm_merged_rate = 1; ++ link->codecs->dai_name = "snd-soc-dummy-dai"; ++ link->codecs->name = "snd-soc-dummy"; ++ ++ if (is_playback) ++ link->dpcm_playback = 1; ++ else ++ link->dpcm_capture = 1; ++ ++ return meson_card_set_link_name(card, link, node, "fe"); ++} ++EXPORT_SYMBOL_GPL(meson_card_set_fe_link); ++ ++static int meson_card_add_links(struct snd_soc_card *card) ++{ ++ struct meson_card *priv = snd_soc_card_get_drvdata(card); ++ struct device_node *node = card->dev->of_node; ++ struct device_node *np; ++ int num, i, ret; ++ ++ num = of_get_child_count(node); ++ if (!num) { ++ dev_err(card->dev, "card has no links\n"); ++ return -EINVAL; ++ } ++ ++ ret = meson_card_reallocate_links(card, num); ++ if (ret) ++ return ret; ++ ++ i = 0; ++ for_each_child_of_node(node, np) { ++ ret = priv->match_data->add_link(card, np, &i); ++ if (ret) { ++ of_node_put(np); ++ return ret; ++ } ++ ++ i++; ++ } ++ ++ return 0; ++} ++ ++static int meson_card_parse_of_optional(struct snd_soc_card *card, ++ const char *propname, ++ int (*func)(struct snd_soc_card *c, ++ const char *p)) ++{ ++ /* If property is not provided, don't fail ... */ ++ if (!of_property_read_bool(card->dev->of_node, propname)) ++ return 0; ++ ++ /* ... but do fail if it is provided and the parsing fails */ ++ return func(card, propname); ++} ++ ++static int meson_card_add_aux_devices(struct snd_soc_card *card) ++{ ++ struct device_node *node = card->dev->of_node; ++ struct snd_soc_aux_dev *aux; ++ int num, i; ++ ++ num = of_count_phandle_with_args(node, "audio-aux-devs", NULL); ++ if (num == -ENOENT) { ++ return 0; ++ } else if (num < 0) { ++ dev_err(card->dev, "error getting auxiliary devices: %d\n", ++ num); ++ return num; ++ } ++ ++ aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); ++ if (!aux) ++ return -ENOMEM; ++ card->aux_dev = aux; ++ card->num_aux_devs = num; ++ ++ for_each_card_pre_auxs(card, i, aux) { ++ aux->dlc.of_node = ++ of_parse_phandle(node, "audio-aux-devs", i); ++ if (!aux->dlc.of_node) ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void meson_card_clean_references(struct meson_card *priv) ++{ ++ struct snd_soc_card *card = &priv->card; ++ struct snd_soc_dai_link *link; ++ struct snd_soc_dai_link_component *codec; ++ struct snd_soc_aux_dev *aux; ++ int i, j; ++ ++ if (card->dai_link) { ++ for_each_card_prelinks(card, i, link) { ++ if (link->cpus) ++ of_node_put(link->cpus->of_node); ++ for_each_link_codecs(link, j, codec) ++ of_node_put(codec->of_node); ++ } ++ } ++ ++ if (card->aux_dev) { ++ for_each_card_pre_auxs(card, i, aux) ++ of_node_put(aux->dlc.of_node); ++ } ++ ++ kfree(card->dai_link); ++ kfree(priv->link_data); ++} ++ ++int meson_card_probe(struct platform_device *pdev) ++{ ++ const struct meson_card_match_data *data; ++ struct device *dev = &pdev->dev; ++ struct meson_card *priv; ++ int ret; ++ ++ data = of_device_get_match_data(dev); ++ if (!data) { ++ dev_err(dev, "failed to match device\n"); ++ return -ENODEV; ++ } ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, priv); ++ snd_soc_card_set_drvdata(&priv->card, priv); ++ ++ priv->card.owner = THIS_MODULE; ++ priv->card.dev = dev; ++ priv->match_data = data; ++ ++ ret = snd_soc_of_parse_card_name(&priv->card, "model"); ++ if (ret < 0) ++ return ret; ++ ++ ret = meson_card_parse_of_optional(&priv->card, "audio-routing", ++ snd_soc_of_parse_audio_routing); ++ if (ret) { ++ dev_err(dev, "error while parsing routing\n"); ++ return ret; ++ } ++ ++ ret = meson_card_parse_of_optional(&priv->card, "audio-widgets", ++ snd_soc_of_parse_audio_simple_widgets); ++ if (ret) { ++ dev_err(dev, "error while parsing widgets\n"); ++ return ret; ++ } ++ ++ ret = meson_card_add_links(&priv->card); ++ if (ret) ++ goto out_err; ++ ++ ret = meson_card_add_aux_devices(&priv->card); ++ if (ret) ++ goto out_err; ++ ++ ret = devm_snd_soc_register_card(dev, &priv->card); ++ if (ret) ++ goto out_err; ++ ++ return 0; ++ ++out_err: ++ meson_card_clean_references(priv); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(meson_card_probe); ++ ++int meson_card_remove(struct platform_device *pdev) ++{ ++ struct meson_card *priv = platform_get_drvdata(pdev); ++ ++ meson_card_clean_references(priv); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(meson_card_remove); ++ ++MODULE_DESCRIPTION("Amlogic Sound Card Utils"); ++MODULE_AUTHOR("Jerome Brunet "); ++MODULE_LICENSE("GPL v2"); +diff --git a/sound/soc/meson/meson-card.h b/sound/soc/meson/meson-card.h +new file mode 100644 +index 0000000000000..74314071c80db +--- /dev/null ++++ b/sound/soc/meson/meson-card.h +@@ -0,0 +1,55 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (c) 2020 BayLibre, SAS. ++ * Author: Jerome Brunet ++ */ ++ ++#ifndef _MESON_SND_CARD_H ++#define _MESON_SND_CARD_H ++ ++struct device_node; ++struct platform_device; ++ ++struct snd_soc_card; ++struct snd_pcm_substream; ++struct snd_pcm_hw_params; ++ ++#define DT_PREFIX "amlogic," ++ ++struct meson_card_match_data { ++ int (*add_link)(struct snd_soc_card *card, ++ struct device_node *node, ++ int *index); ++}; ++ ++struct meson_card { ++ const struct meson_card_match_data *match_data; ++ struct snd_soc_card card; ++ void **link_data; ++}; ++ ++unsigned int meson_card_parse_daifmt(struct device_node *node, ++ struct device_node *cpu_node); ++ ++int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ unsigned int mclk_fs); ++ ++int meson_card_reallocate_links(struct snd_soc_card *card, ++ unsigned int num_links); ++int meson_card_parse_dai(struct snd_soc_card *card, ++ struct device_node *node, ++ struct device_node **dai_of_node, ++ const char **dai_name); ++int meson_card_set_be_link(struct snd_soc_card *card, ++ struct snd_soc_dai_link *link, ++ struct device_node *node); ++int meson_card_set_fe_link(struct snd_soc_card *card, ++ struct snd_soc_dai_link *link, ++ struct device_node *node, ++ bool is_playback); ++ ++int meson_card_probe(struct platform_device *pdev); ++int meson_card_remove(struct platform_device *pdev); ++ ++#endif /* _MESON_SND_CARD_H */ +-- +2.43.0 + diff --git a/queue-5.4/pci-xilinx-nwl-fix-off-by-one-in-intx-irq-handler.patch b/queue-5.4/pci-xilinx-nwl-fix-off-by-one-in-intx-irq-handler.patch new file mode 100644 index 00000000000..396883e9bc1 --- /dev/null +++ b/queue-5.4/pci-xilinx-nwl-fix-off-by-one-in-intx-irq-handler.patch @@ -0,0 +1,73 @@ +From 4d4c4137bcf67176bf3ad6a1159f8e8ad7026494 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 31 May 2024 12:13:32 -0400 +Subject: PCI: xilinx-nwl: Fix off-by-one in INTx IRQ handler + +From: Sean Anderson + +[ Upstream commit 0199d2f2bd8cd97b310f7ed82a067247d7456029 ] + +MSGF_LEG_MASK is laid out with INTA in bit 0, INTB in bit 1, INTC in bit 2, +and INTD in bit 3. Hardware IRQ numbers start at 0, and we register +PCI_NUM_INTX IRQs. So to enable INTA (aka hwirq 0) we should set bit 0. +Remove the subtraction of one. + +This bug would cause INTx interrupts not to be delivered, as enabling INTB +would actually enable INTA, and enabling INTA wouldn't enable anything at +all. It is likely that this got overlooked for so long since most PCIe +hardware uses MSIs. This fixes the following UBSAN error: + + UBSAN: shift-out-of-bounds in ../drivers/pci/controller/pcie-xilinx-nwl.c:389:11 + shift exponent 18446744073709551615 is too large for 32-bit type 'int' + CPU: 1 PID: 61 Comm: kworker/u10:1 Not tainted 6.6.20+ #268 + Hardware name: xlnx,zynqmp (DT) + Workqueue: events_unbound deferred_probe_work_func + Call trace: + dump_backtrace (arch/arm64/kernel/stacktrace.c:235) + show_stack (arch/arm64/kernel/stacktrace.c:242) + dump_stack_lvl (lib/dump_stack.c:107) + dump_stack (lib/dump_stack.c:114) + __ubsan_handle_shift_out_of_bounds (lib/ubsan.c:218 lib/ubsan.c:387) + nwl_unmask_leg_irq (drivers/pci/controller/pcie-xilinx-nwl.c:389 (discriminator 1)) + irq_enable (kernel/irq/internals.h:234 kernel/irq/chip.c:170 kernel/irq/chip.c:439 kernel/irq/chip.c:432 kernel/irq/chip.c:345) + __irq_startup (kernel/irq/internals.h:239 kernel/irq/chip.c:180 kernel/irq/chip.c:250) + irq_startup (kernel/irq/chip.c:270) + __setup_irq (kernel/irq/manage.c:1800) + request_threaded_irq (kernel/irq/manage.c:2206) + pcie_pme_probe (include/linux/interrupt.h:168 drivers/pci/pcie/pme.c:348) + +Fixes: 9a181e1093af ("PCI: xilinx-nwl: Modify IRQ chip for legacy interrupts") +Link: https://lore.kernel.org/r/20240531161337.864994-3-sean.anderson@linux.dev +Signed-off-by: Sean Anderson +Signed-off-by: Bjorn Helgaas +Cc: stable@vger.kernel.org +Signed-off-by: Sasha Levin +--- + drivers/pci/controller/pcie-xilinx-nwl.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c +index eb23bb28221a8..f08606f25e3c8 100644 +--- a/drivers/pci/controller/pcie-xilinx-nwl.c ++++ b/drivers/pci/controller/pcie-xilinx-nwl.c +@@ -389,7 +389,7 @@ static void nwl_mask_leg_irq(struct irq_data *data) + u32 mask; + u32 val; + +- mask = 1 << (data->hwirq - 1); ++ mask = 1 << data->hwirq; + raw_spin_lock_irqsave(&pcie->leg_mask_lock, flags); + val = nwl_bridge_readl(pcie, MSGF_LEG_MASK); + nwl_bridge_writel(pcie, (val & (~mask)), MSGF_LEG_MASK); +@@ -403,7 +403,7 @@ static void nwl_unmask_leg_irq(struct irq_data *data) + u32 mask; + u32 val; + +- mask = 1 << (data->hwirq - 1); ++ mask = 1 << data->hwirq; + raw_spin_lock_irqsave(&pcie->leg_mask_lock, flags); + val = nwl_bridge_readl(pcie, MSGF_LEG_MASK); + nwl_bridge_writel(pcie, (val | mask), MSGF_LEG_MASK); +-- +2.43.0 + diff --git a/queue-5.4/pci-xilinx-nwl-use-irq_data_get_irq_chip_data.patch b/queue-5.4/pci-xilinx-nwl-use-irq_data_get_irq_chip_data.patch new file mode 100644 index 00000000000..4ab306cfd2b --- /dev/null +++ b/queue-5.4/pci-xilinx-nwl-use-irq_data_get_irq_chip_data.patch @@ -0,0 +1,65 @@ +From 0aeb45562ba3a342fb9c2f56f3e50e9c2d9171cf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 10 Dec 2020 20:25:54 +0100 +Subject: PCI: xilinx-nwl: Use irq_data_get_irq_chip_data() + +From: Thomas Gleixner + +[ Upstream commit e56427068a8d796bb7b8e297f2b6e947380e383f ] + +Going through a full irq descriptor lookup instead of just using the proper +helper function which provides direct access is suboptimal. + +In fact it _is_ wrong because the chip callback needs to get the chip data +which is relevant for the chip while using the irq descriptor variant +returns the irq chip data of the top level chip of a hierarchy. It does not +matter in this case because the chip is the top level chip, but that +doesn't make it more correct. + +Signed-off-by: Thomas Gleixner +Reviewed-by: Rob Herring +Cc: Bjorn Helgaas +Link: https://lore.kernel.org/r/20201210194044.364211860@linutronix.de +Stable-dep-of: 0199d2f2bd8c ("PCI: xilinx-nwl: Fix off-by-one in INTx IRQ handler") +Signed-off-by: Sasha Levin +--- + drivers/pci/controller/pcie-xilinx-nwl.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c +index 539bf53beb654..eb23bb28221a8 100644 +--- a/drivers/pci/controller/pcie-xilinx-nwl.c ++++ b/drivers/pci/controller/pcie-xilinx-nwl.c +@@ -384,13 +384,11 @@ static void nwl_pcie_msi_handler_low(struct irq_desc *desc) + + static void nwl_mask_leg_irq(struct irq_data *data) + { +- struct irq_desc *desc = irq_to_desc(data->irq); +- struct nwl_pcie *pcie; ++ struct nwl_pcie *pcie = irq_data_get_irq_chip_data(data); + unsigned long flags; + u32 mask; + u32 val; + +- pcie = irq_desc_get_chip_data(desc); + mask = 1 << (data->hwirq - 1); + raw_spin_lock_irqsave(&pcie->leg_mask_lock, flags); + val = nwl_bridge_readl(pcie, MSGF_LEG_MASK); +@@ -400,13 +398,11 @@ static void nwl_mask_leg_irq(struct irq_data *data) + + static void nwl_unmask_leg_irq(struct irq_data *data) + { +- struct irq_desc *desc = irq_to_desc(data->irq); +- struct nwl_pcie *pcie; ++ struct nwl_pcie *pcie = irq_data_get_irq_chip_data(data); + unsigned long flags; + u32 mask; + u32 val; + +- pcie = irq_desc_get_chip_data(desc); + mask = 1 << (data->hwirq - 1); + raw_spin_lock_irqsave(&pcie->leg_mask_lock, flags); + val = nwl_bridge_readl(pcie, MSGF_LEG_MASK); +-- +2.43.0 + diff --git a/queue-5.4/pps-add-an-error-check-in-parport_attach.patch b/queue-5.4/pps-add-an-error-check-in-parport_attach.patch new file mode 100644 index 00000000000..5e87b8deb16 --- /dev/null +++ b/queue-5.4/pps-add-an-error-check-in-parport_attach.patch @@ -0,0 +1,65 @@ +From 55a0aa62eba90973cdd0ef048db3143695cd07f7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 28 Aug 2024 21:18:14 +0800 +Subject: pps: add an error check in parport_attach + +From: Ma Ke + +[ Upstream commit 62c5a01a5711c8e4be8ae7b6f0db663094615d48 ] + +In parport_attach, the return value of ida_alloc is unchecked, witch leads +to the use of an invalid index value. + +To address this issue, index should be checked. When the index value is +abnormal, the device should be freed. + +Found by code review, compile tested only. + +Cc: stable@vger.kernel.org +Fixes: fb56d97df70e ("pps: client: use new parport device model") +Signed-off-by: Ma Ke +Acked-by: Rodolfo Giometti +Link: https://lore.kernel.org/r/20240828131814.3034338-1-make24@iscas.ac.cn +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/pps/clients/pps_parport.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c +index 4bb3678c7e451..84e49204912f8 100644 +--- a/drivers/pps/clients/pps_parport.c ++++ b/drivers/pps/clients/pps_parport.c +@@ -145,6 +145,9 @@ static void parport_attach(struct parport *port) + } + + index = ida_alloc(&pps_client_index, GFP_KERNEL); ++ if (index < 0) ++ goto err_free_device; ++ + memset(&pps_client_cb, 0, sizeof(pps_client_cb)); + pps_client_cb.private = device; + pps_client_cb.irq_func = parport_irq; +@@ -155,7 +158,7 @@ static void parport_attach(struct parport *port) + index); + if (!device->pardev) { + pr_err("couldn't register with %s\n", port->name); +- goto err_free; ++ goto err_free_ida; + } + + if (parport_claim_or_block(device->pardev) < 0) { +@@ -183,8 +186,9 @@ static void parport_attach(struct parport *port) + parport_release(device->pardev); + err_unregister_dev: + parport_unregister_device(device->pardev); +-err_free: ++err_free_ida: + ida_free(&pps_client_index, index); ++err_free_device: + kfree(device); + } + +-- +2.43.0 + diff --git a/queue-5.4/pps-remove-usage-of-the-deprecated-ida_simple_xx-api.patch b/queue-5.4/pps-remove-usage-of-the-deprecated-ida_simple_xx-api.patch new file mode 100644 index 00000000000..aaf89580d8b --- /dev/null +++ b/queue-5.4/pps-remove-usage-of-the-deprecated-ida_simple_xx-api.patch @@ -0,0 +1,59 @@ +From 2d180419cba909b0ea76c6b99431e0e0347200a9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 14 Apr 2024 12:10:17 +0200 +Subject: pps: remove usage of the deprecated ida_simple_xx() API + +From: Christophe JAILLET + +[ Upstream commit 55dbc5b5174d0e7d1fa397d05aa4cb145e8b887e ] + +ida_alloc() and ida_free() should be preferred to the deprecated +ida_simple_get() and ida_simple_remove(). + +This is less verbose. + +Link: https://lkml.kernel.org/r/9f681747d446b874952a892491387d79ffe565a9.1713089394.git.christophe.jaillet@wanadoo.fr +Signed-off-by: Christophe JAILLET +Cc: Rodolfo Giometti +Cc: Greg Kroah-Hartman +Signed-off-by: Andrew Morton +Stable-dep-of: 62c5a01a5711 ("pps: add an error check in parport_attach") +Signed-off-by: Sasha Levin +--- + drivers/pps/clients/pps_parport.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c +index 7a41fb7b0decf..4bb3678c7e451 100644 +--- a/drivers/pps/clients/pps_parport.c ++++ b/drivers/pps/clients/pps_parport.c +@@ -144,7 +144,7 @@ static void parport_attach(struct parport *port) + return; + } + +- index = ida_simple_get(&pps_client_index, 0, 0, GFP_KERNEL); ++ index = ida_alloc(&pps_client_index, GFP_KERNEL); + memset(&pps_client_cb, 0, sizeof(pps_client_cb)); + pps_client_cb.private = device; + pps_client_cb.irq_func = parport_irq; +@@ -184,7 +184,7 @@ static void parport_attach(struct parport *port) + err_unregister_dev: + parport_unregister_device(device->pardev); + err_free: +- ida_simple_remove(&pps_client_index, index); ++ ida_free(&pps_client_index, index); + kfree(device); + } + +@@ -204,7 +204,7 @@ static void parport_detach(struct parport *port) + pps_unregister_source(device->pps); + parport_release(pardev); + parport_unregister_device(pardev); +- ida_simple_remove(&pps_client_index, device->index); ++ ida_free(&pps_client_index, device->index); + kfree(device); + } + +-- +2.43.0 + diff --git a/queue-5.4/series b/queue-5.4/series index def9f6c2a8c..f3556daf917 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -162,3 +162,13 @@ hwrng-mtk-use-devm_pm_runtime_enable.patch vfs-fix-race-between-evice_inodes-and-find_inode-iput.patch fs-fix-file_set_fowner-lsm-hook-inconsistencies.patch nfs-fix-memory-leak-in-error-path-of-nfs4_do_reclaim.patch +asoc-meson-axg-extract-sound-card-utils.patch +asoc-meson-axg-card-fix-use-after-free.patch +pci-xilinx-nwl-use-irq_data_get_irq_chip_data.patch +pci-xilinx-nwl-fix-off-by-one-in-intx-irq-handler.patch +soc-versatile-realview-fix-memory-leak-during-device.patch +soc-versatile-realview-fix-soc_dev-leak-during-devic.patch +usb-yurex-replace-snprintf-with-the-safer-scnprintf-.patch +usb-misc-yurex-fix-race-between-read-and-write.patch +pps-remove-usage-of-the-deprecated-ida_simple_xx-api.patch +pps-add-an-error-check-in-parport_attach.patch diff --git a/queue-5.4/soc-versatile-realview-fix-memory-leak-during-device.patch b/queue-5.4/soc-versatile-realview-fix-memory-leak-during-device.patch new file mode 100644 index 00000000000..1a96e9d93ed --- /dev/null +++ b/queue-5.4/soc-versatile-realview-fix-memory-leak-during-device.patch @@ -0,0 +1,50 @@ +From 203806e5541d52c2f1a475d3120012ba6dac54d8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 25 Aug 2024 20:05:23 +0200 +Subject: soc: versatile: realview: fix memory leak during device remove + +From: Krzysztof Kozlowski + +[ Upstream commit 1c4f26a41f9d052f334f6ae629e01f598ed93508 ] + +If device is unbound, the memory allocated for soc_dev_attr should be +freed to prevent leaks. + +Signed-off-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/20240825-soc-dev-fixes-v1-2-ff4b35abed83@linaro.org +Signed-off-by: Linus Walleij +Stable-dep-of: c774f2564c00 ("soc: versatile: realview: fix soc_dev leak during device remove") +Signed-off-by: Sasha Levin +--- + drivers/soc/versatile/soc-realview.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/drivers/soc/versatile/soc-realview.c b/drivers/soc/versatile/soc-realview.c +index 9471353dd8c38..60947a5161e4c 100644 +--- a/drivers/soc/versatile/soc-realview.c ++++ b/drivers/soc/versatile/soc-realview.c +@@ -91,7 +91,7 @@ static int realview_soc_probe(struct platform_device *pdev) + if (IS_ERR(syscon_regmap)) + return PTR_ERR(syscon_regmap); + +- soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); ++ soc_dev_attr = devm_kzalloc(&pdev->dev, sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + +@@ -103,10 +103,9 @@ static int realview_soc_probe(struct platform_device *pdev) + soc_dev_attr->machine = "RealView"; + soc_dev_attr->family = "Versatile"; + soc_dev = soc_device_register(soc_dev_attr); +- if (IS_ERR(soc_dev)) { +- kfree(soc_dev_attr); ++ if (IS_ERR(soc_dev)) + return -ENODEV; +- } ++ + ret = regmap_read(syscon_regmap, REALVIEW_SYS_ID_OFFSET, + &realview_coreid); + if (ret) +-- +2.43.0 + diff --git a/queue-5.4/soc-versatile-realview-fix-soc_dev-leak-during-devic.patch b/queue-5.4/soc-versatile-realview-fix-soc_dev-leak-during-devic.patch new file mode 100644 index 00000000000..961af7cf296 --- /dev/null +++ b/queue-5.4/soc-versatile-realview-fix-soc_dev-leak-during-devic.patch @@ -0,0 +1,63 @@ +From 20df9af892df09028517f4da01af278b7c16ea4c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 25 Aug 2024 20:05:24 +0200 +Subject: soc: versatile: realview: fix soc_dev leak during device remove + +From: Krzysztof Kozlowski + +[ Upstream commit c774f2564c0086c23f5269fd4691f233756bf075 ] + +If device is unbound, the soc_dev should be unregistered to prevent +memory leak. + +Fixes: a2974c9c1f83 ("soc: add driver for the ARM RealView") +Cc: stable@vger.kernel.org +Signed-off-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/20240825-soc-dev-fixes-v1-3-ff4b35abed83@linaro.org +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/soc/versatile/soc-realview.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/drivers/soc/versatile/soc-realview.c b/drivers/soc/versatile/soc-realview.c +index 60947a5161e4c..9cddbde399860 100644 +--- a/drivers/soc/versatile/soc-realview.c ++++ b/drivers/soc/versatile/soc-realview.c +@@ -4,6 +4,7 @@ + * + * Author: Linus Walleij + */ ++#include + #include + #include + #include +@@ -79,6 +80,13 @@ static ssize_t realview_get_build(struct device *dev, + static struct device_attribute realview_build_attr = + __ATTR(build, S_IRUGO, realview_get_build, NULL); + ++static void realview_soc_socdev_release(void *data) ++{ ++ struct soc_device *soc_dev = data; ++ ++ soc_device_unregister(soc_dev); ++} ++ + static int realview_soc_probe(struct platform_device *pdev) + { + struct regmap *syscon_regmap; +@@ -106,6 +114,11 @@ static int realview_soc_probe(struct platform_device *pdev) + if (IS_ERR(soc_dev)) + return -ENODEV; + ++ ret = devm_add_action_or_reset(&pdev->dev, realview_soc_socdev_release, ++ soc_dev); ++ if (ret) ++ return ret; ++ + ret = regmap_read(syscon_regmap, REALVIEW_SYS_ID_OFFSET, + &realview_coreid); + if (ret) +-- +2.43.0 + diff --git a/queue-5.4/usb-misc-yurex-fix-race-between-read-and-write.patch b/queue-5.4/usb-misc-yurex-fix-race-between-read-and-write.patch new file mode 100644 index 00000000000..c581ef3f6ea --- /dev/null +++ b/queue-5.4/usb-misc-yurex-fix-race-between-read-and-write.patch @@ -0,0 +1,63 @@ +From eca18fcac1723b0c8c9c52e1985ab22e3e117994 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 12 Sep 2024 15:21:22 +0200 +Subject: USB: misc: yurex: fix race between read and write + +From: Oliver Neukum + +[ Upstream commit 93907620b308609c72ba4b95b09a6aa2658bb553 ] + +The write code path touches the bbu member in a non atomic manner +without taking the spinlock. Fix it. + +The bug is as old as the driver. + +Signed-off-by: Oliver Neukum +CC: stable@vger.kernel.org +Link: https://lore.kernel.org/r/20240912132126.1034743-1-oneukum@suse.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/misc/yurex.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c +index a85cc0f3e15c4..5f271d25b633d 100644 +--- a/drivers/usb/misc/yurex.c ++++ b/drivers/usb/misc/yurex.c +@@ -405,7 +405,6 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, + struct usb_yurex *dev; + int len = 0; + char in_buffer[MAX_S64_STRLEN]; +- unsigned long flags; + + dev = file->private_data; + +@@ -418,9 +417,9 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, + if (WARN_ON_ONCE(dev->bbu > S64_MAX || dev->bbu < S64_MIN)) + return -EIO; + +- spin_lock_irqsave(&dev->lock, flags); ++ spin_lock_irq(&dev->lock); + scnprintf(in_buffer, MAX_S64_STRLEN, "%lld\n", dev->bbu); +- spin_unlock_irqrestore(&dev->lock, flags); ++ spin_unlock_irq(&dev->lock); + mutex_unlock(&dev->io_mutex); + + return simple_read_from_buffer(buffer, count, ppos, in_buffer, len); +@@ -510,8 +509,11 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer, + __func__, retval); + goto error; + } +- if (set && timeout) ++ if (set && timeout) { ++ spin_lock_irq(&dev->lock); + dev->bbu = c2; ++ spin_unlock_irq(&dev->lock); ++ } + return timeout ? count : -EIO; + + error: +-- +2.43.0 + diff --git a/queue-5.4/usb-yurex-replace-snprintf-with-the-safer-scnprintf-.patch b/queue-5.4/usb-yurex-replace-snprintf-with-the-safer-scnprintf-.patch new file mode 100644 index 00000000000..573360b3a33 --- /dev/null +++ b/queue-5.4/usb-yurex-replace-snprintf-with-the-safer-scnprintf-.patch @@ -0,0 +1,77 @@ +From b9ba8d303cd07fa3e2ac6f412edce866f497fda6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 Dec 2023 16:42:37 +0000 +Subject: usb: yurex: Replace snprintf() with the safer scnprintf() variant + +From: Lee Jones + +[ Upstream commit 86b20af11e84c26ae3fde4dcc4f490948e3f8035 ] + +There is a general misunderstanding amongst engineers that {v}snprintf() +returns the length of the data *actually* encoded into the destination +array. However, as per the C99 standard {v}snprintf() really returns +the length of the data that *would have been* written if there were +enough space for it. This misunderstanding has led to buffer-overruns +in the past. It's generally considered safer to use the {v}scnprintf() +variants in their place (or even sprintf() in simple cases). So let's +do that. + +Whilst we're at it, let's define some magic numbers to increase +readability and ease of maintenance. + +Link: https://lwn.net/Articles/69419/ +Link: https://github.com/KSPP/linux/issues/105 +Cc: Tomoki Sekiyama +Signed-off-by: Lee Jones +Link: https://lore.kernel.org/r/20231213164246.1021885-9-lee@kernel.org +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 93907620b308 ("USB: misc: yurex: fix race between read and write") +Signed-off-by: Sasha Levin +--- + drivers/usb/misc/yurex.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c +index 08b72bb22b7ef..a85cc0f3e15c4 100644 +--- a/drivers/usb/misc/yurex.c ++++ b/drivers/usb/misc/yurex.c +@@ -34,6 +34,8 @@ + #define YUREX_BUF_SIZE 8 + #define YUREX_WRITE_TIMEOUT (HZ*2) + ++#define MAX_S64_STRLEN 20 /* {-}922337203685477580{7,8} */ ++ + /* table of devices that work with this driver */ + static struct usb_device_id yurex_table[] = { + { USB_DEVICE(YUREX_VENDOR_ID, YUREX_PRODUCT_ID) }, +@@ -402,7 +404,7 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, + { + struct usb_yurex *dev; + int len = 0; +- char in_buffer[20]; ++ char in_buffer[MAX_S64_STRLEN]; + unsigned long flags; + + dev = file->private_data; +@@ -413,14 +415,14 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, + return -ENODEV; + } + ++ if (WARN_ON_ONCE(dev->bbu > S64_MAX || dev->bbu < S64_MIN)) ++ return -EIO; ++ + spin_lock_irqsave(&dev->lock, flags); +- len = snprintf(in_buffer, 20, "%lld\n", dev->bbu); ++ scnprintf(in_buffer, MAX_S64_STRLEN, "%lld\n", dev->bbu); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->io_mutex); + +- if (WARN_ON_ONCE(len >= sizeof(in_buffer))) +- return -EIO; +- + return simple_read_from_buffer(buffer, count, ppos, in_buffer, len); + } + +-- +2.43.0 +