]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
mdadm/tests: disable selinux
[thirdparty/mdadm.git] / super-intel.c
index 4d82af3dff4f68674c3073abdafb05263c689fce..0287a6184047d5f139f8fe80dfa56002fca1fce5 100644 (file)
 #define HAVE_STDINT_H 1
 #include "mdadm.h"
 #include "mdmon.h"
+#include "dlink.h"
 #include "sha1.h"
 #include "platform-intel.h"
 #include <values.h>
 #include <scsi/sg.h>
 #include <ctype.h>
 #include <dirent.h>
+#include "drive_encryption.h"
 
 /* MPB == Metadata Parameter Block */
 #define MPB_SIGNATURE "Intel Raid ISM Cfg Sig. "
 #define MPB_SIG_LEN (strlen(MPB_SIGNATURE))
-#define MPB_VERSION_RAID0 "1.0.00"
-#define MPB_VERSION_RAID1 "1.1.00"
-#define MPB_VERSION_MANY_VOLUMES_PER_ARRAY "1.2.00"
-#define MPB_VERSION_3OR4_DISK_ARRAY "1.2.01"
-#define MPB_VERSION_RAID5 "1.2.02"
-#define MPB_VERSION_5OR6_DISK_ARRAY "1.2.04"
-#define MPB_VERSION_CNG "1.2.06"
+
+/* Legacy IMSM versions:
+ * MPB_VERSION_RAID0 1.0.00
+ * MPB_VERSION_RAID1 1.1.00
+ * MPB_VERSION_MANY_VOLUMES_PER_ARRAY 1.2.00
+ * MPB_VERSION_3OR4_DISK_ARRAY 1.2.01
+ * MPB_VERSION_RAID5 1.2.02
+ * MPB_VERSION_5OR6_DISK_ARRAY 1.2.04
+ * MPB_VERSION_CNG 1.2.06
+ */
+
 #define MPB_VERSION_ATTRIBS "1.3.00"
+#define MPB_VERSION_ATTRIBS_JD "2.0.00"
 #define MAX_SIGNATURE_LENGTH  32
 #define MAX_RAID_SERIAL_LEN   16
 
@@ -55,6 +62,8 @@
 #define MPB_ATTRIB_RAIDCNG             __cpu_to_le32(0x00000020)
 /* supports expanded stripe sizes of  256K, 512K and 1MB */
 #define MPB_ATTRIB_EXP_STRIPE_SIZE     __cpu_to_le32(0x00000040)
+/* supports RAID10 with more than 4 drives */
+#define MPB_ATTRIB_RAID10_EXT          __cpu_to_le32(0x00000080)
 
 /* The OROM Support RST Caching of Volumes */
 #define MPB_ATTRIB_NVM                 __cpu_to_le32(0x02000000)
@@ -82,6 +91,7 @@
                                        MPB_ATTRIB_RAID10          | \
                                        MPB_ATTRIB_RAID5           | \
                                        MPB_ATTRIB_EXP_STRIPE_SIZE | \
+                                       MPB_ATTRIB_RAID10_EXT      | \
                                        MPB_ATTRIB_BBM)
 
 /* Define attributes that are unused but not harmful */
