Skip to content

Instantly share code, notes, and snippets.

@joevt
Last active April 7, 2024 09:31
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save joevt/4f6d4d97b560efab9603ac509bf00122 to your computer and use it in GitHub Desktop.
Save joevt/4f6d4d97b560efab9603ac509bf00122 to your computer and use it in GitHub Desktop.
A set of functions to examine and modify Thunderbolt DROMs
#! /bin/zsh
# ThunderboltUtil.sh v1.6
# by joevt Apr 2, 2024
#=========================================================================================
#
#
# Thunderbolt DROM Notes:
#
#
# https://lore.kernel.org/patchwork/patch/714766/
#
# Macs with Thunderbolt 1 do not have a unit-specific DROM: The DROM is
# empty with uid 0x1000000000000. (Apple started factory-burning a unit-
# specific DROM with Thunderbolt 2.)
#
# Instead, the NHI EFI driver supplies a DROM in a device property. Use
# it if available. It's only available when booting with the efistub.
# If it's not available, silently fall back to our hardcoded DROM.
#
# The size of the DROM is always 256 bytes. The number is hardcoded into
# the NHI EFI driver. This commit can deal with an arbitrary size however,
# just in case they ever change that.
#
# A modification is needed in the resume code where we currently read the
# uid of all switches in the hierarchy to detect plug events that occurred
# during sleep. On Thunderbolt 1 root switches this will now lead to a
# mismatch between the uid of the empty DROM and the EFI DROM. Exempt the
# root switch from this check: It's built in, so the uid should never
# change. However we continue to *read* the uid of the root switch, this
# seems like a good way to test its reachability after resume.
#
# Background information: The EFI firmware volume contains ROM files for
# the NHI, GMUX and several other chips as well as key material. This
# strategy allows Apple to deploy ROM or key updates by simply publishing
# an EFI firmware update on their website. Drivers do not access those
# files directly but rather through a file server via EFI protocol
# AC5E4829-A8FD-440B-AF33-9FFE013B12D8. Files are identified by GUID, the
# NHI DROM has 339370BD-CFC6-4454-8EF7-704653120818.
#
# The NHI EFI driver amends that file with a unit-specific uid. The uid
# has 64 bit but its entropy is much lower: 24 bit represent the model,
# 24 bit are taken from a serial number, 16 bit are fixed. The NHI EFI
# driver obtains the serial number via the DataHub protocol, copies it
# into the DROM, calculates the CRC and submits the result as a device
# property.
#
#
# https://github.com/torvalds/linux/blob/master/drivers/thunderbolt/eeprom.c
#
# #define TB_DROM_DATA_START 13
# struct tb_drom_header {
# /* BYTE 0 */
# u8 uid_crc8; /* checksum for uid */
# /* BYTES 1-8 */
# u64 uid;
# /* BYTES 9-12 */
# u32 data_crc32; /* checksum for data_len bytes starting at byte 13 */
# /* BYTE 13 */
# u8 device_rom_revision; /* should be <= 1 */
# u16 data_len:10;
# u8 __unknown1:6;
# /* BYTES 16-21 */
# u16 vendor_id;
# u16 model_id;
# u8 model_rev;
# u8 eeprom_rev;
# } __packed;
#
# enum tb_drom_entry_type {
# /* force unsigned to prevent "one-bit signed bitfield" warning */
# TB_DROM_ENTRY_GENERIC = 0U,
# TB_DROM_ENTRY_PORT,
# };
#
# struct tb_drom_entry_header {
# u8 len;
# u8 index:6;
# bool port_disabled:1; /* only valid if type is TB_DROM_ENTRY_PORT */
# enum tb_drom_entry_type type:1;
# } __packed;
#
# struct tb_drom_entry_generic {
# struct tb_drom_entry_header header;
# u8 data[];
# } __packed;
#
# struct tb_drom_entry_port {
# /* BYTES 0-1 */
# struct tb_drom_entry_header header;
# /* BYTE 2 */
# u8 dual_link_port_rid:4; // "Dual-Link Port RID" 0 for the first Thunderbolt controller, 1 for the second Thunderbolt controller, 2 for ?
# u8 link_nr:1; // "Lane" 0 for Lane 1, 1 for Lane 2
# u8 unknown1:2; // 0
# bool has_dual_link_port:1; // 1
#
# /* BYTE 3 */
# u8 dual_link_port_nr:6; // "Dual-Link Port" 2,1,4,3 for ports 1,2,3,4
# u8 unknown2:2; // 0
#
# /* BYTES 4 - 5 TODO decode */
# u8 micro2:4; // 0 0,1,2 (0 for first controller, 1 for second controller)
# u8 micro1:4; // 4 8
# u8 micro3; // "Micro Address" 26,24 00,01
# // or "HPM Address"?
#
# /* BYTES 6-7, TODO: verify (find hardware that has these set) */
# u8 peer_port_rid:4; // 0
# u8 unknown3:3; // 0
# bool has_peer_port:1; // 0
# u8 peer_port_nr:6; // 0
# u8 unknown4:2; // 0
# } __packed;
#
#
#
#
#
# Macmini8,1
#
# 0x00) CRC8: 0x..
# 0x01) UID: 0x000115CE05397601 Least significant byte is Thunderbolt Bus number (0,1,...)
# 0x09) CRC32c: 0x........
# 0x0d) Device ROM Revision: 1
# 0x0e) Length: 0x....
# 0x10) Vendor ID: 0x1 two bytes;
# 1 = Apple
# 0x12) Device ID: 0xD two bytes;
# 16=MacPro7,1
# 13=iMac19,1 or Macmini8,1
# 12=iMac18,3 or iMacPro1,1
# 11=MacBookPro11,5 // Thunderbolt 2
# 0x14) Device Revision: 0x1
# 0x15) EEPROM Revision: 0
# 0x16) 1: 8 1 0 2 8 1 00 0000 Thunderbolt Port
# 0x1e) 2: 9 1 0 1 8 1 00 0000 Thunderbolt Port
# 0x26) 3: 8 1 0 4 8 1 01 0000 Thunderbolt Port
# 0x2e) 4: 9 1 0 3 8 1 01 0000 Thunderbolt Port
# 0x36) 5: 0 9 0 1 0 0 DP or HDMI Adapter (DP In Adapter) Sink 0
# 0x3b) 6: 0 9 0 1 0 0 DP or HDMI Adapter (DP In Adapter) Sink 1
# 0x40) 7: Thunderbolt NHI Adapter
# 0x42) 8: 2 0 PCIe Adapter (PCI Down Adapter) @1 (3 bits PCI Device, 5 bits for ???)
# 0x45) 9: 8 0 PCIe Adapter (PCI Down Adapter) @4
# 0x48) - A:
# 0x4a) - B:
# 0x4c) 1: "Apple Inc."
# 0x59) 2: "Macintosh"
# 0x65) End
#
#
# MacBookPro11,5
#
# 0x01) UID: 0x0001001701C9F100
# 0x0d) Device ROM Revision: 1
# 0x10) Vendor ID: 0x1
# 0x12) Device ID: 0xB
# 0x14) Device Revision: 0x1
# 0x15) EEPROM Revision: 1
# 0x16) 1: 8 0 0 2 8 0 00 0000 Thunderbolt Port 1, Dual Link Port, Lane 0, Dual-Link Port 2, Micro Address 0
# 0x1e) 2: 9 0 0 1 8 0 00 0000 Thunderbolt Port 2, Dual Link Port, Lane 1, Dual-Link Port 1, Micro Address 0
# 0x26) 3: 8 0 0 4 8 0 01 0000 Thunderbolt Port 3, Dual Link Port, Lane 0, Dual-Link Port 4, Micro Address 1
# 0x2e) 4: 9 0 0 3 8 0 01 0000 Thunderbolt Port 4, Dual Link Port, Lane 1, Dual-Link Port 3, Micro Address 1
# 0x36) 5: 000000000000 Thunderbolt NHI Adapter
# 0x3e) 6: 6 0 PCIe Adapter (PCI Down Adapter) @3 (3 bits PCI Device, 5 bits for ???)
# 0x41) 7: 8 0 PCIe Adapter (PCI Down Adapter) @4
# 0x44) 8: a 0 PCIe Adapter (PCI Down Adapter) @5
# 0x47) 9: c 0 PCIe Adapter (PCI Down Adapter) @6
# 0x4a) - A:
# 0x4c) B: 500082 DP or HDMI Adapter (DP In Adapter) Sink 0, Port Affinity 1,2, Preferred Null Port 2
# 0x51) C: 500084 DP or HDMI Adapter (DP In Adapter) Sink 1, Port Affinity 3,4, Preferred Null Port 4
# 0x56) 1: "Apple Inc."
# 0x63) 2: "Macintosh"
# 0x6f) End
#
#=========================================================================================
# Modify DROM
iasl_location=/Applications/MaciASL.app/Contents/MacOS/iasl-stable
getarrstart () {
# bash arrays start at 0
# zsh arrays start at 1 (applies only to [] syntax) but this can be changed with "setopt ksh_arrays"
# zsh arrays start at 0 when using ${arr:x:x} syntax
local arr=(1 0)
arrstart=${arr[1]}
}
getarrstart
crc () {
local bits=$(($1))
local inputreflected=$(($2))
local outputreflected=$(($3))
local polynomial=$(($4))
local crc=$(($5))
local finalxor=$(($6))
for data in $(xxd -p -c 1 | {
if (( inputreflected )); then
rev | tr '0123456789abcdef' '084c2a6e195d3b7f'
else
cat
fi
}); do
((crc ^= (0x$data << (bits-8))))
for ((i=0;i<8;i++)); do
if ((crc >> (bits-1))); then
((crc = (crc << 1) ^ polynomial))
else
((crc <<= 1))
fi
((crc &= ((1 << $bits)-1)))
done
done
printf "%0$(((bits+3) / 4))x" $((crc ^ finalxor)) | {
if (( outputreflected )); then
rev | tr '0123456789abcdef' '084c2a6e195d3b7f'
else
cat
fi
}
}
crc8uid () {
crc 8 0 0 7 0 0xdb
}
crc32c () {
crc 32 1 1 0x1edc6f41 0xffffffff 0xffffffff
}
CRCTABLE=00000000F26B8303E13B70F71350F3F4C79A971F35F1141C26A1E7E8D4CA64EB8AD958CF78B2DBCC6BE228389989AB3B4D43CFD0BF284CD3AC78BF275E133C24105EC76FE235446CF165B798030E349BD7C4507025AFD37336FF2087C494A3849A879FA068EC1CA37BBCEF5789D76C545D1D08BFAF768BBCBC2678484E4DFB4B20BD8EDED2D60DDDC186FE2933ED7D2AE72719C1154C9AC2061C6936F477EA35AA64D611580F55124B5FA6E6B93425E56DFE410E9F95C20D8CC531F97EAEB2FA30E349B1C288CAB2D1D8394623B3BA45F779DEAE05125DAD1642AE59E4292D5ABA3A117E4851927D5B016189A96AE28A7DA086618FCB05629C9BF6966EF07595417B1DBCB3109EBFA0406D4B522BEE4886E18AA3748A09A067DAFA5495B17957CBA2457339C9C6702A993584D8F2B6870C38D26CFE53516FED03A29B1F6821985125DAD3A34E59D0B01EAA244275292796BF4DCC64D4CECF77843D3B85EFBE38DBFC821C2997011F3AC7F2EBC8AC71E81C661503EE0D9600FD5D65F40F36E6F761C6936293AD106180FDE39572966096A65C047D5437877E4767748AB50CF789EB1FCBAD197448AE0A24BB5AF84F38592C855CB2DEEEDFB1CDBE2C453FD5AF467198540D83F3D70E90A324FA62C8A7F9B602C312446940115739B3E5A55230E6FB410CC2092A8FC11A7A7C35E811FF363CDB9BDDCEB018DEDDE0EB2A2F8B682982F63B78709DB87B63CD4B8F91A6C88C456CAC67B7072F64A457DC90563C5F93082F63B7FA44E0B4E91413401B7F9043CFB5F4A83DDE77AB2E8E845FDCE5075C92A8FC1760C37F1473938CE081F80FE355326B08A759E80BB4091BFF466298FC1871A4D8EA1A27DBF94AD42F0B21572CDFEB33C72D80B0C43ED04330CCBBC033A24BB5A6502036A54370C551B11B465265D122B997BAA1BA84EA524E7681D14D2892ED69DAF96E6AC9A99D9E3BC21E9DEF087A761D63F9750E330A81FC588982B21572C9407EF1CA532E023EA145813D758FE5D687E466D594B4952166DF162238CC2A06CAA7A905D9F75AF12B9CD9F2FF56BD190D3D3E1A1E6DCDEEEC064EEDC38D26C431E6A5C722B65633D0DDD5300417B1DBF67C32D8E52CC12C1747422F49547E0BBB3FFD08A86F0EFC5A048DFF8ECEE9147CA56A176FF599E39D9E1AE0D3D3E1AB21B862A832E8915CC083125F144976B4E622F5B7F572064307198540590AB964AB613A67B831C9934A5A4A909E902E7B6CFBAD787FAB5E8C8DC0DD8FE330A81A115B2B19020BD8EDF0605BEE24AA3F05D6C1BC06C5914FF237FACCF169E9F0D59B8273D688D280227AB90321AE7367CA5C18E4C94F48173DBD23943EF36E6F750105EC7612551F82E03E9C8134F4F86AC69F7B69D5CF889D27A40B9E79B737BA8BDCB4B9988C474D6AE7C44EBE2DA0A54C4623A65F16D052AD7D5351
CRC32_8 () {
local crc=$1
local b=$2
local ndx=$(( (crc ^ b) & 0xFF ))
printf $(( (crc >> 8) ^ 0x${CRCTABLE:$ndx*8:8} ))
}
# same as crc32c but faster
CRC32 () {
local crc=$(( 0xFFFFFFFF ));
for bp in $(xxd -p -c 1); do
((crc = $(CRC32_8 $crc 0x$bp) ))
done
printf "%08x" $((crc ^ 0xFFFFFFFF))
}
# same as CRC32 but faster
CRC32b () {
local crc=$(( 0xFFFFFFFF ));
for bp in $(xxd -p -c 1); do
(( crc = (crc >> 8) ^ 0x${CRCTABLE:$(( ((crc ^ 0x$bp) & 0xFF) * 8 )):8} ))
done
printf "%08x" $((crc ^ 0xFFFFFFFF))
}
replacebytes () {
local bytepos=$(($1*2))
local thebytes=$2
local thelen=${#thebytes}
[[ -n $3 ]] && thelen=$(($3*2))
thedrom=${thedrom:0:$bytepos}${thebytes}${thedrom:$bytepos+thelen}
}
processdrom () {
(( debug )) && echo ": processdrom " "$@" 1>&2
local dosetuid=0
local dosetport=0
local dosetstring=0
local doportnumber=-1
local dostringnumber=-1
while (( $# )); do
local param="$1"; shift
if [[ $param != '-' ]]; then
eval "local $param=1"
fi
case "$param" in
dosetuid)
local douuidnum=""
douuidnum=$(perl -pe '$_="0000000000000000" . $_;s/[^A-Fa-f0-9]//g;s/.*(.{16})$/\1/' <<< "$1" | tr 'a-f' 'A-F') # make UID uppercase like Apple does
shift
local dotheuid=""
dotheuid=$(tr 'A-F' 'a-f' <<< "${douuidnum:14:2}${douuidnum:12:2}${douuidnum:10:2}${douuidnum:8:2}${douuidnum:6:2}${douuidnum:4:2}${douuidnum:2:2}${douuidnum:0:2}")
;;
dosetport)
local doportnumber="$(($1))"; shift
local doportcontents="$1"; shift
local doportdisable="$1"
[[ $doportdisable = "-" || $doportdisable = "1" ]] && doportdisable=1 || doportdisable=0
;;
dodeleteport)
local doportnumber="$(($1))"; shift
;;
dosetstring)
local dostringnumber="$(($1))"; shift
local dostringcontents="$1"; shift
;;
dodeletestring)
local dostringnumber="$(($1))"; shift
;;
esac
done
thecrc8=$((0x${thedrom:0:2}))
theuid=${thedrom:2:16}
theuidnum=$(tr 'a-f' 'A-F' <<< "${thedrom:16:2}${thedrom:14:2}${thedrom:12:2}${thedrom:10:2}${thedrom:8:2}${thedrom:6:2}${thedrom:4:2}${thedrom:2:2}") # make UID uppercase like Apple does
if [[ $dosetuid = 1 && $theuidnum != "$douuidnum" ]]; then
replacebytes 1 "$dotheuid"
theuid=$dotheuid
fi
if [[ $theuid == 0000000000000000 ]]; then
isusb4=1
theexpectedcrc8=0
else
isusb4=0
theexpectedcrc8=$((0x$(xxd -p -r <<< "$theuid" | crc8uid)))
fi
(( theexpectedcrc8 != thecrc8 )) && {
if (( dorepairchecksums )); then
replacebytes 0 "$(printf "%02x" $theexpectedcrc8)"
(( dodump )) && printf "0x00) CRC8: 0x%02x (changed: 0x%02x)\n" $thecrc8 $theexpectedcrc8
else
(( dodump )) && printf "0x00) CRC8: 0x%02x (expected: 0x%02x)\n" $thecrc8 $theexpectedcrc8
fi
}
(( dodump && !isusb4 )) && {
printf "0x01) UID: 0x%s // Vendor ID (USB-IF):0x%s Component ID:0x%s Router ID:0x%s" "$theuidnum" "${theuidnum:0:4}" "${theuidnum:4:11}" "${theuidnum:15:1}"
if [[ $dosetuid = 1 && $theuidnum != "$douuidnum" ]]; then
printf " (changed: 0x%s)" "$douuidnum"
fi
echo
}
thecrc32=$((0x${thedrom:24:2}${thedrom:22:2}${thedrom:20:2}${thedrom:18:2}))
thedatalen=$((0x${thedrom:30:2}${thedrom:28:2} & 0x3ff))
theversion=$((0x${thedrom:26:2}))
(( dodump )) && {
printf "0x0d) Version: %d" $theversion # Device ROM Revision
if (( isusb4 )); then
(( theversion == 3 )) && printf " // USB4" || printf " (expected 3)"
else
(( theversion == 1 )) && printf " // TBT3" || printf " (expected 1)"
fi
printf "\n"
}
thereserved=$(((0x${thedrom:30:2}${thedrom:28:2} & ~0x3ff) >> 10))
(( dodump && thereserved )) && printf "0x0e) Reserved: %d (expected 0)\n" $thereserved
thevendorid=0
themodelid=0
themodelrev=0
theeepromrev=0
local startentryoffset=16
if (( !isusb4 )); then
thevendorid=$((0x${thedrom:34:2}${thedrom:32:2}))
(( dodump )) && printf "0x10) TBT3-Vendor ID: 0x%X\n" $thevendorid
themodelid=$((0x${thedrom:38:2}${thedrom:36:2}))
(( dodump )) && printf "0x12) TBT3-Device ID: 0x%X\n" $themodelid
themodelrev=$((0x${thedrom:40:2}))
(( dodump )) && printf "0x14) TBT3-Model Revision: 0x%X\n" $themodelrev
theeepromrev=$((0x${thedrom:42:2}))
(( dodump )) && printf "0x15) TBT3-NVM Revision: %d\n" $theeepromrev
startentryoffset=22
fi
while ((1)); do
local entryoffset=$startentryoffset
((thedataend = 13 + thedatalen))
# Keep a list of ports and strings and generic data
Ports=()
while (( entryoffset <= thedataend )); do
[[ -z ${thedrom:$entryoffset*2:2} ]] && break
local entrylen=$((0x${thedrom:$entryoffset*2:2}))
if ((entrylen < 2)); then
(( dodump && (entryoffset != thedataend) )) && echo "Unexpected error: port length is < 2: $entryoffset + $entrylen <= $thedataend"
break
fi
(( entryoffset == thedataend )) && {
(( dodump )) && echo " ============== (following bytes are unexpected)"
((thedataend += entrylen))
}
local theentrytype=$((0x${thedrom:$entryoffset*2+2:2} >> 7)) # 0=generic,1=Adapter Entry
local theadapterdisabled=$(((0x${thedrom:$entryoffset*2+2:2} >> 6) & 1)) # 0=enabled,1=disabled
local theadapternumber=$((0x${thedrom:$entryoffset*2+2:2} & 0x3f)) # 1..9,a..d
local theadapterbytes=${thedrom:$entryoffset*2+4:$entrylen*2 - 4}
local actualentrylen=$((${#theadapterbytes} / 2 + 2))
if (( dodump )); then
printf "0x%02x) %s %X: " $entryoffset "$( ((theadapterdisabled)) && printf "-" || printf " ")" $theadapternumber
if (( theentrytype || theadapternumber < 1 || theadapternumber > 2 )); then
printf "%s" "$theadapterbytes"
else
printf "\"%s\"" "$(perl -pE "s/(00)+$//" <<< "${theadapterbytes}" | xxd -p -r)"
if [[ $(perl -pE "s/.*?((00)+)$/\1/" <<< "${theadapterbytes}") != "00" ]]; then
printf " (expected single terminating null character: %s)" "$theadapterbytes"
fi
fi
if (( theentrytype )); then
if (( theadapterdisabled && entrylen != 2 )); then
printf " (unexpectedly disabled)"
fi
else
if (( theadapterdisabled )); then
printf " (unexpectedly disabled)"
fi
fi
fi
if ((theadapternumber <= 0)); then
(( dodump )) && printf " (expected port number > 0)"
elif ((theadapternumber == (theentrytype ? doportnumber : dostringnumber))); then
((dodump)) && {
((dosetport || dosetstring)) && printf " (replaced)" || printf " (removed)"
}
else
Ports+=("$(printf "%d %02x %02x %s %s" $((1 - theentrytype)) $theadapternumber $entryoffset $theadapterdisabled "$theadapterbytes")")
fi
if (( entryoffset + entrylen > thedataend )); then
(( dodump )) && printf " (unexpected error: bytes exceeds expected end: 0x%02x + %d = 0x%02x > 0x%02x)" $entryoffset $entrylen $((entryoffset + entrylen)) "$thedataend"
fi
if (( entrylen != actualentrylen )); then
(( dodump )) && printf " (unexpected error: too few remaining bytes: %d > %d)" $entrylen $actualentrylen
fi
if (( dodump )); then
case ${isusb4}_${theentrytype}_$(printf "%02X" $theadapternumber)_${theadapterbytes} in
?_1_??_??????)
local thePreferredLaneAdapter=$(( 0x0${theadapterbytes:4:2} & 0x3f ))
local thePreferenceValid=$(( (0x0${theadapterbytes:4:2} >> 6) & 1 ))
local theReserved=$(( (0x0${theadapterbytes:4:2} >> 7) & 1 ))
printf " // DP {"
((0x${theadapterbytes:0:4})) && printf " Unknown:0x%s," "${theadapterbytes:0:4}"
printf " Preferred Lane Adapter:%d, Preference Valid:%d }" "$thePreferredLaneAdapter" "$thePreferenceValid"
(( theReserved )) && printf ", Reserved:%d (expected 0)" $theReserved
;;
0_1_??_????????????)
printf " // TBT3-Lane Adapter { Lane:%d" "$(( (0x0${theadapterbytes:0:2} >> 4) & 1 ))"
printf ", Dual-Lane Link Capable:"
case $(( (0x0${theadapterbytes:0:2} >> 7) & 1 )) in
0) printf "No" ;;
1) printf "Yes" ;;
esac
printf ", 2nd Adapter Num:%d" "$(( (0x0${theadapterbytes:2:2} >> 0) & 0x3f ))"
((0x${theadapterbytes:4:8})) && printf ", Unknown:0x%s" "${theadapterbytes:4:8}"
printf " }"
;;
0_1_??_??????????????????)
printf " // TBT3-PCIe Upstream Adapter { xx:%02x.%d" \
$(( (0x0${theadapterbytes:0:2} & 0x18) | (0x0${theadapterbytes:0:2} >> 5) )) \
$(( 0x0${theadapterbytes:0:2} & 7 ))
((0x${theadapterbytes:2:16})) && printf ", Unknown:0x%s" "${theadapterbytes:2:16}"
printf " }"
;;
0_1_??_??)
printf " // TBT3-PCIe Downstream Adapter { xx:%02x.%d }" \
$(( (0x0${theadapterbytes:0:2} & 0x18) | (0x0${theadapterbytes:0:2} >> 5) )) \
$(( 0x0${theadapterbytes:0:2} & 7 ))
;;
?_0_01_*) printf " // ASCII Vendor Name" ;;
?_0_02_*) printf " // ASCII Model Name" ;;
?_0_08_*)
local theTMUMode=$(( 0x0${theadapterbytes:0:2} & 3 ))
local theTMURefresh=$(( (0x0${theadapterbytes:0:2} >> 2) & 3 ))
local theReserved=$(( (0x0${theadapterbytes:0:2} >> 4) & 15 ))
printf " // TMU Minimum Requested Mode { TMU Mode:"
case $theTMUMode in
0) printf "Off" ;;
1) printf "Unidirectional" ;;
2) printf "Bidirectional" ;;
*) printf "Reserved" ;;
esac
printf ", TMU Refresh Rate:"
case $theTMURefresh in
1) printf "HiFi" ;;
2) printf "LowRes" ;;
*) printf "Reserved" ;;
esac
(( theReserved )) && printf ", Reserved:%d (expected 0)" $theReserved
printf " }"
;;
?_0_09_*)
local theBcdUSBSpec="$(( 0x0${theadapterbytes:2:1} * 10 + 0x0${theadapterbytes:3:1} )).${theadapterbytes:0:1}.${theadapterbytes:1:1}"
local theIdVendor=$((0x${theadapterbytes:6:2}${theadapterbytes:4:2}))
local theIdProduct=$((0x${theadapterbytes:10:2}${theadapterbytes:8:2}))
local theBcdProductFWRevision="$(( 0x0${theadapterbytes:14:1} * 10 + 0x0${theadapterbytes:15:1} )).${theadapterbytes:12:1}.${theadapterbytes:13:1}"
local theTID=$((0x${theadapterbytes:22:2}${theadapterbytes:20:2}${theadapterbytes:18:2}${theadapterbytes:16:2}))
local theProductHWRevision=$((0x${theadapterbytes:24:2}))
printf " // Product Descriptor { USB Spec:%s, Vendor ID:0x%04x, Product ID:0x%04x, Product FW Revision:%s, TID:0x%08x, Product HW Revision:%d }" \
"$theBcdUSBSpec" "$theIdVendor" "$theIdProduct" "$theBcdProductFWRevision" "$theTID" "$theProductHWRevision"
;;
?_0_0[ACD]_*)
local theLANGID=$((0x${theadapterbytes:2:2}${theadapterbytes:0:2}))
# https://docs.microsoft.com/en-us/windows/win32/intl/language-identifier-constants-and-strings
local theUTF16="$( xxd -p -r <<< ${theadapterbytes:4} | iconv -f UTF-16LE )"
printf " // "
case $(printf "0x%X" $theadapternumber) in
0xA) printf "Serial Number" ;;
0xC) printf "UTF16 Vendor Name" ;;
0xD) printf "UTF16 Model Name" ;;
esac
printf " { LANGID:0x%04X, _:\"%s\" }" "$theLANGID" "$theUTF16"
;;
?_0_0B_*)
printf " // USB Port Mapping {"
for (( usb=0; usb < ${#theadapterbytes} / 6; usb++ )); do
local theUSB=${theadapterbytes:$((usb*6)):6}
(( usb )) && printf ","
printf " { USB3 Port Number:%d, PD Port Number:%d, xHCI Index:%d, USB Type-C:%d, USB3 Adapter Number:%d, Tunnelling Support:%d }" \
$(( (0x${theUSB:0:2} >> 0) & 0x0f )) \
$(( (0x${theUSB:2:2} >> 0) & 0x1f )) \
$(( (0x${theUSB:2:2} >> 5) & 3 )) \
$(( (0x${theUSB:2:2} >> 7) & 1 )) \
$(( (0x${theUSB:4:2} >> 0) & 0x3f )) \
$(( (0x${theUSB:4:2} >> 7) & 1 ))
done
printf " }"
;;
?_0_3F_*) printf " // Reserved" ;;
?_0_3*) printf " // Vendor Specific Type" ;;
?_0_*) printf " // Reserved" ;;
esac
echo
fi
((entryoffset += actualentrylen))
done
if (( doportnumber >= 0 || dostringnumber >= 0 )); then
(( dosetport )) && Ports+=( "$( printf "%d %02x %02x %s %s" 0 $doportnumber 0 "$doportdisable" "$doportcontents" )" )
(( dosetstring )) && Ports+=( "$( printf "%d %02x %02x %s %s" 1 $dostringnumber 0 "0" "$(printf "%s\0" "$dostringcontents" | xxd -p -c 9999)" )" )
local allportbytes=""
allportbytes=$(
IFS=$'\n'
for theentrystring in $(sort <<< "${Ports[*]}"); do
printf "%02x%02x%s" $(( (${#theentrystring} - 10) / 2 + 2 )) $(( ((1 - ${theentrystring:0:1}) << 7) | (${theentrystring:8:1} << 6) | (0x${theentrystring:2:2} & 0x3f) )) "${theentrystring:10}"
done
)
replacebytes $startentryoffset "$allportbytes" ${#thedrom}
((thedatalen = ${#allportbytes}/2 + $startentryoffset - 13 ))
replacebytes 14 "$(printf "%04x" "$thedatalen" | sed -E 's/(..)(..)/\2\1/')"
doportnumber=-1
dostringnumber=-1
(( dodump )) && echo " after changes:"
else
break
fi
done
theexpectedcrc32=$((0x$(xxd -p -r <<< "${thedrom:26:$thedatalen*2}" | CRC32b)))
(( theexpectedcrc32 != thecrc32 )) && {
if (( dorepairchecksums )); then
replacebytes 9 "$(printf "%08x" $theexpectedcrc32 | sed -E 's/(..)(..)(..)(..)/\4\3\2\1/')"
(( dodump )) && printf "0x09) CRC32: 0x%08x (changed: 0x%08x)\n" $thecrc32 $theexpectedcrc32
else
(( dodump )) && printf "0x09) CRC32: 0x%08x (expected: 0x%08x)\n" $thecrc32 $theexpectedcrc32
fi
}
(( dodump )) && {
printf "0x%02x) End" "$entryoffset"
if [[ -n $(tr -d '0' <<< "${thedrom:$entryoffset*2}") ]]; then
printf " (unexpected bytes: %s)" "${thedrom:$entryoffset*2}"
fi
echo
}
if (( domake )); then
IFS=$'\n'
entryoffset=$startentryoffset
for theentrystring in $(sort <<< "${Ports[*]}"); do
entrylen=$(( (${#theentrystring} - 10) / 2 + 2 ))
((entryoffset += entrylen))
done
printf '
"ThunderboltDROM",
Buffer (0x%X)
{
/* 0x00 */ 0x%s, // CRC8 checksum: 0x%02X
/* 0x01 */ 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, // Thunderbolt Bus %d, UID: 0x%s
/* 0x09 */ 0x%s, 0x%s, 0x%s, 0x%s, // CRC32c checksum: 0x%08X
/* 0x0D */ 0x%s, // Device ROM Revision: %d
/* 0x0E */ 0x%s, 0x%s, // Length: %d (starting from previous byte)
/* 0x10 */ 0x%s, 0x%s, // Vendor ID: 0x%X
/* 0x12 */ 0x%s, 0x%s, // Device ID: 0x%X
/* 0x14 */ 0x%s, // Device Revision: 0x%X
/* 0x15 */ 0x%s, // EEPROM Revision: %d
' \
$entryoffset \
"${thedrom:0:2}" $thecrc8 \
"${thedrom:2:2}" "${thedrom:4:2}" "${thedrom:6:2}" "${thedrom:8:2}" "${thedrom:10:2}" "${thedrom:12:2}" "${thedrom:14:2}" "${thedrom:16:2}" $((0x${thedrom:2:2})) "$theuidnum" \
"${thedrom:18:2}" "${thedrom:20:2}" "${thedrom:22:2}" "${thedrom:24:2}" $thecrc32 \
"${thedrom:26:2}" $theversion \
"${thedrom:28:2}" "${thedrom:30:2}" "$thedatalen" \
"${thedrom:32:2}" "${thedrom:34:2}" $thevendorid \
"${thedrom:36:2}" "${thedrom:38:2}" $themodelid \
"${thedrom:40:2}" $themodelrev \
"${thedrom:42:2}" $theeepromrev
IFS=$'\n'
entryoffset=$startentryoffset
for theentrystring in $(sort <<< "${Ports[*]}"); do
entrylen=$(( (${#theentrystring} - 10) / 2 + 2 ))
theadapternumber=$((0x${theentrystring:2:2} & 0x3f))
theadapterdisabled=${theentrystring:8:1}
theentrytype=$((1 - ${theentrystring:0:1}))
printf " /* 0x%02X %s %X */ 0x%02x, 0x%02x, %s" $entryoffset "$( ((theadapterdisabled)) && printf "-" || printf " " )" $theadapternumber $entrylen \
$(( (theentrytype << 7) | (theadapterdisabled << 6) | theadapternumber )) \
"$( perl -pE 's/(..)/0x\1, /g' <<< "${theentrystring:10}" )"
if (( theentrytype == 0 )); then
local thestring=""
thestring="$(perl -pE "s/(00)+$//" <<< "${theentrystring:10}" | xxd -p -r)"
if (( theadapternumber == 1 )); then
printf '// Vendor Name: "%s"' "$thestring"
elif (( theadapternumber == 2 )); then
printf '// Device Name: "%s"' "$thestring"
else
printf '// "%s"' "${theentrystring:10}"
fi
elif (( ${#theentrystring} == 12 )); then
printf '// PCIe xx:%02x.%x' $((0x${theentrystring:10} >> 5)) $((0x${theentrystring:10} & 0x1f)) #### fix this - function should only be 3 bits - therefore there are 2 unknown bits?
fi
printf "\n"
((entryoffset += entrylen))
done
printf " },\n"
fi
}
repairchecksums () {
processdrom dorepairchecksums
}
setuid () {
processdrom dorepairchecksums dosetuid "$1"
}
setport () {
processdrom dorepairchecksums dosetport "$@"
}
deleteport () {
processdrom dorepairchecksums dodeleteport "$1"
}
setstring () {
processdrom dorepairchecksums dosetstring "$@"
}
deletestring () {
processdrom dorepairchecksums dodeletestring "$1"
}
#=========================================================================================
# Files from DROM
dumpdrom () {
processdrom dodump
}
makedromdsl () {
processdrom domake
}
makedromdslall () {
local savedrom="$thedrom"
local thefolderpath="$1"
local i=""
for ((i = 1 ; i <= ${#droms[@]} ; i++)); do
usedromnum "$i"
makedromdsl > "${thefolderpath:=.}/${thefilenamebase}_makedromdsl.txt"
done
usedromstring "$savedrom"
}
dumpdromall () {
local i=""
for ((i = 1 ; i <= ${#droms[@]} ; i++)); do
echo "======================================="
echo "$i)"
usedromnum "$i"
dumpdrom
done
}
dumpdromalltofiles () {
local savedrom="$thedrom"
local thefolderpath="$1"
local i=""
for ((i = 1 ; i <= ${#droms[@]} ; i++)); do
usedromnum "$i"
dumpdrom > "${thefolderpath:=.}/${thefilenamebase}_dumpdrom.txt"
done
usedromstring "$savedrom"
}
#=========================================================================================
# Use DROM
cleardrominfo () {
# clear anything here that is not cleared by processdrom
:
}
usedromstring () {
cleardrominfo
(( debug )) && echo ": usedromstring $1" 1>&2
thedrom="$1"
processdrom
}
usedromnum () {
cleardrominfo
thedrom=${droms[arrstart - 1 + $1]}
processdrom
thefilenamebase="${thefilenamebase}_$i"
}
#=========================================================================================
# DROM List
cleardroms () {
droms=()
paths=()
}
cleardroms
adddrom () {
local savedrom="$thedrom"
local thepath="$1"
usedromstring "$2"
(( ignoreuid )) && replacebytes 0 188877665544332211
local isnew=1
local i=""
for ((i = 0 ; i < ${#droms[@]} ; i++)); do
if [[ $thedrom = "${droms[i+arrstart]}" ]]; then
if [[ ! "$(printf "_\n%s\n_" "${paths[i+arrstart]}")" =~ $(printf ".*\n%s\n.*" "${thepath}") ]]; then
paths[i+arrstart]="$(printf "%s\n%s" "${paths[i+arrstart]}" "$thepath")"
fi
isnew=0
break
fi
done
if (( isnew )); then
(( debug )) && echo ": isnew" 1>&2
droms+=("$thedrom")
paths+=("$thepath")
fi
usedromstring "$savedrom"
}
numstrings=0
loadstring () {
local sourcename="$2"
if [[ -z $sourcename ]]; then
((numstrings++))
sourcename="string:$numstrings"
fi
adddrom "$sourcename" $(xxd -p -r <<< "$1" | xxd -p -c 99999)
}
loadhexfile () {
adddrom "$1" "$(xxd -r "$1" | xxd -p -c 99999)"
}
loadonedromitem () {
local dromitem="$1"
local thepath="${dromitem% = <*}"
local thedrom="${dromitem##* = <}"
local thedrom="${thedrom%>}"
(( debug )) && echo ":" adddrom "${thepath}" "${thedrom}" 1>&2
adddrom "${thepath}" "${thedrom}"
}
loadfwfile () {
while (( $# )); do
local thefilename="$1"
shift
IFS=$'\n'
for dromitem in $(
xxd -p -c 9999999999 "$thefilename" | perl -e '
while (<>) {
s/(..)/\1 /g;
$start = 0;
foreach my $offset (0, 4096 * 3) {
$start = hex(substr($_, $offset, 11) =~ s/(..) (..) (..) (..)/$4$3$2$1/r);
last if ($start != hex("ffffffff"));
}
# look for DROM
while ( /(?{$X=pos()})44 52 4f 4d 20 20 20 20 ff ff ff ff ff ff ff ff ((.. ){1008})(?{$Y=pos()})/g ) {
$X /= 3;
$thedrom=$1;
$thedrom =~ s/(ff )*$//g;
$thedrom =~ s/ //g;
$version = "vers_unknown";
if ($X == hex(200)) {
$version = "v" . substr($_, (10) * 3, 2);
$partition = "linux";
} elsif ($X < hex(4000)) {
$partition = "missing header";
} elsif ($X < hex(82000)) {
$version = "v" . substr($_, (hex(4000) + 10) * 3, 2);
if ($start == hex(4000)) {
$partition = "active";
} elsif ($start == hex(82000)) {
$partition = "inactive";
} else {
$partition = "unknown start";
}
} elsif ($X < hex(100000)) {
$version = "v" . substr($_, (hex(82000) + 10) * 3, 2);
if ($start == hex(4000)) {
$partition = "inactive";
} elsif ($start == hex(82000)) {
$partition = "active";
} else {
$partition = "unknown start";
}
} else {
$partition = "rom too large";
}
pos() = $Y;
$nvmversion = "nvm_unknown";
# get nvm version from after EE_PCIE
if ( /(?{$Z=pos()})45 45 5f 50 43 49 45 20 (?:ff ){8}(?:.. )*?(..) (..) (..) (..) (?:.. ){4}(?:ff ){8}(?{$W=pos()})/g ) {
pos() = $W;
# need to find out what all the digits are for - I am just guessing here:
$nvmversion="nvm_v" . (( $2 . $1 . "." . $3 . $4 ) =~ s/0*([^.]*.)\.0*(.+)/\1.\2/r );
}
else
{
pos() = $Y;
}
printf ("%s:%s:%s:%s:0x%x = <%s>\n", "'"${thefilename}"'", $partition, $version, $nvmversion, $X, $thedrom);
}
}
'
); do
(( debug )) && echo ":" loadonedromitem "${dromitem}" 1>&2
loadonedromitem "${dromitem}"
done
done
}
loadonedslfile () {
local thefilename="$1"
local thesource="$2"
[[ -z $thesource ]] && thesource="$thefilename"
#
# LeadNameChar := [A-Za-z_]
# DigitChar := [0-9]
# NameChar := [A-Za-z_0-9]
# RootChar := \
# ParentPrefixChar := ^
# PathSeparatorChar := .
#
# // Names and paths
#
# NameSeg := [A-Za-z_][A-Za-z_0-9]{0,3}
#
# PrefixPath := \^*
#
# NamePathTail := ([.][A-Za-z_][A-Za-z_0-9]{0,3})*
#
# NamePath := (([A-Za-z_][A-Za-z_0-9]{0,3}([.][A-Za-z_][A-Za-z_0-9]{0,3})*)|)
#
# NonEmptyNamePath := [A-Za-z_][A-Za-z_0-9]{0,3}([.][A-Za-z_][A-Za-z_0-9]{0,3})*
#
# NameString := ([\\]|\^+|)(([A-Za-z_][A-Za-z_0-9]{0,3}([.][A-Za-z_][A-Za-z_0-9]{0,3})*)|)
# | [A-Za-z_][A-Za-z_0-9]{0,3}([.][A-Za-z_][A-Za-z_0-9]{0,3})*
#
IFS=$'\n'
for dromitem in $(
perl -e '
use strict;
my $line3 = "";
my $line2 = "";
my $line1 = "";
my %paths = ( "\\" => 1 );
sub addline {
$line3 = $line2;
$line2 = $line1;
$line1 = $_[0];
}
sub group {
my $indent = $_[0];
my $codepath = $_[1];
my $acpipath = $_[2];
my $nextacpipath = $acpipath;
my $nextcodepath = $codepath;
my $dodump = 0;
my $buffersize = 0;
if ( $line2 =~ /"ThunderboltDROM",\s*$/) {
$dodump = 1;
if ( $line1 =~ /^\s*Buffer\s*\(0x([0-9A-F]+)\)/ ) {
$buffersize = hex($1);
}
elsif ( $line1 =~ /^\s*Buffer\s*\(One\)/ ) {
$buffersize = 1;
}
elsif ( $line1 =~ /^\s*Buffer\s*\(Zero\)/ ) {
$buffersize = 0;
}
else {
$dodump = 0;
}
}
if ( $dodump == 1 ) {
print "'"$thesource"':" . $acpipath . ":ThunderboltDROM = <";
}
while (<>) {
s/\s*\/\/.*//; # single line // comment
s/\s*\/\*.*?\*\/\s*//g; # single line /* */ comment
s/\s*(.*?)\s*$/\1/g; # remove indents and trailing spaces
if (/^\/\*.*/ .. /^.*?\*\/.*/) { } # multi line comment
elsif ( /^$/ ) { } # skip blank lines
elsif ( /^{$/ ) {
group($indent . "\t", $nextcodepath, $nextacpipath);
}
elsif ( /^}/ ) {
if ( $dodump == 1 ) {
if ($buffersize < 0) {
print STDERR "Buffer size is too small\n";
}
elsif ($buffersize > 0) {
print "00"x$buffersize;
}
print ">\n";
$dodump = 0;
}
last;
}
else {
addline($_);
if ( /(External|Device|Field|Method|Name|Scope)\s*\(\s*([\\]|\^+|)(([A-Za-z_][A-Za-z_0-9]{0,3}([.][A-Za-z_][A-Za-z_0-9]{0,3})*)|)/ ) {
my $command = $1;
my $prefix = $2; # \ or ^+ or empty
my $nameparts = $3;
$nameparts =~ s/([A-Za-z0-9])_+/\1/g; # remove trailing spaces
#print "nameparts:" . $nameparts . "\n";
if ( $prefix =~ /\\/ ) {
# absolute path
$nextacpipath = $prefix . $nameparts;
}
elsif ( $prefix =~ /\^+/ ) {
# parent path - remove from the current path a number of parents equal to the number of ^ characters
my $pattern = "(.*?)([\\\\.][^.]+){0," . length($prefix) . "}\$";
# empty path means root "\"
$nextacpipath = ($acpipath =~ s/$pattern/\1/r =~ s/^$/\\/r) . "$nameparts";
}
else {
# assume path is current path appended with name
$nextacpipath = (($acpipath . "." . $nameparts) =~ s/^\\\./\\/r); # "\." is "\"
if ( ($command eq "Scope") && ($nameparts =~ /[^.]+/) && !($paths{$nextacpipath}) ) {
# For Scope, if path does not exist...
if ( ($acpipath . ".") =~ /(.*[.\\]$nameparts)[.].*/ ) {
# if current path includes names then set scope to parent that ends at name
$nextacpipath = (($acpipath . ".") =~ s/(.*[.\\]$nameparts)[.].*/\1/r)
} else {
# unknown path - assume it exists
print STDERR "unknown path " . $nextacpipath . "\n";
#print STDERR "$_\n" for keys %paths;
}
}
}
# code path is just current path appended with entire prefix/nameparts (no interpretation of prefix characters or scoping)
$nextcodepath = (($codepath . "." . $prefix . $nameparts) =~ s/^\\\./\\/r =~ s/\\\\/\\/r); # "\." is "\" and "\\" is "\"
if ( !$paths{$nextacpipath} ) {
# keep a list of all paths
$paths{$nextacpipath} = 1;
}
}
else {
$nextacpipath = $acpipath;
$nextcodepath = $codepath;
if ( $dodump == 1 ) {
my $outline = ($line1 =~ s/0x//gr =~ s/[, ]//gr);
print $outline;
$buffersize -= length($outline) / 2;
}
}
}
}
}
group("", "\\", "\\");
# print "$_\n" for keys %paths;
' < "$thefilename"
); do
(( debug )) && echo ":" loadonedromitem "${dromitem}" 1>&2
loadonedromitem "${dromitem}"
done
}
loaddslfile () {
while (( $# )); do
local thefilename="$1"
shift
loadonedslfile "$thefilename"
done
}
loadamlfile () {
[[ -f $iasl_location ]] || {
echo "# Update iasl_location" 1>&2
exit 1
}
thedirname=$(mktemp -d /tmp/aml.XXXXXX) || exit 1
while (( $# )); do
local thefilename="$1"
shift
if "$iasl_location" -p "$thedirname/xxx" "$thefilename" > /dev/null 2> "$thedirname/out.txt"; then
loadonedslfile "$thedirname/xxx.dsl" "$thefilename"
else
cat "$thedirname/out.txt" 1>&2
fi
done
}
loadoneioregfile () {
local thefilename="$1"
local thesource="$2"
[[ -z $thesource ]] && thesource="$thefilename"
IFS=$'\n'
for dromitem in $(
perl -e '
$inhex=0;
$thepath=""; while (<>) {
if ( $inhex && /^[ |]*[0-9A-F]+:((?: [0-9A-F]{2}){1,32})/ ) { $thehex .= $1 }
elsif ( $inhex ) { $inhex = 0; print "" . (lc ($thehex =~ s/ //gr)) . ">\n" }
if ( /^([ |]*)\+\-o (.+) </ ) { $indent = (length $1) / 2; $name = $2; $thepath =~ s|^((/[^/]*){$indent}).*|$1/$name| }
elsif ( /^[ |]*"(ThunderboltDROM|thunderbolt-drom)" = <(.*)>/i ) { print "'"${thesource}"'" . ":" . $thepath . "/" . $1 . " = <" . $2 . ">\n" }
elsif ( /^[ |]*"(ThunderboltDROM|thunderbolt-drom)" = $/i ) { print "'"${thesource}"'" . ":" . $thepath . "/" . $1 . " = <"; $inhex=1; $thehex="" }
}
' < "${thefilename}"
); do
#echo "«${dromitem}»"
loadonedromitem "${dromitem}"
done
}
loadioregfile () {
while (( $# )); do
local thefilename="$1"
shift
loadoneioregfile "$thefilename"
done
}
loadioreg () {
local tmpfilename=""
tmpfilename=$(mktemp /tmp/local_ioreg.XXXXXX) || exit 1
ioreg -lw0 > "$tmpfilename"
loadoneioregfile "$tmpfilename" "ioreg"
}
listdroms () {
local savedrom="$thedrom"
local i=""
for ((i = 1 ; i <= ${#droms[@]} ; i++)); do
usedromnum "$i"
echo "$i)"
echo "thedrom=$thedrom"
echo "sources:"
echo "${paths[i+arrstart-1]}"
echo
done
usedromstring "$savedrom"
}
#=========================================================================================
# Misc
extractfwfromexe () {
while (( $# )); do
local thefilename="$1"
shift
perl -0777 -e '
while (<>) {
#printf "file:%s\n", $ARGV;
@files = ( /\0([^\0]+.bin)(?=\0)/g );
$filenum = 0;
while ( /(?{$x=pos()})DROM(?{$y=pos()})/g ) {
$sb = substr $_, $x - 0x4200 - 4, 4;
$size = (ord(substr $sb, 3, 1) << 24) | (ord(substr $sb, 2, 1) << 16) | (ord(substr $sb, 1, 1) << 8) | ord (substr $sb, 0, 1);
$filename = @files[$filenum];
if ( $filename =~ /^$/ ) { $filename = "fw.bin"; }
$dir = sprintf "%s/%02d", `dirname "$ARGV"` =~ s/\n$//r, $filenum;
`mkdir -p "$dir"`;
open(FH, ">", "$dir/$filename") or die $!;
print FH substr $_, $x - 0x4200, $size;
close(FH);
pos() = $y;
$filenum++;
}
}
' "$thefilename"
done
}
#=========================================================================================
# Help
dromhelp () {
echo $'
Commands:
Get DROM
loadioreg
loadstring hexstring [sourcename]
loadfwfile filepath...
loadamlfile filepath...
loaddslfile filepath...
loadioregfile filepath...
loadhexfile filepath # for reverses xxd
listdroms
cleardroms
Use DROM
usedromnum numberfromlist
usedromstring lowercasehexstring
Modify DROM
repairchecksums
replacebytes bytepos lowercasehexstring [numbytestoreplace]
setuid newuid
setport 0xportnumber portcontents [-]
deleteport 0xportnumber
setstring 0xstringnumber stringvalue
deletestring 0xstringnumber
Files from DROM
dumpdrom
makedromdsl
makedromdslall [folderpath]
dumpdromall
dumpdromalltofiles [folderpath]
Misc
extractfwfromexe exepath... # $(grep -l --include \'*.exe\' -R \'DROM\' . )
Variables
dodump # Set to 1 to dump the DROM while changes are made to the DROM.
debug # Set to 1 to output debugging info (uses stderr)
ignoreuid # Set to 1 to replace all uids with 0x1122334455667788.
# DROMs with the same contents except UID will be considered identical.
Help
dromhelp
'
}
#dromhelp
#=========================================================================================
@joevt
Copy link
Author

joevt commented Apr 4, 2024

April 2, 2024

  • Added some Thunderbolt 4 / USB4 support.
  • Try to interpret more info for each port.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment