1188354Smarcel/*- 2188354Smarcel * Copyright (c) 2007-2009 Marcel Moolenaar 3188354Smarcel * All rights reserved. 4188354Smarcel * 5188354Smarcel * Redistribution and use in source and binary forms, with or without 6188354Smarcel * modification, are permitted provided that the following conditions 7188354Smarcel * are met: 8188354Smarcel * 9188354Smarcel * 1. Redistributions of source code must retain the above copyright 10188354Smarcel * notice, this list of conditions and the following disclaimer. 11188354Smarcel * 2. Redistributions in binary form must reproduce the above copyright 12188354Smarcel * notice, this list of conditions and the following disclaimer in the 13188354Smarcel * documentation and/or other materials provided with the distribution. 14188354Smarcel * 15188354Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16188354Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17188354Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18188354Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19188354Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20188354Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21188354Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22188354Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23188354Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24188354Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25188354Smarcel */ 26188354Smarcel 27191130Smarcel#include "opt_geom.h" 28191130Smarcel 29188354Smarcel#include <sys/cdefs.h> 30188354Smarcel__FBSDID("$FreeBSD: releng/10.2/sys/geom/part/g_part_ebr.c 265912 2014-05-12 12:04:44Z ae $"); 31188354Smarcel 32188354Smarcel#include <sys/param.h> 33188354Smarcel#include <sys/bio.h> 34188354Smarcel#include <sys/diskmbr.h> 35188354Smarcel#include <sys/endian.h> 36188354Smarcel#include <sys/kernel.h> 37188354Smarcel#include <sys/kobj.h> 38188354Smarcel#include <sys/limits.h> 39188354Smarcel#include <sys/lock.h> 40188354Smarcel#include <sys/malloc.h> 41188354Smarcel#include <sys/mutex.h> 42188354Smarcel#include <sys/queue.h> 43188354Smarcel#include <sys/sbuf.h> 44188354Smarcel#include <sys/systm.h> 45219029Snetchild#include <sys/sysctl.h> 46188354Smarcel#include <geom/geom.h> 47188354Smarcel#include <geom/part/g_part.h> 48188354Smarcel 49188354Smarcel#include "g_part_if.h" 50188354Smarcel 51219029SnetchildFEATURE(geom_part_ebr, 52219029Snetchild "GEOM partitioning class for extended boot records support"); 53219029Snetchild#if defined(GEOM_PART_EBR_COMPAT) 54219029SnetchildFEATURE(geom_part_ebr_compat, 55219029Snetchild "GEOM EBR partitioning class: backward-compatible partition names"); 56219029Snetchild#endif 57219029Snetchild 58188354Smarcel#define EBRSIZE 512 59188354Smarcel 60188354Smarcelstruct g_part_ebr_table { 61188354Smarcel struct g_part_table base; 62223594Sae#ifndef GEOM_PART_EBR_COMPAT 63223594Sae u_char ebr[EBRSIZE]; 64223594Sae#endif 65188354Smarcel}; 66188354Smarcel 67188354Smarcelstruct g_part_ebr_entry { 68188354Smarcel struct g_part_entry base; 69188354Smarcel struct dos_partition ent; 70188354Smarcel}; 71188354Smarcel 72188354Smarcelstatic int g_part_ebr_add(struct g_part_table *, struct g_part_entry *, 73188354Smarcel struct g_part_parms *); 74188354Smarcelstatic int g_part_ebr_create(struct g_part_table *, struct g_part_parms *); 75188354Smarcelstatic int g_part_ebr_destroy(struct g_part_table *, struct g_part_parms *); 76188354Smarcelstatic void g_part_ebr_dumpconf(struct g_part_table *, struct g_part_entry *, 77188354Smarcel struct sbuf *, const char *); 78188354Smarcelstatic int g_part_ebr_dumpto(struct g_part_table *, struct g_part_entry *); 79191130Smarcel#if defined(GEOM_PART_EBR_COMPAT) 80191130Smarcelstatic void g_part_ebr_fullname(struct g_part_table *, struct g_part_entry *, 81191130Smarcel struct sbuf *, const char *); 82191130Smarcel#endif 83188354Smarcelstatic int g_part_ebr_modify(struct g_part_table *, struct g_part_entry *, 84188354Smarcel struct g_part_parms *); 85188354Smarcelstatic const char *g_part_ebr_name(struct g_part_table *, struct g_part_entry *, 86188354Smarcel char *, size_t); 87188667Smarcelstatic int g_part_ebr_precheck(struct g_part_table *, enum g_part_ctl, 88188667Smarcel struct g_part_parms *); 89188354Smarcelstatic int g_part_ebr_probe(struct g_part_table *, struct g_consumer *); 90188354Smarcelstatic int g_part_ebr_read(struct g_part_table *, struct g_consumer *); 91188354Smarcelstatic int g_part_ebr_setunset(struct g_part_table *, struct g_part_entry *, 92188354Smarcel const char *, unsigned int); 93188354Smarcelstatic const char *g_part_ebr_type(struct g_part_table *, struct g_part_entry *, 94188354Smarcel char *, size_t); 95188354Smarcelstatic int g_part_ebr_write(struct g_part_table *, struct g_consumer *); 96265912Saestatic int g_part_ebr_resize(struct g_part_table *, struct g_part_entry *, 97265912Sae struct g_part_parms *); 98188354Smarcel 99188354Smarcelstatic kobj_method_t g_part_ebr_methods[] = { 100188354Smarcel KOBJMETHOD(g_part_add, g_part_ebr_add), 101188354Smarcel KOBJMETHOD(g_part_create, g_part_ebr_create), 102188354Smarcel KOBJMETHOD(g_part_destroy, g_part_ebr_destroy), 103188354Smarcel KOBJMETHOD(g_part_dumpconf, g_part_ebr_dumpconf), 104188354Smarcel KOBJMETHOD(g_part_dumpto, g_part_ebr_dumpto), 105191130Smarcel#if defined(GEOM_PART_EBR_COMPAT) 106191130Smarcel KOBJMETHOD(g_part_fullname, g_part_ebr_fullname), 107191130Smarcel#endif 108188354Smarcel KOBJMETHOD(g_part_modify, g_part_ebr_modify), 109188354Smarcel KOBJMETHOD(g_part_name, g_part_ebr_name), 110188667Smarcel KOBJMETHOD(g_part_precheck, g_part_ebr_precheck), 111188354Smarcel KOBJMETHOD(g_part_probe, g_part_ebr_probe), 112188354Smarcel KOBJMETHOD(g_part_read, g_part_ebr_read), 113265912Sae KOBJMETHOD(g_part_resize, g_part_ebr_resize), 114188354Smarcel KOBJMETHOD(g_part_setunset, g_part_ebr_setunset), 115188354Smarcel KOBJMETHOD(g_part_type, g_part_ebr_type), 116188354Smarcel KOBJMETHOD(g_part_write, g_part_ebr_write), 117188354Smarcel { 0, 0 } 118188354Smarcel}; 119188354Smarcel 120188354Smarcelstatic struct g_part_scheme g_part_ebr_scheme = { 121188354Smarcel "EBR", 122188354Smarcel g_part_ebr_methods, 123188354Smarcel sizeof(struct g_part_ebr_table), 124188354Smarcel .gps_entrysz = sizeof(struct g_part_ebr_entry), 125188354Smarcel .gps_minent = 1, 126188354Smarcel .gps_maxent = INT_MAX, 127188354Smarcel}; 128188354SmarcelG_PART_SCHEME_DECLARE(g_part_ebr); 129188354Smarcel 130218014Saestatic struct g_part_ebr_alias { 131218014Sae u_char typ; 132218014Sae int alias; 133218014Sae} ebr_alias_match[] = { 134218014Sae { DOSPTYP_386BSD, G_PART_ALIAS_FREEBSD }, 135218014Sae { DOSPTYP_NTFS, G_PART_ALIAS_MS_NTFS }, 136218014Sae { DOSPTYP_FAT32, G_PART_ALIAS_MS_FAT32 }, 137218014Sae { DOSPTYP_LINSWP, G_PART_ALIAS_LINUX_SWAP }, 138218014Sae { DOSPTYP_LINUX, G_PART_ALIAS_LINUX_DATA }, 139218014Sae { DOSPTYP_LINLVM, G_PART_ALIAS_LINUX_LVM }, 140218014Sae { DOSPTYP_LINRAID, G_PART_ALIAS_LINUX_RAID }, 141218014Sae}; 142218014Sae 143188667Smarcelstatic void ebr_set_chs(struct g_part_table *, uint32_t, u_char *, u_char *, 144188667Smarcel u_char *); 145188667Smarcel 146188354Smarcelstatic void 147188354Smarcelebr_entry_decode(const char *p, struct dos_partition *ent) 148188354Smarcel{ 149188354Smarcel ent->dp_flag = p[0]; 150188354Smarcel ent->dp_shd = p[1]; 151188354Smarcel ent->dp_ssect = p[2]; 152188354Smarcel ent->dp_scyl = p[3]; 153188354Smarcel ent->dp_typ = p[4]; 154188354Smarcel ent->dp_ehd = p[5]; 155188354Smarcel ent->dp_esect = p[6]; 156188354Smarcel ent->dp_ecyl = p[7]; 157188354Smarcel ent->dp_start = le32dec(p + 8); 158188354Smarcel ent->dp_size = le32dec(p + 12); 159188354Smarcel} 160188354Smarcel 161188667Smarcelstatic void 162188667Smarcelebr_entry_link(struct g_part_table *table, uint32_t start, uint32_t end, 163188667Smarcel u_char *buf) 164188667Smarcel{ 165188667Smarcel 166188667Smarcel buf[0] = 0 /* dp_flag */; 167188667Smarcel ebr_set_chs(table, start, &buf[3] /* dp_scyl */, &buf[1] /* dp_shd */, 168188667Smarcel &buf[2] /* dp_ssect */); 169188667Smarcel buf[4] = 5 /* dp_typ */; 170188667Smarcel ebr_set_chs(table, end, &buf[7] /* dp_ecyl */, &buf[5] /* dp_ehd */, 171188667Smarcel &buf[6] /* dp_esect */); 172188667Smarcel le32enc(buf + 8, start); 173188667Smarcel le32enc(buf + 12, end - start + 1); 174188667Smarcel} 175188667Smarcel 176188354Smarcelstatic int 177188667Smarcelebr_parse_type(const char *type, u_char *dp_typ) 178188667Smarcel{ 179188667Smarcel const char *alias; 180188667Smarcel char *endp; 181188667Smarcel long lt; 182218014Sae int i; 183188667Smarcel 184188667Smarcel if (type[0] == '!') { 185188667Smarcel lt = strtol(type + 1, &endp, 0); 186188667Smarcel if (type[1] == '\0' || *endp != '\0' || lt <= 0 || lt >= 256) 187188667Smarcel return (EINVAL); 188188667Smarcel *dp_typ = (u_char)lt; 189188667Smarcel return (0); 190188667Smarcel } 191218014Sae for (i = 0; 192218014Sae i < sizeof(ebr_alias_match) / sizeof(ebr_alias_match[0]); i++) { 193218014Sae alias = g_part_alias_name(ebr_alias_match[i].alias); 194218014Sae if (strcasecmp(type, alias) == 0) { 195218014Sae *dp_typ = ebr_alias_match[i].typ; 196218014Sae return (0); 197218014Sae } 198188667Smarcel } 199188667Smarcel return (EINVAL); 200188667Smarcel} 201188667Smarcel 202218014Sae 203188667Smarcelstatic void 204188667Smarcelebr_set_chs(struct g_part_table *table, uint32_t lba, u_char *cylp, u_char *hdp, 205188667Smarcel u_char *secp) 206188667Smarcel{ 207188667Smarcel uint32_t cyl, hd, sec; 208188667Smarcel 209188667Smarcel sec = lba % table->gpt_sectors + 1; 210188667Smarcel lba /= table->gpt_sectors; 211188667Smarcel hd = lba % table->gpt_heads; 212188667Smarcel lba /= table->gpt_heads; 213188667Smarcel cyl = lba; 214188667Smarcel if (cyl > 1023) 215188667Smarcel sec = hd = cyl = ~0; 216188667Smarcel 217188667Smarcel *cylp = cyl & 0xff; 218188667Smarcel *hdp = hd & 0xff; 219188667Smarcel *secp = (sec & 0x3f) | ((cyl >> 2) & 0xc0); 220188667Smarcel} 221188667Smarcel 222188667Smarcelstatic int 223265910Saeebr_align(struct g_part_table *basetable, uint32_t *start, uint32_t *size) 224265910Sae{ 225265910Sae uint32_t sectors; 226265910Sae 227265910Sae sectors = basetable->gpt_sectors; 228265910Sae if (*size < 2 * sectors) 229265910Sae return (EINVAL); 230265910Sae if (*start % sectors) { 231265910Sae *size += (*start % sectors) - sectors; 232265910Sae *start -= (*start % sectors) - sectors; 233265910Sae } 234265910Sae if (*size % sectors) 235265910Sae *size -= (*size % sectors); 236265910Sae if (*size < 2 * sectors) 237265910Sae return (EINVAL); 238265910Sae return (0); 239265910Sae} 240265910Sae 241265910Sae 242265910Saestatic int 243188354Smarcelg_part_ebr_add(struct g_part_table *basetable, struct g_part_entry *baseentry, 244188354Smarcel struct g_part_parms *gpp) 245188354Smarcel{ 246188667Smarcel struct g_provider *pp; 247188667Smarcel struct g_part_ebr_entry *entry; 248265910Sae uint32_t start, size; 249188354Smarcel 250188667Smarcel if (gpp->gpp_parms & G_PART_PARM_LABEL) 251188667Smarcel return (EINVAL); 252188667Smarcel 253265910Sae pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider; 254188667Smarcel entry = (struct g_part_ebr_entry *)baseentry; 255188667Smarcel start = gpp->gpp_start; 256188667Smarcel size = gpp->gpp_size; 257265910Sae if (ebr_align(basetable, &start, &size) != 0) 258188667Smarcel return (EINVAL); 259188667Smarcel if (baseentry->gpe_deleted) 260188667Smarcel bzero(&entry->ent, sizeof(entry->ent)); 261188667Smarcel 262213769Srpaulo KASSERT(baseentry->gpe_start <= start, ("%s", __func__)); 263213769Srpaulo KASSERT(baseentry->gpe_end >= start + size - 1, ("%s", __func__)); 264265910Sae baseentry->gpe_index = (start / basetable->gpt_sectors) + 1; 265265910Sae baseentry->gpe_offset = 266265910Sae (off_t)(start + basetable->gpt_sectors) * pp->sectorsize; 267188667Smarcel baseentry->gpe_start = start; 268188667Smarcel baseentry->gpe_end = start + size - 1; 269265910Sae entry->ent.dp_start = basetable->gpt_sectors; 270265910Sae entry->ent.dp_size = size - basetable->gpt_sectors; 271188667Smarcel ebr_set_chs(basetable, entry->ent.dp_start, &entry->ent.dp_scyl, 272188667Smarcel &entry->ent.dp_shd, &entry->ent.dp_ssect); 273188667Smarcel ebr_set_chs(basetable, baseentry->gpe_end, &entry->ent.dp_ecyl, 274188667Smarcel &entry->ent.dp_ehd, &entry->ent.dp_esect); 275188667Smarcel return (ebr_parse_type(gpp->gpp_type, &entry->ent.dp_typ)); 276188354Smarcel} 277188354Smarcel 278188354Smarcelstatic int 279188354Smarcelg_part_ebr_create(struct g_part_table *basetable, struct g_part_parms *gpp) 280188354Smarcel{ 281231754Sae char type[64]; 282188667Smarcel struct g_consumer *cp; 283188667Smarcel struct g_provider *pp; 284190535Smarcel uint32_t msize; 285188667Smarcel int error; 286188354Smarcel 287188667Smarcel pp = gpp->gpp_provider; 288188667Smarcel 289188667Smarcel if (pp->sectorsize < EBRSIZE) 290188667Smarcel return (ENOSPC); 291188667Smarcel if (pp->sectorsize > 4096) 292188667Smarcel return (ENXIO); 293188667Smarcel 294188667Smarcel /* Check that we have a parent and that it's a MBR. */ 295188667Smarcel if (basetable->gpt_depth == 0) 296188667Smarcel return (ENXIO); 297188667Smarcel cp = LIST_FIRST(&pp->consumers); 298231754Sae error = g_getattr("PART::scheme", cp, &type); 299231754Sae if (error != 0) 300188667Smarcel return (error); 301231754Sae if (strcmp(type, "MBR") != 0) 302188667Smarcel return (ENXIO); 303231754Sae error = g_getattr("PART::type", cp, &type); 304231754Sae if (error != 0) 305231754Sae return (error); 306231754Sae if (strcmp(type, "ebr") != 0) 307231754Sae return (ENXIO); 308188667Smarcel 309221656Sae msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX); 310190535Smarcel basetable->gpt_first = 0; 311190535Smarcel basetable->gpt_last = msize - 1; 312188899Smarcel basetable->gpt_entries = msize / basetable->gpt_sectors; 313188667Smarcel return (0); 314188354Smarcel} 315188354Smarcel 316188354Smarcelstatic int 317188354Smarcelg_part_ebr_destroy(struct g_part_table *basetable, struct g_part_parms *gpp) 318188354Smarcel{ 319188354Smarcel 320188354Smarcel /* Wipe the first sector to clear the partitioning. */ 321188354Smarcel basetable->gpt_smhead |= 1; 322188354Smarcel return (0); 323188354Smarcel} 324188354Smarcel 325188354Smarcelstatic void 326188354Smarcelg_part_ebr_dumpconf(struct g_part_table *table, struct g_part_entry *baseentry, 327188354Smarcel struct sbuf *sb, const char *indent) 328188354Smarcel{ 329188354Smarcel struct g_part_ebr_entry *entry; 330188354Smarcel 331188354Smarcel entry = (struct g_part_ebr_entry *)baseentry; 332188354Smarcel if (indent == NULL) { 333188354Smarcel /* conftxt: libdisk compatibility */ 334188354Smarcel sbuf_printf(sb, " xs MBREXT xt %u", entry->ent.dp_typ); 335188354Smarcel } else if (entry != NULL) { 336188354Smarcel /* confxml: partition entry information */ 337188354Smarcel sbuf_printf(sb, "%s<rawtype>%u</rawtype>\n", indent, 338188354Smarcel entry->ent.dp_typ); 339188354Smarcel if (entry->ent.dp_flag & 0x80) 340188354Smarcel sbuf_printf(sb, "%s<attrib>active</attrib>\n", indent); 341188354Smarcel } else { 342188354Smarcel /* confxml: scheme information */ 343188354Smarcel } 344188354Smarcel} 345188354Smarcel 346188354Smarcelstatic int 347188354Smarcelg_part_ebr_dumpto(struct g_part_table *table, struct g_part_entry *baseentry) 348188354Smarcel{ 349188354Smarcel struct g_part_ebr_entry *entry; 350188354Smarcel 351230064Struckman /* Allow dumping to a FreeBSD partition or Linux swap partition only. */ 352188354Smarcel entry = (struct g_part_ebr_entry *)baseentry; 353230064Struckman return ((entry->ent.dp_typ == DOSPTYP_386BSD || 354230064Struckman entry->ent.dp_typ == DOSPTYP_LINSWP) ? 1 : 0); 355188354Smarcel} 356188354Smarcel 357191130Smarcel#if defined(GEOM_PART_EBR_COMPAT) 358191130Smarcelstatic void 359191130Smarcelg_part_ebr_fullname(struct g_part_table *table, struct g_part_entry *entry, 360191130Smarcel struct sbuf *sb, const char *pfx) 361191130Smarcel{ 362191130Smarcel struct g_part_entry *iter; 363191130Smarcel u_int idx; 364191130Smarcel 365191130Smarcel idx = 5; 366191130Smarcel LIST_FOREACH(iter, &table->gpt_entry, gpe_entry) { 367191130Smarcel if (iter == entry) 368191130Smarcel break; 369191130Smarcel idx++; 370191130Smarcel } 371191134Smarcel sbuf_printf(sb, "%.*s%u", (int)strlen(pfx) - 1, pfx, idx); 372191130Smarcel} 373191130Smarcel#endif 374191130Smarcel 375188354Smarcelstatic int 376188354Smarcelg_part_ebr_modify(struct g_part_table *basetable, 377188354Smarcel struct g_part_entry *baseentry, struct g_part_parms *gpp) 378188354Smarcel{ 379188667Smarcel struct g_part_ebr_entry *entry; 380188354Smarcel 381188667Smarcel if (gpp->gpp_parms & G_PART_PARM_LABEL) 382188667Smarcel return (EINVAL); 383188667Smarcel 384188667Smarcel entry = (struct g_part_ebr_entry *)baseentry; 385188667Smarcel if (gpp->gpp_parms & G_PART_PARM_TYPE) 386188667Smarcel return (ebr_parse_type(gpp->gpp_type, &entry->ent.dp_typ)); 387188667Smarcel return (0); 388188354Smarcel} 389188354Smarcel 390265912Saestatic int 391265912Saeg_part_ebr_resize(struct g_part_table *basetable, 392265912Sae struct g_part_entry *baseentry, struct g_part_parms *gpp) 393265912Sae{ 394265912Sae struct g_provider *pp; 395265912Sae 396265912Sae if (baseentry != NULL) 397265912Sae return (EOPNOTSUPP); 398265912Sae pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider; 399265912Sae basetable->gpt_last = MIN(pp->mediasize / pp->sectorsize, 400265912Sae UINT32_MAX) - 1; 401265912Sae return (0); 402265912Sae} 403265912Sae 404188354Smarcelstatic const char * 405188354Smarcelg_part_ebr_name(struct g_part_table *table, struct g_part_entry *entry, 406188354Smarcel char *buf, size_t bufsz) 407188354Smarcel{ 408188354Smarcel 409190849Smarcel snprintf(buf, bufsz, "+%08u", entry->gpe_index); 410188354Smarcel return (buf); 411188354Smarcel} 412188354Smarcel 413188354Smarcelstatic int 414188667Smarcelg_part_ebr_precheck(struct g_part_table *table, enum g_part_ctl req, 415188667Smarcel struct g_part_parms *gpp) 416188667Smarcel{ 417191130Smarcel#if defined(GEOM_PART_EBR_COMPAT) 418216755Sae if (req == G_PART_CTL_DESTROY) 419216755Sae return (0); 420191130Smarcel return (ECANCELED); 421191130Smarcel#else 422188667Smarcel /* 423188667Smarcel * The index is a function of the start of the partition. 424188667Smarcel * This is not something the user can override, nor is it 425188667Smarcel * something the common code will do right. We can set the 426188667Smarcel * index now so that we get what we need. 427188667Smarcel */ 428188667Smarcel if (req == G_PART_CTL_ADD) 429188667Smarcel gpp->gpp_index = (gpp->gpp_start / table->gpt_sectors) + 1; 430188667Smarcel return (0); 431191130Smarcel#endif 432188667Smarcel} 433188667Smarcel 434188667Smarcelstatic int 435188354Smarcelg_part_ebr_probe(struct g_part_table *table, struct g_consumer *cp) 436188354Smarcel{ 437231754Sae char type[64]; 438188354Smarcel struct g_provider *pp; 439188354Smarcel u_char *buf, *p; 440216754Sae int error, index, res; 441188354Smarcel uint16_t magic; 442188354Smarcel 443188354Smarcel pp = cp->provider; 444188354Smarcel 445188354Smarcel /* Sanity-check the provider. */ 446188354Smarcel if (pp->sectorsize < EBRSIZE || pp->mediasize < pp->sectorsize) 447188354Smarcel return (ENOSPC); 448188354Smarcel if (pp->sectorsize > 4096) 449188354Smarcel return (ENXIO); 450188354Smarcel 451188354Smarcel /* Check that we have a parent and that it's a MBR. */ 452188354Smarcel if (table->gpt_depth == 0) 453188354Smarcel return (ENXIO); 454231754Sae error = g_getattr("PART::scheme", cp, &type); 455231754Sae if (error != 0) 456188354Smarcel return (error); 457231754Sae if (strcmp(type, "MBR") != 0) 458188354Smarcel return (ENXIO); 459231754Sae /* Check that partition has type DOSPTYP_EBR. */ 460231754Sae error = g_getattr("PART::type", cp, &type); 461231754Sae if (error != 0) 462231754Sae return (error); 463231754Sae if (strcmp(type, "ebr") != 0) 464231754Sae return (ENXIO); 465188354Smarcel 466188354Smarcel /* Check that there's a EBR. */ 467188354Smarcel buf = g_read_data(cp, 0L, pp->sectorsize, &error); 468188354Smarcel if (buf == NULL) 469188354Smarcel return (error); 470188354Smarcel 471188354Smarcel /* We goto out on mismatch. */ 472188354Smarcel res = ENXIO; 473188354Smarcel 474188354Smarcel magic = le16dec(buf + DOSMAGICOFFSET); 475188354Smarcel if (magic != DOSMAGIC) 476188354Smarcel goto out; 477188354Smarcel 478216754Sae for (index = 0; index < 2; index++) { 479188354Smarcel p = buf + DOSPARTOFF + index * DOSPARTSIZE; 480188354Smarcel if (p[0] != 0 && p[0] != 0x80) 481188354Smarcel goto out; 482188354Smarcel } 483190443Smarcel res = G_PART_PROBE_PRI_NORM; 484188354Smarcel 485188354Smarcel out: 486188354Smarcel g_free(buf); 487188354Smarcel return (res); 488188354Smarcel} 489188354Smarcel 490188354Smarcelstatic int 491188354Smarcelg_part_ebr_read(struct g_part_table *basetable, struct g_consumer *cp) 492188354Smarcel{ 493188354Smarcel struct dos_partition ent[2]; 494188354Smarcel struct g_provider *pp; 495188354Smarcel struct g_part_entry *baseentry; 496188354Smarcel struct g_part_ebr_table *table; 497188354Smarcel struct g_part_ebr_entry *entry; 498188354Smarcel u_char *buf; 499188354Smarcel off_t ofs, msize; 500188354Smarcel u_int lba; 501223594Sae int error, index; 502188354Smarcel 503188354Smarcel pp = cp->provider; 504188354Smarcel table = (struct g_part_ebr_table *)basetable; 505221656Sae msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX); 506188354Smarcel 507188354Smarcel lba = 0; 508188354Smarcel while (1) { 509188354Smarcel ofs = (off_t)lba * pp->sectorsize; 510188354Smarcel buf = g_read_data(cp, ofs, pp->sectorsize, &error); 511188354Smarcel if (buf == NULL) 512188354Smarcel return (error); 513188354Smarcel 514188354Smarcel ebr_entry_decode(buf + DOSPARTOFF + 0 * DOSPARTSIZE, ent + 0); 515188354Smarcel ebr_entry_decode(buf + DOSPARTOFF + 1 * DOSPARTSIZE, ent + 1); 516216754Sae 517216754Sae /* The 3rd & 4th entries should be zeroes. */ 518216754Sae if (le64dec(buf + DOSPARTOFF + 2 * DOSPARTSIZE) + 519216754Sae le64dec(buf + DOSPARTOFF + 3 * DOSPARTSIZE) != 0) { 520216754Sae basetable->gpt_corrupt = 1; 521216754Sae printf("GEOM: %s: invalid entries in the EBR ignored.\n", 522216754Sae pp->name); 523216754Sae } 524223594Sae#ifndef GEOM_PART_EBR_COMPAT 525223594Sae /* Save the first EBR, it can contain a boot code */ 526223594Sae if (lba == 0) 527223594Sae bcopy(buf, table->ebr, sizeof(table->ebr)); 528223594Sae#endif 529188354Smarcel g_free(buf); 530188354Smarcel 531188354Smarcel if (ent[0].dp_typ == 0) 532188354Smarcel break; 533188354Smarcel 534188354Smarcel if (ent[0].dp_typ == 5 && ent[1].dp_typ == 0) { 535188354Smarcel lba = ent[0].dp_start; 536188354Smarcel continue; 537188354Smarcel } 538188354Smarcel 539188354Smarcel index = (lba / basetable->gpt_sectors) + 1; 540188354Smarcel baseentry = (struct g_part_entry *)g_part_new_entry(basetable, 541188354Smarcel index, lba, lba + ent[0].dp_start + ent[0].dp_size - 1); 542188354Smarcel baseentry->gpe_offset = (off_t)(lba + ent[0].dp_start) * 543188354Smarcel pp->sectorsize; 544188354Smarcel entry = (struct g_part_ebr_entry *)baseentry; 545188354Smarcel entry->ent = ent[0]; 546188354Smarcel 547188354Smarcel if (ent[1].dp_typ == 0) 548188354Smarcel break; 549188354Smarcel 550188354Smarcel lba = ent[1].dp_start; 551188354Smarcel } 552188354Smarcel 553188354Smarcel basetable->gpt_entries = msize / basetable->gpt_sectors; 554188354Smarcel basetable->gpt_first = 0; 555222280Sae basetable->gpt_last = msize - 1; 556188354Smarcel return (0); 557188354Smarcel} 558188354Smarcel 559188354Smarcelstatic int 560188354Smarcelg_part_ebr_setunset(struct g_part_table *table, struct g_part_entry *baseentry, 561188354Smarcel const char *attrib, unsigned int set) 562188354Smarcel{ 563188667Smarcel struct g_part_entry *iter; 564188667Smarcel struct g_part_ebr_entry *entry; 565188667Smarcel int changed; 566188354Smarcel 567251588Smarcel if (baseentry == NULL) 568251588Smarcel return (ENODEV); 569188667Smarcel if (strcasecmp(attrib, "active") != 0) 570188667Smarcel return (EINVAL); 571188667Smarcel 572188667Smarcel /* Only one entry can have the active attribute. */ 573188667Smarcel LIST_FOREACH(iter, &table->gpt_entry, gpe_entry) { 574188667Smarcel if (iter->gpe_deleted) 575188667Smarcel continue; 576188667Smarcel changed = 0; 577188667Smarcel entry = (struct g_part_ebr_entry *)iter; 578188667Smarcel if (iter == baseentry) { 579188667Smarcel if (set && (entry->ent.dp_flag & 0x80) == 0) { 580188667Smarcel entry->ent.dp_flag |= 0x80; 581188667Smarcel changed = 1; 582188667Smarcel } else if (!set && (entry->ent.dp_flag & 0x80)) { 583188667Smarcel entry->ent.dp_flag &= ~0x80; 584188667Smarcel changed = 1; 585188667Smarcel } 586188667Smarcel } else { 587188667Smarcel if (set && (entry->ent.dp_flag & 0x80)) { 588188667Smarcel entry->ent.dp_flag &= ~0x80; 589188667Smarcel changed = 1; 590188667Smarcel } 591188667Smarcel } 592188667Smarcel if (changed && !iter->gpe_created) 593188667Smarcel iter->gpe_modified = 1; 594188667Smarcel } 595188667Smarcel return (0); 596188354Smarcel} 597188354Smarcel 598188354Smarcelstatic const char * 599188354Smarcelg_part_ebr_type(struct g_part_table *basetable, struct g_part_entry *baseentry, 600188354Smarcel char *buf, size_t bufsz) 601188354Smarcel{ 602188354Smarcel struct g_part_ebr_entry *entry; 603218014Sae int i; 604188354Smarcel 605188354Smarcel entry = (struct g_part_ebr_entry *)baseentry; 606218014Sae for (i = 0; 607218014Sae i < sizeof(ebr_alias_match) / sizeof(ebr_alias_match[0]); i++) { 608218014Sae if (ebr_alias_match[i].typ == entry->ent.dp_typ) 609218014Sae return (g_part_alias_name(ebr_alias_match[i].alias)); 610218014Sae } 611218014Sae snprintf(buf, bufsz, "!%d", entry->ent.dp_typ); 612188354Smarcel return (buf); 613188354Smarcel} 614188354Smarcel 615188354Smarcelstatic int 616188354Smarcelg_part_ebr_write(struct g_part_table *basetable, struct g_consumer *cp) 617188354Smarcel{ 618223594Sae#ifndef GEOM_PART_EBR_COMPAT 619223594Sae struct g_part_ebr_table *table; 620223594Sae#endif 621188667Smarcel struct g_provider *pp; 622188667Smarcel struct g_part_entry *baseentry, *next; 623188667Smarcel struct g_part_ebr_entry *entry; 624188667Smarcel u_char *buf; 625188667Smarcel u_char *p; 626188667Smarcel int error; 627188354Smarcel 628188667Smarcel pp = cp->provider; 629188667Smarcel buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 630223594Sae#ifndef GEOM_PART_EBR_COMPAT 631223594Sae table = (struct g_part_ebr_table *)basetable; 632223594Sae bcopy(table->ebr, buf, DOSPARTOFF); 633223594Sae#endif 634188667Smarcel le16enc(buf + DOSMAGICOFFSET, DOSMAGIC); 635188667Smarcel 636188667Smarcel baseentry = LIST_FIRST(&basetable->gpt_entry); 637188667Smarcel while (baseentry != NULL && baseentry->gpe_deleted) 638188667Smarcel baseentry = LIST_NEXT(baseentry, gpe_entry); 639188667Smarcel 640218909Sbrucec /* Wipe-out the first EBR when there are no slices. */ 641188667Smarcel if (baseentry == NULL) { 642188667Smarcel error = g_write_data(cp, 0, buf, pp->sectorsize); 643188667Smarcel goto out; 644188667Smarcel } 645188667Smarcel 646188667Smarcel /* 647188667Smarcel * If the first partition is not in LBA 0, we need to 648188667Smarcel * put a "link" EBR in LBA 0. 649188667Smarcel */ 650188667Smarcel if (baseentry->gpe_start != 0) { 651188667Smarcel ebr_entry_link(basetable, (uint32_t)baseentry->gpe_start, 652188667Smarcel (uint32_t)baseentry->gpe_end, buf + DOSPARTOFF); 653188667Smarcel error = g_write_data(cp, 0, buf, pp->sectorsize); 654188667Smarcel if (error) 655188667Smarcel goto out; 656188667Smarcel } 657188667Smarcel 658188667Smarcel do { 659188667Smarcel entry = (struct g_part_ebr_entry *)baseentry; 660188667Smarcel 661188667Smarcel p = buf + DOSPARTOFF; 662188667Smarcel p[0] = entry->ent.dp_flag; 663188667Smarcel p[1] = entry->ent.dp_shd; 664188667Smarcel p[2] = entry->ent.dp_ssect; 665188667Smarcel p[3] = entry->ent.dp_scyl; 666188667Smarcel p[4] = entry->ent.dp_typ; 667188667Smarcel p[5] = entry->ent.dp_ehd; 668188667Smarcel p[6] = entry->ent.dp_esect; 669188667Smarcel p[7] = entry->ent.dp_ecyl; 670188667Smarcel le32enc(p + 8, entry->ent.dp_start); 671188667Smarcel le32enc(p + 12, entry->ent.dp_size); 672188667Smarcel 673188838Smarcel next = LIST_NEXT(baseentry, gpe_entry); 674188838Smarcel while (next != NULL && next->gpe_deleted) 675188838Smarcel next = LIST_NEXT(next, gpe_entry); 676188667Smarcel 677188667Smarcel p += DOSPARTSIZE; 678188667Smarcel if (next != NULL) 679188667Smarcel ebr_entry_link(basetable, (uint32_t)next->gpe_start, 680188667Smarcel (uint32_t)next->gpe_end, p); 681188667Smarcel else 682188667Smarcel bzero(p, DOSPARTSIZE); 683188667Smarcel 684188667Smarcel error = g_write_data(cp, baseentry->gpe_start * pp->sectorsize, 685188667Smarcel buf, pp->sectorsize); 686223594Sae#ifndef GEOM_PART_EBR_COMPAT 687223594Sae if (baseentry->gpe_start == 0) 688223594Sae bzero(buf, DOSPARTOFF); 689223594Sae#endif 690188667Smarcel baseentry = next; 691188667Smarcel } while (!error && baseentry != NULL); 692188667Smarcel 693188667Smarcel out: 694188667Smarcel g_free(buf); 695188667Smarcel return (error); 696188354Smarcel} 697