2020-01-18 09:38:21 +01:00
/*
2021-03-12 12:15:46 +01:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < kling @ serenityos . org >
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2019-07-28 23:44:01 +10:00
# include <AK/ByteBuffer.h>
2020-08-24 19:35:19 -06:00
# include <AK/Singleton.h>
2020-03-23 13:45:10 +01:00
# include <AK/StringView.h>
2020-06-24 14:07:28 -06:00
# include <Kernel/IO.h>
2019-06-07 11:43:58 +02:00
# include <Kernel/Process.h>
2021-02-20 08:27:50 +02:00
# include <Kernel/Storage/ATA.h>
2020-12-19 12:50:57 +02:00
# include <Kernel/Storage/IDEChannel.h>
# include <Kernel/Storage/IDEController.h>
# include <Kernel/Storage/PATADiskDevice.h>
2019-06-07 11:43:58 +02:00
# include <Kernel/VM/MemoryManager.h>
2021-02-05 23:36:38 -07:00
# include <Kernel/WorkQueue.h>
2018-11-10 15:15:31 +01:00
2020-02-16 01:27:42 +01:00
namespace Kernel {
2019-07-28 23:44:01 +10:00
# define PATA_PRIMARY_IRQ 14
# define PATA_SECONDARY_IRQ 15
2018-11-10 15:15:31 +01:00
2021-03-27 09:01:00 +03:00
UNMAP_AFTER_INIT NonnullRefPtr < IDEChannel > IDEChannel : : create ( const IDEController & controller , IOAddressGroup io_group , ChannelType type )
2018-10-16 14:17:43 +02:00
{
2021-04-23 16:46:57 +02:00
return adopt_ref ( * new IDEChannel ( controller , io_group , type ) ) ;
2018-10-16 14:17:43 +02:00
}
2021-03-27 21:44:25 +03:00
UNMAP_AFTER_INIT NonnullRefPtr < IDEChannel > IDEChannel : : create ( const IDEController & controller , u8 irq , IOAddressGroup io_group , ChannelType type )
{
2021-04-23 16:46:57 +02:00
return adopt_ref ( * new IDEChannel ( controller , irq , io_group , type ) ) ;
2021-03-27 21:44:25 +03:00
}
2020-12-19 13:56:12 +02:00
RefPtr < StorageDevice > IDEChannel : : master_device ( ) const
2020-12-19 12:50:57 +02:00
{
return m_master ;
}
2021-01-29 19:37:40 +00:00
2020-12-19 13:56:12 +02:00
RefPtr < StorageDevice > IDEChannel : : slave_device ( ) const
2020-12-19 12:50:57 +02:00
{
return m_slave ;
}
2021-03-27 21:44:25 +03:00
UNMAP_AFTER_INIT void IDEChannel : : initialize ( )
2018-10-16 14:17:43 +02:00
{
2020-01-22 22:23:50 +01:00
disable_irq ( ) ;
2021-03-27 09:01:00 +03:00
dbgln_if ( PATA_DEBUG , " IDEChannel: {} IO base: {} " , channel_type_string ( ) , m_io_group . io_base ( ) ) ;
dbgln_if ( PATA_DEBUG , " IDEChannel: {} control base: {} " , channel_type_string ( ) , m_io_group . control_base ( ) ) ;
2021-03-27 09:22:25 +03:00
if ( m_io_group . bus_master_base ( ) . has_value ( ) )
dbgln_if ( PATA_DEBUG , " IDEChannel: {} bus master base: {} " , channel_type_string ( ) , m_io_group . bus_master_base ( ) . value ( ) ) ;
else
dbgln_if ( PATA_DEBUG , " IDEChannel: {} bus master base disabled " , channel_type_string ( ) ) ;
2021-03-27 09:01:00 +03:00
m_parent_controller - > enable_pin_based_interrupts ( ) ;
2021-04-16 18:03:11 +03:00
// reset the channel
u8 device_control = m_io_group . control_base ( ) . in < u8 > ( ) ;
// Wait 30 milliseconds
IO : : delay ( 30000 ) ;
m_io_group . control_base ( ) . out < u8 > ( device_control | ( 1 < < 2 ) ) ;
// Wait 30 milliseconds
IO : : delay ( 30000 ) ;
m_io_group . control_base ( ) . out < u8 > ( device_control ) ;
// Wait up to 30 seconds before failing
if ( ! wait_until_not_busy ( false , 30000 ) ) {
dbgln ( " IDEChannel: reset failed, busy flag on master stuck " ) ;
return ;
}
// Wait up to 30 seconds before failing
if ( ! wait_until_not_busy ( true , 30000 ) ) {
dbgln ( " IDEChannel: reset failed, busy flag on slave stuck " ) ;
return ;
}
2019-07-28 23:44:01 +10:00
detect_disks ( ) ;
2021-02-05 06:51:47 +02:00
// Note: calling to detect_disks could generate an interrupt, clear it if that's the case
clear_pending_interrupts ( ) ;
2018-10-16 14:17:43 +02:00
}
2021-03-27 21:44:25 +03:00
UNMAP_AFTER_INIT IDEChannel : : IDEChannel ( const IDEController & controller , u8 irq , IOAddressGroup io_group , ChannelType type )
: IRQHandler ( irq )
, m_channel_type ( type )
, m_io_group ( io_group )
, m_parent_controller ( controller )
{
initialize ( ) ;
}
UNMAP_AFTER_INIT IDEChannel : : IDEChannel ( const IDEController & controller , IOAddressGroup io_group , ChannelType type )
: IRQHandler ( type = = ChannelType : : Primary ? PATA_PRIMARY_IRQ : PATA_SECONDARY_IRQ )
, m_channel_type ( type )
, m_io_group ( io_group )
, m_parent_controller ( controller )
{
initialize ( ) ;
}
2021-02-05 06:51:47 +02:00
void IDEChannel : : clear_pending_interrupts ( ) const
{
m_io_group . io_base ( ) . offset ( ATA_REG_STATUS ) . in < u8 > ( ) ;
}
2021-02-20 00:00:19 +01:00
UNMAP_AFTER_INIT IDEChannel : : ~ IDEChannel ( )
2019-05-19 04:40:30 +02:00
{
}
2021-03-27 09:01:00 +03:00
void IDEChannel : : start_request ( AsyncBlockDeviceRequest & request , bool is_slave , u16 capabilities )
2020-03-06 03:22:53 +02:00
{
2021-04-24 15:27:32 -07:00
Locker locker ( m_lock ) ;
2021-03-27 09:53:59 +03:00
VERIFY ( m_current_request . is_null ( ) ) ;
2021-01-29 19:37:40 +00:00
2021-02-07 15:33:24 +03:30
dbgln_if ( PATA_DEBUG , " IDEChannel::start_request " ) ;
2021-01-29 19:37:40 +00:00
2021-03-27 09:53:59 +03:00
m_current_request = request ;
2020-11-02 11:16:01 -07:00
m_current_request_block_index = 0 ;
m_current_request_flushing_cache = false ;
2021-03-27 09:01:00 +03:00
if ( request . request_type ( ) = = AsyncBlockDeviceRequest : : Read )
ata_read_sectors ( is_slave , capabilities ) ;
else
ata_write_sectors ( is_slave , capabilities ) ;
2020-11-02 11:16:01 -07:00
}
2020-12-19 12:50:57 +02:00
void IDEChannel : : complete_current_request ( AsyncDeviceRequest : : RequestResult result )
2020-11-02 11:16:01 -07:00
{
// NOTE: this may be called from the interrupt handler!
2021-02-23 20:42:32 +01:00
VERIFY ( m_current_request ) ;
VERIFY ( m_request_lock . is_locked ( ) ) ;
2020-11-02 11:16:01 -07:00
// Now schedule reading back the buffer as soon as we leave the irq handler.
// This is important so that we can safely write the buffer back,
// which could cause page faults. Note that this may be called immediately
// before Processor::deferred_call_queue returns!
2021-02-05 23:36:38 -07:00
g_io_work - > queue ( [ this , result ] ( ) {
2021-02-07 15:33:24 +03:30
dbgln_if ( PATA_DEBUG , " IDEChannel::complete_current_request result: {} " , ( int ) result ) ;
2021-04-24 15:27:32 -07:00
Locker locker ( m_lock ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( m_current_request ) ;
2021-03-27 09:53:59 +03:00
auto current_request = m_current_request ;
m_current_request . clear ( ) ;
current_request - > complete ( result ) ;
2020-11-02 11:16:01 -07:00
} ) ;
2020-03-06 03:22:53 +02:00
}
2019-07-03 21:17:35 +02:00
static void print_ide_status ( u8 status )
2018-11-10 15:15:31 +01:00
{
2021-03-12 12:15:46 +01:00
dbgln ( " IDEChannel: print_ide_status: DRQ={} BSY={}, DRDY={}, DSC={}, DF={}, CORR={}, IDX={}, ERR={} " ,
( status & ATA_SR_DRQ ) ! = 0 ,
( status & ATA_SR_BSY ) ! = 0 ,
( status & ATA_SR_DRDY ) ! = 0 ,
( status & ATA_SR_DSC ) ! = 0 ,
( status & ATA_SR_DF ) ! = 0 ,
( status & ATA_SR_CORR ) ! = 0 ,
( status & ATA_SR_IDX ) ! = 0 ,
( status & ATA_SR_ERR ) ! = 0 ) ;
2018-11-10 15:15:31 +01:00
}
2021-01-29 19:37:40 +00:00
void IDEChannel : : try_disambiguate_error ( )
{
2021-03-27 09:53:59 +03:00
VERIFY ( m_lock . is_locked ( ) ) ;
2021-01-29 19:37:40 +00:00
dbgln ( " IDEChannel: Error cause: " ) ;
switch ( m_device_error ) {
case ATA_ER_BBK :
dbgln ( " IDEChannel: - Bad block " ) ;
break ;
case ATA_ER_UNC :
dbgln ( " IDEChannel: - Uncorrectable data " ) ;
break ;
case ATA_ER_MC :
dbgln ( " IDEChannel: - Media changed " ) ;
break ;
case ATA_ER_IDNF :
dbgln ( " IDEChannel: - ID mark not found " ) ;
break ;
case ATA_ER_MCR :
dbgln ( " IDEChannel: - Media change request " ) ;
break ;
case ATA_ER_ABRT :
dbgln ( " IDEChannel: - Command aborted " ) ;
break ;
case ATA_ER_TK0NF :
dbgln ( " IDEChannel: - Track 0 not found " ) ;
break ;
case ATA_ER_AMNF :
dbgln ( " IDEChannel: - No address mark " ) ;
break ;
default :
dbgln ( " IDEChannel: - No one knows " ) ;
break ;
}
}
2020-12-19 12:50:57 +02:00
void IDEChannel : : handle_irq ( const RegisterState & )
2018-11-10 15:15:31 +01:00
{
2020-12-19 12:50:57 +02:00
u8 status = m_io_group . io_base ( ) . offset ( ATA_REG_STATUS ) . in < u8 > ( ) ;
2020-06-24 14:07:28 -06:00
m_entropy_source . add_random_event ( status ) ;
2020-11-02 11:16:01 -07:00
ScopedSpinLock lock ( m_request_lock ) ;
2021-03-12 12:15:46 +01:00
dbgln_if ( PATA_DEBUG , " IDEChannel: interrupt: DRQ={}, BSY={}, DRDY={} " ,
( status & ATA_SR_DRQ ) ! = 0 ,
( status & ATA_SR_BSY ) ! = 0 ,
( status & ATA_SR_DRDY ) ! = 0 ) ;
2020-11-02 11:16:01 -07:00
if ( ! m_current_request ) {
2021-01-09 18:51:44 +01:00
dbgln ( " IDEChannel: IRQ but no pending request! " ) ;
2020-11-02 11:16:01 -07:00
return ;
}
2019-05-24 04:17:15 +02:00
if ( status & ATA_SR_ERR ) {
2019-02-11 11:38:14 +01:00
print_ide_status ( status ) ;
2020-12-19 12:50:57 +02:00
m_device_error = m_io_group . io_base ( ) . offset ( ATA_REG_ERROR ) . in < u8 > ( ) ;
2020-12-25 16:45:35 +01:00
dbgln ( " IDEChannel: Error {:#02x}! " , ( u8 ) m_device_error ) ;
2021-01-29 19:37:40 +00:00
try_disambiguate_error ( ) ;
2020-11-02 11:16:01 -07:00
complete_current_request ( AsyncDeviceRequest : : Failure ) ;
return ;
}
m_device_error = 0 ;
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
// Now schedule reading/writing the buffer as soon as we leave the irq handler.
// This is important so that we can safely access the buffers, which could
// trigger page faults
2021-03-27 09:53:59 +03:00
g_io_work - > queue ( [ this ] ( ) {
2021-04-24 15:27:32 -07:00
Locker locker ( m_lock ) ;
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
ScopedSpinLock lock ( m_request_lock ) ;
if ( m_current_request - > request_type ( ) = = AsyncBlockDeviceRequest : : Read ) {
2021-02-07 15:33:24 +03:30
dbgln_if ( PATA_DEBUG , " IDEChannel: Read block {}/{} " , m_current_request_block_index , m_current_request - > block_count ( ) ) ;
2021-03-27 09:53:59 +03:00
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
if ( ata_do_read_sector ( ) ) {
if ( + + m_current_request_block_index > = m_current_request - > block_count ( ) ) {
complete_current_request ( AsyncDeviceRequest : : Success ) ;
return ;
2020-11-02 11:16:01 -07:00
}
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
// Wait for the next block
enable_irq ( ) ;
}
} else {
if ( ! m_current_request_flushing_cache ) {
2021-02-07 15:33:24 +03:30
dbgln_if ( PATA_DEBUG , " IDEChannel: Wrote block {}/{} " , m_current_request_block_index , m_current_request - > block_count ( ) ) ;
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
if ( + + m_current_request_block_index > = m_current_request - > block_count ( ) ) {
// We read the last block, flush cache
2021-02-23 20:42:32 +01:00
VERIFY ( ! m_current_request_flushing_cache ) ;
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
m_current_request_flushing_cache = true ;
m_io_group . io_base ( ) . offset ( ATA_REG_COMMAND ) . out < u8 > ( ATA_CMD_CACHE_FLUSH ) ;
2020-11-02 11:16:01 -07:00
} else {
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
// Read next block
ata_do_write_sector ( ) ;
2020-11-02 11:16:01 -07:00
}
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
} else {
complete_current_request ( AsyncDeviceRequest : : Success ) ;
2020-11-02 11:16:01 -07:00
}
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
}
} ) ;
2018-11-10 15:15:31 +01:00
}
2019-09-04 11:10:07 +02:00
static void io_delay ( )
2018-11-10 15:15:31 +01:00
{
2019-07-28 23:44:01 +10:00
for ( int i = 0 ; i < 4 ; + + i )
2019-09-04 11:10:07 +02:00
IO : : in8 ( 0x3f6 ) ;
2019-07-28 23:44:01 +10:00
}
2018-11-10 15:15:31 +01:00
2021-04-16 18:03:11 +03:00
bool IDEChannel : : wait_until_not_busy ( bool slave , size_t milliseconds_timeout )
2021-01-29 19:37:40 +00:00
{
2021-04-16 18:03:11 +03:00
IO : : delay ( 20 ) ;
m_io_group . io_base ( ) . offset ( ATA_REG_HDDEVSEL ) . out < u8 > ( 0xA0 | ( slave < < 4 ) ) ; // First, we need to select the drive itself
IO : : delay ( 20 ) ;
size_t time_elapsed = 0 ;
while ( m_io_group . control_base ( ) . in < u8 > ( ) & ATA_SR_BSY & & time_elapsed < = milliseconds_timeout ) {
IO : : delay ( 1000 ) ;
time_elapsed + + ;
}
2021-05-13 19:11:14 +02:00
return time_elapsed < = milliseconds_timeout ;
2021-04-16 18:03:11 +03:00
}
bool IDEChannel : : wait_until_not_busy ( size_t milliseconds_timeout )
{
size_t time_elapsed = 0 ;
while ( m_io_group . control_base ( ) . in < u8 > ( ) & ATA_SR_BSY & & time_elapsed < = milliseconds_timeout ) {
IO : : delay ( 1000 ) ;
time_elapsed + + ;
}
2021-05-13 19:11:14 +02:00
return time_elapsed < = milliseconds_timeout ;
2021-01-29 19:37:40 +00:00
}
String IDEChannel : : channel_type_string ( ) const
{
if ( m_channel_type = = ChannelType : : Primary )
return " Primary " ;
return " Secondary " ;
}
2021-02-20 00:00:19 +01:00
UNMAP_AFTER_INIT void IDEChannel : : detect_disks ( )
2019-07-28 23:44:01 +10:00
{
2021-01-29 19:37:40 +00:00
auto channel_string = [ ] ( u8 i ) - > const char * {
if ( i = = 0 )
return " master " ;
return " slave " ;
} ;
2019-07-28 23:44:01 +10:00
// There are only two possible disks connected to a channel
for ( auto i = 0 ; i < 2 ; i + + ) {
2021-04-05 21:24:05 +03:00
// We need to select the drive and then we wait 20 microseconds... and it doesn't hurt anything so let's just do it.
2021-04-16 18:03:11 +03:00
IO : : delay ( 20 ) ;
2020-12-19 12:50:57 +02:00
m_io_group . io_base ( ) . offset ( ATA_REG_HDDEVSEL ) . out < u8 > ( 0xA0 | ( i < < 4 ) ) ; // First, we need to select the drive itself
2021-04-05 21:24:05 +03:00
IO : : delay ( 20 ) ;
2018-11-10 15:15:31 +01:00
2021-04-16 18:03:11 +03:00
auto status = m_io_group . control_base ( ) . in < u8 > ( ) ;
if ( status = = 0x0 ) {
dbgln_if ( PATA_DEBUG , " IDEChannel: No {} {} disk detected! " , channel_type_string ( ) . to_lowercase ( ) , channel_string ( i ) ) ;
continue ;
}
2021-04-05 21:24:05 +03:00
m_io_group . io_base ( ) . offset ( ATA_REG_SECCOUNT0 ) . out < u8 > ( 0 ) ;
m_io_group . io_base ( ) . offset ( ATA_REG_LBA0 ) . out < u8 > ( 0 ) ;
m_io_group . io_base ( ) . offset ( ATA_REG_LBA1 ) . out < u8 > ( 0 ) ;
m_io_group . io_base ( ) . offset ( ATA_REG_LBA2 ) . out < u8 > ( 0 ) ;
2020-12-19 12:50:57 +02:00
m_io_group . io_base ( ) . offset ( ATA_REG_COMMAND ) . out < u8 > ( ATA_CMD_IDENTIFY ) ; // Send the ATA_IDENTIFY command
2018-11-10 15:15:31 +01:00
2021-04-16 18:03:11 +03:00
// Wait 10 second for the BSY flag to clear
if ( ! wait_until_not_busy ( 2000 ) ) {
dbgln_if ( PATA_DEBUG , " IDEChannel: No {} {} disk detected, BSY flag was not reset! " , channel_type_string ( ) . to_lowercase ( ) , channel_string ( i ) ) ;
2019-07-28 23:44:01 +10:00
continue ;
}
2018-11-10 15:15:31 +01:00
2021-01-29 19:37:40 +00:00
bool check_for_atapi = false ;
2021-04-16 18:03:11 +03:00
bool device_presence = true ;
2021-01-29 19:37:40 +00:00
PATADiskDevice : : InterfaceType interface_type = PATADiskDevice : : InterfaceType : : ATA ;
2021-04-16 18:03:11 +03:00
size_t milliseconds_elapsed = 0 ;
2021-01-29 19:37:40 +00:00
for ( ; ; ) {
2021-04-16 18:03:11 +03:00
// Wait about 10 seconds
if ( milliseconds_elapsed > 2000 )
break ;
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
u8 status = m_io_group . control_base ( ) . in < u8 > ( ) ;
2021-01-29 19:37:40 +00:00
if ( status & ATA_SR_ERR ) {
2021-02-07 15:33:24 +03:30
dbgln_if ( PATA_DEBUG , " IDEChannel: {} {} device is not ATA. Will check for ATAPI. " , channel_type_string ( ) , channel_string ( i ) ) ;
2021-01-29 19:37:40 +00:00
check_for_atapi = true ;
break ;
}
if ( ! ( status & ATA_SR_BSY ) & & ( status & ATA_SR_DRQ ) ) {
2021-02-07 15:33:24 +03:30
dbgln_if ( PATA_DEBUG , " IDEChannel: {} {} device appears to be ATA. " , channel_type_string ( ) , channel_string ( i ) ) ;
2021-01-29 19:37:40 +00:00
interface_type = PATADiskDevice : : InterfaceType : : ATA ;
break ;
}
2021-04-16 18:03:11 +03:00
if ( status = = 0 | | status = = 0xFF ) {
dbgln_if ( PATA_DEBUG , " IDEChannel: {} {} device presence - none. " , channel_type_string ( ) , channel_string ( i ) ) ;
device_presence = false ;
break ;
}
IO : : delay ( 1000 ) ;
milliseconds_elapsed + + ;
}
if ( ! device_presence ) {
continue ;
}
if ( milliseconds_elapsed > 10000 ) {
dbgln_if ( PATA_DEBUG , " IDEChannel: {} {} device state unknown. Timeout exceeded. " , channel_type_string ( ) , channel_string ( i ) ) ;
continue ;
2021-01-29 19:37:40 +00:00
}
if ( check_for_atapi ) {
u8 cl = m_io_group . io_base ( ) . offset ( ATA_REG_LBA1 ) . in < u8 > ( ) ;
u8 ch = m_io_group . io_base ( ) . offset ( ATA_REG_LBA2 ) . in < u8 > ( ) ;
if ( ( cl = = 0x14 & & ch = = 0xEB ) | | ( cl = = 0x69 & & ch = = 0x96 ) ) {
interface_type = PATADiskDevice : : InterfaceType : : ATAPI ;
dbgln ( " IDEChannel: {} {} device appears to be ATAPI. We're going to ignore it for now as we don't support it. " , channel_type_string ( ) , channel_string ( i ) ) ;
continue ;
} else {
dbgln ( " IDEChannel: {} {} device doesn't appear to be ATA or ATAPI. Ignoring it. " , channel_type_string ( ) , channel_string ( i ) ) ;
continue ;
}
}
2019-07-28 23:44:01 +10:00
ByteBuffer wbuf = ByteBuffer : : create_uninitialized ( 512 ) ;
ByteBuffer bbuf = ByteBuffer : : create_uninitialized ( 512 ) ;
2019-09-30 08:57:01 +02:00
u8 * b = bbuf . data ( ) ;
u16 * w = ( u16 * ) wbuf . data ( ) ;
2019-07-28 23:44:01 +10:00
for ( u32 i = 0 ; i < 256 ; + + i ) {
2020-12-19 12:50:57 +02:00
u16 data = m_io_group . io_base ( ) . offset ( ATA_REG_DATA ) . in < u16 > ( ) ;
2019-07-28 23:44:01 +10:00
* ( w + + ) = data ;
* ( b + + ) = MSB ( data ) ;
* ( b + + ) = LSB ( data ) ;
}
2019-05-19 03:46:50 +02:00
2019-07-28 23:44:01 +10:00
// "Unpad" the device name string.
for ( u32 i = 93 ; i > 54 & & bbuf [ i ] = = ' ' ; - - i )
bbuf [ i ] = 0 ;
2021-03-26 13:51:31 +03:00
volatile ATAIdentifyBlock & identify_block = ( volatile ATAIdentifyBlock & ) ( * wbuf . data ( ) ) ;
2021-04-18 10:30:03 +02:00
u16 capabilities = identify_block . capabilities [ 0 ] ;
2021-03-26 13:51:31 +03:00
// If the drive is so old that it doesn't support LBA, ignore it.
if ( ! ( capabilities & ATA_CAP_LBA ) )
2020-12-19 12:50:57 +02:00
continue ;
2021-03-26 13:51:31 +03:00
u64 max_addressable_block = identify_block . max_28_bit_addressable_logical_sector ;
2019-07-28 23:44:01 +10:00
2021-03-26 13:51:31 +03:00
// if we support 48-bit LBA, use that value instead.
if ( identify_block . commands_and_feature_sets_supported [ 1 ] & ( 1 < < 10 ) )
max_addressable_block = identify_block . user_addressable_logical_sectors_count ;
dbgln ( " IDEChannel: {} {} {} device found: Name={}, Capacity={}, Capabilities=0x{:04x} " , channel_type_string ( ) , channel_string ( i ) , interface_type = = PATADiskDevice : : InterfaceType : : ATA ? " ATA " : " ATAPI " , ( ( char * ) bbuf . data ( ) + 54 ) , max_addressable_block * 512 , capabilities ) ;
2019-07-28 23:44:01 +10:00
if ( i = = 0 ) {
2021-03-16 20:23:16 +01:00
m_master = PATADiskDevice : : create ( m_parent_controller , * this , PATADiskDevice : : DriveType : : Master , interface_type , capabilities , max_addressable_block ) ;
2019-07-28 23:44:01 +10:00
} else {
2021-03-16 20:23:16 +01:00
m_slave = PATADiskDevice : : create ( m_parent_controller , * this , PATADiskDevice : : DriveType : : Slave , interface_type , capabilities , max_addressable_block ) ;
2019-07-28 23:44:01 +10:00
}
2019-05-19 03:46:50 +02:00
}
}
2021-03-27 09:01:00 +03:00
void IDEChannel : : ata_access ( Direction direction , bool slave_request , u64 lba , u8 block_count , u16 capabilities )
2021-01-29 19:37:40 +00:00
{
2021-03-27 09:53:59 +03:00
VERIFY ( m_lock . is_locked ( ) ) ;
VERIFY ( m_request_lock . is_locked ( ) ) ;
2021-01-29 19:37:40 +00:00
LBAMode lba_mode ;
u8 head = 0 ;
2021-03-26 13:51:31 +03:00
VERIFY ( capabilities & ATA_CAP_LBA ) ;
2021-01-29 19:37:40 +00:00
if ( lba > = 0x10000000 ) {
lba_mode = LBAMode : : FortyEightBit ;
head = 0 ;
2021-03-26 13:51:31 +03:00
} else {
2021-01-29 19:37:40 +00:00
lba_mode = LBAMode : : TwentyEightBit ;
head = ( lba & 0xF000000 ) > > 24 ;
}
2021-04-16 18:03:11 +03:00
// Wait 1 second
wait_until_not_busy ( 1000 ) ;
2021-01-29 19:37:40 +00:00
2021-04-05 21:24:05 +03:00
// We need to select the drive and then we wait 20 microseconds... and it doesn't hurt anything so let's just do it.
2021-03-26 13:51:31 +03:00
m_io_group . io_base ( ) . offset ( ATA_REG_HDDEVSEL ) . out < u8 > ( 0xE0 | ( static_cast < u8 > ( slave_request ) < < 4 ) | head ) ;
2021-04-05 21:24:05 +03:00
IO : : delay ( 20 ) ;
2021-01-29 19:37:40 +00:00
if ( lba_mode = = LBAMode : : FortyEightBit ) {
m_io_group . io_base ( ) . offset ( ATA_REG_SECCOUNT1 ) . out < u8 > ( 0 ) ;
m_io_group . io_base ( ) . offset ( ATA_REG_LBA3 ) . out < u8 > ( ( lba & 0xFF000000 ) > > 24 ) ;
2021-03-16 20:31:45 +01:00
m_io_group . io_base ( ) . offset ( ATA_REG_LBA4 ) . out < u8 > ( ( lba & 0xFF00000000ull ) > > 32 ) ;
m_io_group . io_base ( ) . offset ( ATA_REG_LBA5 ) . out < u8 > ( ( lba & 0xFF0000000000ull ) > > 40 ) ;
2021-01-29 19:37:40 +00:00
}
m_io_group . io_base ( ) . offset ( ATA_REG_SECCOUNT0 ) . out < u8 > ( block_count ) ;
2021-03-26 13:51:31 +03:00
m_io_group . io_base ( ) . offset ( ATA_REG_LBA0 ) . out < u8 > ( ( lba & 0x000000FF ) > > 0 ) ;
m_io_group . io_base ( ) . offset ( ATA_REG_LBA1 ) . out < u8 > ( ( lba & 0x0000FF00 ) > > 8 ) ;
m_io_group . io_base ( ) . offset ( ATA_REG_LBA2 ) . out < u8 > ( ( lba & 0x00FF0000 ) > > 16 ) ;
2021-01-29 19:37:40 +00:00
for ( ; ; ) {
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
auto status = m_io_group . control_base ( ) . in < u8 > ( ) ;
2021-01-29 19:37:40 +00:00
if ( ! ( status & ATA_SR_BSY ) & & ( status & ATA_SR_DRDY ) )
break ;
}
2021-03-27 09:01:00 +03:00
send_ata_io_command ( lba_mode , direction ) ;
2021-01-29 19:37:40 +00:00
enable_irq ( ) ;
}
2021-03-27 09:01:00 +03:00
void IDEChannel : : send_ata_io_command ( LBAMode lba_mode , Direction direction ) const
2019-05-19 03:46:50 +02:00
{
2021-03-27 09:01:00 +03:00
if ( lba_mode ! = LBAMode : : FortyEightBit ) {
m_io_group . io_base ( ) . offset ( ATA_REG_COMMAND ) . out < u8 > ( direction = = Direction : : Read ? ATA_CMD_READ_PIO : ATA_CMD_WRITE_PIO ) ;
} else {
m_io_group . io_base ( ) . offset ( ATA_REG_COMMAND ) . out < u8 > ( direction = = Direction : : Read ? ATA_CMD_READ_PIO_EXT : ATA_CMD_WRITE_PIO_EXT ) ;
}
2020-11-02 11:16:01 -07:00
}
2019-05-19 03:46:50 +02:00
2020-12-19 12:50:57 +02:00
bool IDEChannel : : ata_do_read_sector ( )
2020-11-02 11:16:01 -07:00
{
2021-03-27 09:53:59 +03:00
VERIFY ( m_lock . is_locked ( ) ) ;
VERIFY ( m_request_lock . is_locked ( ) ) ;
VERIFY ( ! m_current_request . is_null ( ) ) ;
2021-02-07 15:33:24 +03:30
dbgln_if ( PATA_DEBUG , " IDEChannel::ata_do_read_sector " ) ;
2020-11-02 11:16:01 -07:00
auto & request = * m_current_request ;
auto out_buffer = request . buffer ( ) . offset ( m_current_request_block_index * 512 ) ;
2021-05-13 09:08:44 +02:00
auto result = request . write_to_buffer_buffered < 512 > ( out_buffer , 512 , [ & ] ( u8 * buffer , size_t buffer_bytes ) {
2020-11-02 11:16:01 -07:00
for ( size_t i = 0 ; i < buffer_bytes ; i + = sizeof ( u16 ) )
2020-12-19 12:50:57 +02:00
* ( u16 * ) & buffer [ i ] = IO : : in16 ( m_io_group . io_base ( ) . offset ( ATA_REG_DATA ) . get ( ) ) ;
2021-05-13 09:08:44 +02:00
return buffer_bytes ;
2020-11-02 11:16:01 -07:00
} ) ;
2021-05-13 09:08:44 +02:00
if ( result . is_error ( ) ) {
2020-11-02 11:16:01 -07:00
// TODO: Do we need to abort the PATA read if this wasn't the last block?
complete_current_request ( AsyncDeviceRequest : : MemoryFault ) ;
2019-05-19 03:56:06 +02:00
return false ;
2020-11-02 11:16:01 -07:00
}
2019-05-19 03:46:50 +02:00
return true ;
2018-11-10 15:15:31 +01:00
}
2021-01-29 19:37:40 +00:00
// FIXME: This doesn't quite work and locks up reading LBA 3.
void IDEChannel : : ata_read_sectors ( bool slave_request , u16 capabilities )
2018-11-10 15:15:31 +01:00
{
2021-03-27 09:53:59 +03:00
VERIFY ( m_lock . is_locked ( ) ) ;
VERIFY ( ! m_current_request . is_null ( ) ) ;
VERIFY ( m_current_request - > block_count ( ) < = 256 ) ;
2019-05-26 14:58:21 +02:00
2021-03-27 09:53:59 +03:00
ScopedSpinLock m_lock ( m_request_lock ) ;
dbgln_if ( PATA_DEBUG , " IDEChannel::ata_read_sectors " ) ;
dbgln_if ( PATA_DEBUG , " IDEChannel: Reading {} sector(s) @ LBA {} " , m_current_request - > block_count ( ) , m_current_request - > block_index ( ) ) ;
ata_access ( Direction : : Read , slave_request , m_current_request - > block_index ( ) , m_current_request - > block_count ( ) , capabilities ) ;
2020-11-02 11:16:01 -07:00
}
2020-04-15 15:46:36 +03:00
2020-12-19 12:50:57 +02:00
void IDEChannel : : ata_do_write_sector ( )
2020-11-02 11:16:01 -07:00
{
2021-03-27 09:53:59 +03:00
VERIFY ( m_lock . is_locked ( ) ) ;
VERIFY ( m_request_lock . is_locked ( ) ) ;
VERIFY ( ! m_current_request . is_null ( ) ) ;
2020-11-02 11:16:01 -07:00
auto & request = * m_current_request ;
2020-04-15 15:46:36 +03:00
2020-11-02 11:16:01 -07:00
io_delay ( ) ;
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
while ( ( m_io_group . control_base ( ) . in < u8 > ( ) & ATA_SR_BSY ) | | ! ( m_io_group . control_base ( ) . in < u8 > ( ) & ATA_SR_DRQ ) )
2020-11-02 11:16:01 -07:00
;
2020-11-04 21:25:26 +01:00
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first
change is about to ensure we only read the ATA Status register only
once, because if we read it multiple times, we acknowledge interrupts
unintentionally. To solve this issue, we always use the alternate Status
register and only read the original status register in the IRQ handler.
The second change is how we handle interrupts - if we use DMA, we can
just complete the request and return from the IRQ handler. For PIO mode,
it's more complicated. For PIO write operation, after setting the ATA
registers, we send out the data to IO port, and wait for an interrupt.
For PIO read operation, we set the ATA registers, and wait for an
interrupt to fire, then we just read from the data IO port.
2021-02-01 19:12:08 +02:00
u8 status = m_io_group . control_base ( ) . in < u8 > ( ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( status & ATA_SR_DRQ ) ;
2020-11-04 21:25:26 +01:00
2020-11-02 11:16:01 -07:00
auto in_buffer = request . buffer ( ) . offset ( m_current_request_block_index * 512 ) ;
2021-02-07 15:33:24 +03:30
dbgln_if ( PATA_DEBUG , " IDEChannel: Writing 512 bytes (part {}) (status={:#02x})... " , m_current_request_block_index , status ) ;
2021-05-13 09:08:44 +02:00
auto result = request . read_from_buffer_buffered < 512 > ( in_buffer , 512 , [ & ] ( u8 const * buffer , size_t buffer_bytes ) {
2020-11-02 11:16:01 -07:00
for ( size_t i = 0 ; i < buffer_bytes ; i + = sizeof ( u16 ) )
2020-12-19 12:50:57 +02:00
IO : : out16 ( m_io_group . io_base ( ) . offset ( ATA_REG_DATA ) . get ( ) , * ( const u16 * ) & buffer [ i ] ) ;
2021-05-13 09:08:44 +02:00
return buffer_bytes ;
2020-11-02 11:16:01 -07:00
} ) ;
2021-05-13 09:08:44 +02:00
if ( result . is_error ( ) )
2020-11-02 11:16:01 -07:00
complete_current_request ( AsyncDeviceRequest : : MemoryFault ) ;
2019-07-28 23:44:01 +10:00
}
2021-01-29 19:37:40 +00:00
// FIXME: I'm assuming this doesn't work based on the fact PIO read doesn't work.
void IDEChannel : : ata_write_sectors ( bool slave_request , u16 capabilities )
2019-07-28 23:44:01 +10:00
{
2021-03-27 09:53:59 +03:00
VERIFY ( m_lock . is_locked ( ) ) ;
VERIFY ( ! m_current_request . is_null ( ) ) ;
VERIFY ( m_current_request - > block_count ( ) < = 256 ) ;
2018-11-18 14:57:41 +01:00
2021-03-27 09:53:59 +03:00
ScopedSpinLock m_lock ( m_request_lock ) ;
dbgln_if ( PATA_DEBUG , " IDEChannel: Writing {} sector(s) @ LBA {} " , m_current_request - > block_count ( ) , m_current_request - > block_index ( ) ) ;
ata_access ( Direction : : Write , slave_request , m_current_request - > block_index ( ) , m_current_request - > block_count ( ) , capabilities ) ;
2020-11-02 11:16:01 -07:00
ata_do_write_sector ( ) ;
2018-11-18 14:57:41 +01:00
}
2020-02-16 01:27:42 +01:00
}