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