2022-09-21 06:27:51 +00:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//
|
2022-12-05 17:58:23 +00:00
|
|
|
// SCSI Target Emulator PiSCSI
|
2022-09-21 06:27:51 +00:00
|
|
|
// for Raspberry Pi
|
|
|
|
//
|
|
|
|
// Copyright (C) 2022 Uwe Seimet
|
|
|
|
//
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
#include "mocks.h"
|
2022-11-10 06:44:06 +00:00
|
|
|
#include "shared/scsi.h"
|
2022-12-05 17:58:23 +00:00
|
|
|
#include "shared/piscsi_exceptions.h"
|
2022-09-21 06:27:51 +00:00
|
|
|
#include "devices/scsi_command_util.h"
|
|
|
|
|
2022-09-25 21:49:24 +00:00
|
|
|
using namespace scsi_command_util;
|
|
|
|
|
2022-10-10 06:16:47 +00:00
|
|
|
TEST(ScsiCommandUtilTest, ModeSelect6)
|
2022-10-08 17:26:04 +00:00
|
|
|
{
|
2022-10-10 06:16:47 +00:00
|
|
|
const int LENGTH = 26;
|
2022-10-08 17:26:04 +00:00
|
|
|
|
2022-10-10 06:16:47 +00:00
|
|
|
vector<int> cdb(6);
|
2022-11-02 06:36:25 +00:00
|
|
|
vector<uint8_t> buf(LENGTH);
|
2022-10-08 17:26:04 +00:00
|
|
|
|
2022-11-02 14:36:19 +00:00
|
|
|
// PF (vendor-specific parameter format) must not fail but be ignored
|
2022-10-08 17:26:04 +00:00
|
|
|
cdb[1] = 0x00;
|
2023-10-15 06:38:15 +00:00
|
|
|
ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 0);
|
2022-10-08 17:26:04 +00:00
|
|
|
|
2022-10-10 06:16:47 +00:00
|
|
|
cdb[0] = 0x15;
|
2022-10-08 17:26:04 +00:00
|
|
|
// PF (standard parameter format)
|
|
|
|
cdb[1] = 0x10;
|
|
|
|
// Request 512 bytes per sector
|
|
|
|
buf[9] = 0x00;
|
|
|
|
buf[10] = 0x02;
|
|
|
|
buf[11] = 0x00;
|
2023-10-15 06:38:15 +00:00
|
|
|
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 256); },
|
2022-11-11 20:08:48 +00:00
|
|
|
Throws<scsi_exception>(AllOf(
|
2023-10-15 06:38:15 +00:00
|
|
|
Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
|
|
|
|
Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list))))
|
2022-10-08 17:26:04 +00:00
|
|
|
<< "Requested sector size does not match current sector size";
|
|
|
|
|
|
|
|
// Page 0
|
2022-10-10 06:16:47 +00:00
|
|
|
buf[12] = 0x00;
|
2023-10-15 06:38:15 +00:00
|
|
|
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512); },
|
2022-11-11 20:08:48 +00:00
|
|
|
Throws<scsi_exception>(AllOf(
|
2023-10-15 06:38:15 +00:00
|
|
|
Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
|
|
|
|
Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list))))
|
2022-10-08 17:26:04 +00:00
|
|
|
<< "Unsupported page 0 was not rejected";
|
|
|
|
|
Multiple fixes for ModeSelect (#1405)
* 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>
2024-01-07 06:06:35 +00:00
|
|
|
// Page 1
|
|
|
|
buf[12] = 0x01;
|
|
|
|
EXPECT_NO_THROW(ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512))
|
|
|
|
<< "Page 1 is supported";
|
|
|
|
|
2022-10-08 17:26:04 +00:00
|
|
|
// Page 3 (Format Device Page)
|
2022-10-10 06:16:47 +00:00
|
|
|
buf[12] = 0x03;
|
2023-10-15 06:38:15 +00:00
|
|
|
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512); },
|
2022-11-11 20:08:48 +00:00
|
|
|
Throws<scsi_exception>(AllOf(
|
2023-10-15 06:38:15 +00:00
|
|
|
Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
|
|
|
|
Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list))))
|
2022-10-08 17:26:04 +00:00
|
|
|
<< "Requested sector size does not match current sector size";
|
|
|
|
|
|
|
|
// Match the requested to the current sector size
|
2022-10-10 06:16:47 +00:00
|
|
|
buf[24] = 0x02;
|
2023-10-15 06:38:15 +00:00
|
|
|
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH - 1, 512); },
|
2022-11-11 20:08:48 +00:00
|
|
|
Throws<scsi_exception>(AllOf(
|
2023-10-15 06:38:15 +00:00
|
|
|
Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
|
|
|
|
Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list))))
|
2022-10-10 06:16:47 +00:00
|
|
|
<< "Not enough command parameters";
|
|
|
|
|
Multiple fixes for ModeSelect (#1405)
* 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>
2024-01-07 06:06:35 +00:00
|
|
|
// check length computation
|
|
|
|
buf[3] = 8;
|
|
|
|
buf[10] = 2;
|
|
|
|
buf[12] = 1;
|
|
|
|
buf[13] = 10;
|
|
|
|
buf[14] = 0x24;
|
|
|
|
buf[24] = 0;
|
|
|
|
EXPECT_NO_THROW(ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512))
|
|
|
|
<< "Multi-page length computation";
|
|
|
|
|
|
|
|
// check length computation
|
|
|
|
buf[3] = 8;
|
|
|
|
buf[10] = 12;
|
|
|
|
buf[12] = 0;
|
|
|
|
buf[13] = 0;
|
|
|
|
buf[14] = 0;
|
|
|
|
buf[24] = 0;
|
|
|
|
EXPECT_NO_THROW(ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, 12, 512))
|
|
|
|
<< "Empty ModeSelect6";
|
2022-10-10 06:16:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ScsiCommandUtilTest, ModeSelect10)
|
|
|
|
{
|
|
|
|
const int LENGTH = 30;
|
|
|
|
|
|
|
|
vector<int> cdb(10);
|
2022-11-02 06:36:25 +00:00
|
|
|
vector<uint8_t> buf(LENGTH);
|
2022-10-10 06:16:47 +00:00
|
|
|
|
2022-11-02 14:36:19 +00:00
|
|
|
// PF (vendor-specific parameter format) must not fail but be ignored
|
2022-10-10 06:16:47 +00:00
|
|
|
cdb[1] = 0x00;
|
2023-10-15 06:38:15 +00:00
|
|
|
ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 0);
|
2022-10-10 06:16:47 +00:00
|
|
|
|
|
|
|
// PF (standard parameter format)
|
|
|
|
cdb[1] = 0x10;
|
|
|
|
// Request 512 bytes per sector
|
|
|
|
buf[13] = 0x00;
|
|
|
|
buf[14] = 0x02;
|
|
|
|
buf[15] = 0x00;
|
2023-10-15 06:38:15 +00:00
|
|
|
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 256); },
|
2022-11-11 20:08:48 +00:00
|
|
|
Throws<scsi_exception>(AllOf(
|
2023-10-15 06:38:15 +00:00
|
|
|
Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
|
|
|
|
Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list))))
|
2022-10-10 06:16:47 +00:00
|
|
|
<< "Requested sector size does not match current sector size";
|
|
|
|
|
|
|
|
// Page 0
|
|
|
|
buf[16] = 0x00;
|
2023-10-15 06:38:15 +00:00
|
|
|
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 512); },
|
2022-11-11 20:08:48 +00:00
|
|
|
Throws<scsi_exception>(AllOf(
|
2023-10-15 06:38:15 +00:00
|
|
|
Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
|
|
|
|
Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list))))
|
2022-10-10 06:16:47 +00:00
|
|
|
<< "Unsupported page 0 was not rejected";
|
|
|
|
|
|
|
|
// Page 3 (Format Device Page)
|
|
|
|
buf[16] = 0x03;
|
2023-10-15 06:38:15 +00:00
|
|
|
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 512); },
|
2022-11-11 20:08:48 +00:00
|
|
|
Throws<scsi_exception>(AllOf(
|
2023-10-15 06:38:15 +00:00
|
|
|
Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
|
|
|
|
Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list))))
|
2022-10-10 06:16:47 +00:00
|
|
|
<< "Requested sector size does not match current sector size";
|
|
|
|
|
|
|
|
// Match the requested to the current sector size
|
|
|
|
buf[28] = 0x02;
|
2023-10-15 06:38:15 +00:00
|
|
|
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH - 1, 512); },
|
2022-11-11 20:08:48 +00:00
|
|
|
Throws<scsi_exception>(AllOf(
|
2023-10-15 06:38:15 +00:00
|
|
|
Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
|
|
|
|
Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list))))
|
2022-10-10 06:16:47 +00:00
|
|
|
<< "Not enough command parameters";
|
2022-10-08 17:26:04 +00:00
|
|
|
}
|
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
TEST(ScsiCommandUtilTest, EnrichFormatPage)
|
|
|
|
{
|
|
|
|
const int SECTOR_SIZE = 512;
|
|
|
|
|
|
|
|
map<int, vector<byte>> pages;
|
|
|
|
vector<byte> format_page(24);
|
|
|
|
pages[3] = format_page;
|
|
|
|
|
2022-09-25 21:49:24 +00:00
|
|
|
EnrichFormatPage(pages, false, SECTOR_SIZE);
|
2022-09-21 06:27:51 +00:00
|
|
|
format_page = pages[3];
|
|
|
|
EXPECT_EQ(byte{0}, format_page[12]);
|
|
|
|
EXPECT_EQ(byte{0}, format_page[13]);
|
|
|
|
|
2022-09-25 21:49:24 +00:00
|
|
|
EnrichFormatPage(pages, true, SECTOR_SIZE);
|
2022-09-21 06:27:51 +00:00
|
|
|
format_page = pages[3];
|
|
|
|
EXPECT_EQ(byte{SECTOR_SIZE >> 8}, format_page[12]);
|
|
|
|
EXPECT_EQ(byte{0}, format_page[13]);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ScsiCommandUtilTest, AddAppleVendorModePage)
|
|
|
|
{
|
|
|
|
map<int, vector<byte>> pages;
|
|
|
|
vector<byte> vendor_page(30);
|
|
|
|
pages[48] = vendor_page;
|
|
|
|
|
2022-09-25 21:49:24 +00:00
|
|
|
AddAppleVendorModePage(pages, true);
|
2022-09-21 06:27:51 +00:00
|
|
|
vendor_page = pages[48];
|
|
|
|
EXPECT_EQ(byte{0}, vendor_page[2]);
|
|
|
|
|
2022-09-25 21:49:24 +00:00
|
|
|
AddAppleVendorModePage(pages, false);
|
2022-09-21 06:27:51 +00:00
|
|
|
vendor_page = pages[48];
|
|
|
|
EXPECT_STREQ("APPLE COMPUTER, INC ", (const char *)&vendor_page[2]);
|
|
|
|
}
|
2022-09-25 21:49:24 +00:00
|
|
|
|
|
|
|
TEST(ScsiCommandUtilTest, GetInt16)
|
|
|
|
{
|
2022-11-02 06:36:25 +00:00
|
|
|
vector<uint8_t> b = { 0xfe, 0xdc };
|
2022-10-10 06:16:47 +00:00
|
|
|
EXPECT_EQ(0xfedc, GetInt16(b, 0));
|
|
|
|
|
2022-09-25 21:49:24 +00:00
|
|
|
vector<int> v = { 0x12, 0x34 };
|
|
|
|
EXPECT_EQ(0x1234, GetInt16(v, 0));
|
|
|
|
}
|
|
|
|
|
2022-10-01 15:56:06 +00:00
|
|
|
TEST(ScsiCommandUtilTest, GetInt24)
|
|
|
|
{
|
|
|
|
vector<int> v = { 0x12, 0x34, 0x56 };
|
|
|
|
EXPECT_EQ(0x123456, GetInt24(v, 0));
|
|
|
|
}
|
|
|
|
|
2022-09-25 21:49:24 +00:00
|
|
|
TEST(ScsiCommandUtilTest, GetInt32)
|
|
|
|
{
|
|
|
|
vector<int> v = { 0x12, 0x34, 0x56, 0x78 };
|
|
|
|
EXPECT_EQ(0x12345678, GetInt32(v, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ScsiCommandUtilTest, GetInt64)
|
|
|
|
{
|
|
|
|
vector<int> v = { 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21 };
|
|
|
|
EXPECT_EQ(0x1234567887654321, GetInt64(v, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ScsiCommandUtilTest, SetInt16)
|
|
|
|
{
|
|
|
|
vector<byte> v(2);
|
|
|
|
SetInt16(v, 0, 0x1234);
|
|
|
|
EXPECT_EQ(byte{0x12}, v[0]);
|
|
|
|
EXPECT_EQ(byte{0x34}, v[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ScsiCommandUtilTest, SetInt32)
|
|
|
|
{
|
2022-11-02 06:36:25 +00:00
|
|
|
vector<uint8_t> buf(4);
|
2022-10-01 15:56:06 +00:00
|
|
|
SetInt32(buf, 0, 0x12345678);
|
2022-09-25 21:49:24 +00:00
|
|
|
EXPECT_EQ(0x12, buf[0]);
|
|
|
|
EXPECT_EQ(0x34, buf[1]);
|
|
|
|
EXPECT_EQ(0x56, buf[2]);
|
|
|
|
EXPECT_EQ(0x78, buf[3]);
|
|
|
|
|
|
|
|
vector<byte> v(4);
|
|
|
|
SetInt32(v, 0, 0x12345678);
|
|
|
|
EXPECT_EQ(byte{0x12}, v[0]);
|
|
|
|
EXPECT_EQ(byte{0x34}, v[1]);
|
|
|
|
EXPECT_EQ(byte{0x56}, v[2]);
|
|
|
|
EXPECT_EQ(byte{0x78}, v[3]);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ScsiCommandUtilTest, SetInt64)
|
|
|
|
{
|
2022-11-02 06:36:25 +00:00
|
|
|
vector<uint8_t> buf(8);
|
2022-10-01 15:56:06 +00:00
|
|
|
SetInt64(buf, 0, 0x1234567887654321);
|
2022-09-25 21:49:24 +00:00
|
|
|
EXPECT_EQ(0x12, buf[0]);
|
|
|
|
EXPECT_EQ(0x34, buf[1]);
|
|
|
|
EXPECT_EQ(0x56, buf[2]);
|
|
|
|
EXPECT_EQ(0x78, buf[3]);
|
|
|
|
EXPECT_EQ(0x87, buf[4]);
|
|
|
|
EXPECT_EQ(0x65, buf[5]);
|
|
|
|
EXPECT_EQ(0x43, buf[6]);
|
|
|
|
EXPECT_EQ(0x21, buf[7]);
|
|
|
|
}
|