]> git.proxmox.com Git - mirror_zfs-debian.git/blobdiff - lib/libefi/rdwr_efi.c
New upstream version 0.7.9
[mirror_zfs-debian.git] / lib / libefi / rdwr_efi.c
index 0600a9523ebce03f4f37dbd0bb0169dbc59cbf0c..7935047ebfcd811dcb002344ec5a9ed51396933b 100644 (file)
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #include <stdio.h>
@@ -69,7 +70,72 @@ static struct uuid_to_ptag {
        { EFI_DELL_LVM },
        { EFI_DELL_RESV },
        { EFI_AAPL_HFS },
-       { EFI_AAPL_UFS }
+       { EFI_AAPL_UFS },
+       { EFI_FREEBSD_BOOT },
+       { EFI_FREEBSD_SWAP },
+       { EFI_FREEBSD_UFS },
+       { EFI_FREEBSD_VINUM },
+       { EFI_FREEBSD_ZFS },
+       { EFI_BIOS_BOOT },
+       { EFI_INTC_RS },
+       { EFI_SNE_BOOT },
+       { EFI_LENOVO_BOOT },
+       { EFI_MSFT_LDMM },
+       { EFI_MSFT_LDMD },
+       { EFI_MSFT_RE },
+       { EFI_IBM_GPFS },
+       { EFI_MSFT_STORAGESPACES },
+       { EFI_HPQ_DATA },
+       { EFI_HPQ_SVC },
+       { EFI_RHT_DATA },
+       { EFI_RHT_HOME },
+       { EFI_RHT_SRV },
+       { EFI_RHT_DMCRYPT },
+       { EFI_RHT_LUKS },
+       { EFI_FREEBSD_DISKLABEL },
+       { EFI_AAPL_RAID },
+       { EFI_AAPL_RAIDOFFLINE },
+       { EFI_AAPL_BOOT },
+       { EFI_AAPL_LABEL },
+       { EFI_AAPL_TVRECOVERY },
+       { EFI_AAPL_CORESTORAGE },
+       { EFI_NETBSD_SWAP },
+       { EFI_NETBSD_FFS },
+       { EFI_NETBSD_LFS },
+       { EFI_NETBSD_RAID },
+       { EFI_NETBSD_CAT },
+       { EFI_NETBSD_CRYPT },
+       { EFI_GOOG_KERN },
+       { EFI_GOOG_ROOT },
+       { EFI_GOOG_RESV },
+       { EFI_HAIKU_BFS },
+       { EFI_MIDNIGHTBSD_BOOT },
+       { EFI_MIDNIGHTBSD_DATA },
+       { EFI_MIDNIGHTBSD_SWAP },
+       { EFI_MIDNIGHTBSD_UFS },
+       { EFI_MIDNIGHTBSD_VINUM },
+       { EFI_MIDNIGHTBSD_ZFS },
+       { EFI_CEPH_JOURNAL },
+       { EFI_CEPH_DMCRYPTJOURNAL },
+       { EFI_CEPH_OSD },
+       { EFI_CEPH_DMCRYPTOSD },
+       { EFI_CEPH_CREATE },
+       { EFI_CEPH_DMCRYPTCREATE },
+       { EFI_OPENBSD_DISKLABEL },
+       { EFI_BBRY_QNX },
+       { EFI_BELL_PLAN9 },
+       { EFI_VMW_KCORE },
+       { EFI_VMW_VMFS },
+       { EFI_VMW_RESV },
+       { EFI_RHT_ROOTX86 },
+       { EFI_RHT_ROOTAMD64 },
+       { EFI_RHT_ROOTARM },
+       { EFI_RHT_ROOTARM64 },
+       { EFI_ACRONIS_SECUREZONE },
+       { EFI_ONIE_BOOT },
+       { EFI_ONIE_CONFIG },
+       { EFI_IBM_PPRPBOOT },
+       { EFI_FREEDESKTOP_BOOT }
 };
 
 /*
@@ -87,7 +153,9 @@ struct dk_map2  default_vtoc_map[NDKMAP] = {
 
 #if defined(_SUNOS_VTOC_16)
 
-#if defined(i386) || defined(__amd64) || defined(__arm)
+#if defined(i386) || defined(__amd64) || defined(__arm) || \
+    defined(__powerpc) || defined(__sparc) || defined(__s390__) || \
+    defined(__mips__)
        {       V_BOOT,         V_UNMNT },              /* i - 8 */
        {       V_ALTSCTR,      0       },              /* j - 9 */
 
