mirror of
https://github.com/akuker/RASCSI.git
synced 2024-11-25 20:33:35 +00:00
ad5eae93e7
* Allow 'empty' ModeSelect6 tl;dr Treat a computed length of 0 as `has_valid_page_code`. Details: The SRM console (aka 'BIOS') of DEC Alpha sends an empty ModeSelect6 with the following data: ~~~ ModeSelect6, CDB $151000000c00 ~~~ That makes 12 byte(s) as follows ~~~ 0 1 2 3 4 5 6 7 8 9 10 11 00 00 00 08 00 00 00 00 00 00 02 00 ~~~ decoding it (accoring to [1], Section 8.3.3, Table 94) gives us Mode Data Length 0 Medium Type 0 Device-specific 0 Block desc len 8 Density Code 0 Number of blks 0 Reserved 0 Block length 512 `scsi_command_util::ModeSelect` computes ~~~ offset = 4 + buf[3]; ~~~ giving 12 and ~~~ length -= offset; ~~~ giving 0. Thus it never enters the `while` loop and `has_valid_page_code` stays `false`, raising an error. [1] [Small Computer System Interface - 2 rev 10L.pdf](https://dn790004.ca.archive.org/0/items/SCSISpecificationDocumentsSCSIDocuments/Small%20Computer%20System%20Interface%20-%202%20rev%2010L.pdf) Signed-off-by: Klaus Kämpf <kkaempf@gmail.com> * Allow ModeSelect with page code 1 OpenVMS Alpha (the operating system, not the SRM BIOS) uses ModeSelect6 with a page code of 1. The semantics are unknown, just accepting it works for me. Signed-off-by: Klaus Kämpf <kkaempf@gmail.com> * Fix page length computation in ModeSelect tl;dr The 'skip to next ModeSelect page' computation was off-by-one, either not taking the page code itself into account or missing the fact that the page length is given as `n - 1`. Fix: Add 1 to the computed length. Details: OpenVMS Alpha sends a ModeSelect6 as follows ~~~ command: ModeSelect6, CDB $151000001900 payload: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 00 00 00 08 00 00 00 00 00 00 02 00 01 0a 24 00 00 00 00 00 00 00 00 00 00 ~~~ This translates to (accoring to [1], Section 8.3.3) ~~~ Mode Data Length 0 Medium Type 0 Device-specific 0 Block desc len 8 ~~~ with the following offset / length computation _before_ the `while` loop ~~~ offset = 12 length = 13 ~~~ The first payload section is ~~~ 4 5 6 7 8 9 10 11 00 00 00 00 00 00 02 00 ~~~ translating to ~~~ Density Code 0 Number of blks 0 Reserved 0 Block length 0x200 512 ~~~ Then follows a pagecode 1 as ~~~ 12 13 14 15 16 17 18 19 20 21 22 23 24 01 0a 24 00 00 00 00 00 00 00 00 00 00 ~~~ translating to ~~~~ Page code 1 Page length -1 10 Mode parameters 24 00 00 00 00 00 00 00 00 00 00 ~~~ computing (inside the `while` loop, as `// Advance to the next page`) ~~~ size = 10 + 2 = 12 ~~~ followed by new `offset` and `length` values ~~~ offset = 25 length = 1 ~~~ So it stays in the `while` loop (and has a larger-than-buffer `offset` value) Signed-off-by: Klaus Kämpf <kkaempf@gmail.com>
150 lines
4.7 KiB
C++
150 lines
4.7 KiB
C++
//---------------------------------------------------------------------------
|
|
//
|
|
// SCSI Target Emulator PiSCSI
|
|
// for Raspberry Pi
|
|
//
|
|
// Copyright (C) 2022-2023 Uwe Seimet
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "shared/piscsi_exceptions.h"
|
|
#include "scsi_command_util.h"
|
|
#include <spdlog/spdlog.h>
|
|
#include <cstring>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
|
|
using namespace scsi_defs;
|
|
|
|
string scsi_command_util::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length, int sector_size)
|
|
{
|
|
assert(cmd == scsi_command::eCmdModeSelect6 || cmd == scsi_command::eCmdModeSelect10);
|
|
assert(length >= 0);
|
|
|
|
string result;
|
|
|
|
// PF
|
|
if (!(cdb[1] & 0x10)) {
|
|
// Vendor-specific parameters (SCSI-1) are not supported.
|
|
// Do not report an error in order to support Apple's HD SC Setup.
|
|
return result;
|
|
}
|
|
|
|
// Skip block descriptors
|
|
int offset;
|
|
if (cmd == scsi_command::eCmdModeSelect10) {
|
|
offset = 8 + GetInt16(buf, 6);
|
|
}
|
|
else {
|
|
offset = 4 + buf[3];
|
|
}
|
|
length -= offset;
|
|
|
|
// treat zero length as valid
|
|
bool has_valid_page_code = (length == 0);
|
|
|
|
// Parse the pages
|
|
while (length > 0) {
|
|
// Format device page
|
|
if (const int page = buf[offset]; page == 0x03) {
|
|
if (length < 14) {
|
|
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_parameter_list);
|
|
}
|
|
|
|
// With this page the sector size for a subsequent FORMAT can be selected, but only very few
|
|
// drives support this, e.g FUJITSU M2624S
|
|
// We are fine as long as the current sector size remains unchanged
|
|
if (GetInt16(buf, offset + 12) != sector_size) {
|
|
// With piscsi it is not possible to permanently (by formatting) change the sector size,
|
|
// because the size is an externally configurable setting only
|
|
spdlog::warn("In order to change the sector size use the -b option when launching piscsi");
|
|
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_parameter_list);
|
|
}
|
|
|
|
has_valid_page_code = true;
|
|
}
|
|
else if (page == 0x01) {
|
|
// OpenVMS Alpha 7.3 uses this
|
|
has_valid_page_code = true;
|
|
}
|
|
else {
|
|
stringstream s;
|
|
s << "Unknown MODE SELECT page code: $" << setfill('0') << setw(2) << hex << page;
|
|
result = s.str();
|
|
}
|
|
|
|
// Advance to the next page
|
|
const int size = buf[offset + 1] + 2;
|
|
|
|
length -= size + 1;
|
|
offset += size;
|
|
}
|
|
|
|
if (!has_valid_page_code) {
|
|
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_parameter_list);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void scsi_command_util::EnrichFormatPage(map<int, vector<byte>>& pages, bool changeable, int sector_size)
|
|
{
|
|
if (changeable) {
|
|
// The sector size is simulated to be changeable, see the MODE SELECT implementation for details
|
|
SetInt16(pages[3], 12, sector_size);
|
|
}
|
|
}
|
|
|
|
void scsi_command_util::AddAppleVendorModePage(map<int, vector<byte>>& pages, bool changeable)
|
|
{
|
|
// Page code 48 (30h) - Apple Vendor Mode Page
|
|
// Needed for SCCD for stock Apple driver support
|
|
// Needed for SCHD for stock Apple HD SC Setup
|
|
pages[48] = vector<byte>(30);
|
|
|
|
// No changeable area
|
|
if (!changeable) {
|
|
constexpr const char APPLE_DATA[] = "APPLE COMPUTER, INC ";
|
|
memcpy(&pages[48].data()[2], APPLE_DATA, sizeof(APPLE_DATA));
|
|
}
|
|
}
|
|
|
|
int scsi_command_util::GetInt24(span <const int> buf, int offset)
|
|
{
|
|
assert(buf.size() > static_cast<size_t>(offset) + 2);
|
|
|
|
return (buf[offset] << 16) | (buf[offset + 1] << 8) | buf[offset + 2];
|
|
}
|
|
|
|
uint32_t scsi_command_util::GetInt32(span <const int> buf, int offset)
|
|
{
|
|
assert(buf.size() > static_cast<size_t>(offset) + 3);
|
|
|
|
return (static_cast<uint32_t>(buf[offset]) << 24) | (static_cast<uint32_t>(buf[offset + 1]) << 16) |
|
|
(static_cast<uint32_t>(buf[offset + 2]) << 8) | static_cast<uint32_t>(buf[offset + 3]);
|
|
}
|
|
|
|
uint64_t scsi_command_util::GetInt64(span<const int> buf, int offset)
|
|
{
|
|
assert(buf.size() > static_cast<size_t>(offset) + 7);
|
|
|
|
return (static_cast<uint64_t>(buf[offset]) << 56) | (static_cast<uint64_t>(buf[offset + 1]) << 48) |
|
|
(static_cast<uint64_t>(buf[offset + 2]) << 40) | (static_cast<uint64_t>(buf[offset + 3]) << 32) |
|
|
(static_cast<uint64_t>(buf[offset + 4]) << 24) | (static_cast<uint64_t>(buf[offset + 5]) << 16) |
|
|
(static_cast<uint64_t>(buf[offset + 6]) << 8) | static_cast<uint64_t>(buf[offset + 7]);
|
|
}
|
|
|
|
void scsi_command_util::SetInt64(vector<uint8_t>& buf, int offset, uint64_t value)
|
|
{
|
|
assert(buf.size() > static_cast<size_t>(offset) + 7);
|
|
|
|
buf[offset] = static_cast<uint8_t>(value >> 56);
|
|
buf[offset + 1] = static_cast<uint8_t>(value >> 48);
|
|
buf[offset + 2] = static_cast<uint8_t>(value >> 40);
|
|
buf[offset + 3] = static_cast<uint8_t>(value >> 32);
|
|
buf[offset + 4] = static_cast<uint8_t>(value >> 24);
|
|
buf[offset + 5] = static_cast<uint8_t>(value >> 16);
|
|
buf[offset + 6] = static_cast<uint8_t>(value >> 8);
|
|
buf[offset + 7] = static_cast<uint8_t>(value);
|
|
}
|