Lenovo ThinkSmart Cam - unbricking after a failed firmware update (Icatchtek V37M)
Jul 7th 2024 — Lenovo, ThinkSmart, Camera, Unbricking, Icatchtek, V37M, UART, Firmware
Lenovo ThinkSmart Camera - a very nice 4K conference camera, working both with PC and their ThinkSmart ecosystem. It has however a fatal flaw in firmware update procedure - if you don't read entire included README file - you wouldn't know device does firmware update on itself - after the installer reported success. If you happen to interrupt this process by eg. disconnecting USB cable - you may end up with a brick that is no longer detected by PC.
The issue
I got this cam with a fault - it had black vertical stripes all around the image. It looked like one of data channels were dead. I checked Lenovo website and found two firmware update packages - one called "Lenovo ThinkSmart Cam Recovery Firmware" and another one "Lenovo ThinkSmart Cam Tencent Firmware".
I decided to install Tercent one first. Installer took a while, reported success. After a moment, I heard USB disconnect and reconnect noises. Camera was detected, but Windows camera app failed to use it with some error. I disconnected the cam from USB and connected again - camera was alive but black bars issue persisted.
Then I tried "recovery" firmware. Same story - installer succeeded, USB reconnected, cam was available but not working. I pulled USB cable out... but after I connected it again there was no reaction. Device was dead.
This is where I found in README this line:
IMPORTANT: Please do not disconnect the Camera during the installation since this might interrupt the process. Camera LED will start blinking after installation to indicate that it is updating. Please do not disconnect cable or reboot the device until LED stops blinking.
The journey
This section is a long story about how I managed to disassemble and unbrick the camera and what I learned during this process.
Disassembly
There's no obvious way in. The only screws visible are ones holding camera mount. It was immediately clear the front glass has to go.
After a couple of failed attempts I realized glass element is not glass. It is a plastic cover. That simplifies things a bit. But the fit is very snug, there's no way to insert any prying tool between plastic lens and a case.
What I found to work is a razor blade. Put one in gap on the side, next to the camera lens. Push it just about 3-4 milimeters down and then try to pry it up. It should bent slightly, allowing you to insert any other plastic prying tool inside.
Caution - don't try to slide the razor blade - it will easily damage the plastic. Use other prying tool.
When the front element is gone - unscrew three screws that hold front internal case. Be careful! There's a flex cable that links internal case to the main PCB - thus pull the case carefully and unplug the cable first.
All is left is to disconnect the sensor flex cable from main PCB, remove 4 screws that keep PCB attached to the case and pull it out.
This is where I found the original culprit though. For some reason in my unit ZIF socket going to the sensor was not closed, thus it made a bad contact.
UART and... an extra USB port?
When examining the PCB, two things stood out to me. There was an obvious UART connection - 4 pins. Quick measurement returned 3.3V UART. After connecting the adaptor, I found it ran on usual 115200/8/n/1.
I also soldered in MicroUSB port into that unpopulated footprint. However there was also no reaction on PC side when cable was connected there.
At this moment I also desoldered SPI flash - MX35LF1GE4AB. This is 1G NAND, I was able to dump it with CH341A reader.
Back to the UART
3VSPI
c212c2
FW1 incomplete
FW2 size fail
loadFw fail
Entering SP-Boot
This wasn't good - and an obvious firmware brick. Pressing any key drops to the bootloader prompt. Googling yields no results for "SP-Boot" string though.
There are some available commands:
SPBoot>help
cachef :
chksum : chksum <addr> <size>
clktree :
cpuinv : cpuinv <addr> <size>
cpuwb : cpuwb <addr> <size>
dump : dump [b|w|h|l] [<saddr> [<[+]eaddr>]]
etfdump :
fill : fill [b|w|h|l] <saddr> <[+]eaddr> <data>
help :
j : j <addr>
loadfw : loadfw
loadhdr : loadfw
memcpy : memcpy <dst> <src> <size>
mmapdrm : __mmap_dram_init()
mmapini : __mmap_init()
mmapuni : __mmap_uninit()
nandblk : nandblk <page idx>
nandrd : nandrd <addr> <page idx> <number>
r : r [b|w|h|l] [<saddr> [<size>]]
ramcpu :
ramdma :
rampara : rampara <addr>
ramsize :
readid :
rtcreg :
search : search [b|w|h|l] <saddr> [<[+]eaddr>] <pattern>
uartld : uartld <addr>
usbld :
w : w [b|h|w|l] <startaddr> <val>
Obviously two interesting ones seem to be uartld and usbld. Trying usbld first - nothing happened when connected to USB-C port. But when I tried again with MicroUSB cable - I got this:
SPBoot>usbld
USB2.0
[usbVNDRAppReg]
[usbVNDRHandle] state:2, data:0x3f00d060, size:8
[usbVNDRDeviceRequest] bmRequest:0xc0, bRequest:0xb0, wValue:0x0, wIndex:0xaa55, wLength:12
[usbVNDRHandle] state:2, data:0x3f00d060, size:8
[usbVNDRDeviceRequest] bmRequest:0xc0, bRequest:0xb0, wValue:0x0, wIndex:0xaa55, wLength:12
[usbVNDRHandle] state:2, data:0x3f00d060, size:8
[usbVNDRDeviceRequest] bmRequest:0xc0, bRequest:0xb0, wValue:0x0, wIndex:0xaa55, wLength:12
aand PC side:
That was promising, but where to get the drivers? And software? Lenovo firmware updater didn't detect this device. There's a driver included but it reported to be not compatible.
Firmware update initial analysis
When you attempt to run firmware update, Lenovo tool makes a subdirectory of C:\Windows\TempInst. All the files will be there when the camera detection failure message is displayed.
In Comp subdirectory you can find 69MB PDCAM_ISP.brn. This definitely seems interesting. I opened the file in hex editor and found this header:
Icatchtek camera ICs
A quick search returned John Wills blog post "Czur, the SUNPLUS Burn file format". It lead me into spca-fun Github repository that contained `sunp_to_elf.py` script.
This script, applied on PDCAM_ISP.brn returned PDCAM_ISP.brn.elf file that seems to contain just the executable part of the firmware.
readelf shares some importand information that will be useful later:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x80000000 0x80000000 0x857400 0x857400 RWE 0x1
Checking the file in hex editor at +0x1000 offset gives another, very needed clue on how to proceed:
SPCA6500 - the most important clue
Searching for this phrase gives just a few results - but most important is iCatch-V50-Playground github repository. It is for V50 chipset which is different than we have here. But it mentions this:
After that it'll show up in the device manager as Icatch(X) KX Series Bulk Camera Device and Icatch(X) KX Series Null Device and then you can use the FRM.exe to flash the firmware.
A hunt for FRM.exe
In drivers subdirectory the repository contains two files - Reflash v2.6.zip and iCatch PC drivers.zip. 2nd contained some drivers that Windows identified as correct ones for "General USB devices" from few steps earlier. They didn't start though - throwing Code 39 - at this moment I thought it is because drivers were for different SoC.
Reflash package contains Download directory, and there it was - FRM.exe. It needed .net Framework 2.0 to be installed.
It turns out this package was preconfigured for that V50 cpu, including some ISP programmer binary. I decided this was too much risk and decided to google a bit more.
I used "v37 frm.exe" phrase... and found some promising images.
Runcam Thumb Pro-W, our saviour
I found this article on Runcam website, detailing debricking of their THumb Pro-W camera. This camera uses v37isp.bin - and same V37M SoC as ours. Is it a win?
BTW: I made sure the page is saved in Wayback machine, including both attachments.
Back to the files. icatch_v3000_For_V37_V50_V35_V33_K33.7z contains a driver package.
This is also where I have learned about "ISP pin" that you can short to make bootloader go directly into USB ISP mode. I did some tests and it turned out there's a testpad next to UART that achieves the same in our case:
Installing drivers, the unsafe way
Driver installed successfully, buth with the same Code 39 error. I had to look it up, but it turns out Windows Memory Protection feature makes some badly written old drivers not working - and this is the sign. It was enabled in Windows by default since some time before 2020.
The workaround is to disable all the memory integrity features in Windows security center, and for a good measure - disable all the virtualisation options in your PC UEFI.
After a reboot drivers started successfully.
The solution
From this point things started to add up. From download_V1.0.4.7z package I started FRM.exe. It confirmed to have v37 ISP included.
In Basic tab I set "F/W" only, flash type SPI (per UART output), and in customize section I set to NONE. After all this software is preconfigured for a different model than we have.
In Advanced tab I clicked on "Firmware" in the list, then "GET". This triggered ISP mode on camera and fetched me a firmware dump straight from device NAND!
Correct firmware file format
At this point I know nothing about what software expects for firmware file. There's obviously AQY022.BIN listed everywhere. Let's look at it. It sits at temp subdirectory.
This looks familiar, right? It is the same header format as in PDCAM_ISP.brn.ELF at offset +0x1000
At this moment I closed FRM.exe, edited PDCAM_ISP.brn.ELF by removing first 0x1000 bytes - so it started with the expected header. I then saved the file replacing original "AQY022.BIN".
(I don't know why, but I wasn't able to load file under a different file name in FRM.exe, thus I went with just replacing one provided in ZIP with our firmware)
Let's make stuff safe(ish?)
After launching FRM.exe again and confirming the settings, now I took a look into Advanced tab again. The partition layout seems to consist of multiple things - A, B, ISP, Firmware, Firmware 2, RawC and Parameter.
I made sure (from right click menu) that Firmware 2 update was disabled. On RawC i used "Unset file" and disabled "Parameter" update as well.
Here goes nothing!
With that I went back to Basic tab, held my breath and clicked on ISP button.
I still had UART connected at this moment, and as soon as I clicked it - messages started to show up at a very fast rate. Just a moments later I got "ISP Finished" message in FRM.
Upon disconnecting camera from MicroUSB socket and connecting it via USB-C...
Reassembly and final firmware reflashing
This was a good moment to reassemble the camera (or at least connect to a sensor and microphone array). After that I connected it to PC and confirmed it still shows up. It still was!
However I was not getting any image in Camera app, it just remained blank. This was an obvious call to run Lenovo firmware update again.
After flashing it once again - only this time waiting for it to finnish (wait until red led no more blinks, it should do at least two power cycles before it stops for good). - I got an image!
And guess what - no more black lines. Properly connecting that flex cable going to sensor fixed the initial issue.
Random thoughts
The process was not that straightforward. I went into a few rabbit holes that I decided not to describe above to make the story go straight to the point. However, here there are some of my findings and thoughts:
UART firmware load
SPboot has ability to load arbitrary data from UART. By default firmware seems to load into RAM at 0x3f000000 so I pointed it there. After using uartld I simply used `cat file.bin > /dev/ttySOMETHING` to transfer it over. It will stop loading after a few seconds without data transfer.
I don't know how to load it into a flash though.
SPBoot>uartld 0x3f000000
Load 4096 bytes
SPBoot>r 0x3f000000
..dump[3f000000]sz0x20
3f000000 7f 45 4c 46 01 01 01 00-00 00 00 00 00 00 00 00 | .ELF............
3f000010 03 00 08 00 01 00 00 00-00 00 00 80 34 00 00 00 | ............4...
Bootloader dump, Ghidra analysis
r 0x0 0x1000 will dump you bootloader context on the console. You can then copy it into a file editor, slightly modify and paste into hex editor to convert it into binary.
It is ARM Cortex image, Little Endian. If you load it in Ghidra at 0x0 you can play around. uart_printf is easy to find, from UART commands listed in help you will quickly discover a list of structures of (char[8] command; void * ptr, char * help) that will led you into naming many functions.
Final words
If you are a developer of some embedded system, make sure that if you have two firmware images, update can't make both inoperable. That makes no sense.
If you develop a firmware update program, for god's sake - don't end with "success" when device is still in vulnerable state!