Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ single_version_override(
# Build settings utilities (bool_flag, string_flag, etc.)
bazel_dep(name = "bazel_skylib", version = "1.9.2")

# s-core communication
bazel_dep(name = "score_communication", version = "0.3.0")

# Testing
bazel_dep(name = "googletest", version = "1.18.0")

Expand Down
1 change: 1 addition & 0 deletions MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions score/crypto/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ Additional documentation for relevant Crypto subcomponents can be found here:
:maxdepth: 1

../src/daemon/data_manager/docs/index
../src/ipc/docs/index

Component Detail Information
============================
Expand Down
342 changes: 342 additions & 0 deletions score/crypto/src/ipc/docs/index.rst

Large diffs are not rendered by default.

199 changes: 199 additions & 0 deletions score/crypto/src/ipc/docs/ipc_comparison.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions score/tests/ipc_poc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mw_com_config_async_consumer_*.json
mw_com_config_async_producer.json
218 changes: 218 additions & 0 deletions score/tests/ipc_poc/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
# *******************************************************************************
# Copyright (c) 2026 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("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")

# Generate C++ and gRPC bindings from the shared POC FlatBuffer schema.
genrule(
name = "generated_poc_control",
srcs = ["poc_control.fbs"],
outs = [
"poc_control_generated.h",
"poc_control.grpc.fb.h",
"poc_control.grpc.fb.cc",
],
cmd = "$(location @flatbuffers//:flatc) --cpp --grpc --size-prefixed -o $(@D) $(SRCS)",
tools = ["@flatbuffers//:flatc"],
)

cc_library(
name = "async_control_plane_service",
hdrs = [
"async_control_interface.h",
"ipc_buffer.h",
":generated_poc_control",
],
include_prefix = "tests/score_com_poc",
deps = [
"@flatbuffers",
"@score_communication//score/mw/com",
],
)

cc_test(
name = "poc_async",
srcs = ["poc_async.cpp"],
deps = [
":async_control_plane_service",
":poc_helper",
"//score/tests/utility:runtime_measurement",
"//score/tests/utility:process_resource_measurement",
],
tags = ["manual"],
)

cc_library(
name = "low_level_poc_deps",
hdrs = [
"ipc_buffer.h",
":generated_poc_control",
],
include_prefix = "tests/score_com_poc",
deps = [
"@flatbuffers",
"@score_communication//score/message_passing",
],
)

cc_library(
name = "direct_socket_poc_deps",
hdrs = [
"ipc_buffer.h",
":generated_poc_control",
],
include_prefix = "tests/score_com_poc",
deps = [
"@flatbuffers",
],
)

cc_library(
name = "poc_helper",
hdrs = [
"ipc_buffer.h",
"poc_helper.hpp",
],
deps = [
":generated_poc_control",
"@flatbuffers",
],
)

cc_test(
name = "poc_low_level",
srcs = ["poc_low_level.cpp"],
deps = [
":low_level_poc_deps",
":poc_helper",
"//score/tests/utility:runtime_measurement",
"//score/tests/utility:process_resource_measurement",
],
tags = ["manual"],
)

cc_test(
name = "poc_low_level_no_reply",
srcs = ["poc_low_level_no_reply.cpp"],
deps = [
":low_level_poc_deps",
":poc_helper",
"//score/tests/utility:runtime_measurement",
"//score/tests/utility:process_resource_measurement",
],
tags = ["manual"],
)

cc_test(
name = "poc_low_level_no_reply_notify_in_thread",
srcs = ["poc_low_level_no_reply_notify_in_thread.cpp"],
deps = [
":low_level_poc_deps",
":poc_helper",
"//score/tests/utility:runtime_measurement",
"//score/tests/utility:process_resource_measurement",
],
tags = ["manual"],
)

cc_test(
name = "poc_thread_pool_queue",
srcs = ["poc_thread_pool_queue.cpp"],
deps = [
"//score/tests/utility:runtime_measurement",
],
tags = ["manual"],
)

cc_test(
name = "poc_engine",
srcs = ["poc_engine.cpp"],
deps = [
":low_level_poc_deps",
":poc_helper",
"//score/tests/utility:runtime_measurement",
"//score/tests/utility:process_resource_measurement",
],
tags = ["manual"],
)

cc_test(
name = "poc_engine_sync",
srcs = ["poc_engine_sync.cpp"],
deps = [
":low_level_poc_deps",
":poc_helper",
"//score/tests/utility:runtime_measurement",
"//score/tests/utility:process_resource_measurement",
],
tags = ["manual"],
)

cc_test(
name = "poc_unix_socket",
srcs = ["poc_unix_socket.cpp"],
linkopts = select({
"//platforms:is_qnx_aarch64": ["-lsocket"],
"//platforms:is_qnx_x86_64": ["-lsocket"],
"//conditions:default": [],
}),
deps = [
":direct_socket_poc_deps",
":poc_helper",
"//score/tests/utility:runtime_measurement",
"//score/tests/utility:process_resource_measurement",
],
tags = ["manual"],
)

cc_test(
name = "poc_qnx_message_passing",
srcs = ["poc_qnx_message_passing.cpp"],
target_compatible_with = select({
"//platforms:is_qnx_aarch64": [],
"//platforms:is_qnx_x86_64": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
deps = [
":direct_socket_poc_deps",
":poc_helper",
"//score/tests/utility:runtime_measurement",
"//score/tests/utility:process_resource_measurement",
],
tags = ["manual"],
)

cc_library(
name = "poc_grpc_protocol",
srcs = [":generated_poc_control"],
hdrs = [
":generated_poc_control",
],
deps = [
"@flatbuffers",
"@grpc//:grpc++_unsecure",
],
)

cc_test(
name = "poc_grpc",
srcs = ["poc_grpc.cpp"],
deps = [
"//score/tests/utility:runtime_measurement",
":poc_helper",
":poc_grpc_protocol",
"//score/tests/utility:process_resource_measurement",
"@grpc//:grpc++_unsecure",
],
tags = ["manual"],
)
57 changes: 57 additions & 0 deletions score/tests/ipc_poc/async_control_interface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/********************************************************************************
* Copyright (c) 2026 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
********************************************************************************/

#pragma once

#include <cstdint>

#include "score/mw/com/types.h"
#include "score/tests/ipc_poc/ipc_buffer.h"

namespace score::crypto::ipc::control
{

/// S-CORE com service interface for the async crypto daemon control plane POC.
///
/// The new communication model uses a two-phase interaction:
///
/// Phase 1 — Short-lived Method call:
/// The client sends a ControlRequest (IpcBuffer) via the "Request" method.
/// The skeleton handler enqueues the work and immediately returns the
/// request_id (== ticket number) to the caller. The round-trip is very
/// short; no processing happens on the calling thread.
///
/// Phase 2 — Event-based response:
/// A server-side background worker dequeues the request, does the actual
/// work, then calls response.Send() to broadcast a ControlResponse
/// (IpcBuffer) to all subscribers. The IpcBuffer carries the original
/// request_id so every client can match the event to its outstanding ticket.
template <typename Trait>
class AsyncControlInterface : public Trait::Base
{
public:
using Trait::Base::Base;

/// Phase 1: client sends a ControlRequest, server returns the ticket number.
typename Trait::template Method<std::uint64_t(IpcBuffer)> request{*this, "Request"};

/// Phase 2: server broadcasts a ControlResponse when work is complete.
/// The IpcBuffer payload is a size-prefixed ControlResponse FlatBuffer
/// whose request_id field equals the ticket returned by Phase 1.
typename Trait::template Event<IpcBuffer> response{*this, "Response"};
};

using AsyncControlProxy = score::mw::com::AsProxy<AsyncControlInterface>;
using AsyncControlSkeleton = score::mw::com::AsSkeleton<AsyncControlInterface>;

} // namespace score::crypto::ipc::control
92 changes: 92 additions & 0 deletions score/tests/ipc_poc/ipc_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/********************************************************************************
* Copyright (c) 2026 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
********************************************************************************/

#pragma once

#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <type_traits>

namespace score::crypto::ipc::control
{

/// Maximum size (bytes) of a serialized FlatBuffer payload that can be
/// transported via mw::com Method RPC. This is an ABI boundary: changing this
/// value requires rebuilding both sides of the IPC (client and server) together.
constexpr std::size_t kMaxIpcBufferSize = 4096U;

/// Trivially copyable envelope for transporting a size-prefixed FlatBuffer over
/// S-CORE LoLa shared-memory Method RPC.
///
/// S-CORE LoLa requires method argument and return types to be trivially
/// copyable (used via reinterpret_cast in shared memory). The payload is stored
/// in size-prefixed FlatBuffer format: the first 4 bytes are a little-endian
/// uint32 encoding the size of the remaining data, so no separate length field
/// is needed.
struct IpcBuffer
{
std::array<std::byte, kMaxIpcBufferSize> payload{};
};

static_assert(std::is_trivially_copyable_v<IpcBuffer>,
"IpcBuffer must be trivially copyable for LoLa shared-memory transport");
static_assert(sizeof(IpcBuffer) == kMaxIpcBufferSize, "IpcBuffer layout must have no padding");

/// Serialize raw size-prefixed FlatBuffer bytes into an IpcBuffer.
/// Returns a zero-initialised buffer (IsValid() == false) if size exceeds kMaxIpcBufferSize.
inline IpcBuffer PackFlatBuffer(const std::uint8_t* data, const std::size_t size) noexcept
{
IpcBuffer buf{};
if (size <= kMaxIpcBufferSize)
{
std::memcpy(buf.payload.data(), data, size);
}
return buf;
}

/// Write size-prefixed FlatBuffer bytes directly into an existing IpcBuffer (e.g. a
/// MethodInArgPtr-backed SHM slot) to avoid an intermediate copy.
/// On success returns true; if size exceeds kMaxIpcBufferSize the buffer is
/// zero-initialised and false is returned.
inline bool PackFlatBufferInto(IpcBuffer& buf, const std::uint8_t* data, const std::size_t size) noexcept
{
if (size > kMaxIpcBufferSize)
{
buf = IpcBuffer{};
return false;
}
std::memcpy(buf.payload.data(), data, size);
return true;
}

/// Read the size prefix embedded in a size-prefixed FlatBuffer.
/// Returns the total number of valid bytes (4-byte prefix + data).
inline std::uint32_t GetPayloadSize(const IpcBuffer& buf) noexcept
{
std::uint32_t prefix{};
std::memcpy(&prefix, buf.payload.data(), sizeof(prefix));
return prefix + static_cast<std::uint32_t>(sizeof(prefix));
}

/// Returns true if the buffer contains a non-empty, in-bounds payload.
inline bool IsValid(const IpcBuffer& buf) noexcept
{
std::uint32_t prefix{};
std::memcpy(&prefix, buf.payload.data(), sizeof(prefix));
const auto total = static_cast<std::size_t>(prefix) + sizeof(prefix);
return prefix > 0U && total <= kMaxIpcBufferSize;
}

} // namespace score::crypto::ipc::control
Loading
Loading