diff --git a/.github/bors.toml b/.github/bors.toml index 0cf10c79..a398b4aa 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -4,6 +4,10 @@ required_approvals = 1 status = [ "ci-linux (stable)", "ci-linux (1.40.0)", + "rt-ci-linux (stable)", + "rt-ci-linux (1.40.0)", + "rt-ci-other-os (macOS-latest)", + "rt-ci-other-os (windows-latest)", "rustfmt", "clippy", ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86000548..3d65b0f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ on: branches: [ staging, trying, master ] pull_request: -name: CI +name: cortex-m CI jobs: ci-linux: @@ -29,6 +29,6 @@ jobs: toolchain: ${{ matrix.rust }} override: true - name: Run tests - run: cargo test --all + run: cargo test --all --exclude cortex-m-rt # FIXME: test on macOS and Windows diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index d55d697f..5a76037b 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -23,3 +23,4 @@ jobs: - uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} + args: --all diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index bc31bb23..d765dbab 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -28,3 +28,48 @@ jobs: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + rt-ci-linux: + runs-on: ubuntu-20.04 + continue-on-error: ${{ matrix.experimental || false }} + defaults: + run: + working-directory: cortex-m-rt + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - name: Install all Rust targets for stable + run: rustup target install --toolchain=stable thumbv6m-none-eabi thumbv7m-none-eabi thumbv7em-none-eabi thumbv7em-none-eabihf thumbv8m.base-none-eabi thumbv8m.main-none-eabi thumbv8m.main-none-eabihf + - name: Install qemu and gcc + run: sudo apt-get update && sudo apt-get install qemu-system-arm gcc-arm-none-eabi + - name: Run CI script for x86_64-unknown-linux-gnu under stable + run: TARGET=x86_64-unknown-linux-gnu TRAVIS_RUST_VERSION=stable bash ci/script.sh + - name: Run CI script for thumbv6m-none-eabi under stable + run: TARGET=thumbv6m-none-eabi TRAVIS_RUST_VERSION=stable bash ci/script.sh + - name: Run CI script for thumbv7m-none-eabi under stable + run: TARGET=thumbv7m-none-eabi TRAVIS_RUST_VERSION=stable bash ci/script.sh + - name: Run CI script for thumbv7em-none-eabi under stable + run: TARGET=thumbv7em-none-eabi TRAVIS_RUST_VERSION=stable bash ci/script.sh + - name: Run CI script for thumbv7em-none-eabihf under stable + run: TARGET=thumbv7em-none-eabihf TRAVIS_RUST_VERSION=stable bash ci/script.sh + - name: Run CI script for thumbv8m.base-none-eabi under stable + run: TARGET=thumbv8m.base-none-eabi TRAVIS_RUST_VERSION=stable bash ci/script.sh + - name: Run CI script for thumbv8m.main-none-eabi under stable + run: TARGET=thumbv8m.main-none-eabi TRAVIS_RUST_VERSION=stable bash ci/script.sh + - name: Run CI script for thumbv8m.main-none-eabihf under stable + run: TARGET=thumbv8m.main-none-eabihf TRAVIS_RUST_VERSION=stable bash ci/script.sh + - uses: imjohnbo/issue-bot@v2 + if: failure() + with: + title: CI Failure + labels: ci + body: | + Scheduled CI run failed. Details: + + https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/rt-ci.yml b/.github/workflows/rt-ci.yml new file mode 100644 index 00000000..0e48b6b2 --- /dev/null +++ b/.github/workflows/rt-ci.yml @@ -0,0 +1,87 @@ +on: + push: + branches: [ staging, trying, master ] + pull_request: + +name: cortex-m-rt CI + +jobs: + rt-ci-linux: + runs-on: ubuntu-20.04 + continue-on-error: ${{ matrix.experimental || false }} + strategy: + matrix: + # All generated code should be running on stable now + rust: [nightly, stable, 1.40.0] + + include: + # Nightly is only for reference and allowed to fail + - rust: nightly + experimental: true + defaults: + run: + working-directory: cortex-m-rt + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + - name: Install all Rust targets for ${{ matrix.rust }} + run: rustup target install --toolchain=${{ matrix.rust }} thumbv6m-none-eabi thumbv7m-none-eabi thumbv7em-none-eabi thumbv7em-none-eabihf thumbv8m.base-none-eabi thumbv8m.main-none-eabi thumbv8m.main-none-eabihf + - name: Install qemu and gcc + run: sudo apt-get update && sudo apt-get install qemu-system-arm gcc-arm-none-eabi + - name: Run CI script for x86_64-unknown-linux-gnu under ${{ matrix.rust }} + run: TARGET=x86_64-unknown-linux-gnu TRAVIS_RUST_VERSION=${{ matrix.rust }} bash ci/script.sh + - name: Run CI script for thumbv6m-none-eabi under ${{ matrix.rust }} + run: TARGET=thumbv6m-none-eabi TRAVIS_RUST_VERSION=${{ matrix.rust }} bash ci/script.sh + - name: Run CI script for thumbv7m-none-eabi under ${{ matrix.rust }} + run: TARGET=thumbv7m-none-eabi TRAVIS_RUST_VERSION=${{ matrix.rust }} bash ci/script.sh + - name: Run CI script for thumbv7em-none-eabi under ${{ matrix.rust }} + run: TARGET=thumbv7em-none-eabi TRAVIS_RUST_VERSION=${{ matrix.rust }} bash ci/script.sh + - name: Run CI script for thumbv7em-none-eabihf under ${{ matrix.rust }} + run: TARGET=thumbv7em-none-eabihf TRAVIS_RUST_VERSION=${{ matrix.rust }} bash ci/script.sh + - name: Run CI script for thumbv8m.base-none-eabi under ${{ matrix.rust }} + run: TARGET=thumbv8m.base-none-eabi TRAVIS_RUST_VERSION=${{ matrix.rust }} bash ci/script.sh + - name: Run CI script for thumbv8m.main-none-eabi under ${{ matrix.rust }} + run: TARGET=thumbv8m.main-none-eabi TRAVIS_RUST_VERSION=${{ matrix.rust }} bash ci/script.sh + - name: Run CI script for thumbv8m.main-none-eabihf under ${{ matrix.rust }} + run: TARGET=thumbv8m.main-none-eabihf TRAVIS_RUST_VERSION=${{ matrix.rust }} bash ci/script.sh + + # On macOS and Windows, we at least make sure that all examples build and link. + rt-ci-other-os: + strategy: + matrix: + os: + - macOS-latest + - windows-latest + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: cortex-m-rt + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - name: Install all Rust targets + run: rustup target install thumbv6m-none-eabi thumbv7m-none-eabi thumbv7em-none-eabi thumbv7em-none-eabihf thumbv8m.base-none-eabi thumbv8m.main-none-eabi thumbv8m.main-none-eabihf + - name: Build examples for thumbv6m-none-eabi + run: cargo build --target=thumbv6m-none-eabi --examples + - name: Build examples for thumbv7m-none-eabi + run: cargo build --target=thumbv7m-none-eabi --examples + - name: Build examples for thumbv7em-none-eabi + run: cargo build --target=thumbv7em-none-eabi --examples + - name: Build examples for thumbv7em-none-eabihf + run: cargo build --target=thumbv7em-none-eabihf --examples + - name: Build examples for thumbv8m.base-none-eabi + run: cargo build --target=thumbv8m.base-none-eabi --examples + - name: Build examples for thumbv8m.main-none-eabi + run: cargo build --target=thumbv8m.main-none-eabi --examples + - name: Build examples for thumbv8m.main-none-eabihf + run: cargo build --target=thumbv8m.main-none-eabihf --examples + - name: Build crate for host OS + run: cargo build diff --git a/Cargo.toml b/Cargo.toml index ead7e2e1..8527a89d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,13 @@ linker-plugin-lto = [] std = [] [workspace] -members = ["xtask", "cortex-m-semihosting", "panic-semihosting", "panic-itm"] +members = [ + "xtask", + "cortex-m-rt", + "cortex-m-semihosting", + "panic-semihosting", + "panic-itm" +] [package.metadata.docs.rs] targets = [ diff --git a/cortex-m-rt/.cargo/config b/cortex-m-rt/.cargo/config new file mode 100644 index 00000000..1662bc61 --- /dev/null +++ b/cortex-m-rt/.cargo/config @@ -0,0 +1,34 @@ +[target.thumbv6m-none-eabi] +runner = "qemu-system-arm -cpu cortex-m0 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.thumbv7m-none-eabi] +runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.thumbv7em-none-eabi] +runner = "qemu-system-arm -cpu cortex-m4 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.thumbv7em-none-eabihf] +runner = "qemu-system-arm -cpu cortex-m4 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# uncomment ONE of these three option to make `cargo run` start a GDB session +# which option to pick depends on your system +# runner = "arm-none-eabi-gdb -q -x openocd.gdb" +# runner = "gdb-multiarch -q -x openocd.gdb" +# runner = "gdb -q -x openocd.gdb" + +rustflags = [ + # LLD (shipped with the Rust toolchain) is used as the default linker + "-C", "link-arg=-Tlink.x", + + # if you run into problems with LLD switch to the GNU linker by commenting out + # this line + # "-C", "linker=arm-none-eabi-ld", + + # if you need to link to pre-compiled C libraries provided by a C toolchain + # use GCC as the linker by commenting out both lines above and then + # uncommenting the three lines below + # "-C", "linker=arm-none-eabi-gcc", + # "-C", "link-arg=-Wl,-Tlink.x", + # "-C", "link-arg=-nostartfiles", +] diff --git a/cortex-m-rt/.gitignore b/cortex-m-rt/.gitignore new file mode 100644 index 00000000..d8748d64 --- /dev/null +++ b/cortex-m-rt/.gitignore @@ -0,0 +1,7 @@ +**/*.rs.bk +.#* +Cargo.lock +bin/*.after +bin/*.before +bin/*.o +target/ diff --git a/cortex-m-rt/CHANGELOG.md b/cortex-m-rt/CHANGELOG.md new file mode 100644 index 00000000..cadeac06 --- /dev/null +++ b/cortex-m-rt/CHANGELOG.md @@ -0,0 +1,630 @@ +# Change Log + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + +## [Unreleased] + +## [v0.7.1] + +## Fixes + +- Fix stack unwinding past `Reset` function ([#337]) + +[#337]: https://github.com/rust-embedded/cortex-m-rt/pull/337 + +## [v0.7.0] + +### New Features + +- Add support for CMSE secure gateway veneers ([#297]). +- Allow using the crate with custom target JSON specs ([#304]). +- Export Exception enum for other crates to use ([#224]). + +[#224]: https://github.com/rust-embedded/cortex-m-rt/pull/224 +[#297]: https://github.com/rust-embedded/cortex-m-rt/pull/297 +[#304]: https://github.com/rust-embedded/cortex-m-rt/pull/304 + +### Fixes + +- Various fixes to the linker script ([#265], [#286], [#287], [#323]). +- Use the correct ABI for the `main` symbol ([#278]). +- Add barriers after FPU enabling ([#279]). +- (ARMv6-M) Set LR value to a known value on reset ([#293]). +- Added CFI and size info to external assembly subroutines (`HardFaultTrampoline` and `FpuTrampoline`) ([#294]). +- Allow building the crate for macOS targets ([#306], [#310]). +- Perform RAM initialization in assembly, to avoid potential UB in Rust ([#301]). +- Perform volatile reads of ICSR in DefaultHandler ([#315]). + +[#265]: https://github.com/rust-embedded/cortex-m-rt/pull/265 +[#278]: https://github.com/rust-embedded/cortex-m-rt/pull/278 +[#279]: https://github.com/rust-embedded/cortex-m-rt/pull/279 +[#286]: https://github.com/rust-embedded/cortex-m-rt/pull/286 +[#287]: https://github.com/rust-embedded/cortex-m-rt/pull/287 +[#293]: https://github.com/rust-embedded/cortex-m-rt/pull/293 +[#294]: https://github.com/rust-embedded/cortex-m-rt/pull/294 +[#301]: https://github.com/rust-embedded/cortex-m-rt/pull/301 +[#306]: https://github.com/rust-embedded/cortex-m-rt/pull/306 +[#310]: https://github.com/rust-embedded/cortex-m-rt/pull/310 +[#315]: https://github.com/rust-embedded/cortex-m-rt/pull/315 +[#323]: https://github.com/rust-embedded/cortex-m-rt/pull/323 + +### Breaking Changes + +- Make `ExceptionFrame`s fields private, adding setters and getters instead + ([#239]). +- Only allow certain attributes on handlers, and apply them to the trampoline + too ([#228]). +- Make it unsafe to define exception handlers for NMIs ([#289]). +- Check that exceptions exist on the target chip when registering a handler for + them ([#308]). + +[#239]: https://github.com/rust-embedded/cortex-m-rt/pull/239 +[#228]: https://github.com/rust-embedded/cortex-m-rt/pull/228 +[#289]: https://github.com/rust-embedded/cortex-m-rt/pull/289 +[#308]: https://github.com/rust-embedded/cortex-m-rt/pull/308 + +### Other + +- Change macros crate to use same version number as cortex-m-rt crate ([#245]). +- Discourage use of `pre_init` in documentation ([#248]). +- Backport: Use `links` in Cargo.toml to prevent multiple linking of + cortex-m-rt ([#276]). + +[#245]: https://github.com/rust-embedded/cortex-m-rt/pull/245 +[#248]: https://github.com/rust-embedded/cortex-m-rt/pull/248 +[#276]: https://github.com/rust-embedded/cortex-m-rt/pull/276 + +## Backport release: [v0.6.15] - 2021-07-12 + +### Fixed + +- Backport: Mark .bss as NOLOAD ([#265]) +- Backport: Fix possible overflow of .data region ([#286]) +- Backport: Perform volatile reads of ICSR in DefaultHandler ([#315]) + +### Other +- Backport: Use `links` in Cargo.toml to prevent multiple linking of + cortex-m-rt ([#276]) +- Backport: Use same verison for macros crate as for cortex-m-rt itself + ([#245]) + +[#245]: https://github.com/rust-embedded/cortex-m-rt/pull/245 +[#265]: https://github.com/rust-embedded/cortex-m-rt/pull/265 +[#276]: https://github.com/rust-embedded/cortex-m-rt/pull/276 +[#286]: https://github.com/rust-embedded/cortex-m-rt/pull/286 +[#315]: https://github.com/rust-embedded/cortex-m-rt/pull/315 + +## [v0.6.14] - 2021-05-19 + +### Fixed + +- Backport: Allow building the crate for macOS targets ([#306], [#310]). + +[#306]: https://github.com/rust-embedded/cortex-m-rt/issues/306 +[#310]: https://github.com/rust-embedded/cortex-m-rt/issues/310 + +## Backport release: [v0.6.13] - 2020-09-07 + +### Fixed + +- (ARMv6-M) Set LR value to a known value on reset (as the ARM spec requires) +- Added CFI and size info to external assembly subroutines (`HardFaultTrampoline`) + +## Backport release: [v0.6.12] - 2020-01-26 + +### Fixed + +- Fixed lint warnings getting emitted on macro-generated code. + +## [v0.6.11] - 2019-12-04 + +### Changed + +- Macros now generate a second trampoline function instead of randomizing the + function's symbol name. This makes the build deterministic. +- [breaking-change] `static mut` resources no longer have `'static` lifetime + except in the `#[entry]` function (this is a soundness fix; see [#212]). + +[#212]: https://github.com/rust-embedded/cortex-m-rt/issues/212 + +## [v0.6.10] - 2019-07-25 + +### Fixed + +- Linker template now takes and discard `*(.ARM.exidx)` + +## [v0.6.9] - 2019-07-11 + +### Added + +- Input `.uninit.*` sections are now collected into an output `.uninit` section. + Uninitialized static variables are meant to be placed in these sections. The + output `.uninit` section is not initialized by the runtime. + +## [v0.6.8] - 2019-04-02 + +### Fixed + +- Correct stack-pointer is selected on `HardFault` +- Linker template now takes and discard `*(.ARM.extab.*)` +- Misc. documentation fixes + +### Changed + +- Architecture added: `armv8-m.main` +- Cortex-M team added to `authors` +- The nightly build is allowed to fail in CI + +## [v0.6.7] - 2018-12-15 + +### Fixed + +- entry / exception / interrupt: `#[cfg]` attributes used on `static mut` + variables are now properly handled. + +## [v0.6.6] - 2018-12-06 + +### Changed + +- Misuse of attributes now produce compiler errors with proper spans instead of + panicking messages. + +- The `HardFault` symbol has been renamed to `HardFaultTrampoline`; likewise the + `UserHardFault` symbol has been renamed to `HardFault`. If you were using + breakpoints on `UserHardFault` to detect hard fault you should now put those + breakpoints on the `HardFault` symbol. + +### Fixed + +- Attributes on local `static mut` variables (declared at the beginning of + `entry` / `interrupt` / `exception`) are now respected. + +- The "GDB can now unwind HardFault callstacks" fix from the previous release + broke `HardFault`'s' `&ExceptionFrame` argument (the pointer was off by 8 + bytes). This release fixes that problem without compromising GDB's ability to + unwind `HardFault`s. + +## [v0.6.5] - 2018-10-23 + +### Changed + +- We now keep `.stack_sizes` by default, for use with external tooling. +- (macros) New `#[interrupt]` attribute added, similar to `#[exception]` for + use with device-specific interrupt handlers. + +### Fixed + +- GDB can now unwind HardFault callstacks + +## [v0.6.4] - 2018-09-25 + +### Changed + +- (macros) Improved the error message when any of the attribute is used on the + wrong kind of item. + +### Fixed + +- (macros) The expansion of the `exception` attribute now uses the `extern "C"` + ABI which is what the hardware expects. + +- (macros) `entry` and `exception` now respect the declared unsafety. That is + `#[entry] unsafe main() -> !` won't require `unsafe` blocks to use `unsafe` + API. + +## [v0.6.3] - 2018-09-09 + +### Fixed + +- Fixed the `rand` problem for real. + +## [v0.6.2] - 2018-09-09 + +### Fixed + +- Worked around a Cargo limitation that broke builds that depend on `rand`. + +- Updated the documentation link in the README to point to working docs. + +## [v0.6.1] - 2018-09-06 + +### Changed + +- Produce a better error message if two (or more) copies of `cortex-m-rt` are + going to be linked into a binary. + +## [v0.6.0] - 2018-09-06 + +### Changed + +- [breaking-change] the `entry!`, `pre_init!` and `exception!` macros have been + replaced with attributes: `#[entry]`, `#[pre_init]` and `#[exception]`, + respectively. This also changes the toolchain requirement to 1.30-beta or + newer. + +## [v0.5.3] - 2018-08-27 + +### Changed + +- This crate no longer depends on `arm-none-eabi-gcc`. + +## [v0.5.2] - 2018-08-11 + +### Added + +* A `pre_init!` macro and related functionality to run a function immediately + after reset, before memory initialisation + +### Changed + +- The `entry!` and `exception!` macros now also accept a closure instead of a path. + +- `DefaultHandler` and `UserHardFault` now default to an infinite loop if left undefined. + +### Fixed + +* Linked script modified to correctly detect `FLASH` overflow caused by `.data` + +## [v0.5.1] - 2018-05-14 + +### Fixed + +- A recompilation problem where this `cortex-m-rt` would be recompiled every time `cargo build` is + invoked. + +## [v0.5.0] - 2018-05-12 + +### Added + +- An `entry!` macro to set the entry point of the program. + +- A `heap_start` function that returns a pointer into the start of the heap region. + +- A `device` feature. When disabled this crate provides the interrupt vectors; when enabled the + interrupt vectors are expected to be provided by another crate. Read the documentation for + details. + +### Changed + +- This crate now compiles on the beta and stable channels. + +- [breaking-change] this crate now requires `arm-none-eabi-gcc` to be installed and available in + `$PATH` to compile. + +- [breaking-change] the `start` lang item has been removed. The standard `main` interface won't + work. Instead use `#![no_main]` and the `entry!` macro. See documentation for details. + +- [breaking-change] the `default_handler!` macro has been merged into the `exception!` macro. Use + `exception!(*, ..)` to set the default exception handler. + +- [breaking-change] there's no weak default handler so a default handler must be defined by the + application, or one of its dependencies. + +- [breaking-change] the syntax of the third argument of the `exception!` handler has changed. See + the documentation of the macro for details. + +- [breaking-change] the exception names that the `exception!` macro accepts has changed to match the + CMSIS specification. See the documentation of the macro for the list of names it accepts. + +- [breaking-change] The number of symbol interfaces has been reduced. Check the advanced section of + the documentation for details. + +## [v0.4.0] - 2018-04-09 + +### Added + +- LLD support. The linker script provided by this crate has been tweaked to support both LLD and GNU + LD. To use LLD as a linker change `.cargo/config` to look like this: + +``` diff + [target.thumbv7m-none-eabi] + rustflags = [ + "-C", "link-arg=-Tlink.x", +- "-C", "linker=arm-none-eabi-ld", +- "-Z", "linker-flavor=ld", ++ "-C", "linker=lld", ++ "-Z", "linker-flavor=ld.lld", + ] +``` + +### Removed + +- [breaking-change] Stack overflow protection has been removed. Unfortunately, supporting this + feature produces totally wrong `arm-none-eabi-size` reports when LLD is used to link the + program. If you need the stack overflow protection feature you can continue to use version + v0.3.13+. + +- [breaking-change] The "abort-on-panic" Cargo feature, which provided a `panic_fmt` implementation, + has been removed. If you were using this feature you can instead use a [panic implementation + crate][panic-impl]. + +[panic-impl]: https://crates.io/keywords/panic-impl + +## [v0.3.15] - 2018-04-08 + +### Fixed + +- Support the newest nightly + +## [v0.3.14] - 2018-04-01 + +### Fixed + +- `dev` channel support + +## [v0.3.13] - 2018-02-17 + +### Added + +- Fictitious `.stack` and `.heap` linker sections that represent the locations of the stack and the + heap in RAM. You can visualize these linker sections by running `arm-none-eabi-size -Ax` over your + binary. + +- Zero cost stack overflow protection when you use the `cortex-m-rt-ld` linker. Check documentation + for details. + +- A `_heap_size` symbol that indicates how large the heap is. This symbol is only used when + `cortex-m-rt-ld` is used as a linker. + +## [v0.3.12] - 2018-01-17 + +### Fixed + +- Support for recent nightlies. + +## [v0.3.11] - 2018-01-17 - YANKED + +### Changed + +- Dynamically support recent nightlies, which have the `termination` lang item, and + nightly-2017-09-22, which doesn't. That nightly version is used by the docs.rs builder. Supporting + that version instead of rejecting it ensures this crate and its reverse-dependencies will get + their documentation built by the docs.rs service. + +## [v0.3.10] - 2018-01-17 - YANKED + +### Removed + +- The nightly date check from build script that improved error messages for users of old, + unsupported nightlies. Unfortunately the check was preventing this crate and reverse-dependencies + from getting their documentation build on docs.rs + +## [v0.3.9] - 2018-01-07 + +### Fixed + +- `cargo doc` warnings + +## [v0.3.8] - 2017-12-29 + +### Added + +- `Termination` lang item + +### Changed + +- The `start` lang item to match the new signature + +## [v0.3.7] - 2017-12-23 + +### Added + +- Support for overriding the DEBUG_MONITOR exception handler on ARMv7-M. + +## [v0.3.6] - 2017-10-03 + +### Fixed + +- Builds with multiple codegen units by forcing the linker to look harder for the exceptions vector + table. + +## [v0.3.5] - 2017-07-21 + +### Fixed + +- Remove duplication of default exception handlers. This saves 32 bytes of Flash + memory (.text). + +## [v0.3.4] - 2017-07-19 + +### Changed + +- Align the end of .rodata to a 4-byte boundary. With this the sections that + will go into Flash memory will be 4 byte aligned at the start and at the + end. Which seems to be required (?) by Cortex-M0 devices. + +- .bss and .data are now padded so their sizes are multiple of 4 bytes. This + improves the output of `objdump`; before, the output showed "Address + 0x20000004 is out of bounds". + +- Linking now aborts if any of the input files contains a .got section. Dynamic + relocations are not supported and Rust code is not relocatable by default. + This error only occurs if C code that was compiled with the -fPIC flag is + linked in. The error message will tell the user how to compile their C code + without -fPIC. + +## [v0.3.3] - 2017-07-14 + +### Changed + +- Updated the documentation: it's no longer necessary to use the + compiler-builtins repository since that crate landed in rust-lang/rust and + it's now available in the `rust-src` component. + +## [v0.3.2] - 2017-07-07 + +### Changed + +- Tweaked documentation + +## [v0.3.1] - 2017-07-07 + +### Fixed + +- A warning when compiling for x86_64 and the "abort-on-panic" feature is + enabled. + +## [v0.3.0] - 2017-07-07 + +### Added + +- A `default_handler!` macro to override the default exception handler. + +- An `exception!` macro to override the handler for a particular exception. + +### Changed + +- The FPU will now be enabled before `main` if the target has FPU support. + +- [breaking-change] the features "panic-over-itm" and "panic-over-semihosting" + has been removed. the `panic_fmt` language item is now *not* included by + default. An opt-in feature named "abort-on-panic" can be enabled to make this + crate provide a `panic_fmt` implementation that simply aborts. + +- [breaking-change] The sections `.rodata.{exceptions,interrupts}` have been + renamed to `.vector_table.{exceptions,interrupts}`. This break the old + mechanism for registering exceptions (`static EXCEPTIONS`); use the new ones: + `default_handler!` and `exception!`. + +- The `_stack_start` is now optional in the `memory.x` file. If unspecified its + value will be set to `ORIGIN(RAM) + LENGTH(RAM)`. + +## [v0.2.4] - 2017-06-03 + +### Added + +- A non-allocatable `.stlog` section to support the [`stlog`] logging framework. + +[`stlog`]: https://crates.io/crates/stlog + +## [v0.2.3] - 2017-05-30 + +### Added + +- A `_stext` symbol which can be specified in the linker script to customize the + location of the `.text` section. If not specified the `.text` section will be + placed right after the `.vector_table` section. + +## [v0.2.2] - 2017-05-27 + +### Added + +- A `_sheap` symbol where the heap can be located. + +### Changed + +- The linker sections have renamed / reorder to make `arm-none-eabi-size -A` + more useful. You'll now see something like this: + +``` +$ arm-none-eabi-size -A hello +hello : +section size addr +.vector_table 1024 134217728 +.text 288 134218752 +.rodata 14 134219040 +``` + +- `cortex-m-rt::reset_handler` is now the entry point of all programs that link + to `cortex-m-rt`. This makes GDB's `load` command work correctly. It will now + set the Program Counter to `reset_handler` after flashing the program so + there's no need to reset the microcontroller after flashing. + +- Renamed `__exceptions` and `__interrupts` symbols, which are only used + internally, to `_eexceptions` and `_einterrupts` respectively for consistency. + +### Fixed + +- Include input `.text` and `.rodata` sections (note: no suffix as in + `.text.foo`) in the output file. (C) Code compiled without the equivalent + `-ffunction-sections` / `-fdata-sections` may place stuff in those unsuffixed + sections. + +## [v0.2.1] - 2017-05-07 + +### Fixed + +- Do not load the `.debug_gdb_script` section in flash. It's only needed for + debugging. + +## [v0.2.0] - 2017-04-27 + +### Changed + +- [breaking-change] the `_stack_start` symbol is now required and must be + provided in the `memory.x` file when using the "linker-script" feature. This + symbol indicates where in memory the call stack will be allocated. + +## [v0.1.3] - 2017-04-25 + +### Fixed + +- A `rustdoc` warning + +## [v0.1.2] - 2017-04-22 + +### Changed + +- Unclutter the `reset_handler` function for a better debugging experience. + +## [v0.1.1] - 2017-04-15 + +### Changed + +- Improved linker error messages + +## v0.1.0 - 2017-04-12 + +Initial release + +[Unreleased]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.7.1...HEAD +[v0.7.1]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.7.0...v0.7.1 +[v0.7.0]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.11...v0.7.0 +[v0.6.15]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.14...v0.6.15 +[v0.6.14]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.13...v0.6.14 +[v0.6.13]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.12...v0.6.13 +[v0.6.12]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.11...v0.6.12 +[v0.6.11]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.10...v0.6.11 +[v0.6.10]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.9...v0.6.10 +[v0.6.9]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.8...v0.6.9 +[v0.6.8]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.7...v0.6.8 +[v0.6.7]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.6...v0.6.7 +[v0.6.6]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.5...v0.6.6 +[v0.6.5]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.4...v0.6.5 +[v0.6.4]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.3...v0.6.4 +[v0.6.3]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.2...v0.6.3 +[v0.6.2]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.1...v0.6.2 +[v0.6.1]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.0...v0.6.1 +[v0.6.0]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.5.3...v0.6.0 +[v0.5.3]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.5.2...v0.5.3 +[v0.5.2]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.5.1...v0.5.2 +[v0.5.1]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.5.0...v0.5.1 +[v0.5.0]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.4.0...v0.5.0 +[v0.4.0]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.15...v0.4.0 +[v0.3.15]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.14...v0.3.15 +[v0.3.14]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.13...v0.3.14 +[v0.3.13]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.12...v0.3.13 +[v0.3.12]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.11...v0.3.12 +[v0.3.11]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.10...v0.3.11 +[v0.3.10]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.9...v0.3.10 +[v0.3.9]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.8...v0.3.9 +[v0.3.8]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.7...v0.3.8 +[v0.3.7]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.6...v0.3.7 +[v0.3.6]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.5...v0.3.6 +[v0.3.5]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.4...v0.3.5 +[v0.3.4]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.3...v0.3.4 +[v0.3.3]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.2...v0.3.3 +[v0.3.2]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.1...v0.3.2 +[v0.3.1]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.3.0...v0.3.1 +[v0.3.0]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.2.4...v0.3.0 +[v0.2.4]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.2.3...v0.2.4 +[v0.2.3]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.2.2...v0.2.3 +[v0.2.2]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.2.1...v0.2.2 +[v0.2.1]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.2.0...v0.2.1 +[v0.2.0]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.1.3...v0.2.0 +[v0.1.3]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.1.2...v0.1.3 +[v0.1.2]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.1.1...v0.1.2 +[v0.1.1]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.1.0...v0.1.1 diff --git a/cortex-m-rt/CODE_OF_CONDUCT.md b/cortex-m-rt/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..3ab76c63 --- /dev/null +++ b/cortex-m-rt/CODE_OF_CONDUCT.md @@ -0,0 +1,37 @@ +# The Rust Code of Conduct + +## Conduct + +**Contact**: [Cortex-M team](https://github.com/rust-embedded/wg#the-cortex-m-team) + +* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. +* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. +* Please be kind and courteous. There's no need to be mean or rude. +* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. +* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. +* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. +* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Cortex-M team][team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back. +* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. + +## Moderation + +These are the policies for upholding our community's standards of conduct. + +1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) +2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. +3. Moderators will first respond to such remarks with a warning. +4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. +5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. +6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology. +7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed. +8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others. + +In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. + +And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. + +The enforcement policies listed above apply to all official embedded WG venues; including official IRC channels (#rust-embedded); GitHub repositories under rust-embedded; and all forums under rust-embedded.org (forum.rust-embedded.org). + +*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* + +[team]: https://github.com/rust-embedded/wg#the-cortex-m-team diff --git a/cortex-m-rt/Cargo.toml b/cortex-m-rt/Cargo.toml new file mode 100644 index 00000000..33a08eb6 --- /dev/null +++ b/cortex-m-rt/Cargo.toml @@ -0,0 +1,46 @@ +[package] +authors = [ + "The Cortex-M Team ", + "Jorge Aparicio ", + "Hideki Sekine ", +] +categories = ["embedded", "no-std"] +description = "Minimal runtime / startup for Cortex-M microcontrollers" +documentation = "https://docs.rs/cortex-m-rt/" +keywords = ["arm", "cortex-m", "runtime", "startup"] +license = "MIT OR Apache-2.0" +name = "cortex-m-rt" +readme = "README.md" +repository = "https://github.com/rust-embedded/cortex-m" +version = "0.7.1" +autoexamples = true +links = "cortex-m-rt" # Prevent multiple versions of cortex-m-rt being linked + +[dependencies] +cortex-m-rt-macros = { path = "macros", version = "=0.7.0" } + +[dev-dependencies] +cortex-m = { version = "0.7.4", path = ".." } +panic-halt = "0.2.0" +cortex-m-semihosting = { path = "../cortex-m-semihosting" } + +[target.'cfg(not(target_os = "none"))'.dev-dependencies] +compiletest_rs = "0.7" + +[[example]] +name = "device" +required-features = ["device"] + +[[example]] +name = "warnings" +required-features = ["device"] + +[[test]] +name = "compiletest" +required-features = ["device"] + +[features] +device = [] + +[package.metadata.docs.rs] +features = ["device"] diff --git a/cortex-m-rt/LICENSE-APACHE b/cortex-m-rt/LICENSE-APACHE new file mode 100644 index 00000000..16fe87b0 --- /dev/null +++ b/cortex-m-rt/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. diff --git a/cortex-m-rt/LICENSE-MIT b/cortex-m-rt/LICENSE-MIT new file mode 100644 index 00000000..a128ba40 --- /dev/null +++ b/cortex-m-rt/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017 Jorge Aparicio + +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. diff --git a/cortex-m-rt/README.md b/cortex-m-rt/README.md new file mode 100644 index 00000000..98577204 --- /dev/null +++ b/cortex-m-rt/README.md @@ -0,0 +1,41 @@ +[![crates.io](https://img.shields.io/crates/v/cortex-m-rt.svg)](https://crates.io/crates/cortex-m-rt) +[![crates.io](https://img.shields.io/crates/d/cortex-m-rt.svg)](https://crates.io/crates/cortex-m-rt) + +# `cortex-m-rt` + +> Startup code and minimal runtime for Cortex-M microcontrollers + +This project is developed and maintained by the [Cortex-M team][team]. + +# [Documentation](https://docs.rs/cortex-m-rt) + +# Minimum Supported Rust Version (MSRV) + +This crate is guaranteed to compile on stable Rust 1.40.0 and up. It *might* +compile with older versions but that may change in any new patch release. + +# License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +## Code of Conduct + +Contribution to this crate is organized under the terms of the [Rust Code of +Conduct][CoC], the maintainer of this crate, the [Cortex-M team][team], promises +to intervene to uphold that code of conduct. + +[CoC]: CODE_OF_CONDUCT.md +[team]: https://github.com/rust-embedded/wg#the-cortex-m-team diff --git a/cortex-m-rt/asm.S b/cortex-m-rt/asm.S new file mode 100644 index 00000000..0d078b37 --- /dev/null +++ b/cortex-m-rt/asm.S @@ -0,0 +1,113 @@ + .cfi_sections .debug_frame + + # Notes for function attributes: + # .type and .thumb_func are _both_ required, otherwise the Thumb mode bit + # will not be set and an invalid vector table is generated. + # LLD requires that section flags are set explicitly. + + .section .HardFaultTrampoline, "ax" + .global HardFaultTrampoline + .type HardFaultTrampoline,%function + .thumb_func + .cfi_startproc + # HardFault exceptions are bounced through this trampoline which grabs the + # stack pointer at the time of the exception and passes it to the user's + # HardFault handler in r0. +HardFaultTrampoline: + # Depending on the stack mode in EXC_RETURN, fetch stack pointer from + # PSP or MSP. + mov r0, lr + mov r1, #4 + tst r0, r1 + bne 0f + mrs r0, MSP + b HardFault +0: + mrs r0, PSP + b HardFault + .cfi_endproc + .size HardFaultTrampoline, . - HardFaultTrampoline + + .section .Reset, "ax" + .global Reset + .type Reset,%function + .thumb_func + .cfi_startproc + # Main entry point after reset. This jumps to the user __pre_init function, + # which cannot be called from Rust code without invoking UB, then + # initialises RAM. If the target has an FPU, it is enabled. Finally, jumps + # to the user main function. +Reset: + # ARMv6-M does not initialise LR, but many tools expect it to be 0xFFFF_FFFF + # when reaching the first call frame, so we set it at startup. + # ARMv7-M and above initialise LR to 0xFFFF_FFFF at reset. + ldr r4,=0xffffffff + mov lr,r4 + + # Run user pre-init code, which must be executed immediately after startup, + # before the potentially time-consuming memory initialisation takes place. + # Example use cases include disabling default watchdogs or enabling RAM. + bl __pre_init + + # Restore LR after calling __pre_init (r4 is preserved by subroutines). + mov lr,r4 + + # Initialise .bss memory. `__sbss` and `__ebss` come from the linker script. + ldr r0,=__sbss + ldr r1,=__ebss + mov r2,#0 +0: + cmp r1, r0 + beq 1f + stm r0!, {r2} + b 0b +1: + + # Initialise .data memory. `__sdata`, `__sidata`, and `__edata` come from the + # linker script. Copy from r2 into r0 until r0 reaches r1. + ldr r0,=__sdata + ldr r1,=__edata + ldr r2,=__sidata +2: + cmp r1, r0 + beq 3f + # load 1 word from r2 to r3, inc r2 + ldm r2!, {r3} + # store 1 word from r3 to r0, inc r0 + stm r0!, {r3} + b 2b +3: + +#ifdef HAS_FPU + # Conditionally enable the FPU. + # Address of SCB.CPACR. + ldr r0, =0xE000ED88 + # Enable access to CP10 and CP11 from both privileged and unprivileged mode. + ldr r1, =(0b1111 << 20) + # RMW. + ldr r2, [r0] + orr r2, r2, r1 + str r2, [r0] + # Barrier is required on some processors. + dsb + isb +#endif + +4: + # Preserve `lr` and emit debuginfo that lets external tools restore it. + # This fixes unwinding past the `Reset` handler. + # See https://sourceware.org/binutils/docs/as/CFI-directives.html for an + # explanation of the directives. +.cfi_def_cfa sp, 0 + push {lr} +.cfi_offset lr, 0 + + # Jump to user main function. We use bl for the extended range, but the + # user main function may not return. + bl main + + # Trap on return. + udf + + .cfi_endproc + .size Reset, . - Reset diff --git a/cortex-m-rt/assemble.sh b/cortex-m-rt/assemble.sh new file mode 100755 index 00000000..9b1f15c4 --- /dev/null +++ b/cortex-m-rt/assemble.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +# cflags taken from cc 1.0.22 + +crate=cortex-m-rt + +# remove existing blobs because otherwise this will append object files to the old blobs +rm -f bin/*.a + +arm-none-eabi-gcc -g -c -march=armv6s-m asm.S -o bin/$crate.o +ar crs bin/thumbv6m-none-eabi.a bin/$crate.o + +arm-none-eabi-gcc -g -c -march=armv7-m asm.S -o bin/$crate.o +ar crs bin/thumbv7m-none-eabi.a bin/$crate.o + +arm-none-eabi-gcc -g -c -march=armv7e-m asm.S -o bin/$crate.o +ar crs bin/thumbv7em-none-eabi.a bin/$crate.o + +arm-none-eabi-gcc -g -c -march=armv7e-m asm.S -DHAS_FPU -o bin/$crate.o +ar crs bin/thumbv7em-none-eabihf.a bin/$crate.o + +arm-none-eabi-gcc -g -c -march=armv8-m.base asm.S -o bin/$crate.o +ar crs bin/thumbv8m.base-none-eabi.a bin/$crate.o + +arm-none-eabi-gcc -g -c -march=armv8-m.main asm.S -o bin/$crate.o +ar crs bin/thumbv8m.main-none-eabi.a bin/$crate.o + +arm-none-eabi-gcc -g -c -march=armv8-m.main -DHAS_FPU asm.S -o bin/$crate.o +ar crs bin/thumbv8m.main-none-eabihf.a bin/$crate.o + +rm bin/$crate.o diff --git a/cortex-m-rt/bin/thumbv6m-none-eabi.a b/cortex-m-rt/bin/thumbv6m-none-eabi.a new file mode 100644 index 00000000..c145cc61 Binary files /dev/null and b/cortex-m-rt/bin/thumbv6m-none-eabi.a differ diff --git a/cortex-m-rt/bin/thumbv7em-none-eabi.a b/cortex-m-rt/bin/thumbv7em-none-eabi.a new file mode 100644 index 00000000..2d6b6a1f Binary files /dev/null and b/cortex-m-rt/bin/thumbv7em-none-eabi.a differ diff --git a/cortex-m-rt/bin/thumbv7em-none-eabihf.a b/cortex-m-rt/bin/thumbv7em-none-eabihf.a new file mode 100644 index 00000000..aa765ea3 Binary files /dev/null and b/cortex-m-rt/bin/thumbv7em-none-eabihf.a differ diff --git a/cortex-m-rt/bin/thumbv7m-none-eabi.a b/cortex-m-rt/bin/thumbv7m-none-eabi.a new file mode 100644 index 00000000..3d1783c6 Binary files /dev/null and b/cortex-m-rt/bin/thumbv7m-none-eabi.a differ diff --git a/cortex-m-rt/bin/thumbv8m.base-none-eabi.a b/cortex-m-rt/bin/thumbv8m.base-none-eabi.a new file mode 100644 index 00000000..a9fb4345 Binary files /dev/null and b/cortex-m-rt/bin/thumbv8m.base-none-eabi.a differ diff --git a/cortex-m-rt/bin/thumbv8m.main-none-eabi.a b/cortex-m-rt/bin/thumbv8m.main-none-eabi.a new file mode 100644 index 00000000..40a5c512 Binary files /dev/null and b/cortex-m-rt/bin/thumbv8m.main-none-eabi.a differ diff --git a/cortex-m-rt/bin/thumbv8m.main-none-eabihf.a b/cortex-m-rt/bin/thumbv8m.main-none-eabihf.a new file mode 100644 index 00000000..6c523af3 Binary files /dev/null and b/cortex-m-rt/bin/thumbv8m.main-none-eabihf.a differ diff --git a/cortex-m-rt/build.rs b/cortex-m-rt/build.rs new file mode 100644 index 00000000..96a8560d --- /dev/null +++ b/cortex-m-rt/build.rs @@ -0,0 +1,90 @@ +use std::fs::{self, File}; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::{env, ffi::OsStr}; + +fn main() { + let mut target = env::var("TARGET").unwrap(); + + // When using a custom target JSON, `$TARGET` contains the path to that JSON file. By + // convention, these files are named after the actual target triple, eg. + // `thumbv7m-customos-elf.json`, so we extract the file stem here to allow custom target specs. + let path = Path::new(&target); + if path.extension() == Some(OsStr::new("json")) { + target = path + .file_stem() + .map_or(target.clone(), |stem| stem.to_str().unwrap().to_string()); + } + + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + if target.starts_with("thumbv") { + let lib_path = format!("bin/{}.a", target); + fs::copy(&lib_path, out_dir.join("libcortex-m-rt.a")).unwrap(); + println!("cargo:rustc-link-lib=static=cortex-m-rt"); + println!("cargo:rerun-if-changed={}", lib_path); + } + + // Put the linker script somewhere the linker can find it + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let link_x = include_bytes!("link.x.in"); + let mut f = if env::var_os("CARGO_FEATURE_DEVICE").is_some() { + let mut f = File::create(out.join("link.x")).unwrap(); + + f.write_all(link_x).unwrap(); + + // *IMPORTANT*: The weak aliases (i.e. `PROVIDED`) must come *after* `EXTERN(__INTERRUPTS)`. + // Otherwise the linker will ignore user defined interrupts and always populate the table + // with the weak aliases. + writeln!( + f, + r#" +/* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */ +/* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */ +INCLUDE device.x"# + ) + .unwrap(); + f + } else { + let mut f = File::create(out.join("link.x")).unwrap(); + f.write_all(link_x).unwrap(); + f + }; + + let max_int_handlers = if target.starts_with("thumbv6m-") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv6m"); + 32 + } else if target.starts_with("thumbv7m-") || target.starts_with("thumbv7em-") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv7m"); + 240 + } else if target.starts_with("thumbv8m") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv8m"); + 240 + } else { + // Non ARM target. We assume you're just testing the syntax. + // This value seems as soon as any + 240 + }; + + // checking the size of the interrupts portion of the vector table is sub-architecture dependent + writeln!( + f, + r#" +ASSERT(SIZEOF(.vector_table) <= 0x{:x}, " +There can't be more than {1} interrupt handlers. This may be a bug in +your device crate, or you may have registered more than {1} interrupt +handlers."); +"#, + max_int_handlers * 4 + 0x40, + max_int_handlers + ) + .unwrap(); + + println!("cargo:rustc-link-search={}", out.display()); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=link.x.in"); +} diff --git a/cortex-m-rt/check-blobs.sh b/cortex-m-rt/check-blobs.sh new file mode 100755 index 00000000..166b4a4c --- /dev/null +++ b/cortex-m-rt/check-blobs.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +# Checks that the blobs are up to date with the committed assembly files + +set -euxo pipefail + +for lib in bin/*.a; do + filename=$(basename "$lib") + arm-none-eabi-objdump -Cd "$lib" > "bin/${filename%.a}.before" +done + +./assemble.sh + +for lib in bin/*.a; do + filename=$(basename "$lib") + arm-none-eabi-objdump -Cd "$lib" > "bin/${filename%.a}.after" +done + +for cksum in bin/*.after; do + diff -u "$cksum" "${cksum%.after}.before" +done diff --git a/cortex-m-rt/ci/script.sh b/cortex-m-rt/ci/script.sh new file mode 100755 index 00000000..08ff863e --- /dev/null +++ b/cortex-m-rt/ci/script.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +main() { + cargo check --target "$TARGET" + + cargo check --target "$TARGET" --features device + + if [ "$TARGET" = x86_64-unknown-linux-gnu ] && [ "$TRAVIS_RUST_VERSION" = stable ]; then + ( cd macros && cargo check && cargo test ) + + cargo test --features device --test compiletest + fi + + local examples=( + alignment + divergent-default-handler + divergent-exception + entry-static + main + minimal + override-exception + pre_init + qemu + state + unsafe-default-handler + unsafe-entry + unsafe-exception + unsafe-hard-fault + ) + local fail_examples=( + data_overflow + ) + local linkers=( + # Link with arm-none-eabi-ld + "-C linker=arm-none-eabi-ld" + # Link with arm-none-eabi-gcc, requires -nostartfiles + "-C linker=arm-none-eabi-gcc -C link-arg=-nostartfiles" + # Link with rust-lld (default) + "" + ) + if [ "$TARGET" != x86_64-unknown-linux-gnu ]; then + # Only test on stable and nightly, not MSRV. + if [ "$TRAVIS_RUST_VERSION" = stable ] || [ "$TRAVIS_RUST_VERSION" = nightly ]; then + RUSTDOCFLAGS="-Cpanic=abort" cargo test --doc + fi + + for linker in "${linkers[@]}"; do + for ex in "${examples[@]}"; do + cargo rustc --target "$TARGET" --example "$ex" -- $linker + cargo rustc --target "$TARGET" --example "$ex" --release -- $linker + done + for ex in "${fail_examples[@]}"; do + ! cargo rustc --target "$TARGET" --example "$ex" -- $linker + ! cargo rustc --target "$TARGET" --example "$ex" --release -- $linker + done + cargo rustc --target "$TARGET" --example device --features device -- $linker + cargo rustc --target "$TARGET" --example device --features device --release -- $linker + done + fi + + case $TARGET in + thumbv6m-none-eabi|thumbv7m-none-eabi) + for linker in "${linkers[@]}"; do + env RUSTFLAGS="$linker -C link-arg=-Tlink.x" cargo run \ + --target "$TARGET" --example qemu | grep "x = 42" + env RUSTFLAGS="$linker -C link-arg=-Tlink.x" cargo run \ + --target "$TARGET" --example qemu --release | grep "x = 42" + done + + ;; + esac + + if [ "$TARGET" = x86_64-unknown-linux-gnu ]; then + ./check-blobs.sh + fi +} + +main diff --git a/cortex-m-rt/examples/alignment.rs b/cortex-m-rt/examples/alignment.rs new file mode 100644 index 00000000..4421e690 --- /dev/null +++ b/cortex-m-rt/examples/alignment.rs @@ -0,0 +1,33 @@ +//! This is not an example; this is a link-pass test + +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt as rt; +extern crate panic_halt; + +use core::ptr; + +use rt::entry; + +static mut BSS1: u16 = 0; +static mut BSS2: u8 = 0; +static mut DATA1: u8 = 1; +static mut DATA2: u16 = 1; +static RODATA1: &[u8; 3] = b"012"; +static RODATA2: &[u8; 2] = b"34"; + +#[entry] +fn main() -> ! { + unsafe { + let _bss1 = ptr::read_volatile(&BSS1); + let _bss2 = ptr::read_volatile(&BSS2); + let _data1 = ptr::read_volatile(&DATA1); + let _data2 = ptr::read_volatile(&DATA2); + let _rodata1 = ptr::read_volatile(&RODATA1); + let _rodata2 = ptr::read_volatile(&RODATA2); + } + + loop {} +} diff --git a/cortex-m-rt/examples/cfg-static.rs b/cortex-m-rt/examples/cfg-static.rs new file mode 100644 index 00000000..2ffee138 --- /dev/null +++ b/cortex-m-rt/examples/cfg-static.rs @@ -0,0 +1,25 @@ +//! using `#[cfg]` on `static` shouldn't cause compile errors + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt as rt; +extern crate panic_halt; + +use rt::{entry, exception}; + +#[entry] +fn main() -> ! { + #[cfg(never)] + static mut COUNT: u32 = 0; + + loop {} +} + +#[exception] +fn SysTick() { + #[cfg(never)] + static mut FOO: u32 = 0; +} diff --git a/cortex-m-rt/examples/data_overflow.rs b/cortex-m-rt/examples/data_overflow.rs new file mode 100644 index 00000000..a84ec122 --- /dev/null +++ b/cortex-m-rt/examples/data_overflow.rs @@ -0,0 +1,30 @@ +//! This is not an example; this is a linker overflow detection test +//! which should fail to link due to .data overflowing FLASH. + +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt as rt; +extern crate panic_halt; + +use core::ptr; + +use rt::entry; + +// This large static array uses most of .rodata +static RODATA: [u8; 48 * 1024] = [1u8; 48 * 1024]; + +// This large mutable array causes .data to use the rest of FLASH +// without also overflowing RAM. +static mut DATA: [u8; 16 * 1024] = [1u8; 16 * 1024]; + +#[entry] +fn main() -> ! { + unsafe { + let _bigdata = ptr::read_volatile(&RODATA as *const u8); + let _bigdata = ptr::read_volatile(&DATA as *const u8); + } + + loop {} +} diff --git a/cortex-m-rt/examples/device.rs b/cortex-m-rt/examples/device.rs new file mode 100644 index 00000000..c18b5691 --- /dev/null +++ b/cortex-m-rt/examples/device.rs @@ -0,0 +1,36 @@ +//! Manually create the interrupts portion of the vector table + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt as rt; +extern crate panic_halt; + +use rt::entry; + +#[entry] +fn main() -> ! { + loop {} +} + +// interrupts portion of the vector table +pub union Vector { + handler: unsafe extern "C" fn(), + reserved: usize, +} + +extern "C" { + fn WWDG(); + fn PVD(); +} + +#[allow(unsafe_code)] +#[link_section = ".vector_table.interrupts"] +#[no_mangle] +pub static __INTERRUPTS: [Vector; 3] = [ + Vector { handler: WWDG }, + Vector { reserved: 0 }, + Vector { handler: PVD }, +]; diff --git a/cortex-m-rt/examples/divergent-default-handler.rs b/cortex-m-rt/examples/divergent-default-handler.rs new file mode 100644 index 00000000..32902540 --- /dev/null +++ b/cortex-m-rt/examples/divergent-default-handler.rs @@ -0,0 +1,18 @@ +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +unsafe fn DefaultHandler(_irqn: i16) -> ! { + loop {} +} diff --git a/cortex-m-rt/examples/divergent-exception.rs b/cortex-m-rt/examples/divergent-exception.rs new file mode 100644 index 00000000..cb2247ba --- /dev/null +++ b/cortex-m-rt/examples/divergent-exception.rs @@ -0,0 +1,18 @@ +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +fn SysTick() -> ! { + loop {} +} diff --git a/cortex-m-rt/examples/entry-static.rs b/cortex-m-rt/examples/entry-static.rs new file mode 100644 index 00000000..55e7a89e --- /dev/null +++ b/cortex-m-rt/examples/entry-static.rs @@ -0,0 +1,20 @@ +//! `static mut` variables local to the entry point are safe to use + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt as rt; +extern crate panic_halt; + +use rt::entry; + +#[entry] +fn main() -> ! { + static mut COUNT: u32 = 0; + + loop { + *COUNT += 1; + } +} diff --git a/cortex-m-rt/examples/main.rs b/cortex-m-rt/examples/main.rs new file mode 100644 index 00000000..58e3cb48 --- /dev/null +++ b/cortex-m-rt/examples/main.rs @@ -0,0 +1,13 @@ +//! Directly plug a `main` symbol instead of using `#[entry]` + +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt as rt; +extern crate panic_halt; + +#[no_mangle] +pub unsafe extern "C" fn main() -> ! { + loop {} +} diff --git a/cortex-m-rt/examples/minimal.rs b/cortex-m-rt/examples/minimal.rs new file mode 100644 index 00000000..bd0a6adb --- /dev/null +++ b/cortex-m-rt/examples/minimal.rs @@ -0,0 +1,17 @@ +//! Minimal `cortex-m-rt` based program + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt as rt; +extern crate panic_halt; + +use rt::entry; + +// the program entry point +#[entry] +fn main() -> ! { + loop {} +} diff --git a/cortex-m-rt/examples/override-exception.rs b/cortex-m-rt/examples/override-exception.rs new file mode 100644 index 00000000..3190b77d --- /dev/null +++ b/cortex-m-rt/examples/override-exception.rs @@ -0,0 +1,29 @@ +//! How to override the hard fault exception handler and the default exception handler + +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m; +extern crate cortex_m_rt as rt; +extern crate panic_halt; + +use cortex_m::asm; +use rt::{entry, exception, ExceptionFrame}; + +#[entry] +fn main() -> ! { + loop {} +} + +#[exception] +unsafe fn DefaultHandler(_irqn: i16) { + asm::bkpt(); +} + +#[exception] +unsafe fn HardFault(_ef: &ExceptionFrame) -> ! { + asm::bkpt(); + + loop {} +} diff --git a/cortex-m-rt/examples/pre_init.rs b/cortex-m-rt/examples/pre_init.rs new file mode 100644 index 00000000..2c931bbd --- /dev/null +++ b/cortex-m-rt/examples/pre_init.rs @@ -0,0 +1,20 @@ +//! `cortex-m-rt` based program with a function run before RAM is initialized. + +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt as rt; +extern crate panic_halt; + +use rt::{entry, pre_init}; + +#[pre_init] +unsafe fn disable_watchdog() { + // Do what you need to disable the watchdog. +} + +#[entry] +fn main() -> ! { + loop {} +} diff --git a/cortex-m-rt/examples/qemu.rs b/cortex-m-rt/examples/qemu.rs new file mode 100644 index 00000000..e903404a --- /dev/null +++ b/cortex-m-rt/examples/qemu.rs @@ -0,0 +1,28 @@ +// #![feature(stdsimd)] +#![no_main] +#![no_std] + +extern crate cortex_m; +extern crate cortex_m_rt as rt; +extern crate cortex_m_semihosting as semihosting; + +extern crate panic_halt; + +use cortex_m::asm; +use rt::entry; + +#[entry] +fn main() -> ! { + use core::fmt::Write; + let x = 42; + + loop { + asm::nop(); + + // write something through semihosting interface + let mut hstdout = semihosting::hio::hstdout().unwrap(); + write!(hstdout, "x = {}\n", x).unwrap(); + // exit from qemu + semihosting::debug::exit(semihosting::debug::EXIT_SUCCESS); + } +} diff --git a/cortex-m-rt/examples/state.rs b/cortex-m-rt/examples/state.rs new file mode 100644 index 00000000..ee6224d5 --- /dev/null +++ b/cortex-m-rt/examples/state.rs @@ -0,0 +1,24 @@ +//! Preserving state across executions of an exception handler + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt as rt; +extern crate panic_halt; + +use rt::{entry, exception}; + +#[entry] +fn main() -> ! { + loop {} +} + +// exception handler with state +#[exception] +fn SysTick() { + static mut STATE: u32 = 0; + + *STATE += 1; +} diff --git a/cortex-m-rt/examples/unsafe-default-handler.rs b/cortex-m-rt/examples/unsafe-default-handler.rs new file mode 100644 index 00000000..a805c120 --- /dev/null +++ b/cortex-m-rt/examples/unsafe-default-handler.rs @@ -0,0 +1,16 @@ +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +unsafe fn DefaultHandler(_irqn: i16) {} diff --git a/cortex-m-rt/examples/unsafe-entry.rs b/cortex-m-rt/examples/unsafe-entry.rs new file mode 100644 index 00000000..9dcbf336 --- /dev/null +++ b/cortex-m-rt/examples/unsafe-entry.rs @@ -0,0 +1,13 @@ +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::entry; + +#[entry] +unsafe fn foo() -> ! { + loop {} +} diff --git a/cortex-m-rt/examples/unsafe-exception.rs b/cortex-m-rt/examples/unsafe-exception.rs new file mode 100644 index 00000000..4212610d --- /dev/null +++ b/cortex-m-rt/examples/unsafe-exception.rs @@ -0,0 +1,16 @@ +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +unsafe fn SysTick() {} diff --git a/cortex-m-rt/examples/unsafe-hard-fault.rs b/cortex-m-rt/examples/unsafe-hard-fault.rs new file mode 100644 index 00000000..b1d48f34 --- /dev/null +++ b/cortex-m-rt/examples/unsafe-hard-fault.rs @@ -0,0 +1,18 @@ +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, ExceptionFrame}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +unsafe fn HardFault(_ef: &ExceptionFrame) -> ! { + loop {} +} diff --git a/cortex-m-rt/examples/unsafety.rs b/cortex-m-rt/examples/unsafety.rs new file mode 100644 index 00000000..cdb5acaf --- /dev/null +++ b/cortex-m-rt/examples/unsafety.rs @@ -0,0 +1,36 @@ +//! Checks that the declared unsafety is respected by the attributes + +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, ExceptionFrame}; + +#[entry] +unsafe fn main() -> ! { + foo(); + + loop {} +} + +#[exception] +unsafe fn DefaultHandler(_irqn: i16) { + foo(); +} + +#[exception] +unsafe fn HardFault(_ef: &ExceptionFrame) -> ! { + foo(); + + loop {} +} + +#[exception] +unsafe fn SysTick() { + foo(); +} + +unsafe fn foo() {} diff --git a/cortex-m-rt/examples/warnings.rs b/cortex-m-rt/examples/warnings.rs new file mode 100644 index 00000000..33720031 --- /dev/null +++ b/cortex-m-rt/examples/warnings.rs @@ -0,0 +1,50 @@ +//! Tests that a crate can still build with all warnings enabled. +//! +//! The code generated by the `cortex-m-rt` macros might need to manually +//! `#[allow]` some of them (even though Rust does that by default for a few +//! warnings too). + +#![no_std] +#![no_main] +#![deny(warnings, missing_docs, rust_2018_idioms)] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt, pre_init, ExceptionFrame}; + +#[allow(non_camel_case_types)] +enum interrupt { + INT, +} + +extern "C" { + fn INT(); +} + +union Vector { + #[allow(dead_code)] + handler: unsafe extern "C" fn(), +} + +#[link_section = ".vector_table.interrupts"] +#[no_mangle] +#[used] +static __INTERRUPTS: [Vector; 1] = [Vector { handler: INT }]; + +/// Dummy interrupt. +#[interrupt] +fn INT() {} + +#[exception] +fn HardFault(_eh: &ExceptionFrame) -> ! { + loop {} +} + +#[entry] +fn main() -> ! { + loop {} +} + +#[pre_init] +unsafe fn pre_init() {} diff --git a/cortex-m-rt/link.x.in b/cortex-m-rt/link.x.in new file mode 100644 index 00000000..92004b7f --- /dev/null +++ b/cortex-m-rt/link.x.in @@ -0,0 +1,247 @@ +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut __sbss }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol if not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization + routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see + "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +/* Provides information about the memory layout of the device */ +/* This will be provided by the user (see `memory.x`) or by a Board Support Crate */ +INCLUDE memory.x + +/* # Entry point = reset vector */ +EXTERN(__RESET_VECTOR); +EXTERN(Reset); +ENTRY(Reset); + +/* # Exception vectors */ +/* This is effectively weak aliasing at the linker level */ +/* The user can override any of these aliases by defining the corresponding symbol themselves (cf. + the `exception!` macro) */ +EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */ + +EXTERN(DefaultHandler); + +PROVIDE(NonMaskableInt = DefaultHandler); +EXTERN(HardFaultTrampoline); +PROVIDE(MemoryManagement = DefaultHandler); +PROVIDE(BusFault = DefaultHandler); +PROVIDE(UsageFault = DefaultHandler); +PROVIDE(SecureFault = DefaultHandler); +PROVIDE(SVCall = DefaultHandler); +PROVIDE(DebugMonitor = DefaultHandler); +PROVIDE(PendSV = DefaultHandler); +PROVIDE(SysTick = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultHandler_); +PROVIDE(HardFault = HardFault_); + +/* # Interrupt vectors */ +EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */ + +/* # Pre-initialization function */ +/* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = DefaultPreInit); + +/* # Sections */ +SECTIONS +{ + PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); + + /* ## Sections in FLASH */ + /* ### Vector table */ + .vector_table ORIGIN(FLASH) : + { + /* Initial Stack Pointer (SP) value */ + LONG(_stack_start); + + /* Reset vector */ + KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */ + __reset_vector = .; + + /* Exceptions */ + KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */ + __eexceptions = .; + + /* Device specific interrupts */ + KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */ + } > FLASH + + PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table)); + + /* ### .text */ + .text _stext : + { + __stext = .; + *(.Reset); + + *(.text .text.*); + + /* The HardFaultTrampoline uses the `b` instruction to enter `HardFault`, + so must be placed close to it. */ + *(.HardFaultTrampoline); + *(.HardFault.*); + + . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */ + __etext = .; + } > FLASH + + /* ### .rodata */ + .rodata : ALIGN(4) + { + . = ALIGN(4); + __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 = .; + } > FLASH + + /* ## Sections in RAM */ + /* ### .data */ + .data : ALIGN(4) + { + . = ALIGN(4); + __sdata = .; + *(.data .data.*); + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + } > RAM AT>FLASH + /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to + * use the .data loading mechanism by pushing __edata. Note: do not change + * output region or load region in those user sections! */ + . = ALIGN(4); + __edata = .; + + /* LMA of .data */ + __sidata = LOADADDR(.data); + + /* ### .gnu.sgstubs + This section contains the TrustZone-M veneers put there by the Arm GNU linker. */ + /* Security Attribution Unit blocks must be 32 bytes aligned. */ + /* Note that this pads the FLASH usage to 32 byte alignment. */ + .gnu.sgstubs : ALIGN(32) + { + . = ALIGN(32); + __veneer_base = .; + *(.gnu.sgstubs*) + . = ALIGN(32); + __veneer_limit = .; + } > FLASH + + /* ### .bss */ + .bss (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __sbss = .; + *(.bss .bss.*); + *(COMMON); /* Uninitialized C statics */ + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + } > RAM + /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to + * use the .bss zeroing mechanism by pushing __ebss. Note: do not change + * output region or load region in those user sections! */ + . = ALIGN(4); + __ebss = .; + + /* ### .uninit */ + .uninit (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __suninit = .; + *(.uninit .uninit.*); + . = ALIGN(4); + __euninit = .; + } > RAM + + /* Place the heap right after `.uninit` in RAM */ + PROVIDE(__sheap = __euninit); + + /* ## .got */ + /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in + the input files and raise an error if relocatable code is found */ + .got (NOLOAD) : + { + KEEP(*(.got .got.*)); + } + + /* ## Discarded sections */ + /DISCARD/ : + { + /* Unused exception related info that only wastes space */ + *(.ARM.exidx); + *(.ARM.exidx.*); + *(.ARM.extab.*); + } +} + +/* Do not exceed this mark in the error messages below | */ +/* # Alignment checks */ +ASSERT(ORIGIN(FLASH) % 4 == 0, " +ERROR(cortex-m-rt): the start of the FLASH region must be 4-byte aligned"); + +ASSERT(ORIGIN(RAM) % 4 == 0, " +ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned"); + +ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, " +BUG(cortex-m-rt): .data is not 4-byte aligned"); + +ASSERT(__sidata % 4 == 0, " +BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned"); + +ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, " +BUG(cortex-m-rt): .bss is not 4-byte aligned"); + +ASSERT(__sheap % 4 == 0, " +BUG(cortex-m-rt): start of .heap is not 4-byte aligned"); + +/* # Position checks */ + +/* ## .vector_table */ +ASSERT(__reset_vector == ADDR(.vector_table) + 0x8, " +BUG(cortex-m-rt): the reset vector is missing"); + +ASSERT(__eexceptions == ADDR(.vector_table) + 0x40, " +BUG(cortex-m-rt): the exception vectors are missing"); + +ASSERT(SIZEOF(.vector_table) > 0x40, " +ERROR(cortex-m-rt): The interrupt vectors are missing. +Possible solutions, from most likely to less likely: +- Link to a svd2rust generated device crate +- Check that you actually use the device/hal/bsp crate in your code +- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency +may be enabling it) +- Supply the interrupt handlers yourself. Check the documentation for details."); + +/* ## .text */ +ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, " +ERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section +Set _stext to an address greater than the end of .vector_table (See output of `nm`)"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " +ERROR(cortex-m-rt): The .text section must be placed inside the FLASH memory. +Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); + +/* # Other checks */ +ASSERT(SIZEOF(.got) == 0, " +ERROR(cortex-m-rt): .got section detected in the input object files +Dynamic relocations are not supported. If you are linking to C code compiled using +the 'cc' crate then modify your build script to compile the C code _without_ +the -fPIC flag. See the documentation of the `cc::Build.pic` method for details."); +/* Do not exceed this mark in the error messages above | */ diff --git a/cortex-m-rt/macros/Cargo.toml b/cortex-m-rt/macros/Cargo.toml new file mode 100644 index 00000000..c73ebc1c --- /dev/null +++ b/cortex-m-rt/macros/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["Jorge Aparicio "] +categories = ["embedded", "no-std"] +description = "Attributes re-exported in `cortex-m-rt`" +documentation = "https://docs.rs/cortex-m-rt" +keywords = ["arm", "cortex-m", "runtime", "startup"] +license = "MIT OR Apache-2.0" +name = "cortex-m-rt-macros" +repository = "https://github.com/rust-embedded/cortex-m" +version = "0.7.0" +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0" +proc-macro2 = "1.0" + +[dependencies.syn] +features = ["extra-traits", "full"] +version = "1.0" diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs new file mode 100644 index 00000000..817e9a18 --- /dev/null +++ b/cortex-m-rt/macros/src/lib.rs @@ -0,0 +1,654 @@ +//! Internal implementation details of `cortex-m-rt`. +//! +//! Do not use this crate directly. + +extern crate proc_macro; + +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::quote; +use std::collections::HashSet; +use std::iter; +use syn::{ + parse, parse_macro_input, spanned::Spanned, AttrStyle, Attribute, FnArg, Ident, Item, ItemFn, + ItemStatic, ReturnType, Stmt, Type, Visibility, +}; + +#[proc_macro_attribute] +pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { + let mut f = parse_macro_input!(input as ItemFn); + + // check the function signature + let valid_signature = f.sig.constness.is_none() + && f.vis == Visibility::Inherited + && f.sig.abi.is_none() + && f.sig.inputs.is_empty() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { + ReturnType::Default => false, + ReturnType::Type(_, ref ty) => match **ty { + Type::Never(_) => true, + _ => false, + }, + }; + + if !valid_signature { + return parse::Error::new( + f.span(), + "`#[entry]` function must have signature `[unsafe] fn() -> !`", + ) + .to_compile_error() + .into(); + } + + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } + + // XXX should we blacklist other attributes? + let (statics, stmts) = match extract_static_muts(f.block.stmts) { + Err(e) => return e.to_compile_error().into(), + Ok(x) => x, + }; + + f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site()); + f.sig.inputs.extend(statics.iter().map(|statik| { + let ident = &statik.ident; + let ty = &statik.ty; + let attrs = &statik.attrs; + + // Note that we use an explicit `'static` lifetime for the entry point arguments. This makes + // it more flexible, and is sound here, since the entry will not be called again, ever. + syn::parse::( + quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &'static mut #ty).into(), + ) + .unwrap() + })); + f.block.stmts = stmts; + + let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); + let ident = &f.sig.ident; + + let resource_args = statics + .iter() + .map(|statik| { + let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone()); + let ident = &statik.ident; + let ty = &statik.ty; + let expr = &statik.expr; + quote! { + #(#cfgs)* + { + #(#attrs)* + static mut #ident: #ty = #expr; + &mut #ident + } + } + }) + .collect::>(); + + if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Entry) { + return error; + } + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = "main"] + pub unsafe extern "C" fn #tramp_ident() { + #ident( + #(#resource_args),* + ) + } + + #f + ) + .into() +} + +#[derive(Debug, PartialEq)] +enum Exception { + DefaultHandler, + HardFault, + NonMaskableInt, + Other, +} + +#[proc_macro_attribute] +pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { + let mut f = parse_macro_input!(input as ItemFn); + + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } + + if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Exception) { + return error; + } + + let fspan = f.span(); + let ident = f.sig.ident.clone(); + + let ident_s = ident.to_string(); + let exn = match &*ident_s { + "DefaultHandler" => Exception::DefaultHandler, + "HardFault" => Exception::HardFault, + "NonMaskableInt" => Exception::NonMaskableInt, + // NOTE that at this point we don't check if the exception is available on the target (e.g. + // MemoryManagement is not available on Cortex-M0) + "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall" + | "DebugMonitor" | "PendSV" | "SysTick" => Exception::Other, + _ => { + return parse::Error::new(ident.span(), "This is not a valid exception name") + .to_compile_error() + .into(); + } + }; + + if f.sig.unsafety.is_none() { + match exn { + Exception::DefaultHandler | Exception::HardFault | Exception::NonMaskableInt => { + // These are unsafe to define. + let name = if exn == Exception::DefaultHandler { + format!("`DefaultHandler`") + } else { + format!("`{:?}` handler", exn) + }; + return parse::Error::new(ident.span(), format_args!("defining a {} is unsafe and requires an `unsafe fn` (see the cortex-m-rt docs)", name)) + .to_compile_error() + .into(); + } + Exception::Other => {} + } + } + + // Emit a reference to the `Exception` variant corresponding to our exception. + // This will fail compilation when the target doesn't have that exception. + let assertion = match exn { + Exception::Other => { + quote! { + const _: () = { + let _ = cortex_m_rt::Exception::#ident; + }; + } + } + _ => quote!(), + }; + + let handler = match exn { + Exception::DefaultHandler => { + let valid_signature = f.sig.constness.is_none() + && f.vis == Visibility::Inherited + && f.sig.abi.is_none() + && f.sig.inputs.len() == 1 + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => match **ty { + Type::Tuple(ref tuple) => tuple.elems.is_empty(), + Type::Never(..) => true, + _ => false, + }, + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`DefaultHandler` must have signature `unsafe fn(i16) [-> !]`", + ) + .to_compile_error() + .into(); + } + + f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site()); + let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); + let ident = &f.sig.ident; + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = #ident_s] + pub unsafe extern "C" fn #tramp_ident() { + extern crate core; + + const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; + + let irqn = unsafe { (core::ptr::read_volatile(SCB_ICSR) & 0x1FF) as i16 - 16 }; + + #ident(irqn) + } + + #f + ) + } + Exception::HardFault => { + let valid_signature = f.sig.constness.is_none() + && f.vis == Visibility::Inherited + && f.sig.abi.is_none() + && f.sig.inputs.len() == 1 + && match &f.sig.inputs[0] { + FnArg::Typed(arg) => match arg.ty.as_ref() { + Type::Reference(r) => r.lifetime.is_none() && r.mutability.is_none(), + _ => false, + }, + _ => false, + } + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { + ReturnType::Default => false, + ReturnType::Type(_, ref ty) => match **ty { + Type::Never(_) => true, + _ => false, + }, + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`", + ) + .to_compile_error() + .into(); + } + + f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site()); + let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); + let ident = &f.sig.ident; + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = "HardFault"] + // Only emit link_section when building for embedded targets, + // because some hosted platforms (used to check the build) + // cannot handle the long link section names. + #[cfg_attr(target_os = "none", link_section = ".HardFault.user")] + pub unsafe extern "C" fn #tramp_ident(frame: &::cortex_m_rt::ExceptionFrame) { + #ident(frame) + } + + #f + ) + } + Exception::NonMaskableInt | Exception::Other => { + let valid_signature = f.sig.constness.is_none() + && f.vis == Visibility::Inherited + && f.sig.abi.is_none() + && f.sig.inputs.is_empty() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => match **ty { + Type::Tuple(ref tuple) => tuple.elems.is_empty(), + Type::Never(..) => true, + _ => false, + }, + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`#[exception]` handlers other than `DefaultHandler` and `HardFault` must have \ + signature `[unsafe] fn() [-> !]`", + ) + .to_compile_error() + .into(); + } + + let (statics, stmts) = match extract_static_muts(f.block.stmts) { + Err(e) => return e.to_compile_error().into(), + Ok(x) => x, + }; + + f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site()); + f.sig.inputs.extend(statics.iter().map(|statik| { + let ident = &statik.ident; + let ty = &statik.ty; + let attrs = &statik.attrs; + syn::parse::( + quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into(), + ) + .unwrap() + })); + f.block.stmts = iter::once( + syn::parse2(quote! {{ + // check that this exception actually exists + exception::#ident; + }}) + .unwrap(), + ) + .chain(stmts) + .collect(); + + let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); + let ident = &f.sig.ident; + + let resource_args = statics + .iter() + .map(|statik| { + let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone()); + let ident = &statik.ident; + let ty = &statik.ty; + let expr = &statik.expr; + quote! { + #(#cfgs)* + { + #(#attrs)* + static mut #ident: #ty = #expr; + &mut #ident + } + } + }) + .collect::>(); + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = #ident_s] + pub unsafe extern "C" fn #tramp_ident() { + #ident( + #(#resource_args),* + ) + } + + #f + ) + } + }; + + quote!( + #assertion + #handler + ) + .into() +} + +#[proc_macro_attribute] +pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { + let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); + + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } + + let fspan = f.span(); + let ident = f.sig.ident.clone(); + let ident_s = ident.to_string(); + + // XXX should we blacklist other attributes? + + let valid_signature = f.sig.constness.is_none() + && f.vis == Visibility::Inherited + && f.sig.abi.is_none() + && f.sig.inputs.is_empty() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => match **ty { + Type::Tuple(ref tuple) => tuple.elems.is_empty(), + Type::Never(..) => true, + _ => false, + }, + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`", + ) + .to_compile_error() + .into(); + } + + let (statics, stmts) = match extract_static_muts(f.block.stmts.iter().cloned()) { + Err(e) => return e.to_compile_error().into(), + Ok(x) => x, + }; + + f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site()); + f.sig.inputs.extend(statics.iter().map(|statik| { + let ident = &statik.ident; + let ty = &statik.ty; + let attrs = &statik.attrs; + syn::parse::(quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into()) + .unwrap() + })); + f.block.stmts = iter::once( + syn::parse2(quote! {{ + // Check that this interrupt actually exists + interrupt::#ident; + }}) + .unwrap(), + ) + .chain(stmts) + .collect(); + + let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); + let ident = &f.sig.ident; + + let resource_args = statics + .iter() + .map(|statik| { + let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone()); + let ident = &statik.ident; + let ty = &statik.ty; + let expr = &statik.expr; + quote! { + #(#cfgs)* + { + #(#attrs)* + static mut #ident: #ty = #expr; + &mut #ident + } + } + }) + .collect::>(); + + if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Interrupt) { + return error; + } + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = #ident_s] + pub unsafe extern "C" fn #tramp_ident() { + #ident( + #(#resource_args),* + ) + } + + #f + ) + .into() +} + +#[proc_macro_attribute] +pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { + let f = parse_macro_input!(input as ItemFn); + + // check the function signature + let valid_signature = f.sig.constness.is_none() + && f.vis == Visibility::Inherited + && f.sig.unsafety.is_some() + && f.sig.abi.is_none() + && f.sig.inputs.is_empty() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => match **ty { + Type::Tuple(ref tuple) => tuple.elems.is_empty(), + _ => false, + }, + }; + + if !valid_signature { + return parse::Error::new( + f.span(), + "`#[pre_init]` function must have signature `unsafe fn()`", + ) + .to_compile_error() + .into(); + } + + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } + + if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::PreInit) { + return error; + } + + // XXX should we blacklist other attributes? + let attrs = f.attrs; + let ident = f.sig.ident; + let block = f.block; + + quote!( + #[export_name = "__pre_init"] + #[allow(missing_docs)] // we make a private fn public, which can trigger this lint + #(#attrs)* + pub unsafe fn #ident() #block + ) + .into() +} + +/// Extracts `static mut` vars from the beginning of the given statements +fn extract_static_muts( + stmts: impl IntoIterator, +) -> Result<(Vec, Vec), parse::Error> { + let mut istmts = stmts.into_iter(); + + let mut seen = HashSet::new(); + let mut statics = vec![]; + let mut stmts = vec![]; + while let Some(stmt) = istmts.next() { + match stmt { + Stmt::Item(Item::Static(var)) => { + if var.mutability.is_some() { + if seen.contains(&var.ident) { + return Err(parse::Error::new( + var.ident.span(), + format!("the name `{}` is defined multiple times", var.ident), + )); + } + + seen.insert(var.ident.clone()); + statics.push(var); + } else { + stmts.push(Stmt::Item(Item::Static(var))); + } + } + _ => { + stmts.push(stmt); + break; + } + } + } + + stmts.extend(istmts); + + Ok((statics, stmts)) +} + +fn extract_cfgs(attrs: Vec) -> (Vec, Vec) { + let mut cfgs = vec![]; + let mut not_cfgs = vec![]; + + for attr in attrs { + if eq(&attr, "cfg") { + cfgs.push(attr); + } else { + not_cfgs.push(attr); + } + } + + (cfgs, not_cfgs) +} + +enum WhiteListCaller { + Entry, + Exception, + Interrupt, + PreInit, +} + +fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<(), TokenStream> { + let whitelist = &[ + "doc", + "link_section", + "cfg", + "allow", + "warn", + "deny", + "forbid", + "cold", + ]; + + 'o: for attr in attrs { + for val in whitelist { + if eq(&attr, &val) { + continue 'o; + } + } + + let err_str = match caller { + WhiteListCaller::Entry => "this attribute is not allowed on a cortex-m-rt entry point", + WhiteListCaller::Exception => { + "this attribute is not allowed on an exception handler controlled by cortex-m-rt" + } + WhiteListCaller::Interrupt => { + "this attribute is not allowed on an interrupt handler controlled by cortex-m-rt" + } + WhiteListCaller::PreInit => { + "this attribute is not allowed on a pre-init controlled by cortex-m-rt" + } + }; + + return Err(parse::Error::new(attr.span(), &err_str) + .to_compile_error() + .into()); + } + + Ok(()) +} + +/// Returns `true` if `attr.path` matches `name` +fn eq(attr: &Attribute, name: &str) -> bool { + attr.style == AttrStyle::Outer && attr.path.is_ident(name) +} diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs new file mode 100644 index 00000000..8003326a --- /dev/null +++ b/cortex-m-rt/src/lib.rs @@ -0,0 +1,1095 @@ +//! Startup code and minimal runtime for Cortex-M microcontrollers +//! +//! This crate contains all the required parts to build a `no_std` application (binary crate) that +//! targets a Cortex-M microcontroller. +//! +//! # Features +//! +//! This crates takes care of: +//! +//! - The memory layout of the program. In particular, it populates the vector table so the device +//! can boot correctly, and properly dispatch exceptions and interrupts. +//! +//! - Initializing `static` variables before the program entry point. +//! +//! - Enabling the FPU before the program entry point if the target is `thumbv7em-none-eabihf`. +//! +//! This crate also provides the following attributes: +//! +//! - [`#[entry]`][attr-entry] to declare the entry point of the program +//! - [`#[exception]`][attr-exception] to override an exception handler. If not overridden all +//! exception handlers default to an infinite loop. +//! - [`#[pre_init]`][attr-pre_init] to run code *before* `static` variables are initialized +//! +//! This crate also implements a related attribute called `#[interrupt]`, which allows you +//! to define interrupt handlers. However, since which interrupts are available depends on the +//! microcontroller in use, this attribute should be re-exported and used from a device crate. +//! +//! The documentation for these attributes can be found in the [Attribute Macros](#attributes) +//! section. +//! +//! # Requirements +//! +//! ## `memory.x` +//! +//! This crate expects the user, or some other crate, to provide the memory layout of the target +//! device via a linker script named `memory.x`. This section covers the contents of `memory.x` +//! The `memory.x` file is used by during linking by the `link.x` script provided by this crate. +//! +//! ### `MEMORY` +//! +//! The linker script must specify the memory available in the device as, at least, two `MEMORY` +//! regions: one named `FLASH` and one named `RAM`. The `.text` and `.rodata` sections of the +//! program will be placed in the `FLASH` region, whereas the `.bss` and `.data` sections, as well +//! as the heap,will be placed in the `RAM` region. +//! +//! ```text +//! /* Linker script for the STM32F103C8T6 */ +//! MEMORY +//! { +//! FLASH : ORIGIN = 0x08000000, LENGTH = 64K +//! RAM : ORIGIN = 0x20000000, LENGTH = 20K +//! } +//! ``` +//! +//! ### `_stack_start` +//! +//! This optional symbol can be used to indicate where the call stack of the program should be +//! placed. If this symbol is not used then the stack will be placed at the *end* of the `RAM` +//! region -- the stack grows downwards towards smaller address. This symbol can be used to place +//! the stack in a different memory region, for example: +//! +//! ```text +//! /* Linker script for the STM32F303VCT6 */ +//! MEMORY +//! { +//! FLASH : ORIGIN = 0x08000000, LENGTH = 256K +//! +//! /* .bss, .data and the heap go in this region */ +//! RAM : ORIGIN = 0x20000000, LENGTH = 40K +//! +//! /* Core coupled (faster) RAM dedicated to hold the stack */ +//! CCRAM : ORIGIN = 0x10000000, LENGTH = 8K +//! } +//! +//! _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM); +//! ``` +//! +//! ### `_stext` +//! +//! This optional symbol can be used to control where the `.text` section is placed. If omitted the +//! `.text` section will be placed right after the vector table, which is placed at the beginning of +//! `FLASH`. Some devices store settings like Flash configuration right after the vector table; +//! for these devices one must place the `.text` section after this configuration section -- +//! `_stext` can be used for this purpose. +//! +//! ```text +//! MEMORY +//! { +//! /* .. */ +//! } +//! +//! /* The device stores Flash configuration in 0x400-0x40C so we place .text after that */ +//! _stext = ORIGIN(FLASH) + 0x40C +//! ``` +//! +//! # An example +//! +//! This section presents a minimal application built on top of `cortex-m-rt`. Apart from the +//! mandatory `memory.x` linker script describing the memory layout of the device, the hard fault +//! handler and the default exception handler must also be defined somewhere in the dependency +//! graph (see [`#[exception]`]). In this example we define them in the binary crate: +//! +//! ```no_run +//! // IMPORTANT the standard `main` interface is not used because it requires nightly +//! #![no_main] +//! #![no_std] +//! +//! // Some panic handler needs to be included. This one halts the processor on panic. +//! extern crate panic_halt; +//! +//! use cortex_m_rt::entry; +//! +//! // use `main` as the entry point of this application +//! // `main` is not allowed to return +//! #[entry] +//! fn main() -> ! { +//! // initialization +//! +//! loop { +//! // application logic +//! } +//! } +//! ``` +//! +//! To actually build this program you need to place a `memory.x` linker script somewhere the linker +//! can find it, e.g. in the current directory; and then link the program using `cortex-m-rt`'s +//! linker script: `link.x`. The required steps are shown below: +//! +//! ```text +//! $ cat > memory.x < !` or its invocation from +//! `Reset` will result in undefined behavior. +//! +//! ## Incorporating device specific interrupts +//! +//! This section covers how an external crate can insert device specific interrupt handlers into the +//! vector table. Most users don't need to concern themselves with these details, but if you are +//! interested in how device crates generated using `svd2rust` integrate with `cortex-m-rt` read on. +//! +//! The information in this section applies when the `"device"` feature has been enabled. +//! +//! ### `__INTERRUPTS` +//! +//! The external crate must provide the interrupts portion of the vector table via a `static` +//! variable named`__INTERRUPTS` (unmangled) that must be placed in the `.vector_table.interrupts` +//! section of its object file. +//! +//! This `static` variable will be placed at `ORIGIN(FLASH) + 0x40`. This address corresponds to the +//! spot where IRQ0 (IRQ number 0) is located. +//! +//! To conform to the Cortex-M ABI `__INTERRUPTS` must be an array of function pointers; some spots +//! in this array may need to be set to 0 if they are marked as *reserved* in the data sheet / +//! reference manual. We recommend using a `union` to set the reserved spots to `0`; `None` +//! (`Option`) may also work but it's not guaranteed that the `None` variant will *always* be +//! represented by the value `0`. +//! +//! Let's illustrate with an artificial example where a device only has two interrupt: `Foo`, with +//! IRQ number = 2, and `Bar`, with IRQ number = 4. +//! +//! ```no_run +//! pub union Vector { +//! handler: unsafe extern "C" fn(), +//! reserved: usize, +//! } +//! +//! extern "C" { +//! fn Foo(); +//! fn Bar(); +//! } +//! +//! #[link_section = ".vector_table.interrupts"] +//! #[no_mangle] +//! pub static __INTERRUPTS: [Vector; 5] = [ +//! // 0-1: Reserved +//! Vector { reserved: 0 }, +//! Vector { reserved: 0 }, +//! +//! // 2: Foo +//! Vector { handler: Foo }, +//! +//! // 3: Reserved +//! Vector { reserved: 0 }, +//! +//! // 4: Bar +//! Vector { handler: Bar }, +//! ]; +//! ``` +//! +//! ### `device.x` +//! +//! Linking in `__INTERRUPTS` creates a bunch of undefined references. If the user doesn't set a +//! handler for *all* the device specific interrupts then linking will fail with `"undefined +//! reference"` errors. +//! +//! We want to provide a default handler for all the interrupts while still letting the user +//! individually override each interrupt handler. In C projects, this is usually accomplished using +//! weak aliases declared in external assembly files. In Rust, we could achieve something similar +//! using `global_asm!`, but that's an unstable feature. +//! +//! A solution that doesn't require `global_asm!` or external assembly files is to use the `PROVIDE` +//! command in a linker script to create the weak aliases. This is the approach that `cortex-m-rt` +//! uses; when the `"device"` feature is enabled `cortex-m-rt`'s linker script (`link.x`) depends on +//! a linker script named `device.x`. The crate that provides `__INTERRUPTS` must also provide this +//! file. +//! +//! For our running example the `device.x` linker script looks like this: +//! +//! ```text +//! /* device.x */ +//! PROVIDE(Foo = DefaultHandler); +//! PROVIDE(Bar = DefaultHandler); +//! ``` +//! +//! This weakly aliases both `Foo` and `Bar`. `DefaultHandler` is the default exception handler and +//! that the core exceptions use unless overridden. +//! +//! Because this linker script is provided by a dependency of the final application the dependency +//! must contain build script that puts `device.x` somewhere the linker can find. An example of such +//! build script is shown below: +//! +//! ```ignore +//! use std::env; +//! use std::fs::File; +//! use std::io::Write; +//! use std::path::PathBuf; +//! +//! fn main() { +//! // Put the linker script somewhere the linker can find it +//! let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); +//! File::create(out.join("device.x")) +//! .unwrap() +//! .write_all(include_bytes!("device.x")) +//! .unwrap(); +//! println!("cargo:rustc-link-search={}", out.display()); +//! } +//! ``` +//! +//! ## Uninitialized static variables +//! +//! The `.uninit` linker section can be used to leave `static mut` variables uninitialized. One use +//! case of unitialized static variables is to avoid zeroing large statically allocated buffers (say +//! to be used as thread stacks) -- this can considerably reduce initialization time on devices that +//! operate at low frequencies. +//! +//! The only correct way to use this section is by placing `static mut` variables with type +//! [`MaybeUninit`] in it. +//! +//! [`MaybeUninit`]: https://doc.rust-lang.org/core/mem/union.MaybeUninit.html +//! +//! ```no_run,edition2018 +//! # extern crate core; +//! use core::mem::MaybeUninit; +//! +//! const STACK_SIZE: usize = 8 * 1024; +//! const NTHREADS: usize = 4; +//! +//! #[link_section = ".uninit.STACKS"] +//! static mut STACKS: MaybeUninit<[[u8; STACK_SIZE]; NTHREADS]> = MaybeUninit::uninit(); +//! ``` +//! +//! Be very careful with the `link_section` attribute because it's easy to misuse in ways that cause +//! undefined behavior. At some point in the future we may add an attribute to safely place static +//! variables in this section. +//! +//! ## Extra Sections +//! +//! Some microcontrollers provide additional memory regions beyond RAM and FLASH. +//! For example, some STM32 devices provide "CCM" or core-coupled RAM that is +//! only accessible from the core. In order to access these using +//! [`link_section`] attributes from your code, you need to modify `memory.x` +//! to declare the additional sections: +//! +//! [`link_section`]: https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute +//! +//! ```text +//! MEMORY +//! { +//! FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K +//! RAM (rw) : ORIGIN = 0x20000000, LENGTH = 128K +//! CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K +//! } +//! +//! SECTIONS +//! { +//! .ccmram (NOLOAD) : ALIGN(4) +//! { +//! *(.ccmram .ccmram.*); +//! . = ALIGN(4); +//! } > CCMRAM +//! } +//! ``` +//! +//! You can then use something like this to place a variable into this specific section of memory: +//! +//! ```no_run,edition2018 +//! #[link_section=".ccmram.BUFFERS"] +//! static mut BUF: [u8; 1024] = [0u8; 1024]; +//! ``` +//! +//! [attr-entry]: attr.entry.html +//! [attr-exception]: attr.exception.html +//! [attr-pre_init]: attr.pre_init.html +//! +//! # Minimum Supported Rust Version (MSRV) +//! +//! The MSRV of this release is Rust 1.40.0. + +// # Developer notes +// +// - `link_section` is used to place symbols in specific places of the final binary. The names used +// here will appear in the linker script (`link.x`) in conjunction with the `KEEP` command. + +#![deny(missing_docs)] +#![no_std] + +extern crate cortex_m_rt_macros as macros; + +use core::fmt; +use core::sync::atomic::{self, Ordering}; + +/// Attribute to declare an interrupt (AKA device-specific exception) handler +/// +/// **IMPORTANT**: If you are using Rust 1.30 this attribute must be used on reachable items (i.e. +/// there must be no private modules between the item and the root of the crate); if the item is in +/// the root of the crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 +/// and newer releases. +/// +/// **NOTE**: This attribute is exposed by `cortex-m-rt` only when the `device` feature is enabled. +/// However, that export is not meant to be used directly -- using it will result in a compilation +/// error. You should instead use the device crate (usually generated using `svd2rust`) re-export of +/// that attribute. You need to use the re-export to have the compiler check that the interrupt +/// exists on the target device. +/// +/// # Syntax +/// +/// ``` ignore +/// extern crate device; +/// +/// // the attribute comes from the device crate not from cortex-m-rt +/// use device::interrupt; +/// +/// #[interrupt] +/// fn USART1() { +/// // .. +/// } +/// ``` +/// +/// where the name of the function must be one of the device interrupts. +/// +/// # Usage +/// +/// `#[interrupt] fn Name(..` overrides the default handler for the interrupt with the given `Name`. +/// These handlers must have signature `[unsafe] fn() [-> !]`. It's possible to add state to these +/// handlers by declaring `static mut` variables at the beginning of the body of the function. These +/// variables will be safe to access from the function body. +/// +/// If the interrupt handler has not been overridden it will be dispatched by the default exception +/// handler (`DefaultHandler`). +/// +/// # Properties +/// +/// Interrupts handlers can only be called by the hardware. Other parts of the program can't refer +/// to the interrupt handlers, much less invoke them as if they were functions. +/// +/// `static mut` variables declared within an interrupt handler are safe to access and can be used +/// to preserve state across invocations of the handler. The compiler can't prove this is safe so +/// the attribute will help by making a transformation to the source code: for this reason a +/// variable like `static mut FOO: u32` will become `let FOO: &mut u32;`. +/// +/// # Examples +/// +/// - Using state within an interrupt handler +/// +/// ``` ignore +/// extern crate device; +/// +/// use device::interrupt; +/// +/// #[interrupt] +/// fn TIM2() { +/// static mut COUNT: i32 = 0; +/// +/// // `COUNT` is safe to access and has type `&mut i32` +/// *COUNT += 1; +/// +/// println!("{}", COUNT); +/// } +/// ``` +#[cfg(feature = "device")] +pub use macros::interrupt; + +/// Attribute to declare the entry point of the program +/// +/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you +/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no +/// private modules between the item and the root of the crate); if the item is in the root of the +/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases. +/// +/// The specified function will be called by the reset handler *after* RAM has been initialized. In +/// the case of the `thumbv7em-none-eabihf` target the FPU will also be enabled before the function +/// is called. +/// +/// The type of the specified function must be `[unsafe] fn() -> !` (never ending function) +/// +/// # Properties +/// +/// The entry point will be called by the reset handler. The program can't reference to the entry +/// point, much less invoke it. +/// +/// `static mut` variables declared within the entry point are safe to access. The compiler can't +/// prove this is safe so the attribute will help by making a transformation to the source code: for +/// this reason a variable like `static mut FOO: u32` will become `let FOO: &'static mut u32;`. Note +/// that `&'static mut` references have move semantics. +/// +/// # Examples +/// +/// - Simple entry point +/// +/// ``` no_run +/// # #![no_main] +/// # use cortex_m_rt::entry; +/// #[entry] +/// fn main() -> ! { +/// loop { +/// /* .. */ +/// } +/// } +/// ``` +/// +/// - `static mut` variables local to the entry point are safe to modify. +/// +/// ``` no_run +/// # #![no_main] +/// # use cortex_m_rt::entry; +/// #[entry] +/// fn main() -> ! { +/// static mut FOO: u32 = 0; +/// +/// let foo: &'static mut u32 = FOO; +/// assert_eq!(*foo, 0); +/// *foo = 1; +/// assert_eq!(*foo, 1); +/// +/// loop { +/// /* .. */ +/// } +/// } +/// ``` +pub use macros::entry; + +/// Attribute to declare an exception handler +/// +/// **IMPORTANT**: If you are using Rust 1.30 this attribute must be used on reachable items (i.e. +/// there must be no private modules between the item and the root of the crate); if the item is in +/// the root of the crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 +/// and newer releases. +/// +/// # Syntax +/// +/// ``` +/// # use cortex_m_rt::exception; +/// #[exception] +/// fn SysTick() { +/// // .. +/// } +/// +/// # fn main() {} +/// ``` +/// +/// where the name of the function must be one of: +/// +/// - `DefaultHandler` +/// - `NonMaskableInt` +/// - `HardFault` +/// - `MemoryManagement` (a) +/// - `BusFault` (a) +/// - `UsageFault` (a) +/// - `SecureFault` (b) +/// - `SVCall` +/// - `DebugMonitor` (a) +/// - `PendSV` +/// - `SysTick` +/// +/// (a) Not available on Cortex-M0 variants (`thumbv6m-none-eabi`) +/// +/// (b) Only available on ARMv8-M +/// +/// # Usage +/// +/// `#[exception] unsafe fn HardFault(..` sets the hard fault handler. The handler must have +/// signature `unsafe fn(&ExceptionFrame) -> !`. This handler is not allowed to return as that can +/// cause undefined behavior. +/// +/// `#[exception] unsafe fn DefaultHandler(..` sets the *default* handler. All exceptions which have +/// not been assigned a handler will be serviced by this handler. This handler must have signature +/// `unsafe fn(irqn: i16) [-> !]`. `irqn` is the IRQ number (See CMSIS); `irqn` will be a negative +/// number when the handler is servicing a core exception; `irqn` will be a positive number when the +/// handler is servicing a device specific exception (interrupt). +/// +/// `#[exception] fn Name(..` overrides the default handler for the exception with the given `Name`. +/// These handlers must have signature `[unsafe] fn() [-> !]`. When overriding these other exception +/// it's possible to add state to them by declaring `static mut` variables at the beginning of the +/// body of the function. These variables will be safe to access from the function body. +/// +/// # Properties +/// +/// Exception handlers can only be called by the hardware. Other parts of the program can't refer to +/// the exception handlers, much less invoke them as if they were functions. +/// +/// `static mut` variables declared within an exception handler are safe to access and can be used +/// to preserve state across invocations of the handler. The compiler can't prove this is safe so +/// the attribute will help by making a transformation to the source code: for this reason a +/// variable like `static mut FOO: u32` will become `let FOO: &mut u32;`. +/// +/// # Safety +/// +/// It is not generally safe to register handlers for non-maskable interrupts. On Cortex-M, +/// `HardFault` is non-maskable (at least in general), and there is an explicitly non-maskable +/// interrupt `NonMaskableInt`. +/// +/// The reason for that is that non-maskable interrupts will preempt any currently running function, +/// even if that function executes within a critical section. Thus, if it was safe to define NMI +/// handlers, critical sections wouldn't work safely anymore. +/// +/// This also means that defining a `DefaultHandler` must be unsafe, as that will catch +/// `NonMaskableInt` and `HardFault` if no handlers for those are defined. +/// +/// The safety requirements on those handlers is as follows: The handler must not access any data +/// that is protected via a critical section and shared with other interrupts that may be preempted +/// by the NMI while holding the critical section. As long as this requirement is fulfilled, it is +/// safe to handle NMIs. +/// +/// # Examples +/// +/// - Setting the default handler +/// +/// ``` +/// use cortex_m_rt::exception; +/// +/// #[exception] +/// unsafe fn DefaultHandler(irqn: i16) { +/// println!("IRQn = {}", irqn); +/// } +/// +/// # fn main() {} +/// ``` +/// +/// - Overriding the `SysTick` handler +/// +/// ``` +/// use cortex_m_rt::exception; +/// +/// #[exception] +/// fn SysTick() { +/// static mut COUNT: i32 = 0; +/// +/// // `COUNT` is safe to access and has type `&mut i32` +/// *COUNT += 1; +/// +/// println!("{}", COUNT); +/// } +/// +/// # fn main() {} +/// ``` +pub use macros::exception; + +/// Attribute to mark which function will be called at the beginning of the reset handler. +/// +/// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. Also, if you +/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no +/// private modules between the item and the root of the crate); if the item is in the root of the +/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer +/// releases. +/// +/// The function must have the signature of `unsafe fn()`. +/// +/// # Safety +/// +/// The function will be called before memory is initialized, as soon as possible after reset. Any +/// access of memory, including any static variables, will result in undefined behavior. +/// +/// **Warning**: Due to [rvalue static promotion][rfc1414] static variables may be accessed whenever +/// taking a reference to a constant. This means that even trivial expressions such as `&1` in the +/// `#[pre_init]` function *or any code called by it* will cause **immediate undefined behavior**. +/// +/// Users are advised to only use the `#[pre_init]` feature when absolutely necessary as these +/// constraints make safe usage difficult. +/// +/// # Examples +/// +/// ``` +/// # use cortex_m_rt::pre_init; +/// #[pre_init] +/// unsafe fn before_main() { +/// // do something here +/// } +/// +/// # fn main() {} +/// ``` +/// +/// [rfc1414]: https://github.com/rust-lang/rfcs/blob/master/text/1414-rvalue_static_promotion.md +pub use macros::pre_init; + +// We export this static with an informative name so that if an application attempts to link +// two copies of cortex-m-rt together, linking will fail. We also declare a links key in +// Cargo.toml which is the more modern way to solve the same problem, but we have to keep +// __ONCE__ around to prevent linking with versions before the links key was added. +#[export_name = "error: cortex-m-rt appears more than once in the dependency graph"] +#[doc(hidden)] +pub static __ONCE__: () = (); + +/// Registers stacked (pushed onto the stack) during an exception. +#[derive(Clone, Copy)] +#[repr(C)] +pub struct ExceptionFrame { + r0: u32, + r1: u32, + r2: u32, + r3: u32, + r12: u32, + lr: u32, + pc: u32, + xpsr: u32, +} + +impl ExceptionFrame { + /// Returns the value of (general purpose) register 0. + #[inline(always)] + pub fn r0(&self) -> u32 { + self.r0 + } + + /// Returns the value of (general purpose) register 1. + #[inline(always)] + pub fn r1(&self) -> u32 { + self.r1 + } + + /// Returns the value of (general purpose) register 2. + #[inline(always)] + pub fn r2(&self) -> u32 { + self.r2 + } + + /// Returns the value of (general purpose) register 3. + #[inline(always)] + pub fn r3(&self) -> u32 { + self.r3 + } + + /// Returns the value of (general purpose) register 12. + #[inline(always)] + pub fn r12(&self) -> u32 { + self.r12 + } + + /// Returns the value of the Link Register. + #[inline(always)] + pub fn lr(&self) -> u32 { + self.lr + } + + /// Returns the value of the Program Counter. + #[inline(always)] + pub fn pc(&self) -> u32 { + self.pc + } + + /// Returns the value of the Program Status Register. + #[inline(always)] + pub fn xpsr(&self) -> u32 { + self.xpsr + } + + /// Sets the stacked value of (general purpose) register 0. + /// + /// # Safety + /// + /// This affects the `r0` register of the preempted code, which must not rely on it getting + /// restored to its previous value. + #[inline(always)] + pub unsafe fn set_r0(&mut self, value: u32) { + self.r0 = value; + } + + /// Sets the stacked value of (general purpose) register 1. + /// + /// # Safety + /// + /// This affects the `r1` register of the preempted code, which must not rely on it getting + /// restored to its previous value. + #[inline(always)] + pub unsafe fn set_r1(&mut self, value: u32) { + self.r1 = value; + } + + /// Sets the stacked value of (general purpose) register 2. + /// + /// # Safety + /// + /// This affects the `r2` register of the preempted code, which must not rely on it getting + /// restored to its previous value. + #[inline(always)] + pub unsafe fn set_r2(&mut self, value: u32) { + self.r2 = value; + } + + /// Sets the stacked value of (general purpose) register 3. + /// + /// # Safety + /// + /// This affects the `r3` register of the preempted code, which must not rely on it getting + /// restored to its previous value. + #[inline(always)] + pub unsafe fn set_r3(&mut self, value: u32) { + self.r3 = value; + } + + /// Sets the stacked value of (general purpose) register 12. + /// + /// # Safety + /// + /// This affects the `r12` register of the preempted code, which must not rely on it getting + /// restored to its previous value. + #[inline(always)] + pub unsafe fn set_r12(&mut self, value: u32) { + self.r12 = value; + } + + /// Sets the stacked value of the Link Register. + /// + /// # Safety + /// + /// This affects the `lr` register of the preempted code, which must not rely on it getting + /// restored to its previous value. + #[inline(always)] + pub unsafe fn set_lr(&mut self, value: u32) { + self.lr = value; + } + + /// Sets the stacked value of the Program Counter. + /// + /// # Safety + /// + /// This affects the `pc` register of the preempted code, which must not rely on it getting + /// restored to its previous value. + #[inline(always)] + pub unsafe fn set_pc(&mut self, value: u32) { + self.pc = value; + } + + /// Sets the stacked value of the Program Status Register. + /// + /// # Safety + /// + /// This affects the `xPSR` registers (`IPSR`, `APSR`, and `EPSR`) of the preempted code, which + /// must not rely on them getting restored to their previous value. + #[inline(always)] + pub unsafe fn set_xpsr(&mut self, value: u32) { + self.xpsr = value; + } +} + +impl fmt::Debug for ExceptionFrame { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + struct Hex(u32); + impl fmt::Debug for Hex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "0x{:08x}", self.0) + } + } + f.debug_struct("ExceptionFrame") + .field("r0", &Hex(self.r0)) + .field("r1", &Hex(self.r1)) + .field("r2", &Hex(self.r2)) + .field("r3", &Hex(self.r3)) + .field("r12", &Hex(self.r12)) + .field("lr", &Hex(self.lr)) + .field("pc", &Hex(self.pc)) + .field("xpsr", &Hex(self.xpsr)) + .finish() + } +} + +/// Returns a pointer to the start of the heap +/// +/// The returned pointer is guaranteed to be 4-byte aligned. +#[inline] +pub fn heap_start() -> *mut u32 { + extern "C" { + static mut __sheap: u32; + } + + unsafe { &mut __sheap } +} + +// Entry point is Reset. +#[doc(hidden)] +#[cfg_attr(cortex_m, link_section = ".vector_table.reset_vector")] +#[no_mangle] +pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset; + +#[allow(unused_variables)] +#[doc(hidden)] +#[cfg_attr(cortex_m, link_section = ".HardFault.default")] +#[no_mangle] +pub unsafe extern "C" fn HardFault_(ef: &ExceptionFrame) -> ! { + loop { + // add some side effect to prevent this from turning into a UDF instruction + // see rust-lang/rust#28728 for details + atomic::compiler_fence(Ordering::SeqCst); + } +} + +#[doc(hidden)] +#[no_mangle] +pub unsafe extern "C" fn DefaultHandler_() -> ! { + loop { + // add some side effect to prevent this from turning into a UDF instruction + // see rust-lang/rust#28728 for details + atomic::compiler_fence(Ordering::SeqCst); + } +} + +#[doc(hidden)] +#[no_mangle] +pub unsafe extern "C" fn DefaultPreInit() {} + +/* Exceptions */ +#[doc(hidden)] +pub enum Exception { + NonMaskableInt, + + // Not overridable + // HardFault, + #[cfg(not(armv6m))] + MemoryManagement, + + #[cfg(not(armv6m))] + BusFault, + + #[cfg(not(armv6m))] + UsageFault, + + #[cfg(armv8m)] + SecureFault, + + SVCall, + + #[cfg(not(armv6m))] + DebugMonitor, + + PendSV, + + SysTick, +} + +#[doc(hidden)] +pub use self::Exception as exception; + +extern "C" { + fn Reset() -> !; + + fn NonMaskableInt(); + + fn HardFaultTrampoline(); + + #[cfg(not(armv6m))] + fn MemoryManagement(); + + #[cfg(not(armv6m))] + fn BusFault(); + + #[cfg(not(armv6m))] + fn UsageFault(); + + #[cfg(armv8m)] + fn SecureFault(); + + fn SVCall(); + + #[cfg(not(armv6m))] + fn DebugMonitor(); + + fn PendSV(); + + fn SysTick(); +} + +#[doc(hidden)] +pub union Vector { + handler: unsafe extern "C" fn(), + reserved: usize, +} + +#[doc(hidden)] +#[cfg_attr(cortex_m, link_section = ".vector_table.exceptions")] +#[no_mangle] +pub static __EXCEPTIONS: [Vector; 14] = [ + // Exception 2: Non Maskable Interrupt. + Vector { + handler: NonMaskableInt, + }, + // Exception 3: Hard Fault Interrupt. + Vector { + handler: HardFaultTrampoline, + }, + // Exception 4: Memory Management Interrupt [not on Cortex-M0 variants]. + #[cfg(not(armv6m))] + Vector { + handler: MemoryManagement, + }, + #[cfg(armv6m)] + Vector { reserved: 0 }, + // Exception 5: Bus Fault Interrupt [not on Cortex-M0 variants]. + #[cfg(not(armv6m))] + Vector { handler: BusFault }, + #[cfg(armv6m)] + Vector { reserved: 0 }, + // Exception 6: Usage Fault Interrupt [not on Cortex-M0 variants]. + #[cfg(not(armv6m))] + Vector { + handler: UsageFault, + }, + #[cfg(armv6m)] + Vector { reserved: 0 }, + // Exception 7: Secure Fault Interrupt [only on Armv8-M]. + #[cfg(armv8m)] + Vector { + handler: SecureFault, + }, + #[cfg(not(armv8m))] + Vector { reserved: 0 }, + // 8-10: Reserved + Vector { reserved: 0 }, + Vector { reserved: 0 }, + Vector { reserved: 0 }, + // Exception 11: SV Call Interrupt. + Vector { handler: SVCall }, + // Exception 12: Debug Monitor Interrupt [not on Cortex-M0 variants]. + #[cfg(not(armv6m))] + Vector { + handler: DebugMonitor, + }, + #[cfg(armv6m)] + Vector { reserved: 0 }, + // 13: Reserved + Vector { reserved: 0 }, + // Exception 14: Pend SV Interrupt [not on Cortex-M0 variants]. + Vector { handler: PendSV }, + // Exception 15: System Tick Interrupt. + Vector { handler: SysTick }, +]; + +// If we are not targeting a specific device we bind all the potential device specific interrupts +// to the default handler +#[cfg(all(any(not(feature = "device"), test), not(armv6m)))] +#[doc(hidden)] +#[cfg_attr(cortex_m, link_section = ".vector_table.interrupts")] +#[no_mangle] +pub static __INTERRUPTS: [unsafe extern "C" fn(); 240] = [{ + extern "C" { + fn DefaultHandler(); + } + + DefaultHandler +}; 240]; + +// ARMv6-M can only have a maximum of 32 device specific interrupts +#[cfg(all(not(feature = "device"), armv6m))] +#[doc(hidden)] +#[link_section = ".vector_table.interrupts"] +#[no_mangle] +pub static __INTERRUPTS: [unsafe extern "C" fn(); 32] = [{ + extern "C" { + fn DefaultHandler(); + } + + DefaultHandler +}; 32]; diff --git a/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-1.rs new file mode 100644 index 00000000..b5908839 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-1.rs @@ -0,0 +1,16 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +unsafe fn DefaultHandler(_irqn: i16, undef: u32) {} +//~^ ERROR `DefaultHandler` must have signature `unsafe fn(i16) [-> !]` diff --git a/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-2.rs b/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-2.rs new file mode 100644 index 00000000..0dadd6ab --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-2.rs @@ -0,0 +1,18 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +unsafe fn DefaultHandler(_irqn: i16) -> u32 { + //~^ ERROR `DefaultHandler` must have signature `unsafe fn(i16) [-> !]` + 0 +} diff --git a/cortex-m-rt/tests/compile-fail/default-handler-hidden.rs b/cortex-m-rt/tests/compile-fail/default-handler-hidden.rs new file mode 100644 index 00000000..c658e2bf --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/default-handler-hidden.rs @@ -0,0 +1,22 @@ +// ignore-test :sadface: it's not possible to prevent this user error at compile time +// see rust-lang/rust#53975 for details + +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +mod hidden { + use cortex_m_rt::exception; + + #[exception] + unsafe fn DefaultHandler(_irqn: i16) {} +} diff --git a/cortex-m-rt/tests/compile-fail/default-handler-twice.rs b/cortex-m-rt/tests/compile-fail/default-handler-twice.rs new file mode 100644 index 00000000..bbf2eddd --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/default-handler-twice.rs @@ -0,0 +1,22 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +unsafe fn DefaultHandler(_irqn: i16) {} + +pub mod reachable { + use cortex_m_rt::exception; + + #[exception] //~ ERROR symbol `DefaultHandler` is already defined + unsafe fn DefaultHandler(_irqn: i16) {} +} diff --git a/cortex-m-rt/tests/compile-fail/duplicate-static.rs b/cortex-m-rt/tests/compile-fail/duplicate-static.rs new file mode 100644 index 00000000..eeb884fe --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/duplicate-static.rs @@ -0,0 +1,32 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[allow(non_camel_case_types)] +enum interrupt { + UART0, +} + +#[entry] +fn foo() -> ! { + static mut X: u32 = 0; + static mut X: i32 = 0; //~ ERROR the name `X` is defined multiple times + + loop {} +} + +#[exception] +fn SVCall() { + static mut X: u32 = 0; + static mut X: i32 = 0; //~ ERROR the name `X` is defined multiple times +} + +#[interrupt] +fn UART0() { + static mut X: u32 = 0; + static mut X: i32 = 0; //~ ERROR the name `X` is defined multiple times +} diff --git a/cortex-m-rt/tests/compile-fail/entry-args.rs b/cortex-m-rt/tests/compile-fail/entry-args.rs new file mode 100644 index 00000000..5f0b837f --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/entry-args.rs @@ -0,0 +1,12 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::entry; + +#[entry(foo)] //~ ERROR This attribute accepts no arguments +fn foo() -> ! { + loop {} +} diff --git a/cortex-m-rt/tests/compile-fail/entry-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/entry-bad-signature-1.rs new file mode 100644 index 00000000..1ed3649e --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/entry-bad-signature-1.rs @@ -0,0 +1,11 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::entry; + +#[entry] +fn foo() {} +//~^ ERROR `#[entry]` function must have signature `[unsafe] fn() -> !` diff --git a/cortex-m-rt/tests/compile-fail/entry-bad-signature-2.rs b/cortex-m-rt/tests/compile-fail/entry-bad-signature-2.rs new file mode 100644 index 00000000..359444ce --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/entry-bad-signature-2.rs @@ -0,0 +1,11 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::entry; + +#[entry] +fn foo(undef: i32) -> ! {} +//~^ ERROR `#[entry]` function must have signature `[unsafe] fn() -> !` diff --git a/cortex-m-rt/tests/compile-fail/entry-bad-signature-3.rs b/cortex-m-rt/tests/compile-fail/entry-bad-signature-3.rs new file mode 100644 index 00000000..048b0e6d --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/entry-bad-signature-3.rs @@ -0,0 +1,13 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::entry; + +#[entry] +extern "C" fn foo() -> ! { + //~^ ERROR `#[entry]` function must have signature `[unsafe] fn() -> !` + loop {} +} diff --git a/cortex-m-rt/tests/compile-fail/entry-hidden.rs b/cortex-m-rt/tests/compile-fail/entry-hidden.rs new file mode 100644 index 00000000..836db0d5 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/entry-hidden.rs @@ -0,0 +1,19 @@ +// ignore-test :sadface: it's not possible to prevent this user error at compile time +// see rust-lang/rust#53975 for details + +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +mod hidden { + use cortex_m_rt::entry; + + // this function needs to be "reachable" (all modules between it and the crate root must be + // `pub`) or linking will fail + #[entry] + fn foo() -> ! { //~ ERROR function is never used + loop {} + } +} diff --git a/cortex-m-rt/tests/compile-fail/entry-soundness.rs b/cortex-m-rt/tests/compile-fail/entry-soundness.rs new file mode 100644 index 00000000..9fc8ec11 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/entry-soundness.rs @@ -0,0 +1,26 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + static mut COUNT: u64 = 0; + + loop { + if *COUNT % 2 == 0 { + *COUNT += 1; + } else { + *COUNT *= 2; + } + } +} + +#[exception] +fn SysTick() { + // If this was allowed it would lead to a data race as `SysTick` can preempt `foo` + foo(); //~ ERROR cannot find function `foo` in this scope +} diff --git a/cortex-m-rt/tests/compile-fail/entry-twice.rs b/cortex-m-rt/tests/compile-fail/entry-twice.rs new file mode 100644 index 00000000..757083a3 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/entry-twice.rs @@ -0,0 +1,17 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::entry; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[entry] //~ ERROR symbol `main` is already defined +fn bar() -> ! { + loop {} +} diff --git a/cortex-m-rt/tests/compile-fail/exception-args.rs b/cortex-m-rt/tests/compile-fail/exception-args.rs new file mode 100644 index 00000000..518ac03e --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/exception-args.rs @@ -0,0 +1,15 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception(SysTick)] //~ ERROR This attribute accepts no arguments +fn SysTick() {} diff --git a/cortex-m-rt/tests/compile-fail/exception-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/exception-bad-signature-1.rs new file mode 100644 index 00000000..2a046c35 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/exception-bad-signature-1.rs @@ -0,0 +1,16 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +fn SysTick(undef: u32) {} +//~^ ERROR `#[exception]` handlers other than `DefaultHandler` and `HardFault` must have signature `[unsafe] fn() [-> !]` diff --git a/cortex-m-rt/tests/compile-fail/exception-bad-signature-2.rs b/cortex-m-rt/tests/compile-fail/exception-bad-signature-2.rs new file mode 100644 index 00000000..d2fb2101 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/exception-bad-signature-2.rs @@ -0,0 +1,18 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +fn SysTick() -> u32 { + //~^ ERROR `#[exception]` handlers other than `DefaultHandler` and `HardFault` must have signature `[unsafe] fn() [-> !]` + 0 +} diff --git a/cortex-m-rt/tests/compile-fail/exception-hidden.rs b/cortex-m-rt/tests/compile-fail/exception-hidden.rs new file mode 100644 index 00000000..6f57089e --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/exception-hidden.rs @@ -0,0 +1,22 @@ +// ignore-test :sadface: it's not possible to prevent this user error at compile time +// see rust-lang/rust#53975 for details + +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +mod hidden { + use cortex_m_rt::exception; + + #[exception] + fn SysTick() {} +} diff --git a/cortex-m-rt/tests/compile-fail/exception-nmi-unsafe.rs b/cortex-m-rt/tests/compile-fail/exception-nmi-unsafe.rs new file mode 100644 index 00000000..f5de2f8d --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/exception-nmi-unsafe.rs @@ -0,0 +1,24 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +fn DefaultHandler(_irq: i16) {} +//~^ ERROR defining a `DefaultHandler` is unsafe and requires an `unsafe fn` + +#[exception] +fn HardFault() {} +//~^ ERROR defining a `HardFault` handler is unsafe and requires an `unsafe fn` + +#[exception] +fn NonMaskableInt() {} +//~^ ERROR defining a `NonMaskableInt` handler is unsafe and requires an `unsafe fn` diff --git a/cortex-m-rt/tests/compile-fail/exception-soundness.rs b/cortex-m-rt/tests/compile-fail/exception-soundness.rs new file mode 100644 index 00000000..9a1e10b0 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/exception-soundness.rs @@ -0,0 +1,29 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +fn SysTick() { + static mut COUNT: u64 = 0; + + if *COUNT % 2 == 0 { + *COUNT += 1; + } else { + *COUNT *= 2; + } +} + +#[exception] +fn SVCall() { + // If this was allowed it would lead to a data race as `SVCall` could preempt `SysTick` + SysTick(); //~ ERROR cannot find function, tuple struct or tuple variant `SysTick` in this scope [E0425] +} diff --git a/cortex-m-rt/tests/compile-fail/exception-twice.rs b/cortex-m-rt/tests/compile-fail/exception-twice.rs new file mode 100644 index 00000000..aabbe5a9 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/exception-twice.rs @@ -0,0 +1,22 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +fn SysTick() {} + +pub mod reachable { + use cortex_m_rt::exception; + + #[exception] //~ ERROR symbol `SysTick` is already defined + fn SysTick() {} +} diff --git a/cortex-m-rt/tests/compile-fail/exception-v8only.rs b/cortex-m-rt/tests/compile-fail/exception-v8only.rs new file mode 100644 index 00000000..d54f7060 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/exception-v8only.rs @@ -0,0 +1,16 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +fn SecureFault() {} +//~^ ERROR no variant or associated item named `SecureFault` diff --git a/cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-1.rs new file mode 100644 index 00000000..11b53dc9 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-1.rs @@ -0,0 +1,18 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, ExceptionFrame}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +unsafe fn HardFault(_ef: &ExceptionFrame, undef: u32) -> ! { + //~^ ERROR `HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !` + loop {} +} diff --git a/cortex-m-rt/tests/compile-fail/hard-fault-hidden.rs b/cortex-m-rt/tests/compile-fail/hard-fault-hidden.rs new file mode 100644 index 00000000..956310b2 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/hard-fault-hidden.rs @@ -0,0 +1,24 @@ +// ignore-test :sadface: it's not possible to prevent this user error at compile time +// see rust-lang/rust#53975 for details + +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, ExceptionFrame}; + +#[entry] +fn foo() -> ! { + loop {} +} + +mod hidden { + use cortex_m_rt::{exception, ExceptionFrame}; + + #[exception] + fn HardFault(_ef: &ExceptionFrame) -> ! { + loop {} + } +} diff --git a/cortex-m-rt/tests/compile-fail/hard-fault-twice.rs b/cortex-m-rt/tests/compile-fail/hard-fault-twice.rs new file mode 100644 index 00000000..03b79a57 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/hard-fault-twice.rs @@ -0,0 +1,26 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, ExceptionFrame}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +unsafe fn HardFault(_ef: &ExceptionFrame) -> ! { + loop {} +} + +pub mod reachable { + use cortex_m_rt::{exception, ExceptionFrame}; + + #[exception] //~ ERROR symbol `HardFault` is already defined + unsafe fn HardFault(_ef: &ExceptionFrame) -> ! { + loop {} + } +} diff --git a/cortex-m-rt/tests/compile-fail/interrupt-args.rs b/cortex-m-rt/tests/compile-fail/interrupt-args.rs new file mode 100644 index 00000000..1e06ec2a --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-args.rs @@ -0,0 +1,20 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, +} + +#[interrupt(true)] //~ ERROR This attribute accepts no arguments +fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/interrupt-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/interrupt-bad-signature-1.rs new file mode 100644 index 00000000..bd5ff9f1 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-bad-signature-1.rs @@ -0,0 +1,21 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, +} + +#[interrupt] +fn USART1(undef: i32) {} +//~^ ERROR `#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]` diff --git a/cortex-m-rt/tests/compile-fail/interrupt-bad-signature-2.rs b/cortex-m-rt/tests/compile-fail/interrupt-bad-signature-2.rs new file mode 100644 index 00000000..56db222c --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-bad-signature-2.rs @@ -0,0 +1,23 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, +} + +#[interrupt] +fn USART1() -> i32 { + //~^ ERROR `#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]` + 0 +} diff --git a/cortex-m-rt/tests/compile-fail/interrupt-invalid.rs b/cortex-m-rt/tests/compile-fail/interrupt-invalid.rs new file mode 100644 index 00000000..f2ec7181 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-invalid.rs @@ -0,0 +1,22 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn entry() -> ! { + loop {} +} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, +} + +// NOTE this looks a bit better when using a device crate: +// "no variant named `foo` found for type `stm32f30x::Interrupt` in the current scope" +#[interrupt] +fn foo() {} //~ ERROR no variant or associated item named `foo` found for enum `interrupt` in the current scope [E0599] diff --git a/cortex-m-rt/tests/compile-fail/interrupt-not-reexported.rs b/cortex-m-rt/tests/compile-fail/interrupt-not-reexported.rs new file mode 100644 index 00000000..6a1dd6e8 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-not-reexported.rs @@ -0,0 +1,15 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[interrupt] //~ ERROR failed to resolve: use of undeclared crate or module `interrupt` +fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/interrupt-soundness.rs b/cortex-m-rt/tests/compile-fail/interrupt-soundness.rs new file mode 100644 index 00000000..faf737dc --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-soundness.rs @@ -0,0 +1,34 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, + USART2, +} + +#[interrupt] +fn USART1() { + static mut COUNT: u64 = 0; + + if *COUNT % 2 == 0 { + *COUNT += 1; + } else { + *COUNT *= 2; + } +} + +#[interrupt] +fn USART2() { + USART1(); //~ ERROR cannot find function, tuple struct or tuple variant `USART1` in this scope [E0425] +} diff --git a/cortex-m-rt/tests/compile-fail/interrupt-twice.rs b/cortex-m-rt/tests/compile-fail/interrupt-twice.rs new file mode 100644 index 00000000..b59aea94 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-twice.rs @@ -0,0 +1,31 @@ +#![allow(non_camel_case_types)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +enum interrupt { + USART1, +} + +#[interrupt] +fn USART1() {} + +pub mod reachable { + use cortex_m_rt::interrupt; + + enum interrupt { + USART1, + } + + #[interrupt] //~ ERROR symbol `USART1` is already defined + fn USART1() {} +} diff --git a/cortex-m-rt/tests/compile-fail/non-static-resource.rs b/cortex-m-rt/tests/compile-fail/non-static-resource.rs new file mode 100644 index 00000000..a6037283 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/non-static-resource.rs @@ -0,0 +1,44 @@ +//! Tests that no `&'static mut` to static mutable resources can be obtained, which would be +//! unsound. +//! +//! Regression test for https://github.com/rust-embedded/cortex-m-rt/issues/212 + +#![no_std] +#![no_main] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt, ExceptionFrame}; + +#[allow(non_camel_case_types)] +enum interrupt { + UART0, +} + +#[exception] +fn SVCall() { + static mut STAT: u8 = 0; + + let _stat: &'static mut u8 = STAT; + //~^ ERROR lifetime of reference outlives lifetime of borrowed content +} + +#[interrupt] +fn UART0() { + static mut STAT: u8 = 0; + + let _stat: &'static mut u8 = STAT; + //~^ ERROR lifetime of reference outlives lifetime of borrowed content +} + +#[entry] +fn you_died_of_dis_entry() -> ! { + static mut STAT: u8 = 0; + + // Allowed. This is sound for the entry point since it is only ever called once, and it makes + // resources far more useful. + let _stat: &'static mut u8 = STAT; + + loop {} +} diff --git a/cortex-m-rt/tests/compile-fail/pre-init-args.rs b/cortex-m-rt/tests/compile-fail/pre-init-args.rs new file mode 100644 index 00000000..97325897 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/pre-init-args.rs @@ -0,0 +1,15 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, pre_init}; + +#[pre_init(foo)] //~ ERROR This attribute accepts no arguments +unsafe fn foo() {} + +#[entry] +fn baz() -> ! { + loop {} +} diff --git a/cortex-m-rt/tests/compile-fail/pre-init-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/pre-init-bad-signature-1.rs new file mode 100644 index 00000000..0c3c4768 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/pre-init-bad-signature-1.rs @@ -0,0 +1,16 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, pre_init}; + +#[pre_init] +fn foo() {} +//~^ ERROR `#[pre_init]` function must have signature `unsafe fn()` + +#[entry] +fn bar() -> ! { + loop {} +} diff --git a/cortex-m-rt/tests/compile-fail/pre-init-bad-signature-2.rs b/cortex-m-rt/tests/compile-fail/pre-init-bad-signature-2.rs new file mode 100644 index 00000000..f41d0329 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/pre-init-bad-signature-2.rs @@ -0,0 +1,16 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, pre_init}; + +#[pre_init] +unsafe fn foo(undef: i32) {} +//~^ ERROR `#[pre_init]` function must have signature `unsafe fn()` + +#[entry] +fn bar() -> ! { + loop {} +} diff --git a/cortex-m-rt/tests/compile-fail/pre-init-hidden.rs b/cortex-m-rt/tests/compile-fail/pre-init-hidden.rs new file mode 100644 index 00000000..63ab90bb --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/pre-init-hidden.rs @@ -0,0 +1,23 @@ +// ignore-test :sadface: it's not possible to prevent this user error at compile time +// see rust-lang/rust#53975 for details + +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +mod hidden { + use cortex_m_rt::pre_init; + + // this function needs to be "reachable" (all modules between it and the crate root must be + // `pub`) or the function will be ignored + #[entry] + unsafe fn pre_init() {} //~ ERROR function is never used +} + +#[entry] +fn foo() -> ! { + //~ ERROR function is never used + loop {} +} diff --git a/cortex-m-rt/tests/compile-fail/pre-init-twice.rs b/cortex-m-rt/tests/compile-fail/pre-init-twice.rs new file mode 100644 index 00000000..74a3f6bd --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/pre-init-twice.rs @@ -0,0 +1,18 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, pre_init}; + +#[pre_init] +unsafe fn foo() {} + +#[pre_init] //~ ERROR symbol `__pre_init` is already defined +unsafe fn bar() {} + +#[entry] +fn baz() -> ! { + loop {} +} diff --git a/cortex-m-rt/tests/compile-fail/unsafe-init-static.rs b/cortex-m-rt/tests/compile-fail/unsafe-init-static.rs new file mode 100644 index 00000000..23105f15 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/unsafe-init-static.rs @@ -0,0 +1,45 @@ +//! Makes sure that the expansion of the attributes doesn't put the resource initializer in an +//! implicit `unsafe` block. + +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[allow(non_camel_case_types)] +enum interrupt { + UART0, +} + +const unsafe fn init() -> u32 { 0 } + +#[entry] +fn foo() -> ! { + static mut X: u32 = init(); //~ ERROR requires unsafe + + loop {} +} + +#[exception] +fn SVCall() { + static mut X: u32 = init(); //~ ERROR requires unsafe +} + +#[exception] +unsafe fn DefaultHandler(_irq: i16) { + static mut X: u32 = init(); //~ ERROR requires unsafe +} + +#[exception] +unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! { + static mut X: u32 = init(); //~ ERROR requires unsafe + loop {} +} + +#[interrupt] +fn UART0() { + static mut X: u32 = init(); //~ ERROR requires unsafe +} diff --git a/cortex-m-rt/tests/compile-fail/whitelist-1.rs b/cortex-m-rt/tests/compile-fail/whitelist-1.rs new file mode 100644 index 00000000..9c5133b8 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/whitelist-1.rs @@ -0,0 +1,32 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[inline] //~ ERROR this attribute is not allowed on a cortex-m-rt entry point +#[entry] +fn foo() -> ! { + loop {} +} + +#[inline] //~ ERROR this attribute is not allowed on an exception handler controlled by cortex-m-rt +#[exception] +fn SysTick() {} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, + USART2, +} + +#[inline] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt +#[interrupt] +fn USART1() {} + +#[cfg(feature = "device")] +#[cfg_attr(feature = "device", inline)] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt +#[interrupt] +fn USART2() {} diff --git a/cortex-m-rt/tests/compile-fail/whitelist-2.rs b/cortex-m-rt/tests/compile-fail/whitelist-2.rs new file mode 100644 index 00000000..086f909f --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/whitelist-2.rs @@ -0,0 +1,32 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a cortex-m-rt entry point +#[entry] +fn foo() -> ! { + loop {} +} + +#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on an exception handler controlled by cortex-m-rt +#[exception] +fn SysTick() {} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, + USART2, +} + +#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt +#[interrupt] +fn USART1() {} + +#[cfg(feature = "device")] +#[cfg_attr(feature = "device", export_name = "not_allowed")] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt +#[interrupt] +fn USART2() {} diff --git a/cortex-m-rt/tests/compile-fail/whitelist-3.rs b/cortex-m-rt/tests/compile-fail/whitelist-3.rs new file mode 100644 index 00000000..9e5fb332 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/whitelist-3.rs @@ -0,0 +1,32 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[no_mangle] //~ ERROR this attribute is not allowed on a cortex-m-rt entry point +#[entry] +fn foo() -> ! { + loop {} +} + +#[no_mangle] //~ ERROR this attribute is not allowed on an exception handler controlled by cortex-m-rt +#[exception] +fn SysTick() {} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, + USART2, +} + +#[no_mangle] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt +#[interrupt] +fn USART1() {} + +#[cfg(feature = "device")] +#[cfg_attr(feature = "device", no_mangle)] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt +#[interrupt] +fn USART2() {} diff --git a/cortex-m-rt/tests/compile-fail/whitelist-4.rs b/cortex-m-rt/tests/compile-fail/whitelist-4.rs new file mode 100644 index 00000000..95dad292 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/whitelist-4.rs @@ -0,0 +1,32 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[must_use] //~ ERROR this attribute is not allowed on a cortex-m-rt entry point +#[entry] +fn foo() -> ! { + loop {} +} + +#[must_use] //~ ERROR this attribute is not allowed on an exception handler controlled by cortex-m-rt +#[exception] +fn SysTick() {} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, + USART2, +} + +#[must_use] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt +#[interrupt] +fn USART1() {} + +#[cfg(feature = "device")] +#[cfg_attr(feature = "device", must_use)] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt +#[interrupt] +fn USART2() {} diff --git a/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs b/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs new file mode 100644 index 00000000..31da76ab --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs @@ -0,0 +1,13 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[exception] +#[entry] //~ ERROR this attribute is not allowed on an exception handler +fn SVCall() -> ! { + loop {} +} diff --git a/cortex-m-rt/tests/compiletest.rs b/cortex-m-rt/tests/compiletest.rs new file mode 100644 index 00000000..cf63d22d --- /dev/null +++ b/cortex-m-rt/tests/compiletest.rs @@ -0,0 +1,22 @@ +extern crate compiletest_rs as compiletest; + +use std::path::PathBuf; + +fn run_mode(mode: &'static str) { + let mut config = compiletest::Config::default(); + + config.mode = mode.parse().expect("Invalid mode"); + config.src_base = PathBuf::from(format!("tests/{}", mode)); + config.target_rustcflags = Some( + "-L ../target/debug -L ../target/debug/deps -C panic=abort --cfg feature=\"device\"" + .to_owned(), + ); + config.clean_rmeta(); // If your tests import the parent crate, this helps with E0464 + + compiletest::run_tests(&config); +} + +#[test] +fn compile_test() { + run_mode("compile-fail"); +} diff --git a/cortex-m-semihosting/src/lib.rs b/cortex-m-semihosting/src/lib.rs index a67b84d8..3bc23ea3 100644 --- a/cortex-m-semihosting/src/lib.rs +++ b/cortex-m-semihosting/src/lib.rs @@ -153,7 +153,7 @@ //! //! ## `inline-asm` //! -//! When this feature is enabled semihosting is implemented using inline assembly (`llvm_asm!`) and +//! When this feature is enabled semihosting is implemented using inline assembly and //! compiling this crate requires nightly. //! //! When this feature is disabled semihosting is implemented using FFI calls into an external @@ -179,7 +179,6 @@ //! //! [pdf]: http://infocenter.arm.com/help/topic/com.arm.doc.dui0471e/DUI0471E_developing_for_arm_processors.pdf -#![cfg_attr(feature = "inline-asm", feature(llvm_asm))] #![deny(missing_docs)] #![no_std] @@ -213,7 +212,12 @@ pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize { #[cfg(all(thumb, feature = "inline-asm", not(feature = "no-semihosting")))] () => { let mut nr = _nr; - llvm_asm!("bkpt 0xAB" : "+{r0}"(nr) : "{r1}"(_arg) :: "volatile"); + core::arch::asm!( + "bkpt #0xab", + inout("r0") nr, + in("r1") _arg, + options(nomem, nostack, preserves_flags) + ); nr } diff --git a/device.x b/device.x new file mode 100644 index 00000000..25e1a330 --- /dev/null +++ b/device.x @@ -0,0 +1,3 @@ +/* Sample device.x file used for corte-m-rt tests only. */ +PROVIDE(WWDG = DefaultHandler); +PROVIDE(PVD = DefaultHandler); diff --git a/memory.x b/memory.x new file mode 100644 index 00000000..a5e118d6 --- /dev/null +++ b/memory.x @@ -0,0 +1,23 @@ +/* Sample memory.x file used for cortex-m-rt examples and tests only. + * You must provide your own memory.x with values correct for your device, + * don't just copy these. + */ + +MEMORY +{ + /* FLASH and RAM are mandatory memory regions */ + /* Update examples/data_overflow.rs if you change these sizes. */ + FLASH : ORIGIN = 0x00000000, LENGTH = 256K + RAM : ORIGIN = 0x20000000, LENGTH = 64K + + /* More memory regions can declared: for example this is a second RAM region */ + /* CCRAM : ORIGIN = 0x10000000, LENGTH = 8K */ +} + +/* The location of the stack can be overridden using the `_stack_start` symbol. + By default it will be placed at the end of the RAM region */ +/* _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM); */ + +/* The location of the .text section can be overridden using the `_stext` symbol. + By default it will place after .vector_table */ +/* _stext = ORIGIN(FLASH) + 0x40c; */ diff --git a/src/peripheral/itm.rs b/src/peripheral/itm.rs index f8e9e25d..7291ae06 100644 --- a/src/peripheral/itm.rs +++ b/src/peripheral/itm.rs @@ -118,6 +118,7 @@ impl core::convert::TryFrom for LocalTimestampOptions { /// Converts an integer value to an enabled [LocalTimestampOptions] /// variant. Accepted values are: 1, 4, 16, 64. Any other value /// yields `Err(())`. + #[inline] fn try_from(value: u8) -> Result { match value { 1 => Ok(Self::Enabled),