From 9326efcd0f3302c850a9cc5328b2155461c9e114 Mon Sep 17 00:00:00 2001 From: Oliver Pajonk Date: Tue, 11 Nov 2025 14:33:56 +0000 Subject: [PATCH] Integrate EB corbos Linux for Safety Applications This uses the ipc_bridge example of communication as example / test. It also contains extensive documentation about the integration itself, and a CI workflow. --- .devcontainer/devcontainer.json | 5 + .github/workflows/build_and_test_ebclfsa.yml | 41 ++ ebclfsa/.bazelrc | 10 + ebclfsa/BUILD | 13 + ebclfsa/MODULE.bazel | 82 ++++ ebclfsa/README.md | 386 +++++++++++++++ ebclfsa/docs/system_setup.drawio.png | Bin 0 -> 38057 bytes ebclfsa/example/ipc_bridge/BUILD | 65 +++ ebclfsa/example/ipc_bridge/assert_handler.cpp | 69 +++ ebclfsa/example/ipc_bridge/assert_handler.h | 23 + ebclfsa/example/ipc_bridge/datatype.cpp | 13 + ebclfsa/example/ipc_bridge/datatype.h | 305 ++++++++++++ ebclfsa/example/ipc_bridge/etc/logging.json | 8 + .../example/ipc_bridge/etc/mw_com_config.json | 63 +++ ebclfsa/example/ipc_bridge/main.cpp | 131 +++++ .../ipc_bridge/sample_sender_receiver.cpp | 463 ++++++++++++++++++ .../ipc_bridge/sample_sender_receiver.h | 54 ++ ebclfsa/example/ipc_bridge_hi_wrapper/BUILD | 118 +++++ .../ipc_bridge_hi_wrapper/logging.json | 8 + ebclfsa/example/ipc_bridge_hi_wrapper/main.cc | 38 ++ .../ipc_bridge_hi_wrapper/mw_com_config.json | 63 +++ .../example/ipc_bridge_hi_wrapper/run_qemu.sh | 33 ++ ebclfsa/patches/BUILD | 0 ebclfsa/patches/fix_hard_coded_amd64.patch | 13 + 24 files changed, 2004 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/workflows/build_and_test_ebclfsa.yml create mode 100644 ebclfsa/.bazelrc create mode 100644 ebclfsa/BUILD create mode 100644 ebclfsa/MODULE.bazel create mode 100644 ebclfsa/README.md create mode 100644 ebclfsa/docs/system_setup.drawio.png create mode 100644 ebclfsa/example/ipc_bridge/BUILD create mode 100644 ebclfsa/example/ipc_bridge/assert_handler.cpp create mode 100644 ebclfsa/example/ipc_bridge/assert_handler.h create mode 100644 ebclfsa/example/ipc_bridge/datatype.cpp create mode 100644 ebclfsa/example/ipc_bridge/datatype.h create mode 100644 ebclfsa/example/ipc_bridge/etc/logging.json create mode 100644 ebclfsa/example/ipc_bridge/etc/mw_com_config.json create mode 100644 ebclfsa/example/ipc_bridge/main.cpp create mode 100644 ebclfsa/example/ipc_bridge/sample_sender_receiver.cpp create mode 100644 ebclfsa/example/ipc_bridge/sample_sender_receiver.h create mode 100644 ebclfsa/example/ipc_bridge_hi_wrapper/BUILD create mode 100644 ebclfsa/example/ipc_bridge_hi_wrapper/logging.json create mode 100644 ebclfsa/example/ipc_bridge_hi_wrapper/main.cc create mode 100644 ebclfsa/example/ipc_bridge_hi_wrapper/mw_com_config.json create mode 100755 ebclfsa/example/ipc_bridge_hi_wrapper/run_qemu.sh create mode 100644 ebclfsa/patches/BUILD create mode 100644 ebclfsa/patches/fix_hard_coded_amd64.patch 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 0000000000000000000000000000000000000000..f6f4fea62d2ce09c5f35c276935ef1ae06e49a68 GIT binary patch literal 38057 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 PfFEgb`Pb#5hW`HxJsk%9 literal 0 HcmV?d00001 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"], + ) +