Files
OpenSuperClone/res/myscripts/wd_royl_mod02_patch_using_ram
T
2022-10-12 10:13:51 +02:00

904 lines
23 KiB
Plaintext

echo 'Western Digital ROYL patch module 0x02 using vendor specific commands.'
echo 'This is to resolve a common WD slow issue, where the drive reads slow.'
echo 'Definition of the "slow responding issue":'
echo ' - The drive is reading very slow, but all reads are GOOD.'
echo 'This may not help any if the reads are bad due to a weak or bad head.'
echo 'WARNING: THIS IS DANGEROUS AND COULD KILL THE DRIVE!!!'
echo 'USE AT YOUR OWN RISK!!!'
seti $printhelp = $printhelp
seti $help = $help
if $help = 1
echo 'WD patch module 02 to perform the slow fix, alternate method.'
echo 'WARNING! Do NOT run the fast ram patch before running this patch!'
echo 'If you have ran the fast ram patch, power cycle the drive to clear it.'
echo 'This patches the module in RAM and uses a trick to write it.'
echo 'This will set and remove a password on the drive.'
echo 'The password will be "password" in case drive ends up locked.'
echo 'You must power cycle the drive for the patch to take effect.'
if $printhelp = 1
exit 0
endif
echo 'Hit ENTER to continue...'
userinput $choicestring
previousscript
endif
echo ""
include good_subroutines
# set a blank string variable to compare with user input
# make sure not to set this anyplace else
sets $null = ""
# set this to itself for use with the menu system
# make sure not to set this anyplace else
# if it was not previously set it will = 0
seti $using_menu = $using_menu
echo "You should have already tried the regular mod02 patch."
echo "Did you already try the regular patch and it failed to apply?"
echo 'Type "YES" to continue:'
userinput $choicestring
sets $safety = "YES"
if $choicestring != $safety
echo "You chose not to continue"
previousscript
endif
echo "WARNING! You are about to write to the service area of the drive."
echo "There is NO UNDO with this script! This operation could be permanent!"
echo "You are on your own if something goes wrong."
echo 'Type "DANGEROUS" to continue:'
userinput $choicestring
sets $safety = "DANGEROUS"
if $choicestring != $safety
echo "You chose not to continue"
previousscript
endif
echo "You must NOT have ran the fast ram patch before running this patch."
echo "Are you sure you did not run the fast ram patch since the drive was powered up?"
echo 'Type "YES" to continue:'
userinput $choicestring
sets $safety = "YES"
if $choicestring != $safety
echo "You chose not to continue"
previousscript
endif
# set these for later, and to make it easy to change if needed
seti $ram_read_block_size = 65536
seti $ram_start = 0x8000000
seti $ram_size = 0x40000
seti $max_ram_size = 0x2000000
if $ram_size < $ram_read_block_size
seti $ram_read_block_size = $ram_size
endif
seti $divide_check = $ram_read_block_size % 512
if $divide_check != 0
echo "ram_read_block_size is not dividable by 512"
previousscript
endif
seti $patch_successful = 0
seti $not_erased_check = 0
gosub idle_drive
gosub identify_device
gosub find_ram_size
gosub dump_ram
if $patch_successful = 1
gosub set_password
gosub remove_password
echo "if password commands completed without errors,"
echo "then the drive should now be patched."
endif
previousscript
end
subroutine identify_device
# set the base filenames
sets $basefilename = "mod02original.bin"
sets $basepatchname = "mod02patched.bin"
echo "Performing identify device command"
buffersize 512
setreadpio
ata28cmd 0 0 0 0 0 0xa0 0xec
# check if command failed
gosub status_check
# extract model and serial and trim leading and trailing spaces
sets $model = "null"
sets $serial = "null"
wordflipbuffer 0 512
seti $count = 0
seti $start_offset = 54
while $count < 40
seti $byte = buffer $start_offset
if $byte > 0x20
break
endif
seti $start_offset = $start_offset + 1
seti $count = $count + 1
done
seti $count = 0
seti $end_offset = 93
while $count < 40
seti $byte = buffer $end_offset
if $byte > 0x20
break
endif
seti $end_offset = $end_offset - 1
seti $count = $count + 1
done
seti $end_offset = $end_offset + 1
seti $size = $end_offset - $start_offset
if $size > 0
sets $model = buffer $start_offset $size
endif
# find out if it is a WD drive
sets $wdcheck = buffer $start_offset 3
sets $wdcompare = "WDC"
if $wdcheck != $wdcompare
echo "Model: " $model
echo "Drive is not WD, exiting
previousscript
endif
seti $count = 0
seti $start_offset = 20
while $count < 20
seti $byte = buffer $start_offset
if $byte > 0x20
break
endif
seti $start_offset = $start_offset + 1
seti $count = $count + 1
done
seti $count = 0
seti $end_offset = 39
while $count < 20
seti $byte = buffer $end_offset
if $byte > 0x20
break
endif
seti $end_offset = $end_offset - 1
seti $count = $count + 1
done
seti $end_offset = $end_offset + 1
seti $size = $end_offset - $start_offset
if $size > 0
sets $serial = buffer $start_offset $size
endif
echo "Model: " $model
echo "Serial: " $serial
# create a directory named with model and serial
sets $directory = $model "__" $serial
sets $wdcommand = 'mkdir -p "' $directory '"'
callcommand $wdcommand
# set filename for backup dump file
gettime
sets $backup_filename = $directory "/" $date "__" $basefilename
sets $backup_patchname = $directory "/" $date "__" $basepatchname
sets $firmware = buffer 46 8
echo "Firmware revision= " $firmware
wordflipbuffer 0 512
# show if drive supports extended commands
seti $tempnum = buffer 167
seti $extended = $tempnum & 4
seti $extended = $extended > 2
#echo "supports 48 bit commands = " $extended
# total addressable sectors
if $extended = 1
seti $addressable = buffer 200 qw
echo "total addressable sectors= " $addressable
else
seti $addressable = buffer 120 dw
echo "total addressable sectors= " $addressable
endif
# words per logical sector
seti $wordsps = buffer 234 w
#echo "words per logical sector= " $wordsps
# size
if $wordsps = 0
seti $sectorsize = 512
else
seti $sectorsize = $wordsps * 2
endif
seti $sizeb = $addressable * $sectorsize
seti $sizemb = $sizeb / 1048576
echo "Size in bytes= " $sizeb
#echo "Size in MiB= " $sizemb
# show drive security status
seti $tempnum = buffer 257
seti $security_level = $tempnum & 1
seti $tempnum = buffer 256
seti $enhanced_security_erase_supported = $tempnum & 32
seti $enhanced_security_erase_supported = $enhanced_security_erase_supported > 5
seti $security_count_expired = $tempnum & 16
seti $security_count_expired = $security_count_expired > 4
seti $security_frozen = $tempnum & 8
seti $security_frozen = $security_frozen > 3
seti $security_locked = $tempnum & 4
seti $security_locked = $security_locked > 2
seti $security_enabled = $tempnum & 2
seti $security_enabled = $security_enabled > 1
seti $security_supported = $tempnum & 1
#echo "enhanced_security_erase_supported= " $enhanced_security_erase_supported
#echo "security_count_expired= " $security_count_expired
#echo "security_frozen= " $security_frozen
echo "security_locked= " $security_locked
echo "security_enabled= " $security_enabled
#echo "security_supported= " $security_supported
if $security_enabled != 0
echo "The drive currently has a password set."
echo "This patch will not work with an existing password."
previousscript
endif
endsubroutine
subroutine find_ram_size
gosub enable_vsc
seti $find_ram_read_size = 0x200
scratchpadsize $find_ram_read_size
seti $position = $ram_start
seti $remainder = $find_ram_read_size
if $remainder != 0
seti $write_offset = 0
seti $read_size = $remainder
hex
echo "position=0x" $position " rsize=0x" $read_size
decimal
# vsc to prepare for ram read
#echo "prepare to read ram"
buffersize 0x200
clearbuffer
seti $offsetl = $position & 0xff
seti $offsetm1 = $position > 8
seti $offsetm1 = $offsetm1 & 0xff
seti $offsetm2 = $position > 16
seti $offsetm2 = $offsetm2 & 0xff
seti $offseth = $position > 24
seti $offseth = $offseth & 0xff
seti $sizel = $read_size & 0xff
seti $sizem1 = $read_size > 8
seti $sizem1 = $sizem1 & 0xff
seti $sizem2 = $read_size > 16
seti $sizem2 = $sizem2 & 0xff
seti $sizeh = $read_size > 24
seti $sizeh = $sizeh & 0xff
setbuffer 0
13 00 01 00 $offsetl $offsetm1 $offsetm2 $offseth $sizel $sizem1 $sizem2 $sizeh
endbuffer
setwritepio
ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
# vsc to get the ram
seti $sectors = $read_size / 512
buffersize $read_size
clearbuffer
setreadpio
#echo "reading ram"
ata28cmd 0xd5 $sectors 0xbf 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
# copy the buffer to the scratchpad
copybuffertoscratchpad 0 $write_offset $read_size
endif
#printscratchpad 0 512
seti $read_size = $find_ram_read_size
while 1 = 1
seti $position = $ram_start + $ram_size
hex
echo "position=0x" $position " size=0x" $read_size
decimal
# vsc to prepare for ram read
#echo "prepare to read ram"
buffersize 0x200
clearbuffer
seti $offsetl = $position & 0xff
seti $offsetm1 = $position > 8
seti $offsetm1 = $offsetm1 & 0xff
seti $offsetm2 = $position > 16
seti $offsetm2 = $offsetm2 & 0xff
seti $offseth = $position > 24
seti $offseth = $offseth & 0xff
seti $sizel = $read_size & 0xff
seti $sizem1 = $read_size > 8
seti $sizem1 = $sizem1 & 0xff
seti $sizem2 = $read_size > 16
seti $sizem2 = $sizem2 & 0xff
seti $sizeh = $read_size > 24
seti $sizeh = $sizeh & 0xff
setbuffer 0
13 00 01 00 $offsetl $offsetm1 $offsetm2 $offseth $sizel $sizem1 $sizem2 $sizeh
endbuffer
hex
#echo "keysector= 13 00 01 00 " $offsetl " " $offsetm1 " " $offsetm2 " " $offseth " " $sizel " " $sizem1 " " $sizem2 " " $sizeh
setwritepio
ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
# vsc to get the ram
seti $sectors = $read_size / 512
buffersize $read_size
clearbuffer
setreadpio
#echo "reading ram"
ata28cmd 0xd5 $sectors 0xbf 0x4f 0xc2 0xa0 0xb0
hex
#echo "command= d5 " $sectors " bf 4f c2 a0 b0"
decimal
# check if command failed
gosub status_check
seti $comparedata = 0
seti $found_match = 1
while $comparedata < 512
seti $cmp = buffer $comparedata qw
seti $check = scratchpad $comparedata qw
if $cmp != $check
seti $found_match = 0
break
endif
seti $comparedata = $comparedata + 8
done
if $found_match = 1
hex
echo "ram size is 0x" $ram_size
decimal
break
endif
seti $ram_size = $ram_size * 2
if $ram_size > $max_ram_size
hex
echo "ram size not found, using default maximum size of 0x" $max_ram_size
decimal
seti $ram_size = $max_ram_size
break
endif
done
gosub disable_vsc
endsubroutine
subroutine dump_ram
gosub enable_vsc
scratchpadsize $ram_size
seti $position = $ram_start
seti $read_size = $ram_read_block_size
seti $block_count = $ram_size / $ram_read_block_size
seti $search_position = 0
seti $count = 0
while $count < $block_count
hex
echo "position=0x" $position " size=0x" $read_size
decimal
# vsc to prepare for ram read
#echo "prepare to read ram"
buffersize 0x200
clearbuffer
seti $offsetl = $position & 0xff
seti $offsetm1 = $position > 8
seti $offsetm1 = $offsetm1 & 0xff
seti $offsetm2 = $position > 16
seti $offsetm2 = $offsetm2 & 0xff
seti $offseth = $position > 24
seti $offseth = $offseth & 0xff
seti $sizel = $read_size & 0xff
seti $sizem1 = $read_size > 8
seti $sizem1 = $sizem1 & 0xff
seti $sizem2 = $read_size > 16
seti $sizem2 = $sizem2 & 0xff
seti $sizeh = $read_size > 24
seti $sizeh = $sizeh & 0xff
setbuffer 0
13 00 01 00 $offsetl $offsetm1 $offsetm2 $offseth $sizel $sizem1 $sizem2 $sizeh
endbuffer
setwritepio
ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
# send again
ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
# vsc to get the ram
seti $sectors = $read_size / 512
buffersize $read_size
clearbuffer
setreadpio
#echo "reading ram"
ata28cmd 0xd5 $sectors 0xbf 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
seti $write_offset = $count * $read_size
# copy the buffer to the scratchpad
copybuffertoscratchpad 0 $write_offset $read_size
gosub process_ram
seti $count = $count + 1
seti $position = $position + $ram_read_block_size
done
gosub disable_vsc
echo "ram processing finished"
if $patch_successful = 1
echo "ram patch successful"
else
echo "ram patch failed"
endif
endsubroutine
subroutine process_ram
#echo "processing ram"
sets $header_check = "ROYL"
seti $search_end = $search_position + $ram_read_block_size
seti $search_end = $search_end - 4096
while $search_position < $search_end
sets $header = scratchpad $search_position 4
if $header = $header_check
# process the module
seti $working_offset = $search_position
#echo "Header:"
#printscratchpad $working_offset 4
seti $working_offset = $search_position + 8
seti $mod_id = scratchpad $working_offset w
hex
#echo "Module ID = 0x" $mod_id
if $mod_id = 2
# find how many sectors the module contains
seti $working_offset = $search_position + 0xa
seti $mod_length_sectors = scratchpad $working_offset
seti $working_offset = $search_position + 0xb
seti $tempnum = scratchpad $working_offset
seti $tempnum = $tempnum > 8
seti $mod_length_sectors = $mod_length_sectors + $tempnum
#echo "Size in sectors = 0x" $mod_length_sectors
seti $working_offset = $search_position + 0xc
seti $checksum = scratchpad $working_offset dw
#echo "32 bit checksum = 0x" $checksum
seti $working_offset = $search_position + 0x10
#echo "Mod version:"
#printscratchpad $working_offset 8
decimal
seti $module_size = $mod_length_sectors * 512
seti $calculated_checksum = 0
seti $check_count = 0
while $check_count < $module_size
seti $working_offset = $search_position + $check_count
if $working_offset > $search_end
echo "end of module is past end of rom"
break
endif
seti $dword = scratchpad $working_offset dw
seti $calculated_checksum = $calculated_checksum + $dword
seti $check_count = $check_count + 4
# skip the actual checksum bytes
if $check_count = 0xc
seti $check_count = $check_count + 4
endif
done
seti $calculated_checksum = $calculated_checksum & 0xffffffff
seti $calculated_checksum = 0xffffffff - $calculated_checksum
seti $calculated_checksum = $calculated_checksum + 1
hex
if $checksum = $calculated_checksum
sets $checkstring = " calulated=0x" $calculated_checksum " (good)"
echo "pos=0x" $search_position " ModID=0x" $mod_id " size=0x" $mod_length_sectors $checkstring
seti $module_position = $search_position
seti $module_length = $mod_length_sectors * 512
gosub patch_module
else
sets $checkstring = " calulated=0x" $calculated_checksum " (bad)"
endif
decimal
endif
endif
seti $search_position = $search_position + 512
done
endsubroutine
subroutine patch_module
seti $wposition = $module_position
seti $wposition = $wposition + 0x8000000
seti $write_size = $module_length
hex
echo "wposition=0x" $wposition " wsize=0x" $write_size
decimal
# vsc to prepare for ram write
echo "prepare to write ram"
buffersize 0x200
clearbuffer
seti $offsetl = $wposition & 0xff
seti $offsetm1 = $wposition > 8
seti $offsetm1 = $offsetm1 & 0xff
seti $offsetm2 = $wposition > 16
seti $offsetm2 = $offsetm2 & 0xff
seti $offseth = $wposition > 24
seti $offseth = $offseth & 0xff
seti $sizel = $write_size & 0xff
seti $sizem1 = $write_size > 8
seti $sizem1 = $sizem1 & 0xff
seti $sizem2 = $write_size > 16
seti $sizem2 = $sizem2 & 0xff
seti $sizeh = $write_size > 24
seti $sizeh = $sizeh & 0xff
setbuffer 0
13 00 02 00 $offsetl $offsetm1 $offsetm2 $offseth $sizel $sizem1 $sizem2 $sizeh
endbuffer
hex
#echo $offsetl " " $offsetm1 " " $offsetm2 " " $offseth " " $sizel " " $sizem1 " " $sizem2 " " $sizeh
decimal
setwritepio
ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
# vsc to get the ram
seti $sectors = $read_size / 512
buffersize $module_length
clearbuffer
copyscratchpadtobuffer $module_position 0 $module_length
#printbuffer 0 $module_length
writebuffer $backup_filename 0 0 $module_length
gosub process_mod02
if $not_erased_check = 1
# calculate new checksum and insert it
seti $calculated_checksum = 0
gosub calculate_checksum_buffer
hex
echo "Calculated Checksum = 0x" $calculated_checksum
decimal
seti $b1 = $calculated_checksum & 0xff
seti $calculated_checksum = $calculated_checksum > 8
seti $b2 = $calculated_checksum & 0xff
seti $calculated_checksum = $calculated_checksum > 8
seti $b3 = $calculated_checksum & 0xff
seti $calculated_checksum = $calculated_checksum > 8
seti $b4 = $calculated_checksum & 0xff
setbuffer 0x0c
$b1 $b2 $b3 $b4
endbuffer
writebuffer $backup_patchname 0 0 $module_length
setwritepio
echo "writing ram"
ata28cmd 0xd6 $mod_length_sectors 0xbf 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
else
echo "Warning! Mod02 record 27 appears to have been erased in ram."
echo "The patch will not be written."
seti $patch_successful = 0
endif
endsubroutine
subroutine process_mod02
# process the module
seti $total_records = buffer 0x30 w
echo "Total records = " $total_records
# process the data records
seti $rcount = 0
while $rcount < $total_records
seti $offset = $rcount * 4
seti $rcount = $rcount + 1
seti $offset = $offset + 0x32
seti $record_offset = buffer $offset w
seti $offset = $offset + 2
seti $record_size = buffer $offset w
# process record 27 (slow fix)
if $rcount = 27
echo "Data record #" $rcount ":"
printbuffer $record_offset $record_size
seti $erased = 0
seti $not_erased_check = 0
while $erased < $record_size
seti $erased_offset = $record_offset + $erased
seti $check_byte = buffer $erased_offset b
if $check_byte != 0
seti $not_erased_check = 1
break
endif
seti $erased = $erased + 1
done
seti $erase_offset = $record_offset + 2
setbuffer $erase_offset
00
endbuffer
echo "Data record #" $rcount " after patch:"
printbuffer $record_offset $record_size
seti $patch_successful = 1
endif
done
endsubroutine
subroutine calculate_checksum_buffer
seti $calculated_checksum = 0
seti $ccount = 0
while $ccount < $module_length
seti $dword = buffer $ccount dw
seti $calculated_checksum = $calculated_checksum + $dword
seti $ccount = $ccount + 4
# skip the actual checksum bytes
if $ccount = 0xc
seti $ccount = $ccount + 4
endif
done
seti $calculated_checksum = $calculated_checksum & 0xffffffff
seti $calculated_checksum = 0xffffffff - $calculated_checksum
seti $calculated_checksum = $calculated_checksum + 1
endsubroutine
subroutine set_password
echo "setting password"
# set the buffer size
buffersize 512
# clear the buffer to 0's
clearbuffer
seti $type = 0
seti $level = 0
sets $password = "password"
# put the type and level in the buffer
seti $byte0 = $type & 0x01
seti $byte1 = $level & 0x01
setbuffer 0
$byte0 $byte1
endbuffer
# put the password (up to 32 characters) in the buffer
# if set this will overwrite part of all of the hex password
stringtobuffer 2 32 $password
# set the LBAs and count to 0
seti $count = 0
seti $LBAlow = 0
seti $LBAmid = 0
seti $LBAhigh = 0
# set some things for the ata command
setwritepio
# set features to 0
seti $features = 0
# set device bits 7(compatibility) 5(compatibility)
seti $device = 0xa0
# set the command for security set password pio data-out
seti $atacommand = 0xf1
# perform the command
ata28cmd $features $count $LBAlow $LBAmid $LBAhigh $device $atacommand
# check if command failed
seti $command_failed = 0
gosub check_command_status
if $command_failed = 1
echo "Command failed!"
gosub show_sense_data
gosub show_ata_return_status
previousscript
endif
echo "password set"
endsubroutine
subroutine remove_password
echo "removing password"
# set the buffer size
buffersize 512
# clear the buffer to 0's
clearbuffer
seti $type = 0
sets $password = "password"
# put the type in the buffer
seti $byte0 = $type & 0x01
setbuffer 0
$byte0
endbuffer
# put the password (up to 32 characters) in the buffer
# if set this will overwrite part of all of the hex password
stringtobuffer 2 32 $password
# set the LBAs and count to 0
seti $count = 0
seti $LBAlow = 0
seti $LBAmid = 0
seti $LBAhigh = 0
# set some things for the ata command
setwritepio
# set features to 0
seti $features = 0
# set device bits 7(compatibility) 5(compatibility)
seti $device = 0xa0
# set the command for security disable password pio data-out
seti $atacommand = 0xf6
# perform the command
ata28cmd $features $count $LBAlow $LBAmid $LBAhigh $device $atacommand
# check if command failed
seti $command_failed = 0
gosub check_command_status
if $command_failed = 1
echo "Command failed!"
gosub show_sense_data
gosub show_ata_return_status
previousscript
endif
echo "password removed"
endsubroutine
subroutine idle_drive
echo "attempting to idle drive"
seti $idle_tries = 0
while $idle_tries < 100
seti $idle_tries = $idle_tries + 1
echo "idle attempt " $idle_tries
gettime
seti $startreadtime = $time
gosub idle
gosub idle
gettime
seti $endreadtime = $time
seti $elapsedtime = $endreadtime - $startreadtime
if $elapsedtime < 100000
break
endif
done
endsubroutine
subroutine idle
buffersize 0
setreadpio
# set the count to 0
seti $count = 0
# set the LBA for the unload command
seti $LBAlow = 0x4c
seti $LBAmid = 0x4e
seti $LBAhigh = 0x55
# set features to unload
seti $features = 0x44
# set device bits 7(compatibility) 5(compatibility)
seti $device = 0xa0
# set the command for idle immediate
seti $atacommand = 0xe1
# perform the command
ata28cmd $features $count $LBAlow $LBAmid $LBAhigh $device $atacommand
gosub status_check
endsubroutine
subroutine status_check
seti $command_failed = 0
gosub check_command_status
if $command_failed = 1
echo "Command failed!"
gosub show_sense_data
gosub show_ata_return_status
previousscript
endif
endsubroutine
subroutine enable_vsc
# enable vendor specific commands
echo "enable vsc"
buffersize 0
setreadpio
ata28cmd 0x45 0x0b 0x00 0x44 0x57 0xa0 0x80
# check if command failed
gosub status_check
endsubroutine
subroutine disable_vsc
# disable vendor specific commands
echo "disable vsc"
buffersize 0
setreadpio
ata28cmd 0x44 0x0b 0x00 0x44 0x57 0xa0 0x80
# check if command failed
gosub status_check
endsubroutine