Skip to content

Commit 77bbde3

Browse files
better AHCI error reporting to BASIC
1 parent 90d0fd5 commit 77bbde3

File tree

13 files changed

+450
-35
lines changed

13 files changed

+450
-35
lines changed

include/ahci.h

Lines changed: 192 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#pragma once
1212

1313
#include <kernel.h>
14+
#include <scsi.h>
1415

1516
/**
1617
* @brief Frame Information Structure (FIS) type identifiers.
@@ -331,8 +332,11 @@ typedef struct ahci_hba_cmd_tbl_t {
331332

332333
/* ----------------------------- Interrupt/status and ATA opcodes ----------------------------- */
333334

334-
/** @brief PxIS: Task File Error Status (TFES). */
335-
#define HBA_PxIS_TFES (1 << 30)
335+
#define HBA_PxIS_TFES (1 << 30) /** Task file error */
336+
#define HBA_PxIS_IFS (1u << 27) /* Interface fatal error */
337+
#define HBA_PxIS_HBDS (1u << 28) /* Host bus data error */
338+
#define HBA_PxIS_HBFS (1u << 29) /* Host bus fatal error */
339+
336340
/** @brief ATA command: READ DMA EXT (48-bit). */
337341
#define ATA_CMD_READ_DMA_EX 0x25
338342
/** @brief ATA command: WRITE DMA EXT (48-bit). */
@@ -392,7 +396,7 @@ int find_cmdslot(ahci_hba_port_t *port, ahci_hba_mem_t *abar);
392396
* @param abar AHCI HBA memory block.
393397
* @return true on success, false on error.
394398
*/
395-
bool ahci_read(ahci_hba_port_t *port, uint64_t start, uint32_t count, uint16_t *buf, ahci_hba_mem_t* abar);
399+
bool ahci_read(ahci_hba_port_t *port, uint64_t start, uint32_t count, char *buf, ahci_hba_mem_t* abar);
396400

397401
/**
398402
* @brief Write to a SATA block device via DMA.
@@ -414,7 +418,7 @@ bool ahci_write(ahci_hba_port_t *port, uint64_t start, uint32_t count, char *buf
414418
* @param abar AHCI HBA memory block.
415419
* @return true on success, false on error.
416420
*/
417-
bool ahci_atapi_read(ahci_hba_port_t *port, uint64_t start, uint32_t count, uint16_t *buf, ahci_hba_mem_t* abar);
421+
bool ahci_atapi_read(ahci_hba_port_t *port, uint64_t start, uint32_t count, char *buf, ahci_hba_mem_t* abar);
418422

419423
/**
420424
* @brief Query capacity (in bytes) from a SATA device using IDENTIFY data.
@@ -466,27 +470,206 @@ int storage_device_ahci_block_read(void* dev, uint64_t start, uint32_t bytes, un
466470
*/
467471
int storage_device_ahci_block_write(void* dev, uint64_t start, uint32_t bytes, const unsigned char* buffer);
468472

