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
87 changes: 87 additions & 0 deletions demos/2024-04-23-cheritech/cheri.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// FFI Imports
// Each function imported from the host environment needs to be assigned to a
// global like this and identified by a constant that the resolver in the C/C++
// code will understand.
// These constants are defined in the `Exports` enumeration.


var FFINumber = 1;

/**
* Log function, writes all arguments to the UART.
*/
export const print = vmImport(FFINumber++);

/**
* led_on(index).
*
* Turns on the LED at the specified index.
*/
export const led_on = vmImport(FFINumber++);

/**
* led_off(index).
*
* Turns off the LED at the specified index.
*/
export const led_off = vmImport(FFINumber++);

/**
* buttons_read().
*
* Reads the value of all of the buttons, returning a 4-bit value indicating
* the states of all of them.
*/
export const buttons_read = vmImport(FFINumber++);

/**
* switches_read().
*
* Reads the value of all of the switches, returning a 4-bit value indicating
* the states of all of them.
*/
export const switches_read = vmImport(FFINumber++);


export const mqtt_publish = vmImport(FFINumber++);
export const mqtt_subscribe = vmImport(FFINumber++);

/**
* led_set(index, state).
*
* Turns the LED at the specified index on or off depending on whether state is
* true or false.
*/
export function led_set(index, state)
{
if (state)
{
led_on(index);
}
else
{
led_off(index);
}
}

/**
* button_read(index).
*
* Reads the value of the button at the specified index.
*/
export function button_read(index)
{
return (buttons_read() & (1 << index)) !== 0;
}


/**
* switch_read(index).
*
* Reads the value of the switch at the specified index.
*/
export function switch_read(index)
{
return (switches_read() & (1 << index)) !== 0;
}

253 changes: 253 additions & 0 deletions demos/2024-04-23-cheritech/demo.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
// Copyright SCI Semiconductor and CHERIoT Contributors.
// SPDX-License-Identifier: MIT

#include <NetAPI.h>
#include <cstdlib>
#include <debug.hh>
#include <errno.h>
#include <fail-simulator-on-error.h>
#include <locks.hh>
#include <mqtt.h>
#include <platform-entropy.hh>
#include <platform-gpio.hh>
#include <sntp.h>
#include <string_view>
#include <tick_macros.h>
#include <vector>

#include "host.cert.h"
#include "javascript.hh"

using CHERI::Capability;

using Debug = ConditionalDebug<true, "MQTT demo">;
constexpr bool UseIPv6 = CHERIOT_RTOS_OPTION_IPv6;

// MQTT network buffer sizes
constexpr const size_t networkBufferSize = 2048;
constexpr const size_t incomingPublishCount = 100;
constexpr const size_t outgoingPublishCount = 100;

namespace
{

DECLARE_AND_DEFINE_CONNECTION_CAPABILITY(DemoHost,
"cheriot.demo",
8883,
ConnectionTypeTCP);

DECLARE_AND_DEFINE_ALLOCATOR_CAPABILITY(mqttTestMalloc, 32 * 1024);

constexpr std::string_view CodeTopic{"cheri-code"};
int32_t codeSubscribePacketId = -1;
bool codeAckReceived = false;

constexpr const char *buttonTopic = "cheri-button";
int buttonCounter = 0;

/**
* Note from the MQTT 3.1.1 spec:
* The Server MUST allow ClientIds which are between 1 and 23 UTF-8 encoded
* bytes in length, and that contain only the characters
* "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
*
* Note from us:
* UTF-8 encoding of 0-9, a-z, A-Z, is 1 Byte per character, so we should be
* able to do up to a length of 22 characters + zero byte.
*/
constexpr const int clientIDlength = 23;
constexpr const int clientIDPrefixLength = 8;
char clientID[clientIDlength] = "cheriot-XXXXXXXXXXXXXX";

/// Callbacks

void __cheri_callback ackCallback(uint16_t packetID, bool isReject)
{
if (packetID == codeSubscribePacketId)
{
codeAckReceived = true;
}
}

void __cheri_callback publishCallback(const char *topicName,
size_t topicNameLength,
const void *payloadData,
size_t payloadLength)
{
std::string_view topic{topicName, topicNameLength};
// FIXME: This is a work around for a compiler bug. __builtin_memcmp
// is being expanded to a call to memcmp with the wrong calling
// convention and so we get linker errors.
// if (topic == CodeTopic)
if ((CodeTopic.size() == topic.size()) &&
(memcmp(topic.data(), CodeTopic.data(), CodeTopic.size()) == 0))
{
load_javascript(payloadData, payloadLength);
return;
}

std::string_view payload{static_cast<const char *>(payloadData),
payloadLength};
publish(topic, payload);
}

/// Handle to the MQTT connection.
SObj handle;

} // namespace

bool mqtt_publish(std::string_view topic, std::string_view message)
{
Timeout t{UnlimitedTimeout};
auto ret = mqtt_publish(&t,
handle,
0, // Don't want acks for this one
topic.data(),
topic.size(),
message.data(),
message.size());
return ret != 0;
}

bool mqtt_subscribe(std::string_view topic)
{
Timeout t{MS_TO_TICKS(100)};
auto ret = mqtt_subscribe(&t, handle, 1, topic.data(), topic.size());
return ret >= 0;
}

/// Main demo

