Index: lib/geom/part/geom_part.c =================================================================== --- lib/geom/part/geom_part.c +++ lib/geom/part/geom_part.c @@ -127,8 +127,10 @@ { 's', "scheme", NULL, G_TYPE_STRING }, { 'n', "entries", G_VAL_OPTIONAL, G_TYPE_NUMBER }, { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, + { 'l', "tblpri_start", G_VAL_OPTIONAL, G_TYPE_STRING }, + { 'L', "tblsec_start", G_VAL_OPTIONAL, G_TYPE_STRING }, G_OPT_SENTINEL }, - "-s scheme [-n entries] [-f flags] provider" + "-s scheme [-l/-L start pri/sec table] [-n entries] [-f flags] provider" }, { "delete", 0, gpart_issue, { { 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER }, Index: lib/geom/part/gpart.8 =================================================================== --- lib/geom/part/gpart.8 +++ lib/geom/part/gpart.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd August 17, 2020 +.Dd November 29, 2021 .Dt GPART 8 .Os .Sh NAME @@ -64,6 +64,8 @@ .Fl s Ar scheme .Op Fl n Ar entries .Op Fl f Ar flags +.Op Fl l Ar pritbl +.Op Fl L Ar sectbl .Ar provider .\" ==== DELETE ==== .Nm @@ -302,6 +304,24 @@ The kernel must have support for a particular scheme before that scheme can be used to partition a disk. .El +.Pp +In addition the +.Cd GPT scheme +accepts the following options to the +.Cm create +command: +.Bl -tag -width 10n +.It Fl l Ar pritbl +The offset in bytes where the primary GPT partition table will begin. +This can be useful for embedded devices which want to place firmware +in the low blocks but still allow the first two disk sectors to be used +for the pmbr and the primary GPT partition header. +An SI unit suffix is not allowed. +.It Fl L Ar sectbl +The offset in bytes where the secondary GPT partition table will begin +(growing backwards). +An SI unit suffix is not allowed. +.El .\" ==== DELETE ==== .It Cm delete Delete a partition from geom Index: sys/geom/part/g_part.h =================================================================== --- sys/geom/part/g_part.h +++ sys/geom/part/g_part.h @@ -145,6 +145,9 @@ quad_t gpt_first; /* First allocatable LBA */ quad_t gpt_last; /* Last allocatable LBA */ int gpt_entries; + /* Offset where to start the primary/secondary table (or 0). */ + quad_t gpt_tblpri_start; + quad_t gpt_tblsec_start; /* * gpt_smhead and gpt_smtail are bitmaps representing the first * 32 sectors on the disk (gpt_smhead) and the last 32 sectors @@ -213,6 +216,8 @@ #define G_PART_PARM_ATTRIB 0x2000 #define G_PART_PARM_FORCE 0x4000 #define G_PART_PARM_SKIP_DSN 0x8000 +#define G_PART_PARM_TBLPRI_START 0x10000 +#define G_PART_PARM_TBLSEC_START 0x20000 struct g_part_parms { unsigned int gpp_parms; @@ -232,6 +237,8 @@ const char *gpp_attrib; unsigned int gpp_force; unsigned int gpp_skip_dsn; + quad_t gpp_tblpri_start; + quad_t gpp_tblsec_start; }; void g_part_geometry_heads(off_t, u_int, off_t *, u_int *); Index: sys/geom/part/g_part.c =================================================================== --- sys/geom/part/g_part.c +++ sys/geom/part/g_part.c @@ -996,6 +996,20 @@ gctl_error(req, "%d entries '%d'", EINVAL, gpp->gpp_entries); return (EINVAL); } + if ((gpp->gpp_parms & G_PART_PARM_TBLPRI_START) && + (gpp->gpp_tblpri_start < 256 || + gpp->gpp_tblpri_start > pp->mediasize - 512)) { + gctl_error(req, "%d tblpri_start '%jd'", EINVAL, + (intmax_t)gpp->gpp_tblpri_start); + return (EINVAL); + } + if ((gpp->gpp_parms & G_PART_PARM_TBLSEC_START) && + (gpp->gpp_tblsec_start < 512 || + gpp->gpp_tblsec_start > pp->mediasize)) { + gctl_error(req, "%d tblsec_start '%jd'", EINVAL, + (intmax_t)gpp->gpp_tblsec_start); + return (EINVAL); + } if (null == NULL) gp = g_new_geomf(&g_part_class, "%s", pp->name); @@ -1006,6 +1020,10 @@ table->gpt_scheme = gpp->gpp_scheme; table->gpt_entries = (gpp->gpp_parms & G_PART_PARM_ENTRIES) ? gpp->gpp_entries : scheme->gps_minent; + table->gpt_tblpri_start = (gpp->gpp_parms & G_PART_PARM_TBLPRI_START) ? + gpp->gpp_tblpri_start : 0; + table->gpt_tblsec_start = (gpp->gpp_parms & G_PART_PARM_TBLSEC_START) ? + gpp->gpp_tblsec_start : 0; LIST_INIT(&table->gpt_entry); if (null == NULL) { cp = g_new_consumer(gp); @@ -1642,6 +1660,8 @@ ctlreq = G_PART_CTL_CREATE; mparms |= G_PART_PARM_PROVIDER | G_PART_PARM_SCHEME; oparms |= G_PART_PARM_ENTRIES; + oparms |= G_PART_PARM_TBLPRI_START; + oparms |= G_PART_PARM_TBLSEC_START; } break; case 'd': @@ -1754,6 +1774,10 @@ case 't': if (!strcmp(ap->name, "type")) parm = G_PART_PARM_TYPE; + if (!strcmp(ap->name, "tblpri_start")) + parm = G_PART_PARM_TBLPRI_START; + if (!strcmp(ap->name, "tblsec_start")) + parm = G_PART_PARM_TBLSEC_START; break; case 'v': if (!strcmp(ap->name, "verb")) @@ -1818,6 +1842,14 @@ error = g_part_parm_quad(req, ap->name, &gpp.gpp_start); break; + case G_PART_PARM_TBLPRI_START: + error = g_part_parm_quad(req, ap->name, + &gpp.gpp_tblpri_start); + break; + case G_PART_PARM_TBLSEC_START: + error = g_part_parm_quad(req, ap->name, + &gpp.gpp_tblsec_start); + break; case G_PART_PARM_TYPE: error = g_part_parm_str(req, ap->name, &gpp.gpp_type); break; Index: sys/geom/part/g_part_gpt.c =================================================================== --- sys/geom/part/g_part_gpt.c +++ sys/geom/part/g_part_gpt.c @@ -105,6 +105,7 @@ static void g_gpt_printf_utf16(struct sbuf *, uint16_t *, size_t); static void g_gpt_utf8_to_utf16(const uint8_t *, uint16_t *, size_t); static void g_gpt_set_defaults(struct g_part_table *, struct g_provider *); +static int g_gpt_set_table_start(struct g_part_table *, struct g_provider *); static int g_part_gpt_add(struct g_part_table *, struct g_part_entry *, struct g_part_parms *); @@ -681,6 +682,7 @@ struct g_provider *pp; struct g_part_gpt_table *table; size_t tblsz; + int error; /* Our depth should be 0 unless nesting was explicitly enabled. */ if (!allow_nesting && basetable->gpt_depth != 0) @@ -695,6 +697,11 @@ pp->sectorsize) return (ENOSPC); + /* Do a pre-check. */ + error = g_gpt_set_table_start(basetable, pp); + if (error != 0) + return (error); + gpt_create_pmbr(table, pp); /* Allocate space for the header */ @@ -1282,6 +1289,41 @@ return (error); } +static int +g_gpt_set_table_start(struct g_part_table *basetable, struct g_provider *pp) +{ + struct g_part_gpt_table *table; + quad_t lba, last; + size_t tblsz; + + table = (struct g_part_gpt_table *)basetable; + last = pp->mediasize / pp->sectorsize - 1; + tblsz = howmany(basetable->gpt_entries * sizeof(struct gpt_ent), + pp->sectorsize); + + /* Set defaults. */ + table->lba[GPT_ELT_PRITBL] = 2; + table->lba[GPT_ELT_SECTBL] = last - tblsz; + + if (basetable->gpt_tblpri_start != 0) { + lba = (basetable->gpt_tblpri_start % pp->sectorsize) ? 1 : 0; + lba += basetable->gpt_tblpri_start / pp->sectorsize; + if (lba < 2 || lba > last) + return (ENOSPC); + table->lba[GPT_ELT_PRITBL] = lba; + } + + if (basetable->gpt_tblsec_start != 0) { + lba = (basetable->gpt_tblsec_start % pp->sectorsize) ? 1 : 0; + lba += basetable->gpt_tblsec_start / pp->sectorsize; + if (lba < 2 + tblsz || lba > last) + return (ENOSPC); + table->lba[GPT_ELT_SECTBL] = lba; + } + + return (0); +} + static void g_gpt_set_defaults(struct g_part_table *basetable, struct g_provider *pp) { @@ -1298,16 +1340,15 @@ pp->sectorsize); table->lba[GPT_ELT_PRIHDR] = 1; - table->lba[GPT_ELT_PRITBL] = 2; table->lba[GPT_ELT_SECHDR] = last; - table->lba[GPT_ELT_SECTBL] = last - tblsz; + g_gpt_set_table_start(basetable, pp); table->state[GPT_ELT_PRIHDR] = GPT_STATE_OK; table->state[GPT_ELT_PRITBL] = GPT_STATE_OK; table->state[GPT_ELT_SECHDR] = GPT_STATE_OK; table->state[GPT_ELT_SECTBL] = GPT_STATE_OK; - max = start = 2 + tblsz; - min = end = last - tblsz - 1; + max = start = table->lba[GPT_ELT_PRITBL] + tblsz; + min = end = table->lba[GPT_ELT_SECTBL] - 1; LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) { if (baseentry->gpe_deleted) continue;