/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include #include #include "ses.h" #include "utils.h" static int debug = 0; #define ENCL_CFG_DIAG_STATUS 0x01 #define ENCL_CTRL_DIAG_STATUS 0x02 #define ENCL_CTRL_DIAG_CFG 0x02 #define ENCL_EL_DESCR_STATUS 0x07 #define ENCL_ADDITIONAL_EL_STATUS 0x0a #define SCSI_PROTOCOL_SAS 6 static int get_ses_page(int fd, struct ses_page *p, int pg_code) { int ret; int retry_count = 3; do { ret = sg_ll_receive_diag(fd, 1, pg_code, p->buf, sizeof(p->buf), 0, debug); } while (ret && retry_count--); if (!ret) p->len = (p->buf[2] << 8) + p->buf[3] + 4; return ret; } static int process_page1(struct ses_pages *sp) { int num_encl; /* number of subenclosures */ unsigned char *ed; /* Enclosure Descriptor */ int len = 0; int sum_headers = 0; /* Number of Type descriptor headers */ int i = 0; /* How many enclosures is in the main enclosure? */ num_encl = sp->page1.buf[1] + 1; /* Go to Enclosure Descriptor */ ed = sp->page1.buf + 8; for (i = 0; i < num_encl; i++, ed += len) { if (ed + 3 > sp->page1.buf + sp->page1.len) { log_debug ("SES: Error, response pare 1 truncated at %d\n", i); return 1; } sum_headers += ed[2]; len = ed[3] + 4; if (len < 40) { log_debug("SES: Response too short for page 1\n"); continue; } } sp->page1_types = (struct type_descriptor_header *)ed; sp->page1_types_len = sum_headers; /* ed is on type descr header */ for (i = 0; i < sum_headers; i++, ed += 4) { if (ed > sp->page1.buf + sp->page1.len) { log_debug("SES: Response page 1 truncated at %d\n", i); return 1; } } return 0; } static void print_page10(struct ses_pages *sp) { unsigned char *ai = sp->page10.buf + 8; int i = 0, len = 0, eip = 0; unsigned char *sas = NULL; while (ai < sp->page10.buf + sp->page10.len) { printf("%s()[%d]: Inv: %d, EIP: %d, Proto: 0x%04x\n", __func__, i++, ((ai[0] & 0x80) >> 7), ((ai[0] & 0x10) >> 4), (unsigned int) (ai[0] & 0xf)); printf("\tDescriptor len (x-1): %d\n", ai[1] + 1); eip = ai[0] & 0x10; if (eip) printf("\tElement Index: %d\n", ai[3]); len = ai[1] + 2; if ((ai[0] & 0xf) == SCSI_PROTOCOL_SAS) { if (eip) sas = ai + 4; else sas = ai + 2; printf("\tProtocol SAS:\n"); printf("\tNumber of phy descriptors: %d\n", sas[0]); printf("\tNot all phys: %d, descriptor type: 0x%1x\n", (sas[1] & 1), ((sas[1] & 0xc0) >> 6)); if (eip) { printf("\tDevice slot number: %d\n", sas[3]); sas += 2; } sas += 2; printf("\tDevice type: 0x%01x\n", (unsigned int)((sas[0] & 0x70) >> 4)); printf("\tSMP Initiator Port: 0x%01x\n", (unsigned int)((sas[2] & 2) >> 1)); printf("\tSTP Initiator Port: 0x%01x\n", (unsigned int)((sas[2] & 4) >> 2)); printf("\tSSP Initiator Port: 0x%01x\n", (unsigned int)((sas[2] & 8) >> 3)); printf("\tSATA DEVICE: 0x%01x\n", (unsigned int)(sas[3] & 1)); printf("\tSMP Target Port: 0x%01x\n", (unsigned int)((sas[3] & 2) >> 1)); printf("\tSTP Target Port: 0x%01x\n", (unsigned int)((sas[3] & 4) >> 2)); printf("\tSSP Target Port: 0x%01x\n", (unsigned int)((sas[3] & 8) >> 3)); printf("\tSATA Port Selector: 0x%01x\n", (unsigned int)((sas[3] & 0X80) >> 7)); printf ("\tAttached SAS Address: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", sas[4], sas[5], sas[6], sas[7], sas[8], sas[9], sas[10], sas[11]); printf ("\tSAS Address: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", sas[12], sas[13], sas[14], sas[15], sas[16], sas[17], sas[18], sas[19]); printf("\tPHY Identified: 0x%01x\n", sas[20]); } else printf("\tProtocol not SAS: 0x%02x, skipping\n", (unsigned int)(ai[0] & 0xf)); /* */ ai += len; } return; } int ses_load_pages(int fd, struct ses_pages *sp) { int ret; /* Read configuration. */ ret = get_ses_page(fd, &sp->page1, ENCL_CFG_DIAG_STATUS); if (ret) return ret; ret = process_page1(sp); if (ret) return ret; /* Get Enclosure Status */ ret = get_ses_page(fd, &sp->page2, ENCL_CTRL_DIAG_STATUS); if (ret) return ret; /* Additional Element Status */ ret = get_ses_page(fd, &sp->page10, ENCL_ADDITIONAL_EL_STATUS); if (ret) return ret; if (debug) print_page10(sp); return ret; } static enum ibpi_pattern ibpi_to_ses(enum ibpi_pattern ibpi) { switch (ibpi) { case IBPI_PATTERN_UNKNOWN: case IBPI_PATTERN_ONESHOT_NORMAL: case IBPI_PATTERN_NORMAL: return SES_REQ_OK; case IBPI_PATTERN_FAILED_ARRAY: return SES_REQ_IFA; case IBPI_PATTERN_DEGRADED: return SES_REQ_ICA; case IBPI_PATTERN_REBUILD: return SES_REQ_REBUILD; case IBPI_PATTERN_FAILED_DRIVE: return SES_REQ_FAULT; case IBPI_PATTERN_LOCATE: return SES_REQ_IDENT; case IBPI_PATTERN_HOTSPARE: return SES_REQ_HOSTSPARE; case IBPI_PATTERN_PFA: return SES_REQ_PRDFAIL; default: return ibpi; } } static inline void _set_prdfail(unsigned char *u) { u[0] |= (1 << 6); } static inline void _set_abrt(unsigned char *u) { u[1] |= (1 << 0); } static inline void _set_rebuild(unsigned char *u) { u[1] |= (1 << 1); } static inline void _set_ifa(unsigned char *u) { u[1] |= (1 << 2); } static inline void _set_ica(unsigned char *u) { u[1] |= (1 << 3); } static inline void _set_cons_check(unsigned char *u) { u[1] |= (1 << 4); } static inline void _set_hspare(unsigned char *u) { u[1] |= (1 << 5); } static inline void _set_rsvd_dev(unsigned char *u) { u[1] |= (1 << 6); } static inline void _set_ok(unsigned char *u) { u[1] |= (1 << 7); } static inline void _set_ident(unsigned char *u) { u[2] |= (1 << 1); } static inline void _clr_ident(unsigned char *u) { u[2] &= ~(1 << 1); } static inline void _set_rm(unsigned char *u) { u[2] |= (1 << 2); } static inline void _set_ins(unsigned char *u) { u[2] |= (1 << 3); } static inline void _set_miss(unsigned char *u) { u[2] |= (1 << 4); } static inline void _set_dnr(unsigned char *u) { u[2] |= (1 << 6); } static inline void _set_actv(unsigned char *u) { u[2] |= (1 << 7); } static inline void _set_enbb(unsigned char *u) { u[3] |= (1 << 2); } static inline void _set_enba(unsigned char *u) { u[3] |= (1 << 3); } static inline void _set_off(unsigned char *u) { u[3] |= (1 << 4); } static inline void _set_fault(unsigned char *u) { u[3] |= (1 << 5); } static int ses_set_message(enum ibpi_pattern ibpi, struct ses_slot_ctrl_elem *el) { struct ses_slot_ctrl_elem msg; memset(&msg, 0, sizeof(msg)); if (ibpi == IBPI_PATTERN_LOCATE_OFF) { /* * For locate_off we don't set a new state, just clear the * IDENT bit and the bits that are reserved or have different * meanings in Status and Control pages (RQST ACTIVE and * RQST MISSING). */ _clr_ident(el->b); el->b2 &= 0x4e; el->b3 &= 0x3c; return 0; } switch (ibpi_to_ses(ibpi)) { case SES_REQ_ABORT: _set_abrt(msg.b); break; case SES_REQ_REBUILD: _set_rebuild(msg.b); break; case SES_REQ_IFA: _set_ifa(msg.b); break; case SES_REQ_ICA: _set_ica(msg.b); break; case SES_REQ_CONS_CHECK: _set_cons_check(msg.b); break; case SES_REQ_HOSTSPARE: _set_hspare(msg.b); break; case SES_REQ_RSVD_DEV: _set_rsvd_dev(msg.b); break; case SES_REQ_OK: _set_ok(msg.b); break; case SES_REQ_IDENT: _set_ident(msg.b); break; case SES_REQ_RM: _set_rm(msg.b); break; case SES_REQ_INS: _set_ins(msg.b); break; case SES_REQ_MISSING: _set_miss(msg.b); break; case SES_REQ_DNR: _set_dnr(msg.b); break; case SES_REQ_ACTIVE: _set_actv(msg.b); break; case SES_REQ_EN_BB: _set_enbb(msg.b); break; case SES_REQ_EN_BA: _set_enba(msg.b); break; case SES_REQ_DEV_OFF: _set_off(msg.b); break; case SES_REQ_FAULT: _set_fault(msg.b); break; case SES_REQ_PRDFAIL: _set_prdfail(msg.b); break; default: return 1; } *el = msg; return 0; } int ses_write_msg(enum ibpi_pattern ibpi, struct ses_pages *sp, int idx) { /* Move do descriptors */ struct ses_slot_ctrl_elem *descriptors = (void *)(sp->page2.buf + 8); int i; struct ses_slot_ctrl_elem *desc_element = NULL; element_type local_element_type = SES_UNSPECIFIED; for (i = 0; i < sp->page1_types_len; i++) { const struct type_descriptor_header *t = &sp->page1_types[i]; descriptors++; /* At first, skip overall header. */ if (t->element_type == SES_DEVICE_SLOT || t->element_type == SES_ARRAY_DEVICE_SLOT) { if (local_element_type < t->element_type && t->num_of_elements > idx) { local_element_type = t->element_type; desc_element = &descriptors[idx]; } } else { /* * Device Slot and Array Device Slot elements are * always first on the type descriptor header list */ break; } descriptors += t->num_of_elements; } if (desc_element) { int ret = ses_set_message(ibpi, desc_element); if (ret) return ret; sp->changes++; /* keep PRDFAIL, clear rest */ desc_element->common_control &= 0x40; /* set select */ desc_element->common_control |= 0x80; /* second byte is valid only for Array Device Slot */ if (local_element_type != SES_ARRAY_DEVICE_SLOT) desc_element->array_slot_control = 0; return 0; } return 1; } int ses_send_diag(int fd, struct ses_pages *sp) { return sg_ll_send_diag(fd, 0, 1, 0, 0, 0, 0, sp->page2.buf, sp->page2.len, 0, debug); } int ses_get_slots(struct ses_pages *sp, struct ses_slot **out_slots, int *out_slots_count) { unsigned char *add_desc = NULL; unsigned char *ap = NULL, *addr_p = NULL; int i, j, len = 0; /* Check Page10 for address. Extract index. */ ap = add_desc = sp->page10.buf + 8; for (i = 0; i < sp->page1_types_len; i++) { const struct type_descriptor_header *t = &sp->page1_types[i]; if (t->element_type == SES_DEVICE_SLOT || t->element_type == SES_ARRAY_DEVICE_SLOT) { struct ses_slot *slots; slots = calloc(t->num_of_elements, sizeof(*slots)); if (!slots) return -1; for (j = 0; j < t->num_of_elements; j++, ap += len) { /* Get Additional Element Status Descriptor */ /* length (x-1) */ len = ap[1] + 2; if ((ap[0] & 0xf) != SCSI_PROTOCOL_SAS) { slots[j].index = -1; continue; /* need SAS PROTO */ } /* It is a SAS protocol, go on */ if ((ap[0] & 0x10)) /* Check EIP */ addr_p = ap + 8; else addr_p = ap + 4; /* Process only PHY 0 descriptor. */ /* Convert be64 to le64 */ slots[j].sas_addr = ((uint64_t)addr_p[12] << 8*7) | ((uint64_t)addr_p[13] << 8*6) | ((uint64_t)addr_p[14] << 8*5) | ((uint64_t)addr_p[15] << 8*4) | ((uint64_t)addr_p[16] << 8*3) | ((uint64_t)addr_p[17] << 8*2) | ((uint64_t)addr_p[18] << 8*1) | ((uint64_t)addr_p[19]); slots[j].index = ap[0] & 0x10 ? ap[3] : j; } *out_slots = slots; *out_slots_count = t->num_of_elements; return 0; } } return 1; }