void __cheri_compartment("mqtt_demo") demo()
{
int ret;
Timeout t{MS_TO_TICKS(5000)};

MMIO_CAPABILITY(GPIO, gpio_led0)->enable_all();

Debug::log("Starting MQTT demo...");
network_start();
Debug::log("Network is ready...");

// systemd decides to restart the ntp server when it detects a new
// interface. If we try to get NTP time too quickly, the server isn't
// ready. Wait one second to give it time to stabilise.
{
Timeout oneSecond(MS_TO_TICKS(1000));
thread_sleep(&oneSecond);
}

// SNTP must be run for the TLS stack to be able to check certificate dates.
Debug::log("Fetching NTP time...");
t = Timeout{MS_TO_TICKS(1000)};
while (sntp_update(&t) != 0)
{
Debug::log("Failed to update NTP time");
t = Timeout{MS_TO_TICKS(1000)};
}

{
timeval tv;
int ret = gettimeofday(&tv, nullptr);
if (ret != 0)
{
Debug::log("Failed to get time of day: {}", ret);
}
else
{
// Truncate the epoch time to 32 bits for printing.
Debug::log("Current UNIX epoch time: {}", (int32_t)tv.tv_sec);
}
}

while (true)
{
load_javascript(nullptr, 0);
Debug::log("Generating client ID...");
mqtt_generate_client_id(clientID + clientIDPrefixLength,
clientIDlength - clientIDPrefixLength - 1);

Debug::log("Connecting to MQTT broker...");
Debug::log("Quota left: {}", heap_quota_remaining(MALLOC_CAPABILITY));
t = UnlimitedTimeout;
handle = mqtt_connect(&t,
STATIC_SEALED_VALUE(mqttTestMalloc),
STATIC_SEALED_VALUE(DemoHost),
publishCallback,
ackCallback,
TAs,
TAs_NUM,
networkBufferSize,
incomingPublishCount,
outgoingPublishCount,
clientID,
strlen(clientID));

if (!Capability{handle}.is_valid())
{
Debug::log("Failed to connect, retrying...");
Timeout pause{MS_TO_TICKS(1000)};
thread_sleep(&pause);
continue;
}

Debug::log("Connected to MQTT broker!");

Debug::log("Subscribing to JavaScript code topic '{}'.", CodeTopic);

ret = mqtt_subscribe(&t,
handle,
1, // QoS 1 = delivered at least once
CodeTopic.data(),
CodeTopic.size());

if (ret < 0)
{
Debug::log("Failed to subscribe, error {}.", ret);
mqtt_disconnect(&t, STATIC_SEALED_VALUE(mqttTestMalloc), handle);
continue;
}

codeSubscribePacketId = ret;

Debug::log("Now fetching the SUBACKs.");

while (!codeAckReceived)
{
t = Timeout{MS_TO_TICKS(1000)};
ret = mqtt_run(&t, handle);

if (ret < 0)
{
Debug::log(
"Failed to wait for the SUBACK for the code node, error {}.",
ret);
mqtt_disconnect(
&t, STATIC_SEALED_VALUE(mqttTestMalloc), handle);
continue;
}
}

Debug::log("Now entering the main loop.");
while (true)
{
// Check for PUBLISHes
t = Timeout{MS_TO_TICKS(100)};
// Debug::log("{} bytes of heap free", heap_available());
ret = mqtt_run(&t, handle);

if ((ret < 0) && (ret != -ETIMEDOUT))
{
Debug::log("Failed to wait for PUBLISHes, error {}.", ret);
break;
}
tick();
}
Debug::log("Exiting main loop, cleaning up.");
mqtt_disconnect(&t, STATIC_SEALED_VALUE(mqttTestMalloc), handle);
// Sleep for a second to allow the network stack to clean up any
// outstanding allocations
Timeout oneSecond{MS_TO_TICKS(1000)};
thread_sleep(&oneSecond);
}
}
30 changes: 30 additions & 0 deletions demos/2024-04-23-cheritech/host.cert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
static const unsigned char TA0_DN[] = {
0x30, 0x17, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C,
0x0C, 0x63, 0x68, 0x65, 0x72, 0x69, 0x6F, 0x74, 0x2E, 0x64, 0x65, 0x6D,
0x6F
};

static const unsigned char TA0_EC_Q[] = {
0x04, 0xDB, 0x3A, 0xBE, 0xAF, 0x9F, 0x69, 0xF9, 0x02, 0xA4, 0xA0, 0xA7,
0x41, 0x82, 0xE5, 0xF1, 0x06, 0x5E, 0x0A, 0xA1, 0x7E, 0x43, 0x61, 0xBE,
0xA0, 0x1F, 0x22, 0x3D, 0x5A, 0xB9, 0xFB, 0xA3, 0x86, 0xF3, 0xF2, 0xB3,
0x59, 0xE0, 0x93, 0xC6, 0xE0, 0x9F, 0xA6, 0x22, 0x51, 0x10, 0x2A, 0xFF,
0x5E, 0x20, 0x96, 0x27, 0xD6, 0x8C, 0x08, 0x59, 0x0E, 0x58, 0x6C, 0xA6,
0x87, 0xA5, 0x9A, 0x96, 0x02
};

static const br_x509_trust_anchor TAs[1] = {
{
{ (unsigned char *)TA0_DN, sizeof TA0_DN },
BR_X509_TA_CA,
{
BR_KEYTYPE_EC,
{ .ec = {
BR_EC_secp256r1,
(unsigned char *)TA0_EC_Q, sizeof TA0_EC_Q,
} }
}
}
};

#define TAs_NUM 1
Loading