From owner-svn-src-head@FreeBSD.ORG Fri Feb 20 06:10:13 2009 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 3046A106564A; Fri, 20 Feb 2009 06:10:13 +0000 (UTC) (envelope-from scottl@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 1E5288FC13; Fri, 20 Feb 2009 06:10:13 +0000 (UTC) (envelope-from scottl@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n1K6ADOL018373; Fri, 20 Feb 2009 06:10:13 GMT (envelope-from scottl@svn.freebsd.org) Received: (from scottl@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n1K6ACM1018370; Fri, 20 Feb 2009 06:10:12 GMT (envelope-from scottl@svn.freebsd.org) Message-Id: <200902200610.n1K6ACM1018370@svn.freebsd.org> From: Scott Long Date: Fri, 20 Feb 2009 06:10:12 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r188840 - head/sys/dev/ata X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 20 Feb 2009 06:10:13 -0000 Author: scottl Date: Fri Feb 20 06:10:12 2009 New Revision: 188840 URL: http://svn.freebsd.org/changeset/base/188840 Log: 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. Added: head/sys/dev/ata/ata-raid-ddf.h (contents, props changed) Modified: head/sys/dev/ata/ata-raid.c head/sys/dev/ata/ata-raid.h Added: head/sys/dev/ata/ata-raid-ddf.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/ata/ata-raid-ddf.h Fri Feb 20 06:10:12 2009 (r188840) @@ -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 Modified: head/sys/dev/ata/ata-raid.c ============================================================================== --- head/sys/dev/ata/ata-raid.c Fri Feb 20 04:48:40 2009 (r188839) +++ head/sys/dev/ata/ata-raid.c Fri Feb 20 06:10:12 2009 (r188840) @@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -65,6 +66,7 @@ static int ata_raid_read_metadata(device 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_s /* 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 adapt 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) { Modified: head/sys/dev/ata/ata-raid.h ============================================================================== --- head/sys/dev/ata/ata-raid.h Fri Feb 20 04:48:40 2009 (r188839) +++ head/sys/dev/ata/ata-raid.h Fri Feb 20 06:10:12 2009 (r188840) @@ -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