From 4c8ce44426e56336c12f0a545025b667809818a4 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Sun, 12 Dec 2021 22:32:55 +0100 Subject: [PATCH] Fix ATA read/write and test it. --- CMakeLists.txt | 1 + mentos/inc/devices/pci.h | 96 ++- mentos/src/drivers/ata.c | 1257 ++++++++++++++++++-------------------- mentos/src/kernel.c | 2 +- 4 files changed, 677 insertions(+), 679 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b10c7c..6b32143 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,6 +138,7 @@ elseif(${DEBUGGING_TYPE} STREQUAL DEBUG_STDIO) endif(${DEBUGGING_TYPE} STREQUAL DEBUG_LOG) set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -vga std) set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -m ${MEMORY_SIZE}) +set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -drive file=rootfs.img,format=raw) set(EMULATOR_KERNEL -kernel mentos/kernel-bootloader.bin) set(EMULATOR_FS -initrd initrd) diff --git a/mentos/inc/devices/pci.h b/mentos/inc/devices/pci.h index f1ea378..3b6caa1 100644 --- a/mentos/inc/devices/pci.h +++ b/mentos/inc/devices/pci.h @@ -13,6 +13,90 @@ #include "stdint.h" +typedef enum { + /// @brief If set to 1 the device can respond to I/O Space accesses; + /// otherwise, the device's response is disabled. + pci_command_io_space = 0, + /// @brief If set to 1 the device can respond to Memory Space accesses; + /// otherwise, the device's response is disabled. + pci_command_memory_space = 1, + /// @brief If set to 1 the device can behave as a bus master; otherwise, the + /// device can not generate PCI accesses. + pci_command_bus_master = 2, + /// @brief If set to 1 the device can monitor Special Cycle operations; + /// otherwise, the device will ignore them. + pci_command_special_cycles = 3, + /// @brief If set to 1 the device can generate the Memory Write and + /// Invalidate command; otherwise, the Memory Write command must be used. + pci_command_mw_ie = 4, + /// @brief If set to 1 the device does not respond to palette register + /// writes and will snoop the data; otherwise, the device will trate palette + /// write accesses like all other accesses. + pci_command_vga_palette_snoop = 5, + /// @brief If set to 1 the device will take its normal action when a parity + /// error is detected; otherwise, when an error is detected, the device will + /// set bit 15 of the Status register (Detected Parity Error Status Bit), + /// but will not assert the PERR# (Parity Error) pin and will continue + /// operation as normal. + pci_command_parity_error_response = 6, + /// @brief If set to 1 the SERR# driver is enabled; otherwise, the driver is + /// disabled. + pci_command_serr_enable = 8, + /// @brief If set to 1 indicates a device is allowed to generate fast + /// back-to-back transactions; otherwise, fast back-to-back transactions are + /// only allowed to the same agent. + pci_command_fast_bb_enable = 9, + /// @brief If set to 1 the assertion of the devices INTx# signal is + /// disabled; otherwise, assertion of the signal is enabled. + pci_command_interrupt_disable = 10, +} pci_command_bit_t; + +typedef enum { + /// @brief Represents the state of the device's INTx# signal. If set to 1 + /// and bit 10 of the Command register (Interrupt Disable bit) is set to 0 + /// the signal will be asserted; otherwise, the signal will be ignored. + pci_status_interrupt_status = 3, + /// @brief If set to 1 the device implements the pointer for a New + /// Capabilities Linked list at offset 0x34; otherwise, the linked list is + /// not available. + pci_status_capabilities_list = 4, + /// @brief If set to 1 the device is capable of running at 66 MHz; + /// otherwise, the device runs at 33 MHz. + pci_status_66_MHz_capable = 5, + /// @brief If set to 1 the device can accept fast back-to-back transactions + /// that are not from the same agent; otherwise, transactions can only be + /// accepted from the same agent. + pci_status_fast_bb_capable = 7, + /// @brief This bit is only set when the following conditions are met. The + /// bus agent asserted PERR# on a read or observed an assertion of PERR# on + /// a write, the agent setting the bit acted as the bus master for the + /// operation in which the error occurred, and bit 6 of the Command register + /// (Parity Error Response bit) is set to 1. + pci_status_master_data_parity_error = 8, + /// @brief Read only bits that represent the slowest time that a device will + /// assert DEVSEL# for any bus command except Configuration Space read and + /// writes. Where a value of 0x0 represents fast timing, a value of 0x1 + /// represents medium timing, and a value of 0x2 represents slow timing. + pci_status_devsel_timing_low = 9, + /// @brief The second bit required to set the devsel. + pci_status_devsel_timing_high = 10, + /// @brief This bit will be set to 1 whenever a target device terminates a + /// transaction with Target-Abort. + pci_status_signalled_target_abort = 11, + /// @brief This bit will be set to 1, by a master device, whenever its + /// transaction is terminated with Target-Abort. + pci_status_received_target_abort = 12, + /// @brief This bit will be set to 1, by a master device, whenever its + /// transaction (except for Special Cycle transactions) is terminated with + /// Master-Abort. + pci_status_received_master_abort = 13, + /// @brief This bit will be set to 1 whenever the device asserts SERR#. + pci_status_signalled_system_error = 14, + /// @brief This bit will be set to 1 whenever the device detects a parity + /// error, even if parity error handling is disabled. + pci_status_detected_parity_error = 15, +} pci_status_bit_t; + /// @name PCI Configuration Space /// @brief /// The PCI Specification defines the organization of the 256-byte. @@ -25,7 +109,7 @@ /// @{ /// @brief Identifies the manufacturer of the device (16 bits). Where valid IDs are allocated by PCI-SIG (the list is here) to -/// ensure uniqueness and 0xFFFF is an invalid value that will be returned +/// ensure uniqueness and 0xFFFF is an invalid value that will be returned /// on read accesses to Configuration Space registers of non-existent devices. #define PCI_VENDOR_ID 0x00 @@ -33,7 +117,7 @@ #define PCI_DEVICE_ID 0x02 /// @brief Provides control over a device's ability to generate and -/// respond to PCI cycles (16 bits). Where the only functionality guaranteed to be supported by all +/// respond to PCI cycles (16 bits). Where the only functionality guaranteed to be supported by all /// devices is, when a 0 is written to this register, the device is disconnected /// from the PCI bus for all accesses except Configuration Space access. #define PCI_COMMAND 0x04 @@ -65,10 +149,10 @@ /// Specifies the latency timer in units of PCI bus clocks (8 bits). #define PCI_LATENCY_TIMER 0x0d -/// @brief Identifies the layout of the header based on the type of device it +/// @brief Identifies the layout of the header based on the type of device it /// begins at byte 0x10 of the header (8 bits). A value of 0x00 specifies a general device, a value of 0x01 specifies -/// a PCI-to-PCI bridge, and a value of 0x02 specifies a CardBus bridge. -/// If bit 7 of this register is set, the device has multiple functions; +/// a PCI-to-PCI bridge, and a value of 0x02 specifies a CardBus bridge. +/// If bit 7 of this register is set, the device has multiple functions; /// otherwise, it is a single function device. #define PCI_HEADER_TYPE 0x0e @@ -100,7 +184,7 @@ /// a value of 0xFF defines no connection. #define PCI_INTERRUPT_LINE 0x3c -/// @brief Specifies which interrupt pin the device uses. Where a value of 0x01 is INTA#, 0x02 is INTB#, 0x03 is INTC#, +/// @brief Specifies which interrupt pin the device uses. Where a value of 0x01 is INTA#, 0x02 is INTB#, 0x03 is INTC#, /// 0x04 is INTD#, and 0x00 means the device does not use an interrupt pin. #define PCI_INTERRUPT_PIN 0x3d diff --git a/mentos/src/drivers/ata.c b/mentos/src/drivers/ata.c index 5557ec8..bfb83e0 100644 --- a/mentos/src/drivers/ata.c +++ b/mentos/src/drivers/ata.c @@ -26,25 +26,61 @@ #include "fcntl.h" #include "stdio.h" -/// @name ATA Status registers -/// @{ -#define ATA_STAT_BUSY 0x80 ///< Device Busy -#define ATA_STAT_READY 0x40 ///< Device Ready -#define ATA_STAT_FAULT 0x20 ///< Device Fault -#define ATA_STAT_SEEK 0x10 ///< Device Seek Complete -#define ATA_STAT_DRQ 0x08 ///< Data Request (ready) -#define ATA_STAT_CORR 0x04 ///< Corrected Data Error -#define ATA_STAT_INDEX 0x02 ///< Vendor specific -#define ATA_STAT_ERR 0x01 ///< Error -/// @} +/// @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; -/// @name ATA Control -/// @{ -#define ATA_CONTROL_STOP_INTERRUPT 0x02 -#define ATA_CONTROL_SOFTWARE_RESET 0x04 -#define ATA_CONTROL_HIGH_ORDER_BYTE 0x80 -#define ATA_CONTROL_ZERO 0x00 -/// @} +/// @brief ATA Status Bits +typedef enum { + ata_status_err = 0, ///< Indicates an error occurred. + ata_status_idx = 1, ///< Index. Always set to zero. + ata_status_corr = 2, ///< Corrected data. Always set to zero. + ata_status_drq = 3, ///< Set when the drive has PIO data to transfer, or is ready to accept PIO data. + ata_status_srv = 4, ///< Overlapped Mode Service Request. + ata_status_df = 5, ///< Drive Fault Error (does not set ERR). + ata_status_rdy = 6, ///< Bit is clear when drive is spun down, or after an error. Set otherwise. + ata_status_bsy = 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, + ata_dev_type_no_device, + ata_dev_type_ata, + ata_dev_type_sata, + ata_dev_type_atapi, + ata_dev_type_satapi +} 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; /// @name ATA Commands /// @{ @@ -60,22 +96,12 @@ #define ATA_CMD_RD_MULT 0xC4 ///< Read Multiple #define ATA_CMD_WR_MULT 0xC5 ///< Write Multiple #define ATA_CMD_SETMULT 0xC6 ///< Set Multiple Mode -#define ATA_CMD_RD_DMA 0xC8 ///< Read DMA (with retries) -#define ATA_CMD_RD_DMAN 0xC9 ///< Read DMS (no retries) -#define ATA_CMD_WR_DMA 0xCA ///< Write DMA (with retries) -#define ATA_CMD_WR_DMAN 0xCB ///< Write DMA (no retires) #define ATA_CMD_IDENT 0xEC ///< Identify Device #define ATA_CMD_CH_FLSH 0xE7 ///< Cache flush. #define ATA_CMD_SETF 0xEF ///< Set Features #define ATA_CMD_CHK_PWR 0xE5 ///< Check Power Mode /// @} -/// @name ATAPI Commands -/// @{ -#define ATAPI_CMD_PACKET 0xA0 ///< -#define ATAPI_CMD_ID_PCKT 0xA1 ///< -/// @} - /// @name ATA Identification Space Bits /// @brief Definitions used to read information from the identification space. /// @{ @@ -104,27 +130,6 @@ #define ATA_DEVICE_1 0x01 ///< /// @} -/// @name ATA Command block registers -/// @{ -#define ATA_REG_DATA 0x00 ///< Read/Write PIO data bytes. -#define ATA_REG_ERROR 0x01 ///< Error generated by the last ATA command. -#define ATA_REG_FEATURES 0x01 ///< Control command specific interface features. -#define ATA_REG_SECCOUNT0 0x02 ///< Number of sectors to read/write (0 is a special value). -#define ATA_REG_LBA0 0x03 ///< Sector Number Register. -#define ATA_REG_LBA1 0x04 ///< Cylinder Low Register. -#define ATA_REG_LBA2 0x05 ///< Cylinder High Register. -#define ATA_REG_HDDEVSEL 0x06 ///< Used to select a drive and/or head. -#define ATA_REG_COMMAND 0x07 ///< Used to send ATA commands to the device. -#define ATA_REG_STATUS 0x07 ///< Used to read the current status. -#define ATA_REG_SECCOUNT1 0x08 ///< -#define ATA_REG_LBA3 0x09 ///< -#define ATA_REG_LBA4 0x0A ///< -#define ATA_REG_LBA5 0x0B ///< -#define ATA_REG_CONTROL 0x0C ///< -#define ATA_REG_ALTSTATUS 0x0C ///< -#define ATA_REG_DEVADDRESS 0x0D ///< -/// @} - /// @name ATA Channels /// @{ #define ATA_PRIMARY 0x00 ///< Primary channel. @@ -186,44 +191,108 @@ typedef struct ata_identify_t { uint16_t unused6[38]; ///< uint64_t sectors_48; ///< uint16_t unused7[152]; ///< -} __attribute__((packed)) ata_identify_t; +} ata_identify_t; /// @brief Master Boot Record. typedef struct mbr_t { uint8_t boostrap[446]; ///< partition_t partitions[4]; ///< uint8_t signature[2]; ///< -} __attribute__((packed)) mbr_t; +} mbr_t; -typedef union atapi_command_t { - uint8_t command_bytes[12]; - uint16_t command_words[6]; -} atapi_command_t; - -/// @brief Physical Region Descriptor Tables (PRDT). +/// @brief Physical Region Descriptor Table (PRDT) entry. +/// @details +/// The physical memory region to be transferred is described by a Physical +/// Region Descriptor (PRD). The data transfer will proceed until all regions +/// described by the PRDs in the table have been transferred. +/// 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| typedef struct prdt_t { - uintptr_t offset; - uint16_t bytes; - uint16_t last; + /// The first 4 bytes specify the byte address of a physical memory region. + unsigned int physical_address; + /// The next two bytes specify the count of the region in bytes (64K byte limit per region). + unsigned short byte_count; + /// Bit 7 of the last byte indicates the end of the table + unsigned short end_of_table; } prdt_t; /// @brief Stores information about an ATA device. typedef struct ata_device_t { /// Name of the device. char name[256]; + /// Name of the device. + char path[PATH_MAX]; /// Does the device support ATA Packet Interface (ATAPI). - bool_t is_atapi; - /// Base address. + ata_device_type_t type; + /// The "I/O" port base. unsigned io_base; - unsigned control; - unsigned slave; - ata_identify_t identity; - - uint32_t bar4; struct { - uint32_t command; - uint32_t status; - uint32_t prdt; + /// [R/W] Data Register. Read/Write PIO data bytes (16-bit). + unsigned data; + /// [R ] Error Register. Read error generated by the last ATA command executed (8-bit). + unsigned error; + /// [ W] Features Register. Used to control command specific interface features (8-bit). + unsigned feature; + /// [R/W] Sector Count Register. Number of sectors to read/write (0 is a special value) (8-bit). + unsigned sector_count; + /// [R/W] Sector Number Register. This is CHS/LBA28/LBA48 specific (8-bit). + unsigned lba_lo; + /// [R/W] Cylinder Low Register. Partial Disk Sector address (8-bit). + unsigned lba_mid; + /// [R/W] Cylinder High Register. Partial Disk Sector address (8-bit). + unsigned lba_hi; + /// [R/W] Drive / Head Register. Used to select a drive and/or head. Supports extra address/flag bits (8-bit). + unsigned hddevsel; + /// [R ] Status Register. Used to read the current status (8-bit). + unsigned status; + /// [ W] Command Register. Used to send ATA commands to the device (8-bit). + unsigned command; + } io_reg; + /// The "Control" port base. + unsigned control_base; + /// If the device master or slave. + unsigned slave; + /// If the device is connected to the primary bus. + bool_t primary; + /// The device identity data. + ata_identify_t identity; + /// Bus Master Register. The "address" of the Bus Master Register is stored + /// in BAR4, in the PCI Configuration Space of the disk controller. The Bus + /// Master Register is generally a set of 16 sequential IO ports. It can + /// also be a 16 byte memory mapped space. + struct { + /// The command byte has only 2 operational bits. All the rest should be + /// 0. Bit 0 (value = 1) is the Start/Stop bit. Setting the bit puts the + /// controller in DMA mode for that ATA channel, and it starts at the + /// beginning of the respective PRDT. Clearing the bit terminates DMA + /// mode for that ATA channel. If the controller was in the middle of a + /// transfer, the remaining data is thrown away. Also, the controller + /// does not remember how far it got in the PRDT. That information is + /// lost, if the OS does not save it. The bit must be cleared when a + /// transfer completes. + /// Bit 3 (value = 8) is the Read/Write bit. This bit is a huge problem. + /// The disk controller does not automatically detect whether the next + /// disk operation is a read or write. You have to tell it, in advance, + /// by setting this bit. Note that when reading from the disk, you must + /// set this bit to 1, and clear it when writing to the disk. You must + /// first stop DMA transfers (by clearing bit 0) before you can change + /// the Read/Write bit! Please note all the bad consequences of clearing + /// bit 0, above! The controller loses its place in the PRDT. + /// In essence, this means that each PRDT must consist exclusively of + /// either read or write entries. You set the Read/Write bit in advance, + /// then "use up" the entire PRDT -- before you can do the opposite + /// operation. + unsigned command; + /// The bits in the status byte are not usually useful. However, you are + /// required to read it after every IRQ on disk reads anyway. Reading this + /// byte may perform a necessary final cache flush of the DMA data to + /// memory. + unsigned status; + /// Physical Region Descriptor Table (PRDT). The PRDT must be uint32_t + /// aligned, contiguous in physical memory, and cannot cross a 64K boundary. + unsigned prdt; } bmr; uint32_t atapi_lba; @@ -246,19 +315,88 @@ typedef struct ata_device_t { static spinlock_t ata_lock; static wait_queue_head_t ata_wait_queue; -static char ata_drive_char = 'a'; -static int cdrom_number = 0; -static uint32_t ata_pci = 0x00000000; -static int atapi_in_progress = 0; +static char ata_drive_char = 'a'; +static int cdrom_number = 0; +static uint32_t ata_pci = 0x00000000; -static ata_device_t ata_primary_master = { .io_base = 0x1F0, .control = 0x3F6, .slave = 0 }; -static ata_device_t ata_primary_slave = { .io_base = 0x1F0, .control = 0x3F6, .slave = 1 }; -static ata_device_t ata_secondary_master = { .io_base = 0x170, .control = 0x376, .slave = 0 }; -static ata_device_t ata_secondary_slave = { .io_base = 0x170, .control = 0x376, .slave = 1 }; +static ata_device_t ata_primary_master = { + .io_base = 0x1F0, + .io_reg = { + .data = 0x1F0 + 0x00, + .error = 0x1F0 + 0x01, + .feature = 0x1F0 + 0x01, + .sector_count = 0x1F0 + 0x02, + .lba_lo = 0x1F0 + 0x03, + .lba_mid = 0x1F0 + 0x04, + .lba_hi = 0x1F0 + 0x05, + .hddevsel = 0x1F0 + 0x06, + .status = 0x1F0 + 0x07, + .command = 0x1F0 + 0x07, + }, + .control_base = 0x3F6, + .slave = 0, + .primary = true +}; + +static ata_device_t ata_primary_slave = { + .io_base = 0x1F0, + .io_reg = { + .data = 0x1F0 + 0x00, + .error = 0x1F0 + 0x01, + .feature = 0x1F0 + 0x01, + .sector_count = 0x1F0 + 0x02, + .lba_lo = 0x1F0 + 0x03, + .lba_mid = 0x1F0 + 0x04, + .lba_hi = 0x1F0 + 0x05, + .hddevsel = 0x1F0 + 0x06, + .status = 0x1F0 + 0x07, + .command = 0x1F0 + 0x07, + }, + .control_base = 0x3F6, + .slave = 1, + .primary = true +}; + +static ata_device_t ata_secondary_master = { + .io_base = 0x170, + .io_reg = { + .data = 0x170 + 0x00, + .error = 0x170 + 0x01, + .feature = 0x170 + 0x01, + .sector_count = 0x170 + 0x02, + .lba_lo = 0x170 + 0x03, + .lba_mid = 0x170 + 0x04, + .lba_hi = 0x170 + 0x05, + .hddevsel = 0x170 + 0x06, + .status = 0x170 + 0x07, + .command = 0x170 + 0x07, + }, + .control_base = 0x376, + .slave = 0, + .primary = false +}; + +static ata_device_t ata_secondary_slave = { + .io_base = 0x170, + .io_reg = { + .data = 0x170 + 0x00, + .error = 0x170 + 0x01, + .feature = 0x170 + 0x01, + .sector_count = 0x170 + 0x02, + .lba_lo = 0x170 + 0x03, + .lba_mid = 0x170 + 0x04, + .lba_hi = 0x170 + 0x05, + .hddevsel = 0x170 + 0x06, + .status = 0x170 + 0x07, + .command = 0x170 + 0x07, + }, + .control_base = 0x376, + .slave = 1, + .primary = false +}; static void ata_device_read_sector(ata_device_t *, uint64_t, uint8_t *); -static void ata_device_read_sector_atapi(ata_device_t *, uint64_t, uint8_t *); -static void ata_device_write_sector_retry(ata_device_t *, uint64_t, uint8_t *); +static void ata_device_write_sector(ata_device_t *, uint64_t, uint8_t *); static vfs_file_t *ata_open(const char *, int, mode_t); static int ata_close(vfs_file_t *); @@ -271,47 +409,52 @@ static int ata_stat(const char *path, stat_t *stat); // == SUPPORT FUNCTIONS ======================================================= /// @brief Waits for 400 nanoseconds. -/// @param dev -void ata_io_wait(ata_device_t *dev) +/// @param dev the device on which we wait. +static inline void ata_io_wait(ata_device_t *dev) { - inportb(dev->control); - inportb(dev->control); - inportb(dev->control); - inportb(dev->control); + inportb(dev->control_base); + inportb(dev->control_base); + inportb(dev->control_base); + inportb(dev->control_base); } -static uint8_t ata_status_wait(ata_device_t *dev, int timeout) +static inline uint8_t ata_status_wait(ata_device_t *dev, int timeout) { uint8_t status; if (timeout > 0) { - for (int i = 0; (status = inportb(dev->io_base + ATA_REG_STATUS)) & ATA_STAT_BUSY && (i < timeout); ++i) {} + while (timeout--) { + if (!bit_check((status = inportb(dev->io_reg.status)), ata_status_bsy)) + break; + } } else { - while ((status = inportb(dev->io_base + ATA_REG_STATUS)) & ATA_STAT_BUSY) {} + while (bit_check((status = inportb(dev->io_reg.status)), ata_status_bsy)) { + cpu_relax(); + } } return status; } -static int ata_wait(ata_device_t *dev, bool_t advanced) +static inline int ata_wait(ata_device_t *dev, bool_t advanced) { ata_io_wait(dev); ata_status_wait(dev, 0); if (advanced) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if (status & ATA_STAT_ERR) + uint8_t status = inportb(dev->io_reg.status); + if (bit_check(status, ata_status_err)) return 1; - if (status & ATA_STAT_FAULT) + if (bit_check(status, ata_status_df)) return 1; - if (!(status & ATA_STAT_DRQ)) + if (!bit_check(status, ata_status_drq)) return 1; } return 0; } -static void ata_device_select(ata_device_t *dev) +static inline void ata_device_select(ata_device_t *dev) { outportb(dev->io_base + 1, 1); - outportb(dev->control, 0); - outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); + outportb(dev->control_base, 0); + outportb(dev->io_reg.hddevsel, 0xA0 | dev->slave << 4); ata_io_wait(dev); } @@ -325,15 +468,6 @@ static inline uint64_t ata_max_offset(ata_device_t *dev) return sectors * ATA_SECTOR_SIZE; } -static inline uint64_t atapi_max_offset(ata_device_t *dev) -{ - uint64_t max_sector = dev->atapi_lba; - if (!max_sector) { - return 0; - } - return (max_sector + 1) * dev->atapi_sector_size; -} - static inline void ata_fix_string(char *str, unsigned len) { char tmp; @@ -345,8 +479,12 @@ static inline void ata_fix_string(char *str, unsigned len) str[len] = 0; } -static bool_t ata_read_device_identity(ata_device_t *dev) +static inline bool_t ata_read_device_identity(ata_device_t *dev, uint8_t command) { + // Request the device identity. + outportb(dev->io_reg.command, command); + // Wait 400ns for the command to work. + ata_io_wait(dev); // Read the identity. uint16_t *buffer = (uint16_t *)&dev->identity; for (unsigned i = 0; i < 256; ++i) { @@ -361,7 +499,123 @@ static bool_t ata_read_device_identity(ata_device_t *dev) return true; } -static int buffer_compare(uint32_t *ptr1, uint32_t *ptr2, size_t size) +static inline const char *ata_get_device_status_str(ata_device_t *dev) +{ + static char status_str[9] = { 0 }; + uint8_t status = inportb(dev->io_reg.status); + status_str[ata_status_err] = bit_check(status, ata_status_err) ? 'e' : ' '; + status_str[ata_status_idx] = bit_check(status, ata_status_idx) ? 'i' : ' '; + status_str[ata_status_corr] = bit_check(status, ata_status_corr) ? 'c' : ' '; + status_str[ata_status_drq] = bit_check(status, ata_status_drq) ? 'd' : ' '; + status_str[ata_status_srv] = bit_check(status, ata_status_srv) ? 's' : ' '; + status_str[ata_status_df] = bit_check(status, ata_status_df) ? 'd' : ' '; + status_str[ata_status_rdy] = bit_check(status, ata_status_rdy) ? 'r' : ' '; + status_str[ata_status_bsy] = bit_check(status, ata_status_bsy) ? 'b' : ' '; + return status_str; +} + +/// @brief Performs a soft reset of the device. +/// @details "For non-ATAPI drives, the only method a driver has of resetting a +/// drive after a major error is to do a "software reset" on the bus. Set bit 2 +/// (SRST, value = 4) in the proper Control Register for the bus. This will +/// reset both ATA devices on the bus." +/// @param dev the device on which we perform the soft reset. +static inline void ata_soft_reset(ata_device_t *dev) +{ + // Do a "software reset" on the bus. + outportb(dev->control_base, ata_control_srst); + // Wait for the soft reset to complete. + ata_io_wait(dev); + // Reset the bus to normal operation. + outportb(dev->control_base, ata_control_zero); +} + +static inline void ata_enable_bus_mastering(ata_device_t *dev) +{ + uint16_t pci_cmd = pci_read_field(ata_pci, PCI_COMMAND, 4); + if (bit_check(pci_cmd, pci_command_bus_master)) { + pr_warning("Bus mastering already enabled.\n"); + } else { + // Set the bit for bus mastering. + bit_set_assign(pci_cmd, pci_command_bus_master); + // Write the PCI command field. + pci_write_field(ata_pci, PCI_COMMAND, 4, pci_cmd); + // Check that the bus mastering is enabled. + pci_cmd = pci_read_field(ata_pci, PCI_COMMAND, 4); + if (!bit_check(pci_cmd, pci_command_bus_master)) { + pr_warning("Bus mastering is not correctly set.\n"); + } + } +} + +/// @brief Initialize the bmr field of the ATA device. +/// @param dev the device to initialize. +/// @details +/// When you want to retrieve the actual base address of a BAR, be sure to mask +/// the lower bits. +/// For 16-bit Memory Space BARs, you calculate (BAR[x] & 0xFFF0). +/// For 32-bit Memory Space BARs, you calculate (BAR[x] & 0xFFFFFFF0). +static inline void ata_initialize_bus_mastering_address(ata_device_t *dev) +{ + unsigned address = pci_read_field(ata_pci, PCI_BASE_ADDRESS_4, 4); + // To distinguish between memory space BARs and I/O space BARs, you can + // check the value of the lowest bit. memory space BARs has always a 0, + // while I/O space BARs has always a 1. + assert(bit_check(address, 0)); + /// When you want to retrieve the actual base address of a BAR, be sure to + /// mask the lower bits, for I/O space BARs you calculate (BAR & 0xFFFFFFFC). + address &= 0xFFFFFFFC; + // Differentiate between primary or secondary ATA bus. + if (dev->primary) { + dev->bmr.command = address + 0x0; + dev->bmr.status = address + 0x2; + dev->bmr.prdt = address + 0x4; + } else { + dev->bmr.command = address + 0x8; + dev->bmr.status = address + 0xA; + dev->bmr.prdt = address + 0xC; + } +} + +/* on Primary bus: ctrl->base =0x1F0, ctrl->dev_ctl =0x3F6. REG_CYL_LO=4, REG_CYL_HI=5, REG_DEVSEL=6 */ +static inline ata_device_type_t ata_detect_device_type(ata_device_t *dev) +{ + // Perform a soft reset. + ata_soft_reset(dev); + // Wait until master drive is ready again. + ata_io_wait(dev); + // Select the drive. + outportb(dev->io_reg.hddevsel, 0xA0 | (dev->slave << 4)); + // Wait for drive select to work. + ata_io_wait(dev); +#if 0 + ata_status_wait(dev, 10000); +#endif + // Get the "signature bytes" by reading low and high cylinder register. + uint8_t cyl_lo = inportb(dev->io_reg.lba_mid); + uint8_t cyl_hi = inportb(dev->io_reg.lba_hi); +#if 0 + uint8_t status = ata_status_wait(dev, 5000); + if (bit_check(status, ata_status_bsy)) { + pr_debug("No drive(s) present\n"); + return ata_dev_type_no_device; + } +#endif + // Differentiate ATA, ATAPI, SATA and SATAPI. + if ((cyl_lo == 0x00) && (cyl_hi == 0x00)) + return ata_dev_type_ata; + if ((cyl_lo == 0x3C) && (cyl_hi == 0xC3)) + return ata_dev_type_sata; + if ((cyl_lo == 0x14) && (cyl_hi == 0xEB)) + return ata_dev_type_atapi; + if ((cyl_lo == 0x69) && (cyl_hi == 0x96)) + return ata_dev_type_satapi; + if ((cyl_lo == 0xFF) && (cyl_hi == 0xFF)) + return ata_dev_type_no_device; + return ata_dev_type_unknown; +} + +static inline int buffer_compare(uint32_t *ptr1, uint32_t *ptr2, size_t size) { assert(!(size % 4)); @@ -379,7 +633,11 @@ static int buffer_compare(uint32_t *ptr1, uint32_t *ptr2, size_t size) return 0; } -static uintptr_t malloc_dma(size_t size, uintptr_t *physical) +/// @brief Creates the DMA memory area used to write and read on the device. +/// @param size the size of the DMA memory area. +/// @param physical the physical address of the DMA memory area. +/// @return the logical address of the DMA memory area. +static inline uintptr_t malloc_dma(size_t size, uintptr_t *physical) { uint32_t order = find_nearest_order_greater(0, sizeof(prdt_t)); page_t *page = _alloc_pages(GFP_KERNEL, order); @@ -387,6 +645,181 @@ static uintptr_t malloc_dma(size_t size, uintptr_t *physical) return get_lowmem_address_from_page(page); } +// == ATA DEVICE MANAGEMENT =================================================== +static bool_t ata_device_init(ata_device_t *dev) +{ + pr_debug("Detected ATA device on bus 0x%3x\n", dev->io_base); + + // Select the ATA device. + ata_device_select(dev); + + // Read the ATA device identity. + ata_read_device_identity(dev, ATA_CMD_IDENT); + + // Allocate the memory for the Physical Region Descriptor Table (PRDT). + dev->dma_prdt = (prdt_t *)malloc_dma(sizeof(prdt_t), &dev->dma_prdt_phys); + // Allocate the memory for the Direct Memory Access (DMA). + dev->dma_start = (uint8_t *)malloc_dma(ATA_DMA_SIZE, &dev->dma_start_phys); + // Initialize the table, specifying the physical address of the DMA. + dev->dma_prdt->physical_address = dev->dma_start_phys; + // The size of the DMA. + dev->dma_prdt->byte_count = ATA_DMA_SIZE; + // Set the EOT to 1. + dev->dma_prdt->end_of_table = 1; + + // Update the filesystem entry with the length of the device. + dev->fs_root->length = ata_max_offset(dev); + + // Enable bus mastering. + ata_enable_bus_mastering(dev); + + // Initialize the bus mastering addresses. + ata_initialize_bus_mastering_address(dev); + + // Print the device data. + pr_debug("Device name : %s\n", dev->name); + pr_debug("Device status : [%s]\n", ata_get_device_status_str(dev)); + pr_debug("Device Serial : %s\n", dev->identity.serial); + pr_debug("Device Firmware : %s\n", dev->identity.firmware); + pr_debug("Device Model : %s\n", dev->identity.model); + pr_debug("Sectors (48) : %d\n", dev->identity.sectors_48); + pr_debug("Sectors (24) : %d\n", dev->identity.sectors_28); + pr_debug("PCI device ID : 0x%x\n", ata_pci); + pr_debug("Device PRDT : 0x%x (Ph: 0x%x)\n", dev->dma_prdt, dev->dma_prdt_phys); + pr_debug("Device START : 0x%x (Ph: 0x%x)\n", dev->dma_start, dev->dma_start_phys); + pr_debug("BMR Command : 0x%x\n", dev->bmr.command); + pr_debug("BMR Status : 0x%x\n", dev->bmr.status); + pr_debug("BMR Prdt : 0x%x\n", dev->bmr.prdt); + return 0; +} + +// == ATA SECTOR READ/WRITE FUNCTIONS ========================================= +static void ata_device_read_sector(ata_device_t *dev, uint32_t lba, uint8_t *buffer) +{ + // Check if we are trying to perform the read on the correct drive type. + if ((dev->type != ata_dev_type_ata) || (dev->type != ata_dev_type_sata)) { + return; + } + pr_debug("ata_device_read_sector(%p, %d, %p)\n", dev, lba, buffer); + spinlock_lock(&ata_lock); + + ata_wait(dev, false); + + // Stop. + outportb(dev->bmr.command, 0x00); + // Set the PRDT. + outportl(dev->bmr.prdt, dev->dma_prdt_phys); + // Enable error, irq status. + outportb(dev->bmr.status, inportb(dev->bmr.status) | 0x04 | 0x02); + // Set read. + outportb(dev->bmr.command, 0x08); + + do { + uint8_t status = inportb(dev->io_reg.status); + if (!bit_check(status, ata_status_bsy)) { + break; + } + } while (true); + + outportb(dev->control_base, 0x00); + outportb(dev->io_reg.hddevsel, 0xe0 | (dev->slave << 4)); + ata_io_wait(dev); + outportb(dev->io_reg.feature, 0x00); + + outportb(dev->io_reg.sector_count, 0); + outportb(dev->io_reg.lba_lo, (lba & 0xff000000) >> 24); + outportb(dev->io_reg.lba_mid, (lba & 0xff00000000) >> 32); + outportb(dev->io_reg.lba_hi, (lba & 0xff0000000000) >> 40); + + outportb(dev->io_reg.sector_count, 1); + outportb(dev->io_reg.lba_lo, (lba & 0x000000ff) >> 0); + outportb(dev->io_reg.lba_mid, (lba & 0x0000ff00) >> 8); + outportb(dev->io_reg.lba_hi, (lba & 0x00ff0000) >> 16); + + while (1) { + uint8_t status = inportb(dev->io_reg.status); + if (!bit_check(status, ata_status_bsy) && bit_check(status, ata_status_rdy)) + break; + } + outportb(dev->io_reg.command, ata_dma_command_read); + + ata_io_wait(dev); + + outportb(dev->bmr.command, 0x08 | 0x01); + + while (1) { + int status = inportb(dev->bmr.status); + int dstatus = inportb(dev->io_reg.status); + if (!(status & 0x04)) { + continue; + } + if (!bit_check(dstatus, ata_status_bsy)) { + break; + } + } + + // Copy from DMA buffer to output buffer. + memcpy(buffer, dev->dma_start, ATA_DMA_SIZE); + + // Inform device we are done. + outportb(dev->bmr.status, inportb(dev->bmr.status) | 0x04 | 0x02); + + spinlock_unlock(&ata_lock); +} + +static void ata_device_write_sector(ata_device_t *dev, uint32_t lba, uint8_t *buffer) +{ + spinlock_lock(&ata_lock); + + // Copy the buffer over to the DMA area + memcpy(dev->dma_prdt, buffer, ATA_DMA_SIZE); + + // Reset bus master register's command register + outportb(dev->bmr.command, 0); + + // Set prdt + outportl(dev->bmr.prdt, dev->dma_prdt_phys); + + // Enable error, irq status. + outportb(dev->bmr.status, inportb(dev->bmr.status) | 0x04 | 0x02); + + // Select drive + ata_wait(dev, false); + outportb(dev->io_reg.hddevsel, 0xe0 | dev->slave << 4 | (lba & 0x0f000000) >> 24); + ata_wait(dev, false); + + // Set sector counts and LBAs + outportb(dev->io_reg.feature, 0x00); + outportb(dev->io_reg.sector_count, 1); + outportb(dev->io_reg.lba_lo, lba & 0x000000ff); + outportb(dev->io_reg.lba_mid, (lba & 0x0000ff00) >> 8); + outportb(dev->io_reg.lba_hi, (lba & 0x00ff0000) >> 16); + ata_wait(dev, false); + + // Notify that we are starting DMA writing. + outportb(dev->io_reg.command, ata_dma_command_write); + + // Start DMA Writing. + outportb(dev->bmr.command, 0x1); + + // Wait for dma write to complete. + while (1) { + int status = inportb(dev->bmr.status); + int dstatus = inportb(dev->io_reg.status); + if (!(status & 0x04)) { + continue; + } + if (!(dstatus & 0x80)) { + break; + } + } + + // Inform device we are done. + outportb(dev->bmr.status, inportb(dev->bmr.status) | 0x04 | 0x02); + + spinlock_unlock(&ata_lock); +} + // == VFS ENTRY GENERATION ==================================================== /// Filesystem general operations. static vfs_sys_operations_t ata_sys_operations = { @@ -408,43 +841,12 @@ static vfs_file_operations_t ata_fs_operations = { .getdents_f = NULL }; -/// ATAPI filesystem file operations. -static vfs_file_operations_t atapi_fs_operations = { - .open_f = ata_open, - .unlink_f = NULL, - .close_f = ata_close, - .read_f = atapi_read, - .write_f = NULL, - .lseek_f = NULL, - .stat_f = ata_fstat, - .ioctl_f = NULL, - .getdents_f = NULL -}; - -static vfs_file_t *atapi_device_create(ata_device_t *device) +static vfs_file_t *ata_device_create(ata_device_t *dev) { - char path[PATH_MAX]; - sprintf(path, "/dev/%s", device->name); // Create the file. - vfs_file_t *file = vfs_open(path, O_RDONLY | O_CREAT, 0); + vfs_file_t *file = vfs_open(dev->path, O_RDWR | O_CREAT, 0); // Set the device. - file->device = device; - // Re-set the flags. - file->flags = DT_BLK; - // Change the operations. - file->sys_operations = &ata_sys_operations; - file->fs_operations = &atapi_fs_operations; - return file; -} - -static vfs_file_t *ata_device_create(ata_device_t *device) -{ - char path[PATH_MAX]; - sprintf(path, "/dev/%s", device->name); - // Create the file. - vfs_file_t *file = vfs_open(path, O_RDWR | O_CREAT, 0); - // Set the device. - file->device = device; + file->device = dev; // Re-set the flags. file->flags = DT_BLK; // Change the operations. @@ -541,56 +943,6 @@ static ssize_t ata_read(vfs_file_t *file, char *buffer, off_t offset, size_t siz return size; } -static ssize_t atapi_read(vfs_file_t *file, char *buffer, off_t offset, size_t size) -{ - pr_debug("atapi_read(%p, %p, %d, %d)\n", file, buffer, offset, size); - ata_device_t *dev = (ata_device_t *)file->device; - assert(dev && "Device not set."); - - uint32_t start_block = offset / dev->atapi_sector_size; - uint32_t start_offset = offset % dev->atapi_sector_size; - uint32_t end_block = (offset + size - 1) / dev->atapi_sector_size; - uint32_t end_offset = (offset + size - 1) % dev->atapi_sector_size; - uint32_t prefix_size = (dev->atapi_sector_size - start_offset); - uint32_t postfix_size = (offset + size) % dev->atapi_sector_size; - uint32_t max_offset = atapi_max_offset(dev); - uint32_t x_offset = 0; - - // Check if with the offset we are exceeding the size. - if (offset > max_offset) { - return 0; - } - - // Check if we are going to readoing over the size. - if (offset + size > max_offset) { - size = max_offset - offset; - } - - if (start_offset) { - char *support_buffer = kmalloc(dev->atapi_sector_size); - ata_device_read_sector_atapi(dev, start_block, (uint8_t *)support_buffer); - memcpy(buffer, (void *)((uintptr_t)support_buffer + (start_offset)), prefix_size); - kfree(support_buffer); - x_offset += prefix_size; - start_block++; - } - - if (postfix_size && (start_block <= end_block)) { - char *support_buffer = kmalloc(dev->atapi_sector_size); - ata_device_read_sector_atapi(dev, end_block, (uint8_t *)support_buffer); - memcpy((void *)((uintptr_t)buffer + size - postfix_size), support_buffer, postfix_size); - kfree(support_buffer); - end_block--; - } - - while (start_block <= end_block) { - ata_device_read_sector_atapi(dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset)); - x_offset += dev->atapi_sector_size; - start_block++; - } - return size; -} - static ssize_t ata_write(vfs_file_t *file, const void *buffer, off_t offset, size_t size) { static char support_buffer[ATA_SECTOR_SIZE]; @@ -621,7 +973,7 @@ static ssize_t ata_write(vfs_file_t *file, const void *buffer, off_t offset, siz if (start_offset) { ata_device_read_sector(dev, start_block, (uint8_t *)support_buffer); memcpy((void *)((uintptr_t)support_buffer + (start_offset)), buffer, prefix_size); - ata_device_write_sector_retry(dev, start_block, (uint8_t *)support_buffer); + ata_device_write_sector(dev, start_block, (uint8_t *)support_buffer); x_offset += prefix_size; ++start_block; } @@ -629,18 +981,19 @@ static ssize_t ata_write(vfs_file_t *file, const void *buffer, off_t offset, siz if (postfix_size && (start_block <= end_block)) { ata_device_read_sector(dev, end_block, (uint8_t *)support_buffer); memcpy(support_buffer, (void *)((uintptr_t)buffer + size - postfix_size), postfix_size); - ata_device_write_sector_retry(dev, end_block, (uint8_t *)support_buffer); + ata_device_write_sector(dev, end_block, (uint8_t *)support_buffer); --end_block; } while (start_block <= end_block) { - ata_device_write_sector_retry(dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset)); + ata_device_write_sector(dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset)); x_offset += ATA_SECTOR_SIZE; ++start_block; } return size; } + static int _ata_stat(const ata_device_t *device, stat_t *stat) { if (device) { @@ -679,496 +1032,66 @@ static int ata_stat(const char *path, stat_t *stat) return -1; } -// == ATA DEVICE MANAGEMENT =================================================== -static bool_t ata_device_init(ata_device_t *dev) -{ - pr_debug("Detected IDE device on bus 0x%3x\n", dev->io_base); - pr_debug("Device name: %s\n", dev->name); - ata_device_select(dev); - outportb(dev->io_base + ATA_REG_COMMAND, ATA_CMD_IDENT); - ata_io_wait(dev); - uint8_t status = inportb(dev->io_base + ATA_REG_COMMAND); - pr_debug("Device status: %d\n", status); - - ata_wait(dev, false); - - // Read the ATA device identity. - ata_read_device_identity(dev); - - pr_debug("Device Serial : %s\n", dev->identity.serial); - pr_debug("Device Firmware : %s\n", dev->identity.firmware); - pr_debug("Device Model : %s\n", dev->identity.model); - pr_debug("Sectors (48): %d\n", (uint32_t)dev->identity.sectors_48); - pr_debug("Sectors (24): %d\n", dev->identity.sectors_28); - - pr_debug("Setting up DMA...\n"); - dev->dma_prdt = (prdt_t *)malloc_dma(sizeof(prdt_t), &dev->dma_prdt_phys); - dev->dma_start = (uint8_t *)malloc_dma(4096, &dev->dma_start_phys); - pr_debug("Device PRDT : 0x%x (Ph: 0x%x)\n", dev->dma_prdt, dev->dma_prdt_phys); - pr_debug("Device START : 0x%x (Ph: 0x%x)\n", dev->dma_start, dev->dma_start_phys); - - dev->dma_prdt[0].offset = dev->dma_start_phys; - dev->dma_prdt[0].bytes = ATA_DMA_SIZE; - dev->dma_prdt[0].last = 0x8000; - - // Set the length - dev->fs_root->length = ata_max_offset(dev); - - pr_debug("ATA PCI device ID: 0x%x\n", ata_pci); - - uint16_t command_reg = pci_read_field(ata_pci, PCI_COMMAND, 4); - pr_debug("COMMAND register before: %s\n", dec_to_binary(command_reg)); - if (command_reg & (1U << 2U)) { - pr_debug("Bus mastering already enabled.\n"); - } else { - // bit 2. - command_reg |= (1U << 2U); - pr_debug("Enabling bus mastering...\n"); - pci_write_field(ata_pci, PCI_COMMAND, 4, command_reg); - command_reg = pci_read_field(ata_pci, PCI_COMMAND, 4); - pr_debug("COMMAND register after : %s\n", dec_to_binary(command_reg)); - } - - dev->bar4 = pci_read_field(ata_pci, PCI_BASE_ADDRESS_4, 4); - pr_debug("BAR4: 0x%x\n", dev->bar4); - - if (dev->bar4 & 0x00000001U) { - dev->bar4 = dev->bar4 & 0xFFFFFFFC; - } else { - pr_debug("? ATA bus master registers are 'usually' I/O ports.\n"); - // No DMA because we're not sure what to do here- - return 1; - } - - dev->bmr.command = dev->bar4; - dev->bmr.status = dev->bar4 + 2; - dev->bmr.prdt = dev->bar4 + 4; - - pci_write_field(ata_pci, PCI_INTERRUPT_LINE, 1, 0xFE); - if (pci_read_field(ata_pci, PCI_INTERRUPT_LINE, 1) == 0xFE) { - // Needs assignment. - pci_write_field(ata_pci, PCI_INTERRUPT_LINE, 1, 14); - } - return 0; -} - -static int atapi_device_init(ata_device_t *dev) -{ - pr_debug("Detected ATAPI device at io-base 0x%3x, ctrl 0x%3x, slave %d\n", - dev->io_base, dev->control, dev->slave); - pr_debug("Device name: %s\n", dev->name); - ata_device_select(dev); - outportb(dev->io_base + ATA_REG_COMMAND, ATAPI_CMD_ID_PCKT); - ata_io_wait(dev); - uint8_t status = inportb(dev->io_base + ATA_REG_COMMAND); - pr_debug("Device status: %d\n", status); - - ata_wait(dev, false); - - uint16_t *buf = (uint16_t *)&dev->identity; - - for (int i = 0; i < 256; ++i) { - buf[i] = inports(dev->io_base); - } - - uint8_t *ptr = (uint8_t *)&dev->identity.model; - for (int i = 0; i < 39; i += 2) { - char tmp = ptr[i + 1]; - ptr[i + 1] = ptr[i]; - ptr[i] = tmp; - } - ptr[39] = 0; - - pr_debug("Device Model: %s\n", dev->identity.model); - - // Detect medium. - atapi_command_t command; - command.command_bytes[0] = 0x25; - command.command_bytes[1] = 0; - command.command_bytes[2] = 0; - command.command_bytes[3] = 0; - command.command_bytes[4] = 0; - command.command_bytes[5] = 0; - command.command_bytes[6] = 0; - command.command_bytes[7] = 0; - // Bit 0 = PMI (0, last sector). - command.command_bytes[8] = 0; - // Control. - command.command_bytes[9] = 0; - command.command_bytes[10] = 0; - command.command_bytes[11] = 0; - - outportb(dev->io_base + ATA_REG_FEATURES, 0x00); - outportb(dev->io_base + ATA_REG_LBA1, 0x08); - outportb(dev->io_base + ATA_REG_LBA2, 0x08); - outportb(dev->io_base + ATA_REG_COMMAND, ATAPI_CMD_PACKET); - - // Poll. - while (1) { - status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { - break; - } - } - - for (int i = 0; i < 6; ++i) - outports(dev->io_base, command.command_words[i]); - - // Poll. - while (1) { - status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error_read; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { - break; - } - if ((status & ATA_STAT_DRQ)) { - break; - } - } - - uint16_t data[4]; - - for (int i = 0; i < 4; ++i) { - data[i] = inports(dev->io_base); - } - -#define htonl(l) \ - ((((l)&0xFF) << 24) | (((l)&0xFF00) << 8) | (((l)&0xFF0000) >> 8) | \ - (((l)&0xFF000000) >> 24)) - uint32_t lba, blocks; - - memcpy(&lba, &data[0], sizeof(uint32_t)); - - lba = htonl(lba); - - memcpy(&blocks, &data[2], sizeof(uint32_t)); - - blocks = htonl(blocks); - - dev->atapi_lba = lba; - dev->atapi_sector_size = blocks; - - if (!lba) { - return false; - } - - pr_debug("Finished! LBA = %x; block length = %x\n", lba, blocks); - return true; - -atapi_error_read: - pr_debug("ATAPI error; no medium?\n"); - return false; - -atapi_error: - pr_debug("ATAPI early error; unsure\n"); - return false; -} - -static void ata_soft_reset(ata_device_t *dev) -{ - outportb(dev->control, ATA_CONTROL_SOFTWARE_RESET); - ata_io_wait(dev); - outportb(dev->control, ATA_CONTROL_ZERO); -} - static int ata_device_detect(ata_device_t *dev) { - ata_soft_reset(dev); - ata_io_wait(dev); - outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); - ata_io_wait(dev); - ata_status_wait(dev, 10000); - - pr_debug("Probing cylinder registers...\n"); - uint8_t cl = inportb(dev->io_base + ATA_REG_LBA1); - uint8_t ch = inportb(dev->io_base + ATA_REG_LBA2); - if ((cl == 0xFF) && (ch == 0xFF)) { + // Detect the device type. + ata_device_type_t type = ata_detect_device_type(dev); + // Exit if there is no drive. + if (type == ata_dev_type_no_device) { pr_debug("No drive(s) present\n"); return 1; } - - pr_debug("Waiting while busy...\n"); - uint8_t status = ata_status_wait(dev, 5000); - if (status & ATA_STAT_BUSY) { - pr_debug("No drive(s) present\n"); - return 1; - } - - pr_debug("Device detected: 0x%2x 0x%2x\n", cl, ch); - if ((cl == 0x00 && ch == 0x00) || (cl == 0x3C && ch == 0xC3)) { - // The device is not an ATAPI. - dev->is_atapi = false; - // Parallel ATA device, or emulated SATA + // Parallel ATA drive, or emulated SATA. + if ((type == ata_dev_type_ata) || (type == ata_dev_type_sata)) { + // Device type supported, set it. + dev->type = type; + // Set the device name. sprintf(dev->name, "hd%c", ata_drive_char); - + // Set the device path. + sprintf(dev->path, "/dev/hd%c", ata_drive_char); + // Create the filesystem entry for the drive. dev->fs_root = ata_device_create(dev); + // Check if we failed to create the filesystem entry. if (!dev->fs_root) { pr_crit("Failed to create ata device!\n"); return 1; } + // Try to mount the drive. if (!vfs_mount(dev->fs_root->name, dev->fs_root)) { pr_crit("Failed to mount ata device!\n"); return 1; } + // Initialize the drive. if (ata_device_init(dev)) { pr_crit("Failed to initialize ata device!\n"); return 1; } + // Increment the drive letter. ++ata_drive_char; - } else if ((cl == 0x14 && ch == 0xEB) || (cl == 0x69 && ch == 0x96)) { -#if 0 - // The device is an ATAPI. - dev->is_atapi = true; - sprintf(dev->name, "cdrom%d", cdrom_number); - - dev->fs_root = atapi_device_create(dev); - if (!dev->fs_root) { - pr_crit("Failed to create atapi device!\n"); - return 1; - } - if (!vfs_mount(dev->fs_root->name, dev->fs_root)) { - pr_crit("Failed to mount atapi device!\n"); - return 1; - } - if (atapi_device_init(dev)) { - pr_crit("Failed to initialize atapi device!\n"); - return 1; - } - ++cdrom_number; -#endif + } else { + pr_alert("Unsupported drive type.\n"); } - pr_debug("\n"); return 0; } -// == ATA SECTOR READ/WRITE FUNCTIONS ========================================= -static void ata_device_read_sector(ata_device_t *dev, uint32_t lba, uint8_t *buffer) -{ - pr_debug("ata_device_read_sector(%p, %d, %p)\n", dev, lba, buffer); - - if (dev->is_atapi) { - return; - } - spinlock_lock(&ata_lock); - - ata_wait(dev, false); - - // Stop. - outportb(dev->bmr.command, 0x00); - // Set the PRDT. - outportl(dev->bmr.prdt, dev->dma_prdt_phys); - // Enable error, irq status. - outportb(dev->bmr.status, inportb(dev->bmr.status) | 0x04 | 0x02); - // Set read. - outportb(dev->bmr.command, 0x08); - - do { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if (!(status & ATA_STAT_BUSY)) { - break; - } - } while (true); - - outportb(dev->io_base + ATA_REG_CONTROL, 0x00); - outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xe0 | (dev->slave << 4)); - ata_io_wait(dev); - outportb(dev->io_base + ATA_REG_FEATURES, 0x00); - - outportb(dev->io_base + ATA_REG_SECCOUNT0, 0); - outportb(dev->io_base + ATA_REG_LBA0, (lba & 0xff000000) >> 24); - outportb(dev->io_base + ATA_REG_LBA1, (lba & 0xff00000000) >> 32); - outportb(dev->io_base + ATA_REG_LBA2, (lba & 0xff0000000000) >> 40); - - outportb(dev->io_base + ATA_REG_SECCOUNT0, 1); - outportb(dev->io_base + ATA_REG_LBA0, (lba & 0x000000ff) >> 0); - outportb(dev->io_base + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8); - outportb(dev->io_base + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16); - - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) - break; - } - outportb(dev->io_base + ATA_REG_COMMAND, ATA_CMD_RD_DMA); - - ata_io_wait(dev); - - outportb(dev->bmr.command, 0x08 | 0x01); - - while (1) { - int status = inportb(dev->bmr.status); - int dstatus = inportb(dev->io_base + ATA_REG_STATUS); - if (!(status & 0x04)) { - continue; - } - if (!(dstatus & ATA_STAT_BUSY)) { - break; - } - } - - // Copy from DMA buffer to output buffer. - memcpy(buffer, dev->dma_start, ATA_DMA_SIZE); - - // Inform device we are done. - outportb(dev->bmr.status, inportb(dev->bmr.status) | 0x04 | 0x02); - - spinlock_unlock(&ata_lock); -} - -static void ata_device_read_sector_atapi(ata_device_t *dev, uint32_t lba, uint8_t *buffer) -{ - if (!dev->is_atapi) { - return; - } - - spinlock_lock(&ata_lock); - - outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); - ata_io_wait(dev); - - outportb(dev->io_base + ATA_REG_FEATURES, 0x00); - outportb(dev->io_base + ATA_REG_LBA1, dev->atapi_sector_size & 0xFF); - outportb(dev->io_base + ATA_REG_LBA2, dev->atapi_sector_size >> 8); - outportb(dev->io_base + ATA_REG_COMMAND, ATAPI_CMD_PACKET); - - // Poll. - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error_on_read_setup; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_DRQ)) { - break; - } - } - - atapi_in_progress = true; - - atapi_command_t command; - command.command_bytes[0] = 0xA8; - command.command_bytes[1] = 0; - command.command_bytes[2] = (lba >> 0x18) & 0xFF; - command.command_bytes[3] = (lba >> 0x10) & 0xFF; - command.command_bytes[4] = (lba >> 0x08) & 0xFF; - command.command_bytes[5] = (lba >> 0x00) & 0xFF; - command.command_bytes[6] = 0; - command.command_bytes[7] = 0; - // Bit 0 = PMI (0, last sector). - command.command_bytes[8] = 0; - // Control. - command.command_bytes[9] = 1; - command.command_bytes[10] = 0; - command.command_bytes[11] = 0; - - for (int i = 0; i < 6; ++i) { - outports(dev->io_base, command.command_words[i]); - } - - // Wait. - sleep_on(&ata_wait_queue); - - atapi_in_progress = false; - - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error_on_read_setup; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_DRQ)) { - break; - } - } - - uint16_t size_to_read = inportb(dev->io_base + ATA_REG_LBA2) << 8; - size_to_read = size_to_read | inportb(dev->io_base + ATA_REG_LBA1); - - inportsm(dev->io_base, buffer, size_to_read / 2); - - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error_on_read_setup; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { - break; - } - } - -atapi_error_on_read_setup:; - spinlock_unlock(&ata_lock); -} - -static void ata_device_write_sector(ata_device_t *dev, uint32_t lba, uint8_t *buffer) -{ - spinlock_lock(&ata_lock); - - outportb(dev->io_base + ATA_REG_CONTROL, 0x02); - - ata_wait(dev, false); - outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xe0 | (dev->slave << 4) | (lba & 0x0f000000) >> 24); - ata_wait(dev, false); - - outportb(dev->io_base + ATA_REG_FEATURES, 0x00); - outportb(dev->io_base + ATA_REG_SECCOUNT0, 0x01); - outportb(dev->io_base + ATA_REG_LBA0, (lba & 0x000000ff) >> 0); - outportb(dev->io_base + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8); - outportb(dev->io_base + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16); - outportb(dev->io_base + ATA_REG_COMMAND, ATA_CMD_WRITE); - ata_wait(dev, false); - int size = ATA_SECTOR_SIZE / 2; - outportsm(dev->io_base, buffer, size); - outportb(dev->io_base + 0x07, ATA_CMD_CH_FLSH); - ata_wait(dev, false); - spinlock_unlock(&ata_lock); -} - -static void ata_device_write_sector_retry(ata_device_t *dev, uint32_t lba, uint8_t *buffer) -{ - uint8_t *read_buf = kmalloc(ATA_SECTOR_SIZE); - do { - ata_device_write_sector(dev, lba, buffer); - ata_device_read_sector(dev, lba, read_buf); - } while (buffer_compare((uint32_t *)buffer, (uint32_t *)read_buf, ATA_SECTOR_SIZE)); - kfree(read_buf); -} - // == IRQ HANDLERS ============================================================ /// @param f The interrupt stack frame. static void ata_irq_handler_master(pt_regs *f) { - inportb(ata_primary_master.io_base + ATA_REG_STATUS); - if (atapi_in_progress) { - list_head *it_save, *it; - list_for_each (it, &ata_wait_queue.task_list) { - wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); - it_save = it->prev; - list_head_del(it); - it = it_save; - kfree(entry); - } - } + pr_warning("ata_irq_handler_master\n"); + inportb(ata_primary_master.io_reg.status); + inportb(ata_primary_master.bmr.status); + //outportb(ata_primary_master.bmr.command, ata_bm_stop_bus_master); pic8259_send_eoi(IRQ_FIRST_HD); } /// @param f The interrupt stack frame. static void ata_irq_handler_slave(pt_regs *f) { - inportb(ata_secondary_master.io_base + ATA_REG_STATUS); - if (atapi_in_progress) { - list_head *it_save, *it; - list_for_each (it, &ata_wait_queue.task_list) { - wait_queue_entry_t *entry = list_entry(it, wait_queue_entry_t, task_list); - it_save = it->prev; - list_head_del(it); - it = it_save; - kfree(entry); - } - } + pr_warning("ata_irq_handler_slave\n"); + inportb(ata_secondary_master.io_reg.status); + inportb(ata_primary_master.bmr.status); + //outportb(ata_primary_master.bmr.command, ata_bm_stop_bus_master); pic8259_send_eoi(IRQ_SECOND_HD); } @@ -1183,45 +1106,35 @@ static void pci_find_ata(uint32_t dev, uint16_t vid, uint16_t did, void *extra) // == INITIALIZE/FINALIZE ATA ================================================= int ata_initialize() { + // Initialize the spinlock. spinlock_init(&ata_lock); - + // Initialize the list of tasks for the wait queue. list_head_init(&ata_wait_queue.task_list); + // Initialize the spinlock for the wait queue. spinlock_init(&ata_wait_queue.lock); + // Search for ATA devices. pci_scan(&pci_find_ata, -1, &ata_pci); + // Install the IRQ handlers. irq_install_handler(IRQ_FIRST_HD, ata_irq_handler_master, "IDE Master"); irq_install_handler(IRQ_SECOND_HD, ata_irq_handler_slave, "IDE Slave"); - pr_debug("Detecteing devices...\n"); - pr_debug("Detecteing Primary Master...\n"); + pr_debug("Detecting devices...\n"); + pr_debug("Detecting Primary Master...\n"); ata_device_detect(&ata_primary_master); pr_debug("\n"); - pr_debug("Detecteing Primary Slave...\n"); + pr_debug("Detecting Primary Slave...\n"); ata_device_detect(&ata_primary_slave); pr_debug("\n"); - pr_debug("Detecteing Secondary Master...\n"); + pr_debug("Detecting Secondary Master...\n"); ata_device_detect(&ata_secondary_master); pr_debug("\n"); - pr_debug("Detecteing Secondary Slave...\n"); + pr_debug("Detecting Secondary Slave...\n"); ata_device_detect(&ata_secondary_slave); pr_debug("\n"); pr_debug("Done\n"); - const int size_to_read = (100 * K); - char buffer[size_to_read]; - ata_read(ata_primary_master.fs_root, buffer, 0, size_to_read); - ata_write(ata_primary_master.fs_root, buffer, 0, size_to_read); - /* - dbg_putchar('\n'); - dbg_putchar('\n'); - for (unsigned i = 0; i < size_to_read; ++i) { - pr_debug("%d ", buffer[i]); - } - dbg_putchar('\n'); - */ - while (true) {} - return 0; } diff --git a/mentos/src/kernel.c b/mentos/src/kernel.c index b244516..96cc79b 100644 --- a/mentos/src/kernel.c +++ b/mentos/src/kernel.c @@ -261,7 +261,7 @@ int kmain(boot_info_t *boot_informations) //========================================================================== // Scan for ata devices. - if (!ata_initialize()) { + if (ata_initialize()) { print_fail(); return 1; }