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: stable/11/sys/geom/geom_map.c 332640 2018-04-17 02:18:04Z kevans $"); 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#ifdef __LP64__ 151 ret = sscanf(line, "search:%li:%li:%63c", 152 &search_start, &search_step, search_key); 153#else 154 ret = sscanf(line, "search:%qi:%qi:%63c", 155 &search_start, &search_step, search_key); 156#endif 157 if (ret < 3) 158 return (1); 159 160 if (bootverbose) { 161 printf("MAP: search %s for key \"%s\" from 0x%jx, step 0x%jx\n", 162 cp->geom->name, search_key, (intmax_t)search_start, (intmax_t)search_step); 163 } 164 165 /* error if search_key is empty */ 166 if (strlen(search_key) < 1) 167 return (1); 168 169 /* sscanf successful, and we start marker search */ 170 for (search_offset = search_start; 171 search_offset < cp->provider->mediasize; 172 search_offset += search_step) { 173 174 g_topology_unlock(); 175 buf = g_read_data(cp, rounddown(search_offset, sectorsize), 176 roundup(strlen(search_key), sectorsize), NULL); 177 g_topology_lock(); 178 179 /* 180 * Don't bother doing the rest if buf==NULL; eg derefencing 181 * to assemble 'key'. 182 */ 183 if (buf == NULL) 184 continue; 185 186 /* Wildcard, replace '.' with byte from data */ 187 /* TODO: add support wildcard escape '\.' */ 188 189 strncpy(key, search_key, MAP_MAX_MARKER_LEN); 190 191 for (c = 0; c < MAP_MAX_MARKER_LEN && key[c]; c++) { 192 if (key[c] == '.') { 193 key[c] = ((char *)(buf + 194 (search_offset % sectorsize)))[c]; 195 } 196 } 197 198 /* Assume buf != NULL here */ 199 if (memcmp(buf + search_offset % sectorsize, 200 key, strlen(search_key)) == 0) { 201 g_free(buf); 202 /* Marker found, so return their offset */ 203 *offset = search_offset; 204 return (0); 205 } 206 g_free(buf); 207 } 208 209 /* Marker not found */ 210 return (1); 211} 212 213static int 214g_map_parse_part(struct g_class *mp, struct g_provider *pp, 215 struct g_consumer *cp, struct g_geom *gp, struct g_map_softc *sc, int i) 216{ 217 const char *value, *name; 218 char *op; 219 off_t start, end, offset, size, dsize; 220 int readonly, ret; 221 222 /* hint.map.0.at="cfid0" - bind to cfid0 media */ 223 if (resource_string_value("map", i, "at", &value) != 0) 224 return (1); 225 226 /* Check if this correct provider */ 227 if (strcmp(pp->name, value) != 0) 228 return (1); 229 230 /* 231 * hint.map.0.name="uboot" - name of partition, will be available 232 * as "/dev/map/uboot" 233 */ 234 if (resource_string_value("map", i, "name", &name) != 0) { 235 if (bootverbose) 236 printf("MAP: hint.map.%d has no name\n", i); 237 return (1); 238 } 239 240 /* 241 * hint.map.0.start="0x00010000" - partition start at 0x00010000 242 * or hint.map.0.start="search:0x00010000:0x200:marker text" - 243 * search for text "marker text", begin at 0x10000, step 0x200 244 * until we found marker or end of media reached 245 */ 246 if (resource_string_value("map", i, "start", &value) != 0) { 247 if (bootverbose) 248 printf("MAP: \"%s\" has no start value\n", name); 249 return (1); 250 } 251 if (find_marker(cp, value, &start) != 0) { 252 if (bootverbose) { 253 printf("MAP: \"%s\" can't parse/use start value\n", 254 name); 255 } 256 return (1); 257 } 258 259 /* like "start" */ 260 if (resource_string_value("map", i, "end", &value) != 0) { 261 if (bootverbose) 262 printf("MAP: \"%s\" has no end value\n", name); 263 return (1); 264 } 265 if (find_marker(cp, value, &end) != 0) { 266 if (bootverbose) { 267 printf("MAP: \"%s\" can't parse/use end value\n", 268 name); 269 } 270 return (1); 271 } 272 273 /* variable readonly optional, disable write access */ 274 if (resource_int_value("map", i, "readonly", &readonly) != 0) 275 readonly = 0; 276 277 /* offset of partition data, from partition begin */ 278 if (resource_string_value("map", i, "offset", &value) == 0) { 279 offset = strtouq(value, &op, 0); 280 if (*op != '\0') { 281 if (bootverbose) { 282 printf("MAP: \"%s\" can't parse offset\n", 283 name); 284 } 285 return (1); 286 } 287 } else { 288 offset = 0; 289 } 290 291 /* partition data size */ 292 if (resource_string_value("map", i, "dsize", &value) == 0) { 293 dsize = strtouq(value, &op, 0); 294 if (*op != '\0') { 295 if (bootverbose) { 296 printf("MAP: \"%s\" can't parse dsize\n", 297 name); 298 } 299 return (1); 300 } 301 } else { 302 dsize = 0; 303 } 304 305 size = end - start; 306 if (dsize == 0) 307 dsize = size - offset; 308 309 /* end is 0 or size is 0, No MAP - so next */ 310 if (end < start) { 311 if (bootverbose) { 312 printf("MAP: \"%s\", \"end\" less than " 313 "\"start\"\n", name); 314 } 315 return (1); 316 } 317 318 if (offset + dsize > size) { 319 if (bootverbose) { 320 printf("MAP: \"%s\", \"dsize\" bigger than " 321 "partition - offset\n", name); 322 } 323 return (1); 324 } 325 326 ret = g_slice_config(gp, i, G_SLICE_CONFIG_SET, start + offset, 327 dsize, cp->provider->sectorsize, "map/%s", name); 328 if (ret != 0) { 329 if (bootverbose) { 330 printf("MAP: g_slice_config returns %d for \"%s\"\n", 331 ret, name); 332 } 333 return (1); 334 } 335 336 if (bootverbose) { 337 printf("MAP: %s: %jxx%jx, data=%jxx%jx " 338 "\"/dev/map/%s\"\n", 339 cp->geom->name, (intmax_t)start, (intmax_t)size, (intmax_t)offset, 340 (intmax_t)dsize, name); 341 } 342 343 sc->offset[i] = start; 344 sc->size[i] = size; 345 sc->entry[i] = offset; 346 sc->dsize[i] = dsize; 347 sc->readonly[i] = readonly ? 1 : 0; 348 349 return (0); 350} 351 352static struct g_geom * 353g_map_taste(struct g_class *mp, struct g_provider *pp, int insist __unused) 354{ 355 struct g_map_softc *sc; 356 struct g_consumer *cp; 357 struct g_geom *gp; 358 int i; 359 360 g_trace(G_T_TOPOLOGY, "map_taste(%s,%s)", mp->name, pp->name); 361 g_topology_assert(); 362 if (strcmp(pp->geom->class->name, MAP_CLASS_NAME) == 0) 363 return (NULL); 364 365 gp = g_slice_new(mp, MAP_MAXSLICE, pp, &cp, &sc, sizeof(*sc), 366 g_map_start); 367 if (gp == NULL) 368 return (NULL); 369 370 /* interpose our access method */ 371 sc->parent_access = gp->access; 372 gp->access = g_map_access; 373 374 for (i = 0; i < MAP_MAXSLICE; i++) 375 g_map_parse_part(mp, pp, cp, gp, sc, i); 376 377 378 g_access(cp, -1, 0, 0); 379 if (LIST_EMPTY(&gp->provider)) { 380 if (bootverbose) 381 printf("MAP: No valid partition found at %s\n", pp->name); 382 g_slice_spoiled(cp); 383 return (NULL); 384 } 385 return (gp); 386} 387 388static void 389g_map_config(struct gctl_req *req, struct g_class *mp, const char *verb) 390{ 391 struct g_geom *gp; 392 393 g_topology_assert(); 394 gp = gctl_get_geom(req, mp, "geom"); 395 if (gp == NULL) 396 return; 397 gctl_error(req, "Unknown verb"); 398} 399 400static struct g_class g_map_class = { 401 .name = MAP_CLASS_NAME, 402 .version = G_VERSION, 403 .taste = g_map_taste, 404 .dumpconf = g_map_dumpconf, 405 .ctlreq = g_map_config, 406}; 407DECLARE_GEOM_CLASS(g_map_class, g_map); 408MODULE_VERSION(geom_map, 0); 409