mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2026-02-04 11:07:43 +01:00
kernel: mtdsplit: create executable prolog splitter
The problem is the following: we have three fixed partitions in a RedBoot partition for kernel, initrd and rootfs. On the surface this looks good. But we have little flash and want to use it efficiently. We want to use the OpenWrt "firmware" partition scheme where the kernel, initramfs and sqashfs+jffs2 rootfs is appended, leaving maximum space for a writeable rootfs. To do this we will override the existing RedBoot partition table with one that merges the three separate partitions into one "firmware" partition. RedBoot is still booting the system. It still needs to read the first two parts "as if" these were the kernel and initrd. This works fine, because the kernel still comes first. We already have hacks in place to merge the two kernel and initrd into one binary image and execute it. This is done by prepending a "prolog" to the kernel that does the necessary copying in memory and then jumps to execute the kernel. Since this "prolog" copying routine is just 92 bytes but has 512 bytes allocated, we can trivially create a firmware format that can be used for splitting the image into kernel and rootfs using a tagging scheme that can be done directly by scripting so we don't need any special binary programs. This splitter implements that idea. This will be used on the Gemini platform and was tested on the Raidsonic IB-4220-B. Link: https://github.com/openwrt/openwrt/pull/21820 Signed-off-by: Linus Walleij <linusw@kernel.org>
This commit is contained in:
parent
3f0de6a28d
commit
5ac8f14ccb
3 changed files with 140 additions and 0 deletions
|
|
@ -115,3 +115,8 @@ config MTD_SPLIT_MSTC_BOOT
|
|||
bool "MSTC bootnum-based parser"
|
||||
depends on MTD_SPLIT_SUPPORT
|
||||
select MTD_SPLIT
|
||||
|
||||
config MTD_SPLIT_OPENWRT_PROLOG
|
||||
bool "OpenWrt executable prolog-based parser"
|
||||
depends on MTD_SPLIT_SUPPORT
|
||||
select MTD_SPLIT
|
||||
|
|
|
|||
|
|
@ -18,3 +18,4 @@ obj-$(CONFIG_MTD_SPLIT_JIMAGE_FW) += mtdsplit_jimage.o
|
|||
obj-$(CONFIG_MTD_SPLIT_ELF_FW) += mtdsplit_elf.o
|
||||
obj-$(CONFIG_MTD_SPLIT_H3C_VFS) += mtdsplit_h3c_vfs.o
|
||||
obj-$(CONFIG_MTD_SPLIT_MSTC_BOOT) += mtdsplit_mstc_boot.o
|
||||
obj-$(CONFIG_MTD_SPLIT_OPENWRT_PROLOG) += mtdsplit_owrt_prolog.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (C) 2026 Linus Walleij <linusw@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* The idea about the "executable prolog" is simple: this is an assembly
|
||||
* prolog that OpenWrt adds in front of the kernel to move it around in
|
||||
* memory. Since it is 512 bytes, we can also store the kernel file size
|
||||
* in the prolog and use it for splitting the partition.
|
||||
*
|
||||
* This format has been designed to be easy to create an executable prolog
|
||||
* with a parseable size from bash scripts:
|
||||
*
|
||||
* cat executable_prolog > image.bin
|
||||
* echo "OPENWRT-PROLOG-512" >> image.bin
|
||||
* stat -c %s zImage >> image.image
|
||||
* dd if=image.bin of=image.new bs=512
|
||||
* mv image.new image.bin
|
||||
*
|
||||
* will create a 512 bytes executable prolog with the needed tag somewhere
|
||||
* in the header area. (The executable_prolog needs to be small.)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "mtdsplit.h"
|
||||
|
||||
#define OWRT_PROLOG_SIZE 512
|
||||
#define OWRT_PROLOG_MAX_OVERHEAD 32
|
||||
#define OWRT_PROLOG_MAGIC "OPENWRT-PROLOG-512"
|
||||
#define OWRT_PROLOG_NR_PARTS 2
|
||||
|
||||
static int mtdsplit_parse_owrt_prolog(struct mtd_info *master,
|
||||
const struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct mtd_partition *parts;
|
||||
char buf[OWRT_PROLOG_SIZE];
|
||||
size_t retlen;
|
||||
unsigned long kernel_size, rootfs_offset;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = mtd_read(master, 0, sizeof(buf), &retlen, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (retlen != sizeof(buf))
|
||||
return -EIO;
|
||||
|
||||
for (i = 0; i < (OWRT_PROLOG_SIZE - OWRT_PROLOG_MAX_OVERHEAD); i++) {
|
||||
if (!strncmp(OWRT_PROLOG_MAGIC, buf + i, strlen(OWRT_PROLOG_MAGIC)))
|
||||
break;
|
||||
}
|
||||
if (i == (OWRT_PROLOG_SIZE - OWRT_PROLOG_MAX_OVERHEAD)) {
|
||||
pr_err("%s: no OpenWrt prolog found\n", master->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("%s: OpenWrt prolog found at offset %d\n", master->name, i);
|
||||
|
||||
i += strlen(OWRT_PROLOG_MAGIC);
|
||||
i++; /* Skip linefeed after the magic */
|
||||
|
||||
ret = kstrtol(buf + i, 10, &kernel_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* From the MTD point of view, the prolog is part of
|
||||
* the kernel.
|
||||
*/
|
||||
kernel_size += OWRT_PROLOG_SIZE;
|
||||
pr_debug("%s: OpenWrt prolog kernel size %08lx\n",
|
||||
master->name, kernel_size);
|
||||
|
||||
/* rootfs starts at the next 0x20000 (128k) boundary: */
|
||||
rootfs_offset = round_up(kernel_size, 0x20000);
|
||||
|
||||
pr_debug("%s: OpenWrt prolog rootfs offset %08lx\n",
|
||||
master->name, rootfs_offset);
|
||||
|
||||
if (rootfs_offset >= master->size)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
parts = kzalloc(OWRT_PROLOG_NR_PARTS * sizeof(*parts), GFP_KERNEL);
|
||||
if (!parts)
|
||||
return -ENOMEM;
|
||||
|
||||
parts[0].name = KERNEL_PART_NAME;
|
||||
parts[0].offset = 0;
|
||||
parts[0].size = kernel_size;
|
||||
|
||||
parts[1].name = ROOTFS_PART_NAME;
|
||||
parts[1].offset = rootfs_offset;
|
||||
parts[1].size = master->size - rootfs_offset;
|
||||
|
||||
*pparts = parts;
|
||||
return OWRT_PROLOG_NR_PARTS;
|
||||
}
|
||||
|
||||
static const struct of_device_id mtdsplit_owrt_prolog_of_match_table[] = {
|
||||
{ .compatible = "openwrt,executable-prolog" },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct mtd_part_parser mtdsplit_owrt_prolog_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "executable-prolog",
|
||||
.of_match_table = mtdsplit_owrt_prolog_of_match_table,
|
||||
.parse_fn = mtdsplit_parse_owrt_prolog,
|
||||
.type = MTD_PARSER_TYPE_FIRMWARE,
|
||||
};
|
||||
|
||||
static int __init mtdsplit_owrt_prolog_init(void)
|
||||
{
|
||||
register_mtd_parser(&mtdsplit_owrt_prolog_parser);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
subsys_initcall(mtdsplit_owrt_prolog_init);
|
||||
Loading…
Add table
Reference in a new issue