Skip to content

Commit

Permalink
Ported tutorials
Browse files Browse the repository at this point in the history
  • Loading branch information
guancio committed Oct 25, 2024
1 parent 2dc49a6 commit d5696ba
Show file tree
Hide file tree
Showing 38 changed files with 1,338 additions and 300 deletions.
291 changes: 291 additions & 0 deletions projects/tutorial-commons/utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
#define APP0_PID 0
#define APP1_PID 1

// See plat_conf.h
#define BOOT_PMP 0
#define RAM_MEM 1
#define UART_MEM 2
#define TIME_MEM 3
#define HART0_TIME 4
#define HART1_TIME 5
#define HART2_TIME 6
#define HART3_TIME 7
#define MONITOR 8
#define CHANNEL 9

// normal caps
#define UART_CAP 10
#define UART_PMP 1
#define BUFFER_PMP 2

// CAPS for second process
#define APP_1_CAP_PMP_MEM 0
#define APP_1_CAP_PMP_UART 10
#define APP_1_TIME 1
#define APP_1_CAP_SOCKET 3

#define APP_1_PMP_SLOT_MEM 0
#define APP_1_PMP_SLOT_UART 1
#define APP_1_PMP_SLOT_BUFFER 2

// CAPS for second process
#define FREE_CAP_BEGIN 12
#define FREE_CAP_END 20

// Other constants
#define APP_1_BASE_ADDR 0x80020000
#define APP_1_SIZE 0x10000
#define SHARED_BUFFER_BASE (APP_1_BASE_ADDR+APP_1_SIZE)
#define SHARED_BUFFER_SIZE 0x10000

void setup_uart()
{
uint64_t uart_addr = s3k_napot_encode(UART0_BASE_ADDR, 0x8);
// Derive a PMP capability for accessing UART
s3k_cap_derive(UART_MEM, UART_CAP, s3k_mk_pmp(uart_addr, S3K_MEM_RW));
// Load the derive PMP capability to PMP configuration
s3k_pmp_load(UART_CAP, UART_PMP);
// Synchronize PMP unit (hardware) with PMP configuration
// false => not full synchronization.
s3k_sync_mem();
}

void default_trap_handler(void) __attribute__((interrupt("machine")));
void default_trap_handler(void) {
// We enter here on illegal instructions, for example writing to
// protected area (UART).

// On an exception we do
// - tf.epc = tf.pc (save program counter)
// - tf.pc = tf.tpc (load trap handler address)
// - tf.esp = tf.sp (save stack pointer)
// - tf.sp = tf.tsp (load trap stack pointer)
// - tf.ecause = mcause (see RISC-V privileged spec)
// - tf.eval = mval (see RISC-V privileged spec)
// tf is the trap frame, all registers of our process
uint64_t epc = s3k_reg_read(S3K_REG_EPC);
uint64_t esp = s3k_reg_read(S3K_REG_ESP);
uint64_t ecause = s3k_reg_read(S3K_REG_ECAUSE);
uint64_t eval = s3k_reg_read(S3K_REG_EVAL);

alt_printf(
"error info:\n- epc: 0x%x\n- esp: 0x%x\n- ecause: 0x%x\n- eval: 0x%x\n",
epc, esp, ecause, eval);
alt_printf("restoring pc and sp\n\n");
// __attribute__((interrupt("machine"))) replaces `ret` with an `mret`.
// When mret is executed in user-mode, the kernel catches it setting the
// following:
// - tf.pc = tf.epc
// - tf.sp = tf.esp
// Restoring pc and sp to the previous values, unless epc and esp was
// overwritten.
}


void setup_trap(void (*trap_handler)(void), void * trap_stack_base, uint64_t trap_stack_size)
{
// Sets the trap handler
s3k_reg_write(S3K_REG_TPC, (uint64_t)trap_handler);
// Set the trap stack
s3k_reg_write(S3K_REG_TSP, ((uint64_t)trap_stack_base) + trap_stack_size);
}


#define TAG_BLOCK_TO_ADDR(tag, block) ( \
(((uint64_t) tag) << S3K_MAX_BLOCK_SIZE) + \
(((uint64_t) block) << S3K_MIN_BLOCK_SIZE) \
)

