Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.0-rc5 325 lines 8.6 kB view raw
1/* 2 * BCM63XX CFE image tag parser 3 * 4 * Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org> 5 * Mike Albon <malbon@openwrt.org> 6 * Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net> 7 * Copyright © 2011-2013 Jonas Gorski <jonas.gorski@gmail.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 */ 24 25#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 26 27#include <linux/bcm963xx_nvram.h> 28#include <linux/bcm963xx_tag.h> 29#include <linux/crc32.h> 30#include <linux/module.h> 31#include <linux/kernel.h> 32#include <linux/sizes.h> 33#include <linux/slab.h> 34#include <linux/vmalloc.h> 35#include <linux/mtd/mtd.h> 36#include <linux/mtd/partitions.h> 37 38#define BCM963XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */ 39 40#define BCM963XX_CFE_MAGIC_OFFSET 0x4e0 41#define BCM963XX_CFE_VERSION_OFFSET 0x570 42#define BCM963XX_NVRAM_OFFSET 0x580 43 44/* Ensure strings read from flash structs are null terminated */ 45#define STR_NULL_TERMINATE(x) \ 46 do { char *_str = (x); _str[sizeof(x) - 1] = 0; } while (0) 47 48static int bcm63xx_detect_cfe(struct mtd_info *master) 49{ 50 char buf[9]; 51 int ret; 52 size_t retlen; 53 54 ret = mtd_read(master, BCM963XX_CFE_VERSION_OFFSET, 5, &retlen, 55 (void *)buf); 56 buf[retlen] = 0; 57 58 if (ret) 59 return ret; 60 61 if (strncmp("cfe-v", buf, 5) == 0) 62 return 0; 63 64 /* very old CFE's do not have the cfe-v string, so check for magic */ 65 ret = mtd_read(master, BCM963XX_CFE_MAGIC_OFFSET, 8, &retlen, 66 (void *)buf); 67 buf[retlen] = 0; 68 69 return strncmp("CFE1CFE1", buf, 8); 70} 71 72static int bcm63xx_read_nvram(struct mtd_info *master, 73 struct bcm963xx_nvram *nvram) 74{ 75 u32 actual_crc, expected_crc; 76 size_t retlen; 77 int ret; 78 79 /* extract nvram data */ 80 ret = mtd_read(master, BCM963XX_NVRAM_OFFSET, BCM963XX_NVRAM_V5_SIZE, 81 &retlen, (void *)nvram); 82 if (ret) 83 return ret; 84 85 ret = bcm963xx_nvram_checksum(nvram, &expected_crc, &actual_crc); 86 if (ret) 87 pr_warn("nvram checksum failed, contents may be invalid (expected %08x, got %08x)\n", 88 expected_crc, actual_crc); 89 90 if (!nvram->psi_size) 91 nvram->psi_size = BCM963XX_DEFAULT_PSI_SIZE; 92 93 return 0; 94} 95 96static int bcm63xx_read_image_tag(struct mtd_info *master, const char *name, 97 loff_t tag_offset, struct bcm_tag *buf) 98{ 99 int ret; 100 size_t retlen; 101 u32 computed_crc; 102 103 ret = mtd_read(master, tag_offset, sizeof(*buf), &retlen, (void *)buf); 104 if (ret) 105 return ret; 106 107 if (retlen != sizeof(*buf)) 108 return -EIO; 109 110 computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf, 111 offsetof(struct bcm_tag, header_crc)); 112 if (computed_crc == buf->header_crc) { 113 STR_NULL_TERMINATE(buf->board_id); 114 STR_NULL_TERMINATE(buf->tag_version); 115 116 pr_info("%s: CFE image tag found at 0x%llx with version %s, board type %s\n", 117 name, tag_offset, buf->tag_version, buf->board_id); 118 119 return 0; 120 } 121 122 pr_warn("%s: CFE image tag at 0x%llx CRC invalid (expected %08x, actual %08x)\n", 123 name, tag_offset, buf->header_crc, computed_crc); 124 return 1; 125} 126 127static int bcm63xx_parse_cfe_nor_partitions(struct mtd_info *master, 128 const struct mtd_partition **pparts, struct bcm963xx_nvram *nvram) 129{ 130 /* CFE, NVRAM and global Linux are always present */ 131 int nrparts = 3, curpart = 0; 132 struct bcm_tag *buf = NULL; 133 struct mtd_partition *parts; 134 int ret; 135 unsigned int rootfsaddr, kerneladdr, spareaddr; 136 unsigned int rootfslen, kernellen, sparelen, totallen; 137 unsigned int cfelen, nvramlen; 138 unsigned int cfe_erasesize; 139 int i; 140 bool rootfs_first = false; 141 142 cfe_erasesize = max_t(uint32_t, master->erasesize, 143 BCM963XX_CFE_BLOCK_SIZE); 144 145 cfelen = cfe_erasesize; 146 nvramlen = nvram->psi_size * SZ_1K; 147 nvramlen = roundup(nvramlen, cfe_erasesize); 148 149 buf = vmalloc(sizeof(struct bcm_tag)); 150 if (!buf) 151 return -ENOMEM; 152 153 /* Get the tag */ 154 ret = bcm63xx_read_image_tag(master, "rootfs", cfelen, buf); 155 if (!ret) { 156 STR_NULL_TERMINATE(buf->flash_image_start); 157 if (kstrtouint(buf->flash_image_start, 10, &rootfsaddr) || 158 rootfsaddr < BCM963XX_EXTENDED_SIZE) { 159 pr_err("invalid rootfs address: %*ph\n", 160 (int)sizeof(buf->flash_image_start), 161 buf->flash_image_start); 162 goto invalid_tag; 163 } 164 165 STR_NULL_TERMINATE(buf->kernel_address); 166 if (kstrtouint(buf->kernel_address, 10, &kerneladdr) || 167 kerneladdr < BCM963XX_EXTENDED_SIZE) { 168 pr_err("invalid kernel address: %*ph\n", 169 (int)sizeof(buf->kernel_address), 170 buf->kernel_address); 171 goto invalid_tag; 172 } 173 174 STR_NULL_TERMINATE(buf->kernel_length); 175 if (kstrtouint(buf->kernel_length, 10, &kernellen)) { 176 pr_err("invalid kernel length: %*ph\n", 177 (int)sizeof(buf->kernel_length), 178 buf->kernel_length); 179 goto invalid_tag; 180 } 181 182 STR_NULL_TERMINATE(buf->total_length); 183 if (kstrtouint(buf->total_length, 10, &totallen)) { 184 pr_err("invalid total length: %*ph\n", 185 (int)sizeof(buf->total_length), 186 buf->total_length); 187 goto invalid_tag; 188 } 189 190 kerneladdr = kerneladdr - BCM963XX_EXTENDED_SIZE; 191 rootfsaddr = rootfsaddr - BCM963XX_EXTENDED_SIZE; 192 spareaddr = roundup(totallen, master->erasesize) + cfelen; 193 194 if (rootfsaddr < kerneladdr) { 195 /* default Broadcom layout */ 196 rootfslen = kerneladdr - rootfsaddr; 197 rootfs_first = true; 198 } else { 199 /* OpenWrt layout */ 200 rootfsaddr = kerneladdr + kernellen; 201 rootfslen = spareaddr - rootfsaddr; 202 } 203 } else if (ret > 0) { 204invalid_tag: 205 kernellen = 0; 206 rootfslen = 0; 207 rootfsaddr = 0; 208 spareaddr = cfelen; 209 } else { 210 goto out; 211 } 212 sparelen = master->size - spareaddr - nvramlen; 213 214 /* Determine number of partitions */ 215 if (rootfslen > 0) 216 nrparts++; 217 218 if (kernellen > 0) 219 nrparts++; 220 221 parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL); 222 if (!parts) { 223 ret = -ENOMEM; 224 goto out; 225 } 226 227 /* Start building partition list */ 228 parts[curpart].name = "CFE"; 229 parts[curpart].offset = 0; 230 parts[curpart].size = cfelen; 231 curpart++; 232 233 if (kernellen > 0) { 234 int kernelpart = curpart; 235 236 if (rootfslen > 0 && rootfs_first) 237 kernelpart++; 238 parts[kernelpart].name = "kernel"; 239 parts[kernelpart].offset = kerneladdr; 240 parts[kernelpart].size = kernellen; 241 curpart++; 242 } 243 244 if (rootfslen > 0) { 245 int rootfspart = curpart; 246 247 if (kernellen > 0 && rootfs_first) 248 rootfspart--; 249 parts[rootfspart].name = "rootfs"; 250 parts[rootfspart].offset = rootfsaddr; 251 parts[rootfspart].size = rootfslen; 252 if (sparelen > 0 && !rootfs_first) 253 parts[rootfspart].size += sparelen; 254 curpart++; 255 } 256 257 parts[curpart].name = "nvram"; 258 parts[curpart].offset = master->size - nvramlen; 259 parts[curpart].size = nvramlen; 260 curpart++; 261 262 /* Global partition "linux" to make easy firmware upgrade */ 263 parts[curpart].name = "linux"; 264 parts[curpart].offset = cfelen; 265 parts[curpart].size = master->size - cfelen - nvramlen; 266 267 for (i = 0; i < nrparts; i++) 268 pr_info("Partition %d is %s offset %llx and length %llx\n", i, 269 parts[i].name, parts[i].offset, parts[i].size); 270 271 pr_info("Spare partition is offset %x and length %x\n", spareaddr, 272 sparelen); 273 274 *pparts = parts; 275 ret = 0; 276 277out: 278 vfree(buf); 279 280 if (ret) 281 return ret; 282 283 return nrparts; 284} 285 286static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, 287 const struct mtd_partition **pparts, 288 struct mtd_part_parser_data *data) 289{ 290 struct bcm963xx_nvram *nvram = NULL; 291 int ret; 292 293 if (bcm63xx_detect_cfe(master)) 294 return -EINVAL; 295 296 nvram = vzalloc(sizeof(*nvram)); 297 if (!nvram) 298 return -ENOMEM; 299 300 ret = bcm63xx_read_nvram(master, nvram); 301 if (ret) 302 goto out; 303 304 if (!mtd_type_is_nand(master)) 305 ret = bcm63xx_parse_cfe_nor_partitions(master, pparts, nvram); 306 else 307 ret = -EINVAL; 308 309out: 310 vfree(nvram); 311 return ret; 312}; 313 314static struct mtd_part_parser bcm63xx_cfe_parser = { 315 .parse_fn = bcm63xx_parse_cfe_partitions, 316 .name = "bcm63xxpart", 317}; 318module_mtd_part_parser(bcm63xx_cfe_parser); 319 320MODULE_LICENSE("GPL"); 321MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>"); 322MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); 323MODULE_AUTHOR("Mike Albon <malbon@openwrt.org>"); 324MODULE_AUTHOR("Jonas Gorski <jonas.gorski@gmail.com"); 325MODULE_DESCRIPTION("MTD partitioning for BCM63XX CFE bootloaders");