Illumos #3306, #3321
[zfs.git] / lib / libefi / rdwr_efi.c
index da71e34..f4cf417 100644 (file)
@@ -87,7 +87,7 @@ struct dk_map2  default_vtoc_map[NDKMAP] = {
 
 #if defined(_SUNOS_VTOC_16)
 
-#if defined(i386) || defined(__amd64)
+#if defined(i386) || defined(__amd64) || defined(__arm) || defined(__powerpc)
        {       V_BOOT,         V_UNMNT },              /* i - 8 */
        {       V_ALTSCTR,      0       },              /* j - 9 */
 
@@ -196,6 +196,12 @@ efi_get_info(int fd, struct dk_cinfo *dki_info)
                rval = sscanf(dev_path, "/dev/%[a-zA-Z0-9]p%hu",
                              dki_info->dki_dname,
                              &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);
        } else if ((strncmp(dev_path, "/dev/dm-", 8) == 0)) {
                strcpy(dki_info->dki_cname, "pseudo");
                dki_info->dki_ctype = DKC_VBD;
@@ -269,19 +275,12 @@ efi_alloc_and_init(int fd, uint32_t nparts, struct dk_gpt **vtoc)
        struct uuid     uuid;
        struct dk_cinfo dki_info;
 
-       if (read_disk_info(fd, &capacity, &lbsize) != 0) {
-               if (efi_debug)
-                       (void) fprintf(stderr,
-                           "couldn't read disk information\n");
+       if (read_disk_info(fd, &capacity, &lbsize) != 0)
                return (-1);
-       }
+
 #if defined(__linux__)
-       if (efi_get_info(fd, &dki_info) != 0) {
-               if (efi_debug)
-                       (void) fprintf(stderr,
-                           "couldn't read disk information\n");
+       if (efi_get_info(fd, &dki_info) != 0)
                return (-1);
-       }
 
        if (dki_info.dki_partition != 0)
                return (-1);
@@ -498,10 +497,9 @@ efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc)
        return (error);
 }
 
-#if defined(__linux__)
-static int
-efi_rescan(int fd)
+int efi_rescan(int fd)
 {
+#if defined(__linux__)
        int retry = 5;
        int error;
 
@@ -513,10 +511,10 @@ efi_rescan(int fd)
                        return (-1);
                }
        }
+#endif
 
        return (0);
 }
-#endif
 
 static int
 check_label(int fd, dk_efi_t *dk_ioc)
@@ -548,16 +546,22 @@ check_label(int fd, dk_efi_t *dk_ioc)
         */
        crc = efi->efi_gpt_HeaderCRC32;
        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 (efi_debug)
+                       (void) fprintf(stderr,
+                               "Invalid EFI HeaderSize %llu.  Assuming %d.\n",
+                               headerSize, EFI_MIN_LABEL_SIZE);
+       }
 
-       if (((len_t)LE_32(efi->efi_gpt_HeaderSize) > dk_ioc->dki_length) ||
-           crc != LE_32(efi_crc32((unsigned char *)efi,
-           LE_32(efi->efi_gpt_HeaderSize)))) {
+       if ((headerSize > dk_ioc->dki_length) ||
+           crc != LE_32(efi_crc32((unsigned char *)efi, headerSize))) {
                if (efi_debug)
                        (void) fprintf(stderr,
                            "Bad EFI CRC: 0x%x != 0x%x\n",
-                           crc,
-                           LE_32(efi_crc32((unsigned char *)efi,
-                           sizeof (struct efi_gpt))));
+                           crc, LE_32(efi_crc32((unsigned char *)efi,
+                           headerSize)));
                return (VT_EINVAL);
        }
 
@@ -1023,24 +1027,15 @@ efi_use_whole_disk(int fd)
        struct dk_gpt           *efi_label;
        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) {
                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,
@@ -1056,16 +1051,28 @@ efi_use_whole_disk(int fd)
                return (VT_ENOSPC);
        }
 
+       difference = efi_label->efi_last_lba - efi_label->efi_altern_lba;
+
        /*
-        * 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.
+        * This is the reserved 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 < 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;
+               }
+       }
+
+       /*
+        * Find the last physically non-zero partition before that.
+        * This is the data partition.
+        */
+       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;
+               }
        }
 
        /*
@@ -1073,10 +1080,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) {
@@ -1159,7 +1165,7 @@ efi_write(int fd, struct dk_gpt *vtoc)
        /* stuff user's input into EFI struct */
        efi->efi_gpt_Signature = LE_64(EFI_SIGNATURE);
        efi->efi_gpt_Revision = LE_32(vtoc->efi_version); /* 0x02000100 */
-       efi->efi_gpt_HeaderSize = LE_32(sizeof (struct efi_gpt));
+       efi->efi_gpt_HeaderSize = LE_32(sizeof (struct efi_gpt) - LEN_EFI_PAD);
        efi->efi_gpt_Reserved1 = 0;
        efi->efi_gpt_MyLBA = LE_64(1ULL);
        efi->efi_gpt_AlternateLBA = LE_64(lba_backup_gpt_hdr);
@@ -1228,7 +1234,8 @@ efi_write(int fd, struct dk_gpt *vtoc)
            LE_32(efi_crc32((unsigned char *)efi_parts,
            vtoc->efi_nparts * (int)sizeof (struct efi_gpe)));
        efi->efi_gpt_HeaderCRC32 =
-           LE_32(efi_crc32((unsigned char *)efi, sizeof (struct efi_gpt)));
+           LE_32(efi_crc32((unsigned char *)efi,
+           LE_32(efi->efi_gpt_HeaderSize)));
 
        if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) {
                free(dk_ioc.dki_data);
@@ -1281,7 +1288,7 @@ efi_write(int fd, struct dk_gpt *vtoc)
        efi->efi_gpt_HeaderCRC32 = 0;
        efi->efi_gpt_HeaderCRC32 =
            LE_32(efi_crc32((unsigned char *)dk_ioc.dki_data,
-           sizeof (struct efi_gpt)));
+           LE_32(efi->efi_gpt_HeaderSize)));
 
        if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) {
                if (efi_debug) {
@@ -1296,12 +1303,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);
 }