@@ -104,11 +172,7 @@ struct dk_map2  default_vtoc_map[NDKMAP] = {
 #endif                 /* defined(_SUNOS_VTOC_16) */
 };
 
-#ifdef DEBUG
-int efi_debug = 1;
-#else
 int efi_debug = 0;
-#endif
 
 static int efi_read(int, struct dk_gpt *);
 
@@ -132,8 +196,8 @@ read_disk_info(int fd, diskaddr_t *capacity, uint_t *lbsize)
        int sector_size;
        unsigned long long capacity_size;
 
-        if (ioctl(fd, BLKSSZGET, &sector_size) < 0)
-                return (-1);
+       if (ioctl(fd, BLKSSZGET, &sector_size) < 0)
+               return (-1);
 
        if (ioctl(fd, BLKGETSIZE64, &capacity_size) < 0)
                return (-1);
@@ -152,7 +216,7 @@ efi_get_info(int fd, struct dk_cinfo *dki_info)
        char *dev_path;
        int rval = 0;
 
-       memset(dki_info, 0, sizeof(*dki_info));
+       memset(dki_info, 0, sizeof (*dki_info));
 
        path = calloc(PATH_MAX, 1);
        if (path == NULL)
@@ -182,44 +246,74 @@ efi_get_info(int fd, struct dk_cinfo *dki_info)
                strcpy(dki_info->dki_cname, "sd");
                dki_info->dki_ctype = DKC_SCSI_CCS;
                rval = sscanf(dev_path, "/dev/%[a-zA-Z]%hu",
-                             dki_info->dki_dname,
-                             &dki_info->dki_partition);
+                   dki_info->dki_dname,
+                   &dki_info->dki_partition);
        } else if ((strncmp(dev_path, "/dev/hd", 7) == 0)) {
                strcpy(dki_info->dki_cname, "hd");
                dki_info->dki_ctype = DKC_DIRECT;
                rval = sscanf(dev_path, "/dev/%[a-zA-Z]%hu",
-                             dki_info->dki_dname,
-                             &dki_info->dki_partition);
+                   dki_info->dki_dname,
+                   &dki_info->dki_partition);
        } else if ((strncmp(dev_path, "/dev/md", 7) == 0)) {
                strcpy(dki_info->dki_cname, "pseudo");
                dki_info->dki_ctype = DKC_MD;
-               rval = sscanf(dev_path, "/dev/%[a-zA-Z0-9]p%hu",
-                             dki_info->dki_dname,
-                             &dki_info->dki_partition);
+               strcpy(dki_info->dki_dname, "md");
+               rval = sscanf(dev_path, "/dev/md%[0-9]p%hu",
+                   dki_info->dki_dname + 2,
+                   &dki_info->dki_partition);
        } else if ((strncmp(dev_path, "/dev/vd", 7) == 0)) {
                strcpy(dki_info->dki_cname, "vd");
                dki_info->dki_ctype = DKC_MD;
                rval = sscanf(dev_path, "/dev/%[a-zA-Z]%hu",
-                             dki_info->dki_dname,
-                             &dki_info->dki_partition);
+                   dki_info->dki_dname,
+                   &dki_info->dki_partition);
+       } else if ((strncmp(dev_path, "/dev/xvd", 8) == 0)) {
+               strcpy(dki_info->dki_cname, "xvd");
+               dki_info->dki_ctype = DKC_MD;
+               rval = sscanf(dev_path, "/dev/%[a-zA-Z]%hu",
+                   dki_info->dki_dname,
+                   &dki_info->dki_partition);
+       } else if ((strncmp(dev_path, "/dev/zd", 7) == 0)) {
+               strcpy(dki_info->dki_cname, "zd");
+               dki_info->dki_ctype = DKC_MD;
+               rval = sscanf(dev_path, "/dev/%[a-zA-Z]%hu",
+                   dki_info->dki_dname,
+                   &dki_info->dki_partition);
        } else if ((strncmp(dev_path, "/dev/dm-", 8) == 0)) {
                strcpy(dki_info->dki_cname, "pseudo");
                dki_info->dki_ctype = DKC_VBD;
-               rval = sscanf(dev_path, "/dev/%[a-zA-Z0-9-]p%hu",
-                             dki_info->dki_dname,
-                             &dki_info->dki_partition);
+               strcpy(dki_info->dki_dname, "dm-");
+               rval = sscanf(dev_path, "/dev/dm-%[0-9]p%hu",
+                   dki_info->dki_dname + 3,
+                   &dki_info->dki_partition);
        } else if ((strncmp(dev_path, "/dev/ram", 8) == 0)) {
                strcpy(dki_info->dki_cname, "pseudo");
                dki_info->dki_ctype = DKC_PCMCIA_MEM;
-               rval = sscanf(dev_path, "/dev/%[a-zA-Z0-9]p%hu",
-                             dki_info->dki_dname,
-                             &dki_info->dki_partition);
+               strcpy(dki_info->dki_dname, "ram");
+               rval = sscanf(dev_path, "/dev/ram%[0-9]p%hu",
+                   dki_info->dki_dname + 3,
+                   &dki_info->dki_partition);
        } else if ((strncmp(dev_path, "/dev/loop", 9) == 0)) {
                strcpy(dki_info->dki_cname, "pseudo");
                dki_info->dki_ctype = DKC_VBD;
-               rval = sscanf(dev_path, "/dev/%[a-zA-Z0-9]p%hu",
-                             dki_info->dki_dname,
-                             &dki_info->dki_partition);
+               strcpy(dki_info->dki_dname, "loop");
+               rval = sscanf(dev_path, "/dev/loop%[0-9]p%hu",
+                   dki_info->dki_dname + 4,
+                   &dki_info->dki_partition);
+       } else if ((strncmp(dev_path, "/dev/nvme", 9) == 0)) {
+               strcpy(dki_info->dki_cname, "nvme");
+               dki_info->dki_ctype = DKC_SCSI_CCS;
+               strcpy(dki_info->dki_dname, "nvme");
+               (void) sscanf(dev_path, "/dev/nvme%[0-9]",
+                   dki_info->dki_dname + 4);
+               size_t controller_length = strlen(
+                   dki_info->dki_dname);
+               strcpy(dki_info->dki_dname + controller_length,
+                   "n");
+               rval = sscanf(dev_path,
+                   "/dev/nvme%*[0-9]n%[0-9]p%hu",
+                   dki_info->dki_dname + controller_length + 1,
+                   &dki_info->dki_partition);
        } else {
                strcpy(dki_info->dki_dname, "unknown");
                strcpy(dki_info->dki_cname, "unknown");
@@ -395,10 +489,10 @@ efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc)
         */
        if (read_disk_info(fd, &capacity, &lbsize) == -1) {
                if (efi_debug)
-                       fprintf(stderr,"unable to read disk info: %d",errno);
+                       fprintf(stderr, "unable to read disk info: %d", errno);
 
                errno = EIO;
-               return -1;
+               return (-1);
        }
 
        switch (cmd) {
@@ -406,7 +500,7 @@ efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc)
                if (lbsize == 0) {
                        if (efi_debug)
                                (void) fprintf(stderr, "DKIOCGETEFI assuming "
-                                              "LBA %d bytes\n", DEV_BSIZE);
+                                   "LBA %d bytes\n", DEV_BSIZE);
 
                        lbsize = DEV_BSIZE;
                }
