When booting an arm64 kernel w/initrd using UEFI/grub, use of mem= will likely
cut off part or all of the initrd. This leaves it outside the kernel linear
map which leads to failure when unpacking. The x86 code has a similar need to
relocate an initrd outside of mapped memory in some cases.
The current x86 code uses early_memremap() to copy the original initrd from
unmapped to mapped RAM. This patchset creates a generic copy_from_early_mem()
utility based on that x86 code and has arm64 use it to relocate the initrd
if necessary.
Mark Salter (2):
mm: add utility for early copy from unmapped ram
arm64: support initrd outside kernel linear map
arch/arm64/kernel/setup.c | 55 +++++++++++++++++++++++++++++++++++++
include/asm-generic/early_ioremap.h | 6 ++++
mm/early_ioremap.c | 22 +++++++++++++++
3 files changed, 83 insertions(+)
--
2.4.3
In some early boot circumstances, it may be necessary to copy
from RAM outside the kernel linear mapping to mapped RAM. The
need to relocate an initrd is one example in the x86 code. This
patch creates a helper function based on current x86 code.
Signed-off-by: Mark Salter <[email protected]>
---
include/asm-generic/early_ioremap.h | 6 ++++++
mm/early_ioremap.c | 22 ++++++++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/include/asm-generic/early_ioremap.h b/include/asm-generic/early_ioremap.h
index a5de55c..e539f27 100644
--- a/include/asm-generic/early_ioremap.h
+++ b/include/asm-generic/early_ioremap.h
@@ -33,6 +33,12 @@ extern void early_ioremap_setup(void);
*/
extern void early_ioremap_reset(void);
+/*
+ * Early copy from unmapped memory to kernel mapped memory.
+ */
+extern void copy_from_early_mem(void *dest, phys_addr_t src,
+ unsigned long size);
+
#else
static inline void early_ioremap_init(void) { }
static inline void early_ioremap_setup(void) { }
diff --git a/mm/early_ioremap.c b/mm/early_ioremap.c
index e10ccd2..acba804 100644
--- a/mm/early_ioremap.c
+++ b/mm/early_ioremap.c
@@ -217,6 +217,28 @@ early_memremap(resource_size_t phys_addr, unsigned long size)
return (__force void *)__early_ioremap(phys_addr, size,
FIXMAP_PAGE_NORMAL);
}
+
+#define MAX_MAP_CHUNK (NR_FIX_BTMAPS << PAGE_SHIFT)
+
+void __init copy_from_early_mem(void *dest, phys_addr_t src, unsigned long size)
+{
+ unsigned long slop, clen;
+ char *p;
+
+ while (size) {
+ slop = src & ~PAGE_MASK;
+ clen = size;
+ if (clen > MAX_MAP_CHUNK - slop)
+ clen = MAX_MAP_CHUNK - slop;
+ p = early_memremap(src & PAGE_MASK, clen + slop);
+ memcpy(dest, p + slop, clen);
+ early_iounmap(p, clen + slop);
+ dest += clen;
+ src += clen;
+ size -= clen;
+ }
+}
+
#else /* CONFIG_MMU */
void __init __iomem *
--
2.4.3
The use of mem= could leave part or all of the initrd outside of
the kernel linear map. This will lead to an error when unpacking
the initrd and a probable failure to boot. This patch catches that
situation and relocates the initrd to be fully within the linear
map.
Signed-off-by: Mark Salter <[email protected]>
---
arch/arm64/kernel/setup.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index f3067d4..96339b5 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -359,6 +359,60 @@ static void __init request_standard_resources(void)
}
}
+/*
+ * Relocate initrd if it is not completely within the linear mapping.
+ * This would be the case if mem= cuts out all or part of it.
+ */
+static void __init relocate_initrd(void)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+ phys_addr_t orig_start = __virt_to_phys(initrd_start);
+ phys_addr_t orig_end = __virt_to_phys(initrd_end);
+ phys_addr_t ram_end = memblock_end_of_DRAM();
+ phys_addr_t new_start;
+ unsigned long size, to_free = 0;
+ void *dest;
+
+ if (orig_end <= ram_end)
+ return;
+
+ /* Note if any of original initrd will freeing below */
+ if (orig_start < ram_end)
+ to_free = ram_end - orig_start;
+
+ size = orig_end - orig_start;
+
+ /* initrd needs to be relocated completely inside linear mapping */
+ new_start = memblock_find_in_range(0, PFN_PHYS(max_pfn),
+ size, PAGE_SIZE);
+ if (!new_start)
+ panic("Cannot relocate initrd of size %ld\n", size);
+ memblock_reserve(new_start, size);
+
+ initrd_start = __phys_to_virt(new_start);
+ initrd_end = initrd_start + size;
+
+ pr_info("Moving initrd from [%llx-%llx] to [%llx-%llx]\n",
+ orig_start, orig_start + size - 1,
+ new_start, new_start + size - 1);
+
+ dest = (void *)initrd_start;
+
+ if (to_free) {
+ memcpy(dest, (void *)__phys_to_virt(orig_start), to_free);
+ dest += to_free;
+ }
+
+ copy_from_early_mem(dest, orig_start + to_free, size - to_free);
+
+ if (to_free) {
+ pr_info("Freeing original RAMDISK from [%llx-%llx]\n",
+ orig_start, orig_start + to_free - 1);
+ memblock_free(orig_start, to_free);
+ }
+#endif
+}
+
u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };
void __init setup_arch(char **cmdline_p)
@@ -392,6 +446,7 @@ void __init setup_arch(char **cmdline_p)
acpi_boot_table_init();
paging_init();
+ relocate_initrd();
request_standard_resources();
early_ioremap_reset();
--
2.4.3
Hi Mark,
As a heads-up, it looks like you missed a space when sending this; Arnd
and Ard got merged into:
"Arnd Bergmann <[email protected]>--cc=Ard Biesheuvel" <[email protected]>
I've corrected that for this reply.
On Tue, Jul 28, 2015 at 03:32:39PM +0100, Mark Salter wrote:
> When booting an arm64 kernel w/initrd using UEFI/grub, use of mem= will likely
> cut off part or all of the initrd. This leaves it outside the kernel linear
> map which leads to failure when unpacking. The x86 code has a similar need to
> relocate an initrd outside of mapped memory in some cases.
>
> The current x86 code uses early_memremap() to copy the original initrd from
> unmapped to mapped RAM. This patchset creates a generic copy_from_early_mem()
> utility based on that x86 code and has arm64 use it to relocate the initrd
> if necessary.
This sounds like a sane idea to me.
> Mark Salter (2):
> mm: add utility for early copy from unmapped ram
> arm64: support initrd outside kernel linear map
>
> arch/arm64/kernel/setup.c | 55 +++++++++++++++++++++++++++++++++++++
> include/asm-generic/early_ioremap.h | 6 ++++
> mm/early_ioremap.c | 22 +++++++++++++++
> 3 files changed, 83 insertions(+)
Any reason for not moving x86 over to the new generic version?
Mark.
On Tue, 2015-07-28 at 15:59 +0100, Mark Rutland wrote:
> Hi Mark,
>
> As a heads-up, it looks like you missed a space when sending this; Arnd
> and Ard got merged into:
>
> "Arnd Bergmann <[email protected]>--cc=Ard Biesheuvel" <
> [email protected]>
>
> I've corrected that for this reply.
Oops. Thanks.
>
> On Tue, Jul 28, 2015 at 03:32:39PM +0100, Mark Salter wrote:
> > When booting an arm64 kernel w/initrd using UEFI/grub, use of mem= will
> > likely
> > cut off part or all of the initrd. This leaves it outside the kernel
> > linear
> > map which leads to failure when unpacking. The x86 code has a similar
> > need to
> > relocate an initrd outside of mapped memory in some cases.
> >
> > The current x86 code uses early_memremap() to copy the original initrd
> > from
> > unmapped to mapped RAM. This patchset creates a generic
> > copy_from_early_mem()
> > utility based on that x86 code and has arm64 use it to relocate the
> > initrd
> > if necessary.
>
> This sounds like a sane idea to me.
>
> > Mark Salter (2):
> > mm: add utility for early copy from unmapped ram
> > arm64: support initrd outside kernel linear map
> >
> > arch/arm64/kernel/setup.c | 55
> > +++++++++++++++++++++++++++++++++++++
> > include/asm-generic/early_ioremap.h | 6 ++++
> > mm/early_ioremap.c | 22 +++++++++++++++
> > 3 files changed, 83 insertions(+)
>
> Any reason for not moving x86 over to the new generic version?
I have a patch to do that but I'm not sure how to contrive a
testcase to exercise it.
> > > arch/arm64/kernel/setup.c | 55
> > > +++++++++++++++++++++++++++++++++++++
> > > include/asm-generic/early_ioremap.h | 6 ++++
> > > mm/early_ioremap.c | 22 +++++++++++++++
> > > 3 files changed, 83 insertions(+)
> >
> > Any reason for not moving x86 over to the new generic version?
>
> I have a patch to do that but I'm not sure how to contrive a
> testcase to exercise it.
The easiest option might be to hack up KVM tool [1] to load a kernel an
initrd at suitable addresses.
Otherwise I'm sure someone else must have a way of exercising this.
Mark.
[1] https://git.kernel.org/cgit/linux/kernel/git/will/kvmtool.git/
On Tue, 2015-07-28 at 10:32 -0400, Mark Salter wrote:
> When booting an arm64 kernel w/initrd using UEFI/grub, use of mem= will likely
> cut off part or all of the initrd. This leaves it outside the kernel linear
> map which leads to failure when unpacking.
Have we got a similar issue for the device-tree blob?
--
Tixy
On Wed, Jul 29, 2015 at 10:20:38AM +0100, Jon Medhurst (Tixy) wrote:
> On Tue, 2015-07-28 at 10:32 -0400, Mark Salter wrote:
> > When booting an arm64 kernel w/initrd using UEFI/grub, use of mem= will likely
> > cut off part or all of the initrd. This leaves it outside the kernel linear
> > map which leads to failure when unpacking.
>
> Have we got a similar issue for the device-tree blob?
Commit 61bd93ce801bb6df ("arm64: use fixmap region for permanent FDT
mapping") [1] solved that for the DTB in v4.2-rc1.
Mark.
[1] https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=61bd93ce801bb6df36eda257a9d2d16c02863cdd