Files
2026-02-23 11:01:15 +01:00

688 lines
19 KiB
Plaintext

echo 'Western Digital ROYL patch module 0x32 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!!!'
echo 'This patch may not be needed if the module 0x02 patch fixes the slow issue.'
echo 'You should perform the module 0x02 patch first and power cycle the drive.'
echo 'If the module 0x02 patch works, then do not bother with this patch.'
seti $printhelp = $printhelp
seti $help = $help
if $help = 1
echo 'Western Digital ROYL patch module 32 to file using vendor specific commands.'
echo 'Note that some USB drives do not support these vendor specific commands.'
echo 'This patch is meant for drives that suffer from the "slow" issue.'
echo 'This patch clears some data in the RE-LO list.'
echo 'However, it may not clear everything that is needed, and is a bit of a hack.'
echo 'It is intended be used along with the module 02 patch.'
echo 'However, it is normally not needed if the module 02 patch solves the slow issue.'
echo 'This patch is based on data from the following forum thread:'
echo ' WD - Fixing the "Slow Issue" manually :'
echo ' http://www.hddoracle.com/viewtopic.php?f=86&t=848'
echo 'This will dump the sectors to the file "<serial>_mod32orig.bin".'
echo 'And it will create the file "<serial>_mod32patched.bin".'
echo 'It will also create a folder using the model and serial as the folder name,'
echo ' and create a backup dump file with the date and time as part of the name.'
echo 'It will create a backup dump every time a read or write is attempted.'
echo 'The drive must be power cycled for the changes to take effect.'
echo 'You have three options:'
echo '1) Read the module to a file and create the patch.'
echo '2) Write the patched data back to the disk.'
echo '3) Restore the original dump.'
if $printhelp = 1
exit 0
endif
echo 'Hit ENTER to continue...'
userinput $choicestring
previousscript
endif
include good_subroutines.osc
# set the module number
seti $module_number = 0x32
# set the filenames to be written to
sets $basefilename = "mod32original.bin"
sets $basepatchname = "mod32patched.bin"
while 1 = 1
echo " "
echo "WD ROYL Patch Mod32"
echo "This modifies data in the service area of the drive!"
echo "Use at your own risk!"
echo "These commands can take several minutes and appear to be hung."
echo "Please be patient and let it finish on its own."
echo "q) Quit"
echo "p) Previous menu"
echo '1) Read the module to a file and create the patch.'
echo '2) Write the patched data back to the disk.'
echo '3) Restore the original dump.'
echo "Enter your choice:"
userinput $choicestring
if $choicestring = q
exit 0
elseif $choicestring = p
previousscript
elseif $choicestring = 1
gosub read_and_create
elseif $choicestring = 2
gosub write_patch
elseif $choicestring = 3
gosub restore_original
else
echo "bad choice= " $choicestring
endif
done
previousscript
end
subroutine read_module
# get model and serial using identify device command
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 $compare = "WDC"
if $wdcheck != $compare
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
clearbuffer
echo "Model: " $model
echo "Serial: " $serial
# create a directory named with model and serial
sets $directory = $model "__" $serial
sets $command = 'mkdir -p "' $directory '"'
callcommand $command
# set filename for backup dump file
gettime
sets $backup_filename = $directory "/" $date "__" $basefilename
sets $backup_patchname = $directory "/" $date "__" $basepatchname
# set filenames to start with serial
sets $filename = $serial "_" $basefilename
sets $patchname = $serial "_" $basepatchname
echo "Reading the module"
gosub enable_vsc
gosub prepare_read_vsc
# vsc to get a sector of the module
echo "read sector"
setreadpio
buffersize 0x200
clearbuffer
ata28cmd 0xd5 0x01 0xbf 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
# find how many sectors the module contains
seti $mod_length_sectors = buffer 0xa
seti $tempnum = buffer 0xb
seti $tempnum = $tempnum > 8
seti $mod_length_sectors = $mod_length_sectors + $tempnum
seti $remaining_sectors = $mod_length_sectors - 1
# copy the sector to the scratchpad
seti $scratchpad_size = $mod_length_sectors * 512
scratchpadsize $scratchpad_size
copybuffertoscratchpad 0 0 0x200
# vsc to get the remaining sectors
# this gets them one at a time
# there is a way to get them all at once,
# but there was a case where that did not work
echo "read sectors"
seti $count = 1
while $count < $mod_length_sectors
setreadpio
buffersize 0x200
clearbuffer
ata28cmd 0xd5 0x01 0xbf 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
# copy the sectors to the scratchpad
seti $offset = $count * 0x200
copybuffertoscratchpad 0 $offset 0x200
seti $count = $count + 1
done
gosub disable_vsc
# write the sectors to the backup file
writescratchpad $backup_filename 0 0 $scratchpad_size
# process the module
echo "Header:"
printscratchpad 0 4
seti $mod_id = scratchpad 8 w
hex
echo "Module ID = 0x" $mod_id
echo "Size in sectors = 0x" $mod_length_sectors
seti $checksum = scratchpad 0xc dw
echo "32 bit checksum = 0x" $checksum
seti $table_offset = scratchpad 0x30 w
echo "Table offset = 0x" $table_offset
decimal
sets $header = scratchpad 0 4
sets $header_check = "ROYL"
if $header != $header_check
echo "Header is not 'ROYL', exiting"
previousscript
endif
# calculate checksum
seti $calculated_checksum = 0
gosub calculate_checksum_scratchpad
hex
if $checksum = $calculated_checksum
echo "Calculated Checksum = 0x" $calculated_checksum " (good)"
else
echo "Calculated Checksum = 0x" $calculated_checksum " (BAD)"
echo "Checksum of read module is not valid, exiting"
previousscript
endif
decimal
endsubroutine
subroutine make_patch
# this patch zeros out all the data past the module header
# the table offset previously extracted is the starting point
# the easiest way to do this is to clear the buffer,
# and then copy only the needed part back from the scratchpad
#copy the scratchpad to the buffer for writing
seti $buffer_size = $scratchpad_size
buffersize $buffer_size
clearbuffer
copyscratchpadtobuffer 0 0 $table_offset
# 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
# write the patch to the backup file
writebuffer $backup_patchname 0 0 $scratchpad_size
getfilesize $patchname
if $error_level > 0
echo ""
echo "WARNING! The file " $patchname " already exists!"
echo "The patched file was NOT created!"
echo "Please rename or delete the original files to be able to run this option again."
returnsub
endif
echo "Writing patched mod to file " $patchname
writebuffer $patchname 0 0 $scratchpad_size
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
subroutine read_and_create
gosub read_module
# write the sectors to the file, this will overwrite any existing file
getfilesize $filename
if $error_level > 0
echo ""
echo "WARNING! The file " $filename " already exists!"
echo "The module was NOT read to a file!"
echo "The patched file was NOT created!"
echo "Please rename or delete the original files to be able to run this option again."
returnsub
endif
echo ""
echo "Writing origial dump to file " $filename
writescratchpad $filename 0 0 $scratchpad_size
gosub make_patch
endsubroutine
subroutine write_patch
echo "WARNING! You are about to write to the service area of the drive."
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"
returnsub
endif
echo "It is recommended to patch mod02 first and power cycle the drive."
echo "Did you already perform the mod02 patch and power cycle the drive?"
echo 'Type "YES" to continue:'
userinput $choicestring
sets $safety = "YES"
if $choicestring != $safety
echo "You chose not to continue"
returnsub
endif
gosub read_module
getfilesize $patchname
seti $patch_size = $error_level
if $patch_size < 0
echo "ERROR! File " $patchname " not found!"
echo "The patch has not been written to the drive!"
returnsub
endif
seti $divide_check = $patch_size % 512
if $divide_check != 0
echo "ERROR! Filesize of " $patchname " is not evenly dividable by sector size!"
echo "The patch has not been written to the drive!"
returnsub
endif
seti $size_check = $patch_size / 512
if $size_check != $mod_length_sectors
echo "ERROR! The size of " $patchname " does not match the value read from the module!"
echo "The patch has not been written to the drive!"
returnsub
endif
echo "Reading the patch file"
readscratchpad $patchname 0 0 $scratchpad_size
# check for proper header
sets $header = scratchpad 0 4
sets $header_check = "ROYL"
if $header != $header_check
echo "Found header = " $header
echo "ERROR! Header is not 'ROYL'!"
echo "The patch has not been written to the drive!"
returnsub
endif
# verify checksum
seti $checksum = scratchpad 0xc dw
seti $calculated_checksum = 0
gosub calculate_checksum_scratchpad
hex
if $checksum != $calculated_checksum
echo "Checksum in patch file = 0x" $checksum
echo "Calculated Checksum = 0x" $calculated_checksum
echo "ERROR! Checksum of patch file is not valid!"
echo "The patch has not been written to the drive!"
returnsub
endif
decimal
echo ""
echo "Writing the patched module to the drive"
gosub enable_vsc
gosub prepare_write_vsc
# vsc to write the sectors to the disk
# this writes them one at a time
# there is a way to get them all at once,
# but there was a case where that did not work
echo "write sectors"
seti $count = 0
while $count < $mod_length_sectors
setwritepio
buffersize 0x200
# copy the sector to the buffer
seti $offset = $count * 0x200
copyscratchpadtobuffer $offset 0 0x200
ata28cmd 0xd6 0x01 0xbf 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
seti $count = $count + 1
done
gosub disable_vsc
# read the module again and compare it to what we wrote
echo "Verifying data"
gosub enable_vsc
gosub prepare_read_vsc
seti $verify_check = 0
gosub read_and_verify
if $verify_check = 0
echo "Data verified!"
else
echo "WARNING! The data failed verification!"
echo "It appears the data write was unsuccessful!"
endif
# write the sectors to the backup file
writescratchpad $backup_filename 0 0 $scratchpad_size
gosub disable_vsc
endsubroutine
subroutine restore_original
echo "WARNING! You are about to write to the service area of the drive."
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"
returnsub
endif
gosub read_module
getfilesize $filename
seti $file_size = $error_level
if $file_size < 0
echo "ERROR! File " $filename " not found!"
echo "The original has not been written back to the drive!"
returnsub
endif
seti $divide_check = $file_size % 512
if $divide_check != 0
echo "ERROR! Filesize of " $filename " is not evenly dividable by sector size!"
echo "The original has not been written back to the drive!"
returnsub
endif
seti $size_check = $file_size / 512
if $size_check != $mod_length_sectors
echo "ERROR! The size of " $filename " does not match the value read from the module!"
echo "The original has not been written back to the drive!"
returnsub
endif
echo "Reading the original file"
readscratchpad $filename 0 0 $scratchpad_size
# check for proper header
sets $header = scratchpad 0 4
sets $header_check = "ROYL"
if $header != $header_check
echo "Found header = " $header
echo "ERROR! Header is not 'ROYL'!"
echo "The patch has not been written to the drive!"
returnsub
endif
# verify checksum
seti $checksum = scratchpad 0xc dw
seti $calculated_checksum = 0
gosub calculate_checksum_scratchpad
hex
if $checksum != $calculated_checksum
echo "Checksum in patch file = 0x" $checksum
echo "Calculated Checksum = 0x" $calculated_checksum
echo "ERROR! Checksum of origial file is not valid!"
echo "The origial has not been written to the drive!"
returnsub
endif
decimal
echo ""
echo "Writing the origial module to the drive"
gosub enable_vsc
gosub prepare_write_vsc
# vsc to write the sectors to the disk
# this writes them one at a time
# there is a way to get them all at once,
# but there was a case where that did not work
echo "write sectors"
seti $count = 0
while $count < $mod_length_sectors
setwritepio
buffersize 0x200
# copy the sector to the buffer
seti $offset = $count * 0x200
copyscratchpadtobuffer $offset 0 0x200
ata28cmd 0xd6 0x01 0xbf 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
seti $count = $count + 1
done
gosub disable_vsc
# read the module again and compare it to what we wrote
echo "Verifying data"
gosub enable_vsc
gosub prepare_read_vsc
seti $verify_check = 0
gosub read_and_verify
if $verify_check = 0
echo "Data verified!"
else
echo "WARNING! The data failed verification!"
echo "It appears the data write was unsuccessful!"
endif
# write the sectors to the backup file
writescratchpad $backup_filename 0 0 $scratchpad_size
gosub disable_vsc
endsubroutine
subroutine prepare_read_vsc
# vsc to prepare (command to set for module read)
echo "prepare for read"
buffersize 0x200
clearbuffer
setbuffer 0
08 00 01 00 $module_number
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
endsubroutine
subroutine prepare_write_vsc
# vsc to prepare (command to set for module write)
echo "prepare for write"
buffersize 0x200
clearbuffer
setbuffer 0
08 00 02 00 $module_number
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
endsubroutine
subroutine read_and_verify
# vsc to get read the sectors
# this gets them and verifies one at a time
echo "read sectors"
seti $count = 0
while $count < $mod_length_sectors
buffersize 0x200
clearbuffer
setreadpio
ata28cmd 0xd5 0x01 0xbf 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
# compare the data
seti $offset = $count * 0x200
seti $byte_count = 0
while $byte_count < 256
seti $b = buffer $byte_count
seti $s = scratchpad $offset
if $b != $s
seti $verify_check = 1
break
endif
seti $offset = $offset + 1
seti $byte_count = $byte_count + 1
done
if $verify_check != 0
break
endif
seti $count = $count + 1
done
endsubroutine
subroutine calculate_checksum_scratchpad
seti $calculated_checksum = 0
seti $count = 0
while $count < $scratchpad_size
seti $dword = scratchpad $count dw
seti $calculated_checksum = $calculated_checksum + $dword
seti $count = $count + 4
# skip the actual checksum bytes
if $count = 0xc
seti $count = $count + 4
endif
done
seti $calculated_checksum = $calculated_checksum & 0xffffffff
seti $calculated_checksum = 0xffffffff - $calculated_checksum
seti $calculated_checksum = $calculated_checksum + 1
endsubroutine
subroutine calculate_checksum_buffer
seti $calculated_checksum = 0
seti $count = 0
while $count < $buffer_size
seti $dword = buffer $count dw
seti $calculated_checksum = $calculated_checksum + $dword
seti $count = $count + 4
# skip the actual checksum bytes
if $count = 0xc
seti $count = $count + 4
endif
done
seti $calculated_checksum = $calculated_checksum & 0xffffffff
seti $calculated_checksum = 0xffffffff - $calculated_checksum
seti $calculated_checksum = $calculated_checksum + 1
endsubroutine