├── .gitignore ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── Makefile ├── README.md ├── byteos ├── byteos.yaml ├── config ├── cv1811h.toml ├── k210.toml ├── linker-general.ld ├── linker-k210.ld ├── linker-x86_64.ld └── qemu.toml ├── crates ├── devices │ ├── Cargo.toml │ └── src │ │ ├── device.rs │ │ ├── lib.rs │ │ └── utils.rs ├── executor │ ├── Cargo.toml │ └── src │ │ ├── executor.rs │ │ ├── lib.rs │ │ ├── ops.rs │ │ ├── task.rs │ │ └── thread.rs ├── libc-types │ ├── Cargo.toml │ └── src │ │ ├── arch │ │ ├── aarch64.rs │ │ ├── loongarch64.rs │ │ ├── mod.rs │ │ ├── riscv64.rs │ │ └── x86_64.rs │ │ ├── consts.rs │ │ ├── elf.rs │ │ ├── epoll.rs │ │ ├── fcntl.rs │ │ ├── futex.rs │ │ ├── internal.rs │ │ ├── ioctl.rs │ │ ├── lib.rs │ │ ├── mman.rs │ │ ├── others.rs │ │ ├── poll.rs │ │ ├── resource.rs │ │ ├── sched.rs │ │ ├── signal.rs │ │ ├── termios.rs │ │ ├── time.rs │ │ ├── times.rs │ │ ├── types.rs │ │ ├── utils │ │ └── mod.rs │ │ └── utsname.rs ├── runtime │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ ├── frame.rs │ │ ├── heap.rs │ │ └── lib.rs └── sync │ ├── Cargo.toml │ └── src │ └── lib.rs ├── driver ├── general-plic │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── plic.rs ├── kgoldfish-rtc │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── kramdisk │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ └── lib.rs ├── kvirtio │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── virtio_blk.rs │ │ ├── virtio_impl.rs │ │ ├── virtio_input.rs │ │ └── virtio_net.rs └── ns16550a │ ├── Cargo.toml │ └── src │ └── lib.rs ├── filesystem ├── devfs │ ├── Cargo.toml │ └── src │ │ ├── cpu_dma_latency.rs │ │ ├── lib.rs │ │ ├── null.rs │ │ ├── rtc.rs │ │ ├── sdx.rs │ │ ├── shm.rs │ │ ├── tty.rs │ │ ├── urandom.rs │ │ └── zero.rs ├── ext4fs │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── ext4rsfs │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── fs │ ├── .gitignore │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ ├── dentry.rs │ │ ├── fatfs_shim.rs │ │ ├── file.rs │ │ ├── lib.rs │ │ ├── pathbuf.rs │ │ └── pipe.rs ├── procfs │ ├── Cargo.toml │ └── src │ │ ├── interrupts.rs │ │ ├── lib.rs │ │ ├── meminfo.rs │ │ └── mounts.rs ├── ramfs │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── vfscore │ ├── Cargo.toml │ └── src │ └── lib.rs ├── kernel ├── Cargo.toml ├── build.rs ├── linker.lds.S └── src │ ├── banner.txt │ ├── consts.rs │ ├── logging.rs │ ├── main.rs │ ├── panic.rs │ ├── socket.rs │ ├── syscall │ ├── fd.rs │ ├── mm.rs │ ├── mod.rs │ ├── shm.rs │ ├── signal.rs │ ├── socket.rs │ ├── sys.rs │ ├── task.rs │ ├── time.rs │ └── types │ │ ├── mm.rs │ │ ├── mod.rs │ │ ├── poll.rs │ │ ├── signal.rs │ │ └── time.rs │ ├── tasks │ ├── async_ops.rs │ ├── elf.rs │ ├── exec.rs │ ├── filetable.rs │ ├── initproc.rs │ ├── memset.rs │ ├── mod.rs │ ├── shm.rs │ ├── stack.rs │ └── task.rs │ ├── user │ ├── entry.rs │ ├── mod.rs │ ├── signal.rs │ └── socket_pair.rs │ └── utils │ ├── mod.rs │ └── useref.rs ├── rust-toolchain.toml ├── scripts ├── cargo.ts ├── cli-build.ts ├── cli-qemu.ts ├── cli-types.ts ├── config.mk └── platform.ts └── tftp-burn.sh /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | mount.img 3 | mount/ 4 | sbi-qemu 5 | kernel-qemu 6 | byteos.bin 7 | .cargo/linker-riscv.ld 8 | packets.pcap 9 | .gdb_history 10 | kernel/*.lds 11 | qemu.log 12 | kernel/src/drivers.rs 13 | graph.png 14 | tools/iso/example 15 | *.log -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.allTargets": false, 3 | "rust-analyzer.check.extraArgs": [], 4 | "rust-analyzer.procMacro.enable": true, 5 | "rust-analyzer.cargo.target": "aarch64-unknown-none-softfloat", 6 | "rust-analyzer.cargo.extraEnv": { 7 | "RUSTFLAGS": "-Clink-arg=-no-pie --cfg=root_fs=\"ext4\"", 8 | "BOARD": "qemu", 9 | "ROOT_MANIFEST_DIR": "${workspaceFolder}" 10 | }, 11 | "rust-analyzer.check.targets": [ 12 | "riscv64gc-unknown-none-elf", 13 | "aarch64-unknown-none-softfloat", 14 | "x86_64-unknown-none", 15 | "loongarch64-unknown-none" 16 | ], 17 | "deno.enable": true 18 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [profile.release] 2 | # debug = true 3 | 4 | [workspace] 5 | resolver = "2" 6 | members = [ 7 | # 普通模块 8 | "crates/devices", 9 | "crates/executor", 10 | "crates/runtime", 11 | "crates/libc-types", 12 | "crates/sync", 13 | 14 | # 文件系统 15 | "filesystem/devfs", 16 | "filesystem/fs", 17 | "filesystem/procfs", 18 | "filesystem/ramfs", 19 | "filesystem/vfscore", 20 | "filesystem/ext4fs", 21 | "filesystem/ext4rsfs", 22 | 23 | # 驱动 24 | "driver/general-plic", 25 | "driver/kgoldfish-rtc", 26 | "driver/kramdisk", 27 | "driver/kvirtio", 28 | "driver/ns16550a", 29 | 30 | #内核 31 | "kernel", 32 | ] 33 | 34 | [workspace.dependencies] 35 | fs = { path = "filesystem/fs" } 36 | devfs = { path = "filesystem/devfs" } 37 | procfs = { path = "filesystem/procfs" } 38 | vfscore = { path = "filesystem/vfscore" } 39 | ramfs = { path = "filesystem/ramfs" } 40 | 41 | general-plic = { path = "driver/general-plic" } 42 | kgoldfish-rtc = { path = "driver/kgoldfish-rtc" } 43 | kramdisk = { path = "driver/kramdisk" } 44 | kvirtio = { path = "driver/kvirtio" } 45 | ns16550a = { path = "driver/ns16550a" } 46 | 47 | devices = { path = "crates/devices" } 48 | executor = { path = "crates/executor" } 49 | runtime = { path = "crates/runtime" } 50 | libc-types = { path = "crates/libc-types" } 51 | logging = { path = "crates/logging" } 52 | sync = { path = "crates/sync" } 53 | 54 | polyhal = { version = "0.4.0", features = ["logger", "trap"] } 55 | polyhal-boot = { version = "0.4.0" } 56 | polyhal-trap = { version = "0.4.0" } 57 | fdt-parser = { version = "0.4.12" } 58 | 59 | syscalls = { git = "https://github.com/jasonwhite/syscalls.git", default-features = false } 60 | 61 | [patch.crates-io] 62 | # polyhal = { path = "../polyhal/polyhal" } 63 | # polyhal-boot = { path = "../polyhal/polyhal-boot" } 64 | # polyhal-trap = { path = "../polyhal/polyhal-trap" } 65 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | include scripts/config.mk 3 | export BOARD := qemu 4 | export ROOT_MANIFEST_DIR := $(shell pwd) 5 | SMP := 1 6 | NVME := off 7 | NET := off 8 | RELEASE := release 9 | GDB ?= gdb-multiarch 10 | KERNEL_ELF = target/$(TARGET)/$(RELEASE)/kernel 11 | KERNEL_BIN = target/$(TARGET)/$(RELEASE)/kernel.bin 12 | 13 | BUS := device 14 | QEMU_EXEC := qemu-system-$(ARCH) 15 | ifeq ($(ARCH), x86_64) 16 | QEMU_EXEC += -machine q35 \ 17 | -cpu IvyBridge-v2 \ 18 | -kernel $(KERNEL_ELF) 19 | BUS := pci 20 | else ifeq ($(ARCH), riscv64) 21 | QEMU_EXEC += -machine virt \ 22 | -kernel $(KERNEL_BIN) 23 | else ifeq ($(ARCH), aarch64) 24 | QEMU_EXEC += -cpu cortex-a72 \ 25 | -machine virt \ 26 | -kernel $(KERNEL_BIN) 27 | else ifeq ($(ARCH), loongarch64) 28 | QEMU_EXEC += -kernel $(KERNEL_ELF) 29 | BUS := pci 30 | else 31 | $(error "ARCH"($(ARCH)) must be one of "x86_64", "riscv64", "aarch64" or "loongarch64") 32 | endif 33 | 34 | FS_IMG := mount.img 35 | features:= 36 | QEMU_EXEC += -m 1G\ 37 | -nographic \ 38 | -smp $(SMP) 39 | ifeq ($(QEMU_LOG), on) 40 | QEMU_EXEC += -D qemu.log -d in_asm,int,pcall,cpu_reset,guest_errors 41 | endif 42 | 43 | TESTCASE := testcase-$(ARCH) 44 | ifeq ($(NVME), on) 45 | QEMU_EXEC += -drive file=$(FS_IMG),if=none,id=nvm \ 46 | -device nvme,serial=deadbeef,drive=nvm 47 | else 48 | QEMU_EXEC += -drive file=$(FS_IMG),if=none,format=raw,id=x0 49 | QEMU_EXEC += -device virtio-blk-$(BUS),drive=x0 50 | endif 51 | 52 | ifeq ($(NET), on) 53 | QEMU_EXEC += -netdev user,id=net0,hostfwd=tcp::6379-:6379,hostfwd=tcp::2222-:2222,hostfwd=tcp::2000-:2000,hostfwd=tcp::8487-:8487,hostfwd=tcp::5188-:5188,hostfwd=tcp::12000-:12000 -object filter-dump,id=net0,netdev=net0,file=packets.pcap \ 54 | -device virtio-net-$(BUS),netdev=net0 55 | features += net 56 | endif 57 | 58 | all: build 59 | test: 60 | @echo $(TARGET) 61 | @echo $(PLATFORM) 62 | @echo $(ARCH) 63 | @echo $(ROOT_FS) 64 | @echo $(CONFIGS) 65 | 66 | offline: 67 | cargo build --features "$(features)" --offline 68 | rust-objcopy --binary-architecture=riscv64 $(KERNEL_ELF) --strip-all -O binary os.bin 69 | 70 | fs-img: 71 | @echo "TESTCASE: $(TESTCASE)" 72 | @echo "ROOT_FS: $(ROOT_FS)" 73 | rm -f $(FS_IMG) 74 | dd if=/dev/zero of=$(FS_IMG) bs=1M count=96 75 | sync 76 | ifeq ($(ROOT_FS), fat32) 77 | mkfs.vfat -F 32 $(FS_IMG) 78 | mkdir mount/ -p 79 | sudo mount $(FS_IMG) mount/ -o uid=1000,gid=1000 80 | sudo rm -rf mount/* 81 | else ifeq ($(ROOT_FS), ext4_rs) 82 | mkfs.ext4 -b 4096 $(FS_IMG) 83 | mkdir mount/ -p 84 | sudo mount $(FS_IMG) mount/ 85 | else 86 | mkfs.ext4 -b 4096 -F -O ^metadata_csum_seed $(FS_IMG) 87 | mkdir mount/ -p 88 | sudo mount $(FS_IMG) mount/ 89 | endif 90 | sudo cp -rf tools/$(TESTCASE)/* mount/ 91 | sync 92 | sudo umount $(FS_IMG) 93 | 94 | build: 95 | cargo build --target $(TARGET) --features "$(features)" --release 96 | rust-objcopy --binary-architecture=$(ARCH) $(KERNEL_ELF) --strip-all -O binary $(KERNEL_BIN) 97 | 98 | justbuild: fs-img build 99 | 100 | run: fs-img build 101 | time $(QEMU_EXEC) 102 | 103 | justrun: fs-img 104 | $(QEMU_EXEC) 105 | 106 | tftp-build: build 107 | sudo ./tftp-burn.sh 108 | 109 | debug: fs-img build 110 | @tmux new-session -d \ 111 | "$(QEMU_EXEC) -s -S && echo '按任意键继续' && read -n 1" && \ 112 | tmux split-window -h "$(GDB) $(KERNEL_ELF) -ex 'target remote localhost:1234' -ex 'disp /16i $pc' " && \ 113 | tmux -2 attach-session -d 114 | # $(QEMU_EXEC) -s -S & 115 | # sleep 1 116 | # $(GDB) $(KERNEL_ELF) \ 117 | # -ex 'target remote localhost:1234' \ 118 | # -ex 'disp /16i $pc' 119 | 120 | clean: 121 | rm -rf target/ 122 | 123 | gdb: 124 | riscv64-elf-gdb \ 125 | -ex 'file $(KERNEL_ELF)' \ 126 | -ex 'set arch riscv:rv64' \ 127 | -ex 'target remote localhost:1234' 128 | 129 | addr2line: 130 | addr2line -sfipe $(KERNEL_ELF) | rustfilt 131 | 132 | iso: build 133 | cp $(KERNEL_ELF) tools/iso/example 134 | grub-mkrescue -o bootable.iso tools/iso 135 | 136 | boot-iso: iso 137 | qemu-system-x86_64 -cdrom bootable.iso -serial stdio 138 | 139 | .PHONY: all run build clean gdb justbuild iso boot-iso 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/oscomp/byteos) 2 | # ByteOS 3 | 4 | ## How to use this project 5 | 6 | Run with make file. 7 | 8 | ```shell 9 | # Get resources 10 | git clone https://github.com/Byte-OS/tools.git 11 | # riscv64 12 | make PLATFORM=riscv64-qemu run 13 | # aarch64 14 | make PLATFORM=aarch64-qemu run 15 | # x86_64 16 | make PLATFORM=x86_64-qemu run 17 | # loongarch64 18 | make PLATFORM=loongarch64-qemu run 19 | ``` 20 | 21 | ## byteos.yaml 22 | 23 | > byteos.yaml is a configuration file for the ByteOS. 24 | 25 | You can change the config in this file. For example, you want to use the ext4 fielsystem. 26 | 27 | Set the root_fs to 'ext4' or 'ext4_rs' will change the root_fs from 'fat32' to 'ext4'. 28 | 29 | The 'ext4' and 'ext4_rs' are the different implementation of the ext4. 30 | 31 | TIPS: Make ensure that the mkefs version of your system lower than 1.70. If not, you have to use another argument to build the ext4 image. 32 | 33 | ## Kernel struct Design 34 | 35 | > ByteOS is a posix-compatible kernel. 36 | > 37 | > If you are interested in this project, please contact me. 38 | > 39 | > email: <321353225@qq.com> qq: 321353225 40 | 41 | ```plain 42 | crates --> arch --> modules --> kernel 43 | ``` 44 | 45 | ## TODO List 46 | 47 | - [x] higher half kernel 48 | - [x] Modular skeleton 49 | - [x] global allocator 50 | - [x] RTC device support 51 | - [x] Timestamp --> actual Date/Time [timestamp crate](crates/timestamp/) 52 | - [x] frame allocator, use bit_field to store page usage 53 | - [x] Interrupt support 54 | - [x] backtrace support 55 | - [x] timer interrupt support 56 | - [x] page mapping support 57 | - [x] get devices info and memory info from device_tree 58 | - [x] VIRTIO blk device support 59 | - [x] Add a banner for os. use tool [banner generation tool](http://patorjk.com/software/taag/#p=display&f=Big&t=ByteOS) 60 | - [x] vfs support 61 | - [x] fatfs support 62 | - [x] fs mount support (a temporary solution) 63 | - [x] ramfs support 64 | - [x] devfs support 65 | - [x] async/await support (simple version) 66 | - [x] process support 67 | - [x] VIRTIO net device support 68 | - [ ] smp support 69 | - [ ] desktop support. eg: dwm, hyprland. 70 | 71 | ## Program support 72 | 73 | tools/final2023: 74 | 75 | - libctest 76 | - libcbench 77 | - busybox 78 | - lua 79 | - lmbench 80 | - iozone 81 | - iperf3 82 | - nerperf 83 | - cyclic 84 | - unixbench 85 | 86 | tools/gcc 87 | 88 | - gcc 89 | - redis-server 90 | - ssh-simple 91 | - http-server 92 | 93 | You can change the `TESTCASE` in the makefile to change the target. You can run other program in the sh or change the default program in the `kernel/src/tasks/initproc.rs` file. 94 | 95 | ## run busybox sh on qemu platform 96 | 97 | ```bash 98 | make run BOARD=qemu LOG=info NET=off 99 | ``` 100 | 101 | Changing 'LOG=info' to 'LOG=error' if you don't need any info output. 102 | -------------------------------------------------------------------------------- /byteos: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S deno --ext=ts --allow-run --allow-read --allow-env 2 | 3 | import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.4/command/mod.ts"; 4 | import { cliCommand as buildCommand } from "./scripts/cli-build.ts"; 5 | import { cliCommand as qemuCommand } from "./scripts/cli-qemu.ts"; 6 | import { logLevelEnum } from "./scripts/cli-types.ts"; 7 | import { parse } from "jsr:@std/yaml"; 8 | import { initPlatform } from "./scripts/platform.ts"; 9 | 10 | const command = new Command() 11 | .name("byteos") 12 | .version("0.1.0") 13 | .description("Building tools for the byteos.") 14 | .globalType("log-level", logLevelEnum) 15 | .globalOption("-l, --log-level ", "Set Log Level", { 16 | default: "info", 17 | }) 18 | .globalOption("-p, --platform ", "Set the platform", { 19 | required: true, 20 | }) 21 | .globalAction(({ platform }) => initPlatform(platform)) 22 | // Sub Command build 23 | .command("build", buildCommand) 24 | .command("qemu", qemuCommand); 25 | 26 | // parse yaml file 27 | const data = parse( 28 | new TextDecoder("utf-8").decode(await Deno.readFile("byteos.yaml")), 29 | ); 30 | console.log(data); 31 | 32 | try { 33 | // Parse the command. 34 | await command.parse(Deno.args); 35 | } catch (e) { 36 | console.error("Error", e); 37 | } 38 | -------------------------------------------------------------------------------- /byteos.yaml: -------------------------------------------------------------------------------- 1 | global: 2 | configs: 3 | board: "qemu" 4 | # Available are fat32, ext4 and ext4_rs. 5 | # root_fs: "fat32" 6 | root_fs: "ext4" 7 | env: 8 | HEAP_SIZE: "0x0180_0000" 9 | MOUNT_IMG_PATH: "mount.img" 10 | 11 | bin: 12 | riscv64-qemu: 13 | target: "riscv64gc-unknown-none-elf" 14 | configs: 15 | driver: "kvirtio" 16 | riscv64-vf2: 17 | target: "riscv64imac-unknown-none-elf" 18 | configs: 19 | board: "visionfive2" 20 | driver: "kramdisk" 21 | x86_64-qemu: 22 | target: "x86_64-unknown-none" 23 | configs: 24 | driver: "kvirtio" 25 | x86_64-generic: 26 | target: "x86_64-unknown-none" 27 | configs: 28 | driver: "kramdisk" 29 | aarch64-qemu: 30 | target: "aarch64-unknown-none-softfloat" 31 | configs: 32 | driver: "kramdisk" 33 | loongarch64-qemu: 34 | target: "loongarch64-unknown-none" 35 | configs: 36 | driver: "kvirtio" 37 | loongarch64-2k1000: 38 | target: "loongarch64-unknown-none" 39 | configs: 40 | driver: "kramdisk" 41 | board: "2k1000" 42 | -------------------------------------------------------------------------------- /config/cv1811h.toml: -------------------------------------------------------------------------------- 1 | # TIPS: This is a config file for cv1811h platform. 2 | 3 | # driver list 4 | drivers = [ 5 | "general-plic", 6 | "ns16550a", 7 | "kcvitek-sd", 8 | ] 9 | 10 | cfgs = [ 11 | "c906" 12 | ] 13 | 14 | features = [] 15 | -------------------------------------------------------------------------------- /config/k210.toml: -------------------------------------------------------------------------------- 1 | # TIPS: This is a config file for qemu-system-riscv64 platform. 2 | 3 | # driver list 4 | drivers = [ 5 | "kvirtio", 6 | "k210-sdcard", 7 | "general-plic", 8 | "ns16550a", 9 | ] 10 | 11 | ld_file = "linker-k210.ld" 12 | -------------------------------------------------------------------------------- /config/linker-general.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(riscv) 2 | ENTRY(_start) 3 | 4 | BASE_ADDRESS = 0xffffffc080200000; 5 | __ENTRY_ADDR = 0x80200000; 6 | 7 | SECTIONS 8 | { 9 | /* Load the kernel at this address: "." means the current address */ 10 | . = BASE_ADDRESS; 11 | start = .; 12 | 13 | .text ALIGN(4K): AT(__ENTRY_ADDR) { 14 | stext = .; 15 | *(.text.entry) 16 | *(.text .text.*) 17 | etext = .; 18 | } 19 | 20 | .sigtrx ALIGN(4K): { 21 | *(.sigtrx .sigtrx.*) 22 | } 23 | 24 | .rodata ALIGN(4K): { 25 | srodata = .; 26 | *(.rodata .rodata.*) 27 | . = ALIGN(4K); 28 | erodata = .; 29 | } 30 | 31 | .data ALIGN(4K): { 32 | . = ALIGN(4K); 33 | *(.data.prepage .data.prepage.*) 34 | . = ALIGN(4K); 35 | _sdata = .; 36 | *(.data .data.*) 37 | *(.sdata .sdata.*) 38 | _edata = .; 39 | } 40 | 41 | .bss ALIGN(4K): { 42 | *(.bss.stack) 43 | _sbss = .; 44 | *(.bss .bss.*) 45 | *(.sbss .sbss.*) 46 | _ebss = .; 47 | } 48 | 49 | PROVIDE(end = .); 50 | } -------------------------------------------------------------------------------- /config/linker-k210.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(riscv) 2 | ENTRY(_start) 3 | 4 | BASE_ADDRESS = 0xffffffc080020000; 5 | 6 | __ENTRY_ADDR = 0x80020000; 7 | 8 | SECTIONS 9 | { 10 | /* Load the kernel at this address: "." means the current address */ 11 | . = BASE_ADDRESS; 12 | start = .; 13 | 14 | .text ALIGN(4K): AT(__ENTRY_ADDR) { 15 | stext = .; 16 | *(.text.entry) 17 | *(.text .text.*) 18 | . = ALIGN(4K); 19 | etext = .; 20 | } 21 | 22 | .rodata ALIGN(4K): { 23 | srodata = .; 24 | *(.rodata .rodata.*) 25 | . = ALIGN(4K); 26 | erodata = .; 27 | } 28 | 29 | .data ALIGN(4K): { 30 | . = ALIGN(4K); 31 | *(.data.prepage) 32 | . = ALIGN(4K); 33 | sdata = .; 34 | *(.data .data.*) 35 | *(.sdata .sdata.*) 36 | edata = .; 37 | } 38 | 39 | .stack : { 40 | *(.bss.stack) 41 | } 42 | 43 | .bss : { 44 | *(.bss.stack) 45 | sbss = .; 46 | *(.bss .bss.*) 47 | *(.sbss .sbss.*) 48 | ebss = .; 49 | } 50 | 51 | PROVIDE(end = .); 52 | } -------------------------------------------------------------------------------- /config/linker-x86_64.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(x86_64) 2 | ENTRY(_start) 3 | 4 | /* BASE_ADDRESS = 0xffffffc000020000; */ 5 | BASE_ADDRESS = 0xffffff8000200000; 6 | 7 | __ENTRY_ADDR = 0x200000; 8 | 9 | SECTIONS 10 | { 11 | /* Load the kernel at this address: "." means the current address */ 12 | . = BASE_ADDRESS; 13 | start = .; 14 | _skernel = .; 15 | 16 | .text ALIGN(4K): AT(__ENTRY_ADDR) { 17 | stext = .; 18 | *(.text.boot) 19 | *(.text.entry) 20 | *(.text .text.*) 21 | etext = .; 22 | } 23 | 24 | .sigtrx ALIGN(4K): { 25 | *(.sigtrx .sigtrx.*) 26 | } 27 | 28 | .rodata ALIGN(4K): { 29 | srodata = .; 30 | *(.rodata .rodata.*) 31 | . = ALIGN(4K); 32 | erodata = .; 33 | } 34 | 35 | .data ALIGN(4K): { 36 | . = ALIGN(4K); 37 | *(.data.prepage .data.prepage.*) 38 | . = ALIGN(4K); 39 | _sdata = .; 40 | *(.data .data.*) 41 | *(.sdata .sdata.*) 42 | _edata = .; 43 | } 44 | 45 | .bss ALIGN(4K): { 46 | *(.bss.stack) 47 | _sbss = .; 48 | *(.bss .bss.*) 49 | *(.sbss .sbss.*) 50 | _ebss = .; 51 | } 52 | 53 | PROVIDE(end = .); 54 | } -------------------------------------------------------------------------------- /config/qemu.toml: -------------------------------------------------------------------------------- 1 | # TIPS: This is a config file for qemu-system-riscv64 platform. 2 | 3 | # driver list 4 | drivers = [ 5 | "kvirtio", 6 | "kgoldfish-rtc", 7 | "general-plic", 8 | "ns16550a", 9 | # "knvme", 10 | ] 11 | 12 | features = [ 13 | "nvme" 14 | ] 15 | -------------------------------------------------------------------------------- /crates/devices/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "devices" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [features] 9 | 10 | [dependencies] 11 | polyhal = { workspace = true } 12 | fdt-parser = { workspace = true } 13 | linkme = { version = "0.3.22", features = ["used_linker"] } 14 | log = "0.4" 15 | sync = { workspace = true } 16 | timestamp = { git = "https://github.com/Byte-OS/timestamp.git" } 17 | runtime = { workspace = true } 18 | -------------------------------------------------------------------------------- /crates/devices/src/device.rs: -------------------------------------------------------------------------------- 1 | use alloc::{sync::Arc, vec::Vec}; 2 | 3 | use crate::{INT_DEVICE, MAIN_UART}; 4 | 5 | pub enum DeviceType { 6 | RTC(Arc), 7 | BLOCK(Arc), 8 | NET(Arc), 9 | INPUT(Arc), 10 | INT(Arc), 11 | UART(Arc), 12 | None, 13 | } 14 | 15 | pub struct DeviceSet { 16 | pub rtc: Vec>, 17 | pub blk: Vec>, 18 | pub net: Vec>, 19 | pub uart: Vec>, 20 | pub input: Vec>, 21 | } 22 | 23 | impl DeviceSet { 24 | pub const fn new() -> Self { 25 | Self { 26 | rtc: vec![], 27 | blk: vec![], 28 | net: vec![], 29 | uart: vec![], 30 | input: vec![], 31 | } 32 | } 33 | 34 | pub fn add_device(&mut self, device: Arc) { 35 | match device.get_device_wrapper() { 36 | DeviceType::RTC(device) => self.rtc.push(device), 37 | DeviceType::BLOCK(device) => self.blk.push(device), 38 | DeviceType::NET(device) => self.net.push(device), 39 | DeviceType::INPUT(device) => self.input.push(device), 40 | DeviceType::INT(device) => INT_DEVICE.init_by(device), 41 | DeviceType::UART(device) => { 42 | if self.uart.is_empty() { 43 | MAIN_UART.init_by(device.clone()); 44 | } 45 | self.uart.push(device) 46 | } 47 | DeviceType::None => {} 48 | } 49 | } 50 | } 51 | 52 | impl Default for DeviceSet { 53 | fn default() -> Self { 54 | Self::new() 55 | } 56 | } 57 | 58 | pub trait Driver: Send + Sync { 59 | fn get_id(&self) -> &str; 60 | 61 | fn interrupts(&self) -> &[u32] { 62 | &[] 63 | } 64 | 65 | fn try_handle_interrupt(&self, _irq: u32) -> bool { 66 | false 67 | } 68 | 69 | fn get_device_wrapper(self: Arc) -> DeviceType; 70 | } 71 | 72 | pub trait RtcDriver: Driver { 73 | fn read_timestamp(&self) -> u64; 74 | fn read(&self) -> u64; 75 | } 76 | 77 | pub trait BlkDriver: Driver { 78 | fn read_blocks(&self, block_id: usize, buf: &mut [u8]); 79 | fn write_blocks(&self, block_id: usize, buf: &[u8]); 80 | fn capacity(&self) -> usize { 81 | 0 82 | } 83 | } 84 | 85 | #[derive(Debug)] 86 | pub enum NetError { 87 | NoData, 88 | } 89 | 90 | pub trait NetDriver: Driver { 91 | fn recv(&self, buf: &mut [u8]) -> Result; 92 | fn send(&self, buf: &[u8]) -> Result<(), NetError>; 93 | } 94 | 95 | pub trait IntDriver: Driver { 96 | fn register_irq(&self, irq: u32, driver: Arc); 97 | } 98 | 99 | pub trait InputDriver: Driver { 100 | fn read_event(&self) -> u64; 101 | fn handle_irq(&self); 102 | fn is_empty(&self) -> bool; 103 | } 104 | 105 | pub trait UartDriver: Driver { 106 | fn put(&self, c: u8); 107 | fn get(&self) -> Option; 108 | } 109 | 110 | pub struct UnsupportedDriver; 111 | 112 | impl Driver for UnsupportedDriver { 113 | fn get_id(&self) -> &str { 114 | "unsupported-driver" 115 | } 116 | 117 | fn get_device_wrapper(self: Arc) -> DeviceType { 118 | DeviceType::None 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /crates/devices/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(used_with_arg)] 3 | #![feature(decl_macro)] 4 | #![feature(iter_intersperse)] 5 | #![feature(fn_ptr_trait)] 6 | 7 | #[macro_use] 8 | extern crate log; 9 | #[macro_use] 10 | extern crate alloc; 11 | 12 | pub mod device; 13 | pub mod utils; 14 | 15 | pub use fdt_parser as fdt; 16 | 17 | use alloc::{collections::BTreeMap, sync::Arc, vec::Vec}; 18 | use device::{BlkDriver, DeviceSet, Driver, IntDriver, NetDriver, UartDriver}; 19 | use fdt_parser::Node; 20 | pub use linkme::{self, distributed_slice as linker_use}; 21 | pub use polyhal::{consts::VIRT_ADDR_START, pagetable::PAGE_SIZE}; 22 | pub use runtime::frame::{frame_alloc, frame_alloc_much, FrameTracker}; 23 | pub use sync::{LazyInit, Mutex, MutexGuard}; 24 | 25 | pub static DEVICE_TREE: LazyInit> = LazyInit::new(); 26 | pub static DRIVER_REGS: Mutex Arc>> = 27 | Mutex::new(BTreeMap::new()); 28 | pub static IRQ_MANAGER: Mutex>> = Mutex::new(BTreeMap::new()); 29 | pub static INT_DEVICE: LazyInit> = LazyInit::new(); 30 | pub static MAIN_UART: LazyInit> = LazyInit::new(); 31 | pub static ALL_DEVICES: Mutex = Mutex::new(DeviceSet::new()); 32 | 33 | #[linkme::distributed_slice] 34 | pub static DRIVERS_INIT: [fn() -> Option>] = [..]; 35 | 36 | #[inline] 37 | pub fn get_blk_device(id: usize) -> Option> { 38 | let all_device = ALL_DEVICES.lock(); 39 | let len = all_device.blk.len(); 40 | match id < len { 41 | true => Some(all_device.blk[id].clone()), 42 | false => None, 43 | } 44 | } 45 | 46 | #[inline] 47 | pub fn get_blk_devices() -> Vec> { 48 | ALL_DEVICES.lock().blk.clone() 49 | } 50 | 51 | #[inline] 52 | pub fn get_int_device() -> Arc { 53 | INT_DEVICE.try_get().expect("can't find int device").clone() 54 | } 55 | 56 | #[inline] 57 | pub fn get_main_uart() -> Option> { 58 | MAIN_UART.try_get().cloned() 59 | } 60 | 61 | #[inline] 62 | pub fn get_net_device(id: usize) -> Arc { 63 | ALL_DEVICES 64 | .lock() 65 | .net 66 | .get(id) 67 | .expect("can't find net device") 68 | .clone() 69 | } 70 | 71 | /// prepare_drivers 72 | /// This function will init drivers 73 | #[inline] 74 | pub fn prepare_drivers() { 75 | DRIVERS_INIT.iter().for_each(|f| { 76 | if let Some(device) = f() { 77 | log::debug!("init driver: {}", device.get_id()); 78 | ALL_DEVICES.lock().add_device(device); 79 | } 80 | }); 81 | } 82 | 83 | pub fn try_to_add_device(node: &Node) { 84 | let driver_manager = DRIVER_REGS.lock(); 85 | if let Some(mut compatible) = node.compatible() { 86 | info!( 87 | " {} {:?}", 88 | node.name, 89 | compatible.next() // compatible.intersperse(" ").collect::() 90 | ); 91 | for compati in node.compatibles() { 92 | if let Some(f) = driver_manager.get(compati) { 93 | ALL_DEVICES.lock().add_device(f(node)); 94 | break; 95 | } 96 | } 97 | } 98 | } 99 | 100 | pub fn regist_devices_irq() { 101 | // register the drivers in the IRQ MANAGER. 102 | if let Some(plic) = INT_DEVICE.try_get() { 103 | for (irq, driver) in IRQ_MANAGER.lock().iter() { 104 | plic.register_irq(*irq, driver.clone()); 105 | } 106 | } 107 | } 108 | 109 | // register the irqs 110 | pub fn register_device_irqs(driver: Arc) { 111 | let mut irq_manager = IRQ_MANAGER.lock(); 112 | driver.interrupts().iter().for_each(|irq| { 113 | irq_manager.insert(*irq, driver.clone()); 114 | }); 115 | } 116 | 117 | pub fn node_to_interrupts(node: &Node) -> Vec { 118 | node.interrupts() 119 | .map(|x| x.flatten().collect()) 120 | .unwrap_or_default() 121 | } 122 | 123 | #[macro_export] 124 | macro_rules! driver_define { 125 | ($body: block) => { 126 | #[devices::linker_use($crate::DRIVERS_INIT)] 127 | #[linkme(crate = devices::linkme)] 128 | fn __driver_init() -> Option> { 129 | $body 130 | } 131 | }; 132 | ($obj:expr, $func: expr) => { 133 | #[devices::linker_use($crate::DRIVERS_INIT)] 134 | #[linkme(crate = devices::linkme)] 135 | fn __driver_init() -> Option> { 136 | $crate::DRIVER_REGS.lock().insert($obj, $func); 137 | None 138 | } 139 | }; 140 | } 141 | -------------------------------------------------------------------------------- /crates/devices/src/utils.rs: -------------------------------------------------------------------------------- 1 | use polyhal::{debug_console::DebugConsole, PageTable}; 2 | 3 | use crate::MAIN_UART; 4 | 5 | /// Translate virtual address into physical address in the current virtual address space 6 | /// 7 | #[inline] 8 | pub fn virt_to_phys(vaddr: usize) -> Option { 9 | PageTable::current() 10 | .translate(vaddr.into()) 11 | .map(|x| x.0.raw()) 12 | } 13 | 14 | pub fn puts(buffer: &[u8]) { 15 | // Use the main uart as much as possible. 16 | let main_uart_inited = MAIN_UART.is_init(); 17 | for i in buffer { 18 | match main_uart_inited { 19 | true => MAIN_UART.put(*i), 20 | false => DebugConsole::putchar(*i), 21 | } 22 | } 23 | } 24 | 25 | /// Get a character from the uart. 26 | /// 27 | /// If the uart device was initialized, then use it. 28 | pub fn get_char() -> Option { 29 | match MAIN_UART.try_get() { 30 | Some(uart) => uart.get(), 31 | None => DebugConsole::getchar(), 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /crates/executor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "executor" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | polyhal = { workspace = true } 10 | sync = { workspace = true } 11 | log = "0.4" 12 | downcast-rs = { version = "2.0.1", default-features = false, features = [ 13 | "sync", 14 | ] } 15 | hashbrown = "0.14" 16 | -------------------------------------------------------------------------------- /crates/executor/src/executor.rs: -------------------------------------------------------------------------------- 1 | use alloc::{ 2 | collections::VecDeque, 3 | sync::{Arc, Weak}, 4 | task::Wake, 5 | vec::Vec, 6 | }; 7 | use core::{ 8 | hint::spin_loop, 9 | sync::atomic::{AtomicBool, Ordering}, 10 | task::{Context, Poll}, 11 | }; 12 | use hashbrown::HashMap; 13 | use log::info; 14 | use polyhal::{hart_id, PageTable}; 15 | use sync::{LazyInit, Mutex}; 16 | 17 | use crate::task::{AsyncTask, AsyncTaskItem, PinedFuture}; 18 | 19 | pub type TaskId = usize; 20 | 21 | pub static TASK_MAP: LazyInit>>> = LazyInit::new(); 22 | /// FIFO task queue, Items will be pushed to the end of the queue after being called. 23 | pub(crate) static TASK_QUEUE: Mutex> = Mutex::new(VecDeque::new()); 24 | 25 | pub static DEFAULT_EXECUTOR: Executor = Executor::new(); 26 | 27 | static BOOT_PAGE: LazyInit = LazyInit::new(); 28 | 29 | pub struct Executor { 30 | cores: LazyInit>>>>, 31 | inited: AtomicBool, 32 | } 33 | 34 | impl Executor { 35 | pub const fn new() -> Self { 36 | Executor { 37 | cores: LazyInit::new(), 38 | inited: AtomicBool::new(false), 39 | } 40 | } 41 | 42 | pub fn init(&self, cores: usize) { 43 | let mut core_container = Vec::with_capacity(cores); 44 | (0..cores).for_each(|_| core_container.push(Mutex::new(None))); 45 | self.cores.init_by(core_container); 46 | 47 | // Init TaskMAP with new empty hash map 48 | TASK_MAP.init_by(Mutex::new(HashMap::new())); 49 | if !BOOT_PAGE.is_init() { 50 | BOOT_PAGE.init_by(PageTable::current()); 51 | } 52 | 53 | // Finish initializing 54 | self.inited.store(true, Ordering::SeqCst); 55 | } 56 | 57 | pub fn spawn(&mut self, task: Arc, future: PinedFuture) { 58 | TASK_QUEUE.lock().push_back(AsyncTaskItem { future, task }) 59 | } 60 | 61 | pub fn run(&self) { 62 | info!("fetch atomic data: {}", self.inited.load(Ordering::SeqCst)); 63 | info!( 64 | "fetch atomic data not: {}", 65 | self.inited.load(Ordering::SeqCst) 66 | ); 67 | // Waiting for executor's initialisation finish. 68 | while !self.inited.load(Ordering::SeqCst) { 69 | spin_loop(); 70 | } 71 | loop { 72 | self.run_ready_task(); 73 | self.hlt_if_idle(); 74 | } 75 | } 76 | 77 | fn run_ready_task(&self) { 78 | let task = TASK_QUEUE.lock().pop_front(); 79 | if let Some(task_item) = task { 80 | let AsyncTaskItem { task, mut future } = task_item; 81 | task.before_run(); 82 | *self.cores[hart_id()].lock() = Some(task.clone()); 83 | // Create Waker 84 | let waker = Arc::new(Waker { 85 | task_id: task.get_task_id(), 86 | }) 87 | .into(); 88 | let mut context = Context::from_waker(&waker); 89 | 90 | match future.as_mut().poll(&mut context) { 91 | Poll::Ready(()) => {} // task done 92 | Poll::Pending => TASK_QUEUE.lock().push_back(AsyncTaskItem { future, task }), 93 | } 94 | } 95 | } 96 | 97 | /// Executes the `hlt` instruction if there are no ready tasks 98 | fn hlt_if_idle(&self) { 99 | // arch::wfi(); 100 | } 101 | } 102 | 103 | impl Default for Executor { 104 | fn default() -> Self { 105 | Self::new() 106 | } 107 | } 108 | 109 | #[allow(dead_code)] 110 | pub struct Waker { 111 | task_id: TaskId, 112 | } 113 | 114 | impl Wake for Waker { 115 | fn wake(self: Arc) { 116 | self.wake_by_ref(); 117 | } 118 | 119 | fn wake_by_ref(self: &Arc) {} 120 | } 121 | 122 | /// Alloc a task id. 123 | pub fn task_id_alloc() -> TaskId { 124 | static TASK_ID: Mutex = Mutex::new(0); 125 | let mut task_id = TASK_ID.lock(); 126 | *task_id += 1; 127 | *task_id 128 | } 129 | 130 | /// Get task through task id. 131 | pub fn tid2task(tid: usize) -> Option> { 132 | TASK_MAP.lock().get(&tid).cloned().map(|x| x.upgrade())? 133 | } 134 | 135 | /// Release task 136 | pub fn release_task(tid: usize) { 137 | // Remove task from TASK_MAP 138 | TASK_MAP.lock().remove(&tid); 139 | } 140 | 141 | #[inline] 142 | pub fn current_task() -> Arc { 143 | // CURRENT_TASK.lock().as_ref().map(|x| x.clone()).unwrap() 144 | DEFAULT_EXECUTOR.cores[hart_id()] 145 | .lock() 146 | .as_ref() 147 | .map(|x| x.clone()) 148 | .unwrap() 149 | } 150 | 151 | pub fn boot_page_table() -> PageTable { 152 | *BOOT_PAGE 153 | } 154 | -------------------------------------------------------------------------------- /crates/executor/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | extern crate alloc; 4 | 5 | mod executor; 6 | mod ops; 7 | pub mod task; 8 | pub mod thread; 9 | 10 | use core::task::Poll; 11 | use core::{future::Future, pin::Pin, task::Context}; 12 | 13 | use alloc::boxed::Box; 14 | pub use executor::*; 15 | pub use ops::*; 16 | pub use task::AsyncTask; 17 | 18 | pub struct Select { 19 | inner: Option<(A, B)>, 20 | } 21 | 22 | impl Unpin for Select {} 23 | 24 | pub fn select(future1: A, future2: B) -> Select 25 | where 26 | A: Future + Unpin, 27 | B: Future + Unpin, 28 | { 29 | Select { 30 | inner: Some((future1, future2)), 31 | } 32 | } 33 | 34 | fn poll_unpin(future: A, cx: &mut Context<'_>) -> Poll { 35 | Box::pin(future).as_mut().poll(cx) 36 | } 37 | 38 | pub enum Either { 39 | /// First branch of the type 40 | Left(/* #[pin] */ A), 41 | /// Second branch of the type 42 | Right(/* #[pin] */ B), 43 | } 44 | 45 | impl Future for Select 46 | where 47 | A: Future + Unpin, 48 | B: Future + Unpin, 49 | { 50 | type Output = Either<(A::Output, B), (B::Output, A)>; 51 | 52 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 53 | /// When compiled with `-C opt-level=z`, this function will help the compiler eliminate the `None` branch, where 54 | /// `Option::unwrap` does not. 55 | #[inline(always)] 56 | fn unwrap_option(value: Option) -> T { 57 | match value { 58 | None => unreachable!(), 59 | Some(value) => value, 60 | } 61 | } 62 | 63 | let (a, b) = self.inner.as_mut().expect("cannot poll Select twice"); 64 | 65 | if let Poll::Ready(val) = poll_unpin(a, cx) { 66 | return Poll::Ready(Either::Left((val, unwrap_option(self.inner.take()).1))); 67 | } 68 | 69 | if let Poll::Ready(val) = poll_unpin(b, cx) { 70 | return Poll::Ready(Either::Right((val, unwrap_option(self.inner.take()).0))); 71 | } 72 | 73 | Poll::Pending 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /crates/executor/src/ops.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | future::Future, 3 | pin::Pin, 4 | task::{Context, Poll}, 5 | }; 6 | 7 | pub struct Yield(bool); 8 | 9 | impl Yield { 10 | pub const fn new() -> Self { 11 | Self(false) 12 | } 13 | } 14 | 15 | impl Default for Yield { 16 | fn default() -> Self { 17 | Self::new() 18 | } 19 | } 20 | 21 | impl Future for Yield { 22 | type Output = (); 23 | 24 | fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { 25 | match self.0 { 26 | true => Poll::Ready(()), 27 | false => { 28 | self.0 = true; 29 | Poll::Pending 30 | } 31 | } 32 | } 33 | } 34 | 35 | pub async fn yield_now() { 36 | Yield::new().await; 37 | } 38 | -------------------------------------------------------------------------------- /crates/executor/src/task.rs: -------------------------------------------------------------------------------- 1 | use core::{future::Future, pin::Pin}; 2 | 3 | use alloc::{boxed::Box, sync::Arc}; 4 | use downcast_rs::{impl_downcast, DowncastSync}; 5 | 6 | use crate::{boot_page_table, TaskId}; 7 | 8 | /// Default is kernel task 9 | pub const TYPE_KERNEL_TASK: u8 = 0; 10 | 11 | /// This is a trait the for generic task. 12 | pub trait AsyncTask: DowncastSync { 13 | /// Get the id of the task 14 | fn get_task_id(&self) -> TaskId; 15 | /// Run befire the kernel 16 | fn before_run(&self); 17 | /// Get task type. 18 | fn get_task_type(&self) -> TaskType; 19 | /// Exit a task with exit code. 20 | fn exit(&self, exit_code: usize); 21 | /// Check if the task was exited successfully 22 | fn exit_code(&self) -> Option; 23 | } 24 | 25 | /// This is a enum that indicates the task type. 26 | #[derive(Debug, PartialEq, PartialOrd)] 27 | pub enum TaskType { 28 | /// Blank Kernel Task Type, Just run in the kernel, 29 | /// No extra pagetable 30 | BlankKernel, 31 | /// Monolithic Task Type, Will have a independent pagetable. 32 | MonolithicTask, 33 | /// Microkernel task 34 | MicroTask, 35 | /// Unikernel task 36 | UnikernelTask, 37 | /// RTOS task 38 | RTOSTask, 39 | /// User defined task 1 40 | UserDefinedTask1, 41 | /// User defined task 2 42 | UserDefinedTask2, 43 | } 44 | 45 | pub type PinedFuture = Pin + Send + 'static>>; 46 | 47 | /// This is a async task container will be called in the Async Task Item 48 | pub struct AsyncTaskItem { 49 | pub future: PinedFuture, 50 | pub task: Arc, 51 | } 52 | 53 | /// This is a blank kernel task. 54 | pub struct BlankKernelTask(pub usize); 55 | impl AsyncTask for BlankKernelTask { 56 | /// Get task identifier 57 | fn get_task_id(&self) -> TaskId { 58 | self.0 59 | } 60 | 61 | /// before run switch to kernel page table. 62 | /// maybe I don't need to do this. 63 | fn before_run(&self) { 64 | boot_page_table().change(); 65 | } 66 | 67 | /// Get task type. 68 | fn get_task_type(&self) -> TaskType { 69 | TaskType::BlankKernel 70 | } 71 | 72 | /// Exit a task with exit code. But kernel blanktask's exit function never be called. 73 | fn exit(&self, _exit_code: usize) { 74 | unreachable!("can't exit blank kernel task") 75 | } 76 | 77 | /// Get the task exit code. 78 | fn exit_code(&self) -> Option { 79 | unreachable!("Kernel blanktask can't exit") 80 | } 81 | } 82 | 83 | impl_downcast!(sync AsyncTask); 84 | -------------------------------------------------------------------------------- /crates/executor/src/thread.rs: -------------------------------------------------------------------------------- 1 | use core::future::Future; 2 | 3 | use alloc::{boxed::Box, sync::Arc}; 4 | 5 | use crate::{ 6 | task::{AsyncTask, AsyncTaskItem, BlankKernelTask}, 7 | task_id_alloc, TASK_MAP, TASK_QUEUE, 8 | }; 9 | 10 | #[inline] 11 | pub fn spawn(task: Arc, future: impl Future + Send + 'static) { 12 | TASK_MAP 13 | .lock() 14 | .insert(task.get_task_id(), Arc::downgrade(&task)); 15 | TASK_QUEUE.lock().push_back(AsyncTaskItem { 16 | future: Box::pin(future), 17 | task, 18 | }); 19 | } 20 | 21 | #[inline] 22 | pub fn spawn_blank(future: impl Future + Send + 'static) { 23 | let task: Arc = Arc::new(BlankKernelTask(task_id_alloc())); 24 | TASK_MAP 25 | .lock() 26 | .insert(task.get_task_id(), Arc::downgrade(&task)); 27 | TASK_QUEUE.lock().push_back(AsyncTaskItem { 28 | future: Box::pin(future), 29 | task, 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /crates/libc-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libc-types" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | bitflags = "2.0.2" 8 | num_enum = { version = "0.7.3", default-features = false } 9 | -------------------------------------------------------------------------------- /crates/libc-types/src/arch/aarch64.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for aarch64. 2 | //! 3 | //! 4 | 5 | use crate::types::SigSetExtended; 6 | 7 | use super::UStack; 8 | /// 存放信号处理上下文的机器寄存器的结构体 9 | /// 10 | /// MUSL: 11 | #[allow(missing_docs)] 12 | #[repr(C)] 13 | #[derive(Debug, Clone)] 14 | pub struct MContext { 15 | pub fault_address: usize, 16 | pub gregs: [usize; 31], 17 | pub sp: usize, 18 | pub pc: usize, 19 | pub pstate: usize, 20 | pub __reserved: [u128; 256], 21 | } 22 | 23 | /// 信号处理上下文的结构体 24 | /// 25 | /// MUSL: 26 | #[repr(C)] 27 | #[derive(Debug, Clone)] 28 | pub struct UContext { 29 | /// 标志位,用于表示上下文的状态或其他标记 30 | pub flags: usize, 31 | /// 链接寄存器,保存返回地址或跳转地址 32 | pub link: usize, 33 | /// 栈,保存上下文的栈信息 34 | pub stack: UStack, 35 | /// 信号掩码,用于记录哪些信号被屏蔽 36 | pub sig_mask: SigSetExtended, 37 | /// 机器寄存器的上下文信息 38 | pub regs: MContext, 39 | } 40 | -------------------------------------------------------------------------------- /crates/libc-types/src/arch/loongarch64.rs: -------------------------------------------------------------------------------- 1 | use crate::types::SigSetExtended; 2 | 3 | use super::UStack; 4 | 5 | /// 存放信号处理上下文的机器寄存器的结构体 6 | /// 7 | /// MUSL: 8 | #[allow(missing_docs)] 9 | #[repr(C)] 10 | #[derive(Debug, Clone)] 11 | pub struct MContext { 12 | pub pc: usize, 13 | pub gregs: [usize; 32], 14 | pub gflags: u32, 15 | pub fcsr: u32, 16 | pub scr: [usize; 4], 17 | pub fregs: [usize; 32], // _extcontext 18 | _reserved: [usize; 512], 19 | } 20 | 21 | /// 用户上下文结构体(用于信号处理) 22 | /// 对应 Linux 中的 `ucontext_t` 结构,用于保存进程的执行状态。 23 | /// 24 | /// MUSL: 25 | #[repr(C)] 26 | #[derive(Debug, Clone)] 27 | pub struct UContext { 28 | /// 上下文标志,用于指定上下文信息的种类。 29 | pub flags: usize, 30 | /// 指向链接的上下文(如 `setcontext` 返回后恢复的上下文)。 31 | pub link: usize, 32 | /// 栈信息(指向 `stack_t` 结构,表示信号处理时使用的栈)。 33 | pub stack: UStack, 34 | /// 信号屏蔽字,表示处理该信号时应屏蔽的其他信号。 35 | pub sig_mask: SigSetExtended, 36 | /// 用于对齐填充,确保结构体与内核兼容。 37 | _pad: u64, 38 | /// 通用寄存器和浮点上下文等寄存器状态(封装在 `MContext` 中)。 39 | pub regs: MContext, 40 | } 41 | -------------------------------------------------------------------------------- /crates/libc-types/src/arch/riscv64.rs: -------------------------------------------------------------------------------- 1 | use crate::types::SigSetExtended; 2 | 3 | use super::UStack; 4 | 5 | /// 存放信号处理上下文的机器寄存器的结构体 6 | /// 7 | /// MUSL: 8 | /// MUSL: 9 | /// 需要注意的事情是,gregs 中本应该存储 0 的寄存器 zero 存储了 PC 10 | #[allow(missing_docs)] 11 | #[repr(C)] 12 | #[derive(Debug, Clone)] 13 | pub struct MContext { 14 | pub gregs: [usize; 32], 15 | /// 16 | pub _fregs: [u64; 66], 17 | } 18 | 19 | /// 信号处理上下文的结构体 20 | /// 21 | /// MUSL: 22 | #[repr(C)] 23 | #[derive(Debug, Clone)] 24 | pub struct UContext { 25 | /// 标志位,用于表示上下文的状态或其他标记 26 | pub flags: usize, 27 | /// 链接寄存器,保存返回地址或跳转地址 28 | pub link: usize, 29 | /// 栈,保存上下文的栈信息 30 | pub stack: UStack, 31 | /// 信号掩码,用于记录哪些信号被屏蔽 32 | pub sig_mask: SigSetExtended, 33 | /// 机器寄存器的上下文信息 34 | pub regs: MContext, 35 | } 36 | -------------------------------------------------------------------------------- /crates/libc-types/src/arch/x86_64.rs: -------------------------------------------------------------------------------- 1 | use crate::types::SigSetExtended; 2 | 3 | use super::UStack; 4 | 5 | /// 信号处理上下文的结构体 6 | /// 7 | /// MUSL: 8 | #[repr(C)] 9 | #[derive(Debug, Clone)] 10 | pub struct UContext { 11 | /// 标志位,用于表示上下文的状态或其他标记 12 | pub flags: usize, 13 | /// 链接寄存器,保存返回地址或跳转地址 14 | pub link: usize, 15 | /// 栈,保存上下文的栈信息 16 | pub stack: UStack, 17 | /// 通用寄存器的上下文信息 18 | pub gregs: MContext, 19 | /// 信号掩码,用于记录哪些信号被屏蔽 20 | pub sig_mask: SigSetExtended, 21 | /// 浮点寄存器的内存表示 22 | pub __fpregs_mem: [u64; 64], 23 | } 24 | 25 | /// 存放信号处理上下文的机器寄存器的结构体 26 | /// 27 | /// MUSL: 28 | #[allow(missing_docs)] 29 | #[repr(C)] 30 | #[derive(Debug, Clone)] 31 | pub struct MContext { 32 | pub r8: usize, 33 | pub r9: usize, 34 | pub r10: usize, 35 | pub r11: usize, 36 | pub r12: usize, 37 | pub r13: usize, 38 | pub r14: usize, 39 | pub r15: usize, 40 | pub rdi: usize, 41 | pub rsi: usize, 42 | pub rbp: usize, 43 | pub rbx: usize, 44 | pub rdx: usize, 45 | pub rax: usize, 46 | pub rcx: usize, 47 | pub rsp: usize, 48 | pub rip: usize, 49 | pub eflags: usize, 50 | pub cs: u8, 51 | pub gs: u8, 52 | pub fs: u8, 53 | __pad0: u8, 54 | pub err: usize, 55 | pub trapno: usize, 56 | pub oldmask: usize, 57 | pub cr2: usize, 58 | pub fp_ptr: usize, 59 | __reserved1: [usize; 8], 60 | } 61 | -------------------------------------------------------------------------------- /crates/libc-types/src/consts.rs: -------------------------------------------------------------------------------- 1 | //! This file contains constants used in the libc crate. 2 | //! 3 | //! 4 | /// 表示更新时间为当前时间(用于 utimensat 等系统调用) 5 | pub const UTIME_NOW: usize = 0x3fffffff; 6 | 7 | /// 表示不修改对应的时间字段(用于 utimensat 等系统调用) 8 | pub const UTIME_OMIT: usize = 0x3ffffffe; 9 | -------------------------------------------------------------------------------- /crates/libc-types/src/elf.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for ELF (Executable and Linkable Format). 2 | 3 | /// ELF auxiliary vector (auxv) entry type 4 | /// 5 | /// MUSL: 6 | #[repr(usize)] 7 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 8 | pub enum AuxType { 9 | /// 结束标志 10 | Null = 0, 11 | /// 被忽略的项 12 | Ignore = 1, 13 | /// 文件描述符(指向可执行文件),在内核加载器中用 14 | ExecFd = 2, 15 | /// 程序头表(Program Header Table)在内存中的地址 16 | Phdr = 3, 17 | /// 每个程序头的大小(以字节为单位) 18 | Phent = 4, 19 | /// 程序头的数量 20 | Phnum = 5, 21 | /// 页大小(单位字节),如 4096 22 | PageSize = 6, 23 | /// 动态链接器的基址(即 ld.so 的加载地址) 24 | Base = 7, 25 | /// 运行时标志,通常为 0 26 | Flags = 8, 27 | /// 程序入口点(Entry Point) 28 | Entry = 9, 29 | /// 如果是非 ELF 二进制(a.out 格式),为 1,否则为 0 30 | NotElf = 10, 31 | /// 实际用户 ID(UID) 32 | UID = 11, 33 | /// 有效用户 ID(EUID) 34 | EUID = 12, 35 | /// 实际组 ID(GID) 36 | GID = 13, 37 | /// 有效组 ID(EGID) 38 | EGID = 14, 39 | /// CPU 平台名称的指针(如 "x86_64") 40 | Platform = 15, 41 | /// 硬件能力位(bitmask),如 SSE/AVX 支持 42 | HwCap = 16, 43 | /// 每秒的时钟滴答数(用于 `times()` 等函数) 44 | ClkTck = 17, 45 | /// x86 FPU 控制字(FPUCW) 46 | FpuCw = 18, 47 | /// D-cache(数据缓存)大小 48 | DCacheBSize = 19, 49 | /// I-cache(指令缓存)大小 50 | ICacheBSize = 20, 51 | /// 通用缓存大小 52 | UCacheBSize = 21, 53 | /// PowerPC 平台专用,被忽略 54 | IgnorePPC = 22, 55 | /// 是否是安全模式(非 suid/guid),0 = 否,1 = 是 56 | Secure = 23, 57 | /// 基础平台名称的指针(字符串) 58 | BasePlatform = 24, 59 | /// 指向随机数种子(stack 上的 16 字节随机值) 60 | Random = 25, 61 | /// 第二组 HWCAP(arm64/aarch64) 62 | HwCap2 = 26, 63 | /// 命令行中可执行文件路径的地址(如 "/bin/ls") 64 | ExecFn = 31, 65 | /// 指向 vsyscall 区域的函数地址(如 `gettimeofday()`) 66 | SysInfo = 32, 67 | /// 指向 VDSO ELF 映射的起始地址 68 | SysInfoEhdr = 33, 69 | } 70 | -------------------------------------------------------------------------------- /crates/libc-types/src/epoll.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for Epoll (event polling). 2 | //! 3 | //! MUSL: 4 | 5 | use num_enum::TryFromPrimitive; 6 | 7 | use crate::poll::PollEvent; 8 | 9 | /// 表示 epoll 事件的结构体(对应 Linux 的 `struct epoll_event`) 10 | /// 11 | /// MUSL: 12 | /// TODO: 根据 data 的类型,可能需要使用不同的结构体来表示不同的事件, 对应 C 语言的 `union` 13 | /// NOTE: 在 x86_64 架构会添加 `__attribute__ ((__packed__))`,以确保结构体的内存对齐 14 | #[repr(C)] 15 | #[derive(Clone, Debug)] 16 | pub struct EpollEvent { 17 | /// 事件类型(如可读、可写等,使用 EpollEventType 表示) 18 | pub events: EpollEventType, 19 | /// 用户数据(如 fd 或标识符),epoll 不做解释 20 | pub data: u64, 21 | } 22 | 23 | bitflags! { 24 | /// Epoll 事件类型(类似 poll 的事件掩码) 25 | #[derive(Clone, Debug)] 26 | pub struct EpollEventType: u32 { 27 | /// 表示对应的文件描述符可读(包括普通数据和优先数据) 28 | const EPOLLIN = 0x001; 29 | /// 表示对应的文件描述符可写(低水位标记) 30 | const EPOLLOUT = 0x004; 31 | /// 文件描述符发生错误(error) 32 | const EPOLLERR = 0x008; 33 | /// 对端挂起或关闭连接(hang up) 34 | const EPOLLHUP = 0x010; 35 | /// 有高优先级数据可读(如带外数据) 36 | const EPOLLPRI = 0x002; 37 | /// 普通数据可读(normal read) 38 | const EPOLLRDNORM = 0x040; 39 | /// 带外数据可读(band read) 40 | const EPOLLRDBAND = 0x080; 41 | /// 普通数据可写(normal write) 42 | const EPOLLWRNORM = 0x100; 43 | /// 带外数据可写(band write) 44 | const EPOLLWRBAND = 0x200; 45 | /// 有系统消息可读(通常未使用) 46 | const EPOLLMSG = 0x400; 47 | /// 流被对端关闭,半关闭状态(对端调用 shutdown 写) 48 | const EPOLLRDHUP = 0x2000; 49 | /// 表示该监听是排他的(exclusive),用于防止多线程同时触发 50 | const EPOLLEXCLUSIVE = 0x1000_0000; 51 | /// 唤醒系统 suspend 状态(需要 CAP_BLOCK_SUSPEND 权限) 52 | const EPOLLWAKEUP = 0x2000_0000; 53 | /// 事件触发一次后就自动删除(one-shot 模式) 54 | const EPOLLONESHOT = 0x4000_0000; 55 | /// 边缘触发(Edge-Triggered)模式 56 | const EPOLLET = 0x8000_0000; 57 | } 58 | } 59 | 60 | impl EpollEventType { 61 | /// 将 EpollEventType 转换为 PollEvent 62 | pub fn to_poll(&self) -> PollEvent { 63 | PollEvent::from_bits_truncate(self.bits() as u16) 64 | } 65 | } 66 | 67 | #[repr(u8)] 68 | #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] 69 | /// `epoll_ctl` 操作类型,用于管理 epoll 实例中的监听目标(fd)。 70 | pub enum EpollCtl { 71 | /// 添加一个新的监听目标到 epoll 实例中(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, event)) 72 | ADD = 1, 73 | /// 从 epoll 实例中删除一个监听目标(epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL)) 74 | DEL = 2, 75 | /// 修改已添加目标的监听事件(epoll_ctl(epfd, EPOLL_CTL_MOD, fd, event)) 76 | MOD = 3, 77 | } 78 | -------------------------------------------------------------------------------- /crates/libc-types/src/futex.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for FUTEX (fast user-space mutex). 2 | use num_enum::TryFromPrimitive; 3 | 4 | /// Futex 操作类型枚举 5 | /// 6 | /// MUSL: 7 | #[derive(Debug, TryFromPrimitive)] 8 | #[repr(usize)] 9 | pub enum FutexFlags { 10 | /// 等待操作,线程阻塞直到被唤醒 11 | Wait = 0, 12 | /// 唤醒等待的线程 13 | Wake = 1, 14 | /// 使用文件描述符的 Futex 操作(较少用) 15 | Fd = 2, 16 | /// 将等待队列中的线程重新排队到另一个 Futex 17 | Requeue = 3, 18 | /// 带比较操作的重新排队,只有在值匹配时才执行排队 19 | CmpRequeue = 4, 20 | /// 执行复杂的唤醒和重新排队组合操作 21 | WakeOp = 5, 22 | /// 获取 Priority Inheritance 锁 23 | LockPi = 6, 24 | /// 释放 Priority Inheritance 锁 25 | UnlockPi = 7, 26 | /// 尝试获取 Priority Inheritance 锁(非阻塞) 27 | TrylockPi = 8, 28 | /// 等待指定的位集合(bitset),类似于 Wait,但支持位掩码 29 | WaitBitset = 9, 30 | } 31 | 32 | /// 标志:表示 futex 是私有的,只在同一进程内使用(性能更好) 33 | /// 相当于 FUTEX_PRIVATE_FLAG,避免跨进程同步开销 34 | pub const FUTEX_PRIVATE: usize = 128; 35 | 36 | /// 标志:使用系统实时时钟(CLOCK_REALTIME)作为超时基准 37 | /// 默认 futex 超时使用的是 CLOCK_MONOTONIC,设置此标志改为使用实时时钟 38 | pub const FUTEX_CLOCK_REALTIME: usize = 256; 39 | -------------------------------------------------------------------------------- /crates/libc-types/src/internal.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for libc internal use. 2 | //! 3 | //! 4 | 5 | use crate::types::SigSet; 6 | 7 | /// 信号处理函数的结构体(对应 C 的 `struct sigaction`) 8 | /// 9 | /// MUSL: 10 | #[repr(C)] 11 | #[derive(Debug, Clone)] 12 | pub struct SigAction { 13 | /// 信号处理函数指针,类似于 C 中的 void (*sa_handler)(int); 14 | /// 当信号发生时将调用此函数。也可以是特殊值,如 SIG_IGN 或 SIG_DFL。 15 | pub handler: usize, 16 | /// 标志位,用于指定处理行为,如 SA_RESTART、SA_NOCLDSTOP 等。 17 | /// 对应 C 中的 int sa_flags; 18 | pub flags: usize, 19 | /// 系统调用的恢复函数指针,一般在使用自定义恢复机制时使用。 20 | /// 对应 C 中的 void (*sa_restorer)(void); 通常不使用,设为 0。 21 | pub restorer: usize, 22 | /// 一个信号集合,用于在处理该信号时阻塞的其他信号。 23 | /// 对应 C 中的 sigset_t sa_mask; 24 | pub mask: SigSet, 25 | } 26 | 27 | impl SigAction { 28 | /// 表示使用该信号的默认处理方式(default action) 29 | /// 用于注册信号处理函数时,表示恢复默认行为(如终止进程等)。 30 | pub const SIG_DFL: usize = 0; 31 | /// 表示忽略该信号(ignore) 32 | /// 用于注册信号处理函数时,表示收到该信号时不做任何处理。 33 | pub const SIG_IGN: usize = 1; 34 | /// 创建一个新的信号处理函数结构体,所有字段初始化为默认值。 35 | pub const fn empty() -> Self { 36 | Self { 37 | handler: 0, 38 | mask: SigSet::empty(), 39 | flags: 0, 40 | restorer: 0, 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/libc-types/src/ioctl.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for IOCTL. 2 | //! 3 | //! MUSL: 4 | //! TODO: Check ioctl command for multi architectures. 5 | 6 | use num_enum::TryFromPrimitive; 7 | 8 | #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] 9 | #[repr(u32)] 10 | /// Teletype 设备相关 ioctl 命令,用于控制终端(如串口、TTY)行为。 11 | pub enum TermIoctlCmd { 12 | // 用于 struct termios 13 | /// 获取当前串口设置(termios 结构体) 14 | TCGETS = 0x5401, 15 | /// 立即设置串口配置(termios 结构体) 16 | TCSETS = 0x5402, 17 | /// 等待输出缓冲区刷新后再设置串口配置 18 | TCSETSW = 0x5403, 19 | /// 刷新输入输出缓冲区后设置串口配置 20 | TCSETSF = 0x5404, 21 | 22 | // 用于 struct termio(旧接口) 23 | /// 获取当前串口设置(termio 结构体) 24 | TCGETA = 0x5405, 25 | /// 立即设置串口配置(termio 结构体) 26 | TCSETA = 0x5406, 27 | /// 等待输出缓冲区刷新后设置串口配置 28 | TCSETAW = 0x5407, 29 | /// 刷新输入输出缓冲区后设置串口配置 30 | TCSETAF = 0x5408, 31 | 32 | /// 获取当前终端的前台进程组 ID 33 | TIOCGPGRP = 0x540F, 34 | /// 设置当前终端的前台进程组 ID 35 | TIOCSPGRP = 0x5410, 36 | 37 | /// 获取终端窗口大小(通常与 struct winsize 搭配) 38 | TIOCGWINSZ = 0x5413, 39 | /// 设置终端窗口大小 40 | TIOCSWINSZ = 0x5414, 41 | 42 | /// 取消 `close-on-exec` 标志(在 `exec` 执行时文件描述符不会自动关闭) 43 | FIONCLEX = 0x5450, 44 | /// 设置 `close-on-exec` 标志(在 `exec` 执行时自动关闭文件描述符) 45 | FIOCLEX = 0x5451, 46 | 47 | /// 设置非阻塞 I/O(rustc 编译器也会用这个 ioctl 命令控制 pipe 行为) 48 | FIONBIO = 0x5421, 49 | 50 | /// 获取 RTC(实时时钟)的当前时间(用于 RTC 设备) 51 | RTCRDTIME = 0x80247009, 52 | } 53 | -------------------------------------------------------------------------------- /crates/libc-types/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides the `libc` types for the `libc` crate. 2 | //! 3 | //! 4 | #![no_std] 5 | #![deny(warnings)] 6 | #![deny(missing_docs)] 7 | #![deny(clippy::all)] 8 | 9 | #[macro_use] 10 | extern crate bitflags; 11 | 12 | #[macro_use] 13 | mod utils; 14 | 15 | mod arch; 16 | pub mod consts; 17 | pub mod elf; 18 | pub mod epoll; 19 | pub mod fcntl; 20 | pub mod futex; 21 | pub mod internal; 22 | pub mod ioctl; 23 | pub mod mman; 24 | pub mod others; 25 | pub mod poll; 26 | pub mod resource; 27 | pub mod sched; 28 | pub mod signal; 29 | pub mod termios; 30 | pub mod time; 31 | pub mod times; 32 | pub mod types; 33 | pub mod utsname; 34 | -------------------------------------------------------------------------------- /crates/libc-types/src/mman.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for MMAN (memory management). 2 | //! 3 | //! MUSL: 4 | 5 | bitflags! { 6 | /// MAP 标志位(用于 mmap 等内存映射操作) 7 | /// 8 | /// MUSL: 9 | #[derive(Debug)] 10 | pub struct MapFlags: u32 { 11 | /// 共享映射,写入会直接影响文件内容 12 | const SHARED = 0x01; 13 | /// 私有映射,写入会产生写时复制(Copy-on-Write) 14 | const PRIVATE = 0x02; 15 | /// 验证共享映射(共享或私有),与 MAP_SHARED_VALIDATE 相关 16 | const SHARED_VALIDATE = 0x03; 17 | /// 映射类型掩码(用于屏蔽高位判断映射类型) 18 | const TYPE = 0x0f; 19 | /// 使用固定地址映射,映射必须在指定地址 20 | const FIXED = 0x10; 21 | /// 匿名映射,不与任何文件关联(内容初始化为 0) 22 | const ANONYMOUS = 0x20; 23 | /// 不保留交换空间(swap) 24 | const NORESERVE = 0x4000; 25 | /// 堆栈向下增长区域(如线程栈) 26 | const GROWSDOWN = 0x0100; 27 | /// 拒绝写操作(通常用于文件系统写保护) 28 | const DENYWRITE = 0x0800; 29 | /// 映射可执行代码(允许执行权限) 30 | const EXECUTABLE = 0x1000; 31 | /// 映射锁定在内存中,避免换出 32 | const LOCKED = 0x2000; 33 | /// 预先加载页面(降低缺页中断) 34 | const POPULATE = 0x8000; 35 | /// 非阻塞映射 36 | const NONBLOCK = 0x10000; 37 | /// 映射用作线程栈 38 | const STACK = 0x20000; 39 | /// 使用大页(HugeTLB) 40 | const HUGETLB = 0x40000; 41 | /// 同步映射(同步内存访问) 42 | const SYNC = 0x80000; 43 | /// 固定映射,但不覆盖已有映射 44 | const FIXED_NOREPLACE = 0x100000; 45 | /// 文件映射(默认标志) 46 | const FILE = 0; 47 | } 48 | 49 | 50 | #[derive(Debug, Clone, Copy)] 51 | /// 内存映射保护标志(mmap 的 prot 参数) 52 | /// MUSL: 53 | pub struct MmapProt: u32 { 54 | /// 可读权限,映射区域可被读取 55 | const READ = bit!(0); 56 | /// 可写权限,映射区域可被写入 57 | const WRITE = bit!(1); 58 | /// 可执行权限,映射区域允许执行代码 59 | const EXEC = bit!(2); 60 | } 61 | 62 | #[derive(Debug)] 63 | /// msync 同步标志,用于控制 msync 行为 64 | /// 65 | /// MUSL: 66 | pub struct MSyncFlags: u32 { 67 | /// 异步同步(异步刷新内存映射区域到存储设备) 68 | const ASYNC = 1 << 0; 69 | /// 使其他缓存失效(使缓存区域无效) 70 | const INVALIDATE = 1 << 1; 71 | /// 同步同步(阻塞直到数据完全写入存储设备) 72 | const SYNC = 1 << 2; 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /crates/libc-types/src/others.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for Other unclassified types. 2 | //! 3 | //! 4 | 5 | use num_enum::TryFromPrimitive; 6 | 7 | /// Architecture-specific command for the `arch_prctl` syscall. 8 | #[repr(usize)] 9 | #[derive(Debug, Clone, TryFromPrimitive)] 10 | pub enum ArchPrctlCmd { 11 | /// Set Per-CPU base 12 | SetGS = 0x1001, 13 | /// Set Thread Local Storage (TLS) base 14 | SetFS = 0x1002, 15 | /// Get Thread Local Storage (TLS) base 16 | GetFS = 0x1003, 17 | /// Get Per-CPU base 18 | GetGS = 0x1004, 19 | } 20 | -------------------------------------------------------------------------------- /crates/libc-types/src/poll.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for Poll (polling). 2 | //! 3 | //! MUSL: 4 | 5 | bitflags! { 6 | /// Poll 事件类型(类似于 epoll 的事件掩码) 7 | /// 8 | /// MUSL: 9 | #[derive(Debug, Clone, PartialEq)] 10 | pub struct PollEvent: u16 { 11 | /// 无事件(默认值) 12 | const NONE = 0; 13 | /// 有数据可读 14 | const IN = 0x001; 15 | /// 有紧急数据可读(带外数据) 16 | const PRI = 0x002; 17 | /// 可写数据(缓冲区未满) 18 | const OUT = 0x004; 19 | /// 普通数据可读(等价于 POLLIN,用于区分优先级) 20 | const RDNORM = 0x040; 21 | /// 带外数据可读 22 | const RDBAND = 0x080; 23 | /// 普通数据可写(等价于 POLLOUT,用于区分优先级) 24 | const WRNORM = 0x100; 25 | /// 带外数据可写 26 | const WRBAND = 0x200; 27 | /// Linux 特有,可能与消息通知相关(通常不使用) 28 | const MSG = 0x400; 29 | /// 从 epoll 或 poll 实例中移除此文件描述符(Linux 特有) 30 | const REMOVE = 0x1000; 31 | /// 远端关闭(对端 shutdown write 或关闭 socket) 32 | const RDHUP = 0x2000; 33 | /// 错误事件(如写管道时接收端关闭) 34 | /// 不需要显式监听,默认总是报告 35 | const ERR = 0x008; 36 | /// 挂起事件(如对端关闭连接) 37 | /// 不需要显式监听,默认总是报告 38 | const HUP = 0x010; 39 | /// 无效的请求(如监听了一个无效的 fd) 40 | /// 不需要显式监听,默认总是报告 41 | const NVAL = 0x020; 42 | } 43 | } 44 | 45 | #[repr(C)] 46 | #[derive(Debug, Clone)] 47 | /// 用于 poll 系统调用的文件描述符结构体 48 | /// 49 | /// MUSL: 50 | pub struct PollFd { 51 | /// 文件描述符(File Descriptor),要监视的对象 52 | pub fd: u32, 53 | /// 期望监听的事件(如可读、可写等),由用户设置 54 | pub events: PollEvent, 55 | /// 实际发生的事件,由内核填写 56 | pub revents: PollEvent, 57 | } 58 | -------------------------------------------------------------------------------- /crates/libc-types/src/resource.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for Resource (system resource management). 2 | //! 3 | //! MUSL: 4 | 5 | use crate::types::TimeVal; 6 | 7 | /// 资源限制结构体(对应 C 的 `struct rlimit`) 8 | /// 用于描述进程对某种资源的当前限制和最大限制 9 | #[repr(C)] 10 | #[derive(Debug, Clone)] 11 | pub struct Rlimit { 12 | /// 当前资源软限制(soft limit),即实际生效的限制值 13 | /// 进程可以在不超过 `max` 的情况下修改它 14 | pub curr: usize, 15 | /// 最大资源限制(hard limit),软限制不能超过该值 16 | /// 只有具有特权的进程才能提升此值 17 | pub max: usize, 18 | } 19 | 20 | /// 资源使用情况结构体(对应 C 的 `struct rusage`) 21 | /// 记录进程或线程的时间和资源消耗信息 22 | /// MUSL: 23 | #[repr(C)] 24 | pub struct Rusage { 25 | /// 用户态运行时间(单位:秒 + 微秒) 26 | pub utime: TimeVal, 27 | /// 内核态运行时间(单位:秒 + 微秒) 28 | pub stime: TimeVal, 29 | /// 最大常驻集大小(单位:KB),即内存占用峰值 30 | pub maxrss: i64, 31 | /// 索引页的使用数量(已废弃) 32 | pub ixrss: i64, 33 | /// 数据段内存使用量(已废弃) 34 | pub idrss: i64, 35 | /// 堆栈段内存使用量(已废弃) 36 | pub isrss: i64, 37 | /// 页面缺页异常数(软缺页,不涉及磁盘 IO) 38 | pub minflt: i64, 39 | /// 主缺页异常数(硬缺页,需要从磁盘读取页面) 40 | pub majflt: i64, 41 | /// 发生的交换(swap)次数 42 | pub nswap: i64, 43 | /// 输入操作(块设备读取)的次数 44 | pub inblock: i64, 45 | /// 输出操作(块设备写入)的次数 46 | pub oublock: i64, 47 | /// 发送的 IPC 消息数(已废弃) 48 | pub msgsnd: i64, 49 | /// 接收的 IPC 消息数(已废弃) 50 | pub msgrcv: i64, 51 | /// 捕获的信号数量 52 | pub nsignals: i64, 53 | /// 自愿上下文切换次数(如等待锁) 54 | pub nvcsw: i64, 55 | /// 非自愿上下文切换次数(被内核抢占) 56 | pub nivcsw: i64, 57 | } 58 | -------------------------------------------------------------------------------- /crates/libc-types/src/sched.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for SCHED (scheduling). 2 | //! 3 | //! MUSL: 4 | 5 | bitflags! { 6 | /// 克隆标志,用于 `clone(2)` 或 `clone3(2)` 系统调用。 7 | /// 8 | /// MUSL: 9 | #[derive(Debug)] 10 | pub struct CloneFlags: usize { 11 | /// 指定发送给子进程的信号(低 8 位),如 SIGCHLD 12 | const CSIGNAL = 0x000000ff; 13 | /// 使用新的 time 命名空间(Linux 5.6+) 14 | const CLONE_NEWTIME = 0x00000080; 15 | /// 与父进程共享内存空间(即使用相同地址空间) 16 | const CLONE_VM = 0x00000100; 17 | /// 与父进程共享文件系统信息(当前目录、root 等) 18 | const CLONE_FS = 0x00000200; 19 | /// 与父进程共享打开的文件描述符 20 | const CLONE_FILES = 0x00000400; 21 | /// 与父进程共享信号处理函数 22 | const CLONE_SIGHAND = 0x00000800; 23 | /// 将 PIDFD 文件描述符写入 clone 参数中指定的位置 24 | const CLONE_PIDFD = 0x00001000; 25 | /// 被调试器使用,子进程会被 trace(如 ptrace) 26 | const CLONE_PTRACE = 0x00002000; 27 | /// 以 vfork 语义启动子进程,阻塞父进程直到 exec/exit 28 | const CLONE_VFORK = 0x00004000; 29 | /// 设置父进程为新进程的 parent,而不是调用进程 30 | const CLONE_PARENT = 0x00008000; 31 | /// 与父进程成为线程(共享 signal、VM、文件等) 32 | const CLONE_THREAD = 0x00010000; 33 | /// 使用新的挂载命名空间(mount namespace) 34 | const CLONE_NEWNS = 0x00020000; 35 | /// 与父进程共享 System V 信号量 36 | const CLONE_SYSVSEM = 0x00040000; 37 | /// 设置 TLS(线程局部存储)指针 38 | const CLONE_SETTLS = 0x00080000; 39 | /// 在指定地址写入子进程的 TID(parent 设置) 40 | const CLONE_PARENT_SETTID = 0x00100000; 41 | /// 进程退出时自动清除 TID(通常用于 futex 唤醒) 42 | const CLONE_CHILD_CLEARTID = 0x00200000; 43 | /// 被废弃,曾用于标记 detached 线程 44 | const CLONE_DETACHED = 0x00400000; 45 | /// 禁用子进程被 trace 46 | const CLONE_UNTRACED = 0x00800000; 47 | /// 在指定地址写入子进程的 TID(child 设置) 48 | const CLONE_CHILD_SETTID = 0x01000000; 49 | /// 使用新的 cgroup 命名空间(隔离控制组) 50 | const CLONE_NEWCGROUP = 0x02000000; 51 | /// 使用新的 UTS 命名空间(隔离主机名/域名) 52 | const CLONE_NEWUTS = 0x04000000; 53 | /// 使用新的 IPC 命名空间(隔离 System V IPC) 54 | const CLONE_NEWIPC = 0x08000000; 55 | /// 使用新的用户命名空间(user namespace) 56 | const CLONE_NEWUSER = 0x10000000; 57 | /// 使用新的 PID 命名空间(隔离进程号) 58 | const CLONE_NEWPID = 0x20000000; 59 | /// 使用新的网络命名空间(network namespace) 60 | const CLONE_NEWNET = 0x40000000; 61 | /// 启用 I/O 上下文的共享(Linux 2.6.25+) 62 | const CLONE_IO = 0x80000000; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /crates/libc-types/src/signal.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for Signal. 2 | //! 3 | //! MUSL: 4 | 5 | pub use crate::arch::{MContext, SignalStackFlags, UContext, UStack}; 6 | use num_enum::TryFromPrimitive; 7 | 8 | /// POSIX 标准、线程扩展与实时信号枚举定义(信号编号从 1 开始) 9 | /// MUSL: 10 | #[repr(u8)] 11 | #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive)] 12 | pub enum SignalNum { 13 | /// 终端挂起(Hangup) 14 | HUP = 1, 15 | /// 交互式中断(Interrupt) 16 | INT, 17 | /// 退出(Quit) 18 | QUIT, 19 | /// 非法指令(Illegal instruction) 20 | ILL, 21 | /// 断点(Trace/breakpoint trap) 22 | TRAP, 23 | /// 异常终止(Abort) 24 | ABRT, 25 | /// 总线错误(Bus error) 26 | BUS, 27 | /// 浮点异常(Floating-point exception) 28 | FPE, 29 | /// 强制终止(Kill,不可被捕获或忽略) 30 | KILL, 31 | /// 用户定义信号 1 32 | USR1, 33 | /// 段错误(Segmentation fault) 34 | SEGV, 35 | /// 用户定义信号 2 36 | USR2, 37 | /// 管道破裂(Broken pipe) 38 | PIPE, 39 | /// 报警时钟(Alarm clock) 40 | ALRM, 41 | /// 终止请求(Termination) 42 | TERM, 43 | /// 协处理器堆栈故障(栈浮点错误,仅部分平台支持) 44 | STKFLT, 45 | /// 子进程终止或状态变化(Child) 46 | CHLD, 47 | /// 继续执行(Continue) 48 | CONT, 49 | /// 停止进程(不可忽略) 50 | STOP, 51 | /// 终端停止(来自 TTY 的 Ctrl+Z) 52 | TSTP, 53 | /// 后台读取控制终端 54 | TTIN, 55 | /// 后台写入控制终端 56 | TTOU, 57 | /// 紧急条件(Urgent socket) 58 | URG, 59 | /// 超过 CPU 时间限制 60 | XCPU, 61 | /// 超过文件大小限制 62 | XFSZ, 63 | /// 虚拟计时器到期(Virtual alarm) 64 | VTALRM, 65 | /// 性能分析计时器到期(Profiling alarm) 66 | PROF, 67 | /// 窗口大小改变(Window size change) 68 | WINCH, 69 | /// 异步 I/O(I/O now possible) 70 | IO, 71 | /// 电源失败(Power failure) 72 | PWR, 73 | /// 非法系统调用(Bad syscall) 74 | SYS, 75 | /// POSIX 线程:定时器信号 76 | TIMER, 77 | /// POSIX 线程:取消信号 78 | CANCEL, 79 | /// POSIX 线程:同步调用信号 80 | SYNCCALL, 81 | /// 实时信号 3(Real-time signal 3) 82 | RT3, 83 | /// 实时信号 4 84 | RT4, 85 | /// 实时信号 5 86 | RT5, 87 | /// 实时信号 6 88 | RT6, 89 | /// 实时信号 7 90 | RT7, 91 | /// 实时信号 8 92 | RT8, 93 | /// 实时信号 9 94 | RT9, 95 | /// 实时信号 10 96 | RT10, 97 | /// 实时信号 11 98 | RT11, 99 | /// 实时信号 12 100 | RT12, 101 | /// 实时信号 13 102 | RT13, 103 | /// 实时信号 14 104 | RT14, 105 | /// 实时信号 15 106 | RT15, 107 | /// 实时信号 16 108 | RT16, 109 | /// 实时信号 17 110 | RT17, 111 | /// 实时信号 18 112 | RT18, 113 | /// 实时信号 19 114 | RT19, 115 | /// 实时信号 20 116 | RT20, 117 | /// 实时信号 21 118 | RT21, 119 | /// 实时信号 22 120 | RT22, 121 | /// 实时信号 23 122 | RT23, 123 | /// 实时信号 24 124 | RT24, 125 | /// 实时信号 25 126 | RT25, 127 | /// 实时信号 26 128 | RT26, 129 | /// 实时信号 27 130 | RT27, 131 | /// 实时信号 28 132 | RT28, 133 | /// 实时信号 29 134 | RT29, 135 | /// 实时信号 30 136 | RT30, 137 | /// 实时信号 31 138 | RT31, 139 | /// 最大实时信号(Real-time signal max) 140 | RTMAX, 141 | } 142 | 143 | /// 实时信号(Real-time signals)的起始编号。 144 | pub const REAL_TIME_SIGNAL_NUM: usize = 33; 145 | 146 | impl SignalNum { 147 | /// 从数字构造 `SignalNum` 枚举,如果超出合法范围则返回 `None`。 148 | #[inline] 149 | pub fn from_num(num: usize) -> Option { 150 | SignalNum::try_from(num as u8).ok() 151 | } 152 | 153 | /// 获取信号的编号(number)。 154 | pub const fn num(&self) -> usize { 155 | *self as usize 156 | } 157 | 158 | /// 判断是否为实时信号(Real-time Signal)。 159 | /// 160 | /// 实时信号从编号 [REAL_TIME_SIGNAL_NUM] 开始。 161 | pub const fn is_rt(&self) -> bool { 162 | *self as usize >= REAL_TIME_SIGNAL_NUM 163 | } 164 | 165 | /// 如果是实时信号,则返回其在实时信号表中的索引(从 1 开始)。 166 | /// 167 | /// 例如,编号为 [REAL_TIME_SIGNAL_NUM] 的信号返回 `1`。 168 | #[inline] 169 | pub fn real_time_index(&self) -> Option { 170 | self.is_rt().then(|| self.num() - 32) 171 | } 172 | 173 | /// 获取信号的位掩码(bit mask)。 174 | /// 175 | /// 信号编号从 1 开始,因此返回值为 `1 << (num - 1)`。 176 | pub const fn mask(&self) -> u64 { 177 | bit!(self.num() - 1) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /crates/libc-types/src/time.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for Time (time management). 2 | //! 3 | //! MUSL: 4 | 5 | use crate::types::TimeVal; 6 | 7 | /// 定时器结构体,表示间隔和当前值(对应 C 语言中的 `struct itimerval`) 8 | #[repr(C)] 9 | #[derive(Clone, Debug, Default, Copy)] 10 | pub struct ITimerVal { 11 | /// 重复触发的间隔时间(interval > 0 表示周期性定时器) 12 | pub interval: TimeVal, 13 | /// 当前倒计时的剩余时间(初始超时时长) 14 | pub value: TimeVal, 15 | } 16 | -------------------------------------------------------------------------------- /crates/libc-types/src/times.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for TIMES (time management). 2 | //! 3 | //! 4 | 5 | /// 程序运行的时间 6 | /// 7 | /// MUSL: 8 | #[allow(dead_code)] 9 | #[derive(Default, Clone, Copy)] 10 | pub struct TMS { 11 | /// 进程执行用户代码的时间 12 | pub utime: u64, 13 | /// 进程执行内核代码的时间 14 | pub stime: u64, 15 | /// 子进程执行用户代码的时间 16 | pub cutime: u64, 17 | /// 子进程执行内核代码的时间 18 | pub cstime: u64, 19 | } 20 | -------------------------------------------------------------------------------- /crates/libc-types/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module provides utility functions to imporove code readability and maintainability. 2 | //! 3 | //! 4 | 5 | /// Get the (1 << $idx) value. 6 | macro_rules! bit { 7 | ($idx:expr) => { 8 | (1 << $idx) 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /crates/libc-types/src/utsname.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `libc` types for UTSNAME (Unix Time Sharing Name). 2 | //! 3 | //! 4 | 5 | /// 系统信息结构体(对应 `struct utsname`),用于表示内核和主机相关信息 6 | /// 7 | /// MUSL: 8 | pub struct UTSname { 9 | /// 操作系统名称,例如 "Linux" 10 | pub sysname: [u8; 65], 11 | /// 主机名称,例如 "my-hostname" 12 | pub nodename: [u8; 65], 13 | /// 内核发行版本,例如 "5.15.0" 14 | pub release: [u8; 65], 15 | /// 内核版本信息,例如 "#1 SMP PREEMPT_DYNAMIC ..." 16 | pub version: [u8; 65], 17 | /// 机器架构,例如 "x86_64" 18 | pub machine: [u8; 65], 19 | /// 域名,例如 "(none)" 或 "example.com" 20 | pub domainname: [u8; 65], 21 | } 22 | -------------------------------------------------------------------------------- /crates/runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | buddy_system_allocator = "0.9" 10 | log = "0.4" 11 | bit_field = "0.10.1" 12 | polyhal = { workspace = true } 13 | sync = { workspace = true } 14 | -------------------------------------------------------------------------------- /crates/runtime/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs, path::PathBuf}; 2 | 3 | fn main() { 4 | let out_dir = PathBuf::from(env::var("OUT_DIR").expect("can't find manifest dir")); 5 | let heap_size = env::var("HEAP_SIZE").unwrap_or("0x0180_0000".into()); 6 | fs::write( 7 | out_dir.join("consts.rs"), 8 | format!("pub const HEAP_SIZE: usize = {heap_size};"), 9 | ) 10 | .expect("can't write data to temp file in the out_dir"); 11 | 12 | println!("cargo:rerun-if-env-changed=HEAP_SIZE"); 13 | println!("cargo:rerun-if-changed=build.rs"); 14 | } 15 | -------------------------------------------------------------------------------- /crates/runtime/src/heap.rs: -------------------------------------------------------------------------------- 1 | extern crate alloc; 2 | 3 | use buddy_system_allocator::LockedHeap; 4 | use log::info; 5 | 6 | include!(concat!(env!("OUT_DIR"), "/consts.rs")); 7 | 8 | // 堆大小 9 | // const HEAP_SIZE: usize = 0x0180_0000; 10 | // pub const HEAP_SIZE: usize = 0x0180_0000; 11 | 12 | // 堆空间 13 | #[link_section = ".bss.heap"] 14 | static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE]; 15 | 16 | /// 堆内存分配器 17 | #[global_allocator] 18 | static HEAP_ALLOCATOR: LockedHeap<30> = LockedHeap::empty(); 19 | 20 | /// 初始化堆内存分配器 21 | pub fn init() { 22 | unsafe { 23 | HEAP_ALLOCATOR 24 | .lock() 25 | .init(&raw mut HEAP as usize, HEAP_SIZE); 26 | 27 | info!( 28 | "kernel HEAP init: {:#x} - {:#x} size: {:#x}", 29 | &raw const HEAP as usize, 30 | &raw const HEAP as usize + HEAP_SIZE, 31 | HEAP_SIZE 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | #[macro_use] 4 | extern crate alloc; 5 | 6 | pub mod frame; 7 | mod heap; 8 | 9 | pub fn init() { 10 | heap::init(); 11 | } 12 | -------------------------------------------------------------------------------- /crates/sync/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sync" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | spin = { version = "0.9.8", features = ["mutex"] } 10 | -------------------------------------------------------------------------------- /crates/sync/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | pub use spin::{ 4 | lazy::Lazy, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockUpgradableGuard, RwLockWriteGuard, 5 | }; 6 | 7 | use core::cell::UnsafeCell; 8 | use core::fmt; 9 | use core::mem::MaybeUninit; 10 | use core::ops::{Deref, DerefMut}; 11 | use core::sync::atomic::{AtomicBool, Ordering}; 12 | 13 | pub struct LazyInit { 14 | inited: AtomicBool, 15 | data: UnsafeCell>, 16 | } 17 | 18 | unsafe impl Sync for LazyInit {} 19 | unsafe impl Send for LazyInit {} 20 | 21 | impl LazyInit { 22 | pub const fn new() -> Self { 23 | Self { 24 | inited: AtomicBool::new(false), 25 | data: UnsafeCell::new(MaybeUninit::uninit()), 26 | } 27 | } 28 | 29 | pub fn init_by(&self, data: T) { 30 | assert!(!self.is_init()); 31 | unsafe { (*self.data.get()).as_mut_ptr().write(data) }; 32 | self.inited.store(true, Ordering::Release); 33 | } 34 | 35 | pub fn is_init(&self) -> bool { 36 | self.inited.load(Ordering::Acquire) 37 | } 38 | 39 | pub fn try_get(&self) -> Option<&T> { 40 | if self.is_init() { 41 | unsafe { Some(&*(*self.data.get()).as_ptr()) } 42 | } else { 43 | None 44 | } 45 | } 46 | 47 | fn check_init(&self) { 48 | if !self.is_init() { 49 | panic!( 50 | "Use uninitialized value: {:?}", 51 | core::any::type_name::() 52 | ) 53 | } 54 | } 55 | 56 | #[inline] 57 | fn get(&self) -> &T { 58 | self.check_init(); 59 | unsafe { self.get_unchecked() } 60 | } 61 | 62 | #[inline] 63 | fn get_mut(&mut self) -> &mut T { 64 | self.check_init(); 65 | unsafe { self.get_mut_unchecked() } 66 | } 67 | 68 | /// # Safety 69 | /// 70 | /// Must be called after initialization. 71 | #[inline] 72 | pub unsafe fn get_unchecked(&self) -> &T { 73 | &*(*self.data.get()).as_ptr() 74 | } 75 | 76 | /// # Safety 77 | /// 78 | /// Must be called after initialization. 79 | #[inline] 80 | pub unsafe fn get_mut_unchecked(&mut self) -> &mut T { 81 | &mut *(*self.data.get()).as_mut_ptr() 82 | } 83 | } 84 | 85 | impl Default for LazyInit { 86 | fn default() -> Self { 87 | Self::new() 88 | } 89 | } 90 | 91 | impl fmt::Debug for LazyInit { 92 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 93 | match self.try_get() { 94 | Some(s) => write!(f, "LazyInit {{ data: ") 95 | .and_then(|()| s.fmt(f)) 96 | .and_then(|()| write!(f, "}}")), 97 | None => write!(f, "LazyInit {{ }}"), 98 | } 99 | } 100 | } 101 | 102 | impl Deref for LazyInit { 103 | type Target = T; 104 | #[inline] 105 | fn deref(&self) -> &T { 106 | self.get() 107 | } 108 | } 109 | 110 | impl DerefMut for LazyInit { 111 | #[inline] 112 | fn deref_mut(&mut self) -> &mut T { 113 | self.get_mut() 114 | } 115 | } 116 | 117 | impl Drop for LazyInit { 118 | fn drop(&mut self) { 119 | if self.is_init() { 120 | unsafe { core::ptr::drop_in_place((*self.data.get()).as_mut_ptr()) }; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /driver/general-plic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "general-plic" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | devices = { workspace = true } 10 | log = "0.4.11" 11 | -------------------------------------------------------------------------------- /driver/general-plic/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(used_with_arg)] 3 | 4 | extern crate alloc; 5 | 6 | mod plic; 7 | 8 | use alloc::sync::Arc; 9 | use devices::{ 10 | device::{DeviceType, Driver, IntDriver}, 11 | driver_define, 12 | fdt::Node, 13 | VIRT_ADDR_START, 14 | }; 15 | 16 | pub struct PLIC { 17 | base: usize, 18 | } 19 | 20 | impl Driver for PLIC { 21 | fn get_id(&self) -> &str { 22 | "riscv-plic" 23 | } 24 | 25 | fn try_handle_interrupt(&self, _irq: u32) -> bool { 26 | let claim = self.get_irq_claim(0, true); 27 | self.complete_irq_claim(0, true, claim); 28 | false 29 | } 30 | 31 | fn get_device_wrapper(self: Arc) -> DeviceType { 32 | DeviceType::INT(self.clone()) 33 | } 34 | } 35 | 36 | impl IntDriver for PLIC { 37 | fn register_irq(&self, irq: u32, driver: Arc) { 38 | log::info!("regist a interrupt {} for {}", irq, driver.get_id()); 39 | self.set_irq_enable(0, true, irq); 40 | self.set_priority(irq, 7); 41 | } 42 | } 43 | 44 | pub fn init_driver(node: &Node) -> Arc { 45 | let addr = node.find_property("reg").unwrap().raw_value()[4..8] 46 | .iter() 47 | .fold(0, |acc, x: &u8| (acc << 8) | (*x as usize)); 48 | let plic = Arc::new(PLIC { 49 | base: VIRT_ADDR_START + addr, 50 | }); 51 | plic.set_thresold(0, true, 0); 52 | // enable_external_irq(); 53 | plic 54 | } 55 | 56 | driver_define!("riscv,plic0", init_driver); 57 | -------------------------------------------------------------------------------- /driver/general-plic/src/plic.rs: -------------------------------------------------------------------------------- 1 | /* 2 | base + 0x000000: Reserved (interrupt source 0 does not exist) 3 | base + 0x000004: Interrupt source 1 priority 4 | base + 0x000008: Interrupt source 2 priority 5 | ... 6 | base + 0x000FFC: Interrupt source 1023 priority 7 | base + 0x001000: Interrupt Pending bit 0-31 8 | base + 0x00107C: Interrupt Pending bit 992-1023 9 | ... 10 | base + 0x002000: Enable bits for sources 0-31 on context 0 11 | base + 0x002004: Enable bits for sources 32-63 on context 0 12 | ... 13 | base + 0x00207C: Enable bits for sources 992-1023 on context 0 14 | base + 0x002080: Enable bits for sources 0-31 on context 1 15 | base + 0x002084: Enable bits for sources 32-63 on context 1 16 | ... 17 | base + 0x0020FC: Enable bits for sources 992-1023 on context 1 18 | base + 0x002100: Enable bits for sources 0-31 on context 2 19 | base + 0x002104: Enable bits for sources 32-63 on context 2 20 | ... 21 | base + 0x00217C: Enable bits for sources 992-1023 on context 2 22 | ... 23 | base + 0x1F1F80: Enable bits for sources 0-31 on context 15871 24 | base + 0x1F1F84: Enable bits for sources 32-63 on context 15871 25 | base + 0x1F1FFC: Enable bits for sources 992-1023 on context 15871 26 | ... 27 | base + 0x1FFFFC: Reserved 28 | base + 0x200000: Priority threshold for context 0 29 | base + 0x200004: Claim/complete for context 0 30 | base + 0x200008: Reserved 31 | ... 32 | base + 0x200FFC: Reserved 33 | base + 0x201000: Priority threshold for context 1 34 | base + 0x201004: Claim/complete for context 1 35 | ... 36 | base + 0x3FFF000: Priority threshold for context 15871 37 | base + 0x3FFF004: Claim/complete for context 15871 38 | base + 0x3FFF008: Reserved 39 | ... 40 | base + 0x3FFFFFC: Reserved 41 | */ 42 | 43 | use core::ptr::{read_volatile, write_volatile}; 44 | 45 | use crate::PLIC; 46 | 47 | impl PLIC { 48 | // enable a interrupt. 49 | pub fn set_irq_enable(&self, hart_id: u32, is_smode: bool, irq: u32) { 50 | // hart 0 M-Mode, addr: base + 0x2000, a irq in per bit. 51 | // such as enable irq 173 is base + 0x2000 + 173 /32, bit 173 % 32. 52 | unsafe { 53 | let mut addr = self.base + 0x2000 + irq as usize / 32; 54 | // if is_smode { 55 | // addr += 0x80; 56 | // } 57 | addr += (hart_id as usize * 2 + is_smode as usize) * 0x80; 58 | write_volatile( 59 | addr as *mut u32, 60 | read_volatile(addr as *const u32) | (1 << irq), 61 | ) 62 | } 63 | } 64 | 65 | pub fn get_irq_claim(&self, hart_id: u32, is_smode: bool) -> u32 { 66 | // return irq claim 67 | // addr: base + 0x20_0000 68 | let mut addr = self.base + 0x20_0004; 69 | addr += (hart_id as usize * 2 + is_smode as usize) * 0x1000; 70 | unsafe { read_volatile(addr as *const u32) } 71 | } 72 | 73 | pub fn complete_irq_claim(&self, hart_id: u32, is_smode: bool, irq: u32) { 74 | let mut addr = self.base + 0x20_0004; 75 | addr += (hart_id as usize * 2 + is_smode as usize) * 0x1000; 76 | unsafe { 77 | write_volatile(addr as *mut u32, irq); 78 | } 79 | } 80 | 81 | pub fn set_thresold(&self, hart_id: u32, is_smode: bool, thresold: u32) { 82 | let mut addr = self.base + 0x20_0000; 83 | addr += (hart_id as usize * 2 + is_smode as usize) * 0x1000; 84 | unsafe { 85 | write_volatile(addr as *mut u32, thresold); 86 | } 87 | } 88 | 89 | pub fn set_priority(&self, irq: u32, priority: u32) { 90 | // base + 4 x interruptID, value range: 0 - 7 91 | unsafe { 92 | // set priority to 7 93 | write_volatile((self.base + (irq as usize) * 4) as *mut u32, priority); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /driver/kgoldfish-rtc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kgoldfish-rtc" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | devices = { workspace = true } 10 | log = "0.4" 11 | timestamp = { git = "https://github.com/Byte-OS/timestamp.git" } 12 | -------------------------------------------------------------------------------- /driver/kgoldfish-rtc/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(used_with_arg)] 3 | 4 | extern crate alloc; 5 | 6 | #[macro_use] 7 | extern crate log; 8 | 9 | use alloc::sync::Arc; 10 | use core::ptr::read_volatile; 11 | use devices::{ 12 | device::{DeviceType, Driver, RtcDriver}, 13 | driver_define, 14 | fdt::Node, 15 | VIRT_ADDR_START, 16 | }; 17 | use timestamp::DateTime; 18 | 19 | const TIMER_TIME_LOW: usize = 0x00; 20 | const TIMER_TIME_HIGH: usize = 0x04; 21 | 22 | pub struct RtcGoldfish { 23 | base: usize, 24 | } 25 | 26 | impl Driver for RtcGoldfish { 27 | fn get_id(&self) -> &str { 28 | "rtc_goldfish" 29 | } 30 | 31 | fn get_device_wrapper(self: Arc) -> DeviceType { 32 | DeviceType::RTC(self.clone()) 33 | } 34 | } 35 | 36 | impl RtcDriver for RtcGoldfish { 37 | // read seconds since 1970-01-01 38 | fn read_timestamp(&self) -> u64 { 39 | self.read() / 1_000_000_000u64 40 | } 41 | // read value 42 | #[inline] 43 | fn read(&self) -> u64 { 44 | unsafe { 45 | let low: u32 = read_volatile((self.base + TIMER_TIME_LOW) as *const u32); 46 | let high: u32 = read_volatile((self.base + TIMER_TIME_HIGH) as *const u32); 47 | ((high as u64) << 32) | (low as u64) 48 | } 49 | } 50 | } 51 | 52 | pub fn init_rtc(node: &Node) -> Arc { 53 | let addr = node.find_property("reg").unwrap().raw_value()[4..8] 54 | .iter() 55 | .fold(0, |acc, x| (acc << 8) | (*x as usize)); 56 | let rtc = Arc::new(RtcGoldfish { 57 | base: VIRT_ADDR_START + addr, 58 | }); 59 | 60 | let date_time = DateTime::new(rtc.read_timestamp() as usize); 61 | 62 | info!("rtc device initialized."); 63 | info!( 64 | "the standard Beijing time: {} timestamp : {}", 65 | date_time, date_time.timestamp 66 | ); 67 | 68 | rtc 69 | } 70 | 71 | driver_define!("google,goldfish-rtc", init_rtc); 72 | -------------------------------------------------------------------------------- /driver/kramdisk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kramdisk" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | log = "0.4" 10 | devices = { workspace = true } 11 | -------------------------------------------------------------------------------- /driver/kramdisk/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs, path::PathBuf}; 2 | 3 | #[allow(unused_macros)] 4 | macro_rules! display { 5 | ($fmt:expr) => (println!("cargo:warning={}", format!($fmt))); 6 | ($fmt:expr, $($arg:tt)*) => (println!(concat!("cargo:warning=", $fmt), $($arg)*)); 7 | } 8 | 9 | fn main() { 10 | let out_dir = PathBuf::from(env::var("OUT_DIR").expect("can't find manifest dir")); 11 | let img_relative_path = env::var("MOUNT_IMG_PATH").unwrap_or("mount.img".into()); 12 | let project_dir = 13 | PathBuf::from(env::var("ROOT_MANIFEST_DIR").expect("can't find manifest directory")); 14 | 15 | let img_path = project_dir.join(img_relative_path); 16 | let img_path = img_path.to_str().expect("can't build a valid img path"); 17 | fs::write( 18 | out_dir.join("inc.S"), 19 | format!( 20 | ".section .data 21 | .global ramdisk_start 22 | .global ramdisk_end 23 | .p2align 12 24 | ramdisk_start: 25 | .incbin \"{img_path}\" 26 | ramdisk_end:" 27 | ), 28 | ) 29 | .expect("can't write ram file to out_dir"); 30 | 31 | // fs::write(path, contents) 32 | 33 | // write module configuration to OUT_PATH, then it will be included in the main.rs 34 | println!("cargo:rerun-if-env-changed=MOUNT_IMG_PATH"); 35 | println!("cargo:rerun-if-changed=mount.img"); 36 | println!("cargo:rerun-if-changed=build.rs"); 37 | } 38 | -------------------------------------------------------------------------------- /driver/kramdisk/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(used_with_arg)] 3 | 4 | use core::{ 5 | arch::global_asm, 6 | ptr::{slice_from_raw_parts, slice_from_raw_parts_mut}, 7 | }; 8 | 9 | extern crate alloc; 10 | 11 | use alloc::sync::Arc; 12 | use devices::{ 13 | device::{BlkDriver, DeviceType, Driver}, 14 | driver_define, 15 | }; 16 | use log::info; 17 | 18 | // 虚拟IO设备 19 | pub struct RamDiskBlock { 20 | start: usize, 21 | size: usize, 22 | } 23 | 24 | impl Driver for RamDiskBlock { 25 | fn get_id(&self) -> &str { 26 | "kramdisk" 27 | } 28 | 29 | fn get_device_wrapper(self: Arc) -> DeviceType { 30 | DeviceType::BLOCK(self.clone()) 31 | } 32 | } 33 | 34 | impl BlkDriver for RamDiskBlock { 35 | fn read_blocks(&self, sector_offset: usize, buf: &mut [u8]) { 36 | assert_eq!(buf.len() % 0x200, 0); 37 | if (sector_offset * 0x200 + buf.len()) >= self.size { 38 | panic!("can't out of ramdisk range") 39 | }; 40 | unsafe { 41 | buf.copy_from_slice( 42 | slice_from_raw_parts((self.start + sector_offset * 0x200) as *const u8, buf.len()) 43 | .as_ref() 44 | .expect("can't deref ptr in the Ramdisk"), 45 | ); 46 | } 47 | } 48 | 49 | fn write_blocks(&self, sector_idx: usize, buf: &[u8]) { 50 | assert_eq!(buf.len() % 0x200, 0); 51 | if (sector_idx * 0x200 + buf.len()) >= self.size { 52 | panic!("can't out of ramdisk range") 53 | }; 54 | unsafe { 55 | slice_from_raw_parts_mut((self.start + sector_idx * 0x200) as *mut u8, buf.len()) 56 | .as_mut() 57 | .expect("can't deref ptr in the ramdisk") 58 | .copy_from_slice(buf); 59 | } 60 | } 61 | 62 | fn capacity(&self) -> usize { 63 | self.size 64 | } 65 | } 66 | 67 | global_asm!(include_str!(concat!(env!("OUT_DIR"), "/inc.S"))); 68 | 69 | driver_define!({ 70 | extern "C" { 71 | fn ramdisk_start(); 72 | fn ramdisk_end(); 73 | } 74 | info!( 75 | "ramdisk range: {:#x} - {:#x}", 76 | ramdisk_start as usize, ramdisk_end as usize 77 | ); 78 | Some(Arc::new(RamDiskBlock { 79 | start: ramdisk_start as _, 80 | size: ramdisk_end as usize - ramdisk_start as usize, 81 | })) 82 | }); 83 | -------------------------------------------------------------------------------- /driver/kvirtio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kvirtio" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | log = "0.4" 10 | # virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "61ece50" } 11 | virtio-drivers = { version = "0.8" } 12 | devices = { workspace = true } 13 | -------------------------------------------------------------------------------- /driver/kvirtio/src/virtio_blk.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use alloc::vec::Vec; 3 | use devices::device::{BlkDriver, DeviceType, Driver}; 4 | use devices::{register_device_irqs, Mutex}; 5 | use virtio_drivers::device::blk::VirtIOBlk; 6 | use virtio_drivers::transport::Transport; 7 | 8 | use super::virtio_impl::HalImpl; 9 | 10 | pub struct VirtIOBlock { 11 | inner: Mutex>, 12 | irqs: Vec, 13 | } 14 | 15 | unsafe impl Sync for VirtIOBlock {} 16 | unsafe impl Send for VirtIOBlock {} 17 | 18 | impl Driver for VirtIOBlock { 19 | fn interrupts(&self) -> &[u32] { 20 | &self.irqs 21 | } 22 | 23 | fn get_id(&self) -> &str { 24 | "virtio-blk" 25 | } 26 | 27 | fn get_device_wrapper(self: Arc) -> DeviceType { 28 | DeviceType::BLOCK(self.clone()) 29 | } 30 | } 31 | 32 | impl BlkDriver for VirtIOBlock { 33 | fn read_blocks(&self, block_id: usize, buf: &mut [u8]) { 34 | self.inner 35 | .lock() 36 | .read_blocks(block_id, buf) 37 | .expect("can't read block by virtio block"); 38 | } 39 | 40 | fn write_blocks(&self, block_id: usize, buf: &[u8]) { 41 | self.inner 42 | .lock() 43 | .write_blocks(block_id, buf) 44 | .expect("can't write block by virtio block"); 45 | } 46 | 47 | fn capacity(&self) -> usize { 48 | self.inner.lock().capacity() as usize * 0x200 49 | } 50 | } 51 | 52 | pub fn init(transport: T, irqs: Vec) -> Arc { 53 | let blk_device = Arc::new(VirtIOBlock { 54 | inner: Mutex::new( 55 | VirtIOBlk::::new(transport).expect("failed to create blk driver"), 56 | ), 57 | irqs, 58 | }); 59 | 60 | register_device_irqs(blk_device.clone()); 61 | info!("Initailize virtio-block device"); 62 | blk_device 63 | } 64 | -------------------------------------------------------------------------------- /driver/kvirtio/src/virtio_impl.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use core::ptr::NonNull; 3 | use devices::{frame_alloc_much, utils::virt_to_phys, FrameTracker, Mutex, VIRT_ADDR_START}; 4 | use log::trace; 5 | use virtio_drivers::{BufferDirection, Hal, PhysAddr}; 6 | 7 | static VIRTIO_CONTAINER: Mutex> = Mutex::new(Vec::new()); 8 | 9 | pub struct HalImpl; 10 | 11 | unsafe impl Hal for HalImpl { 12 | fn dma_alloc(pages: usize, _direction: BufferDirection) -> (PhysAddr, NonNull) { 13 | let trackers = frame_alloc_much(pages).expect("can't alloc page in virtio"); 14 | let paddr = trackers[0].0; 15 | let vaddr = NonNull::new(paddr.get_mut_ptr()).unwrap(); 16 | trace!("alloc DMA: paddr={:#x}, pages={:?}", paddr.raw(), trackers); 17 | VIRTIO_CONTAINER.lock().extend(trackers); 18 | (paddr.raw(), vaddr) 19 | } 20 | 21 | unsafe fn dma_dealloc(paddr: PhysAddr, _vaddr: NonNull, pages: usize) -> i32 { 22 | trace!("dealloc DMA: paddr={:#x}, pages={}", paddr, pages); 23 | // VIRTIO_CONTAINER.lock().drain_filter(|x| { 24 | // let phy_page = paddr as usize >> 12; 25 | // let calc_page = usize::from(x.0); 26 | 27 | // calc_page >= phy_page && calc_page - phy_page < pages 28 | // }); 29 | VIRTIO_CONTAINER.lock().retain(|x| { 30 | let phy_page = paddr >> 12; 31 | let calc_page = x.0.raw(); 32 | 33 | !(phy_page..phy_page + pages).contains(&calc_page) 34 | }); 35 | 0 36 | } 37 | 38 | unsafe fn mmio_phys_to_virt(paddr: PhysAddr, _size: usize) -> NonNull { 39 | NonNull::new((paddr | VIRT_ADDR_START) as *mut u8).unwrap() 40 | } 41 | 42 | unsafe fn share(buffer: NonNull<[u8]>, _direction: BufferDirection) -> PhysAddr { 43 | let raw_ptr = buffer.as_ptr() as *mut u8 as usize; 44 | // Nothing to do, as the host already has access to all memory. 45 | virt_to_phys(raw_ptr).unwrap_or(raw_ptr & (!VIRT_ADDR_START)) 46 | // buffer.as_ptr() as *mut u8 as usize - VIRT_ADDR_START 47 | } 48 | 49 | unsafe fn unshare(_paddr: PhysAddr, _buffer: NonNull<[u8]>, _direction: BufferDirection) { 50 | // Nothing to do, as the host already has access to all memory and we didn't copy the buffer 51 | // anywhere else. 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /driver/kvirtio/src/virtio_input.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use alloc::vec::Vec; 3 | use devices::device::{DeviceType, Driver, InputDriver}; 4 | use devices::{register_device_irqs, Mutex}; 5 | use virtio_drivers::device::input::VirtIOInput as VirtIOInputWrapper; 6 | use virtio_drivers::transport::mmio::MmioTransport; 7 | 8 | use super::virtio_impl::HalImpl; 9 | 10 | pub struct VirtIOInput { 11 | _inner: Mutex>, 12 | interrupts: Vec, 13 | } 14 | 15 | unsafe impl Sync for VirtIOInput {} 16 | unsafe impl Send for VirtIOInput {} 17 | 18 | impl Driver for VirtIOInput { 19 | fn get_id(&self) -> &str { 20 | "virtio-input" 21 | } 22 | 23 | fn interrupts(&self) -> &[u32] { 24 | &self.interrupts 25 | } 26 | 27 | fn get_device_wrapper(self: Arc) -> DeviceType { 28 | DeviceType::INPUT(self.clone()) 29 | } 30 | } 31 | 32 | impl InputDriver for VirtIOInput { 33 | fn read_event(&self) -> u64 { 34 | todo!() 35 | } 36 | 37 | fn handle_irq(&self) { 38 | todo!() 39 | } 40 | 41 | fn is_empty(&self) -> bool { 42 | todo!() 43 | } 44 | } 45 | 46 | pub fn init(transport: MmioTransport, irqs: Vec) -> Arc { 47 | let input_device = Arc::new(VirtIOInput { 48 | _inner: Mutex::new( 49 | VirtIOInputWrapper::::new(transport) 50 | .expect("failed to create blk driver"), 51 | ), 52 | interrupts: irqs, 53 | }); 54 | register_device_irqs(input_device.clone()); 55 | info!("Initailize virtio-iput device"); 56 | input_device 57 | } 58 | -------------------------------------------------------------------------------- /driver/kvirtio/src/virtio_net.rs: -------------------------------------------------------------------------------- 1 | use core::cmp; 2 | 3 | use alloc::sync::Arc; 4 | use alloc::vec::Vec; 5 | use devices::device::{DeviceType, Driver, NetDriver, NetError}; 6 | use devices::{register_device_irqs, Mutex}; 7 | use virtio_drivers::device::net::{self, TxBuffer}; 8 | use virtio_drivers::transport::Transport; 9 | 10 | use super::virtio_impl::HalImpl; 11 | 12 | #[allow(dead_code)] 13 | pub struct VirtIONet { 14 | inner: Mutex>, 15 | irqs: Vec, 16 | } 17 | 18 | unsafe impl Sync for VirtIONet {} 19 | unsafe impl Send for VirtIONet {} 20 | 21 | impl Driver for VirtIONet { 22 | fn get_id(&self) -> &str { 23 | "virtio-blk" 24 | } 25 | 26 | fn get_device_wrapper(self: Arc) -> DeviceType { 27 | DeviceType::NET(self.clone()) 28 | } 29 | } 30 | 31 | impl NetDriver for VirtIONet { 32 | fn recv(&self, buf: &mut [u8]) -> Result { 33 | let packet = self.inner.lock().receive().map_err(|_| NetError::NoData)?; 34 | let rlen = cmp::min(buf.len(), packet.packet_len()); 35 | buf[..rlen].copy_from_slice(&packet.packet()[..rlen]); 36 | self.inner 37 | .lock() 38 | .recycle_rx_buffer(packet) 39 | .expect("can't receive data"); 40 | Ok(rlen) 41 | } 42 | 43 | fn send(&self, buf: &[u8]) -> Result<(), NetError> { 44 | self.inner 45 | .lock() 46 | .send(TxBuffer::from(buf)) 47 | .expect("can't send data"); 48 | Ok(()) 49 | } 50 | } 51 | 52 | pub fn init(transport: T, irqs: Vec) -> Arc { 53 | info!("Initailize virtio-net device, irqs: {:?}", irqs); 54 | let net_device = Arc::new(VirtIONet { 55 | inner: Mutex::new( 56 | net::VirtIONet::::new(transport, 2048) 57 | .expect("failed to create blk driver"), 58 | ), 59 | irqs, 60 | }); 61 | register_device_irqs(net_device.clone()); 62 | net_device 63 | } 64 | -------------------------------------------------------------------------------- /driver/ns16550a/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ns16550a" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ns16550a = "0.2" 10 | log = "0.4" 11 | devices = { workspace = true } 12 | -------------------------------------------------------------------------------- /driver/ns16550a/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(used_with_arg)] 3 | 4 | extern crate alloc; 5 | 6 | use alloc::{sync::Arc, vec::Vec}; 7 | use devices::{ 8 | device::{DeviceType, Driver, UartDriver}, 9 | driver_define, 10 | fdt::Node, 11 | node_to_interrupts, register_device_irqs, VIRT_ADDR_START, 12 | }; 13 | use log::info; 14 | use ns16550a::{ 15 | Break, DMAMode, Divisor, ParityBit, ParitySelect, StickParity, StopBits, Uart, WordLength, 16 | }; 17 | 18 | pub struct NS16550a { 19 | _base: usize, 20 | inner: Uart, 21 | irqs: Vec, 22 | } 23 | 24 | impl Driver for NS16550a { 25 | fn get_id(&self) -> &str { 26 | "ns16550a" 27 | } 28 | 29 | fn get_device_wrapper(self: Arc) -> DeviceType { 30 | DeviceType::UART(self.clone()) 31 | } 32 | 33 | fn try_handle_interrupt(&self, _irq: u32) -> bool { 34 | info!("handle uart interrupt"); 35 | false 36 | } 37 | 38 | fn interrupts(&self) -> &[u32] { 39 | &self.irqs 40 | } 41 | } 42 | 43 | impl UartDriver for NS16550a { 44 | fn put(&self, c: u8) { 45 | self.inner.put(c) 46 | } 47 | 48 | fn get(&self) -> Option { 49 | self.inner.get() 50 | } 51 | } 52 | 53 | fn init_driver(node: &Node) -> Arc { 54 | let addr = node.reg().unwrap().next().unwrap().address as usize; 55 | 56 | info!( 57 | "node interrupts: {:?}", 58 | node.interrupts().unwrap().flatten().collect::>() 59 | ); 60 | let uart = Arc::new(NS16550a { 61 | _base: VIRT_ADDR_START + addr, 62 | inner: Uart::new(VIRT_ADDR_START + addr), 63 | irqs: node_to_interrupts(node), 64 | }); 65 | register_device_irqs(uart.clone()); 66 | uart.inner.init( 67 | WordLength::EIGHT, 68 | StopBits::ONE, 69 | ParityBit::DISABLE, 70 | ParitySelect::EVEN, 71 | StickParity::DISABLE, 72 | Break::DISABLE, 73 | DMAMode::MODE0, 74 | Divisor::BAUD1200, 75 | ); 76 | uart 77 | } 78 | 79 | driver_define!("ns16550a", init_driver); 80 | -------------------------------------------------------------------------------- /filesystem/devfs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "devfs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | vfscore = { workspace = true } 10 | devices = { workspace = true } 11 | log = "0.4" 12 | sync = { workspace = true } 13 | bitflags = "2.0.2" 14 | syscalls = { workspace = true } 15 | libc-types = { workspace = true } 16 | -------------------------------------------------------------------------------- /filesystem/devfs/src/cpu_dma_latency.rs: -------------------------------------------------------------------------------- 1 | use libc_types::types::{Stat, StatMode}; 2 | use vfscore::{INodeInterface, VfsResult}; 3 | 4 | pub struct CpuDmaLatency; 5 | 6 | impl INodeInterface for CpuDmaLatency { 7 | fn readat(&self, _offset: usize, buffer: &mut [u8]) -> VfsResult { 8 | buffer.fill(0); 9 | if buffer.len() > 1 { 10 | buffer[0] = 1; 11 | } 12 | Ok(buffer.len()) 13 | } 14 | 15 | fn writeat(&self, _offset: usize, buffer: &[u8]) -> VfsResult { 16 | Ok(buffer.len()) 17 | } 18 | 19 | fn stat(&self, stat: &mut Stat) -> VfsResult<()> { 20 | stat.dev = 0; 21 | stat.ino = 1; // TODO: convert path to number(ino) 22 | stat.mode = StatMode::CHAR; // TODO: add access mode 23 | stat.nlink = 1; 24 | stat.uid = 1000; 25 | stat.gid = 1000; 26 | stat.size = 0; 27 | stat.blksize = 512; 28 | stat.blocks = 0; 29 | stat.rdev = 0; // TODO: add device id 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /filesystem/devfs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | extern crate alloc; 4 | extern crate log; 5 | 6 | use alloc::{collections::BTreeMap, string::ToString, sync::Arc, vec::Vec}; 7 | use libc_types::types::{Stat, StatMode}; 8 | use syscalls::Errno; 9 | use vfscore::{DirEntry, FileSystem, FileType, INodeInterface, VfsResult}; 10 | 11 | mod cpu_dma_latency; 12 | mod null; 13 | mod rtc; 14 | mod sdx; 15 | mod shm; 16 | mod tty; 17 | mod urandom; 18 | mod zero; 19 | 20 | pub use {sdx::Sdx, tty::Tty}; 21 | 22 | pub struct DevFS { 23 | root_dir: Arc, 24 | } 25 | 26 | impl DevFS { 27 | pub fn new() -> Arc { 28 | Arc::new(Self { 29 | root_dir: Arc::new(DevDir::new()), 30 | }) 31 | } 32 | 33 | pub fn new_with_dir(dev: DevDir) -> Arc { 34 | Arc::new(Self { 35 | root_dir: Arc::new(dev), 36 | }) 37 | } 38 | } 39 | 40 | impl FileSystem for DevFS { 41 | fn root_dir(&self) -> Arc { 42 | Arc::new(DevDirContainer { 43 | inner: self.root_dir.clone(), 44 | }) 45 | } 46 | 47 | fn name(&self) -> &str { 48 | "devfs" 49 | } 50 | } 51 | 52 | pub struct DevDir { 53 | map: BTreeMap<&'static str, Arc>, 54 | } 55 | 56 | pub struct DevDirContainer { 57 | inner: Arc, 58 | } 59 | 60 | impl DevDir { 61 | pub fn new() -> Self { 62 | let mut map: BTreeMap<&'static str, Arc> = BTreeMap::new(); 63 | map.insert("stdout", Arc::new(Tty::new())); 64 | map.insert("stderr", Arc::new(Tty::new())); 65 | map.insert("stdin", Arc::new(Tty::new())); 66 | map.insert("ttyv0", Arc::new(Tty::new())); 67 | map.insert("null", Arc::new(null::Null)); 68 | map.insert("zero", Arc::new(zero::Zero)); 69 | map.insert("shm", Arc::new(shm::Shm)); 70 | map.insert("rtc", Arc::new(rtc::Rtc)); 71 | map.insert("urandom", Arc::new(urandom::Urandom)); 72 | map.insert("cpu_dma_latency", Arc::new(cpu_dma_latency::CpuDmaLatency)); 73 | // map.insert("tty", Arc::new(stdout::Stdout)); 74 | 75 | Self { map } 76 | } 77 | 78 | pub fn add(&mut self, path: &'static str, node: Arc) { 79 | self.map.insert(path, node); 80 | } 81 | } 82 | 83 | impl Default for DevDir { 84 | fn default() -> Self { 85 | Self::new() 86 | } 87 | } 88 | 89 | impl INodeInterface for DevDirContainer { 90 | fn lookup(&self, name: &str) -> VfsResult> { 91 | self.inner.map.get(name).cloned().ok_or(Errno::ENOENT) 92 | } 93 | 94 | fn read_dir(&self) -> VfsResult> { 95 | Ok(self 96 | .inner 97 | .map 98 | .keys() 99 | .map(|name| DirEntry { 100 | filename: name.to_string(), 101 | len: 0, 102 | file_type: FileType::Device, 103 | }) 104 | .collect()) 105 | } 106 | 107 | fn stat(&self, stat: &mut Stat) -> VfsResult<()> { 108 | stat.dev = 0; 109 | stat.ino = 1; // TODO: convert path to number(ino) 110 | stat.mode = StatMode::DIR; // TODO: add access mode 111 | stat.nlink = 1; 112 | stat.uid = 0; 113 | stat.gid = 0; 114 | stat.size = 0; 115 | stat.blksize = 512; 116 | stat.blocks = 0; 117 | stat.rdev = 0; // TODO: add device id 118 | Ok(()) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /filesystem/devfs/src/null.rs: -------------------------------------------------------------------------------- 1 | use libc_types::types::{Stat, StatMode}; 2 | use vfscore::{INodeInterface, VfsResult}; 3 | 4 | pub struct Null; 5 | 6 | impl INodeInterface for Null { 7 | fn readat(&self, _offset: usize, buffer: &mut [u8]) -> VfsResult { 8 | buffer.fill(0); 9 | Ok(buffer.len()) 10 | } 11 | 12 | fn writeat(&self, _offset: usize, buffer: &[u8]) -> VfsResult { 13 | Ok(buffer.len()) 14 | } 15 | 16 | fn stat(&self, stat: &mut Stat) -> VfsResult<()> { 17 | stat.dev = 0; 18 | stat.ino = 1; // TODO: convert path to number(ino) 19 | stat.mode = StatMode::CHAR; // TODO: add access mode 20 | stat.nlink = 1; 21 | stat.uid = 1000; 22 | stat.gid = 1000; 23 | stat.size = 0; 24 | stat.blksize = 512; 25 | stat.blocks = 0; 26 | stat.rdev = 0; // TODO: add device id 27 | Ok(()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /filesystem/devfs/src/rtc.rs: -------------------------------------------------------------------------------- 1 | use libc_types::types::{Stat, StatMode}; 2 | use vfscore::{INodeInterface, VfsResult}; 3 | 4 | pub struct Rtc; 5 | 6 | pub struct RtcTime { 7 | sec: u32, 8 | min: u32, 9 | hour: u32, 10 | mday: u32, 11 | mon: u32, 12 | year: u32, 13 | _wday: u32, // unused 14 | _yday: u32, // unused 15 | _isdst: u32, // unused 16 | } 17 | 18 | impl INodeInterface for Rtc { 19 | fn stat(&self, stat: &mut Stat) -> vfscore::VfsResult<()> { 20 | stat.dev = 0; 21 | stat.ino = 1; // TODO: convert path to number(ino) 22 | stat.mode = StatMode::CHAR; // TODO: add access mode 23 | stat.nlink = 1; 24 | stat.uid = 1000; 25 | stat.gid = 1000; 26 | stat.size = 0; 27 | stat.blksize = 512; 28 | stat.blocks = 0; 29 | stat.rdev = 0; // TODO: add device id 30 | Ok(()) 31 | } 32 | 33 | fn ioctl(&self, _command: usize, arg: usize) -> VfsResult { 34 | let rtc_time = unsafe { (arg as *mut RtcTime).as_mut().unwrap() }; 35 | rtc_time.sec = 0; 36 | rtc_time.min = 0; 37 | rtc_time.hour = 0; 38 | rtc_time.mday = 0; 39 | rtc_time.mon = 0; 40 | rtc_time.year = 0; 41 | Ok(0) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /filesystem/devfs/src/sdx.rs: -------------------------------------------------------------------------------- 1 | use alloc::{string::String, vec::Vec}; 2 | use libc_types::types::{Stat, StatMode}; 3 | use sync::Mutex; 4 | use syscalls::Errno; 5 | use vfscore::INodeInterface; 6 | 7 | pub struct Sdx { 8 | device_id: usize, 9 | mount_fn: fn(usize, &str) -> Result<(), Errno>, 10 | umount_fn: fn(usize, &str) -> Result<(), Errno>, 11 | mount_paths: Mutex>, 12 | } 13 | 14 | impl Sdx { 15 | pub fn new( 16 | device_id: usize, 17 | mount_fn: fn(usize, &str) -> Result<(), Errno>, 18 | umount_fn: fn(usize, &str) -> Result<(), Errno>, 19 | ) -> Self { 20 | Self { 21 | device_id, 22 | mount_fn, 23 | umount_fn, 24 | mount_paths: Mutex::new(Vec::new()), 25 | } 26 | } 27 | } 28 | 29 | impl INodeInterface for Sdx { 30 | fn mount(&self, path: &str) -> vfscore::VfsResult<()> { 31 | let f = self.mount_fn; 32 | self.mount_paths.lock().push(String::from(path)); 33 | f(self.device_id, path) 34 | } 35 | 36 | fn umount(&self) -> vfscore::VfsResult<()> { 37 | let f = self.umount_fn; 38 | let path = self.mount_paths.lock().pop(); 39 | match path { 40 | Some(path) => f(self.device_id, &path), 41 | None => todo!(), 42 | } 43 | } 44 | 45 | fn stat(&self, stat: &mut Stat) -> vfscore::VfsResult<()> { 46 | stat.dev = 0; 47 | stat.ino = 1; // TODO: convert path to number(ino) 48 | stat.mode = StatMode::CHAR; // TODO: add access mode 49 | stat.nlink = 1; 50 | stat.uid = 1000; 51 | stat.gid = 1000; 52 | stat.size = 0; 53 | stat.blksize = 512; 54 | stat.blocks = 0; 55 | stat.rdev = 0; // TODO: add device id 56 | Ok(()) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /filesystem/devfs/src/shm.rs: -------------------------------------------------------------------------------- 1 | use libc_types::types::{Stat, StatMode}; 2 | use vfscore::INodeInterface; 3 | 4 | pub struct Shm; 5 | 6 | impl INodeInterface for Shm { 7 | fn stat(&self, stat: &mut Stat) -> vfscore::VfsResult<()> { 8 | stat.dev = 0; 9 | stat.ino = 1; // TODO: convert path to number(ino) 10 | stat.mode = StatMode::DIR; // TODO: add access mode 11 | stat.nlink = 1; 12 | stat.uid = 1000; 13 | stat.gid = 1000; 14 | stat.size = 0; 15 | stat.blksize = 512; 16 | stat.blocks = 0; 17 | stat.rdev = 0; // TODO: add device id 18 | Ok(()) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /filesystem/devfs/src/tty.rs: -------------------------------------------------------------------------------- 1 | use core::cmp; 2 | 3 | use alloc::collections::VecDeque; 4 | use devices::utils::{get_char, puts}; 5 | use libc_types::{ 6 | ioctl::TermIoctlCmd, 7 | poll::PollEvent, 8 | termios::Termios, 9 | types::{Stat, StatMode, WinSize}, 10 | }; 11 | use sync::Mutex; 12 | use syscalls::Errno; 13 | use vfscore::{INodeInterface, VfsResult}; 14 | pub struct Tty { 15 | buffer: Mutex>, 16 | termios: Mutex, 17 | pgid: Mutex, 18 | winsize: Mutex, 19 | } 20 | 21 | impl Tty { 22 | pub fn new() -> Tty { 23 | Tty { 24 | buffer: Mutex::new(VecDeque::new()), 25 | termios: Default::default(), 26 | pgid: Default::default(), 27 | winsize: Mutex::new(WinSize { 28 | row: 24, 29 | col: 140, 30 | xpixel: 0, 31 | ypixel: 0, 32 | }), 33 | } 34 | } 35 | } 36 | 37 | impl Default for Tty { 38 | fn default() -> Self { 39 | Self::new() 40 | } 41 | } 42 | 43 | impl INodeInterface for Tty { 44 | fn readat(&self, _offset: usize, buffer: &mut [u8]) -> vfscore::VfsResult { 45 | assert!(!buffer.is_empty()); 46 | let mut self_buffer = self.buffer.lock(); 47 | if self_buffer.is_empty() { 48 | if let Some(c) = get_char() { 49 | buffer[0] = c; 50 | Ok(1) 51 | } else { 52 | Err(Errno::EWOULDBLOCK) 53 | } 54 | } else { 55 | let rlen = cmp::min(buffer.len(), self_buffer.len()); 56 | (0..rlen).for_each(|i| buffer[i] = self_buffer.pop_front().unwrap()); 57 | Ok(rlen) 58 | } 59 | } 60 | 61 | fn stat(&self, stat: &mut Stat) -> VfsResult<()> { 62 | stat.dev = 1; 63 | stat.ino = 1; // TODO: convert path to number(ino) 64 | stat.mode = StatMode::CHAR; // TODO: add access mode 65 | stat.nlink = 1; 66 | stat.uid = 1000; 67 | stat.gid = 1000; 68 | stat.size = 15; 69 | stat.blksize = 512; 70 | stat.blocks = 0; 71 | stat.rdev = 0; // TODO: add device id 72 | Ok(()) 73 | } 74 | 75 | fn writeat(&self, _offset: usize, buffer: &[u8]) -> vfscore::VfsResult { 76 | puts(buffer); 77 | Ok(buffer.len()) 78 | } 79 | 80 | fn poll(&self, events: PollEvent) -> VfsResult { 81 | let mut res = PollEvent::NONE; 82 | if events.contains(PollEvent::IN) { 83 | let buf_len = self.buffer.lock().len(); 84 | if buf_len > 0 { 85 | res |= PollEvent::IN; 86 | } else if let Some(c) = get_char() { 87 | res |= PollEvent::IN; 88 | self.buffer.lock().push_back(c); 89 | } 90 | } 91 | if events.contains(PollEvent::OUT) { 92 | res |= PollEvent::OUT; 93 | } 94 | Ok(res) 95 | } 96 | 97 | fn ioctl(&self, command: usize, arg: usize) -> VfsResult { 98 | let cmd = TermIoctlCmd::try_from(command as u32).map_err(|_| Errno::EINVAL)?; 99 | match cmd { 100 | TermIoctlCmd::TCGETS | TermIoctlCmd::TCGETA => { 101 | unsafe { 102 | (arg as *mut Termios).write_volatile(*self.termios.lock()); 103 | } 104 | Ok(0) 105 | } 106 | TermIoctlCmd::TCSETS | TermIoctlCmd::TCSETSW | TermIoctlCmd::TCSETSF => { 107 | // copy_from_user(token, argp as *const Termios, &mut inner.termios); 108 | unsafe { *self.termios.lock() = *(arg as *mut Termios).as_mut().unwrap() } 109 | Ok(0) 110 | } 111 | TermIoctlCmd::TIOCGPGRP => match unsafe { (arg as *mut u32).as_mut() } { 112 | Some(pgid) => { 113 | *pgid = *self.pgid.lock(); 114 | Ok(0) 115 | } 116 | None => Err(Errno::EINVAL), 117 | }, 118 | TermIoctlCmd::TIOCSPGRP => match unsafe { (arg as *mut u32).as_mut() } { 119 | Some(pgid) => { 120 | *self.pgid.lock() = *pgid; 121 | Ok(0) 122 | } 123 | None => Err(Errno::EINVAL), 124 | }, 125 | TermIoctlCmd::TIOCGWINSZ => { 126 | unsafe { 127 | *(arg as *mut WinSize).as_mut().unwrap() = *self.winsize.lock(); 128 | } 129 | Ok(0) 130 | } 131 | TermIoctlCmd::TIOCSWINSZ => { 132 | unsafe { 133 | *self.winsize.lock() = *(arg as *mut WinSize).as_mut().unwrap(); 134 | } 135 | Ok(0) 136 | } 137 | _ => Err(Errno::EPERM), 138 | } 139 | // Err(VfsError::NotSupported) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /filesystem/devfs/src/urandom.rs: -------------------------------------------------------------------------------- 1 | use libc_types::{ 2 | poll::PollEvent, 3 | types::{Stat, StatMode}, 4 | }; 5 | use vfscore::{INodeInterface, VfsResult}; 6 | 7 | pub struct Urandom; 8 | 9 | impl INodeInterface for Urandom { 10 | fn readat(&self, _offset: usize, buffer: &mut [u8]) -> VfsResult { 11 | buffer.fill(0); 12 | Ok(buffer.len()) 13 | } 14 | 15 | fn writeat(&self, _offset: usize, buffer: &[u8]) -> VfsResult { 16 | Ok(buffer.len()) 17 | } 18 | 19 | fn stat(&self, stat: &mut Stat) -> VfsResult<()> { 20 | stat.dev = 0; 21 | stat.ino = 1; // TODO: convert path to number(ino) 22 | stat.mode = StatMode::CHAR; // TODO: add access mode 23 | stat.nlink = 1; 24 | stat.uid = 1000; 25 | stat.gid = 1000; 26 | stat.size = 0; 27 | stat.blksize = 512; 28 | stat.blocks = 0; 29 | stat.rdev = 0; // TODO: add device id 30 | Ok(()) 31 | } 32 | 33 | fn poll(&self, events: PollEvent) -> VfsResult { 34 | let mut res = PollEvent::NONE; 35 | if events.contains(PollEvent::IN) { 36 | res |= PollEvent::IN; 37 | } 38 | Ok(res) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /filesystem/devfs/src/zero.rs: -------------------------------------------------------------------------------- 1 | use libc_types::types::{Stat, StatMode}; 2 | use vfscore::{INodeInterface, VfsResult}; 3 | 4 | pub struct Zero; 5 | 6 | impl INodeInterface for Zero { 7 | fn readat(&self, _offset: usize, buffer: &mut [u8]) -> VfsResult { 8 | buffer.fill(0); 9 | Ok(buffer.len()) 10 | } 11 | 12 | fn writeat(&self, _offset: usize, buffer: &[u8]) -> VfsResult { 13 | Ok(buffer.len()) 14 | } 15 | 16 | fn stat(&self, stat: &mut Stat) -> VfsResult<()> { 17 | stat.dev = 0; 18 | stat.ino = 1; // TODO: convert path to number(ino) 19 | stat.mode = StatMode::CHAR; // TODO: add access mode 20 | stat.nlink = 1; 21 | stat.uid = 1000; 22 | stat.gid = 1000; 23 | stat.size = 0; 24 | stat.blksize = 512; 25 | stat.blocks = 0; 26 | stat.rdev = 0; // TODO: add device id 27 | Ok(()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /filesystem/ext4fs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ext4fs" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | log = "0.4" 8 | vfscore = { workspace = true } 9 | syscalls = { workspace = true } 10 | sync = { workspace = true } 11 | devices = { workspace = true } 12 | libc-types = { workspace = true } 13 | lwext4_rust = { git = "https://github.com/Azure-stars/lwext4_rust.git", default-features = false, rev = "ee5131c" } 14 | -------------------------------------------------------------------------------- /filesystem/ext4rsfs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ext4rsfs" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | log = "0.4" 8 | vfscore = { workspace = true } 9 | syscalls = { workspace = true } 10 | sync = { workspace = true } 11 | devices = { workspace = true } 12 | libc-types = { workspace = true } 13 | ext4_rs = "1.3.1" 14 | -------------------------------------------------------------------------------- /filesystem/fs/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /filesystem/fs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | log = "0.4" 10 | bitflags = "2.0.2" 11 | devfs = { workspace = true } 12 | procfs = { workspace = true } 13 | ramfs = { workspace = true } 14 | vfscore = { workspace = true } 15 | syscalls = { workspace = true } 16 | sync = { workspace = true } 17 | devices = { workspace = true } 18 | libc-types = { workspace = true } 19 | 20 | [target.'cfg(root_fs = "ext4_rs")'.dependencies] 21 | ext4rsfs = { path = "../ext4rsfs" } 22 | 23 | [target.'cfg(root_fs = "ext4")'.dependencies] 24 | ext4fs = { path = "../ext4fs" } 25 | 26 | [target.'cfg(root_fs = "fat32")'.dependencies.fatfs] 27 | git = "https://github.com/byte-os/rust-fatfs.git" 28 | # rev = "a3a834e" 29 | default-features = false 30 | features = ["alloc", "lfn", "unicode"] 31 | -------------------------------------------------------------------------------- /filesystem/fs/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!(r#"cargo::rustc-check-cfg=cfg(root_fs, values("fat32", "ext4", "ext4_rs"))"#); 3 | println!("cargo:rerun-if-env-changed=CARGO_CFG_ROOT_FS"); 4 | } 5 | -------------------------------------------------------------------------------- /filesystem/fs/src/dentry.rs: -------------------------------------------------------------------------------- 1 | use alloc::{sync::Arc, vec::Vec}; 2 | use libc_types::fcntl::OpenFlags; 3 | use sync::Mutex; 4 | use vfscore::{FileSystem, INodeInterface, VfsResult}; 5 | 6 | use crate::{file::File, pathbuf::PathBuf}; 7 | 8 | pub static MOUNTED_FS: Mutex> = Mutex::new(Vec::new()); 9 | 10 | #[derive(Clone)] 11 | pub struct DEntryNode { 12 | pub fs: Arc, 13 | node: Arc, 14 | } 15 | 16 | impl DEntryNode { 17 | #[inline] 18 | pub fn node(&self) -> Arc { 19 | self.node.clone() 20 | } 21 | } 22 | 23 | /// 获取挂载的文件系统和挂载后的路径 24 | /// 25 | /// # Arguments 26 | /// 27 | /// - `path` 需要搜索的路径 28 | /// 29 | /// # Returns 30 | /// 31 | /// - [DEntryNode] `path` 对应挂载的文件系统 32 | /// - [PathBuf] `path` 减去挂载路径后的路径 33 | /// 34 | pub fn get_mounted(path: &PathBuf) -> (DEntryNode, PathBuf) { 35 | let mounted = MOUNTED_FS.lock(); 36 | let finded = mounted 37 | .iter() 38 | .rev() 39 | .find(|x| path.starts_with(&x.0)) 40 | .unwrap(); 41 | (finded.1.clone(), path.trim_start(&finded.0)) 42 | } 43 | 44 | /// 挂载文件系统 45 | /// 46 | /// # Arguments 47 | /// 48 | /// - `fs` 需要挂载的文件系统 49 | /// - `path` 文件系统挂载的路径 50 | pub fn mount_fs(fs: Arc, path: &str) { 51 | if path != "/" { 52 | // 在挂载之前创建对应的文件夹 53 | let _ = File::open(path, OpenFlags::DIRECTORY | OpenFlags::CREAT); 54 | } 55 | let path = PathBuf::from(path); 56 | info!("SYSTEM FS mount {} @ {}", fs.name(), path); 57 | let node = fs.root_dir(); 58 | MOUNTED_FS.lock().push((path, DEntryNode { fs, node })); 59 | } 60 | 61 | /// 取消挂载文件系统 62 | /// 63 | /// # Arguments 64 | /// 65 | /// - `path` 需要取消挂载的路径 66 | pub fn umount(path: PathBuf) -> VfsResult<()> { 67 | MOUNTED_FS.lock().retain(|x| x.0 != path); 68 | Ok(()) 69 | } 70 | -------------------------------------------------------------------------------- /filesystem/fs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(let_chains)] 3 | 4 | #[allow(unused_imports)] 5 | #[macro_use] 6 | extern crate alloc; 7 | #[macro_use] 8 | extern crate log; 9 | extern crate bitflags; 10 | 11 | pub mod dentry; 12 | #[cfg(root_fs = "fat32")] 13 | mod fatfs_shim; 14 | pub mod file; 15 | pub mod pathbuf; 16 | pub mod pipe; 17 | 18 | use alloc::sync::Arc; 19 | use core::{ 20 | future::Future, 21 | pin::Pin, 22 | task::{Context, Poll}, 23 | }; 24 | use dentry::mount_fs; 25 | use devfs::{DevDir, DevFS}; 26 | use devices::get_blk_devices; 27 | use procfs::ProcFS; 28 | use ramfs::RamFs; 29 | use syscalls::Errno; 30 | use vfscore::VfsResult; 31 | pub use vfscore::{FileType, INodeInterface, SeekFrom}; 32 | 33 | pub fn build_devfs() -> Arc { 34 | let dev_dir = DevDir::new(); 35 | 36 | DevFS::new_with_dir(dev_dir) 37 | } 38 | 39 | pub fn init() { 40 | info!("fs module initialized"); 41 | // TODO: Identify the filesystem at the device. 42 | if !get_blk_devices().is_empty() { 43 | #[cfg(root_fs = "fat32")] 44 | mount_fs(fatfs_shim::Fat32FileSystem::new(0), "/"); 45 | #[cfg(root_fs = "ext4")] 46 | mount_fs(ext4fs::Ext4FileSystem::new(0), "/"); 47 | #[cfg(root_fs = "ext4_rs")] 48 | mount_fs(ext4rsfs::Ext4FileSystem::new(0), "/"); 49 | } else { 50 | mount_fs(RamFs::new(), "/"); 51 | } 52 | mount_fs(build_devfs(), "/dev"); 53 | mount_fs(RamFs::new(), "/tmp"); 54 | mount_fs(RamFs::new(), "/dev/shm"); 55 | mount_fs(RamFs::new(), "/home"); 56 | mount_fs(RamFs::new(), "/var"); 57 | mount_fs(ProcFS::new(), "/proc"); 58 | // filesystems.push((RamFs::new(), "/bin")); 59 | } 60 | 61 | pub struct WaitBlockingRead<'a>(pub Arc, pub &'a mut [u8], pub usize); 62 | 63 | impl Future for WaitBlockingRead<'_> { 64 | type Output = VfsResult; 65 | 66 | fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { 67 | let offset = self.2; 68 | let file = self.0.clone(); 69 | let buffer = &mut self.1; 70 | match file.readat(offset, buffer) { 71 | Ok(rsize) => Poll::Ready(Ok(rsize)), 72 | Err(err) => { 73 | if let Errno::EWOULDBLOCK = err { 74 | Poll::Pending 75 | } else { 76 | Poll::Ready(Err(err)) 77 | } 78 | } 79 | } 80 | } 81 | } 82 | 83 | pub struct WaitBlockingWrite<'a>(pub Arc, pub &'a [u8], pub usize); 84 | 85 | impl Future for WaitBlockingWrite<'_> { 86 | type Output = VfsResult; 87 | 88 | fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { 89 | let offset = self.2; 90 | let file = self.0.clone(); 91 | let buffer = &self.1; 92 | 93 | match file.writeat(offset, buffer) { 94 | Ok(wsize) => Poll::Ready(Ok(wsize)), 95 | Err(err) => { 96 | if let Errno::EWOULDBLOCK = err { 97 | Poll::Pending 98 | } else { 99 | Poll::Ready(Err(err)) 100 | } 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /filesystem/fs/src/pathbuf.rs: -------------------------------------------------------------------------------- 1 | use alloc::{borrow::ToOwned, string::String, vec::Vec}; 2 | use core::fmt::Display; 3 | 4 | #[derive(PartialEq, Eq, Clone)] 5 | pub struct PathBuf(Vec); 6 | 7 | impl Display for PathBuf { 8 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 9 | f.write_fmt(format_args!("/{}", self.0.join("/"))) 10 | } 11 | } 12 | 13 | impl From for String { 14 | fn from(value: PathBuf) -> Self { 15 | value.path() 16 | } 17 | } 18 | 19 | impl From for PathBuf { 20 | fn from(value: String) -> Self { 21 | Self::empty().join(&value) 22 | } 23 | } 24 | 25 | impl From<&str> for PathBuf { 26 | fn from(value: &str) -> Self { 27 | Self::empty().join(value) 28 | } 29 | } 30 | 31 | impl PathBuf { 32 | pub const fn empty() -> Self { 33 | Self(Vec::new()) 34 | } 35 | 36 | pub fn join(&self, path: &str) -> Self { 37 | let mut pb = if path.starts_with("/") { 38 | PathBuf::empty() 39 | } else { 40 | self.clone() 41 | }; 42 | path.split("/").for_each(|x| match x { 43 | "." | "" => {} 44 | ".." => { 45 | pb.0.pop(); 46 | } 47 | name => pb.0.push(name.to_owned()), 48 | }); 49 | 50 | pb 51 | } 52 | 53 | pub fn path(&self) -> String { 54 | String::from("/") + &self.0.join("/") 55 | } 56 | 57 | #[inline] 58 | pub fn filename(&self) -> String { 59 | self.0.last().cloned().unwrap_or(String::from("/")) 60 | } 61 | 62 | #[inline] 63 | pub fn dir(&self) -> PathBuf { 64 | let mut paths = self.clone(); 65 | paths.0.pop(); 66 | paths 67 | } 68 | 69 | #[inline] 70 | pub fn levels(&self) -> usize { 71 | self.0.len() 72 | } 73 | 74 | pub fn starts_with(&self, target: &PathBuf) -> bool { 75 | self.0.starts_with(&target.0) 76 | } 77 | 78 | pub fn trim_start(&self, target: &PathBuf) -> Self { 79 | let mut pb = self.clone(); 80 | if pb.starts_with(target) { 81 | pb.0.drain(..target.0.len()); 82 | } 83 | pb 84 | } 85 | 86 | #[inline] 87 | pub fn iter(&self) -> impl Iterator { 88 | self.0.iter() 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /filesystem/fs/src/pipe.rs: -------------------------------------------------------------------------------- 1 | use core::cmp; 2 | 3 | use alloc::{ 4 | collections::VecDeque, 5 | sync::{Arc, Weak}, 6 | }; 7 | use libc_types::{ 8 | poll::PollEvent, 9 | types::{Stat, StatMode}, 10 | }; 11 | use sync::Mutex; 12 | use syscalls::Errno; 13 | use vfscore::{INodeInterface, VfsResult}; 14 | 15 | pub struct PipeSender(Arc>>); 16 | 17 | impl INodeInterface for PipeSender { 18 | fn writeat(&self, _offset: usize, buffer: &[u8]) -> VfsResult { 19 | log::warn!("write pipe:"); 20 | let mut queue = self.0.lock(); 21 | if queue.len() > 0x50000 { 22 | Err(Errno::EWOULDBLOCK) 23 | } else { 24 | let wlen = buffer.len(); 25 | queue.extend(buffer.iter()); 26 | Ok(wlen) 27 | } 28 | } 29 | 30 | fn poll(&self, events: PollEvent) -> VfsResult { 31 | let mut res = PollEvent::NONE; 32 | if events.contains(PollEvent::OUT) && self.0.lock().len() <= 0x50000 { 33 | res |= PollEvent::OUT; 34 | } 35 | Ok(res) 36 | } 37 | 38 | fn stat(&self, stat: &mut Stat) -> VfsResult<()> { 39 | stat.mode = StatMode::FIFO; 40 | Ok(()) 41 | } 42 | } 43 | 44 | // pipe reader, just can read. 45 | pub struct PipeReceiver { 46 | queue: Arc>>, 47 | sender: Weak, 48 | } 49 | 50 | impl INodeInterface for PipeReceiver { 51 | fn readat(&self, _offset: usize, buffer: &mut [u8]) -> VfsResult { 52 | let mut queue = self.queue.lock(); 53 | let rlen = cmp::min(queue.len(), buffer.len()); 54 | queue 55 | .drain(..rlen) 56 | .zip(buffer.iter_mut()) 57 | .for_each(|(src, dst)| *dst = src); 58 | if rlen == 0 && Weak::strong_count(&self.sender) > 0 { 59 | Err(Errno::EWOULDBLOCK) 60 | } else { 61 | Ok(rlen) 62 | } 63 | } 64 | 65 | fn poll(&self, events: PollEvent) -> VfsResult { 66 | let mut res = PollEvent::NONE; 67 | if events.contains(PollEvent::IN) { 68 | if !self.queue.lock().is_empty() { 69 | res |= PollEvent::IN; 70 | } else if Weak::strong_count(&self.sender) == 0 { 71 | res |= PollEvent::ERR; 72 | } 73 | } 74 | if events.contains(PollEvent::ERR) 75 | && self.queue.lock().is_empty() 76 | && Weak::strong_count(&self.sender) == 0 77 | { 78 | res |= PollEvent::ERR; 79 | } 80 | Ok(res) 81 | } 82 | 83 | fn stat(&self, stat: &mut Stat) -> VfsResult<()> { 84 | stat.mode = StatMode::FIFO; 85 | Ok(()) 86 | } 87 | } 88 | 89 | pub fn create_pipe() -> (Arc, Arc) { 90 | let queue = Arc::new(Mutex::new(VecDeque::new())); 91 | let sender = Arc::new(PipeSender(queue.clone())); 92 | ( 93 | Arc::new(PipeReceiver { 94 | queue: queue.clone(), 95 | sender: Arc::downgrade(&sender), 96 | }), 97 | sender, 98 | ) 99 | } 100 | -------------------------------------------------------------------------------- /filesystem/procfs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "procfs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | vfscore = { workspace = true } 10 | sync = { workspace = true } 11 | log = "0.4" 12 | syscalls = { workspace = true } 13 | libc-types = { workspace = true } 14 | -------------------------------------------------------------------------------- /filesystem/procfs/src/interrupts.rs: -------------------------------------------------------------------------------- 1 | use core::cmp; 2 | 3 | use alloc::string::String; 4 | use libc_types::types::{Stat, StatMode}; 5 | use vfscore::{INodeInterface, VfsResult}; 6 | 7 | pub struct Interrupts {} 8 | 9 | impl Interrupts { 10 | pub const fn new() -> Self { 11 | Self {} 12 | } 13 | } 14 | 15 | impl INodeInterface for Interrupts { 16 | fn readat(&self, offset: usize, buffer: &mut [u8]) -> VfsResult { 17 | let str = String::new(); 18 | // FIXME: Use new interrupts method to record this. 19 | // for (irq, times) in get_int_records().iter().enumerate() { 20 | // if *times == 0 { 21 | // continue; 22 | // } 23 | // str += &format!("{}: {}\r\n", irq, *times); 24 | // } 25 | let bytes = str.as_bytes(); 26 | let rsize = cmp::min(bytes.len() - offset, buffer.len()); 27 | buffer[..rsize].copy_from_slice(&bytes[offset..offset + rsize]); 28 | Ok(rsize) 29 | } 30 | 31 | fn stat(&self, stat: &mut Stat) -> vfscore::VfsResult<()> { 32 | stat.dev = 0; 33 | stat.ino = 1; // TODO: convert path to number(ino) 34 | stat.mode = StatMode::CHAR; // TODO: add access mode 35 | stat.nlink = 1; 36 | stat.uid = 1000; 37 | stat.gid = 1000; 38 | stat.size = 0; 39 | stat.blksize = 512; 40 | stat.blocks = 0; 41 | stat.rdev = 0; // TODO: add device id 42 | Ok(()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /filesystem/procfs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | extern crate alloc; 4 | 5 | mod interrupts; 6 | mod meminfo; 7 | mod mounts; 8 | 9 | use alloc::{collections::BTreeMap, string::ToString, sync::Arc, vec::Vec}; 10 | use interrupts::Interrupts; 11 | use libc_types::types::{Stat, StatMode}; 12 | use meminfo::MemInfo; 13 | use mounts::Mounts; 14 | use syscalls::Errno; 15 | use vfscore::{DirEntry, FileSystem, FileType, INodeInterface, VfsResult}; 16 | 17 | pub struct ProcFS { 18 | root: Arc, 19 | } 20 | 21 | impl ProcFS { 22 | pub fn new() -> Arc { 23 | Arc::new(Self { 24 | root: ProcDir::new(), 25 | }) 26 | } 27 | } 28 | 29 | impl FileSystem for ProcFS { 30 | fn root_dir(&self) -> Arc { 31 | Arc::new(DevDirContainer { 32 | inner: self.root.clone(), 33 | }) 34 | } 35 | 36 | fn name(&self) -> &str { 37 | "procfs" 38 | } 39 | } 40 | 41 | pub struct ProcDir { 42 | map: BTreeMap<&'static str, Arc>, 43 | } 44 | 45 | impl ProcDir { 46 | pub fn new() -> Arc { 47 | let mut map: BTreeMap<&str, Arc> = BTreeMap::new(); 48 | map.insert("mounts", Arc::new(Mounts::new())); 49 | map.insert("meminfo", Arc::new(MemInfo::new())); 50 | map.insert("interrupts", Arc::new(Interrupts::new())); 51 | Arc::new(ProcDir { map }) 52 | } 53 | } 54 | 55 | pub struct DevDirContainer { 56 | inner: Arc, 57 | } 58 | 59 | impl INodeInterface for DevDirContainer { 60 | fn lookup(&self, name: &str) -> VfsResult> { 61 | self.inner.map.get(name).cloned().ok_or(Errno::ENOENT) 62 | } 63 | 64 | fn read_dir(&self) -> VfsResult> { 65 | Ok(self 66 | .inner 67 | .map 68 | .keys() 69 | .map(|name| DirEntry { 70 | filename: name.to_string(), 71 | len: 0, 72 | file_type: FileType::Device, 73 | }) 74 | .collect()) 75 | } 76 | 77 | fn stat(&self, stat: &mut Stat) -> VfsResult<()> { 78 | stat.dev = 0; 79 | stat.ino = 1; // TODO: convert path to number(ino) 80 | stat.mode = StatMode::DIR; // TODO: add access mode 81 | stat.nlink = 1; 82 | stat.uid = 0; 83 | stat.gid = 0; 84 | stat.size = 0; 85 | stat.blksize = 512; 86 | stat.blocks = 0; 87 | stat.rdev = 0; // TODO: add device id 88 | Ok(()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /filesystem/procfs/src/meminfo.rs: -------------------------------------------------------------------------------- 1 | use libc_types::types::{Stat, StatMode}; 2 | use vfscore::{INodeInterface, VfsResult}; 3 | 4 | pub struct MemInfo {} 5 | 6 | impl MemInfo { 7 | pub const fn new() -> Self { 8 | Self {} 9 | } 10 | } 11 | 12 | impl INodeInterface for MemInfo { 13 | fn readat(&self, _offset: usize, _buffer: &mut [u8]) -> VfsResult { 14 | Ok(0) 15 | } 16 | 17 | fn stat(&self, stat: &mut Stat) -> vfscore::VfsResult<()> { 18 | stat.dev = 0; 19 | stat.ino = 1; // TODO: convert path to number(ino) 20 | stat.mode = StatMode::CHAR; // TODO: add access mode 21 | stat.nlink = 1; 22 | stat.uid = 1000; 23 | stat.gid = 1000; 24 | stat.size = 0; 25 | stat.blksize = 512; 26 | stat.blocks = 0; 27 | stat.rdev = 0; // TODO: add device id 28 | Ok(()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /filesystem/procfs/src/mounts.rs: -------------------------------------------------------------------------------- 1 | use libc_types::types::{Stat, StatMode}; 2 | use vfscore::{INodeInterface, VfsResult}; 3 | 4 | pub struct Mounts {} 5 | 6 | impl Mounts { 7 | pub const fn new() -> Self { 8 | Self {} 9 | } 10 | } 11 | 12 | impl INodeInterface for Mounts { 13 | fn readat(&self, _offset: usize, _buffer: &mut [u8]) -> VfsResult { 14 | Ok(0) 15 | } 16 | 17 | fn stat(&self, stat: &mut Stat) -> vfscore::VfsResult<()> { 18 | stat.dev = 0; 19 | stat.ino = 1; // TODO: convert path to number(ino) 20 | stat.mode = StatMode::CHAR; // TODO: add access mode 21 | stat.nlink = 1; 22 | stat.uid = 1000; 23 | stat.gid = 1000; 24 | stat.size = 0; 25 | stat.blksize = 512; 26 | stat.blocks = 0; 27 | stat.rdev = 0; // TODO: add device id 28 | Ok(()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /filesystem/ramfs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ramfs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | vfscore = { workspace = true } 10 | log = "0.4" 11 | sync = { workspace = true } 12 | polyhal = { workspace = true } 13 | runtime = { workspace = true } 14 | syscalls = { workspace = true } 15 | libc-types = { workspace = true } 16 | -------------------------------------------------------------------------------- /filesystem/vfscore/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vfscore" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | bitflags = "2.0.2" 10 | downcast-rs = { version = "2.0.1", default-features = false, features = [ 11 | "sync", 12 | ] } 13 | syscalls = { workspace = true } 14 | libc-types = { workspace = true } 15 | -------------------------------------------------------------------------------- /filesystem/vfscore/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | extern crate alloc; 3 | 4 | extern crate bitflags; 5 | 6 | use alloc::string::String; 7 | use alloc::sync::Arc; 8 | use alloc::vec::Vec; 9 | use downcast_rs::{impl_downcast, DowncastSync}; 10 | use libc_types::{ 11 | poll::PollEvent, 12 | types::{Stat, StatFS, StatMode, TimeSpec}, 13 | }; 14 | use syscalls::Errno; 15 | 16 | #[derive(Debug, Copy, Clone, PartialEq)] 17 | pub enum FileType { 18 | File, 19 | Directory, 20 | Device, 21 | Socket, 22 | Link, 23 | } 24 | 25 | impl From for FileType { 26 | fn from(value: StatMode) -> Self { 27 | match value.intersection(StatMode::TYPE_MASK) { 28 | StatMode::SOCKET => FileType::Socket, 29 | StatMode::LINK => FileType::Link, 30 | StatMode::FILE => FileType::File, 31 | StatMode::BLOCK => FileType::Device, 32 | StatMode::DIR => FileType::Directory, 33 | StatMode::CHAR => FileType::Device, 34 | StatMode::FIFO => FileType::Device, 35 | _ => unreachable!(), 36 | } 37 | } 38 | } 39 | 40 | #[derive(Debug, Copy, Clone)] 41 | pub enum SeekFrom { 42 | SET(usize), 43 | CURRENT(isize), 44 | END(isize), 45 | } 46 | 47 | pub struct DirEntry { 48 | pub filename: String, 49 | pub len: usize, 50 | pub file_type: FileType, 51 | } 52 | 53 | pub trait FileSystem: Send + Sync { 54 | fn root_dir(&self) -> Arc; 55 | fn name(&self) -> &str; 56 | fn flush(&self) -> VfsResult<()> { 57 | Ok(()) 58 | } 59 | } 60 | 61 | pub type VfsResult = core::result::Result; 62 | 63 | pub trait BlockDevice: Send + Sync { 64 | fn read_block(&self, block: usize, buffer: &mut [u8]) -> VfsResult; 65 | fn write_block(&self, block: usize, buffer: &[u8]) -> VfsResult; 66 | fn capacity(&self) -> VfsResult; 67 | } 68 | 69 | pub trait INodeInterface: DowncastSync + Send + Sync { 70 | fn readat(&self, _offset: usize, _buffer: &mut [u8]) -> VfsResult { 71 | Err(Errno::ENOENT) 72 | } 73 | 74 | fn writeat(&self, _offset: usize, _buffer: &[u8]) -> VfsResult { 75 | Err(Errno::ENOENT) 76 | } 77 | 78 | fn create(&self, _name: &str, _ty: FileType) -> VfsResult<()> { 79 | Err(Errno::ENOTDIR) 80 | } 81 | 82 | fn mkdir(&self, _name: &str) -> VfsResult<()> { 83 | Err(Errno::EEXIST) 84 | } 85 | 86 | fn rmdir(&self, _name: &str) -> VfsResult<()> { 87 | Err(Errno::ENOENT) 88 | } 89 | 90 | fn remove(&self, _name: &str) -> VfsResult<()> { 91 | Err(Errno::ENOENT) 92 | } 93 | 94 | fn read_dir(&self) -> VfsResult> { 95 | Err(Errno::EPERM) 96 | } 97 | 98 | fn lookup(&self, _name: &str) -> VfsResult> { 99 | Err(Errno::ENOENT) 100 | } 101 | 102 | fn ioctl(&self, _command: usize, _arg: usize) -> VfsResult { 103 | Err(Errno::EPERM) 104 | } 105 | 106 | fn truncate(&self, _size: usize) -> VfsResult<()> { 107 | Err(Errno::EPERM) 108 | } 109 | 110 | fn flush(&self) -> VfsResult<()> { 111 | Err(Errno::EPERM) 112 | } 113 | 114 | fn resolve_link(&self) -> VfsResult { 115 | Err(Errno::EPERM) 116 | } 117 | 118 | fn link(&self, _name: &str, _src: Arc) -> VfsResult<()> { 119 | Err(Errno::EPERM) 120 | } 121 | 122 | fn symlink(&self, _name: &str, _src: &str) -> VfsResult<()> { 123 | Err(Errno::EPERM) 124 | } 125 | 126 | fn unlink(&self, _name: &str) -> VfsResult<()> { 127 | Err(Errno::EPERM) 128 | } 129 | 130 | fn stat(&self, _stat: &mut Stat) -> VfsResult<()> { 131 | Err(Errno::EPERM) 132 | } 133 | 134 | fn mount(&self, _path: &str) -> VfsResult<()> { 135 | Err(Errno::EPERM) 136 | } 137 | 138 | fn umount(&self) -> VfsResult<()> { 139 | Err(Errno::EPERM) 140 | } 141 | 142 | fn statfs(&self, _statfs: &mut StatFS) -> VfsResult<()> { 143 | Err(Errno::EPERM) 144 | } 145 | 146 | fn utimes(&self, _times: &mut [TimeSpec]) -> VfsResult<()> { 147 | Err(Errno::EPERM) 148 | } 149 | 150 | fn poll(&self, _events: PollEvent) -> VfsResult { 151 | Err(Errno::EPERM) 152 | } 153 | } 154 | 155 | impl_downcast!(sync INodeInterface); 156 | -------------------------------------------------------------------------------- /kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kernel" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [features] 8 | net = [] 9 | 10 | [build-dependencies] 11 | toml = "0.5.2" 12 | serde = "1.0.136" 13 | serde_derive = "1.0.136" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | devices = { workspace = true } 18 | executor = { workspace = true } 19 | polyhal = { workspace = true } 20 | polyhal-boot = { workspace = true } 21 | polyhal-trap = { workspace = true } 22 | libc-types = { workspace = true } 23 | xmas-elf = "0.9.0" 24 | async-recursion = "1.1.1" 25 | sync = { workspace = true } 26 | bit_field = "0.10.1" 27 | lose-net-stack = { git = "https://github.com/byte-os/lose-net-stack", rev = "bb99460", features = [ 28 | "log", 29 | ] } 30 | futures-lite = { version = "1.13.0", default-features = false, features = [ 31 | "alloc", 32 | ] } 33 | hashbrown = "0.14" 34 | 35 | syscalls = { workspace = true } 36 | runtime = { workspace = true } 37 | 38 | # filesystem 39 | fs = { workspace = true } 40 | vfscore = { workspace = true } 41 | 42 | # drivers 43 | kvirtio = { workspace = true } 44 | kgoldfish-rtc = { workspace = true } 45 | kramdisk = { workspace = true } 46 | general-plic = { workspace = true } 47 | ns16550a = { workspace = true } 48 | -------------------------------------------------------------------------------- /kernel/build.rs: -------------------------------------------------------------------------------- 1 | use std::io::Result; 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | #[allow(unused_macros)] 5 | macro_rules! display { 6 | ($fmt:expr) => (println!("cargo:warning={}", format!($fmt))); 7 | ($fmt:expr, $($arg:tt)*) => (println!(concat!("cargo:warning=", $fmt), $($arg)*)); 8 | } 9 | 10 | // write module config to file. 11 | fn write_module_config(driver_list: Vec) { 12 | let manifest_path = PathBuf::from(env::var("OUT_DIR").expect("can't find manifest dir")); 13 | let mut module_file_content = String::new(); 14 | driver_list.into_iter().for_each(|module| { 15 | if module.is_empty() { 16 | return; 17 | }; 18 | module_file_content.push_str(&format!("extern crate {};\n", module.replace("-", "_"))) 19 | }); 20 | fs::write(manifest_path.join("drivers.rs"), module_file_content) 21 | .expect("can't write file to manifest dir"); 22 | } 23 | 24 | fn main() { 25 | let drivers = std::env::var("CARGO_CFG_DRIVER") 26 | .unwrap_or(String::from("")) 27 | .split(",") 28 | .map(|x| x.trim().to_owned()) 29 | .collect(); 30 | 31 | // write module configuration to OUT_PATH, then it will be included in the main.rs 32 | write_module_config(drivers); 33 | gen_linker_script(&env::var("BOARD").expect("can't find board")) 34 | .expect("can't generate linker script"); 35 | println!("cargo:rerun-if-env-changed=CARGO_CFG_TARGET_ARCH"); 36 | println!("cargo:rerun-if-env-changed=CARGO_CFG_KERNEL_BASE"); 37 | println!("cargo:rerun-if-env-changed=BOARD"); 38 | println!("cargo:rerun-if-env-changed=CARGO_CFG_DRIVER"); 39 | println!("cargo:rerun-if-changed=build.rs"); 40 | println!("cargo:rerun-if-changed=linker.lds.S"); 41 | } 42 | 43 | fn gen_linker_script(platform: &str) -> Result<()> { 44 | let arch = env::var("CARGO_CFG_TARGET_ARCH").expect("can't find target"); 45 | let board = env::var("BOARD").unwrap_or("qemu".to_string()); 46 | let fname = format!("linker_{}_{}.lds", arch, platform); 47 | let (output_arch, kernel_base) = if arch == "x86_64" { 48 | ("i386:x86-64", "0xffff800000200000") 49 | } else if arch.contains("riscv64") { 50 | ("riscv", "0xffffffc080200000") // OUTPUT_ARCH of both riscv32/riscv64 is "riscv" 51 | } else if arch.contains("aarch64") { 52 | // ("aarch64", "0x40080000") 53 | // ("aarch64", "0xffff000040080000") 54 | ("aarch64", "0xffff000040080000") 55 | } else if arch.contains("loongarch64") { 56 | match board.as_str() { 57 | "2k1000" => ("loongarch64", "0x9000000098000000"), 58 | _ => ("loongarch64", "0x9000000080000000"), 59 | } 60 | } else { 61 | (arch.as_str(), "0") 62 | }; 63 | let ld_content = std::fs::read_to_string("linker.lds.S")?; 64 | let ld_content = ld_content.replace("%ARCH%", output_arch); 65 | let ld_content = ld_content.replace("%KERNEL_BASE%", kernel_base); 66 | let ld_content = ld_content.replace("%SMP%", "4"); 67 | 68 | std::fs::write(&fname, ld_content)?; 69 | println!("cargo:rustc-link-arg=-Tkernel/{}", fname); 70 | println!("cargo:rerun-if-env-changed=CARGO_CFG_KERNEL_BASE"); 71 | Ok(()) 72 | } 73 | -------------------------------------------------------------------------------- /kernel/linker.lds.S: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(%ARCH%) 2 | ENTRY(_start) 3 | 4 | BASE_ADDRESS = %KERNEL_BASE%; 5 | 6 | SECTIONS 7 | { 8 | . = BASE_ADDRESS; 9 | _skernel = .; 10 | 11 | .text ALIGN(4K): { 12 | *(.multiboot .multiboot.*) 13 | stext = .; 14 | *(.text.entry) 15 | *(.text .text.*) 16 | etext = .; 17 | } 18 | 19 | .rodata ALIGN(4K): { 20 | srodata = .; 21 | *(.rodata .rodata.*) 22 | erodata = .; 23 | } 24 | 25 | .got : { 26 | *(.got .got.*) 27 | } 28 | 29 | .data ALIGN(4K): { 30 | _sdata = .; 31 | . = ALIGN(4K); 32 | *(.data.boot_page_table) 33 | 34 | *(.data .data.*) 35 | *(.sdata .sdata.*) 36 | _edata = .; 37 | } 38 | 39 | .bss ALIGN(4K): { 40 | _load_end = .; 41 | *(.bss.bstack .bss.bstack.*) 42 | _sbss = .; 43 | *(.bss .bss.*) 44 | *(.sbss .sbss.*) 45 | _ebss = .; 46 | } 47 | 48 | PROVIDE(_end = .); 49 | /DISCARD/ : { 50 | *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) 51 | } 52 | } -------------------------------------------------------------------------------- /kernel/src/banner.txt: -------------------------------------------------------------------------------- 1 | 2 | ____ _ ____ _____ 3 | | _ \ | | / __ \ / ____| 4 | | |_) |_ _| |_ ___| | | | (___ 5 | | _ <| | | | __/ _ \ | | |\___ \ 6 | | |_) | |_| | || __/ |__| |____) | 7 | |____/ \__, |\__\___|\____/|_____/ 8 | __/ | 9 | |___/ 10 | -------------------------------------------------------------------------------- /kernel/src/consts.rs: -------------------------------------------------------------------------------- 1 | use fs::pathbuf::PathBuf; 2 | 3 | /// 默认的用户程序入口函数 4 | pub const USER_WORK_DIR: PathBuf = PathBuf::empty(); 5 | 6 | /// 用户态动态链接用户程序的偏移 7 | pub const USER_DYN_ADDR: usize = 0x20000000; 8 | 9 | /// 用户态栈顶 10 | pub const USER_STACK_TOP: usize = 0x8000_0000; 11 | 12 | /// 用户栈初始大小 13 | pub const USER_STACK_INIT_SIZE: usize = 0x20000; 14 | -------------------------------------------------------------------------------- /kernel/src/logging.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Write}; 2 | use devices::utils::puts; 3 | 4 | pub struct Logger; 5 | 6 | impl Write for Logger { 7 | fn write_str(&mut self, s: &str) -> fmt::Result { 8 | let mut buffer = [0u8; 4]; 9 | for c in s.chars() { 10 | puts(c.encode_utf8(&mut buffer).as_bytes()) 11 | } 12 | Ok(()) 13 | } 14 | } 15 | 16 | #[macro_export] 17 | macro_rules! print { 18 | ($($arg:tt)*) => ({ 19 | $crate::logging::print(format_args!($($arg)*)); 20 | }); 21 | } 22 | 23 | #[inline] 24 | pub fn print(args: fmt::Arguments) { 25 | Logger 26 | .write_fmt(args) 27 | .expect("can't write string in logging module."); 28 | } 29 | -------------------------------------------------------------------------------- /kernel/src/panic.rs: -------------------------------------------------------------------------------- 1 | // use backtrace::backtrace; 2 | use core::panic::PanicInfo; 3 | 4 | use polyhal::instruction::shutdown; 5 | 6 | #[inline] 7 | fn hart_id() -> usize { 8 | 0 9 | } 10 | 11 | // 程序遇到错误 12 | #[panic_handler] 13 | fn panic_handler(info: &PanicInfo) -> ! { 14 | if let Some(location) = info.location() { 15 | println!( 16 | "\x1b[1;31m[Core {}] [{}:{}]\x1b[0m", 17 | hart_id(), 18 | location.file(), 19 | location.line(), 20 | ); 21 | } 22 | println!( 23 | "\x1b[1;31m[Core {}] panic: '{}'\x1b[0m", 24 | hart_id(), 25 | info.message() 26 | ); 27 | // backtrace(); 28 | println!("!TEST FINISH!"); 29 | // loop {} 30 | 31 | shutdown(); 32 | } 33 | -------------------------------------------------------------------------------- /kernel/src/syscall/shm.rs: -------------------------------------------------------------------------------- 1 | use super::SysResult; 2 | use crate::tasks::{MapedSharedMemory, SharedMemory, SHARED_MEMORY}; 3 | use crate::user::UserTaskContainer; 4 | use alloc::{sync::Arc, vec::Vec}; 5 | use devices::PAGE_SIZE; 6 | use log::debug; 7 | use polyhal::{va, MappingFlags}; 8 | use runtime::frame::{frame_alloc_much, FrameTracker}; 9 | use syscalls::Errno; 10 | 11 | impl UserTaskContainer { 12 | pub fn sys_shmget(&self, mut key: usize, size: usize, shmflg: usize) -> SysResult { 13 | debug!( 14 | "sys_shmget @ key: {}, size: {}, shmflg: {:#o}", 15 | key, size, shmflg 16 | ); 17 | if key == 0 { 18 | key = SHARED_MEMORY.lock().keys().cloned().max().unwrap_or(0) + 1; 19 | } 20 | let mem = SHARED_MEMORY.lock().get(&key).cloned(); 21 | if mem.is_some() { 22 | return Ok(key); 23 | } 24 | if shmflg & 01000 > 0 { 25 | let shm: Vec> = frame_alloc_much(size.div_ceil(PAGE_SIZE)) 26 | .expect("can't alloc page in shm") 27 | .into_iter() 28 | .map(Arc::new) 29 | .collect(); 30 | SHARED_MEMORY 31 | .lock() 32 | .insert(key, Arc::new(SharedMemory::new(shm))); 33 | return Ok(key); 34 | } 35 | Err(Errno::ENOENT) 36 | } 37 | 38 | pub fn sys_shmat(&self, shmid: usize, shmaddr: usize, shmflg: usize) -> SysResult { 39 | debug!( 40 | "sys_shmat @ shmid: {}, shmaddr: {}, shmflg: {:#o}", 41 | shmid, shmaddr, shmflg 42 | ); 43 | let vaddr = self.task.get_last_free_addr(); 44 | 45 | let vaddr = if shmaddr == 0 { 46 | if vaddr >= va!(0x4000_0000) { 47 | vaddr 48 | } else { 49 | va!(0x4000_0000) 50 | } 51 | } else { 52 | va!(shmaddr) 53 | }; 54 | let trackers = SHARED_MEMORY.lock().get(&shmid).cloned(); 55 | if trackers.is_none() { 56 | return Err(Errno::ENOENT); 57 | } 58 | trackers 59 | .as_ref() 60 | .unwrap() 61 | .trackers 62 | .iter() 63 | .enumerate() 64 | .for_each(|(i, x)| { 65 | debug!("map {:?} @ {:?}", vaddr.raw() + i * PAGE_SIZE, x.0); 66 | self.task 67 | .map(x.0, vaddr + i * PAGE_SIZE, MappingFlags::URWX); 68 | }); 69 | let size = trackers.as_ref().unwrap().trackers.len() * PAGE_SIZE; 70 | self.task.pcb.lock().shms.push(MapedSharedMemory { 71 | key: shmid, 72 | mem: trackers.unwrap(), 73 | start: vaddr.raw(), 74 | size, 75 | }); 76 | Ok(vaddr.raw()) 77 | } 78 | 79 | pub fn sys_shmctl(&self, shmid: usize, cmd: usize, arg: usize) -> SysResult { 80 | debug!("sys_shmctl @ shmid: {}, cmd: {}, arg: {}", shmid, cmd, arg); 81 | 82 | if cmd == 0 { 83 | // SHARED_MEMORY.lock().remove(&shmid); 84 | if let Some(map) = SHARED_MEMORY.lock().get_mut(&shmid) { 85 | *map.deleted.lock() = true; 86 | } 87 | return Ok(0); 88 | } 89 | Err(Errno::EPERM) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /kernel/src/syscall/signal.rs: -------------------------------------------------------------------------------- 1 | use super::SysResult; 2 | use crate::{tasks::WaitSignal, user::UserTaskContainer, utils::useref::UserRef}; 3 | use executor::yield_now; 4 | use libc_types::{ 5 | internal::SigAction, 6 | signal::SignalNum, 7 | types::{SigMaskHow, SigSet}, 8 | }; 9 | use log::debug; 10 | use syscalls::Errno; 11 | 12 | /* 13 | * 忽略信号:不采取任何操作、有两个信号不能被忽略:SIGKILL和SIGSTOP。 14 | * 注:SIGKILL和SIGSTOP这两个信号是不会被捕捉、阻塞、忽略的。 15 | * 捕获并处理信号:内核中断正在执行的代码,转去执行信号的处理函数。 16 | * 执行默认操作:默认操作通常是终止进程,这取决于被发送的信号。 17 | * 18 | * 19 | * //struct sigaction 类型用来描述对信号的处理,定义如下: 20 | * 21 | * struct sigaction 22 | * { 23 | * void (*sa_handler)(int); 24 | * void (*sa_sigaction)(int, siginfo_t *, void *); 25 | * sigset_t sa_mask; 26 | * int sa_flags; 27 | * void (*sa_restorer)(void); 28 | * }; 29 | * 在这个结构体中, 30 | * 成员 sa_handler 是一个函数指针,包含一个信号处理函数的地址,与signal函数类似 31 | * 成员sa_sigaction 则是另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。 32 | * int iSignNum : 传入的信号 33 | * //siginfo_t *pSignInfo : 该信号相关的一些信息,他是一个结构体原型如下 34 | * siginfo_t { 35 | * int si_signo; /* 信号值,对所有信号有意义 */ 36 | * int si_errno; /* errno 值,对所有信号有意义 */ 37 | * int si_code; /* 信号产生的原因,对所有信号有意义 */ 38 | * int si_trapno; /* Trap number that caused 39 | * hardware-generated signal 40 | * (unused on most architectures) */ 41 | * pid_t si_pid; /* 发送信号的进程ID */ 42 | * uid_t si_uid; /* 发送信号进程的真实用户ID */ 43 | * int si_status; /* 对出状态,对SIGCHLD 有意义 */ 44 | * clock_t si_utime; /* 用户消耗的时间,对SIGCHLD有意义 */ 45 | * clock_t si_stime; /* 内核消耗的时间,对SIGCHLD有意义 */ 46 | * sigval_t si_value; /* 信号值,对所有实时有意义,是一个联合数据结构,可以为一个整数(由si_int标示,也可以为一个指针,由si_ptr标示) */ 47 | * int si_int; /* POSIX.1b signal */ 48 | * void *si_ptr; /* POSIX.1b signal */ 49 | * int si_overrun; /* Timer overrun count; POSIX.1b timers */ 50 | * int si_timerid; /* Timer ID; POSIX.1b timers */ 51 | * void *si_addr; /* 触发fault的内存地址,对SIGILL,SIGFPE,SIGSEGV,SIGBUS 信号有意义 */ 52 | * long si_band; /* 对SIGPOLL信号有意义 */ 53 | * int si_fd; /* 对SIGPOLL信号有意义 */ 54 | * short si_addr_lsb; /* Least significant bit of address 55 | * (since kernel 2.6.32) */ 56 | * } 57 | */ 58 | 59 | impl UserTaskContainer { 60 | /// TODO: finish sigtimedwait 61 | pub async fn sys_sigtimedwait(&self) -> SysResult { 62 | debug!("sys_sigtimedwait @ "); 63 | WaitSignal(self.task.clone()).await; 64 | // let task = current_user_task(); 65 | // task.inner_map(|x| x.signal.has_signal()); 66 | // Err(LinuxError::EAGAIN) 67 | Ok(0) 68 | } 69 | 70 | pub fn sys_sigprocmask( 71 | &self, 72 | how: u8, 73 | set: UserRef, 74 | oldset: UserRef, 75 | ) -> SysResult { 76 | debug!( 77 | "[task {}] sys_sigprocmask @ how: {:#x}, set: {}, oldset: {}", 78 | self.tid, how, set, oldset 79 | ); 80 | let how = SigMaskHow::try_from(how).map_err(|_| Errno::EINVAL)?; 81 | let mut tcb = self.task.tcb.write(); 82 | if oldset.is_valid() { 83 | oldset.write(tcb.sigmask); 84 | } 85 | if set.is_valid() { 86 | tcb.sigmask.handle(how, &set.read()) 87 | } 88 | drop(tcb); 89 | // Err(LinuxError::EPERM) 90 | Ok(0) 91 | } 92 | 93 | /// 其次,每个线程都有自己独立的signal mask,但所有线程共享进程的signal action。这意味着, 94 | /// 你可以在线程中调用pthread_sigmask(不是sigmask)来决定本线程阻塞哪些信号。 95 | /// 但你不能调用sigaction来指定单个线程的信号处理方式。如果在某个线程中调用了sigaction处理某个信号, 96 | /// 那么这个进程中的未阻塞这个信号的线程在收到这个信号都会按同一种方式处理这个信号。 97 | /// 另外,注意子线程的mask是会从主线程继承而来的。 98 | 99 | pub fn sys_sigaction( 100 | &self, 101 | sig: usize, 102 | act: UserRef, 103 | oldact: UserRef, 104 | ) -> SysResult { 105 | let signal = SignalNum::from_num(sig); 106 | debug!( 107 | "sys_sigaction @ sig: {:?}, act: {}, oldact: {}", 108 | signal, act, oldact 109 | ); 110 | if oldact.is_valid() { 111 | oldact.write(self.task.pcb.lock().sigaction[sig].clone()); 112 | } 113 | if act.is_valid() { 114 | self.task.pcb.lock().sigaction[sig] = act.read(); 115 | } 116 | Ok(0) 117 | } 118 | pub async fn sys_sigsuspend(&self, sigset: UserRef) -> SysResult { 119 | let signal = sigset.read(); 120 | debug!("sys_sigsuspend @ sigset: {:?} signal: {:?}", sigset, signal); 121 | loop { 122 | self.check_timer(); 123 | let tcb = self.task.tcb.read(); 124 | if !tcb.signal.is_empty(None) { 125 | break; 126 | } 127 | drop(tcb); 128 | yield_now().await; 129 | } 130 | debug!("sys_sigsuspend @ sigset: {:?}", signal); 131 | Ok(0) 132 | } 133 | 134 | #[cfg(target_arch = "x86_64")] 135 | pub async fn sys_pause(&self) -> SysResult { 136 | debug!("sys_pause @ "); 137 | loop { 138 | self.check_timer(); 139 | let tcb = self.task.tcb.read(); 140 | if !tcb.signal.is_empty(None) { 141 | break; 142 | } 143 | drop(tcb); 144 | yield_now().await; 145 | } 146 | Ok(0) 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /kernel/src/syscall/sys.rs: -------------------------------------------------------------------------------- 1 | use super::SysResult; 2 | use crate::{user::UserTaskContainer, utils::useref::UserRef}; 3 | use libc_types::{resource::Rlimit, utsname::UTSname}; 4 | use log::{debug, warn}; 5 | 6 | impl UserTaskContainer { 7 | pub fn sys_uname(&self, uts_ptr: UserRef) -> SysResult { 8 | debug!("sys_uname @ uts_ptr: {}", uts_ptr); 9 | 10 | // for linux app compatible 11 | let sys_name = b"Linux"; 12 | let sys_nodename = b"debian"; 13 | let sys_release = b"5.10.0-7-riscv64"; 14 | let sys_version = b"#1 SMP Debian 5.10.40-1 (2021-05-28)"; 15 | let sys_machine = b"riscv qemu"; 16 | let sys_domain = b""; 17 | 18 | uts_ptr.with_mut(|uts| { 19 | uts.sysname[..sys_name.len()].copy_from_slice(sys_name); 20 | uts.nodename[..sys_nodename.len()].copy_from_slice(sys_nodename); 21 | uts.release[..sys_release.len()].copy_from_slice(sys_release); 22 | uts.version[..sys_version.len()].copy_from_slice(sys_version); 23 | uts.machine[..sys_machine.len()].copy_from_slice(sys_machine); 24 | uts.domainname[..sys_domain.len()].copy_from_slice(sys_domain); 25 | }); 26 | Ok(0) 27 | } 28 | 29 | /// TODO: FINISH sys_getrlimit 30 | pub fn sys_prlimit64( 31 | &self, 32 | pid: usize, 33 | resource: usize, 34 | new_limit: UserRef, 35 | old_limit: UserRef, 36 | ) -> SysResult { 37 | debug!( 38 | "sys_getrlimit @ pid: {}, resource: {}, new_limit: {}, old_limit: {}", 39 | pid, resource, new_limit, old_limit 40 | ); 41 | match resource { 42 | 7 => { 43 | if new_limit.is_valid() { 44 | let rlimit = new_limit.read(); 45 | self.task.pcb.lock().rlimits[7] = rlimit.max; 46 | } 47 | if old_limit.is_valid() { 48 | old_limit.with_mut(|rlimit| { 49 | rlimit.max = self.task.inner_map(|inner| inner.rlimits[7]); 50 | rlimit.curr = rlimit.max; 51 | }) 52 | } 53 | } 54 | _ => { 55 | warn!("need to finish prlimit64: resource {}", resource) 56 | } 57 | } 58 | Ok(0) 59 | } 60 | 61 | pub fn sys_geteuid(&self) -> SysResult { 62 | Ok(0) 63 | } 64 | 65 | pub fn sys_getegid(&self) -> SysResult { 66 | Ok(0) 67 | } 68 | 69 | pub fn sys_getgid(&self) -> SysResult { 70 | Ok(0) 71 | } 72 | 73 | pub fn sys_getuid(&self) -> SysResult { 74 | Ok(0) 75 | } 76 | 77 | pub fn sys_getpgid(&self) -> SysResult { 78 | Ok(0) 79 | } 80 | 81 | pub fn sys_setpgid(&self, _pid: usize, _pgid: usize) -> SysResult { 82 | Ok(0) 83 | } 84 | 85 | pub fn sys_klogctl(&self, log_type: usize, buf: UserRef, len: usize) -> SysResult { 86 | debug!( 87 | "sys_klogctl @ log_type: {:?} buf: {:?} len: {:?}", 88 | log_type, buf, len 89 | ); 90 | if buf.is_valid() { 91 | let path = buf.get_cstr().expect("can't log file to control"); 92 | println!("{}", path); 93 | } 94 | Ok(0) 95 | } 96 | 97 | pub fn sys_info(&self, meminfo: UserRef) -> SysResult { 98 | debug!("sys_info: {}", meminfo); 99 | if meminfo.is_valid() { 100 | meminfo.write(3); 101 | } 102 | Ok(0) 103 | } 104 | 105 | pub fn sys_sched_getparam(&self, pid: usize, param: usize) -> SysResult { 106 | debug!("sys_sched_getparam @ pid: {} param: {}", pid, param); 107 | 108 | Ok(0) 109 | } 110 | 111 | pub fn sys_sched_setscheduler(&self, pid: usize, _policy: usize, param: usize) -> SysResult { 112 | debug!("sys_sched_setscheduler @ pid: {} param: {}", pid, param); 113 | 114 | Ok(0) 115 | } 116 | 117 | pub fn sys_getrandom(&self, buf: UserRef, buf_len: usize, flags: usize) -> SysResult { 118 | debug!( 119 | "sys_getrandom @ buf: {}, buf_len: {:#x}, flags: {:#x}", 120 | buf, buf_len, flags 121 | ); 122 | let buf = buf.slice_mut_with_len(buf_len); 123 | static mut SEED: u64 = 0xdead_beef_cafe_babe; 124 | for x in buf.iter_mut() { 125 | unsafe { 126 | // from musl 127 | SEED = SEED.wrapping_mul(0x5851_f42d_4c95_7f2d); 128 | *x = (SEED >> 33) as u8; 129 | } 130 | } 131 | Ok(buf_len) 132 | } 133 | 134 | #[cfg(target_arch = "x86_64")] 135 | pub fn sys_arch_prctl(&self, code: usize, addr: usize) -> SysResult { 136 | use libc_types::others::ArchPrctlCmd; 137 | use polyhal_trap::trapframe::TrapFrameArgs; 138 | use syscalls::Errno; 139 | 140 | let arch_prctl_code = ArchPrctlCmd::try_from(code).map_err(|_| Errno::EINVAL)?; 141 | debug!( 142 | "sys_arch_prctl @ code: {:?}, addr: {:#x}", 143 | arch_prctl_code, addr 144 | ); 145 | let cx_ref = self.task.force_cx_ref(); 146 | match arch_prctl_code { 147 | ArchPrctlCmd::SetFS => cx_ref[TrapFrameArgs::TLS] = addr, 148 | _ => { 149 | error!("arch prctl: {:#x?}", arch_prctl_code); 150 | return Err(Errno::EPERM); 151 | } 152 | } 153 | Ok(0) 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /kernel/src/syscall/types/mm.rs: -------------------------------------------------------------------------------- 1 | use libc_types::mman::MmapProt; 2 | use polyhal::MappingFlags; 3 | 4 | pub fn map_mprot_to_flags(mprot: MmapProt) -> MappingFlags { 5 | let mut res = MappingFlags::empty(); 6 | if mprot.contains(MmapProt::READ) { 7 | res |= MappingFlags::R; 8 | } 9 | if mprot.contains(MmapProt::WRITE) { 10 | res |= MappingFlags::W; 11 | } 12 | if mprot.contains(MmapProt::EXEC) { 13 | res |= MappingFlags::X; 14 | } 15 | res 16 | } 17 | -------------------------------------------------------------------------------- /kernel/src/syscall/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mm; 2 | pub mod poll; 3 | pub mod signal; 4 | pub mod time; 5 | -------------------------------------------------------------------------------- /kernel/src/syscall/types/poll.rs: -------------------------------------------------------------------------------- 1 | use hashbrown::HashMap; 2 | use libc_types::epoll::{EpollCtl, EpollEvent}; 3 | use sync::Mutex; 4 | use vfscore::INodeInterface; 5 | 6 | #[derive(Debug)] 7 | pub struct EpollFile { 8 | pub data: Mutex>, 9 | pub flags: usize, 10 | } 11 | 12 | impl EpollFile { 13 | pub fn new(flags: usize) -> Self { 14 | EpollFile { 15 | data: Mutex::new(HashMap::new()), 16 | flags, 17 | } 18 | } 19 | 20 | pub fn ctl(&self, ctl: EpollCtl, fd: usize, ev: EpollEvent) { 21 | let mut data_map = self.data.lock(); 22 | match ctl { 23 | EpollCtl::ADD => { 24 | data_map.insert(fd, ev); 25 | } 26 | EpollCtl::DEL => { 27 | data_map.remove(&fd); 28 | } 29 | EpollCtl::MOD => { 30 | data_map.get_mut(&fd).map(|x| { 31 | *x = ev; 32 | }); 33 | } 34 | } 35 | } 36 | } 37 | 38 | impl INodeInterface for EpollFile {} 39 | -------------------------------------------------------------------------------- /kernel/src/syscall/types/signal.rs: -------------------------------------------------------------------------------- 1 | use libc_types::{signal::UContext, types::SigSet}; 2 | use polyhal_trap::trapframe::TrapFrame; 3 | 4 | #[repr(C)] 5 | #[derive(Debug, Clone)] 6 | pub struct SignalUserContext(UContext); 7 | 8 | #[cfg(target_arch = "x86_64")] 9 | impl SignalUserContext { 10 | pub const fn store_ctx(&mut self, ctx: &TrapFrame) { 11 | self.0.gregs.r8 = ctx.r8; 12 | self.0.gregs.r9 = ctx.r9; 13 | self.0.gregs.r10 = ctx.r10; 14 | self.0.gregs.r11 = ctx.r11; 15 | self.0.gregs.r12 = ctx.r12; 16 | self.0.gregs.r13 = ctx.r13; 17 | self.0.gregs.r14 = ctx.r14; 18 | self.0.gregs.r15 = ctx.r15; 19 | self.0.gregs.rdi = ctx.rdi; 20 | self.0.gregs.rsi = ctx.rsi; 21 | self.0.gregs.rbp = ctx.rbp; 22 | self.0.gregs.rbx = ctx.rbx; 23 | self.0.gregs.rdx = ctx.rdx; 24 | self.0.gregs.rax = ctx.rax; 25 | self.0.gregs.rcx = ctx.rcx; 26 | self.0.gregs.rsp = ctx.rsp; 27 | self.0.gregs.rip = ctx.rip; 28 | } 29 | 30 | pub const fn restore_ctx(&self, ctx: &mut TrapFrame) { 31 | ctx.r8 = self.0.gregs.r8; 32 | ctx.r9 = self.0.gregs.r9; 33 | ctx.r10 = self.0.gregs.r10; 34 | ctx.r11 = self.0.gregs.r11; 35 | ctx.r12 = self.0.gregs.r12; 36 | ctx.r13 = self.0.gregs.r13; 37 | ctx.r14 = self.0.gregs.r14; 38 | ctx.r15 = self.0.gregs.r15; 39 | ctx.rdi = self.0.gregs.rdi; 40 | ctx.rsi = self.0.gregs.rsi; 41 | ctx.rbp = self.0.gregs.rbp; 42 | ctx.rbx = self.0.gregs.rbx; 43 | ctx.rdx = self.0.gregs.rdx; 44 | ctx.rax = self.0.gregs.rax; 45 | ctx.rcx = self.0.gregs.rcx; 46 | ctx.rsp = self.0.gregs.rsp; 47 | ctx.rip = self.0.gregs.rip; 48 | } 49 | 50 | pub const fn set_sig_mask(&mut self, sigset: SigSet) { 51 | self.0.sig_mask.sigset = sigset; 52 | } 53 | 54 | pub const fn sig_mask(&self) -> SigSet { 55 | self.0.sig_mask.sigset 56 | } 57 | } 58 | 59 | #[cfg(any(target_arch = "riscv64"))] 60 | impl SignalUserContext { 61 | pub const fn store_ctx(&mut self, ctx: &TrapFrame) { 62 | let mut i = 1; 63 | while i < 32 { 64 | self.0.regs.gregs[i] = ctx.x[i]; 65 | i += 1; 66 | } 67 | self.0.regs.gregs[0] = ctx.sepc; 68 | } 69 | 70 | pub const fn restore_ctx(&self, ctx: &mut TrapFrame) { 71 | let mut i = 1; 72 | while i < 32 { 73 | ctx.x[i] = self.0.regs.gregs[i]; 74 | i += 1; 75 | } 76 | ctx.sepc = self.0.regs.gregs[0]; 77 | } 78 | 79 | pub const fn set_sig_mask(&mut self, sigset: SigSet) { 80 | self.0.sig_mask.sigset = sigset; 81 | } 82 | 83 | pub const fn sig_mask(&self) -> SigSet { 84 | self.0.sig_mask.sigset 85 | } 86 | } 87 | 88 | #[cfg(target_arch = "aarch64")] 89 | impl SignalUserContext { 90 | pub const fn store_ctx(&mut self, ctx: &TrapFrame) { 91 | let mut i = 0; 92 | while i < 31 { 93 | self.0.regs.gregs[i] = ctx.regs[i]; 94 | i += 1; 95 | } 96 | self.0.regs.pc = ctx.elr; 97 | self.0.regs.sp = ctx.sp; 98 | } 99 | 100 | pub const fn restore_ctx(&self, ctx: &mut TrapFrame) { 101 | let mut i = 0; 102 | while i < 31 { 103 | ctx.regs[i] = self.0.regs.gregs[i]; 104 | i += 1; 105 | } 106 | ctx.elr = self.0.regs.pc; 107 | ctx.sp = self.0.regs.sp; 108 | } 109 | 110 | pub const fn set_sig_mask(&mut self, sigset: SigSet) { 111 | self.0.sig_mask.sigset = sigset; 112 | } 113 | 114 | pub const fn sig_mask(&self) -> SigSet { 115 | self.0.sig_mask.sigset 116 | } 117 | } 118 | 119 | #[cfg(target_arch = "loongarch64")] 120 | impl SignalUserContext { 121 | pub const fn store_ctx(&mut self, ctx: &TrapFrame) { 122 | let mut i = 0; 123 | while i < 32 { 124 | self.0.regs.gregs[i] = ctx.regs[i]; 125 | i += 1; 126 | } 127 | self.0.regs.pc = ctx.era; 128 | } 129 | 130 | pub const fn restore_ctx(&self, ctx: &mut TrapFrame) { 131 | let mut i = 0; 132 | while i < 32 { 133 | ctx.regs[i] = self.0.regs.gregs[i]; 134 | i += 1; 135 | } 136 | ctx.era = self.0.regs.pc; 137 | } 138 | 139 | pub const fn set_sig_mask(&mut self, sigset: SigSet) { 140 | self.0.sig_mask.sigset = sigset; 141 | } 142 | 143 | pub const fn sig_mask(&self) -> SigSet { 144 | self.0.sig_mask.sigset 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /kernel/src/syscall/types/time.rs: -------------------------------------------------------------------------------- 1 | use libc_types::{time::ITimerVal, types::TimeVal}; 2 | 3 | #[derive(Debug, Clone, Copy, Default)] 4 | pub struct ProcessTimer { 5 | pub timer: ITimerVal, 6 | pub next: TimeVal, 7 | pub last: TimeVal, 8 | } 9 | -------------------------------------------------------------------------------- /kernel/src/tasks/async_ops.rs: -------------------------------------------------------------------------------- 1 | use core::{cmp, future::Future, pin::Pin, task::Poll}; 2 | 3 | use alloc::{sync::Arc, vec::Vec}; 4 | use executor::AsyncTask; 5 | use sync::Mutex; 6 | use syscalls::Errno; 7 | 8 | use super::{ 9 | current_user_task, 10 | task::{FutexTable, UserTask}, 11 | }; 12 | 13 | pub struct WaitPid(pub Arc, pub isize); 14 | 15 | impl Future for WaitPid { 16 | type Output = Result, Errno>; 17 | 18 | fn poll(self: Pin<&mut Self>, _cx: &mut core::task::Context<'_>) -> Poll { 19 | let inner = self.0.pcb.lock(); 20 | let res = inner 21 | .children 22 | .iter() 23 | .find(|x| (self.1 == -1 || x.task_id == self.1 as usize) && x.exit_code().is_some()) 24 | .cloned(); 25 | drop(inner); 26 | match res { 27 | Some(task) => Poll::Ready(Ok(task.clone())), 28 | None => Poll::Pending, 29 | } 30 | } 31 | } 32 | 33 | pub struct WaitSignal(pub Arc); 34 | 35 | impl Future for WaitSignal { 36 | type Output = (); 37 | 38 | fn poll(self: Pin<&mut Self>, _cx: &mut core::task::Context<'_>) -> Poll { 39 | match self.0.tcb.read().signal.is_empty(None) { 40 | false => Poll::Ready(()), 41 | true => Poll::Pending, 42 | } 43 | } 44 | } 45 | 46 | pub fn in_futex(futex_table: Arc>, task_id: usize) -> bool { 47 | let futex_table = futex_table.lock(); 48 | futex_table 49 | .values() 50 | .find(|x| x.contains(&task_id)) 51 | .is_some() 52 | } 53 | 54 | pub struct WaitFutex(pub Arc>, pub usize); 55 | 56 | impl Future for WaitFutex { 57 | type Output = Result; 58 | 59 | fn poll(self: Pin<&mut Self>, _cx: &mut core::task::Context<'_>) -> Poll { 60 | let signal = current_user_task().tcb.read().signal.clone(); 61 | match in_futex(self.0.clone(), self.1) { 62 | true => { 63 | if !signal.is_empty(None) { 64 | self.0 65 | .lock() 66 | .values_mut() 67 | .find(|x| x.contains(&self.1)) 68 | .map(|x| x.retain(|x| *x != self.1)); 69 | Poll::Ready(Err(Errno::EINTR)) 70 | } else { 71 | Poll::Pending 72 | } 73 | } 74 | false => Poll::Ready(Ok(0)), 75 | } 76 | } 77 | } 78 | 79 | pub struct WaitHandleAbleSignal(pub Arc); 80 | 81 | impl Future for WaitHandleAbleSignal { 82 | type Output = (); 83 | 84 | fn poll(self: Pin<&mut Self>, _cx: &mut core::task::Context<'_>) -> Poll { 85 | let task = &self.0; 86 | let sig_mask = task.tcb.read().sigmask; 87 | let has_signal = !task.tcb.read().signal.is_empty(Some(sig_mask)); 88 | 89 | match has_signal { 90 | true => Poll::Ready(()), 91 | false => Poll::Pending, 92 | } 93 | } 94 | } 95 | 96 | pub fn futex_wake(futex_table: Arc>, uaddr: usize, wake_count: usize) -> usize { 97 | let mut futex_table = futex_table.lock(); 98 | let que_size = futex_table.get_mut(&uaddr).map(|x| x.len()).unwrap_or(0); 99 | if que_size == 0 { 100 | 0 101 | } else { 102 | let que = futex_table 103 | .get_mut(&uaddr) 104 | .map(|x| x.drain(..cmp::min(wake_count as usize, que_size))); 105 | 106 | que.map(|x| x.count()).unwrap_or(0) 107 | } 108 | } 109 | 110 | pub fn futex_requeue( 111 | futex_table: Arc>, 112 | uaddr: usize, 113 | wake_count: usize, 114 | uaddr2: usize, 115 | reque_count: usize, 116 | ) -> usize { 117 | let mut futex_table = futex_table.lock(); 118 | 119 | let waked_size = futex_table 120 | .get_mut(&uaddr) 121 | .map(|x| x.drain(..wake_count as usize).count()) 122 | .unwrap_or(0); 123 | 124 | let reque: Option> = futex_table 125 | .get_mut(&uaddr) 126 | .map(|x| x.drain(..reque_count as usize).collect()); 127 | 128 | if let Some(reque) = reque { 129 | if !futex_table.contains_key(&uaddr2) { 130 | futex_table.insert(uaddr2, vec![]); 131 | } 132 | futex_table.get_mut(&uaddr2).unwrap().extend(reque); 133 | } 134 | 135 | waked_size 136 | } 137 | -------------------------------------------------------------------------------- /kernel/src/tasks/elf.rs: -------------------------------------------------------------------------------- 1 | use log::warn; 2 | use syscalls::Errno; 3 | use xmas_elf::{ 4 | program::Type, 5 | sections::SectionData, 6 | symbol_table::{DynEntry64, Entry}, 7 | ElfFile, 8 | }; 9 | 10 | pub trait ElfExtra { 11 | fn get_ph_addr(&self) -> Result; 12 | fn dynsym(&self) -> Result<&[DynEntry64], &'static str>; 13 | fn relocate(&self, base: usize) -> Result; 14 | } 15 | 16 | impl ElfExtra for ElfFile<'_> { 17 | // 获取elf加载需要的内存大小 18 | fn get_ph_addr(&self) -> Result { 19 | if let Some(phdr) = self 20 | .program_iter() 21 | .find(|ph| ph.get_type() == Ok(Type::Phdr)) 22 | { 23 | // if phdr exists in program header, use it 24 | Ok(phdr.virtual_addr()) 25 | } else if let Some(elf_addr) = self 26 | .program_iter() 27 | .find(|ph| ph.get_type() == Ok(Type::Load) && ph.offset() == 0) 28 | { 29 | // otherwise, check if elf is loaded from the beginning, then phdr can be inferred. 30 | Ok(elf_addr.virtual_addr() + self.header.pt2.ph_offset()) 31 | } else { 32 | warn!("elf: no phdr found, tls might not work"); 33 | Err(Errno::EBADF) 34 | } 35 | } 36 | 37 | fn dynsym(&self) -> Result<&[DynEntry64], &'static str> { 38 | match self 39 | .find_section_by_name(".dynsym") 40 | .ok_or(".dynsym not found")? 41 | .get_data(self) 42 | .map_err(|_| "corrupted .dynsym")? 43 | { 44 | SectionData::DynSymbolTable64(dsym) => Ok(dsym), 45 | _ => Err("bad .dynsym"), 46 | } 47 | } 48 | 49 | fn relocate(&self, base: usize) -> Result { 50 | let data = self 51 | .find_section_by_name(".rela.dyn") 52 | .ok_or(".rela.dyn not found")? 53 | .get_data(self) 54 | .map_err(|_| "corrupted .rela.dyn")?; 55 | let entries = match data { 56 | SectionData::Rela64(entries) => entries, 57 | _ => return Err("bad .rela.dyn"), 58 | }; 59 | let dynsym = self.dynsym()?; 60 | for entry in entries.iter() { 61 | const REL_GOT: u32 = 6; 62 | const REL_PLT: u32 = 7; 63 | const REL_RELATIVE: u32 = 8; 64 | const R_RISCV_64: u32 = 2; 65 | const R_RISCV_RELATIVE: u32 = 3; 66 | const R_AARCH64_RELATIVE: u32 = 0x403; 67 | const R_AARCH64_GLOBAL_DATA: u32 = 0x401; 68 | 69 | match entry.get_type() { 70 | REL_GOT | REL_PLT | R_RISCV_64 | R_AARCH64_GLOBAL_DATA => { 71 | let dynsym = &dynsym[entry.get_symbol_table_index() as usize]; 72 | if dynsym.shndx() == 0 { 73 | let name = dynsym.get_name(self)?; 74 | panic!("need to find symbol: {:?}", name); 75 | } else { 76 | base + dynsym.value() as usize 77 | }; 78 | } 79 | REL_RELATIVE | R_RISCV_RELATIVE | R_AARCH64_RELATIVE => {} 80 | t => unimplemented!("unknown type: {}", t), 81 | } 82 | } 83 | // panic!("STOP"); 84 | Ok(base) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /kernel/src/tasks/exec.rs: -------------------------------------------------------------------------------- 1 | use super::{stack::init_task_stack, UserTask}; 2 | use crate::{ 3 | consts::USER_DYN_ADDR, 4 | tasks::{elf::ElfExtra, MemType}, 5 | }; 6 | use alloc::{ 7 | string::{String, ToString}, 8 | sync::Arc, 9 | vec::Vec, 10 | }; 11 | use core::ops::{Add, Mul}; 12 | use devices::{frame_alloc_much, PAGE_SIZE}; 13 | use fs::{file::File, pathbuf::PathBuf}; 14 | use libc_types::fcntl::OpenFlags; 15 | use syscalls::Errno; 16 | use xmas_elf::program::{SegmentData, Type}; 17 | 18 | pub fn exec_with_process( 19 | task: Arc, 20 | curr_dir: PathBuf, 21 | path: String, 22 | args: Vec, 23 | envp: Vec, 24 | ) -> Result, Errno> { 25 | // copy args, avoid free before pushing. 26 | // let path = String::from(path); 27 | let path = curr_dir.join(&path); 28 | 29 | let user_task = task.clone(); 30 | user_task.pcb.lock().memset.clear(); 31 | user_task.page_table.restore(); 32 | user_task.page_table.change(); 33 | 34 | // TODO: 运行程序的时候,判断当前的路径 35 | let file = File::open_link(path.clone(), OpenFlags::RDONLY) 36 | .map(Arc::new)? 37 | .clone(); 38 | let file_size = file.file_size()?; 39 | let frame_ppn = frame_alloc_much(file_size.div_ceil(PAGE_SIZE)); 40 | let buffer = frame_ppn.as_ref().unwrap()[0].slice_mut_with_len(file_size); 41 | let rsize = file.readat(0, buffer)?; 42 | assert_eq!(rsize, file_size); 43 | // flush_dcache_range(); 44 | // 读取elf信息 45 | let elf = if let Ok(elf) = xmas_elf::ElfFile::new(&buffer) { 46 | elf 47 | } else { 48 | let mut new_args = vec!["busybox".to_string(), "sh".to_string()]; 49 | args.iter().for_each(|x| new_args.push(x.clone())); 50 | return exec_with_process(task, curr_dir, String::from("busybox"), new_args, envp); 51 | }; 52 | let elf_header = elf.header; 53 | 54 | let entry_point = elf.header.pt2.entry_point() as usize; 55 | // this assert ensures that the file is elf file. 56 | assert_eq!( 57 | elf_header.pt1.magic, 58 | [0x7f, 0x45, 0x4c, 0x46], 59 | "invalid elf!" 60 | ); 61 | // WARRNING: this convert async task to user task. 62 | let user_task = task.clone(); 63 | 64 | // check if it is libc, dlopen, it needs recurit. 65 | let header = elf 66 | .program_iter() 67 | .find(|ph| ph.get_type() == Ok(Type::Interp)); 68 | if let Some(header) = header { 69 | if let Ok(SegmentData::Undefined(_data)) = header.get_data(&elf) { 70 | drop(frame_ppn); 71 | let mut new_args = vec![String::from("libc.so")]; 72 | new_args.extend(args); 73 | return exec_with_process(task, curr_dir, new_args[0].clone(), new_args, envp); 74 | } 75 | } 76 | 77 | // 获取程序所有段之后的内存,4K 对齐后作为堆底 78 | let heap_bottom = elf 79 | .program_iter() 80 | .map(|x| (x.virtual_addr() + x.mem_size()) as usize) 81 | .max() 82 | .unwrap() 83 | .div_ceil(PAGE_SIZE) 84 | .mul(PAGE_SIZE); 85 | 86 | let base = elf.relocate(USER_DYN_ADDR).unwrap_or(0); 87 | init_task_stack( 88 | user_task.clone(), 89 | args, 90 | base, 91 | &path.path(), 92 | entry_point, 93 | elf_header.pt2.ph_count() as usize, 94 | elf_header.pt2.ph_entry_size() as usize, 95 | elf.get_ph_addr().unwrap_or(0) as usize, 96 | heap_bottom, 97 | ); 98 | 99 | // map sections. 100 | elf.program_iter() 101 | .filter(|x| x.get_type().unwrap() == xmas_elf::program::Type::Load) 102 | .for_each(|ph| { 103 | let file_size = ph.file_size() as usize; 104 | let mem_size = ph.mem_size() as usize; 105 | let offset = ph.offset() as usize; 106 | let virt_addr = base + ph.virtual_addr() as usize; 107 | let vpn = virt_addr / PAGE_SIZE; 108 | 109 | let page_count = (virt_addr + mem_size).div_ceil(PAGE_SIZE) - vpn; 110 | let ppn_start = 111 | user_task.frame_alloc(va!(virt_addr).floor(), MemType::CodeSection, page_count); 112 | let page_space = va!(virt_addr).slice_mut_with_len(file_size); 113 | let ppn_space = ppn_start 114 | .expect("not have enough memory") 115 | .add(virt_addr % PAGE_SIZE) 116 | .slice_mut_with_len(file_size); 117 | 118 | page_space.copy_from_slice(&buffer[offset..offset + file_size]); 119 | assert_eq!(ppn_space, page_space); 120 | assert_eq!(&buffer[offset..offset + file_size], ppn_space); 121 | assert_eq!(&buffer[offset..offset + file_size], page_space); 122 | }); 123 | Ok(user_task) 124 | } 125 | -------------------------------------------------------------------------------- /kernel/src/tasks/filetable.rs: -------------------------------------------------------------------------------- 1 | use alloc::{sync::Arc, vec::Vec}; 2 | use core::ops::{Deref, DerefMut}; 3 | use fs::file::File; 4 | use libc_types::fcntl::OpenFlags; 5 | 6 | const FILE_MAX: usize = 255; 7 | const FD_NONE: Option> = Option::None; 8 | 9 | #[derive(Clone)] 10 | pub struct FileTable { 11 | inner: Vec>>, 12 | limit: usize, 13 | } 14 | 15 | impl FileTable { 16 | pub fn new() -> Self { 17 | let mut file_table: Vec>> = vec![FD_NONE; FILE_MAX]; 18 | file_table[..3].fill(Some( 19 | File::open("/dev/ttyv0", OpenFlags::RDWR) 20 | .map(Arc::new) 21 | .expect("can't read tty file"), 22 | )); 23 | Self { 24 | inner: file_table, 25 | limit: FILE_MAX, 26 | } 27 | } 28 | } 29 | 30 | impl Deref for FileTable { 31 | type Target = Vec>>; 32 | 33 | fn deref(&self) -> &Self::Target { 34 | &self.inner 35 | } 36 | } 37 | 38 | impl DerefMut for FileTable { 39 | fn deref_mut(&mut self) -> &mut Self::Target { 40 | &mut self.inner 41 | } 42 | } 43 | 44 | pub fn rlimits_new() -> Vec { 45 | let mut rlimits = vec![0usize; 8]; 46 | rlimits[7] = FILE_MAX; 47 | rlimits 48 | } 49 | -------------------------------------------------------------------------------- /kernel/src/tasks/initproc.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_imports)] 3 | use alloc::{ 4 | string::{String, ToString}, 5 | vec::Vec, 6 | }; 7 | use devices::utils::get_char; 8 | use executor::{current_task, release_task, task::TaskType, tid2task, yield_now, TASK_MAP}; 9 | use fs::{file::File, FileType}; 10 | use libc_types::fcntl::OpenFlags; 11 | use log::debug; 12 | use polyhal::{debug_console::DebugConsole, instruction::shutdown}; 13 | use vfscore::INodeInterface; 14 | 15 | use crate::tasks::add_user_task; 16 | 17 | use super::UserTask; 18 | 19 | fn clear() { 20 | DebugConsole::putchar(0x1b); 21 | DebugConsole::putchar(0x5b); 22 | DebugConsole::putchar(0x48); 23 | DebugConsole::putchar(0x1b); 24 | DebugConsole::putchar(0x5b); 25 | DebugConsole::putchar(0x32); 26 | DebugConsole::putchar(0x4a); 27 | } 28 | 29 | async fn kill_all_tasks() { 30 | TASK_MAP.lock().values().into_iter().for_each(|task| { 31 | task.upgrade().inspect(|x| { 32 | if x.get_task_type() == TaskType::MonolithicTask { 33 | x.exit(100) 34 | } 35 | }); 36 | }); 37 | } 38 | 39 | async fn command(cmd: &str) { 40 | let mut args: Vec<&str> = cmd.split(" ").filter(|x| *x != "").collect(); 41 | debug!("cmd: {} args: {:?}", cmd, args); 42 | let filename = args.drain(..1).last().unwrap(); 43 | match File::open(filename, OpenFlags::RDONLY) { 44 | Ok(_) => { 45 | info!("exec: {}", filename); 46 | let mut args_extend = vec![filename]; 47 | args_extend.extend(args.into_iter()); 48 | let task_id = add_user_task(&filename, args_extend, Vec::new()).await; 49 | let task = tid2task(task_id).unwrap(); 50 | loop { 51 | if task.exit_code().is_some() { 52 | release_task(task_id); 53 | break; 54 | } 55 | yield_now().await; 56 | } 57 | // syscall(SYS_WAIT4, [0,0,0,0,0,0,0]) 58 | // .await 59 | // .expect("can't wait a pid"); 60 | } 61 | Err(_) => { 62 | println!("unknown command: {}", cmd); 63 | } 64 | } 65 | } 66 | 67 | pub async fn initproc() { 68 | println!("start kernel tasks"); 69 | // command("./runtest.exe -w entry-dynamic.exe argv").await; 70 | // command("./entry-dynamic.exe argv").await; 71 | // command("busybox echo run time-test").await; 72 | // command("time-test").await; 73 | 74 | // command("busybox sh basic/run-all.sh").await; 75 | 76 | // command("busybox echo run netperf_testcode.sh").await; 77 | // command("busybox sh netperf_testcode.sh").await; 78 | 79 | // command("busybox echo run busybox_testcode.sh").await; 80 | // command("busybox sh busybox_testcode.sh").await; 81 | 82 | // command("busybox echo run libctest_testcode.sh").await; 83 | // command("busybox sh libctest_testcode.sh").await; 84 | // command("runtest.exe -w entry-static.exe utime").await; 85 | // command("busybox ln -s /busybox /bin/cat").await; 86 | // command("./bin/cat libctest_testcode.sh").await; 87 | // command("busybox ls -l /bin").await; 88 | // command("busybox ln -s /busybox /bin/ln").await; 89 | // command("busybox ln -s /busybox /bin/wget").await; 90 | // command("busybox ln -s /busybox /bin/xz").await; 91 | // command("busybox ls -l /bin").await; 92 | // command("busybox sh init.sh").await; 93 | // command("busybox ls -l /bin").await; 94 | 95 | command("busybox ash").await; 96 | // command("busybox echo run lua_testcode.sh").await; 97 | // command("busybox sh lua_testcode.sh").await; 98 | 99 | // command("busybox init").await; 100 | // command("busybox sh").await; 101 | // command("busybox sh init.sh").await; 102 | 103 | // command("busybox echo run cyclic_testcode.sh").await; 104 | // command("busybox sh cyclictest_testcode.sh").await; 105 | // kill_all_tasks().await; 106 | 107 | // command("libc-bench").await; 108 | 109 | // command("busybox echo run iperf_testcode.sh").await; 110 | // command("busybox sh iperf_testcode.sh").await; 111 | // kill_all_tasks().await; 112 | 113 | // command("busybox echo run iozone_testcode.sh").await; 114 | // command("busybox sh iozone_testcode.sh ").await; 115 | 116 | // command("busybox echo run lmbench_testcode.sh").await; 117 | // command("busybox sh lmbench_testcode.sh").await; 118 | 119 | // command("busybox echo run unixbench_testcode.sh").await; 120 | // command("busybox sh unixbench_testcode.sh").await; 121 | 122 | // command("copy-file-range-test-1").await; 123 | // command("copy-file-range-test-2").await; 124 | // command("copy-file-range-test-3").await; 125 | // command("copy-file-range-test-4").await; 126 | // command("interrupts-test-1").await; 127 | // command("interrupts-test-2").await; 128 | 129 | // switch_to_kernel_page_table(); 130 | println!("!TEST FINISH!"); 131 | 132 | // Shutdown if there just have blankkernel task. 133 | if TASK_MAP 134 | .lock() 135 | .values() 136 | .find(|x| { 137 | x.upgrade() 138 | .map(|x| x.get_task_type() != TaskType::BlankKernel) 139 | .unwrap_or(false) 140 | }) 141 | .is_none() 142 | { 143 | shutdown(); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /kernel/src/tasks/mod.rs: -------------------------------------------------------------------------------- 1 | mod async_ops; 2 | pub mod elf; 3 | pub mod exec; 4 | mod filetable; 5 | mod initproc; 6 | mod memset; 7 | mod shm; 8 | mod stack; 9 | mod task; 10 | 11 | use self::initproc::initproc; 12 | use crate::{consts::USER_WORK_DIR, syscall::NET_SERVER, user::entry::user_entry}; 13 | use alloc::{ 14 | string::String, 15 | sync::Weak, 16 | {sync::Arc, vec::Vec}, 17 | }; 18 | pub use async_ops::{ 19 | futex_requeue, futex_wake, WaitFutex, WaitHandleAbleSignal, WaitPid, WaitSignal, 20 | }; 21 | use devices::get_net_device; 22 | use exec::exec_with_process; 23 | use executor::{current_task, thread, yield_now, AsyncTask, TaskId, DEFAULT_EXECUTOR}; 24 | use fs::pathbuf::PathBuf; 25 | pub use memset::{MapTrack, MemArea, MemType}; 26 | use polyhal::common::get_cpu_num; 27 | pub use shm::{MapedSharedMemory, SharedMemory, SHARED_MEMORY}; 28 | pub use task::UserTask; 29 | 30 | pub enum UserTaskControlFlow { 31 | Continue, 32 | Break, 33 | } 34 | 35 | #[allow(dead_code)] 36 | pub async fn handle_net() { 37 | let mut buffer = vec![0u8; 2048]; 38 | // #[cfg(feature = "net")] 39 | loop { 40 | let res = get_net_device(0).recv(&mut buffer); 41 | if let Ok(rlen) = res { 42 | NET_SERVER.analysis_net_data(&buffer[..rlen]); 43 | } 44 | yield_now().await; 45 | } 46 | } 47 | 48 | pub fn init() { 49 | DEFAULT_EXECUTOR.init(get_cpu_num()); 50 | thread::spawn_blank(initproc()); 51 | // #[cfg(feature = "net")] 52 | // thread::spawn_blank(KernelTask::new(handle_net())); 53 | } 54 | 55 | pub fn run_tasks() { 56 | DEFAULT_EXECUTOR.run() 57 | } 58 | 59 | pub async fn add_user_task(filename: &str, args: Vec<&str>, envp: Vec<&str>) -> TaskId { 60 | let curr_task = current_task(); 61 | let task = UserTask::new(Weak::new(), USER_WORK_DIR); 62 | task.before_run(); 63 | exec_with_process( 64 | task.clone(), 65 | PathBuf::empty(), 66 | String::from(filename), 67 | args.into_iter().map(String::from).collect(), 68 | envp.into_iter().map(String::from).collect(), 69 | ) 70 | .expect("can't add task to excutor"); 71 | curr_task.before_run(); 72 | thread::spawn(task.clone(), user_entry()); 73 | 74 | task.get_task_id() 75 | } 76 | 77 | #[inline] 78 | pub fn current_user_task() -> Arc { 79 | current_task().downcast_arc::().ok().unwrap() 80 | } 81 | -------------------------------------------------------------------------------- /kernel/src/tasks/shm.rs: -------------------------------------------------------------------------------- 1 | use alloc::{collections::BTreeMap, sync::Arc, vec::Vec}; 2 | use runtime::frame::FrameTracker; 3 | use sync::Mutex; 4 | 5 | pub static SHARED_MEMORY: Mutex>> = Mutex::new(BTreeMap::new()); 6 | 7 | pub struct SharedMemory { 8 | pub trackers: Vec>, 9 | pub deleted: Mutex, 10 | } 11 | 12 | impl SharedMemory { 13 | pub const fn new(trackers: Vec>) -> Self { 14 | Self { 15 | trackers, 16 | deleted: Mutex::new(false), 17 | } 18 | } 19 | } 20 | 21 | #[derive(Clone)] 22 | pub struct MapedSharedMemory { 23 | pub key: usize, 24 | pub mem: Arc, 25 | pub start: usize, 26 | pub size: usize, 27 | } 28 | 29 | impl Drop for MapedSharedMemory { 30 | fn drop(&mut self) { 31 | // self.mem.trackers.remove(self.key); 32 | if Arc::strong_count(&self.mem) == 2 && *self.mem.deleted.lock() == true { 33 | SHARED_MEMORY.lock().remove(&self.key); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /kernel/src/tasks/stack.rs: -------------------------------------------------------------------------------- 1 | use alloc::{collections::btree_map::BTreeMap, string::String, sync::Arc, vec::Vec}; 2 | use devices::PAGE_SIZE; 3 | use executor::AsyncTask; 4 | use libc_types::elf::AuxType; 5 | use polyhal_trap::trapframe::{TrapFrame, TrapFrameArgs}; 6 | 7 | use crate::{ 8 | consts::{USER_STACK_INIT_SIZE, USER_STACK_TOP}, 9 | tasks::MemType, 10 | }; 11 | 12 | use super::UserTask; 13 | 14 | pub fn init_task_stack( 15 | user_task: Arc, 16 | args: Vec, 17 | base: usize, 18 | path: &str, 19 | entry_point: usize, 20 | ph_count: usize, 21 | ph_entry_size: usize, 22 | ph_addr: usize, 23 | heap_bottom: usize, 24 | ) { 25 | // map stack 26 | user_task.frame_alloc( 27 | va!(USER_STACK_TOP - USER_STACK_INIT_SIZE), 28 | MemType::Stack, 29 | USER_STACK_INIT_SIZE / PAGE_SIZE, 30 | ); 31 | log::debug!( 32 | "[task {}] entry: {:#x}", 33 | user_task.get_task_id(), 34 | base + entry_point 35 | ); 36 | user_task.inner_map(|inner| { 37 | inner.heap = heap_bottom; 38 | inner.entry = base + entry_point; 39 | }); 40 | 41 | let mut tcb = user_task.tcb.write(); 42 | 43 | tcb.cx = TrapFrame::new(); 44 | tcb.cx[TrapFrameArgs::SP] = USER_STACK_TOP; // stack top; 45 | tcb.cx[TrapFrameArgs::SEPC] = base + entry_point; 46 | 47 | drop(tcb); 48 | 49 | // push stack 50 | let envp = vec![ 51 | "LD_LIBRARY_PATH=/", 52 | "PS1=\x1b[1m\x1b[32mByteOS\x1b[0m:\x1b[1m\x1b[34m\\w\x1b[0m\\$ \0", 53 | "PATH=/:/bin:/usr/bin", 54 | "UB_BINDIR=./", 55 | ]; 56 | let envp: Vec = envp 57 | .into_iter() 58 | .rev() 59 | .map(|x| user_task.push_str(x)) 60 | .collect(); 61 | let args: Vec = args 62 | .into_iter() 63 | .rev() 64 | .map(|x| user_task.push_str(&x)) 65 | .collect(); 66 | 67 | let random_ptr = user_task.push_arr(&[0u8; 16]); 68 | let mut auxv = BTreeMap::new(); 69 | auxv.insert(AuxType::Platform, user_task.push_str("riscv")); 70 | auxv.insert(AuxType::ExecFn, user_task.push_str(path)); 71 | auxv.insert(AuxType::Phnum, ph_count); 72 | auxv.insert(AuxType::PageSize, PAGE_SIZE); 73 | auxv.insert(AuxType::Entry, base + entry_point); 74 | auxv.insert(AuxType::Phent, ph_entry_size); 75 | auxv.insert(AuxType::Phdr, base + ph_addr); 76 | auxv.insert(AuxType::GID, 0); 77 | auxv.insert(AuxType::EGID, 0); 78 | auxv.insert(AuxType::UID, 0); 79 | auxv.insert(AuxType::EUID, 0); 80 | auxv.insert(AuxType::Secure, 0); 81 | auxv.insert(AuxType::Random, random_ptr); 82 | 83 | // auxv top 84 | user_task.push(0); 85 | // TODO: push auxv 86 | auxv.iter().for_each(|(key, v)| { 87 | user_task.push(*v); 88 | user_task.push(*key as usize); 89 | }); 90 | 91 | user_task.push(0); 92 | envp.iter().for_each(|x| user_task.push(*x)); 93 | user_task.push(0); 94 | args.iter().for_each(|x| user_task.push(*x)); 95 | user_task.push(args.len()); 96 | } 97 | -------------------------------------------------------------------------------- /kernel/src/user/entry.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use async_recursion::async_recursion; 3 | use executor::{boot_page_table, yield_now, AsyncTask}; 4 | use futures_lite::future; 5 | use libc_types::{signal::SignalNum, types::TimeVal}; 6 | use log::debug; 7 | use polyhal::timer::current_time; 8 | use polyhal_trap::trapframe::TrapFrame; 9 | 10 | use crate::tasks::{current_user_task, UserTaskControlFlow}; 11 | 12 | use super::UserTaskContainer; 13 | 14 | impl UserTaskContainer { 15 | pub fn check_thread_exit(&self) -> Option { 16 | self.task 17 | .exit_code() 18 | .or(self.task.tcb.read().thread_exit_code.map(|x| x as usize)) 19 | } 20 | 21 | pub fn check_timer(&self) { 22 | let mut pcb = self.task.pcb.lock(); 23 | let timer = &mut pcb.timer[0]; 24 | if timer.next > timer.last { 25 | let now: TimeVal = current_time().into(); 26 | if now >= timer.next { 27 | self.task.tcb.write().signal.has(SignalNum::ALRM); 28 | timer.last = timer.next; 29 | } 30 | } 31 | } 32 | 33 | pub async fn check_signal(&self) { 34 | loop { 35 | let sig_mask = self.task.tcb.read().sigmask; 36 | let signal = self.task.tcb.read().signal.clone().pop_one(Some(sig_mask)); 37 | if let Some(signal) = signal { 38 | debug!("mask: {:?}", sig_mask); 39 | self.handle_signal(signal).await; 40 | let mut tcb = self.task.tcb.write(); 41 | tcb.signal.remove(signal); 42 | // check if it is a real time signal 43 | if let Some(index) = signal.real_time_index() 44 | && tcb.signal_queue[index] > 0 45 | { 46 | tcb.signal.insert(signal); 47 | tcb.signal_queue[index] -= 1; 48 | } 49 | } else { 50 | break; 51 | } 52 | } 53 | } 54 | 55 | pub async fn entry_point(&mut self, cx_ref: &mut TrapFrame) { 56 | let mut times: i32 = 0; 57 | 58 | loop { 59 | self.check_timer(); 60 | self.check_signal().await; 61 | 62 | // check for task exit status. 63 | if let Some(exit_code) = self.check_thread_exit() { 64 | debug!( 65 | "program exit with code: {} task_id: {} with inner", 66 | exit_code, 67 | self.task.get_task_id() 68 | ); 69 | break; 70 | } 71 | 72 | let res = future::or(self.handle_syscall(cx_ref), async { 73 | loop { 74 | self.check_signal().await; 75 | 76 | if let Some(_exit_code) = self.check_thread_exit() { 77 | return UserTaskControlFlow::Break; 78 | } 79 | self.check_timer(); 80 | yield_now().await; 81 | } 82 | }); 83 | 84 | if let UserTaskControlFlow::Break = res.await { 85 | break; 86 | } 87 | 88 | if let Some(exit_code) = self.check_thread_exit() { 89 | debug!( 90 | "program exit with code: {} task_id: {} with inner", 91 | exit_code, 92 | self.task.get_task_id() 93 | ); 94 | break; 95 | } 96 | 97 | times += 1; 98 | if times >= 50 { 99 | times = 0; 100 | yield_now().await; 101 | } 102 | } 103 | 104 | debug!("exit_task: {}", self.task.get_task_id()); 105 | boot_page_table().change(); 106 | } 107 | } 108 | 109 | #[async_recursion(Sync)] 110 | pub async fn user_entry() { 111 | let task = current_user_task(); 112 | let cx_ref = task.force_cx_ref(); 113 | let tid = task.get_task_id(); 114 | UserTaskContainer { task, tid }.entry_point(cx_ref).await; 115 | } 116 | -------------------------------------------------------------------------------- /kernel/src/user/signal.rs: -------------------------------------------------------------------------------- 1 | use crate::syscall::types::signal::SignalUserContext; 2 | use crate::tasks::current_user_task; 3 | use core::mem::size_of; 4 | use executor::AsyncTask; 5 | use libc_types::internal::SigAction; 6 | use libc_types::signal::SignalNum; 7 | use log::debug; 8 | use polyhal_trap::trapframe::TrapFrameArgs; 9 | 10 | use super::UserTaskContainer; 11 | 12 | impl UserTaskContainer { 13 | pub async fn handle_signal(&self, signal: SignalNum) { 14 | debug!( 15 | "handle signal: {:?} task_id: {}", 16 | signal, 17 | self.task.get_task_id() 18 | ); 19 | 20 | // get the signal action for the signal. 21 | let sigaction = self.task.pcb.lock().sigaction[signal.num()].clone(); 22 | 23 | if sigaction.handler == SigAction::SIG_IGN { 24 | // ignore signal if the handler of is SIG_IGN(1) 25 | return; 26 | } else if sigaction.handler == 0 || sigaction.handler == SigAction::SIG_DFL { 27 | // if there doesn't have signal handler. 28 | // Then use default handler. Exit or do nothing. 29 | if matches!(signal, SignalNum::CANCEL | SignalNum::SEGV | SignalNum::ILL) { 30 | current_user_task().exit_with_signal(signal.num()); 31 | } 32 | return; 33 | } 34 | 35 | let cx_ref = self.task.force_cx_ref(); 36 | // store task_mask and context. 37 | // alloc space for SignalUserContext at stack and align with 16 bytes. 38 | let sp = (cx_ref[TrapFrameArgs::SP] - size_of::()) & !0xF; 39 | let cx = unsafe { (sp as *mut SignalUserContext).as_mut().unwrap() }; 40 | 41 | // change task context to do the signal. 42 | let mut tcb = self.task.tcb.write(); 43 | cx.store_ctx(&cx_ref); 44 | cx.set_sig_mask(tcb.sigmask); 45 | 46 | tcb.sigmask = sigaction.mask; 47 | tcb.cx[TrapFrameArgs::SP] = sp; 48 | tcb.cx[TrapFrameArgs::SEPC] = sigaction.handler; 49 | tcb.cx[TrapFrameArgs::RA] = sigaction.restorer; 50 | tcb.cx[TrapFrameArgs::ARG0] = signal.num(); 51 | tcb.cx[TrapFrameArgs::ARG1] = 0; 52 | tcb.cx[TrapFrameArgs::ARG2] = cx as *mut SignalUserContext as usize; 53 | drop(tcb); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /kernel/src/user/socket_pair.rs: -------------------------------------------------------------------------------- 1 | use core::cmp; 2 | 3 | use alloc::{collections::VecDeque, sync::Arc}; 4 | use libc_types::poll::PollEvent; 5 | use sync::Mutex; 6 | use syscalls::Errno; 7 | use vfscore::{INodeInterface, VfsResult}; 8 | 9 | pub struct SocketPair { 10 | inner: Arc>>, 11 | } 12 | 13 | impl INodeInterface for SocketPair { 14 | fn writeat(&self, _offset: usize, buffer: &[u8]) -> VfsResult { 15 | let mut queue = self.inner.lock(); 16 | if queue.len() > 0x50000 { 17 | Err(Errno::EWOULDBLOCK) 18 | } else { 19 | let wlen = buffer.len(); 20 | queue.extend(buffer.iter()); 21 | Ok(wlen) 22 | } 23 | } 24 | 25 | fn readat(&self, _offset: usize, buffer: &mut [u8]) -> VfsResult { 26 | let mut queue = self.inner.lock(); 27 | let rlen = cmp::min(queue.len(), buffer.len()); 28 | queue 29 | .drain(..rlen) 30 | .enumerate() 31 | .into_iter() 32 | .for_each(|(i, x)| { 33 | buffer[i] = x; 34 | }); 35 | if rlen == 0 { 36 | Err(Errno::EWOULDBLOCK) 37 | } else { 38 | Ok(rlen) 39 | } 40 | } 41 | 42 | fn poll(&self, events: PollEvent) -> VfsResult { 43 | let mut res = PollEvent::NONE; 44 | if events.contains(PollEvent::OUT) { 45 | if self.inner.lock().len() <= 0x50000 { 46 | res |= PollEvent::OUT; 47 | } 48 | } 49 | if events.contains(PollEvent::IN) { 50 | if self.inner.lock().len() > 0 { 51 | res |= PollEvent::IN; 52 | } 53 | } 54 | Ok(res) 55 | } 56 | } 57 | 58 | pub fn create_socket_pair() -> Arc { 59 | Arc::new(SocketPair { 60 | inner: Arc::new(Mutex::new(VecDeque::new())), 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /kernel/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod useref; 2 | 3 | pub fn hexdump(data: &[u8], mut start_addr: usize) { 4 | const PRELAND_WIDTH: usize = 70; 5 | println!("{:-^1$}", " hexdump ", PRELAND_WIDTH); 6 | for offset in (0..data.len()).step_by(16) { 7 | print!("{:08x} ", start_addr); 8 | start_addr += 0x10; 9 | for i in 0..16 { 10 | if offset + i < data.len() { 11 | print!("{:02x} ", data[offset + i]); 12 | } else { 13 | print!("{:02} ", ""); 14 | } 15 | } 16 | 17 | print!("{:>6}", ' '); 18 | 19 | for i in 0..16 { 20 | if offset + i < data.len() { 21 | let c = data[offset + i]; 22 | if c >= 0x20 && c <= 0x7e { 23 | print!("{}", c as char); 24 | } else { 25 | print!("."); 26 | } 27 | } else { 28 | print!("{:02} ", ""); 29 | } 30 | } 31 | 32 | println!(""); 33 | } 34 | println!("{:-^1$}", " hexdump end ", PRELAND_WIDTH); 35 | } 36 | -------------------------------------------------------------------------------- /kernel/src/utils/useref.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt::{Debug, Display}, 3 | marker::PhantomData, 4 | }; 5 | 6 | use polyhal::VirtAddr; 7 | 8 | #[derive(Clone, Copy)] 9 | pub struct UserRef { 10 | addr: VirtAddr, 11 | r#type: PhantomData, 12 | } 13 | 14 | impl From for UserRef { 15 | fn from(value: usize) -> Self { 16 | Self { 17 | addr: value.into(), 18 | r#type: PhantomData, 19 | } 20 | } 21 | } 22 | 23 | impl From for UserRef { 24 | fn from(value: VirtAddr) -> Self { 25 | Self { 26 | addr: value, 27 | r#type: PhantomData, 28 | } 29 | } 30 | } 31 | 32 | impl Into for UserRef { 33 | fn into(self) -> usize { 34 | self.addr.raw() 35 | } 36 | } 37 | 38 | impl UserRef { 39 | #[inline] 40 | pub fn read(&self) -> T { 41 | unsafe { (self.addr() as *const T).as_ref().unwrap().clone() } 42 | } 43 | } 44 | 45 | impl UserRef { 46 | pub const fn addr(&self) -> usize { 47 | self.addr.raw() 48 | } 49 | #[inline] 50 | pub const fn write(&self, val: T) { 51 | unsafe { 52 | (self.addr() as *mut T).write(val); 53 | } 54 | } 55 | #[inline] 56 | pub fn with R>(&self, mut f: F) -> R { 57 | f(self.addr.get_ref()) 58 | } 59 | #[inline] 60 | pub fn with_mut R>(&self, f: F) -> R { 61 | f(self.addr.get_mut_ref()) 62 | } 63 | // #[inline] 64 | // pub fn get_ref(&self) -> &'static T { 65 | // self.addr.get_ref::() 66 | // } 67 | 68 | // #[inline] 69 | // pub fn get_mut(&self) -> &'static mut T { 70 | // self.addr.get_mut_ref::() 71 | // } 72 | 73 | #[inline] 74 | pub fn slice_mut_with_len(&self, len: usize) -> &'static mut [T] { 75 | self.addr.slice_mut_with_len(len) 76 | } 77 | 78 | #[inline] 79 | pub fn slice_until_valid(&self, is_valid: fn(T) -> bool) -> &'static mut [T] { 80 | if self.addr.raw() == 0 { 81 | return &mut []; 82 | } 83 | self.addr.slice_until(is_valid) 84 | } 85 | 86 | #[inline] 87 | pub fn get_cstr(&self) -> Result<&str, core::str::Utf8Error> { 88 | self.addr.get_cstr().to_str() 89 | } 90 | 91 | pub const fn is_null(&self) -> bool { 92 | self.addr.raw() == 0 93 | } 94 | 95 | pub const fn is_valid(&self) -> bool { 96 | !self.is_null() 97 | } 98 | } 99 | 100 | impl Display for UserRef { 101 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 102 | f.write_fmt(format_args!( 103 | "{}({:#x})", 104 | core::any::type_name::(), 105 | self.addr.raw() 106 | )) 107 | } 108 | } 109 | 110 | impl Debug for UserRef { 111 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 112 | f.write_fmt(format_args!( 113 | "{}({:#x})", 114 | core::any::type_name::(), 115 | self.addr.raw() 116 | )) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | profile = "minimal" 3 | # channel = "nightly-2023-03-29" 4 | # channel = "nightly-2024-07-01" 5 | # channel = "nightly-2024-07-15" 6 | channel = "nightly-2025-02-01" 7 | components = [ 8 | "rust-src", 9 | "rustfmt", 10 | "clippy", 11 | "llvm-tools-preview", 12 | "rust-analyzer", 13 | ] 14 | targets = [ 15 | "riscv64imac-unknown-none-elf", 16 | "riscv64gc-unknown-none-elf", 17 | "x86_64-unknown-none", 18 | "aarch64-unknown-none-softfloat", 19 | "loongarch64-unknown-none", 20 | ] 21 | -------------------------------------------------------------------------------- /scripts/cargo.ts: -------------------------------------------------------------------------------- 1 | import { platform } from "./platform.ts"; 2 | 3 | export class Cargo { 4 | configs: Map = new Map(); 5 | rustflags: Array = new Array(0); 6 | env: Map = new Map(); 7 | features = []; 8 | release = true; 9 | 10 | constructor() { 11 | this.rustflags.push("-Clink-arg=-no-pie"); 12 | this.rustflags.push("-Cforce-frame-pointers=yes"); 13 | 14 | this.configs.set("root_fs", "ext4_rs"); 15 | this.configs.set("board", "qemu"); 16 | this.configs.set("driver", "kramdisk"); 17 | 18 | this.env.set("ROOT_MANIFEST_DIR", Deno.cwd() + "/") 19 | this.env.set("MOUNT_IMG_PATH", "mount.img") 20 | this.env.set("HEAP_SIZE", "0x0180_0000") 21 | this.env.set("BOARD", "qemu") 22 | } 23 | 24 | async build() { 25 | const rustflags = this.rustflags; 26 | const args = ["build", "--target", platform.target]; 27 | 28 | if (this.release) args.push("--release"); 29 | 30 | this.configs.forEach((value, key) => { 31 | if (value == undefined) { 32 | rustflags.push(`--cfg=${key}`); 33 | } else { 34 | rustflags.push(`--cfg=${key}="${value}"`) 35 | } 36 | }) 37 | 38 | const buildProc = new Deno.Command("cargo", { 39 | args, 40 | env: { 41 | ...Object.fromEntries(this.env), 42 | RUSTFLAGS: (Deno.env.get('rustflags') || "") + rustflags.join(" ") 43 | }, 44 | }); 45 | const code = await buildProc.spawn().status; 46 | if (!code.success) { 47 | console.error("Failed to build the kernel"); 48 | Deno.exit(1); 49 | } 50 | } 51 | 52 | getTargetPath() { 53 | const mode = this.release ? "release" : "debug"; 54 | return `${Deno.cwd()}/target/${platform.target}/${mode}/kernel`; 55 | } 56 | 57 | getBinPath() { 58 | return `${this.getTargetPath()}.bin`; 59 | } 60 | 61 | async convertBin() { 62 | const objcopyProc = new Deno.Command("rust-objcopy", { 63 | args: [ 64 | `--binary-architecture=${platform.arch}`, 65 | this.getTargetPath(), 66 | "--strip-all", 67 | "-O", 68 | "binary", 69 | this.getBinPath() 70 | ] 71 | }); 72 | await objcopyProc.spawn().status; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /scripts/cli-build.ts: -------------------------------------------------------------------------------- 1 | import { Command, CommandOptions } from "https://deno.land/x/cliffy@v1.0.0-rc.4/command/mod.ts"; 2 | import { globalArgType } from "./cli-types.ts"; 3 | import { Cargo } from "./cargo.ts"; 4 | 5 | 6 | export const cargoBuild = async function (options: CommandOptions) { 7 | 8 | const cargo = new Cargo(); 9 | await cargo.build(); 10 | 11 | console.log("options", options); 12 | } 13 | 14 | export const cliCommand = new Command() 15 | .description("Build Rust Kernel") 16 | .action(cargoBuild); 17 | -------------------------------------------------------------------------------- /scripts/cli-qemu.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.4/command/mod.ts"; 2 | import { globalArgType } from "./cli-types.ts"; 3 | import { platform } from "./platform.ts"; 4 | import { Cargo } from "./cargo.ts"; 5 | 6 | class QemuRunner extends Cargo { 7 | bus: string = "device"; 8 | 9 | constructor() { 10 | super() 11 | if (platform.arch == "x86_64" || platform.arch == "loongarch64") { 12 | this.bus = "pci"; 13 | } 14 | } 15 | 16 | getQemuArchExec(): string[] { 17 | return { 18 | x86_64: [ 19 | "-machine", 20 | "q35", 21 | "-cpu", 22 | "IvyBridge-v2", 23 | "-kernel", 24 | this.getTargetPath(), 25 | ], 26 | riscv64: [ 27 | "-machine", 28 | "virt", 29 | "-kernel", 30 | this.getBinPath() 31 | ], 32 | aarch64: [ 33 | "-cpu", 34 | "cortex-a72", 35 | "-machine", 36 | "virt", 37 | "-kernel", 38 | this.getBinPath() 39 | ], 40 | loongarch64: [ 41 | "-kernel", 42 | this.getTargetPath() 43 | ] 44 | }[platform.arch] ?? []; 45 | } 46 | 47 | async run() { 48 | const qemuCommand = new Deno.Command(`qemu-system-${platform.arch}`, { 49 | args: [ 50 | ...this.getQemuArchExec(), 51 | "-m", 52 | "1G", 53 | "-nographic", 54 | "-smp", 55 | "1", 56 | "-D", 57 | "qemu.log", 58 | "-d", 59 | "in_asm,int,pcall,cpu_reset,guest_errors", 60 | 61 | "-drive", 62 | "file=mount.img,if=none,format=raw,id=x0", 63 | "-device", 64 | `virtio-blk-${this.bus},drive=x0` 65 | ] 66 | }); 67 | await qemuCommand.spawn().status; 68 | } 69 | } 70 | 71 | async function runQemu() { 72 | const cargo = new Cargo(); 73 | await cargo.build(); 74 | 75 | const runner = new QemuRunner(); 76 | await runner.run(); 77 | } 78 | 79 | export const cliCommand = new Command() 80 | .description("Run kernel in the qemu") 81 | .action(runQemu); 82 | -------------------------------------------------------------------------------- /scripts/cli-types.ts: -------------------------------------------------------------------------------- 1 | import { EnumType } from "https://deno.land/x/cliffy@v1.0.0-rc.4/command/mod.ts"; 2 | 3 | export const logLevelEnum = new EnumType(["debug", "info", "warn", "error"]); 4 | export const archEnum = new EnumType(['x86_64', "aarch64", "riscv64", "loongarch64"]); 5 | 6 | export type globalArgType = { 7 | logLevel: typeof logLevelEnum, 8 | platform: typeof String 9 | }; 10 | -------------------------------------------------------------------------------- /scripts/config.mk: -------------------------------------------------------------------------------- 1 | define plat_conf 2 | $(shell yq eval '. |= .global *+ .bin.$(PLATFORM)' $(CONFIG_FILE) 3 | | yq '... comments=""' 4 | | yq -r '.$(strip $1)' $2) 5 | endef 6 | 7 | define spec_conf 8 | $(shell rustc -Z unstable-options --print target-spec-json --target $(TARGET) | yq -r '.$(strip $1)') 9 | endef 10 | 11 | export RUSTFLAGS := -Clink-arg=-no-pie 12 | export LOG := error 13 | 14 | CONFIG_FILE := byteos.yaml 15 | PLATFORM := 16 | TARGET := $(call plat_conf,target) 17 | ARCH := $(call spec_conf,arch) 18 | ROOT_FS := $(call plat_conf,configs.root_fs) 19 | CONFIGS := $(call plat_conf,configs, -o=props | sed 's/ *= */="/' | sed 's/$$/"/') 20 | RUSTFLAGS += $(foreach cfg,$(CONFIGS), --cfg=$(cfg)) 21 | -------------------------------------------------------------------------------- /scripts/platform.ts: -------------------------------------------------------------------------------- 1 | import { parse } from "jsr:@std/yaml"; 2 | 3 | export const platform = { 4 | package: "kernel", 5 | arch: "", 6 | target: "", 7 | configs: [] 8 | }; 9 | 10 | 11 | export function initPlatform(platformStr: string) { 12 | const data: any = parse( 13 | new TextDecoder("utf-8").decode(Deno.readFileSync("byteos.yaml")), 14 | ); 15 | const config = data["bin"][platformStr]; 16 | 17 | platform.target = config["target"]; 18 | platform.arch = platform.target.substring(0, platform.target.indexOf("-")); 19 | platform.configs = config['configs'] 20 | } 21 | -------------------------------------------------------------------------------- /tftp-burn.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | uftpd -n -o ftp=0,tftp=69 . & 3 | 4 | pid=$! 5 | 6 | minicom --color=on -D /dev/ttyUSB0 7 | 8 | kill -9 $pid 9 | --------------------------------------------------------------------------------