469-
473+
/**
474+
* @brief Wait until the AHCI port and attached device are ready to accept a new command.
475+
* @param port AHCI HBA port.
476+
* @return true if ready, false on timeout or fatal port state.
477+
*/
470478
bool wait_for_ready(ahci_hba_port_t* port);
479+
480+
/**
481+
* @brief Fill one PRDT entry in the command table.
482+
* @param cmdtbl Command table to modify.
483+
* @param index PRDT entry index to fill.
484+
* @param address Physical or DMA-able address of the data buffer.
485+
* @param byte_count Number of bytes to transfer (1..4MiB per entry, device-dependent).
486+
* @param interrupt Set true to request interrupt on completion for this PRDT entry.
487+
*/
471488
void fill_prdt(ahci_hba_cmd_tbl_t* cmdtbl, size_t index, void* address, uint32_t byte_count, bool interrupt);
489+
490+
/**
491+
* @brief Obtain and initialise a command header for a slot.
492+
* @param port AHCI HBA port.
493+
* @param slot Command slot number (0..31).
494+
* @param write Set true for host-to-device (write), false for device-to-host (read).
495+
* @param atapi Set true for ATAPI packet command, false for ATA.
496+
* @param prdtls Number of PRDT entries to advertise (PRDTL).
497+
* @return Pointer to the prepared command header.
498+
*/
472499
ahci_hba_cmd_header_t* get_cmdheader_for_slot(ahci_hba_port_t* port, size_t slot, bool write, bool atapi, uint16_t prdtls);
500+
501+
/**
502+
* @brief Return the command table for a header and clear CFIS/ATAPI/reserved regions.
503+
* @param cmdheader Command header whose table will be accessed.
504+
* @return Pointer to the command table.
505+
*/
473506
ahci_hba_cmd_tbl_t* get_and_clear_cmdtbl(ahci_hba_cmd_header_t* cmdheader);
507+
508+
/**
509+
* @brief Prepare a Register H2D FIS in the command table.
510+
* @param cmdtbl Command table to place the FIS into.
511+
* @param type FIS type (e.g., FIS_TYPE_REG_H2D).
512+
* @param command ATA/ATAPI command opcode.
513+
* @param feature_low Feature low byte.
514+
* @return Pointer to the FIS within the command table.
515+
*/
474516
ahci_fis_reg_h2d_t* setup_reg_h2d(ahci_hba_cmd_tbl_t* cmdtbl, uint8_t type, uint8_t command, uint8_t feature_low);
517+
518+
/**
519+
* @brief Fill LBA and sector count fields in a Register H2D FIS.
520+
* @param cmdfis Pointer to the FIS previously created by setup_reg_h2d.
521+
* @param start Starting LBA.
522+
* @param count Sector count (device logical block units).
523+
*/
475524
void fill_reg_h2c(ahci_fis_reg_h2d_t* cmdfis, uint64_t start, uint16_t count);
525+
526+
/**
527+
* @brief Issue a prepared command in the given slot by setting PxCI.
528+
* @param port AHCI HBA port.
529+
* @param slot Command slot number (0..31).
530+
*/
476531
void issue_command_to_slot(ahci_hba_port_t *port, uint8_t slot);
532+
533+
/**
534+
* @brief Wait for a command slot to complete and detect transport/taskfile errors.
535+
* @param port AHCI HBA port.
536+
* @param slot Command slot number (0..31).
537+
* @param function Short label used for diagnostics/logging.
538+
* @return true on success, false if a taskfile/transport error occurred (error already logged).
539+
*/
477540
bool wait_for_completion(ahci_hba_port_t* port, uint8_t slot, const char* function);
541+
542+
/**
543+
* @brief Convenience wrapper to issue a command and wait for completion.
544+
* @param port AHCI HBA port.
545+
* @param slot Command slot number (0..31).
546+
* @param function Short label used for diagnostics/logging.
547+
* @return true on success, false on error (error already logged).
548+
*/
478549
bool issue_and_wait(ahci_hba_port_t* port, uint8_t slot, const char* function);
550+
551+
/**
552+
* @brief Trim trailing ASCII spaces from a NUL-terminated string in place.
553+
* @param s String buffer to modify.
554+
*/
479555
void trim_trailing_spaces(char *s);
556+
557+
/**
558+
* @brief Build a human-readable ATAPI device label from INQUIRY data.
559+
* @param sd Storage device object to receive the label.
560+
* @param inq 36+ byte SCSI INQUIRY response buffer.
561+
*/
480562
void build_atapi_label(struct storage_device_t *sd, const uint8_t *inq);
481563

564+
/**
565+
* @brief Eject removable media from an ATAPI device (ALLOW removal then START STOP with LOEJ).
566+
* @param port AHCI HBA port for the device.
567+
* @param abar AHCI HBA memory (ABAR) pointer.
568+
* @return true on command acceptance, false on error (caller may fetch sense).
569+
*/
482570
bool atapi_eject(ahci_hba_port_t *port, ahci_hba_mem_t *abar);
483-
bool ahci_read(ahci_hba_port_t *port, uint64_t start, uint32_t count, uint16_t *buf, ahci_hba_mem_t* abar);
484-
bool ahci_write(ahci_hba_port_t *port, uint64_t start, uint32_t count, char *buf, ahci_hba_mem_t* abar);
485-
bool ahci_atapi_read(ahci_hba_port_t *port, uint64_t start, uint32_t count, uint16_t *buf, ahci_hba_mem_t* abar);
486-
uint64_t ahci_read_size(ahci_hba_port_t *port, ahci_hba_mem_t* abar);
571+
572+
/**
573+
* @brief Issue IDENTIFY (ATA or ATAPI) and copy the 512-byte identify page to out.
574+
* @param port AHCI HBA port.
575+
* @param abar AHCI HBA memory (ABAR) pointer.
576+
* @param out Destination buffer for identify data (512 bytes).
577+
* @return true on success, false on error.
578+
*/
487579
bool ahci_identify_page(ahci_hba_port_t *port, ahci_hba_mem_t *abar, uint8_t *out);
580+
581+
/**
582+
* @brief Issue ATAPI INQUIRY (0x12) and copy the response.
583+
* @param port AHCI HBA port.
584+
* @param abar AHCI HBA memory (ABAR) pointer.
585+
* @param out Destination buffer for INQUIRY data.
586+
* @param len Allocation length requested and number of bytes to copy.
587+
* @return true on success, false on error (caller may use sense mapping).
588+
*/
488589
bool atapi_enquiry(ahci_hba_port_t *port, ahci_hba_mem_t *abar, uint8_t *out, uint8_t len);
489590

