Browse Source

Identity: implement native libcam versioning and path inquiry tracing

- Migrated advanced ATA version tracking to the official libcam.so exported
  'ata_version' interface, utilizing native kernel resolution matrices.
- Implemented SATA logical revision parsing mapping explicit bit boundaries
  from camcontrol.c (0x0040 for 3.1, 0x0080 for 3.2, 0x0100 for 3.3).
- Integrated active signaling rate tracking via XPT_PATH_INQ bus evaluation,
  enabling precise '(current: X.X Gb/s)' link speed mapping matching smartctl.
- Maintained clean userland fallback protections and universal descriptive
  error logging to stderr across restricted permission environments.
master
Sergey Kiselev 3 months ago
parent
commit
6114f03313
  1. 192
      src/libbsdiskinfo.c

192
src/libbsdiskinfo.c

@ -228,84 +228,101 @@ int diskinfo_get_mounts(const char *base_disk, struct disk_mount *mounts_out, in
/*
* Extracts advanced ATA/SATA hardware descriptor properties from
* the provided native IDENTIFY parameter blocks.
* the provided native IDENTIFY parameter blocks using native libcam functions.
*/
static void parse_advanced_ata_fields(struct ata_params *buf, struct disk_properties *props) {
/* Base internal storage fields configuration */
uint16_t ff = buf->form_factor;
uint16_t major = buf->version_major;
uint16_t transport = buf->transport_major; /* Word 222: Transport Major Revision Number */
uint16_t sata_cap = buf->satacapabilities; /* Word 76: SATA Capabilities descriptor */
uint16_t sec = buf->security_status; /* Word 128: ATA Security Feature Set state */
static void parse_advanced_ata_fields(struct ata_params *buf, struct disk_properties *props, float current_speed) {
/* 1. Form Factor dimensions handling (Word 168) */
uint16_t ff = buf->form_factor;
if (ff == 2) strlcpy(props->form_factor, "3.5 inches", sizeof(props->form_factor));
else if (ff == 3) strlcpy(props->form_factor, "2.5 inches", sizeof(props->form_factor));
else if (ff == 4) strlcpy(props->form_factor, "1.8 inches", sizeof(props->form_factor));
else if (ff == 5) strlcpy(props->form_factor, "less than 1.8", sizeof(props->form_factor));
else strlcpy(props->form_factor, "N/A", sizeof(props->form_factor));
/* 2. Major ATA Version standard handling (Word 80 & Word 222 Transport Framework) */
if (major == 0xFFFF || major == 0x0000) {
fprintf(stderr, "[WARNING] ATA Version unreadable: Word 80 reports invalid boundary (0x%04X)\n", major);
strlcpy(props->ata_version, "Unknown (Invalid Word 80)", sizeof(props->ata_version));
}
/*
* Detect actual ATA/ACS revision specification standard level by
* cross-verifying backward-compatible Word 80 flags against physical Word 222 transport bits.
*/
else if ((major & (1 << 14)) || ((major & (1 << 11)) && (transport & 0x1000) && (transport & 0x0040))) {
strlcpy(props->ata_version, "ACS-4", sizeof(props->ata_version));
} else if ((major & (1 << 13)) || ((major & (1 << 10)) && (transport & 0x1000))) {
strlcpy(props->ata_version, "ACS-3", sizeof(props->ata_version));
} else if (major & (1 << 12)) {
strlcpy(props->ata_version, "ACS-2", sizeof(props->ata_version));
} else if ((major & (1 << 11)) || (major & (1 << 8))) {
strlcpy(props->ata_version, "ATA8-ACS", sizeof(props->ata_version));
} else if (major & (1 << 7)) {
strlcpy(props->ata_version, "ATA/ATAPI-7", sizeof(props->ata_version));
} else if (major & (1 << 6)) {
strlcpy(props->ata_version, "ATA/ATAPI-6", sizeof(props->ata_version));
/* 2. Major ATA Version standard handling using native ata_version() from libcam.so */
int version = ata_version(buf->version_major);
if (buf->version_major == 0xFFFF || buf->version_major == 0x0000 || version < 0) {
strlcpy(props->ata_version, "Unknown", sizeof(props->ata_version));
} else {
snprintf(props->ata_version, sizeof(props->ata_version), "ATA Minor/Legacy (0x%04X)", major);
switch (version) {
case 0:
strlcpy(props->ata_version, "ATA", sizeof(props->ata_version));
break;
case 8:
strlcpy(props->ata_version, "ATA8-ACS", sizeof(props->ata_version));
break;
default:
if (version <= 7) {
snprintf(props->ata_version, sizeof(props->ata_version), "ATA-%d", version);
} else {
snprintf(props->ata_version, sizeof(props->ata_version), "ACS-%d", version - 7);
}
break;
}
}
/* 3. Serial ATA Capabilities layout tracing (Word 76 & Word 222 Context) */
/* 3. Serial ATA Capabilities layout tracing matching camcontrol.c atacapprint() bitmasks (Word 76 & Word 222) */
uint16_t sata_cap = buf->satacapabilities;
uint16_t transport = buf->transport_major;
if (sata_cap == 0xFFFF || sata_cap == 0x0000) {
strlcpy(props->sata_version, "Not a SATA device", sizeof(props->sata_version));
} else {
const char *ver = "SATA";
const char *ver = NULL;
float cap_speed = 1.5;
/* Extract logical transport revision from Word 222 using explicit camcontrol bit boundaries */
if (transport != 0xFFFF && transport != 0x0000 && (transport & 0x1000)) {
if (strcmp(props->ata_version, "ACS-4") == 0) {
if ((transport & (1 << 6)) && (transport & 0x0100)) {
ver = "SATA 3.3";
if (transport & 0x0400) ver = "SATA 3.5";
else if (transport & 0x0200) ver = "SATA 3.4";
else if (transport & 0x0100) ver = "SATA 3.3";
else if (transport & 0x0080) ver = "SATA 3.2";
else if (transport & 0x0040) ver = "SATA 3.1";
else if (transport & 0x0020) ver = "SATA 3.0";
else if (transport & 0x0010) ver = "SATA 2.6";
else if (transport & 0x0008) ver = "SATA 2.5";
}
/* Fallback to physical link generation signaling limits from Word 76 if transport is unmapped */
if (ver == NULL) {
/*
* Enforce strict historical standard ceilings.
* ATA-7 and older legacy specifications historically never supported anything beyond SATA 1.x.
* This prevents modern USB bridges from injecting fake SATA 3.x strings into classic drives.
*/
if (strcmp(props->ata_version, "ATA-7") == 0 || strncmp(props->ata_version, "ATA-", 4) == 0) {
ver = "SATA 1.x";
cap_speed = 1.5; /* Enforce strict hardware capability ceiling for legacy disk lines */
} else {
if (sata_cap & 0x0006) {
ver = "SATA 3.x";
cap_speed = 6.0;
} else if (sata_cap & 0x0004) {
ver = "SATA 2.x";
cap_speed = 3.0;
} else {
ver = "SATA 3.2";
ver = "SATA 1.x";
cap_speed = 1.5;
}
} else if (strcmp(props->ata_version, "ACS-3") == 0) {
ver = "SATA 3.1";
} else {
if (sata_cap & 0x0006) ver = "SATA 3.0";
else if (sata_cap & 0x0004) ver = "SATA 2.6";
else if (sata_cap & 0x0002) ver = "SATA 1.0a";
}
} else {
if (sata_cap & 0x0006) ver = "SATA 3.0";
else if (sata_cap & 0x0004) ver = "SATA 2.6";
else if (sata_cap & 0x0002) ver = "SATA 1.0a";
/* Determine maximum capable speed rate from Word 76 capabilities for modern units */
if (sata_cap & 0x0006) cap_speed = 6.0;
else if (sata_cap & 0x0004) cap_speed = 3.0;
}
if (sata_cap & 0x0006) {
snprintf(props->sata_version, sizeof(props->sata_version), "%s, 6.0 Gb/s", ver);
} else if (sata_cap & 0x0004) {
snprintf(props->sata_version, sizeof(props->sata_version), "%s, 3.0 Gb/s", ver);
} else {
snprintf(props->sata_version, sizeof(props->sata_version), "%s, 1.5 Gb/s", ver);
/* If CAM transport layout parsing returned empty/restricted, sync with capable speed */
if (current_speed <= 0.0) {
current_speed = cap_speed;
}
/* Generate final elegant string matches smartctl style */
snprintf(props->sata_version, sizeof(props->sata_version),
"%s, %.1f Gb/s (current: %.1f Gb/s)", ver, cap_speed, current_speed);
}
/* 4. ATA Security Feature Set state validation (Word 128) */
uint16_t sec = buf->security_status;
if (!(sec & 0x0001)) {
strlcpy(props->security_status, "Disabled", sizeof(props->security_status));
} else if (sec & 0x0004) {
@ -353,12 +370,73 @@ static void query_cam_properties(
return;
}
/* CRITICAL: Must use O_RDWR for transport-layer commands execution path to unlock */
cam_dev = cam_open_device(base_disk, O_RDWR);
if (cam_dev == NULL) return;
/* Extract current active signaling rate mirroring camcontrol.c camxferrate() flow exactly */
float current_speed = 0.0;
uint32_t speed = 0;
union ccb *ccb_cts = NULL;
struct ccb_pathinq cpi;
memset(&cpi, 0, sizeof(struct ccb_pathinq));
/* Step A: Fetch base speed path inquiry matching camcontrol get_cpi() */
union ccb *ccb_cpi = cam_getccb(cam_dev);
if (ccb_cpi != NULL) {
ccb_cpi->ccb_h.func_code = XPT_PATH_INQ;
ccb_cpi->ccb_h.flags = CAM_DIR_NONE;
ccb_cpi->ccb_h.retry_count = 1;
ccb_cpi->ccb_h.timeout = 1000;
if (cam_send_ccb(cam_dev, ccb_cpi) == 0 && (ccb_cpi->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
bcopy(&ccb_cpi->cpi, &cpi, sizeof(struct ccb_pathinq));
}
cam_freeccb(ccb_cpi);
}
/* Initialize baseline transfer speed value from Path Inquiry */
speed = cpi.base_transfer_speed;
/* Step B: Query exact active transfer settings from the CAM layer */
ccb_cts = cam_getccb(cam_dev);
if (ccb_cts != NULL) {
ccb_cts->ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
ccb_cts->ccb_h.flags = CAM_DIR_NONE;
ccb_cts->ccb_h.retry_count = 1;
ccb_cts->ccb_h.timeout = 1000;
ccb_cts->ccb_h.target_id = cam_dev->target_id;
ccb_cts->ccb_h.target_lun = cam_dev->target_lun;
ccb_cts->cts.type = CTS_TYPE_CURRENT_SETTINGS; /* Force runtime active link preference layout fetch */
if (cam_send_ccb(cam_dev, ccb_cts) == 0 && (ccb_cts->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
/* Execute exact transport specific branching copied from camcontrol.c source */
if (ccb_cts->cts.transport == XPORT_ATA) {
struct ccb_trans_settings_pata *pata = &ccb_cts->cts.xport_specific.ata;
if (pata->valid & CTS_ATA_VALID_MODE) {
speed = ata_mode2speed(pata->mode);
}
} else if (ccb_cts->cts.transport == XPORT_SATA) {
struct ccb_trans_settings_sata *sata = &ccb_cts->cts.xport_specific.sata;
if (sata->valid & CTS_SATA_VALID_REVISION) {
speed = ata_revision2speed(sata->revision);
}
} else if (ccb_cts->cts.transport == XPORT_SAS) {
struct ccb_trans_settings_sas *sas = &ccb_cts->cts.xport_specific.sas;
if (sas->valid & 0x01) { /* CTS_SAS_VALID_SPEED */
speed = sas->bitrate;
}
}
}
cam_freeccb(ccb_cts);
}
/* Map finalized speed KB/s calculation to human-readable Gb/s standard format */
uint32_t mb = speed / 1000;
if (mb >= 550) current_speed = 6.0;
else if (mb >= 280) current_speed = 3.0;
else if (mb >= 140) current_speed = 1.5;
/* Allocate CCB structure for standard IDENTIFY command execution path below */
ccb = cam_getccb(cam_dev);
if (ccb == NULL) {
fprintf(stderr, "[ERROR] %s: Failed to allocate CAM CCB structure\n", base_disk);
cam_close_device(cam_dev);
return;
}
@ -477,7 +555,7 @@ static void query_cam_properties(
}
/* Parse advanced hardware descriptors from the verified buffer */
parse_advanced_ata_fields(ident_buf, props);
parse_advanced_ata_fields(ident_buf, props, current_speed);
/* Parse Sector Sizes (Word 106: Physical sector size / Advanced Format) */
if ((ident_buf->pss & 0x4000) && (ident_buf->pss & 0x2000)) {
@ -552,7 +630,7 @@ static void query_cam_properties(
}
/* Parse advanced hardware descriptors from the direct AHCI buffer */
parse_advanced_ata_fields(ident_buf, props);
parse_advanced_ata_fields(ident_buf, props, current_speed);
/* Parse Sector Sizes (Word 106: Physical sector size / Advanced Format) */
if ((ident_buf->pss & 0x4000) && (ident_buf->pss & 0x2000)) {

Loading…
Cancel
Save