summaryrefslogtreecommitdiffstats
path: root/sys/dev
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 /sys/dev
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.
Diffstat (limited to 'sys/dev')
-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