591+
/**
592+
* @brief Map AHCI/AHCI-ATA/ATAPI latched status on a port to a fixed fs_error_t without performing I/O.
593+
* @param port AHCI HBA port; uses PxIS/PxSERR/PxTFD/PxSIG snapshots.
594+
* @return fs_error_t value describing the failure class.
595+
*/
596+
int ahci_classify_error(ahci_hba_port_t* port);
597+
598+
/**
599+
* @brief Map SCSI/ATAPI sense (sense key, ASC, ASCQ) to a fixed fs_error_t.
600+
* @param sense_key SCSI sense key.
601+
* @param additional_sense_code Additional Sense Code (ASC).
602+
* @param additional_sense_code_qualifier Additional Sense Code Qualifier (ASCQ).
603+
* @return fs_error_t value describing the failure class.
604+
*/
605+
int scsi_map_sense_to_fs_error(scsi_sense_key_t sense_key, scsi_additional_sense_code_t additional_sense_code, scsi_additional_sense_code_qualifier_t additional_sense_code_qualifier);
606+
607+
/**
608+
* @brief Issue ATAPI REQUEST SENSE (6) and copy sense data to the caller buffer.
609+
* @param port AHCI HBA port.
610+
* @param abar AHCI HBA memory (ABAR) pointer.
611+
* @param out Destination buffer for sense data.
612+
* @param out_len Allocation length requested (typically 18).
613+
* @return true on success, false on failure to obtain sense data.
614+
*/
615+
bool atapi_request_sense6(ahci_hba_port_t* port, ahci_hba_mem_t* abar, uint8_t* out, uint8_t out_len);
616+
617+
/**
618+
* @brief Extract sense key, ASC and ASCQ from a fixed-format (0x70/0x71) REQUEST SENSE buffer.
619+
* @param buf Sense buffer (>= 14 bytes, typically 18).
620+
* @param sense_key Output: sense key.
621+
* @param additional_sense_code Output: ASC.
622+
* @param additional_sense_code_qualifier Output: ASCQ.
623+
*/
624+
void scsi_extract_fixed_sense(const uint8_t* buf, scsi_sense_key_t* sense_key, scsi_additional_sense_code_t* additional_sense_code, scsi_additional_sense_code_qualifier_t* additional_sense_code_qualifier);
625+
626+
/**
627+
* @brief Handle ATAPI CHECK CONDITION by issuing REQUEST SENSE, mapping to fs_error_t, logging a single line, and returning false.
628+
* @param port AHCI HBA port.
629+
* @param abar AHCI HBA memory (ABAR) pointer.
630+
* @param function Short label used for diagnostics/logging.
631+
* @return Always false to simplify call-sites that branch on failure.
632+
*/
633+
bool atapi_handle_check_condition(ahci_hba_port_t* port, ahci_hba_mem_t* abar, const char* function);
634+
635+
636+
/**
637+
* @brief Map AHCI/AHCI-ATA/ATAPI status to a fixed fs_error_t.
638+
*
639+
* Uses PxIS priority: HBFS → HBDS → IFS → TFES. For TFES, inspects PxTFD ERR bits.
640+
* For ATAPI, returns FS_ERR_ATAPI_CHECK on TFES so the caller can issue REQUEST SENSE.
641+
*
642+
* This function performs no I/O; it only interprets the latched registers you pass in.
643+
*/
644+
int ahci_classify_error(ahci_hba_port_t* port);
645+
646+
/**
647+
* @brief Map SCSI/ATAPI sense (SK/ASC/ASCQ) to fs_error_t with fixed message.
648+
*/
649+
int scsi_map_sense_to_fs_error(scsi_sense_key_t sense_key, scsi_additional_sense_code_t additional_sense_code, scsi_additional_sense_code_qualifier_t additional_sense_code_qualifier);
650+
651+
/**
652+
* @brief Issue ATAPI REQUEST SENSE (6) and copy sense data to 'out'.
653+
* Expects 'out_len' >= 18 for standard fixed-format sense.
654+
* Returns true on success (command accepted and data DMA'd), false on failure.
655+
*/
656+
bool atapi_request_sense6(ahci_hba_port_t* port, ahci_hba_mem_t* abar, uint8_t* out, uint8_t out_len);
657+
658+
/**
659+
* @brief Extract SK/ASC/ASCQ from a fixed-format (0x70/0x71) REQUEST SENSE buffer.
660+
* Caller guarantees 'buf' points to at least 14 bytes (we usually pass 18).
661+
*/
662+
void scsi_extract_fixed_sense(const uint8_t* buf, scsi_sense_key_t* sense_key, scsi_additional_sense_code_t* additional_sense_code, scsi_additional_sense_code_qualifier_t* additional_sense_code_qualifier);
663+
664+
/**
665+
* @brief Convenience helper for ATAPI CHECK CONDITION paths.
666+
* Issues REQUEST SENSE, extracts and maps sense, logs a single line, then returns false.
667+
*
668+
* Always returns false so call-sites can simply: if (!atapi_handle_check_condition(...)) return false;
669+
* If REQUEST SENSE itself fails, logs a generic timeout/hardware error.
670+
*/
671+
bool atapi_handle_check_condition(ahci_hba_port_t* port, ahci_hba_mem_t* abar, const char* function);
672+
490673
/* ----------------------------- Layout sanity checks ----------------------------- */
491674

