Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
bcm63xxpart.c
Go to the documentation of this file.
1 /*
2  * BCM63XX CFE image tag parser
3  *
4  * Copyright © 2006-2008 Florian Fainelli <[email protected]>
5  * Mike Albon <[email protected]>
6  * Copyright © 2009-2010 Daniel Dickinson <[email protected]>
7  * Copyright © 2011-2012 Jonas Gorski <[email protected]>
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/crc32.h>
28 #include <linux/module.h>
29 #include <linux/kernel.h>
30 #include <linux/slab.h>
31 #include <linux/vmalloc.h>
32 #include <linux/mtd/mtd.h>
33 #include <linux/mtd/partitions.h>
34 
37 
38 #define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */
39 
40 #define BCM63XX_MIN_CFE_SIZE 0x10000 /* always at least 64KiB */
41 #define BCM63XX_MIN_NVRAM_SIZE 0x10000 /* always at least 64KiB */
42 
43 #define BCM63XX_CFE_MAGIC_OFFSET 0x4e0
44 
45 static int bcm63xx_detect_cfe(struct mtd_info *master)
46 {
47  char buf[9];
48  int ret;
49  size_t retlen;
50 
51  ret = mtd_read(master, BCM963XX_CFE_VERSION_OFFSET, 5, &retlen,
52  (void *)buf);
53  buf[retlen] = 0;
54 
55  if (ret)
56  return ret;
57 
58  if (strncmp("cfe-v", buf, 5) == 0)
59  return 0;
60 
61  /* very old CFE's do not have the cfe-v string, so check for magic */
62  ret = mtd_read(master, BCM63XX_CFE_MAGIC_OFFSET, 8, &retlen,
63  (void *)buf);
64  buf[retlen] = 0;
65 
66  return strncmp("CFE1CFE1", buf, 8);
67 }
68 
69 static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
70  struct mtd_partition **pparts,
71  struct mtd_part_parser_data *data)
72 {
73  /* CFE, NVRAM and global Linux are always present */
74  int nrparts = 3, curpart = 0;
75  struct bcm_tag *buf;
76  struct mtd_partition *parts;
77  int ret;
78  size_t retlen;
79  unsigned int rootfsaddr, kerneladdr, spareaddr;
80  unsigned int rootfslen, kernellen, sparelen, totallen;
81  unsigned int cfelen, nvramlen;
82  int namelen = 0;
83  int i;
84  u32 computed_crc;
85  bool rootfs_first = false;
86 
87  if (bcm63xx_detect_cfe(master))
88  return -EINVAL;
89 
90  cfelen = max_t(uint32_t, master->erasesize, BCM63XX_MIN_CFE_SIZE);
91  nvramlen = max_t(uint32_t, master->erasesize, BCM63XX_MIN_NVRAM_SIZE);
92 
93  /* Allocate memory for buffer */
94  buf = vmalloc(sizeof(struct bcm_tag));
95  if (!buf)
96  return -ENOMEM;
97 
98  /* Get the tag */
99  ret = mtd_read(master, cfelen, sizeof(struct bcm_tag), &retlen,
100  (void *)buf);
101 
102  if (retlen != sizeof(struct bcm_tag)) {
103  vfree(buf);
104  return -EIO;
105  }
106 
107  computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf,
108  offsetof(struct bcm_tag, header_crc));
109  if (computed_crc == buf->header_crc) {
110  char *boardid = &(buf->board_id[0]);
111  char *tagversion = &(buf->tag_version[0]);
112 
113  sscanf(buf->flash_image_start, "%u", &rootfsaddr);
114  sscanf(buf->kernel_address, "%u", &kerneladdr);
115  sscanf(buf->kernel_length, "%u", &kernellen);
116  sscanf(buf->total_length, "%u", &totallen);
117 
118  pr_info("CFE boot tag found with version %s and board type %s\n",
119  tagversion, boardid);
120 
121  kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
122  rootfsaddr = rootfsaddr - BCM63XX_EXTENDED_SIZE;
123  spareaddr = roundup(totallen, master->erasesize) + cfelen;
124  sparelen = master->size - spareaddr - nvramlen;
125 
126  if (rootfsaddr < kerneladdr) {
127  /* default Broadcom layout */
128  rootfslen = kerneladdr - rootfsaddr;
129  rootfs_first = true;
130  } else {
131  /* OpenWrt layout */
132  rootfsaddr = kerneladdr + kernellen;
133  rootfslen = spareaddr - rootfsaddr;
134  }
135  } else {
136  pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n",
137  buf->header_crc, computed_crc);
138  kernellen = 0;
139  rootfslen = 0;
140  rootfsaddr = 0;
141  spareaddr = cfelen;
142  sparelen = master->size - cfelen - nvramlen;
143  }
144 
145  /* Determine number of partitions */
146  namelen = 8;
147  if (rootfslen > 0) {
148  nrparts++;
149  namelen += 6;
150  }
151  if (kernellen > 0) {
152  nrparts++;
153  namelen += 6;
154  }
155 
156  /* Ask kernel for more memory */
157  parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
158  if (!parts) {
159  vfree(buf);
160  return -ENOMEM;
161  }
162 
163  /* Start building partition list */
164  parts[curpart].name = "CFE";
165  parts[curpart].offset = 0;
166  parts[curpart].size = cfelen;
167  curpart++;
168 
169  if (kernellen > 0) {
170  int kernelpart = curpart;
171 
172  if (rootfslen > 0 && rootfs_first)
173  kernelpart++;
174  parts[kernelpart].name = "kernel";
175  parts[kernelpart].offset = kerneladdr;
176  parts[kernelpart].size = kernellen;
177  curpart++;
178  }
179 
180  if (rootfslen > 0) {
181  int rootfspart = curpart;
182 
183  if (kernellen > 0 && rootfs_first)
184  rootfspart--;
185  parts[rootfspart].name = "rootfs";
186  parts[rootfspart].offset = rootfsaddr;
187  parts[rootfspart].size = rootfslen;
188  if (sparelen > 0 && !rootfs_first)
189  parts[rootfspart].size += sparelen;
190  curpart++;
191  }
192 
193  parts[curpart].name = "nvram";
194  parts[curpart].offset = master->size - nvramlen;
195  parts[curpart].size = nvramlen;
196 
197  /* Global partition "linux" to make easy firmware upgrade */
198  curpart++;
199  parts[curpart].name = "linux";
200  parts[curpart].offset = cfelen;
201  parts[curpart].size = master->size - cfelen - nvramlen;
202 
203  for (i = 0; i < nrparts; i++)
204  pr_info("Partition %d is %s offset %lx and length %lx\n", i,
205  parts[i].name, (long unsigned int)(parts[i].offset),
206  (long unsigned int)(parts[i].size));
207 
208  pr_info("Spare partition is offset %x and length %x\n", spareaddr,
209  sparelen);
210 
211  *pparts = parts;
212  vfree(buf);
213 
214  return nrparts;
215 };
216 
217 static struct mtd_part_parser bcm63xx_cfe_parser = {
218  .owner = THIS_MODULE,
219  .parse_fn = bcm63xx_parse_cfe_partitions,
220  .name = "bcm63xxpart",
221 };
222 
223 static int __init bcm63xx_cfe_parser_init(void)
224 {
225  return register_mtd_parser(&bcm63xx_cfe_parser);
226 }
227 
228 static void __exit bcm63xx_cfe_parser_exit(void)
229 {
230  deregister_mtd_parser(&bcm63xx_cfe_parser);
231 }
232 
233 module_init(bcm63xx_cfe_parser_init);
234 module_exit(bcm63xx_cfe_parser_exit);
235 
236 MODULE_LICENSE("GPL");
237 MODULE_AUTHOR("Daniel Dickinson <[email protected]>");
238 MODULE_AUTHOR("Florian Fainelli <[email protected]>");
239 MODULE_AUTHOR("Mike Albon <[email protected]>");
240 MODULE_AUTHOR("Jonas Gorski <[email protected]");
241 MODULE_DESCRIPTION("MTD partitioning for BCM63XX CFE bootloaders");