1166551Smarcel/*- 2179748Smarcel * Copyright (c) 2006-2008 Marcel Moolenaar 3166551Smarcel * All rights reserved. 4166551Smarcel * 5166551Smarcel * Redistribution and use in source and binary forms, with or without 6166551Smarcel * modification, are permitted provided that the following conditions 7166551Smarcel * are met: 8166551Smarcel * 9166551Smarcel * 1. Redistributions of source code must retain the above copyright 10166551Smarcel * notice, this list of conditions and the following disclaimer. 11166551Smarcel * 2. Redistributions in binary form must reproduce the above copyright 12166551Smarcel * notice, this list of conditions and the following disclaimer in the 13166551Smarcel * documentation and/or other materials provided with the distribution. 14166551Smarcel * 15166551Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16166551Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17166551Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18166551Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19166551Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20166551Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21166551Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22166551Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23166551Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24166551Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25166551Smarcel */ 26166551Smarcel 27166551Smarcel#include <sys/cdefs.h> 28166551Smarcel__FBSDID("$FreeBSD$"); 29166551Smarcel 30166551Smarcel#include <sys/param.h> 31166551Smarcel#include <sys/apm.h> 32166551Smarcel#include <sys/bio.h> 33166551Smarcel#include <sys/endian.h> 34166551Smarcel#include <sys/kernel.h> 35166551Smarcel#include <sys/kobj.h> 36166551Smarcel#include <sys/limits.h> 37166551Smarcel#include <sys/lock.h> 38166551Smarcel#include <sys/malloc.h> 39166551Smarcel#include <sys/mutex.h> 40166551Smarcel#include <sys/queue.h> 41166551Smarcel#include <sys/sbuf.h> 42166551Smarcel#include <sys/systm.h> 43219029Snetchild#include <sys/sysctl.h> 44166551Smarcel#include <geom/geom.h> 45267156Sae#include <geom/geom_int.h> 46166551Smarcel#include <geom/part/g_part.h> 47166551Smarcel 48166551Smarcel#include "g_part_if.h" 49166551Smarcel 50219029SnetchildFEATURE(geom_part_apm, "GEOM partitioning class for Apple-style partitions"); 51219029Snetchild 52166551Smarcelstruct g_part_apm_table { 53166551Smarcel struct g_part_table base; 54166551Smarcel struct apm_ddr ddr; 55166551Smarcel struct apm_ent self; 56184552Simp int tivo_series1; 57166551Smarcel}; 58166551Smarcel 59166551Smarcelstruct g_part_apm_entry { 60166551Smarcel struct g_part_entry base; 61166551Smarcel struct apm_ent ent; 62166551Smarcel}; 63166551Smarcel 64166551Smarcelstatic int g_part_apm_add(struct g_part_table *, struct g_part_entry *, 65166551Smarcel struct g_part_parms *); 66166551Smarcelstatic int g_part_apm_create(struct g_part_table *, struct g_part_parms *); 67166551Smarcelstatic int g_part_apm_destroy(struct g_part_table *, struct g_part_parms *); 68188429Simpstatic void g_part_apm_dumpconf(struct g_part_table *, struct g_part_entry *, 69178444Smarcel struct sbuf *, const char *); 70166551Smarcelstatic int g_part_apm_dumpto(struct g_part_table *, struct g_part_entry *); 71166551Smarcelstatic int g_part_apm_modify(struct g_part_table *, struct g_part_entry *, 72166551Smarcel struct g_part_parms *); 73188429Simpstatic const char *g_part_apm_name(struct g_part_table *, struct g_part_entry *, 74166551Smarcel char *, size_t); 75166551Smarcelstatic int g_part_apm_probe(struct g_part_table *, struct g_consumer *); 76166551Smarcelstatic int g_part_apm_read(struct g_part_table *, struct g_consumer *); 77166551Smarcelstatic const char *g_part_apm_type(struct g_part_table *, struct g_part_entry *, 78166551Smarcel char *, size_t); 79166551Smarcelstatic int g_part_apm_write(struct g_part_table *, struct g_consumer *); 80207094Smarcelstatic int g_part_apm_resize(struct g_part_table *, struct g_part_entry *, 81207094Smarcel struct g_part_parms *); 82166551Smarcel 83166551Smarcelstatic kobj_method_t g_part_apm_methods[] = { 84166551Smarcel KOBJMETHOD(g_part_add, g_part_apm_add), 85166551Smarcel KOBJMETHOD(g_part_create, g_part_apm_create), 86166551Smarcel KOBJMETHOD(g_part_destroy, g_part_apm_destroy), 87178444Smarcel KOBJMETHOD(g_part_dumpconf, g_part_apm_dumpconf), 88166551Smarcel KOBJMETHOD(g_part_dumpto, g_part_apm_dumpto), 89166551Smarcel KOBJMETHOD(g_part_modify, g_part_apm_modify), 90207094Smarcel KOBJMETHOD(g_part_resize, g_part_apm_resize), 91166551Smarcel KOBJMETHOD(g_part_name, g_part_apm_name), 92166551Smarcel KOBJMETHOD(g_part_probe, g_part_apm_probe), 93166551Smarcel KOBJMETHOD(g_part_read, g_part_apm_read), 94166551Smarcel KOBJMETHOD(g_part_type, g_part_apm_type), 95166551Smarcel KOBJMETHOD(g_part_write, g_part_apm_write), 96166551Smarcel { 0, 0 } 97166551Smarcel}; 98166551Smarcel 99166551Smarcelstatic struct g_part_scheme g_part_apm_scheme = { 100166551Smarcel "APM", 101166551Smarcel g_part_apm_methods, 102166551Smarcel sizeof(struct g_part_apm_table), 103166551Smarcel .gps_entrysz = sizeof(struct g_part_apm_entry), 104166551Smarcel .gps_minent = 16, 105231349Sae .gps_maxent = 4096, 106166551Smarcel}; 107177510SmarcelG_PART_SCHEME_DECLARE(g_part_apm); 108166551Smarcel 109184552Simpstatic void 110184552Simpswab(char *buf, size_t bufsz) 111184552Simp{ 112184552Simp int i; 113184552Simp char ch; 114184552Simp 115184552Simp for (i = 0; i < bufsz; i += 2) { 116184552Simp ch = buf[i]; 117184552Simp buf[i] = buf[i + 1]; 118184552Simp buf[i + 1] = ch; 119184552Simp } 120184552Simp} 121184552Simp 122166551Smarcelstatic int 123166551Smarcelapm_parse_type(const char *type, char *buf, size_t bufsz) 124166551Smarcel{ 125169389Smarcel const char *alias; 126166551Smarcel 127169389Smarcel if (type[0] == '!') { 128169389Smarcel type++; 129166551Smarcel if (strlen(type) > bufsz) 130166551Smarcel return (EINVAL); 131166551Smarcel if (!strcmp(type, APM_ENT_TYPE_SELF) || 132166551Smarcel !strcmp(type, APM_ENT_TYPE_UNUSED)) 133166551Smarcel return (EINVAL); 134166551Smarcel strncpy(buf, type, bufsz); 135166551Smarcel return (0); 136166551Smarcel } 137208173Snwhitehorn alias = g_part_alias_name(G_PART_ALIAS_APPLE_BOOT); 138208173Snwhitehorn if (!strcasecmp(type, alias)) { 139208173Snwhitehorn strcpy(buf, APM_ENT_TYPE_APPLE_BOOT); 140208173Snwhitehorn return (0); 141208173Snwhitehorn } 142208173Snwhitehorn alias = g_part_alias_name(G_PART_ALIAS_APPLE_HFS); 143208173Snwhitehorn if (!strcasecmp(type, alias)) { 144208173Snwhitehorn strcpy(buf, APM_ENT_TYPE_APPLE_HFS); 145208173Snwhitehorn return (0); 146208173Snwhitehorn } 147208173Snwhitehorn alias = g_part_alias_name(G_PART_ALIAS_APPLE_UFS); 148208173Snwhitehorn if (!strcasecmp(type, alias)) { 149208173Snwhitehorn strcpy(buf, APM_ENT_TYPE_APPLE_UFS); 150208173Snwhitehorn return (0); 151208173Snwhitehorn } 152169389Smarcel alias = g_part_alias_name(G_PART_ALIAS_FREEBSD); 153169389Smarcel if (!strcasecmp(type, alias)) { 154166551Smarcel strcpy(buf, APM_ENT_TYPE_FREEBSD); 155169389Smarcel return (0); 156169389Smarcel } 157236023Smarcel alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_NANDFS); 158236023Smarcel if (!strcasecmp(type, alias)) { 159236023Smarcel strcpy(buf, APM_ENT_TYPE_FREEBSD_NANDFS); 160236023Smarcel return (0); 161236023Smarcel } 162169389Smarcel alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP); 163169389Smarcel if (!strcasecmp(type, alias)) { 164166551Smarcel strcpy(buf, APM_ENT_TYPE_FREEBSD_SWAP); 165169389Smarcel return (0); 166169389Smarcel } 167169389Smarcel alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS); 168169389Smarcel if (!strcasecmp(type, alias)) { 169166551Smarcel strcpy(buf, APM_ENT_TYPE_FREEBSD_UFS); 170169389Smarcel return (0); 171169389Smarcel } 172169389Smarcel alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM); 173169389Smarcel if (!strcasecmp(type, alias)) { 174166551Smarcel strcpy(buf, APM_ENT_TYPE_FREEBSD_VINUM); 175169389Smarcel return (0); 176169389Smarcel } 177172857Smarcel alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS); 178172857Smarcel if (!strcasecmp(type, alias)) { 179172857Smarcel strcpy(buf, APM_ENT_TYPE_FREEBSD_ZFS); 180172857Smarcel return (0); 181172857Smarcel } 182169389Smarcel return (EINVAL); 183166551Smarcel} 184166551Smarcel 185166551Smarcelstatic int 186184552Simpapm_read_ent(struct g_consumer *cp, uint32_t blk, struct apm_ent *ent, 187184552Simp int tivo_series1) 188166551Smarcel{ 189166551Smarcel struct g_provider *pp; 190166551Smarcel char *buf; 191166551Smarcel int error; 192166551Smarcel 193166551Smarcel pp = cp->provider; 194166551Smarcel buf = g_read_data(cp, pp->sectorsize * blk, pp->sectorsize, &error); 195166551Smarcel if (buf == NULL) 196166551Smarcel return (error); 197184552Simp if (tivo_series1) 198184552Simp swab(buf, pp->sectorsize); 199166551Smarcel ent->ent_sig = be16dec(buf); 200166551Smarcel ent->ent_pmblkcnt = be32dec(buf + 4); 201166551Smarcel ent->ent_start = be32dec(buf + 8); 202166551Smarcel ent->ent_size = be32dec(buf + 12); 203166551Smarcel bcopy(buf + 16, ent->ent_name, sizeof(ent->ent_name)); 204166551Smarcel bcopy(buf + 48, ent->ent_type, sizeof(ent->ent_type)); 205166551Smarcel g_free(buf); 206166551Smarcel return (0); 207166551Smarcel} 208166551Smarcel 209166551Smarcelstatic int 210166551Smarcelg_part_apm_add(struct g_part_table *basetable, struct g_part_entry *baseentry, 211166551Smarcel struct g_part_parms *gpp) 212166551Smarcel{ 213166551Smarcel struct g_part_apm_entry *entry; 214166551Smarcel struct g_part_apm_table *table; 215166551Smarcel int error; 216166551Smarcel 217166551Smarcel entry = (struct g_part_apm_entry *)baseentry; 218166551Smarcel table = (struct g_part_apm_table *)basetable; 219166551Smarcel entry->ent.ent_sig = APM_ENT_SIG; 220166551Smarcel entry->ent.ent_pmblkcnt = table->self.ent_pmblkcnt; 221166551Smarcel entry->ent.ent_start = gpp->gpp_start; 222166551Smarcel entry->ent.ent_size = gpp->gpp_size; 223166551Smarcel if (baseentry->gpe_deleted) { 224166551Smarcel bzero(entry->ent.ent_type, sizeof(entry->ent.ent_type)); 225166551Smarcel bzero(entry->ent.ent_name, sizeof(entry->ent.ent_name)); 226166551Smarcel } 227166551Smarcel error = apm_parse_type(gpp->gpp_type, entry->ent.ent_type, 228166551Smarcel sizeof(entry->ent.ent_type)); 229166551Smarcel if (error) 230166551Smarcel return (error); 231166551Smarcel if (gpp->gpp_parms & G_PART_PARM_LABEL) { 232166551Smarcel if (strlen(gpp->gpp_label) > sizeof(entry->ent.ent_name)) 233166551Smarcel return (EINVAL); 234166551Smarcel strncpy(entry->ent.ent_name, gpp->gpp_label, 235166551Smarcel sizeof(entry->ent.ent_name)); 236166551Smarcel } 237228076Sae if (baseentry->gpe_index >= table->self.ent_pmblkcnt) 238228076Sae table->self.ent_pmblkcnt = baseentry->gpe_index + 1; 239228076Sae KASSERT(table->self.ent_size >= table->self.ent_pmblkcnt, 240228076Sae ("%s", __func__)); 241228076Sae KASSERT(table->self.ent_size > baseentry->gpe_index, 242228076Sae ("%s", __func__)); 243166551Smarcel return (0); 244166551Smarcel} 245166551Smarcel 246166551Smarcelstatic int 247166551Smarcelg_part_apm_create(struct g_part_table *basetable, struct g_part_parms *gpp) 248166551Smarcel{ 249166551Smarcel struct g_provider *pp; 250166551Smarcel struct g_part_apm_table *table; 251190461Smarcel uint32_t last; 252166551Smarcel 253190461Smarcel /* We don't nest, which means that our depth should be 0. */ 254190461Smarcel if (basetable->gpt_depth != 0) 255190461Smarcel return (ENXIO); 256190461Smarcel 257166551Smarcel table = (struct g_part_apm_table *)basetable; 258166551Smarcel pp = gpp->gpp_provider; 259166551Smarcel if (pp->sectorsize != 512 || 260166551Smarcel pp->mediasize < (2 + 2 * basetable->gpt_entries) * pp->sectorsize) 261166551Smarcel return (ENOSPC); 262166551Smarcel 263190461Smarcel /* APM uses 32-bit LBAs. */ 264221654Sae last = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX) - 1; 265190461Smarcel 266166551Smarcel basetable->gpt_first = 2 + basetable->gpt_entries; 267190461Smarcel basetable->gpt_last = last; 268166551Smarcel 269166551Smarcel table->ddr.ddr_sig = APM_DDR_SIG; 270166551Smarcel table->ddr.ddr_blksize = pp->sectorsize; 271190461Smarcel table->ddr.ddr_blkcount = last + 1; 272166551Smarcel 273166551Smarcel table->self.ent_sig = APM_ENT_SIG; 274166551Smarcel table->self.ent_pmblkcnt = basetable->gpt_entries + 1; 275166551Smarcel table->self.ent_start = 1; 276166551Smarcel table->self.ent_size = table->self.ent_pmblkcnt; 277166551Smarcel strcpy(table->self.ent_name, "Apple"); 278166551Smarcel strcpy(table->self.ent_type, APM_ENT_TYPE_SELF); 279166551Smarcel return (0); 280166551Smarcel} 281166551Smarcel 282166551Smarcelstatic int 283166551Smarcelg_part_apm_destroy(struct g_part_table *basetable, struct g_part_parms *gpp) 284166551Smarcel{ 285166551Smarcel 286166551Smarcel /* Wipe the first 2 sectors to clear the partitioning. */ 287166551Smarcel basetable->gpt_smhead |= 3; 288166551Smarcel return (0); 289166551Smarcel} 290166551Smarcel 291188429Simpstatic void 292178444Smarcelg_part_apm_dumpconf(struct g_part_table *table, struct g_part_entry *baseentry, 293178444Smarcel struct sbuf *sb, const char *indent) 294178444Smarcel{ 295179748Smarcel union { 296179748Smarcel char name[APM_ENT_NAMELEN + 1]; 297179748Smarcel char type[APM_ENT_TYPELEN + 1]; 298179748Smarcel } u; 299178444Smarcel struct g_part_apm_entry *entry; 300178444Smarcel 301178444Smarcel entry = (struct g_part_apm_entry *)baseentry; 302179748Smarcel if (indent == NULL) { 303179748Smarcel /* conftxt: libdisk compatibility */ 304179748Smarcel sbuf_printf(sb, " xs APPLE xt %s", entry->ent.ent_type); 305179748Smarcel } else if (entry != NULL) { 306179748Smarcel /* confxml: partition entry information */ 307179748Smarcel strncpy(u.name, entry->ent.ent_name, APM_ENT_NAMELEN); 308179748Smarcel u.name[APM_ENT_NAMELEN] = '\0'; 309267156Sae sbuf_printf(sb, "%s<label>", indent); 310267156Sae g_conf_printf_escaped(sb, "%s", u.name); 311267156Sae sbuf_printf(sb, "</label>\n"); 312179748Smarcel strncpy(u.type, entry->ent.ent_type, APM_ENT_TYPELEN); 313179748Smarcel u.type[APM_ENT_TYPELEN] = '\0'; 314267156Sae sbuf_printf(sb, "%s<rawtype>", indent); 315267156Sae g_conf_printf_escaped(sb, "%s", u.type); 316267156Sae sbuf_printf(sb, "</rawtype>\n"); 317179748Smarcel } else { 318179748Smarcel /* confxml: scheme information */ 319179748Smarcel } 320178444Smarcel} 321178444Smarcel 322178444Smarcelstatic int 323166551Smarcelg_part_apm_dumpto(struct g_part_table *table, struct g_part_entry *baseentry) 324166551Smarcel{ 325166551Smarcel struct g_part_apm_entry *entry; 326166551Smarcel 327166551Smarcel entry = (struct g_part_apm_entry *)baseentry; 328166551Smarcel return ((!strcmp(entry->ent.ent_type, APM_ENT_TYPE_FREEBSD_SWAP)) 329166551Smarcel ? 1 : 0); 330166551Smarcel} 331166551Smarcel 332166551Smarcelstatic int 333166551Smarcelg_part_apm_modify(struct g_part_table *basetable, 334166551Smarcel struct g_part_entry *baseentry, struct g_part_parms *gpp) 335166551Smarcel{ 336166551Smarcel struct g_part_apm_entry *entry; 337166551Smarcel int error; 338166551Smarcel 339166551Smarcel entry = (struct g_part_apm_entry *)baseentry; 340166551Smarcel if (gpp->gpp_parms & G_PART_PARM_LABEL) { 341166551Smarcel if (strlen(gpp->gpp_label) > sizeof(entry->ent.ent_name)) 342166551Smarcel return (EINVAL); 343166551Smarcel } 344166551Smarcel if (gpp->gpp_parms & G_PART_PARM_TYPE) { 345166551Smarcel error = apm_parse_type(gpp->gpp_type, entry->ent.ent_type, 346166551Smarcel sizeof(entry->ent.ent_type)); 347166551Smarcel if (error) 348166551Smarcel return (error); 349166551Smarcel } 350166551Smarcel if (gpp->gpp_parms & G_PART_PARM_LABEL) { 351166551Smarcel strncpy(entry->ent.ent_name, gpp->gpp_label, 352166551Smarcel sizeof(entry->ent.ent_name)); 353166551Smarcel } 354166551Smarcel return (0); 355166551Smarcel} 356166551Smarcel 357207094Smarcelstatic int 358207094Smarcelg_part_apm_resize(struct g_part_table *basetable, 359207094Smarcel struct g_part_entry *baseentry, struct g_part_parms *gpp) 360207094Smarcel{ 361207094Smarcel struct g_part_apm_entry *entry; 362265912Sae struct g_provider *pp; 363207094Smarcel 364265912Sae if (baseentry == NULL) { 365265912Sae pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider; 366265912Sae basetable->gpt_last = MIN(pp->mediasize / pp->sectorsize, 367265912Sae UINT32_MAX) - 1; 368265912Sae return (0); 369265912Sae } 370265912Sae 371207094Smarcel entry = (struct g_part_apm_entry *)baseentry; 372207094Smarcel baseentry->gpe_end = baseentry->gpe_start + gpp->gpp_size - 1; 373207094Smarcel entry->ent.ent_size = gpp->gpp_size; 374207094Smarcel 375207094Smarcel return (0); 376207094Smarcel} 377207094Smarcel 378188429Simpstatic const char * 379166551Smarcelg_part_apm_name(struct g_part_table *table, struct g_part_entry *baseentry, 380166551Smarcel char *buf, size_t bufsz) 381166551Smarcel{ 382166551Smarcel 383166551Smarcel snprintf(buf, bufsz, "s%d", baseentry->gpe_index + 1); 384166551Smarcel return (buf); 385166551Smarcel} 386166551Smarcel 387166551Smarcelstatic int 388166551Smarcelg_part_apm_probe(struct g_part_table *basetable, struct g_consumer *cp) 389166551Smarcel{ 390166551Smarcel struct g_provider *pp; 391166551Smarcel struct g_part_apm_table *table; 392166551Smarcel char *buf; 393166551Smarcel int error; 394166551Smarcel 395166551Smarcel /* We don't nest, which means that our depth should be 0. */ 396166551Smarcel if (basetable->gpt_depth != 0) 397166551Smarcel return (ENXIO); 398166551Smarcel 399166551Smarcel table = (struct g_part_apm_table *)basetable; 400184552Simp table->tivo_series1 = 0; 401166551Smarcel pp = cp->provider; 402166551Smarcel 403166551Smarcel /* Sanity-check the provider. */ 404166551Smarcel if (pp->mediasize < 4 * pp->sectorsize) 405166551Smarcel return (ENOSPC); 406166551Smarcel 407166551Smarcel /* Check that there's a Driver Descriptor Record (DDR). */ 408166551Smarcel buf = g_read_data(cp, 0L, pp->sectorsize, &error); 409166551Smarcel if (buf == NULL) 410166551Smarcel return (error); 411220652Sgavin if (be16dec(buf) == APM_DDR_SIG) { 412184552Simp /* Normal Apple DDR */ 413184552Simp table->ddr.ddr_sig = be16dec(buf); 414184552Simp table->ddr.ddr_blksize = be16dec(buf + 2); 415184552Simp table->ddr.ddr_blkcount = be32dec(buf + 4); 416184552Simp g_free(buf); 417184552Simp if (table->ddr.ddr_blksize != pp->sectorsize) 418184552Simp return (ENXIO); 419221654Sae if (table->ddr.ddr_blkcount > pp->mediasize / pp->sectorsize) 420221654Sae return (ENXIO); 421184552Simp } else { 422184552Simp /* 423184552Simp * Check for Tivo drives, which have no DDR and a different 424184552Simp * signature. Those whose first two bytes are 14 92 are 425184552Simp * Series 2 drives, and aren't supported. Those that start 426184552Simp * with 92 14 are series 1 drives and are supported. 427184552Simp */ 428184552Simp if (be16dec(buf) != 0x9214) { 429184552Simp /* If this is 0x1492 it could be a series 2 drive */ 430184552Simp g_free(buf); 431184552Simp return (ENXIO); 432184552Simp } 433184552Simp table->ddr.ddr_sig = APM_DDR_SIG; /* XXX */ 434184552Simp table->ddr.ddr_blksize = pp->sectorsize; /* XXX */ 435221654Sae table->ddr.ddr_blkcount = 436221654Sae MIN(pp->mediasize / pp->sectorsize, UINT32_MAX); 437184552Simp table->tivo_series1 = 1; 438184552Simp g_free(buf); 439184552Simp } 440166551Smarcel 441166551Smarcel /* Check that there's a Partition Map. */ 442184552Simp error = apm_read_ent(cp, 1, &table->self, table->tivo_series1); 443166551Smarcel if (error) 444166551Smarcel return (error); 445166551Smarcel if (table->self.ent_sig != APM_ENT_SIG) 446166551Smarcel return (ENXIO); 447166551Smarcel if (strcmp(table->self.ent_type, APM_ENT_TYPE_SELF)) 448166551Smarcel return (ENXIO); 449166551Smarcel if (table->self.ent_pmblkcnt >= table->ddr.ddr_blkcount) 450166551Smarcel return (ENXIO); 451166551Smarcel return (G_PART_PROBE_PRI_NORM); 452166551Smarcel} 453166551Smarcel 454166551Smarcelstatic int 455166551Smarcelg_part_apm_read(struct g_part_table *basetable, struct g_consumer *cp) 456166551Smarcel{ 457166551Smarcel struct apm_ent ent; 458166551Smarcel struct g_part_apm_entry *entry; 459166551Smarcel struct g_part_apm_table *table; 460166551Smarcel int error, index; 461166551Smarcel 462166551Smarcel table = (struct g_part_apm_table *)basetable; 463166551Smarcel 464228061Sae basetable->gpt_first = table->self.ent_size + 1; 465166551Smarcel basetable->gpt_last = table->ddr.ddr_blkcount - 1; 466228076Sae basetable->gpt_entries = table->self.ent_size - 1; 467166551Smarcel 468166551Smarcel for (index = table->self.ent_pmblkcnt - 1; index > 0; index--) { 469184552Simp error = apm_read_ent(cp, index + 1, &ent, table->tivo_series1); 470166551Smarcel if (error) 471166551Smarcel continue; 472166551Smarcel if (!strcmp(ent.ent_type, APM_ENT_TYPE_UNUSED)) 473166551Smarcel continue; 474166551Smarcel entry = (struct g_part_apm_entry *)g_part_new_entry(basetable, 475166551Smarcel index, ent.ent_start, ent.ent_start + ent.ent_size - 1); 476166551Smarcel entry->ent = ent; 477166551Smarcel } 478166551Smarcel 479166551Smarcel return (0); 480166551Smarcel} 481166551Smarcel 482166551Smarcelstatic const char * 483166551Smarcelg_part_apm_type(struct g_part_table *basetable, struct g_part_entry *baseentry, 484166551Smarcel char *buf, size_t bufsz) 485166551Smarcel{ 486166551Smarcel struct g_part_apm_entry *entry; 487166551Smarcel const char *type; 488166551Smarcel size_t len; 489166551Smarcel 490166551Smarcel entry = (struct g_part_apm_entry *)baseentry; 491166551Smarcel type = entry->ent.ent_type; 492208173Snwhitehorn if (!strcmp(type, APM_ENT_TYPE_APPLE_BOOT)) 493208173Snwhitehorn return (g_part_alias_name(G_PART_ALIAS_APPLE_BOOT)); 494208173Snwhitehorn if (!strcmp(type, APM_ENT_TYPE_APPLE_HFS)) 495208173Snwhitehorn return (g_part_alias_name(G_PART_ALIAS_APPLE_HFS)); 496208173Snwhitehorn if (!strcmp(type, APM_ENT_TYPE_APPLE_UFS)) 497208173Snwhitehorn return (g_part_alias_name(G_PART_ALIAS_APPLE_UFS)); 498166551Smarcel if (!strcmp(type, APM_ENT_TYPE_FREEBSD)) 499166551Smarcel return (g_part_alias_name(G_PART_ALIAS_FREEBSD)); 500236023Smarcel if (!strcmp(type, APM_ENT_TYPE_FREEBSD_NANDFS)) 501236023Smarcel return (g_part_alias_name(G_PART_ALIAS_FREEBSD_NANDFS)); 502166551Smarcel if (!strcmp(type, APM_ENT_TYPE_FREEBSD_SWAP)) 503166551Smarcel return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP)); 504166551Smarcel if (!strcmp(type, APM_ENT_TYPE_FREEBSD_UFS)) 505166551Smarcel return (g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS)); 506166551Smarcel if (!strcmp(type, APM_ENT_TYPE_FREEBSD_VINUM)) 507166551Smarcel return (g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM)); 508172857Smarcel if (!strcmp(type, APM_ENT_TYPE_FREEBSD_ZFS)) 509172857Smarcel return (g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS)); 510170362Smarcel buf[0] = '!'; 511170362Smarcel len = MIN(sizeof(entry->ent.ent_type), bufsz - 2); 512170362Smarcel bcopy(type, buf + 1, len); 513170362Smarcel buf[len + 1] = '\0'; 514166551Smarcel return (buf); 515166551Smarcel} 516166551Smarcel 517166551Smarcelstatic int 518166551Smarcelg_part_apm_write(struct g_part_table *basetable, struct g_consumer *cp) 519166551Smarcel{ 520228076Sae struct g_provider *pp; 521166551Smarcel struct g_part_entry *baseentry; 522166551Smarcel struct g_part_apm_entry *entry; 523166551Smarcel struct g_part_apm_table *table; 524228076Sae char *buf, *ptr; 525228076Sae uint32_t index; 526228076Sae int error; 527228076Sae size_t tblsz; 528166551Smarcel 529228076Sae pp = cp->provider; 530166551Smarcel table = (struct g_part_apm_table *)basetable; 531184552Simp /* 532184552Simp * Tivo Series 1 disk partitions are currently read-only. 533184552Simp */ 534184552Simp if (table->tivo_series1) 535184552Simp return (EOPNOTSUPP); 536166551Smarcel 537228076Sae /* Write the DDR only when we're newly created. */ 538166551Smarcel if (basetable->gpt_created) { 539228076Sae buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 540166551Smarcel be16enc(buf, table->ddr.ddr_sig); 541166551Smarcel be16enc(buf + 2, table->ddr.ddr_blksize); 542166551Smarcel be32enc(buf + 4, table->ddr.ddr_blkcount); 543228076Sae error = g_write_data(cp, 0, buf, pp->sectorsize); 544228076Sae g_free(buf); 545166551Smarcel if (error) 546166551Smarcel return (error); 547166551Smarcel } 548166551Smarcel 549228076Sae /* Allocate the buffer for all entries */ 550228076Sae tblsz = table->self.ent_pmblkcnt; 551228076Sae buf = g_malloc(tblsz * pp->sectorsize, M_WAITOK | M_ZERO); 552228076Sae 553228076Sae /* Fill the self entry */ 554228076Sae be16enc(buf, APM_ENT_SIG); 555166551Smarcel be32enc(buf + 4, table->self.ent_pmblkcnt); 556228076Sae be32enc(buf + 8, table->self.ent_start); 557228076Sae be32enc(buf + 12, table->self.ent_size); 558228076Sae bcopy(table->self.ent_name, buf + 16, sizeof(table->self.ent_name)); 559228076Sae bcopy(table->self.ent_type, buf + 48, sizeof(table->self.ent_type)); 560166551Smarcel 561166551Smarcel baseentry = LIST_FIRST(&basetable->gpt_entry); 562228076Sae for (index = 1; index < tblsz; index++) { 563169389Smarcel entry = (baseentry != NULL && index == baseentry->gpe_index) 564169389Smarcel ? (struct g_part_apm_entry *)baseentry : NULL; 565228076Sae ptr = buf + index * pp->sectorsize; 566228076Sae be16enc(ptr, APM_ENT_SIG); 567228076Sae be32enc(ptr + 4, table->self.ent_pmblkcnt); 568169389Smarcel if (entry != NULL && !baseentry->gpe_deleted) { 569228076Sae be32enc(ptr + 8, entry->ent.ent_start); 570228076Sae be32enc(ptr + 12, entry->ent.ent_size); 571228076Sae bcopy(entry->ent.ent_name, ptr + 16, 572166551Smarcel sizeof(entry->ent.ent_name)); 573228076Sae bcopy(entry->ent.ent_type, ptr + 48, 574166551Smarcel sizeof(entry->ent.ent_type)); 575166551Smarcel } else { 576228076Sae strcpy(ptr + 48, APM_ENT_TYPE_UNUSED); 577166551Smarcel } 578169389Smarcel if (entry != NULL) 579169389Smarcel baseentry = LIST_NEXT(baseentry, gpe_entry); 580166551Smarcel } 581166551Smarcel 582228076Sae for (index = 0; index < tblsz; index += MAXPHYS / pp->sectorsize) { 583228076Sae error = g_write_data(cp, (1 + index) * pp->sectorsize, 584228076Sae buf + index * pp->sectorsize, 585228076Sae (tblsz - index > MAXPHYS / pp->sectorsize) ? MAXPHYS: 586228076Sae (tblsz - index) * pp->sectorsize); 587228076Sae if (error) { 588228076Sae g_free(buf); 589228076Sae return (error); 590228076Sae } 591228076Sae } 592228076Sae g_free(buf); 593166551Smarcel return (0); 594166551Smarcel} 595