浏览代码

feat: rust support

greatbridf 3 月之前
父节点
当前提交
1f7bed61dd
共有 23 个文件被更改,包括 940 次插入97 次删除
  1. 7 0
      .cargo/config.toml
  2. 2 0
      .gitignore
  3. 79 0
      .rustfmt.toml
  4. 18 4
      CMakeLists.txt
  5. 318 0
      Cargo.lock
  6. 16 0
      Cargo.toml
  7. 25 0
      build.rs
  8. 1 1
      global_find.sh
  9. 30 0
      include/kernel/procfs.hpp
  10. 1 0
      rust-toolchain
  11. 137 79
      src/fs/procfs.cc
  12. 42 0
      src/io.rs
  13. 10 2
      src/kernel.ld
  14. 2 0
      src/kernel.rs
  15. 31 9
      src/kernel/allocator.cc
  16. 42 0
      src/kernel/console.rs
  17. 1 0
      src/kernel/mem.rs
  18. 17 0
      src/kernel/mem/paging.cc
  19. 55 0
      src/kernel/mem/paging.rs
  20. 5 0
      src/kernel/process.cpp
  21. 2 2
      src/kernel/vfs/dentry.cc
  22. 84 0
      src/lib.rs
  23. 15 0
      x86_64-unknown-none.json

+ 7 - 0
.cargo/config.toml

@@ -0,0 +1,7 @@
+[build]
+target = 'x86_64-unknown-none.json'
+target-dir = 'build'
+
+[unstable]
+build-std-features = ['compiler-builtins-mem']
+build-std = ['core', 'compiler_builtins', 'alloc']

+ 2 - 0
.gitignore

@@ -19,3 +19,5 @@ cross-compile.cmake
 .Trashes
 
 .gdbinit
+
+src/bindings.rs

+ 79 - 0
.rustfmt.toml

@@ -0,0 +1,79 @@
+max_width = 80
+hard_tabs = false
+tab_spaces = 4
+newline_style = "Auto"
+indent_style = "Block"
+use_small_heuristics = "Default"
+fn_call_width = 60
+attr_fn_like_width = 70
+struct_lit_width = 18
+struct_variant_width = 35
+array_width = 60
+chain_width = 60
+single_line_if_else_max_width = 50
+single_line_let_else_max_width = 50
+wrap_comments = false
+format_code_in_doc_comments = false
+doc_comment_code_block_width = 100
+comment_width = 80
+normalize_comments = false
+normalize_doc_attributes = false
+format_strings = false
+format_macro_matchers = false
+format_macro_bodies = true
+skip_macro_invocations = []
+hex_literal_case = "Preserve"
+empty_item_single_line = true
+struct_lit_single_line = true
+fn_single_line = false
+where_single_line = false
+imports_indent = "Block"
+imports_layout = "Mixed"
+imports_granularity = "Preserve"
+group_imports = "Preserve"
+reorder_imports = true
+reorder_modules = true
+reorder_impl_items = false
+type_punctuation_density = "Wide"
+space_before_colon = false
+space_after_colon = true
+spaces_around_ranges = false
+binop_separator = "Front"
+remove_nested_parens = true
+combine_control_expr = true
+short_array_element_width_threshold = 10
+overflow_delimited_expr = false
+struct_field_align_threshold = 0
+enum_discrim_align_threshold = 0
+match_arm_blocks = true
+match_arm_leading_pipes = "Never"
+force_multiline_blocks = false
+fn_params_layout = "Tall"
+brace_style = "SameLineWhere"
+control_brace_style = "AlwaysSameLine"
+trailing_semicolon = true
+trailing_comma = "Vertical"
+match_block_trailing_comma = false
+blank_lines_upper_bound = 1
+blank_lines_lower_bound = 0
+edition = "2015"
+version = "One"
+inline_attribute_width = 0
+format_generated_files = true
+generated_marker_line_search_limit = 5
+merge_derives = true
+use_try_shorthand = false
+use_field_init_shorthand = false
+force_explicit_abi = true
+condense_wildcard_suffixes = false
+color = "Auto"
+required_version = "1.7.1"
+unstable_features = false
+disable_all_formatting = false
+skip_children = false
+show_parse_errors = true
+error_on_line_overflow = false
+error_on_unformatted = false
+ignore = []
+emit_mode = "Files"
+make_backup = false

