Compare commits

..

66 Commits

Author SHA1 Message Date
ac8f669e6b meta: update kernel 2026-03-21 10:56:58 +00:00
7d6af0344c bootstrap: cleanup test code 2026-03-21 10:56:47 +00:00
1e52036585 bootstrap: tar: use the nr_written value returned by xpc_buffer_write to increment the file cursor 2026-03-21 10:56:32 +00:00
24fc8ecb97 bootstrap: configure to build as a purely static executable 2026-03-21 10:55:49 +00:00
cc15bb54f3 ld: implement an actual program loader 2026-03-21 10:54:50 +00:00
ed138d581e prog: test: simple function call test 2026-03-21 10:54:21 +00:00
eaf5e02ad4 prog: systemd: simple function call test 2026-03-21 10:54:09 +00:00
88f8d4f18a lib: launch: implement alternate argument handling to support interpreters 2026-03-21 10:53:38 +00:00
5d9a3fa54d lib: launch: implement GLOB_DAT and RELATIVE relocations 2026-03-21 10:53:12 +00:00
8236f99aef lib: xpc: fix overlapping variable uses in xpc_string_t 2026-03-21 10:52:23 +00:00
81b9e7777a lib: xpc: fix overlapping variable uses in xpc_buffer_t 2026-03-21 10:52:16 +00:00
aa155824d3 lib: xpc: add a nr_written output to xpc_msg_write 2026-03-21 10:51:17 +00:00
af424d85d8 lib: fs: implement fs.seek() 2026-03-21 10:48:33 +00:00
a37a07d708 lib: fs: convert some verbose log messages to trace messages 2026-03-21 10:47:08 +00:00
2fab6687b0 lib: fs: fix incorect offset calculation in page request handler 2026-03-21 10:46:40 +00:00
d1a9c5cd45 lib: fs: implement private and shared file mappings 2026-03-21 10:46:15 +00:00
3bf995cdf6 lib: c: io: implement lseek() 2026-03-21 10:44:58 +00:00
8c09909d07 lib: c: malloc: enable the global heap for the static module 2026-03-21 10:44:47 +00:00
a041bc55db lib: c: rt: call task_exit once main() returns 2026-03-21 10:44:21 +00:00
32d0606d16 lib: c: io: add sys/types.h 2026-03-21 10:42:11 +00:00
3bc331b9c5 lib: c: io: add fcntl.h 2026-03-21 10:42:00 +00:00
c2b59ed494 lib: c: io: implement close() 2026-03-21 10:41:29 +00:00
c340f927bb lib: c: io: mmap can now reserve a region of memory when called with PROT_NONE 2026-03-21 10:40:44 +00:00
7a00de28a1 lib: c: io: munmap now unmaps and unreserves a region of memory 2026-03-21 10:40:15 +00:00
9148349f44 lib: c: implement strcpy and strncpy 2026-03-21 10:38:40 +00:00
766411d983 interface: fs: add fs.seek function 2026-03-21 10:35:48 +00:00
555989e74a cmake: build all programs as position-independent by default 2026-03-21 10:35:09 +00:00
ffc2ed735e toolchain: xpcg: replace bluelib with fx 2026-03-21 10:33:39 +00:00
68ae449731 toolchain: xpcg: add an offset type for signed offset values 2026-03-21 10:33:01 +00:00
08a9627548 ld: remove unused address space handle 2026-03-18 21:10:34 +00:00
844f6d50eb bootstrap: test pthread features 2026-03-18 21:10:18 +00:00
863be171c1 lib: launch: populate result with handles to the newly-created task 2026-03-18 21:09:59 +00:00
0471838f30 lib: fs: fix dangling address space handle 2026-03-18 21:09:23 +00:00
cf70352caa lib: c: add pthread implementation 2026-03-18 21:08:59 +00:00
342b588b38 meta: update kernel 2026-03-18 21:08:35 +00:00
cdf828be2d lib: c: implement munmap() 2026-03-18 20:49:19 +00:00
c11d55d675 meta: update kernel 2026-03-15 14:41:12 +00:00
a5cf0b050d ld: try reading multiple pages of mapped file 2026-03-15 14:41:07 +00:00
bd39cb11aa meta: update kernel 2026-03-15 09:50:38 +00:00
a01b5ddb11 cmake: add a simple does-the-os-boot-successfully test
the test will repeated boot the operating system and use the serial log to determine
if the boot was successful. if a problem is detected, a debugger is automatically
started and attached.
2026-03-15 09:49:51 +00:00
1e9fe24b39 ld: anonymous and file-backed mmap tests 2026-03-15 09:49:18 +00:00
9044281d1b bootstrap: use new libfs interface to handle requests 2026-03-15 09:48:47 +00:00
5dd99bb0a6 bootstrap: tar: fix pointer-alignment tar parsing issue 2026-03-15 09:47:58 +00:00
d03c750e4a cmake: ensure interface headers are regenerated when the corresponding .xpc file is changed 2026-03-15 09:47:03 +00:00
c66c7f3f3f x86_64: cmake: enable RDRAND support when running the kernel under qemu 2026-03-15 09:46:32 +00:00
29acfcee69 toolchain: xpcg: fix output header not being truncated 2026-03-15 09:45:31 +00:00
fea89d675e toolchain: xpcg: fix incorrect variable/type names in output header 2026-03-15 09:45:08 +00:00
eb8d9c3512 interface: fs: add error code result to fs.map() 2026-03-15 09:44:32 +00:00
0c56c645ac lib: fs: implement memory-mapped file i/o 2026-03-15 09:43:49 +00:00
68b7783f32 lib: xpc: implement non-blocking msg receive function 2026-03-15 09:43:22 +00:00
ea2b0d3986 lib: xpc: implement writing to local buffers 2026-03-15 09:43:00 +00:00
c9ccebacfc lib: launch: update to use new address-space api 2026-03-15 09:42:22 +00:00
5ad5f57a76 lib: c: update libheap to use new address-space api 2026-03-15 09:42:03 +00:00
f441d633b2 lib: c: implement mmap() 2026-03-15 09:41:25 +00:00
9ea3441fcc lib: c: move io sys headers to include/sys 2026-03-15 09:40:37 +00:00
86ca343cf0 interface: fs: remove old ifc definition file 2026-03-10 19:20:18 +00:00
b680ffdd5b meta: update kernel 2026-03-10 19:16:51 +00:00
14799e0d58 ld: use unistd i/o functions to open/read library files 2026-03-10 19:16:46 +00:00
b7452a449b bootstrap: update tarfs driver with libfs support 2026-03-10 19:16:22 +00:00
ea6ec785a9 lib: c: re-organise into separate static modules, plus a single shared library 2026-03-10 19:15:59 +00:00
6d88cf4bf3 lib: fs: implement mounting filesystems; reading, writing from files 2026-03-10 19:15:26 +00:00
aef0163017 lib: add libxpc to implement functionality needed by xpc interfaces 2026-03-10 19:14:37 +00:00
b0fda122e0 cmake: define BUILD_STATIC and BUILD_SHARED for library builds 2026-03-10 19:14:00 +00:00
79af171384 interface: fs: replace ifc definition with xpcg 2026-03-10 19:13:24 +00:00
5931642cc2 cmake: update inteface functions to use xpcg 2026-03-10 19:13:01 +00:00
26a49162e6 toolchain: replace ifc interface compiler with xpcg
xpcg is used to generate xpc interfaces
2026-03-10 19:12:14 +00:00
137 changed files with 8354 additions and 2332 deletions

View File

@@ -1,6 +1,9 @@
cmake_minimum_required(VERSION 3.14)
project(Rosetta C CXX ASM)
include(CheckPIESupported)
check_pie_supported()
set(sys_dir ${CMAKE_CURRENT_BINARY_DIR}/sys)
set(kernel_name mango_kernel)

View File

@@ -1,3 +1,4 @@
include(System-Disk)
include(QEMU)
include(Bochs)
include(Test)

View File

@@ -16,6 +16,11 @@ set(CMAKE_ASM_COMPILER ${ASM_COMPILER})
SET(CMAKE_C_FLAGS "-ffreestanding -nostdlib -z max-page-size=0x1000 -m64 -mcmodel=large -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -D_64BIT -DBYTE_ORDER=1234" CACHE STRING "" FORCE)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-shared" CACHE STRING "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--unresolved-symbols=report-all,--dynamic-linker=/lib/ld64.so" CACHE STRING "" FORCE)
set(CMAKE_C_LINK_OPTIONS_PIE "-pie")
set(CMAKE_C_LINK_PIE_SUPPORTED TRUE)
set(CMAKE_C_LINK_NO_PIE_SUPPORTED TRUE)
set(CMAKE_C_OUTPUT_EXTENSION .o)
set(CMAKE_CXX_OUTPUT_EXTENSION .o)
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)

View File

@@ -8,7 +8,8 @@ find_program(LLDB lldb)
find_program(GDB gdb)
set(patched_kernel ${CMAKE_CURRENT_BINARY_DIR}/kernel/${kernel_name}.elf32)
set(generic_flags -m 1G)
set(generic_flags -m 128M -cpu qemu64,+rdrand)
set(no_debug_flags)
if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux")

35
arch/x86_64/Test.cmake Normal file
View File

@@ -0,0 +1,35 @@
find_package(Python COMPONENTS Interpreter)
if (NOT Python_EXECUTABLE)
message(STATUS "QEMU: Cannot find python. Direct-kernel boot testing unavailable")
return()
endif ()
find_program(QEMU qemu-system-${TARGET_ARCH} REQUIRED)
if (NOT QEMU)
message(STATUS "QEMU: Cannot find qemu-system-${TARGET_ARCH}. Direct-kernel boot testing unavailable")
return()
endif ()
set(patched_kernel ${CMAKE_CURRENT_BINARY_DIR}/kernel/${kernel_name}.elf32)
set(generic_flags -m 1G -cpu qemu64,+rdrand)
set(this_dir ${CMAKE_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR})
set(no_debug_flags)
if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux")
message(STATUS "QEMU: Enabling KVM acceleration")
set(no_debug_flags ${no_debug_flags} -enable-kvm)
else ()
message(STATUS "QEMU: Host system is not Linux. KVM acceleration unavailable")
endif ()
message(STATUS "Test: Enable direct-kernel boot testing with QEMU")
add_custom_target(test-successful-boot
COMMAND
${this_dir}/test/successful-boot
${Python_EXECUTABLE}
${this_dir}/test/check-results
${QEMU}
${patched_kernel}
${sys_dir}/${bsp_name}
USES_TERMINAL
DEPENDS ${patched_kernel} bsp)

View File

@@ -0,0 +1,52 @@
#!/usr/bin/env python3
# vim: ft=python
import sys
def log(f, msg):
print(msg)
f.write(msg)
f.write('\n')
def successful_boot(boot_log, out):
nr_panic = boot_log.count("---[ kernel panic")
if nr_panic == 1:
log(out, "Kernel panic!")
return 1
if nr_panic > 1:
log(out, "Multiple kernel panics!")
return 1
nr_boots = boot_log.count('Mango kernel version')
if nr_boots == 0:
log(out, "Kernel didn't start!")
return 1
if nr_boots > 1:
log(out, "Kernel rebooted during test!")
return 1
nr_finish = boot_log.count("ld finished")
if nr_finish == 0:
log(out, "Didn't reach end of boot sequence!")
return 1
if nr_finish > 1:
log(out, "Boot sequence performed multiple times!")
return 1
return 0
tests = {
'successful-boot': successful_boot,
}
test_name = sys.argv[1]
boot_log_path = sys.argv[2]
out_path = sys.argv[3]
boot_log_file = open(boot_log_path, 'r')
boot_log = boot_log_file.read()
boot_log_file.close()
out_file = open(out_path, 'a')
exit(tests[test_name](boot_log, out_file))

View File

@@ -0,0 +1,69 @@
#!/bin/bash
# vim: ft=bash
log_dir="test-results/successful-boot"
rm -rf $log_dir
mkdir -p $log_dir
logfile="$log_dir/main.log"
log() {
if [ -n "$logfile" ]; then
printf '%s\n' "$@" >> "$logfile"
fi
printf '%s\n' "$@"
}
log "Running boot test. Press Ctrl+\\ to stop."
declare -i result
declare -i count
declare -i pass
declare -i fail
count=0
pass=0
fail=0
python=$1
validation_script=$2
qemu=$3
kernel=$4
initrd=$5
while true; do
log "Test $count"
result_file="$log_dir/$count.log"
$qemu \
-kernel $kernel \
-initrd $initrd \
-serial file:$result_file \
-cpu qemu64,+rdrand \
--append kernel.early-console=ttyS0 -s > /dev/null &
qemu_id=$!
sleep 1.2
$python $validation_script successful-boot $result_file $logfile
result=$?
count=$count+1
if [ $result -eq 0 ]; then
pass=$pass+1
else
mv $result_file "$result_file.FAIL"
fail=$fail+1
lldb \
-o "file kernel/mango_kernel.debug" \
-o "gdb-remote localhost:1234"
fi
kill -INT $qemu_id
log "---------------"
log "Total tests: $count"
log "Pass: $pass"
log "Fail: $fail"
log "---------------"
done

View File

@@ -1,8 +1,8 @@
find_program(IFC
NAMES ifc
find_program(XPCG
NAMES xpcg
REQUIRED
HINTS ${BUILD_TOOLS_DIR})
message(STATUS "Found interface compiler: ${IFC}")
message(STATUS "Found interface generator: ${XPCG}")
function(add_interface)
set(options)
@@ -20,9 +20,10 @@ function(add_interface)
add_custom_command(
OUTPUT ${header_path}
COMMAND ${IFC} ${arg_PATH}
COMMAND ${XPCG} ${arg_PATH}
DEPENDS ${arg_PATH}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${arg_PARENT_DIR}
COMMENT "Compiling interface: ${arg_NAME}")
COMMENT "Generating interface: ${arg_NAME}")
add_custom_target(ifgen-${arg_NAME} ALL
DEPENDS ${header_path})

View File

@@ -64,6 +64,8 @@ function(rosetta_add_library)
${arg_SOURCES}
${arg_HEADERS})
set_target_properties(${static_lib_name} PROPERTIES OUTPUT_NAME "${lib_name}")
target_compile_definitions(${static_lib_name} PRIVATE
BUILD_STATIC=1)
set(targets ${targets} ${static_lib_name})
if (arg_PUBLIC_INCLUDE_DIRS)
@@ -86,6 +88,8 @@ function(rosetta_add_library)
PATH ${arg_PUBLIC_INCLUDE_DIRS})
endif ()
target_compile_definitions(${shared_lib_name} PRIVATE
BUILD_SHARED=1)
set_target_properties(${shared_lib_name} PROPERTIES
SOVERSION 1)
target_link_options(${shared_lib_name} PRIVATE -Wl,--soname,${soname})

View File

@@ -1,4 +1,4 @@
file(GLOB if_files *.if)
file(GLOB if_files *.xpc)
foreach (file ${if_files})
get_filename_component(name ${file} NAME_WLE)

View File

@@ -1,3 +0,0 @@
interface fs {
msg open(string path, int flags) -> (int err);
}

11
interface/fs.xpc Normal file
View File

@@ -0,0 +1,11 @@
interface fs 6400;
func open[0](path: string, flags: int) -> (err: int);
func close[1]() -> (err: int);
func read[2](count: size) -> (err: int, nr_read: size, data: buffer);
func write[3](data: buffer) -> (err: int, nr_written: size);
func seek[4](offset: offset, origin: int) -> (err: int, new_pos: offset);
func map[5](prot: int, flags: int) -> (err: int, vmo: handle);

2
kernel

Submodule kernel updated: 1d4cb882a8...a2c89df195

View File

@@ -1,4 +1,4 @@
set(source_dirs core malloc)
set(source_dirs core malloc io)
set(public_include_dirs
${CMAKE_CURRENT_SOURCE_DIR}/include)
@@ -26,5 +26,7 @@ bsp_add_library(
NAME libc
LIB_DIR /usr/lib)
target_link_libraries(libc libmango)
target_link_libraries(libc libmango libxpc-static interface::fs)
target_compile_definitions(libc PRIVATE ENABLE_GLOBAL_HEAP=1)
add_subdirectory(pthread)

View File

@@ -1,4 +1,4 @@
set(source_dirs stdio string)
set(source_dirs stdio string errno)
foreach (dir ${source_dirs})
file(GLOB dir_sources ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.c)
@@ -21,3 +21,5 @@ sysroot_add_library(
NAME libc-core
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libc-core libmango)

View File

@@ -0,0 +1,54 @@
#include <errno.h>
#include <mango/status.h>
#if defined(BUILD_STATIC)
int __set_errno(int err)
{
return -err;
}
#endif
#if defined(BUILD_SHARED)
int __set_errno(int err)
{
/* TODO */
return -1;
}
#endif
int __errno_from_kern_status(unsigned int err)
{
switch (err) {
case KERN_OK:
return SUCCESS;
case KERN_UNIMPLEMENTED:
return ENOSYS;
case KERN_NAME_EXISTS:
return EEXIST;
case KERN_INVALID_ARGUMENT:
return EINVAL;
case KERN_UNSUPPORTED:
return ENOTSUP;
case KERN_NO_MEMORY:
return ENOMEM;
case KERN_NO_ENTRY:
return ENOENT;
case KERN_WOULD_BLOCK:
return EWOULDBLOCK;
case KERN_NO_DEVICE:
return ENODEV;
case KERN_DEVICE_STUCK:
case KERN_IO_ERROR:
return EIO;
case KERN_FATAL_ERROR:
return ENXIO;
case KERN_BAD_STATE:
return EPERM;
case KERN_MEMORY_FAULT:
return EFAULT;
case KERN_ACCESS_DENIED:
return EACCES;
default:
return EINVAL;
}
}

View File

@@ -0,0 +1,26 @@
char *strcpy(char *output, const char *input)
{
unsigned int i;
for (i = 0; input[i] != 0; i++) {
output[i] = input[i];
}
output[i] = '\0';
return output;
}
char *strncpy(char *output, const char *input, unsigned int count)
{
unsigned int size = count;
unsigned int i;
for (i = 0; i < size; i++) {
output[i] = input[i];
if (input[i] == 0) {
break;
}
}
output[i] = '\0';
return output;
}

View File

@@ -139,4 +139,7 @@
#define EQFULL 106 /* Interface output queue is full */
#define ELAST 106 /* Must be equal largest errno */
extern int __set_errno(int err);
extern int __errno_from_kern_status(unsigned int err);
#endif

53
lib/libc/include/fcntl.h Normal file
View File

@@ -0,0 +1,53 @@
#ifndef FCNTL_H_
#define FCNTL_H_
#define O_RDONLY 0x0000 /* open for reading only */
#define O_WRONLY 0x0001 /* open for writing only */
#define O_RDWR 0x0002 /* open for reading and writing */
#define O_ACCMODE 0x0003 /* mask for above modes */
#define O_NONBLOCK 0x00000004 /* no delay */
#define O_APPEND 0x00000008 /* set append mode */
#define O_SHLOCK 0x00000010 /* open with shared file lock */
#define O_EXLOCK 0x00000020 /* open with exclusive file lock */
#define O_ASYNC 0x00000040 /* signal pgrp when data ready */
#define O_FSYNC O_SYNC /* source compatibility: do not use */
#define O_NOFOLLOW 0x00000100 /* don't follow symlinks */
#define O_CREAT 0x00000200 /* create if nonexistant */
#define O_TRUNC 0x00000400 /* truncate to zero length */
#define O_EXCL 0x00000800 /* error if already exists */
#define O_RESOLVE_BENEATH \
0x00001000 /* only for open(2), same value as FMARK \
*/
#define O_EVTONLY \
0x00008000 /* descriptor requested for event notifications only */
#define O_NOCTTY 0x00020000 /* don't assign controlling terminal */
#define O_DIRECTORY 0x00100000
#define O_SYMLINK 0x00200000 /* allow open of a symlink */
#define O_CLOEXEC 0x01000000 /* implicitly set FD_CLOEXEC */
#define O_NOFOLLOW_ANY 0x20000000 /* no symlinks allowed in path */
#define O_EXEC 0x40000000 /* open file for execute only */
#define O_SEARCH (O_EXEC | O_DIRECTORY) /* open directory for search only */
#define AT_FDCWD -2
#define AT_EACCESS 0x0010 /* Use effective ids in access check */
#define AT_SYMLINK_NOFOLLOW \
0x0020 /* Act on the symlink itself not the target */
#define AT_SYMLINK_FOLLOW 0x0040 /* Act on target of symlink */
#define AT_REMOVEDIR 0x0080 /* Path refers to directory */
#define AT_REALDEV \
0x0200 /* Return real device inodes resides on for fstatat(2) */
#define AT_FDONLY \
0x0400 /* Use only the fd and Ignore the path for fstatat(2) */
#define AT_SYMLINK_NOFOLLOW_ANY \
0x0800 /* Path should not contain any symlinks */
#endif

View File

@@ -11,7 +11,12 @@ extern size_t strlen(const char *s);
extern int strcmp(const char *s1, const char *s2);
extern int strncmp(const char *s1, const char *s2, unsigned long n);
extern int strcpy(const char *s1, const char *s2);
extern int strncpy(const char *s1, const char *s2, unsigned long n);
extern void *memset(void *str, int c, size_t n);
extern void *memcpy(void *dst, const void *src, size_t len);
extern char *strdup(char *s);
#endif

View File

@@ -0,0 +1,46 @@
#ifndef SYS_MMAN_H_
#define SYS_MMAN_H_
#include <mango/types.h>
#include <stddef.h>
#define PROT_NONE 0x00u
#define PROT_EXEC 0x01u
#define PROT_READ 0x02u
#define PROT_WRITE 0x04u
#define MAP_FAILED ((void *)-1)
#define MAP_SHARED 0x01u
#define MAP_SHARED_VALIDATE 0x02u
#define MAP_PRIVATE 0x04u
#define MAP_32BIT 0x08u
#define MAP_ANON MAP_ANONYMOUS
#define MAP_ANONYMOUS 0x10u
#define MAP_DENYWRITE 0x20u
#define MAP_EXECUTABLE 0x40u
#define MAP_FILE 0x80u
#define MAP_FIXED 0x100u
#define MAP_FIXED_NOREPLACE 0x300u
#define MAP_GROWSDOWN 0x400u
#define MAP_HUGETLB 0x800u
#define MAP_HUGE_2MB 0x1000u
#define MAP_HUGE_1GB 0x2000u
#define MAP_LOCKED 0x4000u
#define MAP_NONBLOCK 0x8000u
#define MAP_NORESERVE 0x10000u
#define MAP_POPULATE 0x20000u
#define MAP_STACK 0x40000u
#define MAP_SYNC 0x80000u
#define MAP_UNINITIALIZED 0x100000u
extern void *mmap(
void *addr,
size_t length,
int prot,
int flags,
int fd,
off_t offset);
extern int munmap(void *addr, size_t length);
#endif

View File

@@ -0,0 +1,18 @@
#ifndef SYS_REMOTE_H_
#define SYS_REMOTE_H_
#include <mango/types.h>
#include <stdbool.h>
enum sys_remote_id {
SYS_REMOTE_NONE,
SYS_REMOTE_NSD,
};
extern bool sys_remote_get(
enum sys_remote_id id,
tid_t *out_tid,
unsigned int *out_chid);
extern void sys_remote_set(enum sys_remote_id id, tid_t tid, unsigned int chid);
#endif

View File

@@ -0,0 +1,10 @@
#ifndef SYS_TYPES_H_
#define SYS_TYPES_H_
#include <mango/types.h>
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif

View File

@@ -1,9 +1,15 @@
#ifndef UNISTD_H_
#define UNISTD_H_
#include <stdint.h>
#include <stddef.h>
#include <sys/types.h>
extern int open(const char *path, int flags);
extern int close(int fd);
extern int read(int fd, void *buf, size_t count);
extern int write(int fd, const void *buf, size_t count);
extern off_t lseek(int fd, off_t offset, int whence);
#endif

View File

@@ -0,0 +1,33 @@
set(source_dirs unistd stdio)
file(GLOB sources *.c *.h)
foreach (dir ${source_dirs})
file(GLOB dir_sources ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.c)
file(GLOB dir_headers ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.h)
set(sources ${sources} ${dir_sources})
set(headers ${headers} ${dir_headers})
endforeach (dir)
file(GLOB_RECURSE sub_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
set(headers ${headers} ${sub_headers})
set(component_sources ${sources} PARENT_SCOPE)
set(component_headers ${headers} PARENT_SCOPE)
set(component_public_include_dirs ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE)
rosetta_add_library(STATIC
NAME libc-io
PUBLIC_INCLUDE_DIRS
${public_include_dirs}
${CMAKE_CURRENT_SOURCE_DIR}/include
SOURCES ${sources}
HEADERS ${headers})
sysroot_add_library(
NAME libc-io
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libc-io libc-core interface::fs libxpc-static libmango)

2
lib/libc/io/fs.c Normal file
View File

@@ -0,0 +1,2 @@
#define MSG_IMPLEMENTATION
#include <rosetta/fs.h>

45
lib/libc/io/remote.c Normal file
View File

@@ -0,0 +1,45 @@
#include <sys/remote.h>
#define TID_INVALID ((tid_t) - 1)
#define CHID_INVALID ((unsigned int)-1)
struct remote {
tid_t tid;
unsigned int chid;
};
static struct remote remotes[] = {
[SYS_REMOTE_NONE] = {.tid = TID_INVALID, .chid = CHID_INVALID},
[SYS_REMOTE_NSD] = {.tid = TID_INVALID, .chid = CHID_INVALID},
};
static const size_t nr_remotes = sizeof remotes / sizeof remotes[0];
bool sys_remote_get(
enum sys_remote_id id,
tid_t *out_tid,
unsigned int *out_chid)
{
if (id < 0 || id >= nr_remotes) {
return false;
}
const struct remote *remote = &remotes[id];
if (remote->tid == TID_INVALID || remote->chid == CHID_INVALID) {
return false;
}
*out_tid = remote->tid;
*out_chid = remote->chid;
return true;
}
void sys_remote_set(enum sys_remote_id id, tid_t tid, unsigned int chid)
{
if (id < 0 || id >= nr_remotes) {
return;
}
struct remote *remote = &remotes[id];
remote->tid = tid;
remote->chid = chid;
}

View File

@@ -0,0 +1,11 @@
#include <errno.h>
#include <mango/handle.h>
#include <mango/msg.h>
#include <rosetta/fs.h>
#include <sys/remote.h>
int close(int fd)
{
kern_handle_close(fd);
return 0;
}

View File

@@ -0,0 +1,22 @@
#include <errno.h>
#include <mango/handle.h>
#include <mango/msg.h>
#include <rosetta/fs.h>
#include <sys/remote.h>
off_t lseek(int fd, off_t offset, int whence)
{
int err;
off_t new_offset;
kern_status_t status = fs_seek(fd, offset, whence, &err, &new_offset);
if (status != KERN_OK) {
return __set_errno(__errno_from_kern_status(status));
}
if (err != SUCCESS) {
return __set_errno(err);
}
return new_offset;
}

170
lib/libc/io/unistd/mmap.c Normal file
View File

@@ -0,0 +1,170 @@
#include <errno.h>
#include <mango/handle.h>
#include <mango/task.h>
#include <mango/vm.h>
#include <rosetta/fs.h>
#include <stdbool.h>
#include <sys/mman.h>
static vm_prot_t vm_prot_from_mmap_prot(int prot)
{
vm_prot_t vm_prot = VM_PROT_USER;
if (prot & PROT_READ) {
vm_prot |= VM_PROT_READ;
}
if (prot & PROT_WRITE) {
vm_prot |= VM_PROT_WRITE;
}
if (prot & PROT_EXEC) {
vm_prot |= VM_PROT_EXEC;
}
return vm_prot;
}
static int get_vmo_anon(
int fd,
int prot,
int flags,
size_t length,
kern_handle_t *out)
{
vm_prot_t vm_prot = vm_prot_from_mmap_prot(prot);
kern_status_t status = vm_object_create(NULL, 0, length, vm_prot, out);
return __errno_from_kern_status(status);
}
static int get_vmo(
int fd,
int prot,
int flags,
size_t length,
kern_handle_t *out)
{
if (fd == -1) {
return get_vmo_anon(fd, prot, flags, length, out);
}
int err = 0;
kern_status_t status = fs_map(fd, prot, flags, &err, out);
if (status != KERN_OK) {
return __errno_from_kern_status(status);
}
return err;
}
static void *mreserve(kern_handle_t address_space, void *addr, size_t length)
{
virt_addr_t base = (virt_addr_t)addr;
if (!base) {
base = MAP_ADDRESS_ANY;
}
kern_status_t status
= address_space_reserve(address_space, base, length, &base);
if (status != KERN_OK) {
__set_errno(__errno_from_kern_status(status));
return MAP_FAILED;
}
return (void *)base;
}
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
{
int tmp = 0;
if (flags & MAP_SHARED) {
tmp++;
}
if (flags & MAP_SHARED_VALIDATE) {
tmp++;
}
if (flags & MAP_PRIVATE) {
tmp++;
}
if (tmp != 1) {
__set_errno(EINVAL);
return MAP_FAILED;
}
if ((flags & MAP_ANONYMOUS) && fd != -1) {
__set_errno(EINVAL);
return MAP_FAILED;
}
if (!(flags & MAP_ANONYMOUS) && fd == -1) {
__set_errno(EINVAL);
return MAP_FAILED;
}
kern_status_t status = KERN_OK;
kern_handle_t self = KERN_HANDLE_INVALID,
address_space = KERN_HANDLE_INVALID;
status = task_self(&self);
if (status != KERN_OK) {
__set_errno(__errno_from_kern_status(status));
return MAP_FAILED;
}
status = task_get_address_space(self, &address_space);
kern_handle_close(self);
if (status != KERN_OK) {
__set_errno(__errno_from_kern_status(status));
return MAP_FAILED;
}
if ((flags & (MAP_ANONYMOUS | MAP_PRIVATE)) && (prot == PROT_NONE)) {
void *ret = mreserve(address_space, addr, length);
kern_handle_close(address_space);
return ret;
}
kern_handle_t vmo = KERN_HANDLE_INVALID;
int err = get_vmo(fd, prot, flags, length, &vmo);
if (err != SUCCESS) {
kern_handle_close(address_space);
__set_errno(err);
return MAP_FAILED;
}
virt_addr_t map_address = 0;
vm_prot_t vm_prot = vm_prot_from_mmap_prot(prot);
if (addr && (flags & MAP_FIXED)) {
status = address_space_map(
address_space,
(virt_addr_t)addr,
vmo,
offset,
length,
vm_prot,
&map_address);
} else {
status = address_space_map(
address_space,
MAP_ADDRESS_ANY,
vmo,
offset,
length,
vm_prot,
&map_address);
}
kern_handle_close(vmo);
kern_handle_close(address_space);
if (status != KERN_OK) {
__set_errno(__errno_from_kern_status(status));
return MAP_FAILED;
}
return (void *)map_address;
}

View File

@@ -0,0 +1,44 @@
#include <errno.h>
#include <mango/handle.h>
#include <mango/task.h>
#include <mango/vm.h>
#include <rosetta/fs.h>
#include <stdbool.h>
#include <sys/mman.h>
int munmap(void *addr, size_t length)
{
kern_status_t status = KERN_OK;
kern_handle_t self = KERN_HANDLE_INVALID,
address_space = KERN_HANDLE_INVALID;
status = task_self(&self);
if (status != KERN_OK) {
return __set_errno(EPERM);
}
status = task_get_address_space(self, &address_space);
kern_handle_close(self);
if (status != KERN_OK) {
return __set_errno(EPERM);
}
status = address_space_unmap(address_space, (virt_addr_t)addr, length);
if (status != KERN_OK) {
kern_handle_close(address_space);
return __set_errno(__errno_from_kern_status(status));
}
status = address_space_release(
address_space,
(virt_addr_t)addr,
length);
kern_handle_close(address_space);
if (status != KERN_OK) {
return __set_errno(__errno_from_kern_status(status));
}
return __set_errno(SUCCESS);
}

40
lib/libc/io/unistd/open.c Normal file
View File

@@ -0,0 +1,40 @@
#include <errno.h>
#include <mango/handle.h>
#include <mango/msg.h>
#include <rosetta/fs.h>
#include <sys/remote.h>
int open(const char *path, int flags)
{
tid_t remote_tid;
unsigned int remote_chid;
if (!sys_remote_get(SYS_REMOTE_NSD, &remote_tid, &remote_chid)) {
return __set_errno(ENXIO);
}
kern_handle_t port;
kern_status_t status = port_create(&port);
if (status != KERN_OK) {
return __set_errno(__errno_from_kern_status(status));
}
status = port_connect(port, remote_tid, remote_chid);
if (status != KERN_OK) {
kern_handle_close(port);
return __set_errno(__errno_from_kern_status(status));
}
int err = SUCCESS;
status = fs_open(port, path, flags, &err);
if (status != KERN_OK) {
kern_handle_close(port);
return __set_errno(__errno_from_kern_status(status));
}
if (err != SUCCESS) {
kern_handle_close(port);
return __set_errno(err);
}
return (int)port;
}

21
lib/libc/io/unistd/read.c Normal file
View File

@@ -0,0 +1,21 @@
#include <errno.h>
#include <mango/handle.h>
#include <mango/msg.h>
#include <rosetta/fs.h>
#include <sys/remote.h>
int read(int fd, void *buf, size_t count)
{
int err;
size_t nr_read;
kern_status_t status = fs_read(fd, count, &err, &nr_read, buf, count);
if (status != KERN_OK) {
return __set_errno(__errno_from_kern_status(status));
}
if (err != SUCCESS) {
return __set_errno(err);
}
return nr_read;
}

View File

View File

@@ -1,4 +1,4 @@
set(source_dirs stdlib)
set(source_dirs stdlib string)
file(GLOB sources *.c *.h)
@@ -30,4 +30,5 @@ sysroot_add_library(
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_compile_definitions(libc-malloc PRIVATE ENABLE_GLOBAL_HEAP=1)
target_link_libraries(libc-malloc libc-core libmango)

View File

@@ -38,14 +38,10 @@ static kern_status_t init_heap_region(heap_t *heap)
task_get_address_space(self, &address_space);
kern_handle_close(self);
kern_status_t status = vm_region_create(
kern_status_t status = address_space_reserve(
address_space,
"libc-heap",
9,
VM_REGION_ANY_OFFSET,
MAP_ADDRESS_ANY,
HEAP_REGION_SIZE,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
&heap->heap_region,
&heap->heap_base);
kern_handle_close(address_space);
@@ -72,9 +68,14 @@ static kern_status_t expand_heap(heap_t *heap)
virt_addr_t base = 0;
status = vm_region_map_relative(
heap->heap_region,
heap->heap_sys_alloc,
kern_handle_t self, address_space;
task_self(&self);
task_get_address_space(self, &address_space);
kern_handle_close(self);
status = address_space_map(
address_space,
heap->heap_base + heap->heap_sys_alloc,
vmo,
0,
HEAP_EXPAND_INCREMENT,
@@ -90,7 +91,7 @@ static kern_status_t expand_heap(heap_t *heap)
void *heap_expand(heap_t *heap, size_t size)
{
kern_status_t status = KERN_OK;
if (heap->heap_region == KERN_HANDLE_INVALID) {
if (!heap->heap_base) {
status = init_heap_region(heap);
}

View File

@@ -6,7 +6,6 @@ struct liballoc_minor;
#define HEAP_INIT \
{ \
.heap_region = KERN_HANDLE_INVALID, \
.heap_liballoc = { \
.l_pageSize = 4096, \
.l_pageCount = 16, \

View File

@@ -12,7 +12,6 @@ typedef enum heap_result {
} heap_result_t;
typedef struct heap {
kern_handle_t heap_region;
/* amount of space requested from the heap by the user */
size_t heap_req_alloc;
/* amount of space requested from the system by the heap */

View File

@@ -0,0 +1,14 @@
#include <stdlib.h>
#include <string.h>
char *strdup(char *s)
{
size_t len = strlen(s);
char *out = malloc(len + 1);
if (!out) {
return NULL;
}
memcpy(out, s, len + 1);
return out;
}

View File

@@ -0,0 +1,42 @@
set(source_dirs thread)
foreach (dir ${source_dirs})
file(GLOB dir_sources ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.c)
file(GLOB dir_headers ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.h)
set(sources ${sources} ${dir_sources})
set(headers ${headers} ${dir_headers})
endforeach (dir)
file(GLOB sys_sources
${CMAKE_CURRENT_SOURCE_DIR}/sys/${CMAKE_SYSTEM_PROCESSOR}/*.c
${CMAKE_CURRENT_SOURCE_DIR}/sys/${CMAKE_SYSTEM_PROCESSOR}/*.S)
set_property(SOURCE ${sys_sources} PROPERTY LANGUAGE C)
set(sources ${sources} ${sys_sources})
set(headers ${headers} ${CMAKE_CURRENT_SOURCE_DIR}/include/pthread.h)
set(public_include_dir ${CMAKE_CURRENT_SOURCE_DIR}/include)
rosetta_add_library(STATIC
NAME libc-pthread
PUBLIC_INCLUDE_DIRS ${public_include_dir}
SOURCES ${sources}
HEADERS ${headers})
rosetta_add_library(SHARED
NAME libpthread
PUBLIC_INCLUDE_DIRS ${public_include_dir}
SOURCES ${sources}
HEADERS ${headers})
sysroot_add_library(
NAME libc-pthread
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
sysroot_add_library(
NAME libpthread
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libc-pthread libc-io libmango)
target_link_libraries(libpthread libmango libc)

View File

@@ -0,0 +1,25 @@
#ifndef PTHREAD_H_
#define PTHREAD_H_
#define __PTHREAD_ATTR_SIZE__ 32
typedef struct __pthread *pthread_t;
typedef struct __pthread_attr {
long __sig;
char __opaque[__PTHREAD_ATTR_SIZE__];
} pthread_attr_t;
extern int __pthread_init(void);
extern int pthread_create(
pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_func)(void *),
void *arg);
extern pthread_t pthread_self(void);
extern int pthread_join(pthread_t thread, void **retval);
extern int pthread_detach(pthread_t thread);
extern void pthread_exit(void *retval);
#endif

0
lib/libc/pthread/init.c Normal file
View File

View File

@@ -0,0 +1,13 @@
.code64
.global pthread_self
.type pthread_self, @function
pthread_self:
push %rbp
mov %rsp, %rbp
mov %gs:0, %rax
pop %rbp
ret

View File

@@ -0,0 +1,35 @@
.code64
#include "mango/syscall.h"
.global __pthread_unmap_exit
.type __pthread_unmap_exit, @function
/*
%rdi = (kern_handle_t)address_space
%rsi = (void *)unmap_base
%rdx = (size_t)unmap_length
*/
__pthread_unmap_exit:
push %rbp
mov %rsp, %rbp
/* save the address space handle for later */
mov %rdi, %rbx
/* first, unmap the specified region */
mov $SYS_ADDRESS_SPACE_UNMAP, %rax
/* args are already in the correct registers */
syscall
/* next, close the handle to the address space */
mov $SYS_THREAD_EXIT, %rax
mov %rbx, %rdi
syscall
/* finally, stop the current thread */
mov $SYS_THREAD_EXIT, %rax
syscall
/* unreachable */
ret

View File

@@ -0,0 +1,24 @@
#ifndef _THREAD_PTHREAD_H_
#define _THREAD_PTHREAD_H_
#include <mango/types.h>
enum pthread_flags {
THREAD_DETACHED = 0x01u,
};
struct __pthread {
struct __pthread *thr_self;
enum pthread_flags thr_flags;
kern_handle_t thr_handle;
void *thr_map_base;
size_t thr_map_size;
void *thr_result;
};
extern void __pthread_unmap_exit(
kern_handle_t address_space,
void *unmap_base,
size_t unmap_length);
#endif

View File

View File

@@ -0,0 +1,89 @@
#include "pthread.h"
#include <errno.h>
#include <mango/status.h>
#include <mango/task.h>
#include <pthread.h>
#include <string.h>
#include <sys/mman.h>
#define DEFAULT_STACK_SIZE 0x40000
static void thread_launchpad(uintptr_t thread, uintptr_t func, uintptr_t arg)
{
struct __pthread *self = (struct __pthread *)thread;
void *(*start_func)(void *) = (void *(*)(void *))func;
void *result = start_func((void *)arg);
pthread_exit(result);
}
int pthread_create(
pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_func)(void *),
void *arg)
{
kern_status_t status = KERN_OK;
kern_handle_t task;
status = task_self(&task);
if (status != KERN_OK) {
return __set_errno(EPERM);
}
size_t stack_size = DEFAULT_STACK_SIZE;
void *base
= mmap(NULL,
stack_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0);
if (!base) {
return __set_errno(ENOMEM);
}
virt_addr_t sp = (virt_addr_t)base + DEFAULT_STACK_SIZE;
sp -= sizeof(struct __pthread);
struct __pthread *new = (struct __pthread *)sp;
sp &= ~(virt_addr_t)0x10;
memset(new, 0x0, sizeof *new);
new->thr_self = new;
new->thr_map_base = base;
new->thr_map_size = stack_size;
uintptr_t args[] = {
(uintptr_t)new,
(uintptr_t)start_func,
(uintptr_t)arg,
};
size_t nr_args = sizeof args / sizeof args[0];
status = task_create_thread(
task,
(virt_addr_t)thread_launchpad,
sp,
args,
nr_args,
&new->thr_handle);
if (status != KERN_OK) {
munmap(base, stack_size);
return __set_errno(__errno_from_kern_status(status));
}
thread_config_set(
new->thr_handle,
THREAD_CFG_GSBASE,
&new,
sizeof(void *));
thread_start(new->thr_handle);
*thread = new;
return __set_errno(SUCCESS);
}

View File

@@ -0,0 +1,17 @@
#include "pthread.h"
#include <errno.h>
#include <mango/handle.h>
#include <mango/object.h>
#include <mango/signal.h>
#include <mango/task.h>
#include <pthread.h>
#include <sys/mman.h>
int pthread_detach(struct __pthread *thread)
{
thread->thr_flags |= THREAD_DETACHED;
kern_handle_close(thread->thr_handle);
return 0;
}

View File

@@ -0,0 +1,31 @@
#include "pthread.h"
#include <mango/handle.h>
#include <mango/task.h>
#include <pthread.h>
#include <stdlib.h>
extern void pthread_exit(void *retval)
{
struct __pthread *self = pthread_self();
if (!self) {
/* TODO: abort(); */
return;
}
if (self->thr_flags & THREAD_DETACHED) {
kern_handle_t task;
kern_handle_t address_space;
task_self(&task);
task_get_address_space(task, &address_space);
kern_handle_close(task);
__pthread_unmap_exit(
address_space,
self->thr_map_base,
self->thr_map_size);
} else {
self->thr_result = retval;
thread_exit();
}
}

View File

@@ -0,0 +1,31 @@
#include "pthread.h"
#include <errno.h>
#include <mango/handle.h>
#include <mango/object.h>
#include <mango/signal.h>
#include <mango/task.h>
#include <pthread.h>
#include <sys/mman.h>
int pthread_join(struct __pthread *thread, void **retval)
{
kern_wait_item_t wait;
wait.w_handle = thread->thr_handle;
wait.w_waitfor = THREAD_SIGNAL_STOPPED;
wait.w_observed = 0;
kern_status_t status = kern_object_wait(&wait, 1);
if (status != KERN_OK) {
return __set_errno(EINVAL);
}
if (retval) {
*retval = thread->thr_result;
}
kern_handle_close(thread->thr_handle);
munmap(thread->thr_map_base, thread->thr_map_size);
return 0;
}

View File

View File

@@ -6,6 +6,9 @@
.extern main
.type main, @function
.extern task_exit
.type task_exit, @function
_start:
# Args (as provided by the ABI)
# %rdi: int argc
@@ -13,4 +16,7 @@ _start:
# %rdx: kern_handle_t task
# %rcx: kern_handle_t address_space
call main
mov %rax, %rdi
call task_exit
1: jmp 1b

View File

@@ -23,7 +23,7 @@ sysroot_add_library(
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libfs libmango interface::fs libc)
target_link_libraries(libfs-static libmango interface::fs libc-core)
target_link_libraries(libfs libmango interface::fs libc libxpc)
target_link_libraries(libfs-static libmango interface::fs libc-core libxpc-static)
set_target_properties(libfs-static PROPERTIES POSITION_INDEPENDENT_CODE FALSE)

View File

@@ -1,9 +1,23 @@
#include "btree.h"
#include "file.h"
#include "interface.h"
#include "mapping.h"
#include <fs/allocator.h>
#include <fs/context.h>
#include <fs/dentry.h>
#include <fs/inode.h>
#include <fs/status.h>
#include <fs/superblock.h>
#include <mango/handle.h>
#include <mango/log.h>
#include <mango/object.h>
#include <mango/signal.h>
#include <mango/task.h>
#include <mango/vm.h>
#include <stdio.h>
#define TEMP_OBJECT_SIZE 0x10000
BTREE_DEFINE_SIMPLE_GET(struct fs_file, unsigned long, f_node, f_id, get_file);
BTREE_DEFINE_SIMPLE_INSERT(struct fs_file, f_node, f_id, put_file);
@@ -12,14 +26,21 @@ struct fs_context {
struct fs_superblock *ctx_sb;
struct fs_allocator *ctx_alloc;
struct btree ctx_filelist;
kern_handle_t ctx_vm_controller;
kern_handle_t ctx_channel;
kern_handle_t ctx_temp_object;
void *ctx_temp_object_buf;
struct fs_vtable ctx_vtable;
};
struct fs_context *fs_context_create(
struct fs_allocator *alloc,
struct fs_superblock *sb)
struct fs_context *fs_context_create(struct fs_allocator *alloc)
{
kern_handle_t self, address_space;
task_self(&self);
task_get_address_space(self, &address_space);
kern_handle_close(self);
struct fs_context *ctx = fs_alloc(alloc, sizeof *ctx);
if (!ctx) {
return NULL;
@@ -27,10 +48,50 @@ struct fs_context *fs_context_create(
memset(ctx, 0x0, sizeof *ctx);
ctx->ctx_sb = sb;
kern_status_t status = vm_controller_create(&ctx->ctx_vm_controller);
if (status != KERN_OK) {
fs_free(alloc, ctx);
return NULL;
}
status = vm_object_create(
NULL,
0,
TEMP_OBJECT_SIZE,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
&ctx->ctx_temp_object);
if (status != KERN_OK) {
kern_handle_close(ctx->ctx_vm_controller);
fs_free(alloc, ctx);
return NULL;
}
virt_addr_t temp_buffer;
status = address_space_map(
address_space,
MAP_ADDRESS_ANY,
ctx->ctx_temp_object,
0,
TEMP_OBJECT_SIZE,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
&temp_buffer);
kern_handle_close(address_space);
if (status != KERN_OK) {
kern_handle_close(ctx->ctx_temp_object);
kern_handle_close(ctx->ctx_vm_controller);
fs_free(alloc, ctx);
return NULL;
}
ctx->ctx_temp_object_buf = (void *)temp_buffer;
ctx->ctx_alloc = alloc;
ctx->ctx_vtable.open = fs_msg_open;
ctx->ctx_vtable.close = fs_msg_close;
ctx->ctx_vtable.read = fs_msg_read;
ctx->ctx_vtable.write = fs_msg_write;
ctx->ctx_vtable.seek = fs_msg_seek;
ctx->ctx_vtable.map = fs_msg_map;
return ctx;
}
@@ -40,6 +101,141 @@ void fs_context_destroy(struct fs_context *ctx)
fs_free(ctx->ctx_alloc, ctx);
}
enum fs_status fs_context_mount_filesystem(
struct fs_context *ctx,
fs_mount_function_t func,
void *arg,
enum fs_mount_flags flags)
{
if (!func) {
return FS_ERR_INVALID_ARGUMENT;
}
struct fs_superblock *sb = NULL;
enum fs_status status = func(ctx, arg, flags, &sb);
if (status != FS_SUCCESS) {
return status;
}
if (!sb) {
return FS_ERR_INTERNAL_FAILURE;
}
ctx->ctx_sb = sb;
return FS_SUCCESS;
}
enum fs_status fs_context_unmount_filesystem(struct fs_context *ctx)
{
return FS_ERR_NOT_IMPLEMENTED;
}
void fs_context_set_channel(struct fs_context *ctx, kern_handle_t channel)
{
ctx->ctx_channel = channel;
}
kern_handle_t fs_context_get_vm_controller(const struct fs_context *ctx)
{
return ctx->ctx_vm_controller;
}
static enum fs_status handle_msg(struct fs_context *ctx)
{
xpc_msg_t msg;
kern_status_t status = xpc_msg_recv_nowait(ctx->ctx_channel, &msg);
if (status == KERN_NO_ENTRY) {
return FS_SUCCESS;
}
if (status != KERN_OK) {
kern_tracef("message recv error %d", status);
return FS_ERR_INTERNAL_FAILURE;
}
switch (msg.msg_header.hdr_interface) {
case INTERFACE_FS:
status = fs_context_dispatch_msg(ctx, &msg);
break;
default:
kern_tracef(
"unknown message protocol %u",
msg.msg_header.hdr_interface);
xpc_msg_reply_error(&msg, KERN_UNSUPPORTED);
break;
}
return FS_SUCCESS;
}
static enum fs_status handle_page_request(struct fs_context *ctx)
{
equeue_packet_page_request_t packet;
vm_controller_recv(ctx->ctx_vm_controller, &packet);
struct file_mapping *mapping = (struct file_mapping *)packet.req_vmo;
kern_tracef(
"received page request [%zx-%zx] for file %s",
packet.req_offset,
packet.req_offset + packet.req_length,
mapping->m_file->f_dent->d_name);
size_t length = packet.req_length;
if (length > TEMP_OBJECT_SIZE) {
length = TEMP_OBJECT_SIZE;
}
char *dst = ctx->ctx_temp_object_buf;
xpc_buffer_t buf = XPC_LOCAL_BUFFER_OUT(dst, TEMP_OBJECT_SIZE);
enum fs_status status = fs_file_read_at(
mapping->m_file,
&buf,
packet.req_offset,
length);
if (status != FS_SUCCESS) {
kern_tracef("map-read failed with code %d", status);
return status;
}
vm_controller_supply_pages(
ctx->ctx_vm_controller,
mapping->m_vmo,
packet.req_offset,
ctx->ctx_temp_object,
0,
packet.req_length);
return FS_SUCCESS;
}
enum fs_status fs_context_handle_request(struct fs_context *ctx)
{
kern_wait_item_t waiters[] = {
{
.w_handle = ctx->ctx_channel,
.w_waitfor = CHANNEL_SIGNAL_MSG_RECEIVED,
},
{
.w_handle = ctx->ctx_vm_controller,
.w_waitfor = VM_CONTROLLER_SIGNAL_REQUEST_RECEIVED,
},
};
const size_t nr_waiters = sizeof waiters / sizeof waiters[0];
kern_status_t kstatus = kern_object_wait(waiters, nr_waiters);
if (kstatus != KERN_OK) {
return FS_ERR_INTERNAL_FAILURE;
}
if (waiters[0].w_observed & CHANNEL_SIGNAL_MSG_RECEIVED) {
return handle_msg(ctx);
}
if (waiters[1].w_observed & VM_CONTROLLER_SIGNAL_REQUEST_RECEIVED) {
return handle_page_request(ctx);
}
return FS_SUCCESS;
}
struct fs_file *fs_context_open_file(struct fs_context *ctx, unsigned long id)
{
struct fs_file *f = get_file(&ctx->ctx_filelist, id);
@@ -58,29 +254,104 @@ struct fs_file *fs_context_open_file(struct fs_context *ctx, unsigned long id)
return f;
}
struct fs_file *fs_context_get_file(struct fs_context *ctx, unsigned long id)
{
return get_file(&ctx->ctx_filelist, id);
}
void fs_context_close_file(struct fs_context *ctx, struct fs_file *f)
{
}
struct fs_dentry *fs_context_resolve_path(
struct fs_context *ctx,
const char *path)
static size_t get_first_path_component(const char *in, char *out, size_t max)
{
return NULL;
size_t i = 0;
while (i < max - 1) {
if (in[i] == '\0' || in[i] == '/') {
break;
}
out[i] = in[i];
i++;
}
out[i] = '\0';
return i;
}
kern_status_t fs_context_dispatch_msg(
extern enum fs_status fs_context_resolve_path(
struct fs_context *ctx,
kern_handle_t channel,
struct msg_endpoint *sender,
struct msg_header *hdr)
const char *path,
struct fs_dentry **out)
{
return fs_dispatch(
channel,
&ctx->ctx_vtable,
sender,
hdr,
NULL,
0,
ctx);
if (!ctx->ctx_sb || !ctx->ctx_sb->s_root) {
return FS_ERR_NO_ENTRY;
}
struct fs_dentry *cur = ctx->ctx_sb->s_root;
char tok[256];
while (*path != '\0') {
while (*path == '/') {
path++;
}
size_t tok_len
= get_first_path_component(path, tok, sizeof tok);
if (!tok_len) {
break;
}
bool is_dir = *(path + tok_len) != '\0';
if (cur->d_inode->i_mode != FS_INODE_DIR) {
return FS_ERR_NOT_DIRECTORY;
}
struct fs_dentry *next = NULL;
enum fs_status status
= fs_inode_lookup(cur->d_inode, tok, &next);
if (status != FS_SUCCESS) {
return status;
}
if (!next) {
return FS_ERR_INTERNAL_FAILURE;
}
cur = next;
path += tok_len;
}
*out = cur;
return FS_SUCCESS;
}
kern_status_t fs_context_dispatch_msg(struct fs_context *ctx, xpc_msg_t *msg)
{
return fs_dispatch(NULL, msg, &ctx->ctx_vtable, ctx);
}
void *fs_context_alloc(struct fs_context *ctx, size_t count)
{
return fs_alloc(ctx->ctx_alloc, count);
}
void *fs_context_calloc(struct fs_context *ctx, size_t count, size_t sz)
{
return fs_calloc(ctx->ctx_alloc, count, sz);
}
void *fs_context_realloc(struct fs_context *ctx, void *p, size_t count)
{
return fs_realloc(ctx->ctx_alloc, p, count);
}
void fs_context_free(struct fs_context *ctx, void *p)
{
fs_free(ctx->ctx_alloc, p);
}

54
lib/libfs/file.c Normal file
View File

@@ -0,0 +1,54 @@
#include "file.h"
struct fs_inode *fs_file_get_inode(const struct fs_file *f)
{
return f->f_inode;
}
size_t fs_file_get_cursor(const struct fs_file *f)
{
return f->f_seek;
}
enum fs_status fs_file_read(
struct fs_file *f,
struct xpc_buffer *buf,
size_t count)
{
if (!f->f_ops || !f->f_ops->f_read) {
return FS_ERR_NOT_IMPLEMENTED;
}
off_t seek = f->f_seek;
enum fs_status status = f->f_ops->f_read(f, buf, count, &seek);
f->f_seek = seek;
return status;
}
enum fs_status fs_file_read_at(
struct fs_file *f,
struct xpc_buffer *buf,
off_t offset,
size_t count)
{
if (!f->f_ops || !f->f_ops->f_read) {
return FS_ERR_NOT_IMPLEMENTED;
}
return f->f_ops->f_read(f, buf, count, &offset);
}
enum fs_status fs_file_write(
struct fs_file *f,
const struct xpc_buffer *buf,
size_t count)
{
if (!f->f_ops || !f->f_ops->f_write) {
return FS_ERR_NOT_IMPLEMENTED;
}
off_t seek = f->f_seek;
enum fs_status status = f->f_ops->f_write(f, buf, count, &seek);
f->f_seek = seek;
return status;
}

View File

@@ -2,8 +2,11 @@
#define _FS_FILE_H_
#include "btree.h"
#include "queue.h"
#include <fs/dentry.h>
#include <fs/file.h>
#include <fs/inode.h>
struct fs_file {
/* id of the open file, equal to the koid of the port being used to
@@ -12,6 +15,12 @@ struct fs_file {
struct btree_node f_node;
const struct fs_file_ops *f_ops;
off_t f_seek;
struct fs_inode *f_inode;
struct fs_dentry *f_dent;
struct queue f_mappings;
};
#endif

View File

@@ -2,30 +2,60 @@
#define FS_CONTEXT_H_
#include <rosetta/fs.h>
#include <xpc/msg.h>
struct fs_file;
struct fs_dentry;
struct fs_context;
struct fs_allocator;
struct fs_superblock;
extern struct fs_context *fs_context_create(
struct fs_allocator *alloc,
struct fs_superblock *sb);
enum fs_mount_flags {
FS_MOUNT_READONLY = 0x01u,
};
typedef enum fs_status (*fs_mount_function_t)(
struct fs_context *,
void *arg,
enum fs_mount_flags,
struct fs_superblock **);
extern struct fs_context *fs_context_create(struct fs_allocator *alloc);
extern void fs_context_destroy(struct fs_context *ctx);
extern enum fs_status fs_context_mount_filesystem(
struct fs_context *ctx,
fs_mount_function_t func,
void *arg,
enum fs_mount_flags flags);
extern enum fs_status fs_context_unmount_filesystem(struct fs_context *ctx);
extern void fs_context_set_channel(
struct fs_context *ctx,
kern_handle_t channel);
extern kern_handle_t fs_context_get_vm_controller(const struct fs_context *ctx);
extern enum fs_status fs_context_handle_request(struct fs_context *ctx);
extern struct fs_file *fs_context_open_file(
struct fs_context *ctx,
unsigned long id);
extern struct fs_file *fs_context_get_file(
struct fs_context *ctx,
unsigned long id);
extern void fs_context_close_file(struct fs_context *ctx, struct fs_file *f);
extern struct fs_dentry *fs_context_resolve_path(
extern enum fs_status fs_context_resolve_path(
struct fs_context *ctx,
const char *path);
const char *path,
struct fs_dentry **out);
extern kern_status_t fs_context_dispatch_msg(
struct fs_context *ctx,
kern_handle_t channel,
struct msg_endpoint *sender,
struct msg_header *hdr);
xpc_msg_t *msg);
extern void *fs_context_alloc(struct fs_context *ctx, size_t count);
extern void *fs_context_calloc(struct fs_context *ctx, size_t count, size_t sz);
extern void *fs_context_realloc(struct fs_context *ctx, void *p, size_t count);
extern void fs_context_free(struct fs_context *ctx, void *p);
#endif

View File

@@ -13,6 +13,7 @@ struct fs_dentry {
struct fs_superblock *d_sb;
const struct fs_dentry_ops *d_ops;
void *d_fsdata;
char *d_name;
};
#endif

View File

@@ -5,11 +5,36 @@
#include <stddef.h>
struct fs_file;
struct xpc_buffer;
struct fs_file_ops {
ssize_t (*f_read)(struct fs_file *, void *, size_t);
ssize_t (*f_write)(struct fs_file *, const void *, size_t);
off_t (*f_seek)(struct fs_file *, off_t, int);
enum fs_status (*f_read)(
struct fs_file *,
struct xpc_buffer *,
size_t,
off_t *);
enum fs_status (*f_write)(
struct fs_file *,
const struct xpc_buffer *,
size_t,
off_t *);
enum fs_status (*f_seek)(struct fs_file *, off_t, int);
};
extern struct fs_inode *fs_file_get_inode(const struct fs_file *f);
extern size_t fs_file_get_cursor(const struct fs_file *f);
extern enum fs_status fs_file_read(
struct fs_file *f,
struct xpc_buffer *buf,
size_t count);
extern enum fs_status fs_file_read_at(
struct fs_file *f,
struct xpc_buffer *buf,
off_t offset,
size_t count);
extern enum fs_status fs_file_write(
struct fs_file *f,
const struct xpc_buffer *buf,
size_t count);
#endif

View File

@@ -1,17 +1,40 @@
#ifndef FS_INODE_H_
#define FS_INODE_H_
#include <fs/status.h>
#include <mango/types.h>
#include <stddef.h>
struct fs_inode;
struct fs_dentry;
struct fs_superblock;
struct fs_file_ops;
struct file_mapping;
enum fs_inode_mode {
FS_INODE_REG = 0x01u,
FS_INODE_DIR = 0x02u,
};
struct fs_inode_ops {
int (*i_lookup)(struct fs_inode *, struct fs_dentry *);
enum fs_status (*i_lookup)(
struct fs_inode *,
const char *,
struct fs_dentry **);
};
struct fs_inode {
enum fs_inode_mode i_mode;
struct fs_superblock *i_sb;
const struct fs_inode_ops *i_ops;
const struct fs_file_ops *i_fops;
size_t i_size;
struct file_mapping *i_shared_mapping;
};
extern enum fs_status fs_inode_lookup(
struct fs_inode *inode,
const char *name,
struct fs_dentry **out);
#endif

View File

@@ -0,0 +1,18 @@
#ifndef FS_STATUS_H_
#define FS_STATUS_H_
enum fs_status {
FS_SUCCESS = 0,
FS_ERR_NO_ENTRY,
FS_ERR_NO_MEMORY,
FS_ERR_INVALID_ARGUMENT,
FS_ERR_NOT_IMPLEMENTED,
FS_ERR_IS_DIRECTORY,
FS_ERR_NOT_DIRECTORY,
FS_ERR_BAD_STATE,
FS_ERR_INTERNAL_FAILURE,
};
extern int fs_status_to_errno(enum fs_status status);
#endif

13
lib/libfs/inode.c Normal file
View File

@@ -0,0 +1,13 @@
#include <fs/inode.h>
enum fs_status fs_inode_lookup(
struct fs_inode *inode,
const char *name,
struct fs_dentry **out)
{
if (!inode->i_ops || !inode->i_ops->i_lookup) {
return FS_ERR_NOT_IMPLEMENTED;
}
return inode->i_ops->i_lookup(inode, name, out);
}

View File

@@ -2,20 +2,57 @@
#define _FS_INTERFACE_H_
#include <mango/types.h>
#include <xpc/buffer.h>
#include <xpc/context.h>
#include <xpc/endpoint.h>
#include <xpc/string.h>
struct msg_endpoint;
extern kern_status_t fs_msg_open(
const struct msg_endpoint *sender,
const char *path,
xpc_context_t *ctx,
const xpc_endpoint_t *sender,
const xpc_string_t *path,
int flags,
int *out_err,
void *arg);
extern kern_status_t fs_msg_close(
const struct msg_endpoint *sender,
const char *path,
int flags,
xpc_context_t *ctx,
const xpc_endpoint_t *sender,
int *out_err,
void *arg);
extern kern_status_t fs_msg_read(
xpc_context_t *ctx,
const xpc_endpoint_t *sender,
size_t count,
int *out_err,
size_t *out_nr_read,
xpc_buffer_t *out_data,
void *arg);
extern kern_status_t fs_msg_write(
xpc_context_t *ctx,
const xpc_endpoint_t *sender,
const xpc_buffer_t *data,
int *out_err,
size_t *out_nr_written,
void *arg);
extern kern_status_t fs_msg_seek(
xpc_context_t *ctx,
const xpc_endpoint_t *sender,
off_t offset,
int origin,
int *out_err,
off_t *out_new_pos,
void *arg);
extern kern_status_t fs_msg_map(
xpc_context_t *ctx,
const xpc_endpoint_t *sender,
int prot,
int flags,
int *out_err,
kern_handle_t *out_vmo,
void *arg);
#endif

View File

@@ -0,0 +1,14 @@
#include "../interface.h"
#include <errno.h>
#include <mango/status.h>
extern kern_status_t fs_msg_close(
xpc_context_t *ctx,
const xpc_endpoint_t *sender,
int *out_err,
void *arg)
{
*out_err = ENOSYS;
return KERN_OK;
}

112
lib/libfs/interface/map.c Normal file
View File

@@ -0,0 +1,112 @@
#include "../file.h"
#include "../mapping.h"
#include <errno.h>
#include <fs/context.h>
#include <fs/file.h>
#include <fs/status.h>
#include <mango/handle.h>
#include <mango/log.h>
#include <mango/vm.h>
#include <stdio.h>
#include <sys/mman.h>
static int create_file_mapping(
struct fs_context *ctx,
int prot,
int flags,
struct fs_file *f,
struct file_mapping **out)
{
if ((flags & MAP_SHARED) && f->f_inode->i_shared_mapping) {
*out = f->f_inode->i_shared_mapping;
return SUCCESS;
}
struct file_mapping *mapping = fs_context_alloc(ctx, sizeof *mapping);
if (!mapping) {
return ENOMEM;
}
memset(mapping, 0x0, sizeof *mapping);
vm_prot_t vm_prot = VM_PROT_USER;
if (prot & PROT_READ) {
vm_prot |= VM_PROT_READ;
}
if (prot & PROT_WRITE) {
vm_prot |= VM_PROT_WRITE;
}
if (prot & PROT_EXEC) {
vm_prot |= VM_PROT_EXEC;
}
kern_handle_t vmo = KERN_HANDLE_INVALID;
kern_status_t status = vm_controller_create_object(
fs_context_get_vm_controller(ctx),
f->f_dent->d_name,
strlen(f->f_dent->d_name),
(equeue_key_t)mapping,
f->f_inode->i_size,
vm_prot,
&vmo);
if (status != KERN_OK) {
fs_context_free(ctx, mapping);
return __errno_from_kern_status(status);
}
mapping->m_file = f;
mapping->m_vmo = vmo;
if (flags & MAP_SHARED) {
mapping->m_type = FILE_MAPPING_SHARED;
f->f_inode->i_shared_mapping = mapping;
} else {
mapping->m_type = FILE_MAPPING_PRIVATE;
queue_push_back(&f->f_mappings, &mapping->m_entry);
}
*out = mapping;
return SUCCESS;
}
extern kern_status_t fs_msg_map(
xpc_context_t *xpc,
const xpc_endpoint_t *sender,
int prot,
int flags,
int *out_err,
kern_handle_t *out_vmo,
void *arg)
{
struct fs_context *ctx = arg;
struct fs_file *f = fs_context_get_file(ctx, sender->e_port);
if (!f) {
*out_err = EBADF;
return KERN_OK;
}
struct file_mapping *mapping = NULL;
int err = create_file_mapping(ctx, prot, flags, f, &mapping);
if (err != SUCCESS) {
*out_err = err;
return KERN_OK;
}
kern_tracef(
"mapping file %s (%s) using vmo %zx",
f->f_dent->d_name,
(flags & MAP_SHARED) ? "shared" : "private",
mapping->m_vmo);
kern_handle_t vmo;
kern_handle_duplicate(mapping->m_vmo, &vmo);
*out_err = SUCCESS;
*out_vmo = vmo;
return KERN_OK;
}

View File

@@ -1,21 +1,53 @@
#include "../file.h"
#include <errno.h>
#include <fs/context.h>
#include <fs/file.h>
#include <fs/status.h>
extern kern_status_t fs_msg_open(
const struct msg_endpoint *sender,
const char *path,
xpc_context_t *xpc,
xpc_endpoint_t *sender,
const xpc_string_t *path,
int flags,
int *out_err,
void *arg)
{
char path_buf[4096];
size_t path_len = 0;
kern_status_t status
= xpc_string_read(path, path_buf, sizeof path_buf, &path_len);
if (status != KERN_OK) {
return status;
}
struct fs_context *ctx = arg;
struct fs_dentry *dent = fs_context_resolve_path(ctx, path);
if (!dent) {
*out_err = ENOENT;
struct fs_file *f = fs_context_open_file(ctx, sender->e_port);
if (!f) {
*out_err = ENOMEM;
return KERN_OK;
}
if (f->f_inode) {
*out_err = EBUSY;
return KERN_OK;
}
struct fs_dentry *dent = NULL;
enum fs_status fs_status
= fs_context_resolve_path(ctx, path_buf, &dent);
if (fs_status != FS_SUCCESS) {
fs_context_close_file(ctx, f);
*out_err = fs_status_to_errno(status);
return KERN_OK;
}
f->f_seek = 0;
f->f_dent = dent;
f->f_inode = dent->d_inode;
f->f_ops = dent->d_inode->i_fops;
*out_err = SUCCESS;
return KERN_OK;
}

View File

@@ -0,0 +1,30 @@
#include <errno.h>
#include <fs/context.h>
#include <fs/file.h>
#include <fs/status.h>
extern kern_status_t fs_msg_read(
xpc_context_t *xpc,
xpc_endpoint_t *sender,
size_t count,
int *out_err,
size_t *out_nr_read,
xpc_buffer_t *out_data,
void *arg)
{
struct fs_context *ctx = arg;
struct fs_file *f = fs_context_get_file(ctx, sender->e_port);
if (!f) {
*out_err = EBADF;
return KERN_OK;
}
size_t start = fs_file_get_cursor(f);
enum fs_status status = fs_file_read(f, out_data, count);
size_t end = fs_file_get_cursor(f);
*out_err = fs_status_to_errno(status);
*out_nr_read = end - start;
return KERN_OK;
}

View File

@@ -0,0 +1,52 @@
#include "../file.h"
#include <errno.h>
#include <fs/context.h>
#include <fs/file.h>
#include <fs/status.h>
#include <sys/types.h>
extern kern_status_t fs_msg_seek(
xpc_context_t *xpc,
const xpc_endpoint_t *sender,
off_t rel_offset,
int origin,
int *out_err,
off_t *out_new_pos,
void *arg)
{
struct fs_context *ctx = arg;
struct fs_file *f = fs_context_get_file(ctx, sender->e_port);
if (!f) {
*out_err = EBADF;
return KERN_OK;
}
off_t new_offset = 0;
switch (origin) {
case SEEK_SET:
new_offset = rel_offset;
break;
case SEEK_CUR:
new_offset = f->f_seek + rel_offset;
break;
case SEEK_END:
new_offset = f->f_inode->i_size + rel_offset;
break;
default:
*out_err = EINVAL;
return KERN_OK;
}
if (new_offset > f->f_inode->i_size) {
*out_err = EINVAL;
return KERN_OK;
}
f->f_seek = new_offset;
*out_err = SUCCESS;
*out_new_pos = new_offset;
return KERN_OK;
}

View File

@@ -0,0 +1,29 @@
#include <errno.h>
#include <fs/context.h>
#include <fs/file.h>
#include <fs/status.h>
extern kern_status_t fs_msg_write(
xpc_context_t *xpc,
const xpc_endpoint_t *sender,
const xpc_buffer_t *data,
int *out_err,
size_t *out_nr_written,
void *arg)
{
struct fs_context *ctx = arg;
struct fs_file *f = fs_context_get_file(ctx, sender->e_port);
if (!f) {
*out_err = EBADF;
return KERN_OK;
}
size_t start = fs_file_get_cursor(f);
enum fs_status status = fs_file_write(f, data, data->buf_len);
size_t end = fs_file_get_cursor(f);
*out_err = fs_status_to_errno(status);
*out_nr_written = end - start;
return KERN_OK;
}

22
lib/libfs/mapping.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef MAPPING_H_
#define MAPPING_H_
#include "queue.h"
#include <mango/types.h>
struct fs_file;
enum file_mapping_type {
FILE_MAPPING_PRIVATE,
FILE_MAPPING_SHARED,
};
struct file_mapping {
enum file_mapping_type m_type;
struct fs_file *m_file;
kern_handle_t m_vmo;
struct queue_entry m_entry;
};
#endif

138
lib/libfs/queue.c Normal file
View File

@@ -0,0 +1,138 @@
#include "queue.h"
size_t queue_length(struct queue *q)
{
size_t i = 0;
struct queue_entry *x = q->q_first;
while (x) {
i++;
x = x->qe_next;
}
return i;
}
void queue_insert_before(
struct queue *q,
struct queue_entry *entry,
struct queue_entry *before)
{
struct queue_entry *x = before->qe_prev;
if (x) {
x->qe_next = entry;
} else {
q->q_first = entry;
}
entry->qe_prev = x;
before->qe_prev = entry;
entry->qe_next = before;
}
void queue_insert_after(
struct queue *q,
struct queue_entry *entry,
struct queue_entry *after)
{
struct queue_entry *x = after->qe_next;
if (x) {
x->qe_prev = entry;
} else {
q->q_last = entry;
}
entry->qe_prev = x;
after->qe_next = entry;
entry->qe_prev = after;
}
void queue_push_front(struct queue *q, struct queue_entry *entry)
{
if (q->q_first) {
q->q_first->qe_prev = entry;
}
entry->qe_next = q->q_first;
entry->qe_prev = NULL;
q->q_first = entry;
if (!q->q_last) {
q->q_last = entry;
}
}
void queue_push_back(struct queue *q, struct queue_entry *entry)
{
if (q->q_last) {
q->q_last->qe_next = entry;
}
entry->qe_prev = q->q_last;
entry->qe_next = NULL;
q->q_last = entry;
if (!q->q_first) {
q->q_first = entry;
}
}
struct queue_entry *queue_pop_front(struct queue *q)
{
struct queue_entry *x = q->q_first;
if (x) {
queue_delete(q, x);
}
return x;
}
struct queue_entry *queue_pop_back(struct queue *q)
{
struct queue_entry *x = q->q_last;
if (x) {
queue_delete(q, x);
}
return x;
}
void queue_delete(struct queue *q, struct queue_entry *entry)
{
if (!entry) {
return;
}
if (entry == q->q_first) {
q->q_first = q->q_first->qe_next;
}
if (entry == q->q_last) {
q->q_last = q->q_last->qe_prev;
}
if (entry->qe_next) {
entry->qe_next->qe_prev = entry->qe_prev;
}
if (entry->qe_prev) {
entry->qe_prev->qe_next = entry->qe_next;
}
entry->qe_next = entry->qe_prev = NULL;
}
void queue_delete_all(struct queue *q)
{
struct queue_entry *x = q->q_first;
while (x) {
struct queue_entry *next = x->qe_next;
x->qe_next = x->qe_prev = NULL;
x = next;
}
q->q_first = q->q_last = NULL;
}

100
lib/libfs/queue.h Normal file
View File

@@ -0,0 +1,100 @@
#ifndef QUEUE_H_
#define QUEUE_H_
#include <stdbool.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#define QUEUE_CONTAINER(t, m, v) \
((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0))
#define QUEUE_INIT ((struct queue) {.q_first = NULL, .q_last = NULL})
#define QUEUE_ENTRY_INIT \
((struct queue_entry) {.qe_next = NULL, .qe_prev = NULL})
#define queue_foreach(iter_type, iter_name, queue_name, node_member) \
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \
iter_type, \
node_member, \
queue_first(queue_name)); \
iter_name; \
iter_name = (iter_type *)QUEUE_CONTAINER( \
iter_type, \
node_member, \
queue_next(&((iter_name)->node_member))))
#define queue_foreach_r(iter_type, iter_name, queue_name, node_member) \
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \
iter_type, \
node_member, \
queue_last(queue_name)); \
iter_name; \
iter_name = (iter_type *)QUEUE_CONTAINER( \
iter_type, \
node_member, \
queue_prev(&((iter_name)->node_member))))
struct queue_entry {
struct queue_entry *qe_next;
struct queue_entry *qe_prev;
};
struct queue {
struct queue_entry *q_first;
struct queue_entry *q_last;
};
static inline void queue_init(struct queue *q)
{
memset(q, 0x00, sizeof *q);
}
static inline bool queue_empty(struct queue *q)
{
return q->q_first == NULL;
}
static inline struct queue_entry *queue_first(struct queue *q)
{
return q->q_first;
}
static inline struct queue_entry *queue_last(struct queue *q)
{
return q->q_last;
}
static inline struct queue_entry *queue_next(struct queue_entry *entry)
{
return entry->qe_next;
}
static inline struct queue_entry *queue_prev(struct queue_entry *entry)
{
return entry->qe_prev;
}
extern size_t queue_length(struct queue *q);
extern void queue_insert_before(
struct queue *q,
struct queue_entry *entry,
struct queue_entry *before);
extern void queue_insert_after(
struct queue *q,
struct queue_entry *entry,
struct queue_entry *after);
extern void queue_push_front(struct queue *q, struct queue_entry *entry);
extern void queue_push_back(struct queue *q, struct queue_entry *entry);
extern struct queue_entry *queue_pop_front(struct queue *q);
extern struct queue_entry *queue_pop_back(struct queue *q);
extern void queue_delete(struct queue *q, struct queue_entry *entry);
extern void queue_delete_all(struct queue *q);
#ifdef __cplusplus
}
#endif
#endif

25
lib/libfs/status.c Normal file
View File

@@ -0,0 +1,25 @@
#include <errno.h>
#include <fs/status.h>
int fs_status_to_errno(enum fs_status status)
{
switch (status) {
case FS_SUCCESS:
return SUCCESS;
case FS_ERR_NO_ENTRY:
return ENOENT;
case FS_ERR_NO_MEMORY:
return ENOMEM;
case FS_ERR_INVALID_ARGUMENT:
return EINVAL;
case FS_ERR_NOT_IMPLEMENTED:
return ENOSYS;
case FS_ERR_IS_DIRECTORY:
return EISDIR;
case FS_ERR_NOT_DIRECTORY:
return ENOTDIR;
default:
return EINVAL;
}
}

View File

@@ -160,15 +160,10 @@ static kern_status_t create_exec_regions(struct elf_image *image)
image->e_total_size);
kern_status_t status = KERN_OK;
if (image->e_local_space != KERN_HANDLE_INVALID) {
status = vm_region_create(
status = address_space_reserve(
image->e_local_space,
NULL,
0,
VM_REGION_ANY_OFFSET,
MAP_ADDRESS_ANY,
image->e_total_size,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXEC
| VM_PROT_USER,
&image->e_local_exec,
&image->e_local_base);
}
@@ -177,22 +172,18 @@ static kern_status_t create_exec_regions(struct elf_image *image)
}
if (image->e_remote_space != KERN_HANDLE_INVALID) {
status = vm_region_create(
status = address_space_reserve(
image->e_remote_space,
NULL,
0,
VM_REGION_ANY_OFFSET,
MAP_ADDRESS_ANY,
image->e_total_size,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXEC
| VM_PROT_USER,
&image->e_remote_exec,
&image->e_remote_base);
}
if (status != KERN_OK) {
vm_region_kill(image->e_local_exec);
kern_handle_close(image->e_local_exec);
image->e_local_exec = KERN_HANDLE_INVALID;
address_space_release(
image->e_local_space,
image->e_local_base,
image->e_total_size);
return status;
}
@@ -200,6 +191,19 @@ static kern_status_t create_exec_regions(struct elf_image *image)
return KERN_OK;
}
static kern_status_t release_exec_regions(struct elf_image *image)
{
address_space_release(
image->e_local_space,
image->e_local_base,
image->e_total_size);
address_space_release(
image->e_remote_space,
image->e_remote_base,
image->e_total_size);
return KERN_OK;
}
static enum launch_status map_executable(struct elf_image *image)
{
elf_phdr_t phdr;
@@ -256,10 +260,10 @@ static enum launch_status map_executable(struct elf_image *image)
return LAUNCH_ERR_IMAGE_DATA_LOAD_FAILED;
}
if (image->e_local_exec != KERN_HANDLE_INVALID) {
status = vm_region_map_relative(
image->e_local_exec,
phdr.p_vaddr,
if (image->e_local_space != KERN_HANDLE_INVALID) {
status = address_space_map(
image->e_local_space,
image->e_local_base + phdr.p_vaddr,
vmo,
offset,
phdr.p_memsz,
@@ -271,10 +275,10 @@ static enum launch_status map_executable(struct elf_image *image)
return LAUNCH_ERR_MEMORY_MAP_FAILED;
}
if (image->e_remote_exec != KERN_HANDLE_INVALID) {
status = vm_region_map_relative(
image->e_remote_exec,
phdr.p_vaddr,
if (image->e_remote_space != KERN_HANDLE_INVALID) {
status = address_space_map(
image->e_remote_space,
image->e_remote_base + phdr.p_vaddr,
vmo,
offset,
phdr.p_memsz,
@@ -330,6 +334,28 @@ static enum launch_status do_rela(struct elf_image *image, elf_rela_t *rela)
ELF64_R_SYM(rela->r_info),
rela->r_addend);
break;
case R_X86_64_GLOB_DAT:
sym = get_dynsym(image, ELF64_R_SYM(rela->r_info));
if (!sym) {
return LAUNCH_ERR_MISSING_SYMBOL;
}
*(uint64_t *)(image->e_local_base + rela->r_offset)
= image->e_remote_base + sym->st_value + rela->r_addend;
kern_tracef(
"GLOB_DAT: offset=%zx, symbol=%zu, addend=%zx",
rela->r_offset,
ELF64_R_SYM(rela->r_info),
rela->r_addend);
break;
case R_X86_64_RELATIVE:
*(uint64_t *)(image->e_local_base + rela->r_offset)
= image->e_remote_base + rela->r_addend;
kern_tracef(
"RELATIVE: offset=%zx, addend=%zx",
rela->r_offset,
rela->r_addend);
break;
default:
kern_trace("Unknown relocation type");
return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE;
@@ -499,8 +525,6 @@ void elf_image_init(struct elf_image *out)
out->e_data = KERN_HANDLE_INVALID;
out->e_local_space = KERN_HANDLE_INVALID;
out->e_remote_space = KERN_HANDLE_INVALID;
out->e_local_exec = KERN_HANDLE_INVALID;
out->e_remote_exec = KERN_HANDLE_INVALID;
}
enum launch_status elf_image_load(
@@ -547,6 +571,11 @@ enum launch_status elf_image_load(
return status;
}
status = release_exec_regions(image);
if (status != KERN_OK) {
return status;
}
status = relocate(image);
if (status != LAUNCH_OK) {
return status;
@@ -557,9 +586,9 @@ enum launch_status elf_image_load(
void elf_image_cleanup(struct elf_image *image)
{
vm_region_unmap_relative(image->e_local_exec, 0, image->e_total_size);
address_space_unmap(
image->e_local_space,
image->e_local_base,
image->e_total_size);
kern_handle_close(image->e_data);
vm_region_kill(image->e_local_exec);
kern_handle_close(image->e_local_exec);
kern_handle_close(image->e_remote_exec);
}

View File

@@ -292,7 +292,6 @@ struct elf_image {
size_t e_page_size;
kern_handle_t e_image, e_data;
kern_handle_t e_local_space, e_remote_space;
kern_handle_t e_local_exec, e_remote_exec;
virt_addr_t e_local_base, e_remote_base;
elf_ehdr_t e_hdr;
elf_phdr_t e_dynamic;

View File

@@ -45,7 +45,9 @@ struct launch_ctx {
struct launch_parameters {
kern_handle_t p_parent_task;
kern_handle_t p_local_address_space;
kern_handle_t p_executable;
const char *p_exec_path;
kern_handle_t p_exec_image;
const char *p_task_name;

View File

@@ -38,6 +38,7 @@ static kern_handle_t get_library(
static virt_addr_t write_bootstrap_data(
struct stack_writer *stack,
const char *interpreter,
const struct launch_parameters *params)
{
virt_addr_t bs_remote;
@@ -51,9 +52,15 @@ static virt_addr_t write_bootstrap_data(
bs->bs_handles_count = params->p_handle_count;
bs->bs_channels_count = params->p_channel_count;
if (interpreter) {
/* two extra args: interpreter path and path to executable */
bs->bs_argc += 2;
}
const char **argv, **envp;
if (bs->bs_argc > 0) {
int argc = bs->bs_argc;
virt_addr_t remote_argv;
argv = stack_writer_put(
stack,
@@ -73,13 +80,22 @@ static virt_addr_t write_bootstrap_data(
bs->bs_envp = (const char **)remote_envp;
}
for (size_t i = 0; i < params->p_argc; i++) {
size_t i = 0, j = 0;
if (interpreter) {
virt_addr_t arg_ptr;
stack_writer_put_string(stack, params->p_argv[i], &arg_ptr);
stack_writer_put_string(stack, interpreter, &arg_ptr);
argv[i++] = (const char *)arg_ptr;
stack_writer_put_string(stack, params->p_exec_path, &arg_ptr);
argv[i++] = (const char *)arg_ptr;
}
for (; i < bs->bs_argc; i++, j++) {
virt_addr_t arg_ptr;
stack_writer_put_string(stack, params->p_argv[j], &arg_ptr);
argv[i] = (const char *)arg_ptr;
}
for (size_t i = 0; i < params->p_envc; i++) {
for (size_t i = 0; i < bs->bs_envc; i++) {
virt_addr_t env_ptr;
stack_writer_put_string(stack, params->p_envp[i], &env_ptr);
envp[i] = (const char *)env_ptr;
@@ -118,16 +134,20 @@ enum launch_status launch_ctx_execute(
return LAUNCH_ERR_TASK_CREATION_FAILED;
}
char interp_path[4096];
interp_path[0] = 0;
struct elf_image image;
elf_image_init(&image);
enum launch_status status = elf_image_load(
&image,
params->p_executable,
params->p_exec_image,
params->p_local_address_space,
remote_address_space);
if (status == LAUNCH_ERR_INTERPRETER_REQUIRED) {
snprintf(interp_path, sizeof interp_path, "%s", image.e_interp);
kern_handle_t interp = get_library(
ctx,
image.e_interp,
@@ -157,17 +177,17 @@ enum launch_status launch_ctx_execute(
}
virt_addr_t remote_stack_buf, local_stack_buf;
kstatus = vm_region_map_relative(
kstatus = address_space_map(
remote_address_space,
VM_REGION_ANY_OFFSET,
MAP_ADDRESS_ANY,
stack_vmo,
0,
STACK_SIZE,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
&remote_stack_buf);
kstatus = vm_region_map_relative(
kstatus = address_space_map(
params->p_local_address_space,
VM_REGION_ANY_OFFSET,
MAP_ADDRESS_ANY,
stack_vmo,
0,
STACK_SIZE,
@@ -187,7 +207,7 @@ enum launch_status launch_ctx_execute(
&stack,
local_stack_buf + STACK_SIZE,
remote_stack_buf + STACK_SIZE);
virt_addr_t bsdata = write_bootstrap_data(&stack, params);
virt_addr_t bsdata = write_bootstrap_data(&stack, interp_path, params);
virt_addr_t ip = image.e_hdr.e_entry + image.e_remote_base;
@@ -208,7 +228,10 @@ enum launch_status launch_ctx_execute(
thread_start(thread);
kern_handle_close(thread);
result->r_task = remote_task;
result->r_thread = thread;
result->r_address_space = remote_address_space;
elf_image_cleanup(&image);
return LAUNCH_OK;

28
lib/libxpc/CMakeLists.txt Normal file
View File

@@ -0,0 +1,28 @@
file(GLOB sources
${CMAKE_CURRENT_SOURCE_DIR}/*.c)
file(GLOB headers
${CMAKE_CURRENT_SOURCE_DIR}/*.h
${CMAKE_CURRENT_SOURCE_DIR}/include/xpc/*.h)
set(public_include_dirs
${CMAKE_CURRENT_SOURCE_DIR}/include)
rosetta_add_library(
NAME libxpc SHARED STATIC
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
SOURCES ${sources}
HEADERS ${headers})
sysroot_add_library(
NAME libxpc
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
sysroot_add_library(
NAME libxpc-static
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libxpc libmango libc)
target_link_libraries(libxpc-static libmango libc-core)
#set_target_properties(libxpc-static PROPERTIES POSITION_INDEPENDENT_CODE FALSE)

61
lib/libxpc/buffer.c Normal file
View File

@@ -0,0 +1,61 @@
#include <mango/status.h>
#include <string.h>
#include <xpc/buffer.h>
#include <xpc/msg.h>
kern_status_t xpc_buffer_read(
const xpc_buffer_t *buf,
void *out,
size_t max,
size_t *nr_read)
{
if ((buf->buf_flags & (XPC_BUFFER_F_IN | XPC_BUFFER_F_REMOTE))
!= (XPC_BUFFER_F_IN | XPC_BUFFER_F_REMOTE)) {
return KERN_BAD_STATE;
}
size_t to_read = max;
if (to_read > buf->buf_len) {
to_read = buf->buf_len;
}
kern_status_t status
= xpc_msg_read(buf->buf_origin, buf->buf_offset, out, to_read);
if (status != KERN_OK) {
return status;
}
/* TODO */
*nr_read = to_read;
return KERN_OK;
}
kern_status_t xpc_buffer_write(
xpc_buffer_t *buf,
const void *in,
size_t len,
size_t *nr_written)
{
if (!(buf->buf_flags & XPC_BUFFER_F_OUT)) {
return KERN_BAD_STATE;
}
size_t to_write = len;
if (to_write > buf->buf_max) {
to_write = buf->buf_max;
}
if (!(buf->buf_flags & XPC_BUFFER_F_REMOTE)) {
memcpy(buf->buf_ptr, in, to_write);
*nr_written = to_write;
return KERN_OK;
}
return xpc_msg_write(
buf->buf_origin,
buf->buf_offset,
in,
to_write,
nr_written);
}

View File

@@ -0,0 +1,83 @@
#ifndef XPC_BUFFER_H_
#define XPC_BUFFER_H_
#include <stddef.h>
#include <xpc/status.h>
#define XPC_BUFFER_IN(msg, offset, size) \
{ \
.buf_flags = XPC_BUFFER_F_IN | XPC_BUFFER_F_REMOTE, \
.buf_origin = (msg), \
.buf_offset = (offset), \
.buf_len = (size), \
}
#define XPC_BUFFER_OUT(msg, offset, size) \
{ \
.buf_flags = XPC_BUFFER_F_OUT | XPC_BUFFER_F_REMOTE, \
.buf_origin = (msg), \
.buf_offset = (offset), \
.buf_max = (size), \
}
#define XPC_LOCAL_BUFFER_OUT(ptr, size) \
{ \
.buf_flags = XPC_BUFFER_F_OUT, \
.buf_ptr = (ptr), \
.buf_max = (size), \
}
struct xpc_msg;
typedef enum xpc_buffer_flags {
/* the buffer can be read from */
XPC_BUFFER_F_IN = 0x01u,
/* the buffer can be written to */
XPC_BUFFER_F_OUT = 0x02u,
/* the buffer is backed by a buffer located in another address space.
* the buffer can only be accessed via xpc_buffer_read and/or
* xpc_buffer_write */
XPC_BUFFER_F_REMOTE = 0x04u,
/* free the buffer backing this buffer when the buffer is discarded.
* this is only used for out-buffers. the buffer must have been
* allocated using xpc_context_alloc, as it will be freed via a call
* to xpc_context_free */
XPC_BUFFER_F_FREE_ON_DISCARD = 0x08u,
} xpc_buffer_flags_t;
typedef struct xpc_buffer {
xpc_buffer_flags_t buf_flags;
/* only valid if F_OUT is set. specifies the maximum
* number of chars that can be written to buf_buf,
* including the null terminator. */
size_t buf_max;
/* only valid if F_OUT is set.
* if F_FREE_ON_DISCARD is set, must be either NULL or
* allocated via xpc_context_alloc */
void *buf_ptr;
/* valid for F_IN and F_OUT. offset of the buffer data
* within the associated message. used when reading
* buffer data from a message. */
size_t buf_offset;
/* only valid if F_REMOTE is set.
* used to read/write buffer data from/to the sender's address
* space. */
const struct xpc_msg *buf_origin;
/* valid for both F_IN and F_OUT buffers.
* F_IN: specifies the length of the incoming buffer data.
* F_OUT: specifies how many bytes from buf_ptr to send. */
size_t buf_len;
} xpc_buffer_t;
extern xpc_status_t xpc_buffer_read(
const xpc_buffer_t *s,
void *out,
size_t max,
size_t *nr_read);
extern xpc_status_t xpc_buffer_write(
xpc_buffer_t *s,
const void *in,
size_t len,
size_t *nr_written);
#endif

View File

@@ -0,0 +1,8 @@
#ifndef XPC_CONTEXT_H_
#define XPC_CONTEXT_H_
typedef struct xpc_context {
} xpc_context_t;
#endif

View File

@@ -0,0 +1,13 @@
#ifndef XPC_ENDPOINT_H_
#define XPC_ENDPOINT_H_
#include <mango/types.h>
typedef struct xpc_endpoint {
kern_handle_t e_channel;
tid_t e_task;
koid_t e_port;
msgid_t e_msg;
} xpc_endpoint_t;
#endif

View File

@@ -0,0 +1,55 @@
#ifndef XPC_MSG_H_
#define XPC_MSG_H_
#include <mango/types.h>
#include <stdbool.h>
#include <stdint.h>
#include <xpc/endpoint.h>
#define XPC_MSG_MAGIC 0x5850434D
typedef struct xpc_msg_header {
uint32_t hdr_magic;
uint32_t hdr_interface;
uint16_t hdr_func;
uint16_t hdr_status;
} xpc_msg_header_t;
typedef struct xpc_msg {
xpc_endpoint_t msg_sender;
xpc_msg_header_t msg_header;
size_t msg_handles_count;
kern_msg_handle_t msg_handles[KERN_MSG_MAX_HANDLES];
} xpc_msg_t;
extern void xpc_msg_header_init(
xpc_msg_header_t *msg,
unsigned long interface,
unsigned short func);
extern bool xpc_msg_header_validate(const xpc_msg_header_t *msg);
extern kern_status_t xpc_msg_recv(kern_handle_t channel, xpc_msg_t *out);
extern kern_status_t xpc_msg_recv_nowait(kern_handle_t channel, xpc_msg_t *out);
extern kern_status_t xpc_msg_read(
const xpc_msg_t *msg,
size_t offset,
void *p,
size_t count);
extern kern_status_t xpc_msg_write(
const xpc_msg_t *msg,
size_t offset,
const void *p,
size_t count,
size_t *nr_written);
extern kern_status_t xpc_msg_reply(
const xpc_msg_t *msg,
kern_iovec_t *iov,
size_t iov_count,
kern_msg_handle_t *handles,
size_t handle_count);
extern kern_status_t xpc_msg_reply_error(
const xpc_msg_t *msg,
unsigned short code);
#endif

View File

@@ -0,0 +1,12 @@
#ifndef XPC_STATUS_H_
#define XPC_STATUS_H_
typedef enum xpc_status {
XPC_SUCCESS = 0,
XPC_ERR_BAD_STATE,
XPC_ERR_INVALID_ARGUMENT,
XPC_ERR_NO_MEMORY,
XPC_ERR_MEMORY_FAULT,
} xpc_status_t;
#endif

View File

@@ -0,0 +1,79 @@
#ifndef XPC_STRING_H_
#define XPC_STRING_H_
#include <stddef.h>
#include <xpc/status.h>
#define XPC_STRING_NPOS ((size_t)-1)
#define XPC_STRING_IN(msg, offset, size) \
{ \
.s_flags = XPC_STRING_F_IN | XPC_STRING_F_REMOTE, \
.s_origin = (msg), \
.s_offset = (offset), \
.s_len = (size), \
}
#define XPC_STRING_OUT(msg, offset, size) \
{ \
.s_flags = XPC_STRING_F_OUT | XPC_STRING_F_REMOTE, \
.s_origin = (msg), \
.s_offset = (offset), \
.s_max = (size), \
}
struct xpc_msg;
typedef enum xpc_string_flags {
/* the string can be read from */
XPC_STRING_F_IN = 0x01u,
/* the string can be written to */
XPC_STRING_F_OUT = 0x02u,
/* the string is backed by a buffer located in another address space.
* the string can only be accessed via xpc_string_read and/or
* xpc_string_write */
XPC_STRING_F_REMOTE = 0x04u,
/* free the buffer backing this string when the string is discarded.
* this is only used for out-strings. the buffer must have been
* allocated using xpc_context_alloc, as it will be freed via a call
* to xpc_context_free */
XPC_STRING_F_FREE_ON_DISCARD = 0x08u,
} xpc_string_flags_t;
typedef struct xpc_string {
xpc_string_flags_t s_flags;
/* only valid if F_OUT is set. specifies the maximum
* number of chars that can be written to s_buf,
* including the null terminator. */
size_t s_max;
/* only valid if F_OUT is set.
* if F_FREE_ON_DISCARD is set, must be either NULL or
* allocated via xpc_context_alloc */
const char *s_buf;
/* valid for F_IN and F_OUT. offset of the string data
* within the associated message. used when reading
* string data from a message. */
size_t s_offset;
/* only valid if F_REMOTE is set.
* used to read/write string data from/to the sender's address space. */
const struct xpc_msg *s_origin;
/* valid for both F_IN and F_OUT strings.
* F_IN: specifies the length of the incoming string data.
* F_OUT: specifies how many characters from s_buf to send. */
size_t s_len;
} xpc_string_t;
extern xpc_status_t xpc_string_read(
const xpc_string_t *s,
char *out,
size_t max,
size_t *nr_read);
extern xpc_status_t xpc_string_write(
xpc_string_t *s,
const char *in,
size_t len,
size_t *nr_written);
#endif

151
lib/libxpc/msg.c Normal file
View File

@@ -0,0 +1,151 @@
#include <mango/msg.h>
#include <mango/object.h>
#include <mango/signal.h>
#include <string.h>
#include <xpc/msg.h>
void xpc_msg_header_init(
xpc_msg_header_t *msg,
unsigned long interface,
unsigned short func)
{
memset(msg, 0x0, sizeof *msg);
msg->hdr_magic = XPC_MSG_MAGIC;
msg->hdr_interface = interface;
msg->hdr_func = func;
msg->hdr_status = 0;
}
bool xpc_msg_header_validate(const xpc_msg_header_t *msg)
{
return msg->hdr_magic == XPC_MSG_MAGIC;
}
static kern_status_t __msg_recv(
kern_handle_t channel,
xpc_msg_t *out,
bool nowait)
{
kern_status_t status = KERN_OK;
if (!nowait) {
kern_wait_item_t wait = {
.w_handle = channel,
.w_waitfor = CHANNEL_SIGNAL_MSG_RECEIVED,
};
status = kern_object_wait(&wait, 1);
if (status != KERN_OK) {
return status;
}
}
kern_iovec_t iov = IOVEC(&out->msg_header, sizeof out->msg_header);
kern_msg_t msg = {
.msg_data = &iov,
.msg_data_count = 1,
.msg_handles = out->msg_handles,
.msg_handles_count = KERN_MSG_MAX_HANDLES,
};
status = msg_recv(channel, &msg);
if (status != KERN_OK) {
return status;
}
if (!xpc_msg_header_validate(&out->msg_header)) {
return KERN_INVALID_ARGUMENT;
}
out->msg_sender.e_channel = channel;
out->msg_sender.e_task = msg.msg_sender;
out->msg_sender.e_port = msg.msg_endpoint;
out->msg_sender.e_msg = msg.msg_id;
return KERN_OK;
}
kern_status_t xpc_msg_recv(kern_handle_t channel, xpc_msg_t *out)
{
kern_status_t status = KERN_OK;
while (1) {
status = __msg_recv(channel, out, false);
if (status != KERN_NO_ENTRY) {
break;
}
}
return status;
}
kern_status_t xpc_msg_recv_nowait(kern_handle_t channel, xpc_msg_t *out)
{
return __msg_recv(channel, out, true);
}
kern_status_t xpc_msg_read(
const xpc_msg_t *msg,
size_t offset,
void *p,
size_t count)
{
kern_iovec_t iov = IOVEC(p, count);
size_t r = 0;
return msg_read(
msg->msg_sender.e_channel,
msg->msg_sender.e_msg,
offset,
&iov,
1,
&r);
}
kern_status_t xpc_msg_write(
const xpc_msg_t *msg,
size_t offset,
const void *p,
size_t count,
size_t *nr_written)
{
kern_iovec_t iov = IOVEC(p, count);
return msg_write(
msg->msg_sender.e_channel,
msg->msg_sender.e_msg,
offset,
&iov,
1,
nr_written);
}
kern_status_t xpc_msg_reply(
const xpc_msg_t *msg,
kern_iovec_t *iov,
size_t iov_count,
kern_msg_handle_t *handles,
size_t handle_count)
{
kern_msg_t reply = MSG(iov, iov_count, handles, handle_count);
return msg_reply(
msg->msg_sender.e_channel,
msg->msg_sender.e_msg,
&reply);
}
kern_status_t xpc_msg_reply_error(const xpc_msg_t *msg, unsigned short code)
{
xpc_msg_header_t reply_data = {
.hdr_magic = XPC_MSG_MAGIC,
.hdr_interface = msg->msg_header.hdr_interface,
.hdr_func = msg->msg_header.hdr_func,
.hdr_status = code,
};
kern_iovec_t iov = IOVEC(&reply_data, sizeof reply_data);
kern_msg_t reply = MSG(&iov, 1, NULL, 0);
return msg_reply(
msg->msg_sender.e_channel,
msg->msg_sender.e_msg,
&reply);
}

56
lib/libxpc/string.c Normal file
View File

@@ -0,0 +1,56 @@
#include <mango/status.h>
#include <xpc/msg.h>
#include <xpc/string.h>
xpc_status_t xpc_string_read(
const xpc_string_t *s,
char *out,
size_t max,
size_t *nr_read)
{
if ((s->s_flags & (XPC_STRING_F_IN | XPC_STRING_F_REMOTE))
!= (XPC_STRING_F_IN | XPC_STRING_F_REMOTE)) {
return KERN_BAD_STATE;
}
size_t to_read = max - 1;
if (to_read > s->s_len) {
to_read = s->s_len;
}
kern_status_t status
= xpc_msg_read(s->s_origin, s->s_offset, out, to_read);
if (status != KERN_OK) {
return status;
}
out[to_read] = '\0';
/* TODO */
*nr_read = to_read;
return KERN_OK;
}
xpc_status_t xpc_string_write(
xpc_string_t *s,
const char *in,
size_t len,
size_t *nr_written)
{
if ((s->s_flags & (XPC_STRING_F_OUT | XPC_STRING_F_REMOTE))
!= (XPC_STRING_F_IN | XPC_STRING_F_REMOTE)) {
return KERN_BAD_STATE;
}
size_t to_write = len;
if (to_write > s->s_max - 1) {
to_write = s->s_max - 1;
}
return xpc_msg_write(
s->s_origin,
s->s_offset,
in,
to_write,
nr_written);
}

View File

@@ -1,4 +1,14 @@
int main(void)
#include <mango/log.h>
#include <stdio.h>
int main(int argc, const char *argv[])
{
kern_logf("systemd");
kern_logf("args:");
for (int i = 0; i < argc; i++) {
kern_logf("[%d]: %s", i, argv[i]);
}
return 0;
}

View File

@@ -1,3 +1,5 @@
#include <mango/log.h>
#include <stdio.h>
#include <string.h>
const char *s
@@ -152,6 +154,14 @@ const char *s
int main(void)
{
const char *s = "Hello, world!";
return strlen(s);
kern_logf("Test 1");
kern_logf("Test 2");
kern_logf("Test 3");
const char *text = "Hello, world!";
char s2[32];
snprintf(s2, sizeof s2, "%s", text);
size_t v = strlen(s);
kern_logf("%s, %zu", s2, v);
return 0;
}

View File

@@ -6,11 +6,12 @@ set_property(SOURCE ${arch_sources} PROPERTY LANGUAGE C)
add_executable(bootstrap ${c_sources} ${arch_sources})
target_link_libraries(bootstrap
libmango libc-core libc-malloc libfs-static liblaunch
libmango libc-core libc-malloc libc-pthread libfs-static liblaunch libxpc-static
interface::fs)
target_compile_options(bootstrap PRIVATE
-fno-stack-protector -nostdlib -ffreestanding)
-fno-stack-protector -nostdlib -ffreestanding -fno-PIC)
target_link_options(bootstrap PRIVATE
-static -nostdlib -ffreestanding)
#-T ${CMAKE_CURRENT_SOURCE_DIR}/arch/${TARGET_ARCH}/layout.ld)
-static -nostdlib -ffreestanding)
set_target_properties(bootstrap PROPERTIES POSITIION_INDEPENDENT_CODE FALSE)

View File

@@ -7,16 +7,18 @@
#include <fs/context.h>
#include <heap/heap.h>
#include <launch.h>
#include <mango/handle.h>
#include <mango/log.h>
#include <mango/msg.h>
#include <mango/task.h>
#include <mango/types.h>
#include <pthread.h>
#include <rosetta/bootstrap.h>
#include <rosetta/fs.h>
#include <stdint.h>
#include <stdio.h>
#define INIT_PATH "/usr/bin/test"
#define INIT_PATH "/usr/bin/systemd"
static enum launch_status resolve_dependency(
struct launch_ctx *ctx,
@@ -29,14 +31,12 @@ static enum launch_status resolve_dependency(
name++;
}
snprintf(s, sizeof s, "searching for library %s", name);
kern_log(s);
kern_tracef("searching for library %s", name);
struct tar *fs = arg;
struct tar_file file = {0};
if (tar_open(fs, name, &file) != 0) {
snprintf(s, sizeof s, "cannot find library %s", name);
kern_log(s);
kern_tracef("cannot find library %s", name);
return LAUNCH_ERR_CANNOT_RESOLVE_DEPENDENCY;
}
@@ -53,7 +53,8 @@ static enum launch_status resolve_dependency(
}
static kern_status_t open(
const struct msg_endpoint *sender,
xpc_context_t *ctx,
const xpc_endpoint_t *sender,
const char *path,
int flags,
int *out_err,
@@ -141,7 +142,8 @@ int main(
};
struct launch_parameters params = {
.p_executable = image,
.p_exec_image = image,
.p_exec_path = INIT_PATH,
.p_parent_task = task,
.p_task_name = "init",
.p_local_address_space = address_space,
@@ -168,6 +170,10 @@ int main(
return -1;
}
kern_handle_close(result.r_task);
kern_handle_close(result.r_thread);
kern_handle_close(result.r_address_space);
heap_t heap = HEAP_INIT;
struct fs_allocator fs_allocator = {
@@ -178,51 +184,50 @@ int main(
.fs_arg = &heap,
};
struct fs_context *fs = fs_context_create(&fs_allocator, NULL);
struct fs_context *fs = fs_context_create(&fs_allocator);
if (!fs) {
kern_logf("cannot initialise fs");
return -1;
}
fs_context_set_channel(fs, channel);
enum fs_status fs_status = fs_context_mount_filesystem(
fs,
tar_mount,
(void *)bsp_base,
0);
if (fs_status != FS_SUCCESS) {
kern_logf("cannot mount filesystem (%d)", fs_status);
return -1;
}
while (1) {
struct msg_endpoint sender;
struct msg_header hdr;
kern_msg_handle_t handles[KERN_MSG_MAX_HANDLES] = {0};
kern_status_t status = msg_recv_generic(
channel,
&sender,
&hdr,
handles,
KERN_MSG_MAX_HANDLES);
fs_context_handle_request(fs);
#if 0
xpc_msg_t msg;
kern_status_t status = xpc_msg_recv(channel, &msg);
if (status != KERN_OK) {
kern_logf("message recv error %d", status);
msg_reply_generic(
channel,
&sender,
&hdr,
KERN_UNSUPPORTED);
continue;
}
switch (hdr.hdr_protocol) {
case PROTOCOL_FS:
status = fs_context_dispatch_msg(
fs,
channel,
&sender,
&hdr);
switch (msg.msg_header.hdr_interface) {
case INTERFACE_FS:
status = fs_context_dispatch_msg(fs, &msg);
break;
default:
kern_logf(
"unknown message protocol %u",
hdr.hdr_protocol);
msg_reply_generic(
channel,
&sender,
&hdr,
KERN_UNSUPPORTED);
msg.msg_header.hdr_interface);
xpc_msg_reply_error(&msg, KERN_UNSUPPORTED);
break;
}
if (status != KERN_OK) {
kern_logf("message reply error %d", status);
continue;
}
#endif
}
return 0;

138
sys/bootstrap/queue.c Normal file
View File

@@ -0,0 +1,138 @@
#include "queue.h"
size_t queue_length(struct queue *q)
{
size_t i = 0;
struct queue_entry *x = q->q_first;
while (x) {
i++;
x = x->qe_next;
}
return i;
}
void queue_insert_before(
struct queue *q,
struct queue_entry *entry,
struct queue_entry *before)
{
struct queue_entry *x = before->qe_prev;
if (x) {
x->qe_next = entry;
} else {
q->q_first = entry;
}
entry->qe_prev = x;
before->qe_prev = entry;
entry->qe_next = before;
}
void queue_insert_after(
struct queue *q,
struct queue_entry *entry,
struct queue_entry *after)
{
struct queue_entry *x = after->qe_next;
if (x) {
x->qe_prev = entry;
} else {
q->q_last = entry;
}
entry->qe_prev = x;
after->qe_next = entry;
entry->qe_prev = after;
}
void queue_push_front(struct queue *q, struct queue_entry *entry)
{
if (q->q_first) {
q->q_first->qe_prev = entry;
}
entry->qe_next = q->q_first;
entry->qe_prev = NULL;
q->q_first = entry;
if (!q->q_last) {
q->q_last = entry;
}
}
void queue_push_back(struct queue *q, struct queue_entry *entry)
{
if (q->q_last) {
q->q_last->qe_next = entry;
}
entry->qe_prev = q->q_last;
entry->qe_next = NULL;
q->q_last = entry;
if (!q->q_first) {
q->q_first = entry;
}
}
struct queue_entry *queue_pop_front(struct queue *q)
{
struct queue_entry *x = q->q_first;
if (x) {
queue_delete(q, x);
}
return x;
}
struct queue_entry *queue_pop_back(struct queue *q)
{
struct queue_entry *x = q->q_last;
if (x) {
queue_delete(q, x);
}
return x;
}
void queue_delete(struct queue *q, struct queue_entry *entry)
{
if (!entry) {
return;
}
if (entry == q->q_first) {
q->q_first = q->q_first->qe_next;
}
if (entry == q->q_last) {
q->q_last = q->q_last->qe_prev;
}
if (entry->qe_next) {
entry->qe_next->qe_prev = entry->qe_prev;
}
if (entry->qe_prev) {
entry->qe_prev->qe_next = entry->qe_next;
}
entry->qe_next = entry->qe_prev = NULL;
}
void queue_delete_all(struct queue *q)
{
struct queue_entry *x = q->q_first;
while (x) {
struct queue_entry *next = x->qe_next;
x->qe_next = x->qe_prev = NULL;
x = next;
}
q->q_first = q->q_last = NULL;
}

100
sys/bootstrap/queue.h Normal file
View File

@@ -0,0 +1,100 @@
#ifndef QUEUE_H_
#define QUEUE_H_
#include <stdbool.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#define QUEUE_CONTAINER(t, m, v) \
((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0))
#define QUEUE_INIT ((struct queue) {.q_first = NULL, .q_last = NULL})
#define QUEUE_ENTRY_INIT \
((struct queue_entry) {.qe_next = NULL, .qe_prev = NULL})
#define queue_foreach(iter_type, iter_name, queue_name, node_member) \
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \
iter_type, \
node_member, \
queue_first(queue_name)); \
iter_name; \
iter_name = (iter_type *)QUEUE_CONTAINER( \
iter_type, \
node_member, \
queue_next(&((iter_name)->node_member))))
#define queue_foreach_r(iter_type, iter_name, queue_name, node_member) \
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \
iter_type, \
node_member, \
queue_last(queue_name)); \
iter_name; \
iter_name = (iter_type *)QUEUE_CONTAINER( \
iter_type, \
node_member, \
queue_prev(&((iter_name)->node_member))))
struct queue_entry {
struct queue_entry *qe_next;
struct queue_entry *qe_prev;
};
struct queue {
struct queue_entry *q_first;
struct queue_entry *q_last;
};
static inline void queue_init(struct queue *q)
{
memset(q, 0x00, sizeof *q);
}
static inline bool queue_empty(struct queue *q)
{
return q->q_first == NULL;
}
static inline struct queue_entry *queue_first(struct queue *q)
{
return q->q_first;
}
static inline struct queue_entry *queue_last(struct queue *q)
{
return q->q_last;
}
static inline struct queue_entry *queue_next(struct queue_entry *entry)
{
return entry->qe_next;
}
static inline struct queue_entry *queue_prev(struct queue_entry *entry)
{
return entry->qe_prev;
}
extern size_t queue_length(struct queue *q);
extern void queue_insert_before(
struct queue *q,
struct queue_entry *entry,
struct queue_entry *before);
extern void queue_insert_after(
struct queue *q,
struct queue_entry *entry,
struct queue_entry *after);
extern void queue_push_front(struct queue *q, struct queue_entry *entry);
extern void queue_push_back(struct queue *q, struct queue_entry *entry);
extern struct queue_entry *queue_pop_front(struct queue *q);
extern struct queue_entry *queue_pop_back(struct queue *q);
extern void queue_delete(struct queue *q, struct queue_entry *entry);
extern void queue_delete_all(struct queue *q);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,8 +1,30 @@
#include "tar.h"
#include "queue.h"
#include <fs/dentry.h>
#include <fs/file.h>
#include <fs/inode.h>
#include <fs/superblock.h>
#include <mango/handle.h>
#include <mango/log.h>
#include <mango/vm.h>
#include <stdio.h>
#include <string.h>
#include <xpc/buffer.h>
struct tar_superblock {
struct fs_superblock sb_base;
struct tar_bin_header *sb_data;
};
struct tar_entry {
struct fs_dentry e_dentry;
struct fs_inode e_inode;
void *e_data;
struct queue_entry e_entry;
struct queue e_children;
};
size_t getsize(const char *in)
{
@@ -52,8 +74,10 @@ int tar_open(struct tar *tar, const char *path, struct tar_file *out)
}
s += header.size;
s += ((sizeof *bin_header)
- ((uintptr_t)s % (sizeof *bin_header)));
if ((uintptr_t)s % (sizeof *bin_header)) {
s += ((sizeof *bin_header)
- ((uintptr_t)s % (sizeof *bin_header)));
}
bin_header = (struct tar_bin_header *)s;
}
@@ -94,3 +118,302 @@ int tar_header_decode(const struct tar_bin_header *in, struct tar_header *out)
out->size = getsize(in->size);
return 0;
}
static struct tar_entry *entry_from_dentry(struct fs_dentry *dent)
{
return QUEUE_CONTAINER(struct tar_entry, e_dentry, dent);
}
static struct tar_entry *entry_from_inode(struct fs_inode *inode)
{
return QUEUE_CONTAINER(struct tar_entry, e_inode, inode);
}
static struct tar_entry *entry_get_child(
struct tar_entry *entry,
const char *name)
{
struct queue_entry *cur = queue_first(&entry->e_children);
while (cur) {
struct tar_entry *child
= QUEUE_CONTAINER(struct tar_entry, e_entry, cur);
if (!strcmp(child->e_dentry.d_name, name)) {
return child;
}
cur = queue_next(cur);
}
return NULL;
}
static const struct fs_dentry_ops dentry_ops = {};
static enum fs_status dir_lookup(
struct fs_inode *inode,
const char *name,
struct fs_dentry **out)
{
struct tar_entry *entry = entry_from_inode(inode);
struct tar_entry *child = entry_get_child(entry, name);
if (!child) {
return FS_ERR_NO_ENTRY;
}
*out = &child->e_dentry;
return FS_SUCCESS;
}
static enum fs_status file_read(
struct fs_file *f,
xpc_buffer_t *buf,
size_t count,
off_t *seek)
{
off_t offset = *seek;
struct tar_entry *entry = entry_from_inode(fs_file_get_inode(f));
if (!entry) {
return FS_ERR_BAD_STATE;
}
if (offset >= entry->e_inode.i_size) {
count = 0;
} else if (offset + count > entry->e_inode.i_size) {
count = entry->e_inode.i_size - offset;
}
if (count == 0) {
return FS_SUCCESS;
}
char *src = (char *)entry->e_data + offset;
size_t w;
xpc_buffer_write(buf, src, count, &w);
offset += w;
*seek = offset;
return FS_SUCCESS;
}
static const struct fs_file_ops file_ops = {
.f_read = file_read,
};
static const struct fs_inode_ops file_inode_ops = {
.i_lookup = NULL,
};
static const struct fs_inode_ops dir_inode_ops = {
.i_lookup = dir_lookup,
};
static struct tar_entry *create_dir_entry(
struct fs_context *ctx,
struct tar_superblock *sb,
const char *name)
{
struct tar_entry *entry = fs_context_alloc(ctx, sizeof *entry);
if (!entry) {
return NULL;
}
memset(entry, 0x0, sizeof *entry);
entry->e_inode.i_sb = &sb->sb_base;
entry->e_inode.i_mode = FS_INODE_DIR;
entry->e_inode.i_ops = &dir_inode_ops;
entry->e_dentry.d_sb = &sb->sb_base;
entry->e_dentry.d_ops = &dentry_ops;
entry->e_dentry.d_inode = &entry->e_inode;
size_t name_len = strlen(name);
entry->e_dentry.d_name = fs_context_alloc(ctx, name_len + 1);
if (!entry->e_dentry.d_name) {
fs_context_free(ctx, entry);
return NULL;
}
memcpy(entry->e_dentry.d_name, name, name_len + 1);
return entry;
}
static struct tar_entry *create_file_entry(
struct fs_context *ctx,
struct tar_superblock *sb,
struct tar_header *header,
void *data,
const char *name)
{
struct tar_entry *entry = fs_context_alloc(ctx, sizeof *entry);
if (!entry) {
return NULL;
}
memset(entry, 0x0, sizeof *entry);
entry->e_inode.i_sb = &sb->sb_base;
entry->e_inode.i_mode = FS_INODE_REG;
entry->e_inode.i_ops = &file_inode_ops;
entry->e_inode.i_fops = &file_ops;
entry->e_inode.i_size = header->size;
entry->e_dentry.d_sb = &sb->sb_base;
entry->e_dentry.d_ops = &dentry_ops;
entry->e_dentry.d_inode = &entry->e_inode;
entry->e_data = data;
size_t name_len = strlen(name);
entry->e_dentry.d_name = fs_context_alloc(ctx, name_len + 1);
if (!entry->e_dentry.d_name) {
fs_context_free(ctx, entry);
return NULL;
}
memcpy(entry->e_dentry.d_name, name, name_len + 1);
return entry;
}
static size_t get_first_path_component(const char *in, char *out, size_t max)
{
size_t i = 0;
while (i < max - 1) {
if (in[i] == '\0' || in[i] == '/') {
break;
}
out[i] = in[i];
i++;
}
out[i] = '\0';
return i;
}
static enum fs_status add_entry_to_tree(
struct fs_context *ctx,
struct tar_superblock *sb,
struct tar_header *entry,
void *data)
{
struct tar_entry *cur = entry_from_dentry(sb->sb_base.s_root);
const char *path = entry->filename;
char tok[256];
while (*path != '\0') {
while (*path == '/') {
path++;
}
size_t tok_len
= get_first_path_component(path, tok, sizeof tok);
if (!tok_len) {
break;
}
struct tar_entry *next = entry_get_child(cur, tok);
bool is_file = *(path + tok_len) == '\0';
if (next) {
goto next;
}
if (is_file) {
next = create_file_entry(ctx, sb, entry, data, tok);
} else {
next = create_dir_entry(ctx, sb, tok);
}
if (!next) {
return FS_ERR_NO_MEMORY;
}
queue_push_back(&cur->e_children, &next->e_entry);
next->e_dentry.d_parent = &cur->e_dentry;
next:
cur = next;
path += tok_len;
}
return FS_SUCCESS;
}
static enum fs_status build_dentry_tree(
struct fs_context *ctx,
struct tar_superblock *sb)
{
struct tar_entry *root = create_dir_entry(ctx, sb, "/");
if (!root) {
return FS_ERR_NO_MEMORY;
}
sb->sb_base.s_root = &root->e_dentry;
struct tar_bin_header *bin_header = sb->sb_data;
struct tar_header header;
enum fs_status status = FS_SUCCESS;
for (size_t i = 0;; i++) {
tar_header_decode(bin_header, &header);
if (bin_header->filename[0] == 0) {
break;
}
char *s = (char *)bin_header;
s += sizeof *bin_header;
bool add = true;
if (!strcmp(header.filename, "././@PaxHeader")) {
add = false;
}
if (add) {
status = add_entry_to_tree(ctx, sb, &header, s);
}
if (status != FS_SUCCESS) {
break;
}
s += header.size;
if ((uintptr_t)s % (sizeof *bin_header)) {
s += ((sizeof *bin_header)
- ((uintptr_t)s % (sizeof *bin_header)));
}
bin_header = (struct tar_bin_header *)s;
}
return FS_SUCCESS;
}
enum fs_status tar_mount(
struct fs_context *ctx,
void *arg,
enum fs_mount_flags flags,
struct fs_superblock **out)
{
struct tar_superblock *sb = fs_context_alloc(ctx, sizeof *sb);
if (!sb) {
return FS_ERR_NO_MEMORY;
}
sb->sb_data = arg;
enum fs_status status = build_dentry_tree(ctx, sb);
if (status != FS_SUCCESS) {
fs_context_free(ctx, sb);
return status;
}
*out = (struct fs_superblock *)sb;
return FS_SUCCESS;
}

View File

@@ -1,6 +1,8 @@
#ifndef TAR_H_
#define TAR_H_
#include <fs/context.h>
#include <fs/status.h>
#include <mango/types.h>
#include <stddef.h>
#include <stdint.h>
@@ -42,4 +44,10 @@ extern int tar_header_decode(
const struct tar_bin_header *in,
struct tar_header *out);
extern enum fs_status tar_mount(
struct fs_context *ctx,
void *arg,
enum fs_mount_flags flags,
struct fs_superblock **out);
#endif

View File

@@ -10,7 +10,7 @@ set_target_properties(ld PROPERTIES
SUFFIX ".so")
target_link_libraries(ld
libc-core libc-malloc libmango librosetta
libc-core libc-malloc libc-io libmango librosetta libxpc-static
interface::fs)
target_compile_options(ld PRIVATE

View File

@@ -0,0 +1,47 @@
.code64
.global _dl_runtime_resolve
.type _dl_runtime_resolve, @function
.extern dl_runtime_resolve
.type dl_runtime_resolve, @function
_dl_runtime_resolve:
// pop %rdi
// pop %rsi
pop %rax
pop %r11
push %rsp
push %rbp
push %rdi
push %rsi
push %rdx
push %rcx
push %r8
push %r9
push %rbx
push %r12
push %r13
push %r14
push %r15
mov %rax, %rdi
mov %r11, %rsi
call dl_runtime_resolve
pop %r15
pop %r14
pop %r13
pop %r12
pop %rbx
pop %r9
pop %r8
pop %rcx
pop %rdx
pop %rsi
pop %rdi
pop %rbp
pop %rsp
jmp *%rax

691
sys/ld/btree.c Normal file
View File

@@ -0,0 +1,691 @@
/*
The Clear BSD License
Copyright (c) 2023 Max Wash
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the disclaimer
below) provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
*/
/* templated AVL binary tree implementation
this file implements an extensible AVL binary tree data structure.
the primary rule of an AVL binary tree is that for a given node N,
the heights of N's left and right subtrees can differ by at most 1.
the height of a subtree is the length of the longest path between
the root of the subtree and a leaf node, including the root node itself.
the height of a leaf node is 1.
when a node is inserted into or deleted from the tree, this rule may
be broken, in which the tree must be rotated to restore the balance.
no more than one rotation is required for any insert operations,
while multiple rotations may be required for a delete operation.
there are four types of rotations that can be applied to a tree:
- left rotation
- right rotation
- double left rotations
- double right rotations
by enforcing the balance rule, for a tree with n nodes, the worst-case
performance for insert, delete, and search operations is guaranteed
to be O(log n).
this file intentionally excludes any kind of search function implementation.
it is up to the programmer to implement their own tree node type
using struct btree_node, and their own search function using struct btree.
this allows the programmer to define their own node types with complex
non-integer key types. btree.h contains a number of macros to help
define these functions. the macros do all the work, you just have to
provide a comparator function.
*/
#include "btree.h"
#include <stddef.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define IS_LEFT_CHILD(p, c) ((p) && (c) && ((p)->b_left == (c)))
#define IS_RIGHT_CHILD(p, c) ((p) && (c) && ((p)->b_right == (c)))
#define HAS_LEFT_CHILD(x) ((x) && ((x)->b_left))
#define HAS_RIGHT_CHILD(x) ((x) && ((x)->b_right))
#define HAS_NO_CHILDREN(x) ((x) && (!(x)->b_left) && (!(x)->b_right))
#define HAS_ONE_CHILD(x) \
((HAS_LEFT_CHILD(x) && !HAS_RIGHT_CHILD(x)) \
|| (!HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x)))
#define HAS_TWO_CHILDREN(x) (HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x))
#define HEIGHT(x) ((x) ? (x)->b_height : 0)
static inline void update_height(struct btree_node *x)
{
x->b_height = MAX(HEIGHT(x->b_left), HEIGHT((x->b_right))) + 1;
}
static inline int bf(struct btree_node *x)
{
int bf = 0;
if (!x) {
return bf;
}
if (x->b_right) {
bf += x->b_right->b_height;
}
if (x->b_left) {
bf -= x->b_left->b_height;
}
return bf;
}
/* perform a left rotation on a subtree
if you have a tree like this:
Z
/ \
X .
/ \
. Y
/ \
. .
and you perform a left rotation on node X,
you will get the following tree:
Z
/ \
Y .
/ \
X .
/ \
. .
note that this function does NOT update b_height for the rotated
nodes. it is up to you to call update_height_to_root().
*/
static void rotate_left(struct btree *tree, struct btree_node *x)
{
struct btree_node *y = x->b_right;
struct btree_node *p = x->b_parent;
if (y->b_left) {
y->b_left->b_parent = x;
}
x->b_right = y->b_left;
if (!p) {
tree->b_root = y;
} else if (x == p->b_left) {
p->b_left = y;
} else {
p->b_right = y;
}
x->b_parent = y;
y->b_left = x;
y->b_parent = p;
}
static void update_height_to_root(struct btree_node *x)
{
while (x) {
update_height(x);
x = x->b_parent;
}
}
/* perform a right rotation on a subtree
if you have a tree like this:
Z
/ \
. X
/ \
Y .
/ \
. .
and you perform a right rotation on node X,
you will get the following tree:
Z
/ \
. Y
/ \
. X
/ \
. .
note that this function does NOT update b_height for the rotated
nodes. it is up to you to call update_height_to_root().
*/
static void rotate_right(struct btree *tree, struct btree_node *y)
{
struct btree_node *x = y->b_left;
struct btree_node *p = y->b_parent;
if (x->b_right) {
x->b_right->b_parent = y;
}
y->b_left = x->b_right;
if (!p) {
tree->b_root = x;
} else if (y == p->b_left) {
p->b_left = x;
} else {
p->b_right = x;
}
y->b_parent = x;
x->b_right = y;
x->b_parent = p;
}
/* for a given node Z, perform a right rotation on Z's right child,
followed by a left rotation on Z itself.
if you have a tree like this:
Z
/ \
. X
/ \
Y .
/ \
. .
and you perform a double-left rotation on node Z,
you will get the following tree:
Y
/ \
/ \
Z X
/ \ / \
. . . .
note that, unlike rotate_left and rotate_right, this function
DOES update b_height for the rotated nodes (since it needs to be
done in a certain order).
*/
static void rotate_double_left(struct btree *tree, struct btree_node *z)
{
struct btree_node *x = z->b_right;
struct btree_node *y = x->b_left;
rotate_right(tree, x);
rotate_left(tree, z);
update_height(z);
update_height(x);
while (y) {
update_height(y);
y = y->b_parent;
}
}
/* for a given node Z, perform a left rotation on Z's left child,
followed by a right rotation on Z itself.
if you have a tree like this:
Z
/ \
X .
/ \
. Y
/ \
. .
and you perform a double-right rotation on node Z,
you will get the following tree:
Y
/ \
/ \
X Z
/ \ / \
. . . .
note that, unlike rotate_left and rotate_right, this function
DOES update b_height for the rotated nodes (since it needs to be
done in a certain order).
*/
static void rotate_double_right(struct btree *tree, struct btree_node *z)
{
struct btree_node *x = z->b_left;
struct btree_node *y = x->b_right;
rotate_left(tree, x);
rotate_right(tree, z);
update_height(z);
update_height(x);
while (y) {
update_height(y);
y = y->b_parent;
}
}
/* run after an insert operation. checks that the balance factor
of the local subtree is within the range -1 <= BF <= 1. if it
is not, rotate the subtree to restore balance.
note that at most one rotation should be required after a node
is inserted into the tree.
this function depends on all nodes in the tree having
correct b_height values.
@param w the node that was just inserted into the tree
*/
static void insert_fixup(struct btree *tree, struct btree_node *w)
{
struct btree_node *z = NULL, *y = NULL, *x = NULL;
z = w;
while (z) {
if (bf(z) >= -1 && bf(z) <= 1) {
goto next_ancestor;
}
if (IS_LEFT_CHILD(z, y)) {
if (IS_LEFT_CHILD(y, x)) {
rotate_right(tree, z);
update_height_to_root(z);
} else {
rotate_double_right(tree, z);
}
} else {
if (IS_LEFT_CHILD(y, x)) {
rotate_double_left(tree, z);
} else {
rotate_left(tree, z);
update_height_to_root(z);
}
}
next_ancestor:
x = y;
y = z;
z = z->b_parent;
}
}
/* run after a delete operation. checks that the balance factor
of the local subtree is within the range -1 <= BF <= 1. if it
is not, rotate the subtree to restore balance.
note that, unlike insert_fixup, multiple rotations may be required
to restore balance after a node is deleted.
this function depends on all nodes in the tree having
correct b_height values.
@param w one of the following:
- the parent of the node that was deleted if the node
had no children.
- the parent of the node that replaced the deleted node
if the deleted node had two children.
- the node that replaced the node that was deleted, if
the node that was deleted had one child.
*/
static void delete_fixup(struct btree *tree, struct btree_node *w)
{
struct btree_node *z = w;
while (z) {
if (bf(z) > 1) {
if (bf(z->b_right) >= 0) {
rotate_left(tree, z);
update_height_to_root(z);
} else {
rotate_double_left(tree, z);
}
} else if (bf(z) < -1) {
if (bf(z->b_left) <= 0) {
rotate_right(tree, z);
update_height_to_root(z);
} else {
rotate_double_right(tree, z);
}
}
z = z->b_parent;
}
}
/* updates b_height for all nodes between the inserted node and the root
of the tree, and calls insert_fixup.
@param node the node that was just inserted into the tree.
*/
void btree_insert_fixup(struct btree *tree, struct btree_node *node)
{
node->b_height = 0;
struct btree_node *cur = node;
while (cur) {
update_height(cur);
cur = cur->b_parent;
}
insert_fixup(tree, node);
}
/* remove a node from a tree.
this function assumes that `node` has no children, and therefore
doesn't need to be replaced.
updates b_height for all nodes between `node` and the tree root.
@param node the node to delete.
*/
static struct btree_node *remove_node_with_no_children(
struct btree *tree,
struct btree_node *node)
{
struct btree_node *w = node->b_parent;
struct btree_node *p = node->b_parent;
node->b_parent = NULL;
if (!p) {
tree->b_root = NULL;
} else if (IS_LEFT_CHILD(p, node)) {
p->b_left = NULL;
} else {
p->b_right = NULL;
}
while (p) {
update_height(p);
p = p->b_parent;
}
return w;
}
/* remove a node from a tree.
this function assumes that `node` has one child.
the child of `node` is inherited by `node`'s parent, and `node` is removed.
updates b_height for all nodes between the node that replaced
`node` and the tree root.
@param node the node to delete.
*/
static struct btree_node *replace_node_with_one_subtree(
struct btree *tree,
struct btree_node *node)
{
struct btree_node *p = node->b_parent;
struct btree_node *z = NULL;
if (HAS_LEFT_CHILD(node)) {
z = node->b_left;
} else {
z = node->b_right;
}
struct btree_node *w = z;
if (!p) {
tree->b_root = z;
} else if (IS_LEFT_CHILD(p, node)) {
p->b_left = z;
} else if (IS_RIGHT_CHILD(p, node)) {
p->b_right = z;
}
z->b_parent = p;
node->b_parent = NULL;
node->b_left = node->b_right = NULL;
while (z) {
update_height(z);
z = z->b_parent;
}
return w;
}
/* remove a node from a tree.
this function assumes that `node` has two children.
find the in-order successor Y of `node` (the largest node in `node`'s left
sub-tree), removes `node` from the tree and moves Y to where `node` used to
be.
if Y has a child (it will never have more than one), have Y's parent inherit
Y's child.
updates b_height for all nodes between the deepest node that was modified
and the tree root.
@param z the node to delete.
*/
static struct btree_node *replace_node_with_two_subtrees(
struct btree *tree,
struct btree_node *z)
{
/* x will replace z */
struct btree_node *x = z->b_left;
while (x->b_right) {
x = x->b_right;
}
/* y is the node that will replace x (if x has a left child) */
struct btree_node *y = x->b_left;
/* w is the starting point for the height update and fixup */
struct btree_node *w = x;
if (w->b_parent != z) {
w = w->b_parent;
}
if (y) {
w = y;
}
if (IS_LEFT_CHILD(x->b_parent, x)) {
x->b_parent->b_left = y;
} else if (IS_RIGHT_CHILD(x->b_parent, x)) {
x->b_parent->b_right = y;
}
if (y) {
y->b_parent = x->b_parent;
}
if (IS_LEFT_CHILD(z->b_parent, z)) {
z->b_parent->b_left = x;
} else if (IS_RIGHT_CHILD(z->b_parent, z)) {
z->b_parent->b_right = x;
}
x->b_parent = z->b_parent;
x->b_left = z->b_left;
x->b_right = z->b_right;
if (x->b_left) {
x->b_left->b_parent = x;
}
if (x->b_right) {
x->b_right->b_parent = x;
}
if (!x->b_parent) {
tree->b_root = x;
}
struct btree_node *cur = w;
while (cur) {
update_height(cur);
cur = cur->b_parent;
}
return w;
}
/* delete a node from the tree and re-balance it afterwards */
void btree_delete(struct btree *tree, struct btree_node *node)
{
struct btree_node *w = NULL;
if (HAS_NO_CHILDREN(node)) {
w = remove_node_with_no_children(tree, node);
} else if (HAS_ONE_CHILD(node)) {
w = replace_node_with_one_subtree(tree, node);
} else if (HAS_TWO_CHILDREN(node)) {
w = replace_node_with_two_subtrees(tree, node);
}
if (w) {
delete_fixup(tree, w);
}
node->b_left = node->b_right = node->b_parent = NULL;
}
struct btree_node *btree_first(struct btree *tree)
{
/* the first node in the tree is the node with the smallest key.
we keep moving left until we can't go any further */
struct btree_node *cur = tree->b_root;
if (!cur) {
return NULL;
}
while (cur->b_left) {
cur = cur->b_left;
}
return cur;
}
struct btree_node *btree_last(struct btree *tree)
{
/* the first node in the tree is the node with the largest key.
we keep moving right until we can't go any further */
struct btree_node *cur = tree->b_root;
if (!cur) {
return NULL;
}
while (cur->b_right) {
cur = cur->b_right;
}
return cur;
}
struct btree_node *btree_next(struct btree_node *node)
{
if (!node) {
return NULL;
}
/* there are two possibilities for the next node:
1. if `node` has a right sub-tree, every node in this sub-tree is
bigger than node. the in-order successor of `node` is the smallest
node in this subtree.
2. if `node` has no right sub-tree, we've reached the largest node in
the sub-tree rooted at `node`. we need to go back to our parent
and continue the search elsewhere.
*/
if (node->b_right) {
/* case 1: step into `node`'s right sub-tree and keep going
left to find the smallest node */
struct btree_node *cur = node->b_right;
while (cur->b_left) {
cur = cur->b_left;
}
return cur;
}
/* case 2: keep stepping back up towards the root of the tree.
if we encounter a step where we are our parent's left child,
we've found a parent with a value larger than us. this parent
is the in-order successor of `node` */
while (node->b_parent && node->b_parent->b_left != node) {
node = node->b_parent;
}
return node->b_parent;
}
struct btree_node *btree_prev(struct btree_node *node)
{
if (!node) {
return NULL;
}
/* there are two possibilities for the previous node:
1. if `node` has a left sub-tree, every node in this sub-tree is
smaller than `node`. the in-order predecessor of `node` is the
largest node in this subtree.
2. if `node` has no left sub-tree, we've reached the smallest node in
the sub-tree rooted at `node`. we need to go back to our parent
and continue the search elsewhere.
*/
if (node->b_left) {
/* case 1: step into `node`'s left sub-tree and keep going
right to find the largest node */
struct btree_node *cur = node->b_left;
while (cur->b_right) {
cur = cur->b_right;
}
return cur;
}
/* case 2: keep stepping back up towards the root of the tree.
if we encounter a step where we are our parent's right child,
we've found a parent with a value smaller than us. this parent
is the in-order predecessor of `node`. */
while (node->b_parent && node->b_parent->b_right != node) {
node = node->b_parent;
}
return node->b_parent;
}

475
sys/ld/btree.h Normal file
View File

@@ -0,0 +1,475 @@
/*
The Clear BSD License
Copyright (c) 2023 Max Wash
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the disclaimer
below) provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
*/
#ifndef BTREE_H_
#define BTREE_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* if your custom structure contains a struct btree_node (i.e. it can be part of
a btree), you can use this macro to convert a struct btree_node* to a
your_type*
@param t the name of your custom type (something that can be passed to
offsetof)
@param m the name of the struct btree_node member variable within your custom
type.
@param v the struct btree_node pointer that you wish to convert. if this is
NULL, NULL will be returned.
*/
#define BTREE_CONTAINER(t, m, v) \
((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0))
/* defines a simple node insertion function.
this function assumes that your nodes have simple integer keys that can be
compared with the usual operators.
EXAMPLE:
if you have a tree node type like this:
struct my_tree_node {
int key;
struct btree_node base;
}
You would use the following call to generate an insert function for a tree
with this node type:
BTREE_DEFINE_SIMPLE_INSERT(struct my_tree_node, base, key,
my_tree_node_insert);
Which would emit a function defined like:
static void my_tree_node_insert(struct btree *tree, struct my_tree_node
*node);
@param node_type your custom tree node type. usually a structure that
contains a struct btree_node member.
@param container_node_member the name of the struct btree_node member
variable within your custom type.
@param container_key_member the name of the key member variable within your
custom type.
@param function_name the name of the function to generate.
*/
#define BTREE_DEFINE_SIMPLE_INSERT( \
node_type, \
container_node_member, \
container_key_member, \
function_name) \
void function_name(struct btree *tree, node_type *node) \
{ \
if (!tree->b_root) { \
tree->b_root = &node->container_node_member; \
btree_insert_fixup( \
tree, \
&node->container_node_member); \
return; \
} \
\
struct btree_node *cur = tree->b_root; \
while (1) { \
node_type *cur_node = BTREE_CONTAINER( \
node_type, \
container_node_member, \
cur); \
struct btree_node *next = NULL; \
\
if (node->container_key_member \
> cur_node->container_key_member) { \
next = btree_right(cur); \
\
if (!next) { \
btree_put_right( \
cur, \
&node->container_node_member); \
break; \
} \
} else if ( \
node->container_key_member \
< cur_node->container_key_member) { \
next = btree_left(cur); \
\
if (!next) { \
btree_put_left( \
cur, \
&node->container_node_member); \
break; \
} \
} else { \
return; \
} \
\
cur = next; \
} \
\
btree_insert_fixup(tree, &node->container_node_member); \
}
/* defines a node insertion function.
this function should be used for trees with complex node keys that cannot be
directly compared. a comparator for your keys must be supplied.
EXAMPLE:
if you have a tree node type like this:
struct my_tree_node {
complex_key_t key;
struct btree_node base;
}
You would need to define a comparator function or macro with the following
signature:
int my_comparator(struct my_tree_node *a, struct my_tree_node *b);
Which implements the following:
return -1 if a < b
return 0 if a == b
return 1 if a > b
You would use the following call to generate an insert function for a tree
with this node type:
BTREE_DEFINE_INSERT(struct my_tree_node, base, key, my_tree_node_insert,
my_comparator);
Which would emit a function defined like:
static void my_tree_node_insert(struct btree *tree, struct my_tree_node
*node);
@param node_type your custom tree node type. usually a structure that
contains a struct btree_node member.
@param container_node_member the name of the struct btree_node member
variable within your custom type.
@param container_key_member the name of the key member variable within your
custom type.
@param function_name the name of the function to generate.
@param comparator the name of a comparator function or functional-macro that
conforms to the requirements listed above.
*/
#define BTREE_DEFINE_INSERT( \
node_type, \
container_node_member, \
container_key_member, \
function_name, \
comparator) \
void function_name(struct btree *tree, node_type *node) \
{ \
if (!tree->b_root) { \
tree->b_root = &node->container_node_member; \
btree_insert_fixup( \
tree, \
&node->container_node_member); \
return; \
} \
\
struct btree_node *cur = tree->b_root; \
while (1) { \
node_type *cur_node = BTREE_CONTAINER( \
node_type, \
container_node_member, \
cur); \
struct btree_node *next = NULL; \
int cmp = comparator(node, cur_node); \
\
if (cmp == 1) { \
next = btree_right(cur); \
\
if (!next) { \
btree_put_right( \
cur, \
&node->container_node_member); \
break; \
} \
} else if (cmp == -1) { \
next = btree_left(cur); \
\
if (!next) { \
btree_put_left( \
cur, \
&node->container_node_member); \
break; \
} \
} else { \
return; \
} \
\
cur = next; \
} \
\
btree_insert_fixup(tree, &node->container_node_member); \
}
/* defines a simple tree search function.
this function assumes that your nodes have simple integer keys that can be
compared with the usual operators.
EXAMPLE:
if you have a tree node type like this:
struct my_tree_node {
int key;
struct btree_node base;
}
You would use the following call to generate a search function for a tree
with this node type:
BTREE_DEFINE_SIMPLE_GET(struct my_tree_node, int, base, key,
my_tree_node_get);
Which would emit a function defined like:
static struct my_tree_node *my_tree_node_get(struct btree *tree, int key);
@param node_type your custom tree node type. usually a structure that
contains a struct btree_node member.
@param key_type the type name of the key embedded in your custom tree node
type. this type must be compatible with the builtin comparison operators.
@param container_node_member the name of the struct btree_node member
variable within your custom type.
@param container_key_member the name of the key member variable within your
custom type.
@param function_name the name of the function to generate.
*/
#define BTREE_DEFINE_SIMPLE_GET( \
node_type, \
key_type, \
container_node_member, \
container_key_member, \
function_name) \
node_type *function_name(struct btree *tree, key_type key) \
{ \
struct btree_node *cur = tree->b_root; \
while (cur) { \
node_type *cur_node = BTREE_CONTAINER( \
node_type, \
container_node_member, \
cur); \
if (key > cur_node->container_key_member) { \
cur = btree_right(cur); \
} else if (key < cur_node->container_key_member) { \
cur = btree_left(cur); \
} else { \
return cur_node; \
} \
} \
\
return NULL; \
}
/* perform an in-order traversal of a binary tree
If you have a tree defined like:
struct btree my_tree;
with nodes defined like:
struct my_tree_node {
int key;
struct btree_node base;
}
and you want to do something like:
foreach (struct my_tree_node *node : my_tree) { ... }
you should use this:
btree_foreach (struct my_tree_node, node, &my_tree, base) { ... }
@param iter_type the type name of the iterator variable. this should be the
tree's node type, and shouldn't be a pointer.
@param iter_name the name of the iterator variable.
@param tree_name a pointer to the tree to traverse.
@param node_member the name of the struct btree_node member variable within
the tree node type.
*/
#define btree_foreach(iter_type, iter_name, tree_name, node_member) \
for (iter_type *iter_name = BTREE_CONTAINER( \
iter_type, \
node_member, \
btree_first(tree_name)); \
iter_name; \
iter_name = BTREE_CONTAINER( \
iter_type, \
node_member, \
btree_next(&((iter_name)->node_member))))
/* perform an reverse in-order traversal of a binary tree
If you have a tree defined like:
struct btree my_tree;
with nodes defined like:
struct my_tree_node {
int key;
struct btree_node base;
}
and you want to do something like:
foreach (struct my_tree_node *node : reverse(my_tree)) { ... }
you should use this:
btree_foreach_r (struct my_tree_node, node, &my_tree, base) { ... }
@param iter_type the type name of the iterator variable. this should be the
tree's node type, and shouldn't be a pointer.
@param iter_name the name of the iterator variable.
@param tree_name a pointer to the tree to traverse.
@param node_member the name of the struct btree_node member variable within
the tree node type.
*/
#define btree_foreach_r(iter_type, iter_name, tree_name, node_member) \
for (iter_type *iter_name \
= BTREE_CONTAINER(iter_type, node_member, btree_last(tree_name)); \
iter_name; \
iter_name = BTREE_CONTAINER( \
iter_type, \
node_member, \
btree_prev(&((iter_name)->node_member))))
/* binary tree nodes. this *cannot* be used directly. you need to define a
custom node type that contains a member variable of type struct btree_node.
you would then use the supplied macros to define functions to manipulate your
custom binary tree.
*/
struct btree_node {
struct btree_node *b_parent, *b_left, *b_right;
unsigned short b_height;
};
/* binary tree. unlike struct btree_node, you can define variables of type
* struct btree. */
struct btree {
struct btree_node *b_root;
};
/* re-balance a binary tree after an insertion operation.
NOTE that, if you define an insertion function using BTREE_DEFINE_INSERT or
similar, this function will automatically called for you.
@param tree the tree to re-balance.
@param node the node that was just inserted into the tree.
*/
extern void btree_insert_fixup(struct btree *tree, struct btree_node *node);
/* delete a node from a binary tree and re-balance the tree afterwards.
@param tree the tree to delete from
@param node the node to delete.
*/
extern void btree_delete(struct btree *tree, struct btree_node *node);
/* get the first node in a binary tree.
this will be the node with the smallest key (i.e. the node that is
furthest-left from the root)
*/
extern struct btree_node *btree_first(struct btree *tree);
/* get the last node in a binary tree.
this will be the node with the largest key (i.e. the node that is
furthest-right from the root)
*/
extern struct btree_node *btree_last(struct btree *tree);
/* for any binary tree node, this function returns the node with the
* next-largest key value */
extern struct btree_node *btree_next(struct btree_node *node);
/* for any binary tree node, this function returns the node with the
* next-smallest key value */
extern struct btree_node *btree_prev(struct btree_node *node);
static inline bool btree_empty(const struct btree *tree)
{
return tree->b_root == NULL;
}
/* sets `child` as the immediate left-child of `parent` */
static inline void btree_put_left(
struct btree_node *parent,
struct btree_node *child)
{
parent->b_left = child;
child->b_parent = parent;
}
/* sets `child` as the immediate right-child of `parent` */
static inline void btree_put_right(
struct btree_node *parent,
struct btree_node *child)
{
parent->b_right = child;
child->b_parent = parent;
}
/* get the immediate left-child of `node` */
static inline struct btree_node *btree_left(struct btree_node *node)
{
return node->b_left;
}
/* get the immediate right-child of `node` */
static inline struct btree_node *btree_right(struct btree_node *node)
{
return node->b_right;
}
/* get the immediate parent of `node` */
static inline struct btree_node *btree_parent(struct btree_node *node)
{
return node->b_parent;
}
/* get the height of `node`.
the height of a node is defined as the length of the longest path
between the node and a leaf node.
this count includes the node itself, so the height of a leaf node will be 1.
*/
static inline unsigned short btree_height(struct btree_node *node)
{
return node->b_height;
}
#ifdef __cplusplus
}
#endif
#endif

800
sys/ld/elf.c Normal file
View File

@@ -0,0 +1,800 @@
#include "elf.h"
#include "resolve.h"
#include <errno.h>
#include <fcntl.h>
#include <mango/config.h>
#include <mango/handle.h>
#include <mango/log.h>
#include <mango/vm.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define NEEDS_NOTHING 0
#define NEEDS_VDSO 1
#define NEEDS_MORE 2
#define ACL (PF_R | PF_W | PF_X)
#define ACCESS(x) ((x) & ACL)
/* TODO in case we ever support ELF32 images */
#define elf_class_bits(x) (64)
#define PAGE_SIZE (image->e_page_size)
#define PAGE_MASK (image->e_page_size - 1)
#define PAGE_OFFSET(v) ((v) & (PAGE_SIZE - 1))
#define PAGE_ALIGN_DOWN(v) (v) &= ~(PAGE_SIZE - 1)
#define PAGE_ALIGN_UP(v) \
do { \
if ((v) & (PAGE_SIZE - 1)) { \
v &= ~(PAGE_SIZE - 1); \
v += PAGE_SIZE; \
} \
} while (0)
#undef DEBUG_LOG
const char *elf_image_status_to_string(enum elf_image_status status)
{
#define ENUM_STR(s) \
case s: \
return #s
switch (status) {
ENUM_STR(ELF_IMAGE_NONE);
ENUM_STR(ELF_IMAGE_OPEN);
ENUM_STR(ELF_IMAGE_PARSED);
ENUM_STR(ELF_IMAGE_LOADED);
ENUM_STR(ELF_IMAGE_LINKED);
default:
return "UNKNOWN";
}
#undef ENUM_STR
}
static bool elf_validate_ehdr(elf_ehdr_t *hdr)
{
if (hdr->e_ident[EI_MAG0] != ELF_MAG0) {
return false;
}
if (hdr->e_ident[EI_MAG1] != ELF_MAG1) {
return false;
}
if (hdr->e_ident[EI_MAG2] != ELF_MAG2) {
return false;
}
if (hdr->e_ident[EI_MAG3] != ELF_MAG3) {
return false;
}
if (hdr->e_ident[EI_CLASS] != ELFCLASS64) {
return false;
}
if (hdr->e_machine != EM_X86_64) {
return false;
}
if (hdr->e_ident[EI_DATA] != ELFDATA2LSB) {
return false;
}
if (hdr->e_ident[EI_VERSION] != EV_CURRENT) {
return false;
}
return true;
}
static int map_image(struct elf_image *image)
{
elf_phdr_t phdr;
size_t r = 0;
size_t data_offset = 0;
for (size_t i = 0; i < image->e_hdr.e_phnum; i++) {
off_t phdr_offset
= image->e_hdr.e_phoff + (i * image->e_hdr.e_phentsize);
lseek(image->e_fd, phdr_offset, SEEK_SET);
int r = read(image->e_fd, &phdr, sizeof phdr);
if (r < 0) {
return -r;
}
if (r != sizeof phdr) {
return ENOEXEC;
}
if (phdr.p_type != PT_LOAD) {
continue;
}
int prot = 0;
size_t offset = phdr.p_offset & ~PAGE_MASK;
phdr.p_flags &PF_R && (prot |= PROT_READ);
phdr.p_flags &PF_W && (prot |= PROT_WRITE);
phdr.p_flags &PF_X && (prot |= PROT_EXEC);
virt_addr_t vaddr = phdr.p_vaddr;
virt_addr_t vlimit = phdr.p_vaddr + phdr.p_memsz;
if (vaddr & PAGE_MASK) {
vaddr &= ~PAGE_MASK;
}
if (vlimit & PAGE_MASK) {
vlimit &= ~PAGE_MASK;
vlimit += PAGE_SIZE;
}
if (image->e_hdr.e_type == ET_DYN) {
vaddr += image->e_base;
vlimit += image->e_base;
}
int fd = image->e_fd;
int flags = MAP_SHARED | MAP_EXECUTABLE | MAP_FIXED;
if (phdr.p_flags & PF_W) {
fd = -1;
flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
offset = 0;
}
void *p
= mmap((void *)vaddr,
vlimit - vaddr,
prot,
flags,
fd,
offset);
if (p == MAP_FAILED) {
return EIO;
}
kern_tracef(
"mapped PHDR %u [%zx-%zx] at %p",
i,
phdr.p_vaddr,
phdr.p_vaddr + phdr.p_memsz,
p);
if (phdr.p_flags & PF_W) {
lseek(image->e_fd, phdr.p_offset, SEEK_SET);
void *dst = (void *)image->e_base + phdr.p_vaddr;
r = read(image->e_fd, dst, phdr.p_filesz);
if (r < 0) {
return -r;
}
}
}
return SUCCESS;
}
static int parse_phdr(struct elf_image *image)
{
elf_phdr_t phdr;
size_t r = 0;
image->e_length = 0;
image->e_data_length = 0;
off_t vaddr, vlimit;
for (size_t i = 0; i < image->e_hdr.e_phnum; i++) {
off_t phdr_offset
= image->e_hdr.e_phoff + (i * image->e_hdr.e_phentsize);
lseek(image->e_fd, phdr_offset, SEEK_SET);
int r = read(image->e_fd, &phdr, sizeof phdr);
if (r < 0) {
return -r;
}
if (r != sizeof phdr) {
return ENOEXEC;
}
vaddr = phdr.p_vaddr;
vlimit = phdr.p_vaddr + phdr.p_memsz;
if (vaddr & (PAGE_SIZE - 1)) {
vaddr &= ~(PAGE_SIZE - 1);
}
if (vlimit & (PAGE_SIZE - 1)) {
vlimit &= ~(PAGE_SIZE - 1);
vlimit += PAGE_SIZE;
}
switch (phdr.p_type) {
case PT_DYNAMIC:
image->e_dynamic = phdr;
break;
case PT_LOAD:
image->e_length = MAX(image->e_length, vlimit);
break;
#if 0
case PT_INTERP: {
size_t r = 0;
vm_object_read(
image->e_image,
image->e_interp,
phdr.p_offset,
MIN(sizeof image->e_interp - 1, phdr.p_filesz),
&r);
image->e_interp[r] = 0;
break;
}
#endif
default:
break;
}
if (phdr.p_flags & PF_W) {
image->e_data_length
= MAX(image->e_data_length, vlimit - vaddr);
}
}
return SUCCESS;
}
#if 1
static elf_sym_t *get_dynsym(struct elf_image *image, size_t index)
{
elf_sym_t *sym = (elf_sym_t *)(image->e_base + image->e_dynsym
+ (index * image->e_dynsym_entsize));
if (!sym->st_value) {
return NULL;
}
return sym;
}
static void resolve_symbol(unsigned int slot)
{
kern_tracef("request for symbol %u", slot);
}
static int do_rela(struct elf_image *image, elf_rela_t *rela, bool lazy)
{
kern_tracef(
"do_rela(%p, %d, %d, %d)",
image,
rela->r_info,
rela->r_addend,
rela->r_offset);
int type = ELF64_R_TYPE(rela->r_info);
elf_sym_t *sym = NULL;
switch (type) {
case R_X86_64_JUMP_SLOT:
*(uint64_t *)(image->e_base + rela->r_offset) += image->e_base;
kern_tracef(
"JUMP_SLOT: offset=%zx, symbol=%zu, addend=%zx",
rela->r_offset,
ELF64_R_SYM(rela->r_info),
rela->r_addend);
break;
case R_X86_64_RELATIVE:
*(uint64_t *)(image->e_base + rela->r_offset)
= image->e_base + rela->r_addend;
kern_tracef(
"RELATIVE: offset=%zx, addend=%zx",
rela->r_offset,
rela->r_addend);
break;
default:
kern_log("Unknown relocation type");
return ENOEXEC;
}
return SUCCESS;
}
static int relocate_pltrel(
struct elf_image *image,
off_t offset,
size_t size,
size_t entsize)
{
size_t entries = size / entsize;
elf_rela_t *rela = (elf_rela_t *)(image->e_base + offset);
int status = SUCCESS;
for (size_t i = 0; i < entries; i++) {
status = do_rela(image, rela, true);
if (status != SUCCESS) {
break;
}
rela = (elf_rela_t *)((char *)rela + entsize);
}
return status;
}
static int relocate_rela(
struct elf_image *image,
off_t offset,
size_t size,
size_t entsize)
{
size_t entries = size / entsize;
elf_rela_t *rela = (elf_rela_t *)(image->e_base + offset);
int status = SUCCESS;
for (size_t i = 0; i < entries; i++) {
status = do_rela(image, rela, false);
if (status != SUCCESS) {
break;
}
rela = (elf_rela_t *)((char *)rela + entsize);
}
return status;
}
static int relocate_rel(
struct elf_image *image,
off_t offset,
size_t size,
size_t entsize)
{
return ENOEXEC;
}
static int do_rel(
struct elf_image *image,
off_t offset,
size_t size,
size_t entsize)
{
kern_tracef("do_rel (unsupported)");
return ENOEXEC;
}
#endif
static int load_dependency(struct elf_image *image, const char *name)
{
kern_tracef("required library: %s", name);
return ENOEXEC;
}
static int parse_dynamic(struct elf_image *image)
{
if (image->e_dynamic.p_type != PT_DYNAMIC) {
return SUCCESS;
}
image->e_dyn = (elf_dyn_t *)(image->e_base + image->e_dynamic.p_vaddr);
int status = SUCCESS;
size_t nr_dyn = image->e_dynamic.p_filesz / sizeof *image->e_dyn;
for (size_t i = 0; i < nr_dyn; i++) {
if (image->e_dyn[i].d_tag == DT_NULL) {
break;
}
switch (image->e_dyn[i].d_tag) {
case DT_NEEDED:
image->e_nr_links++;
break;
case DT_STRTAB:
image->e_strtab = image->e_dyn[i].d_un.d_ptr;
break;
case DT_SYMTAB:
image->e_dynsym = image->e_dyn[i].d_un.d_ptr;
break;
case DT_SYMENT:
image->e_dynsym_entsize = image->e_dyn[i].d_un.d_val;
break;
case DT_PLTGOT:
image->e_got_plt = image->e_dyn[i].d_un.d_val;
break;
case DT_HASH:
image->e_hash_type = ELF_HASH_STANDARD;
image->e_hash_table = image->e_dyn[i].d_un.d_ptr;
break;
case DT_GNU_HASH:
image->e_hash_type = ELF_HASH_GNU;
image->e_hash_table = image->e_dyn[i].d_un.d_ptr;
break;
case DT_REL:
image->e_rel_offset[ELF_RT_REL]
= image->e_dyn[i].d_un.d_ptr;
break;
case DT_RELSZ:
image->e_rel_size[ELF_RT_REL]
= image->e_dyn[i].d_un.d_val;
break;
case DT_RELENT:
image->e_rel_entsize[ELF_RT_REL]
= image->e_dyn[i].d_un.d_val;
break;
case DT_RELA:
image->e_rel_offset[ELF_RT_RELA]
= image->e_dyn[i].d_un.d_ptr;
break;
case DT_RELASZ:
image->e_rel_size[ELF_RT_RELA]
= image->e_dyn[i].d_un.d_val;
break;
case DT_RELAENT:
image->e_rel_entsize[ELF_RT_RELA]
= image->e_dyn[i].d_un.d_val;
break;
case DT_PLTREL:
image->e_pltrel_type = image->e_dyn[i].d_un.d_val;
switch (image->e_pltrel_type) {
case DT_REL:
image->e_rel_entsize[ELF_RT_PLTREL] = 0;
break;
case DT_RELA:
image->e_rel_entsize[ELF_RT_PLTREL]
= sizeof(elf_rela_t);
break;
default:
break;
}
break;
case DT_JMPREL:
image->e_rel_offset[ELF_RT_PLTREL]
= image->e_dyn[i].d_un.d_ptr;
break;
case DT_PLTRELSZ:
image->e_rel_size[ELF_RT_PLTREL]
= image->e_dyn[i].d_un.d_val;
break;
default:
break;
}
image->e_dyn_count++;
}
return SUCCESS;
}
static int reserve_exec_region(struct elf_image *image)
{
void *base
= mmap(NULL,
image->e_length,
PROT_NONE,
MAP_ANONYMOUS | MAP_PRIVATE,
-1,
0);
if (base == MAP_FAILED) {
return ENOMEM;
}
image->e_base = (virt_addr_t)base;
return KERN_OK;
}
static int create_image_with_name(const char *name, struct elf_image **out)
{
struct elf_image *elf = malloc(sizeof *elf);
if (!elf) {
return ENOMEM;
}
memset(elf, 0x0, sizeof *elf);
snprintf(elf->e_leaf.l_name, sizeof elf->e_leaf.l_name, "%s", name);
kern_config_get(
KERN_CFG_PAGE_SIZE,
&elf->e_page_size,
sizeof elf->e_page_size);
*out = elf;
return SUCCESS;
}
int elf_image_open(const char *path, struct elf_image **out)
{
struct elf_image *elf = malloc(sizeof *elf);
if (!elf) {
return ENOMEM;
}
memset(elf, 0x0, sizeof *elf);
kern_config_get(
KERN_CFG_PAGE_SIZE,
&elf->e_page_size,
sizeof elf->e_page_size);
int fd = open(path, O_RDONLY);
if (fd < 0) {
elf_image_close(elf);
return -fd;
}
elf->e_status = ELF_IMAGE_OPEN;
elf->e_fd = fd;
*out = elf;
return SUCCESS;
}
int elf_image_parse(struct elf_image *img)
{
if (img->e_status != ELF_IMAGE_OPEN) {
return EINVAL;
}
int e = read(img->e_fd, &img->e_hdr, sizeof img->e_hdr);
if (e < 0) {
return -e;
}
if (e != sizeof img->e_hdr) {
return ENOEXEC;
}
if (!elf_validate_ehdr(&img->e_hdr)) {
return ENOEXEC;
}
e = parse_phdr(img);
if (e != SUCCESS) {
return e;
}
img->e_status = ELF_IMAGE_PARSED;
return SUCCESS;
}
int elf_image_load(struct elf_image *img)
{
if (img->e_status != ELF_IMAGE_PARSED) {
return EINVAL;
}
int e = reserve_exec_region(img);
if (e != SUCCESS) {
return e;
}
e = map_image(img);
if (e != SUCCESS) {
return e;
}
e = parse_dynamic(img);
if (e != SUCCESS) {
return e;
}
img->e_status = ELF_IMAGE_LOADED;
return SUCCESS;
}
int elf_image_link(struct elf_image *img)
{
if (img->e_status != ELF_IMAGE_LOADED) {
return EINVAL;
}
int status = SUCCESS;
if (img->e_rel_offset[ELF_RT_REL]) {
status = relocate_rel(
img,
img->e_rel_offset[ELF_RT_REL],
img->e_rel_size[ELF_RT_REL],
img->e_rel_entsize[ELF_RT_REL]);
if (status != SUCCESS) {
return status;
}
}
if (img->e_rel_offset[ELF_RT_RELA]) {
status = relocate_rela(
img,
img->e_rel_offset[ELF_RT_RELA],
img->e_rel_size[ELF_RT_RELA],
img->e_rel_entsize[ELF_RT_RELA]);
if (status != SUCCESS) {
return status;
}
}
#if 1
if (img->e_rel_offset[ELF_RT_PLTREL]) {
status = relocate_pltrel(
img,
img->e_rel_offset[ELF_RT_PLTREL],
img->e_rel_size[ELF_RT_PLTREL],
img->e_rel_entsize[ELF_RT_PLTREL]);
if (status != SUCCESS) {
return status;
}
}
#endif
*(uintptr_t *)(img->e_base + img->e_got_plt + 16)
= (uintptr_t)_dl_runtime_resolve;
*(uintptr_t *)(img->e_base + img->e_got_plt + 8) = (uintptr_t)img;
img->e_entry = (virt_addr_t)img->e_base + img->e_hdr.e_entry;
img->e_status = ELF_IMAGE_LINKED;
return SUCCESS;
}
extern int elf_image_collect_dependencies(
struct elf_image *img,
struct image_list *dest)
{
if (!img->e_nr_links || img->e_links) {
return SUCCESS;
}
int nr_added = 0;
img->e_links = calloc(img->e_nr_links, sizeof(struct elf_image *));
for (size_t i = 0; i < img->e_dyn_count; i++) {
if (img->e_dyn[i].d_tag != DT_NEEDED) {
continue;
}
const char *name = (const char *)img->e_base + img->e_strtab
+ img->e_dyn[i].d_un.d_val;
if (image_list_get(dest, name)) {
continue;
}
struct elf_image *dep = NULL;
int status = create_image_with_name(name, &dep);
if (status != SUCCESS) {
return -status;
}
image_list_put(dest, &dep->e_leaf);
img->e_links[nr_added] = dep;
nr_added++;
}
return nr_added;
}
void elf_image_close(struct elf_image *image)
{
if (image->e_fd) {
close(image->e_fd);
}
free(image);
}
static uint32_t std_hash(const char *name)
{
uint32_t h = 0, g;
for (; *name; name++) {
h = (h << 4) + *name;
if ((g = h & 0xf0000000)) {
h ^= g >> 24;
}
h &= ~g;
}
return h;
}
static uint32_t gnu_hash(const char *name)
{
uint32_t h = 5381;
for (; *name; name++) {
h = (h << 5) + h + *name;
}
return h;
}
static virt_addr_t find_symbol_stdhash(
struct elf_image *img,
const char *name,
uint32_t hash)
{
const uint32_t *hashtab
= (void *)((virt_addr_t)img->e_base + img->e_hash_table);
const char *strtab = (void *)((virt_addr_t)img->e_base + img->e_strtab);
const elf_sym_t *symtab
= (void *)((virt_addr_t)img->e_base + img->e_dynsym);
const uint32_t nbucket = hashtab[0];
const uint32_t nchain = hashtab[1];
const uint32_t *bucket = &hashtab[2];
const uint32_t *chain = &bucket[nbucket];
for (uint32_t i = bucket[hash % nbucket]; i; i = chain[i]) {
if (strcmp(name, strtab + symtab[i].st_name) == 0) {
return img->e_base + symtab[i].st_value;
}
}
return 0;
}
static virt_addr_t find_symbol_gnuhash(
struct elf_image *img,
const char *name,
uint32_t hash)
{
return 0;
}
static virt_addr_t find_symbol_slow(struct elf_image *img, const char *name)
{
return 0;
}
static virt_addr_t find_symbol(
struct elf_image *img,
const char *name,
uint32_t std_hash,
uint32_t gnu_hash)
{
switch (img->e_hash_type) {
case ELF_HASH_STANDARD:
return find_symbol_stdhash(img, name, std_hash);
case ELF_HASH_GNU:
return find_symbol_gnuhash(img, name, gnu_hash);
default:
return find_symbol_slow(img, name);
}
}
virt_addr_t elf_image_find_symbol(struct elf_image *img, const char *name)
{
uint32_t std_hash_val = std_hash(name);
uint32_t gnu_hash_val = gnu_hash(name);
return find_symbol(img, name, std_hash_val, gnu_hash_val);
}
virt_addr_t elf_image_find_linked_symbol(
struct elf_image *img,
const char *name)
{
uint32_t std_hash_val = std_hash(name);
uint32_t gnu_hash_val = gnu_hash(name);
virt_addr_t sym = 0;
for (size_t i = 0; i < img->e_nr_links; i++) {
sym = find_symbol(
img->e_links[i],
name,
std_hash_val,
gnu_hash_val);
if (sym) {
break;
}
}
return sym;
}

Some files were not shown because too many files have changed in this diff Show More