Probe isw disks with [some] HPA awareness, thanks to Robert Collins <robert@ubuntu.com>. (LP: #372170)
--- dmraid/lib/format/ataraid/isw.c
+++ dmraid/lib/format/ataraid/isw.c
@@ -353,6 +353,7 @@ raiddev(struct isw *isw, unsigned i)
 enum convert { FULL, FIRST, LAST };
 #if	BYTE_ORDER == LITTLE_ENDIAN
 #  define	to_cpu(x, y)
+#  define       CVT16(x)
 #else
 /*
  * We can differ from the read_raid_dev template here,
@@ -547,15 +548,16 @@ disk_ok(struct lib_context *lc, struct d
 }
 
 static void *
-isw_read_metadata(struct lib_context *lc, struct dev_info *di,
-		  size_t * sz, uint64_t * offset, union read_info *info)
+isw_try_sboffset(struct lib_context *lc, struct dev_info *di,
+		 size_t * sz, uint64_t * offset, union read_info *info,
+		 uint64_t const isw_sboffset)
 {
 	size_t size = ISW_DISK_BLOCK_SIZE;
-	uint64_t isw_sboffset = ISW_CONFIGOFFSET(di);
 	struct isw *isw;
+	uint64_t temp_isw_sboffset = isw_sboffset;
 
 	if (!(isw = alloc_private_and_read(lc, handler, size,
-					   di->path, isw_sboffset)))
+					   di->path, temp_isw_sboffset)))
 		goto out;
 
 	/*
@@ -565,9 +567,15 @@ isw_read_metadata(struct lib_context *lc
 	to_cpu(isw, FIRST);
 
 	/* Check Signature and read optional extended metadata. */
-	if (!is_isw(lc, di, isw) ||
-	    !isw_read_extended(lc, di, &isw, &isw_sboffset, &size))
+	if (!is_isw(lc, di, isw)) {
+		log_dbg(lc, "not isw at %ld", isw_sboffset);
 		goto bad;
+	}
+	if (!isw_read_extended(lc, di, &isw, &temp_isw_sboffset, &size)) {
+		log_err(lc, "isw metadata, but extended read failed at %ld",
+			isw_sboffset);
+		goto bad;
+	}
 
 	/*
 	 * Now that we made sure, that we've got all the
@@ -578,6 +586,8 @@ isw_read_metadata(struct lib_context *lc
 	if (disk_ok(lc, di, isw)) {
 		*sz = size;
 		*offset = info->u64 = isw_sboffset;
+		log_dbg(lc, "isw metadata found at %ld from probe at %ld\n",
+			*offset, isw_sboffset);
 		goto out;
 	}
 
@@ -589,6 +599,54 @@ out:
 	return (void *) isw;
 }
 
+/* HPA on a disk shifts everything down. This is a 'works-enough' approach to
+ * handling that. There is a better long term approach requiring kernel
+ * patches - see the lkml patches for alt_size.
+ */
+static void *
+isw_try_hpa(struct lib_context *lc, struct dev_info *di,
+	   size_t * sz, uint64_t * offset, union read_info *info)
+{
+	struct isw10 *isw10;
+	void *result = NULL;
+	uint64_t actual_offset;
+	if (!(isw10 = alloc_private_and_read(lc, handler, ISW_DISK_BLOCK_SIZE,
+		di->path, ISW_10_CONFIGOFFSET(di))))
+		goto out;
+	if (strncmp((const char *)isw10->sig, ISW10_SIGNATURE, ISW10_SIGNATURE_SIZE))
+		goto out_free;
+	log_dbg(lc, "Found isw 10 gafr signature.");
+	CVT16(isw10->offset);
+	actual_offset = isw10->offset + 2;
+	log_dbg(lc, "isw 10 sector offset calculated at %hd.", actual_offset);
+	if (actual_offset > di->sectors)
+		goto out_free;
+	result = isw_try_sboffset(lc, di, sz, offset, info,
+		ISW_SECTOR_TO_OFFSET(di->sectors - actual_offset));
+      out_free:
+	dbg_free(isw10);
+      out:
+	return result;
+}
+
+
+static void *
+isw_read_metadata(struct lib_context *lc, struct dev_info *di,
+		  size_t * sz, uint64_t * offset, union read_info *info)
+{
+	void *result;
+	if ((result = isw_try_sboffset(
+		lc, di, sz, offset, info, ISW_CONFIGOFFSET(di))))
+		return result;
+	if ((result = isw_try_hpa(lc, di, sz, offset, info)))
+		return result;
+        log_dbg(lc, "isw trying hard coded -2115 offset.");
+	if ((result = isw_try_sboffset(
+		lc, di, sz, offset, info, (di->sectors - 2115)*512)))
+		return result;
+	return NULL;
+}
+
 static int setup_rd(struct lib_context *lc, struct raid_dev *rd,
 		    struct dev_info *di, void *meta, union read_info *info);
 static struct raid_dev *
--- dmraid/lib/format/ataraid/isw.h
+++ dmraid/lib/format/ataraid/isw.h
@@ -36,8 +36,11 @@
 
 /* Intel metadata offset in bytes */
 #define	ISW_CONFIGSECTOR(di)	((di)->sectors - 2)
-#define	ISW_CONFIGOFFSET(di)	(ISW_CONFIGSECTOR(di) << 9)
+#define ISW_SECTOR_TO_OFFSET(sector) ((sector) << 9)
+#define	ISW_CONFIGOFFSET(di)	(ISW_SECTOR_TO_OFFSET(ISW_CONFIGSECTOR(di)))
 #define	ISW_DATAOFFSET		0	/* Data offset in sectors */
+#define ISW_10_CONFIGSECTOR(di) ((di)->sectors - 1)
+#define ISW_10_CONFIGOFFSET(di) (ISW_SECTOR_TO_OFFSET(ISW_10_CONFIGSECTOR(di)))
 
 #define MPB_SIGNATURE	     "Intel Raid ISM Cfg Sig. "
 #define MPB_SIGNATURE_SIZE	(sizeof(MPB_SIGNATURE) - 1)
@@ -222,6 +225,14 @@ struct isw {
 	// here comes isw_dev[num_raid_devs]
 } __attribute__ ((packed));
 
+#define ISW10_SIGNATURE "$GAFR\x10"
+#define ISW10_SIGNATURE_SIZE (sizeof(ISW10_SIGNATURE) - 1)
+struct isw10 {
+	int8_t sig[ISW10_SIGNATURE_SIZE];
+	uint32_t offset; /* offset to real data, in sectors back */
+} __attribute__ ((packed));
+
+
 #endif
 
 int register_isw(struct lib_context *lc);
