zephyr: Enable WASI support for file system and sockets on zephyr (#3633)

To address #3311.

This work also implements the WASI support on Zephyr.

Note that some comments haven't been addressed and will be fixed in the
further patches.
This commit is contained in:
Lucas Abad 2024-11-06 01:06:50 +01:00 committed by GitHub
parent e352f0ab10
commit 04642622d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 9673 additions and 134 deletions

View File

@ -175,14 +175,14 @@ blocking_op_openat(wasm_exec_env_t exec_env, os_file_handle handle,
#ifndef BH_PLATFORM_WINDOWS
/* REVISIT: apply the os_file_handle style abstraction for pollfd? */
__wasi_errno_t
blocking_op_poll(wasm_exec_env_t exec_env, struct pollfd *pfds, nfds_t nfds,
int timeout_ms, int *retp)
blocking_op_poll(wasm_exec_env_t exec_env, os_poll_file_handle *pfds,
os_nfds_t nfds, int timeout_ms, int *retp)
{
int ret;
if (!wasm_runtime_begin_blocking_op(exec_env)) {
return __WASI_EINTR;
}
ret = poll(pfds, nfds, timeout_ms);
ret = os_poll(pfds, nfds, timeout_ms);
wasm_runtime_end_blocking_op(exec_env);
if (ret == -1) {
return convert_errno(errno);

View File

@ -57,8 +57,8 @@ blocking_op_openat(wasm_exec_env_t exec_env, os_file_handle handle,
#ifndef BH_PLATFORM_WINDOWS
__wasi_errno_t
blocking_op_poll(wasm_exec_env_t exec_env, struct pollfd *pfds, nfds_t nfds,
int timeout, int *retp);
blocking_op_poll(wasm_exec_env_t exec_env, os_poll_file_handle *pfds,
os_nfds_t nfds, int timeout, int *retp);
#endif
#endif /* end of _BLOCKING_OP_H_ */

View File

@ -196,8 +196,12 @@ static inline bool
cond_timedwait(struct cond *cond, struct mutex *lock, uint64_t timeout,
bool abstime) REQUIRES_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS
{
#if defined(BH_PLATFORM_ZEPHYR)
// TODO: Implement this for Zephyr
return false;
#else
int ret;
struct timespec ts = {
os_timespec ts = {
.tv_sec = (time_t)(timeout / 1000000000),
.tv_nsec = (long)(timeout % 1000000000),
};
@ -210,8 +214,8 @@ cond_timedwait(struct cond *cond, struct mutex *lock, uint64_t timeout,
* realtime clock.
*/
if (cond->clock != CLOCK_REALTIME) {
struct timespec ts_monotonic;
struct timespec ts_realtime;
os_timespec ts_monotonic;
os_timespec ts_realtime;
clock_gettime(cond->clock, &ts_monotonic);
ts.tv_sec -= ts_monotonic.tv_sec;
@ -229,7 +233,7 @@ cond_timedwait(struct cond *cond, struct mutex *lock, uint64_t timeout,
++ts.tv_sec;
}
}
#endif
#endif /* !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK */
}
else {
#if CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP
@ -241,7 +245,7 @@ cond_timedwait(struct cond *cond, struct mutex *lock, uint64_t timeout,
return ret == ETIMEDOUT;
#else
/* Convert to absolute timeout. */
struct timespec ts_now;
os_timespec ts_now;
#if CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK
clock_gettime(cond->clock, &ts_now);
#else
@ -253,13 +257,14 @@ cond_timedwait(struct cond *cond, struct mutex *lock, uint64_t timeout,
ts.tv_nsec -= 1000000000;
++ts.tv_sec;
}
#endif
#endif /* CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP */
}
ret = pthread_cond_timedwait(&cond->object, &lock->object, &ts);
bh_assert((ret == 0 || ret == ETIMEDOUT)
&& "pthread_cond_timedwait() failed");
return ret == ETIMEDOUT;
#endif /* BH_PLATFORM_ZEPHYR */
}
#endif

View File

@ -356,16 +356,20 @@ fd_table_get_entry(struct fd_table *ft, __wasi_fd_t fd,
REQUIRES_SHARED(ft->lock)
{
// Test for file descriptor existence.
if (fd >= ft->size)
if (fd >= ft->size) {
return __WASI_EBADF;
}
struct fd_entry *fe = &ft->entries[fd];
if (fe->object == NULL)
if (fe->object == NULL) {
return __WASI_EBADF;
}
// Validate rights.
if ((~fe->rights_base & rights_base) != 0
|| (~fe->rights_inheriting & rights_inheriting) != 0)
|| (~fe->rights_inheriting & rights_inheriting) != 0) {
return __WASI_ENOTCAPABLE;
}
*ret = fe;
return 0;
}
@ -426,15 +430,15 @@ fd_table_attach(struct fd_table *ft, __wasi_fd_t fd, struct fd_object *fo,
__wasi_rights_t rights_base, __wasi_rights_t rights_inheriting)
REQUIRES_EXCLUSIVE(ft->lock) CONSUMES(fo->refcount)
{
assert(ft->size > fd && "File descriptor table too small");
bh_assert(ft->size > fd && "File descriptor table too small");
struct fd_entry *fe = &ft->entries[fd];
assert(fe->object == NULL
&& "Attempted to overwrite an existing descriptor");
bh_assert(fe->object == NULL
&& "Attempted to overwrite an existing descriptor");
fe->object = fo;
fe->rights_base = rights_base;
fe->rights_inheriting = rights_inheriting;
++ft->used;
assert(ft->size >= ft->used * 2 && "File descriptor too full");
bh_assert(ft->size >= ft->used * 2 && "File descriptor too full");
}
// Detaches a file descriptor from the file descriptor table.
@ -442,12 +446,12 @@ static void
fd_table_detach(struct fd_table *ft, __wasi_fd_t fd, struct fd_object **fo)
REQUIRES_EXCLUSIVE(ft->lock) PRODUCES((*fo)->refcount)
{
assert(ft->size > fd && "File descriptor table too small");
bh_assert(ft->size > fd && "File descriptor table too small");
struct fd_entry *fe = &ft->entries[fd];
*fo = fe->object;
assert(*fo != NULL && "Attempted to detach nonexistent descriptor");
bh_assert(*fo != NULL && "Attempted to detach nonexistent descriptor");
fe->object = NULL;
assert(ft->used > 0 && "Reference count mismatch");
bh_assert(ft->used > 0 && "Reference count mismatch");
--ft->used;
}
@ -636,7 +640,7 @@ fd_table_insert_existing(struct fd_table *ft, __wasi_fd_t in,
static __wasi_errno_t
fd_table_unused(struct fd_table *ft, __wasi_fd_t *out) REQUIRES_SHARED(ft->lock)
{
assert(ft->size > ft->used && "File descriptor table has no free slots");
bh_assert(ft->size > ft->used && "File descriptor table has no free slots");
for (;;) {
uintmax_t random_fd = 0;
__wasi_errno_t error = random_uniform(ft->size, &random_fd);
@ -1537,7 +1541,8 @@ path_put(struct path_access *pa) UNLOCKS(pa->fd_object->refcount)
{
if (pa->path_start)
wasm_runtime_free(pa->path_start);
if (pa->fd_object->file_handle != pa->fd)
/* Can't use `!=` operator when `os_file_handle` is a struct */
if (!os_compare_file_handle(pa->fd_object->file_handle, pa->fd))
os_close(pa->fd, false);
fd_object_release(NULL, pa->fd_object);
}
@ -1878,7 +1883,7 @@ wasmtime_ssp_fd_filestat_get(wasm_exec_env_t exec_env, struct fd_table *curfds,
}
static void
convert_timestamp(__wasi_timestamp_t in, struct timespec *out)
convert_timestamp(__wasi_timestamp_t in, os_timespec *out)
{
// Store sub-second remainder.
#if defined(__SYSCALL_SLONG_TYPE)
@ -2076,7 +2081,7 @@ wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds,
size_t nsubscriptions,
size_t *nevents) NO_LOCK_ANALYSIS
{
#ifdef BH_PLATFORM_WINDOWS
#if defined(BH_PLATFORM_WINDOWS) || defined(BH_PLATFORM_ZEPHYR)
return __WASI_ENOSYS;
#else
// Sleeping.
@ -2088,7 +2093,7 @@ wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds,
#if CONFIG_HAS_CLOCK_NANOSLEEP
clockid_t clock_id;
if (wasi_clockid_to_clockid(in[0].u.u.clock.clock_id, &clock_id)) {
struct timespec ts;
os_timespec ts;
convert_timestamp(in[0].u.u.clock.timeout, &ts);
int ret = clock_nanosleep(
clock_id,
@ -2115,7 +2120,7 @@ wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds,
else {
// Perform relative sleeps on the monotonic clock also using
// nanosleep(). This is incorrect, but good enough for now.
struct timespec ts;
os_timespec ts;
convert_timestamp(in[0].u.u.clock.timeout, &ts);
nanosleep(&ts, NULL);
}
@ -2143,7 +2148,7 @@ wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds,
}
else {
// Relative sleeps can be done using nanosleep().
struct timespec ts;
os_timespec ts;
convert_timestamp(in[0].u.u.clock.timeout, &ts);
nanosleep(&ts, NULL);
}
@ -2168,7 +2173,7 @@ wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds,
wasm_runtime_malloc((uint32)(nsubscriptions * sizeof(*fos)));
if (fos == NULL)
return __WASI_ENOMEM;
struct pollfd *pfds =
os_poll_file_handle *pfds =
wasm_runtime_malloc((uint32)(nsubscriptions * sizeof(*pfds)));
if (pfds == NULL) {
wasm_runtime_free(fos);
@ -2193,7 +2198,7 @@ wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds,
__WASI_RIGHT_POLL_FD_READWRITE, 0);
if (error == 0) {
// Proper file descriptor on which we can poll().
pfds[i] = (struct pollfd){
pfds[i] = (os_poll_file_handle){
.fd = fos[i]->file_handle,
.events = s->u.type == __WASI_EVENTTYPE_FD_READ
? POLLIN
@ -2203,7 +2208,7 @@ wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds,
else {
// Invalid file descriptor or rights missing.
fos[i] = NULL;
pfds[i] = (struct pollfd){ .fd = -1 };
pfds[i] = (os_poll_file_handle){ .fd = -1 };
out[(*nevents)++] = (__wasi_event_t){
.userdata = s->userdata,
.error = error,
@ -2218,7 +2223,7 @@ wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds,
== 0) {
// Relative timeout.
fos[i] = NULL;
pfds[i] = (struct pollfd){ .fd = -1 };
pfds[i] = (os_poll_file_handle){ .fd = -1 };
clock_subscription = s;
break;
}
@ -2226,7 +2231,7 @@ wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds,
default:
// Unsupported event.
fos[i] = NULL;
pfds[i] = (struct pollfd){ .fd = -1 };
pfds[i] = (os_poll_file_handle){ .fd = -1 };
out[(*nevents)++] = (__wasi_event_t){
.userdata = s->userdata,
.error = __WASI_ENOSYS,
@ -2270,7 +2275,7 @@ wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds,
__wasi_filesize_t nbytes = 0;
if (in[i].u.type == __WASI_EVENTTYPE_FD_READ) {
int l;
if (ioctl(fos[i]->file_handle, FIONREAD, &l) == 0)
if (os_ioctl(fos[i]->file_handle, FIONREAD, &l) == 0)
nbytes = (__wasi_filesize_t)l;
}
if ((pfds[i].revents & POLLNVAL) != 0) {
@ -2436,7 +2441,7 @@ wasi_addr_to_string(const __wasi_addr_t *addr, char *buf, size_t buflen)
if (addr->kind == IPv4) {
const char *format = "%u.%u.%u.%u";
assert(buflen >= 16);
bh_assert(buflen >= 16);
snprintf(buf, buflen, format, addr->addr.ip4.addr.n0,
addr->addr.ip4.addr.n1, addr->addr.ip4.addr.n2,
@ -2448,14 +2453,13 @@ wasi_addr_to_string(const __wasi_addr_t *addr, char *buf, size_t buflen)
const char *format = "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x";
__wasi_addr_ip6_t ipv6 = addr->addr.ip6.addr;
assert(buflen >= 40);
bh_assert(buflen >= 40);
snprintf(buf, buflen, format, ipv6.n0, ipv6.n1, ipv6.n2, ipv6.n3,
ipv6.h0, ipv6.h1, ipv6.h2, ipv6.h3);
return true;
}
return false;
}
@ -2562,9 +2566,11 @@ wasi_ssp_sock_connect(wasm_exec_env_t exec_env, struct fd_table *curfds,
}
error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_BIND, 0);
if (error != __WASI_ESUCCESS)
if (error != __WASI_ESUCCESS) {
return error;
}
/* Consume __wasi_addr_t */
ret = blocking_op_socket_connect(exec_env, fo->file_handle, buf,
addr->kind == IPv4 ? addr->addr.ip4.port
: addr->addr.ip6.port);
@ -2713,10 +2719,10 @@ wasi_ssp_sock_open(wasm_exec_env_t exec_env, struct fd_table *curfds,
}
if (SOCKET_DGRAM == socktype) {
assert(wasi_type == __WASI_FILETYPE_SOCKET_DGRAM);
bh_assert(wasi_type == __WASI_FILETYPE_SOCKET_DGRAM);
}
else {
assert(wasi_type == __WASI_FILETYPE_SOCKET_STREAM);
bh_assert(wasi_type == __WASI_FILETYPE_SOCKET_STREAM);
}
// TODO: base rights and inheriting rights ?
@ -2834,6 +2840,9 @@ wasmtime_ssp_sock_recv_from(wasm_exec_env_t exec_env, struct fd_table *curfds,
return error;
}
wasi_addr_to_bh_sockaddr(src_addr, &sockaddr);
/* Consume bh_sockaddr_t instead of __wasi_addr_t */
ret = blocking_op_socket_recv_from(exec_env, fo->file_handle, buf, buf_len,
0, &sockaddr);
fd_object_release(exec_env, fo);
@ -2899,6 +2908,7 @@ wasmtime_ssp_sock_send_to(wasm_exec_env_t exec_env, struct fd_table *curfds,
wasi_addr_to_bh_sockaddr(dest_addr, &sockaddr);
/* Consume bh_sockaddr instead of __wasi_addr_t */
ret = blocking_op_socket_send_to(exec_env, fo->file_handle, buf, buf_len, 0,
&sockaddr);
fd_object_release(exec_env, fo);
@ -2930,8 +2940,10 @@ wasmtime_ssp_sock_shutdown(wasm_exec_env_t exec_env, struct fd_table *curfds,
__wasi_errno_t
wasmtime_ssp_sched_yield(void)
{
#ifdef BH_PLATFORM_WINDOWS
#if defined(BH_PLATFORM_WINDOWS)
SwitchToThread();
#elif defined(BH_PLATFORM_ZEPHYR)
k_yield();
#else
if (sched_yield() < 0)
return convert_errno(errno);

View File

@ -66,6 +66,20 @@ random_buf(void *buf, size_t len)
return ret ? __WASI_EINVAL : __WASI_ESUCCESS;
}
#elif defined(BH_PLATFORM_ZEPHYR)
#include <zephyr/random/random.h>
// Maybe having an OS abstraction api would be a good idea
// because every platform is implementing this function.
// we could have a function like `os_random_buf`
// and call `os_random_buf.` in the SSP wrapper `random_buf`.
__wasi_errno_t
random_buf(void *buf, size_t len)
{
sys_rand_get(buf, len);
return __WASI_ESUCCESS;
}
#else
static int urandom = -1;

View File

@ -42,7 +42,8 @@
#define CONFIG_HAS_GETRANDOM 0
#endif
#if defined(__CloudABI__) || defined(BH_PLATFORM_FREERTOS)
#if defined(__CloudABI__) || defined(BH_PLATFORM_FREERTOS) \
|| defined(BH_PLATFORM_ZEPHYR)
#define CONFIG_HAS_CAP_ENTER 1
#else
#define CONFIG_HAS_CAP_ENTER 0
@ -50,7 +51,7 @@
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__EMSCRIPTEN__) \
&& !defined(ESP_PLATFORM) && !defined(DISABLE_CLOCK_NANOSLEEP) \
&& !defined(BH_PLATFORM_FREERTOS)
&& !defined(BH_PLATFORM_FREERTOS) && !defined(BH_PLATFORM_ZEPHYR)
#define CONFIG_HAS_CLOCK_NANOSLEEP 1
#else
#define CONFIG_HAS_CLOCK_NANOSLEEP 0
@ -63,7 +64,8 @@
#endif
#if !defined(__APPLE__) && !defined(BH_PLATFORM_LINUX_SGX) && !defined(_WIN32) \
&& !defined(__COSMOPOLITAN__) && !defined(BH_PLATFORM_FREERTOS)
&& !defined(__COSMOPOLITAN__) && !defined(BH_PLATFORM_FREERTOS) \
&& !defined(BH_PLATFORM_ZEPHYR)
#define CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK 1
#else
#define CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK 0

View File

@ -75,6 +75,9 @@ int isnan(double x);
typedef int os_file_handle;
typedef void *os_dir_stream;
typedef int os_raw_file_handle;
typedef int os_poll_file_handle;
typedef unsigned int os_nfds_t;
typedef int os_timespec;
static inline os_file_handle
os_get_invalid_handle(void)

View File

@ -150,6 +150,12 @@ typedef int os_file_handle;
typedef DIR *os_dir_stream;
typedef int os_raw_file_handle;
/* The below types are used in platform_api_extension.h,
we just define them to make the compiler happy */
typedef struct pollfd os_poll_file_handle;
typedef nfds_t os_nfds_t;
typedef struct timespec os_timespec;
static inline os_file_handle
os_get_invalid_handle(void)
{

View File

@ -1032,4 +1032,32 @@ char *
os_realpath(const char *path, char *resolved_path)
{
return realpath(path, resolved_path);
}
// Better to define the function here, as Linux-SGX will
// use this file to implement the `_os` functions.
// So we don't need to define them in the Linux-SGX platform.
int
os_ioctl(os_file_handle handle, int request, ...)
{
int ret = -1;
va_list args;
va_start(args, request);
ret = ioctl(handle, request, args);
va_end(args);
return ret;
}
int
os_poll(os_poll_file_handle *fds, os_nfds_t nfs, int timeout)
{
return poll(fds, nfs, timeout);
}
bool
os_compare_file_handle(os_file_handle handle1, os_file_handle handle2)
{
return handle1 == handle2;
}

View File

@ -1031,4 +1031,4 @@ os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr)
}
return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr);
}
}

View File

@ -67,6 +67,9 @@ typedef sem_t korp_sem;
typedef int os_file_handle;
typedef DIR *os_dir_stream;
typedef int os_raw_file_handle;
typedef struct pollfd os_poll_file_handle;
typedef nfds_t os_nfds_t;
typedef struct timespec os_timespec;
static inline os_file_handle
os_get_invalid_handle(void)

View File

@ -112,6 +112,9 @@ os_set_signal_number_for_blocking_op(int signo);
typedef int os_file_handle;
typedef DIR *os_dir_stream;
typedef int os_raw_file_handle;
typedef struct pollfd os_poll_file_handle;
typedef nfds_t os_nfds_t;
typedef struct timespec os_timespec;
static inline os_file_handle
os_get_invalid_handle(void)

View File

@ -1032,4 +1032,16 @@ char *
os_realpath(const char *path, char *resolved_path)
{
return realpath(path, resolved_path);
}
int
os_ioctl(os_file_handle handle, int request, ...)
{
return BHT_ERROR;
}
int
os_poll(os_poll_file_handle *fds, os_nfds_t nfs, int timeout)
{
return BHT_ERROR;
}

View File

@ -144,6 +144,12 @@ typedef int os_file_handle;
typedef DIR *os_dir_stream;
typedef int os_raw_file_handle;
/* The below types are used in platform_api_extension.h,
we just define them to make the compiler happy */
typedef int os_poll_file_handle;
typedef unsigned int os_nfds_t;
typedef int os_timespec;
static inline os_file_handle
os_get_invalid_handle(void)
{

View File

@ -69,6 +69,9 @@ typedef sem_t korp_sem;
typedef int os_file_handle;
typedef DIR *os_dir_stream;
typedef int os_raw_file_handle;
typedef struct pollfd os_poll_file_handle;
typedef nfds_t os_nfds_t;
typedef struct timespec os_timespec;
#if WASM_DISABLE_HW_BOUND_CHECK == 0
#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \

View File

@ -1664,4 +1664,24 @@ os_clock_time_get(__wasi_clockid_t clock_id, __wasi_timestamp_t precision,
}
#endif
/* Experimental */
/* Used in posix.c around L2259 and expect the return code
* of ioctl() directly.
*/
int
os_ioctl(os_file_handle handle, int request, ...);
/* Higher level API:
* __wasi_errno_t
* blocking_op_poll(wasm_exec_env_t exec_env, os_poll_file_handle *pfds,
* os_nfds_t nfds, int timeout_ms, int *retp)
* Already format the errno and expect the return code of poll() directly.
*/
int
os_poll(os_poll_file_handle *pfds, os_nfds_t nfs, int timeout);
bool
os_compare_file_handle(os_file_handle handle1, os_file_handle handle2);
#endif /* #ifndef PLATFORM_API_EXTENSION_H */

View File

@ -173,7 +173,7 @@ typedef uint8_t __wasi_eventtype_t;
typedef uint32_t __wasi_exitcode_t;
typedef uint32_t __wasi_fd_t;
typedef int32_t __wasi_fd_t;
typedef uint16_t __wasi_fdflags_t;
#define __WASI_FDFLAG_APPEND (0x0001)
@ -539,7 +539,10 @@ typedef enum {
typedef uint16_t __wasi_ip_port_t;
typedef enum { IPv4 = 0, IPv6 } __wasi_addr_type_t;
/* Ensure that __wasi_addr_type_t has a size of 4 byte (I32).
However, it will not have the type safety of enum. */
typedef uint32_t __wasi_addr_type_t;
enum { IPv4 = 0, IPv6 };
/* n0.n1.n2.n3 */
typedef struct __wasi_addr_ip4_t {

View File

@ -73,6 +73,16 @@ typedef int os_file_handle;
typedef DIR *os_dir_stream;
typedef int os_raw_file_handle;
struct _pollfd {
int fd;
short events;
short revents;
};
typedef struct _pollfd os_poll_file_handle;
typedef unsigned long os_nfds_t;
typedef struct timespec os_timespec;
static inline os_file_handle
os_get_invalid_handle(void)
{

View File

@ -126,6 +126,10 @@ typedef int os_file_handle;
typedef DIR *os_dir_stream;
typedef int os_raw_file_handle;
typedef struct pollfd os_poll_file_handle;
typedef nfds_t os_nfds_t;
typedef struct timespec os_timespec;
static inline os_file_handle
os_get_invalid_handle(void)
{

View File

@ -132,9 +132,14 @@ fdopendir(int fd);
void
os_set_signal_number_for_blocking_op(int signo);
/* The below types are used in platform_api_extension.h,
we just define them to make the compiler happy */
typedef int os_file_handle;
typedef DIR *os_dir_stream;
typedef int os_raw_file_handle;
typedef struct pollfd os_poll_file_handle;
typedef nfds_t os_nfds_t;
typedef struct timespec os_timespec;
static inline os_file_handle
os_get_invalid_handle(void)

View File

@ -63,6 +63,9 @@ typedef struct korp_cond {
typedef int os_file_handle;
typedef void *os_dir_stream;
typedef int os_raw_file_handle;
typedef int os_poll_file_handle;
typedef unsigned int os_nfds_t;
typedef int os_timespec;
#if WA_MATH
/* clang-format off */

View File

@ -122,6 +122,9 @@ typedef rt_int64_t int64_t;
typedef int os_file_handle;
typedef void *os_dir_stream;
typedef int os_raw_file_handle;
typedef int os_poll_file_handle;
typedef unsigned int os_nfds_t;
typedef int os_timespec;
static inline os_file_handle
os_get_invalid_handle(void)

View File

@ -65,6 +65,12 @@ typedef int os_file_handle;
typedef DIR *os_dir_stream;
typedef int os_raw_file_handle;
/* The below types are used in platform_api_extension.h,
we just define them to make the compiler happy */
typedef struct pollfd os_poll_file_handle;
typedef nfds_t os_nfds_t;
typedef timespec os_timespec;
#if WASM_DISABLE_HW_BOUND_CHECK == 0
#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \
|| defined(BUILD_TARGET_AARCH64)

View File

@ -186,6 +186,12 @@ typedef uint32_t os_raw_file_handle;
#define bh_socket_t windows_handle *
/* The below types are used in platform_api_extension.h,
we just define them to make the compiler happy */
typedef int os_poll_file_handle;
typedef unsigned int os_nfds_t;
typedef struct timespec os_timespec;
// UWP apps do not have stdout/stderr handles so provide a default
// implementation of vprintf on debug builds so output from WASI libc is sent to
// the debugger and not lost completely.

View File

@ -1810,3 +1810,39 @@ os_realpath(const char *path, char *resolved_path)
return resolved_path;
}
bool
os_compare_file_handle(os_file_handle handle1, os_file_handle handle2)
{
if (handle1->type != handle2->type) {
return false;
}
if (handle1->fdflags != handle2->fdflags
|| handle1->access_mode != handle2->access_mode) {
return false;
}
switch (handle1->type) {
case windows_handle_type_file:
return handle1->raw.handle == handle2->raw.handle;
case windows_handle_type_socket:
return handle1->raw.socket == handle2->raw.socket;
default:
// Unknown handle type
return false;
}
}
int
os_ioctl(os_file_handle handle, int request, ...)
{
return BHT_ERROR;
}
// Should not be called because locked by ifdef.
int
os_poll(os_poll_file_handle *fds, os_nfds_t nfs, int timeout)
{
return BHT_ERROR;
}

View File

@ -50,6 +50,7 @@
#include <zephyr/net/net_ip.h>
#include <zephyr/net/net_core.h>
#include <zephyr/net/net_context.h>
#include <zephyr/net/socket.h>
#endif /* end of KERNEL_VERSION_NUMBER < 0x030200 */
#ifdef CONFIG_USERSPACE
@ -79,29 +80,33 @@
#define BH_PLATFORM_ZEPHYR
#endif
// Synchronization primitives for usermode
/* Synchronization primitives for usermode.
* The macros are prefixed with 'z' because when building
* with WAMR_BUILD_LIBC_WASI the same functions are defined,
* and used in the sandboxed-system-primitives (see locking.h)
*/
#ifdef CONFIG_USERSPACE
#define mutex_t struct sys_mutex
#define mutex_init(mtx) sys_mutex_init(mtx)
#define mutex_lock(mtx, timeout) sys_mutex_lock(mtx, timeout)
#define mutex_unlock(mtx) sys_mutex_unlock(mtx)
#define zmutex_t struct sys_mutex
#define zmutex_init(mtx) sys_mutex_init(mtx)
#define zmutex_lock(mtx, timeout) sys_mutex_lock(mtx, timeout)
#define zmutex_unlock(mtx) sys_mutex_unlock(mtx)
#define sem_t struct sys_sem
#define sem_init(sem, init_count, limit) sys_sem_init(sem, init_count, limit)
#define sem_give(sem) sys_sem_give(sem)
#define sem_take(sem, timeout) sys_sem_take(sem, timeout)
#define sem_count_get(sem) sys_sem_count_get(sem)
#define zsem_t struct sys_sem
#define zsem_init(sem, init_count, limit) sys_sem_init(sem, init_count, limit)
#define zsem_give(sem) sys_sem_give(sem)
#define zsem_take(sem, timeout) sys_sem_take(sem, timeout)
#define zsem_count_get(sem) sys_sem_count_get(sem)
#else /* else of CONFIG_USERSPACE */
#define mutex_t struct k_mutex
#define mutex_init(mtx) k_mutex_init(mtx)
#define mutex_lock(mtx, timeout) k_mutex_lock(mtx, timeout)
#define mutex_unlock(mtx) k_mutex_unlock(mtx)
#define zmutex_t struct k_mutex
#define zmutex_init(mtx) k_mutex_init(mtx)
#define zmutex_lock(mtx, timeout) k_mutex_lock(mtx, timeout)
#define zmutex_unlock(mtx) k_mutex_unlock(mtx)
#define sem_t struct k_sem
#define sem_init(sem, init_count, limit) k_sem_init(sem, init_count, limit)
#define sem_give(sem) k_sem_give(sem)
#define sem_take(sem, timeout) k_sem_take(sem, timeout)
#define sem_count_get(sem) k_sem_count_get(sem)
#define zsem_t struct k_sem
#define zsem_init(sem, init_count, limit) k_sem_init(sem, init_count, limit)
#define zsem_give(sem) k_sem_give(sem)
#define zsem_take(sem, timeout) k_sem_take(sem, timeout)
#define zsem_count_get(sem) k_sem_count_get(sem)
#endif /* end of CONFIG_USERSPACE */
#define BH_APPLET_PRESERVED_STACK_SIZE (2 * BH_KB)
@ -111,22 +116,32 @@
typedef struct k_thread korp_thread;
typedef korp_thread *korp_tid;
typedef mutex_t korp_mutex;
typedef zmutex_t korp_mutex;
typedef unsigned int korp_sem;
/* korp_rwlock is used in platform_api_extension.h,
we just define the type to make the compiler happy */
typedef struct {
int dummy;
} korp_rwlock;
struct os_thread_wait_node;
typedef struct os_thread_wait_node *os_thread_wait_list;
typedef struct korp_cond {
mutex_t wait_list_lock;
zmutex_t wait_list_lock;
os_thread_wait_list thread_wait_list;
} korp_cond;
typedef struct {
struct k_mutex mtx; // Mutex for exclusive access
struct k_sem sem; // Semaphore for shared access
int read_count; // Number of readers
} korp_rwlock;
// TODO: Conform to Zephyr POSIX definition of rwlock:
// struct posix_rwlock {
// struct k_sem rd_sem;
// struct k_sem wr_sem;
// struct k_sem reader_active; /* blocks WR till reader has acquired lock */
// k_tid_t wr_owner;
// };
#ifndef Z_TIMEOUT_MS
#define Z_TIMEOUT_MS(ms) ms
#endif
@ -204,14 +219,65 @@ set_exec_mem_alloc_func(exec_mem_alloc_func_t alloc_func,
/* The below types are used in platform_api_extension.h,
we just define them to make the compiler happy */
typedef int os_file_handle;
typedef void *os_dir_stream;
typedef int os_dir_stream;
typedef int os_raw_file_handle;
// handle for file system descriptor
typedef struct zephyr_fs_desc {
char *path;
union {
struct fs_file_t file;
struct fs_dir_t dir;
};
bool is_dir;
bool used;
} zephyr_fs_desc;
// definition of zephyr_handle
typedef struct zephyr_handle {
int fd;
bool is_sock;
} zephyr_handle;
typedef struct zephyr_handle *os_file_handle;
#define bh_socket_t zephyr_handle *
typedef struct zsock_pollfd os_poll_file_handle;
typedef unsigned int os_nfds_t;
// Some of these definitions will throw warning for macros
// redefinition if CONFIG_POSIX_API=y, but it's fine.
// Warning: the CONFIG_POSIX_API will surely be deprecated and
// split into more macros, so we may use some ifdefs to avoid
// the warning in the future.
#define POLLIN ZSOCK_POLLIN
#define POLLPRI ZSOCK_POLLPRI
#define POLLOUT ZSOCK_POLLOUT
#define POLLERR ZSOCK_POLLERR
#define POLLHUP ZSOCK_POLLHUP
#define POLLNVAL ZSOCK_POLLNVAL
#define FIONREAD ZFD_IOCTL_FIONREAD
typedef struct {
time_t tv_sec;
long tv_nsec;
} os_timespec;
#define CLOCK_REALTIME 1
#define CLOCK_MONOTONIC 4
// TODO: use it in sandboxed posix.c.
// int os_sched_yield(void)
// {
// k_yield();
// return 0;
// }
static inline os_file_handle
os_get_invalid_handle(void)
{
return -1;
return NULL;
}
static inline int

View File

@ -8,11 +8,20 @@ add_definitions(-DBH_PLATFORM_ZEPHYR)
include_directories(${PLATFORM_SHARED_DIR})
include_directories(${PLATFORM_SHARED_DIR}/../include)
if(${CONFIG_MINIMAL_LIBC})
include (${CMAKE_CURRENT_LIST_DIR}/../common/math/platform_api_math.cmake)
endif()
file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c)
set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_MATH_SOURCE})
if(${CONFIG_MINIMAL_LIBC})
include (${CMAKE_CURRENT_LIST_DIR}/../common/math/platform_api_math.cmake)
set (source_all ${source_all} ${PLATFORM_COMMON_MATH_SOURCE})
endif()
if (NOT WAMR_BUILD_LIBC_WASI EQUAL 1)
list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_socket.c)
list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_file.c)
list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_clock.c)
else()
include (${CMAKE_CURRENT_LIST_DIR}/../common/libc-util/platform_common_libc_util.cmake)
set(source_all ${source_all} ${PLATFORM_COMMON_LIBC_UTIL_SOURCE})
endif ()
set (PLATFORM_SHARED_SOURCE ${source_all})

View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include "platform_api_extension.h"
#include "platform_api_vmcore.h"
#include "libc_errno.h"
#include <zephyr/kernel.h>
/* Notes:
* We are using the same implementation for __WASI_CLOCK_REALTIME and
* __WASI_CLOCK_MONOTONIC, because it is a practical solution when there
* is no RTC or external time source available.
* The implementation is based on the Zephyr `k_cycle_get_32()` function or
* the 64bits variant if available.
* We could have used `k_uptime_get()` instead, but it is not as precise,
* it has a millisecond resolution or depend on CONFIG_SYS_CLOCK_TICKS_PER_SEC.
* Feel free to change the implementation if you have a better solution.
* May look at
* https://github.com/zephyrproject-rtos/zephyr/blob/main/lib/posix/options/clock.c
* for reference.
*/
#define NANOSECONDS_PER_SECOND 1000000000ULL
__wasi_errno_t
os_clock_res_get(__wasi_clockid_t clock_id, __wasi_timestamp_t *resolution)
{
switch (clock_id) {
case __WASI_CLOCK_PROCESS_CPUTIME_ID:
case __WASI_CLOCK_THREAD_CPUTIME_ID:
return __WASI_ENOTSUP;
case __WASI_CLOCK_REALTIME:
case __WASI_CLOCK_MONOTONIC:
*resolution =
NANOSECONDS_PER_SECOND / CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC;
return __WASI_ESUCCESS;
default:
return __WASI_EINVAL;
}
}
__wasi_errno_t
os_clock_time_get(__wasi_clockid_t clock_id, __wasi_timestamp_t precision,
__wasi_timestamp_t *time)
{
(void)precision;
switch (clock_id) {
case __WASI_CLOCK_PROCESS_CPUTIME_ID:
case __WASI_CLOCK_THREAD_CPUTIME_ID:
return __WASI_ENOTSUP;
case __WASI_CLOCK_REALTIME:
case __WASI_CLOCK_MONOTONIC:
#ifdef CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER
*time = k_cycle_get_64();
#else
*time = k_cycle_get_32();
#endif
return __WASI_ESUCCESS;
default:
return __WASI_EINVAL;
}
}

View File

@ -0,0 +1,986 @@
/*
* Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include "platform_api_extension.h"
#include "libc_errno.h"
#include <string.h>
#include <stdlib.h>
#include <zephyr/fs/fs.h>
#include <zephyr/fs/fs_interface.h>
#include <zephyr/fs/fs_sys.h>
#include <zephyr/fs/littlefs.h>
/* Notes:
* This is the implementation of a POSIX-like file system interface for Zephyr.
* To manage our file descriptors, we created a struct `zephyr_fs_desc` that
* represent a zephyr file descriptor and hold useful informations.
* We also created a file descriptor table to keep track of all the file
* descriptors.
* To pass the file descriptor reference to the higher level abstraction, we
* pass the index of the fd table to an `os_file_handle` struct.
* Then in the WASI implementation layer we can retrieve the file descriptor
* reference.
* We also fake the stdin, stdout and stderr file descriptors.
* We redirect the write operation on stdin, stdout and stderr to `os_printf`.
* We do not handle write on stdin and read on stdin, stdout and stderr.
*/
// No OS API wrapper (Zephyr):
// file:
// off_t fs_tell(struct fs_file_t *zfp)
// file system:
// int fs_mount(struct fs_mount_t *mp)
// int fs_unmount(struct fs_mount_t *mp
// int fs_readmount(int *index, const char **name)
// int fs_statvfs(const char *path, struct fs_statvfs *stat)
// int fs_mkfs(int fs_type, uintptr_t dev_id, void *cfg, int flags)
// int fs_register(int type, const struct fs_file_system_t *fs)
// int fs_unregister(int type, const struct fs_file_system_t *fs)
// We will take the maximum number of open files
// from the Zephyr POSIX configuration
#define CONFIG_WASI_MAX_OPEN_FILES 16
// Macro to retrieve a file system descriptor and check it's validity.
#define GET_FILE_SYSTEM_DESCRIPTOR(fd, ptr) \
do { \
k_mutex_lock(&desc_array_mutex, K_FOREVER); \
ptr = &desc_array[(int)fd]; \
if (!ptr) { \
k_mutex_unlock(&desc_array_mutex); \
return __WASI_EBADF; \
} \
k_mutex_unlock(&desc_array_mutex); \
} while (0)
// Array to keep track of file system descriptors.
static struct zephyr_fs_desc desc_array[CONFIG_WASI_MAX_OPEN_FILES];
// mutex to protect the file descriptor array
K_MUTEX_DEFINE(desc_array_mutex);
static char prestat_dir[MAX_FILE_NAME + 1];
static struct zephyr_fs_desc *
zephyr_fs_alloc_obj(bool is_dir, const char *path, int *index)
{
int i;
struct zephyr_fs_desc *ptr = NULL;
*index = -1; // give a default value to index in case table is full
k_mutex_lock(&desc_array_mutex, K_FOREVER);
for (i = 0; i < CONFIG_WASI_MAX_OPEN_FILES; i++) {
if (desc_array[i].used == false) {
ptr = &desc_array[i];
ptr->used = true;
ptr->is_dir = is_dir;
ptr->path = strdup(path);
if (ptr->path == NULL) {
ptr->used = false;
k_mutex_unlock(&desc_array_mutex);
return NULL;
}
*index = i;
break;
}
}
k_mutex_unlock(&desc_array_mutex);
return ptr;
}
static inline void
zephyr_fs_free_obj(struct zephyr_fs_desc *ptr)
{
free(ptr->path);
ptr->path = NULL;
ptr->used = false;
}
void
debug_zephyr_fs_desc(const zephyr_fs_desc *desc)
{
if (desc == NULL) {
os_printf("Descriptor is NULL\n");
return;
}
os_printf("Descriptor found at %p\n", desc);
os_printf(" Path: %s\n", desc->path ? desc->path : "NULL");
os_printf(" Is Directory: %s\n", desc->is_dir ? "Yes" : "No");
os_printf(" Used: %s\n", desc->used ? "Yes" : "No");
if (desc->is_dir) {
os_printf(" Directory: %p\n", desc->dir);
}
else {
os_printf(" File: %p\n", desc->file);
}
}
/* /!\ Needed for socket to work */
__wasi_errno_t
os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf)
{
struct zephyr_fs_desc *ptr = NULL;
int socktype, rc;
if (!handle->is_sock) {
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
// debug_zephyr_fs_desc(ptr);
/* We treat the case of std[in/out/err] */
if (ptr->path != NULL
&& (!strcmp(ptr->path, "stdin") || !strcmp(ptr->path, "stdout")
|| !strcmp(ptr->path, "stderr"))) {
buf->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE;
buf->st_size = 0;
buf->st_atim = 0;
buf->st_mtim = 0;
buf->st_ctim = 0;
return __WASI_ESUCCESS;
}
return os_fstatat(handle, ptr->path, buf, 0);
}
else {
// socklen_t socktypelen = sizeof(socktype);
// rc = zsock_getsockopt(handle->fd, SOL_SOCKET, SO_TYPE, &socktype,
// &socktypelen); Using `zsock_getsockopt` will add a dependency on the
// network stack
// TODO: may add a type to the `zephyr_handle`.
rc = 1;
socktype = SOCK_STREAM;
if (rc < 0) {
return convert_errno(-rc);
}
switch (socktype) {
case SOCK_DGRAM:
buf->st_filetype = __WASI_FILETYPE_SOCKET_DGRAM;
break;
case SOCK_STREAM:
buf->st_filetype = __WASI_FILETYPE_SOCKET_STREAM;
break;
default:
buf->st_filetype = __WASI_FILETYPE_UNKNOWN;
break;
}
buf->st_size = 0;
buf->st_atim = 0;
buf->st_mtim = 0;
buf->st_ctim = 0;
return __WASI_ESUCCESS;
}
}
__wasi_errno_t
os_fstatat(os_file_handle handle, const char *path,
struct __wasi_filestat_t *buf, __wasi_lookupflags_t lookup_flags)
{
struct fs_dirent entry;
int rc;
if (handle->fd < 0) {
return __WASI_EBADF;
}
// Get file information using Zephyr's fs_stat function
rc = fs_stat(path, &entry);
if (rc < 0) {
return convert_errno(-rc);
}
// Fill in the __wasi_filestat_t structure
buf->st_dev = 0; // Zephyr's fs_stat doesn't provide a device ID
buf->st_ino = 0; // Zephyr's fs_stat doesn't provide an inode number
buf->st_filetype = entry.type == FS_DIR_ENTRY_DIR
? __WASI_FILETYPE_DIRECTORY
: __WASI_FILETYPE_REGULAR_FILE;
buf->st_nlink = 1; // Zephyr's fs_stat doesn't provide a link count
buf->st_size = entry.size;
buf->st_atim = 0; // Zephyr's fs_stat doesn't provide timestamps
buf->st_mtim = 0;
buf->st_ctim = 0;
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags)
{
struct zephyr_fs_desc *ptr = NULL;
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
if ((ptr->file.flags & FS_O_APPEND) != 0) {
*flags |= __WASI_FDFLAG_APPEND;
}
/* Others flags:
* - __WASI_FDFLAG_DSYNC
* - __WASI_FDFLAG_RSYNC
* - __WASI_FDFLAG_SYNC
* - __WASI_FDFLAG_NONBLOCK
* Have no equivalent in Zephyr.
*/
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags)
{
struct zephyr_fs_desc *ptr = NULL;
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
if ((flags & __WASI_FDFLAG_APPEND) != 0) {
ptr->file.flags |= FS_O_APPEND;
}
/* Same as above */
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_fdatasync(os_file_handle handle)
{
return os_fsync(handle);
}
__wasi_errno_t
os_fsync(os_file_handle handle)
{
struct zephyr_fs_desc *ptr = NULL;
int rc = 0;
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
if (ptr->is_dir) {
return __WASI_EISDIR;
}
rc = fs_sync(&ptr->file);
if (rc < 0) {
return convert_errno(-rc);
}
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_open_preopendir(const char *path, os_file_handle *out)
{
int rc, index;
struct zephyr_fs_desc *ptr;
*out = BH_MALLOC(sizeof(struct zephyr_handle));
if (*out == NULL) {
return __WASI_ENOMEM;
}
ptr = zephyr_fs_alloc_obj(true, path, &index);
if (ptr == NULL) {
BH_FREE(*out);
return __WASI_EMFILE;
}
fs_dir_t_init(&ptr->dir);
rc = fs_opendir(&ptr->dir, path);
if (rc < 0) {
zephyr_fs_free_obj(ptr);
BH_FREE(*out);
return convert_errno(-rc);
}
(*out)->fd = index;
(*out)->is_sock = false;
strncpy(prestat_dir, path, MAX_FILE_NAME + 1);
prestat_dir[MAX_FILE_NAME] = '\0';
return __WASI_ESUCCESS;
}
static int
wasi_flags_to_zephyr(__wasi_oflags_t oflags, __wasi_fdflags_t fd_flags,
__wasi_lookupflags_t lookup_flags,
wasi_libc_file_access_mode access_mode)
{
int mode = 0;
// Convert open flags.
if ((oflags & __WASI_O_CREAT) != 0) {
mode |= FS_O_CREATE;
}
if (((oflags & __WASI_O_EXCL) != 0) || ((oflags & __WASI_O_TRUNC) != 0)
|| ((oflags & __WASI_O_DIRECTORY) != 0)) {
/* Zephyr is not POSIX no equivalent for these flags */
/* __WASI_O_DIRECTORY: Open shouldn't handle directories */
// TODO: log warning
}
// Convert file descriptor flags.
if ((fd_flags & __WASI_FDFLAG_APPEND) != 0) {
mode |= FS_O_APPEND;
}
if (((fd_flags & __WASI_FDFLAG_DSYNC) != 0)
|| ((fd_flags & __WASI_FDFLAG_RSYNC) != 0)
|| ((fd_flags & __WASI_FDFLAG_SYNC) != 0)
|| ((fd_flags & __WASI_FDFLAG_NONBLOCK) != 0)) {
/* Zephyr is not POSIX no equivalent for these flags */
// TODO: log warning
}
// Convert lookup flag.
if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0) {
/* Zephyr is not POSIX no equivalent for these flags */
// TODO: log warning
return __WASI_ENOTSUP;
}
// Convert access mode.
switch (access_mode) {
case WASI_LIBC_ACCESS_MODE_READ_WRITE:
mode |= FS_O_RDWR;
break;
case WASI_LIBC_ACCESS_MODE_READ_ONLY:
mode |= FS_O_READ;
break;
case WASI_LIBC_ACCESS_MODE_WRITE_ONLY:
mode |= FS_O_WRITE;
break;
default:
// TODO: log warning
break;
}
return mode;
}
__wasi_errno_t
os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags,
__wasi_fdflags_t fd_flags, __wasi_lookupflags_t lookup_flags,
wasi_libc_file_access_mode access_mode, os_file_handle *out)
{
/*
* `handle` will be unused because zephyr doesn't expose an openat
* function and don't seem to have the concept of relative path.
* We fill `out` with a new file descriptor.
*/
int rc, index;
struct zephyr_fs_desc *ptr = NULL;
char abs_path[MAX_FILE_NAME + 1];
*out = BH_MALLOC(sizeof(struct zephyr_handle));
if (*out == NULL) {
return __WASI_ENOMEM;
}
snprintf(abs_path, MAX_FILE_NAME, "%s/%s", prestat_dir, path);
int zmode =
wasi_flags_to_zephyr(oflags, fd_flags, lookup_flags, access_mode);
ptr = zephyr_fs_alloc_obj(false, abs_path, &index);
if (!ptr && (index < 0)) {
BH_FREE(*out);
return __WASI_EMFILE;
}
fs_file_t_init(&ptr->file);
rc = fs_open(&ptr->file, abs_path, zmode);
if (rc < 0) {
zephyr_fs_free_obj(ptr);
BH_FREE(*out);
return convert_errno(-rc);
}
(*out)->fd = index;
(*out)->is_sock = false;
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_file_get_access_mode(os_file_handle handle,
wasi_libc_file_access_mode *access_mode)
{
struct zephyr_fs_desc *ptr = NULL;
if (handle->is_sock) {
// for socket we can use the following code
// TODO: Need to determine better logic
*access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE;
return __WASI_ESUCCESS;
}
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
if ((ptr->file.flags & FS_O_RDWR) != 0) {
*access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE;
}
else if ((ptr->file.flags & FS_O_READ) != 0) {
*access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY;
}
else if ((ptr->file.flags & FS_O_WRITE) != 0) {
*access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY;
}
else {
// we return read/write by default
*access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE;
}
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_close(os_file_handle handle, bool is_stdio)
{
int rc;
struct zephyr_fs_desc *ptr = NULL;
if (is_stdio)
return __WASI_ESUCCESS;
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
rc = ptr->is_dir ? fs_closedir(&ptr->dir) : fs_close(&ptr->file);
zephyr_fs_free_obj(ptr); // free in any case.
BH_FREE(handle);
if (rc < 0) {
return convert_errno(-rc);
}
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt,
__wasi_filesize_t offset, size_t *nread)
{
struct zephyr_fs_desc *ptr = NULL;
int rc;
ssize_t total_read = 0;
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
// Seek to the offset
rc = fs_seek(&ptr->file, offset, FS_SEEK_SET);
if (rc < 0) {
return convert_errno(-rc);
}
// Read data into each buffer
for (int i = 0; i < iovcnt; i++) {
ssize_t bytes_read = fs_read(&ptr->file, iov[i].buf, iov[i].buf_len);
if (bytes_read < 0) {
return convert_errno(-bytes_read);
}
total_read += bytes_read;
// If we read less than we asked for, stop reading
if (bytes_read < iov[i].buf_len) {
break;
}
}
*nread = total_read;
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt,
__wasi_filesize_t offset, size_t *nwritten)
{
struct zephyr_fs_desc *ptr = NULL;
ssize_t total_written = 0;
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
// Seek to the offset
int rc = fs_seek(&ptr->file, offset, FS_SEEK_SET);
if (rc < 0) {
return convert_errno(-rc);
}
// Write data from each buffer
for (int i = 0; i < iovcnt; i++) {
ssize_t bytes_written =
fs_write(&ptr->file, iov[i].buf, iov[i].buf_len);
if (bytes_written < 0) {
return convert_errno(-bytes_written);
}
total_written += bytes_written;
// If we wrote less than we asked for, stop writing
if (bytes_written < iov[i].buf_len) {
break;
}
}
*nwritten = total_written;
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt,
size_t *nread)
{
struct zephyr_fs_desc *ptr = NULL;
ssize_t total_read = 0;
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
// Read data into each buffer
for (int i = 0; i < iovcnt; i++) {
ssize_t bytes_read = fs_read(&ptr->file, iov[i].buf, iov[i].buf_len);
if (bytes_read < 0) {
// If an error occurred, return it
return convert_errno(-bytes_read);
}
total_read += bytes_read;
// If we read less than we asked for, stop reading
if (bytes_read < iov[i].buf_len) {
break;
}
}
*nread = total_read;
return __WASI_ESUCCESS;
}
/* With wasi-libc we need to redirect write on stdout/err to printf */
// TODO: handle write on stdin
__wasi_errno_t
os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt,
size_t *nwritten)
{
struct zephyr_fs_desc *ptr = NULL;
ssize_t total_written = 0;
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
if (strncmp(ptr->path, "std", 3) == 0) {
// for std[in/out/err] we don't write because they are not real opened
// files. Instead we emulate a write operation to make it work with
// printf.
for (int i = 0; i < iovcnt; i++) {
if (iov[i].buf_len == 0)
continue;
os_printf("%s", (char *)iov[i].buf);
total_written += iov[i].buf_len;
// Clear the buffer after printing
memset(iov[i].buf, 0, iov[i].buf_len);
}
*nwritten = total_written;
return __WASI_ESUCCESS;
}
// Write data from each buffer
for (int i = 0; i < iovcnt; i++) {
ssize_t bytes_written =
fs_write(&ptr->file, iov[i].buf, iov[i].buf_len);
if (bytes_written < 0) {
return convert_errno(-bytes_written);
}
total_written += bytes_written;
// If we wrote less than we asked for, stop writing
if (bytes_written < iov[i].buf_len) {
break;
}
}
*nwritten = total_written;
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_fallocate(os_file_handle handle, __wasi_filesize_t offset,
__wasi_filesize_t length)
{
return __WASI_ENOSYS;
}
__wasi_errno_t
os_ftruncate(os_file_handle handle, __wasi_filesize_t size)
{
struct zephyr_fs_desc *ptr = NULL;
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
int rc = fs_truncate(&ptr->file, (off_t)size);
if (rc < 0) {
return convert_errno(-rc);
}
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_futimens(os_file_handle handle, __wasi_timestamp_t access_time,
__wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags)
{
return __WASI_ENOSYS;
}
__wasi_errno_t
os_utimensat(os_file_handle handle, const char *path,
__wasi_timestamp_t access_time,
__wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags,
__wasi_lookupflags_t lookup_flags)
{
return __WASI_ENOSYS;
}
__wasi_errno_t
os_readlinkat(os_file_handle handle, const char *path, char *buf,
size_t bufsize, size_t *nread)
{
return __WASI_ENOSYS;
}
__wasi_errno_t
os_linkat(os_file_handle from_handle, const char *from_path,
os_file_handle to_handle, const char *to_path,
__wasi_lookupflags_t lookup_flags)
{
return __WASI_ENOSYS;
}
__wasi_errno_t
os_symlinkat(const char *old_path, os_file_handle handle, const char *new_path)
{
return __WASI_ENOSYS;
}
__wasi_errno_t
os_mkdirat(os_file_handle handle, const char *path)
{
struct zephyr_fs_desc *ptr = NULL;
int index, rc;
char abs_path[MAX_FILE_NAME + 1];
if (handle == NULL) {
return __WASI_EINVAL; // Or another appropriate error code
}
snprintf(abs_path, MAX_FILE_NAME, "%s/%s", prestat_dir, path);
rc = fs_mkdir(abs_path);
if (rc < 0) {
return convert_errno(-rc);
}
ptr = zephyr_fs_alloc_obj(true, abs_path, &index);
if (!ptr) {
fs_unlink(abs_path);
return __WASI_EMFILE;
}
fs_dir_t_init(&ptr->dir);
handle->fd = index;
handle->is_sock = false;
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_renameat(os_file_handle old_handle, const char *old_path,
os_file_handle new_handle, const char *new_path)
{
/* `old_handle` need to be the the fd of the file to rename.
* `new_handle` will not be used.
* paths need to be absolute, no relative path will be accepted.
*/
struct zephyr_fs_desc *ptr = NULL;
(void)new_handle;
char abs_old_path[MAX_FILE_NAME + 1];
char abs_new_path[MAX_FILE_NAME + 1];
snprintf(abs_old_path, MAX_FILE_NAME, "%s/%s", prestat_dir, old_path);
snprintf(abs_new_path, MAX_FILE_NAME, "%s/%s", prestat_dir, new_path);
int rc = fs_rename(abs_old_path, abs_new_path);
if (rc < 0) {
return convert_errno(-rc);
}
GET_FILE_SYSTEM_DESCRIPTOR(old_handle->fd, ptr);
ptr->path = strdup(new_path);
if (ptr->path == NULL) {
ptr->path = old_path;
return __WASI_ENOMEM;
}
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_unlinkat(os_file_handle handle, const char *path, bool is_dir)
{
/* `old_handle` need to be the the fd of the file to unlink.
* `path` need to be absolute, relative path will not be resolved.
*/
char abs_path[MAX_FILE_NAME + 1];
struct zephyr_fs_desc *ptr = NULL;
snprintf(abs_path, MAX_FILE_NAME, "%s/%s", prestat_dir, path);
if (is_dir) {
return __WASI_ENOTDIR;
}
int rc = fs_unlink(path);
if (rc < 0) {
return convert_errno(-rc);
}
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
zephyr_fs_free_obj(ptr);
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_lseek(os_file_handle handle, __wasi_filedelta_t offset,
__wasi_whence_t whence, __wasi_filesize_t *new_offset)
{
struct zephyr_fs_desc *ptr = NULL;
int zwhence;
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
// They have the same value but this is more explicit
switch (whence) {
case __WASI_WHENCE_SET:
zwhence = FS_SEEK_SET;
break;
case __WASI_WHENCE_CUR:
zwhence = FS_SEEK_CUR;
break;
case __WASI_WHENCE_END:
zwhence = FS_SEEK_END;
break;
default:
return __WASI_EINVAL;
}
off_t rc = fs_seek(&ptr->file, (off_t)offset, zwhence);
if (rc < 0) {
return convert_errno(-rc);
}
*new_offset = (__wasi_filesize_t)rc;
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_fadvise(os_file_handle handle, __wasi_filesize_t offset,
__wasi_filesize_t length, __wasi_advice_t advice)
{
return __WASI_ENOSYS;
}
__wasi_errno_t
os_isatty(os_file_handle handle)
{
return __WASI_ENOSYS;
}
os_file_handle
os_convert_stdin_handle(os_raw_file_handle raw_stdin)
{
os_file_handle handle = BH_MALLOC(sizeof(struct zephyr_handle));
if (handle == NULL) {
return NULL;
}
/* We allocate a fake stdin reference */
if (zephyr_fs_alloc_obj(false, "stdin", &handle->fd) == NULL) {
BH_FREE(handle);
return NULL;
}
handle->is_sock = false;
return handle;
}
os_file_handle
os_convert_stdout_handle(os_raw_file_handle raw_stdout)
{
os_file_handle handle = BH_MALLOC(sizeof(struct zephyr_handle));
if (handle == NULL) {
return NULL;
}
/* We allocate a fake stdin reference */
if (zephyr_fs_alloc_obj(false, "stdout", &handle->fd) == NULL) {
BH_FREE(handle);
return NULL;
}
handle->is_sock = false;
return handle;
}
os_file_handle
os_convert_stderr_handle(os_raw_file_handle raw_stderr)
{
os_file_handle handle = BH_MALLOC(sizeof(struct zephyr_handle));
if (handle == NULL) {
return NULL;
}
/* We allocate a fake stdin reference */
if (zephyr_fs_alloc_obj(false, "stderr", &handle->fd) == NULL) {
BH_FREE(handle);
return NULL;
}
handle->is_sock = false;
return handle;
}
__wasi_errno_t
os_fdopendir(os_file_handle handle, os_dir_stream *dir_stream)
{
/* Here we assume that either mdkdir or preopendir was called
* before otherwise function will fail.
*/
struct zephyr_fs_desc *ptr = NULL;
GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
if (!ptr->is_dir) {
return __WASI_ENOTDIR;
}
int rc = fs_opendir(&ptr->dir, ptr->path);
if (rc < 0) {
return convert_errno(-rc);
}
/* we store the fd in the `os_dir_stream` to use other function */
*dir_stream = handle->fd;
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_rewinddir(os_dir_stream dir_stream)
{
return __WASI_ENOSYS;
}
__wasi_errno_t
os_seekdir(os_dir_stream dir_stream, __wasi_dircookie_t position)
{
return __WASI_ENOSYS;
}
__wasi_errno_t
os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry,
const char **d_name)
{
struct fs_dirent fs_entry;
struct zephyr_fs_desc *ptr = NULL;
GET_FILE_SYSTEM_DESCRIPTOR(dir_stream, ptr);
if (!ptr->is_dir) {
return __WASI_ENOTDIR;
}
int rc = fs_readdir(&ptr->dir, &fs_entry);
if (rc < 0) {
return convert_errno(-rc);
}
entry->d_next = 0; // default value to start of the directory.
entry->d_ino = 0; // no inode in zephyr
entry->d_namlen = strlen(fs_entry.name);
entry->d_type = fs_entry.type == FS_DIR_ENTRY_DIR
? __WASI_FILETYPE_DIRECTORY
: __WASI_FILETYPE_REGULAR_FILE;
return __WASI_ESUCCESS;
}
__wasi_errno_t
os_closedir(os_dir_stream dir_stream)
{
struct zephyr_fs_desc *ptr = NULL;
GET_FILE_SYSTEM_DESCRIPTOR(dir_stream, ptr);
if (!ptr->is_dir) {
return __WASI_ENOTDIR;
}
int rc = fs_closedir(&ptr->dir);
zephyr_fs_free_obj(ptr); // free in any case.
if (rc < 0) {
return convert_errno(-rc);
}
return __WASI_ESUCCESS;
}
os_dir_stream
os_get_invalid_dir_stream()
{
return NULL;
}
bool
os_is_dir_stream_valid(os_dir_stream *dir_stream)
{
return false;
}
bool
os_is_handle_valid(os_file_handle *handle)
{
if (handle == NULL || *handle == NULL) {
return false;
}
return (*handle)->fd > -1;
}
char *
os_realpath(const char *path, char *resolved_path)
{
/* In fact we could implement a path resolving method, because every paths
* are at one point put into memory.
* We could then maintain a 'tree' to represent the file system.
* --> The file system root is easily accessable with:
* * (fs_dir_t) dir.mp->mnt_point
* * (fs_file_t) file.mp->mnt_point
* But we will just use absolute path for now.
*/
if (!path) {
// Log error
return NULL;
}
return (const char *)path;
}
bool
os_compare_file_handle(os_file_handle handle1, os_file_handle handle2)
{
return handle1->fd == handle2->fd && handle1->is_sock == handle2->is_sock;
}

File diff suppressed because it is too large Load Diff

View File

@ -34,22 +34,22 @@
static K_THREAD_STACK_ARRAY_DEFINE(mpu_stacks, BH_ZEPHYR_MPU_STACK_COUNT,
BH_ZEPHYR_MPU_STACK_SIZE);
static bool mpu_stack_allocated[BH_ZEPHYR_MPU_STACK_COUNT];
static mutex_t mpu_stack_lock;
static zmutex_t mpu_stack_lock;
static char *
mpu_stack_alloc()
{
int i;
mutex_lock(&mpu_stack_lock, K_FOREVER);
zmutex_lock(&mpu_stack_lock, K_FOREVER);
for (i = 0; i < BH_ZEPHYR_MPU_STACK_COUNT; i++) {
if (!mpu_stack_allocated[i]) {
mpu_stack_allocated[i] = true;
mutex_unlock(&mpu_stack_lock);
zmutex_unlock(&mpu_stack_lock);
return (char *)mpu_stacks[i];
}
}
mutex_unlock(&mpu_stack_lock);
zmutex_unlock(&mpu_stack_lock);
return NULL;
}
@ -58,17 +58,17 @@ mpu_stack_free(char *stack)
{
int i;
mutex_lock(&mpu_stack_lock, K_FOREVER);
zmutex_lock(&mpu_stack_lock, K_FOREVER);
for (i = 0; i < BH_ZEPHYR_MPU_STACK_COUNT; i++) {
if ((char *)mpu_stacks[i] == stack)
mpu_stack_allocated[i] = false;
}
mutex_unlock(&mpu_stack_lock);
zmutex_unlock(&mpu_stack_lock);
}
#endif
typedef struct os_thread_wait_node {
sem_t sem;
zsem_t sem;
os_thread_wait_list next;
} os_thread_wait_node;
@ -80,7 +80,7 @@ typedef struct os_thread_data {
/* Jeff thread local root */
void *tlr;
/* Lock for waiting list */
mutex_t wait_list_lock;
zmutex_t wait_list_lock;
/* Waiting list of other threads who are joining this thread */
os_thread_wait_list thread_wait_list;
/* Thread stack size */
@ -107,13 +107,13 @@ static bool is_thread_sys_inited = false;
static os_thread_data supervisor_thread_data;
/* Lock for thread data list */
static mutex_t thread_data_lock;
static zmutex_t thread_data_lock;
/* Thread data list */
static os_thread_data *thread_data_list = NULL;
/* Lock for thread object list */
static mutex_t thread_obj_lock;
static zmutex_t thread_obj_lock;
/* Thread object list */
static os_thread_obj *thread_obj_list = NULL;
@ -121,7 +121,7 @@ static os_thread_obj *thread_obj_list = NULL;
static void
thread_data_list_add(os_thread_data *thread_data)
{
mutex_lock(&thread_data_lock, K_FOREVER);
zmutex_lock(&thread_data_lock, K_FOREVER);
if (!thread_data_list)
thread_data_list = thread_data;
else {
@ -129,7 +129,7 @@ thread_data_list_add(os_thread_data *thread_data)
os_thread_data *p = thread_data_list;
while (p) {
if (p == thread_data) {
mutex_unlock(&thread_data_lock);
zmutex_unlock(&thread_data_lock);
return;
}
p = p->next;
@ -139,13 +139,13 @@ thread_data_list_add(os_thread_data *thread_data)
thread_data->next = thread_data_list;
thread_data_list = thread_data;
}
mutex_unlock(&thread_data_lock);
zmutex_unlock(&thread_data_lock);
}
static void
thread_data_list_remove(os_thread_data *thread_data)
{
mutex_lock(&thread_data_lock, K_FOREVER);
zmutex_lock(&thread_data_lock, K_FOREVER);
if (thread_data_list) {
if (thread_data_list == thread_data)
thread_data_list = thread_data_list->next;
@ -158,32 +158,32 @@ thread_data_list_remove(os_thread_data *thread_data)
p->next = p->next->next;
}
}
mutex_unlock(&thread_data_lock);
zmutex_unlock(&thread_data_lock);
}
static os_thread_data *
thread_data_list_lookup(k_tid_t tid)
{
mutex_lock(&thread_data_lock, K_FOREVER);
zmutex_lock(&thread_data_lock, K_FOREVER);
if (thread_data_list) {
os_thread_data *p = thread_data_list;
while (p) {
if (p->tid == tid) {
/* Found */
mutex_unlock(&thread_data_lock);
zmutex_unlock(&thread_data_lock);
return p;
}
p = p->next;
}
}
mutex_unlock(&thread_data_lock);
zmutex_unlock(&thread_data_lock);
return NULL;
}
static void
thread_obj_list_add(os_thread_obj *thread_obj)
{
mutex_lock(&thread_obj_lock, K_FOREVER);
zmutex_lock(&thread_obj_lock, K_FOREVER);
if (!thread_obj_list)
thread_obj_list = thread_obj;
else {
@ -191,14 +191,14 @@ thread_obj_list_add(os_thread_obj *thread_obj)
thread_obj->next = thread_obj_list;
thread_obj_list = thread_obj;
}
mutex_unlock(&thread_obj_lock);
zmutex_unlock(&thread_obj_lock);
}
static void
thread_obj_list_reclaim()
{
os_thread_obj *p, *p_prev;
mutex_lock(&thread_obj_lock, K_FOREVER);
zmutex_lock(&thread_obj_lock, K_FOREVER);
p_prev = NULL;
p = thread_obj_list;
while (p) {
@ -219,7 +219,7 @@ thread_obj_list_reclaim()
p = p->next;
}
}
mutex_unlock(&thread_obj_lock);
zmutex_unlock(&thread_obj_lock);
}
int
@ -229,10 +229,10 @@ os_thread_sys_init()
return BHT_OK;
#if BH_ENABLE_ZEPHYR_MPU_STACK != 0
mutex_init(&mpu_stack_lock);
zmutex_init(&mpu_stack_lock);
#endif
mutex_init(&thread_data_lock);
mutex_init(&thread_obj_lock);
zmutex_init(&thread_data_lock);
zmutex_init(&thread_obj_lock);
/* Initialize supervisor thread data */
memset(&supervisor_thread_data, 0, sizeof(supervisor_thread_data));
@ -265,19 +265,19 @@ os_thread_cleanup(void)
os_thread_data *thread_data = thread_data_current();
bh_assert(thread_data != NULL);
mutex_lock(&thread_data->wait_list_lock, K_FOREVER);
zmutex_lock(&thread_data->wait_list_lock, K_FOREVER);
if (thread_data->thread_wait_list) {
/* Signal each joining thread */
os_thread_wait_list head = thread_data->thread_wait_list;
while (head) {
os_thread_wait_list next = head->next;
sem_give(&head->sem);
zsem_give(&head->sem);
/* head will be freed by joining thread */
head = next;
}
thread_data->thread_wait_list = NULL;
}
mutex_unlock(&thread_data->wait_list_lock);
zmutex_unlock(&thread_data->wait_list_lock);
thread_data_list_remove(thread_data);
/* Set flag to true for the next thread creating to
@ -342,7 +342,7 @@ os_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start,
}
memset(thread_data, 0, thread_data_size);
mutex_init(&thread_data->wait_list_lock);
zmutex_init(&thread_data->wait_list_lock);
thread_data->stack_size = stack_size;
thread_data->tid = tid;
@ -397,14 +397,14 @@ os_thread_join(korp_tid thread, void **value_ptr)
if (!(node = BH_MALLOC(sizeof(os_thread_wait_node))))
return BHT_ERROR;
sem_init(&node->sem, 0, 1);
zsem_init(&node->sem, 0, 1);
node->next = NULL;
/* Get thread data */
thread_data = thread_data_list_lookup(thread);
bh_assert(thread_data != NULL);
mutex_lock(&thread_data->wait_list_lock, K_FOREVER);
zmutex_lock(&thread_data->wait_list_lock, K_FOREVER);
if (!thread_data->thread_wait_list)
thread_data->thread_wait_list = node;
else {
@ -414,10 +414,10 @@ os_thread_join(korp_tid thread, void **value_ptr)
p = p->next;
p->next = node;
}
mutex_unlock(&thread_data->wait_list_lock);
zmutex_unlock(&thread_data->wait_list_lock);
/* Wait the sem */
sem_take(&node->sem, K_FOREVER);
zsem_take(&node->sem, K_FOREVER);
/* Wait some time for the thread to be actually terminated */
k_sleep(Z_TIMEOUT_MS(100));
@ -430,14 +430,14 @@ os_thread_join(korp_tid thread, void **value_ptr)
int
os_mutex_init(korp_mutex *mutex)
{
mutex_init(mutex);
zmutex_init(mutex);
return BHT_OK;
}
int
os_recursive_mutex_init(korp_mutex *mutex)
{
mutex_init(mutex);
zmutex_init(mutex);
return BHT_OK;
}
@ -451,16 +451,16 @@ os_mutex_destroy(korp_mutex *mutex)
int
os_mutex_lock(korp_mutex *mutex)
{
return mutex_lock(mutex, K_FOREVER);
return zmutex_lock(mutex, K_FOREVER);
}
int
os_mutex_unlock(korp_mutex *mutex)
{
#if KERNEL_VERSION_NUMBER >= 0x020200 /* version 2.2.0 */
return mutex_unlock(mutex);
return zmutex_unlock(mutex);
#else
mutex_unlock(mutex);
zmutex_unlock(mutex);
return 0;
#endif
}
@ -468,7 +468,7 @@ os_mutex_unlock(korp_mutex *mutex)
int
os_cond_init(korp_cond *cond)
{
mutex_init(&cond->wait_list_lock);
zmutex_init(&cond->wait_list_lock);
cond->thread_wait_list = NULL;
return BHT_OK;
}
@ -489,10 +489,10 @@ os_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, bool timed, int mills)
if (!(node = BH_MALLOC(sizeof(os_thread_wait_node))))
return BHT_ERROR;
sem_init(&node->sem, 0, 1);
zsem_init(&node->sem, 0, 1);
node->next = NULL;
mutex_lock(&cond->wait_list_lock, K_FOREVER);
zmutex_lock(&cond->wait_list_lock, K_FOREVER);
if (!cond->thread_wait_list)
cond->thread_wait_list = node;
else {
@ -502,15 +502,15 @@ os_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, bool timed, int mills)
p = p->next;
p->next = node;
}
mutex_unlock(&cond->wait_list_lock);
zmutex_unlock(&cond->wait_list_lock);
/* Unlock mutex, wait sem and lock mutex again */
mutex_unlock(mutex);
sem_take(&node->sem, timed ? Z_TIMEOUT_MS(mills) : K_FOREVER);
mutex_lock(mutex, K_FOREVER);
zmutex_unlock(mutex);
zsem_take(&node->sem, timed ? Z_TIMEOUT_MS(mills) : K_FOREVER);
zmutex_lock(mutex, K_FOREVER);
/* Remove wait node from wait list */
mutex_lock(&cond->wait_list_lock, K_FOREVER);
zmutex_lock(&cond->wait_list_lock, K_FOREVER);
if (cond->thread_wait_list == node)
cond->thread_wait_list = node->next;
else {
@ -521,7 +521,7 @@ os_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, bool timed, int mills)
p->next = node->next;
}
BH_FREE(node);
mutex_unlock(&cond->wait_list_lock);
zmutex_unlock(&cond->wait_list_lock);
return BHT_OK;
}
@ -559,10 +559,10 @@ int
os_cond_signal(korp_cond *cond)
{
/* Signal the head wait node of wait list */
mutex_lock(&cond->wait_list_lock, K_FOREVER);
zmutex_lock(&cond->wait_list_lock, K_FOREVER);
if (cond->thread_wait_list)
sem_give(&cond->thread_wait_list->sem);
mutex_unlock(&cond->wait_list_lock);
zsem_give(&cond->thread_wait_list->sem);
zmutex_unlock(&cond->wait_list_lock);
return BHT_OK;
}
@ -582,6 +582,67 @@ void
os_thread_jit_write_protect_np(bool enabled)
{}
int
os_rwlock_init(korp_rwlock *lock)
{
if (!lock) {
return BHT_ERROR;
}
k_mutex_init(&lock->mtx);
k_sem_init(&lock->sem, 0, K_SEM_MAX_LIMIT);
lock->read_count = 0;
return BHT_OK;
}
int
os_rwlock_rdlock(korp_rwlock *lock)
{
/* Not implemented */
return BHT_ERROR;
}
int
os_rwlock_wrlock(korp_rwlock *lock)
{
// Acquire the mutex to ensure exclusive access
if (k_mutex_lock(&lock->mtx, K_FOREVER) != 0) {
return BHT_ERROR;
}
// Wait until there are no readers
while (lock->read_count > 0) {
// Release the mutex while we're waiting
k_mutex_unlock(&lock->mtx);
// Wait for a short time
k_sleep(K_MSEC(1));
// Re-acquire the mutex
if (k_mutex_lock(&lock->mtx, K_FOREVER) != 0) {
return BHT_ERROR;
}
}
// At this point, we hold the mutex and there are no readers, so we have the
// write lock
return BHT_OK;
}
int
os_rwlock_unlock(korp_rwlock *lock)
{
k_mutex_unlock(&lock->mtx);
return BHT_OK;
}
int
os_rwlock_destroy(korp_rwlock *lock)
{
/* Not implemented */
return BHT_ERROR;
}
int
os_thread_detach(korp_tid thread)
{
@ -601,13 +662,88 @@ int
os_cond_broadcast(korp_cond *cond)
{
os_thread_wait_node *node;
mutex_lock(&cond->wait_list_lock, K_FOREVER);
zmutex_lock(&cond->wait_list_lock, K_FOREVER);
node = cond->thread_wait_list;
while (node) {
os_thread_wait_node *next = node->next;
sem_give(&node->sem);
zsem_give(&node->sem);
node = next;
}
mutex_unlock(&cond->wait_list_lock);
zmutex_unlock(&cond->wait_list_lock);
return BHT_OK;
}
korp_sem *
os_sem_open(const char *name, int oflags, int mode, int val)
{
/* Not implemented */
return NULL;
}
int
os_sem_close(korp_sem *sem)
{
/* Not implemented */
return BHT_ERROR;
}
int
os_sem_wait(korp_sem *sem)
{
/* Not implemented */
return BHT_ERROR;
}
int
os_sem_trywait(korp_sem *sem)
{
/* Not implemented */
return BHT_ERROR;
}
int
os_sem_post(korp_sem *sem)
{
/* Not implemented */
return BHT_ERROR;
}
int
os_sem_getvalue(korp_sem *sem, int *sval)
{
/* Not implemented */
return BHT_ERROR;
}
int
os_sem_unlink(const char *name)
{
/* Not implemented */
return BHT_ERROR;
}
int
os_blocking_op_init()
{
/* Not implemented */
return BHT_ERROR;
}
void
os_begin_blocking_op()
{
/* Not implemented */
}
void
os_end_blocking_op()
{
/* Not implemented */
}
int
os_wakeup_blocking_op(korp_tid tid)
{
/* Not implemented */
return BHT_ERROR;
}

View File

@ -0,0 +1,91 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
cmake_minimum_required(VERSION 3.8.2)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(wamr)
enable_language (ASM)
set (WAMR_BUILD_PLATFORM "zephyr")
# WAMR Configuration:
set (WAMR_BUILD_TARGET "THUMB")
set (WAMR_BUILD_INTERP 1)
set (WAMR_BUILD_FAST_INTERP 0)
set (WAMR_BUILD_AOT 1)
set (WAMR_BUILD_LIBC_BUILTIN 1) # printf
set (WAMR_BUILD_LIBC_WASI 1)
set (WAMR_BUILD_LIB_PTHREAD 0)
set (WAMR_BUILD_GLOBAL_HEAP_POOL 1)
set (WAMR_BUILD_GLOBAL_HEAP_SIZE 131072) # 128 KB
# set (WAMR_BUILD_GLOBAL_HEAP_SIZE 65536) # 64 KB
# Environment variables:
# Check if WAMR_ROOT_DIR is set
if(DEFINED ENV{WAMR_ROOT_DIR})
set(WAMR_ROOT_DIR $ENV{WAMR_ROOT_DIR})
else()
message(FATAL_ERROR "'WAMR_ROOT_DIR' need to be specified")
endif()
message("wasi-sdk was found at ${WAMR_ROOT_DIR}")
# Check if WASI_SDK_PATH is set
if(NOT $ENV{WASI_SDK_PATH} STREQUAL "")
set(WASI_SDK_PATH $ENV{WASI_SDK_PATH})
else()
find_program(WASM_C_COMPILER clang /opt/wasi-sdk/bin NO_DEFAULT_PATH)
if(NOT WASM_C_COMPILER)
message(FATAL_ERROR "'wasi-sdk' not found, please ensure wasi-sdk is installed.\
You can download and install it from\
https://github.com/WebAssembly/wasi-sdk/releases")
else()
set(WASI_SDK_PATH /opt/wasi-sdk)
endif()
endif()
message("wasi-sdk was found at ${WASI_SDK_PATH}")
# Check if WAMR_APP_FRAMEWORK_DIR is set
if (DEFINED ENV{WAMR_APP_FRAMEWORK_DIR})
set(WAMR_APP_FRAMEWORK_DIR $ENV{WAMR_APP_FRAMEWORK_DIR})
else()
message(FATAL_ERROR "'wamr-app-framework' not found, please ensure they are installed.\
You can download and install them from\
https://github.com/bytecodealliance/wamr-app-framework")
endif()
message("wamr-app-framework was found at ${WAMR_APP_FRAMEWORK_DIR}")
# set the WAMR_SDK_DIR with the path specified in the environment variable
set(WAMR_SDK_DIR
${WAMR_APP_FRAMEWORK_DIR}/wamr-sdk
)
# set the WAMR_LIBC_BUILTIN_DIR
set(WAMR_LIBC_BUILTIN_DIR
${WAMR_SDK_DIR}/wamr-sdk/app/libc-builtin-sysroot
)
# set the WAMR_SDK_PACKAGE_OUT_DIR
set(WAMR_SDK_PACKAGE_OUT_DIR
${CMAKE_CURRENT_BINARY_DIR}/wamr-sdk/app-sdk/wamr-app-framework
)
# # Reset linker flags
# set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
# set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)
# include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) # in socket-api sample
# Build the WAMR runtime
target_sources(app PRIVATE
${WAMR_RUNTIME_LIB_SOURCE}
src/main.c)
# Link libraries like in samples.
set(WASI_LIBM "${WASI_SDK_PATH}/share/wasi-sysroot/lib/wasm32-wasi/libm.a")
set(WASI_LIBDL "${WASI_SDK_PATH}/share/wasi-sysroot/lib/wasm32-wasi/libdl.a")
target_link_libraries(app PUBLIC ${WASI_LIBM} ${WASI_LIBDL})

View File

@ -0,0 +1,91 @@
# File sample
This sample demonstrates the use of WASI API to interact with the file system.
> 🛠️ **Work in progress:** The sample is functional but be aware that just a small part of WASI File System API was tested.
> Actual Zephyr APIs:
> * directory creation = `fs_mkdir`
> * file opening/creation = `fs_open`
> * file write = `fs_write`
> * file offset = `fs_seek`
> * file read = `fs_read`
> * file close = `fs_close`
> * directory close = `fs_closedir`
## Run Command
* **Zephyr Build**
1. **Build:** Replace `nucleo_h743zi` with your board name and the `WAMR_BUILD_TARGET` in `CMakeList.txt` with your target architecture.
```bash
ZEPHYR_BASE=~/zephyrproject/zephyr \
WAMR_ROOT_DIR=~/wasm-micro-runtime \
WASI_SDK_PATH=~/wasi-sdk-21.0 \
WAMR_APP_FRAMEWORK_DIR=~/wamr-app-framework \
west build . -b nucleo_h563zi -p always
```
⚠️ **Warning:** The flags `ZEPHYR_BASE`, `WAMR_ROOT_DIR`, `WASI_SDK_PATH`, and `WAMR_APP_FRAMEWORK_DIR` need to be set otherwise the build will fail.
2. **Flash:**
```bash
ZEPHYR_BASE=~/zephyrproject/zephyr west flash
```
3. **Monitor:** Use a serial link to monitor the output. Personally, I use minicom.
```bash
minicom -D /dev/ttyACM0
```
4. **Debug:** Curently investigating.
* **WebAssembly Module**
**Important:** I used wasi-sdk 21 to compile the module. I still haven't tried the module with the new wasi-sdk 22.
1. **Compile:** in the `wasm-apps` folder.
```bash
~/wasi-sdk-21.0/bin/clang --sysroot=/home/user/wasi-sdk-21.0/share/wasi-sysroot -nodefaultlibs -lc -o file.wasm file.c -z stack-size=8192 -Wl,--initial-memory=65536 -Wl,--export=__heap_base -Wl,--export=__data_end
```
2. **generate a C header:** Use `xxd` or other tool, I also put simple python script. At application root `simple-file/`.
```bash
python3 to_c_header.py
```
Be free to modify the script to fit your needs.
## Output
The output should be similar to the following:
```bash
*** Booting Zephyr OS build v3.6.0-4305-g2ec8f442a505 ***
Area 3 at 0x1f0000 on flash-controller@40022000 for 65536 bytes
[00:00:00.067,000] <inf> littlefs: LittleFS version 2.8, disk version 2.1
[00:00:00.074,000] <inf> littlefs: FS at flash-controller@40022000:0x1f0000 is 8 0x2000-byte blocks with 512 cycle
[00:00:00.085,000] <inf> littlefs: sizes: rd 16 ; pr 16 ; ca 64 ; la 32
[00:00:00.092,000] <err> littlefs: WEST_TOPDIR/modules/fs/littlefs/lfs.c:1351: Corrupted dir pair at {0x0, 0x1}
[00:00:00.103,000] <wrn> littlefs: can't mount (LFS -84); formatting
[00:00:00.114,000] <inf> littlefs: /lfs mounted
/lfs mount: 0
[00:00:00.120,000] <inf> main: stdin = 0
[00:00:00.124,000] <inf> main: stdout = 1
[00:00:00.128,000] <inf> main: stderr = 2
[00:00:00.133,000] <inf> main: global heap size: 131072
[00:00:00.142,000] <inf> main: Wasm file size: 34682
[00:00:00:000 - 2000AFE0]: WASI context initialization: START
[OS] os_rwlock_init
[OS] os_rwlock_init
[00:00:00:000 - 2000AFE0]: WASI context initialization: END
[00:00:00.190,000] <inf> main: main found
Hello WebAssembly Module !
mkdir returned 0
fopen Succeed
fwrite returned 13
fseek returned 0
fread returned 13
buffer read = Hello, World!
[00:00:00.225,000] <inf> main: main executed
[00:00:00.230,000] <inf> main: wasi exit code: 0
[00:00:00.239,000] <inf> main: elapsed: 178ms
[00:00:03.158,000] <inf> phy_mii: PHY (0) Link speed 100 Mb, full duplex
[00:00:00.051,000] <inf> phy_mii: PHY (0) ID 7C131
```

View File

@ -0,0 +1,40 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# Log config
CONFIG_PRINTK=y
CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y
# CONFIG_LOG_MODE_DEFERRED=y
CONFIG_MAIN_STACK_SIZE=8192
# CONFIG_HEAP_MEM_POOL_SIZE=32768
CONFIG_REQUIRES_FULL_LIBC=y
# Config File System
CONFIG_POSIX_API=n
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y
# CONFIG_FS_LITTLEFS_BLK_DEV=y
# Temp Build Network stack to compile.
CONFIG_NETWORKING=y
CONFIG_NET_IPV4=y
CONFIG_NET_IPV6=y
CONFIG_NET_TCP=y
CONFIG_NET_SOCKETS=y
# Random generator
CONFIG_TEST_RANDOM_GENERATOR=y
# Stack conf
CONFIG_STACK_SENTINEL=y
CONFIG_HW_STACK_PROTECTION=y
# Flash
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
# Debug
CONFIG_DEBUG=y

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,225 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
// #include <autoconf.h>
#include <stdlib.h>
#include <string.h>
#include "bh_platform.h"
#include "bh_assert.h"
#include "bh_log.h"
#include "wasm_export.h"
#include "file.h"
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/fs/fs.h>
#include <zephyr/fs/littlefs.h>
#include <zephyr/logging/log.h>
#include <unistd.h>
#define CONFIG_HEAP_MEM_POOL_SIZE WASM_GLOBAL_HEAP_SIZE
#define CONFIG_APP_STACK_SIZE 16384
#define CONFIG_APP_HEAP_SIZE 16384
LOG_MODULE_REGISTER(main);
static char global_heap_buf[CONFIG_HEAP_MEM_POOL_SIZE] = { 0 };
static int app_argc;
static char **app_argv;
//-------------------------------------------------------------------------------------------//
static int
littlefs_flash_erase(unsigned int id)
{
const struct flash_area *pfa;
int rc;
rc = flash_area_open(id, &pfa);
if (rc < 0) {
LOG_ERR("FAIL: unable to find flash area %u: %d\n", id, rc);
return rc;
}
LOG_PRINTK("Area %u at 0x%x on %s for %u bytes\n", id,
(unsigned int)pfa->fa_off, pfa->fa_dev->name,
(unsigned int)pfa->fa_size);
/* Optional wipe flash contents */
if (IS_ENABLED(CONFIG_APP_WIPE_STORAGE)) {
rc = flash_area_erase(pfa, 0, pfa->fa_size);
LOG_ERR("Erasing flash area ... %d", rc);
}
flash_area_close(pfa);
return rc;
}
#define PARTITION_NODE DT_NODELABEL(lfs1)
#if DT_NODE_EXISTS(PARTITION_NODE)
FS_FSTAB_DECLARE_ENTRY(PARTITION_NODE);
#else /* PARTITION_NODE */
FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
static struct fs_mount_t lfs_storage_mnt = {
.type = FS_LITTLEFS,
.fs_data = &storage,
.storage_dev = (void *)FIXED_PARTITION_ID(storage_partition),
.mnt_point = "/lfs",
};
#endif /* PARTITION_NODE */
struct fs_mount_t *mountpoint =
#if DT_NODE_EXISTS(PARTITION_NODE)
&FS_FSTAB_ENTRY(PARTITION_NODE)
#else
&lfs_storage_mnt
#endif
;
static int
littlefs_mount(struct fs_mount_t *mp)
{
int rc;
rc = littlefs_flash_erase((uintptr_t)mp->storage_dev);
if (rc < 0) {
return rc;
}
/* Do not mount if auto-mount has been enabled */
#if !DT_NODE_EXISTS(PARTITION_NODE) \
|| !(FSTAB_ENTRY_DT_MOUNT_FLAGS(PARTITION_NODE) & FS_MOUNT_FLAG_AUTOMOUNT)
rc = fs_mount(mp);
if (rc < 0) {
LOG_PRINTK("FAIL: mount id %" PRIuPTR " at %s: %d\n",
(uintptr_t)mp->storage_dev, mp->mnt_point, rc);
return rc;
}
LOG_PRINTK("%s mount: %d\n", mp->mnt_point, rc);
#else
LOG_PRINTK("%s automounted\n", mp->mnt_point);
#endif
return 0;
}
//-------------------------------------------------------------------------------------------//
int
main(void)
{
int start, end;
start = k_uptime_get_32();
uint8 *wasm_file_buf = NULL;
uint32 wasm_file_size;
wasm_module_t wasm_module = NULL;
wasm_module_inst_t wasm_module_inst = NULL;
RuntimeInitArgs init_args;
char error_buf[128];
const char *exception;
int rc;
int log_verbose_level = 2;
memset(&init_args, 0, sizeof(RuntimeInitArgs));
rc = littlefs_mount(mountpoint);
if (rc < 0) {
LOG_ERR("FAIL: mounting %s: %d\n", mountpoint->mnt_point, rc);
return 0;
}
#if WASM_ENABLE_GLOBAL_HEAP_POOL != 0
init_args.mem_alloc_type = Alloc_With_Pool;
init_args.mem_alloc_option.pool.heap_buf = global_heap_buf;
init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf);
LOG_INF("global heap size: %d", sizeof(global_heap_buf));
#else
#error "memory allocation scheme is not defined."
#endif
/* initialize runtime environment */
if (!wasm_runtime_full_init(&init_args)) {
LOG_ERR("Init runtime environment failed.");
return;
}
/* load WASM byte buffer from byte buffer of include file */
wasm_file_buf = (uint8 *)wasm_test_file;
wasm_file_size = sizeof(wasm_test_file);
LOG_INF("Wasm file size: %d", wasm_file_size);
/* load WASM module */
if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size,
error_buf, sizeof(error_buf)))) {
LOG_ERR("Failed to load module: %s", error_buf);
goto fail1;
}
/* Set the WASI context */
#if WASM_ENABLE_LIBC_WASI != 0
#define DIR_LIST_SIZE 1
const char *dir_list[DIR_LIST_SIZE] = {
"/lfs",
};
/* No dir list => No file system
* dir_cont = 0
* No mapped dir list => No file system
* map_dir_cont = 0
* No environment variables
* env_count = 0
* No command line arguments
* argv 0
*/
wasm_runtime_set_wasi_args(wasm_module, dir_list, DIR_LIST_SIZE, NULL, 0,
NULL, 0, NULL, 0);
#endif
/* instantiate the module */
if (!(wasm_module_inst = wasm_runtime_instantiate(
wasm_module, CONFIG_APP_STACK_SIZE, CONFIG_APP_HEAP_SIZE,
error_buf, sizeof(error_buf)))) {
LOG_ERR("Failed to instantiate module: %s", error_buf);
goto fail2;
}
/* invoke the main function */
if (wasm_runtime_lookup_function(wasm_module_inst, "_start")
|| wasm_runtime_lookup_function(wasm_module_inst, "__main_argc_argv")
|| wasm_runtime_lookup_function(wasm_module_inst, "main")) {
LOG_INF("main found");
wasm_application_execute_main(wasm_module_inst, 0, NULL);
LOG_INF("main executed");
}
else {
LOG_ERR("Failed to lookup function main");
return -1;
}
if ((exception = wasm_runtime_get_exception(wasm_module_inst)))
LOG_ERR("get exception: %s", exception);
rc = wasm_runtime_get_wasi_exit_code(wasm_module_inst);
LOG_INF("wasi exit code: %d", rc);
/* destroy the module instance */
wasm_runtime_deinstantiate(wasm_module_inst);
fail2:
/* unload the module */
wasm_runtime_unload(wasm_module);
fail1:
/* destroy runtime environment */
wasm_runtime_destroy();
end = k_uptime_get_32();
LOG_INF("elapsed: %dms", (end - start));
return 0;
}

View File

@ -0,0 +1,32 @@
# Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# Python script to convert wasm file to byte array in a .h file
import os
CWD = os.getcwd()
CMAKE_CURRENT_BINARY_DIR = os.getenv('CMAKE_CURRENT_BINARY_DIR', CWD)
CMAKE_CURRENT_SOURCE_DIR = os.getenv('CMAKE_CURRENT_SOURCE_DIR', f'{CWD}/../src')
LICENCE_HEADER = """/*
* Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
"""
print('CMAKE_CURRENT_BINARY_DIR:', CMAKE_CURRENT_BINARY_DIR)
print('CMAKE_CURRENT_SOURCE_DIR:', CMAKE_CURRENT_SOURCE_DIR)
# Open the wasm file in binary mode and read the data
with open(f'{CWD}/wasm-apps/file.wasm', 'rb') as f:
wasm_bytes = f.read()
# Convert the bytes to a comma-separated string of hex values
byte_array = ', '.join(f'0x{byte:02x}' for byte in wasm_bytes)
# Create the output string
output = f'{LICENCE_HEADER}\nunsigned char __aligned(4) wasm_test_file[] = {{ {byte_array} }};'
# Write the output string to the .h file
with open(f'{CWD}/src/file.h', 'w') as f:
f.write(output)

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <sys/stat.h>
#include <errno.h>
// Zephyr
#define CWD "/lfs"
#define FOLDER_PATH CWD "/folder"
#define FILE_PATH CWD "folder/test.txt"
int
main(int argc, char **argv)
{
int rc;
const int zero = 0;
printf("Hello WebAssembly Module !\n");
rc = mkdir(FOLDER_PATH, 0777);
if (rc < 0) {
rc = errno;
printf("mkdir failed with error %d\n", rc);
return -1;
}
printf("mkdir returned %d\n", rc);
FILE *file = fopen(FILE_PATH, "w+");
if (!file) {
printf("fopen Failed to open\n");
return -1;
}
printf("fopen Succeed\n");
const char *data = "Hello, World!";
size_t len = 13;
size_t nitems = fwrite(data, sizeof(char), 13, file);
printf("fwrite returned %d\n", (int)nitems);
rc = fseek(file, 0, SEEK_SET);
printf("fseek returned %d\n", rc);
char buffer[32];
nitems = fread(buffer, sizeof(char), 32, file);
printf("fread returned %d\n", (int)nitems);
printf("buffer read = %s\n", buffer);
fclose(file);
return 0;
}

View File

@ -0,0 +1,89 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
cmake_minimum_required(VERSION 3.8.2)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(wamr)
enable_language (ASM)
set (WAMR_BUILD_PLATFORM "zephyr")
# WAMR Configuration:
set (WAMR_BUILD_TARGET "THUMB")
set (WAMR_BUILD_INTERP 1)
set (WAMR_BUILD_AOT 0)
set (WAMR_BUILD_LIBC_BUILTIN 1) # printf
set (WAMR_BUILD_LIBC_WASI 1)
set (WAMR_BUILD_LIB_PTHREAD 0)
set (WAMR_BUILD_GLOBAL_HEAP_POOL 1)
set (WAMR_BUILD_GLOBAL_HEAP_SIZE 98304) # 96 KB
# Environment variables:
# Check if WAMR_ROOT_DIR is set
if(DEFINED ENV{WAMR_ROOT_DIR})
set(WAMR_ROOT_DIR $ENV{WAMR_ROOT_DIR})
else()
message(FATAL_ERROR "'WAMR_ROOT_DIR' need to be specified")
endif()
message("wasi-sdk was found at ${WAMR_ROOT_DIR}")
# Check if WASI_SDK_PATH is set
if(NOT $ENV{WASI_SDK_PATH} STREQUAL "")
set(WASI_SDK_PATH $ENV{WASI_SDK_PATH})
else()
find_program(WASM_C_COMPILER clang /opt/wasi-sdk/bin NO_DEFAULT_PATH)
if(NOT WASM_C_COMPILER)
message(FATAL_ERROR "'wasi-sdk' not found, please ensure wasi-sdk is installed.\
You can download and install it from\
https://github.com/WebAssembly/wasi-sdk/releases")
else()
set(WASI_SDK_PATH /opt/wasi-sdk)
endif()
endif()
message("wasi-sdk was found at ${WASI_SDK_PATH}")
# Check if WAMR_APP_FRAMEWORK_DIR is set
if (DEFINED ENV{WAMR_APP_FRAMEWORK_DIR})
set(WAMR_APP_FRAMEWORK_DIR $ENV{WAMR_APP_FRAMEWORK_DIR})
else()
message(FATAL_ERROR "'wamr-app-framework' not found, please ensure they are installed.\
You can download and install them from\
https://github.com/bytecodealliance/wamr-app-framework")
endif()
message("wamr-app-framework was found at ${WAMR_APP_FRAMEWORK_DIR}")
# set the WAMR_SDK_DIR with the path specified in the environment variable
set(WAMR_SDK_DIR
${WAMR_APP_FRAMEWORK_DIR}/wamr-sdk
)
# set the WAMR_LIBC_BUILTIN_DIR
set(WAMR_LIBC_BUILTIN_DIR
${WAMR_SDK_DIR}/wamr-sdk/app/libc-builtin-sysroot
)
# set the WAMR_SDK_PACKAGE_OUT_DIR
set(WAMR_SDK_PACKAGE_OUT_DIR
${CMAKE_CURRENT_BINARY_DIR}/wamr-sdk/app-sdk/wamr-app-framework
)
# # Reset linker flags
# set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
# set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)
# include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) # in socket-api sample
# Build the WAMR runtime
target_sources(app PRIVATE
${WAMR_RUNTIME_LIB_SOURCE}
src/main.c)
# Link libraries like in samples.
set(WASI_LIBM "${WASI_SDK_PATH}/share/wasi-sysroot/lib/wasm32-wasi/libm.a")
set(WASI_LIBDL "${WASI_SDK_PATH}/share/wasi-sysroot/lib/wasm32-wasi/libdl.a")
target_link_libraries(app PUBLIC ${WASI_LIBM} ${WASI_LIBDL})

View File

@ -0,0 +1,143 @@
# Socket sample
this sample demonstrates the use of WASI API to interact with sockets.
> ❗ **Important:** This sample was ported/adapted from the http_get zephyr sample. The original sample can be found [here]( https://github.com/zephyrproject-rtos/zephyr/blob/main/samples/net/sockets/http_get/src/http_get.c).
> 🛠️ **Work in progress:** The sample is functional but be aware that just a small part of WASI socket API was tested.
> Actual Zephyr APIs:
> * socket creation = `zsock_socket`
> * socket connection = `zsock_connect`
> * socket emission = `zsock_sendto`
> * socket reception = `zsock_recvfrom`
> * socket destruction = `zsock_close`
>
> With the sockets most API are in fact provided by the runtime instead of WASI because of the lack of socket support in WASI preview1.
## Setup
1. Connect a network cable to the board ethernet port.
2. Configure the network interface on the host machine
```
Internet Protocol Version 4 (TCP/IPv4) Properties:
IP Address: 192.0.2.10
Subnet Mask: 255.255.255.0
Default Gateway: 192.0.2.2
```
3. Start a simple HTTP server on the host machine.
```bash
python3 -m http.server --bind 0.0.0.0
```
4. Disable any firewall that may block the connection.
## Configuration
To configure the server side IP address and port modify the following lines in the `http_get.c` file.
1. The `HTTP_HOST` and `HTTP_PORT` macros define the server IP address and port.
```c
/* HTTP server to connect to */
#define HTTP_HOST "192.0.2.10"
/* Port to connect to, as string */
#define HTTP_PORT "8000"
/* HTTP path to request */
#define HTTP_PATH "/"
// ...
#define REQUEST "GET " HTTP_PATH " HTTP/1.0\r\nHost: " HTTP_HOST "\r\n\r\n"
```
> 📄 **Notes:** These macros are used to build the request string, but they are not used to instantiate the address structure. Because at one point we didn't want to use `inet_pton` to convert the string to an address and it remained like this.
2. The `addr` structure is used to store the server address.
```c
addr.sin_port = htons(8000);
addr.sin_addr.s_addr =
htonl(0xC000020A); // hard coded IP address for 192.0.2.10
```
To configure the authorized IP address(es) modify the following lines in the `main.c` file. WAMR will only allow the IP addresses in the pool to connect to the server.
```c
#define ADDRESS_POOL_SIZE 1
const char *addr_pool[ADDRESS_POOL_SIZE] = {
"192.0.2.10/24",
};
```
## Run Command
* **Zephyr Build**
1. **Build:** Replace `nucleo_h743zi` with your board name and the `WAMR_BUILD_TARGET` in `CMakeList.txt` with your target architecture.
```bash
ZEPHYR_BASE=~/zephyrproject/zephyr \
WAMR_ROOT_DIR=~/wasm-micro-runtime \
WASI_SDK_PATH=~/wasi-sdk-21.0 \
WAMR_APP_FRAMEWORK_DIR=~/wamr-app-framework \
west build . -b nucleo_h563zi -p always
```
⚠️ **Warning:** The flags `ZEPHYR_BASE`, `WAMR_ROOT_DIR`, `WASI_SDK_PATH`, and `WAMR_APP_FRAMEWORK_DIR` need to be set otherwise the build will fail.
2. **Flash:**
```bash
ZEPHYR_BASE=~/zephyrproject/zephyr west flash
```
3. **Monitor:** Use a serial link to monitor the output. Personally, I use minicom.
```bash
minicom -D /dev/ttyACM0
```
4. **Debug:** Curently investigating.
* **WebAssembly Module**
**Important:** I used wasi-sdk 21 to compile the module. I still haven't tried the module with the new wasi-sdk 22.
0. **Compile a static lib:** in the `wasm-apps` folder.
* **Compile the an object:**
```bash
~/wasi-sdk-21.0/bin/clang --sysroot=/home/user/wasi-sdk-21.0/share/wasi-sysroot -Iinc/ -c inc/wasi_socket_ext.c -o inc/wasi_socket_ext.o
```
* **Create a static lib:**
```bash
~/wasi-sdk-21.0/bin/llvm-ar rcs inc/libwasi_socket_ext.a inc/wasi_socket_ext.o
```
1. **Compile:** in the `wasm-apps` folder.
```bash
~/wasi-sdk-21.0/bin/clang --sysroot=/home/user/wasi-sdk-21.0/share/wasi-sysroot -Iinc/ -nodefaultlibs -o http_get.wasm http_get.c -lc -Linc/ -lwasi_socket_ext -z stack-size=8192 -Wl,--initial-memory=65536 -Wl,--export=__heap_base -Wl,--export=__data_end -Wl,--allow-undefined
```
2. **generate a C header:** Use `xxd` or other tool, I also put simple python script. At application root `simple-http/`.
```bash
python3 to_c_header.py
```
Be free to modify the script to fit your needs.
## Output
The output should be similar to the following:
```bash
*** Booting Zephyr OS build v3.6.0-4305-g2ec8f442a505 ***
[00:00:00.061,000] <inf> net_config: Initializing network
[00:00:00.067,000] <inf> net_config: Waiting interface 1 (0x2000a910) to be up...
[00:00:03.158,000] <inf> phy_mii: PHY (0) Link speed 100 Mb, full duplex
[00:00:03.288,000] <inf> net_config: Interface 1 (0x2000a910) coming up
[00:00:03.295,000] <inf> net_config: IPv4 address: 192.0.2.1
global heap size: 131072
Wasm file size: 36351
main found
[wasm-mod] Preparing HTTP GET request for http://192.0.2.10:8000/
[wasm-mod] sock = 3
[wasm-mod] connect rc = 0
[wasm-mod] send rc = 36
[wasm-mod] Response:
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.10.10
Date: Fri, 14 Jun 2024 07:26:56 GMT
Content-type: text/html; charset=utf-8
Content-Length: 2821
# Skip the HTML content
[wasm-mod] len = 0 break
[wasm-mod] Connection closed
main executed
wasi exit code: 0
elapsed: 405ms
```

View File

@ -0,0 +1,61 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# Log config
CONFIG_PRINTK=y
CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_NET_LOG=y
CONFIG_MAIN_STACK_SIZE=8192
# CONFIG_HEAP_MEM_POOL_SIZE=32768
CONFIG_REQUIRES_FULL_LIBC=y
# Networking config
CONFIG_NETWORKING=y
CONFIG_NET_IPV4=y
CONFIG_NET_IPV6=y
CONFIG_NET_TCP=y
CONFIG_NET_SOCKETS=y
CONFIG_POSIX_API=n
# Stack conf
# CONFIG_NO_OPTIMIZATIONS=y
CONFIG_STACK_SENTINEL=y
CONFIG_HW_STACK_PROTECTION=y
# CONFIG_STACK_CANARIES=y
# CONFIG_ISR_STACK_SIZE=4096
# Network driver config
CONFIG_TEST_RANDOM_GENERATOR=y
# Network address config
CONFIG_NET_CONFIG_SETTINGS=y
CONFIG_NET_CONFIG_NEED_IPV4=y
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2"
CONFIG_NET_CONFIG_MY_IPV4_GW="192.0.2.2"
# Config File System
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y
# Flash
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
# CONFIG_DNS_RESOLVER=y
# CONFIG_DNS_SERVER_IP_ADDRESSES=y
# CONFIG_DNS_SERVER1="192.0.2.2"
# Config init stack
# CONFIG_INIT_STACKS=y
# CONFIG_NET_PKT_RX_COUNT=100
# CONFIG_NET_PKT_TX_COUNT=100
# CONFIG_NET_BUF_RX_COUNT=100
# CONFIG_NET_BUF_TX_COUNT=100
# Flash
CONFIG_FLASH=y
# Debug
CONFIG_DEBUG=y

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,141 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
// #include <autoconf.h>
#include <stdlib.h>
#include <string.h>
#include "bh_platform.h"
#include "bh_assert.h"
#include "bh_log.h"
#include "wasm_export.h"
#include "http_get.h"
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/socket.h>
#include <zephyr/net/http/client.h>
#define CONFIG_HEAP_MEM_POOL_SIZE WASM_GLOBAL_HEAP_SIZE
#define CONFIG_APP_STACK_SIZE 8192
#define CONFIG_APP_HEAP_SIZE 8192
static char global_heap_buf[CONFIG_HEAP_MEM_POOL_SIZE] = { 0 };
static int app_argc;
static char **app_argv;
int
main(void)
{
int start, end;
start = k_uptime_get_32();
uint8 *wasm_file_buf = NULL;
uint32 wasm_file_size;
wasm_module_t wasm_module = NULL;
wasm_module_inst_t wasm_module_inst = NULL;
RuntimeInitArgs init_args;
char error_buf[128];
const char *exception;
int log_verbose_level = 2;
memset(&init_args, 0, sizeof(RuntimeInitArgs));
#if WASM_ENABLE_GLOBAL_HEAP_POOL != 0
init_args.mem_alloc_type = Alloc_With_Pool;
init_args.mem_alloc_option.pool.heap_buf = global_heap_buf;
init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf);
printf("global heap size: %d\n", sizeof(global_heap_buf));
#else
#error "memory allocation scheme is not defined."
#endif
/* initialize runtime environment */
if (!wasm_runtime_full_init(&init_args)) {
printf("Init runtime environment failed.\n");
return;
}
bh_log_set_verbose_level(log_verbose_level);
/* load WASM byte buffer from byte buffer of include file */
wasm_file_buf = (uint8 *)wasm_test_file;
wasm_file_size = sizeof(wasm_test_file);
printf("Wasm file size: %d\n", wasm_file_size);
/* load WASM module */
if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size,
error_buf, sizeof(error_buf)))) {
printf("Failed to load module: %s\n", error_buf);
goto fail1;
}
/* Set the WASI context */
#if WASM_ENABLE_LIBC_WASI != 0
#define ADDRESS_POOL_SIZE 1
const char *addr_pool[ADDRESS_POOL_SIZE] = {
"192.0.2.10/24",
};
/* No dir list => No file system
* dir_cont = 0
* No mapped dir list => No file system
* map_dir_cont = 0
* No environment variables
* env_count = 0
* No command line arguments
* argv 0
*/
wasm_runtime_set_wasi_args(wasm_module, NULL, 0, NULL, 0, NULL, 0, NULL, 0);
wasm_runtime_set_wasi_addr_pool(wasm_module, addr_pool, ADDRESS_POOL_SIZE);
wasm_runtime_set_wasi_ns_lookup_pool(wasm_module, NULL, 0);
#endif
/* instantiate the module */
if (!(wasm_module_inst = wasm_runtime_instantiate(
wasm_module, CONFIG_APP_STACK_SIZE, CONFIG_APP_HEAP_SIZE,
error_buf, sizeof(error_buf)))) {
printf("Failed to instantiate module: %s\n", error_buf);
goto fail2;
}
/* invoke the main function */
if (wasm_runtime_lookup_function(wasm_module_inst, "_start")
|| wasm_runtime_lookup_function(wasm_module_inst, "__main_argc_argv")) {
printf("main found\n");
wasm_application_execute_main(wasm_module_inst, 0, NULL);
printf("main executed\n");
}
else {
printf("Failed to lookup function main\n");
return -1;
}
if ((exception = wasm_runtime_get_exception(wasm_module_inst)))
printf("%s\n", exception);
int rc = wasm_runtime_get_wasi_exit_code(wasm_module_inst);
printf("wasi exit code: %d\n", rc); // 1 = _WASI_E2BIG
/* destroy the module instance */
wasm_runtime_deinstantiate(wasm_module_inst);
fail2:
/* unload the module */
wasm_runtime_unload(wasm_module);
fail1:
/* destroy runtime environment */
wasm_runtime_destroy();
end = k_uptime_get_32();
printf("elapsed: %dms\n", (end - start));
return 0;
}

View File

@ -0,0 +1,34 @@
# Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# Python script to convert wasm file to byte array in a .h file
import os
CWD = os.getcwd()
CMAKE_CURRENT_BINARY_DIR = os.getenv('CMAKE_CURRENT_BINARY_DIR', CWD)
CMAKE_CURRENT_SOURCE_DIR = os.getenv('CMAKE_CURRENT_SOURCE_DIR', f'{CWD}/../src')
LICENCE_HEADER = """/*
* Copyright (c) 2017 Linaro Limited
* Copyright (C) 2024 Grenoble INP - ESISAR Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
"""
print('CMAKE_CURRENT_BINARY_DIR:', CMAKE_CURRENT_BINARY_DIR)
print('CMAKE_CURRENT_SOURCE_DIR:', CMAKE_CURRENT_SOURCE_DIR)
# Open the wasm file in binary mode and read the data
with open(f'{CWD}/wasm-apps/http_get.wasm', 'rb') as f:
wasm_bytes = f.read()
# Convert the bytes to a comma-separated string of hex values
byte_array = ', '.join(f'0x{byte:02x}' for byte in wasm_bytes)
# Create the output string
output = f'unsigned char __aligned(4) wasm_test_file[] = {{ {byte_array} }};'
# Write the output string to the .h file
with open(f'{CWD}/src/http_get.h', 'w') as f:
f.write(output)

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#ifdef __wasi__
#include <wasi_socket_ext.h>
#endif
/* HTTP server to connect to */
#define HTTP_HOST "192.0.2.10"
/* Port to connect to, as string */
#define HTTP_PORT "8000"
/* HTTP path to request */
#define HTTP_PATH "/"
#define SSTRLEN(s) (sizeof(s) - 1)
// #define CHECK(r) { if (r == -1) { printf("Error %d: " #r "\n", errno);
// exit(1); } }
#define REQUEST "GET " HTTP_PATH " HTTP/1.0\r\nHost: " HTTP_HOST "\r\n\r\n"
static char response[1024];
int
main(int argc, char **argv)
{
int st, sock;
struct sockaddr_in addr;
int rc = 0;
printf("[wasm-mod] Preparing HTTP GET request for http://" HTTP_HOST
":" HTTP_PORT HTTP_PATH "\n");
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8000);
addr.sin_addr.s_addr =
htonl(0xC000020A); // hard coded IP address for 192.0.2.10
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
printf("[wasm-mod] sock = %d\n", sock);
rc = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
printf("[wasm-mod] connect rc = %d\n", rc);
rc = sendto(sock, (const void *)REQUEST, SSTRLEN(REQUEST), 0,
(struct sockaddr *)&addr, sizeof(addr));
printf("[wasm-mod] send rc = %d\n", rc);
if (rc < 0) {
printf("[wasm-mod] Error %d\n", errno);
return 0;
}
printf("[wasm-mod] Response:\n\n");
while (1) {
socklen_t socklen = sizeof(struct sockaddr_in);
int len = recvfrom(sock, response, sizeof(response) - 1, 0,
(struct sockaddr *)&addr, &socklen);
if (len < 0) {
printf("[wasm-mod] Error %d\n", errno);
return 0;
}
response[len] = 0;
printf("%s", response);
if (len == 0) {
printf("[wasm-mod] len = 0 break\n");
break;
}
}
printf("\n");
(void)close(sock);
printf("[wasm-mod] Connection closed\n");
return 0;
}

View File

@ -0,0 +1,5 @@
The lib source code are located there:
* [wasi_socket_ext.h](../../../../../../core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h)
* [wasi_socket_ext.c](../../../../../../core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c)

BIN
samples/socket-api/file.wasm Executable file

Binary file not shown.

View File

@ -88,4 +88,4 @@ compile_with_clang(udp_server.c)
compile_with_clang(multicast_client.c)
compile_with_clang(multicast_server.c)
compile_with_clang(timeout_client.c)
compile_with_clang(timeout_server.c)
compile_with_clang(timeout_server.c)