RASCSI/src/raspberrypi/devices/disk.cpp

912 lines
21 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
//
// XM6i
// Copyright (C) 2010-2015 isaki@NetBSD.org
// Copyright (C) 2010 Y.Sugahara
//
// Imported sava's Anex86/T98Next image and MO format support patch.
// Comments translated to english by akuker.
//
//---------------------------------------------------------------------------
#include "os.h"
#include "fileio.h"
#include "file_support.h"
#include "rascsi_exceptions.h"
#include "dispatcher.h"
#include "scsi_command_util.h"
#include "disk.h"
using namespace scsi_defs;
using namespace scsi_command_util;
Disk::Disk(const string& type, int lun) : ModePageDevice(type, lun)
{
dispatcher.Add(scsi_command::eCmdRezero, "Rezero", &Disk::Rezero);
dispatcher.Add(scsi_command::eCmdFormat, "FormatUnit", &Disk::FormatUnit);
dispatcher.Add(scsi_command::eCmdReassign, "ReassignBlocks", &Disk::ReassignBlocks);
dispatcher.Add(scsi_command::eCmdRead6, "Read6", &Disk::Read6);
dispatcher.Add(scsi_command::eCmdWrite6, "Write6", &Disk::Write6);
dispatcher.Add(scsi_command::eCmdSeek6, "Seek6", &Disk::Seek6);
dispatcher.Add(scsi_command::eCmdReserve6, "Reserve6", &Disk::Reserve);
dispatcher.Add(scsi_command::eCmdRelease6, "Release6", &Disk::Release);
dispatcher.Add(scsi_command::eCmdStartStop, "StartStopUnit", &Disk::StartStopUnit);
dispatcher.Add(scsi_command::eCmdSendDiag, "SendDiagnostic", &Disk::SendDiagnostic);
dispatcher.Add(scsi_command::eCmdRemoval, "PreventAllowMediumRemoval", &Disk::PreventAllowMediumRemoval);
dispatcher.Add(scsi_command::eCmdReadCapacity10, "ReadCapacity10", &Disk::ReadCapacity10);
dispatcher.Add(scsi_command::eCmdRead10, "Read10", &Disk::Read10);
dispatcher.Add(scsi_command::eCmdWrite10, "Write10", &Disk::Write10);
dispatcher.Add(scsi_command::eCmdReadLong10, "ReadLong10", &Disk::ReadWriteLong10);
dispatcher.Add(scsi_command::eCmdWriteLong10, "WriteLong10", &Disk::ReadWriteLong10);
dispatcher.Add(scsi_command::eCmdWriteLong16, "WriteLong16", &Disk::ReadWriteLong16);
dispatcher.Add(scsi_command::eCmdSeek10, "Seek10", &Disk::Seek10);
dispatcher.Add(scsi_command::eCmdVerify10, "Verify10", &Disk::Verify10);
dispatcher.Add(scsi_command::eCmdSynchronizeCache10, "SynchronizeCache10", &Disk::SynchronizeCache);
dispatcher.Add(scsi_command::eCmdSynchronizeCache16, "SynchronizeCache16", &Disk::SynchronizeCache);
dispatcher.Add(scsi_command::eCmdReadDefectData10, "ReadDefectData10", &Disk::ReadDefectData10);
dispatcher.Add(scsi_command::eCmdReserve10, "Reserve10", &Disk::Reserve);
dispatcher.Add(scsi_command::eCmdRelease10, "Release10", &Disk::Release);
dispatcher.Add(scsi_command::eCmdRead16, "Read16", &Disk::Read16);
dispatcher.Add(scsi_command::eCmdWrite16, "Write16", &Disk::Write16);
dispatcher.Add(scsi_command::eCmdVerify16, "Verify16", &Disk::Verify16);
dispatcher.Add(scsi_command::eCmdReadCapacity16_ReadLong16, "ReadCapacity16/ReadLong16", &Disk::ReadCapacity16_ReadLong16);
}
Disk::~Disk()
{
// Save disk cache, only if ready
if (IsReady() && cache != nullptr) {
cache->Save();
}
}
bool Disk::Dispatch(scsi_command cmd)
{
// Media changes must be reported on the next access, i.e. not only for TEST UNIT READY
if (is_medium_changed) {
assert(IsRemovable());
is_medium_changed = false;
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
}
// The superclass handles the less specific commands
return dispatcher.Dispatch(this, cmd) ? true : super::Dispatch(cmd);
}
//---------------------------------------------------------------------------
//
// Open
// * Call as a post-process after successful opening in a derived class
//
//---------------------------------------------------------------------------
void Disk::Open(const Filepath& path)
{
if (blocks == 0) {
throw io_exception("Disk has 0 blocks");
}
SetReady(true);
// Can read/write open
if (Fileio fio; fio.Open(path, Fileio::OpenMode::ReadWrite)) {
// Write permission
fio.Close();
} else {
// Permanently write-protected
SetReadOnly(true);
SetProtectable(false);
SetProtected(false);
}
SetStopped(false);
SetRemoved(false);
SetLocked(false);
}
void Disk::SetUpCache(const Filepath& path, off_t image_offset, bool raw)
{
assert(cache == nullptr);
cache = make_unique<DiskCache>(path, size_shift_count, (uint32_t)blocks, image_offset);
cache->SetRawMode(raw);
}
void Disk::ResizeCache(const Filepath& path, bool raw)
{
cache.reset(new DiskCache(path, GetSectorSizeShiftCount(), (uint32_t)blocks));
cache->SetRawMode(raw);
}
void Disk::FlushCache()
{
if (cache != nullptr) {
cache->Save();
}
}
void Disk::Rezero()
{
Seek();
}
void Disk::FormatUnit()
{
CheckReady();
// FMTDATA=1 is not supported (but OK if there is no DEFECT LIST)
if ((ctrl->cmd[1] & 0x10) != 0 && ctrl->cmd[4] != 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
EnterStatusPhase();
}
void Disk::ReassignBlocks()
{
Seek();
}
void Disk::Read(access_mode mode)
{
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
ctrl->length = Read(ctrl->cmd, controller->GetBuffer(), start);
LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, controller->GetLength())
// Set next block
ctrl->next = start + 1;
EnterDataInPhase();
}
else {
EnterStatusPhase();
}
}
void Disk::Read6()
{
Read(RW6);
}
void Disk::Read10()
{
Read(RW10);
}
void Disk::Read16()
{
Read(RW16);
}
void Disk::ReadWriteLong10()
{
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
if (GetInt16(ctrl->cmd, 7) != 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
ValidateBlockAddress(RW10);
EnterStatusPhase();
}
void Disk::ReadWriteLong16()
{
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
if (GetInt16(ctrl->cmd, 12) != 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
ValidateBlockAddress(RW16);
EnterStatusPhase();
}
void Disk::Write(access_mode mode)
{
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
ctrl->length = WriteCheck(start);
// Set next block
ctrl->next = start + 1;
EnterDataOutPhase();
}
else {
EnterStatusPhase();
}
}
void Disk::Write6()
{
Write(RW6);
}
void Disk::Write10()
{
Write(RW10);
}
void Disk::Write16()
{
Write(RW16);
}
void Disk::Verify(access_mode mode)
{
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
// if BytChk=0
if ((ctrl->cmd[1] & 0x02) == 0) {
Seek();
return;
}
// Test reading
ctrl->length = Read(ctrl->cmd, controller->GetBuffer(), start);
// Set next block
ctrl->next = start + 1;
EnterDataOutPhase();
}
else {
EnterStatusPhase();
}
}
void Disk::Verify10()
{
Verify(RW10);
}
void Disk::Verify16()
{
Verify(RW16);
}
void Disk::StartStopUnit()
{
bool start = ctrl->cmd[4] & 0x01;
bool load = ctrl->cmd[4] & 0x02;
if (load) {
LOGTRACE(start ? "Loading medium" : "Ejecting medium")
}
else {
LOGTRACE(start ? "Starting unit" : "Stopping unit")
SetStopped(!start);
}
if (!start) {
FlushCache();
// Look at the eject bit and eject if necessary
if (load) {
if (IsLocked()) {
// Cannot be ejected because it is locked
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
}
// Eject
if (!Eject(false)) {
throw scsi_error_exception();
}
}
}
EnterStatusPhase();
}
void Disk::SendDiagnostic()
{
// Do not support PF bit
if (ctrl->cmd[1] & 0x10) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// Do not support parameter list
if ((ctrl->cmd[3] != 0) || (ctrl->cmd[4] != 0)) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
EnterStatusPhase();
}
void Disk::PreventAllowMediumRemoval()
{
CheckReady();
bool lock = ctrl->cmd[4] & 0x01;
LOGTRACE(lock ? "Locking medium" : "Unlocking medium")
SetLocked(lock);
EnterStatusPhase();
}
void Disk::SynchronizeCache()
{
FlushCache();
EnterStatusPhase();
}
void Disk::ReadDefectData10()
{
size_t allocation_length = min((size_t)GetInt16(ctrl->cmd, 7), (size_t)4);
// The defect list is empty
fill_n(controller->GetBuffer().begin(), allocation_length, 0);
ctrl->length = (int)allocation_length;
EnterDataInPhase();
}
void Disk::MediumChanged()
{
if (IsRemovable()) {
is_medium_changed = true;
}
else {
LOGERROR("Medium change requested for non-removable medium")
}
}
bool Disk::Eject(bool force)
{
bool status = super::Eject(force);
if (status) {
FlushCache();
cache.reset();
// The image file for this drive is not in use anymore
if (auto file_support = dynamic_cast<FileSupport *>(this); file_support) {
file_support->UnreserveFile();
}
}
return status;
}
int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf) const
{
// Get length, clear buffer
auto length = (int)min(buf.size(), (size_t)cdb[4]);
fill_n(buf.begin(), length, 0);
// DEVICE SPECIFIC PARAMETER
if (IsProtected()) {
buf[2] = 0x80;
}
// Basic information
int size = 4;
// Add block descriptor if DBD is 0
if ((cdb[1] & 0x08) == 0) {
// Mode parameter header, block descriptor length
buf[3] = 0x08;
// Only if ready
if (IsReady()) {
// Short LBA mode parameter block descriptor (number of blocks and block length)
SetInt32(buf, 4, (uint32_t)blocks);
SetInt32(buf, 8, GetSectorSizeInBytes());
}
size = 12;
}
size += super::AddModePages(cdb, buf, size, length - size);
if (size > 255) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// Do not return more than ALLOCATION LENGTH bytes
if (size > length) {
size = length;
}
// Final setting of mode data length
buf[0] = (BYTE)size;
return size;
}
int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf) const
{
// Get length, clear buffer
auto length = (int)min(buf.size(), (size_t)GetInt16(cdb, 7));
fill_n(buf.begin(), length, 0);
// DEVICE SPECIFIC PARAMETER
if (IsProtected()) {
buf[3] = 0x80;
}
// Basic Information
int size = 8;
// Add block descriptor if DBD is 0, only if ready
if ((cdb[1] & 0x08) == 0 && IsReady()) {
uint64_t disk_blocks = blocks;
uint32_t disk_size = GetSectorSizeInBytes();
// Check LLBAA for short or long block descriptor
if ((cdb[1] & 0x10) == 0 || disk_blocks <= 0xFFFFFFFF) {
// Mode parameter header, block descriptor length
buf[7] = 0x08;
// Short LBA mode parameter block descriptor (number of blocks and block length)
SetInt32(buf, 8, (uint32_t)disk_blocks);
SetInt32(buf, 12, disk_size);
size = 16;
}
else {
// Mode parameter header, LONGLBA
buf[4] = 0x01;
// Mode parameter header, block descriptor length
buf[7] = 0x10;
// Long LBA mode parameter block descriptor (number of blocks and block length)
SetInt64(buf, 8, disk_blocks);
SetInt32(buf, 20, disk_size);
size = 24;
}
}
size += super::AddModePages(cdb, buf, size, length - size);
if (size > 65535) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// Do not return more than ALLOCATION LENGTH bytes
if (size > length) {
size = length;
}
// Final setting of mode data length
SetInt16(buf, 0, size);
return size;
}
void Disk::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
{
// Page code 1 (read-write error recovery)
if (page == 0x01 || page == 0x3f) {
AddErrorPage(pages, changeable);
}
// Page code 3 (format device)
if (page == 0x03 || page == 0x3f) {
AddFormatPage(pages, changeable);
}
// Page code 4 (drive parameter)
if (page == 0x04 || page == 0x3f) {
AddDrivePage(pages, changeable);
}
// Page code 8 (caching)
if (page == 0x08 || page == 0x3f) {
AddCachePage(pages, changeable);
}
// Page (vendor special)
AddVendorPage(pages, page, changeable);
}
void Disk::AddErrorPage(map<int, vector<byte>>& pages, bool) const
{
vector<byte> buf(12);
// Retry count is 0, limit time uses internal default value
pages[1] = buf;
}
void Disk::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) const
{
vector<byte> buf(24);
// No changeable area
if (changeable) {
pages[3] = buf;
return;
}
if (IsReady()) {
// Set the number of tracks in one zone to 8
buf[0x03] = (byte)0x08;
// Set sector/track to 25
SetInt16(buf, 0x0a, 25);
// Set the number of bytes in the physical sector
SetInt16(buf, 0x0c, 1 << size_shift_count);
// Interleave 1
SetInt16(buf, 0x0e, 1);
// Track skew factor 11
SetInt16(buf, 0x10, 11);
// Cylinder skew factor 20
SetInt16(buf, 0x12, 20);
}
buf[20] = IsRemovable() ? (byte)0x20 : (byte)0x00;
// Hard-sectored
buf[20] |= (byte)0x40;
pages[3] = buf;
}
void Disk::AddDrivePage(map<int, vector<byte>>& pages, bool changeable) const
{
vector<byte> buf(24);
// No changeable area
if (changeable) {
pages[4] = buf;
return;
}
if (IsReady()) {
// Set the number of cylinders (total number of blocks
// divided by 25 sectors/track and 8 heads)
uint64_t cylinders = blocks;
cylinders >>= 3;
cylinders /= 25;
SetInt32(buf, 0x01, (uint32_t)cylinders);
// Fix the head at 8
buf[0x05] = (byte)0x8;
// Medium rotation rate 7200
SetInt16(buf, 0x14, 7200);
}
pages[4] = buf;
}
void Disk::AddCachePage(map<int, vector<byte>>& pages, bool changeable) const
{
vector<byte> buf(12);
// No changeable area
if (changeable) {
pages[8] = buf;
return;
}
// Only read cache is valid
// Disable pre-fetch transfer length
SetInt16(buf, 0x04, -1);
// Maximum pre-fetch
SetInt16(buf, 0x08, -1);
// Maximum pre-fetch ceiling
SetInt16(buf, 0x0a, -1);
pages[8] = buf;
}
void Disk::AddVendorPage(map<int, vector<byte>>&, int, bool) const
{
// Nothing to add by default
}
// TODO Read more than one block in a single call. Currently blocked by the the track-oriented cache
int Disk::Read(const vector<int>&, vector<BYTE>& buf, uint64_t block)
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
CheckReady();
// Error if the total number of blocks is exceeded
if (block >= blocks) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// leave it to the cache
if (!cache->ReadSector(buf, (uint32_t)block)) {
throw scsi_error_exception(sense_key::MEDIUM_ERROR, asc::READ_FAULT);
}
// Success
return 1 << size_shift_count;
}
int Disk::WriteCheck(uint64_t block)
{
CheckReady();
// Error if the total number of blocks is exceeded
if (block >= blocks) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// Error if write protected
if (IsProtected()) {
throw scsi_error_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
}
// Success
return 1 << size_shift_count;
}
// TODO Write more than one block in a single call. Currently blocked by the track-oriented cache
void Disk::Write(const vector<int>&, const vector<BYTE>& buf, uint64_t block)
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
// Error if not ready
if (!IsReady()) {
throw scsi_error_exception(sense_key::NOT_READY);
}
// Error if the total number of blocks is exceeded
if (block >= blocks) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
// Error if write protected
if (IsProtected()) {
throw scsi_error_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
}
// Leave it to the cache
if (!cache->WriteSector(buf, (uint32_t)block)) {
throw scsi_error_exception(sense_key::MEDIUM_ERROR, asc::WRITE_FAULT);
}
}
void Disk::Seek()
{
CheckReady();
EnterStatusPhase();
}
void Disk::Seek6()
{
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, SEEK6)) {
CheckReady();
}
EnterStatusPhase();
}
void Disk::Seek10()
{
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, SEEK10)) {
CheckReady();
}
EnterStatusPhase();
}
void Disk::ReadCapacity10()
{
CheckReady();
if (blocks == 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
}
vector<BYTE>& buf = controller->GetBuffer();
// Create end of logical block address (blocks-1)
uint64_t capacity = blocks - 1;
// If the capacity exceeds 32 bit, -1 must be returned and the client has to use READ CAPACITY(16)
if (capacity > 4294967295) {
capacity = -1;
}
SetInt32(buf, 0, (uint32_t)capacity);
// Create block length (1 << size)
SetInt32(buf, 4, 1 << size_shift_count);
// the size
ctrl->length = 8;
EnterDataInPhase();
}
void Disk::ReadCapacity16()
{
CheckReady();
if (blocks == 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
}
vector<BYTE>& buf = controller->GetBuffer();
// Create end of logical block address (blocks-1)
SetInt64(buf, 0, blocks - 1);
// Create block length (1 << size)
SetInt32(buf, 8, 1 << size_shift_count);
buf[12] = 0;
// Logical blocks per physical block: not reported (1 or more)
buf[13] = 0;
// the size
ctrl->length = 14;
EnterDataInPhase();
}
void Disk::ReadCapacity16_ReadLong16()
{
// The service action determines the actual command
switch (ctrl->cmd[1] & 0x1f) {
case 0x10:
ReadCapacity16();
break;
case 0x11:
ReadWriteLong16();
break;
default:
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
break;
}
}
//---------------------------------------------------------------------------
//
// RESERVE/RELEASE(6/10)
//
// The reserve/release commands are only used in multi-initiator
// environments. RaSCSI doesn't support this use case. However, some old
// versions of Solaris will issue the reserve/release commands. We will
// just respond with an OK status.
//
//---------------------------------------------------------------------------
void Disk::Reserve()
{
EnterStatusPhase();
}
void Disk::Release()
{
EnterStatusPhase();
}
//---------------------------------------------------------------------------
//
// Check/Get start sector and sector count for a READ/WRITE or READ/WRITE LONG operation
//
//---------------------------------------------------------------------------
void Disk::ValidateBlockAddress(access_mode mode) const
{
uint64_t block = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
uint64_t capacity = blocks;
if (block > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ to_string(block)).c_str())
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
}
bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mode mode) const
{
if (mode == RW6 || mode == SEEK6) {
start = GetInt24(ctrl->cmd, 1);
count = ctrl->cmd[4];
if (!count) {
count= 0x100;
}
}
else {
start = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
if (mode == RW16) {
count = GetInt32(ctrl->cmd, 10);
}
else if (mode != SEEK6 && mode != SEEK10) {
count = GetInt16(ctrl->cmd, 7);
}
else {
count = 0;
}
}
LOGTRACE("%s READ/WRITE/VERIFY/SEEK command record=$%08X blocks=%d", __PRETTY_FUNCTION__, (uint32_t)start, count)
// Check capacity
if (uint64_t capacity = blocks; start > capacity || start + count > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ to_string(start) + ", block count " + to_string(count)).c_str())
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
// Do not process 0 blocks
if (!count && (mode != SEEK6 && mode != SEEK10)) {
return false;
}
return true;
}
uint32_t Disk::GetSectorSizeInBytes() const
{
return size_shift_count ? 1 << size_shift_count : 0;
}
void Disk::SetSectorSizeInBytes(uint32_t size_in_bytes)
{
DeviceFactory device_factory;
if (unordered_set<uint32_t> sizes = device_factory.GetSectorSizes(GetType());
!sizes.empty() && sizes.find(size_in_bytes) == sizes.end()) {
throw io_exception("Invalid block size of " + to_string(size_in_bytes) + " bytes");
}
switch (size_in_bytes) {
case 512:
size_shift_count = 9;
break;
case 1024:
size_shift_count = 10;
break;
case 2048:
size_shift_count = 11;
break;
case 4096:
size_shift_count = 12;
break;
default:
throw io_exception("Invalid block size of " + to_string(size_in_bytes) + " bytes");
break;
}
}
uint32_t Disk::GetConfiguredSectorSize() const
{
return configured_sector_size;
}
bool Disk::SetConfiguredSectorSize(const DeviceFactory& device_factory, uint32_t configured_size)
{
if (unordered_set<uint32_t> sizes = device_factory.GetSectorSizes(GetType());
sizes.find(configured_size) == sizes.end()) {
return false;
}
configured_sector_size = configured_size;
return true;
}