@@ -415,24 +509,24 @@ efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc)
                if (error == -1) {
                        if (efi_debug)
                                (void) fprintf(stderr, "DKIOCGETEFI lseek "
-                                              "error: %d\n", errno);
-                       return error;
+                                   "error: %d\n", errno);
+                       return (error);
                }
 
                error = read(fd, data, dk_ioc->dki_length);
                if (error == -1) {
                        if (efi_debug)
                                (void) fprintf(stderr, "DKIOCGETEFI read "
-                                              "error: %d\n", errno);
-                       return error;
+                                   "error: %d\n", errno);
+                       return (error);
                }
 
                if (error != dk_ioc->dki_length) {
                        if (efi_debug)
                                (void) fprintf(stderr, "DKIOCGETEFI short "
-                                              "read of %d bytes\n", error);
+                                   "read of %d bytes\n", error);
                        errno = EIO;
-                       return -1;
+                       return (-1);
                }
                error = 0;
                break;
@@ -441,43 +535,43 @@ efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc)
                if (lbsize == 0) {
                        if (efi_debug)
                                (void) fprintf(stderr, "DKIOCSETEFI unknown "
-                                              "LBA size\n");
+                                   "LBA size\n");
                        errno = EIO;
-                       return -1;
+                       return (-1);
                }
 
                error = lseek(fd, dk_ioc->dki_lba * lbsize, SEEK_SET);
                if (error == -1) {
                        if (efi_debug)
                                (void) fprintf(stderr, "DKIOCSETEFI lseek "
-                                              "error: %d\n", errno);
-                       return error;
+                                   "error: %d\n", errno);
+                       return (error);
                }
 
                error = write(fd, data, dk_ioc->dki_length);
                if (error == -1) {
                        if (efi_debug)
                                (void) fprintf(stderr, "DKIOCSETEFI write "
-                                              "error: %d\n", errno);
-                       return error;
+                                   "error: %d\n", errno);
+                       return (error);
                }
 
                if (error != dk_ioc->dki_length) {
                        if (efi_debug)
                                (void) fprintf(stderr, "DKIOCSETEFI short "
-                                              "write of %d bytes\n", error);
+                                   "write of %d bytes\n", error);
                        errno = EIO;
