├── libs ├── kernel │ ├── build.mk │ └── wasm │ │ ├── include │ │ ├── assert.h │ │ ├── stdbool.h │ │ ├── stddef.h │ │ ├── stdint.h │ │ ├── string.h │ │ ├── stdatomic.h │ │ └── platform_internal.h │ │ ├── build.mk │ │ └── platform.c ├── user │ ├── riscv32 │ │ ├── build.mk │ │ ├── start.S │ │ ├── arch_syscall.h │ │ ├── user.ld.template │ │ └── arch_mmio.h │ ├── virtio │ │ ├── build.mk │ │ ├── virtio_mmio.h │ │ └── virtio.h │ ├── mmio.h │ ├── task.h │ ├── build.mk │ ├── init.c │ ├── driver.h │ ├── xkcd_rand.h │ ├── task.c │ ├── ipc.h │ ├── dmabuf.h │ ├── malloc.h │ ├── syscall.h │ ├── driver.c │ ├── printf.c │ ├── dmabuf.c │ ├── syscall.c │ └── malloc.c ├── common │ ├── riscv32 │ │ ├── build.mk │ │ └── backtrace.c │ ├── error.h │ ├── vprintf.h │ ├── build.mk │ ├── wasm.h │ ├── ctype.h │ ├── message.c │ ├── symbol_table.S │ ├── string.h │ ├── backtrace.c │ ├── backtrace.h │ ├── ubsan.h │ ├── elf.h │ ├── message.h │ ├── error.c │ ├── ubsan.c │ ├── endian.h │ ├── list.h │ ├── list.c │ └── string.c └── wasm │ ├── build.mk │ ├── string.h │ ├── ipc.h │ └── string.c ├── servers ├── echo │ ├── build.mk │ └── main.c ├── proxy │ ├── build.mk │ └── main.c ├── hello │ ├── build.mk │ └── main.c ├── pong │ ├── build.mk │ └── main.c ├── virtio_blk │ ├── build.mk │ └── virtio_blk.h ├── virtio_net │ ├── build.mk │ └── virtio_net.h ├── wasm_ping │ ├── build.mk │ └── main.c ├── fs │ ├── build.mk │ ├── main.h │ ├── block.h │ ├── fs.h │ └── block.c ├── hello_hinavm │ ├── build.mk │ └── main.c ├── wasm_webapi │ ├── build.mk │ └── main.c ├── shell │ ├── http.h │ ├── build.mk │ ├── fs.h │ ├── command.h │ └── main.c ├── tcpip │ ├── build.mk │ ├── ethernet.h │ ├── main.h │ ├── device.h │ ├── mbuf.h │ ├── ipv4.h │ ├── dns.h │ ├── checksum.h │ ├── udp.h │ ├── arp.h │ ├── ethernet.c │ ├── device.c │ ├── dhcp.h │ ├── ipv4.c │ └── tcp.h ├── vm │ ├── bootfs_image.S │ ├── build.mk │ ├── page_fault.h │ ├── pm.h │ ├── bootfs.h │ ├── task.h │ ├── bootfs.c │ ├── pm.c │ └── page_fault.c └── crack │ ├── build.mk │ └── shellcode.S ├── .gitattributes ├── fs └── hello.txt ├── .gitignore ├── kernel ├── riscv32 │ ├── libgcc.a │ ├── handler.h │ ├── usercopy.h │ ├── switch.h │ ├── debug.h │ ├── asmdefs.h │ ├── mp.h │ ├── build.mk │ ├── vm.h │ ├── boot.S │ ├── uart.h │ ├── uart.c │ ├── plic.h │ ├── plic.c │ ├── kernel.ld.template │ ├── usercopy.S │ ├── trap.h │ ├── debug.c │ ├── switch.S │ ├── include │ │ └── arch_types.h │ └── mp.c ├── printk.h ├── bootelf.S ├── main.h ├── build.mk ├── ipc.h ├── syscall.h ├── interrupt.h ├── hinavm.h ├── wasmvm.h ├── memory.h ├── arch.h ├── task.h ├── printk.c ├── interrupt.c └── main.c ├── tools ├── requirements.txt ├── generate_program_name.py ├── print_build_info.py ├── update_file_if_changed.py ├── merge_compile_commands_json.py ├── generate_gdbinit.py ├── generate_user_ld_params.py ├── mkbootfs.py ├── embed_symbols.py ├── clang-format-align-trailing-comments-kludge.patch └── coreutils.py ├── .gitmodules ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .editorconfig ├── .github └── workflows │ ├── auto-approve.yml │ └── ci.yml ├── mk ├── wasm.mk ├── lib.mk └── executable.mk ├── LICENSE.md ├── .clang-format ├── README.md ├── tests ├── test_commands.py └── conftest.py ├── DEBUG.md └── messages.idl /libs/kernel/build.mk: -------------------------------------------------------------------------------- 1 | subdirs-y += wasm -------------------------------------------------------------------------------- /servers/echo/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.o -------------------------------------------------------------------------------- /servers/proxy/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.o -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=C 2 | -------------------------------------------------------------------------------- /fs/hello.txt: -------------------------------------------------------------------------------- 1 | Hello World from HinaFS! 2 | -------------------------------------------------------------------------------- /libs/kernel/wasm/include/assert.h: -------------------------------------------------------------------------------- 1 | // dummy -------------------------------------------------------------------------------- /libs/kernel/wasm/include/stdbool.h: -------------------------------------------------------------------------------- 1 | // dummy -------------------------------------------------------------------------------- /libs/kernel/wasm/include/stddef.h: -------------------------------------------------------------------------------- 1 | // dummy -------------------------------------------------------------------------------- /libs/kernel/wasm/include/stdint.h: -------------------------------------------------------------------------------- 1 | // dummy -------------------------------------------------------------------------------- /libs/kernel/wasm/include/string.h: -------------------------------------------------------------------------------- 1 | // dummy -------------------------------------------------------------------------------- /servers/hello/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.o 2 | -------------------------------------------------------------------------------- /servers/pong/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.o 2 | -------------------------------------------------------------------------------- /libs/kernel/wasm/include/stdatomic.h: -------------------------------------------------------------------------------- 1 | // dummy -------------------------------------------------------------------------------- /libs/user/riscv32/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += start.o 2 | -------------------------------------------------------------------------------- /servers/virtio_blk/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.o 2 | -------------------------------------------------------------------------------- /servers/virtio_net/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.o 2 | -------------------------------------------------------------------------------- /servers/wasm_ping/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.wasm.o -------------------------------------------------------------------------------- /libs/user/virtio/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += virtio_mmio.o 2 | -------------------------------------------------------------------------------- /servers/fs/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.o block.o fs.o 2 | -------------------------------------------------------------------------------- /servers/hello_hinavm/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.o 2 | -------------------------------------------------------------------------------- /servers/wasm_webapi/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.wasm.o -------------------------------------------------------------------------------- /libs/common/riscv32/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += backtrace.o 2 | -------------------------------------------------------------------------------- /libs/user/mmio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | -------------------------------------------------------------------------------- /libs/common/error.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | const char *err2str(int err); 3 | -------------------------------------------------------------------------------- /libs/wasm/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += string.wasm.o 2 | 3 | $(output): LD := $(WASMLD) -------------------------------------------------------------------------------- /servers/shell/http.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void http_get(const char *url); 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.vscode 3 | /.gdb_history 4 | *.log 5 | *.img 6 | *.pcap 7 | *.pyc 8 | -------------------------------------------------------------------------------- /kernel/riscv32/libgcc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r1ru/WasmOS/HEAD/kernel/riscv32/libgcc.a -------------------------------------------------------------------------------- /tools/requirements.txt: -------------------------------------------------------------------------------- 1 | jinja2 2 | lark 3 | colorama 4 | pytest 5 | pytest-flakefinder 6 | -------------------------------------------------------------------------------- /libs/user/task.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | task_t task_self(void); 5 | -------------------------------------------------------------------------------- /servers/shell/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.o command.o fs.o http.o 2 | cflags-y += -D'AUTORUN="$(AUTORUN)"' 3 | -------------------------------------------------------------------------------- /servers/tcpip/build.mk: -------------------------------------------------------------------------------- 1 | objs-y := main.o mbuf.o device.o ethernet.o arp.o ipv4.o tcp.o udp.o dhcp.o dns.o 2 | -------------------------------------------------------------------------------- /servers/hello/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void main(void) { 4 | INFO("Hello World!"); 5 | } 6 | -------------------------------------------------------------------------------- /kernel/riscv32/handler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void riscv32_trap_handler(void); 4 | void riscv32_timer_handler(void); 5 | -------------------------------------------------------------------------------- /kernel/riscv32/usercopy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern char riscv32_usercopy1[]; 4 | extern char riscv32_usercopy2[]; 5 | -------------------------------------------------------------------------------- /servers/vm/bootfs_image.S: -------------------------------------------------------------------------------- 1 | // BootFSを埋め込むためのファイル 2 | .section .rodata 3 | .global __bootfs 4 | __bootfs: 5 | .incbin BOOTFS_PATH 6 | -------------------------------------------------------------------------------- /kernel/printk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | void handle_serial_interrupt(void); 5 | int serial_read(char *buf, int max_len); 6 | -------------------------------------------------------------------------------- /kernel/bootelf.S: -------------------------------------------------------------------------------- 1 | // 最初のユーザータスクのELFイメージを、カーネルに埋め込むためのファイル。 2 | // .boot_elfセクションに埋め込まれ、リンカースクリプトでどこに配置するかを指定している。 3 | .section .boot_elf 4 | .incbin BOOT_ELF_PATH 5 | -------------------------------------------------------------------------------- /libs/common/vprintf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | extern void printchar(char ch); 5 | void vprintf(const char *fmt, va_list vargs); 6 | -------------------------------------------------------------------------------- /libs/wasm/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | size_t strlen(const char *s); 6 | void *memcpy(void *dst, const void *src, size_t len); -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/wasm-micro-runtime"] 2 | path = third_party/wasm-micro-runtime 3 | url = https://github.com/bytecodealliance/wasm-micro-runtime 4 | -------------------------------------------------------------------------------- /libs/common/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += list.o vprintf.o backtrace.o symbol_table.o string.o ubsan.o error.o message.o 2 | subdirs-y += $(ARCH) 3 | cflags-y += -I$(dir)/include 4 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/vscode/devcontainers/base:ubuntu 2 | RUN apt-get update \ 3 | && apt-get install -y llvm clang lld python3-pip qemu-system gdb-multiarch -------------------------------------------------------------------------------- /libs/user/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += printf.o syscall.o malloc.o init.o ipc.o task.o driver.o dmabuf.o 2 | subdirs-y += $(ARCH) virtio 3 | global-cflags-y += -I$(top_dir)/libs/user/arch/$(ARCH) 4 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WASMOS Development", 3 | "build": { 4 | "dockerfile": "Dockerfile" 5 | }, 6 | "postCreateCommand": "pip3 install --user -r tools/requirements.txt" 7 | } -------------------------------------------------------------------------------- /kernel/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "arch.h" 3 | 4 | void kernel_main(struct bootinfo *bootinfo); 5 | void kernel_mp_main(void); 6 | 7 | // カーネルイメージに埋め込まれた最初のユーザータスク (VMサーバ) のELFイメージ 8 | extern char __boot_elf[]; 9 | -------------------------------------------------------------------------------- /libs/user/init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // userライブラリの初期化を行う。main関数の前に呼び出される。 6 | void hinaos_init(void) { 7 | malloc_init(); 8 | } 9 | -------------------------------------------------------------------------------- /servers/vm/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.o task.o bootfs.o pm.o page_fault.o bootfs_image.o 2 | cflags-y += -DBOOTFS_PATH='"$(bootfs_bin)"' -DBOOT_SERVERS='"$(BOOT_SERVERS)"' 3 | 4 | $(build_dir)/bootfs_image.o: $(bootfs_bin) 5 | -------------------------------------------------------------------------------- /servers/vm/page_fault.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct task; 5 | 6 | error_t handle_page_fault(struct task *task, uaddr_t vaddr, uaddr_t ip, 7 | unsigned fault); 8 | -------------------------------------------------------------------------------- /kernel/riscv32/switch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | void riscv32_task_switch(uint32_t *prev_sp, uint32_t *next_sp); 5 | void riscv32_kernel_entry_trampoline(void); 6 | void riscv32_user_entry_trampoline(void); 7 | -------------------------------------------------------------------------------- /libs/common/wasm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WASM_MAGIC "\00asm" 4 | #define WASM_VERSION 1 5 | 6 | struct wasm_hdr { 7 | uint32_t magic; 8 | uint32_t version; 9 | } __packed; 10 | 11 | typedef struct wasm_hdr wasm_hdr; -------------------------------------------------------------------------------- /kernel/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.o printk.o memory.o task.o interrupt.o ipc.o syscall.o bootelf.o \ 2 | hinavm.o wasmvm.o 3 | 4 | cflags-y += -Ilibs/kernel/wasm/include 5 | libs-y += kernel 6 | subdirs-y += riscv32 7 | 8 | $(build_dir)/bootelf.o: $(boot_elf) -------------------------------------------------------------------------------- /kernel/ipc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct task; 5 | struct message; 6 | error_t ipc(struct task *dst, task_t src, __user struct message *m, 7 | unsigned flags); 8 | void notify(struct task *dst, notifications_t notifications); 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | 8 | [{*.c,*.h,*.S,*.py,*.md}] 9 | indent_style = space 10 | indent_size = 4 11 | 12 | [{Makefile,*.mk}] 13 | indent_style = tab 14 | indent_size = 8 15 | -------------------------------------------------------------------------------- /servers/shell/fs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | void fs_read(const char *path); 5 | void fs_write(const char *path, const uint8_t *buf, size_t len); 6 | void fs_listdir(const char *path); 7 | void fs_mkdir(const char *path); 8 | void fs_delete(const char *path); 9 | -------------------------------------------------------------------------------- /kernel/syscall.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | error_t memcpy_from_user(void *dst, __user const void *src, size_t len); 5 | error_t memcpy_to_user(__user void *dst, const void *src, size_t len); 6 | long handle_syscall(long a0, long a1, long a2, long a3, long a4, long n); 7 | -------------------------------------------------------------------------------- /libs/user/driver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | error_t driver_map_pages(paddr_t paddr, size_t size, int map_flags, 5 | uaddr_t *uaddr); 6 | error_t driver_alloc_pages(size_t size, int map_flags, uaddr_t *uaddr, 7 | paddr_t *paddr); 8 | -------------------------------------------------------------------------------- /servers/vm/pm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "task.h" 3 | 4 | error_t alloc_pages(struct task *task, size_t size, int alloc_flags, 5 | int map_flags, paddr_t *paddr, uaddr_t *uaddr); 6 | error_t map_pages(struct task *task, size_t size, int map_flags, paddr_t paddr, 7 | uaddr_t *uaddr); 8 | -------------------------------------------------------------------------------- /libs/common/ctype.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // アルファベットを大文字に変換する 5 | static inline int toupper(int ch) { 6 | return ('a' <= ch && ch <= 'z') ? ch - 0x20 : ch; 7 | } 8 | 9 | // 文字が数字かどうかを判定する 10 | static inline bool isdigit(int ch) { 11 | return '0' <= ch && ch <= '9'; 12 | } 13 | -------------------------------------------------------------------------------- /kernel/riscv32/debug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // スタックカナリー (stack canary) の値。この値が書き換えられていたらスタックオーバーフロー 5 | // が発生していると判断する。 6 | #define STACK_CANARY_VALUE 0xdeadca71 7 | 8 | void stack_check(void); 9 | void stack_set_canary(uint32_t sp_bottom); 10 | void stack_reset_current_canary(void); 11 | -------------------------------------------------------------------------------- /servers/shell/command.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define ARGC_MAX 32 4 | 5 | struct args { 6 | char *argv[ARGC_MAX]; 7 | int argc; 8 | }; 9 | 10 | struct command { 11 | const char *name; 12 | const char *help; 13 | void (*run)(struct args *args); 14 | }; 15 | 16 | void run_command(struct args *args); 17 | -------------------------------------------------------------------------------- /kernel/interrupt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | extern unsigned uptime_ticks; 5 | 6 | struct task; 7 | error_t irq_listen(struct task *task, unsigned irq); 8 | error_t irq_unlisten(struct task *task, unsigned irq); 9 | void handle_interrupt(unsigned irq); 10 | void handle_timer_interrupt(unsigned ticks); 11 | -------------------------------------------------------------------------------- /libs/user/xkcd_rand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 同様に確からしいことが保証された公正なサイコロを振って計算された真の乱数を返す、 4 | // RFC 1149.5に準拠した乱数生成関数。 5 | // 6 | // 定数を返すだけの単純な関数と同程度に高速で、賢いコンパイラであれば最適化によって 7 | // コンパイル時に乱数を決定できる。 8 | // 9 | // 乱数シードの設定は不要。 10 | // 11 | // アルゴリズムの解説: https://xkcd.com/221/ 12 | static inline int xkcd_rand(void) { 13 | return 4; 14 | } 15 | -------------------------------------------------------------------------------- /kernel/hinavm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define HINAVM_INSTS_MAX 128 5 | 6 | // HinaVMを起動するために必要な情報 (HinaVMプログラム) 7 | struct hinavm { 8 | hinavm_inst_t insts[HINAVM_INSTS_MAX]; // 命令列 9 | uint32_t num_insts; // 命令数 10 | }; 11 | 12 | __noreturn void hinavm_run(struct hinavm *hinavm); 13 | -------------------------------------------------------------------------------- /libs/common/message.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // IPCスタブジェネレータが生成するコンパイル時チェック 4 | IPCSTUB_STATIC_ASSERTIONS 5 | 6 | // メッセージの種類に対応する名前を返す。デバッグ用。 7 | const char *msgtype2str(int type) { 8 | if (type == 0 || type > IPCSTUB_MSGID_MAX) { 9 | return "(invalid)"; 10 | } 11 | 12 | return IPCSTUB_MSGID2STR[type]; 13 | } 14 | -------------------------------------------------------------------------------- /servers/wasm_ping/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __attribute__((export_name("main"))) 4 | int main(void) { 5 | task_t server = ipc_lookup("pong"); 6 | 7 | struct message m; 8 | m.type = PING_MSG; 9 | m.ping.value = 123; 10 | 11 | if (IS_ERROR(ipc_call(server, &m))) { 12 | return -1; 13 | } 14 | 15 | return 0; 16 | } -------------------------------------------------------------------------------- /libs/wasm/ipc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // host functions exported to WASM (defined in kernel/wasmvm.c) 7 | void ipc_reply(task_t dst, struct message *m); 8 | error_t ipc_recv(task_t src, struct message *m); 9 | error_t ipc_call(task_t dst, struct message *m); 10 | task_t ipc_lookup(const char *name); -------------------------------------------------------------------------------- /libs/user/task.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // 実行中タスクのタスクIDを取得する。 5 | task_t task_self(void) { 6 | static task_t tid = 0; 7 | if (tid) { 8 | // 既にタスクIDを取得しているので、キャッシュした値を返す。 9 | return tid; 10 | } 11 | 12 | // タスクIDは一度取得したら変わらないので、キャッシュしておく。 13 | tid = sys_task_self(); 14 | return tid; 15 | } 16 | -------------------------------------------------------------------------------- /libs/common/symbol_table.S: -------------------------------------------------------------------------------- 1 | // シンボルテーブルを埋め込むためのメモリ領域 2 | // tools/embed_symbols.py を使ってビルド時に上書きされる 3 | .section .symbols 4 | 5 | .align 4 6 | .global __symbol_table, __symbol_table_end 7 | __symbol_table: 8 | .ascii "__SYMBOL_TABLE_START__" // シンボルテーブルの開始を示すマーカー 9 | .space 256 * 1024 // 256KBあれば十分なはず 10 | .ascii "__SYMBOL_TABLE_END__" // シンボルテーブルの終了を示すマーカー 11 | __symbol_table_end: 12 | -------------------------------------------------------------------------------- /libs/user/riscv32/start.S: -------------------------------------------------------------------------------- 1 | // ユーザーモードのエントリーポイント 2 | .align 4 3 | .global start 4 | start: 5 | mv fp, zero // フレームポインタをゼロに初期化することで、スタックトレースがここで 6 | // 停止するようにする 7 | la sp, __stack // スタックポインタをスタックの最上位に設定する 8 | 9 | jal hinaos_init // userライブラリの初期化 10 | jal main // ユーザープログラムのエントリーポイント (main関数) 11 | 12 | jal sys_task_exit // main関数から戻ってきたらタスクを終了する 13 | -------------------------------------------------------------------------------- /kernel/riscv32/asmdefs.h: -------------------------------------------------------------------------------- 1 | // アセンブリファイルで使用する定義 2 | // 3 | // #defineや#ifdefといったプリプロセッサの命令 (#から始まるもの) しか使えないので注意。 4 | // 構造体宣言のようなC言語特有のものはコンパイルエラーになる。 5 | #pragma once 6 | 7 | // struct arch_cpuvarのメンバ変数のオフセット 8 | #define CPUVAR_SSCRATCH 0 9 | #define CPUVAR_SP_TOP 4 10 | #define CPUVAR_MSCRATCH0 8 11 | #define CPUVAR_MSCRATCH1 12 12 | #define CPUVAR_MTIMECMP 16 13 | #define CPUVAR_MTIME 20 14 | #define CPUVAR_INTERVAL 24 15 | -------------------------------------------------------------------------------- /kernel/wasmvm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define WASMVM_CODE_SIZE_MAX 4096 7 | #define WASMVM_STACK_SIZE 4096 8 | #define WASMVM_HEAP_SIZE 4096 9 | 10 | struct wasmvm { 11 | uint8_t code[WASMVM_CODE_SIZE_MAX]; 12 | uint32_t size; 13 | }; 14 | 15 | // entry point of WASMVM task 16 | __noreturn void wasmvm_run(struct wasmvm *wasmvm); -------------------------------------------------------------------------------- /.github/workflows/auto-approve.yml: -------------------------------------------------------------------------------- 1 | name: Auto approve 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, ready_for_review] 6 | 7 | jobs: 8 | auto-approve: 9 | if: | 10 | github.event.pull_request.user.login == github.repository_owner 11 | && ! github.event.pull_request.draft 12 | runs-on: ubuntu-latest 13 | permissions: 14 | pull-requests: write 15 | steps: 16 | - uses: hmarr/auto-approve-action@v4 -------------------------------------------------------------------------------- /servers/fs/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define WRITE_BACK_INTERVAL 1000 5 | #define OPEN_FILES_MAX 64 6 | 7 | // 開いているファイルの情報 8 | struct open_file { 9 | bool used; // この管理構造体を利用中か 10 | task_t task; // このファイルを開いているタスク 11 | struct hinafs_entry *entry; // ファイルのエントリ 12 | struct block *entry_block; // ファイルのエントリがあるブロック 13 | uint32_t offset; // 現在のオフセット (読み書き操作をすると動く) 14 | }; 15 | -------------------------------------------------------------------------------- /libs/wasm/string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | // todo: replace with wasi-libc? 4 | size_t strlen(const char *s) { 5 | size_t len = 0; 6 | while (*s != '\0') { 7 | len++; 8 | s++; 9 | } 10 | return len; 11 | } 12 | 13 | void *memcpy(void *dst, const void *src, size_t len) { 14 | uint8_t *d = dst; 15 | const uint8_t *s = src; 16 | while (len-- > 0) { 17 | *d = *s; 18 | d++; 19 | s++; 20 | } 21 | return dst; 22 | } -------------------------------------------------------------------------------- /servers/crack/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += main.o shellcode.bin.o 2 | 3 | $(BUILD_DIR)/servers/crack/main.o: $(BUILD_DIR)/servers/crack/shellcode.bin.o 4 | 5 | $(BUILD_DIR)/servers/crack/shellcode.bin.o: servers/crack/shellcode.S 6 | $(MKDIR) -p $(@D) 7 | $(CC) --target=riscv32 -c -o $@.tmp1 $^ 8 | $(OBJCOPY) -Obinary $@.tmp1 $@.tmp2 9 | cd $(BUILD_DIR)/servers/crack && \ 10 | $(CP) shellcode.bin.o.tmp2 shellcode.bin && \ 11 | $(OBJCOPY) -Ibinary -Oelf32-littleriscv shellcode.bin shellcode.bin.o 12 | -------------------------------------------------------------------------------- /kernel/riscv32/mp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define BKL_LOCKED 0x12ab // カーネルロックをいずれかのCPUが使用中 5 | #define BKL_UNLOCKED 0xc0be // カーネルロックを誰も使用していない 6 | #define BKL_HALTED 0xdead // システムが停止した状態 (ロックを取らず停止する) 7 | 8 | int mp_self(void); 9 | void mp_lock(void); 10 | void mp_force_lock(void); 11 | void mp_unlock(void); 12 | struct cpuvar *riscv32_cpuvar_of(int hartid); 13 | void mp_send_ipi(void); 14 | __noreturn void halt(void); 15 | void riscv32_mp_init_percpu(void); 16 | -------------------------------------------------------------------------------- /tools/generate_program_name.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | 4 | def main(): 5 | parser = argparse.ArgumentParser() 6 | parser.add_argument( 7 | "-o", 8 | metavar="OUTFILE", 9 | dest="out_file", 10 | required=True, 11 | help="The output file path.", 12 | ) 13 | parser.add_argument("name") 14 | args = parser.parse_args() 15 | 16 | text = 'const char *__program_name(void) { return "' + args.name + '"; }' 17 | with open(args.out_file, "w") as f: 18 | f.write(text) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /servers/vm/bootfs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // BootFSファイルシステムヘッダ 5 | struct bootfs_header { 6 | uint16_t version; 7 | uint16_t header_size; 8 | uint16_t num_files; 9 | uint16_t padding; 10 | } __packed; 11 | 12 | // BootFSファイルエントリ 13 | struct bootfs_file { 14 | char name[56]; 15 | uint32_t offset; 16 | uint32_t len; 17 | } __packed; 18 | 19 | struct bootfs_file *bootfs_open(const char *path); 20 | struct bootfs_file *bootfs_open_iter(unsigned index); 21 | void bootfs_read(struct bootfs_file *file, offset_t off, void *buf, size_t len); 22 | void bootfs_init(void); 23 | -------------------------------------------------------------------------------- /libs/user/ipc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | error_t ipc_send(task_t dst, struct message *m); 6 | error_t ipc_send_noblock(task_t dst, struct message *m); 7 | error_t ipc_send_async(task_t dst, struct message *m); 8 | void ipc_reply(task_t dst, struct message *m); 9 | void ipc_reply_err(task_t dst, error_t error); 10 | error_t ipc_recv(task_t src, struct message *m); 11 | error_t ipc_call(task_t dst, struct message *m); 12 | error_t ipc_notify(task_t dst, notifications_t notifications); 13 | error_t ipc_register(const char *name); 14 | task_t ipc_lookup(const char *name); 15 | -------------------------------------------------------------------------------- /libs/common/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | int memcmp(const void *p1, const void *p2, size_t len); 5 | void *memset(void *dst, int ch, size_t len); 6 | void *memcpy(void *dst, const void *src, size_t len); 7 | void *memmove(void *dst, const void *src, size_t len); 8 | size_t strlen(const char *s); 9 | int strcmp(const char *s1, const char *s2); 10 | int strncmp(const char *s1, const char *s2, size_t len); 11 | char *strcpy_safe(char *dst, size_t dst_len, const char *src); 12 | char *strchr(const char *str, int c); 13 | char *strstr(const char *haystack, const char *needle); 14 | int atoi(const char *s); 15 | -------------------------------------------------------------------------------- /libs/common/backtrace.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // シンボルテーブルの中から指定されたアドレスに最も近いシンボルを探す。 5 | struct symbol *find_symbol(vaddr_t addr) { 6 | ASSERT(__symbol_table.magic == SYMBOL_TABLE_MAGIC); 7 | 8 | // 二分探索でシンボルを探す。 9 | int32_t l = -1; 10 | int32_t r = __symbol_table.num_symbols; 11 | while (r - l > 1) { 12 | int32_t mid = (l + r) / 2; 13 | if (addr >= __symbol_table.symbols[mid].addr) { 14 | l = mid; 15 | } else { 16 | r = mid; 17 | } 18 | } 19 | 20 | return (l < 0) ? NULL : &__symbol_table.symbols[l]; 21 | } 22 | -------------------------------------------------------------------------------- /kernel/riscv32/build.mk: -------------------------------------------------------------------------------- 1 | objs-y += boot.o setup.o task.o vm.o mp.o switch.o handler.o trap.o usercopy.o debug.o uart.o plic.o libgcc.a 2 | 3 | # To build WAMR, you will need libgcc.a, so use the one from the riscv-gnu-toolchain(https://github.com/riscv-collab/riscv-gnu-toolchain). 4 | # The pre-built version is available at kernel/riscv32/libgcc.a. 5 | # If you want to build it from the source code, please follow the instructions in the README. 6 | # Use the following command for configuration. 7 | # $ ./configure --prefix=/opt/riscv32 --with-arch=rv32imac --with-abi=ilp32 --with-target-cflags=-mno-relax 8 | 9 | $(build_dir)/libgcc.a: $(dir)/libgcc.a 10 | $(PROGRESS) CP $@ 11 | $(CP) $< $@ 12 | -------------------------------------------------------------------------------- /tools/print_build_info.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | 4 | def main(): 5 | parser = argparse.ArgumentParser() 6 | parser.add_argument("--kernel-elf", required=True) 7 | parser.add_argument("--bootfs-bin", required=True) 8 | parser.add_argument("--hinafs-img", required=True) 9 | args = parser.parse_args() 10 | 11 | print() 12 | print(f"Kernel Executable: {args.kernel_elf} ({os.stat(args.kernel_elf).st_size // 1024} KiB)") 13 | print(f"BootFS Image: {args.bootfs_bin} ({os.stat(args.bootfs_bin).st_size // 1024} KiB)") 14 | print(f"HinaFS Image: {args.hinafs_img} ({os.stat(args.hinafs_img).st_size // 1024} KiB)") 15 | 16 | if __name__ == "__main__": 17 | main() 18 | -------------------------------------------------------------------------------- /libs/user/dmabuf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // DMAバッファ管理構造体。[paddr, paddr + entry_size * num_entries) の範囲が 5 | // この管理構造体が所持する物理メモリ領域になる。 6 | struct dmabuf { 7 | paddr_t paddr; // DMAバッファ領域の物理アドレス 8 | uaddr_t uaddr; // DMAバッファ領域の仮想アドレス 9 | size_t entry_size; // 1つのバッファのサイズ 10 | size_t num_entries; // バッファの数 11 | bool *used; // バッファの使用状況 12 | }; 13 | 14 | // dmabuf_t: DMAバッファ管理構造体へのポインタ。 15 | typedef struct dmabuf *dmabuf_t; 16 | 17 | dmabuf_t dmabuf_create(size_t entry_size, size_t num_entries); 18 | void *dmabuf_alloc(dmabuf_t dmabuf, paddr_t *paddr); 19 | void *dmabuf_p2v(dmabuf_t dmabuf, paddr_t paddr); 20 | void dmabuf_free(dmabuf_t dmabuf, paddr_t paddr); 21 | -------------------------------------------------------------------------------- /servers/tcpip/ethernet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ipv4.h" 3 | #include "mbuf.h" 4 | 5 | // ブロードキャストアドレス 6 | #define MACADDR_BROADCAST ((macaddr_t){0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) 7 | 8 | // MACアドレスを表す型 9 | #define MACADDR_LEN 6 10 | typedef uint8_t macaddr_t[MACADDR_LEN]; 11 | 12 | // イーサネットフレームの種類 13 | enum ether_type { 14 | ETHER_TYPE_IPV4 = 0x0800, 15 | ETHER_TYPE_ARP = 0x0806, 16 | }; 17 | 18 | // イーサーネットフレームヘッダ 19 | struct ethernet_header { 20 | macaddr_t dst; // 宛先MACアドレス 21 | macaddr_t src; // 送信元MACアドレス 22 | uint16_t type; // ペイロードの種類 23 | } __packed; 24 | 25 | void ethernet_transmit(enum ether_type type, ipv4addr_t dst, mbuf_t payload); 26 | void ethernet_receive(const void *pkt, size_t len); 27 | -------------------------------------------------------------------------------- /servers/tcpip/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "device.h" 3 | #include "tcp.h" 4 | #include 5 | #include 6 | #include 7 | 8 | #define TIMER_INTERVAL 100 9 | #define SOCKETS_MAX 256 10 | 11 | // ソケット管理構造体 12 | struct socket { 13 | bool used; // 使用中か 14 | task_t task; // 所有するタスク 15 | int fd; // ソケットID 16 | struct tcp_pcb *tcp_pcb; // TCPコントロールブロック 17 | }; 18 | 19 | void callback_ethernet_transmit(mbuf_t pkt); 20 | void callback_tcp_data(struct tcp_pcb *sock); 21 | void callback_tcp_rst(struct tcp_pcb *sock); 22 | void callback_tcp_closed(struct tcp_pcb *sock); 23 | void callback_dns_got_answer(ipv4addr_t addr, void *arg); 24 | -------------------------------------------------------------------------------- /libs/common/backtrace.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define BACKTRACE_MAX 16 // バックトレースの深さの最大値 5 | #define SYMBOL_TABLE_MAGIC 0x4c4d5953 // "SYML" 6 | 7 | // シンボルテーブルの各エントリ。 8 | struct symbol { 9 | uint32_t addr; // シンボルのアドレス 10 | char name[60]; // シンボル名 11 | } __packed; 12 | 13 | // シンボルテーブル。 14 | struct symbol_table { 15 | uint32_t magic; // SYMBOL_TABLE_MAGIC 16 | uint32_t num_symbols; // シンボルの数 17 | uint64_t padding; // パディング 18 | struct symbol symbols[]; // シンボルの配列。アドレス順にソートされている。 19 | } __packed; 20 | 21 | // ELFファイルに埋め込まれたシンボルテーブル。 22 | extern struct symbol_table __symbol_table; 23 | 24 | struct symbol *find_symbol(vaddr_t addr); 25 | void backtrace(void); 26 | -------------------------------------------------------------------------------- /servers/fs/block.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // ブロックのサイズ (バイト) 7 | #define BLOCK_SIZE 4096 8 | 9 | // ブロック番号 10 | typedef uint16_t block_t; 11 | 12 | // ブロックキャッシュ 13 | // 14 | // ストレージデバイスの内容を読み書きする際には、まずデバイスからBLOCK_SIZE分のデータを一気に 15 | // 読み出してブロックキャッシュとして追加し、ファイルシステム実装はメモリ上にあるキャッシュデータ 16 | // を読み書きする。 17 | struct block { 18 | block_t index; // ディスク上のブロック番号 19 | list_elem_t cache_next; // ブロックキャッシュのリストの要素 20 | list_elem_t dirty_next; // 変更済みブロックキャッシュのリストの要素 21 | uint8_t data[BLOCK_SIZE]; // ブロックの内容 22 | }; 23 | 24 | error_t block_read(block_t index, struct block **block); 25 | void block_mark_as_dirty(struct block *block); 26 | void block_flush_all(void); 27 | void block_init(void); 28 | -------------------------------------------------------------------------------- /kernel/riscv32/vm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define PTE_PADDR_MASK 0xfffffc00 5 | #define PTE_INDEX(level, vaddr) (((vaddr) >> (12 + (level) *10)) & 0x3ff) 6 | #define PTE_PADDR(pte) (((pte) >> 10) << 12) 7 | 8 | #define PTE_V (1 << 0) 9 | #define PTE_R (1 << 1) 10 | #define PTE_W (1 << 2) 11 | #define PTE_X (1 << 3) 12 | #define PTE_U (1 << 4) 13 | 14 | typedef uint32_t pte_t; 15 | 16 | extern char __text[]; 17 | extern char __text_end[]; 18 | extern char __data[]; 19 | extern char __data_end[]; 20 | extern char __bss[]; 21 | extern char __bss_end[]; 22 | extern char __ram_start[]; 23 | extern char __free_ram_start[]; 24 | extern char __boot_elf[]; 25 | 26 | bool riscv32_is_mapped(uint32_t satp, vaddr_t vaddr); 27 | void riscv32_vm_init(void); 28 | -------------------------------------------------------------------------------- /servers/crack/shellcode.S: -------------------------------------------------------------------------------- 1 | .text 2 | 3 | // 文字を出力するマクロ。引数に文字を指定する。 4 | .macro putc c 5 | li a1, \c 6 | sw a1, 0(a0) 7 | .endm 8 | 9 | .global shellcode 10 | shellcode: 11 | li a0, 0x10000000 // シリアルポートのアドレス。ユーザーランドからはアクセスできない。 12 | putc '\n' // シリアルポートに文字列「*** exploited! ***」を出力する。 13 | putc '\n' 14 | putc '*' 15 | putc '*' 16 | putc '*' 17 | putc ' ' 18 | putc 'e' 19 | putc 'x' 20 | putc 'p' 21 | putc 'l' 22 | putc 'o' 23 | putc 'i' 24 | putc 't' 25 | putc 'e' 26 | putc 'd' 27 | putc '!' 28 | putc ' ' 29 | putc '*' 30 | putc '*' 31 | putc '*' 32 | putc '\n' 33 | putc '\n' 34 | 1: // 無限ループ 35 | j 1b 36 | 37 | // ここには到達しない。書きかわったことを確認するための文字列。 38 | .ascii "\\\\\\ OVERWRITTEN BY CRACK PROGRAM //////" 39 | -------------------------------------------------------------------------------- /servers/tcpip/device.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "arp.h" 3 | #include "ethernet.h" 4 | #include "ipv4.h" 5 | #include "mbuf.h" 6 | 7 | // デバイス管理構造体 8 | struct device { 9 | bool initialized; // デバイスが初期化されたかどうか 10 | bool dhcp_enabled; // DHCPが有効化されているかどうか 11 | macaddr_t macaddr; // MACアドレス 12 | ipv4addr_t ipaddr; // IPアドレス 13 | ipv4addr_t gateway; // デフォルトゲートウェイ 14 | ipv4addr_t netmask; // ネットマスク 15 | }; 16 | 17 | bool device_dst_is_ours(ipv4addr_t dst); 18 | ipv4addr_t device_get_next_hop(ipv4addr_t dst); 19 | macaddr_t *device_get_macaddr(void); 20 | ipv4addr_t device_get_ipaddr(void); 21 | void device_set_ip_addrs(ipv4addr_t ipaddr, ipv4addr_t netmask, 22 | ipv4addr_t gateway); 23 | bool device_ready(void); 24 | void device_enable_dhcp(void); 25 | void device_init(macaddr_t *macaddr); 26 | -------------------------------------------------------------------------------- /tools/update_file_if_changed.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | GNU makeではファイルの内容が変わっていなくても更新日時が変わってしまうと、 4 | makeが再ビルドが必要と判断してしまう。 5 | 6 | このスクリプトを使うことで、ファイルの内容が変わっていない場合には、 7 | 更新日時を変更しないようにすることができる。 8 | 9 | """ 10 | 11 | import argparse 12 | import os 13 | 14 | def main(): 15 | parser = argparse.ArgumentParser() 16 | parser.add_argument("outfile") 17 | parser.add_argument("text") 18 | args = parser.parse_args() 19 | 20 | try: 21 | with open(args.outfile, "r") as f: 22 | if f.read() == args.text: 23 | return 24 | except IOError: 25 | pass 26 | 27 | if os.path.exists(args.outfile): 28 | print("update detected in build settings; rebuilding from scratch...") 29 | with open(args.outfile, "w") as f: 30 | f.write(args.text) 31 | 32 | if __name__ == "__main__": 33 | main() 34 | -------------------------------------------------------------------------------- /kernel/riscv32/boot.S: -------------------------------------------------------------------------------- 1 | #include 2 | .section ".boot", "ax" 3 | 4 | // カーネルのエントリーポイント。ブートローダーからジャンプされ、ここから実行が開始される。 5 | .global boot 6 | boot: 7 | // スタックトレースがここで止まるようにするため、raとfpをゼロクリアする。 8 | mv ra, zero 9 | mv fp, zero 10 | 11 | // スタックポインタを設定する。CPUごとに独自のブートスタックを用意している。 12 | // 13 | // 開始アドレス: __boot_stack + KERNEL_STACK_SIZE * CPU番号 14 | // 終了アドレス: __boot_stack + KERNEL_STACK_SIZE * (CPU番号 + 1) 15 | // 16 | // スタックは下向きに伸びていくので、終了アドレスをスタックポインタに設定する。 17 | csrr a0, mhartid 18 | li a1, KERNEL_STACK_SIZE 19 | addi a0, a0, 1 // a0 = CPU番号 + 1 20 | mul a0, a0, a1 // a0 = (CPU番号 + 1) * KERNEL_STACK_SIZE 21 | la sp, __boot_stack 22 | add sp, sp, a0 // sp = __boot_stack + a0 23 | 24 | // C言語のプログラムを実行する準備 (スタック) ができたので、早速処理を移行する。 25 | jal riscv32_boot 26 | -------------------------------------------------------------------------------- /servers/pong/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void main(void) { 6 | // pongサーバとして登録する 7 | ASSERT_OK(ipc_register("pong")); 8 | TRACE("ready"); 9 | 10 | // メインループ 11 | while (true) { 12 | struct message m; 13 | ASSERT_OK(ipc_recv(IPC_ANY, &m)); 14 | switch (m.type) { 15 | case PING_MSG: { 16 | DBG("received ping message from #%d (value=%d)", m.src, 17 | m.ping.value); 18 | 19 | m.type = PING_REPLY_MSG; 20 | m.ping_reply.value = 42; 21 | ipc_reply(m.src, &m); 22 | break; 23 | } 24 | default: 25 | WARN("unhandled message: %s (%x)", msgtype2str(m.type), m.type); 26 | break; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /servers/echo/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(void) { 6 | // Register as echo server. 7 | ASSERT_OK(ipc_register("echo")); 8 | TRACE("ready"); 9 | 10 | while (true) { 11 | struct message m; 12 | ASSERT_OK(ipc_recv(IPC_ANY, &m)); 13 | 14 | switch (m.type) { 15 | case ECHO_MSG: { 16 | DBG("received echo message from #%d (len = %d)", m.src, m.echo.data_len); 17 | m.type = ECHO_REPLY_MSG; 18 | // Assume that echo and echo_reply message have the same layout. 19 | ipc_reply(m.src, &m); 20 | break; 21 | } 22 | default: 23 | WARN("unhandled message: %s (%x)", msgtype2str(m.type), m.type); 24 | break; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /libs/user/malloc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #define MALLOC_FREE 0x0a110ced // チャンクが空き状態 6 | #define MALLOC_IN_USE 0xdea110cd // チャンクが使用中状態 7 | 8 | // チャンク (mallocの割り当て単位) の管理構造体 9 | struct malloc_chunk { 10 | list_elem_t next; // 空きチャンクのリストの要素 11 | size_t capacity; // チャンクのサイズ 12 | size_t size; // ユーザが使っているサイズ (size <= capacity) 13 | uint32_t magic; // チャンクの状態を表すマジックナンバー 14 | uint8_t padding[4]; // 8バイトアラインメントのためのパディング 15 | uint8_t data[]; // ユーザが使う可変長領域 (mallocが返すアドレス) 16 | }; 17 | 18 | STATIC_ASSERT(IS_ALIGNED(sizeof(struct malloc_chunk), 8), 19 | "malloc_chunk size must be aligned to 8 bytes"); 20 | 21 | void *malloc(size_t size); 22 | void *realloc(void *ptr, size_t size); 23 | char *strdup(const char *s); 24 | void free(void *ptr); 25 | void malloc_init(void); 26 | -------------------------------------------------------------------------------- /mk/wasm.mk: -------------------------------------------------------------------------------- 1 | build_dir := $(BUILD_DIR)/$(dir) 2 | objs := $(addprefix $(build_dir)/, $(objs-y)) 3 | dir-saved = $(dir) 4 | $(foreach subdir, $(subdirs-y), \ 5 | $(eval dir := $(dir-saved)/$(subdir)) \ 6 | $(eval build_dir := $(BUILD_DIR)/$(dir)) \ 7 | $(eval objs-y :=) \ 8 | $(eval include $(dir)/build.mk) \ 9 | $(eval objs += $(addprefix $(build_dir)/, $(objs-y))) \ 10 | ) 11 | 12 | objs := \ 13 | $(objs) \ 14 | $(foreach lib, $(libs-y), $(BUILD_DIR)/libs/$(lib).o) 15 | 16 | $(objs): WASMCFLAGS := $(WASMCFLAGS) $(cflags-y) 17 | 18 | $(executable): WASMLDFLAGS := $(WASMLDFLAGS) $(ldflags-y) 19 | $(executable): OBJS := $(objs) 20 | $(executable): $(objs) $(extra-deps-y) 21 | $(PROGRESS) WASMLD $(@) 22 | $(MKDIR) -p $(@D) 23 | $(WASMLD) $(WASMLDFLAGS) -Map $(@:.wasm=.map) -o $(@) $(OBJS) -------------------------------------------------------------------------------- /kernel/riscv32/uart.h: -------------------------------------------------------------------------------- 1 | // UARTデバイス 2 | // https://www.lammertbies.nl/comm/info/serial-uart 3 | #pragma once 4 | #include "uart.h" 5 | 6 | #define UART_ADDR ((paddr_t) 0x10000000) // UARTのMMIOの物理アドレス 7 | #define UART0_IRQ 10 // UARTの割り込み番号 8 | 9 | // Transmitter Holding Register. 10 | #define UART_THR (UART_ADDR + 0x00) 11 | 12 | // Receiver Buffer Register. 13 | #define UART_RBR (UART_ADDR + 0x00) 14 | 15 | /// Interrupt Enable Register. 16 | #define UART_IER (UART_ADDR + 0x01) 17 | #define UART_IER_RX (1 << 0) 18 | 19 | /// FIFO Control Register. 20 | #define UART_FCR (UART_ADDR + 0x02) 21 | 22 | /// Line Status Register. 23 | #define UART_LSR (UART_ADDR + 0x05) 24 | #define UART_LSR_RX_READY (1 << 0) 25 | #define UART_LSR_TX_FULL (1 << 5) 26 | 27 | // FIFO Control Register. 28 | #define UART_FCR_FIFO_ENABLE (1 << 0) 29 | #define UART_FCR_FIFO_CLEAR (0b11 << 1) 30 | 31 | void riscv32_uart_init(void); 32 | -------------------------------------------------------------------------------- /kernel/riscv32/uart.c: -------------------------------------------------------------------------------- 1 | // シリアルポートドライバ 2 | // https://www.lammertbies.nl/comm/info/serial-uart 3 | #include "uart.h" 4 | #include "asm.h" 5 | #include 6 | 7 | // 文字を送信する 8 | void arch_serial_write(char ch) { 9 | // 送信可能になるまで待つ 10 | while ((mmio_read8_paddr(UART_LSR) & UART_LSR_TX_FULL) == 0) 11 | ; 12 | 13 | // 送信 14 | mmio_write8_paddr(UART_THR, ch); 15 | } 16 | 17 | int arch_serial_read(void) { 18 | // 受信した文字があるかどうかをチェック 19 | if ((mmio_read8_paddr(UART_LSR) & UART_LSR_RX_READY) == 0) { 20 | return -1; 21 | } 22 | 23 | // 受信した文字を返す 24 | return mmio_read8_paddr(UART_RBR); 25 | } 26 | 27 | // デバイスドライバを初期化する 28 | void riscv32_uart_init(void) { 29 | // 割り込みを無効にする 30 | mmio_write8_paddr(UART_IER, 0); 31 | 32 | // FIFOをクリアして有効化する 33 | mmio_write8_paddr(UART_FCR, UART_FCR_FIFO_ENABLE | UART_FCR_FIFO_CLEAR); 34 | 35 | // 文字を受信したときのみ割り込みを有効にする 36 | mmio_write8_paddr(UART_IER, UART_IER_RX); 37 | } 38 | -------------------------------------------------------------------------------- /tools/merge_compile_commands_json.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from glob import iglob 3 | import os 4 | 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser( 8 | description="Merge compilation databases into a single file." 9 | ) 10 | parser.add_argument( 11 | "-o", 12 | metavar="OUTFILE", 13 | dest="out_file", 14 | required=True, 15 | help="The output file path.", 16 | ) 17 | parser.add_argument( 18 | "build_dir", 19 | help="The directory containing the JSON files generated by clang -MJ.", 20 | ) 21 | args = parser.parse_args() 22 | 23 | db = "[" 24 | for file in iglob(os.path.join(args.build_dir, "**", "*.json"), recursive=True): 25 | if not file.endswith("compile_commands.json"): 26 | db += open(file).read() 27 | db = db.rstrip("\n,") 28 | db += "]\n" 29 | 30 | with open(args.out_file, "w") as f: 31 | f.write(db) 32 | 33 | 34 | if __name__ == "__main__": 35 | main() 36 | -------------------------------------------------------------------------------- /libs/user/riscv32/arch_syscall.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // システムコール命令を発行する。 5 | static inline uint32_t arch_syscall(uint32_t r0, uint32_t r1, uint32_t r2, 6 | uint32_t r3, uint32_t r4, uint32_t r5) { 7 | register int32_t a0 __asm__("a0") = r0; // a0レジスタの内容 8 | register int32_t a1 __asm__("a1") = r1; // a1レジスタの内容 9 | register int32_t a2 __asm__("a2") = r2; // a2レジスタの内容 10 | register int32_t a3 __asm__("a3") = r3; // a3レジスタの内容 11 | register int32_t a4 __asm__("a4") = r4; // a4レジスタの内容 12 | register int32_t a5 __asm__("a5") = r5; // a5レジスタの内容 13 | register int32_t result __asm__("a0"); // 戻り値 (a0レジスタに戻ってくる) 14 | 15 | // ecall命令を実行し、カーネルのシステムコールハンドラ (riscv32_trap_handler) に処理を移す。 16 | // 返り値がa0レジスタ (result変数) に戻ってくる。 17 | __asm__ __volatile__("ecall" 18 | : "=r"(result) 19 | : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5) 20 | : "memory"); 21 | return result; 22 | } 23 | -------------------------------------------------------------------------------- /tools/generate_gdbinit.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | def main(): 4 | parser = argparse.ArgumentParser() 5 | parser.add_argument( 6 | "-o", 7 | metavar="OUTFILE", 8 | dest="outfile", 9 | required=True, 10 | help="The output file path.", 11 | ) 12 | parser.add_argument("dwarf_files", nargs="+") 13 | args = parser.parse_args() 14 | 15 | text = "# tools/generate_gdbinit.py によって生成されたGDBの初期化スクリプト\n" 16 | text += "# make gdb コマンドで利用される\n" 17 | text += "set confirm off\n" 18 | text += "set history save on\n" 19 | text += "set print pretty on\n" 20 | text += "set disassemble-next-line auto\n" 21 | text += "set architecture riscv:rv32\n" 22 | text += "set riscv use-compressed-breakpoints yes\n" 23 | for dwarf_file in args.dwarf_files: 24 | text += f"add-symbol-file {dwarf_file}\n" 25 | text += "target remote 127.0.0.1:7777\n" 26 | 27 | with open(args.outfile, "w", encoding="utf-8") as f: 28 | f.write(text) 29 | 30 | if __name__ == "__main__": 31 | main() 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright 2023 Seiya Nuta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /servers/virtio_blk/virtio_blk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #define VIRTIO_BLK_T_IN 0 // ディスクからの読み込み 7 | #define VIRTIO_BLK_T_OUT 1 // ディスクへの書き込み 8 | 9 | #define VIRTIO_BLK_S_OK 0 // 処理成功 10 | 11 | // 読み書き処理要求用DMAバッファの数。逐次的に処理するため、ひとつで十分。 12 | #define NUM_REQUEST_BUFFERS 1 13 | 14 | // セクタのサイズ (バイト数)。ディスクの読み書きの最小単位。 15 | #define SECTOR_SIZE 512 16 | 17 | // 一度に読み書きできる最大バイト数。セクタサイズにアラインされている必要がある。 18 | #define REQUEST_BUFFER_SIZE SECTOR_SIZE 19 | 20 | STATIC_ASSERT(IS_ALIGNED(REQUEST_BUFFER_SIZE, SECTOR_SIZE), 21 | "virtio-blk buffer size must be aligned to the sector size"); 22 | 23 | // virtio-blkへの読み書き要求 24 | struct virtio_blk_req { 25 | uint32_t type; // VIRTIO_BLK_T_IN または VIRTIO_BLK_T_OUT 26 | uint32_t reserved; // 予約済み 27 | uint64_t sector; // 読み書きするセクタ番号 28 | uint8_t data[REQUEST_BUFFER_SIZE]; // 読み書きするデータ 29 | uint8_t status; // 処理結果。成功ならばVIRTIO_BLK_S_OK。 30 | } __packed; 31 | -------------------------------------------------------------------------------- /libs/common/ubsan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct ubsan_type { 5 | uint16_t kind; 6 | uint16_t info; 7 | char name[]; 8 | }; 9 | 10 | struct ubsan_sourceloc { 11 | const char *file; 12 | uint32_t line; 13 | uint32_t column; 14 | }; 15 | 16 | struct ubsan_mismatch_data_v1 { 17 | struct ubsan_sourceloc loc; 18 | struct ubsan_type *type; 19 | uint8_t align; 20 | uint8_t kind; 21 | }; 22 | 23 | void __ubsan_handle_type_mismatch_v1(struct ubsan_mismatch_data_v1 *data, 24 | vaddr_t ptr); 25 | void __ubsan_handle_add_overflow(void); 26 | void __ubsan_handle_sub_overflow(void); 27 | void __ubsan_handle_mul_overflow(void); 28 | void __ubsan_handle_divrem_overflow(void); 29 | void __ubsan_handle_negate_overflow(void); 30 | void __ubsan_handle_float_cast_overflow(void); 31 | void __ubsan_handle_pointer_overflow(void); 32 | void __ubsan_handle_out_of_bounds(void); 33 | void __ubsan_handle_shift_out_of_bounds(void); 34 | void __ubsan_handle_builtin_unreachable(void); 35 | void __ubsan_handle_invalid_builtin(void); 36 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | types: [opened, synchronize] 8 | 9 | jobs: 10 | linux_build: 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 15 13 | steps: 14 | - name: Checkout repository and submodules 15 | uses: actions/checkout@v2 16 | with: 17 | submodules: recursive 18 | - name: Install packages 19 | run: sudo apt-get update && sudo apt-get install -y llvm clang lld python3-pip qemu-system tshark 20 | - name: Install pip packages 21 | run: pip3 install --user -r tools/requirements.txt 22 | - name: make doctor 23 | run: make doctor 24 | - name: Debug build 25 | run: make build -j2 26 | - name: Run tests (debug build, 1 CPU) 27 | run: make test -j2 28 | env: 29 | TEST_DEFAULT_TIMEOUT: 10 30 | FLAKE_RUNS: 4 31 | CPUS: 1 32 | - name: Run tests (debug build, 4 CPUs) 33 | run: make test -j2 34 | env: 35 | TEST_DEFAULT_TIMEOUT: 10 36 | FLAKE_RUNS: 4 37 | CPUS: 4 -------------------------------------------------------------------------------- /servers/virtio_net/virtio_net.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #define NUM_TX_BUFFERS 128 7 | #define NUM_RX_BUFFERS 128 8 | #define VIRTIO_NET_MAX_PACKET_SIZE 1514 9 | 10 | #define VIRTIO_NET_F_MAC (1 << 5) 11 | #define VIRTIO_NET_F_MRG_RXBUF (1 << 15) 12 | #define VIRTIO_NET_F_STATUS (1 << 16) 13 | #define VIRTIO_NET_QUEUE_RX 0 14 | #define VIRTIO_NET_QUEUE_TX 1 15 | 16 | // デバイス固有のコンフィグ領域 (MMIO) 17 | struct virtio_net_config { 18 | uint8_t macaddr[6]; 19 | uint16_t status; 20 | uint16_t max_virtqueue_pairs; 21 | uint16_t mtu; 22 | } __packed; 23 | 24 | // 処理要求のヘッダ 25 | #define VIRTIO_NET_HDR_GSO_NONE 0 26 | struct virtio_net_header { 27 | uint8_t flags; 28 | uint8_t gso_type; 29 | uint16_t hdr_len; 30 | uint16_t gso_size; 31 | uint16_t checksum_start; 32 | uint16_t checksum_offset; 33 | } __packed; 34 | 35 | // virtio-netデバイスへの処理要求 36 | struct virtio_net_req { 37 | struct virtio_net_header header; 38 | uint8_t payload[VIRTIO_NET_MAX_PACKET_SIZE]; 39 | } __packed; 40 | -------------------------------------------------------------------------------- /servers/tcpip/mbuf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // mbufのデータ部分のサイズ 5 | #define MBUF_MAX_LEN (512 - (sizeof(struct mbuf *) + 2 * sizeof(uint16_t))) 6 | 7 | // mbuf: 単方向リストで構成される非連続メモリバッファ 8 | struct mbuf { 9 | struct mbuf *next; // 次のmbufへのポインタ 10 | uint16_t offset; // 有効なデータの先頭オフセット 11 | uint16_t offset_end; // 有効化データの終端オフセット 12 | uint8_t data[MBUF_MAX_LEN]; // データ 13 | }; 14 | 15 | // mbufを表す型。いわゆるopaqueポインタ。 16 | typedef struct mbuf *mbuf_t; 17 | 18 | mbuf_t mbuf_alloc(void); 19 | void mbuf_delete(mbuf_t mbuf); 20 | mbuf_t mbuf_new(const void *data, size_t len); 21 | void mbuf_append(mbuf_t mbuf, mbuf_t new_tail); 22 | void mbuf_append_bytes(mbuf_t mbuf, const void *data, size_t len); 23 | const void *mbuf_data(mbuf_t mbuf); 24 | size_t mbuf_len_one(mbuf_t mbuf); 25 | size_t mbuf_len(mbuf_t mbuf); 26 | bool mbuf_is_empty(mbuf_t mbuf); 27 | size_t mbuf_read(mbuf_t *mbuf, void *buf, size_t buf_len); 28 | mbuf_t mbuf_peek(mbuf_t mbuf, size_t len); 29 | size_t mbuf_discard(mbuf_t *mbuf, size_t len); 30 | void mbuf_truncate(mbuf_t mbuf, size_t len); 31 | mbuf_t mbuf_clone(mbuf_t mbuf); 32 | -------------------------------------------------------------------------------- /kernel/riscv32/plic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NUM_IRQS_MAX 1024 // PLICの最大割り込み数 4 | #define PLIC_ADDR ((paddr_t) 0x0c000000) // PLICの物理アドレス 5 | #define PLIC_SIZE 0x400000 // PLICのMMIO領域のサイズ 6 | 7 | // Interrupt Source Priority 8 | // https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic.adoc#3-interrupt-priorities 9 | #define PLIC_PRIORITY(irq) (PLIC_ADDR + 4 * (irq)) 10 | 11 | // Interrupt Enable Bits 12 | // https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic.adoc#5-interrupt-enables 13 | #define PLIC_ENABLE(irq) (PLIC_ADDR + 0x2080 + ((irq) / 32 * sizeof(uint32_t))) 14 | 15 | // Priority Threshold 16 | // https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic.adoc#6-priority-thresholds 17 | #define PLIC_THRESHOLD(hart) (PLIC_ADDR + 0x201000 + 0x2000 * (hart)) 18 | 19 | // Interrupt Claim Register 20 | // https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic.adoc#7-interrupt-claim-process 21 | #define PLIC_CLAIM(hart) (PLIC_ADDR + 0x201004 + 0x2000 * (hart)) 22 | 23 | unsigned riscv32_plic_pending(void); 24 | void riscv32_plic_ack(unsigned irq); 25 | void riscv32_plic_init_percpu(void); 26 | -------------------------------------------------------------------------------- /libs/user/syscall.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | struct message; 7 | 8 | error_t sys_ipc(task_t dst, task_t src, struct message *m, unsigned flags); 9 | error_t sys_notify(task_t dst, notifications_t notifications); 10 | task_t sys_task_create(const char *name, vaddr_t ip, task_t pager); 11 | task_t sys_hinavm(const char *name, hinavm_inst_t *insts, size_t num_insts, 12 | task_t pager); 13 | task_t sys_wasmvm(const char *name, uint8_t *wasm, size_t size, task_t pager); 14 | error_t sys_task_destroy(task_t task); 15 | __noreturn void sys_task_exit(void); 16 | task_t sys_task_self(void); 17 | pfn_t sys_pm_alloc(task_t tid, size_t size, unsigned flags); 18 | error_t sys_vm_map(task_t task, uaddr_t uaddr, paddr_t paddr, unsigned attrs); 19 | error_t sys_vm_unmap(task_t task, uaddr_t uaddr); 20 | error_t sys_irq_listen(unsigned irq); 21 | error_t sys_irq_unlisten(unsigned irq); 22 | int sys_serial_write(const char *buf, size_t len); 23 | int sys_serial_read(const char *buf, int max_len); 24 | error_t sys_time(int milliseconds); 25 | int sys_uptime(void); 26 | __noreturn void sys_shutdown(void); 27 | -------------------------------------------------------------------------------- /libs/common/riscv32/backtrace.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // RISC-Vのスタックフレーム 5 | struct stack_frame { 6 | uint32_t fp; // 呼び出し元のスタックフレーム 7 | uint32_t ra; // 呼び出し元のアドレス 8 | } __packed; 9 | 10 | // 現在のスタックフレームのアドレスを取得する。 11 | static uint32_t read_fp(void) { 12 | uint32_t fp; 13 | __asm__ __volatile__("mv %0, fp" : "=r"(fp)); 14 | return fp; 15 | } 16 | 17 | /// スタックフレームを辿ってバックトレースを表示する。 18 | void backtrace(void) { 19 | uint32_t fp = read_fp(); 20 | if (!fp) { 21 | WARN("backtrace: fp is NULL"); 22 | return; 23 | } 24 | 25 | for (int i = 0; fp && i < BACKTRACE_MAX; i++) { 26 | struct stack_frame *frame = 27 | (struct stack_frame *) (fp - sizeof(*frame)); 28 | 29 | // 呼び出し元のアドレスからシンボルを探す。 30 | const struct symbol *symbol = find_symbol(frame->ra); 31 | const char *name; 32 | size_t offset; 33 | if (!symbol) { 34 | WARN(" #%d: %p (invalid address)", i, frame->ra); 35 | return; 36 | } 37 | 38 | name = symbol->name; 39 | offset = frame->ra - symbol->addr; 40 | WARN(" #%d: %p %s()+0x%x", i, frame->ra, name, offset); 41 | fp = frame->fp; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /servers/tcpip/ipv4.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "mbuf.h" 3 | 4 | // 送信パケットのTTLフィールドのデフォルト値 5 | #define DEFAULT_TTL 32 6 | 7 | // IPv4アドレスを表す型 8 | typedef uint32_t ipv4addr_t; 9 | 10 | // ブロードキャストアドレス (255.255.255.255) 11 | #define IPV4_ADDR_BROADCAST 0xffffffff 12 | // ポインタに対するNULLのような「未指定」を表すアドレス (0.0.0.0) 13 | #define IPV4_ADDR_UNSPECIFIED 0 14 | 15 | // IPv4パケットの上位プロトコル 16 | enum ip_proto_type { 17 | IPV4_PROTO_TCP = 0x06, 18 | IPV4_PROTO_UDP = 0x11, 19 | }; 20 | 21 | // ポート番号 22 | typedef uint16_t port_t; 23 | 24 | // IPv4アドレスとポート番号のペア 25 | typedef struct { 26 | ipv4addr_t addr; 27 | port_t port; 28 | } endpoint_t; 29 | 30 | // IPv4ヘッダ 31 | struct ipv4_header { 32 | uint8_t ver_ihl; // バージョンとヘッダ長 33 | uint8_t dscp_ecn; // DSCPとECN 34 | uint16_t len; // IPv4ペイロード長 35 | uint16_t id; // 識別子 36 | uint16_t flags_frag_off; // フラグとフラグメントオフセット 37 | uint8_t ttl; // TTL 38 | uint8_t proto; // 上位プロトコル 39 | uint16_t checksum; // チェックサム 40 | uint32_t src_addr; // 送信元IPv4アドレス 41 | uint32_t dst_addr; // 宛先IPv4アドレス 42 | } __packed; 43 | 44 | void ipv4_transmit(ipv4addr_t dst, uint8_t proto, mbuf_t payload); 45 | void ipv4_receive(mbuf_t pkt); 46 | -------------------------------------------------------------------------------- /libs/common/elf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define ELF_MAGIC "\177ELF" // ELFファイルのマジックナンバー 5 | #define ET_EXEC 2 // 実行可能ファイル 6 | #define PT_LOAD 1 // メモリ上に展開されるセグメント 7 | 8 | #define PF_R (1 << 2) // 読み込み可能領域 9 | #define PF_W (1 << 1) // 書き込み可能領域 10 | #define PF_X (1 << 0) // 実行可能領域 11 | 12 | // ELFヘッダ (32ビット版) 13 | struct elf32_ehdr { 14 | uint8_t e_ident[16]; 15 | uint16_t e_type; 16 | uint16_t e_machine; 17 | uint32_t e_version; 18 | uint32_t e_entry; 19 | uint32_t e_phoff; 20 | uint32_t e_shoff; 21 | uint32_t e_flags; 22 | uint16_t e_ehsize; 23 | uint16_t e_phentsize; 24 | uint16_t e_phnum; 25 | uint16_t e_shentsize; 26 | uint16_t e_shnum; 27 | uint16_t e_shstrndx; 28 | } __packed; 29 | 30 | // プログラムヘッダ (32ビット版) 31 | struct elf32_phdr { 32 | uint32_t p_type; 33 | uint32_t p_offset; 34 | uint32_t p_vaddr; 35 | uint32_t p_paddr; 36 | uint32_t p_filesz; 37 | uint32_t p_memsz; 38 | uint32_t p_flags; 39 | uint32_t p_align; 40 | } __packed; 41 | 42 | #ifdef __LP64__ 43 | # error "elf.h: __LP64__ not supported" 44 | #else 45 | typedef struct elf32_ehdr elf_ehdr_t; 46 | typedef struct elf32_phdr elf_phdr_t; 47 | #endif 48 | -------------------------------------------------------------------------------- /mk/lib.mk: -------------------------------------------------------------------------------- 1 | # ライブラリの生成ルール 2 | # 3 | # libs/<ライブラリ名>配下に build.mk という名前のファイルを作ると、 4 | # ビルドシステムがそれを認識して必要なビルドルールを構築する。 5 | # 6 | # build.mk内では次の変数を使って宣言的にビルドルールを定義する。原則として、 7 | # 末尾に追記する (+=) ことで定義するのがおすすめ。上書き (:=) するとデフォルト値を 8 | # 失ってしまう。 9 | # 10 | # - objs-y: 生成するオブジェクトファイルのリスト。 11 | # C/アセンブリファイル共に拡張子を.oにする必要があり、 12 | # hello.cとhello.Sのような同名のファイルは扱えない。 13 | # - cflags-y: Cコンパイラのオプション 14 | # - subdirs-y: サブディレクトリのリスト。定義されているとビルドシステムそれらの 15 | # サブディレクトリに入っているbuild.mkを読み込む。 16 | 17 | # サブディレクトリ (subdir-y) を辿って必要なオブジェクトファイルを列挙する 18 | build_dir := $(BUILD_DIR)/$(dir) 19 | objs := $(addprefix $(build_dir)/, $(objs-y)) 20 | dir-saved := $(dir) 21 | $(foreach subdir, $(subdirs-y), \ 22 | $(eval dir := $(dir-saved)/$(subdir)) \ 23 | $(eval build_dir := $(BUILD_DIR)/$(dir)) \ 24 | $(eval objs-y :=) \ 25 | $(eval include $(dir)/build.mk) \ 26 | $(eval objs += $(addprefix $(build_dir)/, $(objs-y))) \ 27 | ) 28 | 29 | # ライブラリファイルの生成ルール (build/libs/<ライブラリ名>.o) 30 | # 31 | # リンカーの-rオプションを使って単一のオブジェクトファイルにまとめる。 32 | $(objs): CFLAGS := $(CFLAGS) $(cflags-y) 33 | $(output): OBJS := $(objs) 34 | $(output): $(objs) 35 | $(PROGRESS) LD $(@) 36 | $(MKDIR) -p $(@D) 37 | $(LD) -r -o $(@) $(OBJS) 38 | -------------------------------------------------------------------------------- /libs/user/riscv32/user.ld.template: -------------------------------------------------------------------------------- 1 | // ユーザープログラムのリンカスクリプトのテンプレート。仮想アドレス上のレイアウトを指定するもの。 2 | // 3 | // 実際に使われるリンカスクリプトは次のコマンドで生成される: 4 | // 5 | // clang -E -include build/user_ld_params.h このファイル -o リンカスクリプト 6 | // 7 | // -Eが指定されているので、このファイルはプリプロセッサによってマクロ展開されるだけで終了する。 8 | // C言語以外のテキストファイルで #ifdef/#define が使える、ちょっとしたテクニック。 9 | // 10 | // user_ld_params.h は各サーバのUSER_BASE_ADDR/USER_SIZEを定義している。 11 | // -include オプションをつけることで、このファイルの先頭でインクルードされる。 12 | ENTRY(start) 13 | 14 | SECTIONS { 15 | . = USER_BASE_ADDR; 16 | 17 | .text : ALIGN(4096) { 18 | *(.text .text.*); 19 | } 20 | 21 | .rodata : ALIGN(4096) { 22 | *(.rodata .rodata.*); 23 | . = ALIGN(16); 24 | *(.srodata .srodata.*); 25 | KEEP(*(.symbols)); 26 | } 27 | 28 | .data : ALIGN(4096) { 29 | *(.data .data.*); 30 | *(.sdata .sdata.*); 31 | } 32 | 33 | .bss : ALIGN(4096) { 34 | *(.bss .bss.*); 35 | . = ALIGN(16); 36 | *(.sbss .sbss.*); 37 | 38 | . = ALIGN(16); 39 | 40 | // ヒープ領域 (mallocが管理) 41 | __heap = .; 42 | . += 4 * 1024 * 1024; // 4MiB 43 | __heap_end = .; 44 | 45 | // スタック領域 46 | . += 256 * 1024; // 256KiB 47 | __stack = .; 48 | 49 | ASSERT(. <= (USER_BASE_ADDR + USER_SIZE), "too large user program"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tools/generate_user_ld_params.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | # user.ldのベース仮想アドレス。ここからユーザープログラムのメモリ領域がマップされる。 4 | # 5 | # 512 MiB目から利用するのは、NULLポインタ参照をきちんと検出できるようにするためなのと、 6 | # MMIO領域 (UART, virtio, PLIC, ACLINT) の物理アドレスがそのまま同じ仮想アドレスに 7 | # 対応するようにカーネルがマップしているため。 8 | USER_BASE_ADDR = 0x20000000 9 | 10 | # ユーザープログラムのメモリ領域 (.text, .data, .rodata, .bss, スタック, ヒープ) のサイズ。 11 | USER_SIZE = 32 * 1024 * 1024 # 32 MiB 12 | 13 | def main(): 14 | parser = argparse.ArgumentParser() 15 | parser.add_argument( 16 | "-o", 17 | metavar="OUTFILE", 18 | dest="out_file", 19 | required=True, 20 | help="The output file path.", 21 | ) 22 | parser.add_argument("servers", nargs="+", help="The list of servers.") 23 | args = parser.parse_args() 24 | 25 | text = "// generate_user_ld_params.py によって生成されたファイル\n" 26 | text += "#pragma once\n" 27 | text += "\n" 28 | 29 | base_addr = USER_BASE_ADDR 30 | for server in sorted(args.servers): 31 | text += f"#ifdef SERVER_{server.lower()}\n" 32 | text += f"#define USER_BASE_ADDR {hex(base_addr)}\n" 33 | text += f"#define USER_SIZE {hex(USER_SIZE)}\n" 34 | text += "#endif\n\n" 35 | base_addr += USER_SIZE 36 | 37 | with open(args.out_file, "w", encoding="utf-8") as f: 38 | f.write(text) 39 | 40 | 41 | if __name__ == "__main__": 42 | main() 43 | -------------------------------------------------------------------------------- /libs/common/message.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ipcstub.h" 3 | 4 | #define IPC_ANY 0 5 | #define IPC_DENY -1 6 | 7 | #define IPC_SEND (1 << 16) 8 | #define IPC_RECV (1 << 17) 9 | #define IPC_NOBLOCK (1 << 18) 10 | #define IPC_KERNEL (1 << 19) 11 | #define IPC_WASMVM (1 << 20) 12 | #define IPC_CALL (IPC_SEND | IPC_RECV) 13 | 14 | #define NOTIFY_TIMER (1 << 0) 15 | #define NOTIFY_IRQ (1 << 1) 16 | #define NOTIFY_ABORTED (1 << 2) 17 | #define NOTIFY_ASYNC_BASE 3 18 | #define NOTIFY_ASYNC(tid) (1 << (NOTIFY_ASYNC_BASE + tid)) 19 | #define NOTIFY_ASYNC_START (NOTIFY_ASYNC(0)) 20 | #define NOTIFY_ASYNC_END (NOTIFY_ASYNC(NUM_TASKS_MAX)) 21 | 22 | // 各タスクは専用のASYNC用ビットフィールドをnotification_tに持つ 23 | STATIC_ASSERT(NOTIFY_ASYNC_BASE + NUM_TASKS_MAX < sizeof(notifications_t) * 8, 24 | "too many tasks for notifications_t"); 25 | 26 | struct message { 27 | int32_t type; // メッセージの種類 (負の数の場合はエラー値) 28 | task_t src; // メッセージの送信元 29 | union { 30 | uint8_t data[0]; // メッセージデータの先頭を指す 31 | /// 自動生成される各メッセージのフィールド定義: 32 | // 33 | // struct { int x; int y; } add; 34 | // struct { int answer; } add_reply; 35 | // ... 36 | // 37 | IPCSTUB_MESSAGE_FIELDS 38 | }; 39 | }; 40 | 41 | STATIC_ASSERT(sizeof(struct message) < 2048, 42 | "sizeof(struct message) too large"); 43 | 44 | const char *msgtype2str(int type); 45 | -------------------------------------------------------------------------------- /servers/tcpip/dns.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ipv4.h" 3 | #include 4 | #include 5 | 6 | // DNSクエリの種類: Aレコード 7 | #define DNS_QTYPE_A 0x0001 8 | // DNSクラス: インターネット 9 | #define DNS_QCLASS_IN 0x0001 10 | 11 | // DNSヘッダのフラグ 12 | #define DNS_FLAG_RD (1 << 7) // 再帰的な解決を要求 13 | 14 | // 応答待ちDNSクエリの情報 15 | struct dns_request { 16 | list_elem_t next; // リスト中の次の要素 17 | uint16_t id; // トランザクションID 18 | void *arg; // 任意の引数 19 | }; 20 | 21 | // DNSヘッダ 22 | struct dns_header { 23 | uint16_t id; // トランザクションID 24 | uint16_t flags; // 各フラグ 25 | uint16_t num_queries; // QUESTIONセクションのクエリ数 26 | uint16_t num_answers; // ANSWERセクションのレコード数 27 | uint16_t num_authority; // AUTHORITYセクションのレコード数 28 | uint16_t num_additional; // ADDITIONALセクションのレコード数 29 | } __packed; 30 | 31 | // QUESTIONセクションのクエリのうち、QNAMEより後ろの部分 32 | struct dns_query_footer { 33 | uint16_t qtype; // 種類 34 | uint16_t qclass; // クラス 35 | } __packed; 36 | 37 | // ANSWERセクションのレコードのうち、NAMEより後ろの部分 38 | struct dns_answer_footer { 39 | uint16_t type; // 種類 40 | uint16_t class; // クラス 41 | uint32_t ttl; // TTL 42 | uint16_t len; // データ長 43 | uint8_t data[]; // データ 44 | } __packed; 45 | 46 | error_t dns_query(const char *hostname, void *arg); 47 | void dns_receive(void); 48 | void dns_set_name_server(ipv4addr_t ipaddr); 49 | void dns_init(void); 50 | -------------------------------------------------------------------------------- /libs/user/driver.c: -------------------------------------------------------------------------------- 1 | // デバイスドライバAPI。基本的にはVMサーバに対するメッセージパッシングのラッパー。 2 | #include 3 | #include 4 | 5 | // 指定された物理メモリ領域を空いている仮想アドレス領域にマップする。MMIO領域にアクセスしたい 6 | // 時に使える。 7 | // 8 | // 引数 map_flags にはメモリ領域の権限 PAGE_(READABLE|WRITABLE|EXECUTABLE) を指定する。 9 | error_t driver_map_pages(paddr_t paddr, size_t size, int map_flags, 10 | uaddr_t *uaddr) { 11 | struct message m; 12 | m.type = VM_MAP_PHYSICAL_MSG; 13 | m.vm_map_physical.paddr = paddr; 14 | m.vm_map_physical.size = size; 15 | m.vm_map_physical.map_flags = map_flags; 16 | error_t err = ipc_call(VM_SERVER, &m); 17 | if (err != OK) { 18 | return err; 19 | } 20 | 21 | *uaddr = m.vm_map_physical_reply.uaddr; 22 | return OK; 23 | } 24 | 25 | // 物理メモリ領域を確保する。 26 | // 27 | // 引数 map_flags にはメモリ領域の権限 PAGE_(READABLE|WRITABLE|EXECUTABLE) を指定する。 28 | error_t driver_alloc_pages(size_t size, int map_flags, uaddr_t *uaddr, 29 | paddr_t *paddr) { 30 | struct message m; 31 | m.type = VM_ALLOC_PHYSICAL_MSG; 32 | m.vm_alloc_physical.size = size; 33 | m.vm_alloc_physical.alloc_flags = 0; 34 | m.vm_alloc_physical.map_flags = map_flags; 35 | error_t err = ipc_call(VM_SERVER, &m); 36 | if (err != OK) { 37 | return err; 38 | } 39 | 40 | *uaddr = m.vm_alloc_physical_reply.uaddr; 41 | *paddr = m.vm_alloc_physical_reply.paddr; 42 | return OK; 43 | } 44 | -------------------------------------------------------------------------------- /kernel/memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | // 物理ページ管理構造体 6 | struct page { 7 | struct task *owner; // 所有者 (NULLならカーネルの内部データ構造) 8 | unsigned ref_count; // 参照カウンタ: 9 | // - 0: 空き 10 | // - 1: 割り当て済み (まだマップされていない) 11 | // - 2: マップ済み (1つのタスクでのみ使用中) 12 | // - 3以上: マップ済み (複数のタスクで使用中。つまり共有メモリ) 13 | list_elem_t next; // 所有者タスクのtask->pagesのリスト要素 14 | }; 15 | 16 | // メモリゾーンの種類 17 | enum memory_zone_type { 18 | MEMORY_ZONE_FREE, // 空き領域 19 | MEMORY_ZONE_MMIO, // MMIO領域 20 | }; 21 | 22 | // メモリゾーン管理構造体 23 | struct memory_zone { 24 | enum memory_zone_type type; // 種類 25 | list_elem_t next; // 各メモリゾーンを繋げたリストの要素 26 | paddr_t base; // 先頭物理アドレス 27 | size_t num_pages; // 物理ページ数 28 | struct page pages[]; // 物理ページ管理構造体の配列 29 | }; 30 | 31 | struct task; 32 | paddr_t pm_alloc(size_t size, struct task *owner, unsigned flags); 33 | void pm_own_page(paddr_t paddr, struct task *owner); 34 | void pm_free(paddr_t paddr, size_t size); 35 | void pm_free_by_list(list_t *pages); 36 | error_t vm_map(struct task *task, uaddr_t uaddr, paddr_t paddr, unsigned attrs); 37 | error_t vm_unmap(struct task *task, uaddr_t uaddr); 38 | void handle_page_fault(uaddr_t uaddr, vaddr_t ip, unsigned fault); 39 | 40 | struct bootinfo; 41 | void memory_init(struct bootinfo *bootinfo); 42 | -------------------------------------------------------------------------------- /kernel/riscv32/plic.c: -------------------------------------------------------------------------------- 1 | // Platform-Level Interrupt Controller (PLIC) デバイスドライバ 2 | // https://github.com/riscv/riscv-plic-spec 3 | #include "plic.h" 4 | #include "asm.h" 5 | #include "mp.h" 6 | #include "uart.h" 7 | #include 8 | 9 | // 受信済み割り込み番号を取得する 10 | unsigned riscv32_plic_pending(void) { 11 | return mmio_read32_paddr(PLIC_CLAIM(CPUVAR->id)); 12 | } 13 | 14 | // 割り込みを処理したことをPLICに通知する 15 | void riscv32_plic_ack(unsigned irq) { 16 | ASSERT(irq < IRQ_MAX); 17 | 18 | mmio_write32_paddr((PLIC_CLAIM(CPUVAR->id)), irq); 19 | } 20 | 21 | // 割り込みを有効にする 22 | error_t arch_irq_enable(unsigned irq) { 23 | ASSERT(irq < IRQ_MAX); 24 | 25 | // 割り込み優先度を1に設定 26 | mmio_write32_paddr((PLIC_PRIORITY(irq)), 1); 27 | // 割り込みを有効化する 28 | mmio_write32_paddr((PLIC_ENABLE(irq)), 29 | mmio_read32_paddr(PLIC_ENABLE(irq)) | (1 << (irq % 32))); 30 | return OK; 31 | } 32 | 33 | // 割り込みを無効にする 34 | error_t arch_irq_disable(unsigned irq) { 35 | ASSERT(irq < IRQ_MAX); 36 | 37 | // 割り込み優先度を0に設定 38 | mmio_write32_paddr((PLIC_PRIORITY(irq)), 0); 39 | // 割り込みを無効化する 40 | mmio_write32_paddr((PLIC_ENABLE(irq)), mmio_read32_paddr(PLIC_ENABLE(irq)) 41 | & ~(1 << (irq % 32))); 42 | return OK; 43 | } 44 | 45 | // 各CPUでのPLICの初期化 46 | void riscv32_plic_init_percpu(void) { 47 | // 割り込み閾値を0に設定し、全ての割り込みを有効にする 48 | mmio_write32_paddr((PLIC_THRESHOLD(CPUVAR->id)), 0); 49 | } 50 | -------------------------------------------------------------------------------- /servers/vm/task.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | // サービス名の最大長 7 | #define SERVICE_NAME_LEN 64 8 | // 動的に割り当てられる仮想アドレスの開始アドレス 9 | #define VALLOC_BASE 0x20000000 10 | // 動的に割り当てられる仮想アドレスの終了アドレス 11 | #define VALLOC_END 0x40000000 12 | 13 | // サービス管理構造体。サービス名とタスクIDの対応を保持し、サービスディスカバリに使われる。 14 | struct service { 15 | list_elem_t next; 16 | char name[SERVICE_NAME_LEN]; // サービス名 17 | task_t task; // タスクID 18 | }; 19 | 20 | // タスク管理構造体 21 | struct bootfs_file; 22 | struct task { 23 | task_t tid; // タスクID 24 | task_t pager; // ページャタスクID 25 | char name[TASK_NAME_LEN]; // タスク名 26 | void *file_header; // ELFファイルの先頭を指す 27 | struct bootfs_file *file; // BootFS上のELFファイル 28 | elf_ehdr_t *ehdr; // ELFヘッダ 29 | elf_phdr_t *phdrs; // プログラムヘッダ 30 | uaddr_t valloc_next; // 動的に割り当てられる仮想アドレスの次のアドレス 31 | char waiting_for[SERVICE_NAME_LEN]; // サービス登録待ちのサービス名 32 | bool watch_tasks; // タスクの終了を監視するかどうか 33 | }; 34 | 35 | struct task *task_find(task_t tid); 36 | task_t task_spawn(struct bootfs_file *file); 37 | void task_destroy(struct task *task); 38 | error_t task_destroy_by_tid(task_t tid); 39 | void service_register(struct task *task, const char *name); 40 | task_t service_lookup_or_wait(struct task *task, const char *name); 41 | void service_dump(void); 42 | -------------------------------------------------------------------------------- /kernel/riscv32/kernel.ld.template: -------------------------------------------------------------------------------- 1 | // カーネルのリンカスクリプトのテンプレート。仮想アドレス上のレイアウトを指定するもの。 2 | // 3 | // 実際に使われるリンカスクリプトは次のコマンドで生成される: 4 | // 5 | // clang -E このファイル -o リンカスクリプト 6 | // 7 | // -Eが指定されているので、このファイルはプリプロセッサによってマクロ展開されるだけで終了する。 8 | // C言語以外のテキストファイルで #ifdef/#define が使える、ちょっとしたテクニック。 9 | #include 10 | 11 | ENTRY(boot) 12 | 13 | SECTIONS { 14 | . = 0x80000000; 15 | __ram_start = .; 16 | 17 | .text : { 18 | // ブートプログラムは 0x80000000 に来る必要がある 19 | __text = .; 20 | KEEP(*(.boot)); 21 | 22 | . = ALIGN(16); 23 | *(.text .text.*); 24 | 25 | . = ALIGN(4096); 26 | __text_end = .; 27 | } 28 | 29 | __data = .; 30 | .rodata : { 31 | *(.rodata .rodata.*); 32 | . = ALIGN(16); 33 | *(.srodata .srodata.*); 34 | 35 | KEEP(*(.symbols)); 36 | 37 | . = ALIGN(4096); 38 | __boot_elf = .; 39 | *(.boot_elf); 40 | . = ALIGN(4096); 41 | } 42 | 43 | .data : { 44 | . = ALIGN(16); 45 | *(.data .data.*); 46 | . = ALIGN(16); 47 | *(.sdata .sdata.*); 48 | } 49 | 50 | .bss : { 51 | __bss = .; 52 | . = ALIGN(16); 53 | *(.bss .bss.*); 54 | . = ALIGN(16); 55 | *(.sbss .sbss.*); 56 | __bss_end = .; 57 | 58 | . = ALIGN(4096); 59 | 60 | __boot_stack = .; 61 | . = . + NUM_CPUS_MAX * KERNEL_STACK_SIZE; 62 | } 63 | 64 | . = ALIGN(4096); 65 | __data_end = .; 66 | __free_ram_start = .; 67 | } 68 | -------------------------------------------------------------------------------- /servers/tcpip/checksum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "mbuf.h" 3 | #include 4 | #include 5 | #include 6 | 7 | // チェックサム計算の途中結果を表す型。チェックサムであることを明示するために新しい型を定義する。 8 | typedef uint32_t checksum_t; 9 | 10 | // チェックサム計算の初期化 11 | static inline void checksum_init(checksum_t *c) { 12 | *c = 0; 13 | } 14 | 15 | // チェックサムに指定したバイト列を追加する 16 | static inline void checksum_update(checksum_t *c, const void *data, 17 | size_t len) { 18 | // 各16ビットワードを加算する 19 | const uint16_t *words = data; 20 | for (size_t i = 0; i < len / 2; i++) { 21 | *c += words[i]; 22 | } 23 | 24 | // 長さが奇数の場合は最後の1バイトを加算する 25 | if (len % 2 != 0) { 26 | *c += ((uint8_t *) data)[len - 1]; 27 | } 28 | } 29 | 30 | // チェックサムに指定した2バイト整数を追加する 31 | static inline void checksum_update_uint16(checksum_t *c, uint16_t data) { 32 | *c += data; 33 | } 34 | 35 | // チェックサムに指定した4バイト整数を追加する 36 | static inline void checksum_update_uint32(checksum_t *c, uint32_t data) { 37 | *c += data & 0xffff; 38 | *c += data >> 16; 39 | } 40 | 41 | // チェックサムに指定したmbufの全データを追加する 42 | static inline void checksum_update_mbuf(checksum_t *c, mbuf_t mbuf) { 43 | while (mbuf) { 44 | checksum_update(c, mbuf_data(mbuf), mbuf_len_one(mbuf)); 45 | mbuf = mbuf->next; 46 | } 47 | } 48 | 49 | // チェックサムを集計して最終結果を返す 50 | static inline uint32_t checksum_finish(checksum_t *c) { 51 | *c = (*c >> 16) + (*c & 0xffff); 52 | *c += *c >> 16; 53 | return ~*c; 54 | } 55 | -------------------------------------------------------------------------------- /servers/vm/bootfs.c: -------------------------------------------------------------------------------- 1 | #include "bootfs.h" 2 | #include 3 | #include 4 | 5 | extern char __bootfs[]; // BootFSイメージ 6 | static struct bootfs_file *files; // BootFSのファイルリスト 7 | static unsigned num_files; // BootFS内のファイル数 8 | 9 | // BootFSからファイルを読み込む。 10 | void bootfs_read(struct bootfs_file *file, offset_t off, void *buf, 11 | size_t len) { 12 | void *p = (void *) (((uaddr_t) __bootfs) + file->offset + off); 13 | memcpy(buf, p, len); 14 | } 15 | 16 | // BootFSのファイルを開く。 17 | struct bootfs_file *bootfs_open(const char *path) { 18 | // ファイル名が一致するエントリを探す。 19 | struct bootfs_file *file; 20 | for (int i = 0; (file = bootfs_open_iter(i)) != NULL; i++) { 21 | if (!strncmp(file->name, path, sizeof(file->name))) { 22 | return file; 23 | } 24 | } 25 | 26 | return NULL; 27 | } 28 | 29 | // index番目のファイルエントリを返す。 30 | struct bootfs_file *bootfs_open_iter(unsigned index) { 31 | if (index >= num_files) { 32 | return NULL; 33 | } 34 | 35 | return &files[index]; 36 | } 37 | 38 | // BootFSを初期化する。 39 | void bootfs_init(void) { 40 | struct bootfs_header *header = (struct bootfs_header *) __bootfs; 41 | num_files = header->num_files; 42 | files = 43 | (struct bootfs_file *) (((uaddr_t) &__bootfs) + header->header_size); 44 | 45 | TRACE("bootfs: found following %d files", num_files); 46 | for (unsigned i = 0; i < num_files; i++) { 47 | TRACE("bootfs: \"%s\" (%d KiB)", files[i].name, files[i].len / 1024); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /libs/common/error.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include 3 | 4 | // エラー番号とエラーメッセージの対応表。 5 | static const char *error_names[] = { 6 | [-OK] = "Success", 7 | [-ERR_NO_MEMORY] = "No Memory", 8 | [-ERR_NO_RESOURCES] = "No Resources Available", 9 | [-ERR_ALREADY_EXISTS] = "Already Exists", 10 | [-ERR_ALREADY_USED] = "Already In Use", 11 | [-ERR_ALREADY_DONE] = "Already Done", 12 | [-ERR_STILL_USED] = "Still In Use", 13 | [-ERR_NOT_FOUND] = "Not Found", 14 | [-ERR_NOT_ALLOWED] = "Not Allowed", 15 | [-ERR_NOT_SUPPORTED] = "Not Supported", 16 | [-ERR_UNEXPECTED] = "Unexpected Error", 17 | [-ERR_INVALID_ARG] = "Invalid Argument", 18 | [-ERR_INVALID_TASK] = "Invalid Task", 19 | [-ERR_INVALID_SYSCALL] = "Invalid System Call", 20 | [-ERR_INVALID_PADDR] = "Invalid Physical Address", 21 | [-ERR_INVALID_UADDR] = "Invalid User Address", 22 | [-ERR_TOO_MANY_TASKS] = "Too Many Tasks", 23 | [-ERR_TOO_LARGE] = "Too Large", 24 | [-ERR_TOO_SMALL] = "Too Small", 25 | [-ERR_WOULD_BLOCK] = "Operation Would Block", 26 | [-ERR_TRY_AGAIN] = "Try Again", 27 | [-ERR_ABORTED] = "Aborted", 28 | [-ERR_EMPTY] = "Empty", 29 | [-ERR_NOT_EMPTY] = "Not Empty", 30 | [-ERR_DEAD_LOCK] = "Deadlock Detected", 31 | [-ERR_NOT_A_FILE] = "Not A File", 32 | [-ERR_NOT_A_DIR] = "Not A Directory", 33 | [-ERR_EOF] = "End of File", 34 | }; 35 | 36 | // エラー番号からエラーメッセージを取得する。 37 | const char *err2str(int err) { 38 | if (err < ERR_END || err > 0) { 39 | return "(Unknown Error)"; 40 | } 41 | 42 | return error_names[-err]; 43 | } 44 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | Language: Cpp 3 | IndentWidth: 4 4 | ColumnLimit: 80 5 | UseTab: Never 6 | 7 | AlignConsecutiveMacros: true 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllArgumentsOnNextLine: true 12 | AllowAllParametersOfDeclarationOnNextLine: true 13 | AllowShortBlocksOnASingleLine: Empty 14 | AllowShortCaseLabelsOnASingleLine: false 15 | AllowShortFunctionsOnASingleLine: None 16 | AllowShortIfStatementsOnASingleLine: Never 17 | AllowShortLoopsOnASingleLine: false 18 | AlwaysBreakBeforeMultilineStrings: true 19 | BinPackArguments: true 20 | BinPackParameters: true 21 | 22 | BreakBeforeBinaryOperators: NonAssignment 23 | BreakBeforeBraces: Attach 24 | BreakStringLiterals: false 25 | 26 | IncludeBlocks: Merge 27 | IncludeCategories: 28 | - Regex: '.*' 29 | Priority: 1 30 | - Regex: '<.+>' 31 | Priority: 2 32 | 33 | SortIncludes: true 34 | IndentCaseLabels: true 35 | IndentGotoLabels: false 36 | IndentPPDirectives: AfterHash 37 | 38 | ReflowComments: false 39 | 40 | # 日本語コメントのためのダーティーハック。利用するには次のパッチを当てる必要がある。 41 | # tools/clang-format-align-trailing-comments-kludge.patch 42 | # 43 | # DisableColumnLimitInAlignTrailingComments: true 44 | 45 | SpaceAfterCStyleCast: true 46 | SpaceAfterLogicalNot: false 47 | SpaceBeforeAssignmentOperators: true 48 | SpaceBeforeParens: ControlStatements 49 | SpaceInEmptyBlock: false 50 | SpaceInEmptyParentheses: false 51 | SpacesInCStyleCastParentheses: false 52 | SpacesInContainerLiterals: false 53 | SpacesInParentheses: false 54 | SpacesInSquareBrackets: false 55 | ForEachMacros: 56 | - LIST_FOR_EACH 57 | -------------------------------------------------------------------------------- /kernel/riscv32/usercopy.S: -------------------------------------------------------------------------------- 1 | // void arch_memcpy_from_user(void *dst, __user const void *src, size_t len); 2 | // ^^^ ^^^ ^^^ 3 | // a0レジスタ a1レジスタ a2レジスタ 4 | .global arch_memcpy_from_user 5 | .global riscv32_usercopy1 6 | arch_memcpy_from_user: 7 | beqz a2, 2f // a2 (コピー長) がゼロならラベル「2」にジャンプ 8 | 1: 9 | riscv32_usercopy1: 10 | lb a3, 0(a1) // a1レジスタの指すアドレス (ユーザーポインタ) から1バイト読み込む 11 | 12 | sb a3, 0(a0) // a0レジスタの指すアドレス (カーネルポインタ) に1バイト書き込む 13 | addi a1, a1, 1 // a1レジスタ (ユーザーポインタ) を1バイト進める 14 | addi a0, a0, 1 // a0レジスタ (カーネルポインタ) を1バイト進める 15 | addi a2, a2, -1 // a2レジスタの値を1減らす 16 | bnez a2, 1b // a2レジスタの値がゼロでなければラベル「1」にジャンプ 17 | 2: 18 | ret // 関数から戻る 19 | 20 | // void arch_memcpy_to_user(__user void *dst, const void *src, size_t len); 21 | // ^^^ ^^^ ^^^ 22 | // a0レジスタ a1レジスタ a2レジスタ 23 | .global arch_memcpy_to_user 24 | .global riscv32_usercopy2 25 | arch_memcpy_to_user: 26 | beqz a2, 2f // a2 (コピー長) がゼロならラベル「2」にジャンプ 27 | 1: 28 | lb a3, 0(a1) // a1レジスタの指すアドレス (カーネルポインタ) から1バイト読み込む 29 | 30 | riscv32_usercopy2: 31 | sb a3, 0(a0) // a0レジスタの指すアドレス (ユーザーポインタ) に1バイト書き込む 32 | 33 | addi a0, a0, 1 // a0レジスタ (ユーザーポインタ) を1バイト進める 34 | addi a1, a1, 1 // a1レジスタ (カーネルポインタ) を1バイト進める 35 | addi a2, a2, -1 // a2レジスタの値を1減らす 36 | bnez a2, 1b // a2レジスタの値がゼロでなければラベル「1」にジャンプ 37 | 2: 38 | ret // 関数から戻る 39 | -------------------------------------------------------------------------------- /servers/tcpip/udp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ipv4.h" 3 | #include "mbuf.h" 4 | #include 5 | 6 | // UDPヘッダ 7 | struct udp_header { 8 | uint16_t src_port; // 送信元ポート番号 9 | uint16_t dst_port; // 宛先ポート番号 10 | uint16_t len; // UDPペイロード長 11 | uint16_t checksum; // チェックサム 12 | } __packed; 13 | 14 | // UDPデータグラムを表す構造体 15 | struct udp_datagram { 16 | list_elem_t next; // リストの次の要素へのポインタ 17 | ipv4addr_t addr; // 送信元IPv4アドレス 18 | port_t port; // 送信元ポート番号 19 | mbuf_t payload; // UDPペイロード 20 | }; 21 | 22 | // 最大UDP通信数 23 | #define UDP_PCBS_MAX 512 24 | 25 | // UDP通信の管理構造体 26 | struct udp_pcb { 27 | list_elem_t next; // active_socksの次の要素へのポインタ 28 | bool in_use; // 使用中かどうか 29 | endpoint_t local; // ソケットに紐付けられたIPアドレスとポート番号 30 | list_t rx; // 受信済みデータグラムのリスト 31 | list_t tx; // 送信待ちデータグラムのリスト 32 | }; 33 | 34 | // UDPソケットを表す型。いわゆるopaqueポインタ。 35 | typedef struct udp_pcb *udp_sock_t; 36 | 37 | udp_sock_t udp_new(void); 38 | void udp_close(udp_sock_t sock); 39 | void udp_bind(udp_sock_t sock, ipv4addr_t addr, port_t port); 40 | void udp_sendto_mbuf(udp_sock_t sock, ipv4addr_t dst, port_t dst_port, 41 | mbuf_t payload); 42 | void udp_sendto(udp_sock_t sock, ipv4addr_t dst, port_t dst_port, 43 | const void *data, size_t len); 44 | mbuf_t udp_recv_mbuf(udp_sock_t sock, ipv4addr_t *src, port_t *src_port); 45 | size_t udp_recv(udp_sock_t sock, void *buf, size_t buf_len, ipv4addr_t *src, 46 | port_t *src_port); 47 | void udp_transmit(udp_sock_t sock); 48 | void udp_receive(ipv4addr_t src, mbuf_t pkt); 49 | void udp_init(void); 50 | -------------------------------------------------------------------------------- /kernel/riscv32/trap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // 割り込み・例外発生時の実行状態。riscv32_trap_handler関数で保存・復元される。 5 | struct riscv32_trap_frame { 6 | uint32_t pc; // オフセット: 4 * 0 7 | uint32_t sstatus; // オフセット: 4 * 1 8 | uint32_t ra; // オフセット: 4 * 2 9 | uint32_t sp; // オフセット: 4 * 3 10 | uint32_t gp; // オフセット: 4 * 4 11 | uint32_t tp; // オフセット: 4 * 5 12 | uint32_t t0; // オフセット: 4 * 6 13 | uint32_t t1; // オフセット: 4 * 7 14 | uint32_t t2; // オフセット: 4 * 8 15 | uint32_t t3; // オフセット: 4 * 9 16 | uint32_t t4; // オフセット: 4 * 10 17 | uint32_t t5; // オフセット: 4 * 11 18 | uint32_t t6; // オフセット: 4 * 12 19 | uint32_t a0; // オフセット: 4 * 13 20 | uint32_t a1; // オフセット: 4 * 14 21 | uint32_t a2; // オフセット: 4 * 15 22 | uint32_t a3; // オフセット: 4 * 16 23 | uint32_t a4; // オフセット: 4 * 17 24 | uint32_t a5; // オフセット: 4 * 18 25 | uint32_t a6; // オフセット: 4 * 19 26 | uint32_t a7; // オフセット: 4 * 20 27 | uint32_t s0; // オフセット: 4 * 21 28 | uint32_t s1; // オフセット: 4 * 22 29 | uint32_t s2; // オフセット: 4 * 23 30 | uint32_t s3; // オフセット: 4 * 24 31 | uint32_t s4; // オフセット: 4 * 25 32 | uint32_t s5; // オフセット: 4 * 26 33 | uint32_t s6; // オフセット: 4 * 27 34 | uint32_t s7; // オフセット: 4 * 28 35 | uint32_t s8; // オフセット: 4 * 29 36 | uint32_t s9; // オフセット: 4 * 30 37 | uint32_t s10; // オフセット: 4 * 31 38 | uint32_t s11; // オフセット: 4 * 32 39 | } __packed; 40 | 41 | void riscv32_handle_trap(struct riscv32_trap_frame *frame); 42 | -------------------------------------------------------------------------------- /servers/vm/pm.c: -------------------------------------------------------------------------------- 1 | #include "pm.h" 2 | #include "task.h" 3 | #include 4 | #include 5 | 6 | // タスクで使われていない仮想アドレス領域を返す。仮想アドレスは割り当てっぱなしで解放はできない。 7 | static uaddr_t valloc(struct task *task, size_t size) { 8 | if (task->valloc_next >= VALLOC_END) { 9 | return 0; 10 | } 11 | 12 | uaddr_t uaddr = task->valloc_next; 13 | task->valloc_next += ALIGN_UP(size, PAGE_SIZE); 14 | return uaddr; 15 | } 16 | 17 | // 物理アドレスをタスクのページテーブルにマップする。uaddrには割り当てた仮想アドレスが返る。 18 | error_t map_pages(struct task *task, size_t size, int map_flags, paddr_t paddr, 19 | uaddr_t *uaddr) { 20 | DEBUG_ASSERT(IS_ALIGNED(size, PAGE_SIZE)); 21 | *uaddr = valloc(task, size); 22 | if (!*uaddr) { 23 | return ERR_NO_RESOURCES; 24 | } 25 | 26 | // 各ページをマップする。 27 | for (offset_t offset = 0; offset < size; offset += PAGE_SIZE) { 28 | error_t err = 29 | sys_vm_map(task->tid, *uaddr + offset, paddr + offset, map_flags); 30 | if (err != OK) { 31 | WARN("vm_map failed: %s", err2str(err)); 32 | return err; 33 | } 34 | } 35 | 36 | return OK; 37 | } 38 | 39 | // 物理ページを割り当てて、タスクのページテーブルにマップする。uaddrには割り当てた仮想アドレスが返る。 40 | error_t alloc_pages(struct task *task, size_t size, int alloc_flags, 41 | int map_flags, paddr_t *paddr, uaddr_t *uaddr) { 42 | pfn_t pfn = sys_pm_alloc(task->tid, size, 43 | alloc_flags | PM_ALLOC_ALIGNED | PM_ALLOC_ZEROED); 44 | if (IS_ERROR(pfn)) { 45 | return pfn; 46 | } 47 | 48 | *paddr = PFN2PADDR(pfn); 49 | return map_pages(task, size, map_flags, *paddr, uaddr); 50 | } 51 | -------------------------------------------------------------------------------- /kernel/riscv32/debug.c: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | #include 3 | #include 4 | #include 5 | 6 | STATIC_ASSERT(IS_ALIGNED(KERNEL_STACK_SIZE, 2), 7 | "KERNEL_STACK_SIZE must be aligned to 2"); 8 | 9 | // カーネルスタックの下端を返す。 10 | static uint32_t *stack_bottom(void) { 11 | // 現在のスタックポインタの値を取得する 12 | uint32_t sp; 13 | __asm__ __volatile__("mv %0, sp" : "=r"(sp)); 14 | 15 | // カーネルスタックは常に KERNEL_STACK_SIZE の倍数のアドレスに配置される。そのため、 16 | // 下位ビットを切り捨てることでカーネルスタックの下端を求めることができる。 17 | return (uint32_t *) ALIGN_DOWN(sp, KERNEL_STACK_SIZE); 18 | } 19 | 20 | // 現在のカーネルスタックの下端にカーネルスタックの有効性をチェックするための値を書き込む。 21 | void stack_reset_current_canary(void) { 22 | stack_set_canary((uint32_t) stack_bottom()); 23 | } 24 | 25 | // カーネルスタックの下端にカーネルスタックの有効性をチェックするための値を書き込む。 26 | // オーバーフローが発生した場合にはこの値が書き換えられているはず。 27 | void stack_set_canary(uint32_t sp_bottom) { 28 | *((uint32_t *) sp_bottom) = STACK_CANARY_VALUE; 29 | } 30 | 31 | // カーネルスタックが有効か (オーバーフロー、有効な値か) をチェックする。また、ついでに 32 | // 割り込みハンドラが呼び出される際に満たしておくべき条件も確認する。割り込み周りは厄介な 33 | // バグが生まれやすいので、無意味に見えるチェックでもバグの早期発見に効果的である。 34 | void stack_check(void) { 35 | if (CPUVAR->magic != CPUVAR_MAGIC) { 36 | PANIC("invalid CPUVAR: addr=%p, magic=%x", CPUVAR, CPUVAR->magic); 37 | } 38 | 39 | if (!CPUVAR->online) { 40 | PANIC("CPUVAR->online is false (sepc=%p, stval=%p, scause=%x)", 41 | read_sepc(), read_stval(), read_scause()); 42 | } 43 | 44 | if (*stack_bottom() != STACK_CANARY_VALUE) { 45 | PANIC("kernel stack has been exhausted (sepc=%p, stval=%p, scause=%x)", 46 | read_sepc(), read_stval(), read_scause()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tools/mkbootfs.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | BootFSイメージを生成するスクリプト 4 | 5 | """ 6 | 7 | import argparse 8 | from glob import glob 9 | from pathlib import Path 10 | import struct 11 | import sys 12 | 13 | VERSION = 1 14 | PAGE_SIZE = 4096 15 | FILE_ENTRY_SIZE = 64 16 | BOOTFS_MAX_SIZE = 32 * 1024 * 1024 17 | 18 | 19 | def align_up(value, align): 20 | return (value + align - 1) & ~(align - 1) 21 | 22 | 23 | def main(): 24 | parser = argparse.ArgumentParser(description="Generates a bootfs.") 25 | parser.add_argument("-o", dest="output", help="The output file.") 26 | parser.add_argument("dir", help="The root directory.") 27 | args = parser.parse_args() 28 | 29 | # Write the boot binary and the file system header. 30 | files = glob(args.dir + "/*") 31 | image = struct.pack("HHHH", VERSION, 8, len(files), 0) 32 | 33 | # Append files. 34 | file_contents = bytes() 35 | file_off = align_up(len(image) + FILE_ENTRY_SIZE * len(files), PAGE_SIZE) 36 | for path in files: 37 | name = str(Path(path).stem) 38 | if len(name) >= 56: 39 | sys.exit(f"too long file name: {name}") 40 | 41 | data = open(path, "rb").read() 42 | file_contents += data 43 | image += struct.pack("56sII", name.encode("ascii"), file_off, len(data)) 44 | 45 | padding = align_up(len(file_contents), PAGE_SIZE) - len(file_contents) 46 | file_contents += struct.pack(f"{padding}x") 47 | file_off += len(data) + padding 48 | 49 | padding = align_up(len(image), PAGE_SIZE) - len(image) 50 | image += struct.pack(f"{padding}x") 51 | image += file_contents 52 | 53 | if len(image) > BOOTFS_MAX_SIZE: 54 | sys.exit(f"bootfs.bin is too big ({len(image) / 1024}KB)") 55 | 56 | with open(args.output, "wb") as f: 57 | f.write(image) 58 | 59 | 60 | if __name__ == "__main__": 61 | main() 62 | -------------------------------------------------------------------------------- /servers/tcpip/arp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ethernet.h" 3 | #include "ipv4.h" 4 | #include "mbuf.h" 5 | #include 6 | 7 | // ARPテーブルのエントリ数 8 | #define ARP_ENTRIES_MAX 128 9 | 10 | // ARPテーブルのエントリ 11 | struct arp_entry { 12 | bool in_use; // 使用中かどうか 13 | bool resolved; // MACアドレスが解決済みかどうか 14 | ipv4addr_t ipaddr; // IPv4アドレス (ネクストホップ) 15 | macaddr_t macaddr; // MACアドレス (resolved == trueのときのみ有効) 16 | list_t queue; // ARP応答待ちパケットリスト 17 | int time_accessed; // 最後にアクセスした時刻 18 | }; 19 | 20 | // ARPテーブル 21 | struct arp_table { 22 | struct arp_entry entries[ARP_ENTRIES_MAX]; 23 | }; 24 | 25 | // ARP応答待ちパケットリストのエントリ 26 | struct arp_queue_entry { 27 | list_elem_t next; // リストの次の要素へのポインタ 28 | ipv4addr_t dst; // 宛先IPv4アドレス (ネクストホップではなくIPヘッダの宛先) 29 | enum ether_type type; // ペイロードの種類 30 | mbuf_t payload; // ペイロード 31 | }; 32 | 33 | // ARPパケットの種類 34 | enum arp_opcode { 35 | ARP_OP_REQUEST = 1, // ARP要求 36 | ARP_OP_REPLY = 2, // ARP応答 37 | }; 38 | 39 | // ARPパケット 40 | struct arp_packet { 41 | uint16_t hw_type; // ハードウェアの種類 (イーサーネットの場合は1) 42 | uint16_t proto_type; // プロトコルの種類 (IPv4の場合は0x0800) 43 | uint8_t hw_size; // ハードウェアアドレスの長さ (MACアドレスの場合は6) 44 | uint8_t proto_size; // プロトコルアドレスの長さ (IPv4アドレスの場合は4) 45 | uint16_t opcode; // ARPパケットの種類 (ARP_OP_REQUEST または ARP_OP_REPLY) 46 | macaddr_t sender; // 送信元MACアドレス 47 | uint32_t sender_addr; // 送信元IPv4アドレス 48 | macaddr_t target; // 宛先MACアドレス 49 | uint32_t target_addr; // 宛先IPv4アドレス 50 | } __packed; 51 | 52 | bool arp_resolve(ipv4addr_t ipaddr, macaddr_t *macaddr); 53 | void arp_enqueue(enum ether_type type, ipv4addr_t dst, mbuf_t payload); 54 | void arp_register_macaddr(ipv4addr_t ipaddr, macaddr_t macaddr); 55 | void arp_request(ipv4addr_t addr); 56 | void arp_receive(mbuf_t pkt); 57 | -------------------------------------------------------------------------------- /libs/kernel/wasm/include/platform_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // types and defines not provided by os and used in the WAMR source code. 8 | typedef task_t korp_tid; 9 | typedef int korp_mutex; 10 | typedef int os_file_handle; 11 | typedef int os_raw_file_handle; 12 | typedef int korp_sem; 13 | typedef int korp_rwlock; 14 | typedef int os_dir_stream; 15 | typedef int korp_cond; 16 | typedef uintmax_t intptr_t; 17 | 18 | #define static_assert STATIC_ASSERT 19 | #define UINT16_MAX (65535U) 20 | #define INT32_MAX (2147483647) 21 | #define INT32_MIN (-2147483647-1) 22 | #define UINT32_MAX (4294967295U) 23 | #define INT64_MAX (9223372036854775807L) 24 | #define INT64_MIN (-9223372036854775807L-1) 25 | #define UINT64_MAX (18446744073709551615UL) 26 | #define UINTPTR_MAX UINT32_MAX 27 | 28 | // math functions not provided by os. 29 | // no need to give an implementation, since it is given by WAMR's math.c. 30 | int isnan(double x); 31 | int signbit(double x); 32 | float fabsf(float x); 33 | float ceilf(float x); 34 | float floorf(float x); 35 | float truncf(float x); 36 | float rintf(float x); 37 | float sqrtf(float x); 38 | double fabs(double x); 39 | double ceil(double x); 40 | double floor(double x); 41 | double trunc(double x); 42 | double rint(double x); 43 | double sqrt(double x); 44 | 45 | // functions used not provided by os and used in the WAMR source code. 46 | void abort(void); 47 | unsigned long int strtoul(const char *nptr, char **endptr, int base); 48 | unsigned long long int strtoull(const char *nptr, char **endptr, int base); 49 | double strtod(const char *nptr, char **endptr); 50 | float strtof(const char *nptr, char **endptr); 51 | int snprintf(char *str, size_t size, const char *format, ...); 52 | int vsnprintf(char *str, size_t size, const char *format, va_list ap); -------------------------------------------------------------------------------- /libs/user/printf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static char printbuf[256]; // 文字出力リングバッファ 6 | static int head = 0; // リングバッファの書き込み位置 7 | static int tail = 0; // リングバッファの読み込み位置 8 | 9 | // カーネルのシリアル出力システムコールを呼び出す。 10 | static void write_all(const char *str, size_t len) { 11 | while (len > 0) { 12 | int written_len = sys_serial_write(str, len); 13 | if (written_len <= 0) { 14 | // カーネルがエラーを返した。 15 | break; 16 | } 17 | 18 | len -= written_len; 19 | str += written_len; 20 | } 21 | } 22 | 23 | // リングバッファの内容をカーネルに出力する。 24 | void printf_flush(void) { 25 | if (tail < head) { 26 | write_all(&printbuf[tail], head - tail); 27 | tail = head; 28 | } else if (tail > head) { 29 | // 一旦ひとつの配列にコピーしてから出力する。一度に出力しないとマルチプロセッサ環境の 30 | // 場合に表示が乱れる。 31 | char tmp[sizeof(printbuf)]; 32 | memcpy(tmp, &printbuf[tail], sizeof(printbuf) - tail); 33 | memcpy(&tmp[sizeof(printbuf) - tail], &printbuf[0], head); 34 | 35 | write_all(tmp, sizeof(printbuf) - tail + head); 36 | tail = head; 37 | } 38 | } 39 | 40 | // 文字を出力する 41 | void printchar(char ch) { 42 | // リングバッファに文字を追加する 43 | printbuf[head] = ch; 44 | head = (head + 1) % sizeof(printbuf); 45 | 46 | // リングバッファが満杯か改行文字が来たら実際に出力する 47 | if ((head < tail && head + 1 == tail) || ch == '\n') { 48 | printf_flush(); 49 | } 50 | } 51 | 52 | // 文字列を出力する 53 | void printf(const char *fmt, ...) { 54 | va_list vargs; 55 | va_start(vargs, fmt); 56 | vprintf(fmt, vargs); 57 | va_end(vargs); 58 | } 59 | 60 | // パニック関数が呼ばれ、パニックメッセージを出力する前に呼ばれる。 61 | void panic_before_hook(void) { 62 | // 何もしない 63 | } 64 | 65 | // パニック関数が呼ばれ、パニックメッセージを出力した後に呼ばれる。 66 | __noreturn void panic_after_hook(void) { 67 | sys_task_exit(); 68 | } 69 | -------------------------------------------------------------------------------- /servers/hello_hinavm/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define M(field) offsetof(struct message, field) 8 | 9 | union hinavm_inst program[] = { 10 | I_LABEL(LABEL_0), // ラベル0: メインループ 11 | I_MOVI(R0, IPC_ANY), // RECV命令に渡す受信元を設定する 12 | I_RECV(R0, R0), // メッセージを受信する 13 | I_NE(R0, R0, 0), // エラーが発生したか? 14 | I_JMP_IF(LABEL_1, R0), // エラーが発生したら ラベル1 にジャンプ 15 | I_LDM32(R0, M(ping.value)), // 受信したメッセージの値を読み込む 16 | I_LDM16(R1, M(src)), // 受信したメッセージの送信元を読み込む 17 | I_PRINT(R0), // 受信したメッセージの値を出力する 18 | I_MOVI(R0, PING_REPLY_MSG), // 返信メッセージの種類をセットする 19 | I_STM32(M(type), R0), // 返信メッセージの種類を書き込む 20 | I_MOVI(R0, 42), // 返信メッセージの値をセットする 21 | I_STM32(R0, M(ping_reply.value)), // 返信メッセージの値を書き込む 22 | I_REPLY(R0, R1), // 返信する 23 | I_JMP(LABEL_0), // ループ 24 | 25 | I_LABEL(LABEL_1), // ラベル1: エラー時処理 26 | I_PRINT(R0), // エラー値を出力する 27 | I_JMP(LABEL_0), // メインループに戻る 28 | }; 29 | 30 | void main(void) { 31 | // HinaVMタスクを生成する 32 | task_t server = 33 | sys_hinavm("hinavm_server", (hinavm_inst_t *) program, 34 | sizeof(program) / sizeof(program[0]), task_self()); 35 | ASSERT_OK(server); 36 | 37 | // HinaVMタスクにメッセージを送信する 38 | struct message m; 39 | m.type = PING_MSG; 40 | m.ping.value = 123; 41 | ASSERT_OK(ipc_call(server, &m)); 42 | 43 | // HinaVMタスクからの返信メッセージの内容を表示する 44 | INFO("reply type: %s", msgtype2str(m.type)); 45 | INFO("reply value: %d", m.ping_reply.value); 46 | 47 | // HinaVMタスクを終了する 48 | ASSERT_OK(sys_task_destroy(server)); 49 | } 50 | -------------------------------------------------------------------------------- /kernel/riscv32/switch.S: -------------------------------------------------------------------------------- 1 | // 実行コンテキストを切り替える 2 | // 3 | // void riscv32_task_switch(uint32_t *prev_sp, uint32_t *next_sp); 4 | // ^^^^^^^ ^^^^^^^ 5 | // a0レジスタ a1レジスタ 6 | .align 4 7 | .global riscv32_task_switch 8 | riscv32_task_switch: 9 | addi sp, sp, -13 * 4 // 13個のレジスタを保存するためのスペースを確保 10 | 11 | // 呼び出し先保存 (callee-saved) レジスタをスタックに保存 12 | sw ra, 0 * 4(sp) // 戻り先アドレス (この関数の呼び出し元) 13 | sw s0, 1 * 4(sp) 14 | sw s1, 2 * 4(sp) 15 | sw s2, 3 * 4(sp) 16 | sw s3, 4 * 4(sp) 17 | sw s4, 5 * 4(sp) 18 | sw s5, 6 * 4(sp) 19 | sw s6, 7 * 4(sp) 20 | sw s7, 8 * 4(sp) 21 | sw s8, 9 * 4(sp) 22 | sw s9, 10 * 4(sp) 23 | sw s10, 11 * 4(sp) 24 | sw s11, 12 * 4(sp) 25 | 26 | sw sp, (a0) // 実行中タスクのスタックポインタを保存 27 | lw sp, (a1) // 次に実行するタスクのスタックポインタを復元 28 | 29 | // レジスタをスタックから復元 30 | lw ra, 0 * 4(sp) // 戻り先アドレス (この関数の呼び出し元) 31 | lw s0, 1 * 4(sp) 32 | lw s1, 2 * 4(sp) 33 | lw s2, 3 * 4(sp) 34 | lw s3, 4 * 4(sp) 35 | lw s4, 5 * 4(sp) 36 | lw s5, 6 * 4(sp) 37 | lw s6, 7 * 4(sp) 38 | lw s7, 8 * 4(sp) 39 | lw s8, 9 * 4(sp) 40 | lw s9, 10 * 4(sp) 41 | lw s10, 11 * 4(sp) 42 | lw s11, 12 * 4(sp) 43 | 44 | addi sp, sp, 13 * 4 // 12個のレジスタを取り出したので、スタックポインタを更新 45 | ret // 次に実行するタスクの実行を再開する 46 | 47 | // カーネルタスクのエントリポイント 48 | .align 4 49 | .global riscv32_kernel_entry_trampoline 50 | riscv32_kernel_entry_trampoline: 51 | // スタックから引数を取り出して、エントリーポイントにジャンプする 52 | lw a0, 0 * 4(sp) // a0 53 | lw a1, 1 * 4(sp) // ip 54 | add sp, sp, 2 * 4 // 取り出した引数だけ解放する 55 | jalr a1 56 | 57 | // 戻ってくるべきではない 58 | 1: 59 | j 1b 60 | 61 | // ユーザータスクのエントリポイント 62 | .align 4 63 | .global riscv32_user_entry_trampoline 64 | riscv32_user_entry_trampoline: 65 | // スタックから引数を取り出して、riscv32_user_entry関数にジャンプする 66 | lw a0, 0 * 4(sp) // ip 67 | j riscv32_user_entry 68 | -------------------------------------------------------------------------------- /kernel/arch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct cpuvar; 5 | #include 6 | 7 | #define CPUVAR_MAGIC 0xbeefbeef 8 | #define CPUVAR (arch_cpuvar_get()) 9 | 10 | struct task; 11 | 12 | struct cpuvar { 13 | struct arch_cpuvar arch; 14 | int id; 15 | bool online; 16 | unsigned ipi_pending; 17 | struct task *idle_task; 18 | struct task *current_task; 19 | unsigned magic; 20 | }; 21 | 22 | #define IPI_TLB_FLUSH (1 << 0) 23 | #define IPI_RESCHEDULE (1 << 1) 24 | 25 | struct memory_map_entry { 26 | paddr_t paddr; 27 | size_t size; 28 | }; 29 | 30 | #define NUM_MEMORY_MAP_ENTRIES_MAX 8 31 | 32 | struct memory_map { 33 | struct memory_map_entry frees[NUM_MEMORY_MAP_ENTRIES_MAX]; 34 | struct memory_map_entry devices[NUM_MEMORY_MAP_ENTRIES_MAX]; 35 | int num_frees; 36 | int num_devices; 37 | }; 38 | 39 | struct bootinfo { 40 | paddr_t boot_elf; 41 | struct memory_map memory_map; 42 | }; 43 | 44 | ARCH_TYPES_STATIC_ASSERTS 45 | 46 | void arch_serial_write(char ch); 47 | int arch_serial_read(void); 48 | error_t arch_vm_init(struct arch_vm *vm); 49 | void arch_vm_destroy(struct arch_vm *vm); 50 | error_t arch_vm_map(struct arch_vm *vm, vaddr_t vaddr, paddr_t paddr, 51 | unsigned attrs); 52 | error_t arch_vm_unmap(struct arch_vm *vm, vaddr_t vaddr); 53 | vaddr_t arch_paddr_to_vaddr(paddr_t paddr); 54 | bool arch_is_mappable_uaddr(uaddr_t uaddr); 55 | error_t arch_task_init(struct task *task, uaddr_t ip, vaddr_t kernel_entry, 56 | void *arg); 57 | void arch_task_destroy(struct task *task); 58 | void arch_task_switch(struct task *prev, struct task *next); 59 | void arch_init(void); 60 | void arch_init_percpu(void); 61 | void arch_idle(void); 62 | void arch_send_ipi(unsigned ipi); 63 | void arch_memcpy_from_user(void *dst, __user const void *src, size_t len); 64 | void arch_memcpy_to_user(__user void *dst, const void *src, size_t len); 65 | error_t arch_irq_enable(unsigned irq); 66 | error_t arch_irq_disable(unsigned irq); 67 | __noreturn void arch_shutdown(void); 68 | -------------------------------------------------------------------------------- /servers/tcpip/ethernet.c: -------------------------------------------------------------------------------- 1 | #include "ethernet.h" 2 | #include "arp.h" 3 | #include "device.h" 4 | #include "ipv4.h" 5 | #include "main.h" 6 | #include "mbuf.h" 7 | #include 8 | #include 9 | #include 10 | 11 | // イーサーネットフレームを送信する。 12 | void ethernet_transmit(enum ether_type type, ipv4addr_t dst, mbuf_t payload) { 13 | // パケットをどこに送るかを決定する。 14 | ipv4addr_t next_hop = device_get_next_hop(dst); 15 | if (device_get_next_hop(dst) == IPV4_ADDR_UNSPECIFIED) { 16 | WARN("ethernet_transmit: no route to %pI4", dst); 17 | mbuf_delete(payload); 18 | return; 19 | } 20 | 21 | // 宛先のMACアドレスを取得する。 22 | macaddr_t dst_macaddr; 23 | if (!arp_resolve(next_hop, &dst_macaddr)) { 24 | // 宛先のMACアドレスがARPテーブルに見つからなかったため、即座に送信できない。 25 | // ARPテーブルの応答待ちキューにパケットを挿入し、ARPリクエストを送信して処理を 26 | // 終える。 27 | arp_enqueue(type, next_hop, payload); 28 | arp_request(next_hop); 29 | return; 30 | } 31 | 32 | // 宛先MACアドレスが見つかったので、パケットを送信する。 33 | struct ethernet_header header; 34 | memcpy(header.dst, dst_macaddr, MACADDR_LEN); 35 | memcpy(header.src, device_get_macaddr(), MACADDR_LEN); 36 | header.type = hton16(type); 37 | mbuf_t pkt = mbuf_new(&header, sizeof(header)); 38 | mbuf_append(pkt, payload); 39 | 40 | // ネットワークデバイスドライバにパケット送信を依頼する。 41 | callback_ethernet_transmit(pkt); 42 | } 43 | 44 | // イーサーネットフレームの受信処理。 45 | void ethernet_receive(const void *pkt, size_t len) { 46 | mbuf_t m = mbuf_new(pkt, len); 47 | 48 | // イーサーネットフレームのヘッダを取り出す。 49 | struct ethernet_header header; 50 | if (mbuf_read(&m, &header, sizeof(header)) != sizeof(header)) { 51 | return; 52 | } 53 | 54 | uint16_t type = ntoh16(header.type); 55 | switch (type) { 56 | case ETHER_TYPE_ARP: 57 | // ARPパケット 58 | arp_receive(m); 59 | break; 60 | case ETHER_TYPE_IPV4: 61 | // IPv4パケット 62 | ipv4_receive(m); 63 | break; 64 | default: 65 | WARN("unknown ethernet type: %x", type); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WasmOS 2 |

