diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 2e179aa664..f04f15683e 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -24,7 +24,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: diff --git a/.github/workflows/ci-nightly.yml b/.github/workflows/ci-nightly.yml index 46d25f08bd..825908517b 100644 --- a/.github/workflows/ci-nightly.yml +++ b/.github/workflows/ci-nightly.yml @@ -19,13 +19,16 @@ permissions: jobs: ci-build: + # Do not run job on forks + if: github.repository == 'tock/tock' + strategy: matrix: os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: ci-job-syntax run: make ci-job-syntax @@ -43,6 +46,9 @@ jobs: path: tools/ci-artifacts ci-tests: + # Do not run job on forks + if: github.repository == 'tock/tock' + strategy: matrix: os: [ubuntu-latest, macos-latest] @@ -61,7 +67,7 @@ jobs: run: | brew install zeromq if: matrix.os == 'macos-latest' - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: ci-job-libraries run: make ci-job-libraries - name: ci-job-archs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6732130e97..f172cc7dae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-node@v3 - name: ci-job-format run: make ci-job-format @@ -50,7 +50,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: ci-job-clippy run: make ci-job-clippy @@ -61,7 +61,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: ci-job-syntax run: make ci-job-syntax - name: ci-job-compilation @@ -91,7 +91,7 @@ jobs: run: | sudo apt install libudev-dev libzmq3-dev if: matrix.os == 'ubuntu-latest' - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: ci-job-libraries run: make ci-job-libraries - name: ci-job-archs @@ -121,6 +121,6 @@ jobs: continue-on-error: true run: | sudo apt install meson - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: ci-job-qemu run: make ci-job-qemu diff --git a/.github/workflows/litex_sim.yml b/.github/workflows/litex_sim.yml index f2877aea3f..fe39f16f38 100644 --- a/.github/workflows/litex_sim.yml +++ b/.github/workflows/litex_sim.yml @@ -39,7 +39,7 @@ jobs: # that other steps (such as the Rust toolchain) depend on files # in this repo. - name: Checkout the current repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Install basic packages required for the GitHub actions workflow - name: Update packages and install dependencies @@ -62,7 +62,7 @@ jobs: # Clone tock-litex support repository under ./tock-litex, check out the # targeted release. - name: Checkout the tock-litex repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: lschuermann/tock-litex # The pinned revision is different from the targeted release as @@ -97,7 +97,7 @@ jobs: # Revision to checkout defined in the main tock repository in # .libtock_c_ci_rev - name: Checkout libtock-c CI revision - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: tock/libtock-c # Pins a libtock-c revision for LiteX CI tests. In case of diff --git a/.github/workflows/mergequeue_docs.yml b/.github/workflows/mergequeue_docs.yml index 6cf8794b62..f99449d536 100644 --- a/.github/workflows/mergequeue_docs.yml +++ b/.github/workflows/mergequeue_docs.yml @@ -28,7 +28,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - # This also sets up the rustup environment name: ci-netlify-build run: tools/netlify-build.sh diff --git a/.github/workflows/tockbot-nightly.yml b/.github/workflows/tockbot-nightly.yml index 158373505d..928114ef3f 100644 --- a/.github/workflows/tockbot-nightly.yml +++ b/.github/workflows/tockbot-nightly.yml @@ -22,6 +22,9 @@ jobs: dispatcher: runs-on: ubuntu-latest + # Do not run job on forks + if: github.repository == 'tock/tock' + # This job determines which other jobs should be run: outputs: run-maint-nightly: ${{ steps.dispatch-logic.outputs.run-maint-nightly }} diff --git a/.github/workflows/treadmill-ci.yml b/.github/workflows/treadmill-ci.yml new file mode 100644 index 0000000000..89166caf48 --- /dev/null +++ b/.github/workflows/treadmill-ci.yml @@ -0,0 +1,218 @@ +# Licensed under the Apache License, Version 2.0 or the MIT License. +# SPDX-License-Identifier: Apache-2.0 OR MIT +# Copyright Tock Contributors 2024. + +# This workflow contains all Treadmill-based hardware CI jobs. +# +# Treadmill is a distributed hardware testbed developed within the Tock OS +# project. For more information on Treadmill, have a look at its documentation +# [1] or repository [2]. +# +# This workflow is based on the Treadmill GitHub Actions integration guide [3]. +# In addition, it features the ability to run multiple Treadmill jobs and +# test-execute stages through GitHub Action's job matrices, and uses a GitHub +# environment to allow deployments with access to secrets for select PRs. +# +# [1]: https://book.treadmill.ci/ +# [2]: https://github.com/treadmill-tb/treadmill +# [3]: https://book.treadmill.ci/user-guide/github-actions-integration.html + +name: treadmill-ci +env: + TERM: xterm # Makes tput work in actions output + +# Controls when the action will run. Triggers the workflow on pull request and +# merge group checks: +# +# KEEP IN SYNC WITH `environment:` ATTRIBUTE BELOW: +on: + push: + branches: + - master + # Pull requests from forks will not have access to the required GitHub API + # secrets below, even if they are using an appropriate deployment environment + # and the workflow runs have been approved according to this environment's + # rules. We don't know whether this is a bug on GitHub's end or deliberate. + # Either way, for now we disable this workflow to run on PRs until we have + # an API proxy that securely performs these GitHub API calls (adding runners + # and starting Treadmill jobs with those runner registration tokens), which + # allows this workflow to run without access to repository secrets. + #pull_request: + merge_group: # Run CI for the GitHub merge queue + +permissions: + contents: read + +jobs: + test-prepare: + runs-on: ubuntu-latest + + # Do not run job on forks + if: github.repository == 'tock/tock' + + # This provides access to the secrets required below: + # - for `treadmill-ci`: after approval by certain persons or GH teams + # - for `treadmill-ci-merged`: without approval, on merge queue branches + # and the master branch + # + # KEEP IN SYNC WITH `on:` EVENTS ABOVE: + environment: ${{ github.event_name == 'pull_request' && 'treadmill-ci' || 'treadmill-ci-merged' }} + + outputs: + tml-job-ids: ${{ steps.treadmill-job-launch.outputs.tml-job-ids }} + tml-jobs: ${{ steps.treadmill-job-launch.outputs.tml-jobs }} + + steps: + - uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Checkout Treadmill repository + uses: actions/checkout@v4 + with: + repository: treadmill-tb/treadmill + # treadmill-tb/treadmill main as of Oct 1, 2024, 3:05 PM EDT + ref: 'c82f4d7ebddd17f8275ba52139e64e04623f30cb' + path: treadmill + + - name: Cache Treadmill CLI compilation artifacts + id: cache-tml-cli + uses: actions/cache@v4 + with: + path: treadmill/target + key: ${{ runner.os }}-tml-cli + + - name: Compile the Treadmill CLI binary + run: | + pushd treadmill + cargo build --package tml-cli + popd + echo "$PWD/treadmill/target/debug" >> "$GITHUB_PATH" + + # - uses: actions/checkout@v4 + # with: + # path: tock + + # - name: Analyze changes and determine types of tests to run + # run: | + # echo "TODO: implement this!" + + - name: Generate a token to register new just-in-time runners + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.TREADMILL_GH_APP_CLIENT_ID }} + private-key: ${{ secrets.TREADMILL_GH_APP_PRIVATE_KEY }} + + - name: Create GitHub just-in-time runners and enqueue Treadmill jobs + id: treadmill-job-launch + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + TML_API_TOKEN: ${{ secrets.TREADMILL_API_TOKEN }} + + # Currently, all tests run only on hosts attached to an nRF52840DK + DUT_BOARD: nrf52840dk + + # A Raspberry Pi OS netboot (NBD) image with a GitHub Actions + # self-hosted runner pre-configured. + # + # For the available images see + # https://book.treadmill.ci/treadmillci-deployment/images.html + IMAGE_ID: 5f4b61324c27472b5354cd11229a0936320148cd6e852fbf05e1b7ff5b4598e6 + + # Limit the supervisors to hosts that are compatible with this + # image. This is a hack until we introduce "image sets" which define + # multiple images for various supervisor hosts, but otherwise behave + # identically: + HOST_TYPE: nbd-netboot + HOST_ARCH: arm64 + run: | + # When we eventually launch tests on multiple hardware platforms in + # parallel, we need to supply different SUB_TEST_IDs here: + SUB_TEST_ID="0" + + # This runner ID uniquely identifies the GitHub Actions runner we're + # registering and allows us to launch test-execute jobs on this exact + # runner (connected to the exact board we want to run tests on). + RUNNER_ID="tml-gh-actions-runner-${GITHUB_REPOSITORY_ID}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${SUB_TEST_ID}" + + # Obtain a new just-in-time runner registration token: + RUNNER_CONFIG_JSON="$(gh api \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/${{ github.repository }}/actions/runners/generate-jitconfig \ + -f "name=$RUNNER_ID" \ + -F "runner_group_id=1" \ + -f "labels[]=$RUNNER_ID" \ + -f "work_folder=_work")" + echo "Generated configuration for runner $(echo "$RUNNER_CONFIG_JSON" | jq -r '.runner.name')" + + # Generate a set of job paramters that includes the GitHub runner + # registration token and a script that shuts down the host once the + # runner has run through successfully: + TML_JOB_PARAMETERS="{\ + \"gh-actions-runner-encoded-jit-config\": {\ + \"secret\": true, \ + \"value\": \"$(echo "$RUNNER_CONFIG_JSON" | jq -r '.encoded_jit_config')\" \ + }, \ + \"gh-actions-runner-exec-stop-post-sh\": {\ + \"secret\": false, \ + \"value\": \"if [ \\\"\$SERVICE_RESULT\\\" = \\\"success\\\" ]; then tml-puppet job terminate; fi\" \ + }\ + }" + + echo "Enqueueing treadmill job:" + TML_JOB_ID_JSON="$(tml job enqueue \ + "$IMAGE_ID" \ + --tag-config "board:$DUT_BOARD;host-type:$HOST_TYPE;host-arch:$HOST_ARCH" \ + --parameters "$TML_JOB_PARAMETERS" \ + )" + + TML_JOB_ID="$(echo "$TML_JOB_ID_JSON" | jq -r .job_id)" + echo "Enqueued Treadmill job with ID $TML_JOB_ID" + + # Pass the job IDs and other configuration data into the outputs of + # this step, such that we can run test-execute job instances for each + # Treadmill job we've started: + echo "tml-job-ids=[ \ + \"$TML_JOB_ID\" \ + ]" >> "$GITHUB_OUTPUT" + + echo "tml-jobs={ \ + \"$TML_JOB_ID\": { \ + \"runner-id\": \"$RUNNER_ID\", \ + } \ + }" >> "$GITHUB_OUTPUT" + + test-execute: + needs: test-prepare + + strategy: + matrix: + tml-job-id: ${{ fromJSON(needs.test-prepare.outputs.tml-job-ids) }} + + runs-on: ${{ fromJSON(needs.test-prepare.outputs.tml-jobs)[matrix.tml-job-id].runner-id }} + + steps: + - name: Print Treadmill Job Context and Debug Information + run: | + echo "Treadmill job id: ${{ matrix.tml-job-id }}" + echo "GitHub Actions Runner ID: ${{ fromJSON(needs.test-prepare.outputs.tml-jobs)[matrix.tml-job-id] }}" + echo "Network configration:" + ip address + echo "Attached USB devices:" + lsusb + echo "Parameters:" + ls /run/tml/parameters + + - uses: actions/checkout@v4 + + - uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Build the Tock kernel + run: | + pushd boards/nordic/nrf52840dk + unset RUSTFLAGS + make + popd + + # TODO: this job runs on the Treadmill host, with access to the target + # board. Add additional test & testing scripts. diff --git a/Cargo.toml b/Cargo.toml index d55399b6e0..802fae1f36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -226,7 +226,6 @@ branches_sharing_code = "allow" missing_const_for_fn = "allow" redundant_pub_crate = "allow" equatable_if_let = "allow" -fallible_impl_from = "allow" derive_partial_eq_without_eq = "allow" empty_line_after_doc_comments = "allow" trait_duplication_in_bounds = "allow" diff --git a/boards/acd52832/Cargo.toml b/boards/acd52832/Cargo.toml index fd489a00f9..0f2227e3ce 100644 --- a/boards/acd52832/Cargo.toml +++ b/boards/acd52832/Cargo.toml @@ -21,5 +21,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/acd52832/layout.ld b/boards/acd52832/layout.ld index 9f56cf4afa..b3013bbbec 100644 --- a/boards/acd52832/layout.ld +++ b/boards/acd52832/layout.ld @@ -12,4 +12,4 @@ MEMORY PAGE_SIZE = 4K; -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/apollo3/lora_things_plus/Cargo.toml b/boards/apollo3/lora_things_plus/Cargo.toml index 1e7066581d..74c3c25c66 100644 --- a/boards/apollo3/lora_things_plus/Cargo.toml +++ b/boards/apollo3/lora_things_plus/Cargo.toml @@ -20,6 +20,9 @@ capsules-core = { path = "../../../capsules/core" } capsules-extra = { path = "../../../capsules/extra" } capsules-system = { path = "../../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../../build_scripts" } + [lints] workspace = true diff --git a/boards/apollo3/lora_things_plus/Makefile b/boards/apollo3/lora_things_plus/Makefile index 152f7c5cab..1bda4b1082 100644 --- a/boards/apollo3/lora_things_plus/Makefile +++ b/boards/apollo3/lora_things_plus/Makefile @@ -44,14 +44,8 @@ flash-app: .PHONY: test test: - mkdir -p $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp ../../kernel_layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/ $(Q)OBJCOPY=${OBJCOPY} PORT=$(PORT) $(CARGO) test $(NO_RUN) --bin $(PLATFORM) --release .PHONY: test-atecc508a test-atecc508a: - mkdir -p $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp ../../kernel_layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/ $(Q)OBJCOPY=${OBJCOPY} PORT=$(PORT) $(CARGO) test $(NO_RUN) --bin $(PLATFORM) --release --features atecc508a diff --git a/boards/apollo3/lora_things_plus/layout.ld b/boards/apollo3/lora_things_plus/layout.ld index 47f8e81def..6f2748d6ce 100644 --- a/boards/apollo3/lora_things_plus/layout.ld +++ b/boards/apollo3/lora_things_plus/layout.ld @@ -20,4 +20,4 @@ _lkv_data = LENGTH(kv_data); PAGE_SIZE = 8K; -INCLUDE ../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/apollo3/redboard_artemis_atp/Cargo.toml b/boards/apollo3/redboard_artemis_atp/Cargo.toml index 3254d4b14c..2e1b5a3ae4 100644 --- a/boards/apollo3/redboard_artemis_atp/Cargo.toml +++ b/boards/apollo3/redboard_artemis_atp/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../../capsules/core" } capsules-extra = { path = "../../../capsules/extra" } capsules-system = { path = "../../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../../build_scripts" } + [lints] workspace = true diff --git a/boards/apollo3/redboard_artemis_atp/Makefile b/boards/apollo3/redboard_artemis_atp/Makefile index ba9c32c40b..a698dd8e79 100644 --- a/boards/apollo3/redboard_artemis_atp/Makefile +++ b/boards/apollo3/redboard_artemis_atp/Makefile @@ -44,7 +44,4 @@ flash-app: .PHONY: test test: - mkdir -p $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp ../../kernel_layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/ $(Q)OBJCOPY=${OBJCOPY} PORT=$(PORT) $(CARGO) test $(NO_RUN) --bin $(PLATFORM) --release diff --git a/boards/apollo3/redboard_artemis_atp/layout.ld b/boards/apollo3/redboard_artemis_atp/layout.ld index a2e4bbdd4d..152c7e0d86 100644 --- a/boards/apollo3/redboard_artemis_atp/layout.ld +++ b/boards/apollo3/redboard_artemis_atp/layout.ld @@ -15,4 +15,4 @@ MEMORY PAGE_SIZE = 8K; -INCLUDE ../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/apollo3/redboard_artemis_nano/Cargo.toml b/boards/apollo3/redboard_artemis_nano/Cargo.toml index f7acdf24d9..e6335ba5ff 100644 --- a/boards/apollo3/redboard_artemis_nano/Cargo.toml +++ b/boards/apollo3/redboard_artemis_nano/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../../capsules/core" } capsules-extra = { path = "../../../capsules/extra" } capsules-system = { path = "../../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../../build_scripts" } + [lints] workspace = true diff --git a/boards/apollo3/redboard_artemis_nano/Makefile b/boards/apollo3/redboard_artemis_nano/Makefile index 3f6828d6f6..a7e5912434 100644 --- a/boards/apollo3/redboard_artemis_nano/Makefile +++ b/boards/apollo3/redboard_artemis_nano/Makefile @@ -44,7 +44,4 @@ flash-app: .PHONY: test test: - mkdir -p $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp ../../kernel_layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/ $(Q)OBJCOPY=${OBJCOPY} PORT=$(PORT) $(CARGO) test $(NO_RUN) --bin $(PLATFORM) --release diff --git a/boards/apollo3/redboard_artemis_nano/layout.ld b/boards/apollo3/redboard_artemis_nano/layout.ld index a2e4bbdd4d..152c7e0d86 100644 --- a/boards/apollo3/redboard_artemis_nano/layout.ld +++ b/boards/apollo3/redboard_artemis_nano/layout.ld @@ -15,4 +15,4 @@ MEMORY PAGE_SIZE = 8K; -INCLUDE ../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/arty_e21/Cargo.toml b/boards/arty_e21/Cargo.toml index 8a6f7eeb5f..4b32b89326 100644 --- a/boards/arty_e21/Cargo.toml +++ b/boards/arty_e21/Cargo.toml @@ -20,5 +20,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/arty_e21/layout.ld b/boards/arty_e21/layout.ld index 2a1f31fa67..4af6345199 100644 --- a/boards/arty_e21/layout.ld +++ b/boards/arty_e21/layout.ld @@ -9,4 +9,4 @@ MEMORY ram (rwx) : ORIGIN = 0x80000000, LENGTH = 64K } -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/build.rs b/boards/build.rs index 0a29ee2a54..e1c1b3c3ed 100644 --- a/boards/build.rs +++ b/boards/build.rs @@ -13,69 +13,9 @@ //! # ... //! build = "../path/to/build.rs" //! ``` - -use std::fs; -use std::path::Path; - -const LINKER_SCRIPT: &str = "layout.ld"; +//! +//! Out-of-tree boards are recommended to copy this file into their board crate. fn main() { - if !Path::new(LINKER_SCRIPT).exists() { - panic!("Boards must provide a `layout.ld` link script file"); - } - - // The `RUSTFLAGS` that the Tock config files set can be easily overridden - // by command line flags. The build will still succeed but the resulting - // binary may be invalid as it was not built with the intended flags. This - // check seeks to prevent that. Our approach is we set a sentinel flag in - // our configuration file and then check that it is set here. If it isn't, - // the flags were overwritten and the build will be invalid. - // - // We only do this check if we are actually building for an embedded target - // (i.e., the TARGET is not the same as the HOST). This avoids a false - // positive when running tools like `cargo clippy`. - // - // If you are intentionally not using the standard Tock config files, set - // `cfg-tock-buildflagssentinel` in your cargo config to prevent this - // error. - if std::env::var("HOST") != std::env::var("TARGET") { - let rust_flags = std::env::var("CARGO_ENCODED_RUSTFLAGS"); - if !rust_flags - .iter() - .any(|f| f.contains("cfg_tock_buildflagssentinel")) - { - panic!( - "Incorrect build configuration. Verify you are using unstable cargo and have not unintentionally set the RUSTFLAGS environment variable." - ); - } - } - - // Include the folder where the board's Cargo.toml is in the linker file - // search path. - println!("cargo:rustc-link-arg=-L{}", std::env!("CARGO_MANIFEST_DIR")); - // `-Tlayout.ld`: Use the linker script `layout.ld` all boards must provide. - println!("cargo:rustc-link-arg=-T{}", LINKER_SCRIPT); - - track_linker_script(LINKER_SCRIPT); -} - -/// Track the given linker script and all of its `INCLUDE`s so that the build -/// is rerun when any of them change. -fn track_linker_script>(path: P) { - let path = path.as_ref(); - - assert!(path.is_file(), "expected path {path:?} to be a file"); - - println!("cargo:rerun-if-changed={}", path.display()); - - // Find all the `INCLUDE ` lines in the linker script. - let link_script = fs::read_to_string(path).expect("failed to read {path:?}"); - let includes = link_script - .lines() - .filter_map(|line| line.strip_prefix("INCLUDE").map(str::trim)); - - // Recursively track included linker scripts. - for include in includes { - track_linker_script(include); - } + tock_build_scripts::default_linker_script(); } diff --git a/boards/build_scripts/Cargo.toml b/boards/build_scripts/Cargo.toml new file mode 100644 index 0000000000..b10760eefd --- /dev/null +++ b/boards/build_scripts/Cargo.toml @@ -0,0 +1,10 @@ +# Licensed under the Apache License, Version 2.0 or the MIT License. +# SPDX-License-Identifier: Apache-2.0 OR MIT +# Copyright Tock Contributors 2024. + +[package] +name = "tock_build_scripts" +description = """Helper functions for compiling Tock boards.""" +version.workspace = true +authors.workspace = true +edition.workspace = true diff --git a/boards/build_scripts/README.md b/boards/build_scripts/README.md new file mode 100644 index 0000000000..3221e675ff --- /dev/null +++ b/boards/build_scripts/README.md @@ -0,0 +1,43 @@ +Tock Build Scripts Support Crate +================================ + +This crate provides helpers for building Tock boards. + +Out-of-tree Tock kernel configurations (i.e., Tock boards) can optionally +include this crate as a dependency to use the +[build.rs](https://doc.rust-lang.org/cargo/reference/build-scripts.html) +functionality provided by the kernel. + +This crate also packages the default `tock_kernel_layout.ld` linker script. + +Usage +----- + +There are three general steps to use this crate. + +1. This crate should be included as a build dependency in the boards's + Cargo.toml file: + + ```toml + # Cargo.toml + + # ...Existing Cargo.toml contents... + + [build-dependencies] + tock_build_scripts = { git = "https://github.com/tock/tock"} + ``` + + This will ensure the crate and the build scripts are available for the board + build. + +2. This crate provides a helper function which can used from the board's + build.rs file. In the common case, you just call the provided function from + the build.rs file in your crate's root: + + ```rs + // build.rs + + fn main() { + tock_build_scripts::default_linker_script(); + } + ``` diff --git a/boards/build_scripts/src/default.rs b/boards/build_scripts/src/default.rs new file mode 100644 index 0000000000..402d532a5f --- /dev/null +++ b/boards/build_scripts/src/default.rs @@ -0,0 +1,126 @@ +// Licensed under the Apache License, Version 2.0 or the MIT License. +// SPDX-License-Identifier: Apache-2.0 OR MIT +// Copyright Tock Contributors 2024. + +//! Provide helpers for building Tock boards that match the default conventions. + +use std::fs; +use std::path::Path; + +const LINKER_SCRIPT: &str = "layout.ld"; + +/// Setup the Tock board to build with a board-provided linker script called +/// `layout.ld`. +/// +/// The board linker script (i.e., `layout.ld`) should end with the command: +/// +/// ``` +/// INCLUDE tock_kernel_layout.ld +/// ``` +/// +/// This function will ensure that the linker's search path is configured to +/// find `tock_kernel_layout.ld`. +pub fn default_linker_script() { + if !Path::new(LINKER_SCRIPT).exists() { + panic!("Boards must provide a `layout.ld` link script file"); + } + + rustflags_check(); + + include_tock_kernel_layout(); + + add_board_dir_to_linker_search_path(); + + set_and_track_linker_script(LINKER_SCRIPT); +} + +/// Include the folder where the board's Cargo.toml is in the linker file +/// search path. +pub fn add_board_dir_to_linker_search_path() { + // Note this is a different path than the one returned by + // `std::env!("CARGO_MANIFEST_DIR")` in `include_tock_kernel_layout()`, + // since that is evaluated at compile + // time while this `std::env::var("CARGO_MANIFEST_DIR")` is evaluated at runtime. + println!( + "cargo:rustc-link-arg=-L{}", + std::env::var("CARGO_MANIFEST_DIR").unwrap() + ); +} + +/// Include the folder where this build_script crate's Cargo.toml is in the +/// linker file search path for `tock_kernel_layout.ld`, and instruct cargo +/// to rebuild if that linker script is changed. +pub fn include_tock_kernel_layout() { + println!("cargo:rustc-link-arg=-L{}", std::env!("CARGO_MANIFEST_DIR")); + // Directive to rebuild if the linker script in this crate is changed. + println!( + "cargo:rerun-if-changed={}", + Path::new(std::env!("CARGO_MANIFEST_DIR")) + .join("tock_kernel_layout.ld") + .to_string_lossy() + ); +} + +pub fn rustflags_check() { + // The `RUSTFLAGS` that the Tock config files set can be easily overridden + // by command line flags. The build will still succeed but the resulting + // binary may be invalid as it was not built with the intended flags. This + // check seeks to prevent that. Our approach is we set a sentinel flag in + // our configuration file and then check that it is set here. If it isn't, + // the flags were overwritten and the build will be invalid. + // + // We only do this check if we are actually building for an embedded target + // (i.e., the TARGET is not the same as the HOST). This avoids a false + // positive when running tools like `cargo clippy`. + // + // If you are intentionally not using the standard Tock config files, set + // `cfg-tock-buildflagssentinel` in your cargo config to prevent this + // error. + if std::env::var("HOST") != std::env::var("TARGET") { + let rust_flags = std::env::var("CARGO_ENCODED_RUSTFLAGS"); + if !rust_flags + .iter() + .any(|f| f.contains("cfg_tock_buildflagssentinel")) + { + panic!( + "Incorrect build configuration. Verify you are using unstable cargo and have not unintentionally set the RUSTFLAGS environment variable." + ); + } + } +} + +/// Pass the given linker script to cargo, and track it and all of its `INCLUDE`s +pub fn set_and_track_linker_script + ToString>(path: P) { + // Use the passed linker script + println!("cargo:rustc-link-arg=-T{}", path.to_string()); + track_linker_script(path); +} + +/// Track the given linker script and all of its `INCLUDE`s so that the build +/// is rerun when any of them change. +pub fn track_linker_script>(path: P) { + let path = path.as_ref(); + + // Skip the default Tock linker script as we have manually added the + // containing directory to the linker search path and we do not know the + // path to add the rerun directive here. Instead, we add the rerun directory + // for the default Tock linker script manually before calling this function. + if path.to_str() == Some("tock_kernel_layout.ld") { + return; + } + + assert!(path.is_file(), "expected path {path:?} to be a file"); + + println!("cargo:rerun-if-changed={}", path.display()); + + // Find all the `INCLUDE ` lines in the linker script. + let link_script = fs::read_to_string(path).expect("failed to read {path:?}"); + let includes = link_script + .lines() + .filter_map(|line| line.strip_prefix("INCLUDE").map(str::trim)); + + // Recursively track included linker scripts. + for include in includes { + track_linker_script(include); + } +} diff --git a/boards/build_scripts/src/lib.rs b/boards/build_scripts/src/lib.rs new file mode 100644 index 0000000000..b861129dd0 --- /dev/null +++ b/boards/build_scripts/src/lib.rs @@ -0,0 +1,7 @@ +// Licensed under the Apache License, Version 2.0 or the MIT License. +// SPDX-License-Identifier: Apache-2.0 OR MIT +// Copyright Tock Contributors 2024. + +pub mod default; + +pub use default::default_linker_script; diff --git a/boards/kernel_layout.ld b/boards/build_scripts/tock_kernel_layout.ld similarity index 98% rename from boards/kernel_layout.ld rename to boards/build_scripts/tock_kernel_layout.ld index 7220f55844..c094343b55 100644 --- a/boards/kernel_layout.ld +++ b/boards/build_scripts/tock_kernel_layout.ld @@ -222,39 +222,6 @@ SECTIONS } > ccfg - /* Section for application binaries in flash. - * - * This section is put into the "prog" memory, which is reserved for - * applications. This section is not used for the kernel, but including it - * in the .elf file allows for concatenating application binaries with the - * kernel. - */ - .apps : - { - /* _sapps symbol used by Tock to look for first application. */ - . = ALIGN(4); - _sapps = .; - - /* Include placeholder bytes in this section so that the linker - * includes a segment for it. Otherwise the section will be empty and - * the linker will ignore it when defining the segments. - * If less then 4 bytes, some linkers set this section to size 0 - * and openocd fails to write it. - * - * An issue has been submitted https://github.com/raspberrypi/openocd/issues/25 - */ - BYTE(0xFF) - BYTE(0xFF) - BYTE(0xFF) - BYTE(0xFF) - } > prog - /* _eapps symbol used by tock to calculate the length of app flash */ - _eapps = _sapps + LENGTH(prog); - - - - - /* Kernel data that must be relocated. This is program data that is @@ -297,6 +264,42 @@ SECTIONS } > ram + + /* Section for application binaries in flash. + * + * This section is put into the "prog" memory, which is reserved for + * applications. This section is not used for the kernel, but including it + * in the .elf file allows for concatenating application binaries with the + * kernel. By default the loader will not include this section in the + * flashed binary. For boards that wish to use this section, it can be set + * to load with `objcopy` similar to: + * + * arm-none-eabi-objcopy --set-section-flags .apps=LOAD,ALLOC + */ + .apps (NOLOAD) : + { + /* _sapps symbol used by Tock to look for first application. */ + . = ALIGN(4); + _sapps = .; + + /* Include placeholder bytes in this section so that the linker + * includes a segment for it. Otherwise the section will be empty and + * the linker will ignore it when defining the segments. + * If less then 4 bytes, some linkers set this section to size 0 + * and openocd fails to write it. + * + * An issue has been submitted https://github.com/raspberrypi/openocd/issues/25 + */ + BYTE(0xFF) + BYTE(0xFF) + BYTE(0xFF) + BYTE(0xFF) + } > prog + /* _eapps symbol used by tock to calculate the length of app flash */ + _eapps = _sapps + LENGTH(prog); + + + .sram (NOLOAD) : { /* Kernel BSS section. Memory that is expected to be initialized to diff --git a/boards/clue_nrf52840/Cargo.toml b/boards/clue_nrf52840/Cargo.toml index 11b6ba7262..32ecdba741 100644 --- a/boards/clue_nrf52840/Cargo.toml +++ b/boards/clue_nrf52840/Cargo.toml @@ -21,5 +21,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/clue_nrf52840/layout.ld b/boards/clue_nrf52840/layout.ld index 155ce70074..bef96ea7ae 100644 --- a/boards/clue_nrf52840/layout.ld +++ b/boards/clue_nrf52840/layout.ld @@ -21,4 +21,4 @@ MEMORY PAGE_SIZE = 4K; -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/components/Cargo.toml b/boards/components/Cargo.toml index 2fde5866c1..02b90b70e5 100644 --- a/boards/components/Cargo.toml +++ b/boards/components/Cargo.toml @@ -16,5 +16,7 @@ capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } segger = { path = "../../chips/segger" } +tock-tbf = { path = "../../libraries/tock-tbf" } + [lints] workspace = true diff --git a/boards/components/src/appid/checker_signature.rs b/boards/components/src/appid/checker_signature.rs new file mode 100644 index 0000000000..d3d13871b0 --- /dev/null +++ b/boards/components/src/appid/checker_signature.rs @@ -0,0 +1,106 @@ +// Licensed under the Apache License, Version 2.0 or the MIT License. +// SPDX-License-Identifier: Apache-2.0 OR MIT +// Copyright Tock Contributors 2024. + +//! Components for signature credential checkers. + +use core::mem::MaybeUninit; +use kernel::component::Component; +use kernel::hil::{digest, public_key_crypto}; + +#[macro_export] +macro_rules! app_checker_signature_component_static { + ($S:ty, $H:ty, $HL:expr, $SL:expr $(,)?) => {{ + let hash_buffer = kernel::static_buf!([u8; $HL]); + let signature_buffer = kernel::static_buf!([u8; $SL]); + let checker = kernel::static_buf!( + capsules_system::process_checker::signature::AppCheckerSignature< + 'static, + $S, + $H, + $HL, + $SL, + > + ); + + (checker, hash_buffer, signature_buffer) + };}; +} + +pub type AppCheckerSignatureComponentType = + capsules_system::process_checker::signature::AppCheckerSignature<'static, S, H, HL, SL>; + +pub struct AppCheckerSignatureComponent< + S: kernel::hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL> + 'static, + H: kernel::hil::digest::DigestDataHash<'static, HL> + 'static, + const HL: usize, + const SL: usize, +> { + hasher: &'static H, + verifier: &'static S, + credential_type: tock_tbf::types::TbfFooterV2CredentialsType, +} + +impl< + S: kernel::hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>, + H: kernel::hil::digest::DigestDataHash<'static, HL>, + const HL: usize, + const SL: usize, + > AppCheckerSignatureComponent +{ + pub fn new( + hasher: &'static H, + verifier: &'static S, + credential_type: tock_tbf::types::TbfFooterV2CredentialsType, + ) -> Self { + Self { + hasher, + verifier, + credential_type, + } + } +} + +impl< + S: kernel::hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>, + H: kernel::hil::digest::DigestDataHash<'static, HL> + kernel::hil::digest::Digest<'static, HL>, + const HL: usize, + const SL: usize, + > Component for AppCheckerSignatureComponent +{ + type StaticInput = ( + &'static mut MaybeUninit< + capsules_system::process_checker::signature::AppCheckerSignature<'static, S, H, HL, SL>, + >, + &'static mut MaybeUninit<[u8; HL]>, + &'static mut MaybeUninit<[u8; SL]>, + ); + + type Output = &'static capsules_system::process_checker::signature::AppCheckerSignature< + 'static, + S, + H, + HL, + SL, + >; + + fn finalize(self, s: Self::StaticInput) -> Self::Output { + let hash_buffer = s.1.write([0; HL]); + let signature_buffer = s.2.write([0; SL]); + + let checker = s.0.write( + capsules_system::process_checker::signature::AppCheckerSignature::new( + self.hasher, + self.verifier, + hash_buffer, + signature_buffer, + self.credential_type, + ), + ); + + digest::Digest::set_client(self.hasher, checker); + public_key_crypto::signature::SignatureVerify::set_verify_client(self.verifier, checker); + + checker + } +} diff --git a/boards/components/src/appid/mod.rs b/boards/components/src/appid/mod.rs index 6bd2004270..ef5d069d71 100644 --- a/boards/components/src/appid/mod.rs +++ b/boards/components/src/appid/mod.rs @@ -7,3 +7,4 @@ pub mod assigner_tbf; pub mod checker; pub mod checker_null; pub mod checker_sha; +pub mod checker_signature; diff --git a/boards/components/src/lib.rs b/boards/components/src/lib.rs index 40640c5a66..d052facf26 100644 --- a/boards/components/src/lib.rs +++ b/boards/components/src/lib.rs @@ -74,6 +74,7 @@ pub mod rng; pub mod sched; pub mod screen; pub mod segger_rtt; +pub mod servo; pub mod sh1106; pub mod sha; pub mod sht3x; diff --git a/boards/components/src/loader/sequential.rs b/boards/components/src/loader/sequential.rs index 15def97a6d..ca85293b54 100644 --- a/boards/components/src/loader/sequential.rs +++ b/boards/components/src/loader/sequential.rs @@ -13,12 +13,13 @@ use kernel::component::Component; use kernel::deferred_call::DeferredCallClient; use kernel::platform::chip::Chip; use kernel::process::ProcessLoadingAsync; +use kernel::process::ProcessStandardDebug; #[macro_export] macro_rules! process_loader_sequential_component_static { - ($C:ty, $NUMPROCS:expr $(,)?) => {{ + ($C:ty, $D:ty, $NUMPROCS:expr $(,)?) => {{ let loader = kernel::static_buf!(kernel::process::SequentialProcessLoaderMachine< - $C, + $C, $D >); let process_binary_array = kernel::static_buf!( [Option; $NUMPROCS] @@ -28,20 +29,26 @@ macro_rules! process_loader_sequential_component_static { };}; } -pub type ProcessLoaderSequentialComponentType = - kernel::process::SequentialProcessLoaderMachine<'static, C>; +pub type ProcessLoaderSequentialComponentType = + kernel::process::SequentialProcessLoaderMachine<'static, C, D>; -pub struct ProcessLoaderSequentialComponent { +pub struct ProcessLoaderSequentialComponent< + C: Chip + 'static, + D: ProcessStandardDebug + 'static, + const NUM_PROCS: usize, +> { checker: &'static kernel::process::ProcessCheckerMachine, processes: &'static mut [Option<&'static dyn kernel::process::Process>], kernel: &'static kernel::Kernel, chip: &'static C, fault_policy: &'static dyn kernel::process::ProcessFaultPolicy, appid_policy: &'static dyn kernel::process_checker::AppIdPolicy, - storage_policy: &'static dyn kernel::process::ProcessStandardStoragePermissionsPolicy, + storage_policy: &'static dyn kernel::process::ProcessStandardStoragePermissionsPolicy, } -impl ProcessLoaderSequentialComponent { +impl + ProcessLoaderSequentialComponent +{ pub fn new( checker: &'static kernel::process::ProcessCheckerMachine, processes: &'static mut [Option<&'static dyn kernel::process::Process>], @@ -49,7 +56,7 @@ impl ProcessLoaderSequentialComponent, + storage_policy: &'static dyn kernel::process::ProcessStandardStoragePermissionsPolicy, ) -> Self { Self { checker, @@ -63,13 +70,15 @@ impl ProcessLoaderSequentialComponent Component for ProcessLoaderSequentialComponent { +impl Component + for ProcessLoaderSequentialComponent +{ type StaticInput = ( - &'static mut MaybeUninit>, + &'static mut MaybeUninit>, &'static mut MaybeUninit<[Option; NUM_PROCS]>, ); - type Output = &'static kernel::process::SequentialProcessLoaderMachine<'static, C>; + type Output = &'static kernel::process::SequentialProcessLoaderMachine<'static, C, D>; fn finalize(mut self, s: Self::StaticInput) -> Self::Output { let proc_manage_cap = diff --git a/boards/components/src/servo.rs b/boards/components/src/servo.rs new file mode 100644 index 0000000000..aa164ee4fe --- /dev/null +++ b/boards/components/src/servo.rs @@ -0,0 +1,60 @@ +// Licensed under the Apache License, Version 2.0 or the MIT License. +// SPDX-License-Identifier: Apache-2.0 OR MIT +// Copyright Tock Contributors 2024. + +//! Components for collections of servomotors. +//! +//! Usage +//! ----- +//! ```rust +//! let servo = components::servo::ServosComponent::new().finalize(components::servo_component_static!( +//! servo1, servo2, +//! )); +//! ``` +use capsules_extra::servo::Servo as ServoDriver; +use core::mem::MaybeUninit; +use kernel::component::Component; + +#[macro_export] +macro_rules! servo_component_static { + ($($S:expr),+ $(,)?) => {{ + use kernel::count_expressions; + use kernel::static_init; + const SERVO_COUNT: usize = count_expressions!($($S),+); + let arr = static_init!( + [&'static dyn Servo; SERVO_COUNT], + [ + $( + + $S + + ),+ + ] + ); + + let servo = kernel::static_buf!( capsules_extra::servo::Servo<'static, SERVO_COUNT>); + (servo, arr) + };}; +} + +pub type ServosComponentType = ServoDriver<'static, SERVO_COUNT>; + +pub struct ServosComponent {} + +impl ServosComponent { + pub fn new() -> Self { + Self {} + } +} + +impl Component for ServosComponent { + type StaticInput = ( + &'static mut MaybeUninit>, + &'static mut [&'static dyn kernel::hil::servo::Servo<'static>; SERVO_COUNT], + ); + type Output = &'static ServoDriver<'static, SERVO_COUNT>; + + fn finalize(self, static_buffer: Self::StaticInput) -> Self::Output { + static_buffer.0.write(ServoDriver::new(static_buffer.1)) + } +} diff --git a/boards/components/src/st77xx.rs b/boards/components/src/st77xx.rs index b2d73b7bc6..87789813e3 100644 --- a/boards/components/src/st77xx.rs +++ b/boards/components/src/st77xx.rs @@ -39,7 +39,7 @@ //! ``` use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm}; -use capsules_extra::bus; +use capsules_extra::bus::{self, BusAddr8}; use capsules_extra::st77xx::{ST77XXScreen, ST77XX}; use core::mem::MaybeUninit; use kernel::component::Component; @@ -72,7 +72,7 @@ macro_rules! st77xx_component_static { pub struct ST77XXComponent< A: 'static + time::Alarm<'static>, - B: 'static + bus::Bus<'static>, + B: 'static + bus::Bus<'static, BusAddr8>, P: 'static + gpio::Pin, > { alarm_mux: &'static MuxAlarm<'static, A>, @@ -82,8 +82,11 @@ pub struct ST77XXComponent< screen: &'static ST77XXScreen, } -impl, B: 'static + bus::Bus<'static>, P: 'static + gpio::Pin> - ST77XXComponent +impl< + A: 'static + time::Alarm<'static>, + B: 'static + bus::Bus<'static, BusAddr8>, + P: 'static + gpio::Pin, + > ST77XXComponent { pub fn new( alarm_mux: &'static MuxAlarm<'static, A>, @@ -102,8 +105,11 @@ impl, B: 'static + bus::Bus<'static>, P: 'stat } } -impl, B: 'static + bus::Bus<'static>, P: 'static + gpio::Pin> - Component for ST77XXComponent +impl< + A: 'static + time::Alarm<'static>, + B: 'static + bus::Bus<'static, BusAddr8>, + P: 'static + gpio::Pin, + > Component for ST77XXComponent { type StaticInput = ( &'static mut MaybeUninit>, diff --git a/boards/components/src/storage_permissions/individual.rs b/boards/components/src/storage_permissions/individual.rs index fb484e9497..8f84b3fff8 100644 --- a/boards/components/src/storage_permissions/individual.rs +++ b/boards/components/src/storage_permissions/individual.rs @@ -17,13 +17,15 @@ use core::mem::MaybeUninit; use kernel::capabilities; use kernel::component::Component; use kernel::platform::chip::Chip; +use kernel::process::ProcessStandardDebug; #[macro_export] macro_rules! storage_permissions_individual_component_static { - ($C:ty $(,)?) => {{ + ($C:ty, $D:ty $(,)?) => {{ kernel::static_buf!( capsules_system::storage_permissions::individual::IndividualStoragePermissions< $C, + $D, components::storage_permissions::individual::AppStoreCapability > ) @@ -33,34 +35,41 @@ macro_rules! storage_permissions_individual_component_static { pub struct AppStoreCapability; unsafe impl capabilities::ApplicationStorageCapability for AppStoreCapability {} -pub type StoragePermissionsIndividualComponentType = +pub type StoragePermissionsIndividualComponentType = capsules_system::storage_permissions::individual::IndividualStoragePermissions< C, + D, AppStoreCapability, >; -pub struct StoragePermissionsIndividualComponent { +pub struct StoragePermissionsIndividualComponent { _chip: core::marker::PhantomData, + _debug: core::marker::PhantomData, } -impl StoragePermissionsIndividualComponent { +impl StoragePermissionsIndividualComponent { pub fn new() -> Self { Self { _chip: core::marker::PhantomData, + _debug: core::marker::PhantomData, } } } -impl Component for StoragePermissionsIndividualComponent { +impl Component + for StoragePermissionsIndividualComponent +{ type StaticInput = &'static mut MaybeUninit< capsules_system::storage_permissions::individual::IndividualStoragePermissions< C, + D, AppStoreCapability, >, >; type Output = &'static capsules_system::storage_permissions::individual::IndividualStoragePermissions< C, + D, AppStoreCapability, >; diff --git a/boards/components/src/storage_permissions/null.rs b/boards/components/src/storage_permissions/null.rs index 7fdfc0a428..a724796a0f 100644 --- a/boards/components/src/storage_permissions/null.rs +++ b/boards/components/src/storage_permissions/null.rs @@ -8,34 +8,39 @@ use core::mem::MaybeUninit; use kernel::component::Component; use kernel::platform::chip::Chip; +use kernel::process::ProcessStandardDebug; #[macro_export] macro_rules! storage_permissions_null_component_static { - ($C:ty $(,)?) => {{ - kernel::static_buf!(capsules_system::storage_permissions::null::NullStoragePermissions<$C>) + ($C:ty, $D:ty $(,)?) => {{ + kernel::static_buf!(capsules_system::storage_permissions::null::NullStoragePermissions<$C, $D>) };}; } -pub type StoragePermissionsNullComponentType = - capsules_system::storage_permissions::null::NullStoragePermissions; +pub type StoragePermissionsNullComponentType = + capsules_system::storage_permissions::null::NullStoragePermissions; -pub struct StoragePermissionsNullComponent { +pub struct StoragePermissionsNullComponent { _chip: core::marker::PhantomData, + _debug: core::marker::PhantomData, } -impl StoragePermissionsNullComponent { +impl StoragePermissionsNullComponent { pub fn new() -> Self { Self { _chip: core::marker::PhantomData, + _debug: core::marker::PhantomData, } } } -impl Component for StoragePermissionsNullComponent { +impl Component + for StoragePermissionsNullComponent +{ type StaticInput = &'static mut MaybeUninit< - capsules_system::storage_permissions::null::NullStoragePermissions, + capsules_system::storage_permissions::null::NullStoragePermissions, >; - type Output = &'static capsules_system::storage_permissions::null::NullStoragePermissions; + type Output = &'static capsules_system::storage_permissions::null::NullStoragePermissions; fn finalize(self, s: Self::StaticInput) -> Self::Output { s.write(capsules_system::storage_permissions::null::NullStoragePermissions::new()) diff --git a/boards/components/src/storage_permissions/tbf_header.rs b/boards/components/src/storage_permissions/tbf_header.rs index 0252c74f17..f81aff515a 100644 --- a/boards/components/src/storage_permissions/tbf_header.rs +++ b/boards/components/src/storage_permissions/tbf_header.rs @@ -8,6 +8,7 @@ use core::mem::MaybeUninit; use kernel::component::Component; use kernel::platform::chip::Chip; +use kernel::process::ProcessStandardDebug; #[macro_export] macro_rules! storage_permissions_tbf_header_component_static { @@ -24,34 +25,41 @@ macro_rules! storage_permissions_tbf_header_component_static { pub struct AppStoreCapability; unsafe impl kernel::capabilities::ApplicationStorageCapability for AppStoreCapability {} -pub type StoragePermissionsTbfHeaderComponentType = +pub type StoragePermissionsTbfHeaderComponentType = capsules_system::storage_permissions::tbf_header::TbfHeaderStoragePermissions< C, + D, AppStoreCapability, >; -pub struct StoragePermissionsTbfHeaderComponent { +pub struct StoragePermissionsTbfHeaderComponent { _chip: core::marker::PhantomData, + _debug: core::marker::PhantomData, } -impl StoragePermissionsTbfHeaderComponent { +impl StoragePermissionsTbfHeaderComponent { pub fn new() -> Self { Self { _chip: core::marker::PhantomData, + _debug: core::marker::PhantomData, } } } -impl Component for StoragePermissionsTbfHeaderComponent { +impl Component + for StoragePermissionsTbfHeaderComponent +{ type StaticInput = &'static mut MaybeUninit< capsules_system::storage_permissions::tbf_header::TbfHeaderStoragePermissions< C, + D, AppStoreCapability, >, >; type Output = &'static capsules_system::storage_permissions::tbf_header::TbfHeaderStoragePermissions< C, + D, AppStoreCapability, >; diff --git a/boards/configurations/nrf52840dk/nrf52840dk-test-appid-sha256/Cargo.toml b/boards/configurations/nrf52840dk/nrf52840dk-test-appid-sha256/Cargo.toml index 9142694943..56c495c0cf 100644 --- a/boards/configurations/nrf52840dk/nrf52840dk-test-appid-sha256/Cargo.toml +++ b/boards/configurations/nrf52840dk/nrf52840dk-test-appid-sha256/Cargo.toml @@ -21,5 +21,8 @@ capsules-core = { path = "../../../../capsules/core" } capsules-extra = { path = "../../../../capsules/extra" } capsules-system = { path = "../../../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../../../build_scripts" } + [lints] workspace = true diff --git a/boards/configurations/nrf52840dk/nrf52840dk-test-appid-sha256/layout.ld b/boards/configurations/nrf52840dk/nrf52840dk-test-appid-sha256/layout.ld index 417a58fe7d..51025ae38d 100644 --- a/boards/configurations/nrf52840dk/nrf52840dk-test-appid-sha256/layout.ld +++ b/boards/configurations/nrf52840dk/nrf52840dk-test-appid-sha256/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ../../../nordic/nrf52840_chip_layout.ld -INCLUDE ../../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/configurations/nrf52840dk/nrf52840dk-test-appid-sha256/src/main.rs b/boards/configurations/nrf52840dk/nrf52840dk-test-appid-sha256/src/main.rs index 1720b24976..1b7f717389 100644 --- a/boards/configurations/nrf52840dk/nrf52840dk-test-appid-sha256/src/main.rs +++ b/boards/configurations/nrf52840dk/nrf52840dk-test-appid-sha256/src/main.rs @@ -278,7 +278,8 @@ pub unsafe fn main() { let storage_permissions_policy = components::storage_permissions::null::StoragePermissionsNullComponent::new().finalize( components::storage_permissions_null_component_static!( - nrf52840::chip::NRF52 + nrf52840::chip::NRF52, + kernel::process::ProcessStandardDebugFull, ), ); @@ -298,6 +299,7 @@ pub unsafe fn main() { ) .finalize(components::process_loader_sequential_component_static!( nrf52840::chip::NRF52, + kernel::process::ProcessStandardDebugFull, NUM_PROCS )); diff --git a/boards/configurations/nrf52840dk/nrf52840dk-test-appid-tbf/Cargo.toml b/boards/configurations/nrf52840dk/nrf52840dk-test-appid-tbf/Cargo.toml index 009af4d495..4b2796d31b 100644 --- a/boards/configurations/nrf52840dk/nrf52840dk-test-appid-tbf/Cargo.toml +++ b/boards/configurations/nrf52840dk/nrf52840dk-test-appid-tbf/Cargo.toml @@ -21,5 +21,8 @@ capsules-core = { path = "../../../../capsules/core" } capsules-extra = { path = "../../../../capsules/extra" } capsules-system = { path = "../../../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../../../build_scripts" } + [lints] workspace = true diff --git a/boards/configurations/nrf52840dk/nrf52840dk-test-appid-tbf/layout.ld b/boards/configurations/nrf52840dk/nrf52840dk-test-appid-tbf/layout.ld index 417a58fe7d..51025ae38d 100644 --- a/boards/configurations/nrf52840dk/nrf52840dk-test-appid-tbf/layout.ld +++ b/boards/configurations/nrf52840dk/nrf52840dk-test-appid-tbf/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ../../../nordic/nrf52840_chip_layout.ld -INCLUDE ../../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/configurations/nrf52840dk/nrf52840dk-test-appid-tbf/src/main.rs b/boards/configurations/nrf52840dk/nrf52840dk-test-appid-tbf/src/main.rs index 7410dbc783..2b7ef58c40 100644 --- a/boards/configurations/nrf52840dk/nrf52840dk-test-appid-tbf/src/main.rs +++ b/boards/configurations/nrf52840dk/nrf52840dk-test-appid-tbf/src/main.rs @@ -313,7 +313,8 @@ pub unsafe fn main() { let storage_permissions_policy = components::storage_permissions::null::StoragePermissionsNullComponent::new().finalize( components::storage_permissions_null_component_static!( - nrf52840::chip::NRF52 + nrf52840::chip::NRF52, + kernel::process::ProcessStandardDebugFull, ), ); @@ -333,6 +334,7 @@ pub unsafe fn main() { ) .finalize(components::process_loader_sequential_component_static!( nrf52840::chip::NRF52, + kernel::process::ProcessStandardDebugFull, NUM_PROCS )); diff --git a/boards/configurations/nrf52840dk/nrf52840dk-test-kernel/Cargo.toml b/boards/configurations/nrf52840dk/nrf52840dk-test-kernel/Cargo.toml index 8ca48bf492..a6e8da641b 100644 --- a/boards/configurations/nrf52840dk/nrf52840dk-test-kernel/Cargo.toml +++ b/boards/configurations/nrf52840dk/nrf52840dk-test-kernel/Cargo.toml @@ -21,5 +21,8 @@ capsules-core = { path = "../../../../capsules/core" } capsules-extra = { path = "../../../../capsules/extra" } capsules-system = { path = "../../../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../../../build_scripts" } + [lints] workspace = true diff --git a/boards/configurations/nrf52840dk/nrf52840dk-test-kernel/layout.ld b/boards/configurations/nrf52840dk/nrf52840dk-test-kernel/layout.ld index 417a58fe7d..51025ae38d 100644 --- a/boards/configurations/nrf52840dk/nrf52840dk-test-kernel/layout.ld +++ b/boards/configurations/nrf52840dk/nrf52840dk-test-kernel/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ../../../nordic/nrf52840_chip_layout.ld -INCLUDE ../../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/esp32-c3-devkitM-1/Cargo.toml b/boards/esp32-c3-devkitM-1/Cargo.toml index 7bf47d9dd9..9e012c7e8e 100644 --- a/boards/esp32-c3-devkitM-1/Cargo.toml +++ b/boards/esp32-c3-devkitM-1/Cargo.toml @@ -20,5 +20,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/esp32-c3-devkitM-1/Makefile b/boards/esp32-c3-devkitM-1/Makefile index 52c572e0dd..73c8c38d66 100644 --- a/boards/esp32-c3-devkitM-1/Makefile +++ b/boards/esp32-c3-devkitM-1/Makefile @@ -20,12 +20,10 @@ flash-app: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf ifeq ($(APP),) $(error "Please specify an APP to be flashed") endif - $(RISC_PREFIX)-objcopy --update-section .apps=$(APP) $^ $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.elf + $(RISC_PREFIX)-objcopy --set-section-flags .apps=LOAD,ALLOC $^ $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.elf + $(RISC_PREFIX)-objcopy --update-section .apps=$(APP) $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.elf esptool.py --port /dev/ttyUSB0 --chip esp32c3 elf2image --use_segments --output binary.hex $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.elf --dont-append-digest esptool.py --port /dev/ttyUSB0 --chip esp32c3 write_flash --flash_mode dio --flash_size detect --flash_freq 80m 0x0 binary.hex test: - mkdir -p $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp ../kernel_layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/ $(Q)$(CARGO) test $(NO_RUN) --bin $(PLATFORM) --release diff --git a/boards/esp32-c3-devkitM-1/layout.ld b/boards/esp32-c3-devkitM-1/layout.ld index 817a10501d..24d5747c04 100644 --- a/boards/esp32-c3-devkitM-1/layout.ld +++ b/boards/esp32-c3-devkitM-1/layout.ld @@ -9,4 +9,4 @@ MEMORY prog (rx) : ORIGIN = 0x403B0000, LENGTH = 0x30000 } -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/hail/Cargo.toml b/boards/hail/Cargo.toml index 51154beb57..7b2eabebd1 100644 --- a/boards/hail/Cargo.toml +++ b/boards/hail/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/hail/layout.ld b/boards/hail/layout.ld index 6814c00acd..c13ac43bca 100644 --- a/boards/hail/layout.ld +++ b/boards/hail/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ./chip_layout.ld -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/hifive1/Cargo.toml b/boards/hifive1/Cargo.toml index b95ba670e1..74145bbaa6 100644 --- a/boards/hifive1/Cargo.toml +++ b/boards/hifive1/Cargo.toml @@ -20,5 +20,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/hifive1/layout.ld b/boards/hifive1/layout.ld index 4be3945554..eb649cc764 100644 --- a/boards/hifive1/layout.ld +++ b/boards/hifive1/layout.ld @@ -15,4 +15,4 @@ MEMORY ram (rwx) : ORIGIN = 0x80000000, LENGTH = 0x4000 } -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/hifive_inventor/Cargo.toml b/boards/hifive_inventor/Cargo.toml index 775a0ec4da..a3aa476119 100644 --- a/boards/hifive_inventor/Cargo.toml +++ b/boards/hifive_inventor/Cargo.toml @@ -20,5 +20,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/hifive_inventor/layout.ld b/boards/hifive_inventor/layout.ld index dd16de43aa..b05f697b46 100644 --- a/boards/hifive_inventor/layout.ld +++ b/boards/hifive_inventor/layout.ld @@ -14,4 +14,4 @@ MEMORY MPU_MIN_ALIGN = 1K; -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/imix/Cargo.toml b/boards/imix/Cargo.toml index bd7558bbc1..1e4dc4d472 100644 --- a/boards/imix/Cargo.toml +++ b/boards/imix/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/imix/layout.ld b/boards/imix/layout.ld index 6814c00acd..c13ac43bca 100644 --- a/boards/imix/layout.ld +++ b/boards/imix/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ./chip_layout.ld -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/imix/src/main.rs b/boards/imix/src/main.rs index c665ef2dd1..b28cf74153 100644 --- a/boards/imix/src/main.rs +++ b/boards/imix/src/main.rs @@ -716,7 +716,8 @@ unsafe fn start() -> ( components::storage_permissions::individual::StoragePermissionsIndividualComponent::new() .finalize( components::storage_permissions_individual_component_static!( - sam4l::chip::Sam4l + sam4l::chip::Sam4l, + kernel::process::ProcessStandardDebugFull, ), ); @@ -736,6 +737,7 @@ unsafe fn start() -> ( ) .finalize(components::process_loader_sequential_component_static!( sam4l::chip::Sam4l, + kernel::process::ProcessStandardDebugFull, NUM_PROCS )); diff --git a/boards/imxrt1050-evkb/Cargo.toml b/boards/imxrt1050-evkb/Cargo.toml index 497de8a5ab..5386241ad7 100644 --- a/boards/imxrt1050-evkb/Cargo.toml +++ b/boards/imxrt1050-evkb/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/imxrt1050-evkb/Makefile b/boards/imxrt1050-evkb/Makefile index e131d36c7a..58c54b584e 100644 --- a/boards/imxrt1050-evkb/Makefile +++ b/boards/imxrt1050-evkb/Makefile @@ -20,8 +20,10 @@ flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf .PHONY: flash-app flash-app: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf @: $(if $(value APP),,$(error Please set APP to the path of a TBF file to program applications)) - arm-none-eabi-objcopy --update-section .apps=$(APP) $< \ - $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-apps.elf + arm-none-eabi-objcopy --set-section-flags .apps=LOAD,ALLOC $< \ + $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-apps.elf + arm-none-eabi-objcopy --update-section .apps=$(APP) \ + $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-apps.elf crt_emu_cm_redlink \ --flash-load-exec "$(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-apps.elf" \ -p MIMXRT1052xxxxB --ConnectScript RT1050_connect.scp \ diff --git a/boards/imxrt1050-evkb/README.md b/boards/imxrt1050-evkb/README.md index 75bda62311..72a680504b 100644 --- a/boards/imxrt1050-evkb/README.md +++ b/boards/imxrt1050-evkb/README.md @@ -49,9 +49,12 @@ with your app(s) included. ```bash $ arm-none-eabi-objcopy \ - --update-section .apps=../../../libtock-c/examples/c_hello/build/cortex-m7/cortex-m7.tbf \ + --set-section-flags .apps=LOAD,ALLOC \ target/thumbv7em-none-eabi/debug/imxrt1050-evkb.elf \ target/thumbv7em-none-eabi/debug/imxrt1050-evkb-app.axf +arm-none-eabi-objcopy \ + --update-section .apps=../../../libtock-c/examples/c_hello/build/cortex-m7/cortex-m7.tbf \ + target/thumbv7em-none-eabi/debug/imxrt1050-evkb-app.axf ``` Conveniently, the Makefile also offers a target which will integrate @@ -88,6 +91,3 @@ Next step, modify the LinkServer Debug, like this: ![image info](./pictures/config-link-server.png) Finally, press debug to run the code on the board and enjoy! - - - diff --git a/boards/imxrt1050-evkb/layout.ld b/boards/imxrt1050-evkb/layout.ld index 6814c00acd..c13ac43bca 100644 --- a/boards/imxrt1050-evkb/layout.ld +++ b/boards/imxrt1050-evkb/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ./chip_layout.ld -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/litex/arty/Cargo.toml b/boards/litex/arty/Cargo.toml index 9d7e5da9ff..9b61a2972e 100644 --- a/boards/litex/arty/Cargo.toml +++ b/boards/litex/arty/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../../capsules/core" } capsules-extra = { path = "../../../capsules/extra" } capsules-system = { path = "../../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../../build_scripts" } + [lints] workspace = true diff --git a/boards/litex/arty/layout.ld b/boards/litex/arty/layout.ld index f9a3511b7c..fbe41beee1 100644 --- a/boards/litex/arty/layout.ld +++ b/boards/litex/arty/layout.ld @@ -24,4 +24,4 @@ SECTIONS { _esram = ORIGIN(ram) + LENGTH(ram); } -INCLUDE ../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/litex/sim/Cargo.toml b/boards/litex/sim/Cargo.toml index 5b100e1e2c..8da4f200e7 100644 --- a/boards/litex/sim/Cargo.toml +++ b/boards/litex/sim/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../../capsules/core" } capsules-extra = { path = "../../../capsules/extra" } capsules-system = { path = "../../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../../build_scripts" } + [lints] workspace = true diff --git a/boards/litex/sim/layout.ld b/boards/litex/sim/layout.ld index 0fe2382257..6e59ac96a1 100644 --- a/boards/litex/sim/layout.ld +++ b/boards/litex/sim/layout.ld @@ -24,4 +24,4 @@ SECTIONS { _esram = ORIGIN(ram) + LENGTH(ram); } -INCLUDE ../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/makepython-nrf52840/Cargo.toml b/boards/makepython-nrf52840/Cargo.toml index a657aafc77..5f7e5f2270 100644 --- a/boards/makepython-nrf52840/Cargo.toml +++ b/boards/makepython-nrf52840/Cargo.toml @@ -21,5 +21,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/makepython-nrf52840/layout.ld b/boards/makepython-nrf52840/layout.ld index d5950e39c4..0bc886e980 100644 --- a/boards/makepython-nrf52840/layout.ld +++ b/boards/makepython-nrf52840/layout.ld @@ -11,4 +11,4 @@ MEMORY PAGE_SIZE = 4K; -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/makepython-nrf52840/src/main.rs b/boards/makepython-nrf52840/src/main.rs index 9e08843f83..8ebc4d90ff 100644 --- a/boards/makepython-nrf52840/src/main.rs +++ b/boards/makepython-nrf52840/src/main.rs @@ -659,7 +659,8 @@ pub unsafe fn start() -> ( components::storage_permissions::individual::StoragePermissionsIndividualComponent::new() .finalize( components::storage_permissions_individual_component_static!( - nrf52840::chip::NRF52 + nrf52840::chip::NRF52, + kernel::process::ProcessStandardDebugFull, ), ); @@ -679,6 +680,7 @@ pub unsafe fn start() -> ( ) .finalize(components::process_loader_sequential_component_static!( nrf52840::chip::NRF52, + kernel::process::ProcessStandardDebugFull, NUM_PROCS )); diff --git a/boards/microbit_v2/Cargo.toml b/boards/microbit_v2/Cargo.toml index 498aaf0c04..64f85fd865 100644 --- a/boards/microbit_v2/Cargo.toml +++ b/boards/microbit_v2/Cargo.toml @@ -21,5 +21,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/microbit_v2/layout.ld b/boards/microbit_v2/layout.ld index b9b80d64d0..e5ef626253 100644 --- a/boards/microbit_v2/layout.ld +++ b/boards/microbit_v2/layout.ld @@ -14,4 +14,4 @@ MEMORY PAGE_SIZE = 4K; -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/msp_exp432p401r/Cargo.toml b/boards/msp_exp432p401r/Cargo.toml index c9679a49dc..9cb09b2243 100644 --- a/boards/msp_exp432p401r/Cargo.toml +++ b/boards/msp_exp432p401r/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/msp_exp432p401r/layout.ld b/boards/msp_exp432p401r/layout.ld index 6814c00acd..c13ac43bca 100644 --- a/boards/msp_exp432p401r/layout.ld +++ b/boards/msp_exp432p401r/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ./chip_layout.ld -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/nano33ble/Cargo.toml b/boards/nano33ble/Cargo.toml index dcce7834e2..01084a2594 100644 --- a/boards/nano33ble/Cargo.toml +++ b/boards/nano33ble/Cargo.toml @@ -21,5 +21,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/nano33ble/layout.ld b/boards/nano33ble/layout.ld index d5950e39c4..0bc886e980 100644 --- a/boards/nano33ble/layout.ld +++ b/boards/nano33ble/layout.ld @@ -11,4 +11,4 @@ MEMORY PAGE_SIZE = 4K; -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/nano33ble_rev2/Cargo.toml b/boards/nano33ble_rev2/Cargo.toml index c766df9871..15358a46e4 100644 --- a/boards/nano33ble_rev2/Cargo.toml +++ b/boards/nano33ble_rev2/Cargo.toml @@ -21,5 +21,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/nano33ble_rev2/layout.ld b/boards/nano33ble_rev2/layout.ld index d5950e39c4..0bc886e980 100644 --- a/boards/nano33ble_rev2/layout.ld +++ b/boards/nano33ble_rev2/layout.ld @@ -11,4 +11,4 @@ MEMORY PAGE_SIZE = 4K; -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/nano_rp2040_connect/Cargo.toml b/boards/nano_rp2040_connect/Cargo.toml index 633f9954ba..88b63d8498 100644 --- a/boards/nano_rp2040_connect/Cargo.toml +++ b/boards/nano_rp2040_connect/Cargo.toml @@ -20,5 +20,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/nano_rp2040_connect/Makefile b/boards/nano_rp2040_connect/Makefile index b2add56a63..1992c9bcc8 100644 --- a/boards/nano_rp2040_connect/Makefile +++ b/boards/nano_rp2040_connect/Makefile @@ -26,10 +26,9 @@ flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf .PHONY: flash-app flash-app: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin ifeq ($(APP),) - $(error Please define the APP variable with the TBF file to flash an application) -endif - arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL) $(KERNEL_WITH_APP) + $(error Please define the APP variable with the TBF file to flash an application) +endif + arm-none-eabi-objcopy --set-section-flags .apps=LOAD,ALLOC $(KERNEL) $(KERNEL_WITH_APP) + arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL_WITH_APP) elf2uf2-rs $(KERNEL_WITH_APP) $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.uf2 @if [ -d $(BOOTSEL_FOLDER) ]; then cp $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.uf2 "$(BOOTSEL_FOLDER)"; else echo; echo Please edit the BOOTSEL_FOLDER variable to point to you Nano RP2040 Flash Drive Folder; fi - - diff --git a/boards/nano_rp2040_connect/layout.ld b/boards/nano_rp2040_connect/layout.ld index 75b23a9d07..9277a44c9f 100644 --- a/boards/nano_rp2040_connect/layout.ld +++ b/boards/nano_rp2040_connect/layout.ld @@ -26,7 +26,7 @@ SECTIONS { { KEEP (*(.flash_bootloader)); . = ALIGN (256); - } > rom + } > rom } -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/nordic/nrf52840_dongle/Cargo.toml b/boards/nordic/nrf52840_dongle/Cargo.toml index 184d0f0a12..418f8c7abb 100644 --- a/boards/nordic/nrf52840_dongle/Cargo.toml +++ b/boards/nordic/nrf52840_dongle/Cargo.toml @@ -21,5 +21,8 @@ capsules-core = { path = "../../../capsules/core" } capsules-extra = { path = "../../../capsules/extra" } capsules-system = { path = "../../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../../build_scripts" } + [lints] workspace = true diff --git a/boards/nordic/nrf52840_dongle/layout.ld b/boards/nordic/nrf52840_dongle/layout.ld index 3eccbaa12d..1bb3f0fb79 100644 --- a/boards/nordic/nrf52840_dongle/layout.ld +++ b/boards/nordic/nrf52840_dongle/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ../nrf52840_chip_layout.ld -INCLUDE ../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/nordic/nrf52840dk/Cargo.toml b/boards/nordic/nrf52840dk/Cargo.toml index 7bc8d1d05b..7f7a6c4d8c 100644 --- a/boards/nordic/nrf52840dk/Cargo.toml +++ b/boards/nordic/nrf52840dk/Cargo.toml @@ -28,5 +28,8 @@ capsules-core = { path = "../../../capsules/core" } capsules-extra = { path = "../../../capsules/extra" } capsules-system = { path = "../../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../../build_scripts" } + [lints] workspace = true diff --git a/boards/nordic/nrf52840dk/layout.ld b/boards/nordic/nrf52840dk/layout.ld index 3eccbaa12d..1bb3f0fb79 100644 --- a/boards/nordic/nrf52840dk/layout.ld +++ b/boards/nordic/nrf52840dk/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ../nrf52840_chip_layout.ld -INCLUDE ../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/nordic/nrf52dk/Cargo.toml b/boards/nordic/nrf52dk/Cargo.toml index ac5952f268..cc23243c24 100644 --- a/boards/nordic/nrf52dk/Cargo.toml +++ b/boards/nordic/nrf52dk/Cargo.toml @@ -21,5 +21,8 @@ capsules-core = { path = "../../../capsules/core" } capsules-extra = { path = "../../../capsules/extra" } capsules-system = { path = "../../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../../build_scripts" } + [lints] workspace = true diff --git a/boards/nordic/nrf52dk/layout.ld b/boards/nordic/nrf52dk/layout.ld index 0142c25b8a..a76f22a889 100644 --- a/boards/nordic/nrf52dk/layout.ld +++ b/boards/nordic/nrf52dk/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ../nrf52832_chip_layout.ld -INCLUDE ../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/nucleo_f429zi/Cargo.toml b/boards/nucleo_f429zi/Cargo.toml index b2091b9db1..54ff6040fd 100644 --- a/boards/nucleo_f429zi/Cargo.toml +++ b/boards/nucleo_f429zi/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/nucleo_f429zi/README.md b/boards/nucleo_f429zi/README.md index 1dc86ee319..f63d2f81c4 100644 --- a/boards/nucleo_f429zi/README.md +++ b/boards/nucleo_f429zi/README.md @@ -30,9 +30,12 @@ apps included. ```bash $ arm-none-eabi-objcopy \ - --update-section .apps=../../../libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf \ + --set-section-flags .apps=LOAD,ALLOC \ target/thumbv7em-none-eabi/debug/nucleo_f429zi.elf \ target/thumbv7em-none-eabi/debug/nucleo_f429zi-app.elf +$ arm-none-eabi-objcopy \ + --update-section .apps=../../../libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf \ + target/thumbv7em-none-eabi/debug/nucleo_f429zi-app.elf ``` For example, you can update `Makefile` as follows. @@ -44,7 +47,8 @@ KERNEL_WITH_APP=$(TOCK_ROOT_DIRECTORY)/target/$(TARGET)/debug/$(PLATFORM)-app.el .PHONY: program program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/debug/$(PLATFORM).elf - arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL) $(KERNEL_WITH_APP) + arm-none-eabi-objcopy --set-section-flags .apps=LOAD,ALLOC $(KERNEL) $(KERNEL_WITH_APP) + arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL_WITH_APP) $(OPENOCD) $(OPENOCD_OPTIONS) -c "init; reset halt; flash write_image erase $(KERNEL_WITH_APP); verify_image $(KERNEL_WITH_APP); reset; shutdown" ``` diff --git a/boards/nucleo_f429zi/layout.ld b/boards/nucleo_f429zi/layout.ld index 6814c00acd..c13ac43bca 100644 --- a/boards/nucleo_f429zi/layout.ld +++ b/boards/nucleo_f429zi/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ./chip_layout.ld -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/nucleo_f429zi/src/main.rs b/boards/nucleo_f429zi/src/main.rs index a97147e350..cd52f4bf58 100644 --- a/boards/nucleo_f429zi/src/main.rs +++ b/boards/nucleo_f429zi/src/main.rs @@ -24,6 +24,7 @@ use kernel::scheduler::round_robin::RoundRobinSched; use kernel::{create_capability, debug, static_init}; use stm32f429zi::chip_specs::Stm32f429Specs; +use stm32f429zi::clocks::hsi::HSI_FREQUENCY_MHZ; use stm32f429zi::gpio::{AlternateFunction, Mode, PinId, PortId}; use stm32f429zi::interrupt_service::Stm32f429ziDefaultPeripherals; @@ -663,7 +664,9 @@ unsafe fn start() -> ( rng, scheduler, - systick: cortexm4::systick::SysTick::new(), + systick: cortexm4::systick::SysTick::new_with_calibration( + (HSI_FREQUENCY_MHZ * 1_000_000) as u32, + ), can, date_time, }; diff --git a/boards/nucleo_f446re/Cargo.toml b/boards/nucleo_f446re/Cargo.toml index 104ed244c9..a037d8b888 100644 --- a/boards/nucleo_f446re/Cargo.toml +++ b/boards/nucleo_f446re/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/nucleo_f446re/README.md b/boards/nucleo_f446re/README.md index 3123473970..b7f0ba4479 100644 --- a/boards/nucleo_f446re/README.md +++ b/boards/nucleo_f446re/README.md @@ -30,9 +30,12 @@ apps included. ```bash $ arm-none-eabi-objcopy \ - --update-section .apps=../../../libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf \ + --set-section-flags .apps=LOAD,ALLOC \ target/thumbv7em-none-eabi/debug/nucleo_f446re.elf \ target/thumbv7em-none-eabi/debug/nucleo_f446re-app.elf +$ arm-none-eabi-objcopy \ + --update-section .apps=../../../libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf \ + target/thumbv7em-none-eabi/debug/nucleo_f446re-app.elf ``` For example, you can update `Makefile` as follows. @@ -44,7 +47,8 @@ KERNEL_WITH_APP=$(TOCK_ROOT_DIRECTORY)/target/$(TARGET)/debug/$(PLATFORM)-app.el .PHONY: program program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/debug/$(PLATFORM).elf - arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL) $(KERNEL_WITH_APP) + arm-none-eabi-objcopy --set-section-flags .apps=LOAD,ALLOC $(KERNEL) $(KERNEL_WITH_APP) + arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL_WITH_APP) $(OPENOCD) $(OPENOCD_OPTIONS) -c "init; reset halt; flash write_image erase $(KERNEL_WITH_APP); verify_image $(KERNEL_WITH_APP); reset; shutdown" ``` diff --git a/boards/nucleo_f446re/layout.ld b/boards/nucleo_f446re/layout.ld index 6814c00acd..c13ac43bca 100644 --- a/boards/nucleo_f446re/layout.ld +++ b/boards/nucleo_f446re/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ./chip_layout.ld -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/nucleo_f446re/src/main.rs b/boards/nucleo_f446re/src/main.rs index ee24c206b6..11276e7c76 100644 --- a/boards/nucleo_f446re/src/main.rs +++ b/boards/nucleo_f446re/src/main.rs @@ -24,6 +24,7 @@ use kernel::platform::{KernelResources, SyscallDriverLookup}; use kernel::scheduler::round_robin::RoundRobinSched; use kernel::{create_capability, debug, static_init}; use stm32f446re::chip_specs::Stm32f446Specs; +use stm32f446re::clocks::hsi::HSI_FREQUENCY_MHZ; use stm32f446re::gpio::{AlternateFunction, Mode, PinId, PortId}; use stm32f446re::interrupt_service::Stm32f446reDefaultPeripherals; @@ -507,7 +508,9 @@ unsafe fn start() -> ( gpio, scheduler, - systick: cortexm4::systick::SysTick::new(), + systick: cortexm4::systick::SysTick::new_with_calibration( + (HSI_FREQUENCY_MHZ * 1_000_000) as u32, + ), }; // // Optional kernel tests diff --git a/boards/opentitan/earlgrey-cw310/Cargo.toml b/boards/opentitan/earlgrey-cw310/Cargo.toml index 2e1d2bf6f4..b9053343d0 100644 --- a/boards/opentitan/earlgrey-cw310/Cargo.toml +++ b/boards/opentitan/earlgrey-cw310/Cargo.toml @@ -6,7 +6,7 @@ name = "earlgrey-cw310" version.workspace = true authors.workspace = true -build = "../../build.rs" +build = "build.rs" edition.workspace = true [dependencies] @@ -22,6 +22,9 @@ capsules-extra = { path = "../../../capsules/extra" } capsules-aes-gcm = { path = "../../../capsules/aes_gcm" } capsules-system = { path = "../../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../../build_scripts" } + [features] default = ["fpga_cw310"] diff --git a/boards/opentitan/earlgrey-cw310/Makefile b/boards/opentitan/earlgrey-cw310/Makefile index 59242308d2..81eebda993 100644 --- a/boards/opentitan/earlgrey-cw310/Makefile +++ b/boards/opentitan/earlgrey-cw310/Makefile @@ -81,7 +81,8 @@ flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin $(OPENTITAN_TREE)/bazel-bin/sw/host/opentitantool/opentitantool.runfiles/lowrisc_opentitan/sw/host/opentitantool/opentitantool --interface=cw310 bootstrap $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin flash-app: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf - $(RISC_PREFIX)-objcopy --update-section .apps=$(APP) $^ $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.elf + $(RISC_PREFIX)-objcopy --set-section-flags .apps=LOAD,ALLOC $^ $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.elf + $(RISC_PREFIX)-objcopy --update-section .apps=$(APP) $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.elf $(RISC_PREFIX)-objcopy --output-target=binary $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.elf $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.bin $(OPENTITAN_TREE)/bazel-bin/sw/host/opentitantool/opentitantool.runfiles/lowrisc_opentitan/sw/host/opentitantool/opentitantool --interface=cw310 bootstrap $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.bin @@ -91,7 +92,8 @@ verilator: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf $(RISC_PREFIX)-objcopy $^ $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)_verilator.elf ifneq ($(APP),) $(info [CW-130: Verilator]: Linking App) - $(RISC_PREFIX)-objcopy --update-section .apps=$(APP) $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)_verilator.elf $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)_verilator.elf + $(RISC_PREFIX)-objcopy --set-section-flags .apps=LOAD,ALLOC $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)_verilator.elf $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)_verilator.elf + $(RISC_PREFIX)-objcopy --update-section .apps=$(APP) $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)_verilator.elf endif $(info [CW-130: Verilator]: Starting) $(RISC_PREFIX)-objcopy --output-target=binary $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)_verilator.elf $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)_verilator.bin @@ -108,19 +110,10 @@ test: ifneq ($(OPENTITAN_TREE),) $(error "Running on QEMU, use test-hardware to run on hardware") endif - mkdir -p $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp test_layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/layout.ld - $(Q)cp ../../kernel_layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/ - $(Q)TOCK_ROOT_DIRECTORY=${TOCK_ROOT_DIRECTORY} QEMU_ENTRY_POINT=${QEMU_ENTRY_POINT} TARGET=${TARGET} $(CARGO) test $(CARGO_FLAGS) $(NO_RUN) --bin $(PLATFORM) --release + $(Q)TOCK_ROOT_DIRECTORY=${TOCK_ROOT_DIRECTORY} QEMU_ENTRY_POINT=${QEMU_ENTRY_POINT} TARGET=${TARGET} LINKER_SCRIPT_OVERRIDE=test_layout.ld $(CARGO) test $(CARGO_FLAGS) $(NO_RUN) --bin $(PLATFORM) --release test-hardware: ot-check - mkdir -p $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp test_layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/layout.ld - $(Q)cp ../../kernel_layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/ - $(Q)OBJCOPY=$(RISC_PREFIX)-objcopy TOCK_ROOT_DIRECTORY=${TOCK_ROOT_DIRECTORY} TARGET=${TARGET} $(CARGO) test $(CARGO_FLAGS) $(NO_RUN) --bin $(PLATFORM) --release --features=hardware_tests + $(Q)OBJCOPY=$(RISC_PREFIX)-objcopy TOCK_ROOT_DIRECTORY=${TOCK_ROOT_DIRECTORY} TARGET=${TARGET} LINKER_SCRIPT_OVERRIDE=test_layout.ld $(CARGO) test $(CARGO_FLAGS) $(NO_RUN) --bin $(PLATFORM) --release --features=hardware_tests test-verilator: ot-check - mkdir -p $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/ - $(Q)cp test_layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/deps/layout.ld - $(Q)cp ../../kernel_layout.ld $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/ - $(Q)VERILATOR="yes" OBJCOPY=$(RISC_PREFIX)-objcopy TOCK_ROOT_DIRECTORY=${TOCK_ROOT_DIRECTORY} TARGET=${TARGET} $(CARGO) test $(CARGO_FLAGS) $(NO_RUN) --bin $(PLATFORM) --release --features=hardware_tests,sim_verilator + $(Q)VERILATOR="yes" OBJCOPY=$(RISC_PREFIX)-objcopy TOCK_ROOT_DIRECTORY=${TOCK_ROOT_DIRECTORY} TARGET=${TARGET} LINKER_SCRIPT_OVERRIDE=test_layout.ld $(CARGO) test $(CARGO_FLAGS) $(NO_RUN) --bin $(PLATFORM) --release --features=hardware_tests,sim_verilator diff --git a/boards/opentitan/earlgrey-cw310/build.rs b/boards/opentitan/earlgrey-cw310/build.rs new file mode 100644 index 0000000000..e0a7bc2ffb --- /dev/null +++ b/boards/opentitan/earlgrey-cw310/build.rs @@ -0,0 +1,29 @@ +// Licensed under the Apache License, Version 2.0 or the MIT License. +// SPDX-License-Identifier: Apache-2.0 OR MIT +// Copyright Tock Contributors 2024. + +//! This board uses a custom build script to enable selecting a different layout +//! file for tests, which require a different layout than normal kernel builds. +//! The script is lightly adapted from the `default_linker_script` in +//! `tock_build_scripts`, and uses the functions provided by that crate. + +use std::path::Path; + +const LINKER_SCRIPT_OVERRIDE_ENV: &str = "LINKER_SCRIPT_OVERRIDE"; + +fn main() { + let linker_script = + std::env::var(LINKER_SCRIPT_OVERRIDE_ENV).unwrap_or("layout.ld".to_string()); + println!("cargo:rerun-if-env-changed={}", LINKER_SCRIPT_OVERRIDE_ENV); + + if !Path::new(&linker_script).exists() { + panic!( + "Boards must provide a linker script file; path does not exist: {:?}", + linker_script + ); + } + tock_build_scripts::default::rustflags_check(); + tock_build_scripts::default::include_tock_kernel_layout(); + tock_build_scripts::default::add_board_dir_to_linker_search_path(); + tock_build_scripts::default::set_and_track_linker_script(linker_script); +} diff --git a/boards/opentitan/earlgrey-cw310/layout.ld b/boards/opentitan/earlgrey-cw310/layout.ld index 868a66beae..870b8b7b4d 100644 --- a/boards/opentitan/earlgrey-cw310/layout.ld +++ b/boards/opentitan/earlgrey-cw310/layout.ld @@ -63,4 +63,4 @@ SECTIONS { } > rom } ASSERT (((_etext - _manifest) > 0), "Error: PMP and Flash protection setup assumes _etext follows _manifest"); -INCLUDE ../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/opentitan/earlgrey-cw310/run.sh b/boards/opentitan/earlgrey-cw310/run.sh index 18e62fd976..4b5d51cc06 100755 --- a/boards/opentitan/earlgrey-cw310/run.sh +++ b/boards/opentitan/earlgrey-cw310/run.sh @@ -6,9 +6,6 @@ BUILD_DIR="verilator_build/" -# Preemptively cleanup layout (incase this was a test) so that following apps (non-tests) load the correct layout. -rm $TOCK_ROOT_DIRECTORY/target/$TARGET/release/deps/layout.ld - if [[ "${VERILATOR}" == "yes" ]]; then if [ -d "$BUILD_DIR" ]; then # Cleanup before we build again diff --git a/boards/opentitan/earlgrey-cw310/test_layout.ld b/boards/opentitan/earlgrey-cw310/test_layout.ld index fbeb1739f6..9569ae07a3 100644 --- a/boards/opentitan/earlgrey-cw310/test_layout.ld +++ b/boards/opentitan/earlgrey-cw310/test_layout.ld @@ -69,4 +69,4 @@ SECTIONS { } > rom } ASSERT (((_etext - _manifest) > 0), "Error: PMP and Flash protection setup assumes _etext follows _manifest"); -INCLUDE ../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/particle_boron/Cargo.toml b/boards/particle_boron/Cargo.toml index 1232fc610e..d9ae0f9e4d 100644 --- a/boards/particle_boron/Cargo.toml +++ b/boards/particle_boron/Cargo.toml @@ -25,5 +25,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/particle_boron/layout.ld b/boards/particle_boron/layout.ld index 89d8ab00fc..f34d7b03de 100644 --- a/boards/particle_boron/layout.ld +++ b/boards/particle_boron/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ../nordic/nrf52840_chip_layout.ld -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/pico_explorer_base/Cargo.toml b/boards/pico_explorer_base/Cargo.toml index bcaaeb1d7a..eb2c7db0b3 100644 --- a/boards/pico_explorer_base/Cargo.toml +++ b/boards/pico_explorer_base/Cargo.toml @@ -20,5 +20,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/pico_explorer_base/Makefile b/boards/pico_explorer_base/Makefile index aa149036b6..cd253ff3af 100644 --- a/boards/pico_explorer_base/Makefile +++ b/boards/pico_explorer_base/Makefile @@ -32,9 +32,9 @@ flash: $(KERNEL) .PHONY: program program: $(KERNEL) ifeq ($(APP),) - $(error Please define the APP variable with the TBF file to flash an application) -endif - arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL) $(KERNEL_WITH_APP) + $(error Please define the APP variable with the TBF file to flash an application) +endif + arm-none-eabi-objcopy --set-section-flags .apps=LOAD,ALLOC $(KERNEL) $(KERNEL_WITH_APP) + arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL_WITH_APP) elf2uf2-rs $(KERNEL_WITH_APP) $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.uf2 @if [ -d $(BOOTSEL_FOLDER) ]; then cp $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.uf2 "$(BOOTSEL_FOLDER)"; else echo; echo Please edit the BOOTSEL_FOLDER variable to point to you Raspberry Pi Pico Flash Drive Folder; echo You can download and flash $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.uf2; fi - diff --git a/boards/pico_explorer_base/layout.ld b/boards/pico_explorer_base/layout.ld index ce99f68d6d..0851ffc33f 100644 --- a/boards/pico_explorer_base/layout.ld +++ b/boards/pico_explorer_base/layout.ld @@ -26,7 +26,7 @@ SECTIONS { { KEEP (*(.flash_bootloader)); . = ALIGN (256); - } > rom + } > rom } -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/qemu_rv32_virt/Cargo.toml b/boards/qemu_rv32_virt/Cargo.toml index d8460e259a..059aae3f58 100644 --- a/boards/qemu_rv32_virt/Cargo.toml +++ b/boards/qemu_rv32_virt/Cargo.toml @@ -20,5 +20,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/qemu_rv32_virt/layout.ld b/boards/qemu_rv32_virt/layout.ld index 6c477a76ae..bc6115f16a 100644 --- a/boards/qemu_rv32_virt/layout.ld +++ b/boards/qemu_rv32_virt/layout.ld @@ -30,4 +30,4 @@ SECTIONS { _esram = ORIGIN(ram) + LENGTH(ram); } -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/raspberry_pi_pico/Cargo.toml b/boards/raspberry_pi_pico/Cargo.toml index d38acf1ce0..9f4abea94c 100644 --- a/boards/raspberry_pi_pico/Cargo.toml +++ b/boards/raspberry_pi_pico/Cargo.toml @@ -20,5 +20,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/raspberry_pi_pico/Makefile b/boards/raspberry_pi_pico/Makefile index aa149036b6..cd253ff3af 100644 --- a/boards/raspberry_pi_pico/Makefile +++ b/boards/raspberry_pi_pico/Makefile @@ -32,9 +32,9 @@ flash: $(KERNEL) .PHONY: program program: $(KERNEL) ifeq ($(APP),) - $(error Please define the APP variable with the TBF file to flash an application) -endif - arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL) $(KERNEL_WITH_APP) + $(error Please define the APP variable with the TBF file to flash an application) +endif + arm-none-eabi-objcopy --set-section-flags .apps=LOAD,ALLOC $(KERNEL) $(KERNEL_WITH_APP) + arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL_WITH_APP) elf2uf2-rs $(KERNEL_WITH_APP) $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.uf2 @if [ -d $(BOOTSEL_FOLDER) ]; then cp $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.uf2 "$(BOOTSEL_FOLDER)"; else echo; echo Please edit the BOOTSEL_FOLDER variable to point to you Raspberry Pi Pico Flash Drive Folder; echo You can download and flash $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.uf2; fi - diff --git a/boards/raspberry_pi_pico/layout.ld b/boards/raspberry_pi_pico/layout.ld index ce99f68d6d..0851ffc33f 100644 --- a/boards/raspberry_pi_pico/layout.ld +++ b/boards/raspberry_pi_pico/layout.ld @@ -26,7 +26,7 @@ SECTIONS { { KEEP (*(.flash_bootloader)); . = ALIGN (256); - } > rom + } > rom } -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/redboard_redv/Cargo.toml b/boards/redboard_redv/Cargo.toml index 62215335f0..33f8a82073 100644 --- a/boards/redboard_redv/Cargo.toml +++ b/boards/redboard_redv/Cargo.toml @@ -20,5 +20,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/redboard_redv/layout.ld b/boards/redboard_redv/layout.ld index ec098cebcb..fdaf6122fa 100644 --- a/boards/redboard_redv/layout.ld +++ b/boards/redboard_redv/layout.ld @@ -14,4 +14,4 @@ MEMORY ram (rwx) : ORIGIN = 0x80000000, LENGTH = 16K } -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/sma_q3/Cargo.toml b/boards/sma_q3/Cargo.toml index 4813d48c74..55968e92e1 100644 --- a/boards/sma_q3/Cargo.toml +++ b/boards/sma_q3/Cargo.toml @@ -25,5 +25,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/sma_q3/layout.ld b/boards/sma_q3/layout.ld index 89d8ab00fc..f34d7b03de 100644 --- a/boards/sma_q3/layout.ld +++ b/boards/sma_q3/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ../nordic/nrf52840_chip_layout.ld -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/stm32f3discovery/Cargo.toml b/boards/stm32f3discovery/Cargo.toml index 85bdda5dce..0e4ddc4296 100644 --- a/boards/stm32f3discovery/Cargo.toml +++ b/boards/stm32f3discovery/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/stm32f3discovery/README.md b/boards/stm32f3discovery/README.md index 51e6e8e05f..494bc62774 100644 --- a/boards/stm32f3discovery/README.md +++ b/boards/stm32f3discovery/README.md @@ -16,43 +16,3 @@ $ make flash $ make flash-debug ``` - -> **Note:** Unlike other Tock platforms, the default kernel image for this -> board will clear flashed apps when the kernel is loaded. This is to support -> the non-tockloader based app flash procedure below. To preserve loaded apps, -> comment out the `APP_HACK` variable in `src/main.rs`. - -## Flashing app - -Apps are built out-of-tree. Once an app is built, you can use -`arm-none-eabi-objcopy` with `--update-section` to create an ELF image with the -apps included. - -```bash -$ arm-none-eabi-objcopy \ - --update-section .apps=../../../libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf \ - target/thumbv7em-none-eabi/release/stm32f3discovery.elf \ - target/thumbv7em-none-eabi/release/stm32f3discovery-app.elf -``` - -For example, you can update `Makefile` as follows. - -``` -APP=../../../libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf -KERNEL=$(TOCK_ROOT_DIRECTORY)/target/$(TARGET)/release/$(PLATFORM).elf -KERNEL_WITH_APP=$(TOCK_ROOT_DIRECTORY)/target/$(TARGET)/release/$(PLATFORM)-app.elf - -.PHONY: program -program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf - arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL) $(KERNEL_WITH_APP) - $(OPENOCD) $(OPENOCD_OPTIONS) -c "init; reset halt; flash write_image erase $(KERNEL_WITH_APP); verify_image $(KERNEL_WITH_APP); reset; shutdown" -``` - -After setting `APP`, `KERNEL`, `KERNEL_WITH_APP`, and `program` target -dependency, you can do - -```bash -$ make program -``` - -to flash the image. diff --git a/boards/stm32f3discovery/layout.ld b/boards/stm32f3discovery/layout.ld index 6814c00acd..c13ac43bca 100644 --- a/boards/stm32f3discovery/layout.ld +++ b/boards/stm32f3discovery/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ./chip_layout.ld -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/stm32f3discovery/src/main.rs b/boards/stm32f3discovery/src/main.rs index b8eab2950b..872e715f30 100644 --- a/boards/stm32f3discovery/src/main.rs +++ b/boards/stm32f3discovery/src/main.rs @@ -806,7 +806,8 @@ unsafe fn start() -> ( nonvolatile_storage, scheduler, - systick: cortexm4::systick::SysTick::new(), + // Systick uses the HSI, which runs at 8MHz + systick: cortexm4::systick::SysTick::new_with_calibration(8_000_000), watchdog: &peripherals.watchdog, }; diff --git a/boards/stm32f412gdiscovery/Cargo.toml b/boards/stm32f412gdiscovery/Cargo.toml index ad53dd58d2..affdf29117 100644 --- a/boards/stm32f412gdiscovery/Cargo.toml +++ b/boards/stm32f412gdiscovery/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/stm32f412gdiscovery/README.md b/boards/stm32f412gdiscovery/README.md index 39e4674c8a..3c083902a6 100644 --- a/boards/stm32f412gdiscovery/README.md +++ b/boards/stm32f412gdiscovery/README.md @@ -17,46 +17,6 @@ $ make flash $ make flash-debug ``` -> **Note:** Unlike other Tock platforms, the default kernel image for this -> board will clear flashed apps when the kernel is loaded. This is to support -> the non-tockloader based app flash procedure below. To preserve loaded apps, -> comment out the `APP_HACK` variable in `src/main.rs`. - -## Flashing app - -Apps are built out-of-tree. Once an app is built, you can use -`arm-none-eabi-objcopy` with `--update-section` to create an ELF image with the -apps included. - -```bash -$ arm-none-eabi-objcopy \ - --update-section .apps=../../../libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf \ - target/thumbv7em-none-eabi/release/discovery_f412g.elf \ - target/thumbv7em-none-eabi/release/discovery_f412g-app.elf -``` - -For example, you can update `Makefile` as follows. - -``` -APP=../../../libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf -KERNEL=$(TOCK_ROOT_DIRECTORY)/target/$(TARGET)/release/$(PLATFORM).elf -KERNEL_WITH_APP=$(TOCK_ROOT_DIRECTORY)/target/$(TARGET)/release/$(PLATFORM)-app.elf - -.PHONY: program -program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf - arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL) $(KERNEL_WITH_APP) - $(OPENOCD) $(OPENOCD_OPTIONS) -c "init; reset halt; flash write_image erase $(KERNEL_WITH_APP); verify_image $(KERNEL_WITH_APP); reset; shutdown" -``` - -After setting `APP`, `KERNEL`, `KERNEL_WITH_APP`, and `program` target -dependency, you can do - -```bash -$ make program -``` - -to flash the image. - ## OpenOCD Note The release version of openocd does not fully support stm32412g discovery kit. Uploading seems to work with the setup for nucelo429zi. The openocd.cfg file contains both setups, one being commented. diff --git a/boards/stm32f412gdiscovery/layout.ld b/boards/stm32f412gdiscovery/layout.ld index 6814c00acd..c13ac43bca 100644 --- a/boards/stm32f412gdiscovery/layout.ld +++ b/boards/stm32f412gdiscovery/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ./chip_layout.ld -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/stm32f412gdiscovery/src/main.rs b/boards/stm32f412gdiscovery/src/main.rs index acecf89583..2d8c75520f 100644 --- a/boards/stm32f412gdiscovery/src/main.rs +++ b/boards/stm32f412gdiscovery/src/main.rs @@ -25,6 +25,7 @@ use kernel::platform::{KernelResources, SyscallDriverLookup}; use kernel::scheduler::round_robin::RoundRobinSched; use kernel::{create_capability, debug, static_init}; use stm32f412g::chip_specs::Stm32f412Specs; +use stm32f412g::clocks::hsi::HSI_FREQUENCY_MHZ; use stm32f412g::interrupt_service::Stm32f412gDefaultPeripherals; /// Support routines for debugging I/O. @@ -766,7 +767,9 @@ unsafe fn start() -> ( rng, scheduler, - systick: cortexm4::systick::SysTick::new(), + systick: cortexm4::systick::SysTick::new_with_calibration( + (HSI_FREQUENCY_MHZ * 1_000_000) as u32, + ), }; // // Optional kernel tests diff --git a/boards/stm32f429idiscovery/Cargo.toml b/boards/stm32f429idiscovery/Cargo.toml index a0b25541f5..205c720b64 100644 --- a/boards/stm32f429idiscovery/Cargo.toml +++ b/boards/stm32f429idiscovery/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/stm32f429idiscovery/README.md b/boards/stm32f429idiscovery/README.md index ba26df9e8d..b2fbc23848 100644 --- a/boards/stm32f429idiscovery/README.md +++ b/boards/stm32f429idiscovery/README.md @@ -17,43 +17,3 @@ $ make flash $ make flash-debug ``` - -> **Note:** Unlike other Tock platforms, the default kernel image for this -> board will clear flashed apps when the kernel is loaded. This is to support -> the non-tockloader based app flash procedure below. To preserve loaded apps, -> comment out the `APP_HACK` variable in `src/main.rs`. - -## Flashing app - -Apps are built out-of-tree. Once an app is built, you can use -`arm-none-eabi-objcopy` with `--update-section` to create an ELF image with the -apps included. - -```bash -$ arm-none-eabi-objcopy \ - --update-section .apps=../../../libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf \ - target/thumbv7em-none-eabi/debug/stm32f429idiscovery.elf \ - target/thumbv7em-none-eabi/debug/stm32f429idiscovery-app.elf -``` - -For example, you can update `Makefile` as follows. - -``` -APP=../../../libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf -KERNEL=$(TOCK_ROOT_DIRECTORY)/target/$(TARGET)/debug/$(PLATFORM).elf -KERNEL_WITH_APP=$(TOCK_ROOT_DIRECTORY)/target/$(TARGET)/debug/$(PLATFORM)-app.elf - -.PHONY: program -program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/debug/$(PLATFORM).elf - arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL) $(KERNEL_WITH_APP) - $(OPENOCD) $(OPENOCD_OPTIONS) -c "init; reset halt; flash write_image erase $(KERNEL_WITH_APP); verify_image $(KERNEL_WITH_APP); reset; shutdown" -``` - -After setting `APP`, `KERNEL`, `KERNEL_WITH_APP`, and `program` target -dependency, you can do - -```bash -$ make program -``` - -to flash the image. diff --git a/boards/stm32f429idiscovery/layout.ld b/boards/stm32f429idiscovery/layout.ld index 6814c00acd..c13ac43bca 100644 --- a/boards/stm32f429idiscovery/layout.ld +++ b/boards/stm32f429idiscovery/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ./chip_layout.ld -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/stm32f429idiscovery/src/main.rs b/boards/stm32f429idiscovery/src/main.rs index 44e1b90dd5..1bdd856f0f 100644 --- a/boards/stm32f429idiscovery/src/main.rs +++ b/boards/stm32f429idiscovery/src/main.rs @@ -24,6 +24,7 @@ use kernel::scheduler::round_robin::RoundRobinSched; use kernel::{create_capability, debug, static_init}; use stm32f429zi::chip_specs::Stm32f429Specs; +use stm32f429zi::clocks::hsi::HSI_FREQUENCY_MHZ; use stm32f429zi::gpio::{AlternateFunction, Mode, PinId, PortId}; use stm32f429zi::interrupt_service::Stm32f429ziDefaultPeripherals; @@ -591,7 +592,9 @@ unsafe fn start() -> ( gpio, scheduler, - systick: cortexm4::systick::SysTick::new(), + systick: cortexm4::systick::SysTick::new_with_calibration( + (HSI_FREQUENCY_MHZ * 1_000_000) as u32, + ), }; // // Optional kernel tests diff --git a/boards/teensy40/Cargo.toml b/boards/teensy40/Cargo.toml index b58b74e07e..1f2fc540c4 100644 --- a/boards/teensy40/Cargo.toml +++ b/boards/teensy40/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/teensy40/Makefile b/boards/teensy40/Makefile index 5c7f38ac6e..1ff962e9d6 100644 --- a/boards/teensy40/Makefile +++ b/boards/teensy40/Makefile @@ -22,7 +22,8 @@ program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex .PHONY: program-app program-app: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf @: $(if $(value APP),,$(error Please set APP to the path of a TBF file to program applications)) - $(Q)arm-none-eabi-objcopy --update-section .apps=$(APP) $< $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-apps.elf + $(Q)arm-none-eabi-objcopy --set-section-flags .apps=LOAD,ALLOC $< $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-apps.elf + $(Q)arm-none-eabi-objcopy --update-section .apps=$(APP) $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-apps.elf $(Q)$(OBJCOPY) -O ihex $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-apps.elf $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-apps.hex teensy_loader_cli --mcu=TEENSY40 -w -v $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-apps.hex diff --git a/boards/teensy40/README.md b/boards/teensy40/README.md index 98564e7f0b..b3869df24d 100644 --- a/boards/teensy40/README.md +++ b/boards/teensy40/README.md @@ -42,9 +42,12 @@ with the Teensy 4 Tock kernel. ```bash $ arm-none-eabi-objcopy \ - --update-section .apps=../../../libtock-c/examples/blink/build/cortex-m7/cortex-m7.tbf \ + --set-section-flags .apps=LOAD,ALLOC \ ../../target/thumbv7em-none-eabi/release/teensy40.elf \ ../../target/thumbv7em-none-eabi/release/teensy40-app.elf +$ arm-none-eabi-objcopy \ + --update-section .apps=../../../libtock-c/examples/blink/build/cortex-m7/cortex-m7.tbf \ + ../../target/thumbv7em-none-eabi/release/teensy40-app.elf ``` Once you've created a single ELF image, use `arm-none-eabi-objcopy` to turn @@ -72,7 +75,8 @@ KERNEL_WITH_APP_HEX=$(TOCK_ROOT_DIRECTORY)/target/teensy40/release/teensy40-app. .PHONY: program program: target/thumbv7em-none-eabi/release/teensy40.elf - arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL) $(KERNEL_WITH_APP) + arm-none-eabi-objcopy --set-section-flags .apps=LOAD,ALLOC $(KERNEL) $(KERNEL_WITH_APP) + arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL_WITH_APP) arm-none-eabi-objcopy -O ihex $(KERNEL_WITH_APP) $(KERNEL_WITH_APP_HEX) teensy_loader_cli -w -v --mcu=TEENSY40 $(KERNEL_WITH_APP_HEX) ``` diff --git a/boards/teensy40/layout.ld b/boards/teensy40/layout.ld index 954fb11fd3..fa1f38ade5 100644 --- a/boards/teensy40/layout.ld +++ b/boards/teensy40/layout.ld @@ -25,7 +25,7 @@ MEMORY SECTIONS { - .boot : + .boot : { /* FlexSPI Configuration Block (defined in source) */ KEEP(*(.fcb)); @@ -71,4 +71,4 @@ SECTIONS } > rom } -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/tutorials/nrf52840dk-hotp-tutorial/Cargo.toml b/boards/tutorials/nrf52840dk-hotp-tutorial/Cargo.toml index 57b4e2e6c2..1623376aee 100644 --- a/boards/tutorials/nrf52840dk-hotp-tutorial/Cargo.toml +++ b/boards/tutorials/nrf52840dk-hotp-tutorial/Cargo.toml @@ -23,5 +23,8 @@ capsules-extra = { path = "../../../capsules/extra" } capsules-system = { path = "../../../capsules/system" } components = { path = "../../components" } +[build-dependencies] +tock_build_scripts = { path = "../../build_scripts" } + [lints] workspace = true diff --git a/boards/tutorials/nrf52840dk-hotp-tutorial/layout.ld b/boards/tutorials/nrf52840dk-hotp-tutorial/layout.ld index 52d52ab3ac..e87005fd9e 100644 --- a/boards/tutorials/nrf52840dk-hotp-tutorial/layout.ld +++ b/boards/tutorials/nrf52840dk-hotp-tutorial/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2024. */ INCLUDE ../../nordic/nrf52840_chip_layout.ld -INCLUDE ../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/tutorials/nrf52840dk-thread-tutorial/Cargo.toml b/boards/tutorials/nrf52840dk-thread-tutorial/Cargo.toml index cdadcc88dd..a51b88671d 100644 --- a/boards/tutorials/nrf52840dk-thread-tutorial/Cargo.toml +++ b/boards/tutorials/nrf52840dk-thread-tutorial/Cargo.toml @@ -23,5 +23,8 @@ capsules-extra = { path = "../../../capsules/extra" } capsules-system = { path = "../../../capsules/system" } components = { path = "../../components" } +[build-dependencies] +tock_build_scripts = { path = "../../build_scripts" } + [lints] workspace = true diff --git a/boards/tutorials/nrf52840dk-thread-tutorial/layout.ld b/boards/tutorials/nrf52840dk-thread-tutorial/layout.ld index ece13c78e4..8924ae11fe 100644 --- a/boards/tutorials/nrf52840dk-thread-tutorial/layout.ld +++ b/boards/tutorials/nrf52840dk-thread-tutorial/layout.ld @@ -3,4 +3,4 @@ /* Copyright Tock Contributors 2023. */ INCLUDE ../../nordic/nrf52840_chip_layout.ld -INCLUDE ../../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/veer_el2_sim/Cargo.toml b/boards/veer_el2_sim/Cargo.toml index 014781c163..ad163b8b37 100644 --- a/boards/veer_el2_sim/Cargo.toml +++ b/boards/veer_el2_sim/Cargo.toml @@ -20,5 +20,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/veer_el2_sim/layout.ld b/boards/veer_el2_sim/layout.ld index 9bed370cc7..ac16f27045 100644 --- a/boards/veer_el2_sim/layout.ld +++ b/boards/veer_el2_sim/layout.ld @@ -9,4 +9,4 @@ MEMORY ram (rwx) : ORIGIN = 0x20600000, LENGTH = 0x300000 } -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/weact_f401ccu6/Cargo.toml b/boards/weact_f401ccu6/Cargo.toml index 8a725d66fa..4c2c66c4fc 100644 --- a/boards/weact_f401ccu6/Cargo.toml +++ b/boards/weact_f401ccu6/Cargo.toml @@ -19,5 +19,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/weact_f401ccu6/Makefile b/boards/weact_f401ccu6/Makefile index f14b977d5f..210a1218b8 100644 --- a/boards/weact_f401ccu6/Makefile +++ b/boards/weact_f401ccu6/Makefile @@ -19,5 +19,6 @@ flash: $(KERNEL) $(OPENOCD) $(OPENOCD_OPTIONS) -c "init; reset halt; flash write_image erase $<; verify_image $<; reset; shutdown" flash-app: $(KERNEL) - arm-none-eabi-objcopy --update-section .apps=$(APP) $< $(KERNEL_WITH_APP) + arm-none-eabi-objcopy --set-section-flags .apps=LOAD,ALLOC $< $(KERNEL_WITH_APP) + arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL_WITH_APP) $(OPENOCD) $(OPENOCD_OPTIONS) -c "init; reset halt; flash write_image erase $(KERNEL_WITH_APP); verify_image $(KERNEL_WITH_APP); reset; shutdown" diff --git a/boards/weact_f401ccu6/README.md b/boards/weact_f401ccu6/README.md index 1834884f98..1e074fca12 100644 --- a/boards/weact_f401ccu6/README.md +++ b/boards/weact_f401ccu6/README.md @@ -34,9 +34,12 @@ ELF image with the apps included. ```bash $ arm-none-eabi-objcopy \ - --update-section .apps=../../../libtock-c/examples/blink/build/cortex-m4/cortex-m4.tbf \ + --set-section-flags .apps=LOAD,ALLOC \ ../../target/thumbv7em-none-eabihf/release/weact-f401ccu6.elf \ ../../target/thumbv7em-none-eabihf/release/weact-f401ccu6-app.elf +$ arm-none-eabi-objcopy \ + --update-section .apps=../../../libtock-c/examples/blink/build/cortex-m4/cortex-m4.tbf \ + ../../target/thumbv7em-none-eabihf/release/weact-f401ccu6-app.elf ``` The board `Makefile` can also handle this process and upload a given app automatically. diff --git a/boards/weact_f401ccu6/layout.ld b/boards/weact_f401ccu6/layout.ld index e7affc0fff..d9cf1b4f12 100644 --- a/boards/weact_f401ccu6/layout.ld +++ b/boards/weact_f401ccu6/layout.ld @@ -17,4 +17,4 @@ MEMORY PAGE_SIZE = 2K; -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/boards/weact_f401ccu6/src/main.rs b/boards/weact_f401ccu6/src/main.rs index a0ce19683c..048a4645d3 100644 --- a/boards/weact_f401ccu6/src/main.rs +++ b/boards/weact_f401ccu6/src/main.rs @@ -24,6 +24,7 @@ use kernel::scheduler::round_robin::RoundRobinSched; use kernel::{create_capability, debug, static_init}; use stm32f401cc::chip_specs::Stm32f401Specs; +use stm32f401cc::clocks::hsi::HSI_FREQUENCY_MHZ; use stm32f401cc::interrupt_service::Stm32f401ccDefaultPeripherals; /// Support routines for debugging I/O. @@ -448,7 +449,9 @@ unsafe fn start() -> ( alarm, gpio, scheduler, - systick: cortexm4::systick::SysTick::new(), + systick: cortexm4::systick::SysTick::new_with_calibration( + (HSI_FREQUENCY_MHZ * 1_000_000) as u32, + ), }; debug!("Initialization complete. Entering main loop"); diff --git a/boards/wm1110dev/Cargo.toml b/boards/wm1110dev/Cargo.toml index af5bb32503..db764aa3c5 100644 --- a/boards/wm1110dev/Cargo.toml +++ b/boards/wm1110dev/Cargo.toml @@ -21,5 +21,8 @@ capsules-core = { path = "../../capsules/core" } capsules-extra = { path = "../../capsules/extra" } capsules-system = { path = "../../capsules/system" } +[build-dependencies] +tock_build_scripts = { path = "../build_scripts" } + [lints] workspace = true diff --git a/boards/wm1110dev/layout.ld b/boards/wm1110dev/layout.ld index a6f358c41c..2a2667603b 100644 --- a/boards/wm1110dev/layout.ld +++ b/boards/wm1110dev/layout.ld @@ -13,4 +13,4 @@ MEMORY PAGE_SIZE = 4K; -INCLUDE ../kernel_layout.ld +INCLUDE tock_kernel_layout.ld diff --git a/capsules/core/src/alarm.rs b/capsules/core/src/alarm.rs index cbab6e5234..d7806edc9b 100644 --- a/capsules/core/src/alarm.rs +++ b/capsules/core/src/alarm.rs @@ -171,7 +171,10 @@ impl<'a, A: Alarm<'a>> AlarmDriver<'a, A> { ALARM_CALLBACK_NUM, ( now.into_u32_left_justified() as usize, - expired.reference.wrapping_add(expired.dt).into_usize(), + expired + .reference + .wrapping_add(expired.dt) + .into_u32_left_justified() as usize, 0, ), ) @@ -226,6 +229,8 @@ impl<'a, A: Alarm<'a>> AlarmDriver<'a, A> { dt_u32: u32, expiration: &mut Option>, ) -> u32 { + let reference_unshifted = reference_u32.map(|ref_u32| ref_u32 >> A::Ticks::u32_padding()); + // If the underlying timer is less than 32-bit wide, userspace is able // to provide a finer `reference` and `dt` resolution than we can // possibly represent in the kernel. @@ -235,10 +240,27 @@ impl<'a, A: Alarm<'a>> AlarmDriver<'a, A> { // with a precision unrepresentible using our Ticks object, we round // `reference` down, and `dt` up (ensuring that the timer cannot fire // earlier than requested). - let reference_unshifted = reference_u32.map(|ref_u32| ref_u32 >> A::Ticks::u32_padding()); + let dt_unshifted = if let Some(reference_u32) = reference_u32 { + // Computing unshifted dt for a userspace alarm can + // underestimate dt in some cases where both reference and + // dt had low-order bits that are rounded off by + // unshifting. To ensure `dt` results in an actual + // expiration that is at least as long as the expected + // expiration in user space, compute unshifted dt from an + // unshifted expiration. + let expiration_shifted = reference_u32.wrapping_add(dt_u32); + let expiration_unshifted = + if expiration_shifted & ((1 << A::Ticks::u32_padding()) - 1) != 0 { + // By right-shifting, we would decrease the requested dt value, + // firing _before_ the time requested by userspace. Add one to + // compensate this: + (expiration_shifted >> A::Ticks::u32_padding()) + 1 + } else { + expiration_shifted >> A::Ticks::u32_padding() + }; - // Round dt up: - let dt_unshifted = if dt_u32 & ((1 << A::Ticks::u32_padding()) - 1) != 0 { + expiration_unshifted.wrapping_sub(reference_u32 >> A::Ticks::u32_padding()) + } else if dt_u32 & ((1 << A::Ticks::u32_padding()) - 1) != 0 { // By right-shifting, we would decrease the requested dt value, // firing _before_ the time requested by userspace. Add one to // compensate this: @@ -331,6 +353,7 @@ impl<'a, A: Alarm<'a>> AlarmDriver<'a, A> { (Some(userspace_reference_unshifted), false) => { // We have a userspace reference and timer is (less than) 32 // bit. Simply set to unshifted values: + Expiration { reference: A::Ticks::from(userspace_reference_unshifted), dt: A::Ticks::from(dt_unshifted), @@ -877,6 +900,68 @@ mod test { ); } + #[test] + fn test_rearm_24bit_left_justified_ref_low_bits_basic() { + let mut expiration = None; + + assert!(Ticks24::u32_padding() == 8); + + let armed_time = + AlarmDriver::>::rearm_u32_left_justified_expiration( + // Current time: + Ticks24::from(0_u32), + // Userspace-provided reference, below minimum precision of Ticks24, will be rounded down: + Some(1_u32), + // Left-justified `dt` value, below minimum precision of Ticks24, will be rounded up: + 3_u32, + // Reference to the `Option`, also used + // to update the counter of armed alarms: + &mut expiration, + ); + + let expiration = expiration.unwrap(); + + // ((1 >> 8) + ((3 >> 8) + 1) << 8) = 1 + assert_eq!(armed_time, 1 << 8); + assert_eq!(expiration.reference.into_u32(), 0); + assert_eq!(expiration.dt.into_u32(), 1); + } + + #[test] + fn test_rearm_24bit_left_justified_ref_low_bits_max_int() { + let mut expiration = None; + + assert!(Ticks24::u32_padding() == 8); + + let armed_time = + AlarmDriver::>::rearm_u32_left_justified_expiration( + // Current time: + Ticks24::from(6_u32), + // Userspace-provided reference, including bits not representable in Ticks24: + // (5 << 8) - 43 = 1237 + Some(Ticks24::from(5_u32).into_u32_left_justified() - 43), + // Left-justified `dt` value, including bits not representable in Ticks24: + // (2 << 8) - 43 = 469 + Ticks24::from(2_u32).into_u32_left_justified() + 43, + // Reference to the `Option`, also used + // to update the counter of armed alarms: + &mut expiration, + ); + + let expiration = expiration.unwrap(); + + // When we naively round down reference `(1237 / 256 ~= 4.83 -> 4)` and + // round up dt `(469 / 256 ~= 1.83 -> 2)` we'd arm the alarm to + // `2 + 4 = 6`. However, when considering the full resolution + // `reference + dt` `(1237 + 256) / 256 ~= 6.67` we can see that arming + // to `6` will have the alarm fire too early. The alarm rearm code needs + // to compensate for the case that (reference + dt) overflow and generate + // a dt from that rounded value, in this case `7`. + assert_eq!(armed_time, 7 << 8); + assert_eq!(expiration.reference.into_u32(), 4); + assert_eq!(expiration.dt, Ticks24::from(3)); + } + #[test] fn test_rearm_32bit_left_justified_noref_basic() { let mut expiration = Some(Expiration { diff --git a/capsules/core/src/driver.rs b/capsules/core/src/driver.rs index b41fce006f..43804d54f2 100644 --- a/capsules/core/src/driver.rs +++ b/capsules/core/src/driver.rs @@ -93,5 +93,6 @@ pub enum NUM { KeyboardHid = 0x90005, DateTime = 0x90007, CycleCount = 0x90008, + Servo = 0x90009, } } diff --git a/capsules/extra/README.md b/capsules/extra/README.md index 9f268263aa..4d9a822ebd 100644 --- a/capsules/extra/README.md +++ b/capsules/extra/README.md @@ -116,6 +116,7 @@ These provide common and better abstractions for userspace. - **[App Flash](src/app_flash_driver.rs)**: Allow applications to write their own flash. - **[Buzzer](src/buzzer_driver.rs)**: Simple buzzer. +- **[Servo](src/servo.rs)**: Servo motor. - **[Date-Time](src/date_time.rs)**: Real time clock date/time support. - **[EUI64](src/eui64.rs)**: Query device's extended unique ID. - **[HMAC](src/hmac.rs)**: Hash-based Message Authentication Code support. @@ -156,6 +157,7 @@ Other capsules that implement reusable logic. - **[Bus Adapters](src/bus.rs)**: Generic abstraction for SPI/I2C/8080. - **[Buzzer PWM](src/buzzer_pwm.rs)**: Buzzer with a PWM pin. +- **[SG90 PWM](src/sg90.rs)**: SG90 servomotor. - **[HMAC-SHA256](src/hmac_sha256.rs)**: HMAC using SHA-256. - **[Key-Value Store with Permissions](src/kv_store_permissions.rs)**: Key-value interface that requires read/write permissions. diff --git a/capsules/extra/src/ble_advertising_driver.rs b/capsules/extra/src/ble_advertising_driver.rs index 106a7811bd..63c6f053e8 100644 --- a/capsules/extra/src/ble_advertising_driver.rs +++ b/capsules/extra/src/ble_advertising_driver.rs @@ -269,7 +269,7 @@ impl App { let adv_data_len = cmp::min(kernel_tx.len() - PACKET_ADDR_LEN - 2, adv_data.len()); let adv_data_corrected = - adv_data.get_to(..adv_data_len).ok_or(ErrorCode::SIZE)?; + adv_data.get(..adv_data_len).ok_or(ErrorCode::SIZE)?; let payload_len = adv_data_corrected.len() + PACKET_ADDR_LEN; { let (header, payload) = kernel_tx.split_at_mut(2); diff --git a/capsules/extra/src/bus.rs b/capsules/extra/src/bus.rs index b547fb6263..7f71e27361 100644 --- a/capsules/extra/src/bus.rs +++ b/capsules/extra/src/bus.rs @@ -29,15 +29,25 @@ use core::cell::Cell; use kernel::debug; -use kernel::hil::bus8080::{self, Bus8080}; +use kernel::hil::bus8080::{self, Bus8080, BusAddr8080}; use kernel::hil::i2c::{Error, I2CClient, I2CDevice}; use kernel::hil::spi::{ClockPhase, ClockPolarity, SpiMasterClient, SpiMasterDevice}; use kernel::utilities::cells::OptionalCell; use kernel::utilities::leasable_buffer::SubSliceMut; use kernel::ErrorCode; -/// Bus width used for address width and data width -pub enum BusWidth { +// Buses, such as I2C or SPI, are generally serial and transmit data byte by byte, +// without taking endianness into account. The receiving device—in this case, +// the screen—interprets the data and determines the endianness. +// In Tock, the lower-level screen driver sets the address endianness. +// For most buses, with the exception of the parallel 8080 bus, endianness +// is largely transparent. +// We store addresses using primitive data types like `u8`, `u16`, `u32`, and `u64`. + +/// The `DataWidth` enum and associated `BusAddr` structs define the width +/// of the data transmitted over a bus. The `BusAddr::bytes`` function transforms +/// the address into the specified endianness and returns an iterator. +pub enum DataWidth { Bits8, Bits16LE, Bits16BE, @@ -47,31 +57,158 @@ pub enum BusWidth { Bits64BE, } -impl BusWidth { +/// Each `BusAddr` struct represents a specific data width and endianness. + +/// 8 bit Bus Address +pub struct BusAddr8(u8); + +/// 16 bit Big Endian Bus Address +pub struct BusAddr16BE(u16); + +/// 16 bit Little Endian Bus Address +pub struct BusAddr16LE(u16); + +/// 32 bit Big Endian Bus Address +pub struct BusAddr32BE(u32); + +/// 32 bit Little Endian Bus Address +pub struct BusAddr32LE(u32); + +/// 64 bit Big Endian Bus Address +pub struct BusAddr64BE(u64); + +/// 64 bit Little Endian Bus Address +pub struct BusAddr64LE(u64); + +impl From for BusAddr8080 { + fn from(value: BusAddr8) -> Self { + BusAddr8080::BusAddr8(value.0) + } +} +impl From for BusAddr8080 { + fn from(value: BusAddr16BE) -> Self { + BusAddr8080::BusAddr16BE(value.0) + } +} +impl From for BusAddr8080 { + fn from(value: BusAddr16LE) -> Self { + BusAddr8080::BusAddr16LE(value.0) + } +} + +impl From for BusAddr8 { + fn from(value: u8) -> Self { + Self(value) + } +} +impl From for BusAddr16BE { + fn from(value: u16) -> Self { + Self(value) + } +} +impl From for BusAddr16LE { + fn from(value: u16) -> Self { + Self(value) + } +} +impl From for BusAddr32BE { + fn from(value: u32) -> Self { + Self(value) + } +} +impl From for BusAddr32LE { + fn from(value: u32) -> Self { + Self(value) + } +} +impl From for BusAddr64BE { + fn from(value: u64) -> Self { + Self(value) + } +} +impl From for BusAddr64LE { + fn from(value: u64) -> Self { + Self(value) + } +} + +/// The `BusAddr` trait is implemented for each BusAddr struct. +/// It provides information about the data width and a way +/// to access the underlying byte representation. +pub trait BusAddr { + const DATA_WIDTH: DataWidth; + fn len(&self) -> usize { + Self::DATA_WIDTH.width_in_bytes() + } + fn bytes(&self) -> impl Iterator; +} +impl BusAddr for BusAddr8 { + const DATA_WIDTH: DataWidth = DataWidth::Bits8; + fn bytes(&self) -> impl Iterator { + self.0.to_be_bytes().into_iter() + } +} +impl BusAddr for BusAddr16BE { + const DATA_WIDTH: DataWidth = DataWidth::Bits16BE; + fn bytes(&self) -> impl Iterator { + self.0.to_be_bytes().into_iter() + } +} +impl BusAddr for BusAddr16LE { + const DATA_WIDTH: DataWidth = DataWidth::Bits16LE; + fn bytes(&self) -> impl Iterator { + self.0.to_le_bytes().into_iter() + } +} +impl BusAddr for BusAddr32BE { + const DATA_WIDTH: DataWidth = DataWidth::Bits32BE; + fn bytes(&self) -> impl Iterator { + self.0.to_be_bytes().into_iter() + } +} +impl BusAddr for BusAddr32LE { + const DATA_WIDTH: DataWidth = DataWidth::Bits32LE; + fn bytes(&self) -> impl Iterator { + self.0.to_le_bytes().into_iter() + } +} +impl BusAddr for BusAddr64BE { + const DATA_WIDTH: DataWidth = DataWidth::Bits64BE; + fn bytes(&self) -> impl Iterator { + self.0.to_be_bytes().into_iter() + } +} +impl BusAddr for BusAddr64LE { + const DATA_WIDTH: DataWidth = DataWidth::Bits64LE; + fn bytes(&self) -> impl Iterator { + self.0.to_le_bytes().into_iter() + } +} + +impl DataWidth { pub fn width_in_bytes(&self) -> usize { match self { - BusWidth::Bits8 => 1, - BusWidth::Bits16BE | BusWidth::Bits16LE => 2, - BusWidth::Bits32BE | BusWidth::Bits32LE => 3, - BusWidth::Bits64BE | BusWidth::Bits64LE => 4, + DataWidth::Bits8 => 1, + DataWidth::Bits16BE | DataWidth::Bits16LE => 2, + DataWidth::Bits32BE | DataWidth::Bits32LE => 4, + DataWidth::Bits64BE | DataWidth::Bits64LE => 8, } } } -pub trait Bus<'a> { +pub trait Bus<'a, A: BusAddr> { /// Set the address to write to /// /// If the underlying bus does not support addresses (eg UART) /// this function returns ENOSUPPORT - fn set_addr(&self, addr_width: BusWidth, addr: usize) -> Result<(), ErrorCode>; - + fn set_addr(&self, addr: A) -> Result<(), ErrorCode>; /// Write data items to the previously set address /// /// data_width specifies the encoding of the data items placed in the buffer /// len specifies the number of data items (the number of bytes is len * data_width.width_in_bytes) fn write( &self, - data_width: BusWidth, + data_width: DataWidth, buffer: &'static mut [u8], len: usize, ) -> Result<(), (ErrorCode, &'static mut [u8])>; @@ -82,7 +219,7 @@ pub trait Bus<'a> { /// len specifies the number of data items (the number of bytes is len * data_width.width_in_bytes) fn read( &self, - data_width: BusWidth, + data_width: DataWidth, buffer: &'static mut [u8], len: usize, ) -> Result<(), (ErrorCode, &'static mut [u8])>; @@ -149,34 +286,38 @@ impl<'a, S: SpiMasterDevice<'a>> SpiMasterBus<'a, S> { } } -impl<'a, S: SpiMasterDevice<'a>> Bus<'a> for SpiMasterBus<'a, S> { - fn set_addr(&self, addr_width: BusWidth, addr: usize) -> Result<(), ErrorCode> { - match addr_width { - BusWidth::Bits8 => { - self.addr_buffer - .take() - .map_or(Err(ErrorCode::NOMEM), |mut buffer| { - self.status.set(BusStatus::SetAddress); - buffer.reset(); - buffer.slice(0..1); - buffer[0] = addr as u8; - if let Err((error, buffer, _)) = self.spi.read_write_bytes(buffer, None) { - self.status.set(BusStatus::Idle); - self.addr_buffer.replace(buffer); - Err(error) - } else { - Ok(()) - } - }) - } - - _ => Err(ErrorCode::NOSUPPORT), - } +impl<'a, A: BusAddr, S: SpiMasterDevice<'a>> Bus<'a, A> for SpiMasterBus<'a, S> { + fn set_addr(&self, addr: A) -> Result<(), ErrorCode> { + self.addr_buffer + .take() + .map_or(Err(ErrorCode::NOMEM), |mut buffer| { + let bytes = addr.bytes(); + if buffer.len() >= addr.len() { + buffer.reset(); + buffer.slice(0..addr.len()); + self.status.set(BusStatus::SetAddress); + buffer + .as_slice() + .iter_mut() + .zip(bytes) + .for_each(|(d, s)| *d = s); + if let Err((error, buffer, _)) = self.spi.read_write_bytes(buffer, None) { + self.status.set(BusStatus::Idle); + self.addr_buffer.replace(buffer); + Err(error) + } else { + Ok(()) + } + } else { + self.addr_buffer.replace(buffer); + Err(ErrorCode::SIZE) + } + }) } fn write( &self, - data_width: BusWidth, + data_width: DataWidth, buffer: &'static mut [u8], len: usize, ) -> Result<(), (ErrorCode, &'static mut [u8])> { @@ -200,7 +341,7 @@ impl<'a, S: SpiMasterDevice<'a>> Bus<'a> for SpiMasterBus<'a, S> { fn read( &self, - data_width: BusWidth, + data_width: DataWidth, buffer: &'static mut [u8], len: usize, ) -> Result<(), (ErrorCode, &'static mut [u8])> { @@ -295,31 +436,32 @@ impl<'a, I: I2CDevice> I2CMasterBus<'a, I> { } } -impl<'a, I: I2CDevice> Bus<'a> for I2CMasterBus<'a, I> { - fn set_addr(&self, addr_width: BusWidth, addr: usize) -> Result<(), ErrorCode> { - match addr_width { - BusWidth::Bits8 => self - .addr_buffer - .take() - .map_or(Err(ErrorCode::NOMEM), |buffer| { - buffer[0] = addr as u8; - self.status.set(BusStatus::SetAddress); - match self.i2c.write(buffer, 1) { +impl<'a, A: BusAddr, I: I2CDevice> Bus<'a, A> for I2CMasterBus<'a, I> { + fn set_addr(&self, addr: A) -> Result<(), ErrorCode> { + self.addr_buffer + .take() + .map_or(Err(ErrorCode::NOMEM), |buffer| { + self.status.set(BusStatus::SetAddress); + let bytes = addr.bytes(); + if buffer.len() >= addr.len() { + let () = buffer.iter_mut().zip(bytes).for_each(|(d, s)| *d = s); + match self.i2c.write(buffer, addr.len()) { Ok(()) => Ok(()), Err((error, buffer)) => { self.addr_buffer.replace(buffer); Err(error.into()) } } - }), - - _ => Err(ErrorCode::NOSUPPORT), - } + } else { + self.addr_buffer.replace(buffer); + Err(ErrorCode::SIZE) + } + }) } fn write( &self, - data_width: BusWidth, + data_width: DataWidth, buffer: &'static mut [u8], len: usize, ) -> Result<(), (ErrorCode, &'static mut [u8])> { @@ -341,7 +483,7 @@ impl<'a, I: I2CDevice> Bus<'a> for I2CMasterBus<'a, I> { fn read( &self, - data_width: BusWidth, + data_width: DataWidth, buffer: &'static mut [u8], len: usize, ) -> Result<(), (ErrorCode, &'static mut [u8])> { @@ -408,28 +550,25 @@ impl<'a, B: Bus8080<'static>> Bus8080Bus<'a, B> { } } - fn to_bus8080_width(bus_width: BusWidth) -> Option { + fn to_bus8080_width(bus_width: DataWidth) -> Option { match bus_width { - BusWidth::Bits8 => Some(bus8080::BusWidth::Bits8), - BusWidth::Bits16LE => Some(bus8080::BusWidth::Bits16LE), - BusWidth::Bits16BE => Some(bus8080::BusWidth::Bits16BE), + DataWidth::Bits8 => Some(bus8080::BusWidth::Bits8), + DataWidth::Bits16LE => Some(bus8080::BusWidth::Bits16LE), + DataWidth::Bits16BE => Some(bus8080::BusWidth::Bits16BE), _ => None, } } } -impl<'a, B: Bus8080<'static>> Bus<'a> for Bus8080Bus<'a, B> { - fn set_addr(&self, addr_width: BusWidth, addr: usize) -> Result<(), ErrorCode> { - if let Some(bus_width) = Self::to_bus8080_width(addr_width) { - self.bus.set_addr(bus_width, addr) - } else { - Err(ErrorCode::INVAL) - } +impl<'a, A: BusAddr + Into, B: Bus8080<'static>> Bus<'a, A> for Bus8080Bus<'a, B> { + fn set_addr(&self, addr: A) -> Result<(), ErrorCode> { + let _ = self.bus.set_addr(addr.into()); + Ok(()) } fn write( &self, - data_width: BusWidth, + data_width: DataWidth, buffer: &'static mut [u8], len: usize, ) -> Result<(), (ErrorCode, &'static mut [u8])> { @@ -442,7 +581,7 @@ impl<'a, B: Bus8080<'static>> Bus<'a> for Bus8080Bus<'a, B> { fn read( &self, - data_width: BusWidth, + data_width: DataWidth, buffer: &'static mut [u8], len: usize, ) -> Result<(), (ErrorCode, &'static mut [u8])> { diff --git a/capsules/extra/src/lib.rs b/capsules/extra/src/lib.rs index 0067fd5a45..6f0d569e52 100644 --- a/capsules/extra/src/lib.rs +++ b/capsules/extra/src/lib.rs @@ -80,7 +80,9 @@ pub mod rf233_const; pub mod screen; pub mod screen_shared; pub mod sdcard; +pub mod servo; pub mod seven_segment; +pub mod sg90; pub mod sh1106; pub mod sha; pub mod sha256; diff --git a/capsules/extra/src/lpm013m126.rs b/capsules/extra/src/lpm013m126.rs index 37a3cb4bd9..a2ae34a37c 100644 --- a/capsules/extra/src/lpm013m126.rs +++ b/capsules/extra/src/lpm013m126.rs @@ -17,21 +17,103 @@ use core::cmp; use kernel::debug; use kernel::deferred_call::{DeferredCall, DeferredCallClient}; use kernel::hil::gpio::Pin; -use kernel::hil::screen::{Screen, ScreenClient, ScreenPixelFormat, ScreenRotation}; +use kernel::hil::screen::{Screen, ScreenClient, ScreenPixelFormat, ScreenRotation, ScreenSetup}; use kernel::hil::spi::{SpiMasterClient, SpiMasterDevice}; use kernel::hil::time::{Alarm, AlarmClient, ConvertTicks}; use kernel::utilities::cells::{OptionalCell, TakeCell}; use kernel::utilities::leasable_buffer::SubSliceMut; use kernel::ErrorCode; -/// Monochrome frame buffer bytes. -/// 176 × 176 bits = 3872 bytes. +/// 4-bit frame buffer bytes. /// -/// 2 bytes for the start of each row (command header), -/// plus 2 bytes of data transfer period at the end -/// -/// 176 * 2 + 2 = 354 bytes. -pub const BUF_LEN: usize = 3872 + 354; +/// 176 rows, of 176 4-bit pixels and a 2-byte command header, plus a +/// trailing 2 byte transfer period +const ROWS: usize = 176; +const COLS: usize = 176; +const ROW_BYTES: usize = COLS / 2; +const LINE_LEN: usize = ROW_BYTES + 2; +pub const BUF_LEN: usize = ROWS * LINE_LEN + 2; + +struct InputBuffer<'a, const PIXEL_BITS: usize> { + data: &'a [u8], + frame: &'a WriteFrame, +} + +impl<'a, const PIXEL_BITS: usize> InputBuffer<'a, PIXEL_BITS> { + fn rows(&self) -> impl Iterator { + let chunk_width = if PIXEL_BITS < 8 { + self.frame.width as usize / (8 / PIXEL_BITS) + } else { + self.frame.width as usize * (PIXEL_BITS / 8) + }; + self.data.chunks(chunk_width).map(|data| Row { data }) + } +} + +struct Pixel<'a> { + data: &'a u8, + top: bool, +} + +impl<'a> Pixel<'a> { + fn get(&self) -> u8 { + if self.top { + (*self.data >> 4) & 0xf + } else { + *self.data & 0xf + } + } +} + +struct PixelMut<'a> { + data: &'a Cell, + top: bool, +} + +impl<'a> PixelMut<'a> { + fn transform(&self, f: F) + where + F: FnOnce(&mut u8), + { + let mut data = if self.top { + (self.data.get() & 0xf0) >> 4 + } else { + self.data.get() & 0x0f + }; + + f(&mut data); + + if self.top { + self.data.set(self.data.get() & 0x0f | ((data << 4) & 0xf0)); + } else { + self.data.set(self.data.get() & 0xf0 | (data & 0x0f)); + } + } +} + +struct Row<'a> { + data: &'a [u8], +} + +impl<'a> Row<'a> { + fn iter<'b>(&'b self) -> impl Iterator> { + self.data + .iter() + .flat_map(|data| [Pixel { data, top: true }, Pixel { data, top: false }]) + } +} + +struct RowMut<'a> { + data: &'a [Cell], +} + +impl<'a> RowMut<'a> { + fn iter_mut(&self) -> impl Iterator { + self.data + .iter() + .flat_map(|data| [PixelMut { data, top: true }, PixelMut { data, top: false }]) + } +} /// Arranges frame data in a buffer /// whose portions can be sent directly to the device. @@ -50,55 +132,150 @@ impl<'a> FrameBuffer<'a> { /// Initialize header bytes for each line. fn initialize(&mut self) { - for i in 0..176 { - let line = self.get_line_mut(i); - let bytes = CommandHeader { - mode: Mode::Input1Bit, - gate_line: i, - } - .encode(); - line[..2].copy_from_slice(&bytes); + for i in 0..ROWS { + self.set_line_header( + i, + &CommandHeader { + mode: Mode::Input4Bit, + gate_line: (i + 1) as u16, + }, + ); } } /// Copy pixels from the buffer. The buffer may be shorter than frame. - fn blit(&mut self, buffer: &[u8], frame: &WriteFrame) { - if frame.column % 8 != 0 { - // Can't be arsed to bit shift pixels… - panic!("Horizontal offset not supported"); + fn blit_rgb565(&mut self, buffer: InputBuffer<16>) { + let frame_rows = self + .rows() + .skip(buffer.frame.row as usize) + .take(buffer.frame.height as usize); + let buf_rows = buffer.rows(); + + for (frame_row, buf_row) in frame_rows.zip(buf_rows) { + for (frame_pixel, buf_pixel) in frame_row + .iter_mut() + .skip(buffer.frame.column as usize) + .zip(buf_row.data.chunks_exact(2)) + { + let buf_pixel = [buf_pixel[0], buf_pixel[1]]; + let buf_p = u16::from_le_bytes(buf_pixel); + frame_pixel.transform(|pixel| { + let red = if (buf_p >> 11) & 0b11111 >= 32 / 2 { + // are red five bits more than 50%? + 0b1000 + } else { + 0 + }; + + let green = if (buf_p >> 5) & 0b111111 >= 64 / 2 { + // green 6 bits more than 50%? + 0b0100 + } else { + 0 + }; + + let blue = if buf_p & 0b11111 >= 32 / 2 { + // blue five bits more than 50%? + 0b0010 + } else { + 0 + }; + + *pixel = red | green | blue; + }); + } } - let rows = (frame.row)..(frame.row + frame.height); - // There are 8 pixels in each row per byte. - let sources = buffer.chunks(frame.width as usize / 8); - for (i, source) in rows.zip(sources) { - let row = self.get_row_mut(i); - row[(frame.column as usize / 8)..][..(source.len())].copy_from_slice(source); + } + + /// Copy pixels from the buffer. The buffer may be shorter than frame. + fn blit_rgb332(&mut self, buffer: InputBuffer<8>) { + let frame_rows = self + .rows() + .skip(buffer.frame.row as usize) + .take(buffer.frame.height as usize); + let buf_rows = buffer.rows(); + + for (frame_row, buf_row) in frame_rows.zip(buf_rows) { + for (frame_pixel, buf_pixel) in frame_row + .iter_mut() + .skip(buffer.frame.column as usize) + .zip(buf_row.data.iter()) + { + let buf_p: u8 = *buf_pixel; + frame_pixel.transform(|pixel| { + let red = if (buf_p >> 5) & 0b111 >= 7 / 2 { + // are red three bits more than 50%? + 0b1000 + } else { + 0 + }; + + let green = if (buf_p >> 2) & 0b111 >= 7 / 2 { + // green three bits more than 50%? + 0b0100 + } else { + 0 + }; + + let blue = if buf_p & 0b11 >= 3 / 2 { + // blue two bits more than 50%? + 0b0010 + } else { + 0 + }; + + *pixel = red | green | blue; + }); + } } } - /// Gets an entire raw line, ready to send. - fn get_line_mut(&mut self, index: u16) -> &mut [u8] { - const CMD: usize = 2; - const TRANSFER_PERIOD: usize = 2; - let line_bytes = CMD + 176 / 8; - &mut self.data[(line_bytes * index as usize)..][..line_bytes + TRANSFER_PERIOD] + /// Copy pixels from the buffer. The buffer may be shorter than frame. + fn blit_4bit_srgb(&mut self, buffer: InputBuffer<4>) { + let frame_rows = self + .rows() + .skip(buffer.frame.row as usize) + .take(buffer.frame.height as usize); + let buf_rows = buffer.rows(); + + for (frame_row, buf_row) in frame_rows.zip(buf_rows) { + for (frame_pixel, buf_pixel) in frame_row + .iter_mut() + .skip(buffer.frame.column as usize) + .zip(buf_row.iter()) + { + let buf_p: u8 = buf_pixel.get(); + if buf_p & 0b1 != 0 { + frame_pixel.transform(|pixel| { + // transform from sRGB to the LPM native 4-bit format. + // + // 4-bit sRGB is encoded as `| B | G | R | s |`, where + // `s` is something like intensity. We'll interpret + // intensity `0` to mean transparent, and intensity + // `1` to mean opaque. Meanwhile LPM native 4-bit is + // encoded as `| R | G | B | x |`, where `x` is + // ignored. So we need to swap the R & B bits, and + // only apply the pixel if `s` is 1. + *pixel = ((buf_p & 0b10) << 2) | (buf_p & 0b100) | ((buf_p & 0b1000) >> 2); + }); + } + } + } } - /// Gets pixel data. - fn get_row_mut(&mut self, index: u16) -> &mut [u8] { - let line_bytes = 176 / 8 + 2; - &mut self.data[(line_bytes * index as usize + 2)..][..(176 / 8)] + fn set_line_header(&mut self, index: usize, header: &CommandHeader) { + const CMD: usize = 2; + if let Some(buf) = self.data[(LINE_LEN * index)..].first_chunk_mut::() { + *buf = header.encode(); + } } - /// Transform into a view of raw data for submitting to the DMA driver - fn with_raw_rows( - frame_buffer: FrameBuffer<'static>, - _start: u16, - _end: u16, - ) -> SubSliceMut<'static, u8> { - // HILs typically can't use offsets :/ - // Best we can do is limit length (TODO) - frame_buffer.data + fn rows(&mut self) -> impl Iterator { + self.data.as_slice().chunks_mut(LINE_LEN).map_while(|c| { + c.get_mut(2..).map(|data| RowMut { + data: Cell::from_mut(data).as_slice_of_cells(), + }) + }) } } @@ -113,6 +290,8 @@ enum Mode { /// Input 1-bit data /// bits: 1 No function, X, 0 Data Update, 01 1-bit, X Input1Bit = 0b100_01_0, + Input4Bit = 0b100100, + NoUpdate = 0b101000, } /// Command header is composed of a 6-bit mode and 10 bits of address, @@ -125,7 +304,7 @@ struct CommandHeader { impl CommandHeader { /// Formats header for transfer fn encode(&self) -> [u8; 2] { - (self.gate_line | ((self.mode as u16) << 10)).to_be_bytes() + ((self.gate_line & 0b1111111111) | ((self.mode as u16) << 10)).to_be_bytes() } } @@ -152,8 +331,9 @@ enum State { InitializingRest, // Normal operation - Idle(WriteFrame), - Writing(WriteFrame), + Idle, + AllClearing, + Writing, /// This driver is buggy. Turning off and on will try to recover it. Bug, @@ -171,16 +351,15 @@ pub struct Lpm013m126<'a, A: Alarm<'a>, P: Pin, S: SpiMasterDevice<'a>> { state: Cell, + pixel_format: Cell, + frame: Cell, + /// Fields responsible for sending callbacks /// for actions completed in software. ready_callback: DeferredCall, ready_callback_handler: ReadyCallbackHandler<'a, A, P, S>, command_complete_callback: DeferredCall, command_complete_callback_handler: CommandCompleteCallbackHandler<'a, A, P, S>, - write_complete_callback: DeferredCall, - write_complete_callback_handler: WriteCompleteCallbackHandler<'a, A, P, S>, - /// Holds the pending call parameter - write_complete_pending_call: OptionalCell>, /// The HIL requires updates to arbitrary rectangles. /// The display supports only updating entire rows, @@ -205,29 +384,29 @@ where extcomin: &'a P, disp: &'a P, alarm: &'a A, - frame_buffer: &'static mut [u8], + frame_buffer: &'static mut [u8; BUF_LEN], ) -> Result { - if frame_buffer.len() < BUF_LEN { - Err(InitError::BufferTooSmall) - } else { - Ok(Self { - spi, - alarm, - disp, - extcomin, - ready_callback: DeferredCall::new(), - ready_callback_handler: ReadyCallbackHandler::new(), - command_complete_callback: DeferredCall::new(), - command_complete_callback_handler: CommandCompleteCallbackHandler::new(), - write_complete_callback: DeferredCall::new(), - write_complete_callback_handler: WriteCompleteCallbackHandler::new(), - write_complete_pending_call: OptionalCell::empty(), - frame_buffer: OptionalCell::new(FrameBuffer::new(frame_buffer.into())), - buffer: TakeCell::empty(), - client: OptionalCell::empty(), - state: Cell::new(State::Uninitialized), - }) - } + Ok(Self { + spi, + alarm, + disp, + extcomin, + ready_callback: DeferredCall::new(), + ready_callback_handler: ReadyCallbackHandler::new(), + command_complete_callback: DeferredCall::new(), + command_complete_callback_handler: CommandCompleteCallbackHandler::new(), + frame_buffer: OptionalCell::new(FrameBuffer::new((frame_buffer as &mut [u8]).into())), + pixel_format: Cell::new(ScreenPixelFormat::RGB_565), + buffer: TakeCell::empty(), + client: OptionalCell::empty(), + state: Cell::new(State::Uninitialized), + frame: Cell::new(WriteFrame { + row: 0, + column: 0, + width: COLS as u16, + height: ROWS as u16, + }), + }) } /// Set up internal data structures. @@ -244,9 +423,6 @@ where self.command_complete_callback_handler.lpm.set(self); self.command_complete_callback .register(&self.command_complete_callback_handler); - self.write_complete_callback_handler.lpm.set(self); - self.write_complete_callback - .register(&self.write_complete_callback_handler); self.state.set(State::Off); Ok(()) @@ -276,20 +452,16 @@ where // Cheating a little: // the frame buffer does not yet contain pixels, // so use its beginning to send the clear command. - let buf = &mut frame_buffer.get_line_mut(0)[..2]; - buf.copy_from_slice( + frame_buffer.set_line_header( + 0, &CommandHeader { mode: Mode::AllClear, gate_line: 0, - } - .encode(), + }, ); - let mut l = FrameBuffer::with_raw_rows(frame_buffer, 0, 1); + let mut l = frame_buffer.data; l.slice(0..2); - let res = self.spi.read_write_bytes( - l, //FrameBuffer::with_raw_rows(frame_buffer, 0, 1), - None, - ); + let res = self.spi.read_write_bytes(l, None); let (res, new_state) = match res { Ok(()) => (Ok(()), State::InitializingPixelMemory), @@ -327,15 +499,10 @@ where } } - fn call_write_complete(&self, ret: Result<(), ErrorCode>) { - self.write_complete_callback.set(); - self.write_complete_pending_call.set(ret); - } - fn arm_alarm(&self) { // Datasheet says 2Hz or more often flipping is required // for transmissive mode. - let delay = self.alarm.ticks_from_ms(500); + let delay = self.alarm.ticks_from_ms(100); self.alarm.set_alarm(self.alarm.now(), delay); } @@ -343,18 +510,6 @@ where self.client.map(|client| client.screen_is_ready()); } - fn handle_write_complete_callback(&self) { - self.client.map(|client| { - self.write_complete_pending_call.map(|pend| { - self.buffer.take().map(|buffer| { - let data = SubSliceMut::new(buffer); - client.write_complete(data, pend) - }); - }); - self.write_complete_pending_call.take(); - }); - } - fn handle_command_complete_callback(&self) { // Thankfully, this is the only command that results in the callback, // so there's no danger that this will get attributed @@ -368,11 +523,11 @@ where Self: 'static, { fn get_resolution(&self) -> (usize, usize) { - (176, 176) + (ROWS, COLS) } fn get_pixel_format(&self) -> ScreenPixelFormat { - ScreenPixelFormat::Mono + self.pixel_format.get() } fn get_rotation(&self) -> ScreenRotation { @@ -388,7 +543,7 @@ where ) -> Result<(), ErrorCode> { let (columns, rows) = self.get_resolution(); if y >= rows || y + height > rows || x >= columns || x + width > columns { - return Err(ErrorCode::INVAL); + //return Err(ErrorCode::INVAL); } let frame = WriteFrame { @@ -397,29 +552,11 @@ where width: width as u16, height: height as u16, }; - - let mut new_state = None; - let ret = match self.state.get() { - State::Uninitialized | State::Off => Err(ErrorCode::OFF), - State::InitializingPixelMemory | State::InitializingRest => Err(ErrorCode::BUSY), - State::Idle(..) => { - new_state = Some(State::Idle(frame)); - Ok(()) - } - State::Writing(..) => { - new_state = Some(State::Writing(frame)); - Ok(()) - } - State::Bug => Err(ErrorCode::FAIL), - }; + self.frame.set(frame); self.command_complete_callback.set(); - if let Some(new_state) = new_state { - self.state.set(new_state); - } - - ret + Ok(()) } fn write( @@ -433,41 +570,57 @@ where let ret = match self.state.get() { State::Uninitialized | State::Off => Err(ErrorCode::OFF), State::InitializingPixelMemory | State::InitializingRest => Err(ErrorCode::BUSY), - State::Idle(frame) => { + State::Idle => { self.frame_buffer .take() .map_or(Err(ErrorCode::NOMEM), |mut frame_buffer| { - // TODO: reject if buffer is shorter than frame - frame_buffer.blit(&buffer[..cmp::min(buffer.len(), len)], &frame); - let send_buf = FrameBuffer::with_raw_rows( - frame_buffer, - frame.row, - frame.row + frame.height, + match self.pixel_format.get() { + ScreenPixelFormat::RGB_332 => { + frame_buffer.blit_rgb332(InputBuffer { + data: &buffer[..cmp::min(buffer.len(), len)], + frame: &self.frame.get(), + }); + } + ScreenPixelFormat::RGB_565 => { + frame_buffer.blit_rgb565(InputBuffer { + data: &buffer[..cmp::min(buffer.len(), len)], + frame: &self.frame.get(), + }); + } + _ => frame_buffer.blit_4bit_srgb(InputBuffer { + data: &buffer[..cmp::min(buffer.len(), len)], + frame: &self.frame.get(), + }), + } + + frame_buffer.set_line_header( + 0, + &CommandHeader { + mode: Mode::NoUpdate, + gate_line: 0, + }, ); + let mut l = frame_buffer.data; + l.slice(0..2); + let sent = self.spi.read_write_bytes(l, None); - let sent = self.spi.read_write_bytes(send_buf, None); let (ret, new_state) = match sent { - Ok(()) => (Ok(()), State::Writing(frame)), + Ok(()) => (Ok(()), State::AllClearing), Err((e, buf, _)) => { self.frame_buffer.replace(FrameBuffer::new(buf)); - (Err(e), State::Idle(frame)) + (Err(e), State::Idle) } }; self.state.set(new_state); ret }) } - State::Writing(..) => Err(ErrorCode::BUSY), + State::AllClearing | State::Writing => Err(ErrorCode::BUSY), State::Bug => Err(ErrorCode::FAIL), }; self.buffer.replace(buffer); - match self.state.get() { - State::Writing(..) => {} - _ => self.call_write_complete(ret), - }; - ret } @@ -502,6 +655,58 @@ where } } +impl<'a, A: Alarm<'a>, P: Pin, S: SpiMasterDevice<'a>> ScreenSetup<'a> for Lpm013m126<'a, A, P, S> { + fn set_client(&self, _client: &'a dyn kernel::hil::screen::ScreenSetupClient) { + todo!() + } + + fn set_resolution(&self, resolution: (usize, usize)) -> Result<(), ErrorCode> { + if resolution == (ROWS, COLS) { + Ok(()) + } else { + Err(ErrorCode::NOSUPPORT) + } + } + + fn set_pixel_format(&self, format: ScreenPixelFormat) -> Result<(), ErrorCode> { + match format { + ScreenPixelFormat::RGB_4BIT | ScreenPixelFormat::RGB_332 => { + self.pixel_format.set(format); + Ok(()) + } + _ => Err(ErrorCode::NOSUPPORT), + } + } + + fn set_rotation(&self, _rotation: ScreenRotation) -> Result<(), ErrorCode> { + todo!() + } + + fn get_num_supported_resolutions(&self) -> usize { + 1 + } + + fn get_supported_resolution(&self, index: usize) -> Option<(usize, usize)> { + match index { + 0 => Some((ROWS, COLS)), + _ => None, + } + } + + fn get_num_supported_pixel_formats(&self) -> usize { + 3 + } + + fn get_supported_pixel_format(&self, index: usize) -> Option { + match index { + 0 => Some(ScreenPixelFormat::RGB_4BIT), + 1 => Some(ScreenPixelFormat::RGB_332), + 2 => Some(ScreenPixelFormat::RGB_565), + _ => None, + } + } +} + impl<'a, A: Alarm<'a>, P: Pin, S: SpiMasterDevice<'a>> AlarmClient for Lpm013m126<'a, A, P, S> where Self: 'static, @@ -511,7 +716,8 @@ where State::InitializingRest => { // Better flip it once too many than go out of spec // by stretching the flip period. - self.extcomin.toggle(); + self.extcomin.set(); + self.disp.set(); self.arm_alarm(); let new_state = self.frame_buffer.take().map_or_else( || { @@ -524,34 +730,18 @@ where |mut buffer| { buffer.initialize(); self.frame_buffer.replace(buffer); - State::Idle( - // The HIL doesn't specify the initial frame - WriteFrame { - row: 0, - column: 0, - width: 176, - height: 176, - }, - ) + State::Idle }, ); self.state.set(new_state); - if let State::Idle(..) = new_state { + if let State::Idle = new_state { self.client.map(|client| client.screen_is_ready()); } } - State::Idle(..) | State::Writing(..) => { + _ => { self.extcomin.toggle(); - self.arm_alarm(); - } - other => { - debug!( - "LPM013M126 driver got alarm in unexpected state {:?}", - other - ); - self.state.set(State::Bug); } }; } @@ -571,13 +761,38 @@ impl<'a, A: Alarm<'a>, P: Pin, S: SpiMasterDevice<'a>> SpiMasterClient for Lpm01 // for 2 reasons: // 1. the upper limit of waiting is only specified for both, // 2. and state flipping code is annoying and bug-friendly. - self.disp.set(); - self.extcomin.set(); - let delay = self.alarm.ticks_from_us(200); + let delay = self.alarm.ticks_from_us(150); self.alarm.set_alarm(self.alarm.now(), delay); State::InitializingRest } - State::Writing(frame) => State::Idle(frame), + State::AllClearing => { + if let Some(mut fb) = self.frame_buffer.take() { + fb.set_line_header( + 0, + &CommandHeader { + mode: Mode::Input4Bit, + gate_line: 1, + }, + ); + let mut send_buf = fb.data; + + let first_row = cmp::min(ROWS as u16, self.frame.get().row); + let offset = first_row as usize * LINE_LEN; + let len = cmp::min(ROWS as u16 - first_row, self.frame.get().height) as usize + * LINE_LEN; + send_buf.slice(offset..(offset + len + 2)); + + let _ = self.spi.read_write_bytes(send_buf, None); + } + State::Writing + } + State::Writing => { + if let Some(mut fb) = self.frame_buffer.take() { + fb.initialize(); + self.frame_buffer.set(fb); + } + State::Idle + } // can't get more buggy than buggy other => { debug!( @@ -588,13 +803,15 @@ impl<'a, A: Alarm<'a>, P: Pin, S: SpiMasterDevice<'a>> SpiMasterClient for Lpm01 } }); - // Device frame buffer is now up to date, return pixel buffer to client. - self.client.map(|client| { - self.buffer.take().map(|buf| { - let data = SubSliceMut::new(buf); - client.write_complete(data, status.map(|_| ())) - }) - }); + if let State::Idle = self.state.get() { + // Device frame buffer is now up to date, return pixel buffer to client. + self.client.map(|client| { + self.buffer.take().map(|buf| { + let data = SubSliceMut::new(buf); + client.write_complete(data, status.map(|_| ())) + }) + }); + } } } @@ -651,29 +868,3 @@ where self.lpm.map(|l| l.command_complete_callback.register(self)); } } - -struct WriteCompleteCallbackHandler<'a, A: Alarm<'a>, P: Pin, S: SpiMasterDevice<'a>> { - lpm: OptionalCell<&'a Lpm013m126<'a, A, P, S>>, -} - -impl<'a, A: Alarm<'a>, P: Pin, S: SpiMasterDevice<'a>> WriteCompleteCallbackHandler<'a, A, P, S> { - fn new() -> Self { - Self { - lpm: OptionalCell::empty(), - } - } -} - -impl<'a, A: Alarm<'a>, P: Pin, S: SpiMasterDevice<'a>> DeferredCallClient - for WriteCompleteCallbackHandler<'a, A, P, S> -where - Self: 'static, -{ - fn handle_deferred_call(&self) { - self.lpm.map(|l| l.handle_write_complete_callback()); - } - - fn register(&'static self) { - self.lpm.map(|l| l.write_complete_callback.register(self)); - } -} diff --git a/capsules/extra/src/screen.rs b/capsules/extra/src/screen.rs index c71f884ab6..6e14ad0d7c 100644 --- a/capsules/extra/src/screen.rs +++ b/capsules/extra/src/screen.rs @@ -49,7 +49,7 @@ fn screen_rotation_from(screen_rotation: usize) -> Option { fn screen_pixel_format_from(screen_pixel_format: usize) -> Option { match screen_pixel_format { 0 => Some(ScreenPixelFormat::Mono), - 1 => Some(ScreenPixelFormat::RGB_233), + 1 => Some(ScreenPixelFormat::RGB_332), 2 => Some(ScreenPixelFormat::RGB_565), 3 => Some(ScreenPixelFormat::RGB_888), 4 => Some(ScreenPixelFormat::ARGB_8888), diff --git a/capsules/extra/src/servo.rs b/capsules/extra/src/servo.rs new file mode 100644 index 0000000000..74523e3bf8 --- /dev/null +++ b/capsules/extra/src/servo.rs @@ -0,0 +1,110 @@ +// Licensed under the Apache License, Version 2.0 or the MIT License. +// SPDX-License-Identifier: Apache-2.0 OR MIT +// Copyright Tock Contributors 2024. + +//! This provides virtualized userspace access to a servomotor. +//! +//! Usage +//! ----- +//! +//! use kernel::static_init; +//! let mux_pwm = components::pwm::PwmMuxComponent::new(&peripherals.pwm) +//! .finalize(components::pwm_mux_component_static!(rp2040::pwm::Pwm)); +//! +//! let virtual_pwm_servo: &PwmPinUser<'static, rp2040::pwm::Pwm<'static>> = +//! components::pwm::PwmPinUserComponent::new(mux_pwm, rp2040::gpio::RPGpio::GPIO4) +//! .finalize(components::pwm_pin_user_component_static!(rp2040::pwm::Pwm)); +//! +//! let sg90_servo = static_init!( +//! capsules_extra::sg90::Sg90< +//! 'static, +//! capsules_core::virtualizers::virtual_pwm::PwmPinUser<'static, rp2040::pwm::Pwm>, +//! >, +//! capsules_extra::sg90::Sg90::new(virtual_pwm_servo) +//! ); +//! +//! // Here, we initialize an array of two SG90 servomotors as an example. +//! let multi_servo = static_init!( +//! [&'static dyn hil::servo::Servo<'static>; 2], +//! [sg90_servo, sg90_servo] +//! ); +//! let servo = static_init!( +//! capsules_extra::servo::Servo<'static, 2>, +//! capsules_extra::servo::Servo::new(multi_servo) +//! ); + +use kernel::hil; +use kernel::syscall::{CommandReturn, SyscallDriver}; +use kernel::{ErrorCode, ProcessId}; + +/// Syscall driver number. +use capsules_core::driver; +pub const DRIVER_NUM: usize = driver::NUM::Servo as usize; + +pub struct Servo<'a, const SERVO_COUNT: usize> { + /// The service capsule servo. + servo: &'a [&'a dyn hil::servo::Servo<'a>; SERVO_COUNT], +} + +impl<'a, const SERVO_COUNT: usize> Servo<'a, SERVO_COUNT> { + pub fn new(servo: &'a [&'a dyn hil::servo::Servo<'a>; SERVO_COUNT]) -> Self { + Self { servo } + } +} +/// Provide an interface for userland. +impl<'a, const SERVO_COUNT: usize> SyscallDriver for Servo<'a, SERVO_COUNT> { + /// Command interface. + /// + /// ### `command_num` + /// + /// - `0`: Return Ok(()) if this driver is included on the platform. + /// - `1`: Returns an u32 representing the number of available servomotors. + /// - `2`: Changing the angle immediatelly.`servo_index` receives the index + /// corresponding to the servo whose angle we want to adjust + /// `angle` is used to receive a value between 0 and 180. + /// - `3`: Returning the current angle for a specific index. + fn command( + &self, + command_num: usize, + servo_index: usize, + angle: usize, + _processid: ProcessId, + ) -> CommandReturn { + match command_num { + // Check whether the driver exists. + 0 => CommandReturn::success(), + // Returns the number of available servomotors. + 1 => CommandReturn::success_u32(SERVO_COUNT as u32), + // Change the angle immediately. + 2 => { + if servo_index >= SERVO_COUNT { + CommandReturn::failure(ErrorCode::NODEVICE) + } else { + match angle.try_into() { + Ok(angle) => match self.servo[servo_index].set_angle(angle) { + Ok(()) => CommandReturn::success(), + Err(_) => CommandReturn::failure(ErrorCode::FAIL), + }, + Err(_) => CommandReturn::failure(ErrorCode::INVAL), + } + } + } + // Return the current angle. + 3 => { + if servo_index >= SERVO_COUNT { + CommandReturn::failure(ErrorCode::NODEVICE) + } else { + match self.servo[servo_index].get_angle() { + Ok(angle) => CommandReturn::success_u32(angle as u32), + Err(err) => CommandReturn::failure(err), + } + } + } + _ => CommandReturn::failure(ErrorCode::NOSUPPORT), + } + } + + fn allocate_grant(&self, _process_id: ProcessId) -> Result<(), kernel::process::Error> { + Ok(()) + } +} diff --git a/capsules/extra/src/sg90.rs b/capsules/extra/src/sg90.rs new file mode 100644 index 0000000000..d298ce71ac --- /dev/null +++ b/capsules/extra/src/sg90.rs @@ -0,0 +1,59 @@ +// Licensed under the Apache License, Version 2.0 or the MIT License. +// SPDX-License-Identifier: Apache-2.0 OR MIT +// Copyright Tock Contributors 2024. + +use core::cell::Cell; +use core::mem::size_of; +use kernel::hil; +use kernel::ErrorCode; +pub struct Sg90<'a, P: hil::pwm::PwmPin> { + /// The underlying PWM generator to change the angle. + pwm_pin: &'a P, + /// Stores the angle everytime it changes. + current_angle: Cell>, +} + +impl<'a, P: hil::pwm::PwmPin> Sg90<'a, P> { + pub fn new(pwm_pin: &'a P) -> Sg90<'a, P> { + Sg90 { + pwm_pin, + current_angle: Cell::new(None), + } + } +} + +impl<'a, P: hil::pwm::PwmPin> kernel::hil::servo::Servo<'a> for Sg90<'a, P> { + fn set_angle(&self, angle: u16) -> Result<(), ErrorCode> { + // The assert! macro ensures that the code will not compile on platforms + // where `usize` is smaller than `u16`. + const _: () = assert!(size_of::() >= size_of::()); + if angle <= 180 { + self.current_angle.set(Some(angle as usize)); + // As specified in the datasheet: + // https://www.friendlywire.com/projects/ne555-servo-safe/SG90-datasheet.pdf, + // the frequency used for sg90 servo is always 50hz. + const FREQUENCY_HZ: usize = 50; + // This calculates the pulse width in microseconds for a specific angle. + // 500 and 2000 miliseconds define the range within + // which the angle can be set to any position. + let pulse_width_us = 500 + 2000 / 180 * (angle as usize); + // The duty_cycle formula is (pulse_width/period)*100. + // The period is 20 000 miliseconds (also specified in the datasheet). + // If we simplify we're left with pulse_width/20. + // We also need to scale this to the maximum duty_cycle suported by the pin. + // We do this by multiplying the value we get from the + // get_maximum_duty_cycle() function with pulse_width/20 and divide it by 100. + // This leaves us with the below formula: + let duty_cycle = pulse_width_us * self.pwm_pin.get_maximum_duty_cycle() / 20000; + self.pwm_pin.start(FREQUENCY_HZ, duty_cycle)?; + Ok(()) + } else { + Err(ErrorCode::INVAL) + } + } + + fn get_angle(&self) -> Result { + //The SG90 servomotor cannot return its angle. + Err(ErrorCode::NOSUPPORT) + } +} diff --git a/capsules/extra/src/st77xx.rs b/capsules/extra/src/st77xx.rs index 83171160bd..a7acae0df4 100644 --- a/capsules/extra/src/st77xx.rs +++ b/capsules/extra/src/st77xx.rs @@ -34,7 +34,7 @@ //! ); //! ``` -use crate::bus::{self, Bus, BusWidth}; +use crate::bus::{self, Bus, BusAddr8, DataWidth}; use core::cell::Cell; use kernel::hil::gpio::Pin; use kernel::hil::screen::{ @@ -206,7 +206,7 @@ pub enum SendCommand { Slice(&'static Command, usize), } -pub struct ST77XX<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> { +pub struct ST77XX<'a, A: Alarm<'a>, B: Bus<'a, BusAddr8>, P: Pin> { bus: &'a B, alarm: &'a A, dc: Option<&'a P>, @@ -234,7 +234,7 @@ pub struct ST77XX<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> { screen: &'static ST77XXScreen, } -impl<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> ST77XX<'a, A, B, P> { +impl<'a, A: Alarm<'a>, B: Bus<'a, BusAddr8>, P: Pin> ST77XX<'a, A, B, P> { pub fn new( bus: &'a B, alarm: &'a A, @@ -336,14 +336,14 @@ impl<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> ST77XX<'a, A, B, P> { self.command.set(cmd); self.status.set(Status::SendCommand(position, len, repeat)); self.dc.map(|dc| dc.clear()); - let _ = self.bus.set_addr(BusWidth::Bits8, cmd.id as usize); + let _ = self.bus.set_addr(cmd.id.into()); } fn send_command_slice(&self, cmd: &'static Command, len: usize) { self.command.set(cmd); self.dc.map(|dc| dc.clear()); self.status.set(Status::SendCommandSlice(len)); - let _ = self.bus.set_addr(BusWidth::Bits8, cmd.id as usize); + let _ = self.bus.set_addr(cmd.id.into()); } fn send_parameters(&self, position: usize, len: usize, repeat: usize) { @@ -359,7 +359,7 @@ impl<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> ST77XX<'a, A, B, P> { } } self.dc.map(|dc| dc.set()); - let _ = self.bus.write(BusWidth::Bits8, buffer, len); + let _ = self.bus.write(DataWidth::Bits8, buffer, len); }, ); } else { @@ -373,7 +373,7 @@ impl<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> ST77XX<'a, A, B, P> { |buffer| { self.status.set(Status::SendParametersSlice); self.dc.map(|dc| dc.set()); - let _ = self.bus.write(BusWidth::Bits16BE, buffer, len / 2); + let _ = self.bus.write(DataWidth::Bits16BE, buffer, len / 2); }, ); } @@ -683,7 +683,9 @@ impl<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> ST77XX<'a, A, B, P> { } } -impl<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> screen::ScreenSetup<'a> for ST77XX<'a, A, B, P> { +impl<'a, A: Alarm<'a>, B: Bus<'a, BusAddr8>, P: Pin> screen::ScreenSetup<'a> + for ST77XX<'a, A, B, P> +{ fn set_client(&self, setup_client: &'a dyn ScreenSetupClient) { self.setup_client.set(setup_client); } @@ -741,7 +743,7 @@ impl<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> screen::ScreenSetup<'a> for ST77XX<'a } } -impl<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> screen::Screen<'a> for ST77XX<'a, A, B, P> { +impl<'a, A: Alarm<'a>, B: Bus<'a, BusAddr8>, P: Pin> screen::Screen<'a> for ST77XX<'a, A, B, P> { fn get_resolution(&self) -> (usize, usize) { (self.width.get(), self.height.get()) } @@ -790,8 +792,18 @@ impl<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> screen::Screen<'a> for ST77XX<'a, A, } } - fn write(&self, data: SubSliceMut<'static, u8>, continue_write: bool) -> Result<(), ErrorCode> { + fn write( + &self, + mut data: SubSliceMut<'static, u8>, + continue_write: bool, + ) -> Result<(), ErrorCode> { if self.status.get() == Status::Idle { + // Data is provided as RGB565 ( RRRRR GGG | GGG BBBBB ), but the device expects it to come over the bus in little endian, so ( GGG BBBBB | RRRRR GGG ). + // TODO(alevy): replace `chunks_mut` wit `array_chunks` when stable. + for pair in data.as_slice().chunks_mut(2) { + pair.swap(0, 1); + } + self.setup_command.set(false); let len = data.len(); self.write_buffer.replace(data.take()); @@ -853,13 +865,13 @@ impl<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> screen::Screen<'a> for ST77XX<'a, A, } } -impl<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> time::AlarmClient for ST77XX<'a, A, B, P> { +impl<'a, A: Alarm<'a>, B: Bus<'a, BusAddr8>, P: Pin> time::AlarmClient for ST77XX<'a, A, B, P> { fn alarm(&self) { self.do_next_op(); } } -impl<'a, A: Alarm<'a>, B: Bus<'a>, P: Pin> bus::Client for ST77XX<'a, A, B, P> { +impl<'a, A: Alarm<'a>, B: Bus<'a, BusAddr8>, P: Pin> bus::Client for ST77XX<'a, A, B, P> { fn command_complete( &self, buffer: Option<&'static mut [u8]>, diff --git a/capsules/system/src/storage_permissions/individual.rs b/capsules/system/src/storage_permissions/individual.rs index af00719c6f..6e16f948da 100644 --- a/capsules/system/src/storage_permissions/individual.rs +++ b/capsules/system/src/storage_permissions/individual.rs @@ -10,25 +10,36 @@ use kernel::storage_permissions::StoragePermissions; /// Assign storage permissions that grant applications access to their own /// state. -pub struct IndividualStoragePermissions { +pub struct IndividualStoragePermissions< + C: Chip, + D: kernel::process::ProcessStandardDebug, + CAP: ApplicationStorageCapability, +> { cap: CAP, _chip: core::marker::PhantomData, + _debug: core::marker::PhantomData, } -impl IndividualStoragePermissions { +impl + IndividualStoragePermissions +{ pub fn new(cap: CAP) -> Self { Self { cap, _chip: core::marker::PhantomData, + _debug: core::marker::PhantomData, } } } -impl - kernel::process::ProcessStandardStoragePermissionsPolicy - for IndividualStoragePermissions +impl + kernel::process::ProcessStandardStoragePermissionsPolicy + for IndividualStoragePermissions { - fn get_permissions(&self, process: &kernel::process::ProcessStandard) -> StoragePermissions { + fn get_permissions( + &self, + process: &kernel::process::ProcessStandard, + ) -> StoragePermissions { // If we have a fixed ShortId then this process can have storage // permissions. Otherwise we get null permissions. match process.short_app_id() { diff --git a/capsules/system/src/storage_permissions/null.rs b/capsules/system/src/storage_permissions/null.rs index 9c0754834f..324e13f13a 100644 --- a/capsules/system/src/storage_permissions/null.rs +++ b/capsules/system/src/storage_permissions/null.rs @@ -6,24 +6,27 @@ use kernel::platform::chip::Chip; use kernel::storage_permissions::StoragePermissions; /// Always assign no storage permissions. -pub struct NullStoragePermissions { +pub struct NullStoragePermissions { _chip: core::marker::PhantomData, + _debug: core::marker::PhantomData, } -impl NullStoragePermissions { +impl NullStoragePermissions { pub fn new() -> Self { Self { _chip: core::marker::PhantomData, + _debug: core::marker::PhantomData, } } } -impl kernel::process::ProcessStandardStoragePermissionsPolicy - for NullStoragePermissions +impl + kernel::process::ProcessStandardStoragePermissionsPolicy + for NullStoragePermissions { fn get_permissions( &self, - _process: &kernel::process::ProcessStandard, + _process: &kernel::process::ProcessStandard, ) -> StoragePermissions { StoragePermissions::new_null() } diff --git a/capsules/system/src/storage_permissions/tbf_header.rs b/capsules/system/src/storage_permissions/tbf_header.rs index fffe5ff8b3..8ef10cb247 100644 --- a/capsules/system/src/storage_permissions/tbf_header.rs +++ b/capsules/system/src/storage_permissions/tbf_header.rs @@ -17,25 +17,36 @@ use kernel::storage_permissions::StoragePermissions; /// /// If the header is _not_ present, then the process will be assigned null /// permissions. -pub struct TbfHeaderStoragePermissions { +pub struct TbfHeaderStoragePermissions< + C: Chip, + D: kernel::process::ProcessStandardDebug, + CAP: ApplicationStorageCapability, +> { cap: CAP, _chip: core::marker::PhantomData, + _debug: core::marker::PhantomData, } -impl TbfHeaderStoragePermissions { +impl + TbfHeaderStoragePermissions +{ pub fn new(cap: CAP) -> Self { Self { cap, _chip: core::marker::PhantomData, + _debug: core::marker::PhantomData, } } } -impl - kernel::process::ProcessStandardStoragePermissionsPolicy - for TbfHeaderStoragePermissions +impl + kernel::process::ProcessStandardStoragePermissionsPolicy + for TbfHeaderStoragePermissions { - fn get_permissions(&self, process: &kernel::process::ProcessStandard) -> StoragePermissions { + fn get_permissions( + &self, + process: &kernel::process::ProcessStandard, + ) -> StoragePermissions { // If we have a fixed ShortId then this process can have storage // permissions. Otherwise we get null permissions. match process.short_app_id() { diff --git a/chips/earlgrey/src/gpio.rs b/chips/earlgrey/src/gpio.rs index 42ae7f7cf1..dd1aa8d0c1 100644 --- a/chips/earlgrey/src/gpio.rs +++ b/chips/earlgrey/src/gpio.rs @@ -15,7 +15,6 @@ use crate::pinmux_config::EarlGreyPinmuxConfig; use crate::registers::top_earlgrey::GPIO_BASE_ADDR; use crate::registers::top_earlgrey::{ MuxedPads, PinmuxInsel, PinmuxOutsel, PinmuxPeripheralIn, PINMUX_MIO_PERIPH_INSEL_IDX_OFFSET, - PINMUX_PERIPH_OUTSEL_IDX_OFFSET, }; pub const GPIO_BASE: StaticRef = @@ -25,27 +24,49 @@ pub struct Port<'a> { pins: [GpioPin<'a, PadConfig>; 32], } -impl From for PinmuxPeripheralIn { - fn from(pin: GpioBitfield) -> PinmuxPeripheralIn { - // We used fact that first 0-31 values are directly maped to GPIO - Self::try_from(pin.shift as u32).unwrap() - } -} - -impl From for PinmuxOutsel { - fn from(pin: GpioBitfield) -> Self { - // We skip first 3 constans to convert value to output selector - match Self::try_from(pin.shift as u32 + PINMUX_PERIPH_OUTSEL_IDX_OFFSET as u32) { - Ok(outsel) => outsel, - Err(_) => PinmuxOutsel::ConstantHighZ, +impl From for PinmuxOutsel { + fn from(pin: PinmuxPeripheralIn) -> Self { + match pin { + PinmuxPeripheralIn::GpioGpio0 => PinmuxOutsel::GpioGpio0, + PinmuxPeripheralIn::GpioGpio1 => PinmuxOutsel::GpioGpio1, + PinmuxPeripheralIn::GpioGpio2 => PinmuxOutsel::GpioGpio2, + PinmuxPeripheralIn::GpioGpio3 => PinmuxOutsel::GpioGpio3, + PinmuxPeripheralIn::GpioGpio4 => PinmuxOutsel::GpioGpio4, + PinmuxPeripheralIn::GpioGpio5 => PinmuxOutsel::GpioGpio5, + PinmuxPeripheralIn::GpioGpio6 => PinmuxOutsel::GpioGpio6, + PinmuxPeripheralIn::GpioGpio7 => PinmuxOutsel::GpioGpio7, + PinmuxPeripheralIn::GpioGpio8 => PinmuxOutsel::GpioGpio8, + PinmuxPeripheralIn::GpioGpio9 => PinmuxOutsel::GpioGpio9, + PinmuxPeripheralIn::GpioGpio10 => PinmuxOutsel::GpioGpio10, + PinmuxPeripheralIn::GpioGpio11 => PinmuxOutsel::GpioGpio11, + PinmuxPeripheralIn::GpioGpio12 => PinmuxOutsel::GpioGpio12, + PinmuxPeripheralIn::GpioGpio13 => PinmuxOutsel::GpioGpio13, + PinmuxPeripheralIn::GpioGpio14 => PinmuxOutsel::GpioGpio14, + PinmuxPeripheralIn::GpioGpio15 => PinmuxOutsel::GpioGpio15, + PinmuxPeripheralIn::GpioGpio16 => PinmuxOutsel::GpioGpio16, + PinmuxPeripheralIn::GpioGpio17 => PinmuxOutsel::GpioGpio17, + PinmuxPeripheralIn::GpioGpio18 => PinmuxOutsel::GpioGpio18, + PinmuxPeripheralIn::GpioGpio19 => PinmuxOutsel::GpioGpio19, + PinmuxPeripheralIn::GpioGpio20 => PinmuxOutsel::GpioGpio20, + PinmuxPeripheralIn::GpioGpio21 => PinmuxOutsel::GpioGpio21, + PinmuxPeripheralIn::GpioGpio22 => PinmuxOutsel::GpioGpio22, + PinmuxPeripheralIn::GpioGpio23 => PinmuxOutsel::GpioGpio23, + PinmuxPeripheralIn::GpioGpio24 => PinmuxOutsel::GpioGpio24, + PinmuxPeripheralIn::GpioGpio25 => PinmuxOutsel::GpioGpio25, + PinmuxPeripheralIn::GpioGpio26 => PinmuxOutsel::GpioGpio26, + PinmuxPeripheralIn::GpioGpio27 => PinmuxOutsel::GpioGpio27, + PinmuxPeripheralIn::GpioGpio28 => PinmuxOutsel::GpioGpio28, + PinmuxPeripheralIn::GpioGpio29 => PinmuxOutsel::GpioGpio29, + PinmuxPeripheralIn::GpioGpio30 => PinmuxOutsel::GpioGpio30, + PinmuxPeripheralIn::GpioGpio31 => PinmuxOutsel::GpioGpio31, + _ => PinmuxOutsel::ConstantHighZ, } } } // This function use extract GPIO mapping from initial pinmux configurations -pub fn gpio_pad_config(pin: GpioBitfield) -> PadConfig { - let inp: PinmuxPeripheralIn = PinmuxPeripheralIn::from(pin); - match Layout::INPUT[inp as usize] { +pub fn gpio_pad_config(pin: PinmuxPeripheralIn) -> PadConfig { + match Layout::INPUT[pin as usize] { // Current implementation don't support Output only GPIO PinmuxInsel::ConstantZero | PinmuxInsel::ConstantOne => PadConfig::Unconnected, input_selector => { @@ -55,9 +76,9 @@ pub fn gpio_pad_config(pin: GpioBitfield) -> PadCo let out: PinmuxOutsel = Layout::OUTPUT[pad as usize]; // Checking for bi-directional I/O if out == PinmuxOutsel::from(pin) { - PadConfig::InOut(pad, inp, out) + PadConfig::InOut(pad, pin, out) } else { - PadConfig::Input(pad, inp) + PadConfig::Input(pad, pin) } } else { // Upper match checked for unconnected pad so in this @@ -75,38 +96,38 @@ impl<'a> Port<'a> { // Intentionally prevent splitting GpioPin to multiple line #[rustfmt::skip] pins: [ - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin0), pins::pin0), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin1), pins::pin1), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin2), pins::pin2), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin3), pins::pin3), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin4), pins::pin4), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin5), pins::pin5), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin6), pins::pin6), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin7), pins::pin7), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin8), pins::pin8), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin9), pins::pin9), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin10), pins::pin10), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin11), pins::pin11), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin12), pins::pin12), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin13), pins::pin13), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin14), pins::pin14), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin15), pins::pin15), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin16), pins::pin16), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin17), pins::pin17), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin18), pins::pin18), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin19), pins::pin19), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin20), pins::pin20), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin21), pins::pin21), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin22), pins::pin22), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin23), pins::pin23), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin24), pins::pin24), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin25), pins::pin25), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin26), pins::pin26), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin27), pins::pin27), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin28), pins::pin28), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin29), pins::pin29), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin30), pins::pin30), - GpioPin::new(GPIO_BASE, gpio_pad_config::(pins::pin31), pins::pin31), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio0), pins::pin0), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio1), pins::pin1), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio2), pins::pin2), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio3), pins::pin3), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio4), pins::pin4), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio5), pins::pin5), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio6), pins::pin6), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio7), pins::pin7), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio8), pins::pin8), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio9), pins::pin9), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio10), pins::pin10), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio11), pins::pin11), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio12), pins::pin12), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio13), pins::pin13), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio14), pins::pin14), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio15), pins::pin15), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio16), pins::pin16), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio17), pins::pin17), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio18), pins::pin18), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio19), pins::pin19), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio20), pins::pin20), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio21), pins::pin21), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio22), pins::pin22), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio23), pins::pin23), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio24), pins::pin24), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio25), pins::pin25), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio26), pins::pin26), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio27), pins::pin27), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio28), pins::pin28), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio29), pins::pin29), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio30), pins::pin30), + GpioPin::new(GPIO_BASE, gpio_pad_config::(PinmuxPeripheralIn::GpioGpio31), pins::pin31), ], } } diff --git a/chips/stm32f4xx/src/fsmc.rs b/chips/stm32f4xx/src/fsmc.rs index a01ed99530..4664326579 100644 --- a/chips/stm32f4xx/src/fsmc.rs +++ b/chips/stm32f4xx/src/fsmc.rs @@ -5,7 +5,7 @@ use crate::clocks::{phclk, Stm32f4Clocks}; use core::cell::Cell; use kernel::deferred_call::{DeferredCall, DeferredCallClient}; -use kernel::hil::bus8080::{Bus8080, BusWidth, Client}; +use kernel::hil::bus8080::{Bus8080, BusAddr8080, BusWidth, Client}; use kernel::platform::chip::ClockInterface; use kernel::utilities::cells::{OptionalCell, TakeCell}; use kernel::utilities::registers::interfaces::{ReadWriteable, Readable}; @@ -315,9 +315,9 @@ impl ClockInterface for FsmcClock<'_> { } impl Bus8080<'static> for Fsmc<'_> { - fn set_addr(&self, addr_width: BusWidth, addr: usize) -> Result<(), ErrorCode> { - match addr_width { - BusWidth::Bits8 => { + fn set_addr(&self, addr: BusAddr8080) -> Result<(), ErrorCode> { + match addr { + BusAddr8080::BusAddr8(addr) => { self.write_reg(FsmcBanks::Bank1, addr as u16); self.deferred_call.set(); Ok(()) diff --git a/doc/OutOfTree.md b/doc/OutOfTree.md index 78e6ce2e77..139f7fad1a 100644 --- a/doc/OutOfTree.md +++ b/doc/OutOfTree.md @@ -1,8 +1,8 @@ Out of Tree Tock ================ -This guide covers best practices for maintaining subsytems not in the -Tock master repository. +This guide covers best practices for maintaining subsytems not in the main Tock +repository. _It is a work in progress. Comments and pull requests are appreciated!_ @@ -25,13 +25,13 @@ Tock aims to maintain a stable syscall ABI, but does not guarantee stability of kernel interfaces. There are two primary channels to stay abreast of Tock development: - - [tock-dev mailing list](https://groups.google.com/forum/#!forum/tock-dev): - Any major Tock changes will be announced via this list. The list - also support general Tock development, however it is relatively low - traffic (<1 email/day on average). - - [Tock GitHub](https://github.com/tock/tock/): All Tock - changes go through Pull Requests. Non-trivial changes will generally - wait at least one week to merge to allow for feedback. +- [tock-dev mailing list](https://lists.tockos.org/postorius/lists/): Any major + Tock changes will be announced via this list. The list also supports general + Tock development, however, it is relatively low traffic (<1 email/day on + average). +- [Tock GitHub](https://github.com/tock/tock/): All Tock changes go through Pull + Requests. Non-trivial changes will generally wait at least one week to merge + to allow for feedback. Finally, please don't hesitate to [ask for help](https://github.com/tock/tock/#keep-up-to-date). @@ -40,112 +40,135 @@ Finally, please don't hesitate to Structure --------- -Usually it is easiest to keep a -[submodule](https://git-scm.com/docs/git-submodule) of Tock in your -project. - -We then suggest generally mirroring the Tock directory structure, -something like: +We suggest generally mirroring the Tock directory structure within your +repository. This looks something like: $ tree . . ├── boards - │   └── my_board - │   ├── Cargo.toml - │   ├── Makefile - │   ├── layout.ld - │   └── src - │   └── main.rs - ├── my_drivers - │   ├── Cargo.toml - │   └── src - │   ├── my_radio.rs - │   └── my_sensor.rs - └── tock # Where this is a git submodule - │   ├── ... + │ └── my_board + │ ├── Cargo.toml + │ ├── Makefile + │ ├── layout.ld + │ └── src + │ └── main.rs + ├── capsules + │ ├── Cargo.toml + │ └── src + │ ├── my_radio.rs + │ └── my_sensor.rs + +Your code is then in individual Cargo crates. This is important because you can +then use Cargo to include dependencies (including the upstream Tock kernel +crates). Boards ------ -Your board's Makefile will need to set a `PLATFORM` variable, specifying -the name of this platform, and include the primary Tock Makefile. We -also strongly suggest defining `program` and `flash` targets that -specify how the kernel is loaded onto your board. - - ```make - PLATFORM = my_board - - # Include Tock build rules - include ../../tock/boards/Makefile.common - - # Rules for loading via bootloader or other simple, direct connection - program: - ... - - # Rules for loading via JTAG or other external programmer - flash: - ... - ``` - -Your board's Cargo.toml will need to express how to find all the -components that your board uses. Most of these will likely be references -to elements of Tock. - - ```toml - [package] - name = "my_board" - version = "0.1.0" - authors = ["Example Developer "] - build = "../../tock/boards/build.rs" - - [profile.dev] - panic = "abort" - lto = true - opt-level = 0 - debug = true - - [profile.release] - panic = "abort" - lto = true - - [dependencies] - cortexm4 = { path = "../../tock/arch/cortex-m4" } - capsules = { path = "../../tock/capsules" } - sam4l = { path = "../../tock/chips/sam4l" } - kernel = { path = "../../tock/kernel" } - my_drivers = { path = "../../my_drivers" } - ``` - -If using a linker script named `layout.ld`, your board crate can make use of the -Tock submodule's `build.rs` script to ensure it rebuilds when any linker scripts -change. +Boards are likely to start with a copy of an existing Tock board. + +You can build a Tock board using Cargo commands (i.e., `cargo build --release`). +If you prefer to have the option to use Make you will need to copy the +`Makefile.common` makefile from the Tock repository and then include a Makefile +in your board's directory. + +Your board's Makefile will need to include the primary Tock Makefile. We also +strongly suggest defining `program` and `flash` targets that specify how the +kernel is loaded onto your board. + +```make +# Include Tock build rules +include ../Makefile.common + +# Rules for loading via bootloader or other simple, direct connection +program: + ... + +# Rules for loading via JTAG or other external programmer +flash: + ... +``` + +Your board's Cargo.toml will need to express how to find all the components that +your board uses. Most of these will likely be references to elements of Tock. + +```toml +[package] +name = "my_board" +version = "0.1.0" +authors = ["Example Developer "] +build = "build.rs" + +[profile.dev] +panic = "abort" +lto = true +opt-level = "z" +debug = true + +[profile.release] +panic = "abort" +lto = true +opt-level = "z" +debug = true +codegen-units = 1 + +[dependencies] +cortexm4 = { git = "https://github.com/tock/tock", rev = "0c1b63b49" } +capsules = { git = "https://github.com/tock/tock", rev = "0c1b63b49" } +sam4l = { git = "https://github.com/tock/tock", rev = "0c1b63b49" } +kernel = { git = "https://github.com/tock/tock", rev = "0c1b63b49" } +my_drivers = { path = "../../my_drivers" } + +[build-dependencies] +tock_build_scripts = { git = "https://github.com/tock/tock", rev = "0c1b63b49" } +``` + +You will need to create a `build.rs` file that simply calls into the generic +build script provided in the Tock repository: + +```rust +// build.rs +// +fn main() { + tock_build_scripts::default_linker_script(); +} +``` + +You can use the default Tock linker script by creating a `layout.ld` file, +specifying the memory map, and then including the linker script +`tock_kernel_layout.ld`. + +```ld +/* layout.ld */ + +... + +INCLUDE tock_kernel_layout.ld +``` Everything Else --------------- -Custom chips, drivers, or other components should only require a -Cargo.toml. - - ```toml - [package] - name = "my_drivers" - version = "0.1.0" - authors = ["Example Developer "] +Custom chips, drivers, or other components should only require a Cargo.toml. - [dependencies] - kernel = { path = "../tock/kernel" } - ``` +```toml +[package] +name = "my_drivers" +version = "0.1.0" +authors = ["Example Developer "] +[dependencies] +kernel = { git = "https://github.com/tock/tock", rev = "0c1b63b49" } +``` Examples -------- - - Several of the Tock core developers also work on the - [Signpost project](https://github.com/lab11/signpost-software). - The project includes - [seven boards (and growing!)](https://github.com/lab11/signpost-software/tree/master/signpost/kernel/boards) - that run Tock. - - New chips and boards often begin life out of tree. A current effort - is [the STM32 port](https://github.com/tock/tock-stm32). +- Several of the Tock core developers also work on the [Signpost + project](https://github.com/lab11/signpost-software). The project includes + [seven boards](https://github.com/lab11/signpost-software/tree/master/signpost/kernel/boards) + that run Tock. +- New chips and boards often begin life out of tree. A current effort is [the + STM32 port](https://github.com/tock/tock-stm32). diff --git a/doc/syscalls/90009_servo.md b/doc/syscalls/90009_servo.md new file mode 100644 index 0000000000..b1b307d74b --- /dev/null +++ b/doc/syscalls/90009_servo.md @@ -0,0 +1,55 @@ +--- +driver number: 0x90009 +--- + +# servo + +## Overview + +The servo driver provides a simple interface for changing the angle and returning to the app the current angle of a servo motor from userland applications. + +## Command + + * ### Command number: `0` + + **Description**: Does the driver exist? + + **Argument 1**: unused + + **Argument 2**: unused + + **Returns**: Success if it exists + + * ### Command number: `1` + + **Description**: Returns the number of servomotors available. + + **Argument 1**: unused + + **Argument 2**: unused + + **Returns**: A value (u32) representing the number of servomotors available. + + + * ### Command number: `2` + + **Description**: Changes the angle of the servo + + **Argument 1**: receives the index (u16) for the servomotors array from the application + + **Argument 2**: receives the angle (in degrees) from the application + + **Returns**: "Ok" if successful, "Fail" if the angle could not be adjusted, "Inval" if the value provided exceeds 360 degrees, or "NoDevice" if the index exceeds the number of available servomotors. + + * ### Command number: `3` + + **Description**: Returns the current angle of the servo + + **Argument 1**: receives the index (u16) for the servomotors array from the application + + **Argument 2**: unused + + **Returns**: A value (u32) representing the current angle if successful, "NoSupport" if the servo cannot return its angle, or "NoDevice" if the index exceeds the number of available servomotors. + + * ### Any other command: + **Returns**: An error indicating the command is not supported diff --git a/doc/syscalls/README.md b/doc/syscalls/README.md index 964ee5fd8a..e8a7176a48 100644 --- a/doc/syscalls/README.md +++ b/doc/syscalls/README.md @@ -146,3 +146,5 @@ _Note:_ GPIO is slated for re-numbering in Tock 2.0. |2.0| Driver Number | Driver | Description | |---|---------------|-----------------------------------------|--------------------------------------------| | | 0x90000 | Buzzer | Buzzer | +| | 0x90009 | [Servo](90009_servo.md) | | +Servo \ No newline at end of file diff --git a/doc/wg/core/notes/core-notes-2024-10-04.md b/doc/wg/core/notes/core-notes-2024-10-04.md new file mode 100644 index 0000000000..e5790fce4a --- /dev/null +++ b/doc/wg/core/notes/core-notes-2024-10-04.md @@ -0,0 +1,123 @@ +# Tock Meeting Notes 2024-10-04 + +## Attendees +- Branden Ghena +- Lawrence Esswood +- Hudson Ayers +- Leon Schuermann +- Chris Frantz +- Tyler Potyondy +- Johnathan Van Why +- Kat Watson +- Brad Campbell + + +## Updates +### Treadmill + * Leon: Treadmill update! We now have a first step of automated testing of Tock merged. This runs on our own infrastructure on a VM that has access over USB to a real hardware board. We've been fixing some bugs we're seeing as we run. We're also working on scripts for particular tests we want to run. Ben should have a PR for this in the next week or two. + * Leon: One great thing: most people shouldn't have to care about Treadmill, it _should_ just work. What tests we want to run is actually something people will care about though, so I look forward to much discussion on that PR. +### Build scripts PR + * Hudson: I had a chance to look back at the build scripts PR issues, and I think I have the build fixed there + * Brad: Hooray! This includes the linker stuff? + * Hudson: Yup. CI is running now, but I think it's good +### RAM Applications + * Brad: There's been a bunch of discussion lately on running applications directly out of RAM. We should be able to have apps that relocate themselves to RAM, with no modifications to the kernel on today's platforms. Amit is really the one working on this + * Lawrence: What if you're loading from somewhere that's not executable? + * Brad: That's a future platform rather than today's + * Lawrence: A patch I had was to do this once. There was a kernel loader that handled it. + * Leon: It would be good to connect people who are interested and/or working on this. It's also good to spread awareness + + +## Pointer Returns from Syscalls + * Initial email: https://lists.tockos.org/hyperkitty/list/devel@lists.tockos.org/thread/ACZBGBDXYF6UMB64BWXM63MFGKZYCD76/ + * Lawrence: Right now syscalls don't just return values, they also have an enum of what type they send back. Some things are sent back as u32s when they're actually pointers. So something needs to be done for CHERI. + * Lawrence: Some options, we could add new enums, or have a variant on CHERI. We are worried about breaking userland-kernel interface stability. I'm curious what approaches we ought to take. How important is backwards compatibility? + * Lawrence: One option is a Tock 3.0 where we change the success result from Allow, which is currently specified as a u32, but that's not always our pointer size + * Leon: We definitely don't want to break our ABI across minor releases. So, we could have a variant that changes for your new platform but is the same as your old platform + * Lawrence: Right now, I've got a system that's doing that. So return usize would exist, that would then transform into a u32 on existing systems and u64 on CHERI + * Leon: I also think there's a question about why we chose the types that we chose. There was frustration by Phil and I about the lack of knowledge about sign and pointerness for things sent in Tock 1.0 to userspace. So we really wanted to stabilize things. + * Lawrence: Were people using the ABI directly in Tock 1.0 instead of a wrapper like libtock-c? + * Leon: People were using libtock-c, but drivers there were varied in what they did + * Leon: Overall, I do think more semantic information is useful in this interface. So a pointer versus number return result would be valuable + * Lawrence: We'd actually end up with a pointer plus usize for allow returns. I'm a little worried that it would explode into a bunch of new return options + * Johnathan: For a Tock 3.0, we could even have a bitfield that describes the types returned + * Leon: I am not so worried about the explosion, because we can add them lazily as needed + * Lawrence: If they're Rust enums, is there a danger that the kernel translates incorrectly? Does it just transmute, or does it check? + * Leon: It checks. + * Lawrence: And there's an assumption that the user trusts the kernel to return a valid enum + * Johnathan: Is this about the different types the kernel can return? That seems to be expansible in the future? + * Lawrence: Yes. I was just worried that the types of enums in userland and kernel could be out-of-sync + * Johnathan: `libtock-rs` certainly assumes there may be new syscall return variants in the future + * Leon: Is there any dissenting opinion about these options? + * Brad: Not quite a dissent, but I struggle with how we're going to resolve this if we assume we can have pointers anywhere. If a command argument can take a pointer, for instance + * Lawrence: I think the plan would be to not allow that. Those must go through allow + * Lawrence: Two more places the size pops up is command and upcalls. We were originally focused on return types, but all are worth discussion + * Brad: I do think we're all in broad agreement that this should be addressed. But it feels like something more structural here than just choosing types. Maybe I'm not quite right about that or others have an understanding I don't. I think there's a general concept, above and beyond Tock 2.0, and we need to record that wisdom as direction for this change + * Lawrence: To clarify, I think the majority thinks that minor changes can't add new type information to the enum. In terms of how this should progress, when should patches land that introduce new types? + * Leon: For 2.0, we had a branch. We did one last 1.x release slightly before merging 2.0 into master. I am a little worried about leaving this change in a branch where it bit-rots for a while + * Lawrence: Mostly this could be backwards compatible to start, and CHERI could just live in a branch until ready + * Leon: I do also think there are a lot of things going on in your PR. Metaptr and raw pointer and things + * Lawrence: I think I'll be reducing those + * Leon: I don't mean reducing variants, I mean understanding the point of each type for drivers + * Lawrence: For CHERI I can be specific. We do want to be more generic though. For CHERI, at any time, userspace might need to be able to access an object at a location, rather than just sending an address. So an authenticated pointer is bigger than u32, but just an address, like a buffer they already own, is usize. + * Leon: Do you really ever want addresses without access rights? + * Lawrence: Maybe the free interface. Or locations of buffers that applications already own + * Johnathan: About metaptr, does it represent capabilities that are pointers only? Aren't there other types of capabilities? + * Lawrence: I'm also using it for the register file, so any capabilities. + * Johnathan: So it's more than just pointers. Okay. The other thing that confused me, in non-CHERI you're relying on the cast to copy provenance information, correct? + * Lawrence: I wonder if I could rewrite that wrapper layer with strict provenance in mind. + * Johnathan: I used `*mut ()` in libtock-rs for similar things. Maybe I'm thinking too strictly about it. + * Lawrence: Maybe I should change to strict provenance APIs. Would that cause issues across the kernel? + * Johnathan: It's problematic. There is a library that would help, but the authors are not responsive about how we could license it. + * Lawrence: Okay, maybe I'll make a partial step that direction but not a full one + * Lawrence: Right now I'm either making it a usize or a CHERI type, which is wider on CHERI platforms. It should probably be `*const ()` in my Rust code + * Lawrence: A cast to usize is probably still required. This is used for all the different register types you might possibly have, and I'm assuming that the one that holds a pointer is the largest. But just because those conversions exist, doesn't mean it should be usize. + * Johnathan: I think metaptr is what I call register in libtock-rs + * Lawrence: I do think they _could_ differ. Some CHERI systems could have separate register banks, for instance. + * Lawrence: Okay, I think I'm happy with what to do about return types from system calls right now + * Lawrence: We also have to discuss the arguments to command, and the upcalls + * Lawrence: For command, should the command number be u32 or usize? Probably u32 since it's just a number + * Leon: I'm reasonably certain that the TRD104 already says that those numbers are u32s. And if it doesn't, we should + * Lawrence: Should the other two arguments be usize? + * Leon: I thought they should be u32s. But there was really no difference and changing just felt like churn + * Johnathan: Libtock-rs already has them as u32s. + * Leon: I think the same motivation applies for command arguments and return types. Wherever it's not explicitly necessary, the interface of drivers shouldn't change based on the platform you're running on. + * Lawrence: But some things always change, like pointers + * Leon: Yes. And we weren't trying to limit to 32 bits, but have stability across platforms + * Johnathan: Addresses shouldn't go into command anyways, but lengths and offsets can + * Leon: The terminology and semantics are a bit tricky here + * Lawrence: Commands might provide lengths and offsets, which are usize + * Leon: If we only had 32-bit values, we couldn't do that, but we'd at least be consistent + * Johnathan: Do we have a 64-bit platform we care about? + * Lawrence: I do! + * Johnathan: Libtock-rs testing environment on host is also 64-bit + * Leon: My solution here is that we should retain the ability for sub-commands to accept usize arguments. But for a subcommand, we should have the same amount of specificity. We should figure out how to encode this in the command + * Leon: Another PR, the Brad and Amit one on auto-generating system call boilerplate https://github.com/tock/tock/pull/4112 could provide syntax that would have a type associated with them. Then the command wrapper would cast usize down into those types or leave it, depending on the types. + * Lawrence: What that would mean right now, is that those arguments should be usize. And a wrapper would cast correctly. + * Leon: I think we should have documentation about types of sub-driver-numbers. But I agree that our interface should be usize + * Lawrence: The last one is upcalls. Currently it's u32 in the kernel userland. The three generic integer arguments. I propose that they become usize. It's easy for userlands to truncate these to u32 where necessary. I've got a patch for libtock-rs that does it. + * Lawrence: I do really think these upcalls could be sizes. And so they should be in a usize + * Leon: I think things break if they're not usize + * Lawrence: Right. On 32-bit systems it mostly doesn't matter. In Rust it matters. In C it doesn't. In the kernel, people are using upcalls all over the place, and changing it might cause issues all over the kernel. + * Lawrence: I'd like to upstream having the tock kernel use usize for the upcalls. And libtock-rs could update. Libtock-c can stay as-is until it wants 64-bit support + * Leon: Are you planning on using the logic to switch types based on the system? + * Lawrence: The prototype for the upcall would go to usize in kernel and userland + * Leon: I think that seems fine to me. We should probably have a typed enum again, which interprets. Fundamentally conveying usize makes sense + * Leon: For me, the more interesting part is that you're proposing a lot of good and necessary changes. But just implementing and throwing them into a PR is problematic. It makes it harder to think about them more generally and it's hard to think about in terms of 2.0 or 3.0 releases + * Lawrence: I've been talking to Amit personally about it, but we could have more public discussions in Slack or Email + * Johnathan: This is really a refinement of TRD104. So this should be a document first, either updating or superseding TRD104 + * Leon: That would be great. It would be a comprehensible diff of what we're planning. + * Lawrence: One backwards compatible solution, and one not would make sense + * Leon: And if we had the documents, that would be easier for us to review and think about in isolation. Many less lines of code change to think about + * Johnathan: If we're talking about 3.0, there are more changes I'm interested in + * Branden: I will warn everyone how long Tock 2.0 took. Particularly because of those "many changes" + * Leon: Yeah, Tock 3.0 might be years of timeline + * Lawrence: Oh, I'm hoping much faster (end of the year). So maybe avoiding 3.0 would be best + * Leon: I do think the mailing list posts are good for discussion and figuring this out. Then an update to the TRD would be great + * Johnathan: Adding a new return value is not a breaking change, so maybe we're okay there + * Lawrence: It is if I update some old things to use the new interface... But we could leave old stuff alone and only update CHERI to the new method + * Lawrence: We could even have a backwards-compatibility shim in the code to make it clear + * Leon: We can definitely create the perfect new interface, but only apply it to 64-bit platforms. As long as we keep the 32-bit ABI unchanged + * Johnathan: I'm saying slight stronger, I think 32-bit CHERI could be a different ABI from 32-bit non-CHERI. As CHERI is brand new, so we're not breaking anything + * Leon: I think multiple ABIs is acceptable. The only issue is code that starts touching drivers + diff --git a/doc/wg/core/notes/core-notes-2024-10-11.md b/doc/wg/core/notes/core-notes-2024-10-11.md new file mode 100644 index 0000000000..4e1e9817e9 --- /dev/null +++ b/doc/wg/core/notes/core-notes-2024-10-11.md @@ -0,0 +1,88 @@ +# Tock Meeting Notes 2024-10-11 + +## Attendees + +- Branden Ghena +- Amit Levy +- Leon Schuremann +- Benjamin Prevor +- Brad Campbell +- Hudson Ayers +- Kat Watson +- Johnathan Van Why +- Pat Pannuto +- Tyler Potyondy +- Chris Frantz + + +## Updates +### Viewing Treadmill CI Runs + * Leon: How to see Treadmill workflows right now + * Leon: Go to a particular merge commit from a PR. For example: https://github.com/tock/tock/commit/948bfe02936d47e38e5ebe8e2d5ba87a862e3381 + * Leon: At the top of the page to the right of "alevy authored ..." there should be a count of CI checks passed + * Leon: Clicking that will take you to a _full_ list of CI checks. Which includes the Treadmill execute checks + * Leon: You can hit details there for more information +### Alarm Bugs + * Amit: Chasing down alarm bugs in libtock-c and kernel boundary. They are subtle. I think I've finally found the issue, but still working on a correct fix. + * Amit: Between the decision to left-pad values less than 32-bit and the logic for dealing with timers that last longer than the maximum number of ticks, there was some subtlety that wasn't handled correctly + * Amit: I have "fixed it", but I'm worried my fix breaks normal thing + * Amit: One example bug was that while the kernel system call driver rounds up `dt` which is the time from the reference to the alarm, it fails to round down the reference properly. In result you can end up with a total expiration that is still too early. That one was more simple to fix + * Amit: In userspace, the subtle thing is that when we iterate through outstanding alarms, we use the current counter value to do comparisons, rather than when the alarm was scheduled to fire. That is correct because now might proceed as we iterate, but on the other hand if the timer overflows and I had a long timer, all of a sudden this timer appears to not have fired. For example, if the reference is 2, firing is 1, and current time is 3. It looks like it's brand new instead of recently expired. I think we can check for this, but it needs more testing. + * Tyler: What's your use case that's causing you to chase down these bugs? + * Amit: I've been working on the SMA_Q3 smartwatch platform. Literally writing a clock right now with adjustments based on the GPS. I've still not really set really long wrapping timers, but I encountered a different bug where inserting ordering was wrong. Then I was writing more tests and found this too. + * Leon: I thought my tests in the kernel covered every case. Did I miss one? + * Amit: I suspect it was just missed. And I'll add one for it + * Brad: Are those tests on LiteX? + * Leon: No, they're capsule tests + * Brad: And those have smaller sizes? + * Leon: 32, 24, and 64. Came out of the major rewrite where I ripped out the timer logic from the userspace boundary +### Non-Volatile Storage + * https://github.com/tock/tock/pull/4109 + * Brad: PR from UCSD for process-specific non-volatile storage. And that's starting to be important for many things, like OpenSK. Also motivated by Thread. So I've been pushing on that +### Process Debug Data + * https://github.com/tock/tock/pull/4188 + * Brad: Inspired a while ago to see if we could make the memory usage of the process struct smaller. The low-hanging fruit was the debug information, so there's a PR to split that debug info into a trait so some, all, or none could be included. As Amit pointed out, that's another configuration option which gets pretty messy and hard to express. + * Brad: So, now I've been looking into machinery for components to standardize configuring boards. Still iterating on this with new ideas. + * Leon: I have been trying to make it so the core kernel logic doesn't need to care about process-specific debugging info. That created so many issues requiring generic types literally everywhere in the codebase. It's still worth pursuing disentangling process debugging and the core kernel, but this PR is still a great first step. + * Brad: What I've found is that types are tractable as long as everything is for ProcessStandard, but not for other things. That does make different implementations more complicated, but simplifies most of our code +### Nightly Update + * https://github.com/tock/tock/pull/4193 + * Brad: This PR has some static mut issues. Amit says they're "easy to handle" and he'll help. + * Hudson: We could also make a partial jump if we needed, to somewhere less than here + * Leon: The warnings in this nightly are not too bad, but they haven't gotten the point of checking board-tests, where there's a ton of mutable statics still + * Hudson: Notably, I think warnings in tests don't block the build right now + * Leon: Oh, that's on me to get to + + +## Treadmill Updates + * https://github.com/treadmill-tb/tock/pull/2 + * Ben: I have a PR in progress that makes a testing framework for adapting existing Tock tests into Treadmill. It defines a board file and a testing framework file in Python. The Board file tells you how to get the UART and Board for programming. The framework shows the tests to run + * Ben: The board harness has architecture, path in the Tock repo, ability to get UART port, and functions for flashing it + * Ben: The testing framework such as the "c_hello.py" file, has a test, steps to load it on the board such as flashing the kernel and app or apps. Then for oneshot tests, which are the typical ones, they run once and collect info + * Ben: Two types of tests, AnalyzeConsole expects output from serial ports. There's also a WaitForConsoleMessage test which instead just waits for a single message. + * Ben: This can run on real hardware with Treadmill now. In the test-execute for Treadmill, the runner will load things, wait for a message, and then return success/failure + * Leon: The basic idea here is that for any given test we want to combine a specification for interaction with a board with a specification for how the test works. So the board is interaction and the test is the strategy of when to flash things, when to flush buffers, when to expect output, when to use GPIO pins, etc. We can inherit parts of this to abstract away common choices here that many tests use. For example WaitForConsoleMessage is an abstraction that just takes an app and a string. + * Leon: So the goal here was to balance expressiveness and abstracting detail away for simple tasks + * Ben: And if we want the c_hello test, for example, on a different board, we just need a new board file, but the same test framework will apply. Separates tests and board specifics + * Branden: What about tests that rely on certain resources such that only some boards can satisfy it? + * Ben: Tests can require certain attributes from boards in the test + * Leon: Maybe we need a different layer. Right now we have a board and test module combination, but the yaml file just has this fixed right now. When we have a set of boards available with varying hardware resources, we really need to make that decision before scheduling jobs that spawn resources next to hardware. Because these tests are python modules, we could add attributes to them, then in the github actions test-prepare phase, we could match boards and test attributes to come up with the testing plan + * Branden: Yeah, that makes sense. So we'd need that list of attributes, but that can be figured out as we go + * Ben: And the runner can double-check this too + * Chris: This is great. I am impressed and enthusiastic here. I agree with Leon that boards should have tags about the features they support or depend on. We do something similar in OpenTitan, although we aren't yet at the detail of specific features like GPIO blocks or something because we only have the one chip right now. But we do have different kinds of execution platforms like verilator, FPGAs, and silicon chip. Within our test harness, we match platforms with known capabilities to the tests. So at the test harness level, the test can say "I require I2C" and if you try to run a test like that against a platform that doesn't support it, the test immediately errors out. + * Chris: I think you'll get a huge benefit just off of console interactions. Many of our tests just run on the device and print out status with some common pass/fail strings. So all one has to do is write the firmware component of the tests and spit out the right codes. We also have regex matching for more complex tests. We also have another level of tests that require more than just the console, such as a GPIO test that sets up a pin mux and uses both console and GPIO. But we have the capability to use the test framework as a library and write these more complicated things + * Chris: I think what you've described here is a very similar model. And the ability for the host side to interact with IO capabilities will be cool. This is great! + * Leon: We have been informed by OpenTitan and other frameworks. We don't have standard pass/fail strings right now, but we should definitely add that. + * Leon: An example of the tag matching, in treadmill-ci.yml, line 101 has a `--tag-config` which could be an arbitrary combination of tags to find a proper board + * Chris: Cool. I think as you go down this journey of adding tests, I think a few common pass/fail codes will make configuration of the test "harness" (the component that runs on the host and interacts with the device under test). You want that piece to not vary a whole lot for the vast majority of tests. You do still want to let tests emit logging statements in addition to the pass/fail states. That'll be really helpful, and most tests can be self-reporting, without requiring the harness to change. + * Chris: And in my OpenTitan experience, the more complex cases like GPIO or I2C interactions are composed with a UART that exchanges messages between the host and device-under-test. So for example, the host can inform the board that it's going to send an I2C transaction and the device will print back what it received. Then the harness can verify that for correctness or error. + * Chris: If I were to give advice, focus first on the easy things which is just tests reporting status over console. That will be a LOT of value for less work + * Leon: Thanks! That's helpful advice + * Leon: For your info, I have been in contact with James Wainwright at lowrisc, who aren't so interested in all of Treadmill, but are interested in the central scheduler design for use with their own system. So maybe some day we can run the upstream bazel tests on this too. + * Chris: There's CI with a bunch of tests that targets a container, the software build happens outside of the container, then the command to invoke tests goes into the container. James is the authority here on how that works though. I interact with it but haven't developed it. + * Leon: Definitely exciting to see us moving in similar directions + * Ben: For the upstream Tock PR, I really just need some small amount of cleanup. Probably a PR today or in the next few days + * Amit: In other Treadmill updates, I've been using it as a local test platform. I am using Treadmill to test my Tock updates remotely, and apart from some simple ergonomic issues, it's been working great for me. + * Leon: And we're happy to record ergonomic issues to do feedback-driven development + * Leon: We also talked about doing a walk-through for interested users. Probably in a position to do that in the next one or two weeks. + diff --git a/doc/wg/legacy/2018-09-25.md b/doc/wg/legacy/2018-09-25.md new file mode 100644 index 0000000000..4dc598c93e --- /dev/null +++ b/doc/wg/legacy/2018-09-25.md @@ -0,0 +1,35 @@ +Attending: Amit, Brad, Hudson, Louis, Phil + +Updates: 15m + - Milestones + - UDP concurrency bug still live, meanwhile fixes to the UDP userland library + - No progress on architecture + +imix hardware update + - 46 imixs: + - 6 are completely functional + - 2 are functional except sensors + - 1 where sensors and debug don't work + - 22 are fully functional except bluetooth + - 14 basically not functional + +1.2 bug update + - Amit is on it + - Phil suggests: sit down on a video call + - Experience: when "if you have this line of code this works," can help pinpoint what changes about the binary + +SenSys Tutorial: 15m + - "Design Doc" here: https://goo.gl/ZR5z7M + - Assign IPv6 addresses from the unique 128-bit identifier associated with the flash. + - Probably hash 128 bits down to 64, use that to generate 16-bit short MAC address, then use MAC to generate link-local IP address via: https://ben.akrin.com/?p=1347 + - What should the bug be? + - The bug should be in userland + - Maybe one of them just spins on the CPU? + - Capsule could just reports timeslice expiration + +Finalize Rng/Entropy: 15m + - Summary: How do you generate random numbers? + - Used to be asynchronous interface, but not clear what the width should be? + - Design now: two separate groups of traits: + - hil/rng for random numbers, and hil/entroy for sources of entropy. Random numbers are not necessarily entropy. + - Best practices for traits with multiple callbacks (if we get to it): 15m diff --git a/kernel/src/debug.rs b/kernel/src/debug.rs index a7abfa44bf..5dd6169958 100644 --- a/kernel/src/debug.rs +++ b/kernel/src/debug.rs @@ -714,10 +714,6 @@ macro_rules! debug_expr { }; } -pub trait Debug { - fn write(&self, buf: &'static mut [u8], len: usize) -> usize; -} - pub unsafe fn flush(writer: &mut W) { if let Some(debug_writer) = try_get_debug_writer() { if let Some(ring_buffer) = debug_writer.extract() { diff --git a/kernel/src/hil/bus8080.rs b/kernel/src/hil/bus8080.rs index e608f1deb5..6407641157 100644 --- a/kernel/src/hil/bus8080.rs +++ b/kernel/src/hil/bus8080.rs @@ -12,6 +12,18 @@ pub enum BusWidth { Bits16LE, Bits16BE, } +/// The enum represents the address of a bus-attached device. +/// +/// For addresses larger than a single byte the enum variant +/// captures the endianess used by the device on the bus. +/// The address is stored in the host endianess in the u16 and +/// must be converted to the correct endianess before using the +/// address on the bus. +pub enum BusAddr8080 { + BusAddr8(u8), + BusAddr16BE(u16), + BusAddr16LE(u16), +} impl BusWidth { pub fn width_in_bytes(&self) -> usize { @@ -24,8 +36,7 @@ impl BusWidth { pub trait Bus8080<'a> { /// Set the address to write to - fn set_addr(&self, addr_width: BusWidth, addr: usize) -> Result<(), ErrorCode>; - + fn set_addr(&self, addr: BusAddr8080) -> Result<(), ErrorCode>; /// Write data items to the previously set address fn write( &self, diff --git a/kernel/src/hil/mod.rs b/kernel/src/hil/mod.rs index d5e38001e3..df31294456 100644 --- a/kernel/src/hil/mod.rs +++ b/kernel/src/hil/mod.rs @@ -32,6 +32,7 @@ pub mod radio; pub mod rng; pub mod screen; pub mod sensors; +pub mod servo; pub mod spi; pub mod symmetric_encryption; pub mod text_screen; diff --git a/kernel/src/hil/screen.rs b/kernel/src/hil/screen.rs index 9d35f62a47..ba41aec15a 100644 --- a/kernel/src/hil/screen.rs +++ b/kernel/src/hil/screen.rs @@ -98,9 +98,12 @@ impl Sub for ScreenRotation { pub enum ScreenPixelFormat { /// Pixels encoded as 1-bit, used for monochromatic displays. Mono, - /// Pixels encoded as 2-bit red channel, 3-bit green channel, 3-bit blue + /// Pixels encoded as 1-bit blue, 1-bit green, 1-bit red, + /// and 1-bit for opaque (1) vs transparent (0) + RGB_4BIT, + /// Pixels encoded as 3-bit red channel, 3-bit green channel, 2-bit blue /// channel. - RGB_233, + RGB_332, /// Pixels encoded as 5-bit red channel, 6-bit green channel, 5-bit blue /// channel. RGB_565, @@ -117,7 +120,8 @@ impl ScreenPixelFormat { pub fn get_bits_per_pixel(&self) -> usize { match self { Self::Mono => 1, - Self::RGB_233 => 8, + Self::RGB_4BIT => 4, + Self::RGB_332 => 8, Self::RGB_565 => 16, Self::RGB_888 => 24, Self::ARGB_8888 => 32, diff --git a/kernel/src/hil/servo.rs b/kernel/src/hil/servo.rs new file mode 100644 index 0000000000..a8373cd64d --- /dev/null +++ b/kernel/src/hil/servo.rs @@ -0,0 +1,27 @@ +// Licensed under the Apache License, Version 2.0 or the MIT License. +// SPDX-License-Identifier: Apache-2.0 OR MIT +// Copyright Tock Contributors 2024. + +use crate::ErrorCode; +pub trait Servo<'a> { + /// Changes the angle of the servo. + /// Return values: + /// + /// - `Ok(())`: The attempt at changing the angle was successful. + /// - `FAIL`: Cannot change the angle. + /// - `INVAL`: The value exceeds u16, indicating it's incorrect + /// since servomotors can only have a maximum of 360 degrees. + /// - `NODEVICE`: The index exceeds the number of servomotors provided. + /// # Arguments + /// - `angle` - the variable that receives the angle + /// (in degrees from 0 to 180) from the servo driver. + fn set_angle(&self, angle: u16) -> Result<(), ErrorCode>; + + /// Returns the angle of the servo. + /// Return values: + /// + /// - `angle`: The value, in angles from 0 to 360, of the servo. + /// - `NOSUPPORT`: The servo cannot return its angle. + /// - `NODEVICE`: The index exceeds the number of servomotors provided. + fn get_angle(&self) -> Result; +} diff --git a/kernel/src/process.rs b/kernel/src/process.rs index 323112c5fd..ae73535c16 100644 --- a/kernel/src/process.rs +++ b/kernel/src/process.rs @@ -32,6 +32,7 @@ pub use crate::process_loading::{ProcessLoadingAsync, ProcessLoadingAsyncClient} pub use crate::process_policies::{ProcessFaultPolicy, ProcessStandardStoragePermissionsPolicy}; pub use crate::process_printer::{ProcessPrinter, ProcessPrinterContext}; pub use crate::process_standard::ProcessStandard; +pub use crate::process_standard::{ProcessStandardDebug, ProcessStandardDebugFull}; /// Userspace process identifier. /// diff --git a/kernel/src/process_loading.rs b/kernel/src/process_loading.rs index e0250b0a11..aaad25c706 100644 --- a/kernel/src/process_loading.rs +++ b/kernel/src/process_loading.rs @@ -28,6 +28,7 @@ use crate::process_checker::{AppIdPolicy, ProcessCheckError, ProcessCheckerMachi use crate::process_policies::ProcessFaultPolicy; use crate::process_policies::ProcessStandardStoragePermissionsPolicy; use crate::process_standard::ProcessStandard; +use crate::process_standard::{ProcessStandardDebug, ProcessStandardDebugFull}; use crate::utilities::cells::{MapCell, OptionalCell}; /// Errors that can occur when trying to load and create processes. @@ -142,7 +143,7 @@ pub fn load_processes( fault_policy: &'static dyn ProcessFaultPolicy, _capability_management: &dyn ProcessManagementCapability, ) -> Result<(), ProcessLoadError> { - load_processes_from_flash( + load_processes_from_flash::( kernel, chip, app_flash, @@ -183,7 +184,7 @@ pub fn load_processes( /// `ProcessLoadError` if something goes wrong during TBF parsing or process /// creation. #[inline(always)] -fn load_processes_from_flash( +fn load_processes_from_flash( kernel: &'static Kernel, chip: &'static C, app_flash: &'static [u8], @@ -213,7 +214,7 @@ fn load_processes_from_flash( Ok((new_flash, process_binary)) => { remaining_flash = new_flash; - let load_result = load_process( + let load_result = load_process::( kernel, chip, process_binary, @@ -351,7 +352,7 @@ fn discover_process_binary( /// pool that its RAM should be allocated from. Returns `Ok` if the process /// object was created, `Err` with a relevant error if the process object could /// not be created. -fn load_process( +fn load_process( kernel: &'static Kernel, chip: &'static C, process_binary: ProcessBinary, @@ -359,7 +360,7 @@ fn load_process( app_id: ShortId, index: usize, fault_policy: &'static dyn ProcessFaultPolicy, - storage_policy: &'static dyn ProcessStandardStoragePermissionsPolicy, + storage_policy: &'static dyn ProcessStandardStoragePermissionsPolicy, ) -> Result<(&'static mut [u8], Option<&'static dyn Process>), (&'static mut [u8], ProcessLoadError)> { if config::CONFIG.debug_load_processes { @@ -383,7 +384,7 @@ fn load_process( // get a process and we didn't get a loading error (aka we got to // this point), then the app is a disabled process or just padding. let (process_option, unused_memory) = unsafe { - ProcessStandard::create( + ProcessStandard::::create( kernel, chip, process_binary, @@ -465,7 +466,8 @@ enum SequentialProcessLoaderMachineState { /// structures stored in the `procs` array. This machine scans the footers in /// the TBF for cryptographic credentials for binary integrity, passing them to /// the checker to decide whether the process has sufficient credentials to run. -pub struct SequentialProcessLoaderMachine<'a, C: Chip + 'static> { +pub struct SequentialProcessLoaderMachine<'a, C: Chip + 'static, D: ProcessStandardDebug + 'static> +{ /// Client to notify as processes are loaded and process loading finishes. client: OptionalCell<&'a dyn ProcessLoadingAsyncClient>, /// Machine to use to check process credentials. @@ -489,12 +491,12 @@ pub struct SequentialProcessLoaderMachine<'a, C: Chip + 'static> { /// The fault policy to assign to each created Process. fault_policy: &'static dyn ProcessFaultPolicy, /// The storage permissions policy to assign to each created Process. - storage_policy: &'static dyn ProcessStandardStoragePermissionsPolicy, + storage_policy: &'static dyn ProcessStandardStoragePermissionsPolicy, /// Current mode of the loading machine. state: OptionalCell, } -impl<'a, C: Chip> SequentialProcessLoaderMachine<'a, C> { +impl<'a, C: Chip, D: ProcessStandardDebug> SequentialProcessLoaderMachine<'a, C, D> { /// This function is made `pub` so that board files can use it, but loading /// processes from slices of flash an memory is fundamentally unsafe. /// Therefore, we require the `ProcessManagementCapability` to call this @@ -508,7 +510,7 @@ impl<'a, C: Chip> SequentialProcessLoaderMachine<'a, C> { flash: &'static [u8], app_memory: &'static mut [u8], fault_policy: &'static dyn ProcessFaultPolicy, - storage_policy: &'static dyn ProcessStandardStoragePermissionsPolicy, + storage_policy: &'static dyn ProcessStandardStoragePermissionsPolicy, policy: &'static dyn AppIdPolicy, _capability_management: &dyn ProcessManagementCapability, ) -> Self { @@ -867,7 +869,9 @@ impl<'a, C: Chip> SequentialProcessLoaderMachine<'a, C> { } } -impl<'a, C: Chip> ProcessLoadingAsync<'a> for SequentialProcessLoaderMachine<'a, C> { +impl<'a, C: Chip, D: ProcessStandardDebug> ProcessLoadingAsync<'a> + for SequentialProcessLoaderMachine<'a, C, D> +{ fn set_client(&self, client: &'a dyn ProcessLoadingAsyncClient) { self.client.set(client); } @@ -884,7 +888,9 @@ impl<'a, C: Chip> ProcessLoadingAsync<'a> for SequentialProcessLoaderMachine<'a, } } -impl<'a, C: Chip> DeferredCallClient for SequentialProcessLoaderMachine<'a, C> { +impl<'a, C: Chip, D: ProcessStandardDebug> DeferredCallClient + for SequentialProcessLoaderMachine<'a, C, D> +{ fn handle_deferred_call(&self) { // We use deferred calls to start the operation in the async loop. match self.state.get() { @@ -913,8 +919,8 @@ impl<'a, C: Chip> DeferredCallClient for SequentialProcessLoaderMachine<'a, C> { } } -impl<'a, C: Chip> crate::process_checker::ProcessCheckerMachineClient - for SequentialProcessLoaderMachine<'a, C> +impl<'a, C: Chip, D: ProcessStandardDebug> crate::process_checker::ProcessCheckerMachineClient + for SequentialProcessLoaderMachine<'a, C, D> { fn done( &self, diff --git a/kernel/src/process_policies.rs b/kernel/src/process_policies.rs index 24bf365b15..81bf2f1fe2 100644 --- a/kernel/src/process_policies.rs +++ b/kernel/src/process_policies.rs @@ -12,6 +12,7 @@ use crate::platform::chip::Chip; use crate::process; use crate::process::Process; use crate::process_standard::ProcessStandard; +use crate::process_standard::ProcessStandardDebug; use crate::storage_permissions::StoragePermissions; /// Generic trait for implementing a policy on what to do when a process faults. @@ -26,16 +27,16 @@ pub trait ProcessFaultPolicy { /// Generic trait for implementing a policy on how applications should be /// assigned storage permissions. -pub trait ProcessStandardStoragePermissionsPolicy { +pub trait ProcessStandardStoragePermissionsPolicy { /// Return the storage permissions for the specified `process`. - fn get_permissions(&self, process: &ProcessStandard) -> StoragePermissions; + fn get_permissions(&self, process: &ProcessStandard) -> StoragePermissions; } // Any platforms that do not issue storage permissions can use `&()` as the // [`ProcessStandardStoragePermissionsPolicy`]. This will only provide null // permissions (that is, no permission to access persistent storage). -impl ProcessStandardStoragePermissionsPolicy for () { - fn get_permissions(&self, _process: &ProcessStandard) -> StoragePermissions { +impl ProcessStandardStoragePermissionsPolicy for () { + fn get_permissions(&self, _process: &ProcessStandard) -> StoragePermissions { StoragePermissions::new_null() } } diff --git a/kernel/src/process_standard.rs b/kernel/src/process_standard.rs index 146334da0a..666016d954 100644 --- a/kernel/src/process_standard.rs +++ b/kernel/src/process_standard.rs @@ -40,11 +40,110 @@ use crate::utilities::cells::{MapCell, NumericCellExt, OptionalCell}; use tock_tbf::types::CommandPermissions; -/// State for helping with debugging apps. +/// Interface supported by [`ProcessStandard`] for recording debug information. +/// +/// This trait provides flexibility to users of [`ProcessStandard`] to determine +/// how debugging information should be recorded, or if debugging information +/// should be recorded at all. +/// +/// Platforms that want to only maintain certain debugging information can +/// implement only part of this trait. +/// +/// Tock provides a default implementation of this trait on the `()` type. +/// Kernels that wish to use [`ProcessStandard`] but do not need process-level +/// debugging information can use `()` as the `ProcessStandardDebug` type. +pub trait ProcessStandardDebug: Default { + /// Record the address in flash the process expects to start at. + fn set_fixed_address_flash(&self, address: u32); + /// Get the address in flash the process expects to start at, if it was + /// recorded. + fn get_fixed_address_flash(&self) -> Option; + /// Record the address in RAM the process expects to start at. + fn set_fixed_address_ram(&self, address: u32); + /// Get the address in RAM the process expects to start at, if it was + /// recorded. + fn get_fixed_address_ram(&self) -> Option; + /// Record the address where the process placed its heap. + fn set_app_heap_start_pointer(&self, ptr: *const u8); + /// Get the address where the process placed its heap, if it was recorded. + fn get_app_heap_start_pointer(&self) -> Option<*const u8>; + /// Record the address where the process placed its stack. + fn set_app_stack_start_pointer(&self, ptr: *const u8); + /// Get the address where the process placed its stack, if it was recorded. + fn get_app_stack_start_pointer(&self) -> Option<*const u8>; + /// Update the lowest address that the process's stack has reached. + fn set_app_stack_min_pointer(&self, ptr: *const u8); + /// Get the lowest address of the process's stack , if it was recorded. + fn get_app_stack_min_pointer(&self) -> Option<*const u8>; + /// Provide the current address of the bottom of the stack and record the + /// address if it is the lowest address that the process's stack has + /// reached. + fn set_new_app_stack_min_pointer(&self, ptr: *const u8); + + /// Record the most recent system call the process called. + fn set_last_syscall(&self, syscall: Syscall); + /// Get the most recent system call the process called, if it was recorded. + fn get_last_syscall(&self) -> Option; + /// Clear any record of the most recent system call the process called. + fn reset_last_syscall(&self); + + /// Increase the recorded count of the number of system calls the process + /// has called. + fn increment_syscall_count(&self); + /// Get the recorded count of the number of system calls the process has + /// called. + /// + /// This should return 0 if + /// [`ProcessStandardDebug::increment_syscall_count()`] is never called. + fn get_syscall_count(&self) -> usize; + /// Reset the recorded count of the number of system calls called by the app + /// to 0. + fn reset_syscall_count(&self); + + /// Increase the recorded count of the number of upcalls that have been + /// dropped for the process. + fn increment_dropped_upcall_count(&self); + /// Get the recorded count of the number of upcalls that have been dropped + /// for the process. + /// + /// This should return 0 if + /// [`ProcessStandardDebug::increment_dropped_upcall_count()`] is never + /// called. + fn get_dropped_upcall_count(&self) -> usize; + /// Reset the recorded count of the number of upcalls that have been dropped + /// for the process to 0. + fn reset_dropped_upcall_count(&self); + + /// Increase the recorded count of the number of times the process has + /// exceeded its timeslice. + fn increment_timeslice_expiration_count(&self); + /// Get the recorded count of the number times the process has exceeded its + /// timeslice. + /// + /// This should return 0 if + /// [`ProcessStandardDebug::increment_timeslice_expiration_count()`] is + /// never called. + fn get_timeslice_expiration_count(&self) -> usize; + /// Reset the recorded count of the number of the process has exceeded its + /// timeslice to 0. + fn reset_timeslice_expiration_count(&self); +} + +/// A debugging implementation for [`ProcessStandard`] that records the full +/// debugging state. +pub struct ProcessStandardDebugFull { + /// Inner field for the debug state that is in a [`MapCell`] to provide + /// mutable access. + debug: MapCell, +} + +/// Struct for debugging [`ProcessStandard`] processes that records the full set +/// of debugging information. /// /// These pointers and counters are not strictly required for kernel operation, /// but provide helpful information when an app crashes. -struct ProcessStandardDebug { +#[derive(Default)] +struct ProcessStandardDebugFullInner { /// If this process was compiled for fixed addresses, save the address /// it must be at in flash. This is useful for debugging and saves having /// to re-parse the entire TBF header. @@ -81,6 +180,146 @@ struct ProcessStandardDebug { timeslice_expiration_count: usize, } +impl ProcessStandardDebug for ProcessStandardDebugFull { + fn set_fixed_address_flash(&self, address: u32) { + self.debug.map(|d| d.fixed_address_flash = Some(address)); + } + fn get_fixed_address_flash(&self) -> Option { + self.debug.map_or(None, |d| d.fixed_address_flash) + } + fn set_fixed_address_ram(&self, address: u32) { + self.debug.map(|d| d.fixed_address_ram = Some(address)); + } + fn get_fixed_address_ram(&self) -> Option { + self.debug.map_or(None, |d| d.fixed_address_ram) + } + fn set_app_heap_start_pointer(&self, ptr: *const u8) { + self.debug.map(|d| d.app_heap_start_pointer = Some(ptr)); + } + fn get_app_heap_start_pointer(&self) -> Option<*const u8> { + self.debug.map_or(None, |d| d.app_heap_start_pointer) + } + fn set_app_stack_start_pointer(&self, ptr: *const u8) { + self.debug.map(|d| d.app_stack_start_pointer = Some(ptr)); + } + fn get_app_stack_start_pointer(&self) -> Option<*const u8> { + self.debug.map_or(None, |d| d.app_stack_start_pointer) + } + fn set_app_stack_min_pointer(&self, ptr: *const u8) { + self.debug.map(|d| d.app_stack_min_pointer = Some(ptr)); + } + fn get_app_stack_min_pointer(&self) -> Option<*const u8> { + self.debug.map_or(None, |d| d.app_stack_min_pointer) + } + fn set_new_app_stack_min_pointer(&self, ptr: *const u8) { + self.debug.map(|d| { + match d.app_stack_min_pointer { + None => d.app_stack_min_pointer = Some(ptr), + Some(asmp) => { + // Update max stack depth if needed. + if ptr < asmp { + d.app_stack_min_pointer = Some(ptr); + } + } + } + }); + } + + fn set_last_syscall(&self, syscall: Syscall) { + self.debug.map(|d| d.last_syscall = Some(syscall)); + } + fn get_last_syscall(&self) -> Option { + self.debug.map_or(None, |d| d.last_syscall) + } + fn reset_last_syscall(&self) { + self.debug.map(|d| d.last_syscall = None); + } + + fn increment_syscall_count(&self) { + self.debug.map(|d| d.syscall_count += 1); + } + fn get_syscall_count(&self) -> usize { + self.debug.map_or(0, |d| d.syscall_count) + } + fn reset_syscall_count(&self) { + self.debug.map(|d| d.syscall_count = 0); + } + + fn increment_dropped_upcall_count(&self) { + self.debug.map(|d| d.dropped_upcall_count += 1); + } + fn get_dropped_upcall_count(&self) -> usize { + self.debug.map_or(0, |d| d.dropped_upcall_count) + } + fn reset_dropped_upcall_count(&self) { + self.debug.map(|d| d.dropped_upcall_count = 0); + } + + fn increment_timeslice_expiration_count(&self) { + self.debug.map(|d| d.timeslice_expiration_count += 1); + } + fn get_timeslice_expiration_count(&self) -> usize { + self.debug.map_or(0, |d| d.timeslice_expiration_count) + } + fn reset_timeslice_expiration_count(&self) { + self.debug.map(|d| d.timeslice_expiration_count = 0); + } +} + +impl Default for ProcessStandardDebugFull { + fn default() -> Self { + Self { + debug: MapCell::new(ProcessStandardDebugFullInner::default()), + } + } +} + +impl ProcessStandardDebug for () { + fn set_fixed_address_flash(&self, _address: u32) {} + fn get_fixed_address_flash(&self) -> Option { + None + } + fn set_fixed_address_ram(&self, _address: u32) {} + fn get_fixed_address_ram(&self) -> Option { + None + } + fn set_app_heap_start_pointer(&self, _ptr: *const u8) {} + fn get_app_heap_start_pointer(&self) -> Option<*const u8> { + None + } + fn set_app_stack_start_pointer(&self, _ptr: *const u8) {} + fn get_app_stack_start_pointer(&self) -> Option<*const u8> { + None + } + fn set_app_stack_min_pointer(&self, _ptr: *const u8) {} + fn get_app_stack_min_pointer(&self) -> Option<*const u8> { + None + } + fn set_new_app_stack_min_pointer(&self, _ptr: *const u8) {} + + fn set_last_syscall(&self, _syscall: Syscall) {} + fn get_last_syscall(&self) -> Option { + None + } + fn reset_last_syscall(&self) {} + + fn increment_syscall_count(&self) {} + fn get_syscall_count(&self) -> usize { + 0 + } + fn reset_syscall_count(&self) {} + fn increment_dropped_upcall_count(&self) {} + fn get_dropped_upcall_count(&self) -> usize { + 0 + } + fn reset_dropped_upcall_count(&self) {} + fn increment_timeslice_expiration_count(&self) {} + fn get_timeslice_expiration_count(&self) -> usize { + 0 + } + fn reset_timeslice_expiration_count(&self) {} +} + /// Entry that is stored in the grant pointer table at the top of process /// memory. /// @@ -103,7 +342,20 @@ struct GrantPointerEntry { } /// A type for userspace processes in Tock. -pub struct ProcessStandard<'a, C: 'static + Chip> { +/// +/// As its name implies, this is the standard implementation for Tock processes +/// that exposes the full support for processes running on embedded hardware. +/// +/// [`ProcessStandard`] is templated on two parameters: +/// +/// - `C`: [`Chip`]: The implementation must know the [`Chip`] the kernel is +/// running on to properly store architecture-specific and MPU state for the +/// process. +/// - `D`: [`ProcessStandardDebug`]: This configures the debugging mechanism the +/// process uses for storing optional debugging data. Kernels that do not wish +/// to store per-process debugging state can use the `()` type for this +/// parameter. +pub struct ProcessStandard<'a, C: 'static + Chip, D: 'static + ProcessStandardDebug + Default> { /// Identifier of this process and the index of the process in the process /// table. process_id: Cell, @@ -240,10 +492,10 @@ pub struct ProcessStandard<'a, C: 'static + Chip> { completion_code: OptionalCell>, /// Values kept so that we can print useful debug messages when apps fault. - debug: MapCell, + debug: D, } -impl Process for ProcessStandard<'_, C> { +impl Process for ProcessStandard<'_, C, D> { fn processid(&self) -> ProcessId { self.process_id.get() } @@ -288,9 +540,7 @@ impl Process for ProcessStandard<'_, C> { if ret.is_err() { // On any error we were unable to enqueue the task. Record the // error, but importantly do _not_ increment kernel work. - self.debug.map(|debug| { - debug.dropped_upcall_count += 1; - }); + self.debug.increment_dropped_upcall_count(); } ret @@ -509,21 +759,16 @@ impl Process for ProcessStandard<'_, C> { fn update_stack_start_pointer(&self, stack_pointer: *const u8) { if stack_pointer >= self.mem_start() && stack_pointer < self.mem_end() { - self.debug.map(|debug| { - debug.app_stack_start_pointer = Some(stack_pointer); - - // We also reset the minimum stack pointer because whatever - // value we had could be entirely wrong by now. - debug.app_stack_min_pointer = Some(stack_pointer); - }); + self.debug.set_app_stack_start_pointer(stack_pointer); + // We also reset the minimum stack pointer because whatever + // value we had could be entirely wrong by now. + self.debug.set_app_stack_min_pointer(stack_pointer); } } fn update_heap_start_pointer(&self, heap_pointer: *const u8) { if heap_pointer >= self.mem_start() && heap_pointer < self.mem_end() { - self.debug.map(|debug| { - debug.app_heap_start_pointer = Some(heap_pointer); - }); + self.debug.set_app_heap_start_pointer(heap_pointer); } } @@ -1113,49 +1358,35 @@ impl Process for ProcessStandard<'_, C> { // If the UKB implementation passed us a stack pointer, update our // debugging state. This is completely optional. if let Some(sp) = stack_pointer { - self.debug.map(|debug| { - match debug.app_stack_min_pointer { - None => debug.app_stack_min_pointer = Some(sp), - Some(asmp) => { - // Update max stack depth if needed. - if sp < asmp { - debug.app_stack_min_pointer = Some(sp); - } - } - } - }); + self.debug.set_new_app_stack_min_pointer(sp); } switch_reason } fn debug_syscall_count(&self) -> usize { - self.debug.map_or(0, |debug| debug.syscall_count) + self.debug.get_syscall_count() } fn debug_dropped_upcall_count(&self) -> usize { - self.debug.map_or(0, |debug| debug.dropped_upcall_count) + self.debug.get_dropped_upcall_count() } fn debug_timeslice_expiration_count(&self) -> usize { - self.debug - .map_or(0, |debug| debug.timeslice_expiration_count) + self.debug.get_timeslice_expiration_count() } fn debug_timeslice_expired(&self) { - self.debug - .map(|debug| debug.timeslice_expiration_count += 1); + self.debug.increment_timeslice_expiration_count(); } fn debug_syscall_called(&self, last_syscall: Syscall) { - self.debug.map(|debug| { - debug.syscall_count += 1; - debug.last_syscall = Some(last_syscall); - }); + self.debug.increment_syscall_count(); + self.debug.set_last_syscall(last_syscall); } fn debug_syscall_last(&self) -> Option { - self.debug.map_or(None, |debug| debug.last_syscall) + self.debug.get_last_syscall() } fn get_addresses(&self) -> ProcessAddresses { @@ -1170,15 +1401,9 @@ impl Process for ProcessStandard<'_, C> { sram_app_brk: self.app_memory_break() as usize, sram_grant_start: self.kernel_memory_break() as usize, sram_end: self.mem_end() as usize, - sram_heap_start: self.debug.map_or(None, |debug| { - debug.app_heap_start_pointer.map(|p| p as usize) - }), - sram_stack_top: self.debug.map_or(None, |debug| { - debug.app_stack_start_pointer.map(|p| p as usize) - }), - sram_stack_bottom: self.debug.map_or(None, |debug| { - debug.app_stack_min_pointer.map(|p| p as usize) - }), + sram_heap_start: self.debug.get_app_heap_start_pointer().map(|p| p as usize), + sram_stack_top: self.debug.get_app_stack_start_pointer().map(|p| p as usize), + sram_stack_bottom: self.debug.get_app_stack_min_pointer().map(|p| p as usize), } } @@ -1257,31 +1482,29 @@ impl Process for ProcessStandard<'_, C> { // generated for those addresses. If the process was already compiled // for a fixed address, then just generating a .lst file is fine. - self.debug.map(|debug| { - if debug.fixed_address_flash.is_some() { - // Fixed addresses, can just run `make lst`. - let _ = writer.write_fmt(format_args!( - "\ + if self.debug.get_fixed_address_flash().is_some() { + // Fixed addresses, can just run `make lst`. + let _ = writer.write_fmt(format_args!( + "\ \r\nTo debug libtock-c apps, run `make lst` in the app's\ \r\nfolder and open the arch.{:#x}.{:#x}.lst file.\r\n\r\n", - debug.fixed_address_flash.unwrap_or(0), - debug.fixed_address_ram.unwrap_or(0) - )); - } else { - // PIC, need to specify the addresses. - let sram_start = self.mem_start() as usize; - let flash_start = self.flash.as_ptr() as usize; - let flash_init_fn = flash_start + self.header.get_init_function_offset() as usize; + self.debug.get_fixed_address_flash().unwrap_or(0), + self.debug.get_fixed_address_ram().unwrap_or(0) + )); + } else { + // PIC, need to specify the addresses. + let sram_start = self.mem_start() as usize; + let flash_start = self.flash.as_ptr() as usize; + let flash_init_fn = flash_start + self.header.get_init_function_offset() as usize; - let _ = writer.write_fmt(format_args!( - "\ + let _ = writer.write_fmt(format_args!( + "\ \r\nTo debug libtock-c apps, run\ \r\n`make debug RAM_START={:#x} FLASH_INIT={:#x}`\ \r\nin the app's folder and open the .lst file.\r\n\r\n", - sram_start, flash_init_fn - )); - } - }); + sram_start, flash_init_fn + )); + } } fn get_stored_state(&self, out: &mut [u8]) -> Result { @@ -1295,13 +1518,13 @@ impl Process for ProcessStandard<'_, C> { } } -impl ProcessStandard<'_, C> { +impl ProcessStandard<'_, C, D> { // Memory offset for upcall ring buffer (10 element length). const CALLBACK_LEN: usize = 10; const CALLBACKS_OFFSET: usize = mem::size_of::() * Self::CALLBACK_LEN; // Memory offset to make room for this process's metadata. - const PROCESS_STRUCT_OFFSET: usize = mem::size_of::>(); + const PROCESS_STRUCT_OFFSET: usize = mem::size_of::>(); /// Create a `ProcessStandard` object based on the found `ProcessBinary`. pub(crate) unsafe fn create<'a>( @@ -1310,7 +1533,7 @@ impl ProcessStandard<'_, C> { pb: ProcessBinary, remaining_memory: &'a mut [u8], fault_policy: &'static dyn ProcessFaultPolicy, - storage_permissions_policy: &'static dyn ProcessStandardStoragePermissionsPolicy, + storage_permissions_policy: &'static dyn ProcessStandardStoragePermissionsPolicy, app_id: ShortId, index: usize, ) -> Result<(Option<&'static dyn Process>, &'a mut [u8]), (ProcessLoadError, &'a mut [u8])> @@ -1643,8 +1866,8 @@ impl ProcessStandard<'_, C> { // Create the Process struct in the app grant region. // Note that this requires every field be explicitly initialized, as // we are just transforming a pointer into a structure. - let process: &mut ProcessStandard = - &mut *(process_struct_memory_location as *mut ProcessStandard<'static, C>); + let process: &mut ProcessStandard = + &mut *(process_struct_memory_location as *mut ProcessStandard<'static, C, D>); // Ask the kernel for a unique identifier for this process that is being // created. @@ -1691,17 +1914,13 @@ impl ProcessStandard<'_, C> { ]; process.tasks = MapCell::new(tasks); - process.debug = MapCell::new(ProcessStandardDebug { - fixed_address_flash, - fixed_address_ram, - app_heap_start_pointer: None, - app_stack_start_pointer: None, - app_stack_min_pointer: None, - syscall_count: 0, - last_syscall: None, - dropped_upcall_count: 0, - timeslice_expiration_count: 0, - }); + process.debug = D::default(); + if let Some(fix_addr_flash) = fixed_address_flash { + process.debug.set_fixed_address_flash(fix_addr_flash); + } + if let Some(fix_addr_ram) = fixed_address_ram { + process.debug.set_fixed_address_ram(fix_addr_ram); + } // Handle any architecture-specific requirements for a new process. // @@ -1777,12 +1996,10 @@ impl ProcessStandard<'_, C> { .set(ProcessId::new(self.kernel, new_identifier, old_index)); // Reset debug information that is per-execution and not per-process. - self.debug.map(|debug| { - debug.syscall_count = 0; - debug.last_syscall = None; - debug.dropped_upcall_count = 0; - debug.timeslice_expiration_count = 0; - }); + self.debug.reset_last_syscall(); + self.debug.reset_syscall_count(); + self.debug.reset_dropped_upcall_count(); + self.debug.reset_timeslice_expiration_count(); // Reset MPU region configuration. // diff --git a/kernel/src/processbuffer.rs b/kernel/src/processbuffer.rs index 6ad9b00e30..935cf15baa 100644 --- a/kernel/src/processbuffer.rs +++ b/kernel/src/processbuffer.rs @@ -648,6 +648,37 @@ impl Deref for ReadWriteProcessBufferRef<'_> { // type so we alias it as `ReadWriteProcessBuffer`. pub type UserspaceReadableProcessBuffer = ReadWriteProcessBuffer; +/// Equivalent of the Rust core library's +/// [`SliceIndex`](core::slice::SliceIndex) type for process slices. This helper +/// trait is used to abstract over indexing operators into process slices, and +/// is used to "overload" the `.get()` methods such that it can be called with +/// multiple different indexing operators. +/// +/// While we can use the core library's `SliceIndex` trait, parameterized over +/// our own `ProcessSlice` types, this trait includes mandatory methods that are +/// undesirable for the process buffer infrastructure, such as unchecked or +/// mutable index operations. Furthermore, implementing it requires the +/// `slice_index_methods` nightly feature. Thus we vendor our own, small variant +/// of this trait. +pub trait ProcessSliceIndex: private_process_slice_index::Sealed { + type Output: ?Sized; + fn get(self, slice: &PB) -> Option<&Self::Output>; + fn index(self, slice: &PB) -> &Self::Output; +} + +// Analog to `private_slice_index` from +// https://github.com/rust-lang/rust/blob/a1eceec00b2684f947481696ae2322e20d59db60/library/core/src/slice/index.rs#L149 +mod private_process_slice_index { + use core::ops::{Range, RangeFrom, RangeTo}; + + pub trait Sealed {} + + impl Sealed for usize {} + impl Sealed for Range {} + impl Sealed for RangeFrom {} + impl Sealed for RangeTo {} +} + /// Read-only wrapper around a [`Cell`] /// /// This type is used in providing the [`ReadableProcessSlice`]. The @@ -802,73 +833,81 @@ impl ReadableProcessSlice { /// Access a portion of the slice with bounds checking. If the access is not /// within the slice then `None` is returned. - pub fn get(&self, range: Range) -> Option<&ReadableProcessSlice> { - if let Some(slice) = self.slice.get(range) { - Some(cast_byte_slice_to_process_slice(slice)) - } else { - None - } + pub fn get>( + &self, + index: I, + ) -> Option<&>::Output> { + index.get(self) } /// Access a portion of the slice with bounds checking. If the access is not /// within the slice then `None` is returned. + #[deprecated = "Use ReadableProcessSlice::get instead"] pub fn get_from(&self, range: RangeFrom) -> Option<&ReadableProcessSlice> { - if let Some(slice) = self.slice.get(range) { - Some(cast_byte_slice_to_process_slice(slice)) - } else { - None - } + range.get(self) } /// Access a portion of the slice with bounds checking. If the access is not /// within the slice then `None` is returned. + #[deprecated = "Use ReadableProcessSlice::get instead"] pub fn get_to(&self, range: RangeTo) -> Option<&ReadableProcessSlice> { - if let Some(slice) = self.slice.get(range) { - Some(cast_byte_slice_to_process_slice(slice)) - } else { - None - } + range.get(self) } } -impl Index> for ReadableProcessSlice { - // Subslicing will still yield a ReadableProcessSlice reference - type Output = Self; +impl ProcessSliceIndex for usize { + type Output = ReadableProcessByte; + + fn get(self, slice: &ReadableProcessSlice) -> Option<&Self::Output> { + slice.slice.get(self) + } - fn index(&self, idx: Range) -> &Self::Output { - cast_byte_slice_to_process_slice(&self.slice[idx]) + fn index(self, slice: &ReadableProcessSlice) -> &Self::Output { + &slice.slice[self] } } -impl Index> for ReadableProcessSlice { - // Subslicing will still yield a ReadableProcessSlice reference - type Output = Self; +impl ProcessSliceIndex for Range { + type Output = ReadableProcessSlice; + + fn get(self, slice: &ReadableProcessSlice) -> Option<&Self::Output> { + slice.slice.get(self).map(cast_byte_slice_to_process_slice) + } - fn index(&self, idx: RangeTo) -> &Self::Output { - &self[0..idx.end] + fn index(self, slice: &ReadableProcessSlice) -> &Self::Output { + cast_byte_slice_to_process_slice(&slice.slice[self]) } } -impl Index> for ReadableProcessSlice { - // Subslicing will still yield a ReadableProcessSlice reference - type Output = Self; +impl ProcessSliceIndex for RangeFrom { + type Output = ReadableProcessSlice; - fn index(&self, idx: RangeFrom) -> &Self::Output { - &self[idx.start..self.len()] + fn get(self, slice: &ReadableProcessSlice) -> Option<&Self::Output> { + slice.slice.get(self).map(cast_byte_slice_to_process_slice) + } + + fn index(self, slice: &ReadableProcessSlice) -> &Self::Output { + cast_byte_slice_to_process_slice(&slice.slice[self]) } } -impl Index for ReadableProcessSlice { - // Indexing into a ReadableProcessSlice must yield a - // ReadableProcessByte, to limit the API surface of the wrapped - // Cell to read-only operations - type Output = ReadableProcessByte; +impl ProcessSliceIndex for RangeTo { + type Output = ReadableProcessSlice; - fn index(&self, idx: usize) -> &Self::Output { - // As ReadableProcessSlice is a transparent wrapper around its - // inner type, [ReadableProcessByte], we can use the regular - // slicing operator here with its usual semantics. - &self.slice[idx] + fn get(self, slice: &ReadableProcessSlice) -> Option<&Self::Output> { + slice.slice.get(self).map(cast_byte_slice_to_process_slice) + } + + fn index(self, slice: &ReadableProcessSlice) -> &Self::Output { + cast_byte_slice_to_process_slice(&slice.slice[self]) + } +} + +impl> Index for ReadableProcessSlice { + type Output = I::Output; + + fn index(&self, index: I) -> &Self::Output { + index.index(self) } } @@ -1039,71 +1078,80 @@ impl WriteableProcessSlice { /// Access a portion of the slice with bounds checking. If the access is not /// within the slice then `None` is returned. - pub fn get(&self, range: Range) -> Option<&WriteableProcessSlice> { - if let Some(slice) = self.slice.get(range) { - Some(cast_cell_slice_to_process_slice(slice)) - } else { - None - } + pub fn get>( + &self, + index: I, + ) -> Option<&>::Output> { + index.get(self) } /// Access a portion of the slice with bounds checking. If the access is not /// within the slice then `None` is returned. + #[deprecated = "Use WriteableProcessSlice::get instead"] pub fn get_from(&self, range: RangeFrom) -> Option<&WriteableProcessSlice> { - if let Some(slice) = self.slice.get(range) { - Some(cast_cell_slice_to_process_slice(slice)) - } else { - None - } + range.get(self) } /// Access a portion of the slice with bounds checking. If the access is not /// within the slice then `None` is returned. + #[deprecated = "Use WriteableProcessSlice::get instead"] pub fn get_to(&self, range: RangeTo) -> Option<&WriteableProcessSlice> { - if let Some(slice) = self.slice.get(range) { - Some(cast_cell_slice_to_process_slice(slice)) - } else { - None - } + range.get(self) + } +} + +impl ProcessSliceIndex for usize { + type Output = Cell; + + fn get(self, slice: &WriteableProcessSlice) -> Option<&Self::Output> { + slice.slice.get(self) + } + + fn index(self, slice: &WriteableProcessSlice) -> &Self::Output { + &slice.slice[self] } } -impl Index> for WriteableProcessSlice { - // Subslicing will still yield a WriteableProcessSlice reference. - type Output = Self; +impl ProcessSliceIndex for Range { + type Output = WriteableProcessSlice; + + fn get(self, slice: &WriteableProcessSlice) -> Option<&Self::Output> { + slice.slice.get(self).map(cast_cell_slice_to_process_slice) + } - fn index(&self, idx: Range) -> &Self::Output { - cast_cell_slice_to_process_slice(&self.slice[idx]) + fn index(self, slice: &WriteableProcessSlice) -> &Self::Output { + cast_cell_slice_to_process_slice(&slice.slice[self]) } } -impl Index> for WriteableProcessSlice { - // Subslicing will still yield a WriteableProcessSlice reference. - type Output = Self; +impl ProcessSliceIndex for RangeFrom { + type Output = WriteableProcessSlice; + + fn get(self, slice: &WriteableProcessSlice) -> Option<&Self::Output> { + slice.slice.get(self).map(cast_cell_slice_to_process_slice) + } - fn index(&self, idx: RangeTo) -> &Self::Output { - &self[0..idx.end] + fn index(self, slice: &WriteableProcessSlice) -> &Self::Output { + cast_cell_slice_to_process_slice(&slice.slice[self]) } } -impl Index> for WriteableProcessSlice { - // Subslicing will still yield a WriteableProcessSlice reference. - type Output = Self; +impl ProcessSliceIndex for RangeTo { + type Output = WriteableProcessSlice; - fn index(&self, idx: RangeFrom) -> &Self::Output { - &self[idx.start..self.len()] + fn get(self, slice: &WriteableProcessSlice) -> Option<&Self::Output> { + slice.slice.get(self).map(cast_cell_slice_to_process_slice) + } + + fn index(self, slice: &WriteableProcessSlice) -> &Self::Output { + cast_cell_slice_to_process_slice(&slice.slice[self]) } } -impl Index for WriteableProcessSlice { - // Indexing into a WriteableProcessSlice yields a Cell, as - // mutating the memory contents is allowed. - type Output = Cell; +impl> Index for WriteableProcessSlice { + type Output = I::Output; - fn index(&self, idx: usize) -> &Self::Output { - // As WriteableProcessSlice is a transparent wrapper around - // its inner type, [Cell], we can use the regular slicing - // operator here with its usual semantics. - &self.slice[idx] + fn index(&self, index: I) -> &Self::Output { + index.index(self) } } diff --git a/kernel/src/syscall_driver.rs b/kernel/src/syscall_driver.rs index e37f3f239b..e7d81fdeaa 100644 --- a/kernel/src/syscall_driver.rs +++ b/kernel/src/syscall_driver.rs @@ -84,7 +84,7 @@ impl From> for CommandReturn { fn from(rc: Result<(), ErrorCode>) -> Self { match rc { Ok(()) => CommandReturn::success(), - _ => CommandReturn::failure(ErrorCode::try_from(rc).unwrap()), + Err(e) => CommandReturn::failure(e), } } } diff --git a/libraries/tock-tbf/src/types.rs b/libraries/tock-tbf/src/types.rs index c12ac77937..282a2df25e 100644 --- a/libraries/tock-tbf/src/types.rs +++ b/libraries/tock-tbf/src/types.rs @@ -258,6 +258,7 @@ pub enum TbfFooterV2CredentialsType { SHA256 = 3, SHA384 = 4, SHA512 = 5, + EcdsaNistP256 = 6, } #[derive(Clone, Copy, Debug)] @@ -603,6 +604,7 @@ impl core::convert::TryFrom<&'static [u8]> for TbfFooterV2Credentials { 3 => TbfFooterV2CredentialsType::SHA256, 4 => TbfFooterV2CredentialsType::SHA384, 5 => TbfFooterV2CredentialsType::SHA512, + 6 => TbfFooterV2CredentialsType::EcdsaNistP256, _ => { return Err(TbfParseError::BadTlvEntry( TbfHeaderTypes::TbfFooterCredentials as usize, @@ -616,6 +618,7 @@ impl core::convert::TryFrom<&'static [u8]> for TbfFooterV2Credentials { TbfFooterV2CredentialsType::SHA256 => 32, TbfFooterV2CredentialsType::SHA384 => 48, TbfFooterV2CredentialsType::SHA512 => 64, + TbfFooterV2CredentialsType::EcdsaNistP256 => 64, }; let data = &b .get(4..(length + 4)) diff --git a/tools/check_boards_readme.py b/tools/check_boards_readme.py index f42ee16099..82e17a9d8f 100755 --- a/tools/check_boards_readme.py +++ b/tools/check_boards_readme.py @@ -13,6 +13,7 @@ import sys SKIP = [ + "boards/build_scripts", "boards/components", "boards/nordic/nrf52_components", "boards/configurations",