1 /* $NetBSD: flash_mtdparts.c,v 1.2 2021/04/24 23:36:53 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: flash_mtdparts.c,v 1.2 2021/04/24 23:36:53 thorpej Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/device.h> 35 #include <sys/systm.h> 36 #include <sys/kmem.h> 37 38 #include <dev/flash/flash.h> 39 40 extern int flash_print(void *, const char *); 41 42 static void 43 flash_attach_partition(struct flash_interface *flash_if, device_t parent, 44 struct flash_partition *part) 45 { 46 struct flash_attach_args faa; 47 48 faa.flash_if = flash_if; 49 faa.partinfo = *part; 50 51 config_found(parent, &faa, flash_print, 52 CFARG_IATTR, "flashbus", 53 CFARG_EOL); 54 } 55 56 static flash_size_t 57 flash_parse_size(char *partdef, char **ep) 58 { 59 flash_size_t size; 60 61 /* Parse the size parameter */ 62 size = strtoul(partdef, ep, 10); 63 if (partdef == *ep) 64 return 0; 65 66 switch (**ep) { 67 case 'G': 68 case 'g': 69 size <<= 10; 70 /* FALLTHROUGH */ 71 case 'M': 72 case 'm': 73 size <<= 10; 74 /* FALLTHROUGH */ 75 case 'K': 76 case 'k': 77 size <<= 10; 78 (*ep)++; 79 break; 80 } 81 82 return size; 83 } 84 85 static int 86 flash_parse_partdef(flash_size_t flash_size, char *partdef, flash_off_t *offset, 87 struct flash_partition *ppart) 88 { 89 struct flash_partition part; 90 91 /* Get the partition size */ 92 if (*partdef == '-') { 93 /* Use the remaining space */ 94 part.part_size = 0; 95 partdef++; 96 } else { 97 part.part_size = flash_parse_size(partdef, &partdef); 98 if (part.part_size == 0) 99 return EINVAL; 100 } 101 102 if (*partdef == '@') { 103 /* Explicit offset */ 104 partdef++; 105 part.part_offset = flash_parse_size(partdef, &partdef); 106 } else { 107 /* Offset is the end of the previous partition */ 108 part.part_offset = *offset; 109 } 110 111 /* Calculate partition size for "all remaining space" parts */ 112 if (part.part_size == 0) 113 part.part_size = flash_size - part.part_offset; 114 115 if (*partdef == '(') { 116 /* Partition name */ 117 partdef++; 118 part.part_name = partdef; 119 partdef = strchr(partdef, ')'); 120 if (partdef == NULL) 121 return EINVAL; 122 *partdef = '\0'; 123 partdef++; 124 } 125 126 part.part_flags = 0; 127 if (strncmp(partdef, "ro", 2) == 0) { 128 part.part_flags |= FLASH_PART_READONLY; 129 partdef += 2; 130 } 131 132 *ppart = part; 133 134 *offset = part.part_offset + part.part_size; 135 136 return 0; 137 } 138 139 static void 140 flash_parse_mtddef(struct flash_interface *flash_if, device_t parent, 141 flash_size_t flash_size, char *mtddef) 142 { 143 struct flash_partition part; 144 char *partdef = mtddef, *nextdef; 145 flash_off_t offset = 0; 146 int error; 147 148 while (partdef && offset < flash_size) { 149 /* Find the end */ 150 nextdef = strchr(partdef, ','); 151 if (nextdef == NULL) 152 nextdef = strchr(partdef, ' '); 153 if (nextdef) 154 *nextdef++ = '\0'; 155 156 error = flash_parse_partdef(flash_size, partdef, &offset, 157 &part); 158 if (error) { 159 aprint_error_dev(parent, "bad partition def '%s'\n", 160 partdef); 161 return; 162 } 163 164 flash_attach_partition(flash_if, parent, &part); 165 166 partdef = nextdef; 167 } 168 } 169 170 /* 171 * Attach partitions to a given parent device node that match the supplied 172 * device id. The cmdline follows the following format: 173 * 174 * mtdparts=<mtddef>[;<mtddef] 175 * <mtddef> := <mtd-id>:<partdef>[,<partdef>] 176 * <partdef> := <size>[@offset][<name>][ro] 177 * <mtd-id> := unique id used in mapping driver/device (number of flash bank) 178 * <size> := memsize OR "-" to denote all remaining space 179 * <name> := '(' NAME ')' 180 */ 181 void 182 flash_attach_mtdparts(struct flash_interface *flash_if, device_t parent, 183 flash_size_t flash_size, const char *mtd_id, const char *cmdline) 184 { 185 char *mtddef; 186 size_t mtddeflen; 187 188 /* Find the definition for our mtd id */ 189 const char *s = strstr(cmdline, mtd_id); 190 if (s == NULL || s[strlen(mtd_id)] != ':') 191 return; 192 193 mtddef = kmem_strdupsize(s + strlen(mtd_id) + 1, &mtddeflen, KM_SLEEP); 194 195 flash_parse_mtddef(flash_if, parent, flash_size, mtddef); 196 197 kmem_free(mtddef, mtddeflen); 198 } 199