-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes for Realtek RTL8720CF #46
base: master
Are you sure you want to change the base?
Conversation
Hi, The SINGLE scheme is used for devices which have a separate "download" partition for OTA - for now it's only BK7231. Realtek chips, as well as ESP chips for example, use a DUAL scheme, which means that two application images are available at any point. I wonder why did you encounter issues with booting. It's possible that TP-LINK used their own firmware keys (or, more likely, different partition offsets). I see you're also working on implementing OTA on this chip. My main problem was having to generate the app image hash on-device. The issue here is that the app header has a counter value. In order to flash an OTA update, the 2nd partition needs to have the counter value higher than the 1st partition. But this same value is also checksummed using the app hash key. When LibreTiny generates an app image, it would need to set this value to a number higher than what's already on the device. But it can't possibly know the number - even if we used the build timestamp, it wouldn't allow for downgrading. I had two solutions in mind:
Another problem (and the reason why OTA was never implemented on AmebaZ2) is partition tables. BK7231 has a fixed app offset and mostly fixed download offset - so it's not an issue. It is, however, an issue on Realtek chips. AmebaZ has a fixed OTA1 offset, but OTA2 offset is stored on the flash. AmebaZ2 has both offsets changeable. Manufacturers often change the offsets. They can also vary between chip types and are also different for devboards. You won't believe how many times I found myself flashing an OTA update without getting the desired results, just to find out that it was written to an offset that wasn't even used by the bootloader. An obvious solution would be to flash the partition table along with the app (in UF2 images). It is implemented this way on AmebaZ2. There is normally no problem with that, but what if someone wants to change the partition table in an OTA update? (as in, not via ltchiptool, but OTA). Two things can happen:
Another problem is flash wear out. There is no point in flashing the partition table and the bootloader every single time an OTA update is applied. Some people like to keep their devices up to date, which might mean flashing new versions even several times a month. Yeah, sure, flash chips can (theoretically) withstand like 100,000 erase cycles, but we have seen some cheaper devices fail prematurely because of this issue. I tried to fail some middle ground here, but I got caught up in some other things and never got the opportunity to get back to this. I'm open for any ideas. Fixing OTA issues is planned for the LibreTiny v2.0 refactor. That, as well as many more things. See issue libretiny#(insert issue number here). I have described most issues I would like to address there. I wanted to make partition offsets (and keys) configurable separately from the board type - currently, offsets change whether you select bw15, generic rtl8720cf, or wbr3. |
Hi, thank you for your quick reply! The LibreTiny AmbZ2 builder generates UF2 where the bootloader and partition table are stored in the SINGLE scheme. So either that has to be fixed, or ltchiptool has to be updated to also flash the SINGLE parts, otherwise the partition table and bootloader aren't updated and can result in the chip not booting. I agree with you that we shouldn't flash the bootloader and partition table every time to reduce wear on the flash. Maybe something like this would be better then: UF2OTA=[
# same OTA images for flasher and device
f"{image_firmware_is},{image_firmware_is}=device:ota1,ota2;flasher:ota1,ota2",
# having flashed an application image, update the bootloader and partition table (incl. keys)
f"{image_bootloader},{image_bootloader}=flasher:boot,boot",
f"{image_part_table},{image_part_table}=flasher:part_table,part_table",
# clearing headers of the "other" OTA image (hence the indexes are swapped)
f"{image_ota_clear},{image_ota_clear}=device:ota2,ota1;flasher:ota2,ota1",
], The OTA implementation I hacked together works, but it is not ready for release. I chose the simpler approach (which is also used in the SDK) where I corrupt the header of the other image. Instead of erasing it entirely, it is possible to reversibly manipulate the signature, which allows for rollback. The header doesn't contain any constant magic bytes which could be used to detect its presence, so I use the first 4 bytes of the known public key to implement The worse issue is that the signature doesn't checksum the whole image, but just the header of the first sub-image. That means that when the OTA is interrupted, the signature can be valid, but the rest may be corrupted. If this happens to the first image (which has priority when the serial numbers are equal), the device is bricked and needs UART flashing. The solution is to write the signature after the rest of the image is written, but I don't see any obvious way to do that in the current code, because writing to the flash is handled by your Also it may be not safe to overwrite the beginning of the currently booted image, I don't know whether there are any interrupt handlers etc, so we shouldn't include the empty header in the UF2, but handle it in the OTA code with proper locking and other measures. So I propose the final config to be: UF2OTA=[
# same OTA images for flasher and device
f"{image_firmware_is},{image_firmware_is}=device:ota1,ota2;flasher:ota1,ota2",
# having flashed an application image, update the bootloader and partition table (incl. keys)
f"{image_bootloader},{image_bootloader}=flasher:boot,boot",
f"{image_part_table},{image_part_table}=flasher:part_table,part_table",
# clearing headers of the "other" OTA image (hence the indexes are swapped)
f"{image_ota_clear},{image_ota_clear}=flasher:ota2,ota1",
], Regarding OTA modification of the partition table, I personally don't need this feature. For initial flashing I can just connect via UART and later use OTA for upgrades. Can you give me some examples when would this be useful? Maybe only if you wanted a single UF2 to be flashable to multiple different boards already running libretiny, but I don't know how common it is. I am using ESPHome, where you build a firmware for the specific device every time you change a setting, so there is no concept of universal firmwares. I am also not an embedded or IoT developer and just wanted to try this as a fun weekend project to learn more about this stuff. |
I was thinking about it some more and had another idea. We can pre-corrupt the OTA image during build (only for the device scheme), flash the corrupted image and then repair it in the |
8f4651d
to
ae07966
Compare
Hi
|
Hi, to move this forward a little bit, I've separated the first commit to #48. That one is clearly a bug with an obvious fix, whereas this OTA stuff needs more discussion. You've proposed a few times in this thread to remove the rollback functionality:
I agree with you, because it complicates things and the usefulness is limited. It would help only if the firmware could detect that it is bootlooping and switch to the other image, but this would require firmware developers to implement this and the firmware would have to be working enough to reach this check in the first place. It cam also be mostly replaced by a safe mode built into the firmware, like the one in ESPHome. Then the next decision needs to be made whether we want to put the cleverness into the UF2 file, or into the OTA functions inside the firmware. From your comments I see that you lean more into the UF2 direction and I don't disagree. My current solution is suboptimal, because it doubles the size of the UF2 file and also erases the first image block twice, but it was easy for me to implement. I don't know Python much and moving the logic into UF2 would require some larger changes to uf2tool and if I implemented that, it would probably look out of place in your clean and well-thought-out code (at least to my eyes 🙃). |
After merging libretiny-eu/libretiny#307 it should be possible to flash RTL8720CF using the UF2 file produced by libretiny. ESPHome should also work, albeit without OTA. |
Hi, I successfully liberated a TP-Link Tapo P100 Mini smart plug and I want to share the modifications I had to do to make it work with LibreTiny and ESPHome.
First I had issues with the initial flashing, I used the UF2 image, but the device couldn't boot after flashing. With
ltchiptool uf2 dump
I found out that the partition table dump didn't start at zero and traced it back tocollect_data
, which skipped the block starting at zero.But it still didn't boot, so I tried passing only one image to uf2ota (so using the SINGLE scheme instead of DUAL) and was confused that the flasher didn't write anything. I found out that the flasher skips parts with SINGLE scheme and fixed it, that also fixed the default config of libretiny, which uses DUAL for OTA1, but SINGLE for partition table and bootloader, so both of those were skipped and the chip didn't boot.
Thank you for all your hard work on this project!