Add and enable CivetWeb server

Add missing files to repository
Update README with current build instructions
parent 1d8b8f8a
......@@ -3,21 +3,33 @@
SET(FPGA_BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bootrom/fpga/release"
CACHE PATH
"Path to the FPGA build folder from the litex-boards repo: <litex-boards-repo>/litex_boards/targets/build/versa_ecp5")
SET(KESTREL_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/kestrel)
SET(CIVETWEB_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/civetweb)
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
# # Default include for all subprojects
include_directories(${FPGA_BUILD_DIR}/software/include)
include_directories(include)
# Default include for all subprojects
include_directories(
${FPGA_BUILD_DIR}/software/include
${CMAKE_CURRENT_SOURCE_DIR}/include
${KESTREL_SOURCE_DIR}/include
${CIVETLIB_SOURCE_DIR}/include
)
zephyr_link_libraries(
soft-fp
)
project(kestrel_module)
target_sources(app PRIVATE
src/zephyr.c
src/shell.c
src/opencores_i2c.c
src/fsi.c
src/kestrel.c
src/direct_uart.c
${KESTREL_SOURCE_DIR}/src/zephyr.c
${KESTREL_SOURCE_DIR}/src/shell.c
${KESTREL_SOURCE_DIR}/src/opencores_i2c.c
${KESTREL_SOURCE_DIR}/src/fsi.c
${KESTREL_SOURCE_DIR}/src/kestrel.c
${KESTREL_SOURCE_DIR}/src/webservice.c
${KESTREL_SOURCE_DIR}/src/direct_uart.c
${CIVETWEB_SOURCE_DIR}/src/libc_extensions.c
)
NOTE
This application is developed on OpenPOWER (Raptor Computing Systems POWER9) host systems. Other development systems may or may not work without significant effort.
With that disclaimer out of the way...
How to build:
0.) Create directory to hold development sources and builds
mkdir kestrel-zephyr
cd kestrel-zephyr
1.) Clone all repositories
git clone https://gitlab.raptorengineering.com/kestrel-collaboration/kestrel-firmware/zephyr-rtos
git clone https://gitlab.raptorengineering.com/kestrel-collaboration/kestrel-firmware/zephyr-firmware
git clone https://gitlab.raptorengineering.com/kestrel-collaboration/kestrel-firmware/mbedtls
git clone https://gitlab.raptorengineering.com/kestrel-collaboration/kestrel-firmware/civetweb
git clone https://gitlab.raptorengineering.com/kestrel-collaboration/kestrel-tooling/gcc
2.) Build soft floating point library from GCC toolchain sources
NOTE:
This is a bit of a hack. Unfortunately, our target platform is soft float, while most (all?) "real" OpenPOWER distros are hard float.
As a result, we need to compile a copy of the soft float static library for our target.
cd gcc/libgcc/soft-fp/
gcc -c -O2 -msoft-float -mno-string -mno-multiple -mno-vsx -mno-altivec -mlittle-endian -mstrict-align -fno-stack-protector -m64 -mabi=elfv2 -mcmodel=small -I../config/rs6000/ -I../../include -I.. *.c
<ignore expected errors>
ar -crv libsoft-fp.a *.o
cp -Rp libsoft-fp.a ../../../
cd ../../../
3.) Build Zephyr application
export SOURCE_ROOT_DIR=$(pwd)
cd zephyr
mkdir build
ZEPHYR_BASE=${SOURCE_ROOT_DIR}/zephyr ZEPHYR_TOOLCHAIN_VARIANT=host cmake -DZEPHYR_MODULES=${SOURCE_ROOT_DIR}/mbedtls -DZEPHYR_MODULES=${SOURCE_ROOT_DIR}/civetweb -DBOARD=litex_kestrel ${SOURCE_ROOT_DIR}/zephyr-firmware
cp -Rp ../libsoft-fp.a zephyr/
make -j144
Output is in zephyr/zephyr.bin
Pass to litex_term using the --kernel parameter
Determine why Zephyr's CivetWeb integration requires src/ and include/ symlinked to the wrapper header directory, then remove the two symlinks.
/*
* Copyright (c) 2019 Antmicro Ltd
* Copyright (c) 2020 Alexander Kozhinov <AlexanderKozhinov@yandex.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
static void log_access(const struct mg_connection *conn)
{
const struct mg_request_info *ri;
char src_addr[IP_ADDR_STR_LEN];
if (!conn || !conn->dom_ctx) {
return;
}
ri = &conn->request_info;
sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
LOG_DBG("%s - \"%s %s%s%s HTTP/%s\" %d\n",
STR_LOG_ALLOC(src_addr),
STR_LOG_ALLOC(ri->request_method),
STR_LOG_ALLOC(ri->request_uri),
(ri->query_string == NULL) ? log_strdup("?") : log_strdup(""),
STR_LOG_ALLOC(ri->query_string),
STR_LOG_ALLOC(ri->http_version),
conn->status_code);
}
/*
* Copyright (c) 2019 Antmicro Ltd
* Copyright (c) 2020 Alexander Kozhinov <AlexanderKozhinov@yandex.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(mg_cry_internal_impl, LOG_LEVEL_DBG);
#include "helper.h"
static void mg_cry_internal_impl(const struct mg_connection *conn,
const char *func,
unsigned line,
const char *fmt,
va_list ap)
{
(void)conn;
LOG_ERR("%s @ %d in civetweb.c", STR_LOG_ALLOC(func), line);
vprintf(fmt, ap);
}
/*
* Copyright (c) 2020 Alexander Kozhinov Mail: <AlexanderKozhinov@yandex.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __HELPER__
#define __HELPER__
#include <logging/log.h>
#define STR_LOG_ALLOC(str) ((str == NULL) ? log_strdup("null") :\
log_strdup(str))
#endif /* __HELPER__ */
/*
* Copyright (c) 2019 Antmicro Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <net/socket.h>
#define pollfd zsock_pollfd
#define fcntl zsock_fcntl
#define POLLIN ZSOCK_POLLIN
#define POLLOUT ZSOCK_POLLOUT
#define addrinfo zsock_addrinfo
#define F_SETFD 2
#define FD_CLOEXEC 1
size_t strcspn(const char *s1, const char *s2);
size_t strspn(const char *s1, const char *s2);
int iscntrl(int c);
double atof(const char *str);
long long strtoll(const char *str, char **endptr, int base);
int sscanf(const char *s, const char *format, ...);
char *strerror(int err);
unsigned long long strtoull(const char *str, char **endptr, int base);
time_t time(time_t *t);
struct tm *gmtime(const time_t *ptime);
size_t strftime(char *dst, size_t dst_size, const char *fmt,
const struct tm *tm);
double difftime(time_t end, time_t beg);
struct tm *localtime(const time_t *timer);
int fileno(FILE *stream);
int ferror(FILE *stream);
int fclose(FILE *stream);
int fseeko(FILE *stream, off_t offset, int whence);
FILE *fopen(const char *filename, const char *mode);
char *fgets(char *str, int num, FILE *stream);
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
int remove(const char *filename);
int getsockname(int sock, struct sockaddr *addr, socklen_t *addrlen);
int poll(struct zsock_pollfd *fds, int nfds, int timeout);
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
char *host, socklen_t hostlen,
char *serv, socklen_t servlen, int flags);
ssize_t send(int sock, const void *buf, size_t len, int flags);
ssize_t recv(int sock, void *buf, size_t max_len, int flags);
int socket(int family, int type, int proto);
int getaddrinfo(const char *host, const char *service,
const struct zsock_addrinfo *hints,
struct zsock_addrinfo **res);
void freeaddrinfo(struct zsock_addrinfo *ai);
int connect(int sock, const struct sockaddr *addr, socklen_t addrlen);
int getsockopt(int sock, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sock, int level, int optname,
const void *optval, socklen_t optlen);
int listen(int sock, int backlog);
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
int bind(int sock, const struct sockaddr *addr, socklen_t addrlen);
/*
* Copyright (c) 2019 Antmicro Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(lib_extensions, LOG_LEVEL_DBG);
#include <zephyr.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "libc_extensions.h"
#define FN_MISSING() LOG_DBG("[IMPLEMENTATION MISSING : %s]\n", __func__)
int iscntrl(int c)
{
/* All the characters placed before the space on the ASCII table
* and the 0x7F character (DEL) are control characters.
*/
return (int)(c < ' ' || c == 0x7F);
}
size_t strftime(char *dst, size_t dst_size,
const char *fmt,
const struct tm *tm)
{
FN_MISSING();
return 0;
}
double difftime(time_t end, time_t beg)
{
return end - beg;
}
struct __strerr_wrap {
int err;
const char *errstr;
};
/* Implementation suggested by @rakons in #16527 */
#define STRERR_DEFINE(e) {e, #e}
static const struct __strerr_wrap error_strings[] = {
STRERR_DEFINE(EILSEQ),
STRERR_DEFINE(EDOM),
STRERR_DEFINE(ERANGE),
STRERR_DEFINE(ENOTTY),
STRERR_DEFINE(EACCES),
STRERR_DEFINE(EPERM),
STRERR_DEFINE(ENOENT),
STRERR_DEFINE(ESRCH),
STRERR_DEFINE(EEXIST),
STRERR_DEFINE(ENOSPC),
STRERR_DEFINE(ENOMEM),
STRERR_DEFINE(EBUSY),
STRERR_DEFINE(EINTR),
STRERR_DEFINE(EAGAIN),
STRERR_DEFINE(ESPIPE),
STRERR_DEFINE(EXDEV),
STRERR_DEFINE(EROFS),
STRERR_DEFINE(ENOTEMPTY),
STRERR_DEFINE(ECONNRESET),
STRERR_DEFINE(ETIMEDOUT),
STRERR_DEFINE(ECONNREFUSED),
STRERR_DEFINE(EHOSTDOWN),
STRERR_DEFINE(EHOSTUNREACH),
STRERR_DEFINE(EADDRINUSE),
STRERR_DEFINE(EPIPE),
STRERR_DEFINE(EIO),
STRERR_DEFINE(ENXIO),
STRERR_DEFINE(ENOTBLK),
STRERR_DEFINE(ENODEV),
STRERR_DEFINE(ENOTDIR),
STRERR_DEFINE(EISDIR),
STRERR_DEFINE(ETXTBSY),
STRERR_DEFINE(ENOEXEC),
STRERR_DEFINE(EINVAL),
STRERR_DEFINE(E2BIG),
STRERR_DEFINE(ELOOP),
STRERR_DEFINE(ENAMETOOLONG),
STRERR_DEFINE(ENFILE),
STRERR_DEFINE(EMFILE),
STRERR_DEFINE(EBADF),
STRERR_DEFINE(ECHILD),
STRERR_DEFINE(EFAULT),
STRERR_DEFINE(EFBIG),
STRERR_DEFINE(EMLINK),
STRERR_DEFINE(ENOLCK),
STRERR_DEFINE(EDEADLK),
STRERR_DEFINE(ECANCELED),
STRERR_DEFINE(ENOSYS),
STRERR_DEFINE(ENOMSG),
STRERR_DEFINE(ENOSTR),
STRERR_DEFINE(ENODATA),
STRERR_DEFINE(ETIME),
STRERR_DEFINE(ENOSR),
STRERR_DEFINE(EPROTO),
STRERR_DEFINE(EBADMSG),
STRERR_DEFINE(ENOTSOCK),
STRERR_DEFINE(EDESTADDRREQ),
STRERR_DEFINE(EMSGSIZE),
STRERR_DEFINE(EPROTOTYPE),
STRERR_DEFINE(ENOPROTOOPT),
STRERR_DEFINE(EPROTONOSUPPORT),
STRERR_DEFINE(ESOCKTNOSUPPORT),
STRERR_DEFINE(ENOTSUP),
STRERR_DEFINE(EPFNOSUPPORT),
STRERR_DEFINE(EAFNOSUPPORT),
STRERR_DEFINE(EADDRNOTAVAIL),
STRERR_DEFINE(ENETDOWN),
STRERR_DEFINE(ENETUNREACH),
STRERR_DEFINE(ENETRESET),
STRERR_DEFINE(ECONNABORTED),
STRERR_DEFINE(ENOBUFS),
STRERR_DEFINE(EISCONN),
STRERR_DEFINE(ENOTCONN),
STRERR_DEFINE(ESHUTDOWN),
STRERR_DEFINE(EALREADY),
STRERR_DEFINE(EINPROGRESS),
};
static char *strerr_unknown = "UNKNOWN";
int fileno(FILE *stream)
{
FN_MISSING();
return -1;
}
int ferror(FILE *stream)
{
FN_MISSING();
return 0;
}
char *strerror(int err)
{
int i;
for (i = 0; i < ARRAY_SIZE(error_strings); ++i) {
if (error_strings[i].err == err) {
return (char *)error_strings[i].errstr;
}
}
return strerr_unknown;
}
int sscanf(const char *s, const char *format, ...)
{
FN_MISSING();
return 0;
}
double atof(const char *str)
{
/* XXX good enough for civetweb uses */
return (double)atoi(str);
}
long long strtoll(const char *str, char **endptr, int base)
{
/* XXX good enough for civetweb uses */
return (long long)strtol(str, endptr, base);
}
/*
* Most of the wrappers below are copies of the wrappers in net/sockets.h,
* but they are available only if CONFIG_NET_SOCKETS_POSIX_NAMES is enabled
* which is impossible here.
*/
int getsockname(int sock, struct sockaddr *addr,
socklen_t *addrlen)
{
return zsock_getsockname(sock, addr, addrlen);
}
int poll(struct zsock_pollfd *fds, int nfds, int timeout)
{
return zsock_poll(fds, nfds, timeout);
}
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
char *host, socklen_t hostlen,
char *serv, socklen_t servlen, int flags)
{
return zsock_getnameinfo(addr, addrlen, host, hostlen,
serv, servlen, flags);
}
ssize_t send(int sock, const void *buf, size_t len, int flags)
{
return zsock_send(sock, buf, len, flags);
}
ssize_t recv(int sock, void *buf, size_t max_len, int flags)
{
return zsock_recv(sock, buf, max_len, flags);
}
int socket(int family, int type, int proto)
{
return zsock_socket(family, type, proto);
}
int getaddrinfo(const char *host, const char *service,
const struct zsock_addrinfo *hints,
struct zsock_addrinfo **res)
{
return zsock_getaddrinfo(host, service, hints, res);
}
void freeaddrinfo(struct zsock_addrinfo *ai)
{
zsock_freeaddrinfo(ai);
}
int connect(int sock, const struct sockaddr *addr,
socklen_t addrlen)
{
return zsock_connect(sock, addr, addrlen);
}
int getsockopt(int sock, int level, int optname,
void *optval, socklen_t *optlen)
{
return zsock_getsockopt(sock, level, optname, optval, optlen);
}
int setsockopt(int sock, int level, int optname,
const void *optval, socklen_t optlen)
{
return zsock_setsockopt(sock, level, optname, optval, optlen);
}
int listen(int sock, int backlog)
{
return zsock_listen(sock, backlog);
}
int bind(int sock, const struct sockaddr *addr, socklen_t addrlen)
{
return zsock_bind(sock, addr, addrlen);
}
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen)
{
return zsock_accept(sock, addr, addrlen);
}
civetweb/include
\ No newline at end of file
// © 2021 Raptor Engineering, LLC
//
// Released under the terms of the GPL v3
// See the LICENSE file for full details
#if (WITH_ZEPHYR)
#include <shell/shell.h>
#endif
#if (WITH_ZEPHYR)
#define KESTREL_SERVICE_THREAD_PRIORITY K_PRIO_COOP(1)
#endif
extern uint8_t kestrel_basic_init_complete;
extern uint8_t host_background_service_task_active;
extern uint8_t host_console_service_task_requested;
#if (WITH_ZEPHYR)
extern k_tid_t kestrel_service_thread_id;
extern k_tid_t kestrel_console_thread_id;
extern const struct shell *host_console_service_task_shell;
#endif
int kestrel_init(void);
int power_on_host(void);
void power_off_chassis(void);
void print_chassis_status(void);
int primary_service_event_loop(void);
#if (WITH_ZEPHYR)
int attach_to_host_console(const struct shell *shell);
int host_console_event_loop(const struct shell *shell);
#else
int attach_to_host_console();
#endif
\ No newline at end of file
/*
* Copyright (c) 2019 Antmicro Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <posix/pthread.h>
#include <data/json.h>
#include "civetweb.h"
#define HTTP_PORT 8080
#define HTTPS_PORT 4443
#define CIVETWEB_MAIN_THREAD_STACK_SIZE CONFIG_MAIN_STACK_SIZE
/* Use samllest possible value of 1024 (see the line 18619 of civetweb.c) */
#define MAX_REQUEST_SIZE_BYTES 1024
K_THREAD_STACK_DEFINE(civetweb_stack, CIVETWEB_MAIN_THREAD_STACK_SIZE);
struct civetweb_info {
const char *version;
const char *os;
uint32_t features;
const char *feature_list;
const char *build;
const char *compiler;
const char *data_model;
};
#define FIELD(struct_, member_, type_) { \
.field_name = #member_, \
.field_name_len = sizeof(#member_) - 1, \
.offset = offsetof(struct_, member_), \
.type = type_ \
}
void send_ok(struct mg_connection *conn)
{
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n\r\n");
}
int hello_world_handler(struct mg_connection *conn, void *cbdata)
{
send_ok(conn);
mg_printf(conn, "<html><body>");
mg_printf(conn, "<h3>Hello World from Zephyr!</h3>");
mg_printf(conn, "See also:\n");
mg_printf(conn, "<ul>\n");
mg_printf(conn, "<li><a href=/info>system info</a></li>\n");
mg_printf(conn, "<li><a href=/history>cookie demo</a></li>\n");
mg_printf(conn, "</ul>\n");
mg_printf(conn, "</body></html>\n");
return 200;
}
int system_info_handler(struct mg_connection *conn, void *cbdata)
{
static const struct json_obj_descr descr[] = {
FIELD(struct civetweb_info, version, JSON_TOK_STRING),
FIELD(struct civetweb_info, os, JSON_TOK_STRING),
FIELD(struct civetweb_info, feature_list, JSON_TOK_STRING),
FIELD(struct civetweb_info, build, JSON_TOK_STRING),
FIELD(struct civetweb_info, compiler, JSON_TOK_STRING),
FIELD(struct civetweb_info, data_model, JSON_TOK_STRING),
};
struct civetweb_info info = {};
char info_str[1024] = {};
int ret;
int size;
size = mg_get_system_info(info_str, sizeof(info_str));
ret = json_obj_parse(info_str, size, descr, ARRAY_SIZE(descr), &info);
send_ok(conn);
if (ret < 0) {
mg_printf(conn, "Could not retrieve: %d\n", ret);
return 500;
}
mg_printf(conn, "<html><body>");
mg_printf(conn, "<h3>Server info</h3>");
mg_printf(conn, "<ul>\n");
mg_printf(conn, "<li>host os - %s</li>\n", info.os);
mg_printf(conn, "<li>server - civetweb %s</li>\n", info.version);
mg_printf(conn, "<li>compiler - %s</li>\n", info.compiler);
mg_printf(conn, "<li>board - %s</li>\n", CONFIG_BOARD);
mg_printf(conn, "</ul>\n");
mg_printf(conn, "</body></html>\n");
return 200;
}
int history_handler(struct mg_connection *conn, void *cbdata)
{
const struct mg_request_info *req_info = mg_get_request_info(conn);
const char *cookie = mg_get_header(conn, "Cookie");
char history_str[64];
mg_get_cookie(cookie, "history", history_str, sizeof(history_str));
mg_printf(conn, "HTTP/1.1 200 OK\r\n");
mg_printf(conn, "Connection: close\r\n");
mg_printf(conn, "Set-Cookie: history='%s'\r\n", req_info->local_uri);
mg_printf(conn, "Content-Type: text/html\r\n\r\n");
mg_printf(conn, "<html><body>");
mg_printf(conn, "<h3>Your URI is: %s<h3>\n", req_info->local_uri);
if (history_str[0] == 0) {
mg_printf(conn, "<h5>This is your first visit.</h5>\n");
} else {
mg_printf(conn, "<h5>your last /history visit was: %s</h5>\n",
history_str);
}
mg_printf(conn, "Some cookie-saving links to try:\n");
mg_printf(conn, "<ul>\n");
mg_printf(conn, "<li><a href=/history/first>first</a></li>\n");
mg_printf(conn, "<li><a href=/history/second>second</a></li>\n");
mg_printf(conn, "<li><a href=/history/third>third</a></li>\n");
mg_printf(conn, "<li><a href=/history/fourth>fourth</a></li>\n");
mg_printf(conn, "<li><a href=/history/fifth>fifth</a></li>\n");
mg_printf(conn, "</ul>\n");
mg_printf(conn, "</body></html>\n");
return 200;
}
void *main_pthread(void *arg)
{
static const char * const options[] = {
"listening_ports",
STRINGIFY(HTTP_PORT),
"num_threads",
"1",
"max_request_size",
STRINGIFY(MAX_REQUEST_SIZE_BYTES),
NULL
};
struct mg_callbacks callbacks;
struct mg_context *ctx;
(void)arg;
memset(&callbacks, 0, sizeof(callbacks));
ctx = mg_start(&callbacks, 0, (const char **)options);
if (ctx == NULL) {