3 | 4 |

5 | 6 | 7 | WasmOS is a microkernel developed based on [HinaOS](https://github.com/nuta/microkernel-book). 8 | It implements a WebAssembly (Wasm) "userland", and all Wasm binaries are executed in Ring 0 using [WAMR](https://github.com/bytecodealliance/wasm-micro-runtime). 9 | See my [blog](https://medium.com/@r1ru/wasmos-a-proof-of-concept-microkernel-that-runs-webassembly-natively-850043cad121) for more details. 10 | 11 | ![WasmOS_shell](https://github.com/r1ru/WasmOS/assets/92210252/67e978a4-9bd9-4d6f-8f15-8efc8de60989) 12 | 13 | 14 | ## Quickstart 15 | First clone this repository, remember to add the --recursive option as Wasm uses [WAMR](https://github.com/bytecodealliance/wasm-micro-runtime) as a submodule. 16 | ``` 17 | git clone --recursive https://github.com/r1ru/WasmOS 18 | ``` 19 | Next, build and run WasmOS. The easiest way is to use [Dev Container](https://code.visualstudio.com/docs/devcontainers/containers). Launch the container and execute following commands. 20 | If you're not using it, please refer to the [Dockerfile](https://github.com/r1ru/wasmos/blob/main/.devcontainer/Dockerfile), install the required packages, and build it. 21 | 22 | ``` 23 | make # Build WasmOS 24 | make V=1 # Build WasmOS (Output detailed build logs) 25 | make run # Run WasmOS in qemu (single core) 26 | make run CPUS=4 # Run WasmOS in qemu (4 cores) 27 | ``` 28 | If it starts successfully, the shell server is launched and you can execute following commands. 29 | 30 | ``` 31 | start hello # Run a helllo-world program 32 | start wasm_ping # Run a Wasm binary that uses message passing APIs 33 | start wasm_webapi # Run a toy web server (Wasm binary). Access localhost:1234 to see the page 34 | ``` 35 | 36 | ## Contributing 37 | We accept bug reports, feature requests, and patches on [GitHub](https://github.com/r1ru/WasmOS). 38 | 39 | ## Similar Projects 40 | - [Nebulet](https://github.com/nebulet/nebulet) 41 | - [Wasmachine](https://ieeexplore.ieee.org/document/9156135) 42 | - [kernel-wasm](https://github.com/wasmerio/kernel-wasm) 43 | -------------------------------------------------------------------------------- /libs/common/ubsan.c: -------------------------------------------------------------------------------- 1 | // 2 | // Undefined Behavior Sanitizer (UBSan) のランタイムライブラリ 3 | // 4 | // UBSanを使うことで、コンパイル時には検出できない未定義動作を実行時に検出する。 5 | // 未定義動作が検出されると以下で定義されているハンドラ関数たちが呼び出される。 6 | // 7 | // https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html 8 | // 9 | #include "ubsan.h" 10 | #include "print.h" 11 | 12 | static inline void report_ubsan_event(const char *event) { 13 | PANIC("detected an undefined behavior: %s (ip=%p)", event, 14 | __builtin_return_address(0)); 15 | } 16 | 17 | void __ubsan_handle_type_mismatch_v1(struct ubsan_mismatch_data_v1 *data, 18 | vaddr_t ptr) { 19 | if (!ptr) { 20 | report_ubsan_event("NULL pointer dereference"); 21 | } else if (data->align && !IS_ALIGNED(ptr, 1 << data->align)) { 22 | WARN("UBSan: %s:%d: pointer %p is not aligned to %d", data->loc.file, 23 | data->loc.line, ptr, 1 << data->align); 24 | } else { 25 | PANIC("UBSan: %s:%d: pointer %p is not large enough for %s", 26 | data->loc.file, data->loc.line, ptr, data->type->name); 27 | } 28 | } 29 | 30 | void __ubsan_handle_add_overflow(void) { 31 | report_ubsan_event("add_overflow"); 32 | } 33 | void __ubsan_handle_sub_overflow(void) { 34 | report_ubsan_event("sub overflow"); 35 | } 36 | void __ubsan_handle_mul_overflow(void) { 37 | report_ubsan_event("mul overflow"); 38 | } 39 | void __ubsan_handle_divrem_overflow(void) { 40 | report_ubsan_event("divrem overflow"); 41 | } 42 | void __ubsan_handle_negate_overflow(void) { 43 | report_ubsan_event("negate overflow"); 44 | } 45 | void __ubsan_handle_float_cast_overflow(void) { 46 | report_ubsan_event("float cast overflow"); 47 | } 48 | void __ubsan_handle_pointer_overflow(void) { 49 | report_ubsan_event("pointer overflow"); 50 | } 51 | void __ubsan_handle_out_of_bounds(void) { 52 | report_ubsan_event("out of bounds"); 53 | } 54 | void __ubsan_handle_shift_out_of_bounds(void) { 55 | report_ubsan_event("shift out of bounds"); 56 | } 57 | void __ubsan_handle_builtin_unreachable(void) { 58 | report_ubsan_event("builtin unreachable"); 59 | } 60 | void __ubsan_handle_invalid_builtin(void) { 61 | report_ubsan_event("invalid builtin"); 62 | } 63 | -------------------------------------------------------------------------------- /libs/user/riscv32/arch_mmio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | // 指定されたメモリアドレスに1バイトを書き込む。必ず1バイト幅のメモリアクセス命令を使う。 6 | static inline void mmio_write8(vaddr_t vaddr, uint8_t val) { 7 | __asm__ __volatile__("sb %0, (%1)" ::"r"(val), "r"(vaddr) : "memory"); 8 | } 9 | 10 | // 指定されたメモリアドレスに2バイトを書き込む。必ず2バイト幅のメモリアクセス命令を使う。 11 | static inline void mmio_write16(vaddr_t vaddr, uint32_t val) { 12 | __asm__ __volatile__("sh %0, (%1)" ::"r"(val), "r"(vaddr) : "memory"); 13 | } 14 | 15 | // 指定されたメモリアドレスに4バイトを書き込む。必ず4バイト幅のメモリアクセス命令を使う。 16 | static inline void mmio_write32(vaddr_t vaddr, uint32_t val) { 17 | __asm__ __volatile__("sw %0, (%1)" ::"r"(val), "r"(vaddr) : "memory"); 18 | } 19 | 20 | // 指定されたメモリアドレスから1バイトを読み込む。必ず1バイト幅のメモリアクセス命令を使う。 21 | static inline uint8_t mmio_read8(vaddr_t vaddr) { 22 | uint8_t val; 23 | __asm__ __volatile__("lb %0, (%1)" : "=r"(val) : "r"(vaddr)); 24 | return val; 25 | } 26 | 27 | // 指定されたメモリアドレスから2バイトを読み込む。必ず2バイト幅のメモリアクセス命令を使う。 28 | static inline uint32_t mmio_read16(vaddr_t vaddr) { 29 | uint32_t val; 30 | __asm__ __volatile__("lh %0, (%1)" : "=r"(val) : "r"(vaddr)); 31 | return val; 32 | } 33 | 34 | // 指定されたメモリアドレスから4バイトを読み込む。必ず4バイト幅のメモリアクセス命令を使う。 35 | static inline uint32_t mmio_read32(vaddr_t vaddr) { 36 | uint32_t val; 37 | __asm__ __volatile__("lw %0, (%1)" : "=r"(val) : "r"(vaddr)); 38 | return val; 39 | } 40 | 41 | // 指定されたメモリアドレスから16ビット整数 (リトルエンディアン) を読み込み、実行環境の 42 | // エンディアンに変換して返す。 43 | static inline uint16_t mmio_read16le(vaddr_t addr) { 44 | return from_le16(mmio_read16(addr)); 45 | } 46 | 47 | // 指定されたメモリアドレスから32ビット整数 (リトルエンディアン) を読み込み、実行環境の 48 | // エンディアンに変換して返す。 49 | static inline uint32_t mmio_read32le(vaddr_t addr) { 50 | return from_le32(mmio_read32(addr)); 51 | } 52 | 53 | // 指定されたメモリアドレスに16ビット整数 (リトルエンディアン) を書き込む。実行環境の 54 | // エンディアンをリトルエンディアンに変換してから書き込む。 55 | static inline void mmio_write16le(vaddr_t addr, uint16_t val) { 56 | mmio_write16(addr, into_le16(val)); 57 | } 58 | 59 | // 指定されたメモリアドレスに32ビット整数 (リトルエンディアン) を書き込む。実行環境の 60 | // エンディアンをリトルエンディアンに変換してから書き込む。 61 | static inline void mmio_write32le(vaddr_t addr, uint32_t val) { 62 | mmio_write32(addr, into_le32(val)); 63 | } 64 | -------------------------------------------------------------------------------- /libs/user/dmabuf.c: -------------------------------------------------------------------------------- 1 | #include "dmabuf.h" 2 | #include "driver.h" 3 | #include 4 | #include 5 | #include 6 | 7 | // 渡された物理アドレスがdmabufの管理下にあるかどうかをチェックする 8 | static void check_paddr(dmabuf_t dmabuf, paddr_t paddr) { 9 | ASSERT(dmabuf->paddr <= paddr); 10 | ASSERT(paddr < dmabuf->paddr + dmabuf->entry_size * dmabuf->num_entries); 11 | } 12 | 13 | // DMAバッファ管理構造体を生成する。entry_sizeはバッファの1つの要素のサイズ。num_entriesは 14 | // バッファの要素数。失敗時にはNULLを返す。 15 | dmabuf_t dmabuf_create(size_t entry_size, size_t num_entries) { 16 | // 返すアドレスがアライメントされることを保証 17 | entry_size = ALIGN_UP(entry_size, 4); 18 | 19 | // バッファの管理情報を確保し、初期化する 20 | struct dmabuf *dmabuf = malloc(sizeof(struct dmabuf)); 21 | dmabuf->entry_size = entry_size; 22 | dmabuf->num_entries = num_entries; 23 | dmabuf->used = malloc(sizeof(bool) * num_entries); 24 | memset(dmabuf->used, 0, sizeof(bool) * num_entries); 25 | 26 | // DMAバッファを確保する 27 | error_t err = driver_alloc_pages( 28 | ALIGN_UP(entry_size * num_entries, PAGE_SIZE), 29 | PAGE_READABLE | PAGE_WRITABLE, &dmabuf->uaddr, &dmabuf->paddr); 30 | if (err != OK) { 31 | WARN("failed to allocate a DMA region for dmabuf: %s", err2str(err)); 32 | return NULL; 33 | } 34 | 35 | DEBUG_ASSERT(dmabuf->paddr != 0); 36 | return dmabuf; 37 | } 38 | 39 | // DMAバッファをひとつ割り当てる。失敗時にはNULLを返す。 40 | void *dmabuf_alloc(dmabuf_t dmabuf, paddr_t *paddr) { 41 | for (size_t i = 0; i < dmabuf->num_entries; i++) { 42 | if (!dmabuf->used[i]) { 43 | dmabuf->used[i] = true; 44 | offset_t offset = i * dmabuf->entry_size; 45 | *paddr = dmabuf->paddr + offset; 46 | return (void *) (dmabuf->uaddr + offset); 47 | } 48 | } 49 | return NULL; 50 | } 51 | 52 | // dmabuf_alloc関数で割り当てた物理アドレスから対応する仮想アドレスを得る。 53 | void *dmabuf_p2v(dmabuf_t dmabuf, paddr_t paddr) { 54 | check_paddr(dmabuf, paddr); 55 | return (void *) (dmabuf->uaddr + paddr - dmabuf->paddr); 56 | } 57 | 58 | // dmabuf_alloc関数で割り当てたDMAバッファを解放する。 59 | void dmabuf_free(dmabuf_t dmabuf, paddr_t paddr) { 60 | check_paddr(dmabuf, paddr); 61 | size_t index = (paddr - dmabuf->paddr) / dmabuf->entry_size; 62 | dmabuf->used[index] = false; 63 | } 64 | -------------------------------------------------------------------------------- /kernel/task.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "arch.h" 3 | #include "hinavm.h" 4 | #include "wasmvm.h" 5 | #include "interrupt.h" 6 | #include 7 | #include 8 | #include 9 | 10 | // タスクの最大連続実行時間 11 | #define TASK_QUANTUM (20 * (TICK_HZ / 1000)) /* 20ミリ秒 */ 12 | 13 | // 現在のCPUのアイドルタスク (struct task *) 14 | #define IDLE_TASK (arch_cpuvar_get()->idle_task) 15 | // 実行中タスク (struct task *) 16 | #define CURRENT_TASK (arch_cpuvar_get()->current_task) 17 | 18 | // タスクの状態 19 | #define TASK_UNUSED 0 20 | #define TASK_RUNNABLE 1 21 | #define TASK_BLOCKED 2 22 | 23 | // タスク管理構造体 24 | struct task { 25 | struct arch_task arch; // CPU依存のタスク情報 26 | struct arch_vm vm; // ページテーブル 27 | task_t tid; // タスクID 28 | char name[TASK_NAME_LEN]; // タスク名 29 | int state; // タスクの状態 30 | bool destroyed; // タスクが削除されている途中かどうか 31 | struct task *pager; // ページャータスク 32 | unsigned timeout; // タイムアウトの残り時間 33 | int ref_count; // タスクが参照されている数 (ゼロでないと削除不可) 34 | unsigned quantum; // タスクの残りクォンタム 35 | list_elem_t waitqueue_next; // 各種待ちリストの次の要素へのポインタ 36 | list_elem_t next; // 全タスクリストの次の要素へのポインタ 37 | list_t senders; // このタスクへの送信待ちタスクリスト 38 | task_t wait_for; // このタスクへメッセージ送信ができるタスクID 39 | // (IPC_ANYの場合は全て) 40 | list_t pages; // 利用中メモリページのリスト 41 | notifications_t notifications; // 受信済みの通知 42 | struct message m; // メッセージの一時保存領域 43 | }; 44 | 45 | extern list_t active_tasks; 46 | 47 | struct task *task_find(task_t tid); 48 | task_t task_create(const char *name, uaddr_t ip, struct task *pager); 49 | task_t hinavm_create(const char *name, hinavm_inst_t *insts, uint32_t num_insts, 50 | struct task *pager); 51 | task_t wasmvm_create(const char *name, uint8_t *wasm, uint32_t size, 52 | struct task *pager); 53 | error_t task_destroy(struct task *task); 54 | __noreturn void task_exit(int exception); 55 | void task_resume(struct task *task); 56 | void task_block(struct task *task); 57 | void task_switch(void); 58 | void task_dump(void); 59 | void task_init_percpu(void); 60 | -------------------------------------------------------------------------------- /libs/common/endian.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // ビッグエンディアンとリトルエンディアンの相互変換 (16ビット) 5 | static inline uint16_t swap16(uint16_t x) { 6 | return ((x & 0xff00) >> 8) | ((x & 0x00ff) << 8); 7 | } 8 | 9 | // ビッグエンディアンとリトルエンディアンの相互変換 (32ビット) 10 | static inline uint32_t swap32(uint32_t x) { 11 | return ((x & 0xff000000) >> 24) | ((x & 0x00ff0000) >> 8) 12 | | ((x & 0x0000ff00) << 8) | ((x & 0x000000ff) << 24); 13 | } 14 | 15 | // CPUのエンディアンを調べる 16 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 17 | static inline uint16_t ntoh16(uint16_t x) { 18 | return x; // ネットワークバイトオーダーはビッグエンディアンなのでそのまま 19 | } 20 | static inline uint32_t ntoh32(uint32_t x) { 21 | return x; // ネットワークバイトオーダーはビッグエンディアンなのでそのまま 22 | } 23 | static inline uint16_t hton16(uint16_t x) { 24 | return x; // ネットワークバイトオーダーはビッグエンディアンなのでそのまま 25 | } 26 | static inline uint32_t hton32(uint32_t x) { 27 | return x; // ネットワークバイトオーダーはビッグエンディアンなのでそのまま 28 | } 29 | static inline uint16_t into_le16(uint16_t x) { 30 | return swap16(x); // ビックエンディアン -> リトルエンディアン 31 | } 32 | static inline uint32_t into_le32(uint32_t x) { 33 | return swap32(x); // ビックエンディアン -> リトルエンディアン 34 | } 35 | static inline uint16_t from_le16(uint16_t x) { 36 | return swap16(x); // リトルエンディアン -> ビックエンディアン 37 | } 38 | static inline uint32_t from_le32(uint32_t x) { 39 | return swap32(x); // リトルエンディアン -> ビックエンディアン 40 | } 41 | #else 42 | static inline uint16_t ntoh16(uint16_t x) { 43 | return swap16(x); // ネットワークバイトオーダー -> リトルエンディアン 44 | } 45 | static inline uint32_t ntoh32(uint32_t x) { 46 | return swap32(x); // ネットワークバイトオーダー -> リトルエンディアン 47 | } 48 | static inline uint16_t hton16(uint16_t x) { 49 | return swap16(x); // リトルエンディアン -> ネットワークバイトオーダー 50 | } 51 | static inline uint32_t hton32(uint32_t x) { 52 | return swap32(x); // リトルエンディアン -> ネットワークバイトオーダー 53 | } 54 | static inline uint16_t into_le16(uint16_t x) { 55 | return x; // リトルエンディアンなのでそのまま 56 | } 57 | static inline uint32_t into_le32(uint32_t x) { 58 | return x; // リトルエンディアンなのでそのまま 59 | } 60 | static inline uint64_t into_le64(uint64_t x) { 61 | return x; // リトルエンディアンなのでそのまま 62 | } 63 | static inline uint16_t from_le16(uint16_t x) { 64 | return x; // リトルエンディアンなのでそのまま 65 | } 66 | static inline uint32_t from_le32(uint32_t x) { 67 | return x; // リトルエンディアンなのでそのまま 68 | } 69 | #endif 70 | -------------------------------------------------------------------------------- /servers/proxy/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | // Find {tcpip, echo} server. 8 | task_t tcpip_server = ipc_lookup("tcpip"); 9 | task_t echo_server = ipc_lookup("echo"); 10 | 11 | // Listen on port 80. 12 | struct message m; 13 | m.type = TCPIP_LISTEN_MSG; 14 | m.tcpip_listen.listen_port = 80; 15 | ASSERT_OK(ipc_call(tcpip_server, &m)); 16 | 17 | while(true) { 18 | ASSERT_OK(ipc_recv(IPC_ANY, &m)); 19 | 20 | DBG( 21 | "received message from #%d: %s (%x)", m.src, 22 | msgtype2str(m.type), 23 | m.type 24 | ); 25 | 26 | switch(m.type) { 27 | case TCPIP_CLOSED_MSG: { 28 | // Destroy socket. 29 | m.type = TCPIP_DESTROY_MSG; 30 | m.tcpip_destroy.sock = m.tcpip_closed.sock; 31 | ASSERT_OK(ipc_call(tcpip_server, &m)); 32 | break; 33 | } 34 | case TCPIP_DATA_MSG: { 35 | int sock = m.tcpip_data.sock; 36 | 37 | // Read the received data. 38 | m.type = TCPIP_READ_MSG; 39 | m.tcpip_read.sock = sock; 40 | ASSERT_OK(ipc_call(tcpip_server, &m)); 41 | 42 | char data[1024]; 43 | size_t data_len = m.tcpip_read_reply.data_len; 44 | memcpy(data, m.tcpip_read_reply.data, data_len); 45 | 46 | DBG("received data (%u bytes) from socket %d", data_len, sock); 47 | 48 | // Relay to the echo server. 49 | m.type = ECHO_MSG; 50 | m.echo.data_len = data_len; 51 | memcpy(m.echo.data, data, data_len); 52 | ASSERT_OK(ipc_call(echo_server, &m)); 53 | ASSERT(m.echo_reply.data_len == data_len); 54 | 55 | // Replay to the client. 56 | memcpy(data, m.echo_reply.data, data_len); 57 | m.type = TCPIP_WRITE_MSG; 58 | m.tcpip_write.sock = sock; 59 | memcpy(m.tcpip_write.data, data, data_len); 60 | m.tcpip_write.data_len = data_len; 61 | ASSERT_OK(ipc_call(tcpip_server, &m)); 62 | break; 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /tests/test_commands.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Test shell server commands. 4 | 5 | """ 6 | 7 | import http 8 | import http.server 9 | import threading 10 | 11 | def test_echo(run_wasmos): 12 | r = run_wasmos("echo howdy") 13 | assert "howdy" in r.log 14 | 15 | def test_start(run_wasmos): 16 | r = run_wasmos("start hello") 17 | assert "Hello World!" in r.log 18 | 19 | def test_hinavm(run_wasmos): 20 | r = run_wasmos("start hello_hinavm") 21 | assert "hinavm_server: pc=7: 123" in r.log 22 | assert "reply value: 42" in r.log 23 | 24 | def test_ls(run_wasmos): 25 | r = run_wasmos("ls") 26 | assert "hello.txt" in r.log 27 | 28 | def test_cat(run_wasmos): 29 | r = run_wasmos("cat hello.txt") 30 | assert "Hello World from HinaFS" in r.log 31 | 32 | def test_write(run_wasmos): 33 | r = run_wasmos("write lfg.txt LFG; ls; cat lfg.txt") 34 | assert '[FILE] "lfg.txt"' in r.log 35 | assert '[shell] LFG' in r.log 36 | 37 | def test_mkdir(run_wasmos): 38 | r = run_wasmos("mkdir new_dir; ls") 39 | assert '[DIR ] "new_dir"' in r.log 40 | 41 | def test_delete_file(run_wasmos): 42 | r = run_wasmos("write lfg.txt LFG; delete lfg.txt; ls") 43 | assert '[FILE] "lfg.txt"' not in r.log 44 | 45 | def test_delete_dir(run_wasmos): 46 | r = run_wasmos("mkdir new_dir; delete new_dir; ls") 47 | assert '[DIR ] "new_dir"' not in r.log 48 | 49 | def test_shutdow(run_wasmos): 50 | r = run_wasmos("shutdown") 51 | assert "[shell] shutting down..." in r.log 52 | 53 | def test_hinavm(run_wasmos): 54 | r = run_wasmos("start hello_hinavm") 55 | assert "hinavm_server: pc=7: 123" in r.log 56 | assert "reply value: 42" in r.log 57 | 58 | def test_http(run_wasmos): 59 | class TeapotServer(http.server.BaseHTTPRequestHandler): 60 | def do_GET(self): 61 | self.send_response(418) 62 | self.end_headers() 63 | self.wfile.write("Hello HTTP!".encode("utf-8")) 64 | 65 | httpd = http.server.HTTPServer(("", 1234), TeapotServer) 66 | httpd_thread = threading.Thread(target=lambda: httpd.serve_forever(), daemon=True) 67 | httpd_thread.start() 68 | 69 | r = run_wasmos( 70 | "http http://10.0.2.100:1234/teapot", 71 | qemu_net0_options=["guestfwd=tcp:10.0.2.100:1234-tcp:127.0.0.1:1234"] 72 | ) 73 | assert "Hello HTTP!" in r.log 74 | 75 | httpd.shutdown() 76 | httpd.server_close() 77 | httpd_thread.join() -------------------------------------------------------------------------------- /kernel/printk.c: -------------------------------------------------------------------------------- 1 | #include "printk.h" 2 | #include "arch.h" 3 | #include "task.h" 4 | #include 5 | #include 6 | #include 7 | 8 | // UARTからのデータを待っているタスクのリスト 9 | static list_t serial_readers = LIST_INIT(serial_readers); 10 | // UARTからの入力データのリングバッファと、その読み書き位置 11 | static char input[128]; 12 | static int input_rp = 0; 13 | static int input_wp = 0; 14 | 15 | // UARTからの割り込みハンドラ 16 | void handle_serial_interrupt(void) { 17 | while (true) { 18 | // 1文字読み込む 19 | int ch = arch_serial_read(); 20 | if (ch == -1) { 21 | // もう読み込めるデータがない 22 | break; 23 | } 24 | 25 | // Ctrl-P: デバッグ情報を出力する 26 | // https://en.wikipedia.org/wiki/Control_character 27 | if (ch == 'P' - '@' /* 0x10 */) { 28 | task_dump(); 29 | continue; 30 | } 31 | 32 | // バッファに書き込む 33 | input[input_wp] = ch; 34 | input_wp = (input_wp + 1) % sizeof(input); 35 | 36 | // バッファが一杯になったら、古いデータを捨てる 37 | if (input_rp == input_wp) { 38 | input_rp = (input_rp + 1) % sizeof(input); 39 | } 40 | } 41 | 42 | // serial_read関数でブロックしているタスクを再開する 43 | LIST_FOR_EACH (task, &serial_readers, struct task, waitqueue_next) { 44 | list_remove(&task->waitqueue_next); 45 | task_resume(task); 46 | } 47 | } 48 | 49 | // UARTからの入力を読み込む。QEMUは標準入力 (一般にキーボード入力) がUARTに繋がっている。 50 | int serial_read(char *buf, int max_len) { 51 | int len = 0; 52 | while (true) { 53 | // バッファに貯まったデータを全て読み込む 54 | for (; len < max_len && input_rp != input_wp; len++) { 55 | char ch = input[input_rp]; 56 | input_rp = (input_rp + 1) % sizeof(input); 57 | buf[len] = ch; 58 | } 59 | 60 | // 1文字以上読み込めたら、それを即座に返す 61 | if (len > 0) { 62 | break; 63 | } 64 | 65 | // 1文字も読み込めなかったら、タスクをブロックしてUARTからの割り込みを待つ 66 | list_push_back(&serial_readers, &CURRENT_TASK->waitqueue_next); 67 | task_block(CURRENT_TASK); 68 | task_switch(); 69 | } 70 | 71 | return len; 72 | } 73 | 74 | // カーネル内部でのみ使用するputchar実装。UARTに出力する。 75 | void printchar(char ch) { 76 | arch_serial_write(ch); 77 | } 78 | 79 | // カーネル内部でのみ使用するprintf実装。UARTに出力する。 80 | void printf(const char *fmt, ...) { 81 | va_list vargs; 82 | va_start(vargs, fmt); 83 | vprintf(fmt, vargs); 84 | va_end(vargs); 85 | } 86 | -------------------------------------------------------------------------------- /kernel/interrupt.c: -------------------------------------------------------------------------------- 1 | #include "interrupt.h" 2 | #include "arch.h" 3 | #include "ipc.h" 4 | #include "task.h" 5 | #include 6 | 7 | // 割り込み通知を受け付けるタスクの一覧。 8 | static struct task *irq_listeners[IRQ_MAX]; 9 | // 起動してからの経過時間。単位はタイマー割り込みの周期 (TICK_HZ) に依存する。 10 | unsigned uptime_ticks = 0; 11 | 12 | // 割り込み通知を受け付けるようにする。 13 | error_t irq_listen(struct task *task, unsigned irq) { 14 | if (irq >= IRQ_MAX) { 15 | return ERR_INVALID_ARG; 16 | } 17 | 18 | if (irq_listeners[irq] != NULL) { 19 | return ERR_ALREADY_USED; 20 | } 21 | 22 | error_t err = arch_irq_enable(irq); 23 | if (err != OK) { 24 | return err; 25 | } 26 | 27 | irq_listeners[irq] = task; 28 | return OK; 29 | } 30 | 31 | // 割り込み通知を受け付けないようにする。 32 | error_t irq_unlisten(struct task *task, unsigned irq) { 33 | if (irq >= IRQ_MAX) { 34 | return ERR_INVALID_ARG; 35 | } 36 | 37 | if (irq_listeners[irq] != task) { 38 | return ERR_NOT_ALLOWED; 39 | } 40 | 41 | error_t err = arch_irq_disable(irq); 42 | if (err != OK) { 43 | return err; 44 | } 45 | 46 | irq_listeners[irq] = NULL; 47 | return OK; 48 | } 49 | 50 | // ハードウェア割り込みハンドラ (タイマー割り込み以外) 51 | void handle_interrupt(unsigned irq) { 52 | if (irq >= IRQ_MAX) { 53 | WARN("invalid IRQ: %u", irq); 54 | return; 55 | } 56 | 57 | // 割り込みを受け付けるタスクを取得し、通知を送る。 58 | struct task *task = irq_listeners[irq]; 59 | if (!task) { 60 | WARN("unhandled IRQ %u", irq); 61 | return; 62 | } 63 | 64 | notify(task, NOTIFY_IRQ); 65 | } 66 | 67 | // タイマー割り込みハンドラ 68 | void handle_timer_interrupt(unsigned ticks) { 69 | // 起動してからの経過時間を更新 70 | uptime_ticks += ticks; 71 | 72 | if (CPUVAR->id == 0) { 73 | // 各タスクのタイマーを更新する 74 | LIST_FOR_EACH (task, &active_tasks, struct task, next) { 75 | if (task->timeout > 0) { 76 | task->timeout -= MIN(task->timeout, ticks); 77 | if (!task->timeout) { 78 | // タイムアウトしたのでタスクに通知する 79 | notify(task, NOTIFY_TIMER); 80 | } 81 | } 82 | } 83 | } 84 | 85 | // 実行中タスクの残り実行可能時間を更新し、ゼロになったらタスク切り替えを行う 86 | struct task *current = CURRENT_TASK; 87 | DEBUG_ASSERT(current->quantum >= 0 || current == IDLE_TASK); 88 | current->quantum -= MIN(ticks, current->quantum); 89 | if (!current->quantum) { 90 | task_switch(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /servers/tcpip/device.c: -------------------------------------------------------------------------------- 1 | #include "device.h" 2 | #include "dhcp.h" 3 | #include "ipv4.h" 4 | #include 5 | #include 6 | 7 | // ネットワークデバイス。現在は1つだけのデバイスしかサポートしていない。 8 | static struct device device; 9 | 10 | // 2つのIPv4アドレスが、与えられたネットマスクで同じネットワークに属するかどうかを返す。 11 | static inline bool ipaddr_equals_in_netmask(ipv4addr_t a, ipv4addr_t b, 12 | ipv4addr_t netmask) { 13 | return (a & netmask) == (b & netmask); 14 | } 15 | 16 | // 与えられた宛先IPv4アドレスへのパケットを受信すべきかどうかを返す。 17 | bool device_dst_is_ours(ipv4addr_t dst) { 18 | ASSERT(device.initialized); 19 | 20 | return dst == device.ipaddr || dst == IPV4_ADDR_BROADCAST; 21 | } 22 | 23 | // 与えられた宛先IPv4アドレスへのパケットをどこに送るべきかを返す。 24 | ipv4addr_t device_get_next_hop(ipv4addr_t dst) { 25 | ASSERT(device.initialized); 26 | 27 | if (dst == IPV4_ADDR_BROADCAST) { 28 | // ブロードキャストアドレスへのパケットは、ブロードキャストアドレスへ送る 29 | return dst; 30 | } else if (ipaddr_equals_in_netmask(device.ipaddr, dst, device.netmask)) { 31 | // 同じネットワーク内の宛先アドレスへのパケットは、そのまま送る 32 | return dst; 33 | } else { 34 | // それ以外の宛先アドレスへのパケットは、デフォルトゲートウェイへ送る 35 | ASSERT(device.gateway != IPV4_ADDR_UNSPECIFIED); 36 | return device.gateway; 37 | } 38 | } 39 | 40 | // デバイスのMACアドレスを返す。 41 | macaddr_t *device_get_macaddr(void) { 42 | ASSERT(device.initialized); 43 | 44 | return &device.macaddr; 45 | } 46 | 47 | // デバイスのIPアドレスを返す。 48 | ipv4addr_t device_get_ipaddr(void) { 49 | ASSERT(device.initialized); 50 | 51 | return device.ipaddr; 52 | } 53 | 54 | // デバイスのIPアドレス、ネットマスク、デフォルトゲートウェイを設定する。 55 | void device_set_ip_addrs(ipv4addr_t ipaddr, ipv4addr_t netmask, 56 | ipv4addr_t gateway) { 57 | ASSERT(device.initialized); 58 | 59 | device.ipaddr = ipaddr; 60 | device.netmask = netmask; 61 | device.gateway = gateway; 62 | } 63 | 64 | // デバイスが利用可能状態かどうかを返す。 65 | bool device_ready(void) { 66 | return device.initialized && device.ipaddr != IPV4_ADDR_UNSPECIFIED; 67 | } 68 | 69 | // デバイスのDHCPクライアントを有効化し、DHCP DISCOVERを送信する。 70 | void device_enable_dhcp(void) { 71 | device.dhcp_enabled = true; 72 | dhcp_transmit(DHCP_TYPE_DISCOVER, IPV4_ADDR_UNSPECIFIED); 73 | } 74 | 75 | // デバイスの初期化を行う。 76 | void device_init(macaddr_t *macaddr) { 77 | device.initialized = true; 78 | device.dhcp_enabled = false; 79 | device.ipaddr = 0; 80 | device.netmask = 0; 81 | device.gateway = 0; 82 | memcpy(device.macaddr, macaddr, MACADDR_LEN); 83 | } 84 | -------------------------------------------------------------------------------- /tools/embed_symbols.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | シンボルテーブルを実行ファイルに埋め込むスクリプト 4 | 5 | """ 6 | 7 | import argparse 8 | import struct 9 | import sys 10 | 11 | SYMBOL_TABLE_MAGIC = bytes([0x53, 0x59, 0x4d, 0x4c]) 12 | 13 | def main(): 14 | parser = argparse.ArgumentParser() 15 | parser.add_argument("symbolsfile") 16 | parser.add_argument("infile") 17 | parser.add_argument("outfile") 18 | args = parser.parse_args() 19 | 20 | symbols = {} 21 | for line in open(args.symbolsfile, "r").readlines(): 22 | cols = line.rstrip().split(" ", 2) 23 | try: 24 | addr = int(cols[0], 16) 25 | except ValueError: 26 | continue 27 | 28 | name = cols[2] 29 | if name.startswith("."): 30 | continue 31 | 32 | if name == "__symbol_table": 33 | start_off = addr 34 | elif name == "__symbol_table_end": 35 | end_off = addr 36 | else: 37 | type_= cols[1] 38 | if type_ not in ["T", "t"]: 39 | continue 40 | 41 | symbols[addr] = name 42 | 43 | image = open(args.infile, "rb").read() 44 | start_off = image.find(b"__SYMBOL_TABLE_START__") 45 | end_off = image.find(b"__SYMBOL_TABLE_END__") + len(b"__SYMBOL_TABLE_END__") 46 | if start_off is None: 47 | sys.exit(f"{args.infile}: BUG: failed to locate the symbol table") 48 | if end_off is None: 49 | sys.exit(f"{args.infile}: BUG: failed to locate the symbol table") 50 | if image.count(b"__SYMBOL_TABLE_START__") != 1: 51 | sys.exit(f"{args.infile}: BUG: multiple symbol tables") 52 | if image.count(b"__SYMBOL_TABLE_END__") != 1: 53 | sys.exit(f"{args.infile}: BUG: multiple symbol tables") 54 | 55 | symbols = sorted(symbols.items(), key=lambda s: s[0]) 56 | 57 | # Build a symbol table. 58 | symbol_table = struct.pack("<4sI8x", SYMBOL_TABLE_MAGIC, len(symbols)) 59 | for addr, name in symbols: 60 | symbol_table += struct.pack(" max_size: 64 | sys.exit(f"{args.infile}: symbol table too big") 65 | 66 | symbol_table += struct.pack(f"{max_size - len(symbol_table)}x") 67 | 68 | # Embed the symbol table. 69 | new_image = image[:start_off] + symbol_table + image[start_off + len(symbol_table):] 70 | assert len(new_image) == len(image) 71 | open(args.outfile, "wb").write(new_image) 72 | 73 | 74 | if __name__ == "__main__": 75 | main() 76 | -------------------------------------------------------------------------------- /libs/common/list.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // リストを初期化する。list_init関数の代替。static変数でリストを宣言した場合に便利。 5 | #define LIST_INIT(list) \ 6 | { .prev = &(list), .next = &(list) } 7 | 8 | // リストから先頭のエントリを取り出す。containerはエントリを含む構造体、fieldはlist_elem_tの 9 | // フィールド名。list_pop_front関数とは違い、list_elem_tへのポインタではなくそれを内包する 10 | // 構造体 (container) へのポインタを返す。エントリがない場合はNULLを返す。 11 | #define LIST_POP_FRONT(list, container, field) \ 12 | ({ \ 13 | list_elem_t *__elem = list_pop_front(list); \ 14 | (__elem) ? LIST_CONTAINER(__elem, container, field) : NULL; \ 15 | }) 16 | 17 | // list_elem_tへのポインタ (elem) から、それを含む構造体 (container) へのポインタを取得する。 18 | #define LIST_CONTAINER(elem, container, field) \ 19 | ((container *) ((vaddr_t) (elem) -offsetof(container, field))) 20 | 21 | // リストの各エントリに対する、いわゆる foreach 文を実現するマクロ。elem はforeach文で 22 | // 使う任意の変数名、list はリスト、container はエントリを含む構造体、field は 23 | // list_elem_tのフィールド名。 24 | // 25 | // 使い方: 26 | // 27 | // struct element { 28 | // list_elem_t next; 29 | // int foo; 30 | // }; 31 | // 32 | // LIST_FOR_EACH(elem, &elems, struct element, next) { 33 | // printf("foo: %d", elem->foo); 34 | // } 35 | // 36 | // なお __next を取り出しているのは、foreach文中で elem が削除された場合に elem->next が 37 | // 無効になるため。 38 | #define LIST_FOR_EACH(elem, list, container, field) \ 39 | for (container *elem = LIST_CONTAINER((list)->next, container, field), \ 40 | *__next = NULL; \ 41 | (&elem->field != (list) \ 42 | && (__next = LIST_CONTAINER(elem->field.next, container, field))); \ 43 | elem = __next) 44 | 45 | // リスト (intrusiveデータ構造) 46 | struct list { 47 | struct list *prev; // 末尾 (list_t) または 前のエントリ (list_elem_t) 48 | struct list *next; // 先頭 (list_t) または 次のエントリ (list_elem_t) 49 | }; 50 | 51 | typedef struct list list_t; // リスト 52 | typedef struct list list_elem_t; // リストのエントリ 53 | 54 | void list_init(list_t *list); 55 | void list_elem_init(list_elem_t *elem); 56 | bool list_is_empty(list_t *list); 57 | bool list_is_linked(list_elem_t *elem); 58 | size_t list_len(list_t *list); 59 | bool list_contains(list_t *list, list_elem_t *elem); 60 | void list_remove(list_elem_t *elem); 61 | void list_push_back(list_t *list, list_elem_t *new_tail); 62 | list_elem_t *list_pop_front(list_t *list); 63 | -------------------------------------------------------------------------------- /libs/user/virtio/virtio_mmio.h: -------------------------------------------------------------------------------- 1 | // MMIOベースのVirtioデバイスドライバライブラリ 2 | // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-1650002 3 | #pragma once 4 | #include "virtio.h" 5 | 6 | // マジックナンバー。VIRTIO_REG_MAGIC_VALUEレジスタを読み込むとこの値が返る。 7 | #define VIRTIO_MMIO_MAGIC_VALUE 0x74726976 8 | 9 | // デバイスレジスタ 10 | // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-1770004 11 | #define VIRTIO_REG_MAGIC_VALUE 0x00 12 | #define VIRTIO_REG_VERSION 0x04 13 | #define VIRTIO_REG_DEVICE_ID 0x08 14 | #define VIRTIO_REG_VENDOR_ID 0x0c 15 | #define VIRTIO_REG_DEVICE_FEATURES 0x10 16 | #define VIRTIO_REG_DEVICE_FEATURES_SEL 0x14 17 | #define VIRTIO_REG_DRIVER_FEATURES 0x20 18 | #define VIRTIO_REG_DRIVER_FEATURES_SEL 0x24 19 | #define VIRTIO_REG_QUEUE_SEL 0x30 20 | #define VIRTIO_REG_QUEUE_NUM_MAX 0x34 21 | #define VIRTIO_REG_QUEUE_NUM 0x38 22 | #define VIRTIO_REG_QUEUE_ALIGN 0x3c 23 | #define VIRTIO_REG_QUEUE_PFN 0x40 24 | #define VIRTIO_REG_QUEUE_READY 0x44 25 | #define VIRTIO_REG_QUEUE_NOTIFY 0x50 26 | #define VIRTIO_REG_INTERRUPT_STATUS 0x60 27 | #define VIRTIO_REG_INTERRUPT_ACK 0x64 28 | #define VIRTIO_REG_DEVICE_STATUS 0x70 29 | #define VIRTIO_REG_DEVICE_CONFIG 0x100 30 | 31 | // 割り込み状態レジスタ (VIRTIO_REG_INTERRUPT_STATUS) のビット。セットされていると 32 | // usedリングにデバイスが処理したディスクリプタが追加されていることを意味する。 33 | #define VIRTIO_ISR_STATUS_QUEUE (1 << 0) 34 | 35 | // MMIOベースのvirtioデバイスを管理する構造体 36 | struct virtio_mmio { 37 | uaddr_t base; 38 | unsigned num_queues; 39 | struct virtio_virtq *virtqs; 40 | }; 41 | 42 | error_t virtio_init(struct virtio_mmio *dev, paddr_t base_paddr, 43 | unsigned num_queues); 44 | uint64_t virtio_read_device_features(struct virtio_mmio *dev); 45 | uint8_t virtio_read_device_config8(struct virtio_mmio *dev, offset_t offset); 46 | error_t virtio_negotiate_feature(struct virtio_mmio *dev, uint64_t features); 47 | error_t virtio_enable(struct virtio_mmio *dev); 48 | uint32_t virtio_read_interrupt_status(struct virtio_mmio *dev); 49 | void virtio_ack_interrupt(struct virtio_mmio *dev, uint32_t status); 50 | struct virtio_virtq *virtq_get(struct virtio_mmio *dev, unsigned index); 51 | uint32_t virtq_num_descs(struct virtio_virtq *vq); 52 | void virtq_notify(struct virtio_mmio *dev, struct virtio_virtq *vq); 53 | int virtq_push(struct virtio_virtq *vq, struct virtio_chain_entry *chain, 54 | int n); 55 | bool virtq_is_empty(struct virtio_virtq *vq); 56 | int virtq_pop(struct virtio_virtq *vq, struct virtio_chain_entry *chain, int n, 57 | size_t *total_len); 58 | -------------------------------------------------------------------------------- /tools/clang-format-align-trailing-comments-kludge.patch: -------------------------------------------------------------------------------- 1 | commit 8677c074e2842c0c6bf0763de500924e8573cabd 2 | Author: Seiya Nuta 3 | Date: Sat Jan 14 13:30:24 2023 +0900 4 | 5 | [clang-format] Add AlignTrailingCommentsColumnLimit option 6 | 7 | 行の末尾のコメントに日本語 (マルチバイト文字) を使うと、文字の幅を上手く考慮できていないのか、 8 | コメントの位置が揃わなくなってしまう。 9 | 10 | そこで、AlignTrailingCommentsにおいてコラム幅の制限を無視するオプションを「その場しのぎ」 11 | の修正として追加する。 12 | 13 | ビルド方法: 14 | 15 | $ git clone https://github.com/llvm/llvm-project.git 16 | $ cmake -S llvm -B build -G Ninja -DLLVM_ENABLE_PROJECTS=clang -DCMAKE_BUILD_TYPE=Release 17 | $ cd build 18 | $ ninja bin/clang-format 19 | 20 | diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h 21 | index 36472c65a892..a0e136440616 100644 22 | --- a/clang/include/clang/Format/Format.h 23 | +++ b/clang/include/clang/Format/Format.h 24 | @@ -1887,6 +1887,7 @@ struct FormatStyle { 25 | /// statements unless they contradict other rules. 26 | /// \version 3.7 27 | unsigned ColumnLimit; 28 | + bool DisableColumnLimitInAlignTrailingComments; 29 | 30 | /// A regular expression that describes comments with special meaning, 31 | /// which should not be split into lines or otherwise changed. 32 | diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp 33 | index b09d05799eb4..1eba81a6203e 100644 34 | --- a/clang/lib/Format/Format.cpp 35 | +++ b/clang/lib/Format/Format.cpp 36 | @@ -879,6 +879,8 @@ template <> struct MappingTraits { 37 | IO.mapOptional("BreakInheritanceList", Style.BreakInheritanceList); 38 | IO.mapOptional("BreakStringLiterals", Style.BreakStringLiterals); 39 | IO.mapOptional("ColumnLimit", Style.ColumnLimit); 40 | + IO.mapOptional("DisableColumnLimitInAlignTrailingComments", 41 | + Style.DisableColumnLimitInAlignTrailingComments); 42 | IO.mapOptional("CommentPragmas", Style.CommentPragmas); 43 | IO.mapOptional("CompactNamespaces", Style.CompactNamespaces); 44 | IO.mapOptional("ConstructorInitializerIndentWidth", 45 | diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp 46 | index 9951906b6af0..55db81be40a6 100644 47 | --- a/clang/lib/Format/WhitespaceManager.cpp 48 | +++ b/clang/lib/Format/WhitespaceManager.cpp 49 | @@ -975,6 +975,8 @@ void WhitespaceManager::alignTrailingComments() { 50 | 51 | if (Style.ColumnLimit == 0) 52 | ChangeMaxColumn = UINT_MAX; 53 | + else if (Style.DisableColumnLimitInAlignTrailingComments) 54 | + ChangeMaxColumn = UINT_MAX; 55 | else if (Style.ColumnLimit >= Changes[i].TokenLength) 56 | ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength; 57 | else 58 | -------------------------------------------------------------------------------- /tools/coreutils.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | UNIX系OSの基本的なコマンドを実装したスクリプト (Windows用) 4 | 5 | Windows上では、GNU makeはUNIX系のシェルではなくコマンドプロンプト (cmd.exe) 上でコマンドを実行するため、 6 | ところどころ互換性がない部分がある。特に、パスの区切りがスラッシュ (macOS/Linux) かバックスラッシュ (Windows) か 7 | の違いをMakefile側で吸収するのが難しい。そこでPythonに任せる。 8 | 9 | """ 10 | import argparse 11 | import fnmatch 12 | import os 13 | import shutil 14 | import sys 15 | 16 | def do_cp(args): 17 | for src in args.src: 18 | shutil.copy(src, args.dst) 19 | 20 | def do_rm(args): 21 | if args.r: 22 | shutil.rmtree(args.path, ignore_errors=args.f) 23 | else: 24 | try: 25 | os.unlink(args.path) 26 | except Exception as e: 27 | if not args.f: 28 | sys.exit(f"rm: failed to remove '{args.path}': {e}") 29 | 30 | def do_mkdir(args): 31 | if args.p: 32 | os.makedirs(args.path, exist_ok=True) 33 | else: 34 | os.mkdir(args.path) 35 | 36 | def do_find(args): 37 | name = None 38 | for k, v in zip(args.exprs[::2], args.exprs[1::2]): 39 | if k not in ["-name"]: 40 | sys.exit(f"find: unknown expression {k}") 41 | if v is None: 42 | sys.exit(f"find: the value for {k} is missing") 43 | if k == "-name": 44 | name = v 45 | 46 | for root, dirs, files in os.walk(args.path): 47 | for file in files: 48 | if name: 49 | show = fnmatch.fnmatch(file, name) 50 | else: 51 | show = True 52 | if show: 53 | print(os.path.join(root, file)) 54 | 55 | def main(): 56 | parser = argparse.ArgumentParser() 57 | subparsers = parser.add_subparsers() 58 | 59 | mkdir_command = subparsers.add_parser("cp") 60 | mkdir_command.add_argument("src", nargs="+") 61 | mkdir_command.add_argument("dst") 62 | mkdir_command.set_defaults(handler=do_cp) 63 | 64 | rm_command = subparsers.add_parser("rm") 65 | rm_command.add_argument("-r", action="store_true") 66 | rm_command.add_argument("-f", action="store_true") 67 | rm_command.add_argument("path") 68 | rm_command.set_defaults(handler=do_rm) 69 | 70 | mkdir_command = subparsers.add_parser("mkdir") 71 | mkdir_command.add_argument("-p", action="store_true") 72 | mkdir_command.add_argument("path") 73 | mkdir_command.set_defaults(handler=do_mkdir) 74 | 75 | find_command = subparsers.add_parser("find") 76 | find_command.add_argument("path") 77 | find_command.add_argument("exprs", nargs=argparse.REMAINDER) 78 | find_command.set_defaults(handler=do_find) 79 | 80 | args = parser.parse_args() 81 | if hasattr(args, "handler"): 82 | args.handler(args) 83 | else: 84 | parser.print_help() 85 | 86 | if __name__ == "__main__": 87 | main() 88 | -------------------------------------------------------------------------------- /servers/tcpip/dhcp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ethernet.h" 3 | #include 4 | 5 | #define BOOTP_OP_REQUEST 1 // BOOTPリクエスト 6 | #define BOOTP_HWTYPE_ETHERNET 1 // イーサネット 7 | #define DHCP_MAGIC 0x63825363 // DHCPを示すマジックナンバー 8 | 9 | // DHCPオプションの種類 10 | enum dhcp_option { 11 | DHCP_OPTION_NETMASK = 1, // ネットマスク 12 | DHCP_OPTION_ROUTER = 3, // デフォルトゲートウェイ 13 | DHCP_OPTION_DNS = 6, // DNSキャッシュサーバ 14 | DHCP_OPTION_REQUESTED_ADDR = 50, // 要求するIPアドレス 15 | DHCP_OPTION_DHCP_TYPE = 53, // DHCPパケットの種類 16 | DHCP_OPTION_PARAM_LIST = 55, // DHCPサーバに要求する追加情報 17 | DHCP_OPTION_END = 255, // オプションの終了マーカー 18 | }; 19 | 20 | // DHCPパケットの種類 21 | enum dhcp_type { 22 | DHCP_TYPE_DISCOVER = 1, // DHCP DISCOVER 23 | DHCP_TYPE_OFFER = 2, // DHCP OFFER 24 | DHCP_TYPE_REQUEST = 3, // DHCP REQUEST 25 | DHCP_TYPE_ACK = 5, // DHCP ACK 26 | }; 27 | 28 | // DHCPパケット 29 | struct dhcp_header { 30 | uint8_t op; // 要求か応答か 31 | uint8_t hw_type; // ハードウェアアドレスの種類 32 | uint8_t hw_len; // ハードウェアアドレスの長さ 33 | uint8_t hops; // ルータを経由した回数 34 | uint32_t xid; // トランザクションID 35 | uint16_t secs; // クライアントがアドレスの取得処理を開始してからの秒数 36 | uint16_t flags; // フラグ 37 | uint32_t client_ipaddr; // クライアントのIPアドレス 38 | uint32_t your_ipaddr; // クライアントに割り当てるIPアドレス 39 | uint32_t server_ipaddr; // TFTPサーバのIPアドレス 40 | uint32_t gateway_ipaddr; // DHCPパケットを転送したノード (参考: DHCPリレー) 41 | macaddr_t client_hwaddr; // クライアントのMACアドレス 42 | uint8_t unused[202]; // 未使用領域 43 | uint32_t magic; // DHCPを示すマジックナンバー 44 | uint8_t options[]; // DHCPオプション: いわゆるTLV (Type-Length-Value) 形式 45 | } __packed; 46 | 47 | // DHCPオプション: DHCPパケットの種類 (DHCP_OPTION_DHCP_TYPE) 48 | struct dhcp_type_option { 49 | uint8_t dhcp_type; 50 | } __packed; 51 | 52 | // DHCPオプション: ネットマスク (DHCP_OPTION_NETMASK) 53 | struct dhcp_netmask_option { 54 | uint32_t netmask; 55 | } __packed; 56 | 57 | // DHCPオプション: デフォルトゲートウェイ (DHCP_OPTION_ROUTER) 58 | struct dhcp_router_option { 59 | uint32_t router; 60 | } __packed; 61 | 62 | // DHCPオプション: DNSキャッシュサーバ (DHCP_OPTION_DNS) 63 | struct dhcp_dns_option { 64 | uint32_t dns; 65 | } __packed; 66 | 67 | // DHCPオプション: DHCPサーバに要求する追加情報 (DHCP_OPTION_PARAM_LIST) 68 | #define DHCP_PARMAS_MAX 3 69 | struct dhcp_params_option { 70 | uint8_t params[DHCP_PARMAS_MAX]; 71 | } __packed; 72 | 73 | // DHCPオプション: 要求するIPアドレス (DHCP_OPTION_REQUESTED_ADDR) 74 | struct dhcp_reqaddr_option { 75 | uint32_t ipaddr; 76 | } __packed; 77 | 78 | void dhcp_transmit(enum dhcp_type type, ipv4addr_t requested_addr); 79 | void dhcp_receive(void); 80 | void dhcp_init(void); 81 | -------------------------------------------------------------------------------- /mk/executable.mk: -------------------------------------------------------------------------------- 1 | # 実行ファイル (カーネルと各サーバ) の生成ルール 2 | # 3 | # kernelまたはservers/<サーバ名>配下に build.mk という名前のファイルを作ると、 4 | # ビルドシステムがそれを認識して必要なビルドルールを構築する。 5 | # 6 | # build.mk内では次の変数を使って宣言的にビルドルールを定義する。原則として、 7 | # 末尾に追記する (+=) ことで定義するのがおすすめ。上書き (:=) するとデフォルト値を 8 | # 失ってしまう。 9 | # 10 | # - objs-y: 生成するオブジェクトファイルのリスト。 11 | # C/アセンブリファイル共に拡張子を.oにする必要があり、 12 | # hello.cとhello.Sのような同名のファイルは扱えない。 13 | # - cflags-y: Cコンパイラのオプション 14 | # - ldflags-y: リンカーのオプション 15 | # - libs-y: リンクするライブラリ名のリスト。 16 | # カーネルはデフォルトでcommon、サーバはデフォルトでcommonとuserがセットされている。 17 | # - subdirs-y: サブディレクトリのリスト。定義されているとビルドシステムそれらの 18 | # サブディレクトリに入っているbuild.mkを読み込む。 19 | 20 | # サブディレクトリ (subdir-y) を辿って必要なオブジェクトファイルを列挙する 21 | build_dir := $(BUILD_DIR)/$(dir) 22 | objs := $(addprefix $(build_dir)/, $(objs-y)) 23 | dir-saved = $(dir) 24 | $(foreach subdir, $(subdirs-y), \ 25 | $(eval dir := $(dir-saved)/$(subdir)) \ 26 | $(eval build_dir := $(BUILD_DIR)/$(dir)) \ 27 | $(eval objs-y :=) \ 28 | $(eval include $(dir)/build.mk) \ 29 | $(eval objs += $(addprefix $(build_dir)/, $(objs-y))) \ 30 | ) 31 | 32 | objs := \ 33 | $(objs) \ 34 | $(foreach lib, $(libs-y), $(BUILD_DIR)/libs/$(lib).o) \ 35 | $(BUILD_DIR)/program_names/$(name).o 36 | 37 | # 各オブジェクトファイルのコンパイルルール 38 | $(objs): CFLAGS := $(CFLAGS) $(cflags-y) 39 | 40 | # 実行ファイルの生成ルール (build/hinaos.elf または build/servers/<サーバ名>.elf) 41 | # 42 | # 1. リンカーでオブジェクトファイルをくっつけて実行ファイルを生成する (*.elf.tmp) 43 | # 2. 実行ファイルからシンボルテーブルを抽出する (*.symbols) 44 | # 3. スタックトレース用のシンボルテーブルを埋め込む (*.elf.debug) 45 | # 4. GDB用にシンボル名に「プログラム名.」を付加した別のELFファイルを生成する (*.elf.gdb) 46 | # 5. bootfsの容量削減のために、.elf.debugファイルからデバッグ情報を削除する (*.elf) 47 | $(executable): LDFLAGS := $(LDFLAGS) $(ldflags-y) 48 | $(executable): OBJS := $(objs) 49 | $(executable): NAME := $(name) 50 | $(executable): $(objs) $(extra-deps-y) tools/embed_symbols.py 51 | $(PROGRESS) LD $(@) 52 | $(MKDIR) -p $(@D) 53 | $(LD) $(LDFLAGS) -Map $(@:.elf=.map) -o $(@).tmp $(OBJS) 54 | 55 | $(PROGRESS) NM $(@) 56 | $(NM) --radix=x --defined-only $(@).tmp > $(@:.elf=.symbols) 57 | 58 | $(PROGRESS) SYMBOLS $(@:.elf=.symbols) 59 | $(PYTHON3) ./tools/embed_symbols.py $(@:.elf=.symbols) $(@).tmp $(@).debug 60 | 61 | $(PROGRESS) GEN $(@).gdb 62 | $(OBJCOPY) --only-keep-debug --prefix-symbols="$(NAME)." $(@).debug $(@).gdb 63 | 64 | $(PROGRESS) STRIP $(@) 65 | $(OBJCOPY) --strip-all $(@).debug $(@) 66 | 67 | $(RM) $(@).tmp 68 | 69 | # プログラム名を返す関数を生成する。INFOやWARNマクロ内で利用する。 70 | $(BUILD_DIR)/program_names/$(name).c: NAME := $(name) 71 | $(BUILD_DIR)/program_names/$(name).c: tools/generate_program_name.py 72 | $(MKDIR) -p $(@D) 73 | $(PYTHON3) tools/generate_program_name.py -o $(@) $(NAME) 74 | -------------------------------------------------------------------------------- /servers/tcpip/ipv4.c: -------------------------------------------------------------------------------- 1 | #include "ipv4.h" 2 | #include "checksum.h" 3 | #include "device.h" 4 | #include "ethernet.h" 5 | #include "tcp.h" 6 | #include "udp.h" 7 | #include 8 | #include 9 | #include 10 | 11 | // IPv4パケットの送信処理 12 | void ipv4_transmit(ipv4addr_t dst, uint8_t proto, mbuf_t payload) { 13 | // IPv4ヘッダを構築 14 | struct ipv4_header header; 15 | memset(&header, 0, sizeof(header)); 16 | size_t total_len = sizeof(header) + mbuf_len(payload); 17 | header.ver_ihl = 0x45; // ヘッダ長 18 | header.len = hton16(total_len); // 合計の長さ 19 | header.ttl = DEFAULT_TTL; // TTL 20 | header.proto = proto; // 上層のプロトコル 21 | header.dst_addr = hton32(dst); // 宛先IPv4アドレス 22 | header.src_addr = hton32(device_get_ipaddr()); // 送信元IPv4アドレス 23 | 24 | // チェックサムを計算してセット 25 | checksum_t checksum; 26 | checksum_init(&checksum); 27 | checksum_update(&checksum, &header, sizeof(header)); 28 | header.checksum = checksum_finish(&checksum); 29 | 30 | // IPv4ヘッダを先頭に付けてイーサーネットの送信処理に回す 31 | mbuf_t pkt = mbuf_new(&header, sizeof(header)); 32 | mbuf_append(pkt, payload); 33 | ethernet_transmit(ETHER_TYPE_IPV4, dst, pkt); 34 | } 35 | 36 | // IPv4パケットの受信処理 37 | void ipv4_receive(mbuf_t pkt) { 38 | // IPv4ヘッダを読み込む 39 | struct ipv4_header header; 40 | if (mbuf_read(&pkt, &header, sizeof(header)) < sizeof(header)) { 41 | return; 42 | } 43 | 44 | // ヘッダサイズを取得して、ヘッダの長さ分だけパケットを捨てる。これでpktはIPv4パケットの 45 | // ペイロードを指すようになる。 46 | size_t header_len = (header.ver_ihl & 0x0f) * 4; 47 | if (header_len > sizeof(header)) { 48 | mbuf_discard(&pkt, header_len - sizeof(header)); 49 | } 50 | 51 | // 宛先が自分でなければ無視 52 | ipv4addr_t dst = ntoh32(header.dst_addr); 53 | if (!device_dst_is_ours(dst)) { 54 | return; 55 | } 56 | 57 | // 変な長さのパケットは無視 58 | if (ntoh16(header.len) < header_len) { 59 | return; 60 | } 61 | 62 | // イーサーネットフレームのペイロードは最小長制約のためにパディングされる可能性があるので、 63 | // IPv4ヘッダに記載されているペイロードの長さに合わせてパケットを切り詰める。こうしないと 64 | // TCPのペイロード長が正しく計算されない (TCPヘッダにはペイロード長が記載されていない)。 65 | uint16_t payload_len = ntoh16(header.len) - header_len; 66 | mbuf_truncate(pkt, payload_len); 67 | if (mbuf_len(pkt) != payload_len) { 68 | // 変な長さのパケットは無視 69 | return; 70 | } 71 | 72 | // パケットの種類に応じて処理を振り分ける 73 | ipv4addr_t src = ntoh32(header.src_addr); 74 | switch (header.proto) { 75 | case IPV4_PROTO_UDP: 76 | udp_receive(src, pkt); 77 | break; 78 | case IPV4_PROTO_TCP: 79 | tcp_receive(dst, src, pkt); 80 | break; 81 | default: 82 | WARN("unknown ip proto type: %x", header.proto); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /servers/fs/fs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "block.h" 4 | #include 5 | 6 | #define FS_MAGIC 0xf2005346 // マジックナンバー 7 | #define FS_HEADER_BLOCK 0 // ファイルシステムヘッダーのブロック番号 8 | #define ROOT_DIR_BLOCK 1 // ルートディレクトリのブロック番号 9 | #define BITMAP_FIRST_BLOCK 2 // ビットマップテーブルの最初のブロック番号 10 | #define NUM_BITMAP_BLOCKS 4 // ビットマップテーブルのブロック数 11 | 12 | #define BLOCKS_PER_ENTRY 1908 // 1エントリに含まれる最大データブロック数 13 | #define FS_TYPE_DIR 0xdd // ディレクトリエントリの種類: ディレクトリ 14 | #define FS_TYPE_FILE 0xff // ファイルエントリの種類: ファイル 15 | #define FS_NAME_LEN 256 // エントリ名の最大長 16 | 17 | // ファイルシステム上の各エントリ 18 | // 19 | // たとえば /foo/bar/hello.txt というファイルがあるとすると、次のような3エントリが存在する: 20 | // 21 | // - エントリ名が「foo」のディレクトリエントリ 22 | // - エントリ名が「bar」のディレクトリエントリ 23 | // - エントリ名が「hello.txt」のファイルエントリ 24 | struct hinafs_entry { 25 | uint8_t type; // エントリの種類 (ファイルかディレクトリか) 26 | uint8_t padding[3]; // パディング 27 | char name[FS_NAME_LEN]; // エントリ名 (終端はヌル文字) 28 | union { 29 | // ファイルエントリの場合のみ有効なフィールド 30 | struct { 31 | uint32_t size; // ファイルサイズ 32 | }; 33 | 34 | // ディレクトリエントリの場合のみ有効なフィールド 35 | struct { 36 | uint16_t num_entries; // ディレクトリ内のエントリ数 37 | uint16_t padding2; // パディング 38 | }; 39 | }; 40 | int64_t created_at; // 作成日時 (使われていない) 41 | int64_t modified_at; // 最終更新日時 (使われていない) 42 | block_t blocks[BLOCKS_PER_ENTRY]; // データブロックのリスト: 43 | // - ファイル: ファイルデータ 44 | // - ディレクトリ: ディレクトリ内の各エントリ 45 | } __packed; 46 | 47 | // ファイルシステムのヘッダ 48 | struct hinafs_header { 49 | uint32_t magic; // マジックナンバー。FS_MAGICでなければならない。 50 | uint32_t num_data_blocks; // データブロック数。 51 | uint8_t padding[4088]; // パディング。ブロックサイズに合わせるために必要。 52 | 53 | // このヘッダの後ろに、次のデータが続く: 54 | // struct hinafs_entry root_dir; // ルートディレクトリ 55 | // uint8_t bitmap_blocks[num_bitmap_blocks * BLOCK_SIZE]; // ビットマップ 56 | // uint8_t blocks[num_data_blocks * BLOCK_SIZE]; // データブロック 57 | } __packed; 58 | 59 | STATIC_ASSERT(sizeof(struct hinafs_header) == BLOCK_SIZE, 60 | "hinafs_header size must be equal to block size"); 61 | STATIC_ASSERT(sizeof(struct hinafs_entry) == BLOCK_SIZE, 62 | "hinafs_entry size must be equal to block size"); 63 | 64 | error_t fs_find(const char *path, struct block **entry_block); 65 | error_t fs_create(const char *path, uint8_t type); 66 | error_t fs_readwrite(struct block *entry_block, void *buf, size_t size, 67 | size_t offset, bool write); 68 | error_t fs_readdir(const char *path, int index, struct hinafs_entry **entry); 69 | error_t fs_delete(const char *path); 70 | void fs_init(void); 71 | -------------------------------------------------------------------------------- /libs/common/list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // prev と next の間に新しいエントリ new を挿入する。 5 | // 6 | // prev <-> next => prev <-> new <-> next 7 | // 8 | static void list_insert(list_elem_t *prev, list_elem_t *next, 9 | list_elem_t *new) { 10 | new->prev = prev; 11 | new->next = next; 12 | next->prev = new; 13 | prev->next = new; 14 | } 15 | 16 | // エントリを無効な状態にする。バグ検出用。 17 | static void list_elem_nullify(list_elem_t *elem) { 18 | elem->prev = NULL; 19 | elem->next = NULL; 20 | } 21 | 22 | // リストを初期化する。初期状態ではリストは空。 23 | void list_init(list_t *list) { 24 | list->prev = list; 25 | list->next = list; 26 | } 27 | 28 | // リストのエントリを初期化する。 29 | void list_elem_init(list_elem_t *elem) { 30 | list_elem_nullify(elem); 31 | } 32 | 33 | // リストが空かどうかを返す。 34 | bool list_is_empty(list_t *list) { 35 | return list->next == list; 36 | } 37 | 38 | // リストのエントリがどこかのリストに所属しているか (つまり使用中か) を返す。O(1)。 39 | bool list_is_linked(list_elem_t *elem) { 40 | return elem->next != NULL; 41 | } 42 | 43 | // リストの総エントリ数を返す。O(n)。 44 | size_t list_len(list_t *list) { 45 | size_t len = 0; 46 | struct list *node = list->next; 47 | while (node != list) { 48 | len++; 49 | node = node->next; 50 | } 51 | 52 | return len; 53 | } 54 | 55 | // リストに指定されたエントリが含まれているかを返す。O(n)。 56 | bool list_contains(list_t *list, list_elem_t *elem) { 57 | list_elem_t *node = list->next; 58 | while (node != list) { 59 | if (node == elem) { 60 | return true; 61 | } 62 | 63 | node = node->next; 64 | } 65 | 66 | return false; 67 | } 68 | 69 | // リストからエントリを削除する。O(1)。 70 | void list_remove(list_elem_t *elem) { 71 | if (!list_is_linked(elem)) { 72 | return; 73 | } 74 | 75 | // 前後のエントリをつなぎ直すことで、エントリをリストから削除する。 76 | // 77 | // prev <-> elem <-> next => prev <-> next 78 | // 79 | elem->prev->next = elem->next; 80 | elem->next->prev = elem->prev; 81 | 82 | // エントリを無効な状態にする。こうすることで二回以上の削除を防ぐ。 83 | list_elem_nullify(elem); 84 | } 85 | 86 | // エントリをリストの末尾に追加する。O(1)。 87 | void list_push_back(list_t *list, list_elem_t *new_tail) { 88 | DEBUG_ASSERT(!list_contains(list, new_tail)); 89 | DEBUG_ASSERT(!list_is_linked(new_tail)); 90 | list_insert(list->prev, list, new_tail); 91 | } 92 | 93 | // リストの先頭エントリを取り出す。空の場合はNULLを返す。O(1)。 94 | list_elem_t *list_pop_front(list_t *list) { 95 | struct list *head = list->next; 96 | if (head == list) { 97 | return NULL; 98 | } 99 | 100 | // エントリをリストから削除する。 101 | struct list *next = head->next; 102 | list->next = next; 103 | next->prev = list; 104 | 105 | // エントリを無効な状態にする。こうすることで list_remove() を呼び出しても削除しない 106 | // ようにする。 107 | list_elem_nullify(head); 108 | return head; 109 | } 110 | -------------------------------------------------------------------------------- /libs/kernel/wasm/build.mk: -------------------------------------------------------------------------------- 1 | WAMR_ROOT := $(top_dir)/third_party/wasm-micro-runtime 2 | WAMR_CORE_ROOT := $(WAMR_ROOT)/core 3 | WAMR_SHARED_ROOT := $(WAMR_CORE_ROOT)/shared 4 | WAMR_IWASM_ROOT := $(WAMR_CORE_ROOT)/iwasm 5 | 6 | objs-y += platform.o 7 | 8 | # see line 5793 of wasm_runtime_common.c 9 | cflags-y += -Wno-error=int-conversion 10 | 11 | # defines 12 | cflags-y += -DWASM_ENABLE_INTERP=1 \ 13 | -DBH_MALLOC=wasm_runtime_malloc \ 14 | -DBH_FREE=wasm_runtime_free 15 | 16 | # include directories 17 | cflags-y += -I$(WAMR_IWASM_ROOT)/include \ 18 | -I$(WAMR_SHARED_ROOT)/platform/include \ 19 | -I$(top_dir)/$(dir)/include \ 20 | -I$(WAMR_IWASM_ROOT)/interpreter \ 21 | -I$(WAMR_SHARED_ROOT)/mem-alloc \ 22 | -I$(WAMR_IWASM_ROOT)/common \ 23 | -I$(WAMR_SHARED_ROOT)/utils 24 | 25 | # WAMR object files 26 | objs-y += wasm_interp_classic.o \ 27 | wasm_loader.o \ 28 | wasm_runtime.o \ 29 | mem_alloc.o \ 30 | ems_alloc.o \ 31 | ems_hmu.o \ 32 | ems_kfc.o \ 33 | wasm_application.o \ 34 | wasm_blocking_op.o \ 35 | wasm_c_api.o \ 36 | wasm_exec_env.o \ 37 | wasm_memory.o \ 38 | wasm_native.o \ 39 | wasm_runtime_common.o \ 40 | bh_assert.o \ 41 | bh_bitmap.o \ 42 | bh_common.o \ 43 | bh_hashmap.o \ 44 | bh_list.o \ 45 | bh_log.o \ 46 | bh_queue.o \ 47 | bh_vector.o \ 48 | runtime_timer.o \ 49 | math.o \ 50 | invokeNative_riscv.o 51 | 52 | # WAMR source code paths 53 | VPATH += $(WAMR_IWASM_ROOT)/interpreter/ \ 54 | $(WAMR_SHARED_ROOT)/mem-alloc \ 55 | $(WAMR_SHARED_ROOT)/mem-alloc/ems \ 56 | $(WAMR_IWASM_ROOT)/common \ 57 | $(WAMR_IWASM_ROOT)/common/arch \ 58 | $(WAMR_SHARED_ROOT)/utils \ 59 | $(WAMR_SHARED_ROOT)/utils/uncommon \ 60 | $(WAMR_SHARED_ROOT)/platform/common/math \ 61 | $(WAMR_IWASM_ROOT)/common/arch 62 | 63 | # build rules 64 | $(build_dir)/%.o: %.S 65 | $(PROGRESS) CC $@ 66 | $(CC) $(CFLAGS) $(extra-cflags)-c -o $@ $^ 67 | 68 | $(build_dir)/%.o: %.c 69 | $(PROGRESS) CC $@ 70 | $(CC) $(CFLAGS) $(extra-cflags) -c -o $@ $^ -------------------------------------------------------------------------------- /servers/tcpip/tcp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ipv4.h" 3 | #include "mbuf.h" 4 | #include 5 | 6 | // TCP通信の管理構造体の最大数 7 | #define TCP_PCBS_MAX 512 8 | // 再送タイムアウトの初期値 9 | #define TCP_TX_INITIAL_TIMEOUT 500 10 | // 再送タイムアウトの最大値 11 | #define TCP_TX_MAX_TIMEOUT 3000 12 | // 受信バッファのサイズ (初期ウィンドウサイズ) 13 | #define TCP_RX_BUF_SIZE 8192 14 | 15 | // TCPコネクションの状態 (参考: TCPの状態遷移図) 16 | // 17 | // 簡単のため、このTCP実装は素朴なHTTPクライアントが動く程度の状態しか実装していない。 18 | // パッシブオープンやアクティブクローズなどを実装する場合には、より多くの状態が登場する。 19 | enum tcp_state { 20 | TCP_STATE_LISTEN, 21 | TCP_STATE_SYN_RECVED, 22 | TCP_STATE_SYN_SENT, // SYNを送信し、SYN+ACKを待っている状態 23 | TCP_STATE_ESTABLISHED, // コネクションを確立した状態 24 | TCP_STATE_LAST_ACK, 25 | TCP_STATE_CLOSED, // コネクションが閉じられた状態 26 | }; 27 | 28 | // 送信待ちフラグ 29 | enum tcp_pending_flag { 30 | TCP_PEND_ACK = 1 << 0, 31 | TCP_PEND_FIN = 1 << 1, 32 | TCP_PEND_SYN = 1 << 2, 33 | }; 34 | 35 | // TCP通信の管理構造体 (PCB: Protocol Control Block) 36 | struct tcp_pcb { 37 | bool in_use; // 使用中かどうか 38 | enum tcp_state state; // コネクションの状態 39 | uint32_t pending_flags; // 送信する必要があるフラグ 40 | uint32_t next_seqno; // 次に送信すべきシーケンス番号 41 | uint32_t last_seqno; // 最後に送信したシーケンス番号 42 | uint32_t last_ack; // 最後に受信したシーケンス番号 + 1 43 | uint32_t local_winsize; // 送信ウィンドウサイズ 44 | uint32_t remote_winsize; // 受信ウィンドウサイズ 45 | endpoint_t local; // ソケットに紐付けられたIPアドレスとポート番号 46 | endpoint_t remote; // 相手のIPアドレスとポート番号 47 | mbuf_t rx_buf; // 受信バッファ 48 | mbuf_t tx_buf; // 送信バッファ 49 | unsigned num_retransmits; // 再送回数 50 | int retransmit_at; // 次に再送すべき時刻 51 | list_elem_t next; // 次の要素へのポインタ 52 | void *arg; // コールバック関数に渡す引数 53 | struct tcp_pcb *parent; 54 | }; 55 | 56 | // TCPヘッダのフラグ 57 | enum tcp_header_flag { 58 | TCP_FIN = 1 << 0, 59 | TCP_SYN = 1 << 1, 60 | TCP_RST = 1 << 2, 61 | TCP_PSH = 1 << 3, 62 | TCP_ACK = 1 << 4, 63 | }; 64 | 65 | // TCPヘッダ 66 | struct tcp_header { 67 | uint16_t src_port; // 送信元ポート番号 68 | uint16_t dst_port; // 宛先ポート番号 69 | uint32_t seqno; // シーケンス番号 70 | uint32_t ackno; // 確認応答番号 71 | uint8_t off_and_ns; // ヘッダ長など 72 | uint8_t flags; // フラグ 73 | uint16_t win_size; // ウィンドウサイズ 74 | uint16_t checksum; // チェックサム 75 | uint16_t urgent; // 緊急ポインタ 76 | } __packed; 77 | 78 | struct tcp_pcb *tcp_new(void *arg); 79 | error_t tcp_bind(struct tcp_pcb *pcb, ipv4addr_t addr, port_t port); 80 | void tcp_listen(struct tcp_pcb *pcb); 81 | error_t tcp_connect(struct tcp_pcb *sock, ipv4addr_t dst_addr, port_t dst_port); 82 | void tcp_destroy(struct tcp_pcb *sock); 83 | void tcp_write(struct tcp_pcb *sock, const void *data, size_t len); 84 | size_t tcp_read(struct tcp_pcb *sock, void *buf, size_t buf_len); 85 | void tcp_receive(ipv4addr_t dst, ipv4addr_t src, mbuf_t pkt); 86 | void tcp_flush(void); 87 | -------------------------------------------------------------------------------- /kernel/riscv32/include/arch_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../asmdefs.h" 3 | #include 4 | 5 | // 仮想アドレス空間上のカーネルメモリ領域の開始アドレス。 6 | #define KERNEL_BASE 0x80000000 7 | // IRQの最大数。 8 | #define IRQ_MAX 32 9 | 10 | // RISC-V特有のタスク管理構造体。 11 | struct arch_task { 12 | uint32_t sp; // 次回実行時に復元されるべきカーネルスタックの値 13 | uint32_t sp_top; // カーネルスタックの上端 14 | paddr_t sp_bottom; // カーネルスタックの底 15 | }; 16 | 17 | // RISC-V特有のページテーブル管理構造体。 18 | struct arch_vm { 19 | paddr_t table; // ページテーブルの物理アドレス (Sv32) 20 | }; 21 | 22 | // RISC-V特有のCPUローカル変数。順番を変える時はasmdefs.hで定義しているマクロも更新する。 23 | struct arch_cpuvar { 24 | uint32_t sscratch; // 変数の一時保管場所 25 | uint32_t sp_top; // 実行中タスクのカーネルスタックの上端 26 | 27 | // タイマー割り込みハンドラ (M-mode) で使用。 28 | uint32_t mscratch0; // 変数の一時保管場所 29 | uint32_t mscratch1; // 変数の一時保管場所その2 30 | paddr_t mtimecmp; // MTIMECMPのアドレス 31 | paddr_t mtime; // MTIMEのアドレス 32 | uint32_t interval; // MTIMECMPに加算していく値 33 | uint64_t last_mtime; // 直前のmtimeの値 34 | }; 35 | 36 | // CPUVAR_* マクロが正しく定義されているかをチェックするためのマクロ。 37 | #define ARCH_TYPES_STATIC_ASSERTS \ 38 | STATIC_ASSERT(offsetof(struct cpuvar, arch.sscratch) == CPUVAR_SSCRATCH, \ 39 | "CPUVAR_SSCRATCH is incorrect"); \ 40 | STATIC_ASSERT(offsetof(struct cpuvar, arch.sp_top) == CPUVAR_SP_TOP, \ 41 | "CPUVAR_SP_TOP is incorrect"); \ 42 | STATIC_ASSERT(offsetof(struct cpuvar, arch.mscratch0) == CPUVAR_MSCRATCH0, \ 43 | "CPUVAR_MSCRATCH0 is incorrect"); \ 44 | STATIC_ASSERT(offsetof(struct cpuvar, arch.mscratch1) == CPUVAR_MSCRATCH1, \ 45 | "CPUVAR_MSCRATCH1 is incorrect"); \ 46 | STATIC_ASSERT(offsetof(struct cpuvar, arch.mtimecmp) == CPUVAR_MTIMECMP, \ 47 | "CPUVAR_MTIMECMP is incorrect"); \ 48 | STATIC_ASSERT(offsetof(struct cpuvar, arch.mtime) == CPUVAR_MTIME, \ 49 | "CPUVAR_MTIME is incorrect"); \ 50 | STATIC_ASSERT(offsetof(struct cpuvar, arch.interval) == CPUVAR_INTERVAL, \ 51 | "CPUVAR_INTERVAL is incorrect"); 52 | 53 | // CPUVARマクロの中身。現在のCPUローカル変数のアドレスを返す。 54 | static inline struct cpuvar *arch_cpuvar_get(void) { 55 | // tpレジスタにCPUローカル変数のアドレスが格納されている。 56 | uint32_t tp; 57 | __asm__ __volatile__("mv %0, tp" : "=r"(tp)); 58 | return (struct cpuvar *) tp; 59 | } 60 | 61 | // 物理アドレスを仮想アドレスに変換する。 62 | static inline vaddr_t arch_paddr_to_vaddr(paddr_t paddr) { 63 | // 0x80000000 以上の物理アドレスは同じ仮想アドレスにマッピングされているため、 64 | // そのまま返す。 65 | return paddr; 66 | } 67 | 68 | // ユーザータスクが利用してもよい仮想アドレスかどうかを返す。 69 | static inline bool arch_is_mappable_uaddr(uaddr_t uaddr) { 70 | // 0番地付近はヌルポインタ参照の可能性が高いため許可しない。また、KERNEL_BASE以降は 71 | // カーネルが利用するので許可しない。 72 | return PAGE_SIZE <= uaddr && uaddr < KERNEL_BASE; 73 | } 74 | -------------------------------------------------------------------------------- /servers/wasm_webapi/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static task_t tcpip_server; 5 | 6 | #define INDEX_HTML \ 7 | "" \ 8 | "" \ 9 | "" \ 10 | " " \ 11 | "" \ 12 | "" \ 13 | "

Hello from WasmOS!

" \ 14 | "" \ 15 | "\n" 16 | 17 | #define ASSERT_OK(expr) \ 18 | do { \ 19 | __typeof__(expr) __expr = (expr); \ 20 | if (IS_ERROR(__expr)) { \ 21 | goto fail; \ 22 | } \ 23 | } while(0) 24 | 25 | __attribute__((export_name("main"))) 26 | int main(void) { 27 | // find tcpip server 28 | tcpip_server = ipc_lookup("tcpip"); 29 | 30 | // send tcp_listen message 31 | struct message m; 32 | m.type = TCPIP_LISTEN_MSG; 33 | m.tcpip_listen.listen_port = 80; 34 | ASSERT_OK(ipc_call(tcpip_server, &m)); 35 | 36 | while(true) { 37 | ASSERT_OK(ipc_recv(IPC_ANY, &m)); 38 | switch (m.type) { 39 | case TCPIP_CLOSED_MSG: { 40 | // destroy socket 41 | m.type = TCPIP_DESTROY_MSG; 42 | m.tcpip_destroy.sock = m.tcpip_closed.sock; 43 | ASSERT_OK(ipc_call(tcpip_server, &m)); 44 | break; 45 | } 46 | case TCPIP_DATA_MSG: { 47 | int sock = m.tcpip_data.sock; 48 | 49 | // read request 50 | m.type = TCPIP_READ_MSG; 51 | m.tcpip_read.sock = sock; 52 | ASSERT_OK(ipc_call(tcpip_server, &m)); 53 | 54 | // response 55 | static char buf[] = "HTTP/1.1 200 OK\r\nConnection: close\rContent-Length: 109\r\n\r\n" INDEX_HTML; 56 | 57 | m.type = TCPIP_WRITE_MSG; 58 | m.tcpip_write.sock = sock; 59 | memcpy(m.tcpip_write.data, buf, sizeof(buf)); 60 | m.tcpip_write.data_len = strlen(buf); 61 | ASSERT_OK(ipc_call(tcpip_server, &m)); 62 | break; 63 | } 64 | 65 | default: 66 | goto fail; 67 | } 68 | } 69 | 70 | fail: 71 | return -1; 72 | } -------------------------------------------------------------------------------- /libs/user/virtio/virtio.h: -------------------------------------------------------------------------------- 1 | // Virtioデバイスドライバライブラリ 2 | // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html 3 | #pragma once 4 | #include 5 | 6 | // デバイスステータスレジスタのビット (2.1 Device Status Field) 7 | // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-110001 8 | #define VIRTIO_STATUS_ACK 1 // OSがvirtioデバイスを認識した 9 | #define VIRTIO_STATUS_DRIVER 2 // OSがそのデバイスの利用法を知っている 10 | #define VIRTIO_STATUS_FEAT_OK 8 // OSがデバイスの機能を理解している 11 | #define VIRTIO_STATUS_DRIVER_OK 4 // OSがそのデバイスを利用する準備ができた 12 | 13 | // ディスクリプタのフラグ (2.7.5 The Virtqueue Descriptor Table) 14 | // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-430005 15 | #define VIRTQ_DESC_F_NEXT 1 // 次のディスクリプタがある 16 | #define VIRTQ_DESC_F_WRITE 2 // (デバイスから見て) 書き込み専用 17 | #define VIRTQ_AVAIL_F_NO_INTERRUPT 1 // 処理を完了しても割り込みしない 18 | 19 | /// virtqueueの管理構造体 20 | struct virtio_virtq { 21 | unsigned index; // virtqueueのインデックス 22 | int num_descs; // ディスクリプタの数 23 | int last_used_index; // 最後に読んだusedリングのインデックス 24 | int free_head; // 空きディスクリプタのインデックス。nextフィールドで 25 | // 全ての空きディスクリプタが連結されている。 26 | int num_free_descs; // 空きディスクリプタの総数 27 | struct virtq_desc *descs; // ディスクリプタテーブル 28 | struct virtq_avail *avail; // availableリング 29 | struct virtq_used *used; // usedリング 30 | }; 31 | 32 | // ディスクリプタ (2.7.5 The Virtqueue Descriptor Table) 33 | // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-430005 34 | struct virtq_desc { 35 | uint64_t addr; // DMA領域の (物理) メモリアドレス 36 | uint32_t len; // DMA領域のサイズ 37 | uint16_t flags; // フラグ (VIRTQ_DESC_F_*) 38 | uint16_t next; // 次のディスクリプタのインデックス 39 | } __packed; 40 | 41 | // availableリング (2.7.6 The Virtqueue Available Ring) 42 | // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-490006 43 | struct virtq_avail { 44 | uint16_t flags; // フラグ 45 | uint16_t index; // ringのどこまで書きこまれたかを示すインデックス 46 | uint16_t ring[]; // 各ディスクリプタチェーンの先頭インデックス 47 | } __packed; 48 | 49 | // usedリングの各エントリ (2.7.8 The Virtqueue Used Ring) 50 | // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-540008 51 | struct virtq_used_elem { 52 | uint32_t id; // ディスクリプタチェーンの先頭のディスクリプタのインデックス 53 | uint32_t len; // ディスクリプタチェーンの合計サイズ 54 | } __packed; 55 | 56 | // usedリング (2.7.8 The Virtqueue Used Ring) 57 | // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-540008 58 | struct virtq_used { 59 | uint16_t flags; // フラグ 60 | uint16_t index; // ringのどこまで書きこまれたかを示すインデックス 61 | struct virtq_used_elem ring[]; // 各処理済みエントリ 62 | } __packed; 63 | 64 | // ディスクリプタチェーン。このvirtioライブラリで使われる構造体で、デバイスドライバはこの構造体を 65 | // 通してデバイスへの処理要求・処理結果を扱う。 66 | struct virtio_chain_entry { 67 | uint16_t desc_index; // ディスクリプタテーブル上のインデックス (戻り値) 68 | paddr_t addr; // DMA領域の (物理) メモリアドレス 69 | uint32_t len; // DMA領域のサイズ 70 | bool device_writable; // デバイスから見て書き込み可能か 71 | }; 72 | -------------------------------------------------------------------------------- /libs/kernel/wasm/platform.c: -------------------------------------------------------------------------------- 1 | #include "platform_api_vmcore.h" 2 | #include "platform_api_extension.h" 3 | 4 | #include 5 | 6 | // functions declared in platform_api_vmcore.h 7 | int bh_platform_init(void) { 8 | return 0; 9 | } 10 | 11 | void bh_platform_destroy(void) {} 12 | 13 | void *os_malloc(unsigned size) { 14 | return NULL; 15 | } 16 | 17 | void *os_realloc(void *ptr, unsigned size) { 18 | return NULL; 19 | } 20 | 21 | void os_free(void *ptr) {} 22 | 23 | int os_printf(const char *fortmat, ...) { 24 | va_list vargs; 25 | va_start(vargs, fortmat); 26 | vprintf(fortmat, vargs); 27 | va_end(vargs); 28 | return 0; 29 | } 30 | 31 | int os_vprintf(const char *format, va_list ap) { 32 | vprintf(format, ap); 33 | return 0; 34 | } 35 | 36 | uint64 os_time_get_boot_microsecond(void) { 37 | return 0; 38 | } 39 | 40 | korp_tid os_self_thread(void) { 41 | return 0; 42 | } 43 | 44 | uint8 *os_thread_get_stack_boundary(void) { 45 | return NULL; 46 | } 47 | 48 | void os_thread_jit_write_protect_np(bool enabled) {} 49 | 50 | int os_mutex_init(korp_mutex *mutex) { 51 | return 0; 52 | } 53 | 54 | int os_mutex_destroy(korp_mutex *mutex) { 55 | return 0; 56 | } 57 | 58 | int os_mutex_lock(korp_mutex *mutex) { 59 | return 0; 60 | } 61 | 62 | int os_mutex_unlock(korp_mutex *mutex) { 63 | return 0; 64 | } 65 | 66 | void *os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) { 67 | return NULL; 68 | } 69 | 70 | void os_munmap(void *addr, size_t size) {} 71 | 72 | int os_mprotect(void *addr, size_t size, int prot) { 73 | return 0; 74 | } 75 | 76 | void os_dcache_flush(void) {} 77 | 78 | void os_icache_flush(void *start, size_t len) {} 79 | 80 | // functions declared in platform_api_extension.h 81 | int os_cond_init(korp_cond *cond) { 82 | NYI(); 83 | return 0; 84 | } 85 | 86 | int os_cond_destroy(korp_cond *cond) { 87 | NYI(); 88 | return 0; 89 | } 90 | 91 | int os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, uint64 useconds) { 92 | NYI(); 93 | return 0; 94 | } 95 | 96 | int os_cond_signal(korp_cond *cond) { 97 | NYI(); 98 | return 0; 99 | } 100 | 101 | int os_dumps_proc_mem_info(char *out, unsigned int size) { 102 | NYI(); 103 | return 0; 104 | } 105 | 106 | // functions used not provided by os and used in the WAMR source code. 107 | void abort(void){ 108 | NYI(); 109 | } 110 | 111 | unsigned long int strtoul(const char *nptr, char **endptr, int base) { 112 | NYI(); 113 | return 0; 114 | } 115 | 116 | unsigned long long int strtoull(const char *nptr, char **endptr, int base) { 117 | NYI(); 118 | return 0; 119 | } 120 | 121 | double strtod(const char *nptr, char **endptr) { 122 | NYI(); 123 | return 0; 124 | } 125 | 126 | float strtof(const char *nptr, char **endptr) { 127 | NYI(); 128 | return 0; 129 | } 130 | 131 | int snprintf(char *str, size_t size, const char *format, ...) { 132 | NYI(); 133 | return 0; 134 | } 135 | 136 | int vsnprintf(char *str, size_t size, const char *format, va_list ap) { 137 | NYI(); 138 | return 0; 139 | } -------------------------------------------------------------------------------- /libs/user/syscall.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // ipcシステムコール: メッセージの送受信 7 | error_t sys_ipc(task_t dst, task_t src, struct message *m, unsigned flags) { 8 | return arch_syscall(dst, src, (uintptr_t) m, flags, 0, SYS_IPC); 9 | } 10 | 11 | // notifyシステムコール: 通知の送信 12 | error_t sys_notify(task_t dst, notifications_t notifications) { 13 | return arch_syscall(dst, notifications, 0, 0, 0, SYS_NOTIFY); 14 | } 15 | 16 | // task_createシステムコール: タスクの生成 17 | task_t sys_task_create(const char *name, vaddr_t ip, task_t pager) { 18 | return arch_syscall((uintptr_t) name, ip, pager, 0, 0, SYS_TASK_CREATE); 19 | } 20 | 21 | // hinavmシステムコール: HinaVMプログラムの実行 22 | task_t sys_hinavm(const char *name, hinavm_inst_t *insts, size_t num_insts, 23 | task_t pager) { 24 | return arch_syscall((uintptr_t) name, (uintptr_t) insts, num_insts, pager, 25 | 0, SYS_HINAVM); 26 | } 27 | 28 | task_t sys_wasmvm(const char *name, uint8_t *wasm, size_t size, task_t pager) { 29 | return arch_syscall((uintptr_t) name, (uintptr_t) wasm, size, pager, 0, SYS_WASMVM); 30 | } 31 | 32 | // task_destroyシステムコール: タスクの削除 33 | error_t sys_task_destroy(task_t task) { 34 | return arch_syscall(task, 0, 0, 0, 0, SYS_TASK_DESTROY); 35 | } 36 | 37 | // task_exitシステムコール: 実行中タスクの終了 38 | __noreturn void sys_task_exit(void) { 39 | arch_syscall(0, 0, 0, 0, 0, SYS_TASK_EXIT); 40 | UNREACHABLE(); 41 | } 42 | 43 | // task_selfシステムコール: 実行中タスクのIDの取得 44 | task_t sys_task_self(void) { 45 | return arch_syscall(0, 0, 0, 0, 0, SYS_TASK_SELF); 46 | } 47 | 48 | // pm_allocシステムコール: 物理メモリの割り当て 49 | pfn_t sys_pm_alloc(task_t tid, size_t size, unsigned flags) { 50 | return arch_syscall(tid, size, flags, 0, 0, SYS_PM_ALLOC); 51 | } 52 | 53 | // vm_mapシステムコール: ページのマップ 54 | error_t sys_vm_map(task_t task, uaddr_t uaddr, paddr_t paddr, unsigned attrs) { 55 | return arch_syscall(task, uaddr, paddr, attrs, 0, SYS_VM_MAP); 56 | } 57 | 58 | // vm_unmapシステムコール: ページのアンマップ 59 | error_t sys_vm_unmap(task_t task, uaddr_t uaddr) { 60 | return arch_syscall(task, uaddr, 0, 0, 0, SYS_VM_UNMAP); 61 | } 62 | 63 | // irq_listenシステムコール: 割り込み通知の購読 64 | error_t sys_irq_listen(unsigned irq) { 65 | return arch_syscall(irq, 0, 0, 0, 0, SYS_IRQ_LISTEN); 66 | } 67 | 68 | // irq_unlistenシステムコール: 割り込み通知の購読解除 69 | error_t sys_irq_unlisten(unsigned irq) { 70 | return arch_syscall(irq, 0, 0, 0, 0, SYS_IRQ_UNLISTEN); 71 | } 72 | 73 | // serial_writeシステムコール: 文字列の主力 74 | int sys_serial_write(const char *buf, size_t len) { 75 | return arch_syscall((uintptr_t) buf, len, 0, 0, 0, SYS_SERIAL_WRITE); 76 | } 77 | 78 | // serial_readシステムコール: 文字入力の読み込み 79 | int sys_serial_read(const char *buf, int max_len) { 80 | return arch_syscall((uintptr_t) buf, max_len, 0, 0, 0, SYS_SERIAL_READ); 81 | } 82 | 83 | // timeシステムコール: タイムアウトの設定 84 | error_t sys_time(int milliseconds) { 85 | return arch_syscall(milliseconds, 0, 0, 0, 0, SYS_TIME); 86 | } 87 | 88 | // uptimeシステムコール: システムの起動時間の取得 (ミリ秒) 89 | int sys_uptime(void) { 90 | return arch_syscall(0, 0, 0, 0, 0, SYS_UPTIME); 91 | } 92 | 93 | // shutdownシステムコール: システムのシャットダウン 94 | __noreturn void sys_shutdown(void) { 95 | arch_syscall(0, 0, 0, 0, 0, SYS_SHUTDOWN); 96 | UNREACHABLE(); 97 | } 98 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Utility functions used in test.py (pytest loads this file automatically). 4 | 5 | """ 6 | 7 | import pytest 8 | import os 9 | import subprocess 10 | import shlex 11 | import struct 12 | import tempfile 13 | import re 14 | import sys 15 | 16 | # To avoid building WasmOS every time, use placefolder and replace this with the specified script. 17 | AUTORUN_PLACEHOLDER = "__REPLACE_ME_AUTORUN__" + "a" * 512 18 | ANSI_ESCAPE_SEQ_REGEX = re.compile(r'\x1B\[[^m]+m') 19 | 20 | runner = None 21 | 22 | # Build WasmOS and create a runner. 23 | @pytest.fixture 24 | def run_wasmos(request): 25 | # Build WasmOS if not built. 26 | global runner 27 | if runner is None: 28 | build_argv = [ 29 | "make", 30 | "build", 31 | f"AUTORUN={AUTORUN_PLACEHOLDER}", 32 | f"-j{os.cpu_count()}" 33 | ] 34 | subprocess.run(build_argv, check=True) 35 | 36 | # Create Runner. 37 | runner = Runner( 38 | request.config.getoption("--qemu"), 39 | shlex.split(os.environ["QEMUFLAGS"]), 40 | "build/wasmos.elf" 41 | ) 42 | 43 | return do_run_wasmos 44 | 45 | def do_run_wasmos(script, qemu_net0_options=None): 46 | return runner.run(script + "; shutdown", qemu_net0_options) 47 | 48 | class Result: 49 | def __init__(self, log, raw_log): 50 | self.log = log 51 | self.raw_log = raw_log 52 | 53 | class Runner: 54 | def __init__(self, qemu, default_qemu_flags, image_path): 55 | self.qemu = qemu 56 | self.default_qemu_flags = default_qemu_flags 57 | self.image = open(image_path, "rb").read() 58 | self.placeholder_bytes = AUTORUN_PLACEHOLDER.encode("ascii") 59 | 60 | def run(self, script, qemu_net0_options): 61 | if len(script.encode("ascii")) > len(self.placeholder_bytes): 62 | raise ValueError("script is too long") 63 | 64 | qemu_args = [] 65 | 66 | for arg in self.default_qemu_flags: 67 | if qemu_net0_options is not None and "-netdev user, id=net0" in arg: 68 | qemu_args.append(arg + "," + ",".join(qemu_net0_options)) 69 | else: 70 | qemu_args.append(arg) 71 | 72 | script_bytes = struct.pack( 73 | f"{len(AUTORUN_PLACEHOLDER)}s", 74 | script.encode("ascii") 75 | ) 76 | 77 | with tempfile.NamedTemporaryFile(mode="wb+", delete=False) as f: 78 | # Replace the placefolder with the specified script. 79 | f.write(self.image.replace(self.placeholder_bytes, script_bytes)) 80 | f.close() 81 | 82 | # Execute the specified script on Qemu. 83 | qemu_argv = [self.qemu, "-kernel", f.name, "-snapshot"] + qemu_args 84 | 85 | try: 86 | raw_log = subprocess.check_output(qemu_argv) 87 | finally: 88 | os.remove(f.name) 89 | 90 | # Write outputs to stdout (for debugging). 91 | sys.stdout.buffer.write(raw_log) 92 | 93 | log = raw_log.decode('utf-8', errors='backslashreplace') 94 | # Remove color. 95 | log = re.sub(ANSI_ESCAPE_SEQ_REGEX, '', log) 96 | 97 | return Result(log, raw_log) 98 | 99 | # Define command line options 100 | def pytest_addoption(parser): 101 | parser.addoption("--qemu", required=True) -------------------------------------------------------------------------------- /kernel/main.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | #include "arch.h" 3 | #include "memory.h" 4 | #include "printk.h" 5 | #include "task.h" 6 | #include 7 | #include 8 | 9 | // 最初のユーザータスク (VMサーバ) を作成する 10 | static void create_first_task(struct bootinfo *bootinfo) { 11 | // ELFヘッダのポインタを取得し、マジックナンバーを確認する 12 | elf_ehdr_t *header = (elf_ehdr_t *) arch_paddr_to_vaddr(bootinfo->boot_elf); 13 | if (memcmp(header->e_ident, ELF_MAGIC, 4) != 0) { 14 | PANIC("bootelf: invalid ELF magic\n"); 15 | } 16 | 17 | // 最初のタスク (VMサーバ) を作成する 18 | task_t tid = task_create("vm", header->e_entry, NULL); 19 | ASSERT_OK(tid); 20 | struct task *task = task_find(tid); 21 | 22 | // プログラムヘッダを読み込む 23 | elf_phdr_t *phdrs = (elf_phdr_t *) ((vaddr_t) header + header->e_phoff); 24 | for (uint16_t i = 0; i < header->e_phnum; i++) { 25 | elf_phdr_t *phdr = &phdrs[i]; 26 | 27 | // PT_LOAD 以外のセグメント (マップする必要のないセグメント) は無視する 28 | if (phdr->p_type != PT_LOAD) { 29 | continue; 30 | } 31 | 32 | ASSERT(phdr->p_memsz >= phdr->p_filesz); 33 | 34 | // デバッグ用にセグメントの情報を出力する 35 | char r = (phdr->p_flags & PF_R) ? 'r' : '-'; 36 | char w = (phdr->p_flags & PF_W) ? 'w' : '-'; 37 | char x = (phdr->p_flags & PF_X) ? 'x' : '-'; 38 | size_t size_in_kb = phdr->p_memsz / 1024; 39 | TRACE("bootelf: %p - %p %c%c%c (%d KiB)", phdr->p_vaddr, 40 | phdr->p_vaddr + phdr->p_memsz, r, w, x, size_in_kb); 41 | 42 | // セグメントのマップ先となる物理メモリ領域を確保する 43 | paddr_t paddr = pm_alloc(phdr->p_memsz, task, PM_ALLOC_ZEROED); 44 | ASSERT(paddr != 0); 45 | 46 | // セグメントの内容を確保したメモリ領域にコピーする 47 | memcpy((void *) arch_paddr_to_vaddr(paddr), 48 | (void *) ((vaddr_t) header + phdr->p_offset), phdr->p_filesz); 49 | 50 | // セグメントが必要とするページのアクセス権限を取り出す 51 | unsigned attrs = PAGE_USER; 52 | attrs |= (phdr->p_flags & PF_R) ? PAGE_READABLE : 0; 53 | attrs |= (phdr->p_flags & PF_W) ? PAGE_WRITABLE : 0; 54 | attrs |= (phdr->p_flags & PF_X) ? PAGE_EXECUTABLE : 0; 55 | 56 | // タスクの各ページをマップする 57 | size_t memsz = ALIGN_UP(phdr->p_memsz, PAGE_SIZE); 58 | for (offset_t offset = 0; offset < memsz; offset += PAGE_SIZE) { 59 | error_t err = 60 | vm_map(task, phdr->p_vaddr + offset, paddr + offset, attrs); 61 | if (err != OK) { 62 | PANIC("bootelf: failed to map %p - %p", phdr->p_vaddr, 63 | phdr->p_vaddr + phdr->p_memsz); 64 | } 65 | } 66 | } 67 | } 68 | 69 | // アイドルタスク: 他のタスクが実行可能な状態になるまで割り込み可能状態でCPUをスリープさせる。 70 | __noreturn static void idle_task(void) { 71 | for (;;) { 72 | task_switch(); 73 | arch_idle(); 74 | } 75 | } 76 | 77 | // 0番目のCPUのブート処理: カーネルと最初のタスク (VMサーバ) を初期化した後にアイドルタスク 78 | // として動作する。 79 | void kernel_main(struct bootinfo *bootinfo) { 80 | printf("Booting WasmOS...\n"); 81 | memory_init(bootinfo); 82 | arch_init(); 83 | task_init_percpu(); 84 | create_first_task(bootinfo); 85 | arch_init_percpu(); 86 | TRACE("CPU #%d is ready", CPUVAR->id); 87 | 88 | // ここからアイドルタスクとして動作する 89 | idle_task(); 90 | } 91 | 92 | // 0番目以外のCPUのブート処理: 各CPUの初期化処理を行った後にアイドルタスクとして動作する。 93 | void kernel_mp_main(void) { 94 | task_init_percpu(); 95 | arch_init_percpu(); 96 | TRACE("CPU #%d is ready", CPUVAR->id); 97 | 98 | // ここからアイドルタスクとして動作する 99 | idle_task(); 100 | } 101 | -------------------------------------------------------------------------------- /kernel/riscv32/mp.c: -------------------------------------------------------------------------------- 1 | #include "mp.h" 2 | #include "asm.h" 3 | #include 4 | #include 5 | #include 6 | 7 | static struct cpuvar cpuvars[NUM_CPUS_MAX]; 8 | static uint32_t big_lock = BKL_UNLOCKED; 9 | static int locked_cpu = -1; 10 | 11 | // setssipレジスタ (ACLINT) への書き込みをしてIPIを発行する。 12 | static void write_setssip(uint32_t hartid) { 13 | full_memory_barrier(); 14 | mmio_write32_paddr(ACLINT_SSWI_SETSSIP(hartid), 1); 15 | } 16 | 17 | // カーネルロック取得前に満たしておくべき条件をチェックする 18 | static void check_lock(void) { 19 | DEBUG_ASSERT((read_sstatus() & SSTATUS_SIE) == 0); 20 | 21 | if (big_lock == BKL_HALTED) { 22 | // 他のCPUが停止状態に入っている。そのCPUがパニックメッセージを出力するために 23 | // 無理矢理カーネルロックを奪っているため、処理を進めるとまずい。 24 | for (;;) { 25 | asm_wfi(); 26 | } 27 | } 28 | } 29 | 30 | // カーネルロックを取得する 31 | void mp_lock(void) { 32 | check_lock(); // ロックの状態をチェック (デバッグ用) 33 | 34 | // 書き込み成功するまで試行し続ける 35 | while (true) { 36 | if (compare_and_swap(&big_lock, BKL_UNLOCKED, BKL_LOCKED)) { 37 | break; 38 | } 39 | } 40 | 41 | locked_cpu = CPUVAR->id; // カーネルロックを持っているCPUのIDを記録 42 | 43 | // ここ以降のメモリ読み書きが上のロック取得前に行われないようにする (メモリバリア)。 44 | // これがないとCPUやコンパイラが並び替えてしまう恐れがある。 45 | full_memory_barrier(); 46 | } 47 | 48 | // カーネルロックを解放する 49 | void mp_unlock(void) { 50 | DEBUG_ASSERT(CPUVAR->id == locked_cpu); 51 | 52 | // ここ以前のメモリ読み書きが上のロック解放前に行われるようにする (メモリバリア)。 53 | // これがないとCPUやコンパイラが並び替えてしまう恐れがある。 54 | full_memory_barrier(); 55 | 56 | // ロックを解放 57 | compare_and_swap(&big_lock, BKL_LOCKED, BKL_UNLOCKED); 58 | } 59 | 60 | // カーネルロックを強制的に取得する。カーネルパニックなど致命的なエラーが発生したときに 61 | // 他のCPUからロックを奪い取ってカーネルを停止するために使う。 62 | void mp_force_lock(void) { 63 | big_lock = BKL_LOCKED; 64 | locked_cpu = CPUVAR->id; 65 | full_memory_barrier(); 66 | } 67 | 68 | // 指定したCPUのCPUローカル変数を取得する 69 | struct cpuvar *riscv32_cpuvar_of(int hartid) { 70 | ASSERT(hartid < NUM_CPUS_MAX); 71 | return &cpuvars[hartid]; 72 | } 73 | 74 | // 他のCPUにプロセッサ間割り込み (IPI) を送信する 75 | void arch_send_ipi(unsigned ipi) { 76 | // 自身を除いた全CPUにIPIを送信する 77 | for (int hartid = 0; hartid < NUM_CPUS_MAX; hartid++) { 78 | struct cpuvar *cpuvar = riscv32_cpuvar_of(hartid); 79 | 80 | // 起動が完了しているCPUかつ自身以外かチェック 81 | if (cpuvar->online && hartid != CPUVAR->id) { 82 | // IPIの送信理由を宛先CPUのローカル変数に記録する (アトミックな |= 演算) 83 | atomic_fetch_and_or(&cpuvar->ipi_pending, ipi); 84 | 85 | // IPIを送信する 86 | write_setssip(hartid); 87 | } 88 | } 89 | 90 | // 各CPUがIPIを処理するまで待つ 91 | for (int hartid = 0; hartid < NUM_CPUS_MAX; hartid++) { 92 | struct cpuvar *cpuvar = riscv32_cpuvar_of(hartid); 93 | if (cpuvar->online && hartid != CPUVAR->id) { 94 | // 一旦カーネルロックを解放して他のCPUがカーネルに入れるようにする 95 | mp_unlock(); 96 | 97 | // CPUがIPIを処理するまで待つ 98 | unsigned pending; 99 | do { 100 | pending = atomic_load(&cpuvar->ipi_pending); 101 | } while (pending != 0); 102 | 103 | // カーネルロックを再取得 104 | mp_lock(); 105 | } 106 | } 107 | } 108 | 109 | // 各CPUの初期化処理 110 | void riscv32_mp_init_percpu(void) { 111 | CPUVAR->online = true; 112 | } 113 | 114 | // コンピュータを停止する 115 | __noreturn void halt(void) { 116 | big_lock = BKL_HALTED; 117 | full_memory_barrier(); 118 | 119 | WARN("kernel halted (CPU #%d)", CPUVAR->id); 120 | for (;;) { 121 | asm_wfi(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /servers/vm/page_fault.c: -------------------------------------------------------------------------------- 1 | #include "page_fault.h" 2 | #include "bootfs.h" 3 | #include "task.h" 4 | #include 5 | #include 6 | 7 | // ページフォルト処理。ページを用意してマップする。できなかった場合はエラーを返す。 8 | error_t handle_page_fault(struct task *task, uaddr_t uaddr, uaddr_t ip, 9 | unsigned fault) { 10 | if (uaddr < PAGE_SIZE) { 11 | // 0番地付近のアドレスはマップできないため、その付近へのアクセスはヌルポインタ参照 12 | // とみなす。なぜ uaddr == 0 ではないかというと、構造体へのヌルポインタに対して 13 | // メンバへのアクセスを試みる場合、uaddrはゼロではなくメンバのオフセットになるため。 14 | WARN("%s (%d): null pointer dereference at vaddr=%p, ip=%p", task->name, 15 | task->tid, uaddr, ip); 16 | return ERR_NOT_ALLOWED; 17 | } 18 | 19 | // ページ境界にアラインする。 20 | uaddr_t uaddr_original = uaddr; 21 | uaddr = ALIGN_DOWN(uaddr, PAGE_SIZE); 22 | 23 | if (fault & PAGE_FAULT_PRESENT) { 24 | // 既にページが存在する。アクセス権限が不正な場合、たとえば読み込み専用ページに 25 | // 書き込もうとした場合。 26 | WARN( 27 | "%s: invalid memory access at %p (IP=%p, reason=%s%s%s, perhaps segfault?)", 28 | task->name, uaddr_original, ip, 29 | (fault & PAGE_FAULT_READ) ? "read" : "", 30 | (fault & PAGE_FAULT_WRITE) ? "write" : "", 31 | (fault & PAGE_FAULT_EXEC) ? "exec" : ""); 32 | return ERR_NOT_ALLOWED; 33 | } 34 | 35 | // ページフォルトが起きたアドレスを踏むセグメントを探す。 36 | elf_phdr_t *phdr = NULL; 37 | for (unsigned i = 0; i < task->ehdr->e_phnum; i++) { 38 | if (task->phdrs[i].p_type != PT_LOAD) { 39 | // PT_LOAD以外の、メモリ上に展開されないセグメントは無視する。 40 | continue; 41 | } 42 | 43 | // ページフォルトが起きたアドレスがセグメントの範囲内にあるかどうかを調べる。 44 | uaddr_t start = task->phdrs[i].p_vaddr; 45 | uaddr_t end = start + task->phdrs[i].p_memsz; 46 | if (start <= uaddr && uaddr < end) { 47 | phdr = &task->phdrs[i]; 48 | break; 49 | } 50 | } 51 | 52 | // 該当するセグメントがない場合は無効なアドレスとみなす。 53 | if (!phdr) { 54 | ERROR("unknown memory address (addr=%p, IP=%p), killing %s...", 55 | uaddr_original, ip, task->name); 56 | return ERR_INVALID_ARG; 57 | } 58 | 59 | // 物理ページを用意する。 60 | pfn_t pfn_or_err = sys_pm_alloc(task->tid, PAGE_SIZE, 0); 61 | if (IS_ERROR(pfn_or_err)) { 62 | return pfn_or_err; 63 | } 64 | 65 | // pm_allocは物理ページ番号を返すので、物理アドレスに変換する。 66 | paddr_t paddr = PFN2PADDR(pfn_or_err); 67 | 68 | // 割り当てた物理ページにセグメントの内容をELFイメージからコピーする。 69 | size_t offset = uaddr - phdr->p_vaddr; 70 | if (offset < phdr->p_filesz) { 71 | // セグメントの内容をコピーするための仮想アドレス領域。ここで確保したメモリ領域が 72 | // 実際に使われず、この領域の仮想アドレスが他の物理ページにマップされる。 73 | static __aligned(PAGE_SIZE) uint8_t tmp_page[PAGE_SIZE]; 74 | 75 | // tmp_pageを一旦アンマップする。カーネルによって起動時にマップされているため。 76 | ASSERT_OK(sys_vm_unmap(sys_task_self(), (uaddr_t) tmp_page)); 77 | 78 | // tmp_pageをpaddrにマップする。これにより、tmp_pageの仮想アドレスを介して 79 | // paddrの内容にアクセスできるようになる。 80 | ASSERT_OK(sys_vm_map(sys_task_self(), (uaddr_t) tmp_page, paddr, 81 | PAGE_READABLE | PAGE_WRITABLE)); 82 | 83 | // BootFSからセグメントの内容を読み込む。 84 | size_t copy_len = MIN(PAGE_SIZE, phdr->p_filesz - offset); 85 | bootfs_read(task->file, phdr->p_offset + offset, tmp_page, copy_len); 86 | } 87 | 88 | // ページの属性をセグメント情報から決定する。 89 | unsigned attrs = 0; 90 | attrs |= (phdr->p_flags & PF_R) ? PAGE_READABLE : 0; 91 | attrs |= (phdr->p_flags & PF_W) ? PAGE_WRITABLE : 0; 92 | attrs |= (phdr->p_flags & PF_X) ? PAGE_EXECUTABLE : 0; 93 | 94 | // ページをマップする。 95 | ASSERT(phdr->p_filesz <= phdr->p_memsz); 96 | ASSERT_OK(sys_vm_map(task->tid, uaddr, paddr, attrs)); 97 | return OK; 98 | } 99 | -------------------------------------------------------------------------------- /libs/common/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // メモリの内容を比較する。 5 | int memcmp(const void *p1, const void *p2, size_t len) { 6 | uint8_t *s1 = (uint8_t *) p1; 7 | uint8_t *s2 = (uint8_t *) p2; 8 | while (*s1 == *s2 && len > 0) { 9 | s1++; 10 | s2++; 11 | len--; 12 | } 13 | 14 | return (len > 0) ? *s1 - *s2 : 0; 15 | } 16 | 17 | // メモリ領域の各バイトを指定した値で埋める。 18 | void *memset(void *dst, int ch, size_t len) { 19 | uint8_t *d = dst; 20 | while (len-- > 0) { 21 | *d = ch; 22 | d++; 23 | } 24 | return dst; 25 | } 26 | 27 | // メモリ領域をコピーする。 28 | void *memcpy(void *dst, const void *src, size_t len) { 29 | DEBUG_ASSERT(len < 256 * 1024 * 1024 /* 256MiB */ 30 | && "too long memcpy (perhaps integer overflow?)"); 31 | 32 | uint8_t *d = dst; 33 | const uint8_t *s = src; 34 | while (len-- > 0) { 35 | *d = *s; 36 | d++; 37 | s++; 38 | } 39 | return dst; 40 | } 41 | 42 | // メモリ領域をコピーする。重なりがあっても正しく動作する。 43 | void *memmove(void *dst, const void *src, size_t len) { 44 | DEBUG_ASSERT(len < 256 * 1024 * 1024 /* 256MiB */ 45 | && "too long memmove (perhaps integer overflow?)"); 46 | 47 | if ((uintptr_t) dst <= (uintptr_t) src) { 48 | memcpy(dst, src, len); 49 | } else { 50 | uint8_t *d = dst + len; 51 | const uint8_t *s = src + len; 52 | while (len-- > 0) { 53 | *d = *s; 54 | --d; 55 | --s; 56 | } 57 | } 58 | return dst; 59 | } 60 | 61 | // 文字列の長さを返す。 62 | size_t strlen(const char *s) { 63 | size_t len = 0; 64 | while (*s != '\0') { 65 | len++; 66 | s++; 67 | } 68 | return len; 69 | } 70 | 71 | // 文字列を比較する。同じなら0を返す。 72 | int strcmp(const char *s1, const char *s2) { 73 | while (true) { 74 | if (*s1 != *s2) { 75 | return *s1 - *s2; 76 | } 77 | 78 | if (*s1 == '\0') { 79 | return 0; 80 | } 81 | 82 | s1++; 83 | s2++; 84 | } 85 | 86 | return 0; 87 | } 88 | 89 | // 指定した文字数まで文字列を比較する。同じなら0を返す。 90 | int strncmp(const char *s1, const char *s2, size_t len) { 91 | while (len > 0) { 92 | if (*s1 != *s2) { 93 | return *s1 - *s2; 94 | } 95 | 96 | if (*s1 == '\0') { 97 | // Both `*s1` and `*s2` equal to '\0'. 98 | break; 99 | } 100 | 101 | s1++; 102 | s2++; 103 | len--; 104 | } 105 | 106 | return 0; 107 | } 108 | 109 | // 文字列をコピーする。宛先のバッファサイズを超える場合は、バッファに収まるだけをコピーする。 110 | char *strcpy_safe(char *dst, size_t dst_len, const char *src) { 111 | ASSERT(dst_len > 0); 112 | 113 | size_t i = 0; 114 | while (i < dst_len - 1 && src[i] != '\0') { 115 | dst[i] = src[i]; 116 | i++; 117 | } 118 | 119 | dst[i] = '\0'; 120 | return dst; 121 | } 122 | 123 | // 指定した文字を文字列から探し、その位置を返す。 124 | char *strchr(const char *str, int c) { 125 | char *s = (char *) str; 126 | while (*s != '\0') { 127 | if (*s == c) { 128 | return s; 129 | } 130 | 131 | s++; 132 | } 133 | 134 | return NULL; 135 | } 136 | 137 | // 指定した文字列を文字列から探し、その位置を返す。 138 | char *strstr(const char *haystack, const char *needle) { 139 | char *s = (char *) haystack; 140 | size_t needle_len = strlen(needle); 141 | while (*s != '\0') { 142 | if (!strncmp(s, needle, needle_len)) { 143 | return s; 144 | } 145 | 146 | s++; 147 | } 148 | 149 | return NULL; 150 | } 151 | 152 | // 文字列を数値に変換する。10進数のみ対応。 153 | int atoi(const char *s) { 154 | int x = 0; 155 | while ('0' <= *s && *s <= '9') { 156 | x = (x * 10) + (*s - '0'); 157 | s++; 158 | } 159 | 160 | return x; 161 | } 162 | -------------------------------------------------------------------------------- /servers/shell/main.c: -------------------------------------------------------------------------------- 1 | #include "command.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static char *skip_whitespaces(char *p) { 8 | for (;;) { 9 | switch (*p) { 10 | case ' ': 11 | case '\t': 12 | case '\n': 13 | p++; 14 | continue; 15 | default: 16 | return p; 17 | } 18 | } 19 | } 20 | 21 | static char *consume_argv(char *p) { 22 | for (;;) { 23 | switch (*p) { 24 | case '\0': 25 | case ';': 26 | return p; 27 | case ' ': 28 | case '\t': 29 | case '\n': 30 | *p = '\0'; 31 | p++; 32 | return p; 33 | default: 34 | p++; 35 | } 36 | } 37 | } 38 | 39 | static void eval(char *script) { 40 | char *p = script; 41 | 42 | while (*p != '\0') { 43 | struct args args; 44 | args.argc = 0; 45 | do { 46 | p = skip_whitespaces(p); 47 | args.argv[args.argc] = p; 48 | args.argc++; 49 | p = consume_argv(p); 50 | } while (*p != '\0' && *p != ';'); 51 | 52 | while (*p == ';') { 53 | *p = '\0'; 54 | p++; 55 | } 56 | 57 | run_command(&args); 58 | } 59 | } 60 | 61 | static error_t read_line(char *cmdline, int len) { 62 | int i = 0; 63 | while (true) { 64 | char buf[32]; 65 | int read_len = sys_serial_read(buf, sizeof(buf)); 66 | if (read_len < 0) { 67 | WARN("serial_read failed: %d", read_len); 68 | return read_len; 69 | } 70 | 71 | for (int j = 0; j < read_len; j++) { 72 | if (i >= len) { 73 | WARN("command line too long"); 74 | return ERR_TOO_LARGE; 75 | } 76 | 77 | char ch = buf[j]; 78 | switch (ch) { 79 | case 0x1b: 80 | if (j + 2 < read_len) { 81 | // 矢印キー入力を無視する 82 | j += 2; 83 | } 84 | continue; 85 | case 0x7f: 86 | if (i > 0) { 87 | printf("\b \b"); 88 | printf_flush(); 89 | i--; 90 | } 91 | continue; 92 | case '\r': 93 | ch = '\n'; 94 | printf("\r"); 95 | break; 96 | } 97 | 98 | printf("%c", ch); 99 | printf_flush(); 100 | 101 | if (ch == '\n') { 102 | cmdline[i] = '\0'; 103 | return OK; 104 | } 105 | 106 | cmdline[i] = ch; 107 | i++; 108 | } 109 | } 110 | } 111 | void main(void) { 112 | char cmdline[1024]; 113 | 114 | // startコマンドで起動したタスクが終了したら知らせてもらうようにしておく。 115 | struct message m; 116 | m.type = WATCH_TASKS_MSG; 117 | ASSERT_OK(ipc_call(VM_SERVER, &m)); 118 | 119 | char *autorun = AUTORUN; 120 | if (autorun[0] != '\0') { 121 | strcpy_safe(cmdline, sizeof(cmdline), autorun); 122 | INFO("running autorun script: %s", cmdline); 123 | eval(cmdline); 124 | } 125 | 126 | // 他のサーバが起動しているとシェルのプロンプトがログの中に紛れて分かりづらいので、 127 | // ここでちょっと待つ。 128 | sys_time(100); 129 | ipc_recv(IPC_ANY, &m); 130 | ASSERT(m.type == NOTIFY_TIMER_MSG); 131 | 132 | printf("\nWelcome to WasmOS!\n\n"); 133 | while (true) { 134 | printf("\x1b[1mshell> \x1b[0m"); 135 | printf_flush(); 136 | 137 | error_t err = read_line(cmdline, sizeof(cmdline)); 138 | if (err == OK) { 139 | eval(cmdline); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /DEBUG.md: -------------------------------------------------------------------------------- 1 | # HinaOSのデバッグ 2 | 3 | ## printfデバッグ 4 | 5 | 一番手っ取り早いのが変数の値をprintfで出力する方法です。HinaOSではprintfを直接呼び出す代わりに次のようなマクロを利用します。各マクロは引数にprintfのフォーマット文字列とその引数を取ります。 6 | 7 | ```c 8 | #define TRACE(fmt, ...) // トレースメッセージ 9 | #define DBG(fmt, ...) // デバッグメッセージ 10 | #define INFO(fmt, ...) // 普通のメッセージ 11 | #define WARN(fmt, ...) // 警告メッセージ 12 | #define ERROR(fmt, ...) // エラーメッセージ 13 | #define OOPS(fmt, ...) // 警告メッセージ + スタックトレース 14 | #define PANIC(fmt, ...) // エラーメッセージ + スタックトレース + 強制終了 15 | ``` 16 | 17 | ### 利用例 18 | 19 | ```c 20 | #include 21 | 22 | int i = 123; 23 | char *s = "abc"; 24 | INFO("hello world: %d, %s", i, s); // hello world: 123, abc 25 | ``` 26 | 27 | ## タスクダンプ 28 | 29 | Ctrl+Pを押下すると、HinaOSカーネルが各タスクの状態を次のように表示します。処理がどこかで止まっている場合は、この結果を元に原因を特定することができます。メッセージパッシングが同期的な処理であるため、とりわけ「各タスクがどのタスクに対してメッセージの送信待ち・受信待ちしているのか」が重要です。 30 | 31 | ``` 32 | [kernel] WARN: active tasks: 33 | [kernel] WARN: #1: vm: BLOCKED (open receive) 34 | [kernel] WARN: #2: virtio_blk: BLOCKED (open receive) 35 | [kernel] WARN: #3: tcpip: BLOCKED (open receive) 36 | [kernel] WARN: #4: virtio_net: BLOCKED (open receive) 37 | [kernel] WARN: #5: shell: BLOCKED (send, serial_read, or exited) 38 | [kernel] WARN: #6: fs: BLOCKED (open receive) 39 | ``` 40 | 41 | ## デバッガ (GDB) を利用したデバッグ 42 | 43 | QEMUにはGDBを利用したデバッグ機能が組み込まれています。この機能を活用することで、HinaOSが具体的にどのような処理を行っているのかを追うことができます。 44 | 45 | GDBを利用するデバッグには (1) QEMUを実行するターミナルと (2) GDBを実行するターミナルの2つが必要です。QEMUを実行するターミナルでは、次のように `GDBSERVER=1` を指定して起動します。 46 | 47 | ``` 48 | make run GDBSERVER=1 49 | ``` 50 | 51 | QEMUはGDBが接続されるまでHinaOSを起動しません。GDBを実行するターミナルでは、次のように `make gdb` を実行します。OSごとにGDBのパスが異なるため、OSに合わせて `GDB` 変数を指定してください。なお、Homebrewでインストールした場合は指定する必要はありません。 52 | 53 | ``` 54 | # macOS Homebrew (riscv-software-src/riscv/riscv-tools パッケージ) 55 | make gdb 56 | 57 | # Ubuntu (gdb-multiarch パッケージ) 58 | make gdb GDB=gdb-multiarch 59 | 60 | # Windows MSYS2 (mingw-w64-clang-x86_64-gdb-multiarch パッケージ) 61 | make gdb GDB="c:\msys64\clang64\bin\gdb-multiarch.exe" 62 | ``` 63 | 64 | GDBが起動すると、次のようなメッセージが表示されます。 65 | 66 | ``` 67 | 0x00001000 in ?? () 68 | => 0x00001000: 97 02 00 00 auipc t0,0x0 69 | (gdb) 70 | ``` 71 | 72 | `(gdb)` が表示されたら、GDBのコマンドを入力できます。GDBのコマンドには次の様なものがあります。 73 | 74 | - `q`: プログラムを終了する。 75 | - `c`: プログラムを続行する。 76 | - `s`: ステップ実行をする。 77 | - `p 変数名`: 変数の値を表示する。 78 | - `b シンボル名`: ブレークポイントを設定する。シンボル名は下記の注意事項を参照。 79 | - `bt`: スタックトレースを表示する。 80 | - `info registers`: レジスタの値を表示する。 81 | 82 | > **Note** 83 | > 84 | > HinaOSではカーネルとユーザプログラムの両方をデバッグすることができます。ただしシンボル名の重複を避けるために、各シンボル名には`kernel.`または`<サーバ名>.`の接頭辞が付いています。例えば、`kernel.main`はカーネルの`main`関数を表します。 85 | 86 | 詳しくはGDBのマニュアルを参照してください。基本的には次の様な流れで利用します。 87 | 88 | 1. `b`コマンドで確認したい関数の先頭にブレークポイントを設定する。 89 | 2. `c`コマンドでHinaOSを起動する。 90 | 3. ブレークポイントで停止すると、GDBのプロンプトが表示される。 91 | 4. `bt`コマンドでスタックトレースを表示したり、`s`コマンドでステップ実行していくことで動作の流れを確認したり、`p` コマンドで変数の値を確認したりする。 92 | 5. `q`コマンドでGDBを終了する。 93 | 94 | 以下に、実際にGDBを利用したデバッグの例を示します。この例ではVMサーバの`bootfs_read`関数にブレークポイントを設定し、それがどこから呼ばれるのか (スタックトレース) 確認しています。 95 | 96 | ``` 97 | 0x00001000 in ?? () 98 | => 0x00001000: 97 02 00 00 auipc t0,0x0 99 | (gdb) b vm.bootfs_read 100 | Breakpoint 1 at 0x2e001fe0: file servers/vm/bootfs.c, line 11. 101 | (gdb) c 102 | Continuing. 103 | 104 | Breakpoint 1, bootfs_read (file=0x2e00d7d4, off=0, buf=0x2e6a3060, len=4096) at servers/vm/bootfs.c:11 105 | 11 void *p = (void *) (((uaddr_t) __bootfs) + file->offset + off); 106 | (gdb) bt 107 | #0 bootfs_read (file=0x2e00d7d4, off=0, buf=0x2e6a3060, len=4096) 108 | at servers/vm/bootfs.c:11 109 | #1 0x2e000cb6 in task_spawn (file=0x2e00d7d4) at servers/vm/task.c:34 110 | #2 0x2e00075a in spawn_servers () at servers/vm/main.c:26 111 | #3 main () at servers/vm/main.c:50 112 | #4 0x2e008692 in vm[] () at libs/user/riscv32/start.S:10 113 | Backtrace stopped: frame did not save the PC 114 | (gdb) 115 | ``` 116 | 117 | ### QEMUにprintfデバッグを追加する 118 | 119 | HinaOSではなくQEMU自体にprintfを入れたり、QEMU自体にデバッガをアタッチして挙動を観察するのも有効なデバッグ手段です。例えば、virtioデバイスがうまく動いていない場合、virtioデバイスのエミュレーション部分にprintfをたくさん入れて、どこまで処理が進んでいるのか、どこで中断されているのかを確認することができます。 120 | 121 | なお、QEMUにはトレース機能 ([ドキュメント](https://qemu-project.gitlab.io/qemu/devel/tracing.html)) が備わっていますが、自身で知りたい情報をprintfで出力する方が正直楽です。 122 | -------------------------------------------------------------------------------- /servers/fs/block.c: -------------------------------------------------------------------------------- 1 | #include "block.h" 2 | #include "fs.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include // SECTOR_SIZE 8 | 9 | // ブロックデバイスドライバサーバのタスクID。 10 | static task_t blk_server; 11 | // キャッシュされたブロックのリスト。 12 | static list_t cached_blocks = LIST_INIT(cached_blocks); 13 | // 変更済みブロックのリスト。ディスクに書き戻す必要がある。 14 | static list_t dirty_blocks = LIST_INIT(dirty_blocks); 15 | 16 | // ブロック番号をセクタ番号に変換する。 17 | static uint64_t block_to_sector(block_t index) { 18 | return (index * BLOCK_SIZE) / SECTOR_SIZE; 19 | } 20 | 21 | // ブロックが変更済みかどうかを返す。 22 | static bool block_is_dirty(struct block *block) { 23 | return list_is_linked(&block->dirty_next); 24 | } 25 | 26 | // ブロックをディスクに書き込む。 27 | static void block_write(struct block *block) { 28 | unsigned sector_base = block_to_sector(block->index); 29 | // 各セクタごとに書き込む 30 | for (int offset = 0; offset < BLOCK_SIZE; offset += SECTOR_SIZE) { 31 | struct message m; 32 | m.type = BLK_WRITE_MSG; 33 | m.blk_write.sector = sector_base + (offset / SECTOR_SIZE); 34 | m.blk_write.data_len = SECTOR_SIZE; 35 | memcpy(m.blk_write.data, block->data + offset, SECTOR_SIZE); 36 | error_t err = ipc_call(blk_server, &m); 37 | if (err != OK) { 38 | OOPS("failed to write block %d: %s", block->index, err2str(err)); 39 | } 40 | } 41 | } 42 | 43 | // ブロックをブロックキャッシュに読み込む。 44 | error_t block_read(block_t index, struct block **block) { 45 | if (index == 0xffff) { 46 | OOPS("invalid block index: %x", index); 47 | return ERR_INVALID_ARG; 48 | } 49 | 50 | // 既にキャッシュされていれば、それを返す。 51 | LIST_FOR_EACH (b, &cached_blocks, struct block, cache_next) { 52 | if (b->index == index) { 53 | *block = b; 54 | return OK; 55 | } 56 | } 57 | 58 | // ブロックキャッシュのメモリ領域を確保して、各セクタを読み込む。 59 | TRACE("block %d is not in cache, reading from disk", index); 60 | struct block *new_block = malloc(sizeof(struct block)); 61 | for (int offset = 0; offset < BLOCK_SIZE; offset += SECTOR_SIZE) { 62 | // デバイスドライバサーバに対して、セクタ読み込み要求を送る。 63 | struct message m; 64 | m.type = BLK_READ_MSG; 65 | m.blk_read.sector = block_to_sector(index) + (offset / SECTOR_SIZE); 66 | m.blk_read.len = SECTOR_SIZE; 67 | error_t err = ipc_call(blk_server, &m); 68 | 69 | if (err != OK) { 70 | OOPS("failed to read block %d: %s", index, err2str(err)); 71 | free(new_block); 72 | return err; 73 | } 74 | 75 | if (m.type != BLK_READ_REPLY_MSG) { 76 | OOPS("unexpected reply message type \"%s\" (expected=%s)", 77 | msgtype2str(m.type), msgtype2str(BLK_READ_REPLY_MSG)); 78 | free(new_block); 79 | return ERR_UNEXPECTED; 80 | } 81 | 82 | if (m.blk_read_reply.data_len != SECTOR_SIZE) { 83 | OOPS("invalid data length from the device: %d", 84 | m.blk_read_reply.data_len); 85 | free(new_block); 86 | return ERR_UNEXPECTED; 87 | } 88 | 89 | // ブロックキャッシュに読み込んだディスクデータをコピーする。 90 | memcpy(&new_block->data[offset], m.blk_read_reply.data, SECTOR_SIZE); 91 | } 92 | 93 | // ブロックキャッシュをリストに追加し、そのポインタを返す。 94 | new_block->index = index; 95 | list_elem_init(&new_block->cache_next); 96 | list_elem_init(&new_block->dirty_next); 97 | list_push_back(&cached_blocks, &new_block->cache_next); 98 | *block = new_block; 99 | return OK; 100 | } 101 | 102 | // ブロックを変更済みにする。 103 | void block_mark_as_dirty(struct block *block) { 104 | if (!block_is_dirty(block)) { 105 | list_push_back(&dirty_blocks, &block->dirty_next); 106 | } 107 | } 108 | 109 | // 変更済みブロックをすべてディスクに書き込む。 110 | void block_flush_all(void) { 111 | LIST_FOR_EACH (b, &dirty_blocks, struct block, dirty_next) { 112 | block_write(b); 113 | list_remove(&b->dirty_next); 114 | } 115 | } 116 | 117 | // ブロックキャッシュレイヤの初期化。 118 | void block_init(void) { 119 | // デバイスドライバサーバのタスクIDを取得する。 120 | blk_server = ipc_lookup("blk_device"); 121 | } 122 | -------------------------------------------------------------------------------- /messages.idl: -------------------------------------------------------------------------------- 1 | // HinaOSのメッセージ定義。カーネルや各サーバ・アプリケーションがメッセージパッシングで送りあう 2 | // メッセージ構造が定義されいている。 3 | // 4 | // 文法: 5 | // 6 | // oneway メッセージ名(引数名: 型, ...); 7 | // 8 | // 一方向 (oneway) のメッセージを定義する。 9 | // 10 | // rpc メッセージ名(引数名: 型, ...) -> (戻り値名: 型, ...); 11 | // 12 | // リモートプロシージャコール (rpc) 形式の通信に利用するメッセージを定義する。 13 | // 最初の括弧には引数を、「->」を挟んで次の括弧には戻り値を記述し、それぞれ別の 14 | // メッセージ定義が生成される。ただし、戻り値側のメッセージ関連の型には、名前に 15 | // 「_reply」という接尾辞が付加される。 16 | // 17 | // 利用可能な型: 18 | // 19 | // int: 符号付き整数 20 | // uint: 符号なし整数 21 | // size: 符号なし整数 (「大きさ」を表す値に使う) 22 | // task: タスクID 23 | // paddr: 物理アドレス 24 | // vaddr: 仮想アドレス 25 | // uaddr: ユーザ空間を指す仮想アドレス 26 | // cstr[N]: 最大Nバイトの文字列 (ヌル終端を含む) 27 | // bytes[N]: 最大Nバイトのバイト列 28 | // notifications: 通知メッセージのビットフィールド 29 | 30 | // 31 | // カーネル 32 | // 33 | 34 | // 例外メッセージ: タスクが正常終了した、無効な命令の実行を試みたなど 35 | oneway exception(task: task, reason: int); 36 | // ページフォルト 37 | rpc page_fault(task: task, uaddr: uaddr, ip: uaddr, fault: uint) -> (); 38 | // 通知メッセージ: libs/user内部でnotify_irqやnotify_timerメッセージに変換される 39 | oneway notify(notifications: notifications); 40 | 41 | // 42 | // libs/userライブラリ内部で使用されるメッセージ 43 | // 44 | 45 | // 割り込み通知メッセージ 46 | oneway notify_irq(); 47 | // タイムアウト通知メッセージ (timeシステムコールで設定した時間になった) 48 | oneway notify_timer(); 49 | // 非同期メッセージパッシング: 未受信のメッセージがある場合は、そのメッセージを返す 50 | rpc async_recv() -> (any); 51 | 52 | // 53 | // VMサーバ 54 | // 55 | 56 | // メッセージパッシングのテスト用。同じ値を返す。 57 | rpc ping(value: int) -> (value: int); 58 | // タスクの作成 59 | rpc spawn_task(name: cstr[32]) -> (task: task); 60 | // タスクの終了 61 | rpc destroy_task(task: task) -> (); 62 | // サービスディスカバリ: サービス名からタスクを検索 63 | rpc service_lookup(name: cstr[64]) -> (task: task); 64 | // サービスディスカバリ: タスク名の登録 65 | rpc service_register(name: cstr[64]) -> (); 66 | // タスクが終了した際にtask_destroyedメッセージを送信するように設定 67 | rpc watch_tasks() -> (); 68 | // タスクが終了した際に送られるメッセージ 69 | oneway task_destroyed(task: task); 70 | // 指定した物理メモリ領域をマップする。MMIO領域などをマップするために使用。 71 | rpc vm_map_physical(paddr: paddr, size: size, map_flags: int) -> (uaddr: uaddr); 72 | // 動的に物理メモリ領域を割り当てる。動的なメモリ領域を割り当てるために使用。 73 | rpc vm_alloc_physical(size: size, alloc_flags: int, map_flags: int) -> (uaddr: uaddr, paddr: paddr); 74 | 75 | // 76 | // ブロックデバイスドライバサーバ 77 | // 78 | 79 | // デバイスからの読み込み 80 | rpc blk_read(sector: uint, offset: size, len: size) -> (data: bytes[1024]); 81 | // デバイスへの書き込み 82 | rpc blk_write(sector: uint, offset: size, data: bytes[1024]) -> (); 83 | 84 | // 85 | // ネットワークデバイスドライバサーバ 86 | // 87 | 88 | // デバイスの初期化: デバイスドライバは受信パケットをこのメッセージを送信元に対して送り始める 89 | rpc net_open() -> (macaddr: uint8[6]); 90 | // 受信パケット: デバイスドライバは net_open RPCを呼び出したサーバに送信する 91 | oneway net_recv(payload: bytes[1500]); 92 | // 送信パケット 93 | rpc net_send(payload: bytes[1500]) -> (); 94 | 95 | // 96 | // ファイルシステムサーバ 97 | // 98 | 99 | // ファイルを開く 100 | rpc fs_open(path: cstr[256], flags: int) -> (fd: int); 101 | // ファイルを閉じる 102 | rpc fs_close(fd: int) -> (); 103 | // ファイルの読み込み 104 | rpc fs_read(fd: int, len: size) -> (data: bytes[1024]); 105 | // ファイルの書き込み 106 | rpc fs_write(fd: int, data: bytes[1024]) -> (written_len: size); 107 | // ディレクトリエントリの取得 108 | rpc fs_readdir(path: cstr[256], index: int) -> (name: cstr[256], type: int, filesize: size); 109 | // ファイルの作成 110 | rpc fs_mkfile(path: cstr[256]) -> (); 111 | // ディレクトリの作成 112 | rpc fs_mkdir(path: cstr[256]) -> (); 113 | // ファイル・ディレクトリの削除 114 | rpc fs_delete(path: cstr[256]) -> (); 115 | 116 | // 117 | // TCP/IPサーバ 118 | // 119 | 120 | // TCPソケットの作成・コネクションの確立 (アクティブオープン) 121 | rpc tcpip_connect(dst_addr: uint32, dst_port: uint16) -> (sock: int); 122 | // create socket and listen on specified port 123 | rpc tcpip_listen(listen_port: uint16) -> (sock: int); 124 | // destroy socket 125 | rpc tcpip_destroy(sock: int) -> (); 126 | // TCP: データの送信 127 | rpc tcpip_write(sock: int, data: bytes[1024]) -> (); 128 | // TCP: 受信済みデータの取得 129 | rpc tcpip_read(sock: int) -> (data: bytes[1024]); 130 | // DNS: ホスト名からIPv4アドレスを取得 131 | rpc tcpip_dns_resolve(hostname: cstr[256]) -> (addr: uint32); 132 | // TCP/IPサーバからメッセージ: データが受信された。tcpip_read RPCを呼び出すべき。 133 | oneway tcpip_data(sock: int); 134 | // TCP/IPサーバからメッセージ: ソケットがクローズされた 135 | oneway tcpip_closed(sock: int); 136 | 137 | // 138 | // Echo Server 139 | // 140 | 141 | rpc echo(data: bytes[1024]) -> (data: bytes[1024]); -------------------------------------------------------------------------------- /libs/user/malloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | extern char __heap[]; // ヒープ領域の先頭アドレス 7 | extern char __heap_end[]; // ヒープ領域の終端アドレス 8 | 9 | // 未使用チャンクリスト 10 | static list_t free_chunks = LIST_INIT(free_chunks); 11 | 12 | // ptrからlenバイトのメモリ領域を新しいチャンクとして登録する。 13 | static void insert(void *ptr, size_t len) { 14 | ASSERT(len > sizeof(struct malloc_chunk)); 15 | 16 | // チャンクの各フィールドを初期化 17 | struct malloc_chunk *new_chunk = ptr; 18 | new_chunk->magic = MALLOC_FREE; 19 | new_chunk->capacity = len - sizeof(struct malloc_chunk); 20 | new_chunk->size = 0; 21 | list_elem_init(&new_chunk->next); 22 | 23 | // フリーリストに追加 24 | list_push_back(&free_chunks, &new_chunk->next); 25 | } 26 | 27 | // チャンクの容量をcapバイトへ縮小する。分割後の余り部分は、新しいチャンクとして 28 | // フリーリストに追加される。 29 | static void split(struct malloc_chunk *chunk, size_t cap) { 30 | ASSERT(chunk->capacity >= cap + sizeof(struct malloc_chunk) + 8); 31 | 32 | // 新しいチャンクの全体サイズを計算 33 | size_t new_chunk_size = chunk->capacity - cap; 34 | 35 | // 新しいチャンクをchunkのデータ領域の末尾に作り、その分chunkを縮小する。 36 | void *new_chunk = &chunk->data[chunk->capacity - new_chunk_size]; 37 | chunk->capacity = cap; 38 | 39 | // 新しく作ったチャンクをフリーリストに登録する。 40 | insert(new_chunk, new_chunk_size); 41 | } 42 | 43 | // 動的メモリ割り当て。ヒープからメモリを割り当てる。C標準ライブラリと違い、メモリ割り当てに 44 | // 失敗したときはプログラムを終了する。 45 | void *malloc(size_t size) { 46 | // 要求サイズを8以上の8にアライメントされた数にする。 47 | // つまり、8、16、24、32、...という単位で割り当てる。 48 | size = ALIGN_UP((size == 0) ? 1 : size, 8); 49 | 50 | LIST_FOR_EACH (chunk, &free_chunks, struct malloc_chunk, next) { 51 | ASSERT(chunk->magic == MALLOC_FREE); 52 | 53 | if (chunk->capacity >= size) { 54 | // 分割可能なほど大きければ、次の2つのチャンクに分割する。 55 | // 余りチャンクはフリーリストに追加される。 56 | // 57 | // - 割り当てられるチャンク (割り当てサイズぴったり) 58 | // - 余りチャンク (最小割り当てサイズの8バイト以上) 59 | if (chunk->capacity >= size + sizeof(struct malloc_chunk) + 8) { 60 | split(chunk, size); 61 | } 62 | 63 | // 使用中であるマークをつけ、未使用チャンクリストから取り除いてから 64 | // アプリケーションにデータ部の先頭アドレスを返す。 65 | chunk->magic = MALLOC_IN_USE; 66 | chunk->size = size; 67 | list_remove(&chunk->next); 68 | 69 | // 割り当てられたメモリ領域をゼロクリアする。本来は呼び出し元が 70 | // きちんと初期化すべきだが、初期化し忘れバグのデバッグは大変なので 71 | // ここでやってしまう。 72 | memset(chunk->data, 0, chunk->size); 73 | return chunk->data; 74 | } 75 | } 76 | 77 | PANIC("out of memory"); 78 | } 79 | 80 | // ポインタからチャンクヘッダを取得する。malloc関数で割り当てたポインタでない場合はパニックする。 81 | static struct malloc_chunk *get_chunk_from_ptr(void *ptr) { 82 | struct malloc_chunk *chunk = 83 | (struct malloc_chunk *) ((uintptr_t) ptr - sizeof(struct malloc_chunk)); 84 | 85 | ASSERT(chunk->magic == MALLOC_IN_USE); 86 | return chunk; 87 | } 88 | 89 | // malloc関数で割り当てたメモリ領域を解放する。 90 | void free(void *ptr) { 91 | struct malloc_chunk *chunk = get_chunk_from_ptr(ptr); 92 | if (chunk->magic == MALLOC_FREE) { 93 | // 既に解放済みのメモリ領域を解放しようとした (double-freeバグ) 94 | PANIC("double-free bug!"); 95 | } 96 | 97 | // チャンクをフリーリストに戻す 98 | chunk->magic = MALLOC_FREE; 99 | list_push_back(&free_chunks, &chunk->next); 100 | } 101 | 102 | // メモリ再割り当て。malloc関数で割り当てたメモリ領域をsizeバイトに拡張した 103 | // 新しいメモリ領域を返す。 104 | void *realloc(void *ptr, size_t size) { 105 | if (!ptr) { 106 | return malloc(size); 107 | } 108 | 109 | struct malloc_chunk *chunk = get_chunk_from_ptr(ptr); 110 | size_t prev_size = chunk->size; 111 | if (size <= chunk->capacity) { 112 | // 今のチャンクに十分あまりがある場合は、そのまま返す。 113 | return ptr; 114 | } 115 | 116 | // 新しいメモリ領域を割り当てて、データをコピーする。 117 | void *new_ptr = malloc(size); 118 | memcpy(new_ptr, ptr, prev_size); 119 | free(ptr); 120 | return new_ptr; 121 | } 122 | 123 | // 文字列を新しく割り当てたメモリ領域にコピーし、その先頭アドレスを返す。 124 | // メモリリークを防ぐために、free関数で解放する必要がある。 125 | char *strdup(const char *s) { 126 | size_t len = strlen(s); 127 | char *new_s = malloc(len + 1); 128 | memcpy(new_s, s, len + 1); 129 | return new_s; 130 | } 131 | 132 | // 動的メモリ割り当ての初期化。ヒープ領域を未使用チャンクリストに追加する。 133 | void malloc_init(void) { 134 | // ヒープ領域 (__heap, __heap_end) はリンカースクリプトで定義される。 135 | insert(__heap, (size_t) __heap_end - (size_t) __heap); 136 | } 137 | --------------------------------------------------------------------------------