Fix how ATA driver determines the type of a device.

This commit is contained in:
Enrico Fraccaroli (Galfurian)
2023-07-12 14:28:36 -04:00
parent 1cbc7519c9
commit ee2c0ebd88
6 changed files with 143 additions and 181 deletions
+72
View File
@@ -0,0 +1,72 @@
/// @file ata_types.h
/// @brief Data types for managing Advanced Technology Attachment (ATA) devices.
/// @copyright (c) 2014-2023 This file is distributed under the MIT License.
/// See LICENSE.md for details.
/// @addtogroup ata
/// @{
#pragma once
/// @brief ATA Error Bits
typedef enum {
ata_err_amnf = (1 << 0), ///< Address mark not found.
ata_err_tkznf = (1 << 1), ///< Track zero not found.
ata_err_abrt = (1 << 2), ///< Aborted command.
ata_err_mcr = (1 << 3), ///< Media change request.
ata_err_idnf = (1 << 4), ///< ID not found.
ata_err_mc = (1 << 5), ///< Media changed.
ata_err_unc = (1 << 6), ///< Uncorrectable data error.
ata_err_bbk = (1 << 7), ///< Bad Block detected.
} ata_error_t;
/// @brief ATA Status Bits
typedef enum {
ata_status_err = (1 << 0), ///< Indicates an error occurred.
ata_status_idx = (1 << 1), ///< Index. Always set to zero.
ata_status_corr = (1 << 2), ///< Corrected data. Always set to zero.
ata_status_drq = (1 << 3), ///< Set when the drive has PIO data to transfer, or is ready to accept PIO data.
ata_status_srv = (1 << 4), ///< Overlapped Mode Service Request.
ata_status_df = (1 << 5), ///< Drive Fault Error (does not set ERR).
ata_status_rdy = (1 << 6), ///< Bit is clear when drive is spun down, or after an error. Set otherwise.
ata_status_bsy = (1 << 7), ///< The drive is preparing to send/receive data (wait for it to clear).
} ata_status_t;
/// @brief ATA Control Bits
typedef enum {
ata_control_zero = 0x00, ///< Always set to zero.
ata_control_nien = 0x02, ///< Set this to stop the current device from sending interrupts.
ata_control_srst = 0x04, ///< Set, then clear (after 5us), this to do a "Software Reset" on all ATA drives on a bus, if one is misbehaving.
ata_control_hob = 0x80, ///< Set this to read back the High Order Byte (HOB) of the last LBA48 value sent to an IO port.
} ata_control_t;
/// @brief Types of ATA devices.
typedef enum {
ata_dev_type_unknown, ///< Device type not recognized.
ata_dev_type_no_device, ///< No device detected.
ata_dev_type_pata, ///< Parallel ATA drive.
ata_dev_type_sata, ///< Serial ATA drive.
ata_dev_type_patapi, ///< Parallel ATAPI drive.
ata_dev_type_satapi ///< Serial ATAPI drive.
} ata_device_type_t;
/// @brief Values used to manage bus mastering.
typedef enum {
ata_bm_stop_bus_master = 0x00, ///< Halts bus master operations of the controller.
ata_bm_start_bus_master = 0x01, ///< Enables bus master operation of the controller.
} ata_bus_mastering_command_t;
/// @brief DMA specific commands.
typedef enum {
ata_dma_command_read = 0xC8, ///< Read DMA with retries (28 bit LBA).
ata_dma_command_read_no_retry = 0xC9, ///< Read DMA without retries (28 bit LBA).
ata_dma_command_write = 0xCA, ///< Write DMA with retries (28 bit LBA).
ata_dma_command_write_no_retry = 0xCB, ///< Write DMA without retries (28 bit LBA).
} ata_dma_command_t;
/// @brief ATA identity commands.
typedef enum {
ata_command_pata_ident = 0xEC, ///< Identify Device.
ata_command_patapi_ident = 0xA1, ///< Identify Device.
} ata_identity_command_t;
/// @}
+4 -4
View File
@@ -5,10 +5,10 @@
///! @cond Doxygen_Suppress
// Setup the logging for this file (do this before any other include).
#include "sys/kernel_levels.h" // Include kernel log levels.
#define __DEBUG_HEADER__ "[PCI ]" ///< Change header.
#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level.
#include "io/debug.h" // Include debugging functions.
#include "sys/kernel_levels.h" // Include kernel log levels.
#define __DEBUG_HEADER__ "[PCI ]" ///< Change header.
#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level.
#include "io/debug.h" // Include debugging functions.
#include "devices/pci.h"
#include "io/port_io.h"
+62 -172
View File
@@ -11,9 +11,11 @@
#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level.
#include "io/debug.h" // Include debugging functions.
#include "drivers/ata/ata.h"
#include "drivers/ata/ata_types.h"
#include "descriptor_tables/isr.h"
#include "devices/pci.h"
#include "drivers/ata.h"
#include "fcntl.h"
#include "fs/vfs.h"
#include "hardware/pic8259.h"
@@ -26,68 +28,6 @@
#include "sys/errno.h"
#include "system/panic.h"
/// @brief ATA Error Bits
typedef enum {
ata_err_amnf = 0, ///< Address mark not found.
ata_err_tkznf = 1, ///< Track zero not found.
ata_err_abrt = 2, ///< Aborted command.
ata_err_mcr = 3, ///< Media change request.
ata_err_idnf = 4, ///< ID not found.
ata_err_mc = 5, ///< Media changed.
ata_err_unc = 6, ///< Uncorrectable data error.
ata_err_bbk = 7, ///< Bad Block detected.
} ata_error_t;
/// @brief ATA Status Bits
typedef enum {
ata_status_err = (1 << 0), ///< Indicates an error occurred.
ata_status_idx = (1 << 1), ///< Index. Always set to zero.
ata_status_corr = (1 << 2), ///< Corrected data. Always set to zero.
ata_status_drq = (1 << 3), ///< Set when the drive has PIO data to transfer, or is ready to accept PIO data.
ata_status_srv = (1 << 4), ///< Overlapped Mode Service Request.
ata_status_df = (1 << 5), ///< Drive Fault Error (does not set ERR).
ata_status_rdy = (1 << 6), ///< Bit is clear when drive is spun down, or after an error. Set otherwise.
ata_status_bsy = (1 << 7), ///< The drive is preparing to send/receive data (wait for it to clear).
} ata_status_t;
/// @brief ATA Control Bits
typedef enum {
ata_control_zero = 0x00, ///< Always set to zero.
ata_control_nien = 0x02, ///< Set this to stop the current device from sending interrupts.
ata_control_srst = 0x04, ///< Set, then clear (after 5us), this to do a "Software Reset" on all ATA drives on a bus, if one is misbehaving.
ata_control_hob = 0x80, ///< Set this to read back the High Order Byte (HOB) of the last LBA48 value sent to an IO port.
} ata_control_t;
/// @brief Types of ATA devices.
typedef enum {
ata_dev_type_unknown, ///< Device type not recognized.
ata_dev_type_no_device, ///< No device detected.
ata_dev_type_pata, ///< Parallel ATA drive.
ata_dev_type_sata, ///< Serial ATA drive.
ata_dev_type_patapi, ///< Parallel ATAPI drive.
ata_dev_type_satapi ///< Serial ATAPI drive.
} ata_device_type_t;
/// @brief Values used to manage bus mastering.
typedef enum {
ata_bm_stop_bus_master = 0x00, ///< Halts bus master operations of the controller.
ata_bm_start_bus_master = 0x01, ///< Enables bus master operation of the controller.
} ata_bus_mastering_command_t;
/// @brief DMA specific commands.
typedef enum {
ata_dma_command_read = 0xC8, ///< Read DMA with retries (28 bit LBA).
ata_dma_command_read_no_retry = 0xC9, ///< Read DMA without retries (28 bit LBA).
ata_dma_command_write = 0xCA, ///< Write DMA with retries (28 bit LBA).
ata_dma_command_write_no_retry = 0xCB, ///< Write DMA without retries (28 bit LBA).
} ata_dma_command_t;
/// @brief ATA identity commands.
typedef enum {
ata_command_pata_ident = 0xEC, ///< Identify Device.
ata_command_patapi_ident = 0xA1, ///< Identify Device.
} ata_identity_command_t;
/// @brief IDENTIFY device data (response to 0xEC).
typedef struct ata_identity_t {
/// Word 0 : General configuration.
@@ -186,29 +126,6 @@ typedef struct ata_identity_t {
uint16_t unused7[152];
} ata_identity_t;
typedef struct {
/// [R/W] Data Register. Read/Write PIO data bytes (16-bit).
uint8_t data;
/// [R ] Error Register. Read error generated by the last ATA command executed (8-bit).
uint8_t error;
/// [ W] Features Register. Used to control command specific interface features (8-bit).
uint8_t feature;
/// [R/W] Sector Count Register. Number of sectors to read/write (0 is a special value) (8-bit).
uint8_t sector_count;
/// [R/W] Sector Number Register. This is CHS/LBA28/LBA48 specific (8-bit).
uint8_t lba_lo;
/// [R/W] Cylinder Low Register. Partial Disk Sector address (8-bit).
uint8_t lba_mid;
/// [R/W] Cylinder High Register. Partial Disk Sector address (8-bit).
uint8_t lba_hi;
/// [R/W] Drive / Head Register. Used to select a drive and/or head. Supports extra address/flag bits (8-bit).
uint8_t hddevsel;
/// [R ] Status Register. Used to read the current status (8-bit).
uint8_t status;
/// [ W] Command Register. Used to send ATA commands to the device (8-bit).
uint8_t command;
} ata_io_reg_t;
/// @brief Physical Region Descriptor Table (PRDT) entry.
/// @details
/// The physical memory region to be transferred is described by a Physical
@@ -217,7 +134,7 @@ typedef struct {
/// Each Physical Region Descriptor entry is 8 bytes in length.
/// | byte 3 | byte 2 | byte 1 | byte 0 |
/// Dword 0 | Memory Region Physical Base Address [31:1] |0|
/// Dword 1 | EOT | reserved | Byte Count [15:1] |0|
/// Dword 1 | EOT | reserved | Byte Count [15:1] |0|
typedef struct prdt_t {
/// The first 4 bytes specify the byte address of a physical memory region.
unsigned int physical_address;
@@ -263,9 +180,13 @@ typedef struct ata_device_t {
/// The "Control" port base.
uint16_t io_control;
/// If the device is connected to the primary bus.
bool_t primary;
/// If the device master or slave.
bool_t slave;
bool_t primary : 8;
/// If the device is connected to the secondary bus.
bool_t secondary : 8;
/// If the device is master.
bool_t master : 8;
/// If the device is slave.
bool_t slave : 8;
/// The device identity data.
ata_identity_t identity;
/// Bus Master Register. The "address" of the Bus Master Register is stored
@@ -344,6 +265,8 @@ static ata_device_t ata_primary_master = {
},
.io_control = 0x3F6,
.primary = 1,
.secondary = 0,
.master = 1,
.slave = 0
};
@@ -363,6 +286,8 @@ static ata_device_t ata_primary_slave = {
},
.io_control = 0x3F6,
.primary = 1,
.secondary = 0,
.master = 0,
.slave = 1
};
@@ -382,6 +307,8 @@ static ata_device_t ata_secondary_master = {
},
.io_control = 0x376,
.primary = 0,
.secondary = 1,
.master = 1,
.slave = 0
};
@@ -401,6 +328,8 @@ static ata_device_t ata_secondary_slave = {
},
.io_control = 0x376,
.primary = 0,
.secondary = 1,
.master = 0,
.slave = 1
};
@@ -410,28 +339,28 @@ static inline const char *ata_get_device_error_str(uint8_t error)
{
static char str[50] = { 0 };
memset(str, 0, sizeof(str));
if (bit_check(error, ata_err_amnf)) {
if (error & ata_err_amnf) {
strcat(str, "amnf,");
}
if (bit_check(error, ata_err_tkznf)) {
if (error & ata_err_tkznf) {
strcat(str, "tkznf,");
}
if (bit_check(error, ata_err_abrt)) {
if (error & ata_err_abrt) {
strcat(str, "abrt,");
}
if (bit_check(error, ata_err_mcr)) {
if (error & ata_err_mcr) {
strcat(str, "mcr,");
}
if (bit_check(error, ata_err_idnf)) {
if (error & ata_err_idnf) {
strcat(str, "idnf,");
}
if (bit_check(error, ata_err_mc)) {
if (error & ata_err_mc) {
strcat(str, "mc,");
}
if (bit_check(error, ata_err_unc)) {
if (error & ata_err_unc) {
strcat(str, "unc,");
}
if (bit_check(error, ata_err_bbk)) {
if (error & ata_err_bbk) {
strcat(str, "bbk,");
}
return str;
@@ -470,10 +399,7 @@ static inline const char *ata_get_device_status_str(uint8_t status)
static inline const char *ata_get_device_settings_str(ata_device_t *dev)
{
if (dev->io_base == 0x1F0) {
return (dev->slave) ? "Primary Slave" : "Primary Master";
}
return (dev->slave) ? "Secondary Slave" : "Secondary Master";
return (dev->primary) ? ((dev->master) ? "Primary Master" : "Primary Slave") : ((dev->master) ? "Secondary Master" : "Secondary Slave");
}
static inline const char *ata_get_device_type_str(ata_device_type_t type)
@@ -556,25 +482,6 @@ static inline void ata_dump_device(ata_device_t *dev)
pr_debug(" }\n");
}
/// @brief Read all the io registers.
/// @param dev the device for which we read the registers.
/// @return the values of all registers.
static inline ata_io_reg_t ata_read_registers(ata_device_t *dev)
{
return (ata_io_reg_t){
.data = inportb(dev->io_reg.data),
.error = inportb(dev->io_reg.error),
.feature = inportb(dev->io_reg.feature),
.sector_count = inportb(dev->io_reg.sector_count),
.lba_lo = inportb(dev->io_reg.lba_lo),
.lba_mid = inportb(dev->io_reg.lba_mid),
.lba_hi = inportb(dev->io_reg.lba_hi),
.hddevsel = inportb(dev->io_reg.hddevsel),
.status = inportb(dev->io_reg.status),
.command = inportb(dev->io_reg.command)
};
}
/// @brief Waits for 400 nanoseconds.
/// @param dev the device on which we wait.
static inline void ata_io_wait(ata_device_t *dev)
@@ -774,40 +681,10 @@ static inline void ata_dma_initialize_bus_mastering_address(ata_device_t *dev)
static inline ata_device_type_t ata_detect_device_type(ata_device_t *dev)
{
pr_debug("[%s] Detecting device type...\n", ata_get_device_settings_str(dev));
// Disable IRQs.
outportb(dev->io_control, 0x00);
// Wait for the command to work.
ata_io_wait(dev);
// Select the drive.
outportb(dev->io_reg.hddevsel, 0xA0 | (dev->slave << 4U));
// Wait for the command to work.
ata_io_wait(dev);
// Get the "signature bytes" by reading low and high cylinder register.
uint8_t lba_lo = inportb(dev->io_reg.lba_hi);
uint8_t lba_mid = inportb(dev->io_reg.lba_mid);
uint8_t lba_hi = inportb(dev->io_reg.lba_hi);
// Differentiate ATA, ATAPI, SATA and SATAPI.
if ((lba_mid == 0x00) && (lba_hi == 0x00)) {
return ata_dev_type_pata;
}
if ((lba_mid == 0x3C) && (lba_hi == 0xC3)) {
return ata_dev_type_sata;
}
if ((lba_mid == 0x14) && (lba_hi == 0xEB)) {
return ata_dev_type_patapi;
}
if ((lba_mid == 0x69) && (lba_hi == 0x96)) {
return ata_dev_type_satapi;
}
if ((lba_mid == 0xFF) && (lba_hi == 0xFF)) {
return ata_dev_type_no_device;
}
return ata_dev_type_unknown;
}
static bool_t ata_device_init(ata_device_t *dev)
{
pr_debug("[%s] Initializing ATA device...\n", ata_get_device_settings_str(dev));
// Select the ATA device.
outportb(dev->io_base + 1, 1);
// Disable IRQs.
@@ -838,16 +715,39 @@ static bool_t ata_device_init(ata_device_t *dev)
return 1;
}
// Read the identity.
uint16_t *buffer = (uint16_t *)&dev->identity;
for (unsigned i = 0; i < (sizeof(ata_identity_t) / sizeof(uint16_t)); ++i) {
buffer[i] = inports(dev->io_reg.data);
}
inportsw(dev->io_reg.data, (uint16_t *)&dev->identity, (sizeof(ata_identity_t) / sizeof(uint16_t)));
// Fix the serial.
ata_fix_string((char *)&dev->identity.serial_number, count_of(dev->identity.serial_number) - 1);
// Fix the firmware.
ata_fix_string((char *)&dev->identity.firmware_revision, count_of(dev->identity.firmware_revision) - 1);
// Fix the model.
ata_fix_string((char *)&dev->identity.model_number, count_of(dev->identity.model_number) - 1);
// Get the "signature bytes" by reading low and high cylinder register.
uint8_t lba_lo = inportb(dev->io_reg.lba_hi);
uint8_t lba_mid = inportb(dev->io_reg.lba_mid);
uint8_t lba_hi = inportb(dev->io_reg.lba_hi);
// Differentiate ATA, ATAPI, SATA and SATAPI.
if ((lba_mid == 0x00) && (lba_hi == 0x00)) {
return ata_dev_type_pata;
}
if ((lba_mid == 0x3C) && (lba_hi == 0xC3)) {
return ata_dev_type_sata;
}
if ((lba_mid == 0x14) && (lba_hi == 0xEB)) {
return ata_dev_type_patapi;
}
if ((lba_mid == 0x69) && (lba_hi == 0x96)) {
return ata_dev_type_satapi;
}
if ((lba_mid == 0xFF) && (lba_hi == 0xFF)) {
return ata_dev_type_no_device;
}
return ata_dev_type_unknown;
}
static bool_t ata_device_init(ata_device_t *dev)
{
pr_debug("[%s] Initializing ATA device...\n", ata_get_device_settings_str(dev));
// Check the status of the device.
if (ata_status_wait_for(dev, ata_status_drq | ata_status_rdy, 10000)) {
ata_print_status_error(dev);
@@ -1318,6 +1218,9 @@ static ata_device_type_t ata_device_detect(ata_device_t *dev)
if (type == ata_dev_type_unknown) {
pr_debug("[%s] Found unsupported device...\n", ata_get_device_settings_str(dev));
}
if ((type != ata_dev_type_no_device) && (type != ata_dev_type_unknown)) {
pr_notice(" Found %s device connected to %s.\n", ata_get_device_type_str(type), ata_get_device_settings_str(dev));
}
return type;
}
@@ -1365,23 +1268,10 @@ int ata_initialize()
// Enable bus mastering.
ata_dma_enable_bus_mastering();
ata_device_type_t type;
type = ata_device_detect(&ata_primary_master);
if ((type != ata_dev_type_no_device) && (type != ata_dev_type_unknown)) {
pr_info(" Found %s device connected to primary master.\n", ata_get_device_type_str(type));
}
type = ata_device_detect(&ata_primary_slave);
if ((type != ata_dev_type_no_device) && (type != ata_dev_type_unknown)) {
pr_info(" Found %s device connected to primary slave.\n", ata_get_device_type_str(type));
}
type = ata_device_detect(&ata_secondary_master);
if ((type != ata_dev_type_no_device) && (type != ata_dev_type_unknown)) {
pr_info(" Found %s device connected to secondary master.\n", ata_get_device_type_str(type));
}
type = ata_device_detect(&ata_secondary_slave);
if ((type != ata_dev_type_no_device) && (type != ata_dev_type_unknown)) {
pr_info(" Found %s device connected to secondary slave.\n", ata_get_device_type_str(type));
}
ata_device_detect(&ata_primary_master);
ata_device_detect(&ata_primary_slave);
ata_device_detect(&ata_secondary_master);
ata_device_detect(&ata_secondary_slave);
return 0;
}
+1 -1
View File
@@ -15,7 +15,7 @@
#include "descriptor_tables/idt.h"
#include "drivers/keyboard/keyboard.h"
#include "drivers/keyboard/keymap.h"
#include "drivers/ata.h"
#include "drivers/ata/ata.h"
#include "drivers/rtc.h"
#include "drivers/ps2.h"
#include "process/scheduler_feedback.h"
+4 -4
View File
@@ -4,10 +4,10 @@
/// See LICENSE.md for details.
// Setup the logging for this file (do this before any other include).
#include "sys/kernel_levels.h" // Include kernel log levels.
#define __DEBUG_HEADER__ "[PMM ]" ///< Change header.
#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level.
#include "io/debug.h" // Include debugging functions.
#include "sys/kernel_levels.h" // Include kernel log levels.
#define __DEBUG_HEADER__ "[PMM ]" ///< Change header.
#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level.
#include "io/debug.h" // Include debugging functions.
#include "mem/zone_allocator.h"
#include "mem/buddysystem.h"