1166551Smarcel/*- 2226647Smarcel * Copyright (c) 2002, 2005-2007, 2011 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/bio.h> 32166551Smarcel#include <sys/diskmbr.h> 33166551Smarcel#include <sys/endian.h> 34166551Smarcel#include <sys/gpt.h> 35166551Smarcel#include <sys/kernel.h> 36166551Smarcel#include <sys/kobj.h> 37166551Smarcel#include <sys/limits.h> 38166551Smarcel#include <sys/lock.h> 39166551Smarcel#include <sys/malloc.h> 40166551Smarcel#include <sys/mutex.h> 41166551Smarcel#include <sys/queue.h> 42166551Smarcel#include <sys/sbuf.h> 43166551Smarcel#include <sys/systm.h> 44219029Snetchild#include <sys/sysctl.h> 45166551Smarcel#include <sys/uuid.h> 46166551Smarcel#include <geom/geom.h> 47166551Smarcel#include <geom/part/g_part.h> 48166551Smarcel 49166551Smarcel#include "g_part_if.h" 50166551Smarcel 51219029SnetchildFEATURE(geom_part_gpt, "GEOM partitioning class for GPT partitions support"); 52219029Snetchild 53166551SmarcelCTASSERT(offsetof(struct gpt_hdr, padding) == 92); 54166551SmarcelCTASSERT(sizeof(struct gpt_ent) == 128); 55166551Smarcel 56166551Smarcel#define EQUUID(a,b) (memcmp(a, b, sizeof(struct uuid)) == 0) 57166551Smarcel 58178180Smarcel#define MBRSIZE 512 59178180Smarcel 60166551Smarcelenum gpt_elt { 61166551Smarcel GPT_ELT_PRIHDR, 62166551Smarcel GPT_ELT_PRITBL, 63166551Smarcel GPT_ELT_SECHDR, 64166551Smarcel GPT_ELT_SECTBL, 65166551Smarcel GPT_ELT_COUNT 66166551Smarcel}; 67166551Smarcel 68166551Smarcelenum gpt_state { 69166551Smarcel GPT_STATE_UNKNOWN, /* Not determined. */ 70166551Smarcel GPT_STATE_MISSING, /* No signature found. */ 71166551Smarcel GPT_STATE_CORRUPT, /* Checksum mismatch. */ 72166551Smarcel GPT_STATE_INVALID, /* Nonconformant/invalid. */ 73166551Smarcel GPT_STATE_OK /* Perfectly fine. */ 74166551Smarcel}; 75166551Smarcel 76166551Smarcelstruct g_part_gpt_table { 77166551Smarcel struct g_part_table base; 78178180Smarcel u_char mbr[MBRSIZE]; 79199017Srnoland struct gpt_hdr *hdr; 80166551Smarcel quad_t lba[GPT_ELT_COUNT]; 81166551Smarcel enum gpt_state state[GPT_ELT_COUNT]; 82226647Smarcel int bootcamp; 83166551Smarcel}; 84166551Smarcel 85166551Smarcelstruct g_part_gpt_entry { 86166551Smarcel struct g_part_entry base; 87166551Smarcel struct gpt_ent ent; 88166551Smarcel}; 89166551Smarcel 90179763Smarcelstatic void g_gpt_printf_utf16(struct sbuf *, uint16_t *, size_t); 91179763Smarcelstatic void g_gpt_utf8_to_utf16(const uint8_t *, uint16_t *, size_t); 92223660Saestatic void g_gpt_set_defaults(struct g_part_table *, struct g_provider *); 93179763Smarcel 94166551Smarcelstatic int g_part_gpt_add(struct g_part_table *, struct g_part_entry *, 95166551Smarcel struct g_part_parms *); 96178180Smarcelstatic int g_part_gpt_bootcode(struct g_part_table *, struct g_part_parms *); 97166551Smarcelstatic int g_part_gpt_create(struct g_part_table *, struct g_part_parms *); 98166551Smarcelstatic int g_part_gpt_destroy(struct g_part_table *, struct g_part_parms *); 99188429Simpstatic void g_part_gpt_dumpconf(struct g_part_table *, struct g_part_entry *, 100178444Smarcel struct sbuf *, const char *); 101166551Smarcelstatic int g_part_gpt_dumpto(struct g_part_table *, struct g_part_entry *); 102214352Saestatic int g_part_gpt_modify(struct g_part_table *, struct g_part_entry *, 103166551Smarcel struct g_part_parms *); 104188429Simpstatic const char *g_part_gpt_name(struct g_part_table *, struct g_part_entry *, 105166551Smarcel char *, size_t); 106166551Smarcelstatic int g_part_gpt_probe(struct g_part_table *, struct g_consumer *); 107166551Smarcelstatic int g_part_gpt_read(struct g_part_table *, struct g_consumer *); 108213135Spjdstatic int g_part_gpt_setunset(struct g_part_table *table, 109213135Spjd struct g_part_entry *baseentry, const char *attrib, unsigned int set); 110166551Smarcelstatic const char *g_part_gpt_type(struct g_part_table *, struct g_part_entry *, 111166551Smarcel char *, size_t); 112166551Smarcelstatic int g_part_gpt_write(struct g_part_table *, struct g_consumer *); 113207094Smarcelstatic int g_part_gpt_resize(struct g_part_table *, struct g_part_entry *, 114207094Smarcel struct g_part_parms *); 115214352Saestatic int g_part_gpt_recover(struct g_part_table *); 116166551Smarcel 117166551Smarcelstatic kobj_method_t g_part_gpt_methods[] = { 118166551Smarcel KOBJMETHOD(g_part_add, g_part_gpt_add), 119178180Smarcel KOBJMETHOD(g_part_bootcode, g_part_gpt_bootcode), 120166551Smarcel KOBJMETHOD(g_part_create, g_part_gpt_create), 121166551Smarcel KOBJMETHOD(g_part_destroy, g_part_gpt_destroy), 122178444Smarcel KOBJMETHOD(g_part_dumpconf, g_part_gpt_dumpconf), 123166551Smarcel KOBJMETHOD(g_part_dumpto, g_part_gpt_dumpto), 124166551Smarcel KOBJMETHOD(g_part_modify, g_part_gpt_modify), 125207094Smarcel KOBJMETHOD(g_part_resize, g_part_gpt_resize), 126166551Smarcel KOBJMETHOD(g_part_name, g_part_gpt_name), 127166551Smarcel KOBJMETHOD(g_part_probe, g_part_gpt_probe), 128166551Smarcel KOBJMETHOD(g_part_read, g_part_gpt_read), 129214352Sae KOBJMETHOD(g_part_recover, g_part_gpt_recover), 130213135Spjd KOBJMETHOD(g_part_setunset, g_part_gpt_setunset), 131166551Smarcel KOBJMETHOD(g_part_type, g_part_gpt_type), 132166551Smarcel KOBJMETHOD(g_part_write, g_part_gpt_write), 133166551Smarcel { 0, 0 } 134166551Smarcel}; 135166551Smarcel 136166551Smarcelstatic struct g_part_scheme g_part_gpt_scheme = { 137166551Smarcel "GPT", 138166551Smarcel g_part_gpt_methods, 139166551Smarcel sizeof(struct g_part_gpt_table), 140166551Smarcel .gps_entrysz = sizeof(struct g_part_gpt_entry), 141166551Smarcel .gps_minent = 128, 142217531Sae .gps_maxent = 4096, 143178180Smarcel .gps_bootcodesz = MBRSIZE, 144166551Smarcel}; 145177510SmarcelG_PART_SCHEME_DECLARE(g_part_gpt); 146166551Smarcel 147200534Srpaulostatic struct uuid gpt_uuid_apple_boot = GPT_ENT_TYPE_APPLE_BOOT; 148182797Srpaulostatic struct uuid gpt_uuid_apple_hfs = GPT_ENT_TYPE_APPLE_HFS; 149200534Srpaulostatic struct uuid gpt_uuid_apple_label = GPT_ENT_TYPE_APPLE_LABEL; 150200534Srpaulostatic struct uuid gpt_uuid_apple_raid = GPT_ENT_TYPE_APPLE_RAID; 151200534Srpaulostatic struct uuid gpt_uuid_apple_raid_offline = GPT_ENT_TYPE_APPLE_RAID_OFFLINE; 152200534Srpaulostatic struct uuid gpt_uuid_apple_tv_recovery = GPT_ENT_TYPE_APPLE_TV_RECOVERY; 153200534Srpaulostatic struct uuid gpt_uuid_apple_ufs = GPT_ENT_TYPE_APPLE_UFS; 154218014Saestatic struct uuid gpt_uuid_bios_boot = GPT_ENT_TYPE_BIOS_BOOT; 155166551Smarcelstatic struct uuid gpt_uuid_efi = GPT_ENT_TYPE_EFI; 156166551Smarcelstatic struct uuid gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD; 157172940Sjhbstatic struct uuid gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT; 158236023Smarcelstatic struct uuid gpt_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS; 159166551Smarcelstatic struct uuid gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP; 160166551Smarcelstatic struct uuid gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; 161166551Smarcelstatic struct uuid gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM; 162172857Smarcelstatic struct uuid gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; 163200534Srpaulostatic struct uuid gpt_uuid_linux_data = GPT_ENT_TYPE_LINUX_DATA; 164200534Srpaulostatic struct uuid gpt_uuid_linux_lvm = GPT_ENT_TYPE_LINUX_LVM; 165200534Srpaulostatic struct uuid gpt_uuid_linux_raid = GPT_ENT_TYPE_LINUX_RAID; 166166551Smarcelstatic struct uuid gpt_uuid_linux_swap = GPT_ENT_TYPE_LINUX_SWAP; 167234417Smarckstatic struct uuid gpt_uuid_vmfs = GPT_ENT_TYPE_VMFS; 168234417Smarckstatic struct uuid gpt_uuid_vmkdiag = GPT_ENT_TYPE_VMKDIAG; 169234417Smarckstatic struct uuid gpt_uuid_vmreserved = GPT_ENT_TYPE_VMRESERVED; 170200539Srpaulostatic struct uuid gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA; 171200539Srpaulostatic struct uuid gpt_uuid_ms_reserved = GPT_ENT_TYPE_MS_RESERVED; 172200539Srpaulostatic struct uuid gpt_uuid_ms_ldm_data = GPT_ENT_TYPE_MS_LDM_DATA; 173200539Srpaulostatic struct uuid gpt_uuid_ms_ldm_metadata = GPT_ENT_TYPE_MS_LDM_METADATA; 174200539Srpaulostatic struct uuid gpt_uuid_netbsd_ccd = GPT_ENT_TYPE_NETBSD_CCD; 175200539Srpaulostatic struct uuid gpt_uuid_netbsd_cgd = GPT_ENT_TYPE_NETBSD_CGD; 176200539Srpaulostatic struct uuid gpt_uuid_netbsd_ffs = GPT_ENT_TYPE_NETBSD_FFS; 177200539Srpaulostatic struct uuid gpt_uuid_netbsd_lfs = GPT_ENT_TYPE_NETBSD_LFS; 178200539Srpaulostatic struct uuid gpt_uuid_netbsd_raid = GPT_ENT_TYPE_NETBSD_RAID; 179200539Srpaulostatic struct uuid gpt_uuid_netbsd_swap = GPT_ENT_TYPE_NETBSD_SWAP; 180166551Smarcelstatic struct uuid gpt_uuid_mbr = GPT_ENT_TYPE_MBR; 181166551Smarcelstatic struct uuid gpt_uuid_unused = GPT_ENT_TYPE_UNUSED; 182166551Smarcel 183200534Srpaulostatic struct g_part_uuid_alias { 184200534Srpaulo struct uuid *uuid; 185214352Sae int alias; 186226647Smarcel int mbrtype; 187200534Srpaulo} gpt_uuid_alias_match[] = { 188226647Smarcel { &gpt_uuid_apple_boot, G_PART_ALIAS_APPLE_BOOT, 0xab }, 189226647Smarcel { &gpt_uuid_apple_hfs, G_PART_ALIAS_APPLE_HFS, 0xaf }, 190226647Smarcel { &gpt_uuid_apple_label, G_PART_ALIAS_APPLE_LABEL, 0 }, 191226647Smarcel { &gpt_uuid_apple_raid, G_PART_ALIAS_APPLE_RAID, 0 }, 192226647Smarcel { &gpt_uuid_apple_raid_offline, G_PART_ALIAS_APPLE_RAID_OFFLINE, 0 }, 193226647Smarcel { &gpt_uuid_apple_tv_recovery, G_PART_ALIAS_APPLE_TV_RECOVERY, 0 }, 194226647Smarcel { &gpt_uuid_apple_ufs, G_PART_ALIAS_APPLE_UFS, 0 }, 195226647Smarcel { &gpt_uuid_bios_boot, G_PART_ALIAS_BIOS_BOOT, 0 }, 196226647Smarcel { &gpt_uuid_efi, G_PART_ALIAS_EFI, 0xee }, 197226647Smarcel { &gpt_uuid_freebsd, G_PART_ALIAS_FREEBSD, 0xa5 }, 198226647Smarcel { &gpt_uuid_freebsd_boot, G_PART_ALIAS_FREEBSD_BOOT, 0 }, 199236023Smarcel { &gpt_uuid_freebsd_nandfs, G_PART_ALIAS_FREEBSD_NANDFS, 0 }, 200226647Smarcel { &gpt_uuid_freebsd_swap, G_PART_ALIAS_FREEBSD_SWAP, 0 }, 201226647Smarcel { &gpt_uuid_freebsd_ufs, G_PART_ALIAS_FREEBSD_UFS, 0 }, 202226647Smarcel { &gpt_uuid_freebsd_vinum, G_PART_ALIAS_FREEBSD_VINUM, 0 }, 203226647Smarcel { &gpt_uuid_freebsd_zfs, G_PART_ALIAS_FREEBSD_ZFS, 0 }, 204226647Smarcel { &gpt_uuid_linux_data, G_PART_ALIAS_LINUX_DATA, 0x0b }, 205226647Smarcel { &gpt_uuid_linux_lvm, G_PART_ALIAS_LINUX_LVM, 0 }, 206226647Smarcel { &gpt_uuid_linux_raid, G_PART_ALIAS_LINUX_RAID, 0 }, 207226647Smarcel { &gpt_uuid_linux_swap, G_PART_ALIAS_LINUX_SWAP, 0 }, 208234417Smarck { &gpt_uuid_vmfs, G_PART_ALIAS_VMFS, 0 }, 209234417Smarck { &gpt_uuid_vmkdiag, G_PART_ALIAS_VMKDIAG, 0 }, 210234417Smarck { &gpt_uuid_vmreserved, G_PART_ALIAS_VMRESERVED, 0 }, 211226647Smarcel { &gpt_uuid_mbr, G_PART_ALIAS_MBR, 0 }, 212226647Smarcel { &gpt_uuid_ms_basic_data, G_PART_ALIAS_MS_BASIC_DATA, 0x0b }, 213226647Smarcel { &gpt_uuid_ms_ldm_data, G_PART_ALIAS_MS_LDM_DATA, 0 }, 214226647Smarcel { &gpt_uuid_ms_ldm_metadata, G_PART_ALIAS_MS_LDM_METADATA, 0 }, 215226647Smarcel { &gpt_uuid_ms_reserved, G_PART_ALIAS_MS_RESERVED, 0 }, 216226647Smarcel { &gpt_uuid_netbsd_ccd, G_PART_ALIAS_NETBSD_CCD, 0 }, 217226647Smarcel { &gpt_uuid_netbsd_cgd, G_PART_ALIAS_NETBSD_CGD, 0 }, 218226647Smarcel { &gpt_uuid_netbsd_ffs, G_PART_ALIAS_NETBSD_FFS, 0 }, 219226647Smarcel { &gpt_uuid_netbsd_lfs, G_PART_ALIAS_NETBSD_LFS, 0 }, 220226647Smarcel { &gpt_uuid_netbsd_raid, G_PART_ALIAS_NETBSD_RAID, 0 }, 221226647Smarcel { &gpt_uuid_netbsd_swap, G_PART_ALIAS_NETBSD_SWAP, 0 }, 222226647Smarcel { NULL, 0, 0 } 223200534Srpaulo}; 224200534Srpaulo 225226647Smarcelstatic int 226226647Smarcelgpt_write_mbr_entry(u_char *mbr, int idx, int typ, quad_t start, 227226647Smarcel quad_t end) 228226647Smarcel{ 229226647Smarcel 230226647Smarcel if (typ == 0 || start > UINT32_MAX || end > UINT32_MAX) 231226647Smarcel return (EINVAL); 232226647Smarcel 233226647Smarcel mbr += DOSPARTOFF + idx * DOSPARTSIZE; 234226647Smarcel mbr[0] = 0; 235226647Smarcel if (start == 1) { 236226647Smarcel /* 237226647Smarcel * Treat the PMBR partition specially to maximize 238226647Smarcel * interoperability with BIOSes. 239226647Smarcel */ 240226647Smarcel mbr[1] = mbr[3] = 0; 241226647Smarcel mbr[2] = 2; 242226647Smarcel } else 243226647Smarcel mbr[1] = mbr[2] = mbr[3] = 0xff; 244226647Smarcel mbr[4] = typ; 245226647Smarcel mbr[5] = mbr[6] = mbr[7] = 0xff; 246226647Smarcel le32enc(mbr + 8, (uint32_t)start); 247226647Smarcel le32enc(mbr + 12, (uint32_t)(end - start + 1)); 248226647Smarcel return (0); 249226647Smarcel} 250226647Smarcel 251226647Smarcelstatic int 252226647Smarcelgpt_map_type(struct uuid *t) 253226647Smarcel{ 254226647Smarcel struct g_part_uuid_alias *uap; 255226647Smarcel 256226647Smarcel for (uap = &gpt_uuid_alias_match[0]; uap->uuid; uap++) { 257226647Smarcel if (EQUUID(t, uap->uuid)) 258226647Smarcel return (uap->mbrtype); 259226647Smarcel } 260226647Smarcel return (0); 261226647Smarcel} 262226647Smarcel 263251588Smarcelstatic void 264251588Smarcelgpt_create_pmbr(struct g_part_gpt_table *table, struct g_provider *pp) 265251588Smarcel{ 266251588Smarcel 267251588Smarcel bzero(table->mbr + DOSPARTOFF, DOSPARTSIZE * NDOSPART); 268251588Smarcel gpt_write_mbr_entry(table->mbr, 0, 0xee, 1, 269251588Smarcel MIN(pp->mediasize / pp->sectorsize - 1, UINT32_MAX)); 270251588Smarcel le16enc(table->mbr + DOSMAGICOFFSET, DOSMAGIC); 271251588Smarcel} 272251588Smarcel 273226647Smarcel/* 274226647Smarcel * Under Boot Camp the PMBR partition (type 0xEE) doesn't cover the 275226647Smarcel * whole disk anymore. Rather, it covers the GPT table and the EFI 276226647Smarcel * system partition only. This way the HFS+ partition and any FAT 277226647Smarcel * partitions can be added to the MBR without creating an overlap. 278226647Smarcel */ 279226647Smarcelstatic int 280226647Smarcelgpt_is_bootcamp(struct g_part_gpt_table *table, const char *provname) 281226647Smarcel{ 282226647Smarcel uint8_t *p; 283226647Smarcel 284226647Smarcel p = table->mbr + DOSPARTOFF; 285226647Smarcel if (p[4] != 0xee || le32dec(p + 8) != 1) 286226647Smarcel return (0); 287226647Smarcel 288226647Smarcel p += DOSPARTSIZE; 289226647Smarcel if (p[4] != 0xaf) 290226647Smarcel return (0); 291226647Smarcel 292226647Smarcel printf("GEOM: %s: enabling Boot Camp\n", provname); 293226647Smarcel return (1); 294226647Smarcel} 295226647Smarcel 296226647Smarcelstatic void 297251588Smarcelgpt_update_bootcamp(struct g_part_table *basetable, struct g_provider *pp) 298226647Smarcel{ 299226647Smarcel struct g_part_entry *baseentry; 300226647Smarcel struct g_part_gpt_entry *entry; 301226647Smarcel struct g_part_gpt_table *table; 302226647Smarcel int bootable, error, index, slices, typ; 303226647Smarcel 304226647Smarcel table = (struct g_part_gpt_table *)basetable; 305226647Smarcel 306226647Smarcel bootable = -1; 307226647Smarcel for (index = 0; index < NDOSPART; index++) { 308226647Smarcel if (table->mbr[DOSPARTOFF + DOSPARTSIZE * index]) 309226647Smarcel bootable = index; 310226647Smarcel } 311226647Smarcel 312226647Smarcel bzero(table->mbr + DOSPARTOFF, DOSPARTSIZE * NDOSPART); 313226647Smarcel slices = 0; 314226647Smarcel LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) { 315226647Smarcel if (baseentry->gpe_deleted) 316226647Smarcel continue; 317226647Smarcel index = baseentry->gpe_index - 1; 318226647Smarcel if (index >= NDOSPART) 319226647Smarcel continue; 320226647Smarcel 321226647Smarcel entry = (struct g_part_gpt_entry *)baseentry; 322226647Smarcel 323226647Smarcel switch (index) { 324226647Smarcel case 0: /* This must be the EFI system partition. */ 325226647Smarcel if (!EQUUID(&entry->ent.ent_type, &gpt_uuid_efi)) 326226647Smarcel goto disable; 327226647Smarcel error = gpt_write_mbr_entry(table->mbr, index, 0xee, 328226647Smarcel 1ull, entry->ent.ent_lba_end); 329226647Smarcel break; 330226647Smarcel case 1: /* This must be the HFS+ partition. */ 331226647Smarcel if (!EQUUID(&entry->ent.ent_type, &gpt_uuid_apple_hfs)) 332226647Smarcel goto disable; 333226647Smarcel error = gpt_write_mbr_entry(table->mbr, index, 0xaf, 334226647Smarcel entry->ent.ent_lba_start, entry->ent.ent_lba_end); 335226647Smarcel break; 336226647Smarcel default: 337226647Smarcel typ = gpt_map_type(&entry->ent.ent_type); 338226647Smarcel error = gpt_write_mbr_entry(table->mbr, index, typ, 339226647Smarcel entry->ent.ent_lba_start, entry->ent.ent_lba_end); 340226647Smarcel break; 341226647Smarcel } 342226647Smarcel if (error) 343226647Smarcel continue; 344226647Smarcel 345226647Smarcel if (index == bootable) 346226647Smarcel table->mbr[DOSPARTOFF + DOSPARTSIZE * index] = 0x80; 347226647Smarcel slices |= 1 << index; 348226647Smarcel } 349226647Smarcel if ((slices & 3) == 3) 350226647Smarcel return; 351226647Smarcel 352226647Smarcel disable: 353226647Smarcel table->bootcamp = 0; 354251588Smarcel gpt_create_pmbr(table, pp); 355226647Smarcel} 356226647Smarcel 357199017Srnolandstatic struct gpt_hdr * 358166551Smarcelgpt_read_hdr(struct g_part_gpt_table *table, struct g_consumer *cp, 359199017Srnoland enum gpt_elt elt) 360166551Smarcel{ 361199017Srnoland struct gpt_hdr *buf, *hdr; 362166551Smarcel struct g_provider *pp; 363166551Smarcel quad_t lba, last; 364166551Smarcel int error; 365166551Smarcel uint32_t crc, sz; 366166551Smarcel 367166551Smarcel pp = cp->provider; 368166551Smarcel last = (pp->mediasize / pp->sectorsize) - 1; 369166551Smarcel table->state[elt] = GPT_STATE_MISSING; 370214352Sae /* 371214352Sae * If the primary header is valid look for secondary 372214352Sae * header in AlternateLBA, otherwise in the last medium's LBA. 373214352Sae */ 374214352Sae if (elt == GPT_ELT_SECHDR) { 375214352Sae if (table->state[GPT_ELT_PRIHDR] != GPT_STATE_OK) 376214352Sae table->lba[elt] = last; 377214352Sae } else 378214352Sae table->lba[elt] = 1; 379166551Smarcel buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, pp->sectorsize, 380166551Smarcel &error); 381166551Smarcel if (buf == NULL) 382199017Srnoland return (NULL); 383199017Srnoland hdr = NULL; 384199017Srnoland if (memcmp(buf->hdr_sig, GPT_HDR_SIG, sizeof(buf->hdr_sig)) != 0) 385199017Srnoland goto fail; 386166551Smarcel 387166551Smarcel table->state[elt] = GPT_STATE_CORRUPT; 388199017Srnoland sz = le32toh(buf->hdr_size); 389166551Smarcel if (sz < 92 || sz > pp->sectorsize) 390199017Srnoland goto fail; 391199017Srnoland 392199017Srnoland hdr = g_malloc(sz, M_WAITOK | M_ZERO); 393199017Srnoland bcopy(buf, hdr, sz); 394166551Smarcel hdr->hdr_size = sz; 395199017Srnoland 396199017Srnoland crc = le32toh(buf->hdr_crc_self); 397199017Srnoland buf->hdr_crc_self = 0; 398199017Srnoland if (crc32(buf, sz) != crc) 399199017Srnoland goto fail; 400166551Smarcel hdr->hdr_crc_self = crc; 401166551Smarcel 402166551Smarcel table->state[elt] = GPT_STATE_INVALID; 403199017Srnoland hdr->hdr_revision = le32toh(buf->hdr_revision); 404214352Sae if (hdr->hdr_revision < GPT_HDR_REVISION) 405199017Srnoland goto fail; 406199017Srnoland hdr->hdr_lba_self = le64toh(buf->hdr_lba_self); 407166551Smarcel if (hdr->hdr_lba_self != table->lba[elt]) 408199017Srnoland goto fail; 409199017Srnoland hdr->hdr_lba_alt = le64toh(buf->hdr_lba_alt); 410214352Sae if (hdr->hdr_lba_alt == hdr->hdr_lba_self || 411214352Sae hdr->hdr_lba_alt > last) 412214352Sae goto fail; 413166551Smarcel 414166551Smarcel /* Check the managed area. */ 415199017Srnoland hdr->hdr_lba_start = le64toh(buf->hdr_lba_start); 416166551Smarcel if (hdr->hdr_lba_start < 2 || hdr->hdr_lba_start >= last) 417199017Srnoland goto fail; 418199017Srnoland hdr->hdr_lba_end = le64toh(buf->hdr_lba_end); 419166551Smarcel if (hdr->hdr_lba_end < hdr->hdr_lba_start || hdr->hdr_lba_end >= last) 420199017Srnoland goto fail; 421166551Smarcel 422166551Smarcel /* Check the table location and size of the table. */ 423199017Srnoland hdr->hdr_entries = le32toh(buf->hdr_entries); 424199017Srnoland hdr->hdr_entsz = le32toh(buf->hdr_entsz); 425166551Smarcel if (hdr->hdr_entries == 0 || hdr->hdr_entsz < 128 || 426166551Smarcel (hdr->hdr_entsz & 7) != 0) 427199017Srnoland goto fail; 428199017Srnoland hdr->hdr_lba_table = le64toh(buf->hdr_lba_table); 429166551Smarcel if (hdr->hdr_lba_table < 2 || hdr->hdr_lba_table >= last) 430199017Srnoland goto fail; 431166551Smarcel if (hdr->hdr_lba_table >= hdr->hdr_lba_start && 432166551Smarcel hdr->hdr_lba_table <= hdr->hdr_lba_end) 433199017Srnoland goto fail; 434166551Smarcel lba = hdr->hdr_lba_table + 435166551Smarcel (hdr->hdr_entries * hdr->hdr_entsz + pp->sectorsize - 1) / 436166551Smarcel pp->sectorsize - 1; 437166551Smarcel if (lba >= last) 438199017Srnoland goto fail; 439166551Smarcel if (lba >= hdr->hdr_lba_start && lba <= hdr->hdr_lba_end) 440199017Srnoland goto fail; 441166551Smarcel 442166551Smarcel table->state[elt] = GPT_STATE_OK; 443199017Srnoland le_uuid_dec(&buf->hdr_uuid, &hdr->hdr_uuid); 444199017Srnoland hdr->hdr_crc_table = le32toh(buf->hdr_crc_table); 445199017Srnoland 446214352Sae /* save LBA for secondary header */ 447214352Sae if (elt == GPT_ELT_PRIHDR) 448214352Sae table->lba[GPT_ELT_SECHDR] = hdr->hdr_lba_alt; 449214352Sae 450199017Srnoland g_free(buf); 451199017Srnoland return (hdr); 452199017Srnoland 453199017Srnoland fail: 454199017Srnoland if (hdr != NULL) 455199017Srnoland g_free(hdr); 456199017Srnoland g_free(buf); 457199017Srnoland return (NULL); 458166551Smarcel} 459166551Smarcel 460166551Smarcelstatic struct gpt_ent * 461166551Smarcelgpt_read_tbl(struct g_part_gpt_table *table, struct g_consumer *cp, 462166551Smarcel enum gpt_elt elt, struct gpt_hdr *hdr) 463166551Smarcel{ 464166551Smarcel struct g_provider *pp; 465166551Smarcel struct gpt_ent *ent, *tbl; 466166551Smarcel char *buf, *p; 467217531Sae unsigned int idx, sectors, tblsz, size; 468166551Smarcel int error; 469166551Smarcel 470199017Srnoland if (hdr == NULL) 471199017Srnoland return (NULL); 472199017Srnoland 473166551Smarcel pp = cp->provider; 474166551Smarcel table->lba[elt] = hdr->hdr_lba_table; 475166551Smarcel 476166551Smarcel table->state[elt] = GPT_STATE_MISSING; 477166551Smarcel tblsz = hdr->hdr_entries * hdr->hdr_entsz; 478166551Smarcel sectors = (tblsz + pp->sectorsize - 1) / pp->sectorsize; 479217531Sae buf = g_malloc(sectors * pp->sectorsize, M_WAITOK | M_ZERO); 480217531Sae for (idx = 0; idx < sectors; idx += MAXPHYS / pp->sectorsize) { 481217531Sae size = (sectors - idx > MAXPHYS / pp->sectorsize) ? MAXPHYS: 482217531Sae (sectors - idx) * pp->sectorsize; 483217531Sae p = g_read_data(cp, (table->lba[elt] + idx) * pp->sectorsize, 484217531Sae size, &error); 485217531Sae if (p == NULL) { 486217531Sae g_free(buf); 487217531Sae return (NULL); 488217531Sae } 489217531Sae bcopy(p, buf + idx * pp->sectorsize, size); 490217531Sae g_free(p); 491217531Sae } 492166551Smarcel table->state[elt] = GPT_STATE_CORRUPT; 493166551Smarcel if (crc32(buf, tblsz) != hdr->hdr_crc_table) { 494166551Smarcel g_free(buf); 495166551Smarcel return (NULL); 496166551Smarcel } 497166551Smarcel 498166551Smarcel table->state[elt] = GPT_STATE_OK; 499166551Smarcel tbl = g_malloc(hdr->hdr_entries * sizeof(struct gpt_ent), 500166551Smarcel M_WAITOK | M_ZERO); 501166551Smarcel 502166551Smarcel for (idx = 0, ent = tbl, p = buf; 503166551Smarcel idx < hdr->hdr_entries; 504166551Smarcel idx++, ent++, p += hdr->hdr_entsz) { 505166551Smarcel le_uuid_dec(p, &ent->ent_type); 506166551Smarcel le_uuid_dec(p + 16, &ent->ent_uuid); 507166551Smarcel ent->ent_lba_start = le64dec(p + 32); 508166551Smarcel ent->ent_lba_end = le64dec(p + 40); 509166551Smarcel ent->ent_attr = le64dec(p + 48); 510179763Smarcel /* Keep UTF-16 in little-endian. */ 511179763Smarcel bcopy(p + 56, ent->ent_name, sizeof(ent->ent_name)); 512166551Smarcel } 513166551Smarcel 514166551Smarcel g_free(buf); 515166551Smarcel return (tbl); 516166551Smarcel} 517166551Smarcel 518166551Smarcelstatic int 519166551Smarcelgpt_matched_hdrs(struct gpt_hdr *pri, struct gpt_hdr *sec) 520166551Smarcel{ 521166551Smarcel 522199017Srnoland if (pri == NULL || sec == NULL) 523199017Srnoland return (0); 524199017Srnoland 525166551Smarcel if (!EQUUID(&pri->hdr_uuid, &sec->hdr_uuid)) 526166551Smarcel return (0); 527166551Smarcel return ((pri->hdr_revision == sec->hdr_revision && 528166551Smarcel pri->hdr_size == sec->hdr_size && 529166551Smarcel pri->hdr_lba_start == sec->hdr_lba_start && 530166551Smarcel pri->hdr_lba_end == sec->hdr_lba_end && 531166551Smarcel pri->hdr_entries == sec->hdr_entries && 532166551Smarcel pri->hdr_entsz == sec->hdr_entsz && 533166551Smarcel pri->hdr_crc_table == sec->hdr_crc_table) ? 1 : 0); 534166551Smarcel} 535166551Smarcel 536166551Smarcelstatic int 537166551Smarcelgpt_parse_type(const char *type, struct uuid *uuid) 538166551Smarcel{ 539166551Smarcel struct uuid tmp; 540169389Smarcel const char *alias; 541166551Smarcel int error; 542200534Srpaulo struct g_part_uuid_alias *uap; 543166551Smarcel 544169389Smarcel if (type[0] == '!') { 545169389Smarcel error = parse_uuid(type + 1, &tmp); 546166551Smarcel if (error) 547166551Smarcel return (error); 548166551Smarcel if (EQUUID(&tmp, &gpt_uuid_unused)) 549166551Smarcel return (EINVAL); 550166551Smarcel *uuid = tmp; 551166551Smarcel return (0); 552166551Smarcel } 553200534Srpaulo for (uap = &gpt_uuid_alias_match[0]; uap->uuid; uap++) { 554200534Srpaulo alias = g_part_alias_name(uap->alias); 555200534Srpaulo if (!strcasecmp(type, alias)) { 556201374Smarcel *uuid = *uap->uuid; 557200534Srpaulo return (0); 558200534Srpaulo } 559169389Smarcel } 560169389Smarcel return (EINVAL); 561166551Smarcel} 562166551Smarcel 563166551Smarcelstatic int 564166551Smarcelg_part_gpt_add(struct g_part_table *basetable, struct g_part_entry *baseentry, 565166551Smarcel struct g_part_parms *gpp) 566166551Smarcel{ 567166551Smarcel struct g_part_gpt_entry *entry; 568166551Smarcel int error; 569166551Smarcel 570166551Smarcel entry = (struct g_part_gpt_entry *)baseentry; 571166551Smarcel error = gpt_parse_type(gpp->gpp_type, &entry->ent.ent_type); 572166551Smarcel if (error) 573166551Smarcel return (error); 574166551Smarcel kern_uuidgen(&entry->ent.ent_uuid, 1); 575166551Smarcel entry->ent.ent_lba_start = baseentry->gpe_start; 576166551Smarcel entry->ent.ent_lba_end = baseentry->gpe_end; 577166551Smarcel if (baseentry->gpe_deleted) { 578166551Smarcel entry->ent.ent_attr = 0; 579166551Smarcel bzero(entry->ent.ent_name, sizeof(entry->ent.ent_name)); 580166551Smarcel } 581179763Smarcel if (gpp->gpp_parms & G_PART_PARM_LABEL) 582179763Smarcel g_gpt_utf8_to_utf16(gpp->gpp_label, entry->ent.ent_name, 583217109Smdf sizeof(entry->ent.ent_name) / 584217109Smdf sizeof(entry->ent.ent_name[0])); 585166551Smarcel return (0); 586166551Smarcel} 587166551Smarcel 588166551Smarcelstatic int 589178180Smarcelg_part_gpt_bootcode(struct g_part_table *basetable, struct g_part_parms *gpp) 590178180Smarcel{ 591178180Smarcel struct g_part_gpt_table *table; 592185497Smarcel size_t codesz; 593178180Smarcel 594185497Smarcel codesz = DOSPARTOFF; 595178180Smarcel table = (struct g_part_gpt_table *)basetable; 596185497Smarcel bzero(table->mbr, codesz); 597185497Smarcel codesz = MIN(codesz, gpp->gpp_codesize); 598185497Smarcel if (codesz > 0) 599185497Smarcel bcopy(gpp->gpp_codeptr, table->mbr, codesz); 600178180Smarcel return (0); 601178180Smarcel} 602178180Smarcel 603178180Smarcelstatic int 604166551Smarcelg_part_gpt_create(struct g_part_table *basetable, struct g_part_parms *gpp) 605166551Smarcel{ 606166551Smarcel struct g_provider *pp; 607166551Smarcel struct g_part_gpt_table *table; 608166551Smarcel size_t tblsz; 609166551Smarcel 610190534Smarcel /* We don't nest, which means that our depth should be 0. */ 611190534Smarcel if (basetable->gpt_depth != 0) 612190534Smarcel return (ENXIO); 613190534Smarcel 614166551Smarcel table = (struct g_part_gpt_table *)basetable; 615166551Smarcel pp = gpp->gpp_provider; 616166551Smarcel tblsz = (basetable->gpt_entries * sizeof(struct gpt_ent) + 617166551Smarcel pp->sectorsize - 1) / pp->sectorsize; 618178180Smarcel if (pp->sectorsize < MBRSIZE || 619166551Smarcel pp->mediasize < (3 + 2 * tblsz + basetable->gpt_entries) * 620166551Smarcel pp->sectorsize) 621166551Smarcel return (ENOSPC); 622166551Smarcel 623251588Smarcel gpt_create_pmbr(table, pp); 624251588Smarcel 625199228Srnoland /* Allocate space for the header */ 626199228Srnoland table->hdr = g_malloc(sizeof(struct gpt_hdr), M_WAITOK | M_ZERO); 627199228Srnoland 628199017Srnoland bcopy(GPT_HDR_SIG, table->hdr->hdr_sig, sizeof(table->hdr->hdr_sig)); 629199017Srnoland table->hdr->hdr_revision = GPT_HDR_REVISION; 630199017Srnoland table->hdr->hdr_size = offsetof(struct gpt_hdr, padding); 631199017Srnoland kern_uuidgen(&table->hdr->hdr_uuid, 1); 632199017Srnoland table->hdr->hdr_entries = basetable->gpt_entries; 633199017Srnoland table->hdr->hdr_entsz = sizeof(struct gpt_ent); 634166551Smarcel 635223660Sae g_gpt_set_defaults(basetable, pp); 636166551Smarcel return (0); 637166551Smarcel} 638166551Smarcel 639166551Smarcelstatic int 640166551Smarcelg_part_gpt_destroy(struct g_part_table *basetable, struct g_part_parms *gpp) 641166551Smarcel{ 642208746Smarius struct g_part_gpt_table *table; 643214352Sae struct g_provider *pp; 644166551Smarcel 645208746Smarius table = (struct g_part_gpt_table *)basetable; 646214352Sae pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider; 647214352Sae g_free(table->hdr); 648208746Smarius table->hdr = NULL; 649208746Smarius 650166551Smarcel /* 651214352Sae * Wipe the first 2 sectors to clear the partitioning. Wipe the last 652214352Sae * sector only if it has valid secondary header. 653166551Smarcel */ 654166551Smarcel basetable->gpt_smhead |= 3; 655214352Sae if (table->state[GPT_ELT_SECHDR] == GPT_STATE_OK && 656214352Sae table->lba[GPT_ELT_SECHDR] == pp->mediasize / pp->sectorsize - 1) 657214352Sae basetable->gpt_smtail |= 1; 658166551Smarcel return (0); 659166551Smarcel} 660166551Smarcel 661188429Simpstatic void 662178444Smarcelg_part_gpt_dumpconf(struct g_part_table *table, struct g_part_entry *baseentry, 663178444Smarcel struct sbuf *sb, const char *indent) 664178444Smarcel{ 665178444Smarcel struct g_part_gpt_entry *entry; 666179751Smarcel 667178444Smarcel entry = (struct g_part_gpt_entry *)baseentry; 668179751Smarcel if (indent == NULL) { 669179751Smarcel /* conftxt: libdisk compatibility */ 670179751Smarcel sbuf_printf(sb, " xs GPT xt "); 671179751Smarcel sbuf_printf_uuid(sb, &entry->ent.ent_type); 672179751Smarcel } else if (entry != NULL) { 673179751Smarcel /* confxml: partition entry information */ 674179763Smarcel sbuf_printf(sb, "%s<label>", indent); 675179763Smarcel g_gpt_printf_utf16(sb, entry->ent.ent_name, 676179763Smarcel sizeof(entry->ent.ent_name) >> 1); 677179763Smarcel sbuf_printf(sb, "</label>\n"); 678213135Spjd if (entry->ent.ent_attr & GPT_ENT_ATTR_BOOTME) 679213135Spjd sbuf_printf(sb, "%s<attrib>bootme</attrib>\n", indent); 680213135Spjd if (entry->ent.ent_attr & GPT_ENT_ATTR_BOOTONCE) { 681213135Spjd sbuf_printf(sb, "%s<attrib>bootonce</attrib>\n", 682213135Spjd indent); 683213135Spjd } 684213135Spjd if (entry->ent.ent_attr & GPT_ENT_ATTR_BOOTFAILED) { 685213135Spjd sbuf_printf(sb, "%s<attrib>bootfailed</attrib>\n", 686213135Spjd indent); 687213135Spjd } 688179751Smarcel sbuf_printf(sb, "%s<rawtype>", indent); 689179751Smarcel sbuf_printf_uuid(sb, &entry->ent.ent_type); 690179751Smarcel sbuf_printf(sb, "</rawtype>\n"); 691203261Smarcel sbuf_printf(sb, "%s<rawuuid>", indent); 692203261Smarcel sbuf_printf_uuid(sb, &entry->ent.ent_uuid); 693203261Smarcel sbuf_printf(sb, "</rawuuid>\n"); 694179751Smarcel } else { 695179751Smarcel /* confxml: scheme information */ 696179751Smarcel } 697178444Smarcel} 698178444Smarcel 699178444Smarcelstatic int 700166551Smarcelg_part_gpt_dumpto(struct g_part_table *table, struct g_part_entry *baseentry) 701166551Smarcel{ 702166551Smarcel struct g_part_gpt_entry *entry; 703166551Smarcel 704166551Smarcel entry = (struct g_part_gpt_entry *)baseentry; 705166551Smarcel return ((EQUUID(&entry->ent.ent_type, &gpt_uuid_freebsd_swap) || 706166551Smarcel EQUUID(&entry->ent.ent_type, &gpt_uuid_linux_swap)) ? 1 : 0); 707166551Smarcel} 708166551Smarcel 709166551Smarcelstatic int 710166551Smarcelg_part_gpt_modify(struct g_part_table *basetable, 711166551Smarcel struct g_part_entry *baseentry, struct g_part_parms *gpp) 712166551Smarcel{ 713166551Smarcel struct g_part_gpt_entry *entry; 714166551Smarcel int error; 715166551Smarcel 716166551Smarcel entry = (struct g_part_gpt_entry *)baseentry; 717169389Smarcel if (gpp->gpp_parms & G_PART_PARM_TYPE) { 718169389Smarcel error = gpt_parse_type(gpp->gpp_type, &entry->ent.ent_type); 719169389Smarcel if (error) 720169389Smarcel return (error); 721169389Smarcel } 722179763Smarcel if (gpp->gpp_parms & G_PART_PARM_LABEL) 723179763Smarcel g_gpt_utf8_to_utf16(gpp->gpp_label, entry->ent.ent_name, 724217109Smdf sizeof(entry->ent.ent_name) / 725217109Smdf sizeof(entry->ent.ent_name[0])); 726166551Smarcel return (0); 727166551Smarcel} 728166551Smarcel 729207094Smarcelstatic int 730207094Smarcelg_part_gpt_resize(struct g_part_table *basetable, 731207094Smarcel struct g_part_entry *baseentry, struct g_part_parms *gpp) 732207094Smarcel{ 733207094Smarcel struct g_part_gpt_entry *entry; 734207094Smarcel entry = (struct g_part_gpt_entry *)baseentry; 735207094Smarcel 736207094Smarcel baseentry->gpe_end = baseentry->gpe_start + gpp->gpp_size - 1; 737207094Smarcel entry->ent.ent_lba_end = baseentry->gpe_end; 738207094Smarcel 739207094Smarcel return (0); 740207094Smarcel} 741207094Smarcel 742188429Simpstatic const char * 743166551Smarcelg_part_gpt_name(struct g_part_table *table, struct g_part_entry *baseentry, 744166551Smarcel char *buf, size_t bufsz) 745166551Smarcel{ 746166551Smarcel struct g_part_gpt_entry *entry; 747166551Smarcel char c; 748166551Smarcel 749166551Smarcel entry = (struct g_part_gpt_entry *)baseentry; 750166551Smarcel c = (EQUUID(&entry->ent.ent_type, &gpt_uuid_freebsd)) ? 's' : 'p'; 751166551Smarcel snprintf(buf, bufsz, "%c%d", c, baseentry->gpe_index); 752166551Smarcel return (buf); 753166551Smarcel} 754166551Smarcel 755166551Smarcelstatic int 756166551Smarcelg_part_gpt_probe(struct g_part_table *table, struct g_consumer *cp) 757166551Smarcel{ 758166551Smarcel struct g_provider *pp; 759166551Smarcel char *buf; 760166551Smarcel int error, res; 761166551Smarcel 762166551Smarcel /* We don't nest, which means that our depth should be 0. */ 763166551Smarcel if (table->gpt_depth != 0) 764166551Smarcel return (ENXIO); 765166551Smarcel 766166551Smarcel pp = cp->provider; 767166551Smarcel 768166551Smarcel /* 769166551Smarcel * Sanity-check the provider. Since the first sector on the provider 770166551Smarcel * must be a PMBR and a PMBR is 512 bytes large, the sector size 771166551Smarcel * must be at least 512 bytes. Also, since the theoretical minimum 772166551Smarcel * number of sectors needed by GPT is 6, any medium that has less 773166551Smarcel * than 6 sectors is never going to be able to hold a GPT. The 774166551Smarcel * number 6 comes from: 775166551Smarcel * 1 sector for the PMBR 776166551Smarcel * 2 sectors for the GPT headers (each 1 sector) 777166551Smarcel * 2 sectors for the GPT tables (each 1 sector) 778166551Smarcel * 1 sector for an actual partition 779166551Smarcel * It's better to catch this pathological case early than behaving 780166551Smarcel * pathologically later on... 781166551Smarcel */ 782178180Smarcel if (pp->sectorsize < MBRSIZE || pp->mediasize < 6 * pp->sectorsize) 783166551Smarcel return (ENOSPC); 784166551Smarcel 785166551Smarcel /* Check that there's a MBR. */ 786166551Smarcel buf = g_read_data(cp, 0L, pp->sectorsize, &error); 787166551Smarcel if (buf == NULL) 788166551Smarcel return (error); 789166551Smarcel res = le16dec(buf + DOSMAGICOFFSET); 790166551Smarcel g_free(buf); 791166551Smarcel if (res != DOSMAGIC) 792166551Smarcel return (ENXIO); 793166551Smarcel 794166551Smarcel /* Check that there's a primary header. */ 795166551Smarcel buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error); 796166551Smarcel if (buf == NULL) 797166551Smarcel return (error); 798166551Smarcel res = memcmp(buf, GPT_HDR_SIG, 8); 799166551Smarcel g_free(buf); 800166551Smarcel if (res == 0) 801166551Smarcel return (G_PART_PROBE_PRI_HIGH); 802166551Smarcel 803166551Smarcel /* No primary? Check that there's a secondary. */ 804166551Smarcel buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 805166551Smarcel &error); 806166551Smarcel if (buf == NULL) 807166551Smarcel return (error); 808166551Smarcel res = memcmp(buf, GPT_HDR_SIG, 8); 809166551Smarcel g_free(buf); 810166551Smarcel return ((res == 0) ? G_PART_PROBE_PRI_HIGH : ENXIO); 811166551Smarcel} 812166551Smarcel 813166551Smarcelstatic int 814166551Smarcelg_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp) 815166551Smarcel{ 816199017Srnoland struct gpt_hdr *prihdr, *sechdr; 817166551Smarcel struct gpt_ent *tbl, *pritbl, *sectbl; 818166551Smarcel struct g_provider *pp; 819166551Smarcel struct g_part_gpt_table *table; 820166551Smarcel struct g_part_gpt_entry *entry; 821178180Smarcel u_char *buf; 822214352Sae uint64_t last; 823178180Smarcel int error, index; 824166551Smarcel 825166551Smarcel table = (struct g_part_gpt_table *)basetable; 826166551Smarcel pp = cp->provider; 827214352Sae last = (pp->mediasize / pp->sectorsize) - 1; 828166551Smarcel 829178180Smarcel /* Read the PMBR */ 830178180Smarcel buf = g_read_data(cp, 0, pp->sectorsize, &error); 831178180Smarcel if (buf == NULL) 832178180Smarcel return (error); 833178180Smarcel bcopy(buf, table->mbr, MBRSIZE); 834178180Smarcel g_free(buf); 835178180Smarcel 836166551Smarcel /* Read the primary header and table. */ 837199017Srnoland prihdr = gpt_read_hdr(table, cp, GPT_ELT_PRIHDR); 838166551Smarcel if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK) { 839199017Srnoland pritbl = gpt_read_tbl(table, cp, GPT_ELT_PRITBL, prihdr); 840166551Smarcel } else { 841166551Smarcel table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING; 842166551Smarcel pritbl = NULL; 843166551Smarcel } 844166551Smarcel 845166551Smarcel /* Read the secondary header and table. */ 846199017Srnoland sechdr = gpt_read_hdr(table, cp, GPT_ELT_SECHDR); 847166551Smarcel if (table->state[GPT_ELT_SECHDR] == GPT_STATE_OK) { 848199017Srnoland sectbl = gpt_read_tbl(table, cp, GPT_ELT_SECTBL, sechdr); 849166551Smarcel } else { 850166551Smarcel table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING; 851166551Smarcel sectbl = NULL; 852166551Smarcel } 853166551Smarcel 854166551Smarcel /* Fail if we haven't got any good tables at all. */ 855166551Smarcel if (table->state[GPT_ELT_PRITBL] != GPT_STATE_OK && 856166551Smarcel table->state[GPT_ELT_SECTBL] != GPT_STATE_OK) { 857166551Smarcel printf("GEOM: %s: corrupt or invalid GPT detected.\n", 858166551Smarcel pp->name); 859166551Smarcel printf("GEOM: %s: GPT rejected -- may not be recoverable.\n", 860166551Smarcel pp->name); 861166551Smarcel return (EINVAL); 862166551Smarcel } 863166551Smarcel 864166551Smarcel /* 865166551Smarcel * If both headers are good but they disagree with each other, 866166551Smarcel * then invalidate one. We prefer to keep the primary header, 867166551Smarcel * unless the primary table is corrupt. 868166551Smarcel */ 869166551Smarcel if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK && 870166551Smarcel table->state[GPT_ELT_SECHDR] == GPT_STATE_OK && 871199017Srnoland !gpt_matched_hdrs(prihdr, sechdr)) { 872184734Smarcel if (table->state[GPT_ELT_PRITBL] == GPT_STATE_OK) { 873166551Smarcel table->state[GPT_ELT_SECHDR] = GPT_STATE_INVALID; 874184734Smarcel table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING; 875199017Srnoland g_free(sechdr); 876199017Srnoland sechdr = NULL; 877184734Smarcel } else { 878166551Smarcel table->state[GPT_ELT_PRIHDR] = GPT_STATE_INVALID; 879184734Smarcel table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING; 880199017Srnoland g_free(prihdr); 881199017Srnoland prihdr = NULL; 882184734Smarcel } 883166551Smarcel } 884166551Smarcel 885184734Smarcel if (table->state[GPT_ELT_PRITBL] != GPT_STATE_OK) { 886166551Smarcel printf("GEOM: %s: the primary GPT table is corrupt or " 887166551Smarcel "invalid.\n", pp->name); 888166551Smarcel printf("GEOM: %s: using the secondary instead -- recovery " 889166551Smarcel "strongly advised.\n", pp->name); 890166551Smarcel table->hdr = sechdr; 891214352Sae basetable->gpt_corrupt = 1; 892199017Srnoland if (prihdr != NULL) 893199017Srnoland g_free(prihdr); 894166551Smarcel tbl = sectbl; 895166551Smarcel if (pritbl != NULL) 896166551Smarcel g_free(pritbl); 897166551Smarcel } else { 898184734Smarcel if (table->state[GPT_ELT_SECTBL] != GPT_STATE_OK) { 899166551Smarcel printf("GEOM: %s: the secondary GPT table is corrupt " 900166551Smarcel "or invalid.\n", pp->name); 901166551Smarcel printf("GEOM: %s: using the primary only -- recovery " 902166551Smarcel "suggested.\n", pp->name); 903214352Sae basetable->gpt_corrupt = 1; 904214352Sae } else if (table->lba[GPT_ELT_SECHDR] != last) { 905214352Sae printf( "GEOM: %s: the secondary GPT header is not in " 906214352Sae "the last LBA.\n", pp->name); 907214352Sae basetable->gpt_corrupt = 1; 908166551Smarcel } 909166551Smarcel table->hdr = prihdr; 910199017Srnoland if (sechdr != NULL) 911199017Srnoland g_free(sechdr); 912166551Smarcel tbl = pritbl; 913166551Smarcel if (sectbl != NULL) 914166551Smarcel g_free(sectbl); 915166551Smarcel } 916166551Smarcel 917199017Srnoland basetable->gpt_first = table->hdr->hdr_lba_start; 918199017Srnoland basetable->gpt_last = table->hdr->hdr_lba_end; 919254095Sae basetable->gpt_entries = (table->hdr->hdr_lba_start - 2) * 920254095Sae pp->sectorsize / table->hdr->hdr_entsz; 921166551Smarcel 922254095Sae for (index = table->hdr->hdr_entries - 1; index >= 0; index--) { 923166551Smarcel if (EQUUID(&tbl[index].ent_type, &gpt_uuid_unused)) 924166551Smarcel continue; 925214352Sae entry = (struct g_part_gpt_entry *)g_part_new_entry( 926214352Sae basetable, index + 1, tbl[index].ent_lba_start, 927214352Sae tbl[index].ent_lba_end); 928166551Smarcel entry->ent = tbl[index]; 929166551Smarcel } 930166551Smarcel 931166551Smarcel g_free(tbl); 932226647Smarcel 933226647Smarcel /* 934226647Smarcel * Under Mac OS X, the MBR mirrors the first 4 GPT partitions 935226647Smarcel * if (and only if) any FAT32 or FAT16 partitions have been 936226647Smarcel * created. This happens irrespective of whether Boot Camp is 937226647Smarcel * used/enabled, though it's generally understood to be done 938226647Smarcel * to support legacy Windows under Boot Camp. We refer to this 939226647Smarcel * mirroring simply as Boot Camp. We try to detect Boot Camp 940226647Smarcel * so that we can update the MBR if and when GPT changes have 941226647Smarcel * been made. Note that we do not enable Boot Camp if not 942226647Smarcel * previously enabled because we can't assume that we're on a 943226647Smarcel * Mac alongside Mac OS X. 944226647Smarcel */ 945226647Smarcel table->bootcamp = gpt_is_bootcamp(table, pp->name); 946226647Smarcel 947166551Smarcel return (0); 948166551Smarcel} 949166551Smarcel 950213135Spjdstatic int 951214352Saeg_part_gpt_recover(struct g_part_table *basetable) 952214352Sae{ 953251588Smarcel struct g_part_gpt_table *table; 954251588Smarcel struct g_provider *pp; 955214352Sae 956251588Smarcel table = (struct g_part_gpt_table *)basetable; 957251588Smarcel pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider; 958251588Smarcel gpt_create_pmbr(table, pp); 959251588Smarcel g_gpt_set_defaults(basetable, pp); 960214352Sae basetable->gpt_corrupt = 0; 961214352Sae return (0); 962214352Sae} 963214352Sae 964214352Saestatic int 965226647Smarcelg_part_gpt_setunset(struct g_part_table *basetable, 966226647Smarcel struct g_part_entry *baseentry, const char *attrib, unsigned int set) 967213135Spjd{ 968213135Spjd struct g_part_gpt_entry *entry; 969226647Smarcel struct g_part_gpt_table *table; 970251588Smarcel uint8_t *p; 971226647Smarcel uint64_t attr; 972226647Smarcel int i; 973213135Spjd 974226647Smarcel table = (struct g_part_gpt_table *)basetable; 975226647Smarcel entry = (struct g_part_gpt_entry *)baseentry; 976226647Smarcel 977226647Smarcel if (strcasecmp(attrib, "active") == 0) { 978251588Smarcel if (table->bootcamp) { 979251588Smarcel /* The active flag must be set on a valid entry. */ 980251588Smarcel if (entry == NULL) 981251588Smarcel return (ENXIO); 982251588Smarcel if (baseentry->gpe_index > NDOSPART) 983251588Smarcel return (EINVAL); 984251588Smarcel for (i = 0; i < NDOSPART; i++) { 985251588Smarcel p = &table->mbr[DOSPARTOFF + i * DOSPARTSIZE]; 986251588Smarcel p[0] = (i == baseentry->gpe_index - 1) 987251588Smarcel ? ((set) ? 0x80 : 0) : 0; 988251588Smarcel } 989251588Smarcel } else { 990251588Smarcel /* The PMBR is marked as active without an entry. */ 991251588Smarcel if (entry != NULL) 992251588Smarcel return (ENXIO); 993251588Smarcel for (i = 0; i < NDOSPART; i++) { 994251588Smarcel p = &table->mbr[DOSPARTOFF + i * DOSPARTSIZE]; 995251588Smarcel p[0] = (p[4] == 0xee) ? ((set) ? 0x80 : 0) : 0; 996251588Smarcel } 997226647Smarcel } 998226647Smarcel return (0); 999226647Smarcel } 1000226647Smarcel 1001251588Smarcel if (entry == NULL) 1002251588Smarcel return (ENODEV); 1003251588Smarcel 1004226647Smarcel attr = 0; 1005213135Spjd if (strcasecmp(attrib, "bootme") == 0) { 1006226647Smarcel attr |= GPT_ENT_ATTR_BOOTME; 1007213135Spjd } else if (strcasecmp(attrib, "bootonce") == 0) { 1008226647Smarcel attr |= GPT_ENT_ATTR_BOOTONCE; 1009213135Spjd if (set) 1010226647Smarcel attr |= GPT_ENT_ATTR_BOOTME; 1011213135Spjd } else if (strcasecmp(attrib, "bootfailed") == 0) { 1012213135Spjd /* 1013213135Spjd * It should only be possible to unset BOOTFAILED, but it might 1014213135Spjd * be useful for test purposes to also be able to set it. 1015213135Spjd */ 1016226647Smarcel attr |= GPT_ENT_ATTR_BOOTFAILED; 1017213135Spjd } 1018226647Smarcel if (attr == 0) 1019213135Spjd return (EINVAL); 1020213135Spjd 1021226647Smarcel if (set) 1022226647Smarcel attr = entry->ent.ent_attr | attr; 1023226647Smarcel else 1024226647Smarcel attr = entry->ent.ent_attr & ~attr; 1025226647Smarcel if (attr != entry->ent.ent_attr) { 1026226647Smarcel entry->ent.ent_attr = attr; 1027226647Smarcel if (!baseentry->gpe_created) 1028226647Smarcel baseentry->gpe_modified = 1; 1029213135Spjd } 1030213135Spjd return (0); 1031213135Spjd} 1032213135Spjd 1033166551Smarcelstatic const char * 1034166551Smarcelg_part_gpt_type(struct g_part_table *basetable, struct g_part_entry *baseentry, 1035166551Smarcel char *buf, size_t bufsz) 1036166551Smarcel{ 1037166551Smarcel struct g_part_gpt_entry *entry; 1038166551Smarcel struct uuid *type; 1039200534Srpaulo struct g_part_uuid_alias *uap; 1040166551Smarcel 1041166551Smarcel entry = (struct g_part_gpt_entry *)baseentry; 1042166551Smarcel type = &entry->ent.ent_type; 1043200534Srpaulo for (uap = &gpt_uuid_alias_match[0]; uap->uuid; uap++) 1044200534Srpaulo if (EQUUID(type, uap->uuid)) 1045200534Srpaulo return (g_part_alias_name(uap->alias)); 1046170362Smarcel buf[0] = '!'; 1047170362Smarcel snprintf_uuid(buf + 1, bufsz - 1, type); 1048200534Srpaulo 1049166551Smarcel return (buf); 1050166551Smarcel} 1051166551Smarcel 1052166551Smarcelstatic int 1053166551Smarcelg_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp) 1054166551Smarcel{ 1055166551Smarcel unsigned char *buf, *bp; 1056166551Smarcel struct g_provider *pp; 1057166551Smarcel struct g_part_entry *baseentry; 1058166551Smarcel struct g_part_gpt_entry *entry; 1059166551Smarcel struct g_part_gpt_table *table; 1060214352Sae size_t tblsz; 1061166551Smarcel uint32_t crc; 1062166551Smarcel int error, index; 1063166551Smarcel 1064166551Smarcel pp = cp->provider; 1065166551Smarcel table = (struct g_part_gpt_table *)basetable; 1066214352Sae tblsz = (table->hdr->hdr_entries * table->hdr->hdr_entsz + 1067166551Smarcel pp->sectorsize - 1) / pp->sectorsize; 1068166551Smarcel 1069226647Smarcel /* Reconstruct the MBR from the GPT if under Boot Camp. */ 1070226647Smarcel if (table->bootcamp) 1071251588Smarcel gpt_update_bootcamp(basetable, pp); 1072226647Smarcel 1073178180Smarcel /* Write the PMBR */ 1074178180Smarcel buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 1075178180Smarcel bcopy(table->mbr, buf, MBRSIZE); 1076178180Smarcel error = g_write_data(cp, 0, buf, pp->sectorsize); 1077178180Smarcel g_free(buf); 1078178180Smarcel if (error) 1079178180Smarcel return (error); 1080166551Smarcel 1081166551Smarcel /* Allocate space for the header and entries. */ 1082214352Sae buf = g_malloc((tblsz + 1) * pp->sectorsize, M_WAITOK | M_ZERO); 1083166551Smarcel 1084199017Srnoland memcpy(buf, table->hdr->hdr_sig, sizeof(table->hdr->hdr_sig)); 1085199017Srnoland le32enc(buf + 8, table->hdr->hdr_revision); 1086199017Srnoland le32enc(buf + 12, table->hdr->hdr_size); 1087199017Srnoland le64enc(buf + 40, table->hdr->hdr_lba_start); 1088199017Srnoland le64enc(buf + 48, table->hdr->hdr_lba_end); 1089199017Srnoland le_uuid_enc(buf + 56, &table->hdr->hdr_uuid); 1090199017Srnoland le32enc(buf + 80, table->hdr->hdr_entries); 1091199017Srnoland le32enc(buf + 84, table->hdr->hdr_entsz); 1092166551Smarcel 1093166551Smarcel LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) { 1094169389Smarcel if (baseentry->gpe_deleted) 1095169389Smarcel continue; 1096166551Smarcel entry = (struct g_part_gpt_entry *)baseentry; 1097166551Smarcel index = baseentry->gpe_index - 1; 1098199017Srnoland bp = buf + pp->sectorsize + table->hdr->hdr_entsz * index; 1099166551Smarcel le_uuid_enc(bp, &entry->ent.ent_type); 1100166551Smarcel le_uuid_enc(bp + 16, &entry->ent.ent_uuid); 1101166551Smarcel le64enc(bp + 32, entry->ent.ent_lba_start); 1102166551Smarcel le64enc(bp + 40, entry->ent.ent_lba_end); 1103166551Smarcel le64enc(bp + 48, entry->ent.ent_attr); 1104166551Smarcel memcpy(bp + 56, entry->ent.ent_name, 1105166551Smarcel sizeof(entry->ent.ent_name)); 1106166551Smarcel } 1107166551Smarcel 1108166551Smarcel crc = crc32(buf + pp->sectorsize, 1109199017Srnoland table->hdr->hdr_entries * table->hdr->hdr_entsz); 1110166551Smarcel le32enc(buf + 88, crc); 1111166551Smarcel 1112166551Smarcel /* Write primary meta-data. */ 1113166551Smarcel le32enc(buf + 16, 0); /* hdr_crc_self. */ 1114166551Smarcel le64enc(buf + 24, table->lba[GPT_ELT_PRIHDR]); /* hdr_lba_self. */ 1115166551Smarcel le64enc(buf + 32, table->lba[GPT_ELT_SECHDR]); /* hdr_lba_alt. */ 1116166551Smarcel le64enc(buf + 72, table->lba[GPT_ELT_PRITBL]); /* hdr_lba_table. */ 1117199017Srnoland crc = crc32(buf, table->hdr->hdr_size); 1118166551Smarcel le32enc(buf + 16, crc); 1119166551Smarcel 1120217531Sae for (index = 0; index < tblsz; index += MAXPHYS / pp->sectorsize) { 1121217531Sae error = g_write_data(cp, 1122217531Sae (table->lba[GPT_ELT_PRITBL] + index) * pp->sectorsize, 1123217531Sae buf + (index + 1) * pp->sectorsize, 1124217531Sae (tblsz - index > MAXPHYS / pp->sectorsize) ? MAXPHYS: 1125217531Sae (tblsz - index) * pp->sectorsize); 1126217531Sae if (error) 1127217531Sae goto out; 1128217531Sae } 1129166551Smarcel error = g_write_data(cp, table->lba[GPT_ELT_PRIHDR] * pp->sectorsize, 1130166551Smarcel buf, pp->sectorsize); 1131166551Smarcel if (error) 1132166551Smarcel goto out; 1133166551Smarcel 1134166551Smarcel /* Write secondary meta-data. */ 1135166551Smarcel le32enc(buf + 16, 0); /* hdr_crc_self. */ 1136166551Smarcel le64enc(buf + 24, table->lba[GPT_ELT_SECHDR]); /* hdr_lba_self. */ 1137166551Smarcel le64enc(buf + 32, table->lba[GPT_ELT_PRIHDR]); /* hdr_lba_alt. */ 1138166551Smarcel le64enc(buf + 72, table->lba[GPT_ELT_SECTBL]); /* hdr_lba_table. */ 1139199017Srnoland crc = crc32(buf, table->hdr->hdr_size); 1140166551Smarcel le32enc(buf + 16, crc); 1141166551Smarcel 1142217531Sae for (index = 0; index < tblsz; index += MAXPHYS / pp->sectorsize) { 1143217531Sae error = g_write_data(cp, 1144217531Sae (table->lba[GPT_ELT_SECTBL] + index) * pp->sectorsize, 1145217531Sae buf + (index + 1) * pp->sectorsize, 1146217531Sae (tblsz - index > MAXPHYS / pp->sectorsize) ? MAXPHYS: 1147217531Sae (tblsz - index) * pp->sectorsize); 1148217531Sae if (error) 1149217531Sae goto out; 1150217531Sae } 1151166551Smarcel error = g_write_data(cp, table->lba[GPT_ELT_SECHDR] * pp->sectorsize, 1152166551Smarcel buf, pp->sectorsize); 1153166551Smarcel 1154166551Smarcel out: 1155166551Smarcel g_free(buf); 1156166551Smarcel return (error); 1157166551Smarcel} 1158166551Smarcel 1159166551Smarcelstatic void 1160223660Saeg_gpt_set_defaults(struct g_part_table *basetable, struct g_provider *pp) 1161223660Sae{ 1162223660Sae struct g_part_gpt_table *table; 1163223660Sae quad_t last; 1164223660Sae size_t tblsz; 1165223660Sae 1166223660Sae table = (struct g_part_gpt_table *)basetable; 1167223660Sae last = pp->mediasize / pp->sectorsize - 1; 1168223660Sae tblsz = (basetable->gpt_entries * sizeof(struct gpt_ent) + 1169223660Sae pp->sectorsize - 1) / pp->sectorsize; 1170223660Sae 1171223660Sae table->lba[GPT_ELT_PRIHDR] = 1; 1172223660Sae table->lba[GPT_ELT_PRITBL] = 2; 1173223660Sae table->lba[GPT_ELT_SECHDR] = last; 1174223660Sae table->lba[GPT_ELT_SECTBL] = last - tblsz; 1175223660Sae table->state[GPT_ELT_PRIHDR] = GPT_STATE_OK; 1176223660Sae table->state[GPT_ELT_PRITBL] = GPT_STATE_OK; 1177223660Sae table->state[GPT_ELT_SECHDR] = GPT_STATE_OK; 1178223660Sae table->state[GPT_ELT_SECTBL] = GPT_STATE_OK; 1179223660Sae 1180223660Sae table->hdr->hdr_lba_start = 2 + tblsz; 1181223660Sae table->hdr->hdr_lba_end = last - tblsz - 1; 1182223660Sae 1183223660Sae basetable->gpt_first = table->hdr->hdr_lba_start; 1184223660Sae basetable->gpt_last = table->hdr->hdr_lba_end; 1185223660Sae} 1186223660Sae 1187223660Saestatic void 1188179763Smarcelg_gpt_printf_utf16(struct sbuf *sb, uint16_t *str, size_t len) 1189166551Smarcel{ 1190166551Smarcel u_int bo; 1191166551Smarcel uint32_t ch; 1192166551Smarcel uint16_t c; 1193166551Smarcel 1194179763Smarcel bo = LITTLE_ENDIAN; /* GPT is little-endian */ 1195166551Smarcel while (len > 0 && *str != 0) { 1196166551Smarcel ch = (bo == BIG_ENDIAN) ? be16toh(*str) : le16toh(*str); 1197166551Smarcel str++, len--; 1198166551Smarcel if ((ch & 0xf800) == 0xd800) { 1199166551Smarcel if (len > 0) { 1200166551Smarcel c = (bo == BIG_ENDIAN) ? be16toh(*str) 1201166551Smarcel : le16toh(*str); 1202166551Smarcel str++, len--; 1203166551Smarcel } else 1204166551Smarcel c = 0xfffd; 1205166551Smarcel if ((ch & 0x400) == 0 && (c & 0xfc00) == 0xdc00) { 1206166551Smarcel ch = ((ch & 0x3ff) << 10) + (c & 0x3ff); 1207166551Smarcel ch += 0x10000; 1208166551Smarcel } else 1209166551Smarcel ch = 0xfffd; 1210166551Smarcel } else if (ch == 0xfffe) { /* BOM (U+FEFF) swapped. */ 1211166551Smarcel bo = (bo == BIG_ENDIAN) ? LITTLE_ENDIAN : BIG_ENDIAN; 1212166551Smarcel continue; 1213166551Smarcel } else if (ch == 0xfeff) /* BOM (U+FEFF) unswapped. */ 1214166551Smarcel continue; 1215166551Smarcel 1216179763Smarcel /* Write the Unicode character in UTF-8 */ 1217166551Smarcel if (ch < 0x80) 1218166551Smarcel sbuf_printf(sb, "%c", ch); 1219166551Smarcel else if (ch < 0x800) 1220166551Smarcel sbuf_printf(sb, "%c%c", 0xc0 | (ch >> 6), 1221166551Smarcel 0x80 | (ch & 0x3f)); 1222166551Smarcel else if (ch < 0x10000) 1223166551Smarcel sbuf_printf(sb, "%c%c%c", 0xe0 | (ch >> 12), 1224166551Smarcel 0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f)); 1225166551Smarcel else if (ch < 0x200000) 1226166551Smarcel sbuf_printf(sb, "%c%c%c%c", 0xf0 | (ch >> 18), 1227166551Smarcel 0x80 | ((ch >> 12) & 0x3f), 1228166551Smarcel 0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f)); 1229166551Smarcel } 1230166551Smarcel} 1231179763Smarcel 1232179763Smarcelstatic void 1233179763Smarcelg_gpt_utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len) 1234179763Smarcel{ 1235179763Smarcel size_t s16idx, s8idx; 1236179763Smarcel uint32_t utfchar; 1237179763Smarcel unsigned int c, utfbytes; 1238179763Smarcel 1239179763Smarcel s8idx = s16idx = 0; 1240179763Smarcel utfchar = 0; 1241179763Smarcel utfbytes = 0; 1242179763Smarcel bzero(s16, s16len << 1); 1243179763Smarcel while (s8[s8idx] != 0 && s16idx < s16len) { 1244179763Smarcel c = s8[s8idx++]; 1245179763Smarcel if ((c & 0xc0) != 0x80) { 1246179763Smarcel /* Initial characters. */ 1247179763Smarcel if (utfbytes != 0) { 1248179763Smarcel /* Incomplete encoding of previous char. */ 1249179763Smarcel s16[s16idx++] = htole16(0xfffd); 1250179763Smarcel } 1251179763Smarcel if ((c & 0xf8) == 0xf0) { 1252179763Smarcel utfchar = c & 0x07; 1253179763Smarcel utfbytes = 3; 1254179763Smarcel } else if ((c & 0xf0) == 0xe0) { 1255179763Smarcel utfchar = c & 0x0f; 1256179763Smarcel utfbytes = 2; 1257179763Smarcel } else if ((c & 0xe0) == 0xc0) { 1258179763Smarcel utfchar = c & 0x1f; 1259179763Smarcel utfbytes = 1; 1260179763Smarcel } else { 1261179763Smarcel utfchar = c & 0x7f; 1262179763Smarcel utfbytes = 0; 1263179763Smarcel } 1264179763Smarcel } else { 1265179763Smarcel /* Followup characters. */ 1266179763Smarcel if (utfbytes > 0) { 1267179763Smarcel utfchar = (utfchar << 6) + (c & 0x3f); 1268179763Smarcel utfbytes--; 1269179763Smarcel } else if (utfbytes == 0) 1270179763Smarcel utfbytes = ~0; 1271179763Smarcel } 1272179763Smarcel /* 1273179763Smarcel * Write the complete Unicode character as UTF-16 when we 1274179763Smarcel * have all the UTF-8 charactars collected. 1275179763Smarcel */ 1276179763Smarcel if (utfbytes == 0) { 1277179763Smarcel /* 1278179763Smarcel * If we need to write 2 UTF-16 characters, but 1279179763Smarcel * we only have room for 1, then we truncate the 1280179763Smarcel * string by writing a 0 instead. 1281179763Smarcel */ 1282179763Smarcel if (utfchar >= 0x10000 && s16idx < s16len - 1) { 1283179763Smarcel s16[s16idx++] = 1284179763Smarcel htole16(0xd800 | ((utfchar >> 10) - 0x40)); 1285179763Smarcel s16[s16idx++] = 1286179763Smarcel htole16(0xdc00 | (utfchar & 0x3ff)); 1287179763Smarcel } else 1288179763Smarcel s16[s16idx++] = (utfchar >= 0x10000) ? 0 : 1289179763Smarcel htole16(utfchar); 1290179763Smarcel } 1291179763Smarcel } 1292179763Smarcel /* 1293179763Smarcel * If our input string was truncated, append an invalid encoding 1294179763Smarcel * character to the output string. 1295179763Smarcel */ 1296179763Smarcel if (utfbytes != 0 && s16idx < s16len) 1297179763Smarcel s16[s16idx++] = htole16(0xfffd); 1298179763Smarcel} 1299