1220559Sadrian/*- 2220559Sadrian * Copyright (c) 2010-2011 Aleksandr Rybalko <ray@dlink.ua> 3220559Sadrian * based on geom_redboot.c 4220559Sadrian * Copyright (c) 2009 Sam Leffler, Errno Consulting 5220559Sadrian * All rights reserved. 6220559Sadrian * 7220559Sadrian * Redistribution and use in source and binary forms, with or without 8220559Sadrian * modification, are permitted provided that the following conditions 9220559Sadrian * are met: 10220559Sadrian * 1. Redistributions of source code must retain the above copyright 11220559Sadrian * notice, this list of conditions and the following disclaimer, 12220559Sadrian * without modification. 13220559Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer 14220559Sadrian * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 15220559Sadrian * redistribution must be conditioned upon including a substantially 16220559Sadrian * similar Disclaimer requirement for further binary redistribution. 17220559Sadrian * 18220559Sadrian * NO WARRANTY 19220559Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20220559Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21220559Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 22220559Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 23220559Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 24220559Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25220559Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26220559Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 27220559Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28220559Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29220559Sadrian * THE POSSIBILITY OF SUCH DAMAGES. 30220559Sadrian */ 31220559Sadrian 32220559Sadrian#include <sys/cdefs.h> 33220559Sadrian__FBSDID("$FreeBSD$"); 34220559Sadrian 35220559Sadrian#include <sys/param.h> 36220559Sadrian#include <sys/bus.h> 37220559Sadrian#include <sys/errno.h> 38220559Sadrian#include <sys/endian.h> 39220559Sadrian#include <sys/systm.h> 40220559Sadrian#include <sys/kernel.h> 41220559Sadrian#include <sys/fcntl.h> 42220559Sadrian#include <sys/malloc.h> 43220559Sadrian#include <sys/bio.h> 44220559Sadrian#include <sys/lock.h> 45220559Sadrian#include <sys/mutex.h> 46221500Sadrian#include <sys/sbuf.h> 47220559Sadrian 48220559Sadrian#include <geom/geom.h> 49220559Sadrian#include <geom/geom_slice.h> 50220559Sadrian 51221500Sadrian#define MAP_CLASS_NAME "MAP" 52220559Sadrian#define MAP_MAXSLICE 64 53221500Sadrian#define MAP_MAX_MARKER_LEN 64 54220559Sadrian 55220559Sadrianstruct g_map_softc { 56221500Sadrian off_t offset[MAP_MAXSLICE]; /* offset in flash */ 57221500Sadrian off_t size[MAP_MAXSLICE]; /* image size in bytes */ 58221500Sadrian off_t entry[MAP_MAXSLICE]; 59221500Sadrian off_t dsize[MAP_MAXSLICE]; 60221500Sadrian uint8_t readonly[MAP_MAXSLICE]; 61221500Sadrian g_access_t *parent_access; 62220559Sadrian}; 63220559Sadrian 64220559Sadrianstatic int 65220559Sadriang_map_access(struct g_provider *pp, int dread, int dwrite, int dexcl) 66220559Sadrian{ 67221500Sadrian struct g_geom *gp; 68221500Sadrian struct g_slicer *gsp; 69221500Sadrian struct g_map_softc *sc; 70220559Sadrian 71221500Sadrian gp = pp->geom; 72221500Sadrian gsp = gp->softc; 73221500Sadrian sc = gsp->softc; 74221500Sadrian 75220559Sadrian if (dwrite > 0 && sc->readonly[pp->index]) 76220559Sadrian return (EPERM); 77221500Sadrian 78220559Sadrian return (sc->parent_access(pp, dread, dwrite, dexcl)); 79220559Sadrian} 80220559Sadrian 81220559Sadrianstatic int 82220559Sadriang_map_start(struct bio *bp) 83220559Sadrian{ 84220559Sadrian struct g_provider *pp; 85221500Sadrian struct g_geom *gp; 86220559Sadrian struct g_map_softc *sc; 87220559Sadrian struct g_slicer *gsp; 88221500Sadrian int idx; 89220559Sadrian 90220559Sadrian pp = bp->bio_to; 91220559Sadrian idx = pp->index; 92220559Sadrian gp = pp->geom; 93220559Sadrian gsp = gp->softc; 94220559Sadrian sc = gsp->softc; 95221500Sadrian 96220559Sadrian if (bp->bio_cmd == BIO_GETATTR) { 97220559Sadrian if (g_handleattr_int(bp, MAP_CLASS_NAME "::entry", 98221500Sadrian sc->entry[idx])) { 99220559Sadrian return (1); 100221500Sadrian } 101220559Sadrian if (g_handleattr_int(bp, MAP_CLASS_NAME "::dsize", 102221500Sadrian sc->dsize[idx])) { 103220559Sadrian return (1); 104221500Sadrian } 105220559Sadrian } 106221500Sadrian 107220559Sadrian return (0); 108220559Sadrian} 109220559Sadrian 110220559Sadrianstatic void 111220559Sadriang_map_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 112221500Sadrian struct g_consumer *cp __unused, struct g_provider *pp) 113220559Sadrian{ 114220559Sadrian struct g_map_softc *sc; 115220559Sadrian struct g_slicer *gsp; 116220559Sadrian 117220559Sadrian gsp = gp->softc; 118220559Sadrian sc = gsp->softc; 119220559Sadrian g_slice_dumpconf(sb, indent, gp, cp, pp); 120220559Sadrian if (pp != NULL) { 121220559Sadrian if (indent == NULL) { 122221500Sadrian sbuf_printf(sb, " entry %lld", sc->entry[pp->index]); 123221500Sadrian sbuf_printf(sb, " dsize %lld", sc->dsize[pp->index]); 124220559Sadrian } else { 125221500Sadrian sbuf_printf(sb, "%s<entry>%lld</entry>\n", indent, 126221500Sadrian sc->entry[pp->index]); 127221500Sadrian sbuf_printf(sb, "%s<dsize>%lld</dsize>\n", indent, 128221500Sadrian sc->dsize[pp->index]); 129220559Sadrian } 130220559Sadrian } 131220559Sadrian} 132220559Sadrian 133221500Sadrianstatic int 134221500Sadrianfind_marker(struct g_consumer *cp, const char *line, off_t *offset) 135220559Sadrian{ 136221500Sadrian off_t search_start, search_offset, search_step; 137221500Sadrian size_t sectorsize; 138221500Sadrian uint8_t *buf; 139221500Sadrian char *op, key[MAP_MAX_MARKER_LEN], search_key[MAP_MAX_MARKER_LEN]; 140221500Sadrian int ret, c; 141220559Sadrian 142221500Sadrian /* Try convert to numeric first */ 143221500Sadrian *offset = strtouq(line, &op, 0); 144221500Sadrian if (*op == '\0') 145221500Sadrian return (0); 146220559Sadrian 147221500Sadrian bzero(search_key, MAP_MAX_MARKER_LEN); 148221500Sadrian sectorsize = cp->provider->sectorsize; 149220559Sadrian 150221500Sadrian ret = sscanf(line, "search:%qi:%qi:%63c", 151221500Sadrian &search_start, &search_step, search_key); 152221500Sadrian if (ret < 3) 153221500Sadrian return (1); 154220559Sadrian 155221500Sadrian if (bootverbose) { 156221500Sadrian printf("MAP: search key \"%s\" from 0x%llx, step 0x%llx\n", 157221500Sadrian search_key, search_start, search_step); 158221500Sadrian } 159220559Sadrian 160221500Sadrian /* error if search_key is empty */ 161221500Sadrian if (strlen(search_key) < 1) 162221500Sadrian return (1); 163220559Sadrian 164221500Sadrian /* sscanf successful, and we start marker search */ 165221500Sadrian for (search_offset = search_start; 166221500Sadrian search_offset < cp->provider->mediasize; 167221500Sadrian search_offset += search_step) { 168220559Sadrian 169221500Sadrian g_topology_unlock(); 170221500Sadrian buf = g_read_data(cp, rounddown(search_offset, sectorsize), 171221500Sadrian roundup(strlen(search_key), sectorsize), NULL); 172221500Sadrian g_topology_lock(); 173220559Sadrian 174221500Sadrian /* Wildcard, replace '.' with byte from data */ 175221500Sadrian /* TODO: add support wildcard escape '\.' */ 176220559Sadrian 177221500Sadrian strncpy(key, search_key, MAP_MAX_MARKER_LEN); 178220559Sadrian 179221500Sadrian for (c = 0; c < MAP_MAX_MARKER_LEN && key[c]; c++) { 180221500Sadrian if (key[c] == '.') { 181221500Sadrian key[c] = ((char *)(buf + 182221500Sadrian (search_offset % sectorsize)))[c]; 183221500Sadrian } 184221500Sadrian } 185220559Sadrian 186221500Sadrian if (buf != NULL && strncmp(buf + search_offset % sectorsize, 187221500Sadrian key, strlen(search_key)) == 0) { 188221500Sadrian g_free(buf); 189221500Sadrian /* Marker found, so return their offset */ 190221500Sadrian *offset = search_offset; 191221500Sadrian return (0); 192221500Sadrian } 193221500Sadrian g_free(buf); 194221500Sadrian } 195220559Sadrian 196221500Sadrian /* Marker not found */ 197221500Sadrian return (1); 198221500Sadrian} 199220559Sadrian 200221500Sadrianstatic int 201221500Sadriang_map_parse_part(struct g_class *mp, struct g_provider *pp, 202221500Sadrian struct g_consumer *cp, struct g_geom *gp, struct g_map_softc *sc, int i) 203221500Sadrian{ 204221500Sadrian const char *value, *name; 205221500Sadrian char *op; 206221500Sadrian off_t start, end, offset, size, dsize; 207221500Sadrian int readonly, ret; 208220559Sadrian 209221500Sadrian /* hint.map.0.at="cfid0" - bind to cfid0 media */ 210221500Sadrian if (resource_string_value("map", i, "at", &value) != 0) 211221500Sadrian return (1); 212220559Sadrian 213221500Sadrian /* Check if this correct provider */ 214221500Sadrian if (strcmp(pp->name, value) != 0) 215221500Sadrian return (1); 216220559Sadrian 217221500Sadrian /* 218221500Sadrian * hint.map.0.name="uboot" - name of partition, will be available 219221500Sadrian * as "/dev/map/uboot" 220221500Sadrian */ 221221500Sadrian if (resource_string_value("map", i, "name", &name) != 0) { 222221500Sadrian if (bootverbose) 223221500Sadrian printf("MAP: hint.map.%d has no name\n", i); 224221500Sadrian return (1); 225221500Sadrian } 226220559Sadrian 227221500Sadrian /* 228221500Sadrian * hint.map.0.start="0x00010000" - partition start at 0x00010000 229221500Sadrian * or hint.map.0.start="search:0x00010000:0x200:marker text" - 230221500Sadrian * search for text "marker text", begin at 0x10000, step 0x200 231221500Sadrian * until we found marker or end of media reached 232221500Sadrian */ 233221500Sadrian if (resource_string_value("map", i, "start", &value) != 0) { 234221500Sadrian if (bootverbose) 235221500Sadrian printf("MAP: \"%s\" has no start value\n", name); 236221500Sadrian return (1); 237221500Sadrian } 238221500Sadrian if (find_marker(cp, value, &start) != 0) { 239221500Sadrian if (bootverbose) { 240221500Sadrian printf("MAP: \"%s\" can't parse/use start value\n", 241221500Sadrian name); 242221500Sadrian } 243221500Sadrian return (1); 244221500Sadrian } 245220559Sadrian 246221500Sadrian /* like "start" */ 247221500Sadrian if (resource_string_value("map", i, "end", &value) != 0) { 248221500Sadrian if (bootverbose) 249221500Sadrian printf("MAP: \"%s\" has no end value\n", name); 250221500Sadrian return (1); 251221500Sadrian } 252221500Sadrian if (find_marker(cp, value, &end) != 0) { 253221500Sadrian if (bootverbose) { 254221500Sadrian printf("MAP: \"%s\" can't parse/use start value\n", 255221500Sadrian name); 256221500Sadrian } 257221500Sadrian return (1); 258221500Sadrian } 259220559Sadrian 260221500Sadrian /* variable readonly optional, disable write access */ 261221500Sadrian if (resource_int_value("map", i, "readonly", &readonly) != 0) 262221500Sadrian readonly = 0; 263220559Sadrian 264221500Sadrian /* offset of partition data, from partition begin */ 265221500Sadrian if (resource_string_value("map", i, "offset", &value) == 0) { 266221500Sadrian offset = strtouq(value, &op, 0); 267221500Sadrian if (*op != '\0') { 268221500Sadrian if (bootverbose) { 269221500Sadrian printf("MAP: \"%s\" can't parse offset\n", 270221500Sadrian name); 271220559Sadrian } 272221500Sadrian return (1); 273220559Sadrian } 274221500Sadrian } else { 275221500Sadrian offset = 0; 276221500Sadrian } 277220559Sadrian 278221500Sadrian /* partition data size */ 279221500Sadrian if (resource_string_value("map", i, "dsize", &value) == 0) { 280221500Sadrian dsize = strtouq(value, &op, 0); 281221500Sadrian if (*op != '\0') { 282221500Sadrian if (bootverbose) { 283221500Sadrian printf("MAP: \"%s\" can't parse dsize\n", 284221500Sadrian name); 285221500Sadrian } 286221500Sadrian return (1); 287221500Sadrian } 288221500Sadrian } else { 289221500Sadrian dsize = 0; 290221500Sadrian } 291220559Sadrian 292221500Sadrian size = end - start; 293221500Sadrian if (dsize == 0) 294221500Sadrian dsize = size - offset; 295220559Sadrian 296221500Sadrian /* end is 0 or size is 0, No MAP - so next */ 297221500Sadrian if (end < start) { 298221500Sadrian if (bootverbose) { 299221500Sadrian printf("MAP: \"%s\", \"end\" less than " 300221500Sadrian "\"start\"\n", name); 301221500Sadrian } 302221500Sadrian return (1); 303221500Sadrian } 304220559Sadrian 305221500Sadrian if (offset + dsize > size) { 306221500Sadrian if (bootverbose) { 307221500Sadrian printf("MAP: \"%s\", \"dsize\" bigger than " 308221500Sadrian "partition - offset\n", name); 309221500Sadrian } 310221500Sadrian return (1); 311221500Sadrian } 312220559Sadrian 313221500Sadrian ret = g_slice_config(gp, i, G_SLICE_CONFIG_SET, start + offset, 314221500Sadrian dsize, cp->provider->sectorsize, "map/%s", name); 315221500Sadrian if (ret != 0) { 316221500Sadrian if (bootverbose) { 317221500Sadrian printf("MAP: g_slice_config returns %d for \"%s\"\n", 318221500Sadrian ret, name); 319221500Sadrian } 320221500Sadrian return (1); 321220559Sadrian } 322220559Sadrian 323221500Sadrian if (bootverbose) { 324221500Sadrian printf("MAP: %llxx%llx, data=%llxx%llx " 325221500Sadrian "\"/dev/map/%s\"\n", 326221500Sadrian start, size, offset, dsize, name); 327221500Sadrian } 328221500Sadrian 329221500Sadrian sc->offset[i] = start; 330221500Sadrian sc->size[i] = size; 331221500Sadrian sc->entry[i] = offset; 332221500Sadrian sc->dsize[i] = dsize; 333221500Sadrian sc->readonly[i] = readonly ? 1 : 0; 334221500Sadrian 335221500Sadrian return (0); 336221500Sadrian} 337221500Sadrian 338221500Sadrianstatic struct g_geom * 339221500Sadriang_map_taste(struct g_class *mp, struct g_provider *pp, int insist __unused) 340221500Sadrian{ 341221500Sadrian struct g_map_softc *sc; 342221500Sadrian struct g_consumer *cp; 343221500Sadrian struct g_geom *gp; 344221500Sadrian int i; 345221500Sadrian 346221500Sadrian g_trace(G_T_TOPOLOGY, "map_taste(%s,%s)", mp->name, pp->name); 347221500Sadrian g_topology_assert(); 348221500Sadrian if (strcmp(pp->geom->class->name, MAP_CLASS_NAME) == 0) 349220559Sadrian return (NULL); 350220559Sadrian 351221500Sadrian gp = g_slice_new(mp, MAP_MAXSLICE, pp, &cp, &sc, sizeof(*sc), 352221500Sadrian g_map_start); 353221500Sadrian if (gp == NULL) 354221500Sadrian return (NULL); 355221500Sadrian 356221500Sadrian /* interpose our access method */ 357221500Sadrian sc->parent_access = gp->access; 358221500Sadrian gp->access = g_map_access; 359221500Sadrian 360221500Sadrian for (i = 0; i < MAP_MAXSLICE; i++) 361221500Sadrian g_map_parse_part(mp, pp, cp, gp, sc, i); 362221500Sadrian 363221500Sadrian 364220559Sadrian g_access(cp, -1, 0, 0); 365220559Sadrian if (LIST_EMPTY(&gp->provider)) { 366221500Sadrian if (bootverbose) 367221500Sadrian printf("MAP: No valid partition found at %s\n", pp->name); 368220559Sadrian g_slice_spoiled(cp); 369220559Sadrian return (NULL); 370220559Sadrian } 371220559Sadrian return (gp); 372220559Sadrian} 373220559Sadrian 374220559Sadrianstatic void 375220559Sadriang_map_config(struct gctl_req *req, struct g_class *mp, const char *verb) 376220559Sadrian{ 377221500Sadrian struct g_geom *gp; 378220559Sadrian 379220559Sadrian g_topology_assert(); 380220559Sadrian gp = gctl_get_geom(req, mp, "geom"); 381220559Sadrian if (gp == NULL) 382220559Sadrian return; 383220559Sadrian gctl_error(req, "Unknown verb"); 384220559Sadrian} 385220559Sadrian 386220559Sadrianstatic struct g_class g_map_class = { 387220559Sadrian .name = MAP_CLASS_NAME, 388220559Sadrian .version = G_VERSION, 389220559Sadrian .taste = g_map_taste, 390220559Sadrian .dumpconf = g_map_dumpconf, 391220559Sadrian .ctlreq = g_map_config, 392220559Sadrian}; 393220559SadrianDECLARE_GEOM_CLASS(g_map_class, g_map); 394