@@ -164,7 +174,8 @@ struct imsm_map {
        __u8  raid_level;
 #define IMSM_T_RAID0 0
 #define IMSM_T_RAID1 1
-#define IMSM_T_RAID5 5         /* since metadata version 1.2.02 ? */
+#define IMSM_T_RAID5 5
+#define IMSM_T_RAID10 10
        __u8  num_members;      /* number of member disks */
        __u8  num_domains;      /* number of parity domains */
        __u8  failed_disk_num;  /* valid only when state is degraded */
@@ -392,8 +403,6 @@ struct md_list {
        struct md_list *next;
 };
 
-#define pr_vrb(fmt, arg...) (void) (verbose && pr_err(fmt, ##arg))
-
 static __u8 migr_type(struct imsm_dev *dev)
 {
        if (dev->vol.migr_type == MIGR_VERIFY &&
@@ -498,8 +507,15 @@ struct intel_disk {
        struct intel_disk *next;
 };
 
+/**
+ * struct extent - reserved space details.
+ * @start: start offset.
+ * @size: size of reservation, set to 0 for metadata reservation.
+ * @vol: index of the volume, meaningful if &size is set.
+ */
 struct extent {
        unsigned long long start, size;
+       int vol;
 };
 
 /* definitions of reshape process types */
@@ -507,6 +523,7 @@ enum imsm_reshape_type {
        CH_TAKEOVER,
        CH_MIGRATION,
        CH_ARRAY_SIZE,
+       CH_ABORT
 };
 
 /* definition of messages passed to imsm_process_update */
@@ -626,9 +643,53 @@ static const char *_sys_dev_type[] = {
        [SYS_DEV_SAS] = "SAS",
        [SYS_DEV_SATA] = "SATA",
        [SYS_DEV_NVME] = "NVMe",
-       [SYS_DEV_VMD] = "VMD"
+       [SYS_DEV_VMD] = "VMD",
+       [SYS_DEV_SATA_VMD] = "SATA VMD"
 };
 
+static int no_platform = -1;
+
+static int check_no_platform(void)
+{
+       static const char search[] = "mdadm.imsm.test=1";
+       FILE *fp;
+
+       if (no_platform >= 0)
+               return no_platform;
+
+       if (check_env("IMSM_NO_PLATFORM")) {
+               no_platform = 1;
+               return 1;
+       }
+       fp = fopen("/proc/cmdline", "r");
+       if (fp) {
+               char *l = conf_line(fp);
+               char *w = l;
+
+               if (l == NULL) {
+                       fclose(fp);
+                       return 0;
+               }
+
+               do {
+                       if (strcmp(w, search) == 0)
+                               no_platform = 1;
+                       w = dl_next(w);
+               } while (w != l);
+               free_line(l);
+               fclose(fp);
+               if (no_platform >= 0)
+                       return no_platform;
+       }
+       no_platform = 0;
+       return 0;
+}
+
+void imsm_set_no_platform(int v)
+{
+       no_platform = v;
+}
+
 const char *get_sys_dev_type(enum sys_dev_type type)
 {
        if (type >= SYS_DEV_MAX)
@@ -713,12 +774,12 @@ static struct sys_dev* find_disk_attached_hba(int fd, const char *devname)
 
        for (elem = list; elem; elem = elem->next)
                if (path_attached_to_hba(disk_path, elem->path))
-                       return elem;
+                       break;
 
        if (disk_path != devname)
                free(disk_path);
 
-       return NULL;
+       return elem;
 }
 
 static int find_intel_hba_capability(int fd, struct intel_super *super,
@@ -1208,14 +1269,42 @@ static int get_imsm_disk_slot(struct imsm_map *map, const unsigned int idx)
 
        return IMSM_STATUS_ERROR;
 }
+/**
+ * update_imsm_raid_level() - update raid level appropriately in &imsm_map.
+ * @map:       &imsm_map pointer.
+ * @new_level: MD style level.
+ *
+ * For backward compatibility reasons we need to differentiate RAID10.
+ * In the past IMSM RAID10 was presented as RAID1.
+ * Keep compatibility unless it is not explicitly updated by UEFI driver.
+ *
+ * Routine needs num_members to be set and (optionally) raid_level.
+ */
+static void update_imsm_raid_level(struct imsm_map *map, int new_level)
+{
+       if (new_level != IMSM_T_RAID10) {
+               map->raid_level = new_level;
+               return;
+       }
+
+       if (map->num_members == 4) {
+               if (map->raid_level == IMSM_T_RAID10 || map->raid_level == IMSM_T_RAID1)
+                       return;
+
+               map->raid_level = IMSM_T_RAID1;
+               return;
+       }
+
+       map->raid_level = IMSM_T_RAID10;
+}
 
 static int get_imsm_raid_level(struct imsm_map *map)
 {
-       if (map->raid_level == 1) {
+       if (map->raid_level == IMSM_T_RAID1) {
                if (map->num_members == 2)
-                       return 1;
+                       return IMSM_T_RAID1;
                else
-                       return 10;
+                       return IMSM_T_RAID10;
        }
 
        return map->raid_level;
@@ -1494,9 +1583,10 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl,
                                  int get_minimal_reservation)
 {
        /* find a list of used extents on the given physical device */
-       struct extent *rv, *e;
-       int i;
        int memberships = count_memberships(dl, super);
+       struct extent *rv = xcalloc(memberships + 1, sizeof(struct extent));
+       struct extent *e = rv;
+       int i;
        __u32 reservation;
 
        /* trim the reserved area for spares, so they can join any array
@@ -1508,9 +1598,6 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl,
        else
                reservation = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
 
-       rv = xcalloc(sizeof(struct extent), (memberships + 1));
-       e = rv;
-
        for (i = 0; i < super->anchor->num_raid_devs; i++) {
                struct imsm_dev *dev = get_imsm_dev(super, i);
                struct imsm_map *map = get_imsm_map(dev, MAP_0);
@@ -1518,6 +1605,7 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl,
                if (get_imsm_disk_slot(map, dl->index) >= 0) {
                        e->start = pba_of_lba0(map);
                        e->size = per_dev_array_size(map);
+                       e->vol = i;
                        e++;
                }
        }
@@ -1599,17 +1687,29 @@ static int is_journal(struct imsm_disk *disk)
        return (disk->status & JOURNAL_DISK) == JOURNAL_DISK;
 }
 
-/* round array size down to closest MB and ensure it splits evenly
- * between members
+/**
+ * round_member_size_to_mb()- Round given size to closest MiB.
+ * @size: size to round in sectors.
  */
-static unsigned long long round_size_to_mb(unsigned long long size, unsigned int
-                                          disk_count)
+static inline unsigned long long round_member_size_to_mb(unsigned long long size)
 {
-       size /= disk_count;
-       size = (size >> SECT_PER_MB_SHIFT) << SECT_PER_MB_SHIFT;
-       size *= disk_count;
+       return (size >> SECT_PER_MB_SHIFT) << SECT_PER_MB_SHIFT;
+}
 
-       return size;
+/**
+ * round_size_to_mb()- Round given size.
+ * @array_size: size to round in sectors.
+ * @disk_count: count of data members.
+ *
+ * Get size per each data member and round it to closest MiB to ensure that data
+ * splits evenly between members.
+ *
+ * Return: Array size, rounded down.
+ */
+static inline unsigned long long round_size_to_mb(unsigned long long array_size,
+                                                 unsigned int disk_count)
+{
+       return round_member_size_to_mb(array_size / disk_count) * disk_count;
 }
 
 static int able_to_resync(int raid_level, int missing_disks)
@@ -1673,48 +1773,6 @@ static __u32 imsm_min_reserved_sectors(struct intel_super *super)
        return  (remainder < rv) ? remainder : rv;
 }
 
-/*
- * Return minimum size of a spare and sector size
- * that can be used in this array
- */
-int get_spare_criteria_imsm(struct supertype *st, struct spare_criteria *c)
-{
-       struct intel_super *super = st->sb;
-       struct dl *dl;
-       struct extent *e;
-       int i;
-       unsigned long long size = 0;
-
-       c->min_size = 0;
-       c->sector_size = 0;
-
-       if (!super)
-               return -EINVAL;
-       /* find first active disk in array */
-       dl = super->disks;
-       while (dl && (is_failed(&dl->disk) || dl->index == -1))
-               dl = dl->next;
-       if (!dl)
-               return -EINVAL;
-       /* find last lba used by subarrays */
-       e = get_extents(super, dl, 0);
-       if (!e)
-               return -EINVAL;
-       for (i = 0; e[i].size; i++)
-               continue;
-       if (i > 0)
-               size = e[i-1].start + e[i-1].size;
-       free(e);
-
-       /* add the amount of space needed for metadata */
-       size += imsm_min_reserved_sectors(super);
-
-       c->min_size = size * 512;
-       c->sector_size = super->sector_size;
-
-       return 0;
-}
-
 static bool is_gen_migration(struct imsm_dev *dev);
 
 #define IMSM_4K_DIV 8
@@ -1762,7 +1820,7 @@ static void print_imsm_dev(struct intel_super *super,
        printf("\n");
        printf("    Failed disk : ");
        if (map->failed_disk_num == 0xff)
-               printf("none");
+               printf(STR_COMMON_NONE);
        else
                printf("%i", map->failed_disk_num);
        printf("\n");
@@ -2078,91 +2136,18 @@ void convert_from_4k(struct intel_super *super)
        mpb->check_sum = __gen_imsm_checksum(mpb);
 }
 
-/*******************************************************************************
- * function: imsm_check_attributes
- * Description: Function checks if features represented by attributes flags
- *             are supported by mdadm.
- * Parameters:
- *             attributes - Attributes read from metadata
- * Returns:
- *             0 - passed attributes contains unsupported features flags
- *             1 - all features are supported
- ******************************************************************************/
-static int imsm_check_attributes(__u32 attributes)
+/**
+ * imsm_check_attributes() - Check if features represented by attributes flags are supported.
+ *
+ * @attributes: attributes read from metadata.
+ * Returns: true if all features are supported, false otherwise.
+ */
+static bool imsm_check_attributes(__u32 attributes)
 {
-       int ret_val = 1;
-       __u32 not_supported = MPB_ATTRIB_SUPPORTED^0xffffffff;
-
-       not_supported &= ~MPB_ATTRIB_IGNORED;
-
-       not_supported &= attributes;
-       if (not_supported) {
-               pr_err("(IMSM): Unsupported attributes : %x\n",
-                       (unsigned)__le32_to_cpu(not_supported));
-               if (not_supported & MPB_ATTRIB_CHECKSUM_VERIFY) {
-                       dprintf("\t\tMPB_ATTRIB_CHECKSUM_VERIFY \n");
-                       not_supported ^= MPB_ATTRIB_CHECKSUM_VERIFY;
-               }
-               if (not_supported & MPB_ATTRIB_2TB) {
-                       dprintf("\t\tMPB_ATTRIB_2TB\n");
-                       not_supported ^= MPB_ATTRIB_2TB;
-               }
-               if (not_supported & MPB_ATTRIB_RAID0) {
-                       dprintf("\t\tMPB_ATTRIB_RAID0\n");
-                       not_supported ^= MPB_ATTRIB_RAID0;
-               }
-               if (not_supported & MPB_ATTRIB_RAID1) {
-                       dprintf("\t\tMPB_ATTRIB_RAID1\n");
-                       not_supported ^= MPB_ATTRIB_RAID1;
-               }
-               if (not_supported & MPB_ATTRIB_RAID10) {
-                       dprintf("\t\tMPB_ATTRIB_RAID10\n");
-                       not_supported ^= MPB_ATTRIB_RAID10;
-               }
-               if (not_supported & MPB_ATTRIB_RAID1E) {
-                       dprintf("\t\tMPB_ATTRIB_RAID1E\n");
-                       not_supported ^= MPB_ATTRIB_RAID1E;
-               }
-               if (not_supported & MPB_ATTRIB_RAID5) {
-               dprintf("\t\tMPB_ATTRIB_RAID5\n");
-                       not_supported ^= MPB_ATTRIB_RAID5;
-               }
-               if (not_supported & MPB_ATTRIB_RAIDCNG) {
-                       dprintf("\t\tMPB_ATTRIB_RAIDCNG\n");
-                       not_supported ^= MPB_ATTRIB_RAIDCNG;
-               }
-               if (not_supported & MPB_ATTRIB_BBM) {
-                       dprintf("\t\tMPB_ATTRIB_BBM\n");
-               not_supported ^= MPB_ATTRIB_BBM;
-               }
-               if (not_supported & MPB_ATTRIB_CHECKSUM_VERIFY) {
-                       dprintf("\t\tMPB_ATTRIB_CHECKSUM_VERIFY (== MPB_ATTRIB_LEGACY)\n");
-                       not_supported ^= MPB_ATTRIB_CHECKSUM_VERIFY;
-               }
-               if (not_supported & MPB_ATTRIB_EXP_STRIPE_SIZE) {
-                       dprintf("\t\tMPB_ATTRIB_EXP_STRIP_SIZE\n");
-                       not_supported ^= MPB_ATTRIB_EXP_STRIPE_SIZE;
-               }
-               if (not_supported & MPB_ATTRIB_2TB_DISK) {
-                       dprintf("\t\tMPB_ATTRIB_2TB_DISK\n");
-                       not_supported ^= MPB_ATTRIB_2TB_DISK;
-               }
-               if (not_supported & MPB_ATTRIB_NEVER_USE2) {
-                       dprintf("\t\tMPB_ATTRIB_NEVER_USE2\n");
-                       not_supported ^= MPB_ATTRIB_NEVER_USE2;
-               }
-               if (not_supported & MPB_ATTRIB_NEVER_USE) {
-                       dprintf("\t\tMPB_ATTRIB_NEVER_USE\n");
-                       not_supported ^= MPB_ATTRIB_NEVER_USE;
-               }
-
-               if (not_supported)
-                       dprintf("(IMSM): Unknown attributes : %x\n", not_supported);
-
-               ret_val = 0;
-       }
+       if ((attributes & (MPB_ATTRIB_SUPPORTED | MPB_ATTRIB_IGNORED)) == attributes)
+               return true;
 
-       return ret_val;
+       return false;
 }
 
 static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char *map);
@@ -2190,13 +2175,12 @@ static void examine_super_imsm(struct supertype *st, char *homehost)
        creation_time = __le64_to_cpu(mpb->creation_time);
        printf("  Creation Time : %.24s\n",
                creation_time ? ctime(&creation_time) : "Unknown");
-       printf("     Attributes : ");
-       if (imsm_check_attributes(mpb->attributes))
-               printf("All supported\n");
-       else
-               printf("not supported\n");
+
+       printf("     Attributes : %08x (%s)\n", mpb->attributes,
+              imsm_check_attributes(mpb->attributes) ? "supported" : "not supported");
+
        getinfo_super_imsm(st, &info, NULL);
-       fname_from_uuid(st, &info, nbuf, ':');
+       fname_from_uuid(&info, nbuf);
        printf("           UUID : %s\n", nbuf + 5);
        sum = __le32_to_cpu(mpb->check_sum);
        printf("       Checksum : %08x %s\n", sum,
@@ -2221,7 +2205,7 @@ static void examine_super_imsm(struct supertype *st, char *homehost)
 
                super->current_vol = i;
                getinfo_super_imsm(st, &info, NULL);
-               fname_from_uuid(st, &info, nbuf, ':');
+               fname_from_uuid(&info, nbuf);
                print_imsm_dev(super, dev, nbuf + 5, super->disks->index);
        }
        for (i = 0; i < mpb->num_disks; i++) {
@@ -2246,7 +2230,7 @@ static void brief_examine_super_imsm(struct supertype *st, int verbose)
        char nbuf[64];
 
        getinfo_super_imsm(st, &info, NULL);
-       fname_from_uuid(st, &info, nbuf, ':');
+       fname_from_uuid(&info, nbuf);
        printf("ARRAY metadata=imsm UUID=%s\n", nbuf + 5);
 }
 
@@ -2263,14 +2247,14 @@ static void brief_examine_subarrays_imsm(struct supertype *st, int verbose)
                return;
 
        getinfo_super_imsm(st, &info, NULL);
-       fname_from_uuid(st, &info, nbuf, ':');
+       fname_from_uuid(&info, nbuf);
        for (i = 0; i < super->anchor->num_raid_devs; i++) {
                struct imsm_dev *dev = get_imsm_dev(super, i);
 
                super->current_vol = i;
                getinfo_super_imsm(st, &info, NULL);
-               fname_from_uuid(st, &info, nbuf1, ':');
-               printf("ARRAY /dev/md/%.16s container=%s member=%d UUID=%s\n",
+               fname_from_uuid(&info, nbuf1);
+               printf("ARRAY " DEV_MD_DIR "%.16s container=%s member=%d UUID=%s\n",
                       dev->volume, nbuf + 5, i, nbuf1 + 5);
        }
 }
@@ -2283,7 +2267,7 @@ static void export_examine_super_imsm(struct supertype *st)
        char nbuf[64];
 
        getinfo_super_imsm(st, &info, NULL);
-       fname_from_uuid(st, &info, nbuf, ':');
+       fname_from_uuid(&info, nbuf);
        printf("MD_METADATA=imsm\n");
        printf("MD_LEVEL=container\n");
        printf("MD_UUID=%s\n", nbuf+5);
@@ -2303,7 +2287,7 @@ static void detail_super_imsm(struct supertype *st, char *homehost,
                super->current_vol = strtoul(subarray, NULL, 10);
 
        getinfo_super_imsm(st, &info, NULL);
-       fname_from_uuid(st, &info, nbuf, ':');
+       fname_from_uuid(&info, nbuf);
        printf("\n              UUID : %s\n", nbuf + 5);
 
        super->current_vol = temp_vol;
@@ -2320,7 +2304,7 @@ static void brief_detail_super_imsm(struct supertype *st, char *subarray)
                super->current_vol = strtoul(subarray, NULL, 10);
 
        getinfo_super_imsm(st, &info, NULL);
-       fname_from_uuid(st, &info, nbuf, ':');
+       fname_from_uuid(&info, nbuf);
        printf(" UUID=%s", nbuf + 5);
 
        super->current_vol = temp_vol;
@@ -2330,12 +2314,42 @@ static int imsm_read_serial(int fd, char *devname, __u8 *serial,
                            size_t serial_buf_len);
 static void fd2devname(int fd, char *name);
 
-static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_base, int verbose)
+void print_encryption_information(int disk_fd, enum sys_dev_type hba_type)
+{
+       struct encryption_information information = {0};
+       mdadm_status_t status = MDADM_STATUS_SUCCESS;
+       const char *indent = "                  ";
+
+       switch (hba_type) {
+       case SYS_DEV_VMD:
+       case SYS_DEV_NVME:
+               status = get_nvme_opal_encryption_information(disk_fd, &information, 1);
+               break;
+       case SYS_DEV_SATA:
+       case SYS_DEV_SATA_VMD:
+               status = get_ata_encryption_information(disk_fd, &information, 1);
+               break;
+       default:
+               return;
+       }
+
+       if (status) {
+               pr_err("Failed to get drive encryption information.\n");
+               return;
+       }
+
+       printf("%sEncryption(Ability|Status): %s|%s\n", indent,
+              get_encryption_ability_string(information.ability),
+              get_encryption_status_string(information.status));
+}
+
+static int ahci_enumerate_ports(struct sys_dev *hba, unsigned long port_count, int host_base,
+                               int verbose)
 {
        /* dump an unsorted list of devices attached to AHCI Intel storage
         * controller, as well as non-connected ports
         */
-       int hba_len = strlen(hba_path) + 1;
+       int hba_len = strlen(hba->path) + 1;
        struct dirent *ent;
        DIR *dir;
        char *path = NULL;
@@ -2344,7 +2358,7 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
 
        if (port_count > (int)sizeof(port_mask) * 8) {
                if (verbose > 0)
-                       pr_err("port_count %d out of range\n", port_count);
+                       pr_err("port_count %ld out of range\n", port_count);
                return 2;
        }
 
@@ -2371,7 +2385,7 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
                path = devt_to_devpath(makedev(major, minor), 1, NULL);
                if (!path)
                        continue;
-               if (!path_attached_to_hba(path, hba_path)) {
+               if (!path_attached_to_hba(path, hba->path)) {
                        free(path);
                        path = NULL;
                        continue;
@@ -2474,6 +2488,8 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
                                printf(" (%s)\n", buf);
                        else
                                printf(" ()\n");
+
+                       print_encryption_information(fd, hba->type);
                        close(fd);
                }
                free(path);
@@ -2484,11 +2500,11 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
        if (dir)
                closedir(dir);
        if (err == 0) {
-               int i;
+               unsigned long i;
 
                for (i = 0; i < port_count; i++)
-                       if (port_mask & (1 << i))
-                               printf("          Port%d : - no device attached -\n", i);
+                       if (port_mask & (1L << i))
+                               printf("          Port%ld : - no device attached -\n", i);
        }
 
        return err;
@@ -2538,6 +2554,8 @@ static int print_nvme_info(struct sys_dev *hba)
                else
                        printf("()\n");
 
+               print_encryption_information(fd, hba->type);
+
 skip:
                close_fd(&fd);
        }
@@ -2559,6 +2577,8 @@ static void print_found_intel_controllers(struct sys_dev *elem)
 
                if (elem->type == SYS_DEV_VMD)
                        fprintf(stderr, "VMD domain");
+               else if (elem->type == SYS_DEV_SATA_VMD)
+                       fprintf(stderr, "SATA VMD domain");
                else
                        fprintf(stderr, "RAID controller");
 
@@ -2597,6 +2617,15 @@ static int ahci_get_port_count(const char *hba_path, int *port_count)
        return host_base;
 }
 
+static void print_imsm_level_capability(const struct imsm_orom *orom)
+{
+       int idx;
+
+       for (idx = 0; imsm_level_ops[idx].name; idx++)
+               if (imsm_level_ops[idx].is_level_supported(orom))
+                       printf("%s ", imsm_level_ops[idx].name);
+}
+
 static void print_imsm_capability(const struct imsm_orom *orom)
 {
        printf("       Platform : Intel(R) ");
@@ -2607,15 +2636,19 @@ static void print_imsm_capability(const struct imsm_orom *orom)
        else
                printf("Rapid Storage Technology%s\n",
                        imsm_orom_is_enterprise(orom) ? " enterprise" : "");
-       if (orom->major_ver || orom->minor_ver || orom->hotfix_ver || orom->build)
-               printf("        Version : %d.%d.%d.%d\n", orom->major_ver,
-                               orom->minor_ver, orom->hotfix_ver, orom->build);
-       printf("    RAID Levels :%s%s%s%s%s\n",
-              imsm_orom_has_raid0(orom) ? " raid0" : "",
-              imsm_orom_has_raid1(orom) ? " raid1" : "",
-              imsm_orom_has_raid1e(orom) ? " raid1e" : "",
-              imsm_orom_has_raid10(orom) ? " raid10" : "",
-              imsm_orom_has_raid5(orom) ? " raid5" : "");
+       if (orom->major_ver || orom->minor_ver || orom->hotfix_ver || orom->build) {
+               if (imsm_orom_is_vmd_without_efi(orom))
+                       printf("        Version : %d.%d\n", orom->major_ver,
+                              orom->minor_ver);
+               else
+                       printf("        Version : %d.%d.%d.%d\n", orom->major_ver,
+                              orom->minor_ver, orom->hotfix_ver, orom->build);
+       }
+
+       printf("    RAID Levels : ");
+       print_imsm_level_capability(orom);
+       printf("\n");
+
        printf("    Chunk Sizes :%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
               imsm_orom_has_chunk(orom, 2) ? " 2k" : "",
               imsm_orom_has_chunk(orom, 4) ? " 4k" : "",
@@ -2650,12 +2683,11 @@ static void print_imsm_capability_export(const struct imsm_orom *orom)
        if (orom->major_ver || orom->minor_ver || orom->hotfix_ver || orom->build)
                printf("IMSM_VERSION=%d.%d.%d.%d\n", orom->major_ver, orom->minor_ver,
                                orom->hotfix_ver, orom->build);
-       printf("IMSM_SUPPORTED_RAID_LEVELS=%s%s%s%s%s\n",
-                       imsm_orom_has_raid0(orom) ? "raid0 " : "",
-                       imsm_orom_has_raid1(orom) ? "raid1 " : "",
-                       imsm_orom_has_raid1e(orom) ? "raid1e " : "",
-                       imsm_orom_has_raid5(orom) ? "raid10 " : "",
-                       imsm_orom_has_raid10(orom) ? "raid5 " : "");
+
+       printf("IMSM_SUPPORTED_RAID_LEVELS=");
+       print_imsm_level_capability(orom);
+       printf("\n");
+
        printf("IMSM_SUPPORTED_CHUNK_SIZES=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
                        imsm_orom_has_chunk(orom, 2) ? "2k " : "",
                        imsm_orom_has_chunk(orom, 4) ? "4k " : "",
@@ -2699,7 +2731,7 @@ static int detail_platform_imsm(int verbose, int enumerate_only, char *controlle
        int result=1;
 
        if (enumerate_only) {
-               if (check_env("IMSM_NO_PLATFORM"))
+               if (check_no_platform())
                        return 0;
                list = find_intel_devices();
                if (!list)
@@ -2729,8 +2761,9 @@ static int detail_platform_imsm(int verbose, int enumerate_only, char *controlle
                if (!find_imsm_capability(hba)) {
                        char buf[PATH_MAX];
                        pr_err("imsm capabilities not found for controller: %s (type %s)\n",
-                                 hba->type == SYS_DEV_VMD ? vmd_domain_to_controller(hba, buf) : hba->path,
-                                 get_sys_dev_type(hba->type));
+                                 hba->type == SYS_DEV_VMD || hba->type == SYS_DEV_SATA_VMD ?
+                                 vmd_domain_to_controller(hba, buf) :
+                                 hba->path, get_sys_dev_type(hba->type));
                        continue;
                }
                result = 0;
@@ -2783,11 +2816,12 @@ static int detail_platform_imsm(int verbose, int enumerate_only, char *controlle
 
                        printf(" I/O Controller : %s (%s)\n",
                                hba->path, get_sys_dev_type(hba->type));
-                       if (hba->type == SYS_DEV_SATA) {
+                       if (hba->type == SYS_DEV_SATA || hba->type == SYS_DEV_SATA_VMD) {
                                host_base = ahci_get_port_count(hba->path, &port_count);
-                               if (ahci_enumerate_ports(hba->path, port_count, host_base, verbose)) {
+                               if (ahci_enumerate_ports(hba, port_count, host_base, verbose)) {
                                        if (verbose > 0)
-                                               pr_err("failed to enumerate ports on SATA controller at %s.\n", hba->pci_id);
+                                               pr_err("failed to enumerate ports on %s controller at %s.\n",
+                                                       get_sys_dev_type(hba->type), hba->pci_id);
                                        result |= 2;
                                }
                        }
@@ -2817,7 +2851,8 @@ static int export_detail_platform_imsm(int verbose, char *controller_path)
                if (!find_imsm_capability(hba) && verbose > 0) {
                        char buf[PATH_MAX];
                        pr_err("IMSM_DETAIL_PLATFORM_ERROR=NO_IMSM_CAPABLE_DEVICE_UNDER_%s\n",
-                       hba->type == SYS_DEV_VMD ? vmd_domain_to_controller(hba, buf) : hba->path);
+                               hba->type == SYS_DEV_VMD || hba->type == SYS_DEV_SATA_VMD ?
+                               vmd_domain_to_controller(hba, buf) : hba->path);
                }
                else
                        result = 0;
@@ -2826,7 +2861,7 @@ static int export_detail_platform_imsm(int verbose, char *controller_path)
        const struct orom_entry *entry;
 
        for (entry = orom_entries; entry; entry = entry->next) {
-               if (entry->type == SYS_DEV_VMD) {
+               if (entry->type == SYS_DEV_VMD || entry->type == SYS_DEV_SATA_VMD) {
                        for (hba = list; hba; hba = hba->next)
                                print_imsm_capability_export(&entry->orom);
                        continue;
@@ -2903,34 +2938,6 @@ static void uuid_from_super_imsm(struct supertype *st, int uuid[4])
        memcpy(uuid, buf, 4*4);
 }
 
-#if 0
-static void
-get_imsm_numerical_version(struct imsm_super *mpb, int *m, int *p)
-{
-       __u8 *v = get_imsm_version(mpb);
-       __u8 *end = mpb->sig + MAX_SIGNATURE_LENGTH;
-       char major[] = { 0, 0, 0 };
-       char minor[] = { 0 ,0, 0 };
-       char patch[] = { 0, 0, 0 };
-       char *ver_parse[] = { major, minor, patch };
-       int i, j;
-
-       i = j = 0;
-       while (*v != '\0' && v < end) {
-               if (*v != '.' && j < 2)
-                       ver_parse[i][j++] = *v;
-               else {
-                       i++;
-                       j = 0;
-               }
-               v++;
-       }
-
-       *m = strtol(minor, NULL, 0);
-       *p = strtol(patch, NULL, 0);
-}
-#endif
-
 static __u32 migr_strip_blocks_resync(struct imsm_dev *dev)
 {
        /* migr_strip_size when repairing or initializing parity */
@@ -3823,7 +3830,7 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char *
                 */
                max_enough = max(max_enough, enough);
        }
-       dprintf("enough: %d\n", max_enough);
+
        info->container_enough = max_enough;
 
        if (super->disks) {
@@ -3893,8 +3900,8 @@ struct mdinfo *getinfo_super_disks_imsm(struct supertype *st)
 }
 
 static int update_super_imsm(struct supertype *st, struct mdinfo *info,
-                            char *update, char *devname, int verbose,
-                            int uuid_set, char *homehost)
+                            enum update_opt update, char *devname,
+                            int verbose, int uuid_set, char *homehost)
 {
        /* For 'assemble' and 'force' we need to return non-zero if any
         * change was made.  For others, the return value is ignored.
@@ -3930,7 +3937,8 @@ static int update_super_imsm(struct supertype *st, struct mdinfo *info,
 
        mpb = super->anchor;
 
-       if (strcmp(update, "uuid") == 0) {
+       switch (update) {
+       case UOPT_UUID:
                /* We take this to mean that the family_num should be updated.
                 * However that is much smaller than the uuid so we cannot really
                 * allow an explicit uuid to be given.  And it is hard to reliably
@@ -3954,10 +3962,14 @@ static int update_super_imsm(struct supertype *st, struct mdinfo *info,
                }
                if (rv == 0)
                        mpb->orig_family_num = info->uuid[0];
-       } else if (strcmp(update, "assemble") == 0)
+               break;
+       case UOPT_SPEC_ASSEMBLE:
                rv = 0;
-       else
+               break;
+       default:
                rv = -1;
+               break;
+       }
 
        /* successful update? recompute checksum */
        if (rv == 0)
@@ -4124,17 +4136,17 @@ static int imsm_read_serial(int fd, char *devname,
 
        memset(buf, 0, sizeof(buf));
 
+       if (check_env("IMSM_DEVNAME_AS_SERIAL")) {
+               memset(serial, 0, serial_buf_len);
+               fd2devname(fd, (char *) serial);
+               return 0;
+       }
+
        rv = nvme_get_serial(fd, buf, sizeof(buf));
 
        if (rv)
                rv = scsi_get_serial(fd, buf, sizeof(buf));
 
-       if (rv && check_env("IMSM_DEVNAME_AS_SERIAL")) {
-               memset(serial, 0, MAX_RAID_SERIAL_LEN);
-               fd2devname(fd, (char *) serial);
-               return 0;
-       }
-
        if (rv != 0) {
                if (devname)
                        pr_err("Failed to retrieve serial for %s\n",
@@ -4228,7 +4240,10 @@ load_imsm_disk(int fd, struct intel_super *super, char *devname, int keep_fd)
 
        dl = xcalloc(1, sizeof(*dl));
 
-       fstat(fd, &stb);
+       if (fstat(fd, &stb) != 0) {
+               free(dl);
+               return 1;
+       }
        dl->major = major(stb.st_rdev);
        dl->minor = minor(stb.st_rdev);
        dl->next = super->disks;
@@ -4510,6 +4525,7 @@ static int load_imsm_mpb(int fd, struct intel_super *super, char *devname)
            MIGR_REC_BUF_SECTORS*MAX_SECTOR_SIZE) != 0) {
                pr_err("could not allocate migr_rec buffer\n");
                free(super->buf);
+               super->buf = NULL;
                return 2;
        }
        super->clean_migration_record_by_mdmon = 0;
@@ -4716,7 +4732,7 @@ static int find_intel_hba_capability(int fd, struct intel_super *super, char *de
                       devname);
                return 1;
        }
-       if (!is_fd_valid(fd) || check_env("IMSM_NO_PLATFORM")) {
+       if (!is_fd_valid(fd) || check_no_platform()) {
                super->orom = NULL;
                super->hba = NULL;
                return 0;
@@ -4737,10 +4753,12 @@ static int find_intel_hba_capability(int fd, struct intel_super *super, char *de
                                "    but the container is assigned to Intel(R) %s %s (",
                                devname,
                                get_sys_dev_type(hba_name->type),
-                               hba_name->type == SYS_DEV_VMD ? "domain" : "RAID controller",
+                               hba_name->type == SYS_DEV_VMD || hba_name->type == SYS_DEV_SATA_VMD ?
+                                       "domain" : "RAID controller",
                                hba_name->pci_id ? : "Err!",
                                get_sys_dev_type(super->hba->type),
-                               hba->type == SYS_DEV_VMD ? "domain" : "RAID controller");
+                               hba->type == SYS_DEV_VMD || hba_name->type == SYS_DEV_SATA_VMD ?
+                                       "domain" : "RAID controller");
 
                        while (hba) {
                                fprintf(stderr, "%s", hba->pci_id ? : "Err!");
@@ -5433,87 +5451,81 @@ static unsigned long long info_to_blocks_per_member(mdu_array_info_t *info,
                return (size * 2) & ~(info_to_blocks_per_strip(info) - 1);
 }
 
+static void imsm_write_signature(struct imsm_super *mpb)
+{
+       /* It is safer to eventually truncate version rather than left it not NULL ended */
+       snprintf((char *) mpb->sig, MAX_SIGNATURE_LENGTH, MPB_SIGNATURE MPB_VERSION_ATTRIBS);
+}
+
 static void imsm_update_version_info(struct intel_super *super)
 {
        /* update the version and attributes */
        struct imsm_super *mpb = super->anchor;
-       char *version;
        struct imsm_dev *dev;
        struct imsm_map *map;
        int i;
 
+       mpb->attributes |= MPB_ATTRIB_CHECKSUM_VERIFY;
+
        for (i = 0; i < mpb->num_raid_devs; i++) {
                dev = get_imsm_dev(super, i);
                map = get_imsm_map(dev, MAP_0);
+
                if (__le32_to_cpu(dev->size_high) > 0)
                        mpb->attributes |= MPB_ATTRIB_2TB;
 
-               /* FIXME detect when an array spans a port multiplier */
-               #if 0
-               mpb->attributes |= MPB_ATTRIB_PM;
-               #endif
-
-               if (mpb->num_raid_devs > 1 ||
-                   mpb->attributes != MPB_ATTRIB_CHECKSUM_VERIFY) {
-                       version = MPB_VERSION_ATTRIBS;
-                       switch (get_imsm_raid_level(map)) {
-                       case 0: mpb->attributes |= MPB_ATTRIB_RAID0; break;
-                       case 1: mpb->attributes |= MPB_ATTRIB_RAID1; break;
-                       case 10: mpb->attributes |= MPB_ATTRIB_RAID10; break;
-                       case 5: mpb->attributes |= MPB_ATTRIB_RAID5; break;
-                       }
-               } else {
-                       if (map->num_members >= 5)
-                               version = MPB_VERSION_5OR6_DISK_ARRAY;
-                       else if (dev->status == DEV_CLONE_N_GO)
-                               version = MPB_VERSION_CNG;
-                       else if (get_imsm_raid_level(map) == 5)
-                               version = MPB_VERSION_RAID5;
-                       else if (map->num_members >= 3)
-                               version = MPB_VERSION_3OR4_DISK_ARRAY;
-                       else if (get_imsm_raid_level(map) == 1)
-                               version = MPB_VERSION_RAID1;
-                       else
-                               version = MPB_VERSION_RAID0;
+               switch (get_imsm_raid_level(map)) {
+               case IMSM_T_RAID0:
+                       mpb->attributes |= MPB_ATTRIB_RAID0;
+                       break;
+               case IMSM_T_RAID1:
+                       mpb->attributes |= MPB_ATTRIB_RAID1;
+                       break;
+               case IMSM_T_RAID5:
+                       mpb->attributes |= MPB_ATTRIB_RAID5;
+                       break;
+               case IMSM_T_RAID10:
+                       mpb->attributes |= MPB_ATTRIB_RAID10;
+                       if (map->num_members > 4)
+                               mpb->attributes |= MPB_ATTRIB_RAID10_EXT;
+                       break;
                }
-               strcpy(((char *) mpb->sig) + strlen(MPB_SIGNATURE), version);
        }
+
+       imsm_write_signature(mpb);
 }
 
-static int check_name(struct intel_super *super, char *name, int quiet)
+/**
+ * imsm_check_name() - check imsm naming criteria.
+ * @super: &intel_super pointer, not NULL.
+ * @name: name to check.
+ * @verbose: verbose level.
+ *
+ * Name must be no longer than &MAX_RAID_SERIAL_LEN and must be unique across volumes.
+ *
+ * Returns: &true if @name matches, &false otherwise.
+ */
+static bool imsm_is_name_allowed(struct intel_super *super, const char * const name,
+                                const int verbose)
 {
        struct imsm_super *mpb = super->anchor;
-       char *reason = NULL;
-       char *start = name;
-       size_t len = strlen(name);
        int i;
 
-       if (len > 0) {
-               while (isspace(start[len - 1]))
-                       start[--len] = 0;
-               while (*start && isspace(*start))
-                       ++start, --len;
-               memmove(name, start, len + 1);
+       if (is_string_lq(name, MAX_RAID_SERIAL_LEN + 1) == false) {
+               pr_vrb("imsm: Name \"%s\" is too long\n", name);
+               return false;
        }
 
-       if (len > MAX_RAID_SERIAL_LEN)
-               reason = "must be 16 characters or less";
-       else if (len == 0)
-               reason = "must be a non-empty string";
-
        for (i = 0; i < mpb->num_raid_devs; i++) {
                struct imsm_dev *dev = get_imsm_dev(super, i);
 
                if (strncmp((char *) dev->volume, name, MAX_RAID_SERIAL_LEN) == 0) {
-                       reason = "already exists";
-                       break;
+                       pr_vrb("imsm: Name \"%s\" already exists\n", name);
+                       return false;
                }
        }
 
-       if (reason && !quiet)
-               pr_err("imsm volume name %s\n", reason);
-
-       return !reason;
+       return true;
 }
 
 static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
@@ -5608,8 +5620,9 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                }
        }
 
-       if (!check_name(super, name, 0))
+       if (imsm_is_name_allowed(super, name, 1) == false)
                return 0;
+
        dv = xmalloc(sizeof(*dv));
        dev = xcalloc(1, sizeof(*dev) + sizeof(__u32) * (info->raid_disks - 1));
        /*
@@ -5637,7 +5650,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        set_pba_of_lba0(map, super->create_offset);
        map->blocks_per_strip = __cpu_to_le16(info_to_blocks_per_strip(info));
        map->failed_disk_num = ~0;
-       if (info->level > 0)
+       if (info->level > IMSM_T_RAID0)
                map->map_state = (info->state ? IMSM_T_STATE_NORMAL
                                  : IMSM_T_STATE_UNINITIALIZED);
        else
@@ -5645,16 +5658,15 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                                                      IMSM_T_STATE_NORMAL;
        map->ddf = 1;
 
-       if (info->level == 1 && info->raid_disks > 2) {
+       if (info->level == IMSM_T_RAID1 && info->raid_disks > 2) {
                free(dev);
                free(dv);
-               pr_err("imsm does not support more than 2 disksin a raid1 volume\n");
+               pr_err("imsm does not support more than 2 disks in a raid1 volume\n");
                return 0;
        }
+       map->num_members = info->raid_disks;
 
-       map->raid_level = info->level;
-       if (info->level == 10)
-               map->raid_level = 1;
+       update_imsm_raid_level(map, info->level);
        set_num_domains(map);
 
        size_per_member += NUM_BLOCKS_DIRTY_STRIPE_REGION;
@@ -5662,7 +5674,6 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                                                             size_per_member /
                                                             BLOCKS_PER_KB));
 
-       map->num_members = info->raid_disks;
        update_num_data_stripes(map, array_blocks);
        for (i = 0; i < map->num_members; i++) {
                /* initialized in add_to_super */
@@ -5710,7 +5721,6 @@ static int init_super_imsm(struct supertype *st, mdu_array_info_t *info,
        struct intel_super *super;
        struct imsm_super *mpb;
        size_t mpb_size;
-       char *version;
 
        if (data_offset != INVALID_SECTORS) {
                pr_err("data-offset not supported by imsm\n");
@@ -5753,13 +5763,7 @@ static int init_super_imsm(struct supertype *st, mdu_array_info_t *info,
                return 0;
        }
 
-       mpb->attributes = MPB_ATTRIB_CHECKSUM_VERIFY;
-
-       version = (char *) mpb->sig;
-       strcpy(version, MPB_SIGNATURE);
-       version += strlen(MPB_SIGNATURE);
-       strcpy(version, MPB_VERSION_RAID0);
-
+       imsm_update_version_info(super);
        return 1;
 }
 
@@ -5981,7 +5985,8 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
        if (super->current_vol >= 0)
                return add_to_super_imsm_volume(st, dk, fd, devname);
 
-       fstat(fd, &stb);
+       if (fstat(fd, &stb) != 0)
+               return 1;
        dd = xcalloc(sizeof(*dd), 1);
        dd->major = major(stb.st_rdev);
        dd->minor = minor(stb.st_rdev);
@@ -6133,7 +6138,6 @@ static union {
 
 static int write_super_imsm_spare(struct intel_super *super, struct dl *d)
 {
-       struct imsm_super *mpb = super->anchor;
        struct imsm_super *spare = &spare_record.anchor;
        __u32 sum;
 
@@ -6142,14 +6146,11 @@ static int write_super_imsm_spare(struct intel_super *super, struct dl *d)
 
        spare->mpb_size = __cpu_to_le32(sizeof(struct imsm_super));
        spare->generation_num = __cpu_to_le32(1UL);
-       spare->attributes = MPB_ATTRIB_CHECKSUM_VERIFY;
        spare->num_disks = 1;
        spare->num_raid_devs = 0;
-       spare->cache_size = mpb->cache_size;
        spare->pwr_cycle_count = __cpu_to_le32(1);
 
-       snprintf((char *) spare->sig, MAX_SIGNATURE_LENGTH,
-                MPB_SIGNATURE MPB_VERSION_RAID0);
+       imsm_write_signature(spare);
 
        spare->disk[0] = d->disk;
        if (__le32_to_cpu(d->disk.total_blocks_hi) > 0)
@@ -6533,7 +6534,7 @@ static int validate_ppl_imsm(struct supertype *st, struct mdinfo *info,
                if (mdmon_running(st->container_devnm))
                        st->update_tail = &st->updates;
 
-               if (st->ss->update_subarray(st, subarray, "ppl", NULL)) {
+               if (st->ss->update_subarray(st, subarray, UOPT_PPL, NULL)) {
                        pr_err("Failed to update subarray %s\n",
                              subarray);
                } else {
@@ -6727,7 +6728,7 @@ static int validate_geometry_imsm_container(struct supertype *st, int level,
        struct intel_super *super = NULL;
        int rv = 0;
 
-       if (level != LEVEL_CONTAINER)
+       if (!is_container(level))
                return 0;
        if (!dev)
                return 1;
@@ -6824,20 +6825,35 @@ static unsigned long long find_size(struct extent *e, int *idx, int num_extents)
        return end - base_start;
 }
 
-static unsigned long long merge_extents(struct intel_super *super, int sum_extents)
+/** merge_extents() - analyze extents and get free size.
+ * @super: Intel metadata, not NULL.
+ * @expanding: if set, we are expanding &super->current_vol.
+ *
+ * Build a composite disk with all known extents and generate a size given the
+ * "all disks in an array must share a common start offset" constraint.
+ * If a volume is expanded, then return free space after the volume.
+ *
+ * Return: Free space or 0 on failure.
+ */
+static unsigned long long merge_extents(struct intel_super *super, const bool expanding)
 {
-       /* build a composite disk with all known extents and generate a new
-        * 'maxsize' given the "all disks in an array must share a common start
-        * offset" constraint
-        */
-       struct extent *e = xcalloc(sum_extents, sizeof(*e));
+       struct extent *e;
        struct dl *dl;
-       int i, j;
-       int start_extent;
-       unsigned long long pos;
+       int i, j, pos_vol_idx = -1;
+       int extent_idx = 0;
+       int sum_extents = 0;
+       unsigned long long pos = 0;
        unsigned long long start = 0;
-       unsigned long long maxsize;
-       unsigned long reserve;
+       unsigned long long free_size = 0;
+
+       unsigned long pre_reservation = 0;
+       unsigned long post_reservation = IMSM_RESERVED_SECTORS;
+       unsigned long reservation_size;
+
+       for (dl = super->disks; dl; dl = dl->next)
+               if (dl->e)
+                       sum_extents += dl->extent_cnt;
+       e = xcalloc(sum_extents, sizeof(struct extent));
 
        /* coalesce and sort all extents. also, check to see if we need to
         * reserve space between member arrays
@@ -6856,72 +6872,94 @@ static unsigned long long merge_extents(struct intel_super *super, int sum_exten
        j = 0;
        while (i < sum_extents) {
                e[j].start = e[i].start;
+               e[j].vol = e[i].vol;
                e[j].size = find_size(e, &i, sum_extents);
                j++;
                if (e[j-1].size == 0)
                        break;
        }
 
-       pos = 0;
-       maxsize = 0;
-       start_extent = 0;
        i = 0;
        do {
-               unsigned long long esize;
+               unsigned long long esize = e[i].start - pos;
 
-               esize = e[i].start - pos;
-               if (esize >= maxsize) {
-                       maxsize = esize;
+               if (expanding ? pos_vol_idx == super->current_vol : esize >= free_size) {
+                       free_size = esize;
                        start = pos;
-                       start_extent = i;
+                       extent_idx = i;
                }
+
                pos = e[i].start + e[i].size;
+               pos_vol_idx = e[i].vol;
+
                i++;
        } while (e[i-1].size);
-       free(e);
 
-       if (maxsize == 0)
+       if (free_size == 0) {
+               dprintf("imsm: Cannot find free size.\n");
+               free(e);
                return 0;
+       }
 
-       /* FIXME assumes volume at offset 0 is the first volume in a
-        * container
-        */
-       if (start_extent > 0)
-               reserve = IMSM_RESERVED_SECTORS; /* gap between raid regions */
-       else
-               reserve = 0;
+       if (!expanding && extent_idx != 0)
+               /*
+                * Not a real first volume in a container is created, pre_reservation is needed.
+                */
+               pre_reservation = IMSM_RESERVED_SECTORS;
 
-       if (maxsize < reserve)
-               return 0;
+       if (e[extent_idx].size == 0)
+               /*
+                * extent_idx points to the metadata, post_reservation is allready done.
+                */
+               post_reservation = 0;
+       free(e);
 
-       super->create_offset = ~((unsigned long long) 0);
-       if (start + reserve > super->create_offset)
-               return 0; /* start overflows create_offset */
-       super->create_offset = start + reserve;
+       reservation_size = pre_reservation + post_reservation;
+
+       if (free_size < reservation_size) {
+               dprintf("imsm: Reservation size is greater than free space.\n");
+               return 0;
+       }
 
-       return maxsize - reserve;
+       super->create_offset = start + pre_reservation;
+       return free_size - reservation_size;
 }
 
-static int is_raid_level_supported(const struct imsm_orom *orom, int level, int raiddisks)
+/**
+ * is_raid_level_supported() - check if this count of drives and level is supported by platform.
+ * @orom: hardware properties, could be NULL.
+ * @level: requested raid level.
+ * @raiddisks: requested disk count.
+ *
+ * IMSM UEFI/OROM does not provide information about supported count of raid disks
+ * for particular level. That is why it is hardcoded.
+ * It is recommended to not allow of usage other levels than supported,
+ * IMSM code is not tested against different level implementations.
+ *
+ * Return: true if supported, false otherwise.
+ */
+static bool is_raid_level_supported(const struct imsm_orom *orom, int level, int raiddisks)
 {
-       if (level < 0 || level == 6 || level == 4)
-               return 0;
+       int idx;
 
-       /* if we have an orom prevent invalid raid levels */
-       if (orom)
-               switch (level) {
-               case 0: return imsm_orom_has_raid0(orom);
-               case 1:
-                       if (raiddisks > 2)
-                               return imsm_orom_has_raid1e(orom);
-                       return imsm_orom_has_raid1(orom) && raiddisks == 2;
-               case 10: return imsm_orom_has_raid10(orom) && raiddisks == 4;
-               case 5: return imsm_orom_has_raid5(orom) && raiddisks > 2;
-               }
-       else
-               return 1; /* not on an Intel RAID platform so anything goes */
+       for (idx = 0; imsm_level_ops[idx].name; idx++) {
+               if (imsm_level_ops[idx].level == level)
+                       break;
+       }
 
-       return 0;
+       if (!imsm_level_ops[idx].name)
+               return false;
+
+       if (!imsm_level_ops[idx].is_raiddisks_count_supported(raiddisks))
+               return false;
+
+       if (!orom)
+               return true;
+
+       if (imsm_level_ops[idx].is_level_supported(orom))
+               return true;
+
+       return false;
 }
 
 static int
@@ -6945,7 +6983,7 @@ active_arrays_by_format(char *name, char* hba, struct md_list **devlist,
                        int fd = -1;
                        while (dev && !is_fd_valid(fd)) {
                                char *path = xmalloc(strlen(dev->name) + strlen("/dev/") + 1);
-                               num = sprintf(path, "%s%s", "/dev/", dev->name);
+                               num = snprintf(path, PATH_MAX, "%s%s", "/dev/", dev->name);
                                if (num > 0)
                                        fd = open(path, O_RDONLY, 0);
                                if (num <= 0 || !is_fd_valid(fd)) {
@@ -7009,7 +7047,6 @@ get_devices(const char *hba_path)
        struct md_list *dv;
        struct dirent *ent;
        DIR *dir;
-       int err = 0;
 
 #if DEBUG_LOOP
        devlist = get_loop_devices();
@@ -7051,14 +7088,6 @@ get_devices(const char *hba_path)
                dv->next = devlist;
                devlist = dv;
        }
-       if (err) {
-               while(devlist) {
-                       dv = devlist;
-                       devlist = devlist->next;
-                       free(dv->devname);
-                       free(dv);
-               }
-       }
        closedir(dir);
        return devlist;
 }
@@ -7497,13 +7526,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
                return 0;
        }
 
-       /* count total number of extents for merge */
-       i = 0;
-       for (dl = super->disks; dl; dl = dl->next)
-               if (dl->e)
-                       i += dl->extent_cnt;
-
-       maxsize = merge_extents(super, i);
+       maxsize = merge_extents(super, false);
 
        if (mpb->num_raid_devs > 0 && size && size != maxsize)
                pr_err("attempting to create a second volume with size less then remaining space.\n");
@@ -7538,7 +7561,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
  * @super: &intel_super pointer, not NULL.
  * @raiddisks: number of raid disks.
  * @size: requested size, could be 0 (means max size).
- * @chunk: requested chunk.
+ * @chunk: requested chunk size in KiB.
  * @freesize: pointer for returned size value.
  *
  * Return: &IMSM_STATUS_OK or &IMSM_STATUS_ERROR.
@@ -7552,22 +7575,22 @@ static imsm_status_t imsm_get_free_size(struct intel_super *super,
                                        const int raiddisks,
                                        unsigned long long size,
                                        const int chunk,
-                                       unsigned long long *freesize)
+                                       unsigned long long *freesize,
+                                       bool expanding)
 {
        struct imsm_super *mpb = super->anchor;
        struct dl *dl;
        int i;
-       int extent_cnt;
        struct extent *e;
+       int cnt = 0;
+       int used = 0;
        unsigned long long maxsize;
-       unsigned long long minsize;
-       int cnt;
-       int used;
+       unsigned long long minsize = size;
+
+       if (minsize == 0)
+               minsize = chunk * 2;
 
        /* find the largest common start free region of the possible disks */
-       used = 0;
-       extent_cnt = 0;
-       cnt = 0;
        for (dl = super->disks; dl; dl = dl->next) {
                dl->raiddisk = -1;
 
@@ -7587,19 +7610,18 @@ static imsm_status_t imsm_get_free_size(struct intel_super *super,
                        ;
                dl->e = e;
                dl->extent_cnt = i;
-               extent_cnt += i;
                cnt++;
        }
 
-       maxsize = merge_extents(super, extent_cnt);
-       minsize = size;
-       if (size == 0)
-               /* chunk is in K */
-               minsize = chunk * 2;
+       maxsize = merge_extents(super, expanding);
+       if (maxsize < minsize)  {
+               pr_err("imsm: Free space is %llu but must be equal or larger than %llu.\n",
+                      maxsize, minsize);
+               return IMSM_STATUS_ERROR;
+       }
 
-       if (cnt < raiddisks || (super->orom && used && used != raiddisks) ||
-           maxsize < minsize || maxsize == 0) {
-               pr_err("not enough devices with space to create array.\n");
+       if (cnt < raiddisks || (super->orom && used && used != raiddisks)) {
+               pr_err("imsm: Not enough devices with space to create array.\n");
                return IMSM_STATUS_ERROR;
        }
 
@@ -7649,7 +7671,7 @@ static imsm_status_t autolayout_imsm(struct intel_super *super,
        int vol_cnt = super->anchor->num_raid_devs;
        imsm_status_t rv;
 
-       rv = imsm_get_free_size(super, raiddisks, size, chunk, freesize);
+       rv = imsm_get_free_size(super, raiddisks, size, chunk, freesize, false);
        if (rv != IMSM_STATUS_OK)
                return IMSM_STATUS_ERROR;
 
@@ -7692,7 +7714,7 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
         * if given unused devices create a container
         * if given given devices in a container create a member volume
         */
-       if (level == LEVEL_CONTAINER)
+       if (is_container(level))
                /* Must be a fresh device to add to a container */
                return validate_geometry_imsm_container(st, level, raiddisks,
                                                        data_offset, dev,
@@ -7714,11 +7736,11 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
                struct intel_super *super = st->sb;
 
                /*
-                * Autolayout mode, st->sb and freesize must be set.
+                * Autolayout mode, st->sb must be set.
                 */
-               if (!super || !freesize) {
-                       pr_vrb("freesize and superblock must be set for autolayout, aborting\n");
-                       return 1;
+               if (!super) {
+                       pr_vrb("superblock must be set for autolayout, aborting\n");
+                       return 0;
                }
 
                if (!validate_geometry_imsm_orom(st->sb, level, layout,
@@ -7726,7 +7748,7 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
                                                 verbose))
                        return 0;
 
-               if (super->orom) {
+               if (super->orom && freesize) {
                        imsm_status_t rv;
                        int count = count_volumes(super->hba, super->orom->dpa,
                                              verbose);
@@ -7844,7 +7866,7 @@ static int kill_subarray_imsm(struct supertype *st, char *subarray_id)
 
                if (i < current_vol)
                        continue;
-               sprintf(subarray, "%u", i);
+               snprintf(subarray, sizeof(subarray), "%u", i);
                if (is_subarray_active(subarray, st->devnm)) {
                        pr_err("deleting subarray-%d would change the UUID of active subarray-%d, aborting\n",
                               current_vol, i);
@@ -7889,37 +7911,40 @@ static int kill_subarray_imsm(struct supertype *st, char *subarray_id)
        return 0;
 }
 
-static int get_rwh_policy_from_update(char *update)
+/**
+ * get_rwh_policy_from_update() - Get the rwh policy for update option.
+ * @update: Update option.
+ */
+static int get_rwh_policy_from_update(enum update_opt update)
 {
-       if (strcmp(update, "ppl") == 0)
+       switch (update) {
+       case UOPT_PPL:
                return RWH_MULTIPLE_DISTRIBUTED;
-       else if (strcmp(update, "no-ppl") == 0)
+       case UOPT_NO_PPL:
                return RWH_MULTIPLE_OFF;
-       else if (strcmp(update, "bitmap") == 0)
+       case UOPT_BITMAP:
                return RWH_BITMAP;
-       else if (strcmp(update, "no-bitmap") == 0)
+       case UOPT_NO_BITMAP:
                return RWH_OFF;
-       return -1;
+       default:
+               break;
+       }
+       return UOPT_UNDEFINED;
 }
 
 static int update_subarray_imsm(struct supertype *st, char *subarray,
-                               char *update, struct mddev_ident *ident)
+                               enum update_opt update, struct mddev_ident *ident)
 {
        /* update the subarray currently referenced by ->current_vol */
        struct intel_super *super = st->sb;
        struct imsm_super *mpb = super->anchor;
 
-       if (strcmp(update, "name") == 0) {
+       if (update == UOPT_NAME) {
                char *name = ident->name;
                char *ep;
                int vol;
 
-               if (is_subarray_active(subarray, st->devnm)) {
-                       pr_err("Unable to update name of active subarray\n");
-                       return 2;
-               }
-
-               if (!check_name(super, name, 0))
+               if (imsm_is_name_allowed(super, name, 1) == false)
                        return 2;
 
                vol = strtoul(subarray, &ep, 10);
@@ -7948,7 +7973,7 @@ static int update_subarray_imsm(struct supertype *st, char *subarray,
                        }
                        super->updates_pending++;
                }
-       } else if (get_rwh_policy_from_update(update) != -1) {
+       } else if (get_rwh_policy_from_update(update) != UOPT_UNDEFINED) {
                int new_policy;
                char *ep;
                int vol = strtoul(subarray, &ep, 10);
@@ -8080,9 +8105,9 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
        int current_vol = super->current_vol;
 
        /* do not assemble arrays when not all attributes are supported */
-       if (imsm_check_attributes(mpb->attributes) == 0) {
+       if (imsm_check_attributes(mpb->attributes) == false) {
                sb_errors = 1;
-               pr_err("Unsupported attributes in IMSM metadata.Arrays activation is blocked.\n");
+               pr_err("Unsupported attributes in IMSM metadata. Arrays activation is blocked.\n");
        }
 
        /* count spare devices, not used in maps
@@ -8216,7 +8241,7 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                        info_d->data_offset = pba_of_lba0(map);
                        info_d->component_size = calc_component_size(map, dev);
 
-                       if (map->raid_level == 5) {
+                       if (map->raid_level == IMSM_T_RAID5) {
                                info_d->ppl_sector = this->ppl_sector;
                                info_d->ppl_size = this->ppl_size;
                                if (this->consistency_policy == CONSISTENCY_POLICY_PPL &&
@@ -8639,23 +8664,6 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                        /* still reshaping, maybe update vol_curr_migr_unit */
                        goto mark_checkpoint;
                } else {
-                       if (a->last_checkpoint == 0 && a->prev_action == reshape) {
-                               /* for some reason we aborted the reshape.
-                                *
-                                * disable automatic metadata rollback
-                                * user action is required to recover process
-                                */
-                               if (0) {
-                                       struct imsm_map *map2 =
-                                               get_imsm_map(dev, MAP_1);
-                                       dev->vol.migr_state = 0;
-                                       set_migr_type(dev, 0);
-                                       set_vol_curr_migr_unit(dev, 0);
-                                       memcpy(map, map2,
-                                              sizeof_imsm_map(map2));
-                                       super->updates_pending++;
-                               }
-                       }
                        if (a->last_checkpoint >= a->info.component_size) {
                                unsigned long long array_blocks;
                                int used_disks;
@@ -8719,6 +8727,9 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                super->updates_pending++;
        }
 
+       if (a->prev_action == idle)
+               goto skip_mark_checkpoint;
+
 mark_checkpoint:
        /* skip checkpointing for general migration,
         * it is controlled in mdadm
@@ -9488,7 +9499,7 @@ static int apply_reshape_migration_update(struct imsm_update_reshape_migration *
                        }
 
                        to_state = map->map_state;
-                       if ((u->new_level == 5) && (map->raid_level == 0)) {
+                       if ((u->new_level == IMSM_T_RAID5) && (map->raid_level == IMSM_T_RAID0)) {
                                map->num_members++;
                                /* this should not happen */
                                if (u->new_disks[0] < 0) {
@@ -9499,11 +9510,13 @@ static int apply_reshape_migration_update(struct imsm_update_reshape_migration *
                                        to_state = IMSM_T_STATE_NORMAL;
                        }
                        migrate(new_dev, super, to_state, MIGR_GEN_MIGR);
+
                        if (u->new_level > -1)
-                               map->raid_level = u->new_level;
+                               update_imsm_raid_level(map, u->new_level);
+
                        migr_map = get_imsm_map(new_dev, MAP_1);
-                       if ((u->new_level == 5) &&
-                           (migr_map->raid_level == 0)) {
+                       if ((u->new_level == IMSM_T_RAID5) &&
+                           (migr_map->raid_level == IMSM_T_RAID0)) {
                                int ord = map->num_members - 1;
                                migr_map->num_members--;
                                if (u->new_disks[0] < 0)
@@ -9539,7 +9552,7 @@ static int apply_reshape_migration_update(struct imsm_update_reshape_migration *
 
                        /* add disk
                         */
-                       if (u->new_level != 5 || migr_map->raid_level != 0 ||
+                       if (u->new_level != IMSM_T_RAID5 || migr_map->raid_level != IMSM_T_RAID0 ||
                            migr_map->raid_level == map->raid_level)
                                goto skip_disk_add;
 
@@ -9918,7 +9931,7 @@ static int apply_takeover_update(struct imsm_update_takeover *u,
                /* update map */
                map->num_members /= map->num_domains;
                map->map_state = IMSM_T_STATE_NORMAL;
-               map->raid_level = 0;
+               update_imsm_raid_level(map, IMSM_T_RAID0);
                set_num_domains(map);
                update_num_data_stripes(map, imsm_dev_size(dev));
                map->failed_disk_num = -1;
@@ -9962,7 +9975,7 @@ static int apply_takeover_update(struct imsm_update_takeover *u,
                map = get_imsm_map(dev_new, MAP_0);
 
                map->map_state = IMSM_T_STATE_DEGRADED;
-               map->raid_level = 1;
+               update_imsm_raid_level(map, IMSM_T_RAID10);
                set_num_domains(map);
                map->num_members = map->num_members * map->num_domains;
                update_num_data_stripes(map, imsm_dev_size(dev));
@@ -10246,7 +10259,8 @@ static void imsm_process_update(struct supertype *st,
                        if (a->info.container_member == target)
                                break;
                dev = get_imsm_dev(super, u->dev_idx);
-               if (a || !check_name(super, name, 1)) {
+
+               if (a || !dev || imsm_is_name_allowed(super, name, 0) == false) {
                        dprintf("failed to rename subarray-%d\n", target);
                        break;
                }
@@ -10688,7 +10702,7 @@ static int imsm_get_allowed_degradation(int level, int raid_disks,
  ******************************************************************************/
 int validate_container_imsm(struct mdinfo *info)
 {
-       if (check_env("IMSM_NO_PLATFORM"))
+       if (check_no_platform())
                return 0;
 
        struct sys_dev *idev;
@@ -11116,11 +11130,11 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
        unsigned int sector_size = super->sector_size;
        unsigned long long curr_migr_unit = current_migr_unit(migr_rec);
        unsigned long long num_migr_units = get_num_migr_units(migr_rec);
-       char buffer[20];
+       char buffer[SYSFS_MAX_BUF_SIZE];
        int skipped_disks = 0;
        struct dl *dl_disk;
 
-       err = sysfs_get_str(info, NULL, "array_state", (char *)buffer, 20);
+       err = sysfs_get_str(info, NULL, "array_state", (char *)buffer, sizeof(buffer));
        if (err < 1)
                return 1;
 
@@ -11206,39 +11220,247 @@ abort:
        return retval;
 }
 
-static char disk_by_path[] = "/dev/disk/by-path/";
-
-static const char *imsm_get_disk_controller_domain(const char *path)
-{
-       char disk_path[PATH_MAX];
-       char *drv=NULL;
-       struct stat st;
-
-       strcpy(disk_path, disk_by_path);
-       strncat(disk_path, path, PATH_MAX - strlen(disk_path) - 1);
-       if (stat(disk_path, &st) == 0) {
-               struct sys_dev* hba;
-               char *path;
-
-               path = devt_to_devpath(st.st_rdev, 1, NULL);
-               if (path == NULL)
-                       return "unknown";
-               hba = find_disk_attached_hba(-1, path);
-               if (hba && hba->type == SYS_DEV_SAS)
-                       drv = "isci";
-               else if (hba && hba->type == SYS_DEV_SATA)
-                       drv = "ahci";
-               else if (hba && hba->type == SYS_DEV_VMD)
-                       drv = "vmd";
-               else if (hba && hba->type == SYS_DEV_NVME)
-                       drv = "nvme";
-               else
-                       drv = "unknown";
-               dprintf("path: %s hba: %s attached: %s\n",
-                       path, (hba) ? hba->path : "NULL", drv);
-               free(path);
+/**
+ * test_and_add_drive_controller_policy_imsm() - add disk controller to policies list.
+ * @type: Policy type to search on list.
+ * @pols: List of currently recorded policies.
+ * @disk_fd: File descriptor of the device to check.
+ * @hba: The hba disk is attached, could be NULL if verification is disabled.
+ * @verbose: verbose flag.
+ *
+ * IMSM cares about drive physical placement. If @hba is not set, it adds unknown policy.
+ * If there is no controller policy on pols we are free to add first one. If there is a policy then,
+ * new must be the same - no controller mixing allowed.
+ */
+static mdadm_status_t
+test_and_add_drive_controller_policy_imsm(const char * const type, dev_policy_t **pols, int disk_fd,
+                                         struct sys_dev *hba, const int verbose)
+{
+       const char *controller_policy = get_sys_dev_type(SYS_DEV_UNKNOWN);
+       struct dev_policy *pol = pol_find(*pols, (char *)type);
+       char devname[MAX_RAID_SERIAL_LEN];
+
+       if (hba)
+               controller_policy = get_sys_dev_type(hba->type);
+
+       if (!pol) {
+               pol_add(pols, (char *)type, (char *)controller_policy, "imsm");
+               return MDADM_STATUS_SUCCESS;
+       }
+
+       if (strcmp(pol->value, controller_policy) == 0)
+               return MDADM_STATUS_SUCCESS;
+
+       fd2devname(disk_fd, devname);
+       pr_vrb("Intel(R) raid controller \"%s\" found for %s, but \"%s\" was detected earlier\n",
+              controller_policy, devname, pol->value);
+       pr_vrb("Disks under different controllers cannot be used, aborting\n");
+
+       return MDADM_STATUS_ERROR;
+}
+
+/**
+ * test_and_add_drive_encryption_policy_imsm() - add disk encryption to policies list.
+ * @type: policy type to search in the list.
+ * @pols: list of currently recorded policies.
+ * @disk_fd: file descriptor of the device to check.
+ * @hba: The hba to which the drive is attached, could be NULL if verification is disabled.
+ * @verbose: verbose flag.
+ *
+ * IMSM cares about drive encryption state. It is not allowed to mix disks with different
+ * encryption state within one md device.
+ * If there is no encryption policy on pols we are free to add first one.
+ * If there is a policy then, new must be the same.
+ */
+static mdadm_status_t
+test_and_add_drive_encryption_policy_imsm(const char * const type, dev_policy_t **pols, int disk_fd,
+                                         struct sys_dev *hba, const int verbose)
+{
+       struct dev_policy *expected_policy = pol_find(*pols, (char *)type);
+       struct encryption_information information = {0};
+       char *encryption_state = "Unknown";
+       int status = MDADM_STATUS_SUCCESS;
+       bool encryption_checked = true;
+       char devname[PATH_MAX];
+
+       if (!hba)
+               goto check_policy;
+
+       switch (hba->type) {
+       case SYS_DEV_NVME:
+       case SYS_DEV_VMD:
+               status = get_nvme_opal_encryption_information(disk_fd, &information, verbose);
+               break;
+       case SYS_DEV_SATA:
+       case SYS_DEV_SATA_VMD:
+               status = get_ata_encryption_information(disk_fd, &information, verbose);
+               break;
+       default:
+               encryption_checked = false;
+       }
+
+       if (status) {
+               fd2devname(disk_fd, devname);
+               pr_vrb("Failed to read encryption information of device %s\n", devname);
+               return MDADM_STATUS_ERROR;
+       }
+
+       if (encryption_checked) {
+               if (information.status == ENC_STATUS_LOCKED) {
+                       fd2devname(disk_fd, devname);
+                       pr_vrb("Device %s is in Locked state, cannot use. Aborting.\n", devname);
+                       return MDADM_STATUS_ERROR;
+               }
+               encryption_state = (char *)get_encryption_status_string(information.status);
+       }
+
+check_policy:
+       if (expected_policy) {
+               if (strcmp(expected_policy->value, encryption_state) == 0)
+                       return MDADM_STATUS_SUCCESS;
+
+               fd2devname(disk_fd, devname);
+               pr_vrb("Encryption status \"%s\" detected for disk %s, but \"%s\" status was detected earlier.\n",
+                      encryption_state, devname, expected_policy->value);
+               pr_vrb("Disks with different encryption status cannot be used.\n");
+               return MDADM_STATUS_ERROR;
+       }
+
+       pol_add(pols, (char *)type, encryption_state, "imsm");
+
+       return MDADM_STATUS_SUCCESS;
+}
+
+struct imsm_drive_policy {
+       char *type;
+       mdadm_status_t (*test_and_add_drive_policy)(const char * const type,
+                                                   struct dev_policy **pols, int disk_fd,
+                                                   struct sys_dev *hba, const int verbose);
+};
+
+struct imsm_drive_policy imsm_policies[] = {
+       {"controller", test_and_add_drive_controller_policy_imsm},
+       {"encryption", test_and_add_drive_encryption_policy_imsm}
+};
+
+mdadm_status_t test_and_add_drive_policies_imsm(struct dev_policy **pols, int disk_fd,
+                                               const int verbose)
+{
+       struct imsm_drive_policy *imsm_pol;
+       struct sys_dev *hba = NULL;
+       char path[PATH_MAX];
+       mdadm_status_t ret;
+       unsigned int i;
+
+       /* If imsm platform verification is disabled, do not search for hba. */
+       if (check_no_platform() != 1) {
+               if (!diskfd_to_devpath(disk_fd, 1, path)) {
+                       pr_vrb("IMSM: Failed to retrieve device path by file descriptor.\n");
+                       return MDADM_STATUS_ERROR;
+               }
+
+               hba = find_disk_attached_hba(disk_fd, path);
+               if (!hba) {
+                       pr_vrb("IMSM: Failed to find hba for %s\n", path);
+                       return MDADM_STATUS_ERROR;
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(imsm_policies); i++) {
+               imsm_pol = &imsm_policies[i];
+
+               ret = imsm_pol->test_and_add_drive_policy(imsm_pol->type, pols, disk_fd, hba,
+                                                         verbose);
+               if (ret != MDADM_STATUS_SUCCESS)
+                       /* Inherit error code */
+                       return ret;
        }
-       return drv;
+
+       return MDADM_STATUS_SUCCESS;
+}
+
+/**
+ * get_spare_criteria_imsm() - set spare criteria.
+ * @st: supertype.
+ * @mddev_path: path to md device devnode, it must be container.
+ * @c: spare_criteria struct to fill, not NULL.
+ *
+ * If superblock is not loaded, use mddev_path to load_container. It must be given in this case.
+ * Filles size and sector size accordingly to superblock.
+ */
+mdadm_status_t get_spare_criteria_imsm(struct supertype *st, char *mddev_path,
+                                      struct spare_criteria *c)
+{
+       mdadm_status_t ret = MDADM_STATUS_ERROR;
+       bool free_superblock = false;
+       unsigned long long size = 0;
+       struct intel_super *super;
+       struct extent *e;
+       struct dl *dl;
+       int i;
+
+       /* If no superblock and no mddev_path, we cannot load superblock. */
+       assert(st->sb || mddev_path);
+
+       if (mddev_path) {
+               int fd = open(mddev_path, O_RDONLY);
+               mdadm_status_t rv;
+
+               if (!is_fd_valid(fd))
+                       return MDADM_STATUS_ERROR;
+
+               if (!st->sb) {
+                       if (load_container_imsm(st, fd, st->devnm)) {
+                               close(fd);
+                               return MDADM_STATUS_ERROR;
+                       }
+                       free_superblock = true;
+               }
+
+               rv = mddev_test_and_add_drive_policies(st, &c->pols, fd, 0);
+               close(fd);
+
+               if (rv != MDADM_STATUS_SUCCESS)
+                       goto out;
+       }
+
+       super = st->sb;
+
+       /* find first active disk in array */
+       dl = super->disks;
+       while (dl && (is_failed(&dl->disk) || dl->index == -1))
+               dl = dl->next;
+
+       if (!dl)
+               goto out;
+
+       /* find last lba used by subarrays */
+       e = get_extents(super, dl, 0);
+       if (!e)
+               goto out;
+
+       for (i = 0; e[i].size; i++)
+               continue;
+       if (i > 0)
+               size = e[i - 1].start + e[i - 1].size;
+       free(e);
+
+       /* add the amount of space needed for metadata */
+       size += imsm_min_reserved_sectors(super);
+
+       c->min_size = size * 512;
+       c->sector_size = super->sector_size;
+       c->criteria_set = true;
+       ret = MDADM_STATUS_SUCCESS;
+
+out:
+       if (free_superblock)
+               free_super_imsm(st);
+
+       if (ret != MDADM_STATUS_SUCCESS)
+               c->criteria_set = false;
+
+       return ret;
 }
 
 static char *imsm_find_array_devnm_by_subdev(int subdev, char *container)
@@ -11369,10 +11591,15 @@ static int imsm_reshape_is_allowed_on_container(struct supertype *st,
  */
 static struct mdinfo *get_spares_for_grow(struct supertype *st)
 {
-       struct spare_criteria sc;
+       struct spare_criteria sc = {0};
+       struct mdinfo *spares;
+
+       get_spare_criteria_imsm(st, NULL, &sc);
+       spares = container_choose_spares(st, &sc, NULL, NULL, NULL, 0);
 
-       get_spare_criteria_imsm(st, &sc);
-       return container_choose_spares(st, &sc, NULL, NULL, NULL, 0);
+       dev_policy_free(sc.pols);
+
+       return spares;
 }
 
 /******************************************************************************
@@ -11568,6 +11795,96 @@ static void imsm_update_metadata_locally(struct supertype *st,
        }
 }
 
+/**
+ * imsm_analyze_expand() - check expand properties and calculate new size.
+ * @st: imsm supertype.
+ * @geo: new geometry params.
+ * @array: array info.
+ * @direction: reshape direction.
+ *
+ * Obtain free space after the &array and verify if expand to requested size is
+ * possible. If geo->size is set to %MAX_SIZE, assume that max free size is
+ * requested.
+ *
+ * Return:
+ * On success %IMSM_STATUS_OK is returned, geo->size and geo->raid_disks are
+ * updated.
+ * On error, %IMSM_STATUS_ERROR is returned.
+ */
+static imsm_status_t imsm_analyze_expand(struct supertype *st,
+                                        struct geo_params *geo,
+                                        struct mdinfo *array,
+                                        int direction)
+{
+       struct intel_super *super = st->sb;
+       struct imsm_dev *dev = get_imsm_dev(super, super->current_vol);
+       struct imsm_map *map = get_imsm_map(dev, MAP_0);
+       int data_disks = imsm_num_data_members(map);
+
+       unsigned long long current_size;
+       unsigned long long free_size;
+       unsigned long long new_size;
+       unsigned long long max_size;
+
+       const int chunk_kib = geo->chunksize / 1024;
+       imsm_status_t rv;
+
+       if (direction == ROLLBACK_METADATA_CHANGES) {
+               /**
+                * Accept size for rollback only.
+                */
+               new_size = geo->size * 2;
+               goto success;
+       }
+
+       if (data_disks == 0) {
+               pr_err("imsm: Cannot retrieve data disks.\n");
+               return IMSM_STATUS_ERROR;
+       }
+       current_size = array->custom_array_size / data_disks;
+
+       rv = imsm_get_free_size(super, dev->vol.map->num_members, 0, chunk_kib, &free_size, true);
+       if (rv != IMSM_STATUS_OK) {
+               pr_err("imsm: Cannot find free space for expand.\n");
+               return IMSM_STATUS_ERROR;
+       }
+       max_size = round_member_size_to_mb(free_size + current_size);
+
+       if (geo->size == MAX_SIZE)
+               new_size = max_size;
+       else
+               new_size = round_member_size_to_mb(geo->size * 2);
+
+       if (new_size == 0) {
+               pr_err("imsm: Rounded requested size is 0.\n");
+               return IMSM_STATUS_ERROR;
+       }
+
+       if (new_size > max_size) {
+               pr_err("imsm: Rounded requested size (%llu) is larger than free space available (%llu).\n",
+                      new_size, max_size);
+               return IMSM_STATUS_ERROR;
+       }
+
+       if (new_size == current_size) {
+               pr_err("imsm: Rounded requested size (%llu) is same as current size (%llu).\n",
+                      new_size, current_size);
+               return IMSM_STATUS_ERROR;
+       }
+
+       if (new_size < current_size) {
+               pr_err("imsm: Size reduction is not supported, rounded requested size (%llu) is smaller than current (%llu).\n",
+                      new_size, current_size);
+               return IMSM_STATUS_ERROR;
+       }
+
+success:
+       dprintf("imsm: New size per member is %llu.\n", new_size);
+       geo->size = data_disks * new_size;
+       geo->raid_disks = dev->vol.map->num_members;
+       return IMSM_STATUS_OK;
+}
+
 /***************************************************************************
 * Function:    imsm_analyze_change
 * Description: Function analyze change for single volume
@@ -11578,31 +11895,23 @@ static void imsm_update_metadata_locally(struct supertype *st,
 ****************************************************************************/
 enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                                           struct geo_params *geo,
-                                          int direction)
+                                          int direction, struct context *c)
 {
        struct mdinfo info;
        int change = -1;
        int check_devs = 0;
        int chunk;
-       /* number of added/removed disks in operation result */
-       int devNumChange = 0;
        /* imsm compatible layout value for array geometry verification */
        int imsm_layout = -1;
-       int data_disks;
-       struct imsm_dev *dev;
-       struct imsm_map *map;
-       struct intel_super *super;
-       unsigned long long current_size;
-       unsigned long long free_size;
-       unsigned long long max_size;
+       int raid_disks = geo->raid_disks;
        imsm_status_t rv;
 
        getinfo_super_imsm_volume(st, &info, NULL);
-       if (geo->level != info.array.level && geo->level >= 0 &&
+       if (geo->level != info.array.level && geo->level >= IMSM_T_RAID0 &&
            geo->level != UnSet) {
                switch (info.array.level) {
-               case 0:
-                       if (geo->level == 5) {
+               case IMSM_T_RAID0:
+                       if (geo->level == IMSM_T_RAID5) {
                                change = CH_MIGRATION;
                                if (geo->layout != ALGORITHM_LEFT_ASYMMETRIC) {
                                        pr_err("Error. Requested Layout not supported (left-asymmetric layout is supported only)!\n");
@@ -11611,20 +11920,28 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                                }
                                imsm_layout =  geo->layout;
                                check_devs = 1;
-                               devNumChange = 1; /* parity disk added */
-                       } else if (geo->level == 10) {
+                               raid_disks += 1; /* parity disk added */
+                       } else if (geo->level == IMSM_T_RAID10) {
+                               if (geo->level == IMSM_T_RAID10 && geo->raid_disks > 2 &&
+                                   !c->force) {
+                                       pr_err("Warning! VROC UEFI driver does not support RAID10 in requested layout.\n");
+                                       pr_err("Array won't be suitable as boot device.\n");
+                                       pr_err("Note: You can omit this check with \"--force\"\n");
+                                       if (ask("Do you want to continue") < 1)
+                                               return CH_ABORT;
+                               }
                                change = CH_TAKEOVER;
                                check_devs = 1;
-                               devNumChange = 2; /* two mirrors added */
+                               raid_disks *= 2; /* mirrors added */
                                imsm_layout = 0x102; /* imsm supported layout */
                        }
                        break;
-               case 1:
-               case 10:
+               case IMSM_T_RAID1:
+               case IMSM_T_RAID10:
                        if (geo->level == 0) {
                                change = CH_TAKEOVER;
                                check_devs = 1;
-                               devNumChange = -(geo->raid_disks/2);
+                               raid_disks /= 2;
                                imsm_layout = 0; /* imsm raid0 layout */
                        }
                        break;
@@ -11640,10 +11957,10 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
        if (geo->layout != info.array.layout &&
            (geo->layout != UnSet && geo->layout != -1)) {
                change = CH_MIGRATION;
-               if (info.array.layout == 0 && info.array.level == 5 &&
+               if (info.array.layout == 0 && info.array.level == IMSM_T_RAID5 &&
                    geo->layout == 5) {
                        /* reshape 5 -> 4 */
-               } else if (info.array.layout == 5 && info.array.level == 5 &&
+               } else if (info.array.layout == 5 && info.array.level == IMSM_T_RAID5 &&
                           geo->layout == 0) {
                        /* reshape 4 -> 5 */
                        geo->layout = 0;
@@ -11662,7 +11979,7 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
 
        if (geo->chunksize > 0 && geo->chunksize != UnSet &&
            geo->chunksize != info.array.chunk_size) {
-               if (info.array.level == 10) {
+               if (info.array.level == IMSM_T_RAID10) {
                        pr_err("Error. Chunk size change for RAID 10 is not supported.\n");
                        change = -1;
                        goto analyse_change_exit;
@@ -11677,99 +11994,26 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                geo->chunksize = info.array.chunk_size;
        }
 
-       chunk = geo->chunksize / 1024;
-
-       super = st->sb;
-       dev = get_imsm_dev(super, super->current_vol);
-       map = get_imsm_map(dev, MAP_0);
-       data_disks = imsm_num_data_members(map);
-       /* compute current size per disk member
-        */
-       current_size = info.custom_array_size / data_disks;
-
-       if (geo->size > 0 && geo->size != MAX_SIZE) {
-               /* align component size
-                */
-               geo->size = imsm_component_size_alignment_check(
-                                   get_imsm_raid_level(dev->vol.map),
-                                   chunk * 1024, super->sector_size,
-                                   geo->size * 2);
-               if (geo->size == 0) {
-                       pr_err("Error. Size expansion is supported only (current size is %llu, requested size /rounded/ is 0).\n",
-                                  current_size);
-                       goto analyse_change_exit;
-               }
-       }
-
-       if (current_size != geo->size && geo->size > 0) {
+       if (geo->size > 0) {
                if (change != -1) {
                        pr_err("Error. Size change should be the only one at a time.\n");
                        change = -1;
                        goto analyse_change_exit;
                }
-               if ((super->current_vol + 1) != super->anchor->num_raid_devs) {
-                       pr_err("Error. The last volume in container can be expanded only (%i/%s).\n",
-                              super->current_vol, st->devnm);
-                       goto analyse_change_exit;
-               }
-               /* check the maximum available size
-                */
-               rv = imsm_get_free_size(super, dev->vol.map->num_members,
-                                       0, chunk, &free_size);
 
+               rv = imsm_analyze_expand(st, geo, &info, direction);
                if (rv != IMSM_STATUS_OK)
-                       /* Cannot find maximum available space
-                        */
-                       max_size = 0;
-               else {
-                       max_size = free_size + current_size;
-                       /* align component size
-                        */
-                       max_size = imsm_component_size_alignment_check(
-                                       get_imsm_raid_level(dev->vol.map),
-                                       chunk * 1024, super->sector_size,
-                                       max_size);
-               }
-               if (geo->size == MAX_SIZE) {
-                       /* requested size change to the maximum available size
-                        */
-                       if (max_size == 0) {
-                               pr_err("Error. Cannot find maximum available space.\n");
-                               change = -1;
-                               goto analyse_change_exit;
-                       } else
-                               geo->size = max_size;
-               }
-
-               if (direction == ROLLBACK_METADATA_CHANGES) {
-                       /* accept size for rollback only
-                       */
-               } else {
-                       /* round size due to metadata compatibility
-                       */
-                       geo->size = (geo->size >> SECT_PER_MB_SHIFT)
-                                   << SECT_PER_MB_SHIFT;
-                       dprintf("Prepare update for size change to %llu\n",
-                               geo->size );
-                       if (current_size >= geo->size) {
-                               pr_err("Error. Size expansion is supported only (current size is %llu, requested size /rounded/ is %llu).\n",
-                                      current_size, geo->size);
-                               goto analyse_change_exit;
-                       }
-                       if (max_size && geo->size > max_size) {
-                               pr_err("Error. Requested size is larger than maximum available size (maximum available size is %llu, requested size /rounded/ is %llu).\n",
-                                      max_size, geo->size);
-                               goto analyse_change_exit;
-                       }
-               }
-               geo->size *= data_disks;
-               geo->raid_disks = dev->vol.map->num_members;
+                       goto analyse_change_exit;
+               raid_disks = geo->raid_disks;
                change = CH_ARRAY_SIZE;
        }
+
+       chunk = geo->chunksize / 1024;
+
        if (!validate_geometry_imsm(st,
                                    geo->level,
                                    imsm_layout,
-                                   geo->raid_disks + devNumChange,
+                                   raid_disks,
                                    &chunk,
                                    geo->size, INVALID_SECTORS,
                                    0, 0, info.consistency_policy, 1))
@@ -11885,28 +12129,37 @@ exit:
        return ret_val;
 }
 
-static int imsm_reshape_super(struct supertype *st, unsigned long long size,
-                             int level,
-                             int layout, int chunksize, int raid_disks,
-                             int delta_disks, char *backup, char *dev,
-                             int direction, int verbose)
+/**
+ * shape_to_geo() - fill geo_params from shape.
+ *
+ * @shape: array details.
+ * @geo: new geometry params.
+ * Returns: 0 on success, 1 otherwise.
+ */
+static void shape_to_geo(struct shape *shape, struct geo_params *geo)
+{
+       assert(shape);
+       assert(geo);
+
+       geo->dev_name = shape->dev;
+       geo->size = shape->size;
+       geo->level = shape->level;
+       geo->layout = shape->layout;
+       geo->chunksize = shape->chunk;
+       geo->raid_disks = shape->raiddisks;
+}
+
+static int imsm_reshape_super(struct supertype *st, struct shape *shape, struct context *c)
 {
        int ret_val = 1;
-       struct geo_params geo;
+       struct geo_params geo = {0};
 
        dprintf("(enter)\n");
 
-       memset(&geo, 0, sizeof(struct geo_params));
-
-       geo.dev_name = dev;
+       shape_to_geo(shape, &geo);
        strcpy(geo.devnm, st->devnm);
-       geo.size = size;
-       geo.level = level;
-       geo.layout = layout;
-       geo.chunksize = chunksize;
-       geo.raid_disks = raid_disks;
-       if (delta_disks != UnSet)
-               geo.raid_disks += delta_disks;
+       if (shape->delta_disks != UnSet)
+               geo.raid_disks += shape->delta_disks;
 
        dprintf("for level      : %i\n", geo.level);
        dprintf("for raid_disks : %i\n", geo.raid_disks);
@@ -11917,7 +12170,7 @@ static int imsm_reshape_super(struct supertype *st, unsigned long long size,
                int old_raid_disks = 0;
 
                if (imsm_reshape_is_allowed_on_container(
-                           st, &geo, &old_raid_disks, direction)) {
+                           st, &geo, &old_raid_disks, shape->direction)) {
                        struct imsm_update_reshape *u = NULL;
                        int len;
 
@@ -11971,7 +12224,7 @@ static int imsm_reshape_super(struct supertype *st, unsigned long long size,
                        goto exit_imsm_reshape_super;
                }
                super->current_vol = dev->index;
-               change = imsm_analyze_change(st, &geo, direction);
+               change = imsm_analyze_change(st, &geo, shape->direction, c);
                switch (change) {
                case CH_TAKEOVER:
                        ret_val = imsm_takeover(st, &geo);
@@ -12014,6 +12267,7 @@ static int imsm_reshape_super(struct supertype *st, unsigned long long size,
                                free(u);
                }
                break;
+               case CH_ABORT:
                default:
                        ret_val = 1;
                }
@@ -12031,14 +12285,14 @@ exit_imsm_reshape_super:
 static int read_completed(int fd, unsigned long long *val)
 {
        int ret;
-       char buf[50];
+       char buf[SYSFS_MAX_BUF_SIZE];
 
-       ret = sysfs_fd_get_str(fd, buf, 50);
+       ret = sysfs_fd_get_str(fd, buf, sizeof(buf));
        if (ret < 0)
                return ret;
 
        ret = COMPLETED_OK;
-       if (strncmp(buf, "none", 4) == 0) {
+       if (str_is_none(buf) == true) {
                ret = COMPLETED_NONE;
        } else if (strncmp(buf, "delayed", 7) == 0) {
                ret = COMPLETED_DELAYED;
@@ -12106,12 +12360,12 @@ int wait_for_reshape_imsm(struct mdinfo *sra, int ndata)
 
        do {
                int rc;
-               char action[20];
+               char action[SYSFS_MAX_BUF_SIZE];
                int timeout = 3000;
 
                sysfs_wait(fd, &timeout);
                if (sysfs_get_str(sra, NULL, "sync_action",
-                                 action, 20) > 0 &&
+                                 action, sizeof(action)) > 0 &&
                                strncmp(action, "reshape", 7) != 0) {
                        if (strncmp(action, "idle", 4) == 0)
                                break;
@@ -12158,7 +12412,7 @@ int check_degradation_change(struct mdinfo *info,
                        if (sd->disk.state & (1<<MD_DISK_FAULTY))
                                continue;
                        if (sd->disk.state & (1<<MD_DISK_SYNC)) {
-                               char sbuf[100];
+                               char sbuf[SYSFS_MAX_BUF_SIZE];
                                int raid_disk = sd->disk.raid_disk;
 
                                if (sysfs_get_str(info,
@@ -12926,7 +13180,7 @@ struct superswitch super_imsm = {
        .update_subarray = update_subarray_imsm,
        .load_container = load_container_imsm,
        .default_geometry = default_geometry_imsm,
-       .get_disk_controller_domain = imsm_get_disk_controller_domain,
+       .test_and_add_drive_policies = test_and_add_drive_policies_imsm,
        .reshape_super  = imsm_reshape_super,
        .manage_reshape = imsm_manage_reshape,
        .recover_backup = recover_backup_imsm,
@@ -12959,6 +13213,7 @@ struct superswitch super_imsm = {
        .validate_ppl   = validate_ppl_imsm,
 
        .external       = 1,
+       .swapuuid       = 0,
        .name = "imsm",
 
 /* for mdmon */