-                       return -1;
+                       return (-1);
                }
 
                /* Sync the new EFI table to disk */
                error = fsync(fd);
                if (error == -1)
-                       return error;
+                       return (error);
 
                /* Ensure any local disk cache is also flushed */
                if (ioctl(fd, BLKFLSBUF, 0) == -1)
-                       return error;
+                       return (error);
 
                error = 0;
                break;
@@ -487,7 +581,7 @@ efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc)
                        (void) fprintf(stderr, "unsupported ioctl()\n");
 
                errno = EIO;
-               return -1;
+               return (-1);
        }
 #else
        dk_ioc->dki_data_64 = (uint64_t)(uintptr_t)data;
@@ -497,25 +591,26 @@ efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc)
        return (error);
 }
 
-#if defined(__linux__)
-static int
+int
 efi_rescan(int fd)
 {
-       int retry = 5;
+#if defined(__linux__)
+       int retry = 10;
        int error;
 
        /* Notify the kernel a devices partition table has been updated */
        while ((error = ioctl(fd, BLKRRPART)) != 0) {
-               if (--retry == 0) {
+               if ((--retry == 0) || (errno != EBUSY)) {
                        (void) fprintf(stderr, "the kernel failed to rescan "
-                                      "the partition table: %d\n", errno);
+                           "the partition table: %d\n", errno);
                        return (-1);
                }
+               usleep(50000);
        }
+#endif
 
        return (0);
 }
-#endif
 
 static int
 check_label(int fd, dk_efi_t *dk_ioc)
@@ -549,11 +644,11 @@ check_label(int fd, dk_efi_t *dk_ioc)
        efi->efi_gpt_HeaderCRC32 = 0;
        len_t headerSize = (len_t)LE_32(efi->efi_gpt_HeaderSize);
 
