diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 7bde0593..f60dda4d 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -110,24 +110,37 @@ jobs: needs: check strategy: matrix: - arch: [x64, arm64] - runs-on: macos-14 + include: + - arch: x64 + os: macos-15-intel + host_arch: x86_64 + - arch: arm64 + os: macos-15 + host_arch: arm64 + runs-on: ${{matrix.os}} steps: - - name: Customize Software + - name: Verify Runner Toolchain + shell: bash run: | - brew install llvm@18 - - name: Customize Software - if: ${{ matrix.arch == 'x64' }} - run: | - softwareupdate --install-rosetta --agree-to-license - arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - arch -x86_64 /usr/local/bin/brew install llvm@18 + actual_arch="$(uname -m)" + echo "Host architecture: ${actual_arch}" + test "${actual_arch}" = "${{matrix.host_arch}}" + + llvm_root="$(brew --prefix llvm@18)" + echo "LLVM root: ${llvm_root}" + test -x "${llvm_root}/bin/clang++" - name: Checkout uses: actions/checkout@v6 - name: CMake Configure run: cmake -B ${{github.workspace}}/BUILD -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DREDUMPER_VERSION_BUILD=${{needs.check.outputs.build_number}} -DCMAKE_TOOLCHAIN_FILE="cmake/toolchains/macos-${{matrix.arch}}.cmake" - name: CMake Build run: cmake --build ${{github.workspace}}/BUILD --config Release + - name: Verify Architecture + shell: bash + run: | + binary_arch="$(lipo -archs "${{github.workspace}}/BUILD/redumper")" + echo "Binary architecture: ${binary_arch}" + test "${binary_arch}" = "${{matrix.host_arch}}" - name: CTest working-directory: ${{github.workspace}}/BUILD run: ctest -C Release --output-on-failure --verbose diff --git a/cd/cd_common.ixx b/cd/cd_common.ixx index 9d246f87..9a68b1a8 100644 --- a/cd/cd_common.ixx +++ b/cd/cd_common.ixx @@ -468,7 +468,7 @@ export SPTD::Status read_sector(SPTD &sptd, uint8_t *sector, bool &unscrambled, if(drive_config.read_method == ReadMethod::D8) { auto sub_code = drive_config.sector_order == SectorOrder::DATA_SUB ? READ_CDDA_SubCode::DATA_SUB : READ_CDDA_SubCode::DATA_C2_SUB; - status = cmd_read_cdda(sptd, sector_buffer.data(), CD_RAW_DATA_SIZE, lba, sectors_count, sub_code); + status = cmd_read_cdda(sptd, sector_buffer.data(), layout.size, lba, sectors_count, sub_code); } else { diff --git a/cmake/toolchains/macos-x64.cmake b/cmake/toolchains/macos-x64.cmake index 63fd243f..874a2bab 100644 --- a/cmake/toolchains/macos-x64.cmake +++ b/cmake/toolchains/macos-x64.cmake @@ -1,4 +1,4 @@ - # cross-compiling for x64 using native system compiler +# native x64 compilation execute_process(COMMAND brew --prefix llvm@18 OUTPUT_VARIABLE LLVM_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE) set(CMAKE_SYSTEM_NAME Darwin) @@ -9,9 +9,8 @@ set(CMAKE_PREFIX_PATH "${LLVM_ROOT}") set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0") execute_process(COMMAND xcrun --sdk macosx --show-sdk-path OUTPUT_VARIABLE CMAKE_OSX_SYSROOT OUTPUT_STRIP_TRAILING_WHITESPACE) - # use x64 LLVM libc++ instead of system, arch -x86_64 ensures x64 libs -execute_process(COMMAND arch -x86_64 /usr/local/bin/brew --prefix llvm@18 OUTPUT_VARIABLE LLVM_X64_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE) -set(CMAKE_EXE_LINKER_FLAGS_INIT "-L${LLVM_X64_ROOT}/lib/c++") +# use x64 LLVM libc++ instead of system +set(CMAKE_EXE_LINKER_FLAGS_INIT "-L${LLVM_ROOT}/lib/c++") # used in CMakeLists.txt to bundle libc++ with the application -set(LLVM_LIB_PATH "${LLVM_X64_ROOT}/lib" CACHE PATH "Path to LLVM libraries") +set(LLVM_LIB_PATH "${LLVM_ROOT}/lib" CACHE PATH "Path to LLVM libraries") diff --git a/debug.ixx b/debug.ixx index 89ef7f01..abd7db6f 100644 --- a/debug.ixx +++ b/debug.ixx @@ -124,6 +124,95 @@ export int redumper_debug(Context &ctx, Options &options) std::filesystem::path physical_path(image_prefix + ".physical"); std::filesystem::path sub_path(image_prefix + ".subcode"); + if(1) + { + SPTD sptd(options.drive, options.scsi_timeout); + + auto drive_query = cmd_drive_query(sptd); + bool monitor_mode = drive_query.vendor_id == "PLEXTOR" && drive_query.product_id == "MONT"; + bool firmware_mode = drive_query.vendor_id == "PLEXTOR" && drive_query.product_id.find("PX-W5224A") != std::string::npos; + + LOG("Plextor boot-state probe"); + LOG(" inquiry: {} {} {}", drive_query.vendor_id, drive_query.product_id, drive_query.product_revision_level); + LOG(" active mode: {}", monitor_mode ? "recovery monitor" : firmware_mode ? "normal firmware" : "unknown"); + + uint8_t port_mask = 0; + for(uint8_t mode = 0; mode <= 4; ++mode) + { + uint8_t cdb[12] = {}; + cdb[0] = 0xde; + cdb[1] = mode; + + uint8_t response[2] = {}; + if(auto status = sptd.sendCommand(cdb, sizeof(cdb), response, sizeof(response)).first; status.status_code) + throw_line("DE mode {} probe failed, SCSI ({})", mode, SPTD::StatusMessage(status)); + + LOG(" DE mode {}: {:02x} {:02x} ({})", mode, response[0], response[1], (uint8_t)(response[0] ^ response[1]) == 0xff ? "complement valid" : "complement invalid"); + if(mode == 0) + port_mask = response[0]; + } + + LOG(" P0.1: {}", port_mask & 0x01 ? "low" : "high"); + LOG(" P0.2: {}", port_mask & 0x02 ? "low" : "high"); + LOG(" P0.6: {}", port_mask & 0x04 ? "low" : "high"); + LOG(" P0.0: {}", port_mask & 0x08 ? "low" : "high"); + LOG(" P0.5: {}", port_mask & 0x10 ? "low" : "high"); + + uint8_t cdb[12] = {}; + cdb[0] = 0xf5; + cdb[5] = 0xff; + cdb[6] = 0xc0; + cdb[7] = 0x1e; + cdb[9] = 2; + + uint8_t configuration[2] = {}; + if(auto status = sptd.sendCommand(cdb, sizeof(cdb), configuration, sizeof(configuration)).first; status.status_code) + throw_line("F5 configuration read failed, SCSI ({})", SPTD::StatusMessage(status)); + + uint8_t configuration_value = configuration[1]; + uint32_t low_count = std::popcount((uint32_t)(port_mask & 0x07)); + bool visible_recovery_gates = low_count < 2 && ((port_mask & 0x04) == 0 || configuration_value != 0) && configuration_value != 0x11 && configuration_value != 0x12; + + LOG(" configuration entry 0x0f at 0x00ffc01e: {:02x} {:02x}", configuration[0], configuration[1]); + LOG(" forced-recovery gates excluding P1.6: {}", visible_recovery_gates ? "pass" : "fail"); + LOG(" P1.6: not observable through DE/F5"); + LOG(""); + } + + if(0) + { + SPTD sptd(options.drive, options.scsi_timeout); + + auto read_memory = [&sptd](uint32_t address, uint16_t length) + { + uint8_t cdb[12] = {}; + cdb[0] = 0xf5; + cdb[4] = (uint8_t)(address >> 24); + cdb[5] = (uint8_t)(address >> 16); + cdb[6] = (uint8_t)(address >> 8); + cdb[7] = (uint8_t)address; + cdb[8] = (uint8_t)(length >> 8); + cdb[9] = (uint8_t)length; + + std::vector data(length); + if(auto status = sptd.sendCommand(cdb, sizeof(cdb), data.data(), data.size()).first; status.status_code) + throw_line("F5 memory read failed at 0x{:08x}, SCSI ({})", address, SPTD::StatusMessage(status)); + + return data; + }; + + std::vector high_memory; + high_memory.reserve(0x10000); + for(uint32_t address = 0x00ff0000; address < 0x01000000; address += 0x4000) + { + LOG("reading memory at 0x{:08x}", address); + auto chunk = read_memory(address, 0x4000); + high_memory.insert(high_memory.end(), chunk.begin(), chunk.end()); + } + write_vector("read_memory_ff0000.debug", high_memory); + LOG(""); + } + if(0) { // auto ss = read_vector("security_sector3.debug"); @@ -305,7 +394,7 @@ export int redumper_debug(Context &ctx, Options &options) } // MEDIATEK cache dump extract - if(1) + if(0) { std::vector cache = read_vector(cache_path); diff --git a/scsi/cmd.ixx b/scsi/cmd.ixx index 1775fba3..1ebb2cb8 100644 --- a/scsi/cmd.ixx +++ b/scsi/cmd.ixx @@ -49,7 +49,7 @@ SPTD::Status cdb_send_receive(SPTD &sptd, std::vector &response, T &cdb response.resize(initial_size); *(uint16_t *)cdb.allocation_length = endian_swap(response.size()); - status = sptd.sendCommand(&cdb, sizeof(cdb), response.data(), (uint32_t)response.size()); + status = sptd.sendCommand(&cdb, sizeof(cdb), response.data(), (uint32_t)response.size()).first; if(status.status_code) { response.clear(); @@ -63,7 +63,7 @@ SPTD::Status cdb_send_receive(SPTD &sptd, std::vector &response, T &cdb *(uint16_t *)cdb.allocation_length = endian_swap(response_size); - status = sptd.sendCommand(&cdb, sizeof(cdb), response.data(), (uint32_t)response.size()); + status = sptd.sendCommand(&cdb, sizeof(cdb), response.data(), (uint32_t)response.size()).first; if(status.status_code) response_size = 0; // always use the size from the latest read attempt, this fixes some identified KREON issues @@ -83,7 +83,7 @@ export SPTD::Status cmd_drive_ready(SPTD &sptd) CDB6_Generic cdb = {}; cdb.operation_code = (uint8_t)CDB_OperationCode::TEST_UNIT_READY; - return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0); + return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first; } @@ -97,7 +97,7 @@ export SPTD::Status cmd_inquiry(SPTD &sptd, uint8_t *data, uint32_t data_size, I *(uint16_t *)cdb.allocation_length = endian_swap(data_size); - return sptd.sendCommand(&cdb, sizeof(cdb), data, data_size); + return sptd.sendCommand(&cdb, sizeof(cdb), data, data_size).first; } @@ -111,7 +111,7 @@ export SPTD::Status cmd_read_capacity(SPTD &sptd, uint32_t &lba, uint32_t &block READ_CAPACITY_Response response; - auto status = sptd.sendCommand(&cdb, sizeof(cdb), &response, sizeof(response)); + auto status = sptd.sendCommand(&cdb, sizeof(cdb), &response, sizeof(response)).first; if(!status.status_code) { lba = endian_swap(response.address); @@ -176,7 +176,7 @@ export SPTD::Status cmd_send_key(SPTD &sptd, const uint8_t *data, uint32_t data_ memcpy(parameter_list.data() + sizeof(CMD_ParameterListHeader), data, data_size); } - return sptd.sendCommand(&cdb, sizeof(cdb), parameter_list.empty() ? nullptr : parameter_list.data(), parameter_list.size(), true); + return sptd.sendCommand(&cdb, sizeof(cdb), parameter_list.empty() ? nullptr : parameter_list.data(), parameter_list.size(), true).first; } @@ -189,7 +189,7 @@ export SPTD::Status cmd_report_key(SPTD &sptd, std::vector &response, u cdb.agid = agid; cdb.key_format = (uint8_t)key_format; - return key_format == REPORT_KEY_KeyFormat::INVALIDATE_AGID ? sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0) : cdb_send_receive(sptd, response, cdb); + return key_format == REPORT_KEY_KeyFormat::INVALIDATE_AGID ? sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first : cdb_send_receive(sptd, response, cdb); } @@ -199,7 +199,7 @@ export SPTD::Status cmd_seek(SPTD &sptd, int32_t lba) cdb.operation_code = (uint8_t)CDB_OperationCode::SEEK; *(int32_t *)cdb.lba = endian_swap(lba); - return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0); + return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first; } @@ -211,7 +211,7 @@ export SPTD::Status cmd_read(SPTD &sptd, uint8_t *buffer, uint32_t block_size, i *(int32_t *)cdb.starting_lba = endian_swap(start_lba); *(uint32_t *)cdb.transfer_blocks = endian_swap(transfer_length); - return sptd.sendCommand(&cdb, sizeof(cdb), buffer, block_size * transfer_length); + return sptd.sendCommand(&cdb, sizeof(cdb), buffer, block_size * transfer_length).first; } @@ -237,7 +237,7 @@ SPTD::Status cmd_read_cd_msf(SPTD &sptd, uint8_t *sectors, uint32_t block_size, uint32_t transfer_length = MSF_to_LBA(end_msf) - MSF_to_LBA(start_msf); - return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * transfer_length); + return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * transfer_length).first; } @@ -273,7 +273,7 @@ export SPTD::Status cmd_read_cd(SPTD &sptd, uint8_t *sectors, uint32_t block_siz cdb.include_sync_data = expected_sector_type == READ_CD_ExpectedSectorType::CD_DA ? 0 : 1; cdb.sub_channel_selection = (uint8_t)sub_channel; - return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * transfer_length); + return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * transfer_length).first; } @@ -287,7 +287,11 @@ export SPTD::Status cmd_read_cdda(SPTD &sptd, uint8_t *sectors, uint32_t block_s *(uint32_t *)cdb.transfer_blocks = endian_swap(transfer_length); cdb.sub_code = (uint8_t)sub_code; - return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * transfer_length); + auto [status, transferred_length] = sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * transfer_length); + if(!status.status_code && transferred_length != READ_CDDA_SIZES[(uint8_t)sub_code] * transfer_length) + status.status_code = SPTD::HOST_SHORT_TRANSFER; + + return status; } @@ -296,7 +300,7 @@ export SPTD::Status cmd_plextor_reset(SPTD &sptd) CDB6_Generic cdb = {}; cdb.operation_code = (uint8_t)CDB_OperationCode::PLEXTOR_RESET; - return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0); + return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first; } @@ -305,7 +309,7 @@ export SPTD::Status cmd_synchronize_cache(SPTD &sptd) CDB6_Generic cdb = {}; cdb.operation_code = (uint8_t)CDB_OperationCode::SYNCHRONIZE_CACHE; - return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0); + return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first; } @@ -315,7 +319,7 @@ export SPTD::Status cmd_set_cd_speed(SPTD &sptd, uint16_t speed) cdb.operation_code = (uint8_t)CDB_OperationCode::SET_CD_SPEED; *(uint16_t *)cdb.read_speed = endian_swap(speed); - return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0); + return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first; } @@ -327,7 +331,7 @@ export SPTD::Status cmd_mediatek_read_cache(SPTD &sptd, uint8_t *buffer, uint32_ cdb.offset = endian_swap(offset); cdb.size = endian_swap(size); - return sptd.sendCommand(&cdb, sizeof(cdb), buffer, size); + return sptd.sendCommand(&cdb, sizeof(cdb), buffer, size).first; } @@ -341,7 +345,7 @@ export SPTD::Status cmd_get_configuration_current_profile(SPTD &sptd, GET_CONFIG GET_CONFIGURATION_FeatureHeader feature_header = {}; uint16_t size = sizeof(feature_header); *(uint16_t *)cdb.allocation_length = endian_swap(size); - auto status = sptd.sendCommand(&cdb, sizeof(cdb), &feature_header, size); + auto status = sptd.sendCommand(&cdb, sizeof(cdb), &feature_header, size).first; current_profile = (GET_CONFIGURATION_FeatureCode_ProfileList)endian_swap(feature_header.current_profile); @@ -360,7 +364,7 @@ SPTD::Status cmd_get_configuration(SPTD &sptd) *(uint16_t *)cdb.allocation_length = endian_swap(size); std::vector buffer(size); - auto status = sptd.sendCommand(&cdb, sizeof(cdb), buffer.data(), buffer.size()); + auto status = sptd.sendCommand(&cdb, sizeof(cdb), buffer.data(), buffer.size()).first; auto feature_header = (GET_CONFIGURATION_FeatureHeader *)buffer.data(); uint32_t fds_size = endian_swap(feature_header->data_length) - (sizeof(GET_CONFIGURATION_FeatureHeader) - sizeof(feature_header->data_length)); @@ -391,7 +395,7 @@ export SPTD::Status cmd_kreon_get_security_sector(SPTD &sptd, std::vector #include #include +#include #include #include "throw_line.hh" @@ -45,6 +46,7 @@ export class SPTD { public: static constexpr uint32_t DEFAULT_TIMEOUT = 50000; + static constexpr uint8_t HOST_SHORT_TRANSFER = 0xFF; struct Status { @@ -65,7 +67,11 @@ public: #endif { #if defined(_WIN32) - _handle = CreateFile(std::format("//./{}:", drive_path[0]).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); + std::string device_name = drive_path; + if(device_name.size() == 1) + device_name += ':'; + + _handle = CreateFile(std::format("//./{}", device_name).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if(_handle == INVALID_HANDLE_VALUE) throw_line("unable to open drive ({}, SYSTEM: {})", drive_path, getLastError()); #elif defined(__APPLE__) @@ -150,9 +156,10 @@ public: } - Status sendCommand(const void *cdb, uint8_t cdb_length, void *buffer, uint32_t buffer_length, bool out = false) + std::pair sendCommand(const void *cdb, uint8_t cdb_length, void *buffer, uint32_t buffer_length, bool out = false) { Status status = {}; + uint32_t transferred_length = 0; #if defined(_WIN32) // FIXME: simplify and reuse common SenseData @@ -172,6 +179,8 @@ public: if(success != TRUE) throw_line("SYSTEM ({})", getLastError()); + transferred_length = sptd_sd.sptd.DataTransferLength; + if(sptd_sd.sptd.ScsiStatus != SCSISTAT_GOOD) { status.status_code = sptd_sd.sptd.ScsiStatus; @@ -209,6 +218,8 @@ public: if(auto kret = (*task.get())->ExecuteTaskSync(task.get(), &sense_data, &task_status, &transfer_count); kret != KERN_SUCCESS) throw_line("failed to execute task (MACH: {})", mach_error_string(kret)); + transferred_length = (uint32_t)transfer_count; + if(task_status != kSCSITaskStatus_GOOD) { status.status_code = task_status; @@ -234,6 +245,8 @@ public: if(result < 0) throw_line("SYSTEM ({})", getLastError()); + transferred_length = hdr.dxfer_len - hdr.resid; + if(hdr.status) { status.status_code = hdr.status; @@ -243,7 +256,7 @@ public: } #endif - return status; + return { status, transferred_length }; } @@ -561,7 +574,8 @@ const std::map SPTD::_SCSISTAT_STRINGS = { { 0x22, "COMMAND TERMINATED" }, { 0x28, "TASK SET FULL" }, { 0x30, "ACA ACTIVE" }, - { 0x40, "TASK ABORTED" } + { 0x40, "TASK ABORTED" }, + { 0xFF, "HOST: SHORT TRANSFER" } // host error };