logo elektroda
logo elektroda
X
logo elektroda

BK7231 Emulator: early experiments with Capstone & Unicorn in Python

p.kaczmarek2  0 36 Cool? (+2)

TL;DR LABEL_AI_GENERATED

  • BekenEmulator is an early Python proof of concept that executes BK7231 ARM firmware—including bootloader and application code—rather than merely porting OpenBeken firmware to a host OS.
  • It combines Capstone for ARM/Thumb disassembly with Unicorn Engine for ARM968E-S CPU emulation, while mocking Beken-specific registers to prevent firmware polling loops.
  • The emulator booted OpenBK7231T QIO version 1.18.300 through Main_Init_After_Delay and ran a float test producing 3.140000 + 1.860000 = 5.000000.
  • It also generated extensive UART logs from a BK7231T Woox/Tuya firmware image, reaching Tuya initialization, radio calibration output, and TCP/IP-stack initialization.
  • Hardware-register and peripheral emulation remains incomplete: Wi-Fi is not initialized, flash-backed data is missing, and OpenBeken has not yet reached its recurring “on every second” loop.
AI summary based on the discussion. May contain errors.

Beken chips have been around for quite some time now, but there isn't any Beken emulator that I am aware of. We have our OBK Simulator, but it's basically a Windows/Linux port of OBK firmware, and not the true emulator which tries to mimic Beken chip by running their instructions at hardware level. However, as I've recently shown with ALI M3801, it's relatively simple to get a basic control flow running with capstone in Python. In this topic, I will show a basic preview of my Beken simulator - it's still an early proof of concept, but it's already showing some really promising results!

The basic idea here is to use existing Python libraries to do the heavy lifting. I went with Capstone for disassembling the ARM/Thumb instructions and Unicorn Engine (which is based on QEMU) for the actual CPU emulation part. This works mostly out of the box because Beken chips like BK7231 are just standard ARM9 architectures (ARM968E-S). The only real tricky part is mocking the Beken-specific hardware registers so the firmware doesn't get stuck in an infinite loop when it tries to talk to the Wi-Fi baseband or hardware timers.

So far I've did only a very basic draft, trying to "hack" only together some basic operations, for debug log is printed correctly. Here are some sample logs generated by the current version of the tool (at the time of writing):

First test - BK7231T_QIO_Woox_R5111_2023-14-10-23-46-06:

