1/*- 2 * Copyright (c) 2010-2011 Aleksandr Rybalko <ray@dlink.ua> 3 * based on geom_redboot.c 4 * Copyright (c) 2009 Sam Leffler, Errno Consulting 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 * without modification. 13 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 14 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 15 * redistribution must be conditioned upon including a substantially 16 * similar Disclaimer requirement for further binary redistribution. 17 * 18 * NO WARRANTY 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 22 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 23 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 24 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 27 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 * THE POSSIBILITY OF SUCH DAMAGES. 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD$"); 34 35#include <sys/param.h> 36#include <sys/bus.h> 37#include <sys/errno.h> 38#include <sys/endian.h> 39#include <sys/systm.h> 40#include <sys/kernel.h> 41#include <sys/fcntl.h> 42#include <sys/malloc.h> 43#include <sys/bio.h> 44#include <sys/lock.h> 45#include <sys/mutex.h> 46#include <sys/sbuf.h> 47 48#include <geom/geom.h> 49#include <geom/geom_slice.h> 50 51#define MAP_CLASS_NAME "MAP" 52#define MAP_MAXSLICE 64 53#define MAP_MAX_MARKER_LEN 64 54 55struct g_map_softc { 56 off_t offset[MAP_MAXSLICE]; /* offset in flash */ 57 off_t size[MAP_MAXSLICE]; /* image size in bytes */ 58 off_t entry[MAP_MAXSLICE]; 59 off_t dsize[MAP_MAXSLICE]; 60 uint8_t readonly[MAP_MAXSLICE]; 61 g_access_t *parent_access; 62}; 63 64static int 65g_map_access(struct g_provider *pp, int dread, int dwrite, int dexcl) 66{ 67 struct g_geom *gp; 68 struct g_slicer *gsp; 69 struct g_map_softc *sc; 70 71 gp = pp->geom; 72 gsp = gp->softc; 73 sc = gsp->softc; 74 75 if (dwrite > 0 && sc->readonly[pp->index]) 76 return (EPERM); 77 78 return (sc->parent_access(pp, dread, dwrite, dexcl)); 79} 80 81static int 82g_map_start(struct bio *bp) 83{ 84 struct g_provider *pp; 85 struct g_geom *gp; 86 struct g_map_softc *sc; 87 struct g_slicer *gsp; 88 int idx; 89 90 pp = bp->bio_to; 91 idx = pp->index; 92 gp = pp->geom; 93 gsp = gp->softc; 94 sc = gsp->softc; 95 96 if (bp->bio_cmd == BIO_GETATTR) { 97 if (g_handleattr_int(bp, MAP_CLASS_NAME "::entry", 98 sc->entry[idx])) { 99 return (1); 100 } 101 if (g_handleattr_int(bp, MAP_CLASS_NAME "::dsize", 102 sc->dsize[idx])) { 103 return (1); 104 } 105 } 106 107 return (0); 108} 109 110static void 111g_map_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 112 struct g_consumer *cp __unused, struct g_provider *pp) 113{ 114 struct g_map_softc *sc; 115 struct g_slicer *gsp; 116 117 gsp = gp->softc; 118 sc = gsp->softc; 119 g_slice_dumpconf(sb, indent, gp, cp, pp); 120 if (pp != NULL) { 121 if (indent == NULL) { 122 sbuf_printf(sb, " entry %jd", (intmax_t)sc->entry[pp->index]); 123 sbuf_printf(sb, " dsize %jd", (intmax_t)sc->dsize[pp->index]); 124 } else { 125 sbuf_printf(sb, "%s<entry>%jd</entry>\n", indent, 126 (intmax_t)sc->entry[pp->index]); 127 sbuf_printf(sb, "%s<dsize>%jd</dsize>\n", indent, 128 (intmax_t)sc->dsize[pp->index]); 129 } 130 } 131} 132 133static int 134find_marker(struct g_consumer *cp, const char *line, off_t *offset) 135{ 136 off_t search_start, search_offset, search_step; 137 size_t sectorsize; 138 uint8_t *buf; 139 char *op, key[MAP_MAX_MARKER_LEN], search_key[MAP_MAX_MARKER_LEN]; 140 int ret, c; 141 142 /* Try convert to numeric first */ 143 *offset = strtouq(line, &op, 0); 144 if (*op == '\0') 145 return (0); 146 147 bzero(search_key, MAP_MAX_MARKER_LEN); 148 sectorsize = cp->provider->sectorsize; 149 150 ret = sscanf(line, "search:%qi:%qi:%63c", 151 &search_start, &search_step, search_key); 152 if (ret < 3) 153 return (1); 154 155 if (bootverbose) { 156 printf("MAP: search key \"%s\" from 0x%jx, step 0x%jx\n", 157 search_key, (intmax_t)search_start, (intmax_t)search_step); 158 } 159 160 /* error if search_key is empty */ 161 if (strlen(search_key) < 1) 162 return (1); 163 164 /* sscanf successful, and we start marker search */ 165 for (search_offset = search_start; 166 search_offset < cp->provider->mediasize; 167 search_offset += search_step) { 168 169 g_topology_unlock(); 170 buf = g_read_data(cp, rounddown(search_offset, sectorsize), 171 roundup(strlen(search_key), sectorsize), NULL); 172 g_topology_lock(); 173 174 /* Wildcard, replace '.' with byte from data */ 175 /* TODO: add support wildcard escape '\.' */ 176 177 strncpy(key, search_key, MAP_MAX_MARKER_LEN); 178 179 for (c = 0; c < MAP_MAX_MARKER_LEN && key[c]; c++) { 180 if (key[c] == '.') { 181 key[c] = ((char *)(buf + 182 (search_offset % sectorsize)))[c]; 183 } 184 } 185 186 if (buf != NULL && strncmp(buf + search_offset % sectorsize, 187 key, strlen(search_key)) == 0) { 188 g_free(buf); 189 /* Marker found, so return their offset */ 190 *offset = search_offset; 191 return (0); 192 } 193 g_free(buf); 194 } 195 196 /* Marker not found */ 197 return (1); 198} 199 200static int 201g_map_parse_part(struct g_class *mp, struct g_provider *pp, 202 struct g_consumer *cp, struct g_geom *gp, struct g_map_softc *sc, int i) 203{ 204 const char *value, *name; 205 char *op; 206 off_t start, end, offset, size, dsize; 207 int readonly, ret; 208 209 /* hint.map.0.at="cfid0" - bind to cfid0 media */ 210 if (resource_string_value("map", i, "at", &value) != 0) 211 return (1); 212 213 /* Check if this correct provider */ 214 if (strcmp(pp->name, value) != 0) 215 return (1); 216 217 /* 218 * hint.map.0.name="uboot" - name of partition, will be available 219 * as "/dev/map/uboot" 220 */ 221 if (resource_string_value("map", i, "name", &name) != 0) { 222 if (bootverbose) 223 printf("MAP: hint.map.%d has no name\n", i); 224 return (1); 225 } 226 227 /* 228 * hint.map.0.start="0x00010000" - partition start at 0x00010000 229 * or hint.map.0.start="search:0x00010000:0x200:marker text" - 230 * search for text "marker text", begin at 0x10000, step 0x200 231 * until we found marker or end of media reached 232 */ 233 if (resource_string_value("map", i, "start", &value) != 0) { 234 if (bootverbose) 235 printf("MAP: \"%s\" has no start value\n", name); 236 return (1); 237 } 238 if (find_marker(cp, value, &start) != 0) { 239 if (bootverbose) { 240 printf("MAP: \"%s\" can't parse/use start value\n", 241 name); 242 } 243 return (1); 244 } 245 246 /* like "start" */ 247 if (resource_string_value("map", i, "end", &value) != 0) { 248 if (bootverbose) 249 printf("MAP: \"%s\" has no end value\n", name); 250 return (1); 251 } 252 if (find_marker(cp, value, &end) != 0) { 253 if (bootverbose) { 254 printf("MAP: \"%s\" can't parse/use start value\n", 255 name); 256 } 257 return (1); 258 } 259 260 /* variable readonly optional, disable write access */ 261 if (resource_int_value("map", i, "readonly", &readonly) != 0) 262 readonly = 0; 263 264 /* offset of partition data, from partition begin */ 265 if (resource_string_value("map", i, "offset", &value) == 0) { 266 offset = strtouq(value, &op, 0); 267 if (*op != '\0') { 268 if (bootverbose) { 269 printf("MAP: \"%s\" can't parse offset\n", 270 name); 271 } 272 return (1); 273 } 274 } else { 275 offset = 0; 276 } 277 278 /* partition data size */ 279 if (resource_string_value("map", i, "dsize", &value) == 0) { 280 dsize = strtouq(value, &op, 0); 281 if (*op != '\0') { 282 if (bootverbose) { 283 printf("MAP: \"%s\" can't parse dsize\n", 284 name); 285 } 286 return (1); 287 } 288 } else { 289 dsize = 0; 290 } 291 292 size = end - start; 293 if (dsize == 0) 294 dsize = size - offset; 295 296 /* end is 0 or size is 0, No MAP - so next */ 297 if (end < start) { 298 if (bootverbose) { 299 printf("MAP: \"%s\", \"end\" less than " 300 "\"start\"\n", name); 301 } 302 return (1); 303 } 304 305 if (offset + dsize > size) { 306 if (bootverbose) { 307 printf("MAP: \"%s\", \"dsize\" bigger than " 308 "partition - offset\n", name); 309 } 310 return (1); 311 } 312 313 ret = g_slice_config(gp, i, G_SLICE_CONFIG_SET, start + offset, 314 dsize, cp->provider->sectorsize, "map/%s", name); 315 if (ret != 0) { 316 if (bootverbose) { 317 printf("MAP: g_slice_config returns %d for \"%s\"\n", 318 ret, name); 319 } 320 return (1); 321 } 322 323 if (bootverbose) { 324 printf("MAP: %jxx%jx, data=%jxx%jx " 325 "\"/dev/map/%s\"\n", 326 (intmax_t)start, (intmax_t)size, (intmax_t)offset, 327 (intmax_t)dsize, name); 328 } 329 330 sc->offset[i] = start; 331 sc->size[i] = size; 332 sc->entry[i] = offset; 333 sc->dsize[i] = dsize; 334 sc->readonly[i] = readonly ? 1 : 0; 335 336 return (0); 337} 338 339static struct g_geom * 340g_map_taste(struct g_class *mp, struct g_provider *pp, int insist __unused) 341{ 342 struct g_map_softc *sc; 343 struct g_consumer *cp; 344 struct g_geom *gp; 345 int i; 346 347 g_trace(G_T_TOPOLOGY, "map_taste(%s,%s)", mp->name, pp->name); 348 g_topology_assert(); 349 if (strcmp(pp->geom->class->name, MAP_CLASS_NAME) == 0) 350 return (NULL); 351 352 gp = g_slice_new(mp, MAP_MAXSLICE, pp, &cp, &sc, sizeof(*sc), 353 g_map_start); 354 if (gp == NULL) 355 return (NULL); 356 357 /* interpose our access method */ 358 sc->parent_access = gp->access; 359 gp->access = g_map_access; 360 361 for (i = 0; i < MAP_MAXSLICE; i++) 362 g_map_parse_part(mp, pp, cp, gp, sc, i); 363 364 365 g_access(cp, -1, 0, 0); 366 if (LIST_EMPTY(&gp->provider)) { 367 if (bootverbose) 368 printf("MAP: No valid partition found at %s\n", pp->name); 369 g_slice_spoiled(cp); 370 return (NULL); 371 } 372 return (gp); 373} 374 375static void 376g_map_config(struct gctl_req *req, struct g_class *mp, const char *verb) 377{ 378 struct g_geom *gp; 379 380 g_topology_assert(); 381 gp = gctl_get_geom(req, mp, "geom"); 382 if (gp == NULL) 383 return; 384 gctl_error(req, "Unknown verb"); 385} 386 387static struct g_class g_map_class = { 388 .name = MAP_CLASS_NAME, 389 .version = G_VERSION, 390 .taste = g_map_taste, 391 .dumpconf = g_map_dumpconf, 392 .ctlreq = g_map_config, 393}; 394DECLARE_GEOM_CLASS(g_map_class, g_map); 395