mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
481 lines
20 KiB
C#
481 lines
20 KiB
C#
// /***************************************************************************
|
|
// Aaru Data Preservation Suite
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Filename : ReaderATA.cs
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
//
|
|
// Component : Core algorithms.
|
|
//
|
|
// --[ Description ] ----------------------------------------------------------
|
|
//
|
|
// Contains common code for reading ATA devices.
|
|
//
|
|
// --[ License ] --------------------------------------------------------------
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
// Copyright © 2011-2025 Natalia Portillo
|
|
// ****************************************************************************/
|
|
|
|
using System;
|
|
using Aaru.CommonTypes.Enums;
|
|
using Aaru.Decoders.ATA;
|
|
using Aaru.Logging;
|
|
using Identify = Aaru.CommonTypes.Structs.Devices.ATA.Identify;
|
|
|
|
namespace Aaru.Core.Devices;
|
|
|
|
sealed partial class Reader
|
|
{
|
|
Identify.IdentifyDevice _ataId;
|
|
bool _ataRead;
|
|
bool _ataReadDma;
|
|
bool _ataReadDmaLba;
|
|
bool _ataReadDmaLba48;
|
|
bool _ataReadDmaRetry;
|
|
bool _ataReadDmaRetryLba;
|
|
bool _ataReadLba;
|
|
bool _ataReadLba48;
|
|
bool _ataReadRetry;
|
|
bool _ataReadRetryLba;
|
|
bool _ataSeek;
|
|
bool _ataSeekLba;
|
|
|
|
internal bool IsLba { get; private set; }
|
|
internal ushort Cylinders { get; private set; }
|
|
internal byte Heads { get; private set; }
|
|
internal byte Sectors { get; private set; }
|
|
|
|
void GetDeviceChs()
|
|
{
|
|
if(_dev.Type != DeviceType.ATA) return;
|
|
|
|
if(_ataId.CurrentCylinders > 0 && _ataId is { CurrentHeads: > 0, CurrentSectorsPerTrack: > 0 })
|
|
{
|
|
Cylinders = _ataId.CurrentCylinders;
|
|
Heads = (byte)_ataId.CurrentHeads;
|
|
Sectors = (byte)_ataId.CurrentSectorsPerTrack;
|
|
Blocks = (ulong)(Cylinders * Heads * Sectors);
|
|
}
|
|
|
|
if(_ataId.CurrentCylinders != 0 && _ataId.CurrentHeads != 0 && _ataId.CurrentSectorsPerTrack != 0 ||
|
|
_ataId.Cylinders <= 0 ||
|
|
_ataId.Heads <= 0 ||
|
|
_ataId.SectorsPerTrack <= 0)
|
|
return;
|
|
|
|
Cylinders = _ataId.Cylinders;
|
|
Heads = (byte)_ataId.Heads;
|
|
Sectors = (byte)_ataId.SectorsPerTrack;
|
|
Blocks = (ulong)(Cylinders * Heads * Sectors);
|
|
}
|
|
|
|
ulong AtaGetBlocks()
|
|
{
|
|
GetDeviceChs();
|
|
|
|
if(_ataId.Capabilities.HasFlag(Identify.CapabilitiesBit.LBASupport))
|
|
{
|
|
Blocks = _ataId.LBASectors;
|
|
IsLba = true;
|
|
}
|
|
|
|
if(!_ataId.CommandSet2.HasFlag(Identify.CommandSetBit2.LBA48)) return Blocks;
|
|
|
|
Blocks = _ataId.LBA48Sectors;
|
|
IsLba = true;
|
|
|
|
return Blocks;
|
|
}
|
|
|
|
bool AtaFindReadCommand()
|
|
{
|
|
if(Blocks == 0) GetDeviceBlocks();
|
|
|
|
bool sense;
|
|
var tries = 0;
|
|
uint lba = 0;
|
|
ushort cyl = 0;
|
|
byte head = 0;
|
|
byte sector = 1;
|
|
AtaErrorRegistersChs errorChs;
|
|
AtaErrorRegistersLba28 errorLba;
|
|
var rnd = new Random();
|
|
|
|
while(tries < 10)
|
|
{
|
|
sense = _dev.Read(out byte[] cmdBuf, out errorChs, false, cyl, head, sector, 1, _timeout, out _);
|
|
_ataRead = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && cmdBuf.Length > 0;
|
|
sense = _dev.Read(out cmdBuf, out errorChs, true, cyl, head, sector, 1, _timeout, out _);
|
|
_ataReadRetry = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && cmdBuf.Length > 0;
|
|
sense = _dev.ReadDma(out cmdBuf, out errorChs, false, cyl, head, sector, 1, _timeout, out _);
|
|
_ataReadDma = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && cmdBuf.Length > 0;
|
|
sense = _dev.ReadDma(out cmdBuf, out errorChs, true, cyl, head, sector, 1, _timeout, out _);
|
|
_ataReadDmaRetry = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && cmdBuf.Length > 0;
|
|
|
|
sense = _dev.Read(out cmdBuf, out errorLba, false, lba, 1, _timeout, out _);
|
|
_ataReadLba = !sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0;
|
|
sense = _dev.Read(out cmdBuf, out errorLba, true, lba, 1, _timeout, out _);
|
|
_ataReadRetryLba = !sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0;
|
|
sense = _dev.ReadDma(out cmdBuf, out errorLba, false, lba, 1, _timeout, out _);
|
|
_ataReadDmaLba = !sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0;
|
|
sense = _dev.ReadDma(out cmdBuf, out errorLba, true, lba, 1, _timeout, out _);
|
|
|
|
_ataReadDmaRetryLba = !sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0;
|
|
|
|
sense = _dev.Read(out cmdBuf, out AtaErrorRegistersLba48 errorLba48, lba, 1, _timeout, out _);
|
|
_ataReadLba48 = !sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && cmdBuf.Length > 0;
|
|
sense = _dev.ReadDma(out cmdBuf, out errorLba48, lba, 1, _timeout, out _);
|
|
|
|
_ataReadDmaLba48 = !sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && cmdBuf.Length > 0;
|
|
|
|
if(_ataRead ||
|
|
_ataReadRetry ||
|
|
_ataReadDma ||
|
|
_ataReadDmaRetry ||
|
|
_ataReadLba ||
|
|
_ataReadRetryLba ||
|
|
_ataReadDmaLba ||
|
|
_ataReadDmaRetryLba ||
|
|
_ataReadLba48 ||
|
|
_ataReadDmaLba48)
|
|
break;
|
|
|
|
lba = (uint)rnd.Next(1, (int)Blocks);
|
|
cyl = (ushort)rnd.Next(0, Cylinders);
|
|
head = (byte)rnd.Next(0, Heads);
|
|
sector = (byte)rnd.Next(1, Sectors);
|
|
tries++;
|
|
}
|
|
|
|
sense = _dev.Seek(out errorChs, 0, 0, 1, _timeout, out _);
|
|
_ataSeek = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0;
|
|
sense = _dev.Seek(out errorLba, 0, _timeout, out _);
|
|
_ataSeekLba = !sense && (errorLba.Status & 0x27) == 0 && errorChs.Error == 0;
|
|
|
|
if(IsLba)
|
|
{
|
|
if(Blocks > 0xFFFFFFF && !_ataReadLba48 && !_ataReadDmaLba48)
|
|
{
|
|
ErrorMessage = Localization.Core.Device_needs_48_bit_LBA_commands_but_I_cant_issue_them_Aborting;
|
|
|
|
return true;
|
|
}
|
|
|
|
if(!_ataReadLba && !_ataReadRetryLba && !_ataReadDmaLba && !_ataReadDmaRetryLba)
|
|
{
|
|
ErrorMessage = Localization.Core.Device_needs_28_bit_LBA_commands_but_I_cant_issue_them_Aborting;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!_ataRead && !_ataReadRetry && !_ataReadDma && !_ataReadDmaRetry)
|
|
{
|
|
ErrorMessage = Localization.Core.Device_needs_CHS_commands_but_I_cant_issue_them_Aborting;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(_ataReadDmaLba48)
|
|
AaruLogging.WriteLine(Localization.Core.Using_ATA_READ_DMA_EXT_command);
|
|
else if(_ataReadLba48)
|
|
AaruLogging.WriteLine(Localization.Core.Using_ATA_READ_EXT_command);
|
|
else if(_ataReadDmaRetryLba)
|
|
AaruLogging.WriteLine(Localization.Core.Using_ATA_READ_DMA_command_with_retries_LBA);
|
|
else if(_ataReadDmaLba)
|
|
AaruLogging.WriteLine(Localization.Core.Using_ATA_READ_DMA_command_LBA);
|
|
else if(_ataReadRetryLba)
|
|
AaruLogging.WriteLine(Localization.Core.Using_ATA_READ_command_with_retries_LBA);
|
|
else if(_ataReadLba)
|
|
AaruLogging.WriteLine(Localization.Core.Using_ATA_READ_command_LBA);
|
|
else if(_ataReadDmaRetry)
|
|
AaruLogging.WriteLine(Localization.Core.Using_ATA_READ_DMA_command_with_retries_CHS);
|
|
else if(_ataReadDma)
|
|
AaruLogging.WriteLine(Localization.Core.Using_ATA_READ_DMA_command_CHS);
|
|
else if(_ataReadRetry)
|
|
AaruLogging.WriteLine(Localization.Core.Using_ATA_READ_command_with_retries_CHS);
|
|
else if(_ataRead)
|
|
AaruLogging.WriteLine(Localization.Core.Using_ATA_READ_command_CHS);
|
|
else
|
|
{
|
|
ErrorMessage = Localization.Core.Could_not_get_a_working_read_command;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AtaGetBlockSize()
|
|
{
|
|
if((_ataId.PhysLogSectorSize & 0x8000) == 0x0000 && (_ataId.PhysLogSectorSize & 0x4000) == 0x4000)
|
|
{
|
|
if((_ataId.PhysLogSectorSize & 0x1000) == 0x1000)
|
|
{
|
|
if(_ataId.LogicalSectorWords <= 255 || _ataId.LogicalAlignment == 0xFFFF)
|
|
LogicalBlockSize = 512;
|
|
else
|
|
LogicalBlockSize = _ataId.LogicalSectorWords * 2;
|
|
}
|
|
else
|
|
LogicalBlockSize = 512;
|
|
|
|
if((_ataId.PhysLogSectorSize & 0x2000) == 0x2000)
|
|
PhysicalBlockSize = LogicalBlockSize * (uint)Math.Pow(2, _ataId.PhysLogSectorSize & 0xF);
|
|
else
|
|
PhysicalBlockSize = LogicalBlockSize;
|
|
}
|
|
else
|
|
{
|
|
LogicalBlockSize = 512;
|
|
PhysicalBlockSize = 512;
|
|
}
|
|
|
|
// TODO: ATA READ LONG
|
|
LongBlockSize = 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AtaGetBlocksToRead(uint startWithBlocks)
|
|
{
|
|
BlocksToRead = startWithBlocks;
|
|
|
|
if(!IsLba)
|
|
{
|
|
BlocksToRead = 1;
|
|
|
|
return false;
|
|
}
|
|
|
|
var error = true;
|
|
|
|
while(IsLba)
|
|
{
|
|
byte[] cmdBuf;
|
|
bool sense;
|
|
AtaErrorRegistersLba48 errorLba48;
|
|
|
|
if(_ataReadDmaLba48)
|
|
{
|
|
sense = _dev.ReadDma(out cmdBuf, out errorLba48, 0, (byte)BlocksToRead, _timeout, out _);
|
|
error = !(!sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && cmdBuf.Length > 0);
|
|
}
|
|
else if(_ataReadLba48)
|
|
{
|
|
sense = _dev.Read(out cmdBuf, out errorLba48, 0, (byte)BlocksToRead, _timeout, out _);
|
|
error = !(!sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && cmdBuf.Length > 0);
|
|
}
|
|
else
|
|
{
|
|
AtaErrorRegistersLba28 errorLba;
|
|
|
|
if(_ataReadDmaRetryLba)
|
|
{
|
|
sense = _dev.ReadDma(out cmdBuf, out errorLba, true, 0, (byte)BlocksToRead, _timeout, out _);
|
|
error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0);
|
|
}
|
|
else if(_ataReadDmaLba)
|
|
{
|
|
sense = _dev.ReadDma(out cmdBuf, out errorLba, false, 0, (byte)BlocksToRead, _timeout, out _);
|
|
error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0);
|
|
}
|
|
else if(_ataReadRetryLba)
|
|
{
|
|
sense = _dev.Read(out cmdBuf, out errorLba, true, 0, (byte)BlocksToRead, _timeout, out _);
|
|
error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0);
|
|
}
|
|
else if(_ataReadLba)
|
|
{
|
|
sense = _dev.Read(out cmdBuf, out errorLba, false, 0, (byte)BlocksToRead, _timeout, out _);
|
|
error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0);
|
|
}
|
|
}
|
|
|
|
if(error) BlocksToRead /= 2;
|
|
|
|
if(!error || BlocksToRead == 1) break;
|
|
}
|
|
|
|
if(!error || !IsLba) return false;
|
|
|
|
BlocksToRead = 1;
|
|
|
|
ErrorMessage = string.Format(Localization.Core.Device_error_0_trying_to_guess_ideal_transfer_length,
|
|
_dev.LastError);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AtaReadBlocks(out byte[] buffer, ulong block, uint count, out double duration, out bool recoveredError)
|
|
{
|
|
var error = true;
|
|
bool sense;
|
|
AtaErrorRegistersLba28 errorLba;
|
|
AtaErrorRegistersLba48 errorLba48;
|
|
byte status = 0, errorByte = 0;
|
|
buffer = null;
|
|
duration = 0;
|
|
recoveredError = false;
|
|
|
|
if(_ataReadDmaLba48)
|
|
{
|
|
sense = _dev.ReadDma(out buffer, out errorLba48, block, (byte)count, _timeout, out duration);
|
|
error = !(!sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && buffer.Length > 0);
|
|
status = errorLba48.Status;
|
|
errorByte = errorLba48.Error;
|
|
|
|
if(error) _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba48);
|
|
}
|
|
else if(_ataReadLba48)
|
|
{
|
|
sense = _dev.Read(out buffer, out errorLba48, block, (byte)count, _timeout, out duration);
|
|
error = !(!sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && buffer.Length > 0);
|
|
status = errorLba48.Status;
|
|
errorByte = errorLba48.Error;
|
|
|
|
if(error) _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba48);
|
|
}
|
|
else if(_ataReadDmaRetryLba)
|
|
{
|
|
sense = _dev.ReadDma(out buffer, out errorLba, true, (uint)block, (byte)count, _timeout, out duration);
|
|
|
|
error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && buffer.Length > 0);
|
|
status = errorLba.Status;
|
|
errorByte = errorLba.Error;
|
|
|
|
if(error) _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba);
|
|
}
|
|
else if(_ataReadDmaLba)
|
|
{
|
|
sense = _dev.ReadDma(out buffer, out errorLba, false, (uint)block, (byte)count, _timeout, out duration);
|
|
|
|
error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && buffer.Length > 0);
|
|
status = errorLba.Status;
|
|
errorByte = errorLba.Error;
|
|
|
|
if(error) _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba);
|
|
}
|
|
else if(_ataReadRetryLba)
|
|
{
|
|
sense = _dev.Read(out buffer, out errorLba, true, (uint)block, (byte)count, _timeout, out duration);
|
|
error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && buffer.Length > 0);
|
|
status = errorLba.Status;
|
|
errorByte = errorLba.Error;
|
|
|
|
if(error) _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba);
|
|
}
|
|
else if(_ataReadLba)
|
|
{
|
|
sense = _dev.Read(out buffer, out errorLba, false, (uint)block, (byte)count, _timeout, out duration);
|
|
error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && buffer.Length > 0);
|
|
status = errorLba.Status;
|
|
errorByte = errorLba.Error;
|
|
|
|
if(error) _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba);
|
|
}
|
|
|
|
if(!error) return false;
|
|
|
|
if((status & 0x04) == 0x04) recoveredError = true;
|
|
|
|
AaruLogging.Debug(ATA_MODULE_NAME, Localization.Core.ATA_ERROR_0_STATUS_1, errorByte, status);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AtaReadChs(out byte[] buffer, ushort cylinder, byte head, byte sector, out double duration,
|
|
out bool recoveredError)
|
|
{
|
|
var error = true;
|
|
bool sense;
|
|
AtaErrorRegistersChs errorChs;
|
|
byte status = 0, errorByte = 0;
|
|
buffer = null;
|
|
duration = 0;
|
|
recoveredError = false;
|
|
|
|
if(_ataReadDmaRetry)
|
|
{
|
|
sense = _dev.ReadDma(out buffer, out errorChs, true, cylinder, head, sector, 1, _timeout, out duration);
|
|
|
|
error = !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && buffer.Length > 0);
|
|
status = errorChs.Status;
|
|
errorByte = errorChs.Error;
|
|
|
|
if(error) _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs);
|
|
}
|
|
else if(_ataReadDma)
|
|
{
|
|
sense = _dev.ReadDma(out buffer, out errorChs, false, cylinder, head, sector, 1, _timeout, out duration);
|
|
|
|
error = !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && buffer.Length > 0);
|
|
status = errorChs.Status;
|
|
errorByte = errorChs.Error;
|
|
|
|
if(error) _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs);
|
|
}
|
|
else if(_ataReadRetry)
|
|
{
|
|
sense = _dev.Read(out buffer, out errorChs, true, cylinder, head, sector, 1, _timeout, out duration);
|
|
error = !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && buffer.Length > 0);
|
|
status = errorChs.Status;
|
|
errorByte = errorChs.Error;
|
|
|
|
if(error) _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs);
|
|
}
|
|
else if(_ataRead)
|
|
{
|
|
sense = _dev.Read(out buffer, out errorChs, false, cylinder, head, sector, 1, _timeout, out duration);
|
|
error = !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && buffer.Length > 0);
|
|
status = errorChs.Status;
|
|
errorByte = errorChs.Error;
|
|
|
|
if(error) _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs);
|
|
}
|
|
|
|
if(!error) return false;
|
|
|
|
if((status & 0x04) == 0x04) recoveredError = true;
|
|
|
|
AaruLogging.Debug(ATA_MODULE_NAME, Localization.Core.ATA_ERROR_0_STATUS_1, errorByte, status);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AtaSeek(ulong block, out double duration)
|
|
{
|
|
bool sense = _dev.Seek(out AtaErrorRegistersLba28 errorLba, (uint)block, _timeout, out duration);
|
|
|
|
if(!(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0))
|
|
_errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba);
|
|
|
|
return !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0);
|
|
}
|
|
|
|
bool AtaSeekChs(ushort cylinder, byte head, byte sector, out double duration)
|
|
{
|
|
bool sense = _dev.Seek(out AtaErrorRegistersChs errorChs, cylinder, head, sector, _timeout, out duration);
|
|
|
|
if(!(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0))
|
|
_errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs);
|
|
|
|
return !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0);
|
|
}
|
|
} |