diff --git a/ISSUES.md b/ISSUES.md new file mode 100644 index 0000000..30116b8 --- /dev/null +++ b/ISSUES.md @@ -0,0 +1,47 @@ +# Known Issues + +## SD reads can corrupt guest memory + +Opening `/Test/example.txt` can panic consistently: + +```text +Invalid read at addr 0x253D7474, size 4, region '(null)' +Guru Meditation Error: Core 0 panic'ed (Load access fault) +``` + +The fault occurs in `spiTransferByteNL()` after the `SdSpiCard` SPI pointer at `0x3fc94b40` becomes `0x253D7474`, which resembles file data. The crash is deterministic with the directory-backed `vvfat` card. A raw FAT32 diagnostic image fails to mount, so it has not yet provided a clean backend comparison. + +The crash dump also shows `SdSpiCard::readData()` reaching its CRC reads while its destination pointer and end pointer differ. Keeping conditional translation blocks on TCI did not fix the corruption. The next diagnostic build keeps guest-store TBs on TCI and watches writes to `0x3fc94b40`–`0x3fc94b57`. + +## SD card can remain in `receivingdata` + +The SD model can become stuck after an incomplete `CMD24` write: + +```text +SPI: CMD17 in a wrong state: receivingdata (spec v3.01) +SPI: CMD24 in a wrong state: receivingdata (spec v3.01) +``` + +Subsequent reads and writes then fail. This may be related to the guest-memory corruption, but remains tracked separately until the first incomplete transfer is identified. + +## Reader page can render completely black + +Opening a generated crash report can appear to work—the Back button returns normally and the file is marked as read—but the reader page is entirely black. + +This is likely an e-ink model issue where an intermediate all-black waveform phase is exposed instead of the completed frame. It is separate from the SD pointer panic. + +## Cold boot requires a manual power-button hold + +If the power button is not held until the Booting icon appears, the firmware can enter an unwanted watchdog reboot path: + +```text +Guru Meditation Error: Core 0 panic'ed (Interrupt wdt timeout on CPU0) +``` + +The emulator should model a cold-boot power hold and release automatically rather than requiring precisely timed user input. The preceding NVS `NOT_FOUND` messages are expected first-boot initialization noise. + +## WASM heap balloons after watchdog reboot + +The committed WASM heap starts near 176.3 MiB but can grow to approximately 631.8 MiB after the watchdog reboot described above. + +This indicates translated-block or dynamic WebAssembly module state is retained across guest reboot. TB cleanup must release old dynamic instances during reset; reboot-time growth should remain bounded. diff --git a/Makefile b/Makefile index 294fe58..f3538bb 100644 --- a/Makefile +++ b/Makefile @@ -6,10 +6,14 @@ SD_SRC ?= $(FIRMWARE_REPO)/sdcard QEMU_SRC ?= _scratch/qemu-src QEMU_BIN = $(QEMU_SRC)/build/qemu-system-riscv32 QEMU_WASM_SRC ?= _scratch/qemu-wasm-src +# Working tree for the fast dev loop: edit here, `make web-dev` compiles it incrementally. +DEV_SRC ?= _scratch/qemu-fix-src +# Commit the 0002 patch is diffed against when running `make patch`. +PATCH_BASE ?= 610f8c69bc91dcb4ba81de1b7d2e74cc2adfb31e WASM_OUT ?= dist/wasm VARIANT ?= x3 -.PHONY: firmware sdimage qemu qemu-wasm web web-test run run-upstream chip clean +.PHONY: firmware sdimage qemu qemu-wasm qemu-wasm-dev web web-dev web-test patch run run-upstream chip clean firmware: esptool --chip esp32c3 merge-bin -o flash.bin \ @@ -29,11 +33,23 @@ qemu: qemu-wasm: QEMU_WASM_SRC=$(QEMU_WASM_SRC) WASM_OUT=$(WASM_OUT) scripts/build-qemu-wasm.sh +# Incremental build straight from $(DEV_SRC); no patch reset, so edits compile fast. +qemu-wasm-dev: + QEMU_WASM_DEV=1 QEMU_WASM_SRC=$(DEV_SRC) WASM_OUT=$(WASM_OUT) scripts/build-qemu-wasm.sh + +# Regenerate the wasm patch from $(DEV_SRC) once the dev tree is where you want it. +patch: + cd $(DEV_SRC) && git diff --binary $(PATCH_BASE) > $(CURDIR)/qemu/patches/0002-qemu-wasm.patch + # Serve over HTTPS by default so headless/LAN access is cross-origin isolated. Override with WEB_TLS=. WEB_TLS ?= --tls web: qemu-wasm python3 scripts/serve-web.py $(WEB_TLS) +# Fast iteration: incremental compile from $(DEV_SRC), then serve. Run `make patch` before committing. +web-dev: qemu-wasm-dev + python3 scripts/serve-web.py $(WEB_TLS) + web-test: node scripts/test-web.mjs diff --git a/qemu/patches/0002-qemu-wasm.patch b/qemu/patches/0002-qemu-wasm.patch index 325c7c6..60481ca 100644 --- a/qemu/patches/0002-qemu-wasm.patch +++ b/qemu/patches/0002-qemu-wasm.patch @@ -303,7 +303,7 @@ index 90fa54352c..0fa9cc31b6 100644 /* * parse_zone - Fill a zone descriptor diff --git c/block/vvfat.c w/block/vvfat.c -index 8ffe8b3b9b..4cf02d9a55 100644 +index 8ffe8b3b9b..3c0ab6c228 100644 --- c/block/vvfat.c +++ w/block/vvfat.c @@ -749,6 +749,7 @@ static int read_directory(BDRVVVFATState* s, int mapping_index) @@ -353,7 +353,7 @@ index 8ffe8b3b9b..4cf02d9a55 100644 s->directory.next < s->root_entries) { /* root directory */ int cur = s->directory.next; -@@ -877,20 +878,24 @@ static int read_directory(BDRVVVFATState* s, int mapping_index) +@@ -877,20 +878,29 @@ static int read_directory(BDRVVVFATState* s, int mapping_index) * 0x20 / s->cluster_size; mapping->end = first_cluster; @@ -370,7 +370,12 @@ index 8ffe8b3b9b..4cf02d9a55 100644 static inline int32_t sector2cluster(BDRVVVFATState* s,off_t sector_num) { - return (sector_num - s->offset_to_root_dir) / s->sectors_per_cluster; -+ return (sector_num - s->offset_to_root_dir) / s->sectors_per_cluster ++ off_t relative_sector = sector_num - s->offset_to_root_dir; ++ ++ if (relative_sector < 0) { ++ return -1 - (-relative_sector - 1) / s->sectors_per_cluster; ++ } ++ return relative_sector / s->sectors_per_cluster + + (s->fat_type == 32 ? 2 : 0); } @@ -382,7 +387,7 @@ index 8ffe8b3b9b..4cf02d9a55 100644 } static int init_directories(BDRVVVFATState* s, -@@ -934,11 +939,12 @@ static int init_directories(BDRVVVFATState* s, +@@ -934,11 +944,12 @@ static int init_directories(BDRVVVFATState* s, init_fat(s); /* TODO: if there are more entries, bootsector has to be adjusted! */ @@ -397,7 +402,7 @@ index 8ffe8b3b9b..4cf02d9a55 100644 mapping->dir_index = 0; mapping->info.dir.parent_mapping_index = -1; mapping->first_mapping_index = -1; -@@ -950,11 +956,10 @@ static int init_directories(BDRVVVFATState* s, +@@ -950,11 +961,10 @@ static int init_directories(BDRVVVFATState* s, mapping->read_only = 0; s->path = mapping->path; @@ -413,7 +418,7 @@ index 8ffe8b3b9b..4cf02d9a55 100644 mapping = array_get(&(s->mapping), i); if (mapping->mode & MODE_DIRECTORY) { -@@ -1026,22 +1031,35 @@ static int init_directories(BDRVVVFATState* s, +@@ -1026,22 +1036,35 @@ static int init_directories(BDRVVVFATState* s, /* media descriptor: hard disk=0xf8, floppy=0xf0 */ bootsector->media_type = (s->offset_to_bootsector > 0 ? 0xf8 : 0xf0); s->fat.pointer[0] = bootsector->media_type; @@ -460,7 +465,7 @@ index 8ffe8b3b9b..4cf02d9a55 100644 bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa; return 0; -@@ -1139,6 +1157,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, +@@ -1139,6 +1162,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, { BDRVVVFATState *s = bs->opaque; int cyls, heads, secs; @@ -468,7 +473,7 @@ index 8ffe8b3b9b..4cf02d9a55 100644 bool floppy; const char *dirname, *label; QemuOpts *opts; -@@ -1205,7 +1224,9 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, +@@ -1205,7 +1229,9 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, switch (s->fat_type) { case 32: @@ -478,7 +483,7 @@ index 8ffe8b3b9b..4cf02d9a55 100644 break; case 16: case 12: -@@ -1219,8 +1240,17 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, +@@ -1219,8 +1245,17 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, s->bs = bs; @@ -498,7 +503,7 @@ index 8ffe8b3b9b..4cf02d9a55 100644 s->current_cluster=0xffffffff; -@@ -1232,8 +1262,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, +@@ -1232,8 +1267,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, DLOG(fprintf(stderr, "vvfat %s chs %d,%d,%d\n", dirname, cyls, heads, secs)); @@ -509,16 +514,123 @@ index 8ffe8b3b9b..4cf02d9a55 100644 if (qemu_opt_get_bool(opts, "rw", false)) { if (!bdrv_is_read_only(bs)) { -@@ -1523,7 +1553,8 @@ vvfat_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sector +@@ -1259,8 +1294,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, + goto fail; + } + +- s->sector_count = s->offset_to_root_dir +- + s->sectors_per_cluster * s->cluster_count; ++ s->sector_count = cluster2sector(s, s->cluster_count); + + /* Disable migration when vvfat is used rw */ + if (s->qcow) { +@@ -1415,6 +1449,7 @@ read_cluster_directory: + if(lseek(s->current_fd, offset, SEEK_SET)!=offset) + return -3; + s->cluster=s->cluster_buffer; ++ memset(s->cluster, 0, s->cluster_size); + result=read(s->current_fd,s->cluster,s->cluster_size); + if(result<0) { + s->current_cluster = -1; +@@ -1523,8 +1558,9 @@ vvfat_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sector } else { uint32_t sector = sector_num - s->offset_to_root_dir, sector_offset_in_cluster=(sector%s->sectors_per_cluster), - cluster_num=sector/s->sectors_per_cluster; +- if(cluster_num > s->cluster_count || read_cluster(s, cluster_num) != 0) { + cluster_num=sector/s->sectors_per_cluster + + (s->fat_type == 32 ? 2 : 0); - if(cluster_num > s->cluster_count || read_cluster(s, cluster_num) != 0) { ++ if(cluster_num >= s->cluster_count || read_cluster(s, cluster_num) != 0) { /* LATER TODO: strict: return -1; */ memset(buf+i*0x200,0,0x200); + continue; +@@ -1780,7 +1816,8 @@ static int parse_short_name(BDRVVVFATState* s, + static inline uint32_t modified_fat_get(BDRVVVFATState* s, + unsigned int cluster) + { +- if (cluster < s->last_cluster_of_root_directory) { ++ if (s->fat_type != 32 && ++ cluster < s->last_cluster_of_root_directory) { + if (cluster + 1 == s->last_cluster_of_root_directory) + return s->max_fat_value; + else +@@ -2182,14 +2219,17 @@ DLOG(checkpoint()); + mapping->mode |= MODE_DELETED; + } + +- used_clusters_count = check_directory_consistency(s, 0, s->path); ++ mapping_t *root_mapping = array_get(&(s->mapping), 0); ++ used_clusters_count = check_directory_consistency(s, ++ root_mapping->begin, s->path); + if (used_clusters_count <= 0) { + DLOG(fprintf(stderr, "problem in directory\n")); + return 0; + } + +- check = s->last_cluster_of_root_directory; +- for (i = check; i < sector2cluster(s, s->sector_count); i++) { ++ check = s->fat_type == 32 ? 0 : s->last_cluster_of_root_directory; ++ for (i = s->fat_type == 32 ? 2 : check; ++ i < sector2cluster(s, s->sector_count); i++) { + if (modified_fat_get(s, i)) { + if(!s->used_clusters[i]) { + DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i)); +@@ -2417,7 +2457,10 @@ static int coroutine_fn GRAPH_RDLOCK + commit_direntries(BDRVVVFATState* s, int dir_index, int parent_mapping_index) + { + direntry_t* direntry = array_get(&(s->directory), dir_index); +- uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry); ++ bool is_root = dir_index == 0; ++ mapping_t *root_mapping = array_get(&(s->mapping), 0); ++ uint32_t first_cluster = is_root ? root_mapping->begin : ++ begin_of_direntry(direntry); + mapping_t* mapping = find_mapping_for_cluster(s, first_cluster); + int factor = 0x10 * s->sectors_per_cluster; + int old_cluster_count, new_cluster_count; +@@ -2440,7 +2483,7 @@ commit_direntries(BDRVVVFATState* s, int dir_index, int parent_mapping_index) + first_dir_index = current_dir_index; + mapping->info.dir.parent_mapping_index = parent_mapping_index; + +- if (first_cluster == 0) { ++ if (is_root && s->fat_type != 32) { + old_cluster_count = new_cluster_count = + s->last_cluster_of_root_directory; + } else { +@@ -2998,11 +3041,9 @@ DLOG(checkpoint()); + */ + unsigned char *bootsector = s->first_sectors + + s->offset_to_bootsector * 0x200; +- /* +- * LATER TODO: if FAT32, this is wrong (see init_directories(), +- * which always creates a FAT16 bootsector) +- */ +- const int reserved1_offset = offsetof(bootsector_t, u.fat16.reserved1); ++ const int reserved1_offset = s->fat_type == 32 ? ++ offsetof(bootsector_t, u.fat32.reserved1) : ++ offsetof(bootsector_t, u.fat16.reserved1); + + for (i = 0; i < 0x200; i++) { + if (i != reserved1_offset && bootsector[i] != buf[i]) { +@@ -3057,8 +3098,8 @@ DLOG(checkpoint()); + begin = sector_num; + if (end > sector_num + nb_sectors) + end = sector_num + nb_sectors; +- dir_index = mapping->dir_index + +- 0x10 * (begin - mapping->begin * s->sectors_per_cluster); ++ dir_index = mapping->info.dir.first_dir_index + ++ 0x10 * (begin - cluster2sector(s, mapping->begin)); + direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num)); + + for (k = 0; k < (end - begin) * 0x10; k++) { +@@ -3093,7 +3134,7 @@ DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sec + } + + for (i = first_cluster; i <= last_cluster; i++) { +- if (i >= 0) { ++ if (i >= 0 && i < sector2cluster(s, s->sector_count)) { + s->used_clusters[i] |= USED_ALLOCATED; + } + } diff --git c/configs/devices/riscv32-softmmu/xteink.mak w/configs/devices/riscv32-softmmu/xteink.mak new file mode 100644 index 0000000000..a9179b4119 @@ -1135,6 +1247,21 @@ index c1376b7299..1275808dc0 100644 { level_shared |= 1; } +diff --git c/hw/sd/ssi-sd.c w/hw/sd/ssi-sd.c +index 552dc2c8ea..eb12207897 100644 +--- c/hw/sd/ssi-sd.c ++++ w/hw/sd/ssi-sd.c +@@ -162,6 +162,10 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) + return SSI_DUMMY; + } + ++ if ((val & 0xc0) != 0x40) { ++ DPRINTF("Ignore non-command byte 0x%02x\n", val); ++ return SSI_DUMMY; ++ } + s->cmd = val & 0x3f; + s->mode = SSI_SD_CMDARG; + s->arglen = 0; diff --git c/include/exec/exec-all.h w/include/exec/exec-all.h index 2e4c4cc4b4..99e6da08c9 100644 --- c/include/exec/exec-all.h @@ -1616,7 +1743,7 @@ index 478ec051c4..41895c68fa 100644 need_prot |= host_prot_read_exec(); } diff --git c/tcg/tcg.c w/tcg/tcg.c -index 6dc3072f92..214e66e9d6 100644 +index 6dc3072f92..0f6c0f5082 100644 --- c/tcg/tcg.c +++ w/tcg/tcg.c @@ -131,6 +131,10 @@ static void tcg_out_goto_tb(TCGContext *s, int which); @@ -2027,7 +2154,7 @@ index 6dc3072f92..214e66e9d6 100644 #ifdef TCG_TARGET_NEED_LDST_LABELS QSIMPLEQ_INIT(&s->ldst_labels); #endif -@@ -6286,13 +6599,88 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) +@@ -6286,13 +6599,94 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) return -2; } @@ -2040,6 +2167,12 @@ index 6dc3072f92..214e66e9d6 100644 #endif +#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ if (wasm_tci_only_tb) { ++ for (int i = 0; i < get_core_nums(); i++) { ++ export_vec_base[i] = WASM_TCI_ONLY_ENTRY; ++ } ++ } ++ + tcg_sub_out8(s, 0x0b); //end if + tcg_sub_out8(s, 0x0b); //end loop + tcg_sub_out8(s, 0x0); // unreachable @@ -2119,10 +2252,10 @@ index 6dc3072f92..214e66e9d6 100644 diff --git c/tcg/wasm32.c w/tcg/wasm32.c new file mode 100644 -index 0000000000..55ad790f17 +index 0000000000..19b31ed4df --- /dev/null +++ w/tcg/wasm32.c -@@ -0,0 +1,1246 @@ +@@ -0,0 +1,1275 @@ +/* + * Tiny Code Generator for QEMU + * @@ -2154,8 +2287,10 @@ index 0000000000..55ad790f17 +#include +#include +#include "wasm32.h" ++#include "exec/translation-block.h" + +__thread uintptr_t tci_tb_ptr; ++__thread bool wasm_tci_only_tb; + +/* Disassemble TCI bytecode. */ +int print_insn_tci(bfd_vma addr, disassemble_info *info) @@ -2191,12 +2326,16 @@ index 0000000000..55ad790f17 + }); + var ptr = export_vec_begin + 4 * Module.__wasm32_tb.cur_core_num; + const fidx = addFunction(inst.exports.start, 'ii'); ++ Module.__wasm32_tb.dynamic_functions.add(fidx); + memory_v.setUint32(ptr, fidx, true); + + const remove_n = memory_v.getInt32(Module.__wasm32_tb.to_remove_instance_idx_ptr, true); + if (remove_n > 500) { + for (var i = 0; i < remove_n * 4; i += 4) { -+ removeFunction(memory_v.getInt32(Module.__wasm32_tb.to_remove_instance_ptr + i, true)); ++ const fidx = memory_v.getInt32(Module.__wasm32_tb.to_remove_instance_ptr + i, true); ++ if (Module.__wasm32_tb.dynamic_functions.delete(fidx)) { ++ removeFunction(fidx); ++ } + } + memory_v.setInt32(Module.__wasm32_tb.to_remove_instance_idx_ptr, 0, true); + } @@ -2212,7 +2351,10 @@ index 0000000000..55ad790f17 + const memory_v = new DataView(HEAP8.buffer); + const remove_n = memory_v.getInt32(tb.to_remove_instance_idx_ptr, true); + for (var i = 0; i < remove_n * 4; i += 4) { -+ removeFunction(memory_v.getInt32(tb.to_remove_instance_ptr + i, true)); ++ const fidx = memory_v.getInt32(tb.to_remove_instance_ptr + i, true); ++ if (tb.dynamic_functions.delete(fidx)) { ++ removeFunction(fidx); ++ } + } + memory_v.setInt32(tb.to_remove_instance_idx_ptr, 0, true); +}); @@ -2272,6 +2414,7 @@ index 0000000000..55ad790f17 + cur_core_num: cur_core_num, + to_remove_instance_ptr: to_remove_instance_ptr, + to_remove_instance_idx_ptr: to_remove_instance_idx_ptr, ++ dynamic_functions: new Set(), + }; +}); + @@ -2627,6 +2770,19 @@ index 0000000000..55ad790f17 + + uint64_t target_addr = tlb_load(env, taddr, mop, ptr, false); + if (target_addr != 0) { ++ uint64_t mem_bytes = (uint64_t)__builtin_wasm_memory_size(0) << 16; ++ if (target_addr + (1u << (mop & MO_SIZE)) > mem_bytes) { ++ TranslationBlock *tb = tcg_tb_lookup(ra); ++ fprintf(stderr, ++ "[sd-watch] OOB store pc=%08llx gaddr=%08llx haddr=%08llx size=%u value=%08llx membytes=%08llx\n", ++ (unsigned long long)(tb ? tb->pc : 0), ++ (unsigned long long)taddr, (unsigned long long)target_addr, ++ 1u << (mop & MO_SIZE), (unsigned long long)val, ++ (unsigned long long)mem_bytes); ++ return; ++ } ++ } ++ if (target_addr != 0) { + switch (mop & MO_SIZE) { + case MO_UB: + *(uint8_t*)target_addr = (uint8_t)val; @@ -3252,7 +3408,9 @@ index 0000000000..55ad790f17 + tb_ptr = *(uint32_t **)ptr; + ctx.tb_ptr = tb_ptr; + int tb_entry_ptr = (uint32_t)tb_ptr + export_vec_off; -+ if ((*(int32_t*)tb_entry_ptr <= 0) && (*(int32_t*)tb_entry_ptr > (-1 * INSTANTIATE_NUM))) { ++ if (*(int32_t *)tb_entry_ptr == WASM_TCI_ONLY_ENTRY) { ++ /* ponytail: Store TBs use TCI while tracing SD object corruption. */ ++ } else if ((*(int32_t*)tb_entry_ptr <= 0) && (*(int32_t*)tb_entry_ptr > (-1 * INSTANTIATE_NUM))) { + *(int32_t*)tb_entry_ptr -= 1; + } else { + // enter to wasm TB @@ -3273,7 +3431,9 @@ index 0000000000..55ad790f17 + + ctx.tb_ptr = tb_ptr; + int tb_entry_ptr = (uint32_t)tb_ptr + export_vec_off; -+ if ((*(int32_t*)tb_entry_ptr <= 0) && (*(int32_t*)tb_entry_ptr > (-1 * INSTANTIATE_NUM))) { ++ if (*(int32_t *)tb_entry_ptr == WASM_TCI_ONLY_ENTRY) { ++ /* Continue in TCI. */ ++ } else if ((*(int32_t*)tb_entry_ptr <= 0) && (*(int32_t*)tb_entry_ptr > (-1 * INSTANTIATE_NUM))) { + *(int32_t*)tb_entry_ptr -= 1; + } else { + // enter to wasm TB @@ -3355,6 +3515,8 @@ index 0000000000..55ad790f17 + uint32_t res; + if (*(int32_t*)tb_entry_ptr > 0) { + res = ((wasm_func_ptr)(*(uint32_t*)tb_entry_ptr))(&ctx); ++ } else if (*(int32_t *)tb_entry_ptr == WASM_TCI_ONLY_ENTRY) { ++ res = tcg_qemu_tb_exec_tci(env); + } else if (*(int32_t*)tb_entry_ptr > (-1 * INSTANTIATE_NUM)) { + *(int32_t*)tb_entry_ptr -= 1; + res = tcg_qemu_tb_exec_tci(env); @@ -3371,10 +3533,10 @@ index 0000000000..55ad790f17 +#endif diff --git c/tcg/wasm32.h w/tcg/wasm32.h new file mode 100644 -index 0000000000..1527bf4875 +index 0000000000..a8b525abe3 --- /dev/null +++ w/tcg/wasm32.h -@@ -0,0 +1,46 @@ +@@ -0,0 +1,49 @@ +#ifndef TCG_WASM32_H +#define TCG_WASM32_H + @@ -3418,7 +3580,10 @@ index 0000000000..1527bf4875 + +void init_wasm32(); + ++extern __thread bool wasm_tci_only_tb; ++ +#define INSTANTIATE_NUM 1500 ++#define WASM_TCI_ONLY_ENTRY (-2147483647 - 1) + +#endif diff --git c/tcg/wasm32/tcg-target-con-set.h w/tcg/wasm32/tcg-target-con-set.h @@ -3467,10 +3632,10 @@ index 0000000000..196395aef9 +#endif diff --git c/tcg/wasm32/tcg-target.c.inc w/tcg/wasm32/tcg-target.c.inc new file mode 100644 -index 0000000000..9e82297a3f +index 0000000000..053c941f99 --- /dev/null +++ w/tcg/wasm32/tcg-target.c.inc -@@ -0,0 +1,3824 @@ +@@ -0,0 +1,3826 @@ +/* + * Tiny Code Generator for QEMU + * @@ -6843,6 +7008,7 @@ index 0000000000..9e82297a3f +} +static void tcg_out_qemu_st(TCGContext *s, TCGOpcode opc, const TCGArg *args, bool is_64) +{ ++ wasm_tci_only_tb = true; + tcg_tci_out_qemu_ldst(s, opc, args); + tcg_wasm_out_qemu_st(s, args, is_64); +} @@ -7246,6 +7412,7 @@ index 0000000000..9e82297a3f +void tcg_out_init() { + current_label_pos = 0; + env_cached = false; ++ wasm_tci_only_tb = false; +} + +/* Test if a constant matches the constraint. */ diff --git a/scripts/build-qemu-wasm.sh b/scripts/build-qemu-wasm.sh index ab11aeb..e611844 100755 --- a/scripts/build-qemu-wasm.sh +++ b/scripts/build-qemu-wasm.sh @@ -20,26 +20,32 @@ else ENGINE=docker fi -if [ ! -d "$SRC/.git" ]; then - git clone --depth 1 --branch "$TAG" https://github.com/espressif/qemu "$SRC" -fi +# Dev Mode - Build directly out of an already-patched working tree (e.g. _scratch/qemu-fix-src); +# skip clone/commit-check/patch so edits compile incrementally without the patch round-trip. +if [ -n "${QEMU_WASM_DEV:-}" ]; then + cd "$SRC" +else + if [ ! -d "$SRC/.git" ]; then + git clone --depth 1 --branch "$TAG" https://github.com/espressif/qemu "$SRC" + fi -cd "$SRC" -if [ "$(git rev-parse HEAD)" != "$COMMIT" ]; then - echo "QEMU source must be $COMMIT ($TAG)" >&2 - exit 1 -fi + cd "$SRC" + if [ "$(git rev-parse HEAD)" != "$COMMIT" ]; then + echo "QEMU source must be $COMMIT ($TAG)" >&2 + exit 1 + fi -# Re-apply Patches On Drift - The source tree is a build artifact of the patches; reset and re-apply whenever a patch is newer than the last apply so `make web` never silently rebuilds stale sources. -STAMP=".xteink-patch-stamp" -if [ ! -f hw/display/xteink_x3_eink.c ] || [ ! -f tcg/wasm32.c ] \ - || [ "$ROOT/qemu/patches/0001-xteink-machine.patch" -nt "$STAMP" ] \ - || [ "$ROOT/qemu/patches/0002-qemu-wasm.patch" -nt "$STAMP" ]; then - git reset --hard "$COMMIT" - git clean -fdx -e build - git apply "$ROOT/qemu/patches/0001-xteink-machine.patch" - git apply --whitespace=nowarn "$ROOT/qemu/patches/0002-qemu-wasm.patch" - touch "$STAMP" + # Re-apply Patches On Drift - The source tree is a build artifact of the patches; reset and re-apply whenever a patch is newer than the last apply so `make web` never silently rebuilds stale sources. + STAMP=".xteink-patch-stamp" + if [ ! -f hw/display/xteink_x3_eink.c ] || [ ! -f tcg/wasm32.c ] \ + || [ "$ROOT/qemu/patches/0001-xteink-machine.patch" -nt "$STAMP" ] \ + || [ "$ROOT/qemu/patches/0002-qemu-wasm.patch" -nt "$STAMP" ]; then + git reset --hard "$COMMIT" + git clean -fdx -e build + git apply "$ROOT/qemu/patches/0001-xteink-machine.patch" + git apply --whitespace=nowarn "$ROOT/qemu/patches/0002-qemu-wasm.patch" + touch "$STAMP" + fi fi printf 'Building Emscripten environment (qemu-wasm %s)\n' "$QEMU_WASM_COMMIT"