Running simulator (UART Only) for: W:\GIT\BekenSimulator\references\FlashDumps\IoT\BK7231T\BK7231T_QIO_Woox_R5111_2023-14-10-23-46-06.bin
prvHeapInit-start addr:0x4203d0, size:130096
[01-01 18:12:15 TUYA Info][mqc_app.c:175] mqc app init ...
[01-01 18:12:15 TUYA Info][sf_mqc_cb.c:42] register mqc app callback
[01-01 18:12:15 TUYA Debug][mqc_app.c:118] mq_pro:5 mqc_handler_cnt:1
[01-01 18:12:15 TUYA Debug][mqc_app.c:118] mq_pro:31 mqc_handler_cnt:2
[01-01 18:12:15 TUYA Err][uf_flash_file_app.c:411] uf_get_size err,filepath:log_seq_stat,ret:4
[01-01 18:12:15 TUYA Err][uf_flash_file_app.c:266] uf_open log_seq_stat err 4
[01-01 18:12:15 TUYA Err][log_seq.c:701] log stats open fail.
[01-01 18:12:15 TUYA Debug][svc_online_log.c:288] svc online log init success
[01-01 18:12:15 TUYA Err][tuya_ws_db.c:314] kvs_read fails gw_bi -1
[01-01 18:12:15 TUYA Err][ws_db_gw.c:111] gw base read fails -935
[01-01 18:12:15 TUYA Debug][tuya_bt_sdk.c:89] ty bt cmmod register finish 1
[01-01 18:12:15 TUYA Debug][tuya_ble_api.c:301] ble sdk inited
!!!!!!!!!!tuya_bt_port_init
No TLV header found in flash
No TLV header found in flash
[01-01 18:12:15 TUYA Debug][tuya_ble_api.c:337] ble sdk re_inited
[01-01 18:12:15 TUYA Notice][tuya_bt_sdk.c:130] ty bt sdk init success finish
[01-01 18:12:15 TUYA Notice][light_system.c:2196] go to pre device!
[01-01 18:12:15 TUYA Err][uf_flash_file_app.c:266] uf_open oem_cfg err 4
[01-01 18:12:15 TUYA Err][soc_flash.c:196] uf file can't open and read data!
[01-01 18:12:15 TUYA Err][user_flash.c:154] oem cfg data read error!
[01-01 18:12:15 TUYA Err][device_config_load.c:444] oem data ERROR
[01-01 18:12:15 TUYA Notice][light_init.c:38] oem cfg load error!
[01-01 18:12:15 TUYA Err][light_init.c:241] Light hardware init error!
[01-01 18:12:15 TUYA Notice][light_system.c:2199] Fast boot error!
[01-01 18:12:15 TUYA Notice][simple_flash.c:432] key_addr: 0x1ee000   block_sz 4096
[01-01 18:12:15 TUYA Err][simple_flash.c:452] key data is not matched magic(0x3a2d3e45),crc32(0xe2d8782c)
[01-01 18:12:15 TUYA Notice][simple_flash.c:486] init key:
0xcb 0x4e 0x3e 0xa4 0x0 0x30 0x9d 0xab 0x65 0x6d 0x8d 0xbf 0xe4 0xb9 0x3f 0x35
[01-01 18:12:15 TUYA Err][simple_flash.c:691] user area read != write! 6
[01-01 18:12:15 TUYA Err][tuya_ws_db.c:442] db init fails -1
[01-01 18:12:15 TUYA Err][tuya_iot_com_api.c:311] db init fails -1
[01-01 18:12:15 TUYA Notice][tuya_main.c:311] **********[oem_bk7231s_light_ty] [2.9.6] compiled at Oct 29 2020 14:38:00**********
No TLV header found in flash
No TLV header found in flash
[bk]tx_txdesc_flush
[rx_iq]rx_amp_err_rd: 0xffffffe8
[rx_iq]rx_phase_err_rd: 0xffffffcb
[rx_iq]rx_ty2_rd: 0x001
*********** finally result **********
gtx_dcorMod            : 0x8
gtx_dcorPA             : 0xa
gtx_pre_gain           : 0xe
gtx_i_dc_comp          : 0x241
gtx_q_dc_comp          : 0x241
gtx_i_gain_comp        : 0x3ff
gtx_q_gain_comp        : 0x3ff
gtx_ifilter_corner over: 0x3d
gtx_qfilter_corner over: 0x3d
gtx_phase_comp         : 0x2ff
gtx_phase_ty2          : 0x221
gbias_after_cal        : 0x2
gav_tssi               : 0x2
g_rx_dc_gain_tab 0 over: 0x1ff01ff
g_rx_dc_gain_tab 1 over: 0x1ff01ff
g_rx_dc_gain_tab 2 over: 0x1ff01ff
g_rx_dc_gain_tab 3 over: 0x1ff01ff
g_rx_dc_gain_tab 4 over: 0x1ff01ff
g_rx_dc_gain_tab 5 over: 0x1ff01ff
g_rx_dc_gain_tab 6 over: 0x1ff01ff
g_rx_dc_gain_tab 7 over: 0x1ff01ff
grx_amp_err_wr         : 0x20c
grx_phase_err_wr       : 0x3e6
**************************************
NO TXID found in flash, use def temp:280
temp in flash is:280
NO TXID found in flash, use lpf i&q:61, 61
NO TXID found in flash, use def xtal:25
xtal in flash is:25
-----pwr_gain:12, g_idx:12, shift_b:0, shift_g:0
-----[pwr_gain]12
Initializing TCP/IP stack