492675
/** @brief Compile-time size check: command header must be 0x20 bytes. */

include/ata.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,15 @@
1212
#define ATA_IDENT_MAX_LBA 120
1313
#define ATA_IDENT_MAX_LBA_EXT 200
1414

15+
/* ATA Error Register bits */
16+
#define ATA_ERR_AMNF 0x01 /* Address mark not found (obsolete, legacy) */
17+
#define ATA_ERR_TK0NF 0x02 /* Track 0 not found (obsolete, legacy) */
18+
#define ATA_ERR_ABRT 0x04 /* Aborted command (illegal/unsupported) */
19+
#define ATA_ERR_MCR 0x08 /* Media change request (obsolete, legacy) */
20+
#define ATA_ERR_IDNF 0x10 /* ID not found (LBA not found) */
21+
#define ATA_ERR_MC 0x20 /* Media changed */
22+
#define ATA_ERR_UNC 0x40 /* Uncorrectable data error */
23+
#define ATA_ERR_ICRC 0x80 /* Interface CRC error / bad sector transfer */
24+
25+
1526
void init_ide();

include/filesystem.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,18 @@ typedef enum fs_error_t {
8484
FS_ERR_BAD_CLUSTER,
8585
FS_ERR_INTERNAL,
8686
FS_ERR_OUT_OF_BOUNDS,
87+
FS_ERR_AHCI_HOSTBUS_FATAL,
88+
FS_ERR_AHCI_HOSTBUS_DATA,
89+
FS_ERR_AHCI_LINK_ERROR,
90+
FS_ERR_AHCI_TASKFILE,
91+
FS_ERR_ATAPI_CHECK,
92+
FS_ERR_TIMEOUT,
93+
FS_ERR_NO_MEDIA,
94+
FS_ERR_DEVICE_LOCKED,
95+
FS_ERR_DEVICE_BUSY,
96+
FS_ERR_DEVICE_UNSUPPORTED,
97+
FS_ERR_HW_ERROR,
98+
FS_ERR_DATA_ERROR,
8799
} fs_error_t;
88100

89101
/**

include/random.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212
#define RAND_MAX_ENTROPY (size_t)24
1313
#define RAND_MAX SIZE_MAX
1414

15+
#define SAMPLE_FROM_BUFFER(p) \
16+
(((uint64_t)((const uint8_t*)(p))[0] << 56) | \
17+
((uint64_t)((const uint8_t*)(p))[1] << 48) | \
18+
((uint64_t)((const uint8_t*)(p))[2] << 40) | \
19+
((uint64_t)((const uint8_t*)(p))[3] << 32) | \
20+
((uint64_t)((const uint8_t*)(p))[4] << 24) | \
21+
((uint64_t)((const uint8_t*)(p))[5] << 16) | \
22+
((uint64_t)((const uint8_t*)(p))[6] << 8) | \
23+
((uint64_t)((const uint8_t*)(p))[7]))
24+
1525
typedef struct mt_rand_t {
1626
uint32_t mt[STATE_VECTOR_LENGTH];
1727
int index;

include/scsi.h

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,46 @@
1818
#define SCSI_OP_WRITE_16 0x8A
1919
#define SCSI_OP_SERVICE_ACTION_IN_16 0x9E /* use SA=0x10 for READ CAPACITY(16) */
2020

