File Index
Files and directories
Sign in
FILE

parser_imagetag.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mtd/parsers/parser_imagetag.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * BCM63XX CFE image tag parser
  4 *
  5 * Copyright © 2006-2008  Florian Fainelli <florian@openwrt.org>
  6 *			  Mike Albon <malbon@openwrt.org>
  7 * Copyright © 2009-2010  Daniel Dickinson <openwrt@cshore.neomailbox.net>
  8 * Copyright © 2011-2013  Jonas Gorski <jonas.gorski@gmail.com>
  9 */
 10
 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 12
 13#include <linux/bcm963xx_tag.h>
 14#include <linux/crc32.h>
 15#include <linux/module.h>
 16#include <linux/kernel.h>
 17#include <linux/sizes.h>
 18#include <linux/slab.h>
 19#include <linux/vmalloc.h>
 20#include <linux/mtd/mtd.h>
 21#include <linux/mtd/partitions.h>
 22#include <linux/of.h>
 23
 24/* Ensure strings read from flash structs are null terminated */
 25#define STR_NULL_TERMINATE(x) \
 26	do { char *_str = (x); _str[sizeof(x) - 1] = 0; } while (0)
 27
 28static int bcm963xx_read_imagetag(struct mtd_info *master, const char *name,
 29	loff_t tag_offset, struct bcm_tag *buf)
 30{
 31	int ret;
 32	size_t retlen;
 33	u32 computed_crc;
 34
 35	ret = mtd_read(master, tag_offset, sizeof(*buf), &retlen, (void *)buf);
 36	if (ret)
 37		return ret;
 38
 39	if (retlen != sizeof(*buf))
 40		return -EIO;
 41
 42	computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf,
 43				offsetof(struct bcm_tag, header_crc));
 44	if (computed_crc == buf->header_crc) {
 45		STR_NULL_TERMINATE(buf->board_id);
 46		STR_NULL_TERMINATE(buf->tag_version);
 47
 48		pr_info("%s: CFE image tag found at 0x%llx with version %s, board type %s\n",
 49			name, tag_offset, buf->tag_version, buf->board_id);
 50
 51		return 0;
 52	}
 53
 54	pr_warn("%s: CFE image tag at 0x%llx CRC invalid (expected %08x, actual %08x)\n",
 55		name, tag_offset, buf->header_crc, computed_crc);
 56	return -EINVAL;
 57}
 58
 59static int bcm963xx_parse_imagetag_partitions(struct mtd_info *master,
 60					const struct mtd_partition **pparts,
 61					struct mtd_part_parser_data *data)
 62{
 63	/* CFE, NVRAM and global Linux are always present */
 64	int nrparts = 0, curpart = 0;
 65	struct bcm_tag *buf = NULL;
 66	struct mtd_partition *parts;
 67	int ret;
 68	unsigned int rootfsaddr, kerneladdr, spareaddr, offset;
 69	unsigned int rootfslen, kernellen, sparelen, totallen;
 70	int i;
 71	bool rootfs_first = false;
 72
 73	buf = vmalloc(sizeof(struct bcm_tag));
 74	if (!buf)
 75		return -ENOMEM;
 76
 77	/* Get the tag */
 78	ret = bcm963xx_read_imagetag(master, "rootfs", 0, buf);
 79	if (!ret) {
 80		STR_NULL_TERMINATE(buf->flash_image_start);
 81		if (kstrtouint(buf->flash_image_start, 10, &rootfsaddr) ||
 82				rootfsaddr < BCM963XX_EXTENDED_SIZE) {
 83			pr_err("invalid rootfs address: %*ph\n",
 84				(int)sizeof(buf->flash_image_start),
 85				buf->flash_image_start);
 86			ret = -EINVAL;
 87			goto out;
 88		}
 89
 90		STR_NULL_TERMINATE(buf->kernel_address);
 91		if (kstrtouint(buf->kernel_address, 10, &kerneladdr) ||
 92				kerneladdr < BCM963XX_EXTENDED_SIZE) {
 93			pr_err("invalid kernel address: %*ph\n",
 94				(int)sizeof(buf->kernel_address),
 95				buf->kernel_address);
 96			ret = -EINVAL;
 97			goto out;
 98		}
 99
100		STR_NULL_TERMINATE(buf->kernel_length);
101		if (kstrtouint(buf->kernel_length, 10, &kernellen)) {
102			pr_err("invalid kernel length: %*ph\n",
103				(int)sizeof(buf->kernel_length),
104				buf->kernel_length);
105			ret = -EINVAL;
106			goto out;
107		}
108
109		STR_NULL_TERMINATE(buf->total_length);
110		if (kstrtouint(buf->total_length, 10, &totallen)) {
111			pr_err("invalid total length: %*ph\n",
112				(int)sizeof(buf->total_length),
113				buf->total_length);
114			ret = -EINVAL;
115			goto out;
116		}
117
118		/*
119		 * Addresses are flash absolute, so convert to partition
120		 * relative addresses. Assume either kernel or rootfs will
121		 * directly follow the image tag.
122		 */
123		if (rootfsaddr < kerneladdr)
124			offset = rootfsaddr - sizeof(struct bcm_tag);
125		else
126			offset = kerneladdr - sizeof(struct bcm_tag);
127
128		kerneladdr = kerneladdr - offset;
129		rootfsaddr = rootfsaddr - offset;
130		spareaddr = roundup(totallen, master->erasesize);
131
132		if (rootfsaddr < kerneladdr) {
133			/* default Broadcom layout */
134			rootfslen = kerneladdr - rootfsaddr;
135			rootfs_first = true;
136		} else {
137			/* OpenWrt layout */
138			rootfsaddr = kerneladdr + kernellen;
139			rootfslen = spareaddr - rootfsaddr;
140		}
141	} else {
142		goto out;
143	}
144	sparelen = master->size - spareaddr;
145
146	/* Determine number of partitions */
147	if (rootfslen > 0)
148		nrparts++;
149
150	if (kernellen > 0)
151		nrparts++;
152
153	parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
154	if (!parts) {
155		ret = -ENOMEM;
156		goto out;
157	}
158
159	/* Start building partition list */
160	if (kernellen > 0) {
161		int kernelpart = curpart;
162
163		if (rootfslen > 0 && rootfs_first)
164			kernelpart++;
165		parts[kernelpart].name = "kernel";
166		parts[kernelpart].offset = kerneladdr;
167		parts[kernelpart].size = kernellen;
168		curpart++;
169	}
170
171	if (rootfslen > 0) {
172		int rootfspart = curpart;
173
174		if (kernellen > 0 && rootfs_first)
175			rootfspart--;
176		parts[rootfspart].name = "rootfs";
177		parts[rootfspart].offset = rootfsaddr;
178		parts[rootfspart].size = rootfslen;
179		if (sparelen > 0  && !rootfs_first)
180			parts[rootfspart].size += sparelen;
181		curpart++;
182	}
183
184	for (i = 0; i < nrparts; i++)
185		pr_info("Partition %d is %s offset %llx and length %llx\n", i,
186			parts[i].name, parts[i].offset,	parts[i].size);
187
188	pr_info("Spare partition is offset %x and length %x\n",	spareaddr,
189		sparelen);
190
191	*pparts = parts;
192	ret = 0;
193
194out:
195	vfree(buf);
196
197	if (ret)
198		return ret;
199
200	return nrparts;
201}
202
203static const struct of_device_id parse_bcm963xx_imagetag_match_table[] = {
204	{ .compatible = "brcm,bcm963xx-imagetag" },
205	{},
206};
207MODULE_DEVICE_TABLE(of, parse_bcm963xx_imagetag_match_table);
208
209static struct mtd_part_parser bcm963xx_imagetag_parser = {
210	.parse_fn = bcm963xx_parse_imagetag_partitions,
211	.name = "bcm963xx-imagetag",
212	.of_match_table = parse_bcm963xx_imagetag_match_table,
213};
214module_mtd_part_parser(bcm963xx_imagetag_parser);
215
216MODULE_LICENSE("GPL");
217MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>");
218MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
219MODULE_AUTHOR("Mike Albon <malbon@openwrt.org>");
220MODULE_AUTHOR("Jonas Gorski <jonas.gorski@gmail.com>");
221MODULE_DESCRIPTION("MTD parser for BCM963XX CFE Image Tag partitions");
~/ Read-only file preview.