summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorscottl <scottl@FreeBSD.org>2009-02-20 06:10:12 +0000
committerscottl <scottl@FreeBSD.org>2009-02-20 06:10:12 +0000
commit261fc57b267bcc31fc486f94e6472a0c30fa457f (patch)
treea882b5be9baa8c318d36a317d809d9a12050e32d
parentc0f13aca4f82041b9d69bedf27f4211b3f75d0d9 (diff)
downloadFreeBSD-src-261fc57b267bcc31fc486f94e6472a0c30fa457f.zip
FreeBSD-src-261fc57b267bcc31fc486f94e6472a0c30fa457f.tar.gz
Add basic support for DDF, often found on Adaptec HostRAID controllers.
Spares and rebuilds are not supported, so this code should be considered for entertainment purposes only.
-rw-r--r--sys/dev/ata/ata-raid-ddf.h333
-rw-r--r--sys/dev/ata/ata-raid.c405
-rw-r--r--sys/dev/ata/ata-raid.h6
3 files changed, 743 insertions, 1 deletions
diff --git a/sys/dev/ata/ata-raid-ddf.h b/sys/dev/ata/ata-raid-ddf.h
new file mode 100644
index 0000000..30326bc
--- /dev/null
+++ b/sys/dev/ata/ata-raid-ddf.h
@@ -0,0 +1,333 @@
+/*-
+ * Copyright (c) 2008 Scott Long
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ATA_RAID_DDF_H
+#define ATA_RAID_DDF_H
+
+/* Definitions from the SNIA DDF spec, rev 1.2 */
+
+#define DDF_HEADER_LENGTH 512
+struct ddf_header {
+ uint32_t Signature;
+#define DDF_HEADER_SIGNATURE 0xde11de11
+ uint32_t CRC;
+ uint8_t DDF_Header_GUID[24];
+ uint8_t DDF_rev[8];
+ uint32_t Sequence_Number;
+ uint32_t TimeStamp;
+ uint8_t Open_Flag;
+#define DDF_HEADER_CLOSED 0x00
+#define DDF_HEADER_OPENED_MASK 0x0f
+#define DDF_HEADER_OPEN_ANCHOR 0xff
+ uint8_t Foreign_Flag;
+ uint8_t Diskgrouping;
+ uint8_t pad1[13];
+ uint8_t Header_ext[32];
+ uint64_t Primary_Header_LBA;
+ uint64_t Secondary_Header_LBA;
+ uint8_t Header_Type;
+#define DDF_HEADER_ANCHOR 0x00
+#define DDF_HEADER_PRIMARY 0x01
+#define DDF_HEADER_SECONDARY 0x02
+ uint8_t pad2[3];
+ uint32_t WorkSpace_Length;
+ uint64_t WorkSpace_LBA;
+ uint16_t Max_PD_Entries;
+ uint16_t Max_VD_Entries;
+ uint16_t Max_Partitions;
+ uint16_t Configuration_Record_Length;
+ uint16_t Max_Primary_Element_Entries;
+ uint8_t pad3[54];
+ uint32_t cd_section; /* Controller_Data_Section */
+ uint32_t cd_length; /* Controller_Data_Section_Length */
+ uint32_t pdr_section; /* Physical_Drive_Records_Section */
+ uint32_t pdr_length; /* Physical_Drive_Records_Length */
+ uint32_t vdr_section; /* Virtual_Drive_Records_Section */
+ uint32_t vdr_length; /* Virtual_Drive_Records_Length */
+ uint32_t cr_section; /* Configuration_Records_Section */
+ uint32_t cr_length; /* Configuration_Records_Length */
+ uint32_t pdd_section; /* Physical_Drive_Data_Section */
+ uint32_t pdd_length; /* Physical_Drive_Data_Length */
+ uint32_t bbmlog_section; /* BBM_Log_Section */
+ uint32_t bbmlog_length; /* BBM_Log_Section_Length */
+ uint32_t Diagnostic_Space;
+ uint32_t Diagnostic_Space_Length;
+ uint32_t Vendor_Specific_Logs;
+ uint32_t Vendor_Specific_Logs_Length;
+ uint8_t pad4[256];
+} __packed;
+
+struct ddf_cd_record {
+ uint32_t Signature;
+#define DDF_CONTROLLER_DATA_SIGNATURE 0xad111111
+ uint32_t CRC;
+ uint8_t Controller_GUID[24];
+ struct {
+ uint16_t Vendor_ID;
+ uint16_t Device_ID;
+ uint16_t SubVendor_ID;
+ uint16_t SubDevice_ID;
+ } Controller_Type __packed;
+ uint8_t Product_ID[16];
+ uint8_t pad1[8];
+ uint8_t Controller_Data[448];
+} __packed;
+
+struct ddf_device_scsi {
+ uint8_t Lun;
+ uint8_t Id;
+ uint8_t Channel;
+ uint8_t Path_Flags;
+#define DDF_DEVICE_SCSI_FLAG_BROKEN (1 << 7)
+} __packed;
+
+struct ddf_device_sas {
+ uint64_t Initiator_Path;
+} __packed;
+
+union ddf_pathinfo {
+ struct {
+ struct ddf_device_scsi Path0;
+ struct ddf_device_scsi Path1;
+ uint8_t pad[10];
+ } __packed scsi;
+ struct {
+ struct ddf_device_sas Path0;
+ struct ddf_device_sas Path1;
+ uint8_t Path0_Flags;
+ uint8_t Path1_Flags;
+#define DDF_DEVICE_SAS_FLAG_BROKEN (1 << 7)
+ } __packed sas;
+} __packed;
+
+struct ddf_pd_entry {
+ uint8_t PD_GUID[24];
+ uint32_t PD_Reference;
+ uint16_t PD_Type;
+#define DDF_PDE_GUID_FORCE (1 << 0)
+#define DDF_PDE_PARTICIPATING (1 << 1)
+#define DDF_PDE_GLOBAL_SPARE (1 << 2)
+#define DDF_PDE_CONFIG_SPARE (1 << 3)
+#define DDF_PDE_FOREIGN (1 << 4)
+#define DDF_PDE_LEGACY (1 << 5)
+#define DDF_PDE_TYPE_MASK (0x0f << 12)
+#define DDF_PDE_UNKNOWN (0x00 << 12)
+#define DDF_PDE_SCSI (0x01 << 12)
+#define DDF_PDE_SAS (0x02 << 12)
+#define DDF_PDE_SATA (0x03 << 12)
+#define DDF_PDE_FC (0x04 << 12)
+ uint16_t PD_State;
+#define DDF_PDE_ONLINE (1 << 0)
+#define DDF_PDE_FAILED (1 << 1)
+#define DDF_PDE_REBUILD (1 << 2)
+#define DDF_PDE_TRANSITION (1 << 3)
+#define DDF_PDE_PFA (1 << 4)
+#define DDF_PDE_UNRECOVERED (1 << 5)
+#define DDF_PDE_MISSING (1 << 6)
+ uint64_t Configured_Size;
+ union ddf_pathinfo Path_Information;
+ uint8_t pad1[6];
+} __packed;
+
+struct ddf_pd_record {
+ uint32_t Signature;
+#define DDF_PDR_SIGNATURE 0x22222222
+ uint32_t CRC;
+ uint16_t Populated_PDEs;
+ uint16_t Max_PDE_Supported;
+ uint8_t pad1[52];
+ struct ddf_pd_entry entry[0];
+} __packed;
+
+struct ddf_vd_entry {
+ uint8_t VD_GUID[24];
+ uint16_t VD_Number;
+ uint8_t pad1[2];
+ uint16_t VD_Type;
+#define DDF_VDE_SHARED (1 << 0)
+#define DDF_VDE_ENFORCE_GROUP (1 << 1)
+#define DDF_VDE_UNICODE_NAME (1 << 2)
+#define DDF_VDE_OWNER_ID_VALID (1 << 3)
+ uint16_t Controller_GUID_CRC;
+ uint8_t VD_State;
+#define DDF_VDE_OPTIMAL 0x00
+#define DDF_VDE_DEGRADED 0x01
+#define DDF_VDE_DELETED 0x02
+#define DDF_VDE_MISSING 0x03
+#define DDF_VDE_FAILED 0x04
+#define DDF_VDE_PARTIAL 0x05
+#define DDF_VDE_STATE_MASK 0x07
+#define DDF_VDE_MORPH (1 << 3)
+#define DDF_VDE_DIRTY (1 << 4)
+ uint8_t Init_State;
+#define DDF_VDE_UNINTIALIZED 0x00
+#define DDF_VDE_INIT_QUICK 0x01
+#define DDF_VDE_INIT_FULL 0x02
+#define DDF_VDE_INIT_MASK 0x03
+#define DDF_VDE_UACCESS_RW 0x00
+#define DDF_VDE_UACCESS_RO 0x80
+#define DDF_VDE_UACCESS_BLOCKED 0xc0
+#define DDF_VDE_UACCESS_MASK 0xc0
+ uint8_t pad2[14];
+ uint8_t VD_Name[16];
+} __packed;
+
+struct ddf_vd_record {
+ uint32_t Signature;
+#define DDF_VD_RECORD_SIGNATURE 0xdddddddd
+ uint32_t CRC;
+ uint16_t Populated_VDEs;
+ uint16_t Max_VDE_Supported;
+ uint8_t pad1[52];
+ struct ddf_vd_entry entry[0];
+} __packed;
+
+#define DDF_CR_INVALID 0xffffffff
+
+struct ddf_vdc_record {
+ uint32_t Signature;
+#define DDF_VDCR_SIGNATURE 0xeeeeeeee
+ uint32_t CRC;
+ uint8_t VD_GUID[24];
+ uint32_t Timestamp;
+ uint32_t Sequence_Number;
+ uint8_t pad1[24];
+ uint16_t Primary_Element_Count;
+ uint8_t Stripe_Size;
+ uint8_t Primary_RAID_Level;
+#define DDF_VDCR_RAID0 0x00
+#define DDF_VDCR_RAID1 0x01
+#define DDF_VDCR_RAID3 0x03
+#define DDF_VDCR_RAID4 0x04
+#define DDF_VDCR_RAID5 0x05
+#define DDF_VDCR_RAID6 0x06
+#define DDF_VDCR_RAID1E 0x11
+#define DDF_VDCR_SINGLE 0x0f
+#define DDF_VDCR_CONCAT 0x1f
+#define DDF_VDCR_RAID5E 0x15
+#define DDF_VDCR_RAID5EE 0x25
+ uint8_t RLQ;
+ uint8_t Secondary_Element_Count;
+ uint8_t Secondary_Element_Seq;
+ uint8_t Secondary_RAID_Level;
+ uint64_t Block_Count;
+ uint64_t VD_Size;
+ uint8_t pad2[8];
+ uint32_t Associated_Spares[8];
+ uint64_t Cache_Flags;
+#define DDF_VDCR_CACHE_WB (1 << 0)
+#define DDF_VDCR_CACHE_WB_ADAPTIVE (1 << 1)
+#define DDF_VDCR_CACHE_RA (1 << 2)
+#define DDF_VDCR_CACHE_RA_ADAPTIVE (1 << 3)
+#define DDF_VDCR_CACHE_WCACHE_NOBATTERY (1 << 4)
+#define DDF_VDCR_CACHE_WCACHE_ALLOW (1 << 5)
+#define DDF_VDCR_CACHE_RCACHE_ALLOW (1 << 6)
+#define DDF_VDCR_CACHE_VENDOR (1 << 7)
+ uint8_t BG_Rate;
+ uint8_t pad3[3];
+ uint8_t pad4[52];
+ uint8_t pad5[192];
+ uint8_t V0[32];
+ uint8_t V1[32];
+ uint8_t V2[16];
+ uint8_t V3[16];
+ uint8_t Vendor_Scratch[32];
+ uint32_t Physical_Disk_Sequence[0];
+} __packed;
+
+struct ddf_vuc_record {
+ uint32_t Signature;
+#define DDF_VUCR_SIGNATURE 0x88888888
+ uint32_t CRC;
+ uint8_t VD_GUID[24];
+} __packed;
+
+struct ddf_sa_entry {
+ uint8_t VD_GUID[24];
+ uint16_t Secondary_Element;
+ uint8_t rsrvd2[6];
+} __packed;
+
+struct ddf_sa_record {
+ uint32_t Signature;
+#define DDF_SA_SIGNATURE 0x55555555
+ uint32_t CRC;
+ uint32_t Timestamp;
+ uint8_t pad1[7];
+ uint8_t Spare_Type;
+#define DDF_SAR_TYPE_DEDICATED (1 << 0)
+#define DDF_SAR_TYPE_REVERTIBLE (1 << 1)
+#define DDF_SAR_TYPE_ACTIVE (1 << 2)
+#define DDF_SAR_TYPE_ENCL_AFFINITY (1 << 3)
+ uint16_t Populated_SAEs;
+ uint16_t MAX_SAE_Supported;
+ uint8_t pad2[8];
+ struct ddf_sa_entry entry[0];
+} __packed;
+
+struct ddf_pdd_record {
+ uint32_t Signature;
+#define DDF_PDD_SIGNATURE 0x33333333
+ uint32_t CRC;
+ uint8_t PD_GUID[24];
+ uint32_t PD_Reference;
+ uint8_t Forced_Ref_Flag;
+#define DDF_PDD_FORCED_REF 0x01
+ uint8_t Forced_PD_GUID_Flag;
+#define DDF_PDD_FORCED_GUID 0x01
+ uint8_t Vendor_Scratch[32];
+ uint8_t pad2[442];
+} __packed;
+
+struct ddf_bbm_entry {
+ uint64_t Defective_Block_Start;
+ uint32_t Spare_Block_Offset;
+ uint16_t Remapped_Count;
+ uint8_t pad[2];
+};
+
+struct ddf_bbm_log {
+ uint32_t Signature;
+#define DDF_BBML_SIGNATURE 0xabadb10c
+ uint32_t CRC;
+ uint16_t Entry_Count;
+ uint32_t Spare_Block_Count;
+ uint8_t pad1[10];
+ uint64_t First_Spare_LBA;
+ uint64_t Mapped_Block_Entry[0];
+} __packed;
+
+struct ddf_vendor_log {
+ uint32_t Signature;
+#define DDF_VENDOR_LOG_SIGNATURE 0x01dbeef0
+ uint32_t CRC;
+ uint64_t Log_Owner;
+ uint8_t pad1[16];
+} __packed;
+
+#endif
diff --git a/sys/dev/ata/ata-raid.c b/sys/dev/ata/ata-raid.c
index 40a880c..fc6a23c 100644
--- a/sys/dev/ata/ata-raid.c
+++ b/sys/dev/ata/ata-raid.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <dev/ata/ata-all.h>
#include <dev/ata/ata-disk.h>
#include <dev/ata/ata-raid.h>
+#include <dev/ata/ata-raid-ddf.h>
#include <dev/ata/ata-pci.h>
#include <ata_if.h>
@@ -65,6 +66,7 @@ static int ata_raid_read_metadata(device_t subdisk);
static int ata_raid_write_metadata(struct ar_softc *rdp);
static int ata_raid_wipe_metadata(struct ar_softc *rdp);
static int ata_raid_adaptec_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_ddf_read_meta(device_t dev, struct ar_softc **raidp);
static int ata_raid_hptv2_read_meta(device_t dev, struct ar_softc **raidp);
static int ata_raid_hptv2_write_meta(struct ar_softc *rdp);
static int ata_raid_hptv3_read_meta(device_t dev, struct ar_softc **raidp);
@@ -93,6 +95,7 @@ static char * ata_raid_flags(struct ar_softc *rdp);
/* debugging only */
static void ata_raid_print_meta(struct ar_softc *meta);
static void ata_raid_adaptec_print_meta(struct adaptec_raid_conf *meta);
+static void ata_raid_ddf_print_meta(uint8_t *meta);
static void ata_raid_hptv2_print_meta(struct hptv2_raid_conf *meta);
static void ata_raid_hptv3_print_meta(struct hptv3_raid_conf *meta);
static void ata_raid_intel_print_meta(struct intel_raid_conf *meta);
@@ -1417,6 +1420,10 @@ ata_raid_read_metadata(device_t subdisk)
if (ata_raid_lsiv2_read_meta(subdisk, ata_raid_arrays))
return 0;
+ /* DDF (used by Adaptec, maybe others) */
+ if (ata_raid_ddf_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+
/* if none of the above matched, try FreeBSD native format */
return ata_raid_promise_read_meta(subdisk, ata_raid_arrays, 1);
}
@@ -1688,6 +1695,338 @@ adaptec_out:
return retval;
}
+static uint64_t
+ddfbe64toh(uint64_t val)
+{
+ return (be64toh(val));
+}
+
+static uint32_t
+ddfbe32toh(uint32_t val)
+{
+ return (be32toh(val));
+}
+
+static uint16_t
+ddfbe16toh(uint16_t val)
+{
+ return (be16toh(val));
+}
+
+static uint64_t
+ddfle64toh(uint64_t val)
+{
+ return (le64toh(val));
+}
+
+static uint32_t
+ddfle32toh(uint32_t val)
+{
+ return (le32toh(val));
+}
+
+static uint16_t
+ddfle16toh(uint16_t val)
+{
+ return (le16toh(val));
+}
+
+static int
+ata_raid_ddf_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars;
+ device_t parent = device_get_parent(dev);
+ struct ddf_header *hdr;
+ struct ddf_pd_record *pdr;
+ struct ddf_pd_entry *pde = NULL;
+ struct ddf_vd_record *vdr;
+ struct ddf_pdd_record *pdd;
+ struct ddf_sa_record *sa = NULL;
+ struct ddf_vdc_record *vdcr = NULL;
+ struct ddf_vd_entry *vde = NULL;
+ struct ar_softc *raid;
+ uint64_t pri_lba;
+ uint32_t pd_ref, pd_pos;
+ uint8_t *meta, *cr;
+ int hdr_len, vd_state = 0, pd_state = 0;
+ int i, disk, array, retval = 0;
+ uintptr_t max_cr_addr;
+ uint64_t (*ddf64toh)(uint64_t) = NULL;
+ uint32_t (*ddf32toh)(uint32_t) = NULL;
+ uint16_t (*ddf16toh)(uint16_t) = NULL;
+
+ ars = device_get_softc(dev);
+ raid = NULL;
+
+ /* Read in the anchor header */
+ if (!(meta = malloc(DDF_HEADER_LENGTH, M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, DDF_LBA(parent),
+ meta, DDF_HEADER_LENGTH, ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "DDF read metadata failed\n");
+ goto ddf_out;
+ }
+
+ /*
+ * Check if this is a DDF RAID struct. Note the apparent "flexibility"
+ * regarding endianness.
+ */
+ hdr = (struct ddf_header *)meta;
+ if (be32toh(hdr->Signature) == DDF_HEADER_SIGNATURE) {
+ ddf64toh = ddfbe64toh;
+ ddf32toh = ddfbe32toh;
+ ddf16toh = ddfbe16toh;
+ } else if (le32toh(hdr->Signature) == DDF_HEADER_SIGNATURE) {
+ ddf64toh = ddfle64toh;
+ ddf32toh = ddfle32toh;
+ ddf16toh = ddfle16toh;
+ } else
+ goto ddf_out;
+
+ if (hdr->Header_Type != DDF_HEADER_ANCHOR) {
+ if (testing || bootverbose)
+ device_printf(parent, "DDF check1 failed\n");
+ goto ddf_out;
+ }
+
+ pri_lba = ddf64toh(hdr->Primary_Header_LBA);
+ hdr_len = ddf32toh(hdr->cd_section) + ddf32toh(hdr->cd_length);
+ hdr_len = max(hdr_len,ddf32toh(hdr->pdr_section)+ddf32toh(hdr->pdr_length));
+ hdr_len = max(hdr_len,ddf32toh(hdr->vdr_section)+ddf32toh(hdr->vdr_length));
+ hdr_len = max(hdr_len,ddf32toh(hdr->cr_section) +ddf32toh(hdr->cr_length));
+ hdr_len = max(hdr_len,ddf32toh(hdr->pdd_section)+ddf32toh(hdr->pdd_length));
+ if (testing || bootverbose)
+ device_printf(parent, "DDF pri_lba= %llu length= %d blocks\n",
+ (unsigned long long)pri_lba, hdr_len);
+ if ((pri_lba + hdr_len) > DDF_LBA(parent)) {
+ device_printf(parent, "DDF exceeds length of disk\n");
+ goto ddf_out;
+ }
+
+ /* Don't need the anchor anymore, read the rest of the metadata */
+ free(meta, M_AR);
+ if (!(meta = malloc(hdr_len * DEV_BSIZE, M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, pri_lba, meta, hdr_len * DEV_BSIZE, ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "DDF read full metadata failed\n");
+ goto ddf_out;
+ }
+
+ /* Check that we got a Primary Header */
+ hdr = (struct ddf_header *)meta;
+ if ((ddf32toh(hdr->Signature) != DDF_HEADER_SIGNATURE) ||
+ (hdr->Header_Type != DDF_HEADER_PRIMARY)) {
+ if (testing || bootverbose)
+ device_printf(parent, "DDF check2 failed\n");
+ goto ddf_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_ddf_print_meta(meta);
+
+ if ((hdr->Open_Flag >= 0x01) && (hdr->Open_Flag <= 0x0f)) {
+ device_printf(parent, "DDF Header open, possibly corrupt metadata\n");
+ goto ddf_out;
+ }
+
+ pdr = (struct ddf_pd_record*)(meta + ddf32toh(hdr->pdr_section)*DEV_BSIZE);
+ vdr = (struct ddf_vd_record*)(meta + ddf32toh(hdr->vdr_section)*DEV_BSIZE);
+ cr = (uint8_t *)(meta + ddf32toh(hdr->cr_section)*DEV_BSIZE);
+ pdd = (struct ddf_pdd_record*)(meta + ddf32toh(hdr->pdd_section)*DEV_BSIZE);
+
+ /* Verify the Physical Disk Device Record */
+ if (ddf32toh(pdd->Signature) != DDF_PDD_SIGNATURE) {
+ device_printf(parent, "Invalid PD Signature\n");
+ goto ddf_out;
+ }
+ pd_ref = ddf32toh(pdd->PD_Reference);
+ pd_pos = -1;
+
+ /* Verify the Physical Disk Record and make sure the disk is usable */
+ if (ddf32toh(pdr->Signature) != DDF_PDR_SIGNATURE) {
+ device_printf(parent, "Invalid PDR Signature\n");
+ goto ddf_out;
+ }
+ for (i = 0; i < ddf16toh(pdr->Populated_PDEs); i++) {
+ if (ddf32toh(pdr->entry[i].PD_Reference) != pd_ref)
+ continue;
+ pde = &pdr->entry[i];
+ pd_state = ddf16toh(pde->PD_State);
+ }
+ if ((pde == NULL) ||
+ ((pd_state & DDF_PDE_ONLINE) == 0) ||
+ (pd_state & (DDF_PDE_FAILED|DDF_PDE_MISSING|DDF_PDE_UNRECOVERED))) {
+ device_printf(parent, "Physical disk not usable\n");
+ goto ddf_out;
+ }
+
+ /* Parse out the configuration record, look for spare and VD records.
+ * While DDF supports a disk being part of more than one array, and
+ * thus having more than one VDCR record, that feature is not supported
+ * by ATA-RAID. Therefore, the first record found takes precedence.
+ */
+ max_cr_addr = (uintptr_t)cr + ddf32toh(hdr->cr_length) * DEV_BSIZE - 1;
+ for ( ; (uintptr_t)cr < max_cr_addr;
+ cr += ddf16toh(hdr->Configuration_Record_Length) * DEV_BSIZE) {
+ switch (ddf32toh(((uint32_t *)cr)[0])) {
+ case DDF_VDCR_SIGNATURE:
+ vdcr = (struct ddf_vdc_record *)cr;
+ goto cr_found;
+ break;
+ case DDF_VUCR_SIGNATURE:
+ /* Don't care about this record */
+ break;
+ case DDF_SA_SIGNATURE:
+ sa = (struct ddf_sa_record *)cr;
+ goto cr_found;
+ break;
+ case DDF_CR_INVALID:
+ /* A record was deliberately invalidated */
+ break;
+ default:
+ device_printf(parent, "Invalid CR signature found\n");
+ }
+ }
+cr_found:
+ if ((vdcr == NULL) /* && (sa == NULL) * Spares not supported yet */) {
+ device_printf(parent, "No usable configuration record found\n");
+ goto ddf_out;
+ }
+
+ if (vdcr != NULL) {
+ if (vdcr->Secondary_Element_Count != 1) {
+ device_printf(parent, "Unsupported multi-level Virtual Disk\n");
+ goto ddf_out;
+ }
+
+ /* Find the Virtual Disk Entry for this array */
+ if (ddf32toh(vdr->Signature) != DDF_VD_RECORD_SIGNATURE) {
+ device_printf(parent, "Invalid VDR Signature\n");
+ goto ddf_out;
+ }
+ for (i = 0; i < ddf16toh(vdr->Populated_VDEs); i++) {
+ if (bcmp(vdr->entry[i].VD_GUID, vdcr->VD_GUID, 24))
+ continue;
+ vde = &vdr->entry[i];
+ vd_state = vde->VD_State & DDF_VDE_STATE_MASK;
+ }
+ if ((vde == NULL) ||
+ ((vd_state != DDF_VDE_OPTIMAL) && (vd_state != DDF_VDE_DEGRADED))) {
+ device_printf(parent, "Unusable Virtual Disk\n");
+ goto ddf_out;
+ }
+ for (i = 0; i < ddf16toh(hdr->Max_Primary_Element_Entries); i++) {
+ uint32_t pd_tmp;
+
+ pd_tmp = ddf32toh(vdcr->Physical_Disk_Sequence[i]);
+ if ((pd_tmp == 0x00000000) || (pd_tmp == 0xffffffff))
+ continue;
+ if (pd_tmp == pd_ref) {
+ pd_pos = i;
+ break;
+ }
+ }
+ if (pd_pos == -1) {
+ device_printf(parent, "Physical device not part of array\n");
+ goto ddf_out;
+ }
+ }
+
+ /* now convert DDF metadata into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!raidp[array]) {
+ raid = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raid) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto ddf_out;
+ }
+ } else
+ raid = raidp[array];
+
+ if (raid->format && (raid->format != AR_F_DDF_RAID))
+ continue;
+
+ if (raid->magic_0 && (raid->magic_0 != crc32(vde->VD_GUID, 24)))
+ continue;
+
+ if (!raidp[array]) {
+ raidp[array] = raid;
+
+ switch (vdcr->Primary_RAID_Level) {
+ case DDF_VDCR_RAID0:
+ raid->magic_0 = crc32(vde->VD_GUID, 24);
+ raid->magic_1 = ddf16toh(vde->VD_Number);
+ raid->type = AR_T_RAID0;
+ raid->interleave = 1 << vdcr->Stripe_Size;
+ raid->width = ddf16toh(vdcr->Primary_Element_Count);
+ break;
+
+ case DDF_VDCR_RAID1:
+ raid->magic_0 = crc32(vde->VD_GUID, 24);
+ raid->magic_1 = ddf16toh(vde->VD_Number);
+ raid->type = AR_T_RAID1;
+ raid->width = 1;
+ break;
+
+ default:
+ device_printf(parent, "DDF unsupported RAID type 0x%02x\n",
+ vdcr->Primary_RAID_Level);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto ddf_out;
+ }
+
+ raid->format = AR_F_DDF_RAID;
+ raid->generation = ddf32toh(vdcr->Sequence_Number);
+ raid->total_disks = ddf16toh(vdcr->Primary_Element_Count);
+ raid->total_sectors = ddf64toh(vdcr->VD_Size);
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = 0;
+ raid->rebuild_lba = 0;
+ raid->lun = array;
+ strncpy(raid->name, vde->VD_Name,
+ min(sizeof(raid->name), sizeof(vde->VD_Name)));
+
+ /* clear out any old info */
+ if (raid->generation) {
+ for (disk = 0; disk < raid->total_disks; disk++) {
+ raid->disks[disk].dev = NULL;
+ raid->disks[disk].flags = 0;
+ }
+ }
+ }
+ if (ddf32toh(vdcr->Sequence_Number) >= raid->generation) {
+ int disk_number = pd_pos;
+
+ raid->disks[disk_number].dev = parent;
+
+ /* Adaptec appears to not set vdcr->Block_Count, yet again in
+ * gross violation of the spec.
+ */
+ raid->disks[disk_number].sectors = ddf64toh(vdcr->Block_Count);
+ if (raid->disks[disk_number].sectors == 0)
+ raid->disks[disk_number].sectors=ddf64toh(pde->Configured_Size);
+ raid->disks[disk_number].flags =
+ (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED);
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = disk_number;
+ retval = 1;
+ }
+ break;
+ }
+
+ddf_out:
+ free(meta, M_AR);
+ return retval;
+}
+
/* Highpoint V2 RocketRAID Metadata */
static int
ata_raid_hptv2_read_meta(device_t dev, struct ar_softc **raidp)
@@ -4271,6 +4610,7 @@ ata_raid_format(struct ar_softc *rdp)
switch (rdp->format) {
case AR_F_FREEBSD_RAID: return "FreeBSD PseudoRAID";
case AR_F_ADAPTEC_RAID: return "Adaptec HostRAID";
+ case AR_F_DDF_RAID: return "DDF";
case AR_F_HPTV2_RAID: return "HighPoint v2 RocketRAID";
case AR_F_HPTV3_RAID: return "HighPoint v3 RocketRAID";
case AR_F_INTEL_RAID: return "Intel MatrixRAID";
@@ -4427,6 +4767,71 @@ ata_raid_adaptec_print_meta(struct adaptec_raid_conf *meta)
printf("=================================================\n");
}
+static void
+ata_raid_ddf_print_meta(uint8_t *meta)
+{
+ struct ddf_header *hdr;
+ struct ddf_cd_record *cd;
+ struct ddf_pd_record *pdr;
+ struct ddf_pd_entry *pde;
+ struct ddf_vd_record *vdr;
+ struct ddf_vd_entry *vde;
+ struct ddf_pdd_record *pdd;
+ uint64_t (*ddf64toh)(uint64_t) = NULL;
+ uint32_t (*ddf32toh)(uint32_t) = NULL;
+ uint16_t (*ddf16toh)(uint16_t) = NULL;
+ uint8_t *cr;
+ char *r;
+
+ /* Check if this is a DDF RAID struct */
+ hdr = (struct ddf_header *)meta;
+ if (be32toh(hdr->Signature) == DDF_HEADER_SIGNATURE) {
+ ddf64toh = ddfbe64toh;
+ ddf32toh = ddfbe32toh;
+ ddf16toh = ddfbe16toh;
+ } else {
+ ddf64toh = ddfle64toh;
+ ddf32toh = ddfle32toh;
+ ddf16toh = ddfle16toh;
+ }
+
+ hdr = (struct ddf_header*)meta;
+ cd = (struct ddf_cd_record*)(meta + ddf32toh(hdr->cd_section) *DEV_BSIZE);
+ pdr = (struct ddf_pd_record*)(meta + ddf32toh(hdr->pdr_section)*DEV_BSIZE);
+ vdr = (struct ddf_vd_record*)(meta + ddf32toh(hdr->vdr_section)*DEV_BSIZE);
+ cr = (uint8_t *)(meta + ddf32toh(hdr->cr_section) * DEV_BSIZE);
+ pdd = (struct ddf_pdd_record*)(meta + ddf32toh(hdr->pdd_section)*DEV_BSIZE);
+ pde = NULL;
+ vde = NULL;
+
+ printf("********* ATA DDF Metadata *********\n");
+ printf("**** Header ****\n");
+ r = (char *)&hdr->DDF_rev[0];
+ printf("DDF_rev= %8.8s Sequence_Number= 0x%x Open_Flag= 0x%x\n", r,
+ ddf32toh(hdr->Sequence_Number), hdr->Open_Flag);
+ printf("Primary Header LBA= %llu Header_Type = 0x%x\n",
+ (unsigned long long)ddf64toh(hdr->Primary_Header_LBA),
+ hdr->Header_Type);
+ printf("Max_PD_Entries= %d Max_VD_Entries= %d Max_Partitions= %d "
+ "CR_Length= %d\n", ddf16toh(hdr->Max_PD_Entries),
+ ddf16toh(hdr->Max_VD_Entries), ddf16toh(hdr->Max_Partitions),
+ ddf16toh(hdr->Configuration_Record_Length));
+ printf("CD= %d:%d PDR= %d:%d VDR= %d:%d CR= %d:%d PDD= %d%d\n",
+ ddf32toh(hdr->cd_section), ddf32toh(hdr->cd_length),
+ ddf32toh(hdr->pdr_section), ddf32toh(hdr->pdr_length),
+ ddf32toh(hdr->vdr_section), ddf32toh(hdr->vdr_length),
+ ddf32toh(hdr->cr_section), ddf32toh(hdr->cr_length),
+ ddf32toh(hdr->pdd_section), ddf32toh(hdr->pdd_length));
+ printf("**** Controler Data ****\n");
+ r = (char *)&cd->Product_ID[0];
+ printf("Product_ID: %16.16s\n", r);
+ printf("Vendor 0x%x, Device 0x%x, SubVendor 0x%x, Sub_Device 0x%x\n",
+ ddf16toh(cd->Controller_Type.Vendor_ID),
+ ddf16toh(cd->Controller_Type.Device_ID),
+ ddf16toh(cd->Controller_Type.SubVendor_ID),
+ ddf16toh(cd->Controller_Type.SubDevice_ID));
+}
+
static char *
ata_raid_hptv2_type(int type)
{
diff --git a/sys/dev/ata/ata-raid.h b/sys/dev/ata/ata-raid.h
index f8cc2b9..b0aa52b 100644
--- a/sys/dev/ata/ata-raid.h
+++ b/sys/dev/ata/ata-raid.h
@@ -76,7 +76,8 @@ struct ar_softc {
#define AR_F_SII_RAID 0x0800
#define AR_F_SIS_RAID 0x1000
#define AR_F_VIA_RAID 0x2000
-#define AR_F_FORMAT_MASK 0x3fff
+#define AR_F_DDF_RAID 0x4000
+#define AR_F_FORMAT_MASK 0x7fff
u_int generation;
u_int64_t total_sectors;
@@ -164,6 +165,9 @@ struct adaptec_raid_conf {
u_int32_t dummy_9[62];
} __packed;
+/* DDF Information. Metadata definitions are in another file */
+#define DDF_LBA(dev) \
+ (((struct ad_softc *)device_get_ivars(dev))->total_secs - 1)
/* Highpoint V2 RocketRAID Metadata */
#define HPTV2_LBA(dev) 9
OpenPOWER on IntegriCloud