Second test - OOBK 1.18.300:

Running simulator (UART Only) for: W:\GIT\BekenSimulator\firmwares\OpenBK7231T_QIO_1.18.300.bin
prvHeapInit-start addr:0x41f390, size:134256
OpenBK7231T, version 1.18.300
Entering initLog()...
Commands registered!
initLog() done!
Info:MAIN:Main_Init_Before_Delay
Main_Init_Before_Delay
Warn:CFG:CFG_InitAndLoad: Config crc or ident mismatch. Default config will be loaded.
No TLV header found in flash
No TLV header found in flash
Info:CFG:CFG_SetDefaultLEDCorrectionTable: setting defaults
Info:NTP:CLOCK driver initialized.
Error:CMD:lfs is absent
Info:GEN:PIN_SetupPins pins have been set up.
Info:MAIN:Main_Init_Before_Delay done
Main_Init_Before_Delay done
Info:MAIN:Main_Init_Delay
Main_Init_Delay
No TLV header found in flash
No TLV header found in flash
[bk]tx_txdesc_flush
[rx_iq]rx_amp_err_rd: 0xffffffe8
[rx_iq]rx_phase_err_rd: 0xffffffcb
[rx_iq]rx_ty2_rd: 0x001
*********** finally result **********
gtx_dcorMod            : 0x8
gtx_dcorPA             : 0xa
gtx_pre_gain           : 0xe
gtx_i_dc_comp          : 0x241
gtx_q_dc_comp          : 0x241
gtx_i_gain_comp        : 0x3ff
gtx_q_gain_comp        : 0x3ff
gtx_ifilter_corner over: 0x3d
gtx_qfilter_corner over: 0x3d
gtx_phase_comp         : 0x2ff
gtx_phase_ty2          : 0x221
gbias_after_cal        : 0x2
gav_tssi               : 0x2
g_rx_dc_gain_tab 0 over: 0x1ff01ff
g_rx_dc_gain_tab 1 over: 0x1ff01ff
g_rx_dc_gain_tab 2 over: 0x1ff01ff
g_rx_dc_gain_tab 3 over: 0x1ff01ff
g_rx_dc_gain_tab 4 over: 0x1ff01ff
g_rx_dc_gain_tab 5 over: 0x1ff01ff
g_rx_dc_gain_tab 6 over: 0x1ff01ff
g_rx_dc_gain_tab 7 over: 0x1ff01ff
grx_amp_err_wr         : 0x20c
grx_phase_err_wr       : 0x3e6
**************************************
NO TXID found in flash, use def temp:280
temp in flash is:280
NO TXID found in flash, use lpf i&q:61, 61
NO TXID found in flash, use def xtal:25
xtal in flash is:25
-----pwr_gain:12, g_idx:12, shift_b:0, shift_g:0
-----[pwr_gain]12
Initializing TCP/IP stack
Info:MAIN:Main_Init_Delay done
Main_Init_Delay done
Info:MAIN:Main_Init_After_Delay
Info:MAIN:Using SSID []
Info:MAIN:Using Pass []
Error:HTTP:Created HTTP SV thread with (stack=2048)
Info:MQTT:MQTT_RegisterCallback called for bT obk8C000000/ subT obk8C000000/+/set
Info:MQTT:MQTT_RegisterCallback called for bT bekens_t/ subT bekens_t/+/set
Info:MQTT:MQTT_RegisterCallback called for bT cmnd/obk8C000000/ subT cmnd/obk8C000000/+
Info:MQTT:MQTT_RegisterCallback called for bT cmnd/bekens_t/ subT cmnd/bekens_t/+
Info:MQTT:MQTT_RegisterCallback called for bT obk8C000000/ subT obk8C000000/+/get
Info:MQTT:MQTT_RegisterCallback called for bT homeassistant/ subT homeassistant/+
Info:CMD:CMD_StartScript: started @startup at the beginning
Error:CMD:LFS_ReadFile: lfs is absent
Info:CMD:CMD_StartScript: failed to get file autoexec.bat
Info:MAIN:Main_Init_After_Delay done




