From 6a715f0f6f315cd02aca9d35e756df1632d5d995 Mon Sep 17 00:00:00 2001 From: Martin Valik Date: Tue, 8 Mar 2022 16:53:45 +0100 Subject: [PATCH 01/10] SLIP implemented --- .cargo/config.toml | 13 ++ .gitignore | 2 + Cargo.lock | 538 ++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 13 ++ LICENSE-APACHE | 201 +++++++++++++++++ LICENSE-MIT | 25 ++ README.md | 8 +- flash | 320 ++++++++++++++++++++++++++ memory.x | 193 ++++++++++++++++ openocd.cfg | 6 + partitions.csv | 3 + ram.x | 4 + rom.x | 4 + rust-toolchain.toml | 4 + src/main.rs | 96 ++++++++ src/protocol.rs | 365 ++++++++++++++++++++++++++++++ 16 files changed, 1794 insertions(+), 1 deletion(-) create mode 100644 .cargo/config.toml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100755 flash create mode 100644 memory.x create mode 100644 openocd.cfg create mode 100644 partitions.csv create mode 100644 ram.x create mode 100644 rom.x create mode 100644 rust-toolchain.toml create mode 100644 src/main.rs create mode 100644 src/protocol.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..349c34f --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,13 @@ +[target.xtensa-esp32-none-elf] +runner = "xtensa-esp32-elf-gdb -q -x xtensa.gdb" + +[build] +target = "xtensa-esp32-none-elf" +[target.'cfg(any(target_arch="xtensa"))'] +rustflags = [ + "-C", "link-arg=-nostartfiles", + "-C", "link-arg=-Wl,-Tlink.x", +] + +# [unstable] +# build-std=["core", "alloc"] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7be846b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +.vscode \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5b0d8c8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,538 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "assert2" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1b167af16149cd41ff2b784bf511bb4208b21c3b05f3f61e30823ce3986361" +dependencies = [ + "assert2-macros", + "atty", + "yansi", +] + +[[package]] +name = "assert2-macros" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6ac27dd1c8f16b282d1c22a8a5ae17119acc757101dec79054458fef62c447e" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn", +] + +[[package]] +name = "atomic-polyfill" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6adc1648f03fbc1bc1b5cf0f2fdfb5edbc96215b711edcfe6ce2641ef9b347" +dependencies = [ + "critical-section", + "riscv-target", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version 0.2.3", +] + +[[package]] +name = "bare-metal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" + +[[package]] +name = "bit_field" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cortex-m" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ff967e867ca14eba0c34ac25cd71ea98c678e741e3915d923999bb2fe7c826" +dependencies = [ + "bare-metal 0.2.5", + "bitfield", + "embedded-hal", + "volatile-register", +] + +[[package]] +name = "critical-section" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70" +dependencies = [ + "bare-metal 1.0.0", + "cfg-if", + "cortex-m", + "riscv", +] + +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "esp32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73f0f4e30155c65e65697f672957343b74ca75b0942f3da51903c11fa220ad1" +dependencies = [ + "bare-metal 1.0.0", + "vcell", + "xtensa-lx", + "xtensa-lx-rt", +] + +[[package]] +name = "esp32-hal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ebf28b2417a279e518343d16c64a94daa915d9e110f2ce0aee8a71d5e1b809" +dependencies = [ + "bare-metal 0.2.5", + "embedded-hal", + "esp32", + "esp32-hal-proc-macros", + "nb 0.1.3", + "void", + "xtensa-lx", + "xtensa-lx-rt", +] + +[[package]] +name = "esp32-hal-proc-macros" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe047c6f062015c28a44de887a711fd2850975ee15452e4de83565b0a5ffdc2" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d076121838e03f862871315477528debffdb7462fb229216ecef91b1a3eb31eb" +dependencies = [ + "atomic-polyfill", + "hash32", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "mutex-trait" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4bb1638d419e12f8b1c43d9e639abd0d1424285bdea2f76aa231e233c63cd3a" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.0.0", +] + +[[package]] +name = "nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r0" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "riscv" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba" +dependencies = [ + "bare-metal 1.0.0", + "bit_field", + "riscv-target", +] + +[[package]] +name = "riscv-target" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.6", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "spin" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + +[[package]] +name = "stub" +version = "0.1.0" +dependencies = [ + "assert2", + "esp32-hal", + "heapless", + "matches", +] + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" +dependencies = [ + "vcell", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "xtensa-lx" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8c07b753b1711deb84033ba74274336117400c7e0f14e454cfeecb7303274f" +dependencies = [ + "bare-metal 1.0.0", + "mutex-trait", + "r0", + "spin", +] + +[[package]] +name = "xtensa-lx-rt" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "158560f495a7d707880b79984e05d3c8f0b5736d3206d27b45c58a5aaf9813b7" +dependencies = [ + "bare-metal 1.0.0", + "r0", + "xtensa-lx-rt-proc-macros", +] + +[[package]] +name = "xtensa-lx-rt-proc-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a8200930e2dbd515c231f7a46033bd6dfe1497a8e9a539878f0de8f0cd730b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "yansi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..498ae1e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "stub" +version = "0.1.0" +edition = "2021" + +[dependencies] +heapless = "0.7.10" +[target.'cfg(target_arch = "xtensa")'.dependencies] +esp32-hal = "0.3.0" + +[dev-dependencies] +assert2 = "0.3.6" +matches = "0.1.9" \ No newline at end of file diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..f8e5e5e --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..b03fd3c --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright 2019-2020 Contributors to xtensa-lx6-rt + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index d96e52b..2589cdf 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ -# esp-flasher-stub \ No newline at end of file +# esp-flasher-stub + +## Build +cargo build -Z build-std=core,alloc + +## Test +cargo test --target=x86_64-unknown-linux-gnu diff --git a/flash b/flash new file mode 100755 index 0000000..999f67d --- /dev/null +++ b/flash @@ -0,0 +1,320 @@ +#!/bin/bash + +# port esp32 is connected to (leave empty to autodetect) +PORT= +#PORT=/dev/ttyUSB1 + +# baud rate for programming +FLASH_BAUDRATE=921600 + +# baud rate for terminal +TERM_BAUDRATE=115200 + +# Flash Mode +FLASH_MODE="dio" + +# Flash Speed +FLASH_SPEED="40m" + +# Use bootloader (needed for using irom, drom and psram) +USE_BOOTLOADER=1 + +# debug or release build +TYPE=debug + +# address of partition table +PARTION_ADDR=0x8000 + +# address of application +APP_ADDR=0x10000 + +#source of the utility to generate the binary partition table +GENPART_SOURCE=https://github.com/espressif/esp-idf/blob/v4.0/components/partition_table/gen_esp32part.py?raw=true + +# source of the bootloader +# customized bootloader which initializes the external psram +BOOTLOADER_SOURCE=https://github.com/arjanmels/esp32_bootloader_init_extram/blob/v1.0/build/bootloader/bootloader.bin?raw=true +# default bootloader from the espressif arduino github +#BOOTLOADER_SOURCE=https://github.com/espressif/arduino-esp32/blob/idf-release/v4.0/tools/sdk/bin/bootloader_dio_40m.bin?raw=true + +# color codes +STAGE="\033[1;36m" +SUCCESS="\033[1;32m" +ERROR="\033[1;31m" +RESET="\033[0m" + + + +showhelp() { +cat << EOF +Usage: flash -hrtbs [-p ] [-e ] + +-h, --help Display Help +-r, --release Build in release mode +-p, --port Set serial port (default: autodetect) +-b, --baudrate Set baudrate for flasing (default: $FLASH_BAUDRATE) + , --termbaudrate Set baudrate for monitoring (default: $TERM_BAUDRATE) +-t, --terminal Open terminal program after flashing +-e, --example Build the specified example +-s, --skip Skip actual flashing +EOF +} + +# get command line options +options=$(getopt -l "help,release,port:,terminal,baudrate:,termbaudrate:,example:,skip" -o "hrp:tb:e:s" -a -- "$@") + +if [[ $? -ne 0 ]] +then + echo + showhelp + exit 1 +fi + +eval set -- "$options" + + +while true +do + case $1 in + -h|--help) + showhelp + exit 0 + ;; + -r|--release) + export TYPE=release + ;; + -p|--port) + shift + export PORT=$1 + ;; + -s|--skip) + export SKIPFLASH=1 + ;; + -t|--terminal) + export TERMINAL=1 + ;; + -b|--baudrate) + shift + export FLASH_BAUDRATE=$1 + ;; + --termbaudrate) + shift + export TERM_BAUDRATE=$1 + ;; + -e|--example) + shift + export EXAMPLE=$1 + ;; + --) + shift + break;; + esac +shift +done + +if [[ $# -ne 0 ]] +then + printf "${ERROR}*** Wrong number of arguments${RESET}\n\n" >&2 + showhelp + exit 1 +fi + +if [[ ! -f Cargo.toml ]] +then + printf "${ERROR}*** Must be run in root of project where Cargo.toml file is located${RESET}\n" >&2 + exit 1 +fi + +if [[ -z "$EXAMPLE" ]] +then + FILE=$(awk 'BEGIN {FS="[ \t=\"]+";} /^\s*\[/ {section=$1} tolower(section)=="[package]" && tolower($0) ~ /^\s*name/ {print $2}' Cargo.toml) + BIN_PATH=target/xtensa-esp32-none-elf/$TYPE/$FILE + EXAMPLE_ARG="" +else + BIN_PATH=target/xtensa-esp32-none-elf/$TYPE/examples/$EXAMPLE + EXAMPLE_ARG="--example "$EXAMPLE +fi + +# set the release flag +if [ "$TYPE" = "release" ] +then + RELEASE="--release" +else + RELEASE="" +fi + +CMD="cargo xbuild $RELEASE $EXAMPLE_ARG" + +printf "${STAGE}Building application with $CMD...${RESET}\n\n" + +rm target/current.elf 2> /dev/null +rm target/current.bin 2> /dev/null + + +# get error code of any step of the pipe +set -o pipefail + +# run cargo & get missing features +{ FEATURES=$(script -efqc "$CMD 2>&1" /dev/null | tee /dev/stderr | egrep -o '\-\-features=".*"'; exit ${PIPESTATUS[0]}); } +RES=$? + +# if cargo returned an error because features are missing to rerun +if [[ $RES -ne 0 && -n $FEATURES ]] +then + CMD="cargo xbuild $RELEASE $EXAMPLE_ARG $FEATURES" + printf "\n\n${STAGE}Building application with $CMD...${RESET}\n\n" + eval $CMD + RES=$? +fi + +# if cargo returned an error exit +if [[ $RES -ne 0 ]] +then + exit 1 +fi + +if [[ ! -f $BIN_PATH ]] +then + printf "${ERROR}Error: Output file ($BIN_PATH) not generated!${RESET}\n\n" + exit 1 +fi + +#display section sizes +echo +xtensa-esp32-elf-readelf $BIN_PATH -S|egrep 'BIT|\[Nr\]' |awk 'BEGIN {FS="[ \t\[\]]+"} $9~/A|Flg/ {size=sprintf("%7d", "0x" $7)+0; printf("%-3s %-20s %-8s %-8s %-8s %8s %-3s %-3s\n",$2,$3,$4,$5,$7,((size>0)?size:$7),$9,$12); total+=size; } END { printf("\nTotal: %d bytes\n",total)}' +echo + +# convert to bin +rm $BIN_PATH.bin 2>/dev/null +esptool.py --chip esp32 elf2image --flash_mode=$FLASH_MODE --flash_freq $FLASH_SPEED -o $BIN_PATH.bin $BIN_PATH > /dev/null +if [ $? -ne 0 ] +then + printf "${ERROR}Error: Output file ($BIN_PATH).bin not generated!${RESET}\n\n" + esptool.py --chip esp32 elf2image --flash_mode=$FLASH_MODE --flash_freq $FLASH_SPEED -o $BIN_PATH.bin $BIN_PATH + exit 1 +fi + +esptool.py --chip esp32 image_info $BIN_PATH.bin |egrep -v -i "esptool.py|Image version|Checksum|Validation Hash|Segments" +echo + +if [ $? -ne 0 ] +then + exit 1 +fi + +# create links to output binaries for linking with debugger +ln -sf $(pwd)/$BIN_PATH target/current.elf +ln -sf $(pwd)/$BIN_PATH.bin target/current.bin + +if [[ $SKIPFLASH -ne 1 || $TERMINAL -eq 1 ]] +then + if [[ -z "$PORT" ]] + then + # kill terminal programs using any port + ps -ef|grep "/dev/ttyUSB" |egrep -v "$0|grep" |awk '{print $2}'|xargs -r kill + printf "${STAGE}Detecting port...${RESET} " + PORT=$(esptool.py --no-stub read_mac 2> /dev/null | awk '/^Serial port / {port=$3} END {print port}') + if [[ -z $PORT ]] + then + printf "${ERROR}Error: cannot detect port!${RESET}\n\n" + exit 1 + fi + printf "$PORT\n\n" + else + # kill terminal programs using the same port + ps -ef|grep $PORT|egrep -v "$0|grep" |awk '{print $2}'|xargs -r kill + fi +fi + +if [[ $SKIPFLASH -ne 1 ]] +then + flash() { + echo -e "${STAGE}Flashing...${RESET} $@\n" + esptool.py --chip esp32 --port $PORT --baud $FLASH_BAUDRATE --after hard_reset write_flash --flash_mode=$FLASH_MODE --flash_freq $FLASH_SPEED --flash_size detect ${@} |egrep -v -i "stub|baud rate|Changed.|Configuring flash size|Serial port|esptool.py|Leaving" + } + + if [[ !USE_BOOTLOADER -eq 1 ]] + then + flash 0x1000 $BIN_PATH.bin + else + + printf "${STAGE}Creating partition table... ${RESET}" + if [[ target/partitions.bin -ot partitions.csv ]] + then + printf "\n\n" + # get gen_esp32part.py and create binary partition table + curl -s -S -f --max-time 5 -L $GENPART_SOURCE --output target/gen_esp32part.py_new + + if [ $? -ne 0 ]; then + if [ -f target/gen_esp32part.py ]; then + printf "\n${ERROR}Failed to get gen_esp32part.py${RESET} using old one\n\n" + else + printf "\n${ERROR}Failed to get gen_esp32part.py${RESET}\n\n" + exit 1 + fi + else + mv target/gen_esp32part.py_new target/gen_esp32part.py + fi + + rm target/partitions.bin 2> /dev/null + python target/gen_esp32part.py partitions.csv target/partitions.bin + + echo + else + printf "skipping as it is up to date\n\n" + fi + + + printf "${STAGE}Getting bootloader... ${RESET}" + + # get bootloader.bin file + # (different variants exist, but only difference is flash settings which are overriden by esptool) + curl -s -S -f --max-time 5 -L $BOOTLOADER_SOURCE --output target/bootloader.bin_new + + if [ $? -ne 0 ]; then + if [ -f target/bootloader.bin ]; then + printf "\n${ERROR}Failed to get bootloader${RESET} using old one\n\n" + else + printf "\n${ERROR}Failed to get bootloader${RESET}\n\n" + exit 1 + fi + else + mv target/bootloader.bin_new target/bootloader.bin + printf "succesfully downloader bootloader\n\n" + fi + + # check if bootloader.bin and paritions.bin are already correctly flashed (to prevent unnecessary writes) + printf "${STAGE}Verify bootloader and partition table...${RESET} " + esptool.py --no-stub --chip esp32 --port $PORT --baud $FLASH_BAUDRATE verify_flash 0x1000 target/bootloader.bin $PARTION_ADDR target/partitions.bin > /dev/null + if [ $? -ne 0 ]; then + printf "modified\n\n" + # flash bootloader.bin, partitions.bin and application + flash 0x1000 target/bootloader.bin $PARTION_ADDR target/partitions.bin $APP_ADDR $BIN_PATH.bin + else + printf "unchanged\n\n" + # flash application only + flash $APP_ADDR $BIN_PATH.bin + fi + fi + + if [[ $? -ne 0 ]] + then + printf "\n${ERROR}Error flashing application${RESET}\n\n" + exit 1 + fi +fi + +# start terminal program +if [[ TERMINAL -eq 1 ]] +then + printf "\n${STAGE}Starting terminal.${RESET}\n" + gnome-terminal --geometry 200x15+0+9999 -- picocom -b $TERM_BAUDRATE $PORT --imap lfcrlf 2>/dev/null +fi + + +if [[ $SKIPFLASH -ne 1 ]] +then + printf "\n${SUCCESS}Succesfully build and flashed application${RESET}\n\n" +else + printf "\n${SUCCESS}Succesfully build application${RESET}\n\n" +fi \ No newline at end of file diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..0c4200e --- /dev/null +++ b/memory.x @@ -0,0 +1,193 @@ +/* This memory map assumes the flash cache is on; + the blocks used are excluded from the various memory ranges + + see: https://github.com/espressif/esp-idf/blob/master/components/soc/src/esp32/soc_memory_layout.c + for details + */ + +/* override entry point */ +ENTRY(ESP32Reset) + +/* reserved at the start of DRAM for e.g. teh BT stack */ +RESERVE_DRAM = 0; + +/* reserved at the start of the RTC memories for use by the ULP processor */ +RESERVE_RTC_FAST = 0; +RESERVE_RTC_SLOW = 0; + +/* define stack size for both cores */ +STACK_SIZE = 8k; + +/* Specify main memory areas */ +MEMORY +{ + reserved_cache_seg : ORIGIN = 0x40070000, len = 64k /* SRAM0; reserved for usage as flash cache*/ + vectors_seg ( RX ) : ORIGIN = 0x40080000, len = 1k /* SRAM0 */ + iram_seg ( RX ) : ORIGIN = 0x40080400, len = 128k-0x400 /* SRAM0 */ + + reserved_for_rom_seg : ORIGIN = 0x3FFAE000, len = 8k /* SRAM2; reserved for usage by the ROM */ + dram_seg ( RW ) : ORIGIN = 0x3FFB0000 + RESERVE_DRAM, len = 176k - RESERVE_DRAM /* SRAM2+1; first 64kB used by BT if enable */ + reserved_for_boot_seg : ORIGIN = 0x3FFDC200, len = 144k /* SRAM1; reserved for static ROM usage; can be used for heap */ + + /* external flash + The 0x20 offset is a convenience for the app binary image generation. + Flash cache has 64KB pages. The .bin file which is flashed to the chip + has a 0x18 byte file header, and each segment has a 0x08 byte segment + header. Setting this offset makes it simple to meet the flash cache MMU's + constraint that (paddr % 64KB == vaddr % 64KB).) + */ + irom_seg ( RX ) : ORIGIN = 0x400D0020, len = 3M - 0x20 + drom_seg ( R ) : ORIGIN = 0x3F400020, len = 4M - 0x20 + + + /* RTC fast memory (executable). Persists over deep sleep. Only for core 0 (PRO_CPU) */ + rtc_fast_iram_seg(RWX) : ORIGIN = 0x400C0000, len = 8k + + /* RTC fast memory (same block as above), viewed from data bus. Only for core 0 (PRO_CPU) */ + rtc_fast_dram_seg(RW) : ORIGIN = 0x3FF80000 + RESERVE_RTC_FAST, len = 8k - RESERVE_RTC_FAST + + /* RTC slow memory (data accessible). Persists over deep sleep. */ + rtc_slow_seg(RW) : ORIGIN = 0x50000000 + RESERVE_RTC_SLOW, len = 8k - RESERVE_RTC_SLOW + + /* external memory, including data and text, + 4MB is the maximum, if external psram is bigger, paging is required */ + psram_seg(RWX) : ORIGIN = 0x3F800000, len = 4M +} + +/* map generic regions to output sections */ +INCLUDE "alias.x" + +/* esp32 specific regions */ +SECTIONS { + .rtc_fast.text : { + . = ALIGN(4); + *(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*) + } > rtc_fast_iram_seg AT > RODATA + + /* + This section is required to skip rtc.text area because rtc_iram_seg and + rtc_data_seg are reflect the same address space on different buses. + */ + .rtc_fast.dummy (NOLOAD) : + { + _rtc_dummy_start = ABSOLUTE(.); /* needed to make section proper size */ + . = SIZEOF(.rtc_fast.text); + _rtc_dummy_end = ABSOLUTE(.); /* needed to make section proper size */ + } > rtc_fast_dram_seg + + + .rtc_fast.data : + { + _rtc_fast_data_start = ABSOLUTE(.); + . = ALIGN(4); + *(.rtc_fast.data .rtc_fast.data.*) + _rtc_fast_data_end = ABSOLUTE(.); + } > rtc_fast_dram_seg AT > RODATA + + .rtc_fast.bss (NOLOAD) : + { + _rtc_fast_bss_start = ABSOLUTE(.); + . = ALIGN(4); + *(.rtc_fast.bss .rtc_fast.bss.*) + _rtc_fast_bss_end = ABSOLUTE(.); + } > rtc_fast_dram_seg + + .rtc_fast.noinit (NOLOAD) : + { + . = ALIGN(4); + *(.rtc_fast.noinit .rtc_fast.noinit.*) + } > rtc_fast_dram_seg + + + .rtc_slow.text : { + . = ALIGN(4); + *(.rtc_slow.literal .rtc_slow.text .rtc_slow.literal.* .rtc_slow.text.*) + } > rtc_slow_seg AT > RODATA + + .rtc_slow.data : + { + _rtc_slow_data_start = ABSOLUTE(.); + . = ALIGN(4); + *(.rtc_slow.data .rtc_slow.data.*) + _rtc_slow_data_end = ABSOLUTE(.); + } > rtc_slow_seg AT > RODATA + + .rtc_slow.bss (NOLOAD) : + { + _rtc_slow_bss_start = ABSOLUTE(.); + . = ALIGN(4); + *(.rtc_slow.bss .rtc_slow.bss.*) + _rtc_slow_bss_end = ABSOLUTE(.); + } > rtc_slow_seg + + .rtc_slow.noinit (NOLOAD) : + { + . = ALIGN(4); + *(.rtc_slow.noinit .rtc_slow.noinit.*) + } > rtc_slow_seg + + .external.data : + { + _external_data_start = ABSOLUTE(.); + . = ALIGN(4); + *(.external.data .external.data.*) + _external_data_end = ABSOLUTE(.); + } > psram_seg AT > RODATA + + .external.bss (NOLOAD) : + { + _external_bss_start = ABSOLUTE(.); + . = ALIGN(4); + *(.external.bss .external.bss.*) + _external_bss_end = ABSOLUTE(.); + } > psram_seg + + .external.noinit (NOLOAD) : + { + . = ALIGN(4); + *(.external.noinit .external.noinit.*) + } > psram_seg + + /* must be last segment using psram_seg */ + .external_heap_start (NOLOAD) : + { + . = ALIGN (4); + _external_heap_start = ABSOLUTE(.); + } > psram_seg + + + /* wifi data */ + + .rwtext.wifi : + { + . = ALIGN(4); + *( .wifi0iram .wifi0iram.*) + *( .wifirxiram .wifirxiram.*) + *( .iram1 .iram1.*) + } > RWTEXT AT > RODATA + + .data.wifi : + { + . = ALIGN(4); + *( .dram1 .dram1.*) + } > RWDATA AT > RODATA +} + +_external_ram_start = ABSOLUTE(ORIGIN(psram_seg)); +_external_ram_end = ABSOLUTE(ORIGIN(psram_seg)+LENGTH(psram_seg)); + +_heap_end = ABSOLUTE(ORIGIN(dram_seg))+LENGTH(dram_seg)+LENGTH(reserved_for_boot_seg) - 2*STACK_SIZE; +_text_heap_end = ABSOLUTE(ORIGIN(iram_seg)+LENGTH(iram_seg)); +_external_heap_end = ABSOLUTE(ORIGIN(psram_seg)+LENGTH(psram_seg)); + +_stack_start_cpu1 = _heap_end; +_stack_end_cpu1 = _stack_start_cpu1 + STACK_SIZE; +_stack_start_cpu0 = _stack_end_cpu1; +_stack_end_cpu0 = _stack_start_cpu0 + STACK_SIZE; + +EXTERN(DefaultHandler); + +EXTERN(WIFI_EVENT); /* Force inclusion of WiFi libraries */ + +INCLUDE "device.x" + diff --git a/openocd.cfg b/openocd.cfg new file mode 100644 index 0000000..cdbbe9c --- /dev/null +++ b/openocd.cfg @@ -0,0 +1,6 @@ + +adapter_khz 4000 + +source [find interface/jlink.cfg] + +source [find board/esp-wroom-32.cfg] \ No newline at end of file diff --git a/partitions.csv b/partitions.csv new file mode 100644 index 0000000..712699d --- /dev/null +++ b/partitions.csv @@ -0,0 +1,3 @@ +# Partition table +# Name, Type, SubType, Offset, Size, Flags +factory, app, factory, 0x10000, 0x3f0000, diff --git a/ram.x b/ram.x new file mode 100644 index 0000000..b6f7b28 --- /dev/null +++ b/ram.x @@ -0,0 +1,4 @@ +REGION_ALIAS("ROTEXT", iram_seg); +REGION_ALIAS("RWTEXT", iram_seg); +REGION_ALIAS("RODATA", dram_seg); +REGION_ALIAS("RWDATA", dram_seg); diff --git a/rom.x b/rom.x new file mode 100644 index 0000000..af743b0 --- /dev/null +++ b/rom.x @@ -0,0 +1,4 @@ +REGION_ALIAS("ROTEXT", irom_seg); +REGION_ALIAS("RWTEXT", iram_seg); +REGION_ALIAS("RODATA", drom_seg); +REGION_ALIAS("RWDATA", dram_seg); diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..a69994c --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "esp" +components = ["rustfmt", "rustc-dev"] +targets = ["xtensa-esp32-none-elf"] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..bcff5ec --- /dev/null +++ b/src/main.rs @@ -0,0 +1,96 @@ +#![cfg_attr(not(test), no_std)] +#![cfg_attr(not(test), no_main)] +#![allow(dead_code)] + +mod protocol; + +#[cfg(not(test))] +mod main { + + use core::{fmt::Write, panic::PanicInfo}; + + use esp32_hal::{ + clock_control::{sleep, ClockControl, XTAL_FREQUENCY_AUTO}, + dport::Split, + dprintln, + prelude::*, + serial::{config::Config, Pins, Serial}, + target, + timer::Timer, + }; + + const BLINK_HZ: Hertz = Hertz(2); + + #[entry] + fn main() -> ! { + let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); + + let (_, dport_clock_control) = dp.DPORT.split(); + + let clkcntrl = ClockControl::new( + dp.RTCCNTL, + dp.APB_CTRL, + dport_clock_control, + XTAL_FREQUENCY_AUTO, + ) + .unwrap(); + + let (clkcntrl_config, mut watchdog) = clkcntrl.freeze().unwrap(); + watchdog.disable(); + + let (_, _, _, mut watchdog0) = Timer::new(dp.TIMG0, clkcntrl_config); + let (_, _, _, mut watchdog1) = Timer::new(dp.TIMG1, clkcntrl_config); + watchdog0.disable(); + watchdog1.disable(); + + let pins = dp.GPIO.split(); + + let mut blinky = pins.gpio0.into_push_pull_output(); + + // Use UART1 as example: will cause dprintln statements not to be printed + let serial: Serial<_, _, _> = Serial::new( + dp.UART1, + Pins { + tx: pins.gpio1, + rx: pins.gpio3, + cts: None, + rts: None, + }, + Config { + // default configuration is 19200 baud, 8 data bits, 1 stop bit & no parity (8N1) + baudrate: 115200.Hz(), + ..Config::default() + }, + clkcntrl_config, + ) + .unwrap(); + + let (mut tx, mut rx) = serial.split(); + + writeln!(tx, "\n\nESP32 Started\n\n").unwrap(); + + // line will not be printed as using UART1 + dprintln!("UART0\n"); + + loop { + writeln!(tx, "Characters received: {:?}", rx.count()).unwrap(); + + while let Ok(x) = rx.read() { + write!(tx, "{} ({:#x}) ", if x >= 32 { x as char } else { '?' }, x).unwrap() + } + writeln!(tx, "").unwrap(); + + blinky.set_high().unwrap(); + sleep((Hertz(1_000_000) / BLINK_HZ).us()); + blinky.set_low().unwrap(); + sleep((Hertz(1_000_000) / BLINK_HZ).us()); + } + } + + #[panic_handler] + fn panic(info: &PanicInfo) -> ! { + dprintln!("\n\n*** {:?}", info); + loop {} + } +} + diff --git a/src/protocol.rs b/src/protocol.rs new file mode 100644 index 0000000..d10c1b4 --- /dev/null +++ b/src/protocol.rs @@ -0,0 +1,365 @@ +#![allow(dead_code)] + +#[derive(Debug, PartialEq)] +pub enum ErrorIO { + Overflow, + Incomplete, + InvalidResponse, + Hardware, +} + +pub trait InputIO { + fn read(&mut self) -> Result; + fn write(&mut self, data: &[u8]) -> Result<(), ErrorIO>; +} + +type Buffer = heapless::Vec; + +mod stub +{ + use super::*; + use super::slip::read_packet; + // use heapless::Vec; + use core::mem; + + #[derive(PartialEq, Copy, Clone)] + pub enum CommandType + { + FlashBegin = 0x02, + FlashData = 0x03, + FlashEnd = 0x04, + MemBegin = 0x05, + MemEnd = 0x06, + MemData = 0x07, + Sync = 0x08, + WriteReg = 0x09, + ReadReg = 0x0A, + SpiSetParams = 0x0B, + SpiAttach = 0x0D, + ChangeBaudrate = 0x0F, + FlashDeflBegin = 0x10, + FlashDeflData = 0x11, + FlashDeflEnd = 0x12, + SpiFlashMd5 = 0x13, + EraseFlash = 0xD0, + EraseRegion = 0xD1, + ReadFlash = 0xD2, + RunUserCode = 0xD3, + } + + #[derive(PartialEq)] + pub enum Error + { + InvalidMessageReceived = 0x5, + FailedToProcessCommand = 0x6, + InvalidCRC = 0x7, + FlashWrite = 0x8, + FlashRead = 0x9, + FlashReadLength = 0xA, + Deflate = 0xB, + } + + // #[derive(PartialEq, Copy, Clone)] + #[repr(C, packed(1))] + pub struct CommandBase { + pub direction: u8, + pub command: CommandType, + pub size: u16, + pub checksum: u32, + } + + #[repr(C, packed(1))] + pub struct FlashBeginCommand { + pub base: CommandBase, + pub erase_addr: u32, + pub packt_count: u32, + pub packet_size: u32, + pub offset: u32, + } + + // #[derive(PartialEq)] + #[repr(C, packed(1))] + pub struct FlashDataCommand<'a> { + pub base: CommandBase, + pub size: u32, + pub sequence_num: u32, + pub reserved0: u32, + pub reserved1: u32, + pub data: &'a [u8], + } + + // #[derive(PartialEq)] + #[repr(C, packed(1))] + pub struct FlashEndCommand { + pub base: CommandBase, + pub run_user_code: u32, + } + + // #[derive(PartialEq)] + pub enum Command<'a> + { + FlashBegin(FlashBeginCommand), + FlashData(FlashDataCommand<'a>), + FlashEnd(FlashEndCommand), + } + + pub struct Stub<'a> { + io: &'a mut (dyn InputIO + 'a), + payload: Buffer, + } + + impl From for Error { + fn from(_: ErrorIO) -> Self { + Error::InvalidMessageReceived + } + } + + fn slice_to_array(slice: &[u8]) -> [u8; 8] { + slice.try_into().unwrap() + } + + fn slice_to_array_2(slice: &[u8]) -> [u8; mem::size_of::()] { + slice.try_into().unwrap() + } + + impl<'a> Stub<'a> { + + pub fn new(input_io: &'a mut dyn InputIO) -> Self { + Stub { + io: input_io, + payload: heapless::Vec::new(), + } + } + + pub fn wait_for_command(&mut self) -> Result + { + read_packet(self.io, &mut self.payload)?; + + if self.payload.len() < mem::size_of::() { + return Err(Error::InvalidMessageReceived); + } + + let data = slice_to_array(self.payload.as_slice()); + let _command = unsafe { mem::transmute::<[u8; 8], CommandBase>(data) }; + + let data = slice_to_array_2(self.payload.as_slice()); + let _command = unsafe { mem::transmute::<[u8; mem::size_of::()], FlashDataCommand>(data) }; + + let cmd = FlashBeginCommand { + base : CommandBase { + direction: 0, + command: CommandType::FlashBegin, + size: 16, + checksum: 0, + }, + erase_addr: 0, + packt_count: 0, + packet_size: 0, + offset: 0, + }; + + Ok(Command::FlashBegin(cmd)) + } + + } +} + +// Check command and its size, cast it +mod slip { + use super::*; + use super::ErrorIO::*; + + impl From for ErrorIO { + fn from(_: u8) -> Self { + ErrorIO::Overflow + } + } + + pub fn read_packet(io: &mut dyn InputIO, packet: &mut Buffer) -> Result<(), ErrorIO> { + packet.clear(); + + while io.read()? != 0xC0 { } + + // Replase: 0xDB 0xDC -> 0xC0 and 0xDB 0xDD -> 0xDB + loop { + match io.read()? { + 0xDB => match io.read()? { + 0xDC => packet.push(0xC0)?, + 0xDD => packet.push(0xDB)?, + _ => return Err(InvalidResponse), + } + 0xC0 => break, + other => packet.push(other)?, + }; + } + Ok(()) + } + + pub fn write_packet(io: &mut dyn InputIO, packet: &[u8]) -> Result<(), ErrorIO> { + io.write(&[0xC0])?; + for byte in packet { + match byte { + 0xC0 => io.write(&[0xDB, 0xDC])?, + 0xDB => io.write(&[0xDB, 0xDD])?, + other => io.write(&[*other])?, + } + } + io.write(&[0xC0]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use super::ErrorIO::*; + // use super::stub::*; + // use super::stub::Error::*; + use super::slip::{read_packet, write_packet}; + use assert2::{assert}; + // use assert2::{assert, let_assert}; + // use matches::assert_matches; + use std::collections::VecDeque; + use std::vec::Vec; + + struct MockIO { + data: VecDeque + } + + impl MockIO { + fn from_slice(bytes: &[u8]) -> Self { + let bytes_vec = Vec::from(bytes); + MockIO { data: VecDeque::from(bytes_vec) } + } + + fn new() -> Self { + MockIO { data: VecDeque::new() } + } + + fn fill(&mut self, bytes: &[u8]) { + self.data.clear(); + self.data.extend(bytes); + } + + fn clear(&mut self) { + self.data.clear(); + } + + fn written(&mut self) -> &[u8] { + self.data.make_contiguous() + } + } + + impl InputIO for MockIO { + fn read(&mut self) -> Result { + match self.data.pop_front() { + Some(top) => Ok(top), + None => Err(Incomplete) + } + } + + fn write(&mut self, bytes: &[u8]) -> Result<(), ErrorIO> + { + self.data.extend(bytes); + Ok(()) + } + } + + #[test] + fn test_read_packet() { + let mut io = MockIO::new(); + let mut buffer: Buffer = heapless::Vec::new(); + + // Returns Incomplete when packet enclosed by 0xC0 was not found + io.fill(&[0xC0, 0xAA, 0x22]); + assert!( read_packet(&mut io, &mut buffer) == Err(Incomplete)); + + // Returns Incomplete when no 0xC0 is found + io.fill(&[0x00, 0xAA, 0x22]); + assert!( read_packet(&mut io, &mut buffer) == Err(Incomplete)); + + // Can find packet by 0xC0 + io.fill(&[0xC0, 0x11, 0x22, 0xC0]); + assert!( read_packet(&mut io, &mut buffer) == Ok(())); + assert!( buffer.as_slice() == &[0x11, 0x22] ); + + // Can find packet by 0xC0 + io.fill(&[0xC0, 0x11, 0x22, 0xC0]); + assert!( read_packet(&mut io, &mut buffer) == Ok(())); + assert!( buffer.as_slice() == &[0x11, 0x22] ); + + // Can convert 0xDB 0xDC -> 0xC0 + io.fill(&[0xC0, 0x11, 0xDB, 0xDC, 0x22, 0xC0]); + assert!( read_packet(&mut io, &mut buffer) == Ok(())); + assert!( buffer.as_slice() == &[0x11, 0xC0, 0x22] ); + + // Can convert 0xDB 0xDD -> 0xDB + io.fill(&[0xC0, 0x11, 0xDB, 0xDD, 0x22, 0xC0]); + assert!( read_packet(&mut io, &mut buffer) == Ok(())); + assert!( buffer.as_slice() == &[0x11, 0xDB, 0x22] ); + + // Returns InvalidResponse after invalid byte pair + io.fill(&[0xC0, 0x11, 0xDB, 0x22, 0xDB, 0x33, 0x44, 0xC0]); + assert!( read_packet(&mut io, &mut buffer) == Err(InvalidResponse)); + } + + #[test] + fn test_write_packet() { + let mut io = MockIO::new(); + + // 0xC0 is added at the beginning and end of payload + assert!( write_packet(&mut io, &[1, 2, 3]) == Ok(())); + assert!( io.written() == &[0xC0, 1, 2, 3, 0xC0] ); + io.clear(); + + // 0xC0 is replaced with 0xDB 0xDC + assert!( write_packet(&mut io, &[1, 0xC0, 3]) == Ok(())); + assert!( io.written() == &[0xC0, 1, 0xDB, 0xDC, 3, 0xC0] ); + io.clear(); + + // 0xDB is replaced with 0xDB 0xDD + assert!( write_packet(&mut io, &[1, 0xDB, 3]) == Ok(())); + assert!( io.written() == &[0xC0, 1, 0xDB, 0xDD, 3, 0xC0] ); + io.clear(); + } + + #[test] + fn test_wait_for_packet() { + + // let mut io = MockIO::from_slice(&[0xC0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC0]); + // let mut stub = Stub::new(&mut io); + + // let_assert!( Ok(cmd) = stub.wait_for_command() ); + // assert!( cmd == Command { + // direction_in: true, + // command: CommandType::FlashBegin, + // size: 16, + // checksum: 0x12345678, + // payload: heapless::Vec::new() + // } ); + + // Returns InvalidMessageReceived after receiving incomplete message + // let mut io = MockIO::from_slice(&[0xC0, 0xAA, 0x00]); + // let mut stub = Stub::new(&mut io); + // assert!( Err(InvalidMessageReceived) == stub.wait_for_command() ); + + // Returns InvalidMessageReceived after receiving incomplete message + // let mut io = MockIO::from_slice(&[0xC0, 0xAA, 0x00]); + // let mut stub = Stub::new(&mut io); + // assert!( Err(InvalidMessageReceived) == stub.wait_for_command() ); + + // let _slice = [1 as u8, 0xAu8, 16u16]; + } + + #[test] + fn test_mock() { + let mut io = MockIO::new(); + + io.fill(&[1, 2, 3]); + assert!(io.read() == Ok(1)); + assert!(io.read() == Ok(2)); + assert!(io.read() == Ok(3)); + assert!(io.read() == Err(Incomplete)); + } +} + +// wait_for_command -> slip::read_packet -> || -> io::read \ No newline at end of file From c8f1e0bf39c3ccf2e4030910e02f1cbd678d187b Mon Sep 17 00:00:00 2001 From: Martin Valik Date: Sun, 13 Mar 2022 14:36:45 +0100 Subject: [PATCH 02/10] Added slice_2_struct --- .cargo/config.toml | 4 +- Cargo.toml | 2 +- ...chain.toml => inactive-rust-toolchain.toml | 0 src/protocol.rs | 99 ++++++++----------- 4 files changed, 44 insertions(+), 61 deletions(-) rename rust-toolchain.toml => inactive-rust-toolchain.toml (100%) diff --git a/.cargo/config.toml b/.cargo/config.toml index 349c34f..ecf4bc6 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,8 +2,8 @@ runner = "xtensa-esp32-elf-gdb -q -x xtensa.gdb" [build] -target = "xtensa-esp32-none-elf" -[target.'cfg(any(target_arch="xtensa"))'] +target = "riscv32imc-unknown-none-elf" +[target.'cfg(any(target_arch="riscv"))'] rustflags = [ "-C", "link-arg=-nostartfiles", "-C", "link-arg=-Wl,-Tlink.x", diff --git a/Cargo.toml b/Cargo.toml index 498ae1e..d2f98e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] heapless = "0.7.10" -[target.'cfg(target_arch = "xtensa")'.dependencies] +[target.'cfg(target_arch = "riscv")'.dependencies] esp32-hal = "0.3.0" [dev-dependencies] diff --git a/rust-toolchain.toml b/inactive-rust-toolchain.toml similarity index 100% rename from rust-toolchain.toml rename to inactive-rust-toolchain.toml diff --git a/src/protocol.rs b/src/protocol.rs index d10c1b4..2b0ac93 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -22,7 +22,7 @@ mod stub // use heapless::Vec; use core::mem; - #[derive(PartialEq, Copy, Clone)] + #[derive(PartialEq, Copy, Clone, Debug)] pub enum CommandType { FlashBegin = 0x02, @@ -59,7 +59,7 @@ mod stub Deflate = 0xB, } - // #[derive(PartialEq, Copy, Clone)] + #[derive(PartialEq, Copy, Clone, Debug)] #[repr(C, packed(1))] pub struct CommandBase { pub direction: u8, @@ -67,7 +67,8 @@ mod stub pub size: u16, pub checksum: u32, } - + + #[derive(PartialEq, Copy, Clone, Debug)] #[repr(C, packed(1))] pub struct FlashBeginCommand { pub base: CommandBase, @@ -76,8 +77,8 @@ mod stub pub packet_size: u32, pub offset: u32, } - - // #[derive(PartialEq)] + + #[derive(PartialEq, Copy, Clone)] #[repr(C, packed(1))] pub struct FlashDataCommand<'a> { pub base: CommandBase, @@ -87,15 +88,15 @@ mod stub pub reserved1: u32, pub data: &'a [u8], } - - // #[derive(PartialEq)] + + #[derive(PartialEq, Copy, Clone)] #[repr(C, packed(1))] pub struct FlashEndCommand { pub base: CommandBase, pub run_user_code: u32, } - - // #[derive(PartialEq)] + + #[derive(PartialEq, Copy, Clone)] pub enum Command<'a> { FlashBegin(FlashBeginCommand), @@ -113,14 +114,20 @@ mod stub Error::InvalidMessageReceived } } - - fn slice_to_array(slice: &[u8]) -> [u8; 8] { - slice.try_into().unwrap() + + // todo: get rid of SIZE + fn slice_to_struct(slice: &[u8]) -> T + { + let array: [u8; SIZE] = slice.try_into().unwrap(); + unsafe { mem::transmute_copy::<[u8; SIZE], T>(&array) } } - fn slice_to_array_2(slice: &[u8]) -> [u8; mem::size_of::()] { - slice.try_into().unwrap() + macro_rules! slice_2_struct { + ($slice:expr, $type:ty) => { + slice_to_struct::<$type, { mem::size_of::<$type>() }>($slice) + }; } + impl<'a> Stub<'a> { @@ -139,26 +146,8 @@ mod stub return Err(Error::InvalidMessageReceived); } - let data = slice_to_array(self.payload.as_slice()); - let _command = unsafe { mem::transmute::<[u8; 8], CommandBase>(data) }; - - let data = slice_to_array_2(self.payload.as_slice()); - let _command = unsafe { mem::transmute::<[u8; mem::size_of::()], FlashDataCommand>(data) }; - - let cmd = FlashBeginCommand { - base : CommandBase { - direction: 0, - command: CommandType::FlashBegin, - size: 16, - checksum: 0, - }, - erase_addr: 0, - packt_count: 0, - packet_size: 0, - offset: 0, - }; - - Ok(Command::FlashBegin(cmd)) + let command = slice_2_struct!(self.payload.as_slice(), FlashBeginCommand); + Ok(Command::FlashBegin(command)) } } @@ -212,11 +201,10 @@ mod slip { mod tests { use super::*; use super::ErrorIO::*; - // use super::stub::*; - // use super::stub::Error::*; + use super::stub::*; + use super::stub::Error::*; use super::slip::{read_packet, write_packet}; - use assert2::{assert}; - // use assert2::{assert, let_assert}; + use assert2::{assert, let_assert}; // use matches::assert_matches; use std::collections::VecDeque; use std::vec::Vec; @@ -325,27 +313,22 @@ mod tests { #[test] fn test_wait_for_packet() { - // let mut io = MockIO::from_slice(&[0xC0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC0]); - // let mut stub = Stub::new(&mut io); - - // let_assert!( Ok(cmd) = stub.wait_for_command() ); - // assert!( cmd == Command { - // direction_in: true, - // command: CommandType::FlashBegin, - // size: 16, - // checksum: 0x12345678, - // payload: heapless::Vec::new() - // } ); - - // Returns InvalidMessageReceived after receiving incomplete message - // let mut io = MockIO::from_slice(&[0xC0, 0xAA, 0x00]); - // let mut stub = Stub::new(&mut io); - // assert!( Err(InvalidMessageReceived) == stub.wait_for_command() ); - // Returns InvalidMessageReceived after receiving incomplete message - // let mut io = MockIO::from_slice(&[0xC0, 0xAA, 0x00]); - // let mut stub = Stub::new(&mut io); - // assert!( Err(InvalidMessageReceived) == stub.wait_for_command() ); + let mut io = MockIO::from_slice(&[ + 0xC0, + 1, + CommandType::FlashBegin as u8, + 4, 0, + 1, 0, 0, 0, // checksum + 2, 0, 0, 0, // erase_addr + 3, 0, 0, 0, // packt_count + 4, 0, 0, 0, // packet_size + 5, 0, 0, 0, // offset + 0xC0]); + let mut stub = Stub::new(&mut io); + let_assert!( Ok(Command::FlashBegin(cmd)) = stub.wait_for_command() ); + println!("{:?}", cmd); + assert!(cmd.base.direction == 1); // let _slice = [1 as u8, 0xAu8, 16u16]; } From ddc94048151888d54db6e0fa5a45aa453267f9ec Mon Sep 17 00:00:00 2001 From: Martin Valik Date: Mon, 14 Mar 2022 09:41:06 +0100 Subject: [PATCH 03/10] Command processing partially implemented --- .cargo/config.toml | 15 +- Cargo.lock | 454 +++++++++++++++++++++++++++++++++++++-------- Cargo.toml | 10 +- build.rs | 14 ++ ld/rom.x | 77 ++++++++ memory.x | 193 ------------------- ram.x | 4 - rom.x | 4 - src/commands.rs | 206 ++++++++++++++++++++ src/main.rs | 122 ++++++------ src/protocol.rs | 445 +++++++++++++++++++++++++++++++++----------- src/targets.rs | 74 ++++++++ 12 files changed, 1165 insertions(+), 453 deletions(-) create mode 100644 build.rs create mode 100644 ld/rom.x delete mode 100644 memory.x delete mode 100644 ram.x delete mode 100644 rom.x create mode 100644 src/commands.rs create mode 100644 src/targets.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index ecf4bc6..a124839 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,13 +1,12 @@ -[target.xtensa-esp32-none-elf] -runner = "xtensa-esp32-elf-gdb -q -x xtensa.gdb" +# [target.riscv32imc-unknown-none-elf] +# runner = "espflash --format direct-boot --monitor" +# rustflags = [ +# "-C", "link-arg=-Tlinkall.x" +# ] [build] target = "riscv32imc-unknown-none-elf" -[target.'cfg(any(target_arch="riscv"))'] +[target.'cfg(any(target_arch="riscv32"))'] rustflags = [ - "-C", "link-arg=-nostartfiles", - "-C", "link-arg=-Wl,-Tlink.x", + "-C", "link-arg=-Tlinkall.x" ] - -# [unstable] -# build-std=["core", "alloc"] diff --git a/Cargo.lock b/Cargo.lock index 5b0d8c8..0be8539 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,10 +28,10 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6ac27dd1c8f16b282d1c22a8a5ae17119acc757101dec79054458fef62c447e" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.36", + "quote 1.0.15", "rustc_version 0.4.0", - "syn", + "syn 1.0.86", ] [[package]] @@ -55,6 +55,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bare-metal" version = "0.2.5" @@ -82,12 +88,27 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -113,11 +134,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70" dependencies = [ "bare-metal 1.0.0", - "cfg-if", + "cfg-if 1.0.0", "cortex-m", "riscv", ] +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "darling" version = "0.10.2" @@ -136,10 +167,10 @@ checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" dependencies = [ "fnv", "ident_case", - "proc-macro2", - "quote", + "proc-macro2 1.0.36", + "quote 1.0.15", "strsim", - "syn", + "syn 1.0.86", ] [[package]] @@ -149,10 +180,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ "darling_core", - "quote", - "syn", + "quote 1.0.15", + "syn 1.0.86", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer", + "crypto-common", ] +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "embedded-hal" version = "0.2.7" @@ -164,43 +223,64 @@ dependencies = [ ] [[package]] -name = "esp32" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73f0f4e30155c65e65697f672957343b74ca75b0942f3da51903c11fa220ad1" +name = "esp-hal-common" +version = "0.1.0" +dependencies = [ + "cfg-if 1.0.0", + "embedded-hal", + "esp-hal-procmacros", + "esp32c3", + "nb 1.0.0", + "paste", + "riscv", + "riscv-atomic-emulation-trap", + "void", +] + +[[package]] +name = "esp-hal-procmacros" +version = "0.1.0" +dependencies = [ + "darling", + "proc-macro-error", + "proc-macro2 1.0.36", + "quote 1.0.15", + "syn 1.0.86", +] + +[[package]] +name = "esp32c3" +version = "0.3.0" +source = "git+https://github.com/esp-rs/esp-pacs.git?branch=with_source#91102d1de0bba040e2523f423d73f8d24332c4fa" dependencies = [ "bare-metal 1.0.0", + "riscv", + "riscv-rt", "vcell", - "xtensa-lx", - "xtensa-lx-rt", ] [[package]] -name = "esp32-hal" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ebf28b2417a279e518343d16c64a94daa915d9e110f2ce0aee8a71d5e1b809" +name = "esp32c3-hal" +version = "0.1.0" dependencies = [ - "bare-metal 0.2.5", + "bare-metal 1.0.0", "embedded-hal", - "esp32", - "esp32-hal-proc-macros", - "nb 0.1.3", + "esp-hal-common", + "nb 1.0.0", + "r0", + "riscv", + "riscv-atomic-emulation-trap", + "riscv-rt", "void", - "xtensa-lx", - "xtensa-lx-rt", ] [[package]] -name = "esp32-hal-proc-macros" -version = "0.2.0" +name = "float-cmp" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe047c6f062015c28a44de887a711fd2850975ee15452e4de83565b0a5ffdc2" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", + "num-traits", ] [[package]] @@ -209,6 +289,22 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fragile" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da1b8f89c5b5a5b7e59405cfcf0bb9588e5ed19f0b57a4cd542bbba3f164a6d" + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "hash32" version = "0.2.1" @@ -245,6 +341,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -272,6 +377,15 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest", +] + [[package]] name = "memchr" version = "2.4.1" @@ -279,10 +393,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] -name = "mutex-trait" -version = "0.2.0" +name = "mockall" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4bb1638d419e12f8b1c43d9e639abd0d1424285bdea2f76aa231e233c63cd3a" +checksum = "3d4d70639a72f972725db16350db56da68266ca368b2a1fe26724a903ad3d6b8" +dependencies = [ + "cfg-if 1.0.0", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79ef208208a0dea3f72221e26e904cdc6db2e481d9ade89081ddd494f1dbaa6b" +dependencies = [ + "cfg-if 1.0.0", + "proc-macro2 1.0.36", + "quote 1.0.15", + "syn 1.0.86", +] + +[[package]] +name = "mockall_double" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eacc2558c7ab425550913e9ba69077513922396bc4a1338301e3a0d244d2555" +dependencies = [ + "cfg-if 0.1.10", + "proc-macro2 1.0.36", + "quote 1.0.15", + "syn 1.0.86", +] [[package]] name = "nb" @@ -299,13 +446,106 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "paste" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" + +[[package]] +name = "predicates" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" + +[[package]] +name = "predicates-tree" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2 1.0.36", + "quote 1.0.15", + "syn 1.0.86", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2 1.0.36", + "quote 1.0.15", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + [[package]] name = "proc-macro2" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ - "unicode-xid", + "unicode-xid 0.2.2", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", ] [[package]] @@ -314,7 +554,7 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.36", ] [[package]] @@ -323,6 +563,30 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" +[[package]] +name = "rand" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "regex" version = "1.5.4" @@ -351,6 +615,41 @@ dependencies = [ "riscv-target", ] +[[package]] +name = "riscv-atomic-emulation-trap" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d8ed35d10950305d9abbc3fa49540b948e03b653c85012b4cd9f7d249029f0" +dependencies = [ + "riscv", + "riscv-rt", + "riscv-target", +] + +[[package]] +name = "riscv-rt" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9f28dc850356196a36078c51c9dc7b46014a29a8fde35d5f81c99e489d0e00" +dependencies = [ + "r0", + "riscv", + "riscv-rt-macros", + "riscv-target", +] + +[[package]] +name = "riscv-rt-macros" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3525f8341898dec060782087b7a15969e1cfe52818afacc47709265c19a23d53" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "rand", + "syn 0.15.44", +] + [[package]] name = "riscv-target" version = "0.1.2" @@ -432,9 +731,24 @@ name = "stub" version = "0.1.0" dependencies = [ "assert2", - "esp32-hal", + "esp32c3-hal", "heapless", "matches", + "md-5", + "mockall", + "mockall_double", + "riscv-rt", +] + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", ] [[package]] @@ -443,11 +757,29 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "proc-macro2 1.0.36", + "quote 1.0.15", + "unicode-xid 0.2.2", ] +[[package]] +name = "termtree" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.2" @@ -460,6 +792,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "void" version = "1.0.2" @@ -497,40 +835,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "xtensa-lx" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb8c07b753b1711deb84033ba74274336117400c7e0f14e454cfeecb7303274f" -dependencies = [ - "bare-metal 1.0.0", - "mutex-trait", - "r0", - "spin", -] - -[[package]] -name = "xtensa-lx-rt" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "158560f495a7d707880b79984e05d3c8f0b5736d3206d27b45c58a5aaf9813b7" -dependencies = [ - "bare-metal 1.0.0", - "r0", - "xtensa-lx-rt-proc-macros", -] - -[[package]] -name = "xtensa-lx-rt-proc-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a8200930e2dbd515c231f7a46033bd6dfe1497a8e9a539878f0de8f0cd730b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "yansi" version = "0.5.0" diff --git a/Cargo.toml b/Cargo.toml index d2f98e4..fb1ec3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,9 +5,13 @@ edition = "2021" [dependencies] heapless = "0.7.10" -[target.'cfg(target_arch = "riscv")'.dependencies] -esp32-hal = "0.3.0" +mockall_double = "0.1.0" +md-5 = { version = "0.10.1", default-features = false } +[target.'cfg(target_arch = "riscv32")'.dependencies] +esp32c3-hal = { path = "../esp-hal/esp32c3-hal" } +riscv-rt = "0.8.1" [dev-dependencies] assert2 = "0.3.6" -matches = "0.1.9" \ No newline at end of file +matches = "0.1.9" +mockall = "0.11.0" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..b9fe7cd --- /dev/null +++ b/build.rs @@ -0,0 +1,14 @@ +use std::path::PathBuf; +use std::{env, fs}; + +fn main() { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + println!("cargo:rustc-link-search)={}", out_dir.display()); + + #[cfg(target_arch = "riscv32")] + { + fs::copy("ld/rom.x", out_dir.join("rom.x")).unwrap(); + println!("cargo:rerun-if-changed=ld/ld/rom.x"); + println!("cargo:rustc-link-arg=-Tld/rom.x"); + } +} diff --git a/ld/rom.x b/ld/rom.x new file mode 100644 index 0000000..4dd48c8 --- /dev/null +++ b/ld/rom.x @@ -0,0 +1,77 @@ + +PROVIDE( esp_rom_spiflash_wait_idle = 0x4000010c ); +PROVIDE( esp_rom_spiflash_write_encrypted = 0x40000110 ); +PROVIDE( esp_rom_spiflash_write_encrypted_dest = 0x40000114 ); +PROVIDE( esp_rom_spiflash_write_encrypted_enable = 0x40000118 ); +PROVIDE( esp_rom_spiflash_write_encrypted_disable = 0x4000011c ); +PROVIDE( esp_rom_spiflash_erase_chip = 0x40000120 ); +PROVIDE( esp_rom_spiflash_erase_block = 0x40000124 ); +PROVIDE( esp_rom_spiflash_erase_sector = 0x40000128 ); +PROVIDE( esp_rom_spiflash_write = 0x4000012c ); +PROVIDE( esp_rom_spiflash_read = 0x40000130 ); +PROVIDE( esp_rom_spiflash_config_param = 0x40000134 ); +PROVIDE( esp_rom_spiflash_read_user_cmd = 0x40000138 ); +PROVIDE( esp_rom_spiflash_select_qio_pins = 0x4000013c ); +PROVIDE( esp_rom_spiflash_unlock = 0x40000140 ); +PROVIDE( esp_rom_spi_flash_auto_sus_res = 0x40000144 ); +PROVIDE( esp_rom_spi_flash_send_resume = 0x40000148 ); +PROVIDE( esp_rom_spi_flash_update_id = 0x4000014c ); +PROVIDE( esp_rom_spiflash_config_clk = 0x40000150 ); +PROVIDE( esp_rom_spiflash_config_readmode = 0x40000154 ); +PROVIDE( esp_rom_spiflash_read_status = 0x40000158 ); +PROVIDE( esp_rom_spiflash_read_statushigh = 0x4000015c ); +PROVIDE( esp_rom_spiflash_write_status = 0x40000160 ); +PROVIDE( esp_rom_spiflash_attach = 0x40000164 ); +PROVIDE( spi_flash_get_chip_size = 0x40000168 ); +PROVIDE( spi_flash_guard_set = 0x4000016c ); +PROVIDE( spi_flash_guard_get = 0x40000170 ); +PROVIDE( spi_flash_write_config_set = 0x40000174 ); +PROVIDE( spi_flash_write_config_get = 0x40000178 ); +PROVIDE( spi_flash_safe_write_address_func_set = 0x4000017c ); +PROVIDE( spi_flash_unlock = 0x40000180 ); +PROVIDE( spi_flash_erase_range = 0x40000184 ); +PROVIDE( spi_flash_erase_sector = 0x40000188 ); +PROVIDE( spi_flash_write = 0x4000018c ); +PROVIDE( spi_flash_read = 0x40000190 ); +PROVIDE( spi_flash_write_encrypted = 0x40000194 ); +PROVIDE( spi_flash_read_encrypted = 0x40000198 ); +PROVIDE( spi_flash_mmap_os_func_set = 0x4000019c ); +PROVIDE( spi_flash_mmap_page_num_init = 0x400001a0 ); +PROVIDE( spi_flash_mmap = 0x400001a4 ); +PROVIDE( spi_flash_mmap_pages = 0x400001a8 ); +PROVIDE( spi_flash_munmap = 0x400001ac ); +PROVIDE( spi_flash_mmap_dump = 0x400001b0 ); +PROVIDE( spi_flash_check_and_flush_cache = 0x400001b4 ); +PROVIDE( spi_flash_mmap_get_free_pages = 0x400001b8 ); +PROVIDE( spi_flash_cache2phys = 0x400001bc ); +PROVIDE( spi_flash_phys2cache = 0x400001c0 ); +PROVIDE( spi_flash_disable_cache = 0x400001c4 ); +PROVIDE( spi_flash_restore_cache = 0x400001c8 ); +PROVIDE( spi_flash_cache_enabled = 0x400001cc ); +PROVIDE( spi_flash_enable_cache = 0x400001d0 ); +PROVIDE( spi_cache_mode_switch = 0x400001d4 ); +PROVIDE( spi_common_set_dummy_output = 0x400001d8 ); +PROVIDE( spi_common_set_flash_cs_timing = 0x400001dc ); +PROVIDE( esp_enable_cache_flash_wrap = 0x400001e0 ); +PROVIDE( SPIEraseArea = 0x400001e4 ); +PROVIDE( SPILock = 0x400001e8 ); +PROVIDE( SPIMasterReadModeCnfig = 0x400001ec ); +PROVIDE( SPI_Common_Command = 0x400001f0 ); +PROVIDE( SPI_WakeUp = 0x400001f4 ); +PROVIDE( SPI_block_erase = 0x400001f8 ); +PROVIDE( SPI_chip_erase = 0x400001fc ); +PROVIDE( SPI_init = 0x40000200 ); +PROVIDE( SPI_page_program = 0x40000204 ); +PROVIDE( SPI_read_data = 0x40000208 ); +PROVIDE( SPI_sector_erase = 0x4000020c ); +PROVIDE( SPI_write_enable = 0x40000210 ); +PROVIDE( SelectSpiFunction = 0x40000214 ); +PROVIDE( SetSpiDrvs = 0x40000218 ); +PROVIDE( Wait_SPI_Idle = 0x4000021c ); +PROVIDE( spi_dummy_len_fix = 0x40000220 ); +PROVIDE( Disable_QMode = 0x40000224 ); +PROVIDE( Enable_QMode = 0x40000228 ); +PROVIDE( ets_efuse_get_spiconfig = 0x4000071c ); +PROVIDE( uart_tx_one_char = 0x40000068 ); + +/* Do not exceed this mark in the error messages above | */ diff --git a/memory.x b/memory.x deleted file mode 100644 index 0c4200e..0000000 --- a/memory.x +++ /dev/null @@ -1,193 +0,0 @@ -/* This memory map assumes the flash cache is on; - the blocks used are excluded from the various memory ranges - - see: https://github.com/espressif/esp-idf/blob/master/components/soc/src/esp32/soc_memory_layout.c - for details - */ - -/* override entry point */ -ENTRY(ESP32Reset) - -/* reserved at the start of DRAM for e.g. teh BT stack */ -RESERVE_DRAM = 0; - -/* reserved at the start of the RTC memories for use by the ULP processor */ -RESERVE_RTC_FAST = 0; -RESERVE_RTC_SLOW = 0; - -/* define stack size for both cores */ -STACK_SIZE = 8k; - -/* Specify main memory areas */ -MEMORY -{ - reserved_cache_seg : ORIGIN = 0x40070000, len = 64k /* SRAM0; reserved for usage as flash cache*/ - vectors_seg ( RX ) : ORIGIN = 0x40080000, len = 1k /* SRAM0 */ - iram_seg ( RX ) : ORIGIN = 0x40080400, len = 128k-0x400 /* SRAM0 */ - - reserved_for_rom_seg : ORIGIN = 0x3FFAE000, len = 8k /* SRAM2; reserved for usage by the ROM */ - dram_seg ( RW ) : ORIGIN = 0x3FFB0000 + RESERVE_DRAM, len = 176k - RESERVE_DRAM /* SRAM2+1; first 64kB used by BT if enable */ - reserved_for_boot_seg : ORIGIN = 0x3FFDC200, len = 144k /* SRAM1; reserved for static ROM usage; can be used for heap */ - - /* external flash - The 0x20 offset is a convenience for the app binary image generation. - Flash cache has 64KB pages. The .bin file which is flashed to the chip - has a 0x18 byte file header, and each segment has a 0x08 byte segment - header. Setting this offset makes it simple to meet the flash cache MMU's - constraint that (paddr % 64KB == vaddr % 64KB).) - */ - irom_seg ( RX ) : ORIGIN = 0x400D0020, len = 3M - 0x20 - drom_seg ( R ) : ORIGIN = 0x3F400020, len = 4M - 0x20 - - - /* RTC fast memory (executable). Persists over deep sleep. Only for core 0 (PRO_CPU) */ - rtc_fast_iram_seg(RWX) : ORIGIN = 0x400C0000, len = 8k - - /* RTC fast memory (same block as above), viewed from data bus. Only for core 0 (PRO_CPU) */ - rtc_fast_dram_seg(RW) : ORIGIN = 0x3FF80000 + RESERVE_RTC_FAST, len = 8k - RESERVE_RTC_FAST - - /* RTC slow memory (data accessible). Persists over deep sleep. */ - rtc_slow_seg(RW) : ORIGIN = 0x50000000 + RESERVE_RTC_SLOW, len = 8k - RESERVE_RTC_SLOW - - /* external memory, including data and text, - 4MB is the maximum, if external psram is bigger, paging is required */ - psram_seg(RWX) : ORIGIN = 0x3F800000, len = 4M -} - -/* map generic regions to output sections */ -INCLUDE "alias.x" - -/* esp32 specific regions */ -SECTIONS { - .rtc_fast.text : { - . = ALIGN(4); - *(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*) - } > rtc_fast_iram_seg AT > RODATA - - /* - This section is required to skip rtc.text area because rtc_iram_seg and - rtc_data_seg are reflect the same address space on different buses. - */ - .rtc_fast.dummy (NOLOAD) : - { - _rtc_dummy_start = ABSOLUTE(.); /* needed to make section proper size */ - . = SIZEOF(.rtc_fast.text); - _rtc_dummy_end = ABSOLUTE(.); /* needed to make section proper size */ - } > rtc_fast_dram_seg - - - .rtc_fast.data : - { - _rtc_fast_data_start = ABSOLUTE(.); - . = ALIGN(4); - *(.rtc_fast.data .rtc_fast.data.*) - _rtc_fast_data_end = ABSOLUTE(.); - } > rtc_fast_dram_seg AT > RODATA - - .rtc_fast.bss (NOLOAD) : - { - _rtc_fast_bss_start = ABSOLUTE(.); - . = ALIGN(4); - *(.rtc_fast.bss .rtc_fast.bss.*) - _rtc_fast_bss_end = ABSOLUTE(.); - } > rtc_fast_dram_seg - - .rtc_fast.noinit (NOLOAD) : - { - . = ALIGN(4); - *(.rtc_fast.noinit .rtc_fast.noinit.*) - } > rtc_fast_dram_seg - - - .rtc_slow.text : { - . = ALIGN(4); - *(.rtc_slow.literal .rtc_slow.text .rtc_slow.literal.* .rtc_slow.text.*) - } > rtc_slow_seg AT > RODATA - - .rtc_slow.data : - { - _rtc_slow_data_start = ABSOLUTE(.); - . = ALIGN(4); - *(.rtc_slow.data .rtc_slow.data.*) - _rtc_slow_data_end = ABSOLUTE(.); - } > rtc_slow_seg AT > RODATA - - .rtc_slow.bss (NOLOAD) : - { - _rtc_slow_bss_start = ABSOLUTE(.); - . = ALIGN(4); - *(.rtc_slow.bss .rtc_slow.bss.*) - _rtc_slow_bss_end = ABSOLUTE(.); - } > rtc_slow_seg - - .rtc_slow.noinit (NOLOAD) : - { - . = ALIGN(4); - *(.rtc_slow.noinit .rtc_slow.noinit.*) - } > rtc_slow_seg - - .external.data : - { - _external_data_start = ABSOLUTE(.); - . = ALIGN(4); - *(.external.data .external.data.*) - _external_data_end = ABSOLUTE(.); - } > psram_seg AT > RODATA - - .external.bss (NOLOAD) : - { - _external_bss_start = ABSOLUTE(.); - . = ALIGN(4); - *(.external.bss .external.bss.*) - _external_bss_end = ABSOLUTE(.); - } > psram_seg - - .external.noinit (NOLOAD) : - { - . = ALIGN(4); - *(.external.noinit .external.noinit.*) - } > psram_seg - - /* must be last segment using psram_seg */ - .external_heap_start (NOLOAD) : - { - . = ALIGN (4); - _external_heap_start = ABSOLUTE(.); - } > psram_seg - - - /* wifi data */ - - .rwtext.wifi : - { - . = ALIGN(4); - *( .wifi0iram .wifi0iram.*) - *( .wifirxiram .wifirxiram.*) - *( .iram1 .iram1.*) - } > RWTEXT AT > RODATA - - .data.wifi : - { - . = ALIGN(4); - *( .dram1 .dram1.*) - } > RWDATA AT > RODATA -} - -_external_ram_start = ABSOLUTE(ORIGIN(psram_seg)); -_external_ram_end = ABSOLUTE(ORIGIN(psram_seg)+LENGTH(psram_seg)); - -_heap_end = ABSOLUTE(ORIGIN(dram_seg))+LENGTH(dram_seg)+LENGTH(reserved_for_boot_seg) - 2*STACK_SIZE; -_text_heap_end = ABSOLUTE(ORIGIN(iram_seg)+LENGTH(iram_seg)); -_external_heap_end = ABSOLUTE(ORIGIN(psram_seg)+LENGTH(psram_seg)); - -_stack_start_cpu1 = _heap_end; -_stack_end_cpu1 = _stack_start_cpu1 + STACK_SIZE; -_stack_start_cpu0 = _stack_end_cpu1; -_stack_end_cpu0 = _stack_start_cpu0 + STACK_SIZE; - -EXTERN(DefaultHandler); - -EXTERN(WIFI_EVENT); /* Force inclusion of WiFi libraries */ - -INCLUDE "device.x" - diff --git a/ram.x b/ram.x deleted file mode 100644 index b6f7b28..0000000 --- a/ram.x +++ /dev/null @@ -1,4 +0,0 @@ -REGION_ALIAS("ROTEXT", iram_seg); -REGION_ALIAS("RWTEXT", iram_seg); -REGION_ALIAS("RODATA", dram_seg); -REGION_ALIAS("RWDATA", dram_seg); diff --git a/rom.x b/rom.x deleted file mode 100644 index af743b0..0000000 --- a/rom.x +++ /dev/null @@ -1,4 +0,0 @@ -REGION_ALIAS("ROTEXT", irom_seg); -REGION_ALIAS("RWTEXT", iram_seg); -REGION_ALIAS("RODATA", drom_seg); -REGION_ALIAS("RWDATA", dram_seg); diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..a44716e --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,206 @@ +#[derive(PartialEq, Debug)] +pub enum Error +{ + BadDataLen = 0xC0, + BadDataChecksum = 0xC1, + BadBlocksize = 0xC2, + InvalidCommand = 0xC3, + FailedSpiOp = 0xC4, + FailedSpiUnlock = 0xC5, + NotInFlashMode = 0xC6, + InflateError = 0xC7, + NotEnoughData = 0xC8, + TooMuchData = 0xC9, + CmdNotImplemented = 0xFF, + + Err0x63 = 0x63, +} + +#[derive(PartialEq, Copy, Clone, Debug)] +pub enum CommandCode +{ + FlashBegin = 0x02, + FlashData = 0x03, + FlashEnd = 0x04, + MemBegin = 0x05, + MemEnd = 0x06, + MemData = 0x07, + Sync = 0x08, + WriteReg = 0x09, + ReadReg = 0x0A, + SpiSetParams = 0x0B, + SpiAttach = 0x0D, + ChangeBaudrate = 0x0F, + FlashDeflBegin = 0x10, + FlashDeflData = 0x11, + FlashDeflEnd = 0x12, + SpiFlashMd5 = 0x13, + EraseFlash = 0xD0, + EraseRegion = 0xD1, + ReadFlash = 0xD2, + RunUserCode = 0xD3, +} + +#[derive(PartialEq, Copy, Clone, Debug)] +#[repr(C, packed(1))] +pub struct CommandBase { + pub direction: u8, + pub code: CommandCode, + pub size: u16, + pub checksum: u32, +} + +#[derive(PartialEq, Copy, Clone, Debug)] +#[repr(C, packed(1))] +pub struct SyncCommand { + pub base: CommandBase, + pub payload: [u8; 36], +} + +#[derive(PartialEq, Copy, Clone, Debug)] +#[repr(C, packed(1))] +pub struct BeginCommand { + pub base: CommandBase, + pub total_size: u32, + pub packt_count: u32, + pub packet_size: u32, + pub offset: u32, +} + +#[derive(PartialEq, Copy, Clone, Debug)] +#[repr(C, packed(1))] +pub struct DataCommand { + pub base: CommandBase, + pub size: u32, + pub sequence_num: u32, + pub reserved: [u32; 2], +} + +#[derive(PartialEq, Copy, Clone)] +#[repr(C, packed(1))] +pub struct EndCommand { + pub base: CommandBase, + pub run_user_code: u32, +} + +#[derive(PartialEq, Copy, Clone)] +#[repr(C, packed(1))] +pub struct WriteRegCommand { + pub base: CommandBase, + pub address: u32, + pub value: u32, + pub mask: u32, + pub delay_us: u32, +} + +#[derive(PartialEq, Copy, Clone)] +#[repr(C, packed(1))] +pub struct ReadRegCommand { + pub base: CommandBase, + pub address: u32, +} + +// Possibly move to other module +#[derive(PartialEq, Copy, Clone)] +#[repr(C, packed(1))] +pub struct SpiParams { + pub id: u32, + pub total_size: u32, + pub block_size: u32, + pub sector_size: u32, + pub page_size: u32, + pub status_mask: u32, +} + +#[derive(PartialEq, Copy, Clone)] +#[repr(C, packed(1))] +pub struct SpiSetParamsCommand { + pub base: CommandBase, + pub params: SpiParams, +} + +#[derive(PartialEq, Copy, Clone)] +#[repr(C, packed(1))] +pub struct ChangeBaudrateCommand { + pub base: CommandBase, + pub new: u32, + pub old: u32, +} + +#[derive(PartialEq, Copy, Clone)] +#[repr(C, packed(1))] +pub struct SpiFlashMd5Command { + pub base: CommandBase, + pub address: u32, + pub size: u32, + pub reserved: [u32; 2], +} + +#[derive(PartialEq, Copy, Clone)] +#[repr(C, packed(1))] +pub struct EraseRegionCommand { + pub base: CommandBase, + pub address: u32, + pub size: u32, +} + +// Possibly move to other module +#[derive(PartialEq, Copy, Clone)] +#[repr(C, packed(1))] +pub struct ReadFlashParams { + pub address: u32, + pub erase_size: u32, + pub sector_size: u32, + pub packet_size: u32, +} + +#[derive(PartialEq, Copy, Clone)] +#[repr(C, packed(1))] +pub struct ReadFlashCommand { + pub base: CommandBase, + pub params: ReadFlashParams, +} + +#[derive(PartialEq, Copy, Clone)] +#[repr(C, packed(1))] +pub struct Response<'a> { + pub direction: u8, + pub command: CommandCode, + pub size: u16, + pub value: u32, + pub status: u8, + pub error: u8, + pub data: &'a[u8] +} + +// Size of sesponse without data reference +pub const RESPONSE_SIZE: usize = 10; + +impl<'a> Response<'a> { + + pub fn new(cmd: CommandCode) -> Self { + Response { + direction: 1, + command: cmd, + size: 2, + value: 0, + status: 0, + error: 0, + data: &[], + } + } + + pub fn value(&mut self, value: u32) { + self.value = value; + } + + pub fn data(&mut self, data: &'a[u8]) { + self.size = 2 + data.len() as u16; + self.data = data; + } + + pub fn error(&mut self, error: Error) { + self.status = 1; + self.error = error as u8; + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index bcff5ec..71ac0e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,93 +3,97 @@ #![allow(dead_code)] mod protocol; +mod commands; +mod targets; #[cfg(not(test))] mod main { + use riscv_rt::entry; use core::{fmt::Write, panic::PanicInfo}; - - use esp32_hal::{ - clock_control::{sleep, ClockControl, XTAL_FREQUENCY_AUTO}, - dport::Split, - dprintln, - prelude::*, - serial::{config::Config, Pins, Serial}, - target, - timer::Timer, + + use esp32c3_hal::{ + // clock_control::{sleep, ClockControl, XTAL_FREQUENCY_AUTO}, + // dport::Split, + // dprintln, + // prelude::*, + // serial::{config::Config, Pins, Serial}, + Serial, + // target, + // timer::Timer, }; - const BLINK_HZ: Hertz = Hertz(2); + // const BLINK_HZ: Hertz = Hertz(2); #[entry] fn main() -> ! { - let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); + // let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); - let (_, dport_clock_control) = dp.DPORT.split(); + // let (_, dport_clock_control) = dp.DPORT.split(); - let clkcntrl = ClockControl::new( - dp.RTCCNTL, - dp.APB_CTRL, - dport_clock_control, - XTAL_FREQUENCY_AUTO, - ) - .unwrap(); + // let clkcntrl = ClockControl::new( + // dp.RTCCNTL, + // dp.APB_CTRL, + // dport_clock_control, + // XTAL_FREQUENCY_AUTO, + // ) + // .unwrap(); - let (clkcntrl_config, mut watchdog) = clkcntrl.freeze().unwrap(); - watchdog.disable(); + // let (clkcntrl_config, mut watchdog) = clkcntrl.freeze().unwrap(); + // watchdog.disable(); - let (_, _, _, mut watchdog0) = Timer::new(dp.TIMG0, clkcntrl_config); - let (_, _, _, mut watchdog1) = Timer::new(dp.TIMG1, clkcntrl_config); - watchdog0.disable(); - watchdog1.disable(); + // let (_, _, _, mut watchdog0) = Timer::new(dp.TIMG0, clkcntrl_config); + // let (_, _, _, mut watchdog1) = Timer::new(dp.TIMG1, clkcntrl_config); + // watchdog0.disable(); + // watchdog1.disable(); - let pins = dp.GPIO.split(); + // let pins = dp.GPIO.split(); - let mut blinky = pins.gpio0.into_push_pull_output(); + // let mut blinky = pins.gpio0.into_push_pull_output(); // Use UART1 as example: will cause dprintln statements not to be printed - let serial: Serial<_, _, _> = Serial::new( - dp.UART1, - Pins { - tx: pins.gpio1, - rx: pins.gpio3, - cts: None, - rts: None, - }, - Config { - // default configuration is 19200 baud, 8 data bits, 1 stop bit & no parity (8N1) - baudrate: 115200.Hz(), - ..Config::default() - }, - clkcntrl_config, - ) - .unwrap(); - - let (mut tx, mut rx) = serial.split(); - - writeln!(tx, "\n\nESP32 Started\n\n").unwrap(); + // let serial: Serial<_, _, _> = Serial::new( + // dp.UART1, + // Pins { + // tx: pins.gpio1, + // rx: pins.gpio3, + // cts: None, + // rts: None, + // }, + // Config { + // // default configuration is 19200 baud, 8 data bits, 1 stop bit & no parity (8N1) + // baudrate: 115200.Hz(), + // ..Config::default() + // }, + // clkcntrl_config, + // ) + // .unwrap(); + + // let (mut tx, mut rx) = serial.split(); + + // writeln!(tx, "\n\nESP32 Started\n\n").unwrap(); // line will not be printed as using UART1 - dprintln!("UART0\n"); + // dprintln!("UART0\n"); loop { - writeln!(tx, "Characters received: {:?}", rx.count()).unwrap(); + // writeln!(tx, "Characters received: {:?}", rx.count()).unwrap(); - while let Ok(x) = rx.read() { - write!(tx, "{} ({:#x}) ", if x >= 32 { x as char } else { '?' }, x).unwrap() - } - writeln!(tx, "").unwrap(); + // while let Ok(x) = rx.read() { + // write!(tx, "{} ({:#x}) ", if x >= 32 { x as char } else { '?' }, x).unwrap() + // } + // writeln!(tx, "").unwrap(); - blinky.set_high().unwrap(); - sleep((Hertz(1_000_000) / BLINK_HZ).us()); - blinky.set_low().unwrap(); - sleep((Hertz(1_000_000) / BLINK_HZ).us()); + // blinky.set_high().unwrap(); + // sleep((Hertz(1_000_000) / BLINK_HZ).us()); + // blinky.set_low().unwrap(); + // sleep((Hertz(1_000_000) / BLINK_HZ).us()); } } #[panic_handler] - fn panic(info: &PanicInfo) -> ! { - dprintln!("\n\n*** {:?}", info); + fn panic(_info: &PanicInfo) -> ! { + // dprintln!("\n\n*** {:?}", info); loop {} } } diff --git a/src/protocol.rs b/src/protocol.rs index 2b0ac93..f5e718a 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,5 +1,10 @@ #![allow(dead_code)] +use mockall_double::double; + +#[double] +use crate::targets::esp32c3 as target; + #[derive(Debug, PartialEq)] pub enum ErrorIO { Overflow, @@ -18,115 +23,81 @@ type Buffer = heapless::Vec; mod stub { use super::*; - use super::slip::read_packet; - // use heapless::Vec; + use super::target::*; + use super::slip::*; use core::mem; - - #[derive(PartialEq, Copy, Clone, Debug)] - pub enum CommandType - { - FlashBegin = 0x02, - FlashData = 0x03, - FlashEnd = 0x04, - MemBegin = 0x05, - MemEnd = 0x06, - MemData = 0x07, - Sync = 0x08, - WriteReg = 0x09, - ReadReg = 0x0A, - SpiSetParams = 0x0B, - SpiAttach = 0x0D, - ChangeBaudrate = 0x0F, - FlashDeflBegin = 0x10, - FlashDeflData = 0x11, - FlashDeflEnd = 0x12, - SpiFlashMd5 = 0x13, - EraseFlash = 0xD0, - EraseRegion = 0xD1, - ReadFlash = 0xD2, - RunUserCode = 0xD3, - } - - #[derive(PartialEq)] - pub enum Error - { - InvalidMessageReceived = 0x5, - FailedToProcessCommand = 0x6, - InvalidCRC = 0x7, - FlashWrite = 0x8, - FlashRead = 0x9, - FlashReadLength = 0xA, - Deflate = 0xB, - } + use core::slice; + use core::cmp::min; + use core::mem::MaybeUninit; + use crate::commands::*; + use crate::commands::CommandCode::*; + use md5::{Md5, Digest}; + + const DATA_CMD_SIZE: usize = mem::size_of::(); + const CMD_BASE_SIZE: usize = mem::size_of::(); + + const MAX_WRITE_BLOCK: u32 = 0x4000; + + const FLASH_SECTOR_SIZE: u32 = 4096; - #[derive(PartialEq, Copy, Clone, Debug)] - #[repr(C, packed(1))] - pub struct CommandBase { - pub direction: u8, - pub command: CommandType, - pub size: u16, - pub checksum: u32, - } - - #[derive(PartialEq, Copy, Clone, Debug)] - #[repr(C, packed(1))] - pub struct FlashBeginCommand { - pub base: CommandBase, - pub erase_addr: u32, - pub packt_count: u32, - pub packet_size: u32, - pub offset: u32, - } - - #[derive(PartialEq, Copy, Clone)] - #[repr(C, packed(1))] - pub struct FlashDataCommand<'a> { - pub base: CommandBase, - pub size: u32, - pub sequence_num: u32, - pub reserved0: u32, - pub reserved1: u32, - pub data: &'a [u8], - } - - #[derive(PartialEq, Copy, Clone)] - #[repr(C, packed(1))] - pub struct FlashEndCommand { - pub base: CommandBase, - pub run_user_code: u32, - } #[derive(PartialEq, Copy, Clone)] pub enum Command<'a> { - FlashBegin(FlashBeginCommand), - FlashData(FlashDataCommand<'a>), - FlashEnd(FlashEndCommand), - } + Sync(SyncCommand), + Begin(BeginCommand), + Data(DataCommand, &'a[u8]), + End(EndCommand), + ReadReg(u32), + WriteReg(WriteRegCommand), + SpiSetParams(SpiSetParamsCommand), + SpiAttach(u32), + ChangeBaudrate(ChangeBaudrateCommand), + SpiFlashMd5(SpiFlashMd5Command), + EraseFlash, + EraseRegion(EraseRegionCommand), + ReadFlash(ReadFlashCommand), + RunUserCode, + Unwkown(CommandCode) + } pub struct Stub<'a> { io: &'a mut (dyn InputIO + 'a), payload: Buffer, + // Begin + total_size: u32, + offset: u32, } impl From for Error { fn from(_: ErrorIO) -> Self { - Error::InvalidMessageReceived + Error::FailedSpiOp } } // todo: get rid of SIZE - fn slice_to_struct(slice: &[u8]) -> T + fn slice_to_struct(slice: &[u8]) -> Result { - let array: [u8; SIZE] = slice.try_into().unwrap(); - unsafe { mem::transmute_copy::<[u8; SIZE], T>(&array) } + if SIZE != slice.len() { + return Err(Error::BadDataLen); + } + let array: &[u8; SIZE] = &slice[0..SIZE].try_into().unwrap(); + unsafe { Ok(mem::transmute_copy::<[u8; SIZE], T>(array)) } } - macro_rules! slice_2_struct { + macro_rules! transmute { ($slice:expr, $type:ty) => { slice_to_struct::<$type, { mem::size_of::<$type>() }>($slice) }; } + + pub unsafe fn to_u8_slice(p: &T) -> &[u8] { + slice::from_raw_parts( (p as *const T) as *const u8, mem::size_of::(), ) + } + + fn u32_from_slice(slice: &[u8], index: usize) -> u32 { + u32::from_le_bytes(slice[index..index+4].try_into().unwrap()) + } impl<'a> Stub<'a> { @@ -135,21 +106,154 @@ mod stub Stub { io: input_io, payload: heapless::Vec::new(), + offset: 0, + total_size: 0, } } - pub fn wait_for_command(&mut self) -> Result + pub fn wait_for_command(&mut self, code: &mut CommandCode) -> Result { read_packet(self.io, &mut self.payload)?; - if self.payload.len() < mem::size_of::() { - return Err(Error::InvalidMessageReceived); + let payload = self.payload.as_slice(); + let command = transmute!(&payload[..CMD_BASE_SIZE], CommandBase)?; + *code = command.code; + + match command.code { + FlashData | FlashDeflData | MemData => { + let cmd = transmute!(&payload[..DATA_CMD_SIZE], DataCommand)?; + return Ok(Command::Data( cmd, &payload[DATA_CMD_SIZE..]) ); + }, + Sync => return Ok(Command::Sync( transmute!(payload, SyncCommand)? )), + FlashBegin => return Ok(Command::Begin( transmute!(payload, BeginCommand)? )), + FlashEnd => return Ok(Command::End( transmute!(payload, EndCommand)? )), + WriteReg => return Ok(Command::WriteReg( transmute!(payload, WriteRegCommand)? )), + ReadReg => return Ok(Command::ReadReg( u32_from_slice(payload, CMD_BASE_SIZE))), + SpiSetParams => return Ok(Command::SpiSetParams( transmute!(payload, SpiSetParamsCommand)? )), + SpiAttach => return Ok(Command::SpiAttach( u32_from_slice(payload, CMD_BASE_SIZE))), + ChangeBaudrate => return Ok(Command::ChangeBaudrate( transmute!(payload, ChangeBaudrateCommand)? )), + SpiFlashMd5 => return Ok(Command::SpiFlashMd5( transmute!(payload, SpiFlashMd5Command)? )), + EraseRegion => return Ok(Command::EraseRegion( transmute!(payload, EraseRegionCommand)? )), + ReadFlash => return Ok(Command::ReadFlash( transmute!(payload, ReadFlashCommand)? )), + _ => return Ok(Command::Unwkown(command.code)) } + } - let command = slice_2_struct!(self.payload.as_slice(), FlashBeginCommand); - Ok(Command::FlashBegin(command)) + pub fn send_response(&mut self, resp: &Response) -> Result<(), Error> { + let resp_slice = unsafe{ to_u8_slice(resp) }; + write_delimiter(self.io)?; + write_packet(self.io, &resp_slice[..RESPONSE_SIZE])?; + write_packet(self.io, resp.data)?; + write_delimiter(self.io)?; + Ok(()) + } + + pub fn send_md5_response(&mut self, resp: &Response) -> Result<(), Error> { + let resp_slice = unsafe{ to_u8_slice(resp) }; + write_delimiter(self.io)?; + write_packet(self.io, &resp_slice[..RESPONSE_SIZE-2])?; + write_packet(self.io, resp.data)?; + write_packet(self.io, &resp_slice[RESPONSE_SIZE-2..])?; + write_delimiter(self.io)?; + Ok(()) } + pub fn process_commands(&mut self) -> Result<(), Error> { + + let mut command_code = Sync; + let offset = self.offset; + let command = self.wait_for_command(&mut command_code)?; // todo handle errors + let mut response = Response::new(command_code); + + match command { + Command::Sync(_) => (), + Command::ReadReg(address) => response.value(read_register(address)), + Command::WriteReg(reg) => write_register(reg.address, reg.value), + Command::Begin(cmd) => { + if cmd.packet_size > MAX_WRITE_BLOCK { + response.error(Error::BadBlocksize) + } else { + self.offset = cmd.offset; + self.total_size = cmd.total_size; + } + } + Command::Data(cmd, data) => { + let checksum = data.iter().fold(0xEF, |acc, x| acc + x); + let code = cmd.base.code; + if cmd.size != data.len() as u32 { + response.error(Error::BadDataLen) + } else if cmd.base.checksum != checksum as u32 { + response.error(Error::BadDataChecksum) + } else { + if let Err(err) = memory_write(code, offset, data) { + response.error(err); + } + self.offset += data.len() as u32 + } + } + Command::End(cmd) => { + if cmd.run_user_code == 1 { + run_user_code(); + } + } + Command::SpiFlashMd5(cmd) => { + match calculate_md5(cmd.address, cmd.size) { + Ok(md5) => { + response.data(&md5); + return self.send_md5_response(&response); + } + Err(err) => response.error(err) + } + } + Command::SpiSetParams(cmd) => { + spi_set_params(&cmd.params); + } + Command::SpiAttach(param) => { + spi_attach(param); + } + Command::ChangeBaudrate(baud) => { + change_baudrate(baud.old, baud.new); + } + Command::EraseFlash => { + erase_flash().unwrap_or_else(|e| response.error(e)); // unwrap_or_else ? + } + Command::EraseRegion(reg) => { + erase_region(reg.address, reg.size).unwrap_or_else(|e| response.error(e)); + } + Command::ReadFlash(cmd) => { + read_flash(&cmd.params).unwrap_or_else(|e| response.error(e)); + } + Command::RunUserCode => { + run_user_code(); + } + Command::Unwkown(_) => { + response.error(Error::InvalidCommand); + } + }; + + self.send_response(&response)?; + Ok(()) + } + } + + pub fn calculate_md5(mut address: u32, mut size: u32) -> Result<[u8; 16], Error> { + let mut buffer: [u8; FLASH_SECTOR_SIZE as usize] = unsafe { MaybeUninit::uninit().assume_init() }; + let mut hasher = Md5::new(); + + while size > 0 { + let to_read = min(size, FLASH_SECTOR_SIZE); + spi_read(address, to_read, &mut buffer).map_err(|_| Error::Err0x63 )?; + hasher.update(buffer); + size -= to_read; + address += to_read; + } + + let result: [u8; 16] = hasher.finalize().into(); + Ok(result) + } + + fn spi_read(_address: u32, _size: u32, _data: &mut [u8]) -> Result<(), ErrorIO> { + todo!(); } } @@ -185,7 +289,6 @@ mod slip { } pub fn write_packet(io: &mut dyn InputIO, packet: &[u8]) -> Result<(), ErrorIO> { - io.write(&[0xC0])?; for byte in packet { match byte { 0xC0 => io.write(&[0xDB, 0xDC])?, @@ -193,6 +296,10 @@ mod slip { other => io.write(&[*other])?, } } + Ok(()) + } + + pub fn write_delimiter(io: &mut dyn InputIO) -> Result<(), ErrorIO> { io.write(&[0xC0]) } } @@ -202,12 +309,14 @@ mod tests { use super::*; use super::ErrorIO::*; use super::stub::*; - use super::stub::Error::*; + // use super::stub::Error::*; use super::slip::{read_packet, write_packet}; use assert2::{assert, let_assert}; // use matches::assert_matches; use std::collections::VecDeque; use std::vec::Vec; + use crate::commands::*; + use mockall::predicate; struct MockIO { data: VecDeque @@ -294,31 +403,26 @@ mod tests { fn test_write_packet() { let mut io = MockIO::new(); - // 0xC0 is added at the beginning and end of payload - assert!( write_packet(&mut io, &[1, 2, 3]) == Ok(())); - assert!( io.written() == &[0xC0, 1, 2, 3, 0xC0] ); - io.clear(); - // 0xC0 is replaced with 0xDB 0xDC assert!( write_packet(&mut io, &[1, 0xC0, 3]) == Ok(())); - assert!( io.written() == &[0xC0, 1, 0xDB, 0xDC, 3, 0xC0] ); + assert!( io.written() == &[1, 0xDB, 0xDC, 3] ); io.clear(); // 0xDB is replaced with 0xDB 0xDD assert!( write_packet(&mut io, &[1, 0xDB, 3]) == Ok(())); - assert!( io.written() == &[0xC0, 1, 0xDB, 0xDD, 3, 0xC0] ); + assert!( io.written() == &[1, 0xDB, 0xDD, 3] ); io.clear(); } #[test] fn test_wait_for_packet() { - - // Returns InvalidMessageReceived after receiving incomplete message + let mut dummy = CommandCode::Sync; + // Check FlashBegin command let mut io = MockIO::from_slice(&[ 0xC0, - 1, - CommandType::FlashBegin as u8, - 4, 0, + 0, // direction + CommandCode::FlashBegin as u8, + 16, 0, // size 1, 0, 0, 0, // checksum 2, 0, 0, 0, // erase_addr 3, 0, 0, 0, // packt_count @@ -326,11 +430,138 @@ mod tests { 5, 0, 0, 0, // offset 0xC0]); let mut stub = Stub::new(&mut io); - let_assert!( Ok(Command::FlashBegin(cmd)) = stub.wait_for_command() ); - println!("{:?}", cmd); - assert!(cmd.base.direction == 1); + let_assert!( Ok(Command::Begin(cmd)) = stub.wait_for_command(&mut dummy) ); + assert!( {cmd.base.direction == 0} ); + assert!( {cmd.base.code == CommandCode::FlashBegin} ); + assert!( {cmd.base.size == 16} ); + assert!( {cmd.base.checksum == 1} ); + assert!( {cmd.total_size == 2} ); + assert!( {cmd.packt_count == 3} ); + assert!( {cmd.packet_size == 4} ); + assert!( {cmd.offset == 5} ); + + // Check FlashData command + let mut io = MockIO::from_slice(&[ + 0xC0, + 0, // direction + CommandCode::FlashData as u8, + 20, 0, // size + 1, 0, 0, 0, // checksum + 4, 0, 0, 0, // size + 3, 0, 0, 0, // sequence_num + 0, 0, 0, 0, // reserved 1 + 0, 0, 0, 0, // reserved 1 + 9, 8, 7, 6, // payload + 0xC0]); + let mut stub = Stub::new(&mut io); + let_assert!( Ok(Command::Data(cmd, data)) = stub.wait_for_command(&mut dummy) ); + assert!( {cmd.base.code == CommandCode::FlashData} ); + assert!( {cmd.base.size == 20} ); + assert!( {cmd.base.checksum == 1} ); + assert!( {cmd.size == 4} ); + assert!( {cmd.sequence_num == 3} ); + assert!( {cmd.reserved[0] == 0} ); + assert!( {cmd.reserved[1] == 0} ); + assert!( data == &[9, 8, 7, 6] ); + + // Check Sync command + let mut io = MockIO::from_slice(&[ + 0xC0, + 0, // direction + CommandCode::Sync as u8, + 36, 0, // size + 1, 0, 0, 0, // checksum + 0x7, 0x7, 0x12, 0x20, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0xC0]); + let mut stub = Stub::new(&mut io); + let_assert!( Ok(Command::Sync(_)) = stub.wait_for_command(&mut dummy) ); - // let _slice = [1 as u8, 0xAu8, 16u16]; + // Check ReadReg command + let mut io = MockIO::from_slice(&[ + 0xC0, + 0, // direction + CommandCode::ReadReg as u8, + 4, 0, // size + 1, 0, 0, 0, // checksum + 200, 0, 0, 0, // address + 0xC0]); + let mut stub = Stub::new(&mut io); + let_assert!( Ok(Command::ReadReg(address)) = stub.wait_for_command(&mut dummy) ); + assert!( address == 200 ); + } + + #[test] + fn test_send_response() { + + // Can write error response + let mut io = MockIO::new(); + let mut stub = Stub::new(&mut io); + let mut response = Response::new(CommandCode::FlashBegin); + response.error(Error::BadDataChecksum); + let expected = &[0xC0, 1, CommandCode::FlashBegin as u8, 2,0, 0,0,0,0, 1, Error::BadDataChecksum as u8, 0xC0]; + assert!( stub.send_response(&response) == Ok(())); + assert!( io.written() == expected); + + // Can write response with data + let mut io = MockIO::new(); + let mut stub = Stub::new(&mut io); + let data = &[1, 2, 3, 4, 5, 6, 7, 8]; + let mut response = Response::new(CommandCode::FlashBegin); + response.data(data); + let expected = &[0xC0, 1, CommandCode::FlashBegin as u8, 10,0, 0,0,0,0, 0,0, 1, 2, 3, 4, 5, 6, 7, 8, 0xC0]; + assert!( stub.send_response(&response) == Ok(())); + assert!( io.written() == expected); + } + + // fn slice_to_cmd(slice: &[u8]) -> Vec { + // let mut v = Vec::new(); + // v.push(0xC0); + // v.extend_from_slice( slice ); + // v.push(0xC0); + // v + // } + + fn decorate_command(data: T) -> Vec { + let mut v = Vec::new(); + v.push(0xC0); + v.extend_from_slice( unsafe{ to_u8_slice(&data) } ); + v.push(0xC0); + v + } + + #[repr(C, packed(1))] + pub struct TestResponse { + pub direction: u8, + pub command: CommandCode, + pub size: u16, + pub value: u32, + pub status: u8, + pub error: u8, + } + + #[test] + fn test_read_register() { + let cmd = ReadRegCommand { + base: CommandBase { direction: 1, code: CommandCode::ReadReg, size: 4, checksum: 0 }, + address: 200 + }; + let mut io = MockIO::from_slice(&decorate_command(cmd)); + let mut stub = Stub::new(&mut io); + + let ctx = target::read_register_context(); + ctx.expect().with(predicate::eq(200)).returning(|x| x + 1); + assert!( Ok(()) == stub.process_commands() ); + + // let resp = TestResponse { + // direction: 1, command: CommandCode::ReadReg, size: 2, value: 201, status: 0, error: 0 + // }; + // let expect = decorate_command(resp); + let expect = &[0xC0, 1, CommandCode::ReadReg as u8, 2,0, 201,0,0,0, 0,0, 0xC0]; + assert!( io.written() == expect ); } #[test] diff --git a/src/targets.rs b/src/targets.rs new file mode 100644 index 0000000..1c377d1 --- /dev/null +++ b/src/targets.rs @@ -0,0 +1,74 @@ + +#[cfg(test)] +use mockall::automock; + +#[allow(unused)] +extern "C" { + fn esp_rom_spiflash_erase_chip() -> i32; + fn esp_rom_spiflash_erase_block(block_number: u32) -> i32; + fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32; + /// address (4 byte alignment), data, length + fn esp_rom_spiflash_write(dest_addr: u32, data: *const u8, len: u32) -> i32; + /// address (4 byte alignment), data, length + fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32; + fn esp_rom_spiflash_unlock() -> i32; + // fn esp_rom_spiflash_lock(); // can't find in idf defs? + fn esp_rom_spiflash_attach(config: u32, legacy: bool); + + fn uart_tx_one_char(byte: u8); + + fn ets_efuse_get_spiconfig() -> u32; +} + + +#[cfg_attr(test, automock)] +pub mod esp32c3 { + use crate::commands::*; + use super::*; + + pub fn read_register(address: u32) -> u32 { + unsafe { *(address as *const u32) } + } + + pub fn write_register(address: u32, value: u32) { + // let reg_ptr = address as *mut u32; + unsafe { *(address as *mut u32) = value; } + todo!(); + } + + pub fn memory_write(_mem_type: CommandCode, address: u32, data: &[u8]) -> Result<(), Error>{ + let err = unsafe { esp_rom_spiflash_write(address, data.as_ptr(), data.len() as u32) }; + + if err == 0 { Ok(()) } else { Err(Error::FailedSpiOp) } } + + pub fn run_user_code() { + todo!(); + } + + pub fn spi_set_params(_params: &SpiParams) { + todo!(); + } + + pub fn spi_attach(_param: u32) { + todo!(); + } + + pub fn change_baudrate(_old: u32, _new: u32) { + todo!(); + } + + pub fn erase_flash() -> Result<(), Error> { + // Can return FailedSpiOp (1, 2) + todo!(); + } + + pub fn erase_region(_address: u32, _size: u32) -> Result<(), Error> { + // Can return FailedSpiOp (?) + todo!(); + } + + pub fn read_flash(_params: &ReadFlashParams) -> Result<(), Error> { + // Can return FailedSpiOp (?) + todo!(); + } +} From a40815f95a0ae7bb069a942a0a934e56fa475459 Mon Sep 17 00:00:00 2001 From: Martin Valik Date: Wed, 6 Apr 2022 15:55:57 +0200 Subject: [PATCH 04/10] Can be Flashed --- .cargo/config.toml | 4 +- Cargo.lock | 3 + Cargo.toml | 9 ++ build.rs | 14 +- ld/rom.x | 6 +- ld/stub.x | 311 +++++++++++++++++++++++++++++++++++++++++++++ partitions.csv | 4 +- src/commands.rs | 5 + src/main.rs | 165 ++++++++++++++---------- src/protocol.rs | 275 ++++++++++++++++++++------------------- src/targets.rs | 225 +++++++++++++++++++++++++++++--- 11 files changed, 795 insertions(+), 226 deletions(-) create mode 100644 ld/stub.x diff --git a/.cargo/config.toml b/.cargo/config.toml index a124839..b9f55bb 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -8,5 +8,7 @@ target = "riscv32imc-unknown-none-elf" [target.'cfg(any(target_arch="riscv32"))'] rustflags = [ - "-C", "link-arg=-Tlinkall.x" + # "-C", "link-arg=-Tmy_link.x", + # "-C", "link-arg=-Tld/rom.x", + "-C", "link-args=-Map=target/stub.map", ] diff --git a/Cargo.lock b/Cargo.lock index 0be8539..a8bf179 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -731,12 +731,15 @@ name = "stub" version = "0.1.0" dependencies = [ "assert2", + "embedded-hal", + "esp-hal-common", "esp32c3-hal", "heapless", "matches", "md-5", "mockall", "mockall_double", + "nb 1.0.0", "riscv-rt", ] diff --git a/Cargo.toml b/Cargo.toml index fb1ec3e..71d733a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,18 @@ mockall_double = "0.1.0" md-5 = { version = "0.10.1", default-features = false } [target.'cfg(target_arch = "riscv32")'.dependencies] esp32c3-hal = { path = "../esp-hal/esp32c3-hal" } +esp-hal-common = { path = "../esp-hal/esp-hal-common" } riscv-rt = "0.8.1" +nb = "1.0.0" +embedded-hal = "0.2.7" [dev-dependencies] assert2 = "0.3.6" matches = "0.1.9" mockall = "0.11.0" + +[profile.release] +# strip = true # Automatically strip symbols from the binary. +# opt-level = "z" +# lto = true +# panic = "abort" \ No newline at end of file diff --git a/build.rs b/build.rs index b9fe7cd..c0c7700 100644 --- a/build.rs +++ b/build.rs @@ -1,14 +1,18 @@ -use std::path::PathBuf; -use std::{env, fs}; +use std::{env, fs, path::PathBuf}; fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); println!("cargo:rustc-link-search)={}", out_dir.display()); - #[cfg(target_arch = "riscv32")] - { + + // #[cfg(target_arch = "riscv32")] + // { + fs::copy("ld/stub.x", out_dir.join("stub.x")).unwrap(); + println!("cargo:rerun-if-changed=ld/stub.x"); + println!("cargo:rustc-link-arg=-Tld/stub.x"); + fs::copy("ld/rom.x", out_dir.join("rom.x")).unwrap(); println!("cargo:rerun-if-changed=ld/ld/rom.x"); println!("cargo:rustc-link-arg=-Tld/rom.x"); - } + // } } diff --git a/ld/rom.x b/ld/rom.x index 4dd48c8..2f69841 100644 --- a/ld/rom.x +++ b/ld/rom.x @@ -73,5 +73,9 @@ PROVIDE( Disable_QMode = 0x40000224 ); PROVIDE( Enable_QMode = 0x40000228 ); PROVIDE( ets_efuse_get_spiconfig = 0x4000071c ); PROVIDE( uart_tx_one_char = 0x40000068 ); - +PROVIDE( uart_div_modify = 0x40000088 ); +PROVIDE( ets_get_apb_freq = 0x40000580 ); +PROVIDE( ets_get_cpu_frequency = 0x40000584 ); +PROVIDE( software_reset = 0x40000090 ); +PROVIDE( ets_delay_us = 0x40000050 ); /* Do not exceed this mark in the error messages above | */ diff --git a/ld/stub.x b/ld/stub.x new file mode 100644 index 0000000..74c408b --- /dev/null +++ b/ld/stub.x @@ -0,0 +1,311 @@ +MEMORY +{ + /* + https://github.com/espressif/esptool/blob/master/esptool.py#L1919 + MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], + [0x3C000000, 0x3C800000, "DROM"], + [0x3FC80000, 0x3FCE0000, "DRAM"], + [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"], + [0x3FF00000, 0x3FF20000, "DROM_MASK"], + [0x40000000, 0x40060000, "IROM_MASK"], + [0x42000000, 0x42800000, "IROM"], + [0x4037C000, 0x403E0000, "IRAM"], + [0x50000000, 0x50002000, "RTC_IRAM"], + [0x50000000, 0x50002000, "RTC_DRAM"], + [0x600FE000, 0x60100000, "MEM_INTERNAL2"]] + */ + /* 400K of on soc RAM, 16K reserved for cache */ + ICACHE : ORIGIN = 0x4037C000, LENGTH = 0x4000 + /* Instruction RAM */ + IRAM : ORIGIN = 0x4037C000 + 0x4000, LENGTH = 400K - 0x4000 + /* Data RAM */ + DRAM : ORIGIN = 0x3FC80000, LENGTH = 0x50000 + + + /* External flash */ + /* Instruction ROM */ + IROM : ORIGIN = 0x42000000 + 0x20, LENGTH = 0x400000 - 0x20 + /* Data ROM */ + DROM : ORIGIN = 0x3C000000, LENGTH = 0x400000 + + /* RTC fast memory (executable). Persists over deep sleep. */ + RTC_FAST : ORIGIN = 0x50000000, LENGTH = 0x2000 /*- ESP_BOOTLOADER_RESERVE_RTC*/ +} + +REGION_ALIAS("REGION_TEXT", IROM); +REGION_ALIAS("REGION_RODATA", DROM); + +REGION_ALIAS("REGION_DATA", DRAM); +REGION_ALIAS("REGION_BSS", DRAM); +REGION_ALIAS("REGION_HEAP", DRAM); +REGION_ALIAS("REGION_STACK", DRAM); + +REGION_ALIAS("REGION_RWTEXT", IRAM); +REGION_ALIAS("REGION_RTC_FAST", RTC_FAST); + + +ENTRY(_start_hal) +PROVIDE(_start_trap = _start_trap_hal); + +PROVIDE(_stext = ORIGIN(REGION_TEXT)); +PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 32K); +PROVIDE(_heap_size = 0); + +PROVIDE(UserSoft = DefaultHandler); +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(UserTimer = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(UserExternal = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > REGION_TEXT + + .text _stext : + { + _stext = .; + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + /* KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + (*(.trap)); + (*(.trap.rust)); + + *(.text .text.*); */ + _etext = .; + } > REGION_TEXT + + /** + * This dummy section represents the .text section but in rodata. + * Thus, it must have its alignement and (at least) its size. + */ + .text_dummy (NOLOAD): + { + /* Start at the same alignement constraint than .text */ + . = ALIGN(ALIGNOF(.text)); + /* Create an empty gap as big as .text section */ + . = . + SIZEOF(.text); + /* Prepare the alignement of the section above. Few bytes (0x20) must be + * added for the mapping header. */ + . = ALIGN(0x10000) + 0x20; + } > REGION_RODATA + + .rodata : ALIGN(4) + { + _srodata = .; + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + _erodata = .; + } > REGION_RODATA + + .rwtext : ALIGN(4) { + _irwtext = LOADADDR(.rwtext); + _srwtext = .; + *(.rwtext); + . = ALIGN(4); + + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + (*(.trap)); + (*(.trap.rust)); + *(.text .text.*); + + _erwtext = .; + } > REGION_RWTEXT + + /* similar as text_dummy */ + .ram_dummy (NOLOAD) : { + . = ALIGN(ALIGNOF(.rwtext)); + . = . + SIZEOF(.rwtext); + } > REGION_DATA + + .data : ALIGN(4) + { + _sidata = LOADADDR(.data); + _sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(4); + _edata = .; + } > REGION_DATA + + .bss (NOLOAD) : + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(4); + _ebss = .; + } > REGION_BSS + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > REGION_HEAP + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > REGION_STACK + + .rtc_fast.text : ALIGN(4) { + *(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*) + } > REGION_RTC_FAST AT > REGION_RODATA + + .rtc_fast.data : ALIGN(4) + { + _rtc_fast_data_start = ABSOLUTE(.); + *(.rtc_fast.data .rtc_fast.data.*) + _rtc_fast_data_end = ABSOLUTE(.); + } > REGION_RTC_FAST AT > REGION_RODATA + + .rtc_fast.bss (NOLOAD) : ALIGN(4) + { + _rtc_fast_bss_start = ABSOLUTE(.); + *(.rtc_fast.bss .rtc_fast.bss.*) + _rtc_fast_bss_end = ABSOLUTE(.); + } > REGION_RTC_FAST + + .rtc_fast.noinit (NOLOAD) : ALIGN(4) + { + *(.rtc_fast.noinit .rtc_fast.noinit.*) + } > REGION_RTC_FAST + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_DATA) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_DATA must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_STACK) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 4 == 0 && _edata % 4 == 0, " +BUG(riscv-rt): .data is not 4-byte aligned"); + +ASSERT(_sidata % 4 == 0, " +BUG(riscv-rt): the LMA of .data is not 4-byte aligned"); + +ASSERT(_sbss % 4 == 0 && _ebss % 4 == 0, " +BUG(riscv-rt): .bss is not 4-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), " +ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region. +Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ + + +PROVIDE(interrupt1 = DefaultHandler); +PROVIDE(interrupt2 = DefaultHandler); +PROVIDE(interrupt3 = DefaultHandler); +PROVIDE(interrupt4 = DefaultHandler); +PROVIDE(interrupt5 = DefaultHandler); +PROVIDE(interrupt6 = DefaultHandler); +PROVIDE(interrupt7 = DefaultHandler); +PROVIDE(interrupt8 = DefaultHandler); +PROVIDE(interrupt9 = DefaultHandler); +PROVIDE(interrupt10 = DefaultHandler); +PROVIDE(interrupt11 = DefaultHandler); +PROVIDE(interrupt12 = DefaultHandler); +PROVIDE(interrupt13 = DefaultHandler); +PROVIDE(interrupt14 = DefaultHandler); +PROVIDE(interrupt15 = DefaultHandler); +PROVIDE(interrupt16 = DefaultHandler); +PROVIDE(interrupt17 = DefaultHandler); +PROVIDE(interrupt18 = DefaultHandler); +PROVIDE(interrupt19 = DefaultHandler); +PROVIDE(interrupt20 = DefaultHandler); +PROVIDE(interrupt21 = DefaultHandler); +PROVIDE(interrupt22 = DefaultHandler); +PROVIDE(interrupt23 = DefaultHandler); +PROVIDE(interrupt24 = DefaultHandler); +PROVIDE(interrupt25 = DefaultHandler); +PROVIDE(interrupt26 = DefaultHandler); +PROVIDE(interrupt27 = DefaultHandler); +PROVIDE(interrupt28 = DefaultHandler); +PROVIDE(interrupt29 = DefaultHandler); +PROVIDE(interrupt30 = DefaultHandler); +PROVIDE(interrupt31 = DefaultHandler); diff --git a/partitions.csv b/partitions.csv index 712699d..462f2e5 100644 --- a/partitions.csv +++ b/partitions.csv @@ -1,3 +1,5 @@ # Partition table # Name, Type, SubType, Offset, Size, Flags -factory, app, factory, 0x10000, 0x3f0000, +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, diff --git a/src/commands.rs b/src/commands.rs index a44716e..84f8dc0 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -14,6 +14,11 @@ pub enum Error CmdNotImplemented = 0xFF, Err0x63 = 0x63, + Err0x32 = 0x32, + Err0x33 = 0x33, + Err0x34 = 0x34, + Err0x35 = 0x35, + RunUserCode = 0xEE, } #[derive(PartialEq, Copy, Clone, Debug)] diff --git a/src/main.rs b/src/main.rs index 71ac0e0..56ede02 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,88 +6,116 @@ mod protocol; mod commands; mod targets; -#[cfg(not(test))] +// #[cfg(not(test))] mod main { + #[allow(unused)] + extern "C" { + fn ets_get_apb_freq() -> u32; + fn ets_get_cpu_frequency() -> u32; + fn ets_efuse_get_spiconfig() -> u32; + } + use riscv_rt::entry; - use core::{fmt::Write, panic::PanicInfo}; + // use core::fmt::Write; + use core::panic::PanicInfo; + use crate::protocol::{InputIO, ErrorIO}; + use embedded_hal::serial::Read; + use nb; + use heapless; use esp32c3_hal::{ // clock_control::{sleep, ClockControl, XTAL_FREQUENCY_AUTO}, - // dport::Split, // dprintln, - // prelude::*, - // serial::{config::Config, Pins, Serial}, Serial, - // target, - // timer::Timer, + pac, + // pac::system, + // pac::uart0, + // Delay, }; - - // const BLINK_HZ: Hertz = Hertz(2); + use esp_hal_common::serial::Instance; + use crate::protocol::stub::Stub; + use crate::targets::esp32c3 as target; + + // fn init_uart(system: &system::RegisterBlock, uart: &uart0::RegisterBlock) { + // system.perip_clk_en0.modify(|_, w| w.uart_mem_clk_en().set_bit() + // .uart_clk_en().set_bit()); + // system.perip_rst_en0.modify(|_, w| w.uart1_rst().clear_bit()); + // system.perip_rst_en0.modify(|_, w| w.uart1_rst().clear_bit()); + // system.perip_rst_en0.modify(|_, w| w.uart1_rst().clear_bit()); + + // uart.clk_conf.modify(|_, w| w.rst_core().set_bit()); + // system.perip_rst_en0.modify(|_, w| w.uart1_rst().set_bit()); + // system.perip_rst_en0.modify(|_, w| w.uart1_rst().clear_bit()); + // uart.clk_conf.modify(|_, w| w.rst_core().clear_bit()); + // uart.id.modify(|_, w| w.reg_update().clear_bit()); + + // while uart.id.read().reg_update().bit_is_set() { } + // uart.clk_conf.modify(|_, w| unsafe{ w.sclk_sel().bits(1).sclk_div_num().bits(10) } ); + // uart.clkdiv.modify(|_, w| unsafe{ w.clkdiv().bits(138).frag().bits(13) } ); + // uart.id.modify(|_, w| w.reg_update().set_bit()); + // } + + struct StubIO<'a, T> { + io: &'a mut Serial + } + + impl<'a, T> StubIO<'a, T> { + pub fn new(serial: &'a mut Serial) -> Self { + StubIO { + io: serial, + } + } + } + + impl<'a, T: Instance> InputIO for StubIO<'a, T> { + fn read(&mut self) -> Result { + nb::block!(self.io.read()).map_err(|_| ErrorIO::Hardware) + } + + fn write(&mut self, bytes: &[u8]) -> Result<(), ErrorIO> + { + self.io.write_bytes(bytes).map_err(|_| ErrorIO::Hardware) + } + } #[entry] fn main() -> ! { - // let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); - - // let (_, dport_clock_control) = dp.DPORT.split(); - - // let clkcntrl = ClockControl::new( - // dp.RTCCNTL, - // dp.APB_CTRL, - // dport_clock_control, - // XTAL_FREQUENCY_AUTO, - // ) - // .unwrap(); - - // let (clkcntrl_config, mut watchdog) = clkcntrl.freeze().unwrap(); - // watchdog.disable(); - - // let (_, _, _, mut watchdog0) = Timer::new(dp.TIMG0, clkcntrl_config); - // let (_, _, _, mut watchdog1) = Timer::new(dp.TIMG1, clkcntrl_config); - // watchdog0.disable(); - // watchdog1.disable(); - - // let pins = dp.GPIO.split(); - - // let mut blinky = pins.gpio0.into_push_pull_output(); - - // Use UART1 as example: will cause dprintln statements not to be printed - // let serial: Serial<_, _, _> = Serial::new( - // dp.UART1, - // Pins { - // tx: pins.gpio1, - // rx: pins.gpio3, - // cts: None, - // rts: None, - // }, - // Config { - // // default configuration is 19200 baud, 8 data bits, 1 stop bit & no parity (8N1) - // baudrate: 115200.Hz(), - // ..Config::default() - // }, - // clkcntrl_config, - // ) - // .unwrap(); - - // let (mut tx, mut rx) = serial.split(); - - // writeln!(tx, "\n\nESP32 Started\n\n").unwrap(); - - // line will not be printed as using UART1 - // dprintln!("UART0\n"); + let peripherals = pac::Peripherals::take().unwrap(); + + // let delay = Delay::new(peripherals.SYSTIMER); + // init_uart(&peripherals.SYSTEM, &peripherals.UART1); + + let mut serial = Serial::new(peripherals.UART0).unwrap(); - loop { - // writeln!(tx, "Characters received: {:?}", rx.count()).unwrap(); + let mut serial_io = StubIO::new(&mut serial); + + let mut stub = Stub::new(&mut serial_io); + + let mut spiconfig = unsafe{ ets_efuse_get_spiconfig() }; + + let strapping = target::read_gpio_strap_reg(); + + if spiconfig == 0 && (strapping & 0x1c) == 0x08 { + spiconfig = 1; /* HSPI flash mode */ + } - // while let Ok(x) = rx.read() { - // write!(tx, "{} ({:#x}) ", if x >= 32 { x as char } else { '?' }, x).unwrap() - // } - // writeln!(tx, "").unwrap(); + target::spi_attach(spiconfig); - // blinky.set_high().unwrap(); - // sleep((Hertz(1_000_000) / BLINK_HZ).us()); - // blinky.set_low().unwrap(); - // sleep((Hertz(1_000_000) / BLINK_HZ).us()); + target::spi_set_default_params().unwrap(); + + let mut buffer = heapless::Vec::::new(); + + loop { + stub.read_command(&mut buffer).unwrap(); + stub.process_command(&buffer).unwrap(); + + // delay.delay(500u32 * 1000); + // serial.write_str("Hello world\n").unwrap(); + // let apb_freq = unsafe{ ets_get_apb_freq() }; + // let cpu_freq = unsafe{ ets_get_cpu_frequency() }; + // writeln!(serial, "APB: {}, CPU: {}", apb_freq, cpu_freq / 2).unwrap(); + // writeln!(tx, "Characters received: {:?}", rx.count()).unwrap(); } } @@ -97,4 +125,5 @@ mod main { loop {} } } - + +// ~/esp/esptool.py -p /dev/ttyUSB0 --chip esp32c3 write_flash --flash_mode dio --flash_size detect --flash_freq 40m 0x0 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/hello_world.bin diff --git a/src/protocol.rs b/src/protocol.rs index f5e718a..c9688d9 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -18,9 +18,10 @@ pub trait InputIO { fn write(&mut self, data: &[u8]) -> Result<(), ErrorIO>; } -type Buffer = heapless::Vec; -mod stub +type Buffer = heapless::Vec; + +pub mod stub { use super::*; use super::target::*; @@ -28,45 +29,21 @@ mod stub use core::mem; use core::slice; use core::cmp::min; - use core::mem::MaybeUninit; + // use core::mem::MaybeUninit; use crate::commands::*; use crate::commands::CommandCode::*; use md5::{Md5, Digest}; - + const DATA_CMD_SIZE: usize = mem::size_of::(); const CMD_BASE_SIZE: usize = mem::size_of::(); - - const MAX_WRITE_BLOCK: u32 = 0x4000; - - const FLASH_SECTOR_SIZE: u32 = 4096; - - #[derive(PartialEq, Copy, Clone)] - pub enum Command<'a> - { - Sync(SyncCommand), - Begin(BeginCommand), - Data(DataCommand, &'a[u8]), - End(EndCommand), - ReadReg(u32), - WriteReg(WriteRegCommand), - SpiSetParams(SpiSetParamsCommand), - SpiAttach(u32), - ChangeBaudrate(ChangeBaudrateCommand), - SpiFlashMd5(SpiFlashMd5Command), - EraseFlash, - EraseRegion(EraseRegionCommand), - ReadFlash(ReadFlashCommand), - RunUserCode, - Unwkown(CommandCode) - } - + const FLASH_SECTOR_SIZE: u32 = 4096; + const MAX_WRITE_BLOCK: u32 = 0x4000; pub struct Stub<'a> { io: &'a mut (dyn InputIO + 'a), - payload: Buffer, - // Begin - total_size: u32, - offset: u32, + end_addr: u32, + write_addr: u32, + erase_addr: u32, } impl From for Error { @@ -91,7 +68,7 @@ mod stub }; } - pub unsafe fn to_u8_slice(p: &T) -> &[u8] { + pub unsafe fn to_slice_u8(p: &T) -> &[u8] { slice::from_raw_parts( (p as *const T) as *const u8, mem::size_of::(), ) } @@ -105,42 +82,14 @@ mod stub pub fn new(input_io: &'a mut dyn InputIO) -> Self { Stub { io: input_io, - payload: heapless::Vec::new(), - offset: 0, - total_size: 0, - } - } - - pub fn wait_for_command(&mut self, code: &mut CommandCode) -> Result - { - read_packet(self.io, &mut self.payload)?; - - let payload = self.payload.as_slice(); - let command = transmute!(&payload[..CMD_BASE_SIZE], CommandBase)?; - *code = command.code; - - match command.code { - FlashData | FlashDeflData | MemData => { - let cmd = transmute!(&payload[..DATA_CMD_SIZE], DataCommand)?; - return Ok(Command::Data( cmd, &payload[DATA_CMD_SIZE..]) ); - }, - Sync => return Ok(Command::Sync( transmute!(payload, SyncCommand)? )), - FlashBegin => return Ok(Command::Begin( transmute!(payload, BeginCommand)? )), - FlashEnd => return Ok(Command::End( transmute!(payload, EndCommand)? )), - WriteReg => return Ok(Command::WriteReg( transmute!(payload, WriteRegCommand)? )), - ReadReg => return Ok(Command::ReadReg( u32_from_slice(payload, CMD_BASE_SIZE))), - SpiSetParams => return Ok(Command::SpiSetParams( transmute!(payload, SpiSetParamsCommand)? )), - SpiAttach => return Ok(Command::SpiAttach( u32_from_slice(payload, CMD_BASE_SIZE))), - ChangeBaudrate => return Ok(Command::ChangeBaudrate( transmute!(payload, ChangeBaudrateCommand)? )), - SpiFlashMd5 => return Ok(Command::SpiFlashMd5( transmute!(payload, SpiFlashMd5Command)? )), - EraseRegion => return Ok(Command::EraseRegion( transmute!(payload, EraseRegionCommand)? )), - ReadFlash => return Ok(Command::ReadFlash( transmute!(payload, ReadFlashCommand)? )), - _ => return Ok(Command::Unwkown(command.code)) + write_addr: 0, + end_addr: 0, + erase_addr: 0, } } pub fn send_response(&mut self, resp: &Response) -> Result<(), Error> { - let resp_slice = unsafe{ to_u8_slice(resp) }; + let resp_slice = unsafe{ to_slice_u8(resp) }; write_delimiter(self.io)?; write_packet(self.io, &resp_slice[..RESPONSE_SIZE])?; write_packet(self.io, resp.data)?; @@ -148,102 +97,172 @@ mod stub Ok(()) } - pub fn send_md5_response(&mut self, resp: &Response) -> Result<(), Error> { - let resp_slice = unsafe{ to_u8_slice(resp) }; + pub fn send_md5_response(&mut self, resp: &Response, md5: &[u8]) -> Result<(), Error> { + let resp_slice = unsafe{ to_slice_u8(resp) }; write_delimiter(self.io)?; write_packet(self.io, &resp_slice[..RESPONSE_SIZE-2])?; - write_packet(self.io, resp.data)?; - write_packet(self.io, &resp_slice[RESPONSE_SIZE-2..])?; + write_packet(self.io, md5)?; + write_packet(self.io, &resp_slice[RESPONSE_SIZE-2..RESPONSE_SIZE])?; write_delimiter(self.io)?; Ok(()) } - - pub fn process_commands(&mut self) -> Result<(), Error> { - - let mut command_code = Sync; - let offset = self.offset; - let command = self.wait_for_command(&mut command_code)?; // todo handle errors - let mut response = Response::new(command_code); + + fn process_begin(&mut self, cmd: &BeginCommand) -> Result<(), Error> { - match command { - Command::Sync(_) => (), - Command::ReadReg(address) => response.value(read_register(address)), - Command::WriteReg(reg) => write_register(reg.address, reg.value), - Command::Begin(cmd) => { + match cmd.base.code { + FlashBegin => { if cmd.packet_size > MAX_WRITE_BLOCK { - response.error(Error::BadBlocksize) - } else { - self.offset = cmd.offset; - self.total_size = cmd.total_size; + return Err(Error::BadBlocksize); } + self.write_addr = cmd.offset; + self.erase_addr = cmd.offset; + self.end_addr = cmd.offset + cmd.total_size; + // Todo check max size 16MB + return unlock_flash(); + }, + MemBegin => { + self.write_addr = cmd.offset; + self.end_addr = cmd.offset + cmd.total_size; } - Command::Data(cmd, data) => { - let checksum = data.iter().fold(0xEF, |acc, x| acc + x); - let code = cmd.base.code; - if cmd.size != data.len() as u32 { - response.error(Error::BadDataLen) - } else if cmd.base.checksum != checksum as u32 { - response.error(Error::BadDataChecksum) + _ => () + } + Ok(()) + } + + fn process_data(&mut self, cmd: &DataCommand, data: &[u8]) -> Result<(), Error> { + let checksum: u8 = data.iter().fold(0xEF, |acc, x| acc ^ x); + let code = cmd.base.code; + let data_len = data.len() as u32; + + if cmd.size != data_len { + return Err(Error::BadDataLen); + } else if cmd.base.checksum != checksum as u32 { + return Err(Error::BadDataChecksum); + } else { + while self.erase_addr < self.write_addr + data_len { + if self.end_addr >= self.erase_addr + FLASH_BLOCK_SIZE && + self.erase_addr % FLASH_BLOCK_SIZE == 0 { + flash_erase_block(self.erase_addr); + self.erase_addr += FLASH_BLOCK_SIZE; } else { - if let Err(err) = memory_write(code, offset, data) { - response.error(err); - } - self.offset += data.len() as u32 + flash_erase_sector(self.erase_addr); + self.erase_addr += FLASH_SECTOR_SIZE; } } - Command::End(cmd) => { + memory_write(code, self.write_addr, data)?; + self.write_addr += data_len + } + Ok(()) + } + + #[allow(unreachable_patterns)] + pub fn process_cmd(&mut self, payload: &[u8], + code: CommandCode, + response: &mut Response) -> Result { + + let mut response_sent = false; + + match code { + Sync => (), + ReadReg => { + let address = u32_from_slice(payload, CMD_BASE_SIZE); + response.value(read_register(address)); + } + WriteReg => { + let reg = transmute!(payload, WriteRegCommand)?; + write_register(reg.address, reg.value); + } + FlashBegin | MemBegin | FlashDeflBegin => { + let cmd = transmute!(payload, BeginCommand)?; + self.process_begin(&cmd)? + } + FlashData | FlashDeflData | MemData => { + let cmd = transmute!(&payload[..DATA_CMD_SIZE], DataCommand)?; + let data = &payload[DATA_CMD_SIZE..]; + self.process_data(&cmd, data)? + } + FlashEnd | MemEnd | FlashDeflEnd => { + let cmd = transmute!(payload, EndCommand)?; if cmd.run_user_code == 1 { - run_user_code(); + self.send_response(&response)?; + delay_us(10000); + soft_reset(); } } - Command::SpiFlashMd5(cmd) => { - match calculate_md5(cmd.address, cmd.size) { - Ok(md5) => { - response.data(&md5); - return self.send_md5_response(&response); - } - Err(err) => response.error(err) - } + SpiFlashMd5 => { + let cmd = transmute!(payload, SpiFlashMd5Command)?; + let md5 = calculate_md5(cmd.address, cmd.size)?; + self.send_md5_response(&response, &md5)?; + response_sent = true; } - Command::SpiSetParams(cmd) => { - spi_set_params(&cmd.params); + SpiSetParams => { + let cmd = transmute!(payload, SpiSetParamsCommand)?; + spi_set_params(&cmd.params)? } - Command::SpiAttach(param) => { + SpiAttach => { + let param = u32_from_slice(payload, CMD_BASE_SIZE); spi_attach(param); } - Command::ChangeBaudrate(baud) => { + ChangeBaudrate => { + let baud = transmute!(payload, ChangeBaudrateCommand)?; + self.send_response(&response)?; + delay_us(10000); change_baudrate(baud.old, baud.new); + response_sent = true; } - Command::EraseFlash => { - erase_flash().unwrap_or_else(|e| response.error(e)); // unwrap_or_else ? + EraseFlash => { + erase_flash()? } - Command::EraseRegion(reg) => { - erase_region(reg.address, reg.size).unwrap_or_else(|e| response.error(e)); + EraseRegion => { + let reg = transmute!(payload, EraseRegionCommand)?; + erase_region(reg.address, reg.size)? } - Command::ReadFlash(cmd) => { - read_flash(&cmd.params).unwrap_or_else(|e| response.error(e)); + ReadFlash => { + let cmd = transmute!(payload, ReadFlashCommand)?; + read_flash(&cmd.params)? } - Command::RunUserCode => { - run_user_code(); + RunUserCode => { + todo!(); } - Command::Unwkown(_) => { - response.error(Error::InvalidCommand); + _ => { + return Err(Error::InvalidCommand); } }; - self.send_response(&response)?; + Ok(response_sent) + } + + #[allow(unreachable_patterns)] + pub fn process_command(&mut self, payload: &[u8]) -> Result<(), Error> { + + let command = transmute!(&payload[..CMD_BASE_SIZE], CommandBase)?; + let mut response = Response::new(command.code); + + match self.process_cmd(payload, command.code, &mut response) { + Ok(response_sent) => match response_sent { + true => return Ok(()), + false => (), + } + Err(err) => response.error(err) + } + + self.send_response(&response) + } + + pub fn read_command(&mut self, buffer: &mut Buffer) -> Result<(), Error> { + read_packet(self.io, buffer)?; Ok(()) } } pub fn calculate_md5(mut address: u32, mut size: u32) -> Result<[u8; 16], Error> { - let mut buffer: [u8; FLASH_SECTOR_SIZE as usize] = unsafe { MaybeUninit::uninit().assume_init() }; + let mut buffer: [u8; FLASH_SECTOR_SIZE as usize] = [0; FLASH_SECTOR_SIZE as usize]; let mut hasher = Md5::new(); while size > 0 { let to_read = min(size, FLASH_SECTOR_SIZE); - spi_read(address, to_read, &mut buffer).map_err(|_| Error::Err0x63 )?; - hasher.update(buffer); + target::spi_flash_read(address, &mut buffer)?; + hasher.update(&buffer[0..to_read as usize]); size -= to_read; address += to_read; } @@ -251,10 +270,6 @@ mod stub let result: [u8; 16] = hasher.finalize().into(); Ok(result) } - - fn spi_read(_address: u32, _size: u32, _data: &mut [u8]) -> Result<(), ErrorIO> { - todo!(); - } } // Check command and its size, cast it @@ -528,7 +543,7 @@ mod tests { fn decorate_command(data: T) -> Vec { let mut v = Vec::new(); v.push(0xC0); - v.extend_from_slice( unsafe{ to_u8_slice(&data) } ); + v.extend_from_slice( unsafe{ to_slice_u8(&data) } ); v.push(0xC0); v } diff --git a/src/targets.rs b/src/targets.rs index 1c377d1..4289881 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -10,65 +10,250 @@ extern "C" { /// address (4 byte alignment), data, length fn esp_rom_spiflash_write(dest_addr: u32, data: *const u8, len: u32) -> i32; /// address (4 byte alignment), data, length - fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32; + fn esp_rom_spiflash_read(src_addr: u32, data: *const u8, len: u32) -> i32; fn esp_rom_spiflash_unlock() -> i32; // fn esp_rom_spiflash_lock(); // can't find in idf defs? fn esp_rom_spiflash_attach(config: u32, legacy: bool); + fn esp_rom_spiflash_config_param(device_id: u32, chip_size: u32, block_size: u32, + sector_size: u32, page_size: u32, status_mask: u32) -> u32; fn uart_tx_one_char(byte: u8); + fn uart_div_modify(uart_number: u32, baud_div: u32); fn ets_efuse_get_spiconfig() -> u32; + fn software_reset(); + fn ets_delay_us(timeout: u32); } #[cfg_attr(test, automock)] pub mod esp32c3 { use crate::commands::*; + use crate::commands::Error::*; + use crate::commands::CommandCode::*; use super::*; + const SPI_BASE_REG: u32 = 0x60002000; + const SPI_CMD_REG: u32 = SPI_BASE_REG + 0x00; + const SPI_ADDR_REG: u32 = SPI_BASE_REG + 0x04; + const SPI_RD_STATUS_REG: u32 = SPI_BASE_REG + 0x2C; + const SPI_EXT2_REG: u32 = SPI_BASE_REG + 0x54; + + const SPI0_BASE_REG: u32 = 0x60003000; + const SPI0_EXT2_REG: u32 = SPI0_BASE_REG + 0x54; + + const SPI_ST: u32 = 0x7; + const SPI_FLASH_RDSR: u32 = 1<<27; + const STATUS_WIP_BIT: u32 = 1<<0; + const SPI_FLASH_WREN: u32 = 1<<30; + const SPI_FLASH_SE: u32 = 1<<24; + const SPI_FLASH_BE: u32 = 1<<23; + + const UART_BASE_REG: u32 = 0x60000000; + const UART0_CLKDIV_REG: u32 = UART_BASE_REG + 0x14; + const UART_CLKDIV_M: u32 = 0x000FFFFF; + const UART_CLKDIV_FRAG_S: u32 = 20; + const UART_CLKDIV_FRAG_V: u32 = 0xF; + pub const FLASH_SECTOR_SIZE: u32 = 4096; + pub const FLASH_BLOCK_SIZE: u32 = 65536; + + const GPIO_BASE_REG: u32 = 0x60004000; + const GPIO_STRAP_REG: u32 = GPIO_BASE_REG + 0x38; + + const FLASH_MAX_SIZE: u32 = 16*1024*1024; + const FLASH_PAGE_SIZE: u32 = 256; + const FLASH_STATUS_MASK: u32 = 0xFFFF; + + fn get_uart_div(current_baud: u32, new_baud: u32) -> u32 { + let clock_div_reg = unsafe{ *(UART0_CLKDIV_REG as *const u32) }; + let uart_div = clock_div_reg & UART_CLKDIV_M; + let fraction = (clock_div_reg >> UART_CLKDIV_FRAG_S) & UART_CLKDIV_FRAG_V; + let uart_div = (uart_div << 4) + fraction; + (uart_div * current_baud) / new_baud + } + pub fn read_register(address: u32) -> u32 { unsafe { *(address as *const u32) } } pub fn write_register(address: u32, value: u32) { - // let reg_ptr = address as *mut u32; unsafe { *(address as *mut u32) = value; } - todo!(); } - pub fn memory_write(_mem_type: CommandCode, address: u32, data: &[u8]) -> Result<(), Error>{ - let err = unsafe { esp_rom_spiflash_write(address, data.as_ptr(), data.len() as u32) }; - - if err == 0 { Ok(()) } else { Err(Error::FailedSpiOp) } } + pub fn memory_write(mem_type: CommandCode, address: u32, data: &[u8]) -> Result<(), Error> { - pub fn run_user_code() { - todo!(); + let result = match mem_type { + FlashData => { + let mut remaining = data.len() as u32; + let mut written = 0; + + while remaining > 0 { + let to_write = core::cmp::min(FLASH_SECTOR_SIZE, remaining); + let data_ptr = data[written..].as_ptr(); + let err = unsafe { esp_rom_spiflash_write(address + written as u32, data_ptr, to_write) }; + if err != 0 { + return Err(Error::FailedSpiOp); + } + remaining -= to_write; + written += to_write as usize; + } + Ok(()) + }, + MemData => { + if data.len() % 4 != 0 { + Err(Error::BadDataLen) + } else { + let addr = address as *mut u32; + let (_, data_u32, _) = unsafe{ data.align_to::() }; + for word in data_u32 { + unsafe{ *addr = *word }; + } + Ok(()) + } + }, + _ => Ok(()) + }; + result } - pub fn spi_set_params(_params: &SpiParams) { - todo!(); + pub fn spi_set_params(params: &SpiParams) -> Result<(), Error> { + let result = unsafe{ esp_rom_spiflash_config_param( + params.id, + params.total_size, + params.block_size, + params.sector_size, + params.page_size, + params.status_mask) }; + + if result == 0 { Ok(()) } else { Err(Error::FailedSpiOp) } } + + pub fn spi_set_default_params() -> Result<(), Error> { + let params = SpiParams { + id: 0, + total_size: FLASH_MAX_SIZE, + block_size: FLASH_BLOCK_SIZE, + sector_size: FLASH_SECTOR_SIZE, + page_size: FLASH_PAGE_SIZE, + status_mask: FLASH_STATUS_MASK, + }; - pub fn spi_attach(_param: u32) { - todo!(); + spi_set_params(¶ms) } - pub fn change_baudrate(_old: u32, _new: u32) { - todo!(); + pub fn spi_attach(param: u32) { + unsafe{ esp_rom_spiflash_attach(param, false) }; } + pub fn change_baudrate(old: u32, new: u32) { + unsafe{ uart_div_modify(0, get_uart_div(old, new)) }; + } + pub fn erase_flash() -> Result<(), Error> { - // Can return FailedSpiOp (1, 2) - todo!(); + // Returns 1 or 2 in case of failure + let result = unsafe{ esp_rom_spiflash_erase_chip() }; + if result == 0 { Ok(()) } else { Err(Error::FailedSpiOp) } } - pub fn erase_region(_address: u32, _size: u32) -> Result<(), Error> { - // Can return FailedSpiOp (?) - todo!(); + fn erase(address: u32, block: bool) { + spiflash_wait_for_ready(); + spi_write_enable(); + wait_for_ready(); + + let command = if block { SPI_FLASH_BE } else { SPI_FLASH_SE }; + write_register(SPI_ADDR_REG, address); + write_register(SPI_CMD_REG, command); + while read_register(SPI_CMD_REG) != 0 { } + + spiflash_wait_for_ready(); + + // match unsafe{ esp_rom_spiflash_erase_block(address / FLASH_BLOCK_SIZE) } { // ??? + // 0 => Ok(()), + // _ => Err(Error::FailedSpiOp) + // } + } + + fn wait_for_ready() { + while (read_register(SPI_EXT2_REG) & SPI_ST) != 0 { } + while (read_register(SPI0_EXT2_REG) & SPI_ST) != 0 { } // ESP32_OR_LATER + } + + fn spiflash_wait_for_ready() { + wait_for_ready(); + + write_register(SPI_RD_STATUS_REG, 0); + write_register(SPI_CMD_REG, SPI_FLASH_RDSR); + while read_register(SPI_CMD_REG) != 0 { } + while (read_register(SPI_RD_STATUS_REG) & STATUS_WIP_BIT) != 0 {} + } + + fn spi_write_enable() { + write_register(SPI_CMD_REG, SPI_FLASH_WREN); + while read_register(SPI_CMD_REG) != 0 { } + } + + pub fn flash_erase_block(address: u32 ) { + // unsafe{ esp_rom_spiflash_erase_block(address / FLASH_BLOCK_SIZE) }; + erase(address, true); + } + + pub fn flash_erase_sector(address: u32 ) { + erase(address, false); + } + + + pub fn erase_region(address: u32, size: u32) -> Result<(), Error> { + if address % FLASH_SECTOR_SIZE != 0 { + return Err(Err0x32); + } + if size % FLASH_SECTOR_SIZE != 0 { + return Err(Err0x33); + } + if unsafe{ esp_rom_spiflash_unlock() } != 0 { + return Err(Err0x34); + } + + for offset in (0..size).step_by(FLASH_SECTOR_SIZE as usize) { + if unsafe{ esp_rom_spiflash_erase_sector(address + offset) } != 0 { + return Err(Err0x35); + } + } + + Ok(()) } pub fn read_flash(_params: &ReadFlashParams) -> Result<(), Error> { // Can return FailedSpiOp (?) todo!(); } + + pub fn spi_flash_read(address: u32, data: &mut [u8]) -> Result<(), Error> { + let data_ptr = data.as_mut_ptr(); + let data_len = data.len() as u32; + + match unsafe{ esp_rom_spiflash_read(address, data_ptr, data_len) } { + 0 => Ok(()), + _ => Err(Error::Err0x63) + } + } + + pub fn unlock_flash() -> Result<(), Error> { + if unsafe{ esp_rom_spiflash_unlock() } != 0 { + Err(FailedSpiUnlock) + } else { + Ok(()) + } + } + + pub fn read_gpio_strap_reg() -> u32 { + read_register(GPIO_STRAP_REG) + } + + pub fn soft_reset() { + unsafe { software_reset() }; + } + + pub fn delay_us(micro_seconds: u32) { + unsafe{ ets_delay_us(micro_seconds) }; + } } From 58f72009c153ddd41824b23e3060e9495ff1bd75 Mon Sep 17 00:00:00 2001 From: Martin Valik Date: Mon, 25 Apr 2022 13:44:51 +0200 Subject: [PATCH 05/10] BaudRate, EraseFash, EraseRegion works --- src/commands.rs | 4 ++-- src/main.rs | 2 +- src/protocol.rs | 33 +++++++++++++++++++++++++++++---- src/targets.rs | 26 +++++++++++++------------- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 84f8dc0..ee7080c 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -154,9 +154,9 @@ pub struct EraseRegionCommand { #[repr(C, packed(1))] pub struct ReadFlashParams { pub address: u32, - pub erase_size: u32, - pub sector_size: u32, + pub total_size: u32, pub packet_size: u32, + pub max_inflight: u32, } #[derive(PartialEq, Copy, Clone)] diff --git a/src/main.rs b/src/main.rs index 56ede02..d547c3c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -126,4 +126,4 @@ mod main { } } -// ~/esp/esptool.py -p /dev/ttyUSB0 --chip esp32c3 write_flash --flash_mode dio --flash_size detect --flash_freq 40m 0x0 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/hello_world.bin +// ~/esp/esptool.py -p /dev/ttyUSB0 -b 460800 --chip esp32c3 write_flash --flash_mode dio --flash_size detect --flash_freq 40m 0x0 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/hello_world.bin diff --git a/src/protocol.rs b/src/protocol.rs index c9688d9..f1d77f8 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -131,7 +131,6 @@ pub mod stub fn process_data(&mut self, cmd: &DataCommand, data: &[u8]) -> Result<(), Error> { let checksum: u8 = data.iter().fold(0xEF, |acc, x| acc ^ x); - let code = cmd.base.code; let data_len = data.len() as u32; if cmd.size != data_len { @@ -149,12 +148,38 @@ pub mod stub self.erase_addr += FLASH_SECTOR_SIZE; } } - memory_write(code, self.write_addr, data)?; + memory_write(cmd.base.code, self.write_addr, data)?; self.write_addr += data_len } Ok(()) } + // pub fn process_read_flash(&mut self, params: &ReadFlashParams) -> Result<(), Error> { + // // Can return FailedSpiOp (?) + // const BUF_SIZE: usize = FLASH_SECTOR_SIZE as usize; + // let mut buffer: [u8; BUF_SIZE] = [0; BUF_SIZE]; + // let mut address = params.address; + // let mut remaining = params.total_size; + // let to_ack = params.total_size / params.packet_size; + // let acked = 0; + // let mut ack_buf = heapless::Vec::::new(); + + // while acked < to_ack { + // while remaining > 0 && (to_ack - acked) < params.max_inflight { + // let len = min(params.packet_size, remaining); + // spi_flash_read(address, &mut buffer[..len as usize])?; + // write_packet(self.io, &buffer[..len as usize])?; + // remaining -= len; + // address += len; + // to_ack += 1; + // } + // read_packet(self.io, &mut ack_buf)?; + // acked = u32_from_slice(&ack_buf); + // } + + // Ok(()) + // } + #[allow(unreachable_patterns)] pub fn process_cmd(&mut self, payload: &[u8], code: CommandCode, @@ -219,7 +244,8 @@ pub mod stub } ReadFlash => { let cmd = transmute!(payload, ReadFlashCommand)?; - read_flash(&cmd.params)? + todo!(); + // self.process_read_flash(&cmd.params)? } RunUserCode => { todo!(); @@ -232,7 +258,6 @@ pub mod stub Ok(response_sent) } - #[allow(unreachable_patterns)] pub fn process_command(&mut self, payload: &[u8]) -> Result<(), Error> { let command = transmute!(&payload[..CMD_BASE_SIZE], CommandBase)?; diff --git a/src/targets.rs b/src/targets.rs index 4289881..1c44a70 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -65,7 +65,7 @@ pub mod esp32c3 { const FLASH_STATUS_MASK: u32 = 0xFFFF; fn get_uart_div(current_baud: u32, new_baud: u32) -> u32 { - let clock_div_reg = unsafe{ *(UART0_CLKDIV_REG as *const u32) }; + let clock_div_reg = read_register(UART0_CLKDIV_REG); let uart_div = clock_div_reg & UART_CLKDIV_M; let fraction = (clock_div_reg >> UART_CLKDIV_FRAG_S) & UART_CLKDIV_FRAG_V; let uart_div = (uart_div << 4) + fraction; @@ -80,7 +80,7 @@ pub mod esp32c3 { unsafe { *(address as *mut u32) = value; } } - pub fn memory_write(mem_type: CommandCode, address: u32, data: &[u8]) -> Result<(), Error> { + pub fn memory_write(mem_type: CommandCode, mut address: u32, data: &[u8]) -> Result<(), Error> { let result = match mem_type { FlashData => { @@ -90,12 +90,13 @@ pub mod esp32c3 { while remaining > 0 { let to_write = core::cmp::min(FLASH_SECTOR_SIZE, remaining); let data_ptr = data[written..].as_ptr(); - let err = unsafe { esp_rom_spiflash_write(address + written as u32, data_ptr, to_write) }; + let err = unsafe { esp_rom_spiflash_write(address, data_ptr, to_write) }; if err != 0 { return Err(Error::FailedSpiOp); } remaining -= to_write; written += to_write as usize; + address += to_write; } Ok(()) }, @@ -151,8 +152,10 @@ pub mod esp32c3 { pub fn erase_flash() -> Result<(), Error> { // Returns 1 or 2 in case of failure - let result = unsafe{ esp_rom_spiflash_erase_chip() }; - if result == 0 { Ok(()) } else { Err(Error::FailedSpiOp) } + match unsafe{ esp_rom_spiflash_erase_chip() } { + 0 => Ok(()), + _ => Err(Error::FailedSpiOp) + } } fn erase(address: u32, block: bool) { @@ -201,7 +204,6 @@ pub mod esp32c3 { erase(address, false); } - pub fn erase_region(address: u32, size: u32) -> Result<(), Error> { if address % FLASH_SECTOR_SIZE != 0 { return Err(Err0x32); @@ -213,8 +215,11 @@ pub mod esp32c3 { return Err(Err0x34); } - for offset in (0..size).step_by(FLASH_SECTOR_SIZE as usize) { - if unsafe{ esp_rom_spiflash_erase_sector(address + offset) } != 0 { + let sector_start = address / FLASH_SECTOR_SIZE; + let sector_end = sector_start + (size / FLASH_SECTOR_SIZE); + + for sector in sector_start..sector_end { + if unsafe{ esp_rom_spiflash_erase_sector(sector) } != 0 { return Err(Err0x35); } } @@ -222,11 +227,6 @@ pub mod esp32c3 { Ok(()) } - pub fn read_flash(_params: &ReadFlashParams) -> Result<(), Error> { - // Can return FailedSpiOp (?) - todo!(); - } - pub fn spi_flash_read(address: u32, data: &mut [u8]) -> Result<(), Error> { let data_ptr = data.as_mut_ptr(); let data_len = data.len() as u32; From d106814dbee905763fa68105a1bd4306e142f9b8 Mon Sep 17 00:00:00 2001 From: Martin Valik Date: Tue, 26 Apr 2022 14:09:22 +0200 Subject: [PATCH 06/10] ReadFlash Implemented --- src/main.rs | 7 ++- src/protocol.rs | 122 +++++++++++++++++++++++++++++------------------- 2 files changed, 78 insertions(+), 51 deletions(-) diff --git a/src/main.rs b/src/main.rs index d547c3c..47a8c9c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,6 @@ mod main { use crate::protocol::{InputIO, ErrorIO}; use embedded_hal::serial::Read; use nb; - use heapless; use esp32c3_hal::{ // clock_control::{sleep, ClockControl, XTAL_FREQUENCY_AUTO}, @@ -104,11 +103,11 @@ mod main { target::spi_set_default_params().unwrap(); - let mut buffer = heapless::Vec::::new(); + let mut buffer: [u8; 0x5000] = [0; 0x5000]; loop { - stub.read_command(&mut buffer).unwrap(); - stub.process_command(&buffer).unwrap(); + let data = stub.read_command(&mut buffer).unwrap(); + stub.process_command(data).unwrap(); // delay.delay(500u32 * 1000); // serial.write_str("Hello world\n").unwrap(); diff --git a/src/protocol.rs b/src/protocol.rs index f1d77f8..460b9e2 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -91,8 +91,8 @@ pub mod stub pub fn send_response(&mut self, resp: &Response) -> Result<(), Error> { let resp_slice = unsafe{ to_slice_u8(resp) }; write_delimiter(self.io)?; - write_packet(self.io, &resp_slice[..RESPONSE_SIZE])?; - write_packet(self.io, resp.data)?; + write_raw(self.io, &resp_slice[..RESPONSE_SIZE])?; + write_raw(self.io, resp.data)?; write_delimiter(self.io)?; Ok(()) } @@ -100,9 +100,9 @@ pub mod stub pub fn send_md5_response(&mut self, resp: &Response, md5: &[u8]) -> Result<(), Error> { let resp_slice = unsafe{ to_slice_u8(resp) }; write_delimiter(self.io)?; - write_packet(self.io, &resp_slice[..RESPONSE_SIZE-2])?; - write_packet(self.io, md5)?; - write_packet(self.io, &resp_slice[RESPONSE_SIZE-2..RESPONSE_SIZE])?; + write_raw(self.io, &resp_slice[..RESPONSE_SIZE-2])?; + write_raw(self.io, md5)?; + write_raw(self.io, &resp_slice[RESPONSE_SIZE-2..RESPONSE_SIZE])?; write_delimiter(self.io)?; Ok(()) } @@ -154,31 +154,50 @@ pub mod stub Ok(()) } - // pub fn process_read_flash(&mut self, params: &ReadFlashParams) -> Result<(), Error> { - // // Can return FailedSpiOp (?) - // const BUF_SIZE: usize = FLASH_SECTOR_SIZE as usize; - // let mut buffer: [u8; BUF_SIZE] = [0; BUF_SIZE]; - // let mut address = params.address; - // let mut remaining = params.total_size; - // let to_ack = params.total_size / params.packet_size; - // let acked = 0; - // let mut ack_buf = heapless::Vec::::new(); + pub fn calculate_md5(mut address: u32, mut size: u32) -> Result<[u8; 16], Error> { + let mut buffer: [u8; FLASH_SECTOR_SIZE as usize] = [0; FLASH_SECTOR_SIZE as usize]; + let mut hasher = Md5::new(); - // while acked < to_ack { - // while remaining > 0 && (to_ack - acked) < params.max_inflight { - // let len = min(params.packet_size, remaining); - // spi_flash_read(address, &mut buffer[..len as usize])?; - // write_packet(self.io, &buffer[..len as usize])?; - // remaining -= len; - // address += len; - // to_ack += 1; - // } - // read_packet(self.io, &mut ack_buf)?; - // acked = u32_from_slice(&ack_buf); - // } - - // Ok(()) - // } + while size > 0 { + let to_read = min(size, FLASH_SECTOR_SIZE); + target::spi_flash_read(address, &mut buffer)?; + hasher.update(&buffer[0..to_read as usize]); + size -= to_read; + address += to_read; + } + + let result: [u8; 16] = hasher.finalize().into(); + Ok(result) + } + + pub fn process_read_flash(&mut self, params: &ReadFlashParams) -> Result<(), Error> { + // Can return FailedSpiOp (?) + const BUF_SIZE: usize = FLASH_SECTOR_SIZE as usize; + let mut buffer: [u8; BUF_SIZE] = [0; BUF_SIZE]; + let mut address = params.address; + let mut remaining = params.total_size; + let to_ack = params.total_size / params.packet_size; + let mut acked = 0; + let mut ack_buf: [u8; 4] = [0; 4]; + let mut hasher = Md5::new(); + + while acked < to_ack { + while remaining > 0 && (to_ack - acked) < params.max_inflight { + let len = min(params.packet_size, remaining); + spi_flash_read(address, &mut buffer[..len as usize])?; + write_packet(self.io, &buffer[..len as usize])?; + hasher.update(&buffer[0..len as usize]); + remaining -= len; + address += len; + } + let resp = read_packet(self.io, &mut ack_buf)?; + acked = u32_from_slice(resp, 0); + } + + let md5: [u8; 16] = hasher.finalize().into(); + write_packet(self.io, &md5)?; + Ok(()) + } #[allow(unreachable_patterns)] pub fn process_cmd(&mut self, payload: &[u8], @@ -240,12 +259,13 @@ pub mod stub } EraseRegion => { let reg = transmute!(payload, EraseRegionCommand)?; - erase_region(reg.address, reg.size)? + erase_region(reg.address, reg.size)?; } ReadFlash => { + self.send_response(&response)?; let cmd = transmute!(payload, ReadFlashCommand)?; - todo!(); - // self.process_read_flash(&cmd.params)? + self.process_read_flash(&cmd.params)?; + response_sent = true; } RunUserCode => { todo!(); @@ -274,9 +294,9 @@ pub mod stub self.send_response(&response) } - pub fn read_command(&mut self, buffer: &mut Buffer) -> Result<(), Error> { - read_packet(self.io, buffer)?; - Ok(()) + pub fn read_command<'c, 'd>(&'c mut self, buffer: &'d mut [u8]) -> Result<&'d [u8], Error> { + let data = read_packet(self.io, buffer)?; + Ok(data) } } @@ -308,28 +328,30 @@ mod slip { } } - pub fn read_packet(io: &mut dyn InputIO, packet: &mut Buffer) -> Result<(), ErrorIO> { - packet.clear(); + pub fn read_packet<'c, 'd>(io: &'c mut dyn InputIO, packet: &'d mut [u8]) -> Result<&'d [u8], ErrorIO> { while io.read()? != 0xC0 { } // Replase: 0xDB 0xDC -> 0xC0 and 0xDB 0xDD -> 0xDB + let mut i = 0; loop { match io.read()? { 0xDB => match io.read()? { - 0xDC => packet.push(0xC0)?, - 0xDD => packet.push(0xDB)?, + 0xDC => packet[i] = 0xC0, + 0xDD => packet[i] = 0xDB, _ => return Err(InvalidResponse), } 0xC0 => break, - other => packet.push(other)?, + other => packet[i] = other, }; + i += 1; } - Ok(()) + + Ok(&packet[..i]) } - pub fn write_packet(io: &mut dyn InputIO, packet: &[u8]) -> Result<(), ErrorIO> { - for byte in packet { + pub fn write_raw(io: &mut dyn InputIO, data: &[u8]) -> Result<(), ErrorIO> { + for byte in data { match byte { 0xC0 => io.write(&[0xDB, 0xDC])?, 0xDB => io.write(&[0xDB, 0xDD])?, @@ -338,6 +360,12 @@ mod slip { } Ok(()) } + + pub fn write_packet(io: &mut dyn InputIO, data: &[u8]) -> Result<(), ErrorIO> { + write_delimiter(io)?; + write_raw(io, data)?; + write_delimiter(io) + } pub fn write_delimiter(io: &mut dyn InputIO) -> Result<(), ErrorIO> { io.write(&[0xC0]) @@ -350,7 +378,7 @@ mod tests { use super::ErrorIO::*; use super::stub::*; // use super::stub::Error::*; - use super::slip::{read_packet, write_packet}; + use super::slip::{read_packet, write_raw}; use assert2::{assert, let_assert}; // use matches::assert_matches; use std::collections::VecDeque; @@ -440,16 +468,16 @@ mod tests { } #[test] - fn test_write_packet() { + fn test_write_raw() { let mut io = MockIO::new(); // 0xC0 is replaced with 0xDB 0xDC - assert!( write_packet(&mut io, &[1, 0xC0, 3]) == Ok(())); + assert!( write_raw(&mut io, &[1, 0xC0, 3]) == Ok(())); assert!( io.written() == &[1, 0xDB, 0xDC, 3] ); io.clear(); // 0xDB is replaced with 0xDB 0xDD - assert!( write_packet(&mut io, &[1, 0xDB, 3]) == Ok(())); + assert!( write_raw(&mut io, &[1, 0xDB, 3]) == Ok(())); assert!( io.written() == &[1, 0xDB, 0xDD, 3] ); io.clear(); } From 6a3a78bedb5a2bbbeffc840749c3fa9b2a30e174 Mon Sep 17 00:00:00 2001 From: Martin Valik Date: Mon, 9 May 2022 14:15:03 +0200 Subject: [PATCH 07/10] Compressed image can be flashed --- ld/rom.x | 1 + src/commands.rs | 1 - src/main.rs | 1 + src/miniz_types.rs | 88 ++++++++++++++ src/protocol.rs | 281 +++++++++++++++++++++++++++++++++------------ src/targets.rs | 81 ++++++------- 6 files changed, 340 insertions(+), 113 deletions(-) create mode 100644 src/miniz_types.rs diff --git a/ld/rom.x b/ld/rom.x index 2f69841..9656cb9 100644 --- a/ld/rom.x +++ b/ld/rom.x @@ -78,4 +78,5 @@ PROVIDE( ets_get_apb_freq = 0x40000580 ); PROVIDE( ets_get_cpu_frequency = 0x40000584 ); PROVIDE( software_reset = 0x40000090 ); PROVIDE( ets_delay_us = 0x40000050 ); +PROVIDE( tinfl_decompress = 0x400000f4 ); /* Do not exceed this mark in the error messages above | */ diff --git a/src/commands.rs b/src/commands.rs index ee7080c..917d653 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -18,7 +18,6 @@ pub enum Error Err0x33 = 0x33, Err0x34 = 0x34, Err0x35 = 0x35, - RunUserCode = 0xEE, } #[derive(PartialEq, Copy, Clone, Debug)] diff --git a/src/main.rs b/src/main.rs index 47a8c9c..a8f646b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod protocol; mod commands; mod targets; +mod miniz_types; // #[cfg(not(test))] mod main { diff --git a/src/miniz_types.rs b/src/miniz_types.rs new file mode 100644 index 0000000..1bcf5c7 --- /dev/null +++ b/src/miniz_types.rs @@ -0,0 +1,88 @@ +const TINFL_MAX_HUFF_TABLES: usize = 3; +const TINFL_MAX_HUFF_SYMBOLS_0: usize = 288; +const TINFL_MAX_HUFF_SYMBOLS_1: usize = 32; +const TINFL_MAX_HUFF_SYMBOLS_2: usize = 19; +const TINFL_FAST_LOOKUP_BITS: usize = 10; +const TINFL_FAST_LOOKUP_SIZE: usize = 1 << TINFL_FAST_LOOKUP_BITS; + +pub const TINFL_FLAG_PARSE_ZLIB_HEADER: u32 = 1; +pub const TINFL_FLAG_HAS_MORE_INPUT: u32 = 2; +pub const TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: u32 = 4; +pub const TINFL_FLAG_COMPUTE_ADLER32: u32 = 8; + +#[repr(C)] +#[derive(PartialEq, PartialOrd)] +pub enum TinflStatus { + FailedCannotMakeProgress = -4, + BadParam = -3, + Adler32Mismatch = -2, + Failed = -1, + Done = 0, + NeedsMoreInput = 1, + HasMoreOutput = 2 +} + +#[derive(Clone, Copy)] +#[repr(C, packed(1))] +struct tinfl_huff_table +{ + code_size: [u8; TINFL_MAX_HUFF_SYMBOLS_0], + look_up: [u16;TINFL_FAST_LOOKUP_SIZE], + tree: [u16; TINFL_MAX_HUFF_SYMBOLS_0 * 2] +} + +#[repr(C, packed(1))] +pub struct tinfl_decompressor +{ + pub state: u32, + num_bits: u32, + zhdr0: u32, + zhdr1: u32, + z_adler32: u32, + m_final: u32, + m_type: u32, + check_adler32: u32, + dist: u32, + counter: u32, + num_extra: u32, + table_sizes: [u32; TINFL_MAX_HUFF_TABLES], + bit_buf: u64, + dist_from_out_buf_start: u32, + tables: [tinfl_huff_table; TINFL_MAX_HUFF_TABLES], + raw_header: [u8; 4], + len_codes: [u8; TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137] +} + +impl Default for tinfl_huff_table { + fn default() -> Self { + tinfl_huff_table { + code_size: [0; TINFL_MAX_HUFF_SYMBOLS_0], + look_up: [0;TINFL_FAST_LOOKUP_SIZE], + tree: [0; TINFL_MAX_HUFF_SYMBOLS_0 * 2] + } + } +} + +impl Default for tinfl_decompressor { + fn default() -> Self { + tinfl_decompressor { + state: 0, + num_bits: 0, + zhdr0: 0, + zhdr1: 0, + z_adler32: 0, + m_final: 0, + m_type: 0, + check_adler32: 0, + dist: 0, + counter: 0, + num_extra: 0, + table_sizes: [0; TINFL_MAX_HUFF_TABLES], + bit_buf: 0, + dist_from_out_buf_start: 0, + tables: [Default::default(); TINFL_MAX_HUFF_TABLES], + raw_header: [0; 4], + len_codes: [0; TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137] + } + } +} \ No newline at end of file diff --git a/src/protocol.rs b/src/protocol.rs index 460b9e2..8f12f71 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -32,7 +32,9 @@ pub mod stub // use core::mem::MaybeUninit; use crate::commands::*; use crate::commands::CommandCode::*; + use crate::commands::Error::*; use md5::{Md5, Digest}; + use crate::miniz_types::*; const DATA_CMD_SIZE: usize = mem::size_of::(); const CMD_BASE_SIZE: usize = mem::size_of::(); @@ -44,6 +46,12 @@ pub mod stub end_addr: u32, write_addr: u32, erase_addr: u32, + remaining: u32, + remaining_compressed: usize, + decompressor: tinfl_decompressor, + last_error: Option, + in_flash_mode: bool, + total_decompressed_size: usize, } impl From for Error { @@ -56,7 +64,7 @@ pub mod stub fn slice_to_struct(slice: &[u8]) -> Result { if SIZE != slice.len() { - return Err(Error::BadDataLen); + return Err(BadDataLen); } let array: &[u8; SIZE] = &slice[0..SIZE].try_into().unwrap(); unsafe { Ok(mem::transmute_copy::<[u8; SIZE], T>(array)) } @@ -75,7 +83,22 @@ pub mod stub fn u32_from_slice(slice: &[u8], index: usize) -> u32 { u32::from_le_bytes(slice[index..index+4].try_into().unwrap()) } - + + fn calculate_md5(mut address: u32, mut size: u32) -> Result<[u8; 16], Error> { + let mut buffer: [u8; FLASH_SECTOR_SIZE as usize] = [0; FLASH_SECTOR_SIZE as usize]; + let mut hasher = Md5::new(); + + while size > 0 { + let to_read = min(size, FLASH_SECTOR_SIZE); + target::spi_flash_read(address, &mut buffer)?; + hasher.update(&buffer[0..to_read as usize]); + size -= to_read; + address += to_read; + } + + let result: [u8; 16] = hasher.finalize().into(); + Ok(result) + } impl<'a> Stub<'a> { @@ -85,10 +108,16 @@ pub mod stub write_addr: 0, end_addr: 0, erase_addr: 0, + remaining: 0, + remaining_compressed: 0, + decompressor: Default::default(), + last_error: None, + in_flash_mode: false, + total_decompressed_size: 0, } } - pub fn send_response(&mut self, resp: &Response) -> Result<(), Error> { + fn send_response(&mut self, resp: &Response) -> Result<(), Error> { let resp_slice = unsafe{ to_slice_u8(resp) }; write_delimiter(self.io)?; write_raw(self.io, &resp_slice[..RESPONSE_SIZE])?; @@ -97,7 +126,7 @@ pub mod stub Ok(()) } - pub fn send_md5_response(&mut self, resp: &Response, md5: &[u8]) -> Result<(), Error> { + fn send_md5_response(&mut self, resp: &Response, md5: &[u8]) -> Result<(), Error> { let resp_slice = unsafe{ to_slice_u8(resp) }; write_delimiter(self.io)?; write_raw(self.io, &resp_slice[..RESPONSE_SIZE-2])?; @@ -108,69 +137,197 @@ pub mod stub } fn process_begin(&mut self, cmd: &BeginCommand) -> Result<(), Error> { + + self.write_addr = cmd.offset; + self.erase_addr = cmd.offset; + self.end_addr = cmd.offset + cmd.total_size; + self.remaining_compressed = (cmd.packt_count * cmd.packet_size) as usize; + self.remaining = cmd.total_size; + self.decompressor.state = 0; match cmd.base.code { - FlashBegin => { + FlashBegin | FlashDeflBegin => { if cmd.packet_size > MAX_WRITE_BLOCK { - return Err(Error::BadBlocksize); + return Err(BadBlocksize); } - self.write_addr = cmd.offset; - self.erase_addr = cmd.offset; - self.end_addr = cmd.offset + cmd.total_size; - // Todo check max size 16MB - return unlock_flash(); - }, - MemBegin => { - self.write_addr = cmd.offset; - self.end_addr = cmd.offset + cmd.total_size; + // Todo: check for 16MB flash only + self.in_flash_mode = true; + unlock_flash()?; } - _ => () + _ => () // Do nothing for MemBegin } + Ok(()) } - - fn process_data(&mut self, cmd: &DataCommand, data: &[u8]) -> Result<(), Error> { - let checksum: u8 = data.iter().fold(0xEF, |acc, x| acc ^ x); - let data_len = data.len() as u32; - if cmd.size != data_len { - return Err(Error::BadDataLen); - } else if cmd.base.checksum != checksum as u32 { - return Err(Error::BadDataChecksum); - } else { - while self.erase_addr < self.write_addr + data_len { - if self.end_addr >= self.erase_addr + FLASH_BLOCK_SIZE && - self.erase_addr % FLASH_BLOCK_SIZE == 0 { - flash_erase_block(self.erase_addr); - self.erase_addr += FLASH_BLOCK_SIZE; - } else { - flash_erase_sector(self.erase_addr); - self.erase_addr += FLASH_SECTOR_SIZE; - } + fn process_end(&mut self, cmd: &EndCommand, response: &Response) -> Result<(), Error> { + + if cmd.base.code == MemEnd { + + let addr = self.erase_addr as *const u32; + let length = self.end_addr - self.erase_addr; + let slice = unsafe { slice::from_raw_parts(addr, length as usize) }; + let mut memory: [u32; 32] = [0; 32]; + memory.copy_from_slice(&slice); + + return match self.remaining { + 0 => Ok(()), + _ => Err(NotEnoughData) } - memory_write(cmd.base.code, self.write_addr, data)?; - self.write_addr += data_len + } else if !self.in_flash_mode { + return Err(NotInFlashMode); + } else if self.remaining > 0 { + return Err(NotEnoughData); + } + + self.in_flash_mode = false; + + if cmd.run_user_code == 1 { + self.send_response(&response)?; + delay_us(10000); + soft_reset(); } + Ok(()) } - pub fn calculate_md5(mut address: u32, mut size: u32) -> Result<[u8; 16], Error> { - let mut buffer: [u8; FLASH_SECTOR_SIZE as usize] = [0; FLASH_SECTOR_SIZE as usize]; - let mut hasher = Md5::new(); - - while size > 0 { - let to_read = min(size, FLASH_SECTOR_SIZE); - target::spi_flash_read(address, &mut buffer)?; - hasher.update(&buffer[0..to_read as usize]); - size -= to_read; - address += to_read; + fn write_ram(&mut self, data: &[u8]) -> Result<(), Error> { + let data_len = data.len() as u32; + + if self.write_addr == 0 && data_len > 0 { + return Err(NotInFlashMode); + } else if data_len > self.remaining { + return Err(TooMuchData); + } else if data_len % 4 != 0 { + return Err(BadDataLen); + } + + let (_, data_u32, _) = unsafe{ data.align_to::() }; + + for word in data_u32 { + let memory = self.write_addr as *mut u32; + unsafe{ *memory = *word }; + self.write_addr += 4; + } + + Ok(()) + } + + fn flash_data(&mut self, data: &[u8]) -> Result<(), Error> { + + let mut address = self.write_addr; + let mut remaining = data.len() as u32; + let mut written = 0; + + // Erase flash + while self.erase_addr < self.write_addr + remaining { + if self.end_addr >= self.erase_addr + FLASH_BLOCK_SIZE && + self.erase_addr % FLASH_BLOCK_SIZE == 0 { + flash_erase_block(self.erase_addr); + self.erase_addr += FLASH_BLOCK_SIZE; + } else { + flash_erase_sector(self.erase_addr); + self.erase_addr += FLASH_SECTOR_SIZE; + } + } + + // Write flash + while remaining > 0 { + let to_write = min(FLASH_SECTOR_SIZE, remaining); + let data_ptr = data[written..].as_ptr(); + spiflash_write(address, data_ptr, to_write)?; + remaining -= to_write; + written += to_write as usize; + address += to_write; } + + self.write_addr += written as u32; + self.remaining -= min(self.remaining, written as u32); + + Ok(()) + } + + fn flash_defl_data(&mut self, data: &[u8]) -> Result<(), Error> { + use crate::miniz_types::TinflStatus::*; + + const OUT_BUFFER_SIZE: usize = 0x8000; //32768; + static mut DECOMPRESS_BUF: [u8; OUT_BUFFER_SIZE] = [0; OUT_BUFFER_SIZE]; + static mut DECOMPRESS_INDEX: usize = 0; + + let mut out_index = unsafe{ DECOMPRESS_INDEX }; + let out_buf = unsafe{ &mut DECOMPRESS_BUF }; + let mut in_index = 0; + let mut length = data.len(); + let mut status = NeedsMoreInput; + let mut flags = TINFL_FLAG_PARSE_ZLIB_HEADER; + + while length > 0 && self.remaining > 0 && status != Done { + let mut in_bytes = length; + let mut out_bytes = out_buf.len() - out_index; + let next_out: *mut u8 = out_buf[out_index..].as_mut_ptr(); + + if self.remaining_compressed > length { + flags |= TINFL_FLAG_HAS_MORE_INPUT; + } - let result: [u8; 16] = hasher.finalize().into(); - Ok(result) + status = target::decompress( + &mut self.decompressor, + data[in_index..].as_ptr(), + &mut in_bytes, + out_buf.as_mut_ptr(), + next_out, + &mut out_bytes, + flags); + + self.remaining_compressed -= in_bytes; + length -= in_bytes; + in_index += in_bytes; + out_index += out_bytes; + + if status == Done || out_index == OUT_BUFFER_SIZE { + self.flash_data(&out_buf[..out_index])?; + self.total_decompressed_size += out_index; + out_index = 0; + } + } + + unsafe{ DECOMPRESS_INDEX = out_index }; + + // error won't get sent back to esptool.py until next block is sent + if status < Done { + self.last_error = Some(InflateError); + } else if status == Done && self.remaining > 0 { + self.last_error = Some(NotEnoughData); + } else if status != Done && self.remaining == 0 { + self.last_error = Some(TooMuchData); + } + + Ok(()) + } + + fn process_data(&mut self, cmd: &DataCommand, data: &[u8]) -> Result<(), Error> { + + let checksum: u8 = data.iter().fold(0xEF, |acc, x| acc ^ x); + + if !self.in_flash_mode { + return Err(NotInFlashMode); + } else if cmd.size != data.len() as u32 { + return Err(BadDataLen); + } else if cmd.base.checksum != checksum as u32 { + return Err(BadDataChecksum); + } + + match cmd.base.code { + FlashDeflData => self.flash_defl_data(data)?, + FlashData => self.flash_data(data)?, + MemData => self.write_ram(data)?, + _ => () + } + + Ok(()) } - pub fn process_read_flash(&mut self, params: &ReadFlashParams) -> Result<(), Error> { + fn process_read_flash(&mut self, params: &ReadFlashParams) -> Result<(), Error> { // Can return FailedSpiOp (?) const BUF_SIZE: usize = FLASH_SECTOR_SIZE as usize; let mut buffer: [u8; BUF_SIZE] = [0; BUF_SIZE]; @@ -200,7 +357,7 @@ pub mod stub } #[allow(unreachable_patterns)] - pub fn process_cmd(&mut self, payload: &[u8], + fn process_cmd(&mut self, payload: &[u8], code: CommandCode, response: &mut Response) -> Result { @@ -227,11 +384,7 @@ pub mod stub } FlashEnd | MemEnd | FlashDeflEnd => { let cmd = transmute!(payload, EndCommand)?; - if cmd.run_user_code == 1 { - self.send_response(&response)?; - delay_us(10000); - soft_reset(); - } + self.process_end(&cmd, &response)?; } SpiFlashMd5 => { let cmd = transmute!(payload, SpiFlashMd5Command)?; @@ -271,7 +424,7 @@ pub mod stub todo!(); } _ => { - return Err(Error::InvalidCommand); + return Err(InvalidCommand); } }; @@ -299,22 +452,6 @@ pub mod stub Ok(data) } } - - pub fn calculate_md5(mut address: u32, mut size: u32) -> Result<[u8; 16], Error> { - let mut buffer: [u8; FLASH_SECTOR_SIZE as usize] = [0; FLASH_SECTOR_SIZE as usize]; - let mut hasher = Md5::new(); - - while size > 0 { - let to_read = min(size, FLASH_SECTOR_SIZE); - target::spi_flash_read(address, &mut buffer)?; - hasher.update(&buffer[0..to_read as usize]); - size -= to_read; - address += to_read; - } - - let result: [u8; 16] = hasher.finalize().into(); - Ok(result) - } } // Check command and its size, cast it diff --git a/src/targets.rs b/src/targets.rs index 1c44a70..4a51c6d 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -2,6 +2,8 @@ #[cfg(test)] use mockall::automock; +use crate::miniz_types::*; + #[allow(unused)] extern "C" { fn esp_rom_spiflash_erase_chip() -> i32; @@ -23,6 +25,15 @@ extern "C" { fn ets_efuse_get_spiconfig() -> u32; fn software_reset(); fn ets_delay_us(timeout: u32); + pub fn tinfl_decompress( + r: *mut tinfl_decompressor, + in_buf: *const u8, + in_buf_size: *mut usize, + out_buf_start: *mut u8, + out_buf_next: *mut u8, + out_buf_size: *mut usize, + flags: u32 + ) -> TinflStatus; } @@ -30,7 +41,7 @@ extern "C" { pub mod esp32c3 { use crate::commands::*; use crate::commands::Error::*; - use crate::commands::CommandCode::*; + // use crate::commands::CommandCode::*; use super::*; const SPI_BASE_REG: u32 = 0x60002000; @@ -80,41 +91,11 @@ pub mod esp32c3 { unsafe { *(address as *mut u32) = value; } } - pub fn memory_write(mem_type: CommandCode, mut address: u32, data: &[u8]) -> Result<(), Error> { - - let result = match mem_type { - FlashData => { - let mut remaining = data.len() as u32; - let mut written = 0; - - while remaining > 0 { - let to_write = core::cmp::min(FLASH_SECTOR_SIZE, remaining); - let data_ptr = data[written..].as_ptr(); - let err = unsafe { esp_rom_spiflash_write(address, data_ptr, to_write) }; - if err != 0 { - return Err(Error::FailedSpiOp); - } - remaining -= to_write; - written += to_write as usize; - address += to_write; - } - Ok(()) - }, - MemData => { - if data.len() % 4 != 0 { - Err(Error::BadDataLen) - } else { - let addr = address as *mut u32; - let (_, data_u32, _) = unsafe{ data.align_to::() }; - for word in data_u32 { - unsafe{ *addr = *word }; - } - Ok(()) - } - }, - _ => Ok(()) - }; - result + pub fn spiflash_write(dest_addr: u32, data: *const u8, len: u32) -> Result<(), Error> { + match unsafe{ esp_rom_spiflash_write(dest_addr, data, len) } { + 0 => Ok(()), + _ => Err(FailedSpiOp) + } } pub fn spi_set_params(params: &SpiParams) -> Result<(), Error> { @@ -126,7 +107,7 @@ pub mod esp32c3 { params.page_size, params.status_mask) }; - if result == 0 { Ok(()) } else { Err(Error::FailedSpiOp) } + if result == 0 { Ok(()) } else { Err(FailedSpiOp) } } pub fn spi_set_default_params() -> Result<(), Error> { @@ -154,7 +135,7 @@ pub mod esp32c3 { // Returns 1 or 2 in case of failure match unsafe{ esp_rom_spiflash_erase_chip() } { 0 => Ok(()), - _ => Err(Error::FailedSpiOp) + _ => Err(FailedSpiOp) } } @@ -172,7 +153,7 @@ pub mod esp32c3 { // match unsafe{ esp_rom_spiflash_erase_block(address / FLASH_BLOCK_SIZE) } { // ??? // 0 => Ok(()), - // _ => Err(Error::FailedSpiOp) + // _ => Err(FailedSpiOp) // } } @@ -233,7 +214,7 @@ pub mod esp32c3 { match unsafe{ esp_rom_spiflash_read(address, data_ptr, data_len) } { 0 => Ok(()), - _ => Err(Error::Err0x63) + _ => Err(Err0x63) } } @@ -256,4 +237,24 @@ pub mod esp32c3 { pub fn delay_us(micro_seconds: u32) { unsafe{ ets_delay_us(micro_seconds) }; } + + pub fn decompress( + r: *mut tinfl_decompressor, + in_buf: *const u8, + in_buf_size: *mut usize, + out_buf_start: *mut u8, + out_buf_next: *mut u8, + out_buf_size: *mut usize, + flags: u32 + ) -> TinflStatus { + unsafe { tinfl_decompress( + r, + in_buf, + in_buf_size, + out_buf_start, + out_buf_next, + out_buf_size, + flags, + ) } + } } From 7860a977f5cd65964ebb6a1735b86034996f9993 Mon Sep 17 00:00:00 2001 From: Martin Valik Date: Wed, 11 May 2022 15:08:13 +0200 Subject: [PATCH 08/10] Cleanup --- src/main.rs | 98 +----- src/miniz_types.rs | 16 +- src/protocol.rs | 776 ++++++++++++++++++++++----------------------- src/targets.rs | 58 ++-- 4 files changed, 435 insertions(+), 513 deletions(-) diff --git a/src/main.rs b/src/main.rs index a8f646b..f6f2d99 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,123 +7,55 @@ mod commands; mod targets; mod miniz_types; -// #[cfg(not(test))] +#[cfg(not(test))] mod main { - #[allow(unused)] - extern "C" { - fn ets_get_apb_freq() -> u32; - fn ets_get_cpu_frequency() -> u32; - fn ets_efuse_get_spiconfig() -> u32; - } - use riscv_rt::entry; - // use core::fmt::Write; use core::panic::PanicInfo; use crate::protocol::{InputIO, ErrorIO}; use embedded_hal::serial::Read; - use nb; - - use esp32c3_hal::{ - // clock_control::{sleep, ClockControl, XTAL_FREQUENCY_AUTO}, - // dprintln, - Serial, - pac, - // pac::system, - // pac::uart0, - // Delay, - }; + use esp32c3_hal::{ Serial, pac }; use esp_hal_common::serial::Instance; - use crate::protocol::stub::Stub; + use crate::protocol::Stub; use crate::targets::esp32c3 as target; + use nb; - // fn init_uart(system: &system::RegisterBlock, uart: &uart0::RegisterBlock) { - // system.perip_clk_en0.modify(|_, w| w.uart_mem_clk_en().set_bit() - // .uart_clk_en().set_bit()); - // system.perip_rst_en0.modify(|_, w| w.uart1_rst().clear_bit()); - // system.perip_rst_en0.modify(|_, w| w.uart1_rst().clear_bit()); - // system.perip_rst_en0.modify(|_, w| w.uart1_rst().clear_bit()); - - // uart.clk_conf.modify(|_, w| w.rst_core().set_bit()); - // system.perip_rst_en0.modify(|_, w| w.uart1_rst().set_bit()); - // system.perip_rst_en0.modify(|_, w| w.uart1_rst().clear_bit()); - // uart.clk_conf.modify(|_, w| w.rst_core().clear_bit()); - // uart.id.modify(|_, w| w.reg_update().clear_bit()); - - // while uart.id.read().reg_update().bit_is_set() { } - // uart.clk_conf.modify(|_, w| unsafe{ w.sclk_sel().bits(1).sclk_div_num().bits(10) } ); - // uart.clkdiv.modify(|_, w| unsafe{ w.clkdiv().bits(138).frag().bits(13) } ); - // uart.id.modify(|_, w| w.reg_update().set_bit()); - // } - - struct StubIO<'a, T> { - io: &'a mut Serial - } - - impl<'a, T> StubIO<'a, T> { - pub fn new(serial: &'a mut Serial) -> Self { - StubIO { - io: serial, - } - } - } - - impl<'a, T: Instance> InputIO for StubIO<'a, T> { - fn read(&mut self) -> Result { - nb::block!(self.io.read()).map_err(|_| ErrorIO::Hardware) + impl<'a, T: Instance> InputIO for Serial { + fn recv(&mut self) -> Result { + nb::block!(self.read()).map_err(|_| ErrorIO::Hardware) } - fn write(&mut self, bytes: &[u8]) -> Result<(), ErrorIO> + fn send(&mut self, bytes: &[u8]) -> Result<(), ErrorIO> { - self.io.write_bytes(bytes).map_err(|_| ErrorIO::Hardware) + self.write_bytes(bytes).map_err(|_| ErrorIO::Hardware) } } + const MSG_BUFFER_SIZE: usize = 0x5000; + #[entry] fn main() -> ! { let peripherals = pac::Peripherals::take().unwrap(); - // let delay = Delay::new(peripherals.SYSTIMER); - // init_uart(&peripherals.SYSTEM, &peripherals.UART1); - let mut serial = Serial::new(peripherals.UART0).unwrap(); - let mut serial_io = StubIO::new(&mut serial); - - let mut stub = Stub::new(&mut serial_io); + let mut stub = Stub::new(&mut serial); - let mut spiconfig = unsafe{ ets_efuse_get_spiconfig() }; + stub.send_greeting().unwrap(); - let strapping = target::read_gpio_strap_reg(); + target::init().unwrap(); - if spiconfig == 0 && (strapping & 0x1c) == 0x08 { - spiconfig = 1; /* HSPI flash mode */ - } - - target::spi_attach(spiconfig); - - target::spi_set_default_params().unwrap(); - - let mut buffer: [u8; 0x5000] = [0; 0x5000]; + let mut buffer: [u8; MSG_BUFFER_SIZE] = [0; MSG_BUFFER_SIZE]; loop { let data = stub.read_command(&mut buffer).unwrap(); stub.process_command(data).unwrap(); - - // delay.delay(500u32 * 1000); - // serial.write_str("Hello world\n").unwrap(); - // let apb_freq = unsafe{ ets_get_apb_freq() }; - // let cpu_freq = unsafe{ ets_get_cpu_frequency() }; - // writeln!(serial, "APB: {}, CPU: {}", apb_freq, cpu_freq / 2).unwrap(); - // writeln!(tx, "Characters received: {:?}", rx.count()).unwrap(); } } #[panic_handler] fn panic(_info: &PanicInfo) -> ! { - // dprintln!("\n\n*** {:?}", info); loop {} } } -// ~/esp/esptool.py -p /dev/ttyUSB0 -b 460800 --chip esp32c3 write_flash --flash_mode dio --flash_size detect --flash_freq 40m 0x0 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/hello_world.bin diff --git a/src/miniz_types.rs b/src/miniz_types.rs index 1bcf5c7..235fe99 100644 --- a/src/miniz_types.rs +++ b/src/miniz_types.rs @@ -1,14 +1,11 @@ const TINFL_MAX_HUFF_TABLES: usize = 3; const TINFL_MAX_HUFF_SYMBOLS_0: usize = 288; const TINFL_MAX_HUFF_SYMBOLS_1: usize = 32; -const TINFL_MAX_HUFF_SYMBOLS_2: usize = 19; const TINFL_FAST_LOOKUP_BITS: usize = 10; const TINFL_FAST_LOOKUP_SIZE: usize = 1 << TINFL_FAST_LOOKUP_BITS; pub const TINFL_FLAG_PARSE_ZLIB_HEADER: u32 = 1; pub const TINFL_FLAG_HAS_MORE_INPUT: u32 = 2; -pub const TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: u32 = 4; -pub const TINFL_FLAG_COMPUTE_ADLER32: u32 = 8; #[repr(C)] #[derive(PartialEq, PartialOrd)] @@ -85,4 +82,17 @@ impl Default for tinfl_decompressor { len_codes: [0; TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137] } } +} + +#[allow(unused)] +extern "C" { + pub fn tinfl_decompress( + r: *mut tinfl_decompressor, + in_buf: *const u8, + in_buf_size: *mut usize, + out_buf_start: *mut u8, + out_buf_next: *mut u8, + out_buf_size: *mut usize, + flags: u32 + ) -> TinflStatus; } \ No newline at end of file diff --git a/src/protocol.rs b/src/protocol.rs index 8f12f71..8bc00d3 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -14,447 +14,440 @@ pub enum ErrorIO { } pub trait InputIO { - fn read(&mut self) -> Result; - fn write(&mut self, data: &[u8]) -> Result<(), ErrorIO>; + fn recv(&mut self) -> Result; + fn send(&mut self, data: &[u8]) -> Result<(), ErrorIO>; } +use target::*; +use slip::*; +use core::mem; +use core::slice; +use core::cmp::min; +use crate::commands::*; +use crate::commands::CommandCode::*; +use crate::commands::Error::*; +use md5::{Md5, Digest}; +use crate::miniz_types::*; + +const DATA_CMD_SIZE: usize = mem::size_of::(); +const CMD_BASE_SIZE: usize = mem::size_of::(); + +const FLASH_SECTOR_SIZE: u32 = 4096; +const MAX_WRITE_BLOCK: u32 = 0x4000; +pub struct Stub<'a> { + io: &'a mut (dyn InputIO + 'a), + end_addr: u32, + write_addr: u32, + erase_addr: u32, + remaining: u32, + remaining_compressed: usize, + decompressor: tinfl_decompressor, + last_error: Option, + in_flash_mode: bool, +} -type Buffer = heapless::Vec; - -pub mod stub -{ - use super::*; - use super::target::*; - use super::slip::*; - use core::mem; - use core::slice; - use core::cmp::min; - // use core::mem::MaybeUninit; - use crate::commands::*; - use crate::commands::CommandCode::*; - use crate::commands::Error::*; - use md5::{Md5, Digest}; - use crate::miniz_types::*; - - const DATA_CMD_SIZE: usize = mem::size_of::(); - const CMD_BASE_SIZE: usize = mem::size_of::(); - - const FLASH_SECTOR_SIZE: u32 = 4096; - const MAX_WRITE_BLOCK: u32 = 0x4000; - pub struct Stub<'a> { - io: &'a mut (dyn InputIO + 'a), - end_addr: u32, - write_addr: u32, - erase_addr: u32, - remaining: u32, - remaining_compressed: usize, - decompressor: tinfl_decompressor, - last_error: Option, - in_flash_mode: bool, - total_decompressed_size: usize, - } - - impl From for Error { - fn from(_: ErrorIO) -> Self { - Error::FailedSpiOp - } - } - - // todo: get rid of SIZE - fn slice_to_struct(slice: &[u8]) -> Result - { - if SIZE != slice.len() { - return Err(BadDataLen); - } - let array: &[u8; SIZE] = &slice[0..SIZE].try_into().unwrap(); - unsafe { Ok(mem::transmute_copy::<[u8; SIZE], T>(array)) } +impl From for Error { + fn from(_: ErrorIO) -> Self { + Error::FailedSpiOp } +} - macro_rules! transmute { - ($slice:expr, $type:ty) => { - slice_to_struct::<$type, { mem::size_of::<$type>() }>($slice) - }; +// todo: get rid of SIZE +fn slice_to_struct(slice: &[u8]) -> Result +{ + if SIZE != slice.len() { + return Err(BadDataLen); } + let array: &[u8; SIZE] = &slice[0..SIZE].try_into().unwrap(); + unsafe { Ok(mem::transmute_copy::<[u8; SIZE], T>(array)) } +} - pub unsafe fn to_slice_u8(p: &T) -> &[u8] { - slice::from_raw_parts( (p as *const T) as *const u8, mem::size_of::(), ) - } +macro_rules! transmute { + ($slice:expr, $type:ty) => { + slice_to_struct::<$type, { mem::size_of::<$type>() }>($slice) + }; +} - fn u32_from_slice(slice: &[u8], index: usize) -> u32 { - u32::from_le_bytes(slice[index..index+4].try_into().unwrap()) - } +pub unsafe fn to_slice_u8(p: &T) -> &[u8] { + slice::from_raw_parts( (p as *const T) as *const u8, mem::size_of::(), ) +} - fn calculate_md5(mut address: u32, mut size: u32) -> Result<[u8; 16], Error> { - let mut buffer: [u8; FLASH_SECTOR_SIZE as usize] = [0; FLASH_SECTOR_SIZE as usize]; - let mut hasher = Md5::new(); +fn u32_from_slice(slice: &[u8], index: usize) -> u32 { + u32::from_le_bytes(slice[index..index+4].try_into().unwrap()) +} - while size > 0 { - let to_read = min(size, FLASH_SECTOR_SIZE); - target::spi_flash_read(address, &mut buffer)?; - hasher.update(&buffer[0..to_read as usize]); - size -= to_read; - address += to_read; - } +fn calculate_md5(mut address: u32, mut size: u32) -> Result<[u8; 16], Error> { + let mut buffer: [u8; FLASH_SECTOR_SIZE as usize] = [0; FLASH_SECTOR_SIZE as usize]; + let mut hasher = Md5::new(); - let result: [u8; 16] = hasher.finalize().into(); - Ok(result) + while size > 0 { + let to_read = min(size, FLASH_SECTOR_SIZE); + target::spi_flash_read(address, &mut buffer)?; + hasher.update(&buffer[0..to_read as usize]); + size -= to_read; + address += to_read; } - impl<'a> Stub<'a> { + let result: [u8; 16] = hasher.finalize().into(); + Ok(result) +} - pub fn new(input_io: &'a mut dyn InputIO) -> Self { - Stub { - io: input_io, - write_addr: 0, - end_addr: 0, - erase_addr: 0, - remaining: 0, - remaining_compressed: 0, - decompressor: Default::default(), - last_error: None, - in_flash_mode: false, - total_decompressed_size: 0, - } +impl<'a> Stub<'a> { + + pub fn new(input_io: &'a mut dyn InputIO) -> Self { + Stub { + io: input_io, + write_addr: 0, + end_addr: 0, + erase_addr: 0, + remaining: 0, + remaining_compressed: 0, + decompressor: Default::default(), + last_error: None, + in_flash_mode: false, } + } - fn send_response(&mut self, resp: &Response) -> Result<(), Error> { - let resp_slice = unsafe{ to_slice_u8(resp) }; - write_delimiter(self.io)?; - write_raw(self.io, &resp_slice[..RESPONSE_SIZE])?; - write_raw(self.io, resp.data)?; - write_delimiter(self.io)?; - Ok(()) - } + fn send_response(&mut self, resp: &Response) -> Result<(), Error> { + let resp_slice = unsafe{ to_slice_u8(resp) }; + write_delimiter(self.io)?; + write_raw(self.io, &resp_slice[..RESPONSE_SIZE])?; + write_raw(self.io, resp.data)?; + write_delimiter(self.io)?; + Ok(()) + } - fn send_md5_response(&mut self, resp: &Response, md5: &[u8]) -> Result<(), Error> { - let resp_slice = unsafe{ to_slice_u8(resp) }; - write_delimiter(self.io)?; - write_raw(self.io, &resp_slice[..RESPONSE_SIZE-2])?; - write_raw(self.io, md5)?; - write_raw(self.io, &resp_slice[RESPONSE_SIZE-2..RESPONSE_SIZE])?; - write_delimiter(self.io)?; - Ok(()) - } + fn send_md5_response(&mut self, resp: &Response, md5: &[u8]) -> Result<(), Error> { + let resp_slice = unsafe{ to_slice_u8(resp) }; + write_delimiter(self.io)?; + write_raw(self.io, &resp_slice[..RESPONSE_SIZE-2])?; + write_raw(self.io, md5)?; + write_raw(self.io, &resp_slice[RESPONSE_SIZE-2..RESPONSE_SIZE])?; + write_delimiter(self.io)?; + Ok(()) + } - fn process_begin(&mut self, cmd: &BeginCommand) -> Result<(), Error> { + pub fn send_greeting(&mut self) -> Result<(), Error> { + let greeting = [0x4f, 0x48, 0x41, 0x49]; // OHAI + write_packet(self.io, &greeting)?; + Ok(()) + } - self.write_addr = cmd.offset; - self.erase_addr = cmd.offset; - self.end_addr = cmd.offset + cmd.total_size; - self.remaining_compressed = (cmd.packt_count * cmd.packet_size) as usize; - self.remaining = cmd.total_size; - self.decompressor.state = 0; - - match cmd.base.code { - FlashBegin | FlashDeflBegin => { - if cmd.packet_size > MAX_WRITE_BLOCK { - return Err(BadBlocksize); - } - // Todo: check for 16MB flash only - self.in_flash_mode = true; - unlock_flash()?; + fn process_begin(&mut self, cmd: &BeginCommand) -> Result<(), Error> { + + self.write_addr = cmd.offset; + self.erase_addr = cmd.offset; + self.end_addr = cmd.offset + cmd.total_size; + self.remaining_compressed = (cmd.packt_count * cmd.packet_size) as usize; + self.remaining = cmd.total_size; + self.decompressor.state = 0; + + match cmd.base.code { + FlashBegin | FlashDeflBegin => { + if cmd.packet_size > MAX_WRITE_BLOCK { + return Err(BadBlocksize); } - _ => () // Do nothing for MemBegin + // Todo: check for 16MB flash only + self.in_flash_mode = true; + unlock_flash()?; } - - Ok(()) + _ => () // Do nothing for MemBegin } - fn process_end(&mut self, cmd: &EndCommand, response: &Response) -> Result<(), Error> { + Ok(()) + } - if cmd.base.code == MemEnd { + fn process_end(&mut self, cmd: &EndCommand, response: &Response) -> Result<(), Error> { - let addr = self.erase_addr as *const u32; - let length = self.end_addr - self.erase_addr; - let slice = unsafe { slice::from_raw_parts(addr, length as usize) }; - let mut memory: [u32; 32] = [0; 32]; - memory.copy_from_slice(&slice); + if cmd.base.code == MemEnd { - return match self.remaining { - 0 => Ok(()), - _ => Err(NotEnoughData) - } - } else if !self.in_flash_mode { - return Err(NotInFlashMode); - } else if self.remaining > 0 { - return Err(NotEnoughData); - } - - self.in_flash_mode = false; + let addr = self.erase_addr as *const u32; + let length = self.end_addr - self.erase_addr; + let slice = unsafe { slice::from_raw_parts(addr, length as usize) }; + let mut memory: [u32; 32] = [0; 32]; + memory.copy_from_slice(&slice); - if cmd.run_user_code == 1 { - self.send_response(&response)?; - delay_us(10000); - soft_reset(); + return match self.remaining { + 0 => Ok(()), + _ => Err(NotEnoughData) } - - Ok(()) + } else if !self.in_flash_mode { + return Err(NotInFlashMode); + } else if self.remaining > 0 { + return Err(NotEnoughData); } + + self.in_flash_mode = false; - fn write_ram(&mut self, data: &[u8]) -> Result<(), Error> { - let data_len = data.len() as u32; - - if self.write_addr == 0 && data_len > 0 { - return Err(NotInFlashMode); - } else if data_len > self.remaining { - return Err(TooMuchData); - } else if data_len % 4 != 0 { - return Err(BadDataLen); - } - - let (_, data_u32, _) = unsafe{ data.align_to::() }; - - for word in data_u32 { - let memory = self.write_addr as *mut u32; - unsafe{ *memory = *word }; - self.write_addr += 4; - } - - Ok(()) + if cmd.run_user_code == 1 { + self.send_response(&response)?; + delay_us(10000); + soft_reset(); } - fn flash_data(&mut self, data: &[u8]) -> Result<(), Error> { - - let mut address = self.write_addr; - let mut remaining = data.len() as u32; - let mut written = 0; - - // Erase flash - while self.erase_addr < self.write_addr + remaining { - if self.end_addr >= self.erase_addr + FLASH_BLOCK_SIZE && - self.erase_addr % FLASH_BLOCK_SIZE == 0 { - flash_erase_block(self.erase_addr); - self.erase_addr += FLASH_BLOCK_SIZE; - } else { - flash_erase_sector(self.erase_addr); - self.erase_addr += FLASH_SECTOR_SIZE; - } - } - - // Write flash - while remaining > 0 { - let to_write = min(FLASH_SECTOR_SIZE, remaining); - let data_ptr = data[written..].as_ptr(); - spiflash_write(address, data_ptr, to_write)?; - remaining -= to_write; - written += to_write as usize; - address += to_write; - } + Ok(()) + } - self.write_addr += written as u32; - self.remaining -= min(self.remaining, written as u32); + fn write_ram(&mut self, data: &[u8]) -> Result<(), Error> { + let data_len = data.len() as u32; - Ok(()) + if self.write_addr == 0 && data_len > 0 { + return Err(NotInFlashMode); + } else if data_len > self.remaining { + return Err(TooMuchData); + } else if data_len % 4 != 0 { + return Err(BadDataLen); + } + + let (_, data_u32, _) = unsafe{ data.align_to::() }; + + for word in data_u32 { + let memory = self.write_addr as *mut u32; + unsafe{ *memory = *word }; + self.write_addr += 4; } + + Ok(()) + } - fn flash_defl_data(&mut self, data: &[u8]) -> Result<(), Error> { - use crate::miniz_types::TinflStatus::*; - - const OUT_BUFFER_SIZE: usize = 0x8000; //32768; - static mut DECOMPRESS_BUF: [u8; OUT_BUFFER_SIZE] = [0; OUT_BUFFER_SIZE]; - static mut DECOMPRESS_INDEX: usize = 0; - - let mut out_index = unsafe{ DECOMPRESS_INDEX }; - let out_buf = unsafe{ &mut DECOMPRESS_BUF }; - let mut in_index = 0; - let mut length = data.len(); - let mut status = NeedsMoreInput; - let mut flags = TINFL_FLAG_PARSE_ZLIB_HEADER; - - while length > 0 && self.remaining > 0 && status != Done { - let mut in_bytes = length; - let mut out_bytes = out_buf.len() - out_index; - let next_out: *mut u8 = out_buf[out_index..].as_mut_ptr(); - - if self.remaining_compressed > length { - flags |= TINFL_FLAG_HAS_MORE_INPUT; - } - - status = target::decompress( - &mut self.decompressor, - data[in_index..].as_ptr(), - &mut in_bytes, - out_buf.as_mut_ptr(), - next_out, - &mut out_bytes, - flags); - - self.remaining_compressed -= in_bytes; - length -= in_bytes; - in_index += in_bytes; - out_index += out_bytes; - - if status == Done || out_index == OUT_BUFFER_SIZE { - self.flash_data(&out_buf[..out_index])?; - self.total_decompressed_size += out_index; - out_index = 0; - } + fn flash_data(&mut self, data: &[u8]) -> Result<(), Error> { + + let mut address = self.write_addr; + let mut remaining = data.len() as u32; + let mut written = 0; + + // Erase flash + while self.erase_addr < self.write_addr + remaining { + if self.end_addr >= self.erase_addr + FLASH_BLOCK_SIZE && + self.erase_addr % FLASH_BLOCK_SIZE == 0 { + flash_erase_block(self.erase_addr); + self.erase_addr += FLASH_BLOCK_SIZE; + } else { + flash_erase_sector(self.erase_addr); + self.erase_addr += FLASH_SECTOR_SIZE; } + } + + // Write flash + while remaining > 0 { + let to_write = min(FLASH_SECTOR_SIZE, remaining); + let data_ptr = data[written..].as_ptr(); + spiflash_write(address, data_ptr, to_write)?; + remaining -= to_write; + written += to_write as usize; + address += to_write; + } - unsafe{ DECOMPRESS_INDEX = out_index }; + self.write_addr += written as u32; + self.remaining -= min(self.remaining, written as u32); - // error won't get sent back to esptool.py until next block is sent - if status < Done { - self.last_error = Some(InflateError); - } else if status == Done && self.remaining > 0 { - self.last_error = Some(NotEnoughData); - } else if status != Done && self.remaining == 0 { - self.last_error = Some(TooMuchData); - } - - Ok(()) - } + Ok(()) + } - fn process_data(&mut self, cmd: &DataCommand, data: &[u8]) -> Result<(), Error> { + fn flash_defl_data(&mut self, data: &[u8]) -> Result<(), Error> { + use crate::miniz_types::TinflStatus::*; + + const OUT_BUFFER_SIZE: usize = 0x8000; //32768; + static mut DECOMPRESS_BUF: [u8; OUT_BUFFER_SIZE] = [0; OUT_BUFFER_SIZE]; + static mut DECOMPRESS_INDEX: usize = 0; + + let mut out_index = unsafe{ DECOMPRESS_INDEX }; + let out_buf = unsafe{ &mut DECOMPRESS_BUF }; + let mut in_index = 0; + let mut length = data.len(); + let mut status = NeedsMoreInput; + let mut flags = TINFL_FLAG_PARSE_ZLIB_HEADER; + + while length > 0 && self.remaining > 0 && status != Done { + let mut in_bytes = length; + let mut out_bytes = out_buf.len() - out_index; + let next_out: *mut u8 = out_buf[out_index..].as_mut_ptr(); - let checksum: u8 = data.iter().fold(0xEF, |acc, x| acc ^ x); - - if !self.in_flash_mode { - return Err(NotInFlashMode); - } else if cmd.size != data.len() as u32 { - return Err(BadDataLen); - } else if cmd.base.checksum != checksum as u32 { - return Err(BadDataChecksum); + if self.remaining_compressed > length { + flags |= TINFL_FLAG_HAS_MORE_INPUT; } - match cmd.base.code { - FlashDeflData => self.flash_defl_data(data)?, - FlashData => self.flash_data(data)?, - MemData => self.write_ram(data)?, - _ => () + status = target::decompress( + &mut self.decompressor, + data[in_index..].as_ptr(), + &mut in_bytes, + out_buf.as_mut_ptr(), + next_out, + &mut out_bytes, + flags); + + self.remaining_compressed -= in_bytes; + length -= in_bytes; + in_index += in_bytes; + out_index += out_bytes; + + if status == Done || out_index == OUT_BUFFER_SIZE { + self.flash_data(&out_buf[..out_index])?; + out_index = 0; } + } - Ok(()) + unsafe{ DECOMPRESS_INDEX = out_index }; + + // error won't get sent back until next block is sent + if status < Done { + self.last_error = Some(InflateError); + } else if status == Done && self.remaining > 0 { + self.last_error = Some(NotEnoughData); + } else if status != Done && self.remaining == 0 { + self.last_error = Some(TooMuchData); } + + Ok(()) + } - fn process_read_flash(&mut self, params: &ReadFlashParams) -> Result<(), Error> { - // Can return FailedSpiOp (?) - const BUF_SIZE: usize = FLASH_SECTOR_SIZE as usize; - let mut buffer: [u8; BUF_SIZE] = [0; BUF_SIZE]; - let mut address = params.address; - let mut remaining = params.total_size; - let to_ack = params.total_size / params.packet_size; - let mut acked = 0; - let mut ack_buf: [u8; 4] = [0; 4]; - let mut hasher = Md5::new(); + fn process_data(&mut self, cmd: &DataCommand, data: &[u8]) -> Result<(), Error> { - while acked < to_ack { - while remaining > 0 && (to_ack - acked) < params.max_inflight { - let len = min(params.packet_size, remaining); - spi_flash_read(address, &mut buffer[..len as usize])?; - write_packet(self.io, &buffer[..len as usize])?; - hasher.update(&buffer[0..len as usize]); - remaining -= len; - address += len; - } - let resp = read_packet(self.io, &mut ack_buf)?; - acked = u32_from_slice(resp, 0); - } + let checksum: u8 = data.iter().fold(0xEF, |acc, x| acc ^ x); - let md5: [u8; 16] = hasher.finalize().into(); - write_packet(self.io, &md5)?; - Ok(()) + if !self.in_flash_mode { + return Err(NotInFlashMode); + } else if cmd.size != data.len() as u32 { + return Err(BadDataLen); + } else if cmd.base.checksum != checksum as u32 { + return Err(BadDataChecksum); } - #[allow(unreachable_patterns)] - fn process_cmd(&mut self, payload: &[u8], - code: CommandCode, - response: &mut Response) -> Result { - - let mut response_sent = false; - - match code { - Sync => (), - ReadReg => { - let address = u32_from_slice(payload, CMD_BASE_SIZE); - response.value(read_register(address)); - } - WriteReg => { - let reg = transmute!(payload, WriteRegCommand)?; - write_register(reg.address, reg.value); - } - FlashBegin | MemBegin | FlashDeflBegin => { - let cmd = transmute!(payload, BeginCommand)?; - self.process_begin(&cmd)? - } - FlashData | FlashDeflData | MemData => { - let cmd = transmute!(&payload[..DATA_CMD_SIZE], DataCommand)?; - let data = &payload[DATA_CMD_SIZE..]; - self.process_data(&cmd, data)? - } - FlashEnd | MemEnd | FlashDeflEnd => { - let cmd = transmute!(payload, EndCommand)?; - self.process_end(&cmd, &response)?; - } - SpiFlashMd5 => { - let cmd = transmute!(payload, SpiFlashMd5Command)?; - let md5 = calculate_md5(cmd.address, cmd.size)?; - self.send_md5_response(&response, &md5)?; - response_sent = true; - } - SpiSetParams => { - let cmd = transmute!(payload, SpiSetParamsCommand)?; - spi_set_params(&cmd.params)? - } - SpiAttach => { - let param = u32_from_slice(payload, CMD_BASE_SIZE); - spi_attach(param); - } - ChangeBaudrate => { - let baud = transmute!(payload, ChangeBaudrateCommand)?; - self.send_response(&response)?; - delay_us(10000); - change_baudrate(baud.old, baud.new); - response_sent = true; - } - EraseFlash => { - erase_flash()? - } - EraseRegion => { - let reg = transmute!(payload, EraseRegionCommand)?; - erase_region(reg.address, reg.size)?; - } - ReadFlash => { - self.send_response(&response)?; - let cmd = transmute!(payload, ReadFlashCommand)?; - self.process_read_flash(&cmd.params)?; - response_sent = true; - } - RunUserCode => { - todo!(); - } - _ => { - return Err(InvalidCommand); - } - }; + match cmd.base.code { + FlashDeflData => self.flash_defl_data(data)?, + FlashData => self.flash_data(data)?, + MemData => self.write_ram(data)?, + _ => () + } - Ok(response_sent) + Ok(()) + } + + fn process_read_flash(&mut self, params: &ReadFlashParams) -> Result<(), Error> { + // Can return FailedSpiOp (?) + const BUF_SIZE: usize = FLASH_SECTOR_SIZE as usize; + let mut buffer: [u8; BUF_SIZE] = [0; BUF_SIZE]; + let mut address = params.address; + let mut remaining = params.total_size; + let to_ack = params.total_size / params.packet_size; + let mut acked = 0; + let mut ack_buf: [u8; 4] = [0; 4]; + let mut hasher = Md5::new(); + + while acked < to_ack { + while remaining > 0 && (to_ack - acked) < params.max_inflight { + let len = min(params.packet_size, remaining); + spi_flash_read(address, &mut buffer[..len as usize])?; + write_packet(self.io, &buffer[..len as usize])?; + hasher.update(&buffer[0..len as usize]); + remaining -= len; + address += len; + } + let resp = read_packet(self.io, &mut ack_buf)?; + acked = u32_from_slice(resp, 0); } - pub fn process_command(&mut self, payload: &[u8]) -> Result<(), Error> { - - let command = transmute!(&payload[..CMD_BASE_SIZE], CommandBase)?; - let mut response = Response::new(command.code); + let md5: [u8; 16] = hasher.finalize().into(); + write_packet(self.io, &md5)?; + Ok(()) + } - match self.process_cmd(payload, command.code, &mut response) { - Ok(response_sent) => match response_sent { - true => return Ok(()), - false => (), - } - Err(err) => response.error(err) + #[allow(unreachable_patterns)] + fn process_cmd(&mut self, payload: &[u8], + code: CommandCode, + response: &mut Response) -> Result { + + let mut response_sent = false; + + match code { + Sync => (), + ReadReg => { + let address = u32_from_slice(payload, CMD_BASE_SIZE); + response.value(read_register(address)); + } + WriteReg => { + let reg = transmute!(payload, WriteRegCommand)?; + write_register(reg.address, reg.value); + } + FlashBegin | MemBegin | FlashDeflBegin => { + let cmd = transmute!(payload, BeginCommand)?; + self.process_begin(&cmd)? + } + FlashData | FlashDeflData | MemData => { + let cmd = transmute!(&payload[..DATA_CMD_SIZE], DataCommand)?; + let data = &payload[DATA_CMD_SIZE..]; + self.process_data(&cmd, data)? + } + FlashEnd | MemEnd | FlashDeflEnd => { + let cmd = transmute!(payload, EndCommand)?; + self.process_end(&cmd, &response)?; + } + SpiFlashMd5 => { + let cmd = transmute!(payload, SpiFlashMd5Command)?; + let md5 = calculate_md5(cmd.address, cmd.size)?; + self.send_md5_response(&response, &md5)?; + response_sent = true; + } + SpiSetParams => { + let cmd = transmute!(payload, SpiSetParamsCommand)?; + spi_set_params(&cmd.params)? + } + SpiAttach => { + let param = u32_from_slice(payload, CMD_BASE_SIZE); + spi_attach(param); } + ChangeBaudrate => { + let baud = transmute!(payload, ChangeBaudrateCommand)?; + self.send_response(&response)?; + delay_us(10000); + change_baudrate(baud.old, baud.new); + response_sent = true; + } + EraseFlash => { + erase_flash()? + } + EraseRegion => { + let reg = transmute!(payload, EraseRegionCommand)?; + erase_region(reg.address, reg.size)?; + } + ReadFlash => { + self.send_response(&response)?; + let cmd = transmute!(payload, ReadFlashCommand)?; + self.process_read_flash(&cmd.params)?; + response_sent = true; + } + RunUserCode => { + todo!(); + } + _ => { + return Err(InvalidCommand); + } + }; - self.send_response(&response) - } + Ok(response_sent) + } - pub fn read_command<'c, 'd>(&'c mut self, buffer: &'d mut [u8]) -> Result<&'d [u8], Error> { - let data = read_packet(self.io, buffer)?; - Ok(data) + pub fn process_command(&mut self, payload: &[u8]) -> Result<(), Error> { + + let command = transmute!(&payload[..CMD_BASE_SIZE], CommandBase)?; + let mut response = Response::new(command.code); + + match self.process_cmd(payload, command.code, &mut response) { + Ok(response_sent) => match response_sent { + true => return Ok(()), + false => (), + } + Err(err) => response.error(err) } + + self.send_response(&response) + } + + pub fn read_command<'c, 'd>(&'c mut self, buffer: &'d mut [u8]) -> Result<&'d [u8], Error> { + Ok( read_packet(self.io, buffer)? ) } } -// Check command and its size, cast it mod slip { use super::*; use super::ErrorIO::*; @@ -467,13 +460,13 @@ mod slip { pub fn read_packet<'c, 'd>(io: &'c mut dyn InputIO, packet: &'d mut [u8]) -> Result<&'d [u8], ErrorIO> { - while io.read()? != 0xC0 { } + while io.recv()? != 0xC0 { } // Replase: 0xDB 0xDC -> 0xC0 and 0xDB 0xDD -> 0xDB let mut i = 0; loop { - match io.read()? { - 0xDB => match io.read()? { + match io.recv()? { + 0xDB => match io.recv()? { 0xDC => packet[i] = 0xC0, 0xDD => packet[i] = 0xDB, _ => return Err(InvalidResponse), @@ -490,9 +483,9 @@ mod slip { pub fn write_raw(io: &mut dyn InputIO, data: &[u8]) -> Result<(), ErrorIO> { for byte in data { match byte { - 0xC0 => io.write(&[0xDB, 0xDC])?, - 0xDB => io.write(&[0xDB, 0xDD])?, - other => io.write(&[*other])?, + 0xC0 => io.send(&[0xDB, 0xDC])?, + 0xDB => io.send(&[0xDB, 0xDD])?, + other => io.send(&[*other])?, } } Ok(()) @@ -505,7 +498,7 @@ mod slip { } pub fn write_delimiter(io: &mut dyn InputIO) -> Result<(), ErrorIO> { - io.write(&[0xC0]) + io.send(&[0xC0]) } } @@ -513,7 +506,6 @@ mod slip { mod tests { use super::*; use super::ErrorIO::*; - use super::stub::*; // use super::stub::Error::*; use super::slip::{read_packet, write_raw}; use assert2::{assert, let_assert}; @@ -774,11 +766,9 @@ mod tests { let mut io = MockIO::new(); io.fill(&[1, 2, 3]); - assert!(io.read() == Ok(1)); - assert!(io.read() == Ok(2)); - assert!(io.read() == Ok(3)); - assert!(io.read() == Err(Incomplete)); + assert!(io.recv() == Ok(1)); + assert!(io.recv() == Ok(2)); + assert!(io.recv() == Ok(3)); + assert!(io.recv() == Err(Incomplete)); } } - -// wait_for_command -> slip::read_packet -> || -> io::read \ No newline at end of file diff --git a/src/targets.rs b/src/targets.rs index 4a51c6d..f9b3bd1 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -1,4 +1,3 @@ - #[cfg(test)] use mockall::automock; @@ -14,35 +13,22 @@ extern "C" { /// address (4 byte alignment), data, length fn esp_rom_spiflash_read(src_addr: u32, data: *const u8, len: u32) -> i32; fn esp_rom_spiflash_unlock() -> i32; - // fn esp_rom_spiflash_lock(); // can't find in idf defs? fn esp_rom_spiflash_attach(config: u32, legacy: bool); fn esp_rom_spiflash_config_param(device_id: u32, chip_size: u32, block_size: u32, sector_size: u32, page_size: u32, status_mask: u32) -> u32; - fn uart_tx_one_char(byte: u8); fn uart_div_modify(uart_number: u32, baud_div: u32); - fn ets_efuse_get_spiconfig() -> u32; fn software_reset(); fn ets_delay_us(timeout: u32); - pub fn tinfl_decompress( - r: *mut tinfl_decompressor, - in_buf: *const u8, - in_buf_size: *mut usize, - out_buf_start: *mut u8, - out_buf_next: *mut u8, - out_buf_size: *mut usize, - flags: u32 - ) -> TinflStatus; } #[cfg_attr(test, automock)] pub mod esp32c3 { + use super::*; use crate::commands::*; use crate::commands::Error::*; - // use crate::commands::CommandCode::*; - use super::*; const SPI_BASE_REG: u32 = 0x60002000; const SPI_CMD_REG: u32 = SPI_BASE_REG + 0x00; @@ -109,19 +95,6 @@ pub mod esp32c3 { if result == 0 { Ok(()) } else { Err(FailedSpiOp) } } - - pub fn spi_set_default_params() -> Result<(), Error> { - let params = SpiParams { - id: 0, - total_size: FLASH_MAX_SIZE, - block_size: FLASH_BLOCK_SIZE, - sector_size: FLASH_SECTOR_SIZE, - page_size: FLASH_PAGE_SIZE, - status_mask: FLASH_STATUS_MASK, - }; - - spi_set_params(¶ms) - } pub fn spi_attach(param: u32) { unsafe{ esp_rom_spiflash_attach(param, false) }; @@ -188,11 +161,9 @@ pub mod esp32c3 { pub fn erase_region(address: u32, size: u32) -> Result<(), Error> { if address % FLASH_SECTOR_SIZE != 0 { return Err(Err0x32); - } - if size % FLASH_SECTOR_SIZE != 0 { + } else if size % FLASH_SECTOR_SIZE != 0 { return Err(Err0x33); - } - if unsafe{ esp_rom_spiflash_unlock() } != 0 { + } else if unsafe{ esp_rom_spiflash_unlock() } != 0 { return Err(Err0x34); } @@ -226,8 +197,27 @@ pub mod esp32c3 { } } - pub fn read_gpio_strap_reg() -> u32 { - read_register(GPIO_STRAP_REG) + pub fn init() -> Result<(), Error> { + let mut spiconfig = unsafe{ ets_efuse_get_spiconfig() }; + + let strapping = read_register(GPIO_STRAP_REG); + + if spiconfig == 0 && (strapping & 0x1c) == 0x08 { + spiconfig = 1; /* HSPI flash mode */ + } + + spi_attach(spiconfig); + + let deafault_params = SpiParams { + id: 0, + total_size: FLASH_MAX_SIZE, + block_size: FLASH_BLOCK_SIZE, + sector_size: FLASH_SECTOR_SIZE, + page_size: FLASH_PAGE_SIZE, + status_mask: FLASH_STATUS_MASK, + }; + + spi_set_params(&deafault_params) } pub fn soft_reset() { From 618e63281917b86179cef55614dd8b77d28c9e4b Mon Sep 17 00:00:00 2001 From: Martin Valik Date: Fri, 3 Jun 2022 15:40:31 +0200 Subject: [PATCH 09/10] Passes esptool tests for ESP32C3 --- .cargo/config.toml | 8 -- Cargo.lock | 40 ++++-- Cargo.toml | 4 + README.md | 26 +++- build.rs | 16 +-- esptool.patch | 54 ++++++++ flash | 320 --------------------------------------------- ld/rom.x | 1 + ld/stub.x | 60 +-------- openocd.cfg | 6 - src/commands.rs | 6 +- src/dprint.rs | 162 +++++++++++++++++++++++ src/main.rs | 67 +++++----- src/protocol.rs | 288 +++++++++++++++++++--------------------- src/serial_io.rs | 58 ++++++++ src/targets.rs | 47 +++++-- 16 files changed, 557 insertions(+), 606 deletions(-) create mode 100644 esptool.patch delete mode 100755 flash delete mode 100644 openocd.cfg create mode 100644 src/dprint.rs create mode 100644 src/serial_io.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index b9f55bb..0adcf86 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,14 +1,6 @@ -# [target.riscv32imc-unknown-none-elf] -# runner = "espflash --format direct-boot --monitor" -# rustflags = [ -# "-C", "link-arg=-Tlinkall.x" -# ] - [build] target = "riscv32imc-unknown-none-elf" [target.'cfg(any(target_arch="riscv32"))'] rustflags = [ - # "-C", "link-arg=-Tmy_link.x", - # "-C", "link-arg=-Tld/rom.x", "-C", "link-args=-Map=target/stub.map", ] diff --git a/Cargo.lock b/Cargo.lock index a8bf179..ba092d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,12 +36,11 @@ dependencies = [ [[package]] name = "atomic-polyfill" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6adc1648f03fbc1bc1b5cf0f2fdfb5edbc96215b711edcfe6ce2641ef9b347" +checksum = "e14bf7b4f565e5e717d7a7a65b2a05c0b8c96e4db636d6f780f03b15108cdd1b" dependencies = [ "critical-section", - "riscv-target", ] [[package]] @@ -129,14 +128,14 @@ dependencies = [ [[package]] name = "critical-section" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70" +checksum = "95da181745b56d4bd339530ec393508910c909c784e8962d15d722bacf0bcbcd" dependencies = [ "bare-metal 1.0.0", "cfg-if 1.0.0", "cortex-m", - "riscv", + "riscv 0.7.0", ] [[package]] @@ -232,7 +231,7 @@ dependencies = [ "esp32c3", "nb 1.0.0", "paste", - "riscv", + "riscv 0.7.0", "riscv-atomic-emulation-trap", "void", ] @@ -254,7 +253,7 @@ version = "0.3.0" source = "git+https://github.com/esp-rs/esp-pacs.git?branch=with_source#91102d1de0bba040e2523f423d73f8d24332c4fa" dependencies = [ "bare-metal 1.0.0", - "riscv", + "riscv 0.7.0", "riscv-rt", "vcell", ] @@ -268,7 +267,7 @@ dependencies = [ "esp-hal-common", "nb 1.0.0", "r0", - "riscv", + "riscv 0.7.0", "riscv-atomic-emulation-trap", "riscv-rt", "void", @@ -316,12 +315,13 @@ dependencies = [ [[package]] name = "heapless" -version = "0.7.10" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d076121838e03f862871315477528debffdb7462fb229216ecef91b1a3eb31eb" +checksum = "065681e99f9ef7e0e813702a0326aedbcbbde7db5e55f097aedd1bf50b9dca43" dependencies = [ "atomic-polyfill", "hash32", + "rustc_version 0.4.0", "spin", "stable_deref_trait", ] @@ -615,13 +615,24 @@ dependencies = [ "riscv-target", ] +[[package]] +name = "riscv" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2856a701069e2d262b264750d382407d272d5527f7a51d3777d1805b4e2d3c" +dependencies = [ + "bare-metal 1.0.0", + "bit_field", + "embedded-hal", +] + [[package]] name = "riscv-atomic-emulation-trap" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d8ed35d10950305d9abbc3fa49540b948e03b653c85012b4cd9f7d249029f0" dependencies = [ - "riscv", + "riscv 0.7.0", "riscv-rt", "riscv-target", ] @@ -633,7 +644,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab9f28dc850356196a36078c51c9dc7b46014a29a8fde35d5f81c99e489d0e00" dependencies = [ "r0", - "riscv", + "riscv 0.7.0", "riscv-rt-macros", "riscv-target", ] @@ -731,6 +742,7 @@ name = "stub" version = "0.1.0" dependencies = [ "assert2", + "bare-metal 1.0.0", "embedded-hal", "esp-hal-common", "esp32c3-hal", @@ -740,6 +752,8 @@ dependencies = [ "mockall", "mockall_double", "nb 1.0.0", + "r0", + "riscv 0.8.0", "riscv-rt", ] diff --git a/Cargo.toml b/Cargo.toml index 71d733a..1d99574 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,10 @@ esp-hal-common = { path = "../esp-hal/esp-hal-common" } riscv-rt = "0.8.1" nb = "1.0.0" embedded-hal = "0.2.7" +r0 = "1.0.0" +bare-metal = "1.0.0" +riscv = "0.8.0" +heapless = "0.7.14" [dev-dependencies] assert2 = "0.3.6" diff --git a/README.md b/README.md index 2589cdf..0c4c7d8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,31 @@ # esp-flasher-stub +Rust implementation of flasher stub located in esptool. +Currently only supports ESP32C3 through UART. + ## Build -cargo build -Z build-std=core,alloc +cargo build ## Test cargo test --target=x86_64-unknown-linux-gnu + +## Run esptool test +Since esptool uses precompiled stub binaries located in `stub_flasher.py`, +binary for ESP32C3 has to be replaced the one otained from `esp-flasher-stub`. + +In order to run `test_espttol.py` follow steps below: +* Build `esp-flasher-stub` with `cargo build --release` +* Clone esptool to the same directory where `esp-flasher-stub` resides. +``` +git clone https://github.com/espressif/esptool +``` +* Navigate to `esptool/test` and apply patch located in `esp-flasher-stub` directory. +``` +cd esptool/test +git am ../../esp-flasher-stub/esptool.patch +``` +* Regenerate `stub_flasher.py` by running patched Makefile and run the tests +``` +make -C ../flasher_stub/ && python test_esptool.py /dev/ttyUSB0 esp32c3 115200 +``` +This last step requires toolchain for ESP8266 to be installed and IFD exported. \ No newline at end of file diff --git a/build.rs b/build.rs index c0c7700..198f91e 100644 --- a/build.rs +++ b/build.rs @@ -4,15 +4,11 @@ fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); println!("cargo:rustc-link-search)={}", out_dir.display()); - - // #[cfg(target_arch = "riscv32")] - // { - fs::copy("ld/stub.x", out_dir.join("stub.x")).unwrap(); - println!("cargo:rerun-if-changed=ld/stub.x"); - println!("cargo:rustc-link-arg=-Tld/stub.x"); + fs::copy("ld/stub.x", out_dir.join("stub.x")).unwrap(); + println!("cargo:rerun-if-changed=ld/stub.x"); + println!("cargo:rustc-link-arg=-Tld/stub.x"); - fs::copy("ld/rom.x", out_dir.join("rom.x")).unwrap(); - println!("cargo:rerun-if-changed=ld/ld/rom.x"); - println!("cargo:rustc-link-arg=-Tld/rom.x"); - // } + fs::copy("ld/rom.x", out_dir.join("rom.x")).unwrap(); + println!("cargo:rerun-if-changed=ld/ld/rom.x"); + println!("cargo:rustc-link-arg=-Tld/rom.x"); } diff --git a/esptool.patch b/esptool.patch new file mode 100644 index 0000000..4adda98 --- /dev/null +++ b/esptool.patch @@ -0,0 +1,54 @@ +From 5ff6e377e8ddd123b6d6112d1547908bd2a47e01 Mon Sep 17 00:00:00 2001 +From: Martin Valik +Date: Thu, 23 Jun 2022 10:18:54 +0200 +Subject: [PATCH] new stub + +--- + flasher_stub/Makefile | 3 ++- + test/test_esptool.py | 4 ++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/flasher_stub/Makefile b/flasher_stub/Makefile +index af51a39..1ce39b5 100644 +--- a/flasher_stub/Makefile ++++ b/flasher_stub/Makefile +@@ -105,7 +105,7 @@ $(STUB_ELF_32S3): $(SRCS) $(BUILD_DIR) ld/stub_32s3.ld + + $(STUB_ELF_32C3): $(SRCS) $(BUILD_DIR) ld/stub_32c3.ld + @echo " CC(32C3) $^ -> $@" +- $(Q) $(CROSS_32C3)gcc $(CFLAGS_ESPRISCV32) -DESP32C3=1 -Tstub_32c3.ld -Wl,-Map=$(@:.elf=.map) -o $@ $(filter %.c, $^) $(LDLIBS) ++ cp ../../esp-flasher-stub/target/riscv32imc-unknown-none-elf/release/stub build/stub_flasher_32c3.elf + + $(STUB_ELF_32C6BETA): $(SRCS) $(BUILD_DIR) ld/stub_32c6_beta.ld + @echo " CC(32C6) $^ -> $@" +@@ -126,6 +126,7 @@ $(STUB_ELF_32C2): $(SRCS) $(BUILD_DIR) ld/stub_32c2.ld + $(STUB_PY): $(STUB_ELF_8266) $(STUB_ELF_32) $(STUB_ELF_32S2) $(STUB_ELF_32S3_BETA_2) $(STUB_ELF_32S3) $(STUB_ELF_32C3) $(STUB_ELF_32C6BETA) $(STUB_ELF_32H2_BETA_1) $(STUB_ELF_32H2_BETA_2) $(STUB_ELF_32C2) wrap_stub.py + @echo " WRAP $^ -> $@" + $(Q) $(WRAP_STUB) --out-file $@ $(filter %.elf,$^) ++ cp build/stub_flasher_snippet.py ../esptool/stub_flasher.py + + embed: $(STUB_ELF_8266) $(STUB_ELF_32) $(STUB_ELF_32S2) $(STUB_ELF_32S3_BETA_2) $(STUB_ELF_32S3) $(STUB_ELF_32C3) $(STUB_ELF_32C6BETA) $(STUB_ELF_32H2_BETA_1) $(STUB_ELF_32H2_BETA_2) $(STUB_ELF_32C2) wrap_stub.py + @echo " WRAP $^ -> esptool.py" +diff --git a/test/test_esptool.py b/test/test_esptool.py +index 79d22a6..e8e019e 100755 +--- a/test/test_esptool.py ++++ b/test/test_esptool.py +@@ -38,7 +38,7 @@ NODEMCU_FILE = "nodemcu-master-7-modules-2017-01-19-11-10-03-integer.bin" + TEST_DIR = os.path.abspath(os.path.dirname(__file__)) + os.chdir(os.path.dirname(__file__)) + try: +- ESPTOOL_PY = os.environ["ESPTOOL_PY"] ++ ESPTOOL_PY = os.path.join(TEST_DIR, "..", "esptool.py") + except KeyError: + ESPTOOL_PY = os.path.join(TEST_DIR, "..", "esptool/__init__.py") + ESPSECURE_PY = os.path.join(TEST_DIR, "..", "espsecure/__init__.py") +@@ -1015,4 +1015,4 @@ if __name__ == "__main__": + with io.open("report.xml", "wb") as output: + unittest.main(testRunner=xmlrunner.XMLTestRunner(output=output)) + except ImportError: +- unittest.main(buffer=True) ++ unittest.main() +\ No newline at end of file +-- +2.34.1 + diff --git a/flash b/flash deleted file mode 100755 index 999f67d..0000000 --- a/flash +++ /dev/null @@ -1,320 +0,0 @@ -#!/bin/bash - -# port esp32 is connected to (leave empty to autodetect) -PORT= -#PORT=/dev/ttyUSB1 - -# baud rate for programming -FLASH_BAUDRATE=921600 - -# baud rate for terminal -TERM_BAUDRATE=115200 - -# Flash Mode -FLASH_MODE="dio" - -# Flash Speed -FLASH_SPEED="40m" - -# Use bootloader (needed for using irom, drom and psram) -USE_BOOTLOADER=1 - -# debug or release build -TYPE=debug - -# address of partition table -PARTION_ADDR=0x8000 - -# address of application -APP_ADDR=0x10000 - -#source of the utility to generate the binary partition table -GENPART_SOURCE=https://github.com/espressif/esp-idf/blob/v4.0/components/partition_table/gen_esp32part.py?raw=true - -# source of the bootloader -# customized bootloader which initializes the external psram -BOOTLOADER_SOURCE=https://github.com/arjanmels/esp32_bootloader_init_extram/blob/v1.0/build/bootloader/bootloader.bin?raw=true -# default bootloader from the espressif arduino github -#BOOTLOADER_SOURCE=https://github.com/espressif/arduino-esp32/blob/idf-release/v4.0/tools/sdk/bin/bootloader_dio_40m.bin?raw=true - -# color codes -STAGE="\033[1;36m" -SUCCESS="\033[1;32m" -ERROR="\033[1;31m" -RESET="\033[0m" - - - -showhelp() { -cat << EOF -Usage: flash -hrtbs [-p ] [-e ] - --h, --help Display Help --r, --release Build in release mode --p, --port Set serial port (default: autodetect) --b, --baudrate Set baudrate for flasing (default: $FLASH_BAUDRATE) - , --termbaudrate Set baudrate for monitoring (default: $TERM_BAUDRATE) --t, --terminal Open terminal program after flashing --e, --example Build the specified example --s, --skip Skip actual flashing -EOF -} - -# get command line options -options=$(getopt -l "help,release,port:,terminal,baudrate:,termbaudrate:,example:,skip" -o "hrp:tb:e:s" -a -- "$@") - -if [[ $? -ne 0 ]] -then - echo - showhelp - exit 1 -fi - -eval set -- "$options" - - -while true -do - case $1 in - -h|--help) - showhelp - exit 0 - ;; - -r|--release) - export TYPE=release - ;; - -p|--port) - shift - export PORT=$1 - ;; - -s|--skip) - export SKIPFLASH=1 - ;; - -t|--terminal) - export TERMINAL=1 - ;; - -b|--baudrate) - shift - export FLASH_BAUDRATE=$1 - ;; - --termbaudrate) - shift - export TERM_BAUDRATE=$1 - ;; - -e|--example) - shift - export EXAMPLE=$1 - ;; - --) - shift - break;; - esac -shift -done - -if [[ $# -ne 0 ]] -then - printf "${ERROR}*** Wrong number of arguments${RESET}\n\n" >&2 - showhelp - exit 1 -fi - -if [[ ! -f Cargo.toml ]] -then - printf "${ERROR}*** Must be run in root of project where Cargo.toml file is located${RESET}\n" >&2 - exit 1 -fi - -if [[ -z "$EXAMPLE" ]] -then - FILE=$(awk 'BEGIN {FS="[ \t=\"]+";} /^\s*\[/ {section=$1} tolower(section)=="[package]" && tolower($0) ~ /^\s*name/ {print $2}' Cargo.toml) - BIN_PATH=target/xtensa-esp32-none-elf/$TYPE/$FILE - EXAMPLE_ARG="" -else - BIN_PATH=target/xtensa-esp32-none-elf/$TYPE/examples/$EXAMPLE - EXAMPLE_ARG="--example "$EXAMPLE -fi - -# set the release flag -if [ "$TYPE" = "release" ] -then - RELEASE="--release" -else - RELEASE="" -fi - -CMD="cargo xbuild $RELEASE $EXAMPLE_ARG" - -printf "${STAGE}Building application with $CMD...${RESET}\n\n" - -rm target/current.elf 2> /dev/null -rm target/current.bin 2> /dev/null - - -# get error code of any step of the pipe -set -o pipefail - -# run cargo & get missing features -{ FEATURES=$(script -efqc "$CMD 2>&1" /dev/null | tee /dev/stderr | egrep -o '\-\-features=".*"'; exit ${PIPESTATUS[0]}); } -RES=$? - -# if cargo returned an error because features are missing to rerun -if [[ $RES -ne 0 && -n $FEATURES ]] -then - CMD="cargo xbuild $RELEASE $EXAMPLE_ARG $FEATURES" - printf "\n\n${STAGE}Building application with $CMD...${RESET}\n\n" - eval $CMD - RES=$? -fi - -# if cargo returned an error exit -if [[ $RES -ne 0 ]] -then - exit 1 -fi - -if [[ ! -f $BIN_PATH ]] -then - printf "${ERROR}Error: Output file ($BIN_PATH) not generated!${RESET}\n\n" - exit 1 -fi - -#display section sizes -echo -xtensa-esp32-elf-readelf $BIN_PATH -S|egrep 'BIT|\[Nr\]' |awk 'BEGIN {FS="[ \t\[\]]+"} $9~/A|Flg/ {size=sprintf("%7d", "0x" $7)+0; printf("%-3s %-20s %-8s %-8s %-8s %8s %-3s %-3s\n",$2,$3,$4,$5,$7,((size>0)?size:$7),$9,$12); total+=size; } END { printf("\nTotal: %d bytes\n",total)}' -echo - -# convert to bin -rm $BIN_PATH.bin 2>/dev/null -esptool.py --chip esp32 elf2image --flash_mode=$FLASH_MODE --flash_freq $FLASH_SPEED -o $BIN_PATH.bin $BIN_PATH > /dev/null -if [ $? -ne 0 ] -then - printf "${ERROR}Error: Output file ($BIN_PATH).bin not generated!${RESET}\n\n" - esptool.py --chip esp32 elf2image --flash_mode=$FLASH_MODE --flash_freq $FLASH_SPEED -o $BIN_PATH.bin $BIN_PATH - exit 1 -fi - -esptool.py --chip esp32 image_info $BIN_PATH.bin |egrep -v -i "esptool.py|Image version|Checksum|Validation Hash|Segments" -echo - -if [ $? -ne 0 ] -then - exit 1 -fi - -# create links to output binaries for linking with debugger -ln -sf $(pwd)/$BIN_PATH target/current.elf -ln -sf $(pwd)/$BIN_PATH.bin target/current.bin - -if [[ $SKIPFLASH -ne 1 || $TERMINAL -eq 1 ]] -then - if [[ -z "$PORT" ]] - then - # kill terminal programs using any port - ps -ef|grep "/dev/ttyUSB" |egrep -v "$0|grep" |awk '{print $2}'|xargs -r kill - printf "${STAGE}Detecting port...${RESET} " - PORT=$(esptool.py --no-stub read_mac 2> /dev/null | awk '/^Serial port / {port=$3} END {print port}') - if [[ -z $PORT ]] - then - printf "${ERROR}Error: cannot detect port!${RESET}\n\n" - exit 1 - fi - printf "$PORT\n\n" - else - # kill terminal programs using the same port - ps -ef|grep $PORT|egrep -v "$0|grep" |awk '{print $2}'|xargs -r kill - fi -fi - -if [[ $SKIPFLASH -ne 1 ]] -then - flash() { - echo -e "${STAGE}Flashing...${RESET} $@\n" - esptool.py --chip esp32 --port $PORT --baud $FLASH_BAUDRATE --after hard_reset write_flash --flash_mode=$FLASH_MODE --flash_freq $FLASH_SPEED --flash_size detect ${@} |egrep -v -i "stub|baud rate|Changed.|Configuring flash size|Serial port|esptool.py|Leaving" - } - - if [[ !USE_BOOTLOADER -eq 1 ]] - then - flash 0x1000 $BIN_PATH.bin - else - - printf "${STAGE}Creating partition table... ${RESET}" - if [[ target/partitions.bin -ot partitions.csv ]] - then - printf "\n\n" - # get gen_esp32part.py and create binary partition table - curl -s -S -f --max-time 5 -L $GENPART_SOURCE --output target/gen_esp32part.py_new - - if [ $? -ne 0 ]; then - if [ -f target/gen_esp32part.py ]; then - printf "\n${ERROR}Failed to get gen_esp32part.py${RESET} using old one\n\n" - else - printf "\n${ERROR}Failed to get gen_esp32part.py${RESET}\n\n" - exit 1 - fi - else - mv target/gen_esp32part.py_new target/gen_esp32part.py - fi - - rm target/partitions.bin 2> /dev/null - python target/gen_esp32part.py partitions.csv target/partitions.bin - - echo - else - printf "skipping as it is up to date\n\n" - fi - - - printf "${STAGE}Getting bootloader... ${RESET}" - - # get bootloader.bin file - # (different variants exist, but only difference is flash settings which are overriden by esptool) - curl -s -S -f --max-time 5 -L $BOOTLOADER_SOURCE --output target/bootloader.bin_new - - if [ $? -ne 0 ]; then - if [ -f target/bootloader.bin ]; then - printf "\n${ERROR}Failed to get bootloader${RESET} using old one\n\n" - else - printf "\n${ERROR}Failed to get bootloader${RESET}\n\n" - exit 1 - fi - else - mv target/bootloader.bin_new target/bootloader.bin - printf "succesfully downloader bootloader\n\n" - fi - - # check if bootloader.bin and paritions.bin are already correctly flashed (to prevent unnecessary writes) - printf "${STAGE}Verify bootloader and partition table...${RESET} " - esptool.py --no-stub --chip esp32 --port $PORT --baud $FLASH_BAUDRATE verify_flash 0x1000 target/bootloader.bin $PARTION_ADDR target/partitions.bin > /dev/null - if [ $? -ne 0 ]; then - printf "modified\n\n" - # flash bootloader.bin, partitions.bin and application - flash 0x1000 target/bootloader.bin $PARTION_ADDR target/partitions.bin $APP_ADDR $BIN_PATH.bin - else - printf "unchanged\n\n" - # flash application only - flash $APP_ADDR $BIN_PATH.bin - fi - fi - - if [[ $? -ne 0 ]] - then - printf "\n${ERROR}Error flashing application${RESET}\n\n" - exit 1 - fi -fi - -# start terminal program -if [[ TERMINAL -eq 1 ]] -then - printf "\n${STAGE}Starting terminal.${RESET}\n" - gnome-terminal --geometry 200x15+0+9999 -- picocom -b $TERM_BAUDRATE $PORT --imap lfcrlf 2>/dev/null -fi - - -if [[ $SKIPFLASH -ne 1 ]] -then - printf "\n${SUCCESS}Succesfully build and flashed application${RESET}\n\n" -else - printf "\n${SUCCESS}Succesfully build application${RESET}\n\n" -fi \ No newline at end of file diff --git a/ld/rom.x b/ld/rom.x index 9656cb9..3d512a0 100644 --- a/ld/rom.x +++ b/ld/rom.x @@ -79,4 +79,5 @@ PROVIDE( ets_get_cpu_frequency = 0x40000584 ); PROVIDE( software_reset = 0x40000090 ); PROVIDE( ets_delay_us = 0x40000050 ); PROVIDE( tinfl_decompress = 0x400000f4 ); +PROVIDE( GetSecurityInfoProc = 0x4004b9da ); /* Do not exceed this mark in the error messages above | */ diff --git a/ld/stub.x b/ld/stub.x index 74c408b..b6b8739 100644 --- a/ld/stub.x +++ b/ld/stub.x @@ -44,7 +44,7 @@ REGION_ALIAS("REGION_RWTEXT", IRAM); REGION_ALIAS("REGION_RTC_FAST", RTC_FAST); -ENTRY(_start_hal) +ENTRY(_start_hal) /* _start_hal */ PROVIDE(_start_trap = _start_trap_hal); PROVIDE(_stext = ORIGIN(REGION_TEXT)); @@ -97,53 +97,10 @@ SECTIONS . = ABSOLUTE(_stext); } > REGION_TEXT - .text _stext : - { - _stext = .; - /* Put reset handler first in .text section so it ends up as the entry */ - /* point of the program. */ - /* KEEP(*(.init)); - KEEP(*(.init.rust)); - . = ALIGN(4); - (*(.trap)); - (*(.trap.rust)); - - *(.text .text.*); */ - _etext = .; - } > REGION_TEXT - - /** - * This dummy section represents the .text section but in rodata. - * Thus, it must have its alignement and (at least) its size. - */ - .text_dummy (NOLOAD): - { - /* Start at the same alignement constraint than .text */ - . = ALIGN(ALIGNOF(.text)); - /* Create an empty gap as big as .text section */ - . = . + SIZEOF(.text); - /* Prepare the alignement of the section above. Few bytes (0x20) must be - * added for the mapping header. */ - . = ALIGN(0x10000) + 0x20; - } > REGION_RODATA - - .rodata : ALIGN(4) - { - _srodata = .; - *(.srodata .srodata.*); - *(.rodata .rodata.*); - - /* 4-byte align the end (VMA) of this section. - This is required by LLD to ensure the LMA of the following .data - section will have the correct alignment. */ - . = ALIGN(4); - _erodata = .; - } > REGION_RODATA - - .rwtext : ALIGN(4) { - _irwtext = LOADADDR(.rwtext); + .text : ALIGN(4) { + _irwtext = LOADADDR(.text); _srwtext = .; - *(.rwtext); + *(.text); . = ALIGN(4); KEEP(*(.init)); @@ -158,8 +115,8 @@ SECTIONS /* similar as text_dummy */ .ram_dummy (NOLOAD) : { - . = ALIGN(ALIGNOF(.rwtext)); - . = . + SIZEOF(.rwtext); + . = ALIGN(ALIGNOF(.text)); + . = . + SIZEOF(.text); } > REGION_DATA .data : ALIGN(4) @@ -170,6 +127,7 @@ SECTIONS PROVIDE(__global_pointer$ = . + 0x800); *(.sdata .sdata.* .sdata2 .sdata2.*); *(.data .data.*); + *(.rodata .rodata.*); . = ALIGN(4); _edata = .; } > REGION_DATA @@ -260,10 +218,6 @@ BUG(riscv-rt): .bss is not 4-byte aligned"); ASSERT(_sheap % 4 == 0, " BUG(riscv-rt): start of .heap is not 4-byte aligned"); -ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), " -ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region. -Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'"); - ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. Consider changing `_max_hart_id` or `_hart_stack_size`."); diff --git a/openocd.cfg b/openocd.cfg deleted file mode 100644 index cdbbe9c..0000000 --- a/openocd.cfg +++ /dev/null @@ -1,6 +0,0 @@ - -adapter_khz 4000 - -source [find interface/jlink.cfg] - -source [find board/esp-wroom-32.cfg] \ No newline at end of file diff --git a/src/commands.rs b/src/commands.rs index 917d653..1de91fb 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -39,10 +39,12 @@ pub enum CommandCode FlashDeflData = 0x11, FlashDeflEnd = 0x12, SpiFlashMd5 = 0x13, + GetSecurityInfo = 0x14, EraseFlash = 0xD0, EraseRegion = 0xD1, ReadFlash = 0xD2, RunUserCode = 0xD3, + FlashEncryptedData = 0xD4, } #[derive(PartialEq, Copy, Clone, Debug)] @@ -149,7 +151,7 @@ pub struct EraseRegionCommand { } // Possibly move to other module -#[derive(PartialEq, Copy, Clone)] +#[derive(PartialEq, Copy, Clone, Debug)] #[repr(C, packed(1))] pub struct ReadFlashParams { pub address: u32, @@ -158,7 +160,7 @@ pub struct ReadFlashParams { pub max_inflight: u32, } -#[derive(PartialEq, Copy, Clone)] +#[derive(PartialEq, Copy, Clone, Debug)] #[repr(C, packed(1))] pub struct ReadFlashCommand { pub base: CommandBase, diff --git a/src/dprint.rs b/src/dprint.rs new file mode 100644 index 0000000..0f03b3b --- /dev/null +++ b/src/dprint.rs @@ -0,0 +1,162 @@ +//! Print debug information to UART1 +//! +//! Directly writes to the UART1 TX uart queue. +//! This is unsafe! It is asynchronous with normal UART1 usage and +//! interrupts are not disabled. + +use esp32c3_hal::pac::UART1; +use esp_hal_common::{ + OutputPin, + types::OutputSignal, +}; +pub struct DebugLog {} + +pub enum Error {} + +use esp32c3_hal::pac::{ system, uart0 }; + +static mut SCLK_SEL: u8 = 0; + +fn div_up(a: u32, b: u32) -> u32{ + (a + b - 1) / b +} + +fn get_sclk_freq(uart: &uart0::RegisterBlock) -> u32 +{ + const APB_CLK_FREQ: u32 = 80000000; + const RTC_CLK_FREQ: u32 = 20000000; + const XTAL_CLK_FREQ: u32 = 40000000; + + unsafe{ SCLK_SEL = uart.clk_conf.read().sclk_sel().bits(); } + + + match uart.clk_conf.read().sclk_sel().bits() { + 1 => APB_CLK_FREQ, + 2 => RTC_CLK_FREQ, + 3 => XTAL_CLK_FREQ, + _ => XTAL_CLK_FREQ, + } +} + +fn set_baudrate(uart: &uart0::RegisterBlock, baud: u32) +{ + let sclk_freq: u32 = get_sclk_freq(uart); + let max_div: u32 = (1 << 12) - 1; + let sclk_div: u32 = div_up(sclk_freq, max_div * baud); + let clk_div: u32 = ((sclk_freq) << 4) / (baud * sclk_div); + let clk_div_shift = (clk_div >> 4) as u16; + + uart.clkdiv.modify(|_, w| unsafe{ w.clkdiv().bits(clk_div_shift) + .frag().bits((clk_div & 0xf) as u8) } ); + uart.clk_conf.modify(|_, w| unsafe{ w.sclk_div_num().bits((sclk_div - 1) as u8) } ); + } + + pub fn init_debug_uart >( + system: &system::RegisterBlock, + uart: &uart0::RegisterBlock, + mut tx_pin: TxPin, + baudrate: u32) { + + tx_pin.set_to_push_pull_output().connect_peripheral_to_output(OutputSignal::U1TXD); + + system.perip_clk_en0.modify(|_, w| w.uart_mem_clk_en().set_bit() + .uart_clk_en().set_bit()); + system.perip_rst_en0.modify(|_, w| w.uart1_rst().clear_bit()); + + uart.clk_conf.modify(|_, w| w.rst_core().set_bit()); + system.perip_rst_en0.modify(|_, w| w.uart1_rst().set_bit()); + system.perip_rst_en0.modify(|_, w| w.uart1_rst().clear_bit()); + uart.clk_conf.modify(|_, w| w.rst_core().clear_bit()); + uart.id.modify(|_, w| w.reg_update().clear_bit()); + + while uart.id.read().reg_update().bit_is_set() { } + set_baudrate(uart, baudrate); + uart.id.modify(|_, w| w.reg_update().set_bit()); +} + + +impl DebugLog { + pub fn count(&mut self) -> u16 { + unsafe { (*UART1::ptr()).status.read().txfifo_cnt().bits() } + } + + // pub fn is_idle(&mut self) -> bool { + // unsafe { (*UART1::ptr()).status.read().st_utx_out().is_tx_idle() } + // } + + fn write(&mut self, word: u8) -> nb::Result<(), Error> { + if self.count() < 128 { + unsafe{ (*UART1::ptr()).fifo.write(|w| w.rxfifo_rd_byte().bits(word) ) }; + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } +} + +impl core::fmt::Write for DebugLog { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + s.as_bytes() + .iter() + .try_for_each(|c| nb::block!(self.write(*c))) + .map_err(|_| core::fmt::Error) + } +} + +pub static mut DEBUG_LOG: DebugLog = DebugLog {}; + +/// Macro for sending a formatted string to UART1 for debugging +#[macro_export] +macro_rules! dprint { + ($s:expr) => { + #[allow(unused_unsafe)] + unsafe { + use core::fmt::Write; + $crate::dprint::DEBUG_LOG.write_str($s).unwrap(); + } + }; + ($($arg:tt)*) => { + #[allow(unused_unsafe)] + unsafe { + use core::fmt::Write; + $crate::dprint::DEBUG_LOG.write_fmt(format_args!($($arg)*)).unwrap(); + } + }; +} + +/// Macro for sending a formatted string to UART1 for debugging, with a newline. +#[macro_export] +macro_rules! dprintln { + () => { + #[allow(unused_unsafe)] + unsafe { + use core::fmt::Write; + $crate::dprint::DEBUG_LOG.write_str("\n").unwrap(); + } + }; + ($fmt:expr) => { + #[allow(unused_unsafe)] + unsafe { + use core::fmt::Write; + $crate::dprint::DEBUG_LOG.write_str(concat!($fmt, "\n")).unwrap(); + } + }; + ($fmt:expr, $($arg:tt)*) => { + #[allow(unused_unsafe)] + unsafe { + use core::fmt::Write; + $crate::dprint::DEBUG_LOG.write_fmt(format_args!(concat!($fmt, "\n"), $($arg)*)).unwrap(); + } + }; +} + +/// Macro for flushing the UART1 TX buffer +#[macro_export] +macro_rules! dflush { + () => { + #[allow(unused_unsafe)] + unsafe { + while !$crate::dprint::DEBUG_LOG.is_idle() {} + } + }; +} diff --git a/src/main.rs b/src/main.rs index f6f2d99..46c1527 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,56 +6,57 @@ mod protocol; mod commands; mod targets; mod miniz_types; +mod dprint; +mod serial_io; -#[cfg(not(test))] +// #[cfg(not(test))] mod main { - use riscv_rt::entry; use core::panic::PanicInfo; - use crate::protocol::{InputIO, ErrorIO}; - use embedded_hal::serial::Read; - use esp32c3_hal::{ Serial, pac }; - use esp_hal_common::serial::Instance; - use crate::protocol::Stub; - use crate::targets::esp32c3 as target; - use nb; - - impl<'a, T: Instance> InputIO for Serial { - fn recv(&mut self) -> Result { - nb::block!(self.read()).map_err(|_| ErrorIO::Hardware) - } - - fn send(&mut self, bytes: &[u8]) -> Result<(), ErrorIO> - { - self.write_bytes(bytes).map_err(|_| ErrorIO::Hardware) - } - } - - const MSG_BUFFER_SIZE: usize = 0x5000; + use esp32c3_hal::{ + Serial, + pac, + gpio::IO + }; + use crate::{ + protocol::Stub, + targets::esp32c3 as target, + dprintln, + dprint::*, + serial_io, + }; + + const MSG_BUFFER_SIZE: usize = target::MAX_WRITE_BLOCK + 0x400; #[entry] fn main() -> ! { + let mut buffer: [u8; MSG_BUFFER_SIZE] = [0; MSG_BUFFER_SIZE]; + let peripherals = pac::Peripherals::take().unwrap(); + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + init_debug_uart(&peripherals.SYSTEM, &peripherals.UART1, io.pins.gpio10, 921600); + let mut serial = Serial::new(peripherals.UART0).unwrap(); - + + // Must be called after Serial::new, as it disables interrupts + serial_io::enable_uart0_rx_interrupt(); + let mut stub = Stub::new(&mut serial); - - stub.send_greeting().unwrap(); - - target::init().unwrap(); - - let mut buffer: [u8; MSG_BUFFER_SIZE] = [0; MSG_BUFFER_SIZE]; - + + stub.send_greeting(); + + target::init(); + loop { - let data = stub.read_command(&mut buffer).unwrap(); - stub.process_command(data).unwrap(); + let data = stub.read_command(&mut buffer); + stub.process_command(data); } } #[panic_handler] fn panic(_info: &PanicInfo) -> ! { + dprintln!("Panic !!!"); loop {} } } - diff --git a/src/protocol.rs b/src/protocol.rs index 8bc00d3..2de5e25 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -5,22 +5,16 @@ use mockall_double::double; #[double] use crate::targets::esp32c3 as target; -#[derive(Debug, PartialEq)] -pub enum ErrorIO { - Overflow, - Incomplete, - InvalidResponse, - Hardware, -} - pub trait InputIO { - fn recv(&mut self) -> Result; - fn send(&mut self, data: &[u8]) -> Result<(), ErrorIO>; + fn recv(&mut self) -> u8; + fn send(&mut self, data: &[u8]); } +type FlashFunc = fn(addr: u32, data: *const u8, len: u32) -> Result<(), Error>; + use target::*; use slip::*; -use core::mem; +use core::mem::size_of; use core::slice; use core::cmp::min; use crate::commands::*; @@ -28,9 +22,10 @@ use crate::commands::CommandCode::*; use crate::commands::Error::*; use md5::{Md5, Digest}; use crate::miniz_types::*; +use crate::dprintln; -const DATA_CMD_SIZE: usize = mem::size_of::(); -const CMD_BASE_SIZE: usize = mem::size_of::(); +const DATA_CMD_SIZE: usize = size_of::(); +const CMD_BASE_SIZE: usize = size_of::(); const FLASH_SECTOR_SIZE: u32 = 4096; const MAX_WRITE_BLOCK: u32 = 0x4000; @@ -46,30 +41,17 @@ pub struct Stub<'a> { in_flash_mode: bool, } -impl From for Error { - fn from(_: ErrorIO) -> Self { - Error::FailedSpiOp - } -} - -// todo: get rid of SIZE -fn slice_to_struct(slice: &[u8]) -> Result +fn slice_to_struct(slice: &[u8]) -> Result { - if SIZE != slice.len() { - return Err(BadDataLen); + if slice.len() < size_of::() { + return Err(Error::BadDataLen); } - let array: &[u8; SIZE] = &slice[0..SIZE].try_into().unwrap(); - unsafe { Ok(mem::transmute_copy::<[u8; SIZE], T>(array)) } -} - -macro_rules! transmute { - ($slice:expr, $type:ty) => { - slice_to_struct::<$type, { mem::size_of::<$type>() }>($slice) - }; + // SAFETY alignment and size have already been checked + unsafe { Ok(*(slice.as_ptr() as *const T)) } } pub unsafe fn to_slice_u8(p: &T) -> &[u8] { - slice::from_raw_parts( (p as *const T) as *const u8, mem::size_of::(), ) + slice::from_raw_parts( (p as *const T) as *const u8, size_of::(), ) } fn u32_from_slice(slice: &[u8], index: usize) -> u32 { @@ -108,35 +90,41 @@ impl<'a> Stub<'a> { } } - fn send_response(&mut self, resp: &Response) -> Result<(), Error> { + fn send_response(&mut self, resp: &Response) { let resp_slice = unsafe{ to_slice_u8(resp) }; - write_delimiter(self.io)?; - write_raw(self.io, &resp_slice[..RESPONSE_SIZE])?; - write_raw(self.io, resp.data)?; - write_delimiter(self.io)?; - Ok(()) + write_delimiter(self.io); + write_raw(self.io, &resp_slice[..RESPONSE_SIZE]); + write_raw(self.io, resp.data); + write_delimiter(self.io); } - fn send_md5_response(&mut self, resp: &Response, md5: &[u8]) -> Result<(), Error> { + fn send_response_with_data(&mut self, resp: &Response, data: &[u8]) { let resp_slice = unsafe{ to_slice_u8(resp) }; - write_delimiter(self.io)?; - write_raw(self.io, &resp_slice[..RESPONSE_SIZE-2])?; - write_raw(self.io, md5)?; - write_raw(self.io, &resp_slice[RESPONSE_SIZE-2..RESPONSE_SIZE])?; - write_delimiter(self.io)?; - Ok(()) + write_delimiter(self.io); + write_raw(self.io, &resp_slice[..RESPONSE_SIZE-2]); + write_raw(self.io, data); + write_raw(self.io, &resp_slice[RESPONSE_SIZE-2..RESPONSE_SIZE]); + write_delimiter(self.io); } - pub fn send_greeting(&mut self) -> Result<(), Error> { - let greeting = [0x4f, 0x48, 0x41, 0x49]; // OHAI - write_packet(self.io, &greeting)?; - Ok(()) + fn send_md5_response(&mut self, resp: &Response, md5: &[u8]) { + self.send_response_with_data(resp, md5) + } + + fn send_security_info_response(&mut self, resp: &Response, info: &[u8]) { + self.send_response_with_data(resp, info) + } + + pub fn send_greeting(&mut self) { + let greeting = ['O' as u8, 'H' as u8, 'A' as u8, 'I' as u8]; + write_packet(self.io, &greeting); } fn process_begin(&mut self, cmd: &BeginCommand) -> Result<(), Error> { + // Align erase addreess to sector boundady. + self.erase_addr = cmd.offset & FLASH_SECTOR_MASK; self.write_addr = cmd.offset; - self.erase_addr = cmd.offset; self.end_addr = cmd.offset + cmd.total_size; self.remaining_compressed = (cmd.packt_count * cmd.packet_size) as usize; self.remaining = cmd.total_size; @@ -160,13 +148,11 @@ impl<'a> Stub<'a> { fn process_end(&mut self, cmd: &EndCommand, response: &Response) -> Result<(), Error> { if cmd.base.code == MemEnd { - let addr = self.erase_addr as *const u32; let length = self.end_addr - self.erase_addr; let slice = unsafe { slice::from_raw_parts(addr, length as usize) }; let mut memory: [u32; 32] = [0; 32]; memory.copy_from_slice(&slice); - return match self.remaining { 0 => Ok(()), _ => Err(NotEnoughData) @@ -180,7 +166,7 @@ impl<'a> Stub<'a> { self.in_flash_mode = false; if cmd.run_user_code == 1 { - self.send_response(&response)?; + self.send_response(&response); delay_us(10000); soft_reset(); } @@ -191,9 +177,7 @@ impl<'a> Stub<'a> { fn write_ram(&mut self, data: &[u8]) -> Result<(), Error> { let data_len = data.len() as u32; - if self.write_addr == 0 && data_len > 0 { - return Err(NotInFlashMode); - } else if data_len > self.remaining { + if data_len > self.remaining { return Err(TooMuchData); } else if data_len % 4 != 0 { return Err(BadDataLen); @@ -210,16 +194,16 @@ impl<'a> Stub<'a> { Ok(()) } - fn flash_data(&mut self, data: &[u8]) -> Result<(), Error> { + fn flash(&mut self, flash_write: FlashFunc, data: &[u8]) { let mut address = self.write_addr; - let mut remaining = data.len() as u32; + let mut remaining = min(self.remaining, data.len() as u32); let mut written = 0; // Erase flash while self.erase_addr < self.write_addr + remaining { if self.end_addr >= self.erase_addr + FLASH_BLOCK_SIZE && - self.erase_addr % FLASH_BLOCK_SIZE == 0 { + self.erase_addr % FLASH_BLOCK_SIZE == 0 { flash_erase_block(self.erase_addr); self.erase_addr += FLASH_BLOCK_SIZE; } else { @@ -232,7 +216,7 @@ impl<'a> Stub<'a> { while remaining > 0 { let to_write = min(FLASH_SECTOR_SIZE, remaining); let data_ptr = data[written..].as_ptr(); - spiflash_write(address, data_ptr, to_write)?; + self.last_error = flash_write(address, data_ptr, to_write).err(); remaining -= to_write; written += to_write as usize; address += to_write; @@ -240,11 +224,13 @@ impl<'a> Stub<'a> { self.write_addr += written as u32; self.remaining -= min(self.remaining, written as u32); + } - Ok(()) + fn flash_data(&mut self, data: &[u8]) { + self.flash(spiflash_write, data); } - fn flash_defl_data(&mut self, data: &[u8]) -> Result<(), Error> { + fn flash_defl_data(&mut self, data: &[u8]) { use crate::miniz_types::TinflStatus::*; const OUT_BUFFER_SIZE: usize = 0x8000; //32768; @@ -266,7 +252,7 @@ impl<'a> Stub<'a> { if self.remaining_compressed > length { flags |= TINFL_FLAG_HAS_MORE_INPUT; } - + status = target::decompress( &mut self.decompressor, data[in_index..].as_ptr(), @@ -275,20 +261,20 @@ impl<'a> Stub<'a> { next_out, &mut out_bytes, flags); - - self.remaining_compressed -= in_bytes; - length -= in_bytes; - in_index += in_bytes; - out_index += out_bytes; - - if status == Done || out_index == OUT_BUFFER_SIZE { - self.flash_data(&out_buf[..out_index])?; - out_index = 0; + + self.remaining_compressed -= in_bytes; + length -= in_bytes; + in_index += in_bytes; + out_index += out_bytes; + + if status == Done || out_index == OUT_BUFFER_SIZE { + self.flash_data(&out_buf[..out_index]); + out_index = 0; + } } - } - + unsafe{ DECOMPRESS_INDEX = out_index }; - + // error won't get sent back until next block is sent if status < Done { self.last_error = Some(InflateError); @@ -297,12 +283,15 @@ impl<'a> Stub<'a> { } else if status != Done && self.remaining == 0 { self.last_error = Some(TooMuchData); } - - Ok(()) } - fn process_data(&mut self, cmd: &DataCommand, data: &[u8]) -> Result<(), Error> { - + fn flash_encrypt_data(&mut self, data: &[u8]) { + write_encrypted_enable(); + self.flash(write_encrypted, data); + write_encrypted_disable(); + } + + fn process_data(&mut self, cmd: &DataCommand, data: &[u8], response: &Response) -> Result<(), Error> { let checksum: u8 = data.iter().fold(0xEF, |acc, x| acc ^ x); if !self.in_flash_mode { @@ -312,43 +301,47 @@ impl<'a> Stub<'a> { } else if cmd.base.checksum != checksum as u32 { return Err(BadDataChecksum); } - + + self.send_response(&response); + match cmd.base.code { - FlashDeflData => self.flash_defl_data(data)?, - FlashData => self.flash_data(data)?, + FlashEncryptedData => self.flash_encrypt_data(data), + FlashDeflData => self.flash_defl_data(data), + FlashData => self.flash_data(data), MemData => self.write_ram(data)?, _ => () } - + Ok(()) } fn process_read_flash(&mut self, params: &ReadFlashParams) -> Result<(), Error> { - // Can return FailedSpiOp (?) const BUF_SIZE: usize = FLASH_SECTOR_SIZE as usize; let mut buffer: [u8; BUF_SIZE] = [0; BUF_SIZE]; let mut address = params.address; let mut remaining = params.total_size; - let to_ack = params.total_size / params.packet_size; let mut acked = 0; let mut ack_buf: [u8; 4] = [0; 4]; let mut hasher = Md5::new(); - - while acked < to_ack { - while remaining > 0 && (to_ack - acked) < params.max_inflight { + let mut sent = 0; + let max_inflight_bytes = params.max_inflight * params.packet_size; + + while acked < params.total_size { + while remaining > 0 && sent < (acked + max_inflight_bytes) { let len = min(params.packet_size, remaining); spi_flash_read(address, &mut buffer[..len as usize])?; - write_packet(self.io, &buffer[..len as usize])?; + write_packet(self.io, &buffer[..len as usize]); hasher.update(&buffer[0..len as usize]); remaining -= len; address += len; + sent += len; } - let resp = read_packet(self.io, &mut ack_buf)?; + let resp = read_packet(self.io, &mut ack_buf); acked = u32_from_slice(resp, 0); } let md5: [u8; 16] = hasher.finalize().into(); - write_packet(self.io, &md5)?; + write_packet(self.io, &md5); Ok(()) } @@ -359,37 +352,44 @@ impl<'a> Stub<'a> { let mut response_sent = false; + dprintln!("process command: {:?}", code); + match code { - Sync => (), + Sync => { + for _ in 1..=7 { + self.send_response(&response); + } + }, ReadReg => { let address = u32_from_slice(payload, CMD_BASE_SIZE); response.value(read_register(address)); } WriteReg => { - let reg = transmute!(payload, WriteRegCommand)?; + let reg: WriteRegCommand = slice_to_struct(payload)?; write_register(reg.address, reg.value); } FlashBegin | MemBegin | FlashDeflBegin => { - let cmd = transmute!(payload, BeginCommand)?; + let cmd: BeginCommand = slice_to_struct(payload)?; self.process_begin(&cmd)? } - FlashData | FlashDeflData | MemData => { - let cmd = transmute!(&payload[..DATA_CMD_SIZE], DataCommand)?; + FlashData | FlashDeflData | FlashEncryptedData | MemData => { + let cmd: DataCommand = slice_to_struct(&payload)?; let data = &payload[DATA_CMD_SIZE..]; - self.process_data(&cmd, data)? + self.process_data(&cmd, data, &response)?; + response_sent = true; } FlashEnd | MemEnd | FlashDeflEnd => { - let cmd = transmute!(payload, EndCommand)?; + let cmd: EndCommand = slice_to_struct(payload)?; self.process_end(&cmd, &response)?; } SpiFlashMd5 => { - let cmd = transmute!(payload, SpiFlashMd5Command)?; + let cmd: SpiFlashMd5Command = slice_to_struct(payload)?; let md5 = calculate_md5(cmd.address, cmd.size)?; - self.send_md5_response(&response, &md5)?; + self.send_md5_response(&response, &md5); response_sent = true; } SpiSetParams => { - let cmd = transmute!(payload, SpiSetParamsCommand)?; + let cmd: SpiSetParamsCommand = slice_to_struct(payload)?; spi_set_params(&cmd.params)? } SpiAttach => { @@ -397,27 +397,33 @@ impl<'a> Stub<'a> { spi_attach(param); } ChangeBaudrate => { - let baud = transmute!(payload, ChangeBaudrateCommand)?; - self.send_response(&response)?; - delay_us(10000); + let baud: ChangeBaudrateCommand = slice_to_struct(payload)?; + self.send_response(&response); + delay_us(10000); // Wait for response to be transfered change_baudrate(baud.old, baud.new); + self.send_greeting(); response_sent = true; } EraseFlash => { erase_flash()? } EraseRegion => { - let reg = transmute!(payload, EraseRegionCommand)?; + let reg: EraseRegionCommand = slice_to_struct(payload)?; erase_region(reg.address, reg.size)?; } ReadFlash => { - self.send_response(&response)?; - let cmd = transmute!(payload, ReadFlashCommand)?; + self.send_response(&response); + let cmd: ReadFlashCommand = slice_to_struct(payload)?; self.process_read_flash(&cmd.params)?; response_sent = true; } + GetSecurityInfo => { + let info = get_security_info()?; + self.send_security_info_response(&response, &info); + response_sent = true; + } RunUserCode => { - todo!(); + soft_reset(); // ESP8266 Only } _ => { return Err(InvalidCommand); @@ -427,49 +433,43 @@ impl<'a> Stub<'a> { Ok(response_sent) } - pub fn process_command(&mut self, payload: &[u8]) -> Result<(), Error> { + pub fn process_command(&mut self, payload: &[u8]) { - let command = transmute!(&payload[..CMD_BASE_SIZE], CommandBase)?; + let command: CommandBase = slice_to_struct(&payload).unwrap(); let mut response = Response::new(command.code); match self.process_cmd(payload, command.code, &mut response) { Ok(response_sent) => match response_sent { - true => return Ok(()), + true => return, false => (), } Err(err) => response.error(err) } - self.send_response(&response) + self.send_response(&response); } - pub fn read_command<'c, 'd>(&'c mut self, buffer: &'d mut [u8]) -> Result<&'d [u8], Error> { - Ok( read_packet(self.io, buffer)? ) + pub fn read_command<'c, 'd>(&'c mut self, buffer: &'d mut [u8]) -> &'d [u8] { + read_packet(self.io, buffer) } } mod slip { use super::*; - use super::ErrorIO::*; - - impl From for ErrorIO { - fn from(_: u8) -> Self { - ErrorIO::Overflow - } - } - pub fn read_packet<'c, 'd>(io: &'c mut dyn InputIO, packet: &'d mut [u8]) -> Result<&'d [u8], ErrorIO> { + pub fn read_packet<'c, 'd>(io: &'c mut dyn InputIO, packet: &'d mut [u8]) -> &'d [u8] { - while io.recv()? != 0xC0 { } + while io.recv() != 0xC0 { } // Replase: 0xDB 0xDC -> 0xC0 and 0xDB 0xDD -> 0xDB let mut i = 0; loop { - match io.recv()? { - 0xDB => match io.recv()? { + match io.recv() { + 0xDB => match io.recv() { 0xDC => packet[i] = 0xC0, 0xDD => packet[i] = 0xDB, - _ => return Err(InvalidResponse), + _ => continue, // Framing error, continue processing + } 0xC0 => break, other => packet[i] = other, @@ -477,35 +477,33 @@ mod slip { i += 1; } - Ok(&packet[..i]) + &packet[..i] } - pub fn write_raw(io: &mut dyn InputIO, data: &[u8]) -> Result<(), ErrorIO> { + pub fn write_raw(io: &mut dyn InputIO, data: &[u8]) { for byte in data { match byte { - 0xC0 => io.send(&[0xDB, 0xDC])?, - 0xDB => io.send(&[0xDB, 0xDD])?, - other => io.send(&[*other])?, + 0xC0 => io.send(&[0xDB, 0xDC]), + 0xDB => io.send(&[0xDB, 0xDD]), + other => io.send(&[*other]), } } - Ok(()) } - pub fn write_packet(io: &mut dyn InputIO, data: &[u8]) -> Result<(), ErrorIO> { - write_delimiter(io)?; - write_raw(io, data)?; - write_delimiter(io) + pub fn write_packet(io: &mut dyn InputIO, data: &[u8]) { + write_delimiter(io); + write_raw(io, data); + write_delimiter(io); } - pub fn write_delimiter(io: &mut dyn InputIO) -> Result<(), ErrorIO> { - io.send(&[0xC0]) + pub fn write_delimiter(io: &mut dyn InputIO) { + io.send(&[0xC0]); } } #[cfg(test)] mod tests { use super::*; - use super::ErrorIO::*; // use super::stub::Error::*; use super::slip::{read_packet, write_raw}; use assert2::{assert, let_assert}; @@ -714,14 +712,6 @@ mod tests { assert!( io.written() == expected); } - // fn slice_to_cmd(slice: &[u8]) -> Vec { - // let mut v = Vec::new(); - // v.push(0xC0); - // v.extend_from_slice( slice ); - // v.push(0xC0); - // v - // } - fn decorate_command(data: T) -> Vec { let mut v = Vec::new(); v.push(0xC0); @@ -753,10 +743,6 @@ mod tests { ctx.expect().with(predicate::eq(200)).returning(|x| x + 1); assert!( Ok(()) == stub.process_commands() ); - // let resp = TestResponse { - // direction: 1, command: CommandCode::ReadReg, size: 2, value: 201, status: 0, error: 0 - // }; - // let expect = decorate_command(resp); let expect = &[0xC0, 1, CommandCode::ReadReg as u8, 2,0, 201,0,0,0, 0,0, 0xC0]; assert!( io.written() == expect ); } diff --git a/src/serial_io.rs b/src/serial_io.rs new file mode 100644 index 0000000..3eed3d8 --- /dev/null +++ b/src/serial_io.rs @@ -0,0 +1,58 @@ +use heapless::Deque; +use esp32c3_hal::{ + Serial, + pac, + pac::UART0, +}; +use esp_hal_common::{ + serial::Instance, + interrupt, + interrupt::*, + interrupt::CpuInterrupt::*, + Cpu::*, +}; +use crate::protocol::InputIO; +use crate::targets::esp32c3 as target; + +const RX_QUEUE_SIZE: usize = target::MAX_WRITE_BLOCK + 0x400; +static mut RX_QUEUE: Deque = Deque::new(); + +impl<'a, T: Instance> InputIO for Serial { + fn recv(&mut self) -> u8 { + unsafe{ + while riscv::interrupt::free(|_| RX_QUEUE.is_empty() ) { } + riscv::interrupt::free(|_| RX_QUEUE.pop_front().unwrap_unchecked()) + } + } + + fn send(&mut self, bytes: &[u8]) { + self.write_bytes(bytes).unwrap() + } +} + +pub fn enable_uart0_rx_interrupt() { + let uart = unsafe{ &*UART0::ptr() }; + + uart.conf1.modify(|_, w| unsafe{ w.rxfifo_full_thrhd().bits(1) }); + uart.int_ena.write(|w| w.rxfifo_full_int_ena().set_bit() ); + + interrupt::enable( ProCpu, pac::Interrupt::UART0, Interrupt3 ); + interrupt::set_kind( ProCpu, Interrupt3, InterruptKind::Level ); + interrupt::set_priority( ProCpu, Interrupt3, Priority::Priority10 ); + + unsafe { riscv::interrupt::enable(); } +} + + +#[no_mangle] +pub fn interrupt3() { + let uart = unsafe{ &*UART0::ptr() }; + + while uart.status.read().rxfifo_cnt().bits() > 0 { + let data = uart.fifo.read().rxfifo_rd_byte().bits(); + unsafe{ RX_QUEUE.push_back(data).unwrap() }; + } + + uart.int_clr.write(|w| w.rxfifo_full_int_clr().set_bit()); + interrupt::clear(ProCpu, Interrupt3); +} \ No newline at end of file diff --git a/src/targets.rs b/src/targets.rs index f9b3bd1..fe447c9 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -21,6 +21,10 @@ extern "C" { fn ets_efuse_get_spiconfig() -> u32; fn software_reset(); fn ets_delay_us(timeout: u32); + fn GetSecurityInfoProc(pMsg: u8, pnErr: u8, data: *const u8) -> u32; + fn esp_rom_spiflash_write_encrypted_enable(); + fn esp_rom_spiflash_write_encrypted_disable(); + fn esp_rom_spiflash_write_encrypted(dest_addr: u32, data: *const u8, len: u32) -> i32; } @@ -29,6 +33,7 @@ pub mod esp32c3 { use super::*; use crate::commands::*; use crate::commands::Error::*; + use core::ptr::{read_volatile, write_volatile}; const SPI_BASE_REG: u32 = 0x60002000; const SPI_CMD_REG: u32 = SPI_BASE_REG + 0x00; @@ -53,6 +58,8 @@ pub mod esp32c3 { const UART_CLKDIV_FRAG_V: u32 = 0xF; pub const FLASH_SECTOR_SIZE: u32 = 4096; pub const FLASH_BLOCK_SIZE: u32 = 65536; + pub const FLASH_SECTOR_MASK: u32 = 0xFFFFF000; + pub const MAX_WRITE_BLOCK: usize = 0x4000; const GPIO_BASE_REG: u32 = 0x60004000; const GPIO_STRAP_REG: u32 = GPIO_BASE_REG + 0x38; @@ -61,6 +68,8 @@ pub mod esp32c3 { const FLASH_PAGE_SIZE: u32 = 256; const FLASH_STATUS_MASK: u32 = 0xFFFF; + const SECURITY_INFO_BYTES : usize = 20; + fn get_uart_div(current_baud: u32, new_baud: u32) -> u32 { let clock_div_reg = read_register(UART0_CLKDIV_REG); let uart_div = clock_div_reg & UART_CLKDIV_M; @@ -70,11 +79,11 @@ pub mod esp32c3 { } pub fn read_register(address: u32) -> u32 { - unsafe { *(address as *const u32) } + unsafe{ read_volatile(address as *const u32) } } pub fn write_register(address: u32, value: u32) { - unsafe { *(address as *mut u32) = value; } + unsafe{ write_volatile(address as *mut _, value) } } pub fn spiflash_write(dest_addr: u32, data: *const u8, len: u32) -> Result<(), Error> { @@ -123,11 +132,6 @@ pub mod esp32c3 { while read_register(SPI_CMD_REG) != 0 { } spiflash_wait_for_ready(); - - // match unsafe{ esp_rom_spiflash_erase_block(address / FLASH_BLOCK_SIZE) } { // ??? - // 0 => Ok(()), - // _ => Err(FailedSpiOp) - // } } fn wait_for_ready() { @@ -197,7 +201,18 @@ pub mod esp32c3 { } } - pub fn init() -> Result<(), Error> { + // ESP32S2_OR_LATER && !ESP32H2BETA2 + pub fn get_security_info() -> Result<[u8; SECURITY_INFO_BYTES], Error> + { + let mut buf: [u8; SECURITY_INFO_BYTES] = [0; SECURITY_INFO_BYTES]; + + match unsafe{ GetSecurityInfoProc(0, 0, buf.as_mut_ptr()) } { + 0 => Ok(buf), + _ => Err(InvalidCommand), // Todo check ROM code for err val + } + } + + pub fn init() { let mut spiconfig = unsafe{ ets_efuse_get_spiconfig() }; let strapping = read_register(GPIO_STRAP_REG); @@ -217,7 +232,7 @@ pub mod esp32c3 { status_mask: FLASH_STATUS_MASK, }; - spi_set_params(&deafault_params) + let _ = spi_set_params(&deafault_params); } pub fn soft_reset() { @@ -228,6 +243,20 @@ pub mod esp32c3 { unsafe{ ets_delay_us(micro_seconds) }; } + pub fn write_encrypted_enable() { + unsafe{ esp_rom_spiflash_write_encrypted_enable(); } + } + pub fn write_encrypted_disable() { + unsafe{ esp_rom_spiflash_write_encrypted_disable(); } + } + + pub fn write_encrypted(addr: u32, data: *const u8, len: u32) -> Result<(), Error> { + match unsafe{ esp_rom_spiflash_write_encrypted(addr, data, len) } { + 0 => Ok(()), + _ => Err(FailedSpiOp) + } + } + pub fn decompress( r: *mut tinfl_decompressor, in_buf: *const u8, From 01244cdf09d99e813b04e4f9ab0d3a297815ac29 Mon Sep 17 00:00:00 2001 From: Martin Valik Date: Mon, 25 Jul 2022 13:24:56 +0200 Subject: [PATCH 10/10] Added support for ESP32 --- .cargo/config.toml | 13 +- Cargo.lock | 599 +++++++++++++++------------------- Cargo.toml | 34 +- README.md | 15 +- build.rs | 28 +- ld/esp32_rom.x | 18 + ld/esp32_stub.x | 353 ++++++++++++++++++++ ld/esp32c3_rom.x | 20 ++ ld/{stub.x => esp32c3_stub.x} | 106 +++--- ld/rom.x | 83 ----- src/commands.rs | 10 +- src/dprint.rs | 73 +---- src/main.rs | 101 +++--- src/protocol.rs | 421 +++++------------------- src/serial_io.rs | 77 +++-- src/targets.rs | 256 +++++++++------ 16 files changed, 1126 insertions(+), 1081 deletions(-) create mode 100644 ld/esp32_rom.x create mode 100644 ld/esp32_stub.x create mode 100644 ld/esp32c3_rom.x rename ld/{stub.x => esp32c3_stub.x} (77%) delete mode 100644 ld/rom.x diff --git a/.cargo/config.toml b/.cargo/config.toml index 0adcf86..cd13f07 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,9 @@ [build] -target = "riscv32imc-unknown-none-elf" -[target.'cfg(any(target_arch="riscv32"))'] -rustflags = [ - "-C", "link-args=-Map=target/stub.map", -] + +[target.'cfg(target_arch="riscv32")'] +rustflags = [ "-C", "link-args=-Map=target/stub.map" ] +[target.'cfg(target_arch="xtensa")'] +rustflags = [ "-Clink-args=-Wl,-Map=target/stub.map" ] + +[unstable] +build-std = ["core"] diff --git a/Cargo.lock b/Cargo.lock index ba092d7..69de62e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,54 +4,26 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] [[package]] -name = "assert2" -version = "0.3.6" +name = "anyhow" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1b167af16149cd41ff2b784bf511bb4208b21c3b05f3f61e30823ce3986361" -dependencies = [ - "assert2-macros", - "atty", - "yansi", -] - -[[package]] -name = "assert2-macros" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ac27dd1c8f16b282d1c22a8a5ae17119acc757101dec79054458fef62c447e" -dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", - "rustc_version 0.4.0", - "syn 1.0.86", -] +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "atomic-polyfill" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14bf7b4f565e5e717d7a7a65b2a05c0b8c96e4db636d6f780f03b15108cdd1b" -dependencies = [ - "critical-section", -] - -[[package]] -name = "atty" -version = "0.2.14" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "9c041a8d9751a520ee19656232a18971f18946a7900f1520ee4400002244dd89" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "critical-section 0.2.7", ] [[package]] @@ -89,9 +61,9 @@ checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] @@ -114,11 +86,24 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "core-isa-parser" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ec98e54b735872e54b2335c2e5a5c7fa7d9c3bfd45500f75280f84089a0083" +dependencies = [ + "anyhow", + "enum-as-inner", + "regex", + "strum", + "strum_macros", +] + [[package]] name = "cortex-m" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ff967e867ca14eba0c34ac25cd71ea98c678e741e3915d923999bb2fe7c826" +checksum = "70858629a458fdfd39f9675c4dc309411f2a3f83bede76988d81bf1a0ecee9e0" dependencies = [ "bare-metal 0.2.5", "bitfield", @@ -138,11 +123,17 @@ dependencies = [ "riscv 0.7.0", ] +[[package]] +name = "critical-section" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" + [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -150,9 +141,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.10.2" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02" dependencies = [ "darling_core", "darling_macro", @@ -160,126 +151,157 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.10.2" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2", + "quote", "strsim", - "syn 1.0.86", + "syn", ] [[package]] name = "darling_macro" -version = "0.10.2" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5" dependencies = [ "darling_core", - "quote 1.0.15", - "syn 1.0.86", + "quote", + "syn", ] -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - [[package]] name = "digest" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ "block-buffer", "crypto-common", ] [[package]] -name = "downcast" -version = "0.11.0" +name = "embedded-hal" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] [[package]] -name = "either" -version = "1.6.1" +name = "enum-as-inner" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "embedded-hal" -version = "0.2.7" +name = "esp-backtrace" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +checksum = "1cc04e4a62fe7905eab18e12715feaded6cb51a5a878a0c067bbe29436bad96a" dependencies = [ - "nb 0.1.3", - "void", + "esp-println", + "riscv 0.8.0", + "xtensa-lx-rt", ] [[package]] name = "esp-hal-common" -version = "0.1.0" +version = "0.2.0" +source = "git+https://github.com/esp-rs/esp-hal#5054681ba3a7333666e4685df05c2554625ff835" dependencies = [ "cfg-if 1.0.0", + "critical-section 1.1.1", "embedded-hal", "esp-hal-procmacros", + "esp32", "esp32c3", + "fugit", + "lock_api", "nb 1.0.0", "paste", - "riscv 0.7.0", + "riscv 0.8.0", "riscv-atomic-emulation-trap", "void", + "xtensa-lx", + "xtensa-lx-rt", ] [[package]] name = "esp-hal-procmacros" version = "0.1.0" +source = "git+https://github.com/esp-rs/esp-hal#5054681ba3a7333666e4685df05c2554625ff835" dependencies = [ "darling", "proc-macro-error", - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "esp32c3" -version = "0.3.0" -source = "git+https://github.com/esp-rs/esp-pacs.git?branch=with_source#91102d1de0bba040e2523f423d73f8d24332c4fa" +name = "esp-println" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab33baae57ce7c0869c32885ff136f9c8405e0c201d039ccd767c3e7f79502b" + +[[package]] +name = "esp32" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d26be703e6ca53cf5b81ae164c13eecd31d5cc4e25d04082f682345eab8cfc9" dependencies = [ "bare-metal 1.0.0", - "riscv 0.7.0", - "riscv-rt", "vcell", + "xtensa-lx", + "xtensa-lx-rt", ] [[package]] -name = "esp32c3-hal" -version = "0.1.0" +name = "esp32-hal" +version = "0.5.0" +source = "git+https://github.com/esp-rs/esp-hal#5054681ba3a7333666e4685df05c2554625ff835" dependencies = [ - "bare-metal 1.0.0", "embedded-hal", "esp-hal-common", - "nb 1.0.0", - "r0", - "riscv 0.7.0", - "riscv-atomic-emulation-trap", - "riscv-rt", - "void", + "xtensa-lx", + "xtensa-lx-rt", ] [[package]] -name = "float-cmp" -version = "0.9.0" +name = "esp32c3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +checksum = "79e9b4b4c623a6567e3282ec5df2daf38f1da7631b41da5be8c94432db8e11aa" +dependencies = [ + "bare-metal 1.0.0", + "riscv 0.8.0", + "riscv-rt", + "vcell", +] + +[[package]] +name = "esp32c3-hal" +version = "0.2.0" +source = "git+https://github.com/esp-rs/esp-hal#5054681ba3a7333666e4685df05c2554625ff835" dependencies = [ - "num-traits", + "embedded-hal", + "esp-hal-common", + "r0", + "riscv 0.8.0", + "riscv-rt", ] [[package]] @@ -289,16 +311,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "fragile" -version = "1.1.0" +name = "fugit" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da1b8f89c5b5a5b7e59405cfcf0bb9588e5ed19f0b57a4cd542bbba3f164a6d" +checksum = "7ab17bb279def6720d058cb6c052249938e7f99260ab534879281a95367a87e5" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37978dab2ca789938a83b2f8bc1ef32db6633af9051a6cd409eff72cbaaa79a" +dependencies = [ + "paste", +] [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", @@ -315,9 +349,9 @@ dependencies = [ [[package]] name = "heapless" -version = "0.7.14" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065681e99f9ef7e0e813702a0326aedbcbbde7db5e55f097aedd1bf50b9dca43" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" dependencies = [ "atomic-polyfill", "hash32", @@ -327,13 +361,10 @@ dependencies = [ ] [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "heck" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "ident_case" @@ -341,82 +372,44 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "itertools" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" -dependencies = [ - "either", -] - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "libc" -version = "0.2.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" - [[package]] name = "lock_api" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ + "autocfg", "scopeguard", ] -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - [[package]] name = "md-5" -version = "0.10.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ "digest", ] [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "mockall" -version = "0.11.0" +name = "minijinja" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d4d70639a72f972725db16350db56da68266ca368b2a1fe26724a903ad3d6b8" +checksum = "359c4820413be7706e93999171652e140578384f85faac14cb22d350bd0fbabf" dependencies = [ - "cfg-if 1.0.0", - "downcast", - "fragile", - "lazy_static", - "mockall_derive", - "predicates", - "predicates-tree", -] - -[[package]] -name = "mockall_derive" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ef208208a0dea3f72221e26e904cdc6db2e481d9ade89081ddd494f1dbaa6b" -dependencies = [ - "cfg-if 1.0.0", - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "serde", ] [[package]] @@ -426,11 +419,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eacc2558c7ab425550913e9ba69077513922396bc4a1338301e3a0d244d2555" dependencies = [ "cfg-if 0.1.10", - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "proc-macro2", + "quote", + "syn", ] +[[package]] +name = "mutex-trait" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4bb1638d419e12f8b1c43d9e639abd0d1424285bdea2f76aa231e233c63cd3a" + [[package]] name = "nb" version = "0.1.3" @@ -446,56 +445,11 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" -[[package]] -name = "normalize-line-endings" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - [[package]] name = "paste" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" - -[[package]] -name = "predicates" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" -dependencies = [ - "difflib", - "float-cmp", - "itertools", - "normalize-line-endings", - "predicates-core", - "regex", -] - -[[package]] -name = "predicates-core" -version = "1.0.3" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" - -[[package]] -name = "predicates-tree" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" -dependencies = [ - "predicates-core", - "termtree", -] +checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22" [[package]] name = "proc-macro-error" @@ -504,9 +458,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "proc-macro2", + "quote", + "syn", "version_check", ] @@ -516,45 +470,27 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2", + "quote", "version_check", ] [[package]] name = "proc-macro2" -version = "0.4.30" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58" dependencies = [ - "unicode-xid 0.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" -dependencies = [ - "unicode-xid 0.2.2", + "unicode-ident", ] [[package]] name = "quote" -version = "0.6.13" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ - "proc-macro2 0.4.30", -] - -[[package]] -name = "quote" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" -dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2", ] [[package]] @@ -563,35 +499,11 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" -[[package]] -name = "rand" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "regex" -version = "1.5.4" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -600,9 +512,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "riscv" @@ -628,37 +540,36 @@ dependencies = [ [[package]] name = "riscv-atomic-emulation-trap" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d8ed35d10950305d9abbc3fa49540b948e03b653c85012b4cd9f7d249029f0" +checksum = "4daa6e1007782fb8d56cf8c39fd97cfdbca77004ef6eadb884c7e11603bc8313" dependencies = [ - "riscv 0.7.0", + "riscv 0.8.0", "riscv-rt", "riscv-target", ] [[package]] name = "riscv-rt" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9f28dc850356196a36078c51c9dc7b46014a29a8fde35d5f81c99e489d0e00" +checksum = "527ddfacbbfeed89256163c2655b0392c4aa76dd95c0189dc05044bf2c47c3f8" dependencies = [ "r0", - "riscv 0.7.0", + "riscv 0.8.0", "riscv-rt-macros", "riscv-target", ] [[package]] name = "riscv-rt-macros" -version = "0.1.6" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3525f8341898dec060782087b7a15969e1cfe52818afacc47709265c19a23d53" +checksum = "f38509d7b17c2f604ceab3e5ff8ac97bb8cd2f544688c512be75c715edaf4daf" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "rand", - "syn 0.15.44", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -686,9 +597,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.6", + "semver 1.0.14", ] +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + [[package]] name = "scopeguard" version = "1.1.0" @@ -706,9 +623,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.6" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" [[package]] name = "semver-parser" @@ -716,11 +633,17 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "serde" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" + [[package]] name = "spin" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" dependencies = [ "lock_api", ] @@ -733,58 +656,58 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "strsim" -version = "0.9.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] [[package]] name = "stub" version = "0.1.0" dependencies = [ - "assert2", - "bare-metal 1.0.0", - "embedded-hal", + "esp-backtrace", "esp-hal-common", + "esp32-hal", "esp32c3-hal", "heapless", - "matches", "md-5", - "mockall", "mockall_double", "nb 1.0.0", - "r0", "riscv 0.8.0", "riscv-rt", + "xtensa-lx", + "xtensa-lx-rt", ] [[package]] name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid 0.1.0", -] - -[[package]] -name = "syn" -version = "1.0.86" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", - "unicode-xid 0.2.2", + "proc-macro2", + "quote", + "unicode-ident", ] -[[package]] -name = "termtree" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" - [[package]] name = "typenum" version = "1.15.0" @@ -792,16 +715,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - -[[package]] -name = "unicode-xid" -version = "0.2.2" +name = "unicode-ident" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" [[package]] name = "vcell" @@ -831,29 +748,37 @@ dependencies = [ ] [[package]] -name = "winapi" -version = "0.3.9" +name = "xtensa-lx" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "b874b2b60f9c25682e0961fd53a802053e6950f7567bc6f2d6c734fb6d93f45a" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "bare-metal 1.0.0", + "mutex-trait", + "r0", + "spin", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "xtensa-lx-rt" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "33a17a83d25998da4f2dca85afb6456ea340d3bde0211bebac011eb45dcbd2f4" +dependencies = [ + "bare-metal 1.0.0", + "core-isa-parser", + "minijinja", + "r0", + "xtensa-lx-rt-proc-macros", +] [[package]] -name = "yansi" -version = "0.5.0" +name = "xtensa-lx-rt-proc-macros" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" +checksum = "21a8200930e2dbd515c231f7a46033bd6dfe1497a8e9a539878f0de8f0cd730b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 1d99574..95a6ac7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,26 +5,28 @@ edition = "2021" [dependencies] heapless = "0.7.10" +esp-backtrace = { version = "0.2.0", features = ["panic-handler", "exception-handler", "print-uart"], optional = true } mockall_double = "0.1.0" md-5 = { version = "0.10.1", default-features = false } -[target.'cfg(target_arch = "riscv32")'.dependencies] -esp32c3-hal = { path = "../esp-hal/esp32c3-hal" } -esp-hal-common = { path = "../esp-hal/esp-hal-common" } -riscv-rt = "0.8.1" nb = "1.0.0" -embedded-hal = "0.2.7" -r0 = "1.0.0" -bare-metal = "1.0.0" +esp-hal-common = { package = "esp-hal-common", git = "https://github.com/esp-rs/esp-hal" } +esp32-hal = { package = "esp32-hal", git = "https://github.com/esp-rs/esp-hal", optional = true } +esp32c3-hal = { package = "esp32c3-hal", git = "https://github.com/esp-rs/esp-hal", optional = true } + +[target.'cfg(target_arch = "xtensa")'.dependencies] +xtensa-lx = "0.7.0" +xtensa-lx-rt = "0.13.0" +[target.'cfg(target_arch = "riscv32")'.dependencies] +riscv-rt = "0.9.0" riscv = "0.8.0" -heapless = "0.7.14" -[dev-dependencies] -assert2 = "0.3.6" -matches = "0.1.9" -mockall = "0.11.0" +[features] +esp32 = [ "esp32-hal", "esp-backtrace", "esp-backtrace?/esp32" ] +esp32c3 = [ "esp32c3-hal", "esp-backtrace", "esp-backtrace?/esp32c3" ] [profile.release] -# strip = true # Automatically strip symbols from the binary. -# opt-level = "z" -# lto = true -# panic = "abort" \ No newline at end of file +strip = true # Automatically strip symbols from the binary. +opt-level = "z" +codegen-units = 1 +lto = true +panic = "abort" \ No newline at end of file diff --git a/README.md b/README.md index 0c4c7d8..b8b0ca0 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,16 @@ # esp-flasher-stub Rust implementation of flasher stub located in esptool. -Currently only supports ESP32C3 through UART. +Currently only supports ESP32C3 and ESP32 through UART. ## Build -cargo build +``` + cargo build --features esp32 --target xtensa-esp32-none-elf --release +``` + or +``` + cargo build --features esp32c3 --target riscv32imc-unknown-none-elf --release +``` ## Test cargo test --target=x86_64-unknown-linux-gnu @@ -14,7 +20,7 @@ Since esptool uses precompiled stub binaries located in `stub_flasher.py`, binary for ESP32C3 has to be replaced the one otained from `esp-flasher-stub`. In order to run `test_espttol.py` follow steps below: -* Build `esp-flasher-stub` with `cargo build --release` +* Build `esp-flasher-stub` as described in build section above. * Clone esptool to the same directory where `esp-flasher-stub` resides. ``` git clone https://github.com/espressif/esptool @@ -27,5 +33,4 @@ git am ../../esp-flasher-stub/esptool.patch * Regenerate `stub_flasher.py` by running patched Makefile and run the tests ``` make -C ../flasher_stub/ && python test_esptool.py /dev/ttyUSB0 esp32c3 115200 -``` -This last step requires toolchain for ESP8266 to be installed and IFD exported. \ No newline at end of file +``` \ No newline at end of file diff --git a/build.rs b/build.rs index 198f91e..f81fc53 100644 --- a/build.rs +++ b/build.rs @@ -4,11 +4,27 @@ fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); println!("cargo:rustc-link-search)={}", out_dir.display()); - fs::copy("ld/stub.x", out_dir.join("stub.x")).unwrap(); - println!("cargo:rerun-if-changed=ld/stub.x"); - println!("cargo:rustc-link-arg=-Tld/stub.x"); + #[cfg(feature = "esp32c3")] + { + fs::copy("ld/esp32c3_stub.x", out_dir.join("esp32c3_stub.x")).unwrap(); + println!("cargo:rerun-if-changed=ld/esp32c3_stub.x"); + println!("cargo:rustc-link-arg=-Tld/esp32c3_stub.x"); - fs::copy("ld/rom.x", out_dir.join("rom.x")).unwrap(); - println!("cargo:rerun-if-changed=ld/ld/rom.x"); - println!("cargo:rustc-link-arg=-Tld/rom.x"); + fs::copy("ld/esp32c3_rom.x", out_dir.join("esp32c3_rom.x")).unwrap(); + println!("cargo:rerun-if-changed=ld/ld/esp32c3_rom.x"); + println!("cargo:rustc-link-arg=-Tld/esp32c3_rom.x"); + + println!("cargo:rustc-link-arg=-Thal-defaults.x"); + } + + #[cfg(feature = "esp32")] + { + fs::copy("ld/esp32_stub.x", out_dir.join("esp32_stub.x")).unwrap(); + println!("cargo:rerun-if-changed=ld/esp32_stub.x"); + println!("cargo:rustc-link-arg=-Tld/esp32_stub.x"); + + fs::copy("ld/esp32_rom.x", out_dir.join("esp32_rom.x")).unwrap(); + println!("cargo:rerun-if-changed=ld/ld/esp32_rom.x"); + println!("cargo:rustc-link-arg=-Tld/esp32_rom.x"); + } } diff --git a/ld/esp32_rom.x b/ld/esp32_rom.x new file mode 100644 index 0000000..d011632 --- /dev/null +++ b/ld/esp32_rom.x @@ -0,0 +1,18 @@ +PROVIDE( esp_rom_spiflash_erase_chip = 0x40062c14 ); +PROVIDE( esp_rom_spiflash_erase_block = 0x40062c4c ); +PROVIDE( esp_rom_spiflash_erase_sector = 0x40062ccc ); +PROVIDE( esp_rom_spiflash_write = 0x40062d50 ); +PROVIDE( esp_rom_spiflash_read = 0x40062ed8 ); +PROVIDE( esp_rom_spiflash_write_encrypted = 0x40062e78 ); +PROVIDE( esp_rom_spiflash_write_encrypted_enable = 0x40062df4 ); +PROVIDE( esp_rom_spiflash_write_encrypted_disable = 0x40062e60 ); +PROVIDE( esp_rom_spiflash_attach = 0x40062a6c ); +PROVIDE( esp_rom_spiflash_config_param = 0x40063238 ); +PROVIDE( uart_tx_one_char = 0x40009200 ); +PROVIDE( uart_div_modify = 0x400090cc ); +PROVIDE( ets_efuse_get_spiconfig = 0x40008658 ); +PROVIDE( software_reset = 0x4000824c ); +PROVIDE( ets_delay_us = 0x40008534 ); +PROVIDE( tinfl_decompress = 0x4005ef30 ); +PROVIDE ( spi_read_status_high = 0x40062448 ); +PROVIDE ( spi_write_status = 0x400622f0 ); \ No newline at end of file diff --git a/ld/esp32_stub.x b/ld/esp32_stub.x new file mode 100644 index 0000000..8f71290 --- /dev/null +++ b/ld/esp32_stub.x @@ -0,0 +1,353 @@ + +/* before memory.x to allow override */ +ENTRY(Reset) + +/* This memory map assumes the flash cache is on; + the blocks used are excluded from the various memory ranges + + see: https://github.com/espressif/esp-idf/blob/5b1189570025ba027f2ff6c2d91f6ffff3809cc2/components/heap/port/esp32/memory_layout.c + for details + */ + +/* override entry point */ +ENTRY(ESP32Reset) + +/* reserved at the start of DRAM for e.g. the BT stack */ +RESERVE_DRAM = 0x0; + +/* reserved at the start of the RTC memories for use by the ULP processor */ +RESERVE_RTC_FAST = 0; +RESERVE_RTC_SLOW = 0; + +/* define stack size for both cores */ +STACK_SIZE = 32k; + +/* Specify main memory areas */ +MEMORY +{ + reserved_cache_seg : ORIGIN = 0x40070000, len = 64k /* SRAM0; reserved for usage as flash cache*/ + iram_seg ( RX ) : ORIGIN = 0x40080000, len = 128k /* SRAM0 */ + + reserved_for_rom_seg : ORIGIN = 0x3FFAE000, len = 8k /* SRAM2; reserved for usage by the ROM */ + dram_seg ( RW ) : ORIGIN = 0x3FFB0000 + RESERVE_DRAM, len = 176k - RESERVE_DRAM /* SRAM2+1; first 64kB used by BT if enable */ + reserved_for_boot_seg : ORIGIN = 0x3FFDC200, len = 144k /* SRAM1; reserved for static ROM usage; can be used for heap */ + + /* external flash + The 0x20 offset is a convenience for the app binary image generation. + Flash cache has 64KB pages. The .bin file which is flashed to the chip + has a 0x18 byte file header, and each segment has a 0x08 byte segment + header. Setting this offset makes it simple to meet the flash cache MMU's + constraint that (paddr % 64KB == vaddr % 64KB).) + */ + irom_seg ( RX ) : ORIGIN = 0x400D0020, len = 3M - 0x20 + drom_seg ( R ) : ORIGIN = 0x3F400020, len = 4M - 0x20 + + /* RTC fast memory (executable). Persists over deep sleep. Only for core 0 (PRO_CPU) */ + rtc_fast_iram_seg(RWX) : ORIGIN = 0x400C0000, len = 8k + + /* RTC fast memory (same block as above), viewed from data bus. Only for core 0 (PRO_CPU) */ + rtc_fast_dram_seg(RW) : ORIGIN = 0x3FF80000 + RESERVE_RTC_FAST, len = 8k - RESERVE_RTC_FAST + + /* RTC slow memory (data accessible). Persists over deep sleep. */ + rtc_slow_seg(RW) : ORIGIN = 0x50000000 + RESERVE_RTC_SLOW, len = 8k - RESERVE_RTC_SLOW +} + +/* map generic regions to output sections */ +REGION_ALIAS("ROTEXT", irom_seg); +REGION_ALIAS("RWTEXT", iram_seg); +REGION_ALIAS("RODATA", drom_seg); +REGION_ALIAS("RWDATA", dram_seg); + +/* esp32 specific regions */ +SECTIONS { + .rtc_fast.text : { + . = ALIGN(4); + *(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*) + } > rtc_fast_iram_seg AT > RODATA + + /* + This section is required to skip rtc.text area because rtc_iram_seg and + rtc_data_seg are reflect the same address space on different buses. + */ + .rtc_fast.dummy (NOLOAD) : + { + _rtc_dummy_start = ABSOLUTE(.); /* needed to make section proper size */ + . = SIZEOF(.rtc_fast.text); + _rtc_dummy_end = ABSOLUTE(.); /* needed to make section proper size */ + } > rtc_fast_dram_seg + + + .rtc_fast.data : + { + . = ALIGN(4); + _rtc_fast_data_start = ABSOLUTE(.); + *(.rtc_fast.data .rtc_fast.data.*) + _rtc_fast_data_end = ABSOLUTE(.); + } > rtc_fast_dram_seg AT > RODATA + + .rtc_fast.bss (NOLOAD) : + { + . = ALIGN(4); + _rtc_fast_bss_start = ABSOLUTE(.); + *(.rtc_fast.bss .rtc_fast.bss.*) + _rtc_fast_bss_end = ABSOLUTE(.); + } > rtc_fast_dram_seg + + .rtc_fast.noinit (NOLOAD) : + { + . = ALIGN(4); + *(.rtc_fast.noinit .rtc_fast.noinit.*) + } > rtc_fast_dram_seg + + + .rtc_slow.text : { + . = ALIGN(4); + *(.rtc_slow.literal .rtc_slow.text .rtc_slow.literal.* .rtc_slow.text.*) + } > rtc_slow_seg AT > RODATA + + .rtc_slow.data : + { + . = ALIGN(4); + _rtc_slow_data_start = ABSOLUTE(.); + *(.rtc_slow.data .rtc_slow.data.*) + _rtc_slow_data_end = ABSOLUTE(.); + } > rtc_slow_seg AT > RODATA + + .rtc_slow.bss (NOLOAD) : + { + . = ALIGN(4); + _rtc_slow_bss_start = ABSOLUTE(.); + *(.rtc_slow.bss .rtc_slow.bss.*) + _rtc_slow_bss_end = ABSOLUTE(.); + } > rtc_slow_seg + + .rtc_slow.noinit (NOLOAD) : + { + . = ALIGN(4); + *(.rtc_slow.noinit .rtc_slow.noinit.*) + } > rtc_slow_seg +} + +_heap_end = ABSOLUTE(ORIGIN(dram_seg))+LENGTH(dram_seg)+LENGTH(reserved_for_boot_seg) - 2*STACK_SIZE; +_text_heap_end = ABSOLUTE(ORIGIN(iram_seg)+LENGTH(iram_seg)); +_external_heap_end = ABSOLUTE(ORIGIN(psram_seg)+LENGTH(psram_seg)); + +_stack_start_cpu1 = _heap_end; +_stack_end_cpu1 = _stack_start_cpu1 + STACK_SIZE; +_stack_start_cpu0 = _stack_end_cpu1; +_stack_end_cpu0 = _stack_start_cpu0 + STACK_SIZE; + +EXTERN(DefaultHandler); + +EXTERN(WIFI_EVENT); /* Force inclusion of WiFi libraries */ + +PROVIDE(WIFI_MAC = DefaultHandler); +PROVIDE(WIFI_NMI = DefaultHandler); +PROVIDE(WIFI_BB = DefaultHandler); +PROVIDE(BT_MAC = DefaultHandler); +PROVIDE(BT_BB = DefaultHandler); +PROVIDE(BT_BB_NMI = DefaultHandler); +PROVIDE(RWBT = DefaultHandler); +PROVIDE(RWBLE = DefaultHandler); +PROVIDE(RWBT_NMI = DefaultHandler); +PROVIDE(RWBLE_NMI = DefaultHandler); +PROVIDE(UHCI0 = DefaultHandler); +PROVIDE(UHCI1 = DefaultHandler); +PROVIDE(TG0_T0_LEVEL = DefaultHandler); +PROVIDE(TG0_T1_LEVEL = DefaultHandler); +PROVIDE(TG0_WDT_LEVEL = DefaultHandler); +PROVIDE(TG0_LACT_LEVEL = DefaultHandler); +PROVIDE(TG1_T0_LEVEL = DefaultHandler); +PROVIDE(TG1_T1_LEVEL = DefaultHandler); +PROVIDE(TG1_WDT_LEVEL = DefaultHandler); +PROVIDE(TG1_LACT_LEVEL = DefaultHandler); +PROVIDE(GPIO = DefaultHandler); +PROVIDE(GPIO_NMI = DefaultHandler); +PROVIDE(SPI0 = DefaultHandler); +PROVIDE(SPI1 = DefaultHandler); +PROVIDE(SPI2 = DefaultHandler); +PROVIDE(SPI3 = DefaultHandler); +PROVIDE(I2S0 = DefaultHandler); +PROVIDE(I2S1 = DefaultHandler); +PROVIDE(UART0 = DefaultHandler); +PROVIDE(UART1 = DefaultHandler); +PROVIDE(UART2 = DefaultHandler); +PROVIDE(PWM0 = DefaultHandler); +PROVIDE(PWM1 = DefaultHandler); +PROVIDE(LEDC = DefaultHandler); +PROVIDE(EFUSE = DefaultHandler); +PROVIDE(TWAI = DefaultHandler); +PROVIDE(RTC_CORE = DefaultHandler); +PROVIDE(RMT = DefaultHandler); +PROVIDE(PCNT = DefaultHandler); +PROVIDE(I2C_EXT0 = DefaultHandler); +PROVIDE(I2C_EXT1 = DefaultHandler); +PROVIDE(RSA = DefaultHandler); +PROVIDE(SPI1_DMA = DefaultHandler); +PROVIDE(SPI2_DMA = DefaultHandler); +PROVIDE(SPI3_DMA = DefaultHandler); +PROVIDE(TIMER1 = DefaultHandler); +PROVIDE(TIMER2 = DefaultHandler); +PROVIDE(TG0_T0_EDGE = DefaultHandler); +PROVIDE(TG0_T1_EDGE = DefaultHandler); +PROVIDE(TG0_WDT_EDGE = DefaultHandler); +PROVIDE(TG0_LACT_EDGE = DefaultHandler); +PROVIDE(TG1_T0_EDGE = DefaultHandler); +PROVIDE(TG1_T1_EDGE = DefaultHandler); +PROVIDE(TG1_WDT_EDGE = DefaultHandler); +PROVIDE(TG1_LACT_EDGE = DefaultHandler); + + +/* after memory.x to allow override */ +PROVIDE(__pre_init = DefaultPreInit); +PROVIDE(__zero_bss = default_mem_hook); +PROVIDE(__init_data = default_mem_hook); + +/* exception vector for the ESP32, requiring high priority interrupts and register window support */ + +/* high level exception/interrupt routines, which can be override with Rust functions */ +PROVIDE(__exception = __default_exception); +PROVIDE(__user_exception = __default_user_exception); +PROVIDE(__double_exception = __default_double_exception); +PROVIDE(__level_1_interrupt = __default_interrupt); +PROVIDE(__level_2_interrupt = __default_interrupt); +PROVIDE(__level_3_interrupt = __default_interrupt); +PROVIDE(__level_4_interrupt = __default_interrupt); +PROVIDE(__level_5_interrupt = __default_interrupt); +PROVIDE(__level_6_interrupt = __default_interrupt); +PROVIDE(__level_7_interrupt = __default_interrupt); + +/* high level CPU interrupts */ +PROVIDE(Timer0 = __default_user_exception); +PROVIDE(Timer1 = __default_user_exception); +PROVIDE(Timer2 = __default_user_exception); +PROVIDE(Timer3 = __default_user_exception); +PROVIDE(Profiling = __default_user_exception); +PROVIDE(NMI = __default_user_exception); +PROVIDE(Software0 = __default_user_exception); +PROVIDE(Software1 = __default_user_exception); + +/* low level exception/interrupt, which must be overridden using naked functions */ +PROVIDE(__naked_user_exception = __default_naked_exception); +PROVIDE(__naked_kernel_exception = __default_naked_exception); +PROVIDE(__naked_double_exception = __default_naked_double_exception); +PROVIDE(__naked_level_2_interrupt = __default_naked_level_2_interrupt); +PROVIDE(__naked_level_3_interrupt = __default_naked_level_3_interrupt); +PROVIDE(__naked_level_4_interrupt = __default_naked_level_4_interrupt); +PROVIDE(__naked_level_5_interrupt = __default_naked_level_5_interrupt); +PROVIDE(__naked_level_6_interrupt = __default_naked_level_6_interrupt); +PROVIDE(__naked_level_7_interrupt = __default_naked_level_7_interrupt); + + +/* needed to force inclusion of the vectors */ +EXTERN(__default_exception); +EXTERN(__default_double_exception); +EXTERN(__default_interrupt); + +EXTERN(__default_naked_exception); +EXTERN(__default_naked_double_exception); +EXTERN(__default_naked_level_2_interrupt); +EXTERN(__default_naked_level_3_interrupt); +EXTERN(__default_naked_level_4_interrupt); +EXTERN(__default_naked_level_5_interrupt); +EXTERN(__default_naked_level_6_interrupt); +EXTERN(__default_naked_level_7_interrupt); + + +SECTIONS { + .text : ALIGN(4) + { + /* Vector table */ + . = 0x0; + _init_start = ABSOLUTE(.); + . = 0x00000000 ; + KEEP(*(.WindowOverflow4.text)); + . = 0x00000040; + KEEP(*(.WindowUnderflow4.text)); + . = 0x00000080; + KEEP(*(.WindowOverflow8.text)); + . = 0x000000C0; + KEEP(*(.WindowUnderflow8.text)); + . = 0x00000100; + KEEP(*(.WindowOverflow12.text)); + . = 0x00000140; + KEEP(*(.WindowUnderflow12.text)); + . = 0x00000180; + KEEP(*(.Level2InterruptVector.text)); + . = 0x000001C0; + KEEP(*(.Level3InterruptVector.text)); + . = 0x00000200; + KEEP(*(.Level4InterruptVector.text)); + . = 0x00000240; + KEEP(*(.Level5InterruptVector.text)); + . = 0x00000280; + KEEP(*(.DebugExceptionVector.text)); + . = 0x000002C0; + KEEP(*(.NMIExceptionVector.text)); + . = 0x00000300; + KEEP(*(.KernelExceptionVector.text)); + . = 0x00000340; + KEEP(*(.UserExceptionVector.text)); + . = 0x000003C0; + KEEP(*(.DoubleExceptionVector.text)); + . = 0x400; + _init_end = ABSOLUTE(.); + + _stext = .; + . = ALIGN (4); + _text_start = ABSOLUTE(.); + . = ALIGN (4); + *(.literal .text .literal.* .text.*) + _text_end = ABSOLUTE(.); + _etext = .; + *(.rwtext.literal .rwtext .rwtext.literal.* .rwtext.*) + } > RWTEXT + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + . = ALIGN (4); + *(.data .data.*) + *(.rodata .rodata.*) + _data_end = ABSOLUTE(.); + } > RWDATA AT > RWTEXT + + /* LMA of .data */ + _sidata = LOADADDR(.data); + + .bss (NOLOAD) : ALIGN(4) + { + _bss_start = ABSOLUTE(.); + . = ALIGN (4); + *(.bss .bss.* COMMON) + _bss_end = ABSOLUTE(.); + } > RWDATA + + .noinit (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + *(.noinit .noinit.*) + } > RWDATA + + /* must be last segment using RWTEXT */ + .text_heap_start (NOLOAD) : ALIGN(4) + { + . = ALIGN (4); + _text_heap_start = ABSOLUTE(.); + } > RWTEXT + + /* must be last segment using RWDATA */ + .heap_start (NOLOAD) : ALIGN(4) + { + . = ALIGN (4); + _heap_start = ABSOLUTE(.); + } > RWDATA +} + +PROVIDE(level1_interrupt = DefaultHandler); +PROVIDE(level2_interrupt = DefaultHandler); +PROVIDE(level3_interrupt = DefaultHandler); +PROVIDE(level4_interrupt = DefaultHandler); +PROVIDE(level5_interrupt = DefaultHandler); +PROVIDE(level6_interrupt = DefaultHandler); +PROVIDE(level7_interrupt = DefaultHandler); diff --git a/ld/esp32c3_rom.x b/ld/esp32c3_rom.x new file mode 100644 index 0000000..5208f14 --- /dev/null +++ b/ld/esp32c3_rom.x @@ -0,0 +1,20 @@ +PROVIDE( esp_rom_spiflash_erase_chip = 0x40000120 ); +PROVIDE( esp_rom_spiflash_erase_block = 0x40000124 ); +PROVIDE( esp_rom_spiflash_erase_sector = 0x40000128 ); +PROVIDE( esp_rom_spiflash_write = 0x4000012c ); +PROVIDE( esp_rom_spiflash_read = 0x40000130 ); +PROVIDE( esp_rom_spiflash_unlock = 0x40000140 ); +PROVIDE( esp_rom_spiflash_attach = 0x40000164 ); +PROVIDE( esp_rom_spiflash_config_param = 0x40000134 ); +PROVIDE( uart_tx_one_char = 0x40000068 ); +PROVIDE( uart_div_modify = 0x40000088 ); +PROVIDE( ets_efuse_get_spiconfig = 0x4000071c ); +PROVIDE( software_reset = 0x40000090 ); +PROVIDE( ets_delay_us = 0x40000050 ); +PROVIDE( tinfl_decompress = 0x400000f4 ); +PROVIDE( get_security_info_proc = 0x4004b9da ); +PROVIDE( esp_rom_spiflash_write_encrypted_enable = 0x40000118 ); +PROVIDE( esp_rom_spiflash_write_encrypted_disable = 0x4000011c ); +PROVIDE( esp_rom_spiflash_write_encrypted = 0x40000110 ); +PROVIDE( spi_read_status_high = 0x4000015c ); +PROVIDE( spi_write_status = 0x40000160 ); \ No newline at end of file diff --git a/ld/stub.x b/ld/esp32c3_stub.x similarity index 77% rename from ld/stub.x rename to ld/esp32c3_stub.x index b6b8739..a64530f 100644 --- a/ld/stub.x +++ b/ld/esp32c3_stub.x @@ -44,7 +44,7 @@ REGION_ALIAS("REGION_RWTEXT", IRAM); REGION_ALIAS("REGION_RTC_FAST", RTC_FAST); -ENTRY(_start_hal) /* _start_hal */ +ENTRY(_start_hal) PROVIDE(_start_trap = _start_trap_hal); PROVIDE(_stext = ORIGIN(REGION_TEXT)); @@ -87,7 +87,6 @@ PROVIDE(_mp_hook = default_mp_hook); By default uses the riscv crates default trap handler but by providing the `_start_trap` symbol external crates can override. */ -PROVIDE(_start_trap = default_start_trap); SECTIONS { @@ -100,15 +99,16 @@ SECTIONS .text : ALIGN(4) { _irwtext = LOADADDR(.text); _srwtext = .; - *(.text); + *(.init.literal .literal .text .init.literal.* .literal.* .text.* .rwtext .rwtext.*) . = ALIGN(4); KEEP(*(.init)); KEEP(*(.init.rust)); - . = ALIGN(4); - (*(.trap)); - (*(.trap.rust)); - *(.text .text.*); + KEEP(*(.trap)); + KEEP(*(.trap.rust)); + + *(.iram1) + *(.iram1.*) _erwtext = .; } > REGION_RWTEXT @@ -128,6 +128,8 @@ SECTIONS *(.sdata .sdata.* .sdata2 .sdata2.*); *(.data .data.*); *(.rodata .rodata.*); + *(.data1) + *(.srodata .srodata.*); . = ALIGN(4); _edata = .; } > REGION_DATA @@ -184,51 +186,6 @@ SECTIONS .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } } -/* Do not exceed this mark in the error messages above | */ -ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " -ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); - -ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, " -ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned"); - -ASSERT(ORIGIN(REGION_DATA) % 4 == 0, " -ERROR(riscv-rt): the start of the REGION_DATA must be 4-byte aligned"); - -ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, " -ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned"); - -ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " -ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); - -ASSERT(ORIGIN(REGION_STACK) % 4 == 0, " -ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned"); - -ASSERT(_stext % 4 == 0, " -ERROR(riscv-rt): `_stext` must be 4-byte aligned"); - -ASSERT(_sdata % 4 == 0 && _edata % 4 == 0, " -BUG(riscv-rt): .data is not 4-byte aligned"); - -ASSERT(_sidata % 4 == 0, " -BUG(riscv-rt): the LMA of .data is not 4-byte aligned"); - -ASSERT(_sbss % 4 == 0 && _ebss % 4 == 0, " -BUG(riscv-rt): .bss is not 4-byte aligned"); - -ASSERT(_sheap % 4 == 0, " -BUG(riscv-rt): start of .heap is not 4-byte aligned"); - -ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " -ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. -Consider changing `_max_hart_id` or `_hart_stack_size`."); - -ASSERT(SIZEOF(.got) == 0, " -.got section detected in the input files. Dynamic relocations are not -supported. If you are linking to C code compiled using the `gcc` crate -then modify your build script to compile the C code _without_ the --fPIC flag. See the documentation of the `gcc::Config.fpic` method for -details."); - /* Do not exceed this mark in the error messages above | */ @@ -263,3 +220,48 @@ PROVIDE(interrupt28 = DefaultHandler); PROVIDE(interrupt29 = DefaultHandler); PROVIDE(interrupt30 = DefaultHandler); PROVIDE(interrupt31 = DefaultHandler); + +PROVIDE(WIFI_MAC = DefaultHandler); +PROVIDE(WIFI_MAC_NMI = DefaultHandler); +PROVIDE(WIFI_PWR = DefaultHandler); +PROVIDE(WIFI_BB = DefaultHandler); +PROVIDE(BT_MAC = DefaultHandler); +PROVIDE(BT_BB = DefaultHandler); +PROVIDE(BT_BB_NMI = DefaultHandler); +PROVIDE(RWBT = DefaultHandler); +PROVIDE(RWBLE = DefaultHandler); +PROVIDE(RWBT_NMI = DefaultHandler); +PROVIDE(RWBLE_NMI = DefaultHandler); +PROVIDE(UHCI0 = DefaultHandler); +PROVIDE(GPIO = DefaultHandler); +PROVIDE(GPIO_NMI = DefaultHandler); +PROVIDE(SPI2 = DefaultHandler); +PROVIDE(I2S = DefaultHandler); +PROVIDE(UART0 = DefaultHandler); +PROVIDE(UART1 = DefaultHandler); +PROVIDE(LEDC = DefaultHandler); +PROVIDE(EFUSE = DefaultHandler); +PROVIDE(TWAI = DefaultHandler); +PROVIDE(USB_SERIAL_JTAG = DefaultHandler); +PROVIDE(RTC_CORE = DefaultHandler); +PROVIDE(RMT = DefaultHandler); +PROVIDE(I2C_EXT0 = DefaultHandler); +PROVIDE(TG0_T0_LEVEL = DefaultHandler); +PROVIDE(TG0_WDT_LEVEL = DefaultHandler); +PROVIDE(TG1_T0_LEVEL = DefaultHandler); +PROVIDE(TG1_WDT_LEVEL = DefaultHandler); +PROVIDE(SYSTIMER_TARGET0 = DefaultHandler); +PROVIDE(SYSTIMER_TARGET1 = DefaultHandler); +PROVIDE(SYSTIMER_TARGET2 = DefaultHandler); +PROVIDE(APB_ADC = DefaultHandler); +PROVIDE(DMA_CH0 = DefaultHandler); +PROVIDE(DMA_CH1 = DefaultHandler); +PROVIDE(DMA_CH2 = DefaultHandler); +PROVIDE(RSA = DefaultHandler); +PROVIDE(AES = DefaultHandler); +PROVIDE(SHA = DefaultHandler); +PROVIDE(SW_INTR_0 = DefaultHandler); +PROVIDE(SW_INTR_1 = DefaultHandler); +PROVIDE(SW_INTR_2 = DefaultHandler); +PROVIDE(SW_INTR_3 = DefaultHandler); +PROVIDE(ASSIST_DEBUG = DefaultHandler); \ No newline at end of file diff --git a/ld/rom.x b/ld/rom.x deleted file mode 100644 index 3d512a0..0000000 --- a/ld/rom.x +++ /dev/null @@ -1,83 +0,0 @@ - -PROVIDE( esp_rom_spiflash_wait_idle = 0x4000010c ); -PROVIDE( esp_rom_spiflash_write_encrypted = 0x40000110 ); -PROVIDE( esp_rom_spiflash_write_encrypted_dest = 0x40000114 ); -PROVIDE( esp_rom_spiflash_write_encrypted_enable = 0x40000118 ); -PROVIDE( esp_rom_spiflash_write_encrypted_disable = 0x4000011c ); -PROVIDE( esp_rom_spiflash_erase_chip = 0x40000120 ); -PROVIDE( esp_rom_spiflash_erase_block = 0x40000124 ); -PROVIDE( esp_rom_spiflash_erase_sector = 0x40000128 ); -PROVIDE( esp_rom_spiflash_write = 0x4000012c ); -PROVIDE( esp_rom_spiflash_read = 0x40000130 ); -PROVIDE( esp_rom_spiflash_config_param = 0x40000134 ); -PROVIDE( esp_rom_spiflash_read_user_cmd = 0x40000138 ); -PROVIDE( esp_rom_spiflash_select_qio_pins = 0x4000013c ); -PROVIDE( esp_rom_spiflash_unlock = 0x40000140 ); -PROVIDE( esp_rom_spi_flash_auto_sus_res = 0x40000144 ); -PROVIDE( esp_rom_spi_flash_send_resume = 0x40000148 ); -PROVIDE( esp_rom_spi_flash_update_id = 0x4000014c ); -PROVIDE( esp_rom_spiflash_config_clk = 0x40000150 ); -PROVIDE( esp_rom_spiflash_config_readmode = 0x40000154 ); -PROVIDE( esp_rom_spiflash_read_status = 0x40000158 ); -PROVIDE( esp_rom_spiflash_read_statushigh = 0x4000015c ); -PROVIDE( esp_rom_spiflash_write_status = 0x40000160 ); -PROVIDE( esp_rom_spiflash_attach = 0x40000164 ); -PROVIDE( spi_flash_get_chip_size = 0x40000168 ); -PROVIDE( spi_flash_guard_set = 0x4000016c ); -PROVIDE( spi_flash_guard_get = 0x40000170 ); -PROVIDE( spi_flash_write_config_set = 0x40000174 ); -PROVIDE( spi_flash_write_config_get = 0x40000178 ); -PROVIDE( spi_flash_safe_write_address_func_set = 0x4000017c ); -PROVIDE( spi_flash_unlock = 0x40000180 ); -PROVIDE( spi_flash_erase_range = 0x40000184 ); -PROVIDE( spi_flash_erase_sector = 0x40000188 ); -PROVIDE( spi_flash_write = 0x4000018c ); -PROVIDE( spi_flash_read = 0x40000190 ); -PROVIDE( spi_flash_write_encrypted = 0x40000194 ); -PROVIDE( spi_flash_read_encrypted = 0x40000198 ); -PROVIDE( spi_flash_mmap_os_func_set = 0x4000019c ); -PROVIDE( spi_flash_mmap_page_num_init = 0x400001a0 ); -PROVIDE( spi_flash_mmap = 0x400001a4 ); -PROVIDE( spi_flash_mmap_pages = 0x400001a8 ); -PROVIDE( spi_flash_munmap = 0x400001ac ); -PROVIDE( spi_flash_mmap_dump = 0x400001b0 ); -PROVIDE( spi_flash_check_and_flush_cache = 0x400001b4 ); -PROVIDE( spi_flash_mmap_get_free_pages = 0x400001b8 ); -PROVIDE( spi_flash_cache2phys = 0x400001bc ); -PROVIDE( spi_flash_phys2cache = 0x400001c0 ); -PROVIDE( spi_flash_disable_cache = 0x400001c4 ); -PROVIDE( spi_flash_restore_cache = 0x400001c8 ); -PROVIDE( spi_flash_cache_enabled = 0x400001cc ); -PROVIDE( spi_flash_enable_cache = 0x400001d0 ); -PROVIDE( spi_cache_mode_switch = 0x400001d4 ); -PROVIDE( spi_common_set_dummy_output = 0x400001d8 ); -PROVIDE( spi_common_set_flash_cs_timing = 0x400001dc ); -PROVIDE( esp_enable_cache_flash_wrap = 0x400001e0 ); -PROVIDE( SPIEraseArea = 0x400001e4 ); -PROVIDE( SPILock = 0x400001e8 ); -PROVIDE( SPIMasterReadModeCnfig = 0x400001ec ); -PROVIDE( SPI_Common_Command = 0x400001f0 ); -PROVIDE( SPI_WakeUp = 0x400001f4 ); -PROVIDE( SPI_block_erase = 0x400001f8 ); -PROVIDE( SPI_chip_erase = 0x400001fc ); -PROVIDE( SPI_init = 0x40000200 ); -PROVIDE( SPI_page_program = 0x40000204 ); -PROVIDE( SPI_read_data = 0x40000208 ); -PROVIDE( SPI_sector_erase = 0x4000020c ); -PROVIDE( SPI_write_enable = 0x40000210 ); -PROVIDE( SelectSpiFunction = 0x40000214 ); -PROVIDE( SetSpiDrvs = 0x40000218 ); -PROVIDE( Wait_SPI_Idle = 0x4000021c ); -PROVIDE( spi_dummy_len_fix = 0x40000220 ); -PROVIDE( Disable_QMode = 0x40000224 ); -PROVIDE( Enable_QMode = 0x40000228 ); -PROVIDE( ets_efuse_get_spiconfig = 0x4000071c ); -PROVIDE( uart_tx_one_char = 0x40000068 ); -PROVIDE( uart_div_modify = 0x40000088 ); -PROVIDE( ets_get_apb_freq = 0x40000580 ); -PROVIDE( ets_get_cpu_frequency = 0x40000584 ); -PROVIDE( software_reset = 0x40000090 ); -PROVIDE( ets_delay_us = 0x40000050 ); -PROVIDE( tinfl_decompress = 0x400000f4 ); -PROVIDE( GetSecurityInfoProc = 0x4004b9da ); -/* Do not exceed this mark in the error messages above | */ diff --git a/src/commands.rs b/src/commands.rs index 1de91fb..2d4bf35 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -84,11 +84,19 @@ pub struct DataCommand { #[derive(PartialEq, Copy, Clone)] #[repr(C, packed(1))] -pub struct EndCommand { +pub struct EndFlashCommand { pub base: CommandBase, pub run_user_code: u32, } +#[derive(PartialEq, Copy, Clone)] +#[repr(C, packed(1))] +pub struct MemEndCommand { + pub base: CommandBase, + pub stay_in_stub: u32, + pub entrypoint: fn(), +} + #[derive(PartialEq, Copy, Clone)] #[repr(C, packed(1))] pub struct WriteRegCommand { diff --git a/src/dprint.rs b/src/dprint.rs index 0f03b3b..2f5f53c 100644 --- a/src/dprint.rs +++ b/src/dprint.rs @@ -4,86 +4,17 @@ //! This is unsafe! It is asynchronous with normal UART1 usage and //! interrupts are not disabled. -use esp32c3_hal::pac::UART1; -use esp_hal_common::{ - OutputPin, - types::OutputSignal, -}; pub struct DebugLog {} pub enum Error {} -use esp32c3_hal::pac::{ system, uart0 }; - -static mut SCLK_SEL: u8 = 0; - -fn div_up(a: u32, b: u32) -> u32{ - (a + b - 1) / b -} - -fn get_sclk_freq(uart: &uart0::RegisterBlock) -> u32 -{ - const APB_CLK_FREQ: u32 = 80000000; - const RTC_CLK_FREQ: u32 = 20000000; - const XTAL_CLK_FREQ: u32 = 40000000; - - unsafe{ SCLK_SEL = uart.clk_conf.read().sclk_sel().bits(); } - - - match uart.clk_conf.read().sclk_sel().bits() { - 1 => APB_CLK_FREQ, - 2 => RTC_CLK_FREQ, - 3 => XTAL_CLK_FREQ, - _ => XTAL_CLK_FREQ, - } -} - -fn set_baudrate(uart: &uart0::RegisterBlock, baud: u32) -{ - let sclk_freq: u32 = get_sclk_freq(uart); - let max_div: u32 = (1 << 12) - 1; - let sclk_div: u32 = div_up(sclk_freq, max_div * baud); - let clk_div: u32 = ((sclk_freq) << 4) / (baud * sclk_div); - let clk_div_shift = (clk_div >> 4) as u16; - - uart.clkdiv.modify(|_, w| unsafe{ w.clkdiv().bits(clk_div_shift) - .frag().bits((clk_div & 0xf) as u8) } ); - uart.clk_conf.modify(|_, w| unsafe{ w.sclk_div_num().bits((sclk_div - 1) as u8) } ); - } - - pub fn init_debug_uart >( - system: &system::RegisterBlock, - uart: &uart0::RegisterBlock, - mut tx_pin: TxPin, - baudrate: u32) { - - tx_pin.set_to_push_pull_output().connect_peripheral_to_output(OutputSignal::U1TXD); - - system.perip_clk_en0.modify(|_, w| w.uart_mem_clk_en().set_bit() - .uart_clk_en().set_bit()); - system.perip_rst_en0.modify(|_, w| w.uart1_rst().clear_bit()); - - uart.clk_conf.modify(|_, w| w.rst_core().set_bit()); - system.perip_rst_en0.modify(|_, w| w.uart1_rst().set_bit()); - system.perip_rst_en0.modify(|_, w| w.uart1_rst().clear_bit()); - uart.clk_conf.modify(|_, w| w.rst_core().clear_bit()); - uart.id.modify(|_, w| w.reg_update().clear_bit()); - - while uart.id.read().reg_update().bit_is_set() { } - set_baudrate(uart, baudrate); - uart.id.modify(|_, w| w.reg_update().set_bit()); -} - +use esp_hal_common::pac::UART1; impl DebugLog { pub fn count(&mut self) -> u16 { - unsafe { (*UART1::ptr()).status.read().txfifo_cnt().bits() } + unsafe { (*UART1::ptr()).status.read().txfifo_cnt().bits().into() } } - // pub fn is_idle(&mut self) -> bool { - // unsafe { (*UART1::ptr()).status.read().st_utx_out().is_tx_idle() } - // } - fn write(&mut self, word: u8) -> nb::Result<(), Error> { if self.count() < 128 { unsafe{ (*UART1::ptr()).fifo.write(|w| w.rxfifo_rd_byte().bits(word) ) }; diff --git a/src/main.rs b/src/main.rs index 46c1527..a95f2a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ -#![cfg_attr(not(test), no_std)] -#![cfg_attr(not(test), no_main)] #![allow(dead_code)] +#![no_main] +#![no_std] mod protocol; mod commands; @@ -9,54 +9,59 @@ mod miniz_types; mod dprint; mod serial_io; -// #[cfg(not(test))] -mod main { - use riscv_rt::entry; - use core::panic::PanicInfo; - use esp32c3_hal::{ - Serial, - pac, - gpio::IO - }; - use crate::{ - protocol::Stub, - targets::esp32c3 as target, - dprintln, - dprint::*, - serial_io, - }; - - const MSG_BUFFER_SIZE: usize = target::MAX_WRITE_BLOCK + 0x400; +#[cfg(feature = "esp32c3")] +use esp32c3_hal::{ interrupt, IO }; +#[cfg(feature = "esp32")] +use esp32_hal::{ IO }; - #[entry] - fn main() -> ! { - let mut buffer: [u8; MSG_BUFFER_SIZE] = [0; MSG_BUFFER_SIZE]; +#[cfg(any(target_arch = "riscv32"))] +use riscv_rt::entry; +#[cfg(any(target_arch = "xtensa"))] +use xtensa_lx_rt::entry; + +use esp_backtrace as _; +use esp_hal_common::{ + prelude::SystemExt, + clock::ClockControl, + serial::config::Config, + serial::TxRxPins, + Serial, + pac, +}; +use crate::protocol::Stub; - let peripherals = pac::Peripherals::take().unwrap(); - - let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); - init_debug_uart(&peripherals.SYSTEM, &peripherals.UART1, io.pins.gpio10, 921600); - - let mut serial = Serial::new(peripherals.UART0).unwrap(); - - // Must be called after Serial::new, as it disables interrupts - serial_io::enable_uart0_rx_interrupt(); - - let mut stub = Stub::new(&mut serial); - - stub.send_greeting(); - - target::init(); - - loop { - let data = stub.read_command(&mut buffer); - stub.process_command(data); - } - } - #[panic_handler] - fn panic(_info: &PanicInfo) -> ! { - dprintln!("Panic !!!"); - loop {} +#[entry] +fn main() -> ! { + const MSG_BUFFER_SIZE: usize = crate::targets::MAX_WRITE_BLOCK + 0x400; + let mut buffer: [u8; MSG_BUFFER_SIZE] = [0; MSG_BUFFER_SIZE]; + + let peripherals = pac::Peripherals::take().unwrap(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let pins = TxRxPins::new_tx_rx( + io.pins.gpio18.into_push_pull_output(), + io.pins.gpio9.into_floating_input(), + ); + #[cfg(any(target_arch = "xtensa"))] + let system = peripherals.DPORT.split(); + #[cfg(any(target_arch = "riscv32"))] + let system = peripherals.SYSTEM.split(); + let cfg = Config::default().baudrate(921600); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let _ = Serial::new_with_config(peripherals.UART1, Some(cfg), Some(pins), &clocks); + + let mut serial = Serial::new(peripherals.UART0); + + // Must be called after Serial::new, as it disables interrupts + serial_io::enable_uart0_rx_interrupt(); + + let mut stub = Stub::new(&mut serial); + + stub.send_greeting(); + + loop { + let data = stub.read_command(&mut buffer); + stub.process_command(data); } } diff --git a/src/protocol.rs b/src/protocol.rs index 2de5e25..776d5b2 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,28 +1,16 @@ #![allow(dead_code)] -use mockall_double::double; - -#[double] -use crate::targets::esp32c3 as target; - pub trait InputIO { fn recv(&mut self) -> u8; fn send(&mut self, data: &[u8]); } -type FlashFunc = fn(addr: u32, data: *const u8, len: u32) -> Result<(), Error>; - -use target::*; use slip::*; -use core::mem::size_of; -use core::slice; -use core::cmp::min; -use crate::commands::*; -use crate::commands::CommandCode::*; -use crate::commands::Error::*; -use md5::{Md5, Digest}; +use core::{mem::size_of, slice, cmp::min}; +use crate::commands::{*, CommandCode::*, Error::*}; +use crate::targets::{EspCommon, FLASH_SECTOR_MASK, FLASH_BLOCK_SIZE}; use crate::miniz_types::*; -use crate::dprintln; +use md5::{Md5, Digest}; const DATA_CMD_SIZE: usize = size_of::(); const CMD_BASE_SIZE: usize = size_of::(); @@ -39,6 +27,10 @@ pub struct Stub<'a> { decompressor: tinfl_decompressor, last_error: Option, in_flash_mode: bool, + #[cfg(feature = "esp32c3")] + target: crate::targets::Esp32c3, + #[cfg(feature = "esp32")] + target: crate::targets::Esp32, } fn slice_to_struct(slice: &[u8]) -> Result @@ -58,26 +50,10 @@ fn u32_from_slice(slice: &[u8], index: usize) -> u32 { u32::from_le_bytes(slice[index..index+4].try_into().unwrap()) } -fn calculate_md5(mut address: u32, mut size: u32) -> Result<[u8; 16], Error> { - let mut buffer: [u8; FLASH_SECTOR_SIZE as usize] = [0; FLASH_SECTOR_SIZE as usize]; - let mut hasher = Md5::new(); - - while size > 0 { - let to_read = min(size, FLASH_SECTOR_SIZE); - target::spi_flash_read(address, &mut buffer)?; - hasher.update(&buffer[0..to_read as usize]); - size -= to_read; - address += to_read; - } - - let result: [u8; 16] = hasher.finalize().into(); - Ok(result) -} - impl<'a> Stub<'a> { pub fn new(input_io: &'a mut dyn InputIO) -> Self { - Stub { + let stub = Stub { io: input_io, write_addr: 0, end_addr: 0, @@ -87,7 +63,11 @@ impl<'a> Stub<'a> { decompressor: Default::default(), last_error: None, in_flash_mode: false, - } + target: Default::default(), + }; + + stub.target.init(); + stub } fn send_response(&mut self, resp: &Response) { @@ -120,8 +100,23 @@ impl<'a> Stub<'a> { write_packet(self.io, &greeting); } - fn process_begin(&mut self, cmd: &BeginCommand) -> Result<(), Error> { + fn calculate_md5(&mut self, mut address: u32, mut size: u32) -> Result<[u8; 16], Error> { + let mut buffer: [u8; FLASH_SECTOR_SIZE as usize] = [0; FLASH_SECTOR_SIZE as usize]; + let mut hasher = Md5::new(); + + while size > 0 { + let to_read = min(size, FLASH_SECTOR_SIZE); + self.target.spi_flash_read(address, &mut buffer)?; + hasher.update(&buffer[0..to_read as usize]); + size -= to_read; + address += to_read; + } + + let result: [u8; 16] = hasher.finalize().into(); + Ok(result) + } + fn process_begin(&mut self, cmd: &BeginCommand) -> Result<(), Error> { // Align erase addreess to sector boundady. self.erase_addr = cmd.offset & FLASH_SECTOR_MASK; self.write_addr = cmd.offset; @@ -129,6 +124,7 @@ impl<'a> Stub<'a> { self.remaining_compressed = (cmd.packt_count * cmd.packet_size) as usize; self.remaining = cmd.total_size; self.decompressor.state = 0; + self.in_flash_mode = true; match cmd.base.code { FlashBegin | FlashDeflBegin => { @@ -136,28 +132,16 @@ impl<'a> Stub<'a> { return Err(BadBlocksize); } // Todo: check for 16MB flash only - self.in_flash_mode = true; - unlock_flash()?; + self.target.unlock_flash()?; } - _ => () // Do nothing for MemBegin + _ => () } Ok(()) } - fn process_end(&mut self, cmd: &EndCommand, response: &Response) -> Result<(), Error> { - - if cmd.base.code == MemEnd { - let addr = self.erase_addr as *const u32; - let length = self.end_addr - self.erase_addr; - let slice = unsafe { slice::from_raw_parts(addr, length as usize) }; - let mut memory: [u32; 32] = [0; 32]; - memory.copy_from_slice(&slice); - return match self.remaining { - 0 => Ok(()), - _ => Err(NotEnoughData) - } - } else if !self.in_flash_mode { + fn process_flash_end(&mut self, cmd: &EndFlashCommand, response: &Response) -> Result<(), Error> { + if !self.in_flash_mode { return Err(NotInFlashMode); } else if self.remaining > 0 { return Err(NotEnoughData); @@ -167,8 +151,20 @@ impl<'a> Stub<'a> { if cmd.run_user_code == 1 { self.send_response(&response); - delay_us(10000); - soft_reset(); + self.target.delay_us(10000); + self.target.soft_reset(); + } + + Ok(()) + } + + fn process_mem_end(&mut self, cmd: &MemEndCommand, response: &Response) -> Result<(), Error> { + if self.remaining != 0 { + return Err(NotEnoughData); + } else if cmd.stay_in_stub == 0 { + self.send_response(&response); + self.target.delay_us(10000); + (cmd.entrypoint)(); } Ok(()) @@ -189,12 +185,13 @@ impl<'a> Stub<'a> { let memory = self.write_addr as *mut u32; unsafe{ *memory = *word }; self.write_addr += 4; + self.remaining -= 4; } Ok(()) } - fn flash(&mut self, flash_write: FlashFunc, data: &[u8]) { + fn flash(&mut self, encrypted: bool, data: &[u8]) { let mut address = self.write_addr; let mut remaining = min(self.remaining, data.len() as u32); @@ -204,10 +201,10 @@ impl<'a> Stub<'a> { while self.erase_addr < self.write_addr + remaining { if self.end_addr >= self.erase_addr + FLASH_BLOCK_SIZE && self.erase_addr % FLASH_BLOCK_SIZE == 0 { - flash_erase_block(self.erase_addr); + self.target.flash_erase_block(self.erase_addr); self.erase_addr += FLASH_BLOCK_SIZE; } else { - flash_erase_sector(self.erase_addr); + self.target.flash_erase_sector(self.erase_addr); self.erase_addr += FLASH_SECTOR_SIZE; } } @@ -216,7 +213,11 @@ impl<'a> Stub<'a> { while remaining > 0 { let to_write = min(FLASH_SECTOR_SIZE, remaining); let data_ptr = data[written..].as_ptr(); - self.last_error = flash_write(address, data_ptr, to_write).err(); + if encrypted { + self.last_error = self.target.write_encrypted(address, data_ptr, to_write).err(); + } else { + self.last_error = self.target.spiflash_write(address, data_ptr, to_write).err(); + } remaining -= to_write; written += to_write as usize; address += to_write; @@ -227,7 +228,7 @@ impl<'a> Stub<'a> { } fn flash_data(&mut self, data: &[u8]) { - self.flash(spiflash_write, data); + self.flash(false, data); } fn flash_defl_data(&mut self, data: &[u8]) { @@ -253,7 +254,7 @@ impl<'a> Stub<'a> { flags |= TINFL_FLAG_HAS_MORE_INPUT; } - status = target::decompress( + status = self.target.decompress( &mut self.decompressor, data[in_index..].as_ptr(), &mut in_bytes, @@ -286,9 +287,9 @@ impl<'a> Stub<'a> { } fn flash_encrypt_data(&mut self, data: &[u8]) { - write_encrypted_enable(); - self.flash(write_encrypted, data); - write_encrypted_disable(); + self.target.write_encrypted_enable(); + self.flash(true, data); + self.target.write_encrypted_disable(); } fn process_data(&mut self, cmd: &DataCommand, data: &[u8], response: &Response) -> Result<(), Error> { @@ -329,7 +330,7 @@ impl<'a> Stub<'a> { while acked < params.total_size { while remaining > 0 && sent < (acked + max_inflight_bytes) { let len = min(params.packet_size, remaining); - spi_flash_read(address, &mut buffer[..len as usize])?; + self.target.spi_flash_read(address, &mut buffer[..len as usize])?; write_packet(self.io, &buffer[..len as usize]); hasher.update(&buffer[0..len as usize]); remaining -= len; @@ -352,7 +353,7 @@ impl<'a> Stub<'a> { let mut response_sent = false; - dprintln!("process command: {:?}", code); + crate::dprintln!("process command: {:?}", code); match code { Sync => { @@ -362,11 +363,11 @@ impl<'a> Stub<'a> { }, ReadReg => { let address = u32_from_slice(payload, CMD_BASE_SIZE); - response.value(read_register(address)); + response.value(self.target.read_register(address)); } WriteReg => { let reg: WriteRegCommand = slice_to_struct(payload)?; - write_register(reg.address, reg.value); + self.target.write_register(reg.address, reg.value); } FlashBegin | MemBegin | FlashDeflBegin => { let cmd: BeginCommand = slice_to_struct(payload)?; @@ -378,38 +379,42 @@ impl<'a> Stub<'a> { self.process_data(&cmd, data, &response)?; response_sent = true; } - FlashEnd | MemEnd | FlashDeflEnd => { - let cmd: EndCommand = slice_to_struct(payload)?; - self.process_end(&cmd, &response)?; + FlashEnd | FlashDeflEnd => { + let cmd: EndFlashCommand = slice_to_struct(payload)?; + self.process_flash_end(&cmd, &response)?; + } + MemEnd => { + let cmd: MemEndCommand = slice_to_struct(payload)?; + self.process_mem_end(&cmd, &response)?; } SpiFlashMd5 => { let cmd: SpiFlashMd5Command = slice_to_struct(payload)?; - let md5 = calculate_md5(cmd.address, cmd.size)?; + let md5 = self.calculate_md5(cmd.address, cmd.size)?; self.send_md5_response(&response, &md5); response_sent = true; } SpiSetParams => { let cmd: SpiSetParamsCommand = slice_to_struct(payload)?; - spi_set_params(&cmd.params)? + self.target.spi_set_params(&cmd.params)? } SpiAttach => { let param = u32_from_slice(payload, CMD_BASE_SIZE); - spi_attach(param); + self.target.spi_attach(param); } ChangeBaudrate => { let baud: ChangeBaudrateCommand = slice_to_struct(payload)?; self.send_response(&response); - delay_us(10000); // Wait for response to be transfered - change_baudrate(baud.old, baud.new); + self.target.delay_us(10000); // Wait for response to be transfered + self.target.change_baudrate(baud.old, baud.new); self.send_greeting(); response_sent = true; } EraseFlash => { - erase_flash()? + self.target.erase_flash()? } EraseRegion => { let reg: EraseRegionCommand = slice_to_struct(payload)?; - erase_region(reg.address, reg.size)?; + self.target.erase_region(reg.address, reg.size)?; } ReadFlash => { self.send_response(&response); @@ -418,12 +423,12 @@ impl<'a> Stub<'a> { response_sent = true; } GetSecurityInfo => { - let info = get_security_info()?; + let info = self.target.get_security_info()?; self.send_security_info_response(&response, &info); response_sent = true; } RunUserCode => { - soft_reset(); // ESP8266 Only + self.target.soft_reset(); // ESP8266 Only } _ => { return Err(InvalidCommand); @@ -499,262 +504,4 @@ mod slip { pub fn write_delimiter(io: &mut dyn InputIO) { io.send(&[0xC0]); } -} - -#[cfg(test)] -mod tests { - use super::*; - // use super::stub::Error::*; - use super::slip::{read_packet, write_raw}; - use assert2::{assert, let_assert}; - // use matches::assert_matches; - use std::collections::VecDeque; - use std::vec::Vec; - use crate::commands::*; - use mockall::predicate; - - struct MockIO { - data: VecDeque - } - - impl MockIO { - fn from_slice(bytes: &[u8]) -> Self { - let bytes_vec = Vec::from(bytes); - MockIO { data: VecDeque::from(bytes_vec) } - } - - fn new() -> Self { - MockIO { data: VecDeque::new() } - } - - fn fill(&mut self, bytes: &[u8]) { - self.data.clear(); - self.data.extend(bytes); - } - - fn clear(&mut self) { - self.data.clear(); - } - - fn written(&mut self) -> &[u8] { - self.data.make_contiguous() - } - } - - impl InputIO for MockIO { - fn read(&mut self) -> Result { - match self.data.pop_front() { - Some(top) => Ok(top), - None => Err(Incomplete) - } - } - - fn write(&mut self, bytes: &[u8]) -> Result<(), ErrorIO> - { - self.data.extend(bytes); - Ok(()) - } - } - - #[test] - fn test_read_packet() { - let mut io = MockIO::new(); - let mut buffer: Buffer = heapless::Vec::new(); - - // Returns Incomplete when packet enclosed by 0xC0 was not found - io.fill(&[0xC0, 0xAA, 0x22]); - assert!( read_packet(&mut io, &mut buffer) == Err(Incomplete)); - - // Returns Incomplete when no 0xC0 is found - io.fill(&[0x00, 0xAA, 0x22]); - assert!( read_packet(&mut io, &mut buffer) == Err(Incomplete)); - - // Can find packet by 0xC0 - io.fill(&[0xC0, 0x11, 0x22, 0xC0]); - assert!( read_packet(&mut io, &mut buffer) == Ok(())); - assert!( buffer.as_slice() == &[0x11, 0x22] ); - - // Can find packet by 0xC0 - io.fill(&[0xC0, 0x11, 0x22, 0xC0]); - assert!( read_packet(&mut io, &mut buffer) == Ok(())); - assert!( buffer.as_slice() == &[0x11, 0x22] ); - - // Can convert 0xDB 0xDC -> 0xC0 - io.fill(&[0xC0, 0x11, 0xDB, 0xDC, 0x22, 0xC0]); - assert!( read_packet(&mut io, &mut buffer) == Ok(())); - assert!( buffer.as_slice() == &[0x11, 0xC0, 0x22] ); - - // Can convert 0xDB 0xDD -> 0xDB - io.fill(&[0xC0, 0x11, 0xDB, 0xDD, 0x22, 0xC0]); - assert!( read_packet(&mut io, &mut buffer) == Ok(())); - assert!( buffer.as_slice() == &[0x11, 0xDB, 0x22] ); - - // Returns InvalidResponse after invalid byte pair - io.fill(&[0xC0, 0x11, 0xDB, 0x22, 0xDB, 0x33, 0x44, 0xC0]); - assert!( read_packet(&mut io, &mut buffer) == Err(InvalidResponse)); - } - - #[test] - fn test_write_raw() { - let mut io = MockIO::new(); - - // 0xC0 is replaced with 0xDB 0xDC - assert!( write_raw(&mut io, &[1, 0xC0, 3]) == Ok(())); - assert!( io.written() == &[1, 0xDB, 0xDC, 3] ); - io.clear(); - - // 0xDB is replaced with 0xDB 0xDD - assert!( write_raw(&mut io, &[1, 0xDB, 3]) == Ok(())); - assert!( io.written() == &[1, 0xDB, 0xDD, 3] ); - io.clear(); - } - - #[test] - fn test_wait_for_packet() { - let mut dummy = CommandCode::Sync; - // Check FlashBegin command - let mut io = MockIO::from_slice(&[ - 0xC0, - 0, // direction - CommandCode::FlashBegin as u8, - 16, 0, // size - 1, 0, 0, 0, // checksum - 2, 0, 0, 0, // erase_addr - 3, 0, 0, 0, // packt_count - 4, 0, 0, 0, // packet_size - 5, 0, 0, 0, // offset - 0xC0]); - let mut stub = Stub::new(&mut io); - let_assert!( Ok(Command::Begin(cmd)) = stub.wait_for_command(&mut dummy) ); - assert!( {cmd.base.direction == 0} ); - assert!( {cmd.base.code == CommandCode::FlashBegin} ); - assert!( {cmd.base.size == 16} ); - assert!( {cmd.base.checksum == 1} ); - assert!( {cmd.total_size == 2} ); - assert!( {cmd.packt_count == 3} ); - assert!( {cmd.packet_size == 4} ); - assert!( {cmd.offset == 5} ); - - // Check FlashData command - let mut io = MockIO::from_slice(&[ - 0xC0, - 0, // direction - CommandCode::FlashData as u8, - 20, 0, // size - 1, 0, 0, 0, // checksum - 4, 0, 0, 0, // size - 3, 0, 0, 0, // sequence_num - 0, 0, 0, 0, // reserved 1 - 0, 0, 0, 0, // reserved 1 - 9, 8, 7, 6, // payload - 0xC0]); - let mut stub = Stub::new(&mut io); - let_assert!( Ok(Command::Data(cmd, data)) = stub.wait_for_command(&mut dummy) ); - assert!( {cmd.base.code == CommandCode::FlashData} ); - assert!( {cmd.base.size == 20} ); - assert!( {cmd.base.checksum == 1} ); - assert!( {cmd.size == 4} ); - assert!( {cmd.sequence_num == 3} ); - assert!( {cmd.reserved[0] == 0} ); - assert!( {cmd.reserved[1] == 0} ); - assert!( data == &[9, 8, 7, 6] ); - - // Check Sync command - let mut io = MockIO::from_slice(&[ - 0xC0, - 0, // direction - CommandCode::Sync as u8, - 36, 0, // size - 1, 0, 0, 0, // checksum - 0x7, 0x7, 0x12, 0x20, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0xC0]); - let mut stub = Stub::new(&mut io); - let_assert!( Ok(Command::Sync(_)) = stub.wait_for_command(&mut dummy) ); - - // Check ReadReg command - let mut io = MockIO::from_slice(&[ - 0xC0, - 0, // direction - CommandCode::ReadReg as u8, - 4, 0, // size - 1, 0, 0, 0, // checksum - 200, 0, 0, 0, // address - 0xC0]); - let mut stub = Stub::new(&mut io); - let_assert!( Ok(Command::ReadReg(address)) = stub.wait_for_command(&mut dummy) ); - assert!( address == 200 ); - } - - #[test] - fn test_send_response() { - - // Can write error response - let mut io = MockIO::new(); - let mut stub = Stub::new(&mut io); - let mut response = Response::new(CommandCode::FlashBegin); - response.error(Error::BadDataChecksum); - let expected = &[0xC0, 1, CommandCode::FlashBegin as u8, 2,0, 0,0,0,0, 1, Error::BadDataChecksum as u8, 0xC0]; - assert!( stub.send_response(&response) == Ok(())); - assert!( io.written() == expected); - - // Can write response with data - let mut io = MockIO::new(); - let mut stub = Stub::new(&mut io); - let data = &[1, 2, 3, 4, 5, 6, 7, 8]; - let mut response = Response::new(CommandCode::FlashBegin); - response.data(data); - let expected = &[0xC0, 1, CommandCode::FlashBegin as u8, 10,0, 0,0,0,0, 0,0, 1, 2, 3, 4, 5, 6, 7, 8, 0xC0]; - assert!( stub.send_response(&response) == Ok(())); - assert!( io.written() == expected); - } - - fn decorate_command(data: T) -> Vec { - let mut v = Vec::new(); - v.push(0xC0); - v.extend_from_slice( unsafe{ to_slice_u8(&data) } ); - v.push(0xC0); - v - } - - #[repr(C, packed(1))] - pub struct TestResponse { - pub direction: u8, - pub command: CommandCode, - pub size: u16, - pub value: u32, - pub status: u8, - pub error: u8, - } - - #[test] - fn test_read_register() { - let cmd = ReadRegCommand { - base: CommandBase { direction: 1, code: CommandCode::ReadReg, size: 4, checksum: 0 }, - address: 200 - }; - let mut io = MockIO::from_slice(&decorate_command(cmd)); - let mut stub = Stub::new(&mut io); - - let ctx = target::read_register_context(); - ctx.expect().with(predicate::eq(200)).returning(|x| x + 1); - assert!( Ok(()) == stub.process_commands() ); - - let expect = &[0xC0, 1, CommandCode::ReadReg as u8, 2,0, 201,0,0,0, 0,0, 0xC0]; - assert!( io.written() == expect ); - } - - #[test] - fn test_mock() { - let mut io = MockIO::new(); - - io.fill(&[1, 2, 3]); - assert!(io.recv() == Ok(1)); - assert!(io.recv() == Ok(2)); - assert!(io.recv() == Ok(3)); - assert!(io.recv() == Err(Incomplete)); - } -} +} \ No newline at end of file diff --git a/src/serial_io.rs b/src/serial_io.rs index 3eed3d8..49e731c 100644 --- a/src/serial_io.rs +++ b/src/serial_io.rs @@ -1,28 +1,29 @@ use heapless::Deque; -use esp32c3_hal::{ - Serial, - pac, - pac::UART0, -}; use esp_hal_common::{ + pac::UART0, + pac, + Serial, serial::Instance, interrupt, - interrupt::*, interrupt::CpuInterrupt::*, Cpu::*, + prelude::* }; + +#[cfg(any(target_arch = "riscv32"))] +use riscv::interrupt::free as interrupt_free; +#[cfg(any(target_arch = "xtensa"))] +use xtensa_lx::interrupt::free as interrupt_free; + use crate::protocol::InputIO; -use crate::targets::esp32c3 as target; -const RX_QUEUE_SIZE: usize = target::MAX_WRITE_BLOCK + 0x400; +const RX_QUEUE_SIZE: usize = crate::targets::MAX_WRITE_BLOCK + 0x400; static mut RX_QUEUE: Deque = Deque::new(); impl<'a, T: Instance> InputIO for Serial { fn recv(&mut self) -> u8 { - unsafe{ - while riscv::interrupt::free(|_| RX_QUEUE.is_empty() ) { } - riscv::interrupt::free(|_| RX_QUEUE.pop_front().unwrap_unchecked()) - } + unsafe{ while interrupt_free(|_| RX_QUEUE.is_empty() ) { } } + unsafe{ interrupt_free(|_| RX_QUEUE.pop_front().unwrap()) } } fn send(&mut self, bytes: &[u8]) { @@ -30,29 +31,53 @@ impl<'a, T: Instance> InputIO for Serial { } } +fn uart_isr() { + let uart = unsafe{ &*UART0::ptr() }; + + while uart.status.read().rxfifo_cnt().bits() > 0 { + let data = uart.fifo.read().rxfifo_rd_byte().bits(); + unsafe{ RX_QUEUE.push_back(data).unwrap() }; + } + + uart.int_clr.write(|w| w.rxfifo_full_int_clr().set_bit()); +} + +#[cfg(feature = "esp32c3")] pub fn enable_uart0_rx_interrupt() { let uart = unsafe{ &*UART0::ptr() }; uart.conf1.modify(|_, w| unsafe{ w.rxfifo_full_thrhd().bits(1) }); uart.int_ena.write(|w| w.rxfifo_full_int_ena().set_bit() ); - interrupt::enable( ProCpu, pac::Interrupt::UART0, Interrupt3 ); - interrupt::set_kind( ProCpu, Interrupt3, InterruptKind::Level ); - interrupt::set_priority( ProCpu, Interrupt3, Priority::Priority10 ); - + interrupt::enable(pac::Interrupt::UART0, interrupt::Priority::Priority1).unwrap(); + interrupt::set_kind( ProCpu, Interrupt1, interrupt::InterruptKind::Level); + unsafe { riscv::interrupt::enable(); } } - -#[no_mangle] -pub fn interrupt3() { +#[cfg(feature = "esp32")] +pub fn enable_uart0_rx_interrupt() { let uart = unsafe{ &*UART0::ptr() }; - - while uart.status.read().rxfifo_cnt().bits() > 0 { - let data = uart.fifo.read().rxfifo_rd_byte().bits(); - unsafe{ RX_QUEUE.push_back(data).unwrap() }; + + uart.conf1.modify(|_, w| unsafe{ w.rxfifo_full_thrhd().bits(1) }); + uart.int_ena.write(|w| w.rxfifo_full_int_ena().set_bit() ); + + unsafe { + interrupt::map( ProCpu, pac::Interrupt::UART0, Interrupt17LevelPriority1 ); + xtensa_lx::interrupt::enable_mask(1 << 17); } - - uart.int_clr.write(|w| w.rxfifo_full_int_clr().set_bit()); - interrupt::clear(ProCpu, Interrupt3); +} + +#[interrupt] +#[cfg(feature = "esp32c3")] +fn UART0() { + uart_isr(); + interrupt::clear(ProCpu, Interrupt1); +} + +#[interrupt] +#[cfg(feature = "esp32")] +fn UART0() { + uart_isr(); + interrupt::clear(ProCpu, Interrupt17LevelPriority1); } \ No newline at end of file diff --git a/src/targets.rs b/src/targets.rs index fe447c9..e32e78c 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -1,16 +1,23 @@ -#[cfg(test)] -use mockall::automock; - use crate::miniz_types::*; +use crate::commands::{*, Error::*}; +use core::ptr::{read_volatile, write_volatile}; + +#[repr(C, packed(1))] +struct RomSpiFlashChip { + device_id: u32, + chip_size: u32, + block_size: u32, + sector_size: u32, + page_size: u32, + status_mask: u32, +} #[allow(unused)] extern "C" { fn esp_rom_spiflash_erase_chip() -> i32; fn esp_rom_spiflash_erase_block(block_number: u32) -> i32; fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32; - /// address (4 byte alignment), data, length fn esp_rom_spiflash_write(dest_addr: u32, data: *const u8, len: u32) -> i32; - /// address (4 byte alignment), data, length fn esp_rom_spiflash_read(src_addr: u32, data: *const u8, len: u32) -> i32; fn esp_rom_spiflash_unlock() -> i32; fn esp_rom_spiflash_attach(config: u32, legacy: bool); @@ -21,28 +28,35 @@ extern "C" { fn ets_efuse_get_spiconfig() -> u32; fn software_reset(); fn ets_delay_us(timeout: u32); - fn GetSecurityInfoProc(pMsg: u8, pnErr: u8, data: *const u8) -> u32; + fn get_security_info_proc(pMsg: u8, pnErr: u8, data: *const u8) -> u32; fn esp_rom_spiflash_write_encrypted_enable(); fn esp_rom_spiflash_write_encrypted_disable(); + fn spi_write_status(chip: *const RomSpiFlashChip, status: u32) -> u32; fn esp_rom_spiflash_write_encrypted(dest_addr: u32, data: *const u8, len: u32) -> i32; + // fn spi_read_status_high(spi: *const RomSpiFlashChip, status: *const u32) -> u32; + fn spi_read_status_high(status: *const u32) -> u32; } +const SECURITY_INFO_BYTES : usize = 20; -#[cfg_attr(test, automock)] -pub mod esp32c3 { - use super::*; - use crate::commands::*; - use crate::commands::Error::*; - use core::ptr::{read_volatile, write_volatile}; +pub const FLASH_SECTOR_SIZE: u32 = 4096; +pub const FLASH_BLOCK_SIZE: u32 = 65536; +pub const FLASH_SECTOR_MASK: u32 = 0xFFFFF000; +pub const MAX_WRITE_BLOCK: usize = 0x4000; - const SPI_BASE_REG: u32 = 0x60002000; - const SPI_CMD_REG: u32 = SPI_BASE_REG + 0x00; - const SPI_ADDR_REG: u32 = SPI_BASE_REG + 0x04; - const SPI_RD_STATUS_REG: u32 = SPI_BASE_REG + 0x2C; - const SPI_EXT2_REG: u32 = SPI_BASE_REG + 0x54; +pub trait EspCommon { + const SPI_BASE_REG: u32 = 0x60002000; + const SPI_RD_STATUS_REG: u32 = Self::SPI_BASE_REG + 0x2C; + const SPI_EXT2_REG: u32 = Self::SPI_BASE_REG + 0x54; const SPI0_BASE_REG: u32 = 0x60003000; - const SPI0_EXT2_REG: u32 = SPI0_BASE_REG + 0x54; + const SPI0_EXT2_REG: u32 = Self::SPI0_BASE_REG + 0x54; + const UART_BASE_REG: u32 = 0x60000000; + const GPIO_BASE_REG: u32 = 0x60004000; + + const SPI_CMD_REG: u32 = Self::SPI_BASE_REG + 0x00; + const SPI_ADDR_REG: u32 = Self::SPI_BASE_REG + 0x04; + const SPI_CTRL_REG: u32 = Self::SPI_BASE_REG + 0x08; const SPI_ST: u32 = 0x7; const SPI_FLASH_RDSR: u32 = 1<<27; @@ -51,18 +65,12 @@ pub mod esp32c3 { const SPI_FLASH_SE: u32 = 1<<24; const SPI_FLASH_BE: u32 = 1<<23; - const UART_BASE_REG: u32 = 0x60000000; - const UART0_CLKDIV_REG: u32 = UART_BASE_REG + 0x14; + const UART0_CLKDIV_REG: u32 = Self::UART_BASE_REG + 0x14; const UART_CLKDIV_M: u32 = 0x000FFFFF; const UART_CLKDIV_FRAG_S: u32 = 20; const UART_CLKDIV_FRAG_V: u32 = 0xF; - pub const FLASH_SECTOR_SIZE: u32 = 4096; - pub const FLASH_BLOCK_SIZE: u32 = 65536; - pub const FLASH_SECTOR_MASK: u32 = 0xFFFFF000; - pub const MAX_WRITE_BLOCK: usize = 0x4000; - const GPIO_BASE_REG: u32 = 0x60004000; - const GPIO_STRAP_REG: u32 = GPIO_BASE_REG + 0x38; + const GPIO_STRAP_REG: u32 = Self::GPIO_BASE_REG + 0x38; const FLASH_MAX_SIZE: u32 = 16*1024*1024; const FLASH_PAGE_SIZE: u32 = 256; @@ -70,30 +78,34 @@ pub mod esp32c3 { const SECURITY_INFO_BYTES : usize = 20; - fn get_uart_div(current_baud: u32, new_baud: u32) -> u32 { - let clock_div_reg = read_register(UART0_CLKDIV_REG); - let uart_div = clock_div_reg & UART_CLKDIV_M; - let fraction = (clock_div_reg >> UART_CLKDIV_FRAG_S) & UART_CLKDIV_FRAG_V; + fn get_uart_div(&self, current_baud: u32, new_baud: u32) -> u32 { + let clock_div_reg = self.read_register(Self::UART0_CLKDIV_REG); + let uart_div = clock_div_reg & Self::UART_CLKDIV_M; + let fraction = (clock_div_reg >> Self::UART_CLKDIV_FRAG_S) & Self::UART_CLKDIV_FRAG_V; let uart_div = (uart_div << 4) + fraction; (uart_div * current_baud) / new_baud } - pub fn read_register(address: u32) -> u32 { + fn read_register(&self, address: u32) -> u32 { unsafe{ read_volatile(address as *const u32) } } - pub fn write_register(address: u32, value: u32) { + fn write_register(&self, address: u32, value: u32) { unsafe{ write_volatile(address as *mut _, value) } } - pub fn spiflash_write(dest_addr: u32, data: *const u8, len: u32) -> Result<(), Error> { + fn set_register_mask(&self, address: u32, mask: u32) { + self.write_register(address, self.read_register(address) | mask); + } + + fn spiflash_write(&self, dest_addr: u32, data: *const u8, len: u32) -> Result<(), Error> { match unsafe{ esp_rom_spiflash_write(dest_addr, data, len) } { 0 => Ok(()), _ => Err(FailedSpiOp) } } - pub fn spi_set_params(params: &SpiParams) -> Result<(), Error> { + fn spi_set_params(&self, params: &SpiParams) -> Result<(), Error> { let result = unsafe{ esp_rom_spiflash_config_param( params.id, params.total_size, @@ -105,15 +117,15 @@ pub mod esp32c3 { if result == 0 { Ok(()) } else { Err(FailedSpiOp) } } - pub fn spi_attach(param: u32) { + fn spi_attach(&self, param: u32) { unsafe{ esp_rom_spiflash_attach(param, false) }; } - pub fn change_baudrate(old: u32, new: u32) { - unsafe{ uart_div_modify(0, get_uart_div(old, new)) }; + fn change_baudrate(&self, old: u32, new: u32) { + unsafe{ uart_div_modify(0, self.get_uart_div(old, new)) }; } - pub fn erase_flash() -> Result<(), Error> { + fn erase_flash(&self) -> Result<(), Error> { // Returns 1 or 2 in case of failure match unsafe{ esp_rom_spiflash_erase_chip() } { 0 => Ok(()), @@ -121,53 +133,54 @@ pub mod esp32c3 { } } - fn erase(address: u32, block: bool) { - spiflash_wait_for_ready(); - spi_write_enable(); - wait_for_ready(); - - let command = if block { SPI_FLASH_BE } else { SPI_FLASH_SE }; - write_register(SPI_ADDR_REG, address); - write_register(SPI_CMD_REG, command); - while read_register(SPI_CMD_REG) != 0 { } - - spiflash_wait_for_ready(); + fn erase(&self, address: u32, block: bool) { + self.spiflash_wait_for_ready(); + self.spi_write_enable(); + self.wait_for_ready(); + + let command = if block { Self::SPI_FLASH_BE } else { Self::SPI_FLASH_SE }; + self.write_register(Self::SPI_ADDR_REG, address); + self.write_register(Self::SPI_CMD_REG, command); + while self.read_register(Self::SPI_CMD_REG) != 0 { } + + self.spiflash_wait_for_ready(); } - - fn wait_for_ready() { - while (read_register(SPI_EXT2_REG) & SPI_ST) != 0 { } - while (read_register(SPI0_EXT2_REG) & SPI_ST) != 0 { } // ESP32_OR_LATER + + fn wait_for_ready(&self) { + while (self.read_register(Self::SPI_EXT2_REG) & Self::SPI_ST) != 0 { } + while (self.read_register(Self::SPI0_EXT2_REG) & Self::SPI_ST) != 0 { } // ESP32_OR_LATER } - - fn spiflash_wait_for_ready() { - wait_for_ready(); - - write_register(SPI_RD_STATUS_REG, 0); - write_register(SPI_CMD_REG, SPI_FLASH_RDSR); - while read_register(SPI_CMD_REG) != 0 { } - while (read_register(SPI_RD_STATUS_REG) & STATUS_WIP_BIT) != 0 {} + + fn spiflash_wait_for_ready(&self) { + + self.wait_for_ready(); + + self.write_register(Self::SPI_RD_STATUS_REG, 0); + self.write_register(Self::SPI_CMD_REG, Self::SPI_FLASH_RDSR); + while self.read_register(Self::SPI_CMD_REG) != 0 { } + while (self.read_register(Self::SPI_RD_STATUS_REG) & Self::STATUS_WIP_BIT) != 0 {} } - fn spi_write_enable() { - write_register(SPI_CMD_REG, SPI_FLASH_WREN); - while read_register(SPI_CMD_REG) != 0 { } + fn spi_write_enable(&self) { + self.write_register(Self::SPI_CMD_REG, Self::SPI_FLASH_WREN); + while self.read_register(Self::SPI_CMD_REG) != 0 { } } - pub fn flash_erase_block(address: u32 ) { - // unsafe{ esp_rom_spiflash_erase_block(address / FLASH_BLOCK_SIZE) }; - erase(address, true); + fn flash_erase_block(&self, address: u32 ) { + self.erase(address, true); } - - pub fn flash_erase_sector(address: u32 ) { - erase(address, false); + + fn flash_erase_sector(&self, address: u32 ) { + self.erase(address, false); } + - pub fn erase_region(address: u32, size: u32) -> Result<(), Error> { + fn erase_region(&self, address: u32, size: u32) -> Result<(), Error> { if address % FLASH_SECTOR_SIZE != 0 { return Err(Err0x32); } else if size % FLASH_SECTOR_SIZE != 0 { return Err(Err0x33); - } else if unsafe{ esp_rom_spiflash_unlock() } != 0 { + } else if self.unlock_flash().is_err() { return Err(Err0x34); } @@ -183,7 +196,7 @@ pub mod esp32c3 { Ok(()) } - pub fn spi_flash_read(address: u32, data: &mut [u8]) -> Result<(), Error> { + fn spi_flash_read(&self, address: u32, data: &mut [u8]) -> Result<(), Error> { let data_ptr = data.as_mut_ptr(); let data_len = data.len() as u32; @@ -193,71 +206,88 @@ pub mod esp32c3 { } } - pub fn unlock_flash() -> Result<(), Error> { - if unsafe{ esp_rom_spiflash_unlock() } != 0 { - Err(FailedSpiUnlock) - } else { - Ok(()) + fn unlock_flash(&self) -> Result<(), Error> { + let mut status: u32 = 0; + const STATUS_QIE_BIT: u32 = 1 << 9; + const SPI_WRSR_2B: u32 = 1<<22; + const FLASHCHIP: *const RomSpiFlashChip = 0x3ffae270 as *const RomSpiFlashChip; + + self.wait_for_ready(); + if ( unsafe{ spi_read_status_high(&status) } != 0) { + return Err(FailedSpiUnlock); } + + // Clear all bits except QIE, if it is set. + // (This is different from ROM SPIUnlock, which keeps all bits as-is.) + status &= STATUS_QIE_BIT; + + self.spi_write_enable(); + self.set_register_mask(Self::SPI_CTRL_REG, SPI_WRSR_2B); + + if ( unsafe{ spi_write_status(FLASHCHIP, status) } != 0) { + return Err(FailedSpiUnlock); + } + + Ok(()) } - // ESP32S2_OR_LATER && !ESP32H2BETA2 - pub fn get_security_info() -> Result<[u8; SECURITY_INFO_BYTES], Error> + fn get_security_info(&self) -> Result<[u8; SECURITY_INFO_BYTES], Error> { let mut buf: [u8; SECURITY_INFO_BYTES] = [0; SECURITY_INFO_BYTES]; - match unsafe{ GetSecurityInfoProc(0, 0, buf.as_mut_ptr()) } { + match unsafe{ get_security_info_proc(0, 0, buf.as_mut_ptr()) } { 0 => Ok(buf), _ => Err(InvalidCommand), // Todo check ROM code for err val } } - pub fn init() { + fn init(&self) { let mut spiconfig = unsafe{ ets_efuse_get_spiconfig() }; - let strapping = read_register(GPIO_STRAP_REG); + let strapping = self.read_register(Self::GPIO_STRAP_REG); if spiconfig == 0 && (strapping & 0x1c) == 0x08 { spiconfig = 1; /* HSPI flash mode */ } - spi_attach(spiconfig); + self.spi_attach(spiconfig); let deafault_params = SpiParams { id: 0, - total_size: FLASH_MAX_SIZE, + total_size: Self::FLASH_MAX_SIZE, block_size: FLASH_BLOCK_SIZE, sector_size: FLASH_SECTOR_SIZE, - page_size: FLASH_PAGE_SIZE, - status_mask: FLASH_STATUS_MASK, + page_size: Self::FLASH_PAGE_SIZE, + status_mask: Self::FLASH_STATUS_MASK, }; - let _ = spi_set_params(&deafault_params); + let _ = self.spi_set_params(&deafault_params); } - pub fn soft_reset() { + fn soft_reset(&self) { unsafe { software_reset() }; } - pub fn delay_us(micro_seconds: u32) { + fn delay_us(&self, micro_seconds: u32) { unsafe{ ets_delay_us(micro_seconds) }; } - pub fn write_encrypted_enable() { + fn write_encrypted_enable(&self) { unsafe{ esp_rom_spiflash_write_encrypted_enable(); } } - pub fn write_encrypted_disable() { + fn write_encrypted_disable(&self, ) { unsafe{ esp_rom_spiflash_write_encrypted_disable(); } } - pub fn write_encrypted(addr: u32, data: *const u8, len: u32) -> Result<(), Error> { + fn write_encrypted(&self, addr: u32, data: *const u8, len: u32) -> Result<(), Error> { match unsafe{ esp_rom_spiflash_write_encrypted(addr, data, len) } { 0 => Ok(()), _ => Err(FailedSpiOp) } } - pub fn decompress( + fn decompress( + &self, r: *mut tinfl_decompressor, in_buf: *const u8, in_buf_size: *mut usize, @@ -277,3 +307,41 @@ pub mod esp32c3 { ) } } } +#[derive(Default)] +pub struct Esp32c3; + +#[derive(Default)] +pub struct Esp32; + +impl EspCommon for Esp32c3 { + // fn unlock_flash(&self, ) -> Result<(), Error> { + // if unsafe{ esp_rom_spiflash_unlock() } != 0 { + // Err(FailedSpiUnlock) + // } else { + // Ok(()) + // } + // } +} + +impl EspCommon for Esp32 { + const SPI_BASE_REG: u32 = 0x3ff42000; + const SPI_RD_STATUS_REG: u32 = Self::SPI_BASE_REG + 0x10; + const SPI_EXT2_REG: u32 = Self::SPI_BASE_REG + 0xF8; + const SPI0_BASE_REG: u32 = 0x3ff43000; + const SPI0_EXT2_REG: u32 = Self::SPI0_BASE_REG + 0xF8; + const UART_BASE_REG: u32 = 0x3ff40000; + const GPIO_BASE_REG: u32 = 0x3ff44000; + + fn get_security_info(&self) -> Result<[u8; SECURITY_INFO_BYTES], Error> + { + Err(InvalidCommand) + } + + fn flash_erase_block(&self, address: u32 ) { + unsafe{ esp_rom_spiflash_erase_block(address / FLASH_BLOCK_SIZE) }; + } + + fn flash_erase_sector(&self, address: u32 ) { + unsafe{ esp_rom_spiflash_erase_sector(address / FLASH_SECTOR_SIZE) }; + } +} \ No newline at end of file