void s3k_print_cap(s3k_cap_t *cap) {
if (!cap)
alt_printf("Capability is NULL\n");
switch ((*cap).type) {
case S3K_CAPTY_NONE:
alt_printf("No Capability\n");
break;
case S3K_CAPTY_TIME:
alt_printf("Time hart:%X bgn:%X mrk:%X end:%Z\n",
(*cap).time.hart, (*cap).time.bgn, (*cap).time.mrk, (*cap).time.end);
break;
case S3K_CAPTY_MEMORY:
alt_printf("Memory rwx:%X lock:%X bgn:%X mrk:%X end:%X\n",
(*cap).mem.rwx, (*cap).mem.lck,
TAG_BLOCK_TO_ADDR((*cap).mem.tag, (*cap).mem.bgn),
TAG_BLOCK_TO_ADDR((*cap).mem.tag, (*cap).mem.mrk),
TAG_BLOCK_TO_ADDR((*cap).mem.tag, (*cap).mem.end)
);
break;
case S3K_CAPTY_PMP:
alt_printf("PMP rwx:%X used:%X index:%X address:%Z\n",
(*cap).pmp.rwx, (*cap).pmp.used, (*cap).pmp.slot, (*cap).pmp.addr);
break;
case S3K_CAPTY_MONITOR:
alt_printf("Monitor bgn:%X mrk:%X end:%X\n",
(*cap).mon.bgn, (*cap).mon.mrk, (*cap).mon.end);
break;
case S3K_CAPTY_CHANNEL:
alt_printf("Channel bgn:%X mrk:%X end:%X\n",
(*cap).chan.bgn, (*cap).chan.mrk, (*cap).chan.end);
break;
case S3K_CAPTY_SOCKET:
alt_printf("Socket mode:%X perm:%X channel:%X tag:%X\n",
(*cap).sock.mode, (*cap).sock.perm, (*cap).sock.chan, (*cap).sock.tag);
break;
}
}

void debug_capability_from_idx(uint32_t cap_idx) {
s3k_cap_t cap;
while (s3k_cap_read(cap_idx, &cap));
s3k_print_cap(&cap);
}

uint32_t log_sys(char * msg, uint32_t res) {
alt_printf("%s %X\n", msg, res);
}

uint32_t find_free_cap() {
s3k_cap_t cap;
for (uint32_t i = FREE_CAP_BEGIN; i <= FREE_CAP_END; i++) {
if (s3k_cap_read(i, &cap))
return i;
}
return 0;
}


void setup_app_1()
{
uint64_t uart_addr = s3k_napot_encode(UART0_BASE_ADDR, 0x8);
uint64_t app1_addr = s3k_napot_encode(APP_1_BASE_ADDR, APP_1_SIZE);

// Derive a PMP capability for app1 main memory
uint32_t free_cap_mem_idx = find_free_cap();
log_sys("1",
s3k_cap_derive(RAM_MEM, free_cap_mem_idx, s3k_mk_memory(APP_1_BASE_ADDR, APP_1_BASE_ADDR + APP_1_SIZE, S3K_MEM_RWX)));
uint32_t free_cap_idx = find_free_cap();
log_sys("1",
s3k_cap_derive(free_cap_mem_idx, free_cap_idx, s3k_mk_pmp(app1_addr, S3K_MEM_RWX)));
log_sys("2",
s3k_mon_cap_move(MONITOR, APP0_PID, free_cap_idx, APP1_PID, APP_1_CAP_PMP_MEM));
log_sys("3",
s3k_mon_pmp_load(MONITOR, APP1_PID, APP_1_CAP_PMP_MEM, APP_1_PMP_SLOT_MEM));

// Derive a PMP capability for uart
log_sys("4",
s3k_cap_derive(UART_MEM, free_cap_idx, s3k_mk_pmp(uart_addr, S3K_MEM_RW)));
log_sys("5",
s3k_mon_cap_move(MONITOR, APP0_PID, free_cap_idx, APP1_PID, APP_1_CAP_PMP_UART));
log_sys("6",
s3k_mon_pmp_load(MONITOR, APP1_PID, APP_1_CAP_PMP_UART, APP_1_PMP_SLOT_UART));

// Write start PC of app1 to PC
log_sys("7",
s3k_mon_reg_write(MONITOR, APP1_PID, S3K_REG_PC, APP_1_BASE_ADDR));

s3k_sync_mem();
}

#define NO_APP_1 0
#define NO_APP_0 1
#define ROUND_ROBIN 2
#define PARALLEL 3