Test with bootloader - one of Woox:

Running simulator (UART Only) for: W:\GIT\BekenSimulator\references\FlashDumps\IoT\BK7231T\BK7231T_QIO_Woox_R5111_2023-14-10-23-46-06.bin

V:BK7231S_1.0.5
CPSR:400001D3
R0:00000000
R1:00000000
R2:00000000
R3:00000000
R4:00000000
R13:00000000
R14(LR):00000000
ST:00000000
J 0x10000
prvHeapInit-start addr:0x4203d0, size:130096
[01-01 18:12:15 TUYA Info][mqc_app.c:175] mqc app init ...
[01-01 18:12:15 TUYA Info][sf_mqc_cb.c:42] register mqc app callback
[01-01 18:12:15 TUYA Debug][mqc_app.c:118] mq_pro:5 mqc_handler_cnt:1
[01-01 18:12:15 TUYA Debug][mqc_app.c:118] mq_pro:31 mqc_handler_cnt:2
[01-01 18:12:15 TUYA Err][uf_flash_file_app.c:411] uf_get_size err,filepath:log_seq_stat,ret:4
[01-01 18:12:15 TUYA Err][uf_flash_file_app.c:266] uf_open log_se


I've also went futher and decided to try testing some math operations. Integer, and floating points. I've added following snippet to Main_Init_Before_Delay:
Code: C / C++
Log in, to see the code

It runs just fine and prints expected output:

Running simulator (UART Only) for: W:\GIT\BekenSimulator\firmwares\OpenBK7231T_QIO_1.18.300_mathDemo.bin
prvHeapInit-start addr:0x41f390, size:134256
OpenBK7231T, version 2178_merge_f1fd73da79fc
Entering initLog()...
Commands registered!
initLog() done!
Info:MAIN:Main_Init_Before_Delay
Main_Init_Before_Delay
Info:MAIN:integer test
Info:MAIN:a = 123
Info:MAIN:float test
Info:MAIN:x = 3.140000
Info:MAIN:y = 1.860000
Info:MAIN:x + y = 5.000000
Warn:CFG:CFG_InitAndLoad: Config crc or ident mismatch. Default config will be loaded.

thus proving that the emulator is correctly executing the soft-float routines (libgcc) compiled into the firmware, meaning the fundamental ARM instruction emulation is flawless!

So, summing up, basic emulation of the BK7231 series is entirely feasible and brings a really promising set of possibilities for the future. We could potentially run automated tests for OpenBeken, debug crashes without needing hardware, or even just learn more about how the Tuya SDK interacts with the baseband. There's still a ton of work to do on mocking the hardware registers, peripherals, and getting Wi-Fi initialized, but we've already gotten past the bootloader and into the main application logic. The next goal is probably to get past Info:MAIN:Main_Init_After_Delay done and into the "on every second" OpenBeken loop, because it doesn't seem to be reaching it yet... needs more investigation.

If anyone wants to play around with this, check out the code on GitHub. Feel free to contribute or just run it to see the magic happen.

Repository:
https://github.com/openshwprojects/BekenEmulator

Added after 3 [hours] 7 [minutes]:

The current question is how to get into the main loop. I've found some potential stalling loops, but some of them must still remain.

