Open hansemro opened 2 years ago
Raspberry Pi 4 : ProS
Rebased openocd-ht32 with latest commits to fix some issues with raspberry pi: https://github.com/hansemro/openocd-ht32/tree/ht32f165x-dev
Build:
git clone -b ht32f165x-dev-v0.12.0-rc2 https://github.com/hansemro/openocd-ht32.git
cd openocd-ht32
./bootstrap
./configure --enable-bcm2835gpio --enable-sysfsgpio
make
sudo make install
openocd.cfg:
adapter driver bcm2835gpio
transport select swd
### for RPi4
bcm2835gpio peripheral_base 0xFE000000
bcm2835gpio speed_coeffs 236181 60
## Pi pin 18 (GPIO24) : SWDIO
adapter gpio swdio 24
## Pi pin 22 (GPIO25) : SWCLK
adapter gpio swclk 25
## Pi pin 12 (GPIO18) : nRST
adapter gpio srst 18
## dont forget pin 9 for Ground
## HT32F1654
set HT32_SRAM_SIZE 0x8000
set HT32_FLASH_SIZE 0x10000
## HT32F52352
#set HT32_SRAM_SIZE 0x8000
#set HT32_FLASH_SIZE 0x20000
source [find target/ht32f165x.cfg]
pi@raspberrypi:~ $ sudo openocd
Open On-Chip Debugger 0.12.0-rc2+dev-00964-g9b86c7e5e (2022-11-03-16:13)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
trst_only separate trst_push_pull
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : BCM2835 GPIO JTAG/SWD bitbang driver
Info : clock speed 12 kHz
Info : SWD DPIDR 0x2ba01477
Info : [ht32f165x.cpu] Cortex-M3 r2p0 processor detected
Info : [ht32f165x.cpu] target has 6 breakpoints, 4 watchpoints
Info : starting gdb server for ht32f165x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Info : accepting 'telnet' connection on tcp/4444
Warn : [ht32f165x.cpu] Only resetting the Cortex-M core, use a reset-init event handler to reset any peripherals or configure hardware srst support.
Pro S RGB firmware patch: https://github.com/hansemro/re-masterkeys/blob/Pro_S_RGB/binaries/Pro_S_RGB/patch.txt
FW offset 0x3400
67fa: ff 28 cmp r0,#0xff
67fc: 04 d1 bne 0x00006808
67fe: 60 68 ldr r0,[r4,#0x4]
6800: 3c 21 movs r1,#0x3c
6802: 31 e0 b 0x00006868
6804: 00 bf nop
6806: 00 bf nop
EDIT Sept 8, 2022: Fixed and tested firmware patch
Some USB packet captures:
1: Plugging in keyboard plug-in.zip
2: Firmware upgrade (1.2.2 to 1.2.2) via OEM software fw-upgrade.zip
3: Firmware upgrade from fake version (1.1.9) to 1.2.2 via OEM software: no major difference fw_upgrade_from_1.1.9.zip
python script to get current firmware version from Pro S: RGB
Dependency: pyusb
get_version.py:
#!/usr/bin/env python3
import usb.core
import usb.util
dev = usb.core.find(idVendor=0x2516, idProduct=0x003c)
if dev is None:
raise ValueError('CM Pro S not found')
else:
print("CM Pro S found")
if dev.is_kernel_driver_active(1):
try:
dev.detach_kernel_driver(1)
print("kernel driver detached")
except usb.core.USBERROR as e:
print(f"Could not detach kernel driver: {e}")
exit(1)
try:
usb.util.claim_interface(dev, 1)
print("Claimed usb device")
except Exception as e:
print(f"{e}")
exit(1)
#ver_req = [ 0x01,0x02,0xdf,0xfc,0x00,0x30,0x00,0x00,0x3d,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0
x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00 ]
ver_req = [ 0x01,0x02 ]
dev.write(0x04, ver_req)
out = dev.read(0x83, 64)
out = "".join(chr(x) for x in out)
print(f"{out[4:9]}")
usb.util.dispose_resources(dev)
output:
$ python3 get_version.py
CM Pro S found
kernel driver detached
Claimed usb device
1.2.2
Additional commands:
Properties:
[0x4,0x0]
(BF)Reset between bootloader/firmware.
[0x4,0x1]
(BF)Reset to bootloader. Observation: replacing 0x1 with any non-zero value seems to behave similarly.
[0x3,N]
(BF)After sending command, keyboard returns device information (firmware address, page size, and version address).
[0x12,0x0]
(BF)After sending command, keyboard returns constant 40+ byte data.
[0x12,0x1]
(BF)After sending command, keyboard returns constant 35+ byte data.
Analyzing fw-upgrade.pcap from above.
Start writing firmware at 0x3400 in 0x34 byte increments...
write-unkwn-StartAddres-End.Address-_data...
0000 01 01 2a 4b 00 34 00 00 33 34 00 00 _48 27 00 20
0010 55 35 00 00 61 35 00 00 63 35 00 00 65 35 00 00
0020 67 35 00 00 69 35 00 00 00 00 00 00 00 00 00 00
0030 00 00 00 00 00 00 00 00 6b 35 00 00 6d 35 00 00
0x34 (52,+0x34) bytes written expect next data to be written start with 0000 0000 6f35... next_write_addr: 0x3434
0000 01 01 c9 af 34 34 00 00 67 34 00 00 _00 00 00 00
0010 6f 35 00 00 71 35 00 00 73 35 00 00 73 35 00 00
0020 73 35 00 00 73 35 00 00 73 35 00 00 73 35 00 00
0030 73 35 00 00 73 35 00 00 73 35 00 00 73 35 00 00
0x34 (+0x68) bytes written expected next data: 7335 0000 7335 0000 7335 .... next_write_addr: 0x3468
0000 01 01 ae 41 68 34 00 00 9b 34 00 00 _73 35 00 00
0010 73 35 00 00 73 35 00 00 73 35 00 00 73 35 00 00
0020 73 35 00 00 73 35 00 00 73 35 00 00 73 35 00 00
0030 73 35 00 00 73 35 00 00 73 35 00 00 73 35 00 00
0x34 (+0x9c) bytes written next_write_addr: 0x349c
0000 01 01 b2 e2 9c 34 00 00 cf 34 00 00 _73 35 00 00
0010 73 35 00 00 73 35 00 00 00 00 00 00 73 35 00 00
0020 73 35 00 00 73 35 00 00 73 35 00 00 73 35 00 00
0030 73 35 00 00 73 35 00 00 73 35 00 00 73 35 00 00
0x34 (+0xd0) bytes written next_write_addr: 0x34d0
0000 01 01 e3 0c d0 34 00 00 03 35 00 00 _73 35 00 00
0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0020 e1 37 00 00 8d 38 00 00 73 35 00 00 73 35 00 00
0030 73 35 00 00 73 35 00 00 73 35 00 00 73 35 00 00
0x34 (+x01_04) bytes written next_write_addr: 0x3504
0000 01 01 5f 7d 04 35 00 00 37 35 00 00 _73 35 00 00
0010 73 35 00 00 73 35 00 00 73 35 00 00 bd 85 00 00
0020 00 00 00 00 73 35 00 00 73 35 00 00 73 35 00 00
0030 73 35 00 00 73 35 00 00 73 35 00 00 73 35 00 00
0x34 (+x01_38) bytes written next_write_addr: 0x3538
0000 01 01 28 9b 38 35 00 00 6b 35 00 00 _73 35 00 00
0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0020 00 00 00 00 73 35 00 00 07 48 80 47 07 48 80 47
0030 07 48 00 47 fe e7 fe e7 fe e7 fe e7 fe e7 fe e7
Need help figuring out what the two unknown bytes are.
Finding crc (the 2 unknown bytes above) with https://sourceforge.net/projects/reveng/ for write commands that start with [0x1,0x1]
:
$ ./reveng -a 8 -X -w 16 -s \
003400003334000048270020553500006135000063350000653500006735000069350000000000000000000000000000000000006b3500006d3500004b2a \
3434000067340000000000006f3500007135000073350000733500007335000073350000733500007335000073350000733500007335000073350000afc9 \
683400009b3400007335000073350000733500007335000073350000733500007335000073350000733500007335000073350000733500007335000041ae \
9c340000cf34000073350000733500007335000000000000733500007335000073350000733500007335000073350000733500007335000073350000e2b2 \
d0340000033500007335000000000000000000000000000000000000e13700008d3800007335000073350000733500007335000073350000733500000ce3 \
383500006b35000073350000000000000000000000000000000000000000000073350000074880470748804707480047fee7fee7fee7fee7fee7fee79b28
width=16 poly=0x1021 init=0x4184 refin=false refout=false xorout=0x0000 check=0x77d5 residue=0x0000 name=(none)
Testing crc against random flash commands that start with [0x1,0x1]
:
#0000 01 01 86 7b 6c 35 00 00 9f 35 00 00 fe e7 fe e7
#0010 fe e7 fe e7 c9 85 00 00 01 72 00 00 81 35 00 00
#0020 df f8 0c d0 00 f0 3c f8 00 48 00 47 21 94 00 00
#0030 48 27 00 20 06 49 07 4a 08 68 50 43 43 f2 39 02
./reveng -w 16 -p 0x1021 -i 0x4184 -c 6c3500009f350000fee7fee7fee7fee7c98500000172000081350000dff80cd000f03cf80048004721940000482700200649074a0868504343f23902
7b68
#0000 01 01 60 c7 04 69 00 00 37 69 00 00 0d 68 05 60
#0010 00 e0 03 60 00 1d 09 1d 52 1e d2 b2 f4 d2 30 bc
#0020 70 47 00 00 10 b5 19 49 60 b1 01 28 0c d0 02 28
#0030 2b d1 35 20 ff f7 90 fc fd f7 7b f8 bd e8 10 40
./reveng -w 16 -p 0x1021 -i 0x4184 -c 04690000376900000d68056000e00360001d091d521ed2b2f4d230bc7047000010b5194960b101280cd002282bd13520fff790fcfdf77bf8bde81040
c760
#0000 01 01 5f a1 28 7f 00 00 5b 7f 00 00 1c 20 14 18
#0010 40 1e a1 70 fb d5 03 48 03 70 10 bd 18 00 00 20
#0020 d0 09 00 20 44 00 00 20 02 4a 01 21 42 f8 20 10
#0030 70 47 00 00 c4 09 00 20 c2 78 81 88 01 23 21 2a
./reveng -w 16 -p 0x1021 -i 0x4184 -c 287f00005b7f00001c201418401ea170fbd50348037010bd18000020d009002044000020024a012142f8201070470000c4090020c27881880123212a
a15f
Looks good so far
There is a different CRC for flash commands that start with [0x1,0x0]
:
./reveng -a 8 -X -w 16 -s \
548a0000878a00002e00fcf769fc0446c0b2424e424f012807d0022820d0042826d0bde8f041fcf7b9bafcf7b7fac4f3072404eb840006eb8000c068e0e6 \
888a0000bb8a000080477868002866d004eb440000ebc400284490f84210bde8f041c1f38011493000f074b8fcf79cfac4f30720bde8f041fdf738b8e84e \
bc8a0000ef8a000078212c480024ba680be01378d82b01d3fe2b04d1782304701c54f0231c54401c521c491ec9b2f0d2012400253868204219d005eb23c5 \
f08a0000238b0000850006eb8000c0688047786820420dd005eb450000ebc500ba6800f179018030515c1044c1f3801100f03cf82846fff74ffe6d1c88c8
width=16 poly=0x1021 init=0x76b4 refin=false refout=false xorout=0x0000 check=0x74a0 residue=0x0000 name=(none)
Seems like [0x1,0x2]
which is used for reading also has its own crc and follows similar syntax as writing. However, the read command is only used for getting firmware version at a fixed address range, which makes it difficult to narrow down crc.
By making assumptions, we can reduce possibilities:
[0x1,0x0]
and [0x1,0x1]
.$ ./reveng -w 16 -p 0x1021 -s 003000003d30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fcdf
./reveng: warning: you have only given 1 sample
./reveng: warning: to reduce false positives, give 4 or more samples
./reveng: no models found
./reveng: warning: you have only given 1 sample
./reveng: warning: to reduce false positives, give 4 or more samples
width=16 poly=0x1021 init=0x18d4 refin=false refout=false xorout=0x0000 check=0x724a residue=0x0000 name=(none)
width=16 poly=0x1021 init=0x2bfa refin=true refout=true xorout=0x0000 check=0x4c77 residue=0x0000 name=(none)
pok3rtool seems to do crc correctly. No need to investigate further
~Protocol is nearly the same as the one descibed in proto_pok3r instead of proto_cykb.~
Correction: Pro S RGB seems to implement/borrow functions in pok3r and cykb protocols. For example, [0x3,0x0]
from pok3r protocol works to retrieve fw/ver addresses and [0x12,0x20]
from cykb protocol works to retrieve version string (like [0x1,0x2]
).
Good and bad news: erase command works for clearing version and firmware data....
In my attempt to write firmware with flash erase step (in my fork of pok3rtool: https://github.com/hansemro/pok3rtool/tree/cooler-master-dev), I noticed that cooler master can no longer be detected over USB and can not function as a HID device.
I was able to set version successfully, but it seems firmware region could not be written to.
Shorting SEL2 to boot into bootloader mode allows the keyboard to be detected, but not with cooler master VID/PID/IPID.
ID 2516:003c Cooler Master Technology Inc. MASTERKEYS PRO S <- firmware mode
ID 2516:003d Cooler Master Technology Inc. USB-HID IAP <- bootloader mode
ID 04d9:8010 Holtek Semiconductor, Inc. <- SEL2 short mode
Not sure what can be done to reprogram firmware without erasing bootloader...
EDIT: For less confusion: bootloader mode actually refers to IAP firmware that lies at 0x0, and SEL2 short mode refers to bootrom/bootloader at 0x1f000000.
Modified SVD with some changes to fix overlapping peripheral regions: HT32F1653_54.svd.zip
SVD Source: https://web.archive.org/web/20220827045212/https://mcu.holtek.com.tw/pack/Holtek.HT32_DFP.1.0.41.pack
Fixed firmware writing with my fork of pok3rtool with https://github.com/hansemro/pok3rtool/commit/11b822daccf6230801e9e4c1574e27bc54f884a1, managed to dump flash from another Pro S RGB, and recovered my previously dead board with stock bootloader!
Flashing patched firmware (from 1.2.2):
$ pok3rtool_cm -t prosrgb flash 1.2.3 ProSRGB_fw_patched.bin --ok
Opened MasterKeys Pro S RGB
Update Firmware: ProSRGB_fw_patched.bin
Reset to Bootloader
Current Version: 1.2.2
Clear Version
Erase...
Write...
Check...
Flashed succesfully
Clear Version
Writing Version: 1.2.3
Reset to Firmware
true
$ pok3rtool_cm -t prosrgb dump flash.dump
WARNING: THIS TOOL IS RELATIVELY UNTESTED, AND HAS A VERY REAL RISK OF CORRUPTING YOUR KEYBOARD, MAKING IT UNUSABLE WITHOUT EXPENSIVE DEVELOPMENT TOOLS. PROCEED AT YOUR OW
N RISK.
Type "OK" to continue:
OK
Proceeding...
Opened MasterKeys Pro S RGB
Dump Flash
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Out: flash.dump, 65520 bytes
$ xxd flash.dump | head -n 5
00000000: 480d 0020 1901 0000 2501 0000 2501 0000 H.. ....%...%...
00000010: 2501 0000 2501 0000 2501 0000 0000 0000 %...%...%.......
00000020: dff8 0cd0 00f0 30f8 0048 0047 8924 0000 ......0..H.G.$..
00000030: 480d 0020 0168 8d46 4168 0847 4115 0000 H.. .h.FAh.GA...
00000040: 40ea 0103 9b07 03d0 09e0 08c9 121f 08c0 @...............
Added initial schematic drawing after a few days of tracing: https://github.com/hansemro/re-masterkeys/commit/35a5ebbd49aea75002c9e5f61e1f66275bda403c
Added initial QMK support (key switch and USB support only): https://github.com/hansemro/qmk_firmware/tree/prosrgb_dev
These results were discovered from firmware disassembly and testing in QMK.
QMK test branch: https://github.com/hansemro/qmk_firmware/tree/prosrgb_mbia043_testing2 QMK (soon)PR branch: https://github.com/hansemro/qmk_firmware/tree/prosrgb_mbia043_dev
Instructions are determined by the number of DCLK rising edges while LE is asserted.
For instructions that read data from shift register, the falling edge of LE must occur shortly after the final data entry gets shifted.
Moves data from shift register to buffer.
Moves data in buffers to comparators.
Moves 10-bit configuration data to shift register.
Configuration:
MSB ... LSB
0b0_0_0_0_0_0_0_0_1_0 (default)
| | | | | | | | | `-> counter disable (when 1)
| | | | | | | | `-> unk1
| | | | | | | `-> unk2
| | | | | | `-> unk3
| | | | | `-> unk4 (not saved)
| | | | `-> unk5 (not saved)
| | | `-> unk6 (not saved)
| | `-> unk7 (not saved)
| `-> unk8
`-> unk9 (not saved)
Enables Write Configuration instruction (if it is the next instruction).
Used by v122 firmware.
Moves data from shift register to (undocumented) configuration register.
Used by v122 firmware to configure each MBIA IC to 0b0000001100.
On every rising edge of DCLK, the shift-register shifts in data from SDI and out of SDO. This includes cases when LE is asserted high (to call an instruction using data in the shift-register).
v122 mbia_shift_data
: https://github.com/hansemro/pok3r_re_firmware/blob/8346ba6d6a77e60094a02dcac3b5c8559cb7bf75/disassemble/cmprosrgb/v122/cmprosrgb_v122.txt#L13111-L13145
Set LE high until the requisite number of DCLK rising edges has been met, then set LE low. Whatever data is in the shift-register on the final DCLK edge is used for the instruction (and not a DCLK cycle later).
v122 mbia_send_instruction
: https://github.com/hansemro/pok3r_re_firmware/blob/8346ba6d6a77e60094a02dcac3b5c8559cb7bf75/disassemble/cmprosrgb/v122/cmprosrgb_v122.txt#L13075-L13108
A grayscale value for each channel must be set (starting from channel 16/OUT15) when updating comparator values. For each channel, a (10-bit) grayscale data is shifted in with LE asserted only for the last bit (Data Latch). After latching data for the final channel, send Overall Latch command.
Note that cascading MBIAs essentially become an extended shift-register, so the same general steps above apply to this case but with N * 10-bit data to shift for each channel.
v122 mbia_shift_RGB_from_sram (writes RGB data to buffers for single row): https://github.com/hansemro/pok3r_re_firmware/blob/8346ba6d6a77e60094a02dcac3b5c8559cb7bf75/disassemble/cmprosrgb/v122/cmprosrgb_v122.txt#L8413-L8490
v122 bftm1_intr (commits buffers to comparators): https://github.com/hansemro/pok3r_re_firmware/blob/8346ba6d6a77e60094a02dcac3b5c8559cb7bf75/disassemble/cmprosrgb/v122/cmprosrgb_v122.txt#L657-L708
For MBIA043 at 3.3V, DCLK has maximum allowed frequency of 25 MHz, so use the appropriate number of NOP delays to operate below this limit. (e.g. For ARM core running at 72 MHz, 3 NOPs are required to run at/below 24 MHz).
Dimming test (shortened cycle length to reduce video length) with QMK.
https://user-images.githubusercontent.com/40348686/195756326-3854ea60-c413-4904-a0f4-0d8b56e75f63
Keyboard (with QMK) hardfaults after enabling Flash Memory Security Protection bit (OB_PP[0]).
Added a new openocd branch to enable security bits (which will be used to debug lockup when security is enabled): https://github.com/hansemro/openocd-ht32/tree/ht32f165x-security-test
> # unlocked keyboard
> halt; ht32f165x check_security 0
[ht32f165x.cpu] halted due to debug-request, current mode: Handler External Interrupt(41)
xPSR: 0x21000039 pc: 0x00000108 msp: 0x20000d18
ht32f165x probe: 128 pages, 0x400 bytes, 0x20000 total
ht32f165x opt byte: ffffffff ffffffff ffffffff ffffffff ffffffff
ht32f165x CPSR: 0xffffffff
ht32f165x OB_CP: 0xffffffff
ht32f165x OB_CK: 0xffffffff
ht32f165x opt byte: ffffffff ffffffff ffffffff ffffffff ffffffff
ht32f165x check_security complete
# relocking keyboard
> halt; ht32f165x enable_security 0
[ht32f165x.cpu] halted due to debug-request, current mode: Handler External Interrupt(41)
xPSR: 0x01000039 pc: 0x00000134 msp: 0x20000d28
ht32f165x probe: 128 pages, 0x400 bytes, 0x20000 total
ht32f165x opt byte: ffffffff ffffffff ffffffff ffffffff ffffffff
ht32f165x programming word 0x0000 @ 0x1ff00000
ht32f165x programming word 0xffffffff @ 0x1ff00004
ht32f165x programming word 0xffffffff @ 0x1ff00008
ht32f165x programming word 0xffffffff @ 0x1ff0000c
ht32f165x programming word 0xfffffffe @ 0x1ff00010
ht32f165x programming word 0xfffffffb @ 0x1ff00020
ht32f165x Security will be set on reset
ht32f165x enable security complete
# confirming keyboard is locked after reset (unplug & plug usb)
> ht32f165x check_security 0
ht32f165x probe: 128 pages, 0x400 bytes, 0x20000 total
ht32f165x opt byte: 0000 ffffffff ffffffff ffffffff fffffffc
ht32f165x CPSR: 0xfffffffc
ht32f165x OB_CP: 0x0000
ht32f165x OB_CK: 0x0000
ht32f165x opt byte: 0000 ffffffff ffffffff ffffffff fffffffc
ht32f165x check_security complete
While keyboard is locked, things you can do in openocd:
reset halt
command to halt MCU in working state before it faults.reg <r0|r1|...|pc> <value>
) and step
through instructions while in working state.reg
commandSome things you cannot do while locked:
CN2 header:
SEL1 header:
SEL2 header:
So far, very similar to POK3R RGB. I will update with more details as I find them.
Tasks:
[0x1, 0x2]
command[0x0, 0x8, ...]
command[0x1, 0x1, ...]
command[0x4, 0x0]
command[0x4, 0x1]
command[0x3, 0x0]
command[0x12, 0xff, ...]
[0x12, 0xff]
command