-       if(headerSize < EFI_MIN_LABEL_SIZE || headerSize > EFI_LABEL_SIZE) {
+       if (headerSize < EFI_MIN_LABEL_SIZE || headerSize > EFI_LABEL_SIZE) {
                if (efi_debug)
                        (void) fprintf(stderr,
-                               "Invalid EFI HeaderSize %llu.  Assuming %d.\n",
-                               headerSize, EFI_MIN_LABEL_SIZE);
+                           "Invalid EFI HeaderSize %llu.  Assuming %d.\n",
+                           headerSize, EFI_MIN_LABEL_SIZE);
        }
 
        if ((headerSize > dk_ioc->dki_length) ||
@@ -591,7 +686,7 @@ efi_read(int fd, struct dk_gpt *vtoc)
         * get the partition number for this file descriptor.
         */
        if ((rval = efi_get_info(fd, &dki_info)) != 0)
-               return rval;
+               return (rval);
 
        if ((strncmp(dki_info.dki_cname, "pseudo", 7) == 0) &&
            (strncmp(dki_info.dki_dname, "md", 3) == 0)) {
@@ -609,8 +704,8 @@ efi_read(int fd, struct dk_gpt *vtoc)
        if (read_disk_info(fd, &capacity, &lbsize) == -1) {
                if (efi_debug) {
                        (void) fprintf(stderr,
-                                      "unable to read disk info: %d",
-                                      errno);
+                           "unable to read disk info: %d",
+                           errno);
                }
                return (VT_EINVAL);
        }
@@ -643,7 +738,7 @@ efi_read(int fd, struct dk_gpt *vtoc)
        }
 
        if (posix_memalign((void **)&dk_ioc.dki_data,
-                          disk_info.dki_lbsize, label_len))
+           disk_info.dki_lbsize, label_len))
                return (VT_ERROR);
 
        memset(dk_ioc.dki_data, 0, label_len);
@@ -855,11 +950,11 @@ write_pmbr(int fd, struct dk_gpt *vtoc)
        /* LINTED -- always longlong aligned */
        dk_ioc.dki_data = (efi_gpt_t *)buf;
        if (efi_ioctl(fd, DKIOCGETEFI, &dk_ioc) == -1) {
-               (void *) memcpy(&mb, buf, sizeof (mb));
+               (void) memcpy(&mb, buf, sizeof (mb));
                bzero(&mb, sizeof (mb));
                mb.signature = LE_16(MBB_MAGIC);
        } else {
-               (void *) memcpy(&mb, buf, sizeof (mb));
+               (void) memcpy(&mb, buf, sizeof (mb));
                if (mb.signature != LE_16(MBB_MAGIC)) {
                        bzero(&mb, sizeof (mb));
                        mb.signature = LE_16(MBB_MAGIC);
@@ -899,7 +994,7 @@ write_pmbr(int fd, struct dk_gpt *vtoc)
                *cp++ = 0xff;
        }
 
-       (void *) memcpy(buf, &mb, sizeof (mb));
+       (void) memcpy(buf, &mb, sizeof (mb));
        /* LINTED -- always longlong aligned */
        dk_ioc.dki_data = (efi_gpt_t *)buf;
        dk_ioc.dki_lba = 0;
@@ -1025,27 +1120,20 @@ check_input(struct dk_gpt *vtoc)
 int
 efi_use_whole_disk(int fd)
 {
-       struct dk_gpt           *efi_label;
+       struct dk_gpt           *efi_label = NULL;
        int                     rval;
        int                     i;
-       uint_t                  phy_last_slice = 0;
-       diskaddr_t              pl_start = 0;
-       diskaddr_t              pl_size;
+       uint_t                  resv_index = 0, data_index = 0;
+       diskaddr_t              resv_start = 0, data_start = 0;
+       diskaddr_t              difference;
 
        rval = efi_alloc_and_read(fd, &efi_label);
        if (rval < 0) {
+               if (efi_label != NULL)
+                       efi_free(efi_label);
                return (rval);
        }
 
-       /* find the last physically non-zero partition */
-       for (i = 0; i < efi_label->efi_nparts - 2; i ++) {
-               if (pl_start < efi_label->efi_parts[i].p_start) {
-                       pl_start = efi_label->efi_parts[i].p_start;
-                       phy_last_slice = i;
-               }
-       }
-       pl_size = efi_label->efi_parts[phy_last_slice].p_size;
-
        /*
         * If alter_lba is 1, we are using the backup label.
         * Since we can locate the backup label by disk capacity,
@@ -1061,16 +1149,28 @@ efi_use_whole_disk(int fd)
                return (VT_ENOSPC);
        }
 
+       difference = efi_label->efi_last_lba - efi_label->efi_altern_lba;
+
+       /*
+        * Find the last physically non-zero partition.
+        * This is the reserved partition.
+        */
+       for (i = 0; i < efi_label->efi_nparts; i ++) {
+               if (resv_start < efi_label->efi_parts[i].p_start) {
+                       resv_start = efi_label->efi_parts[i].p_start;
+                       resv_index = i;
+               }
+       }
+
        /*
-        * If there is space between the last physically non-zero partition
-        * and the reserved partition, just add the unallocated space to this
-        * area. Otherwise, the unallocated space is added to the last
-        * physically non-zero partition.
+        * Find the last physically non-zero partition before that.
+        * This is the data partition.
         */
-       if (pl_start + pl_size - 1 == efi_label->efi_last_u_lba -
-           EFI_MIN_RESV_SIZE) {
-               efi_label->efi_parts[phy_last_slice].p_size +=
-                   efi_label->efi_last_lba - efi_label->efi_altern_lba;
+       for (i = 0; i < resv_index; i ++) {
+               if (data_start < efi_label->efi_parts[i].p_start) {
+                       data_start = efi_label->efi_parts[i].p_start;
+                       data_index = i;
+               }
        }
 
        /*
@@ -1078,10 +1178,9 @@ efi_use_whole_disk(int fd)
         * here except fabricated devids (which get generated via
         * efi_write()). So there is no need to copy data.
         */
-       efi_label->efi_parts[efi_label->efi_nparts - 1].p_start +=
-           efi_label->efi_last_lba - efi_label->efi_altern_lba;
-       efi_label->efi_last_u_lba += efi_label->efi_last_lba
-           - efi_label->efi_altern_lba;
+       efi_label->efi_parts[data_index].p_size += difference;
+       efi_label->efi_parts[resv_index].p_start += difference;
+       efi_label->efi_last_u_lba += difference;
 
        rval = efi_write(fd, efi_label);
        if (rval < 0) {
@@ -1116,7 +1215,7 @@ efi_write(int fd, struct dk_gpt *vtoc)
        diskaddr_t              lba_backup_gpt_hdr;
 
        if ((rval = efi_get_info(fd, &dki_info)) != 0)
-               return rval;
+               return (rval);
 
        /* check if we are dealing wih a metadevice */
        if ((strncmp(dki_info.dki_cname, "pseudo", 7) == 0) &&
@@ -1155,7 +1254,7 @@ efi_write(int fd, struct dk_gpt *vtoc)
         */
        lba_backup_gpt_hdr = vtoc->efi_last_u_lba + 1 + nblocks;
        if (posix_memalign((void **)&dk_ioc.dki_data,
-                          vtoc->efi_lbasize, dk_ioc.dki_length))
+           vtoc->efi_lbasize, dk_ioc.dki_length))
                return (VT_ERROR);
 
        memset(dk_ioc.dki_data, 0, dk_ioc.dki_length);
@@ -1302,12 +1401,6 @@ efi_write(int fd, struct dk_gpt *vtoc)
        (void) write_pmbr(fd, vtoc);
        free(dk_ioc.dki_data);
 
-#if defined(__linux__)
-       rval = efi_rescan(fd);
-       if (rval)
-               return (VT_ERROR);
-#endif
-
        return (0);
 }