21+
typedef enum scsi_sense_key_t {
22+
SCSI_SK_NO_SENSE = 0x00,
23+
SCSI_SK_RECOVERED_ERROR = 0x01,
24+
SCSI_SK_NOT_READY = 0x02,
25+
SCSI_SK_MEDIUM_ERROR = 0x03,
26+
SCSI_SK_HARDWARE_ERROR = 0x04,
27+
SCSI_SK_ILLEGAL_REQUEST = 0x05,
28+
SCSI_SK_UNIT_ATTENTION = 0x06,
29+
SCSI_SK_DATA_PROTECT = 0x07,
30+
SCSI_SK_BLANK_CHECK = 0x08,
31+
SCSI_SK_VENDOR_SPECIFIC = 0x09,
32+
SCSI_SK_COPY_ABORTED = 0x0A,
33+
SCSI_SK_ABORTED_COMMAND = 0x0B,
34+
SCSI_SK_VOLUME_OVERFLOW = 0x0D,
35+
SCSI_SK_MISCOMPARE = 0x0E,
36+
} scsi_sense_key_t;
37+
38+
typedef enum scsi_additional_sense_code_t {
39+
SCSI_ASC_LUN_NOT_READY = 0x04,
40+
SCSI_ASC_INVALID_FIELD = 0x24,
41+
SCSI_ASC_WRITE_PROTECTED = 0x27,
42+
SCSI_ASC_MEDIA_CHANGED = 0x28,
43+
SCSI_ASC_MEDIUM_NOT_PRESENT = 0x3A,
44+
SCSI_ASC_ANY = 0xFF, /* Wildcard */
45+
} scsi_additional_sense_code_t;
46+
47+
typedef enum scsi_additional_sense_code_qualifier_t {
48+
SCSI_ASCQ_BECOMING_READY = 0x01,
49+
SCSI_ASCQ_INIT_REQUIRED = 0x02,
50+
SCSI_ASCQ_MANUAL_INTERVENTION = 0x03,
51+
SCSI_ASCQ_MEDIUM_NOT_PRESENT = 0x00,
52+
SCSI_ASCQ_MEDIA_CHANGED = 0x00,
53+
SCSI_ASCQ_WRITE_PROTECTED = 0x00,
54+
SCSI_ASCQ_ANY = 0xFF, /* Wildcard */
55+
} scsi_additional_sense_code_qualifier_t;
56+
2157
/* INQUIRY(6) — 6 bytes */
2258
typedef struct __attribute__((packed)) scsi_cdb_inquiry6 {
2359
uint8_t opcode; /* 0x12 */
24-
uint8_t evpd; /* bit0 = EVPD; you use 0 */
60+
uint8_t evpd; /* bit0 = EVPD */
2561
uint8_t page_code; /* usually 0 */
2662
uint8_t reserved; /* 0 */
2763
uint8_t alloc_len; /* 1-byte allocation length */
@@ -31,7 +67,7 @@ typedef struct __attribute__((packed)) scsi_cdb_inquiry6 {
3167
/* READ(12) — 12 bytes; LBA and transfer length are big-endian */
3268
typedef struct __attribute__((packed)) scsi_cdb_read12 {
3369
uint8_t opcode; /* 0xA8 */
34-
uint8_t flags; /* DPO/FUA/etc; you use 0 */
70+
uint8_t flags; /* DPO/FUA/etc */
3571
uint32_t lba_be; /* big-endian */
3672
uint32_t xfer_be; /* big-endian (blocks) */
3773
uint8_t group; /* 0 */
@@ -61,7 +97,7 @@ typedef struct __attribute__((packed)) scsi_cdb_request_sense6 {
6197
/* START STOP UNIT (6) — opcode 0x1B */
6298
typedef struct __attribute__((packed)) scsi_cdb_start_stop_unit6 {
6399
uint8_t opcode; /* 0x1B */
64-
uint8_t immed; /* bit0 IMMED if you need it */
100+
uint8_t immed; /* bit0 IMMED */
65101
uint8_t rsv1;
66102
uint8_t rsv2;
67103
uint8_t power; /* bits: LOEJ/START/POWER-COND */

src/block/ahci/enquiry.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ bool atapi_enquiry(ahci_hba_port_t *port, ahci_hba_mem_t *abar, uint8_t *out, ui
2424
cdb->alloc_len = len;
2525

2626
if (!issue_and_wait(port, slot, "inquiry")) {
27-
return false;
27+
return atapi_handle_check_condition(port, abar, "inquiry");
2828
}
2929

3030
memcpy(out, aligned_read_buf, len);

0 commit comments

Comments
 (0)