+ 18 - 4
CMakeLists.txt

@@ -9,16 +9,20 @@ set(CMAKE_CXX_LINK_EXECUTABLE
 set(C_CXX_FLAGS "-nostdinc -nostdlib -W -Wall -Wextra -Wno-stringop-overflow -Wno-builtin-declaration-mismatch -Wno-format -fverbose-asm -fno-exceptions -ffreestanding -fno-pic -mno-red-zone -mstack-protector-guard=global -mcmodel=kernel")
 set(CMAKE_C_FLAGS "${C_CXX_FLAGS} -Werror=implicit-int -Werror=implicit-function-declaration -Werror=strict-aliasing")
 set(CMAKE_CXX_FLAGS "${C_CXX_FLAGS} -fno-use-cxa-atexit -fno-rtti")
-set(CMAKE_CXX_LINK_FLAGS "")
+set(CMAKE_CXX_LINK_FLAGS "-z noexecstack --gc-sections")
 SET(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
 set(CMAKE_CXX_STANDARD 20)
 
 if (CMAKE_BUILD_TYPE STREQUAL "Debug")
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEBUG -O0 -g")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG -O0 -g")
+    set(CARGO_BUILD_TYPE "debug")
+    set(CARGO_BUILD_FLAGS "")
 elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -g -DNDEBUG")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -g -DNDEBUG")
+    set(CARGO_BUILD_TYPE "release")
+    set(CARGO_BUILD_FLAGS "--release")
 endif()
 
 if (NOT DEFINED FDISK_BIN)
@@ -125,11 +129,21 @@ set(KERNEL_MAIN_SOURCES src/dev/builtin-chardev.cc
                         )
 
 add_executable(kernel.out ${KERNEL_MAIN_SOURCES} ${BOOTLOADER_SOURCES})
