Skip to content

Commit 70f1411

Browse files
committed
Initial version of the demo (WIP).
JavaScript VM is linked but not yet used. 16 KiB free.
1 parent 238b804 commit 70f1411

File tree

3 files changed

+463
-0
lines changed

3 files changed

+463
-0
lines changed

demos/2024-04-23-cheritech/demo.cc

Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
// Copyright SCI Semiconductor and CHERIoT Contributors.
2+
// SPDX-License-Identifier: MIT
3+
4+
#include <NetAPI.h>
5+
#include <cstdlib>
6+
#include <debug.hh>
7+
#include <errno.h>
8+
#include <fail-simulator-on-error.h>
9+
#include <locks.hh>
10+
#include <mqtt.h>
11+
#include <platform-entropy.hh>
12+
#include <platform-gpio.hh>
13+
#include <sntp.h>
14+
#include <tick_macros.h>
15+
16+
#include "host.cert.h"
17+
#include "thread.h"
18+
#include "timeout.h"
19+
//#include "mosquitto.org.h"
20+
21+
using CHERI::Capability;
22+
23+
using Debug = ConditionalDebug<true, "MQTT demo">;
24+
constexpr bool UseIPv6 = CHERIOT_RTOS_OPTION_IPv6;
25+
26+
// MQTT network buffer sizes
27+
constexpr const size_t networkBufferSize = 1024;
28+
constexpr const size_t incomingPublishCount = 100;
29+
constexpr const size_t outgoingPublishCount = 100;
30+
31+
namespace
32+
{
33+
34+
DECLARE_AND_DEFINE_CONNECTION_CAPABILITY(DemoHost,
35+
"cheriot.demo",
36+
8883,
37+
ConnectionTypeTCP);
38+
39+
DECLARE_AND_DEFINE_ALLOCATOR_CAPABILITY(mqttTestMalloc, 32 * 1024);
40+
41+
constexpr const char *ledTopic = "cheri-led";
42+
int32_t ledSubscribePacketId = -1;
43+
bool ledAckReceived = false;
44+
45+
constexpr const char *buttonTopic = "cheri-button";
46+
int buttonCounter = 0;
47+
48+
/// Helpers
49+
50+
/// Returns a weak pseudo-random number.
51+
uint64_t rand()
52+
{
53+
EntropySource rng;
54+
return rng();
55+
}
56+
57+
/**
58+
* Note from the MQTT 3.1.1 spec:
59+
* The Server MUST allow ClientIds which are between 1 and 23 UTF-8 encoded
60+
* bytes in length, and that contain only the characters
61+
* "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
62+
*
63+
* Note from us:
64+
* UTF-8 encoding of 0-9, a-z, A-Z, is 1 Byte per character, so we should be
65+
* able to do up to a length of 22 characters + zero byte.
66+
*/
67+
constexpr const int clientIDlength = 23;
68+
constexpr const int clientIDPrefixLength = 8;
69+
char clientID[clientIDlength] = "cheriot-XXXXXXXXXXXXXX";
70+
71+
/**
72+
* Turn an LED on.
73+
*/
74+
void gpios_on()
75+
{
76+
MMIO_CAPABILITY(GPIO, gpio_led0)->enable_all();
77+
}
78+
79+
/**
80+
* Turn an LED on.
81+
*/
82+
void led_on(int32_t index)
83+
{
84+
MMIO_CAPABILITY(GPIO, gpio_led0)->led_on(index);
85+
}
86+
87+
/**
88+
* Turn an LED off.
89+
*/
90+
void led_off(int32_t index)
91+
{
92+
MMIO_CAPABILITY(GPIO, gpio_led0)->led_off(index);
93+
}
94+
95+
/**
96+
* Read a single button.
97+
*/
98+
int32_t read_button(int32_t index)
99+
{
100+
return MMIO_CAPABILITY(GPIO, gpio_led0)->button(index);
101+
}
102+
103+
uint32_t read_switches()
104+
{
105+
return MMIO_CAPABILITY(GPIO, gpio_led0)->switches();
106+
}
107+
108+
/// Callbacks
109+
110+
void __cheri_callback ackCallback(uint16_t packetID, bool isReject)
111+
{
112+
if (packetID == ledSubscribePacketId)
113+
{
114+
ledAckReceived = true;
115+
}
116+
}
117+
118+
void __cheri_callback publishCallback(const char *topicName,
119+
size_t topicNameLength,
120+
const void *payload,
121+
size_t payloadLength)
122+
{
123+
// TODO check input pointers
124+
125+
const char *payloadStr = static_cast<const char *>(payload);
126+
size_t length = std::min(strlen(ledTopic), topicNameLength);
127+
if (strncmp(topicName, ledTopic, length) == 0)
128+
{
129+
if (payloadLength >= 1)
130+
{
131+
switch (static_cast<const char *>(payload)[0])
132+
{
133+
case '0':
134+
led_off(0);
135+
led_off(1);
136+
return;
137+
case '1':
138+
led_on(0);
139+
led_off(1);
140+
return;
141+
case '2':
142+
led_off(0);
143+
led_on(1);
144+
return;
145+
case '3':
146+
led_on(0);
147+
led_on(1);
148+
return;
149+
}
150+
}
151+
}
152+
153+
Debug::log("Received PUBLISH notification with invalid topic ({}) or "
154+
"payload ({}).",
155+
std::string_view{topicName, topicNameLength},
156+
std::string_view{payloadStr, payloadLength});
157+
}
158+
159+
uint32_t switches;
160+
161+
} // namespace
162+
163+
/// Main demo
164+
165+
void __cheri_compartment("mqtt_demo") demo()
166+
{
167+
int ret;
168+
Timeout t{MS_TO_TICKS(5000)};
169+
170+
gpios_on();
171+
172+
Debug::log("Starting MQTT demo...");
173+
network_start();
174+
Debug::log("Network is ready...");
175+
176+
// systemd decides to restart the ntp server when it detects a new
177+
// interface. If we try to get NTP time too quickly, the server isn't
178+
// ready. Wait one second to give it time to stabilise.
179+
{
180+
Timeout oneSecond(MS_TO_TICKS(1000));
181+
thread_sleep(&oneSecond);
182+
}
183+
184+
// SNTP must be run for the TLS stack to be able to check certificate dates.
185+
Debug::log("Fetching NTP time...");
186+
t = Timeout{MS_TO_TICKS(1000)};
187+
while (sntp_update(&t) != 0)
188+
{
189+
Debug::log("Failed to update NTP time");
190+
t = Timeout{MS_TO_TICKS(1000)};
191+
}
192+
193+
{
194+
timeval tv;
195+
int ret = gettimeofday(&tv, nullptr);
196+
if (ret != 0)
197+
{
198+
Debug::log("Failed to get time of day: {}", ret);
199+
}
200+
else
201+
{
202+
// Truncate the epoch time to 32 bits for printing.
203+
Debug::log("Current UNIX epoch time: {}", (int32_t)tv.tv_sec);
204+
}
205+
}
206+
207+
while (true)
208+
{
209+
Debug::log("Generating client ID...");
210+
mqtt_generate_client_id(clientID + clientIDPrefixLength,
211+
clientIDlength - clientIDPrefixLength - 1);
212+
213+
Debug::log("Connecting to MQTT broker...");
214+
Debug::log("Quota left: {}", heap_quota_remaining(MALLOC_CAPABILITY));
215+
t = UnlimitedTimeout;
216+
SObj handle = mqtt_connect(&t,
217+
STATIC_SEALED_VALUE(mqttTestMalloc),
218+
STATIC_SEALED_VALUE(DemoHost),
219+
publishCallback,
220+
ackCallback,
221+
TAs,
222+
TAs_NUM,
223+
networkBufferSize,
224+
incomingPublishCount,
225+
outgoingPublishCount,
226+
clientID,
227+
strlen(clientID));
228+
229+
if (!Capability{handle}.is_valid())
230+
{
231+
Debug::log("Failed to connect, retrying...");
232+
Timeout pause{MS_TO_TICKS(1000)};
233+
thread_sleep(&pause);
234+
continue;
235+
}
236+
237+
Debug::log("Connected to MQTT broker!");
238+
239+
Debug::log("Subscribing to LED topic '{}'.", ledTopic);
240+
241+
ret = mqtt_subscribe(&t,
242+
handle,
243+
1, // QoS 1 = delivered at least once
244+
ledTopic,
245+
strlen(ledTopic));
246+
247+
if (ret < 0)
248+
{
249+
Debug::log("Failed to subscribe, error {}.", ret);
250+
mqtt_disconnect(&t, STATIC_SEALED_VALUE(mqttTestMalloc), handle);
251+
continue;
252+
}
253+
254+
ledSubscribePacketId = ret;
255+
256+
Debug::log("Now fetching the SUBACKs.");
257+
258+
while (!ledAckReceived)
259+
{
260+
t = Timeout{MS_TO_TICKS(100)};
261+
ret = mqtt_run(&t, handle);
262+
263+
if (ret < 0)
264+
{
265+
Debug::log("Failed to wait for the SUBACK, error {}.", ret);
266+
mqtt_disconnect(&t, STATIC_SEALED_VALUE(mqttTestMalloc), handle);
267+
continue;
268+
}
269+
}
270+
271+
Timeout coolDown{0};
272+
Debug::log("Now entering the main loop.");
273+
// Hugo: If I comment out the next line, it will try a reconnect
274+
// immediately. For some reason that I haven't been able to track down
275+
// yet, this fails 100% of the time.
276+
while (true)
277+
{
278+
SystickReturn timestampBefore = thread_systemtick_get();
279+
280+
// Check for PUBLISHes
281+
t = Timeout{MS_TO_TICKS(100)};
282+
ret = mqtt_run(&t, handle);
283+
284+
if (ret < 0)
285+
{
286+
Debug::log("Failed to wait for PUBLISHes, error {}.", ret);
287+
break;
288+
}
289+
290+
uint32_t newSwitches = read_switches();
291+
if (newSwitches != switches)
292+
{
293+
for (int i = 0; i < 8; i++)
294+
{
295+
bool newSwitch = (newSwitches & (1 << i)) != 0;
296+
bool oldSwitch = (switches & (1 << i)) != 0;
297+
if (newSwitch != oldSwitch)
298+
{
299+
Debug::log("Setting switch {} to {}.", i, newSwitch ? "ON" : "OFF");
300+
char topic[] = "cheri-switch-X";
301+
topic[sizeof(topic) - 2] = '0' + i;
302+
t = Timeout{MS_TO_TICKS(5000)};
303+
ret = mqtt_publish(&t,
304+
handle,
305+
0, // Don't want acks for this one
306+
topic,
307+
sizeof(topic) - 1,
308+
newSwitch ? "ON" : "OFF",
309+
newSwitch ? 2 : 3);
310+
Debug::log("Free heap space: {}", heap_available());
311+
if (ret < 0)
312+
{
313+
Debug::log(
314+
"Failed to publish button change, error {}.",
315+
ret);
316+
break;
317+
}
318+
}
319+
}
320+
switches = newSwitches;
321+
}
322+
323+
// Check the button
324+
if (read_button(0) && !coolDown.remaining)
325+
{
326+
Debug::log("Publishing {} to button topic '{}'.",
327+
buttonCounter,
328+
buttonTopic);
329+
330+
char num_char[20] = {0};
331+
snprintf(num_char, 20, "%lu", buttonCounter);
332+
333+
t = Timeout{MS_TO_TICKS(5000)};
334+
ret = mqtt_publish(&t,
335+
handle,
336+
0, // Don't want acks for this one
337+
buttonTopic,
338+
strlen(buttonTopic),
339+
static_cast<const void *>(num_char),
340+
strlen(num_char));
341+
342+
if (ret < 0)
343+
{
344+
Debug::log("Failed to publish, error {}.", ret);
345+
break;
346+
}
347+
348+
buttonCounter++;
349+
350+
// Set cool down timer
351+
coolDown = Timeout{MS_TO_TICKS(500)};
352+
continue;
353+
}
354+
355+
SystickReturn timestampAfter = thread_systemtick_get();
356+
// Timeouts should not overflow a 32 bit value
357+
coolDown.elapse(timestampAfter.lo - timestampBefore.lo);
358+
}
359+
Debug::log("Exiting main loop, cleaning up.");
360+
mqtt_disconnect(&t, STATIC_SEALED_VALUE(mqttTestMalloc), handle);
361+
// Sleep for a second to allow the network stack to clean up any
362+
// outstanding allocations
363+
Timeout oneSecond{MS_TO_TICKS(1000)};
364+
thread_sleep(&oneSecond);
365+
}
366+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
static const unsigned char TA0_DN[] = {
2+
0x30, 0x17, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C,
3+
0x0C, 0x63, 0x68, 0x65, 0x72, 0x69, 0x6F, 0x74, 0x2E, 0x64, 0x65, 0x6D,
4+
0x6F
5+
};
6+
7+
static const unsigned char TA0_EC_Q[] = {
8+
0x04, 0xDB, 0x3A, 0xBE, 0xAF, 0x9F, 0x69, 0xF9, 0x02, 0xA4, 0xA0, 0xA7,
9+
0x41, 0x82, 0xE5, 0xF1, 0x06, 0x5E, 0x0A, 0xA1, 0x7E, 0x43, 0x61, 0xBE,
10+
0xA0, 0x1F, 0x22, 0x3D, 0x5A, 0xB9, 0xFB, 0xA3, 0x86, 0xF3, 0xF2, 0xB3,
11+
0x59, 0xE0, 0x93, 0xC6, 0xE0, 0x9F, 0xA6, 0x22, 0x51, 0x10, 0x2A, 0xFF,
12+
0x5E, 0x20, 0x96, 0x27, 0xD6, 0x8C, 0x08, 0x59, 0x0E, 0x58, 0x6C, 0xA6,
13+
0x87, 0xA5, 0x9A, 0x96, 0x02
14+
};
15+
16+
static const br_x509_trust_anchor TAs[1] = {
17+
{
18+
{ (unsigned char *)TA0_DN, sizeof TA0_DN },
19+
BR_X509_TA_CA,
20+
{
21+
BR_KEYTYPE_EC,
22+
{ .ec = {
23+
BR_EC_secp256r1,
24+
(unsigned char *)TA0_EC_Q, sizeof TA0_EC_Q,
25+
} }
26+
}
27+
}
28+
};
29+
30+
#define TAs_NUM 1

0 commit comments

Comments
 (0)