diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..d848a7d1fd --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,5 @@ +{ + "name": "eclipse-s-core", + "image": "ghcr.io/eclipse-score/devcontainer:0.1.0", + "postStartCommand": "ssh-keygen -f '/home/vscode/.ssh/known_hosts' -R '[localhost]:2222' || true" +} diff --git a/.github/workflows/build_and_test_ebclfsa.yml b/.github/workflows/build_and_test_ebclfsa.yml new file mode 100644 index 0000000000..4dcab88585 --- /dev/null +++ b/.github/workflows/build_and_test_ebclfsa.yml @@ -0,0 +1,41 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +name: Build for and Test on EB corbos Linux for Safety Applications + +on: + pull_request: + paths: + - 'ebclfsa/**' + workflow_dispatch: + +jobs: + build: + name: build-and-test-ebclfsa + runs-on: ubuntu-latest + container: + image: ghcr.io/eclipse-score/devcontainer:0.1.0 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build for EB corbos Linux for Safety Applications and run tests + run: bazel build --config=aarch64 --spawn_strategy=local //example/ipc_bridge_hi_wrapper:run_example + working-directory: ./ebclfsa + + - name: Upload test logs + uses: actions/upload-artifact@v5 + with: + name: test-logs + path: ebclfsa/bazel-bin/example/ipc_bridge_hi_wrapper/*.log diff --git a/ebclfsa/.bazelrc b/ebclfsa/.bazelrc new file mode 100644 index 0000000000..4cde06a656 --- /dev/null +++ b/ebclfsa/.bazelrc @@ -0,0 +1,10 @@ +common --@score_baselibs//score/mw/log/detail/flags:KUse_Stub_Implementation_Only=False +common --@score_baselibs//score/mw/log/flags:KRemote_Logging=False +common --@score_baselibs//score/json:base_library=nlohmann +common --@score_communication//score/mw/com/flags:tracing_library=stub + +common --registry=https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/ +common --registry=https://bcr.bazel.build + +common --extra_toolchains=@gcc_toolchain//:aarch64_gcc_13 +build:aarch64 --platforms=@score_toolchains_gcc//platforms:aarch64-linux diff --git a/ebclfsa/BUILD b/ebclfsa/BUILD new file mode 100644 index 0000000000..df35a19965 --- /dev/null +++ b/ebclfsa/BUILD @@ -0,0 +1,13 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 + +# ******************************************************************************* diff --git a/ebclfsa/MODULE.bazel b/ebclfsa/MODULE.bazel new file mode 100644 index 0000000000..4fdc46c3f5 --- /dev/null +++ b/ebclfsa/MODULE.bazel @@ -0,0 +1,82 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +"EB corbos Linux for Safety Applications - S-CORE Reference Integration" + +module( + name = "reference_integration_ebclfsa", + version = "0.1.0", + compatibility_level = 0, +) + +bazel_dep(name = "rules_cc", version = "0.2.0") + +bazel_dep(name = "score_toolchains_gcc", version = "0.0.0", dev_dependency=True) +git_override( + module_name = "score_toolchains_gcc", + remote = "https://github.com/Elektrobit/eclipse-score_toolchains_gcc.git", + commit = "fb009e490b9b8f28805d587f50d0bf6d885f3414", +) +gcc = use_extension("@score_toolchains_gcc//extentions:gcc.bzl", "gcc", dev_dependency=True) +gcc.toolchain( + url = "https://linux.elektrobit.com/tmp/score/fastdev-sdk-ubuntu-ebcl-deb-qemu-arm64.tar.xz", + sha256 = "cf8d277a2b95bbdad3e177c488fa77d01723510690a911218ef33747574d78fe", + strip_prefix = "fastdev-sdk-ubuntu-ebcl-deb-qemuarm64", +) + +# TODO to be moved to toolchain. https://github.com/eclipse-score/toolchains_gcc/issues/11 +gcc.extra_features( + features = [ + "minimal_warnings", + "treat_warnings_as_errors", + ], +) +gcc.warning_flags( + minimal_warnings = ["-Wall", "-Wno-error=deprecated-declarations"], + strict_warnings = ["-Wextra", "-Wpedantic"], + treat_warnings_as_errors = ["-Werror"], +) + +use_repo(gcc, "gcc_toolchain", "gcc_toolchain_gcc") + + +bazel_dep(name = "rules_boost", repo_name = "com_github_nelhage_rules_boost") +archive_override( + module_name = "rules_boost", + urls = ["https://github.com/nelhage/rules_boost/archive/refs/heads/master.tar.gz"], + strip_prefix = "rules_boost-master", +) + +bazel_dep(name = "boost.program_options", version = "1.87.0") + +bazel_dep(name = "score_baselibs") +single_version_override( + module_name = "score_baselibs", + version = "0.1.3", + patch_strip = 1, + patches = [ + "//patches:fix_hard_coded_amd64.patch", + ], +) +bazel_dep(name = "score_communication") +single_version_override( + module_name = "score_communication", + version = "0.1.1", +) + +# git_override are not forwarded by bazel_dep, so we need to redefine it here +git_override( + module_name = "trlc", + remote = "https://github.com/bmw-software-engineering/trlc.git", + commit = "650b51a47264a4f232b3341f473527710fc32669", # trlc-2.0.2 release +) diff --git a/ebclfsa/README.md b/ebclfsa/README.md new file mode 100644 index 0000000000..57b9e91cb5 --- /dev/null +++ b/ebclfsa/README.md @@ -0,0 +1,386 @@ +# Eclipse S-CORE on Elektrobit corbos Linux for Safety Applications + +This directory shows the integration of Eclipse S-CORE on Elektrobit corbos Linux for Safety Applications. +It builds an [example](example/) based on the Eclipse S-CORE communication framework as demo/test application. +This application is then integrated into the so-called "fast-dev" variant of EB corbos Linux for Safety Applications (EBcLfSA). +This is an `aarch64`-based, pre-built image, capable of demonstraing the execution of high integrity applications in regular Linux user-space. +The example can be executed using QEMU. +In the [related CI workflow](../.github/workflows/build_and_test_ebclfsa.yml), all these steps are performed, and the resulting log files are stored and made available for download. + + +> [!TIP] +> **Quick Start** +> +> The fastest way to run the integration interactively is to use [GitHub Codespaces](https://github.com/features/codespaces), a cloud based development environment. +> You need a GitHub.com account for this to work. +There is a free tier of this commercial service, which is sufficient. +However, please understand that we cannot advise you about possible costs in your specific case. +> - Click on the following badge: [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/eclipse-score/reference_integration) +> - In the following dialog, make sure to select "Machine type" as "4-core". +> Click "Create codespace." +> It will take some time (2-3 minutes) for the machine to start. +> There will be a log message "Finished configuring codespace." +> - Hit "Ctrl-Shift-C" to open a new terminal. +> - Copy and paste the following command into the terminal and hit "Enter": +> +> ```bash +> cd ./ebclfsa && bazel build --config=aarch64 --spawn_strategy=local //example/ipc_bridge_hi_wrapper:run_example +> ``` +> +> This will build and run the example. +> There may be a warning about "High codespace CPU (100%) utilization detected.", which you can ignore. +> The complete process will take around 6 minutes to complete on the 4-core machine. +> +> The expected output looks like this: +> +> ```console +> [...] +> Target //example/ipc_bridge_hi_wrapper:run_example up-to-date: +> bazel-bin/example/ipc_bridge_hi_wrapper/qemu_run.log +> bazel-bin/example/ipc_bridge_hi_wrapper/ssh_run.log +>INFO: Elapsed time: 361.273s, Critical Path: 91.92s +>INFO: 836 processes: 10 internal, 826 local. +>INFO: Build completed successfully, 836 total actions +> ``` +> +> The two log files mentioned in this output are the main results of the test execution. +> You can open them by Ctrl-clicking on the output entry. +> +> The remainder of this document describes in detail what you have just accomplished. +> +> In order to close the Codespace again, first take note of the name of the Codespace. +> It is a random combination of and adjective and a noun, mentioned in the bottom left of the browser window. +> Go to your [GitHub Codespaces Dashboard](https://github.com/codespaces), find the Codespace in your list, click on the "..." in that row and select "Delete". +> +> Note that the demo can, of course, als run locally on your computer. +> Clone the repository, open it in Visual Studio Code, start the supplied Development Container, and run the demo as described above. +> This requires a setup that can run [Development Containers](https://containers.dev/) using [Visual Studio Code](https://code.visualstudio.com/). +> The [Visual Studio Code documentation](https://code.visualstudio.com/docs/devcontainers/containers) can be a good starting point; however, an in-depth explanation of this is beyond the goals of this Quick Start. + +## Prebuilt Binary Assets + +The whole setup is open source. +To simplify the deployment and focus on the integration itself, this demo uses pre-built binary assets. +These consist of a pre-built image and a cross-compilation toolchain. +Both assets are referenced in the corresponding Bazel targets and are automatically downloaded on demand. + +The pre-built image provides a so called "fast-dev" integration for EBcLfSA, +which makes development and debugging of high integrity applications easy. +The fast-dev image itself is based on a single aarch64 Linux VM with a specially patched Linux kernel and user-space. +It checks at runtime, whether high integrity applications adhere to certain assumptions of use (AoU) of EBcLfSA. + +Note that this image represents a development image but not a production image of Linux for Safety Application. +It is aimed at demonstrating development with focus on key features and AoUs. + +## Main constraints for high-integrity applications + +For non-safety ("low integrity") applications, Linux for Safety Applications _is_ a standard Linux system. +For applications with safety requirements, also referred to as _high integrity (HI) applications_, +Linux for Safety Applications expects few constraints, especially: + +1. An HI application must be flagged with an additional checksum in its ELF-header to be detected as an high integrity application. +2. An HI application must check its registration status with the supervisor. +3. At least one HI application (e.g. a health manager) must cyclically trigger + the watchdog of Linux for Safety Applications. +4. Few kernel system calls are not allowed to be invoked by an HI application. + - The patches of the "fast-dev" kernel will create a log which helps you to identify them. + - Recommendation: + Build your HI applications and associated libraries on top of the C standard's library definition. + Elektrobit will provide an appropriately qualified version for production projects. +5. Ask for advice when you want to modify/extend kernel functionality or invoke device-specific I/O operations. + +The example application disregards the items 2-5. +They are mandatory for production systems but can be violated during development in the fast-dev environment. +For example, the items 3 and 4 only make sense in a complete system. + +Current restrictions: + +- HI applications must be statically linked. +- PID1 needs to be an HI process. +- HI processes can only be started by another HI process. + +## Application- and Integration-Specific Violation of System Call Restrictions + +The current integration setup is based on a non-safety-certified set of a toolchain and standard libraries. +As a result, an application compiled and linked with the provided example toolchain will generate system call violations. +For the used communication example app this happens during application startup/teardown and is indicated by the occurrence of `ioctl`,`clone3` and `madvice` system calls. + +The full product version intended for production implements process and memory management for high integrity applications according to the C Standard Library. +When using other standard library implementations, `clone3` and `madvice` might be called. +This is ok during development and will not affect you when switching to the safety compliant C Standard Library. +Avoid calling such system calls directly from HI applications, though. +The following table gives an overview why the occurred system calls are not supported and what would be the proposed alternative solution. +Keep in mind, this is only relevant if the system calls are explicitly called by the application code, or libraries other than the provided standard libraries. + +| System Call | Reason | Suggested Alternative or Workaround | +|---------|--------|-------------------------------------| +| `ioctl` | Very flexible function signature, which is hard to "make safe" in a generic way. | Try to avoid direct `ioctl` calls. If direct driver interaction is needed, use alternative kernel standard interfaces like `netlink` or device file IO. A customer specific implementation of a certain function signature might be possible. | +| `clone3` | No part of C standard library. Not needed to create HI processes. | Use C standard library functions to create processes and threads (or system call `clone`). The full product version intended for production will implement them in a way that ensures safe execution. | +| `madvise` | No part of C standard library. All memory of HI Apps is pre-faulted and fully committed at allocation time, hence most kernel optimizations/hints have limited effect. | Use C standard library functions for memory management. The full product version intended for production will implement them in a way that ensures safe execution. | + +## User-Space + +The user-space of the pre-built image consists of three main components: + +- The _trampoline application_, a simplified HI init process +- EBcLfSA example HI and LI applications +- Low integrity system init and user-space + +system_setup drawio + +The system itself is able to run without any Eclipse S-CORE demo applications. +Nevertheless, the trampoline application already provides an entry point for a subsequently deployed application binary. +This entry point is used by the [Eclipse S-CORE Example](#eclipse-s-core-example) application. + +### Trampoline App (cflinit) + +The trampoline application `cflinit` acts as a simplified HI init process which starts the applications as listed above. +This includes the HI application of the EBcLfSA example, as well as a wrapper for the Eclipse S-CORE application binary. +Besides the HI applications, it starts [crinit](https://gitext.elektrobitautomotive.com/EB-Linux/crinit) as a secondary low integrity init daemon, +which brings up the regular (low integrity) Linux user-land. +Once all apps are started, it sleeps forever. + +Below you can see the output generated by `cflinit`. + +```console +cflinit: INFO: Hello from EBcLfSA HI init application +cflinit: INFO: Mounted tmpfs to /dev/shm +cflinit: INFO: Starting application crinit +cflinit: INFO: Starting application HI App +cflinit: ERROR: Could not read /usr/bin/hi_app (2) +cflinit: INFO: Starting application EBcLfSA HI demo +cflinit: INFO: Starting application EBcLfSA HI upper +cflinit: INFO: Finished starting child applications; going to sleep +``` + +As long as the Eclipse S-CORE example is not yet deployed (detected by a missing `/usr/bin/hi_app` binary) the above `ERROR` occurs and `cflinit` skips starting it. + +### EBcLfSA HI Demo + +For technical reasons, the image contains also a secondary demo, with the executables `ebclfsa-hi-demo`, `ebclfsa-hi-upper`, and `ebclfsa-li-demo`. +They demonstrate message passing via a shared memory interface, which does not use Eclipse S-CORE. +Hence, they are not relevant for the demonstration and should be ignored. + +### Low Integrity System Init and User-Space + +As mentioned above, `crinit` is used to set up a low integrity user-land beside the high integrity applications. +This is used primarily for development and user experience by providing components like an SSH server, a login daemon, or `gdbserver`. + +## Eclipse S-CORE Example + +> [!IMPORTANT] +> This guide assumes that you use the SDK's [dev-container](https://github.com/eclipse-score/devcontainer). +> If you are using the Codespace as described in the Quick Start, this is the case. +> The dev-container contains all required dependencies, like `qemu-system-aarch64` and `sshpass`. + +This section shows how you can use the above described SDK with the [example application](example/). +You will see how you can create a low integrity and a high integrity application, build them with the S-CORE toolchain and run them finally on Linux for Safety Applications. + +The first three subsections explain the build and runtime setup. +They help you to understand the integration. +You can apply the approach on other S-CORE apps, too. + +- Application Setup: + The two application setup of the `ipc_bridge` example and how to make one of them an HI application. +- S-CORE Toolchain in Linux for Safety Applications: + The general integration of the required tools into S-CORE's Bazel toolchain. + This should work for other applications, too. +- Bazel Rules for the Example Applications: The specific Bazel ruleset for the `ipc_bridge` example + +The next three sections guide you through the concrete steps of applying these rules +to build and deploy the example. + +- Full Run of the Example Application +- Building the application +- Using the fast-dev image + +And please also look at the shortcuts we implemented in the Visual Studio Code workspace to speed up the usage of the application example. +You find them at the end of this section. + +### Application Setup + +The application setup is constructed of two instances of the `ipc_bridge` application, a high integrity (HI) instance acting as receiver and a low integrity (LI) instance acting as sender. +The HI instance is started automatically by `cflinit` and listens in background. +You start the LI instance manually in a terminal to run the demo. +Even though both instances rely on the same source code, they do not use the same application binary. +The HI application instance uses a binary located at `/usr/bin/ipc_bridge_cpp_sil` with a marked ELF-header, while the unmodified binary for the LI application is located at `/usr/bin/ipc_bridge_cpp`. + +The application instances are called with the following arguments: + +| HI | LI | +| -------- | ------- | +| `-n 10 -m recv -t 200 -s /etc/mw_com_config.json` | `-n 10 -m send -t 200 -s /etc/mw_com_config.json` | + +Feel free to adjust them as needed. + +In order to have those arguments changeable, the HI arguments are not hardcoded into `cflinit`. +Instead, `cflinit` calls a small wrapper binary `/usr/bin/hi_app` which is based on the implementation in `example/ipc_bridge_hi_wrapper/main.cc`. +When `hi_app` is executed by `cflinit`, it simply calls `execve` on `/usr/bin/ipc_bridge_cpp_sil` with the correct set of arguments. +This way `cflinit` keeps its static entrypoint for the Eclipse S-CORE example app, while the user is still able to specify the used arguments for the HI `ipc_bridge` instance. + +### S-CORE Toolchain in Linux for Safety Applications + +The demo SDK integrates the [S-CORE toolchain with two extensions](https://github.com/Elektrobit/eclipse-score_toolchains_gcc/tree/ebclfsa_integration_demo): + +- Additional tooling for AArch64 cross-building. +- Additional tool `lisa-elf-enabler`: It marks an ELF header of an application in a way that Linux for Safety Applications detects it as an HI application. + The tool is available to Bazel via `@gcc_toolchain_gcc//:elf-enabler`. + +### Bazel Rules for the Example Applications + +The [example](example/) extends the original `ipc_brige` example of the [communication module](https://github.com/eclipse-score/communication) with the application setup and the toolchain extensions described above. +With those changes, the toolchain can be used via `bazel build --config=aarch64 --spawn_strategy=local `. + +> [!IMPORTANT] +> Building inside a sandbox is currently not possible. + +For building and running the example setup, the following Bazel rules have been created in `example/ipc_bridge_hi_wrapper/BUILD`: + +| Target | Dependencies | Description | +| ------ | ------------ | ----------- | +| `ipc_bridge_cpp_sil` | `//example/ipc_bridge:ipc_bridge_cpp` | Create copy of `ipc_bridge_cpp` at `ipc_bridge_cpp_sil` and add CRC checksum to ELF-header | +| `ipc_bridge_hi_wrapper` | | Build intermediate `ipc_bridge_hi_wrapper` | +| `hi_app` | `:ipc_bridge_hi_wrapper` `:ipc_bridge_cpp_sil` | Create copy of `ipc_bridge_hi_wrapper` at `hi_app` and add CRC checksum to ELF-header. | +| `fetch-fastdev-archive` | | Download fast-dev image archive | +| `fastdev-image` | `:fetch-fastdev-archive` | Extract fast-dev image archive | +| `upload` | `:hi_app` `:fastdev-image` | Upload application binaries to fast-dev image | +| `run_example` | `:upload`| Run Eclipse S-CORE example application | + +The following sections introduce some of the rules mentioned above. + +### Full Run of the Example Application + +The `run_example` target provides an easy entry point, to build, post-process, deploy, run and stop the example: + +```bash +bazel build --config=aarch64 --spawn_strategy=local //example/ipc_bridge_hi_wrapper:run_example +``` + +This command will take a while to finish, since it performs some downloads and starts the fast-dev image. +After successful execution LI application instance can be seen in the `ssh_run.log`: + +```console +Starting to send data +Sending sample: 0 +Sending sample: 1 +Sending sample: 2 +Sending sample: 3 +Sending sample: 4 +Sending sample: 5 +Sending sample: 6 +Sending sample: 7 +Sending sample: 8 +Sending sample: 9 +Stop offering service...and terminating, bye bye +``` + +`qemu_run.log` provides the serial console output of the started fast-dev image, including the following noteworthy parts: + +1. `cflinit` starting `hi_app` as HI application: + +```console +cflinit: INFO: Starting application HI App +``` + +2. `hi_app` starting `ipc_bridge_cpp_sil` as HI application: + +```console +HI_App: Starting ipc_bridge_cpp_sil +``` + +3. The logs from `ipc_bridge_cpp_sil` as receiver itself + +```console +1970/01/01 00:00:02.2898 29129 000 ECU1 IPBR lola log debug verbose 2 LoLa SD: find service for /tmp/mw_com_lola/service_discovery/6432/1 +... +1970/01/01 00:00:11.11349 113498 000 ECU1 IPBR lola log debug verbose 3 LoLa SD: Synchronous call to handler for FindServiceHandle 0 finished +xpad/cp60/MapApiLanesStamped: Subscribing to service +xpad/cp60/MapApiLanesStamped: Received sample: 2 +xpad/cp60/MapApiLanesStamped: Received sample: 3 +... +xpad/cp60/MapApiLanesStamped: Cycle duration 204ms +1970/01/01 00:00:12.12851 128518 000 ECU1 IPBR lola log debug verbose 3 LoLa SD: Asynchronous call to handler for FindServiceHandle 0 finished +... +``` + +4. Kernel logs indicating that some performed system calls would not be allowed on a production system. +For more information on this, check the [previous section](#application--and-integration-specific-violation-of-system-call-restrictions) on application- and integration-specific syscall violations. + +```console +SDK:handler_do_el0_svc_pre: syscall __NR_clone3 (435) is not allowed +``` + +### Building the Application + +Building all components of the example application can be performed with the `hi_app` rule. + +```bash +bazel build --config=aarch64 --spawn_strategy=local //example/ipc_bridge_hi_wrapper:hi_app +``` + +Due the dependencies towards `:ipc_bridge_cpp_sil` and `:ipc_bridge_hi_wrapper` this will build all required binaries. +Including the LI `ipc_bridge_cpp` binary, a temporary `ipc_bridge_hi_wrapper` binary as well as the post-processed `ipc_bridge_cpp_sil` and `hi_app` binaries. + +### Using the fast-dev Image + +The easiest way to setup the fast-dev image, is to use the `fastdev-image` rule. + +```bash +bazel build --config=aarch64 --spawn_strategy=local //example/ipc_bridge_hi_wrapper:fastdev-image +``` + +This will first download the image via the `fetch-fastdev-archive` rule and cache the archive. +Afterwards, the `fastdev-image` rule extracts the archive (containing a disk image and a kernel) to `bazel-bin/example/ipc_bridge_hi_wrapper/deb-qemuarm64/`. + +To start the unmodified base image (without the Eclipse S-CORE example application) manually, the included `run_qemu.sh` script can be used. + +```bash +./run_qemu.sh bazel-bin/example/ipc_bridge_hi_wrapper/deb-qemuarm64/ +``` + +This is of course optional, and only needed if a deeper manual look into the image is wished. +After the image has started up, the password `linux` can be used for the `root` user for login. +The ssh port of the qemu instance is forwarded to `localhost:2222`. +Therefore `ssh` and `scp` can be used with the same credentials from inside the development container. + +```bash +ssh -p 2222 root@localhost +``` + +> [!NOTE] +> Be aware, that running the image via qemu, will change the stored disk image. +> Bazel will detect this change and overwrite the disk image with the original one from the downloaded archive. +> If it is planned to have persistent changes on the image, copy the content of `bazel-bin/example/ipc_bridge_hi_wrapper/deb-qemuarm64/` to a location outside of `bazel-bin` and adapt the command line argument in the above `./run_qemu.sh` call accordingly. + +For deploying the example application to the image, the `upload` rule is available, which will start the image based on the content of `bazel-bin/example/ipc_bridge_hi_wrapper/deb-qemuarm64/` and deploy all needed files via `scp`. + +```bash +bazel build --config=aarch64 --spawn_strategy=local //example/ipc_bridge_hi_wrapper:upload +``` + +Since the deployment step will change the stored disk image, the `upload` rule stores its output in `bazel-bin/example/ipc_bridge_hi_wrapper/deb-qemuarm64-modified/`. +Running the image with the deployed example applications works the same way as before, just with a different folder for the used image and kernel: + +```bash +./run_qemu.sh bazel-bin/example/ipc_bridge_hi_wrapper/deb-qemuarm64-modified/ +``` + +Like before you can interact with the image via the serial console or ssh. +To trigger the LI Eclipse S-CORE example app, simply call: + +```bash +ipc_bridge_cpp -n 10 -m send -t 200 -s /etc/mw_com_config.json +``` + +To reboot or power-off the running image, `crinit-ctl` with the command line argument `reboot` or `poweroff` can be used. + +```bash +# Reboot +crinit-ctl reboot +# Poweroff +crinit-ctl poweroff +``` + +## Further notes + +The toolchain and librares are provided for demonstration and prototyping purposes without further qualification. diff --git a/ebclfsa/docs/system_setup.drawio.png b/ebclfsa/docs/system_setup.drawio.png new file mode 100644 index 0000000000..f6f4fea62d Binary files /dev/null and b/ebclfsa/docs/system_setup.drawio.png differ diff --git a/ebclfsa/example/ipc_bridge/BUILD b/ebclfsa/example/ipc_bridge/BUILD new file mode 100644 index 0000000000..79b84cc3bc --- /dev/null +++ b/ebclfsa/example/ipc_bridge/BUILD @@ -0,0 +1,65 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") + +cc_binary( + name = "ipc_bridge_cpp", + srcs = [ + "assert_handler.cpp", + "assert_handler.h", + "main.cpp", + ], + data = ["etc/mw_com_config.json"], + features = COMPILER_WARNING_FEATURES, + deps = [ + ":sample_sender_receiver", + "@score_communication//score/mw/com", + "@score_baselibs//score/mw/log", + "@score_baselibs//score/language/futurecpp", + "@boost.program_options", + ], + visibility = [ + "//visibility:public", # platform_only + ], +) + +cc_library( + name = "sample_sender_receiver", + srcs = [ + "sample_sender_receiver.cpp", + ], + hdrs = [ + "sample_sender_receiver.h", + ], + features = COMPILER_WARNING_FEATURES, + deps = [ + ":datatype", + "@score_communication//score/mw/com", + "@score_baselibs//score/mw/log", + ], +) + +cc_library( + name = "datatype", + srcs = [ + "datatype.cpp", + ], + hdrs = [ + "datatype.h", + ], + features = COMPILER_WARNING_FEATURES, + deps = [ + "@score_communication//score/mw/com", + "@score_baselibs//score/language/futurecpp", + ], +) diff --git a/ebclfsa/example/ipc_bridge/assert_handler.cpp b/ebclfsa/example/ipc_bridge/assert_handler.cpp new file mode 100644 index 0000000000..9a46bdae1f --- /dev/null +++ b/ebclfsa/example/ipc_bridge/assert_handler.cpp @@ -0,0 +1,69 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "assert_handler.h" + +#include "score/mw/log/logging.h" + +#include +#include +#include +#include +#include + +namespace score::mw::com +{ + +namespace +{ + +void assert_handler(const score::cpp::handler_parameters& params) +{ + std::cerr << "Assertion \"" << params.condition << "\" failed"; + if (params.message != nullptr) + { + std::cerr << ": " << params.message; + } + std::cerr << " (" << params.file << ':' << params.line << ")" << std::endl; + std::cerr.flush(); + + score::mw::log::LogFatal("AsHa") << params.condition << params.message << params.file << params.line; + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + const char* const no_abort = std::getenv("ASSERT_NO_CORE"); + if (no_abort != nullptr) + { + std::cerr << "Would not coredump on \"" << no_abort << "\"" << std::endl; + if (std::strcmp(no_abort, params.condition) == 0) + { + std::cerr << "... matched." << std::endl; + std::cerr.flush(); + std::quick_exit(1); + } + std::cerr << "... not matched." << std::endl; + } + std::cerr.flush(); +} + +} // namespace + +void SetupAssertHandler() +{ + score::cpp::set_assertion_handler(assert_handler); + // in addition, delay the calls to std::terminate() till the datarouter is able to read the logs + std::set_terminate([]() { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + std::abort(); + }); +} + +} // namespace score::mw::com diff --git a/ebclfsa/example/ipc_bridge/assert_handler.h b/ebclfsa/example/ipc_bridge/assert_handler.h new file mode 100644 index 0000000000..b163843cd2 --- /dev/null +++ b/ebclfsa/example/ipc_bridge/assert_handler.h @@ -0,0 +1,23 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_MW_IPC_BRIDGE_ASSERTHANDLER_H +#define SCORE_MW_IPC_BRIDGE_ASSERTHANDLER_H + +namespace score::mw::com +{ + +void SetupAssertHandler(); + +} // namespace score::mw::com + +#endif // SCORE_MW_IPC_BRIDGE_ASSERTHANDLER_H diff --git a/ebclfsa/example/ipc_bridge/datatype.cpp b/ebclfsa/example/ipc_bridge/datatype.cpp new file mode 100644 index 0000000000..83dcd7d427 --- /dev/null +++ b/ebclfsa/example/ipc_bridge/datatype.cpp @@ -0,0 +1,13 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "datatype.h" diff --git a/ebclfsa/example/ipc_bridge/datatype.h b/ebclfsa/example/ipc_bridge/datatype.h new file mode 100644 index 0000000000..8f2b6c321d --- /dev/null +++ b/ebclfsa/example/ipc_bridge/datatype.h @@ -0,0 +1,305 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_IPC_BRIDGE_DATATYPE_H +#define SCORE_IPC_BRIDGE_DATATYPE_H + +#include "score/mw/com/types.h" + +namespace score::mw::com +{ + +constexpr std::size_t MAX_SUCCESSORS = 16U; +constexpr std::size_t MAX_LANES = 16U; + +enum class StdTimestampSyncState : std::uint32_t +{ + /// @brief Timestamp is in sync with the global master, MAX_DIFF property is guaranteed. + kStdTimestampSyncState_InSync = 0U, + /// @brief Timestamp is not in sync with the global master, no property guarantees can be given, use at your own + /// risk. + kStdTimestampSyncState_NotInSync = 1U, + /// @brief No timestamp is available due to infrastructure reasons (e.g. initial value, or no StbM integrated, or + /// prediction target timestamp cannot be calculated, ...). + kStdTimestampSyncState_Invalid = 255U +}; + +struct StdTimestamp +{ + /// @brief The sub-seconds part of the timestamp + /// + //// unit: [ns] + std::uint32_t fractional_seconds; + /// @brief The seconds part of the timestamp. + /// + /// unit: [s] + std::uint32_t seconds; + /// @brief Status whether the timestamp is in sync with the global master or not. + StdTimestampSyncState sync_status; +}; + +enum class EventDataQualifier : std::uint32_t +{ + /// @brief Event data available, normal operation. + /// + /// The event is valid and all data elements in the scope of the qualifier should be evaluated. Parts of the service + /// may still be in degradation (i.e.contained qualifiers or quality of service attributes (e.g. standard deviation) + /// must be evaluated). + kEventDataQualifier_EventDataAvailable = 0U, + /// @brief Event data available, but a degradation condition applies (e.g. calibration). The reason of the + /// degradation is stored in the parameter extendedQualifier. + /// + /// Parts of the data may still be in degradation. Therefore, the receiver must decide (based on contained + /// qualifiers or quality of service attributes) whether the data can be still used. + kEventDataQualifier_EventDataAvailableReduced = 1U, + /// @brief Data for this event is currently not available. The extendedQualifier (if present) contains information + /// on the reason for non-availability. + /// + /// The remaining information in the scope of the event (except extendedQualifier) must not be evaluated. + kEventDataQualifier_EventDataNotAvailable = 2U, + /// @brief Data for this event is currently unknown. + /// + /// The remaining information in the scope of the event (except extendedQualifier) must not be evaluated. + // kEventDataQualifier_EventDataUnknown = 14U, + /// @brief There is no event data available, due to the event data being invalid (e.g. CRC error) or due to a + /// timeout. + /// + /// The remaining information in the scope of the event (except extendedQualifier) must not be evaluated. + kEventDataQualifier_EventDataInvalid = 255U +}; + +struct MapApiLaneBoundaryData +{ +}; + +using LaneIdType = std::size_t; +using LaneWidth = std::size_t; +using LaneBoundaryId = std::size_t; + +namespace map_api +{ + +using LinkId = std::size_t; +using LengthM = std::double_t; + +struct LaneConnectionInfo +{ +}; + +using LaneConnectionInfoList = std::array; + +struct LaneRestrictionInfo +{ +}; + +using LaneRestrictionInfoList = std::array; + +struct ShoulderLaneInfo +{ +}; + +using ShoulderLaneInfoList = std::array; + +struct LaneToLinkAssociation +{ +}; + +using LaneUsedInBothDirections = bool; + +} // namespace map_api + +namespace adp +{ + +struct MapApiPointData +{ +}; + +enum class LaneType : std::size_t +{ + UNKNOWN, +}; + +enum class LaneTypeNew : std::size_t +{ + Unknown, +}; + +struct TurnDirection +{ +}; + +namespace map_api +{ + +using SpeedLimit = std::size_t; +using LaneFollowsMpp = bool; + +} // namespace map_api + +} // namespace adp + +struct MapApiLaneData +{ + MapApiLaneData() = default; + + MapApiLaneData(MapApiLaneData&&) = default; + + MapApiLaneData(const MapApiLaneData&) = default; + + MapApiLaneData& operator=(MapApiLaneData&&) = default; + + MapApiLaneData& operator=(const MapApiLaneData&) = default; + + /// @brief range: [1, n]. Unique ID of the lane + LaneIdType lane_id{0U}; + + /// @brief range: [1, n]. The IDs of all links that this lane belongs to + std::array link_ids; + + /// @brief The IDs of all lane from which this lane can be reached in longitudinal direction + std::array predecessor_lanes; + + /// @brief The IDs of all lane that can be reached from this lane in longitudinal direction + std::array successor_lanes; + + /// @brief The center line of this lane + std::array center_line; + + /// @brief The innermost left boundary at the beginning of this lane + LaneBoundaryId left_boundary_id{0U}; + + /// @brief The innermost right boundary at the beginning of this lane + LaneBoundaryId right_boundary_id{0U}; + + /// @brief The ID of the lane to the left + /// @note 0 indicates that there is no lane to the left + LaneIdType left_lane_id{0U}; + + /// @brief The id of the lane ro the right + /// @note 0 indicates that there is no lane to the right + LaneIdType right_lane_id{0U}; + + /// @brief The type of the lane + adp::LaneType lane_type{adp::LaneType::UNKNOWN}; + + /// @brief The type of the lane + adp::LaneTypeNew lane_type_new{adp::LaneTypeNew::Unknown}; + + /// @brief Describes Lane Connection Type and the range on the lane for which it applies + map_api::LaneConnectionInfoList lane_connection_info; + + /// @brief Describes Lane Restriction Type and the range on the lane for which it applies + map_api::LaneRestrictionInfoList lane_restriction_info; + + /// @brief Describes Shoulder Lane Type and the range on the lane for which it applies + /// @note not provided by MapDAL as of 6.08.2021r + map_api::ShoulderLaneInfoList shoulder_lane_info; + + /// @brief The turn direction associated with the lane + adp::TurnDirection turn_direction; + /// @brief unit: [cm]. The width of the current lane + /// @details This is the smallest width over the whole lane. When the lane is splitting or + /// merging, the width can be 0. + /// The width is also set to 0 when no width is available. + LaneWidth width_in_cm{0U}; + + /// @brief unit: [m]. The length of the current lane + map_api::LengthM length_in_m{0.0}; + + /// @brief The speed limits on the current lane + std::array speed_limits; + + /// @brief struct describing whether the lane is part of calculated Most Probable Path, or if yes within a range + adp::map_api::LaneFollowsMpp lane_follows_mpp; + + /// @brief Boolean flag describing whether lane is fully attributed + bool is_fully_attributed{false}; + + /// @brief array containing the IDs of all left lane boundaries ordered from curb to middle + std::array left_lane_boundaries_ids; + + /// @brief array containing the IDs of all right lane boundaries ordered from curb to middle + std::array right_lane_boundaries_ids; + + /// @brief links associated with current lane + std::array link_associations; + + /// @brief array of lane ranges where lane can be used in both directions. + std::array used_in_both_directions; +}; + +struct LaneGroupData +{ +}; + +struct MapApiLanesStamped +{ + MapApiLanesStamped() = default; + + MapApiLanesStamped(MapApiLanesStamped&&) = default; + + MapApiLanesStamped(const MapApiLanesStamped&) = default; + + MapApiLanesStamped& operator=(MapApiLanesStamped&&) = default; + + MapApiLanesStamped& operator=(const MapApiLanesStamped&) = default; + + StdTimestamp time_stamp{0, 0, StdTimestampSyncState::kStdTimestampSyncState_Invalid}; + + /// @brief A name of the coordinate frame, used while fetching data. + /// + /// Depending on the driving scenario, different coordinate frames can be used. + /// Case "map_debug" : for Highway scenario it is an NTM planar coordinate system. + /// Case: "local_map_frame": for Urban scenario it is a vehicle's local coordinate system. + std::array frame_id; + + /// @brief Current projection id. + /// + /// In case of NTM geodetic reference system, a zone can be of an arbitrary size, thus doesn't have a fixed + /// descriptor. This variable provides an index of the zone, in which the vehicle is currently located. + /// + /// range: [0, n] + std::uint32_t projection_id{}; + + /// @brief Describes the different kinds of quality levels of interface data. (placeholder for future concrete + /// implementation, for now we just initialize by not available) + EventDataQualifier event_data_qualifier{EventDataQualifier::kEventDataQualifier_EventDataNotAvailable}; + + /// @brief An array, containing lane boundaries, which refer to lanes from the given parent data structure. Lane + /// boundary indicates edge of the lane. + std::array lane_boundaries; + + /// @brief All lanes from HD map for a relevant piece of road. + std::array lanes; + + std::array lane_groups; + + std::uint32_t x; + std::size_t hash_value; +}; + +template +class IpcBridgeInterface : public Trait::Base +{ + public: + using Trait::Base::Base; + + typename Trait::template Event map_api_lanes_stamped_{*this, "map_api_lanes_stamped"}; +}; + +using IpcBridgeProxy = AsProxy; +using IpcBridgeSkeleton = AsSkeleton; + +} // namespace score::mw::com + +#endif // SCORE_IPC_BRIDGE_DATATYPE_H diff --git a/ebclfsa/example/ipc_bridge/etc/logging.json b/ebclfsa/example/ipc_bridge/etc/logging.json new file mode 100644 index 0000000000..ac6589ca35 --- /dev/null +++ b/ebclfsa/example/ipc_bridge/etc/logging.json @@ -0,0 +1,8 @@ +{ + "appId": "IPBR", + "appDesc": "ipc_bridge", + "logLevel": "kDebug", + "logLevelThresholdConsole": "kDebug", + "logMode": "kRemote|kConsole", + "dynamicDatarouterIdentifiers" : true +} diff --git a/ebclfsa/example/ipc_bridge/etc/mw_com_config.json b/ebclfsa/example/ipc_bridge/etc/mw_com_config.json new file mode 100644 index 0000000000..f9a7701535 --- /dev/null +++ b/ebclfsa/example/ipc_bridge/etc/mw_com_config.json @@ -0,0 +1,63 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/bmw/adp/MapApiLanesStamped", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 6432, + "events": [ + { + "eventName": "map_api_lanes_stamped", + "eventId": 1 + }, + { + "eventName": "dummy_data_stamped", + "eventId": 2 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "xpad/cp60/MapApiLanesStamped", + "serviceTypeName": "/bmw/adp/MapApiLanesStamped", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "allowedConsumer": { + "QM": [ + 4002, + 0 + ] + }, + "allowedProvider": { + "QM": [ + 4001, + 0 + ] + }, + "asil-level": "QM", + "binding": "SHM", + "events": [ + { + "eventName": "map_api_lanes_stamped", + "numberOfSampleSlots": 10, + "maxSubscribers": 3 + } + ] + } + ] + } + ] +} diff --git a/ebclfsa/example/ipc_bridge/main.cpp b/ebclfsa/example/ipc_bridge/main.cpp new file mode 100644 index 0000000000..ca3ea37d16 --- /dev/null +++ b/ebclfsa/example/ipc_bridge/main.cpp @@ -0,0 +1,131 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "assert_handler.h" +#include "sample_sender_receiver.h" + +#include + +#include +#include + +#include "score/mw/com/types.h" +#include "score/mw/com/runtime.h" + +using namespace std::chrono_literals; + +struct Params +{ + score::cpp::optional mode; + score::cpp::optional instance_manifest; + score::cpp::optional cycle_time; + score::cpp::optional cycle_num; + bool check_sample_hash; +}; + +template +score::cpp::optional GetValueIfProvided(const boost::program_options::variables_map& args, std::string arg_string) +{ + return (args.count(arg_string) > 0U) ? static_cast(args[arg_string].as()) + : score::cpp::optional(); +} + +Params ParseCommandLineArguments(const int argc, const char** argv) +{ + namespace po = boost::program_options; + + po::options_description options; + + options.add_options()("help,h", "Display the help message"); + options.add_options()( + "num-cycles,n", + po::value()->default_value(0U), + "Number of cycles that are executed before determining success or failure. 0 indicates no limit."); + options.add_options()("mode,m", + po::value(), + "Set to either send/skeleton or recv/proxy to determine the role of the process"); + options.add_options()("cycle-time,t", po::value(), "Cycle time in milliseconds for sending/polling"); + options.add_options()( + "service_instance_manifest,s", po::value(), "Path to the com configuration file"); + options.add_options()( + "disable-hash-check,d", + po::bool_switch(), + "Do not check the sample hash value in the receiver. If true, the sample hash is not checked."); + + po::variables_map args; + const auto parsed_args = + po::command_line_parser{argc, argv} + .options(options) + .style(po::command_line_style::unix_style | po::command_line_style::allow_long_disguise) + .run(); + po::store(parsed_args, args); + + if (args.count("help") > 0U) + { + std::cerr << options << std::endl; + throw std::runtime_error("Could not parse command line arguments"); + } + + return {GetValueIfProvided(args, "mode"), + GetValueIfProvided(args, "service_instance_manifest"), + GetValueIfProvided(args, "cycle-time"), + GetValueIfProvided(args, "num-cycles"), + args.count("disable-hash-check") == 0U}; +} + +int main(const int argc, const char** argv) +{ + score::mw::com::SetupAssertHandler(); + Params params = ParseCommandLineArguments(argc, argv); + + if (!params.mode.has_value() || !params.cycle_num.has_value() || !params.cycle_time.has_value()) + { + std::cerr << "Mode, number of cycles and cycle time should be specified" << std::endl; + return EXIT_FAILURE; + } + + if (params.instance_manifest.has_value()) + { + const std::string& manifest_path = params.instance_manifest.value(); + score::StringLiteral runtime_args[2u] = {"-service_instance_manifest", manifest_path.c_str()}; + score::mw::com::runtime::InitializeRuntime(2, runtime_args); + } + + const auto mode = params.mode.value(); + const auto cycles = params.cycle_num.value(); + const auto cycle_time = params.cycle_time.value(); + const auto check_sample_hash = params.check_sample_hash; + + score::mw::com::EventSenderReceiver event_sender_receiver{}; + + const auto instance_specifier_result = score::mw::com::InstanceSpecifier::Create("xpad/cp60/MapApiLanesStamped"); + if (!instance_specifier_result.has_value()) + { + std::cerr << "Invalid instance specifier, terminating." << std::endl; + return EXIT_FAILURE; + } + const auto& instance_specifier = instance_specifier_result.value(); + + if (mode == "send" || mode == "skeleton") + { + return event_sender_receiver.RunAsSkeleton(instance_specifier, cycle_time, cycles); + } + else if (mode == "recv" || mode == "proxy") + { + return event_sender_receiver.RunAsProxy(instance_specifier, cycle_time, cycles, false, check_sample_hash); + } + else + { + std::cerr << "Unknown mode " << mode << ", terminating." << std::endl; + return EXIT_FAILURE; + } +} diff --git a/ebclfsa/example/ipc_bridge/sample_sender_receiver.cpp b/ebclfsa/example/ipc_bridge/sample_sender_receiver.cpp new file mode 100644 index 0000000000..db24972df2 --- /dev/null +++ b/ebclfsa/example/ipc_bridge/sample_sender_receiver.cpp @@ -0,0 +1,463 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "sample_sender_receiver.h" +#include "score/mw/com/impl/generic_proxy.h" +#include "score/mw/com/impl/generic_proxy_event.h" +#include "score/mw/com/impl/handle_type.h" + +#include "score/concurrency/notification.h" + +#include "score/mw/com/impl/proxy_event.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +namespace score::mw::com +{ + +namespace +{ + +constexpr std::size_t START_HASH = 64738U; + +std::ostream& operator<<(std::ostream& stream, const InstanceSpecifier& instance_specifier) +{ + stream << instance_specifier.ToString(); + return stream; +} + +template +void ToStringImpl(std::ostream& o, T t) +{ + o << t; +} + +template +void ToStringImpl(std::ostream& o, T t, Args... args) +{ + ToStringImpl(o, t); + ToStringImpl(o, args...); +} + +template +std::string ToString(Args... args) +{ + std::ostringstream oss; + ToStringImpl(oss, args...); + return oss.str(); +} + +void HashArray(const std::array& array, std::size_t& seed) +{ + const std::ptrdiff_t buffer_size = + reinterpret_cast(&*array.cend()) - reinterpret_cast(&*array.cbegin()); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(buffer_size > 0); + seed = score::cpp::hash_bytes_fnv1a(static_cast(array.data()), static_cast(buffer_size), seed); +} + +class SampleReceiver +{ + public: + explicit SampleReceiver(const InstanceSpecifier& instance_specifier, bool check_sample_hash = true) + : instance_specifier_{instance_specifier}, + last_received_{}, + received_{0U}, + check_sample_hash_{check_sample_hash} + { + } + + void ReceiveSample(const MapApiLanesStamped& map) noexcept + { + std::cout << ToString(instance_specifier_, ": Received sample: ", map.x, "\n"); + + if (CheckReceivedSample(map)) + { + received_ += 1U; + } + last_received_ = map.x; + } + + std::size_t GetReceivedSampleCount() const noexcept + { + return received_; + } + + private: + bool CheckReceivedSample(const MapApiLanesStamped& map) const noexcept + { + if (last_received_.has_value()) + { + if (map.x <= last_received_.value()) + { + std::cerr << ToString(instance_specifier_, + ": The received sample is out of order. Expected that ", + map.x, + " > ", + last_received_.value(), + "\n"); + return false; + } + } + + if (check_sample_hash_) + { + std::size_t hash_value = START_HASH; + for (const MapApiLaneData& lane : map.lanes) + { + HashArray(lane.successor_lanes, hash_value); + } + + if (hash_value != map.hash_value) + { + std::cerr << ToString(instance_specifier_, + ": Unexpected data received, hash comparison failed: ", + hash_value, + ", expected ", + map.hash_value, + "\n"); + return false; + } + } + + return true; + } + + const score::mw::com::InstanceSpecifier& instance_specifier_; + score::cpp::optional last_received_; + std::size_t received_; + bool check_sample_hash_; +}; + +score::cpp::optional>> GetMapApiLanesStampedProxyEvent( + IpcBridgeProxy& proxy) +{ + return proxy.map_api_lanes_stamped_; +} + +score::cpp::optional> GetMapApiLanesStampedProxyEvent( + GenericProxy& generic_proxy) +{ + const std::string event_name{"map_api_lanes_stamped"}; + auto event_it = generic_proxy.GetEvents().find(event_name); + if (event_it == generic_proxy.GetEvents().cend()) + { + std::cerr << "Could not find event " << event_name << " in generic proxy event map\n"; + return {}; + } + return event_it->second; +} + +/// \brief Function that returns the value pointed to by a pointer +const MapApiLanesStamped& GetSamplePtrValue(const MapApiLanesStamped* const sample_ptr) +{ + return *sample_ptr; +} + +/// \brief Function that casts and returns the value pointed to by a void pointer +/// +/// Assumes that the object in memory being pointed to is of type MapApiLanesStamped. +const MapApiLanesStamped& GetSamplePtrValue(const void* const void_ptr) +{ + auto* const typed_ptr = static_cast(void_ptr); + return *typed_ptr; +} + +/// \brief Function that extracts the underlying pointer to const from a SamplePtr and casts away the const. Only used +/// in death test to check that we can't modify the SamplePtr! +template +SampleType* ExtractNonConstPointer(const SamplePtr& sample) noexcept +{ + const SampleType* sample_const_ptr = sample.get(); + + // The underlying shared memory in which the SamplePtr is stored (i.e. the data section) is opened read-only by the + // operating system when we open and mmap the memory into our consumer process. However, the SampleType itself is + // not a const object (although the SamplePtr holds a pointer to const). The standard states that "Modifying a const + // object through a non-const access path and referring to a volatile object through a non-volatile glvalue results + // in undefined behavior." (https://en.cppreference.com/w/cpp/language/const_cast). We are _not_ modifying a const + // object. We are modifying a non-const object that is pointer to by a pointer to const. Therefore, modifying the + // underlying object after using const cast is not undefined behaviour. We expect that the failure should occur + // since the memory in which the object is allocated is in read-only memory. + auto* sample_non_const_ptr = const_cast(sample_const_ptr); + return sample_non_const_ptr; +} + +void ModifySampleValue(const SamplePtr& sample) +{ + auto* const sample_non_const_ptr = ExtractNonConstPointer(sample); + sample_non_const_ptr->x += 1; +} + +void ModifySampleValue(const SamplePtr& sample) +{ + auto* const sample_non_const_ptr = ExtractNonConstPointer(sample); + + auto* const typed_ptr = static_cast(sample_non_const_ptr); + typed_ptr->x += 1; +} + +template +score::Result GetHandleFromSpecifier(const InstanceSpecifier& instance_specifier) +{ + std::cout << ToString(instance_specifier, ": Running as proxy, looking for services\n"); + ServiceHandleContainer handles{}; + do + { + auto handles_result = ProxyType::FindService(instance_specifier); + if (!handles_result.has_value()) + { + return MakeUnexpected(std::move(handles_result.error())); + } + handles = std::move(handles_result).value(); + if (handles.size() == 0) + { + std::this_thread::sleep_for(500ms); + } + } while (handles.size() == 0); + + std::cout << ToString(instance_specifier, ": Found service, instantiating proxy\n"); + return handles.front(); +} + +Result> PrepareMapLaneSample(IpcBridgeSkeleton& skeleton, + const std::size_t cycle) +{ + const std::default_random_engine::result_type seed{static_cast( + std::chrono::steady_clock::now().time_since_epoch().count())}; + std::default_random_engine rng{seed}; + + auto sample_result = skeleton.map_api_lanes_stamped_.Allocate(); + if (!sample_result.has_value()) + { + return sample_result; + } + auto sample = std::move(sample_result).value(); + sample->hash_value = START_HASH; + sample->x = static_cast(cycle); + + std::cout << ToString("Sending sample: ", sample->x, "\n"); + for (MapApiLaneData& lane : sample->lanes) + { + for (LaneIdType& successor : lane.successor_lanes) + { + successor = std::uniform_int_distribution()(rng); + } + + HashArray(lane.successor_lanes, sample->hash_value); + } + return sample; +} + +} // namespace + +template +int EventSenderReceiver::RunAsProxy(const score::mw::com::InstanceSpecifier& instance_specifier, + const score::cpp::optional cycle_time, + const std::size_t num_cycles, + bool try_writing_to_data_segment, + bool check_sample_hash) +{ + // For a GenericProxy, the SampleType will be void. For a regular proxy, it will by MapApiLanesStamped. + using SampleType = + typename std::conditional::value, void, MapApiLanesStamped>::type; + constexpr std::size_t SAMPLES_PER_CYCLE = 2U; + + auto handle_result = GetHandleFromSpecifier(instance_specifier); + if (!handle_result.has_value()) + { + std::cerr << "Unable to find service: " << instance_specifier + << ". Failed with error: " << handle_result.error() << ", bailing!\n"; + return EXIT_FAILURE; + } + auto handle = handle_result.value(); + + auto proxy_result = ProxyType::Create(std::move(handle)); + if (!proxy_result.has_value()) + { + std::cerr << "Unable to construct proxy: " << proxy_result.error() << ", bailing!\n"; + return EXIT_FAILURE; + } + auto& proxy = proxy_result.value(); + + auto map_api_lanes_stamped_event_optional = GetMapApiLanesStampedProxyEvent(proxy); + if (!map_api_lanes_stamped_event_optional.has_value()) + { + std::cerr << "Could not get MapApiLanesStamped proxy event\n"; + return EXIT_FAILURE; + } + auto& map_api_lanes_stamped_event = map_api_lanes_stamped_event_optional.value().get(); + + concurrency::Notification event_received; + if (!cycle_time.has_value()) + { + map_api_lanes_stamped_event.SetReceiveHandler([&event_received, &instance_specifier]() { + std::cout << ToString(instance_specifier, ": Callback called\n"); + event_received.notify(); + }); + } + + std::cout << ToString(instance_specifier, ": Subscribing to service\n"); + map_api_lanes_stamped_event.Subscribe(SAMPLES_PER_CYCLE); + + score::cpp::optional last_received{}; + SampleReceiver receiver{instance_specifier, check_sample_hash}; + for (std::size_t cycle = 0U; cycle < num_cycles;) + { + const auto cycle_start_time = std::chrono::steady_clock::now(); + if (cycle_time.has_value()) + { + std::this_thread::sleep_for(*cycle_time); + } + + const auto received_before = receiver.GetReceivedSampleCount(); + Result num_samples_received = map_api_lanes_stamped_event.GetNewSamples( + [&receiver, try_writing_to_data_segment](SamplePtr sample) noexcept { + if (try_writing_to_data_segment) + { + // Try writing to the data segment (in which the sample data is stored). Used in a death test to + // ensure that this is not possible. + ModifySampleValue(sample); + } + + // For the GenericProxy case, the void pointer managed by the SamplePtr will be cast to + // MapApiLanesStamped. + const MapApiLanesStamped& sample_value = GetSamplePtrValue(sample.get()); + receiver.ReceiveSample(sample_value); + }, + SAMPLES_PER_CYCLE); + const auto received = receiver.GetReceivedSampleCount() - received_before; + + const bool get_new_samples_api_error = !num_samples_received.has_value(); + const bool mismatch_api_returned_receive_count_vs_sample_callbacks = *num_samples_received != received; + const bool receive_handler_called_without_new_samples = *num_samples_received == 0 && !cycle_time.has_value(); + + if (get_new_samples_api_error || mismatch_api_returned_receive_count_vs_sample_callbacks || + receive_handler_called_without_new_samples) + { + std::stringstream ss; + ss << instance_specifier << ": Error in cycle " << cycle << " during sample reception: "; + if (!get_new_samples_api_error) + { + if (mismatch_api_returned_receive_count_vs_sample_callbacks) + { + ss << "number of received samples doesn't match to what IPC claims: " << *num_samples_received + << " vs " << received; + } + else + { + ss << "expected at least one new sample, since event-notifier has been called, but " + "GetNewSamples() didn't provide one! "; + } + } + else + { + ss << std::move(num_samples_received).error(); + } + ss << ", terminating.\n"; + std::cerr << ss.str(); + + map_api_lanes_stamped_event.Unsubscribe(); + return EXIT_FAILURE; + } + + if (*num_samples_received >= 1U) + { + std::cout << ToString(instance_specifier, ": Proxy received valid data\n"); + cycle += *num_samples_received; + } + + const auto cycle_duration = std::chrono::steady_clock::now() - cycle_start_time; + + std::cout << ToString(instance_specifier, + ": Cycle duration ", + std::chrono::duration_cast(cycle_duration).count(), + "ms\n"); + + event_received.reset(); + } + + std::cout << ToString(instance_specifier, ": Unsubscribing...\n"); + map_api_lanes_stamped_event.Unsubscribe(); + std::cout << ToString(instance_specifier, ": and terminating, bye bye\n"); + return EXIT_SUCCESS; +} + +int EventSenderReceiver::RunAsSkeleton(const score::mw::com::InstanceSpecifier& instance_specifier, + const std::chrono::milliseconds cycle_time, + const std::size_t num_cycles) +{ + auto create_result = IpcBridgeSkeleton::Create(instance_specifier); + if (!create_result.has_value()) + { + std::cerr << "Unable to construct skeleton: " << create_result.error() << ", bailing!\n"; + return EXIT_FAILURE; + } + auto& skeleton = create_result.value(); + + const auto offer_result = skeleton.OfferService(); + if (!offer_result.has_value()) + { + std::cerr << "Unable to offer service for skeleton: " << offer_result.error() << ", bailing!\n"; + return EXIT_FAILURE; + } + std::cout << "Starting to send data\n"; + + for (std::size_t cycle = 0U; cycle < num_cycles || num_cycles == 0U; ++cycle) + { + auto sample_result = PrepareMapLaneSample(skeleton, cycle); + if (!sample_result.has_value()) + { + std::cerr << "No sample received. Exiting.\n"; + return EXIT_FAILURE; + } + auto sample = std::move(sample_result).value(); + + { + std::lock_guard lock{event_sending_mutex_}; + skeleton.map_api_lanes_stamped_.Send(std::move(sample)); + event_published_ = true; + } + std::this_thread::sleep_for(cycle_time); + } + + std::cout << "Stop offering service..."; + skeleton.StopOfferService(); + std::cout << "and terminating, bye bye\n"; + + return EXIT_SUCCESS; +} + +template int EventSenderReceiver::RunAsProxy>( + const score::mw::com::InstanceSpecifier&, + const score::cpp::optional, + const std::size_t, + bool, + bool); +template int EventSenderReceiver::RunAsProxy( + const score::mw::com::InstanceSpecifier&, + const score::cpp::optional, + const std::size_t, + bool, + bool); + +} // namespace score::mw::com diff --git a/ebclfsa/example/ipc_bridge/sample_sender_receiver.h b/ebclfsa/example/ipc_bridge/sample_sender_receiver.h new file mode 100644 index 0000000000..541ad6decb --- /dev/null +++ b/ebclfsa/example/ipc_bridge/sample_sender_receiver.h @@ -0,0 +1,54 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_MW_COM_IPC_BRIDGE_SAMPLE_SENDER_RECEIVER_H +#define SCORE_MW_COM_IPC_BRIDGE_SAMPLE_SENDER_RECEIVER_H + +#include "datatype.h" + +#include + +#include +#include +#include +#include +#include + +namespace score::mw::com +{ + +class EventSenderReceiver +{ + public: + int RunAsSkeleton(const score::mw::com::InstanceSpecifier& instance_specifier, + const std::chrono::milliseconds cycle_time, + const std::size_t num_cycles); + + template > + int RunAsProxy(const score::mw::com::InstanceSpecifier& instance_specifier, + const score::cpp::optional cycle_time, + const std::size_t num_cycles, + bool try_writing_to_data_segment = false, + bool check_sample_hash = true); + + private: + std::mutex event_sending_mutex_{}; + std::atomic event_published_{false}; + + std::mutex map_lanes_mutex_{}; + std::vector> map_lanes_list_{}; +}; + +} // namespace score::mw::com + +#endif // SCORE_MW_COM_IPC_BRIDGE_SAMPLE_SENDER_RECEIVER_H diff --git a/ebclfsa/example/ipc_bridge_hi_wrapper/BUILD b/ebclfsa/example/ipc_bridge_hi_wrapper/BUILD new file mode 100644 index 0000000000..7248b4a8f7 --- /dev/null +++ b/ebclfsa/example/ipc_bridge_hi_wrapper/BUILD @@ -0,0 +1,118 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") + +genrule( + name = "ipc_bridge_cpp_sil", + srcs = ["//example/ipc_bridge:ipc_bridge_cpp"], + outs = ["ipc_bridge_cpp_sil"], + cmd = "cp $(SRCS) $@ && \ + chmod ugo+w $@ && \ + $(execpath @gcc_toolchain_gcc//:elf-enabler) $@ && \ + chmod ugo-w $@", + tools = ["@gcc_toolchain_gcc//:elf-enabler"], +) + +genrule( + name = "hi_app", + srcs = [ + ":ipc_bridge_hi_wrapper", + ":ipc_bridge_cpp_sil" + ], + outs = ["hi_app"], + cmd = "cp $(location :ipc_bridge_hi_wrapper) $@ && \ + chmod ugo+w $@ && \ + $(execpath @gcc_toolchain_gcc//:elf-enabler) $@ && \ + chmod ugo-w $@", + tools = ["@gcc_toolchain_gcc//:elf-enabler"], +) + +cc_binary( + name = "ipc_bridge_hi_wrapper", + srcs = [ + "main.cc", + ], + features = COMPILER_WARNING_FEATURES, + deps = [ + "//example/ipc_bridge:ipc_bridge_cpp", + ], +) + +genrule( + name = "fetch-fastdev-archive", + srcs = [], + outs = ["fastdev-archive.tgz"], + cmd = "wget -O $@ https://linux.elektrobit.com/tmp/score/fastdev-ubuntu-ebcl-deb-qemu-arm64.tgz" +) + +genrule( + name = "fastdev-image", + srcs = [":fetch-fastdev-archive"], + outs = [ + "deb-qemuarm64/fastdev-ubuntu-ebcl-deb-qemuarm64.wic", + "deb-qemuarm64/fastdev-ubuntu-ebcl-deb-qemuarm64-vmlinux" + ], + cmd = "tar xzf $(location :fetch-fastdev-archive) -C $(RULEDIR)", +) + +genrule( + name = "upload", + srcs = [ + ":ipc_bridge_cpp_sil", + "//example/ipc_bridge:ipc_bridge_cpp", + ":hi_app", + ":fastdev-image", + "mw_com_config.json", + "run_qemu.sh", + "logging.json" + ], + cmd = " \ + mkdir -p $(RULEDIR)/deb-qemuarm64-modified &&\ + cp $(RULEDIR)/deb-qemuarm64/* $(RULEDIR)/deb-qemuarm64-modified/ &&\ + $(location run_qemu.sh) $(RULEDIR)/deb-qemuarm64-modified -pidfile $(RULEDIR)/qemu.pid > $(RULEDIR)/qemu_upload.log &\ + sleep 30 ; \ + sshpass -p linux scp -o StrictHostKeyChecking=no -P 2222 $(location :ipc_bridge_cpp_sil) root@localhost:/usr/bin &&\ + sshpass -p linux scp -o StrictHostKeyChecking=no -P 2222 $(location //example/ipc_bridge:ipc_bridge_cpp) root@localhost:/usr/bin &&\ + sshpass -p linux scp -o StrictHostKeyChecking=no -P 2222 $(location :hi_app) root@localhost:/usr/bin &&\ + sshpass -p linux scp -o StrictHostKeyChecking=no -P 2222 $(location mw_com_config.json) root@localhost:/etc/ &&\ + sshpass -p linux scp -o StrictHostKeyChecking=no -P 2222 $(location logging.json) root@localhost:/etc/ &&\ + sshpass -p linux ssh -o StrictHostKeyChecking=no -p 2222 root@localhost sync &&\ + sshpass -p linux ssh -o StrictHostKeyChecking=no -p 2222 root@localhost crinit-ctl poweroff || true \ + sleep 5 \ + ", + outs = [ + "qemu_upload.log", + "deb-qemuarm64-modified/fastdev-ubuntu-ebcl-deb-qemuarm64.wic", + "deb-qemuarm64-modified/fastdev-ubuntu-ebcl-deb-qemuarm64-vmlinux" + ], +) + +genrule( + name = "run_example", + srcs = [ + "deb-qemuarm64-modified/fastdev-ubuntu-ebcl-deb-qemuarm64.wic", + "deb-qemuarm64-modified/fastdev-ubuntu-ebcl-deb-qemuarm64-vmlinux", + "run_qemu.sh" + ], + cmd = " \ + $(location run_qemu.sh) $(RULEDIR)/deb-qemuarm64-modified -pidfile $(RULEDIR)/qemu.pid > $(RULEDIR)/qemu_run.log &\ + sleep 10 ; \ + sshpass -p linux ssh -o StrictHostKeyChecking=no -p 2222 root@localhost ipc_bridge_cpp -n 10 -m send -t 200 -s /etc/mw_com_config.json > $(RULEDIR)/ssh_run.log && \ + sshpass -p linux ssh -o StrictHostKeyChecking=no -p 2222 root@localhost crinit-ctl poweroff || true \ + sleep 5 \ + ", + outs = [ + "qemu_run.log", + "ssh_run.log", + ], +) diff --git a/ebclfsa/example/ipc_bridge_hi_wrapper/logging.json b/ebclfsa/example/ipc_bridge_hi_wrapper/logging.json new file mode 100644 index 0000000000..54646e1055 --- /dev/null +++ b/ebclfsa/example/ipc_bridge_hi_wrapper/logging.json @@ -0,0 +1,8 @@ +{ + "appId": "IPBR", + "appDesc": "ipc_bridge", + "logLevel": "kInfo", + "logLevelThresholdConsole": "kInfo", + "logMode": "kRemote|kConsole", + "dynamicDatarouterIdentifiers" : true +} diff --git a/ebclfsa/example/ipc_bridge_hi_wrapper/main.cc b/ebclfsa/example/ipc_bridge_hi_wrapper/main.cc new file mode 100644 index 0000000000..d0909dd65f --- /dev/null +++ b/ebclfsa/example/ipc_bridge_hi_wrapper/main.cc @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include +#include +#include + +int main() { + std::cout << "HI_App: Starting ipc_bridge_cpp_sil" << std::endl; + + const char *c_args[] = { + "/usr/bin/ipc_bridge_cpp_sil", + "-n", "10", + "-m", "recv", + "-t", "200", + "-s", "/etc/mw_com_config.json", + nullptr + }; + + execve("/usr/bin/ipc_bridge_cpp_sil", const_cast(c_args), nullptr); + + std::cerr << "execve failed, sleeping... Reason: " << strerror(errno) + << std::endl; + while (true) { + sleep(10); + } + return 0; +} + diff --git a/ebclfsa/example/ipc_bridge_hi_wrapper/mw_com_config.json b/ebclfsa/example/ipc_bridge_hi_wrapper/mw_com_config.json new file mode 100644 index 0000000000..f9a7701535 --- /dev/null +++ b/ebclfsa/example/ipc_bridge_hi_wrapper/mw_com_config.json @@ -0,0 +1,63 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/bmw/adp/MapApiLanesStamped", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 6432, + "events": [ + { + "eventName": "map_api_lanes_stamped", + "eventId": 1 + }, + { + "eventName": "dummy_data_stamped", + "eventId": 2 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "xpad/cp60/MapApiLanesStamped", + "serviceTypeName": "/bmw/adp/MapApiLanesStamped", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "allowedConsumer": { + "QM": [ + 4002, + 0 + ] + }, + "allowedProvider": { + "QM": [ + 4001, + 0 + ] + }, + "asil-level": "QM", + "binding": "SHM", + "events": [ + { + "eventName": "map_api_lanes_stamped", + "numberOfSampleSlots": 10, + "maxSubscribers": 3 + } + ] + } + ] + } + ] +} diff --git a/ebclfsa/example/ipc_bridge_hi_wrapper/run_qemu.sh b/ebclfsa/example/ipc_bridge_hi_wrapper/run_qemu.sh new file mode 100755 index 0000000000..627c81ba41 --- /dev/null +++ b/ebclfsa/example/ipc_bridge_hi_wrapper/run_qemu.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -xu + +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi +BASEFOLDER=$1 +IMAGE="${BASEFOLDER}/fastdev-ubuntu-ebcl-deb-qemuarm64.wic" +KERNEL="${BASEFOLDER}/fastdev-ubuntu-ebcl-deb-qemuarm64-vmlinux" +if [ ! -d "${BASEFOLDER}" ] || [ ! -f "${IMAGE}" ] || [ ! -f "${KERNEL}" ] ; then + echo "Run \"bazel build --config=aarch64 --spawn_strategy=local //score/mw/com/example/ipc_bridge_hi_wrapper:fastdev-image\" first to fetch the image" +fi + +MACHINE="virt,virtualization=true,gic-version=3" +CPU="cortex-a53" +SMP="8" +MEM="4G" +KERNEL_ARGS=("-append" "root=/dev/vda1 sdk_enable lisa_syscall_whitelist=2026 rw sharedmem.enable_sharedmem=0 init=/usr/bin/ebclfsa-cflinit") +DISK_ARGS="-device virtio-blk-device,drive=vd0 -drive if=none,format=raw,file=${IMAGE},id=vd0" +NETWORK_ARGS="-netdev user,id=net0,net=192.168.7.0/24,dhcpstart=192.168.7.2,dns=192.168.7.3,host=192.168.7.5,hostfwd=tcp::2222-:22,hostfwd=tcp::3333-:3333 -device virtio-net-device,netdev=net0 " + +if ! command -v qemu-system-aarch64 > /dev/null; then + echo "Please install qemu-system-aarch64" + exit 1 +fi + +chmod +w ${IMAGE} + +exec qemu-system-aarch64 -m "${MEM}" -machine "${MACHINE}" -cpu "${CPU}" \ + -smp "${SMP}" -kernel "${KERNEL}" "${KERNEL_ARGS[@]}" ${DISK_ARGS} \ + ${NETWORK_ARGS} -nographic ${@:2} diff --git a/ebclfsa/patches/BUILD b/ebclfsa/patches/BUILD new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ebclfsa/patches/fix_hard_coded_amd64.patch b/ebclfsa/patches/fix_hard_coded_amd64.patch new file mode 100644 index 0000000000..014b4f80ab --- /dev/null +++ b/ebclfsa/patches/fix_hard_coded_amd64.patch @@ -0,0 +1,13 @@ +diff --git a/MODULE.bazel b/MODULE.bazel +index a302a84..7917e2b 100644 +--- a/MODULE.bazel ++++ b/MODULE.bazel +@@ -60,7 +60,7 @@ deb = use_repo_rule("@download_utils//download/deb:defs.bzl", "download_deb") + deb( + name = "acl-deb", + build = "//third_party/acl:acl.BUILD", +- urls = ["https://archive.ubuntu.com/ubuntu/pool/main/a/acl/libacl1-dev_2.2.52-3build1_amd64.deb"], ++ urls = ["https://ports.ubuntu.com/pool/main/a/acl/libacl1-dev_2.2.52-3build1_arm64.deb"], + visibility = ["//visibility:public"], + ) +