-target_link_libraries(kernel.out gblibc gblibstdc++)
+add_dependencies(kernel.out rustpart)
+target_link_libraries(kernel.out gblibc gblibstdc++ gbos_rust_part)
 target_include_directories(kernel.out PRIVATE ${PROJECT_SOURCE_DIR}/include)
 target_link_options(kernel.out PRIVATE
-    -T ${CMAKE_SOURCE_DIR}/src/kernel.ld -lgblibc -L${CMAKE_BINARY_DIR}/gblibc)
-set_target_properties(kernel.out PROPERTIES LINK_DEPENDS ${CMAKE_SOURCE_DIR}/src/kernel.ld)
+    -T "${CMAKE_SOURCE_DIR}/src/kernel.ld"
+    -L "${CMAKE_BINARY_DIR}/x86_64-unknown-none/${CARGO_BUILD_TYPE}"
+    )
+set_target_properties(kernel.out PROPERTIES LINK_DEPENDS "${CMAKE_SOURCE_DIR}/src/kernel.ld")
+set_source_files_properties(src/mbr.S PROPERTIES OBJECT_DEPENDS
+    "${CMAKE_BINARY_DIR}/x86_64-unknown-none/${CARGO_BUILD_TYPE}/libgbos_rust_part.a"
+    )
+
+add_custom_target(rustpart
+    COMMAND cargo build ${CARGO_BUILD_FLAGS}
+)
 
 add_custom_command(OUTPUT mbr_hole.bin
     DEPENDS kernel.out

+ 318 - 0
Cargo.lock

@@ -0,0 +1,318 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "bindgen"
+version = "0.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "itertools",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clang-sys"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "gbos-rust-part"
+version = "0.1.0"
+dependencies = [
+ "bindgen",
+ "spin",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.158"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+
+[[package]]
+name = "libloading"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
+dependencies = [
+ "cfg-if",
+ "windows-targets",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "prettyplease"
+version = "0.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

+ 16 - 0
Cargo.toml

@@ -0,0 +1,16 @@
+[package]
+name = "gbos-rust-part"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["staticlib"]
+
+[dependencies]
+spin = "0.9.8"
+
+[build-dependencies]
+bindgen = "0.70.1"
+
+[profile.dev]
+panic = "abort"

+ 25 - 0
build.rs

@@ -0,0 +1,25 @@
+fn main() {
+    println!("cargo:rustc-link-search=native=./build/gblibstdc++");
+    println!("cargo:rustc-link-lib=static=gblibstdc++");
+
+    let headers = ["include/kernel/process.hpp"];
+
+    let bindings = bindgen::Builder::default()
+        .use_core()
+        .ctypes_prefix("core::ffi")
+        .headers(headers)
+        .clang_arg("-I./gblibstdc++/include")
+        .clang_arg("-I./gblibc/include")
+        .clang_arg("-I./include")
+        .clang_arg("-std=c++20")
+        .opaque_type("std::.*")
+        .enable_cxx_namespaces()
+        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
+        .generate()
+        .expect("Unable to generate bindings");
+
+    let out_path = std::path::PathBuf::from(std::env::var("PWD").unwrap());
+    bindings
+        .write_to_file(out_path.join("src/bindings.rs"))
+        .expect("Couldn't write bindings!");
+}

+ 1 - 1
global_find.sh

@@ -9,4 +9,4 @@ do_find()
     done
 }
 
-do_find "$1" "c h cpp hpp s cc"
+do_find "$1" "c h cpp hpp s cc rs"

+ 30 - 0
include/kernel/procfs.hpp

@@ -0,0 +1,30 @@
+#pragma once
+
+#include <defs.hpp>
+#include <string>
+#include <vector>
+
+#include <sys/types.h>
+
+namespace kernel::procfs {
+
+using read_fn = std::function<isize(u8*, usize)>;
+using write_fn = std::function<isize(const u8*, usize)>;
+
+struct procfs_file {
+    std::string name;
+    ino_t ino;
+
+    read_fn read;
+    write_fn write;
+    std::vector<procfs_file>* children;
+};
+
+const procfs_file* root();
+
+const procfs_file* find(const procfs_file* parent, std::string name);
+const procfs_file* mkdir(const procfs_file* parent, std::string name);
+const procfs_file* create(const procfs_file* parent, std::string name,
+                          read_fn read, write_fn write);
+
+} // namespace kernel::procfs

+ 1 - 0
rust-toolchain

@@ -0,0 +1 @@
+nightly

+ 137 - 79
src/fs/procfs.cc

@@ -3,15 +3,23 @@
 #include <errno.h>
 #include <stdint.h>
 #include <sys/mount.h>
+#include <sys/types.h>
 #include <unistd.h>
 
+#include <kernel/async/lock.hpp>
 #include <kernel/hw/timer.hpp>
+#include <kernel/mem/paging.hpp>
+#include <kernel/mem/phys.hpp>
 #include <kernel/module.hpp>
 #include <kernel/process.hpp>
+#include <kernel/procfs.hpp>
 #include <kernel/vfs.hpp>
+#include <kernel/vfs/inode.hpp>
 #include <kernel/vfs/vfs.hpp>
 
 using namespace kernel::kmod;
+using namespace kernel::procfs;
+using fs::inode, fs::make_device;
 
 struct mount_flags_opt {
     unsigned long flag;
@@ -40,162 +48,212 @@ static std::string get_mount_opts(unsigned long mnt_flags) {
     return retval;
 }
 
-static ssize_t mounts_read(char* page, size_t n) {
-    auto orig_n = n;
+static isize mounts_read(u8* page, usize bufsize) {
+    auto orig_bufsize = bufsize;
 
     for (const auto& [_, mdata] : fs::mounts) {
-        if (n == 0)
-            break;
-
         // TODO: get vfs options
         auto mount_flags = get_mount_opts(mdata.flags);
 
-        int nwrote = snprintf(page, n, "%s %s %s %s 0 0\n",
+        isize nwrote = snprintf((char*)page, bufsize, "%s %s %s %s 0 0\n",
                               mdata.source.c_str(), mdata.mount_point.c_str(),
                               mdata.fstype.c_str(), mount_flags.c_str());
+        if (nwrote < 0)
+            return nwrote;
+
+        assert((usize)nwrote < bufsize);
 
-        n -= nwrote;
+        bufsize -= nwrote;
         page += nwrote;
     }
 
-    return orig_n - n;
+    return orig_bufsize - bufsize;
 }
 
-static ssize_t schedstat_read(char* page, size_t n) {
-    auto orig_n = n;
+static isize schedstat_read(u8* page, usize bufsize) {
+    auto orig_bufsize = bufsize;
 
-    if (n == 0)
-        return n;
+    int nw = snprintf((char*)page, bufsize, "%d\n",
+                      kernel::hw::timer::current_ticks());
+    if (nw < 0)
+        return nw;
 
-    int nw = snprintf(page, n, "%d\n", kernel::hw::timer::current_ticks());
-    n -= nw, page += nw;
+    assert((usize)nw < bufsize);
+    bufsize -= nw, page += nw;
 
     for (const auto& proc : *procs) {
         for (const auto& thd : proc.second.thds) {
-            int nwrote = snprintf(page, n, "%d %x %d\n", proc.first, thd.tid(),
-                                  thd.elected_times);
+            int nwrote = snprintf((char*)page, bufsize, "%d %x %d\n",
+                                  proc.first, thd.tid(), thd.elected_times);
+            if (nwrote < 0)
+                return nwrote;
 
-            n -= nwrote;
+            assert((usize)nwrote < bufsize);
+
+            bufsize -= nwrote;
             page += nwrote;
         }
     }
 
-    return orig_n - n;
+    return orig_bufsize - bufsize;
 }
 
-namespace fs::proc {
+namespace kernel::procfs {
 
-struct proc_file {
-    std::string name;
+static procfs_file* s_root;
+static ino_t s_next_ino = 1;
 
-    ssize_t (*read)(char* page_buffer, size_t n);
-    ssize_t (*write)(const char* data, size_t n);
-};
+static mode_t _get_mode(const procfs_file& file) {
+    if (file.children)
+        return S_IFDIR | 0755;
+
+    mode_t mode = S_IFREG;
+    if (file.read)
+        mode |= 0444;
+    if (file.write)
+        mode |= 0200;
+
+    return mode;
+}
 
 class procfs : public virtual fs::vfs {
    private:
     std::string source;
-    std::map<ino_t, proc_file> files;
-
-    ino_t free_ino = 1;
 
     procfs(const char* _source)
         : vfs(make_device(0, 10), 4096), source{_source} {
-        auto* ind = alloc_inode(0);
-        ind->mode = S_IFDIR | 0777;
+        assert(s_root);
 
-        create_file("mounts", mounts_read, nullptr);
-        create_file("schedstat", schedstat_read, nullptr);
+        auto* ind = alloc_inode(s_root->ino);
+        ind->fs_data = s_root;
+        ind->mode = _get_mode(*s_root);
 
         register_root_node(ind);
     }
 
    public:
-    static procfs* create(const char* source, unsigned long, const void*) {
-        // TODO: flags
-        return new procfs(source);
-    }
+    static int init() {
+        auto children = std::make_unique<std::vector<procfs_file>>();
+        auto procfsFile = std::make_unique<procfs_file>();
 
-    int create_file(std::string name, ssize_t (*read_func)(char*, size_t),
-                    ssize_t (*write_func)(const char*, size_t)) {
-        auto ino = free_ino++;
+        procfsFile->name = "[root]";
+        procfsFile->ino = 0;
+        procfsFile->read = [](u8*, usize) { return -EISDIR; };
+        procfsFile->write = [](const u8*, usize) { return -EISDIR; };
+        procfsFile->children = children.release();
+        s_root = procfsFile.release();
 
-        auto [_, inserted] =
-            files.insert({ino, proc_file{name, read_func, write_func}});
+        kernel::procfs::create(s_root, "mounts", mounts_read, nullptr);
+        kernel::procfs::create(s_root, "schedstat", schedstat_read, nullptr);
 
-        auto* ind = alloc_inode(ino);
-        ind->mode = S_IFREG | 0666;
+        return 0;
+    }
 
-        return inserted ? 0 : -EEXIST;
+    static procfs* create(const char* source, unsigned long, const void*) {
+        return new procfs(source);
     }
 
     ssize_t read(inode* file, char* buf, size_t buf_size, size_t n,
                  off_t offset) override {
-        if (file->ino == 0)
-            return -EISDIR;
+        if (offset < 0)
+            return -EINVAL;
 
-        auto iter = files.find(file->ino);
-        if (!iter)
-            return -EIO;
+        n = std::min(n, buf_size);
 
-        auto& [ino, pf] = *iter;
+        auto pFile = (procfs_file*)file->fs_data;
 
-        if (!pf.read)
-            return -EINVAL;
+        if (!pFile->read)
+            return -EACCES;
+        if (pFile->children)
+            return -EISDIR;
 
-        // TODO: fix it
-        if (offset)
-            return 0;
+        using namespace kernel::mem;
+        using namespace kernel::mem::paging;
+        auto* page = alloc_page();
+        auto pPageBuffer = physaddr<u8>(page_to_pfn(page));
 
-        // TODO: allocate page buffer
-        char* page_buffer = new char[4096];
+        ssize_t nread = pFile->read(pPageBuffer, 4096);
+        if (nread < offset) {
+            free_page(page);
 
-        ssize_t nread = pf.read(page_buffer, 4096);
-        if (nread < 0) {
-            delete[] page_buffer;
-            return nread;
+            return nread < 0 ? nread : 0;
         }
 
-        n = std::min(n, buf_size);
-        n = std::min(n, (size_t)nread);
-
-        strncpy(buf, page_buffer, n);
+        n = std::min(n, (usize)nread - offset);
+        memcpy(buf, pPageBuffer + offset, n);
 
-        delete[] page_buffer;
+        free_page(page);
         return n;
     }
 
     ssize_t readdir(inode* dir, size_t offset,
                     const filldir_func& callback) override {
-        if (dir->ino != 0)
+        auto* pFile = (procfs_file*)dir->fs_data;
+        if (!pFile->children)
             return -ENOTDIR;
 
-        // TODO: fix it
-        if (offset)
-            return 0;
+        const auto& children = *pFile->children;
 
-        int nread = 0;
-        for (const auto& [ino, pf] : files) {
-            auto* ind = get_inode(ino);
-            int ret = callback(pf.name.c_str(), ind);
+        usize cur = offset;
+        for (; cur < pFile->children->size(); ++cur) {
+            auto& file = children[cur];
+            auto* inode = get_inode(file.ino);
+            if (!inode) {
+                inode = alloc_inode(file.ino);
+
+                inode->fs_data = (void*)&file;
+                inode->mode = _get_mode(file);
+            }
+
+            int ret = callback(file.name.c_str(), inode, 0);
             if (ret != 0)
-                return -EIO;
-            ++nread;
+                break;
         }
 
-        return nread;
+        return cur - offset;
     }
 };
 
-class procfs_module : public virtual kmod {
+const procfs_file* root() { return s_root; }
+
+const procfs_file* create(const procfs_file* parent, std::string name,
+                          read_fn read, write_fn write) {
+    auto& file = parent->children->emplace_back();
+
+    file.name = std::move(name);
+    file.ino = s_next_ino++;
+    file.read = std::move(read);
+    file.write = std::move(write);
+    file.children = nullptr;
+
+    return 0;
+}
+
+const procfs_file* mkdir(const procfs_file* parent, std::string name) {
+    auto& file = parent->children->emplace_back();
+
+    file.name = std::move(name);
+    file.ino = s_next_ino++;
+    file.read = nullptr;
+    file.write = nullptr;
+    file.children = new std::vector<procfs_file>();
+
+    return &file;
+}
+
+class procfs_module : public virtual kernel::kmod::kmod {
    public:
     procfs_module() : kmod("procfs") {}
 
     virtual int init() override {
+        int ret = procfs::init();
+        if (ret < 0)
+            return ret;
+
         return fs::register_fs("procfs", procfs::create);
     }
 };
 
-} // namespace fs::proc
+} // namespace kernel::procfs
 
-INTERNAL_MODULE(procfs, fs::proc::procfs_module);
+INTERNAL_MODULE(procfs, procfs_module);

+ 42 - 0
src/io.rs

@@ -0,0 +1,42 @@
+use core::ffi::{c_size_t, c_uchar};
+
+pub struct Buffer {
+    buf: *mut c_uchar,
+    size: usize,
+    rem: usize,
+}
+
+impl Buffer {
+    pub fn new(buf: *mut c_uchar, _size: c_size_t) -> Self {
+        let size = _size as usize;
+        Self {
+            buf,
+            size,
+            rem: size,
+        }
+    }
+
+    pub fn count(&self) -> usize {
+        self.size - self.rem
+    }
+}
+
+use core::fmt::Write;
+impl Write for Buffer {
+    fn write_str(&mut self, s: &str) -> core::fmt::Result {
+        let s = s.as_bytes();
+        let len = s.len();
+
+        if self.rem <= len {
+            return Err(core::fmt::Error);
+        }
+
+        unsafe {
+            core::ptr::copy_nonoverlapping(s.as_ptr(), self.buf, len);
+            self.buf = self.buf.add(len);
+        }
+        self.rem -= len;
+
+        Ok(())
+    }
+}

+ 10 - 2
src/kernel.ld

@@ -14,7 +14,7 @@ SECTIONS
 {
     .mbr : AT(0)
     {
-        *(.mbr)
+        KEEP(*(.mbr));
 
         . = 510;
         BYTE(0x55);
@@ -89,9 +89,15 @@ SECTIONS
         . = ALIGN(16);
         KMOD_LOADERS_START = .;
 
-        *(.kmods)
+        KEEP(*(.kmods));
         QUAD(0);
 
+        . = ALIGN(16);
+        late_init_start = .;
+        KEEP(*(.late_init));
+        QUAD(0);
+        late_init_end = .;
+
         . = ALIGN(16);
 
         BSS_ADDR = .;
@@ -174,11 +180,13 @@ SECTIONS
     .debug_funcnames 0 : { *(.debug_funcnames) }
     .debug_typenames 0 : { *(.debug_typenames) }
     .debug_varnames  0 : { *(.debug_varnames) }
+    /* Rust stuff */
 
     /DISCARD/ :
     {
         *(.fini_array*)
         *(.note*)
         *(.dtors*)
+        *(.debug_gdb_scripts*)
     }
 }

+ 2 - 0
src/kernel.rs

@@ -0,0 +1,2 @@
+pub mod console;
+pub mod mem;

+ 31 - 9
src/kernel/allocator.cc

@@ -232,7 +232,7 @@ void kernel::kinit::init_allocator() {
         (std::byte*)KERNEL_HEAP_START, KERNEL_HEAP_SIZE);
 }
 
-void* operator new(size_t size) {
+extern "C" void* _do_allocate(uintptr_t size) {
     int idx = __cache_index(size);
     void* ptr = nullptr;
     if (idx < 0)
@@ -240,10 +240,37 @@ void* operator new(size_t size) {
     else
         ptr = kernel::mem::slab_alloc(&caches[idx]);
 
-    assert(ptr);
     return ptr;
 }
 
+// return 0 if deallocate successfully
+// return -1 if ptr is nullptr
+// return -2 if size is not correct for slab allocated memory
+extern "C" int32_t _do_deallocate(void* ptr, uintptr_t size) {
+    if (!ptr)
+        return -1;
+
+    if (types::memory::k_alloc->allocated(ptr)) {
+        types::memory::k_alloc->deallocate(ptr);
+        return 0;
+    }
+
+    int idx = __cache_index(size);
+    if (idx < 0)
+        return -2;
+
+    kernel::mem::slab_free(ptr);
+
+    return 0;
+}
+
+void* operator new(size_t size) {
+    auto* ret = _do_allocate(size);
+    assert(ret);
+
+    return ret;
+}
+
 void operator delete(void* ptr) {
     if (!ptr)
         return;
@@ -258,14 +285,9 @@ void operator delete(void* ptr, std::size_t size) {
     if (!ptr)
         return;
 
-    if (types::memory::k_alloc->allocated(ptr)) {
-        types::memory::k_alloc->deallocate(ptr);
-        return;
-    }
-    int idx = __cache_index(size);
-    assert(idx >= 0);
+    int ret = _do_deallocate(ptr, size);
 
-    kernel::mem::slab_free(ptr);
+    assert(ret == 0);
 }
 
 void* operator new[](size_t sz) {

+ 42 - 0
src/kernel/console.rs

@@ -0,0 +1,42 @@
+pub struct Console {}
+
+use core::fmt::Write;
+impl Write for Console {
+    fn write_str(&mut self, s: &str) -> core::fmt::Result {
+        use crate::bindings::root::kernel::tty::console as _console;
+
+        if let Some(console) = unsafe { _console.as_mut() } {
+            for &ch in s.as_bytes() {
+                unsafe {
+                    console.show_char(ch as i32);
+                }
+            }
+        }
+
+        Ok(())
+    }
+}
+
+#[doc(hidden)]
+pub fn _print(args: core::fmt::Arguments) -> core::fmt::Result {
+    CONSOLE.lock().write_fmt(args)
+}
+
+pub static CONSOLE: spin::Mutex<Console> = spin::Mutex::new(Console {});
+
+#[macro_export]
+macro_rules! print {
+    ($($arg:tt)*) => {
+        $crate::kernel::console::_print(format_args!($($arg)*))
+    };
+}
+
+#[macro_export]
+macro_rules! println {
+    () => {
+        $crate::print!("\n")
+    };
+    ($($arg:tt)*) => {
+        $crate::print!("{}\n", format_args!($($arg)*))
+    };
+}

+ 1 - 0
src/kernel/mem.rs

@@ -0,0 +1 @@
+pub mod paging;

+ 17 - 0
src/kernel/mem/paging.cc

@@ -10,6 +10,7 @@
 #include <kernel/mem/slab.hpp>
 #include <kernel/mem/vm_area.hpp>
 #include <kernel/process.hpp>
+#include <kernel/procfs.hpp>
 
 using namespace types::list;
 
@@ -421,3 +422,19 @@ vaddr_range::operator bool() const noexcept {
 bool vaddr_range::operator==(const vaddr_range& other) const noexcept {
     return n == other.n;
 }
+
+extern "C" isize real_dump_buddy(const zone_info* zones, u8* buf,
+                                 usize buf_size);
+
+static isize _dump_buddy(u8* buf, usize buf_size) {
+    return real_dump_buddy(zones, buf, buf_size);
+}
+
+static void _init_procfs_files() {
+    auto* root = kernel::procfs::root();
+
+    kernel::procfs::create(root, "buddyinfo", _dump_buddy, nullptr);
+}
+
+__attribute__((used))
+SECTION(".late_init") void (*const _paging_late_init)() = _init_procfs_files;

+ 55 - 0
src/kernel/mem/paging.rs

@@ -0,0 +1,55 @@
+use crate::io::Buffer;
+
+#[repr(C)]
+struct ZoneInfo {
+    head: *mut core::ffi::c_void,
+    count: core::ffi::c_size_t,
+}
+
+fn do_dump_buddy(
+    zones: &[ZoneInfo],
+    buffer: &mut Buffer,
+) -> Result<usize, core::fmt::Error> {
+    let maxi = {
+        let mut maxi = 0;
+        for (idx, zone) in zones.iter().enumerate() {
+            if zone.count > 0 {
+                maxi = idx;
+            }
+        }
+        maxi
+    };
+
+    use core::fmt::Write;
+    write!(buffer, "Order")?;
+
+    for idx in 0..=maxi {
+        write!(buffer, "\t{}", idx)?;
+    }
+
+    write!(buffer, "\nCount")?;
+
+    for zone in zones.iter().take(maxi + 1) {
+        write!(buffer, "\t{}", zone.count)?;
+    }
+
+    write!(buffer, "\n")?;
+
+    Ok(buffer.count())
+}
+
+#[no_mangle]
+extern "C" fn real_dump_buddy(
+    zones: *const ZoneInfo,
+    buf: *mut core::ffi::c_uchar,
+    buf_size: core::ffi::c_size_t,
+) -> core::ffi::c_ssize_t {
+    let zones = unsafe { core::slice::from_raw_parts(zones, 52) };
+    let mut buffer = Buffer::new(buf, buf_size);
+
+    use crate::bindings::root::ENOMEM;
+    match do_dump_buddy(zones, &mut buffer) {
+        Ok(size) => size as core::ffi::c_ssize_t,
+        Err(_) => -(ENOMEM as core::ffi::c_ssize_t),
+    }
+}

+ 5 - 0
src/kernel/process.cpp

@@ -204,6 +204,8 @@ static void release_kinit() {
     create_zone(0x2000, 0x2000 + 0x1000 * pages);
 }
 
+extern "C" void (*const late_init_start[])();
+
 void NORETURN _kernel_init(kernel::mem::paging::pfn_t kernel_stack_pfn) {
     kernel::mem::paging::free_pages(kernel_stack_pfn, 9);
     release_kinit();
@@ -228,6 +230,9 @@ void NORETURN _kernel_init(kernel::mem::paging::pfn_t kernel_stack_pfn) {
     // interrupt enabled
     // ------------------------------------------
 
+    for (auto* init = late_init_start; *init; ++init)
+        (*init)();
+
     const auto& context = current_process->fs_context;
 
     // mount fat32 /mnt directory

+ 2 - 2
src/kernel/vfs/dentry.cc

@@ -93,14 +93,14 @@ static inline int __d_load(struct dentry* parent) {
     while (true) {
         ssize_t off = fs->readdir(
             inode, offset,
-            [parent](const char* fn, struct inode* inode, uint8_t mode) -> int {
+            [parent](const char* fn, struct inode* inode, u8) -> int {
                 struct dentry* dentry = dcache_alloc(parent->cache);
 
                 dentry->fs = inode->fs;
                 dentry->inode = inode;
                 dentry->name = fn;
 
-                if (S_ISDIR(mode))
+                if (S_ISDIR(inode->mode))
                     dentry->flags = D_PRESENT | D_DIRECTORY;
                 else
                     dentry->flags = D_PRESENT;

+ 84 - 0
src/lib.rs

@@ -0,0 +1,84 @@
+#![no_std]
+#![no_main]
+#![feature(c_size_t)]
+#![feature(concat_idents)]
+extern crate alloc;
+
+#[allow(warnings)]
+mod bindings;
+
+mod io;
+mod kernel;
+
+macro_rules! dont_check {
+    ($arg:expr) => {
+        match $arg {
+            Ok(_) => (),
+            Err(_) => (),
+        }
+    };
+}
+
+pub(crate) use dont_check;
+
+#[panic_handler]
+fn panic(info: &core::panic::PanicInfo) -> ! {
+    dont_check!(println!("[kernel] panic: {:?}", info));
+
+    unsafe { bindings::root::freeze() };
+}
+
+extern "C" {
+    fn _do_allocate(size: usize) -> *mut core::ffi::c_void;
+    fn _do_deallocate(
+        ptr: *mut core::ffi::c_void,
+        size: core::ffi::c_size_t,
+    ) -> i32;
+}
+
+use core::alloc::{GlobalAlloc, Layout};
+
+struct Allocator {}
+unsafe impl GlobalAlloc for Allocator {
+    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+        let result = _do_allocate(layout.size());
+
+        if result.is_null() {
+            core::ptr::null_mut()
+        } else {
+            result as *mut u8
+        }
+    }
+
+    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+        match _do_deallocate(ptr as *mut core::ffi::c_void, layout.size()) {
+            0 => (),
+            _ => panic!("Failed to deallocate memory"),
+        }
+    }
+}
+
+#[global_allocator]
+static ALLOCATOR: Allocator = Allocator {};
+
+#[repr(C)]
+#[allow(dead_code)]
+struct Fp {
+    fp: *const core::ffi::c_void,
+}
+
+unsafe impl Sync for Fp {}
+
+#[allow(unused_macros)]
+macro_rules! late_init {
+    ($name:ident, $func:ident) => {
+        #[used]
+        #[link_section = ".late_init"]
+        static $name: $crate::Fp = $crate::Fp {
+            fp: $func as *const core::ffi::c_void,
+        };
+    };
+}
+
+#[allow(unused_imports)]
+pub(crate) use late_init;

+ 15 - 0
x86_64-unknown-none.json

@@ -0,0 +1,15 @@
+{
+    "llvm-target": "x86_64-unknown-none",
+    "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
+    "arch": "x86_64",
+    "target-endian": "little",
+    "target-pointer-width": "64",
+    "target-c-int-width": "32",
+    "os": "none",
+    "executables": true,
+    "linker-flavor": "ld.lld",
+    "linker": "rust-lld",
+    "panic-strategy": "abort",
+    "disable-redzone": true,
+    "features": "-mmx,-sse,+soft-float"
+}