* [PATCH V3 1/2] ASoC: fsl_esai: Wrap some operations to be functions
From: shengjiu.wang @ 2019-07-08 6:38 UTC (permalink / raw)
To: timur, nicoleotsuka, Xiubo.Lee, festevam, broonie, alsa-devel
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1562566531.git.shengjiu.wang@nxp.com>
From: Shengjiu Wang <shengjiu.wang@nxp.com>
Extract the operation to be functions, to improve the
readability.
In this patch, fsl_esai_hw_init, fsl_esai_register_restore,
fsl_esai_trigger_start and fsl_esai_trigger_stop are
extracted.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Acked-by: Nicolin Chen <nicoleotsuka@gmail.com>
---
sound/soc/fsl/fsl_esai.c | 192 ++++++++++++++++++++++++---------------
1 file changed, 119 insertions(+), 73 deletions(-)
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index 10d2210c91ef..ab460d6d7432 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -35,6 +35,7 @@
* @fifo_depth: depth of tx/rx FIFO
* @slot_width: width of each DAI slot
* @slots: number of slots
+ * @channels: channel num for tx or rx
* @hck_rate: clock rate of desired HCKx clock
* @sck_rate: clock rate of desired SCKx clock
* @hck_dir: the direction of HCKx pads
@@ -57,6 +58,7 @@ struct fsl_esai {
u32 slots;
u32 tx_mask;
u32 rx_mask;
+ u32 channels[2];
u32 hck_rate[2];
u32 sck_rate[2];
bool hck_dir[2];
@@ -543,64 +545,132 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
+static int fsl_esai_hw_init(struct fsl_esai *esai_priv)
{
- struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
- bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- u8 i, channels = substream->runtime->channels;
+ struct platform_device *pdev = esai_priv->pdev;
+ int ret;
+
+ /* Reset ESAI unit */
+ ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
+ ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK,
+ ESAI_ECR_ESAIEN | ESAI_ECR_ERST);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * We need to enable ESAI so as to access some of its registers.
+ * Otherwise, we would fail to dump regmap from user space.
+ */
+ ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
+ ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK,
+ ESAI_ECR_ESAIEN);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret);
+ return ret;
+ }
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
+ ESAI_PRRC_PDC_MASK, 0);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
+ ESAI_PCRC_PC_MASK, 0);
+
+ return 0;
+}
+
+static int fsl_esai_register_restore(struct fsl_esai *esai_priv)
+{
+ int ret;
+
+ /* FIFO reset for safety */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR,
+ ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR,
+ ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+
+ regcache_mark_dirty(esai_priv->regmap);
+ ret = regcache_sync(esai_priv->regmap);
+ if (ret)
+ return ret;
+
+ /* FIFO reset done */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0);
+
+ return 0;
+}
+
+static void fsl_esai_trigger_start(struct fsl_esai *esai_priv, bool tx)
+{
+ u8 i, channels = esai_priv->channels[tx];
u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
u32 mask;
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
+ ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN);
+
+ /* Write initial words reqiured by ESAI as normal procedure */
+ for (i = 0; tx && i < channels; i++)
+ regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0);
+
+ /*
+ * When set the TE/RE in the end of enablement flow, there
+ * will be channel swap issue for multi data line case.
+ * In order to workaround this issue, we switch the bit
+ * enablement sequence to below sequence
+ * 1) clear the xSMB & xSMA: which is done in probe and
+ * stop state.
+ * 2) set TE/RE
+ * 3) set xSMB
+ * 4) set xSMA: xSMA is the last one in this flow, which
+ * will trigger esai to start.
+ */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
+ tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK,
+ tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins));
+ mask = tx ? esai_priv->tx_mask : esai_priv->rx_mask;
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx),
+ ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(mask));
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),
+ ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(mask));
+}
+
+static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx)
+{
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
+ tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),
+ ESAI_xSMA_xS_MASK, 0);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx),
+ ESAI_xSMB_xS_MASK, 0);
+
+ /* Disable and reset FIFO */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
+ ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
+ ESAI_xFCR_xFR, 0);
+}
+
+static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ esai_priv->channels[tx] = substream->runtime->channels;
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
- ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN);
-
- /* Write initial words reqiured by ESAI as normal procedure */
- for (i = 0; tx && i < channels; i++)
- regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0);
-
- /*
- * When set the TE/RE in the end of enablement flow, there
- * will be channel swap issue for multi data line case.
- * In order to workaround this issue, we switch the bit
- * enablement sequence to below sequence
- * 1) clear the xSMB & xSMA: which is done in probe and
- * stop state.
- * 2) set TE/RE
- * 3) set xSMB
- * 4) set xSMA: xSMA is the last one in this flow, which
- * will trigger esai to start.
- */
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
- tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK,
- tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins));
- mask = tx ? esai_priv->tx_mask : esai_priv->rx_mask;
-
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx),
- ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(mask));
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),
- ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(mask));
-
+ fsl_esai_trigger_start(esai_priv, tx);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
- tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0);
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),
- ESAI_xSMA_xS_MASK, 0);
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx),
- ESAI_xSMB_xS_MASK, 0);
-
- /* Disable and reset FIFO */
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
- ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR);
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
- ESAI_xFCR_xFR, 0);
+ fsl_esai_trigger_stop(esai_priv, tx);
break;
default:
return -EINVAL;
@@ -866,22 +936,9 @@ static int fsl_esai_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, esai_priv);
- /* Reset ESAI unit */
- ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ERST);
- if (ret) {
- dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret);
+ ret = fsl_esai_hw_init(esai_priv);
+ if (ret)
return ret;
- }
-
- /*
- * We need to enable ESAI so as to access some of its registers.
- * Otherwise, we would fail to dump regmap from user space.
- */
- ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ESAIEN);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret);
- return ret;
- }
esai_priv->tx_mask = 0xFFFFFFFF;
esai_priv->rx_mask = 0xFFFFFFFF;
@@ -955,20 +1012,10 @@ static int fsl_esai_runtime_resume(struct device *dev)
regcache_cache_only(esai->regmap, false);
- /* FIFO reset for safety */
- regmap_update_bits(esai->regmap, REG_ESAI_TFCR,
- ESAI_xFCR_xFR, ESAI_xFCR_xFR);
- regmap_update_bits(esai->regmap, REG_ESAI_RFCR,
- ESAI_xFCR_xFR, ESAI_xFCR_xFR);
-
- ret = regcache_sync(esai->regmap);
+ ret = fsl_esai_register_restore(esai);
if (ret)
goto err_regcache_sync;
- /* FIFO reset done */
- regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0);
- regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0);
-
return 0;
err_regcache_sync:
@@ -991,7 +1038,6 @@ static int fsl_esai_runtime_suspend(struct device *dev)
struct fsl_esai *esai = dev_get_drvdata(dev);
regcache_cache_only(esai->regmap, true);
- regcache_mark_dirty(esai->regmap);
if (!IS_ERR(esai->fsysclk))
clk_disable_unprepare(esai->fsysclk);
--
2.21.0
^ permalink raw reply related
* [PATCH V3 2/2] ASoC: fsl_esai: recover the channel swap after xrun
From: shengjiu.wang @ 2019-07-08 6:38 UTC (permalink / raw)
To: timur, nicoleotsuka, Xiubo.Lee, festevam, broonie, alsa-devel
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1562566531.git.shengjiu.wang@nxp.com>
From: Shengjiu Wang <shengjiu.wang@nxp.com>
There is chip errata ERR008000, the reference doc is
(https://www.nxp.com/docs/en/errata/IMX6DQCE.pdf),
The issue is "While using ESAI transmit or receive and
an underrun/overrun happens, channel swap may occur.
The only recovery mechanism is to reset the ESAI."
This issue exist in imx3/imx5/imx6(partial) series.
In this commit add a tasklet to handle reset of ESAI
after xrun happens to recover the channel swap.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
sound/soc/fsl/fsl_esai.c | 78 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index ab460d6d7432..416bec424fd6 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -32,6 +32,7 @@
* @extalclk: esai clock source to derive HCK, SCK and FS
* @fsysclk: system clock source to derive HCK, SCK and FS
* @spbaclk: SPBA clock (optional, depending on SoC design)
+ * @task: tasklet to handle the reset operation
* @fifo_depth: depth of tx/rx FIFO
* @slot_width: width of each DAI slot
* @slots: number of slots
@@ -42,6 +43,7 @@
* @sck_div: if using PSR/PM dividers for SCKx clock
* @slave_mode: if fully using DAI slave mode
* @synchronous: if using tx/rx synchronous mode
+ * @reset_at_xrun: flags for enable reset operaton
* @name: driver name
*/
struct fsl_esai {
@@ -53,6 +55,7 @@ struct fsl_esai {
struct clk *extalclk;
struct clk *fsysclk;
struct clk *spbaclk;
+ struct tasklet_struct task;
u32 fifo_depth;
u32 slot_width;
u32 slots;
@@ -65,6 +68,7 @@ struct fsl_esai {
bool sck_div[2];
bool slave_mode;
bool synchronous;
+ bool reset_at_xrun;
char name[32];
};
@@ -73,8 +77,16 @@ static irqreturn_t esai_isr(int irq, void *devid)
struct fsl_esai *esai_priv = (struct fsl_esai *)devid;
struct platform_device *pdev = esai_priv->pdev;
u32 esr;
+ u32 saisr;
regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr);
+ regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr);
+
+ if ((saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE)) &&
+ esai_priv->reset_at_xrun) {
+ dev_dbg(&pdev->dev, "reset module for xrun\n");
+ tasklet_schedule(&esai_priv->task);
+ }
if (esr & ESAI_ESR_TINIT_MASK)
dev_dbg(&pdev->dev, "isr: Transmission Initialized\n");
@@ -635,10 +647,17 @@ static void fsl_esai_trigger_start(struct fsl_esai *esai_priv, bool tx)
ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(mask));
regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),
ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(mask));
+
+ /* Enable Exception interrupt */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
+ ESAI_xCR_xEIE_MASK, ESAI_xCR_xEIE);
}
static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx)
{
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
+ ESAI_xCR_xEIE_MASK, 0);
+
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0);
regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),
@@ -653,6 +672,55 @@ static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx)
ESAI_xFCR_xFR, 0);
}
+static void fsl_esai_hw_reset(unsigned long arg)
+{
+ struct fsl_esai *esai_priv = (struct fsl_esai *)arg;
+ u32 saisr, tfcr, rfcr;
+ bool tx = true, rx = false, enabled[2];
+
+ /* Save the registers */
+ regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr);
+ regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr);
+ enabled[tx] = tfcr & ESAI_xFCR_xFEN;
+ enabled[rx] = rfcr & ESAI_xFCR_xFEN;
+
+ /* Stop the tx & rx */
+ fsl_esai_trigger_stop(esai_priv, tx);
+ fsl_esai_trigger_stop(esai_priv, rx);
+
+ /* Reset the esai, and ignore return value */
+ fsl_esai_hw_init(esai_priv);
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
+ ESAI_xCR_xPR_MASK, ESAI_xCR_xPR);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,
+ ESAI_xCR_xPR_MASK, ESAI_xCR_xPR);
+
+ /*
+ * Restore registers by regcache_sync, and ignore
+ * return value
+ */
+ fsl_esai_register_restore(esai_priv);
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
+ ESAI_xCR_xPR_MASK, 0);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,
+ ESAI_xCR_xPR_MASK, 0);
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
+ ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
+ ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
+
+ regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr);
+
+ /* Restart tx / rx, if they already enabled */
+ if (enabled[tx])
+ fsl_esai_trigger_start(esai_priv, tx);
+ if (enabled[rx])
+ fsl_esai_trigger_start(esai_priv, rx);
+}
+
static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
@@ -857,6 +925,10 @@ static int fsl_esai_probe(struct platform_device *pdev)
esai_priv->pdev = pdev;
snprintf(esai_priv->name, sizeof(esai_priv->name), "%pOFn", np);
+ if (of_device_is_compatible(np, "fsl,vf610-esai") ||
+ of_device_is_compatible(np, "fsl,imx35-esai"))
+ esai_priv->reset_at_xrun = true;
+
/* Get the addresses and IRQ */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(&pdev->dev, res);
@@ -956,6 +1028,9 @@ static int fsl_esai_probe(struct platform_device *pdev)
return ret;
}
+ tasklet_init(&esai_priv->task, fsl_esai_hw_reset,
+ (unsigned long)esai_priv);
+
pm_runtime_enable(&pdev->dev);
regcache_cache_only(esai_priv->regmap, true);
@@ -969,7 +1044,10 @@ static int fsl_esai_probe(struct platform_device *pdev)
static int fsl_esai_remove(struct platform_device *pdev)
{
+ struct fsl_esai *esai_priv = platform_get_drvdata(pdev);
+
pm_runtime_disable(&pdev->dev);
+ tasklet_kill(&esai_priv->task);
return 0;
}
--
2.21.0
^ permalink raw reply related
* Re: [PATCH kernel v3 2/3] powerpc/powernv/ioda2: Allocate TCE table levels on demand for default DMA window
From: alistair @ 2019-07-08 7:01 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: Sam Bobroff, Oliver O'Halloran, linuxppc-dev, David Gibson
In-Reply-To: <20190530070355.121802-3-aik@ozlabs.ru>
It seems this is mostly just enabling already existing code used by KVM
for
on-demand TCE level allocation on baremetal as well. Given that I
suppose the
implementation of the on-demand allocation itself is already used and
therefore somewhat tested by KVM. I took a look at pnv_tce() which does
the
on-demand allocation and the changes here seem like they should work
with that
so:
Reviewed-by: Alistair Popple <alistair@popple.id.au>
On Thursday, 30 May 2019 5:03:54 PM AEST Alexey Kardashevskiy wrote:
> We allocate only the first level of multilevel TCE tables for KVM
> already (alloc_userspace_copy==true), and the rest is allocated on
> demand.
> This is not enabled though for baremetal.
>
> This removes the KVM limitation (implicit, via the alloc_userspace_copy
> parameter) and always allocates just the first level. The on-demand
> allocation of missing levels is already implemented.
>
> As from now on DMA map might happen with disabled interrupts, this
> allocates TCEs with GFP_ATOMIC.
>
> To save time when creating a new clean table, this skips non-allocated
> indirect TCE entries in pnv_tce_free just like we already do in
> the VFIO IOMMU TCE driver.
>
> This changes the default level number from 1 to 2 to reduce the amount
> of memory required for the default 32bit DMA window at the boot time.
> The default window size is up to 2GB which requires 4MB of TCEs which
> is
> unlikely to be used entirely or at all as most devices these days are
> 64bit capable so by switching to 2 levels by default we save 4032KB of
> RAM per a device.
>
> While at this, add __GFP_NOWARN to alloc_pages_node() as the userspace
> can trigger this path via VFIO, see the failure and try creating a
> table
> again with different parameters which might succeed.
>
> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> ---
> Changes:
> v2:
> * added __GFP_NOWARN to alloc_pages_node
> ---
> arch/powerpc/platforms/powernv/pci.h | 2 +-
> arch/powerpc/platforms/powernv/pci-ioda-tce.c | 20 +++++++++----------
> 2 files changed, 11 insertions(+), 11 deletions(-)
>
> diff --git a/arch/powerpc/platforms/powernv/pci.h
> b/arch/powerpc/platforms/powernv/pci.h index 1a51e7bfc541..a55dabc8d057
> 100644
> --- a/arch/powerpc/platforms/powernv/pci.h
> +++ b/arch/powerpc/platforms/powernv/pci.h
> @@ -225,7 +225,7 @@ extern struct iommu_table_group
> *pnv_npu_compound_attach( struct pnv_ioda_pe *pe);
>
> /* pci-ioda-tce.c */
> -#define POWERNV_IOMMU_DEFAULT_LEVELS 1
> +#define POWERNV_IOMMU_DEFAULT_LEVELS 2
> #define POWERNV_IOMMU_MAX_LEVELS 5
>
> extern int pnv_tce_build(struct iommu_table *tbl, long index, long
> npages,
> diff --git a/arch/powerpc/platforms/powernv/pci-ioda-tce.c
> b/arch/powerpc/platforms/powernv/pci-ioda-tce.c index
> e28f03e1eb5e..c75ec37bf0cd 100644
> --- a/arch/powerpc/platforms/powernv/pci-ioda-tce.c
> +++ b/arch/powerpc/platforms/powernv/pci-ioda-tce.c
> @@ -36,7 +36,8 @@ static __be64 *pnv_alloc_tce_level(int nid, unsigned
> int
> shift) struct page *tce_mem = NULL;
> __be64 *addr;
>
> - tce_mem = alloc_pages_node(nid, GFP_KERNEL, shift - PAGE_SHIFT);
> + tce_mem = alloc_pages_node(nid, GFP_ATOMIC | __GFP_NOWARN,
> + shift - PAGE_SHIFT);
> if (!tce_mem) {
> pr_err("Failed to allocate a TCE memory, level shift=%d\n",
> shift);
> @@ -161,6 +162,9 @@ void pnv_tce_free(struct iommu_table *tbl, long
> index,
> long npages)
>
> if (ptce)
> *ptce = cpu_to_be64(0);
> + else
> + /* Skip the rest of the level */
> + i |= tbl->it_level_size - 1;
> }
> }
>
> @@ -260,7 +264,6 @@ long pnv_pci_ioda2_table_alloc_pages(int nid, __u64
> bus_offset, unsigned int table_shift = max_t(unsigned int,
> entries_shift +
> 3, PAGE_SHIFT);
> const unsigned long tce_table_size = 1UL << table_shift;
> - unsigned int tmplevels = levels;
>
> if (!levels || (levels > POWERNV_IOMMU_MAX_LEVELS))
> return -EINVAL;
> @@ -268,9 +271,6 @@ long pnv_pci_ioda2_table_alloc_pages(int nid, __u64
> bus_offset, if (!is_power_of_2(window_size))
> return -EINVAL;
>
> - if (alloc_userspace_copy && (window_size > (1ULL << 32)))
> - tmplevels = 1;
> -
> /* Adjust direct table size from window_size and levels */
> entries_shift = (entries_shift + levels - 1) / levels;
> level_shift = entries_shift + 3;
> @@ -281,7 +281,7 @@ long pnv_pci_ioda2_table_alloc_pages(int nid, __u64
> bus_offset,
>
> /* Allocate TCE table */
> addr = pnv_pci_ioda2_table_do_alloc_pages(nid, level_shift,
> - tmplevels, tce_table_size, &offset, &total_allocated);
> + 1, tce_table_size, &offset, &total_allocated);
>
> /* addr==NULL means that the first level allocation failed */
> if (!addr)
> @@ -292,18 +292,18 @@ long pnv_pci_ioda2_table_alloc_pages(int nid,
> __u64
> bus_offset, * we did not allocate as much as we wanted,
> * release partially allocated table.
> */
> - if (tmplevels == levels && offset < tce_table_size)
> + if (levels == 1 && offset < tce_table_size)
> goto free_tces_exit;
>
> /* Allocate userspace view of the TCE table */
> if (alloc_userspace_copy) {
> offset = 0;
> uas = pnv_pci_ioda2_table_do_alloc_pages(nid, level_shift,
> - tmplevels, tce_table_size, &offset,
> + 1, tce_table_size, &offset,
> &total_allocated_uas);
> if (!uas)
> goto free_tces_exit;
> - if (tmplevels == levels && (offset < tce_table_size ||
> + if (levels == 1 && (offset < tce_table_size ||
> total_allocated_uas != total_allocated))
> goto free_uas_exit;
> }
> @@ -318,7 +318,7 @@ long pnv_pci_ioda2_table_alloc_pages(int nid, __u64
> bus_offset,
>
> pr_debug("Created TCE table: ws=%08llx ts=%lx @%08llx base=%lx uas=%p
> levels=%d/%d\n", window_size, tce_table_size, bus_offset, tbl->it_base,
> - tbl->it_userspace, tmplevels, levels);
> + tbl->it_userspace, 1, levels);
>
> return 0;
^ permalink raw reply
* Re: [PATCH kernel v3 3/3] powerpc/powernv/ioda2: Create bigger default window with 64k IOMMU pages
From: alistair @ 2019-07-08 7:01 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: Sam Bobroff, Oliver O'Halloran, linuxppc-dev, David Gibson
In-Reply-To: <20190530070355.121802-4-aik@ozlabs.ru>
Hi Alexey,
Couple of comment/questions below.
> - /*
> - * Reserve page 0 so it will not be used for any mappings.
> - * This avoids buggy drivers that consider page 0 to be invalid
> - * to crash the machine or even lose data.
> - */
> - if (tbl->it_offset == 0)
> - set_bit(0, tbl->it_map);
> + tbl->it_reserved_start = res_start;
> + tbl->it_reserved_end = res_end;
> + iommu_table_reserve_pages(tbl);
Personally I think it would be cleaner to set tbl->it_reserved_start/end
in
iommu_table_reserve_pages() rather than separating the setup like this.
>
> /* We only split the IOMMU table if we have 1GB or more of space */
> if ((tbl->it_size << tbl->it_page_shift) >= (1UL * 1024 * 1024 *
> 1024))
> @@ -727,12 +755,7 @@ static void iommu_table_free(struct kref *kref)
> return;
> }
>
> - /*
> - * In case we have reserved the first bit, we should not emit
> - * the warning below.
> - */
> - if (tbl->it_offset == 0)
> - clear_bit(0, tbl->it_map);
> + iommu_table_release_pages(tbl);
>
> /* verify that table contains no entries */
> if (!bitmap_empty(tbl->it_map, tbl->it_size))
> @@ -1037,8 +1060,7 @@ int iommu_take_ownership(struct iommu_table *tbl)
> for (i = 0; i < tbl->nr_pools; i++)
> spin_lock(&tbl->pools[i].lock);
>
> - if (tbl->it_offset == 0)
> - clear_bit(0, tbl->it_map);
> + iommu_table_reserve_pages(tbl);
>
> if (!bitmap_empty(tbl->it_map, tbl->it_size)) {
> pr_err("iommu_tce: it_map is not empty");
> @@ -1068,9 +1090,7 @@ void iommu_release_ownership(struct iommu_table
> *tbl)
>
> memset(tbl->it_map, 0, sz);
>
> - /* Restore bit#0 set by iommu_init_table() */
> - if (tbl->it_offset == 0)
> - set_bit(0, tbl->it_map);
> + iommu_table_release_pages(tbl);
Are the above two changes correct? From the perspective of code
behaviour this
seems to swap the set/clear_bit() calls. For example above you are
replacing
set_bit(0, tbl->it_map) with a call to iommu_table_release_pages() which
does
clear_bit(0, tbl->it_map) instead.
- Alistair
> for (i = 0; i < tbl->nr_pools; i++)
> spin_unlock(&tbl->pools[i].lock);
> diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c
> b/arch/powerpc/platforms/powernv/pci-ioda.c index
> 126602b4e399..ce2efdb3900d 100644
> --- a/arch/powerpc/platforms/powernv/pci-ioda.c
> +++ b/arch/powerpc/platforms/powernv/pci-ioda.c
> @@ -2422,6 +2422,7 @@ static long
> pnv_pci_ioda2_setup_default_config(struct
> pnv_ioda_pe *pe) {
> struct iommu_table *tbl = NULL;
> long rc;
> + unsigned long res_start, res_end;
>
> /*
> * crashkernel= specifies the kdump kernel's maximum memory at
> @@ -2435,19 +2436,46 @@ static long
> pnv_pci_ioda2_setup_default_config(struct pnv_ioda_pe *pe) * DMA window
> can
> be larger than available memory, which will
> * cause errors later.
> */
> - const u64 window_size = min((u64)pe->table_group.tce32_size,
> max_memory);
> + const u64 maxblock = 1UL << (PAGE_SHIFT + MAX_ORDER - 1);
>
> - rc = pnv_pci_ioda2_create_table(&pe->table_group, 0,
> - IOMMU_PAGE_SHIFT_4K,
> - window_size,
> - POWERNV_IOMMU_DEFAULT_LEVELS, false, &tbl);
> + /*
> + * We create the default window as big as we can. The constraint is
> + * the max order of allocation possible. The TCE tableis likely to
> + * end up being multilevel and with on-demand allocation in place,
> + * the initial use is not going to be huge as the default window aims
> + * to support cripplied devices (i.e. not fully 64bit DMAble) only.
> + */
> + /* iommu_table::it_map uses 1 bit per IOMMU page, hence 8 */
> + const u64 window_size = min((maxblock * 8) << PAGE_SHIFT,
> max_memory);
> + /* Each TCE level cannot exceed maxblock so go multilevel if needed
> */
> + unsigned long tces_order = ilog2(window_size >> PAGE_SHIFT);
> + unsigned long tcelevel_order = ilog2(maxblock >> 3);
> + unsigned int levels = tces_order / tcelevel_order;
> +
> + if (tces_order % tcelevel_order)
> + levels += 1;
> + /*
> + * We try to stick to default levels (which is >1 at the moment) in
> + * order to save memory by relying on on-demain TCE level allocation.
> + */
> + levels = max_t(unsigned int, levels, POWERNV_IOMMU_DEFAULT_LEVELS);
> +
> + rc = pnv_pci_ioda2_create_table(&pe->table_group, 0, PAGE_SHIFT,
> + window_size, levels, false, &tbl);
> if (rc) {
> pe_err(pe, "Failed to create 32-bit TCE table, err %ld",
> rc);
> return rc;
> }
>
> - iommu_init_table(tbl, pe->phb->hose->node);
> + /* We use top part of 32bit space for MMIO so exclude it from DMA */
> + res_start = 0;
> + res_end = 0;
> + if (window_size > pe->phb->ioda.m32_pci_base) {
> + res_start = pe->phb->ioda.m32_pci_base >> tbl->it_page_shift;
> + res_end = min(window_size, SZ_4G) >> tbl->it_page_shift;
> + }
> + iommu_init_table_res(tbl, pe->phb->hose->node, res_start, res_end);
>
> rc = pnv_pci_ioda2_set_window(&pe->table_group, 0, tbl);
> if (rc) {
^ permalink raw reply
* Re: [PATCH] cpufreq/pasemi: fix an use-after-free inpas_cpufreq_cpu_init()
From: wen.yang99 @ 2019-07-08 7:03 UTC (permalink / raw)
To: viresh.kumar
Cc: wang.yi59, linux-pm, rjw, linux-kernel, xue.zhihong,
cheng.shengyu, linuxppc-dev
In-Reply-To: <20190708062708.5zufqsmgso436idn@vireshk-i7>
[-- Attachment #1.1: Type: text/plain, Size: 745 bytes --]
> > The cpu variable is still being used in the of_get_property() call
> > after the of_node_put() call, which may result in use-after-free.
> >
> > Fixes: a9acc26b75f ("cpufreq/pasemi: fix possible object reference leak")
> > Signed-off-by: Wen Yang <wen.yang99@zte.com.cn>
> > Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
> > Cc: Viresh Kumar <viresh.kumar@linaro.org>
> > Cc: linuxppc-dev@lists.ozlabs.org
> > Cc: linux-pm@vger.kernel.org
> > Cc: linux-kernel@vger.kernel.org
> > ---
> > drivers/cpufreq/pasemi-cpufreq.c | 10 ++++++----
> > 1 file changed, 6 insertions(+), 4 deletions(-)
>
> I will suggest some changes here.
Hello viresh, thank you for your comments.
We will make changes as soon as possible.
--
Thanks and regards,
Wen
^ permalink raw reply
* [PATCH v2] cpufreq/pasemi: fix an use-after-free in pas_cpufreq_cpu_init()
From: Wen Yang @ 2019-07-08 7:19 UTC (permalink / raw)
To: linux-kernel
Cc: wang.yi59, linux-pm, Viresh Kumar, Rafael J. Wysocki, xue.zhihong,
cheng.shengyu, linuxppc-dev, Wen Yang
The cpu variable is still being used in the of_get_property() call
after the of_node_put() call, which may result in use-after-free.
Fixes: a9acc26b75f ("cpufreq/pasemi: fix possible object reference leak")
Signed-off-by: Wen Yang <wen.yang99@zte.com.cn>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-pm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
v2: clean up the code according to the advice of viresh.
drivers/cpufreq/pasemi-cpufreq.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c
index 6b1e4ab..c6d464b 100644
--- a/drivers/cpufreq/pasemi-cpufreq.c
+++ b/drivers/cpufreq/pasemi-cpufreq.c
@@ -128,20 +128,18 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
int cur_astate, idx;
struct resource res;
struct device_node *cpu, *dn;
- int err = -ENODEV;
+ int err;
cpu = of_get_cpu_node(policy->cpu, NULL);
-
- of_node_put(cpu);
if (!cpu)
- goto out;
+ return -ENODEV;
dn = of_find_compatible_node(NULL, NULL, "1682m-sdc");
if (!dn)
dn = of_find_compatible_node(NULL, NULL,
"pasemi,pwrficient-sdc");
if (!dn)
- goto out;
+ return -ENODEV;
err = of_address_to_resource(dn, 0, &res);
of_node_put(dn);
if (err)
@@ -196,6 +194,7 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
policy->cur = pas_freqs[cur_astate].frequency;
ppc_proc_freq = policy->cur * 1000ul;
+ of_node_put(cpu);
return cpufreq_generic_init(policy, pas_freqs, get_gizmo_latency());
out_unmap_sdcpwr:
@@ -204,6 +203,7 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
out_unmap_sdcasr:
iounmap(sdcasr_mapbase);
out:
+ of_node_put(cpu);
return err;
}
--
2.9.5
^ permalink raw reply related
* Re: [PATCH v2] cpufreq/pasemi: fix an use-after-free in pas_cpufreq_cpu_init()
From: Viresh Kumar @ 2019-07-08 7:44 UTC (permalink / raw)
To: Wen Yang
Cc: wang.yi59, linux-pm, Rafael J. Wysocki, linux-kernel, xue.zhihong,
cheng.shengyu, linuxppc-dev
In-Reply-To: <1562570393-8684-1-git-send-email-wen.yang99@zte.com.cn>
On 08-07-19, 15:19, Wen Yang wrote:
> The cpu variable is still being used in the of_get_property() call
> after the of_node_put() call, which may result in use-after-free.
>
> Fixes: a9acc26b75f ("cpufreq/pasemi: fix possible object reference leak")
> Signed-off-by: Wen Yang <wen.yang99@zte.com.cn>
> Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
> Cc: Viresh Kumar <viresh.kumar@linaro.org>
> Cc: linuxppc-dev@lists.ozlabs.org
> Cc: linux-pm@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> ---
> v2: clean up the code according to the advice of viresh.
>
> drivers/cpufreq/pasemi-cpufreq.c | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c
> index 6b1e4ab..c6d464b 100644
> --- a/drivers/cpufreq/pasemi-cpufreq.c
> +++ b/drivers/cpufreq/pasemi-cpufreq.c
> @@ -128,20 +128,18 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
> int cur_astate, idx;
> struct resource res;
> struct device_node *cpu, *dn;
> - int err = -ENODEV;
> + int err;
>
> cpu = of_get_cpu_node(policy->cpu, NULL);
> -
> - of_node_put(cpu);
> if (!cpu)
> - goto out;
> + return -ENODEV;
>
> dn = of_find_compatible_node(NULL, NULL, "1682m-sdc");
> if (!dn)
> dn = of_find_compatible_node(NULL, NULL,
> "pasemi,pwrficient-sdc");
> if (!dn)
> - goto out;
> + return -ENODEV;
This change looks incorrect. You still need to drop reference to cpu ?
> err = of_address_to_resource(dn, 0, &res);
> of_node_put(dn);
> if (err)
> @@ -196,6 +194,7 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
> policy->cur = pas_freqs[cur_astate].frequency;
> ppc_proc_freq = policy->cur * 1000ul;
>
> + of_node_put(cpu);
> return cpufreq_generic_init(policy, pas_freqs, get_gizmo_latency());
>
> out_unmap_sdcpwr:
> @@ -204,6 +203,7 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
> out_unmap_sdcasr:
> iounmap(sdcasr_mapbase);
> out:
> + of_node_put(cpu);
> return err;
> }
>
> --
> 2.9.5
--
viresh
^ permalink raw reply
* Re: [v3 4/7] powerpc/mce: Handle UE event for memcpy_mcsafe
From: Mahesh Jagannath Salgaonkar @ 2019-07-08 8:23 UTC (permalink / raw)
To: Nicholas Piggin, linuxppc-dev, Santosh Sivaraj
Cc: Aneesh Kumar K.V, Mahesh Salgaonkar, Chandan Rajendra, Reza Arbab
In-Reply-To: <1562406379.9eh1e6edgk.astroid@bobo.none>
On 7/6/19 3:23 PM, Nicholas Piggin wrote:
> Santosh Sivaraj's on July 6, 2019 7:26 am:
>> If we take a UE on one of the instructions with a fixup entry, set nip
>> to continue exucution at the fixup entry. Stop processing the event
>> further or print it.
>
> Minor nit, but can you instead a field in the mce data structure that
> describes the property of the event, and then the code that intends to
> ignore such events can test for it (with an appropriate comment).
>
> So it would be has_fixup_handler or similar. Then queue_event can have
> the logic
>
> /*
> * Don't report this machine check because the caller has a fixup
> * handler which will do the appropriate error handling and reporting.
> */
>
>
>> @@ -565,9 +567,18 @@ static int mce_handle_derror(struct pt_regs *regs,
>> return 0;
>> }
>>
>> -static long mce_handle_ue_error(struct pt_regs *regs)
>> +static long mce_handle_ue_error(struct pt_regs *regs,
>> + struct mce_error_info *mce_err)
>> {
>> long handled = 0;
>> + const struct exception_table_entry *entry;
>> +
>> + entry = search_exception_tables(regs->nip);
>
> Uh oh, this searches module exception tables too... we can't do that
> in real mode, can we?
Yeah, we can not do that in real mode. Should we directly call
search_extable() for kernel exception table ?
>
> Thanks,
> Nick
>
^ permalink raw reply
* Re: [PATCH v2] cpufreq/pasemi: fix an use-after-free inpas_cpufreq_cpu_init()
From: wen.yang99 @ 2019-07-08 8:26 UTC (permalink / raw)
To: viresh.kumar
Cc: wang.yi59, linux-pm, rjw, linux-kernel, xue.zhihong,
cheng.shengyu, linuxppc-dev
In-Reply-To: <20190708074432.56q2e3ig5ehiee5f@vireshk-i7>
[-- Attachment #1.1: Type: text/plain, Size: 1674 bytes --]
> > The cpu variable is still being used in the of_get_property() call
> > after the of_node_put() call, which may result in use-after-free.
> >
> > Fixes: a9acc26b75f ("cpufreq/pasemi: fix possible object reference leak")
> > Signed-off-by: Wen Yang <wen.yang99@zte.com.cn>
> > Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
> > Cc: Viresh Kumar <viresh.kumar@linaro.org>
> > Cc: linuxppc-dev@lists.ozlabs.org
> > Cc: linux-pm@vger.kernel.org
> > Cc: linux-kernel@vger.kernel.org
> > ---
> > v2: clean up the code according to the advice of viresh.
> >
> > drivers/cpufreq/pasemi-cpufreq.c | 10 +++++-----
> > 1 file changed, 5 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c
> > index 6b1e4ab..c6d464b 100644
> > --- a/drivers/cpufreq/pasemi-cpufreq.c
> > +++ b/drivers/cpufreq/pasemi-cpufreq.c
> > @@ -128,20 +128,18 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
> > int cur_astate, idx;
> > struct resource res;
> > struct device_node *cpu, *dn;
> > - int err = -ENODEV;
> > + int err;
> >
> > cpu = of_get_cpu_node(policy->cpu, NULL);
> > -
> > - of_node_put(cpu);
> > if (!cpu)
> > - goto out;
> > + return -ENODEV;
> >
>
>
> > dn = of_find_compatible_node(NULL, NULL, "1682m-sdc");
> > if (!dn)
> > dn = of_find_compatible_node(NULL, NULL,
> > "pasemi,pwrficient-sdc");
> > if (!dn)
> > - goto out;
> > + return -ENODEV;
>
> This change looks incorrect. You still need to drop reference to cpu ?
>
Thanks!
We will fix it immediately.
--
Thanks and regards,
Wen
^ permalink raw reply
* [PATCH v3] cpufreq/pasemi: fix an use-after-free in pas_cpufreq_cpu_init()
From: Wen Yang @ 2019-07-08 8:48 UTC (permalink / raw)
To: linux-kernel
Cc: wang.yi59, linux-pm, Viresh Kumar, Rafael J. Wysocki, xue.zhihong,
cheng.shengyu, linuxppc-dev, Wen Yang
The cpu variable is still being used in the of_get_property() call
after the of_node_put() call, which may result in use-after-free.
Fixes: a9acc26b75f ("cpufreq/pasemi: fix possible object reference leak")
Signed-off-by: Wen Yang <wen.yang99@zte.com.cn>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-pm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
v3: fix a leaked reference.
v2: clean up the code according to the advice of viresh.
drivers/cpufreq/pasemi-cpufreq.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c
index 6b1e4ab..9dc5163 100644
--- a/drivers/cpufreq/pasemi-cpufreq.c
+++ b/drivers/cpufreq/pasemi-cpufreq.c
@@ -128,20 +128,20 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
int cur_astate, idx;
struct resource res;
struct device_node *cpu, *dn;
- int err = -ENODEV;
+ int err;
cpu = of_get_cpu_node(policy->cpu, NULL);
-
- of_node_put(cpu);
if (!cpu)
- goto out;
+ return -ENODEV;
dn = of_find_compatible_node(NULL, NULL, "1682m-sdc");
if (!dn)
dn = of_find_compatible_node(NULL, NULL,
"pasemi,pwrficient-sdc");
- if (!dn)
+ if (!dn) {
+ err = -ENODEV;
goto out;
+ }
err = of_address_to_resource(dn, 0, &res);
of_node_put(dn);
if (err)
@@ -196,6 +196,7 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
policy->cur = pas_freqs[cur_astate].frequency;
ppc_proc_freq = policy->cur * 1000ul;
+ of_node_put(cpu);
return cpufreq_generic_init(policy, pas_freqs, get_gizmo_latency());
out_unmap_sdcpwr:
@@ -204,6 +205,7 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
out_unmap_sdcasr:
iounmap(sdcasr_mapbase);
out:
+ of_node_put(cpu);
return err;
}
--
2.9.5
^ permalink raw reply related
* Re: [PATCH v3] cpufreq/pasemi: fix an use-after-free in pas_cpufreq_cpu_init()
From: Viresh Kumar @ 2019-07-08 8:56 UTC (permalink / raw)
To: Wen Yang
Cc: wang.yi59, linux-pm, Rafael J. Wysocki, linux-kernel, xue.zhihong,
cheng.shengyu, linuxppc-dev
In-Reply-To: <1562575726-17438-1-git-send-email-wen.yang99@zte.com.cn>
On 08-07-19, 16:48, Wen Yang wrote:
> The cpu variable is still being used in the of_get_property() call
> after the of_node_put() call, which may result in use-after-free.
>
> Fixes: a9acc26b75f ("cpufreq/pasemi: fix possible object reference leak")
> Signed-off-by: Wen Yang <wen.yang99@zte.com.cn>
> Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
> Cc: Viresh Kumar <viresh.kumar@linaro.org>
> Cc: linuxppc-dev@lists.ozlabs.org
> Cc: linux-pm@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> ---
> v3: fix a leaked reference.
> v2: clean up the code according to the advice of viresh.
>
> drivers/cpufreq/pasemi-cpufreq.c | 12 +++++++-----
> 1 file changed, 7 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c
> index 6b1e4ab..9dc5163 100644
> --- a/drivers/cpufreq/pasemi-cpufreq.c
> +++ b/drivers/cpufreq/pasemi-cpufreq.c
> @@ -128,20 +128,20 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
> int cur_astate, idx;
> struct resource res;
> struct device_node *cpu, *dn;
> - int err = -ENODEV;
> + int err;
>
> cpu = of_get_cpu_node(policy->cpu, NULL);
> -
> - of_node_put(cpu);
> if (!cpu)
> - goto out;
> + return -ENODEV;
>
> dn = of_find_compatible_node(NULL, NULL, "1682m-sdc");
> if (!dn)
> dn = of_find_compatible_node(NULL, NULL,
> "pasemi,pwrficient-sdc");
> - if (!dn)
> + if (!dn) {
> + err = -ENODEV;
> goto out;
> + }
Please restore the blank line here.
> err = of_address_to_resource(dn, 0, &res);
> of_node_put(dn);
> if (err)
> @@ -196,6 +196,7 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
> policy->cur = pas_freqs[cur_astate].frequency;
> ppc_proc_freq = policy->cur * 1000ul;
>
> + of_node_put(cpu);
> return cpufreq_generic_init(policy, pas_freqs, get_gizmo_latency());
>
> out_unmap_sdcpwr:
> @@ -204,6 +205,7 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
> out_unmap_sdcasr:
> iounmap(sdcasr_mapbase);
> out:
> + of_node_put(cpu);
> return err;
> }
>
> --
> 2.9.5
--
viresh
^ permalink raw reply
* Re: [PATCH v3 3/3] powerpc/module64: Use symbolic instructions names.
From: Christophe Leroy @ 2019-07-08 8:58 UTC (permalink / raw)
To: Michael Ellerman, Benjamin Herrenschmidt, Paul Mackerras
Cc: Ulirch Weigand, linuxppc-dev, linux-kernel
In-Reply-To: <87bly5ibsd.fsf@concordia.ellerman.id.au>
Le 08/07/2019 à 02:56, Michael Ellerman a écrit :
> Christophe Leroy <christophe.leroy@c-s.fr> writes:
>> To increase readability/maintainability, replace hard coded
>> instructions values by symbolic names.
>>
>> Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
>> ---
>> v3: fixed warning by adding () in an 'if' around X | Y (unlike said in v2 history, this change was forgotten in v2)
>> v2: rearranged comments
>>
>> arch/powerpc/kernel/module_64.c | 53 +++++++++++++++++++++++++++--------------
>> 1 file changed, 35 insertions(+), 18 deletions(-)
>>
>> diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
>> index c2e1b06253b8..b33a5d5e2d35 100644
>> --- a/arch/powerpc/kernel/module_64.c
>> +++ b/arch/powerpc/kernel/module_64.c
>> @@ -704,18 +711,21 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
> ...
>> /*
>> * If found, replace it with:
>> * addis r2, r12, (.TOC.-func)@ha
>> * addi r2, r12, (.TOC.-func)@l
>> */
>> - ((uint32_t *)location)[0] = 0x3c4c0000 + PPC_HA(value);
>> - ((uint32_t *)location)[1] = 0x38420000 + PPC_LO(value);
>> + ((uint32_t *)location)[0] = PPC_INST_ADDIS | __PPC_RT(R2) |
>> + __PPC_RA(R12) | PPC_HA(value);
>> + ((uint32_t *)location)[1] = PPC_INST_ADDI | __PPC_RT(R2) |
>> + __PPC_RA(R12) | PPC_LO(value);
>> break;
>
> This was crashing and it's amazing how long you can stare at a
> disassembly and not see the difference between `r2` and `r12` :)
Argh, yes. I was misleaded by the comment I guess. Sorry for that and
thanks for fixing.
Christophe
>
> Fixed now.
>
> cheers
>
^ permalink raw reply
* [PATCH using git format-patch -C] kexec: add generic support for elf kernel images
From: Christophe Leroy @ 2019-07-08 10:06 UTC (permalink / raw)
To: kexec; +Cc: linuxppc-dev, deller, Sven Schnelle
From: Sven Schnelle <svens@stackframe.org>
Signed-off-by: Sven Schnelle <svens@stackframe.org>
---
patch generated with 'git format-patch -C' in order to
see the modifications done in kexec_elf.c in addition to
copying it from arch/powerpc/kernel/kexec_elf_64.c
arch/Kconfig | 3 +
arch/powerpc/Kconfig | 1 +
arch/powerpc/kernel/kexec_elf_64.c | 547 +--------------------
include/linux/kexec.h | 35 ++
kernel/Makefile | 1 +
.../kernel/kexec_elf_64.c => kernel/kexec_elf.c | 264 +++-------
6 files changed, 118 insertions(+), 733 deletions(-)
copy arch/powerpc/kernel/kexec_elf_64.c => kernel/kexec_elf.c (64%)
diff --git a/arch/Kconfig b/arch/Kconfig
index 33687dddd86a..c7c75fbc0b79 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -18,6 +18,9 @@ config KEXEC_CORE
select CRASH_CORE
bool
+config KEXEC_ELF
+ bool
+
config HAVE_IMA_KEXEC
bool
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 2d0be82c3061..447b6e3ad999 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -501,6 +501,7 @@ config KEXEC_FILE
select KEXEC_CORE
select HAVE_IMA_KEXEC
select BUILD_BIN2C
+ select KEXEC_ELF
depends on PPC64
depends on CRYPTO=y
depends on CRYPTO_SHA256=y
diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/arch/powerpc/kernel/kexec_elf_64.c
index ba4f18a43ee8..d062c5991722 100644
--- a/arch/powerpc/kernel/kexec_elf_64.c
+++ b/arch/powerpc/kernel/kexec_elf_64.c
@@ -31,539 +31,7 @@
#include <linux/slab.h>
#include <linux/types.h>
-#define PURGATORY_STACK_SIZE (16 * 1024)
-
-#define elf_addr_to_cpu elf64_to_cpu
-
-#ifndef Elf_Rel
-#define Elf_Rel Elf64_Rel
-#endif /* Elf_Rel */
-
-struct elf_info {
- /*
- * Where the ELF binary contents are kept.
- * Memory managed by the user of the struct.
- */
- const char *buffer;
-
- const struct elfhdr *ehdr;
- const struct elf_phdr *proghdrs;
- struct elf_shdr *sechdrs;
-};
-
-static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
-{
- return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
-}
-
-static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
-{
- if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
- value = le64_to_cpu(value);
- else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
- value = be64_to_cpu(value);
-
- return value;
-}
-
-static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
-{
- if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
- value = le16_to_cpu(value);
- else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
- value = be16_to_cpu(value);
-
- return value;
-}
-
-static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
-{
- if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
- value = le32_to_cpu(value);
- else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
- value = be32_to_cpu(value);
-
- return value;
-}
-
-/**
- * elf_is_ehdr_sane - check that it is safe to use the ELF header
- * @buf_len: size of the buffer in which the ELF file is loaded.
- */
-static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
-{
- if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
- pr_debug("Bad program header size.\n");
- return false;
- } else if (ehdr->e_shnum > 0 &&
- ehdr->e_shentsize != sizeof(struct elf_shdr)) {
- pr_debug("Bad section header size.\n");
- return false;
- } else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
- ehdr->e_version != EV_CURRENT) {
- pr_debug("Unknown ELF version.\n");
- return false;
- }
-
- if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
- size_t phdr_size;
-
- /*
- * e_phnum is at most 65535 so calculating the size of the
- * program header cannot overflow.
- */
- phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
-
- /* Sanity check the program header table location. */
- if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
- pr_debug("Program headers at invalid location.\n");
- return false;
- } else if (ehdr->e_phoff + phdr_size > buf_len) {
- pr_debug("Program headers truncated.\n");
- return false;
- }
- }
-
- if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
- size_t shdr_size;
-
- /*
- * e_shnum is at most 65536 so calculating
- * the size of the section header cannot overflow.
- */
- shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
-
- /* Sanity check the section header table location. */
- if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
- pr_debug("Section headers at invalid location.\n");
- return false;
- } else if (ehdr->e_shoff + shdr_size > buf_len) {
- pr_debug("Section headers truncated.\n");
- return false;
- }
- }
-
- return true;
-}
-
-static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
-{
- struct elfhdr *buf_ehdr;
-
- if (len < sizeof(*buf_ehdr)) {
- pr_debug("Buffer is too small to hold ELF header.\n");
- return -ENOEXEC;
- }
-
- memset(ehdr, 0, sizeof(*ehdr));
- memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
- if (!elf_is_elf_file(ehdr)) {
- pr_debug("No ELF header magic.\n");
- return -ENOEXEC;
- }
-
- if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
- pr_debug("Not a supported ELF class.\n");
- return -ENOEXEC;
- } else if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
- ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
- pr_debug("Not a supported ELF data format.\n");
- return -ENOEXEC;
- }
-
- buf_ehdr = (struct elfhdr *) buf;
- if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
- pr_debug("Bad ELF header size.\n");
- return -ENOEXEC;
- }
-
- ehdr->e_type = elf16_to_cpu(ehdr, buf_ehdr->e_type);
- ehdr->e_machine = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
- ehdr->e_version = elf32_to_cpu(ehdr, buf_ehdr->e_version);
- ehdr->e_entry = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
- ehdr->e_phoff = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
- ehdr->e_shoff = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
- ehdr->e_flags = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
- ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
- ehdr->e_phnum = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
- ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
- ehdr->e_shnum = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
- ehdr->e_shstrndx = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
-
- return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_is_phdr_sane - check that it is safe to use the program header
- * @buf_len: size of the buffer in which the ELF file is loaded.
- */
-static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
-{
-
- if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
- pr_debug("ELF segment location wraps around.\n");
- return false;
- } else if (phdr->p_offset + phdr->p_filesz > buf_len) {
- pr_debug("ELF segment not in file.\n");
- return false;
- } else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
- pr_debug("ELF segment address wraps around.\n");
- return false;
- }
-
- return true;
-}
-
-static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
- int idx)
-{
- /* Override the const in proghdrs, we are the ones doing the loading. */
- struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
- const char *pbuf;
- struct elf_phdr *buf_phdr;
-
- pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
- buf_phdr = (struct elf_phdr *) pbuf;
-
- phdr->p_type = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
- phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
- phdr->p_paddr = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
- phdr->p_vaddr = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
- phdr->p_flags = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
-
- /*
- * The following fields have a type equivalent to Elf_Addr
- * both in 32 bit and 64 bit ELF.
- */
- phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
- phdr->p_memsz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
- phdr->p_align = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
-
- return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_read_phdrs - read the program headers from the buffer
- *
- * This function assumes that the program header table was checked for sanity.
- * Use elf_is_ehdr_sane() if it wasn't.
- */
-static int elf_read_phdrs(const char *buf, size_t len,
- struct elf_info *elf_info)
-{
- size_t phdr_size, i;
- const struct elfhdr *ehdr = elf_info->ehdr;
-
- /*
- * e_phnum is at most 65535 so calculating the size of the
- * program header cannot overflow.
- */
- phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
-
- elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
- if (!elf_info->proghdrs)
- return -ENOMEM;
-
- for (i = 0; i < ehdr->e_phnum; i++) {
- int ret;
-
- ret = elf_read_phdr(buf, len, elf_info, i);
- if (ret) {
- kfree(elf_info->proghdrs);
- elf_info->proghdrs = NULL;
- return ret;
- }
- }
-
- return 0;
-}
-
-/**
- * elf_is_shdr_sane - check that it is safe to use the section header
- * @buf_len: size of the buffer in which the ELF file is loaded.
- */
-static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
-{
- bool size_ok;
-
- /* SHT_NULL headers have undefined values, so we can't check them. */
- if (shdr->sh_type == SHT_NULL)
- return true;
-
- /* Now verify sh_entsize */
- switch (shdr->sh_type) {
- case SHT_SYMTAB:
- size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
- break;
- case SHT_RELA:
- size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
- break;
- case SHT_DYNAMIC:
- size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
- break;
- case SHT_REL:
- size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
- break;
- case SHT_NOTE:
- case SHT_PROGBITS:
- case SHT_HASH:
- case SHT_NOBITS:
- default:
- /*
- * This is a section whose entsize requirements
- * I don't care about. If I don't know about
- * the section I can't care about it's entsize
- * requirements.
- */
- size_ok = true;
- break;
- }
-
- if (!size_ok) {
- pr_debug("ELF section with wrong entry size.\n");
- return false;
- } else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
- pr_debug("ELF section address wraps around.\n");
- return false;
- }
-
- if (shdr->sh_type != SHT_NOBITS) {
- if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
- pr_debug("ELF section location wraps around.\n");
- return false;
- } else if (shdr->sh_offset + shdr->sh_size > buf_len) {
- pr_debug("ELF section not in file.\n");
- return false;
- }
- }
-
- return true;
-}
-
-static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
- int idx)
-{
- struct elf_shdr *shdr = &elf_info->sechdrs[idx];
- const struct elfhdr *ehdr = elf_info->ehdr;
- const char *sbuf;
- struct elf_shdr *buf_shdr;
-
- sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
- buf_shdr = (struct elf_shdr *) sbuf;
-
- shdr->sh_name = elf32_to_cpu(ehdr, buf_shdr->sh_name);
- shdr->sh_type = elf32_to_cpu(ehdr, buf_shdr->sh_type);
- shdr->sh_addr = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
- shdr->sh_offset = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
- shdr->sh_link = elf32_to_cpu(ehdr, buf_shdr->sh_link);
- shdr->sh_info = elf32_to_cpu(ehdr, buf_shdr->sh_info);
-
- /*
- * The following fields have a type equivalent to Elf_Addr
- * both in 32 bit and 64 bit ELF.
- */
- shdr->sh_flags = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
- shdr->sh_size = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
- shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
- shdr->sh_entsize = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
-
- return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_read_shdrs - read the section headers from the buffer
- *
- * This function assumes that the section header table was checked for sanity.
- * Use elf_is_ehdr_sane() if it wasn't.
- */
-static int elf_read_shdrs(const char *buf, size_t len,
- struct elf_info *elf_info)
-{
- size_t shdr_size, i;
-
- /*
- * e_shnum is at most 65536 so calculating
- * the size of the section header cannot overflow.
- */
- shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
-
- elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
- if (!elf_info->sechdrs)
- return -ENOMEM;
-
- for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
- int ret;
-
- ret = elf_read_shdr(buf, len, elf_info, i);
- if (ret) {
- kfree(elf_info->sechdrs);
- elf_info->sechdrs = NULL;
- return ret;
- }
- }
-
- return 0;
-}
-
-/**
- * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
- * @buf: Buffer to read ELF file from.
- * @len: Size of @buf.
- * @ehdr: Pointer to existing struct which will be populated.
- * @elf_info: Pointer to existing struct which will be populated.
- *
- * This function allows reading ELF files with different byte order than
- * the kernel, byte-swapping the fields as needed.
- *
- * Return:
- * On success returns 0, and the caller should call elf_free_info(elf_info) to
- * free the memory allocated for the section and program headers.
- */
-int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
- struct elf_info *elf_info)
-{
- int ret;
-
- ret = elf_read_ehdr(buf, len, ehdr);
- if (ret)
- return ret;
-
- elf_info->buffer = buf;
- elf_info->ehdr = ehdr;
- if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
- ret = elf_read_phdrs(buf, len, elf_info);
- if (ret)
- return ret;
- }
- if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
- ret = elf_read_shdrs(buf, len, elf_info);
- if (ret) {
- kfree(elf_info->proghdrs);
- return ret;
- }
- }
-
- return 0;
-}
-
-/**
- * elf_free_info - free memory allocated by elf_read_from_buffer
- */
-void elf_free_info(struct elf_info *elf_info)
-{
- kfree(elf_info->proghdrs);
- kfree(elf_info->sechdrs);
- memset(elf_info, 0, sizeof(*elf_info));
-}
-/**
- * build_elf_exec_info - read ELF executable and check that we can use it
- */
-static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
- struct elf_info *elf_info)
-{
- int i;
- int ret;
-
- ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
- if (ret)
- return ret;
-
- /* Big endian vmlinux has type ET_DYN. */
- if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
- pr_err("Not an ELF executable.\n");
- goto error;
- } else if (!elf_info->proghdrs) {
- pr_err("No ELF program header.\n");
- goto error;
- }
-
- for (i = 0; i < ehdr->e_phnum; i++) {
- /*
- * Kexec does not support loading interpreters.
- * In addition this check keeps us from attempting
- * to kexec ordinay executables.
- */
- if (elf_info->proghdrs[i].p_type == PT_INTERP) {
- pr_err("Requires an ELF interpreter.\n");
- goto error;
- }
- }
-
- return 0;
-error:
- elf_free_info(elf_info);
- return -ENOEXEC;
-}
-
-static int elf64_probe(const char *buf, unsigned long len)
-{
- struct elfhdr ehdr;
- struct elf_info elf_info;
- int ret;
-
- ret = build_elf_exec_info(buf, len, &ehdr, &elf_info);
- if (ret)
- return ret;
-
- elf_free_info(&elf_info);
-
- return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_exec_load - load ELF executable image
- * @lowest_load_addr: On return, will be the address where the first PT_LOAD
- * section will be loaded in memory.
- *
- * Return:
- * 0 on success, negative value on failure.
- */
-static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
- struct elf_info *elf_info,
- unsigned long *lowest_load_addr)
-{
- unsigned long base = 0, lowest_addr = UINT_MAX;
- int ret;
- size_t i;
- struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size,
- .top_down = false };
-
- /* Read in the PT_LOAD segments. */
- for (i = 0; i < ehdr->e_phnum; i++) {
- unsigned long load_addr;
- size_t size;
- const struct elf_phdr *phdr;
-
- phdr = &elf_info->proghdrs[i];
- if (phdr->p_type != PT_LOAD)
- continue;
-
- size = phdr->p_filesz;
- if (size > phdr->p_memsz)
- size = phdr->p_memsz;
-
- kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
- kbuf.bufsz = size;
- kbuf.memsz = phdr->p_memsz;
- kbuf.buf_align = phdr->p_align;
- kbuf.buf_min = phdr->p_paddr + base;
- ret = kexec_add_buffer(&kbuf);
- if (ret)
- goto out;
- load_addr = kbuf.mem;
-
- if (load_addr < lowest_addr)
- lowest_addr = load_addr;
- }
-
- /* Update entry point to reflect new load address. */
- ehdr->e_entry += base;
-
- *lowest_load_addr = lowest_addr;
- ret = 0;
- out:
- return ret;
-}
+#define PURGATORY_STACK_SIZE (16 * 1024)
static void *elf64_load(struct kimage *image, char *kernel_buf,
unsigned long kernel_len, char *initrd,
@@ -577,17 +45,17 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
void *fdt;
const void *slave_code;
struct elfhdr ehdr;
- struct elf_info elf_info;
+ struct kexec_elf_info elf_info;
struct kexec_buf kbuf = { .image = image, .buf_min = 0,
.buf_max = ppc64_rma_size };
struct kexec_buf pbuf = { .image = image, .buf_min = 0,
.buf_max = ppc64_rma_size, .top_down = true };
- ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
+ ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
if (ret)
goto out;
- ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
+ ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
if (ret)
goto out;
@@ -606,6 +74,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
kbuf.bufsz = kbuf.memsz = initrd_len;
kbuf.buf_align = PAGE_SIZE;
kbuf.top_down = false;
+ kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
ret = kexec_add_buffer(&kbuf);
if (ret)
goto out;
@@ -638,6 +107,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
kbuf.bufsz = kbuf.memsz = fdt_size;
kbuf.buf_align = PAGE_SIZE;
kbuf.top_down = true;
+ kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
ret = kexec_add_buffer(&kbuf);
if (ret)
goto out;
@@ -652,13 +122,12 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
pr_err("Error setting up the purgatory.\n");
out:
- elf_free_info(&elf_info);
-
+ kexec_free_elf_info(&elf_info);
/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
return ret ? ERR_PTR(ret) : fdt;
}
const struct kexec_file_ops kexec_elf64_ops = {
- .probe = elf64_probe,
+ .probe = kexec_elf_probe,
.load = elf64_load,
};
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index b9b1bc5f9669..72648b18b778 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -216,6 +216,41 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
void **addr, unsigned long *sz);
#endif /* CONFIG_KEXEC_FILE */
+#ifdef CONFIG_KEXEC_ELF
+
+struct kexec_elf_info {
+ /*
+ * Where the ELF binary contents are kept.
+ * Memory managed by the user of the struct.
+ */
+ const char *buffer;
+
+ const struct elfhdr *ehdr;
+ const struct elf_phdr *proghdrs;
+ struct elf_shdr *sechdrs;
+};
+
+void kexec_free_elf_info(struct kexec_elf_info *elf_info);
+
+int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
+ struct kexec_elf_info *elf_info);
+
+int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
+ char *kernel_buf, unsigned long kernel_len,
+ unsigned long *kernel_load_addr);
+
+int kexec_elf_probe(const char *buf, unsigned long len);
+
+int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
+ struct kexec_elf_info *elf_info,
+ struct kexec_buf *kbuf,
+ unsigned long *lowest_load_addr);
+
+int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
+ struct kexec_elf_info *elf_info,
+ struct kexec_buf *kbuf,
+ unsigned long *lowest_load_addr);
+#endif
struct kimage {
kimage_entry_t head;
kimage_entry_t *entry;
diff --git a/kernel/Makefile b/kernel/Makefile
index 6c57e78817da..081b378050d0 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o
obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
obj-$(CONFIG_KEXEC) += kexec.o
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
+obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
obj-$(CONFIG_COMPAT) += compat.o
obj-$(CONFIG_CGROUPS) += cgroup/
diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/kernel/kexec_elf.c
similarity index 64%
copy from arch/powerpc/kernel/kexec_elf_64.c
copy to kernel/kexec_elf.c
index ba4f18a43ee8..07e5bcc79bba 100644
--- a/arch/powerpc/kernel/kexec_elf_64.c
+++ b/kernel/kexec_elf.c
@@ -1,55 +1,13 @@
-/*
- * Load ELF vmlinux file for the kexec_file_load syscall.
- *
- * Copyright (C) 2004 Adam Litke (agl@us.ibm.com)
- * Copyright (C) 2004 IBM Corp.
- * Copyright (C) 2005 R Sharada (sharada@in.ibm.com)
- * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com)
- * Copyright (C) 2016 IBM Corporation
- *
- * Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
- * Heavily modified for the kernel by
- * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation (version 2 of the License).
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0
#define pr_fmt(fmt) "kexec_elf: " fmt
#include <linux/elf.h>
#include <linux/kexec.h>
-#include <linux/libfdt.h>
#include <linux/module.h>
-#include <linux/of_fdt.h>
#include <linux/slab.h>
#include <linux/types.h>
-#define PURGATORY_STACK_SIZE (16 * 1024)
-
-#define elf_addr_to_cpu elf64_to_cpu
-
-#ifndef Elf_Rel
-#define Elf_Rel Elf64_Rel
-#endif /* Elf_Rel */
-
-struct elf_info {
- /*
- * Where the ELF binary contents are kept.
- * Memory managed by the user of the struct.
- */
- const char *buffer;
-
- const struct elfhdr *ehdr;
- const struct elf_phdr *proghdrs;
- struct elf_shdr *sechdrs;
-};
static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
{
@@ -66,22 +24,22 @@ static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
return value;
}
-static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
+static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
{
if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
- value = le16_to_cpu(value);
+ value = le32_to_cpu(value);
else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
- value = be16_to_cpu(value);
+ value = be32_to_cpu(value);
return value;
}
-static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
+static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
{
if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
- value = le32_to_cpu(value);
+ value = le16_to_cpu(value);
else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
- value = be32_to_cpu(value);
+ value = be16_to_cpu(value);
return value;
}
@@ -180,9 +138,9 @@ static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
ehdr->e_type = elf16_to_cpu(ehdr, buf_ehdr->e_type);
ehdr->e_machine = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
ehdr->e_version = elf32_to_cpu(ehdr, buf_ehdr->e_version);
- ehdr->e_entry = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
- ehdr->e_phoff = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
- ehdr->e_shoff = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
+ ehdr->e_entry = elf64_to_cpu(ehdr, buf_ehdr->e_entry);
+ ehdr->e_phoff = elf64_to_cpu(ehdr, buf_ehdr->e_phoff);
+ ehdr->e_shoff = elf64_to_cpu(ehdr, buf_ehdr->e_shoff);
ehdr->e_flags = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
ehdr->e_phnum = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
@@ -214,7 +172,7 @@ static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
return true;
}
-static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
+static int elf_read_phdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
int idx)
{
/* Override the const in proghdrs, we are the ones doing the loading. */
@@ -226,18 +184,18 @@ static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
buf_phdr = (struct elf_phdr *) pbuf;
phdr->p_type = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
- phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
- phdr->p_paddr = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
- phdr->p_vaddr = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
+ phdr->p_offset = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
+ phdr->p_paddr = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
+ phdr->p_vaddr = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
phdr->p_flags = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
/*
* The following fields have a type equivalent to Elf_Addr
* both in 32 bit and 64 bit ELF.
*/
- phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
- phdr->p_memsz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
- phdr->p_align = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
+ phdr->p_filesz = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
+ phdr->p_memsz = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
+ phdr->p_align = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_align);
return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
}
@@ -249,7 +207,7 @@ static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
* Use elf_is_ehdr_sane() if it wasn't.
*/
static int elf_read_phdrs(const char *buf, size_t len,
- struct elf_info *elf_info)
+ struct kexec_elf_info *elf_info)
{
size_t phdr_size, i;
const struct elfhdr *ehdr = elf_info->ehdr;
@@ -296,13 +254,13 @@ static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
break;
case SHT_RELA:
- size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
+ size_ok = shdr->sh_entsize == sizeof(Elf64_Rela);
break;
case SHT_DYNAMIC:
size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
break;
case SHT_REL:
- size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
+ size_ok = shdr->sh_entsize == sizeof(Elf64_Rel);
break;
case SHT_NOTE:
case SHT_PROGBITS:
@@ -340,7 +298,7 @@ static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
return true;
}
-static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
+static int elf_read_shdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
int idx)
{
struct elf_shdr *shdr = &elf_info->sechdrs[idx];
@@ -353,8 +311,8 @@ static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
shdr->sh_name = elf32_to_cpu(ehdr, buf_shdr->sh_name);
shdr->sh_type = elf32_to_cpu(ehdr, buf_shdr->sh_type);
- shdr->sh_addr = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
- shdr->sh_offset = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
+ shdr->sh_addr = elf64_to_cpu(ehdr, buf_shdr->sh_addr);
+ shdr->sh_offset = elf64_to_cpu(ehdr, buf_shdr->sh_offset);
shdr->sh_link = elf32_to_cpu(ehdr, buf_shdr->sh_link);
shdr->sh_info = elf32_to_cpu(ehdr, buf_shdr->sh_info);
@@ -362,10 +320,10 @@ static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
* The following fields have a type equivalent to Elf_Addr
* both in 32 bit and 64 bit ELF.
*/
- shdr->sh_flags = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
- shdr->sh_size = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
- shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
- shdr->sh_entsize = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
+ shdr->sh_flags = elf64_to_cpu(ehdr, buf_shdr->sh_flags);
+ shdr->sh_size = elf64_to_cpu(ehdr, buf_shdr->sh_size);
+ shdr->sh_addralign = elf64_to_cpu(ehdr, buf_shdr->sh_addralign);
+ shdr->sh_entsize = elf64_to_cpu(ehdr, buf_shdr->sh_entsize);
return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
}
@@ -377,7 +335,7 @@ static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
* Use elf_is_ehdr_sane() if it wasn't.
*/
static int elf_read_shdrs(const char *buf, size_t len,
- struct elf_info *elf_info)
+ struct kexec_elf_info *elf_info)
{
size_t shdr_size, i;
@@ -416,11 +374,11 @@ static int elf_read_shdrs(const char *buf, size_t len,
* the kernel, byte-swapping the fields as needed.
*
* Return:
- * On success returns 0, and the caller should call elf_free_info(elf_info) to
+ * On success returns 0, and the caller should call kexec_free_elf_info(elf_info) to
* free the memory allocated for the section and program headers.
*/
int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
- struct elf_info *elf_info)
+ struct kexec_elf_info *elf_info)
{
int ret;
@@ -447,19 +405,19 @@ int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
}
/**
- * elf_free_info - free memory allocated by elf_read_from_buffer
+ * kexec_free_elf_info - free memory allocated by elf_read_from_buffer
*/
-void elf_free_info(struct elf_info *elf_info)
+void kexec_free_elf_info(struct kexec_elf_info *elf_info)
{
kfree(elf_info->proghdrs);
- kfree(elf_info->sechdrs);
memset(elf_info, 0, sizeof(*elf_info));
}
+
/**
- * build_elf_exec_info - read ELF executable and check that we can use it
+ * kexec_build_elf_info - read ELF executable and check that we can use it
*/
-static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
- struct elf_info *elf_info)
+int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
+ struct kexec_elf_info *elf_info)
{
int i;
int ret;
@@ -491,25 +449,10 @@ static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
return 0;
error:
- elf_free_info(elf_info);
+ kexec_free_elf_info(elf_info);
return -ENOEXEC;
}
-static int elf64_probe(const char *buf, unsigned long len)
-{
- struct elfhdr ehdr;
- struct elf_info elf_info;
- int ret;
-
- ret = build_elf_exec_info(buf, len, &ehdr, &elf_info);
- if (ret)
- return ret;
-
- elf_free_info(&elf_info);
-
- return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
-}
-
/**
* elf_exec_load - load ELF executable image
* @lowest_load_addr: On return, will be the address where the first PT_LOAD
@@ -518,16 +461,14 @@ static int elf64_probe(const char *buf, unsigned long len)
* Return:
* 0 on success, negative value on failure.
*/
-static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
- struct elf_info *elf_info,
- unsigned long *lowest_load_addr)
+int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
+ struct kexec_elf_info *elf_info,
+ struct kexec_buf *kbuf,
+ unsigned long *lowest_load_addr)
{
- unsigned long base = 0, lowest_addr = UINT_MAX;
+ unsigned long lowest_addr = UINT_MAX;
int ret;
size_t i;
- struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size,
- .top_down = false };
-
/* Read in the PT_LOAD segments. */
for (i = 0; i < ehdr->e_phnum; i++) {
unsigned long load_addr;
@@ -542,123 +483,58 @@ static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
if (size > phdr->p_memsz)
size = phdr->p_memsz;
- kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
- kbuf.bufsz = size;
- kbuf.memsz = phdr->p_memsz;
- kbuf.buf_align = phdr->p_align;
- kbuf.buf_min = phdr->p_paddr + base;
- ret = kexec_add_buffer(&kbuf);
+ kbuf->buffer = (void *) elf_info->buffer + phdr->p_offset;
+ kbuf->bufsz = size;
+ kbuf->memsz = phdr->p_memsz;
+ kbuf->buf_align = phdr->p_align;
+ kbuf->buf_min = phdr->p_paddr;
+ kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
+ ret = kexec_add_buffer(kbuf);
if (ret)
goto out;
- load_addr = kbuf.mem;
+ load_addr = kbuf->mem;
if (load_addr < lowest_addr)
lowest_addr = load_addr;
}
- /* Update entry point to reflect new load address. */
- ehdr->e_entry += base;
-
+ image->start = ehdr->e_entry;
*lowest_load_addr = lowest_addr;
ret = 0;
out:
return ret;
}
-static void *elf64_load(struct kimage *image, char *kernel_buf,
- unsigned long kernel_len, char *initrd,
- unsigned long initrd_len, char *cmdline,
- unsigned long cmdline_len)
+int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
+ char *kernel_buf, unsigned long kernel_len,
+ unsigned long *kernel_load_addr)
{
int ret;
- unsigned int fdt_size;
- unsigned long kernel_load_addr;
- unsigned long initrd_load_addr = 0, fdt_load_addr;
- void *fdt;
- const void *slave_code;
struct elfhdr ehdr;
- struct elf_info elf_info;
- struct kexec_buf kbuf = { .image = image, .buf_min = 0,
- .buf_max = ppc64_rma_size };
- struct kexec_buf pbuf = { .image = image, .buf_min = 0,
- .buf_max = ppc64_rma_size, .top_down = true };
-
- ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
- if (ret)
- goto out;
+ struct kexec_elf_info elf_info;
- ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
+ ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
if (ret)
goto out;
- pr_debug("Loaded the kernel at 0x%lx\n", kernel_load_addr);
-
- ret = kexec_load_purgatory(image, &pbuf);
- if (ret) {
- pr_err("Loading purgatory failed.\n");
- goto out;
- }
-
- pr_debug("Loaded purgatory at 0x%lx\n", pbuf.mem);
-
- if (initrd != NULL) {
- kbuf.buffer = initrd;
- kbuf.bufsz = kbuf.memsz = initrd_len;
- kbuf.buf_align = PAGE_SIZE;
- kbuf.top_down = false;
- ret = kexec_add_buffer(&kbuf);
- if (ret)
- goto out;
- initrd_load_addr = kbuf.mem;
-
- pr_debug("Loaded initrd at 0x%lx\n", initrd_load_addr);
- }
-
- fdt_size = fdt_totalsize(initial_boot_params) * 2;
- fdt = kmalloc(fdt_size, GFP_KERNEL);
- if (!fdt) {
- pr_err("Not enough memory for the device tree.\n");
- ret = -ENOMEM;
- goto out;
- }
- ret = fdt_open_into(initial_boot_params, fdt, fdt_size);
- if (ret < 0) {
- pr_err("Error setting up the new device tree.\n");
- ret = -EINVAL;
- goto out;
- }
-
- ret = setup_new_fdt(image, fdt, initrd_load_addr, initrd_len, cmdline);
- if (ret)
- goto out;
-
- fdt_pack(fdt);
+ ret = kexec_elf_load(image, &ehdr, &elf_info, kbuf, kernel_load_addr);
+out:
+ kexec_free_elf_info(&elf_info);
+ return ret;
- kbuf.buffer = fdt;
- kbuf.bufsz = kbuf.memsz = fdt_size;
- kbuf.buf_align = PAGE_SIZE;
- kbuf.top_down = true;
- ret = kexec_add_buffer(&kbuf);
- if (ret)
- goto out;
- fdt_load_addr = kbuf.mem;
+}
- pr_debug("Loaded device tree at 0x%lx\n", fdt_load_addr);
+int kexec_elf_probe(const char *buf, unsigned long len)
+{
+ struct elfhdr ehdr;
+ struct kexec_elf_info elf_info;
+ int ret;
- slave_code = elf_info.buffer + elf_info.proghdrs[0].p_offset;
- ret = setup_purgatory(image, slave_code, fdt, kernel_load_addr,
- fdt_load_addr);
+ ret = kexec_build_elf_info(buf, len, &ehdr, &elf_info);
if (ret)
- pr_err("Error setting up the purgatory.\n");
+ return ret;
-out:
- elf_free_info(&elf_info);
+ kexec_free_elf_info(&elf_info);
- /* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
- return ret ? ERR_PTR(ret) : fdt;
+ return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
}
-
-const struct kexec_file_ops kexec_elf64_ops = {
- .probe = elf64_probe,
- .load = elf64_load,
-};
--
2.13.3
^ permalink raw reply related
* Re: [PATCH] kexec: add generic support for elf kernel images
From: Christophe Leroy @ 2019-07-08 10:12 UTC (permalink / raw)
To: Sven Schnelle, kexec; +Cc: deller, linuxppc-dev
In-Reply-To: <20190707192153.7821-1-svens@stackframe.org>
Le 07/07/2019 à 21:21, Sven Schnelle a écrit :
> Signed-off-by: Sven Schnelle <svens@stackframe.org>
> ---
Please add here a description of the changes done since RFC.
> arch/Kconfig | 3 +
> arch/powerpc/Kconfig | 1 +
> arch/powerpc/kernel/kexec_elf_64.c | 547 +----------------------------
> include/linux/kexec.h | 35 ++
> kernel/Makefile | 1 +
> kernel/kexec_elf.c | 540 ++++++++++++++++++++++++++++
> 6 files changed, 588 insertions(+), 539 deletions(-)
> create mode 100644 kernel/kexec_elf.c
kernel/kexec_elf.c is a modified copy of arch/powerpc/kernel/kexec_elf_64.c.
You should generate your patch usign 'git format-patch -C' in order to
let git identify the copy and the changes to the copy.
That would ease the review. I have regenerated your patch with -C and
resent it.
Christophe
>
> diff --git a/arch/Kconfig b/arch/Kconfig
> index c47b328eada0..30694aca4316 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -18,6 +18,9 @@ config KEXEC_CORE
> select CRASH_CORE
> bool
>
> +config KEXEC_ELF
> + bool
> +
> config HAVE_IMA_KEXEC
> bool
>
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 8c1c636308c8..97aa81622452 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -502,6 +502,7 @@ config KEXEC_FILE
> select KEXEC_CORE
> select HAVE_IMA_KEXEC
> select BUILD_BIN2C
> + select KEXEC_ELF
> depends on PPC64
> depends on CRYPTO=y
> depends on CRYPTO_SHA256=y
> diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/arch/powerpc/kernel/kexec_elf_64.c
> index ba4f18a43ee8..d062c5991722 100644
> --- a/arch/powerpc/kernel/kexec_elf_64.c
> +++ b/arch/powerpc/kernel/kexec_elf_64.c
> @@ -31,539 +31,7 @@
> #include <linux/slab.h>
> #include <linux/types.h>
>
> -#define PURGATORY_STACK_SIZE (16 * 1024)
> -
> -#define elf_addr_to_cpu elf64_to_cpu
> -
> -#ifndef Elf_Rel
> -#define Elf_Rel Elf64_Rel
> -#endif /* Elf_Rel */
> -
> -struct elf_info {
> - /*
> - * Where the ELF binary contents are kept.
> - * Memory managed by the user of the struct.
> - */
> - const char *buffer;
> -
> - const struct elfhdr *ehdr;
> - const struct elf_phdr *proghdrs;
> - struct elf_shdr *sechdrs;
> -};
> -
> -static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
> -{
> - return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
> -}
> -
> -static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
> -{
> - if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> - value = le64_to_cpu(value);
> - else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> - value = be64_to_cpu(value);
> -
> - return value;
> -}
> -
> -static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
> -{
> - if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> - value = le16_to_cpu(value);
> - else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> - value = be16_to_cpu(value);
> -
> - return value;
> -}
> -
> -static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
> -{
> - if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> - value = le32_to_cpu(value);
> - else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> - value = be32_to_cpu(value);
> -
> - return value;
> -}
> -
> -/**
> - * elf_is_ehdr_sane - check that it is safe to use the ELF header
> - * @buf_len: size of the buffer in which the ELF file is loaded.
> - */
> -static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
> -{
> - if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
> - pr_debug("Bad program header size.\n");
> - return false;
> - } else if (ehdr->e_shnum > 0 &&
> - ehdr->e_shentsize != sizeof(struct elf_shdr)) {
> - pr_debug("Bad section header size.\n");
> - return false;
> - } else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
> - ehdr->e_version != EV_CURRENT) {
> - pr_debug("Unknown ELF version.\n");
> - return false;
> - }
> -
> - if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> - size_t phdr_size;
> -
> - /*
> - * e_phnum is at most 65535 so calculating the size of the
> - * program header cannot overflow.
> - */
> - phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> -
> - /* Sanity check the program header table location. */
> - if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
> - pr_debug("Program headers at invalid location.\n");
> - return false;
> - } else if (ehdr->e_phoff + phdr_size > buf_len) {
> - pr_debug("Program headers truncated.\n");
> - return false;
> - }
> - }
> -
> - if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> - size_t shdr_size;
> -
> - /*
> - * e_shnum is at most 65536 so calculating
> - * the size of the section header cannot overflow.
> - */
> - shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
> -
> - /* Sanity check the section header table location. */
> - if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
> - pr_debug("Section headers at invalid location.\n");
> - return false;
> - } else if (ehdr->e_shoff + shdr_size > buf_len) {
> - pr_debug("Section headers truncated.\n");
> - return false;
> - }
> - }
> -
> - return true;
> -}
> -
> -static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
> -{
> - struct elfhdr *buf_ehdr;
> -
> - if (len < sizeof(*buf_ehdr)) {
> - pr_debug("Buffer is too small to hold ELF header.\n");
> - return -ENOEXEC;
> - }
> -
> - memset(ehdr, 0, sizeof(*ehdr));
> - memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
> - if (!elf_is_elf_file(ehdr)) {
> - pr_debug("No ELF header magic.\n");
> - return -ENOEXEC;
> - }
> -
> - if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
> - pr_debug("Not a supported ELF class.\n");
> - return -ENOEXEC;
> - } else if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
> - ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
> - pr_debug("Not a supported ELF data format.\n");
> - return -ENOEXEC;
> - }
> -
> - buf_ehdr = (struct elfhdr *) buf;
> - if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
> - pr_debug("Bad ELF header size.\n");
> - return -ENOEXEC;
> - }
> -
> - ehdr->e_type = elf16_to_cpu(ehdr, buf_ehdr->e_type);
> - ehdr->e_machine = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
> - ehdr->e_version = elf32_to_cpu(ehdr, buf_ehdr->e_version);
> - ehdr->e_entry = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
> - ehdr->e_phoff = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
> - ehdr->e_shoff = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
> - ehdr->e_flags = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
> - ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
> - ehdr->e_phnum = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
> - ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
> - ehdr->e_shnum = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
> - ehdr->e_shstrndx = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
> -
> - return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_is_phdr_sane - check that it is safe to use the program header
> - * @buf_len: size of the buffer in which the ELF file is loaded.
> - */
> -static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
> -{
> -
> - if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
> - pr_debug("ELF segment location wraps around.\n");
> - return false;
> - } else if (phdr->p_offset + phdr->p_filesz > buf_len) {
> - pr_debug("ELF segment not in file.\n");
> - return false;
> - } else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
> - pr_debug("ELF segment address wraps around.\n");
> - return false;
> - }
> -
> - return true;
> -}
> -
> -static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
> - int idx)
> -{
> - /* Override the const in proghdrs, we are the ones doing the loading. */
> - struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
> - const char *pbuf;
> - struct elf_phdr *buf_phdr;
> -
> - pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
> - buf_phdr = (struct elf_phdr *) pbuf;
> -
> - phdr->p_type = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
> - phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
> - phdr->p_paddr = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
> - phdr->p_vaddr = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
> - phdr->p_flags = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
> -
> - /*
> - * The following fields have a type equivalent to Elf_Addr
> - * both in 32 bit and 64 bit ELF.
> - */
> - phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
> - phdr->p_memsz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
> - phdr->p_align = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
> -
> - return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_read_phdrs - read the program headers from the buffer
> - *
> - * This function assumes that the program header table was checked for sanity.
> - * Use elf_is_ehdr_sane() if it wasn't.
> - */
> -static int elf_read_phdrs(const char *buf, size_t len,
> - struct elf_info *elf_info)
> -{
> - size_t phdr_size, i;
> - const struct elfhdr *ehdr = elf_info->ehdr;
> -
> - /*
> - * e_phnum is at most 65535 so calculating the size of the
> - * program header cannot overflow.
> - */
> - phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> -
> - elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
> - if (!elf_info->proghdrs)
> - return -ENOMEM;
> -
> - for (i = 0; i < ehdr->e_phnum; i++) {
> - int ret;
> -
> - ret = elf_read_phdr(buf, len, elf_info, i);
> - if (ret) {
> - kfree(elf_info->proghdrs);
> - elf_info->proghdrs = NULL;
> - return ret;
> - }
> - }
> -
> - return 0;
> -}
> -
> -/**
> - * elf_is_shdr_sane - check that it is safe to use the section header
> - * @buf_len: size of the buffer in which the ELF file is loaded.
> - */
> -static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
> -{
> - bool size_ok;
> -
> - /* SHT_NULL headers have undefined values, so we can't check them. */
> - if (shdr->sh_type == SHT_NULL)
> - return true;
> -
> - /* Now verify sh_entsize */
> - switch (shdr->sh_type) {
> - case SHT_SYMTAB:
> - size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
> - break;
> - case SHT_RELA:
> - size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
> - break;
> - case SHT_DYNAMIC:
> - size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
> - break;
> - case SHT_REL:
> - size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
> - break;
> - case SHT_NOTE:
> - case SHT_PROGBITS:
> - case SHT_HASH:
> - case SHT_NOBITS:
> - default:
> - /*
> - * This is a section whose entsize requirements
> - * I don't care about. If I don't know about
> - * the section I can't care about it's entsize
> - * requirements.
> - */
> - size_ok = true;
> - break;
> - }
> -
> - if (!size_ok) {
> - pr_debug("ELF section with wrong entry size.\n");
> - return false;
> - } else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
> - pr_debug("ELF section address wraps around.\n");
> - return false;
> - }
> -
> - if (shdr->sh_type != SHT_NOBITS) {
> - if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
> - pr_debug("ELF section location wraps around.\n");
> - return false;
> - } else if (shdr->sh_offset + shdr->sh_size > buf_len) {
> - pr_debug("ELF section not in file.\n");
> - return false;
> - }
> - }
> -
> - return true;
> -}
> -
> -static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
> - int idx)
> -{
> - struct elf_shdr *shdr = &elf_info->sechdrs[idx];
> - const struct elfhdr *ehdr = elf_info->ehdr;
> - const char *sbuf;
> - struct elf_shdr *buf_shdr;
> -
> - sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
> - buf_shdr = (struct elf_shdr *) sbuf;
> -
> - shdr->sh_name = elf32_to_cpu(ehdr, buf_shdr->sh_name);
> - shdr->sh_type = elf32_to_cpu(ehdr, buf_shdr->sh_type);
> - shdr->sh_addr = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
> - shdr->sh_offset = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
> - shdr->sh_link = elf32_to_cpu(ehdr, buf_shdr->sh_link);
> - shdr->sh_info = elf32_to_cpu(ehdr, buf_shdr->sh_info);
> -
> - /*
> - * The following fields have a type equivalent to Elf_Addr
> - * both in 32 bit and 64 bit ELF.
> - */
> - shdr->sh_flags = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
> - shdr->sh_size = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
> - shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
> - shdr->sh_entsize = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
> -
> - return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_read_shdrs - read the section headers from the buffer
> - *
> - * This function assumes that the section header table was checked for sanity.
> - * Use elf_is_ehdr_sane() if it wasn't.
> - */
> -static int elf_read_shdrs(const char *buf, size_t len,
> - struct elf_info *elf_info)
> -{
> - size_t shdr_size, i;
> -
> - /*
> - * e_shnum is at most 65536 so calculating
> - * the size of the section header cannot overflow.
> - */
> - shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
> -
> - elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
> - if (!elf_info->sechdrs)
> - return -ENOMEM;
> -
> - for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
> - int ret;
> -
> - ret = elf_read_shdr(buf, len, elf_info, i);
> - if (ret) {
> - kfree(elf_info->sechdrs);
> - elf_info->sechdrs = NULL;
> - return ret;
> - }
> - }
> -
> - return 0;
> -}
> -
> -/**
> - * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
> - * @buf: Buffer to read ELF file from.
> - * @len: Size of @buf.
> - * @ehdr: Pointer to existing struct which will be populated.
> - * @elf_info: Pointer to existing struct which will be populated.
> - *
> - * This function allows reading ELF files with different byte order than
> - * the kernel, byte-swapping the fields as needed.
> - *
> - * Return:
> - * On success returns 0, and the caller should call elf_free_info(elf_info) to
> - * free the memory allocated for the section and program headers.
> - */
> -int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
> - struct elf_info *elf_info)
> -{
> - int ret;
> -
> - ret = elf_read_ehdr(buf, len, ehdr);
> - if (ret)
> - return ret;
> -
> - elf_info->buffer = buf;
> - elf_info->ehdr = ehdr;
> - if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> - ret = elf_read_phdrs(buf, len, elf_info);
> - if (ret)
> - return ret;
> - }
> - if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> - ret = elf_read_shdrs(buf, len, elf_info);
> - if (ret) {
> - kfree(elf_info->proghdrs);
> - return ret;
> - }
> - }
> -
> - return 0;
> -}
> -
> -/**
> - * elf_free_info - free memory allocated by elf_read_from_buffer
> - */
> -void elf_free_info(struct elf_info *elf_info)
> -{
> - kfree(elf_info->proghdrs);
> - kfree(elf_info->sechdrs);
> - memset(elf_info, 0, sizeof(*elf_info));
> -}
> -/**
> - * build_elf_exec_info - read ELF executable and check that we can use it
> - */
> -static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
> - struct elf_info *elf_info)
> -{
> - int i;
> - int ret;
> -
> - ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
> - if (ret)
> - return ret;
> -
> - /* Big endian vmlinux has type ET_DYN. */
> - if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
> - pr_err("Not an ELF executable.\n");
> - goto error;
> - } else if (!elf_info->proghdrs) {
> - pr_err("No ELF program header.\n");
> - goto error;
> - }
> -
> - for (i = 0; i < ehdr->e_phnum; i++) {
> - /*
> - * Kexec does not support loading interpreters.
> - * In addition this check keeps us from attempting
> - * to kexec ordinay executables.
> - */
> - if (elf_info->proghdrs[i].p_type == PT_INTERP) {
> - pr_err("Requires an ELF interpreter.\n");
> - goto error;
> - }
> - }
> -
> - return 0;
> -error:
> - elf_free_info(elf_info);
> - return -ENOEXEC;
> -}
> -
> -static int elf64_probe(const char *buf, unsigned long len)
> -{
> - struct elfhdr ehdr;
> - struct elf_info elf_info;
> - int ret;
> -
> - ret = build_elf_exec_info(buf, len, &ehdr, &elf_info);
> - if (ret)
> - return ret;
> -
> - elf_free_info(&elf_info);
> -
> - return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_exec_load - load ELF executable image
> - * @lowest_load_addr: On return, will be the address where the first PT_LOAD
> - * section will be loaded in memory.
> - *
> - * Return:
> - * 0 on success, negative value on failure.
> - */
> -static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
> - struct elf_info *elf_info,
> - unsigned long *lowest_load_addr)
> -{
> - unsigned long base = 0, lowest_addr = UINT_MAX;
> - int ret;
> - size_t i;
> - struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size,
> - .top_down = false };
> -
> - /* Read in the PT_LOAD segments. */
> - for (i = 0; i < ehdr->e_phnum; i++) {
> - unsigned long load_addr;
> - size_t size;
> - const struct elf_phdr *phdr;
> -
> - phdr = &elf_info->proghdrs[i];
> - if (phdr->p_type != PT_LOAD)
> - continue;
> -
> - size = phdr->p_filesz;
> - if (size > phdr->p_memsz)
> - size = phdr->p_memsz;
> -
> - kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
> - kbuf.bufsz = size;
> - kbuf.memsz = phdr->p_memsz;
> - kbuf.buf_align = phdr->p_align;
> - kbuf.buf_min = phdr->p_paddr + base;
> - ret = kexec_add_buffer(&kbuf);
> - if (ret)
> - goto out;
> - load_addr = kbuf.mem;
> -
> - if (load_addr < lowest_addr)
> - lowest_addr = load_addr;
> - }
> -
> - /* Update entry point to reflect new load address. */
> - ehdr->e_entry += base;
> -
> - *lowest_load_addr = lowest_addr;
> - ret = 0;
> - out:
> - return ret;
> -}
> +#define PURGATORY_STACK_SIZE (16 * 1024)
>
> static void *elf64_load(struct kimage *image, char *kernel_buf,
> unsigned long kernel_len, char *initrd,
> @@ -577,17 +45,17 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
> void *fdt;
> const void *slave_code;
> struct elfhdr ehdr;
> - struct elf_info elf_info;
> + struct kexec_elf_info elf_info;
> struct kexec_buf kbuf = { .image = image, .buf_min = 0,
> .buf_max = ppc64_rma_size };
> struct kexec_buf pbuf = { .image = image, .buf_min = 0,
> .buf_max = ppc64_rma_size, .top_down = true };
>
> - ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
> + ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
> if (ret)
> goto out;
>
> - ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
> + ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
> if (ret)
> goto out;
>
> @@ -606,6 +74,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
> kbuf.bufsz = kbuf.memsz = initrd_len;
> kbuf.buf_align = PAGE_SIZE;
> kbuf.top_down = false;
> + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
> ret = kexec_add_buffer(&kbuf);
> if (ret)
> goto out;
> @@ -638,6 +107,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
> kbuf.bufsz = kbuf.memsz = fdt_size;
> kbuf.buf_align = PAGE_SIZE;
> kbuf.top_down = true;
> + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
> ret = kexec_add_buffer(&kbuf);
> if (ret)
> goto out;
> @@ -652,13 +122,12 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
> pr_err("Error setting up the purgatory.\n");
>
> out:
> - elf_free_info(&elf_info);
> -
> + kexec_free_elf_info(&elf_info);
> /* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
> return ret ? ERR_PTR(ret) : fdt;
> }
>
> const struct kexec_file_ops kexec_elf64_ops = {
> - .probe = elf64_probe,
> + .probe = kexec_elf_probe,
> .load = elf64_load,
> };
> diff --git a/include/linux/kexec.h b/include/linux/kexec.h
> index b9b1bc5f9669..72648b18b778 100644
> --- a/include/linux/kexec.h
> +++ b/include/linux/kexec.h
> @@ -216,6 +216,41 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
> void **addr, unsigned long *sz);
> #endif /* CONFIG_KEXEC_FILE */
>
> +#ifdef CONFIG_KEXEC_ELF
> +
> +struct kexec_elf_info {
> + /*
> + * Where the ELF binary contents are kept.
> + * Memory managed by the user of the struct.
> + */
> + const char *buffer;
> +
> + const struct elfhdr *ehdr;
> + const struct elf_phdr *proghdrs;
> + struct elf_shdr *sechdrs;
> +};
> +
> +void kexec_free_elf_info(struct kexec_elf_info *elf_info);
> +
> +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> + struct kexec_elf_info *elf_info);
> +
> +int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
> + char *kernel_buf, unsigned long kernel_len,
> + unsigned long *kernel_load_addr);
> +
> +int kexec_elf_probe(const char *buf, unsigned long len);
> +
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> + struct kexec_elf_info *elf_info,
> + struct kexec_buf *kbuf,
> + unsigned long *lowest_load_addr);
> +
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> + struct kexec_elf_info *elf_info,
> + struct kexec_buf *kbuf,
> + unsigned long *lowest_load_addr);
> +#endif
> struct kimage {
> kimage_entry_t head;
> kimage_entry_t *entry;
> diff --git a/kernel/Makefile b/kernel/Makefile
> index 33824f0385b3..7062306de9b7 100644
> --- a/kernel/Makefile
> +++ b/kernel/Makefile
> @@ -64,6 +64,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o
> obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
> obj-$(CONFIG_KEXEC) += kexec.o
> obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
> +obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
> obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
> obj-$(CONFIG_COMPAT) += compat.o
> obj-$(CONFIG_CGROUPS) += cgroup/
> diff --git a/kernel/kexec_elf.c b/kernel/kexec_elf.c
> new file mode 100644
> index 000000000000..07e5bcc79bba
> --- /dev/null
> +++ b/kernel/kexec_elf.c
> @@ -0,0 +1,540 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#define pr_fmt(fmt) "kexec_elf: " fmt
> +
> +#include <linux/elf.h>
> +#include <linux/kexec.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +
> +static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
> +{
> + return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
> +}
> +
> +static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
> +{
> + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> + value = le64_to_cpu(value);
> + else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> + value = be64_to_cpu(value);
> +
> + return value;
> +}
> +
> +static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
> +{
> + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> + value = le32_to_cpu(value);
> + else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> + value = be32_to_cpu(value);
> +
> + return value;
> +}
> +
> +static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
> +{
> + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> + value = le16_to_cpu(value);
> + else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> + value = be16_to_cpu(value);
> +
> + return value;
> +}
> +
> +/**
> + * elf_is_ehdr_sane - check that it is safe to use the ELF header
> + * @buf_len: size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
> +{
> + if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
> + pr_debug("Bad program header size.\n");
> + return false;
> + } else if (ehdr->e_shnum > 0 &&
> + ehdr->e_shentsize != sizeof(struct elf_shdr)) {
> + pr_debug("Bad section header size.\n");
> + return false;
> + } else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
> + ehdr->e_version != EV_CURRENT) {
> + pr_debug("Unknown ELF version.\n");
> + return false;
> + }
> +
> + if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> + size_t phdr_size;
> +
> + /*
> + * e_phnum is at most 65535 so calculating the size of the
> + * program header cannot overflow.
> + */
> + phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> +
> + /* Sanity check the program header table location. */
> + if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
> + pr_debug("Program headers at invalid location.\n");
> + return false;
> + } else if (ehdr->e_phoff + phdr_size > buf_len) {
> + pr_debug("Program headers truncated.\n");
> + return false;
> + }
> + }
> +
> + if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> + size_t shdr_size;
> +
> + /*
> + * e_shnum is at most 65536 so calculating
> + * the size of the section header cannot overflow.
> + */
> + shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
> +
> + /* Sanity check the section header table location. */
> + if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
> + pr_debug("Section headers at invalid location.\n");
> + return false;
> + } else if (ehdr->e_shoff + shdr_size > buf_len) {
> + pr_debug("Section headers truncated.\n");
> + return false;
> + }
> + }
> +
> + return true;
> +}
> +
> +static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
> +{
> + struct elfhdr *buf_ehdr;
> +
> + if (len < sizeof(*buf_ehdr)) {
> + pr_debug("Buffer is too small to hold ELF header.\n");
> + return -ENOEXEC;
> + }
> +
> + memset(ehdr, 0, sizeof(*ehdr));
> + memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
> + if (!elf_is_elf_file(ehdr)) {
> + pr_debug("No ELF header magic.\n");
> + return -ENOEXEC;
> + }
> +
> + if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
> + pr_debug("Not a supported ELF class.\n");
> + return -ENOEXEC;
> + } else if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
> + ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
> + pr_debug("Not a supported ELF data format.\n");
> + return -ENOEXEC;
> + }
> +
> + buf_ehdr = (struct elfhdr *) buf;
> + if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
> + pr_debug("Bad ELF header size.\n");
> + return -ENOEXEC;
> + }
> +
> + ehdr->e_type = elf16_to_cpu(ehdr, buf_ehdr->e_type);
> + ehdr->e_machine = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
> + ehdr->e_version = elf32_to_cpu(ehdr, buf_ehdr->e_version);
> + ehdr->e_entry = elf64_to_cpu(ehdr, buf_ehdr->e_entry);
> + ehdr->e_phoff = elf64_to_cpu(ehdr, buf_ehdr->e_phoff);
> + ehdr->e_shoff = elf64_to_cpu(ehdr, buf_ehdr->e_shoff);
> + ehdr->e_flags = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
> + ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
> + ehdr->e_phnum = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
> + ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
> + ehdr->e_shnum = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
> + ehdr->e_shstrndx = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
> +
> + return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_is_phdr_sane - check that it is safe to use the program header
> + * @buf_len: size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
> +{
> +
> + if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
> + pr_debug("ELF segment location wraps around.\n");
> + return false;
> + } else if (phdr->p_offset + phdr->p_filesz > buf_len) {
> + pr_debug("ELF segment not in file.\n");
> + return false;
> + } else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
> + pr_debug("ELF segment address wraps around.\n");
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static int elf_read_phdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
> + int idx)
> +{
> + /* Override the const in proghdrs, we are the ones doing the loading. */
> + struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
> + const char *pbuf;
> + struct elf_phdr *buf_phdr;
> +
> + pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
> + buf_phdr = (struct elf_phdr *) pbuf;
> +
> + phdr->p_type = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
> + phdr->p_offset = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
> + phdr->p_paddr = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
> + phdr->p_vaddr = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
> + phdr->p_flags = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
> +
> + /*
> + * The following fields have a type equivalent to Elf_Addr
> + * both in 32 bit and 64 bit ELF.
> + */
> + phdr->p_filesz = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
> + phdr->p_memsz = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
> + phdr->p_align = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_align);
> +
> + return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_read_phdrs - read the program headers from the buffer
> + *
> + * This function assumes that the program header table was checked for sanity.
> + * Use elf_is_ehdr_sane() if it wasn't.
> + */
> +static int elf_read_phdrs(const char *buf, size_t len,
> + struct kexec_elf_info *elf_info)
> +{
> + size_t phdr_size, i;
> + const struct elfhdr *ehdr = elf_info->ehdr;
> +
> + /*
> + * e_phnum is at most 65535 so calculating the size of the
> + * program header cannot overflow.
> + */
> + phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> +
> + elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
> + if (!elf_info->proghdrs)
> + return -ENOMEM;
> +
> + for (i = 0; i < ehdr->e_phnum; i++) {
> + int ret;
> +
> + ret = elf_read_phdr(buf, len, elf_info, i);
> + if (ret) {
> + kfree(elf_info->proghdrs);
> + elf_info->proghdrs = NULL;
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * elf_is_shdr_sane - check that it is safe to use the section header
> + * @buf_len: size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
> +{
> + bool size_ok;
> +
> + /* SHT_NULL headers have undefined values, so we can't check them. */
> + if (shdr->sh_type == SHT_NULL)
> + return true;
> +
> + /* Now verify sh_entsize */
> + switch (shdr->sh_type) {
> + case SHT_SYMTAB:
> + size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
> + break;
> + case SHT_RELA:
> + size_ok = shdr->sh_entsize == sizeof(Elf64_Rela);
> + break;
> + case SHT_DYNAMIC:
> + size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
> + break;
> + case SHT_REL:
> + size_ok = shdr->sh_entsize == sizeof(Elf64_Rel);
> + break;
> + case SHT_NOTE:
> + case SHT_PROGBITS:
> + case SHT_HASH:
> + case SHT_NOBITS:
> + default:
> + /*
> + * This is a section whose entsize requirements
> + * I don't care about. If I don't know about
> + * the section I can't care about it's entsize
> + * requirements.
> + */
> + size_ok = true;
> + break;
> + }
> +
> + if (!size_ok) {
> + pr_debug("ELF section with wrong entry size.\n");
> + return false;
> + } else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
> + pr_debug("ELF section address wraps around.\n");
> + return false;
> + }
> +
> + if (shdr->sh_type != SHT_NOBITS) {
> + if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
> + pr_debug("ELF section location wraps around.\n");
> + return false;
> + } else if (shdr->sh_offset + shdr->sh_size > buf_len) {
> + pr_debug("ELF section not in file.\n");
> + return false;
> + }
> + }
> +
> + return true;
> +}
> +
> +static int elf_read_shdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
> + int idx)
> +{
> + struct elf_shdr *shdr = &elf_info->sechdrs[idx];
> + const struct elfhdr *ehdr = elf_info->ehdr;
> + const char *sbuf;
> + struct elf_shdr *buf_shdr;
> +
> + sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
> + buf_shdr = (struct elf_shdr *) sbuf;
> +
> + shdr->sh_name = elf32_to_cpu(ehdr, buf_shdr->sh_name);
> + shdr->sh_type = elf32_to_cpu(ehdr, buf_shdr->sh_type);
> + shdr->sh_addr = elf64_to_cpu(ehdr, buf_shdr->sh_addr);
> + shdr->sh_offset = elf64_to_cpu(ehdr, buf_shdr->sh_offset);
> + shdr->sh_link = elf32_to_cpu(ehdr, buf_shdr->sh_link);
> + shdr->sh_info = elf32_to_cpu(ehdr, buf_shdr->sh_info);
> +
> + /*
> + * The following fields have a type equivalent to Elf_Addr
> + * both in 32 bit and 64 bit ELF.
> + */
> + shdr->sh_flags = elf64_to_cpu(ehdr, buf_shdr->sh_flags);
> + shdr->sh_size = elf64_to_cpu(ehdr, buf_shdr->sh_size);
> + shdr->sh_addralign = elf64_to_cpu(ehdr, buf_shdr->sh_addralign);
> + shdr->sh_entsize = elf64_to_cpu(ehdr, buf_shdr->sh_entsize);
> +
> + return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_read_shdrs - read the section headers from the buffer
> + *
> + * This function assumes that the section header table was checked for sanity.
> + * Use elf_is_ehdr_sane() if it wasn't.
> + */
> +static int elf_read_shdrs(const char *buf, size_t len,
> + struct kexec_elf_info *elf_info)
> +{
> + size_t shdr_size, i;
> +
> + /*
> + * e_shnum is at most 65536 so calculating
> + * the size of the section header cannot overflow.
> + */
> + shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
> +
> + elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
> + if (!elf_info->sechdrs)
> + return -ENOMEM;
> +
> + for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
> + int ret;
> +
> + ret = elf_read_shdr(buf, len, elf_info, i);
> + if (ret) {
> + kfree(elf_info->sechdrs);
> + elf_info->sechdrs = NULL;
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
> + * @buf: Buffer to read ELF file from.
> + * @len: Size of @buf.
> + * @ehdr: Pointer to existing struct which will be populated.
> + * @elf_info: Pointer to existing struct which will be populated.
> + *
> + * This function allows reading ELF files with different byte order than
> + * the kernel, byte-swapping the fields as needed.
> + *
> + * Return:
> + * On success returns 0, and the caller should call kexec_free_elf_info(elf_info) to
> + * free the memory allocated for the section and program headers.
> + */
> +int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
> + struct kexec_elf_info *elf_info)
> +{
> + int ret;
> +
> + ret = elf_read_ehdr(buf, len, ehdr);
> + if (ret)
> + return ret;
> +
> + elf_info->buffer = buf;
> + elf_info->ehdr = ehdr;
> + if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> + ret = elf_read_phdrs(buf, len, elf_info);
> + if (ret)
> + return ret;
> + }
> + if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> + ret = elf_read_shdrs(buf, len, elf_info);
> + if (ret) {
> + kfree(elf_info->proghdrs);
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * kexec_free_elf_info - free memory allocated by elf_read_from_buffer
> + */
> +void kexec_free_elf_info(struct kexec_elf_info *elf_info)
> +{
> + kfree(elf_info->proghdrs);
> + memset(elf_info, 0, sizeof(*elf_info));
> +}
> +
> +/**
> + * kexec_build_elf_info - read ELF executable and check that we can use it
> + */
> +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> + struct kexec_elf_info *elf_info)
> +{
> + int i;
> + int ret;
> +
> + ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
> + if (ret)
> + return ret;
> +
> + /* Big endian vmlinux has type ET_DYN. */
> + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
> + pr_err("Not an ELF executable.\n");
> + goto error;
> + } else if (!elf_info->proghdrs) {
> + pr_err("No ELF program header.\n");
> + goto error;
> + }
> +
> + for (i = 0; i < ehdr->e_phnum; i++) {
> + /*
> + * Kexec does not support loading interpreters.
> + * In addition this check keeps us from attempting
> + * to kexec ordinay executables.
> + */
> + if (elf_info->proghdrs[i].p_type == PT_INTERP) {
> + pr_err("Requires an ELF interpreter.\n");
> + goto error;
> + }
> + }
> +
> + return 0;
> +error:
> + kexec_free_elf_info(elf_info);
> + return -ENOEXEC;
> +}
> +
> +/**
> + * elf_exec_load - load ELF executable image
> + * @lowest_load_addr: On return, will be the address where the first PT_LOAD
> + * section will be loaded in memory.
> + *
> + * Return:
> + * 0 on success, negative value on failure.
> + */
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> + struct kexec_elf_info *elf_info,
> + struct kexec_buf *kbuf,
> + unsigned long *lowest_load_addr)
> +{
> + unsigned long lowest_addr = UINT_MAX;
> + int ret;
> + size_t i;
> + /* Read in the PT_LOAD segments. */
> + for (i = 0; i < ehdr->e_phnum; i++) {
> + unsigned long load_addr;
> + size_t size;
> + const struct elf_phdr *phdr;
> +
> + phdr = &elf_info->proghdrs[i];
> + if (phdr->p_type != PT_LOAD)
> + continue;
> +
> + size = phdr->p_filesz;
> + if (size > phdr->p_memsz)
> + size = phdr->p_memsz;
> +
> + kbuf->buffer = (void *) elf_info->buffer + phdr->p_offset;
> + kbuf->bufsz = size;
> + kbuf->memsz = phdr->p_memsz;
> + kbuf->buf_align = phdr->p_align;
> + kbuf->buf_min = phdr->p_paddr;
> + kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
> + ret = kexec_add_buffer(kbuf);
> + if (ret)
> + goto out;
> + load_addr = kbuf->mem;
> +
> + if (load_addr < lowest_addr)
> + lowest_addr = load_addr;
> + }
> +
> + image->start = ehdr->e_entry;
> + *lowest_load_addr = lowest_addr;
> + ret = 0;
> + out:
> + return ret;
> +}
> +
> +int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
> + char *kernel_buf, unsigned long kernel_len,
> + unsigned long *kernel_load_addr)
> +{
> + int ret;
> + struct elfhdr ehdr;
> + struct kexec_elf_info elf_info;
> +
> + ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
> + if (ret)
> + goto out;
> +
> + ret = kexec_elf_load(image, &ehdr, &elf_info, kbuf, kernel_load_addr);
> +out:
> + kexec_free_elf_info(&elf_info);
> + return ret;
> +
> +}
> +
> +int kexec_elf_probe(const char *buf, unsigned long len)
> +{
> + struct elfhdr ehdr;
> + struct kexec_elf_info elf_info;
> + int ret;
> +
> + ret = kexec_build_elf_info(buf, len, &ehdr, &elf_info);
> + if (ret)
> + return ret;
> +
> + kexec_free_elf_info(&elf_info);
> +
> + return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
> +}
>
^ permalink raw reply
* Re: [PATCH using git format-patch -C] kexec: add generic support for elf kernel images
From: Christophe Leroy @ 2019-07-08 10:40 UTC (permalink / raw)
To: kexec, Sven Schnelle; +Cc: deller, linuxppc-dev
In-Reply-To: <20190708100624.EBCDF68DB2@pc17473vm.idsi0.si.c-s.fr>
Hi Sven,
Le 08/07/2019 à 12:06, Christophe Leroy a écrit :
> From: Sven Schnelle <svens@stackframe.org>
Please describe you patch.
All the changes you are doing to the ppc64 version in order to make it
generic should be described.
Those changes are also maybe worth splitting the patch in several parts,
either preparing the ppc64 for generic then moving to kernel/kexec_elf.c
or moving to kernel/kexec_elf.c without modifications, then modifying it
to make it generic.
Note that your patch only applies on Linux 5.1, it doesn't apply on
powerpc/next.
I think it also be worth taking the opportunity to fix the sparse
warnings, see https://patchwork.ozlabs.org/patch/1128720/
checkpatch comments should also be fixed, see
https://openpower.xyz/job/snowpatch/job/snowpatch-linux-checkpatch/8044//artifact/linux/checkpatch.log
Other comments below,
>
> Signed-off-by: Sven Schnelle <svens@stackframe.org>
> ---
> patch generated with 'git format-patch -C' in order to
> see the modifications done in kexec_elf.c in addition to
> copying it from arch/powerpc/kernel/kexec_elf_64.c
>
> arch/Kconfig | 3 +
> arch/powerpc/Kconfig | 1 +
> arch/powerpc/kernel/kexec_elf_64.c | 547 +--------------------
> include/linux/kexec.h | 35 ++
> kernel/Makefile | 1 +
> .../kernel/kexec_elf_64.c => kernel/kexec_elf.c | 264 +++-------
> 6 files changed, 118 insertions(+), 733 deletions(-)
> copy arch/powerpc/kernel/kexec_elf_64.c => kernel/kexec_elf.c (64%)
>
> diff --git a/arch/Kconfig b/arch/Kconfig
> index 33687dddd86a..c7c75fbc0b79 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -18,6 +18,9 @@ config KEXEC_CORE
> select CRASH_CORE
> bool
>
> +config KEXEC_ELF
> + bool
> +
> config HAVE_IMA_KEXEC
> bool
>
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 2d0be82c3061..447b6e3ad999 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -501,6 +501,7 @@ config KEXEC_FILE
> select KEXEC_CORE
> select HAVE_IMA_KEXEC
> select BUILD_BIN2C
> + select KEXEC_ELF
> depends on PPC64
> depends on CRYPTO=y
> depends on CRYPTO_SHA256=y
> diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/arch/powerpc/kernel/kexec_elf_64.c
> index ba4f18a43ee8..d062c5991722 100644
> --- a/arch/powerpc/kernel/kexec_elf_64.c
> +++ b/arch/powerpc/kernel/kexec_elf_64.c
> @@ -31,539 +31,7 @@
> #include <linux/slab.h>
> #include <linux/types.h>
>
> -#define PURGATORY_STACK_SIZE (16 * 1024)
[snip]
> +#define PURGATORY_STACK_SIZE (16 * 1024)
This line shouldn't be modified by your patch. I see a tab was replaced
by space.
>
> static void *elf64_load(struct kimage *image, char *kernel_buf,
> unsigned long kernel_len, char *initrd,
> @@ -577,17 +45,17 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
> void *fdt;
> const void *slave_code;
> struct elfhdr ehdr;
> - struct elf_info elf_info;
> + struct kexec_elf_info elf_info;
> struct kexec_buf kbuf = { .image = image, .buf_min = 0,
> .buf_max = ppc64_rma_size };
> struct kexec_buf pbuf = { .image = image, .buf_min = 0,
> .buf_max = ppc64_rma_size, .top_down = true };
>
> - ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
> + ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
> if (ret)
> goto out;
>
> - ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
> + ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
> if (ret)
> goto out;
>
> @@ -606,6 +74,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
> kbuf.bufsz = kbuf.memsz = initrd_len;
> kbuf.buf_align = PAGE_SIZE;
> kbuf.top_down = false;
> + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
Is that correct ? I see:
- kbuf.mem is unsigned
- KEXEC_BUF_MEM_UNKNOWN is -1 on s390
> ret = kexec_add_buffer(&kbuf);
> if (ret)
> goto out;
> @@ -638,6 +107,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
> kbuf.bufsz = kbuf.memsz = fdt_size;
> kbuf.buf_align = PAGE_SIZE;
> kbuf.top_down = true;
> + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
> ret = kexec_add_buffer(&kbuf);
> if (ret)
> goto out;
> @@ -652,13 +122,12 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
> pr_err("Error setting up the purgatory.\n");
>
> out:
> - elf_free_info(&elf_info);
> -
> + kexec_free_elf_info(&elf_info);
> /* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
> return ret ? ERR_PTR(ret) : fdt;
> }
>
> const struct kexec_file_ops kexec_elf64_ops = {
> - .probe = elf64_probe,
> + .probe = kexec_elf_probe,
> .load = elf64_load,
> };
> diff --git a/include/linux/kexec.h b/include/linux/kexec.h
> index b9b1bc5f9669..72648b18b778 100644
> --- a/include/linux/kexec.h
> +++ b/include/linux/kexec.h
> @@ -216,6 +216,41 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
> void **addr, unsigned long *sz);
> #endif /* CONFIG_KEXEC_FILE */
>
> +#ifdef CONFIG_KEXEC_ELF
> +
> +struct kexec_elf_info {
> + /*
> + * Where the ELF binary contents are kept.
> + * Memory managed by the user of the struct.
> + */
> + const char *buffer;
> +
> + const struct elfhdr *ehdr;
> + const struct elf_phdr *proghdrs;
> + struct elf_shdr *sechdrs;
> +};
> +
> +void kexec_free_elf_info(struct kexec_elf_info *elf_info);
> +
> +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> + struct kexec_elf_info *elf_info);
> +
> +int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
> + char *kernel_buf, unsigned long kernel_len,
> + unsigned long *kernel_load_addr);
> +
> +int kexec_elf_probe(const char *buf, unsigned long len);
> +
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> + struct kexec_elf_info *elf_info,
> + struct kexec_buf *kbuf,
> + unsigned long *lowest_load_addr);
> +
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> + struct kexec_elf_info *elf_info,
> + struct kexec_buf *kbuf,
> + unsigned long *lowest_load_addr);
Duplication of kexec_elf_load()
> +#endif
> struct kimage {
> kimage_entry_t head;
> kimage_entry_t *entry;
> diff --git a/kernel/Makefile b/kernel/Makefile
> index 6c57e78817da..081b378050d0 100644
> --- a/kernel/Makefile
> +++ b/kernel/Makefile
> @@ -63,6 +63,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o
> obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
> obj-$(CONFIG_KEXEC) += kexec.o
> obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
> +obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
> obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
> obj-$(CONFIG_COMPAT) += compat.o
> obj-$(CONFIG_CGROUPS) += cgroup/
> diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/kernel/kexec_elf.c
> similarity index 64%
> copy from arch/powerpc/kernel/kexec_elf_64.c
> copy to kernel/kexec_elf.c
> index ba4f18a43ee8..07e5bcc79bba 100644
> --- a/arch/powerpc/kernel/kexec_elf_64.c
> +++ b/kernel/kexec_elf.c
> @@ -1,55 +1,13 @@
> -/*
> - * Load ELF vmlinux file for the kexec_file_load syscall.
> - *
> - * Copyright (C) 2004 Adam Litke (agl@us.ibm.com)
> - * Copyright (C) 2004 IBM Corp.
> - * Copyright (C) 2005 R Sharada (sharada@in.ibm.com)
> - * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com)
> - * Copyright (C) 2016 IBM Corporation
> - *
> - * Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
> - * Heavily modified for the kernel by
> - * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation (version 2 of the License).
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - */
> +// SPDX-License-Identifier: GPL-2.0
>
> #define pr_fmt(fmt) "kexec_elf: " fmt
>
> #include <linux/elf.h>
> #include <linux/kexec.h>
> -#include <linux/libfdt.h>
> #include <linux/module.h>
> -#include <linux/of_fdt.h>
> #include <linux/slab.h>
> #include <linux/types.h>
>
> -#define PURGATORY_STACK_SIZE (16 * 1024)
> -
> -#define elf_addr_to_cpu elf64_to_cpu
> -
> -#ifndef Elf_Rel
> -#define Elf_Rel Elf64_Rel
> -#endif /* Elf_Rel */
> -
> -struct elf_info {
> - /*
> - * Where the ELF binary contents are kept.
> - * Memory managed by the user of the struct.
> - */
> - const char *buffer;
> -
> - const struct elfhdr *ehdr;
> - const struct elf_phdr *proghdrs;
> - struct elf_shdr *sechdrs;
> -};
>
> static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
> {
> @@ -66,22 +24,22 @@ static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
> return value;
> }
>
> -static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
> +static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
Any reason to switch position of elf16_to_cpu() with elf32_to_cpu() ?
> {
> if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> - value = le16_to_cpu(value);
> + value = le32_to_cpu(value);
> else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> - value = be16_to_cpu(value);
> + value = be32_to_cpu(value);
>
> return value;
> }
>
> -static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
> +static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
> {
> if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> - value = le32_to_cpu(value);
> + value = le16_to_cpu(value);
> else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> - value = be32_to_cpu(value);
> + value = be16_to_cpu(value);
>
> return value;
> }
> @@ -180,9 +138,9 @@ static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
> ehdr->e_type = elf16_to_cpu(ehdr, buf_ehdr->e_type);
> ehdr->e_machine = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
> ehdr->e_version = elf32_to_cpu(ehdr, buf_ehdr->e_version);
> - ehdr->e_entry = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
> - ehdr->e_phoff = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
> - ehdr->e_shoff = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
> + ehdr->e_entry = elf64_to_cpu(ehdr, buf_ehdr->e_entry);
> + ehdr->e_phoff = elf64_to_cpu(ehdr, buf_ehdr->e_phoff);
> + ehdr->e_shoff = elf64_to_cpu(ehdr, buf_ehdr->e_shoff);
> ehdr->e_flags = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
> ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
> ehdr->e_phnum = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
> @@ -214,7 +172,7 @@ static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
> return true;
> }
>
> -static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
> +static int elf_read_phdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
> int idx)
> {
> /* Override the const in proghdrs, we are the ones doing the loading. */
> @@ -226,18 +184,18 @@ static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
> buf_phdr = (struct elf_phdr *) pbuf;
>
> phdr->p_type = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
> - phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
> - phdr->p_paddr = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
> - phdr->p_vaddr = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
> + phdr->p_offset = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
> + phdr->p_paddr = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
> + phdr->p_vaddr = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
> phdr->p_flags = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
>
> /*
> * The following fields have a type equivalent to Elf_Addr
> * both in 32 bit and 64 bit ELF.
> */
> - phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
> - phdr->p_memsz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
> - phdr->p_align = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
> + phdr->p_filesz = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
> + phdr->p_memsz = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
> + phdr->p_align = elf64_to_cpu(elf_info->ehdr, buf_phdr->p_align);
>
> return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
> }
> @@ -249,7 +207,7 @@ static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
> * Use elf_is_ehdr_sane() if it wasn't.
> */
> static int elf_read_phdrs(const char *buf, size_t len,
> - struct elf_info *elf_info)
> + struct kexec_elf_info *elf_info)
> {
> size_t phdr_size, i;
> const struct elfhdr *ehdr = elf_info->ehdr;
> @@ -296,13 +254,13 @@ static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
> size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
> break;
> case SHT_RELA:
> - size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
> + size_ok = shdr->sh_entsize == sizeof(Elf64_Rela);
> break;
> case SHT_DYNAMIC:
> size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
> break;
> case SHT_REL:
> - size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
> + size_ok = shdr->sh_entsize == sizeof(Elf64_Rel);
> break;
> case SHT_NOTE:
> case SHT_PROGBITS:
> @@ -340,7 +298,7 @@ static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
> return true;
> }
>
> -static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
> +static int elf_read_shdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
> int idx)
> {
> struct elf_shdr *shdr = &elf_info->sechdrs[idx];
> @@ -353,8 +311,8 @@ static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
>
> shdr->sh_name = elf32_to_cpu(ehdr, buf_shdr->sh_name);
> shdr->sh_type = elf32_to_cpu(ehdr, buf_shdr->sh_type);
> - shdr->sh_addr = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
> - shdr->sh_offset = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
> + shdr->sh_addr = elf64_to_cpu(ehdr, buf_shdr->sh_addr);
> + shdr->sh_offset = elf64_to_cpu(ehdr, buf_shdr->sh_offset);
> shdr->sh_link = elf32_to_cpu(ehdr, buf_shdr->sh_link);
> shdr->sh_info = elf32_to_cpu(ehdr, buf_shdr->sh_info);
>
> @@ -362,10 +320,10 @@ static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
> * The following fields have a type equivalent to Elf_Addr
> * both in 32 bit and 64 bit ELF.
> */
> - shdr->sh_flags = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
> - shdr->sh_size = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
> - shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
> - shdr->sh_entsize = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
> + shdr->sh_flags = elf64_to_cpu(ehdr, buf_shdr->sh_flags);
> + shdr->sh_size = elf64_to_cpu(ehdr, buf_shdr->sh_size);
> + shdr->sh_addralign = elf64_to_cpu(ehdr, buf_shdr->sh_addralign);
> + shdr->sh_entsize = elf64_to_cpu(ehdr, buf_shdr->sh_entsize);
>
> return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
> }
> @@ -377,7 +335,7 @@ static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
> * Use elf_is_ehdr_sane() if it wasn't.
> */
> static int elf_read_shdrs(const char *buf, size_t len,
> - struct elf_info *elf_info)
> + struct kexec_elf_info *elf_info)
> {
> size_t shdr_size, i;
>
> @@ -416,11 +374,11 @@ static int elf_read_shdrs(const char *buf, size_t len,
> * the kernel, byte-swapping the fields as needed.
> *
> * Return:
> - * On success returns 0, and the caller should call elf_free_info(elf_info) to
> + * On success returns 0, and the caller should call kexec_free_elf_info(elf_info) to
> * free the memory allocated for the section and program headers.
> */
> int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
> - struct elf_info *elf_info)
> + struct kexec_elf_info *elf_info)
> {
> int ret;
>
> @@ -447,19 +405,19 @@ int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
> }
>
> /**
> - * elf_free_info - free memory allocated by elf_read_from_buffer
> + * kexec_free_elf_info - free memory allocated by elf_read_from_buffer
> */
> -void elf_free_info(struct elf_info *elf_info)
> +void kexec_free_elf_info(struct kexec_elf_info *elf_info)
> {
> kfree(elf_info->proghdrs);
> - kfree(elf_info->sechdrs);
Why is the above kfree() removed ?
> memset(elf_info, 0, sizeof(*elf_info));
> }
> +
> /**
> - * build_elf_exec_info - read ELF executable and check that we can use it
> + * kexec_build_elf_info - read ELF executable and check that we can use it
> */
> -static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
> - struct elf_info *elf_info)
> +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> + struct kexec_elf_info *elf_info)
> {
> int i;
> int ret;
> @@ -491,25 +449,10 @@ static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
>
> return 0;
> error:
> - elf_free_info(elf_info);
> + kexec_free_elf_info(elf_info);
> return -ENOEXEC;
> }
>
> -static int elf64_probe(const char *buf, unsigned long len)
> -{
> - struct elfhdr ehdr;
> - struct elf_info elf_info;
> - int ret;
> -
> - ret = build_elf_exec_info(buf, len, &ehdr, &elf_info);
> - if (ret)
> - return ret;
> -
> - elf_free_info(&elf_info);
> -
> - return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
> -}
> -
> /**
> * elf_exec_load - load ELF executable image
> * @lowest_load_addr: On return, will be the address where the first PT_LOAD
> @@ -518,16 +461,14 @@ static int elf64_probe(const char *buf, unsigned long len)
> * Return:
> * 0 on success, negative value on failure.
> */
> -static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
> - struct elf_info *elf_info,
> - unsigned long *lowest_load_addr)
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> + struct kexec_elf_info *elf_info,
> + struct kexec_buf *kbuf,
> + unsigned long *lowest_load_addr)
> {
> - unsigned long base = 0, lowest_addr = UINT_MAX;
> + unsigned long lowest_addr = UINT_MAX;
> int ret;
> size_t i;
> - struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size,
> - .top_down = false };
> -
> /* Read in the PT_LOAD segments. */
> for (i = 0; i < ehdr->e_phnum; i++) {
> unsigned long load_addr;
> @@ -542,123 +483,58 @@ static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
> if (size > phdr->p_memsz)
> size = phdr->p_memsz;
>
> - kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
> - kbuf.bufsz = size;
> - kbuf.memsz = phdr->p_memsz;
> - kbuf.buf_align = phdr->p_align;
> - kbuf.buf_min = phdr->p_paddr + base;
> - ret = kexec_add_buffer(&kbuf);
> + kbuf->buffer = (void *) elf_info->buffer + phdr->p_offset;
> + kbuf->bufsz = size;
> + kbuf->memsz = phdr->p_memsz;
> + kbuf->buf_align = phdr->p_align;
> + kbuf->buf_min = phdr->p_paddr;
> + kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
> + ret = kexec_add_buffer(kbuf);
> if (ret)
> goto out;
> - load_addr = kbuf.mem;
> + load_addr = kbuf->mem;
>
> if (load_addr < lowest_addr)
> lowest_addr = load_addr;
> }
>
> - /* Update entry point to reflect new load address. */
> - ehdr->e_entry += base;
> -
> + image->start = ehdr->e_entry;
> *lowest_load_addr = lowest_addr;
> ret = 0;
> out:
> return ret;
> }
>
> -static void *elf64_load(struct kimage *image, char *kernel_buf,
> - unsigned long kernel_len, char *initrd,
> - unsigned long initrd_len, char *cmdline,
> - unsigned long cmdline_len)
> +int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
> + char *kernel_buf, unsigned long kernel_len,
> + unsigned long *kernel_load_addr)
> {
> int ret;
> - unsigned int fdt_size;
> - unsigned long kernel_load_addr;
> - unsigned long initrd_load_addr = 0, fdt_load_addr;
> - void *fdt;
> - const void *slave_code;
> struct elfhdr ehdr;
> - struct elf_info elf_info;
> - struct kexec_buf kbuf = { .image = image, .buf_min = 0,
> - .buf_max = ppc64_rma_size };
> - struct kexec_buf pbuf = { .image = image, .buf_min = 0,
> - .buf_max = ppc64_rma_size, .top_down = true };
> -
> - ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
> - if (ret)
> - goto out;
> + struct kexec_elf_info elf_info;
>
> - ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
> + ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
> if (ret)
> goto out;
>
> - pr_debug("Loaded the kernel at 0x%lx\n", kernel_load_addr);
> -
> - ret = kexec_load_purgatory(image, &pbuf);
> - if (ret) {
> - pr_err("Loading purgatory failed.\n");
> - goto out;
> - }
> -
> - pr_debug("Loaded purgatory at 0x%lx\n", pbuf.mem);
> -
> - if (initrd != NULL) {
> - kbuf.buffer = initrd;
> - kbuf.bufsz = kbuf.memsz = initrd_len;
> - kbuf.buf_align = PAGE_SIZE;
> - kbuf.top_down = false;
> - ret = kexec_add_buffer(&kbuf);
> - if (ret)
> - goto out;
> - initrd_load_addr = kbuf.mem;
> -
> - pr_debug("Loaded initrd at 0x%lx\n", initrd_load_addr);
> - }
> -
> - fdt_size = fdt_totalsize(initial_boot_params) * 2;
> - fdt = kmalloc(fdt_size, GFP_KERNEL);
> - if (!fdt) {
> - pr_err("Not enough memory for the device tree.\n");
> - ret = -ENOMEM;
> - goto out;
> - }
> - ret = fdt_open_into(initial_boot_params, fdt, fdt_size);
> - if (ret < 0) {
> - pr_err("Error setting up the new device tree.\n");
> - ret = -EINVAL;
> - goto out;
> - }
> -
> - ret = setup_new_fdt(image, fdt, initrd_load_addr, initrd_len, cmdline);
> - if (ret)
> - goto out;
> -
> - fdt_pack(fdt);
> + ret = kexec_elf_load(image, &ehdr, &elf_info, kbuf, kernel_load_addr);
> +out:
> + kexec_free_elf_info(&elf_info);
> + return ret;
>
> - kbuf.buffer = fdt;
> - kbuf.bufsz = kbuf.memsz = fdt_size;
> - kbuf.buf_align = PAGE_SIZE;
> - kbuf.top_down = true;
> - ret = kexec_add_buffer(&kbuf);
> - if (ret)
> - goto out;
> - fdt_load_addr = kbuf.mem;
> +}
>
> - pr_debug("Loaded device tree at 0x%lx\n", fdt_load_addr);
> +int kexec_elf_probe(const char *buf, unsigned long len)
> +{
> + struct elfhdr ehdr;
> + struct kexec_elf_info elf_info;
> + int ret;
>
> - slave_code = elf_info.buffer + elf_info.proghdrs[0].p_offset;
> - ret = setup_purgatory(image, slave_code, fdt, kernel_load_addr,
> - fdt_load_addr);
> + ret = kexec_build_elf_info(buf, len, &ehdr, &elf_info);
> if (ret)
> - pr_err("Error setting up the purgatory.\n");
> + return ret;
>
> -out:
> - elf_free_info(&elf_info);
> + kexec_free_elf_info(&elf_info);
>
> - /* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
> - return ret ? ERR_PTR(ret) : fdt;
> + return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
> }
> -
> -const struct kexec_file_ops kexec_elf64_ops = {
> - .probe = elf64_probe,
> - .load = elf64_load,
> -};
>
Christophe
^ permalink raw reply
* linux-next: manual merge of the akpm-current tree with the powerpc tree
From: Stephen Rothwell @ 2019-07-08 11:43 UTC (permalink / raw)
To: Andrew Morton, Michael Ellerman, PowerPC
Cc: Aneesh Kumar K.V, Linux Next Mailing List,
Linux Kernel Mailing List
[-- Attachment #1: Type: text/plain, Size: 1899 bytes --]
Hi all,
Today's linux-next merge of the akpm-current tree got a conflict in:
arch/powerpc/include/asm/pgtable.h
between commit:
d6eacedd1f0e ("powerpc/book3s: Use config independent helpers for page table walk")
from the powerpc tree and commit:
be66a174b253 ("mm/nvdimm: add is_ioremap_addr and use that to check ioremap address")
from the akpm-current tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
--
Cheers,
Stephen Rothwell
diff --cc arch/powerpc/include/asm/pgtable.h
index bf7d771f342e,64145751b2fd..000000000000
--- a/arch/powerpc/include/asm/pgtable.h
+++ b/arch/powerpc/include/asm/pgtable.h
@@@ -140,30 -140,20 +140,44 @@@ static inline void pte_frag_set(mm_cont
}
#endif
+#ifndef pmd_is_leaf
+#define pmd_is_leaf pmd_is_leaf
+static inline bool pmd_is_leaf(pmd_t pmd)
+{
+ return false;
+}
+#endif
+
+#ifndef pud_is_leaf
+#define pud_is_leaf pud_is_leaf
+static inline bool pud_is_leaf(pud_t pud)
+{
+ return false;
+}
+#endif
+
+#ifndef pgd_is_leaf
+#define pgd_is_leaf pgd_is_leaf
+static inline bool pgd_is_leaf(pgd_t pgd)
+{
+ return false;
+}
+#endif
+
+ #ifdef CONFIG_PPC64
+ #define is_ioremap_addr is_ioremap_addr
+ static inline bool is_ioremap_addr(const void *x)
+ {
+ #ifdef CONFIG_MMU
+ unsigned long addr = (unsigned long)x;
+
+ return addr >= IOREMAP_BASE && addr < IOREMAP_END;
+ #else
+ return false;
+ #endif
+ }
+ #endif /* CONFIG_PPC64 */
+
#endif /* __ASSEMBLY__ */
#endif /* _ASM_POWERPC_PGTABLE_H */
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [Bug 203839] Kernel 5.2-rc3 fails to boot on a PowerMac G4 3,6: systemd[1]: Failed to bump fs.file-max, ignoring: invalid argument
From: bugzilla-daemon @ 2019-07-08 14:15 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <bug-203839-206035@https.bugzilla.kernel.org/>
https://bugzilla.kernel.org/show_bug.cgi?id=203839
Michael Ellerman (michael@ellerman.id.au) changed:
What |Removed |Added
----------------------------------------------------------------------------
Status|RESOLVED |CLOSED
CC| |michael@ellerman.id.au
--- Comment #11 from Michael Ellerman (michael@ellerman.id.au) ---
Fixed in b7f8b440f300 ("powerpc/32s: fix initial setup of segment registers on
secondary CPU")
--
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply
* Re: [PATCH 4/4] powerpc/64: reuse PPC32 static inline flush_dcache_range()
From: Aneesh Kumar K.V @ 2019-07-08 14:21 UTC (permalink / raw)
To: Christophe Leroy, Benjamin Herrenschmidt, Paul Mackerras,
Michael Ellerman, Oliver O'Halloran, Segher Boessenkool
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <d6f628ffdeb9c7863da722a8f6ef2949e57bb360.1557824379.git.christophe.leroy@c-s.fr>
Christophe Leroy <christophe.leroy@c-s.fr> writes:
> This patch drops the assembly PPC64 version of flush_dcache_range()
> and re-uses the PPC32 static inline version.
>
> With GCC 8.1, the following code is generated:
>
> void flush_test(unsigned long start, unsigned long stop)
> {
> flush_dcache_range(start, stop);
> }
>
> 0000000000000130 <.flush_test>:
> 130: 3d 22 00 00 addis r9,r2,0
> 132: R_PPC64_TOC16_HA .data+0x8
> 134: 81 09 00 00 lwz r8,0(r9)
> 136: R_PPC64_TOC16_LO .data+0x8
> 138: 3d 22 00 00 addis r9,r2,0
> 13a: R_PPC64_TOC16_HA .data+0xc
> 13c: 80 e9 00 00 lwz r7,0(r9)
> 13e: R_PPC64_TOC16_LO .data+0xc
> 140: 7d 48 00 d0 neg r10,r8
> 144: 7d 43 18 38 and r3,r10,r3
> 148: 7c 00 04 ac hwsync
> 14c: 4c 00 01 2c isync
> 150: 39 28 ff ff addi r9,r8,-1
> 154: 7c 89 22 14 add r4,r9,r4
> 158: 7c 83 20 50 subf r4,r3,r4
> 15c: 7c 89 3c 37 srd. r9,r4,r7
> 160: 41 82 00 1c beq 17c <.flush_test+0x4c>
> 164: 7d 29 03 a6 mtctr r9
> 168: 60 00 00 00 nop
> 16c: 60 00 00 00 nop
> 170: 7c 00 18 ac dcbf 0,r3
> 174: 7c 63 42 14 add r3,r3,r8
> 178: 42 00 ff f8 bdnz 170 <.flush_test+0x40>
> 17c: 7c 00 04 ac hwsync
> 180: 4c 00 01 2c isync
> 184: 4e 80 00 20 blr
> 188: 60 00 00 00 nop
> 18c: 60 00 00 00 nop
>
> Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
> ---
> arch/powerpc/include/asm/cache.h | 10 ++++++++++
> arch/powerpc/include/asm/cacheflush.h | 14 ++++++++------
> arch/powerpc/kernel/misc_64.S | 29 -----------------------------
> 3 files changed, 18 insertions(+), 35 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/cache.h b/arch/powerpc/include/asm/cache.h
> index 0009a0a82e86..45e3137ccd71 100644
> --- a/arch/powerpc/include/asm/cache.h
> +++ b/arch/powerpc/include/asm/cache.h
> @@ -54,6 +54,16 @@ struct ppc64_caches {
> };
>
> extern struct ppc64_caches ppc64_caches;
> +
> +static inline u32 l1_cache_shift(void)
> +{
> + return ppc64_caches.l1d.log_block_size;
> +}
> +
> +static inline u32 l1_cache_bytes(void)
> +{
> + return ppc64_caches.l1d.block_size;
> +}
> #else
> static inline u32 l1_cache_shift(void)
> {
> diff --git a/arch/powerpc/include/asm/cacheflush.h b/arch/powerpc/include/asm/cacheflush.h
> index d405f18441cd..3cd7ce3dec8b 100644
> --- a/arch/powerpc/include/asm/cacheflush.h
> +++ b/arch/powerpc/include/asm/cacheflush.h
> @@ -57,7 +57,6 @@ static inline void __flush_dcache_icache_phys(unsigned long physaddr)
> }
> #endif
>
> -#ifdef CONFIG_PPC32
> /*
> * Write any modified data cache blocks out to memory and invalidate them.
> * Does not invalidate the corresponding instruction cache blocks.
> @@ -70,9 +69,17 @@ static inline void flush_dcache_range(unsigned long start, unsigned long stop)
> unsigned long size = stop - (unsigned long)addr + (bytes - 1);
> unsigned long i;
>
> + if (IS_ENABLED(CONFIG_PPC64)) {
> + mb(); /* sync */
> + isync();
> + }
> +
> for (i = 0; i < size >> shift; i++, addr += bytes)
> dcbf(addr);
> mb(); /* sync */
> +
> + if (IS_ENABLED(CONFIG_PPC64))
> + isync();
> }
Was checking with Michael about why we need that extra isync. Michael
pointed this came via
https://github.com/mpe/linux-fullhistory/commit/faa5ee3743ff9b6df9f9a03600e34fdae596cfb2#diff-67c7ffa8e420c7d4206cae4a9e888e14
for 970 which doesn't have coherent icache. So possibly isync there is
to flush the prefetch instructions? But even so we would need an icbi
there before that isync.
So overall wondering why we need that extra barriers there.
>
> /*
> @@ -112,11 +119,6 @@ static inline void invalidate_dcache_range(unsigned long start,
> mb(); /* sync */
> }
>
-aneesh
^ permalink raw reply
* Re: [PATCH v2] powerpc/mm: Implement STRICT_MODULE_RWX
From: Aneesh Kumar K.V @ 2019-07-08 14:54 UTC (permalink / raw)
To: Russell Currey, linuxppc-dev; +Cc: kernel-hardening
In-Reply-To: <20190614055013.21014-1-ruscur@russell.cc>
Russell Currey <ruscur@russell.cc> writes:
> Strict module RWX is just like strict kernel RWX, but for modules - so
> loadable modules aren't marked both writable and executable at the same
> time. This is handled by the generic code in kernel/module.c, and
> simply requires the architecture to implement the set_memory() set of
> functions, declared with ARCH_HAS_SET_MEMORY.
>
> There's nothing other than these functions required to turn
> ARCH_HAS_STRICT_MODULE_RWX on, so turn that on too.
>
> With STRICT_MODULE_RWX enabled, there are as many W+X pages at runtime
> as there are with CONFIG_MODULES=n (none), so in Russel's testing it works
> well on both Hash and Radix book3s64.
>
> There's a TODO in the code for also applying the page permission changes
> to the backing pages in the linear mapping: this is pretty simple for
> Radix and (seemingly) a lot harder for Hash, so I've left it for now
> since there's still a notable security benefit for the patch as-is.
>
> Technically can be enabled without STRICT_KERNEL_RWX, but
> that doesn't gets you a whole lot, so we should leave it off by default
> until we can get STRICT_KERNEL_RWX to the point where it's enabled by
> default.
>
> Signed-off-by: Russell Currey <ruscur@russell.cc>
> Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
> ---
> Changes from v1 (sent by Christophe):
> - return if VM_FLUSH_RESET_PERMS is set
>
> arch/powerpc/Kconfig | 2 +
> arch/powerpc/include/asm/set_memory.h | 32 ++++++++++
> arch/powerpc/mm/Makefile | 2 +-
> arch/powerpc/mm/pageattr.c | 85 +++++++++++++++++++++++++++
> 4 files changed, 120 insertions(+), 1 deletion(-)
> create mode 100644 arch/powerpc/include/asm/set_memory.h
> create mode 100644 arch/powerpc/mm/pageattr.c
>
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 8c1c636308c8..3d98240ce965 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -131,7 +131,9 @@ config PPC
> select ARCH_HAS_PTE_SPECIAL
> select ARCH_HAS_MEMBARRIER_CALLBACKS
> select ARCH_HAS_SCALED_CPUTIME if VIRT_CPU_ACCOUNTING_NATIVE && PPC64
> + select ARCH_HAS_SET_MEMORY
> select ARCH_HAS_STRICT_KERNEL_RWX if ((PPC_BOOK3S_64 || PPC32) && !RELOCATABLE && !HIBERNATION)
> + select ARCH_HAS_STRICT_MODULE_RWX if PPC_BOOK3S_64 || PPC32
> select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
> select ARCH_HAS_UACCESS_FLUSHCACHE if PPC64
> select ARCH_HAS_UBSAN_SANITIZE_ALL
> diff --git a/arch/powerpc/include/asm/set_memory.h b/arch/powerpc/include/asm/set_memory.h
> new file mode 100644
> index 000000000000..4b9683f3b3dd
> --- /dev/null
> +++ b/arch/powerpc/include/asm/set_memory.h
> @@ -0,0 +1,32 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +#ifndef _ASM_POWERPC_SET_MEMORY_H
> +#define _ASM_POWERPC_SET_MEMORY_H
> +
> +#define SET_MEMORY_RO 1
> +#define SET_MEMORY_RW 2
> +#define SET_MEMORY_NX 3
> +#define SET_MEMORY_X 4
> +
> +int change_memory(unsigned long addr, int numpages, int action);
> +
> +static inline int set_memory_ro(unsigned long addr, int numpages)
> +{
> + return change_memory(addr, numpages, SET_MEMORY_RO);
> +}
> +
> +static inline int set_memory_rw(unsigned long addr, int numpages)
> +{
> + return change_memory(addr, numpages, SET_MEMORY_RW);
> +}
> +
> +static inline int set_memory_nx(unsigned long addr, int numpages)
> +{
> + return change_memory(addr, numpages, SET_MEMORY_NX);
> +}
> +
> +static inline int set_memory_x(unsigned long addr, int numpages)
> +{
> + return change_memory(addr, numpages, SET_MEMORY_X);
> +}
> +
> +#endif
> diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile
> index 0f499db315d6..b683d1c311b3 100644
> --- a/arch/powerpc/mm/Makefile
> +++ b/arch/powerpc/mm/Makefile
> @@ -7,7 +7,7 @@ ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
>
> obj-y := fault.o mem.o pgtable.o mmap.o \
> init_$(BITS).o pgtable_$(BITS).o \
> - pgtable-frag.o \
> + pgtable-frag.o pageattr.o \
> init-common.o mmu_context.o drmem.o
> obj-$(CONFIG_PPC_MMU_NOHASH) += nohash/
> obj-$(CONFIG_PPC_BOOK3S_32) += book3s32/
> diff --git a/arch/powerpc/mm/pageattr.c b/arch/powerpc/mm/pageattr.c
> new file mode 100644
> index 000000000000..41baf92f632b
> --- /dev/null
> +++ b/arch/powerpc/mm/pageattr.c
> @@ -0,0 +1,85 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +/*
> + * Page attribute and set_memory routines
> + *
> + * Derived from the arm64 implementation.
> + *
> + * Author: Russell Currey <ruscur@russell.cc>
> + *
> + * Copyright 2019, IBM Corporation.
> + *
> + */
> +
> +#include <linux/mm.h>
> +#include <linux/set_memory.h>
> +#include <linux/vmalloc.h>
> +
> +#include <asm/mmu.h>
> +#include <asm/page.h>
> +#include <asm/pgtable.h>
> +
> +static int change_page_ro(pte_t *ptep, pgtable_t token, unsigned long addr, void *data)
> +{
> + set_pte_at(&init_mm, addr, ptep, pte_wrprotect(READ_ONCE(*ptep)));
> + return 0;
> +}
We can't use set_pte_at when updating a valid pte entry. This should have
triggered
/*
* Make sure hardware valid bit is not set. We don't do
* tlb flush for this update.
*/
VM_WARN_ON(pte_hw_valid(*ptep) && !pte_protnone(*ptep));
The details are explained as part of
56eecdb912b536a4fa97fb5bfe5a940a54d79be6
-aneesh
^ permalink raw reply
* Re: [v3 7/7] powerpc/64s: save r13 in MCE handler (simulator workaroud)
From: Reza Arbab @ 2019-07-08 15:11 UTC (permalink / raw)
To: Nicholas Piggin
Cc: Santosh Sivaraj, Aneesh Kumar K.V, Mahesh Salgaonkar,
Chandan Rajendra, linuxppc-dev
In-Reply-To: <1562406898.yw658lj3rp.astroid@bobo.none>
On Sat, Jul 06, 2019 at 07:56:39PM +1000, Nicholas Piggin wrote:
>Santosh Sivaraj's on July 6, 2019 7:26 am:
>> From: Reza Arbab <arbab@linux.ibm.com>
>>
>> Testing my memcpy_mcsafe() work in progress with an injected UE, I get
>> an error like this immediately after the function returns:
>>
>> BUG: Unable to handle kernel data access at 0x7fff84dec8f8
>> Faulting instruction address: 0xc0080000009c00b0
>> Oops: Kernel access of bad area, sig: 11 [#1]
>> LE PAGE_SIZE=64K MMU=Radix MMU=Hash SMP NR_CPUS=2048 NUMA PowerNV
>> Modules linked in: mce(O+) vmx_crypto crc32c_vpmsum
>> CPU: 0 PID: 1375 Comm: modprobe Tainted: G O 5.1.0-rc6 #267
>> NIP: c0080000009c00b0 LR: c0080000009c00a8 CTR: c000000000095f90
>> REGS: c0000000ee197790 TRAP: 0300 Tainted: G O (5.1.0-rc6)
>> MSR: 900000000280b033 <SF,HV,VEC,VSX,EE,FP,ME,IR,DR,RI,LE> CR: 88002826 XER: 00040000
>> CFAR: c000000000095f8c DAR: 00007fff84dec8f8 DSISR: 40000000 IRQMASK: 0
>> GPR00: 000000006c6c6568 c0000000ee197a20 c0080000009c8400 fffffffffffffff2
>> GPR04: c0080000009c02e0 0000000000000006 0000000000000000 c000000003c834c8
>> GPR08: 0080000000000000 776a6681b7fb5100 0000000000000000 c0080000009c01c8
>> GPR12: c000000000095f90 00007fff84debc00 000000004d071440 0000000000000000
>> GPR16: 0000000100000601 c0080000009e0000 c000000000c98dd8 c000000000c98d98
>> GPR20: c000000003bba970 c0080000009c04d0 c0080000009c0618 c0000000001e5820
>> GPR24: 0000000000000000 0000000000000100 0000000000000001 c000000003bba958
>> GPR28: c0080000009c02e8 c0080000009c0318 c0080000009c02e0 0000000000000000
>> NIP [c0080000009c00b0] cause_ue+0xa8/0xe8 [mce]
>> LR [c0080000009c00a8] cause_ue+0xa0/0xe8 [mce]
>>
>> After debugging we see that the first instruction at vector 200 is skipped by
>> the simulator, due to which r13 is not saved. Adding a nop at 0x200 fixes the
>> issue.
>>
>> (This commit is needed for testing this series. This should not be taken
>> into the tree)
>
>Would be good if this was testable in simulator upstream, did you
>report it? What does cause_ue do? exc_mce in mambo seems to do the
>right thing AFAIKS.
I think I posted this earlier, but cause_ue() is just a test function
telling me where to set up the error injection:
static noinline void cause_ue(void)
{
static const char src[] = "hello";
char dst[10];
int rc;
/* During the pause, break into mambo and run the following */
pr_info("inject_mce_ue_on_addr 0x%px\n", src);
pause(10);
rc = memcpy_mcsafe(dst, src, sizeof(src));
pr_info("memcpy_mcsafe() returns %d\n", rc);
if (!rc)
pr_info("dst=\"%s\"\n", dst);
}
Can't speak for the others, but I haven't chased this upstream. I didn't
know it was a simulator issue.
--
Reza Arbab
^ permalink raw reply
* Re: [PATCH v2] tpm: tpm_ibm_vtpm: Fix unallocated banks
From: Jarkko Sakkinen @ 2019-07-08 15:11 UTC (permalink / raw)
To: Nayna Jain, linux-integrity, linuxppc-dev
Cc: Sachin Sant, George Wilson, linux-kernel, Mimi Zohar,
Jason Gunthorpe, Peter Huewe, Michal Suchanek
In-Reply-To: <1562458725-15999-1-git-send-email-nayna@linux.ibm.com>
On Sat, 2019-07-06 at 20:18 -0400, Nayna Jain wrote:
> +/*
> + * tpm_get_pcr_allocation() - initialize the chip allocated banks for PCRs
> + * @chip: TPM chip to use.
> + */
> +static int tpm_get_pcr_allocation(struct tpm_chip *chip)
> +{
> + int rc;
> +
> + if (chip->flags & TPM_CHIP_FLAG_TPM2)
> + rc = tpm2_get_pcr_allocation(chip);
> + else
> + rc = tpm1_get_pcr_allocation(chip);
> +
> + return rc;
> +}
It is just a trivial static function, which means that kdoc comment is
not required and neither it is useful. Please remove that. I would
rewrite the function like:
static int tpm_get_pcr_allocation(struct tpm_chip *chip)
{
int rc;
rc = (chip->flags & TPM_CHIP_FLAG_TPM2) ?
tpm2_get_pcr_allocation(chip) :
tpm1_get_pcr_allocation(chip);
return rc > 0 ? -ENODEV : rc;
}
This addresses the issue that Stefan also pointed out. You have to
deal with the TPM error codes.
/Jarkko
^ permalink raw reply
* Re: [PATCH] tpm: fixes uninitialized allocated banks for IBM vtpm driver
From: Jarkko Sakkinen @ 2019-07-08 15:12 UTC (permalink / raw)
To: Nayna, Stefan Berger, Nayna Jain, linux-integrity, linuxppc-dev
Cc: Sachin Sant, Michal Suchanek, linux-kernel, Mimi Zohar,
Jason Gunthorpe, Peter Huewe, George Wilson
In-Reply-To: <bd961ef2-baed-8fc3-7f21-566bbcf9da8b@linux.vnet.ibm.com>
On Sat, 2019-07-06 at 20:25 -0400, Nayna wrote:
> Thanks Jarkko. I just now posted the v2 version that includes your and
> Stefan's feedbacks.
Looks almost legit :-)
/Jarkko
^ permalink raw reply
* Re: [v2 09/12] powerpc/mce: Enable MCE notifiers in external modules
From: Reza Arbab @ 2019-07-08 15:23 UTC (permalink / raw)
To: Nicholas Piggin
Cc: Santosh Sivaraj, Aneesh Kumar K.V, Mahesh Salgaonkar,
Chandan Rajendra, linuxppc-dev
In-Reply-To: <1562304274.ecukc5yx4t.astroid@bobo.none>
On Fri, Jul 05, 2019 at 03:29:39PM +1000, Nicholas Piggin wrote:
>Okay. It might be possible to save the address in the kernel and
>then notify the driver afterward. For user-mode and any non-atomic
>user copy AFAIK the irq_work should practically run synchronously
>after the machine check returns so it might be enough to have a
>notifier in the irq work processing.
We can pick up this thread later, but if I remember correctly the
sticking point we ran into was that we never got that far. Instead of
returning from the MCE, we went down the fatal codepath.
--
Reza Arbab
^ permalink raw reply
* Re: powerpc/83xx: fix use-after-free on mpc831x_usb_cfg()
From: Markus Elfring @ 2019-07-08 15:21 UTC (permalink / raw)
To: Wen Yang, linuxppc-dev
Cc: Yi Wang, Cheng Shengyu, kernel-janitors, linux-kernel, Scott Wood,
Paul Mackerras, Xue Zhihong
In-Reply-To: <1562317084-13140-1-git-send-email-wen.yang99@zte.com.cn>
> The np variable is still being used after the of_node_put() call,
> which may result in use-after-free.
> We fix this issue by calling of_node_put() after the last usage.
I imagine that this commit description can be improved a bit more
(by mentioning the influence of “immr_node”?).
How do you think about to omit the word “We” here?
> This patatch also do some cleanup.
Should the renaming of a jump label be contributed in a separate update
step of a small patch series besides a wording without a typo?
Regards,
Markus
^ permalink raw reply
* Re: [PATCH v4 1/8] KVM: PPC: Ultravisor: Introduce the MSR_S bit
From: janani @ 2019-07-08 17:38 UTC (permalink / raw)
To: Claudio Carvalho
Cc: Madhavan Srinivasan, Michael Anderson, Ram Pai, kvm-ppc,
Bharata B Rao, linuxppc-dev, Ryan Grimm, Sukadev Bhattiprolu,
Thiago Bauermann, Anshuman Khandual
In-Reply-To: <20190628200825.31049-2-cclaudio@linux.ibm.com>
On 2019-06-28 15:08, Claudio Carvalho wrote:
> From: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
>
> The ultravisor processor mode is introduced in POWER platforms that
> supports the Protected Execution Facility (PEF). Ultravisor is higher
> privileged than hypervisor mode.
>
> In PEF enabled platforms, the MSR_S bit is used to indicate if the
> thread is in secure state. With the MSR_S bit, the privilege state of
> the thread is now determined by MSR_S, MSR_HV and MSR_PR, as follows:
>
> S HV PR
> -----------------------
> 0 x 1 problem
> 1 0 1 problem
> x x 0 privileged
> x 1 0 hypervisor
> 1 1 0 ultravisor
> 1 1 1 reserved
>
> The hypervisor doesn't (and can't) run with the MSR_S bit set, but a
> secure guest and the ultravisor firmware do.
>
> Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
> Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> [ Update the commit message ]
> Signed-off-by: Claudio Carvalho <cclaudio@linux.ibm.com>
Reviewed-by: Janani Janakiraman <janani@linux.ibm.com>
> ---
> arch/powerpc/include/asm/reg.h | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/arch/powerpc/include/asm/reg.h
> b/arch/powerpc/include/asm/reg.h
> index 10caa145f98b..39b4c0a519f5 100644
> --- a/arch/powerpc/include/asm/reg.h
> +++ b/arch/powerpc/include/asm/reg.h
> @@ -38,6 +38,7 @@
> #define MSR_TM_LG 32 /* Trans Mem Available */
> #define MSR_VEC_LG 25 /* Enable AltiVec */
> #define MSR_VSX_LG 23 /* Enable VSX */
> +#define MSR_S_LG 22 /* Secure VM bit */
> #define MSR_POW_LG 18 /* Enable Power Management */
> #define MSR_WE_LG 18 /* Wait State Enable */
> #define MSR_TGPR_LG 17 /* TLB Update registers in use */
> @@ -71,11 +72,13 @@
> #define MSR_SF __MASK(MSR_SF_LG) /* Enable 64 bit mode */
> #define MSR_ISF __MASK(MSR_ISF_LG) /* Interrupt 64b mode valid on 630
> */
> #define MSR_HV __MASK(MSR_HV_LG) /* Hypervisor state */
> +#define MSR_S __MASK(MSR_S_LG) /* Secure state */
> #else
> /* so tests for these bits fail on 32-bit */
> #define MSR_SF 0
> #define MSR_ISF 0
> #define MSR_HV 0
> +#define MSR_S 0
> #endif
>
> /*
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox