diff --git a/.clang-format b/.clang-format index ee01e8cf..4a2ca881 100644 --- a/.clang-format +++ b/.clang-format @@ -1,66 +1,32 @@ Language: Cpp - UseTab: Always TabWidth: 8 IndentWidth: 8 ContinuationIndentWidth: 4 -ColumnLimit: 80 - +ColumnLimit: 120 AlignAfterOpenBracket: Align +AlignArrayOfStructures: Left AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Left AlignOperands: true AlignTrailingComments: true -AlignArrayOfStructures: Left -Cpp11BracedListStyle: true AllowAllParametersOfDeclarationOnNextLine: false - - AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false - BinPackArguments: true BinPackParameters: true - -BreakBeforeBraces: Linux BreakBeforeBinaryOperators: true +BreakBeforeBraces: Linux BreakBeforeTernaryOperators: false - BreakStringLiterals: false - +IncludeBlocks: Regroup IndentCaseLabels: false - -PointerAlignment: Right - -SpaceAfterCStyleCast: false - -SpaceBeforeAssignmentOperators: true - -SpaceBeforeParens: ControlStatements - -SpaceInEmptyParentheses: false - -SpacesBeforeTrailingComments: 1 - -SpacesInCStyleCastParentheses: false - -SpacesInContainerLiterals: false - -SpacesInParentheses: false - -SpacesInSquareBrackets: false - -Cpp11BracedListStyle: false - -MaxEmptyLinesToKeep: 1 KeepEmptyLinesAtTheStartOfBlocks: false - -SeparateDefinitionBlocks: Always - +MaxEmptyLinesToKeep: 1 PenaltyBreakAssignment: 10 PenaltyBreakBeforeFirstCallParameter: 30 PenaltyBreakComment: 10 @@ -68,6 +34,15 @@ PenaltyBreakFirstLessLess: 0 PenaltyBreakString: 10 PenaltyExcessCharacter: 100 PenaltyReturnTypeOnItsOwnLine: 60 - +PointerAlignment: Right +SeparateDefinitionBlocks: Always SortIncludes: true -IncludeBlocks: Regroup +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml deleted file mode 100644 index ca1d65d4..00000000 --- a/.github/workflows/unittest.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Unit Test -on: [push] -jobs: - testing: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Install gtest - run: > - sudo apt-get install libgtest-dev && - cd /usr/src/gtest && - sudo cmake CMakeLists.txt && - sudo make && - sudo cp lib/*.a /usr/lib && - sudo ln -s /usr/lib/libgtest.a /usr/local/lib/libgtest.a && - sudo ln -s /usr/lib/libgtest_main.a /usr/local/lib/libgtest_main.a - - name: Make and run tests - run: make test diff --git a/.gitignore b/.gitignore index 21d2f21d..7bf5873e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ docs -test_detail.json *.elf *.a *.da @@ -8,4 +7,5 @@ test_detail.json *.su *.out *.elf +*.bin *.res diff --git a/API.md b/API.md new file mode 100644 index 00000000..25de0b33 --- /dev/null +++ b/API.md @@ -0,0 +1,384 @@ +# S3K API +--- +## System calls +- Get local information + - [s3k_get_pid](#s3k_get_pid) - Get the process ID. + - [s3k_get_hartid](#s3k_get_hartid) - Get the hardware thread ID. + - [s3k_get_time](#s3k_get_time) - Get the current real-time. + - [s3k_get_timeout](#s3k_get_timeout) - Get the timeout of the current minor frame. +- Write and read registers + - [s3k_reg_read](#s3k_reg_read) - Read from register. + - [s3k_reg_write](#s3k_reg_write) - Write to register. +- Process synchronization + - [s3k_sync](#s3k_sync) - Synchronize process's memory and time allocation with capabilities. + - [s3k_sync_mem](#s3k_sync_mem) - Synchronize process's memory with capabilities. +- Common capability operations + - [s3k_cap_read](#s3k_cap_read) - Read capability's descriptor. + - [s3k_cap_move](#s3k_cap_move) - Move a capability. + - [s3k_cap_delete](#s3k_cap_delete) - Delete a capability. + - [s3k_cap_revoke](#s3k_cap_revoke) - Revoke child capabilities. + - [s3k_cap_derive](#s3k_cap_derive) - Derive a new capability. +- PMP capability invocations + - [s3k_pmp_load](#s3k_pmp_load) - Load PMP capability into PMP slot. + - [s3k_pmp_unload](#s3k_pmp_load) - Unload a PMP capability. +- Monitor capability invocations + - [s3k_monitor_suspend](#s3k_monitor_suspend) - Suspend a process. + - [s3k_monitor_resume](#s3k_monitor_resume) - Resume a process. + - [s3k_monitor_reg_read](#s3k_monitor_reg_read) - Read from process's register. + - [s3k_monitor_reg_write](#s3k_monitor_reg_write) - Write to process's register. + - [s3k_monitor_pmp_load](#s3k_monitor_pmp_load) - Load a PMP capability into PMP slot. + - [s3k_monitor_pmp_unload](#s3k_monitor_pmp_unload) - Unload a PMP capability. + - [s3k_monitor_cap_move](#s3k_monitor_cap_move) - Move a capability. +- IPC capability invocations + - [s3k_sock_send](#s3k_sock_send) - Send a packet to the server. + - [s3k_sock_call](#s3k_sock_call) - Make an RPC. + - [s3k_sock_reply](#s3k_sock_reply) - Send a packet to the client. + - [s3k_sock_recv](#s3k_sock_recv) - Receive a packet from client. + - [s3k_sock_replyrecv](#s3k_sock_replyrecv) - Send then receive a packet from client. + +### s3k_get_pid +```c +int s3k_get_pid(void); +``` +Returns the process ID of the caller. + +**Return:** the process ID of the caller + +### s3k_get_time +```c +uint64_t s3k_get_time(void); +``` +Returns the current real-time. + +**Return:** the current real-time. + + +### s3k_get_timeout +```c +uint64_t s3k_get_timeout(void); +``` +Returns the timeout of the current minor frame. + +**Return:** the timeout of the current minor frame. + + +### s3k_reg_read +```c +uint64_t s3k_reg_read(uint64_t reg); +``` +Returns the value of register. + +**Parameters:** +- `reg` - index of register. + +**Return:** the value of register with `reg`. If `reg` is invalid, returns 0. + + +### s3k_reg_write +```c +void s3k_reg_write(uint64_t reg, uint64_t val); +``` +Sets the value of a register. Has no effect if `reg` is invalid. + +**Parameters:** +- `reg` - ID of register. +- `val` - value to write to the register. + +**Note:** Setting the standard RISC-V registers may have unintended consequences. + + +### s3k_sync +```c +void s3k_sync(void); +``` +Synchronize the process's memory and time permissions with the underlying capabilities. + + +### s3k_sync_mem +```c +void s3k_sync_mem(void); +``` +Synchronize the process's memory permissions with the underlying capabilities. + + +### s3k_cap_read +```c +error_t s3k_cap_read(uint64_t i, cap_t *cap); +``` +Read the descriptor of the i'th capability. + +**Parameters:** +- `i` - index of capability. +- `cap` - buffer to write the descriptor. + +**Returns:** +- `S3K_SUCCESS` - if descriptor was read. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). + +### s3k_cap_move +```c +error_t s3k_cap_move(uint64_t i, uint64_t j); +``` +Move the i'th capability to the j'th slot. + +**Parameters:** +- `i` - source index of capability to move. +- `j` - destination index of capability to move. + +**Returns:** +- `S3K_SUCCESS` - if capability was moved. +- `S3K_PREEMPTED` - if system call was aborted due to timer preemption. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). +- `S3K_ERR_INVALID_INDEX` - if `j` is invalid (`j >= N_CAP`). +- `S3K_ERR_SRC_EMPTY` - if the i'th slot was empty. +- `S3K_ERR_DST_OCCUPIED` - if the j'th slot was not empty + + +### s3k_cap_delete +```c +error_t s3k_cap_delete(uint64_t i); +``` +Delete the i'th capability. + +**Parameters:** +- `i` - index of capability to delete. + +**Returns:** +- `S3K_SUCCESS` - if capability was deleted. +- `S3K_PREEMPTED` - if system call was aborted due to timer preemption. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). +- `S3K_ERR_EMPTY` - if the i'th slot was empty. + +### s3k_cap_revoke +```c +error_t s3k_cap_revoke(uint64_t i); +``` +Recursively deletes the children of the i'th capability, then restore the i'th capability to its original state. + +**Parameters:** +- `i` - index of capability to revoke with. + +**Returns:** +- `S3K_SUCCESS` - if capabilities were revoked and restored. +- `S3K_PREEMPTED` - if system call was aborted due to timer preemption. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). +- `S3K_ERR_EMPTY` - if the i'th slot was empty. + + +### s3k_cap_derive +```c +error_t s3k_cap_derive(uint64_t i, uint64_t j, cap_t cap); +``` +Create a new capability at the j'th slot using the i'th capability. +If successful, the i'th capability is updated to reflect that resources were reallocated (only slice capabilities). + +**Parameters:** +- `i` - index of capability to derive from. +- `j` - destination of the new capability. +- `cap` - description of capability to create + +**Returns:** +- `S3K_SUCCESS` - if the new capability wass successfully created. +- `S3K_PREEMPTED` - if system call was aborted due to timer preemption. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). +- `S3K_ERR_INVALID_INDEX` - if `j` is invalid (`j >= N_CAP`). +- `S3K_ERR_EMPTY` - if the i'th slot was empty. +- `S3K_ERR_DST_OCCUPIED` - if the j'th slot was occupied. +- `S3K_ERR_INVALID_CAPABILITY` - if i'th capability can not be used to derive `cap` + +**Note:** If preempted, the syscall may be partially; that is, only some child capabilities have been revoked. + +### s3k_pmp_load +```c +error_t s3k_pmp_load(uint64_t i, uint64_t j) +``` +Use the i'th capability to set the j'th PMP slot. + +The i'th capability is valid if it is a PMP capability not in use. + +**Parameters:** +- `i` - source index of PMP capability to load +- `j` - index of PMP slot + +**Returns:** +- `S3K_SUCCESS` - if PMP capability was loaded. +- `S3K_PREEMPTED` - if system call was aborted due to timer preemption. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). +- `S3K_ERR_INVALID_SLOT` - if `j` is invalid (`j >= N_PMP`). +- `S3K_ERR_EMPTY` - if the i'th capability slot was empty. +- `S3K_ERR_DST_OCCUPIED` - if the j'th PMP slot was occupied. +- `S3K_ERR_INVALID_PMP` - if i'th capability is invalid. + +### s3k_pmp_unload +```c +error_t s3k_pmp_unload(uint64_t i) +``` +Unload the i'th capability. + +The i'th capability is valid if it is a PMP capability in use. + +**Parameters:** +- `i` - source index of PMP capability to load +- `j` - index of PMP slot + +**Returns:** +- `S3K_SUCCESS` - if PMP capability was unloaded. +- `S3K_PREEMPTED` - if system call was aborted due to timer preemption. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). +- `S3K_ERR_EMPTY` - if the i'th capability slot was empty. +- `S3K_ERR_INVALID_PMP` - if i'th capability is invalid. + +### s3k_monitor_suspend +```c +error_t s3k_monitor_suspend(uint64_t i, uint64_t pid) +``` +Use the i'th capability to suspend the process `pid`. + +The i'th capability is valid if it is a monitor capability such that `pid` is in the free segment. + +**Parameters:** +- `i` - index of monitor capability +- `pid` - PID of the process to suspend + +**Returns:** +- `S3K_SUCCESS` - if process was resumed. +- `S3K_PREEMPTED` - if system call was aborted due to timer preemption. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). +- `S3K_INVALID_PID` - if `pid` is invalid (`pid >= N_PROC`). +- `S3K_ERR_EMPTY` - if the i'th capability slot was empty. +- `S3K_ERR_INVALID_MONITOR` - if i'th capability is invalid. + +### s3k_monitor_resume +```c +error_t s3k_monitor_resume(uint64_t i, uint64_t pid) +``` +Use the i'th capability to resume the process `pid`. + +The i'th capability is valid if it is a monitor capability such that `pid` is in the free segment. + +**Parameters:** +- `i` - index of monitor capability +- `pid` - PID of the process to resume + +**Returns:** +- `S3K_SUCCESS` - if capability was deleted. +- `S3K_PREEMPTED` - if system call was aborted due to timer preemption. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). +- `S3K_INVALID_PID` - if `pid` is invalid (`pid >= N_PROC`). +- `S3K_ERR_EMPTY` - if the i'th capability slot was empty. +- `S3K_ERR_INVALID_MONITOR` - if i'th capability is invalid. + +### s3k_monitor_reg_read +```c +error_t s3k_monitor_reg_read(uint64_t i, uint64_t pid, uint64_t reg, uint64_t *val) +``` +Use the i'th capability to read from register of process `pid`. + +The i'th capability is valid if it is a monitor capability such that `pid` is in the free segment. + +**Parameters:** +- `i` - index of monitor capability +- `pid` - PID of the process to resume +- `reg` - index of register +- `val` - buffer to read register value into, 0 if `reg` is invalid + +**Returns:** +- `S3K_SUCCESS` - if capability was deleted. +- `S3K_PREEMPTED` - if system call was aborted due to timer preemption. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). +- `S3K_INVALID_PID` - if `pid` is invalid (`pid >= N_PROC`). +- `S3K_ERR_EMPTY` - if the i'th capability slot was empty. +- `S3K_ERR_INVALID_MONITOR` - if i'th capability is invalid. + +### s3k_monitor_reg_write +```c +error_t s3k_monitor_reg_write(uint64_t i, uint64_t pid, uint64_t reg, uint64_t val) +``` +Use the i'th capability to write to register of process `pid`. Has no effect if `reg` is invalid. + +The i'th capability is valid if it is a monitor capability such that `pid` is in the free segment. + +**Parameters:** +- `i` - index of monitor capability +- `pid` - PID of the process to resume +- `reg` - index of register +- `val` - value to write + +**Returns:** +- `S3K_SUCCESS` - if capability was deleted. +- `S3K_PREEMPTED` - if system call was aborted due to timer preemption. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). +- `S3K_INVALID_PID` - if `pid` is invalid (`pid >= N_PROC`). +- `S3K_ERR_EMPTY` - if the i'th capability slot was empty. +- `S3K_ERR_INVALID_MONITOR` - if i'th capability is invalid. + +### s3k_monitor_pmp_load +```c +error_t s3k_monitor_pmp_load(uint64_t i, uint64_t pid, uint64_t j, uint64_t k); +``` + +**Parameters:** +- `i` - index of monitor capability +- `pid` - PID of the process to resume +- `j` - index of pmp capability +- `k` - pmp slot + +**Returns:** +- `S3K_SUCCESS` - if PMP capability was loaded. +- `S3K_PREEMPTED` - if system call was aborted due to timer preemption. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). +- `S3K_ERR_INVALID_INDEX` - if `j` is invalid (`j >= N_CAP`). +- `S3K_ERR_INVALID_SLOT` - if `k` is invalid (`k >= N_PMP`). +- `S3K_ERR_EMPTY` - if the i'th capability slot was empty. +- `S3K_ERR_DST_OCCUPIED` - if the k'th PMP slot of process `pid` was occupied. +- `S3K_ERR_INVALID_MONITOR` - if i'th capability is invalid. +- `S3K_ERR_INVALID_PMP` - if j'th capability is invalid. + +### s3k_monitor_pmp_unload +```c +error_t s3k_monitor_pmp_unload(); +``` + +**Parameters:** +- `i` - index of monitor capability +- `pid` - PID of the process to resume +- `j` - index of pmp capability + +**Returns:** +- `S3K_SUCCESS` - if PMP capability was loaded. +- `S3K_PREEMPTED` - if system call was aborted due to timer preemption. +- `S3K_ERR_INVALID_INDEX` - if `i` is invalid (`i >= N_CAP`). +- `S3K_ERR_INVALID_INDEX` - if `j` is invalid (`j >= N_CAP`). +- `S3K_ERR_EMPTY` - if the i'th capability slot was empty. +- `S3K_ERR_INVALID_MONITOR` - if i'th capability is invalid. +- `S3K_ERR_INVALID_PMP` - if j'th capability is invalid. + +### s3k_monitor_cap_move +```c +error_t s3k_monitor_cap_move(); +``` + +### s3k_sock_send +```c +error_t s3k_client_send(); +``` + +### s3k_sock_call +```c +error_t s3k_client_call(); +``` + +### s3k_sock_recv +```c +error_t s3k_sock_recv(); +``` + +### s3k_sock_reply +```c +error_t s3k_server_reply(); +``` + +### s3k_sock_replyrecv +```c +error_t s3k_server_replyrecv(); +``` diff --git a/LICENSE b/LICENSE index f39306a0..1e7dc242 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -MIT License +MIT LICENSE (modified) -Copyright (c) 2023 Henrik Akira Karlsson +Copyright (c) 2023 Henrik Akira Karlsson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,5 +18,6 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - +SOFTWARE. NOTWITHSTANDING ANY JURISDICTION WHERE NON-LIABILITY PROVISIONS MAY +NOT APPLY, ANY USE OF THE SOFTWARE THAT COULD RESULT IN LIABILITY FOR THE +AUTHORS OR COPYRIGHT HOLDERS IS STRICTLY PROHIBITED. diff --git a/Makefile b/Makefile deleted file mode 100644 index 6ed929a9..00000000 --- a/Makefile +++ /dev/null @@ -1,62 +0,0 @@ -# See LICENSE file for copyright and license details. -.POSIX: - -include config.mk - -vpath %.c src -vpath %.S src - -AS_SRCS=head.S trap.S -C_SRCS=cap.c cnode.c current.c csr.c exception.c init.c proc.c schedule.c \ - syscall.c syscall_lock.c syscall_monitor.c syscall_ipc.c ticket_lock.c \ - timer.c wfi.c altio.c kassert.c -OBJS=$(patsubst %.S, $(OBJ_DIR)/%.o, ${AS_SRCS}) \ - $(patsubst %.c, $(OBJ_DIR)/%.o, ${C_SRCS}) -DEPS=${OBJS:.o=.d} - -all: options kernel dasm - -options: - @printf "build options:\n" - @printf "CC = ${CC}\n" - @printf "LDFLAGS = ${LDFLAGS}\n" - @printf "ASFLAGS = ${ASFLAGS}\n" - @printf "CFLAGS = ${CFLAGS}\n" - @printf "INC = ${INC}\n" - @printf "CONFIG_H = ${abspath ${CONFIG_H}}\n" - @printf "BUILD_DIR = ${abspath ${BUILD_DIR}}\n" - -kernel: $(BUILD_DIR)/s3k.elf - -dasm: $(BUILD_DIR)/s3k.da - -test: - $(MAKE) -C test - -format: - clang-format -i $(shell find -wholename "*.[chC]" -not -path '*/.*') - -clean: - rm -rf $(BUILD_DIR) - -$(OBJ_DIR)/%.o: %.S - @mkdir -p ${@D} - @printf "CC ${@F}\n" - @${CC} ${ASFLAGS} ${INC} -MMD -c -o $@ $< - -$(OBJ_DIR)/%.o: %.c - @mkdir -p ${@D} - @printf "CC ${@F}\n" - @${CC} ${CFLAGS} ${INC} -MMD -c -o $@ $< - -$(BUILD_DIR)/s3k.elf: ${OBJS} - @printf "CC ${@F}\n" - @${CC} ${LDFLAGS} -o $@ $^ - -$(BUILD_DIR)/s3k.da: $(BUILD_DIR)/s3k.elf - @printf "OBJDUMP ${ $@ - -.PHONY: all options clean dasm docs test kernel - --include ${DEPS} diff --git a/api/Makefile b/api/Makefile deleted file mode 100644 index 1b1a230b..00000000 --- a/api/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -RISCV_PREFIX?=riscv64-unknown-elf- -CC=$(RISCV_PREFIX)gcc -AR=$(RISCV_PREFIX)ar - -CFLAGS=-march=rv64imac -mabi=lp64 -mcmodel=medany -CFLAGS+=-ffat-lto-objects -flto -CFLAGS+=-Os -g - -all: libs3k.a - -libs3k.a: s3k-syscall.o s3k-utils.o - $(AR) rcs -o $@ $^ - -clean: - rm -f s3k-syscall.o s3k-utils.o - -.PHONY: clean diff --git a/api/s3k-syscall.c b/api/s3k-syscall.c deleted file mode 100644 index aedc06bb..00000000 --- a/api/s3k-syscall.c +++ /dev/null @@ -1,258 +0,0 @@ -#include "s3k.h" - -// Syscall -uint64_t s3k_getpid(void) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_PROC; - register uint64_t a1 __asm__("a1") = 0; - __asm__ volatile("ecall" : "+r"(a0) : "r"(a1)); - return a0; -} - -uint64_t s3k_getreg(enum s3k_reg reg) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_PROC; - register uint64_t a1 __asm__("a1") = 1; - register uint64_t a2 __asm__("a2") = reg; - __asm__ volatile("ecall" : "+r"(a0) : "r"(a1), "r"(a2)); - return a0; -} - -uint64_t s3k_setreg(enum s3k_reg reg, uint64_t val) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_PROC; - register uint64_t a1 __asm__("a1") = 2; - register uint64_t a2 __asm__("a2") = reg; - register uint64_t a3 __asm__("a3") = val; - __asm__ volatile("ecall" ::"r"(a0), "r"(a1), "r"(a2), "r"(a3)); - return a0; -} - -uint64_t s3k_gethartid(void) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_PROC; - register uint64_t a1 __asm__("a1") = 3; - __asm__ volatile("ecall" : "+r"(a0) : "r"(a1)); - return a0; -} - -uint64_t s3k_gettime(void) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_PROC; - register uint64_t a1 __asm__("a1") = 4; - __asm__ volatile("ecall" : "+r"(a0) : "r"(a1)); - return a0; -} - -uint64_t s3k_gettimeout(void) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_PROC; - register uint64_t a1 __asm__("a1") = 5; - __asm__ volatile("ecall" : "+r"(a0) : "r"(a1)); - return a0; -} - -void s3k_yield(void) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_PROC; - register uint64_t a1 __asm__("a1") = 6; - __asm__("ecall" : : "r"(a0), "r"(a1)); -} - -void s3k_suspend(void) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_PROC; - register uint64_t a1 __asm__("a1") = 7; - __asm__("ecall" : : "r"(a0), "r"(a1)); -} - -union s3k_cap s3k_getcap(uint64_t i) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_GETCAP; - register uint64_t a1 __asm__("a1") = i; - __asm__ volatile("ecall" : "+r"(a0) : "r"(a1)); - return (union s3k_cap){ .raw = a0 }; -} - -enum s3k_excpt s3k_movcap(uint64_t i, uint64_t j) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_MOVCAP; - register uint64_t a1 __asm__("a1") = i; - register uint64_t a2 __asm__("a2") = j; - __asm__ volatile("ecall" : "+r"(a0) : "r"(a1), "r"(a2)); - return a0; -} - -enum s3k_excpt s3k_delcap(uint64_t i) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_DELCAP; - register uint64_t a1 __asm__("a1") = i; - __asm__ volatile("ecall" : "+r"(a0) : "r"(a1)); - return a0; -} - -enum s3k_excpt s3k_revcap(uint64_t i) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_REVCAP; - register uint64_t a1 __asm__("a1") = i; - __asm__ volatile("ecall" : "+r"(a0) : "r"(a1)); - return a0; -} - -enum s3k_excpt s3k_drvcap(uint64_t i, uint64_t j, union s3k_cap cap) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_DRVCAP; - register uint64_t a1 __asm__("a1") = i; - register uint64_t a2 __asm__("a2") = j; - register uint64_t a3 __asm__("a3") = cap.raw; - __asm__ volatile("ecall" : "+r"(a0) : "r"(a1), "r"(a2), "r"(a3)); - return a0; -} - -enum s3k_excpt s3k_msuspend(uint64_t i, uint64_t pid) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_MSUSPEND; - register uint64_t a1 __asm__("a1") = i; - register uint64_t a2 __asm__("a2") = pid; - __asm__ volatile("ecall" : "+r"(a0) : "r"(a1), "r"(a2)); - return a0; -} - -enum s3k_excpt s3k_mresume(uint64_t i, uint64_t pid) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_MRESUME; - register uint64_t a1 __asm__("a1") = i; - register uint64_t a2 __asm__("a2") = pid; - __asm__ volatile("ecall" : "+r"(a0) : "r"(a1), "r"(a2)); - return a0; -} - -enum s3k_excpt s3k_mgetreg(uint64_t i, uint64_t pid, enum s3k_reg reg, - uint64_t *val) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_MGETREG; - register uint64_t a1 __asm__("a1") = i; - register uint64_t a2 __asm__("a2") = pid; - register uint64_t a3 __asm__("a3") = reg; - __asm__ volatile("ecall" : "+r"(a0), "+r"(a1) : "r"(a2), "r"(a3)); - if (a0 == S3K_EXCPT_NONE) - *val = a1; - return a0; -} - -enum s3k_excpt s3k_msetreg(uint64_t i, uint64_t pid, enum s3k_reg reg, - uint64_t val) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_MSETREG; - register uint64_t a1 __asm__("a1") = i; - register uint64_t a2 __asm__("a2") = pid; - register uint64_t a3 __asm__("a3") = reg; - register uint64_t a4 __asm__("a4") = val; - __asm__ volatile("ecall" - : "+r"(a0) - : "r"(a1), "r"(a2), "r"(a3), "r"(a4)); - return a0; -} - -enum s3k_excpt s3k_mgetcap(uint64_t i, uint64_t pid, uint64_t j, - union s3k_cap *cap) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_MGETCAP; - register uint64_t a1 __asm__("a1") = i; - register uint64_t a2 __asm__("a2") = pid; - register uint64_t a3 __asm__("a3") = j; - __asm__ volatile("ecall" : "+r"(a0), "+r"(a1) : "r"(a2), "r"(a3)); - if (a0 == S3K_EXCPT_NONE) - cap->raw = a1; - return a0; -} - -enum s3k_excpt s3k_mtakecap(uint64_t i, uint64_t pid, uint64_t src, - uint64_t dst) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_MTAKECAP; - register uint64_t a1 __asm__("a1") = i; - register uint64_t a2 __asm__("a2") = pid; - register uint64_t a3 __asm__("a3") = src; - register uint64_t a4 __asm__("a4") = dst; - __asm__ volatile("ecall" - : "+r"(a0) - : "r"(a1), "r"(a2), "r"(a3), "r"(a4)); - return a0; -} - -enum s3k_excpt s3k_mgivecap(uint64_t i, uint64_t pid, uint64_t src, - uint64_t dst) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_MGIVECAP; - register uint64_t a1 __asm__("a1") = i; - register uint64_t a2 __asm__("a2") = pid; - register uint64_t a3 __asm__("a3") = src; - register uint64_t a4 __asm__("a4") = dst; - __asm__ volatile("ecall" - : "+r"(a0) - : "r"(a1), "r"(a2), "r"(a3), "r"(a4)); - return a0; -} - -enum s3k_excpt s3k_recv(uint64_t i, uint64_t buf[4], uint64_t cap_dest, - uint64_t *tag) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_RECV; - register uint64_t a1 __asm__("a1") = i; - register uint64_t a2 __asm__("a2"); - register uint64_t a3 __asm__("a3"); - register uint64_t a4 __asm__("a4"); - register uint64_t a5 __asm__("a5") = cap_dest; - __asm__ volatile("ecall" - : "+r"(a0), "+r"(a1), "=r"(a2), "=r"(a3), "=r"(a4), - "+r"(a5)); - buf[0] = a1; - buf[1] = a2; - buf[2] = a3; - buf[3] = a4; - buf[4] = a5; - *tag = a5; - return a0; -} - -enum s3k_excpt s3k_send(uint64_t i, uint64_t buf[4], uint64_t cap_src, - bool yield) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_SEND; - register uint64_t a1 __asm__("a1") = i; - register uint64_t a2 __asm__("a2") = buf[0]; - register uint64_t a3 __asm__("a3") = buf[1]; - register uint64_t a4 __asm__("a4") = buf[2]; - register uint64_t a5 __asm__("a5") = buf[3]; - register uint64_t a6 __asm__("a6") = cap_src; - register uint64_t a7 __asm__("a7") = yield; - __asm__ volatile("ecall" - : "+r"(a0) - : "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(a6), - "r"(a7)); - return a0; -} - -enum s3k_excpt s3k_sendrecv(uint64_t i, uint64_t j, uint64_t buf[4], - uint64_t cap_src, uint64_t cap_dest, uint64_t *tag) -{ - register uint64_t a0 __asm__("a0") = S3K_SYSCALL_SENDRECV; - register uint64_t a1 __asm__("a1") = i << 16 | j; - register uint64_t a2 __asm__("a2") = buf[0]; - register uint64_t a3 __asm__("a3") = buf[1]; - register uint64_t a4 __asm__("a4") = buf[2]; - register uint64_t a5 __asm__("a5") = buf[3]; - register uint64_t a6 __asm__("a6") = cap_src; - register uint64_t a7 __asm__("a7") = cap_dest; - __asm__ volatile("ecall" - : "+r"(a0), "+r"(a1), "+r"(a2), "+r"(a3), "+r"(a4), - "+r"(a5) - : "r"(a6), "r"(a7)); - buf[0] = a1; - buf[1] = a2; - buf[2] = a3; - buf[3] = a4; - *tag = a5; - return a0; -} diff --git a/api/s3k-utils.c b/api/s3k-utils.c deleted file mode 100644 index 8d9d8b60..00000000 --- a/api/s3k-utils.c +++ /dev/null @@ -1,196 +0,0 @@ -#include "s3k.h" - -uint64_t s3k_pmp_napot_addr(uint64_t begin, uint64_t end) -{ - return (begin | (((end - begin) - 1) >> 1)) >> 2; -} - -uint64_t s3k_pmp_napot_begin(uint64_t addr) -{ - return ((addr + 1) & addr) << 2; -} - -uint64_t s3k_pmp_napot_end(uint64_t addr) -{ - return (((addr + 1) | addr) + 1) << 2; -} - -union s3k_cap s3k_time(uint64_t hartid, uint64_t begin, uint64_t end) -{ - return (union s3k_cap){ - .time = {S3K_CAPTY_TIME, 0, hartid, begin, begin, end} - }; -} - -union s3k_cap s3k_memory(uint64_t begin, uint64_t end, uint64_t offset, - uint64_t rwx) -{ - return (union s3k_cap){ - .memory - = {S3K_CAPTY_MEMORY, false, rwx, offset, begin, begin, end} - }; -} - -union s3k_cap s3k_pmp(uint64_t addr, uint64_t rwx) -{ - return (union s3k_cap){ - .pmp = {S3K_CAPTY_PMP, addr, 0x18 | rwx} - }; -} - -union s3k_cap s3k_monitor(uint64_t begin, uint64_t end) -{ - return (union s3k_cap){ - .monitor = {S3K_CAPTY_MONITOR, 0, begin, begin, end} - }; -} - -union s3k_cap s3k_channel(uint64_t begin, uint64_t end) -{ - return (union s3k_cap){ - .channel = {S3K_CAPTY_CHANNEL, 0, begin, begin, end} - }; -} - -union s3k_cap s3k_socket(uint64_t channel, uint64_t tag) -{ - return (union s3k_cap){ - .socket = {S3K_CAPTY_SOCKET, 0, channel, tag} - }; -} - -bool s3k_can_derive(union s3k_cap parent, union s3k_cap child) -{ - return s3k_time_derive(parent, child) - || s3k_memory_derive(parent, child) - || s3k_monitor_derive(parent, child) - || s3k_channel_derive(parent, child) - || s3k_socket_derive(parent, child); -} - -bool s3k_is_parent(union s3k_cap parent, union s3k_cap child) -{ - return s3k_time_parent(parent, child) - || s3k_memory_parent(parent, child) - || s3k_monitor_parent(parent, child) - || s3k_channel_parent(parent, child) - || s3k_socket_parent(parent, child); -} - -bool s3k_time_parent(union s3k_cap parent, union s3k_cap child) -{ - return parent.type == S3K_CAPTY_TIME && child.type == S3K_CAPTY_TIME - && parent.time.begin <= child.time.begin - && child.time.end <= parent.time.end - && child.time.hartid == parent.time.hartid; -} - -bool s3k_memory_parent(union s3k_cap parent, union s3k_cap child) -{ - if (parent.type == S3K_CAPTY_MEMORY && child.type == S3K_CAPTY_MEMORY) { - return parent.memory.offset == child.memory.offset - && parent.memory.begin <= child.memory.begin - && child.memory.end <= parent.memory.end - && ((parent.memory.rwx & child.memory.rwx) - == child.memory.rwx); - } - if (parent.type == S3K_CAPTY_MEMORY && child.type == S3K_CAPTY_PMP) { - uint64_t pmp_begin = s3k_pmp_napot_begin(child.pmp.addr); - uint64_t pmp_end = s3k_pmp_napot_end(child.pmp.addr); - uint64_t mem_begin = ((uint64_t)parent.memory.offset << 27) - + (parent.memory.begin << 12); - uint64_t mem_end = ((uint64_t)parent.memory.offset << 27) - + (parent.memory.end << 12); - return mem_begin <= pmp_begin && pmp_end <= mem_end; - } - return false; -} - -bool s3k_monitor_parent(union s3k_cap parent, union s3k_cap child) -{ - return parent.type == S3K_CAPTY_MONITOR - && child.type == S3K_CAPTY_MONITOR - && parent.monitor.begin <= child.monitor.begin - && child.monitor.end <= parent.monitor.end; -} - -bool s3k_channel_parent(union s3k_cap parent, union s3k_cap child) -{ - if (parent.type == S3K_CAPTY_CHANNEL && child.type == S3K_CAPTY_CHANNEL) - return parent.channel.begin <= child.channel.begin - && child.channel.end <= parent.channel.end; - if (parent.type == S3K_CAPTY_CHANNEL && child.type == S3K_CAPTY_SOCKET) - return parent.channel.begin <= child.socket.channel - && child.socket.channel < parent.channel.end; - return false; -} - -bool s3k_socket_parent(union s3k_cap parent, union s3k_cap child) -{ - return parent.type == S3K_CAPTY_SOCKET && child.type == S3K_CAPTY_SOCKET - && parent.socket.tag == 0 - && parent.socket.channel == child.socket.channel; -} - -bool s3k_time_derive(union s3k_cap parent, union s3k_cap child) -{ - return parent.type == S3K_CAPTY_TIME && child.type == S3K_CAPTY_TIME - && parent.time.free == child.time.begin - && parent.time.free == child.time.free - && child.time.end <= parent.time.end - && child.time.hartid == parent.time.hartid; -} - -bool s3k_memory_derive(union s3k_cap parent, union s3k_cap child) -{ - if (parent.type == S3K_CAPTY_MEMORY && child.type == S3K_CAPTY_MEMORY) { - return parent.memory.offset == child.memory.offset - && parent.memory.free == child.memory.begin - && parent.memory.free == child.memory.free - && child.memory.end <= parent.memory.end - && ((parent.memory.rwx & child.memory.rwx) - == child.memory.rwx); - } - if (parent.type == S3K_CAPTY_MEMORY && child.type == S3K_CAPTY_PMP) { - uint64_t pmp_begin = s3k_pmp_napot_begin(child.pmp.addr); - uint64_t pmp_end = s3k_pmp_napot_end(child.pmp.addr); - uint64_t pmp_rwx = child.pmp.cfg & 0x7; - uint64_t pmp_mode = child.pmp.cfg >> 3; - uint64_t mem_free = ((uint64_t)parent.memory.offset << 27) - + (parent.memory.free << 12); - uint64_t mem_end = ((uint64_t)parent.memory.offset << 27) - + (parent.memory.end << 12); - uint64_t mem_rwx = parent.memory.rwx; - return mem_free <= pmp_begin && pmp_end <= mem_end - && pmp_mode == 0x3 && (mem_rwx & pmp_rwx) == pmp_rwx; - } - return false; -} - -bool s3k_monitor_derive(union s3k_cap parent, union s3k_cap child) -{ - return parent.type == S3K_CAPTY_MONITOR - && child.type == S3K_CAPTY_MONITOR - && parent.monitor.free == child.monitor.begin - && parent.monitor.free == child.monitor.free - && child.monitor.end <= parent.monitor.end; -} - -bool s3k_channel_derive(union s3k_cap parent, union s3k_cap child) -{ - if (parent.type == S3K_CAPTY_CHANNEL && child.type == S3K_CAPTY_CHANNEL) - return parent.channel.free == child.channel.begin - && parent.channel.free == child.channel.free - && child.channel.end <= parent.channel.end; - if (parent.type == S3K_CAPTY_CHANNEL && child.type == S3K_CAPTY_SOCKET) - return parent.channel.free == child.socket.channel - && child.socket.channel < parent.channel.end; - return false; -} - -bool s3k_socket_derive(union s3k_cap parent, union s3k_cap child) -{ - return parent.type == S3K_CAPTY_SOCKET && child.type == S3K_CAPTY_SOCKET - && parent.socket.tag == 0 && child.socket.tag > 0 - && parent.socket.channel == child.socket.channel; -} diff --git a/api/s3k.h b/api/s3k.h deleted file mode 100644 index 166057db..00000000 --- a/api/s3k.h +++ /dev/null @@ -1,544 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -/** - * @file s3k.h - * @brief Kernel API for user applications. - * - * This file contains all necessary enums, structs, and functions used for - * interacting with the S3K Kernel. - * - * @author Henrik Karlsson (henrik10@kth.se) - */ -#ifndef __S3K_H__ -#define __S3K_H__ -#include -#include - -/// @defgroup api S3K Kernel API -/// -/// S3K Kernel User-level API -/// -/// @{ - -#define S3K_RWX 0x7 ///< Read, write and execute for Memory and PMP capability -#define S3K_RW 0x3 ///< Read and write permissions for memory and PMP capability -#define S3K_RX \ - 0x5 ///< Read and execute permissions for memory and PMP capability -#define S3K_R 0x1 ///< Read permissions for memory and PMP capability - -/** - * @brief Enumeration S3K system call exception codes. - * - * These exeception codes are returned by most of S3K's system calls. When no - * exception occured while executing a system call, `S3K_EXCPT_NONE` (=0) is - * returned. - */ -enum s3k_excpt { - S3K_EXCPT_NONE, ///< No exception. - S3K_EXCPT_EMPTY, ///< Capability slot is empty. - S3K_EXCPT_COLLISION, ///< Capability slot is occupied. - S3K_EXCPT_DERIVATION, ///< Capability can not be derived. - S3K_EXCPT_PREEMPTED, ///< System call was preempted. - S3K_EXCPT_SUSPENDED, ///< Process was suspended - S3K_EXCPT_MPID, ///< Bad PID for monitor operation. - S3K_EXCPT_MBUSY, ///< Process busy. - S3K_EXCPT_INVALID_CAP, ///< Capability used for the system call is - ///< invalid. - S3K_EXCPT_NO_RECEIVER, ///< No receiver for the send call. - S3K_EXCPT_SEND_CAP, ///< Something stops sending of capability. - S3K_EXCPT_UNIMPLEMENTED ///< System call not implemented for specified - ///< capability. -}; - -/// @brief Enumeration of capability types. -enum s3k_capty { - /// **No capability** - S3K_CAPTY_NONE, - /// **Time slice capability**, letting the process modify and manage the - /// minor frames of the - /// round-robin scheduler. - S3K_CAPTY_TIME, - /// **Memory slice capability** for manage slices of memory. Can be used - /// to derive memory - /// slice capabilities and PMP capabilities. - S3K_CAPTY_MEMORY, - /// **PMP capability**, letting the process modify RISC-V's Physical - /// Memory Protection (PMP) - /// unit. - S3K_CAPTY_PMP, - /// **Process monitor capability**, letting the process suspend, resume - /// and modify suspended - /// processes. - S3K_CAPTY_MONITOR, - /// **IPC channel capability**, for creating and managing IPC channel - /// receive endpoints. - S3K_CAPTY_CHANNEL, - /// **IPC receive capability**, IPC endpoint for receiving messages and - /// capabilities. Can be - /// used to derive IPC send capabilities. - S3K_CAPTY_SOCKET, -}; - -/** - * @brief Enumeration of S3K registers. - * - * Includes RISC-V's general purpose registers (GPR), program counter, and S3K - * specific virtual registers (VR). - */ -enum s3k_reg { - /* General purpose registers */ - S3K_REG_PC, ///< Program counter - S3K_REG_RA, ///< Return address (GPR) - S3K_REG_SP, ///< Stack pointer (GPR) - S3K_REG_GP, ///< Global pointer (GPR) - S3K_REG_TP, ///< Thread pointer (GPR) - S3K_REG_T0, ///< Temporary register (GPR) - S3K_REG_T1, ///< Temporary register (GPR) - S3K_REG_T2, ///< Temporary register (GPR) - S3K_REG_S0, ///< Saved register/Stack frame pointer (GPR) - S3K_REG_S1, ///< Saved register (GPR) - S3K_REG_A0, ///< Argument/Return register (GPR) - S3K_REG_A1, ///< Argument/Return register (GPR) - S3K_REG_A2, ///< Argument register (GPR) - S3K_REG_A3, ///< Argument register (GPR) - S3K_REG_A4, ///< Argument register (GPR) - S3K_REG_A5, ///< Argument register (GPR) - S3K_REG_A6, ///< Argument register (GPR) - S3K_REG_A7, ///< Argument register (GPR) - S3K_REG_S2, ///< Saved register (GPR) - S3K_REG_S3, ///< Saved register (GPR) - S3K_REG_S4, ///< Saved register (GPR) - S3K_REG_S5, ///< Saved register (GPR) - S3K_REG_S6, ///< Saved register (GPR) - S3K_REG_S7, ///< Saved register (GPR) - S3K_REG_S8, ///< Saved register (GPR) - S3K_REG_S9, ///< Saved register (GPR) - S3K_REG_S10, ///< Saved register (GPR) - S3K_REG_S11, ///< Saved register (GPR) - S3K_REG_T3, ///< Temporary register (GPR) - S3K_REG_T4, ///< Temporary register (GPR) - S3K_REG_T5, ///< Temporary register (GPR) - S3K_REG_T6, ///< Temporary register (GPR) - /* Virtual registers */ - /* Trap handling setup */ - S3K_REG_TPC, ///< Trap program counter. - S3K_REG_TSP, ///< Trap stack pointer. - /* Exception handling registers */ - S3K_REG_EPC, ///< Exception program counter. - S3K_REG_ESP, ///< Exception stack pointer. - S3K_REG_ECAUSE, ///< Exception cause code. - S3K_REG_EVAL, ///< Exception value. - /* PMP registers */ - S3K_REG_PMP, ///< PMP configuration. - /* End of registers */ - S3K_REG_COUNT ///< *Number of S3K registers.* -}; - -/// @defgroup cap-def S3K Capability Definitions -/// @{ - -/// Capability description -union s3k_cap { - uint64_t type : 4; ///< Type of capability - uint64_t raw; ///< Capability as 64-bit word - - struct { - uint64_t type : 4; ///< Type of capability, should be - ///< S3K_CAPTY_TIME. - uint64_t unused : 4; ///< Padding, should be zero. - uint64_t hartid : 8; ///< Hardware Thread ID. - uint64_t begin : 16; ///< Beginning of time slice. - uint64_t free : 16; ///< Beginning of available/unallocated time - ///< slice. - uint64_t end : 16; ///< End of time slice. - } time; - - struct { - uint64_t type : 4; ///< Type of capability, should be - ///< S3K_CAPTY_MEMORY - uint64_t lock : 1; ///< Prevents creating of memory capabilities - uint64_t rwx : 3; ///< Read, write and execute (reverse order) - uint64_t offset : 8; ///< 128 MiB offset of memory slice - uint64_t begin : 16; ///< Beginning of memory slice - uint64_t free : 16; ///< Beginning of available/unallocated - ///< memory slice - uint64_t end : 16; ///< End of memory slice - } memory; - - struct { - uint64_t type : 4; ///< Type of capability, should be - ///< S3K_CAPTY_PMP - uint64_t addr : 52; ///< pmpaddr - uint64_t cfg : 8; ///< pmpcfg - } pmp; - - struct { - uint64_t type : 4; ///< Type of capability, should be - ///< S3K_CAPTY_MONITOR - uint64_t unused : 12; ///< Padding, should be zero - uint64_t begin : 16; ///< Beginning of monitored PIDs. - uint64_t free : 16; ///< Beginning of available monitored PIDs. - uint64_t end : 16; ///< End of monitred PIDs - } monitor; - - struct { - uint64_t type : 4; - uint64_t unused : 12; - uint64_t begin : 16; - uint64_t free : 16; - uint64_t end : 16; - } channel; - - struct { - uint64_t type : 4; - uint64_t unused : 28; - uint64_t channel : 16; - uint64_t tag : 16; - } socket; -}; - -#ifdef _Static_assert -_Static_assert(sizeof(union s3k_cap) == 8, "sizeof(union s3k_cap) != 8"); -#endif /* _Static_assert */ - -/// @} - -/** - * @defgroup api-syscall System Calls - * - * System calls for user processes. - * - * @{ - */ - -/// S3K Syscall Numbers -enum s3k_syscall_nr { - // Capabilityless syscalls - S3K_SYSCALL_PROC, ///< Process local system call. - // Capability syscalls - S3K_SYSCALL_GETCAP, ///< Get capability description - S3K_SYSCALL_MOVCAP, ///< Move capability - S3K_SYSCALL_DELCAP, ///< Delete capability - S3K_SYSCALL_REVCAP, ///< Revoke capability - S3K_SYSCALL_DRVCAP, ///< Derive capability - // Monitor syscalls - S3K_SYSCALL_MSUSPEND, ///< Monitor suspend process - S3K_SYSCALL_MRESUME, ///< Monitor resume process - S3K_SYSCALL_MGETREG, ///< Monitor get register value - S3K_SYSCALL_MSETREG, ///< Monitor set register value - S3K_SYSCALL_MGETCAP, ///< Monitor get capability description - S3K_SYSCALL_MTAKECAP, ///< Monitor take capability - S3K_SYSCALL_MGIVECAP, ///< Monitor give capability - // IPC syscalls - S3K_SYSCALL_RECV, ///< Receive message/capability - S3K_SYSCALL_SEND, ///< Send message/capability - S3K_SYSCALL_SENDRECV, ///< Send then receive message/capability -}; - -/** - * @brief Get the process ID. - * @return Process ID. - */ -uint64_t s3k_getpid(void); - -/** - * @brief Get hardware thread ID. - * @return Hart ID. - */ -uint64_t s3k_gethartid(void); - -/** - * @brief Get the current RTC time. - * @return Time. - */ -uint64_t s3k_gettime(void); - -/** - * @brief Get the time slice timeout. - * @return Timeout. - */ -uint64_t s3k_gettimeout(void); - -/** - * @brief Get the value of a register. - * - * @param reg Register ID. - * @return Read value. - * @warning Register IDs that are out-of-range are silently ignored. - */ -uint64_t s3k_getreg(enum s3k_reg reg); - -/** - * @brief Read and set value. - * - * @param reg Register ID. - * @param val Value to write. - * @return Read value. - * @warning Register IDs that are out-of-range are silently ignored. - */ -uint64_t s3k_setreg(enum s3k_reg reg, uint64_t val); - -/** - * @brief Yield remaining time slice. - */ -void s3k_yield(void); - -/** - * @brief Reads a capability description. - * - * @param i Index of capability table. - * @return The read capability. - */ -union s3k_cap s3k_getcap(uint64_t i); - -/** - * @brief Moves a capability. - * - * @param i Index of the capability to move. - * @param j Destination index. - * @return `S3K_EXCPT_INDEX` if index i or j is out-of-range. - * @return `S3K_EXCPT_EMPTY` if slot i is empty. - * @return `S3K_EXCPT_NONE` if move was successful. - */ -enum s3k_excpt s3k_movcap(uint64_t i, uint64_t j); - -/** - * @brief Deletes a capability. - * - * @param i Index of the capability to delete. - * @return `S3K_EXCPT_INDEX` if index i is out-of-range. - * @return `S3K_EXCPT_EMPTY` if slot i is empty. - * @return `S3K_EXCPT_NONE` if deletion was successful. - */ -enum s3k_excpt s3k_delcap(uint64_t i); - -/** - * @brief Revokes a capability. - * - * Deletes all of of the capabilities descendants. - * - * @param i Index of the capability to revoke. - * @return `S3K_EXCPT_INDEX` if index i is out-of-range. - * @return `S3K_EXCPT_EMPTY` if slot i is empty. - * @return `S3K_EXCPT_PREEMPTED` if the system call is preempted. - * @return `S3K_EXCPT_NONE` if revokation was successful. - * @warning If preempted, only a subset of the descendants will be deleted. - */ -enum s3k_excpt s3k_revcap(uint64_t i); - -/** - * @brief Derives a new capability. - * - * Creates a new capability based on another. Capability derivation will fail if - * the corresponding `s3k_cap_deriveable` call returns false. - * - * @param i Index of the capability to revoke. - * @param j Destination index. - * @param cap Description of the derived capability. - * @return `S3K_EXCPT_INDEX` if index i or j is out-of-range. - * @return `S3K_EXCPT_EMPTY` if slot i is empty. - * @return `S3K_EXCPT_OCCUPIED` if slot j is occupied. - * @return `S3K_EXCPT_DERIVATION` if slot cap can **not** be derived from the - * capability in index i. - * @return `S3K_EXCPT_NONE` if capability was successfully derived. - */ -enum s3k_excpt s3k_drvcap(uint64_t i, uint64_t j, union s3k_cap cap); - -enum s3k_excpt s3k_recv(uint64_t i, uint64_t buf[4], uint64_t cap_dest, - uint64_t *tag); -enum s3k_excpt s3k_send(uint64_t i, uint64_t buf[4], uint64_t cap_src, - bool yield); -enum s3k_excpt s3k_sendrecv(uint64_t i, uint64_t j, uint64_t buf[4], - uint64_t cap_src, uint64_t cap_dest, uint64_t *tag); -/** - * @brief Monitor suspends a process. - * - * @param i Index to monitor capability. - * @param pid Process to suspend. - * @return TODO - */ -enum s3k_excpt s3k_msuspend(uint64_t i, uint64_t pid); -/** - * @brief Monitor resumes a process. - * - * @param i Index to monitor capability. - * @param pid Process to resume. - * @return TODO - */ -enum s3k_excpt s3k_mresume(uint64_t i, uint64_t pid); -/** - * @brief Monitor reads a process's register. - * - * @param i Index to monitor capability. - * @param pid Process... - * @param reg Register to read - * @param val Read value - * @return TODO - */ -enum s3k_excpt s3k_mgetreg(uint64_t i, uint64_t pid, enum s3k_reg reg, - uint64_t *val); -/** - * @brief Monitor write a process's register. - * - * @param i Index to monitor capability. - * @param pid Process... - * @param reg Register to read - * @param val Value to write - * @return TODO - */ -enum s3k_excpt s3k_msetreg(uint64_t i, uint64_t pid, enum s3k_reg reg, - uint64_t val); -/** - * @brief Monitor read process's capability. - * - * @param i Index to monitor capability. - * @param pid Process to read capability of. - * @param j Index of capability to read - * @param cap Read capability - * @return TODO - */ -enum s3k_excpt s3k_mgetcap(uint64_t i, uint64_t pid, uint64_t j, - union s3k_cap *cap); -/** - * @brief Monitor give a capability to a process. - * - * @param i Index to monitor capability. - * @param pid Process to give capability. - * @param src Index of capability to give - * @param dst Destination of capability - * @return TODO - */ -enum s3k_excpt s3k_mgivecap(uint64_t i, uint64_t pid, uint64_t src, - uint64_t dst); -/** - * @brief Monitor take a capability from a process. - * - * @param i Index to monitor capability. - * @param pid Process to take capability from. - * @param src Index of capability to take - * @param dst Destination of capability - * @return TODO - */ -enum s3k_excpt s3k_mtakecap(uint64_t i, uint64_t pid, uint64_t src, - uint64_t dst); -/// @} - -/**************************** API UTILITY *********************************/ - -/** - * @defgroup api-pmp PMP helper functions - * @{ - */ - -/** - * @brief Returns a PMP NAPOT representation of the address range. - * @param begin Start of the range - * @param end End of the range - * @return The PMP NAPOT representation of the range. - * @warning If there is not NAPOT representation, then returns 0. - */ -uint64_t s3k_pmp_napot_addr(uint64_t begin, uint64_t end) - __attribute__((const)); -/** - * @brief Returns start of the range of PMP NAPOT address. - * @param addr The PMP NAPOT address. - * @return The start of the address range. - */ -uint64_t s3k_pmp_napot_begin(uint64_t addr); - -/** - * @brief Returns the end of the range of PMP NAPOT address. - * @param addr The PMP NAPOT address - * @return The end of the address range. - */ -uint64_t s3k_pmp_napot_end(uint64_t addr); - -/// @} - -/** - * @defgroup api-cap-constr S3K Capability constructors. - * @{ - */ - -/** - * @brief Create a time slice capability - * @param hartid Hardware thread to run on. - * @param begin Start of time slice. - * @param end End of time slice. - * @return A time slice capability. - */ -union s3k_cap s3k_time(uint64_t hartid, uint64_t begin, uint64_t end); - -/** - * @brief Create a memory slice capability - * - * To compress the representation of memory slices, we have split the beginning - * and end of the memory slice with offset. The start of the memory slice is - * `(offset << 27) + (begin << 12)', the end is `(offset << 27) + (begin << - * 12)'. - * - * @param begin Start of memory slice. - * @param end End of end slice. - * @param offset 128 MiB offset - * @param rwx Read, write, and execute permissions. - * @return A memory slice capability. - */ -union s3k_cap s3k_memory(uint64_t begin, uint64_t end, uint64_t offset, - uint64_t rwx); - -/** - * @brief Create a PMP NAPOT frame capability - * - * @param addr PMP address in NAPOT format. - * @param cfg PMP cfg. - * @return A PMP capability. - */ -union s3k_cap s3k_pmp(uint64_t addr, uint64_t rwx); - -/** - * @brief Create a monitor slice capability - * - * @param begin Start of monitored processes' PID. - * @param end End of monitored processes' PID. - * @return A monitor slice capability. - */ -union s3k_cap s3k_monitor(uint64_t begin, uint64_t end); -/// Create a channel slice capability -union s3k_cap s3k_channel(uint64_t begin, uint64_t end); -/// Create a socket capability -union s3k_cap s3k_socket(uint64_t port, uint64_t tag); - -/// @} - -/** - * @defgroup api-cap-help S3K Capability helper functions. - * @{ - */ -/// Check if a time slice can derive the child. -bool s3k_time_derive(union s3k_cap parent, union s3k_cap child); -/// Check if a memory slice can derive the child. -bool s3k_memory_derive(union s3k_cap parent, union s3k_cap child); -/// Check if a monitor slice can derive the child. -bool s3k_monitor_derive(union s3k_cap parent, union s3k_cap child); -/// Check if channel slice can derive the child. -bool s3k_channel_derive(union s3k_cap parent, union s3k_cap child); -/// Check if socket slice can derive the child. -bool s3k_socket_derive(union s3k_cap parent, union s3k_cap child); -/// Check if parent can derive the child. -bool s3k_can_derive(union s3k_cap parent, union s3k_cap child); -/// Check if the time slice capability is a parent -bool s3k_time_parent(union s3k_cap parent, union s3k_cap child); -/// Check if the memory slice capability is a parent -bool s3k_memory_parent(union s3k_cap parent, union s3k_cap child); -/// Check if the monitor slice capability is a parent -bool s3k_monitor_parent(union s3k_cap parent, union s3k_cap child); -/// Check if the channel slice capability is a parent -bool s3k_channel_parent(union s3k_cap parent, union s3k_cap child); -/// Check if the socket slice capability is a parent -bool s3k_socket_parent(union s3k_cap parent, union s3k_cap child); -/// Check if capability is a parent -bool s3k_is_parent(union s3k_cap parent, union s3k_cap child); - -/// @} -/// @} - -#endif /* _S3K_H_ */ diff --git a/plat/virt/altio.h b/common/inc/altio/altio.h similarity index 54% rename from plat/virt/altio.h rename to common/inc/altio/altio.h index 016569d2..b0bf7bbb 100644 --- a/plat/virt/altio.h +++ b/common/inc/altio/altio.h @@ -1,12 +1,7 @@ -#ifndef __ALTIO_H__ -#define __ALTIO_H__ - -#define UART_BASE ((volatile unsigned char *)0x10000000) +#pragma once int alt_getchar(void); int alt_putchar(char c); int alt_putstr(const char *str); int alt_puts(const char *str); int alt_printf(const char *fmt, ...); - -#endif /* __ALTIO_H__ */ diff --git a/common/inc/drivers/timer.h b/common/inc/drivers/timer.h new file mode 100644 index 00000000..0fed4904 --- /dev/null +++ b/common/inc/drivers/timer.h @@ -0,0 +1,14 @@ +#pragma once +#include + +/** Get the RT clock */ +uint64_t time_get(void); + +/** Set the RT clock */ +void time_set(uint64_t time); + +/** Get the RT clock timeout for hart `hartid' */ +uint64_t timer_get(uint64_t hartid); + +/** Set the RT clock timeout for hart `hartid' */ +void timer_set(uint64_t hartid, uint64_t timeout); diff --git a/common/inc/drivers/uart.h b/common/inc/drivers/uart.h new file mode 100644 index 00000000..29453def --- /dev/null +++ b/common/inc/drivers/uart.h @@ -0,0 +1,16 @@ +#pragma once + +/** Enable serial port. */ +void uart_init(void); + +/** Put char on serial port. */ +int uart_putc(char c); + +/** Get char from serial port. */ +int uart_getc(void); + +/** Puts a string on serial port */ +int uart_puts(const char *s); + +/** Gets a string from serial port */ +char *uart_gets(char *s); diff --git a/common/inc/s3k/s3k.h b/common/inc/s3k/s3k.h new file mode 100644 index 00000000..253ad526 --- /dev/null +++ b/common/inc/s3k/s3k.h @@ -0,0 +1,628 @@ +#ifndef S3K_H +#define S3K_H + +#include + +typedef enum { + S3K_MEM_NONE = 0, + S3K_MEM_R = 1, + S3K_MEM_W = 2, + S3K_MEM_X = 4, + S3K_MEM_RW = S3K_MEM_R | S3K_MEM_W, + S3K_MEM_RX = S3K_MEM_R | S3K_MEM_X, + S3K_MEM_RWX = S3K_MEM_R | S3K_MEM_W | S3K_MEM_X, +} s3k_mem_perm_t; + +// IPC Modes +typedef enum { + S3K_IPC_NOYIELD = 0, // Non-Yielding Synchronous + S3K_IPC_YIELD = 1, // Yielding Synchronous + // S3K_IPC_ASYNC = 2, // Asynchronous +} s3k_ipc_mode_t; + +// IPC Permissions +typedef enum { + S3K_IPC_SDATA = 1, // Server can send data + S3K_IPC_SCAP = 2, // Server can send capabilities + S3K_IPC_CDATA = 4, // Client can send data + S3K_IPC_CCAP = 8, // Client can send capabilities +} s3k_ipc_perm_t; + +// Capability types +typedef enum s3k_capty { + S3K_CAPTY_NONE = 0, ///< No capability. + S3K_CAPTY_TIME = 1, ///< Time Slice capability. + S3K_CAPTY_MEMORY = 2, ///< Memory Slice capability. + S3K_CAPTY_PMP = 3, ///< PMP Frame capability. + S3K_CAPTY_MONITOR = 4, ///< Monitor capability. + S3K_CAPTY_CHANNEL = 5, ///< IPC Channel capability. + S3K_CAPTY_SOCKET = 6, ///< IPC Socket capability. +} s3k_capty_t; + +/// Capability description +typedef union s3k_cap { + uint64_t type : 4; + uint64_t raw; + + struct { + uint64_t type : 4; + uint64_t unused : 4; + uint64_t hart : 8; + uint64_t base : 16; + uint64_t alloc : 16; + uint64_t size : 16; + } time; + + struct { + uint64_t type : 4; + uint64_t rwx : 3; + uint64_t lock : 1; + uint64_t base : 24; + uint64_t alloc : 16; + uint64_t size : 16; + } memory; + + struct { + uint64_t type : 4; + uint64_t rwx : 3; + uint64_t used : 1; + uint64_t index : 8; + uint64_t addr : 48; + } pmp; + + struct { + uint64_t type : 4; + uint64_t unused : 12; + uint64_t base : 16; + uint64_t alloc : 16; + uint64_t size : 16; + } monitor; + + struct { + uint64_t type : 4; + uint64_t unused : 12; + uint64_t base : 16; + uint64_t alloc : 16; + uint64_t size : 16; + } channel; + + struct { + uint64_t type : 4; + uint64_t mode : 4; + uint64_t perm : 8; + uint64_t channel : 16; + uint64_t tag : 32; + } socket; +} s3k_cap_t; + +typedef enum { + SUCCESS, + ERR_EMPTY, + ERR_SRC_EMPTY, + ERR_DST_OCCUPIED, + ERR_INVALID_CLIENT, + ERR_INVALID_DERIVATION, + ERR_INVALID_INDEX, + ERR_INVALID_MONITOR, + ERR_INVALID_PID, + ERR_INVALID_PMP, + ERR_INVALID_SERVER, + ERR_INVALID_SLOT, + ERR_INVALID_STATE, + ERR_INVALID_SYSCALL, + ERR_NO_RECEIVER, + ERR_PREEMPTED, + ERR_SUSPENDED, +} s3k_error_t; + +typedef enum { + // Basic Info & Registers + S3K_SYSCALL_GET_INFO, // Retrieve system information + S3K_SYSCALL_REG_READ, // Read from a register + S3K_SYSCALL_REG_WRITE, // Write to a register + S3K_SYSCALL_SYNC, // Synchronize memory and time. + S3K_SYSCALL_SYNC_MEM, // Synchronize memory. + + // Capability Management + S3K_SYSCALL_CAP_READ, // Read the properties of a capability. + S3K_SYSCALL_CAP_MOVE, // Move a capability to a different slot. + S3K_SYSCALL_CAP_DELETE, // Delete a capability from the system. + S3K_SYSCALL_CAP_REVOKE, // Deletes derived capabilities. + S3K_SYSCALL_CAP_DERIVE, // Creates a new capability. + + // PMP calls + S3K_SYSCALL_PMP_LOAD, + S3K_SYSCALL_PMP_UNLOAD, + + // Monitor calls + S3K_SYSCALL_MON_SUSPEND, + S3K_SYSCALL_MON_RESUME, + S3K_SYSCALL_MON_REG_READ, + S3K_SYSCALL_MON_REG_WRITE, + S3K_SYSCALL_MON_CAP_READ, + S3K_SYSCALL_MON_CAP_MOVE, + S3K_SYSCALL_MON_PMP_LOAD, + S3K_SYSCALL_MON_PMP_UNLOAD, + + S3K_SYSCALL_SOCK_SEND, + S3K_SYSCALL_SOCK_CALL, + S3K_SYSCALL_SOCK_REPLY, + S3K_SYSCALL_SOCK_RECV, + S3K_SYSCALL_SOCK_REPLYRECV, +} s3k_syscall_t; + +typedef enum { + S3K_REG_PC, + S3K_REG_RA, + S3K_REG_SP, + S3K_REG_GP, + S3K_REG_TP, + S3K_REG_T0, + S3K_REG_T1, + S3K_REG_T2, + S3K_REG_S0, + S3K_REG_S1, + S3K_REG_A0, + S3K_REG_A1, + S3K_REG_A2, + S3K_REG_A3, + S3K_REG_A4, + S3K_REG_A5, + S3K_REG_A6, + S3K_REG_A7, + S3K_REG_S2, + S3K_REG_S3, + S3K_REG_S4, + S3K_REG_S5, + S3K_REG_S6, + S3K_REG_S7, + S3K_REG_S8, + S3K_REG_S9, + S3K_REG_S10, + S3K_REG_S11, + S3K_REG_T3, + S3K_REG_T4, + S3K_REG_T5, + S3K_REG_T6, + S3K_REG_TPC, + S3K_REG_TSP, + S3K_REG_EPC, + S3K_REG_ESP, + S3K_REG_ECAUSE, + S3K_REG_EVAL +} s3k_reg_t; + +/** + * Construct a time slice capability. + * + * @param hart Hardware thread ID. + * @param base Base of the time slice. + * @param size Size of the time slice. + * @return Initialized time capability. + */ +static inline s3k_cap_t s3k_time_cap(uint64_t hart, uint64_t base, uint64_t size) +{ + s3k_cap_t cap; + cap.type = S3K_CAPTY_TIME; + cap.time.base = base; + cap.time.alloc = 0; + cap.time.size = size; + return cap; +} + +/** + * Construct a memory slice capability. + * + * @param base Base memory address in 4KB. + * @param size Size of the memory in 4KB. + * @param rwx Read-write-execute permissions encoded as bit flags. + * @return Initialized memory capability. + */ +static inline s3k_cap_t s3k_memory_cap(uint64_t base, uint64_t size, uint64_t rwx) +{ + s3k_cap_t cap; + cap.type = S3K_CAPTY_MEMORY; + cap.memory.base = base; + cap.memory.alloc = 0; + cap.memory.size = size; + cap.memory.rwx = rwx; + cap.memory.lock = 0; + return cap; +} + +/** + * Construct a PMP (Physical Memory Protection) capability using NAPOT mode. + * + * @param addr Base address of the naturally aligned power-of-two region. The + * alignment should adhere to the constraints set by NAPOT mode. + * @param rwx Read-write-execute permissions encoded as bit flags for the + * region. + * @return Initialized PMP capability. + */ +static inline s3k_cap_t s3k_pmp_cap(uint64_t addr, uint64_t rwx) +{ + s3k_cap_t cap; + cap.type = S3K_CAPTY_PMP; + cap.pmp.addr = addr; + cap.pmp.rwx = rwx; + cap.pmp.used = 0; + cap.pmp.index = 0; + return cap; +} + +/** + * Construct a monitor slice capability. + * + * @param base Base of for the monitor. + * @param size Size of the monitor capability. + * @return Initialized monitor capability. + */ +static inline s3k_cap_t s3k_monitor_cap(uint64_t base, uint64_t size) +{ + s3k_cap_t cap; + cap.type = S3K_CAPTY_MONITOR; + cap.monitor.base = base; + cap.monitor.size = size; + return cap; +} + +/** + * Construct an IPC channel slice capability. + * + * @param base Base of the IPC channel. + * @param size Size of the IPC channel capability. + * @return Initialized IPC channel capability. + */ +static inline s3k_cap_t s3k_channel_cap(uint64_t base, uint64_t size) +{ + s3k_cap_t cap; + cap.type = S3K_CAPTY_CHANNEL; + cap.channel.base = base; + cap.channel.size = size; + return cap; +} + +/** + * Construct an IPC socket capability. + * + * Sockets are of two main types: server and client. Server sockets have a tag + * of 0, while client sockets have a non-zero tag. Depending on the mode + * (yielding, non-yielding, or asynchronous), there can be a transfer or + * donation of execution time. Specifically: + * - Yielding: Upon sending a message, the sender donates its remaining + * execution time to the receiver. + * - Non-Yielding: Standard communication without time donation. + * - Asynchronous: The client donates its execution time to the server + * asynchronously. + * + * @param channel Channel identifier or address for the IPC socket. + * @param mode Communication mode for the IPC socket. + * @param perm Permissions associated with the IPC socket, determining the + * behavior of both server and client: + * - SERVER_DATA: Server can send data, and the client is + * expected to receive data. + * - SERVER_CAP: Server can send capabilities, and the client is + * expected to receive capabilities. + * - CLIENT_DATA: Client can send data, and the server is + * expected to receive data. + * - CLIENT_CAP: Client can send capabilities, and the server is + * expected to receive capabilities. + * @param tag Tag or type identifier for the IPC socket. A tag of 0 + * indicates a server socket, whereas a non-zero tag indicates a client socket. + * @return Initialized IPC socket capability. + */ +static inline s3k_cap_t s3k_socket_cap(uint64_t channel, uint64_t mode, uint64_t perm, uint64_t tag) +{ + s3k_cap_t cap; + cap.type = S3K_CAPTY_SOCKET; + cap.socket.channel = channel; + cap.socket.mode = mode; + cap.socket.perm = perm; + cap.socket.tag = tag; + return cap; +} + +// Utility functions +static inline void s3k_napot_decode(uint64_t addr, uint64_t *base, uint64_t *size) +{ + *base = ((addr + 1) & addr) << 2; + *size = (((addr + 1) ^ addr) + 1) << 2; +} + +static inline uint64_t s3k_napot_encode(uint64_t base, uint64_t size) +{ + return (base | (size / 2 - 1)) >> 2; +} + +static inline int s3k_is_parent(s3k_cap_t a, s3k_cap_t b) +{ + if (a.type == S3K_CAPTY_TIME && b.type == S3K_CAPTY_TIME) { + return a.time.hart == b.time.hart && a.time.base <= b.time.base + && (b.time.base + b.time.size) <= (a.time.base + a.time.size); + } + + if (a.type == S3K_CAPTY_MEMORY && b.type == S3K_CAPTY_MEMORY) { + return a.memory.base <= b.memory.base + && (b.memory.base + b.memory.size) <= (a.memory.base + a.memory.size) + && (a.memory.rwx & b.memory.rwx) == b.memory.rwx; + } + + if (a.type == S3K_CAPTY_MEMORY && b.type == S3K_CAPTY_PMP) { + uint64_t mem_base = a.memory.base << 12; + uint64_t mem_size = a.memory.size << 12; + uint64_t pmp_begin, pmp_end; + s3k_napot_decode(b.pmp.addr, &pmp_begin, &pmp_end); + return mem_base <= pmp_begin && pmp_end <= (mem_base + mem_size) + && (a.memory.rwx & b.pmp.rwx) == b.pmp.rwx; + } + + if (a.type == S3K_CAPTY_MONITOR && b.type == S3K_CAPTY_MONITOR) { + return a.monitor.base <= b.monitor.base + && (b.monitor.base + b.monitor.size) <= (a.monitor.base + a.monitor.size); + } + + if (a.type == S3K_CAPTY_CHANNEL && b.type == S3K_CAPTY_CHANNEL) { + return a.channel.base <= b.channel.base + && (b.channel.base + b.channel.size) <= (a.channel.base + a.channel.size); + } + + if (a.type == S3K_CAPTY_CHANNEL && b.type == S3K_CAPTY_SOCKET) { + return a.channel.base <= b.socket.channel && b.socket.channel < (a.channel.base + a.channel.size); + } + + if (a.type == S3K_CAPTY_SOCKET && b.type == S3K_CAPTY_SOCKET) { + return a.socket.channel == b.socket.channel && a.socket.mode == b.socket.mode + && a.socket.perm == b.socket.perm && a.socket.tag == 0 && b.socket.tag != 0; + } + + return 0; +} + +static inline int s3k_is_derivable(s3k_cap_t a, s3k_cap_t b) +{ + if (a.type == S3K_CAPTY_TIME && b.type == S3K_CAPTY_TIME) { + return a.time.hart == b.time.hart && (a.time.base + a.time.alloc) == b.time.base + && (b.time.base + b.time.size) <= (a.time.base + a.time.size); + } + + if (a.type == S3K_CAPTY_MEMORY && b.type == S3K_CAPTY_MEMORY) { + return (a.memory.base + a.time.alloc) == b.memory.base + && (b.memory.base + b.memory.size) <= (a.memory.base + a.memory.size) && a.memory.lock == 0 + && b.memory.lock == 0 && (a.memory.rwx & b.memory.rwx) == b.memory.rwx; + } + + if (a.type == S3K_CAPTY_MEMORY && b.type == S3K_CAPTY_PMP) { + uint64_t memory_base = a.memory.base << 12; + uint64_t memory_alloc = a.memory.alloc << 12; + uint64_t memory_size = a.memory.size << 12; + uint64_t pmp_begin, pmp_end; + s3k_napot_decode(b.pmp.addr, &pmp_begin, &pmp_end); + return (memory_base + memory_alloc) <= pmp_begin && pmp_end <= (memory_base + memory_size) + && (a.memory.rwx & b.pmp.rwx) == b.pmp.rwx; + } + + if (a.type == S3K_CAPTY_MONITOR && b.type == S3K_CAPTY_MONITOR) { + return (a.monitor.base + a.monitor.alloc) == b.monitor.base + && (b.monitor.base + b.monitor.size) <= (a.monitor.base + a.monitor.size); + } + + if (a.type == S3K_CAPTY_CHANNEL && b.type == S3K_CAPTY_CHANNEL) { + return (a.channel.base + a.channel.alloc) == b.channel.base + && (b.channel.base + b.channel.size) <= (a.channel.base + a.channel.size); + } + + if (a.type == S3K_CAPTY_CHANNEL && b.type == S3K_CAPTY_SOCKET) { + return (a.channel.base + a.channel.alloc) == b.socket.channel + && b.socket.channel < (a.channel.base + a.channel.size) && b.socket.tag == 0; + } + + if (a.type == S3K_CAPTY_SOCKET && b.type == S3K_CAPTY_SOCKET) { + return a.socket.channel == b.socket.channel && a.socket.mode == b.socket.mode + && a.socket.perm == b.socket.perm && a.socket.tag == 0 && b.socket.tag != 0; + } + + return 0; +} + +// System calls +static inline uint64_t s3k_get_pid(void) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_GET_INFO; + register uint64_t a0 __asm__("a0") = 0; + __asm__ volatile("ecall" : "+r"(a0) : "r"(t0)); + return a0; +} + +static inline uint64_t s3k_get_time(void) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_GET_INFO; + register uint64_t a0 __asm__("a0") = 1; + __asm__ volatile("ecall" : "+r"(a0) : "r"(t0)); + return a0; +} + +static inline uint64_t s3k_get_timeout(void) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_GET_INFO; + register uint64_t a0 __asm__("a0") = 2; + __asm__ volatile("ecall" : "+r"(a0) : "r"(t0)); + return a0; +} + +static inline uint64_t s3k_read_reg(uint64_t reg) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_REG_READ; + register uint64_t a0 __asm__("a0") = reg; + __asm__ volatile("ecall" : "+r"(a0) : "r"(t0)); + return a0; +} + +static inline void s3k_write_reg(uint64_t reg, uint64_t val) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_REG_WRITE; + register uint64_t a0 __asm__("a0") = reg; + register uint64_t a1 __asm__("a1") = val; + __asm__ volatile("ecall" ::"r"(a0), "r"(a1), "r"(t0)); +} + +static inline void s3k_sync(void) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_SYNC; + __asm__ volatile("ecall" ::"r"(t0)); +} + +static inline void s3k_sync_mem(void) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_SYNC_MEM; + __asm__ volatile("ecall" ::"r"(t0)); +} + +static inline s3k_error_t s3k_cap_read(uint64_t read_idx, s3k_cap_t *cap) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_CAP_READ; + register uint64_t a0 __asm__("a0") = read_idx; + __asm__ volatile("ecall" : "+r"(t0), "+r"(a0)); + if (!a0) + cap->raw = a0; + return t0; +} + +static inline s3k_error_t s3k_cap_move(uint64_t src_idx, uint64_t dst_idx) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_CAP_MOVE; + register uint64_t a0 __asm__("a0") = src_idx; + register uint64_t a1 __asm__("a1") = dst_idx; + __asm__ volatile("ecall" : "+r"(t0) : "r"(a0), "r"(a1)); + return t0; +} + +static inline s3k_error_t s3k_cap_delete(uint64_t del_idx) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_CAP_DELETE; + register uint64_t a0 __asm__("a0") = del_idx; + __asm__ volatile("ecall" : "+r"(t0) : "r"(a0)); + return t0; +} + +static inline s3k_error_t s3k_cap_revoke(uint64_t rev_idx) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_CAP_REVOKE; + register uint64_t a0 __asm__("a0") = rev_idx; + __asm__ volatile("ecall" : "+r"(t0) : "r"(a0)); + return t0; +} + +static inline s3k_error_t s3k_cap_derive(uint64_t src_idx, uint64_t dst_idx, s3k_cap_t new_cap) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_CAP_DERIVE; + register uint64_t a0 __asm__("a0") = src_idx; + register uint64_t a1 __asm__("a1") = dst_idx; + register uint64_t a2 __asm__("a2") = new_cap.raw; + __asm__ volatile("ecall" : "+r"(t0) : "r"(a0), "r"(a1), "r"(a2)); + return t0; +} + +static inline s3k_error_t s3k_pmp_load(uint64_t pmp_idx, uint64_t pmp_slot) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_PMP_LOAD; + register uint64_t a0 __asm__("a0") = pmp_idx; + register uint64_t a1 __asm__("a1") = pmp_slot; + __asm__ volatile("ecall" : "+r"(t0) : "r"(a0), "r"(a1)); + return t0; +} + +static inline s3k_error_t s3k_pmp_unload(uint64_t pmp_idx) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_PMP_UNLOAD; + register uint64_t a0 __asm__("a0") = pmp_idx; + __asm__ volatile("ecall" : "+r"(t0) : "r"(a0)); + return t0; +} + +static inline s3k_error_t s3k_mon_suspend(uint64_t mon_idx, uint64_t pid) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_MON_SUSPEND; + register uint64_t a0 __asm__("a0") = mon_idx; + register uint64_t a1 __asm__("a1") = pid; + __asm__ volatile("ecall" : "+r"(t0) : "r"(a0), "r"(a1)); + return t0; +} + +static inline s3k_error_t s3k_mon_resume(uint64_t mon_idx, uint64_t pid) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_MON_RESUME; + register uint64_t a0 __asm__("a0") = mon_idx; + register uint64_t a1 __asm__("a1") = pid; + __asm__ volatile("ecall" : "+r"(t0) : "r"(a0), "r"(a1)); + return t0; +} + +static inline s3k_error_t s3k_mon_reg_read(uint64_t mon_idx, uint64_t pid, uint64_t reg, uint64_t *val) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_MON_REG_READ; + register uint64_t a0 __asm__("a0") = mon_idx; + register uint64_t a1 __asm__("a1") = pid; + register uint64_t a2 __asm__("a2") = reg; + __asm__ volatile("ecall" : "+r"(t0), "+r"(a0) : "r"(a1), "r"(a2)); + *val = a0; + return t0; +} + +static inline s3k_error_t s3k_mon_reg_write(uint64_t mon_idx, uint64_t pid, uint64_t reg, uint64_t val) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_MON_REG_WRITE; + register uint64_t a0 __asm__("a0") = mon_idx; + register uint64_t a1 __asm__("a1") = pid; + register uint64_t a2 __asm__("a2") = reg; + register uint64_t a3 __asm__("a3") = val; + __asm__ volatile("ecall" : "+r"(t0) : "r"(a0), "r"(a1), "r"(a2), "r"(a3)); + return t0; +} + +static inline s3k_error_t s3k_mon_cap_read(uint64_t mon_idx, uint64_t pid, uint64_t read_idx, s3k_cap_t *cap) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_MON_CAP_READ; + register uint64_t a0 __asm__("a0") = mon_idx; + register uint64_t a1 __asm__("a1") = pid; + register uint64_t a2 __asm__("a2") = read_idx; + __asm__ volatile("ecall" : "+r"(t0), "+r"(a0) : "r"(a1), "r"(a2)); + if (!t0) + cap->raw = a0; + return t0; +} + +static inline s3k_error_t s3k_mon_cap_move(uint64_t mon_idx, uint64_t src_pid, uint64_t src_idx, uint64_t dst_pid, + uint64_t dst_idx) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_MON_CAP_MOVE; + register uint64_t a0 __asm__("a0") = mon_idx; + register uint64_t a1 __asm__("a1") = src_pid; + register uint64_t a2 __asm__("a2") = src_idx; + register uint64_t a3 __asm__("a3") = dst_pid; + register uint64_t a4 __asm__("a4") = dst_idx; + __asm__ volatile("ecall" : "+r"(t0) : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4)); + return t0; +} + +static inline s3k_error_t s3k_mon_pmp_load(uint64_t mon_idx, uint64_t pid, uint64_t pmp_idx, uint64_t pmp_slot) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_MON_PMP_LOAD; + register uint64_t a0 __asm__("a0") = mon_idx; + register uint64_t a1 __asm__("a1") = pid; + register uint64_t a2 __asm__("a2") = pmp_idx; + register uint64_t a3 __asm__("a3") = pmp_slot; + __asm__ volatile("ecall" : "+r"(t0) : "r"(a0), "r"(a1), "r"(a2), "r"(a3)); + return t0; +} + +static inline s3k_error_t s3k_mon_pmp_unload(uint64_t mon_idx, uint64_t pid, uint64_t pmp_idx) +{ + register uint64_t t0 __asm__("t0") = S3K_SYSCALL_MON_PMP_UNLOAD; + register uint64_t a0 __asm__("a0") = mon_idx; + register uint64_t a1 __asm__("a1") = pid; + register uint64_t a2 __asm__("a2") = pmp_idx; + __asm__ volatile("ecall" : "+r"(t0) : "r"(a0), "r"(a1), "r"(a2)); + return t0; +} + +#endif /* S3K_H */ diff --git a/src/altio.c b/common/src/altio/altio.c similarity index 79% rename from src/altio.c rename to common/src/altio/altio.c index 5f6e48c3..2f0a3352 100644 --- a/src/altio.c +++ b/common/src/altio/altio.c @@ -1,20 +1,17 @@ -#include "altio.h" +#include "altio/altio.h" -#include +#include "drivers/uart.h" -#define UART_BASE ((volatile unsigned char *)0x10000000) +#include int alt_getchar(void) { - while (!(UART_BASE[5] & 1)) - ; - return UART_BASE[0]; + return uart_getc(); } int alt_putchar(char c) { - UART_BASE[0] = c; - return c; + return uart_putc(c); } int alt_putstr(const char *str) @@ -35,8 +32,8 @@ int alt_puts(const char *str) int alt_printf(const char *fmt, ...) { - static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + static const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; unsigned long long x; int len; va_list args; @@ -51,7 +48,7 @@ int alt_printf(const char *fmt, ...) } switch (*fmt++) { case 'c': - alt_putchar(va_arg(args, int)); + alt_putchar((char)va_arg(args, int)); len++; break; case 's': diff --git a/common/src/drivers/fu540-uart.c b/common/src/drivers/fu540-uart.c new file mode 100644 index 00000000..76e81c3e --- /dev/null +++ b/common/src/drivers/fu540-uart.c @@ -0,0 +1,41 @@ +#include "drivers/uart.h" + +extern volatile unsigned long long _uart[]; + +void uart_init(void) +{ + _uart[2] = 1; + _uart[3] = 1; +} + +int uart_putc(char c) +{ + while (_uart[0] < 0) + ; + _uart[0] = c; + return c; +} + +int uart_getc(void) +{ + int c; + while ((c = _uart[1]) < 0) + ; + return c; +} + +int uart_puts(const char *s) +{ + int i = 0; + while (s[i] != '\0') + uart_putc(s[i++]); + uart_putc('\n'); + return i + 1; +} + +char *uart_gets(char *s) +{ + while ((*s = (char)uart_getc()) != '\0') + ; + return s; +} diff --git a/common/src/drivers/ns16550a.c b/common/src/drivers/ns16550a.c new file mode 100644 index 00000000..d57319fb --- /dev/null +++ b/common/src/drivers/ns16550a.c @@ -0,0 +1,37 @@ +#include "drivers/uart.h" + +extern volatile char _uart[]; + +void uart_init(void) +{ + /** TODO: Proper init code for uart */ +} + +int uart_putc(char c) +{ + _uart[0] = c; + return c; +} + +int uart_getc(void) +{ + while (!(_uart[5] & 0x1)) + ; + return _uart[0]; +} + +int uart_puts(const char *s) +{ + int i = 0; + while (s[i] != '\0') + uart_putc(s[i++]); + uart_putc('\n'); + return i + 1; +} + +char *uart_gets(char *s) +{ + while ((*s = (char)uart_getc()) != '\0') + ; + return s; +} diff --git a/common/src/drivers/timer.c b/common/src/drivers/timer.c new file mode 100644 index 00000000..5bd24991 --- /dev/null +++ b/common/src/drivers/timer.c @@ -0,0 +1,28 @@ +#include "drivers/timer.h" + +extern volatile uint64_t _mtime[]; +extern volatile uint64_t _mtimecmp[]; + +/** Get the RT clock */ +uint64_t time_get(void) +{ + return _mtime[0]; +} + +/** Set the RT clock */ +void time_set(uint64_t time) +{ + _mtime[0] = time; +} + +/** Get the RT clock timeout for hart `hartid' */ +uint64_t timer_get(uint64_t hartid) +{ + return _mtimecmp[hartid]; +} + +/** Set the RT clock timeout for hart `hartid' */ +void timer_set(uint64_t hartid, uint64_t timeout) +{ + _mtimecmp[hartid] = timeout; +} diff --git a/common/src/init/start.S b/common/src/init/start.S new file mode 100644 index 00000000..ea9c7216 --- /dev/null +++ b/common/src/init/start.S @@ -0,0 +1,28 @@ +.globl _start +#define STACK_SIZE 1024 +/* Defines program entry */ +.section .init,"ax",@progbits +_start: + +.option push +.option norelax + la gp,__global_pointer$ +.option pop + la sp,__stack_top + + la t0,_bss + la t1,_end + j 2f +1: sb zero,(t0) + addi t0,t0,1 +2: bne t0,t1,1b + + call main + +1: j 1b + +/* Defines stack */ +.section .bss,"aw",@nobits +.balign 8 + .space STACK_SIZE +__stack_top: diff --git a/config.def.h b/config.def.h new file mode 100644 index 00000000..d7edadd6 --- /dev/null +++ b/config.def.h @@ -0,0 +1,29 @@ +// Default kernel configuration +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +// Number of user processes +#define N_PROC 8 + +// Number of capabilities per process. +#define N_CAP 32 + +// Number of IPC channels. +#define N_CHAN 16 + +// Number of slots per period +#define N_SLOT 64ull + +// Length of slots in ticks. +#define SLOT_LENGTH (TICKS_PER_SECOND / N_SLOT / 1ull) + +// Scheduler time +#define SCHEDULER_TIME 10000 + +// If debugging, uncomment +// #define NDEBUG + +// If instrumenting +#define INSTRUMENTATION + +#endif /* __CONFIG_H__ */ diff --git a/config.h b/config.h deleted file mode 100644 index 21c456b7..00000000 --- a/config.h +++ /dev/null @@ -1,15 +0,0 @@ -// Kernel configuration - -// Number of user processes -#define NPROC 16 -// Number of capabilities per process. -#define NCAP 32 -// Number of IPC channels. -#define NCHANNEL 16 -// Number of slices per period -#define NSLICE 64 -// Number of ticks per slice -#define NTICK 100000000 -// Number of slack ticks per slice -#define NSLACK 10000 -// #define NDEBUG diff --git a/config.mk b/config.mk deleted file mode 100644 index 1a0c5891..00000000 --- a/config.mk +++ /dev/null @@ -1,40 +0,0 @@ -### Kernel configuration -# build dir -BUILD_DIR?=build -OBJ_DIR?=$(BUILD_DIR)/obj -# config -CONFIG_H?=config.h -# Platform -PLATFORM?=plat/virt -# Prefix for toolchain -RISCV_PREFIX?=riscv64-unknown-elf- - - -### Compilation configuration -CC=${RISCV_PREFIX}gcc -OBJDUMP=${RISCV_PREFIX}objdump - -INC =-include ${CONFIG_H} -Iinc -I${PLATFORM} -CFLAGS =-march=rv64imac -mabi=lp64 -mcmodel=medany\ - -std=gnu17\ - -Wall -Wextra -Werror\ - -Wno-unused-parameter\ - -Wshadow -fno-common\ - -ffunction-sections -fdata-sections\ - -ffreestanding\ - -Wno-builtin-declaration-mismatch\ - -flto -fwhole-program --specs=nosys.specs\ - -Wstack-usage=1024 -fstack-usage\ - -fno-stack-protector \ - -Os -g3 -ASFLAGS=-march=rv64imac -mabi=lp64 -mcmodel=medany\ - -g3 -LDFLAGS=-march=rv64imac -mabi=lp64 -mcmodel=medany\ - -nostartfiles -ffreestanding\ - -Wstack-usage=1024 -fstack-usage\ - -fno-stack-protector \ - -Wl,--gc-sections\ - -flto\ - -T${PLATFORM}/linker.ld - -# -Wl,--no-warn-rwx-segments\ <== this option is not supported by all linkers diff --git a/inc/cap.h b/inc/cap.h deleted file mode 100644 index da804169..00000000 --- a/inc/cap.h +++ /dev/null @@ -1,170 +0,0 @@ -/** - * @file cap.h - * @brief Functions for handling capabilities. - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - */ -#ifndef __CAP_H__ -#define __CAP_H__ - -#include "common.h" -#include "consts.h" - -#include -#include - -#define CAP_RWX 0x7 -#define CAP_RW 0x3 -#define CAP_RX 0x5 -#define CAP_R 0x1 - -#define CAP_NULL \ - { \ - .raw = 0 \ - } - -#define CAP_TIME(_hartid, _begin, _end) \ - { \ - .time \ - = {.type = CAPTY_TIME, \ - .unused = 0, \ - .hartid = _hartid, \ - .begin = _begin, \ - .free = _begin, \ - .end = _end } \ - } - -#define CAP_MEMORY(_begin, _end, _offset, _rwx) \ - { \ - .memory \ - = {.type = CAPTY_MEMORY, \ - .lock = 0, \ - .rwx = _rwx, \ - .offset = _offset, \ - .begin = _begin, \ - .free = _begin, \ - .end = _end } \ - } - -#define CAP_PMP(_addr, _cfg) \ - { \ - .pmp = {.type = CAPTY_PMP, .addr = _addr, .cfg = 0x18 | _cfg } \ - } - -#define CAP_MONITOR(_begin, _end) \ - { \ - .monitor \ - = {.type = CAPTY_MONITOR, \ - .unused = 0, \ - .begin = _begin, \ - .free = _begin, \ - .end = _end } \ - } - -#define CAP_CHANNEL(_begin, _end) \ - { \ - .channel \ - = {.type = CAPTY_CHANNEL, \ - .unused = 0, \ - .begin = _begin, \ - .free = _begin, \ - .end = _end } \ - } - -#define CAP_SOCKET(_channel, _tag) \ - { \ - .socket \ - = {.type = CAPTY_SOCKET, \ - .unused = 0, \ - .channel = _channel, \ - .tag = _tag } \ - } - -enum capty { - CAPTY_NONE, ///< No capability - CAPTY_TIME, ///< Time Slice capability - CAPTY_MEMORY, ///< Memory Slice capability - CAPTY_PMP, ///< PMP Frame capability - CAPTY_MONITOR, ///< Monitor capability - CAPTY_CHANNEL, ///< IPC Channel capability - CAPTY_SOCKET, ///< IPC Socket capability -}; - -/// Capability description -union cap { - uint64_t type : 4; - uint64_t raw; - - struct { - uint64_t type : 4; - uint64_t unused : 4; - uint64_t hartid : 8; - uint64_t begin : 16; - uint64_t free : 16; - uint64_t end : 16; - } time; - - struct { - uint64_t type : 4; - uint64_t lock : 1; - uint64_t rwx : 3; - uint64_t offset : 8; - uint64_t begin : 16; - uint64_t free : 16; - uint64_t end : 16; - } memory; - - struct { - uint64_t type : 4; - uint64_t addr : 52; - uint64_t cfg : 8; - } pmp; - - struct { - uint64_t type : 4; - uint64_t unused : 12; - uint64_t begin : 16; - uint64_t free : 16; - uint64_t end : 16; - } monitor; - - struct { - uint64_t type : 4; - uint64_t unused : 12; - uint64_t begin : 16; - uint64_t free : 16; - uint64_t end : 16; - } channel; - - struct { - uint64_t type : 4; - uint64_t unused : 28; - uint64_t channel : 16; - uint64_t tag : 16; - } socket; -}; - -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L -_Static_assert(sizeof(union cap) == 8, "union cap size != 8 bytes"); -#endif - -// PMP utils -uint64_t pmp_napot_addr(uint64_t begin, uint64_t end); -uint64_t pmp_napot_begin(uint64_t addr); -uint64_t pmp_napot_end(uint64_t addr); - -// Derivation check -bool cap_time_derive(union cap parent, union cap child); -bool cap_memory_derive(union cap parent, union cap child); -bool cap_monitor_derive(union cap parent, union cap child); -bool cap_channel_derive(union cap parent, union cap child); -bool cap_socket_derive(union cap parent, union cap child); - -// Check for parent (revocation) -bool cap_time_parent(union cap parent, union cap child); -bool cap_memory_parent(union cap parent, union cap child); -bool cap_monitor_parent(union cap parent, union cap child); -bool cap_channel_parent(union cap parent, union cap child); -bool cap_socket_parent(union cap parent, union cap child); - -#endif /* __CAP_H__ */ diff --git a/inc/cnode.h b/inc/cnode.h deleted file mode 100644 index b6b6bcdb..00000000 --- a/inc/cnode.h +++ /dev/null @@ -1,107 +0,0 @@ -/** - * @file cnode.h - * @brief Functions for handling capabilities tree. - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - */ -#ifndef __CNODE_H__ -#define __CNODE_H__ - -#include "cap.h" - -#include -#include -#include - -#define CNODE_ROOT_HANDLE (NPROC * NCAP) - -/// @defgroup cnode Capability Node -/// -/// Kernel internal module for handling capability tree. -/// -/// The capability tree is not an actual tree, it is a linked list backed by an -/// array. The tree properties exists implicitly by the relations of -/// capabilities. -/// -/// @{ - -typedef unsigned long cnode_handle_t; - -/** - * @brief Initialize the cnode structure. - */ -void cnode_init(const union cap *caps, size_t size); - -/** - * @brief Get the handle for capability i of a process. - * @param pid Process ID of the process. - * @param idx Index. - * @return handle for capability i of process pid. - */ -cnode_handle_t cnode_get_handle(cnode_handle_t pid, cnode_handle_t idx); - -/** - * @brief Get the Process ID of the handle. - * @param handle Handle - * @return PID of handle - */ -cnode_handle_t cnode_get_pid(cnode_handle_t handle); - -/** - * @brief Get the next node after n. - * @param handle Handle for node n. - * @return _handle to the next node. - */ -cnode_handle_t cnode_get_next(cnode_handle_t handle); - -/** - * @brief Get capability at node n. - * @param handle Handle for node n. - * @return Capability. - */ -union cap cnode_get_cap(cnode_handle_t handle); - -/** - * @brief Set capability at node n. - * @param handle Handle for node n. - * @param cap Cap to put in node n. - */ -void cnode_set_cap(cnode_handle_t handle, union cap cap); - -/** - * @brief Checks if capability tree contains node n. - * @param handle Handle for node n. - * @return true if capability is present. - */ -bool cnode_contains(cnode_handle_t handle); - -/** - * @brief Insert a capability at node n after node m. - * @param handle Handle to node n. - * @param cap Capability to insert. - * @param prev_handle Handle to node m. - */ -void cnode_insert(cnode_handle_t handle, union cap cap, - cnode_handle_t prev_handle); - -/** - * @brief Move capability from node n to node m. - * @param src_handle Handle to node n. - * @param dst_handle Handle to node m. - */ -void cnode_move(cnode_handle_t src_handle, cnode_handle_t dstHandle); - -/** - * @brief Delete capability at n. - * @param handle Handle to node n. - */ -void cnode_delete(cnode_handle_t handle); - -/** - * @brief Delete capability at n if m is the predecessor. - * @param handle Handle to node n. - * @param handle Handle to node m. - */ -bool cnode_delete_if(cnode_handle_t handle, cnode_handle_t prev_handle); -/// @} -#endif /* __CNODE_H__ */ diff --git a/inc/consts.h b/inc/consts.h deleted file mode 100644 index 12969998..00000000 --- a/inc/consts.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @file consts.h - * @brief Constants and enums. - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - */ - -#ifndef __CONSTS_H__ -#define __CONSTS_H__ - -/** Process state flags - * PSF_BUSY: Some core - * PSF_BLOCK: Waiting for IPC. - * PSF_SUSPEND: Waiting for monitor - */ -#define PSF_BUSY 1 -#define PSF_BLOCK 2 -#define PSF_SUSPEND 4 -#define PSF_WAITING 8 - -/** Process states - * - */ -#define PS_READY 0 -#define PS_RUNNING 1 -#define PS_BLOCKED 2 -#define PS_BLOCKED_BUSY 3 -#define PS_SUSPENDED 4 -#define PS_SUSPENDED_BUSY 5 - -#endif /* __CONSTS_H__ */ diff --git a/inc/csr.h b/inc/csr.h deleted file mode 100644 index 82c23faf..00000000 --- a/inc/csr.h +++ /dev/null @@ -1,152 +0,0 @@ -/** - * @file csr.h - * @brief Interact with platform - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - */ -#ifndef __CSR_H__ -#define __CSR_H__ - -#include - -/** - * @brief Read the hardware thread ID (mhartid) register. - * - * @return uint64_t The value of the mhartid register. - */ -uint64_t csrr_mhartid(void); - -/** - * @brief Read the machine interrupt pending register (mip). - * - * @return uint64_t The value of the mip register. - */ -uint64_t csrr_mip(void); - -/** - * @brief Read the PMP configuration register 0 (pmpcfg0). - * - * @return uint64_t The value of the pmpcfg0 register. - */ -uint64_t csrr_pmpcfg0(void); - -/** - * @brief Write to the PMP configuration register 0 (pmpcfg0). - * - * @param val The value to write to the pmpcfg0 register. - */ -void csrw_pmpcfg0(uint64_t val); - -/** - * @brief Read PMP address register 0. - * - * @return uint64_t The value of the pmpaddr0 register. - */ -uint64_t csrr_pmpaddr0(void); - -/** - * @brief Read PMP address register 1. - * - * @return uint64_t The value of the pmpaddr1 register. - */ -uint64_t csrr_pmpaddr1(void); - -/** - * @brief Read PMP address register 2. - * - * @return uint64_t The value of the pmpaddr2 register. - */ -uint64_t csrr_pmpaddr2(void); - -/** - * @brief Read PMP address register 3. - * - * @return uint64_t The value of the pmpaddr3 register. - */ -uint64_t csrr_pmpaddr3(void); - -/** - * @brief Read PMP address register 4. - * - * @return uint64_t The value of the pmpaddr4 register. - */ -uint64_t csrr_pmpaddr4(void); - -/** - * @brief Read PMP address register 5. - * - * @return uint64_t The value of the pmpaddr5 register. - */ -uint64_t csrr_pmpaddr5(void); - -/** - * @brief Read PMP address register 6. - * - * @return uint64_t The value of the pmpaddr6 register. - */ -uint64_t csrr_pmpaddr6(void); - -/** - * @brief Read PMP address register 7. - * - * @return uint64_t The value of the pmpaddr7 register. - */ -uint64_t csrr_pmpaddr7(void); - -/** - * @brief Write to PMP address register 0. - * - * @param val The value to write to the pmpaddr0 register. - */ -void csrw_pmpaddr0(uint64_t val); - -/** - * @brief Write to PMP address register 1. - * - * @param val The value to write to the pmpaddr1 register. - */ -void csrw_pmpaddr1(uint64_t val); - -/** - * @brief write to pmp address register 2. - * - * @param val the value to write to the pmpaddr2 register. - */ -void csrw_pmpaddr2(uint64_t val); - -/** - * @brief write to pmp address register 3. - * - * @param val the value to write to the pmpaddr3 register. - */ -void csrw_pmpaddr3(uint64_t val); - -/** - * @brief write to pmp address register 4. - * - * @param val the value to write to the pmpaddr4 register. - */ -void csrw_pmpaddr4(uint64_t val); - -/** - * @brief write to pmp address register 5. - * - * @param val the value to write to the pmpaddr4 register. - */ -void csrw_pmpaddr5(uint64_t val); - -/** - * @brief write to pmp address register 6. - * - * @param val the value to write to the pmpaddr4 register. - */ -void csrw_pmpaddr6(uint64_t val); - -/** - * @brief write to pmp address register 7. - * - * @param val the value to write to the pmpaddr4 register. - */ -void csrw_pmpaddr7(uint64_t val); - -#endif /* __CSR_H__ */ diff --git a/inc/current.h b/inc/current.h deleted file mode 100644 index 446a6dd2..00000000 --- a/inc/current.h +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @file current.h - * @brief Set current process - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - */ -#ifndef __CURRENT_H__ -#define __CURRENT_H__ -#include "proc.h" - -/** - * @brief Sets the currently executing process to the given process. - * - * @param proc A pointer to the process to set as the currently executing - * process. - */ -void current_set(struct proc *proc); - -/** - * @brief Gets a pointer to the currently executing process. - * - * @return A pointer to the currently executing process. - */ -struct proc *current_get(); - -#endif /* __CURRENT_H__ */ diff --git a/inc/kassert.h b/inc/kassert.h deleted file mode 100644 index 09721716..00000000 --- a/inc/kassert.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file kassert.h - * - * @brief Assertion support for the kernel. - * - * This header provides support for assertions in the kernel. When the NDEBUG - * symbol is not defined, the `kassert()` macro can be used to check whether a - * condition is true. If the condition is false, the `kassert_failure()` - * function will be called to report the failure. - * - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - */ -#ifndef __KASSERT_H__ -#define __KASSERT_H__ - -#ifdef NDEBUG - -#define kassert(expr) ((void)0) - -#else /* NDEBUG */ - -#include // for uint64_t type - -/** - * @brief Assertion failure handler. - * - * This function is called when an assertion fails. It prints an error message - * to the console using the `alt_printf()` function and halts the system. - * - * @param file The name of the source file where the assertion failed. - * @param line The line number in the source file where the assertion failed. - * @param expr The assertion expression that failed. - */ -void kassert_failure(const char *file, uint64_t line, const char *expr); - -/** - * @brief Asserts that an expression is true. - * - * This macro evaluates the given expression and, if it is false, calls the - * `kassert_failure()` function with information about the assertion failure. - * - * @param expr The expression to evaluate. - */ -#define kassert(expr) \ - do { \ - if (!(expr)) { \ - kassert_failure(__FILE__, __LINE__, #expr); \ - } \ - } while (false) - -#endif /* NDEBUG */ - -#endif /* __KASSERT_H__ */ diff --git a/inc/macro.inc b/inc/macro.inc deleted file mode 100644 index 67ce3fc9..00000000 --- a/inc/macro.inc +++ /dev/null @@ -1,9 +0,0 @@ -// See LICENSE file for copyright and license details. -#include "stack.h" -.extern stack_top -.macro load_sp tmp - la sp,stack_top - csrr \tmp,mhartid - slli \tmp,\tmp,LOG_STACK_SIZE - sub sp,sp,\tmp -.endm diff --git a/inc/proc.h b/inc/proc.h deleted file mode 100644 index a8d4319d..00000000 --- a/inc/proc.h +++ /dev/null @@ -1,170 +0,0 @@ -/** - * @file proc.h - * @brief Defines the process control block and its associated functions. - * - * This file contains the definition of the `struct proc` data structure, which - * represents a process control block (PCB) in the operating system. It also - * contains the declarations of functions for manipulating the PCB. - * - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - */ -#ifndef __PROC_H__ -#define __PROC_H__ - -#include "cnode.h" - -#include - -enum reg { - /* General purpose registers */ - REG_PC, ///< Program counter - REG_RA, ///< Return address (GPR) - REG_SP, ///< Stack pointer (GPR) - REG_GP, ///< Global pointer (GPR) - REG_TP, ///< Thread pointer (GPR) - REG_T0, ///< Temporary register (GPR) - REG_T1, ///< Temporary register (GPR) - REG_T2, ///< Temporary register (GPR) - REG_S0, ///< Saved register/Stack frame pointer (GPR) - REG_S1, ///< Saved register (GPR) - REG_A0, ///< Argument/Return register (GPR) - REG_A1, ///< Argument/Return register (GPR) - REG_A2, ///< Argument register (GPR) - REG_A3, ///< Argument register (GPR) - REG_A4, ///< Argument register (GPR) - REG_A5, ///< Argument register (GPR) - REG_A6, ///< Argument register (GPR) - REG_A7, ///< Argument register (GPR) - REG_S2, ///< Saved register (GPR) - REG_S3, ///< Saved register (GPR) - REG_S4, ///< Saved register (GPR) - REG_S5, ///< Saved register (GPR) - REG_S6, ///< Saved register (GPR) - REG_S7, ///< Saved register (GPR) - REG_S8, ///< Saved register (GPR) - REG_S9, ///< Saved register (GPR) - REG_S10, ///< Saved register (GPR) - REG_S11, ///< Saved register (GPR) - REG_T3, ///< Temporary register (GPR) - REG_T4, ///< Temporary register (GPR) - REG_T5, ///< Temporary register (GPR) - REG_T6, ///< Temporary register (GPR) - /* Virtual registers */ - /* Trap handling setup */ - REG_TPC, ///< Trap program counter. - REG_TSP, ///< Trap stack pointer. - /* Exception handling registers */ - REG_EPC, ///< Exception program counter. - REG_ESP, ///< Exception stack pointer. - REG_ECAUSE, ///< Exception cause code. - REG_EVAL, ///< Exception value. - /* PMP registers */ - REG_PMP, ///< PMP configuration. - /* End of registers */ - REG_COUNT ///< *Number of S3K registers.* -}; - -/** - * @brief Process control block. - * - * Contains all information needed manage a process except the capabilities. - */ -struct proc { - /** The registers of the process (RISC-V registers and virtual - * registers). */ - uint64_t regs[REG_COUNT]; - /** Process ID. */ - uint64_t pid; - /** Process state. */ - uint64_t state; - /** Sleep until. */ - uint64_t sleep; - /** Capability destination for receive calls */ - uint64_t cap_dest; -}; - -/** - * Initializes all processes in the system. - * - * @param payload A pointer to the boot loader's code. - * - * @note This function should be called only once during system startup. - */ -void proc_init(uint64_t payload); - -/** - * @brief Gets the process corresponding to a given process ID. - * - * @param pid The process ID to look for. - * @return A pointer to the process corresponding to the given PID. - */ -struct proc *proc_get(uint64_t pid); - -/** - * @brief Attempt to acquire the lock for a process. - * - * The process's lock is embedded in its state. This function attempts to - * acquire the lock by atomically setting the LSB of the state to 1 if it - * currently has the value 'expected'. If the lock is already held by another - * process, this function will return false. - * - * @param proc Pointer to the process to acquire the lock for. - * @param expected The expected value of the process's state. - * @return True if the lock was successfully acquired, false otherwise. - */ -bool proc_acquire(struct proc *proc, uint64_t expected); - -/** - * @brief Release the lock on a process. - * - * The process's lock is embedded in its state. This function sets the LSB of - * the state to 0 to unlock the process. - * - * @param proc Pointer to the process to release the lock for. - */ -void proc_release(struct proc *proc); - -/** - * Set the process to a suspended state without locking it. The process may - * still be running, but it will not resume after its timeslice has ended. - * - * @param proc Pointer to process to suspend. - */ -void proc_suspend(struct proc *proc); - -/** - * Resumes a process from its suspend state without locking it. - * - * @param proc Pointer to process to be resumed. - */ -void proc_resume(struct proc *proc); - -/** - * The process is set to wait on a channel atomically if it is not ordered to - * suspend. After begin set to wait, schedule_next() should be called. If - * ordered to suspend, schedule_yield() should be called. - */ -bool proc_ipc_wait(struct proc *proc, uint64_t channel_id); - -/** - * The process is waiting for an IPC send, the channel it is waiting on is - * included in its state. This function will atomically acquire the process - * if its state is waiting on the provided channel id. The processes is - * released with proc_ipc_release(). - */ -bool proc_ipc_acquire(struct proc *proc, uint64_t channel_id); - -/** - * @brief Loads the PMP settings of the process to the hardware. - * - * This function loads the PMP settings of the process to the hardware. The PMP - * settings specify the memory regions that the process can access. This - * function loads the PMP settings to the hardware so that the hardware enforces - * the process's memory access permissions. - * - * @param proc Pointer to the process for which we load PMP settings. - */ -void proc_load_pmp(const struct proc *proc); - -#endif /* __PROC_H__ */ diff --git a/inc/schedule.h b/inc/schedule.h deleted file mode 100644 index e0086bdb..00000000 --- a/inc/schedule.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * @file sched.h - * @brief Scheduler. - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - * @bug QEMU mret does not work properly if all pmp registers are 0, so we have - * a temporary fix in sched_next. - */ -#ifndef __SCHED_H__ -#define __SCHED_H__ - -#include "common.h" -#include "proc.h" - -#include - -struct sched_entry { - uint8_t pid; - uint8_t len; -}; - -/** - * @brief Get the scheduling entry. - * - * Get the schedule entry of the hartid, position i. - * - * @param hartid The hartid for of the schedule entry. - * @param i Index of the schedule entry. - * @return Schedule entry (process ID and length of time slice). - */ -struct sched_entry schedule_get(uint64_t hartid, size_t i); - -/** - * @brief Initialize the scheduler. - * - * This function initializes the scheduler, which is responsible for managing - * the execution of processes on the system. It sets up the necessary data - * structures and configurations to support scheduling. - */ -void schedule_init(void); - -/** - * @brief Yield the current time slice for a given process. - * - * This function yields the current time slice for a given process. It marks - * the process as being suspended and schedules the next available process - * to run. When the yielded process is scheduled again, it will resume - * execution from where it left off. - * - * @param proc Pointer to the process for which the time slice should be - * yielded. - */ -void schedule_yield(struct proc *proc); - -/** - * @brief Find the next process to schedule. - * - * This function finds the next process to schedule based on the current - * state of the system. - */ -void schedule_next(void); - -/// Delete scheduling at hartid, begin-end. -void schedule_delete(uint64_t hartid, uint64_t begin, uint64_t end); -/// Let pid run on hartid, begin-end. -void schedule_update(uint64_t hartid, uint64_t pid, uint64_t begin, - uint64_t end); - -#endif /* __SCHED_H__ */ diff --git a/inc/stack.h b/inc/stack.h deleted file mode 100644 index 37095f0e..00000000 --- a/inc/stack.h +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @file stack.h - * @brief Kernel stack definition. - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - */ -#ifndef __STACK_H__ -#define __STACK_H__ -#include "platform.h" - -/// Stack size of 1 KiB -#define LOG_STACK_SIZE 12 -#define STACK_SIZE (1ull << LOG_STACK_SIZE) - -#endif /* __STACK_H__ */ diff --git a/inc/syscall.h b/inc/syscall.h deleted file mode 100644 index ba5bf8c1..00000000 --- a/inc/syscall.h +++ /dev/null @@ -1,109 +0,0 @@ -/** - * @file stack.h - * @brief Definition of system calls and error codes. - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - */ -#ifndef __SYSCALL_H__ -#define __SYSCALL_H__ - -#include "proc.h" - -#include - -/// @defgroup syscall-handle System Call Handling -/// @{ - -/// System call exceptions -enum excpt { - EXCPT_NONE, ///< No exception. - EXCPT_EMPTY, ///< Capability slot is empty. - EXCPT_COLLISION, ///< Capability slot is occupied. - EXCPT_DERIVATION, ///< Capability can not be derived. - EXCPT_PREEMPTED, ///< System call was preempted. - EXCPT_SUSPENDED, ///< Process was suspended - EXCPT_MPID, ///< Bad PID for monitor operation. - EXCPT_MBUSY, ///< Process busy. - EXCPT_INVALID_CAP, ///< Capability used for the system call is invalid. - EXCPT_NO_RECEIVER, ///< No receiver for the send call. - EXCPT_SEND_CAP, ///< Something stops sending of capability. - EXCPT_UNIMPLEMENTED ///< System call not implemented for specified - ///< capability. -}; - -enum syscall_nr { - // Capabilityless syscalls - SYSCALL_PROC, ///< Process local system call. - // Capability syscalls - SYSCALL_GETCAP, ///< Get capability description - SYSCALL_MOVCAP, ///< Move capability - SYSCALL_DELCAP, ///< Delete capability - SYSCALL_REVCAP, ///< Revoke capability - SYSCALL_DRVCAP, ///< Derive capability - // Monitor syscalls - SYSCALL_MSUSPEND, ///< Monitor suspend process - SYSCALL_MRESUME, ///< Monitor resume process - SYSCALL_MGETREG, ///< Monitor get register value - SYSCALL_MSETREG, ///< Monitor set register value - SYSCALL_MGETCAP, ///< Monitor get capability description - SYSCALL_MTAKECAP, ///< Monitor take capability - SYSCALL_MGIVECAP, ///< Monitor give capability - // IPC syscalls - SYSCALL_RECV, ///< Receive message/capability - SYSCALL_SEND, ///< Send message/capability - SYSCALL_SENDRECV, ///< Send then receive message/capability -}; - -void syscall_lock(void); -void syscall_unlock(void); - -// Simple system calls -/// Process local system call. -void syscall_proc(struct proc *proc, uint64_t a0, uint64_t a1, uint64_t a2); - -// Capability system calls -/// Get capability description. -void syscall_getcap(struct proc *proc, uint64_t i); -/// Get move capability. -void syscall_movcap(struct proc *proc, uint64_t i, uint64_t j); -/// Delete capability. -void syscall_delcap(struct proc *proc, uint64_t i); -/// Revoke capability. -void syscall_revcap(struct proc *proc, uint64_t i); -/// Derive capability. -void syscall_drvcap(struct proc *proc, uint64_t i, uint64_t j, union cap cap); - -// Monitor capability system calls -/// Suspend a process -void syscall_msuspend(struct proc *proc, uint64_t mon, uint64_t pid); -/// Resume a process -void syscall_mresume(struct proc *proc, uint64_t mon, uint64_t pid); -/// Get a register of a process -void syscall_mgetreg(struct proc *proc, uint64_t mon, uint64_t pid, - uint64_t reg); -/// Set a register of a process -void syscall_msetreg(struct proc *proc, uint64_t mon, uint64_t pid, - uint64_t reg, uint64_t val); -/// Read a capability of a process -void syscall_mgetcap(struct proc *proc, uint64_t mon, uint64_t pid, uint64_t i); -/// Take a capability from a process -void syscall_mtakecap(struct proc *proc, uint64_t mon, uint64_t pid, uint64_t i, - uint64_t j); -/// Give a capability to a process -void syscall_mgivecap(struct proc *proc, uint64_t mon, uint64_t pid, uint64_t i, - uint64_t j); - -// IPC capability system calls -/// Receive a message. -void syscall_recv(struct proc *proc, uint64_t recv_idx, uint64_t dest_cap); -/// Send a message. -void syscall_send(struct proc *proc, uint64_t send_idx, uint64_t msg0, - uint64_t msg1, uint64_t msg2, uint64_t msg3, uint64_t src_cap, - uint64_t yield); -/// Send then receive a message. -void syscall_sendrecv(struct proc *proc, uint64_t sendrecv_idx, uint64_t msg0, - uint64_t msg1, uint64_t msg2, uint64_t msg3, - uint64_t src_cap, uint64_t dest_cap); -/// @} - -#endif /* __SYSCALL_H__ */ diff --git a/inc/ticket_lock.h b/inc/ticket_lock.h deleted file mode 100644 index 0c7b5158..00000000 --- a/inc/ticket_lock.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @file lock.h - * - * This header file contains the declarations for the ticket lock - * synchronization primitive, which allows multiple threads to acquire - * a lock in a fair and orderly manner. The ticket lock works by assigning - * each thread a unique ticket number, and then serving threads in the order - * of their ticket numbers. This ensures that threads acquire the lock in - * the order that they request it, preventing starvation and ensuring - * fairness. - * - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - */ -#ifndef __LOCK_H__ -#define __LOCK_H__ - -#include - -struct ticket_lock { - /// next ticket number to be issued. - int next_ticket; - /// ticket number currently being served. - volatile int serving_ticket; -}; - -/** - * Acquire a ticket lock. - * - * @param lock Pointer to the ticket lock to acquire. - */ -void tl_acq(struct ticket_lock *lock); - -/** - * Release a ticket lock. - * - * @param lock Pointer to the ticket lock to release. - */ -void tl_rel(struct ticket_lock *lock); - -#endif /* __LOCK_H__ */ diff --git a/inc/timer.h b/inc/timer.h deleted file mode 100644 index 492a62ef..00000000 --- a/inc/timer.h +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @file platform.h - * @brief Interact with platform - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - */ -#ifndef __TIMER_H__ -#define __TIMER_H__ - -#include - -uint64_t time_get(void); -void time_set(uint64_t time); -uint64_t timeout_get(uint64_t i); -void timeout_set(uint64_t i, uint64_t val); - -#endif /* __TIMER_H__ */ diff --git a/kernel/Makefile b/kernel/Makefile new file mode 100644 index 00000000..cae3d6f2 --- /dev/null +++ b/kernel/Makefile @@ -0,0 +1,98 @@ +# See LICENSE file for copyright and license details. +.POSIX: + +# Program name +BUILD?=build +PROGRAM?=s3k + +# Root of repository +ROOT=.. + +# Kernel configuration +CONFIG_H?=${ROOT}/config.def.h + +# Platform +PLATFORM?=${ROOT}/plat/virt + +# Common sources +COMMON=${ROOT}/common + +# Get toolchain +include ${ROOT}/tools.mk + +# Platform specific config +include ${PLATFORM}/platform.mk + +# Search path +vpath %.c src ${COMMON}/src +vpath %.S src + +# Source files +SRCS =head.S trap.S stack.S +SRCS+=cap_table.c cap_types.c cap_ops.c cap_pmp.c cap_monitor.c cap_ipc.c \ + csr.c exception.c kernel.c proc.c sched.c mcslock.c syscall.c +SRCS+=${DRIVERS} +OBJS=${patsubst %, ${BUILD}/${PROGRAM}/%.o, ${SRCS}} +DEPS=${patsubst %, ${BUILD}/${PROGRAM}/%.d, ${SRCS}} + +# Compilation flags +CFLAGS =-march=${ARCH} -mabi=${ABI} -mcmodel=${CMODEL} +CFLAGS+=-std=c11 +CFLAGS+=-Wall -Wextra -Werror +CFLAGS+=-Wno-unused-parameter +CFLAGS+=-Wshadow -fno-common +CFLAGS+=-Wno-builtin-declaration-mismatch +CFLAGS+=-Os -g3 +CFLAGS+=-nostartfiles -ffreestanding +CFLAGS+=-fno-stack-protector +CFLAGS+=-Wl,--gc-sections +CFLAGS+=-flto -fwhole-program +CFLAGS+=--specs=nosys.specs +CFLAGS+=-ffunction-sections -fdata-sections +CFLAGS+=-T${PLATFORM}/platform.ld -Tlinker.ld +CFLAGS+=-Wl,--no-warn-rwx-segments + +INC+=-include ${PLATFORM}/config.h +INC+=-include ${CONFIG_H} +INC+=-Iinc -I${COMMON}/inc + +ELF=${BUILD}/${PROGRAM}.elf +BIN=${ELF:.elf=.bin} +DEP=${ELF:.elf=.d} +DA=${ELF:.elf=.da} + +all: ${ELF} ${BIN} ${DA} + +elf: ${ELF} +bin: ${BIN} +da: ${DA} + +${BUILD}/${PROGRAM}/%.S.o: %.S + @mkdir -p ${@D} + @echo "CC $@" + @${CC} ${CFLAGS} ${INC} -MMD -c -o $@ $< + +${BUILD}/${PROGRAM}/%.c.o: %.c + @mkdir -p ${@D} + @echo "CC $@" + @${CC} ${CFLAGS} ${INC} -MMD -c -o $@ $< + +${ELF}: ${OBJS} + @mkdir -p ${@D} + @echo "CC $@" + @${CC} ${CFLAGS} ${INC} -o $@ ${OBJS} + +${BIN}: ${ELF} + @echo "OBJCOPY $@" + @${OBJCOPY} -O binary $< $@ + +${DA}: ${ELF} + @echo "OBJDUMP $@" + @${OBJDUMP} -D $< > $@ + +clean: + rm -f ${ELF} ${BIN} ${DA} ${OBJS} ${DEPS} + +.PHONY: all elf bin da format clean + +-include ${DEPS} diff --git a/kernel/inc/cap_ipc.h b/kernel/inc/cap_ipc.h new file mode 100644 index 00000000..ff58dfef --- /dev/null +++ b/kernel/inc/cap_ipc.h @@ -0,0 +1,19 @@ +#pragma once + +#include "cap_table.h" +#include "error.h" +#include "proc.h" + +#include + +typedef struct ipc_msg { + uint64_t buf[4]; + cte_t cbuf; + bool send_cap; +} ipc_msg_t; + +err_t cap_sock_send(proc_t *p, cte_t c, ipc_msg_t *msg, reg_t *yield_to); +err_t cap_sock_call(proc_t *p, cte_t c, ipc_msg_t *msg, reg_t *yield_to); +err_t cap_sock_reply(proc_t *p, cte_t c, ipc_msg_t *msg, reg_t *yield_to); +err_t cap_sock_replyrecv(proc_t *p, cte_t c, ipc_msg_t *msg, reg_t *yield_to); +err_t cap_sock_recv(proc_t *p, cte_t c, ipc_msg_t *msg); diff --git a/kernel/inc/cap_monitor.h b/kernel/inc/cap_monitor.h new file mode 100644 index 00000000..d59d1fbb --- /dev/null +++ b/kernel/inc/cap_monitor.h @@ -0,0 +1,11 @@ +#include "cap_table.h" +#include "error.h" + +err_t cap_monitor_suspend(cte_t mon, uint64_t pid); +err_t cap_monitor_resume(cte_t mon, uint64_t pid); +err_t cap_monitor_reg_read(cte_t mon, uint64_t pid, uint64_t reg, uint64_t *val); +err_t cap_monitor_reg_write(cte_t mon, uint64_t pid, uint64_t reg, uint64_t val); +err_t cap_monitor_cap_read(cte_t mon, cte_t src, cap_t *cap); +err_t cap_monitor_cap_move(cte_t mon, cte_t src, cte_t dst, cap_t *cap); +err_t cap_monitor_pmp_load(cte_t mon, cte_t pmp, uint64_t pmpslot); +err_t cap_monitor_pmp_unload(cte_t mon, cte_t pmp); diff --git a/kernel/inc/cap_ops.h b/kernel/inc/cap_ops.h new file mode 100644 index 00000000..f4b0464c --- /dev/null +++ b/kernel/inc/cap_ops.h @@ -0,0 +1,11 @@ +#pragma once + +#include "cap_table.h" +#include "error.h" + +err_t cap_read(cte_t cte, cap_t *cap); +err_t cap_move(cte_t src, cte_t dst, cap_t *cap); +err_t cap_delete(cte_t cte); +void cap_reclaim(cte_t parent, cap_t parent_cap, cte_t child, cap_t child_cap); +err_t cap_reset(cte_t cte); +err_t cap_derive(cte_t src, cte_t dst, cap_t new_cap); diff --git a/kernel/inc/cap_pmp.h b/kernel/inc/cap_pmp.h new file mode 100644 index 00000000..59dc6271 --- /dev/null +++ b/kernel/inc/cap_pmp.h @@ -0,0 +1,7 @@ +#pragma once + +#include "cap_table.h" +#include "error.h" + +err_t cap_pmp_load(cte_t cidx, uint64_t slot); +err_t cap_pmp_unload(cte_t cidx); diff --git a/kernel/inc/cap_table.h b/kernel/inc/cap_table.h new file mode 100644 index 00000000..ac052196 --- /dev/null +++ b/kernel/inc/cap_table.h @@ -0,0 +1,25 @@ +#pragma once + +#include "cap_types.h" +#include "error.h" +#include "kassert.h" + +#include +#include +#include + +typedef struct cte *cte_t; + +void ctable_init(void); +cte_t ctable_get(uint64_t pid, uint64_t index); +bool cte_is_empty(cte_t c); +void cte_set_next(cte_t c, cte_t next); +void cte_set_prev(cte_t c, cte_t prev); +void cte_set_cap(cte_t c, cap_t cap); +cte_t cte_next(cte_t c); +cte_t cte_prev(cte_t c); +cap_t cte_cap(cte_t c); +uint64_t cte_pid(cte_t c); +void cte_move(cte_t src, cte_t dst, cap_t *cap); +cap_t cte_delete(cte_t c); +void cte_insert(cte_t c, cap_t cap, cte_t prev); diff --git a/kernel/inc/cap_types.h b/kernel/inc/cap_types.h new file mode 100644 index 00000000..fb9408d8 --- /dev/null +++ b/kernel/inc/cap_types.h @@ -0,0 +1,117 @@ +#pragma once + +#include "error.h" +#include "kassert.h" + +#include +#include +#include + +typedef enum { + MEM_NONE = 0, + MEM_R = 1, + MEM_W = 2, + MEM_X = 4, + MEM_RW = MEM_R | MEM_W, + MEM_RX = MEM_R | MEM_X, + MEM_RWX = MEM_R | MEM_W | MEM_X, +} mem_perm_t; + +// IPC Modes +typedef enum { + IPC_NOYIELD = 0, // Non-Yielding Synchronous + IPC_YIELD = 1, // Yielding Synchronous + // IPC_ASYNC = 2, // Asynchronous +} ipc_mode_t; + +// IPC Permissions +typedef enum { + IPC_SDATA = 1, // Server can send data + IPC_SCAP = 2, // Server can send capabilities + IPC_CDATA = 4, // Client can send data + IPC_CCAP = 8, // Client can send capabilities +} ipc_perm_t; + +// Capability types +typedef enum capty { + CAPTY_NONE = 0, ///< No capability. + CAPTY_TIME = 1, ///< Time Slice capability. + CAPTY_MEMORY = 2, ///< Memory Slice capability. + CAPTY_PMP = 3, ///< PMP Frame capability. + CAPTY_MONITOR = 4, ///< Monitor capability. + CAPTY_CHANNEL = 5, ///< IPC Channel capability. + CAPTY_SOCKET = 6, ///< IPC Server/Client capability. +} capty_t; + +typedef struct { + uint64_t type : 4; + uint64_t unused : 4; + uint64_t hart : 8; + uint64_t bgn : 16; + uint64_t mrk : 16; + uint64_t end : 16; +} cap_time_t; + +typedef struct { + uint64_t type : 4; + uint64_t rwx : 3; + uint64_t lck : 1; + uint64_t offset : 8; + uint64_t bgn : 16; + uint64_t mrk : 16; + uint64_t end : 16; +} cap_memory_t; + +typedef struct { + uint64_t type : 4; + uint64_t rwx : 3; + uint64_t used : 1; + uint64_t slot : 8; + uint64_t addr : 48; +} cap_pmp_t; + +typedef struct { + uint64_t type : 4; + uint64_t unused : 12; + uint64_t bgn : 16; + uint64_t mrk : 16; + uint64_t end : 16; +} cap_monitor_t; + +typedef struct { + uint64_t type : 4; + uint64_t unused : 12; + uint64_t bgn : 16; + uint64_t mrk : 16; + uint64_t end : 16; +} cap_channel_t; + +typedef struct { + uint64_t type : 4; + uint64_t mode : 4; + uint64_t perm : 8; + uint64_t chan : 16; + uint64_t tag : 32; +} cap_socket_t; + +/// Capability description +typedef union cap { + uint64_t type : 4; + uint64_t raw; + cap_time_t time; + cap_memory_t mem; + cap_pmp_t pmp; + cap_monitor_t mon; + cap_channel_t chan; + cap_socket_t sock; +} cap_t; + +cap_t cap_mk_time(uint64_t hart, uint64_t bgn, uint64_t end); +cap_t cap_mk_memory(uint64_t bgn, uint64_t end, uint64_t rwx); +cap_t cap_mk_pmp(uint64_t addr, uint64_t rwx); +cap_t cap_mk_monitor(uint64_t bgn, uint64_t end); +cap_t cap_mk_channel(uint64_t bgn, uint64_t end); +cap_t cap_mk_socket(uint64_t chan, uint64_t mode, uint64_t perm, uint64_t tag); + +bool cap_revokable(cap_t p, cap_t c); +bool cap_derivable(cap_t p, cap_t c); diff --git a/kernel/inc/csr.h b/kernel/inc/csr.h new file mode 100644 index 00000000..0b95af9a --- /dev/null +++ b/kernel/inc/csr.h @@ -0,0 +1,46 @@ +/** + * @file csr.h + * @brief Interact with control and status registers. + * @copyright MIT License + * @author Henrik Karlsson (henrik10@kth.se) + */ +#pragma once + +/* Machine CSR constants */ +#define MIP_MSIP 0x8 +#define MIE_MSIE 0x8 +#define MIP_MTIP 0x80 +#define MIE_MTIE 0x80 +#define MCAUSE_USER_ECALL 0x8 +#define MSTATUS_MIE 0x8 + +#ifndef __ASSEMBLER__ +#include +uint64_t csrr_mhartid(void); +uint64_t csrr_mip(void); +uint64_t csrr_mcycle(void); +uint64_t csrr_mhpmcounter3(void); +void csrw_mcycle(uint64_t val); +void csrw_mhpmcounter3(uint64_t val); +void csrw_mstatus(uint64_t val); +void csrs_mstatus(uint64_t val); +void csrc_mstatus(uint64_t val); +uint64_t csrr_pmpcfg0(void); +uint64_t csrr_pmpaddr0(void); +uint64_t csrr_pmpaddr1(void); +uint64_t csrr_pmpaddr2(void); +uint64_t csrr_pmpaddr3(void); +uint64_t csrr_pmpaddr4(void); +uint64_t csrr_pmpaddr5(void); +uint64_t csrr_pmpaddr6(void); +uint64_t csrr_pmpaddr7(void); +void csrw_pmpcfg0(uint64_t val); +void csrw_pmpaddr0(uint64_t val); +void csrw_pmpaddr1(uint64_t val); +void csrw_pmpaddr2(uint64_t val); +void csrw_pmpaddr3(uint64_t val); +void csrw_pmpaddr4(uint64_t val); +void csrw_pmpaddr5(uint64_t val); +void csrw_pmpaddr6(uint64_t val); +void csrw_pmpaddr7(uint64_t val); +#endif diff --git a/kernel/inc/error.h b/kernel/inc/error.h new file mode 100644 index 00000000..e212591c --- /dev/null +++ b/kernel/inc/error.h @@ -0,0 +1,27 @@ +#pragma once + +typedef enum { + SUCCESS = 0, + ERR_EMPTY, + ERR_SRC_EMPTY, + ERR_DST_OCCUPIED, + ERR_INVALID_CLIENT, + ERR_INVALID_DERIVATION, + ERR_INVALID_INDEX, + ERR_INVALID_MONITOR, + ERR_INVALID_PID, + ERR_INVALID_PMP, + ERR_INVALID_SERVER, + ERR_INVALID_SLOT, + ERR_INVALID_STATE, + ERR_INVALID_SYSCALL, + ERR_NO_RECEIVER, + ERR_PREEMPTED, + ERR_TIMEOUT, + ERR_SUSPENDED, + // Special values. + JUSTRET, + SYNC, + IPC_SYNC, + YIELD, +} err_t; diff --git a/inc/exception.h b/kernel/inc/exception.h similarity index 73% rename from inc/exception.h rename to kernel/inc/exception.h index efbff983..1670dac0 100644 --- a/inc/exception.h +++ b/kernel/inc/exception.h @@ -1,3 +1,4 @@ +#pragma once /** * @file exception.h * @brief User exception handler @@ -5,9 +6,6 @@ * @author Henrik Karlsson (henrik10@kth.se) */ -#ifndef __EXCEPTION_H__ -#define __EXCEPTION_H__ - #include "proc.h" #include @@ -21,12 +19,8 @@ * handle_ret() function. Otherwise, it calls the handle_default() function * to handle the exception. * - * @param proc Pointer to the process that encountered the exception * @param mcause The value of the mcause register * @param mepc The value of the mepc register * @param mtval The value of the mtval register */ -void handle_exception(struct proc *proc, uint64_t mcause, uint64_t mepc, - uint64_t mtval); - -#endif /* __EXCEPTION_H__ */ +proc_t *handle_exception(proc_t *p, reg_t mcause, reg_t mepc, reg_t mtval); diff --git a/inc/init.h b/kernel/inc/init.h similarity index 91% rename from inc/init.h rename to kernel/inc/init.h index 15d60288..fcea1f9b 100644 --- a/inc/init.h +++ b/kernel/inc/init.h @@ -1,3 +1,4 @@ +#pragma once /** * @file init.h * @brief Declares the initialization function for the kernel. @@ -10,10 +11,7 @@ * @copyright MIT License * @author Henrik Karlsson (henrik10@kth.se) */ -#ifndef __INIT_H__ -#define __INIT_H__ - -#include "proc.h" +#include /** * @brief Initializes the kernel with the given payload, which is a pointer to @@ -26,5 +24,3 @@ * @param payload A pointer to the boot process's code. */ void init_kernel(uint64_t payload); - -#endif /* __INIT_H__ */ diff --git a/kernel/inc/kassert.h b/kernel/inc/kassert.h new file mode 100644 index 00000000..e9fabe40 --- /dev/null +++ b/kernel/inc/kassert.h @@ -0,0 +1,43 @@ +#pragma once +#include "drivers/uart.h" + +/** + * @file kassert.h + * + * @brief Assertion support for the kernel. + * + * This header provides support for assertions in the kernel. When the NDEBUG + * symbol is not defined, the `kassert()` macro can be used to check whether a + * condition is true. If the condition is false, the `kassert_failure()` + * function will be called to report the failure. If NDEBUG is defined, then + * if the condition is false, we get a __builtin_unreachable, hinting to the + * compiler that the condition is assumed to be true. + * + * @copyright MIT License + */ +#ifndef NDEBUG + +#define _X_(x) #x +#define KASSERT_FAILURE(FILE, LINE) uart_puts("Kernel assertion failed at " FILE ":" _X_(LINE) "."); + +#define KASSERT(EXPR) \ + do { \ + if (!(EXPR)) { \ + KASSERT_FAILURE(__FILE__, __LINE__); \ + while (1) \ + ; \ + } \ + } while (false) + +#else /* NDEBUG */ + +#define KASSERT(expr) \ + do { \ + if (!(expr)) { \ + __builtin_unreachable(); \ + while (1) \ + ; \ + } \ + } while (false) + +#endif /* NDEBUG */ diff --git a/kernel/inc/kernel.h b/kernel/inc/kernel.h new file mode 100644 index 00000000..68ac36f4 --- /dev/null +++ b/kernel/inc/kernel.h @@ -0,0 +1,10 @@ +#pragma once + +#include "mcslock.h" + +#include +#include + +void kernel_init(void); +bool kernel_lock(void); +void kernel_unlock(void); diff --git a/inc/common.h b/kernel/inc/macro.h similarity index 85% rename from inc/common.h rename to kernel/inc/macro.h index 60bdebd2..a1c2be1c 100644 --- a/inc/common.h +++ b/kernel/inc/macro.h @@ -1,13 +1,11 @@ +#pragma once /** - * @file common.h + * @file macro.h * @brief Macros. * @copyright MIT License * @author Henrik Karlsson (henrik10@kth.se) */ -#ifndef __COMMON_H__ -#define __COMMON_H__ - #define _STR_(x) #x #define STR(x) _STR_(x) @@ -21,5 +19,3 @@ /* Returns the size of an array */ #define ARRAY_SIZE(x) (sizeof(x) / (sizeof((x)[0]))) - -#endif /* __COMMON_H__ */ diff --git a/kernel/inc/macro.inc b/kernel/inc/macro.inc new file mode 100644 index 00000000..4d347809 --- /dev/null +++ b/kernel/inc/macro.inc @@ -0,0 +1,19 @@ +#pragma once + +// See LICENSE file for copyright and license details. +.macro load_sp tmp + la sp,_sp + csrr \tmp,mhartid +#if MIN_HARTID != 0 + addi \tmp,\tmp,-MIN_HARTID +#endif + slli \tmp,\tmp,LOG_STACK_SIZE + sub sp,sp,\tmp +.endm + +.macro load_gp +.option push +.option norelax + la gp, __global_pointer$ +.option pop +.endm diff --git a/kernel/inc/mcslock.h b/kernel/inc/mcslock.h new file mode 100644 index 00000000..44ebc29d --- /dev/null +++ b/kernel/inc/mcslock.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +typedef struct qnode { + struct qnode *prev, *next; +} qnode_t; + +typedef struct mcslock { + struct qnode tail; + struct qnode *nodes; +} mcslock_t; + +void mcslock_init(mcslock_t *lock, qnode_t *nodes); +bool mcslock_try_acquire(mcslock_t *lock, uint64_t me); +void mcslock_acquire(mcslock_t *lock, uint64_t me); +void mcslock_release(mcslock_t *lock, uint64_t me); diff --git a/kernel/inc/offsets.h b/kernel/inc/offsets.h new file mode 100644 index 00000000..ba1a0d3b --- /dev/null +++ b/kernel/inc/offsets.h @@ -0,0 +1,41 @@ +#pragma once + +/* Register offsets */ +#define TF_PC 0 +#define TF_RA 8 +#define TF_SP 16 +#define TF_GP 24 +#define TF_TP 32 +#define TF_T0 40 +#define TF_T1 48 +#define TF_T2 56 +#define TF_S0 64 +#define TF_S1 72 +#define TF_A0 80 +#define TF_A1 88 +#define TF_A2 96 +#define TF_A3 104 +#define TF_A4 112 +#define TF_A5 120 +#define TF_A6 128 +#define TF_A7 136 +#define TF_S2 144 +#define TF_S3 152 +#define TF_S4 160 +#define TF_S5 168 +#define TF_S6 176 +#define TF_S7 184 +#define TF_S8 196 +#define TF_S9 204 +#define TF_S10 212 +#define TF_S11 220 +#define TF_T3 228 +#define TF_T4 236 +#define TF_T5 244 +#define TF_T6 256 +#define TF_TPC 264 +#define TF_TSP 272 +#define TF_EPC 280 +#define TF_ESP 288 +#define TF_ECAUSE 296 +#define TF_EVAL 304 diff --git a/kernel/inc/pmp.h b/kernel/inc/pmp.h new file mode 100644 index 00000000..f069e88a --- /dev/null +++ b/kernel/inc/pmp.h @@ -0,0 +1,16 @@ +#pragma once + +#include "proc.h" + +#include + +static inline void pmp_napot_decode(uint64_t addr, uint64_t *base, uint64_t *size) +{ + *base = ((addr + 1) & addr) << 2; + *size = (((addr + 1) ^ addr) + 1) << 2; +} + +static inline uint64_t pmp_napot_encode(uint64_t base, uint64_t size) +{ + return (base | (size / 2 - 1)) >> 2; +} diff --git a/kernel/inc/preempt.h b/kernel/inc/preempt.h new file mode 100644 index 00000000..072fbf21 --- /dev/null +++ b/kernel/inc/preempt.h @@ -0,0 +1,19 @@ +#pragma once +#include "csr.h" + +#include + +static inline bool preempt(void) +{ + return csrr_mip() & MIP_MTIP; +} + +static inline void preempt_enable(void) +{ + csrs_mstatus(MSTATUS_MIE); +} + +static inline void preempt_disable(void) +{ + csrc_mstatus(MSTATUS_MIE); +} diff --git a/kernel/inc/proc.h b/kernel/inc/proc.h new file mode 100644 index 00000000..c3f2005d --- /dev/null +++ b/kernel/inc/proc.h @@ -0,0 +1,129 @@ +#pragma once +/** + * @file proc.h + * @brief Defines the process control block and its associated functions. + * + * This file contains the definition of the `proc_t` data structure, which + * represents a process control block (PCB) in the operating system. It also + * contains the declarations of functions for manipulating the PCB. + * + * @copyright MIT License + */ + +#include "types.h" + +#include +#include + +/** Process state flags + * PSF_BUSY: Process has been acquired. + * PSF_BLOCKED: Waiting for IPC. + * PSF_SUSPENDED: Waiting for monitor + */ +#define PSF_BUSY 1 +#define PSF_BLOCKED 2 +#define PSF_SUSPENDED 4 + +#define N_REG (sizeof(trap_frame_t) / sizeof(uint64_t)) + +typedef struct { + reg_t pc, ra, sp, gp, tp; + reg_t t0, t1, t2; + reg_t s0, s1; + reg_t a0, a1, a2, a3, a4, a5, a6, a7; + reg_t s2, s3, s4, s5, s6, s7, s8, s9, s10, s11; + reg_t t3, t4, t5, t6; + reg_t tpc, tsp; + reg_t epc, esp, ecause, eval; +} trap_frame_t; + +/** + * @brief Process control block. + * + * Contains all information needed manage a process except the capabilities. + */ +typedef struct { + /** The registers of the process (RISC-V registers and virtual + * registers). */ + trap_frame_t tf; + /** PMP registers */ + uint8_t pmpcfg[N_PMP]; + uint64_t pmpaddr[N_PMP]; + /** Instrumentation registers */ + uint64_t instrument_wcet; + /** Process ID. */ + uint64_t pid; + /** Process state. */ + uint64_t state; + /** Sleep until. */ + uint64_t sleep; + /** timeout */ + uint64_t timeout; +} proc_t; + +/** + * Initializes all processes in the system. + * + * @param payload A pointer to the boot loader's code. + * + * @note This function should be called only once during system startup. + */ +void proc_init(void); + +/** + * @brief Gets the process corresponding to a given process ID. + * + * @param pid The process ID to look for. + * @return A pointer to the process corresponding to the given PID. + */ +proc_t *proc_get(uint64_t pid); + +/** + * @brief Attempt to acquire the lock for a process. + * + * The process's lock is embedded in its state. This function attempts to + * acquire the lock by atomically setting the LSB of the state to 1 if it + * currently has the value 'expected'. If the lock is already held by another + * process, this function will return false. + * + * @param proc Pointer to the process to acquire the lock for. + * @param expected The expected value of the process's state. + * @return True if the lock was successfully acquired, false otherwise. + */ +bool proc_acquire(proc_t *proc); + +/** + * @brief Release the lock on a process. + * + * The process's lock is embedded in its state. This function sets the LSB of + * the state to 0 to unlock the process. + * + * @param proc Pointer to the process to release the lock for. + */ +void proc_release(proc_t *proc); + +/** + * Set the process to a suspended state without locking it. The process may + * still be running, but it will not resume after its timeslice has ended. + * + * @param proc Pointer to process to suspend. + */ +void proc_suspend(proc_t *proc); + +/** + * Resumes a process from its suspend state without locking it. + * + * @param proc Pointer to process to be resumed. + */ +void proc_resume(proc_t *proc); + + +void proc_ipc_wait(proc_t *proc, uint64_t channel); +bool proc_ipc_acquire(proc_t *proc, uint64_t channel); + +bool proc_is_suspended(proc_t *proc); + +bool proc_pmp_avail(proc_t *proc, uint64_t slot); +void proc_pmp_load(proc_t *proc, uint64_t slot, uint64_t cfg, uint64_t addr); +void proc_pmp_unload(proc_t *proc, uint64_t slot); +void proc_pmp_sync(proc_t *proc); diff --git a/kernel/inc/sched.h b/kernel/inc/sched.h new file mode 100644 index 00000000..273b6c71 --- /dev/null +++ b/kernel/inc/sched.h @@ -0,0 +1,38 @@ +#pragma once +/** + * @file sched.h + * @brief Scheduler. + * @copyright MIT License + * @author Henrik Karlsson (henrik10@kth.se) + * @bug QEMU mret does not work properly if all pmp registers are 0, so we have + * a temporary fix in sched_next. + */ + +#include "macro.h" +#include "proc.h" + +#include +#include + +/** + * @brief Initialize the scheduler. + * + * This function initializes the scheduler, which is responsible for managing + * the execution of processes on the system. It sets up the necessary data + * structures and configurations to support scheduling. + */ +void sched_init(void); + +/** + * @brief Find the next process to schedule. + * + * This function finds the next process to schedule based on the current + * state of the system. + */ +proc_t *sched(proc_t *); + +/// Let pid run on hartid, begin-end. +void sched_update(uint64_t pid, uint64_t end, uint64_t hartid, uint64_t from, uint64_t to); + +/// Delete scheduling at hartid, begin-end. +void sched_delete(uint64_t hartid, uint64_t from, uint64_t to); diff --git a/kernel/inc/semaphore.h b/kernel/inc/semaphore.h new file mode 100644 index 00000000..99c334fb --- /dev/null +++ b/kernel/inc/semaphore.h @@ -0,0 +1,59 @@ +/** + * A counting semaphore based on a ticket lock. + */ +#pragma once +#include + +typedef struct semaphore { + uint64_t released; + uint64_t acquired; +} semaphore_t; + +/** + * Initialize the semaphore with n tickets. + */ +static inline void semaphore_init(semaphore_t *sem, uint64_t tickets) +{ + // Initial number of served tickets. + sem->released = tickets; + // Initiallly, 0 tickets are released. + sem->acquired = 0; +} + +/** + * Acquire n tickets and wait for release. + */ +static inline void semaphore_acquire_n(semaphore_t *sem, uint64_t n) +{ + uint64_t my_ticket; + // Acquire n tickets. + my_ticket = __atomic_add_fetch(&sem->acquired, n, __ATOMIC_RELAXED); + // Wait until the tickets are released. + while (my_ticket > __atomic_load_n(&sem->released, __ATOMIC_ACQUIRE)) + ; +} + +/** + * Release n tickets. + */ +static inline void semaphore_release_n(semaphore_t *sem, uint64_t n) +{ + // Release n tickets. + __atomic_fetch_add(&sem->released, n, __ATOMIC_RELEASE); +} + +/** + * Acquire 1 ticket and wait for release. + */ +static inline void semaphore_acquire(semaphore_t *sem) +{ + semaphore_acquire_n(sem, 1); +} + +/** + * Release 1 ticket. + */ +static inline void semaphore_release(semaphore_t *sem) +{ + semaphore_release_n(sem, 1); +} diff --git a/kernel/inc/syscall.h b/kernel/inc/syscall.h new file mode 100644 index 00000000..a26b60b1 --- /dev/null +++ b/kernel/inc/syscall.h @@ -0,0 +1,45 @@ +#pragma once + +#include "cap_types.h" +#include "proc.h" + +#include + +typedef enum { + // Basic Info & Registers + SYSCALL_GET_INFO, // Retrieve basic system information + SYSCALL_REG_READ, // Set the value of a specific register + SYSCALL_REG_WRITE, // Get the value of a specific register + SYSCALL_SYNC, // Synchronize with capabilities/scheduling + SYSCALL_SYNC_MEM, // Synchronize with capabilities/scheduling + + // Capability Management + SYSCALL_CAP_READ, // Read the properties of a capability + SYSCALL_CAP_MOVE, // Move a capability to a different slot + SYSCALL_CAP_DELETE, // Remove a capability from the system + SYSCALL_CAP_REVOKE, // Revoke a derived capabilities + SYSCALL_CAP_DERIVE, // Derive a new capability from an existing one + + // PMP + SYSCALL_PMP_LOAD, + SYSCALL_PMP_UNLOAD, + + // Monitor + SYSCALL_MONITOR_SUSPEND, + SYSCALL_MONITOR_RESUME, + SYSCALL_MONITOR_REG_READ, + SYSCALL_MONITOR_REG_WRITE, + SYSCALL_MONITOR_CAP_READ, + SYSCALL_MONITOR_CAP_MOVE, + SYSCALL_MONITOR_PMP_LOAD, + SYSCALL_MONITOR_PMP_UNLOAD, + + // Socket + SYSCALL_SOCK_SEND, + SYSCALL_SOCK_CALL, + SYSCALL_SOCK_REPLY, + SYSCALL_SOCK_RECV, + SYSCALL_SOCK_REPLYRECV, +} syscall_t; + +proc_t *handle_syscall(proc_t *p); diff --git a/inc/trap.h b/kernel/inc/trap.h similarity index 57% rename from inc/trap.h rename to kernel/inc/trap.h index 74f5f52d..67c6aac3 100644 --- a/inc/trap.h +++ b/kernel/inc/trap.h @@ -1,14 +1,13 @@ +#pragma once /** * @file trap.h * @brief Declares trap entry/exit functions. * @copyright MIT License * @author Henrik Karlsson (henrik10@kth.se) */ -#ifndef __TRAP_H__ -#define __TRAP_H__ -#include "common.h" +#include void trap_entry(void) __attribute__((noreturn)); void trap_exit(void) __attribute__((noreturn)); - -#endif /* __TRAP_H__ */ +void trap_schedule_exit(void) __attribute__((noreturn)); +void trap_syscall_exit(uint64_t a0) __attribute__((noreturn)) __attribute__((naked)); diff --git a/kernel/inc/types.h b/kernel/inc/types.h new file mode 100644 index 00000000..637ee338 --- /dev/null +++ b/kernel/inc/types.h @@ -0,0 +1,3 @@ +#pragma once + +typedef unsigned long reg_t; diff --git a/inc/wfi.h b/kernel/inc/wfi.h similarity index 62% rename from inc/wfi.h rename to kernel/inc/wfi.h index eaa91583..3880561b 100644 --- a/inc/wfi.h +++ b/kernel/inc/wfi.h @@ -1,10 +1,13 @@ +#pragma once + /** * @file wfi.h * @brief Declares wfi function. * @copyright MIT License * @author Henrik Karlsson (henrik10@kth.se) */ -#ifndef __WFI_H__ -#define __WFI_H__ -void wfi(void); -#endif /* __WFI_H__ */ + +static inline void wfi(void) +{ + __asm__ volatile("wfi"); +} diff --git a/plat/virt/linker.ld b/kernel/linker.ld similarity index 58% rename from plat/virt/linker.ld rename to kernel/linker.ld index 13ddb8b5..ab3f9154 100644 --- a/plat/virt/linker.ld +++ b/kernel/linker.ld @@ -2,27 +2,21 @@ OUTPUT_ARCH(riscv) ENTRY(_start) -CODE_ALIGNMENT = 4; /* Align code on a 4 byte boundary */ -DATA_ALIGNMENT = 8; /* Align data on a 8 byte boundary */ - -_mtime = 0x200bff8; -_mtimecmp = 0x2004000; +_payload = ORIGIN(RAM) + LENGTH(RAM); __global_pointer$ = MIN(_sdata + 0x800, MAX(_data + 0x800, _end - 0x800)); -__payload = ORIGIN(RAM) + LENGTH(RAM); - MEMORY { RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 64K } SECTIONS { - .text : ALIGN(CODE_ALIGNMENT) { + .text : { *( .text.init ) *( .text .text.* ) } > RAM - .data : ALIGN(DATA_ALIGNMENT) { + .data : ALIGN(4K) { _data = . ; *( .data ) *( .data.* ) @@ -31,11 +25,11 @@ SECTIONS { *( .sdata.* ) } > RAM - .bss : ALIGN(DATA_ALIGNMENT) { + .bss : ALIGN(4K) { _bss = .; _sbss = .; *(.sbss .sbss.*) *(.bss .bss.*) - _end = ALIGN(DATA_ALIGNMENT); + _end = ALIGN(8); } > RAM } diff --git a/kernel/src/cap_ipc.c b/kernel/src/cap_ipc.c new file mode 100644 index 00000000..a3187fea --- /dev/null +++ b/kernel/src/cap_ipc.c @@ -0,0 +1,226 @@ +#include "cap_ipc.h" + +#include "cap_ops.h" +#include "cap_table.h" +#include "error.h" +#include "kassert.h" +#include "proc.h" + +typedef struct { + proc_t *serv; + proc_t *clnt; + cte_t serv_dst, clnt_dst; +} chan_info_t; + +static chan_info_t chan_infos[N_CHAN]; + +static err_t validate_client_cap(cap_t cap, ipc_msg_t *msg, bool recv) +{ + if (!cap.type) + return ERR_EMPTY; + if ((cap.type != CAPTY_SOCKET) || (cap.sock.tag == 0)) + return ERR_INVALID_CLIENT; + // If we try to send capability but not allowed. + if (!(cap.sock.perm & IPC_CCAP) && msg->send_cap) + return ERR_INVALID_CLIENT; + // If we do send capability + if ((cap.sock.perm & IPC_CCAP) && msg->send_cap) + return SUCCESS; + // If we must receive capability but has no space + if (recv && (cap.sock.perm & IPC_SCAP) && !cte_is_empty(msg->cbuf)) + return ERR_DST_OCCUPIED; + return SUCCESS; +} + +static err_t validate_server_cap(cap_t cap, ipc_msg_t *msg, bool recv) +{ + if (!cap.type) + return ERR_EMPTY; + if ((cap.type != CAPTY_SOCKET) || (cap.sock.tag != 0)) + return ERR_INVALID_SERVER; + // If we try to send capability but not allowed. + if (!(cap.sock.perm & IPC_SCAP) && msg->send_cap) + return ERR_INVALID_SERVER; + // If we do send capability + if ((cap.sock.perm & IPC_SCAP) && msg->send_cap) + return SUCCESS; + // If we must receive capability but has no space + if (recv && (cap.sock.perm & IPC_CCAP) && !cte_is_empty(msg->cbuf)) + return ERR_DST_OCCUPIED; + return SUCCESS; +} + +static void send_msg(uint64_t *args, cte_t dst, ipc_msg_t *msg, uint64_t tag) +{ + args[0] = tag; + args[1] = msg->buf[0]; + args[2] = msg->buf[1]; + args[3] = msg->buf[2]; + args[4] = msg->buf[3]; + args[5] = 0; + args[6] = (msg->send_cap) ? cap_move(msg->cbuf, dst, (cap_t *)&args[5]) : 0; +} + +err_t cap_sock_send(proc_t *p, cte_t c, ipc_msg_t *msg, reg_t *yield_to) +{ + cap_t cap = cte_cap(c); + + // check capability type + err_t err = validate_client_cap(cap, msg, false); + if (err) + return err; + + if (proc_is_suspended(p)) + return ERR_SUSPENDED; + + chan_info_t *ci = &chan_infos[cap.sock.chan]; + + proc_t *recv = ci->serv; + cte_t dst = ci->serv_dst; + if (!recv || proc_ipc_acquire(recv, cap.sock.chan)) + return ERR_NO_RECEIVER; + + // check capability sending + recv->tf.t0 = SUCCESS; + send_msg(&recv->tf.a0, dst, msg, cap.sock.tag); + + switch (cap.sock.mode) { + case IPC_YIELD: + proc_release(p); + *yield_to = recv->pid; + return YIELD; + case IPC_NOYIELD: + proc_release(recv); + return SUCCESS; + default: + KASSERT(0); + } +} + +err_t cap_sock_call(proc_t *p, cte_t c, ipc_msg_t *msg, reg_t *yield_to) +{ + cap_t cap = cte_cap(c); + + // check capability type + err_t err = validate_client_cap(cap, msg, true); + if (err) + return err; + + if (proc_is_suspended(p)) + return ERR_SUSPENDED; + + chan_info_t *ci = &chan_infos[cap.sock.chan]; + + proc_t *recv = ci->serv; + cte_t dst = ci->serv_dst; + if (!recv || proc_ipc_acquire(recv, cap.sock.chan)) + return ERR_NO_RECEIVER; + + // check capability sending + send_msg(&recv->tf.a0, dst, msg, cap.sock.tag); + + proc_ipc_wait(p, cap.sock.chan); + ci->clnt = p; + ci->clnt_dst = (cap.sock.perm & IPC_SCAP) ? msg->cbuf : NULL; + + switch (cap.sock.mode) { + case IPC_YIELD: + *yield_to = recv->pid; + return YIELD; + case IPC_NOYIELD: + proc_release(recv); + return SYNC; + default: + KASSERT(0); + } +} + +err_t cap_sock_reply(proc_t *p, cte_t c, ipc_msg_t *msg, reg_t *yield_to) +{ + cap_t cap = cte_cap(c); + + // check capability type + err_t err = validate_server_cap(cap, msg, false); + if (err) + return err; + + if (proc_is_suspended(p)) + return ERR_SUSPENDED; + + chan_info_t *ci = &chan_infos[cap.sock.chan]; + + proc_t *recv = ci->clnt; + if (!recv || proc_ipc_acquire(recv, cap.sock.chan)) + return ERR_NO_RECEIVER; + + // check capability sending + send_msg(&recv->tf.a0, ci->clnt_dst, msg, 0); + + switch (cap.sock.mode) { + case IPC_YIELD: + proc_release(p); + *yield_to = recv->pid; + return YIELD; + case IPC_NOYIELD: + proc_release(recv); + return SUCCESS; + default: + KASSERT(0); + } +} + +err_t cap_sock_recv(proc_t *p, cte_t c, ipc_msg_t *msg) +{ + cap_t cap = cte_cap(c); + + // check capability + err_t err = validate_server_cap(cap, msg, true); + if (err) + return err; + + chan_info_t *ci = &chan_infos[cap.sock.chan]; + + if (proc_is_suspended(p)) + return ERR_SUSPENDED; + + proc_ipc_wait(p, cap.sock.chan); + + ci->serv = p; + ci->serv_dst = msg->cbuf; + return SYNC; +} + +err_t cap_sock_replyrecv(proc_t *p, cte_t c, ipc_msg_t *msg, reg_t *yield_to) +{ + cap_t cap = cte_cap(c); + + // check capability type + err_t err = validate_server_cap(cap, msg, false); + if (err) + return err; + + if (proc_is_suspended(p)) + return ERR_SUSPENDED; + + chan_info_t *ci = &chan_infos[cap.sock.chan]; + + proc_t *recv = ci->clnt; + if (!recv || proc_ipc_acquire(recv, cap.sock.chan)) + return ERR_NO_RECEIVER; + + // check capability sending + send_msg(&recv->tf.a0, ci->clnt_dst, msg, 0); + + proc_ipc_wait(p, cap.sock.chan); + + switch (cap.sock.mode) { + case IPC_YIELD: + *yield_to = recv->pid; + return YIELD; + case IPC_NOYIELD: + proc_release(recv); + return SYNC; + default: + KASSERT(0); + } +} diff --git a/kernel/src/cap_monitor.c b/kernel/src/cap_monitor.c new file mode 100644 index 00000000..3cbfa574 --- /dev/null +++ b/kernel/src/cap_monitor.c @@ -0,0 +1,96 @@ +#include "cap_monitor.h" + +#include "cap_ops.h" +#include "cap_pmp.h" +#include "proc.h" + +static err_t check_monitor(cte_t mon, uint64_t pid, bool check_suspended) +{ + cap_t mon_cap = cte_cap(mon); + if (mon_cap.type != CAPTY_MONITOR || mon_cap.mon.mrk > pid || pid >= mon_cap.mon.end) + return ERR_INVALID_MONITOR; + if (check_suspended && !proc_is_suspended(proc_get(pid))) + return ERR_INVALID_STATE; + return SUCCESS; +} + +static err_t check_monitor_move(cte_t mon, cte_t src, cte_t dst) +{ + uint64_t mon_pid = cte_pid(mon); + uint64_t src_pid = cte_pid(src); + uint64_t dst_pid = cte_pid(dst); + err_t err; + if (mon_pid != src_pid && (err = check_monitor(mon, src_pid, true))) + return err; + if (mon_pid != dst_pid && (err = check_monitor(mon, dst_pid, true))) + return err; + return SUCCESS; +} + +err_t cap_monitor_suspend(cte_t mon, uint64_t pid) +{ + err_t err = check_monitor(mon, pid, false); + if (!err) + proc_suspend(proc_get(pid)); + return err; +} + +err_t cap_monitor_resume(cte_t mon, uint64_t pid) +{ + err_t err = check_monitor(mon, pid, false); + if (!err) + proc_resume(proc_get(pid)); + return err; +} + +err_t cap_monitor_reg_read(cte_t mon, uint64_t pid, uint64_t reg, uint64_t *val) +{ + err_t err = check_monitor(mon, pid, true); + if (!err) { + uint64_t *regs = (uint64_t *)&(proc_get(pid)->tf); + *val = reg < N_REG ? regs[reg] : 0; + } + return err; +} + +err_t cap_monitor_reg_write(cte_t mon, uint64_t pid, uint64_t reg, uint64_t val) +{ + err_t err = check_monitor(mon, pid, true); + if (!err && reg < N_REG) { + uint64_t *regs = (uint64_t *)&(proc_get(pid)->tf); + regs[reg] = val; + } + return err; +} + +err_t cap_monitor_cap_read(cte_t mon, cte_t src, cap_t *cap) +{ + err_t err = check_monitor(mon, cte_pid(src), true); + if (!err) + err = cap_read(src, cap); + return err; +} + +err_t cap_monitor_cap_move(cte_t mon, cte_t src, cte_t dst, cap_t *cap) +{ + err_t err = check_monitor_move(mon, src, dst); + if (!err) + err = cap_move(src, dst, cap); + return err; +} + +err_t cap_monitor_pmp_load(cte_t mon, cte_t pmp, uint64_t slot) +{ + err_t err = check_monitor(mon, cte_pid(pmp), true); + if (!err) + err = cap_pmp_load(pmp, slot); + return err; +} + +err_t cap_monitor_pmp_unload(cte_t mon, cte_t pmp) +{ + err_t err = check_monitor(mon, cte_pid(pmp), true); + if (!err) + err = cap_pmp_unload(pmp); + return err; +} diff --git a/kernel/src/cap_ops.c b/kernel/src/cap_ops.c new file mode 100644 index 00000000..5167cfea --- /dev/null +++ b/kernel/src/cap_ops.c @@ -0,0 +1,191 @@ +#include "cap_ops.h" + +#include "pmp.h" +#include "sched.h" + +err_t cap_read(cte_t c, cap_t *cap) +{ + *cap = cte_cap(c); + return cap->raw ? SUCCESS : ERR_EMPTY; +} + +static void ipc_move_hook(cte_t src, cte_t dst) +{ + cap_t cap = cte_cap(src); + switch (cap.type) { + case CAPTY_TIME: { + uint64_t pid = cte_pid(dst); + uint64_t end = cap.time.end; + uint64_t hartid = cap.time.hart; + uint64_t from = cap.time.mrk; + uint64_t to = cap.time.end; + sched_update(pid, end, hartid, from, to); + } break; + case CAPTY_PMP: + if (cap.pmp.used) { + proc_pmp_unload(proc_get(cte_pid(src)), cap.pmp.slot); + cap.pmp.used = 0; + cap.pmp.slot = 0; + cte_set_cap(src, cap); + } + break; + } +} + +err_t cap_move(cte_t src, cte_t dst, cap_t *cap) +{ + if (!cte_cap(src).type) + return ERR_SRC_EMPTY; + + if (cte_cap(dst).type) + return ERR_DST_OCCUPIED; + + if (cte_pid(src) != cte_pid(dst)) + ipc_move_hook(src, dst); + cte_move(src, dst, cap); + return SUCCESS; +} + +static void delete_hook(cte_t c, cap_t cap) +{ + // Clean-up resources + switch (cap.type) { + case CAPTY_TIME: { + uint64_t hartid = cap.time.hart; + uint64_t from = cap.time.mrk; + uint64_t end = cap.time.end; + sched_delete(hartid, from, end); + } break; + case CAPTY_PMP: + if (cap.pmp.used) + proc_pmp_unload(proc_get(cte_pid(c)), cap.pmp.slot); + break; + } +} + +err_t cap_delete(cte_t c) +{ + if (!cte_cap(c).type) + return ERR_EMPTY; + delete_hook(c, cte_delete(c)); + return SUCCESS; +} + +void cap_reclaim(cte_t p, cap_t pcap, cte_t c, cap_t ccap) +{ + if ((cte_prev(c) != p) || cte_cap(c).raw == ccap.raw) + return; + + cte_delete(c); + + switch (ccap.type) { + case CAPTY_TIME: { + pcap.time.mrk = ccap.time.mrk; + uint64_t pid = cte_pid(p); + uint64_t end = pcap.time.end; + uint64_t hartid = pcap.time.hart; + uint64_t from = pcap.time.mrk; + uint64_t to = pcap.time.end; + sched_update(pid, end, hartid, from, to); + } break; + case CAPTY_MEMORY: + pcap.mem.mrk = ccap.mem.mrk; + pcap.mem.lck = ccap.mem.lck; + break; + case CAPTY_PMP: + if (ccap.pmp.used) { + proc_pmp_unload(proc_get(cte_pid(c)), ccap.pmp.slot); + } + return; + case CAPTY_MONITOR: + pcap.mon.mrk = ccap.mon.mrk; + break; + case CAPTY_CHANNEL: + pcap.chan.mrk = ccap.chan.mrk; + break; + case CAPTY_SOCKET: + return; + default: + KASSERT(0); + } + + cte_set_cap(p, pcap); + + return; +} + +err_t cap_reset(cte_t c) +{ + if (!cte_cap(c).type) + return ERR_EMPTY; + + cap_t cap = cte_cap(c); + switch (cap.type) { + case CAPTY_TIME: + cap.time.mrk = cap.time.bgn; + break; + case CAPTY_MEMORY: + cap.mem.mrk = cap.mem.bgn; + cap.mem.lck = false; + break; + case CAPTY_MONITOR: + cap.mon.mrk = cap.mon.bgn; + break; + case CAPTY_CHANNEL: + cap.chan.mrk = cap.chan.bgn; + break; + } + + cte_set_cap(c, cap); + + return SUCCESS; +} + +static void derive(cte_t src, cap_t scap, cte_t dst, cap_t ncap) +{ + // Update the original capability + switch (ncap.type) { + case CAPTY_TIME: { + uint64_t pid = cte_pid(dst); + uint64_t end = ncap.time.end; + uint64_t hartid = ncap.time.hart; + uint64_t from = ncap.time.mrk; + uint64_t to = ncap.time.end; + sched_update(pid, end, hartid, from, to); + scap.time.mrk = ncap.time.bgn; + } break; + case CAPTY_MEMORY: + scap.mem.mrk = ncap.mem.bgn; + break; + case CAPTY_PMP: + scap.mem.lck = true; + break; + case CAPTY_MONITOR: + scap.mon.mrk = ncap.mon.bgn; + break; + case CAPTY_CHANNEL: + scap.chan.mrk = ncap.chan.bgn; + break; + case CAPTY_SOCKET: + if (ncap.sock.tag == 0) + scap.chan.mrk = ncap.sock.chan + 1; + break; + } + cte_insert(dst, ncap, src); + cte_set_cap(src, scap); +} + +err_t cap_derive(cte_t src, cte_t dst, cap_t ncap) +{ + if (!cte_cap(src).type) + return ERR_SRC_EMPTY; + + if (cte_cap(dst).type) + return ERR_DST_OCCUPIED; + + cap_t scap = cte_cap(src); + if (!cap_derivable(scap, ncap)) + return ERR_INVALID_DERIVATION; + derive(src, scap, dst, ncap); + return SUCCESS; +} diff --git a/kernel/src/cap_pmp.c b/kernel/src/cap_pmp.c new file mode 100644 index 00000000..b8731706 --- /dev/null +++ b/kernel/src/cap_pmp.c @@ -0,0 +1,38 @@ +#include "cap_table.h" +#include "cap_types.h" +#include "kernel.h" +#include "pmp.h" + +err_t cap_pmp_load(cte_t pmp, uint64_t slot) +{ + proc_t *proc = proc_get(cte_pid(pmp)); + cap_t pmp_cap = cte_cap(pmp); + + if (!pmp_cap.type) + return ERR_EMPTY; + if (pmp_cap.type != CAPTY_PMP || pmp_cap.pmp.used) + return ERR_INVALID_PMP; + if (!proc_pmp_avail(proc, slot)) + return ERR_DST_OCCUPIED; + proc_pmp_load(proc, slot, pmp_cap.pmp.rwx, pmp_cap.pmp.addr); + pmp_cap.pmp.slot = slot; + pmp_cap.pmp.used = 1; + cte_set_cap(pmp, pmp_cap); + return SUCCESS; +} + +err_t cap_pmp_unload(cte_t pmp) +{ + proc_t *proc = proc_get(cte_pid(pmp)); + cap_t pmp_cap = cte_cap(pmp); + + if (!pmp_cap.type) + return ERR_EMPTY; + if (pmp_cap.type != CAPTY_PMP || !pmp_cap.pmp.used) + return ERR_INVALID_PMP; + proc_pmp_unload(proc, pmp_cap.pmp.slot); + pmp_cap.pmp.slot = 0; + pmp_cap.pmp.used = 0; + cte_set_cap(pmp, pmp_cap); + return SUCCESS; +} diff --git a/kernel/src/cap_table.c b/kernel/src/cap_table.c new file mode 100644 index 00000000..310d6417 --- /dev/null +++ b/kernel/src/cap_table.c @@ -0,0 +1,104 @@ +#include "cap_table.h" + +#include "kassert.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +struct cte { + uint32_t prev, next; + cap_t cap; +}; + +static struct cte ctable[N_PROC * N_CAP]; + +static uint32_t offset(cte_t c) +{ + return (uint32_t)(c - ctable); +} + +void ctable_init(void) +{ + const cap_t caps[] = INIT_CAPS; + + cte_t prev = ctable; + for (unsigned int i = 0; i < ARRAY_SIZE(caps); ++i) + cte_insert(&ctable[i], caps[i], prev); +} + +cte_t ctable_get(uint64_t pid, uint64_t index) +{ + if (pid >= N_PROC || index >= N_CAP) + return NULL; + return &ctable[pid * N_CAP + index]; +} + +bool cte_is_empty(cte_t c) +{ + return c->cap.type == 0; +} + +void cte_set_next(cte_t c, cte_t next) +{ + c->next = offset(next); +} + +void cte_set_prev(cte_t c, cte_t prev) +{ + c->prev = offset(prev); +} + +void cte_set_cap(cte_t c, cap_t cap) +{ + c->cap = cap; +} + +cte_t cte_next(cte_t c) +{ + return &ctable[c->next]; +} + +cte_t cte_prev(cte_t c) +{ + return &ctable[c->prev]; +} + +cap_t cte_cap(cte_t c) +{ + return c->cap; +} + +uint64_t cte_pid(cte_t c) +{ + return offset(c) / N_CAP; +} + +void cte_move(cte_t src, cte_t dst, cap_t *cap) +{ + *cap = src->cap; + if (src == dst) + return; + cte_set_cap(src, (cap_t){0}); + cte_set_prev(dst, cte_prev(src)); + cte_set_next(dst, cte_next(src)); + cte_prev(dst)->next = offset(dst); + cte_next(dst)->prev = offset(dst); + cte_set_cap(dst, *cap); +} + +cap_t cte_delete(cte_t c) +{ + cap_t cap = cte_cap(c); + cte_set_cap(c, (cap_t){0}); + cte_set_next(cte_prev(c), cte_next(c)); + cte_set_prev(cte_next(c), cte_prev(c)); + return cap; +} + +void cte_insert(cte_t c, cap_t cap, cte_t prev) +{ + cte_set_prev(c, prev); + cte_set_next(c, cte_next(prev)); + cte_set_next(cte_prev(c), c); + cte_set_prev(cte_next(c), c); + cte_set_cap(c, cap); +} diff --git a/kernel/src/cap_types.c b/kernel/src/cap_types.c new file mode 100644 index 00000000..1fb0d958 --- /dev/null +++ b/kernel/src/cap_types.c @@ -0,0 +1,232 @@ +#include "cap_types.h" + +#include "pmp.h" + +cap_t cap_mk_time(uint64_t hart, uint64_t bgn, uint64_t end) +{ + cap_t cap; + cap.type = CAPTY_TIME; + cap.time.hart = hart; + cap.time.bgn = bgn; + cap.time.mrk = cap.time.bgn; + cap.time.end = end; + return cap; +} + +cap_t cap_mk_memory(uint64_t bgn, uint64_t end, uint64_t rwx) +{ + uint64_t offset = bgn >> 15; + + cap_t cap; + cap.type = CAPTY_MEMORY; + cap.mem.offset = offset; + cap.mem.bgn = (bgn - (offset << 15)); + cap.mem.end = (end - (offset << 15)); + cap.mem.mrk = cap.mem.bgn; + cap.mem.rwx = (rwx & 0x7); + cap.mem.lck = 0; + return cap; +} + +cap_t cap_mk_pmp(uint64_t addr, uint64_t rwx) +{ + cap_t cap; + cap.type = CAPTY_PMP; + cap.pmp.addr = addr & 0xFFFFFFFFFF; + cap.pmp.rwx = rwx & 0x7; + cap.pmp.used = 0; + cap.pmp.slot = 0; + return cap; +} + +cap_t cap_mk_monitor(uint64_t bgn, uint64_t end) +{ + cap_t cap; + cap.type = CAPTY_MONITOR; + cap.mon.bgn = bgn & 0xFFFF; + cap.mon.end = end & 0xFFFF; + cap.mon.mrk = cap.mon.bgn; + return cap; +} + +cap_t cap_mk_channel(uint64_t bgn, uint64_t end) +{ + cap_t cap; + cap.type = CAPTY_CHANNEL; + cap.chan.bgn = bgn & 0xFFFF; + cap.chan.end = end & 0xFFFF; + cap.chan.mrk = cap.chan.bgn; + return cap; +} + +cap_t cap_mk_socket(uint64_t chan, uint64_t mode, uint64_t perm, uint64_t tag) +{ + cap_t cap; + cap.type = CAPTY_SOCKET; + cap.sock.chan = chan & 0xFFFF; + cap.sock.mode = mode & 0xF; + cap.sock.perm = perm & 0xFF; + cap.sock.tag = tag & 0xFFFFFFFF; + return cap; +} + +static inline bool is_range_subset(uint64_t a_bgn, uint64_t a_end, uint64_t b_bgn, uint64_t b_end) +{ + return a_bgn <= b_bgn && b_end <= a_end; +} + +static inline bool is_range_prefix(uint64_t a_bgn, uint64_t a_end, uint64_t b_bgn, uint64_t b_end) +{ + return a_bgn == b_bgn && b_end <= a_end; +} + +static inline bool is_bit_subset(uint64_t a, uint64_t b) +{ + return (a & b) == a; +} + +static bool cap_time_revokable(cap_t p, cap_t c) +{ + KASSERT(p.type == CAPTY_TIME); + return (c.type == CAPTY_TIME) && (p.time.hart == c.time.hart) + && is_range_subset(p.time.bgn, p.time.end, c.time.bgn, c.time.end); +} + +static bool cap_mem_revokable(cap_t p, cap_t c) +{ + KASSERT(p.type == CAPTY_MEMORY); + if (c.type == CAPTY_PMP) { + uint64_t p_off = (uint64_t)p.mem.offset << 27; + uint64_t p_bgn = p_off + ((uint64_t)p.mem.mrk << 12); + uint64_t p_end = p_off + ((uint64_t)p.mem.end << 12); + uint64_t c_bgn, c_end; + pmp_napot_decode(c.pmp.addr, &c_bgn, &c_end); + return is_range_subset(p_bgn, p_end, c_bgn, c_end); + } + return (c.type == CAPTY_MEMORY) && (p.mem.offset == c.mem.offset) + && is_range_subset(p.mem.bgn, p.mem.end, c.mem.bgn, c.mem.end); +} + +static bool cap_mon_revokable(cap_t p, cap_t c) +{ + KASSERT(p.type == CAPTY_MONITOR); + return (c.type == CAPTY_MONITOR) && is_range_subset(p.mon.bgn, p.mon.end, c.mon.bgn, c.mon.end); +} + +static bool cap_chan_revokable(cap_t p, cap_t c) +{ + KASSERT(p.type == CAPTY_CHANNEL); + if (c.type == CAPTY_SOCKET) { + return is_range_subset(p.chan.bgn, p.chan.end, c.sock.chan, c.sock.chan + 1); + } + return (c.type == CAPTY_CHANNEL) && is_range_subset(p.chan.bgn, p.chan.end, c.chan.bgn, c.chan.end); +} + +static bool cap_sock_revokable(cap_t p, cap_t c) +{ + KASSERT(p.type == CAPTY_SOCKET); + return (p.sock.tag == 0) && (c.sock.tag != 0) && (p.sock.chan == c.sock.chan); +} + +bool cap_revokable(cap_t p, cap_t c) +{ + switch (p.type) { + case CAPTY_TIME: + return cap_time_revokable(p, c); + case CAPTY_MEMORY: + return cap_mem_revokable(p, c); + case CAPTY_MONITOR: + return cap_mon_revokable(p, c); + case CAPTY_CHANNEL: + return cap_chan_revokable(p, c); + case CAPTY_SOCKET: + return cap_sock_revokable(p, c); + default: + return false; + } +} + +static bool cap_valid(cap_t c) +{ + switch (c.type) { + case CAPTY_TIME: + return (c.time.bgn == c.time.mrk) && (c.time.bgn < c.time.end); + case CAPTY_MEMORY: + return (c.mem.bgn == c.mem.mrk) && (c.mem.bgn < c.mem.end); + case CAPTY_PMP: + return (c.pmp.used == 0) && (c.pmp.slot == 0); + case CAPTY_MONITOR: + return (c.mon.bgn == c.mon.mrk) && (c.mon.bgn < c.mon.end); + case CAPTY_CHANNEL: + return (c.chan.bgn == c.chan.mrk) && (c.chan.bgn < c.chan.end); + case CAPTY_SOCKET: + return is_bit_subset(c.sock.perm, IPC_SDATA | IPC_CDATA | IPC_SCAP | IPC_CCAP) + && is_bit_subset(c.sock.mode, IPC_YIELD | IPC_NOYIELD); + default: + return false; + } +} + +static bool cap_time_derivable(cap_t p, cap_t c) +{ + KASSERT(p.type == CAPTY_TIME); + return (c.type == CAPTY_TIME) && (p.time.hart == c.time.hart) + && is_range_prefix(p.time.bgn, p.time.end, c.time.bgn, c.time.end); +} + +static bool cap_mem_derivable(cap_t p, cap_t c) +{ + KASSERT(p.type == CAPTY_MEMORY); + if (c.type == CAPTY_PMP) { + uint64_t p_off = (uint64_t)p.mem.offset << 27; + uint64_t p_mark = p_off + ((uint64_t)p.mem.mrk << 12); + uint64_t p_end = p_off + ((uint64_t)p.mem.end << 12); + uint64_t c_bgn, c_end; + pmp_napot_decode(c.pmp.addr, &c_bgn, &c_end); + return is_range_subset(p_mark, p_end, c_bgn, c_end) && is_bit_subset(c.pmp.rwx, p.mem.rwx); + } + return (c.type == CAPTY_MEMORY) && (p.mem.offset == c.mem.offset) + && is_range_subset(p.mem.mrk, p.mem.end, c.mem.bgn, c.mem.end) && is_bit_subset(c.mem.rwx, p.mem.rwx); +} + +static bool cap_mon_derivable(cap_t p, cap_t c) +{ + KASSERT(p.type == CAPTY_MONITOR); + return (c.type == CAPTY_MONITOR) && is_range_subset(p.mon.mrk, p.mon.end, c.mon.bgn, c.mon.end); +} + +static bool cap_chan_derivable(cap_t p, cap_t c) +{ + KASSERT(p.type == CAPTY_CHANNEL); + if (c.type == CAPTY_SOCKET) { + return (p.sock.tag == 0) && is_range_subset(p.chan.mrk, p.chan.end, c.sock.chan, c.sock.chan + 1); + } + return (c.type == CAPTY_CHANNEL) && is_range_subset(p.chan.mrk, p.chan.end, c.chan.bgn, c.chan.end); +} + +static bool cap_sock_derivable(cap_t p, cap_t c) +{ + KASSERT(p.type == CAPTY_SOCKET); + return (c.type == CAPTY_SOCKET) && (p.sock.chan == c.sock.chan) && (p.sock.tag == 0) && (c.sock.tag != 0) + && (p.sock.mode == c.sock.mode) && (p.sock.perm == c.sock.perm); +} + +bool cap_derivable(cap_t p, cap_t c) +{ + if (!cap_valid(c)) + return false; + switch (p.type) { + case CAPTY_TIME: + return cap_time_derivable(p, c); + case CAPTY_MEMORY: + return cap_mem_derivable(p, c); + case CAPTY_MONITOR: + return cap_mon_derivable(p, c); + case CAPTY_CHANNEL: + return cap_chan_derivable(p, c); + case CAPTY_SOCKET: + return cap_sock_derivable(p, c); + default: + return false; + } +} diff --git a/src/csr.c b/kernel/src/csr.c similarity index 75% rename from src/csr.c rename to kernel/src/csr.c index 8988c4a6..c7fd970f 100644 --- a/src/csr.c +++ b/kernel/src/csr.c @@ -14,6 +14,45 @@ uint64_t csrr_mip(void) return val; } +uint64_t csrr_mcycle(void) +{ + uint64_t val; + __asm__ volatile("csrr %0,mcycle" : "=r"(val)); + return val; +} + +void csrw_mcycle(uint64_t val) +{ + __asm__ volatile("csrw mcycle,%0" ::"r"(val)); +} + +uint64_t csrr_mhpmcounter3(void) +{ + uint64_t val; + __asm__ volatile("csrr %0,mhpmcounter3" : "=r"(val)); + return val; +} + +void csrw_mhpmcounter3(uint64_t val) +{ + __asm__ volatile("csrw mhpmcounter3,%0" ::"r"(val)); +} + +void csrw_mstatus(uint64_t val) +{ + __asm__ volatile("csrw mstatus,%0" ::"r"(val)); +} + +void csrs_mstatus(uint64_t val) +{ + __asm__ volatile("csrs mstatus,%0" ::"r"(val)); +} + +void csrc_mstatus(uint64_t val) +{ + __asm__ volatile("csrc mstatus,%0" ::"r"(val)); +} + uint64_t csrr_pmpcfg0(void) { uint64_t val; diff --git a/kernel/src/exception.c b/kernel/src/exception.c new file mode 100644 index 00000000..ffb329c7 --- /dev/null +++ b/kernel/src/exception.c @@ -0,0 +1,58 @@ +/* See LICENSE file for copyright and license details. */ +#include "exception.h" + +#include "kernel.h" +#include "proc.h" + +#define ILLEGAL_INSTRUCTION 0x2 + +#define MRET 0x30200073 +#define SRET 0x10200073 +#define URET 0x00200073 + +static proc_t *handle_ret(proc_t *p); +static proc_t *handle_default(proc_t *p, reg_t mcause, reg_t mepc, reg_t mtval); + +proc_t *handle_exception(proc_t *p, reg_t mcause, reg_t mepc, reg_t mtval) +{ + /* Check if it is a return from exception */ + if (mcause == ILLEGAL_INSTRUCTION && (mtval == MRET || mtval == SRET || mtval == URET)) + // Handle return from exception + return handle_ret(p); + // Handle default exception + return handle_default(p, mcause, mepc, mtval); +} + +/** + * This function restores the program counter and stack pointer to their values + * prior to the exception, and clears the exception cause and exception value + * registers. + */ +proc_t *handle_ret(proc_t *p) +{ + p->tf.pc = p->tf.epc; + p->tf.sp = p->tf.esp; + p->tf.ecause = 0; + p->tf.eval = 0; + p->tf.epc = 0; + p->tf.esp = 0; + return p; +} + +/* + * This function is called when an exception occurs that doesn't fall under the + * category of an illegal instruction return, such as a page fault or a timer + * interrupt. It updates the exception cause, value, program counter, and stack + * pointer in the process's registers, and switches to the trap handler program + * counter and stack pointer. + */ +proc_t *handle_default(proc_t *p, uint64_t mcause, uint64_t mepc, uint64_t mtval) +{ + p->tf.ecause = mcause; + p->tf.eval = mtval; + p->tf.epc = p->tf.pc; + p->tf.esp = p->tf.sp; + p->tf.pc = p->tf.tpc; + p->tf.sp = p->tf.tsp; + return p; +} diff --git a/kernel/src/head.S b/kernel/src/head.S new file mode 100644 index 00000000..935d19af --- /dev/null +++ b/kernel/src/head.S @@ -0,0 +1,84 @@ +// See LICENSE file for copyright and license details. +#include "macro.inc" +#include "offsets.h" +#include "csr.h" + +.extern init_kernel +.extern sched +.extern trap_entry +.extern trap_exit + +.section .text.init,"ax",@progbits + +.globl _start +.type _start, @function +_start: + /* Load gloabl pointer and stack pointer */ + load_gp + load_sp t1 + + /* Set some CSRs to 0 */ + csrw mstatus, 0 + csrw medeleg, 0 + csrw mideleg, 0 + csrw mscratch, 0 + csrw mie, 0 + csrw satp, 0 + + /* Set trap entry. */ + la t0, trap_entry + csrw mtvec, t0 + + /* If hartid != MIN_HARTID, then jump to wait. */ + csrr t0, mhartid + li t1, MIN_HARTID + bne t0, t1, wait + +zero_bss: /* write zeros to the bss section */ + la t0, _bss + la t1, _end + j 2f +1: sb zero, (t0) + addi t0, t0, 1 +2: bne t0, t1, 1b + + call kernel_init + +wake: /* Wake harts MIN_HARTID .. MAX_HARTID. */ + /* Wake using software interrupt */ + la t0, _clint + addi t1, t0, (MAX_HARTID * 4) + addi t0, t0, (MIN_HARTID * 4) + li t2, 1 +1: sw t2, 0(t0) + addi t0, t0, 4 + bne t1, t0, 1b + +wait: /* Wait for initilization to finish. */ + /* Wait on software interrupt */ + csrw mie, MIE_MSIE + wfi + csrr t0, mip + andi t0, t0, MIP_MSIP + beqz t0, wait + + /* Disable software interrupt. */ + csrw mie, 0 + la t0, _clint + csrr t1, mhartid + slli t1, t1, 2 + add t0, t0, t1 + sw x0, (t0) + +head_exit: + csrw mcounteren,0x3 + csrw scounteren,0x3 + csrw mcountinhibit,0x8 + csrw mhpmcounter3,0 + /* Enable timer interrupts */ + li t0, MIE_MTIE + csrw mie,t0 + /* Start user processes. */ + li a0, 0 + la ra, trap_exit + tail sched diff --git a/kernel/src/kernel.c b/kernel/src/kernel.c new file mode 100644 index 00000000..86cdd942 --- /dev/null +++ b/kernel/src/kernel.c @@ -0,0 +1,32 @@ +#include "kernel.h" + +#include "cap_table.h" +#include "csr.h" +#include "drivers/uart.h" +#include "kassert.h" +#include "mcslock.h" +#include "proc.h" +#include "sched.h" + +static mcslock_t lock; +static qnode_t nodes[N_HART]; + +void kernel_init(void) +{ + mcslock_init(&lock, nodes); + uart_init(); + ctable_init(); + sched_init(); + proc_init(); + uart_puts("Kernel initialization complete"); +} + +bool kernel_lock(void) +{ + return mcslock_try_acquire(&lock, csrr_mhartid() - MIN_HARTID); +} + +void kernel_unlock(void) +{ + mcslock_release(&lock, csrr_mhartid() - MIN_HARTID); +} diff --git a/kernel/src/mcslock.c b/kernel/src/mcslock.c new file mode 100644 index 00000000..c77a3ae4 --- /dev/null +++ b/kernel/src/mcslock.c @@ -0,0 +1,59 @@ +// TODO: Replace with proper MCS Lock +#include "mcslock.h" + +#include "preempt.h" + +#include + +static void _release(qnode_t *me) +{ + qnode_t *next, *prev; + + prev = __atomic_exchange_n(&me->prev, NULL, __ATOMIC_RELAXED); + + do { + next = me->next; + } while (__atomic_compare_exchange(&next->prev, &me, &prev, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED)); + if (prev != NULL) + prev->next = next; +} + +static bool _acquire(qnode_t *tail, qnode_t *me, bool preemptive) +{ + me->next = tail; + me->prev = __atomic_exchange_n(&tail->prev, me, __ATOMIC_ACQUIRE); + + // If there is a predecessor, set its next ptr to me. + if (me->prev) + me->prev->next = me; + + // Wait while me->prev != NULL + while (__atomic_load_n(&me->prev, __ATOMIC_ACQUIRE)) { + if (preemptive && preempt()) { + _release(me); + return false; + } + } + return true; +} + +void mcslock_init(mcslock_t *lock, qnode_t *nodes) +{ + lock->tail.prev = lock->tail.next = NULL; + lock->nodes = nodes; +} + +void mcslock_acquire(mcslock_t *lock, uint64_t me) +{ + _acquire(&lock->tail, &lock->nodes[me], false); +} + +bool mcslock_try_acquire(mcslock_t *lock, uint64_t me) +{ + return _acquire(&lock->tail, &lock->nodes[me], true); +} + +void mcslock_release(mcslock_t *lock, uint64_t me) +{ + _release(&lock->nodes[me]); +} diff --git a/kernel/src/proc.c b/kernel/src/proc.c new file mode 100644 index 00000000..01971400 --- /dev/null +++ b/kernel/src/proc.c @@ -0,0 +1,121 @@ +/* See LICENSE file for copyright and license details. */ +#include "proc.h" + +#include "cap_pmp.h" +#include "csr.h" +#include "kassert.h" +#include "drivers/timer.h" + +static proc_t _processes[N_PROC]; +extern unsigned char _payload[]; + +void proc_init(void) +{ + for (uint64_t i = 0; i < N_PROC; i++) { + _processes[i].pid = i; + _processes[i].state = PSF_SUSPENDED; + _processes[i].instrument_wcet = 0; + } + _processes[0].state = 0; + _processes[0].tf.pc = (uint64_t)_payload; + KASSERT(cap_pmp_load(ctable_get(0, 0), 0) == SUCCESS); +} + +proc_t *proc_get(uint64_t pid) +{ + KASSERT(pid < N_PROC); + KASSERT(_processes[pid].pid == pid); + return &_processes[pid]; +} + +bool proc_acquire(proc_t *proc) +{ + // Set the busy flag if expected state + uint64_t expected = proc->state; + uint64_t desired = PSF_BUSY; + + // If state == 0, then process is ready. + bool is_ready = (expected == 0); + // If state is blocked, then process logically ready on timeout. + bool is_timeout = ((expected & PSF_BLOCKED) && time_get() >= proc->timeout); + + if (!is_ready && !is_timeout) + return false; + + return __atomic_compare_exchange(&proc->state, &expected, &desired, false /* not weak */, + __ATOMIC_ACQUIRE /* succ */, __ATOMIC_RELAXED /* fail */); +} + +void proc_release(proc_t *proc) +{ + // Unset all flags except suspend flag. + __atomic_fetch_and(&proc->state, (uint64_t)~PSF_BUSY, __ATOMIC_RELEASE); +} + +void proc_suspend(proc_t *proc) +{ + // Set the suspend flag + uint64_t prev_state = __atomic_fetch_or(&proc->state, PSF_SUSPENDED, __ATOMIC_ACQUIRE); + + // If the process was waiting, we also unset the waiting flag. + if ((prev_state & 0xFF) == PSF_BLOCKED) { + proc->tf.t0 = ERR_SUSPENDED; + proc->state = PSF_SUSPENDED; + __atomic_thread_fence(__ATOMIC_ACQUIRE); + } +} + +void proc_resume(proc_t *proc) +{ + // Unset the suspend flag + __atomic_fetch_and(&proc->state, (uint64_t)~PSF_SUSPENDED, __ATOMIC_RELEASE); +} + +void proc_ipc_wait(proc_t *proc, uint64_t channel) +{ + KASSERT(proc->state == PSF_BUSY); + proc->state = PSF_BLOCKED | (channel << 16); +} + +bool proc_ipc_acquire(proc_t *proc, uint64_t channel) +{ + if (proc->timeout <= time_get()) + return false; + uint64_t expected = PSF_BLOCKED | (channel << 16); + uint64_t desired = PSF_BUSY; + return __atomic_compare_exchange(&proc->state, &expected, &desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED); +} + +bool proc_is_suspended(proc_t *proc) +{ + return proc->state == PSF_SUSPENDED; +} + +bool proc_pmp_avail(proc_t *proc, uint64_t slot) +{ + return proc->pmpcfg[slot] == 0; +} + +void proc_pmp_load(proc_t *proc, uint64_t slot, uint64_t rwx, uint64_t addr) +{ + proc->pmpcfg[slot] = (uint8_t)(rwx | 0x18); + proc->pmpaddr[slot] = addr; +} + +void proc_pmp_unload(proc_t *proc, uint64_t slot) +{ + proc->pmpcfg[slot] = 0; +} + +void proc_pmp_sync(proc_t *proc) +{ + csrw_pmpcfg0(*((uint64_t *)proc->pmpcfg)); + csrw_pmpaddr0(proc->pmpaddr[0]); + csrw_pmpaddr1(proc->pmpaddr[1]); + csrw_pmpaddr2(proc->pmpaddr[2]); + csrw_pmpaddr3(proc->pmpaddr[3]); + csrw_pmpaddr4(proc->pmpaddr[4]); + csrw_pmpaddr5(proc->pmpaddr[5]); + csrw_pmpaddr6(proc->pmpaddr[6]); + csrw_pmpaddr7(proc->pmpaddr[7]); +} diff --git a/kernel/src/sched.c b/kernel/src/sched.c new file mode 100644 index 00000000..4f7c51a7 --- /dev/null +++ b/kernel/src/sched.c @@ -0,0 +1,125 @@ +/* See LICENSE file for copyright and license details. */ + +#include "sched.h" + +#include "csr.h" +#include "drivers/timer.h" +#include "kassert.h" +#include "proc.h" +#include "semaphore.h" +#include "trap.h" +#include "wfi.h" + +typedef struct slot_info { + // Owner of time slot. + uint32_t pid; + // Remaining length of corresponding slice. + uint32_t length; +} slot_info_t; + +static slot_info_t slots[N_HART][N_SLOT]; +static semaphore_t sched_semaphore; + +void sched_init(void) +{ + uint64_t pid = 0; + uint64_t end = N_SLOT; + uint64_t from = 0; + uint64_t to = N_SLOT; + + semaphore_init(&sched_semaphore, N_HART); + + for (uint64_t hartid = MIN_HARTID; hartid <= MAX_HARTID; hartid++) + sched_update(pid, end, hartid, from, to); +} + +void sched_update(uint64_t pid, uint64_t end, uint64_t hartid, uint64_t from, uint64_t to) +{ + // Acquire all resources, blocking everyone else. + semaphore_acquire_n(&sched_semaphore, N_HART); + for (uint64_t i = from; i < to; i++) { + slots[hartid - MIN_HARTID][i].pid = pid & 0xFF; + slots[hartid - MIN_HARTID][i].length = (end - i) & 0xFF; + } + // Release the resources. + semaphore_release_n(&sched_semaphore, N_HART); +} + +void sched_delete(uint64_t hartid, uint64_t from, uint64_t to) +{ + semaphore_acquire_n(&sched_semaphore, N_HART); + for (uint64_t i = from; i < to; ++i) { + slots[hartid - MIN_HARTID][i].pid = 0; + slots[hartid - MIN_HARTID][i].length = 0; + } + // Release the resources. + semaphore_release_n(&sched_semaphore, N_HART); +} + +slot_info_t slot_info_get(uint64_t hartid, uint64_t slot) +{ + return slots[hartid - MIN_HARTID][slot % N_SLOT]; +} + +static proc_t *sched_fetch(uint64_t hartid, uint64_t *start_time, uint64_t *end_time) +{ + proc_t *p = NULL; + semaphore_acquire(&sched_semaphore); + + uint64_t slot = time_get() / SLOT_LENGTH; + slot_info_t si = slot_info_get(hartid, slot); + + // If length = 0, then slice is deleted. + if (si.length == 0) + goto fail; + + // Have priority over harts with lower ID when scheduling length is + // longer. + for (uint64_t i = MIN_HARTID; i < hartid; i++) { + slot_info_t other_si = slot_info_get(hartid, slot); + if (si.pid == other_si.pid && si.length <= other_si.length) + goto fail; + } + + // Have priority over harts with higher ID when scheduling length is + // equal or longer. + for (uint64_t i = hartid + 1; i < MAX_HARTID; i++) { + slot_info_t other_si = slot_info_get(hartid, slot); + if (si.pid == other_si.pid && si.length < other_si.length) + goto fail; + } + + // Get the process. + p = proc_get(si.pid); + + // Try to acquire the process. + if (!proc_acquire(p)) { + p = NULL; + goto fail; + } + *start_time = slot * SLOT_LENGTH; + *end_time = (slot + si.length) * SLOT_LENGTH - SCHEDULER_TIME; + p->timeout = *end_time; +fail: + semaphore_release(&sched_semaphore); + return p; +} + +proc_t *sched(proc_t *p) +{ + uint64_t hartid = csrr_mhartid(); + uint64_t start_time, end_time; + if (p) + proc_release(p); + + do { + p = sched_fetch(hartid, &start_time, &end_time); + } while (!p); + + proc_pmp_sync(p); + timer_set(hartid, start_time); + while (!(csrr_mip() & MIP_MTIP)) + wfi(); + timer_set(hartid, end_time); + return p; +} diff --git a/kernel/src/stack.S b/kernel/src/stack.S new file mode 100644 index 00000000..5d3ee81d --- /dev/null +++ b/kernel/src/stack.S @@ -0,0 +1,5 @@ +.globl _sp + +.section .bss.stack +.skip N_HART * (1 << LOG_STACK_SIZE) +_sp: diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c new file mode 100644 index 00000000..f198ccd5 --- /dev/null +++ b/kernel/src/syscall.c @@ -0,0 +1,491 @@ +/* See LICENSE file for copyright and license details. */ +#include "syscall.h" + +#include "cap_ipc.h" +#include "cap_monitor.h" +#include "cap_ops.h" +#include "cap_pmp.h" +#include "cap_table.h" +#include "cap_types.h" +#include "csr.h" +#include "drivers/timer.h" +#include "error.h" +#include "kernel.h" +#include "sched.h" + +#include + +#define N_ARGS 8 + +static err_t sys_get_info(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_reg_read(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_reg_write(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_sync(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_sync_mem(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_cap_read(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_cap_move(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_cap_delete(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_cap_revoke(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_cap_derive(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_pmp_load(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_pmp_unload(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_mon_suspend(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_mon_resume(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_mon_reg_read(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_mon_reg_write(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_mon_cap_read(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_mon_cap_move(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_mon_pmp_load(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_mon_pmp_unload(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_sock_send(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_sock_call(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_sock_reply(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_sock_recv(proc_t *p, reg_t args[N_ARGS], reg_t *ret); +static err_t sys_sock_replyrecv(proc_t *p, reg_t args[N_ARGS], reg_t *ret); + +typedef err_t (*sys_handler_t)(proc_t *, reg_t *, reg_t *); + +sys_handler_t handlers[] = { + sys_get_info, sys_reg_read, sys_reg_write, sys_sync, sys_sync_mem, + sys_cap_read, sys_cap_move, sys_cap_delete, sys_cap_revoke, sys_cap_derive, + sys_pmp_load, sys_pmp_unload, sys_mon_suspend, sys_mon_resume, sys_mon_reg_read, + sys_mon_reg_write, sys_mon_cap_read, sys_mon_cap_move, sys_mon_pmp_load, sys_mon_pmp_unload, + sys_sock_send, sys_sock_call, sys_sock_reply, sys_sock_recv, sys_sock_replyrecv, +}; + +proc_t *handle_syscall(proc_t *p) +{ + // Error code. + err_t err = ERR_INVALID_SYSCALL; + // System call arguments. + reg_t *args = &p->tf.a0; + // System call number. + reg_t call = p->tf.t0; + // Return value. + reg_t ret = p->tf.a0; + + p->tf.pc += 4; + + if (call < ARRAY_SIZE(handlers)) + err = handlers[call](p, args, &ret); + + switch (err) { + case JUSTRET: // No return value. + break; + case YIELD: // Yield to another process. + p = proc_get(ret); + proc_pmp_sync(p); + break; + case SYNC: + p = sched(p); + break; + default: + p->tf.a0 = ret; + p->tf.t0 = err; + break; + } + return p; +} + +err_t sys_get_info(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + switch (args[0]) { + case 0: + *ret = p->pid; + break; + case 1: + *ret = time_get(); + break; + case 2: + *ret = timer_get(csrr_mhartid()); + break; + default: + *ret = 0; + } + return SUCCESS; +} + +err_t sys_reg_read(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + reg_t reg = args[0]; + reg_t *regs = (reg_t *)&p->tf; + if (reg < N_REG) + *ret = regs[reg]; + return SUCCESS; +} + +err_t sys_reg_write(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + reg_t reg = args[0], val = args[1]; + reg_t *regs = (reg_t *)&p->tf; + if (args[0] < N_REG) + regs[reg] = val; + return JUSTRET; +} + +err_t sys_sync(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + return SYNC; +} + +err_t sys_sync_mem(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + // In YIELD handling, memory is synced. + *ret = p->pid; + return YIELD; +} + +err_t sys_cap_read(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t c = ctable_get(p->pid, args[0]); + if (!c) + return ERR_INVALID_INDEX; + return cap_read(c, (cap_t *)ret); +} + +err_t sys_cap_move(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t src = ctable_get(p->pid, args[0]); + cte_t dst = ctable_get(p->pid, args[1]); + if (!src || !dst) + return ERR_INVALID_INDEX; + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_move(src, dst, (cap_t *)ret); + kernel_unlock(); + return err; +} + +err_t sys_cap_delete(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t c = ctable_get(p->pid, args[0]); + if (!c) + return ERR_INVALID_INDEX; + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_delete(c); + kernel_unlock(); + return err; +} + +err_t sys_cap_revoke(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t c = ctable_get(p->pid, args[0]); + if (!c) + return ERR_INVALID_INDEX; + while (1) { + cap_t cap = cte_cap(c); + cte_t next = cte_next(c); + cap_t ncap = cte_cap(next); + if (!cap.type) + return ERR_EMPTY; + if (!cap_revokable(cap, ncap)) + break; + if (!kernel_lock()) + return ERR_PREEMPTED; + cap_reclaim(c, cap, next, ncap); + kernel_unlock(); + } + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_reset(c); + kernel_unlock(); + return err; +} + +err_t sys_cap_derive(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t src = ctable_get(p->pid, args[0]); + cte_t dst = ctable_get(p->pid, args[1]); + if (!src || !dst) + return ERR_INVALID_INDEX; + + cap_t new_cap = (cap_t){.raw = args[2]}; + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_derive(src, dst, new_cap); + kernel_unlock(); + return err; +} + +err_t sys_pmp_load(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t pmp = ctable_get(p->pid, args[0]); + if (!pmp) + return ERR_INVALID_INDEX; + + if (args[1] >= N_PMP) + return ERR_INVALID_SLOT; + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_pmp_load(pmp, args[1]); + kernel_unlock(); + return err; +} + +err_t sys_pmp_unload(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t pmp = ctable_get(p->pid, args[0]); + if (!pmp) + return ERR_INVALID_INDEX; + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_pmp_unload(pmp); + kernel_unlock(); + return err; +} + +err_t sys_mon_suspend(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t mon = ctable_get(p->pid, args[0]); + if (!mon) + return ERR_INVALID_INDEX; + + if (args[1] >= N_PROC) + return ERR_INVALID_PID; + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_monitor_suspend(mon, args[1]); + kernel_unlock(); + return err; +} + +err_t sys_mon_resume(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t mon = ctable_get(p->pid, args[0]); + if (!mon) + return ERR_INVALID_INDEX; + + if (args[1] >= N_PROC) + return ERR_INVALID_PID; + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_monitor_resume(mon, args[1]); + kernel_unlock(); + return err; +} + +err_t sys_mon_reg_read(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t mon = ctable_get(p->pid, args[0]); + if (!mon) + return ERR_INVALID_INDEX; + + if (args[1] >= N_PROC) + return ERR_INVALID_PID; + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_monitor_reg_read(mon, args[1], args[2], ret); + kernel_unlock(); + return err; +} + +err_t sys_mon_reg_write(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t mon = ctable_get(p->pid, args[0]); + if (!mon) + return ERR_INVALID_INDEX; + + if (args[1] >= N_PROC) + return ERR_INVALID_PID; + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_monitor_reg_write(mon, args[1], args[2], args[3]); + kernel_unlock(); + return err; +} + +err_t sys_mon_cap_read(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t mon = ctable_get(p->pid, args[0]); + cte_t src = ctable_get(args[1], args[2]); + if (!mon || !src) + return ERR_INVALID_INDEX; + if (args[1] >= N_PROC) + return ERR_INVALID_PID; + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_monitor_cap_read(mon, src, (cap_t *)ret); + kernel_unlock(); + return err; +} + +err_t sys_mon_cap_move(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t mon = ctable_get(p->pid, args[0]); + cte_t src = ctable_get(args[1], args[2]); + cte_t dst = ctable_get(args[3], args[4]); + if (!mon) + return ERR_INVALID_INDEX; + if (!src) + return (args[1] >= N_PROC) ? ERR_INVALID_PID : ERR_INVALID_INDEX; + if (!dst) + return (args[3] >= N_PROC) ? ERR_INVALID_PID : ERR_INVALID_INDEX; + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_monitor_cap_move(mon, src, dst, (cap_t *)ret); + kernel_unlock(); + return err; +} + +err_t sys_mon_pmp_load(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t mon = ctable_get(p->pid, args[0]); + cte_t pmp = ctable_get(args[1], args[2]); + if (!mon) + return ERR_INVALID_INDEX; + if (!pmp) + return (args[1] >= N_PROC) ? ERR_INVALID_PID : ERR_INVALID_INDEX; + if (args[3] >= N_PMP) + return ERR_INVALID_SLOT; + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_monitor_pmp_load(mon, pmp, args[3]); + kernel_unlock(); + return err; +} + +err_t sys_mon_pmp_unload(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t mon = ctable_get(p->pid, args[0]); + cte_t pmp = ctable_get(args[1], args[2]); + if (!mon) + return ERR_INVALID_INDEX; + if (!pmp) + return (args[1] >= N_PROC) ? ERR_INVALID_PID : ERR_INVALID_INDEX; + + if (!kernel_lock()) + return ERR_PREEMPTED; + err_t err = cap_monitor_pmp_unload(mon, pmp); + kernel_unlock(); + return err; +} + +err_t sys_sock_send(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t clnt = ctable_get(p->pid, args[0]); + if (!clnt) + return ERR_INVALID_INDEX; + cte_t cbuf = ctable_get(p->pid, args[5]); + if (!cbuf) + return ERR_INVALID_INDEX; + + if (!kernel_lock()) + return ERR_PREEMPTED; + + ipc_msg_t msg; + msg.buf[0] = args[1]; + msg.buf[1] = args[2]; + msg.buf[2] = args[3]; + msg.buf[3] = args[4]; + msg.cbuf = cbuf; + msg.send_cap = args[6]; + + err_t err = cap_sock_send(p, clnt, &msg, ret); + kernel_unlock(); + return err; +} + +err_t sys_sock_call(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t clnt = ctable_get(p->pid, args[0]); + if (!clnt) + return ERR_INVALID_INDEX; + + cte_t cbuf = ctable_get(p->pid, args[5]); + if (!cbuf) + return ERR_INVALID_INDEX; + + if (!kernel_lock()) + return ERR_PREEMPTED; + + ipc_msg_t msg; + msg.buf[0] = args[1]; + msg.buf[1] = args[2]; + msg.buf[2] = args[3]; + msg.buf[3] = args[4]; + msg.cbuf = cbuf; + msg.send_cap = args[6]; + + err_t err = cap_sock_call(p, clnt, &msg, ret); + kernel_unlock(); + return err; +} + +err_t sys_sock_recv(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t clnt = ctable_get(p->pid, args[0]); + if (!clnt) + return ERR_INVALID_INDEX; + + cte_t cbuf = ctable_get(p->pid, args[5]); + if (!cbuf) + return ERR_INVALID_INDEX; + + if (!kernel_lock()) + return ERR_PREEMPTED; + ipc_msg_t msg; + msg.cbuf = cbuf; + msg.send_cap = false; + err_t err = cap_sock_recv(p, clnt, &msg); + kernel_unlock(); + return err; +} + +err_t sys_sock_reply(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t clnt = ctable_get(p->pid, args[0]); + if (!clnt) + return ERR_INVALID_INDEX; + cte_t cbuf = ctable_get(p->pid, args[5]); + if (!cbuf) + return ERR_INVALID_INDEX; + + if (!kernel_lock()) + return ERR_PREEMPTED; + ipc_msg_t msg; + msg.buf[0] = args[1]; + msg.buf[1] = args[2]; + msg.buf[2] = args[3]; + msg.buf[3] = args[4]; + msg.cbuf = cbuf; + msg.send_cap = args[6]; + err_t err = cap_sock_reply(p, clnt, &msg, ret); + kernel_unlock(); + return err; +} + +err_t sys_sock_replyrecv(proc_t *p, reg_t args[N_ARGS], reg_t *ret) +{ + cte_t clnt = ctable_get(p->pid, args[0]); + if (!clnt) + return ERR_INVALID_INDEX; + cte_t cbuf = ctable_get(p->pid, args[5]); + if (!cbuf) + return ERR_INVALID_INDEX; + + if (!kernel_lock()) + return ERR_PREEMPTED; + ipc_msg_t msg; + msg.buf[0] = args[1]; + msg.buf[1] = args[2]; + msg.buf[2] = args[3]; + msg.buf[3] = args[4]; + msg.cbuf = cbuf; + msg.send_cap = args[6]; + err_t err = cap_sock_replyrecv(p, clnt, &msg, ret); + kernel_unlock(); + return err; +} diff --git a/kernel/src/trap.S b/kernel/src/trap.S new file mode 100644 index 00000000..8311d2af --- /dev/null +++ b/kernel/src/trap.S @@ -0,0 +1,139 @@ +// See LICENSE file for copyright and license details. +#include "macro.inc" +#include "offsets.h" +#include "csr.h" + +.globl trap_entry +.globl trap_syscall_exit +.globl trap_schedule_exit +.globl trap_exit + +.type trap_entry, @function +.type trap_syscall_exit, @function +.type trap_schedule_exit, @function +.type trap_exit, @function + +.section .text.trap +.balign 16 +trap_entry: + /* Save user tp to scratch, load PCB pointer */ + csrrw a0, mscratch, a0 + beqz a0, _myield +1: + sd ra, TF_RA(a0) + sd sp, TF_SP(a0) + sd gp, TF_GP(a0) + sd tp, TF_TP(a0) + sd t0, TF_T0(a0) + sd t1, TF_T1(a0) + sd t2, TF_T2(a0) + sd s0, TF_S0(a0) + sd s1, TF_S1(a0) + sd s2, TF_S2(a0) + sd s3, TF_S3(a0) + sd s4, TF_S4(a0) + sd s5, TF_S5(a0) + sd s6, TF_S6(a0) + sd s7, TF_S7(a0) + sd s8, TF_S8(a0) + sd s9, TF_S9(a0) + sd s10, TF_S10(a0) + sd s11, TF_S11(a0) + sd a1, TF_A1(a0) + sd a2, TF_A2(a0) + sd a3, TF_A3(a0) + sd a4, TF_A4(a0) + sd a5, TF_A5(a0) + sd a6, TF_A6(a0) + sd a7, TF_A7(a0) + sd t3, TF_T3(a0) + sd t4, TF_T4(a0) + sd t5, TF_T5(a0) + sd t6, TF_T6(a0) + csrr t1, mepc + sd t1, TF_PC(a0) + csrrw t2, mscratch, 0 + sd t2, TF_A0(a0) + + /* Load the global and stack pointer of the kernel. */ + load_gp + load_sp t0 + + /* Check if system call. */ + csrr t0, mcause + bltz t0, _yield + + la ra,trap_exit + + li t1, MCAUSE_USER_ECALL + beq t0, t1, _syscall + +_exception: + /* Otherwise, it is exception. */ + csrr a1, mcause + csrr a2, mepc + csrr a3, mtval + tail handle_exception + +_syscall: + tail handle_syscall + +_myield: + csrrw a0, mscratch, a0 + load_gp + load_sp t1 + +_yield: + /* Call scheduler */ + call sched + +trap_exit: + csrs mstatus,MSTATUS_MIE + + /* Load call-used registers, ra, sp, gp, tp, pc */ + ld t0, TF_PC(a0) + csrw mepc,t0 + + ld ra, TF_RA(a0) + ld sp, TF_SP(a0) + ld gp, TF_GP(a0) + ld tp, TF_TP(a0) + ld t0, TF_T0(a0) + ld t1, TF_T1(a0) + ld t2, TF_T2(a0) + ld s0, TF_S0(a0) + ld s1, TF_S1(a0) + ld s2, TF_S2(a0) + ld s3, TF_S3(a0) + ld s4, TF_S4(a0) + ld s5, TF_S5(a0) + ld s6, TF_S6(a0) + ld s7, TF_S7(a0) + ld s8, TF_S8(a0) + ld s9, TF_S9(a0) + ld s10, TF_S10(a0) + ld s11, TF_S11(a0) + ld a1, TF_A1(a0) + ld a2, TF_A2(a0) + ld a3, TF_A3(a0) + ld a4, TF_A4(a0) + ld a5, TF_A5(a0) + ld a6, TF_A6(a0) + ld a7, TF_A7(a0) + ld t3, TF_T3(a0) + ld t4, TF_T4(a0) + ld t5, TF_T5(a0) + ld t6, TF_T6(a0) + + csrw mstatus,MSTATUS_MIE + + /* Save PCB pointer */ + csrw mscratch, a0 + + /* Load user tp */ + ld a0, TF_A0(a0) + + mret + +__hang: + j __hang diff --git a/model/bigstepScript.sml b/model/bigstepScript.sml deleted file mode 100644 index 4ea4fdd2..00000000 --- a/model/bigstepScript.sml +++ /dev/null @@ -1,112 +0,0 @@ -(* - * Here is a sketch of the big-step semantics for the kernel: - * - * - bigstep (P, M, H, C, S) (P', M', H', C', S') - * - compute H (p, M) (p', M') - * /\ P' = {p'} UNION (P/p') - * /\ C = C /\ S = S' /\ H = H' - * - switch (P, H, S) (P, H', S) - * /\ M = M' /\ C = C' - * - syscall (p, H, C) (p', H', C') - * /\ P' = {p'} UNION (P/p') - * /\ S = S' /\ M = M' - * - * - P, P': Set of processes. - * - H, H': Hardware context (e.g., PMP). - * - S, S': Scheduling context. - * - C, C': Capability sets. - * - compute H (p, M) (p', M'): Process's compute step. - * - switch (P, H, S) (P', H', S'): Schedule context switch. - * - syscall (p, H, C) (p', H', C'): System call. - *) - - -(* Capability defintions *) -(* -* - CapRoot -* - Just a root capability. -* - CapTime hartid range alloc -* - range: memory controlled by this capability. -* - free: memory not allocated to children. -* - CapMemory range alloc l r w x -* - range: memory controlled by this capability. -* - free: memory not allocated to children. -* - l: lock, set if there may be a child PMP slice. -* - rwx: read write execute permissions. -* - CapPMP addr r w x -* - range: PMP address range -* - rwx: read write execute permissions. -* - CapChannel range alloc -* - range: channels controlled by this capability. -* - free: channels not allocated to other children. -* - CapSocket channel tag -* - channel: communication channel. -* - tag: if 0 then receiver, otherwise identifies sender. -*) -Datatype: - cap_t = CapRoot - | CapTime num (num set) (num set) - | CapMemory (num set) (num set) bool bool bool bool - | CapPMP (num set) bool bool bool - | CapChannel (num set) (num set) - | CapSocket num num -End - -Datatype: - cnode_t = CNode num capability_t (cnode_t list) -End - -(* Check if the capability is well-formed, mainly, the allocated -* part of slices should be contained in the ranges. *) -Definition cap_is_wf_def: -cap_is_wf CapRoot = T -/\ -cap_is_wf (CapTime hartid range free) = free SUBSET range -/\ -cap_is_wf (CapMemory range free l r w x) = free SUBSET range -/\ -cap_is_wf (CapPMP range r w x) = T -/\ -cap_is_wf (CapChannel range free) = free SUBSET range -/\ -cap_is_wf (CapSocket channel tag) = T -End - -Definition cap_is_child_def: - ( - cap_is_child (CapRoot) _ = False - ) /\ ( - cap_is_child (CapMemory range free l r w x) - (CapMemory range' free' l' r' w' x') - = (range' SUBSET range /\ r ==> r' /\ w ==> w' /\ x ==> x') - ) /\ ( - cap_is_child (CapMemory range free l r w x) - (CapPMP range' r' w' x') - = (range' SUBSET range /\ r ==> r' /\ w ==> w' /\ x ==> x') - ) /\ ( - cap_is_child (CapTime hartid range free) - (CapTime hartid' range' free') - = (hartid = hartid' /\ range' SUBSET range) - ) /\ ( - cap_is_child (CapChannel range free) - (CapChannel range' free') - = (range' SUBSET range) - ) /\ ( - cap_is_child (CapChannel range free) - (CapSocket channel' tag') - = (channel IN range) - ) -End - -val _ = Hol_reln ` - (compute H (p, M) (p', M') /\ - P' = {p'} UNION (P DIFF p) - ==> bigstep (P, M, H, C, S) (P', M', H, C, S)) - /\ - (switch (P, H, M) (P',H' M') /\ - ==> bigstep (P, M, H, C, S) (P', M, H', C, S')) - /\ - (syscall (p, H, C) (p',H' C') /\ - P' = {p'} UNION (P DIFF p) - ==> bigstep (P, M, H, C, S) (P', M, H', C', S)) -`; diff --git a/model/forestScript.sml b/model/forestScript.sml deleted file mode 100644 index c4e4580a..00000000 --- a/model/forestScript.sml +++ /dev/null @@ -1,73 +0,0 @@ -open HolKernel Parse boolLib bossLib set_relationTheory; - -val _ = new_theory "forest" - -Definition injective_def: - injective r = (!x y z. (x,z) IN r /\ (y,z) IN r ==> x = y) -End - -Definition forest_def: - forest r = (acyclic r /\ injective r) -End - -Definition children_def: - children r x = {y | (x,y) IN r} -End - -Definition parent_def: - parent r x = {y | (y,x) IN r} -End - -Definition descendants_def: - descendants r x = {y | (x,y) IN (tc r)} -End - -(* Delete node x - * - Remove all edges involving x - * - Add children of x to parent(x) - *) -Definition delete_def: - delete r x = {(z,y) | (z,y) IN r /\ x <> z /\ x <> y } - UNION {(z,y) | (z,x) IN r /\ (x,y) IN r} -End - - -(* In a forest, each node has at most one parent. *) -Theorem forest_CARD_parent_def: -!r x. - forest r ==> (CARD (parent r x) <= 1) -Proof - cheat -QED - -(* When we delete a node, the parent of the node inherits the children. *) -Theorem delete_inherit_thm: -!r x. - {(y,z) | y IN (parent r x) /\ z IN (children r x)} SUBSET (delete r x) -Proof - cheat -QED - -(* Children are descendants. *) -Theorem children_SUBSET_descendants_thm: -!r x. - (children r x) SUBSET (descendants r x) -Proof - cheat -QED - -Theorem children_SUBSET_range: -!r x. - (children r x) SUBSET (range r) -Proof - cheat -QED - -Theorem parent_SUBSET_domain: -!r x. - (parent r x) SUBSET (domain r) -Proof - cheat -QED - -val _ = export_theory (); diff --git a/plat/hifive-u/config.h b/plat/hifive-u/config.h new file mode 100644 index 00000000..95d77a70 --- /dev/null +++ b/plat/hifive-u/config.h @@ -0,0 +1,33 @@ +#ifndef __PLATFORM_H__ +#define __PLATFORM_H__ + + +// Min and max usable hart ID. +#define MIN_HARTID 1 +#define MAX_HARTID 4 + +// Total number of usable harts. +#define N_HART 4 + +// Number of PMP slots. +#define N_PMP 8 + +// RTC ticks per second +#define TICKS_PER_SECOND 1000000ull + +/// Stack size of 256 B +#define LOG_STACK_SIZE 8 + +// Initial capabilities. +#define INIT_CAPS \ + { \ + CAP_PMP(0x20005fff, CAP_RWX), \ + CAP_MEMORY(0x80020, 0x80, CAP_RWX), \ + CAP_MEMORY(0x10010, 0x1, CAP_RW), \ + CAP_MEMORY(0x200b, 0x1, CAP_R), CAP_TIME(1, 0, N_SLOT), \ + CAP_TIME(2, 0, N_SLOT), CAP_TIME(3, 0, N_SLOT), \ + CAP_TIME(4, 0, N_SLOT), CAP_MONITOR(0, N_PROC), \ + CAP_CHANNEL(0, N_CHAN) \ + } + +#endif /* __PLATFORM_H__ */ diff --git a/plat/hifive-u/platform.ld b/plat/hifive-u/platform.ld new file mode 100644 index 00000000..30a781a0 --- /dev/null +++ b/plat/hifive-u/platform.ld @@ -0,0 +1,4 @@ +_clint = 0x2000000; +_mtime = 0x200bff8; +_mtimecmp = 0x2004000; +_uart = 0x10010000; diff --git a/plat/hifive-u/platform.mk b/plat/hifive-u/platform.mk new file mode 100644 index 00000000..84b2a62d --- /dev/null +++ b/plat/hifive-u/platform.mk @@ -0,0 +1,4 @@ +ARCH=rv64imac_zicsr_zifencei +ABI=lp64 +CMODEL=medany +DRIVERS=drivers/timer.c drivers/fs540-uart.c diff --git a/plat/virt/config.h b/plat/virt/config.h new file mode 100644 index 00000000..4451334d --- /dev/null +++ b/plat/virt/config.h @@ -0,0 +1,33 @@ +#ifndef __PLATFORM_H__ +#define __PLATFORM_H__ + + +// Min and max usable hart ID. +#define MIN_HARTID 0 +#define MAX_HARTID 3 + +// Total number of usable harts. +#define N_HART 4 + +// Number of PMP slots. +#define N_PMP 8 + +// RTC ticks per second +#define TICKS_PER_SECOND 1000000ull + +/// Stack size of 1024 KiB +#define LOG_STACK_SIZE 10 + +// Initial capabilities. +#define INIT_CAPS \ + { \ + cap_mk_pmp(0x20005fff, MEM_RWX), \ + cap_mk_memory(0x80020, 0x80, MEM_RWX), \ + cap_mk_memory(0x10000, 0x1, MEM_RW), \ + cap_mk_memory(0x200b, 0x1, MEM_R), cap_mk_time(0, 0, N_SLOT), \ + cap_mk_time(1, 0, N_SLOT), cap_mk_time(2, 0, N_SLOT), \ + cap_mk_time(3, 0, N_SLOT), cap_mk_monitor(0, N_PROC), \ + cap_mk_channel(0, N_CHAN) \ + } + +#endif /* __PLATFORM_H__ */ diff --git a/plat/virt/init_caps.h b/plat/virt/init_caps.h deleted file mode 100644 index ab09fc2d..00000000 --- a/plat/virt/init_caps.h +++ /dev/null @@ -1,12 +0,0 @@ -#include "cap.h" - -static const union cap init_caps[] - = { [0] = CAP_PMP(0x20005fff, CAP_RWX), - [1] = CAP_MEMORY(0x0020, 0x8000, 0x10, CAP_RWX), - [2] = CAP_MEMORY(0x0000, 0x0001, 0x2, CAP_RW), - [3] = CAP_TIME(0, 0, NSLICE), - [4] = CAP_TIME(1, 0, NSLICE), - [5] = CAP_TIME(2, 0, NSLICE), - [6] = CAP_TIME(3, 0, NSLICE), - [7] = CAP_MONITOR(0, NPROC), - [8] = CAP_CHANNEL(0, NCHANNEL) }; diff --git a/plat/virt/platform.h b/plat/virt/platform.h deleted file mode 100644 index 5466f6d5..00000000 --- a/plat/virt/platform.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __PLATFORM_H__ -#define __PLATFORM_H__ - -#define NHART 4 - -#endif /* __PLATFORM_H__ */ diff --git a/plat/virt/platform.ld b/plat/virt/platform.ld new file mode 100644 index 00000000..bcc47dc0 --- /dev/null +++ b/plat/virt/platform.ld @@ -0,0 +1,4 @@ +_clint = 0x2000000; +_mtime = 0x200bff8; +_mtimecmp = 0x2004000; +_uart = 0x10000000; diff --git a/plat/virt/platform.mk b/plat/virt/platform.mk new file mode 100644 index 00000000..08a23c3a --- /dev/null +++ b/plat/virt/platform.mk @@ -0,0 +1,4 @@ +ARCH=rv64imac_zicsr_zifencei +ABI=lp64 +CMODEL=medany +DRIVERS=drivers/timer.c drivers/ns16550a.c diff --git a/projects/hello/Makefile b/projects/hello/Makefile new file mode 100644 index 00000000..08c0934e --- /dev/null +++ b/projects/hello/Makefile @@ -0,0 +1,13 @@ +export BUILD=${abspath build} + +DIRS=../../kernel app0 app1 + +all: ${DIRS} + +${DIRS}: + @${MAKE} -C $@ + +clean: + rm -rf ${BUILD} + +.PHONY: all clean ${DIRS} diff --git a/projects/hello/app0/Makefile b/projects/hello/app0/Makefile new file mode 100644 index 00000000..c66edac2 --- /dev/null +++ b/projects/hello/app0/Makefile @@ -0,0 +1,67 @@ +BUILD?=build +PROGRAM?=app0 + +ROOT=../../.. +COMMON=${ROOT}/common +PLATFORM=${ROOT}/plat/virt + +include ${ROOT}/tools.mk +include ${PLATFORM}/platform.mk + +vpath %.c src ${COMMON}/src +vpath %.S src ${COMMON}/src + +SRCS=init/start.S altio/altio.c +SRCS+=${DRIVERS} +SRCS+=main.c + +OBJS=${patsubst %, ${BUILD}/${PROGRAM}/%.o, ${SRCS}} +DEPS=${patsubst %, ${BUILD}/${PROGRAM}/%.d, ${SRCS}} + +CFLAGS+=-march=${ARCH} -mabi=${ABI} -mcmodel=${CMODEL} +CFLAGS+=-nostartfiles -nostdlib +CFLAGS+=-DSTACK_SIZE=1024 +CFLAGS+=-T${PLATFORM}/platform.ld -Tlinker.ld +CFLAGS+=-Wl,--no-warn-rwx-segments + +INC=-Iinc -I${COMMON}/inc + +ELF=${BUILD}/${PROGRAM}.elf +BIN=${ELF:.elf=.bin} +DA=${ELF:.elf=.da} + +all: ${ELF} ${BIN} ${DA} + +elf: ${ELF} +bin: ${BIN} +da: ${DA} + +${BUILD}/${PROGRAM}/%.S.o: %.S + @mkdir -p ${@D} + @echo "CC $@" + @${CC} ${CFLAGS} ${INC} -MMD -c -o $@ $< + +${BUILD}/${PROGRAM}/%.c.o: %.c + @mkdir -p ${@D} + @echo "CC $@" + @${CC} ${CFLAGS} ${INC} -MMD -c -o $@ $< + +${ELF}: ${OBJS} + @mkdir -p ${@D} + @echo "CC $@" + @${CC} ${CFLAGS} -MMD ${INC} -o $@ ${OBJS} + +${BIN}: ${ELF} + @echo "OBJCOPY $@" + @${OBJCOPY} -O binary $< $@ + +${DA}: ${ELF} + @echo "OBJDUMP $@" + @${OBJDUMP} -D $< > $@ + +clean: + rm -f ${ELF} ${BIN} ${DA} ${OBJS} ${DEPS} + +.PHONY: all elf bin da clean + +-include ${DEPS} diff --git a/projects/hello/app0/linker.ld b/projects/hello/app0/linker.ld new file mode 100644 index 00000000..7f5ba272 --- /dev/null +++ b/projects/hello/app0/linker.ld @@ -0,0 +1,33 @@ +/* See LICENSE file for copyright and license details. */ +OUTPUT_ARCH(riscv) +ENTRY(_start) + +__global_pointer$ = MIN(_sdata + 0x800, MAX(_data + 0x800, _end - 0x800)); + +MEMORY { + RAM (rwx) : ORIGIN = 0x80010000, LENGTH = 0x10000 +} + +SECTIONS { + .text : { + *( .init ) + *( .text .text.* ) + } > RAM + + .data : { + _data = . ; + *( .data ) + *( .data.* ) + _sdata = . ; + *( .sdata ) + *( .sdata.* ) + } > RAM + + .bss : { + _bss = .; + _sbss = .; + *(.sbss .sbss.*) + *(.bss .bss.*) + _end = .; + } > RAM +} diff --git a/projects/hello/app0/src/main.c b/projects/hello/app0/src/main.c new file mode 100644 index 00000000..daf03dae --- /dev/null +++ b/projects/hello/app0/src/main.c @@ -0,0 +1,38 @@ +#include "altio/altio.h" +#include "s3k/s3k.h" + +int main(void) +{ + uint64_t uart_addr = s3k_napot_encode(0x10000000, 0x8); + while (s3k_cap_derive(2, 16, s3k_pmp_cap(uart_addr, S3K_MEM_RW))) + ; + while (s3k_cap_derive(2, 18, s3k_pmp_cap(uart_addr, S3K_MEM_RW))) + ; + while (s3k_pmp_load(16, 1)) + ; + s3k_sync(); + alt_puts("hello, world from app0"); + + uint64_t app1_addr = s3k_napot_encode(0x80020000, 0x10000); + s3k_error_t err; + while (s3k_cap_derive(1, 17, s3k_pmp_cap(app1_addr, S3K_MEM_RWX))) + alt_puts("err0"); + while (s3k_mon_cap_move(8, 0, 17, 1, 0)) + alt_puts("err1"); + while (s3k_mon_cap_move(8, 0, 18, 1, 1)) + alt_puts("err2"); + while (s3k_cap_derive(4, 19, s3k_time_cap(0, 0, 15))) + alt_puts("err2a"); + while (s3k_mon_cap_move(8, 0, 19, 1, 2)) + alt_puts("err2b"); + while (s3k_mon_pmp_load(8, 1, 0, 0)) + alt_puts("err3"); + while (s3k_mon_pmp_load(8, 1, 1, 1)) + alt_puts("err4"); + while ((err = s3k_mon_reg_write(8, 1, 0, 0x80020000))) + if (err != ERR_PREEMPTED) + alt_puts("err4b"); + while ((err = s3k_mon_resume(8, 1))) + if (err != ERR_PREEMPTED) + alt_puts("err5"); +} diff --git a/projects/hello/app1/Makefile b/projects/hello/app1/Makefile new file mode 100644 index 00000000..b8da97bf --- /dev/null +++ b/projects/hello/app1/Makefile @@ -0,0 +1,67 @@ +BUILD?=build +PROGRAM?=app1 + +ROOT=../../.. +COMMON=${ROOT}/common +PLATFORM=${ROOT}/plat/virt + +include ${ROOT}/tools.mk +include ${PLATFORM}/platform.mk + +vpath %.c src ${COMMON}/src +vpath %.S src ${COMMON}/src + +SRCS=init/start.S altio/altio.c +SRCS+=${DRIVERS} +SRCS+=main.c + +OBJS=${patsubst %, ${BUILD}/${PROGRAM}/%.o, ${SRCS}} +DEPS=${patsubst %, ${BUILD}/${PROGRAM}/%.d, ${SRCS}} + +CFLAGS+=-march=${ARCH} -mabi=${ABI} -mcmodel=${CMODEL} +CFLAGS+=-nostartfiles -nostdlib +CFLAGS+=-DSTACK_SIZE=1024 +CFLAGS+=-T${PLATFORM}/platform.ld -Tlinker.ld +CFLAGS+=-Wl,--no-warn-rwx-segments + +INC=-Iinc -I${COMMON}/inc + +ELF=${BUILD}/${PROGRAM}.elf +BIN=${ELF:.elf=.bin} +DA=${ELF:.elf=.da} + +all: ${ELF} ${BIN} ${DA} + +elf: ${ELF} +bin: ${BIN} +da: ${DA} + +${BUILD}/${PROGRAM}/%.S.o: %.S + @mkdir -p ${@D} + @echo "CC $@" + @${CC} ${CFLAGS} ${INC} -MMD -c -o $@ $< + +${BUILD}/${PROGRAM}/%.c.o: %.c + @mkdir -p ${@D} + @echo "CC $@" + @${CC} ${CFLAGS} ${INC} -MMD -c -o $@ $< + +${ELF}: ${OBJS} + @mkdir -p ${@D} + @echo "CC $@" + @${CC} ${CFLAGS} -MMD ${INC} -o $@ ${OBJS} + +${BIN}: ${ELF} + @echo "OBJCOPY $@" + @${OBJCOPY} -O binary $< $@ + +${DA}: ${ELF} + @echo "OBJDUMP $@" + @${OBJDUMP} -D $< > $@ + +clean: + rm -f ${ELF} ${BIN} ${DA} ${OBJS} ${DEPS} + +.PHONY: all elf bin da clean + +-include ${DEPS} diff --git a/projects/hello/app1/linker.ld b/projects/hello/app1/linker.ld new file mode 100644 index 00000000..9a158252 --- /dev/null +++ b/projects/hello/app1/linker.ld @@ -0,0 +1,33 @@ +/* See LICENSE file for copyright and license details. */ +OUTPUT_ARCH(riscv) +ENTRY(_start) + +__global_pointer$ = MIN(_sdata + 0x800, MAX(_data + 0x800, _end - 0x800)); + +MEMORY { + RAM (rwx) : ORIGIN = 0x80020000, LENGTH = 0x10000 +} + +SECTIONS { + .text : { + *( .init ) + *( .text .text.* ) + } > RAM + + .data : { + _data = . ; + *( .data ) + *( .data.* ) + _sdata = . ; + *( .sdata ) + *( .sdata.* ) + } > RAM + + .bss : { + _bss = .; + _sbss = .; + *(.sbss .sbss.*) + *(.bss .bss.*) + _end = .; + } > RAM +} diff --git a/projects/hello/app1/src/main.c b/projects/hello/app1/src/main.c new file mode 100644 index 00000000..b2429c1f --- /dev/null +++ b/projects/hello/app1/src/main.c @@ -0,0 +1,7 @@ +#include "altio/altio.h" +#include "s3k/s3k.h" + +int main(void) +{ + alt_puts("hello, world from app1"); +} diff --git a/projects/hello/run.sh b/projects/hello/run.sh new file mode 100755 index 00000000..a8dc1200 --- /dev/null +++ b/projects/hello/run.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +KERNEL=build/s3k +APP0=build/app0 +APP1=build/app1 + +# Build the kernel and application +make all + +# Run the kernel, load the application as payload +qemu-system-riscv64 -M virt -smp 1 -m 128M \ + -nographic -bios none -kernel $KERNEL.elf \ + -device loader,file=$APP0.bin,addr=0x80010000 \ + -device loader,file=$APP1.bin,addr=0x80020000 \ + -s -S & + +st -e riscv64-unknown-elf-gdb \ + -ex "set confirm off" \ + -ex "set pagination off" \ + -ex "set output-radix 16" \ + -ex "symbol-file $KERNEL.elf" \ + -ex "add-symbol-file $APP0.elf" \ + -ex "add-symbol-file $APP1.elf" \ + -ex "j 0x80000000" \ + -ex "b _hang" \ + -ex "b *0x80010000" \ + -ex "b *0x80020000" \ + -ex "b handle_exception" \ + -ex "target remote localhost:1234" \ + -ex "layout split" \ + -ex "fs cmd" + +# After qemu, kill linguring processes. +pkill -P $$ diff --git a/projects/wcet/Makefile b/projects/wcet/Makefile new file mode 100644 index 00000000..901af9f5 --- /dev/null +++ b/projects/wcet/Makefile @@ -0,0 +1,17 @@ + +export CC=riscv64-unknown-elf-gcc +export OBJCOPY=riscv64-unknown-elf-objcopy + +all: s3k app0 + +s3k: + ${MAKE} -C ../../kernel BUILD=${abspath s3k} + +app0: + ${MAKE} -C app0 + +clean: + rm -rf s3k + ${MAKE} -C app0 clean + +.PHONY: clean app0 s3k diff --git a/projects/wcet/app0/Makefile b/projects/wcet/app0/Makefile new file mode 100644 index 00000000..b3e5befb --- /dev/null +++ b/projects/wcet/app0/Makefile @@ -0,0 +1,36 @@ +PROGRAM=app0 +ELF=${PROGRAM}.elf +BIN=${PROGRAM}.bin +COMMON=../../../common +LIBS3K=../../../libs3k +DRIVERS=../../../drivers + +PLATFORM=../../../plat/virt + +include ${PLATFORM}/platform.mk + +vpath %.c src + +SRCS=${COMMON}/start.S ${COMMON}/altio.c main.c +SRCS+=${PLATFORM_SRCS} + +CFLAGS+=-march=${ARCH} -mabi=${ABI} -mcmodel=${CMODEL} +CFLAGS+=-nostartfiles -nostdlib +CFLAGS+=-DSTACK_SIZE=1024 +CFLAGS+=-T${PLATFORM}/platform.ld -Tlinker.ld +CFLAGS+=-Wl,--no-warn-rwx-segments + +INC=-I${DRIVERS}/inc -I${COMMON}/inc -I${LIBS3K} + +all: ${ELF} ${BIN} + +${ELF}: ${SRCS} + $(CC) ${CFLAGS} ${INC} -o $@ ${filter %.S, $^} ${filter %.c, $^} + +${BIN}: ${ELF} + ${OBJCOPY} -O binary $< $@ + +clean: + rm -f ${ELF} ${BIN} + +.PHONY: clean diff --git a/projects/wcet/app0/linker.ld b/projects/wcet/app0/linker.ld new file mode 100644 index 00000000..b75694a6 --- /dev/null +++ b/projects/wcet/app0/linker.ld @@ -0,0 +1,33 @@ +/* See LICENSE file for copyright and license details. */ +OUTPUT_ARCH(riscv) +ENTRY(_start) + +__global_pointer$ = MIN(_sdata + 0x800, MAX(_data + 0x800, _end - 0x800)); + +MEMORY { + RAM (rwx) : ORIGIN = 0x80010000, LENGTH = 64K +} + +SECTIONS { + .text : { + *( .init ) + *( .text .text.* ) + } > RAM + + .data : { + _data = . ; + *( .data ) + *( .data.* ) + _sdata = . ; + *( .sdata ) + *( .sdata.* ) + } > RAM + + .bss : { + _bss = .; + _sbss = .; + *(.sbss .sbss.*) + *(.bss .bss.*) + _end = .; + } > RAM +} diff --git a/projects/wcet/app0/src/main.c b/projects/wcet/app0/src/main.c new file mode 100644 index 00000000..dd0c4e6c --- /dev/null +++ b/projects/wcet/app0/src/main.c @@ -0,0 +1,35 @@ +#include "altio.h" +#include "s3k.h" +#include "timer.h" + +#include + +int main(void) +{ + // Access UART and timer + uint64_t uart_addr = s3k_napot_encode(0x10000000, 0x8); + uint64_t time_addr = s3k_napot_encode(0x200bff8, 0x8); + s3k_drvcap(2, 16, s3k_mkpmp(uart_addr, S3K_RW)); + s3k_drvcap(3, 17, s3k_mkpmp(time_addr, 1)); + s3k_pmpset(16, 1); + s3k_pmpset(17, 2); + + alt_puts("hello, world"); + uint64_t old = -1; + uint64_t start, end, elapsed, wcet; + s3k_cap_t time = s3k_mktime(1, 0, 64); + while (1) { + s3k_revcap(5); + while (s3k_drvcap(5, 18, time)) + ; + asm volatile("csrr %0,cycle" : "=r"(start)); + s3k_revcap(5); + asm volatile("csrr %0,cycle" : "=r"(end)); + elapsed = end - start; + if (old == elapsed) + continue; + asm volatile("csrr %0,hpmcounter3" : "=r"(wcet)); + alt_printf("%X %X\n", elapsed, wcet); + old = elapsed; + } +} diff --git a/projects/wcet/run.sh b/projects/wcet/run.sh new file mode 100755 index 00000000..17c3d2cc --- /dev/null +++ b/projects/wcet/run.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +KERNEL_ELF=s3k/s3k.elf +PAYLOAD_ELF=app0/app0.elf +PAYLOAD_BIN=app0/app0.bin + +# Build the kernel and application +make -B + +# Run the kernel, load the application as payload +qemu-system-riscv64 -M virt -smp 1 -m 128M -icount 3 \ + -nographic -bios none -kernel $KERNEL_ELF \ + -device loader,file=$PAYLOAD_BIN,addr=0x80010000 + +# After qemu, kill linguring processes. +pkill -P $$ diff --git a/src/cap.c b/src/cap.c deleted file mode 100644 index 031cb675..00000000 --- a/src/cap.c +++ /dev/null @@ -1,132 +0,0 @@ -#include "cap.h" - -uint64_t pmp_napot_addr(uint64_t begin, uint64_t end) -{ - return (begin | (((end - begin) - 1) >> 1)) >> 2; -} - -uint64_t pmp_napot_begin(uint64_t addr) -{ - return ((addr + 1) & addr) << 2; -} - -uint64_t pmp_napot_end(uint64_t addr) -{ - return (((addr + 1) | addr) + 1) << 2; -} - -bool cap_time_parent(union cap parent, union cap child) -{ - return parent.type == CAPTY_TIME && child.type == CAPTY_TIME - && parent.time.begin <= child.time.begin - && child.time.end <= parent.time.end - && child.time.hartid == parent.time.hartid; -} - -bool cap_memory_parent(union cap parent, union cap child) -{ - if (parent.type == CAPTY_MEMORY && child.type == CAPTY_MEMORY) { - return parent.memory.offset == child.memory.offset - && parent.memory.begin <= child.memory.begin - && child.memory.end <= parent.memory.end - && ((parent.memory.rwx & child.memory.rwx) - == child.memory.rwx); - } - if (parent.type == CAPTY_MEMORY && child.type == CAPTY_PMP) { - uint64_t pmp_begin = pmp_napot_begin(child.pmp.addr); - uint64_t pmp_end = pmp_napot_end(child.pmp.addr); - uint64_t mem_begin = ((uint64_t)parent.memory.offset << 27) - + (parent.memory.begin << 12); - uint64_t mem_end = ((uint64_t)parent.memory.offset << 27) - + (parent.memory.end << 12); - return mem_begin <= pmp_begin && pmp_end <= mem_end; - } - return false; -} - -bool cap_monitor_parent(union cap parent, union cap child) -{ - return parent.type == CAPTY_MONITOR && child.type == CAPTY_MONITOR - && parent.monitor.begin <= child.monitor.begin - && child.monitor.end <= parent.monitor.end; -} - -bool cap_channel_parent(union cap parent, union cap child) -{ - if (parent.type == CAPTY_CHANNEL && child.type == CAPTY_CHANNEL) - return parent.channel.begin <= child.channel.begin - && child.channel.end <= parent.channel.end; - if (parent.type == CAPTY_CHANNEL && child.type == CAPTY_SOCKET) - return parent.channel.begin <= child.socket.channel - && child.socket.channel < parent.channel.end; - return false; -} - -bool cap_socket_parent(union cap parent, union cap child) -{ - return parent.type == CAPTY_SOCKET && child.type == CAPTY_SOCKET - && parent.socket.tag == 0 - && parent.socket.channel == child.socket.channel; -} - -bool cap_time_derive(union cap parent, union cap child) -{ - return parent.type == CAPTY_TIME && child.type == CAPTY_TIME - && parent.time.free == child.time.begin - && parent.time.free == child.time.free - && child.time.end <= parent.time.end - && child.time.hartid == parent.time.hartid; -} - -bool cap_memory_derive(union cap parent, union cap child) -{ - if (parent.type == CAPTY_MEMORY && child.type == CAPTY_MEMORY) { - return parent.memory.offset == child.memory.offset - && parent.memory.free == child.memory.begin - && parent.memory.free == child.memory.free - && child.memory.end <= parent.memory.end - && ((parent.memory.rwx & child.memory.rwx) - == child.memory.rwx); - } - if (parent.type == CAPTY_MEMORY && child.type == CAPTY_PMP) { - uint64_t pmp_begin = pmp_napot_begin(child.pmp.addr); - uint64_t pmp_end = pmp_napot_end(child.pmp.addr); - uint64_t pmp_rwx = child.pmp.cfg & 0x7; - uint64_t pmp_mode = child.pmp.cfg >> 3; - uint64_t mem_free = ((uint64_t)parent.memory.offset << 27) - + (parent.memory.free << 12); - uint64_t mem_end = ((uint64_t)parent.memory.offset << 27) - + (parent.memory.end << 12); - uint64_t mem_rwx = parent.memory.rwx; - return mem_free <= pmp_begin && pmp_end <= mem_end - && pmp_mode == 0x3 && (mem_rwx & pmp_rwx) == pmp_rwx; - } - return false; -} - -bool cap_monitor_derive(union cap parent, union cap child) -{ - return parent.type == CAPTY_MONITOR && child.type == CAPTY_MONITOR - && parent.monitor.free == child.monitor.begin - && parent.monitor.free == child.monitor.free - && child.monitor.end <= parent.monitor.end; -} - -bool cap_channel_derive(union cap parent, union cap child) -{ - if (parent.type == CAPTY_CHANNEL && child.type == CAPTY_CHANNEL) - return parent.channel.free == child.channel.begin - && parent.channel.free == child.channel.free - && child.channel.end <= parent.channel.end; - if (parent.type == CAPTY_CHANNEL && child.type == CAPTY_SOCKET) - return parent.channel.free == child.socket.channel - && child.socket.channel < parent.channel.end; - return false; -} - -bool cap_socket_derive(union cap parent, union cap child) -{ - return parent.type == CAPTY_SOCKET && child.type == CAPTY_SOCKET - && parent.socket.tag == 0 && child.socket.tag > 0 - && parent.socket.channel == child.socket.channel; -} diff --git a/src/cnode.c b/src/cnode.c deleted file mode 100644 index 1bf262d1..00000000 --- a/src/cnode.c +++ /dev/null @@ -1,153 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "cnode.h" - -#include "cap.h" -#include "common.h" -#include "consts.h" -#include "kassert.h" - -struct cnode { - cnode_handle_t prev, next; - union cap cap; -}; - -static volatile struct cnode _cnodes[NPROC * NCAP + 1]; - -static void _insert(cnode_handle_t curr, union cap cap, cnode_handle_t prev) -{ - cnode_handle_t next = _cnodes[prev].next; - _cnodes[curr].prev = prev; - _cnodes[curr].next = next; - _cnodes[prev].next = curr; - _cnodes[next].prev = curr; - _cnodes[curr].cap = cap; -} - -void _delete(cnode_handle_t curr) -{ - cnode_handle_t prev = _cnodes[curr].prev; - cnode_handle_t next = _cnodes[curr].next; - _cnodes[next].prev = prev; - _cnodes[prev].next = next; - - _cnodes[curr].cap.raw = 0; - _cnodes[curr].prev = 0; - _cnodes[curr].next = 0; -} - -static void _move(cnode_handle_t src, cnode_handle_t dst) -{ - cnode_handle_t prev = _cnodes[src].prev; - union cap cap = _cnodes[src].cap; - cnode_handle_t next = _cnodes[src].next; - - // update destination node - _cnodes[dst].prev = prev; - _cnodes[dst].cap = cap; - _cnodes[dst].next = next; - - // update previous and next node - _cnodes[prev].next = dst; - _cnodes[next].prev = dst; - - _cnodes[src].cap.raw = 0; - _cnodes[src].prev = 0; - _cnodes[src].next = 0; -} - -static bool _contains(cnode_handle_t curr) -{ - return _cnodes[curr].cap.type != 0; -} - -void cnode_init(const union cap *caps, size_t size) -{ - // zero cnodes - for (int i = 0; i < NPROC * NCAP; ++i) - _cnodes[i] = (struct cnode){ 0 }; - // Initialize root node - cnode_handle_t root = NPROC * NCAP; - _cnodes[root].prev = root; - _cnodes[root].next = root; - // Add initial nodes - int prev = root; - for (cnode_handle_t i = 0; i < size; i++) { - _insert(i, caps[i], prev); - prev = i; - } -} - -// Handle is just index to corresponding element in cnodes -cnode_handle_t cnode_get_handle(cnode_handle_t pid, cnode_handle_t idx) -{ - kassert(pid < NPROC); - return pid * NCAP + (idx % NCAP); -} - -cnode_handle_t cnode_get_pid(cnode_handle_t handle) -{ - kassert(handle < NPROC * NCAP); - return handle / NCAP; -} - -cnode_handle_t cnode_get_next(cnode_handle_t handle) -{ - kassert(handle < NPROC * NCAP); - return _cnodes[handle].next; -} - -union cap cnode_get_cap(cnode_handle_t handle) -{ - kassert(handle < NPROC * NCAP); - return _cnodes[handle].cap; -} - -void cnode_set_cap(cnode_handle_t handle, union cap cap) -{ - kassert(cap.raw != 0); - kassert(cnode_contains(handle)); - _cnodes[handle].cap = cap; -} - -bool cnode_contains(cnode_handle_t handle) -{ - kassert(handle < NPROC * NCAP); - return _contains(handle); -} - -void cnode_insert(cnode_handle_t curr, union cap cap, cnode_handle_t prev) -{ - kassert(curr < NPROC * NCAP); - kassert(prev < NPROC * NCAP); - kassert(cap.raw != 0); - kassert(!_contains(curr)); - kassert(_contains(prev)); - - _insert(curr, cap, prev); -} - -void cnode_move(cnode_handle_t src, cnode_handle_t dst) -{ - kassert(src < NPROC * NCAP); - kassert(dst < NPROC * NCAP); - kassert(_contains(src)); - kassert(!_contains(dst)); - _move(src, dst); -} - -void cnode_delete(cnode_handle_t curr) -{ - kassert(curr < NPROC * NCAP); - kassert(_contains(curr)); - - _delete(curr); -} - -bool cnode_delete_if(cnode_handle_t curr, cnode_handle_t prev) -{ - kassert(curr < NPROC * NCAP); - if (!_contains(curr) || _cnodes[curr].prev != prev) - return false; - _delete(curr); - return true; -} diff --git a/src/current.c b/src/current.c deleted file mode 100644 index 35bb2438..00000000 --- a/src/current.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "current.h" - -register struct proc *current asm("tp"); - -void current_set(struct proc *proc) -{ - current = proc; -} - -struct proc *current_get(void) -{ - return current; -} diff --git a/src/exception.c b/src/exception.c deleted file mode 100644 index 1c470efe..00000000 --- a/src/exception.c +++ /dev/null @@ -1,58 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "exception.h" - -#include "proc.h" -#include "trap.h" - -#define ILLEGAL_INSTRUCTION 0x2 - -#define MRET 0x30200073 -#define SRET 0x10200073 -#define URET 0x00200073 - -/** - * This function restores the program counter and stack pointer to their values - * prior to the exception, and clears the exception cause and exception value - * registers. - */ -static void handle_ret(struct proc *proc) -{ - proc->regs[REG_PC] = proc->regs[REG_EPC]; - proc->regs[REG_SP] = proc->regs[REG_ESP]; - proc->regs[REG_ECAUSE] = 0; - proc->regs[REG_EVAL] = 0; - proc->regs[REG_EPC] = 0; - proc->regs[REG_ESP] = 0; -} - -/* - * This function is called when an exception occurs that doesn't fall under the - * category of an illegal instruction return, such as a page fault or a timer - * interrupt. It updates the exception cause, value, program counter, and stack - * pointer in the process's registers, and switches to the trap handler program - * counter and stack pointer. - */ -static void handle_default(struct proc *proc, uint64_t mcause, uint64_t mepc, - uint64_t mtval) -{ - proc->regs[REG_ECAUSE] = mcause; - proc->regs[REG_EVAL] = mtval; - proc->regs[REG_EPC] = proc->regs[REG_PC]; - proc->regs[REG_ESP] = proc->regs[REG_SP]; - proc->regs[REG_PC] = proc->regs[REG_TPC]; - proc->regs[REG_SP] = proc->regs[REG_TSP]; -} - -void handle_exception(struct proc *proc, uint64_t mcause, uint64_t mepc, - uint64_t mtval) -{ - /* Check if it is a return from exception */ - if (mcause == ILLEGAL_INSTRUCTION - && (mtval == MRET || mtval == SRET || mtval == URET)) { - // Handle return from exception - handle_ret(proc); - } else { - // Handle default exception - handle_default(proc, mcause, mepc, mtval); - } -} diff --git a/src/head.S b/src/head.S deleted file mode 100644 index 8c200622..00000000 --- a/src/head.S +++ /dev/null @@ -1,54 +0,0 @@ -// See LICENSE file for copyright and license details. -#include "stack.h" -#include "macro.inc" -.globl _start -.globl stack_top - -.extern init_kernel -.extern sched_next -.extern trap_entry -.extern trap_exit - -.section .text.init - -_start: - // Load the global and stack pointers. - .option push - .option norelax - la gp, __global_pointer$ - .option pop - load_sp t0 - - - - // Set trap entry/handler. - la t0, trap_entry - csrw mtvec, t0 - - // Disable interrupts in machine mode. - csrw mstatus, 0 - // Enable timer interrupts for user mode. - li t0, (1<<7) - csrw mie, t0 - - // write zeros to be bss section - la t0, _bss - la t1, _end - j 2f -1: sd zero, (t0) - addi t0, t0, 8 -2: bne t0, t1, 1b - - // Initialize the kernel. - la a0,__payload - call init_kernel - // Schedule the boot process. - call schedule_next - // Load the boot process. - tail trap_exit - -.section .bss.stack -.balign 8 -stack_start: - .space (STACK_SIZE * NHART) -stack_top: diff --git a/src/init.c b/src/init.c deleted file mode 100644 index 681ca90f..00000000 --- a/src/init.c +++ /dev/null @@ -1,30 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "init.h" - -#include "altio.h" -#include "cnode.h" -#include "common.h" -#include "csr.h" -#include "init_caps.h" -#include "proc.h" -#include "schedule.h" -#include "ticket_lock.h" - -void init_kernel(uint64_t payload) -{ - static struct ticket_lock lock; - static volatile int done = 0; - - tl_acq(&lock); - if (!done) { - alt_printf("s3k(0x%X): Setting up.\n", csrr_mhartid()); - proc_init(payload); - cnode_init(init_caps, ARRAY_SIZE(init_caps)); - schedule_init(); - __sync_synchronize(); - alt_printf("s3k(0x%X): Setup complete.\n", csrr_mhartid()); - done = 1; - } - alt_printf("s3k(0x%X): Running.\n", csrr_mhartid()); - tl_rel(&lock); -} diff --git a/src/kassert.c b/src/kassert.c deleted file mode 100644 index 6054adbc..00000000 --- a/src/kassert.c +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef NDEBUG -#include "kassert.h" - -#include "altio.h" - -void kassert_failure(const char *file, uint64_t line, const char *expr) -{ - alt_printf("Assertion failed: %s, file %s, line 0x%X\n", expr, file, - line); - while (1) { - } // halt the system -} - -#endif /* NDEBUG */ diff --git a/src/proc.c b/src/proc.c deleted file mode 100644 index 42a1c6dc..00000000 --- a/src/proc.c +++ /dev/null @@ -1,110 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "proc.h" - -#include "cnode.h" -#include "csr.h" -#include "kassert.h" - -static struct proc _processes[NPROC]; - -void proc_init(uint64_t payload) -{ - for (int i = 1; i < NPROC; i++) { - _processes[i] - = (struct proc){ .pid = i, .state = PS_SUSPENDED }; - } - _processes[0] = (struct proc){ .pid = 0, .state = PS_READY }; - _processes[0].regs[REG_PC] = payload; -} - -struct proc *proc_get(uint64_t pid) -{ - kassert(pid < NPROC); - return &_processes[pid]; -} - -bool proc_acquire(struct proc *proc, uint64_t expected) -{ - kassert(!(expected & PSF_BUSY)); - // Set the busy flag if expected state - uint64_t desired = expected | PSF_BUSY; - return __atomic_compare_exchange_n(&proc->state, &expected, desired, - false /* not weak */, - __ATOMIC_ACQUIRE /* succ */, - __ATOMIC_RELAXED /* fail */); -} - -void proc_release(struct proc *proc) -{ - // Unset all flags except suspend flag. - __atomic_fetch_and(&proc->state, PSF_SUSPEND, __ATOMIC_RELEASE); -} - -void proc_suspend(struct proc *proc) -{ - // Set the suspend flag - uint64_t prev_state - = __atomic_fetch_or(&proc->state, PSF_SUSPEND, __ATOMIC_ACQUIRE); - - // If the process was waiting, we also unset the waiting flag. - if ((prev_state & 0xFF) == PSF_WAITING) { - proc->state = PSF_SUSPEND; - __atomic_thread_fence(__ATOMIC_ACQUIRE); - } -} - -void proc_resume(struct proc *proc) -{ - // Unset the suspend flag - __atomic_fetch_and(&proc->state, ~PSF_SUSPEND, __ATOMIC_RELEASE); -} - -bool proc_ipc_wait(struct proc *proc, uint64_t channel_id) -{ - // We expect that the process is running as usual. - uint64_t expected = PSF_BUSY; - // We set the - uint64_t desired = (channel_id << 48) | PSF_WAITING; - // This should fail only if the process should be suspended. - return __atomic_compare_exchange_n(&proc->state, &expected, desired, - false /* not weak */, - __ATOMIC_SEQ_CST /* succ */, - __ATOMIC_RELAXED /* fail */); -} - -bool proc_ipc_acquire(struct proc *proc, uint64_t channel_id) -{ - // We expect that the process is waiting on channel_id. - // Channel ID is set to the most significant bytes. - uint64_t expected = (channel_id << 48) | PSF_WAITING; - return proc_acquire(proc, expected); -} - -void proc_load_pmp(const struct proc *proc) -{ - uint64_t pmp = proc->regs[REG_PMP]; - uint64_t pmpaddr[8]; - uint64_t pmpcfg = 0; - cnode_handle_t handle; - union cap cap; - for (int i = 0; i < 8; i++) { - handle = cnode_get_handle(proc->pid, pmp & 0xFF); - cap = cnode_get_cap(handle); - if (cap.type == CAPTY_PMP) { - pmpcfg |= (uint64_t)cap.pmp.cfg << (i * 8); - pmpaddr[i] = cap.pmp.addr; - } else { - pmpaddr[i] = 0; - } - pmp >>= 8; - } - csrw_pmpcfg0(pmpcfg); - csrw_pmpaddr0(pmpaddr[0]); - csrw_pmpaddr1(pmpaddr[1]); - csrw_pmpaddr2(pmpaddr[2]); - csrw_pmpaddr3(pmpaddr[3]); - csrw_pmpaddr4(pmpaddr[4]); - csrw_pmpaddr5(pmpaddr[5]); - csrw_pmpaddr6(pmpaddr[6]); - csrw_pmpaddr7(pmpaddr[7]); -} diff --git a/src/schedule.c b/src/schedule.c deleted file mode 100644 index 345daf14..00000000 --- a/src/schedule.c +++ /dev/null @@ -1,82 +0,0 @@ -/* See LICENSE file for copyright and license details. */ - -#include "schedule.h" - -#include "consts.h" -#include "csr.h" -#include "current.h" -#include "platform.h" -#include "proc.h" -#include "timer.h" -#include "trap.h" -#include "wfi.h" - -#define NONE_PID 0xFF - -static volatile struct sched_entry _schedule[NHART][NSLICE]; - -struct sched_entry schedule_get(uint64_t hartid, size_t i) -{ - return _schedule[hartid][i]; -} - -void schedule_update(uint64_t hartid, uint64_t pid, uint64_t begin, - uint64_t end) -{ - for (uint64_t i = begin; i < end; i++) { - _schedule[hartid][i] = (struct sched_entry){ pid, end - i }; - } -} - -void schedule_delete(uint64_t hartid, uint64_t begin, uint64_t end) -{ - schedule_update(hartid, NONE_PID, begin, end); -} - -void schedule_next() -{ - uint64_t hartid = csrr_mhartid(); - uint64_t quantum; - struct proc *proc; - struct sched_entry entry; -retry: - for (;;) { - quantum = (time_get() + NSLACK) / NTICK; - entry = schedule_get(hartid, quantum % NSLICE); - if (entry.pid == NONE_PID) - continue; - proc = proc_get(entry.pid); - if (proc->sleep > time_get()) - continue; - if (proc_acquire(proc, PS_READY)) - break; - } - proc_load_pmp(proc); - if (!csrr_pmpcfg0()) { - // Temporary fix. QEMU does not allow this to be zero. - proc_release(proc); - goto retry; - } - uint64_t start_time = quantum * NTICK; - uint64_t end_time = start_time + entry.len * NTICK - NSLACK; - - current_set(proc); - timeout_set(hartid, start_time); - while (!(csrr_mip() & (1 << 7))) { - wfi(); - } - timeout_set(hartid, end_time); -} - -void schedule_yield(struct proc *proc) -{ - proc_release(proc); - schedule_next(); -} - -void schedule_init(void) -{ - for (int i = 0; i < NHART; i++) { - schedule_update(i, 0, 0, NSLICE); - } -} diff --git a/src/syscall.c b/src/syscall.c deleted file mode 100644 index 8d817b3c..00000000 --- a/src/syscall.c +++ /dev/null @@ -1,371 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "syscall.h" - -#include "cap.h" -#include "common.h" -#include "consts.h" -#include "csr.h" -#include "current.h" -#include "schedule.h" -#include "timer.h" -#include "trap.h" - -extern struct proc *_listeners[NCHANNEL]; - -/*** System call handlers ***/ -static void _getreg(struct proc *proc, uint64_t reg) -{ - reg %= REG_COUNT; - proc->regs[REG_A0] = proc->regs[reg]; -} - -static void _setreg(struct proc *proc, uint64_t reg, uint64_t val) -{ - reg %= REG_COUNT; - proc->regs[reg] = val; - if (reg == REG_PMP) - proc_load_pmp(proc); -} - -void syscall_proc(struct proc *proc, uint64_t a1, uint64_t a2, uint64_t a3) -{ - switch (a1) { - case 0: /* Get process ID */ - proc->regs[REG_A0] = proc->pid; - break; - case 1: /* Read register */ - _getreg(proc, a2); - break; - case 2: /* Set register */ - _setreg(proc, a2, a3); - break; - case 3: /* Get HartID */ - proc->regs[REG_A0] = csrr_mhartid(); - break; - case 4: /* Get RTC */ - proc->regs[REG_A0] = time_get(); - break; - case 5: /* Get timeout */ - proc->regs[REG_A0] = timeout_get(csrr_mhartid()); - break; - case 6: /* Yield the remaining time slice */ - proc->sleep = timeout_get(csrr_mhartid()); - schedule_yield(proc); - break; - case 7: /* Suspend */ - proc_suspend(proc); - schedule_yield(proc); - break; - default: - proc->regs[REG_A0] = 0; - break; - } -} - -void syscall_getcap(struct proc *proc, uint64_t idx) -{ - cnode_handle_t handle = cnode_get_handle(proc->pid, idx); - proc->regs[REG_A0] = cnode_get_cap(handle).raw; -} - -void syscall_movcap(struct proc *proc, uint64_t srcIdx, uint64_t dstIdx) -{ - // Get handles - cnode_handle_t src_handle = cnode_get_handle(proc->pid, srcIdx); - cnode_handle_t dst_handle = cnode_get_handle(proc->pid, dstIdx); - - if (!cnode_contains(src_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - if (cnode_contains(dst_handle)) { - proc->regs[REG_A0] = EXCPT_COLLISION; - return; - } - syscall_lock(); - if (cnode_contains(src_handle)) { - cnode_move(src_handle, dst_handle); - proc->regs[REG_A0] = EXCPT_NONE; - } else { - proc->regs[REG_A0] = EXCPT_EMPTY; - } - syscall_unlock(); -} - -void syscall_delcap(struct proc *proc, uint64_t idx) -{ - cnode_handle_t handle = cnode_get_handle(proc->pid, idx); - union cap cap = cnode_get_cap(handle); - if (!cnode_contains(handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - syscall_lock(); - if (cnode_contains(handle)) { - cnode_delete(handle); - if (cap.type == CAPTY_TIME) - schedule_delete(cap.time.hartid, cap.time.free, - cap.time.end); - proc->regs[REG_A0] = EXCPT_NONE; - } else { - proc->regs[REG_A0] = EXCPT_EMPTY; - } - syscall_unlock(); -} - -static void _revoke_time_hook(cnode_handle_t handle, union cap cap, - union cap child_cap) -{ - cap.time.free = child_cap.time.free; - cnode_set_cap(handle, cap); - schedule_update(cap.time.hartid, cnode_get_pid(handle), cap.time.free, - cap.time.end); -} - -static void _revoke_time_post_hook(cnode_handle_t handle, union cap cap) -{ - cap.time.free = cap.time.begin; - cnode_set_cap(handle, cap); - schedule_update(cap.time.hartid, cnode_get_pid(handle), cap.time.free, - cap.time.end); -} - -static void _revoke_memory_hook(cnode_handle_t handle, union cap cap, - union cap child_cap) -{ - if (cap.type == CAPTY_MEMORY) { - cap.memory.free = child_cap.memory.free; // Inherit free region - cap.memory.lock = child_cap.memory.lock; // Inherit lock - cnode_set_cap(handle, cap); - } -} - -static void _revoke_memory_post_hook(cnode_handle_t handle, union cap cap) -{ - cap.memory.free = cap.memory.begin; - cap.memory.lock = 0; - cnode_set_cap(handle, cap); -} - -static void _revoke_monitor_hook(cnode_handle_t handle, union cap cap, - union cap child_cap) -{ - cap.monitor.free = child_cap.monitor.free; - cnode_set_cap(handle, cap); -} - -static void _revoke_monitor_post_hook(cnode_handle_t handle, union cap cap) -{ - cap.monitor.free = cap.monitor.begin; - cnode_set_cap(handle, cap); -} - -static void _revoke_channel_hook(cnode_handle_t handle, union cap cap, - union cap child_cap) -{ - if (child_cap.type == CAPTY_CHANNEL) { - cap.channel.free = child_cap.channel.free; - cnode_set_cap(handle, cap); - } else if (child_cap.type == CAPTY_SOCKET - && child_cap.socket.tag == 0) { - cap.channel.free = child_cap.socket.channel; - _listeners[child_cap.socket.channel] = NULL; - } -} - -static void _revoke_channel_post_hook(cnode_handle_t handle, union cap cap) -{ - cap.channel.free = cap.channel.begin; - cnode_set_cap(handle, cap); -} - -static void _revoke_socket_hook(cnode_handle_t handle, union cap cap, - union cap child_cap) -{ - /* This should be empty */ -} - -static void _revoke_socket_post_hook(cnode_handle_t handle, union cap cap) -{ - /* This should be empty */ -} - -void syscall_revcap(struct proc *proc, uint64_t idx) -{ - // Get handle - cnode_handle_t handle = cnode_get_handle(proc->pid, idx); - union cap cap = cnode_get_cap(handle); - - // If empty slot - if (!cnode_contains(handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - - bool (*is_parent)(union cap, union cap); - void (*hook)(cnode_handle_t, union cap, union cap); - void (*post_hook)(cnode_handle_t, union cap); - switch (cap.type) { - case CAPTY_TIME: - is_parent = cap_time_parent; - hook = _revoke_time_hook; - post_hook = _revoke_time_post_hook; - break; - case CAPTY_MEMORY: - is_parent = cap_memory_parent; - hook = _revoke_memory_hook; - post_hook = _revoke_memory_post_hook; - break; - case CAPTY_MONITOR: - is_parent = cap_monitor_parent; - hook = _revoke_monitor_hook; - post_hook = _revoke_monitor_post_hook; - break; - case CAPTY_CHANNEL: - is_parent = cap_channel_parent; - hook = _revoke_channel_hook; - post_hook = _revoke_channel_post_hook; - break; - case CAPTY_SOCKET: - is_parent = cap_socket_parent; - hook = _revoke_socket_hook; - post_hook = _revoke_socket_post_hook; - break; - default: - proc->regs[REG_A0] = EXCPT_NONE; - return; - } - - cnode_handle_t next_handle; - union cap next_cap; - while (cnode_contains(handle)) { - next_handle = cnode_get_next(handle); - if (next_handle == CNODE_ROOT_HANDLE) - break; - next_cap = cnode_get_cap(next_handle); - if (!is_parent(cap, next_cap)) - break; - syscall_lock(); - if (cnode_delete_if(next_handle, handle)) - hook(handle, cap, next_cap); - syscall_unlock(); - } - - if (!cnode_contains(handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - syscall_lock(); - if (cnode_contains(handle)) { - post_hook(handle, cap); - proc->regs[REG_A0] = EXCPT_NONE; - } else { - proc->regs[REG_A0] = EXCPT_EMPTY; - } - syscall_unlock(); -} - -static void _derive_time(cnode_handle_t orig_handle, union cap orig_cap, - cnode_handle_t drv_handle, union cap drv_cap) -{ - orig_cap.time.free = drv_cap.time.end; - cnode_set_cap(orig_handle, orig_cap); - schedule_update(drv_cap.time.hartid, cnode_get_pid(orig_handle), - drv_cap.time.begin, drv_cap.time.end); -} - -static void _derive_memory(cnode_handle_t orig_handle, union cap orig_cap, - cnode_handle_t drv_handle, union cap drv_cap) -{ - if (drv_cap.type == CAPTY_MEMORY) { // Memory - orig_cap.memory.free = drv_cap.memory.end; - } else { // PMP - orig_cap.memory.lock = true; - } - cnode_set_cap(orig_handle, orig_cap); -} - -static void _derive_monitor(cnode_handle_t orig_handle, union cap orig_cap, - cnode_handle_t drv_handle, union cap drv_cap) -{ - orig_cap.monitor.free = drv_cap.monitor.end; - cnode_set_cap(orig_handle, orig_cap); -} - -static void _derive_channel(cnode_handle_t orig_handle, union cap orig_cap, - cnode_handle_t drv_handle, union cap drv_cap) -{ - // Update free pointer. - if (drv_cap.type == CAPTY_CHANNEL) { - orig_cap.channel.free = drv_cap.channel.end; - } else { - orig_cap.channel.free = drv_cap.socket.channel + 1; - _listeners[drv_cap.socket.channel] = current_get(); - } - cnode_set_cap(orig_handle, orig_cap); -} - -static void _derive_socket(cnode_handle_t orig_handle, union cap orig_cap, - cnode_handle_t drv_handle, union cap drv_cap) -{ - /* This should be empty */ -} - -void syscall_drvcap(struct proc *proc, uint64_t orig_idx, uint64_t drv_idx, - union cap drv_cap) -{ - // Get handle - cnode_handle_t orig_handle = cnode_get_handle(proc->pid, orig_idx); - cnode_handle_t drv_handle = cnode_get_handle(proc->pid, drv_idx); - union cap orig_cap = cnode_get_cap(orig_handle); - - if (!cnode_contains(orig_handle)) { // If empty slot - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } else if (cnode_contains(drv_handle)) { // If destination occupied - proc->regs[REG_A0] = EXCPT_COLLISION; - return; - } - - void (*hook)(cnode_handle_t, union cap, cnode_handle_t, union cap); - bool (*can_derive)(union cap, union cap); - // Call specific handler for capability or return unimplemented. - switch (orig_cap.type) { - case CAPTY_TIME: - hook = _derive_time; - can_derive = cap_time_derive; - break; - case CAPTY_MEMORY: - hook = _derive_memory; - can_derive = cap_memory_derive; - break; - case CAPTY_MONITOR: - hook = _derive_monitor; - can_derive = cap_monitor_derive; - break; - case CAPTY_CHANNEL: - hook = _derive_channel; - can_derive = cap_channel_derive; - break; - case CAPTY_SOCKET: - hook = _derive_socket; - can_derive = cap_socket_derive; - break; - default: - proc->regs[REG_A0] = EXCPT_UNIMPLEMENTED; - return; - } - if (!can_derive(orig_cap, drv_cap)) { - proc->regs[REG_A0] = EXCPT_DERIVATION; - } else { - syscall_lock(); - if (!cnode_contains(orig_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - } else { - cnode_insert(drv_handle, drv_cap, orig_handle); - hook(orig_handle, orig_cap, drv_handle, drv_cap); - proc->regs[REG_A0] = EXCPT_NONE; - } - syscall_unlock(); - } -} diff --git a/src/syscall_ipc.c b/src/syscall_ipc.c deleted file mode 100644 index 7a99df2a..00000000 --- a/src/syscall_ipc.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "kassert.h" -#include "proc.h" -#include "schedule.h" -#include "syscall.h" - -struct proc *_listeners[NCHANNEL]; - -void syscall_recv(struct proc *proc, uint64_t recv_idx, uint64_t cap_dest) -{ - cnode_handle_t recv_cap_handle = cnode_get_handle(proc->pid, recv_idx); - union cap recv_cap = cnode_get_cap(recv_cap_handle); - if (!cnode_contains(recv_cap_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - if (recv_cap.type != CAPTY_SOCKET || recv_cap.socket.tag != 0) { - proc->regs[REG_A0] = EXCPT_INVALID_CAP; - return; - } - uint64_t channel = recv_cap.socket.channel; - syscall_lock(); - if (!cnode_contains(recv_cap_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - syscall_unlock(); - return; - } - - kassert(_listeners[channel] == proc); - - proc->regs[REG_A0] = EXCPT_PREEMPTED; - proc->cap_dest = cap_dest; - if (proc_ipc_wait(proc, channel)) { - // Waiting success - syscall_unlock(); - schedule_next(); - } else { - // Waiting failed, process is suspended. - syscall_unlock(); - schedule_yield(proc); - } -} - -void syscall_send(struct proc *proc, uint64_t send_idx, uint64_t msg0, - uint64_t msg1, uint64_t msg2, uint64_t msg3, uint64_t cap_src, - uint64_t yield) -{ - cnode_handle_t send_cap_handle = cnode_get_handle(proc->pid, send_idx); - union cap send_cap = cnode_get_cap(send_cap_handle); - if (!cnode_contains(send_cap_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - if (send_cap.type != CAPTY_SOCKET || send_cap.socket.tag == 0) { - proc->regs[REG_A0] = EXCPT_INVALID_CAP; - return; - } - uint64_t channel = send_cap.socket.channel; - struct proc *receiver = _listeners[channel]; - if (receiver == NULL || !proc_ipc_acquire(receiver, channel)) { - proc->regs[REG_A0] = EXCPT_NO_RECEIVER; - return; - } - - syscall_lock(); - if (!cnode_contains(send_cap_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - syscall_unlock(); - return; - } - // Check if we can send a capability. - uint64_t cap_dest = receiver->cap_dest; - cnode_handle_t dest_handle = cnode_get_handle(receiver->pid, cap_dest); - cnode_handle_t src_handle = cnode_get_handle(proc->pid, cap_src); - if (cap_src != -1ull) { - if (cnode_contains(dest_handle) - || !cnode_contains(src_handle)) { - proc->regs[REG_A0] = EXCPT_SEND_CAP; - syscall_unlock(); - return; - } - union cap cap = cnode_get_cap(src_handle); - cnode_move(src_handle, dest_handle); - if (cap.type == CAPTY_TIME) { - schedule_update(cap.time.hartid, receiver->pid, - cap.time.free, cap.time.end); - } - if (cap.type == CAPTY_SOCKET && cap.socket.tag == 0) { - _listeners[cap.socket.channel] = receiver; - } - } - receiver->regs[REG_A0] = EXCPT_NONE; - receiver->regs[REG_A1] = msg0; - receiver->regs[REG_A2] = msg1; - receiver->regs[REG_A3] = msg2; - receiver->regs[REG_A4] = msg3; - receiver->regs[REG_A5] = send_cap.socket.tag; - proc_release(receiver); - proc->regs[REG_A0] = EXCPT_NONE; - syscall_unlock(); - if (yield) - schedule_yield(proc); -} - -void syscall_sendrecv(struct proc *proc, uint64_t sendrecv_idx, uint64_t msg0, - uint64_t msg1, uint64_t msg2, uint64_t msg3, - uint64_t send_cap, uint64_t dest_cap) -{ - syscall_send(proc, sendrecv_idx >> 16, msg0, msg1, msg2, msg3, send_cap, - false); - syscall_recv(proc, sendrecv_idx & 0xFFFF, dest_cap); -} diff --git a/src/syscall_lock.c b/src/syscall_lock.c deleted file mode 100644 index b3abe461..00000000 --- a/src/syscall_lock.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "ticket_lock.h" - -// Lock used for capability operations. -static struct ticket_lock _lock; - -void syscall_lock(void) -{ - tl_acq(&_lock); -} - -void syscall_unlock(void) -{ - tl_rel(&_lock); -} diff --git a/src/syscall_monitor.c b/src/syscall_monitor.c deleted file mode 100644 index d647f4ef..00000000 --- a/src/syscall_monitor.c +++ /dev/null @@ -1,275 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "cap.h" -#include "common.h" -#include "consts.h" -#include "csr.h" -#include "schedule.h" -#include "syscall.h" -#include "timer.h" -#include "trap.h" - -extern struct proc *_listeners[NCHANNEL]; - -void syscall_msuspend(struct proc *proc, uint64_t mon_idx, uint64_t pid) -{ - cnode_handle_t mon_handle = cnode_get_handle(proc->pid, mon_idx); - union cap cap = cnode_get_cap(mon_handle); - - if (!cnode_contains(mon_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - if (cap.type != CAPTY_MONITOR) { - proc->regs[REG_A0] = EXCPT_UNIMPLEMENTED; - return; - } - if (cap.monitor.free > pid || pid >= cap.monitor.end) { - proc->regs[REG_A0] = EXCPT_MPID; - return; - } - - struct proc *other_proc = proc_get(pid); - syscall_lock(); - if (!cnode_contains(mon_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - proc_suspend(other_proc); - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_NONE; -} - -void syscall_mresume(struct proc *proc, uint64_t mon_idx, uint64_t pid) -{ - cnode_handle_t mon_handle = cnode_get_handle(proc->pid, mon_idx); - union cap cap = cnode_get_cap(mon_handle); - - if (!cnode_contains(mon_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - if (cap.type != CAPTY_MONITOR) { - proc->regs[REG_A0] = EXCPT_UNIMPLEMENTED; - return; - } - if (cap.monitor.free > pid || pid >= cap.monitor.end) { - proc->regs[REG_A0] = EXCPT_MPID; - return; - } - - struct proc *other_proc = proc_get(pid); - syscall_lock(); - if (!cnode_contains(mon_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - proc_resume(other_proc); - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_NONE; -} - -void syscall_mgetreg(struct proc *proc, uint64_t mon_idx, uint64_t pid, - uint64_t reg) -{ - cnode_handle_t mon_handle = cnode_get_handle(proc->pid, mon_idx); - union cap cap = cnode_get_cap(mon_handle); - if (!cnode_contains(mon_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - if (cap.type != CAPTY_MONITOR) { - proc->regs[REG_A0] = EXCPT_UNIMPLEMENTED; - return; - } - if (cap.monitor.free > pid || pid >= cap.monitor.end) { - proc->regs[REG_A0] = EXCPT_MPID; - return; - } - syscall_lock(); - if (!cnode_contains(mon_handle)) { - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - - struct proc *other_proc = proc_get(pid); - if (!proc_acquire(other_proc, PS_SUSPENDED)) { - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_MBUSY; - return; - } - proc->regs[REG_A1] = other_proc->regs[reg % REG_COUNT]; - proc_release(other_proc); - __sync_fetch_and_and(&other_proc->state, ~PSF_BUSY); - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_NONE; -} - -void syscall_msetreg(struct proc *proc, uint64_t mon_idx, uint64_t pid, - uint64_t reg, uint64_t val) -{ - cnode_handle_t mon_handle = cnode_get_handle(proc->pid, mon_idx); - union cap cap = cnode_get_cap(mon_handle); - if (!cnode_contains(mon_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - if (cap.type != CAPTY_MONITOR) { - proc->regs[REG_A0] = EXCPT_UNIMPLEMENTED; - return; - } - if (cap.monitor.free > pid || pid >= cap.monitor.end) { - proc->regs[REG_A0] = EXCPT_MPID; - return; - } - - syscall_lock(); - if (!cnode_contains(mon_handle)) { - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - struct proc *other_proc = proc_get(pid); - if (!proc_acquire(other_proc, PS_SUSPENDED)) { - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_MBUSY; - return; - } - other_proc->regs[reg % REG_COUNT] = val; - proc_release(other_proc); - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_NONE; -} - -void syscall_mgetcap(struct proc *proc, uint64_t mon_idx, uint64_t pid, - uint64_t node_idx) -{ - cnode_handle_t mon_handle = cnode_get_handle(proc->pid, mon_idx); - union cap cap = cnode_get_cap(mon_handle); - if (!cnode_contains(mon_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - if (cap.type != CAPTY_MONITOR) { - proc->regs[REG_A0] = EXCPT_UNIMPLEMENTED; - return; - } - if (cap.monitor.free > pid || pid >= cap.monitor.end) { - proc->regs[REG_A0] = EXCPT_MPID; - return; - } - syscall_lock(); - if (!cnode_contains(mon_handle)) { - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - - struct proc *other_proc = proc_get(pid); - if (!proc_acquire(other_proc, PS_SUSPENDED)) { - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_MBUSY; - return; - } - cnode_handle_t node_handle = cnode_get_handle(pid, node_idx); - proc->regs[REG_A1] = cnode_get_cap(node_handle).raw; - proc_release(other_proc); - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_NONE; -} - -void syscall_mtakecap(struct proc *proc, uint64_t mon_idx, uint64_t pid, - uint64_t src_idx, uint64_t dst_idx) -{ - cnode_handle_t mon_handle = cnode_get_handle(proc->pid, mon_idx); - union cap cap = cnode_get_cap(mon_handle); - if (!cnode_contains(mon_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - if (cap.type != CAPTY_MONITOR) { - proc->regs[REG_A0] = EXCPT_UNIMPLEMENTED; - return; - } - if (cap.monitor.free > pid || pid >= cap.monitor.end) { - proc->regs[REG_A0] = EXCPT_MPID; - return; - } - syscall_lock(); - if (!cnode_contains(mon_handle)) { - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - struct proc *other_proc = proc_get(pid); - if (!proc_acquire(other_proc, PS_SUSPENDED)) { - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_MBUSY; - return; - } - cnode_handle_t src_handle = cnode_get_handle(pid, src_idx); - cnode_handle_t dst_handle = cnode_get_handle(proc->pid, dst_idx); - if (cnode_contains(src_handle) && !cnode_contains(dst_handle)) { - union cap src_cap = cnode_get_cap(src_handle); - cnode_move(src_handle, dst_handle); - if (src_cap.type == CAPTY_TIME) { - schedule_update(src_cap.time.hartid, proc->pid, - src_cap.time.free, src_cap.time.end); - } - if (src_cap.type == CAPTY_SOCKET && src_cap.socket.tag == 0) { - _listeners[src_cap.socket.channel] = proc; - } - } - proc_release(other_proc); - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_NONE; -} - -void syscall_mgivecap(struct proc *proc, uint64_t mon_idx, uint64_t pid, - uint64_t src_idx, uint64_t dst_idx) -{ - cnode_handle_t mon_handle = cnode_get_handle(proc->pid, mon_idx); - union cap cap = cnode_get_cap(mon_handle); - if (!cnode_contains(mon_handle)) { - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - if (cap.type != CAPTY_MONITOR) { - proc->regs[REG_A0] = EXCPT_UNIMPLEMENTED; - return; - } - if (cap.monitor.free > pid || pid >= cap.monitor.end) { - proc->regs[REG_A0] = EXCPT_MPID; - return; - } - syscall_lock(); - if (!cnode_contains(mon_handle)) { - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_EMPTY; - return; - } - - struct proc *other_proc = proc_get(pid); - if (!proc_acquire(other_proc, PS_SUSPENDED)) { - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_MBUSY; - return; - } - - cnode_handle_t src_handle = cnode_get_handle(proc->pid, src_idx); - cnode_handle_t dst_handle = cnode_get_handle(pid, dst_idx); - if (cnode_contains(src_handle) && !cnode_contains(dst_handle)) { - union cap src_cap = cnode_get_cap(src_handle); - cnode_move(src_handle, dst_handle); - if (src_cap.type == CAPTY_TIME) { - schedule_update(src_cap.time.hartid, pid, - src_cap.time.free, src_cap.time.end); - } - if (src_cap.type == CAPTY_SOCKET && src_cap.socket.tag == 0) { - _listeners[src_cap.socket.channel] = other_proc; - } - } - proc_release(other_proc); - syscall_unlock(); - proc->regs[REG_A0] = EXCPT_NONE; -} diff --git a/src/ticket_lock.c b/src/ticket_lock.c deleted file mode 100644 index e8466b50..00000000 --- a/src/ticket_lock.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "ticket_lock.h" - -#include - -void tl_acq(struct ticket_lock *lock) -{ - // Increment next ticket number and return the previous value - int ticket - = __atomic_fetch_add(&lock->next_ticket, 1, __ATOMIC_ACQUIRE); - - // Wait until our ticket number is being served - while (lock->serving_ticket != ticket) { - // Wait until our ticket number is being served - } - // Ensure visibility of memory operations - __atomic_thread_fence(__ATOMIC_ACQUIRE); -} - -void tl_rel(struct ticket_lock *lock) -{ - // Atomically increment the serving ticket number - __atomic_fetch_add(&lock->serving_ticket, 1, __ATOMIC_RELEASE); -} diff --git a/src/timer.c b/src/timer.c deleted file mode 100644 index 83d8ddec..00000000 --- a/src/timer.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "timer.h" - -extern volatile uint64_t _mtime[]; -extern volatile uint64_t _mtimecmp[]; - -uint64_t time_get(void) -{ - return _mtime[0]; -} - -void time_set(uint64_t time) -{ - _mtime[0] = time; -} - -uint64_t timeout_get(uint64_t i) -{ - return _mtimecmp[i]; -} - -void timeout_set(uint64_t i, uint64_t val) -{ - _mtimecmp[i] = val; -} diff --git a/src/trap.S b/src/trap.S deleted file mode 100644 index d0c25995..00000000 --- a/src/trap.S +++ /dev/null @@ -1,265 +0,0 @@ -# See LICENSE file for copyright and license details. - -#include "macro.inc" -#define PC_OFFSET 0 -#define RA_OFFSET 8 -#define SP_OFFSET 16 -#define GP_OFFSET 24 -#define TP_OFFSET 32 -#define T0_OFFSET 40 -#define T1_OFFSET 48 -#define T2_OFFSET 56 -#define S0_OFFSET 64 -#define S1_OFFSET 72 -#define A0_OFFSET 80 -#define A1_OFFSET 88 -#define A2_OFFSET 96 -#define A3_OFFSET 104 -#define A4_OFFSET 112 -#define A5_OFFSET 120 -#define A6_OFFSET 128 -#define A7_OFFSET 136 -#define S2_OFFSET 144 -#define S3_OFFSET 152 -#define S4_OFFSET 160 -#define S5_OFFSET 168 -#define S6_OFFSET 176 -#define S7_OFFSET 184 -#define S8_OFFSET 192 -#define S9_OFFSET 200 -#define S10_OFFSET 208 -#define S11_OFFSET 216 -#define T3_OFFSET 224 -#define T4_OFFSET 232 -#define T5_OFFSET 240 -#define T6_OFFSET 248 - -#define USER_ECALL 8 -#define MSTATUS_MIE 8 -#define SYSCALL_COUNT 16 - -.globl trap_entry -.globl trap_exit - -.section .text.trap - -# This is a trap handler that saves user registers to the process control -# block (PCB) and handles system calls and interrupts. It first saves the user -# thread pointer (TP) to the machine scratch register (mscratch) and loads the -# PCB for the current thread. If the scratch register is zero, this means that -# the exception/interrupt occurred in kernel mode. Otherwise, the handler -# saves the user registers (except TP and PC) to the PCB, using fixed offsets. -# This allows the kernel to resume the user thread at a later time, restoring -# its saved state. The handler then loads the user program counter (PC) and -# thread pointer (TP) and saves them to the PCB. Next, the handler checks the -# cause of the trap using the mcause register. If mcause < 0, this indicates -# an interrupt. Otherwise, if mcause = 8, this indicates a system call. The -# handler then jumps to the appropriate label to handle the trap. Finally, if -# neither case is true, the handler falls through to the .exception label where -# the exception is forwarded to the process. -.balign 16 -trap_entry: - # save user tp to scratch, load PCB - csrrw tp, mscratch, tp - - # scratch == 0, then exception/interrupt in kernel - beqz tp, .machine_interrupt - - # save user registers (except tp and pc) - sd ra, (RA_OFFSET)(tp) - sd sp, (SP_OFFSET)(tp) - sd gp, (GP_OFFSET)(tp) - sd t0, (T0_OFFSET)(tp) - sd t1, (T1_OFFSET)(tp) - sd t2, (T2_OFFSET)(tp) - sd s0, (S0_OFFSET)(tp) - sd s1, (S1_OFFSET)(tp) - sd a0, (A0_OFFSET)(tp) - sd a1, (A1_OFFSET)(tp) - sd a2, (A2_OFFSET)(tp) - sd a3, (A3_OFFSET)(tp) - sd a4, (A4_OFFSET)(tp) - sd a5, (A5_OFFSET)(tp) - sd a6, (A6_OFFSET)(tp) - sd a7, (A7_OFFSET)(tp) - sd s2, (S2_OFFSET)(tp) - sd s3, (S3_OFFSET)(tp) - sd s4, (S4_OFFSET)(tp) - sd s5, (S5_OFFSET)(tp) - sd s6, (S6_OFFSET)(tp) - sd s7, (S7_OFFSET)(tp) - sd s8, (S8_OFFSET)(tp) - sd s9, (S9_OFFSET)(tp) - sd s10,(S10_OFFSET)(tp) - sd s11,(S11_OFFSET)(tp) - sd t3, (T3_OFFSET)(tp) - sd t4, (T4_OFFSET)(tp) - sd t5, (T5_OFFSET)(tp) - sd t6, (T6_OFFSET)(tp) - - # load user pc and tp - csrr t0, mepc - csrrw t1, mscratch, zero - # save user pc and tp - sd t0, (PC_OFFSET)(tp) - sd t1, (TP_OFFSET)(tp) - - # Do not touch a0-a8, and t0 as they are used in syscall - - # Load the global pointer and stack pointer of the kernel. - .option push - .option norelax - la gp, __global_pointer$ - .option pop - load_sp t1 - - csrr t1, mcause - - bltz t1, .interrupt # mcause < 0 ==> interupt - li t2, USER_ECALL - beq t1, t2, .syscall # if mcause = 8, then syscall - - # fallthrough to .exception - -# This subroutine handles user exceptions. -# We call handle_exception(proc, mcause, mepc, mtval). -# Aftwerwards we go to trap exit. -.exception: - # Save a0 (pcb pointer) so we know the process in context - mv a0, tp - csrr a1, mcause - csrr a2, mepc - csrr a3, mtval - la ra, handle_exception - j .call - -# This subroutine handles machine interrupt. -# We only have timer interrupts in machine mode. -# We fix the thread pointer, global pointer and the stack pointer -# before continuing to the interrupt subroutine. -.machine_interrupt: - csrrw tp,mscratch,zero - .option push - .option norelax - la gp, __global_pointer$ - .option pop - load_sp t1 - # fallthrough to .interrupt - -# This subroutine handles interrupts. -# We only have timer interrupts so we invoke the scheduler -# schedule_yield(proc). -.interrupt: - mv a0, tp - la ra,schedule_yield - j .call - -# This subroutine handles syscalls. We check that the syscall number is -# valid, if it is invalid we have an exception and jump to the -# exception handler. If the syscall is valid, we increment the PC -# with 4 (ecall instruction is 4 bytes wide), calculate the target -# address of the trampoline, and jump to the trampoline entry. Syscall -# handlers are in a trampoline with 4 byte wide jump instructions. -# -# We assume that the user process's PC is in register t0, and that the -# syscall number is in register a0. After the syscall has been handled, -# we go to trap_exit. -.syscall: - # Check syscall number. - li t1, SYSCALL_COUNT - bge a0, t1, .exception - - # increment PC - addi t0, t0, 4 - sd t0, (PC_OFFSET)(tp) - - # Compute address of desired system call function in jump table - la ra, .syscall_table - slli a0, a0, 2 # multiply index by 4 to get offset - add ra, ra, a0 # add offset to jump table address - - # fallthrough to .call - -# Sets the pointer to PCB at a0 (first argument) then call function at ra -.call: - mv a0,tp - jalr ra,0(ra) - - # fallthrough to trap_exit - -# This function restores the user context. -# The function is preemptable while the tp points to the PCB. -trap_exit: - # Enable preemption by setting the machine interrupt enable bit - # in mstatus - csrw mstatus, MSTATUS_MIE - - # Load user pc to mepc. - ld t0, (PC_OFFSET)(tp) - csrw mepc,t0 - - # Load other user registers - ld ra, (RA_OFFSET)(tp) - ld sp, (SP_OFFSET)(tp) - ld gp, (GP_OFFSET)(tp) - ld t0, (T0_OFFSET)(tp) - ld t1, (T1_OFFSET)(tp) - ld t2, (T2_OFFSET)(tp) - ld s0, (S0_OFFSET)(tp) - ld s1, (S1_OFFSET)(tp) - ld a0, (A0_OFFSET)(tp) - ld a1, (A1_OFFSET)(tp) - ld a2, (A2_OFFSET)(tp) - ld a3, (A3_OFFSET)(tp) - ld a4, (A4_OFFSET)(tp) - ld a5, (A5_OFFSET)(tp) - ld a6, (A6_OFFSET)(tp) - ld a7, (A7_OFFSET)(tp) - ld s2, (S2_OFFSET)(tp) - ld s3, (S3_OFFSET)(tp) - ld s4, (S4_OFFSET)(tp) - ld s5, (S5_OFFSET)(tp) - ld s6, (S6_OFFSET)(tp) - ld s7, (S7_OFFSET)(tp) - ld s8, (S8_OFFSET)(tp) - ld s9, (S9_OFFSET)(tp) - ld s10,(S10_OFFSET)(tp) - ld s11,(S11_OFFSET)(tp) - ld t3, (T3_OFFSET)(tp) - ld t4, (T4_OFFSET)(tp) - ld t5, (T5_OFFSET)(tp) - ld t6, (T6_OFFSET)(tp) - - # Disable preemption - csrw mstatus,0 - # Save the PCB pointer to mscratch. - csrw mscratch, tp - # Load the user thread pointer. - ld tp,(TP_OFFSET)(tp) # load user tp - # Return to the user. - mret - -# Table of jump instructions for system call functions. Aligned on 4-byte -# boundary. The 'norvc' option disables compressed encoding, ensuring each -# instruction is exactly 4 bytes wide. -.balign 4 -.syscall_table: - .option push - .option norvc - j syscall_proc - j syscall_getcap - j syscall_movcap - j syscall_delcap - j syscall_revcap - j syscall_drvcap - j syscall_msuspend - j syscall_mresume - j syscall_mgetreg - j syscall_msetreg - j syscall_mgetcap - j syscall_mtakecap - j syscall_mgivecap - j syscall_recv - j syscall_send - j syscall_sendrecv - .option pop diff --git a/src/wfi.c b/src/wfi.c deleted file mode 100644 index 4db01086..00000000 --- a/src/wfi.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "wfi.h" - -void wfi() -{ - __asm__ volatile("wfi"); -} diff --git a/test/Makefile b/test/Makefile deleted file mode 100644 index 3cf5953e..00000000 --- a/test/Makefile +++ /dev/null @@ -1,33 +0,0 @@ - -$(info Unit tests requires Google Test to be installed) -vpath %.c ../src -SRCS=cap.c cnode.c exception.c ticket_lock.c proc.c schedule.c syscall.c \ - syscall_lock.c syscall_monitor.c syscall_ipc.c mockup.c ../api/s3k-utils.c -TEST_SRCS=$(wildcard test_*.C) - -OBJS=$(patsubst %.c,obj/%.o, $(SRCS)) -DEPS=$(patsubst %.d,obj/%.o, $(SRCS)) -TEST_OBJS=$(patsubst %.cc,obj/%.o, $(TEST_SRCS)) - -CFLAGS=-I../inc -I. -MMD -include config.h -CXXFLAGS=-I../inc -I. -include config.h - -test: test_detail.json - -obj/%.o : %.c - @mkdir -p $(@D) - $(CC) $(CFLAGS) -c -o $@ $< - -obj/%.o: %.cc - @mkdir -p $(@D) - $(CXX) $(CXXFLAGS) -c -o $@ $^ -lgtest - -run_tests.out: $(OBJS) $(TEST_OBJS) - $(CXX) $(CXXFLAGS) -o $@ $^ -lgtest -lgtest_main - -test_detail.json: run_tests.out - ./run_tests.out --gtest_output=json - -.PHONY: test - --include: $(DEPS) diff --git a/test/config.h b/test/config.h deleted file mode 100644 index 54b7ea10..00000000 --- a/test/config.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __MOCKUP_H__ -#define __MOCKUP_H__ -#define NPROC 2 -#define NCAP 32 -#define NSLACK 1000 -#define NSLICE 128 -#define NCHANNEL 128 -#define NTICK 10000 -#define NHART 4 - -#define NDEBUG -#endif // __MOCKUP_H__ diff --git a/test/init_caps.h b/test/init_caps.h deleted file mode 100644 index ab09fc2d..00000000 --- a/test/init_caps.h +++ /dev/null @@ -1,12 +0,0 @@ -#include "cap.h" - -static const union cap init_caps[] - = { [0] = CAP_PMP(0x20005fff, CAP_RWX), - [1] = CAP_MEMORY(0x0020, 0x8000, 0x10, CAP_RWX), - [2] = CAP_MEMORY(0x0000, 0x0001, 0x2, CAP_RW), - [3] = CAP_TIME(0, 0, NSLICE), - [4] = CAP_TIME(1, 0, NSLICE), - [5] = CAP_TIME(2, 0, NSLICE), - [6] = CAP_TIME(3, 0, NSLICE), - [7] = CAP_MONITOR(0, NPROC), - [8] = CAP_CHANNEL(0, NCHANNEL) }; diff --git a/test/mockup.c b/test/mockup.c deleted file mode 100644 index aeab10c4..00000000 --- a/test/mockup.c +++ /dev/null @@ -1,158 +0,0 @@ -/** - * @file mockup.c - * @brief Emulates hardware for unit-testing purposes. - * @copyright MIT License - * @author Henrik Karlsson (henrik10@kth.se) - */ -#include "csr.h" -#include "timer.h" -#include "wfi.h" - -#include -#include - -struct proc *current; -static uint64_t _mhartid; -static uint64_t _time; -static uint64_t _timeout[8]; -static uint64_t _pmpcfg0; -static uint64_t _pmpaddr[8]; - -void current_set(struct proc *proc) -{ - current = proc; -} - -struct proc *current_get(void) -{ - return current; -} - -void wfi(void) -{ - while (!csrr_mip()) - ; -} - -uint64_t time_get(void) -{ - _time += 10; - return _time; -} - -void time_set(uint64_t time) -{ - _time = time; -} - -uint64_t timeout_get(uint64_t i) -{ - return _timeout[i]; -} - -void timeout_set(uint64_t i, uint64_t val) -{ - _timeout[i] = val; -} - -uint64_t csrr_mhartid(void) -{ - return _mhartid; -} - -uint64_t csrr_mip(void) -{ - if (time_get() < _timeout[_mhartid]) - return 0; - return (1 << 7); -} - -uint64_t csrr_pmpcfg0(void) -{ - return _pmpcfg0; -} - -void csrw_pmpcfg0(uint64_t val) -{ - _pmpcfg0 = val; -} - -uint64_t csrr_pmpaddr0(void) -{ - return _pmpaddr[0]; -} - -uint64_t csrr_pmpaddr1(void) -{ - return _pmpaddr[1]; -} - -uint64_t csrr_pmpaddr2(void) -{ - return _pmpaddr[2]; -} - -uint64_t csrr_pmpaddr3(void) -{ - return _pmpaddr[3]; -} - -uint64_t csrr_pmpaddr4(void) -{ - return _pmpaddr[4]; -} - -uint64_t csrr_pmpaddr5(void) -{ - return _pmpaddr[5]; -} - -uint64_t csrr_pmpaddr6(void) -{ - return _pmpaddr[6]; -} - -uint64_t csrr_pmpaddr7(void) -{ - return _pmpaddr[7]; -} - -void csrw_pmpaddr0(uint64_t val) -{ - _pmpaddr[0] = val; -} - -void csrw_pmpaddr1(uint64_t val) -{ - _pmpaddr[1] = val; -} - -void csrw_pmpaddr2(uint64_t val) -{ - _pmpaddr[2] = val; -} - -void csrw_pmpaddr3(uint64_t val) -{ - _pmpaddr[3] = val; -} - -void csrw_pmpaddr4(uint64_t val) -{ - _pmpaddr[4] = val; -} - -void csrw_pmpaddr5(uint64_t val) -{ - _pmpaddr[5] = val; -} - -void csrw_pmpaddr6(uint64_t val) -{ - _pmpaddr[6] = val; -} - -void csrw_pmpaddr7(uint64_t val) -{ - _pmpaddr[7] = val; -} diff --git a/test/platform.h b/test/platform.h deleted file mode 100644 index 5466f6d5..00000000 --- a/test/platform.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __PLATFORM_H__ -#define __PLATFORM_H__ - -#define NHART 4 - -#endif /* __PLATFORM_H__ */ diff --git a/test/test_api.C b/test/test_api.C deleted file mode 100644 index 7bd7bdd3..00000000 --- a/test/test_api.C +++ /dev/null @@ -1,118 +0,0 @@ -extern "C" { -#include "../api/s3k.h" -#include "cap.h" -} -#include - -namespace -{ -class ApiTest : public ::testing::Test -{ - protected: - ApiTest() - { - } - - ~ApiTest() override - { - } - - void SetUp() override - { - } - - void TearDown() override - { - } - - unsigned long long llrand() - { - unsigned long long r = 0; - - for (int i = 0; i < 5; ++i) { - r = (r << 15) | (rand() & 0x7FFF); - } - - return r & 0xFFFFFFFFFFFFFFFFULL; - } -}; -} // namespace - -TEST_F(ApiTest, TimeCap) -{ - union cap cap; - union s3k_cap s3k_cap; - // Test if time capabilities match - for (int i = 0; i < 100; ++i) { - uint64_t begin = llrand(); - uint64_t end = llrand(); - uint64_t hartid = llrand(); - cap = CAP_TIME(hartid, begin, end); - s3k_cap = s3k_time(hartid, begin, end); - ASSERT_EQ(cap.raw, s3k_cap.raw); - } -} - -TEST_F(ApiTest, TimeParent) -{ - union cap cap, cap_child; - union s3k_cap s3k_cap, s3k_cap_child; - - // Test if parent predicate match - int children = 0; - while (children < 120) { - uint64_t raw1 = llrand(); - uint64_t raw2 = llrand(); - cap.raw = raw1; - cap_child.raw = raw2; - s3k_cap.raw = raw1; - s3k_cap_child.raw = raw2; - ASSERT_EQ(cap_time_parent(cap, cap_child), - s3k_time_parent(s3k_cap, s3k_cap_child)); - if (cap_time_parent(cap, cap_child)) - children++; - } -} - -TEST_F(ApiTest, MemoryCap) -{ - // Test if memory capabilities match - union cap cap; - union s3k_cap s3k_cap; - for (int i = 0; i < 100; ++i) { - uint64_t begin = llrand() % 0x8001; - uint64_t end = llrand() % 0x8001; - uint64_t free = llrand() % 0x8001; - uint64_t offset = llrand(); - uint64_t rwx = llrand(); - uint64_t lock = llrand(); - cap = CAP_MEMORY(begin, end, offset, rwx); - cap.memory.lock = lock; - cap.memory.free = free; - s3k_cap = s3k_memory(begin, end, offset, rwx); - s3k_cap.memory.lock = lock; - s3k_cap.memory.free = free; - ASSERT_EQ(cap.raw, s3k_cap.raw); - } -} - -TEST_F(ApiTest, MemoryParent) -{ - // Test if memory capabilities match - union cap cap, cap_child; - union s3k_cap s3k_cap, s3k_cap_child; - // Test if parent predicate match - int children = 0; - while (children < 120) { - uint64_t raw1 = llrand(); - uint64_t raw2 = llrand(); - cap.raw = raw1; - cap_child.raw = raw2; - s3k_cap.raw = raw1; - s3k_cap_child.raw = raw2; - ASSERT_EQ(cap_memory_parent(cap, cap_child), - s3k_memory_parent(s3k_cap, s3k_cap_child)); - if (cap_memory_parent(cap, cap_child)) - children++; - } -} diff --git a/test/test_cap.C b/test/test_cap.C deleted file mode 100644 index 99ac5c7f..00000000 --- a/test/test_cap.C +++ /dev/null @@ -1,190 +0,0 @@ -extern "C" { -#include "../api/s3k.h" -#include "cap.h" -} -#include - -namespace -{ -class CapTest : public ::testing::Test -{ - protected: - CapTest() - { - } - - ~CapTest() override - { - } - - void SetUp() override - { - } - - void TearDown() override - { - } -}; -} // namespace - -TEST_F(CapTest, MakeTest) -{ - uint64_t hartid = 3, begin = 0x5, end = 0x12; - union cap cap = CAP_TIME(hartid, begin, end); - EXPECT_EQ(cap.type, CAPTY_TIME); - EXPECT_EQ(cap.time.hartid, hartid); - EXPECT_EQ(cap.time.begin, begin); - EXPECT_EQ(cap.time.free, begin); - EXPECT_EQ(cap.time.end, end); - - EXPECT_EQ(cap.time.unused, 0); -} - -TEST_F(CapTest, MakeMemory) -{ - uint64_t begin = 0x100, end = 0x200, offset = 0x3, rwx = CAP_RWX; - union cap cap = CAP_MEMORY(begin, end, offset, rwx); - EXPECT_EQ(cap.type, CAPTY_MEMORY); - EXPECT_EQ(cap.memory.begin, begin); - EXPECT_EQ(cap.memory.free, begin); - EXPECT_EQ(cap.memory.end, end); - EXPECT_EQ(cap.memory.offset, offset); - EXPECT_EQ(cap.memory.rwx, rwx); -} - -TEST_F(CapTest, MakePMP) -{ - uint64_t addr = 0x85fff, rwx = CAP_RWX; - union cap cap = CAP_PMP(addr, rwx); - EXPECT_EQ(cap.type, CAPTY_PMP); - EXPECT_EQ(cap.pmp.addr, addr); - EXPECT_EQ(cap.pmp.cfg, 0x18 | rwx); -} - -TEST_F(CapTest, MakeMonitor) -{ - uint64_t begin = 0x1, end = 0x20; - union cap cap = CAP_MONITOR(begin, end); - EXPECT_EQ(cap.type, CAPTY_MONITOR); - EXPECT_EQ(cap.monitor.begin, begin); - EXPECT_EQ(cap.monitor.free, begin); - EXPECT_EQ(cap.monitor.end, end); - - EXPECT_EQ(cap.monitor.unused, 0); -} - -TEST_F(CapTest, MakeChannel) -{ - uint64_t begin = 0x1, end = 0x20; - union cap cap = CAP_CHANNEL(begin, end); - EXPECT_EQ(cap.type, CAPTY_CHANNEL); - EXPECT_EQ(cap.channel.begin, begin); - EXPECT_EQ(cap.channel.free, begin); - EXPECT_EQ(cap.channel.end, end); - - EXPECT_EQ(cap.channel.unused, 0); -} - -TEST_F(CapTest, MakeSocket) -{ - uint64_t channel = 0x3, tag = 0x20; - union cap cap = CAP_SOCKET(channel, tag); - EXPECT_EQ(cap.type, CAPTY_SOCKET); - EXPECT_EQ(cap.socket.channel, channel); - EXPECT_EQ(cap.socket.tag, tag); - - EXPECT_EQ(cap.socket.unused, 0); -} - -TEST_F(CapTest, TimeDerive) -{ - EXPECT_FALSE(cap_time_derive(CAP_TIME(3, 1, 20), CAP_TIME(3, 0, 20))); - EXPECT_FALSE(cap_time_derive(CAP_TIME(2, 1, 20), CAP_TIME(3, 1, 20))); - for (unsigned int i = 0; i < 255; i++) { - EXPECT_TRUE( - cap_time_derive(CAP_TIME(i, 1, 20), CAP_TIME(i, 1, 20))); - EXPECT_FALSE( - cap_time_derive(CAP_TIME(i, 5, 20), CAP_TIME(i, 4, 10))); - EXPECT_FALSE( - cap_time_derive(CAP_TIME(i, 5, 20), CAP_TIME(i, 6, 10))); - } -} - -TEST_F(CapTest, TimeParent) -{ - EXPECT_FALSE(cap_time_parent(CAP_TIME(3, 1, 20), CAP_TIME(3, 0, 20))); - EXPECT_FALSE(cap_time_parent(CAP_TIME(2, 1, 20), CAP_TIME(3, 1, 20))); - for (unsigned int i = 0; i < 255; i++) { - EXPECT_TRUE( - cap_time_parent(CAP_TIME(i, 1, 20), CAP_TIME(i, 1, 20))); - EXPECT_FALSE( - cap_time_parent(CAP_TIME(i, 5, 20), CAP_TIME(i, 4, 10))); - } -} - -TEST_F(CapTest, MemoryDerive) -{ - // RWX - EXPECT_TRUE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_RWX), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RWX))); - EXPECT_TRUE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_RWX), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RX))); - EXPECT_TRUE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_RWX), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RW))); - EXPECT_TRUE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_RWX), - CAP_MEMORY(0x0, 0x2000, 8, CAP_R))); - // RX - EXPECT_TRUE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_RX), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RX))); - EXPECT_TRUE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_RX), - CAP_MEMORY(0x0, 0x2000, 8, CAP_R))); - // RW - EXPECT_TRUE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_RW), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RW))); - EXPECT_TRUE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_RW), - CAP_MEMORY(0x0, 0x2000, 8, CAP_R))); - // R - EXPECT_TRUE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_R), - CAP_MEMORY(0x0, 0x2000, 8, CAP_R))); - - // EQ - EXPECT_TRUE(cap_memory_derive(CAP_MEMORY(0x100, 0x200, 8, CAP_RWX), - CAP_MEMORY(0x100, 0x200, 8, CAP_RWX))); - // Larger - EXPECT_FALSE(cap_memory_derive(CAP_MEMORY(0x100, 0x3000, 8, CAP_RWX), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RWX))); - EXPECT_FALSE(cap_memory_derive(CAP_MEMORY(0x100, 0x3000, 8, CAP_RWX), - CAP_MEMORY(0x100, 0x3001, 8, CAP_RWX))); - EXPECT_FALSE(cap_memory_derive(CAP_MEMORY(0x100, 0x3000, 8, CAP_RWX), - CAP_MEMORY(0x99, 0x3001, 8, CAP_RWX))); - // OFFSET NOT EQ - EXPECT_FALSE(cap_memory_derive(CAP_MEMORY(0x100, 0x200, 8, CAP_RWX), - CAP_MEMORY(0x100, 0x200, 7, CAP_RWX))); - // Bad permissions - EXPECT_FALSE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_R), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RW))); - EXPECT_FALSE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_R), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RWX))); - EXPECT_FALSE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_R), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RX))); - EXPECT_FALSE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_RW), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RWX))); - EXPECT_FALSE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_RW), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RX))); - EXPECT_FALSE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_RX), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RWX))); - EXPECT_FALSE(cap_memory_derive(CAP_MEMORY(0x0, 0x3000, 8, CAP_RX), - CAP_MEMORY(0x0, 0x2000, 8, CAP_RW))); -} - -TEST_F(CapTest, PmpAddr) -{ - uint64_t begin = 0x100; - uint64_t end = 0x200; - uint64_t pmpaddr = pmp_napot_addr(begin, end); - EXPECT_EQ(begin, pmp_napot_begin(pmpaddr)); - EXPECT_EQ(end, pmp_napot_end(pmpaddr)); - - EXPECT_EQ(0x210000, pmp_napot_begin(0x85fff)); - EXPECT_EQ(0x220000, pmp_napot_end(0x85fff)); -} diff --git a/test/test_proc.C b/test/test_proc.C deleted file mode 100644 index 6430f7b8..00000000 --- a/test/test_proc.C +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include - -extern "C" { -#include "cap.h" -#include "cnode.h" -#include "csr.h" -#include "proc.h" -} - -namespace -{ -class ProcTest : public ::testing::Test -{ - private: - static constexpr union cap caps[] - = { CAP_PMP(0x205fff, CAP_RWX), CAP_PMP(0x305fff, CAP_RX), - CAP_PMP(0x405fff, CAP_RW), CAP_PMP(0x505fff, CAP_R) }; - - protected: - ProcTest() - { - cnode_init(caps, ARRAY_SIZE(caps)); - proc_init(0); - } - - ~ProcTest() override - { - } - - void SetUp() override - { - } - - void TearDown() override - { - } -}; -} // namespace - -TEST_F(ProcTest, LoadPmp) -{ - struct proc *proc = proc_get(0); - proc->regs[REG_PMP] = 0x03020100ull; - proc_load_pmp(proc); - EXPECT_EQ(csrr_pmpcfg0(), 0x1F1F1F1F191B1D1Full); - EXPECT_EQ(csrr_pmpaddr0(), 0x205FFFull); - EXPECT_EQ(csrr_pmpaddr1(), 0x305FFFull); - EXPECT_EQ(csrr_pmpaddr2(), 0x405FFFull); - EXPECT_EQ(csrr_pmpaddr3(), 0x505FFFull); - EXPECT_EQ(csrr_pmpaddr4(), 0x205FFFull); - EXPECT_EQ(csrr_pmpaddr5(), 0x205FFFull); - EXPECT_EQ(csrr_pmpaddr6(), 0x205FFFull); - EXPECT_EQ(csrr_pmpaddr7(), 0x205FFFull); -} - -TEST_F(ProcTest, ProcAcqRel) -{ - struct proc *proc = proc_get(0); - EXPECT_TRUE(proc_acquire(proc, PS_READY)); - EXPECT_EQ(proc->state, PS_RUNNING); - proc_release(proc); - EXPECT_EQ(proc->state, PS_READY); -} - -TEST_F(ProcTest, ProcAcqSupRel) -{ - struct proc *proc = proc_get(0); - EXPECT_TRUE(proc_acquire(proc, PS_READY)); - EXPECT_EQ(proc->state, PS_RUNNING); - proc_suspend(proc); - EXPECT_EQ(proc->state, PS_SUSPENDED_BUSY); - proc_release(proc); - EXPECT_EQ(proc->state, PS_SUSPENDED); - EXPECT_FALSE(proc_acquire(proc, PS_READY)); -} diff --git a/test/test_schedule.C b/test/test_schedule.C deleted file mode 100644 index 3a82ef65..00000000 --- a/test/test_schedule.C +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include - -extern "C" { -#include "csr.h" -#include "proc.h" -#include "schedule.h" -} - -namespace -{ -class SchedTest : public ::testing::Test -{ - private: - protected: - SchedTest() - { - proc_init(0); - schedule_init(); - } - - ~SchedTest() override - { - } - - void SetUp() override - { - } - - void TearDown() override - { - } -}; -} // namespace - -TEST_F(SchedTest, SchedUpdate) -{ - struct sched_entry entry; - schedule_update(0, 2, 0, 8); - schedule_update(0, 3, 8, 16); - // First slice [0,8) belongs to process 2 - // Second slice [8,16) belongs to process 3 - // Remainder [16,NSLICE) belongs to process 0 - - // First slice - entry = schedule_get(0, 0); - EXPECT_EQ(entry.pid, 2); - EXPECT_EQ(entry.len, 8); - - entry = schedule_get(0, 1); - EXPECT_EQ(entry.pid, 2); - EXPECT_EQ(entry.len, 7); - - entry = schedule_get(0, 7); - EXPECT_EQ(entry.pid, 2); - EXPECT_EQ(entry.len, 1); - - // Second slice - entry = schedule_get(0, 8); - EXPECT_EQ(entry.pid, 3); - EXPECT_EQ(entry.len, 8); - - entry = schedule_get(0, 9); - EXPECT_EQ(entry.pid, 3); - EXPECT_EQ(entry.len, 7); - - entry = schedule_get(0, 15); - EXPECT_EQ(entry.pid, 3); - EXPECT_EQ(entry.len, 1); - - // Remaining - entry = schedule_get(0, 16); - EXPECT_EQ(entry.pid, 0); - EXPECT_EQ(entry.len, NSLICE - 16); - - entry = schedule_get(0, 17); - EXPECT_EQ(entry.pid, 0); - EXPECT_EQ(entry.len, NSLICE - 17); - - entry = schedule_get(0, NSLICE - 1); - EXPECT_EQ(entry.pid, 0); - EXPECT_EQ(entry.len, 1); -} - -TEST_F(SchedTest, SchedDelete) -{ - struct sched_entry entry; - // Create slice [0,8) for proc 2 - // Delete the slice - schedule_update(0, 2, 0, 8); - schedule_delete(0, 0, 8); - - // Deleted slice, pid == 0xFF means NONE/INVALID - entry = schedule_get(0, 0); - EXPECT_EQ(entry.pid, 0xFF); - EXPECT_EQ(entry.len, 8); - - entry = schedule_get(0, 1); - EXPECT_EQ(entry.pid, 0xFF); - EXPECT_EQ(entry.len, 7); - - entry = schedule_get(0, 7); - EXPECT_EQ(entry.pid, 0xFF); - EXPECT_EQ(entry.len, 1); - - // Remaining - entry = schedule_get(0, 8); - EXPECT_EQ(entry.pid, 0); - EXPECT_EQ(entry.len, NSLICE - 8); - - entry = schedule_get(0, 17); - EXPECT_EQ(entry.pid, 0); - EXPECT_EQ(entry.len, NSLICE - 17); - - entry = schedule_get(0, NSLICE - 1); - EXPECT_EQ(entry.pid, 0); - EXPECT_EQ(entry.len, 1); -} diff --git a/test/test_syscall.C b/test/test_syscall.C deleted file mode 100644 index ddedb303..00000000 --- a/test/test_syscall.C +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -extern "C" { -#include "cap.h" -#include "cnode.h" -#include "csr.h" -#include "proc.h" -#include "syscall.h" -#include "timer.h" -} - -namespace -{ -class SyscallTest : public ::testing::Test -{ - private: - static constexpr union cap caps[] - = { CAP_PMP(0x20005FFF, CAP_RWX), - CAP_MEMORY(0x0020, 0X8000, 0x10, CAP_RWX), - CAP_MEMORY(0x0000, 0X0001, 0x2, CAP_RW), - CAP_TIME(0, 0, NSLICE), - CAP_TIME(1, 0, NSLICE), - CAP_TIME(2, 0, NSLICE), - CAP_TIME(3, 0, NSLICE), - CAP_MONITOR(0, NPROC), - CAP_CHANNEL(0, NCHANNEL) }; - - protected: - SyscallTest() - { - } - - ~SyscallTest() override - { - } - - void SetUp() override - { - cnode_init(caps, ARRAY_SIZE(caps)); - proc_init(0); - } - - void TearDown() override - { - } -}; -} // namespace - -TEST_F(SyscallTest, GetPid) -{ - for (int i = 0; i < NPROC; ++i) { - struct proc *proc = proc_get(i); - syscall_proc(proc, 0, 0, 0); - EXPECT_EQ(proc->regs[REG_A0], i); - } -} - -TEST_F(SyscallTest, GetHartID) -{ - for (int i = 0; i < NPROC; ++i) { - struct proc *proc = proc_get(i); - syscall_proc(proc, 3, 0, 0); - EXPECT_EQ(proc->regs[REG_A0], 0); - } -} - -TEST_F(SyscallTest, GetTime) -{ - for (int i = 0; i < NPROC; ++i) { - struct proc *proc = proc_get(i); - uint64_t before = time_get(); - syscall_proc(proc, 4, 0, 0); - uint64_t after = time_get(); - EXPECT_GE(proc->regs[REG_A0], before); - EXPECT_LE(proc->regs[REG_A0], after); - } -} - -TEST_F(SyscallTest, GetTimeout) -{ - for (int i = 0; i < NPROC; ++i) { - struct proc *proc = proc_get(i); - uint64_t timeout = time_get(); - timeout_set(0, timeout); - syscall_proc(proc, 5, 0, 0); - EXPECT_EQ(proc->regs[REG_A0], timeout); - } -} diff --git a/tools.mk b/tools.mk new file mode 100644 index 00000000..5f4c8415 --- /dev/null +++ b/tools.mk @@ -0,0 +1,5 @@ +RISCV_PREFIX?=riscv64-unknown-elf- +CC=${RISCV_PREFIX}gcc +SIZE=${RISCV_PREFIX}size +OBJDUMP=${RISCV_PREFIX}objdump +OBJCOPY=${RISCV_PREFIX}objcopy