1/* 2 * Parse MyLoader-style flash partition tables and produce a Linux partition 3 * array to match. 4 * 5 * Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org> 6 * 7 * This file was based on drivers/mtd/redboot.c 8 * Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com> 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License version 2 as published 12 * by the Free Software Foundation. 13 * 14 */ 15 16#include <linux/kernel.h> 17#include <linux/module.h> 18#include <linux/version.h> 19#include <linux/slab.h> 20#include <linux/init.h> 21#include <linux/vmalloc.h> 22#include <linux/mtd/mtd.h> 23#include <linux/mtd/partitions.h> 24#include <linux/byteorder/generic.h> 25#include <linux/myloader.h> 26 27#define BLOCK_LEN_MIN 0x10000 28#define PART_NAME_LEN 32 29 30struct part_data { 31 struct mylo_partition_table tab; 32 char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN]; 33}; 34 35static int myloader_parse_partitions(struct mtd_info *master, 36 struct mtd_partition **pparts, 37 struct mtd_part_parser_data *data) 38{ 39 struct part_data *buf; 40 struct mylo_partition_table *tab; 41 struct mylo_partition *part; 42 struct mtd_partition *mtd_parts; 43 struct mtd_partition *mtd_part; 44 int num_parts; 45 int ret, i; 46 size_t retlen; 47 char *names; 48 unsigned long offset; 49 unsigned long blocklen; 50 51 buf = vmalloc(sizeof(*buf)); 52 if (!buf) { 53 return -ENOMEM; 54 goto out; 55 } 56 tab = &buf->tab; 57 58 blocklen = master->erasesize; 59 if (blocklen < BLOCK_LEN_MIN) 60 blocklen = BLOCK_LEN_MIN; 61 62 offset = blocklen; 63 64 /* Find the partition table */ 65 for (i = 0; i < 4; i++, offset += blocklen) { 66 printk(KERN_DEBUG "%s: searching for MyLoader partition table" 67 " at offset 0x%lx\n", master->name, offset); 68 69 ret = mtd_read(master, offset, sizeof(*buf), &retlen, 70 (void *)buf); 71 if (ret) 72 goto out_free_buf; 73 74 if (retlen != sizeof(*buf)) { 75 ret = -EIO; 76 goto out_free_buf; 77 } 78 79 /* Check for Partition Table magic number */ 80 if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS)) 81 break; 82 83 } 84 85 if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) { 86 printk(KERN_DEBUG "%s: no MyLoader partition table found\n", 87 master->name); 88 ret = 0; 89 goto out_free_buf; 90 } 91 92 /* The MyLoader and the Partition Table is always present */ 93 num_parts = 2; 94 95 /* Detect number of used partitions */ 96 for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { 97 part = &tab->partitions[i]; 98 99 if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) 100 continue; 101 102 num_parts++; 103 } 104 105 mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) + 106 num_parts * PART_NAME_LEN), GFP_KERNEL); 107 108 if (!mtd_parts) { 109 ret = -ENOMEM; 110 goto out_free_buf; 111 } 112 113 mtd_part = mtd_parts; 114 names = (char *)&mtd_parts[num_parts]; 115 116 strncpy(names, "myloader", PART_NAME_LEN); 117 mtd_part->name = names; 118 mtd_part->offset = 0; 119 mtd_part->size = offset; 120 mtd_part->mask_flags = MTD_WRITEABLE; 121 mtd_part++; 122 names += PART_NAME_LEN; 123 124 strncpy(names, "partition_table", PART_NAME_LEN); 125 mtd_part->name = names; 126 mtd_part->offset = offset; 127 mtd_part->size = blocklen; 128 mtd_part->mask_flags = MTD_WRITEABLE; 129 mtd_part++; 130 names += PART_NAME_LEN; 131 132 for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { 133 part = &tab->partitions[i]; 134 135 if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) 136 continue; 137 138 if ((buf->names[i][0]) && (buf->names[i][0] != '\xff')) 139 strncpy(names, buf->names[i], PART_NAME_LEN); 140 else 141 snprintf(names, PART_NAME_LEN, "partition%d", i); 142 143 mtd_part->offset = le32_to_cpu(part->addr); 144 mtd_part->size = le32_to_cpu(part->size); 145 mtd_part->name = names; 146 mtd_part++; 147 names += PART_NAME_LEN; 148 } 149 150 *pparts = mtd_parts; 151 ret = num_parts; 152 153 out_free_buf: 154 vfree(buf); 155 out: 156 return ret; 157} 158 159static struct mtd_part_parser myloader_mtd_parser = { 160 .owner = THIS_MODULE, 161 .parse_fn = myloader_parse_partitions, 162 .name = "MyLoader", 163}; 164 165static int __init myloader_mtd_parser_init(void) 166{ 167 register_mtd_parser(&myloader_mtd_parser); 168 169 return 0; 170} 171 172static void __exit myloader_mtd_parser_exit(void) 173{ 174 deregister_mtd_parser(&myloader_mtd_parser); 175} 176 177module_init(myloader_mtd_parser_init); 178module_exit(myloader_mtd_parser_exit); 179 180MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); 181MODULE_DESCRIPTION("Parsing code for MyLoader partition tables"); 182MODULE_LICENSE("GPL v2"); 183