Currently when setting up an IMR around the kernel's .text area we lock that IMR, preventing further modification. While superficially this appears to be the right thing to do, in fact this doesn't account for a legitimate change in the memory map such as when executing a new kernel via kexec. In such a scenario a second kernel can have a different size and location to it's predecessor and can view some of the memory occupied by it's predecessor as legitimately usable DMA RAM. If this RAM were then subsequently allocated to DMA agents within the system it could conceivably trigger an IMR violation. This patch fixes the this potential situation by keeping the kernel's .text section IMR lock bit false by default, thus ensuring that a user of the system will have kexec just work without having to pass special parameters on the kernel command line to influence the state of the kernel's IMR. If a user so desires then it is possible to explicitly set the lock bit to true. The new parameter is kernel_imr and this may be set to kernel_imr=locked or kernel_imr=unlocked. Signed-off-by: Bryan O'Donoghue <pure.logic@xxxxxxxxxxxxxxxxx> Reported-by: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx> --- Documentation/kernel-parameters.txt | 7 +++++++ arch/x86/platform/intel-quark/imr.c | 39 +++++++++++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 9a53c92..1aad1d2 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1359,6 +1359,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. hardware thread id mappings. Format: <cpu>:<hwthread> + kernel_imr= [X86, INTEL_IMR] Control the lock bit for the Isolated + Memory Region protecting the kernel's .text section on + X86 architectures that support IMRs such as Quark X1000. + When an IMR lock bit is set it is not possible to unset + it without a CPU reset. + Format : {locked | unlocked (default) } + keep_bootcon [KNL] Do not unregister boot console at start. This is only useful for debugging when something happens in the window diff --git a/arch/x86/platform/intel-quark/imr.c b/arch/x86/platform/intel-quark/imr.c index c61b6c3..8249f65 100644 --- a/arch/x86/platform/intel-quark/imr.c +++ b/arch/x86/platform/intel-quark/imr.c @@ -37,6 +37,7 @@ struct imr_device { struct dentry *file; bool init; + bool kernel_imr_locked; struct mutex lock; int max_imr; int reg_base; @@ -568,10 +569,15 @@ static inline int imr_clear(int reg) * BIOS and Grub both setup IMRs around compressed kernel, initrd memory * that need to be removed before the kernel hands out one of the IMR * encased addresses to a downstream DMA agent such as the SD or Ethernet. + * Additionally if the current kernel is executing via kexec then we need to + * tear down the IMR the previous kernel had setup. + * * IMRs on Galileo are setup to immediately reset the system on violation. * As a result if you're running a root filesystem from SD - you'll need * the boot-time IMRs torn down or you'll find seemingly random resets when - * using your filesystem. + * using your filesystem; similarly if you're doing a kexec boot of Linux then + * its required to sanitize the memory map with-respect to the previous IMR + * settings. * * @idev: pointer to imr_device structure. * @return: @@ -592,14 +598,20 @@ static void __init imr_fixup_memmap(struct imr_device *idev) end = (unsigned long)__end_rodata - 1; /* - * Setup a locked IMR around the physical extent of the kernel - * from the beginning of the .text secton to the end of the - * .rodata section as one physically contiguous block. + * Setup an IMR around the physical extent of the kernel from the + * beginning of the .text section to the end of the .rodata section + * as one physically contiguous block. * * We don't round up @size since it is already PAGE_SIZE aligned. * See vmlinux.lds.S for details. + * + * By default this IMR is unlocked to enable subsequent kernels booted + * by kexec to tear down the IMR we are setting up below. Its possible + * to set this IMR to the locked state by passing kernel_imr=locked on + * the kernel command line. */ - ret = imr_add_range(base, size, IMR_CPU, IMR_CPU, true); + ret = imr_add_range(base, size, IMR_CPU, IMR_CPU, + imr_dev.kernel_imr_locked); if (ret < 0) { pr_err("unable to setup IMR for kernel: %zu KiB (%lx - %lx)\n", size / 1024, start, end); @@ -617,8 +629,23 @@ static const struct x86_cpu_id imr_ids[] __initconst = { MODULE_DEVICE_TABLE(x86cpu, imr_ids); /** - * imr_init - entry point for IMR driver. + * imr_kernel_lock_setup - control the lock bit of the kernel's IMR * + */ +static int __init imr_kernel_lock_setup(char *str) +{ + if (!strcmp(str, "unlocked")) + imr_dev.kernel_imr_locked = false; + else if (!strcmp(str, "locked")) + imr_dev.kernel_imr_locked = true; + else + return 0; + return 1; +} +__setup("kernel_imr=", imr_kernel_lock_setup); + +/** + * imr_init - entry point for IMR driver. * return: -ENODEV for no IMR support 0 if good to go. */ static int __init imr_init(void) -- 2.5.0 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html