diff --git a/package/kernel/ubootenv-nvram/Makefile b/package/kernel/ubootenv-nvram/Makefile new file mode 100644 index 0000000000..0574ea61d7 --- /dev/null +++ b/package/kernel/ubootenv-nvram/Makefile @@ -0,0 +1,30 @@ +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:=ubootenv-nvram +PKG_RELEASE:=1 +PKG_LICENSE:=GPL-2.0 + +include $(INCLUDE_DIR)/package.mk + +define KernelPackage/ubootenv-nvram + SUBMENU:=Other modules + TITLE:=NVRAM environment for uboot-envtools + FILES:=$(PKG_BUILD_DIR)/ubootenv-nvram.ko + AUTOLOAD:=$(call AutoLoad,30,ubootenv-nvram,1) + KCONFIG:= +endef + +define KernelPackage/ubootenv-nvram/description + Support vendor modified U-Boot storing the environment + in RAM. This driver exports the environment memory + region as a misc device named "ubootenv", pretending + it is a NOR mtd device to let existing userspace tools + work without modifications. +endef + +define Build/Compile + $(KERNEL_MAKE) M="$(PKG_BUILD_DIR)" modules +endef + +$(eval $(call KernelPackage,ubootenv-nvram)) diff --git a/package/kernel/ubootenv-nvram/src/Makefile b/package/kernel/ubootenv-nvram/src/Makefile new file mode 100644 index 0000000000..469cb801f3 --- /dev/null +++ b/package/kernel/ubootenv-nvram/src/Makefile @@ -0,0 +1 @@ +obj-m += ubootenv-nvram.o diff --git a/package/kernel/ubootenv-nvram/src/ubootenv-nvram.c b/package/kernel/ubootenv-nvram/src/ubootenv-nvram.c new file mode 100644 index 0000000000..f624414216 --- /dev/null +++ b/package/kernel/ubootenv-nvram/src/ubootenv-nvram.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Bjørn Mork + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NAME "ubootenv" + +struct ubootenv_drvdata { + void *env; + struct reserved_mem *rmem; + struct miscdevice misc; +}; + +static inline struct ubootenv_drvdata *to_ubootenv_drvdata(struct file *file) +{ + return container_of(file->private_data, struct ubootenv_drvdata, misc); +} + +static ssize_t ubootenv_write(struct file *file, const char __user *buffer, size_t count, + loff_t *ppos) +{ + struct ubootenv_drvdata *data = to_ubootenv_drvdata(file); + + if (!data->env) + return -EIO; + return simple_write_to_buffer(data->env, data->rmem->size, ppos, buffer, count); +} + +static ssize_t ubootenv_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct ubootenv_drvdata *data = to_ubootenv_drvdata(file); + + if (!data->env) + return 0; + return simple_read_from_buffer(buffer, count, ppos, data->env, data->rmem->size); +} + +static loff_t ubootenv_llseek(struct file *file, loff_t off, int whence) +{ + struct ubootenv_drvdata *data = to_ubootenv_drvdata(file); + + return fixed_size_llseek(file, off, whence, data->rmem->size); +} + +/* Faking the minimal mtd ioctl subset required by the fw_env.c */ +static long ubootenv_ioctl(struct file *file, u_int cmd, u_long arg) +{ + struct ubootenv_drvdata *data = to_ubootenv_drvdata(file); + void __user *argp = (void __user *)arg; + struct mtd_info_user info = { + .type = MTD_NORFLASH, + .size = data->rmem->size, + }; + + switch (cmd) { + case MEMISLOCKED: + case MEMERASE: + break; + case MEMGETINFO: + if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) + return -EFAULT; + break; + default: + return -ENOTTY; + } + return 0; +} + +static const struct file_operations ubootenv_fops = { + .owner = THIS_MODULE, + .read = ubootenv_read, + .write = ubootenv_write, + .llseek = ubootenv_llseek, + .unlocked_ioctl = ubootenv_ioctl, +}; + +/* We can only map a single reserved-memory range */ +static struct ubootenv_drvdata drvdata = { + .misc = { + .fops = &ubootenv_fops, + .minor = MISC_DYNAMIC_MINOR, + .name = NAME, + }, +}; + +const struct of_device_id of_ubootenv_match[] = { + { .compatible = "ubootenv" }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_ubootenv_match); + +static int ubootenv_probe(struct platform_device *pdev) +{ + struct ubootenv_drvdata *data = &drvdata; + struct device *dev = &pdev->dev; + struct device_node *np; + + /* enforce single instance */ + if (data->env) + return -EINVAL; + + np = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!np) + return -ENODEV; + + data->rmem = of_reserved_mem_lookup(np); + of_node_put(np); + if (!data->rmem) + return -ENODEV; + + if (!data->rmem->size || (data->rmem->size > ULONG_MAX)) + return -EINVAL; + + if (!PAGE_ALIGNED(data->rmem->base) || !PAGE_ALIGNED(data->rmem->size)) + return -EINVAL; + + data->env = devm_memremap(&pdev->dev, data->rmem->base, data->rmem->size, MEMREMAP_WB); + platform_set_drvdata(pdev, data); + + data->misc.parent = &pdev->dev; + return misc_register(&data->misc); +} + +static int ubootenv_remove(struct platform_device *pdev) +{ + struct ubootenv_drvdata *data = platform_get_drvdata(pdev); + + data->env = NULL; + misc_deregister(&data->misc); + return 0; +} + +static struct platform_driver ubootenv_driver = { + .probe = ubootenv_probe, + .remove = ubootenv_remove, + .driver = { + .name = NAME, + .owner = THIS_MODULE, + .of_match_table = of_ubootenv_match, + }, +}; + +module_platform_driver(ubootenv_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bjørn Mork "); +MODULE_DESCRIPTION("Access u-boot environment in RAM");