void setup_scheduling(uint32_t scheduling_type) {
// Disable the other two cores
s3k_cap_delete(HART2_TIME);
s3k_cap_delete(HART3_TIME);

if (scheduling_type == NO_APP_1) {
s3k_cap_delete(HART1_TIME);
}
else if (scheduling_type == NO_APP_0) {
// Notice that we must be able to finish our job to setup APP 1
s3k_cap_delete(HART1_TIME);
uint32_t cap_idx = find_free_cap();
s3k_cap_derive(HART0_TIME, cap_idx, s3k_mk_time(S3K_MIN_HART, 0, S3K_SLOT_CNT / 2));
s3k_mon_cap_move(MONITOR, APP0_PID, HART0_TIME, APP1_PID, APP_1_TIME);
}
else if (scheduling_type == ROUND_ROBIN) {
s3k_cap_delete(HART1_TIME);
uint32_t cap_idx = find_free_cap();
log_sys("Time derivation",
s3k_cap_derive(HART0_TIME, cap_idx,
s3k_mk_time(S3K_MIN_HART, 0, S3K_SLOT_CNT / 2)));
log_sys("Time delegation",
s3k_mon_cap_move(MONITOR, APP0_PID, cap_idx, APP1_PID, APP_1_TIME));
}
else if (scheduling_type == PARALLEL) {
s3k_mon_cap_move(MONITOR, APP0_PID, HART1_TIME, APP1_PID, APP_1_TIME);
}
s3k_sync();
}


uint32_t setup_socket(bool server, bool yield, bool capability)
{
uint64_t socket_server = find_free_cap();
uint64_t yield_mode = (yield)?S3K_IPC_YIELD:S3K_IPC_NOYIELD;

s3k_ipc_perm_t permission = S3K_IPC_SDATA | S3K_IPC_CDATA;
if (capability)
permission |= S3K_IPC_CCAP | S3K_IPC_SCAP;

s3k_cap_derive(CHANNEL, socket_server,
s3k_mk_socket(0, yield_mode, permission, 0));
uint64_t socket_client = find_free_cap();
s3k_cap_derive(socket_server, socket_client,
s3k_mk_socket(0, yield_mode, permission, 1));
if (server) {
s3k_mon_cap_move(MONITOR, APP0_PID, socket_client, APP1_PID, APP_1_CAP_SOCKET);
return socket_server;
}
s3k_mon_cap_move(MONITOR, APP0_PID, socket_server, APP1_PID, APP_1_CAP_SOCKET);
return socket_client;
}


void server_main_loop(s3k_msg_t (* handler) (s3k_reply_t, uint32_t), bool capability) {
s3k_msg_t msg;
s3k_reply_t reply;
while (true)
{
do {
if (capability) {
msg.cap_idx = find_free_cap();
msg.send_cap = 1;
}
alt_puts("APP1: ready to receive\n");
reply = s3k_sock_sendrecv(APP_1_CAP_SOCKET, &msg);
if (reply.err == S3K_ERR_TIMEOUT)
alt_puts("APP1: timeout\n");
} while (reply.err);
msg = (*handler)(reply, msg.cap_idx);
}
}

void wait_for_app1_blocked() {
s3k_state_t state;
while (true) {
alt_puts("APP0: waiting loop\n");
if (s3k_mon_state_get(MONITOR, APP1_PID, &state))
continue;
if (state & S3K_PSF_BLOCKED)
break;
alt_puts("APP0: waiting 1\n");
s3k_mon_yield(MONITOR, APP1_PID);
alt_puts("APP0: waiting 2\n");
}
}

s3k_reply_t send_receive_forever(uint64_t socket, s3k_msg_t msg) {
s3k_reply_t reply;
do {
reply = s3k_sock_sendrecv(socket, &msg);
while (reply.err == S3K_ERR_TIMEOUT) {
alt_puts("APP0: timeout");
reply = s3k_sock_recv(socket, msg.cap_idx);
}
} while (reply.err);
return reply;
}
32 changes: 3 additions & 29 deletions projects/tutorial.01.hello-world/app0/main.c
Original file line number Diff line number Diff line change
@@ -1,39 +1,13 @@
#include "altc/altio.h"
#include "s3k/s3k.h"

#define APP0_PID 0
#define APP1_PID 1

// See plat_conf.h
#define BOOT_PMP 0
#define RAM_MEM 1
#define UART_MEM 2
#define TIME_MEM 3
#define HART0_TIME 4
#define HART1_TIME 5
#define HART2_TIME 6
#define HART3_TIME 7
#define MONITOR 8
#define CHANNEL 9

void setup_uart(uint64_t uart_idx)
{
uint64_t uart_addr = s3k_napot_encode(UART0_BASE_ADDR, 0x8);
// Derive a PMP capability for accessing UART
s3k_cap_derive(UART_MEM, uart_idx, s3k_mk_pmp(uart_addr, S3K_MEM_RW));
// Load the derive PMP capability to PMP configuration
s3k_pmp_load(uart_idx, 1);
// Synchronize PMP unit (hardware) with PMP configuration
// false => not full synchronization.
s3k_sync_mem();
}
#include "../../tutorial-commons/utils.h"

int main(void)
{
// Setup UART access
setup_uart(10);
setup_uart();

// Write hello world.
alt_puts("Hello, world");

alt_printf("Hello, world\n");
}
Loading

0 comments on commit d5696ba

Please sign in to comment.