Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add XGD support for Kreon Drives #181

Merged
merged 29 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a9c5b63
Add Support for dumping Xbox Security Sectors
tbejos Aug 2, 2024
4dc507a
Add Kreon "Set Lock State" Command
tbejos Aug 2, 2024
8713cd4
Unlock the Drive and Dump the XGD
tbejos Aug 2, 2024
b16c0c9
Zero out L1 Middle
tbejos Aug 5, 2024
95cb887
Dump L1 Video at end
tbejos Aug 5, 2024
b250aa4
Add Support for XDG2/3
tbejos Aug 5, 2024
7bf77e5
Fix Typos and Xbox 360 Dump Support
tbejos Aug 6, 2024
554d521
Cleanup and XGD3 support
tbejos Aug 7, 2024
091a376
Make variables const
tbejos Aug 9, 2024
dbb9caa
Refactor, move code into main loop
Deterous Aug 9, 2024
2e72289
Fix issue where skip regions overlap
tbejos Aug 11, 2024
e3d3ea0
Merge pull request #2 from Deterous/main
tbejos Aug 11, 2024
0ac21a7
Refactoring and style
tbejos Aug 12, 2024
2b93df0
Proper Kreon lock/unlock
Deterous Aug 13, 2024
09999fb
Try unlock Kreon at start
Deterous Aug 13, 2024
b169b21
Fix libata issue
tbejos Aug 16, 2024
174fefd
Don't check capacity when locking
Deterous Aug 16, 2024
f6a21fb
Merge pull request #3 from Deterous/main
tbejos Aug 16, 2024
3f8177c
Cleanup no-longer needed code, refactoring
tbejos Aug 16, 2024
51f1363
Fix state not being updated for skip regions
tbejos Aug 17, 2024
9361f67
Fix refine/verify not having skip regions or lock sector set
tbejos Aug 18, 2024
1faf9d1
Clean up security sector with ssv1/ssv2 fixes
tbejos Aug 18, 2024
6e527ae
Fix PFI not hashing correctly
tbejos Aug 18, 2024
e65c19a
Fix Style issues
tbejos Aug 18, 2024
11971f9
Use struct for xgd_get_type, rename .raw_ss to .security
tbejos Aug 19, 2024
dd81526
Pull Xbox SS dump loop out of cmd.ixx
tbejos Aug 23, 2024
5e2f986
Do not store cleaned SS, clean when refining
tbejos Oct 20, 2024
ad95833
Add initial XGD1 and XGD2 Security Sector structs
tbejos Oct 20, 2024
a3e4c7f
Merge remote-tracking branch 'upstream/main'
tbejos Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ target_sources(redumper
"utils/misc.ixx"
"utils/signal.ixx"
"utils/strings.ixx"
"utils/xbox.ixx"
"debug.ixx"
"dump.ixx"
"drive.ixx"
Expand Down
231 changes: 224 additions & 7 deletions dvd/dvd_dump.ixx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module;
#include <algorithm>
#include <array>
#include <filesystem>
#include <fstream>
#include <functional>
Expand All @@ -26,6 +27,7 @@ import utils.logger;
import utils.misc;
import utils.signal;
import utils.strings;
import utils.xbox;



Expand Down Expand Up @@ -374,9 +376,19 @@ export bool redumper_dump_dvd(Context &ctx, const Options &options, DumpMode dum
if(dump_mode == DumpMode::DUMP)
image_check_overwrite(options);

SPTD::Status status;

// unlock drive if Kreon firmware detected so we can identify XGD later
if(ctx.drive_config.vendor_specific.starts_with("KREON V1.00"))
{
status = cmd_kreon_set_lock_state(*ctx.sptd, KREON_LockState::WXRIPPER);
if(status.status_code)
LOG("warning: failed to unlock Kreon drive, SCSI ({})", SPTD::StatusMessage(status));
superg marked this conversation as resolved.
Show resolved Hide resolved
}

// get sectors count
uint32_t sector_last, block_length;
auto status = cmd_read_capacity(*ctx.sptd, sector_last, block_length, false, 0, false);
status = cmd_read_capacity(*ctx.sptd, sector_last, block_length, false, 0, false);
if(status.status_code)
throw_line("failed to read capacity, SCSI ({})", SPTD::StatusMessage(status));
if(block_length != FORM1_DATA_SIZE)
Expand All @@ -386,6 +398,12 @@ export bool redumper_dump_dvd(Context &ctx, const Options &options, DumpMode dum
auto readable_formats = get_readable_formats(*ctx.sptd, profile_is_bluray(ctx.current_profile));

bool trim_to_filesystem_size = false;

bool is_xbox = false;
std::vector<std::array<uint32_t, 2>> xbox_skip_ranges;
tbejos marked this conversation as resolved.
Show resolved Hide resolved
uint32_t xbox_lock_sector = 0;
uint32_t xbox_l1_video_shift = 0;

if(readable_formats.find(READ_DISC_STRUCTURE_Format::PHYSICAL) != readable_formats.end())
{
// function call changes rom flag if discrepancy is detected
Expand All @@ -412,11 +430,131 @@ export bool redumper_dump_dvd(Context &ctx, const Options &options, DumpMode dum
physical_sectors_count += get_layer_length(layer_descriptor);
}

// Kreon drives return incorrect sectors count
if(physical_sectors_count != sectors_count)
{
// Kreon PFI sector count is only for Video portion when XGD present
if(ctx.drive_config.vendor_specific.starts_with("KREON V1.00"))
is_xbox = true;
superg marked this conversation as resolved.
Show resolved Hide resolved
else
{
LOG("warning: READ_CAPACITY / PHYSICAL sectors count mismatch, using PHYSICAL");
sectors_count = physical_sectors_count;
}
}
}

uint32_t xbox_layer0_end_sector = 0;
if(is_xbox)
{
std::vector<uint8_t> security_sector(0x800);
superg marked this conversation as resolved.
Show resolved Hide resolved

status = cmd_kreon_get_security_sector(*ctx.sptd, security_sector);
if(status.status_code)
throw_line("failed to get security sectors, SCSI ({})", SPTD::StatusMessage(status));

// store security sector
if(dump_mode == DumpMode::DUMP)
write_vector(image_prefix + ".raw_ss", security_sector);
superg marked this conversation as resolved.
Show resolved Hide resolved

// validate security sector
XGD_Type xgd_type = get_xgd_type(security_sector);
if(xgd_type == XGD_Type::UNKNOWN)
{
LOG("warning: READ_CAPACITY / PHYSICAL sectors count mismatch, using PHYSICAL");
sectors_count = physical_sectors_count;
LOG("warning: Kreon Drive with malformed XGD detected, reverting to normal DVD mode");
LOG("");
is_xbox = false;
}

if(is_xbox && !physical_structures.empty())
{
LOG("Kreon Drive with XGD{} detected", (uint8_t)xgd_type);
LOG("");

std::vector<uint8_t> clean_ss = security_sector;
clean_xbox_security_sector(clean_ss);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of clean_ss here, is it just to make sure we refine the same disc or there is more?
Can't ".raw_ss" be used for that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .raw_ss is the security sector as received by the drive, this cleaned version is the one that is used for redump submissions as (I might get some details wrong here) my understanding is the security sector contains disc angle data, and if there was a new manufacturing run of a disc with the same data the security sector would have different angles in it. This cleaned version overwrites that angle data in order to make sure that we can check if the rest of the security sector is the same. @Deterous do you happen to know more details about this?

For the non-dump runs though we can check the .raw_ss if you think that would be better. The goal of doing the cleaned version for the comparison is if you have two of the same disc you would be able to verify that the contents of the disc are the same even if it has different angles in the security sector.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may has misinterpreted this, if you are referring to the variable itself, yes I think we can reuse the security_sector variable and don't need to copy to a new vector

Copy link
Contributor

@Deterous Deterous Aug 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reason for clean_ss is both for making sure we refine the same disc as you mentioned, and also for redump. The clean SS is the hash that redump puts in comments, and it will match the output of ss_sector_range (dumpers won't have to run that program anymore).
We had to put it in redumper dump code because we use it for checking same disc is being refined as you mentioned.
The .raw_ss contents differ between dumps of the same disc, the cleaning function is meant to remove the parts that differ.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so we have two things:

  1. raw security sector dump that will have different angle data for the discs with otherwise same dump data
  2. security sector dump that has angle data zeroed and redump is using hash of this file in the submission

Specifically, I don't like outputting both files with otherwise identical content when using redumper dump command. This command should always store only what was received from the drive in the most RAW formats.

Here's what I would like to do here:
(1) should be dumped as .security
(2) should be generated from (1) on refine in memory and compared to the one from disc to ensure we refine the same disc

Hash for the cleaned one should be printed in redumper info command from (1) when it detects xbox dump.

Optionally, (2) can be generated during redumper info using a command line option. We have a similar problem with PSX discs where we want to generate .sbi file for libcrypt games (generated from raw .subcode). I have to think what would be the proper implementation of this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So just to make sure I understand everything correctly:

(1) should be dumped as .security

  • Always save (1) for every dump

(2) should be generated from (1) on refine in memory and compared to the one from disc to ensure we refine the same disc

  • When doing a refine create (2) for the existing .security file and for the current disc to be able to do the comparison (but don't save anything to disc, only exists in memory for the given run)

Hash for the cleaned one should be printed in redumper info command from (1) when it detects xbox dump.

  • Create (2) during info to print hash (but don't save as file to disc)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When doing a refine create (2) for the existing .security file and for the current disc to be able to do the comparison (but don't save anything to disc, only exists in memory for the given run)

Yes, basically load .security file to memory, clear angle data, read security sector from the disc, clear it's angle data and compare the two.

Create (2) during info to print hash (but don't save as file to disc)

That should be the output of redumper info command that analyzes dump files and do not access the drive at all. If xbox dump is detected, read .security file, clear angle data in memory, print hash and that's it.

That should come as a separate PR after we're done with this one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay great, that is pretty straight forward. Looking at the code I have one more question.

Currently only one user has reported this happening (so I think it should be uncommon) but their drive sometimes fails to send the full SS sequence leading to only a partial SS being received, in the current code I save it as the .security and just log that it is incomplete but didn't perform the cleanup. Now that we are only outputting the .security file should I:

a) Continue to save it anyways
b) Save it but change the extension to something like .partial_security
c) Do NOT save it (should we attempt to continue the dump (theoretically should be fine) or force a failure)

note in all of these cases it would log that it was incomplete

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer (a). We can't always troubleshoot drive reliability so redumper dump does best effort. Warning about it being incomplete is nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the lack of response the past few months, I got caught up with some stuff and then never revisited this.

I believe I have updated the behavior to address everything so now it will:

  • Save "raw" SS as .security file
  • Clean SS to continue using but do not save to file
  • When refining (and not forcing) read saved .security and clean it so we can do a proper compare without worrying about faked angles

I re-tested with an XGD1 and XGD3 disc and both dumped correctly. I deleted some random portion of .state for the XGD1 after it completed and did a refine and it completed and the SHA matched (did a manual SHA-1 compute of the ISO after refine)


// TODO: when 0800 support added check for `v2`
auto security_sector_fn = std::format("{}.ss{}", image_prefix, xgd_type == XGD_Type::XGD1 ? "" : "v1");

if(dump_mode == DumpMode::DUMP)
{
// store cleaned security sector
write_vector(security_sector_fn, clean_ss);
}
else if(!options.force_refine)
{
// if not dumping, compare security sector to stored to make sure it's the same disc
if(!std::filesystem::exists(security_sector_fn) || read_vector(security_sector_fn) != clean_ss)
throw_line("disc / file security sector doesn't match, refining from a different disc?");
}

auto &structure = physical_structures.front();

if(structure.size() < sizeof(CMD_ParameterListHeader) + sizeof(READ_DVD_STRUCTURE_LayerDescriptor))
throw_line("invalid layer descriptor size (layer: 0)");

auto &pfi_layer_descriptor = (READ_DVD_STRUCTURE_LayerDescriptor &)structure[sizeof(CMD_ParameterListHeader)];

int32_t lba_first = sign_extend<24>(endian_swap(pfi_layer_descriptor.data_start_sector));
int32_t layer0_last = sign_extend<24>(endian_swap(pfi_layer_descriptor.layer0_end_sector));

uint32_t l1_video_start = layer0_last + 1 - lba_first;
uint32_t l1_video_length = get_layer_length(pfi_layer_descriptor) - l1_video_start;

auto &ss_layer_descriptor = (READ_DVD_STRUCTURE_LayerDescriptor &)security_sector[0];

int32_t ss_lba_first = sign_extend<24>(endian_swap(ss_layer_descriptor.data_start_sector));
int32_t ss_layer0_last = sign_extend<24>(endian_swap(ss_layer_descriptor.layer0_end_sector));

uint32_t l1_padding_length = ss_lba_first - layer0_last - 1;
if(xgd_type == XGD_Type::XGD3)
l1_padding_length += 4096;

// extract security sector ranges
bool is_xgd1 = (xgd_type == XGD_Type::XGD1);

const auto media_specific_offset = offsetof(READ_DVD_STRUCTURE_LayerDescriptor, media_specific);
uint8_t num_ss_regions = ss_layer_descriptor.media_specific[1632 - media_specific_offset];
// partial pre-compute of conversion to Layer 1
const uint32_t layer1_offset = (ss_layer0_last * 2) - 0x30000 + 1;

for(int ss_pos = 1633 - media_specific_offset, i = 0; i < num_ss_regions; ss_pos += 9, i++)
{
uint32_t start_psn = ((uint32_t)ss_layer_descriptor.media_specific[ss_pos + 3] << 16) | ((uint32_t)ss_layer_descriptor.media_specific[ss_pos + 4] << 8)
| (uint32_t)ss_layer_descriptor.media_specific[ss_pos + 5];
uint32_t end_psn = ((uint32_t)ss_layer_descriptor.media_specific[ss_pos + 6] << 16) | ((uint32_t)ss_layer_descriptor.media_specific[ss_pos + 7] << 8)
| (uint32_t)ss_layer_descriptor.media_specific[ss_pos + 8];
if((i < 8 && is_xgd1) || (i == 0 && !is_xgd1))
{
// Layer 0
xbox_skip_ranges.push_back({ start_psn - 0x30000, end_psn - 0x30000 });
}
else if((i < 16 && is_xgd1) || (i == 3 && !is_xgd1))
{
// Layer 1
xbox_skip_ranges.push_back({ layer1_offset - (start_psn ^ 0xFFFFFF), layer1_offset - (end_psn ^ 0xFFFFFF) });
}
}
superg marked this conversation as resolved.
Show resolved Hide resolved

// append L1 padding to ranges
xbox_skip_ranges.push_back({ sectors_count, sectors_count + l1_padding_length - 1 });

// sort the skip ranges
std::sort(xbox_skip_ranges.begin(), xbox_skip_ranges.end(), [](const std::array<uint32_t, 2> &a, const std::array<uint32_t, 2> &b) { return a[0] < b[0]; });

// add L1 padding to sectors count
sectors_count += l1_padding_length;

// must relock drive to read L1 video
xbox_lock_sector = sectors_count;
xbox_l1_video_shift = xbox_lock_sector - l1_video_start;

// add L1 video to sectors count
sectors_count += l1_video_length;

// overwrite physical structure with true layer0_last from SS, so that disc structure logging is correct
xbox_layer0_end_sector = ss_layer_descriptor.layer0_end_sector;
}
}

Expand All @@ -428,7 +566,7 @@ export bool redumper_dump_dvd(Context &ctx, const Options &options, DumpMode dum
for(uint32_t i = 0; i < physical_structures.size(); ++i)
{
std::vector<uint8_t> structure;
cmd_read_disc_structure(*ctx.sptd, structure, 0, 0, i, READ_DISC_STRUCTURE_Format::MANUFACTURER, 0);
status = cmd_read_disc_structure(*ctx.sptd, structure, 0, 0, i, READ_DISC_STRUCTURE_Format::MANUFACTURER, 0);
tbejos marked this conversation as resolved.
Show resolved Hide resolved
if(status.status_code)
throw_line("failed to read disc manufacturer structure, SCSI ({})", SPTD::StatusMessage(status));

Expand Down Expand Up @@ -502,7 +640,13 @@ export bool redumper_dump_dvd(Context &ctx, const Options &options, DumpMode dum
for(uint32_t i = 0; i < physical_structures.size(); ++i)
{
auto const &structure = physical_structures[i];
print_physical_structure((READ_DVD_STRUCTURE_LayerDescriptor &)structure[sizeof(CMD_ParameterListHeader)], i);
auto &pfi_layer_descriptor = (READ_DVD_STRUCTURE_LayerDescriptor &)structure[sizeof(CMD_ParameterListHeader)];

// overwrite physical structure with true layer0_last from SS, so that disc structure logging is correct
if(is_xbox)
pfi_layer_descriptor.layer0_end_sector = xbox_layer0_end_sector;

print_physical_structure(pfi_layer_descriptor, i);
}
LOG("");

Expand Down Expand Up @@ -557,7 +701,7 @@ export bool redumper_dump_dvd(Context &ctx, const Options &options, DumpMode dum
if(dump_mode == DumpMode::DUMP && readable_formats.find(READ_DISC_STRUCTURE_Format::COPYRIGHT) != readable_formats.end())
{
std::vector<uint8_t> copyright;
auto status = cmd_read_disc_structure(*ctx.sptd, copyright, 0, 0, 0, READ_DISC_STRUCTURE_Format::COPYRIGHT, 0);
status = cmd_read_disc_structure(*ctx.sptd, copyright, 0, 0, 0, READ_DISC_STRUCTURE_Format::COPYRIGHT, 0);
if(!status.status_code)
{
strip_response_header(copyright);
Expand Down Expand Up @@ -605,12 +749,72 @@ export bool redumper_dump_dvd(Context &ctx, const Options &options, DumpMode dum

SignalINT signal;

uint8_t skip_range_idx = 0;
bool kreon_locked = false;
for(uint32_t s = 0; s < sectors_count;)
{
bool increment = true;

uint32_t sectors_to_read = std::min(sectors_at_once, sectors_count - s);

if(is_xbox && !kreon_locked)
{
// skip xbox security sector ranges and L1 filler range
if(skip_range_idx < xbox_skip_ranges.size())
{
if(xbox_skip_ranges[skip_range_idx][0] <= s && s <= xbox_skip_ranges[skip_range_idx][1] + 1)
{
if(s == xbox_skip_ranges[skip_range_idx][1] + 1)
{
if(options.verbose)
LOG_R("skipped sectors: {}-{}", xbox_skip_ranges[skip_range_idx][0], xbox_skip_ranges[skip_range_idx][1]);

skip_range_idx++;
tbejos marked this conversation as resolved.
Show resolved Hide resolved
// skip any overlapping ranges we have already completed
while(skip_range_idx < xbox_skip_ranges.size() && s >= xbox_skip_ranges[skip_range_idx][1] + 1)
skip_range_idx++;

// if still in a security sector range do not allow later read to happen
if(skip_range_idx < xbox_skip_ranges.size() && xbox_skip_ranges[skip_range_idx][0] <= s)
continue;
}
else
{
// skip at most to the end of the security sector range
sectors_to_read = std::min(sectors_to_read, xbox_skip_ranges[skip_range_idx][1] + 1 - s);
progress_output(s, sectors_count, errors_scsi);

std::vector<uint8_t> zeroes(sectors_to_read * FORM1_DATA_SIZE);
write_entry(fs_iso, zeroes.data(), FORM1_DATA_SIZE, s, sectors_to_read, 0);
std::fill(file_state.begin(), file_state.end(), State::SUCCESS);
write_entry(fs_state, (uint8_t *)file_state.data(), sizeof(State), s, sectors_to_read, 0);

rom_entry.update(zeroes.data(), sectors_to_read * FORM1_DATA_SIZE);

s += sectors_to_read;
continue;
}
}
else
{
sectors_to_read = std::min(sectors_to_read, xbox_skip_ranges[skip_range_idx][0] - s);
}
}

// check if Kreon drive needs locking
if(s < xbox_lock_sector && s + sectors_to_read >= xbox_lock_sector)
sectors_to_read = std::min(sectors_to_read, xbox_lock_sector - s);
else if(s == xbox_lock_sector)
{
status = cmd_kreon_set_lock_state(*ctx.sptd, KREON_LockState::LOCKED);
if(status.status_code)
throw_line("failed to set lock state, SCSI ({})", SPTD::StatusMessage(status));
if(options.verbose)
LOG_R("locked kreon drive at sector: {}", s);
kreon_locked = true;
}
}

bool read = false;
if(dump_mode == DumpMode::DUMP)
{
Expand All @@ -634,7 +838,12 @@ export bool redumper_dump_dvd(Context &ctx, const Options &options, DumpMode dum
progress_output(s, sectors_count, errors_scsi);

std::vector<uint8_t> drive_data(sectors_at_once * FORM1_DATA_SIZE);
auto status = cmd_read(*ctx.sptd, drive_data.data(), FORM1_DATA_SIZE, s, sectors_to_read, dump_mode == DumpMode::REFINE && refine_counter);

uint32_t dump_sector = s;
if(kreon_locked)
dump_sector -= xbox_l1_video_shift;

status = cmd_read(*ctx.sptd, drive_data.data(), FORM1_DATA_SIZE, dump_sector, sectors_to_read, dump_mode == DumpMode::REFINE && refine_counter);

if(status.status_code)
{
Expand Down Expand Up @@ -735,6 +944,14 @@ export bool redumper_dump_dvd(Context &ctx, const Options &options, DumpMode dum
s += sectors_to_read;
}

if(is_xbox)
{
// re-unlock drive before returning
status = cmd_kreon_set_lock_state(*ctx.sptd, KREON_LockState::WXRIPPER);
if(status.status_code)
LOG("warning: failed to unlock drive at end of dump, SCSI ({})", SPTD::StatusMessage(status));
}
superg marked this conversation as resolved.
Show resolved Hide resolved

if(!signal.interrupt())
{
progress_output(sectors_count, sectors_count, errors_scsi);
Expand Down
51 changes: 51 additions & 0 deletions scsi/cmd.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -446,4 +446,55 @@ SPTD::Status cmd_get_configuration(SPTD &sptd)
return status;
}


export SPTD::Status cmd_kreon_get_security_sector(SPTD &sptd, std::vector<uint8_t> &response_data)
superg marked this conversation as resolved.
Show resolved Hide resolved
{
SPTD::Status status;

// AD 00 FF 02 FD FF FE 00 08 00 xx C0
CDB12_ReadDiscStructure cdb = {};
cdb.operation_code = (uint8_t)CDB_OperationCode::READ_DISC_STRUCTURE;
*(uint32_t *)cdb.address = endian_swap<uint32_t>(0xFF02FDFF);
cdb.layer_number = 0xFE;
*(uint16_t *)cdb.allocation_length = endian_swap<uint16_t>((uint16_t)response_data.size());
superg marked this conversation as resolved.
Show resolved Hide resolved
cdb.control = 0xC0;
superg marked this conversation as resolved.
Show resolved Hide resolved

const uint8_t kreon_ss_vals[4] = { 0x01, 0x03, 0x05, 0x07 };
for(auto ss_val : kreon_ss_vals)
{
cdb.reserved2 = ss_val;
status = sptd.sendCommand(&cdb, sizeof(cdb), response_data.data(), response_data.size());
if(status.status_code)
break;
}

return status;
}

export SPTD::Status cmd_kreon_set_lock_state(SPTD &sptd, KREON_LockState lock_state)
{
SPTD::Status status;

// FF 08 01 01 (Legacy)
// FF 08 01 11 xx
bool is_legacy = (lock_state == KREON_LockState::LEGACY);
uint8_t cdb[10] = {};
tbejos marked this conversation as resolved.
Show resolved Hide resolved
cdb[0] = 0xFF;
cdb[1] = 0x08;
cdb[2] = 0x01;
if(is_legacy)
{
cdb[3] = 0x01;
}
else
{
cdb[3] = 0x11;
cdb[4] = (uint8_t)lock_state;
}

status = sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0);

return status;
}

}
9 changes: 9 additions & 0 deletions scsi/mmc.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,15 @@ export enum class REPORT_KEY_KeyFormat : uint8_t
};


export enum class KREON_LockState : uint8_t
{
LOCKED,
XTREME,
WXRIPPER,
LEGACY
};


export struct CMD_ParameterListHeader
{
uint16_t data_length;
Expand Down
Loading