2020-08-28 14:18:02 +00:00
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
2022-12-05 17:58:23 +00:00
|
|
|
|
// SCSI Target Emulator PiSCSI
|
2020-08-28 14:18:02 +00:00
|
|
|
|
// for Raspberry Pi
|
|
|
|
|
//
|
|
|
|
|
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
|
|
|
|
// Copyright (C) 2014-2020 GIMONS
|
2021-03-08 01:29:30 +00:00
|
|
|
|
// Copyright (C) akuker
|
2020-08-28 14:18:02 +00:00
|
|
|
|
//
|
2022-10-08 17:26:04 +00:00
|
|
|
|
// Licensed under the BSD 3-Clause License.
|
2021-03-08 01:29:30 +00:00
|
|
|
|
// See LICENSE file in the project root folder.
|
2020-08-28 14:18:02 +00:00
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
2022-12-05 17:58:23 +00:00
|
|
|
|
#include "shared/piscsi_util.h"
|
|
|
|
|
#include "shared/piscsi_exceptions.h"
|
2022-09-25 21:49:24 +00:00
|
|
|
|
#include "scsi_command_util.h"
|
2022-10-25 08:29:57 +00:00
|
|
|
|
#include "scsihd_nec.h"
|
|
|
|
|
#include <fstream>
|
2022-09-25 21:49:24 +00:00
|
|
|
|
|
2022-12-05 17:58:23 +00:00
|
|
|
|
using namespace piscsi_util;
|
2022-09-25 21:49:24 +00:00
|
|
|
|
using namespace scsi_command_util;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2022-10-23 19:51:39 +00:00
|
|
|
|
void SCSIHD_NEC::Open()
|
2020-08-28 14:18:02 +00:00
|
|
|
|
{
|
2022-09-25 21:49:24 +00:00
|
|
|
|
assert(!IsReady());
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2022-10-23 19:51:39 +00:00
|
|
|
|
off_t size = GetFileSize();
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2022-10-25 08:29:57 +00:00
|
|
|
|
array<char, 512> root_sector;
|
|
|
|
|
ifstream in(GetFilename(), ios::binary);
|
|
|
|
|
in.read(root_sector.data(), root_sector.size());
|
2022-11-02 06:36:25 +00:00
|
|
|
|
if (!in.good() || size < static_cast<off_t>(root_sector.size())) {
|
2022-09-21 06:27:51 +00:00
|
|
|
|
throw io_exception("Can't read NEC hard disk file root sector");
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-13 16:41:21 +00:00
|
|
|
|
// Effective size must be a multiple of 512
|
2022-10-04 15:23:42 +00:00
|
|
|
|
size = (size / 512) * 512;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2021-07-26 18:33:36 +00:00
|
|
|
|
// Determine parameters by extension
|
2022-11-02 06:36:25 +00:00
|
|
|
|
const auto [image_size, sector_size] = SetParameters(root_sector, static_cast<int>(size));
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2022-11-02 06:36:25 +00:00
|
|
|
|
SetSectorSizeShiftCount(static_cast<uint32_t>(size));
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2022-10-01 15:56:06 +00:00
|
|
|
|
SetBlockCount(image_size >> GetSectorSizeShiftCount());
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2022-10-25 08:29:57 +00:00
|
|
|
|
FinalizeSetup(image_offset);
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-25 08:29:57 +00:00
|
|
|
|
pair<int, int> SCSIHD_NEC::SetParameters(const array<char, 512>& data, int size)
|
2020-08-28 14:18:02 +00:00
|
|
|
|
{
|
2022-11-02 06:36:25 +00:00
|
|
|
|
array<uint8_t, 512> root_sector = {};
|
2022-10-25 08:29:57 +00:00
|
|
|
|
memcpy(root_sector.data(), data.data(), root_sector.size());
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2022-10-23 19:51:39 +00:00
|
|
|
|
int image_size;
|
|
|
|
|
int sector_size;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2022-10-24 20:41:04 +00:00
|
|
|
|
// PC-9801-55 NEC compatible?
|
2022-10-25 08:29:57 +00:00
|
|
|
|
if (const string ext = GetExtensionLowerCase(GetFilename()); ext == "hdn") {
|
2022-10-23 19:51:39 +00:00
|
|
|
|
// Assuming sector size 512, number of sectors 25, number of heads 8 as default settings
|
|
|
|
|
image_offset = 0;
|
|
|
|
|
image_size = size;
|
|
|
|
|
sector_size = 512;
|
|
|
|
|
sectors = 25;
|
|
|
|
|
heads = 8;
|
|
|
|
|
cylinders = size >> 9;
|
|
|
|
|
cylinders >>= 3;
|
|
|
|
|
cylinders /= 25;
|
|
|
|
|
}
|
|
|
|
|
// Anex86 HD image?
|
2022-10-25 08:29:57 +00:00
|
|
|
|
else if (ext == "hdi") {
|
2022-10-23 19:51:39 +00:00
|
|
|
|
image_offset = GetInt32LittleEndian(&root_sector[8]);
|
|
|
|
|
image_size = GetInt32LittleEndian(&root_sector[12]);
|
|
|
|
|
sector_size = GetInt32LittleEndian(&root_sector[16]);
|
|
|
|
|
sectors = GetInt32LittleEndian(&root_sector[20]);
|
|
|
|
|
heads = GetInt32LittleEndian(&root_sector[24]);
|
|
|
|
|
cylinders = GetInt32LittleEndian(&root_sector[28]);
|
|
|
|
|
}
|
|
|
|
|
// T98Next HD image?
|
2022-10-25 08:29:57 +00:00
|
|
|
|
else if (ext == "nhd") {
|
2022-10-23 19:51:39 +00:00
|
|
|
|
if (!memcmp(root_sector.data(), "T98HDDIMAGE.R0\0", 15)) {
|
|
|
|
|
image_offset = GetInt32LittleEndian(&root_sector[0x110]);
|
|
|
|
|
cylinders = GetInt32LittleEndian(&root_sector[0x114]);
|
|
|
|
|
heads = GetInt16LittleEndian(&root_sector[0x118]);
|
|
|
|
|
sectors = GetInt16LittleEndian(&root_sector[0x11a]);
|
|
|
|
|
sector_size = GetInt16LittleEndian(&root_sector[0x11c]);
|
2022-11-02 06:36:25 +00:00
|
|
|
|
image_size = static_cast<int>(static_cast<off_t>(cylinders * heads * sectors * sector_size));
|
2022-10-23 19:51:39 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
throw io_exception("Invalid NEC image file format");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
throw io_exception("Invalid NEC image file extension");
|
|
|
|
|
}
|
2022-02-27 21:58:01 +00:00
|
|
|
|
|
2022-10-25 08:29:57 +00:00
|
|
|
|
if (sector_size == 0) {
|
|
|
|
|
throw io_exception("Invalid NEC sector size 0");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Image size consistency check
|
|
|
|
|
if (image_offset + image_size > size) {
|
|
|
|
|
throw io_exception("NEC image offset/size consistency check failed");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CalculateShiftCount(sector_size) == 0) {
|
|
|
|
|
throw io_exception("Invalid NEC sector size of " + to_string(sector_size) + " byte(s)");
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-23 19:51:39 +00:00
|
|
|
|
return make_pair(image_size, sector_size);
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 14:36:19 +00:00
|
|
|
|
vector<uint8_t> SCSIHD_NEC::InquiryInternal() const
|
2022-10-23 19:51:39 +00:00
|
|
|
|
{
|
|
|
|
|
return HandleInquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_1_CCS, false);
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
|
void SCSIHD_NEC::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) const
|
2020-08-28 14:18:02 +00:00
|
|
|
|
{
|
2022-09-21 06:27:51 +00:00
|
|
|
|
vector<byte> buf(24);
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2022-02-27 21:58:01 +00:00
|
|
|
|
// Page can be saved
|
2022-09-21 06:27:51 +00:00
|
|
|
|
buf[0] = (byte)0x80;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2021-07-26 18:33:36 +00:00
|
|
|
|
// Make the number of bytes in the physical sector appear mutable (although it cannot actually be)
|
2022-02-27 21:58:01 +00:00
|
|
|
|
if (changeable) {
|
2022-09-25 21:49:24 +00:00
|
|
|
|
SetInt16(buf, 0x0c, -1);
|
2022-02-27 21:58:01 +00:00
|
|
|
|
|
|
|
|
|
pages[3] = buf;
|
|
|
|
|
|
|
|
|
|
return;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
Refactoring, device handling extensions, additional settings, improved error handling, 64 bit OS support, fixed issues (#184)
* Device type unification, support of removable media
* Added support for .hdr extension
* Removable flag cleanup
* Manpage update
* Enriched PbOperation with PbDevice
* Added file size to PbImageFile
* Added device list support
* Set image_file
* Make remote interface more robust by ignoring SIGPIPE
* Return status only once
* Fixed typo
* Error handling update
* When starting rascsi parse everything before attaching devices
* Added dry run mode
* Comment update
* Updated logging
* Added Device base class, Disk class inherits from it
* Renaming
* Use vectors for controllers and disks, as preparation for using maps
* Updated file support handling
* Comment update
* DaynaPort and Bridge inherit from Device instead of Disk
* ProcessCmd() now works with devices instead of disks
* Renaming
* Added DeviceFactory
* Improved factory
* Comment update
* protected disk_t
* Code cleanup, added translations
* Device name can be set for rascsi
* rasctl can set device name
* Manpage update
* Manpage update
* Formatting update
* Check for missing name
* Initialize fd
* Initialize type
* Fixed string length issue
* Updated capacity formatting
* Fixed typo
* Split PbDevice into device and device definition
* Added TODO
* Renaming
* Renaming
* Device types can be explicitly specified with -t (no FILE:TYPE syntax anymore)
* Fixed compile-time issue
* Removed unused Append mode, updated read-only handling
* Type handling and manpage update
* Cleanup
* rasctl parser cleanup
* Review
* Constructor update
* Added .hdr (SCRM) support to web interface, tested web interface
* Default folder can be set remotely
* Removed deprecated operation
* DETACH supports all parameters in order to detach all devices
* include cleanup
* Logging should not depend on NDEBUG, for RaSCSI it is not peformance-critical
* INFO is default log level
* Exception renaming
* Updated GetPaddedName()
* Inheritance update
* Added BlockDevice class
* Removed unused code
* Updated typedefs
* Revert "Updated typedefs"
This reverts commit 546b46215a4d9b65067a11698e59ab1123cc6d64.
* Removed unused code
* Fixed warnign
* Use standard C++ integer types, use streams to resolve printf data type issues
* Added TODOs
* Added TODO
* Renaming
* Added TODO
* Added TODO
* Improved dry-run
* Code cleanup
* Updated handling of unknown options, code review and cleanup
* Manpage update
* Added PrimaryDevice
* Include cleanup
* Added pure virtual methods
* Comment updates
* Split rasutil
* Replaced some occurrences of BOOL
* Removed obsolete RASCSI definition in xm6.h
* Removed unused code, updated TODOs, replaced BOOL
* Added capacity check (issue #192)
* Fixed (most likely) https://github.com/akuker/RASCSI/issues/191
* Fixed wrong error messages
* For root the default image folder is /home/pi/images, updated error handling
* Dynaport code review
* Improved error handling
* Implemented READ CAPACITY(16)
* Comment update
* Commands can be 16 bytes long
* Implemented READ/WRITE/VERIFY(16)
* Comment update
* Renamed method to reflect the name of the respective SCSI command
* Do not created devices during dryRun
* Fixed padding of SCSIHD_APPLE vendor and product
* Initial implementation
* Updated ReportLuns
* Byte count update
* Fixed typo
* Finalized REPORT LUNS
* Removed TODO
* Updated TODO
* TODO update
* Updated device factory
* Comment update
* 64 bit update, tested on Ubuntu 64 bit system
* Removed assertion
* SCSI hard disks always have Apple specific mode pages (resolves issue #193)
* Error messsage update, 64 bit cleanup
* Reduced streams usage
* Updated handling of device flags
* MOs are protectable
* Removed duplicate error code handling
* Removed duplicate code
* Fixed CmdReadToc buffer overflow (https://github.com/akuker/RASCSI/issues/194)
* Added naive implementation of GET EVENT STATUS NOTIFICATION to avoid wranings
* HD must set removable device bit if the media is removable
* Removed duplicate logging
* Updated daynaport additional length
* Removed broken daynaport REQUEST SENSE. Successfully tested with my Mac.
* EnableInterface should not always return TRUE
* Updated Inquiry
* Updated LUN handling
* Replaced incorrect free by delete
* Updated comments and write-protection handling
* Made default HD name consistent
* STATUS_NOERROR is default
* Fixed Eject
* More eject handling updates
* Manpage updates
* Logging update
* Changed debug level
* Logging update
* Log capacity of all media types
* Logging update
* Encapsulated disk.blocks
* Encapsulated sector size
* Added overrides
* Added more overrides
* Fixed error message
* Fixed typos
* Fixed logging
* Added logging
* Use PrimaryDevice when calling Inquiry
* Comment update
* Changed default buffer size for testing
* Reverted last change
* Removed debug output
* De-inlined methods because optimized code did not work with them inlined
* Web interface can attach Daynaport again
* Improved handling of read-only hard disks
* Fixed issue with "all" semantics of DETACH
* rasctl supports adding removable media devices without providing a filename
* Removed unused flag in PbDeviceDefinition
* Updated rasctl output for ecjected media (resolves issue #199)
* Validate default folder name when changing default folder
2021-08-21 21:45:30 +00:00
|
|
|
|
if (IsReady()) {
|
2021-07-26 18:33:36 +00:00
|
|
|
|
// Set the number of tracks in one zone (PC-9801-55 seems to see this value)
|
2022-09-25 21:49:24 +00:00
|
|
|
|
SetInt16(buf, 0x02, heads);
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2021-07-26 18:33:36 +00:00
|
|
|
|
// Set the number of sectors per track
|
2022-09-25 21:49:24 +00:00
|
|
|
|
SetInt16(buf, 0x0a, sectors);
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2021-07-26 18:33:36 +00:00
|
|
|
|
// Set the number of bytes in the physical sector
|
2022-10-01 15:56:06 +00:00
|
|
|
|
SetInt16(buf, 0x0c, GetSectorSizeInBytes());
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-26 18:33:36 +00:00
|
|
|
|
// Set removable attributes (remains of the old days)
|
Refactoring, device handling extensions, additional settings, improved error handling, 64 bit OS support, fixed issues (#184)
* Device type unification, support of removable media
* Added support for .hdr extension
* Removable flag cleanup
* Manpage update
* Enriched PbOperation with PbDevice
* Added file size to PbImageFile
* Added device list support
* Set image_file
* Make remote interface more robust by ignoring SIGPIPE
* Return status only once
* Fixed typo
* Error handling update
* When starting rascsi parse everything before attaching devices
* Added dry run mode
* Comment update
* Updated logging
* Added Device base class, Disk class inherits from it
* Renaming
* Use vectors for controllers and disks, as preparation for using maps
* Updated file support handling
* Comment update
* DaynaPort and Bridge inherit from Device instead of Disk
* ProcessCmd() now works with devices instead of disks
* Renaming
* Added DeviceFactory
* Improved factory
* Comment update
* protected disk_t
* Code cleanup, added translations
* Device name can be set for rascsi
* rasctl can set device name
* Manpage update
* Manpage update
* Formatting update
* Check for missing name
* Initialize fd
* Initialize type
* Fixed string length issue
* Updated capacity formatting
* Fixed typo
* Split PbDevice into device and device definition
* Added TODO
* Renaming
* Renaming
* Device types can be explicitly specified with -t (no FILE:TYPE syntax anymore)
* Fixed compile-time issue
* Removed unused Append mode, updated read-only handling
* Type handling and manpage update
* Cleanup
* rasctl parser cleanup
* Review
* Constructor update
* Added .hdr (SCRM) support to web interface, tested web interface
* Default folder can be set remotely
* Removed deprecated operation
* DETACH supports all parameters in order to detach all devices
* include cleanup
* Logging should not depend on NDEBUG, for RaSCSI it is not peformance-critical
* INFO is default log level
* Exception renaming
* Updated GetPaddedName()
* Inheritance update
* Added BlockDevice class
* Removed unused code
* Updated typedefs
* Revert "Updated typedefs"
This reverts commit 546b46215a4d9b65067a11698e59ab1123cc6d64.
* Removed unused code
* Fixed warnign
* Use standard C++ integer types, use streams to resolve printf data type issues
* Added TODOs
* Added TODO
* Renaming
* Added TODO
* Added TODO
* Improved dry-run
* Code cleanup
* Updated handling of unknown options, code review and cleanup
* Manpage update
* Added PrimaryDevice
* Include cleanup
* Added pure virtual methods
* Comment updates
* Split rasutil
* Replaced some occurrences of BOOL
* Removed obsolete RASCSI definition in xm6.h
* Removed unused code, updated TODOs, replaced BOOL
* Added capacity check (issue #192)
* Fixed (most likely) https://github.com/akuker/RASCSI/issues/191
* Fixed wrong error messages
* For root the default image folder is /home/pi/images, updated error handling
* Dynaport code review
* Improved error handling
* Implemented READ CAPACITY(16)
* Comment update
* Commands can be 16 bytes long
* Implemented READ/WRITE/VERIFY(16)
* Comment update
* Renamed method to reflect the name of the respective SCSI command
* Do not created devices during dryRun
* Fixed padding of SCSIHD_APPLE vendor and product
* Initial implementation
* Updated ReportLuns
* Byte count update
* Fixed typo
* Finalized REPORT LUNS
* Removed TODO
* Updated TODO
* TODO update
* Updated device factory
* Comment update
* 64 bit update, tested on Ubuntu 64 bit system
* Removed assertion
* SCSI hard disks always have Apple specific mode pages (resolves issue #193)
* Error messsage update, 64 bit cleanup
* Reduced streams usage
* Updated handling of device flags
* MOs are protectable
* Removed duplicate error code handling
* Removed duplicate code
* Fixed CmdReadToc buffer overflow (https://github.com/akuker/RASCSI/issues/194)
* Added naive implementation of GET EVENT STATUS NOTIFICATION to avoid wranings
* HD must set removable device bit if the media is removable
* Removed duplicate logging
* Updated daynaport additional length
* Removed broken daynaport REQUEST SENSE. Successfully tested with my Mac.
* EnableInterface should not always return TRUE
* Updated Inquiry
* Updated LUN handling
* Replaced incorrect free by delete
* Updated comments and write-protection handling
* Made default HD name consistent
* STATUS_NOERROR is default
* Fixed Eject
* More eject handling updates
* Manpage updates
* Logging update
* Changed debug level
* Logging update
* Log capacity of all media types
* Logging update
* Encapsulated disk.blocks
* Encapsulated sector size
* Added overrides
* Added more overrides
* Fixed error message
* Fixed typos
* Fixed logging
* Added logging
* Use PrimaryDevice when calling Inquiry
* Comment update
* Changed default buffer size for testing
* Reverted last change
* Removed debug output
* De-inlined methods because optimized code did not work with them inlined
* Web interface can attach Daynaport again
* Improved handling of read-only hard disks
* Fixed issue with "all" semantics of DETACH
* rasctl supports adding removable media devices without providing a filename
* Removed unused flag in PbDeviceDefinition
* Updated rasctl output for ecjected media (resolves issue #199)
* Validate default folder name when changing default folder
2021-08-21 21:45:30 +00:00
|
|
|
|
if (IsRemovable()) {
|
2022-09-21 06:27:51 +00:00
|
|
|
|
buf[20] = (byte)0x20;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-27 21:58:01 +00:00
|
|
|
|
pages[3] = buf;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
|
void SCSIHD_NEC::AddDrivePage(map<int, vector<byte>>& pages, bool changeable) const
|
2020-08-28 14:18:02 +00:00
|
|
|
|
{
|
2022-09-21 06:27:51 +00:00
|
|
|
|
vector<byte> buf(20);
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
|
|
|
|
// No changeable area
|
2022-02-27 21:58:01 +00:00
|
|
|
|
if (!changeable && IsReady()) {
|
2021-07-26 18:33:36 +00:00
|
|
|
|
// Set the number of cylinders
|
2022-09-25 21:49:24 +00:00
|
|
|
|
SetInt32(buf, 0x01, cylinders);
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
2021-07-26 18:33:36 +00:00
|
|
|
|
// Set the number of heads
|
2022-09-21 06:27:51 +00:00
|
|
|
|
buf[0x5] = (byte)heads;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-27 21:58:01 +00:00
|
|
|
|
pages[4] = buf;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
2022-10-23 19:51:39 +00:00
|
|
|
|
|
2022-11-02 06:36:25 +00:00
|
|
|
|
int SCSIHD_NEC::GetInt16LittleEndian(const uint8_t *buf)
|
2022-10-23 19:51:39 +00:00
|
|
|
|
{
|
2022-11-02 06:36:25 +00:00
|
|
|
|
return (static_cast<int>(buf[1]) << 8) | buf[0];
|
2022-10-23 19:51:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 06:36:25 +00:00
|
|
|
|
int SCSIHD_NEC::GetInt32LittleEndian(const uint8_t *buf)
|
2022-10-23 19:51:39 +00:00
|
|
|
|
{
|
2022-11-02 06:36:25 +00:00
|
|
|
|
return (static_cast<int>(buf[3]) << 24) | (static_cast<int>(buf[2]) << 16) | (static_cast<int>(buf[1]) << 8) | buf[0];
|
2022-10-23 19:51:39 +00:00
|
|
|
|
}
|