Running simulator (UART Only) for: W:\GIT\BekenSimulator\firmwares\OpenBK7231T_QIO_1.18.300.bin
Warning: 'bootloader' RBL container not found. Assuming raw payload and applying default flash decryption.
Warning: 'app' RBL container not found. Assuming raw payload and applying default flash decryption.
prvHeapInit-start addr:0x41f390, size:134256
OpenBK7231T, version 1.18.300
Entering initLog()...
Commands registered!
initLog() done!
Info:MAIN:Main_Init_Before_Delay
Main_Init_Before_Delay
Warn:CFG:CFG_InitAndLoad: Config crc or ident mismatch. Default config will be loaded.
No TLV header found in flash
No TLV header found in flash
Info:CFG:CFG_SetDefaultLEDCorrectionTable: setting defaults
Info:NTP:CLOCK driver initialized.
Error:CMD:lfs is absent
Info:GEN:PIN_SetupPins pins have been set up.
Info:MAIN:Main_Init_Before_Delay done
Main_Init_Before_Delay done
Info:MAIN:Main_Init_Delay
Main_Init_Delay
No TLV header found in flash
No TLV header found in flash
[bk]tx_txdesc_flush
[rx_iq]rx_amp_err_rd: 0xffffffe8
[rx_iq]rx_phase_err_rd: 0xffffffcb
[rx_iq]rx_ty2_rd: 0x001
*********** finally result **********
gtx_dcorMod            : 0x8
gtx_dcorPA             : 0xa
gtx_pre_gain           : 0xe
gtx_i_dc_comp          : 0x241
gtx_q_dc_comp          : 0x241
gtx_i_gain_comp        : 0x3ff
gtx_q_gain_comp        : 0x3ff
gtx_ifilter_corner over: 0x3d
gtx_qfilter_corner over: 0x3d
gtx_phase_comp         : 0x2ff
gtx_phase_ty2          : 0x221
gbias_after_cal        : 0x2
gav_tssi               : 0x2
g_rx_dc_gain_tab 0 over: 0x1ff01ff
g_rx_dc_gain_tab 1 over: 0x1ff01ff
g_rx_dc_gain_tab 2 over: 0x1ff01ff
g_rx_dc_gain_tab 3 over: 0x1ff01ff
g_rx_dc_gain_tab 4 over: 0x1ff01ff
g_rx_dc_gain_tab 5 over: 0x1ff01ff
g_rx_dc_gain_tab 6 over: 0x1ff01ff
g_rx_dc_gain_tab 7 over: 0x1ff01ff
grx_amp_err_wr         : 0x20c
grx_phase_err_wr       : 0x3e6
**************************************
NO TXID found in flash, use def temp:280
temp in flash is:280
NO TXID found in flash, use lpf i&q:61, 61
NO TXID found in flash, use def xtal:25
xtal in flash is:25
-----pwr_gain:12, g_idx:12, shift_b:0, shift_g:0
-----[pwr_gain]12
Initializing TCP/IP stack
Info:MAIN:Main_Init_Delay done
Main_Init_Delay done
Info:MAIN:Main_Init_After_Delay
Info:MAIN:Using SSID []
Info:MAIN:Using Pass []
Error:HTTP:Created HTTP SV thread with (stack=2048)
Info:MQTT:MQTT_RegisterCallback called for bT obk8C000000/ subT obk8C000000/+/set
Info:MQTT:MQTT_RegisterCallback called for bT bekens_t/ subT bekens_t/+/set
Info:MQTT:MQTT_RegisterCallback called for bT cmnd/obk8C000000/ subT cmnd/obk8C000000/+
Info:MQTT:MQTT_RegisterCallback called for bT cmnd/bekens_t/ subT cmnd/bekens_t/+
Info:MQTT:MQTT_RegisterCallback called for bT obk8C000000/ subT obk8C000000/+/get
Info:MQTT:MQTT_RegisterCallback called for bT homeassistant/ subT homeassistant/+
Info:CMD:CMD_StartScript: started @startup at the beginning
Error:CMD:LFS_ReadFile: lfs is absent
Info:CMD:CMD_StartScript: failed to get file autoexec.bat
Info:MAIN:Main_Init_After_Delay done
Emulation finished. PC: 0x0006627c, CPSR: 0x0000003f (T-bit: 1)
Time taken: 2717.23 seconds.


Or maybe it really takes so much instructions...

About Author
p.kaczmarek2
p.kaczmarek2 wrote 14790 posts with rating 12934 , helped 659 times. Been with us since 2014 year.

Comments

%}