1214 lines
27 KiB
Plaintext
1214 lines
27 KiB
Plaintext
// Moved from docs/src/platform-integration/L4RE_INTEGRATION.adoc to docs/working/scratch/src/platform-integration/L4RE_INTEGRATION.adoc on 2026-06-16 (docs reorg Phase 2)
|
|
== StarForth L4Re Integration Guide
|
|
:toc: left
|
|
:toc-title: Contents
|
|
:toclevels: 3
|
|
xref:../README.adoc[← Back to Documentation Index]
|
|
|
|
|
|
|
|
=== Overview
|
|
|
|
This guide covers integrating StarForth into the L4Re Operating System
|
|
Framework, including:
|
|
|
|
* Building as an L4Re package
|
|
* Memory management with L4Re dataspaces
|
|
* IPC communication between VMs
|
|
* Integration with StarshipOS
|
|
* Kernel-level integration (if needed)
|
|
|
|
=== Table of Contents
|
|
|
|
[arabic]
|
|
. link:#l4re-basics[L4Re Basics]
|
|
. link:#package-structure[Package Structure]
|
|
. link:#build-system-integration[Build System Integration]
|
|
. link:#memory-management[Memory Management]
|
|
. link:#ipc-communication[IPC Communication]
|
|
. link:#multi-vm-architecture[Multi-VM Architecture]
|
|
. link:#kernel-integration[Kernel Integration]
|
|
. link:#starshipos-specifics[StarshipOS Specifics]
|
|
|
|
=== L4Re Basics
|
|
|
|
==== What is L4Re?
|
|
|
|
L4Re (L4 Runtime Environment) is a user-level infrastructure for
|
|
building systems on top of the L4 microkernel ( Fiasco.OC). It provides:
|
|
|
|
* Memory management (dataspaces)
|
|
* Task/thread management
|
|
* Inter-Process Communication (IPC)
|
|
* Device drivers
|
|
* Runtime libraries
|
|
|
|
==== Key Concepts
|
|
|
|
[width="100%",cols="27%,73%",options="header",]
|
|
|===
|
|
|Concept |Description
|
|
|*Task* |Address space + threads
|
|
|*Dataspace* |Memory object (like file or anonymous memory)
|
|
|*Capability* |Reference to kernel object (task, dataspace, IPC gate)
|
|
|*IPC Gate* |Communication endpoint
|
|
|*Region Manager* |Virtual memory management
|
|
|*Name Server* |Service discovery
|
|
|===
|
|
|
|
=== Package Structure
|
|
|
|
==== Directory Layout
|
|
|
|
Create this structure in your L4Re source tree:
|
|
|
|
....
|
|
l4/pkg/starforth/
|
|
├── Control # Package metadata
|
|
├── Makefile # Top-level build
|
|
├── server/ # Main StarForth server
|
|
│ ├── Makefile
|
|
│ └── src/
|
|
│ ├── main.cc # L4Re entry point
|
|
│ ├── l4_vm.cc # L4Re-specific VM wrapper
|
|
│ └── l4_vm.h
|
|
├── lib/ # StarForth as library
|
|
│ ├── Makefile
|
|
│ └── src/ # Your existing src/ files
|
|
├── include/ # Your existing include/ files
|
|
└── examples/ # Example clients
|
|
├── Makefile
|
|
└── forth_client.cc
|
|
....
|
|
|
|
==== Control File
|
|
|
|
Create `+l4/pkg/starforth/Control+`:
|
|
|
|
....
|
|
provides: starforth
|
|
requires: libc libstdc++ l4re-core
|
|
maintainer: rajames
|
|
description: StarForth VM for L4Re
|
|
license: CC0
|
|
....
|
|
|
|
=== Build System Integration
|
|
|
|
==== Top-Level Makefile
|
|
|
|
Create `+l4/pkg/starforth/Makefile+`:
|
|
|
|
[source,makefile]
|
|
----
|
|
PKGDIR ?= .
|
|
L4DIR ?= $(PKGDIR)/../..
|
|
|
|
# Subdirectories to build
|
|
TARGET = lib server examples
|
|
|
|
include $(L4DIR)/mk/subdir.mk
|
|
----
|
|
|
|
==== Library Makefile
|
|
|
|
Create `+l4/pkg/starforth/lib/Makefile+`:
|
|
|
|
[source,makefile]
|
|
----
|
|
PKGDIR ?= ..
|
|
L4DIR ?= $(PKGDIR)/../..
|
|
|
|
TARGET = libstarforth.a libstarforth.so
|
|
PC_FILENAME = libstarforth
|
|
|
|
# Architecture detection
|
|
ifeq ($(ARCH),amd64)
|
|
ARCH_FLAGS = -march=x86-64-v2
|
|
ARCH_DEFINES = -DARCH_X86_64=1
|
|
else ifeq ($(ARCH),arm64)
|
|
ARCH_FLAGS = -march=armv8-a+crc+simd
|
|
ARCH_DEFINES = -DARCH_ARM64=1
|
|
endif
|
|
|
|
# Compiler flags
|
|
CFLAGS = -std=c99 -O3 $(ARCH_FLAGS) $(ARCH_DEFINES) \
|
|
-DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 \
|
|
-DL4RE_BUILD=1
|
|
CXXFLAGS = -std=c++17 -O3 $(ARCH_FLAGS)
|
|
|
|
# Include paths
|
|
PRIVATE_INCDIR = $(PKGDIR)/include $(PKGDIR)/lib/src
|
|
|
|
# Source files (adapt to your structure)
|
|
SRC_C = vm.c stack_management.c memory_management.c \
|
|
dictionary_management.c io.c log.c profiler.c \
|
|
repl.c vm_api.c vm_debug.c word_registry.c \
|
|
word_source/arithmetic_words.c \
|
|
word_source/stack_words.c \
|
|
word_source/logical_words.c \
|
|
word_source/memory_words.c \
|
|
word_source/control_words.c \
|
|
word_source/defining_words.c \
|
|
word_source/dictionary_words.c \
|
|
word_source/double_words.c \
|
|
word_source/mixed_arithmetic_words.c \
|
|
word_source/return_stack_words.c \
|
|
word_source/string_words.c \
|
|
word_source/system_words.c \
|
|
word_source/vocabulary_words.c \
|
|
word_source/io_words.c \
|
|
word_source/format_words.c \
|
|
word_source/block_words.c \
|
|
word_source/editor_words.c \
|
|
word_source/starforth_words.c
|
|
|
|
# L4Re-specific wrapper
|
|
SRC_CC = l4_vm.cc
|
|
|
|
# Dependencies
|
|
REQUIRES_LIBS = libc libstdc++
|
|
|
|
include $(L4DIR)/mk/lib.mk
|
|
----
|
|
|
|
==== Server Makefile
|
|
|
|
Create `+l4/pkg/starforth/server/Makefile+`:
|
|
|
|
[source,makefile]
|
|
----
|
|
PKGDIR ?= ..
|
|
L4DIR ?= $(PKGDIR)/../..
|
|
|
|
TARGET = starforth_server
|
|
MODE = static
|
|
|
|
# Architecture detection
|
|
ifeq ($(ARCH),amd64)
|
|
ARCH_FLAGS = -march=x86-64-v2
|
|
ARCH_DEFINES = -DARCH_X86_64=1
|
|
else ifeq ($(ARCH),arm64)
|
|
ARCH_FLAGS = -march=armv8-a+crc+simd
|
|
ARCH_DEFINES = -DARCH_ARM64=1
|
|
endif
|
|
|
|
CXXFLAGS = -std=c++17 -O3 $(ARCH_FLAGS) $(ARCH_DEFINES)
|
|
|
|
PRIVATE_INCDIR = $(PKGDIR)/include
|
|
|
|
SRC_CC = main.cc l4_vm.cc
|
|
|
|
REQUIRES_LIBS = libstarforth l4re_c l4re_c-util libstdc++ libc
|
|
|
|
include $(L4DIR)/mk/prog.mk
|
|
----
|
|
|
|
=== Memory Management
|
|
|
|
==== L4Re Dataspace Integration
|
|
|
|
Create `+l4/pkg/starforth/server/src/l4_vm.h+`:
|
|
|
|
[source,cpp]
|
|
----
|
|
#pragma once
|
|
|
|
#include <l4/re/c/dataspace.h>
|
|
#include <l4/re/c/mem_alloc.h>
|
|
#include <l4/re/c/rm.h>
|
|
#include <l4/re/c/util/cap_alloc.h>
|
|
|
|
extern "C" {
|
|
#include "vm.h"
|
|
}
|
|
|
|
namespace StarForth {
|
|
|
|
/**
|
|
* L4Re-specific VM wrapper
|
|
*
|
|
* Uses L4Re dataspaces for memory management instead of malloc()
|
|
*/
|
|
class L4VM {
|
|
public:
|
|
L4VM();
|
|
~L4VM();
|
|
|
|
// Initialize VM with L4Re dataspaces
|
|
bool init(size_t memory_size = VM_MEMORY_SIZE);
|
|
|
|
// Get underlying VM structure
|
|
VM* get_vm() { return &vm_; }
|
|
|
|
// Cleanup
|
|
void cleanup();
|
|
|
|
// Memory management
|
|
void* map_dataspace(l4_cap_idx_t ds, size_t size);
|
|
bool unmap_dataspace(void* addr, size_t size);
|
|
|
|
private:
|
|
VM vm_;
|
|
l4_cap_idx_t memory_ds_; // Dataspace for VM memory
|
|
void* memory_addr_; // Mapped address
|
|
size_t memory_size_;
|
|
bool initialized_;
|
|
};
|
|
|
|
} // namespace StarForth
|
|
----
|
|
|
|
Create `+l4/pkg/starforth/server/src/l4_vm.cc+`:
|
|
|
|
[source,cpp]
|
|
----
|
|
#include "l4_vm.h"
|
|
#include <l4/re/env>
|
|
#include <l4/sys/err.h>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
extern "C" {
|
|
#include "log.h"
|
|
}
|
|
|
|
namespace StarForth {
|
|
|
|
L4VM::L4VM()
|
|
: memory_ds_(L4_INVALID_CAP),
|
|
memory_addr_(nullptr),
|
|
memory_size_(0),
|
|
initialized_(false)
|
|
{
|
|
memset(&vm_, 0, sizeof(vm_));
|
|
}
|
|
|
|
L4VM::~L4VM() {
|
|
cleanup();
|
|
}
|
|
|
|
bool L4VM::init(size_t memory_size) {
|
|
if (initialized_) {
|
|
return false;
|
|
}
|
|
|
|
memory_size_ = memory_size;
|
|
|
|
// Allocate capability slot
|
|
memory_ds_ = l4re_util_cap_alloc();
|
|
if (l4_is_invalid_cap(memory_ds_)) {
|
|
log_message(LOG_ERROR, "L4VM: Failed to allocate capability");
|
|
return false;
|
|
}
|
|
|
|
// Allocate dataspace
|
|
long ret = l4re_ma_alloc(memory_size_, memory_ds_, 0);
|
|
if (ret) {
|
|
log_message(LOG_ERROR, "L4VM: Failed to allocate dataspace: %ld", ret);
|
|
l4re_util_cap_free(memory_ds_);
|
|
return false;
|
|
}
|
|
|
|
// Map dataspace into our address space
|
|
ret = l4re_rm_attach((void**)&memory_addr_, memory_size_,
|
|
L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW,
|
|
memory_ds_, 0, L4_PAGESHIFT);
|
|
if (ret) {
|
|
log_message(LOG_ERROR, "L4VM: Failed to map dataspace: %ld", ret);
|
|
l4re_util_cap_free(memory_ds_);
|
|
return false;
|
|
}
|
|
|
|
log_message(LOG_INFO, "L4VM: Allocated %zu bytes at %p",
|
|
memory_size_, memory_addr_);
|
|
|
|
// Initialize VM structure
|
|
vm_.memory = static_cast<uint8_t*>(memory_addr_);
|
|
vm_.dsp = -1;
|
|
vm_.rsp = -1;
|
|
vm_.here = 0;
|
|
vm_.exit_colon = 0;
|
|
vm_.error = 0;
|
|
vm_.halted = 0;
|
|
|
|
// Initialize VM subsystems (from your vm_init function)
|
|
vm_align(&vm_);
|
|
|
|
// Allocate SCR
|
|
void *p = vm_allot(&vm_, sizeof(cell_t));
|
|
if (!p) {
|
|
log_message(LOG_ERROR, "L4VM: SCR allot failed");
|
|
cleanup();
|
|
return false;
|
|
}
|
|
vm_.scr_addr = (vaddr_t)((uint8_t*)p - vm_.memory);
|
|
vm_store_cell(&vm_, vm_.scr_addr, 0);
|
|
|
|
// Allocate STATE
|
|
p = vm_allot(&vm_, sizeof(cell_t));
|
|
if (!p) {
|
|
log_message(LOG_ERROR, "L4VM: STATE allot failed");
|
|
cleanup();
|
|
return false;
|
|
}
|
|
vm_.state_addr = (vaddr_t)((uint8_t*)p - vm_.memory);
|
|
vm_store_cell(&vm_, vm_.state_addr, 0);
|
|
vm_.state_var = 0;
|
|
|
|
// Allocate BASE
|
|
p = vm_allot(&vm_, sizeof(cell_t));
|
|
if (!p) {
|
|
log_message(LOG_ERROR, "L4VM: BASE allot failed");
|
|
cleanup();
|
|
return false;
|
|
}
|
|
vm_.base_addr = (vaddr_t)((uint8_t*)p - vm_.memory);
|
|
vm_store_cell(&vm_, vm_.base_addr, 10);
|
|
vm_.base = 10;
|
|
|
|
initialized_ = true;
|
|
log_message(LOG_INFO, "L4VM: Initialization complete");
|
|
return true;
|
|
}
|
|
|
|
void L4VM::cleanup() {
|
|
if (memory_addr_) {
|
|
l4re_rm_detach(memory_addr_);
|
|
memory_addr_ = nullptr;
|
|
}
|
|
|
|
if (l4_is_valid_cap(memory_ds_)) {
|
|
// Note: dataspace will be freed when capability is released
|
|
l4re_util_cap_free(memory_ds_);
|
|
memory_ds_ = L4_INVALID_CAP;
|
|
}
|
|
|
|
initialized_ = false;
|
|
}
|
|
|
|
void* L4VM::map_dataspace(l4_cap_idx_t ds, size_t size) {
|
|
void* addr = nullptr;
|
|
long ret = l4re_rm_attach(&addr, size,
|
|
L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW,
|
|
ds, 0, L4_PAGESHIFT);
|
|
if (ret) {
|
|
log_message(LOG_ERROR, "L4VM: Failed to map dataspace: %ld", ret);
|
|
return nullptr;
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
bool L4VM::unmap_dataspace(void* addr, size_t size) {
|
|
(void)size; // L4Re tracks size internally
|
|
long ret = l4re_rm_detach(addr);
|
|
if (ret) {
|
|
log_message(LOG_ERROR, "L4VM: Failed to unmap dataspace: %ld", ret);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace StarForth
|
|
----
|
|
|
|
=== IPC Communication
|
|
|
|
==== Server Main Entry Point
|
|
|
|
Create `+l4/pkg/starforth/server/src/main.cc+`:
|
|
|
|
[source,cpp]
|
|
----
|
|
#include <l4/re/env>
|
|
#include <l4/re/util/cap_alloc>
|
|
#include <l4/re/util/object_registry>
|
|
#include <l4/cxx/ipc_server>
|
|
#include <l4/sys/cxx/ipc_epiface>
|
|
#include <cstdio>
|
|
|
|
#include "l4_vm.h"
|
|
|
|
extern "C" {
|
|
#include "vm.h"
|
|
#include "log.h"
|
|
#include "word_registry.h"
|
|
}
|
|
|
|
// StarForth IPC protocol opcodes
|
|
enum {
|
|
OP_INTERPRET = 0,
|
|
OP_PUSH = 1,
|
|
OP_POP = 2,
|
|
OP_GET_STATE = 3,
|
|
OP_RESET = 4,
|
|
};
|
|
|
|
/**
|
|
* StarForth server implementing IPC interface
|
|
*/
|
|
class StarForth_server : public L4::Epiface_t<StarForth_server, L4::Kobject>
|
|
{
|
|
public:
|
|
StarForth_server() {
|
|
if (!l4vm_.init()) {
|
|
printf("Failed to initialize L4VM\n");
|
|
return;
|
|
}
|
|
|
|
// Register standard Forth words
|
|
VM* vm = l4vm_.get_vm();
|
|
register_stack_words(vm);
|
|
register_arithmetic_words(vm);
|
|
register_logical_words(vm);
|
|
register_memory_words(vm);
|
|
register_control_words(vm);
|
|
// ... register other word sets ...
|
|
|
|
printf("StarForth server initialized\n");
|
|
}
|
|
|
|
// IPC dispatch - handle incoming messages
|
|
long op_dispatch(l4_umword_t obj, L4::Ipc::Iostream &ios) {
|
|
l4_msgtag_t tag;
|
|
ios >> tag;
|
|
|
|
if (tag.label() == 0) {
|
|
// Handle our custom protocol
|
|
l4_umword_t opcode;
|
|
ios >> opcode;
|
|
|
|
switch (opcode) {
|
|
case OP_INTERPRET:
|
|
return handle_interpret(ios);
|
|
case OP_PUSH:
|
|
return handle_push(ios);
|
|
case OP_POP:
|
|
return handle_pop(ios);
|
|
case OP_GET_STATE:
|
|
return handle_get_state(ios);
|
|
case OP_RESET:
|
|
return handle_reset(ios);
|
|
default:
|
|
return -L4_EINVAL;
|
|
}
|
|
}
|
|
|
|
return -L4_ENOSYS;
|
|
}
|
|
|
|
private:
|
|
long handle_interpret(L4::Ipc::Iostream &ios) {
|
|
char buffer[256];
|
|
unsigned long len;
|
|
|
|
ios >> L4::Ipc::buf_cp_in(buffer, sizeof(buffer), len);
|
|
buffer[len < sizeof(buffer) ? len : sizeof(buffer)-1] = '\0';
|
|
|
|
VM* vm = l4vm_.get_vm();
|
|
vm_interpret(vm, buffer);
|
|
|
|
// Send reply with error status
|
|
ios << L4::Ipc::Small_buf(&vm->error, sizeof(vm->error));
|
|
return L4_EOK;
|
|
}
|
|
|
|
long handle_push(L4::Ipc::Iostream &ios) {
|
|
cell_t value;
|
|
ios >> value;
|
|
|
|
VM* vm = l4vm_.get_vm();
|
|
vm_push(vm, value);
|
|
|
|
ios << L4::Ipc::Small_buf(&vm->error, sizeof(vm->error));
|
|
return L4_EOK;
|
|
}
|
|
|
|
long handle_pop(L4::Ipc::Iostream &ios) {
|
|
VM* vm = l4vm_.get_vm();
|
|
cell_t value = vm_pop(vm);
|
|
|
|
ios << value;
|
|
ios << L4::Ipc::Small_buf(&vm->error, sizeof(vm->error));
|
|
return L4_EOK;
|
|
}
|
|
|
|
long handle_get_state(L4::Ipc::Iostream &ios) {
|
|
VM* vm = l4vm_.get_vm();
|
|
|
|
struct {
|
|
int dsp;
|
|
int rsp;
|
|
int error;
|
|
int halted;
|
|
} state;
|
|
|
|
state.dsp = vm->dsp;
|
|
state.rsp = vm->rsp;
|
|
state.error = vm->error;
|
|
state.halted = vm->halted;
|
|
|
|
ios << L4::Ipc::buf_cp_out(L4::Ipc::Small_buf(&state, sizeof(state)));
|
|
return L4_EOK;
|
|
}
|
|
|
|
long handle_reset(L4::Ipc::Iostream &ios) {
|
|
l4vm_.cleanup();
|
|
if (!l4vm_.init()) {
|
|
int error = 1;
|
|
ios << error;
|
|
return -L4_ENOMEM;
|
|
}
|
|
|
|
int success = 0;
|
|
ios << success;
|
|
return L4_EOK;
|
|
}
|
|
|
|
StarForth::L4VM l4vm_;
|
|
};
|
|
|
|
int main() {
|
|
printf("StarForth L4Re Server starting...\n");
|
|
|
|
// Create server object
|
|
static StarForth_server server;
|
|
|
|
// Register with name server
|
|
L4Re::Env const *env = L4Re::Env::env();
|
|
|
|
// Get capability to registry
|
|
L4::Cap<void> registry = env->get_cap<void>("starforth");
|
|
if (!registry.is_valid()) {
|
|
printf("Failed to get starforth capability\n");
|
|
return 1;
|
|
}
|
|
|
|
// Create object registry for IPC
|
|
static L4Re::Util::Registry_server<> registry_server(
|
|
L4::cap_reinterpret_cast<L4::Thread>(env->main_thread()),
|
|
env->factory());
|
|
|
|
// Register server
|
|
if (!registry_server.registry()->register_obj(&server, "starforth").is_valid()) {
|
|
printf("Failed to register server object\n");
|
|
return 1;
|
|
}
|
|
|
|
printf("StarForth server ready\n");
|
|
|
|
// Enter server loop
|
|
registry_server.loop();
|
|
|
|
return 0;
|
|
}
|
|
----
|
|
|
|
==== Client Example
|
|
|
|
Create `+l4/pkg/starforth/examples/forth_client.cc+`:
|
|
|
|
[source,cpp]
|
|
----
|
|
#include <l4/re/env>
|
|
#include <l4/re/util/cap_alloc>
|
|
#include <l4/sys/ipc.h>
|
|
#include <l4/sys/types.h>
|
|
#include <cstdio>
|
|
|
|
extern "C" {
|
|
#include "vm.h"
|
|
}
|
|
|
|
// Match server opcodes
|
|
enum {
|
|
OP_INTERPRET = 0,
|
|
OP_PUSH = 1,
|
|
OP_POP = 2,
|
|
OP_GET_STATE = 3,
|
|
OP_RESET = 4,
|
|
};
|
|
|
|
class StarForthClient {
|
|
public:
|
|
StarForthClient() : server_cap_(L4_INVALID_CAP) {
|
|
// Get capability to StarForth server
|
|
L4Re::Env const *env = L4Re::Env::env();
|
|
server_cap_ = env->get_cap<void>("starforth");
|
|
|
|
if (!server_cap_.is_valid()) {
|
|
printf("Failed to get starforth server capability\n");
|
|
}
|
|
}
|
|
|
|
bool interpret(const char* code) {
|
|
if (!server_cap_.is_valid()) return false;
|
|
|
|
l4_msgtag_t tag = l4_msgtag(0, 0, 0, 0);
|
|
l4_msg_regs_t *mr = l4_utcb_mr();
|
|
|
|
// Send opcode
|
|
mr->mr[0] = OP_INTERPRET;
|
|
|
|
// Send code string
|
|
size_t len = strlen(code);
|
|
memcpy(&mr->mr[1], code, len);
|
|
|
|
tag = l4_msgtag(0, 1 + (len + sizeof(l4_umword_t) - 1) / sizeof(l4_umword_t),
|
|
0, 0);
|
|
tag = l4_ipc_call(server_cap_.cap(), l4_utcb(), tag, L4_IPC_NEVER);
|
|
|
|
if (l4_ipc_error(tag, l4_utcb())) {
|
|
printf("IPC error: %ld\n", l4_ipc_error(tag, l4_utcb()));
|
|
return false;
|
|
}
|
|
|
|
// Get error status from reply
|
|
int error;
|
|
memcpy(&error, &mr->mr[0], sizeof(error));
|
|
return error == 0;
|
|
}
|
|
|
|
bool push(cell_t value) {
|
|
if (!server_cap_.is_valid()) return false;
|
|
|
|
l4_msgtag_t tag = l4_msgtag(0, 2, 0, 0);
|
|
l4_msg_regs_t *mr = l4_utcb_mr();
|
|
|
|
mr->mr[0] = OP_PUSH;
|
|
memcpy(&mr->mr[1], &value, sizeof(value));
|
|
|
|
tag = l4_ipc_call(server_cap_.cap(), l4_utcb(), tag, L4_IPC_NEVER);
|
|
|
|
if (l4_ipc_error(tag, l4_utcb())) {
|
|
return false;
|
|
}
|
|
|
|
int error;
|
|
memcpy(&error, &mr->mr[0], sizeof(error));
|
|
return error == 0;
|
|
}
|
|
|
|
cell_t pop(bool* success = nullptr) {
|
|
if (!server_cap_.is_valid()) {
|
|
if (success) *success = false;
|
|
return 0;
|
|
}
|
|
|
|
l4_msgtag_t tag = l4_msgtag(0, 1, 0, 0);
|
|
l4_msg_regs_t *mr = l4_utcb_mr();
|
|
|
|
mr->mr[0] = OP_POP;
|
|
|
|
tag = l4_ipc_call(server_cap_.cap(), l4_utcb(), tag, L4_IPC_NEVER);
|
|
|
|
if (l4_ipc_error(tag, l4_utcb())) {
|
|
if (success) *success = false;
|
|
return 0;
|
|
}
|
|
|
|
cell_t value;
|
|
memcpy(&value, &mr->mr[0], sizeof(value));
|
|
|
|
int error;
|
|
memcpy(&error, &mr->mr[1], sizeof(error));
|
|
|
|
if (success) *success = (error == 0);
|
|
return value;
|
|
}
|
|
|
|
private:
|
|
L4::Cap<void> server_cap_;
|
|
};
|
|
|
|
int main() {
|
|
printf("StarForth Client Example\n");
|
|
|
|
StarForthClient client;
|
|
|
|
// Test pushing and popping
|
|
printf("Pushing 42...\n");
|
|
if (client.push(42)) {
|
|
printf("Success\n");
|
|
}
|
|
|
|
printf("Pushing 17...\n");
|
|
client.push(17);
|
|
|
|
// Pop and print
|
|
bool success;
|
|
cell_t val = client.pop(&success);
|
|
if (success) {
|
|
printf("Popped: %ld\n", val);
|
|
}
|
|
|
|
val = client.pop(&success);
|
|
if (success) {
|
|
printf("Popped: %ld\n", val);
|
|
}
|
|
|
|
// Interpret some Forth code
|
|
printf("\nInterpreting: 10 20 + .\n");
|
|
if (client.interpret("10 20 + .")) {
|
|
printf("Interpretation successful\n");
|
|
}
|
|
|
|
printf("\nInterpreting: : SQUARE DUP * ;\n");
|
|
client.interpret(": SQUARE DUP * ;");
|
|
|
|
printf("Interpreting: 5 SQUARE .\n");
|
|
client.interpret("5 SQUARE .");
|
|
|
|
return 0;
|
|
}
|
|
----
|
|
|
|
=== Multi-VM Architecture
|
|
|
|
==== Design Pattern: Multiple Forth VMs
|
|
|
|
....
|
|
┌─────────────────┐
|
|
│ Ned (Root) │
|
|
│ - Name Server │
|
|
│ - VM Manager │
|
|
└────────┬────────┘
|
|
│
|
|
┌────┴─────┬─────────────┐
|
|
│ │ │
|
|
┌───▼────┐ ┌──▼─────┐ ┌─────▼────┐
|
|
│ VM 1 │ │ VM 2 │ │ VM 3 │
|
|
│ Forth │ │ Forth │ │ Forth │
|
|
│ Tasks │ │ Server │ │ REPL │
|
|
└────────┘ └────────┘ └──────────┘
|
|
....
|
|
|
|
==== VM Manager (Ned Configuration)
|
|
|
|
Create `+l4/conf/modules.list+` entry:
|
|
|
|
[source,lua]
|
|
----
|
|
-- StarForth VMs
|
|
entry {
|
|
name = "starforth-server",
|
|
cmdline = "rom/starforth_server",
|
|
caps = {
|
|
starforth = L4.default_loader:new_channel(),
|
|
},
|
|
}
|
|
|
|
entry {
|
|
name = "starforth-client",
|
|
cmdline = "rom/forth_client",
|
|
caps = {
|
|
starforth = L4.Env.starforth_server:svr(),
|
|
},
|
|
}
|
|
----
|
|
|
|
=== Kernel Integration
|
|
|
|
==== For StarshipOS: Forth in Kernel Context
|
|
|
|
⚠️ *Warning*: Running Forth in kernel requires extreme care!
|
|
|
|
===== Use Cases
|
|
|
|
[arabic]
|
|
. *Kernel configuration* - Runtime kernel tunables
|
|
. *Device driver scripting* - Hotpatch device drivers
|
|
. *Debugging* - Interactive kernel debugging
|
|
|
|
===== Limitations
|
|
|
|
* No malloc/free (use static memory pools)
|
|
* No syscalls
|
|
* Limited stack space
|
|
* Must be interrupt-safe
|
|
* Must not block
|
|
|
|
===== Example: Kernel Module
|
|
|
|
Create `+l4/pkg/starforth/kernel/starforth_kernel.cc+`:
|
|
|
|
[source,cpp]
|
|
----
|
|
#include <l4/sys/kernel_object.h>
|
|
#include <l4/sys/kip.h>
|
|
|
|
extern "C" {
|
|
#include "vm.h"
|
|
}
|
|
|
|
// Static memory pool for kernel VM
|
|
static uint8_t kernel_vm_memory[256 * 1024] __attribute__((aligned(4096)));
|
|
static VM kernel_vm;
|
|
static bool kernel_vm_initialized = false;
|
|
|
|
/**
|
|
* Initialize kernel-mode Forth VM
|
|
*
|
|
* Called during kernel initialization
|
|
*/
|
|
extern "C" void starforth_kernel_init(void) {
|
|
if (kernel_vm_initialized) return;
|
|
|
|
// Initialize VM with static memory
|
|
kernel_vm.memory = kernel_vm_memory;
|
|
kernel_vm.dsp = -1;
|
|
kernel_vm.rsp = -1;
|
|
kernel_vm.here = 0;
|
|
kernel_vm.error = 0;
|
|
kernel_vm.halted = 0;
|
|
|
|
// Register minimal word set (safe for kernel)
|
|
// NO I/O words, NO blocking operations
|
|
register_arithmetic_words(&kernel_vm);
|
|
register_logical_words(&kernel_vm);
|
|
register_stack_words(&kernel_vm);
|
|
|
|
// Add kernel-specific words
|
|
// e.g., words to read/write kernel data structures
|
|
|
|
kernel_vm_initialized = true;
|
|
}
|
|
|
|
/**
|
|
* Execute Forth code in kernel context
|
|
*
|
|
* DANGEROUS: Only call from trusted sources!
|
|
*/
|
|
extern "C" int starforth_kernel_exec(const char* code) {
|
|
if (!kernel_vm_initialized) return -1;
|
|
|
|
vm_interpret(&kernel_vm, code);
|
|
|
|
return kernel_vm.error;
|
|
}
|
|
|
|
/**
|
|
* Kernel debugger integration
|
|
*/
|
|
extern "C" void starforth_kernel_repl(void) {
|
|
// Interactive REPL for kernel debugging
|
|
// Use polling serial I/O, no interrupts
|
|
|
|
char buffer[256];
|
|
|
|
while (1) {
|
|
// Read line from serial (implement polling version)
|
|
kernel_serial_read_line(buffer, sizeof(buffer));
|
|
|
|
if (strcmp(buffer, "exit") == 0) break;
|
|
|
|
vm_interpret(&kernel_vm, buffer);
|
|
|
|
if (kernel_vm.error) {
|
|
kernel_serial_write("Error\n");
|
|
kernel_vm.error = 0;
|
|
}
|
|
}
|
|
}
|
|
----
|
|
|
|
=== StarshipOS Specifics
|
|
|
|
==== Integration Points
|
|
|
|
[arabic]
|
|
. *Boot Service* - Start StarForth server during boot
|
|
. *System Configuration* - Use Forth for configuration
|
|
. *Hot-patching* - Runtime system updates
|
|
. *Interactive Debugging* - REPL for live system
|
|
|
|
==== Boot Integration
|
|
|
|
Modify StarshipOS boot sequence:
|
|
|
|
[source,cpp]
|
|
----
|
|
// In your StarshipOS init task
|
|
void starship_boot() {
|
|
// ... other initialization ...
|
|
|
|
// Start StarForth server
|
|
L4Re::Util::Env_ns ns;
|
|
ns.register_obj("starforth",
|
|
L4Re::Env::env()->get_cap<void>("starforth_server"));
|
|
|
|
// Load boot scripts
|
|
starforth_load_script("/boot/init.fth");
|
|
|
|
// ... continue boot ...
|
|
}
|
|
----
|
|
|
|
==== System Configuration Example
|
|
|
|
Create `+/boot/init.fth+`:
|
|
|
|
[source,forth]
|
|
----
|
|
\ StarshipOS Boot Configuration
|
|
|
|
." StarshipOS initializing..." CR
|
|
|
|
\ Configure kernel parameters
|
|
: SET-KERNEL-PARAM ( value param-id -- )
|
|
\ ... syscall to set kernel parameter ...
|
|
;
|
|
|
|
\ Example: Set scheduler quantum
|
|
100 1 SET-KERNEL-PARAM
|
|
|
|
\ Start system services
|
|
: START-SERVICE ( service-name -- )
|
|
\ ... load and start service ...
|
|
;
|
|
|
|
" network-stack" START-SERVICE
|
|
" file-system" START-SERVICE
|
|
|
|
." Boot complete!" CR
|
|
----
|
|
|
|
=== Building and Testing
|
|
|
|
==== Build Commands
|
|
|
|
[source,bash]
|
|
----
|
|
# From L4Re source root
|
|
cd l4/pkg/starforth
|
|
|
|
# Build for x86_64
|
|
make ARCH=amd64 O=build_amd64
|
|
make ARCH=amd64 O=build_amd64 install
|
|
|
|
# Build for ARM64
|
|
make ARCH=arm64 O=build_arm64
|
|
make ARCH=arm64 O=build_arm64 install
|
|
|
|
# Cross-build from x86_64
|
|
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- O=build_arm64_cross
|
|
----
|
|
|
|
==== Creating Boot Image
|
|
|
|
[source,bash]
|
|
----
|
|
# Create modules list
|
|
cd l4/conf
|
|
cat > myconf.list << EOF
|
|
modaddr 0x02000000
|
|
|
|
entry starforth_server
|
|
kernel fiasco -serial_esc
|
|
roottask moe rom/myconf.cfg
|
|
module l4re
|
|
module ned rom/myconf.lua
|
|
module starforth_server
|
|
module forth_client
|
|
EOF
|
|
|
|
# Build image
|
|
make O=mybuild qemu E=myconf
|
|
----
|
|
|
|
==== Running in QEMU
|
|
|
|
[source,bash]
|
|
----
|
|
# x86_64
|
|
make O=mybuild qemu E=myconf
|
|
|
|
# ARM64 (for Raspberry Pi 4 testing)
|
|
make O=mybuild ARCH=arm64 PLATFORM_TYPE=rv_pbx qemu E=myconf
|
|
----
|
|
|
|
=== Performance Considerations
|
|
|
|
==== Memory Layout Optimization
|
|
|
|
[source,cpp]
|
|
----
|
|
// Align VM memory to huge pages (2MB on x86_64, ARM64)
|
|
#define VM_MEMORY_SIZE (2 * 1024 * 1024)
|
|
|
|
// Request huge page when allocating dataspace
|
|
l4re_ds_flags_t flags = L4RE_DS_F_NORMAL | L4RE_DS_F_ALIGN(21); // 2^21 = 2MB
|
|
long ret = l4re_ma_alloc_align(VM_MEMORY_SIZE, memory_ds_, flags, 21);
|
|
----
|
|
|
|
==== IPC Optimization
|
|
|
|
[source,cpp]
|
|
----
|
|
// Use shared memory for large data transfers
|
|
class FastForthClient {
|
|
public:
|
|
bool interpret_large(const char* code, size_t len) {
|
|
if (len < 1024) {
|
|
return interpret_small(code, len); // Use IPC
|
|
}
|
|
|
|
// Use shared dataspace for large code
|
|
l4_cap_idx_t ds = create_shared_buffer(len);
|
|
memcpy(shared_addr_, code, len);
|
|
|
|
// Send dataspace capability + offset
|
|
send_interpret_ds(ds, 0, len);
|
|
|
|
return true;
|
|
}
|
|
};
|
|
----
|
|
|
|
==== CPU Affinity
|
|
|
|
[source,cpp]
|
|
----
|
|
// Pin Forth VM to specific CPU for better cache locality
|
|
l4_sched_param_t sp = l4_sched_param(255, 0); // Priority 255, CPU 0
|
|
sp.affinity = l4_sched_cpu_set(0, 0); // CPU 0 only
|
|
|
|
l4_scheduler()->run_thread(L4Re::Env::env()->main_thread(), sp);
|
|
----
|
|
|
|
=== Debugging
|
|
|
|
==== GDB with L4Re
|
|
|
|
[source,bash]
|
|
----
|
|
# Start QEMU with GDB server
|
|
make O=mybuild qemu E=myconf QEMU_OPTIONS="-s -S"
|
|
|
|
# In another terminal
|
|
aarch64-linux-gnu-gdb build_arm64/pkg/starforth/server/starforth_server
|
|
(gdb) target remote :1234
|
|
(gdb) break main
|
|
(gdb) continue
|
|
----
|
|
|
|
==== JDB (Fiasco Kernel Debugger)
|
|
|
|
....
|
|
# Enter JDB
|
|
Ctrl+^ (or configured escape sequence)
|
|
|
|
# Show tasks
|
|
l t
|
|
|
|
# Switch to StarForth server task
|
|
t <task_id>
|
|
|
|
# Show threads
|
|
l T
|
|
|
|
# Backtrace
|
|
i s
|
|
....
|
|
|
|
==== Logging
|
|
|
|
[source,cpp]
|
|
----
|
|
// Use L4Re logging
|
|
#include <l4/re/util/debug>
|
|
|
|
L4Re::Util::Dbg log(L4Re::Util::Dbg::Info, "starforth");
|
|
|
|
log.printf("VM state: dsp=%d error=%d\n", vm->dsp, vm->error);
|
|
----
|
|
|
|
=== Security Considerations
|
|
|
|
==== Capability-based Security
|
|
|
|
[source,cpp]
|
|
----
|
|
// Restrict VM capabilities
|
|
class RestrictedVM {
|
|
public:
|
|
RestrictedVM() {
|
|
// Only grant specific capabilities
|
|
vm_grant_cap(CAP_MEMORY_READ);
|
|
vm_grant_cap(CAP_COMPUTE);
|
|
// Do NOT grant CAP_MEMORY_WRITE or CAP_IPC
|
|
}
|
|
};
|
|
----
|
|
|
|
==== Sandboxing
|
|
|
|
[source,lua]
|
|
----
|
|
-- In Ned configuration
|
|
local vm = L4.default_loader:start({
|
|
caps = {
|
|
-- Only grant necessary capabilities
|
|
icu = L4.Env.icu,
|
|
log = L4.Env.log,
|
|
-- No scheduler, no memory allocator
|
|
},
|
|
log = {"vm", "yellow"},
|
|
}, "rom/starforth_server")
|
|
----
|
|
|
|
=== Troubleshooting
|
|
|
|
==== Common Issues
|
|
|
|
*Problem*: Dataspace allocation fails
|
|
|
|
[source,cpp]
|
|
----
|
|
// Check quota
|
|
l4re_ma_query_quota();
|
|
|
|
// Increase quota in Ned config
|
|
entry {
|
|
mem = 8 * 1024 * 1024, -- 8MB quota
|
|
}
|
|
----
|
|
|
|
*Problem*: IPC timeout
|
|
|
|
[source,cpp]
|
|
----
|
|
// Increase timeout
|
|
l4_timeout_t timeout = l4_timeout(L4_IPC_TIMEOUT_NEVER);
|
|
tag = l4_ipc_call(cap, utcb, tag, timeout);
|
|
----
|
|
|
|
*Problem*: Capability not found
|
|
|
|
[source,lua]
|
|
----
|
|
-- Check Ned configuration
|
|
caps = {
|
|
starforth = L4.default_loader:new_channel(), -- Create channel
|
|
}
|
|
----
|
|
|
|
=== References
|
|
|
|
* https://l4re.org/doc/[L4Re Documentation]
|
|
* https://l4re.org/fiasco/[Fiasco.OC Reference Manual]
|
|
* https://l4re.org/doc/tutorial.html[L4Re Tutorial]
|
|
* https://os.inf.tu-dresden.de/L4/[TU Dresden L4 Research]
|
|
|
|
=== Next Steps
|
|
|
|
[arabic]
|
|
. Port your existing StarForth code to L4Re package structure
|
|
. Test in QEMU with L4Re
|
|
. Deploy to actual hardware (x86_64 or Raspberry Pi 4)
|
|
. Integrate with StarshipOS services
|
|
. Add security policies and sandboxing
|
|
. Performance tuning with real workloads
|
|
|
|
Good luck with your L4Re integration! 🚀
|