diff --git a/.github/workflows/actions/setup-musl/action.yml b/.github/workflows/actions/setup-musl/action.yml index b8aee3661..fc6cb7913 100644 --- a/.github/workflows/actions/setup-musl/action.yml +++ b/.github/workflows/actions/setup-musl/action.yml @@ -20,8 +20,8 @@ runs: shell: bash run: | MUSL_PATH=${{ inputs.arch }}-linux-musl-cross - wget https://musl.cc/${MUSL_PATH}.tgz - tar -xf ${MUSL_PATH}.tgz + git clone -b ${{ inputs.arch }} https://github.com/syswonder/ruxos-musl-toolchain.git + tar -xf ruxos-musl-toolchain/${MUSL_PATH}.tgz - uses: actions/cache/save@v3 if: steps.cache-musl.outputs.cache-hit != 'true' with: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fb1a97e37..4812f19dd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,11 +3,11 @@ name: Build CI on: [push, pull_request] env: - rust-toolchain: nightly-2023-09-01 + rust-toolchain: nightly-2025-05-07 jobs: clippy: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false steps: @@ -37,7 +37,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-22.04] arch: [x86_64, riscv64, aarch64] steps: - uses: actions/checkout@v3 @@ -51,7 +51,7 @@ jobs: - uses: actions-rs/install@v0.1 with: crate: cargo-binutils - version: latest + version: 0.3.5 use-tool-cache: true - name: Install gcc multilib run: sudo apt update && sudo apt install -y gcc-multilib @@ -66,7 +66,7 @@ jobs: - name: Build display/draw_map run: make ARCH=${{ matrix.arch }} A=apps/display/draw_map - name: Build fs/shell - run: make ARCH=${{ matrix.arch }} A=apps/fs/shell + run: make ARCH=${{ matrix.arch }} A=apps/fs/shell FEATURES=fatfs - name: Build c/helloworld run: make ARCH=${{ matrix.arch }} A=apps/c/helloworld @@ -83,18 +83,18 @@ jobs: - name: Build c/iperf run: | git clone https://github.com/syswonder/rux-iperf ./apps/c/iperf/ \ - && make ARCH=${{ matrix.arch }} A=apps/c/iperf + && make ARCH=${{ matrix.arch }} A=apps/c/iperf FEATURES=fatfs - name: Build c/redis run: | git clone https://github.com/syswonder/rux-redis ./apps/c/redis/ \ - && make ARCH=${{ matrix.arch }} A=apps/c/redis SMP=4 + && make ARCH=${{ matrix.arch }} A=apps/c/redis SMP=4 FEATURES=fatfs build-apps-for-std: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-22.04] arch: [x86_64] steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/commit.yml b/.github/workflows/commit.yml new file mode 100644 index 000000000..0f55f9ca3 --- /dev/null +++ b/.github/workflows/commit.yml @@ -0,0 +1,122 @@ +name: Custom Commit Message Validator + +on: + pull_request: + types: [opened, reopened, synchronize] + +jobs: + validate_COMMIT_MESSAGEs: + runs-on: ubuntu-latest + + permissions: + contents: read + pull-requests: read + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get PR Commits SHAs + id: get_pr_commits + run: | + # Fetch all commit SHAs associated with the current Pull Request + PR_COMMIT_SHAS=$(gh pr view ${{ github.event.pull_request.number }} --json commits --jq '[.commits[].oid] | join(" ")') + echo "PR_COMMITS_LIST=${PR_COMMIT_SHAS}" >> "$GITHUB_OUTPUT" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Validate Each Commit Message + env: + TARGET_DATE: "2025-07-22" # Only check commits after this date + SKIP_KEYWORDS: "Merge,Revert,Release" # Comma-separated keywords to skip validation + run: | + # Read commit SHAs into array + IFS=' ' read -r -a commit_shas <<< "${{ steps.get_pr_commits.outputs.PR_COMMITS_LIST }}" + has_validation_failed=false + + # Convert skip keywords to array + IFS=',' read -r -a skip_keywords <<< "$SKIP_KEYWORDS" + + for commit_sha in "${commit_shas[@]}"; do + echo "--- Checking commit: ${commit_sha} ---" + + # Get commit timestamps for date filtering + COMMIT_DATE_UNIX=$(git log -n 1 --format=%at "${commit_sha}") + TARGET_DATE_UNIX=$(date -d "${TARGET_DATE}" +"%s") + COMMIT_DATE_HUMAN=$(git log -n 1 --format=%ad --date=iso-strict "${commit_sha}") + + # Skip older commits + if (( COMMIT_DATE_UNIX < TARGET_DATE_UNIX )); then + echo "Skipping commit ${commit_sha} (Date: ${COMMIT_DATE_HUMAN}) as it is older than ${TARGET_DATE}." + continue + fi + + echo "Processing commit ${commit_sha} (Date: ${COMMIT_DATE_HUMAN}) as it is on or after ${TARGET_DATE}." + COMMIT_MESSAGE=$(git log --format=%B -n 1 "${commit_sha}") + echo "Message content:" + echo "${COMMIT_MESSAGE}" + echo "" + + # Check for skip keywords + skip_this_commit=false + for keyword in "${skip_keywords[@]}"; do + # Trim whitespace from keyword + trimmed_keyword=$(echo "$keyword" | xargs) + + if [[ -n "$trimmed_keyword" ]] && + [[ "${COMMIT_MESSAGE}" == *"$trimmed_keyword"* ]]; then + echo "Skipping validation for commit ${commit_sha} due to skip keyword: ${trimmed_keyword}" + skip_this_commit=true + break + fi + done + + if [ "$skip_this_commit" = true ]; then + continue + fi + + # --- Validation Logic Start --- + + # 1. Check Conventional Commit format + if [[ ! "${COMMIT_MESSAGE}" =~ ^(feat|fix|docs|chore|style|refactor|perf|test|build|ci|revert)(\([a-zA-Z0-9_-]+\))?(!?): ]]; then + echo "::error file=COMMIT_MESSAGE::Commit ${commit_sha} does not start with a conventional commit type. Message: '${COMMIT_MESSAGE}'" + has_validation_failed=true + continue + fi + + # Extract commit subject (first line) + commit_subject=$(echo "${COMMIT_MESSAGE}" | head -n 1) + + # 2. NEW: Check minimum subject length (15 characters) + if [[ ${#commit_subject} -lt 15 ]]; then + echo "::error file=COMMIT_MESSAGE::Commit ${commit_sha} subject is too short (min 15 characters). Length: ${#commit_subject}. Subject: '${commit_subject}'" + has_validation_failed=true + continue + fi + + # 3. Check maximum subject length (72 characters) + if [[ ${#commit_subject} -gt 72 ]]; then + echo "::warning file=COMMIT_MESSAGE::Commit ${commit_sha} subject line exceeds 72 characters. Length: ${#commit_subject}. Subject: '${commit_subject}'" + fi + + # 4. Check empty line between subject and body + if [[ $(echo "${COMMIT_MESSAGE}" | wc -l) -gt 1 ]]; then + second_line=$(echo "${COMMIT_MESSAGE}" | awk 'NR==2 {print}' | xargs) + if [[ -n "${second_line}" ]]; then + echo "::error file=COMMIT_MESSAGE::Commit ${commit_sha} is missing an empty line between subject and body. Message: '${COMMIT_MESSAGE}'" + has_validation_failed=true + continue + fi + fi + + done + + # Fail job if any validation errors occurred + if [ "$has_validation_failed" = true ]; then + echo "::error::One or more commit messages failed validation. Please review the errors above." + exit 1 + fi + + echo "All relevant commit messages in the PR passed validation." diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 68e607319..2c5dacdd0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -3,11 +3,11 @@ name: Build & Deploy docs on: [push, pull_request] env: - rust-toolchain: nightly-2023-09-01 + rust-toolchain: nightly-2025-05-07 jobs: doc: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false permissions: diff --git a/.github/workflows/license.yml b/.github/workflows/license.yml new file mode 100644 index 000000000..fc1bd3755 --- /dev/null +++ b/.github/workflows/license.yml @@ -0,0 +1,25 @@ +name: License Header Check + +on: + pull_request: + types: [opened, reopened, synchronize] # Triggers on PR open, reopen, or new commits pushed to the PR branch + +jobs: + check_license_headers: + runs-on: ubuntu-latest # Specifies the runner environment + + permissions: + contents: read # Needed for checkout + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 # Action to checkout your repository + # with: + # fetch-depth: 0 is not strictly necessary for license check unless your script + # needs to read historical commits, which a basic license checker usually doesn't. + # Default fetch-depth is 1, which is usually sufficient. + # If your license checker needs to diff against a base branch, you might need fetch-depth: 0 + # or a specific fetch-depth. For a simple "check all files" script, default is fine. + + - name: Run License Checker Script + run: bash scripts/test/license_checker.sh # Path to your license checking script diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1cdb06752..b0112b1f7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,11 +4,11 @@ on: [push, pull_request] env: qemu-version: 7.1.0 - rust-toolchain: nightly-2023-09-01 + rust-toolchain: nightly-2025-05-07 jobs: unit-test: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 with: @@ -26,7 +26,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-22.04] arch: [x86_64, riscv64, aarch64] steps: - uses: actions/checkout@v3 @@ -40,7 +40,7 @@ jobs: - uses: actions-rs/install@v0.1 with: crate: cargo-binutils - version: latest + version: 0.3.5 use-tool-cache: true - name: Install gcc multilib run: sudo apt update && sudo apt install -y gcc-multilib diff --git a/.gitignore b/.gitignore index da9c3bd6e..d9cecc23c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,11 +10,12 @@ compile_commands.json /fw_dynamic.bin *.asm -disk.img +*.img actual.out -qemu.log +*.log rusty-tags.vi apps/c/redis/ apps/c/iperf/ apps/c/wamr/ apps/c/nginx/ +apps/c/python3/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..a3d1e74bf --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,82 @@ +# CHANGELOG + +## [v0.1.0] - 2025-07 + +### ✨ New Features +#### Architecture Support +- **RISC-V64** (#196) + - mmap & process fork implementation + - toolchain compatibility fixes + - handle `GP` register to make `execv` work +- **ARM Enhancements** (#183) + - GICv3 interrupt controller + - Device Tree Blob (DTB) parsing + +#### Core Systems +- **Filesystem & Storage** + - FUSE integration (#201) + - New syscalls: `fchmodat` (#190), `ftruncate` (#187) + - VFS refactoring with ext4/fatfs support (#152) + - Unified file/directory implementations (#186) + - FIFO/named pipes (#185) & PTY support (#189) +- **Networking & IPC** + - UNIX socket enhancements (#195) + - DGRAM support (#164) & `socketpair` (#171), `getsockname` (#182) + - Static routing with loopback support (#179) +- **Device Drivers** + - Virtio subsystem updates (#205) + - Virtio-console support (#143) + - TTY/Termios improvements (#181) +- **Process Management** + - Support of fork/clone in single address space (#142) + - Signal handling (#178) & `rt_sigaction`, `kill` and `tkill` (#131) + - CLOEXEC flag support (#162) + +### 🐛 Bug Fixes +- Fixed `rename` operations (#202) +- Resolved busybox page faults (#194) +- Fixed network buffer memory leaks (#173) +- Corrected page fault handling during nested forks (#151) +- Fixed `wait4` null pointer dereference (#166) +- Fixed problems within `poll`, `clock_nanosleep` and `getsockopt` (#139) +- Resolved file close scheduling deadlock (#175) +- Corrected `getsockopt` implementation (#170) +- Fixed percpu crate alignment (#184) + +### ⚙️ Infrastructure +- Toolchain upgraded to `nightly-2025-05-07` (#197) + +### 📦 Applications +- Support of sshd after PR #202 + +--- + +## [v0.0.3] - Initial Release + +### 🚀 Core Capabilities +- **Architectures**: x86_64 • AArch64 • RiscV64 +- **Platforms**: QEMU pc-q35 (x86) • virt (RiscV/ARM) +- **Schedulers**: FIFO • RR • CFS +- **Drivers**: VirtIO (net, blk, gpu, 9p) +- **Networking**: TCP/UDP stack (smoltcp/LwIP) +- **Filesystems**: fatfs • ramfs • 9pfs • devfs +- **Dynamic App Loading** (#97) + +### 📦 Supported Applications +| Application | Functionality | Repository | +|-------------|---------------|------------| +| **Redis** | Server with `redis-cli` & benchmark | [syswonder/rux-redis](https://github.com/syswonder/rux-redis) | +| **Nginx** | HTTP/HTTPS web server | [syswonder/rux-nginx](https://github.com/syswonder/rux-nginx) | +| **Wamr** | WASM execution + wasi-nn | [syswonder/rux-wamr](https://github.com/syswonder/rux-wamr) | +| **Iperf** | Network performance testing | [syswonder/rux-iperf](https://github.com/syswonder/rux-iperf) | +| **Sqlite** | Embedded SQL database | | +| **Python** | Python 3 runtime | [syswonder/rux-python3](https://github.com/syswonder/rux-python3) | + +### 🛠️ Development Ecosystem +- **Languages**: Rust • C/C++ (musl/ruxlibc) • Perl +- **Tools**: [RuxGo](https://github.com/syswonder/ruxgo) +- **Documentation**: + - [RuxOS-Book](https://ruxos.syswonder.org) + - [RuxGo-Book](https://ruxgo.syswonder.org) + +> **Get Started**: We recommend beginning with the [RuxOS-Book](https://ruxos.syswonder.org) diff --git a/Cargo.lock b/Cargo.lock index 859e50f6a..8f7e89bb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aarch64-cpu" -version = "9.3.1" +version = "9.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb711c57d60565ba8f6523eb371e6243639617d817b4df1ae09af250af1af411" +checksum = "ac42a04a61c19fc8196dd728022a784baecc5d63d7e256c01ad1b3fbfab26287" dependencies = [ "tock-registers", ] @@ -21,14 +21,14 @@ dependencies = [ "const-random", "once_cell", "version_check", - "zerocopy 0.7.32", + "zerocopy 0.7.35", ] [[package]] name = "aho-corasick" -version = "1.0.4" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -47,6 +47,12 @@ dependencies = [ "slab_allocator", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -68,11 +74,20 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "another_ext4" +version = "0.1.0" +source = "git+https://github.com/LJxTHUCS/another_ext4.git?rev=3c3e03a#3c3e03a1b63a254f9cc5adb603d01c746f3fcf4b" +dependencies = [ + "bitflags 2.9.1", + "log", +] + [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "arceos-shell" @@ -92,11 +107,13 @@ dependencies = [ "axerrno", "axio", "axlog", + "crate_interface", "ruxconfig", "ruxdisplay", "ruxfeat", "ruxfs", "ruxhal", + "ruxmm", "ruxnet", "ruxruntime", "ruxtask", @@ -106,6 +123,7 @@ dependencies = [ name = "arm_gic" version = "0.1.0" dependencies = [ + "log", "tock-registers", ] @@ -118,18 +136,18 @@ dependencies = [ [[package]] name = "atomic-polyfill" -version = "0.1.11" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" dependencies = [ "critical-section", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axalloc" @@ -154,18 +172,28 @@ dependencies = [ name = "axfs_devfs" version = "0.1.0" dependencies = [ + "axerrno", "axfs_vfs", + "axio", "log", - "spin 0.9.8", + "ringbuffer", + "ruxconfig", + "spin", + "spinlock", + "tty", ] [[package]] name = "axfs_ramfs" version = "0.1.0" dependencies = [ + "axerrno", "axfs_vfs", + "axio", "log", - "spin 0.9.8", + "ringbuffer", + "ruxfifo", + "spin", ] [[package]] @@ -173,8 +201,11 @@ name = "axfs_vfs" version = "0.1.0" dependencies = [ "axerrno", - "bitflags 2.4.0", + "axio", + "bitflags 2.9.1", + "lazy_static", "log", + "spin", ] [[package]] @@ -203,6 +234,7 @@ dependencies = [ "arceos_api", "axerrno", "axio", + "axlog", "ruxfeat", "spinlock", ] @@ -259,7 +291,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.32", + "syn 2.0.87", "which", ] @@ -269,7 +301,7 @@ version = "0.66.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "lazy_static", @@ -282,7 +314,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.32", + "syn 2.0.87", "which", ] @@ -306,9 +338,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bitmap-allocator" @@ -320,34 +352,35 @@ dependencies = [ [[package]] name = "bitmaps" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703642b98a00b3b90513279a8ede3fcfa479c126c5fb46e78f3051522f021403" +checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" [[package]] name = "buddy_system_allocator" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f9365b6b0c9e1663ca4ca9440c00eda46bc85a3407070be8b5e0d8d1f29629" +checksum = "d44d578cadd17312c75e7d0ef489361f160ace58f7139aa32001fee1a51b89b5" [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "capability" version = "0.1.0" dependencies = [ "axerrno", - "bitflags 2.4.0", + "axfs_vfs", + "bitflags 2.9.1", ] [[package]] @@ -358,12 +391,13 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.83" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -383,24 +417,23 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", - "time", "wasm-bindgen", - "winapi", + "windows-targets", ] [[package]] name = "ciborium" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -409,15 +442,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", @@ -425,9 +458,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -436,18 +469,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.23" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03aef18ddf7d879c15ce20f04826ef8418101c7e528014c3eeea13321047dca3" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.3.23" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ce6fffb678c9b80a70b6b6de0aad31df727623a70fd9a842c30cd573e2fa98" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstyle", "clap_lex", @@ -455,9 +488,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "const-default" @@ -487,9 +520,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core_detect" @@ -499,13 +532,13 @@ checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" [[package]] name = "crate_interface" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54b79db793871881b52a1eb5dc6fd869743122e34795f12db8e183dcb6a4f28" +checksum = "6af24c4862260a825484470f5526a91ad1031e04ab899be62478241231f62b46" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.87", ] [[package]] @@ -546,52 +579,34 @@ dependencies = [ [[package]] name = "critical-section" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] +checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.15" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset 0.9.0", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -617,9 +632,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "defmt" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2d011b2fee29fb7d659b83c43fce9a2cb4df453e16d441a51448e448f3f98" +checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0" dependencies = [ "bitflags 1.3.2", "defmt-macros", @@ -627,24 +642,24 @@ dependencies = [ [[package]] name = "defmt-macros" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54f0216f6c5acb5ae1a47050a6645024e6edafc2ee32d421955eccfef12ef92e" +checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb" dependencies = [ "defmt-parser", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.87", ] [[package]] name = "defmt-parser" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "269924c02afd7f94bc4cecbfa5c379f6ffcf9766b3408fe63d22c728654eccd0" +checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f" dependencies = [ - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -653,7 +668,7 @@ version = "0.1.0" dependencies = [ "driver_common", "log", - "spin 0.9.8", + "spin", ] [[package]] @@ -669,6 +684,13 @@ dependencies = [ name = "driver_common" version = "0.1.0" +[[package]] +name = "driver_console" +version = "0.1.0" +dependencies = [ + "driver_common", +] + [[package]] name = "driver_display" version = "0.1.0" @@ -683,7 +705,7 @@ dependencies = [ "driver_common", "ixgbe-driver", "log", - "spin 0.9.8", + "spin", ] [[package]] @@ -700,10 +722,11 @@ dependencies = [ "driver_9p", "driver_block", "driver_common", + "driver_console", "driver_display", "driver_net", "log", - "spin 0.9.8", + "spin", "virtio-drivers", ] @@ -713,6 +736,7 @@ version = "0.1.0" dependencies = [ "fdt-rs", "lazy_init", + "log", ] [[package]] @@ -724,9 +748,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elf" @@ -767,12 +791,29 @@ dependencies = [ "void", ] +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "endian-type-rs" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6419a5c75e40011b9fe0174db3fe24006ab122fbe1b7e9cc5974b338a755c76" +[[package]] +name = "enumn" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -781,23 +822,21 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.2" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ - "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +name = "ext4_rs" +version = "1.0.0" +source = "git+https://github.com/yuoo655/ext4_rs.git?rev=6bcc7f5#6bcc7f5d6382ba1940f29f9a15869f35fe77b5c0" dependencies = [ - "cc", - "libc", + "bitflags 2.9.1", + "log", ] [[package]] @@ -809,22 +848,22 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fatfs" version = "0.4.0" -source = "git+https://github.com/syswonder/rust-fatfs.git?rev=bf8ad02#bf8ad02bc6060728f6d3b81ee8428d731da4ca94" +source = "git+https://github.com/syswonder/rust-fatfs.git#a1d6da5cb6c046992396e11e9aca63d4b155431a" dependencies = [ "bitflags 1.3.2", "log", - "spin 0.9.8", + "spin", ] [[package]] name = "fdt-rs" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a40cabc11c8258822a593f5c51f2d9f4923e715ca9e2a0630cf77ae15f390b" +checksum = "581d3afdd654deb68c19fcbe4bc411910cc64067d4a13d8637bda7722cb9c2ea" dependencies = [ "endian-type-rs", "fallible-iterator", - "memoffset 0.5.6", + "memoffset", "num-derive", "num-traits", "rustc_version 0.2.3", @@ -850,13 +889,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -867,15 +906,19 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "half" -version = "1.8.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] [[package]] name = "handler_table" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011878ffbfb4cbfcbd9fd799d1168a65897b0b0195fc55f9ec73d27f1685b1dd" +checksum = "e81913856cda07e1e7044a375043c2e4429ddb7d94a0d4ad10d4c27796ce4bd9" [[package]] name = "hash32" @@ -888,41 +931,54 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heapless" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ "atomic-polyfill", "hash32", - "rustc_version 0.4.0", - "spin 0.9.8", + "rustc_version 0.4.1", + "spin", "stable_deref_trait", ] [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -936,23 +992,31 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", ] +[[package]] +name = "iovec" +version = "0.0.1" +dependencies = [ + "axerrno", + "smallvec", +] + [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ "hermit-abi", - "rustix", - "windows-sys", + "libc", + "windows-sys 0.52.0", ] [[package]] @@ -975,9 +1039,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "ixgbe-driver" @@ -993,27 +1057,27 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] [[package]] name = "kernel_guard" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5638cc8298368ef284589ad372890c849b520cccd50567d86a77a9232e68afdc" +checksum = "36172feaa47f9967efd3bc5fc77462899286e821de3de68c5fe88176a24d3a1f" dependencies = [ "cfg-if", "crate_interface", @@ -1025,11 +1089,11 @@ version = "0.1.0" [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.5.2", + "spin", ] [[package]] @@ -1040,18 +1104,18 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "winapi", + "windows-targets", ] [[package]] @@ -1060,15 +1124,15 @@ version = "0.1.0" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1076,9 +1140,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "lwip_rust" @@ -1096,9 +1160,9 @@ checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" @@ -1109,26 +1173,17 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - [[package]] name = "memory_addr" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b53e2ffbb2aaf3b738bd38ddd7ca8c511e47e05cd03fc09eacb7c5fa1285e5" +checksum = "359639f2591c78370034feaa79ebd114516d04ba642a89bb68b2b806ef2bc2f6" [[package]] name = "micromath" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39617bc909d64b068dcffd0e3e31679195b5576d0c83fadc52690268cc2b2b55" +checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815" [[package]] name = "minimal-lexical" @@ -1174,23 +1229,13 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "num_enum" version = "0.5.11" @@ -1213,15 +1258,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" -version = "11.1.3" +version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "page_table" @@ -1237,17 +1282,17 @@ name = "page_table_entry" version = "0.1.0" dependencies = [ "aarch64-cpu", - "bitflags 2.4.0", + "bitflags 2.9.1", "log", "memory_addr", - "x86_64", + "x86_64 0.14.13", ] [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "peeking_take_while" @@ -1257,29 +1302,33 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percpu" -version = "0.1.0" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e56c0c558952222967b592899f98765b48590e7bd7403bfd7075f73afc6ed6" dependencies = [ "cfg-if", "kernel_guard", "percpu_macros", - "spin 0.9.8", + "spin", "x86", ] [[package]] name = "percpu_macros" -version = "0.1.0" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9f4cc54a2e471ff72f1499461ba381ad4eae9cbd60d29c258545b995e406e0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.87", ] [[package]] name = "plotters" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -1290,33 +1339,36 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy 0.7.35", +] [[package]] name = "prettyplease" -version = "0.2.12" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.32", + "syn 2.0.87", ] [[package]] @@ -1357,18 +1409,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1403,6 +1455,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + [[package]] name = "ratio" version = "0.1.0" @@ -1418,18 +1479,18 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.0.1" +version = "11.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d86a7c4638d42c44551f4791a20e687dbb4c3de1f33c43dd71e355cd429def1" +checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.9.1", ] [[package]] name = "rayon" -version = "1.7.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -1437,21 +1498,19 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] name = "regex" -version = "1.9.3" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -1461,9 +1520,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1472,9 +1531,13 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "ringbuffer" +version = "0.1.0" [[package]] name = "riscv" @@ -1516,31 +1579,31 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.18", + "semver 1.0.23", ] [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rux9p" @@ -1554,7 +1617,7 @@ dependencies = [ "ruxdriver", "ruxfs", "ruxnet", - "spin 0.9.8", + "spin", ] [[package]] @@ -1582,6 +1645,7 @@ version = "0.1.0" dependencies = [ "axalloc", "cfg-if", + "crate_interface", "driver_9p", "driver_block", "driver_common", @@ -1594,16 +1658,28 @@ dependencies = [ "ruxhal", ] +[[package]] +name = "ruxdtb" +version = "0.1.0" +dependencies = [ + "dtb", + "log", +] + [[package]] name = "ruxfdtable" version = "0.1.0" dependencies = [ "axerrno", + "axfs_vfs", "axio", + "bitflags 2.9.1", + "bitmaps", + "capability", "flatten_objects", "lazy_static", "log", - "spin 0.9.8", + "spin", ] [[package]] @@ -1617,34 +1693,76 @@ dependencies = [ "ruxdisplay", "ruxdriver", "ruxfs", + "ruxfuse", "ruxhal", + "ruxmm", "ruxnet", "ruxruntime", "ruxtask", + "ruxvda", "spinlock", + "tty", +] + +[[package]] +name = "ruxfifo" +version = "0.1.0" +dependencies = [ + "axerrno", + "axfs_vfs", + "axio", + "ringbuffer", + "spin", ] [[package]] name = "ruxfs" version = "0.1.0" dependencies = [ + "another_ext4", "axalloc", "axerrno", "axfs_devfs", "axfs_ramfs", "axfs_vfs", "axio", - "axsync", "capability", "cfg-if", "crate_interface", "driver_block", + "ext4_rs", "fatfs", "lazy_init", + "lazy_static", "log", "memory_addr", "ruxdriver", + "ruxfdtable", + "ruxfifo", + "spin", + "spinlock", +] + +[[package]] +name = "ruxfuse" +version = "0.1.0" +dependencies = [ + "axalloc", + "axfs_devfs", + "axfs_ramfs", + "axfs_vfs", + "capability", + "cfg-if", + "crate_interface", + "driver_block", + "lazy_init", + "log", + "memory_addr", + "ruxdriver", + "ruxfs", "ruxtask", + "spin", + "spinlock", ] [[package]] @@ -1653,7 +1771,7 @@ version = "0.1.0" dependencies = [ "ahash", "axerrno", - "bitflags 2.4.0", + "bitflags 2.9.1", "lazy_static", "log", "memory_addr", @@ -1669,23 +1787,29 @@ dependencies = [ "arm_gic", "arm_pl011", "axalloc", + "axerrno", + "axio", "axlog", - "bitflags 2.4.0", + "bitflags 2.9.1", "cfg-if", "crate_interface", + "driver_common", + "driver_console", + "driver_virtio", "dtb", "dw_apb_uart", "embedded-hal", "handler_table", "kernel_guard", "lazy_init", + "lazy_static", "log", "memory_addr", "page_table", "page_table_entry", "percpu", "ratio", - "raw-cpuid 11.0.1", + "raw-cpuid 11.1.0", "riscv", "ruxconfig", "sbi-rt", @@ -1693,20 +1817,30 @@ dependencies = [ "static_assertions", "tock-registers", "tty", + "virtio-drivers", "x2apic", "x86", - "x86_64", + "x86_64 0.14.13", ] [[package]] -name = "ruxlibc" +name = "ruxmm" version = "0.1.0" dependencies = [ - "axerrno", - "axio", - "bindgen 0.66.1", - "ruxfeat", - "ruxos_posix_api", + "axalloc", + "bitflags 2.9.1", + "cfg-if", + "crate_interface", + "kernel_guard", + "log", + "memory_addr", + "page_table", + "page_table_entry", + "ruxdriver", + "ruxhal", + "ruxtask", + "spinlock", + "static_assertions", ] [[package]] @@ -1728,21 +1862,28 @@ name = "ruxnet" version = "0.1.0" dependencies = [ "axerrno", + "axfs_vfs", "axio", - "axlog", "axsync", + "bitflags 2.9.1", "cfg-if", "cty", "driver_net", + "flatten_objects", + "hashbrown", + "iovec", "lazy_init", + "lazy_static", "log", "lwip_rust", "printf-compat", "ruxdriver", + "ruxfdtable", + "ruxfs", "ruxhal", "ruxtask", "smoltcp", - "spin 0.9.8", + "spin", ] [[package]] @@ -1772,27 +1913,45 @@ dependencies = [ "axlog", "axsync", "bindgen 0.66.1", - "bitflags 2.4.0", + "bitflags 2.9.1", + "capability", "cfg-if", "crate_interface", "elf", "flatten_objects", + "iovec", "lazy_init", "lazy_static", "memory_addr", "page_table", + "ringbuffer", "ruxconfig", "ruxfdtable", "ruxfeat", "ruxfs", + "ruxfuse", "ruxfutex", "ruxhal", + "ruxmm", "ruxnet", "ruxruntime", "ruxtask", - "spin 0.9.8", + "spin", "spinlock", "static_assertions", + "tty", +] + +[[package]] +name = "ruxrand" +version = "0.1.0" +dependencies = [ + "crate_interface", + "lazy_init", + "percpu", + "rand", + "rand_xoshiro", + "spinlock", ] [[package]] @@ -1800,23 +1959,32 @@ name = "ruxruntime" version = "0.1.0" dependencies = [ "axalloc", + "axfs_ramfs", + "axfs_vfs", + "axio", "axlog", "axsync", "cfg-if", "crate_interface", + "driver_block", "dtb", "kernel_guard", "lazy_init", + "log", "percpu", "rux9p", "ruxconfig", "ruxdisplay", "ruxdriver", + "ruxdtb", "ruxfs", "ruxfutex", "ruxhal", + "ruxmm", "ruxnet", + "ruxrand", "ruxtask", + "ruxvda", "tty", ] @@ -1824,29 +1992,69 @@ dependencies = [ name = "ruxtask" version = "0.1.0" dependencies = [ + "axalloc", "axerrno", + "axfs_vfs", + "axio", "cfg-if", "crate_interface", + "flatten_objects", "kernel_guard", "lazy_init", + "lazy_static", "log", "memory_addr", + "page_table", + "page_table_entry", "percpu", "rand", "ruxconfig", "ruxfdtable", + "ruxfs", "ruxhal", - "ruxtask", "scheduler", + "spin", "spinlock", "timer_list", ] +[[package]] +name = "ruxvda" +version = "0.1.0" +dependencies = [ + "axalloc", + "axfs_devfs", + "axfs_ramfs", + "axfs_vfs", + "capability", + "cfg-if", + "crate_interface", + "driver_block", + "driver_common", + "lazy_init", + "log", + "memory_addr", + "ruxdriver", + "ruxfs", + "ruxtask", + "spin", + "spinlock", +] + [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "safe-mmio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02a82ad13df46afeba34a4e54065fa912308b9101b060e4422898eac0e06f6" +dependencies = [ + "zerocopy 0.8.26", +] [[package]] name = "same-file" @@ -1896,9 +2104,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "semver-parser" @@ -1908,40 +2116,41 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.185" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.185" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.87", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "shlex" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slab_allocator" @@ -1950,6 +2159,12 @@ dependencies = [ "buddy_system_allocator", ] +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + [[package]] name = "smoltcp" version = "0.10.0" @@ -1964,12 +2179,6 @@ dependencies = [ "managed", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -2025,9 +2234,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.32" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -2036,33 +2245,42 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.47" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.63", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.87", ] [[package]] -name = "time" -version = "0.1.45" +name = "thiserror-impl" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -2105,15 +2323,15 @@ checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", @@ -2124,8 +2342,14 @@ dependencies = [ name = "tty" version = "0.0.1" dependencies = [ + "axerrno", + "axio", + "axlog", + "bitflags 2.9.1", "lazy_init", - "log", + "num_enum", + "ringbuffer", + "spin", "spinlock", ] @@ -2135,20 +2359,20 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.87", ] [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unsafe_unwrap" @@ -2158,18 +2382,22 @@ checksum = "1230ec65f13e0f9b28d789da20d2d419511893ea9dac2c1f4ef67b8b14e5da80" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "virtio-drivers" -version = "0.4.0" -source = "git+https://github.com/syswonder/virtio-drivers.git?rev=62dbe5a#62dbe5a09aff0cac00037c8fe6528d94665286b8" +version = "0.11.0" +source = "git+https://github.com/syswonder/virtio-drivers.git?rev=31f6555#31f6555c79f00f90caaedb09a8b1eab248c01afc" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.1", + "embedded-io", + "enumn", "log", - "zerocopy 0.6.3", + "safe-mmio", + "thiserror 2.0.12", + "zerocopy 0.8.26", ] [[package]] @@ -2198,20 +2426,14 @@ checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2220,34 +2442,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.87", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2255,28 +2478,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -2284,73 +2507,62 @@ dependencies = [ [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix", ] [[package]] -name = "winapi" -version = "0.3.9" +name = "winapi-util" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows-sys 0.59.0", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "winapi", + "windows-targets", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.48.0" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +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", @@ -2359,66 +2571,72 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +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.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.14" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] name = "x2apic" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "547152b57dd1ae0ce7a4ef1c6470f6039aa7ed22e2179d5bc4f3eda1304e0db3" +checksum = "db5cbcb7faedfa15f90376004ffc0cb42e427623ab56629f0073d275ee8e7043" dependencies = [ "bit", "bitflags 1.3.2", "paste", "raw-cpuid 10.7.0", - "x86_64", + "x86_64 0.15.2", ] [[package]] @@ -2434,53 +2652,65 @@ dependencies = [ [[package]] name = "x86_64" -version = "0.14.10" +version = "0.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "100555a863c0092238c2e0e814c1096c1e5cf066a309c696a87e907b5f8c5d69" +checksum = "c101112411baafbb4bf8d33e4c4a80ab5b02d74d2612331c61e8192fc9710491" dependencies = [ "bit_field", - "bitflags 1.3.2", + "bitflags 2.9.1", + "rustversion", + "volatile 0.4.6", +] + +[[package]] +name = "x86_64" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f042214de98141e9c8706e8192b73f56494087cc55ebec28ce10f26c5c364ae" +dependencies = [ + "bit_field", + "bitflags 2.9.1", "rustversion", "volatile 0.4.6", ] [[package]] name = "zerocopy" -version = "0.6.3" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b9c234616391070b0b173963ebc65a9195068e7ed3731c6edac2ec45ebe106" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive 0.6.3", + "zerocopy-derive 0.7.35", ] [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ - "zerocopy-derive 0.7.32", + "zerocopy-derive 0.8.26", ] [[package]] name = "zerocopy-derive" -version = "0.6.3" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f7f3a471f98d0a61c34322fbbfd10c384b07687f680d4119813713f72308d91" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.87", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.87", ] diff --git a/Cargo.toml b/Cargo.toml index c1500efeb..8bb56c3b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,61 +2,10 @@ resolver = "2" members = [ - "crates/allocator", - "crates/arm_gic", - "crates/arm_pl011", - "crates/dw_apb_uart", - "crates/axerrno", - "crates/axfs_devfs", - "crates/axfs_ramfs", - "crates/axfs_vfs", - "crates/axio", - "crates/capability", - "crates/driver_9p", - "crates/driver_block", - "crates/driver_common", - "crates/driver_display", - "crates/driver_net", - "crates/driver_pci", - "crates/driver_virtio", - "crates/dtb", - "crates/flatten_objects", - "crates/lazy_init", - "crates/linked_list", - "crates/lwip_rust", - "crates/page_table", - "crates/page_table_entry", - "crates/percpu", - "crates/percpu_macros", - "crates/ratio", - "crates/scheduler", - "crates/slab_allocator", - "crates/spinlock", - "crates/timer_list", - "crates/tuple_for_each", - "crates/tty", - - "modules/axalloc", - "modules/axlog", - "modules/ruxnet", - "modules/axsync", - "modules/rux9p", - "modules/ruxconfig", - "modules/ruxdisplay", - "modules/ruxdriver", - "modules/ruxfdtable", - "modules/ruxfs", - "modules/ruxhal", - "modules/ruxruntime", - "modules/ruxtask", - "modules/ruxfutex", - - "api/ruxfeat", - "api/arceos_api", - "api/ruxos_posix_api", - + "crates/*", + "modules/*", + "api/*", "ulib/axstd", - "ulib/ruxlibc", "ulib/ruxmusl", "apps/display/basic_painting", @@ -64,6 +13,13 @@ members = [ "apps/fs/shell", ] +[workspace.dependencies] +# Centralized dependency version for shared crates. +# Usage: In child crates, write `bitflags = { workspace = true }`. +bitflags = "2.6" +log = "0.4" +spin = "0.9" + [profile.release] lto = true diff --git a/LICENSE.MulanPSL2 b/LICENSE similarity index 90% rename from LICENSE.MulanPSL2 rename to LICENSE index 6bf65053e..67ad8ef34 100644 --- a/LICENSE.MulanPSL2 +++ b/LICENSE @@ -1,6 +1,6 @@ 木兰宽松许可证, 第2版 - 木兰宽松许可证, 第2版 + 木兰宽松许可证, 第2版 2020年1月 http://license.coscl.org.cn/MulanPSL2 @@ -36,15 +36,15 @@ 5. 免责声明与责任限制 - “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 + “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 6. 语言 “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 - 条款结束 + 条款结束 如何将木兰宽松许可证,第2版,应用到您的软件 - + 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; @@ -53,13 +53,13 @@ 3, 请将如下声明文本放入每个源文件的头部注释中。 - Copyright (c) [2023] [Syswonder Community] - [Ruxos] is licensed under Mulan PSL v2. - You can use this software according to the terms and conditions of the Mulan PSL v2. + Copyright (c) [Year] [name of copyright holder] + [Software Name] is licensed under Mulan PSL v2. + You can use this software according to the terms and conditions of the Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - See the Mulan PSL v2 for more details. + http://license.coscl.org.cn/MulanPSL2 + THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + See the Mulan PSL v2 for more details. Mulan Permissive Software License,Version 2 @@ -67,25 +67,25 @@ Mulan Permissive Software License,Version 2 (Mulan PSL v2) January 2020 http://license.coscl.org.cn/MulanPSL2 - Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: - + Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: + 0. Definition - - Software means the program and related documents which are licensed under this License and comprise all Contribution(s). - + + Software means the program and related documents which are licensed under this License and comprise all Contribution(s). + Contribution means the copyrightable work licensed by a particular Contributor under this License. - + Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. - + Legal Entity means the entity making a Contribution and all its Affiliates. - + Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. 1. Grant of Copyright License Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. - 2. Grant of Patent License + 2. Grant of Patent License Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. @@ -111,17 +111,17 @@ To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: - i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; + i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. - Copyright (c) [2023] [Syswonder Community] - [Ruxos] is licensed under Mulan PSL v2. - You can use this software according to the terms and conditions of the Mulan PSL v2. + Copyright (c) [Year] [name of copyright holder] + [Software Name] is licensed under Mulan PSL v2. + You can use this software according to the terms and conditions of the Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - See the Mulan PSL v2 for more details. + http://license.coscl.org.cn/MulanPSL2 + THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + See the Mulan PSL v2 for more details. \ No newline at end of file diff --git a/Makefile b/Makefile index 97b46532b..ee4c54223 100644 --- a/Makefile +++ b/Makefile @@ -51,14 +51,17 @@ FEATURES ?= APP_FEATURES ?= # QEMU options +CONSOLE ?= n BLK ?= n NET ?= n GRAPHIC ?= n V9P ?= n BUS ?= mmio RISCV_BIOS ?= default +GICV3 ?= n DISK_IMG ?= disk.img +FS ?= fat32 QEMU_LOG ?= n NET_DUMP ?= n NET_DEV ?= user @@ -78,7 +81,7 @@ ARGS ?= ENVS ?= # Libc options -MUSL ?= n +MUSL ?= y # App type ifeq ($(wildcard $(APP)),) @@ -135,7 +138,7 @@ else ifeq ($(ARCH), riscv64) else ifeq ($(ARCH), aarch64) ACCEL ?= n PLATFORM_NAME ?= aarch64-qemu-virt - TARGET := aarch64-unknown-none-softfloat + TARGET := aarch64-unknown-none ifeq ($(findstring fp_simd,$(FEATURES)),) TARGET_CFLAGS := -mgeneral-regs-only endif @@ -183,6 +186,7 @@ all: build # prebuild option to be overriden in `$(PREBUILD)` define run_prebuild + git submodule update --init endef # prebuild makefile might override some variables by their own @@ -202,7 +206,7 @@ endif _force: ; -# prebuild scripts must track their dependencies by themselves +# # prebuild scripts must track their dependencies by themselves prebuild: _force $(call run_prebuild) @@ -234,12 +238,21 @@ debug_no_attach: build $(call run_qemu_debug) clippy: + $(call run_prebuild) ifeq ($(origin ARCH), command line) $(call cargo_clippy,--target $(TARGET)) else $(call cargo_clippy) endif +clippy_fix: + $(call run_prebuild) +ifeq ($(origin ARCH), command line) + $(call cargo_clippy,--target $(TARGET) --fix) +else + $(call cargo_clippy,--fix) +endif + doc: $(call cargo_doc) @@ -249,9 +262,6 @@ doc_check_missing: fmt: cargo fmt --all -fmt_c: - @clang-format --style=file -i $(shell find ulib/ruxlibc -iname '*.c' -o -iname '*.h') - test: $(call app_test) @@ -264,8 +274,14 @@ unittest_no_fail_fast: disk_img: ifneq ($(wildcard $(DISK_IMG)),) @printf "$(YELLOW_C)warning$(END_C): disk image \"$(DISK_IMG)\" already exists!\n" -else +else ifeq ($(FS), fat32) $(call make_disk_image,fat32,$(DISK_IMG)) +else ifeq ($(FS), ext4) + $(call make_disk_image,ext4,$(DISK_IMG)) +else ifeq ($(FS), exfat) + $(call make_disk_image,exfat,$(DISK_IMG)) +else + $(error "FS" must be one of "fat32" or "ext4" or "exfat") endif clean: clean_c clean_musl @@ -273,12 +289,11 @@ clean: clean_c clean_musl cargo clean clean_c:: - rm -rf ulib/ruxlibc/build_* rm -rf $(app-objs) clean_musl: rm -rf ulib/ruxmusl/build_* rm -rf ulib/ruxmusl/install -.PHONY: all build disasm run justrun debug clippy fmt fmt_c test test_no_fail_fast clean clean_c\ +.PHONY: all build disasm run justrun debug clippy fmt fmt_c test test_no_fail_fast clean \ clean_musl doc disk_image debug_no_attach prebuild _force diff --git a/README.md b/README.md index 006ca3a32..686c6ff21 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ The currently supported applications and programming languages, as well as their | Language | Description | |- | - | -| C | Run C apps by RuxOS ruxlibc or standard musl libc supported by ruxmusl. Evaluated by libc-bench. | +| C | Run C apps by standard musl libc supported by ruxmusl. Evaluated by libc-bench. | | C++ | Run C++ apps by c++ static library provided by musl libc. Passed c++ benchmark. Evaluated by c++ benchmark. | | [Perl](https://github.com/syswonder/rux-perl) | Run Perl standard library by musl libc. Evaluated by Perl benchmark. | | [Python](https://github.com/syswonder/rux-python3) | Run Python apps by dynamically loading Python modules. Evaluated by Python benchmark. | diff --git a/api/arceos_api/Cargo.toml b/api/arceos_api/Cargo.toml index b65cb69d9..dfc0d88b4 100644 --- a/api/arceos_api/Cargo.toml +++ b/api/arceos_api/Cargo.toml @@ -21,17 +21,19 @@ fs = ["dep:ruxfs", "ruxfeat/fs"] net = ["dep:ruxnet", "ruxfeat/net"] lwip = ["ruxfeat/lwip"] display = ["dep:ruxdisplay", "ruxfeat/display"] - +blkfs = ["ruxfeat/blkfs"] myfs = ["ruxfeat/myfs"] # Use dummy functions if the feature is not enabled dummy-if-not-enabled = [] [dependencies] +crate_interface = "0.1.1" ruxfeat = { path = "../ruxfeat" } ruxruntime = { path = "../../modules/ruxruntime" } ruxconfig = { path = "../../modules/ruxconfig" } -axlog = { path = "../../modules/axlog" } +ruxmm = { path = "../../modules/ruxmm" } +axlog = { path = "../../crates/axlog" } axio = { path = "../../crates/axio" } axerrno = { path = "../../crates/axerrno" } ruxhal = { path = "../../modules/ruxhal" } diff --git a/api/arceos_api/src/imp/fs.rs b/api/arceos_api/src/imp/fs.rs index 4488e1a68..75a6c6f47 100644 --- a/api/arceos_api/src/imp/fs.rs +++ b/api/arceos_api/src/imp/fs.rs @@ -9,17 +9,130 @@ use alloc::string::String; use axerrno::AxResult; -use ruxfs::fops::{Directory, File}; - -pub use ruxfs::fops::DirEntry as AxDirEntry; -pub use ruxfs::fops::FileAttr as AxFileAttr; -pub use ruxfs::fops::FilePerm as AxFilePerm; -pub use ruxfs::fops::FileType as AxFileType; -pub use ruxfs::fops::OpenOptions as AxOpenOptions; pub use axio::SeekFrom as AxSeekFrom; +use axio::{Read, Seek, Write}; +pub use ruxfs::api::DirEntry as AxDirEntry; +pub use ruxfs::api::FileAttr as AxFileAttr; +pub use ruxfs::api::FilePerm as AxFilePerm; +pub use ruxfs::api::FileType as AxFileType; +use ruxfs::api::{open_dir, open_file, AbsPath, Directory, File, OpenFlags, RelPath}; +#[derive(Clone, Debug)] +/// arceos api OpenOptions +pub struct AxOpenOptions { + /// Open for reading. + pub read: bool, + /// Open for writing. + pub write: bool, + /// Append to the end of the file. + pub append: bool, + /// Truncate the file to zero length. + pub truncate: bool, + /// Create a new file. + pub create: bool, + /// Create a new file, failing if it already exists. + pub create_new: bool, + // system-specific + _custom_flags: i32, + _mode: u32, +} + +impl Default for AxOpenOptions { + fn default() -> Self { + Self::new() + } +} + +impl AxOpenOptions { + /// Creates a blank new set of options ready for configuration. + pub const fn new() -> Self { + Self { + // generic + read: false, + write: false, + append: false, + truncate: false, + create: false, + create_new: false, + // system-specific + _custom_flags: 0, + _mode: 0o666, + } + } + /// Sets the option for read access. + pub fn read(&mut self, read: bool) { + self.read = read; + } + /// Sets the option for write access. + pub fn write(&mut self, write: bool) { + self.write = write; + } + /// Sets the option for the append mode. + pub fn append(&mut self, append: bool) { + self.append = append; + } + /// Sets the option for truncating a previous file. + pub fn truncate(&mut self, truncate: bool) { + self.truncate = truncate; + } + /// Sets the option to create a new file, or open it if it already exists. + pub fn create(&mut self, create: bool) { + self.create = create; + } + /// Sets the option to create a new file, failing if it already exists. + pub fn create_new(&mut self, create_new: bool) { + self.create_new = create_new; + } + + /// Get flags access mode + fn get_access_mode(&self) -> AxResult { + match (self.read, self.write) { + (true, false) => Ok(OpenFlags::empty()), + (false, true) => Ok(OpenFlags::O_WRONLY), + (true, true) => Ok(OpenFlags::O_RDWR), + (false, false) => Err(axio::Error::InvalidInput), + } + } + + /// Get flags creation mode + fn get_creation_mode(&self) -> AxResult { + match (self.write, self.append) { + (true, false) => {} + (false, false) => { + if self.truncate || self.create || self.create_new { + return Err(axio::Error::InvalidInput); + } + } + (_, true) => { + if self.truncate && !self.create_new { + return Err(axio::Error::InvalidInput); + } + } + } + + Ok(match (self.create, self.truncate, self.create_new) { + (false, false, false) => OpenFlags::empty(), + (true, false, false) => OpenFlags::O_CREAT, + (false, true, false) => OpenFlags::O_TRUNC, + (true, true, false) => OpenFlags::O_CREAT | OpenFlags::O_TRUNC, + (_, _, true) => OpenFlags::O_CREAT | OpenFlags::O_EXCL, + }) + } +} + +impl TryFrom for OpenFlags { + type Error = axerrno::AxError; + + fn try_from(opt: AxOpenOptions) -> Result { + Ok(opt.get_access_mode()? | opt.get_creation_mode()?) + } +} + +#[cfg(feature = "blkfs")] +pub use ruxfs::dev::Disk as AxDisk; #[cfg(feature = "myfs")] -pub use ruxfs::fops::{Disk as AxDisk, MyFileSystemIf}; +pub use ruxfs::MyFileSystemIf; +use ruxtask::fs::{FileSystem, InitFs}; /// A handle to an opened file. pub struct AxFileHandle(File); @@ -28,29 +141,27 @@ pub struct AxFileHandle(File); pub struct AxDirHandle(Directory); pub fn ax_open_file(path: &str, opts: &AxOpenOptions) -> AxResult { - Ok(AxFileHandle(File::open(path, opts)?)) + let file = open_file(&parse_path(path)?, OpenFlags::try_from(opts.clone())?)?; + Ok(AxFileHandle(file)) } pub fn ax_open_dir(path: &str, opts: &AxOpenOptions) -> AxResult { - Ok(AxDirHandle(Directory::open_dir(path, opts)?)) + let dir = open_dir(&parse_path(path)?, OpenFlags::try_from(opts.clone())?)?; + Ok(AxDirHandle(dir)) } -pub fn ax_read_file(file: &mut AxFileHandle, buf: &mut [u8]) -> AxResult { - file.0.read(buf) +pub fn ax_get_attr(path: &str) -> AxResult { + ruxfs::api::get_attr(&parse_path(path)?) } -pub fn ax_read_file_at(file: &AxFileHandle, offset: u64, buf: &mut [u8]) -> AxResult { - file.0.read_at(offset, buf) +pub fn ax_read_file(file: &mut AxFileHandle, buf: &mut [u8]) -> AxResult { + file.0.read(buf) } pub fn ax_write_file(file: &mut AxFileHandle, buf: &[u8]) -> AxResult { file.0.write(buf) } -pub fn ax_write_file_at(file: &AxFileHandle, offset: u64, buf: &[u8]) -> AxResult { - file.0.write_at(offset, buf) -} - pub fn ax_truncate_file(file: &AxFileHandle, size: u64) -> AxResult { file.0.truncate(size) } @@ -72,29 +183,45 @@ pub fn ax_read_dir(dir: &mut AxDirHandle, dirents: &mut [AxDirEntry]) -> AxResul } pub fn ax_create_dir(path: &str) -> AxResult { - ruxfs::api::create_dir(path) + ruxfs::api::create_dir(&parse_path(path)?) } pub fn ax_create_dir_all(path: &str) -> AxResult { - ruxfs::api::create_dir_all(path) + ruxfs::api::create_dir_all(&parse_path(path)?) } pub fn ax_remove_dir(path: &str) -> AxResult { - ruxfs::api::remove_dir(path) + ruxfs::api::remove_dir(&parse_path(path)?) } pub fn ax_remove_file(path: &str) -> AxResult { - ruxfs::api::remove_file(path) + ruxfs::api::remove_file(&parse_path(path)?) } pub fn ax_rename(old: &str, new: &str) -> AxResult { - ruxfs::api::rename(old, new) + ruxfs::api::rename(&parse_path(old)?, &parse_path(new)?) } pub fn ax_current_dir() -> AxResult { - ruxfs::api::current_dir() + ruxfs::api::current_dir().map(|path| path.to_string()) } pub fn ax_set_current_dir(path: &str) -> AxResult { - ruxfs::api::set_current_dir(path) + ruxfs::api::set_current_dir(parse_path(path)?) +} + +fn parse_path(path: &str) -> AxResult> { + if path.starts_with('/') { + Ok(AbsPath::new_canonicalized(path)) + } else { + ruxfs::api::current_dir().map(|cwd| cwd.join(&RelPath::new_canonicalized(path))) + } +} + +impl Iterator for AxDirHandle { + type Item = AxResult; + + fn next(&mut self) -> Option { + self.0.next() + } } diff --git a/api/arceos_api/src/imp/mod.rs b/api/arceos_api/src/imp/mod.rs index c4d2e01ec..7a34fe345 100644 --- a/api/arceos_api/src/imp/mod.rs +++ b/api/arceos_api/src/imp/mod.rs @@ -9,6 +9,8 @@ mod mem; mod task; +#[cfg(feature = "paging")] +mod trap; cfg_fs! { mod fs; diff --git a/api/arceos_api/src/imp/net.rs b/api/arceos_api/src/imp/net.rs index 7cb3e5e6b..55e490176 100644 --- a/api/arceos_api/src/imp/net.rs +++ b/api/arceos_api/src/imp/net.rs @@ -9,8 +9,8 @@ use crate::io::AxPollState; use axerrno::AxResult; -use ruxnet::{UdpSocket, TcpSocket}; use core::net::{IpAddr, SocketAddr}; +use ruxnet::{message::MessageFlags, TcpSocket, UdpSocket}; /// A handle to a TCP socket. pub struct AxTcpSocketHandle(TcpSocket); @@ -23,7 +23,7 @@ pub struct AxUdpSocketHandle(UdpSocket); //////////////////////////////////////////////////////////////////////////////// pub fn ax_tcp_socket() -> AxTcpSocketHandle { - AxTcpSocketHandle(TcpSocket::new()) + AxTcpSocketHandle(TcpSocket::new(false)) } pub fn ax_tcp_socket_addr(socket: &AxTcpSocketHandle) -> AxResult { @@ -62,7 +62,7 @@ pub fn ax_tcp_send(socket: &AxTcpSocketHandle, buf: &[u8]) -> AxResult { } pub fn ax_tcp_recv(socket: &AxTcpSocketHandle, buf: &mut [u8]) -> AxResult { - socket.0.recv(buf, 0) + socket.0.recv(buf, MessageFlags::empty()) } pub fn ax_tcp_poll(socket: &AxTcpSocketHandle) -> AxResult { @@ -98,11 +98,17 @@ pub fn ax_udp_bind(socket: &AxUdpSocketHandle, addr: SocketAddr) -> AxResult { socket.0.bind(addr) } -pub fn ax_udp_recv_from(socket: &AxUdpSocketHandle, buf: &mut [u8]) -> AxResult<(usize, SocketAddr)> { +pub fn ax_udp_recv_from( + socket: &AxUdpSocketHandle, + buf: &mut [u8], +) -> AxResult<(usize, SocketAddr)> { socket.0.recv_from(buf) } -pub fn ax_udp_peek_from(socket: &AxUdpSocketHandle, buf: &mut [u8]) -> AxResult<(usize, SocketAddr)> { +pub fn ax_udp_peek_from( + socket: &AxUdpSocketHandle, + buf: &mut [u8], +) -> AxResult<(usize, SocketAddr)> { socket.0.peek_from(buf) } diff --git a/api/arceos_api/src/imp/task.rs b/api/arceos_api/src/imp/task.rs index 805d3a76b..a1db9ac73 100644 --- a/api/arceos_api/src/imp/task.rs +++ b/api/arceos_api/src/imp/task.rs @@ -61,6 +61,12 @@ cfg_task! { } } + impl Default for AxWaitQueueHandle { + fn default() -> Self { + Self::new() + } + } + pub fn ax_current_task_id() -> u64 { ruxtask::current().id().as_u64() } diff --git a/ulib/ruxlibc/src/utils.rs b/api/arceos_api/src/imp/trap.rs similarity index 62% rename from ulib/ruxlibc/src/utils.rs rename to api/arceos_api/src/imp/trap.rs index 14d05e5cf..bb90964ee 100644 --- a/ulib/ruxlibc/src/utils.rs +++ b/api/arceos_api/src/imp/trap.rs @@ -7,13 +7,14 @@ * See the Mulan PSL v2 for more details. */ -use core::ffi::c_int; +use ruxhal::trap::{PageFaultCause, TrapHandler}; -pub fn e(ret: c_int) -> c_int { - if ret < 0 { - crate::errno::set_errno(ret.abs()); - -1 - } else { - ret as _ +struct TrapHandlerImpl; + +#[crate_interface::impl_interface] +impl TrapHandler for TrapHandlerImpl { + fn handle_page_fault(vaddr: usize, cause: PageFaultCause) -> bool { + // TODO: handle page fault + panic!("Page fault at {:#x} with cause {:?}.", vaddr, cause); } } diff --git a/api/arceos_api/src/lib.rs b/api/arceos_api/src/lib.rs index 0e2e0e1f6..d3e943ac6 100644 --- a/api/arceos_api/src/lib.rs +++ b/api/arceos_api/src/lib.rs @@ -12,7 +12,6 @@ //! [ArceOS]: https://github.com/rcore-os/arceos #![no_std] -#![feature(ip_in_core)] #![feature(doc_auto_cfg)] #![feature(doc_cfg)] #![allow(unused_imports)] @@ -159,7 +158,7 @@ pub mod fs { pub type AxFilePerm; pub type AxDirEntry; pub type AxSeekFrom; - #[cfg(feature = "myfs")] + #[cfg(feature = "blkfs")] pub type AxDisk; #[cfg(feature = "myfs")] pub type MyFileSystemIf; @@ -174,26 +173,19 @@ pub mod fs { /// Opens a directory at the path relative to the current directory with /// the options specified by `opts`. pub fn ax_open_dir(path: &str, opts: &AxOpenOptions) -> AxResult; + /// Returns attributes of a file or directory at the given path. + pub fn ax_get_attr(path: &str) -> AxResult; /// Reads the file at the current position, returns the number of bytes read. /// /// After the read, the cursor will be advanced by the number of bytes read. pub fn ax_read_file(file: &mut AxFileHandle, buf: &mut [u8]) -> AxResult; - /// Reads the file at the given position, returns the number of bytes read. - /// - /// It does not update the file cursor. - pub fn ax_read_file_at(file: &AxFileHandle, offset: u64, buf: &mut [u8]) -> AxResult; /// Writes the file at the current position, returns the number of bytes /// written. /// /// After the write, the cursor will be advanced by the number of bytes /// written. pub fn ax_write_file(file: &mut AxFileHandle, buf: &[u8]) -> AxResult; - /// Writes the file at the given position, returns the number of bytes - /// written. - /// - /// It does not update the file cursor. - pub fn ax_write_file_at(file: &AxFileHandle, offset: u64, buf: &[u8]) -> AxResult; /// Truncates the file to the specified size. pub fn ax_truncate_file(file: &AxFileHandle, size: u64) -> AxResult; /// Flushes the file, writes all buffered data to the underlying device. diff --git a/api/ruxfeat/Cargo.toml b/api/ruxfeat/Cargo.toml index 7926cee2f..d13cca4b8 100644 --- a/api/ruxfeat/Cargo.toml +++ b/api/ruxfeat/Cargo.toml @@ -22,6 +22,7 @@ fp_simd = ["ruxhal/fp_simd", "ruxfs/fp_simd"] # Interrupts irq = ["ruxhal/irq", "ruxruntime/irq", "ruxtask?/irq"] +gic-v3 = ["ruxhal/gic-v3","ruxruntime/gic-v3"] # Real time clock rtc = ["ruxhal/rtc", "ruxruntime/rtc"] @@ -31,7 +32,13 @@ alloc = ["axalloc", "ruxruntime/alloc", "ruxfs/alloc", "ruxhal/alloc"] alloc-tlsf = ["axalloc/tlsf"] alloc-slab = ["axalloc/slab"] alloc-buddy = ["axalloc/buddy"] -paging = ["alloc", "ruxhal/paging", "ruxruntime/paging"] +paging = [ + "alloc", + "ruxhal/paging", + "ruxtask/paging", + "ruxruntime/paging", + "ruxmm/paging", +] tls = ["alloc", "ruxhal/tls", "ruxruntime/tls", "ruxtask?/tls"] # Multi-threading and scheduler @@ -47,9 +54,14 @@ sched_cfs = ["ruxtask/sched_cfs", "irq"] # File system fs = ["alloc", "dep:ruxfs", "ruxruntime/fs"] -blkfs = ["ruxdriver/virtio-blk", "ruxruntime/blkfs"] +blkfs = ["ruxdriver/virtio-blk", "ruxruntime/blkfs", "ruxfs/blkfs"] +fusefs = ["ruxruntime/fusefs"] myfs = ["ruxfs?/myfs"] 9pfs = [] +fatfs = ["ruxfs?/fatfs"] +# lwext4_rust = ["ruxfs?/lwext4_rust"] +ext4_rs = ["ruxfs?/ext4_rs"] +another_ext4 = ["ruxfs?/another_ext4"] # Networking net = ["alloc", "ruxdriver/virtio-net", "dep:ruxnet", "ruxruntime/net"] @@ -73,6 +85,15 @@ virtio-9p = [ ] net-9p = ["9pfs", "net", "rux9p/net-9p", "ruxruntime/net-9p"] +# virtio console +virtio_console = [ + "ruxhal/virtio_console", + "ruxruntime/virtio_console", + "alloc", + "ruxdriver/virtio_console", + "ruxdriver/virtio", +] + # Device drivers bus-mmio = ["ruxdriver?/bus-mmio"] bus-pci = ["ruxdriver?/bus-pci"] @@ -80,26 +101,25 @@ driver-ramdisk = ["ruxdriver?/ramdisk", "ruxfs?/use-ramdisk"] driver-ixgbe = ["ruxdriver?/ixgbe"] driver-bcm2835-sdhci = ["ruxdriver?/bcm2835-sdhci"] -# Logging -log-level-off = ["axlog/log-level-off"] -log-level-error = ["axlog/log-level-error"] -log-level-warn = ["axlog/log-level-warn"] -log-level-info = ["axlog/log-level-info"] -log-level-debug = ["axlog/log-level-debug"] -log-level-trace = ["axlog/log-level-trace"] -tty = ["ruxhal/tty", "ruxruntime/tty", "alloc", "irq"] + +# this feature has already been deprecated +tty = [] [dependencies] ruxruntime = { path = "../../modules/ruxruntime" } ruxhal = { path = "../../modules/ruxhal" } -axlog = { path = "../../modules/axlog" } +ruxmm = { path = "../../modules/ruxmm" } axalloc = { path = "../../modules/axalloc", optional = true } ruxdriver = { path = "../../modules/ruxdriver", optional = true } ruxfs = { path = "../../modules/ruxfs", optional = true } rux9p = { path = "../../modules/rux9p", optional = true } ruxnet = { path = "../../modules/ruxnet", optional = true } +ruxfuse = { path = "../../modules/ruxfuse", optional = true } +ruxvda = { path = "../../modules/ruxvda", optional = true } ruxdisplay = { path = "../../modules/ruxdisplay", optional = true } axsync = { path = "../../modules/axsync", optional = true } ruxtask = { path = "../../modules/ruxtask", optional = true } spinlock = { path = "../../crates/spinlock", optional = true } +axlog = { path = "../../crates/axlog" } +tty = { path = "../../crates/tty" } diff --git a/api/ruxos_posix_api/Cargo.toml b/api/ruxos_posix_api/Cargo.toml index 6ae5c1cea..6c81b07a3 100644 --- a/api/ruxos_posix_api/Cargo.toml +++ b/api/ruxos_posix_api/Cargo.toml @@ -19,12 +19,12 @@ default = [] smp = ["ruxfeat/smp"] alloc = ["dep:axalloc", "ruxfeat/alloc"] -paging = ["alloc", "ruxfeat/paging"] -multitask = ["ruxfeat/multitask", "ruxtask/multitask", "dep:ruxfutex"] +paging = ["alloc", "ruxfeat/paging", "ruxmm"] +multitask = ["ruxfeat/multitask", "ruxfuse/multitask", "ruxtask/multitask", "dep:ruxfutex"] fd = ["alloc"] -fs = ["dep:ruxfs", "ruxfeat/fs", "fd"] +fs = ["dep:ruxfs", "ruxfeat/fs", "fd", "ruxfuse"] net = ["dep:ruxnet", "ruxfeat/net", "fd"] -signal = ["ruxruntime/signal"] +signal = ["ruxruntime/signal", "ruxhal/signal", "ruxtask/signal"] pipe = ["fd"] select = ["fd"] epoll = ["fd"] @@ -40,31 +40,39 @@ musl = ["ruxhal/musl", "ruxruntime/musl", "axalloc/slab", "ruxtask/musl"] ruxfeat = { path = "../ruxfeat" } ruxruntime = { path = "../../modules/ruxruntime" } ruxconfig = { path = "../../modules/ruxconfig" } -axlog = { path = "../../modules/axlog" } ruxhal = { path = "../../modules/ruxhal" } axsync = { path = "../../modules/axsync" } ruxfdtable = { path = "../../modules/ruxfdtable" } +ruxmm = { path = "../../modules/ruxmm", optional = true } ruxfutex = { path = "../../modules/ruxfutex", optional = true } axalloc = { path = "../../modules/axalloc", optional = true } -ruxtask = { path = "../../modules/ruxtask", optional = true } +ruxtask = { path = "../../modules/ruxtask", features = [ + "notest", +], optional = true } ruxfs = { path = "../../modules/ruxfs", optional = true } +ruxfuse = { path = "../../modules/ruxfuse", optional = true } ruxnet = { path = "../../modules/ruxnet", optional = true } # Other crates +tty = { path = "../../crates/tty" } +axlog = { path = "../../crates/axlog" } axio = { path = "../../crates/axio" } axerrno = { path = "../../crates/axerrno" } +ringbuffer = { path = "../../crates/ringbuffer" } +iovec = { path = "../../crates/iovec" } memory_addr = "0.1.0" static_assertions = "1.1.0" -spin = { version = "0.9" } +spin = { workspace = true } spinlock = { path = "../../crates/spinlock" } lazy_static = { version = "1.4", features = ["spin_no_std"] } flatten_objects = { path = "../../crates/flatten_objects" } page_table = { path = "../../crates/page_table" } crate_interface = "0.1.1" +capability = { path = "../../crates/capability" } cfg-if = "1.0" elf = { version = "0.7", default-features = false } -bitflags = "2.2" +bitflags = { workspace = true } lazy_init = { path = "../../crates/lazy_init" } diff --git a/api/ruxos_posix_api/build.rs b/api/ruxos_posix_api/build.rs index a5b17661b..5ef6bbc7b 100644 --- a/api/ruxos_posix_api/build.rs +++ b/api/ruxos_posix_api/build.rs @@ -94,6 +94,7 @@ typedef struct {{ "rlimit", "aibuf", "msghdr", + "cmsghdr", "pthread_cond_t", "pthread_condattr_t", "sysinfo", @@ -105,12 +106,15 @@ typedef struct {{ "kstat", "stack_t", "ino_t", + "rusage", "dirent", ]; let allow_vars = [ "O_.*", "AF_.*", + "SO_.*", "SOCK_.*", + "SOL_.*", "IPPROTO_.*", "FD_.*", "F_.*", @@ -130,6 +134,8 @@ typedef struct {{ "MS_.+", "MREMAP_.+", "GRND_.*", + "S_IF.+", + "SCM_.*", ]; #[derive(Debug)] @@ -138,14 +144,14 @@ typedef struct {{ impl bindgen::callbacks::ParseCallbacks for MyCallbacks { fn include_file(&self, fname: &str) { if !fname.contains("ax_pthread_mutex.h") { - println!("cargo:rerun-if-changed={}", fname); + println!("cargo:rerun-if-changed={fname}"); } } } let mut builder = bindgen::Builder::default() .header(in_file) - .clang_arg("-I./../../ulib/ruxlibc/include") + .clang_arg("-I./../../ulib/include") .parse_callbacks(Box::new(MyCallbacks)) .derive_default(true) .size_t_is_usize(false) @@ -164,7 +170,7 @@ typedef struct {{ .expect("Couldn't write bindings!"); } - gen_pthread_mutex("../../ulib/ruxlibc/include/ax_pthread_mutex.h").unwrap(); - gen_pthread_cond("../../ulib/ruxlibc/include/ax_pthread_cond.h").unwrap(); + gen_pthread_mutex("../../ulib/include/ax_pthread_mutex.h").unwrap(); + gen_pthread_cond("../../ulib/include/ax_pthread_cond.h").unwrap(); gen_c_to_rust_bindings("ctypes.h", "src/ctypes_gen.rs"); } diff --git a/api/ruxos_posix_api/ctypes.h b/api/ruxos_posix_api/ctypes.h index 6298ce6c3..1d43015d5 100644 --- a/api/ruxos_posix_api/ctypes.h +++ b/api/ruxos_posix_api/ctypes.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include diff --git a/api/ruxos_posix_api/src/imp/cap.rs b/api/ruxos_posix_api/src/imp/cap.rs index 4e4d28697..73f32f6a2 100644 --- a/api/ruxos_posix_api/src/imp/cap.rs +++ b/api/ruxos_posix_api/src/imp/cap.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + use core::ffi::c_int; #[derive(Debug, Clone, Copy)] diff --git a/api/ruxos_posix_api/src/imp/execve/auxv.rs b/api/ruxos_posix_api/src/imp/execve/auxv.rs index 169bed630..667c723af 100644 --- a/api/ruxos_posix_api/src/imp/execve/auxv.rs +++ b/api/ruxos_posix_api/src/imp/execve/auxv.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #![allow(unused)] pub const AT_NULL: usize = 0; diff --git a/api/ruxos_posix_api/src/imp/execve/load_elf.rs b/api/ruxos_posix_api/src/imp/execve/load_elf.rs index 2359148c1..431b6c7cd 100644 --- a/api/ruxos_posix_api/src/imp/execve/load_elf.rs +++ b/api/ruxos_posix_api/src/imp/execve/load_elf.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + use crate::{ctypes::kstat, *}; use alloc::{vec, vec::Vec}; use core::ptr::null_mut; @@ -88,10 +97,7 @@ impl ElfProg { .unwrap() .unwrap() .sh_offset as usize; - debug!( - "sys_execve: loaded ELF in 0x{:x}, .text is 0x{:x}", - base, text_section_addr - ); + debug!("sys_execve: loaded ELF in 0x{base:x}, .text is 0x{text_section_addr:x}"); // create retval Self { diff --git a/api/ruxos_posix_api/src/imp/execve/mod.rs b/api/ruxos_posix_api/src/imp/execve/mod.rs index 4b1dc5c46..67f03fabd 100644 --- a/api/ruxos_posix_api/src/imp/execve/mod.rs +++ b/api/ruxos_posix_api/src/imp/execve/mod.rs @@ -1,9 +1,19 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + mod auxv; mod load_elf; mod stack; use alloc::vec; use core::ffi::c_char; +use ruxtask::current; use crate::{ config, @@ -14,9 +24,11 @@ use crate::{ /// int execve(const char *pathname, char *const argv[], char *const envp[] ); pub fn sys_execve(pathname: *const c_char, argv: usize, envp: usize) -> ! { + debug!("execve: pathname {pathname:?}, argv {argv:?}, envp {envp:?}"); use auxv::*; let path = char_ptr_to_str(pathname).unwrap(); + debug!("sys_execve: path is {path}"); let prog = load_elf::ElfProg::new(path); // get entry @@ -29,10 +41,11 @@ pub fn sys_execve(pathname: *const c_char, argv: usize, envp: usize) -> ! { let interp_prog = load_elf::ElfProg::new(interp_path); entry = interp_prog.entry; at_base = interp_prog.base; - debug!("sys_execve: INTERP base is {:x}", at_base); + debug!("sys_execve: INTERP base is {at_base:x}"); }; // create stack + // memory broken, use stack alloc to store args and envs let mut stack = stack::Stack::new(); // non 8B info @@ -100,11 +113,13 @@ pub fn sys_execve(pathname: *const c_char, argv: usize, envp: usize) -> ! { let mut argv = argv as *const usize; unsafe { - while *argv != 0 { - arg_vec.push(*argv); - argv = argv.add(1); + if !argv.is_null() { + while *argv != 0 { + arg_vec.push(*argv); + argv = argv.add(1); + } + arg_vec.push(0); } - arg_vec.push(0); } // push @@ -119,6 +134,23 @@ pub fn sys_execve(pathname: *const c_char, argv: usize, envp: usize) -> ! { prog.entry ); + // TODO: may lead to memory leaky, release stack after the change of stack + current().set_stack_top(stack.stack_top() - stack.stack_size(), stack.stack_size()); + warn!( + "sys_execve: current_id_name {:?}, stack top 0x{:x}, size 0x{:x}", + current().id_name(), + current().stack_top(), + stack.stack_size() + ); + + current() + .fs + .lock() + .as_mut() + .unwrap() + .fd_table + .do_close_on_exec(); + set_sp_and_jmp(sp, entry); } @@ -143,6 +175,17 @@ fn set_sp_and_jmp(sp: usize, entry: usize) -> ! { in(reg)entry, ); } + #[cfg(target_arch = "riscv64")] + unsafe { + core::arch::asm!( + " + mv sp, {0} + jalr {1} + ", + in(reg) sp, + in(reg) entry, + ); + } unreachable!("sys_execve: unknown arch, sp 0x{sp:x}, entry 0x{entry:x}"); } diff --git a/api/ruxos_posix_api/src/imp/execve/stack.rs b/api/ruxos_posix_api/src/imp/execve/stack.rs index 4561a09f3..6c2435a98 100644 --- a/api/ruxos_posix_api/src/imp/execve/stack.rs +++ b/api/ruxos_posix_api/src/imp/execve/stack.rs @@ -1,9 +1,21 @@ -use alloc::{vec, vec::Vec}; +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use alloc::vec::Vec; +use ruxtask::task::TaskStack; const STACK_SIZE: usize = ruxconfig::TASK_STACK_SIZE; #[derive(Debug)] pub struct Stack { + /// task stack + task_stack: TaskStack, /// stack data: Vec, /// index of top byte of stack @@ -13,9 +25,15 @@ pub struct Stack { impl Stack { /// alloc a stack pub fn new() -> Self { - Self { - data: vec![0u8; STACK_SIZE], - top: STACK_SIZE, + let task_stack = TaskStack::alloc(STACK_SIZE); + unsafe { + let start = task_stack.top().as_mut_ptr().sub(STACK_SIZE); + + Self { + task_stack, + data: Vec::from_raw_parts(start, STACK_SIZE, STACK_SIZE), + top: STACK_SIZE, + } } } @@ -24,6 +42,14 @@ impl Stack { self.data.as_ptr() as usize + self.top } + pub fn stack_size(&self) -> usize { + self.data.len() + } + + pub fn stack_top(&self) -> usize { + self.task_stack.top().into() + } + /// push data to stack and return the addr of sp pub fn push(&mut self, data: &[T], align: usize) -> usize { // move sp to right place @@ -41,3 +67,9 @@ impl Stack { sp as usize } } + +impl Drop for Stack { + fn drop(&mut self) { + error!("execve's stack dropped. {self:#?}"); + } +} diff --git a/api/ruxos_posix_api/src/imp/fd_ops.rs b/api/ruxos_posix_api/src/imp/fd_ops.rs index 6f271de17..7494dabb0 100644 --- a/api/ruxos_posix_api/src/imp/fd_ops.rs +++ b/api/ruxos_posix_api/src/imp/fd_ops.rs @@ -7,13 +7,13 @@ * See the Mulan PSL v2 for more details. */ -use alloc::sync::Arc; use core::ffi::c_int; -use axerrno::{LinuxError, LinuxResult}; -use ruxfdtable::{FileLike, RuxStat, RuxTimeSpec, FD_TABLE, RUX_FILE_LIMIT}; +use axerrno::LinuxError; +use ruxfdtable::{OpenFlags, RuxStat, RuxTimeSpec, RUX_FILE_LIMIT}; +use ruxtask::current; +pub use ruxtask::fs::{close_file_like, get_file_like}; -use super::stdio::{stdin, stdout}; use crate::ctypes; impl From for RuxTimeSpec { @@ -26,7 +26,7 @@ impl From for RuxTimeSpec { } impl From for RuxStat { - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] fn from(cstat: ctypes::stat) -> Self { RuxStat { st_dev: cstat.st_dev, @@ -48,7 +48,7 @@ impl From for RuxStat { } } - #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] + #[cfg(target_arch = "x86_64")] fn from(cstat: ctypes::stat) -> Self { RuxStat { st_dev: cstat.st_dev, @@ -80,7 +80,7 @@ impl From for ctypes::timespec { } impl From for ctypes::stat { - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] fn from(rstat: RuxStat) -> Self { ctypes::stat { st_dev: rstat.st_dev, @@ -102,7 +102,7 @@ impl From for ctypes::stat { } } - #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] + #[cfg(target_arch = "x86_64")] fn from(rstat: RuxStat) -> Self { ctypes::stat { st_dev: rstat.st_dev, @@ -124,84 +124,46 @@ impl From for ctypes::stat { } } -lazy_static::lazy_static! { - static ref MUST_EXEC: usize = { - FD_TABLE.write().add_at(0, Arc::new(stdin()) as _).unwrap(); // stdin - FD_TABLE.write().add_at(1, Arc::new(stdout()) as _).unwrap(); // stdout - FD_TABLE.write().add_at(2, Arc::new(stdout()) as _).unwrap(); // stderr - 0 - }; -} - -pub fn get_file_like(fd: c_int) -> LinuxResult> { - let _exec = *MUST_EXEC; - FD_TABLE - .read() - .get(fd as usize) - .cloned() - .ok_or(LinuxError::EBADF) -} - -pub fn add_file_like(f: Arc) -> LinuxResult { - let _exec = *MUST_EXEC; - Ok(FD_TABLE.write().add(f).ok_or(LinuxError::EMFILE)? as c_int) -} - -pub fn close_file_like(fd: c_int) -> LinuxResult { - let _exec = *MUST_EXEC; - let f = FD_TABLE - .write() - .remove(fd as usize) - .ok_or(LinuxError::EBADF)?; - drop(f); - Ok(()) -} - /// Close a file by `fd`. pub fn sys_close(fd: c_int) -> c_int { - debug!("sys_close <= {}", fd); - if (0..=2).contains(&fd) { - return 0; // stdin, stdout, stderr - } + debug!("sys_close <= {fd}"); syscall_body!(sys_close, close_file_like(fd).map(|_| 0)) } -fn dup_fd(old_fd: c_int) -> LinuxResult { - let f = get_file_like(old_fd)?; - let new_fd = add_file_like(f)?; - Ok(new_fd) -} - /// Duplicate a file descriptor. pub fn sys_dup(old_fd: c_int) -> c_int { - debug!("sys_dup <= {}", old_fd); - syscall_body!(sys_dup, dup_fd(old_fd)) + debug!("sys_dup <= {old_fd}"); + syscall_body!(sys_dup, { + let binding_task = current(); + let mut binding_fs = binding_task.fs.lock(); + let new_fd = binding_fs.as_mut().unwrap().fd_table.dup(old_fd as _)?; + Ok(new_fd as c_int) + }) } /// Duplicate a file descriptor, but it uses the file descriptor number specified in `new_fd`. /// -/// TODO: `dup2` should forcibly close new_fd if it is already opened. +/// The close-on-exec flag for the duplicate descriptor is off. pub fn sys_dup2(old_fd: c_int, new_fd: c_int) -> c_int { - debug!("sys_dup2 <= old_fd: {}, new_fd: {}", old_fd, new_fd); + debug!("sys_dup2 <= old_fd: {old_fd}, new_fd: {new_fd}"); syscall_body!(sys_dup2, { if old_fd == new_fd { - let r = sys_fcntl(old_fd, ctypes::F_GETFD as _, 0); - if r >= 0 { - return Ok(old_fd); - } else { - return Ok(r); - } + // check if `oldfd` isn't an open file descriptor. If it not, return `EBADF` + get_file_like(old_fd as _)?; + return Ok(new_fd); } if new_fd as usize >= RUX_FILE_LIMIT { return Err(LinuxError::EBADF); } - close_file_like(new_fd)?; - let f = get_file_like(old_fd)?; - FD_TABLE - .write() - .add_at(new_fd as usize, f) - .ok_or(LinuxError::EMFILE)?; + // The steps of closing and reusing the file descriptor newfd are performed atomically. + current() + .fs + .lock() + .as_mut() + .unwrap() + .fd_table + .dup3(old_fd as _, new_fd as _, false)?; Ok(new_fd) }) @@ -210,22 +172,20 @@ pub fn sys_dup2(old_fd: c_int, new_fd: c_int) -> c_int { /// `dup3` used by A64 for MUSL #[cfg(feature = "musl")] pub fn sys_dup3(old_fd: c_int, new_fd: c_int, flags: c_int) -> c_int { - debug!( - "sys_dup3 <= old_fd: {}, new_fd: {}, flags: {:x}", - old_fd, new_fd, flags - ); + debug!("sys_dup3 <= old_fd: {old_fd}, new_fd: {new_fd}, flags: {flags:x}"); syscall_body!(sys_dup3, { if old_fd == new_fd { return Err(LinuxError::EINVAL); } - sys_dup2(old_fd, new_fd); - if flags as u32 & ctypes::O_CLOEXEC != 0 { - sys_fcntl( - new_fd, - ctypes::F_SETFD as c_int, - ctypes::FD_CLOEXEC as usize, - ); - } + let cloexec = (flags as u32 & ctypes::O_CLOEXEC) != 0; + let binding_task = current(); + let mut binding_fs = binding_task.fs.lock(); + binding_fs + .as_mut() + .unwrap() + .fd_table + .dup3(old_fd as _, new_fd as _, cloexec)?; + Ok(new_fd) }) } @@ -234,48 +194,70 @@ pub fn sys_dup3(old_fd: c_int, new_fd: c_int, flags: c_int) -> c_int { /// /// TODO: `SET/GET` command is ignored, hard-code stdin/stdout pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> c_int { - debug!("sys_fcntl <= fd: {} cmd: {} arg: {}", fd, cmd, arg); + debug!("sys_fcntl <= fd: {fd} cmd: {cmd} arg: {arg}"); syscall_body!(sys_fcntl, { match cmd as u32 { - ctypes::F_DUPFD => dup_fd(fd), + ctypes::F_DUPFD => { + let new_fd = current() + .fs + .lock() + .as_mut() + .unwrap() + .fd_table + .dup_with_low_bound(fd as _, arg as _, false)?; + Ok(new_fd as _) + } + ctypes::F_GETFD => { + let binding_task = current(); + let mut binding_fs = binding_task.fs.lock(); + let fd_table = &mut binding_fs.as_mut().unwrap().fd_table; + if fd_table.get(fd as _).is_none() { + return Err(LinuxError::EBADF); + } + let cloexec = if fd_table.get_cloexec(fd as _) { + ctypes::FD_CLOEXEC + } else { + 0 + }; + Ok(cloexec as _) + } ctypes::F_DUPFD_CLOEXEC => { - // TODO: Change fd flags - dup_fd(fd) + let new_fd = current() + .fs + .lock() + .as_mut() + .unwrap() + .fd_table + .dup_with_low_bound(fd as _, arg as _, true)?; + Ok(new_fd as _) } ctypes::F_SETFL => { - if fd == 0 || fd == 1 || fd == 2 { - return Ok(0); - } - get_file_like(fd)?.set_nonblocking(arg & (ctypes::O_NONBLOCK as usize) > 0)?; + // Set the file status flags to the value specified by `arg` + let f = get_file_like(fd)?; + let old_access_flags = f.flags() & OpenFlags::O_ACCMODE; + f.set_flags( + old_access_flags | OpenFlags::from_bits_truncate(arg as _).status_flags(), + )?; Ok(0) } ctypes::F_GETFL => { - use ctypes::{O_RDONLY, O_RDWR, O_WRONLY}; - let f_state = get_file_like(fd)?.poll()?; - let mut flags: core::ffi::c_uint = 0; - // Only support read/write flags(O_ACCMODE) - if f_state.writable && f_state.readable { - flags |= O_RDWR; - } else if f_state.writable { - flags |= O_WRONLY; - } else if f_state.readable { - flags |= O_RDONLY; - } - Ok(flags as c_int) + // Return the file access mode and the file status flags; `arg` is ignored. + Ok(get_file_like(fd)?.flags().getfl().bits()) } ctypes::F_SETFD => { - if arg == 0 || arg == 1 || arg == 2 { - return Ok(0); + let cloexec = + OpenFlags::from_bits_truncate(arg as _).contains(OpenFlags::O_CLOEXEC); + let binding_task = current(); + let mut binding_fs = binding_task.fs.lock(); + let fd_table = &mut binding_fs.as_mut().unwrap().fd_table; + if fd_table.get(fd as _).is_none() { + return Err(LinuxError::EBADF); } - FD_TABLE - .write() - .add_at(arg, get_file_like(fd)?) - .ok_or(LinuxError::EMFILE)?; - let _ = close_file_like(fd); + fd_table.set_cloexec(fd as _, cloexec); Ok(0) } _ => { - warn!("unsupported fcntl parameters: cmd {}", cmd); + warn!("unsupported fcntl parameters: cmd {cmd}"); Ok(0) } } diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index 89019d865..e97f16aa9 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -7,195 +7,39 @@ * See the Mulan PSL v2 for more details. */ -use alloc::{borrow::Cow, string::String, sync::Arc}; -use core::ffi::{c_char, c_int, c_long, c_void, CStr}; +use alloc::string::String; +use alloc::sync::Arc; +use core::{ + ffi::{c_char, c_int, c_long, c_ulong, c_void, CStr}, + str, +}; + +use ruxfs::{api::FileType, fops::lookup, FilePerm, MountPoint}; use axerrno::{LinuxError, LinuxResult}; -use axio::{PollState, SeekFrom}; -use axsync::Mutex; -use ruxfdtable::{FileLike, RuxStat}; +use axio::{Error, SeekFrom}; +use ruxfdtable::{OpenFlags, RuxStat}; use ruxfs::{ - api::set_current_dir, - fops::{DirEntry, OpenOptions}, + fops::{self, open_file_like}, + AbsPath, DirEntry, Directory, File, RelPath, }; -use super::fd_ops::get_file_like; -use crate::ctypes; -use alloc::vec::Vec; - -pub struct File { - pub(crate) inner: Mutex, -} - -impl File { - pub(crate) fn new(inner: ruxfs::fops::File) -> Self { - Self { - inner: Mutex::new(inner), - } - } - - pub(crate) fn add_to_fd_table(self) -> LinuxResult { - super::fd_ops::add_file_like(Arc::new(self)) - } - - pub(crate) fn from_fd(fd: c_int) -> LinuxResult> { - let f = super::fd_ops::get_file_like(fd)?; - f.into_any() - .downcast::() - .map_err(|_| LinuxError::EINVAL) - } -} - -impl FileLike for File { - fn read(&self, buf: &mut [u8]) -> LinuxResult { - Ok(self.inner.lock().read(buf)?) - } - - fn write(&self, buf: &[u8]) -> LinuxResult { - Ok(self.inner.lock().write(buf)?) - } - - fn flush(&self) -> LinuxResult { - Ok(self.inner.lock().flush()?) - } - - fn stat(&self) -> LinuxResult { - let metadata = self.inner.lock().get_attr()?; - let ty = metadata.file_type() as u8; - let perm = metadata.perm().bits() as u32; - let st_mode = ((ty as u32) << 12) | perm; - - // Inode of files, for musl dynamic linker. - // WARN: there will be collision for files with the same size. - // TODO: implement real inode. - let st_ino = metadata.size() + st_mode as u64; - - let res = RuxStat::from(ctypes::stat { - st_ino, - st_nlink: 1, - st_mode, - st_uid: 1000, - st_gid: 1000, - st_size: metadata.size() as _, - st_blocks: metadata.blocks() as _, - st_blksize: 512, - ..Default::default() - }); - - Ok(res) - } - - fn into_any(self: Arc) -> Arc { - self - } - - fn poll(&self) -> LinuxResult { - Ok(PollState { - readable: true, - writable: true, - }) - } - - fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult { - Ok(()) - } -} - -pub struct Directory { - inner: Mutex, -} - -impl Directory { - fn new(inner: ruxfs::fops::Directory) -> Self { - Self { - inner: Mutex::new(inner), - } - } - - fn add_to_fd_table(self) -> LinuxResult { - super::fd_ops::add_file_like(Arc::new(self)) - } - - fn from_fd(fd: c_int) -> LinuxResult> { - let f = super::fd_ops::get_file_like(fd)?; - f.into_any() - .downcast::() - .map_err(|_| LinuxError::EINVAL) - } -} - -impl FileLike for Directory { - fn read(&self, _buf: &mut [u8]) -> LinuxResult { - Err(LinuxError::EACCES) - } - - fn write(&self, _buf: &[u8]) -> LinuxResult { - Err(LinuxError::EACCES) - } - - fn flush(&self) -> LinuxResult { - Ok(()) - } - - fn stat(&self) -> LinuxResult { - let metadata = self.inner.lock().get_attr()?; - let ty = metadata.file_type() as u8; - let perm = metadata.perm().bits() as u32; - let st_mode = ((ty as u32) << 12) | perm; - Ok(RuxStat::from(ctypes::stat { - st_ino: 1, - st_nlink: 1, - st_mode, - st_uid: 1000, - st_gid: 1000, - st_size: metadata.size() as _, - st_blocks: metadata.blocks() as _, - st_blksize: 512, - ..Default::default() - })) - } +use crate::{ctypes, utils::char_ptr_to_str}; +use ruxtask::fs::{add_file_like, get_file_like, get_umask}; - fn into_any(self: Arc) -> Arc { - self - } - - fn poll(&self) -> LinuxResult { - Ok(PollState { - readable: true, - writable: true, - }) - } +use super::stdio::{Stdin, Stdout}; - fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult { - Ok(()) - } -} +struct InitFsImpl; -/// Convert open flags to [`OpenOptions`]. -fn flags_to_options(flags: c_int, _mode: ctypes::mode_t) -> OpenOptions { - let flags = flags as u32; - let mut options = OpenOptions::new(); - match flags & 0b11 { - ctypes::O_RDONLY => options.read(true), - ctypes::O_WRONLY => options.write(true), - _ => { - options.read(true); - options.write(true); - } - }; - if flags & ctypes::O_APPEND != 0 { - options.append(true); - } - if flags & ctypes::O_TRUNC != 0 { - options.truncate(true); - } - if flags & ctypes::O_CREAT != 0 { - options.create(true); +#[crate_interface::impl_interface] +impl ruxtask::fs::InitFs for InitFsImpl { + fn add_stdios_to_fd_table(fs: &mut ruxtask::fs::FileSystem) { + debug!("init initial process's fd_table"); + let fd_table = &mut fs.fd_table; + fd_table.add(Arc::new(Stdin::default()) as _, OpenFlags::empty()); // stdin + fd_table.add(Arc::new(Stdout {}) as _, OpenFlags::empty()); // stdout + fd_table.add(Arc::new(Stdout {}) as _, OpenFlags::empty()); // stderr } - if flags & ctypes::O_EXEC != 0 { - options.create_new(true); - } - options } /// Open a file by `filename` and insert it into the file descriptor table. @@ -203,43 +47,17 @@ fn flags_to_options(flags: c_int, _mode: ctypes::mode_t) -> OpenOptions { /// Return its index in the file table (`fd`). Return `EMFILE` if it already /// has the maximum number of files open. pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { - let filename = char_ptr_to_absolute_path(filename); - debug!("sys_open <= {:?} {:#o} {:#o}", filename, flags, mode); - syscall_body!(sys_open, { - let options = flags_to_options(flags, mode); - let file = ruxfs::fops::File::open(&filename?, &options)?; - File::new(file).add_to_fd_table() - }) + sys_openat(ctypes::AT_FDCWD, filename, flags, mode) } /// Open a file under a specific dir -pub fn sys_openat(fd: usize, path: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { - let path = char_ptr_to_absolute_path(path); - let fd: c_int = fd as c_int; - debug!("sys_openat <= {}, {:?}, {:#o} {:#o}", fd, path, flags, mode); +pub fn sys_openat(fd: c_int, path: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { syscall_body!(sys_openat, { - let options = flags_to_options(flags, mode); - if (flags as u32) & ctypes::O_DIRECTORY != 0 { - let dir = if fd == ctypes::AT_FDCWD { - ruxfs::fops::Directory::open_dir(&path?, &options)? - } else { - Directory::from_fd(fd)? - .inner - .lock() - .open_dir_at(&path?, &options)? - }; - Directory::new(dir).add_to_fd_table() - } else { - let file = if fd == ctypes::AT_FDCWD { - ruxfs::fops::File::open(&path?, &options)? - } else { - Directory::from_fd(fd)? - .inner - .lock() - .open_file_at(&path?, &options)? - }; - File::new(file).add_to_fd_table() - } + let path = parse_path_at(fd, path)?; + let flags = OpenFlags::from_bits(flags).ok_or(LinuxError::EINVAL)?; + debug!("sys_openat <= fd {fd} {path:?}, {flags:?}, {mode:#o}"); + let mode = FilePerm::from_bits_truncate(mode as u16 & !get_umask()); + add_file_like(open_file_like(&path, flags, mode)?, flags) }) } @@ -252,13 +70,14 @@ pub fn sys_pread64( count: usize, pos: ctypes::off_t, ) -> ctypes::ssize_t { - debug!("sys_pread64 <= {} {} {}", fd, count, pos); syscall_body!(sys_pread64, { + debug!("sys_pread64 <= {fd} {count} {pos}"); if buf.is_null() { return Err(LinuxError::EFAULT); } let dst = unsafe { core::slice::from_raw_parts_mut(buf as *mut u8, count) }; - let size = File::from_fd(fd)?.inner.lock().read_at(pos as u64, dst)?; + + let size = file_from_fd(fd)?.read_at(pos as u64, dst)?; Ok(size as ctypes::ssize_t) }) } @@ -272,13 +91,13 @@ pub fn sys_pwrite64( count: usize, pos: ctypes::off_t, ) -> ctypes::ssize_t { - debug!("sys_pwrite64 <= {} {} {}", fd, count, pos); syscall_body!(sys_pwrite64, { + debug!("sys_pwrite64 <= {fd} {count} {pos}"); if buf.is_null() { return Err(LinuxError::EFAULT); } let src = unsafe { core::slice::from_raw_parts_mut(buf as *mut u8, count) }; - let size = File::from_fd(fd)?.inner.lock().write_at(pos as u64, src)?; + let size = file_from_fd(fd)?.write_at(pos as u64, src)?; Ok(size as ctypes::ssize_t) }) } @@ -287,24 +106,34 @@ pub fn sys_pwrite64( /// /// Return its position after seek. pub fn sys_lseek(fd: c_int, offset: ctypes::off_t, whence: c_int) -> ctypes::off_t { - debug!("sys_lseek <= {} {} {}", fd, offset, whence); syscall_body!(sys_lseek, { + debug!("sys_lseek <= {fd} {offset} {whence}"); let pos = match whence { 0 => SeekFrom::Start(offset as _), 1 => SeekFrom::Current(offset as _), 2 => SeekFrom::End(offset as _), _ => return Err(LinuxError::EINVAL), }; - let off = File::from_fd(fd)?.inner.lock().seek(pos)?; + let off = file_from_fd(fd)?.seek(pos)?; Ok(off) }) } +/// Truncate a file to a specified length. +pub unsafe fn sys_ftruncate(fd: c_int, length: ctypes::off_t) -> c_int { + syscall_body!(sys_ftruncate, { + debug!("sys_ftruncate <= {fd} {length}"); + let file = file_from_fd(fd)?; + file.truncate(length as u64)?; + Ok(0) + }) +} + /// Synchronize a file's in-core state with storage device /// /// TODO pub unsafe fn sys_fsync(fd: c_int) -> c_int { - debug!("sys_fsync <= fd: {}", fd); + debug!("sys_fsync <= fd: {fd}"); syscall_body!(sys_fsync, Ok(0)) } @@ -312,7 +141,7 @@ pub unsafe fn sys_fsync(fd: c_int) -> c_int { /// /// TODO pub unsafe fn sys_fdatasync(fd: c_int) -> c_int { - debug!("sys_fdatasync <= fd: {}", fd); + debug!("sys_fdatasync <= fd: {fd}"); syscall_body!(sys_fdatasync, Ok(0)) } @@ -320,16 +149,14 @@ pub unsafe fn sys_fdatasync(fd: c_int) -> c_int { /// /// Return 0 if success. pub unsafe fn sys_stat(path: *const c_char, buf: *mut core::ffi::c_void) -> c_int { - let path = char_ptr_to_absolute_path(path); - debug!("sys_stat <= {:?} {:#x}", path, buf as usize); syscall_body!(sys_stat, { + let path = parse_path(path)?; + debug!("sys_stat <= {:?} {:#x}", path, buf as usize); if buf.is_null() { return Err(LinuxError::EFAULT); } - let mut options = OpenOptions::new(); - options.read(true); - let file = ruxfs::fops::File::open(&path?, &options)?; - let st: ctypes::stat = File::new(file).stat()?.into(); + let node = fops::lookup(&path)?; + let st = RuxStat::from(node.get_attr()?); #[cfg(not(feature = "musl"))] { @@ -359,8 +186,8 @@ pub unsafe fn sys_stat(path: *const c_char, buf: *mut core::ffi::c_void) -> c_in /// retrieve information about the file pointed by `fd` pub fn sys_fstat(fd: c_int, kst: *mut core::ffi::c_void) -> c_int { - debug!("sys_fstat <= {} {:#x}", fd, kst as usize); syscall_body!(sys_fstat, { + debug!("sys_fstat <= {} {:#x}", fd, kst as usize); if kst.is_null() { return Err(LinuxError::EFAULT); } @@ -401,9 +228,9 @@ pub fn sys_fstat(fd: c_int, kst: *mut core::ffi::c_void) -> c_int { /// /// Return 0 if success. pub unsafe fn sys_lstat(path: *const c_char, buf: *mut ctypes::stat) -> ctypes::ssize_t { - let path = char_ptr_to_absolute_path(path); - debug!("sys_lstat <= {:?} {:#x}", path, buf as usize); syscall_body!(sys_lstat, { + let path = parse_path(path)?; + debug!("sys_lstat <= {:?} {:#x}", path, buf as usize); if buf.is_null() { return Err(LinuxError::EFAULT); } @@ -414,24 +241,25 @@ pub unsafe fn sys_lstat(path: *const c_char, buf: *mut ctypes::stat) -> ctypes:: /// `newfstatat` used by A64 pub unsafe fn sys_newfstatat( - _fd: c_int, + fd: c_int, path: *const c_char, kst: *mut ctypes::kstat, flag: c_int, ) -> c_int { - let path = char_ptr_to_absolute_path(path); - debug!( - "sys_newfstatat <= fd: {}, path: {:?}, flag: {:x}", - _fd, path, flag - ); syscall_body!(sys_newfstatat, { + let path = parse_path_at(fd, path)?; + debug!("sys_newfstatat <= fd: {fd}, path: {path:?}, flag: {flag:x}"); if kst.is_null() { return Err(LinuxError::EFAULT); } - let mut options = OpenOptions::new(); - options.read(true); - let file = ruxfs::fops::File::open(&path?, &options)?; - let st = File::new(file).stat()?; + let node = fops::lookup(&path)?; + let st = RuxStat::from(node.get_attr()?); + + // TODO: remove this initialization when fields are fully implemented + unsafe { + core::ptr::write_bytes(kst, 0, 1); + } + unsafe { (*kst).st_dev = st.st_dev; (*kst).st_ino = st.st_ino; @@ -454,8 +282,8 @@ pub fn sys_getcwd(buf: *mut c_char, size: usize) -> c_int { if buf.is_null() { return Err(LinuxError::EINVAL); } - let dst = unsafe { core::slice::from_raw_parts_mut(buf as *mut u8, size as _) }; - let cwd = ruxfs::api::current_dir()?; + let dst: &mut [u8] = unsafe { core::slice::from_raw_parts_mut(buf as _, size as _) }; + let cwd = fops::current_dir()?; let cwd = cwd.as_bytes(); if cwd.len() < size { dst[..cwd.len()].copy_from_slice(cwd); @@ -473,28 +301,35 @@ pub fn sys_getcwd(buf: *mut c_char, size: usize) -> c_int { /// Return 0 if the operation succeeds, otherwise return -1. pub fn sys_rename(old: *const c_char, new: *const c_char) -> c_int { syscall_body!(sys_rename, { - let old_path = char_ptr_to_absolute_path(old)?; - let new_path = char_ptr_to_absolute_path(new)?; - debug!("sys_rename <= old: {:?}, new: {:?}", old_path, new_path); - ruxfs::api::rename(&old_path, &new_path)?; + let old = parse_path(old)?; + let new = parse_path(new)?; + debug!("sys_rename <= old: {old:?}, new: {new:?}"); + if old == new { + return Ok(0); + } + match fops::lookup(&old) { + Ok(_) => {} + Err(e) => return Err(e.into()), + } + match fops::lookup(&new) { + Ok(_) => return Err(LinuxError::EEXIST), + Err(Error::NotFound) => {} + Err(e) => return Err(e.into()), + } + fops::rename(&old, &new)?; Ok(0) }) } /// Rename at certain directory pointed by `oldfd` -/// -/// TODO: only support `oldfd`, `newfd` equals to AT_FDCWD pub fn sys_renameat(oldfd: c_int, old: *const c_char, newfd: c_int, new: *const c_char) -> c_int { - let old_path = char_ptr_to_absolute_path(old); - let new_path = char_ptr_to_absolute_path(new); - debug!( - "sys_renameat <= oldfd: {}, old: {:?}, newfd: {}, new: {:?}", - oldfd, old_path, newfd, new_path - ); - assert_eq!(oldfd, ctypes::AT_FDCWD as c_int); - assert_eq!(newfd, ctypes::AT_FDCWD as c_int); syscall_body!(sys_renameat, { - ruxfs::api::rename(&old_path?, &new_path?)?; + let old_path = parse_path_at(oldfd, old)?; + let new_path = parse_path_at(newfd, new)?; + debug!( + "sys_renameat <= oldfd: {oldfd}, old: {old_path:?}, newfd: {newfd}, new: {new_path:?}" + ); + fops::rename(&old_path, &new_path)?; Ok(0) }) } @@ -502,9 +337,27 @@ pub fn sys_renameat(oldfd: c_int, old: *const c_char, newfd: c_int, new: *const /// Remove a directory, which must be empty pub fn sys_rmdir(pathname: *const c_char) -> c_int { syscall_body!(sys_rmdir, { - let path = char_ptr_to_absolute_path(pathname)?; - debug!("sys_rmdir <= path: {:?}", path); - ruxfs::api::remove_dir(&path)?; + let path = parse_path(pathname)?; + debug!("sys_rmdir <= path: {path:?}"); + match fops::lookup(&path) { + Ok(node) => { + let attr = node.get_attr()?; + if !attr.is_dir() { + return Err(LinuxError::ENOTDIR); + } + if fops::is_mount_point(&path) { + return Err(LinuxError::EPERM); + } + if !attr.perm().owner_writable() { + return Err(LinuxError::EPERM); + } + if !node.is_empty()? { + return Err(LinuxError::ENOTEMPTY); + } + fops::remove_dir(&path)?; + } + Err(e) => return Err(e.into()), + } Ok(0) }) } @@ -512,49 +365,115 @@ pub fn sys_rmdir(pathname: *const c_char) -> c_int { /// Removes a file from the filesystem. pub fn sys_unlink(pathname: *const c_char) -> c_int { syscall_body!(sys_unlink, { - let path = char_ptr_to_absolute_path(pathname)?; - debug!("sys_unlink <= path: {:?}", path); - ruxfs::api::remove_file(&path)?; + let path = parse_path(pathname)?; + debug!("sys_unlink <= path: {path:?}"); + match fops::lookup(&path) { + Ok(node) => { + let attr = node.get_attr()?; + if attr.is_dir() { + return Err(LinuxError::EISDIR); + } + if !attr.perm().owner_writable() { + return Err(LinuxError::EPERM); + } + fops::remove_file(&path)?; + } + Err(e) => return Err(e.into()), + } Ok(0) }) } /// deletes a name from the filesystem pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { - debug!( - "sys_unlinkat <= fd: {}, pathname: {:?}, flags: {}", - fd, - char_ptr_to_absolute_path(pathname), - flags - ); - if flags as u32 & ctypes::AT_REMOVEDIR != 0 { - return sys_rmdir(pathname); - } - sys_unlink(pathname) + syscall_body!(sys_unlinkat, { + let path = parse_path_at(fd, pathname)?; + let rmdir = flags as u32 & ctypes::AT_REMOVEDIR != 0; + debug!("sys_unlinkat <= fd: {fd}, pathname: {path:?}, flags: {flags}"); + match fops::lookup(&path) { + Ok(node) => { + let attr = node.get_attr()?; + if rmdir { + if !attr.is_dir() { + return Err(LinuxError::ENOTDIR); + } + if fops::is_mount_point(&path) { + return Err(LinuxError::EPERM); + } + if !attr.perm().owner_writable() { + return Err(LinuxError::EPERM); + } + if !node.is_empty()? { + return Err(LinuxError::ENOTEMPTY); + } + fops::remove_dir(&path)?; + } else { + if attr.is_dir() { + return Err(LinuxError::EISDIR); + } + if !attr.perm().owner_writable() { + return Err(LinuxError::EPERM); + } + fops::remove_file(&path)?; + } + } + Err(e) => return Err(e.into()), + } + Ok(0) + }) } -/// Creates a new, empty directory at the provided path. -pub fn sys_mkdir(pathname: *const c_char, mode: ctypes::mode_t) -> c_int { - // TODO: implement mode - syscall_body!(sys_mkdir, { - let path = char_ptr_to_absolute_path(pathname)?; - debug!("sys_mkdir <= path: {:?}, mode: {:?}", path, mode); - ruxfs::api::create_dir(&path)?; +/// Change permissions of a file +pub fn sys_fchmodat(dirfd: c_int, pathname: *const c_char, mode: ctypes::mode_t) -> c_int { + syscall_body!(sys_mknodat, { + let path = parse_path_at(dirfd, pathname)?; + debug!("sys_fchmodat <= fd: {dirfd}, path: {path:?}, mode: {mode:#o}"); + let node = lookup(&path)?; + node.set_mode(FilePerm::from_bits_truncate(mode as u16))?; + Ok(0) + }) +} + +/// Creates a new, empty file at the provided path. +pub fn sys_mknodat( + fd: c_int, + pathname: *const c_char, + mode: ctypes::mode_t, + _dev: ctypes::dev_t, +) -> c_int { + // TODO: implement permissions mode + syscall_body!(sys_mknodat, { + let path = parse_path_at(fd, pathname)?; + debug!("sys_mknodat <= fd: {fd}, pathname: {path:?}, mode: {mode:#o}, dev: {_dev:x?}"); + let file_type = match mode & ctypes::S_IFMT { + ctypes::S_IFREG => FileType::File, + ctypes::S_IFIFO => FileType::Fifo, + _ => todo!(), + }; + let mode = FilePerm::from_bits_truncate(mode as u16 & !get_umask()); + fops::create(&path, file_type, mode)?; Ok(0) }) } +/// Creates a new, empty directory at the provided path. +pub fn sys_mkdir(pathname: *const c_char, mode: ctypes::mode_t) -> c_int { + sys_mkdirat(ctypes::AT_FDCWD, pathname, mode) +} + /// attempts to create a directory named pathname under directory pointed by `fd` -/// -/// TODO: currently fd is not used pub fn sys_mkdirat(fd: c_int, pathname: *const c_char, mode: ctypes::mode_t) -> c_int { - debug!( - "sys_mkdirat <= fd: {}, pathname: {:?}, mode: {:x?}", - fd, - char_ptr_to_absolute_path(pathname), - mode - ); - sys_mkdir(pathname, mode) + syscall_body!(sys_mkdirat, { + let path = parse_path_at(fd, pathname)?; + debug!("sys_mkdirat <= fd: {fd}, pathname: {path:?}, mode: {mode:#o}",); + let mode = FilePerm::from_bits_truncate(mode as u16 & !get_umask()); + match fops::lookup(&path) { + Ok(_) => return Err(LinuxError::EEXIST), + Err(Error::NotFound) => fops::create(&path, FileType::Dir, mode)?, + Err(e) => return Err(e.into()), + } + Ok(0) + }) } /// Changes the ownership of the file referred to by the open file descriptor fd @@ -565,15 +484,11 @@ pub fn sys_fchownat( gid: ctypes::gid_t, flag: c_int, ) -> c_int { - debug!( - "sys_fchownat <= fd: {}, path: {:?}, uid: {}, gid: {}, flag: {}", - fd, - char_ptr_to_absolute_path(path), - uid, - gid, - flag - ); - syscall_body!(sys_fchownat, Ok(0)) + syscall_body!(sys_fchownat, { + let path = parse_path_at(fd, path)?; + debug!("sys_fchownat <= fd: {fd}, path: {path:?}, uid: {uid}, gid: {gid}, flag: {flag}"); + Ok(0) + }) } /// read value of a symbolic link relative to directory file descriptor @@ -584,67 +499,75 @@ pub fn sys_readlinkat( buf: *mut c_char, bufsize: usize, ) -> usize { - let path = char_ptr_to_absolute_path(pathname); - debug!( - "sys_readlinkat <= path = {:?}, fd = {:}, buf = {:p}, bufsize = {:}", - path, fd, buf, bufsize - ); syscall_body!(sys_readlinkat, { + let path = parse_path_at(fd, pathname)?; + debug!( + "sys_readlinkat <= path = {path:?}, fd = {fd:}, buf = {buf:p}, bufsize = {bufsize:}" + ); Err::(LinuxError::EINVAL) }) } type LinuxDirent64 = ctypes::dirent; - -fn convert_name_to_array(name: &[u8]) -> [i8; 256] { - let mut array = [0i8; 256]; - let len = name.len(); - let name_ptr = name.as_ptr() as *const i8; - let array_ptr = array.as_mut_ptr(); - - unsafe { - core::ptr::copy_nonoverlapping(name_ptr, array_ptr, len); - } - - array -} +/// `d_ino` + `d_off` + `d_reclen` + `d_type` +const DIRENT64_FIXED_SIZE: usize = 19; /// Read directory entries from a directory file descriptor. -/// -/// TODO: check errors, change 280 to a special value -pub unsafe fn sys_getdents64( - fd: c_int, - dirent: *mut LinuxDirent64, - count: ctypes::size_t, -) -> c_long { - debug!( - "sys_getdents64 <= fd: {}, dirent: {:p}, count: {}", - fd, dirent, count - ); - +pub unsafe fn sys_getdents64(fd: c_int, dirp: *mut LinuxDirent64, count: ctypes::size_t) -> c_long { + debug!("sys_getdents64 <= fd: {fd}, dirp: {dirp:p}, count: {count}"); syscall_body!(sys_getdents64, { - let expect_entries = count / 280; - let dir = Directory::from_fd(fd)?; - let mut my_dirent: Vec = - (0..expect_entries).map(|_| DirEntry::default()).collect(); - - let n = dir.inner.lock().read_dir(&mut my_dirent)?; - - for (i, entry) in my_dirent.iter().enumerate() { - let linux_dirent = LinuxDirent64 { - d_ino: 1, - d_off: 280, - d_reclen: 280, - d_type: entry.entry_type() as u8, - d_name: convert_name_to_array(entry.name_as_bytes()), - }; - - unsafe { - core::ptr::write(dirent.add(i), linux_dirent); + if count < DIRENT64_FIXED_SIZE { + return Err(LinuxError::EINVAL); + } + let buf = unsafe { core::slice::from_raw_parts_mut(dirp as *mut u8, count) }; + // EBADFD handles here + let dir = dir_from_fd(fd)?; + // bytes written in buf + let mut written = 0; + + loop { + let mut entry = [DirEntry::default()]; + let offset = dir.entry_idx(); + let n = dir.read_dir(&mut entry)?; + debug!( + "entry {:?}", + str::from_utf8(entry[0].name_as_bytes()).unwrap() + ); + if n == 0 { + return Ok(written as isize); + } + let entry = &entry[0]; + + let name = entry.name_as_bytes(); + let name_len = name.len(); + let entry_size = (DIRENT64_FIXED_SIZE + name_len + 1 + 7) & !7; // align to 8 bytes + + // buf not big enough to hold the entry + if written + entry_size > count { + debug!("buf not big enough"); + // revert the offset + dir.set_entry_idx(offset); + break; } + + // write entry to buffer + let dirent: &mut LinuxDirent64 = + unsafe { &mut *(buf.as_mut_ptr().add(written) as *mut LinuxDirent64) }; + // set fixed-size fields + dirent.d_ino = 1; + dirent.d_off = (offset + 1) as i64; + dirent.d_reclen = entry_size as u16; + dirent.d_type = entry.entry_type() as u8; + // set file name + dirent.d_name[..name_len].copy_from_slice(unsafe { + core::slice::from_raw_parts(name.as_ptr() as *const c_char, name_len) + }); + dirent.d_name[name_len] = 0; + + written += entry_size; } - Ok(n * 280) + Ok(written as isize) }) } @@ -656,10 +579,7 @@ pub unsafe fn sys_preadv( iocnt: c_int, offset: ctypes::off_t, ) -> ctypes::ssize_t { - debug!( - "sys_preadv <= fd: {}, iocnt: {}, offset: {}", - fd, iocnt, offset - ); + debug!("sys_preadv <= fd: {fd}, iocnt: {iocnt}, offset: {offset}"); syscall_body!(sys_preadv, { if !(0..=1024).contains(&iocnt) { return Err(LinuxError::EINVAL); @@ -682,57 +602,156 @@ pub unsafe fn sys_preadv( /// The mode is either the value F_OK, for the existence of the file, /// or a mask consisting of the bitwise OR of one or more of R_OK, W_OK, and X_OK, for the read, write, execute permissions. pub fn sys_faccessat(dirfd: c_int, pathname: *const c_char, mode: c_int, flags: c_int) -> c_int { - let path = char_ptr_to_absolute_path(pathname).unwrap(); - debug!( - "sys_faccessat <= dirfd {} path {} mode {} flags {}", - dirfd, path, mode, flags - ); syscall_body!(sys_faccessat, { - let mut options = OpenOptions::new(); - options.read(true); - let _file = ruxfs::fops::File::open(&path, &options)?; + let path = parse_path_at(dirfd, pathname)?; + debug!("sys_faccessat <= dirfd {dirfd} path {path} mode {mode} flags {flags}"); Ok(0) }) } /// changes the current working directory to the directory specified in path. pub fn sys_chdir(path: *const c_char) -> c_int { - let p = char_ptr_to_absolute_path(path).unwrap(); - debug!("sys_chdir <= path: {}", p); syscall_body!(sys_chdir, { - set_current_dir(&p)?; + let path = parse_path(path)?; + debug!("sys_chdir <= path: {path:?}"); + fops::set_current_dir(path)?; + Ok(0) + }) +} + +pub const MS_NODEV: u32 = 2; +pub const MS_NOSUID: u32 = 4; + +/// umount a filesystem at a specific location in the filesystem tree +pub fn sys_umount2(target: *const c_char, flags: c_int) -> c_int { + info!( + "sys_umount2 <= target: {:?}, flags: {:#x}", + char_ptr_to_str(target), + flags + ); + syscall_body!(sys_umount2, { + let target = char_ptr_to_str(target)?; + let dir = ruxtask::current() + .fs + .lock() + .as_mut() + .unwrap() + .root_dir + .clone(); + dir.umount(&AbsPath::new(target)); + Ok(0) + }) +} + +/// mount a filesystem at a specific location in the filesystem tree +pub fn sys_mount( + source: *const c_char, + raw_target: *const c_char, + filesystemtype: *const c_char, + mountflags: c_ulong, + data: *const c_void, +) -> c_int { + info!( + "sys_mount <= source: {:?}, target: {:?}, filesystemtype: {:?}, mountflags: {:#x}, data: {:p}", + char_ptr_to_str(source), + char_ptr_to_str(raw_target), + char_ptr_to_str(filesystemtype), + mountflags, + data + ); + syscall_body!(sys_mount, { + let f1 = MS_NODEV; //ctypes::MS_NODEV; + let f2 = MS_NOSUID; //ctypes::MS_NOSUID; + info!( + "mount flags: {:#x}, f1: {:#}, f2: {:#}, flag: {:#}", + mountflags, + f1, + f2, + f1 | f2 + ); + if mountflags != (f1 | f2).into() { + warn!("mount flags not supported: {mountflags:#x}"); + } + + let target = char_ptr_to_str(raw_target)?; + let target = String::from(target); + let dir = ruxtask::current() + .fs + .lock() + .as_mut() + .unwrap() + .root_dir + .clone(); + let vfsops = ruxfuse::fuse::fusefs(); + info!("mounting filesystem at {target}"); + dir.mount(MountPoint { + path: target, + fs: vfsops, + })?; Ok(0) }) } -/// from char_ptr get absolute_path_str -pub fn char_ptr_to_absolute_path<'a>(ptr: *const c_char) -> LinuxResult> { +/// perform a memory barrier operation. +pub fn sys_membarrier(cmd: c_int, flags: c_int) -> c_int { + info!("sys_membarrier <= cmd: {cmd}, flags: {flags}"); + syscall_body!(sys_membarrier, Ok(0)) +} + +/// from char_ptr get path_str +fn char_ptr_to_path_str<'a>(ptr: *const c_char) -> LinuxResult<&'a str> { if ptr.is_null() { return Err(LinuxError::EFAULT); } - - let path = unsafe { + unsafe { let cstr = CStr::from_ptr(ptr); - cstr.to_str().map_err(|_| LinuxError::EINVAL)? - }; - - if path.starts_with("..") { - let stripped = path.strip_prefix("..").unwrap(); - let mut cwd = ruxfs::api::current_dir()?; - if let Some(index) = cwd.rfind('/') { - cwd.truncate(index); - if let Some(index) = cwd.rfind('/') { - cwd.truncate(index); - } - } - let absolute_path: String = cwd + stripped; - Ok(Cow::Owned(absolute_path)) - } else if path.starts_with('.') { - let stripped = path.strip_prefix('.').unwrap(); - let cwd = ruxfs::api::current_dir()?; - let absolute_path: String = cwd + stripped; - Ok(Cow::Owned(absolute_path)) + cstr.to_str().map_err(|_| LinuxError::EINVAL) + } +} + +/// Parse `path` argument for fs syscalls. +/// +/// * If the given `path` is absolute, return it as is. +/// * If the given `path` is relative, join it against the current working directory. +pub fn parse_path(path: *const c_char) -> LinuxResult> { + let path = char_ptr_to_path_str(path)?; + if path.starts_with('/') { + Ok(AbsPath::new_canonicalized(path)) } else { - Ok(Cow::Borrowed(path)) + Ok(fops::current_dir()?.join(&RelPath::new_canonicalized(path))) } } + +/// Parse `path` and `dirfd` arguments for fs syscalls. +/// +/// * If the given `path` is absolute, return it as is. +/// * If the given `path` is relative and `dirfd` is `AT_FDCWD`, join it against the +/// current working directory. +/// * If the given `path` is relative and `dirfd` is not `AT_FDCWD`, join it against the +/// directory of the file descriptor. +pub fn parse_path_at(dirfd: c_int, path: *const c_char) -> LinuxResult> { + let path = char_ptr_to_path_str(path)?; + if path.starts_with('/') { + Ok(AbsPath::new_canonicalized(path)) + } else if dirfd == ctypes::AT_FDCWD { + Ok(fops::current_dir()?.join(&RelPath::new_canonicalized(path))) + } else { + Ok(dir_from_fd(dirfd)? + .path() + .join(&RelPath::new_canonicalized(path))) + } +} + +fn file_from_fd(fd: i32) -> LinuxResult> { + get_file_like(fd)? + .into_any() + .downcast::() + .map_err(|_| LinuxError::EINVAL) +} + +fn dir_from_fd(fd: i32) -> LinuxResult> { + get_file_like(fd)? + .into_any() + .downcast::() + .map_err(|_| LinuxError::EINVAL) +} diff --git a/api/ruxos_posix_api/src/imp/getrandom.rs b/api/ruxos_posix_api/src/imp/getrandom.rs index 61cafb103..99425d7dd 100644 --- a/api/ruxos_posix_api/src/imp/getrandom.rs +++ b/api/ruxos_posix_api/src/imp/getrandom.rs @@ -149,19 +149,15 @@ pub unsafe extern "C" fn sys_random() -> c_long { /// Fills the buffer pointed to by buf with up to buflen random bytes. pub unsafe extern "C" fn sys_getrandom(buf: *mut c_void, buflen: size_t, flags: c_int) -> ssize_t { - debug!( - "sys_getrandom <= buf: {:?}, buflen: {}, flags: {}", - buf, buflen, flags - ); + debug!("sys_getrandom <= buf: {buf:?}, buflen: {buflen}, flags: {flags}"); syscall_body!(sys_getrandom, { if buf.is_null() { return Err(LinuxError::EFAULT); } - match flags as _ { - crate::ctypes::GRND_NONBLOCK => {} - crate::ctypes::GRND_RANDOM => {} - _ => return Err(LinuxError::EINVAL), + // BUG: flags are implemented wrongly, flags should be checks bit by bit + if flags != 0 { + warn!("flags are not implemented yet, flags: {flags}, ignored"); } // fill the buffer 8 bytes at a time first, then fill the remaining bytes let buflen_mod = buflen % (core::mem::size_of::() / core::mem::size_of::()); diff --git a/api/ruxos_posix_api/src/imp/io.rs b/api/ruxos_posix_api/src/imp/io.rs index c509c567c..57d12a047 100644 --- a/api/ruxos_posix_api/src/imp/io.rs +++ b/api/ruxos_posix_api/src/imp/io.rs @@ -12,9 +12,12 @@ use axerrno::LinuxError; use core::ffi::{c_int, c_void}; #[cfg(feature = "fd")] -use crate::imp::fd_ops::get_file_like; +use ruxtask::fs::get_file_like; #[cfg(not(feature = "fd"))] -use axio::prelude::*; +use { + crate::imp::stdio::{Stdin, Stdout}, + axio::prelude::*, +}; /// Read data from the file indicated by `fd`. /// @@ -28,11 +31,11 @@ pub fn sys_read(fd: c_int, buf: *mut c_void, count: usize) -> ctypes::ssize_t { let dst = unsafe { core::slice::from_raw_parts_mut(buf as *mut u8, count) }; #[cfg(feature = "fd")] { - Ok(get_file_like(fd)?.read(dst)? as ctypes::ssize_t) + Ok(get_file_like(fd as _)?.read(dst)? as ctypes::ssize_t) } #[cfg(not(feature = "fd"))] match fd { - 0 => Ok(super::stdio::stdin().read(dst)? as ctypes::ssize_t), + 0 => Ok(Stdin::default().read(dst)? as ctypes::ssize_t), 1 | 2 => Err(LinuxError::EPERM), _ => Err(LinuxError::EBADF), } @@ -56,7 +59,7 @@ pub fn sys_write(fd: c_int, buf: *const c_void, count: usize) -> ctypes::ssize_t #[cfg(not(feature = "fd"))] match fd { 0 => Err(LinuxError::EPERM), - 1 | 2 => Ok(super::stdio::stdout().write(src)? as ctypes::ssize_t), + 1 | 2 => Ok(Stdout {}.write(src)? as ctypes::ssize_t), _ => Err(LinuxError::EBADF), } }) @@ -65,7 +68,7 @@ pub fn sys_write(fd: c_int, buf: *const c_void, count: usize) -> ctypes::ssize_t /// Writes `iocnt` buffers of data described by `iov` to the file associated with the file /// descriptor `fd` pub unsafe fn sys_writev(fd: c_int, iov: *const ctypes::iovec, iocnt: c_int) -> ctypes::ssize_t { - debug!("sys_writev <= fd: {}, iocnt: {}", fd, iocnt); + debug!("sys_writev <= fd: {fd}, iocnt: {iocnt}"); syscall_body!(sys_writev, { if !(0..=1024).contains(&iocnt) { return Err(LinuxError::EINVAL); @@ -86,7 +89,7 @@ pub unsafe fn sys_writev(fd: c_int, iov: *const ctypes::iovec, iocnt: c_int) -> /// Reads `iocnt` buffers from the file associated with the file descriptor `fd` into the /// buffers described by `iov` pub unsafe fn sys_readv(fd: c_int, iov: *const ctypes::iovec, iocnt: c_int) -> ctypes::ssize_t { - debug!("sys_readv <= fd: {}, iocnt: {}", fd, iocnt); + debug!("sys_readv <= fd: {fd}, iocnt: {iocnt}"); syscall_body!(sys_readv, { if !(0..=1024).contains(&iocnt) { return Err(LinuxError::EINVAL); diff --git a/api/ruxos_posix_api/src/imp/io_mpx/epoll.rs b/api/ruxos_posix_api/src/imp/io_mpx/epoll.rs index 12bc3b16e..5b41c6e13 100644 --- a/api/ruxos_posix_api/src/imp/io_mpx/epoll.rs +++ b/api/ruxos_posix_api/src/imp/io_mpx/epoll.rs @@ -22,7 +22,8 @@ use ruxfdtable::{FileLike, RuxStat}; use ruxhal::time::current_time; use crate::ctypes; -use crate::imp::fd_ops::{add_file_like, get_file_like}; +use ruxfs::{AbsPath, OpenFlags}; +use ruxtask::fs::{add_file_like, get_file_like}; pub struct EpollInstance { events: Mutex>, @@ -108,6 +109,12 @@ impl EpollInstance { events[events_num].data = ev.data; events_num += 1; } + + if state.pollhup { + events[events_num].events = ctypes::EPOLLHUP; + events[events_num].data = ev.data; + events_num += 1; + } } } } @@ -116,6 +123,10 @@ impl EpollInstance { } impl FileLike for EpollInstance { + fn path(&self) -> AbsPath { + AbsPath::new("/epoll") + } + fn read(&self, _buf: &mut [u8]) -> LinuxResult { Err(LinuxError::ENOSYS) } @@ -146,22 +157,28 @@ impl FileLike for EpollInstance { Err(LinuxError::ENOSYS) } - fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult { + fn set_flags(&self, _flags: ruxfs::OpenFlags) -> LinuxResult { Ok(()) } + + fn flags(&self) -> OpenFlags { + OpenFlags::O_RDWR + } } /// Creates a new epoll instance. /// /// It returns a file descriptor referring to the new epoll instance. -pub fn sys_epoll_create(size: c_int) -> c_int { - debug!("sys_epoll_create <= {}", size); +pub fn sys_epoll_create1(flags: c_int) -> c_int { + debug!("sys_epoll_create <= {flags}"); syscall_body!(sys_epoll_create, { - if size < 0 { + if flags < 0 { return Err(LinuxError::EINVAL); } - let epoll_instance = EpollInstance::new(0); - add_file_like(Arc::new(epoll_instance)) + add_file_like( + Arc::new(EpollInstance::new(0)), + OpenFlags::from_bits_truncate(flags), + ) }) } @@ -172,7 +189,7 @@ pub unsafe fn sys_epoll_ctl( fd: c_int, event: *mut ctypes::epoll_event, ) -> c_int { - debug!("sys_epoll_ctl <= epfd: {} op: {} fd: {}", epfd, op, fd); + debug!("sys_epoll_ctl <= epfd: {epfd} op: {op} fd: {fd}"); syscall_body!(sys_epoll_ctl, { let ret = unsafe { EpollInstance::from_fd(epfd)?.control(op as usize, fd as usize, &(*event))? as c_int @@ -190,10 +207,7 @@ pub unsafe fn sys_epoll_pwait( _sigs: *const ctypes::sigset_t, _sig_num: *const ctypes::size_t, ) -> c_int { - debug!( - "sys_epoll_pwait <= epfd: {}, maxevents: {}, timeout: {}", - epfd, maxevents, timeout - ); + debug!("sys_epoll_pwait <= epfd: {epfd}, maxevents: {maxevents}, timeout: {timeout}"); sys_epoll_wait(epfd, events, maxevents, timeout) } @@ -204,10 +218,7 @@ pub unsafe fn sys_epoll_wait( maxevents: c_int, timeout: c_int, ) -> c_int { - debug!( - "sys_epoll_wait <= epfd: {}, maxevents: {}, timeout: {}", - epfd, maxevents, timeout - ); + debug!("sys_epoll_wait <= epfd: {epfd}, maxevents: {maxevents}, timeout: {timeout}"); syscall_body!(sys_epoll_wait, { if maxevents <= 0 { @@ -245,7 +256,7 @@ pub unsafe fn sys_epoll_wait( return Ok(events_num as c_int); } - if deadline.map_or(false, |ddl| current_time() >= ddl) { + if deadline.is_some_and(|ddl| current_time() >= ddl) { debug!(" timeout!"); return Ok(0); } diff --git a/api/ruxos_posix_api/src/imp/io_mpx/mod.rs b/api/ruxos_posix_api/src/imp/io_mpx/mod.rs index 77a62e3e8..f3dbaebe7 100644 --- a/api/ruxos_posix_api/src/imp/io_mpx/mod.rs +++ b/api/ruxos_posix_api/src/imp/io_mpx/mod.rs @@ -23,7 +23,7 @@ mod poll; mod select; #[cfg(feature = "epoll")] -pub use self::epoll::{sys_epoll_create, sys_epoll_ctl, sys_epoll_pwait, sys_epoll_wait}; +pub use self::epoll::{sys_epoll_create1, sys_epoll_ctl, sys_epoll_pwait, sys_epoll_wait}; #[cfg(feature = "poll")] pub use self::poll::{sys_poll, sys_ppoll}; #[cfg(feature = "select")] diff --git a/api/ruxos_posix_api/src/imp/io_mpx/poll.rs b/api/ruxos_posix_api/src/imp/io_mpx/poll.rs index d9aa4ff70..6bc5fcd06 100644 --- a/api/ruxos_posix_api/src/imp/io_mpx/poll.rs +++ b/api/ruxos_posix_api/src/imp/io_mpx/poll.rs @@ -7,10 +7,11 @@ * See the Mulan PSL v2 for more details. */ -use crate::{ctypes, imp::fd_ops::get_file_like}; use axerrno::{LinuxError, LinuxResult}; use ruxhal::time::current_time; +use ruxtask::fs::get_file_like; +use crate::ctypes; use core::{ffi::c_int, time::Duration}; fn poll_all(fds: &mut [ctypes::pollfd]) -> LinuxResult { @@ -37,6 +38,11 @@ fn poll_all(fds: &mut [ctypes::pollfd]) -> LinuxResult { *revents |= ctypes::EPOLLOUT as i16; events_num += 1; } + + if state.pollhup { + *revents |= ctypes::EPOLLHUP as i16; + events_num += 1; + } } } } @@ -56,21 +62,26 @@ pub unsafe fn sys_ppoll( } else { Duration::from(*timeout).as_millis() as c_int }; - debug!("sys_ppoll <= nfds: {} timeout: {:?}", nfds, to); + debug!("sys_ppoll <= nfds: {nfds} timeout: {to:?}"); sys_poll(fds, nfds, to) } /// Used to monitor multiple file descriptors for events pub unsafe fn sys_poll(fds: *mut ctypes::pollfd, nfds: ctypes::nfds_t, timeout: c_int) -> c_int { - debug!("sys_poll <= nfds: {} timeout: {} ms", nfds, timeout); + debug!("sys_poll <= nfds: {nfds} timeout: {timeout} ms"); syscall_body!(sys_poll, { if nfds == 0 { return Err(LinuxError::EINVAL); } let fds = core::slice::from_raw_parts_mut(fds, nfds as usize); + debug!("[sys_poll] monitored fds is {fds:?}"); let deadline = (!timeout.is_negative()) .then(|| current_time() + Duration::from_millis(timeout as u64)); + for pollfd_item in fds.iter_mut() { + let revents = &mut pollfd_item.revents; + *revents &= 0; + } loop { #[cfg(feature = "net")] ruxnet::poll_interfaces(); @@ -79,7 +90,7 @@ pub unsafe fn sys_poll(fds: *mut ctypes::pollfd, nfds: ctypes::nfds_t, timeout: return Ok(fds_num as c_int); } - if deadline.map_or(false, |ddl| current_time() >= ddl) { + if deadline.is_some_and(|ddl| current_time() >= ddl) { debug!(" timeout!"); return Ok(0); } diff --git a/api/ruxos_posix_api/src/imp/io_mpx/select.rs b/api/ruxos_posix_api/src/imp/io_mpx/select.rs index f8b9f383f..063c8b97a 100644 --- a/api/ruxos_posix_api/src/imp/io_mpx/select.rs +++ b/api/ruxos_posix_api/src/imp/io_mpx/select.rs @@ -12,7 +12,8 @@ use core::ffi::{c_int, c_void}; use axerrno::{LinuxError, LinuxResult}; use ruxhal::time::current_time; -use crate::{ctypes, imp::fd_ops::get_file_like}; +use crate::ctypes; +use ruxtask::fs::get_file_like; const FD_SETSIZE: usize = 1024; const BITS_PER_USIZE: usize = usize::BITS as usize; @@ -33,7 +34,8 @@ impl FdSets { let nfds = nfds.min(FD_SETSIZE); let nfds_usizes = nfds.div_ceil(BITS_PER_USIZE); let mut bits = core::mem::MaybeUninit::<[usize; FD_SETSIZE_USIZES * 3]>::uninit(); - let bits_ptr = unsafe { core::mem::transmute(bits.as_mut_ptr()) }; + let bits_ptr = + unsafe { core::mem::transmute::<*mut [usize; 48], *mut usize>(bits.as_mut_ptr()) }; let copy_from_fd_set = |bits_ptr: *mut usize, fds: *const ctypes::fd_set| unsafe { let dst = core::slice::from_raw_parts_mut(bits_ptr, nfds_usizes); @@ -99,9 +101,13 @@ impl FdSets { unsafe { set_fd_set(res_write_fds, fd) }; res_num += 1; } + if state.pollhup { + unsafe { set_fd_set(res_except_fds, fd) }; + res_num += 1; + } } Err(e) => { - debug!(" except: {} {:?}", fd, e); + debug!(" except: {fd} {e:?}"); if except_bits & bit != 0 { unsafe { set_fd_set(res_except_fds, fd) }; res_num += 1; @@ -150,7 +156,7 @@ pub unsafe fn sys_select( return Ok(res); } - if deadline.map_or(false, |ddl| current_time() >= ddl) { + if deadline.is_some_and(|ddl| current_time() >= ddl) { debug!(" timeout!"); return Ok(0); } @@ -177,7 +183,7 @@ pub unsafe fn sys_pselect6( unsafe fn zero_fd_set(fds: *mut ctypes::fd_set, nfds: usize) { if !fds.is_null() { let nfds_usizes = nfds.div_ceil(BITS_PER_USIZE); - let dst = &mut (*fds).fds_bits[..nfds_usizes]; + let dst = &mut (&mut (*fds).fds_bits)[..nfds_usizes]; dst.fill(0); } } diff --git a/api/ruxos_posix_api/src/imp/ioctl.rs b/api/ruxos_posix_api/src/imp/ioctl.rs index 4838772c9..cb0a4600b 100644 --- a/api/ruxos_posix_api/src/imp/ioctl.rs +++ b/api/ruxos_posix_api/src/imp/ioctl.rs @@ -7,62 +7,33 @@ * See the Mulan PSL v2 for more details. */ -use crate::{imp::fd_ops::get_file_like, sys_getpgid}; -use axerrno::LinuxError; use core::ffi::c_int; +use ruxfdtable::OpenFlags; +use ruxtask::fs::get_file_like; -/// IOCTL oprations -pub const TCGETS: usize = 0x5401; -pub const TIOCGPGRP: usize = 0x540F; -pub const TIOCSPGRP: usize = 0x5410; -pub const TIOCGWINSZ: usize = 0x5413; pub const FIONBIO: usize = 0x5421; pub const FIOCLEX: usize = 0x5451; -#[derive(Clone, Copy, Default)] -pub struct ConsoleWinSize { - pub ws_row: u16, - pub ws_col: u16, - pub ws_xpixel: u16, - pub ws_ypixel: u16, -} - -/// ioctl implementation, -/// currently only support fd = 1 +/// ioctl implementation pub fn sys_ioctl(fd: c_int, request: usize, data: usize) -> c_int { - debug!("sys_ioctl <= fd: {}, request: {}", fd, request); + debug!("sys_ioctl <= fd: {fd}, request: {request}"); syscall_body!(sys_ioctl, { match request { FIONBIO => { - unsafe { - get_file_like(fd)?.set_nonblocking(*(data as *const i32) > 0)?; - } - Ok(0) - } - TIOCGWINSZ => { - let winsize = data as *mut ConsoleWinSize; - unsafe { - *winsize = ConsoleWinSize::default(); - } - Ok(0) - } - TCGETS => { - debug!("sys_ioctl: tty TCGETS"); - Ok(0) - } - TIOCSPGRP => { - warn!("stdout pretend to be tty"); + let f = get_file_like(fd)?; + let flags = if unsafe { *(data as *const i32) } > 0 { + f.flags() | OpenFlags::O_NONBLOCK + } else { + f.flags() & !OpenFlags::O_NONBLOCK + }; + f.set_flags(flags)?; Ok(0) } - TIOCGPGRP => { - warn!("stdout TIOCGPGRP, pretend to be have a tty process group."); - unsafe { - *(data as *mut u32) = sys_getpgid(0) as _; - } + FIOCLEX => Ok(0), + _ => { + get_file_like(fd)?.ioctl(request, data)?; Ok(0) } - FIOCLEX => Ok(0), - _ => Err(LinuxError::EINVAL), } }) } diff --git a/api/ruxos_posix_api/src/imp/mmap/api.rs b/api/ruxos_posix_api/src/imp/mmap/api.rs index 9a671d160..f26eee7c4 100644 --- a/api/ruxos_posix_api/src/imp/mmap/api.rs +++ b/api/ruxos_posix_api/src/imp/mmap/api.rs @@ -15,17 +15,21 @@ use core::{ ops::Bound, }; use memory_addr::PAGE_SIZE_4K; -use ruxhal::{mem::VirtAddr, paging::pte_update_page}; +use ruxhal::mem::VirtAddr; +use ruxmm::paging::pte_update_page; use super::utils::{ - find_free_region, get_mflags_from_usize, get_overlap, release_pages_mapped, shift_mapped_page, - snatch_fixed_region, Vma, MEM_MAP, VMA_END, VMA_MAP, + find_free_region, get_mflags_from_usize, get_overlap, release_pages_mapped, + release_pages_mapped_without_wb, shift_mapped_page, snatch_fixed_region, VMA_END, }; +use ruxtask::current; +use ruxtask::vma::Vma; #[cfg(feature = "fs")] use { super::utils::{release_pages_swaped, write_into}, alloc::sync::Arc, + ruxtask::vma::FileInfo, }; /// Creates a new mapping in the virtual address space of the calling process. @@ -40,12 +44,16 @@ pub fn sys_mmap( off: ctypes::off_t, ) -> *mut c_void { debug!( - "sys_mmap <= start: {:p}, len: 0x{:x}, prot:0x{:x?}, flags:0x{:x?}, fd: {}", - start, len, prot, flags, fd + "sys_mmap <= start: {start:p}, len: 0x{len:x}, prot:0x{prot:x?}, flags:0x{flags:x?}, fd: {fd}, off: 0x{off:x}" ); syscall_body!(sys_mmap, { // transform C-type into rust-type let start = start as usize; + let mapping_len = VirtAddr::from(len).align_up_4k().as_usize(); + if !VirtAddr::from(start).is_aligned(PAGE_SIZE_4K) || len == 0 { + error!("mmap failed because start:0x{start:x} is not aligned or len:0x{len:x} == 0"); + return Err(LinuxError::EINVAL); + } let prot = prot as u32; let flags = flags as u32; let fid = fd; @@ -67,36 +75,22 @@ pub fn sys_mmap( fid }; - // align len to PAGE_SIZE_4K depending on `MAP_ANONYMOUS` or not. - let len = if fid < 0 { - VirtAddr::from(len).align_up_4k().as_usize() - } else { - VirtAddr::from(len).as_usize() - }; - - // check if `start` is aligned to `PAGE_SIZE_4K`or len is large than 0. - if !VirtAddr::from(start).is_aligned(PAGE_SIZE_4K) || len == 0 { - error!( - "mmap failed because start:0x{:x} is not aligned or len:0x{:x} == 0", - start, len - ); - return Err(LinuxError::EINVAL); - } - let mut new = Vma::new(fid, offset, prot, flags); - let mut vma_map = VMA_MAP.lock(); + let binding_task = current(); + let mut vma_map = binding_task.mm.vma_map.lock(); let addr_condition = if start == 0 { None } else { Some(start) }; let try_addr = if flags & ctypes::MAP_FIXED != 0 { - snatch_fixed_region(&mut vma_map, start, len) + snatch_fixed_region(&mut vma_map, start, mapping_len) } else { - find_free_region(&vma_map, addr_condition, len) + find_free_region(&vma_map, addr_condition, mapping_len) }; match try_addr { Some(vaddr) => { new.start_addr = vaddr; - new.end_addr = vaddr + len; + new.end_addr = vaddr + mapping_len; + new.size = len; vma_map.insert(vaddr, new); Ok(vaddr as *mut c_void) } @@ -107,21 +101,19 @@ pub fn sys_mmap( /// Deletes the mappings for the specified address range pub fn sys_munmap(start: *mut c_void, len: ctypes::size_t) -> c_int { - debug!("sys_munmap <= start: {:p}, len: 0x{:x}", start, len); + debug!("sys_munmap <= start: {start:p}, len: 0x{len:x}"); syscall_body!(sys_munmap, { // transform C-type into rust-type let start = start as usize; - let end = VirtAddr::from(start + len).as_usize(); + let end = VirtAddr::from(start + len).align_up_4k().as_usize(); if !VirtAddr::from(start).is_aligned(PAGE_SIZE_4K) || len == 0 { - error!( - "sys_munmap start_address=0x{:x}, len 0x{:x?} not aligned", - start, len - ); + error!("sys_munmap start_address=0x{start:x}, len 0x{len:x?} not aligned"); return Err(LinuxError::EINVAL); } - let mut vma_map = VMA_MAP.lock(); + let binding = current(); + let mut vma_map = binding.mm.vma_map.lock(); // In order to ensure that munmap can exit directly if it fails, it must // ensure that munmap semantics are correct before taking action. @@ -131,7 +123,7 @@ pub fn sys_munmap(start: *mut c_void, len: ctypes::size_t) -> c_int { let mut node = vma_map.upper_bound_mut(Bound::Included(&start)); let mut counter = 0; // counter to check if all address in [start, start+len) is mapped. - while let Some(vma) = node.value_mut() { + while let Some((_, vma)) = node.peek_prev() { if vma.start_addr >= end { break; } @@ -153,7 +145,10 @@ pub fn sys_munmap(start: *mut c_void, len: ctypes::size_t) -> c_int { post_remove.push(vma.start_addr); } } - node.move_next(); + + if node.next().is_none() { + break; + } } // check if any address in [start, end) not mayed. @@ -179,7 +174,7 @@ pub fn sys_munmap(start: *mut c_void, len: ctypes::size_t) -> c_int { } // delete the mapped and swapped page. - release_pages_mapped(start, end, true); + release_pages_mapped(start, end); #[cfg(feature = "fs")] release_pages_swaped(start, end); @@ -191,15 +186,12 @@ pub fn sys_munmap(start: *mut c_void, len: ctypes::size_t) -> c_int { /// containing any part of the address range in the interval [addr, addr+len). /// addr must be aligned to a page boundary. pub fn sys_mprotect(start: *mut c_void, len: ctypes::size_t, prot: c_int) -> c_int { - debug!( - "sys_mprotect <= addr: {:p}, len: 0x{:x}, prot: {}", - start, len, prot - ); + debug!("sys_mprotect <= addr: {start:p}, len: 0x{len:x}, prot: {prot}"); syscall_body!(sys_mprotect, { // transform C-type into rust-type let start = start as usize; - let end = VirtAddr::from(start + len).as_usize(); + let end = VirtAddr::from(start + len).align_up_4k().as_usize(); if !VirtAddr::from(start).is_aligned(PAGE_SIZE_4K) || len == 0 { return Err(LinuxError::EINVAL); } @@ -211,10 +203,11 @@ pub fn sys_mprotect(start: *mut c_void, len: ctypes::size_t, prot: c_int) -> c_i let mut post_shrink: Vec<(usize, usize)> = Vec::new(); let mut post_align_changed: Vec<(usize, usize)> = Vec::new(); - let mut vma_map = VMA_MAP.lock(); + let binding_task = current(); + let mut vma_map = binding_task.mm.vma_map.lock(); let mut node = vma_map.upper_bound_mut(Bound::Included(&start)); let mut counter = 0; // counter to check if all address in [start, start+len) is mapped. - while let Some(vma) = node.value_mut() { + while let Some((_, vma)) = node.peek_prev() { if vma.start_addr >= end { break; } @@ -243,7 +236,9 @@ pub fn sys_mprotect(start: *mut c_void, len: ctypes::size_t, prot: c_int) -> c_i post_align_changed.push((vma.start_addr, overlapped_end)); } } - node.move_next(); + if node.next().is_none() { + break; + } } // check if any address in [start, end) not mayed. if counter != end - start { @@ -256,7 +251,7 @@ pub fn sys_mprotect(start: *mut c_void, len: ctypes::size_t, prot: c_int) -> c_i } // upate PTEs if mprotect is successful. - for (&vaddr, _) in MEM_MAP.lock().range(start..end) { + for (&vaddr, _) in current().mm.mem_map.lock().range(start..end) { if pte_update_page( VirtAddr::from(vaddr), None, @@ -265,8 +260,7 @@ pub fn sys_mprotect(start: *mut c_void, len: ctypes::size_t, prot: c_int) -> c_i .is_err() { error!( - "Updating page prot failed when mprotecting the page: vaddr=0x{:x?}, prot={:?}", - vaddr, prot + "Updating page prot failed when mprotecting the page: vaddr=0x{vaddr:x?}, prot={prot:?}" ); } } @@ -293,20 +287,17 @@ pub fn sys_mprotect(start: *mut c_void, len: ctypes::size_t, prot: c_int) -> c_i /// /// Note: support flags `MS_SYNC` only. pub fn sys_msync(start: *mut c_void, len: ctypes::size_t, flags: c_int) -> c_int { - debug!( - "sys_msync <= addr: {:p}, len: {}, flags: {}", - start, len, flags - ); + debug!("sys_msync <= addr: {start:p}, len: {len}, flags: {flags}"); syscall_body!(sys_msync, { #[cfg(feature = "fs")] { let start = start as usize; - let end = VirtAddr::from(start + len).as_usize(); + let end = VirtAddr::from(start + len).align_up_4k().as_usize(); if !VirtAddr::from(start).is_aligned(PAGE_SIZE_4K) || len == 0 { return Err(LinuxError::EINVAL); } - for (&vaddr, page_info) in MEM_MAP.lock().range(start..end) { - if let Some((file, offset, size)) = page_info { + for (&vaddr, page_info) in current().mm.mem_map.lock().range(start..end) { + if let Some(FileInfo { file, offset, size }) = &page_info.mapping_file { let src = vaddr as *mut u8; write_into(file, src, *offset as u64, *size); } @@ -325,8 +316,7 @@ pub fn sys_mremap( new_addr: *mut c_void, ) -> *mut c_void { debug!( - "sys_mremap <= old_addr: {:p}, old_size: {}, new_size: {}, flags: {}, new_addr: {:p}", - old_addr, old_size, new_size, flags, new_addr + "sys_mremap <= old_addr: {old_addr:p}, old_size: {old_size}, new_size: {new_size}, flags: {flags}, new_addr: {new_addr:p}" ); syscall_body!(sys_mremap, { let old_vaddr = VirtAddr::from(old_addr as usize); @@ -351,10 +341,11 @@ pub fn sys_mremap( let mut consistent_vma: Option = None; // structure to verify the consistent in the range of [old_start, old_end) let mut post_remove: Vec = Vec::new(); // vma should be removed if success. - let mut vma_map = VMA_MAP.lock(); + let binding_task = current(); + let mut vma_map = binding_task.mm.vma_map.lock(); // collect and check vma alongside the range of [old_start, old_end). let mut node = vma_map.upper_bound_mut(Bound::Included(&old_start)); - while let Some(vma) = node.value_mut() { + while let Some((_, vma)) = node.peek_prev() { if vma.start_addr > old_end { break; } @@ -388,7 +379,9 @@ pub fn sys_mremap( } post_remove.push(vma.start_addr); - node.move_next(); + if node.next().is_none() { + break; + } } // check if consistent_vma full match the remapping memory. @@ -449,14 +442,15 @@ pub fn sys_mremap( Vma::clone_from(&old_vma, old_end, old_vma.end_addr), ); } - old_vma.end_addr = new_end; + old_vma.end_addr = VirtAddr::from(new_end).align_up_4k().as_usize(); + old_vma.size = new_size; + vma_map.insert(old_vma.start_addr, old_vma); - // delete the mapped and swapped page outside of new vma. - release_pages_mapped(new_end, old_end, false); + // for the shrink region, it should be removed without write-back + release_pages_mapped_without_wb(new_end, old_end); #[cfg(feature = "fs")] release_pages_swaped(new_end, old_end); - // vma_map.insert(old_vma.start_addr, old_vma); return Ok(ret as *mut c_void); } // expanding the original address does not require changing the mapped page. @@ -466,10 +460,10 @@ pub fn sys_mremap( return Err(LinuxError::ENOMEM); } // find the right region to expand them in orignal addr. - let upper = vma_map + let (upper, _) = vma_map .lower_bound(Bound::Included(&old_end)) - .key() - .unwrap_or(&VMA_END); + .peek_next() + .unwrap_or((&VMA_END, &Vma::new(-1, 0, 0, 0))); if upper - old_end >= new_size - old_size { let ret = old_vma.start_addr; let new_end = old_start + new_size; @@ -542,9 +536,6 @@ pub fn sys_mremap( /// /// TODO: implement this to improve performance. pub fn sys_madvise(addr: *mut c_void, len: ctypes::size_t, advice: c_int) -> c_int { - debug!( - "sys_madvise <= addr: {:p}, len: {}, advice: {}", - addr, len, advice - ); + debug!("sys_madvise <= addr: {addr:p}, len: {len}, advice: {advice}"); syscall_body!(sys_madvise, Ok(0)) } diff --git a/api/ruxos_posix_api/src/imp/mmap/mod.rs b/api/ruxos_posix_api/src/imp/mmap/mod.rs index 0ab39c2ed..aed170efc 100644 --- a/api/ruxos_posix_api/src/imp/mmap/mod.rs +++ b/api/ruxos_posix_api/src/imp/mmap/mod.rs @@ -9,7 +9,7 @@ cfg_if::cfg_if! { // for X86_64 with SMP, it must flush TLB via IPI - if #[cfg( all(feature = "paging", any(target_arch = "aarch64", any( all(target_arch = "x86_64", feature = "irq", feature = "smp"), all(target_arch = "x86_64", not(feature = "smp")) ) ) ))] { + if #[cfg( all(feature = "paging", any(target_arch = "aarch64",target_arch="riscv64",any( all(target_arch = "x86_64", feature = "irq", feature = "smp"), all(target_arch = "x86_64", not(feature = "smp")) ) ) ))] { #[macro_use] mod utils; mod api; diff --git a/api/ruxos_posix_api/src/imp/mmap/trap.rs b/api/ruxos_posix_api/src/imp/mmap/trap.rs index 38cdee849..f62127533 100644 --- a/api/ruxos_posix_api/src/imp/mmap/trap.rs +++ b/api/ruxos_posix_api/src/imp/mmap/trap.rs @@ -7,33 +7,54 @@ * See the Mulan PSL v2 for more details. */ +#[cfg(not(feature = "fs"))] +use ruxmm::paging::alloc_page_preload; #[cfg(feature = "fs")] -use crate::{ - ctypes, - imp::mmap::utils::{preload_page_with_swap, read_from, BITMAP_FREE, SWAPED_MAP, SWAP_FILE}, +use { + crate::{ + ctypes, + imp::mmap::utils::{preload_page_with_swap, read_from}, + }, + ruxtask::vma::{FileInfo, BITMAP_FREE, SWAPED_MAP, SWAP_FILE}, }; -#[cfg(not(feature = "fs"))] -use ruxhal::paging::alloc_page_preload; -use crate::imp::mmap::utils::{get_mflags_from_usize, MEM_MAP, VMA_MAP}; -use core::{cmp::min, ops::Bound}; +use crate::imp::mmap::utils::get_mflags_from_usize; +use alloc::sync::Arc; +use core::{ + cmp::min, + ops::{Bound, DerefMut}, + sync::atomic::{fence, Ordering}, +}; use memory_addr::PAGE_SIZE_4K; use page_table::MappingFlags; use ruxhal::{ - mem::VirtAddr, - paging::{do_pte_map, pte_query}, + mem::{direct_virt_to_phys, VirtAddr}, trap::PageFaultCause, }; +use ruxtask::{current, vma::PageInfo}; + +use ruxmm::paging::{do_pte_map, pte_query, pte_update_page}; struct TrapHandlerImpl; #[crate_interface::impl_interface] impl ruxhal::trap::TrapHandler for TrapHandlerImpl { fn handle_page_fault(vaddr: usize, cause: PageFaultCause) -> bool { - let vma_map = VMA_MAP.lock(); - if let Some(vma) = vma_map.upper_bound(Bound::Included(&vaddr)).value() { + let binding_task = current(); + let mut binding_mem_map = binding_task.mm.vma_map.lock(); + let vma_map = binding_mem_map.deref_mut(); + if let Some((_, vma)) = vma_map.upper_bound(Bound::Included(&vaddr)).peek_prev() { + trace!( + "vaddr=({:#x?}, {:#x?}) {:#x?} {:#x?}", + vma.start_addr, + vma.end_addr, + vaddr, + cause + ); + let vma_end_aligned = vma.end_addr; // Check if page existing in the vma, go to panic if not. - if vma.end_addr <= vaddr { + if vma_end_aligned <= vaddr { + error!("Page Fault not match: vaddr=0x{vaddr:x?}, cause={cause:?}"); return false; } @@ -42,38 +63,54 @@ impl ruxhal::trap::TrapHandler for TrapHandlerImpl { let map_flag = get_mflags_from_usize(vma.prot); trace!( - "Page Fault Happening, vaddr:0x{:x?}, casue:{:?}, map_flags:0x{:x?}", - vaddr, - cause, - map_flag + "Page Fault Happening, vaddr:0x{vaddr:x?}, casue:{cause:?}, map_flags:0x{map_flag:x?}" ); // Check if the access meet the prot - match cause { - PageFaultCause::INSTRUCTION if !map_flag.contains(MappingFlags::EXECUTE) => { - return false - } - PageFaultCause::READ if !map_flag.contains(MappingFlags::READ) => return false, - PageFaultCause::WRITE if !map_flag.contains(MappingFlags::WRITE) => return false, - _ => {} - } - - // In a multi-threaded situation, it is possible that multiple threads - // simultaneously trigger a page miss interrupt on the same page, - // resulting in the page being actually mapped and causing an `AlreadyMap` - // error - if pte_query(VirtAddr::from(vaddr)).is_ok() { - return true; + if !map_flag.contains(cause.into()) { + error!("Page Fault: Access violation, vaddr:0x{vaddr:x?}, cause:{cause:?}"); + return false; } - let mut memory_map = MEM_MAP.lock(); + let binding_task = current(); + let mut binding_mem_map = binding_task.mm.mem_map.lock(); + let memory_map = binding_mem_map.deref_mut(); used_fs! { let mut swaped_map = SWAPED_MAP.lock(); let mut off_pool = BITMAP_FREE.lock(); } + // In a multi-threaded situation, it is possible that multiple threads + // simultaneously trigger a page miss interrupt on the same page, + // resulting in the page being actually mapped and causing an `AlreadyMap` + // error + let query_result = pte_query(VirtAddr::from(vaddr)); + let mem_item = memory_map.get(&vaddr); + let is_cow = if let Ok((_, mapping_flags, _)) = query_result { + assert!(mem_item.is_some()); + // Check if: + // 1. the page is mapped by another thread. + if mapping_flags.contains(cause.into()) { + return true; + } + // 2. the page is in Copy-on-Write mode so that it's set in read-only mode; + assert!(mapping_flags.contains(MappingFlags::READ)); + assert!(!mapping_flags.contains(MappingFlags::WRITE)); + let mem_arc = mem_item.unwrap(); + if Arc::strong_count(mem_arc).eq(&1) { + // the last owner of the page, we can safely map it. + pte_update_page(vaddr.into(), None, Some(map_flag)) + .expect("failed to update page table entry"); + return true; + } + true + } else { + // no page table entry found, it means the page is not mapped yet. + false + }; + // Due to the existence of only one page table in ruxos, in - // order to prevent data competition in multi-threaded environ- + // order to prevent data race in multi-threaded environ- // -ments caused by adding the current virtual address to the // page table, it is necessary to first map the physical address // that needs to be mapped to another virtual address, and then @@ -85,66 +122,107 @@ impl ruxhal::trap::TrapHandler for TrapHandlerImpl { #[cfg(not(feature = "fs"))] let fake_vaddr = alloc_page_preload().expect("alloc memory for new page failed"); #[cfg(feature = "fs")] - let fake_vaddr = - preload_page_with_swap(&mut memory_map, &mut swaped_map, &mut off_pool); + let fake_vaddr = preload_page_with_swap(memory_map, &mut swaped_map, &mut off_pool); // Fill target data to assigned physical addresses, from file or zero according to mapping type let dst: *mut u8 = fake_vaddr.as_mut_ptr(); - #[cfg(feature = "fs")] - { - if let Some(off) = swaped_map.remove(&vaddr) { - off_pool.push(off); - read_from(&SWAP_FILE, dst, off as u64, size); - } else if let Some(file) = &vma.file { - let off = (vma.offset + (vaddr - vma.start_addr)) as u64; - read_from(file, dst, off, size); - } else { - // Set page to 0 for anonymous mapping - // - // Safe because the page memory is allocated here - // and the page fault exception has not exited. - unsafe { - dst.write_bytes(0, size); + + if !is_cow { + // get here if the page is belong to current process + #[cfg(feature = "fs")] + { + if let Some(swap_info) = swaped_map.remove(&vaddr) { + read_from(&SWAP_FILE, dst, swap_info.offset as u64, size); + } else if let Some(file) = &vma.file { + let off = (vma.offset + (vaddr - vma.start_addr)) as u64; + read_from(file, dst, off, size); + } else { + // Set page to 0 for anonymous mapping + // + // Safe because the page memory is allocated here + // and the page fault exception has not exited. + unsafe { + dst.write_bytes(0, size); + } } } - } - // Set page to 0 for anonymous mapping - // - // Safe because the page memory is allocated here - // and the page fault exception has not exited. - #[cfg(not(feature = "fs"))] - unsafe { - dst.write_bytes(0, size); - } + // Set page to 0 for anonymous mapping + // + // Safe because the page memory is allocated here + // and the page fault exception has not exited. + #[cfg(not(feature = "fs"))] + unsafe { + dst.write_bytes(0, size); + } - // Insert the record into `MEM_MAP` with write-back information(`None` if no need to write-back). - #[cfg(feature = "fs")] - if (vma.prot & ctypes::PROT_WRITE != 0) - && (vma.flags & ctypes::MAP_PRIVATE == 0) - && (vma.file.is_some()) - { - let map_length = min(PAGE_SIZE_4K, vma.end_addr - vaddr); - let offset = vma.offset + (vaddr - vma.start_addr); + // Insert the record into `MEM_MAP` with write-back information(`None` if no need to write-back). + #[cfg(feature = "fs")] + if (vma.prot & ctypes::PROT_WRITE != 0) + && (vma.flags & ctypes::MAP_PRIVATE == 0) + && (vma.file.is_some()) + { + let map_length = min(PAGE_SIZE_4K, vma.start_addr + vma.size - vaddr); + let offset = vma.offset + (vaddr - vma.start_addr); + let file_info = FileInfo { + file: vma.file.as_ref().unwrap().clone(), + offset, + size: map_length, + }; + let page_info = PageInfo { + paddr: direct_virt_to_phys(fake_vaddr), + mapping_file: Some(file_info), + }; + memory_map.insert(vaddr, Arc::new(page_info)); + } else { + memory_map.insert( + vaddr, + Arc::new(PageInfo { + paddr: direct_virt_to_phys(fake_vaddr), + mapping_file: None, + }), + ); + } + #[cfg(not(feature = "fs"))] memory_map.insert( vaddr, - Some((vma.file.as_ref().unwrap().clone(), offset, map_length)), + Arc::new(PageInfo { + paddr: direct_virt_to_phys(fake_vaddr), + }), ); - } else { - memory_map.insert(vaddr, None); - } - #[cfg(not(feature = "fs"))] - memory_map.insert(vaddr, None); - // Do actual mmapping for target vaddr - // - // Note: other threads can access this page of memory after this code. - match do_pte_map(VirtAddr::from(vaddr), fake_vaddr, map_flag) { - Ok(()) => true, - Err(_) => false, + // Do actual mmapping for target vaddr + // + // Note: other threads can access this page of memory after this code. + match do_pte_map(VirtAddr::from(vaddr), fake_vaddr, map_flag) { + Ok(()) => true, + Err(_) => false, + } + } else { + // get here if the page is belong to current process and is in Copy-on-Write mode. + unsafe { + dst.copy_from(vaddr as *mut u8, size); + } + let paddr = direct_virt_to_phys(fake_vaddr); + let mapping_file = memory_map.get(&vaddr).unwrap().mapping_file.clone(); + memory_map.remove(&vaddr); + memory_map.insert( + vaddr, + Arc::new(PageInfo { + paddr, + #[cfg(feature = "fs")] + mapping_file, + }), + ); + fence(Ordering::SeqCst); + // Update the page table entry to map the physical address of the fake virtual address. + match pte_update_page(vaddr.into(), Some(paddr), Some(map_flag)) { + Ok(()) => true, + Err(_) => false, + } } } else { - warn!("vaddr=0x{:x?},cause=0x{:x?}", vaddr, cause); + warn!("vaddr={vaddr:#x?},cause={cause:#x?}"); false } } diff --git a/api/ruxos_posix_api/src/imp/mmap/utils.rs b/api/ruxos_posix_api/src/imp/mmap/utils.rs index ff4729a67..4fbac13b1 100644 --- a/api/ruxos_posix_api/src/imp/mmap/utils.rs +++ b/api/ruxos_posix_api/src/imp/mmap/utils.rs @@ -9,124 +9,41 @@ use crate::ctypes; -#[cfg(feature = "fs")] -use {crate::imp::fs::File, alloc::sync::Arc, page_table::PagingError, ruxfs::fops::OpenOptions}; - use alloc::{collections::BTreeMap, vec::Vec}; -use axsync::Mutex; use core::{ cmp::{max, min}, - ops::Bound, + ops::{Bound, DerefMut}, }; use memory_addr::PAGE_SIZE_4K; use page_table::MappingFlags; -use ruxhal::{ - mem::VirtAddr, - paging::{alloc_page_preload, do_pte_map, pte_query, pte_swap_preload, pte_unmap_page}, -}; +use ruxfs::File; +use ruxhal::mem::VirtAddr; +use ruxmm::paging::{alloc_page_preload, do_pte_map, pte_query, pte_swap_preload, pte_unmap_page}; +#[cfg(feature = "fs")] +use ruxtask::vma::{FileInfo, PageInfo, SwapInfo, BITMAP_FREE, SWAPED_MAP, SWAP_FILE}; +use ruxtask::{current, vma::Vma}; +#[cfg(feature = "fs")] +use {alloc::sync::Arc, page_table::PagingError}; + +pub(crate) const VMA_START: usize = ruxconfig::MMAP_START_VADDR; +pub(crate) const VMA_END: usize = ruxconfig::MMAP_END_VADDR; // use `used_fs` instead of `#[cfg(feature = "fs")]{}` to cancel the scope of code. #[cfg(feature = "fs")] macro_rules! used_fs { - ($($code:tt)*) => {$($code)*}; - } + ($($code:tt)*) => {$($code)*}; + } #[cfg(not(feature = "fs"))] macro_rules! used_fs { ($($code:tt)*) => {}; } -pub(crate) const VMA_START: usize = ruxconfig::MMAP_START_VADDR; -pub(crate) const VMA_END: usize = ruxconfig::MMAP_END_VADDR; - -// TODO: move defination of `SWAP_MAX` and `SWAP_PATH` from const numbers to `ruxconfig`. -used_fs! { - pub(crate) const SWAP_MAX: usize = 1024 * 1024 * 1024; - pub(crate) const SWAP_PATH: &str = "swap.raw\0"; - pub(crate) static SWAPED_MAP: Mutex> = Mutex::new(BTreeMap::new()); // Vaddr => (page_size, offset_at_swaped) - lazy_static::lazy_static! { - pub(crate) static ref SWAP_FILE: Arc = open_swap_file(SWAP_PATH); - pub(crate) static ref BITMAP_FREE: Mutex> = Mutex::new((0..SWAP_MAX).step_by(PAGE_SIZE_4K).collect()); - } -} - -pub(crate) static VMA_MAP: Mutex> = Mutex::new(BTreeMap::new()); // start_addr -pub(crate) static MEM_MAP: Mutex> = Mutex::new(BTreeMap::new()); // Vaddr => (fid, offset, page_size) - -#[cfg(feature = "fs")] -type PageInfo = Option<(Arc, Offset, Len)>; // (fid, offset, page_size) -#[cfg(not(feature = "fs"))] -type PageInfo = Option; // (fid, offset, page_size) -#[cfg(feature = "fs")] -type Offset = usize; -type Len = usize; - -/// Data structure for mapping [start_addr, end_addr) with meta data. -pub(crate) struct Vma { - pub start_addr: usize, - pub end_addr: usize, - #[cfg(feature = "fs")] - pub file: Option>, - pub offset: usize, - pub prot: u32, - pub flags: u32, -} - -/// Impl for Vma. -impl Vma { - pub(crate) fn new(_fid: i32, offset: usize, prot: u32, flags: u32) -> Self { - #[cfg(feature = "fs")] - let file = if _fid < 0 { - None - } else { - Some(File::from_fd(_fid).expect("should be effective fid")) - }; - Vma { - start_addr: 0, - end_addr: 0, - #[cfg(feature = "fs")] - file, - offset, - flags, - prot, - } - } - - pub(crate) fn clone_from(vma: &Vma, start_addr: usize, end_addr: usize) -> Self { - Vma { - start_addr, - end_addr, - #[cfg(feature = "fs")] - file: vma.file.clone(), - offset: vma.offset, - prot: vma.prot, - flags: vma.prot, - } - } -} - -/// open target file -#[cfg(feature = "fs")] -fn open_swap_file(filename: &str) -> Arc { - let mut opt = OpenOptions::new(); - opt.read(true); - opt.write(true); - opt.append(true); - opt.create(true); - - let file = ruxfs::fops::File::open(filename, &opt).expect("create swap file failed"); - Arc::new(File::new(file)) -} - /// read from target file #[cfg(feature = "fs")] pub(crate) fn read_from(file: &Arc, buf: *mut u8, offset: u64, len: usize) { let src = unsafe { core::slice::from_raw_parts_mut(buf, len) }; - let actual_len = file - .inner - .lock() - .read_at(offset, src) - .expect("read_from failed"); + let actual_len = file.read_at(offset, src).expect("read_from failed"); if len != actual_len { warn!("read_from len=0x{len:x} but actual_len=0x{actual_len:x}"); } @@ -136,11 +53,7 @@ pub(crate) fn read_from(file: &Arc, buf: *mut u8, offset: u64, len: usize) #[cfg(feature = "fs")] pub(crate) fn write_into(file: &Arc, buf: *mut u8, offset: u64, len: usize) { let src = unsafe { core::slice::from_raw_parts_mut(buf, len) }; - let actual_len = file - .inner - .lock() - .write_at(offset, src) - .expect("write_into failed"); + let actual_len = file.write_at(offset, src).expect("write_into failed"); if len != actual_len { warn!("write_into len=0x{len:x} but actual_len=0x{actual_len:x}"); } @@ -188,27 +101,26 @@ pub(crate) fn find_free_region( ) -> Option { // Search free region in select region if start!=NULL, return error if `MAP_FIXED` flags exist. if let Some(start) = addr { - let end_addr = if let Some(lower_vma) = vma_map.upper_bound(Bound::Included(&start)).value() + let end_addr = if let Some((_, lower_vma)) = + vma_map.upper_bound(Bound::Included(&start)).peek_prev() { lower_vma.end_addr } else { VMA_START }; - let upper = vma_map + let (upper, _) = vma_map .lower_bound(Bound::Included(&start)) - .key() - .unwrap_or(&VMA_END); + .peek_next() + .unwrap_or((&VMA_END, &Vma::new(-1, 0, 0, 0))); if upper - start >= len && end_addr <= start { return Some(start); } } // Search free region on the top of VMA_LISTS first. - const ALIGN_PAGE_MASK: usize = !(PAGE_SIZE_4K - 1); if let Some((_, last_vma)) = vma_map.last_key_value() { - let end_boundry = (last_vma.end_addr + PAGE_SIZE_4K) & ALIGN_PAGE_MASK; - if (VMA_END - end_boundry) & ALIGN_PAGE_MASK >= len { - return Some(end_boundry); + if VMA_END - last_vma.end_addr >= len { + return Some(last_vma.end_addr); } } else if VMA_END >= VMA_START + len { return Some(VMA_START); @@ -246,7 +158,7 @@ pub(crate) fn snatch_fixed_region( let mut post_remove: Vec = Vec::new(); // vma should be removed. let mut node = vma_map.upper_bound_mut(Bound::Included(&start)); - while let Some(vma) = node.value_mut() { + while let Some((_, vma)) = node.peek_prev() { if vma.start_addr >= end { break; } @@ -264,7 +176,9 @@ pub(crate) fn snatch_fixed_region( post_remove.push(vma.start_addr); } } - node.move_next(); + if node.next().is_none() { + break; + } } // do action after success. @@ -276,25 +190,41 @@ pub(crate) fn snatch_fixed_region( } // delete the mapped and swapped page. - release_pages_mapped(start, end, true); + release_pages_mapped(start, end); #[cfg(feature = "fs")] release_pages_swaped(start, end); Some(start) } +/// release the range of [start, end) in mem_map without writeback. +/// take care of AA-deadlock, this function should not be used after `MEM_MAP` is used. +pub(crate) fn release_pages_mapped_without_wb(start: usize, end: usize) { + let binding = current(); + let mut memory_map = binding.mm.mem_map.lock(); + let mut removing_vaddr = Vec::new(); + for (&vaddr, _) in memory_map.range(start..end) { + if pte_unmap_page(VirtAddr::from(vaddr)).is_err() { + panic!("Release page failed when munmapping!"); + } + removing_vaddr.push(vaddr); + } + for vaddr in removing_vaddr { + memory_map.remove(&vaddr); + } +} + /// release the range of [start, end) in mem_map /// take care of AA-deadlock, this function should not be used after `MEM_MAP` is used. -pub(crate) fn release_pages_mapped(start: usize, end: usize, writeback: bool) { - let mut memory_map = MEM_MAP.lock(); +pub(crate) fn release_pages_mapped(start: usize, end: usize) { + let binding = current(); + let mut memory_map = binding.mm.mem_map.lock(); let mut removing_vaddr = Vec::new(); - for (&vaddr, _page_info) in memory_map.range(start..end) { + for (&vaddr, page_info) in memory_map.range(start..end) { #[cfg(feature = "fs")] - if writeback { - if let Some((file, offset, size)) = _page_info { - let src = vaddr as *mut u8; - write_into(file, src, *offset as u64, *size); - } + if let Some(FileInfo { file, offset, size }) = &page_info.mapping_file { + let src = vaddr as *mut u8; + write_into(file, src, *offset as u64, *size); } if pte_unmap_page(VirtAddr::from(vaddr)).is_err() { panic!("Release page failed when munmapping!"); @@ -314,9 +244,9 @@ pub(crate) fn release_pages_swaped(start: usize, end: usize) { let mut off_pool = BITMAP_FREE.lock(); let mut removing_vaddr = Vec::new(); - for (&vaddr, &off) in swap_map.range(start..end) { + for (&vaddr, swap_info) in swap_map.range(start..end) { removing_vaddr.push(vaddr); - off_pool.push(off); + off_pool.push(swap_info.offset); } for vaddr in removing_vaddr { swap_map.remove(&vaddr); @@ -326,7 +256,9 @@ pub(crate) fn release_pages_swaped(start: usize, end: usize) { /// shift mapped the page in both MEM_MAP and SWAPED_MAP. /// No page fault here should be guaranteed pub(crate) fn shift_mapped_page(start: usize, end: usize, vma_offset: usize, copy: bool) { - let mut memory_map = MEM_MAP.lock(); + let binding_task = current(); + let mut binding_mem_map = binding_task.mm.mem_map.lock(); + let memory_map = binding_mem_map.deref_mut(); used_fs! { let mut swaped_map = SWAPED_MAP.lock(); let mut off_pool = BITMAP_FREE.lock(); @@ -350,8 +282,7 @@ pub(crate) fn shift_mapped_page(start: usize, end: usize, vma_offset: usize, cop #[cfg(not(feature = "fs"))] let fake_vaddr = alloc_page_preload().expect("alloc memory for new page failed"); #[cfg(feature = "fs")] - let fake_vaddr = - preload_page_with_swap(&mut memory_map, &mut swaped_map, &mut off_pool); + let fake_vaddr = preload_page_with_swap(memory_map, &mut swaped_map, &mut off_pool); let dst = unsafe { core::slice::from_raw_parts_mut(fake_vaddr.as_usize() as *mut u8, PAGE_SIZE_4K) @@ -361,12 +292,12 @@ pub(crate) fn shift_mapped_page(start: usize, end: usize, vma_offset: usize, cop let src = unsafe { core::slice::from_raw_parts_mut(start as *mut u8, PAGE_SIZE_4K) }; dst.clone_from_slice(src); - } else if page_info.is_none() + } else if page_info.mapping_file.is_none() /* has been swapped from memory */ { used_fs! { - let offset = swaped_map.get(&start).unwrap(); - read_from(&SWAP_FILE, start as *mut u8, *offset as u64, PAGE_SIZE_4K); + let swap_info = swaped_map.get(&start).unwrap(); + read_from(&SWAP_FILE, start as *mut u8, swap_info.offset as u64, PAGE_SIZE_4K); } } (fake_vaddr, flags) @@ -377,10 +308,10 @@ pub(crate) fn shift_mapped_page(start: usize, end: usize, vma_offset: usize, cop used_fs! { let mut opt_buffer = Vec::new(); - for (&start, &off_in_swap) in swaped_map.range(start..end) { - opt_buffer.push((start, off_in_swap)); + for (&start, off_in_swap) in swaped_map.range(start..end) { + opt_buffer.push((start, off_in_swap.clone())); } - for (start, off_in_swap) in opt_buffer { + for (start, swap_info) in opt_buffer { // opt for the swapped file, should copy swaped page for the new page. if !copy { swaped_map.remove(&start); @@ -389,10 +320,10 @@ pub(crate) fn shift_mapped_page(start: usize, end: usize, vma_offset: usize, cop .pop() .expect("There are no free space in swap-file!"); let mut rw_buffer: [u8; PAGE_SIZE_4K] = [0_u8; PAGE_SIZE_4K]; - read_from(&SWAP_FILE, rw_buffer.as_mut_ptr(), off_in_swap as u64, PAGE_SIZE_4K); + read_from(&SWAP_FILE, rw_buffer.as_mut_ptr(), swap_info.offset as u64, PAGE_SIZE_4K); write_into(&SWAP_FILE, rw_buffer.as_mut_ptr(), off_ptr as u64, PAGE_SIZE_4K); } - swaped_map.insert(start + vma_offset, off_in_swap); + swaped_map.insert(start + vma_offset, swap_info.clone()); } } } @@ -402,8 +333,8 @@ pub(crate) fn shift_mapped_page(start: usize, end: usize, vma_offset: usize, cop /// of a virtual address that is also mapped to the allocated physical address. #[cfg(feature = "fs")] pub(crate) fn preload_page_with_swap( - memory_map: &mut BTreeMap, - swaped_map: &mut BTreeMap, + memory_map: &mut BTreeMap>, + swaped_map: &mut BTreeMap>, off_pool: &mut Vec, ) -> VirtAddr { match alloc_page_preload() { @@ -411,33 +342,35 @@ pub(crate) fn preload_page_with_swap( // Try to swap the mapped memory into Disk and use this segment of physical memory #[cfg(feature = "fs")] Err(PagingError::NoMemory) => match memory_map.pop_first() { - // For file mapping, the mapped content will be written directly to the original file. - Some((vaddr_swapped, Some((file, offset, size)))) => { - let offset = offset.try_into().unwrap(); - write_into(&file, vaddr_swapped as *mut u8, offset, size); - pte_swap_preload(VirtAddr::from(vaddr_swapped)).unwrap() - } - // For anonymous mapping, you need to save the mapped memory to the prepared swap file, - // and record the memory address and its offset in the swap file. - Some((vaddr_swapped, None)) => { - let offset_get = off_pool.pop(); - let offset = offset_get.unwrap(); - swaped_map.insert(vaddr_swapped, offset); - - write_into( - &SWAP_FILE, - vaddr_swapped as *mut u8, - offset as u64, - PAGE_SIZE_4K, - ); - pte_swap_preload(VirtAddr::from(vaddr_swapped)).unwrap() + // Some((vaddr_swapped, PageInfo{paddr:_, mapping_file:Some(FileInfo{file, offset, size})})) => { + Some((vaddr_swapped, page_info)) => { + match &page_info.mapping_file { + // For file mapping, the mapped content will be written directly to the original file. + Some(FileInfo { file, offset, size }) => { + let offset = *offset as u64; + write_into(file, vaddr_swapped as *mut u8, offset, *size); + pte_swap_preload(VirtAddr::from(vaddr_swapped)).unwrap() + } + // For anonymous mapping, you need to save the mapped memory to the prepared swap file, + // and record the memory address and its offset in the swap file. + None => { + let offset_get = off_pool.pop(); + let offset = offset_get.unwrap(); + swaped_map.insert(vaddr_swapped, Arc::new(offset.into())); + + write_into( + &SWAP_FILE, + vaddr_swapped as *mut u8, + offset as u64, + PAGE_SIZE_4K, + ); + pte_swap_preload(VirtAddr::from(vaddr_swapped)).unwrap() + } + } } _ => panic!("No memory for mmap, check if huge memory leaky exists"), }, - Err(ecode) => panic!( - "Unexpected error 0x{:x?} happening when page fault occurs!", - ecode - ), + Err(ecode) => panic!("Unexpected error 0x{ecode:x?} happening when page fault occurs!"), } } diff --git a/api/ruxos_posix_api/src/imp/mod.rs b/api/ruxos_posix_api/src/imp/mod.rs index 7b7880d35..b9bed9f8a 100644 --- a/api/ruxos_posix_api/src/imp/mod.rs +++ b/api/ruxos_posix_api/src/imp/mod.rs @@ -14,7 +14,6 @@ pub mod getrandom; pub mod io; pub mod prctl; pub mod resources; -pub mod rt_sig; pub mod stat; pub mod sys; pub mod task; @@ -39,11 +38,13 @@ pub mod pipe; #[cfg(feature = "multitask")] pub mod pthread; #[cfg(feature = "signal")] +pub mod rt_sig; +#[cfg(feature = "signal")] pub mod signal; /// Invalid syscall pub fn sys_invalid(id: core::ffi::c_int) -> core::ffi::c_int { - debug!("sys_invalid <= id: {}", id); + debug!("sys_invalid <= id: {id}"); syscall_body!( sys_invalid, Err::(axerrno::LinuxError::ENOSYS) diff --git a/api/ruxos_posix_api/src/imp/net.rs b/api/ruxos_posix_api/src/imp/net.rs index ef8465aba..617fc1eea 100644 --- a/api/ruxos_posix_api/src/imp/net.rs +++ b/api/ruxos_posix_api/src/imp/net.rs @@ -8,179 +8,144 @@ */ use alloc::{sync::Arc, vec, vec::Vec}; +use axsync::Mutex; use core::ffi::{c_char, c_int, c_void}; use core::mem::size_of; -use core::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; +use core::net::{IpAddr, Ipv4Addr, SocketAddrV4}; +use iovec::{read_iovecs_ptr, IoVecsInput, IoVecsOutput}; +use ruxnet::address::{SocketAddress, UnixSocketAddr}; +use ruxnet::message::{ControlMessageData, MessageFlags}; +use ruxnet::socket::{Socket, SocketDomain, SocketType}; +use ruxnet::unix::UnixSocket; +use ruxtask::fs::{add_file_like, get_file_like}; use axerrno::{LinuxError, LinuxResult}; -use axio::PollState; -use axsync::Mutex; -use ruxfdtable::{FileLike, RuxStat}; -use ruxnet::{TcpSocket, UdpSocket}; +use ruxfdtable::OpenFlags; +use ruxnet::{ShutdownFlags, TcpSocket, UdpSocket}; -use crate::ctypes; +use crate::ctypes::{self}; +use crate::imp::fs::parse_path; use crate::utils::char_ptr_to_str; -pub enum Socket { - Udp(Mutex), - Tcp(Mutex), -} - -impl Socket { - fn add_to_fd_table(self) -> LinuxResult { - super::fd_ops::add_file_like(Arc::new(self)) - } - - fn from_fd(fd: c_int) -> LinuxResult> { - let f = super::fd_ops::get_file_like(fd)?; - f.into_any() - .downcast::() - .map_err(|_| LinuxError::EINVAL) - } - - fn send(&self, buf: &[u8]) -> LinuxResult { - match self { - Socket::Udp(udpsocket) => Ok(udpsocket.lock().send(buf)?), - Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().send(buf)?), - } - } - - fn recv(&self, buf: &mut [u8], flags: i32) -> LinuxResult { - match self { - Socket::Udp(udpsocket) => Ok(udpsocket.lock().recv_from(buf).map(|e| e.0)?), - Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().recv(buf, flags)?), - } - } - - pub fn poll(&self) -> LinuxResult { - match self { - Socket::Udp(udpsocket) => Ok(udpsocket.lock().poll()?), - Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().poll()?), - } - } - - fn local_addr(&self) -> LinuxResult { - match self { - Socket::Udp(udpsocket) => Ok(udpsocket.lock().local_addr()?), - Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().local_addr()?), - } - } - - fn peer_addr(&self) -> LinuxResult { - match self { - Socket::Udp(udpsocket) => Ok(udpsocket.lock().peer_addr()?), - Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().peer_addr()?), - } - } +const SA_FAMILY_SIZE: usize = size_of::(); - fn bind(&self, addr: SocketAddr) -> LinuxResult { - match self { - Socket::Udp(udpsocket) => Ok(udpsocket.lock().bind(addr)?), - Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().bind(addr)?), - } - } - - fn connect(&self, addr: SocketAddr) -> LinuxResult { - match self { - Socket::Udp(udpsocket) => Ok(udpsocket.lock().connect(addr)?), - Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().connect(addr)?), - } - } - - fn sendto(&self, buf: &[u8], addr: SocketAddr) -> LinuxResult { - match self { - // diff: must bind before sendto - Socket::Udp(udpsocket) => Ok(udpsocket.lock().send_to(buf, addr)?), - Socket::Tcp(_) => Err(LinuxError::EISCONN), - } - } - - fn recvfrom(&self, buf: &mut [u8]) -> LinuxResult<(usize, Option)> { - match self { - // diff: must bind before recvfrom - Socket::Udp(udpsocket) => Ok(udpsocket - .lock() - .recv_from(buf) - .map(|res| (res.0, Some(res.1)))?), - Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().recv(buf, 0).map(|res| (res, None))?), - } - } - - fn listen(&self) -> LinuxResult { - match self { - Socket::Udp(_) => Err(LinuxError::EOPNOTSUPP), - Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().listen()?), +fn parse_socket_address( + addr: *const ctypes::sockaddr, + addrlen: ctypes::socklen_t, +) -> LinuxResult { + let domain = SocketDomain::try_from(unsafe { *(addr as *const u16) })?; + match domain { + SocketDomain::Inet => { + if addrlen < (size_of::() as u32) { + return Err(LinuxError::EINVAL); + } + let addr = unsafe { *(addr as *const ctypes::sockaddr_in) }; + Ok(SocketAddress::Inet(SocketAddrV4::new( + Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()), + u16::from_be(addr.sin_port), + ))) + } + SocketDomain::Unix => { + const UNIX_PATH_MAX: u32 = 108; + if addrlen < 2 || addrlen > UNIX_PATH_MAX + SA_FAMILY_SIZE as u32 { + return Err(LinuxError::EINVAL); + } + let len = (addrlen - 2).min(UNIX_PATH_MAX); + let sun_path = unsafe { + core::slice::from_raw_parts((addr as *const u8).add(SA_FAMILY_SIZE), len as usize) + }; + if sun_path[0] == b'\0' { + let path = sun_path[1..] + .iter() + .take_while(|&&c| c != 0) + .cloned() + .collect::>(); + Ok(SocketAddress::Unix(UnixSocketAddr::Abstract(Arc::from( + path, + )))) + } else { + let abs_path = parse_path(sun_path.as_ptr() as *const c_char)?; + Ok(SocketAddress::Unix(UnixSocketAddr::PathName(abs_path))) + } } + SocketDomain::Inet6 => Err(LinuxError::EAFNOSUPPORT), } +} - fn accept(&self) -> LinuxResult { - match self { - Socket::Udp(_) => Err(LinuxError::EOPNOTSUPP), - Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().accept()?), - } +fn write_sockaddr_with_max_len( + address: SocketAddress, + addr_ptr: *mut ::core::ffi::c_void, + max_len: u32, +) -> LinuxResult { + if addr_ptr.is_null() { + warn!("write_sockaddr_with_max_len with null pointer"); + return Err(LinuxError::EFAULT); } - - fn shutdown(&self) -> LinuxResult { - match self { - Socket::Udp(udpsocket) => { - let udpsocket = udpsocket.lock(); - udpsocket.peer_addr()?; - udpsocket.shutdown()?; - Ok(()) + let actual_len = match address { + SocketAddress::Unix(unix_addr) => match unix_addr { + UnixSocketAddr::PathName(abs_path) => { + let actual_len = SA_FAMILY_SIZE + abs_path.len() + 1; + let write_len = core::cmp::min(actual_len, max_len as usize); + unsafe { *(addr_ptr as *mut u16) = ctypes::AF_UNIX as u16 }; + let sun_path_ptr = unsafe { addr_ptr.add(SA_FAMILY_SIZE) }; + unsafe { + core::ptr::copy_nonoverlapping( + abs_path.as_ptr(), + sun_path_ptr as *mut u8, + write_len, + ) + }; + unsafe { *(addr_ptr as *mut u8).add(write_len - 1) = b'\0' }; // Null-terminate the path + actual_len } - - Socket::Tcp(tcpsocket) => { - let tcpsocket = tcpsocket.lock(); - tcpsocket.peer_addr()?; - tcpsocket.shutdown()?; - Ok(()) + UnixSocketAddr::Unamed => { + warn!("write unamed unix addr"); + let actual_len = SA_FAMILY_SIZE + 1; + unsafe { *(addr_ptr as *mut u16) = ctypes::AF_UNIX as u16 }; + let sun_path_ptr = unsafe { addr_ptr.add(SA_FAMILY_SIZE) }; + unsafe { *(sun_path_ptr as *mut u8) = b'\0' }; + actual_len } + UnixSocketAddr::Abstract(_) => todo!(), + }, + SocketAddress::Inet(ipv4_addr) => { + let actual_len = size_of::(); + let write_len = core::cmp::min(actual_len, max_len as usize); + let sockaddr_in = ctypes::sockaddr_in::from(ipv4_addr); + unsafe { + core::ptr::copy_nonoverlapping( + &sockaddr_in as *const ctypes::sockaddr_in as *const u8, + addr_ptr as *mut u8, + write_len, + ); + } + actual_len } - } + SocketAddress::Inet6(_) => todo!(), + }; + Ok(actual_len as u32) } -impl FileLike for Socket { - fn read(&self, buf: &mut [u8]) -> LinuxResult { - self.recv(buf, 0) - } - - fn write(&self, buf: &[u8]) -> LinuxResult { - self.send(buf) - } - - ///TODO - fn flush(&self) -> LinuxResult { - Ok(()) - } - - fn stat(&self) -> LinuxResult { - // not really implemented - let st_mode = 0o140000 | 0o777u32; // S_IFSOCK | rwxrwxrwx - Ok(RuxStat::from(ctypes::stat { - st_ino: 1, - st_nlink: 1, - st_mode, - st_uid: 1000, - st_gid: 1000, - st_blksize: 4096, - ..Default::default() - })) - } - - fn into_any(self: Arc) -> Arc { - self - } - - fn poll(&self) -> LinuxResult { - self.poll() +fn write_sockaddr_with_max_len_ptr( + address: SocketAddress, + addr_ptr: *mut ::core::ffi::c_void, + max_len_ptr: *mut ctypes::socklen_t, +) -> LinuxResult { + if addr_ptr.is_null() || max_len_ptr.is_null() { + warn!("write_sockaddr_with_max_len_ptr with null pointer"); + return Err(LinuxError::EFAULT); } + let actual_len = write_sockaddr_with_max_len(address, addr_ptr, unsafe { *max_len_ptr })?; + unsafe { *max_len_ptr = actual_len as ctypes::socklen_t }; + Ok(()) +} - fn set_nonblocking(&self, nonblock: bool) -> LinuxResult { - match self { - Socket::Udp(udpsocket) => udpsocket.lock().set_nonblocking(nonblock), - Socket::Tcp(tcpsocket) => tcpsocket.lock().set_nonblocking(nonblock), - } - Ok(()) +impl From for SocketAddrV4 { + fn from(addr: ctypes::sockaddr_in) -> SocketAddrV4 { + SocketAddrV4::new( + Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()), + u16::from_be(addr.sin_port), + ) } } @@ -199,71 +164,30 @@ impl From for ctypes::sockaddr_in { } } -impl From for SocketAddrV4 { - fn from(addr: ctypes::sockaddr_in) -> SocketAddrV4 { - SocketAddrV4::new( - Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()), - u16::from_be(addr.sin_port), - ) - } -} - -fn into_sockaddr(addr: SocketAddr) -> (ctypes::sockaddr, ctypes::socklen_t) { - debug!(" Sockaddr: {}", addr); - match addr { - SocketAddr::V4(addr) => ( - unsafe { *(&ctypes::sockaddr_in::from(addr) as *const _ as *const ctypes::sockaddr) }, - size_of::() as _, - ), - SocketAddr::V6(_) => panic!("IPv6 is not supported"), - } -} - -fn from_sockaddr( - addr: *const ctypes::sockaddr, - addrlen: ctypes::socklen_t, -) -> LinuxResult { - if addr.is_null() { - return Err(LinuxError::EFAULT); - } - if addrlen != size_of::() as _ { - return Err(LinuxError::EINVAL); - } - - let mid = unsafe { *(addr as *const ctypes::sockaddr_in) }; - if mid.sin_family != ctypes::AF_INET as u16 { - return Err(LinuxError::EINVAL); - } - - let res = SocketAddr::V4(mid.into()); - debug!(" load sockaddr:{:#x} => {:?}", addr as usize, res); - Ok(res) -} - /// Create an socket for communication. /// /// Return the socket file descriptor. pub fn sys_socket(domain: c_int, socktype: c_int, protocol: c_int) -> c_int { - debug!("sys_socket <= {} {} {}", domain, socktype, protocol); - let (domain, socktype, protocol) = (domain as u32, socktype as u32, protocol as u32); - pub const _SOCK_STREAM_NONBLOCK: u32 = ctypes::SOCK_STREAM | ctypes::SOCK_NONBLOCK; syscall_body!(sys_socket, { - match (domain, socktype, protocol) { - (ctypes::AF_INET, ctypes::SOCK_STREAM, ctypes::IPPROTO_TCP) - | (ctypes::AF_INET, ctypes::SOCK_STREAM, 0) => { - Socket::Tcp(Mutex::new(TcpSocket::new())).add_to_fd_table() - } - (ctypes::AF_INET, ctypes::SOCK_DGRAM, ctypes::IPPROTO_UDP) - | (ctypes::AF_INET, ctypes::SOCK_DGRAM, 0) => { - Socket::Udp(Mutex::new(UdpSocket::new())).add_to_fd_table() - } - (ctypes::AF_INET, _SOCK_STREAM_NONBLOCK, ctypes::IPPROTO_TCP) => { - let tcp_socket = TcpSocket::new(); - tcp_socket.set_nonblocking(true); - Socket::Tcp(Mutex::new(tcp_socket)).add_to_fd_table() - } - _ => Err(LinuxError::EINVAL), - } + let socktype = socktype as u32; + let mut flags = OpenFlags::empty(); + let nonblock = socktype & ctypes::SOCK_NONBLOCK != 0; + if socktype & ctypes::SOCK_CLOEXEC != 0 { + flags |= OpenFlags::O_CLOEXEC; + } + let socktype = SocketType::try_from(socktype & 0xf)?; + let domain = SocketDomain::try_from(domain as u16)?; + debug!("sys_socket <= {domain:?} {socktype:?} {protocol}"); + debug!("nonblock: {nonblock}, cloexec: {flags:?}"); + let f = match domain { + SocketDomain::Inet => match socktype { + SocketType::Stream => Arc::new(Socket::Tcp(Mutex::new(TcpSocket::new(nonblock)))), + SocketType::Datagram => Arc::new(Socket::Udp(Mutex::new(UdpSocket::new()))), + }, + SocketDomain::Unix => UnixSocket::create_socket(socktype, nonblock), + SocketDomain::Inet6 => return Err(LinuxError::EAFNOSUPPORT), + }; + add_file_like(f, flags) }) } @@ -278,8 +202,7 @@ pub fn sys_setsockopt( optlen: ctypes::socklen_t, ) -> c_int { debug!( - "sys_setsockopt <= fd: {}, level: {}, optname: {}, optlen: {}, IGNORED", - fd, level, optname, optlen + "sys_setsockopt <= fd: {fd}, level: {level}, optname: {optname}, optlen: {optlen}, IGNORED" ); syscall_body!(sys_setsockopt, Ok(0)) } @@ -292,13 +215,10 @@ pub fn sys_bind( socket_addr: *const ctypes::sockaddr, addrlen: ctypes::socklen_t, ) -> c_int { - debug!( - "sys_bind <= {} {:#x} {}", - socket_fd, socket_addr as usize, addrlen - ); syscall_body!(sys_bind, { - let addr = from_sockaddr(socket_addr, addrlen)?; - Socket::from_fd(socket_fd)?.bind(addr)?; + let address = parse_socket_address(socket_addr, addrlen)?; + debug!("sys_bind <= {socket_fd} {address:?}"); + socket_from_fd(socket_fd)?.bind(address)?; Ok(0) }) } @@ -316,8 +236,8 @@ pub fn sys_connect( socket_fd, socket_addr as usize, addrlen ); syscall_body!(sys_connect, { - let addr = from_sockaddr(socket_addr, addrlen)?; - Socket::from_fd(socket_fd)?.connect(addr)?; + let address = parse_socket_address(socket_addr, addrlen)?; + socket_from_fd(socket_fd)?.connect(address)?; Ok(0) }) } @@ -329,25 +249,26 @@ pub fn sys_sendto( socket_fd: c_int, buf_ptr: *const c_void, len: ctypes::size_t, - flag: c_int, // currently not used + flags: c_int, socket_addr: *const ctypes::sockaddr, addrlen: ctypes::socklen_t, ) -> ctypes::ssize_t { - debug!( - "sys_sendto <= {} {:#x} {} {} {:#x} {}", - socket_fd, buf_ptr as usize, len, flag, socket_addr as usize, addrlen - ); if socket_addr.is_null() { - return sys_send(socket_fd, buf_ptr, len, flag); + debug!("sendto without address, use send instead"); + return sys_send(socket_fd, buf_ptr, len, flags); } - syscall_body!(sys_sendto, { + let address = parse_socket_address(socket_addr, addrlen)?; + let flags = MessageFlags::from_bits_truncate(flags); + debug!( + "sys_sendto <= {socket_fd} {:#x} {len} {flags:?} {address:?}", + buf_ptr as usize + ); if buf_ptr.is_null() { return Err(LinuxError::EFAULT); } - let addr = from_sockaddr(socket_addr, addrlen)?; let buf = unsafe { core::slice::from_raw_parts(buf_ptr as *const u8, len) }; - Socket::from_fd(socket_fd)?.sendto(buf, addr) + socket_from_fd(socket_fd)?.sendto(buf, address, flags) }) } @@ -358,18 +279,16 @@ pub fn sys_send( socket_fd: c_int, buf_ptr: *const c_void, len: ctypes::size_t, - flag: c_int, // currently not used + flag: c_int, ) -> ctypes::ssize_t { - debug!( - "sys_sendto <= {} {:#x} {} {}", - socket_fd, buf_ptr as usize, len, flag - ); syscall_body!(sys_send, { - if buf_ptr.is_null() { - return Err(LinuxError::EFAULT); - } + let flags = MessageFlags::from_bits_truncate(flag); + debug!( + "sys_send <= {socket_fd} {:#x} {len} {flags:?}", + buf_ptr as usize + ); let buf = unsafe { core::slice::from_raw_parts(buf_ptr as *const u8, len) }; - Socket::from_fd(socket_fd)?.send(buf) + socket_from_fd(socket_fd)?.send(buf, flags) }) } @@ -380,30 +299,31 @@ pub unsafe fn sys_recvfrom( socket_fd: c_int, buf_ptr: *mut c_void, len: ctypes::size_t, - flag: c_int, // currently not used + flags: c_int, socket_addr: *mut ctypes::sockaddr, addrlen: *mut ctypes::socklen_t, ) -> ctypes::ssize_t { - debug!( - "sys_recvfrom <= {} {:#x} {} {} {:#x} {:#x}", - socket_fd, buf_ptr as usize, len, flag, socket_addr as usize, addrlen as usize - ); if socket_addr.is_null() { - return sys_recv(socket_fd, buf_ptr, len, flag); + debug!("recvfrom without address, use recv instead"); + return sys_recv(socket_fd, buf_ptr, len, flags); } syscall_body!(sys_recvfrom, { + let flags = MessageFlags::from_bits_truncate(flags); + debug!( + "sys_recvfrom <= {socket_fd} {:#x} {len} {flags:?} {:#x} {:#x}", + buf_ptr as usize, socket_addr as usize, addrlen as usize + ); if buf_ptr.is_null() || addrlen.is_null() { + warn!("recvfrom with null buffer or addrlen"); return Err(LinuxError::EFAULT); } - let socket = Socket::from_fd(socket_fd)?; + let socket = socket_from_fd(socket_fd)?; let buf = unsafe { core::slice::from_raw_parts_mut(buf_ptr as *mut u8, len) }; - let res = socket.recvfrom(buf)?; + let res = socket.recvfrom(buf, flags)?; if let Some(addr) = res.1 { - unsafe { - (*socket_addr, *addrlen) = into_sockaddr(addr); - } + write_sockaddr_with_max_len_ptr(addr, socket_addr as _, addrlen)?; } Ok(res.0) }) @@ -416,31 +336,29 @@ pub fn sys_recv( socket_fd: c_int, buf_ptr: *mut c_void, len: ctypes::size_t, - flag: c_int, // currently not used + flags: c_int, ) -> ctypes::ssize_t { - debug!( - "sys_recv <= {} {:#x} {} {}", - socket_fd, buf_ptr as usize, len, flag - ); syscall_body!(sys_recv, { if buf_ptr.is_null() { return Err(LinuxError::EFAULT); } let buf = unsafe { core::slice::from_raw_parts_mut(buf_ptr as *mut u8, len) }; - Socket::from_fd(socket_fd)?.recv(buf, flag) + let flags = MessageFlags::from_bits_truncate(flags); + debug!( + "sys_recv <= {} {:#x} {} {:?}", + socket_fd, buf_ptr as usize, len, flags + ); + socket_from_fd(socket_fd)?.recv(buf, flags) }) } /// Listen for connections on a socket /// /// Return 0 if success. -pub fn sys_listen( - socket_fd: c_int, - backlog: c_int, // currently not used -) -> c_int { - debug!("sys_listen <= {} {}", socket_fd, backlog); +pub fn sys_listen(socket_fd: c_int, backlog: c_int) -> c_int { + debug!("sys_listen <= {socket_fd} {backlog}"); syscall_body!(sys_listen, { - Socket::from_fd(socket_fd)?.listen()?; + socket_from_fd(socket_fd)?.listen(backlog)?; Ok(0) }) } @@ -458,16 +376,13 @@ pub unsafe fn sys_accept( socket_fd, socket_addr as usize, socket_len as usize ); syscall_body!(sys_accept, { - if socket_addr.is_null() || socket_len.is_null() { - return Err(LinuxError::EFAULT); - } - let socket = Socket::from_fd(socket_fd)?; + let socket = socket_from_fd(socket_fd)?; let new_socket = socket.accept()?; - let addr = new_socket.peer_addr()?; - let new_fd = Socket::add_to_fd_table(Socket::Tcp(Mutex::new(new_socket)))?; - unsafe { - (*socket_addr, *socket_len) = into_sockaddr(addr); + if !socket_addr.is_null() { + let peer_addr = new_socket.peer_addr()?; + write_sockaddr_with_max_len_ptr(peer_addr, socket_addr as _, socket_len)?; } + let new_fd = add_file_like(new_socket, OpenFlags::empty())?; Ok(new_fd) }) } @@ -475,13 +390,11 @@ pub unsafe fn sys_accept( /// Shut down a full-duplex connection. /// /// Return 0 if success. -pub fn sys_shutdown( - socket_fd: c_int, - flag: c_int, // currently not used -) -> c_int { - debug!("sys_shutdown <= {} {}", socket_fd, flag); +pub fn sys_shutdown(socket_fd: c_int, how: c_int) -> c_int { syscall_body!(sys_shutdown, { - Socket::from_fd(socket_fd)?.shutdown()?; + let flags = ShutdownFlags::try_from(how)?; + debug!("sys_shutdown <= {socket_fd} {flags:?}"); + socket_from_fd(socket_fd)?.shutdown(flags)?; Ok(0) }) } @@ -500,7 +413,7 @@ pub unsafe fn sys_getaddrinfo( ) -> c_int { let name = char_ptr_to_str(nodename); let port = char_ptr_to_str(servname); - debug!("sys_getaddrinfo <= {:?} {:?}", name, port); + debug!("sys_getaddrinfo <= {name:?} {port:?}"); syscall_body!(sys_getaddrinfo, { if nodename.is_null() && servname.is_null() { return Ok(0); @@ -588,39 +501,91 @@ pub unsafe fn sys_getsockname( sock_fd, addr as usize, addrlen as usize ); syscall_body!(sys_getsockname, { - if addr.is_null() || addrlen.is_null() { + let local_addr = socket_from_fd(sock_fd)?.local_addr()?; + debug!("socket {sock_fd} local address: {local_addr:?}"); + write_sockaddr_with_max_len_ptr(local_addr, addr as _, addrlen)?; + Ok(0) + }) +} + +/// get socket option +/// +/// TODO: some options not impl, just return 0, like SO_RCVBUF SO_SNDBUF +pub fn sys_getsockopt( + socket_fd: c_int, + level: c_int, + optname: c_int, + optval: *mut c_void, + optlen: *mut ctypes::socklen_t, +) -> c_int { + unsafe { + info!( + "sys_getsockopt <= fd: {}, level: {}, optname: {}, optlen: {}, IGNORED", + socket_fd, + level, + optname, + core::ptr::read(optlen as *mut usize) + ); + } + syscall_body!(sys_getsockopt, { + if optval.is_null() { return Err(LinuxError::EFAULT); } - if unsafe { *addrlen } < size_of::() as u32 { - return Err(LinuxError::EINVAL); - } - unsafe { - (*addr, *addrlen) = into_sockaddr(Socket::from_fd(sock_fd)?.local_addr()?); + let socket = socket_from_fd(socket_fd)?; + match level as u32 { + ctypes::SOL_SOCKET => { + let val = match optname as u32 { + ctypes::SO_ACCEPTCONN => match &*socket { + Socket::Udp(_) => 0, + Socket::Tcp(tcpsocket) => tcpsocket.lock().is_listening() as u32, + Socket::Unix(unixsocket) => unixsocket.is_listening() as u32, + }, + ctypes::SO_TYPE => match &*socket { + Socket::Udp(_) => ctypes::SOCK_DGRAM, + Socket::Tcp(_) => ctypes::SOCK_STREAM, + Socket::Unix(unixsocket) => unixsocket.socket_type().into(), + }, + ctypes::SO_RCVLOWAT | ctypes::SO_SNDLOWAT | ctypes::SO_BROADCAST => 1, + ctypes::SO_ERROR + | ctypes::SO_DONTROUTE + | ctypes::SO_KEEPALIVE + | ctypes::SO_LINGER + | ctypes::SO_OOBINLINE + | ctypes::SO_RCVBUF + | ctypes::SO_RCVTIMEO + | ctypes::SO_REUSEADDR + | ctypes::SO_SNDBUF + | ctypes::SO_SNDTIMEO + | ctypes::SO_BINDTODEVICE => 0, + _ => return Err(LinuxError::ENOPROTOOPT), + }; + + unsafe { + core::ptr::write(optlen as *mut usize, core::mem::size_of::()); + core::ptr::write(optval as *mut i32, val as i32); + } + + Ok(0) + } + _ => Err(LinuxError::ENOSYS), } - Ok(0) }) } /// Get peer address to which the socket sockfd is connected. -pub unsafe fn sys_getpeername( +pub fn sys_getpeername( sock_fd: c_int, - addr: *mut ctypes::sockaddr, - addrlen: *mut ctypes::socklen_t, + socket_addr: *mut ctypes::sockaddr, + socket_len: *mut ctypes::socklen_t, ) -> c_int { debug!( "sys_getpeername <= {} {:#x} {:#x}", - sock_fd, addr as usize, addrlen as usize + sock_fd, socket_addr as usize, socket_len as usize ); syscall_body!(sys_getpeername, { - if addr.is_null() || addrlen.is_null() { - return Err(LinuxError::EFAULT); - } - if unsafe { *addrlen } < size_of::() as u32 { - return Err(LinuxError::EINVAL); - } - unsafe { - (*addr, *addrlen) = into_sockaddr(Socket::from_fd(sock_fd)?.peer_addr()?); - } + let peer_addr = socket_from_fd(sock_fd)?.peer_addr()?; + debug!("socket {sock_fd} peer address: {peer_addr:?}"); + write_sockaddr_with_max_len_ptr(peer_addr, socket_addr as _, socket_len)?; Ok(0) }) } @@ -639,27 +604,222 @@ pub unsafe fn sys_sendmsg( if msg.is_null() { return Err(LinuxError::EFAULT); } - let msg = *msg; - if msg.msg_iov.is_null() { + let msghdr = unsafe { *msg }; + if msghdr.msg_iov.is_null() { return Err(LinuxError::EFAULT); } - let iovs = core::slice::from_raw_parts(msg.msg_iov, msg.msg_iovlen as usize); - let socket = Socket::from_fd(socket_fd)?; - let mut ret = 0; + let dst_address = if msghdr.msg_name.is_null() { + None + } else { + Some(parse_socket_address( + msghdr.msg_name as _, + msghdr.msg_namelen, + )?) + }; - for iov in iovs.iter() { - if iov.iov_base.is_null() { - return Err(LinuxError::EFAULT); + let flags = MessageFlags::from_bits_truncate(flags); + let socket = socket_from_fd(socket_fd)?; + debug!("send msg to {dst_address:?} with flags {flags:?}"); + + let mut ancillary_data = Vec::new(); + let cmsg_header_size = size_of::(); + let mut cmsg_header_ptr = msghdr.msg_control as *const u8; + let cmsg_header_ptr_end = unsafe { cmsg_header_ptr.add(msghdr.msg_controllen as usize) }; + if !msghdr.msg_control.is_null() { + loop { + if cmsg_header_ptr >= cmsg_header_ptr_end { + break; + } + let cmsg_header = unsafe { *(cmsg_header_ptr as *const ctypes::cmsghdr) }; + if (cmsg_header.cmsg_len as usize) < cmsg_header_size { + return Err(LinuxError::EINVAL); + } + let cmsg_data_ptr = unsafe { cmsg_header_ptr.add(cmsg_header_size) }; + let cmsg_data_size = (cmsg_header.cmsg_len) as usize - cmsg_header_size; + let cmsg_data = + unsafe { core::slice::from_raw_parts(cmsg_data_ptr, cmsg_data_size).to_vec() }; + ancillary_data.push(ControlMessageData::try_new( + cmsg_header.cmsg_level, + cmsg_header.cmsg_type, + cmsg_data, + )?); + cmsg_header_ptr = unsafe { cmsg_header_ptr.add(cmsg_header.cmsg_len as usize) }; + cmsg_header_ptr = cmsg_align(cmsg_header_ptr as _) as *const u8; } - let buf = core::slice::from_raw_parts(iov.iov_base as *const u8, iov.iov_len); - ret += match &socket as &Socket { - Socket::Udp(udpsocket) => udpsocket.lock().send_to( - buf, - from_sockaddr(msg.msg_name as *const ctypes::sockaddr, msg.msg_namelen)?, - )?, - Socket::Tcp(tcpsocket) => tcpsocket.lock().send(buf)?, + } + let iovecs = + IoVecsInput::from_iovecs(read_iovecs_ptr(msghdr.msg_iov as _, msghdr.msg_iovlen as _)); + let bytes_send = socket.sendmsg(&iovecs, dst_address, &mut ancillary_data, flags)?; + Ok(bytes_send) + }) +} + +/// Receives a message from a socket, supporting scatter/gather I/O and ancillary data. +pub unsafe fn sys_recvmsg( + socket_fd: c_int, + msg: *mut ctypes::msghdr, + flags: c_int, +) -> ctypes::ssize_t { + debug!("sys_recvmsg <= {} {:#x} {}", socket_fd, msg as usize, flags); + syscall_body!(sys_recvmsg, { + if msg.is_null() { + return Err(LinuxError::EFAULT); + } + let mut msghdr = unsafe { *msg }; + if msghdr.msg_iov.is_null() { + return Err(LinuxError::EFAULT); + } + let flags = MessageFlags::from_bits_truncate(flags); + let socket = socket_from_fd(socket_fd)?; + let mut iovecs = + IoVecsOutput::from_iovecs(read_iovecs_ptr(msghdr.msg_iov as _, msghdr.msg_iovlen as _)); + let info = socket.recvmsg(&mut iovecs, flags)?; + + // Handle source address return (if requested) + if !msghdr.msg_name.is_null() { + // The `msg_name` field points to a caller-allocated buffer that is used to return the source address if the socket is unconnected. + // The caller should set msg_namelen to the size of this buffer before this call; upon return from a successful call, msg_namelen + // will contain the size of the returned address. + if let Some(address) = info.address { + msghdr.msg_namelen = + write_sockaddr_with_max_len(address, msghdr.msg_name, msghdr.msg_namelen)?; + } + } + + // Initialize flags output field + msghdr.msg_flags = 0; + // Set `MSG_TRUNC` if received more data than buffer space + if info.bytes_read != info.bytes_total { + msghdr.msg_flags |= MessageFlags::MSG_TRUNC.bits(); + } + + // Ancillary data processing setup + let cmsg_header_size = size_of::(); + let ancillary_data_buffer_size = msghdr.msg_controllen as usize; + let msg_control_ptr = msghdr.msg_control as *const u8; + let mut ancillary_data_bytes_writen = 0; + // Process each ancillary data item + for cmsg_data in info.ancillary_data { + let cmsg_data_size = cmsg_data.size(); + let expect_size = cmsg_header_size + cmsg_data_size; + let minium_size = cmsg_data.minium_size(); + let space_available = ancillary_data_buffer_size - ancillary_data_bytes_writen; + // cmsg_bytes = cmsg_header + cmsg_data + let cmsg_bytes = if space_available < cmsg_header_size + minium_size { + // Not enough space even for minimal data + Vec::new() + } else { + let (cmsg_level, cmsg_type, mut cmsg_data_bytes) = cmsg_data.parse(flags)?; + // If the space allocated for receiving incoming ancillary data is + // too small then the ancillary data is truncated to the number of + // headers that will fit in the supplied buffer (see unix.7) + cmsg_data_bytes.truncate(space_available - cmsg_header_size); + + pack_cmsg(cmsg_level, cmsg_type, cmsg_data_bytes) }; + + // Handle truncation flags + let truncated = cmsg_bytes.len() < expect_size; + if truncated { + msghdr.msg_flags |= MessageFlags::MSG_CTRUNC.bits(); + } + + if cmsg_bytes.len() < cmsg_header_size { + // Can't fit the header, so stop trying to write + break; + } + + // Copy to user-space buffer if valid + if !cmsg_bytes.is_empty() { + unsafe { + core::ptr::copy_nonoverlapping( + cmsg_bytes.as_ptr(), + (msg_control_ptr.add(ancillary_data_bytes_writen)) as *mut u8, + cmsg_bytes.len(), + ) + }; + ancillary_data_bytes_writen += cmsg_bytes.len(); + // Align for next CMsg + if !truncated { + ancillary_data_bytes_writen = cmsg_align(ancillary_data_bytes_writen); + } + } + } + + // Update user-space `msghdr` structure + unsafe { *msg = msghdr }; + + match flags.contains(MessageFlags::MSG_TRUNC) { + true => Ok(info.bytes_total), // Return actual packet size + false => Ok(info.bytes_read), // Return received bytes } - Ok(ret) }) } + +/// Creates a pair of Unix domain sockets and stores the file descriptors in `sv` +/// +/// This system call only works for UNIX domain sockets (AF_UNIX), which are used for communication +/// between processes on the same machine. It cannot be used for communication over the network (e.g., +/// using AF_INET or AF_INET6). The created socket pair is anonymous, meaning it does not require +/// a pathname, and is typically used for communication between related processes (e.g., parent-child processes) +pub fn sys_socketpair(domain: c_int, socktype: c_int, protocol: c_int, sv: &mut [c_int]) -> c_int { + syscall_body!(sys_socketpair, { + let domain = SocketDomain::try_from(domain as u16)?; + if domain != SocketDomain::Unix { + return Err(LinuxError::EAFNOSUPPORT); + } + let mut flags = OpenFlags::empty(); + let socktype = socktype as u32; + let nonblock = (socktype & ctypes::SOCK_NONBLOCK) != 0; + if socktype & ctypes::SOCK_CLOEXEC != 0 { + flags |= OpenFlags::O_CLOEXEC; + } + let socktype = socktype & !ctypes::SOCK_CLOEXEC & !ctypes::SOCK_NONBLOCK; + let socktype = SocketType::try_from(socktype)?; + info!("sys_socketpair <= domain: {domain:?}, socktype: {socktype:?}, protocol: {protocol}, sv pointer: {:#x}", sv.as_ptr() as usize); + info!("nonblock: {nonblock}, cloexec: {flags:?}"); + let (sk1, sk2) = UnixSocket::create_socket_pair(socktype, nonblock); + sv[0] = add_file_like(sk1, flags)?; + sv[1] = add_file_like(sk2, flags)?; + info!("create sv[0] {}, sv[1] {}", sv[0], sv[1]); + Ok(0) + }) +} + +fn socket_from_fd(fd: i32) -> LinuxResult> { + get_file_like(fd)? + .into_any() + .downcast::() + .map_err(|_| LinuxError::ENOTSOCK) +} + +/* +* the following functions refers to macros in musl +* see https://www.man7.org/linux/man-pages/man3/cmsg.3.html +*/ + +/// ```c +/// #define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1)) +/// ``` +fn cmsg_align(len: usize) -> usize { + const ALIGNMENT: usize = core::mem::size_of::(); + (len + ALIGNMENT - 1) & !(ALIGNMENT - 1) +} + +fn pack_cmsg(cmsg_level: i32, cmsg_type: i32, cmsg_data_bytes: Vec) -> Vec { + let cmsg_header_size = size_of::(); + let cmsg_len = cmsg_header_size + cmsg_data_bytes.len(); + let cmsghdr = ctypes::cmsghdr { + cmsg_len: cmsg_len as _, + __pad1: 0, + cmsg_level, + cmsg_type, + }; + let mut buffer = Vec::with_capacity(cmsg_len); + unsafe { + let hdr_ptr = &cmsghdr as *const _ as *const u8; + buffer.extend_from_slice(core::slice::from_raw_parts(hdr_ptr, cmsg_header_size)); + } + buffer.extend(cmsg_data_bytes); + buffer +} diff --git a/api/ruxos_posix_api/src/imp/pipe.rs b/api/ruxos_posix_api/src/imp/pipe.rs index 7a67120ff..c62d3023b 100644 --- a/api/ruxos_posix_api/src/imp/pipe.rs +++ b/api/ruxos_posix_api/src/imp/pipe.rs @@ -7,265 +7,39 @@ * See the Mulan PSL v2 for more details. */ -use alloc::sync::Arc; use core::ffi::c_int; +use ruxfs::fifo; -use axerrno::{LinuxError, LinuxResult}; -use axio::PollState; -use axsync::Mutex; -use ruxfdtable::{FileLike, RuxStat}; +use axerrno::LinuxError; +use ruxfdtable::OpenFlags; -use super::fd_ops::{add_file_like, close_file_like}; -use crate::{ctypes, sys_fcntl}; - -#[derive(Copy, Clone, PartialEq)] -enum RingBufferStatus { - Full, - Empty, - Normal, -} - -const RING_BUFFER_SIZE: usize = 256; - -pub struct PipeRingBuffer { - arr: [u8; RING_BUFFER_SIZE], - head: usize, - tail: usize, - status: RingBufferStatus, -} - -impl PipeRingBuffer { - pub const fn new() -> Self { - Self { - arr: [0; RING_BUFFER_SIZE], - head: 0, - tail: 0, - status: RingBufferStatus::Empty, - } - } - - pub fn write_byte(&mut self, byte: u8) { - self.status = RingBufferStatus::Normal; - self.arr[self.tail] = byte; - self.tail = (self.tail + 1) % RING_BUFFER_SIZE; - if self.tail == self.head { - self.status = RingBufferStatus::Full; - } - } - - pub fn read_byte(&mut self) -> u8 { - self.status = RingBufferStatus::Normal; - let c = self.arr[self.head]; - self.head = (self.head + 1) % RING_BUFFER_SIZE; - if self.head == self.tail { - self.status = RingBufferStatus::Empty; - } - c - } - - /// Get the length of remaining data in the buffer - pub const fn available_read(&self) -> usize { - if matches!(self.status, RingBufferStatus::Empty) { - 0 - } else if self.tail > self.head { - self.tail - self.head - } else { - self.tail + RING_BUFFER_SIZE - self.head - } - } - - /// Get the length of remaining space in the buffer - pub const fn available_write(&self) -> usize { - if matches!(self.status, RingBufferStatus::Full) { - 0 - } else { - RING_BUFFER_SIZE - self.available_read() - } - } -} - -pub struct Pipe { - readable: bool, - buffer: Arc>, -} - -impl Pipe { - pub fn new() -> (Pipe, Pipe) { - let buffer = Arc::new(Mutex::new(PipeRingBuffer::new())); - let read_end = Pipe { - readable: true, - buffer: buffer.clone(), - }; - let write_end = Pipe { - readable: false, - buffer, - }; - (read_end, write_end) - } - - pub const fn readable(&self) -> bool { - self.readable - } - - pub const fn writable(&self) -> bool { - !self.readable - } - - pub fn write_end_close(&self) -> bool { - Arc::strong_count(&self.buffer) == 1 - } -} - -impl FileLike for Pipe { - fn read(&self, buf: &mut [u8]) -> LinuxResult { - if !self.readable() { - return Err(LinuxError::EPERM); - } - let mut read_size = 0usize; - let max_len = buf.len(); - let mut ring_buffer = self.buffer.lock(); - // First, check if there is data in the read end. - // This loop is only runs when the write end is open - // and there is no data available - loop { - let loop_read = ring_buffer.available_read(); - // If there is no data - if loop_read == 0 { - if self.write_end_close() { - // write end is closed, read 0 bytes. - return Ok(0); - } else { - // write end is open - drop(ring_buffer); - // Data not ready, wait for write end - crate::sys_sched_yield(); // TODO: use synconize primitive - ring_buffer = self.buffer.lock(); - } - } else { - break; - } - } - // read data - let loop_read = ring_buffer.available_read(); - for _ in 0..loop_read { - if read_size == max_len { - return Ok(read_size); - } - buf[read_size] = ring_buffer.read_byte(); - read_size += 1; - } - Ok(read_size) - } - - fn write(&self, buf: &[u8]) -> LinuxResult { - if !self.writable() { - return Err(LinuxError::EPERM); - } - let mut write_size = 0usize; - let max_len = buf.len(); - loop { - let mut ring_buffer = self.buffer.lock(); - let loop_write = ring_buffer.available_write(); - if loop_write == 0 { - drop(ring_buffer); - // Buffer is full, wait for read end to consume - crate::sys_sched_yield(); // TODO: use synconize primitive - continue; - } - for _ in 0..loop_write { - if write_size == max_len { - return Ok(write_size); - } - ring_buffer.write_byte(buf[write_size]); - write_size += 1; - } - } - } - - fn flush(&self) -> LinuxResult { - Ok(()) - } - - fn stat(&self) -> LinuxResult { - let st_mode = 0o10000 | 0o600u32; // S_IFIFO | rw------- - Ok(RuxStat::from(ctypes::stat { - st_ino: 1, - st_nlink: 1, - st_mode, - st_uid: 1000, - st_gid: 1000, - st_blksize: 4096, - ..Default::default() - })) - } - - fn into_any(self: Arc) -> Arc { - self - } - - fn poll(&self) -> LinuxResult { - let buf = self.buffer.lock(); - Ok(PollState { - readable: self.readable() && buf.available_read() > 0, - writable: self.writable() && buf.available_write() > 0, - }) - } - - fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult { - Ok(()) - } -} +use ruxtask::fs::add_file_like; /// Create a pipe /// /// Return 0 if succeed pub fn sys_pipe(fds: &mut [c_int]) -> c_int { - debug!("sys_pipe <= {:#x}", fds.as_ptr() as usize); - syscall_body!(sys_pipe, { - if fds.len() != 2 { - return Err(LinuxError::EFAULT); - } - - let (read_end, write_end) = Pipe::new(); - let read_fd = add_file_like(Arc::new(read_end))?; - let write_fd = add_file_like(Arc::new(write_end)).inspect_err(|_| { - close_file_like(read_fd).ok(); - })?; - - fds[0] = read_fd as c_int; - fds[1] = write_fd as c_int; - - Ok(0) - }) + sys_pipe2(fds, 0) } /// `pipe2` syscall, used by AARCH64 /// /// Return 0 on success pub fn sys_pipe2(fds: &mut [c_int], flag: c_int) -> c_int { - debug!( - "sys_pipe2 <= fds: {:#x}, flag: {}", - fds.as_ptr() as usize, - flag - ); - let ret = sys_pipe(fds); - if ret < 0 { - return ret; - } syscall_body!(sys_pipe2, { - if (flag as u32 & !(ctypes::O_CLOEXEC | ctypes::O_NONBLOCK)) != 0 { - return Err(LinuxError::EINVAL); - } - - if (flag as u32 & ctypes::O_CLOEXEC) != 0 { - sys_fcntl(fds[0], ctypes::F_SETFD as _, ctypes::FD_CLOEXEC as _); - sys_fcntl(fds[1], ctypes::F_SETFD as _, ctypes::FD_CLOEXEC as _); - } - - if (flag as u32 & ctypes::O_NONBLOCK) != 0 { - sys_fcntl(fds[0], ctypes::F_SETFL as _, ctypes::O_NONBLOCK as _); - sys_fcntl(fds[1], ctypes::F_SETFL as _, ctypes::O_NONBLOCK as _); - } + let flags = OpenFlags::from_bits(flag).ok_or(LinuxError::EINVAL)?; + debug!( + "sys_pipe2 <= fds: {:#x}, flag: {:?}", + fds.as_ptr() as usize, + flags + ); + let (reader, writer) = fifo::new_pipe_pair(flags); + fds[0] = add_file_like(reader, flags)?; + fds[1] = add_file_like(writer, flags)?; + debug!( + "[sys_pipe] create pipe with read fd {} and write fd {}", + fds[0], fds[1] + ); Ok(0) }) } diff --git a/api/ruxos_posix_api/src/imp/prctl.rs b/api/ruxos_posix_api/src/imp/prctl.rs index a9b8ddf01..6710e8fd3 100644 --- a/api/ruxos_posix_api/src/imp/prctl.rs +++ b/api/ruxos_posix_api/src/imp/prctl.rs @@ -15,7 +15,7 @@ const ARCH_SET_FS: i32 = 0x1002; /// set thread state pub fn sys_arch_prctl(code: c_int, addr: c_ulong) -> c_int { - debug!("sys_arch_prctl <= code: {}, addr: {:#x}", code, addr); + debug!("sys_arch_prctl <= code: {code}, addr: {addr:#x}"); syscall_body!(sys_arch_prctl, { match code { ARCH_SET_FS => { @@ -31,9 +31,6 @@ pub fn sys_arch_prctl(code: c_int, addr: c_ulong) -> c_int { /// TODO: fake implementation for prctl pub fn sys_prctl(op: c_int, arg0: c_ulong, arg1: c_ulong, arg2: c_ulong, arg3: c_ulong) -> c_int { - debug!( - "sys_prctl <= op: {}, arg0: {}, arg1: {}, arg2: {}, arg3: {}", - op, arg0, arg1, arg2, arg3 - ); + debug!("sys_prctl <= op: {op}, arg0: {arg0}, arg1: {arg1}, arg2: {arg2}, arg3: {arg3}"); syscall_body!(sys_prctl, Ok(0)) } diff --git a/api/ruxos_posix_api/src/imp/pthread/dummy.rs b/api/ruxos_posix_api/src/imp/pthread/dummy.rs index f87c2dd9a..5dba4c2cf 100644 --- a/api/ruxos_posix_api/src/imp/pthread/dummy.rs +++ b/api/ruxos_posix_api/src/imp/pthread/dummy.rs @@ -21,7 +21,7 @@ pub unsafe fn sys_pthread_key_create( /// Destroy a specific key for a process. pub fn sys_pthread_key_delete(key: ctypes::pthread_key_t) -> c_int { - debug!("sys_pthread_key_delete <= {}", key); + debug!("sys_pthread_key_delete <= {key}"); syscall_body!(sys_pthread_key_delete, Ok(0)) } @@ -33,7 +33,7 @@ pub fn sys_pthread_setspecific(key: ctypes::pthread_key_t, value: *const c_void) /// Get the value of a specific key for a thread. pub fn sys_pthread_getspecific(key: ctypes::pthread_key_t) -> *mut c_void { - debug!("sys_pthread_getspecific <= {}", key); + debug!("sys_pthread_getspecific <= {key}"); syscall_body!( sys_pthread_getspecific, Ok::<*mut c_void, axerrno::LinuxError>(core::ptr::null_mut() as _) diff --git a/api/ruxos_posix_api/src/imp/pthread/futex.rs b/api/ruxos_posix_api/src/imp/pthread/futex.rs index 05cb6ee11..8a9e97b51 100644 --- a/api/ruxos_posix_api/src/imp/pthread/futex.rs +++ b/api/ruxos_posix_api/src/imp/pthread/futex.rs @@ -111,10 +111,7 @@ pub fn sys_futex( } else { None }; - debug!( - "sys_futex <= addr: {:#x}, op: {:?}, val: {}, to: {:?}", - uaddr, op, val, timeout, - ); + debug!("sys_futex <= addr: {uaddr:#x}, op: {op:?}, val: {val}, to: {timeout:?}",); let ret = match op { FutexOp::FUTEX_WAIT => futex_wait(futex_addr, futex_val, timeout).map(|_| 0), diff --git a/api/ruxos_posix_api/src/imp/pthread/mod.rs b/api/ruxos_posix_api/src/imp/pthread/mod.rs index 6d95dae34..3ccf8c031 100644 --- a/api/ruxos_posix_api/src/imp/pthread/mod.rs +++ b/api/ruxos_posix_api/src/imp/pthread/mod.rs @@ -12,7 +12,7 @@ use core::cell::UnsafeCell; use core::ffi::{c_int, c_void}; use axerrno::{LinuxError, LinuxResult}; -use ruxtask::AxTaskRef; +use ruxtask::{current, AxTaskRef}; use spin::RwLock; use crate::ctypes; @@ -92,7 +92,7 @@ impl Pthread { inner: task_inner, retval: my_packet, }; - let ptr = Box::into_raw(Box::new(thread)) as *mut c_void; + let ptr: *mut c_void = Box::into_raw(Box::new(thread)) as *mut c_void; TID_TO_PTHREAD.write().insert(tid, ForceSendSync(ptr)); Ok(ptr) } @@ -119,7 +119,6 @@ impl Pthread { }; let task_inner = ruxtask::pspawn(main, tls as usize, set_tid, tl); - let tid = task_inner.id().as_u64(); let thread = Pthread { inner: task_inner.clone(), @@ -152,7 +151,7 @@ impl Pthread { let thread = { TID_TO_PTHREAD.read().get(&tid).unwrap().0 }; let thread = unsafe { Box::from_raw(thread as *mut Pthread) }; TID_TO_PTHREAD.write().remove(&tid); - debug!("Exit_musl, tid: {}", tid); + debug!("Exit_musl, tid: {tid}"); drop(thread); ruxtask::exit(0) } @@ -227,6 +226,21 @@ pub fn sys_pthread_exit(retval: *mut c_void) -> ! { Pthread::exit_current(retval); } +/// Exits the current thread. The value `retval` will be returned to the joiner. +pub fn sys_exit_group(status: c_int) -> ! { + debug!("sys_exit_group <= status: {status:#?}"); + + // TODO: exit all threads, send signal to all threads + + // drop all file opened by current task + current().fs.lock().as_mut().unwrap().close_all_files(); + + #[cfg(feature = "multitask")] + ruxtask::exit(status); + #[cfg(not(feature = "multitask"))] + ruxhal::misc::terminate(); +} + /// Waits for the given thread to exit, and stores the return value in `retval`. pub unsafe fn sys_pthread_join(thread: ctypes::pthread_t, retval: *mut *mut c_void) -> c_int { debug!("sys_pthread_join <= {:#x}", retval as usize); @@ -246,10 +260,7 @@ unsafe impl Send for ForceSendSync {} unsafe impl Sync for ForceSendSync {} /// Create new thread by `sys_clone`, return new thread ID -#[cfg(all( - feature = "musl", - any(target_arch = "aarch64", target_arch = "riscv64") -))] +#[cfg(all(feature = "musl", target_arch = "aarch64"))] pub unsafe fn sys_clone( flags: c_int, stack: *mut c_void, @@ -263,41 +274,128 @@ pub unsafe fn sys_clone( ); syscall_body!(sys_clone, { - if (flags as u32 & ctypes::CLONE_THREAD) == 0 { - debug!("ONLY support thread"); + if (flags as u32 & ctypes::CLONE_THREAD) != 0 { + let func = unsafe { + core::mem::transmute::<*const (), extern "C" fn(arg: *mut c_void) -> *mut c_void>( + (*(stack as *mut usize)) as *const (), + ) + }; + let args = unsafe { *((stack as usize + 8) as *mut usize) } as *mut c_void; + + let set_tid = if (flags as u32 & ctypes::CLONE_CHILD_SETTID) != 0 { + core::sync::atomic::AtomicU64::new(ctid as _) + } else { + core::sync::atomic::AtomicU64::new(0) + }; + + let (tid, task_inner) = Pthread::pcreate( + core::ptr::null(), + func, + args, + tls, + set_tid, + core::sync::atomic::AtomicU64::from(ctid as u64), + )?; + + // write tid to ptid + if (flags as u32 & ctypes::CLONE_PARENT_SETTID) != 0 { + unsafe { *ptid = tid as c_int }; + } + ruxtask::put_task(task_inner); + + return Ok(tid); + } else if (flags as u32 & ctypes::SIGCHLD) != 0 { + let pid = if let Some(task_ref) = ruxtask::fork_task() { + task_ref.id().as_u64() + } else { + let children_ref = ruxtask::current(); + let tid = children_ref.id().as_u64(); + let thread = Pthread { + inner: children_ref.clone_as_taskref(), + retval: Arc::new(Packet { + result: UnsafeCell::new(core::ptr::null_mut()), + }), + }; + let ptr = Box::into_raw(Box::new(thread)) as *mut c_void; + TID_TO_PTHREAD.write().insert(tid, ForceSendSync(ptr)); + 0 + }; + debug!("will sys_clone <= pid: {pid}"); + return Ok(pid); + } else { + debug!("ONLY support CLONE_THREAD and SIGCHLD"); return Err(LinuxError::EINVAL); } + }) +} - let func = unsafe { - core::mem::transmute::<*const (), extern "C" fn(arg: *mut c_void) -> *mut c_void>( - (*(stack as *mut usize)) as *const (), - ) - }; - let args = unsafe { *((stack as usize + 8) as *mut usize) } as *mut c_void; +/// Create new thread by `sys_clone`, return new thread ID +#[cfg(all(feature = "musl", target_arch = "riscv64"))] +pub unsafe fn sys_clone( + flags: c_int, + stack: *mut c_void, + ptid: *mut ctypes::pid_t, + tls: *mut c_void, + ctid: *mut ctypes::pid_t, +) -> c_int { + debug!( + "sys_clone <= flags: {:x}, stack: {:p}, ctid: {:x}", + flags, stack, ctid as usize + ); - let set_tid = if (flags as u32 & ctypes::CLONE_CHILD_SETTID) != 0 { - core::sync::atomic::AtomicU64::new(ctid as _) + syscall_body!(sys_clone, { + if (flags as u32 & ctypes::CLONE_THREAD) != 0 { + let func = unsafe { + core::mem::transmute::<*const (), extern "C" fn(arg: *mut c_void) -> *mut c_void>( + (*(stack as *mut usize)) as *const (), + ) + }; + let args = unsafe { *((stack as usize + 8) as *mut usize) } as *mut c_void; + + let set_tid = if (flags as u32 & ctypes::CLONE_CHILD_SETTID) != 0 { + core::sync::atomic::AtomicU64::new(ctid as _) + } else { + core::sync::atomic::AtomicU64::new(0) + }; + + let (tid, task_inner) = Pthread::pcreate( + core::ptr::null(), + func, + args, + tls, + set_tid, + core::sync::atomic::AtomicU64::from(ctid as u64), + )?; + + // write tid to ptid + if (flags as u32 & ctypes::CLONE_PARENT_SETTID) != 0 { + unsafe { *ptid = tid as c_int }; + } + ruxtask::put_task(task_inner); + + return Ok(tid); + } else if (flags as u32 & ctypes::SIGCHLD) != 0 { + let pid = if let Some(task_ref) = ruxtask::fork_task() { + task_ref.id().as_u64() + } else { + let children_ref = ruxtask::current(); + let tid = children_ref.id().as_u64(); + let thread = Pthread { + inner: children_ref.clone_as_taskref(), + retval: Arc::new(Packet { + result: UnsafeCell::new(core::ptr::null_mut()), + }), + }; + let ptr = Box::into_raw(Box::new(thread)) as *mut c_void; + TID_TO_PTHREAD.write().insert(tid, ForceSendSync(ptr)); + 0 + }; + debug!("will sys_clone <= pid: {pid}"); + return Ok(pid); } else { - core::sync::atomic::AtomicU64::new(0) - }; - - let (tid, task_inner) = Pthread::pcreate( - core::ptr::null(), - func, - args, - tls, - set_tid, - core::sync::atomic::AtomicU64::from(ctid as u64), - )?; - - // write tid to ptid - if (flags as u32 & ctypes::CLONE_PARENT_SETTID) != 0 { - unsafe { *ptid = tid as c_int }; + debug!("ONLY support CLONE_THREAD and SIGCHLD"); + return Err(LinuxError::EINVAL); } - - ruxtask::put_task(task_inner); - - Ok(tid) }) } @@ -359,7 +457,7 @@ pub unsafe fn sys_clone( #[cfg(feature = "musl")] pub fn sys_set_tid_address(tid: usize) -> c_int { syscall_body!(sys_set_tid_address, { - debug!("set_tid_address <= addr: {:#x}", tid); + debug!("set_tid_address <= addr: {tid:#x}"); let id = ruxtask::current().id().as_u64() as c_int; ruxtask::current().as_task_ref().set_child_tid(tid); Ok(id) diff --git a/api/ruxos_posix_api/src/imp/resources.rs b/api/ruxos_posix_api/src/imp/resources.rs index f3e05da73..542668a53 100644 --- a/api/ruxos_posix_api/src/imp/resources.rs +++ b/api/ruxos_posix_api/src/imp/resources.rs @@ -81,9 +81,17 @@ pub unsafe fn sys_setrlimit(resource: c_int, rlimits: *const ctypes::rlimit) -> debug!("sys_setrlimit <= {} {:#x}", resource, rlimits as usize); syscall_body!(sys_setrlimit, { match resource as u32 { + ctypes::RLIMIT_FSIZE => { + warn!("[sys_setrlimit] set RLIMIT_FSIZE do nothing") + } ctypes::RLIMIT_DATA => {} ctypes::RLIMIT_STACK => {} - ctypes::RLIMIT_NOFILE => {} + ctypes::RLIMIT_NOFILE => { + warn!("[sys_setrlimit] set RLIMIT_NOFILE do nothing") + } + ctypes::RLIMIT_NPROC => { + warn!("[sys_setrlimit] set RLIMIT_NPROC do nothing") + } _ => return Err(LinuxError::EINVAL), } // Currently do not support set resources @@ -98,7 +106,7 @@ pub unsafe fn sys_prlimit64( new_limit: *const ctypes::rlimit, old_limit: *mut ctypes::rlimit, ) -> c_int { - debug!("sys_prlimit64 <= resource: {}", resource); + debug!("sys_prlimit64 <= resource: {resource}"); if !new_limit.is_null() { return sys_setrlimit(resource, new_limit); } diff --git a/api/ruxos_posix_api/src/imp/rt_sig.rs b/api/ruxos_posix_api/src/imp/rt_sig.rs index aa7a4ba7f..b24f8dc3c 100644 --- a/api/ruxos_posix_api/src/imp/rt_sig.rs +++ b/api/ruxos_posix_api/src/imp/rt_sig.rs @@ -11,7 +11,10 @@ use axerrno::LinuxError; -use crate::ctypes; +use crate::{ + ctypes::{self, k_sigaction}, + sys_sigaction, +}; use core::{ ffi::c_int, sync::atomic::{AtomicUsize, Ordering}, @@ -48,16 +51,14 @@ fn get_mask(mask: *const usize) -> usize { } /// Set mask for given thread +#[allow(static_mut_refs)] pub fn sys_rt_sigprocmask( how: c_int, _new_mask: *const usize, _old_mask: *mut usize, sigsetsize: usize, ) -> c_int { - debug!( - "sys_rt_sigprocmask <= flag: {}, sigsetsize: {}", - how, sigsetsize - ); + debug!("sys_rt_sigprocmask <= flag: {how}, sigsetsize: {sigsetsize}"); syscall_body!(sys_rt_sigprocmask, { if !_old_mask.is_null() { @@ -84,12 +85,55 @@ pub fn sys_rt_sigprocmask( } /// sigaction syscall for A64 musl -pub fn sys_rt_sigaction( +/// +/// TODO: if sa is 0, return now action +pub unsafe fn sys_rt_sigaction( sig: c_int, - _sa: *const ctypes::sigaction, - _old: *mut ctypes::sigaction, + sa: *const ctypes::sigaction, + old: *mut ctypes::sigaction, _sigsetsize: ctypes::size_t, ) -> c_int { - debug!("sys_rt_sigaction <= sig: {}", sig); - syscall_body!(sys_rt_sigaction, Ok(0)) + debug!( + "sys_rt_sigaction <= sig: {} sa {:x} old {:x}", + sig, sa as u64, old as u64 + ); + syscall_body!(sys_rt_sigaction, { + if sa as u64 == 0 && old as u64 == 0 { + sys_sigaction(sig as _, None, None); + Ok(0) + } else if sa as u64 != 0 && old as u64 == 0 { + let sa = unsafe { *sa }; + let sa = k_sigaction::from(sa); + sys_sigaction(sig as _, Some(&sa), None); + Ok(0) + } else if sa as u64 == 0 && old as u64 != 0 { + let old = unsafe { *old }; + let mut old_sa = k_sigaction::from(old); + sys_sigaction(sig as _, None, Some(&mut old_sa)); + Ok(0) + } else { + let sa = unsafe { *sa }; + let old = unsafe { *old }; + let sa = k_sigaction::from(sa); + let mut old_sa = k_sigaction::from(old); + sys_sigaction(sig as _, Some(&sa), Some(&mut old_sa)); + Ok(0) + } + }) +} + +impl From for k_sigaction { + fn from(sa: ctypes::sigaction) -> Self { + let mut ret = Self { + ..Default::default() + }; + ret.flags = sa.sa_flags as _; + let mask = sa.sa_mask.__bits[0]; // only get the first 64 signals + ret.mask[0] = mask as _; + ret.mask[1] = (mask >> 32) as _; + + ret.handler = unsafe { sa.__sa_handler.sa_handler }; + ret.restorer = sa.sa_restorer; + ret + } } diff --git a/api/ruxos_posix_api/src/imp/signal.rs b/api/ruxos_posix_api/src/imp/signal.rs index 971408dc1..9568edd72 100644 --- a/api/ruxos_posix_api/src/imp/signal.rs +++ b/api/ruxos_posix_api/src/imp/signal.rs @@ -14,7 +14,7 @@ use crate::ctypes::k_sigaction; use crate::ctypes::{self, pid_t}; use axerrno::LinuxError; -use ruxruntime::{rx_sigaction, Signal}; +use ruxtask::{rx_sigaction, Signal}; /// Set signal handler pub fn sys_sigaction( @@ -22,7 +22,7 @@ pub fn sys_sigaction( sigaction: Option<&k_sigaction>, oldact: Option<&mut k_sigaction>, ) -> c_int { - debug!("sys_sigaction <= signum: {}", signum,); + debug!("sys_sigaction <= signum: {signum}",); syscall_body!(sys_sigaction, { Signal::sigaction( signum, @@ -35,7 +35,7 @@ pub fn sys_sigaction( /// Set a timer to send a signal to the current process after a specified time pub unsafe fn sys_setitimer(which: c_int, new: *const ctypes::itimerval) -> c_int { - debug!("sys_setitimer <= which: {}, new: {:p}", which, new); + debug!("sys_setitimer <= which: {which}, new: {new:p}"); syscall_body!(sys_setitimer, { let which = which as usize; let new_interval = Duration::from((*new).it_interval).as_nanos() as u64; @@ -50,10 +50,7 @@ pub unsafe fn sys_setitimer(which: c_int, new: *const ctypes::itimerval) -> c_in /// Get timer to send signal after some time pub unsafe fn sys_getitimer(which: c_int, curr_value: *mut ctypes::itimerval) -> c_int { - debug!( - "sys_getitimer <= which: {}, curr_value: {:p}", - which, curr_value - ); + debug!("sys_getitimer <= which: {which}, curr_value: {curr_value:p}"); syscall_body!(sys_getitimer, { let ddl = Duration::from_nanos(Signal::timer_deadline(which as usize, None).unwrap()); if ddl.as_nanos() == 0 { @@ -82,12 +79,24 @@ pub unsafe fn sys_sigaltstack( _ss: *const core::ffi::c_void, _old_ss: *mut core::ffi::c_void, ) -> c_int { - debug!("sys_sigaltstack <= ss: {:p}, old_ss: {:p}", _ss, _old_ss); + debug!("sys_sigaltstack <= ss: {_ss:p}, old_ss: {_old_ss:p}"); syscall_body!(sys_sigaltstack, Ok(0)) } -/// TODO: send a signal to a process -pub unsafe fn sys_kill(pid: pid_t, sig: c_int) -> c_int { - debug!("sys_kill <= pid {} sig {}", pid, sig); - syscall_body!(sys_kill, Ok(0)) +/// send a signal to a process +pub fn sys_kill(pid: pid_t, sig: c_int) -> c_int { + debug!("sys_kill <= pid {pid} sig {sig}"); + syscall_body!(sys_kill, { + match Signal::signal(sig as _, true) { + None => Err(LinuxError::EINVAL), + Some(_) => Ok(0), + } + }) +} + +/// send a signal to a thread +/// TODO: send to the specified thread. +pub fn sys_tkill(tid: pid_t, sig: c_int) -> c_int { + debug!("sys_tkill <= tid {tid} sig {sig}"); + sys_kill(tid, sig) } diff --git a/api/ruxos_posix_api/src/imp/stat.rs b/api/ruxos_posix_api/src/imp/stat.rs index ecb25edf2..f115872ca 100644 --- a/api/ruxos_posix_api/src/imp/stat.rs +++ b/api/ruxos_posix_api/src/imp/stat.rs @@ -11,11 +11,17 @@ use crate::ctypes::{self, gid_t, pid_t, uid_t}; use core::ffi::c_int; /// Set file mode creation mask -/// -/// TODO: pub fn sys_umask(mode: ctypes::mode_t) -> ctypes::mode_t { - debug!("sys_umask <= mode: {:x}", mode); - syscall_body!(sys_umask, Ok(0)) + debug!("sys_umask <= mode: {mode:#o}"); + + syscall_body!(sys_umask, { + #[cfg(feature = "fd")] + { + Ok(ruxtask::fs::replace_umask(mode as _)) + } + #[cfg(not(feature = "fd"))] + Ok(0) + }) } /// Returns the effective user ID of the calling process @@ -40,24 +46,32 @@ pub fn sys_getgid() -> c_int { /// set current user id pub fn sys_setuid(uid: uid_t) -> c_int { - debug!("sys_setuid: uid {}", uid); + debug!("sys_setuid: uid {uid}"); syscall_body!(sys_setuid, Ok(0)) } /// set current group id pub fn sys_setgid(gid: gid_t) -> c_int { - debug!("sys_setgid: gid {}", gid); + debug!("sys_setgid: gid {gid}"); syscall_body!(sys_setgid, Ok(0)) } /// get process gid pub fn sys_getpgid(pid: pid_t) -> c_int { - debug!("sys_getpgid: getting pgid of pid {} ", pid); + debug!("sys_getpgid: getting pgid of pid {pid} "); syscall_body!(sys_getpgid, Ok(1000)) } /// set process gid pub fn sys_setpgid(pid: pid_t, pgid: pid_t) -> c_int { - debug!("sys_setpgid: pid {}, pgid {} ", pid, pgid); + debug!("sys_setpgid: pid {pid}, pgid {pgid} "); syscall_body!(sys_setpgid, Ok(0)) } + +/// set process sid and create a new session (return 1000) +/// +/// TODO: +pub fn sys_setsid() -> pid_t { + info!("sys_setsid: create a new session"); + syscall_body!(sys_setsid, Ok(1000)) +} diff --git a/api/ruxos_posix_api/src/imp/stdio.rs b/api/ruxos_posix_api/src/imp/stdio.rs index 73af31e31..91b142435 100644 --- a/api/ruxos_posix_api/src/imp/stdio.rs +++ b/api/ruxos_posix_api/src/imp/stdio.rs @@ -7,134 +7,82 @@ * See the Mulan PSL v2 for more details. */ -use axerrno::AxResult; -use axio::{prelude::*, BufReader}; -use axsync::Mutex; +#[cfg(feature = "alloc")] +extern crate alloc; #[cfg(feature = "fd")] use { alloc::sync::Arc, - axerrno::{AxError, LinuxError, LinuxResult}, + axerrno::{LinuxError, LinuxResult}, axio::PollState, core::sync::atomic::{AtomicBool, Ordering}, + ruxfdtable::OpenFlags, + ruxfs::AbsPath, }; -struct StdinRaw; -struct StdoutRaw; - -#[cfg(feature = "alloc")] -extern crate alloc; -#[cfg(feature = "alloc")] -static STDIO_TTY_NAME: lazy_init::LazyInit = lazy_init::LazyInit::new(); -#[cfg(not(feature = "alloc"))] -static STDIO_TTY_NAME: &str = "dummy"; - -fn get_stdio_tty_name() -> &'static str { - #[cfg(feature = "alloc")] - { - if !STDIO_TTY_NAME.is_init() { - let name = ruxhal::get_all_device_names().first().unwrap().clone(); - STDIO_TTY_NAME.init_by(name); - } - } - &STDIO_TTY_NAME -} - -impl Read for StdinRaw { - // Non-blocking read, returns number of bytes read. - fn read(&mut self, buf: &mut [u8]) -> AxResult { - Ok(ruxhal::tty_read(buf, get_stdio_tty_name())) - } -} - -impl Write for StdoutRaw { - fn write(&mut self, buf: &[u8]) -> AxResult { - Ok(ruxhal::tty_write(buf, get_stdio_tty_name())) - } - - fn flush(&mut self) -> AxResult { - Ok(()) - } -} +use axerrno::{AxError, AxResult}; +use axio::prelude::*; +#[derive(Default)] pub struct Stdin { - inner: &'static Mutex>, #[cfg(feature = "fd")] nonblocking: AtomicBool, } +pub struct Stdout; + impl Stdin { - // Block until at least one byte is read. - fn read_blocked(&self, buf: &mut [u8]) -> AxResult { - let read_len = self.inner.lock().read(buf)?; - if buf.is_empty() || read_len > 0 { - return Ok(read_len); - } - // try again until we get something + fn read_inner(&self, buf: &mut [u8]) -> AxResult { loop { - let read_len = self.inner.lock().read(buf)?; - if read_len > 0 { - return Ok(read_len); + #[cfg(not(all(feature = "irq", target_arch = "aarch64")))] + { + // Only the aarch64 architecture implements UART IRQ Handler, which asynchronously + // transmits characters to Tty with higher efficiency. Current implementations for + // x86_64 and riscv architectures lack this capability, requiring polling-based + // reads from the console instead. + while let Some(c) = ruxhal::console::getchar() { + tty::tty_receive_char(c); + } } - crate::sys_sched_yield(); - } - } - - // Attempt a non-blocking read operation. - #[cfg(feature = "fd")] - fn read_nonblocked(&self, buf: &mut [u8]) -> AxResult { - if let Some(mut inner) = self.inner.try_lock() { - let read_len = inner.read(buf)?; - Ok(read_len) - } else { - Err(AxError::WouldBlock) + match tty::tty_read(buf) { + Ok(len) => return Ok(len), + Err(AxError::WouldBlock) => { + #[cfg(feature = "fd")] + if self.nonblocking.load(Ordering::Relaxed) { + return Err(AxError::WouldBlock); + } + crate::sys_sched_yield(); + } + Err(_) => unreachable!(), + }; } } } impl Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> AxResult { - self.read_blocked(buf) + self.read_inner(buf) } } -pub struct Stdout { - inner: &'static Mutex, -} - impl Write for Stdout { fn write(&mut self, buf: &[u8]) -> AxResult { - self.inner.lock().write(buf) + tty::tty_write(buf) } fn flush(&mut self) -> AxResult { - self.inner.lock().flush() - } -} - -/// Constructs a new handle to the standard input of the current process. -pub fn stdin() -> Stdin { - static INSTANCE: Mutex> = Mutex::new(BufReader::new(StdinRaw)); - Stdin { - inner: &INSTANCE, - #[cfg(feature = "fd")] - nonblocking: AtomicBool::from(false), + Ok(()) } } -/// Constructs a new handle to the standard output of the current process. -pub fn stdout() -> Stdout { - static INSTANCE: Mutex = Mutex::new(StdoutRaw); - Stdout { inner: &INSTANCE } -} - #[cfg(feature = "fd")] impl ruxfdtable::FileLike for Stdin { + fn path(&self) -> AbsPath { + AbsPath::new("/dev/stdin") + } + fn read(&self, buf: &mut [u8]) -> LinuxResult { - match self.nonblocking.load(Ordering::Relaxed) { - true => Ok(self.read_nonblocked(buf)?), - false => Ok(self.read_blocked(buf)?), - } + self.read_inner(buf).map_err(|_| LinuxError::EAGAIN) } fn write(&self, _buf: &[u8]) -> LinuxResult { @@ -160,26 +108,47 @@ impl ruxfdtable::FileLike for Stdin { } fn poll(&self) -> LinuxResult { - Ok(PollState { - readable: true, - writable: true, - }) + #[cfg(not(all(feature = "irq", target_arch = "aarch64")))] + { + while let Some(c) = ruxhal::console::getchar() { + tty::tty_receive_char(c); + } + } + Ok(tty::tty_poll()) + } + + fn ioctl(&self, cmd: usize, arg: usize) -> LinuxResult { + tty::tty_ioctl(cmd, arg).map_err(LinuxError::from) } - fn set_nonblocking(&self, nonblocking: bool) -> LinuxResult { - self.nonblocking.store(nonblocking, Ordering::Relaxed); + fn set_flags(&self, flags: OpenFlags) -> LinuxResult { + if flags.contains(OpenFlags::O_NONBLOCK) { + self.nonblocking.store(true, Ordering::Release); + } Ok(()) } + + fn flags(&self) -> OpenFlags { + let mut flags = OpenFlags::O_RDONLY; + if self.nonblocking.load(Ordering::Acquire) { + flags |= OpenFlags::O_NONBLOCK; + } + flags + } } #[cfg(feature = "fd")] impl ruxfdtable::FileLike for Stdout { + fn path(&self) -> AbsPath { + AbsPath::new("/dev/stdout") + } + fn read(&self, _buf: &mut [u8]) -> LinuxResult { Err(LinuxError::EPERM) } fn write(&self, buf: &[u8]) -> LinuxResult { - Ok(self.inner.lock().write(buf)?) + tty::tty_write(buf).map_err(LinuxError::from) } fn flush(&self) -> LinuxResult { @@ -204,10 +173,19 @@ impl ruxfdtable::FileLike for Stdout { Ok(PollState { readable: true, writable: true, + pollhup: false, }) } - fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult { + fn ioctl(&self, cmd: usize, arg: usize) -> LinuxResult { + tty::tty_ioctl(cmd, arg).map_err(LinuxError::from) + } + + fn set_flags(&self, _flags: OpenFlags) -> LinuxResult { Ok(()) } + + fn flags(&self) -> OpenFlags { + OpenFlags::O_WRONLY + } } diff --git a/api/ruxos_posix_api/src/imp/task.rs b/api/ruxos_posix_api/src/imp/task.rs index 2ad8ff009..2238863a5 100644 --- a/api/ruxos_posix_api/src/imp/task.rs +++ b/api/ruxos_posix_api/src/imp/task.rs @@ -9,6 +9,12 @@ use core::ffi::c_int; +#[cfg(feature = "multitask")] +use { + crate::ctypes, + ruxtask::{task::PROCESS_MAP, yield_now}, +}; + /// Relinquish the CPU, and switches to another task. /// /// For single-threaded configuration (`multitask` feature is disabled), we just @@ -28,30 +34,134 @@ pub fn sys_sched_yield() -> c_int { /// Get current thread ID. pub fn sys_gettid() -> c_int { syscall_body!(sys_gettid, - #[cfg(feature = "multitask")] - { - Ok(ruxtask::current().id().as_u64() as c_int) - } - #[cfg(not(feature = "multitask"))] - { + #[cfg(not(feature = "multitask"))]{ Ok(2) // `main` task ID } + #[cfg(feature = "multitask")]{ + Ok(ruxtask::current().id().as_u64() as c_int) + } ) } /// Get current process ID. pub fn sys_getpid() -> c_int { - syscall_body!(sys_getpid, Ok(2)) + #[cfg(not(feature = "multitask"))] + { + syscall_body!(sys_getpid, Ok(1)) + } + + #[cfg(feature = "multitask")] + { + syscall_body!(sys_getpid, Ok(ruxtask::current().id().as_u64() as c_int)) + } } /// Get parent process's ID. pub fn sys_getppid() -> c_int { - syscall_body!(sys_getppid, Ok(1)) + #[cfg(not(feature = "multitask"))] + { + syscall_body!(sys_getppid, Ok(1)) + } + + #[cfg(feature = "multitask")] + { + syscall_body!(sys_getppid, { + if let Some(parent_taskid) = ruxtask::current().parent_process() { + Ok(parent_taskid.id().as_u64() as c_int) + } else { + Ok(0) // `init` process ID + } + }) + } +} + +/// Wait for a child process to exit and return its status. +/// +/// TODO: part of options, and rusage are not implemented yet. +#[cfg(feature = "multitask")] +pub unsafe fn sys_wait4( + pid: c_int, + wstatus: *mut c_int, + options: c_int, + rusage: *mut ctypes::rusage, +) -> c_int { + const WNOHANG: c_int = 0x00000001; + + debug!("sys_wait4 <= pid: {pid}, wstatus: {wstatus:?}, options: {options}, rusage: {rusage:?}"); + + if pid > 0 { + loop { + let mut process_map = PROCESS_MAP.lock(); + if let Some(task) = process_map.get(&(pid as u64)) { + if task.state() == ruxtask::task::TaskState::Exited { + if !wstatus.is_null() { + unsafe { + // lower 8 bits of exit_code is the signal number, while upper 8 bits of exit_code is the exit status + // according to "bits/waitstatus.h" in glibc source code. + // TODO: add signal number to wstatus + wstatus.write(task.exit_code() << 8); + } + } + process_map.remove(&(pid as u64)); + return pid; + } else if options & WNOHANG != 0 { + return 0; // No child process + } + } else { + return -1; // No such process + } + // drop lock before yielding to other tasks + drop(process_map); + // for single-cpu system, we must yield to other tasks instead of dead-looping here. + yield_now(); + } + } else if pid == -1 { + let mut to_remove: Option = None; + while to_remove.is_none() { + let process_map = PROCESS_MAP.lock(); + for (child_pid, task) in process_map + .iter() + .filter(|(_, task)| task.parent_process().is_some()) + { + let parent_pid = task.parent_process().unwrap().id().as_u64(); + if parent_pid == ruxtask::current().id().as_u64() + && task.state() == ruxtask::task::TaskState::Exited + { + // add to to_remove list + if !wstatus.is_null() { + unsafe { + // lower 8 bits of exit_code is the signal number, while upper 8 bits of exit_code is the exit status + // according to "bits/waitstatus.h" in glibc source code. + // TODO: add signal number to wstatus + wstatus.write(task.exit_code() << 8); + } + } + let _ = to_remove.insert(*child_pid); + break; + } + } + if options & WNOHANG != 0 { + return 0; // No child process + } + // drop lock before yielding to other tasks + drop(process_map); + // check if the condition is meet + if to_remove.is_some() { + break; + } + // for single-cpu system, we must yield to other tasks instead of dead-looping here. + yield_now(); + } + PROCESS_MAP.lock().remove(&to_remove.unwrap()); + return to_remove.unwrap() as c_int; + } else { + return -1; // Invalid argument + } } /// Exit current task pub fn sys_exit(exit_code: c_int) -> ! { - debug!("sys_exit <= {}", exit_code); + debug!("sys_exit <= {exit_code}"); #[cfg(feature = "multitask")] ruxtask::exit(exit_code); #[cfg(not(feature = "multitask"))] diff --git a/api/ruxos_posix_api/src/imp/time.rs b/api/ruxos_posix_api/src/imp/time.rs index 2fa61c80e..aab6cd477 100644 --- a/api/ruxos_posix_api/src/imp/time.rs +++ b/api/ruxos_posix_api/src/imp/time.rs @@ -14,6 +14,9 @@ use crate::ctypes; use axerrno::LinuxError; +// nanoseconds per a second +const NANO_PER_SECOND: i64 = 1000000000; + impl From for Duration { fn from(ts: ctypes::timespec) -> Self { Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32) @@ -74,13 +77,75 @@ pub unsafe fn sys_clock_settime(_clk: ctypes::clockid_t, ts: *const ctypes::time }) } +/// Return the resolution (precision) of a specified clock `clk_id`. +/// TODO: Currently we only have one simple clock source in the OS. +/// We ignore `clk_id` and always return a fixed resolution of 1ns. +/// In the future, we may: +/// - Distinguish different clock IDs (e.g., REALTIME vs MONOTONIC). +/// - Return a more realistic resolution based on hardware capabilities. +pub unsafe fn sys_clock_getres(clk_id: ctypes::clockid_t, ts: *mut ctypes::timespec) -> c_int { + syscall_body!(sys_clock_getres, { + if ts.is_null() { + return Err(LinuxError::EFAULT); + } + (*ts).tv_sec = 0; + (*ts).tv_nsec = 1; + debug!("sys_clock_getres: clk_id={clk_id}, returning 0s + 1ns"); + Ok(0) + }) +} + +/// Sleep until some nanoseconds +/// +/// TODO: should be woken by signals, and set errno +/// TODO: deal with flags +pub unsafe fn sys_clock_nanosleep( + _which_clock: ctypes::clockid_t, + _flags: c_int, + req: *const ctypes::timespec, + rem: *mut ctypes::timespec, +) -> c_int { + syscall_body!(sys_clock_nanosleep, { + unsafe { + if req.is_null() || (*req).tv_nsec < 0 || (*req).tv_nsec >= NANO_PER_SECOND { + return Err(LinuxError::EINVAL); + } + } + + let deadline = unsafe { Duration::from(*req) }; + + let now = ruxhal::time::current_time(); + + if now >= deadline { + return Ok(0); + } + + #[cfg(feature = "multitask")] + ruxtask::sleep_until(deadline); + #[cfg(not(feature = "multitask"))] + ruxhal::time::busy_wait_until(deadline); + + let after = ruxhal::time::current_time(); + let actual = after - now; + let due = deadline - now; + + if let Some(diff) = due.checked_sub(actual) { + if !rem.is_null() { + unsafe { (*rem) = diff.into() }; + } + return Err(LinuxError::EINTR); + } + Ok(0) + }) +} + /// Sleep some nanoseconds /// /// TODO: should be woken by signals, and set errno pub unsafe fn sys_nanosleep(req: *const ctypes::timespec, rem: *mut ctypes::timespec) -> c_int { syscall_body!(sys_nanosleep, { unsafe { - if req.is_null() || (*req).tv_nsec < 0 || (*req).tv_nsec > 999999999 { + if req.is_null() || (*req).tv_nsec < 0 || (*req).tv_nsec >= NANO_PER_SECOND { return Err(LinuxError::EINVAL); } } @@ -112,7 +177,7 @@ pub unsafe fn sys_nanosleep(req: *const ctypes::timespec, rem: *mut ctypes::time /// Get time of the day, ignore second parameter pub unsafe fn sys_gettimeofday(ts: *mut ctypes::timespec, flags: c_int) -> c_int { - debug!("sys_gettimeofday <= flags: {}", flags); + debug!("sys_gettimeofday <= flags: {flags}"); unsafe { sys_clock_gettime(0, ts) } } diff --git a/api/ruxos_posix_api/src/lib.rs b/api/ruxos_posix_api/src/lib.rs index 263134c42..8ff6de50e 100644 --- a/api/ruxos_posix_api/src/lib.rs +++ b/api/ruxos_posix_api/src/lib.rs @@ -12,7 +12,6 @@ //! [Ruxos]: https://github.com/syswonder/ruxos #![cfg_attr(all(not(test), not(doc)), no_std)] -#![feature(ip_in_core)] #![feature(doc_cfg)] #![feature(doc_auto_cfg)] #![allow(clippy::missing_safety_doc)] @@ -50,16 +49,18 @@ pub use imp::getrandom::{sys_getrandom, sys_rand, sys_random, sys_srand}; pub use imp::io::{sys_read, sys_readv, sys_write, sys_writev}; pub use imp::prctl::{sys_arch_prctl, sys_prctl}; pub use imp::resources::{sys_getrlimit, sys_prlimit64, sys_setrlimit}; -pub use imp::rt_sig::{sys_rt_sigaction, sys_rt_sigprocmask}; pub use imp::stat::{ sys_getegid, sys_geteuid, sys_getgid, sys_getpgid, sys_getuid, sys_setgid, sys_setpgid, - sys_setuid, sys_umask, + sys_setsid, sys_setuid, sys_umask, }; pub use imp::sys::{sys_sysinfo, sys_uname}; pub use imp::sys_invalid; +#[cfg(feature = "multitask")] +pub use imp::task::sys_wait4; pub use imp::task::{sys_exit, sys_getpid, sys_getppid, sys_gettid, sys_sched_yield}; pub use imp::time::{ - sys_clock_gettime, sys_clock_settime, sys_gettimeofday, sys_nanosleep, sys_times, + sys_clock_getres, sys_clock_gettime, sys_clock_nanosleep, sys_clock_settime, sys_gettimeofday, + sys_nanosleep, sys_times, }; #[cfg(all(feature = "fd", feature = "musl"))] @@ -68,13 +69,14 @@ pub use imp::fd_ops::sys_dup3; pub use imp::fd_ops::{sys_close, sys_dup, sys_dup2, sys_fcntl}; #[cfg(feature = "fs")] pub use imp::fs::{ - sys_chdir, sys_faccessat, sys_fchownat, sys_fdatasync, sys_fstat, sys_fsync, sys_getcwd, - sys_getdents64, sys_lseek, sys_lstat, sys_mkdir, sys_mkdirat, sys_newfstatat, sys_open, - sys_openat, sys_pread64, sys_preadv, sys_pwrite64, sys_readlinkat, sys_rename, sys_renameat, - sys_rmdir, sys_stat, sys_unlink, sys_unlinkat, + sys_chdir, sys_faccessat, sys_fchmodat, sys_fchownat, sys_fdatasync, sys_fstat, sys_fsync, + sys_ftruncate, sys_getcwd, sys_getdents64, sys_lseek, sys_lstat, sys_membarrier, sys_mkdir, + sys_mkdirat, sys_mknodat, sys_mount, sys_newfstatat, sys_open, sys_openat, sys_pread64, + sys_preadv, sys_pwrite64, sys_readlinkat, sys_rename, sys_renameat, sys_rmdir, sys_stat, + sys_umount2, sys_unlink, sys_unlinkat, }; #[cfg(feature = "epoll")] -pub use imp::io_mpx::{sys_epoll_create, sys_epoll_ctl, sys_epoll_pwait, sys_epoll_wait}; +pub use imp::io_mpx::{sys_epoll_create1, sys_epoll_ctl, sys_epoll_pwait, sys_epoll_wait}; #[cfg(feature = "poll")] pub use imp::io_mpx::{sys_poll, sys_ppoll}; #[cfg(feature = "select")] @@ -86,8 +88,8 @@ pub use imp::mmap::{sys_madvise, sys_mmap, sys_mprotect, sys_mremap, sys_msync, #[cfg(feature = "net")] pub use imp::net::{ sys_accept, sys_bind, sys_connect, sys_freeaddrinfo, sys_getaddrinfo, sys_getpeername, - sys_getsockname, sys_listen, sys_recv, sys_recvfrom, sys_send, sys_sendmsg, sys_sendto, - sys_setsockopt, sys_shutdown, sys_socket, + sys_getsockname, sys_getsockopt, sys_listen, sys_recv, sys_recvfrom, sys_recvmsg, sys_send, + sys_sendmsg, sys_sendto, sys_setsockopt, sys_shutdown, sys_socket, sys_socketpair, }; #[cfg(feature = "pipe")] pub use imp::pipe::{sys_pipe, sys_pipe2}; @@ -107,7 +109,11 @@ pub use imp::pthread::{ sys_pthread_setspecific, }; #[cfg(feature = "signal")] -pub use imp::signal::{sys_getitimer, sys_kill, sys_setitimer, sys_sigaction, sys_sigaltstack}; +pub use imp::rt_sig::{sys_rt_sigaction, sys_rt_sigprocmask}; +#[cfg(feature = "signal")] +pub use imp::signal::{ + sys_getitimer, sys_kill, sys_setitimer, sys_sigaction, sys_sigaltstack, sys_tkill, +}; #[cfg(feature = "multitask")] pub use imp::pthread::futex::sys_futex; @@ -116,7 +122,9 @@ pub use imp::pthread::sys_clone; #[cfg(all(feature = "multitask", feature = "musl"))] pub use imp::pthread::sys_set_tid_address; #[cfg(feature = "multitask")] -pub use imp::pthread::{sys_pthread_create, sys_pthread_exit, sys_pthread_join, sys_pthread_self}; +pub use imp::pthread::{ + sys_exit_group, sys_pthread_create, sys_pthread_exit, sys_pthread_join, sys_pthread_self, +}; #[cfg(feature = "fs")] pub use imp::execve::sys_execve; diff --git a/apps/c/busybox/main.c b/apps/c/busybox/main.c index cb479e391..ca33071e0 100644 --- a/apps/c/busybox/main.c +++ b/apps/c/busybox/main.c @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #include #include diff --git a/apps/c/dl/features.txt b/apps/c/dl/features.txt index ed120d4d3..3a5bcfa19 100644 --- a/apps/c/dl/features.txt +++ b/apps/c/dl/features.txt @@ -9,3 +9,4 @@ poll rtc signal virtio-9p +fp_simd diff --git a/apps/c/dl/main.c b/apps/c/dl/main.c index cb479e391..ca33071e0 100644 --- a/apps/c/dl/main.c +++ b/apps/c/dl/main.c @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #include #include diff --git a/apps/c/dl/rootfs/hello.c b/apps/c/dl/rootfs/hello.c index f7180dfaf..b1ed9e8d1 100644 --- a/apps/c/dl/rootfs/hello.c +++ b/apps/c/dl/rootfs/hello.c @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #include extern int add(int, int); diff --git a/apps/c/dl/rootfs/libadd.c b/apps/c/dl/rootfs/libadd.c index 1be20d21b..33aa20f3f 100644 --- a/apps/c/dl/rootfs/libadd.c +++ b/apps/c/dl/rootfs/libadd.c @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #include int add(int a, int b) diff --git a/apps/c/envtest/expect_info.out b/apps/c/envtest/expect_info.out index cd1e5927d..5c11ea248 100644 --- a/apps/c/envtest/expect_info.out +++ b/apps/c/envtest/expect_info.out @@ -11,7 +11,6 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | EXECUTE | FREE) Initialize global memory allocator... - use TLSF allocator. Initialize kernel page table... Initialize platform devices... Primary CPU 0 init OK. diff --git a/apps/c/envtest/main.c b/apps/c/envtest/main.c index 2a48ac60e..b8b0060d1 100644 --- a/apps/c/envtest/main.c +++ b/apps/c/envtest/main.c @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #include #include #include diff --git a/apps/c/filetest/features.txt b/apps/c/filetest/features.txt index d42d543b2..b9b232882 100644 --- a/apps/c/filetest/features.txt +++ b/apps/c/filetest/features.txt @@ -2,3 +2,4 @@ alloc paging fs blkfs +fatfs diff --git a/apps/c/filetest/main.c b/apps/c/filetest/main.c index 86da56493..86ebedf06 100644 --- a/apps/c/filetest/main.c +++ b/apps/c/filetest/main.c @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #include #include #include diff --git a/apps/c/helloworld/features.txt b/apps/c/helloworld/features.txt new file mode 100644 index 000000000..e69de29bb diff --git a/apps/c/httpclient/expect_info.out b/apps/c/httpclient/expect_info.out index 19688d533..a4a92b011 100644 --- a/apps/c/httpclient/expect_info.out +++ b/apps/c/httpclient/expect_info.out @@ -17,7 +17,12 @@ Initialize platform devices... Initialize device drivers... registered a new Net device at .\+: "virtio-net" Initialize network subsystem... - use NIC 0: "virtio-net" + net stack: smoltcp + use NIC: "loopback" +created net interface "loopback": + ether: 00-00-00-00-00-00 + ip: 127.0.0.1/24 + use NIC: "virtio-net" created net interface "eth0": ether: 52-54-00-12-34-56 ip: 10.0.2.15/24 @@ -26,13 +31,12 @@ Primary CPU 0 init OK. Hello, Ruxos C HTTP client! IP: [0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+ HTTP/1.1 200 OK -Server: nginx -Date: -Content-Type: text/plain -Content-Length: -Connection: keep-alive Access-Control-Allow-Origin: * +Alt-Svc: h3=":443"; ma= Cache-Control: no-cache, no-store, must-revalidate +Date: +Content-Length: +Content-Type: text/plain; charset=utf-8 ^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+ -Shutting down... +Shutting down... \ No newline at end of file diff --git a/apps/c/httpclient/features.txt b/apps/c/httpclient/features.txt index 25ca64f38..af0b03c96 100644 --- a/apps/c/httpclient/features.txt +++ b/apps/c/httpclient/features.txt @@ -1,3 +1,5 @@ alloc paging net +poll +rtc diff --git a/apps/c/memtest/expect_trace.out b/apps/c/memtest/expect_trace.out index 3bdabf61c..52e341aa0 100644 --- a/apps/c/memtest/expect_trace.out +++ b/apps/c/memtest/expect_trace.out @@ -1,43 +1,14 @@ -smp = 1 -build_mode = release -log_level = trace - -Primary CPU 0 started, -Found physcial memory regions: - .text (READ | EXECUTE | RESERVED) - .rodata (READ | RESERVED) - .data .tdata .tbss .percpu (READ | WRITE | RESERVED) - .percpu (READ | WRITE | RESERVED) - boot stack (READ | WRITE | RESERVED) - .bss (READ | WRITE | RESERVED) - free memory (READ | WRITE | EXECUTE | FREE) -Initialize global memory allocator... - use TLSF allocator. -initialize global allocator at: \[0x[0-9a-f]\+, 0x[0-9a-f]\+) -Initialize kernel page table... -Initialize platform devices... -map_region(PA:0x[0-9a-f]\+): \[VA:0x[0-9a-f]\+, VA:0x[0-9a-f]\+) -> \[PA:0x[0-9a-f]\+, PA:0x[0-9a-f]\+) MappingFlags(READ | EXECUTE) -map_region(PA:0x[0-9a-f]\+): \[VA:0x[0-9a-f]\+, VA:0x[0-9a-f]\+) -> \[PA:0x[0-9a-f]\+, PA:0x[0-9a-f]\+) MappingFlags(READ) -map_region(PA:0x[0-9a-f]\+): \[VA:0x[0-9a-f]\+, VA:0x[0-9a-f]\+) -> \[PA:0x[0-9a-f]\+, PA:0x[0-9a-f]\+) MappingFlags(READ | WRITE) -map_region(PA:0x[0-9a-f]\+): \[VA:0x[0-9a-f]\+, VA:0x[0-9a-f]\+) -> \[PA:0x[0-9a-f]\+, PA:0x[0-9a-f]\+) MappingFlags(READ | WRITE) -map_region(PA:0x[0-9a-f]\+): \[VA:0x[0-9a-f]\+, VA:0x[0-9a-f]\+) -> \[PA:0x[0-9a-f]\+, PA:0x[0-9a-f]\+) MappingFlags(READ | WRITE) -map_region(PA:0x[0-9a-f]\+): \[VA:0x[0-9a-f]\+, VA:0x[0-9a-f]\+) -> \[PA:0x[0-9a-f]\+, PA:0x[0-9a-f]\+) MappingFlags(READ | WRITE) -map_region(PA:0x[0-9a-f]\+): \[VA:0x[0-9a-f]\+, VA:0x[0-9a-f]\+) -> \[PA:0x[0-9a-f]\+, PA:0x[0-9a-f]\+) MappingFlags(READ | WRITE | DEVICE) -map_region(PA:0x[0-9a-f]\+): \[VA:0x[0-9a-f]\+, VA:0x[0-9a-f]\+) -> \[PA:0x[0-9a-f]\+, PA:0x[0-9a-f]\+) MappingFlags(READ | WRITE) -set page table root: PA:0x[0-9a-f]\+ => PA:0x[0-9a-f]\+ -Primary CPU 0 init OK. Running memory tests... -top of heap=0x[0-9a-f]\{16\} -72(+8)Byte allocated: p=0x[0-9a-f]\{16\} -allocate 8(+8)Byte for 9 times: -allocated addr=0x[0-9a-f]\{16\} -allocated addr=0x[0-9a-f]\{16\} -allocated addr=0x[0-9a-f]\{16\} -allocated addr=0x[0-9a-f]\{16\} -allocated addr=0x[0-9a-f]\{16\} -allocated addr=0x[0-9a-f]\{16\} -allocated addr=0x[0-9a-f]\{16\} -allocated addr=0x[0-9a-f]\{16\} -allocated addr=0x[0-9a-f]\{16\} -Memory tests run OK! -Shutting down... \ No newline at end of file +top of heap= +72(+8)Byte allocated: p= +allocate 8(+8)Byte for 9 +allocated addr= +allocated addr= +allocated addr= +allocated addr= +allocated addr= +allocated addr= +allocated addr= +allocated addr= +allocated addr= +Memory tests run OK! \ No newline at end of file diff --git a/apps/c/pthread/basic/expect_info_smp4_fifo.out b/apps/c/pthread/basic/expect_info_smp4_fifo.out index 3b75dedc8..115be8878 100644 --- a/apps/c/pthread/basic/expect_info_smp4_fifo.out +++ b/apps/c/pthread/basic/expect_info_smp4_fifo.out @@ -15,7 +15,6 @@ Initialize global memory allocator... Initialize kernel page table... Initialize platform devices... Initialize scheduling... - use FIFO scheduler. Pass NULL argument Recieve: Main thread pass message test_create_join: Child thread return message diff --git a/apps/c/pthread/basic/features.txt b/apps/c/pthread/basic/features.txt index 035f5582a..86e9e458a 100644 --- a/apps/c/pthread/basic/features.txt +++ b/apps/c/pthread/basic/features.txt @@ -2,3 +2,4 @@ alloc paging multitask irq +signal diff --git a/apps/c/pthread/parallel/expect_info_smp4_fifo.out b/apps/c/pthread/parallel/expect_info_smp4_fifo.out index b11eb07ca..c0e92224f 100644 --- a/apps/c/pthread/parallel/expect_info_smp4_fifo.out +++ b/apps/c/pthread/parallel/expect_info_smp4_fifo.out @@ -1,28 +1,3 @@ -smp = 4 -build_mode = release -log_level = info - -CPU 0 started -Found physcial memory regions: - .text (READ | EXECUTE | RESERVED) - .rodata (READ | RESERVED) - .data .tdata .tbss .percpu (READ | WRITE | RESERVED) - .percpu (READ | WRITE | RESERVED) - boot stack (READ | WRITE | RESERVED) - .bss (READ | WRITE | RESERVED) - free memory (READ | WRITE | EXECUTE | FREE) -Initialize global memory allocator... -Initialize kernel page table... -Initialize platform devices... -Initialize scheduling... - use FIFO scheduler -CPU 1 started -CPU 1 init OK -CPU 2 started -CPU 2 init OK -CPU 3 started -CPU 3 init OK -CPU 0 init OK part 0: \[0, 125000) part 1: \[125000, 250000) part 2: \[250000, 375000) @@ -56,4 +31,3 @@ part 13 finished part 14 finished part 15 finished (C)Pthread parallel run OK! -Shutting down... diff --git a/apps/c/pthread/parallel/expect_info_smp4_rr.out b/apps/c/pthread/parallel/expect_info_smp4_rr.out index 12efc08ad..c0e92224f 100644 --- a/apps/c/pthread/parallel/expect_info_smp4_rr.out +++ b/apps/c/pthread/parallel/expect_info_smp4_rr.out @@ -1,29 +1,3 @@ -smp = 4 -build_mode = release -log_level = info - -CPU 0 started -Found physcial memory regions: - .text (READ | EXECUTE | RESERVED) - .rodata (READ | RESERVED) - .data .tdata .tbss .percpu (READ | WRITE | RESERVED) - .percpu (READ | WRITE | RESERVED) - boot stack (READ | WRITE | RESERVED) - .bss (READ | WRITE | RESERVED) - free memory (READ | WRITE | EXECUTE | FREE) -Initialize global memory allocator... -Initialize kernel page table... -Initialize platform devices... -Initialize scheduling... - use Round-robin scheduler -CPU 1 started -CPU 1 init OK -CPU 2 started -CPU 2 init OK -CPU 3 started -CPU 3 init OK -Initialize interrupt handlers... -CPU 0 init OK part 0: \[0, 125000) part 1: \[125000, 250000) part 2: \[250000, 375000) @@ -57,4 +31,3 @@ part 13 finished part 14 finished part 15 finished (C)Pthread parallel run OK! -Shutting down... diff --git a/apps/c/pthread/parallel/features.txt b/apps/c/pthread/parallel/features.txt index 6ca9b0c5f..56be374d4 100644 --- a/apps/c/pthread/parallel/features.txt +++ b/apps/c/pthread/parallel/features.txt @@ -1,3 +1,4 @@ alloc paging multitask +signal diff --git a/apps/c/pthread/parallel/test_cmd b/apps/c/pthread/parallel/test_cmd index b0d44ae37..e6d65df42 100644 --- a/apps/c/pthread/parallel/test_cmd +++ b/apps/c/pthread/parallel/test_cmd @@ -1,3 +1,3 @@ -test_one "SMP=4 LOG=info" "expect_info_smp4_fifo.out" -test_one "SMP=4 LOG=info FEATURES=sched_rr" "expect_info_smp4_rr.out" +test_one "SMP=4 LOG=off" "expect_info_smp4_fifo.out" +test_one "SMP=4 LOG=off FEATURES=sched_rr" "expect_info_smp4_rr.out" rm -f $APP/*.o diff --git a/apps/c/pthread/pipe/expect_info_smp4_fifo.out b/apps/c/pthread/pipe/expect_info_smp4_fifo.out index fefc8cc0c..213c1d841 100644 --- a/apps/c/pthread/pipe/expect_info_smp4_fifo.out +++ b/apps/c/pthread/pipe/expect_info_smp4_fifo.out @@ -15,7 +15,6 @@ Initialize global memory allocator... Initialize kernel page table... Initialize platform devices... Initialize scheduling... - use FIFO scheduler. Child thread send message(1) Child thread send message(2) Main thread recieve (1): I am child(1)! diff --git a/apps/c/pthread/pipe/features.txt b/apps/c/pthread/pipe/features.txt index be558434d..1bcdfc264 100644 --- a/apps/c/pthread/pipe/features.txt +++ b/apps/c/pthread/pipe/features.txt @@ -2,3 +2,4 @@ paging alloc multitask pipe +signal diff --git a/apps/c/pthread/sleep/expect_info_smp4_fifo.out b/apps/c/pthread/sleep/expect_info_smp4_fifo.out index 6446cd725..b3b99198f 100644 --- a/apps/c/pthread/sleep/expect_info_smp4_fifo.out +++ b/apps/c/pthread/sleep/expect_info_smp4_fifo.out @@ -15,7 +15,6 @@ Initialize global memory allocator... Initialize kernel page table... Initialize platform devices... Initialize scheduling... - use FIFO scheduler. Initialize interrupt handlers... Hello, main task! main task sleep for 1\.[0-9]\+s diff --git a/apps/c/pthread/sleep/features.txt b/apps/c/pthread/sleep/features.txt index efe7876d3..a80f84ac6 100644 --- a/apps/c/pthread/sleep/features.txt +++ b/apps/c/pthread/sleep/features.txt @@ -2,3 +2,4 @@ paging alloc multitask irq +signal diff --git a/apps/c/pthread/tsd/expect_info_smp4_fifo.out b/apps/c/pthread/tsd/expect_info_smp4_fifo.out index 28018797a..480302205 100644 --- a/apps/c/pthread/tsd/expect_info_smp4_fifo.out +++ b/apps/c/pthread/tsd/expect_info_smp4_fifo.out @@ -15,7 +15,6 @@ Initialize global memory allocator... Initialize kernel page table... Initialize platform devices... Initialize scheduling... - use FIFO scheduler. max_keys = 1024, got No.0 destr_func, *arg = 0x1234 destr_func, *arg = 0x5678 diff --git a/apps/c/pthread/tsd/features.txt b/apps/c/pthread/tsd/features.txt index 035f5582a..86e9e458a 100644 --- a/apps/c/pthread/tsd/features.txt +++ b/apps/c/pthread/tsd/features.txt @@ -2,3 +2,4 @@ alloc paging multitask irq +signal diff --git a/apps/c/sqlite3/features.txt b/apps/c/sqlite3/features.txt index 488f57a29..a04203c5b 100644 --- a/apps/c/sqlite3/features.txt +++ b/apps/c/sqlite3/features.txt @@ -3,3 +3,4 @@ alloc paging fs blkfs +fatfs diff --git a/apps/c/systime/main.c b/apps/c/systime/main.c index 60194917a..3a4e9f51d 100644 --- a/apps/c/systime/main.c +++ b/apps/c/systime/main.c @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #include #include diff --git a/apps/display/draw_map/src/display.rs b/apps/display/draw_map/src/display.rs index a22f561ab..cba80caa1 100644 --- a/apps/display/draw_map/src/display.rs +++ b/apps/display/draw_map/src/display.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + use embedded_graphics::pixelcolor::Rgb888; use embedded_graphics::prelude::{RgbColor, Size}; use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions}; diff --git a/apps/display/draw_map/src/main.rs b/apps/display/draw_map/src/main.rs index 45a2eef88..f4507cc7a 100644 --- a/apps/display/draw_map/src/main.rs +++ b/apps/display/draw_map/src/main.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #![cfg_attr(feature = "axstd", no_std)] #![cfg_attr(feature = "axstd", no_main)] diff --git a/apps/fs/shell/Cargo.toml b/apps/fs/shell/Cargo.toml index 069c77a3b..aa7cc26a5 100644 --- a/apps/fs/shell/Cargo.toml +++ b/apps/fs/shell/Cargo.toml @@ -14,4 +14,4 @@ default = [] axfs_vfs = { path = "../../../crates/axfs_vfs", optional = true } axfs_ramfs = { path = "../../../crates/axfs_ramfs", optional = true } crate_interface = { version = "0.1.1", optional = true } -axstd = { path = "../../../ulib/axstd", features = ["alloc", "fs","blkfs"], optional = true } +axstd = { path = "../../../ulib/axstd", features = ["alloc", "fs", "blkfs"], optional = true } diff --git a/apps/fs/shell/src/cmd.rs b/apps/fs/shell/src/cmd.rs index c240231dd..fb5d61d1c 100644 --- a/apps/fs/shell/src/cmd.rs +++ b/apps/fs/shell/src/cmd.rs @@ -104,10 +104,13 @@ fn do_ls(args: &str) { if print_name { println!("{}:", name); } - let mut entries = fs::read_dir(name)? - .filter_map(|e| e.ok()) - .map(|e| e.file_name()) - .collect::>(); + let mut entries = Vec::new(); + for entry in fs::read_dir(name)? { + match entry { + Ok(entry) => entries.push(entry.file_name()), + Err(e) => return Err(e), + } + } entries.sort(); for entry in entries { diff --git a/crates/allocator/Cargo.toml b/crates/allocator/Cargo.toml index 5c458461a..ec6c1e378 100644 --- a/crates/allocator/Cargo.toml +++ b/crates/allocator/Cargo.toml @@ -22,7 +22,7 @@ buddy = ["dep:buddy_system_allocator"] allocator_api = [] [dependencies] -log = "0.4" +log = { workspace = true } buddy_system_allocator = { version = "0.9", default-features = false, optional = true } slab_allocator = { path = "../slab_allocator", optional = true } rlsf = { version = "0.2", optional = true } diff --git a/crates/allocator/benches/collections.rs b/crates/allocator/benches/collections.rs index 09b9368d5..dfd5766d3 100644 --- a/crates/allocator/benches/collections.rs +++ b/crates/allocator/benches/collections.rs @@ -24,6 +24,8 @@ use self::utils::MemoryPool; const POOL_SIZE: usize = 1024 * 1024 * 128; +// below code is a benchmark, so it shouldn't be created without push. +#[allow(clippy::same_item_push)] fn vec_push(n: usize, alloc: &(impl Allocator + Clone)) { let mut v: Vec = Vec::new_in(alloc.clone()); for _ in 0..n { diff --git a/crates/allocator/src/bitmap.rs b/crates/allocator/src/bitmap.rs index d79ad1de2..3e73f4bc2 100644 --- a/crates/allocator/src/bitmap.rs +++ b/crates/allocator/src/bitmap.rs @@ -45,6 +45,12 @@ impl BitmapPageAllocator { } } +impl Default for BitmapPageAllocator { + fn default() -> Self { + Self::new() + } +} + impl BaseAllocator for BitmapPageAllocator { fn init(&mut self, start: usize, size: usize) { assert!(PAGE_SIZE.is_power_of_two()); diff --git a/crates/allocator/src/buddy.rs b/crates/allocator/src/buddy.rs index 046debaba..6d0a42c45 100644 --- a/crates/allocator/src/buddy.rs +++ b/crates/allocator/src/buddy.rs @@ -33,6 +33,12 @@ impl BuddyByteAllocator { } } +impl Default for BuddyByteAllocator { + fn default() -> Self { + Self::new() + } +} + impl BaseAllocator for BuddyByteAllocator { fn init(&mut self, start: usize, size: usize) { unsafe { self.inner.init(start, size) }; diff --git a/crates/allocator/src/slab.rs b/crates/allocator/src/slab.rs index b6c08f242..15a75d4a5 100644 --- a/crates/allocator/src/slab.rs +++ b/crates/allocator/src/slab.rs @@ -38,6 +38,12 @@ impl SlabByteAllocator { } } +impl Default for SlabByteAllocator { + fn default() -> Self { + Self::new() + } +} + impl BaseAllocator for SlabByteAllocator { fn init(&mut self, start: usize, size: usize) { self.inner = unsafe { Some(Heap::new(start, size)) }; diff --git a/crates/allocator/src/tlsf.rs b/crates/allocator/src/tlsf.rs index 0badb76dd..8781bdac5 100644 --- a/crates/allocator/src/tlsf.rs +++ b/crates/allocator/src/tlsf.rs @@ -37,6 +37,12 @@ impl TlsfByteAllocator { } } +impl Default for TlsfByteAllocator { + fn default() -> Self { + Self::new() + } +} + impl BaseAllocator for TlsfByteAllocator { fn init(&mut self, start: usize, size: usize) { unsafe { diff --git a/crates/allocator/tests/allocator.rs b/crates/allocator/tests/allocator.rs index 9a2b14a1b..61115448a 100644 --- a/crates/allocator/tests/allocator.rs +++ b/crates/allocator/tests/allocator.rs @@ -73,7 +73,7 @@ pub fn test_alignment(n: usize, alloc: &(impl Allocator + Clone)) { let mut rng = rand::thread_rng(); let mut blocks = vec![]; for _ in 0..n { - if rng.gen_ratio(2, 3) || blocks.len() == 0 { + if rng.gen_ratio(2, 3) || blocks.is_empty() { // insert a block let size = ((1 << rng.gen_range(0..16)) as f32 * rng.gen_range(1.0..2.0)).round() as usize; diff --git a/crates/arm_gic/Cargo.toml b/crates/arm_gic/Cargo.toml index ce2f06530..76d3d02f2 100644 --- a/crates/arm_gic/Cargo.toml +++ b/crates/arm_gic/Cargo.toml @@ -9,5 +9,10 @@ homepage = "https://github.com/rcore-os/arceos" repository = "https://github.com/rcore-os/arceos/tree/main/crates/arm_gic" documentation = "https://rcore-os.github.io/arceos/arm_gic/index.html" +[features] +default = [] +gic-v3 = [] + [dependencies] tock-registers = "0.8" +log = "0.4" diff --git a/crates/arm_gic/src/gic_v2.rs b/crates/arm_gic/src/gic_v2.rs index 3aa485295..65a03064b 100644 --- a/crates/arm_gic/src/gic_v2.rs +++ b/crates/arm_gic/src/gic_v2.rs @@ -277,7 +277,7 @@ impl GicCpuInterface { /// This function should be called only once. pub fn init(&self) { // enable GIC0 - self.regs().CTLR.set(1); + self.regs().CTLR.set(0x1); // unmask interrupts at all priority levels self.regs().PMR.set(0xff); } diff --git a/crates/arm_gic/src/gic_v3.rs b/crates/arm_gic/src/gic_v3.rs new file mode 100644 index 000000000..2ee49d563 --- /dev/null +++ b/crates/arm_gic/src/gic_v3.rs @@ -0,0 +1,546 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! Types and definitions for GICv3. +//! +//! The official documentation: + +use core::ptr::NonNull; + +use crate::{TriggerMode, GIC_MAX_IRQ, SPI_RANGE}; +use tock_registers::interfaces::{Readable, Writeable}; +use tock_registers::register_structs; +use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly}; + +/// Reads and returns the value of the given aarch64 system register. +/// use crate::arch::sysreg::write_sysreg; +/// unsafe {write_sysreg!(icc_sgi1r_el1, val);} +/// let intid = unsafe { read_sysreg!(icc_iar1_el1) } as u32; +macro_rules! read_sysreg { + ($name:ident) => { + { + let mut value: u64; + unsafe{::core::arch::asm!( + concat!("mrs {value:x}, ", ::core::stringify!($name)), + value = out(reg) value, + options(nomem, nostack), + );} + value + } + } +} +pub(crate) use read_sysreg; + +/// Writes the given value to the given aarch64 system register. +macro_rules! write_sysreg { + ($name:ident, $value:expr) => { + { + let v: u64 = $value; + unsafe{::core::arch::asm!( + concat!("msr ", ::core::stringify!($name), ", {value:x}"), + value = in(reg) v, + options(nomem, nostack), + )} + } + } +} +pub(crate) use write_sysreg; + +register_structs! { + /// GIC Distributor registers. + #[allow(non_snake_case)] + GicDistributorRegs { + /// Distributor Control Register. + (0x0000 => CTLR: ReadWrite), + /// Interrupt Controller Type Register. + (0x0004 => TYPER: ReadOnly), + /// Distributor Implementer Identification Register. + (0x0008 => IIDR: ReadOnly), + (0x000c => _reserved_0), + /// Interrupt Group Registers. + (0x0080 => IGROUPR: [ReadWrite; 0x20]), + /// Interrupt Set-Enable Registers. + (0x0100 => ISENABLER: [ReadWrite; 0x20]), + /// Interrupt Clear-Enable Registers. + (0x0180 => ICENABLER: [ReadWrite; 0x20]), + /// Interrupt Set-Pending Registers. + (0x0200 => ISPENDR: [ReadWrite; 0x20]), + /// Interrupt Clear-Pending Registers. + (0x0280 => ICPENDR: [ReadWrite; 0x20]), + /// Interrupt Set-Active Registers. + (0x0300 => ISACTIVER: [ReadWrite; 0x20]), + /// Interrupt Clear-Active Registers. + (0x0380 => ICACTIVER: [ReadWrite; 0x20]), + /// Interrupt Priority Registers. + (0x0400 => IPRIORITYR: [ReadWrite; 0x100]), + /// Interrupt Processor Targets Registers. + (0x0800 => ITARGETSR: [ReadWrite; 0x100]), + /// Interrupt Configuration Registers. + (0x0c00 => ICFGR: [ReadWrite; 0x40]), + (0x0d00 => _reserved_1), + /// Non-secure Access Control Registers (GICD_NSACR) + (0x0E00 => NSACR: [ReadWrite; 64]), + (0x0F00 => _reserved_2), + /// Interrupt Routing Registers (GICD_IROUTER) + (0x6100 => IROUTER: [ReadWrite; 988]), + (0x7FE0 => _reserved_3), + /// Chip Status Register (GICD_CHIPSR) + (0xC000 => CHIPSR: ReadWrite), + /// Default Chip Register (GICD_DCHIPR) + (0xC004 => DCHIPR: ReadWrite), + /// Chip Registers (GICD_CHIPRn) + (0xC008 => CHIPR: [ReadWrite; 0x10]), + (0xC088 => _reserved_4), + /// Interrupt Class Registers (GICD_ICLARn) + (0xE008 => ICLAR: [ReadWrite; 0x40]), + /// Interrupt Error Registers (GICD_IERRRn) + (0xE108 => IERRR: [ReadWrite; 0x1e]), + (0xE180 => _reserved_5), + /// Configuration ID Register (GICD_CFGID) + (0xF000 => CFGID: ReadOnly), + (0xF008 => _reserved_6), + /// Peripheral ID Registers + (0xFFD0 => PIDR4: ReadOnly), + (0xFFD4 => PIDR5: ReadOnly), + (0xFFD8 => PIDR6: ReadOnly), + (0xFFDC => PIDR7: ReadOnly), + (0xFFE0 => PIDR0: ReadOnly), + (0xFFE4 => PIDR1: ReadOnly), + (0xFFE8 => PIDR2: ReadOnly), + (0xFFEC => PIDR3: ReadOnly), + /// Component ID Registers + (0xFFF0 => CIDR0: ReadOnly), + (0xFFF4 => CIDR1: ReadOnly), + (0xFFF8 => CIDR2: ReadOnly), + (0xFFFC => CIDR3: ReadOnly), + (0x10000 => @END), + } +} + +register_structs! { + /// GIC Redistributor Registers + #[allow(non_snake_case)] + GICv3RdistLpisIf { + /// Redistributor Control Register (GICR_CTLR) + (0x0000 => CTLR: ReadWrite), + /// Redistributor Implementation Identification Register (GICR_IIDR) + (0x0004 => IIDR: ReadOnly), + /// Interrupt Controller Type Register (GICR_TYPER) + (0x0008 => TYPER: ReadOnly), + (0x0010 => _reserved_0), + /// Power Management Control Register (GICR_WAKER) + (0x0014 => WAKER: ReadWrite), + (0x0018 => _reserved_1), + /// Function Control Register (GICR_FCTLR) + (0x0020 => FCTLR: ReadWrite), + /// Power Register (GICR_PWRR) + (0x0024 => PWRR: ReadWrite), + /// Secure-only Class Register (GICR_CLASS) + (0x0028 => CLASS: ReadWrite), + (0x002C => _reserved_2), + /// Set LPI Register (GICR_SETLPIR) + (0x0040 => SETLPIR: WriteOnly), + /// Clear LPI Register (GICR_CLRLPIR) + (0x0048 => CLRLPIR: WriteOnly), + (0x0050 => _reserved_3), + /// Redistributor Properties Base Address Register (GICR_PROPBASER) + (0x0070 => PROPBASER: ReadWrite), + /// Redistributor LPI Pending Table Base Address Register (GICR_PENDBASER) + (0x0078 => PENDBASER: ReadWrite), + (0x0080 => _reserved_4), + /// Invalidate LPI Register (GICR_INVLPIR) + (0x00A0 => INVLPIR: WriteOnly), + (0x00A8 => _reserved_5), + /// Invalidate All LPI Register (GICR_INVALLR) + (0x00B0 => INVALLR: WriteOnly), + (0x00B8 => _reserved_6), + /// Redistributor Synchronization Register (GICR_SYNCR) + (0x00C0 => SYNCR: ReadOnly), + (0x00C4 => _reserved_7), + /// *eripheral ID Registers (GICR_PIDRn) + (0xFFD0 => PIDR4: ReadOnly), + (0xFFD4 => PIDR5: ReadOnly), + (0xFFD8 => PIDR6: ReadOnly), + (0xFFDC => PIDR7: ReadOnly), + (0xFFE0 => PIDR0: ReadOnly), + (0xFFE4 => PIDR1: ReadOnly), + (0xFFE8 => PIDR2: ReadOnly), + (0xFFEC => PIDR3: ReadOnly), + /// Component ID Registers (GICR_CIDRn) + (0xFFF0 => CIDR0: ReadOnly), + (0xFFF4 => CIDR1: ReadOnly), + (0xFFF8 => CIDR2: ReadOnly), + (0xFFFC => CIDR3: ReadOnly), + (0x10000 => @END), + } +} + +register_structs! { + /// GIC Redistributor Registers + #[allow(non_snake_case)] + GICv3RdistSgisIf { + (0x0000 => _reserved_0), + (0x0080 => IGROUPR0: ReadWrite), + (0x0084 => _reserved_4), + /// Interrupt Set-Enable Register + (0x0100 => ISENABLER0: ReadWrite), + (0x0104 => _reserved_8), + /// Interrupt Clear-Enable Register + (0x0180 => ICENABLER0: ReadWrite), + (0x0184 => _reserved_9), + /// Interrupt Set-Pending Register + (0x0200 => ISPENDR0: ReadWrite), + (0x0204 => _reserved_10), + /// Interrupt Clear-Pending Register + (0x0280 => ICPENDR0: ReadWrite), + (0x0284 => _reserved_11), + /// Interrupt Set-Active Register + (0x0300 => ISACTIVER0: ReadWrite), + (0x0304 => _reserved_12), + /// Interrupt Clear-Active Register + (0x0380 => ICACTIVER0: ReadWrite), + (0x0384 => _reserved_13), + /// Interrupt Priority Registers (multiple entries) + (0x0400 => IPRIORITYR: [ReadWrite; 8]), + (0x0420 => _reserved_14), + /// Interrupt Configuration Register + (0x0C00 => ICFGR: [ReadWrite;2]), + (0x0C08 => _reserved_15), + /// Interrupt Group Modifier Register + (0x0D00 => IGRPMODR0: ReadWrite), + (0x0D04 => _reserved_16), + /// Non-secure Access Control Register + (0x0E00 => NSACR: ReadWrite), + (0x0E04 => _reserved_17), + /// Miscellaneous Status Register + (0xC000 => MISCSTATUSR: ReadOnly), + (0xC004 => _reserved_18), + /// Interrupt Error Valid Register + (0xC008 => IERRVR: ReadOnly), + (0xC00C => _reserved_19), + /// SGI Default Register + (0xC010 => SGIDR: ReadWrite), + (0xC018 => _reserved_20), + /// Configuration ID0 Register + (0xF000 => CFGID0: ReadOnly), + /// Configuration ID1 Register + (0xF004 => CFGID1: ReadOnly), + (0xF008 => _reserved_21), + /// Component ID Registers (GICR_CIDRn) + (0xFFF0 => CIDR0: ReadOnly), + (0xFFF4 => CIDR1: ReadOnly), + (0xFFF8 => CIDR2: ReadOnly), + (0xFFFC => CIDR3: ReadOnly), + (0x10000 => @END), + } +} + +/// The GIC distributor. +/// +/// The Distributor block performs interrupt prioritization and distribution +/// to the CPU interface blocks that connect to the processors in the system. +/// +/// The Distributor provides a programming interface for: +/// - Globally enabling the forwarding of interrupts to the CPU interfaces. +/// - Enabling or disabling each interrupt. +/// - Setting the priority level of each interrupt. +/// - Setting the target processor list of each interrupt. +/// - Setting each peripheral interrupt to be level-sensitive or edge-triggered. +/// - Setting each interrupt as either Group 0 or Group 1. +/// - Forwarding an SGI to one or more target processors. +/// +/// In addition, the Distributor provides: +/// - visibility of the state of each interrupt +/// - a mechanism for software to set or clear the pending state of a peripheral +/// interrupt. +pub struct GicDistributor { + base: NonNull, + max_irqs: usize, +} + +/// The GIC redistributor. +/// +/// The Redistributor block is responsible for managing per-core interrupts, +/// including Software Generated Interrupts (SGIs), Private Peripheral Interrupts (PPIs), +/// and Locality-specific Peripheral Interrupts (LPIs). Each CPU core has its own +/// instance of the Redistributor. +/// +/// The Redistributor provides a programming interface for: +/// - Managing the power state of the Redistributor for each CPU core. +/// - Enabling or disabling SGIs and PPIs. +/// - Setting the priority level of SGIs and PPIs. +/// - Configuring the trigger mode (level-sensitive or edge-triggered) for SGIs and PPIs. +/// - Managing Locality-specific Peripheral Interrupts (LPIs), which are designed for +/// scalable interrupt handling. +/// - Configuring the Redistributor's memory-mapped structures for LPI storage. +/// +/// In addition, the Redistributor provides: +/// - A mechanism to wake up a CPU from power-saving states when an interrupt occurs. +/// - Controls to enable or disable specific interrupts at the core level. +/// - Support for Interrupt Translation Services (ITS) for LPI handling in large systems. +pub struct GicRedistributor { + lpis: NonNull, + sgis: NonNull, +} + +unsafe impl Send for GicDistributor {} +unsafe impl Sync for GicDistributor {} + +unsafe impl Send for GicRedistributor {} +unsafe impl Sync for GicRedistributor {} + +impl GicDistributor { + /// Construct a new GIC distributor instance from the base address. + pub const fn new(base: *mut u8) -> Self { + Self { + base: NonNull::new(base).unwrap().cast(), + max_irqs: GIC_MAX_IRQ, + } + } + + const fn regs(&self) -> &GicDistributorRegs { + unsafe { self.base.as_ref() } + } + + /// The number of implemented CPU interfaces. + pub fn cpu_num(&self) -> usize { + ((self.regs().TYPER.get() as usize >> 5) & 0b111) + 1 + } + + /// The maximum number of interrupts that the GIC supports + pub fn max_irqs(&self) -> usize { + ((self.regs().TYPER.get() as usize & 0b11111) + 1) * 32 + } + + /// Configures the trigger mode for the given interrupt. + pub fn configure_interrupt(&mut self, vector: usize, tm: TriggerMode) { + // Only configurable for SPI interrupts + if vector >= self.max_irqs || vector < SPI_RANGE.start { + return; + } + + // type is encoded with two bits, MSB of the two determine type + // 16 irqs encoded per ICFGR register + let reg_idx = vector >> 4; + let bit_shift = ((vector & 0xf) << 1) + 1; + let mut reg_val = self.regs().ICFGR[reg_idx].get(); + match tm { + TriggerMode::Edge => reg_val |= 1 << bit_shift, + TriggerMode::Level => reg_val &= !(1 << bit_shift), + } + self.regs().ICFGR[reg_idx].set(reg_val); + } + + /// Enables or disables the given interrupt. + pub fn set_enable(&mut self, vector: usize, enable: bool) { + if vector >= self.max_irqs { + return; + } + let reg = vector / 32; + let mask = 1 << (vector % 32); + if enable { + self.regs().ISENABLER[reg].set(mask); + } else { + self.regs().ICENABLER[reg].set(mask); + } + } + + /// Initializes the GIC distributor. + /// + /// It disables all interrupts, sets the target of all SPIs to CPU 0, + /// configures all SPIs to be edge-triggered, and finally enables the GICD. + /// + /// This function should be called only once. + pub fn init(&mut self) { + let mut irq_number: usize; + + // Disable the distributor + //self.disable_dist(); + + // Get GIC redistributor interface + let val: u32 = self.regs().TYPER.get(); + irq_number = ((((val) & 0x1f) + 1) << 5) as usize; + if irq_number > GIC_MAX_IRQ { + irq_number = GIC_MAX_IRQ + 1; + } + + // Configure all SPIs as non-secure Group 1 + for i in (SPI_RANGE.start..irq_number).step_by(32_usize) { + self.regs().IGROUPR[i / 32].set(0xffffffff); + } + + // Route all global SPIs to this CPU + let mpidr: u64 = read_sysreg!(MPIDR_EL1); + let aff = ((mpidr & 0xff00000000) >> 8) | //Affinity AFF3 bit mask + (mpidr & 0x0000ff0000) | // Affinity AFF2 bit mask + (mpidr & 0x000000ff00) | // Affinity AFF1 bit mask + (mpidr & 0x00000000ff); // Affinity AFF0 bit mask + let irouter_val = ((aff << 8) & 0xff00000000) | (aff & 0xffffff); + + for i in SPI_RANGE.start..irq_number { + self.regs().IROUTER[i].set(irouter_val); + } + + // Set all SPI's interrupt type to be level-sensitive + for i in (SPI_RANGE.start..irq_number).step_by(16_usize) { + self.regs().ICFGR[i / 16].set(0); + } + + // Set all SPI's priority to a default value + for i in (SPI_RANGE.start..irq_number).step_by(4_usize) { + self.regs().IPRIORITYR[i / 4].set(0x80808080); + } + + // Deactivate and disable all SPIs + for i in (SPI_RANGE.start..irq_number).step_by(32_usize) { + self.regs().ICACTIVER[i / 32].set(0xffffffff); + self.regs().ICENABLER[i / 32].set(0xffffffff); + } + + // Wait for completion + while self.regs().CTLR.get() & (1 << 31) != 0 {} + + // Enable the distributor + self.regs().CTLR.set((1 << 4) | (1 << 1) | (1 << 0)); + } +} + +impl GicRedistributor { + /// Construct a new GIC redistributor instance from the base address. + pub const fn new(base: *mut u8) -> Self { + Self { + lpis: NonNull::new(base).unwrap().cast(), + sgis: NonNull::new(base.wrapping_add(0x010000)).unwrap().cast(), + } + } + + const fn lpis(&self) -> &GICv3RdistLpisIf { + unsafe { self.lpis.as_ref() } + } + + const fn sgis(&self) -> &GICv3RdistSgisIf { + unsafe { self.sgis.as_ref() } + } + + /// Reads the Interrupt Acknowledge Register. + /// + /// This retrieves the IRQ number of the highest-priority pending interrupt. + pub fn iar(&self) -> u32 { + read_sysreg!(icc_iar1_el1) as u32 + } + + /// Writes to the End of Interrupt Register. + /// + /// Marks the interrupt as handled. + pub fn eoi(&self, iar: u32) { + write_sysreg!(icc_eoir1_el1, iar as u64); + } + + /// Writes to the Deactivate Interrupt Register. + /// + /// Ensures that the interrupt is fully deactivated in the GIC. + pub fn dir(&self, iar: u32) { + write_sysreg!(icc_dir_el1, iar as u64); + } + + /// Handles an interrupt by invoking the provided handler function. + /// + /// # Arguments + /// - `handler`: A function that takes an IRQ number and processes it. + pub fn handle_irq(&mut self, handler: F) + where + F: FnOnce(u32), + { + let iar = self.iar(); + let vector = iar & 0x3ff; + if vector < 1020 { + handler(vector); + self.eoi(iar); + self.dir(iar); + } else { + // spurious + } + } + + /// Configures the trigger mode for the given interrupt. + pub fn configure_interrupt(&mut self, vector: usize, tm: TriggerMode) { + // Only configurable for SPI interrupts + if vector >= 32 { + return; + } + + // type is encoded with two bits, MSB of the two determine type + // 16 irqs encoded per ICFGR register + let reg_idx = vector >> 4; + let bit_shift = ((vector & 0xf) << 1) + 1; + let mut reg_val = self.sgis().ICFGR[reg_idx].get(); + match tm { + TriggerMode::Edge => reg_val |= 1 << bit_shift, + TriggerMode::Level => reg_val &= !(1 << bit_shift), + } + self.sgis().ICFGR[reg_idx].set(reg_val); + } + + /// Enables or disables the given SGI or PPI interrupt. + pub fn set_enable(&mut self, vector: usize, enable: bool) { + if vector >= 32 { + return; + } + let mask = 1 << (vector % 32); + if enable { + self.sgis().ISENABLER0.set(mask); + } else { + self.sgis().ICENABLER0.set(mask); + } + } + + /// Initializes the GIC Redistributor. + /// + /// This function: + /// - Wakes up the Redistributor from power-saving mode. + /// - Sets default interrupt priorities. + /// - Disables all SGIs and PPIs. + /// - Configures the GICv3 CPU interface for handling interrupts. + /// + /// This function should be executed for each CPU before enabling LPIs. + pub fn init(&mut self) { + let waker = self.lpis().WAKER.get(); + self.lpis().WAKER.set(waker & !0x02); + while self.lpis().WAKER.get() & 0x04 != 0 {} + for i in 0..8 { + self.sgis().IPRIORITYR[i].set(0x80808080); + } + + // Disable all SGIs and PPIs + self.sgis().ICACTIVER0.set(0xffffffff); + self.sgis().ICENABLER0.set(0xffff0000); + self.sgis().IGROUPR0.set(0xffffffff); + self.sgis().ISENABLER0.set(0xffff); + + while self.lpis().CTLR.get() & (1 << 31) != 0 {} + + let sre = read_sysreg!(icc_sre_el1); + write_sysreg!(icc_sre_el1, sre | 0x7); + + write_sysreg!(icc_bpr1_el1, 0); + + let _pmr = read_sysreg!(icc_pmr_el1); + write_sysreg!(icc_pmr_el1, 0xff); + // enable GIC0 + let _ctlr = read_sysreg!(icc_ctlr_el1); + write_sysreg!(icc_ctlr_el1, 0x2); + // unmask interrupts at all priority levels + // Enable group 1 irq + let _igrpen = read_sysreg!(icc_igrpen1_el1); + write_sysreg!(icc_igrpen1_el1, 0x1); + + let _cntp = read_sysreg!(CNTP_CTL_EL0); + write_sysreg!(CNTP_CTL_EL0, 1); + } +} diff --git a/crates/arm_gic/src/lib.rs b/crates/arm_gic/src/lib.rs index 5dc5bdc5e..e2dadba5a 100644 --- a/crates/arm_gic/src/lib.rs +++ b/crates/arm_gic/src/lib.rs @@ -11,11 +11,13 @@ //! operations. #![no_std] -#![feature(const_ptr_as_ref)] -#![feature(const_option)] -#![feature(const_nonnull_new)] +#[cfg(target_arch = "aarch64")] +#[cfg(not(feature = "gic-v3"))] pub mod gic_v2; +#[cfg(target_arch = "aarch64")] +#[cfg(feature = "gic-v3")] +pub mod gic_v3; use core::ops::Range; diff --git a/crates/arm_pl011/src/lib.rs b/crates/arm_pl011/src/lib.rs index d442d92c0..f842764e4 100644 --- a/crates/arm_pl011/src/lib.rs +++ b/crates/arm_pl011/src/lib.rs @@ -10,8 +10,5 @@ //! Definitions for PL011 UART. #![no_std] -#![feature(const_ptr_as_ref)] -#![feature(const_option)] -#![feature(const_nonnull_new)] pub mod pl011; diff --git a/crates/axerrno/Cargo.toml b/crates/axerrno/Cargo.toml index e686496e8..1ebb1d138 100644 --- a/crates/axerrno/Cargo.toml +++ b/crates/axerrno/Cargo.toml @@ -10,4 +10,4 @@ repository = "https://github.com/rcore-os/arceos/tree/main/crates/axerrno" documentation = "https://rcore-os.github.io/arceos/axerrno/index.html" [dependencies] -log = "0.4" +log = { workspace = true } diff --git a/crates/axerrno/src/lib.rs b/crates/axerrno/src/lib.rs index 36bf5b7fa..5c9697cb6 100644 --- a/crates/axerrno/src/lib.rs +++ b/crates/axerrno/src/lib.rs @@ -97,6 +97,10 @@ pub enum AxError { /// It is a temporary error code that usually returns when a non_blocking operation /// is not completed, prompting the caller to try again later. InProgress, + /// The function is not implemented. + FunctionNotImplemented, + /// Not a tty device + NoTty, } /// A specialized [`Result`] type with [`AxError`] as the error type. @@ -133,7 +137,6 @@ pub type LinuxResult = Result; /// ax_err_type!(BadAddress, "the address is {}!", "xxx"), /// AxError::BadAddress, /// ); - /// ``` #[macro_export] macro_rules! ax_err_type { @@ -248,6 +251,8 @@ impl AxError { WouldBlock => "Operation would block", WriteZero => "Write zero", InProgress => "non_blocking operation is not completed", + FunctionNotImplemented => "Function not implemented", + NoTty => "not a tty device", } } @@ -263,7 +268,7 @@ impl TryFrom for AxError { #[inline] fn try_from(value: i32) -> Result { if value > 0 && value <= core::mem::variant_count::() as i32 { - Ok(unsafe { core::mem::transmute(value) }) + Ok(unsafe { core::mem::transmute::(value) }) } else { Err(value) } @@ -300,6 +305,8 @@ impl From for LinuxError { UnexpectedEof | WriteZero => LinuxError::EIO, WouldBlock => LinuxError::EAGAIN, InProgress => LinuxError::EINPROGRESS, + FunctionNotImplemented => LinuxError::ENOSYS, + NoTty => LinuxError::ENOTTY, } } } @@ -322,13 +329,13 @@ mod tests { #[test] fn test_try_from() { let max_code = core::mem::variant_count::() as i32; - assert_eq!(max_code, 23); - assert_eq!(max_code, AxError::InProgress.code()); + assert_eq!(max_code, 25); + assert_eq!(max_code, AxError::NoTty.code()); assert_eq!(AxError::AddrInUse.code(), 1); assert_eq!(Ok(AxError::AddrInUse), AxError::try_from(1)); assert_eq!(Ok(AxError::AlreadyExists), AxError::try_from(2)); - assert_eq!(Ok(AxError::InProgress), AxError::try_from(max_code)); + assert_eq!(Ok(AxError::NoTty), AxError::try_from(max_code)); assert_eq!(Err(max_code + 1), AxError::try_from(max_code + 1)); assert_eq!(Err(0), AxError::try_from(0)); assert_eq!(Err(-1), AxError::try_from(-1)); diff --git a/crates/axfs_devfs/Cargo.toml b/crates/axfs_devfs/Cargo.toml index c618c80bb..d3b0531cd 100644 --- a/crates/axfs_devfs/Cargo.toml +++ b/crates/axfs_devfs/Cargo.toml @@ -11,5 +11,11 @@ documentation = "https://rcore-os.github.io/arceos/axfs_devfs/index.html" [dependencies] axfs_vfs = { path = "../axfs_vfs" } -spin = "0.9" -log = "0.4" +spin = { workspace = true } +log = { workspace = true } +axerrno = { path = "../axerrno" } +tty = { path = "../tty" } +ringbuffer = { path = "../ringbuffer" } +spinlock = { path = "../spinlock" } +axio = { path = "../axio" } +ruxconfig = { path = "../../modules/ruxconfig" } diff --git a/crates/axfs_devfs/src/dir.rs b/crates/axfs_devfs/src/dir.rs index 2245fcb49..5b6005637 100644 --- a/crates/axfs_devfs/src/dir.rs +++ b/crates/axfs_devfs/src/dir.rs @@ -9,24 +9,38 @@ use alloc::collections::BTreeMap; use alloc::sync::{Arc, Weak}; -use axfs_vfs::{VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType}; +use axfs_vfs::{ + RelPath, VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, +}; use axfs_vfs::{VfsError, VfsResult}; use spin::RwLock; +use crate::pts::PTS_FS; +use crate::InoAllocator; + /// The directory node in the device filesystem. /// /// It implements [`axfs_vfs::VfsNodeOps`]. pub struct DirNode { + attr: RwLock, parent: RwLock>, children: RwLock>, + ialloc: Weak, } impl DirNode { - pub(super) fn new(parent: Option<&VfsNodeRef>) -> Arc { + pub(super) fn new( + ino: u64, + mode: VfsNodePerm, + parent: Option<&VfsNodeRef>, + ialloc: Weak, + ) -> Arc { let parent = parent.map_or(Weak::::new() as _, Arc::downgrade); Arc::new(Self { + attr: RwLock::new(VfsNodeAttr::new(ino, mode, VfsNodeType::Dir, 4096, 0)), parent: RwLock::new(parent), children: RwLock::new(BTreeMap::new()), + ialloc, }) } @@ -35,9 +49,10 @@ impl DirNode { } /// Create a subdirectory at this directory. - pub fn mkdir(self: &Arc, name: &'static str) -> Arc { + pub fn mkdir(self: &Arc, name: &'static str, mode: VfsNodePerm) -> Arc { let parent = self.clone() as VfsNodeRef; - let node = Self::new(Some(&parent)); + let ino = self.ialloc.upgrade().unwrap().alloc(); + let node = Self::new(ino, mode, Some(&parent), self.ialloc.clone()); self.children.write().insert(name, node.clone()); node } @@ -50,40 +65,54 @@ impl DirNode { impl VfsNodeOps for DirNode { fn get_attr(&self) -> VfsResult { - Ok(VfsNodeAttr::new_dir(4096, 0)) + Ok(*self.attr.read()) + } + + fn set_mode(&self, mode: VfsNodePerm) -> VfsResult { + self.attr.write().set_perm(mode); + Ok(()) } fn parent(&self) -> Option { self.parent.read().upgrade() } - fn lookup(self: Arc, path: &str) -> VfsResult { + fn lookup(self: Arc, path: &RelPath) -> VfsResult { let (name, rest) = split_path(path); - let node = match name { - "" | "." => Ok(self.clone() as VfsNodeRef), - ".." => self.parent().ok_or(VfsError::NotFound), - _ => self - .children + if let Some(rest) = rest { + match name { + ".." => self.parent().ok_or(VfsError::NotFound)?.lookup(&rest), + _ => self + .children + .read() + .get(name) + .cloned() + .ok_or(VfsError::NotFound)? + .lookup(&rest), + } + } else if name.is_empty() { + Ok(self.clone() as VfsNodeRef) + } else if name == ".." { + self.parent().ok_or(VfsError::NotFound) + } else if name == "ptmx" { + Ok(PTS_FS.get().unwrap().ptmx()) + } else { + self.children .read() .get(name) .cloned() - .ok_or(VfsError::NotFound), - }?; - - if let Some(rest) = rest { - node.lookup(rest) - } else { - Ok(node) + .ok_or(VfsError::NotFound) } } fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult { let children = self.children.read(); - let mut children = children.iter().skip(start_idx.max(2) - 2); + let mut children = children.iter().skip(start_idx.max(3) - 3); for (i, ent) in dirents.iter_mut().enumerate() { match i + start_idx { 0 => *ent = VfsDirEntry::new(".", VfsNodeType::Dir), 1 => *ent = VfsDirEntry::new("..", VfsNodeType::Dir), + 2 => *ent = VfsDirEntry::new("ptmx", VfsNodeType::CharDevice), _ => { if let Some((name, node)) = children.next() { *ent = VfsDirEntry::new(name, node.get_attr().unwrap().file_type()); @@ -96,52 +125,50 @@ impl VfsNodeOps for DirNode { Ok(dirents.len()) } - fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult { - log::debug!("create {:?} at devfs: {}", ty, path); + fn create(&self, path: &RelPath, ty: VfsNodeType, mode: VfsNodePerm) -> VfsResult { let (name, rest) = split_path(path); if let Some(rest) = rest { match name { - "" | "." => self.create(rest, ty), - ".." => self.parent().ok_or(VfsError::NotFound)?.create(rest, ty), + ".." => self + .parent() + .ok_or(VfsError::NotFound)? + .create(&rest, ty, mode), _ => self .children .read() .get(name) .ok_or(VfsError::NotFound)? - .create(rest, ty), + .create(&rest, ty, mode), } - } else if name.is_empty() || name == "." || name == ".." { + } else if name.is_empty() || name == ".." { Ok(()) // already exists } else { Err(VfsError::PermissionDenied) // do not support to create nodes dynamically } } - fn remove(&self, path: &str) -> VfsResult { - log::debug!("remove at devfs: {}", path); + fn unlink(&self, path: &RelPath) -> VfsResult { let (name, rest) = split_path(path); if let Some(rest) = rest { match name { - "" | "." => self.remove(rest), - ".." => self.parent().ok_or(VfsError::NotFound)?.remove(rest), + ".." => self.parent().ok_or(VfsError::NotFound)?.unlink(&rest), _ => self .children .read() .get(name) .ok_or(VfsError::NotFound)? - .remove(rest), + .unlink(&rest), } } else { - Err(VfsError::PermissionDenied) // do not support to remove nodes dynamically + Err(VfsError::PermissionDenied) // do not support to unlink nodes dynamically } } axfs_vfs::impl_vfs_dir_default! {} } -fn split_path(path: &str) -> (&str, Option<&str>) { - let trimmed_path = path.trim_start_matches('/'); - trimmed_path.find('/').map_or((trimmed_path, None), |n| { - (&trimmed_path[..n], Some(&trimmed_path[n + 1..])) +pub(crate) fn split_path<'a>(path: &'a RelPath) -> (&'a str, Option>) { + path.find('/').map_or((path, None), |n| { + (&path[..n], Some(RelPath::new(&path[n + 1..]))) }) } diff --git a/crates/axfs_devfs/src/lib.rs b/crates/axfs_devfs/src/lib.rs index 3334fb777..e09cf5625 100644 --- a/crates/axfs_devfs/src/lib.rs +++ b/crates/axfs_devfs/src/lib.rs @@ -20,36 +20,62 @@ mod null; mod random; mod zero; +mod pts; #[cfg(test)] mod tests; pub use self::dir::DirNode; pub use self::null::NullDev; +pub use self::pts::{init_pts, PtsFileSystem}; pub use self::random::RandomDev; pub use self::zero::ZeroDev; use alloc::sync::Arc; -use axfs_vfs::{VfsNodeRef, VfsOps, VfsResult}; +use axfs_vfs::{VfsNodePerm, VfsNodeRef, VfsOps, VfsResult}; +use core::sync::atomic::AtomicU64; use spin::once::Once; +/// An auto-increasing inode number allocator. +pub struct InoAllocator { + current: AtomicU64, +} + +impl InoAllocator { + /// Create a new allocator instance. + pub fn new(start: u64) -> Self { + Self { + current: AtomicU64::new(start), + } + } + + /// Allocate a new inode number. + pub fn alloc(&self) -> u64 { + self.current + .fetch_add(1, core::sync::atomic::Ordering::SeqCst) + } +} + /// A device filesystem that implements [`axfs_vfs::VfsOps`]. pub struct DeviceFileSystem { parent: Once, root: Arc, + _ialloc: Arc, } impl DeviceFileSystem { /// Create a new instance. pub fn new() -> Self { + let ialloc = Arc::new(InoAllocator::new(10)); Self { parent: Once::new(), - root: DirNode::new(None), + root: DirNode::new(2, VfsNodePerm::default_dir(), None, Arc::downgrade(&ialloc)), + _ialloc: ialloc, } } /// Create a subdirectory at the root directory. - pub fn mkdir(&self, name: &'static str) -> Arc { - self.root.mkdir(name) + pub fn mkdir(&self, name: &'static str, mode: VfsNodePerm) -> Arc { + self.root.mkdir(name, mode) } /// Add a node to the root directory. @@ -61,12 +87,8 @@ impl DeviceFileSystem { } impl VfsOps for DeviceFileSystem { - fn mount(&self, _path: &str, mount_point: VfsNodeRef) -> VfsResult { - if let Some(parent) = mount_point.parent() { - self.root.set_parent(Some(self.parent.call_once(|| parent))); - } else { - self.root.set_parent(None); - } + fn mount(&self, parent: VfsNodeRef) -> VfsResult { + self.root.set_parent(Some(self.parent.call_once(|| parent))); Ok(()) } diff --git a/crates/axfs_devfs/src/null.rs b/crates/axfs_devfs/src/null.rs index 183428411..1d7bf5143 100644 --- a/crates/axfs_devfs/src/null.rs +++ b/crates/axfs_devfs/src/null.rs @@ -17,13 +17,16 @@ pub struct NullDev; impl VfsNodeOps for NullDev { fn get_attr(&self) -> VfsResult { Ok(VfsNodeAttr::new( + 3, VfsNodePerm::default_file(), VfsNodeType::CharDevice, 0, 0, )) } - + fn set_mode(&self, _mode: VfsNodePerm) -> VfsResult { + Ok(()) + } fn read_at(&self, _offset: u64, _buf: &mut [u8]) -> VfsResult { Ok(0) } diff --git a/crates/axfs_devfs/src/pts/master.rs b/crates/axfs_devfs/src/pts/master.rs new file mode 100644 index 000000000..109862d0d --- /dev/null +++ b/crates/axfs_devfs/src/pts/master.rs @@ -0,0 +1,179 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ +use core::sync::atomic::{AtomicUsize, Ordering}; + +use alloc::sync::Arc; +use axerrno::{AxError, AxResult}; +use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeType, VfsResult}; +use axio::PollState; +use log::warn; +use ringbuffer::RingBuffer; +use spinlock::SpinNoIrq; +use tty::ioctl::{FromPrimitive, IoctlCommand}; +use tty::ldisc::{Ldisc, WinSize}; +use tty::termios::Termios; + +use super::ptmx::Ptmx; +use super::PTS_PTMX_INO; + +/// Pseudoterminal master device controller +pub struct PtyMaster { + /// Main pseudoterminal device (PTMX) + ptmx: Arc, + /// Index identifier for this pseudoterminal instance + idx: usize, + /// Foreground process group ID + fg_pgid: AtomicUsize, + /// Output buffer with line discipline processing (master → slave) + /// + /// Writen by master and read by slave + pub(in crate::pts) output: Arc, + /// Raw input buffer with spinlock protection (slave → master) + /// + /// Writen by slave and read by master + pub(in crate::pts) input: SpinNoIrq, +} + +impl PtyMaster { + pub fn new(ptmx: Arc, idx: usize) -> Self { + Self { + ptmx, + idx, + output: Ldisc::new(), + input: SpinNoIrq::new(RingBuffer::new(ruxconfig::PTY_BUFFER_CAPACITY)), + fg_pgid: AtomicUsize::new(1000), + } + } + + pub fn idx(&self) -> usize { + self.idx + } + + /// Retrieves current foreground process group ID + pub fn fg_pgid(&self) -> usize { + self.fg_pgid.load(Ordering::Acquire) + } + + /// Updates foreground process group ID with release ordering + pub fn set_fg_pgid(&self, fg_pgid: usize) { + self.fg_pgid.store(fg_pgid, Ordering::Release); + } +} + +impl VfsNodeOps for PtyMaster { + fn get_attr(&self) -> VfsResult { + Ok(VfsNodeAttr::new( + PTS_PTMX_INO, + VfsNodePerm::default_file(), + VfsNodeType::CharDevice, + 0, + 0, + )) + } + + fn set_mode(&self, _mode: VfsNodePerm) -> VfsResult { + Ok(()) + } + + fn release(&self) -> VfsResult { + self.ptmx.ptsfs().remove_pty(self.idx); + Ok(()) + } + + fn read_at(&self, _offset: u64, dst: &mut [u8]) -> VfsResult { + let mut input = self.input.lock(); + if input.is_empty() { + return Err(AxError::WouldBlock); + } + Ok(input.read(dst)) + } + + fn write_at(&self, _offset: u64, src: &[u8]) -> VfsResult { + let mut input = self.input.lock(); + for ch in src { + self.output.push_char(*ch, |content| { + input.write(content.as_bytes()); + }); + } + Ok(src.len()) + } + + fn poll(&self) -> AxResult { + Ok(PollState { + readable: !self.input.lock().is_empty(), + writable: self.output.poll().writable, + // TODO: pollhup + pollhup: false, + }) + } + + fn ioctl(&self, cmd: usize, arg: usize) -> VfsResult { + let ioctl_cmd = IoctlCommand::from_primitive(cmd as u32); + log::debug!("Pty ioctl cmd: {ioctl_cmd:?}"); + match ioctl_cmd { + IoctlCommand::TCGETS | IoctlCommand::TCGETA => { + let termios = self.output.termios(); + unsafe { *(arg as *mut Termios) = termios }; + Ok(0) + } + IoctlCommand::TCSETS | IoctlCommand::TCSETSW => { + let termios = unsafe { *(arg as *const Termios) }; + self.output.set_termios(&termios); + Ok(0) + } + IoctlCommand::TIOCSPTLCK => { + // TODO + Ok(0) + } + IoctlCommand::TIOCGPTPEER => { + todo!(); + } + IoctlCommand::TIOCGPTN => { + unsafe { *(arg as *mut u32) = self.idx as _ }; + Ok(0) + } + IoctlCommand::TIOCGPGRP => { + let fg_pgid = self.fg_pgid() as u32; + unsafe { + *(arg as *mut u32) = fg_pgid; + } + Ok(0) + } + IoctlCommand::TIOCSPGRP => { + let fg_pgid = unsafe { *(arg as *const u32) } as usize; + self.set_fg_pgid(fg_pgid); + Ok(0) + } + IoctlCommand::TCSETSF => { + let termios = unsafe { *(arg as *const Termios) }; + self.output.set_termios(&termios); + self.output.clear_input(); + Ok(0) + } + IoctlCommand::TIOCGWINSZ => { + let winsize = self.output.winsize(); + unsafe { *(arg as *mut WinSize) = winsize }; + Ok(0) + } + IoctlCommand::TIOCSWINSZ => { + let winsize = unsafe { *(arg as *const WinSize) }; + self.output.set_winsize(&winsize); + Ok(0) + } + _ => { + warn!("unimplemented tty ioctl, cmd {ioctl_cmd:?} {cmd:x}"); + Ok(0) + } + } + } + + fn fsync(&self) -> VfsResult { + Ok(()) + } +} diff --git a/crates/axfs_devfs/src/pts/mod.rs b/crates/axfs_devfs/src/pts/mod.rs new file mode 100644 index 000000000..00d20334d --- /dev/null +++ b/crates/axfs_devfs/src/pts/mod.rs @@ -0,0 +1,138 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! A pseudoterminal (Pty) consists of two endpoints: +//! ​Master: Controls the terminal session (handled by PtyMaster) +//! Slave: Emulates physical terminal hardware for programs +use alloc::collections::BinaryHeap; +use alloc::sync::Arc; +use axfs_vfs::{VfsNodeRef, VfsOps, VfsResult}; +use core::cmp::Reverse; +use master::PtyMaster; +use ptmx::Ptmx; +use root::PtsRootInode; +use slave::{PtySlave, PtySlaveInode}; +use spin::{mutex::Mutex, once::Once}; + +mod master; +mod ptmx; +mod root; +mod slave; + +/// A virtual filesystem managing pseudo-terminal (PTY) devices. +/// +/// This filesystem exposes PTY master/slave pairs through device nodes +/// (typically /dev/ptmx for master and /dev/pts/* for slaves). +pub struct PtsFileSystem { + /// Root inode of the PTY filesystem (usually mounted at /dev/pts) + root: Arc, + /// Reusable index allocator for slave devices + idx_allocator: Mutex, +} + +/// Used for allocating index +struct RecycleAllocator { + /// Current max id allocated + current_max_id: usize, + /// Hold deallocated id, will be recycled first when alloc happen + recycled: BinaryHeap>, +} + +impl RecycleAllocator { + /// Create an empty `RecycleAllocator` + fn new(init_val: usize) -> Self { + RecycleAllocator { + current_max_id: init_val, + recycled: BinaryHeap::new(), + } + } + + /// Allocate an id + fn alloc(&mut self) -> usize { + if let Some(Reverse(id)) = self.recycled.pop() { + id + } else { + self.current_max_id += 1; + self.current_max_id - 1 + } + } + + /// Recycle an id + fn dealloc(&mut self, id: usize) { + debug_assert!(id < self.current_max_id); + debug_assert!( + !self.recycled.iter().any(|iid| iid.0 == id), + "id {id} has been deallocated!", + ); + self.recycled.push(Reverse(id)); + } +} + +/// Inode number for root directory +pub(crate) const PTS_ROOT_INO: u64 = 1; +/// Inode number for ptmx character device +pub(crate) const PTS_PTMX_INO: u64 = 2; + +/// Global singleton instance of the PTY filesystem +pub(crate) static PTS_FS: Once> = Once::new(); + +/// Initialize the PTY filesystem and return root inode +pub fn init_pts() -> Arc { + let ptsfs = PtsFileSystem::new(); + let root = ptsfs.root.clone(); + PTS_FS.call_once(|| ptsfs); + root +} + +impl PtsFileSystem { + /// Create a new PTY filesystem instance + pub fn new() -> Arc { + // using cyclic reference pattern to break dependency between filesystem and root inode + Arc::new_cyclic(|weak_fs| Self { + root: PtsRootInode::new(weak_fs.clone()), + idx_allocator: Mutex::new(RecycleAllocator::new(0)), + }) + } + + /// Get the ptmx device (pseudo-terminal master multiplexer) + pub fn ptmx(&self) -> Arc { + self.root.ptmx().clone() + } + + /// Allocate a new PTY master/slave pair + /// 1. Allocates new inode number + /// 2. Creates master device handle + /// 3. Creates associated slave device + /// 4. Registers slave in the filesystem + pub fn allocate_pty(&self) -> Arc { + let idx = self.idx_allocator.lock().alloc(); + let master = Arc::new(PtyMaster::new(self.root.ptmx().clone(), idx as _)); + let slave = Arc::new(PtySlave::new(&master)); + let slave_inode = Arc::new(PtySlaveInode::new(slave)); + self.root.push_slave(idx as _, slave_inode); + master + } + + /// Remove a PTY slave device by index + pub fn remove_pty(&self, idx: usize) { + self.root.remove_slave(idx); + self.idx_allocator.lock().dealloc(idx); + } +} + +impl VfsOps for PtsFileSystem { + fn mount(&self, parent: VfsNodeRef) -> VfsResult { + self.root.set_parent(&parent); + Ok(()) + } + + fn root_dir(&self) -> VfsNodeRef { + self.root.clone() + } +} diff --git a/crates/axfs_devfs/src/pts/ptmx.rs b/crates/axfs_devfs/src/pts/ptmx.rs new file mode 100644 index 000000000..62c98e06b --- /dev/null +++ b/crates/axfs_devfs/src/pts/ptmx.rs @@ -0,0 +1,52 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ +use super::{PtsFileSystem, PTS_PTMX_INO}; +use alloc::sync::{Arc, Weak}; +use axfs_vfs::{ + impl_vfs_non_dir_default, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, + VfsResult, +}; + +/// Pseudo-Terminal Master Multiplexer +pub struct Ptmx { + ptsfs: Weak, +} + +impl Ptmx { + pub fn new(fs: Weak) -> Arc { + Arc::new(Self { ptsfs: fs }) + } + + pub fn ptsfs(&self) -> Arc { + self.ptsfs.upgrade().unwrap() + } +} + +impl VfsNodeOps for Ptmx { + fn open(&self) -> VfsResult> { + let ptsfs = self.ptsfs.upgrade().unwrap(); + Ok(Some(ptsfs.allocate_pty())) + } + + fn get_attr(&self) -> VfsResult { + Ok(VfsNodeAttr::new( + PTS_PTMX_INO, + VfsNodePerm::default_file(), + VfsNodeType::CharDevice, + 0, + 0, + )) + } + + fn set_mode(&self, _mode: VfsNodePerm) -> VfsResult { + Ok(()) + } + + impl_vfs_non_dir_default!(); +} diff --git a/crates/axfs_devfs/src/pts/root.rs b/crates/axfs_devfs/src/pts/root.rs new file mode 100644 index 000000000..ec1e425df --- /dev/null +++ b/crates/axfs_devfs/src/pts/root.rs @@ -0,0 +1,147 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ +use crate::alloc::string::ToString; +use crate::dir::split_path; +use axerrno::ax_err; +use axfs_vfs::{ + impl_vfs_dir_default, RelPath, VfsDirEntry, VfsError, VfsNodeAttr, VfsNodeOps, VfsNodePerm, + VfsNodeRef, VfsNodeType, VfsResult, +}; +use spin::once::Once; +use spin::rwlock::RwLock; + +use super::ptmx::Ptmx; +use super::slave::PtySlaveInode; +use super::{PtsFileSystem, PTS_ROOT_INO}; +use alloc::string::String; +use alloc::sync::{Arc, Weak}; +use alloc::vec::Vec; + +type PtyIndexStr = String; + +/// The root node in pts file system +pub struct PtsRootInode { + parent: Once>, + ptmx: Arc, + slave_inodes: RwLock)>>, +} + +impl PtsRootInode { + pub fn new(fs: Weak) -> Arc { + Arc::new(Self { + parent: Once::new(), + ptmx: Ptmx::new(fs), + slave_inodes: RwLock::new(Vec::new()), + }) + } + + pub fn set_parent(&self, parent: &Arc) { + self.parent.call_once(|| Arc::downgrade(parent)); + } + + pub fn ptmx(&self) -> &Arc { + &self.ptmx + } + + pub fn push_slave(&self, idx: usize, slave: Arc) { + self.slave_inodes.write().push((idx.to_string(), slave)); + } + + pub fn remove_slave(&self, idx: usize) { + let mut slave_inodes = self.slave_inodes.write(); + if let Some(pos) = slave_inodes.iter().position(|(s, _)| s == &idx.to_string()) { + slave_inodes.remove(pos); + } + } +} + +impl VfsNodeOps for PtsRootInode { + fn get_attr(&self) -> VfsResult { + Ok(VfsNodeAttr::new( + PTS_ROOT_INO, + VfsNodePerm::default_file(), + VfsNodeType::Dir, + 0, + 0, + )) + } + + fn set_mode(&self, _mode: VfsNodePerm) -> VfsResult { + Ok(()) + } + + fn parent(&self) -> Option { + self.parent.get().unwrap().upgrade() + } + + fn lookup(self: Arc, path: &RelPath) -> VfsResult { + let (name, rest) = split_path(path); + if let Some(rest) = rest { + match name { + ".." => self.parent().ok_or(VfsError::NotFound)?.lookup(&rest), + _ => Err(VfsError::NotFound), // there is no sub directory + } + } else if name.is_empty() { + Ok(self.clone() as VfsNodeRef) + } else if name == ".." { + self.parent().ok_or(VfsError::NotFound) + } else if name == "ptmx" { + Ok(self.ptmx.clone()) + } else { + for (pty_index, slave_inode) in self.slave_inodes.read().iter() { + if name == pty_index { + return Ok(slave_inode.clone()); + } + } + Err(VfsError::NotFound) + } + } + + fn read_dir( + &self, + start_idx: usize, + dirents: &mut [axfs_vfs::VfsDirEntry], + ) -> VfsResult { + let slave_inodes = self.slave_inodes.read(); + let mut slave_iter = slave_inodes.iter().skip(start_idx.max(3) - 3); + for (i, ent) in dirents.iter_mut().enumerate() { + match i + start_idx { + 0 => *ent = VfsDirEntry::new(".", VfsNodeType::Dir), + 1 => *ent = VfsDirEntry::new("..", VfsNodeType::Dir), + 2 => *ent = VfsDirEntry::new("ptmx", VfsNodeType::CharDevice), + _ => { + if let Some((name, _)) = slave_iter.next() { + *ent = VfsDirEntry::new(name, VfsNodeType::CharDevice); + } else { + return Ok(i); + } + } + } + } + Ok(dirents.len()) + } + + fn create(&self, _path: &RelPath, _ty: axfs_vfs::VfsNodeType, _mode: VfsNodePerm) -> VfsResult { + ax_err!(PermissionDenied) + } + + fn link(&self, _name: &RelPath, _src: Arc) -> VfsResult> { + ax_err!(PermissionDenied) + } + + fn unlink(&self, _path: &RelPath) -> VfsResult { + ax_err!(PermissionDenied) + } + + fn rename(&self, _src_path: &RelPath, _dst_path: &RelPath) -> VfsResult<()> { + ax_err!(PermissionDenied) + } + + impl_vfs_dir_default!(); +} diff --git a/crates/axfs_devfs/src/pts/slave.rs b/crates/axfs_devfs/src/pts/slave.rs new file mode 100644 index 000000000..2bcf0941e --- /dev/null +++ b/crates/axfs_devfs/src/pts/slave.rs @@ -0,0 +1,176 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ +use core::sync::atomic::{AtomicUsize, Ordering}; + +use alloc::sync::{Arc, Weak}; +use axerrno::AxResult; +use axfs_vfs::{ + impl_vfs_non_dir_default, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, + VfsResult, +}; +use axio::PollState; +use log::warn; +use tty::ioctl::{FromPrimitive, IoctlCommand}; + +use super::{master::PtyMaster, PTS_PTMX_INO}; + +pub struct PtySlaveInode { + slave: Arc, +} + +impl PtySlaveInode { + pub fn new(slave: Arc) -> Self { + Self { slave } + } +} + +impl VfsNodeOps for PtySlaveInode { + fn open(&self) -> VfsResult> { + Ok(Some(self.slave.clone())) + } + + fn release(&self) -> VfsResult { + Ok(()) + } + + fn get_attr(&self) -> VfsResult { + Ok(VfsNodeAttr::new( + self.slave.idx() as u64 + PTS_PTMX_INO + 1, + VfsNodePerm::default_file(), + VfsNodeType::CharDevice, + 0, + 0, + )) + } + + fn set_mode(&self, _mode: VfsNodePerm) -> VfsResult { + Ok(()) + } + + fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { + self.slave.read_at(offset, buf) + } + + fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { + self.slave.write_at(offset, buf) + } + impl_vfs_non_dir_default!(); +} + +pub struct PtySlave { + master: Weak, + fg_pgid: AtomicUsize, +} + +impl PtySlave { + pub fn new(master: &Arc) -> Self { + Self { + master: Arc::downgrade(master), + fg_pgid: AtomicUsize::new(1000), + } + } + + pub fn master(&self) -> Arc { + self.master.upgrade().unwrap() + } + + fn fg_pgid(&self) -> usize { + self.fg_pgid.load(Ordering::Acquire) + } + + pub fn set_fg_pgid(&self, fg_pgid: usize) { + self.fg_pgid.store(fg_pgid, Ordering::Release); + } + + pub fn idx(&self) -> usize { + self.master().idx() + } +} + +impl VfsNodeOps for PtySlave { + fn get_attr(&self) -> VfsResult { + Ok(VfsNodeAttr::new( + self.idx() as u64 + PTS_PTMX_INO + 1, + VfsNodePerm::default_file(), + VfsNodeType::CharDevice, + 0, + 0, + )) + } + + fn set_mode(&self, _mode: VfsNodePerm) -> VfsResult { + Ok(()) + } + + fn read_at(&self, _offset: u64, dst: &mut [u8]) -> VfsResult { + self.master().output.read(dst) + } + + fn write_at(&self, _offset: u64, src: &[u8]) -> VfsResult { + let master = self.master(); + let mut buf = master.input.lock(); + for ch in src { + if *ch == b'\n' { + buf.force_enqueue(b'\r'); + buf.force_enqueue(b'\n'); + } else { + buf.force_enqueue(*ch); + } + } + Ok(src.len()) + } + + fn ioctl(&self, cmd: usize, arg: usize) -> VfsResult { + let ioctl_cmd = IoctlCommand::from_primitive(cmd as u32); + match ioctl_cmd { + IoctlCommand::TCGETS + | IoctlCommand::TCSETS + | IoctlCommand::TIOCGPTN + | IoctlCommand::TIOCGWINSZ + | IoctlCommand::TIOCSWINSZ => self.master().ioctl(cmd, arg), + + IoctlCommand::FIONREAD => { + let len = self.master().output.read_buffer_len(); + unsafe { *(arg as *mut u32) = len as u32 }; + Ok(0) + } + IoctlCommand::TIOCGPGRP => { + let fg_pgid = self.fg_pgid() as u32; + unsafe { + *(arg as *mut u32) = fg_pgid; + }; + Ok(0) + } + IoctlCommand::TIOCSPGRP => { + let fg_pgid = unsafe { *(arg as *const u32) } as usize; + self.set_fg_pgid(fg_pgid); + Ok(0) + } + _ => { + warn!("unimplemented tty ioctl, cmd {ioctl_cmd:?} {cmd:x}"); + Ok(0) + } + } + } + + fn poll(&self) -> AxResult { + let master = self.master(); + let readable = master.output.poll().readable; + let writable = master.input.lock().is_full(); + Ok(PollState { + readable, + writable, + pollhup: false, + }) + } + + fn fsync(&self) -> VfsResult { + Ok(()) + } +} diff --git a/crates/axfs_devfs/src/random.rs b/crates/axfs_devfs/src/random.rs index 2bf10fe77..29eb9baf6 100644 --- a/crates/axfs_devfs/src/random.rs +++ b/crates/axfs_devfs/src/random.rs @@ -30,6 +30,7 @@ fn rand_lcg32() -> u32 { impl VfsNodeOps for RandomDev { fn get_attr(&self) -> VfsResult { Ok(VfsNodeAttr::new( + 6, VfsNodePerm::default_file(), VfsNodeType::CharDevice, 0, @@ -37,6 +38,10 @@ impl VfsNodeOps for RandomDev { )) } + fn set_mode(&self, _mode: VfsNodePerm) -> VfsResult { + Ok(()) + } + fn read_at(&self, _offset: u64, buf: &mut [u8]) -> VfsResult { let len = buf.len() >> 2; let remainder = buf.len() & 0x3; diff --git a/crates/axfs_devfs/src/tests.rs b/crates/axfs_devfs/src/tests.rs index 71c8007ad..400db97ea 100644 --- a/crates/axfs_devfs/src/tests.rs +++ b/crates/axfs_devfs/src/tests.rs @@ -9,7 +9,7 @@ use std::sync::Arc; -use axfs_vfs::{VfsError, VfsNodeType, VfsResult}; +use axfs_vfs::{RelPath, VfsError, VfsNodeType, VfsResult}; use crate::*; @@ -21,24 +21,27 @@ fn test_devfs_ops(devfs: &DeviceFileSystem) -> VfsResult { assert!(root.get_attr()?.is_dir()); assert_eq!(root.get_attr()?.file_type(), VfsNodeType::Dir); assert_eq!( - root.clone().lookup("urandom").err(), + root.clone() + .lookup(&RelPath::new_canonicalized("urandom")) + .err(), Some(VfsError::NotFound) ); - assert_eq!( - root.clone().lookup("zero/").err(), - Some(VfsError::NotADirectory) - ); - let node = root.lookup("////null")?; + let node = root.lookup(&RelPath::new_canonicalized("////null"))?; assert_eq!(node.get_attr()?.file_type(), VfsNodeType::CharDevice); assert!(!node.get_attr()?.is_dir()); assert_eq!(node.get_attr()?.size(), 0); assert_eq!(node.read_at(0, &mut buf)?, 0); assert_eq!(buf, [1; N]); assert_eq!(node.write_at(N as _, &buf)?, N); - assert_eq!(node.lookup("/").err(), Some(VfsError::NotADirectory)); + assert_eq!( + node.lookup(&RelPath::new_canonicalized("/")).err(), + Some(VfsError::NotADirectory) + ); - let node = devfs.root_dir().lookup(".///.//././/.////zero")?; + let node = devfs + .root_dir() + .lookup(&RelPath::new_canonicalized(".///.//././/.////zero"))?; assert_eq!(node.get_attr()?.file_type(), VfsNodeType::CharDevice); assert!(!node.get_attr()?.is_dir()); assert_eq!(node.get_attr()?.size(), 0); @@ -46,22 +49,31 @@ fn test_devfs_ops(devfs: &DeviceFileSystem) -> VfsResult { assert_eq!(buf, [0; N]); assert_eq!(node.write_at(0, &buf)?, N); - let foo = devfs.root_dir().lookup(".///.//././/.////foo")?; + let foo = devfs + .root_dir() + .lookup(&RelPath::new_canonicalized(".///.//././/.////foo"))?; assert!(foo.get_attr()?.is_dir()); assert_eq!( foo.read_at(10, &mut buf).err(), Some(VfsError::IsADirectory) ); assert!(Arc::ptr_eq( - &foo.clone().lookup("/f2")?, - &devfs.root_dir().lookup(".//./foo///f2")?, + &foo.clone().lookup(&RelPath::new_canonicalized("/f2"))?, + &devfs + .root_dir() + .lookup(&RelPath::new_canonicalized(".//./foo///f2"))?, )); assert_eq!( - foo.clone().lookup("/bar//f1")?.get_attr()?.file_type(), + foo.clone() + .lookup(&RelPath::new_canonicalized("/bar//f1"))? + .get_attr()? + .file_type(), VfsNodeType::CharDevice ); assert_eq!( - foo.lookup("/bar///")?.get_attr()?.file_type(), + foo.lookup(&RelPath::new_canonicalized("/bar///"))? + .get_attr()? + .file_type(), VfsNodeType::Dir ); @@ -72,29 +84,47 @@ fn test_get_parent(devfs: &DeviceFileSystem) -> VfsResult { let root = devfs.root_dir(); assert!(root.parent().is_none()); - let node = root.clone().lookup("null")?; + let node = root.clone().lookup(&RelPath::new_canonicalized("null"))?; assert!(node.parent().is_none()); - let node = root.clone().lookup(".//foo/bar")?; + let node = root + .clone() + .lookup(&RelPath::new_canonicalized(".//foo/bar"))?; assert!(node.parent().is_some()); let parent = node.parent().unwrap(); - assert!(Arc::ptr_eq(&parent, &root.clone().lookup("foo")?)); - assert!(parent.lookup("bar").is_ok()); + assert!(Arc::ptr_eq( + &parent, + &root.clone().lookup(&RelPath::new_canonicalized("foo"))? + )); + assert!(parent.lookup(&RelPath::new_canonicalized("bar")).is_ok()); - let node = root.clone().lookup("foo/..")?; - assert!(Arc::ptr_eq(&node, &root.clone().lookup(".")?)); + let node = root.clone().lookup(&RelPath::new_canonicalized("foo/.."))?; + assert!(Arc::ptr_eq( + &node, + &root.clone().lookup(&RelPath::new_canonicalized("."))? + )); assert!(Arc::ptr_eq( - &root.clone().lookup("/foo/..")?, - &devfs.root_dir().lookup(".//./foo/././bar/../..")?, + &root + .clone() + .lookup(&RelPath::new_canonicalized("/foo/.."))?, + &devfs + .root_dir() + .lookup(&RelPath::new_canonicalized(".//./foo/././bar/../.."))?, )); assert!(Arc::ptr_eq( - &root.clone().lookup("././/foo//./../foo//bar///..//././")?, - &devfs.root_dir().lookup(".//./foo/")?, + &root.clone().lookup(&RelPath::new_canonicalized( + "././/foo//./../foo//bar///..//././" + ))?, + &devfs + .root_dir() + .lookup(&RelPath::new_canonicalized(".//./foo/"))?, )); assert!(Arc::ptr_eq( - &root.clone().lookup("///foo//bar///../f2")?, - &root.lookup("foo/.//f2")?, + &root + .clone() + .lookup(&RelPath::new_canonicalized("///foo//bar///../f2"))?, + &root.lookup(&RelPath::new_canonicalized("foo/.//f2"))?, )); Ok(()) @@ -114,9 +144,9 @@ fn test_devfs() { devfs.add("null", Arc::new(NullDev)); devfs.add("zero", Arc::new(ZeroDev)); - let dir_foo = devfs.mkdir("foo"); + let dir_foo = devfs.mkdir("foo", VfsNodePerm::default_dir()); dir_foo.add("f2", Arc::new(ZeroDev)); - let dir_bar = dir_foo.mkdir("bar"); + let dir_bar = dir_foo.mkdir("bar", VfsNodePerm::default_dir()); dir_bar.add("f1", Arc::new(NullDev)); test_devfs_ops(&devfs).unwrap(); diff --git a/crates/axfs_devfs/src/zero.rs b/crates/axfs_devfs/src/zero.rs index b4244349a..6eacdb23e 100644 --- a/crates/axfs_devfs/src/zero.rs +++ b/crates/axfs_devfs/src/zero.rs @@ -17,6 +17,7 @@ pub struct ZeroDev; impl VfsNodeOps for ZeroDev { fn get_attr(&self) -> VfsResult { Ok(VfsNodeAttr::new( + 4, VfsNodePerm::default_file(), VfsNodeType::CharDevice, 0, @@ -24,6 +25,10 @@ impl VfsNodeOps for ZeroDev { )) } + fn set_mode(&self, _mode: VfsNodePerm) -> VfsResult { + Ok(()) + } + fn read_at(&self, _offset: u64, buf: &mut [u8]) -> VfsResult { buf.fill(0); Ok(buf.len()) diff --git a/crates/axfs_ramfs/Cargo.toml b/crates/axfs_ramfs/Cargo.toml index 928cc2a78..540077110 100644 --- a/crates/axfs_ramfs/Cargo.toml +++ b/crates/axfs_ramfs/Cargo.toml @@ -11,5 +11,9 @@ documentation = "https://rcore-os.github.io/arceos/axfs_ramfs/index.html" [dependencies] axfs_vfs = { path = "../axfs_vfs" } -spin = "0.9" -log = "0.4" +spin = { workspace = true } +log = { workspace = true } +axerrno = { path = "../axerrno" } +ringbuffer = { path = "../ringbuffer" } +axio = { path = "../axio" } +ruxfifo = { path = "../ruxfifo" } diff --git a/crates/axfs_ramfs/src/dir.rs b/crates/axfs_ramfs/src/dir.rs index e685bd255..898b6c8c9 100644 --- a/crates/axfs_ramfs/src/dir.rs +++ b/crates/axfs_ramfs/src/dir.rs @@ -11,27 +11,40 @@ use alloc::collections::BTreeMap; use alloc::sync::{Arc, Weak}; use alloc::{string::String, vec::Vec}; -use axfs_vfs::{VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType}; +use axfs_vfs::{ + RelPath, VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, +}; use axfs_vfs::{VfsError, VfsResult}; -use spin::RwLock; +use ruxfifo::FifoNode; +use spin::rwlock::RwLock; use crate::file::FileNode; +use crate::InoAllocator; /// The directory node in the RAM filesystem. /// /// It implements [`axfs_vfs::VfsNodeOps`]. pub struct DirNode { + attr: RwLock, this: Weak, parent: RwLock>, children: RwLock>, + ialloc: Weak, } impl DirNode { - pub(super) fn new(parent: Option>) -> Arc { + pub(super) fn new( + ino: u64, + mode: VfsNodePerm, + parent: Option>, + ialloc: Weak, + ) -> Arc { Arc::new_cyclic(|this| Self { + attr: RwLock::new(VfsNodeAttr::new(ino, mode, VfsNodeType::Dir, 4096, 0)), this: this.clone(), parent: RwLock::new(parent.unwrap_or_else(|| Weak::::new())), children: RwLock::new(BTreeMap::new()), + ialloc, }) } @@ -50,14 +63,16 @@ impl DirNode { } /// Creates a new node with the given name and type in this directory. - pub fn create_node(&self, name: &str, ty: VfsNodeType) -> VfsResult { + pub fn create_node(&self, name: &str, ty: VfsNodeType, mode: VfsNodePerm) -> VfsResult { if self.exist(name) { - log::error!("AlreadyExists {}", name); + log::error!("AlreadyExists {name}"); return Err(VfsError::AlreadyExists); } + let ino = self.ialloc.upgrade().unwrap().alloc(); let node: VfsNodeRef = match ty { - VfsNodeType::File => Arc::new(FileNode::new()), - VfsNodeType::Dir => Self::new(Some(self.this.clone())), + VfsNodeType::File => Arc::new(FileNode::new(ino, mode)), + VfsNodeType::Fifo => Arc::new(FifoNode::new(ino, mode)), + VfsNodeType::Dir => Self::new(ino, mode, Some(self.this.clone()), self.ialloc.clone()), _ => return Err(VfsError::Unsupported), }; self.children.write().insert(name.into(), node); @@ -80,30 +95,40 @@ impl DirNode { impl VfsNodeOps for DirNode { fn get_attr(&self) -> VfsResult { - Ok(VfsNodeAttr::new_dir(4096, 0)) + Ok(*self.attr.read()) + } + fn set_mode(&self, mode: VfsNodePerm) -> VfsResult { + self.attr.write().set_perm(mode); + Ok(()) } fn parent(&self) -> Option { self.parent.read().upgrade() } - fn lookup(self: Arc, path: &str) -> VfsResult { + fn lookup(self: Arc, path: &RelPath) -> VfsResult { let (name, rest) = split_path(path); - let node = match name { - "" | "." => Ok(self.clone() as VfsNodeRef), - ".." => self.parent().ok_or(VfsError::NotFound), - _ => self - .children + if let Some(rest) = rest { + match name { + ".." => self.parent().ok_or(VfsError::NotFound)?.lookup(&rest), + _ => self + .children + .read() + .get(name) + .cloned() + .ok_or(VfsError::NotFound)? + .lookup(&rest), + } + } else if name.is_empty() { + Ok(self.clone() as VfsNodeRef) + } else if name == ".." { + self.parent().ok_or(VfsError::NotFound) + } else { + self.children .read() .get(name) .cloned() - .ok_or(VfsError::NotFound), - }?; - - if let Some(rest) = rest { - node.lookup(rest) - } else { - Ok(node) + .ok_or(VfsError::NotFound) } } @@ -126,48 +151,50 @@ impl VfsNodeOps for DirNode { Ok(dirents.len()) } - fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult { - log::debug!("create {:?} at ramfs: {}", ty, path); + fn create(&self, path: &RelPath, ty: VfsNodeType, mode: VfsNodePerm) -> VfsResult { let (name, rest) = split_path(path); if let Some(rest) = rest { match name { - "" | "." => self.create(rest, ty), - ".." => self.parent().ok_or(VfsError::NotFound)?.create(rest, ty), - _ => { - let subdir = self - .children - .read() - .get(name) - .ok_or(VfsError::NotFound)? - .clone(); - subdir.create(rest, ty) - } + ".." => self + .parent() + .ok_or(VfsError::NotFound)? + .create(&rest, ty, mode), + _ => self + .children + .read() + .get(name) + .ok_or(VfsError::NotFound)? + .create(&rest, ty, mode), } - } else if name.is_empty() || name == "." || name == ".." { + } else if name.is_empty() || name == ".." { Ok(()) // already exists } else { - self.create_node(name, ty) + self.create_node(name, ty, mode) } } - fn remove(&self, path: &str) -> VfsResult { - log::debug!("remove at ramfs: {}", path); + fn create_socket_node(&self, name: &RelPath, node: VfsNodeRef) -> VfsResult { + if self.exist(name) { + log::error!("AlreadyExists {name}"); + return Err(VfsError::AlreadyExists); + } + self.children.write().insert(name.as_str().into(), node); + Ok(()) + } + + fn unlink(&self, path: &RelPath) -> VfsResult { let (name, rest) = split_path(path); if let Some(rest) = rest { match name { - "" | "." => self.remove(rest), - ".." => self.parent().ok_or(VfsError::NotFound)?.remove(rest), - _ => { - let subdir = self - .children - .read() - .get(name) - .ok_or(VfsError::NotFound)? - .clone(); - subdir.remove(rest) - } + ".." => self.parent().ok_or(VfsError::NotFound)?.unlink(&rest), + _ => self + .children + .read() + .get(name) + .ok_or(VfsError::NotFound)? + .unlink(&rest), } - } else if name.is_empty() || name == "." || name == ".." { + } else if name.is_empty() || name == ".." { Err(VfsError::InvalidInput) // remove '.' or '.. } else { self.remove_node(name) @@ -177,9 +204,8 @@ impl VfsNodeOps for DirNode { axfs_vfs::impl_vfs_dir_default! {} } -fn split_path(path: &str) -> (&str, Option<&str>) { - let trimmed_path = path.trim_start_matches('/'); - trimmed_path.find('/').map_or((trimmed_path, None), |n| { - (&trimmed_path[..n], Some(&trimmed_path[n + 1..])) +fn split_path<'a>(path: &'a RelPath) -> (&'a str, Option>) { + path.find('/').map_or((path, None), |n| { + (&path[..n], Some(RelPath::new(&path[n + 1..]))) }) } diff --git a/crates/axfs_ramfs/src/file.rs b/crates/axfs_ramfs/src/file.rs index 7464c5d4e..3f9915fba 100644 --- a/crates/axfs_ramfs/src/file.rs +++ b/crates/axfs_ramfs/src/file.rs @@ -8,19 +8,25 @@ */ use alloc::vec::Vec; -use axfs_vfs::{impl_vfs_non_dir_default, VfsNodeAttr, VfsNodeOps, VfsResult}; -use spin::RwLock; +use axfs_vfs::{ + impl_vfs_non_dir_default, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeType, VfsResult, +}; +use spin::rwlock::RwLock; /// The file node in the RAM filesystem. /// /// It implements [`axfs_vfs::VfsNodeOps`]. pub struct FileNode { + ino: u64, + mode: RwLock, content: RwLock>, } impl FileNode { - pub(super) const fn new() -> Self { + pub(super) const fn new(ino: u64, mode: VfsNodePerm) -> Self { Self { + ino, + mode: RwLock::new(mode), content: RwLock::new(Vec::new()), } } @@ -28,7 +34,18 @@ impl FileNode { impl VfsNodeOps for FileNode { fn get_attr(&self) -> VfsResult { - Ok(VfsNodeAttr::new_file(self.content.read().len() as _, 0)) + Ok(VfsNodeAttr::new( + self.ino, + *self.mode.read(), + VfsNodeType::File, + self.content.read().len() as _, + 0, + )) + } + + fn set_mode(&self, mode: VfsNodePerm) -> VfsResult { + *self.mode.write() = mode; + Ok(()) } fn truncate(&self, size: u64) -> VfsResult { diff --git a/crates/axfs_ramfs/src/lib.rs b/crates/axfs_ramfs/src/lib.rs index 45f11b66b..7bb317758 100644 --- a/crates/axfs_ramfs/src/lib.rs +++ b/crates/axfs_ramfs/src/lib.rs @@ -25,21 +25,50 @@ pub use self::dir::DirNode; pub use self::file::FileNode; use alloc::sync::Arc; -use axfs_vfs::{VfsNodeRef, VfsOps, VfsResult}; +use axfs_vfs::{VfsNodePerm, VfsNodeRef, VfsOps, VfsResult}; +use core::sync::atomic::AtomicU64; use spin::once::Once; +/// An auto-increasing inode number allocator. +pub struct InoAllocator { + current: AtomicU64, +} + +impl InoAllocator { + /// Create a new allocator instance. + pub fn new(start: u64) -> Self { + Self { + current: AtomicU64::new(start), + } + } + + /// Allocate a new inode number. + pub fn alloc(&self) -> u64 { + self.current + .fetch_add(1, core::sync::atomic::Ordering::SeqCst) + } +} + /// A RAM filesystem that implements [`axfs_vfs::VfsOps`]. pub struct RamFileSystem { parent: Once, root: Arc, + _ialloc: Arc, } impl RamFileSystem { /// Create a new instance. pub fn new() -> Self { + let ialloc = Arc::new(InoAllocator::new(0)); Self { parent: Once::new(), - root: DirNode::new(None), + root: DirNode::new( + ialloc.alloc(), + VfsNodePerm::default_dir(), + None, + Arc::downgrade(&ialloc), + ), + _ialloc: ialloc, } } @@ -50,12 +79,8 @@ impl RamFileSystem { } impl VfsOps for RamFileSystem { - fn mount(&self, _path: &str, mount_point: VfsNodeRef) -> VfsResult { - if let Some(parent) = mount_point.parent() { - self.root.set_parent(Some(self.parent.call_once(|| parent))); - } else { - self.root.set_parent(None); - } + fn mount(&self, parent: VfsNodeRef) -> VfsResult { + self.root.set_parent(Some(self.parent.call_once(|| parent))); Ok(()) } diff --git a/crates/axfs_ramfs/src/tests.rs b/crates/axfs_ramfs/src/tests.rs index 1e767635e..09bae162b 100644 --- a/crates/axfs_ramfs/src/tests.rs +++ b/crates/axfs_ramfs/src/tests.rs @@ -9,7 +9,7 @@ use std::sync::Arc; -use axfs_vfs::{VfsError, VfsNodeType, VfsResult}; +use axfs_vfs::{RelPath, VfsError, VfsNodePerm, VfsNodeType, VfsResult}; use crate::*; @@ -22,15 +22,13 @@ fn test_ramfs_ops(devfs: &RamFileSystem) -> VfsResult { assert!(root.get_attr()?.is_dir()); assert_eq!(root.get_attr()?.file_type(), VfsNodeType::Dir); assert_eq!( - root.clone().lookup("urandom").err(), + root.clone() + .lookup(&RelPath::new_canonicalized("urandom")) + .err(), Some(VfsError::NotFound) ); - assert_eq!( - root.clone().lookup("f1/").err(), - Some(VfsError::NotADirectory) - ); - let node = root.lookup("////f1")?; + let node = root.lookup(&RelPath::new_canonicalized("////f1"))?; assert_eq!(node.get_attr()?.file_type(), VfsNodeType::File); assert!(!node.get_attr()?.is_dir()); assert_eq!(node.get_attr()?.size(), 0); @@ -41,24 +39,36 @@ fn test_ramfs_ops(devfs: &RamFileSystem) -> VfsResult { assert_eq!(node.read_at(0, &mut buf)?, N); assert_eq!(buf[..N_HALF], [0; N_HALF]); assert_eq!(buf[N_HALF..], [1; N_HALF]); - assert_eq!(node.lookup("/").err(), Some(VfsError::NotADirectory)); + assert_eq!( + node.lookup(&RelPath::new_canonicalized("/")).err(), + Some(VfsError::NotADirectory) + ); - let foo = devfs.root_dir().lookup(".///.//././/.////foo")?; + let foo = devfs + .root_dir() + .lookup(&RelPath::new_canonicalized(".///.//././/.////foo"))?; assert!(foo.get_attr()?.is_dir()); assert_eq!( foo.read_at(10, &mut buf).err(), Some(VfsError::IsADirectory) ); assert!(Arc::ptr_eq( - &foo.clone().lookup("/f3")?, - &devfs.root_dir().lookup(".//./foo///f3")?, + &foo.clone().lookup(&RelPath::new_canonicalized("/f3"))?, + &devfs + .root_dir() + .lookup(&RelPath::new_canonicalized(".//./foo///f3"))?, )); assert_eq!( - foo.clone().lookup("/bar//f4")?.get_attr()?.file_type(), + foo.clone() + .lookup(&RelPath::new_canonicalized("/bar//f4"))? + .get_attr()? + .file_type(), VfsNodeType::File ); assert_eq!( - foo.lookup("/bar///")?.get_attr()?.file_type(), + foo.lookup(&RelPath::new_canonicalized("/bar///"))? + .get_attr()? + .file_type(), VfsNodeType::Dir ); @@ -69,29 +79,47 @@ fn test_get_parent(devfs: &RamFileSystem) -> VfsResult { let root = devfs.root_dir(); assert!(root.parent().is_none()); - let node = root.clone().lookup("f1")?; + let node = root.clone().lookup(&RelPath::new_canonicalized("f1"))?; assert!(node.parent().is_none()); - let node = root.clone().lookup(".//foo/bar")?; + let node = root + .clone() + .lookup(&RelPath::new_canonicalized(".//foo/bar"))?; assert!(node.parent().is_some()); let parent = node.parent().unwrap(); - assert!(Arc::ptr_eq(&parent, &root.clone().lookup("foo")?)); - assert!(parent.lookup("bar").is_ok()); + assert!(Arc::ptr_eq( + &parent, + &root.clone().lookup(&RelPath::new_canonicalized("foo"))? + )); + assert!(parent.lookup(&RelPath::new_canonicalized("bar")).is_ok()); - let node = root.clone().lookup("foo/..")?; - assert!(Arc::ptr_eq(&node, &root.clone().lookup(".")?)); + let node = root.clone().lookup(&RelPath::new_canonicalized("foo/.."))?; + assert!(Arc::ptr_eq( + &node, + &root.clone().lookup(&RelPath::new_canonicalized("."))? + )); assert!(Arc::ptr_eq( - &root.clone().lookup("/foo/..")?, - &devfs.root_dir().lookup(".//./foo/././bar/../..")?, + &root + .clone() + .lookup(&RelPath::new_canonicalized("/foo/.."))?, + &devfs + .root_dir() + .lookup(&RelPath::new_canonicalized(".//./foo/././bar/../.."))?, )); assert!(Arc::ptr_eq( - &root.clone().lookup("././/foo//./../foo//bar///..//././")?, - &devfs.root_dir().lookup(".//./foo/")?, + &root.clone().lookup(&RelPath::new_canonicalized( + "././/foo//./../foo//bar///..//././" + ))?, + &devfs + .root_dir() + .lookup(&RelPath::new_canonicalized(".//./foo/"))?, )); assert!(Arc::ptr_eq( - &root.clone().lookup("///foo//bar///../f3")?, - &root.lookup("foo/.//f3")?, + &root + .clone() + .lookup(&RelPath::new_canonicalized("///foo//bar///../f3"))?, + &root.lookup(&RelPath::new_canonicalized("foo/.//f3"))?, )); Ok(()) @@ -109,16 +137,49 @@ fn test_ramfs() { let ramfs = RamFileSystem::new(); let root = ramfs.root_dir(); - root.create("f1", VfsNodeType::File).unwrap(); - root.create("f2", VfsNodeType::File).unwrap(); - root.create("foo", VfsNodeType::Dir).unwrap(); - - let dir_foo = root.lookup("foo").unwrap(); - dir_foo.create("f3", VfsNodeType::File).unwrap(); - dir_foo.create("bar", VfsNodeType::Dir).unwrap(); - - let dir_bar = dir_foo.lookup("bar").unwrap(); - dir_bar.create("f4", VfsNodeType::File).unwrap(); + root.create( + &RelPath::new_canonicalized("f1"), + VfsNodeType::File, + VfsNodePerm::default_file(), + ) + .unwrap(); + root.create( + &RelPath::new_canonicalized("f2"), + VfsNodeType::File, + VfsNodePerm::default_file(), + ) + .unwrap(); + root.create( + &RelPath::new_canonicalized("foo"), + VfsNodeType::Dir, + VfsNodePerm::default_file(), + ) + .unwrap(); + + let dir_foo = root.lookup(&RelPath::new_canonicalized("foo")).unwrap(); + dir_foo + .create( + &RelPath::new_canonicalized("f3"), + VfsNodeType::File, + VfsNodePerm::default_file(), + ) + .unwrap(); + dir_foo + .create( + &RelPath::new_canonicalized("bar"), + VfsNodeType::Dir, + VfsNodePerm::default_dir(), + ) + .unwrap(); + + let dir_bar = dir_foo.lookup(&RelPath::new_canonicalized("bar")).unwrap(); + dir_bar + .create( + &RelPath::new_canonicalized("f4"), + VfsNodeType::File, + VfsNodePerm::default_file(), + ) + .unwrap(); let mut entries = ramfs.root_dir_node().get_entries(); entries.sort(); @@ -128,18 +189,33 @@ fn test_ramfs() { test_get_parent(&ramfs).unwrap(); let root = ramfs.root_dir(); - assert_eq!(root.remove("f1"), Ok(())); - assert_eq!(root.remove("//f2"), Ok(())); - assert_eq!(root.remove("f3").err(), Some(VfsError::NotFound)); - assert_eq!(root.remove("foo").err(), Some(VfsError::DirectoryNotEmpty)); - assert_eq!(root.remove("foo/..").err(), Some(VfsError::InvalidInput)); + assert_eq!(root.unlink(&RelPath::new_canonicalized("f1")), Ok(())); + assert_eq!(root.unlink(&RelPath::new_canonicalized("//f2")), Ok(())); assert_eq!( - root.remove("foo/./bar").err(), + root.unlink(&RelPath::new_canonicalized("f3")).err(), + Some(VfsError::NotFound) + ); + assert_eq!( + root.unlink(&RelPath::new_canonicalized("foo")).err(), + Some(VfsError::DirectoryNotEmpty) + ); + assert_eq!( + root.unlink(&RelPath::new_canonicalized("foo/..")).err(), + Some(VfsError::InvalidInput) + ); + assert_eq!( + root.unlink(&RelPath::new_canonicalized("foo/./bar")).err(), Some(VfsError::DirectoryNotEmpty) ); - assert_eq!(root.remove("foo/bar/f4"), Ok(())); - assert_eq!(root.remove("foo/bar"), Ok(())); - assert_eq!(root.remove("./foo//.//f3"), Ok(())); - assert_eq!(root.remove("./foo"), Ok(())); + assert_eq!( + root.unlink(&RelPath::new_canonicalized("foo/bar/f4")), + Ok(()) + ); + assert_eq!(root.unlink(&RelPath::new_canonicalized("foo/bar")), Ok(())); + assert_eq!( + root.unlink(&RelPath::new_canonicalized("./foo//.//f3")), + Ok(()) + ); + assert_eq!(root.unlink(&RelPath::new_canonicalized("./foo")), Ok(())); assert!(ramfs.root_dir_node().get_entries().is_empty()); } diff --git a/crates/axfs_vfs/Cargo.toml b/crates/axfs_vfs/Cargo.toml index 319dd8a8a..1493eda61 100644 --- a/crates/axfs_vfs/Cargo.toml +++ b/crates/axfs_vfs/Cargo.toml @@ -13,6 +13,9 @@ documentation = "https://rcore-os.github.io/arceos/axfs_vfs/index.html" default = [] [dependencies] -log = "0.4" -bitflags = "2.2" +log = { workspace = true } +spin = { workspace = true } +bitflags = { workspace = true } axerrno = { path = "../axerrno" } +lazy_static = { version = "1.4", features = ["spin_no_std"] } +axio = { path = "../axio" } diff --git a/crates/axfs_vfs/src/lib.rs b/crates/axfs_vfs/src/lib.rs index cc0f87870..32950e92e 100644 --- a/crates/axfs_vfs/src/lib.rs +++ b/crates/axfs_vfs/src/lib.rs @@ -38,8 +38,10 @@ //! | [`parent()`](VfsNodeOps::parent) | Get the parent directory | directory | //! | [`lookup()`](VfsNodeOps::lookup) | Lookup the node with the given path | directory | //! | [`create()`](VfsNodeOps::create) | Create a new node with the given path | directory | -//! | [`remove()`](VfsNodeOps::remove) | Remove the node with the given path | directory | +//! | [`link()`](VfsNodeOps::link) | Create a hard link with the given path | directory | +//! | [`unlink()`](VfsNodeOps::unlink) | Remove the node with the given path | directory | //! | [`read_dir()`](VfsNodeOps::read_dir) | Read directory entries | directory | +//! | [`is_empty()`](VfsNodeOps::is_empty) | Check if the directory is empty | directory | //! //! [inodes]: https://en.wikipedia.org/wiki/Inode @@ -48,13 +50,16 @@ extern crate alloc; mod macros; +mod path; mod structs; -pub mod path; +use core::any::Any; use alloc::sync::Arc; use axerrno::{ax_err, AxError, AxResult}; +use axio::PollState; +pub use self::path::{AbsPath, RelPath}; pub use self::structs::{FileSystemInfo, VfsDirEntry, VfsNodeAttr, VfsNodePerm, VfsNodeType}; /// A wrapper of [`Arc`]. @@ -69,7 +74,7 @@ pub type VfsResult = AxResult; /// Filesystem operations. pub trait VfsOps: Send + Sync { /// Do something when the filesystem is mounted. - fn mount(&self, _path: &str, _mount_point: VfsNodeRef) -> VfsResult { + fn mount(&self, _parent: VfsNodeRef) -> VfsResult { Ok(()) } @@ -92,11 +97,12 @@ pub trait VfsOps: Send + Sync { fn root_dir(&self) -> VfsNodeRef; } -/// Node (file/directory) operations. +/// Node (file/directory/lib) operations. pub trait VfsNodeOps: Send + Sync { /// Do something when the node is opened. - fn open(&self) -> VfsResult { - Ok(()) + /// For example, open some special nodes like `/dev/ptmx` should return a new node named `PtyMaster` + fn open(&self) -> VfsResult> { + Ok(None) } /// Do something when the node is closed. @@ -106,19 +112,29 @@ pub trait VfsNodeOps: Send + Sync { /// Get the attributes of the node. fn get_attr(&self) -> VfsResult { - ax_err!(Unsupported) + ax_err!(Unsupported, "get_attr method is unsupported") + } + + /// Set the mode of the node. + fn set_mode(&self, _mode: VfsNodePerm) -> VfsResult { + ax_err!(Unsupported, "set_attr method is unsupported") + } + + /// Get the inode number of the node. + fn get_inode(&self) -> Option { + None } // file operations: /// Read data from the file at the given offset. fn read_at(&self, _offset: u64, _buf: &mut [u8]) -> VfsResult { - ax_err!(InvalidInput) + ax_err!(InvalidInput, "read_at method InvalidInput") } /// Write data to the file at the given offset. fn write_at(&self, _offset: u64, _buf: &[u8]) -> VfsResult { - ax_err!(InvalidInput) + ax_err!(InvalidInput, "write_at method InvalidInput") } /// Flush the file, synchronize the data to disk. @@ -143,30 +159,71 @@ pub trait VfsNodeOps: Send + Sync { /// Lookup the node with given `path` in the directory. /// /// Return the node if found. - fn lookup(self: Arc, _path: &str) -> VfsResult { - ax_err!(Unsupported) + fn lookup(self: Arc, path: &RelPath) -> VfsResult { + ax_err!(Unsupported, "lookup method is unsupported in path {}", path) } /// Create a new node with the given `path` in the directory /// /// Return [`Ok(())`](Ok) if it already exists. - fn create(&self, _path: &str, _ty: VfsNodeType) -> VfsResult { - ax_err!(Unsupported) + fn create(&self, path: &RelPath, ty: VfsNodeType, mode: VfsNodePerm) -> VfsResult { + ax_err!( + Unsupported, + "create method is unsupported in path {} type {:?}, mode {:?}", + path, + ty, + mode + ) } - /// Remove the node with the given `path` in the directory. - fn remove(&self, _path: &str) -> VfsResult { - ax_err!(Unsupported) + /// Add a new node(especially `SocketNode`) with the given `name` in the directory. + fn create_socket_node(&self, name: &RelPath, _node: VfsNodeRef) -> VfsResult { + ax_err!( + Unsupported, + "create_socket_node method is unsupported in path {}", + name + ) + } + + /// Create a new hard link to the src dentry + fn link(&self, name: &RelPath, _src: Arc) -> VfsResult> { + ax_err!(Unsupported, "link method is unsupported in path {}", name) + } + + /// Remove (the hard link of) the node with the given `path` in the directory. + fn unlink(&self, path: &RelPath) -> VfsResult { + ax_err!(Unsupported, "unlink method is unsupported in path {}", path) + } + + /// Rename the node `src_path` to `dst_path` in the directory. + fn rename(&self, src_path: &RelPath, dst_path: &RelPath) -> VfsResult<()> { + ax_err!( + Unsupported, + "rename method is unsupported, src {}, dst {}", + src_path, + dst_path + ) } /// Read directory entries into `dirents`, starting from `start_idx`. - fn read_dir(&self, _start_idx: usize, _dirents: &mut [VfsDirEntry]) -> VfsResult { - ax_err!(Unsupported) + fn read_dir(&self, start_idx: usize, _dirents: &mut [VfsDirEntry]) -> VfsResult { + ax_err!( + Unsupported, + "read_dir method is unsupported, start_idx is {}", + start_idx + ) } - /// Renames or moves existing file or directory. - fn rename(&self, _src_path: &str, _dst_path: &str) -> VfsResult { - ax_err!(Unsupported) + /// Check if the directory is empty. An empty directory only contains `.` and `..`. + /// + /// Brute implementation: read entries and check if there are more than 2. + fn is_empty(&self) -> VfsResult { + let mut buf = [ + VfsDirEntry::default(), + VfsDirEntry::default(), + VfsDirEntry::default(), + ]; + self.read_dir(0, &mut buf).map(|n| n <= 2) } /// Convert `&self` to [`&dyn Any`][1] that can use @@ -178,32 +235,59 @@ pub trait VfsNodeOps: Send + Sync { unimplemented!() } + /// Provides type-erased access to the underlying `Arc` for downcasting. + fn as_any_arc(self: Arc) -> Arc { + unimplemented!() + } + /// Create a new node with given `path` in the directory, recursively. /// /// Default implementation `create`s all prefix sub-paths sequentially, /// implementor may provide a more efficient impl. /// /// Return [`Ok(())`](Ok) if already exists. - fn create_recursive(&self, path: &str, ty: VfsNodeType) -> VfsResult { - if path.starts_with('/') { - return ax_err!(InvalidInput); - } - let path = path.trim_end_matches('/'); + fn create_recursive(&self, path: &RelPath, ty: VfsNodeType, mode: VfsNodePerm) -> VfsResult { for (i, c) in path.char_indices() { let part = if c == '/' { unsafe { path.get_unchecked(..i) } } else { continue; }; - match self.create(part, VfsNodeType::Dir) { + match self.create( + &RelPath::new(part), + VfsNodeType::Dir, + VfsNodePerm::default_dir(), + ) { Ok(()) | Err(AxError::AlreadyExists) => {} err @ Err(_) => return err, } } - self.create(path, ty)?; - + self.create(path, ty, mode)?; Ok(()) } + + /// Manipulates the underlying device parameters of special files. + /// In particular, many operating characteristics of character special files + /// (e.g., terminals) may be controlled with ioctl() requests. + fn ioctl(&self, _cmd: usize, _arg: usize) -> VfsResult { + Err(AxError::Unsupported) + } + + /// For regular files, the poll() always returns immediately with POLLIN | POLLOUT + /// events set, since I/O operations on regular files are always considered ready. + /// + /// For special files like character devices, poll() requires actual readiness + /// checks: + /// - POLLIN is set when the device's input buffer has data available + /// - POLLOUT is set when the device's output buffer has space available + /// - POLLHUP is set when peer closed + fn poll(&self) -> AxResult { + Ok(PollState { + readable: true, + writable: true, + pollhup: false, + }) + } } #[doc(hidden)] diff --git a/crates/axfs_vfs/src/macros.rs b/crates/axfs_vfs/src/macros.rs index 99f8ab3e3..a83444ba0 100644 --- a/crates/axfs_vfs/src/macros.rs +++ b/crates/axfs_vfs/src/macros.rs @@ -34,6 +34,13 @@ macro_rules! impl_vfs_dir_default { fn as_any(&self) -> &dyn core::any::Any { self } + + #[inline] + fn as_any_arc( + self: $crate::__priv::Arc, + ) -> $crate::__priv::Arc { + self + } }; } @@ -46,16 +53,37 @@ macro_rules! impl_vfs_non_dir_default { () => { fn lookup( self: $crate::__priv::Arc, - _path: &str, + _path: &$crate::RelPath, ) -> $crate::VfsResult<$crate::VfsNodeRef> { $crate::__priv::ax_err!(NotADirectory) } - fn create(&self, _path: &str, _ty: $crate::VfsNodeType) -> $crate::VfsResult { + fn create( + &self, + _path: &$crate::RelPath, + _ty: $crate::VfsNodeType, + _mode: $crate::VfsNodePerm, + ) -> $crate::VfsResult { + $crate::__priv::ax_err!(NotADirectory) + } + + fn link( + &self, + _name: &$crate::RelPath, + _src: $crate::__priv::Arc, + ) -> VfsResult<$crate::__priv::Arc> { $crate::__priv::ax_err!(NotADirectory) } - fn remove(&self, _path: &str) -> $crate::VfsResult { + fn unlink(&self, _path: &$crate::RelPath) -> $crate::VfsResult { + $crate::__priv::ax_err!(NotADirectory) + } + + fn rename( + &self, + _src_path: &$crate::RelPath, + _dst_path: &$crate::RelPath, + ) -> $crate::VfsResult { $crate::__priv::ax_err!(NotADirectory) } @@ -71,5 +99,12 @@ macro_rules! impl_vfs_non_dir_default { fn as_any(&self) -> &dyn core::any::Any { self } + + #[inline] + fn as_any_arc( + self: $crate::__priv::Arc, + ) -> $crate::__priv::Arc { + self + } }; } diff --git a/crates/axfs_vfs/src/path.rs b/crates/axfs_vfs/src/path.rs index 37faf3555..80ec8fe2c 100644 --- a/crates/axfs_vfs/src/path.rs +++ b/crates/axfs_vfs/src/path.rs @@ -9,29 +9,172 @@ //! Utilities for path manipulation. -use alloc::string::String; +use alloc::{ + borrow::{Cow, ToOwned}, + format, + string::{String, ToString}, +}; -/// Returns the canonical form of the path with all intermediate components -/// normalized. +/// Canonicalized absolute path type. Requirements: /// -/// It won't force convert the path to an absolute form. +/// - Starting with `/` +/// - No `.` or `..` components +/// - No redundant or tailing `/` +/// - Valid examples: `/`, `/root/foo/bar` /// -/// # Examples +/// Using `Cow` type to avoid unnecessary allocations. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AbsPath<'a>(Cow<'a, str>); + +impl AbsPath<'static> { + /// Simply wrap a string into a `AbsPath`. + /// + /// Caller should ensure that the path is absolute and canonicalized. + pub const fn new_owned(path: String) -> Self { + Self(Cow::Owned(path)) + } + + /// Parse and canonicalize an absolute path from a string. + /// + /// - If the given path is not canonicalized, it will be canonicalized. + /// - If the given path is not absolute, it will be prefixed with `/`. + pub fn new_canonicalized(path: &str) -> Self { + if !path.starts_with('/') { + Self(Cow::Owned(canonicalize(&("/".to_owned() + path)))) + } else { + Self(Cow::Owned(canonicalize(path))) + } + } +} + +impl<'a> AbsPath<'a> { + /// Simply wrap a str slice into a `AbsPath`. + /// + /// Caller should ensure that the path is absolute and canonicalized. + pub const fn new(path: &'a str) -> Self { + Self(Cow::Borrowed(path)) + } + + /// Trim the starting `/` to transform this `AbsPath` into a `RelPath`. + pub fn to_rel(&self) -> RelPath { + RelPath(Cow::Borrowed(self.0.trim_start_matches('/'))) + } + + /// Create a new `AbsPath` with 'static lifetime. + pub fn to_owned(&self) -> AbsPath<'static> { + AbsPath::new_owned(self.0.to_string()) + } + + /// Convert this `AbsPath` into a raw str slice. + pub fn as_str(&self) -> &str { + self.0.as_ref() + } + + /// Convert this `AbsPath` into a raw string. + #[allow(clippy::inherent_to_string_shadow_display)] + pub fn to_string(&self) -> String { + self.0.to_string() + } + + /// Concatenate a `RelPath` to this `AbsPath`. + pub fn join(&self, rel: &RelPath) -> AbsPath<'static> { + AbsPath::new_canonicalized(&format!("{}/{}", self.0, rel.0)) + } +} + +impl core::ops::Deref for AbsPath<'_> { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl core::fmt::Display for AbsPath<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.0) + } +} + +/// Canonicalized relative path type. Requirements: /// -/// ``` -/// use axfs_vfs::path::canonicalize; +/// - No starting `/` +/// - No `.` components +/// - No redundant or tailing `/` +/// - Possibly starts with `..` +/// - Valid examples: ` `, `..`, `../b`, `../..` /// -/// assert_eq!(canonicalize("/path/./to//foo"), "/path/to/foo"); -/// assert_eq!(canonicalize("/./path/to/../bar.rs"), "/path/bar.rs"); -/// assert_eq!(canonicalize("./foo/./bar"), "foo/bar"); -/// ``` -pub fn canonicalize(path: &str) -> String { +/// Using `Cow` type to avoid unnecessary allocations. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RelPath<'a>(Cow<'a, str>); + +impl RelPath<'static> { + /// Simply wrap a string into a `RelPath`. + /// + /// Caller should ensure that the path is relative and canonicalized. + pub const fn new_owned(path: String) -> Self { + Self(Cow::Owned(path)) + } + + /// Parse and canonicalize a relative path from a string. + /// + /// - If the given path is not canonicalized, it will be canonicalized. + /// - If the given path is absolute, the starting '/' will be trimmed. + pub fn new_canonicalized(path: &str) -> Self { + Self(Cow::Owned(canonicalize(path.trim_start_matches('/')))) + } +} + +impl<'a> RelPath<'a> { + /// Simply wrap a string into a `RelPath`. + /// + /// Caller should ensure that the path is relative and canonicalized. + pub const fn new(path: &'a str) -> Self { + Self(Cow::Borrowed(path)) + } + + /// Wrap a string into a `RelPath` with possibly leading '/' trimmed. + /// + /// Caller should ensure that the path is canonicalized. + pub fn new_trimmed(path: &'a str) -> Self { + Self(Cow::Borrowed(path.trim_start_matches('/'))) + } + + /// Convert this `RelPath` into a raw str slice. + pub fn as_str(&self) -> &str { + self.0.as_ref() + } +} + +impl core::ops::Deref for RelPath<'_> { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl core::fmt::Display for RelPath<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.0) + } +} + +/// Returns the canonical form of the path with all intermediate components +/// normalized. +/// +/// It won't force convert the path to an absolute form. +fn canonicalize(path: &str) -> String { let mut buf = String::new(); let is_absolute = path.starts_with('/'); for part in path.split('/') { match part { "" | "." => continue, ".." => { + if !is_absolute && buf.is_empty() { + buf.push_str(".."); + continue; + } while !buf.is_empty() { if buf == "/" { break; @@ -72,9 +215,9 @@ mod tests { assert_eq!(canonicalize("/a/../"), "/"); assert_eq!(canonicalize("/a/../..///"), "/"); assert_eq!(canonicalize("a/../"), ""); - assert_eq!(canonicalize("a/..//.."), ""); + assert_eq!(canonicalize("a/..//.."), ".."); assert_eq!(canonicalize("././a"), "a"); - assert_eq!(canonicalize(".././a"), "a"); + assert_eq!(canonicalize(".././a"), "../a"); assert_eq!(canonicalize("/././a"), "/a"); assert_eq!(canonicalize("/abc/../abc"), "/abc"); assert_eq!(canonicalize("/test"), "/test"); @@ -93,8 +236,8 @@ mod tests { assert_eq!(canonicalize("/test//./../foo/bar//"), "/foo/bar"); assert_eq!(canonicalize("/test/../foo"), "/foo"); assert_eq!(canonicalize("/test/bar/../foo"), "/test/foo"); - assert_eq!(canonicalize("../foo"), "foo"); - assert_eq!(canonicalize("../foo/"), "foo"); + assert_eq!(canonicalize("../foo"), "../foo"); + assert_eq!(canonicalize("../foo/"), "../foo"); assert_eq!(canonicalize("/../foo"), "/foo"); assert_eq!(canonicalize("/../foo/"), "/foo"); assert_eq!(canonicalize("/../../foo"), "/foo"); diff --git a/crates/axfs_vfs/src/structs.rs b/crates/axfs_vfs/src/structs.rs index 7a4b3834b..6a037eb9e 100644 --- a/crates/axfs_vfs/src/structs.rs +++ b/crates/axfs_vfs/src/structs.rs @@ -7,6 +7,10 @@ * See the Mulan PSL v2 for more details. */ +use core::str; + +use alloc::{borrow::ToOwned, string::String}; + /// Filesystem attributes. /// /// Currently not used. @@ -17,6 +21,8 @@ pub struct FileSystemInfo; #[allow(dead_code)] #[derive(Debug, Clone, Copy)] pub struct VfsNodeAttr { + /// Inode number. + ino: u64, /// File permission mode. mode: VfsNodePerm, /// File type. @@ -74,7 +80,14 @@ pub enum VfsNodeType { Socket = 0o14, } +impl Default for VfsNodeType { + fn default() -> Self { + Self::File + } +} + /// Directory entry. +#[derive(Clone, Copy)] pub struct VfsDirEntry { d_type: VfsNodeType, d_name: [u8; 63], @@ -96,6 +109,20 @@ impl VfsNodePerm { Self::from_bits_truncate(0o755) } + /// Returns the default permission for a socket. + /// + /// The default permission is `0o777` (owner/group/others can read, write and execute). + pub const fn default_socket() -> Self { + Self::from_bits_truncate(0o777) + } + + /// Returns the default permission for a fifo file. + /// + /// The default permission is `0o777` (owner/group/others can read, write and execute). + pub const fn default_fifo() -> Self { + Self::from_bits_truncate(0o777) + } + /// Returns the underlying raw `st_mode` bits that contain the standard /// Unix permissions for this file. pub const fn mode(&self) -> u32 { @@ -206,10 +233,11 @@ impl VfsNodeType { } impl VfsNodeAttr { - /// Creates a new `VfsNodeAttr` with the given permission mode, type, size + /// Creates a new `VfsNodeAttr` with the given inode number, permission mode, type, size /// and number of blocks. - pub const fn new(mode: VfsNodePerm, ty: VfsNodeType, size: u64, blocks: u64) -> Self { + pub const fn new(ino: u64, mode: VfsNodePerm, ty: VfsNodeType, size: u64, blocks: u64) -> Self { Self { + ino, mode, ty, size, @@ -218,8 +246,9 @@ impl VfsNodeAttr { } /// Creates a new `VfsNodeAttr` for a file, with the default file permission. - pub const fn new_file(size: u64, blocks: u64) -> Self { + pub const fn new_file(ino: u64, size: u64, blocks: u64) -> Self { Self { + ino, mode: VfsNodePerm::default_file(), ty: VfsNodeType::File, size, @@ -227,10 +256,10 @@ impl VfsNodeAttr { } } - /// Creates a new `VfsNodeAttr` for a directory, with the default directory - /// permission. - pub const fn new_dir(size: u64, blocks: u64) -> Self { + /// Creates a new `VfsNodeAttr` for a directory, with the default directory permission. + pub const fn new_dir(ino: u64, size: u64, blocks: u64) -> Self { Self { + ino, mode: VfsNodePerm::default_dir(), ty: VfsNodeType::Dir, size, @@ -238,6 +267,10 @@ impl VfsNodeAttr { } } + /// Returns the inode number of the node. + pub const fn ino(&self) -> u64 { + self.ino + } /// Returns the size of the node. pub const fn size(&self) -> u64 { self.size @@ -272,6 +305,16 @@ impl VfsNodeAttr { pub const fn is_dir(&self) -> bool { self.ty.is_dir() } + + /// Whether the node is a fifo. + pub const fn is_fifo(&self) -> bool { + self.ty.is_fifo() + } + + /// Whether the node is a socket. + pub const fn is_socket(&self) -> bool { + self.ty.is_socket() + } } impl VfsDirEntry { @@ -302,6 +345,11 @@ impl VfsDirEntry { self.d_type } + /// Convert dentry name from bytes to string. Used in arceos api + pub fn name_as_string(&self) -> String { + unsafe { alloc::str::from_utf8_unchecked(self.name_as_bytes()).to_owned() } + } + /// Converts the name of the entry to a byte slice. pub fn name_as_bytes(&self) -> &[u8] { let len = self diff --git a/crates/axio/src/lib.rs b/crates/axio/src/lib.rs index 6a34e59f3..b42ae626e 100644 --- a/crates/axio/src/lib.rs +++ b/crates/axio/src/lib.rs @@ -266,4 +266,6 @@ pub struct PollState { pub readable: bool, /// Object can be writen now. pub writable: bool, + /// Object is closed (by remote) now. + pub pollhup: bool, } diff --git a/modules/axlog/Cargo.toml b/crates/axlog/Cargo.toml similarity index 87% rename from modules/axlog/Cargo.toml rename to crates/axlog/Cargo.toml index 98ecdcf70..7827dfb35 100644 --- a/modules/axlog/Cargo.toml +++ b/crates/axlog/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Yuekai Jia "] description = "Macros for multi-level formatted logging used by ArceOS" license = "GPL-3.0-or-later OR Apache-2.0" homepage = "https://github.com/rcore-os/arceos" -repository = "https://github.com/rcore-os/arceos/tree/main/modules/axlog" +repository = "https://github.com/rcore-os/arceos/tree/main/crates/axlog" documentation = "https://rcore-os.github.io/arceos/axlog/index.html" [features] @@ -22,7 +22,7 @@ default = [] [dependencies] cfg-if = "1.0" log = "0.4" -spinlock = { path = "../../crates/spinlock" } +spinlock = { path = "../spinlock" } crate_interface = { version = "0.1.1" } chrono = { version = "0.4", optional = true } diff --git a/modules/axlog/src/lib.rs b/crates/axlog/src/lib.rs similarity index 99% rename from modules/axlog/src/lib.rs rename to crates/axlog/src/lib.rs index 2b954a0cc..4652a9fa8 100644 --- a/modules/axlog/src/lib.rs +++ b/crates/axlog/src/lib.rs @@ -142,7 +142,7 @@ impl Write for Logger { fn write_str(&mut self, s: &str) -> fmt::Result { cfg_if::cfg_if! { if #[cfg(feature = "std")] { - std::print!("{}", s); + std::print!("{s}"); } else { call_interface!(LogIf::console_write_str, s); } diff --git a/crates/capability/Cargo.toml b/crates/capability/Cargo.toml index 274f30863..bd985919e 100644 --- a/crates/capability/Cargo.toml +++ b/crates/capability/Cargo.toml @@ -10,5 +10,6 @@ repository = "https://github.com/rcore-os/arceos/tree/main/crates/capability" documentation = "https://rcore-os.github.io/arceos/capability/index.html" [dependencies] -bitflags = "2.2" +bitflags = { workspace = true } axerrno = { path = "../axerrno" } +axfs_vfs = { path = "../axfs_vfs" } diff --git a/crates/capability/src/lib.rs b/crates/capability/src/lib.rs index af5015021..1f1e8c820 100644 --- a/crates/capability/src/lib.rs +++ b/crates/capability/src/lib.rs @@ -37,6 +37,8 @@ #![no_std] +use axfs_vfs::VfsNodePerm; + bitflags::bitflags! { /// Capabilities (access rights). #[derive(Default, Debug, Clone, Copy)] @@ -141,8 +143,29 @@ impl WithCap { } impl From for axerrno::AxError { - #[inline] fn from(_: CapError) -> Self { Self::PermissionDenied } } + +impl From for axerrno::LinuxError { + fn from(_: CapError) -> Self { + Self::EPERM + } +} + +impl From for Cap { + fn from(perm: VfsNodePerm) -> Self { + let mut cap = Cap::empty(); + if perm.owner_readable() { + cap |= Cap::READ; + } + if perm.owner_writable() { + cap |= Cap::WRITE; + } + if perm.owner_executable() { + cap |= Cap::EXECUTE; + } + cap + } +} diff --git a/crates/driver_9p/Cargo.toml b/crates/driver_9p/Cargo.toml index dd56965c8..297d05e56 100644 --- a/crates/driver_9p/Cargo.toml +++ b/crates/driver_9p/Cargo.toml @@ -12,6 +12,6 @@ repository = "https://github.com/syswonder/ruxos/tree/main/crates/driver_9p" default = [] [dependencies] -log = "0.4" -spin = "0.9" +log = { workspace = true } +spin = { workspace = true } driver_common = { path = "../driver_common" } diff --git a/crates/driver_block/Cargo.toml b/crates/driver_block/Cargo.toml index a264145e7..96c322b22 100644 --- a/crates/driver_block/Cargo.toml +++ b/crates/driver_block/Cargo.toml @@ -15,6 +15,6 @@ bcm2835-sdhci = ["dep:bcm2835-sdhci"] default = [] [dependencies] -log = "0.4" +log = { workspace = true } driver_common = { path = "../driver_common" } bcm2835-sdhci = { git = "https://github.com/syswonder/bcm2835-sdhci.git", rev = "e974f16", optional = true } diff --git a/crates/driver_console/Cargo.toml b/crates/driver_console/Cargo.toml new file mode 100644 index 000000000..30fc2b253 --- /dev/null +++ b/crates/driver_console/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "driver_console" +version = "0.1.0" +edition = "2021" +authors = [ + "Hangqi Ren <2572131118@qq.com>" +] +description = "Common traits and types for console drivers" +license = "GPL-3.0-or-later OR Apache-2.0" +homepage = "https://github.com/syswonder/ruxos" +repository = "https://github.com/syswonder/ruxos/tree/main/crates/driver_console" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = [] + +[dependencies] +driver_common = { path = "../driver_common" } \ No newline at end of file diff --git a/crates/driver_console/src/lib.rs b/crates/driver_console/src/lib.rs new file mode 100644 index 000000000..68342c693 --- /dev/null +++ b/crates/driver_console/src/lib.rs @@ -0,0 +1,27 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! Common traits and types for block storage device drivers (i.e. disk). + +#![no_std] +#![feature(doc_auto_cfg)] +#![feature(const_trait_impl)] + +#[doc(no_inline)] +pub use driver_common::{BaseDriverOps, DevError, DevResult, DeviceType}; + +/// Operations that require a console device driver to implement. +pub trait ConsoleDriverOps: BaseDriverOps { + /// Writes a single byte to the console. + fn putchar(&mut self, c: u8); + /// Reads a single byte from the console. + fn getchar(&mut self) -> Option; + /// Acknowledge an interrupt from the console. + fn ack_interrupt(&mut self) -> DevResult; +} diff --git a/crates/driver_net/Cargo.toml b/crates/driver_net/Cargo.toml index 5018c4a7c..cd72e1946 100644 --- a/crates/driver_net/Cargo.toml +++ b/crates/driver_net/Cargo.toml @@ -2,7 +2,10 @@ name = "driver_net" version = "0.1.0" edition = "2021" -authors = ["Yuekai Jia ", "ChengXiang Qi "] +authors = [ + "Yuekai Jia ", + "ChengXiang Qi ", +] description = "Common traits and types for network device (NIC) drivers" license = "GPL-3.0-or-later OR Apache-2.0" homepage = "https://github.com/rcore-os/arceos" @@ -11,10 +14,11 @@ documentation = "https://rcore-os.github.io/arceos/driver_net/index.html" [features] default = [] +loopback = [] ixgbe = ["dep:ixgbe-driver"] [dependencies] -spin = "0.9" -log = "0.4" +spin = { workspace = true } +log = { workspace = true } driver_common = { path = "../driver_common" } -ixgbe-driver = {git = "https://github.com/KuangjuX/ixgbe-driver.git", rev = "8e5eb74", optional = true} +ixgbe-driver = { git = "https://github.com/KuangjuX/ixgbe-driver.git", rev = "8e5eb74", optional = true } diff --git a/crates/driver_net/src/ixgbe.rs b/crates/driver_net/src/ixgbe.rs index 3fe98b4c6..6fce04004 100644 --- a/crates/driver_net/src/ixgbe.rs +++ b/crates/driver_net/src/ixgbe.rs @@ -10,7 +10,7 @@ use core::convert::From; use core::{mem::ManuallyDrop, ptr::NonNull}; -use alloc::{collections::VecDeque, sync::Arc}; +use alloc::{boxed::Box, collections::VecDeque, sync::Arc}; use driver_common::{BaseDriverOps, DevError, DevResult, DeviceType}; use ixgbe_driver::{IxgbeDevice, IxgbeError, IxgbeNetBuf, MemPool, NicDevice}; pub use ixgbe_driver::{IxgbeHal, PhysAddr, INTEL_82599, INTEL_VEND}; @@ -43,7 +43,7 @@ impl IxgbeNic { let mem_pool = MemPool::allocate::(MEM_POOL, MEM_POOL_ENTRY_SIZE) .map_err(|_| DevError::NoMemory)?; let inner = IxgbeDevice::::init(base, len, QN, QN, &mem_pool).map_err(|err| { - log::error!("Failed to initialize ixgbe device: {:?}", err); + log::error!("Failed to initialize ixgbe device: {err:?}"); DevError::BadState })?; @@ -164,11 +164,14 @@ impl From for NetBufPtr { // In ixgbe, `raw_ptr` is the pool entry, `buf_ptr` is the packet ptr, `len` is packet len // to avoid too many dynamic memory allocation. let buf_ptr = buf.packet_mut().as_mut_ptr(); - Self::new( - NonNull::new(buf.pool_entry() as *mut u8).unwrap(), - NonNull::new(buf_ptr).unwrap(), - buf.packet_len(), - ) + let raw_ptr = NonNull::new(buf.pool_entry() as *mut u8).unwrap(); + let buf_ptr = NonNull::new(buf_ptr).unwrap(); + let len = buf.packet_len(); + // TODO: Verify the safety of `drop_fn`. + let drop_fn = Box::new(move || { + ManuallyDrop::into_inner(buf); + }); + Self::new(raw_ptr, buf_ptr, drop_fn, len) } } diff --git a/crates/driver_net/src/lib.rs b/crates/driver_net/src/lib.rs index 6fd20c600..16ae71d4c 100644 --- a/crates/driver_net/src/lib.rs +++ b/crates/driver_net/src/lib.rs @@ -10,16 +10,17 @@ //! Common traits and types for network device (NIC) drivers. #![no_std] -#![feature(const_mut_refs)] -#![feature(const_slice_from_raw_parts_mut)] #![feature(box_into_inner)] extern crate alloc; -use alloc::sync::Arc; +use alloc::{boxed::Box, sync::Arc}; #[cfg(feature = "ixgbe")] /// ixgbe NIC device driver. pub mod ixgbe; +#[cfg(feature = "loopback")] +/// loopback device driver +pub mod loopback; mod net_buf; use core::ptr::NonNull; @@ -94,15 +95,23 @@ pub struct NetBufPtr { raw_ptr: NonNull, // The pointer to the net buffer. buf_ptr: NonNull, + // drop function for the original object. + drop_fn: Box, len: usize, } impl NetBufPtr { /// Create a new [`NetBufPtr`]. - pub fn new(raw_ptr: NonNull, buf_ptr: NonNull, len: usize) -> Self { + pub fn new( + raw_ptr: NonNull, + buf_ptr: NonNull, + drop_fn: Box, + len: usize, + ) -> Self { Self { raw_ptr, buf_ptr, + drop_fn, len, } } @@ -127,3 +136,10 @@ impl NetBufPtr { unsafe { core::slice::from_raw_parts_mut(self.buf_ptr.as_ptr(), self.len) } } } + +impl Drop for NetBufPtr { + fn drop(&mut self) { + let todo = core::mem::replace(&mut self.drop_fn, Box::new(|| {})); + todo(); + } +} diff --git a/crates/driver_net/src/loopback.rs b/crates/driver_net/src/loopback.rs new file mode 100644 index 000000000..f36f06674 --- /dev/null +++ b/crates/driver_net/src/loopback.rs @@ -0,0 +1,129 @@ +/* Copyright (c) [2023] [Syswonder Community] +* [Ruxos] is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ +use crate::{EthernetAddress, NetBuf, NetBufBox, NetBufPool, NetBufPtr, NetDriverOps}; +use alloc::collections::VecDeque; +use alloc::sync::Arc; +use driver_common::{BaseDriverOps, DevError, DevResult, DeviceType}; + +extern crate alloc; + +const NET_BUF_LEN: usize = 1526; + +/// The VirtIO network device driver. +/// +/// `QS` is the VirtIO queue size. +pub struct LoopbackDevice { + mac_address: EthernetAddress, + pub(crate) queue: VecDeque, + buf_pool: Arc, +} + +unsafe impl Send for LoopbackDevice {} +unsafe impl Sync for LoopbackDevice {} + +impl LoopbackDevice { + /// Creates a new driver instance and initializes the device + pub fn new(mac_address: Option<[u8; 6]>) -> Self { + let buf_pool = match NetBufPool::new(1024, NET_BUF_LEN) { + Ok(pool) => pool, + Err(_) => { + panic!("fail to create netbufpool"); + } + }; + Self { + mac_address: match mac_address { + Some(address) => EthernetAddress(address), + None => EthernetAddress([0; 6]), + }, + queue: VecDeque::new(), + buf_pool, + } + } +} + +impl BaseDriverOps for LoopbackDevice { + fn device_name(&self) -> &str { + "loopback" + } + + fn device_type(&self) -> DeviceType { + DeviceType::Net + } +} + +impl NetDriverOps for LoopbackDevice { + #[inline] + fn mac_address(&self) -> EthernetAddress { + EthernetAddress(self.mac_address.0) + } + + #[inline] + fn can_transmit(&self) -> bool { + true + } + + #[inline] + fn can_receive(&self) -> bool { + !self.queue.is_empty() + } + + #[inline] + fn rx_queue_size(&self) -> usize { + self.queue.len() + } + + #[inline] + fn tx_queue_size(&self) -> usize { + self.queue.len() + } + + fn fill_rx_buffers(&mut self, _buf_pool: &Arc) -> DevResult { + Ok(()) + } + + fn recycle_rx_buffer(&mut self, _rx_buf: NetBufPtr) -> DevResult { + Ok(()) + } + + fn recycle_tx_buffers(&mut self) -> DevResult { + Ok(()) + } + + fn prepare_tx_buffer(&self, _tx_buf: &mut NetBuf, _pkt_len: usize) -> DevResult { + Ok(()) + } + + fn transmit(&mut self, tx_buf: NetBufPtr) -> DevResult { + unsafe { self.queue.push_back(NetBuf::from_buf_ptr(tx_buf)) } + Ok(()) + } + + fn receive(&mut self) -> DevResult { + if let Some(token) = self.queue.pop_front() { + Ok(token.into_buf_ptr()) + } else { + Err(DevError::Again) + } + } + + fn alloc_tx_buffer(&mut self, size: usize) -> DevResult { + let mut net_buf = self.buf_pool.alloc_boxed().ok_or(DevError::NoMemory)?; + let pkt_len = size; + + // 1. Check if the buffer is large enough. + let hdr_len = net_buf.header_len(); + if hdr_len + pkt_len > net_buf.capacity() { + return Err(DevError::InvalidParam); + } + net_buf.set_packet_len(pkt_len); + + // 2. Return the buffer. + Ok(net_buf.into_buf_ptr()) + } +} diff --git a/crates/driver_net/src/net_buf.rs b/crates/driver_net/src/net_buf.rs index b218320cb..3ffa95af1 100644 --- a/crates/driver_net/src/net_buf.rs +++ b/crates/driver_net/src/net_buf.rs @@ -11,6 +11,7 @@ extern crate alloc; use crate::{DevError, DevResult, NetBufPtr}; use alloc::{boxed::Box, sync::Arc, vec, vec::Vec}; +use core::mem::ManuallyDrop; use core::ptr::NonNull; use spin::Mutex; @@ -114,11 +115,13 @@ impl NetBuf { pub fn into_buf_ptr(mut self: Box) -> NetBufPtr { let buf_ptr = self.packet_mut().as_mut_ptr(); let len = self.packet_len; - NetBufPtr::new( - NonNull::new(Box::into_raw(self) as *mut u8).unwrap(), - NonNull::new(buf_ptr).unwrap(), - len, - ) + let raw_ptr = NonNull::new(&mut *self as *mut Self as *mut u8).unwrap(); + let buf_ptr = NonNull::new(buf_ptr).unwrap(); + let drop_fn = Box::new(move || { + self.pool.dealloc(self.pool_offset); + let _ = ManuallyDrop::new(self); + }); + NetBufPtr::new(raw_ptr, buf_ptr, drop_fn, len) } /// Restore [`NetBuf`] struct from a raw pointer. diff --git a/crates/driver_pci/Cargo.toml b/crates/driver_pci/Cargo.toml index dec497669..63981dfaa 100644 --- a/crates/driver_pci/Cargo.toml +++ b/crates/driver_pci/Cargo.toml @@ -10,4 +10,4 @@ repository = "https://github.com/rcore-os/arceos/tree/main/crates/driver_pci" documentation = "https://rcore-os.github.io/arceos/driver_pci/index.html" [dependencies] -virtio-drivers = { git = "https://github.com/syswonder/virtio-drivers.git", rev = "62dbe5a"} +virtio-drivers = { git = "https://github.com/syswonder/virtio-drivers.git", rev = "31f6555"} diff --git a/crates/driver_pci/src/lib.rs b/crates/driver_pci/src/lib.rs index 11f5d95d9..50532b625 100644 --- a/crates/driver_pci/src/lib.rs +++ b/crates/driver_pci/src/lib.rs @@ -17,11 +17,13 @@ #![no_std] -pub use virtio_drivers::transport::pci::bus::{BarInfo, Cam, HeaderType, MemoryBarType, PciError}; +pub use virtio_drivers::transport::pci::bus::ConfigurationAccess; +pub use virtio_drivers::transport::pci::bus::{ + BarInfo, Cam, HeaderType, MemoryBarType, MmioCam, PciError, +}; pub use virtio_drivers::transport::pci::bus::{ CapabilityInfo, Command, DeviceFunction, DeviceFunctionInfo, PciRoot, Status, }; - /// Used to allocate MMIO regions for PCI BARs. pub struct PciRangeAllocator { _start: u64, diff --git a/crates/driver_virtio/Cargo.toml b/crates/driver_virtio/Cargo.toml index 2415441ec..57a1b5a7e 100644 --- a/crates/driver_virtio/Cargo.toml +++ b/crates/driver_virtio/Cargo.toml @@ -2,7 +2,10 @@ name = "driver_virtio" version = "0.1.0" edition = "2021" -authors = ["Yuekai Jia ", "ChengXiang Qi "] +authors = [ + "Yuekai Jia ", + "ChengXiang Qi ", +] description = "Wrappers of some devices in the `virtio-drivers` crate, that implement traits in the `driver_common` series crates" license = "GPL-3.0-or-later OR Apache-2.0" homepage = "https://github.com/rcore-os/arceos" @@ -14,13 +17,15 @@ block = ["driver_block"] net = ["driver_net"] gpu = ["driver_display"] v9p = ["driver_9p"] +console = ["driver_console"] [dependencies] -log = "0.4" -spin = "0.9" +log = { workspace = true } +spin = { workspace = true } driver_common = { path = "../driver_common" } driver_block = { path = "../driver_block", optional = true } driver_net = { path = "../driver_net", optional = true } -driver_display = { path = "../driver_display", optional = true} -driver_9p = { path = "../driver_9p", optional = true} -virtio-drivers = { git = "https://github.com/syswonder/virtio-drivers.git", rev = "62dbe5a" } +driver_display = { path = "../driver_display", optional = true } +driver_9p = { path = "../driver_9p", optional = true } +driver_console = { path = "../driver_console", optional = true } +virtio-drivers = { git = "https://github.com/syswonder/virtio-drivers.git", rev = "31f6555" } diff --git a/crates/driver_virtio/src/blk.rs b/crates/driver_virtio/src/blk.rs index 3a2e9ffad..994eb1d74 100644 --- a/crates/driver_virtio/src/blk.rs +++ b/crates/driver_virtio/src/blk.rs @@ -53,13 +53,13 @@ impl BlockDriverOps for VirtIoBlkDev { fn read_block(&mut self, block_id: u64, buf: &mut [u8]) -> DevResult { self.inner - .read_block(block_id as _, buf) + .read_blocks(block_id as _, buf) .map_err(as_dev_err) } fn write_block(&mut self, block_id: u64, buf: &[u8]) -> DevResult { self.inner - .write_block(block_id as _, buf) + .write_blocks(block_id as _, buf) .map_err(as_dev_err) } diff --git a/crates/driver_virtio/src/console.rs b/crates/driver_virtio/src/console.rs new file mode 100644 index 000000000..15d4b5a3c --- /dev/null +++ b/crates/driver_virtio/src/console.rs @@ -0,0 +1,59 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use crate::as_dev_err; +use driver_common::{BaseDriverOps, DevResult, DeviceType}; +use driver_console::ConsoleDriverOps; +use virtio_drivers::{device::console::VirtIOConsole as InnerDev, transport::Transport, Hal}; + +/// VirtIO console device +pub struct VirtIoConsoleDev { + inner: InnerDev, +} + +unsafe impl Send for VirtIoConsoleDev {} +unsafe impl Sync for VirtIoConsoleDev {} + +impl VirtIoConsoleDev { + /// Creates a new driver instance and initializes the device, or returns + /// an error if any step fails. + pub fn try_new(transport: T) -> DevResult { + Ok(Self { + inner: InnerDev::new(transport).map_err(as_dev_err)?, + }) + } +} + +impl BaseDriverOps for VirtIoConsoleDev { + fn device_name(&self) -> &str { + "virtio-console" + } + + fn device_type(&self) -> DeviceType { + DeviceType::Char + } +} + +impl ConsoleDriverOps for VirtIoConsoleDev { + fn putchar(&mut self, c: u8) { + self.inner + .send(c) + .expect("VirtConsole: failed to send char"); + } + + fn getchar(&mut self) -> Option { + self.inner + .recv(true) + .expect("VirtConsole: failed to recv char") + } + + fn ack_interrupt(&mut self) -> DevResult { + self.inner.ack_interrupt().map_err(as_dev_err) + } +} diff --git a/crates/driver_virtio/src/gpu.rs b/crates/driver_virtio/src/gpu.rs index f4577fd16..a1b62b57b 100644 --- a/crates/driver_virtio/src/gpu.rs +++ b/crates/driver_virtio/src/gpu.rs @@ -16,7 +16,7 @@ use virtio_drivers::{device::gpu::VirtIOGpu as InnerDev, transport::Transport, H /// The VirtIO GPU device driver. pub struct VirtIoGpuDev { - inner: InnerDev<'static, H, T>, + inner: InnerDev, info: DisplayInfo, } diff --git a/crates/driver_virtio/src/lib.rs b/crates/driver_virtio/src/lib.rs index 269a5c1d5..f71803c92 100644 --- a/crates/driver_virtio/src/lib.rs +++ b/crates/driver_virtio/src/lib.rs @@ -25,6 +25,8 @@ #[cfg(feature = "block")] mod blk; +#[cfg(feature = "console")] +mod console; #[cfg(feature = "gpu")] mod gpu; #[cfg(feature = "net")] @@ -34,6 +36,8 @@ mod v9p; #[cfg(feature = "block")] pub use self::blk::VirtIoBlkDev; +#[cfg(feature = "console")] +pub use self::console::VirtIoConsoleDev; #[cfg(feature = "gpu")] pub use self::gpu::VirtIoGpuDev; #[cfg(feature = "net")] @@ -42,6 +46,7 @@ pub use self::net::VirtIoNetDev; pub use self::v9p::VirtIo9pDev; pub use virtio_drivers::transport::pci::bus as pci; +use virtio_drivers::transport::pci::bus::ConfigurationAccess; pub use virtio_drivers::transport::{mmio::MmioTransport, pci::PciTransport, Transport}; pub use virtio_drivers::{BufferDirection, Hal as VirtIoHal, PhysAddr}; @@ -55,13 +60,13 @@ use virtio_drivers::transport::DeviceType as VirtIoDevType; /// for later operations. Otherwise, returns [`None`]. pub fn probe_mmio_device( reg_base: *mut u8, - _reg_size: usize, -) -> Option<(DeviceType, MmioTransport)> { + reg_size: usize, +) -> Option<(DeviceType, MmioTransport<'static>)> { use core::ptr::NonNull; use virtio_drivers::transport::mmio::VirtIOHeader; let header = NonNull::new(reg_base as *mut VirtIOHeader).unwrap(); - let transport = unsafe { MmioTransport::new(header) }.ok()?; + let transport = unsafe { MmioTransport::new(header, reg_size) }.ok()?; let dev_type = as_dev_type(transport.device_type())?; Some((dev_type, transport)) } @@ -70,15 +75,19 @@ pub fn probe_mmio_device( /// /// If the device is recognized, returns the device type and a transport object /// for later operations. Otherwise, returns [`None`]. -pub fn probe_pci_device( - root: &mut PciRoot, +pub fn probe_pci_device( + root: &mut PciRoot, bdf: DeviceFunction, dev_info: &DeviceFunctionInfo, -) -> Option<(DeviceType, PciTransport)> { +) -> Option<(DeviceType, PciTransport)> +where + H: VirtIoHal, + C: ConfigurationAccess, +{ use virtio_drivers::transport::pci::virtio_device_type; let dev_type = virtio_device_type(dev_info).and_then(as_dev_type)?; - let transport = PciTransport::new::(root, bdf).ok()?; + let transport = PciTransport::new::(root, bdf).ok()?; Some((dev_type, transport)) } @@ -89,6 +98,7 @@ const fn as_dev_type(t: VirtIoDevType) -> Option { Network => Some(DeviceType::Net), GPU => Some(DeviceType::Display), _9P => Some(DeviceType::_9P), + Console => Some(DeviceType::Char), _ => None, } } diff --git a/crates/driver_virtio/src/net.rs b/crates/driver_virtio/src/net.rs index abd0e0f47..a575b34a9 100644 --- a/crates/driver_virtio/src/net.rs +++ b/crates/driver_virtio/src/net.rs @@ -99,12 +99,12 @@ impl NetDriverOps for VirtIoNetDev bool { - !self.free_tx_bufs.is_empty() && self.inner.can_transmit() + !self.free_tx_bufs.is_empty() && self.inner.can_send() } #[inline] fn can_receive(&self) -> bool { - self.inner.can_receive() + self.inner.poll_receive().is_some() } #[inline] diff --git a/crates/dtb/Cargo.toml b/crates/dtb/Cargo.toml index 2f65516dd..2df02d244 100644 --- a/crates/dtb/Cargo.toml +++ b/crates/dtb/Cargo.toml @@ -11,3 +11,4 @@ repository = "https://github.com/syswonder/ruxos/tree/main/crates/dtb" [dependencies] fdt-rs = { version = "0.4.3", default-features = false } lazy_init = { path = "../../crates/lazy_init" } +log = "0.4" diff --git a/crates/dtb/src/lib.rs b/crates/dtb/src/lib.rs index 551b49f6f..02e247f5f 100644 --- a/crates/dtb/src/lib.rs +++ b/crates/dtb/src/lib.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + //! Some useful interfaces for device tree. #![no_std] @@ -58,6 +67,11 @@ impl<'a> DeviceProp<'a> { pub fn str(&self) -> &'static str { self.0.str().unwrap() } + + /// Return offset of the given prop in the node. + pub fn offset(&self) -> usize { + self.0.nameoff() + } } /// Find the first node with given compatible(may not exist). @@ -112,3 +126,26 @@ where }) .unwrap(); } + +/// Get DevTreeNode by prop and value +pub fn get_node_by_prop_value<'a>(prop: &'a str, val: &'a str) -> Option> { + TREE.props() + .find(|p| Ok(p.name()? == prop && p.str()? == val)) + .unwrap() + .map(|prop| prop.node()) + .map(DeviceNode) +} + +/// Parse DevTreeProp propbuf with given indev, return `u64` type +/// +/// TODO: error handle +pub fn get_propbuf_u64(prop: &DeviceProp, index: usize) -> u64 { + prop.0.u64(index).unwrap() +} + +/// Parse DevTreeProp propbuf with given indev, return `u32` type +/// +/// TODO: error handle +pub fn get_propbuf_u32(prop: &DeviceProp, index: usize) -> u32 { + prop.0.u32(index).unwrap() +} diff --git a/crates/flatten_objects/src/lib.rs b/crates/flatten_objects/src/lib.rs index 11387fb8b..af93aa3f2 100644 --- a/crates/flatten_objects/src/lib.rs +++ b/crates/flatten_objects/src/lib.rs @@ -39,8 +39,6 @@ //! ``` #![no_std] -#![feature(maybe_uninit_uninit_array)] -#![feature(const_maybe_uninit_uninit_array)] use bitmaps::Bitmap; use core::mem::MaybeUninit; @@ -58,12 +56,18 @@ pub struct FlattenObjects { count: usize, } +impl Default for FlattenObjects { + fn default() -> Self { + Self::new() + } +} + impl FlattenObjects { /// Creates a new empty `FlattenObjects`. pub const fn new() -> Self { assert!(CAP <= 1024); Self { - objects: MaybeUninit::uninit_array(), + objects: [const { MaybeUninit::uninit() }; CAP], // SAFETY: zero initialization is OK for `id_bitmap` (an array of integers). id_bitmap: unsafe { MaybeUninit::zeroed().assume_init() }, count: 0, @@ -144,6 +148,25 @@ impl FlattenObjects { Some(id) } + /// Add an object and assign it a unique ID, but only search for IDs starting from `low_bound`. + /// + /// Returns the assigned ID if an available slot is found; otherwise, returns `None`. + pub fn add_with_low_bound(&mut self, value: T, low_bound: usize) -> Option { + let id = if low_bound == 0 { + self.id_bitmap.first_false_index() + } else { + self.id_bitmap.next_false_index(low_bound - 1) + }?; + if id < CAP { + self.count += 1; + self.id_bitmap.set(id, true); + self.objects[id].write(value); + Some(id) + } else { + None + } + } + /// Removes the object with the given ID. /// /// Returns the object if there is one assigned to the ID. Otherwise, diff --git a/crates/iovec/Cargo.toml b/crates/iovec/Cargo.toml new file mode 100644 index 000000000..ab3753783 --- /dev/null +++ b/crates/iovec/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "iovec" +version = "0.0.1" +edition = "2021" +authors = ["Quan Shi <749990226@qq.com>"] +description = "Scatter/Gather I/O Vector Library" +license = "Mulan PSL v2" +homepage = "https://github.com/syswonder/ruxos" +repository = "https://github.com/syswonder/ruxos/tree/dev/crates/iovec" + +[dependencies] +smallvec = "1.15.0" +axerrno = { path = "../axerrno" } diff --git a/crates/iovec/src/lib.rs b/crates/iovec/src/lib.rs new file mode 100644 index 000000000..bb82a04f6 --- /dev/null +++ b/crates/iovec/src/lib.rs @@ -0,0 +1,403 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ +//! # Scatter/Gather I/O Vector Library +//! +//! This crate provides efficient implementations of scatter/gather I/O operations using +//! I/O vectors (similar to POSIX iovec). It's designed for systems programming where +//! performance and memory efficiency are critical. +//! +//! ## Key Features +//! +//! - **Zero-copy operations**: Avoid unnecessary data copying between buffers +//! - **Small-vector optimization**: Stores single I/O vectors on the stack +//! - **Scatter/gather support**: Read/write across multiple disjoint buffers +//! +//! ## Core Concepts +//! +//! 1. **`IoVector`**: Describes a single memory region (base + length) +//! 2. **`IoVecs`**: Collection of vectors (stack-allocated when possible) +//! 3. **`IoVecsInput`**: For scatter reads (reading into multiple buffers) +//! 4. **`IoVecsOutput`**: For gather writes (writing from multiple buffers) +//! +//! ## Usage Examples +//! +//! ### Writing to multiple buffers +//! ```rust +//! use iovec::{IoVecsOutput, IoVector}; +//! use smallvec::smallvec; +//! +//! let mut buf1 = [0u8; 5]; +//! let mut buf2 = [0u8; 5]; +//! +//! let iovecs = smallvec![ +//! IoVector { base: buf1.as_mut_ptr() as usize, len: 5 }, +//! IoVector { base: buf2.as_mut_ptr() as usize, len: 5 } +//! ]; +//! +//! let mut output = IoVecsOutput::from_iovecs(iovecs); +//! let data = [1, 2, 3, 4, 5, 6, 7, 8]; +//! let written = output.write(&data); +//! ``` +//! +//! ### Reading from multiple buffers +//! ```rust +//! use iovec::{IoVecsInput, IoVector}; +//! use smallvec::smallvec; +//! let buf1 = [1u8, 2, 3]; +//! let buf2 = [4u8, 5, 6]; +//! +//! let iovecs = smallvec![ +//! IoVector { base: buf1.as_ptr() as usize, len: 3 }, +//! IoVector { base: buf2.as_ptr() as usize, len: 3 } +//! ]; +//! +//! let input = IoVecsInput::from_iovecs(iovecs); +//! let collected: Vec = input.read_to_vec(5); // Gets [1, 2, 3, 4, 5] +//! ``` +#![cfg_attr(not(test), no_std)] +extern crate alloc; +use alloc::vec::Vec; +use core::{cmp::min, ptr::copy_nonoverlapping}; +use smallvec::smallvec; +use smallvec::SmallVec; + +type Addr = usize; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +/// Describes a region of memory, beginning at `base` address and with the size of `len` bytes. +pub struct IoVector { + /// memory begin + pub base: Addr, + /// vector size + pub len: usize, +} + +impl IoVector { + /// Checks if the buffer is empty (zero length) + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Advances the buffer position by reducing length and moving base forward + pub fn advance(&mut self, length: usize) { + debug_assert!( + length <= self.len, + "Cannot advance more than the length of the IoVec" + ); + self.base += length; + self.len -= length; + } +} + +impl<'a> IoVector { + /// Converts to an immutable byte slice reference + /// # Safety + /// Caller must ensure the memory is valid and accessible + pub fn into_read_buf(self) -> &'a [u8] { + unsafe { core::slice::from_raw_parts(self.base as *const u8, self.len) } + } + + /// Converts to a mutable byte slice reference + /// # Safety + /// Caller must ensure the memory is valid and writable + pub fn into_write_buf(self) -> &'a mut [u8] { + unsafe { core::slice::from_raw_parts_mut(self.base as *mut u8, self.len) } + } +} + +/// Collection of I/O vectors with small-array optimization: +/// - Stores 1 vector inline (no heap allocation) +/// - Allocates on heap when more vectors are needed +pub type IoVecs = SmallVec<[IoVector; 1]>; + +/// Reads I/O vectors from a pointer (C-style iovec array) +/// # Safety +/// Caller must ensure the pointer and count are valid +pub fn read_iovecs_ptr(iovptr: Addr, iovcnt: usize) -> IoVecs { + if iovcnt == 0 { + return SmallVec::new(); + } + let iovecs = unsafe { core::slice::from_raw_parts(iovptr as *const IoVector, iovcnt) }; + iovecs.iter().copied().collect() +} + +/// Output buffer collection for scatter/gather writes +pub struct IoVecsOutput { + /// I/O vectors (stored in reverse order for efficient popping) + iovecs: IoVecs, + /// Total remaining capacity across all buffers + avaliable: usize, + /// Total bytes written so far + bytes_written: usize, +} + +impl IoVecsOutput { + /// Creates from a set of I/O vectors + pub fn from_iovecs(mut iovecs: IoVecs) -> Self { + // revserse the iovecs to make it easier to pop from the end + iovecs.reverse(); + let mut avaliable = 0; + for iovec in iovecs.iter() { + avaliable += iovec.len; + } + Self { + iovecs, + avaliable, + bytes_written: 0, + } + } + + /// Creates from a single mutable buffer + pub fn from_single_buffer(buf: &mut [u8]) -> Self { + Self::from_iovecs(smallvec![IoVector { + base: buf.as_mut_ptr() as usize, + len: buf.len() + }]) + } + + /// Returns total remaining writable capacity + pub fn avaliable(&self) -> usize { + self.avaliable + } + + /// Writes data sequentially across buffers + /// Returns number of bytes actually written + pub fn write(&mut self, src: &[u8]) -> usize { + let mut bytes_written = 0; + let mut remain_bytes_to_write = src.len(); + let mut src_ptr = src.as_ptr() as *mut u8; + while let Some(mut iovec) = self.iovecs.pop() { + if iovec.is_empty() { + continue; + } + let bytes_len = min(remain_bytes_to_write, iovec.len); + unsafe { copy_nonoverlapping(src_ptr, iovec.base as *mut u8, bytes_len) }; + src_ptr = unsafe { src_ptr.add(bytes_len) }; + iovec.advance(bytes_len); + self.avaliable -= bytes_len; + self.bytes_written += bytes_len; + remain_bytes_to_write -= bytes_len; + bytes_written += bytes_len; + if !iovec.is_empty() { + self.iovecs.push(iovec); + break; + } + } + bytes_written + } +} + +/// Input buffer collection for scatter/gather reads +pub struct IoVecsInput { + iovecs: IoVecs, +} + +impl IoVecsInput { + /// Creates from a set of I/O vectors + pub fn from_iovecs(iovecs: IoVecs) -> Self { + Self { iovecs } + } + + /// Creates from a single buffer + pub fn from_single_buffer(buf: &[u8]) -> Self { + Self::from_iovecs(smallvec![IoVector { + base: buf.as_ptr() as usize, + len: buf.len() + }]) + } + + /// Returns total readable length across all buffers + pub fn total_len(&self) -> usize { + let mut total_len = 0; + for iov in self.iovecs.iter() { + total_len += iov.len; + } + total_len + } + + /// Reads data into a vector (up to maxlen bytes) + pub fn read_to_vec(&self, maxlen: usize) -> Vec { + let mut result = Vec::with_capacity(maxlen.min(self.total_len())); + let mut remaining = maxlen; + + for iov in self.iovecs.iter() { + if remaining == 0 { + break; + } + let read_len = iov.len.min(remaining); + let bytes = unsafe { + let ptr = iov.base as *const u8; + core::slice::from_raw_parts(ptr, read_len) + }; + result.extend_from_slice(bytes); + remaining -= read_len; + } + + result + } + + /// Returns an iterator over buffer slices + pub fn as_slices(&self) -> impl Iterator + '_ { + self.iovecs + .iter() + .map(|iov| unsafe { core::slice::from_raw_parts(iov.base as *const u8, iov.len) }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::mem::MaybeUninit; + + fn create_buffer(size: usize) -> (&'static mut [MaybeUninit], Addr) { + let buf = Box::leak(vec![MaybeUninit::uninit(); size].into_boxed_slice()); + let ptr = buf.as_ptr() as usize; + (buf, ptr) + } + + #[test] + fn test_single_iovec_full_write() { + let (buf, ptr) = create_buffer(5); + let iovecs = SmallVec::from_vec(vec![IoVector { base: ptr, len: 5 }]); + let mut output = IoVecsOutput::from_iovecs(iovecs); + + let src = [1, 2, 3, 4, 5]; + let written = output.write(&src); + + assert_eq!(written, 5); + assert_eq!(output.bytes_written, 5); + assert_eq!(output.avaliable, 0); + + let filled = unsafe { &*(buf as *const _ as *const [u8; 5]) }; + assert_eq!(filled, &[1, 2, 3, 4, 5]); + } + + #[test] + fn test_multiple_iovecs_partial_write() { + let (buf1, ptr1) = create_buffer(5); + let (buf2, ptr2) = create_buffer(5); + + let iovecs = SmallVec::from_vec(vec![ + IoVector { base: ptr1, len: 5 }, + IoVector { base: ptr2, len: 5 }, + ]); + let mut output = IoVecsOutput::from_iovecs(iovecs); + + let src = [1, 2, 3, 4, 5, 6, 7, 8]; + let written = output.write(&src); + + assert_eq!(written, 8); + assert_eq!(output.bytes_written, 8); + assert_eq!(output.avaliable, 2); + + let filled1 = unsafe { &*(buf1 as *const _ as *const [u8; 5]) }; + let filled2 = unsafe { &*(buf2 as *const _ as *const [u8; 3]) }; + assert_eq!(filled1, &[1, 2, 3, 4, 5]); + assert_eq!(filled2, &[6, 7, 8]); + } + + #[test] + fn test_write_more_than_available() { + let (buf, ptr) = create_buffer(3); + let iovecs = SmallVec::from_vec(vec![IoVector { base: ptr, len: 3 }]); + let mut output = IoVecsOutput::from_iovecs(iovecs); + + let src = [1, 2, 3, 4, 5]; + let written = output.write(&src); + + assert_eq!(written, 3); + assert_eq!(output.bytes_written, 3); + assert_eq!(output.avaliable, 0); + let filled = unsafe { &*(buf as *const _ as *const [u8; 3]) }; + assert_eq!(filled, &[1, 2, 3]); + } + + #[test] + fn test_empty_iovecs() { + let iovecs = SmallVec::new(); + let mut output = IoVecsOutput::from_iovecs(iovecs); + let src = [1, 2, 3]; + let written = output.write(&src); + assert_eq!(written, 0); + } + + #[test] + fn test_partial_advance() { + let (buf, ptr) = create_buffer(5); + let mut iovec = IoVector { base: ptr, len: 5 }; + + iovec.advance(2); + assert_eq!(iovec.len, 3); + assert_eq!(iovec.base, ptr + 2); + + let src = [9, 8, 7]; + unsafe { copy_nonoverlapping(src.as_ptr(), iovec.base as *mut u8, 3) }; + let filled = unsafe { &*(buf as *const _ as *const [u8; 5]) }; + assert_eq!(&filled[2..], &[9, 8, 7]); + } + + #[test] + #[should_panic] + fn test_advance_panic() { + let (_, ptr) = create_buffer(5); + let mut iovec = IoVector { base: ptr, len: 3 }; + iovec.advance(5); + } + + #[test] + fn test_multiple_writes_with_partial_fills() { + let (buf1, ptr1) = create_buffer(4); // 长度4 + let (buf2, ptr2) = create_buffer(5); // 长度5 + let (buf3, ptr3) = create_buffer(5); // 长度5 + + let iovecs = SmallVec::from_vec(vec![ + IoVector { base: ptr1, len: 4 }, + IoVector { base: ptr2, len: 5 }, + IoVector { base: ptr3, len: 5 }, + ]); + let mut output = IoVecsOutput::from_iovecs(iovecs); + assert_eq!(output.avaliable, 4 + 5 + 5); + + let src1 = [1, 2, 3, 4, 5]; + let written = output.write(&src1); + assert_eq!(written, 5); // 4+1 + assert_eq!(output.bytes_written, 5); + assert_eq!(output.avaliable, 4 + 5 + 5 - 5); + + let filled1 = unsafe { &*(buf1 as *const _ as *const [u8; 4]) }; + let filled2_part = unsafe { &*(buf2 as *const _ as *const [u8; 1]) }; + assert_eq!(filled1, &[1, 2, 3, 4]); + assert_eq!(filled2_part, &[5]); + + let src2 = [6, 7, 8]; + let written = output.write(&src2); + assert_eq!(written, 3); + assert_eq!(output.bytes_written, 5 + 3); + assert_eq!(output.avaliable, 4 + 5 + 5 - 5 - 3); + + let filled2 = unsafe { &*(buf2 as *const _ as *const [u8; 5]) }; + assert_eq!(&filled2[0..4], &[5, 6, 7, 8]); + + let src3 = [9, 10, 11, 12, 13, 14, 15, 16, 17, 18]; + let written = output.write(&src3); + assert_eq!(written, 6); + assert_eq!(output.bytes_written, 5 + 3 + 6); + assert_eq!(output.avaliable, 0); // 14 - 14 + + let filled2_remain = unsafe { &*(buf2.as_ptr().add(4) as *const u8) }; + assert_eq!(*filled2_remain, 9); + let filled3 = unsafe { &*(buf3 as *const _ as *const [u8; 5]) }; + assert_eq!(filled3, &[10, 11, 12, 13, 14]); // write 5 bytes + + let src4 = [99, 100, 101]; + let written = output.write(&src4); + assert_eq!(written, 0); + } +} diff --git a/crates/lazy_init/src/lib.rs b/crates/lazy_init/src/lib.rs index ef0e929e6..ef65a48be 100644 --- a/crates/lazy_init/src/lib.rs +++ b/crates/lazy_init/src/lib.rs @@ -53,6 +53,12 @@ pub struct LazyInit { unsafe impl Sync for LazyInit {} unsafe impl Send for LazyInit {} +impl Default for LazyInit { + fn default() -> Self { + Self::new() + } +} + impl LazyInit { /// Creates a new uninitialized value. pub const fn new() -> Self { diff --git a/crates/linked_list/src/unsafe_list.rs b/crates/linked_list/src/unsafe_list.rs index 030156208..96075234a 100644 --- a/crates/linked_list/src/unsafe_list.rs +++ b/crates/linked_list/src/unsafe_list.rs @@ -18,6 +18,7 @@ //! example, `dyn Trait`. //! - It would require the list head to be pinned (in addition to the list entries). +use core::ptr; use core::{cell::UnsafeCell, iter, marker::PhantomPinned, mem::MaybeUninit, ptr::NonNull}; /// An intrusive circular doubly-linked list. @@ -103,6 +104,12 @@ unsafe impl Send for List where A::EntryType: Send {} // `Sync` only when its entries are also `Sync`. unsafe impl Sync for List where A::EntryType: Sync {} +impl Default for List { + fn default() -> Self { + Self::new() + } +} + impl List { /// Constructs a new empty list. pub const fn new() -> Self { @@ -251,7 +258,7 @@ impl List { // SAFETY: The safety requirements of this function satisfy those of `insert_after`. unsafe { self.insert_after(self.inner_ref(existing).prev, new) }; - if self.first.unwrap() == existing { + if ptr::addr_eq(self.first.unwrap().as_ptr(), existing.as_ptr()) { // Update the pointer to the first element as we're inserting before it. self.first = Some(NonNull::from(new)); } @@ -439,6 +446,12 @@ unsafe impl Send for Links {} // only when the list entries it points to are also `Sync`. unsafe impl Sync for Links {} +impl Default for Links { + fn default() -> Self { + Self::new() + } +} + impl Links { /// Constructs a new instance of the linked-list links. pub const fn new() -> Self { @@ -468,7 +481,7 @@ impl CommonCursor { if let Some(head) = list.first { // SAFETY: Per the function safety requirements, `cur` is in the list. let links = unsafe { list.inner_ref(cur) }; - if links.next != head { + if !ptr::addr_eq(links.next.as_ptr(), head.as_ptr()) { self.cur = Some(links.next); } } @@ -489,7 +502,7 @@ impl CommonCursor { let next = match self.cur.take() { None => head, Some(cur) => { - if cur == head { + if ptr::addr_eq(cur.as_ptr(), head.as_ptr()) { return; } cur @@ -566,9 +579,9 @@ mod tests { } } + #[allow(clippy::vec_box)] fn build_vector(size: usize) -> Vec> { - let mut v = Vec::new(); - v.reserve(size); + let mut v = Vec::with_capacity(size); for _ in 0..size { v.push(Box::new(Example { links: super::Links::new(), @@ -613,10 +626,10 @@ mod tests { let mut list = super::List::::new(); // Build list. - for j in 0..n { + for vi in v.iter().take(n) { // SAFETY: The entry was allocated above, it's not in any lists yet, is never // moved, and outlives the list. - unsafe { list.push_back(&v[j]) }; + unsafe { list.push_back(vi) }; } // Call the test case. diff --git a/crates/lwip_rust/build.rs b/crates/lwip_rust/build.rs index 1fcdf0c3e..3a9307556 100644 --- a/crates/lwip_rust/build.rs +++ b/crates/lwip_rust/build.rs @@ -1,4 +1,11 @@ -use std::path::PathBuf; +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ fn main() { println!("cargo:rustc-link-lib=lwip"); @@ -28,9 +35,8 @@ fn generate_lwip_bindings() { .generate() .expect("Unable to generate bindings"); - let out_path = PathBuf::from("src"); bindings - .write_to_file(out_path.join("bindings.rs")) + .write_to_file("src/bindings.rs") .expect("Couldn't write bindings!"); } diff --git a/crates/lwip_rust/custom/arch/cc.h b/crates/lwip_rust/custom/arch/cc.h index 081b74f58..6b66e4478 100644 --- a/crates/lwip_rust/custom/arch/cc.h +++ b/crates/lwip_rust/custom/arch/cc.h @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #ifndef __ARCH_CC_H__ #define __ARCH_CC_H__ diff --git a/crates/lwip_rust/custom/custom_pool.c b/crates/lwip_rust/custom/custom_pool.c index da2181b58..2ffd56365 100644 --- a/crates/lwip_rust/custom/custom_pool.c +++ b/crates/lwip_rust/custom/custom_pool.c @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #include "custom_pool.h" #include "lwip/memp.h" #include "lwip/pbuf.h" diff --git a/crates/lwip_rust/custom/custom_pool.h b/crates/lwip_rust/custom/custom_pool.h index 66ccde732..ccfc01a30 100644 --- a/crates/lwip_rust/custom/custom_pool.h +++ b/crates/lwip_rust/custom/custom_pool.h @@ -1,3 +1,11 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ #ifndef __CUSTOM_POOL_H__ #define __CUSTOM_POOL_H__ diff --git a/crates/lwip_rust/custom/lwipopts.h b/crates/lwip_rust/custom/lwipopts.h index fefe19fce..ea1d2b396 100644 --- a/crates/lwip_rust/custom/lwipopts.h +++ b/crates/lwip_rust/custom/lwipopts.h @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #ifndef __LWIPOPTS_H__ #define __LWIPOPTS_H__ @@ -25,8 +34,8 @@ #define LWIP_TCP 1 #define LWIP_CALLBACK_API 1 #define LWIP_NETIF_API 0 -#define LWIP_NETIF_LOOPBACK 0 -#define LWIP_HAVE_LOOPIF 1 +#define LWIP_NETIF_LOOPBACK 1 +#define LWIP_HAVE_LOOPIF 0 #define LWIP_HAVE_SLIPIF 0 #define LWIP_NETCONN 0 #define LWIP_SOCKET 0 diff --git a/crates/lwip_rust/src/lib.rs b/crates/lwip_rust/src/lib.rs index 77a366662..8a744b839 100644 --- a/crates/lwip_rust/src/lib.rs +++ b/crates/lwip_rust/src/lib.rs @@ -1,7 +1,17 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #![no_std] #![allow(dead_code)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(missing_docs)] +#[rustfmt::skip] pub mod bindings; diff --git a/crates/lwip_rust/wrapper.h b/crates/lwip_rust/wrapper.h index eeac68cf2..d05e4f2ef 100644 --- a/crates/lwip_rust/wrapper.h +++ b/crates/lwip_rust/wrapper.h @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #include "depend/lwip/src/include/lwip/def.h" #include "depend/lwip/src/include/lwip/dns.h" #include "depend/lwip/src/include/lwip/etharp.h" diff --git a/crates/page_table/src/bits64.rs b/crates/page_table/src/bits64.rs index 8cd8ac896..f1c906ce3 100644 --- a/crates/page_table/src/bits64.rs +++ b/crates/page_table/src/bits64.rs @@ -196,10 +196,7 @@ impl PageTable64 { PageSize::Size4K }; self.map(vaddr, paddr, page_size, flags).inspect_err(|e| { - error!( - "failed to map page: {:#x?}({:?}) -> {:#x?}, {:?}", - vaddr, page_size, paddr, e - ) + error!("failed to map page: {vaddr:#x?}({page_size:?}) -> {paddr:#x?}, {e:?}") })?; vaddr += page_size as usize; paddr += page_size as usize; @@ -224,7 +221,7 @@ impl PageTable64 { while size > 0 { let (_, page_size) = self .unmap(vaddr) - .inspect_err(|e| error!("failed to unmap page: {:#x?}, {:?}", vaddr, e))?; + .inspect_err(|e| error!("failed to unmap page: {vaddr:#x?}, {e:?}"))?; assert!(vaddr.is_aligned(page_size)); assert!(page_size as usize <= size); vaddr += page_size as usize; diff --git a/crates/page_table_entry/Cargo.toml b/crates/page_table_entry/Cargo.toml index c42558047..9e1c7b155 100644 --- a/crates/page_table_entry/Cargo.toml +++ b/crates/page_table_entry/Cargo.toml @@ -11,9 +11,9 @@ documentation = "https://rcore-os.github.io/arceos/page_table_entry/index.html" [dependencies] log = "0.4" -bitflags = "2.2" +bitflags = { workspace = true } memory_addr = "0.1.0" aarch64-cpu = "9.3" # TODO: put it in [target.'cfg(target_arch = "aarch64")'.dependencies] [target.'cfg(target_arch = "x86_64")'.dependencies] -x86_64 = "0.14" +x86_64 = "0.14.13" diff --git a/crates/percpu/Cargo.toml b/crates/percpu/Cargo.toml deleted file mode 100644 index 25f82144e..000000000 --- a/crates/percpu/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "percpu" -version = "0.1.0" -edition = "2021" -authors = ["Yuekai Jia "] -description = "Define and access per-CPU data structures" -license = "GPL-3.0-or-later OR Apache-2.0" -homepage = "https://github.com/rcore-os/arceos" -repository = "https://github.com/rcore-os/arceos/tree/main/crates/percpu" -documentation = "https://rcore-os.github.io/arceos/percpu/index.html" - -[features] -# For single CPU use, just make the per-CPU data a global variable. -sp-naive = ["percpu_macros/sp-naive"] - -# Whether the system enables preemption. -preempt = ["percpu_macros/preempt", "dep:kernel_guard"] - -default = [] - -[dependencies] -cfg-if = "1.0" -kernel_guard = { version = "0.1.0", optional = true } -percpu_macros = { path = "../percpu_macros" } - -[target.'cfg(target_arch = "x86_64")'.dependencies] -x86 = "0.52" - -[target.'cfg(not(target_os = "none"))'.dependencies] -spin = "0.9" diff --git a/crates/percpu/build.rs b/crates/percpu/build.rs deleted file mode 100644 index 926b4c3f5..000000000 --- a/crates/percpu/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::path::Path; - -fn main() { - if cfg!(target_os = "linux") && cfg!(not(feature = "sp-naive")) { - let ld_script_path = Path::new(std::env!("CARGO_MANIFEST_DIR")).join("test_percpu.x"); - println!("cargo:rustc-link-arg-tests=-no-pie"); - println!("cargo:rustc-link-arg-tests=-T{}", ld_script_path.display()); - } -} diff --git a/crates/percpu/src/imp.rs b/crates/percpu/src/imp.rs deleted file mode 100644 index aa7aeed99..000000000 --- a/crates/percpu/src/imp.rs +++ /dev/null @@ -1,134 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -const fn align_up(val: usize) -> usize { - const PAGE_SIZE: usize = 0x1000; - (val + PAGE_SIZE - 1) & !(PAGE_SIZE - 1) -} - -#[cfg(not(target_os = "none"))] -static PERCPU_AREA_BASE: spin::once::Once = spin::once::Once::new(); - -/// Returns the per-CPU data area size for one CPU. -#[doc(cfg(not(feature = "sp-naive")))] -pub fn percpu_area_size() -> usize { - extern "C" { - fn _percpu_load_start(); - fn _percpu_load_end(); - } - use percpu_macros::percpu_symbol_offset; - percpu_symbol_offset!(_percpu_load_end) - percpu_symbol_offset!(_percpu_load_start) -} - -/// Returns the base address of the per-CPU data area on the given CPU. -/// -/// if `cpu_id` is 0, it returns the base address of all per-CPU data areas. -#[doc(cfg(not(feature = "sp-naive")))] -pub fn percpu_area_base(cpu_id: usize) -> usize { - cfg_if::cfg_if! { - if #[cfg(target_os = "none")] { - extern "C" { - fn _percpu_start(); - } - let base = _percpu_start as usize; - } else { - let base = *PERCPU_AREA_BASE.get().unwrap(); - } - } - base + cpu_id * align_up(percpu_area_size()) -} - -/// Initialize the per-CPU data area for `max_cpu_num` CPUs. -pub fn init(max_cpu_num: usize) { - let size = percpu_area_size(); - - #[cfg(target_os = "linux")] - { - // we not load the percpu section in ELF, allocate them here. - let total_size = align_up(size) * max_cpu_num; - let layout = std::alloc::Layout::from_size_align(total_size, 0x1000).unwrap(); - PERCPU_AREA_BASE.call_once(|| unsafe { std::alloc::alloc(layout) as usize }); - } - - let base = percpu_area_base(0); - for i in 1..max_cpu_num { - let secondary_base = percpu_area_base(i); - // copy per-cpu data of the primary CPU to other CPUs. - unsafe { - core::ptr::copy_nonoverlapping(base as *const u8, secondary_base as *mut u8, size); - } - } -} - -/// Read the architecture-specific thread pointer register on the current CPU. -pub fn get_local_thread_pointer() -> usize { - let tp; - unsafe { - cfg_if::cfg_if! { - if #[cfg(target_arch = "x86_64")] { - tp = if cfg!(target_os = "linux") { - SELF_PTR.read_current_raw() - } else if cfg!(target_os = "none") { - x86::msr::rdmsr(x86::msr::IA32_GS_BASE) as usize - } else { - unimplemented!() - }; - } else if #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] { - core::arch::asm!("mv {}, gp", out(reg) tp) - } else if #[cfg(target_arch = "aarch64")] { - core::arch::asm!("mrs {}, TPIDR_EL1", out(reg) tp) - } - } - } - tp -} - -/// Set the architecture-specific thread pointer register to the per-CPU data -/// area base on the current CPU. -/// -/// `cpu_id` indicates which per-CPU data area to use. -pub fn set_local_thread_pointer(cpu_id: usize) { - let tp = percpu_area_base(cpu_id); - unsafe { - cfg_if::cfg_if! { - if #[cfg(target_arch = "x86_64")] { - if cfg!(target_os = "linux") { - const ARCH_SET_GS: u32 = 0x1001; - const SYS_ARCH_PRCTL: u32 = 158; - core::arch::asm!( - "syscall", - in("eax") SYS_ARCH_PRCTL, - in("edi") ARCH_SET_GS, - in("rsi") tp, - ); - } else if cfg!(target_os = "none") { - x86::msr::wrmsr(x86::msr::IA32_GS_BASE, tp as u64); - } else { - unimplemented!() - } - SELF_PTR.write_current_raw(tp); - } else if #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] { - core::arch::asm!("mv gp, {}", in(reg) tp) - } else if #[cfg(target_arch = "aarch64")] { - core::arch::asm!("msr TPIDR_EL1, {}", in(reg) tp) - } - } - } -} - -/// To use `percpu::__priv::NoPreemptGuard::new()` in macro expansion. -#[allow(unused_imports)] -#[cfg(feature = "preempt")] -use crate as percpu; - -/// On x86, we use `gs:SELF_PTR` to store the address of the per-CPU data area base. -#[cfg(target_arch = "x86_64")] -#[no_mangle] -#[percpu_macros::def_percpu] -static SELF_PTR: usize = 0; diff --git a/crates/percpu/src/lib.rs b/crates/percpu/src/lib.rs deleted file mode 100644 index 758582199..000000000 --- a/crates/percpu/src/lib.rs +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -//! Define and access per-CPU data structures. -//! -//! All per-CPU data is placed into several contiguous memory regions called -//! **per-CPU data areas**, the number of which is the number of CPUs. Each CPU -//! has its own per-CPU data area. The architecture-specific thread pointer -//! register (e.g., `GS_BASE` on x86_64) is set to the base address of the area -//! on initialization. -//! -//! When accessing the per-CPU data on the current CPU, it first use the thread -//! pointer register to obtain the corresponding per-CPU data area, and then add -//! an offset to access the corresponding field. -//! -//! # Notes -//! -//! Since RISC-V does not provide separate thread pointer registers for user and -//! kernel mode, we temporarily use the `gp` register to point to the per-CPU data -//! area, while the `tp` register is used for thread-local storage. -//! -//! # Examples -//! -//! ```no_run -//! #[percpu::def_percpu] -//! static CPU_ID: usize = 0; -//! -//! // initialize per-CPU data for 4 CPUs. -//! percpu::init(4); -//! // set the thread pointer register to the per-CPU data area 0. -//! percpu::set_local_thread_pointer(0); -//! -//! // access the per-CPU data `CPU_ID` on the current CPU. -//! println!("{}", CPU_ID.read_current()); // prints "0" -//! CPU_ID.write_current(1); -//! println!("{}", CPU_ID.read_current()); // prints "1" -//! ``` -//! -//! # Cargo Features -//! -//! - `sp-naive`: For **single-core** use. In this case, each per-CPU data is -//! just a global variable, architecture-specific thread pointer register is -//! not used. -//! - `preempt`: For **preemptible** system use. In this case, we need to disable -//! preemption when accessing per-CPU data. Otherwise, the data may be corrupted -//! when it's being accessing and the current thread happens to be preempted. - -#![cfg_attr(target_os = "none", no_std)] -#![feature(doc_cfg)] - -extern crate percpu_macros; - -#[cfg_attr(feature = "sp-naive", path = "naive.rs")] -mod imp; - -pub use self::imp::*; -pub use percpu_macros::def_percpu; - -#[doc(hidden)] -pub mod __priv { - #[cfg(feature = "preempt")] - pub use kernel_guard::NoPreempt as NoPreemptGuard; -} - -cfg_if::cfg_if! { - if #[cfg(doc)] { - /// Example per-CPU data for documentation only. - #[doc(cfg(doc))] - #[def_percpu] - pub static EXAMPLE_PERCPU_DATA: usize = 0; - } -} diff --git a/crates/percpu/src/naive.rs b/crates/percpu/src/naive.rs deleted file mode 100644 index 432a13321..000000000 --- a/crates/percpu/src/naive.rs +++ /dev/null @@ -1,19 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -/// No effect for "sp-naive" use. -pub fn init(_max_cpu_num: usize) {} - -/// Always returns `0` for "sp-naive" use. -pub fn get_local_thread_pointer() -> usize { - 0 -} - -/// No effect for "sp-naive" use. -pub fn set_local_thread_pointer(_cpu_id: usize) {} diff --git a/crates/percpu/test_percpu.x b/crates/percpu/test_percpu.x deleted file mode 100644 index 89dd5ba93..000000000 --- a/crates/percpu/test_percpu.x +++ /dev/null @@ -1,19 +0,0 @@ -CPU_NUM = 4; - -SECTIONS -{ - . = ALIGN(4K); - _percpu_start = .; - .percpu 0x0 (NOLOAD) : AT(_percpu_start) { - _percpu_load_start = .; - *(.percpu .percpu.*) - _percpu_load_end = .; - . = ALIGN(64); - _percpu_size_aligned = .; - - . = _percpu_load_start + _percpu_size_aligned * CPU_NUM; - } - . = _percpu_start + SIZEOF(.percpu); - _percpu_end = .; -} -INSERT BEFORE .bss; diff --git a/crates/percpu/tests/test_percpu.rs b/crates/percpu/tests/test_percpu.rs deleted file mode 100644 index c5832092e..000000000 --- a/crates/percpu/tests/test_percpu.rs +++ /dev/null @@ -1,111 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#![cfg(not(target_os = "macos"))] - -use percpu::*; - -// Initial value is unsupported for testing. - -#[def_percpu] -static BOOL: bool = false; - -#[def_percpu] -static U8: u8 = 0; - -#[def_percpu] -static U16: u16 = 0; - -#[def_percpu] -static U32: u32 = 0; - -#[def_percpu] -static U64: u64 = 0; - -#[def_percpu] -static USIZE: usize = 0; - -struct Struct { - foo: usize, - bar: u8, -} - -#[def_percpu] -static STRUCT: Struct = Struct { foo: 0, bar: 0 }; - -#[cfg(target_os = "linux")] -#[test] -fn test_percpu() { - println!("feature = \"sp-naive\": {}", cfg!(feature = "sp-naive")); - - #[cfg(feature = "sp-naive")] - let base = 0; - - #[cfg(not(feature = "sp-naive"))] - let base = { - init(4); - set_local_thread_pointer(0); - - let base = get_local_thread_pointer(); - println!("per-CPU area base = {:#x}", base); - println!("per-CPU area size = {}", percpu_area_size()); - base - }; - - println!("bool offset: {:#x}", BOOL.offset()); - println!("u8 offset: {:#x}", U8.offset()); - println!("u16 offset: {:#x}", U16.offset()); - println!("u32 offset: {:#x}", U32.offset()); - println!("u64 offset: {:#x}", U64.offset()); - println!("usize offset: {:#x}", USIZE.offset()); - println!("struct offset: {:#x}", STRUCT.offset()); - println!(); - - unsafe { - assert_eq!(base + BOOL.offset(), BOOL.current_ptr() as usize); - assert_eq!(base + U8.offset(), U8.current_ptr() as usize); - assert_eq!(base + U16.offset(), U16.current_ptr() as usize); - assert_eq!(base + U32.offset(), U32.current_ptr() as usize); - assert_eq!(base + U64.offset(), U64.current_ptr() as usize); - assert_eq!(base + USIZE.offset(), USIZE.current_ptr() as usize); - assert_eq!(base + STRUCT.offset(), STRUCT.current_ptr() as usize); - } - - BOOL.write_current(true); - U8.write_current(123); - U16.write_current(0xabcd); - U32.write_current(0xdead_beef); - U64.write_current(0xa2ce_a2ce_a2ce_a2ce); - USIZE.write_current(0xffff_0000); - - STRUCT.with_current(|s| { - s.foo = 0x2333; - s.bar = 100; - }); - - println!("bool value: {}", BOOL.read_current()); - println!("u8 value: {}", U8.read_current()); - println!("u16 value: {:#x}", U16.read_current()); - println!("u32 value: {:#x}", U32.read_current()); - println!("u64 value: {:#x}", U64.read_current()); - println!("usize value: {:#x}", USIZE.read_current()); - - assert_eq!(U8.read_current(), 123); - assert_eq!(U16.read_current(), 0xabcd); - assert_eq!(U32.read_current(), 0xdead_beef); - assert_eq!(U64.read_current(), 0xa2ce_a2ce_a2ce_a2ce); - assert_eq!(USIZE.read_current(), 0xffff_0000); - - STRUCT.with_current(|s| { - println!("struct.foo value: {:#x}", s.foo); - println!("struct.bar value: {}", s.bar); - assert_eq!(s.foo, 0x2333); - assert_eq!(s.bar, 100); - }); -} diff --git a/crates/percpu_macros/Cargo.toml b/crates/percpu_macros/Cargo.toml deleted file mode 100644 index 1f6963fbf..000000000 --- a/crates/percpu_macros/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "percpu_macros" -version = "0.1.0" -edition = "2021" -authors = ["Yuekai Jia "] -description = "Macros to define and access a per-CPU data structure" -license = "GPL-3.0-or-later OR Apache-2.0" -homepage = "https://github.com/rcore-os/arceos" -repository = "https://github.com/rcore-os/arceos/tree/main/crates/percpu_macros" -documentation = "https://rcore-os.github.io/arceos/percpu_macros/index.html" - -[features] -# For single CPU use, just make the per-CPU data a global variable. -sp-naive = [] - -# Whether the system enables preemption. -preempt = [] - -default = [] - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "2.0", features = ["full"] } - -[lib] -proc-macro = true diff --git a/crates/percpu_macros/src/arch.rs b/crates/percpu_macros/src/arch.rs deleted file mode 100644 index 89c9a97bc..000000000 --- a/crates/percpu_macros/src/arch.rs +++ /dev/null @@ -1,204 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use quote::{format_ident, quote}; -use syn::{Ident, Type}; - -fn macos_unimplemented(item: proc_macro2::TokenStream) -> proc_macro2::TokenStream { - quote! { - #[cfg(not(target_os = "macos"))] - { #item } - #[cfg(target_os = "macos")] - unimplemented!() - } -} - -pub fn gen_offset(symbol: &Ident) -> proc_macro2::TokenStream { - quote! { - let value: usize; - unsafe { - #[cfg(target_arch = "x86_64")] - ::core::arch::asm!( - "movabs {0}, offset {VAR}", - out(reg) value, - VAR = sym #symbol, - ); - #[cfg(target_arch = "aarch64")] - ::core::arch::asm!( - "movz {0}, #:abs_g0_nc:{VAR}", - out(reg) value, - VAR = sym #symbol, - ); - #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] - ::core::arch::asm!( - "lui {0}, %hi({VAR})", - "addi {0}, {0}, %lo({VAR})", - out(reg) value, - VAR = sym #symbol, - ); - } - value - } -} - -pub fn gen_current_ptr(symbol: &Ident, ty: &Type) -> proc_macro2::TokenStream { - macos_unimplemented(quote! { - let base: usize; - #[cfg(target_arch = "x86_64")] - { - // `__PERCPU_SELF_PTR` stores GS_BASE, which is defined in crate `percpu`. - ::core::arch::asm!( - "mov {0}, gs:[offset __PERCPU_SELF_PTR]", - "add {0}, offset {VAR}", - out(reg) base, - VAR = sym #symbol, - ); - base as *const #ty - } - #[cfg(not(target_arch = "x86_64"))] - { - #[cfg(target_arch = "aarch64")] - ::core::arch::asm!("mrs {}, TPIDR_EL1", out(reg) base); - #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] - ::core::arch::asm!("mv {}, gp", out(reg) base); - (base + self.offset()) as *const #ty - } - }) -} - -pub fn gen_read_current_raw(symbol: &Ident, ty: &Type) -> proc_macro2::TokenStream { - let ty_str = quote!(#ty).to_string(); - let rv64_op = match ty_str.as_str() { - "bool" => "lbu", - "u8" => "lbu", - "u16" => "lhu", - "u32" => "lwu", - "u64" => "ld", - "usize" => "ld", - _ => unreachable!(), - }; - let rv64_asm = quote! { - ::core::arch::asm!( - "lui {0}, %hi({VAR})", - "add {0}, {0}, gp", - concat!(#rv64_op, " {0}, %lo({VAR})({0})"), - out(reg) value, - VAR = sym #symbol, - ) - }; - - let (x64_asm, x64_reg) = if ["bool", "u8"].contains(&ty_str.as_str()) { - ( - "mov {0}, byte ptr gs:[offset {VAR}]".into(), - format_ident!("reg_byte"), - ) - } else { - let (x64_mod, x64_ptr) = match ty_str.as_str() { - "u16" => ("x", "word"), - "u32" => ("e", "dword"), - "u64" => ("r", "qword"), - "usize" => ("r", "qword"), - _ => unreachable!(), - }; - ( - format!("mov {{0:{x64_mod}}}, {x64_ptr} ptr gs:[offset {{VAR}}]"), - format_ident!("reg"), - ) - }; - let x64_asm = quote! { - ::core::arch::asm!(#x64_asm, out(#x64_reg) value, VAR = sym #symbol) - }; - - let gen_code = |asm_stmt| { - if ty_str.as_str() == "bool" { - quote! { - let value: u8; - #asm_stmt; - value != 0 - } - } else { - quote! { - let value: #ty; - #asm_stmt; - value - } - } - }; - - let rv64_code = gen_code(rv64_asm); - let x64_code = gen_code(x64_asm); - macos_unimplemented(quote! { - #[cfg(target_arch = "riscv64")] - { #rv64_code } - #[cfg(target_arch = "x86_64")] - { #x64_code } - #[cfg(not(any(target_arch = "riscv64", target_arch = "x86_64")))] - { *self.current_ptr() } - }) -} - -pub fn gen_write_current_raw(symbol: &Ident, val: &Ident, ty: &Type) -> proc_macro2::TokenStream { - let ty_str = quote!(#ty).to_string(); - let ty_fixup = if ty_str.as_str() == "bool" { - format_ident!("u8") - } else { - format_ident!("{}", ty_str) - }; - - let rv64_op = match ty_str.as_str() { - "bool" => "sb", - "u8" => "sb", - "u16" => "sh", - "u32" => "sw", - "u64" => "sd", - "usize" => "sd", - _ => unreachable!(), - }; - let rv64_code = quote! { - ::core::arch::asm!( - "lui {0}, %hi({VAR})", - "add {0}, {0}, gp", - concat!(#rv64_op, " {1}, %lo({VAR})({0})"), - out(reg) _, - in(reg) #val as #ty_fixup, - VAR = sym #symbol, - ); - }; - - let (x64_asm, x64_reg) = if ["bool", "u8"].contains(&ty_str.as_str()) { - ( - "mov byte ptr gs:[offset {VAR}], {0}".into(), - format_ident!("reg_byte"), - ) - } else { - let (x64_mod, x64_ptr) = match ty_str.as_str() { - "u16" => ("x", "word"), - "u32" => ("e", "dword"), - "u64" => ("r", "qword"), - "usize" => ("r", "qword"), - _ => unreachable!(), - }; - ( - format!("mov {x64_ptr} ptr gs:[offset {{VAR}}], {{0:{x64_mod}}}"), - format_ident!("reg"), - ) - }; - let x64_code = quote! { - ::core::arch::asm!(#x64_asm, in(#x64_reg) #val as #ty_fixup, VAR = sym #symbol) - }; - - macos_unimplemented(quote! { - #[cfg(target_arch = "riscv64")] - { #rv64_code } - #[cfg(target_arch = "x86_64")] - { #x64_code } - #[cfg(not(any(target_arch = "riscv64", target_arch = "x86_64")))] - { *(self.current_ptr() as *mut #ty) = #val } - }) -} diff --git a/crates/percpu_macros/src/lib.rs b/crates/percpu_macros/src/lib.rs deleted file mode 100644 index e8ff57333..000000000 --- a/crates/percpu_macros/src/lib.rs +++ /dev/null @@ -1,184 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -//! Macros to define and access a per-CPU data structure. -//! -//! **DO NOT** use this crate directly. Use the [percpu] crate instead. -//! -//! [percpu]: ../percpu/index.html - -#![feature(doc_cfg)] - -use proc_macro::TokenStream; -use proc_macro2::Span; -use quote::{format_ident, quote}; -use syn::{Error, ItemStatic}; - -#[cfg_attr(feature = "sp-naive", path = "naive.rs")] -mod arch; - -fn compiler_error(err: Error) -> TokenStream { - err.to_compile_error().into() -} - -/// Defines a per-CPU data structure. -/// -/// It should be used on a `static` variable. -/// -/// See the [crate-level documentation](../percpu/index.html) for more details. -#[proc_macro_attribute] -pub fn def_percpu(attr: TokenStream, item: TokenStream) -> TokenStream { - if !attr.is_empty() { - return compiler_error(Error::new( - Span::call_site(), - "expect an empty attribute: `#[def_percpu]`", - )); - } - - let ast = syn::parse_macro_input!(item as ItemStatic); - - let attrs = &ast.attrs; - let vis = &ast.vis; - let name = &ast.ident; - let ty = &ast.ty; - let init_expr = &ast.expr; - - let inner_symbol_name = &format_ident!("__PERCPU_{}", name); - let struct_name = &format_ident!("{}_WRAPPER", name); - - let ty_str = quote!(#ty).to_string(); - let is_primitive_int = ["bool", "u8", "u16", "u32", "u64", "usize"].contains(&ty_str.as_str()); - - let no_preempt_guard = if cfg!(feature = "preempt") { - quote! { let _guard = percpu::__priv::NoPreemptGuard::new(); } - } else { - quote! {} - }; - - // Do not generate `fn read_current()`, `fn write_current()`, etc for non primitive types. - let read_write_methods = if is_primitive_int { - let read_current_raw = arch::gen_read_current_raw(inner_symbol_name, ty); - let write_current_raw = - arch::gen_write_current_raw(inner_symbol_name, &format_ident!("val"), ty); - - quote! { - /// Returns the value of the per-CPU data on the current CPU. - /// - /// # Safety - /// - /// Caller must ensure that preemption is disabled on the current CPU. - #[inline] - pub unsafe fn read_current_raw(&self) -> #ty { - #read_current_raw - } - - /// Set the value of the per-CPU data on the current CPU. - /// - /// # Safety - /// - /// Caller must ensure that preemption is disabled on the current CPU. - #[inline] - pub unsafe fn write_current_raw(&self, val: #ty) { - #write_current_raw - } - - /// Returns the value of the per-CPU data on the current CPU. Preemption will - /// be disabled during the call. - pub fn read_current(&self) -> #ty { - #no_preempt_guard - unsafe { self.read_current_raw() } - } - - /// Set the value of the per-CPU data on the current CPU. Preemption will - /// be disabled during the call. - pub fn write_current(&self, val: #ty) { - #no_preempt_guard - unsafe { self.write_current_raw(val) } - } - } - } else { - quote! {} - }; - - let offset = arch::gen_offset(inner_symbol_name); - let current_ptr = arch::gen_current_ptr(inner_symbol_name, ty); - quote! { - #[cfg_attr(not(target_os = "macos"), link_section = ".percpu")] // unimplemented on macos - #(#attrs)* - static mut #inner_symbol_name: #ty = #init_expr; - - #[doc = concat!("Wrapper struct for the per-CPU data [`", stringify!(#name), "`]")] - #[allow(non_camel_case_types)] - #vis struct #struct_name {} - - #(#attrs)* - #vis static #name: #struct_name = #struct_name {}; - - impl #struct_name { - /// Returns the offset relative to the per-CPU data area base on the current CPU. - #[inline] - pub fn offset(&self) -> usize { - #offset - } - - /// Returns the raw pointer of this per-CPU data on the current CPU. - /// - /// # Safety - /// - /// Caller must ensure that preemption is disabled on the current CPU. - #[inline] - pub unsafe fn current_ptr(&self) -> *const #ty { - #current_ptr - } - - /// Returns the reference of the per-CPU data on the current CPU. - /// - /// # Safety - /// - /// Caller must ensure that preemption is disabled on the current CPU. - #[inline] - pub unsafe fn current_ref_raw(&self) -> &#ty { - &*self.current_ptr() - } - - /// Returns the mutable reference of the per-CPU data on the current CPU. - /// - /// # Safety - /// - /// Caller must ensure that preemption is disabled on the current CPU. - #[inline] - #[allow(clippy::mut_from_ref)] - pub unsafe fn current_ref_mut_raw(&self) -> &mut #ty { - &mut *(self.current_ptr() as *mut #ty) - } - - /// Manipulate the per-CPU data on the current CPU in the given closure. - /// Preemption will be disabled during the call. - pub fn with_current(&self, f: F) -> T - where - F: FnOnce(&mut #ty) -> T, - { - #no_preempt_guard - f(unsafe { self.current_ref_mut_raw() }) - } - - #read_write_methods - } - } - .into() -} - -#[doc(hidden)] -#[cfg(not(feature = "sp-naive"))] -#[proc_macro] -pub fn percpu_symbol_offset(item: TokenStream) -> TokenStream { - let symbol = &format_ident!("{}", item.to_string()); - let offset = arch::gen_offset(symbol); - quote!({ #offset }).into() -} diff --git a/crates/percpu_macros/src/naive.rs b/crates/percpu_macros/src/naive.rs deleted file mode 100644 index a8490d69e..000000000 --- a/crates/percpu_macros/src/naive.rs +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -//! For single CPU use, we just make the per-CPU data a global variable. - -use quote::quote; -use syn::{Ident, Type}; - -pub fn gen_offset(symbol: &Ident) -> proc_macro2::TokenStream { - quote! { - unsafe { ::core::ptr::addr_of!(#symbol) as usize } - } -} - -pub fn gen_current_ptr(symbol: &Ident, _ty: &Type) -> proc_macro2::TokenStream { - quote! { - unsafe { ::core::ptr::addr_of!(#symbol) } - } -} - -pub fn gen_read_current_raw(_symbol: &Ident, _ty: &Type) -> proc_macro2::TokenStream { - quote! { - *self.current_ptr() - } -} - -pub fn gen_write_current_raw(_symbol: &Ident, val: &Ident, ty: &Type) -> proc_macro2::TokenStream { - quote! { - *(self.current_ptr() as *mut #ty) = #val - } -} diff --git a/crates/ratio/src/lib.rs b/crates/ratio/src/lib.rs index 5362c609b..1eac58a85 100644 --- a/crates/ratio/src/lib.rs +++ b/crates/ratio/src/lib.rs @@ -178,10 +178,10 @@ mod tests { assert_eq!(c.shift, 0); assert_eq!(c.mul_trunc(u32::MAX as _), u32::MAX as _); - println!("{:?}", a); - println!("{:?}", b); - println!("{:?}", c); - println!("{:?}", d); + println!("{a:?}"); + println!("{b:?}"); + println!("{c:?}"); + println!("{d:?}"); } #[test] diff --git a/crates/ringbuffer/Cargo.toml b/crates/ringbuffer/Cargo.toml new file mode 100644 index 000000000..56daa0c36 --- /dev/null +++ b/crates/ringbuffer/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "ringbuffer" +version = "0.1.0" +edition = "2021" +authors = ["RuiWei Cheng <1982833213@qq.com>", "Quan Shi <749990226@qq.com>"] +description = "A data structure that uses a single, fixed-size buffer as if it were connected end-to-end" +license = "GPL-3.0-or-later OR Apache-2.0" +homepage = "https://github.com/syswonder/ruxos" +repository = "https://github.com/syswonder/ruxos/tree/dev/crates/ringbuffer" + +[build-dependencies] diff --git a/crates/ringbuffer/src/lib.rs b/crates/ringbuffer/src/lib.rs new file mode 100644 index 000000000..769ebcf5f --- /dev/null +++ b/crates/ringbuffer/src/lib.rs @@ -0,0 +1,469 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! A circular buffer (ring buffer) implementation for efficient FIFO operations. +#![cfg_attr(not(test), no_std)] + +extern crate alloc; + +use alloc::{vec, vec::Vec}; +use core::cmp; + +/// Represents the current state of the ring buffer +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +enum RingBufferState { + #[default] + /// Buffer contains no data + Empty, + /// Buffer is completely full + Full, + /// Buffer has data but isn't full + Normal, +} + +/// A circular buffer implementation using a `Vec` as backing storage +pub struct RingBuffer { + /// Underlying data storage + arr: Vec, + // NOTE: When and only when `head` equals `tail`, `state` can only be `Full` or `Empty`. + /// Index of the next element to read + head: usize, + /// Index of the next element to write + tail: usize, + /// Current buffer state + state: RingBufferState, +} + +impl RingBuffer { + /// Creates a new RingBuffer with the specified capacity + /// + /// # Arguments + /// * `len` - Capacity of the buffer (must be greater than 0) + /// + /// # Panics + /// Panics if `len` is 0 + pub fn new(len: usize) -> Self { + assert!(len > 0, "Buffer length must be positive"); + Self { + arr: vec![0; len], + head: 0, + tail: 0, + state: RingBufferState::Empty, + } + } + + /// Returns true if the buffer contains no data + pub fn is_empty(&self) -> bool { + self.state == RingBufferState::Empty + } + + /// Returns true if the buffer has no free space + pub fn is_full(&self) -> bool { + self.state == RingBufferState::Full + } + + /// Read as much as possible to fill `dst`. + /// + /// # Arguments + /// * `dst` - Destination buffer for read data + /// + /// # Returns + /// Number of bytes actually written + pub fn read(&mut self, dst: &mut [u8]) -> usize { + if self.state == RingBufferState::Empty || dst.is_empty() { + return 0; + } + + let ret_len; + let n = self.arr.len(); + if self.head < self.tail { + ret_len = cmp::min(self.tail - self.head, dst.len()); + dst[..ret_len].copy_from_slice(&self.arr[self.head..self.head + ret_len]); + } else { + // also handles full + ret_len = cmp::min(n - self.head + self.tail, dst.len()); + if ret_len <= (n - self.head) { + dst[..ret_len].copy_from_slice(&self.arr[self.head..self.head + ret_len]); + } else { + let right_len = n - self.head; + dst[..right_len].copy_from_slice(&self.arr[self.head..]); + dst[right_len..ret_len].copy_from_slice(&self.arr[..(ret_len - right_len)]); + } + } + self.head = (self.head + ret_len) % n; + + if self.head == self.tail { + self.state = RingBufferState::Empty; + } else { + self.state = RingBufferState::Normal; + } + + ret_len + } + + /// Write as much as possible to fill the ring buffer. + /// + /// # Arguments + /// * `src` - Source buffer containing data to write + /// + /// # Returns + /// Number of bytes actually written + pub fn write(&mut self, src: &[u8]) -> usize { + if self.state == RingBufferState::Full || src.is_empty() { + return 0; + } + + let ret_len; + let n = self.arr.len(); + if self.head <= self.tail { + // also handles empty + ret_len = cmp::min(n - (self.tail - self.head), src.len()); + if ret_len <= (n - self.tail) { + self.arr[self.tail..self.tail + ret_len].copy_from_slice(&src[..ret_len]); + } else { + self.arr[self.tail..].copy_from_slice(&src[..n - self.tail]); + self.arr[..(ret_len - (n - self.tail))] + .copy_from_slice(&src[n - self.tail..ret_len]); + } + } else { + ret_len = cmp::min(self.head - self.tail, src.len()); + self.arr[self.tail..self.tail + ret_len].copy_from_slice(&src[..ret_len]); + } + self.tail = (self.tail + ret_len) % n; + + if self.head == self.tail { + self.state = RingBufferState::Full; + } else { + self.state = RingBufferState::Normal; + } + + ret_len + } + + /// Removes and returns the next byte from the buffer + /// + /// # Returns + /// `Some(byte)` if available, `None` if buffer is empty + pub fn dequeue(&mut self) -> Option { + if self.is_empty() { + return None; + } + + let n = self.arr.len(); + let c = self.arr[self.head]; + self.head = (self.head + 1) % n; + if self.head == self.tail { + self.state = RingBufferState::Empty; + } else { + self.state = RingBufferState::Normal; + } + Some(c) + } + + /// Adds a single byte to the buffer + /// + /// # Arguments + /// * `byte` - Byte to add to the buffer + /// + /// # Returns + /// `Some(())` if successful, `None` if buffer is full + pub fn enqueue(&mut self, byte: u8) -> Option<()> { + if self.is_full() { + return None; + } + + let n = self.arr.len(); + self.arr[self.tail] = byte; + self.tail = (self.tail + 1) % n; + if self.head == self.tail { + self.state = RingBufferState::Full; + } else { + self.state = RingBufferState::Normal; + } + Some(()) + } + + /// Forces a byte into the buffer by overwriting the oldest element if full + /// + /// # Arguments + /// * `byte` - Byte to be inserted into the buffer + pub fn force_enqueue(&mut self, byte: u8) { + if self.is_full() { + self.dequeue(); + } + self.enqueue(byte); + } + + /// Clears the buffer, resetting it to an empty state. + /// + /// This method resets the read and write pointers to the start of the buffer + /// and marks the buffer as empty. All existing data in the buffer is logically + /// discarded, and the full capacity becomes available for new writes. + pub fn clear(&mut self) { + self.head = 0; + self.tail = 0; + self.state = RingBufferState::Empty; + } + + /// Reads and removes all available data from the buffer, returning the collected bytes + pub fn drain(&mut self) -> Vec { + let mut ret = vec![0u8; self.available_read()]; + self.read(ret.as_mut_slice()); + ret + } + + /// Returns the number of bytes available for reading + pub fn available_read(&self) -> usize { + match self.state { + RingBufferState::Empty => 0, + RingBufferState::Full => self.arr.len(), + RingBufferState::Normal => { + if self.head < self.tail { + self.tail - self.head + } else { + (self.arr.len() - self.head) + self.tail + } + } + } + } + + /// Returns the number of bytes available for writing + pub fn available_write(&self) -> usize { + self.arr.len() - self.available_read() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let rb = RingBuffer::new(5); + assert_eq!(rb.arr.len(), 5); + assert!(rb.is_empty()); + assert!(!rb.is_full()); + } + + #[test] + fn test_enqueue_dequeue_single() { + let mut rb = RingBuffer::new(3); + assert_eq!(rb.enqueue(1), Some(())); + assert!(!rb.is_empty()); + assert!(!rb.is_full()); + assert_eq!(rb.dequeue(), Some(1)); + assert!(rb.is_empty()); + } + + #[test] + fn test_full_condition() { + let mut rb = RingBuffer::new(2); + assert_eq!(rb.enqueue(1), Some(())); + assert_eq!(rb.enqueue(2), Some(())); + assert!(rb.is_full()); + assert_eq!(rb.enqueue(3), None); + } + + #[test] + fn test_empty_condition() { + let mut rb = RingBuffer::new(2); + assert_eq!(rb.dequeue(), None); + rb.enqueue(1).unwrap(); + rb.dequeue().unwrap(); + assert_eq!(rb.dequeue(), None); + } + + #[test] + fn test_wrap_around() { + let mut rb = RingBuffer::new(3); + rb.enqueue(1).unwrap(); + rb.enqueue(2).unwrap(); + rb.enqueue(3).unwrap(); + assert!(rb.is_full()); + assert_eq!(rb.dequeue().unwrap(), 1); + assert_eq!(rb.dequeue().unwrap(), 2); + assert_eq!(rb.dequeue().unwrap(), 3); + assert!(rb.is_empty()); + + rb.enqueue(4).unwrap(); + rb.enqueue(5).unwrap(); + assert_eq!(rb.dequeue().unwrap(), 4); + rb.enqueue(6).unwrap(); + assert_eq!(rb.dequeue().unwrap(), 5); + assert_eq!(rb.dequeue().unwrap(), 6); + } + + #[test] + fn test_read_write_basic() { + let mut rb = RingBuffer::new(5); + let data = [1, 2, 3]; + assert_eq!(rb.write(&data), 3); + assert_eq!(rb.head, 0); + assert_eq!(rb.tail, 3); + + let mut buf = [0; 5]; + assert_eq!(rb.read(&mut buf), 3); + assert_eq!(&buf[..3], &[1, 2, 3]); + assert!(rb.is_empty()); + } + + #[test] + fn test_read_write_wrap() { + let mut rb = RingBuffer::new(5); + + assert_eq!(rb.write(&[1, 2, 3, 4]), 4); + assert_eq!(rb.head, 0); + assert_eq!(rb.tail, 4); + assert!(!rb.is_full()); + + let mut buf = [0; 3]; + assert_eq!(rb.read(&mut buf), 3); + assert_eq!(buf, [1, 2, 3]); + assert_eq!(rb.head, 3); + assert_eq!(rb.tail, 4); + + assert_eq!(rb.write(&[5, 6, 7]), 3); + assert_eq!(rb.tail, (4 + 3) % 5); + assert_eq!(rb.tail, 2); + + let mut buf = [0; 5]; + assert_eq!(rb.read(&mut buf), 4); + assert_eq!(&buf[..4], &[4, 5, 6, 7]); + assert!(rb.is_empty()); + } + + #[test] + fn test_full_read_write() { + let mut rb = RingBuffer::new(5); + assert_eq!(rb.write(&[1, 2, 3, 4, 5]), 5); + assert!(rb.is_full()); + assert_eq!(rb.write(&[6]), 0); + let mut buf = [0; 5]; + assert_eq!(rb.read(&mut buf), 5); + assert_eq!(buf, [1, 2, 3, 4, 5]); + assert!(rb.is_empty()); + } + + #[test] + fn test_partial_read_write() { + let mut rb = RingBuffer::new(5); + assert_eq!(rb.write(&[1, 2]), 2); + let mut buf = [0; 3]; + assert_eq!(rb.read(&mut buf), 2); + assert_eq!(&buf[..2], &[1, 2]); + } + + #[test] + fn test_buffer_edge_cases() { + let mut rb = RingBuffer::new(1); + assert!(rb.is_empty()); + rb.enqueue(42).unwrap(); + assert!(rb.is_full()); + assert_eq!(rb.dequeue(), Some(42)); + assert!(rb.is_empty()); + } + + #[test] + fn test_complex_operations() { + let mut rb = RingBuffer::new(5); + rb.write(&[1, 2, 3]); + let mut buf = [0; 2]; + rb.read(&mut buf); + rb.write(&[4, 5, 6, 7]); + let mut buf = [0; 5]; + assert_eq!(rb.read(&mut buf), 5); + } + + #[test] + fn test_state_transitions() { + let mut rb = RingBuffer::new(3); + // Empty -> Normal + rb.enqueue(1).unwrap(); + assert_eq!(rb.state, RingBufferState::Normal); + // Normal -> Full + rb.enqueue(2).unwrap(); + rb.enqueue(3).unwrap(); + assert_eq!(rb.state, RingBufferState::Full); + // Full -> Normal + rb.dequeue().unwrap(); + assert_eq!(rb.state, RingBufferState::Normal); + // Normal -> Empty + rb.dequeue().unwrap(); + rb.dequeue().unwrap(); + assert_eq!(rb.state, RingBufferState::Empty); + } + + #[test] + fn test_available() { + let mut rb = RingBuffer::new(5); + rb.enqueue(1); + rb.enqueue(2); + assert_eq!(rb.available_read(), 2); + assert_eq!(rb.available_write(), 3); + + let byte = rb.dequeue().unwrap(); + assert_eq!(byte, 1); + assert_eq!(rb.available_read(), 1); + assert_eq!(rb.available_write(), 4); + } + + #[test] + fn test_clear() { + let mut rb = RingBuffer::new(4); + + // Test empty buffer + rb.clear(); + assert!(rb.is_empty()); + assert_eq!(rb.available_read(), 0); + assert_eq!(rb.available_write(), 4); + + // Test after writes + rb.write(&[1, 2, 3]); + assert_eq!(rb.available_read(), 3); + rb.clear(); + assert!(rb.is_empty()); + assert_eq!(rb.available_write(), 4); + + // Test after wrap-around + rb.write(&[1, 2, 3, 4]); // Fill buffer + rb.dequeue(); // head = 1 + rb.enqueue(5); // tail wraps to 0 + rb.clear(); + assert_eq!(rb.head, 0); + assert_eq!(rb.tail, 0); + assert_eq!(rb.available_write(), 4); + + // Verify post-clear functionality + rb.write(&[6, 7]); + let mut buf = [0u8; 2]; + assert_eq!(rb.read(&mut buf), 2); + assert_eq!(buf, [6, 7]); + } + + #[test] + fn test_clear_edge_cases() { + let mut rb = RingBuffer::new(3); + + // Clear full buffer + rb.write(&[1, 2, 3]); + assert!(rb.is_full()); + rb.clear(); + assert!(rb.is_empty()); + rb.write(&[4, 5, 6]); + assert_eq!(rb.drain(), vec![4, 5, 6]); + + // Clear partially read buffer + rb.write(&[7, 8, 9]); + rb.dequeue(); // head = 1 + rb.dequeue(); // head = 2 + rb.clear(); + rb.write(&[10]); + assert_eq!(rb.dequeue().unwrap(), 10); + } +} diff --git a/crates/ruxfifo/Cargo.toml b/crates/ruxfifo/Cargo.toml new file mode 100644 index 000000000..3259e3830 --- /dev/null +++ b/crates/ruxfifo/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "ruxfifo" +version = "0.1.0" +edition = "2021" +authors = ["FeiYang Li <3200105712@zju.edu.cn>", "Quan Shi <749990226@qq.com>"] +description = "Fifo(named pipe) used by RuxOS" +license = "Mulan PSL v2" +homepage = "https://github.com/syswonder/ruxos" +repository = "https://github.com/syswonder/ruxos/tree/dev/crates/ruxfifo" + +[dependencies] +axfs_vfs = { path = "../axfs_vfs" } +spin = { workspace = true } +axerrno = { path = "../axerrno" } +ringbuffer = { path = "../ringbuffer" } +axio = { path = "../axio" } diff --git a/crates/ruxfifo/src/lib.rs b/crates/ruxfifo/src/lib.rs new file mode 100644 index 000000000..2c21e7e79 --- /dev/null +++ b/crates/ruxfifo/src/lib.rs @@ -0,0 +1,168 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! Fifo(named pipe) used by RuxOS +#![no_std] +extern crate alloc; +use alloc::sync::Arc; +use axerrno::AxError; +use axfs_vfs::{impl_vfs_non_dir_default, VfsNodeAttr, VfsNodeOps, VfsResult}; +use axfs_vfs::{VfsNodePerm, VfsNodeType}; +use axio::PollState; +use core::sync::atomic::{AtomicUsize, Ordering}; +use ringbuffer::RingBuffer; +use spin::mutex::Mutex; + +/// Default size of fifo +const FIFO_SIZE: usize = 65536; + +/// FIFO (named pipe) node implementation for inter-process communication +/// +/// Provides synchronized read/write operations through a fixed-size ring buffer, +/// with atomic reference counting for concurrent access management. +pub struct FifoNode { + /// VFS node attributes + attr: Mutex, + /// Thread-safe ring buffer with mutual exclusion lock + buffer: Mutex, + /// Active readers counter (atomic for lock-free access) + readers: AtomicUsize, + /// Active writers counter (atomic for lock-free access) + writers: AtomicUsize, +} + +impl FifoNode { + /// create a new fifo + pub fn new(ino: u64, mode: VfsNodePerm) -> Self { + Self { + attr: Mutex::new(VfsNodeAttr::new(ino, mode, VfsNodeType::Fifo, 0, 0)), + buffer: Mutex::new(RingBuffer::new(FIFO_SIZE)), + readers: AtomicUsize::new(0), + writers: AtomicUsize::new(0), + } + } + + /// Creates an interconnected pair of FIFO nodes for pipe communication + /// + /// Returns two `Arc` references sharing the same underlying buffer and counters, + /// typically used for creating pipe reader/writer endpoints. + pub fn new_pair() -> (Arc, Arc) { + let node = Arc::new(Self { + attr: Mutex::new(VfsNodeAttr::new( + 1, + VfsNodePerm::default_fifo(), + VfsNodeType::Fifo, + 0, + 0, + )), + buffer: Mutex::new(RingBuffer::new(FIFO_SIZE)), + readers: AtomicUsize::new(1), + writers: AtomicUsize::new(1), + }); + (node.clone(), node) + } + + /// Returns current number of active readers + pub fn readers(&self) -> usize { + self.readers.load(Ordering::Acquire) + } + + /// Registers a new reader with atomic reference counting + /// + /// # Memory Ordering + /// Uses `Ordering::AcqRel` to synchronize with other atomic operations: + /// - Acquire: See previous writes to the buffer + /// - Release: Make buffer writes visible to others + pub fn acquire_reader(&self) { + self.readers.fetch_add(1, Ordering::AcqRel); + } + + /// Unregisters a reader and checks for underflow + pub fn release_reader(&self) { + let cnt_before = self.readers.fetch_sub(1, Ordering::AcqRel); + debug_assert!(cnt_before != 0) + } + + /// Returns current number of active writers + pub fn writers(&self) -> usize { + self.writers.load(Ordering::Acquire) + } + + /// Registers a new writer with atomic reference counting + pub fn acquire_writer(&self) { + self.writers.fetch_add(1, Ordering::AcqRel); + } + + /// Unregisters a writer and checks for underflow + pub fn release_writer(&self) { + let cnt_before = self.writers.fetch_sub(1, Ordering::AcqRel); + debug_assert!(cnt_before != 0) + } + + /// Checks readable status and peer existence for readers + pub fn reader_poll(&self) -> VfsResult { + let buffer = self.buffer.lock(); + Ok(PollState { + readable: !buffer.is_empty(), + writable: false, + pollhup: self.writers() == 0, + }) + } + + /// Checks writable status and peer existence for writers + pub fn writer_poll(&self) -> VfsResult { + let buffer = self.buffer.lock(); + Ok(PollState { + readable: false, + writable: !buffer.is_full(), + pollhup: self.readers() == 0, + }) + } +} + +impl VfsNodeOps for FifoNode { + fn get_attr(&self) -> VfsResult { + Ok(*self.attr.lock()) + } + + fn set_mode(&self, mode: VfsNodePerm) -> VfsResult { + self.attr.lock().set_perm(mode); + Ok(()) + } + + // for fifo, offset is useless and ignored + fn read_at(&self, _offset: u64, buf: &mut [u8]) -> VfsResult { + let mut buffer = self.buffer.lock(); + if buffer.is_empty() { + return Err(AxError::WouldBlock); + } + Ok(buffer.read(buf)) + } + + // for fifo, offset is useless and ignored + fn write_at(&self, _offset: u64, buf: &[u8]) -> VfsResult { + let mut buffer = self.buffer.lock(); + if buffer.is_full() { + return Err(AxError::WouldBlock); + } + Ok(buffer.write(buf)) + } + + // fifo does not support truncate + fn truncate(&self, _size: u64) -> VfsResult { + Ok(()) + } + + // fifo does not support fsync + fn fsync(&self) -> VfsResult { + Ok(()) + } + + impl_vfs_non_dir_default! {} +} diff --git a/crates/scheduler/src/cfs.rs b/crates/scheduler/src/cfs.rs index 988c86d7d..683660ac1 100644 --- a/crates/scheduler/src/cfs.rs +++ b/crates/scheduler/src/cfs.rs @@ -122,6 +122,12 @@ pub struct CFScheduler { id_pool: AtomicIsize, } +impl Default for CFScheduler { + fn default() -> Self { + Self::new() + } +} + impl CFScheduler { /// Creates a new empty [`CFScheduler`]. pub const fn new() -> Self { diff --git a/crates/scheduler/src/fifo.rs b/crates/scheduler/src/fifo.rs index 34a360465..ed1681461 100644 --- a/crates/scheduler/src/fifo.rs +++ b/crates/scheduler/src/fifo.rs @@ -73,6 +73,12 @@ pub struct FifoScheduler { ready_queue: List>>, } +impl Default for FifoScheduler { + fn default() -> Self { + Self::new() + } +} + impl FifoScheduler { /// Creates a new empty [`FifoScheduler`]. pub const fn new() -> Self { diff --git a/crates/scheduler/src/lib.rs b/crates/scheduler/src/lib.rs index 2cd6000ec..590bb0184 100644 --- a/crates/scheduler/src/lib.rs +++ b/crates/scheduler/src/lib.rs @@ -16,7 +16,6 @@ //! - [`CFScheduler`]: Completely Fair Scheduler (preemptive). #![cfg_attr(not(test), no_std)] -#![feature(const_mut_refs)] mod cfs; mod fifo; diff --git a/crates/scheduler/src/round_robin.rs b/crates/scheduler/src/round_robin.rs index 8c696a20e..802a58ad4 100644 --- a/crates/scheduler/src/round_robin.rs +++ b/crates/scheduler/src/round_robin.rs @@ -75,6 +75,12 @@ pub struct RRScheduler { ready_queue: VecDeque>>, } +impl Default for RRScheduler { + fn default() -> Self { + Self::new() + } +} + impl RRScheduler { /// Creates a new empty [`RRScheduler`]. pub const fn new() -> Self { diff --git a/crates/slab_allocator/Cargo.toml b/crates/slab_allocator/Cargo.toml index faba1f5e5..9c117abe9 100644 --- a/crates/slab_allocator/Cargo.toml +++ b/crates/slab_allocator/Cargo.toml @@ -1,7 +1,11 @@ [package] name = "slab_allocator" version = "0.3.1" -authors = ["Robert Węcławski ", "Yiren Zhang "] +edition = "2021" +authors = [ + "Robert Węcławski ", + "Yiren Zhang ", +] license = "MIT" description = "Slab allocator for `no_std` systems. Uses multiple slabs with blocks of different sizes and a linked list for blocks larger than 4096 bytes" diff --git a/crates/slab_allocator/src/slab.rs b/crates/slab_allocator/src/slab.rs index 14a5d8c4e..84414d962 100644 --- a/crates/slab_allocator/src/slab.rs +++ b/crates/slab_allocator/src/slab.rs @@ -99,11 +99,13 @@ impl FreeBlockList { } fn pop(&mut self) -> Option<&'static mut FreeBlock> { - self.head.take().map(|node| { + if let Some(node) = self.head.take() { self.head = node.next.take(); self.len -= 1; - node - }) + Some(node) // Wrap the node back in Some + } else { + None // Return None if the original was None + } } fn push(&mut self, free_block: &'static mut FreeBlock) { diff --git a/crates/slab_allocator/src/tests.rs b/crates/slab_allocator/src/tests.rs index 46c5faac5..b67638adb 100644 --- a/crates/slab_allocator/src/tests.rs +++ b/crates/slab_allocator/src/tests.rs @@ -28,21 +28,21 @@ fn new_heap() -> Heap { let test_heap = TestHeap { heap_space: [0u8; HEAP_SIZE], }; - let heap = unsafe { Heap::new(&test_heap.heap_space[0] as *const u8 as usize, HEAP_SIZE) }; - heap + + unsafe { Heap::new(&test_heap.heap_space[0] as *const u8 as usize, HEAP_SIZE) } } fn new_big_heap() -> Heap { let test_heap = TestBigHeap { heap_space: [0u8; BIG_HEAP_SIZE], }; - let heap = unsafe { + + unsafe { Heap::new( &test_heap.heap_space[0] as *const u8 as usize, BIG_HEAP_SIZE, ) - }; - heap + } } #[test] @@ -66,13 +66,13 @@ fn allocate_double_usize() { fn allocate_and_free_double_usize() { let mut heap = new_heap(); let layout = Layout::from_size_align(size_of::() * 2, align_of::()).unwrap(); - let addr = heap.allocate(layout.clone()); + let addr = heap.allocate(layout); assert!(addr.is_ok()); let addr = addr.unwrap(); unsafe { *(addr as *mut (usize, usize)) = (0xdeafdeadbeafbabe, 0xdeafdeadbeafbabe); - heap.deallocate(addr, layout.clone()); + heap.deallocate(addr, layout); } } @@ -82,14 +82,14 @@ fn reallocate_double_usize() { let layout = Layout::from_size_align(size_of::() * 2, align_of::()).unwrap(); - let x = heap.allocate(layout.clone()).unwrap(); + let x = heap.allocate(layout).unwrap(); unsafe { - heap.deallocate(x, layout.clone()); + heap.deallocate(x, layout); } - let y = heap.allocate(layout.clone()).unwrap(); + let y = heap.allocate(layout).unwrap(); unsafe { - heap.deallocate(y, layout.clone()); + heap.deallocate(y, layout); } assert_eq!(x as usize, y as usize); @@ -106,16 +106,16 @@ fn allocate_multiple_sizes() { let layout_3 = Layout::from_size_align(base_size * 3, base_align * 8).unwrap(); let layout_4 = Layout::from_size_align(base_size * 10, base_align).unwrap(); - let x = heap.allocate(layout_1.clone()).unwrap(); - let y = heap.allocate(layout_2.clone()).unwrap(); - let z = heap.allocate(layout_3.clone()).unwrap(); + let x = heap.allocate(layout_1).unwrap(); + let y = heap.allocate(layout_2).unwrap(); + let z = heap.allocate(layout_3).unwrap(); unsafe { - heap.deallocate(x, layout_1.clone()); + heap.deallocate(x, layout_1); } - let a = heap.allocate(layout_4.clone()).unwrap(); - let b = heap.allocate(layout_1.clone()).unwrap(); + let a = heap.allocate(layout_4).unwrap(); + let b = heap.allocate(layout_1).unwrap(); unsafe { heap.deallocate(y, layout_2); @@ -133,10 +133,10 @@ fn allocate_one_4096_block() { let layout = Layout::from_size_align(base_size * 512, base_align).unwrap(); - let x = heap.allocate(layout.clone()).unwrap(); + let x = heap.allocate(layout).unwrap(); unsafe { - heap.deallocate(x, layout.clone()); + heap.deallocate(x, layout); } } @@ -149,23 +149,23 @@ fn allocate_multiple_4096_blocks() { let layout = Layout::from_size_align(base_size * 512, base_align).unwrap(); let layout_2 = Layout::from_size_align(base_size * 1024, base_align).unwrap(); - let _x = heap.allocate(layout.clone()).unwrap(); - let y = heap.allocate(layout.clone()).unwrap(); - let z = heap.allocate(layout.clone()).unwrap(); + let _x = heap.allocate(layout).unwrap(); + let y = heap.allocate(layout).unwrap(); + let z = heap.allocate(layout).unwrap(); unsafe { - heap.deallocate(y, layout.clone()); + heap.deallocate(y, layout); } - let a = heap.allocate(layout.clone()).unwrap(); - let _b = heap.allocate(layout.clone()).unwrap(); + let a = heap.allocate(layout).unwrap(); + let _b = heap.allocate(layout).unwrap(); unsafe { - heap.deallocate(a, layout.clone()); - heap.deallocate(z, layout.clone()); + heap.deallocate(a, layout); + heap.deallocate(z, layout); } - let c = heap.allocate(layout_2.clone()).unwrap(); - let _d = heap.allocate(layout.clone()).unwrap(); + let c = heap.allocate(layout_2).unwrap(); + let _d = heap.allocate(layout).unwrap(); unsafe { *(c as *mut (usize, usize)) = (0xdeafdeadbeafbabe, 0xdeafdeadbeafbabe); } diff --git a/crates/spinlock/Cargo.toml b/crates/spinlock/Cargo.toml index 4cd97499d..855e7fa96 100644 --- a/crates/spinlock/Cargo.toml +++ b/crates/spinlock/Cargo.toml @@ -2,7 +2,10 @@ name = "spinlock" version = "0.1.0" edition = "2021" -authors = ["Yuekai Jia "] +authors = [ + "Yuekai Jia ", + "Igna ", +] description = "`no_std` spin lock implementation that can disable kernel local IRQs or preemption while locking" license = "GPL-3.0-or-later OR Apache-2.0" homepage = "https://github.com/rcore-os/arceos" diff --git a/crates/spinlock/src/base.rs b/crates/spinlock/src/base.rs index 1dd6e5e3a..2dfe4a151 100644 --- a/crates/spinlock/src/base.rs +++ b/crates/spinlock/src/base.rs @@ -24,17 +24,23 @@ use core::sync::atomic::{AtomicBool, Ordering}; use kernel_guard::BaseGuard; +use crate::{strategy, Strategy}; + +/// The default strategy used in spinlocks. +pub type DefaultStrategy = strategy::Once; + /// A [spin lock](https://en.m.wikipedia.org/wiki/Spinlock) providing mutually /// exclusive access to data. /// /// This is a base struct, the specific behavior depends on the generic /// parameter `G` that implements [`BaseGuard`], such as whether to disable -/// local IRQs or kernel preemption before acquiring the lock. +/// local IRQs or kernel preemption before acquiring the lock. The parameter `S` +/// that implements [`Strategy`] defines the behavior when encountering contention. /// /// For single-core environment (without the "smp" feature), we remove the lock /// state, CPU can always get the lock if we follow the proper guard in use. -pub struct BaseSpinLock { - _phantom: PhantomData, +pub struct BaseSpinLock { + _phantom: PhantomData<(DG, S)>, #[cfg(feature = "smp")] lock: AtomicBool, data: UnsafeCell, @@ -52,10 +58,10 @@ pub struct BaseSpinLockGuard<'a, G: BaseGuard, T: ?Sized + 'a> { } // Same unsafe impls as `std::sync::Mutex` -unsafe impl Sync for BaseSpinLock {} -unsafe impl Send for BaseSpinLock {} +unsafe impl Sync for BaseSpinLock {} +unsafe impl Send for BaseSpinLock {} -impl BaseSpinLock { +impl BaseSpinLock { /// Creates a new [`BaseSpinLock`] wrapping the supplied data. #[inline(always)] pub const fn new(data: T) -> Self { @@ -77,16 +83,20 @@ impl BaseSpinLock { } } -impl BaseSpinLock { - /// Locks the [`BaseSpinLock`] and returns a guard that permits access to the inner data. +impl BaseSpinLock { + /// Locks the [`BaseSpinLock`] using the given guard type and backoff strategy, + /// and returns a guard that permits access to the inner data. /// /// The returned value may be dereferenced for data access /// and the lock will be dropped when the guard falls out of scope. - #[inline(always)] - pub fn lock(&self) -> BaseSpinLockGuard { - let irq_state = G::acquire(); + pub fn lock_as(&self) -> BaseSpinLockGuard { + let irq_state = GT::acquire(); + #[cfg(feature = "smp")] { + use crate::strategy::{Backoff, Relax}; + + let mut backoff = ::new_backoff(); // Can fail to lock even if the spinlock is not locked. May be more efficient than `try_lock` // when called in a loop. while self @@ -94,9 +104,12 @@ impl BaseSpinLock { .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed) .is_err() { + backoff.backoff(); + let mut relax = ::new_relax(); + // Wait until the lock looks unlocked before retrying while self.is_locked() { - core::hint::spin_loop(); + relax.relax(); } } } @@ -109,6 +122,16 @@ impl BaseSpinLock { } } + /// Locks the [`BaseSpinLock`] using the "default" strategy specified by lock type, + /// and returns a guard that permits access to the inner data. + /// + /// The returned value may be dereferenced for data access + /// and the lock will be dropped when the guard falls out of scope. + #[inline(always)] + pub fn lock(&self) -> BaseSpinLockGuard { + self.lock_as::() + } + /// Returns `true` if the lock is currently held. /// /// # Safety @@ -183,14 +206,14 @@ impl BaseSpinLock { } } -impl Default for BaseSpinLock { +impl Default for BaseSpinLock { #[inline(always)] fn default() -> Self { Self::new(Default::default()) } } -impl fmt::Debug for BaseSpinLock { +impl fmt::Debug for BaseSpinLock { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.try_lock() { Some(guard) => write!(f, "SpinLock {{ data: ") diff --git a/crates/spinlock/src/lib.rs b/crates/spinlock/src/lib.rs index 2ce00ebaf..32582d720 100644 --- a/crates/spinlock/src/lib.rs +++ b/crates/spinlock/src/lib.rs @@ -16,15 +16,23 @@ //! environment (without this feature), the lock state is unnecessary and //! optimized out. CPU can always get the lock if we follow the proper guard //! in use. By default, this feature is disabled. +//! - `rand`: Provide extra contention-alleviating strategy using exponential +//! backoff algorithm. The user is responsible for providing the random number +//! generator implementation. #![cfg_attr(not(test), no_std)] mod base; -use kernel_guard::{NoOp, NoPreempt, NoPreemptIrqSave}; +/// Defines the strategies used when encountering lock contention. +pub mod strategy; + +use kernel_guard::{NoPreempt, NoPreemptIrqSave}; pub use self::base::{BaseSpinLock, BaseSpinLockGuard}; +pub use self::strategy::*; + /// A spin lock that disables kernel preemption while trying to lock, and /// re-enables it after unlocking. /// @@ -48,7 +56,7 @@ pub type SpinNoIrqGuard<'a, T> = BaseSpinLockGuard<'a, NoPreemptIrqSave, T>; /// /// It must be used in the preemption-disabled and local IRQ-disabled context, /// or never be used in interrupt handlers. -pub type SpinRaw = BaseSpinLock; +pub type SpinRaw = BaseSpinLock; /// A guard that provides mutable data access for [`SpinRaw`]. -pub type SpinRawGuard<'a, T> = BaseSpinLockGuard<'a, NoOp, T>; +pub type SpinRawGuard<'a, T> = BaseSpinLockGuard<'a, kernel_guard::NoOp, T>; diff --git a/crates/spinlock/src/strategy.rs b/crates/spinlock/src/strategy.rs new file mode 100644 index 000000000..3b8b80daa --- /dev/null +++ b/crates/spinlock/src/strategy.rs @@ -0,0 +1,157 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use core::marker::PhantomData; + +#[inline(always)] +fn exp_backoff(current_limit: &mut u32, max: u32) { + let limit = *current_limit; + *current_limit = max.max(limit << 1); + for _ in 0..limit { + core::hint::spin_loop(); + } +} + +/// Defines the backoff behavior of a spinlock. +pub trait Backoff { + /// Backoff behavior when failed to acquire the lock. + fn backoff(&mut self); +} + +/// Defines the relax behavior of a spinlock. +pub trait Relax { + /// Relax behavior when the lock seemed still held. + fn relax(&mut self); +} + +/// Defines the lock behavior when encountering contention. +/// [`Backoff::backoff`] is called when failed to acquire the lock, and +/// [`Relax::relax`]` is called when the lock seemed still held. +/// +/// One can easily define a new [`Strategy`] impl that +/// combines existing backoff/relax behaviors. +pub trait Strategy { + /// The type that defines the relax behavior. + type Relax: Relax; + + /// The type that defines the backoff behavior. + type Backoff: Backoff; + + /// Create a new relax state every time after failed to acquire the lock. + fn new_relax() -> Self::Relax; + + /// Create a new backoff state every time after the locking procedure began. + fn new_backoff() -> Self::Backoff; +} + +impl Strategy for T { + type Relax = T; + type Backoff = T; + + #[inline(always)] + fn new_relax() -> Self::Relax { + T::default() + } + + #[inline(always)] + fn new_backoff() -> Self::Backoff { + T::default() + } +} + +/// Do nothing when backoff/relax is required. +/// It can be used as a baseline, or under rare circumstances be used as a +/// performance improvement. +/// +/// Note that under most modern CPU design, not using any backoff/relax strategy +/// would normally make things slower. +#[derive(Debug, Default)] +pub struct NoOp; + +/// Call [`core::hint::spin_loop`] once when backoff/relax is required. +/// +/// This may improve performance by said, reducing bus traffic. The exact +/// behavior and benefits depend on the machine. +#[derive(Debug, Default)] +pub struct Once; + +/// Call [`core::hint::spin_loop`] with exponentially increased time when +/// backoff/relax is required. +/// +/// This would generally increase performance when the lock is highly contended. +#[derive(Debug)] +pub struct Exp(u32); + +/// Combines a [`Relax`] and a [`Backoff`] into a strategy. +#[derive(Debug, Default)] +pub struct Combine(PhantomData<(R, B)>); + +impl Relax for NoOp { + #[inline(always)] + fn relax(&mut self) {} +} + +impl Backoff for NoOp { + #[inline(always)] + fn backoff(&mut self) {} +} + +impl Relax for Once { + #[inline(always)] + fn relax(&mut self) { + core::hint::spin_loop(); + } +} + +impl Backoff for Once { + #[inline(always)] + fn backoff(&mut self) { + core::hint::spin_loop(); + } +} + +impl Relax for Exp { + #[inline(always)] + fn relax(&mut self) { + exp_backoff(&mut self.0, N); + } +} + +impl Backoff for Exp { + #[inline(always)] + fn backoff(&mut self) { + exp_backoff(&mut self.0, N); + } +} + +impl Default for Exp { + #[inline(always)] + fn default() -> Self { + Self(1) + } +} + +impl Strategy for Combine +where + R: Relax + Default, + B: Backoff + Default, +{ + type Relax = R; + type Backoff = B; + + #[inline(always)] + fn new_relax() -> Self::Relax { + R::default() + } + + #[inline(always)] + fn new_backoff() -> Self::Backoff { + B::default() + } +} diff --git a/crates/timer_list/src/lib.rs b/crates/timer_list/src/lib.rs index f23a0a092..0ba0817a3 100644 --- a/crates/timer_list/src/lib.rs +++ b/crates/timer_list/src/lib.rs @@ -229,7 +229,7 @@ mod tests { for ddl in deadlines { timer_list.set( ddl, - TimerEventFn::new(|now| println!("timer fn expired at {:?}", now)), + TimerEventFn::new(|now| println!("timer fn expired at {now:?}")), ); } diff --git a/crates/tty/Cargo.toml b/crates/tty/Cargo.toml index 8d8da252c..efb95c047 100644 --- a/crates/tty/Cargo.toml +++ b/crates/tty/Cargo.toml @@ -2,8 +2,19 @@ name = "tty" version = "0.0.1" edition = "2021" +authors = ["Quan Shi <749990226@qq.com>", "Zhengning Chen "] +description = "The TTY module provides a terminal interface, managing data flow through layers like TTY Core, Line Discipline, and Driver" +license = "Mulan PSL v2" +homepage = "https://github.com/syswonder/ruxos" +repository = "https://github.com/syswonder/ruxos/tree/dev/crates/tty" [dependencies] spinlock = { path = "../spinlock" } lazy_init = { path = "../lazy_init" } -log = "0.4" +axlog = { path = "../axlog" } +bitflags = { workspace = true } +ringbuffer = { path = "../../crates/ringbuffer" } +spin = { workspace = true } +axerrno = { path = "../../crates/axerrno" } +axio = { path = "../../crates/axio" } +num_enum = { version = "0.5.11", default-features = false } diff --git a/crates/tty/src/buffer.rs b/crates/tty/src/buffer.rs deleted file mode 100644 index 2c63d2324..000000000 --- a/crates/tty/src/buffer.rs +++ /dev/null @@ -1,149 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -//! functions for tty buffer. -//! Drivers should fill the buffer by functions below. -//! then the data will be passed to line discipline for processing. - -/// tty buffer size. -const TTY_BUF_SIZE: usize = 4096; - -/// ring buffer. -#[derive(Debug)] -struct RingBuffer { - /// data. - buf: [u8; TTY_BUF_SIZE], - - /// the first element or empty slot if buffer is empty. - head: usize, - - /// the first empty slot. - tail: usize, - - /// number of elements. - len: usize, -} - -/// tty buffer. -/// TODO: use flip buffer. -#[derive(Debug)] -pub struct TtyBuffer { - /// use ring buffer to save chars. - buffer: spinlock::SpinNoIrq, -} - -impl TtyBuffer { - pub fn new() -> Self { - Self { - buffer: spinlock::SpinNoIrq::new(RingBuffer { - buf: [0u8; TTY_BUF_SIZE], - head: 0, - tail: 0, - len: 0, - }), - } - } - - /// get `index`th element without changing buffer. - pub fn see(&self, index: usize) -> u8 { - let buf = self.buffer.lock(); - if index < buf.len { - buf.buf[(index + buf.head) % TTY_BUF_SIZE] - } else { - 0 - } - } - - /// push a char to tail. - pub fn push(&self, ch: u8) { - let mut buf = self.buffer.lock(); - if buf.len != TTY_BUF_SIZE { - buf.len += 1; - let idx = buf.tail; - buf.buf[idx] = ch; - buf.tail = (buf.tail + 1) % TTY_BUF_SIZE; - } - } - - /// delete and return the heading char. - pub fn pop(&self) -> u8 { - self.delete(0) - } - - /// insert `ch` to `index`th position. - pub fn insert(&self, ch: u8, index: usize) { - let mut buf = self.buffer.lock(); - // if not full and index is right - if buf.len != TTY_BUF_SIZE && index <= buf.len { - // shift buffer[index..move_len+index] one slot right. - let move_len = buf.len - index; - let mut i = buf.tail; - for _ in 0..move_len { - i -= 1; - buf.buf[(i + 1) % TTY_BUF_SIZE] = buf.buf[i % TTY_BUF_SIZE]; - } - // insert - let idx = (buf.head + index) % TTY_BUF_SIZE; - buf.buf[idx] = ch; - buf.len += 1; - buf.tail = (buf.tail + 1) % TTY_BUF_SIZE; - } - } - - /// delete and return the `index`th element. - pub fn delete(&self, index: usize) -> u8 { - let mut buf = self.buffer.lock(); - // if not empty and index is right - if buf.len != 0 && index < buf.len { - let move_len = buf.len - index; - let mut i = index + buf.head; - - // save retval - let ret = buf.buf[i % TTY_BUF_SIZE]; - - // copy move_len elements from buffer[index+head] to buffer[index+head-1]; - for _ in 0..move_len { - buf.buf[i % TTY_BUF_SIZE] = buf.buf[(i + 1) % TTY_BUF_SIZE]; - i += 1; - } - - // len -= 1 - buf.len -= 1; - buf.tail -= 1; - ret - } else { - 0 - } - } - - /// get current length of buffer. - pub fn len(&self) -> usize { - self.buffer.lock().len - } -} - -/// a buffer for echo of line discipline. -/// additionally saving the cursor position. -#[derive(Debug)] -pub struct EchoBuffer { - /// chars buffer. - pub buffer: TtyBuffer, - - /// current column of cursor. - pub col: usize, -} - -impl EchoBuffer { - pub fn new() -> Self { - Self { - buffer: TtyBuffer::new(), - col: 0, - } - } -} diff --git a/crates/tty/src/constant.rs b/crates/tty/src/constant.rs deleted file mode 100644 index 9a134764f..000000000 --- a/crates/tty/src/constant.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -pub const LF: u8 = b'\n'; -pub const CR: u8 = b'\r'; - -pub const DEL: u8 = b'\x7f'; -pub const BS: u8 = b'\x08'; - -pub const SPACE: u8 = b' '; - -/// escape -pub const ESC: u8 = 27; -/// [ -pub const LEFT_BRACKET: u8 = 91; - -/// an arrow char is `ARROW_PREFIX` + `UP/DOWN/RIGHT/LEFT` -pub const ARROW_PREFIX: [u8; 2] = [ESC, LEFT_BRACKET]; - -// const UP: u8 = 65; -// const DOWN: u8 = 66; -pub const RIGHT: u8 = 67; -pub const LEFT: u8 = 68; diff --git a/crates/tty/src/driver.rs b/crates/tty/src/driver.rs index dfe106a31..5e393d7b1 100644 --- a/crates/tty/src/driver.rs +++ b/crates/tty/src/driver.rs @@ -6,162 +6,53 @@ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PSL v2 for more details. */ +use alloc::{sync::Arc, vec::Vec}; -//! the first thing a driver should do is registering itself by `register_driver()`, -//! which will allocate an index for this driver. -//! -//! then, driver should register every device it has by `register_device()`, -//! which will allocate an index for this device. - -use crate::tty::TtyStruct; -use alloc::collections::BTreeMap; -use alloc::string::String; -use alloc::sync::Arc; -use alloc::{vec, vec::Vec}; -use lazy_init::LazyInit; +use crate::tty::Tty; use spinlock::SpinNoIrq; -/// all tty drivers. -/// only be written when registering a driver. -pub(super) static ALL_DRIVERS: LazyInit>>> = LazyInit::new(); - -/// the operations a tty driver must implement. -/// passed by driver when registering itself. -#[derive(Debug)] -pub struct TtyDriverOps { - /// push a char to device. - pub putchar: fn(u8), -} - -/// tty driver. -#[derive(Debug)] +/// TTY device controller managing registered TTY instances pub struct TtyDriver { - /// driver operations. - pub ops: TtyDriverOps, - - /// driver's devices. - /// TODO: maybe use rwlock for dynamicly adding devices is better. - ttys: SpinNoIrq>>, - - /// index of driver. - index: usize, - - /// name of driver. - name: String, + /// When registering a tty device(e.g. ttyS), it will be put in tty device list `ttys` + ttys: SpinNoIrq>>, } -impl TtyDriver { - pub fn new(ops: TtyDriverOps, name: &str) -> Self { +impl Default for TtyDriver { + fn default() -> Self { Self { - ops, - ttys: SpinNoIrq::new(BTreeMap::new()), - index: 0, - name: String::from(name), - } - } - - /// add a device, return its index, -1 means failure. - fn add_one_device(&self, tty: Arc) -> isize { - let mut index = 0; - if let Some(k) = self.ttys.lock().last_key_value() { - index = *k.0; + ttys: SpinNoIrq::new(Vec::new()), } - - // set index of device - tty.set_index(index); - - // set name of device - let mut name = self.name.clone(); - name.push(core::char::from_digit(index as _, 16).unwrap()); - tty.set_name(&name); - - // save this device - self.ttys.lock().insert(index, tty.clone()); - - // return device's index - index as _ - } - - pub fn name(&self) -> String { - self.name.clone() } +} - pub fn index(&self) -> usize { - self.index +impl TtyDriver { + /// Creates a new TTY driver with empty device list + pub fn new() -> Arc { + Arc::new(Self { + ttys: SpinNoIrq::new(Vec::new()), + }) } - /// get all devices' name - pub fn get_all_device_names(&self) -> Vec { - let mut ret = vec![]; - for (_, tty) in self.ttys.lock().iter() { - ret.push(tty.name()); - } - ret + /// Registers a TTY device to the driver's management list + pub fn add_tty(&self, tty: Arc) { + self.ttys.lock().push(tty) } - /// get device - pub fn get_device_by_name(&self, name: &str) -> Option> { - for (_, tty) in self.ttys.lock().iter() { - if tty.name() == name { - return Some(tty.clone()); + /// Broadcasts input bytes to all registered TTY devices + /// + /// Sequentially sends each byte to every managed TTY + pub fn push_slice(&self, slice: &[u8]) { + for tty in &*self.ttys.lock() { + for ch in slice { + tty.push_char(*ch); } } - None } - /// get device - pub fn get_device_by_index(&self, index: usize) -> Option> { - self.ttys.lock().get(&index).cloned() - } -} - -pub fn init() { - ALL_DRIVERS.init_by(SpinNoIrq::new(vec![])); -} - -/// get driver by index. -pub fn get_driver_by_index(index: usize) -> Option> { - let lock = ALL_DRIVERS.lock(); - for driver in lock.iter() { - if driver.index == index { - return Some(driver.clone()); + /// Broadcasts input byte to all registered TTY devices + pub fn push_char(&self, ch: u8) { + for tty in &*self.ttys.lock() { + tty.push_char(ch); } } - None -} - -/// called by driver to register itself. -/// return driver's index. -pub fn register_driver(ops: TtyDriverOps, name: &str) -> usize { - // create a tty driver structure - let mut driver = TtyDriver::new(ops, name); - - // lock - let mut lock = ALL_DRIVERS.lock(); - - // grant an index to the driver - let index = lock.len(); - driver.index = index; - - // push - lock.push(Arc::new(driver)); - - // return index - index -} - -/// called by driver to register device. -/// return device's index, or -1 on failure. -pub fn register_device(driver_index: usize) -> isize { - let mut index = -1; - // if driver is found - if let Some(driver) = get_driver_by_index(driver_index) { - // create a tty structure - let tty = Arc::new(TtyStruct::new(driver.clone())); - - // save this structure - index = driver.add_one_device(tty.clone()); - crate::tty::add_one_device(tty.clone()); - } - index } diff --git a/crates/tty/src/ioctl.rs b/crates/tty/src/ioctl.rs new file mode 100644 index 000000000..7746414bf --- /dev/null +++ b/crates/tty/src/ioctl.rs @@ -0,0 +1,118 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ +//! Tty/Pty Ioctl Command +#![allow(clippy::upper_case_acronyms)] +#![allow(dead_code)] +pub use num_enum::FromPrimitive; + +#[derive(Debug, FromPrimitive, Clone, Copy, Eq, PartialEq)] +#[repr(u32)] +/// Tty IO control command +pub enum IoctlCommand { + /// Default variant to catch invalid/unknown ioctl commands + #[num_enum(default)] + InvalidCommand, + + // ============================================== + // Terminal control commands (termios structure) + // ============================================== + /// TCGETS: Terminal Control GET Settings + /// + /// Gets current terminal settings using termios structure + TCGETS = 0x5401, + + /// TCSETS: Terminal Control SET Settings + /// + /// Sets terminal settings immediately using termios structure + TCSETS = 0x5402, + + /// TCSETSW: Terminal Control SET Settings and Wait + /// + /// Sets terminal settings after draining output buffer + TCSETSW = 0x5403, + + /// TCSETSF: Terminal Control SET Settings and Flush + /// + /// Sets terminal settings after flushing input/output buffers + TCSETSF = 0x5404, + + // ============================================== + // Terminal control commands (termio structure - legacy BSD) + // ============================================== + /// TCGETA: Terminal Control GET Attributes + /// + /// Gets current terminal settings using legacy termio structure + TCGETA = 0x5405, + + /// TCSETA: Terminal Control SET Attributes + /// + /// Sets terminal settings immediately using termio structure + TCSETA = 0x5406, + + /// TCSETAW: Terminal Control SET Attributes and Wait + /// + /// Sets termio settings after draining output buffer + TCSETAW = 0x5407, + + /// TCSETAF: Terminal Control SET Attributes and Flush + /// + /// Sets termio settings after flushing input/output buffers + TCSETAF = 0x5408, + + // ============================================== + // Special control commands + // ============================================== + /// TCSBRK: Terminal Control Send BReaK + /// + /// Sends a break sequence (stream of zero bits) for 0.25-0.5 seconds + TCSBRK = 0x5409, + + /// TIOCSCTTY: Terminal IOCtl Set Controlling TTY + /// + /// Makes the given terminal the controlling terminal of the calling process + TIOCSCTTY = 0x540E, + + /// TIOCGPGRP: Terminal IOCtl Get Process GRouP + /// + /// Gets foreground process group ID associated with terminal + TIOCGPGRP = 0x540F, + + /// TIOCSPGRP: Terminal IOCtl Set Process GRouP + /// + /// Sets foreground process group ID associated with terminal + TIOCSPGRP = 0x5410, + + /// TIOCGWINSZ: Terminal IOCtl Get WINdow SiZe + /// + /// Gets terminal window size (rows/columns) + TIOCGWINSZ = 0x5413, + + /// TIOCSWINSZ: Terminal IOCtl Set WINdow SiZe + /// + /// Sets terminal window size (rows/columns) + TIOCSWINSZ = 0x5414, + + /// Get the number of bytes in the input buffer. + FIONREAD = 0x541B, + + /// TIOCNOTTY: Terminal IOCtl No TTY + /// + /// Disassociates from controlling terminal (used by daemons/sshd) + TIOCNOTTY = 0x5422, + + /// Lock/unlock Pty + TIOCSPTLCK = 0x40045431, + + /// Given a file descriptor in fd that refers to a pseudoterminal master, + /// open and return a new file descriptor that refers to the peer pseudoterminal slave device. + TIOCGPTPEER = 0x40045441, + + /// Get Pty Number + TIOCGPTN = 0x80045430, +} diff --git a/crates/tty/src/ldisc.rs b/crates/tty/src/ldisc.rs index e7394ac42..7ed7d5342 100644 --- a/crates/tty/src/ldisc.rs +++ b/crates/tty/src/ldisc.rs @@ -11,211 +11,342 @@ //! the currently implemented line discipline is N_TTY. //! line disciplines are registered when a device is registered. -use alloc::sync::Arc; +use crate::termios::{Termios, CC_C_CHAR}; +use alloc::{format, sync::Arc}; +use axerrno::AxResult; +use axio::PollState; +use ringbuffer::RingBuffer; use spinlock::SpinNoIrq; -use crate::{ - buffer::{EchoBuffer, TtyBuffer}, - tty::TtyStruct, -}; +const BUFFER_CAPACITY: usize = 4096; -/// tty line discipline. -#[derive(Debug)] -pub struct TtyLdisc { - /// chars that can be read by kernel. - read_buf: TtyBuffer, - - /// chars being echoed on the screen. - echo_buf: SpinNoIrq, - - /// chars from driver, and not yet been processed. - rec_buf: TtyBuffer, +/// Line discipline managing terminal input processing +/// +/// Handles raw input buffering, line editing, and terminal control settings +pub struct Ldisc { + /// Buffer for current line being edited + current_line: SpinNoIrq, + /// Buffer for completed lines ready for reading + read_buffer: SpinNoIrq, + /// Terminal control settings + termios: SpinNoIrq, + /// Terminal window dimensions + winsize: SpinNoIrq, } -/// implement N_TTY. -impl TtyLdisc { - pub fn new() -> Self { - Self { - read_buf: TtyBuffer::new(), - echo_buf: SpinNoIrq::new(EchoBuffer::new()), - rec_buf: TtyBuffer::new(), - } +impl Ldisc { + /// Creates a new line discipline instance with default buffers and terminal settings + pub fn new() -> Arc { + Arc::new(Self { + current_line: SpinNoIrq::new(RingBuffer::new(BUFFER_CAPACITY)), + read_buffer: SpinNoIrq::new(RingBuffer::new(BUFFER_CAPACITY)), + termios: SpinNoIrq::new(Termios::default()), + winsize: SpinNoIrq::new(WinSize::default()), + }) } - /// kernel reads data. - pub fn read(&self, buf: &mut [u8]) -> usize { - let read_buf = &self.read_buf; - - // len of this reading - let len = buf.len().min(read_buf.len()); - - // return if nothing can be read - if len == 0 { - return 0; + /// Reads bytes from terminal, dispatching to raw/canonical mode handlers + pub fn read(&self, dst: &mut [u8]) -> AxResult { + let termios = *self.termios.lock(); + if termios.is_raw_mode() { + self.raw_mode_read(dst, &termios) + } else { + self.canonical_mode_read(dst, &termios) } + } - // copy data from read_buf to `buf` - for ch in buf.iter_mut().take(len) { - *ch = read_buf.pop(); + /// Canonical mode is enabled by setting the `ICANON` flag in terminal attributes. + /// It provides line-oriented input processing with the following characteristics: + /// + /// 1. **Line Assembly**: + /// Input is buffered into lines terminated by any of these characters: + /// - `NL` (newline, ASCII `\n`) + /// - `EOL` (end-of-line, configurable via `VEOL`) + /// - `EOL2` (secondary EOL, configurable via `VEOL2` if `IEXTEN` is set) + /// - `EOF` (end-of-file, typically `Ctrl+D` via `VEOF`, but ignored if at line start) + /// + /// All terminators except `EOF` are included in the input line passed to the reading process. + /// + /// 2. **Line Editing**: + /// Supports in-line editing with these control characters: + /// - `ERASE` (backspace, deletes previous character) + /// - `KILL` (deletes entire line) + /// - `WERASE` (word erase, deletes previous word if `IEXTEN` is set) + /// + /// 3. **Read Behavior**: + /// - A `read()` call returns only when a complete line is available. + /// - If the requested byte count is smaller than the line length, partial data is returned; subsequent reads fetch remaining bytes. + /// - (unimplemented) If interrupted by a signal without `SA_RESTART` flag, `read()` terminates early (partial read or `EINTR` error). + fn canonical_mode_read(&self, dst: &mut [u8], termios: &Termios) -> AxResult { + let mut buf = self.read_buffer.lock(); + let available_read = buf.available_read(); + if available_read == 0 { + return Err(axio::Error::WouldBlock); } + let max_read_len = available_read.min(dst.len()); + let mut read_len = 0; + while read_len < max_read_len { + let Some(next_char) = buf.dequeue() else { + break; + }; + + if is_line_terminator(next_char, termios) { + if !is_eof(next_char, termios) { + dst[read_len] = next_char; + read_len += 1; + } + break; // Stop at line terminator + } - len + dst[read_len] = next_char; + read_len += 1; + } + Ok(read_len) } - /// driver sends data from device for processing and echoing. - /// running in irq. - pub fn receive_buf(&self, tty: Arc, buf: &[u8]) { - use crate::constant::*; + /// Non-canonical mode(raw mode, enabled by unsetting `ICANON`) allows applications like `vi` and `less` + /// to read input immediately without line termination. + /// + /// In this mode: + /// - Input bypasses line assembly and becomes visible to applications byte-by-byte. + /// - Special editing characters (e.g., `ERASE`, `KILL`) are disabled. + /// - Read completion is controlled by `VMIN` (minimum bytes) and `VTIME` (timeout) in `termios.c_cc`. + /// + /// ### **VMIN and VTIME Behavior** + /// The interaction of `VMIN` and `VTIME` defines four distinct read modes: + /// + /// #### **1. `VMIN = 0`, `VTIME = 0` (Non-Blocking Poll)** + /// - **Behavior**: + /// - Returns immediately with available bytes (up to requested count) or `0` if no data. + /// - **Use Case**: + /// - Non-blocking input checks (similar to `O_NONBLOCK` flag, but returns `0` instead of `EAGAIN`). + /// + /// #### **2. `VMIN > 0`, `VTIME = 0` (Blocking Read)** + /// - **Behavior**: + /// - Blocks indefinitely until at least `min(VMIN, requested_bytes)` are read. + /// - **Use Case**: + /// - Efficient single-byte input (e.g., `less` sets `VMIN=1` to wait for keystrokes without CPU polling). + /// + /// #### **3. `VMIN = 0`, `VTIME > 0` (Timeout-Based Read)** (TODO) + /// - **Behavior**: + /// - Starts a timer (`VTIME × 0.1 seconds`) on `read()` call. + /// - Returns immediately if ≥1 byte arrives or timer expires (returns `0` on timeout). + /// - **Use Case**: + /// - Handling serial devices (e.g., modems) to avoid indefinite hangs. + /// + /// #### **4. `VMIN > 0`, `VTIME > 0` (Inter-Byte Timeout)** (TODO) + /// - **Behavior**: + /// - After receiving the first byte, a timer resets for each subsequent byte. + /// - Returns when: + /// - `min(VMIN, requested_bytes)` are read, **OR** + /// - Inter-byte gap exceeds `VTIME × 0.1 seconds` (returns available bytes ≥1). + /// - **Use Case**: + /// - Detecting escape sequences (e.g., terminal arrow keys generating multi-byte sequences like `←` → `\x1B[D`). + /// - Applications like `vi` use short timeouts (e.g., 0.2s) to distinguish keystrokes from manual input. + fn raw_mode_read(&self, dst: &mut [u8], termios: &Termios) -> AxResult { + let vmin = *termios.special_char(CC_C_CHAR::VMIN); + let vtime = *termios.special_char(CC_C_CHAR::VTIME); + let read_len = { + if vmin == 0 && vtime == 0 { + self.polling_read(dst) + } else if vmin > 0 && vtime == 0 { + self.blocking_read(dst, vmin)? + } else if vmin == 0 && vtime > 0 { + todo!() + } else if vmin > 0 && vtime > 0 { + todo!() + } else { + unreachable!() + } + }; + Ok(read_len) + } - let rec_buf = &self.rec_buf; + /// Used in non-canonical mode read + /// + /// Returns immediately with available bytes (up to requested count) or `0` if no data + fn polling_read(&self, dst: &mut [u8]) -> usize { + let mut buf = self.read_buffer.lock(); + let max_read_len = buf.available_read().min(dst.len()); + buf.read(&mut dst[..max_read_len]) + } - // save data to receive buffer - for ch in buf { - rec_buf.push(*ch); + /// Used in non-canonical mode read + /// + /// Blocks indefinitely until at least `min(VMIN, requested_bytes)` are read. + fn blocking_read(&self, dst: &mut [u8], vmin: u8) -> AxResult { + let mut buf = self.read_buffer.lock(); + let buffer_len = buf.available_read(); + if buffer_len >= dst.len() { + return Ok(buf.read(dst)); } + if buffer_len < vmin as usize { + return Err(axio::Error::WouldBlock); + } + Ok(buf.read(&mut dst[..buffer_len])) + } - // process chars in receive buffer - while rec_buf.len() > 0 { - let ch = rec_buf.see(0); + /// Processes an input character through terminal line discipline + /// + /// Applies termios settings for character conversion, echo handling, and + /// buffering in either raw or canonical mode. + pub fn push_char(&self, mut ch: u8, mut echo: F) { + let termios = self.termios.lock(); - // if char may be arrow char - if ch == ARROW_PREFIX[0] { - // no enough len, just break, waitting for next time - if rec_buf.len() < 3 { - break; - } + // Convert CR to LF if ICRNL is enabled + if termios.contain_icrnl() && ch == b'\r' { + ch = b'\n'; + } - // enough len, but not a arrow char, just ignore - if rec_buf.see(1) != ARROW_PREFIX[1] { - rec_buf.pop(); - rec_buf.pop(); - break; + // Echo handling + if termios.contain_echo() { + match ch { + b'\n' => echo("\n"), // Standard newline echo + b'\r' => echo("\r\n"), // Carriage return expands to CR+LF + ch if ch == *termios.special_char(CC_C_CHAR::VERASE) => { + // Visual backspace sequence: + // 1. `\x08` (Backspace) moves cursor left + // 2. ` ` (Space) overwrites character + // 3. `\x08` moves cursor left again to final position + // This achieves character erasure in terminal display + echo(core::str::from_utf8(b"\x08 \x08").unwrap()); } - - // it is an arrow char, get it - rec_buf.pop(); - rec_buf.pop(); - let ch = rec_buf.pop(); - - // deal with arrow char - match ch { - LEFT => { - let mut lock = self.echo_buf.lock(); - // if can go left - if lock.col > 0 { - self.write(tty.clone(), &[ARROW_PREFIX[0], ARROW_PREFIX[1], ch]); - lock.col -= 1; - } - } - RIGHT => { - let mut lock = self.echo_buf.lock(); - // if can go right - if lock.col < lock.buffer.len() { - self.write(tty.clone(), &[ARROW_PREFIX[0], ARROW_PREFIX[1], ch]); - lock.col += 1; - } - } - _ => { - // it is UP/DOWN, just ignore - } + ch if is_printable_char(ch) => { + echo(core::str::from_utf8(&[ch]).unwrap()); } - // not a arrow char, handle it as a normal char - } else { - let ch = rec_buf.pop(); - match ch { - CR | LF => { - // always '\n' - let ch = LF; - - // echo - self.write(tty.clone(), &[ch]); - - // push this char to echo buffer - let mut lock = self.echo_buf.lock(); - lock.buffer.push(ch); - - // copy echo buffer to read buffer - // FIXME: currently will push all data to read_buf - let len = lock.buffer.len(); - for _ in 0..len { - self.read_buf.push(lock.buffer.pop()); - } - - // echo buffer's column is set to 0 - lock.col = 0; - } - BS | DEL => { - let mut lock = self.echo_buf.lock(); - let col = lock.col; - let len = lock.buffer.len(); - // if can delete - if col > 0 { - // perform a backspace - self.write(tty.clone(), &[BS, SPACE, BS]); - - // if cursor is not on the rightmost - if col != len { - for i in col..len { - let ch = lock.buffer.see(i); - self.write(tty.clone(), &[ch]); - } - self.write(tty.clone(), &[SPACE]); - for _ in 0..(len - col + 1) { - self.write( - tty.clone(), - &[ARROW_PREFIX[0], ARROW_PREFIX[1], LEFT], - ); - } - } - - // modify echo buffer - lock.buffer.delete(col - 1); - lock.col -= 1; - } - } - _ => { - // process normal chars. - let mut echo_buf = self.echo_buf.lock(); - let col = echo_buf.col; - let len = echo_buf.buffer.len(); - - // echo - self.write(tty.clone(), &[ch]); - - // if cursor is not on the rightmost - if col != len { - for i in col..len { - self.write(tty.clone(), &[echo_buf.buffer.see(i)]); - } - for _ in 0..(len - col) { - self.write(tty.clone(), &[ARROW_PREFIX[0], ARROW_PREFIX[1], LEFT]); - } - } - - // modify echo buffer - echo_buf.buffer.insert(ch, col); - echo_buf.col += 1; - } + ch if is_ctrl_char(ch) && termios.contain_echo_ctl() => { + // Convert control character (0x01-0x1F) to ^X notation: + // 0x01 → 1 + 'A' - 1 = 65 → 'A' → "^A" + let ctrl_char = format!("^{}", char::from_u32((ch + b'A' - 1) as u32).unwrap()); + echo(&ctrl_char); } + _ => {} } } + // Raw mode + if termios.is_raw_mode() { + self.read_buffer.lock().force_enqueue(ch); + return; + } + + // Canonical mode (line-editing) + if ch == *termios.special_char(CC_C_CHAR::VKILL) { + // Erase current line + self.current_line.lock().clear(); + } + + if ch == *termios.special_char(CC_C_CHAR::VERASE) { + // Type backspace + let mut current_line = self.current_line.lock(); + if !current_line.is_empty() { + current_line.dequeue(); + } + } + + if is_line_terminator(ch, &termios) { + // If a new line is met, all bytes in current_line will be moved to read_buffer + let mut current_line = self.current_line.lock(); + current_line.force_enqueue(ch); + let current_line_chars = current_line.drain(); + for char in current_line_chars { + self.read_buffer.lock().force_enqueue(char); + } + } + + if is_printable_char(ch) { + self.current_line.lock().enqueue(ch); + } } - /// kernel writes data to device. - pub fn write(&self, tty: Arc, buf: &[u8]) -> usize { - let mut len = 0; - let driver = tty.driver(); - for ch in buf { - len += 1; - // call driver's method - (driver.ops.putchar)(*ch); + /// Ldisc read buffer length + pub fn read_buffer_len(&self) -> usize { + self.read_buffer.lock().available_read() + } + + /// poll Ldisc + pub fn poll(&self) -> PollState { + let readable = !self.read_buffer.lock().is_empty(); + PollState { + readable, + writable: true, + pollhup: false, } - len } + + /// Get termios + pub fn termios(&self) -> Termios { + *self.termios.lock() + } + + /// Set termios + pub fn set_termios(&self, termios: &Termios) { + *self.termios.lock() = *termios + } + + /// Get winsize + pub fn winsize(&self) -> WinSize { + *self.winsize.lock() + } + + /// Set winsize + pub fn set_winsize(&self, winsize: &WinSize) { + *self.winsize.lock() = *winsize + } + + /// Clear all inputs + pub fn clear_input(&self) { + self.current_line.lock().clear(); + self.read_buffer.lock().clear(); + } +} + +/// Terminal window size information (rows/columns) +/// +/// Follows POSIX `struct winsize` convention, pixel fields unused in most TTY implementations +#[derive(Debug, Clone, Copy, Default)] +#[repr(C)] +pub struct WinSize { + /// Number of terminal rows + ws_row: u16, + /// Number of terminal columns + ws_col: u16, + /// Horizontal size in pixels + ws_xpixel: u16, + /// Vertical size in pixels + ws_ypixel: u16, +} + +/// printable characters​ refer to characters that can be visually displayed in terminals +/// +/// ASCII Printable Characters: Ranging from decimal 32 (space) to 126 (the ~ symbol), +/// covering letters (uppercase and lowercase), digits, punctuation marks (e.g., !@#$%), and spaces. +fn is_printable_char(ch: u8) -> bool { + (0x20..0x7f).contains(&ch) } -impl Default for TtyLdisc { - fn default() -> Self { - Self::new() +/// Checks if the character is a control character (0x00-0x1F), excluding \r and \n +fn is_ctrl_char(ch: u8) -> bool { + if ch == b'\r' || ch == b'\n' { + return false; + } + (0..0x20).contains(&ch) +} + +/// Checks if the character matches the EOF control character (VEOF) +fn is_eof(ch: u8, termios: &Termios) -> bool { + ch == *termios.special_char(CC_C_CHAR::VEOF) +} + +/// Checks if the character is a line terminator (\n, VEOF, VEOL, or VEOL2 with IEXTEN) +fn is_line_terminator(ch: u8, termios: &Termios) -> bool { + if ch == b'\n' + || ch == *termios.special_char(CC_C_CHAR::VEOF) + || ch == *termios.special_char(CC_C_CHAR::VEOL) + { + return true; + } + if termios.contain_iexten() && ch == *termios.special_char(CC_C_CHAR::VEOL2) { + return true; } + false } diff --git a/crates/tty/src/lib.rs b/crates/tty/src/lib.rs index e6e30a134..c8313a2b5 100644 --- a/crates/tty/src/lib.rs +++ b/crates/tty/src/lib.rs @@ -7,66 +7,76 @@ * See the Mulan PSL v2 for more details. */ -//! Init -//! -//! firstly, a driver registers itself to get its index. -//! next, the driver registers all devices it found to get their indices. -//! -//! Read -//! -//! when a device receives data, it will cause a irq. -//! then the driver sends the data to tty layer using their indices. -//! finally, kernel will get the data using the device's name. -//! -//! Write -//! -//! kernel writes data to a device using its name. +//! Portions of this code are inspired by the design of Asterinas OS #![no_std] - extern crate alloc; -mod buffer; -mod constant; mod driver; -mod ldisc; +pub mod ioctl; +pub mod ldisc; +pub mod termios; mod tty; -use driver::get_driver_by_index; +use alloc::{ffi::CString, sync::Arc}; +use axerrno::AxResult; +use axio::PollState; +use axlog::{ax_print, ax_println}; +use driver::TtyDriver; +use lazy_init::LazyInit; -pub use driver::{register_device, register_driver, TtyDriverOps}; -pub use tty::{get_all_device_names, get_device_by_name}; +use spin::once::Once; +use tty::Tty; +/// Global singleton instance for the default TTY device +static N_TTY: Once> = Once::new(); -/// called by driver when irq, to send data from hardware. -pub fn tty_receive_buf(driver_index: usize, device_index: usize, buf: &[u8]) { - // check the validation of index - if let Some(driver) = get_driver_by_index(driver_index) { - if let Some(tty) = driver.get_device_by_index(device_index) { - tty.ldisc().receive_buf(tty.clone(), buf); - } - } +/// all tty drivers. +/// only be written when registering a driver. +/// +/// Current now there is only ttyS device +static TTY_DRIVER: LazyInit> = LazyInit::new(); + +/// Initializes the TTY subsystem, creates a new TTY device and registers it +pub fn init_tty() { + let tty = Tty::new(CString::new("ttyS").unwrap()); + N_TTY.call_once(|| tty.clone()); + let driver = TtyDriver::new(); + driver.add_tty(tty); + TTY_DRIVER.init_by(driver); } -/// called by kernel to read a tty device. -pub fn tty_read(buf: &mut [u8], dev_name: &str) -> usize { - if let Some(tty) = get_device_by_name(dev_name) { - tty.ldisc().read(buf) - } else { - 0 - } +/// Pushes received data into the TTY driver's input buffer +/// # Note: This implementation is ​**only enabled for `aarch64` architecture** since others haven't implement irq +pub fn tty_receive_buf(buf: &[u8]) { + TTY_DRIVER.try_get().unwrap().push_slice(buf); +} + +/// Pushes received char into the TTY driver's input buffer +pub fn tty_receive_char(ch: u8) { + TTY_DRIVER.try_get().unwrap().push_char(ch); +} + +/// Reads data from TTY line discipline into the destination buffer +pub fn tty_read(dst: &mut [u8]) -> AxResult { + N_TTY.get().unwrap().ldisc.read(dst) +} + +/// Checks if TTY has data available for reading +pub fn tty_poll() -> PollState { + N_TTY.get().unwrap().ldisc.poll() } -/// called by kernel to write a tty device. -pub fn tty_write(buf: &[u8], dev_name: &str) -> usize { - if let Some(tty) = get_device_by_name(dev_name) { - tty.ldisc().write(tty.clone(), buf) +/// Writes data to TTY output, handles UTF-8 and binary content +pub fn tty_write(src: &[u8]) -> AxResult { + if let Ok(content) = alloc::str::from_utf8(src) { + ax_print!("{}", content); } else { - 0 + ax_println!("Not utf-8 content: {:?}", src); } + Ok(src.len()) } -/// init -pub fn init() { - driver::init(); - tty::init(); +/// Handles TTY-specific ioctl commands +pub fn tty_ioctl(cmd: usize, arg: usize) -> AxResult { + N_TTY.get().unwrap().ioctl(cmd, arg) } diff --git a/crates/tty/src/termios.rs b/crates/tty/src/termios.rs new file mode 100644 index 000000000..d6ff59b47 --- /dev/null +++ b/crates/tty/src/termios.rs @@ -0,0 +1,411 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ +//! Termios Struct for Tty and Pty +#![allow(non_camel_case_types)] +#![allow(clippy::upper_case_acronyms)] +#![allow(dead_code)] +use bitflags::bitflags; +const NCCS: usize = 19; +type CcT = u8; + +/// Represents terminal I/O settings and control characters. +/// +/// This structure mirrors the behavior of the POSIX `termios` structure, +/// providing configuration for serial communication, line discipline, and +/// terminal control. It is typically used with `tcgetattr` and `tcsetattr` +/// for configuring TTY devices. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Termios { + /// Input mode flags + c_iflags: C_IFLAGS, + /// Output mode flags + c_oflags: C_OFLAGS, + /// Control mode flags (e.g., baud rate) + c_cflags: C_CFLAGS, + /// Local mode flags (e.g., echo, canonical mode). + c_lflags: C_LFLAGS, + /// Line discipline type + c_line: CcT, + /// Array of control characters + c_cc: [CcT; NCCS], +} + +impl Default for Termios { + fn default() -> Self { + Self { + c_iflags: C_IFLAGS::default(), + c_oflags: C_OFLAGS::default(), + c_cflags: C_CFLAGS::default(), + c_lflags: C_LFLAGS::default(), + c_line: 0, + c_cc: [ + 3, // VINTR Ctrl-C + 28, // VQUIT + 127, // VERASE + 21, // VKILL + 4, // VEOF Ctrl-D + 0, // VTIME + 1, // VMIN + 0, // VSWTC + 17, // VSTART + 19, // VSTOP + 26, // VSUSP Ctrl-Z + 255, // VEOL + 18, // VREPAINT + 15, // VDISCARD + 23, // VWERASE + 22, // VLNEXT + 255, // VEOL2 + 0, 0, + ], + } + } +} + +impl Termios { + /// Gets the value of a specific control character (e.g. VINTR, VERASE) + pub fn special_char(&self, cc_c_char: CC_C_CHAR) -> &CcT { + &self.c_cc[cc_c_char as usize] + } + + /// Checks if terminal is in raw mode (non-canonical input processing) + pub fn is_raw_mode(&self) -> bool { + !self.c_lflags.contains(C_LFLAGS::ICANON) + } + + /// Checks if carriage return to newline conversion is enabled (\r → \n) + pub fn contain_icrnl(&self) -> bool { + self.c_iflags.contains(C_IFLAGS::ICRNL) + } + + /// Checks if signal-generating characters (e.g. Ctrl+C) are enabled + pub fn contain_isig(&self) -> bool { + self.c_lflags.contains(C_LFLAGS::ISIG) + } + + /// Checks if input character echoing is enabled + pub fn contain_echo(&self) -> bool { + self.c_lflags.contains(C_LFLAGS::ECHO) + } + + /// Checks if control characters are echoed as ^X + pub fn contain_echo_ctl(&self) -> bool { + self.c_lflags.contains(C_LFLAGS::ECHOCTL) + } + + /// Checks if extended input character processing is enabled + pub fn contain_iexten(&self) -> bool { + self.c_lflags.contains(C_LFLAGS::IEXTEN) + } +} +bitflags! { + #[repr(C)] + #[derive(Debug, Clone, Copy)] + /// Input mode flags + pub struct C_IFLAGS: u32 { + // ============================================== + // Break condition handling + // ============================================== + + /// IGNBRK: IGNore BReaK condition + /// Ignore break conditions (BREAK treated as valid NULL byte) + const IGNBRK = 0x001; + + /// BRKINT: BReaK INTerrupt + /// Generate SIGINT on break condition (when not ignored by IGNBRK) + const BRKINT = 0x002; + + // ============================================== + // Parity/error handling + // ============================================== + + /// IGNPAR: IGNore PARity errors + /// Ignore bytes with framing/parity errors (keep invalid bytes) + const IGNPAR = 0x004; + + /// PARMRK: PARity MarK + /// Insert error marker bytes (0xFF + 0x00) before invalid bytes + const PARMRK = 0x008; + + /// INPCK: INput Parity ChecK + /// Enable parity checking for incoming bytes + const INPCK = 0x010; + + // ============================================== + // Character processing + // ============================================== + + /// ISTRIP: STRIP high bit + /// Strip 8th bit of input characters (force 7-bit processing) + const ISTRIP = 0x020; + + /// INLCR: INput NL to CR + /// Map received newline (NL, 0x0A) to carriage return (CR, 0x0D) + const INLCR = 0x040; + + /// IGNCR: IGNore CR + /// Ignore received carriage return (CR, 0x0D) characters + const IGNCR = 0x080; + + /// ICRNL: Input CR to NL + /// Map received carriage return (CR, 0x0D) to newline (NL, 0x0A) + const ICRNL = 0x100; + + /// IUCLC: Input UpperCase to LowerCase + /// Map uppercase letters (A-Z) to lowercase (a-z) on input + const IUCLC = 0x0200; + + // ============================================== + // Flow control + // ============================================== + + /// IXON: Enable XON/XOFF output control + /// Enable software flow control for output (Ctrl-Q/Ctrl-S) + const IXON = 0x0400; + + /// IXOFF: Enable XON/XOFF input control + /// Enable software flow control for input (Ctrl-Q/Ctrl-S) + const IXOFF = 0x1000; + + /// IXANY: Enable any character to restart output + /// Allow any character (not just XON) to resume paused output + const IXANY = 0x800; + + // ============================================== + // Special behaviors + // ============================================== + + /// IMAXBEL: Input MAX buffer BEL + /// Ring terminal bell when input buffer is full + const IMAXBEL = 0x2000; + + /// IUTF8: Input UTF-8 + /// Enable UTF-8 input processing (required for canonical mode) + const IUTF8 = 0x4000; + } +} +impl Default for C_IFLAGS { + fn default() -> Self { + C_IFLAGS::ICRNL | C_IFLAGS::IXON + } +} + +bitflags! { + #[repr(C)] + #[derive(Debug, Clone, Copy)] + /// Output mode flags + pub struct C_OFLAGS: u32 { + // ============================================== + // Output processing control + // ============================================== + + /// OPOST: Output POST-process + /// Enable output post-processing (required for other transformations) + const OPOST = 1 << 0; + + /// ONLCR: Output NL to CR-NL + /// Map newline (NL, 0x0A) to CR-NL sequence (0x0D 0x0A) + const ONLCR = 1 << 2; + + /// OCRNL: Output CR to NL + /// Map carriage return (CR, 0x0D) to newline (NL, 0x0A) + const OCRNL = 1 << 3; + + /// ONOCR: Output No CR at column 0 + /// Discard CR characters when at start of line (column 0) + const ONOCR = 1 << 4; + + /// ONLRET: Output NL performs RETurn + /// Newline (NL) moves cursor to column 0 (carriage return behavior) + const ONLRET = 1 << 5; + + // ============================================== + // Character case conversion + // ============================================== + + /// OLCUC: Output LowerCase UpperCase + /// Convert lowercase letters (a-z) to uppercase (A-Z) on output + const OLCUC = 1 << 1; + + // ============================================== + // Fill/delay handling (legacy systems) + // ============================================== + + /// OFILL: Output FILL characters + /// Send fill characters for timing delays (obsolete on modern systems) + const OFILL = 1 << 6; + + /// OFDEL: Output DEL as fill character + /// Use DEL (0x7F) instead of NUL (0x00) for fill (requires OFILL) + const OFDEL = 1 << 7; + } +} + +impl Default for C_OFLAGS { + fn default() -> Self { + C_OFLAGS::OPOST | C_OFLAGS::ONLCR + } +} + +/// Control mode flags +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct C_CFLAGS(u32); + +impl Default for C_CFLAGS { + /// Creates default c_cflags configuration: + /// - Baud rate: 38400 (B38400) + /// - Data bits: 8 (CS8) + /// - Enable receiver (CREAD) + fn default() -> Self { + /// Enable receiver flag (termios CREAD) + const CREAD: u32 = 0x00000080; + let cbaud = C_CFLAGS_BAUD::B38400 as u32; + let csize = C_CFLAGS_CSIZE::CS8 as u32; + let c_cflags = cbaud | csize | CREAD; + Self(c_cflags) + } +} + +/// Baud rate constants for termios c_cflags field +/// +/// These values correspond to the `B*` constants in Unix termios implementations. +/// Used to set serial communication speed. The special value `B0` indicates hang-up. +#[repr(u32)] +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub enum C_CFLAGS_BAUD { + /// Hang up/discard data + B0 = 0x00000000, + B50 = 0x00000001, + B75 = 0x00000002, + B110 = 0x00000003, + B134 = 0x00000004, + B150 = 0x00000005, + B200 = 0x00000006, + B300 = 0x00000007, + B600 = 0x00000008, + B1200 = 0x00000009, + B1800 = 0x0000000a, + B2400 = 0x0000000b, + B4800 = 0x0000000c, + B9600 = 0x0000000d, + B19200 = 0x0000000e, + B38400 = 0x0000000f, +} + +bitflags! { + #[repr(C)] + #[derive(Debug, Clone, Copy)] + /// Local mode flags (e.g., echo, canonical mode). + pub struct C_LFLAGS: u32 { + /// Generate signals (SIGINT/SIGQUIT/SIGSTOP) for control characters. + const ISIG = 0x00001; + /// Enable canonical input mode (line-by-line input with editing). + const ICANON = 0x00002; + /// Obsolete flag for uppercase/lowercase input processing (use `IUTF8` instead). + const XCASE = 0x00004; + /// Echo input characters back to the terminal. + const ECHO = 0x00008; + /// Echo ERASE character as backspace-space-backspace sequence (visual erase). + const ECHOE = 0x00010; + /// Echo KILL character (line deletion) by erasing the entire line. + const ECHOK = 0x00020; + /// Echo newline (`NL`) even if `ECHO` is disabled. + const ECHONL = 0x00040; + /// Disable flushing after SIGINT/SIGQUIT signals. + const NOFLSH = 0x00080; + /// Send SIGTTOU when background processes write to terminal (job control). + const TOSTOP = 0x00100; + /// Echo control characters as `^X` (Ctrl-X notation). + const ECHOCTL = 0x00200; + /// Echo erased characters visually (between `\` and `/` during erase). + const ECHOPRT = 0x00400; + /// Erase entire line on KILL character (enhanced `ECHOK` behavior). + const ECHOKE = 0x00800; + /// Output is being flushed (set internally; do not configure manually). + const FLUSHO = 0x01000; + /// Redraw pending input after next read (typeahead handling for line editing). + const PENDIN = 0x04000; + /// Enable extended input character processing (non-canonical mode features). + const IEXTEN = 0x08000; + /// External processing mode (used with `ICANON` for shell job control). + const EXTPROC = 0x10000; + } +} + +impl Default for C_LFLAGS { + fn default() -> Self { + C_LFLAGS::ICANON + | C_LFLAGS::ECHO + | C_LFLAGS::ISIG + | C_LFLAGS::ECHOE + | C_LFLAGS::ECHOK + | C_LFLAGS::ECHOCTL + | C_LFLAGS::ECHOKE + | C_LFLAGS::IEXTEN + } +} + +#[repr(u32)] +#[doc(hidden)] +#[derive(Clone, Copy)] +/// These values correspond to the `CS*` constants in Unix termios implementations. +/// Used with `CSIZE_MASK` to set/clear the number of data bits per character. +pub enum C_CFLAGS_CSIZE { + CS5 = 0x00000000, + CS6 = 0x00000010, + CS7 = 0x00000020, + CS8 = 0x00000030, +} + +/* c_cc characters index*/ +#[repr(u32)] +#[derive(Debug, Clone, Copy)] +/// Represents indices for terminal control characters in the `c_cc` array of [`Termios`]. +/// +/// These constants define positions in the control character array (`c_cc`), which configure +/// terminal behavior (e.g., signal triggers, line editing). Values correspond to the POSIX standard +pub enum CC_C_CHAR { + /// Interrupt character (e.g., `Ctrl+C`). Sends `SIGINT` to the foreground process group. + VINTR = 0, + /// Quit character (e.g., `Ctrl+\`). Sends `SIGQUIT` to the foreground process group. + VQUIT = 1, + /// Erase character (e.g., `Backspace` or `Ctrl+?`). Deletes the preceding character in line-editing mode. + VERASE = 2, + /// Kill character (e.g., `Ctrl+U`). Deletes the entire line in line-editing mode. + VKILL = 3, + /// End-of-file character (e.g., `Ctrl+D`). Signals EOF in canonical input mode. + VEOF = 4, + /// Timeout value (in tenths of a second) for non-canonical reads. Used with [`VMIN`](Self::VMIN). + VTIME = 5, + /// Minimum number of bytes to read in non-canonical mode. Used with [`VTIME`](Self::VTIME). + VMIN = 6, + /// Switch process group character (obsolete; unused in modern systems). + VSWTC = 7, + /// Resume output character (e.g., `Ctrl+Q`). Restarts paused output (XON). + VSTART = 8, + /// Suspend output character (e.g., `Ctrl+S`). Pauses terminal output (XOFF). + VSTOP = 9, + /// Suspend character (e.g., `Ctrl+Z`). Sends `SIGTSTP` to the foreground process group. + VSUSP = 10, + /// End-of-line character (alternate; `\0` by default). Terminates input in canonical mode. + VEOL = 11, + /// Reprint character (e.g., `Ctrl+R`). Redisplays the current line in line-editing mode. + VREPRINT = 12, + /// Discard character (e.g., `Ctrl+O`). Toggles discarding of pending output. + VDISCARD = 13, + /// Word-erase character (e.g., `Ctrl+W`). Deletes the preceding word in line-editing mode. + VWERASE = 14, + /// Literal-next character (e.g., `Ctrl+V`). Quotes the next input character (disables special handling). + VLNEXT = 15, + /// Secondary end-of-line character (rarely used). Alternative to [`VEOL`](Self::VEOL). + VEOL2 = 16, +} diff --git a/crates/tty/src/tty.rs b/crates/tty/src/tty.rs index 2a54c2164..80db25f55 100644 --- a/crates/tty/src/tty.rs +++ b/crates/tty/src/tty.rs @@ -6,99 +6,107 @@ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PSL v2 for more details. */ - -use core::sync::atomic::AtomicUsize; - -use alloc::{string::String, sync::Arc, vec, vec::Vec}; -use lazy_init::LazyInit; -use spinlock::SpinNoIrq; - -use crate::{driver::TtyDriver, ldisc::TtyLdisc}; - -/// all registered devices. -pub(super) static ALL_DEVICES: LazyInit>>> = LazyInit::new(); - -/// tty device. -#[derive(Debug)] -pub struct TtyStruct { - /// driver of device. - driver: Arc, - - /// device's line discipline. - ldisc: Arc, - - /// index of device. - index: AtomicUsize, - - /// name of device. - name: SpinNoIrq, +#![allow(dead_code)] +use core::sync::atomic::{AtomicUsize, Ordering}; + +use crate::{ + ioctl::IoctlCommand, + ldisc::{Ldisc, WinSize}, + termios::Termios, +}; +use alloc::{ffi::CString, sync::Arc}; +use axerrno::AxResult; +use axlog::{ax_print, warn}; +use num_enum::FromPrimitive; + +/// Terminal device representation with line discipline and process control +/// +/// Manages character input/output processing and foreground process group +pub struct Tty { + /// Device name + name: CString, + /// Line discipline handling input processing + pub ldisc: Arc, + // TODO: Implement process group management + fg_pgid: AtomicUsize, } -impl TtyStruct { - pub fn new(driver: Arc) -> Self { - Self { - driver: driver.clone(), - ldisc: Arc::new(TtyLdisc::new()), - index: AtomicUsize::new(0), - name: SpinNoIrq::new(String::new()), - } - } - - /// get tty line discipline. - pub fn ldisc(&self) -> Arc { - self.ldisc.clone() +impl Tty { + /// Creates new TTY device + pub fn new(name: CString) -> Arc { + Arc::new(Self { + name, + ldisc: Ldisc::new(), + fg_pgid: AtomicUsize::new(1000), + }) } - /// set device index. - pub fn set_index(&self, index: usize) { - self.index - .store(index, core::sync::atomic::Ordering::Relaxed); + // Handles incoming character with line discipline processing + /// + /// Invokes callback for immediate echo when applicable + pub fn push_char(&self, ch: u8) { + self.ldisc.push_char(ch, |content| ax_print!("{}", content)); } - /// set name of device - pub fn set_name(&self, name: &str) { - let mut lock = self.name.lock(); - lock.clone_from(&String::from(name)); + /// Retrieves current foreground process group ID + pub fn fg_pgid(&self) -> usize { + self.fg_pgid.load(Ordering::Acquire) } - /// Convert a tty structure into a name, reflecting the kernel naming policy. - pub fn name(&self) -> String { - self.name.lock().clone() + /// Updates foreground process group ID with release ordering + pub fn set_fg_pgid(&self, fg_pgid: usize) { + self.fg_pgid.store(fg_pgid, Ordering::Release); } - /// get device's driver. - pub fn driver(&self) -> Arc { - self.driver.clone() - } -} - -/// called by kernel to get a device. -pub fn get_device_by_name(name: &str) -> Option> { - let lock = ALL_DEVICES.lock(); - for tty in lock.iter() { - if tty.name() == name { - return Some(tty.clone()); + pub fn ioctl(&self, cmd: usize, arg: usize) -> AxResult { + match IoctlCommand::from_primitive(cmd as u32) { + IoctlCommand::TCGETS | IoctlCommand::TCGETA => { + let termios = self.ldisc.termios(); + unsafe { *(arg as *mut Termios) = termios }; + Ok(0) + } + IoctlCommand::TCSETS | IoctlCommand::TCSETSW => { + let termios = unsafe { *(arg as *const Termios) }; + self.ldisc.set_termios(&termios); + Ok(0) + } + IoctlCommand::TIOCGPGRP => { + let fg_pgid = self.fg_pgid() as u32; + unsafe { + *(arg as *mut u32) = fg_pgid; + } + Ok(0) + } + IoctlCommand::TIOCSPGRP => { + let fg_pgid = unsafe { *(arg as *const u32) } as usize; + self.set_fg_pgid(fg_pgid); + Ok(0) + } + + IoctlCommand::TCSETSF => { + let termios = unsafe { *(arg as *const Termios) }; + self.ldisc.set_termios(&termios); + self.ldisc.clear_input(); + Ok(0) + } + IoctlCommand::TIOCGWINSZ => { + let winsize = self.ldisc.winsize(); + unsafe { *(arg as *mut WinSize) = winsize }; + Ok(0) + } + IoctlCommand::TIOCSWINSZ => { + let winsize = unsafe { *(arg as *const WinSize) }; + self.ldisc.set_winsize(&winsize); + Ok(0) + } + IoctlCommand::TIOCSCTTY => { + warn!("TtyIoctlCmd::TIOCSCTTY not implemented"); + Ok(0) + } + ioctl_cmd => { + warn!("unimplemented tty ioctl, cmd {ioctl_cmd:?} {cmd:x}"); + Ok(0) + } } } - None -} - -/// called by kernel to get all devices' names. -/// usually used in init to get the view of tty. -pub fn get_all_device_names() -> Vec { - let mut ret = vec![]; - let alldev = ALL_DEVICES.lock(); - for dev in alldev.iter() { - ret.push(dev.name()); - } - ret -} - -/// save a device when registered. -pub fn add_one_device(tty: Arc) { - ALL_DEVICES.lock().push(tty); -} - -pub fn init() { - ALL_DEVICES.init_by(SpinNoIrq::new(vec![])); } diff --git a/doc/README.md b/doc/README.md index 18b742254..ac057948a 100644 --- a/doc/README.md +++ b/doc/README.md @@ -40,8 +40,6 @@ * [linked_list](../crates/linked_list): Linked lists that supports arbitrary removal in constant time. * [page_table](../crates/page_table): Generic page table structures for various hardware architectures. * [page_table_entry](../crates/page_table_entry): Page table entry definition for various hardware architectures. -* [percpu](../crates/percpu): Define and access per-CPU data structures. -* [percpu_macros](../crates/percpu_macros): Macros to define and access a per-CPU data structure. * [ratio](../crates/ratio): The type of ratios and related operations. * [scheduler](../crates/scheduler): Various scheduler algorithms in a unified interface. * [slab_allocator](../crates/slab_allocator): Slab allocator for `no_std` systems. Uses multiple slabs with blocks of different sizes and a linked list for blocks larger than 4096 bytes. diff --git a/doc/build.md b/doc/build.md index 00f8af46e..af4772d0f 100644 --- a/doc/build.md +++ b/doc/build.md @@ -40,7 +40,7 @@ What happens when "make A=apps/net/httpserver ARCH=aarch64 LOG=info NET=y SMP=1 - Following this, it was found that the `qemu.mk` file would call run_qemu. Similar to the build process, the execution process would also use conditional selection and run. - At runtime, Ruxos first performs some boot operations, such as executing in the riscv64 environment: ```rust - #[naked] + #[unsafe(naked)] #[no_mangle] #[link_section = ".text.boot"] unsafe extern "C" fn _start() -> ! { @@ -50,7 +50,7 @@ What happens when "make A=apps/net/httpserver ARCH=aarch64 LOG=info NET=y SMP=1 // PC = 0x8020_0000 // a0 = hartid // a1 = dtb - core::arch::asm!(" + core::arch::naked_asm!(" mv s0, a0 // save hartid mv s1, a1 // save DTB pointer la sp, {boot_stack} @@ -82,7 +82,7 @@ What happens when "make A=apps/net/httpserver ARCH=aarch64 LOG=info NET=y SMP=1 init_mmu = sym init_mmu, platform_init = sym super::platform_init, rust_main = sym rust_main, - options(noreturn), + ) } ``` diff --git a/modules/axalloc/src/lib.rs b/modules/axalloc/src/lib.rs index 6a0017a61..ff5f79c1f 100644 --- a/modules/axalloc/src/lib.rs +++ b/modules/axalloc/src/lib.rs @@ -58,6 +58,12 @@ pub struct GlobalAllocator { palloc: SpinNoIrq>, } +impl Default for GlobalAllocator { + fn default() -> Self { + Self::new() + } +} + impl GlobalAllocator { /// Creates an empty [`GlobalAllocator`]. pub const fn new() -> Self { diff --git a/modules/axsync/src/mutex.rs b/modules/axsync/src/mutex.rs index 25eb18815..dd7530dc3 100644 --- a/modules/axsync/src/mutex.rs +++ b/modules/axsync/src/mutex.rs @@ -158,7 +158,7 @@ impl Mutex { } } -impl Default for Mutex { +impl Default for Mutex { #[inline(always)] fn default() -> Self { Self::new(Default::default()) diff --git a/modules/rux9p/Cargo.toml b/modules/rux9p/Cargo.toml index 7304aff45..7476babe2 100644 --- a/modules/rux9p/Cargo.toml +++ b/modules/rux9p/Cargo.toml @@ -9,18 +9,18 @@ homepage = "https://github.com/syswonder/ruxos" repository = "https://github.com/syswonder/ruxos/tree/main/modules/rux9p" [features] -virtio-9p = ["ruxdriver/virtio-9p", "ruxdriver/virtio-9p"] -net-9p = ["ruxnet", "driver_common", "ruxdriver/dyn" , "ruxdriver/_9p"] +virtio-9p = ["ruxdriver/virtio-9p"] +net-9p = ["ruxnet", "driver_common", "ruxdriver/dyn", "ruxdriver/_9p"] need_auth = [] [dependencies] log = "0.4" -spin = "0.9" -driver_9p = { path = "../../crates/driver_9p"} -axfs_vfs = { path = "../../crates/axfs_vfs"} +spin = { workspace = true } +driver_9p = { path = "../../crates/driver_9p" } +axfs_vfs = { path = "../../crates/axfs_vfs" } axsync = { path = "../axsync" } -driver_common = { path = "../../crates/driver_common", optional = true} +driver_common = { path = "../../crates/driver_common", optional = true } -ruxfs = { path = "../ruxfs"} -ruxnet = { path = "../ruxnet", optional = true} -ruxdriver = { path = "../ruxdriver"} \ No newline at end of file +ruxfs = { path = "../ruxfs" } +ruxnet = { path = "../ruxnet", optional = true } +ruxdriver = { path = "../ruxdriver" } diff --git a/modules/rux9p/src/drv.rs b/modules/rux9p/src/drv.rs index 82951fb3e..b9924b243 100644 --- a/modules/rux9p/src/drv.rs +++ b/modules/rux9p/src/drv.rs @@ -45,7 +45,7 @@ impl Drv9pOps { info!("9p dev init success"); } Err(ecode) => { - error!("9p dev init fail! error code:{}", ecode); + error!("9p dev init fail! error code:{ecode}"); } } Self { @@ -90,7 +90,7 @@ impl Drv9pOps { pub fn recycle_fid(&mut self, id: u32) { let mut fid_gen = self.fid_gen.write(); if fid_gen.contains(&id) { - warn!("fid {} already exist", id); + warn!("fid {id} already exist"); } else { fid_gen.push(id); } @@ -757,7 +757,7 @@ impl _9PReq { } fn write_str(&mut self, value: &str) { - let str_size: u32 = value.as_bytes().len() as u32; + let str_size: u32 = value.len() as u32; self.write_u16(str_size as u16); for cbyte in value.bytes() { self.buffer.push(cbyte); @@ -780,7 +780,7 @@ impl _9PReq { } } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub struct _9PQid { ftype: u8, version: u32, @@ -795,6 +795,9 @@ impl _9PQid { path: lbytes2u64(&bytes[5..13]), } } + pub fn path(&self) -> u64 { + self.path + } } pub struct PosLock { @@ -904,10 +907,19 @@ impl FileAttr { } } + pub fn get_qid(&self) -> _9PQid { + self.qid + } + pub fn get_perm(&self) -> u32 { self.mode } + pub fn set_perm(&mut self, perm: u32) { + self.vaild |= _9P_SETATTR_MODE; + self.mode = perm; + } + pub fn set_size(&mut self, size: u64) { self.vaild |= _9P_SETATTR_SIZE; self.size = size; @@ -1027,6 +1039,10 @@ impl UStatFs { self.mode } + pub fn set_mode(&mut self, perm: u32) { + self.mode = perm; + } + pub fn get_ftype(&self) -> u8 { match self.qid.ftype { 0x00 => 0o10, diff --git a/modules/rux9p/src/fs.rs b/modules/rux9p/src/fs.rs index 9ca29f73b..e14bb13b4 100644 --- a/modules/rux9p/src/fs.rs +++ b/modules/rux9p/src/fs.rs @@ -13,8 +13,8 @@ use crate::drv::{self, Drv9pOps}; use alloc::{string::String, string::ToString, sync::Arc, sync::Weak, vec::Vec}; use axfs_vfs::{ - VfsDirEntry, VfsError, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, VfsOps, - VfsResult, + RelPath, VfsDirEntry, VfsError, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, + VfsOps, VfsResult, }; use log::*; use spin::{once::Once, RwLock}; @@ -46,13 +46,13 @@ impl _9pFileSystem { let mut protocol = protocol.to_string(); match dev.write().tversion(&protocol) { Ok(protocol_server) => { - info!("9pfs server's protocol: {}", protocol_server); + info!("9pfs server's protocol: {protocol_server}"); if protocol != protocol_server { protocol = protocol_server; } } Err(errcode) => { - error!("9pfs tversion failed! error code: {}", errcode); + error!("9pfs tversion failed! error code: {errcode}"); } } @@ -86,12 +86,8 @@ impl _9pFileSystem { } impl VfsOps for _9pFileSystem { - fn mount(&self, _path: &str, mount_point: VfsNodeRef) -> VfsResult { - if let Some(parent) = mount_point.parent() { - self.root.set_parent(Some(self.parent.call_once(|| parent))); - } else { - self.root.set_parent(None); - } + fn mount(&self, parent: VfsNodeRef) -> VfsResult { + self.root.set_parent(Some(self.parent.call_once(|| parent))); Ok(()) } @@ -144,7 +140,7 @@ impl CommonNode { Err(ELOOP) if *protocol == "9P2000.L" => { let try_readlink = dev.write().treadlink(fid); if let Ok(path) = try_readlink { - debug!("read link path ==> {:}", path); + debug!("read link path ==> {path:}"); let mut splited: Vec<&str> = path .split('/') .filter(|&x| !x.is_empty() && (x != ".")) @@ -153,13 +149,13 @@ impl CommonNode { let try_walk = dev.write().twalk(fid, fid, splited.len() as u16, &splited); match try_walk { Ok(_) => return Self::new(fid, parent, dev, protocol), - Err(ecode) => error!("9pfs twalk failed! error code: {}", ecode), + Err(ecode) => error!("9pfs twalk failed! error code: {ecode}"), } } else { - error!("9pfs treadlink failed! error code: {:?}", try_readlink); + error!("9pfs treadlink failed! error code: {try_readlink:?}"); } } - Err(ecode) => error!("9pfs topen failed! error code: {}", ecode), + Err(ecode) => error!("9pfs topen failed! error code: {ecode}"), _ => {} }; @@ -177,14 +173,14 @@ impl CommonNode { } /// Checks whether a node with the given name exists in this directory. - fn exist(&self, path: &str) -> bool { + fn exist(&self, path: &RelPath) -> bool { self.try_get(path).is_ok() } - /// Creates a new node with the given name and type in this directory. - fn create_node(&self, name: &str, ty: VfsNodeType) -> VfsResult { + /// Creates a new node with the given name, type and mode in this directory. + fn create_node(&self, name: &RelPath, ty: VfsNodeType, mode: VfsNodePerm) -> VfsResult { if self.exist(name) { - error!("AlreadyExists {}", name); + error!("AlreadyExists {name}"); return Err(VfsError::AlreadyExists); } let fid = match self.inner.write().get_fid() { @@ -201,12 +197,24 @@ impl CommonNode { ); if *self.protocol == "9P2000.L" { handle_result!( - self.inner.write().l_tcreate(fid, name, 0x02, 0o100644, 500), + self.inner.write().l_tcreate( + fid, + name, + 0x02, + ((0o100000 + mode.bits()) & 0o777) as _, + 500 + ), "9pfs l_create failed! error code: {}" ); } else if *self.protocol == "9P2000.u" { handle_result!( - self.inner.write().u_tcreate(fid, name, 0o777, 0o02, ""), + self.inner.write().u_tcreate( + fid, + name, + (mode.bits() & 0o777) as _, + 0o02, + "" + ), "9pfs create failed! error code: {}" ); } else { @@ -219,7 +227,7 @@ impl CommonNode { "9pfs mkdir failed! error code: {}" ); handle_result!( - self.inner.write().twalk(*self.fid, fid, 1, &[&name]), + self.inner.write().twalk(*self.fid, fid, 1, &[name]), "9pfs twalk failed! error code: {}" ); } @@ -235,18 +243,18 @@ impl CommonNode { Ok(()) } - fn try_get(&self, path: &str) -> VfsResult { + fn try_get(&self, path: &RelPath) -> VfsResult { let (name, rest) = split_path(path); if name == ".." { match self.parent() { - Some(parent) => return parent.lookup(rest.unwrap_or("")), + Some(parent) => return parent.lookup(&rest.unwrap_or(RelPath::new(""))), None => { error!("9pfs: try_get a directory out of 9pfs boundary"); return Err(VfsError::BadState); } } } else if name == "." { - return self.try_get(rest.unwrap_or("")); + return self.try_get(&rest.unwrap_or(RelPath::new(""))); } let fid = match self.inner.write().get_fid() { @@ -273,19 +281,19 @@ impl CommonNode { self.protocol.clone(), ); match rest { - Some(rpath) => node.try_get(rpath), + Some(rpath) => node.try_get(&rpath), None => Ok(node), } } // No such file or directory Err(ENOENT) => { self.inner.write().recycle_fid(fid); - debug!("try_get failed {:?}=={}+{:?}", path, name, rest); + debug!("try_get failed {path:?}=={name}+{rest:?}"); Err(VfsError::NotFound) } Err(ecode) => { self.inner.write().recycle_fid(fid); - error!("Failed when getting node in 9pfs, ecode:{}", ecode); + error!("Failed when getting node in 9pfs, ecode:{ecode}"); Err(VfsError::BadState) } } @@ -344,22 +352,21 @@ impl Drop for CommonNode { impl VfsNodeOps for CommonNode { /// Renames or moves existing file or directory. - fn rename(&self, src_path: &str, dst_path: &str) -> VfsResult { + fn rename(&self, src_path: &RelPath, dst_path: &RelPath) -> VfsResult { let (src_prefixs, old_name) = if let Some(src_sindex) = src_path.rfind('/') { (&src_path[..src_sindex], &src_path[src_sindex + 1..]) } else { - ("", src_path) + ("", src_path.as_str()) }; let (dst_prefixs, new_name) = if let Some(dst_sindex) = dst_path.rfind('/') { (&dst_path[..dst_sindex], &dst_path[dst_sindex + 1..]) } else { - ("", dst_path) + ("", dst_path.as_str()) }; debug!( - "9pfs src_path:{} dst_path:{}, src_prefixs:{:?}, dst_prefixs:{:?}", - src_path, dst_path, src_prefixs, dst_prefixs + "9pfs src_path:{src_path} dst_path:{dst_path}, src_prefixs:{src_prefixs:?}, dst_prefixs:{dst_prefixs:?}" ); let src_result = self.get_in_9pfs(src_prefixs); @@ -377,7 +384,8 @@ impl VfsNodeOps for CommonNode { } else { //create a new file and write content from original file. let src_fnode = self.try_get(src_path)?; - let _ = self.create(dst_path, src_fnode.get_attr()?.file_type()); + let src_attr = src_fnode.get_attr()?; + let _ = self.create(dst_path, src_attr.file_type(), src_attr.perm()); let dst_fnode = self.try_get(dst_path)?; let mut buffer = [0_u8; 1024]; // a length for one turn to read and write @@ -390,50 +398,81 @@ impl VfsNodeOps for CommonNode { dst_fnode.write_at(offset, &buffer[..length])?; offset += length as u64; } - src_fnode.remove("")?; + src_fnode.unlink(&RelPath::new(""))?; } Ok(()) } fn get_attr(&self) -> VfsResult { - if *self.protocol == "9P2000.L" { - let resp = self.inner.write().tgetattr(*self.fid, 0x3fff_u64); - debug!("get_attr {:?}", resp); - match resp { - Ok(stat) if stat.get_ftype() == 0o4 => { - let mut attr = VfsNodeAttr::new_dir(stat.get_size(), stat.get_blk_num()); - let mode = stat.get_perm() as u16 & 0o777_u16; - attr.set_perm(VfsNodePerm::from_bits(mode).unwrap()); - Ok(attr) - } - Ok(stat) if stat.get_ftype() == 0o10 => { - let mut attr = VfsNodeAttr::new_file(stat.get_size(), stat.get_blk_num()); - let mode = stat.get_perm() as u16 & 0o777_u16; - attr.set_perm(VfsNodePerm::from_bits(mode).unwrap()); - Ok(attr) + match self.protocol.as_str() { + "9P2000.L" => { + let resp = self.inner.write().tgetattr(*self.fid, 0x3fff_u64); + debug!("get_attr {resp:?}"); + if let Ok(stat) = resp { + let ty = match stat.get_ftype() { + 0o4 => VfsNodeType::Dir, + 0o10 => VfsNodeType::File, + _ => return Err(VfsError::BadState), + }; + Ok(VfsNodeAttr::new( + stat.get_qid().path(), + VfsNodePerm::from_bits_truncate(stat.get_perm() as u16 & 0o777), + ty, + stat.get_size(), + stat.get_blk_num(), + )) + } else { + Err(VfsError::BadState) } - _ => Err(VfsError::BadState), } - } else if *self.protocol == "9P2000.u" { - let resp = self.inner.write().tstat(*self.fid); - match resp { - Ok(stat) if stat.get_ftype() == 0o4 => { - let mut attr = VfsNodeAttr::new_dir(stat.get_length(), stat.get_blk_num()); - let mode = stat.get_perm() as u16 & 0o777_u16; - attr.set_perm(VfsNodePerm::from_bits(mode).unwrap()); - Ok(attr) - } - Ok(stat) if stat.get_ftype() == 0o10 => { - let mut attr = VfsNodeAttr::new_file(stat.get_length(), stat.get_blk_num()); - let mode = stat.get_perm() as u16 & 0o777_u16; - attr.set_perm(VfsNodePerm::from_bits(mode).unwrap()); - Ok(attr) + "9P2000.u" => { + let resp = self.inner.write().tstat(*self.fid); + if let Ok(stat) = resp { + let ty = match stat.get_ftype() { + 0o4 => VfsNodeType::Dir, + 0o10 => VfsNodeType::File, + _ => return Err(VfsError::BadState), + }; + Ok(VfsNodeAttr::new( + stat.get_qid().path(), + VfsNodePerm::from_bits_truncate(stat.get_perm() as u16 & 0o777), + ty, + stat.get_length(), + stat.get_blk_num(), + )) + } else { + Err(VfsError::BadState) } - _ => Err(VfsError::BadState), } - } else { - Err(VfsError::Unsupported) + _ => Err(VfsError::Unsupported), + } + } + + fn set_mode(&self, mode: VfsNodePerm) -> VfsResult { + let perm = mode.bits() as u32 & 0o777; + debug!("9pfs set mode, mode:{mode:?}"); + let mut dev = self.inner.write(); + match self.protocol.as_str() { + "9P2000.L" => { + let mut attr = dev + .tgetattr(*self.fid, 0x3fff_u64) + .map_err(|_| VfsError::BadState)?; + attr.set_perm(perm); + dev.tsetattr(*self.fid, attr) + .map_err(|_| VfsError::BadState)?; + } + "9P2000.u" => { + let mut attr = dev.tstat(*self.fid).map_err(|_| VfsError::BadState)?; + attr.set_mode(perm); + dev.twstat(*self.fid, attr) + .map_err(|_| VfsError::BadState)?; + } + _ => { + error!("{} is not supported", self.protocol); + return Err(VfsError::Unsupported); + } } + Ok(()) } fn parent(&self) -> Option { @@ -441,25 +480,25 @@ impl VfsNodeOps for CommonNode { } /// for 9p filesystem's directory, lookup() will return node in 9p if path existing in both 9p and mounted_map. - fn lookup(self: Arc, path: &str) -> VfsResult { - debug!("lookup 9pfs: {}", path); + fn lookup(self: Arc, path: &RelPath) -> VfsResult { + debug!("lookup 9pfs: {path}"); self.try_get(path) } fn read_dir(&self, start_idx: usize, vfs_dirents: &mut [VfsDirEntry]) -> VfsResult { - debug!("9pfs reading dirents: start_idx = {:x?}", start_idx); + debug!("9pfs reading dirents: start_idx = {start_idx:x?}"); let dirents = match self.protocol.as_str() { "9P2000.L" => match self.inner.write().treaddir(*self.fid) { Ok(contents) => contents, Err(errcode) => { - error!("9pfs treaddir failed! error code: {}", errcode); + error!("9pfs treaddir failed! error code: {errcode}"); return Err(VfsError::BadState); } }, "9P2000.u" => match self.inner.write().u_treaddir(*self.fid) { Ok(contents) => contents, Err(errcode) => { - error!("9pfs u_treaddir failed! error code: {}", errcode); + error!("9pfs u_treaddir failed! error code: {errcode}"); return Err(VfsError::BadState); } }, @@ -491,42 +530,42 @@ impl VfsNodeOps for CommonNode { }; *ent = VfsDirEntry::new(entry.get_name(), file_type); } else { - debug!("9pfs read dirents finished: start_idx = {:x?}", start_idx); + debug!("9pfs read dirents finished: start_idx = {start_idx:x?}"); return Ok(i); } } } } - debug!("9pfs read dirents finished: start_idx = {:x?}", start_idx); + debug!("9pfs read dirents finished: start_idx = {start_idx:x?}"); Ok(vfs_dirents.len()) } - fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult { - debug!("create {:?} at 9pfs: {}", ty, path); + fn create(&self, path: &RelPath, ty: VfsNodeType, mode: VfsNodePerm) -> VfsResult { + debug!("create {ty:?} at 9pfs: {path}"); let (name, rest) = split_path(path); if let Some(rpath) = rest { - self.try_get(name)?.create(rpath, ty) + self.try_get(&RelPath::new(name))?.create(&rpath, ty, mode) } else { - self.create_node(name, ty) + self.create_node(&RelPath::new(name), ty, mode) } } - fn remove(&self, path: &str) -> VfsResult { - debug!("remove at 9pfs: {}", path); + fn unlink(&self, path: &RelPath) -> VfsResult { + debug!("unlink at 9pfs: {path}"); match split_path(path) { ("", None) | (".", None) => match self.inner.write().tremove(*self.fid) { Ok(_) => Ok(()), Err(_) => Err(VfsError::BadState), }, - _ => self.try_get(path)?.remove(""), + _ => self.try_get(path)?.unlink(&RelPath::new("")), } } // Operation only for file usually /// Truncate the file to the given size. fn truncate(&self, size: u64) -> VfsResult { - debug!("9pfs truncating, size:{}", size); + debug!("9pfs truncating, size:{size}"); if *self.protocol == "9P2000.L" { let mut attr = drv::FileAttr::new(); attr.set_size(size); @@ -608,9 +647,8 @@ impl VfsNodeOps for CommonNode { } } -fn split_path(path: &str) -> (&str, Option<&str>) { - let trimmed_path = path.trim_start_matches('/'); - trimmed_path.find('/').map_or((trimmed_path, None), |n| { - (&trimmed_path[..n], Some(&trimmed_path[n + 1..])) +fn split_path<'a>(path: &'a RelPath) -> (&'a str, Option>) { + path.find('/').map_or((path, None), |n| { + (&path[..n], Some(RelPath::new(&path[n + 1..]))) }) } diff --git a/modules/rux9p/src/lib.rs b/modules/rux9p/src/lib.rs index 46d0d200f..c443c600c 100644 --- a/modules/rux9p/src/lib.rs +++ b/modules/rux9p/src/lib.rs @@ -15,7 +15,6 @@ #![cfg_attr(all(not(test), not(doc)), no_std)] #![feature(doc_auto_cfg)] -#![feature(ip_in_core)] #![cfg(any(feature = "virtio-9p", feature = "net-9p"))] #[doc(no_inline)] @@ -27,9 +26,9 @@ mod fs; #[cfg(feature = "net-9p")] mod netdev; -use alloc::sync::Arc; +use alloc::{string::String, sync::Arc}; use log::*; -use ruxfs::MountPoint; +use ruxfs::root::MountPoint; use spin::RwLock; #[cfg(feature = "virtio-9p")] @@ -56,7 +55,7 @@ pub fn init_virtio_9pfs( let v9p_driver = self::drv::Drv9pOps::new(v9p); let v9p_fs = self::fs::_9pFileSystem::new(Arc::new(RwLock::new(v9p_driver)), aname, protocol); - MountPoint::new("/v9fs", Arc::new(v9p_fs)) + MountPoint::new(String::from("/v9fs"), Arc::new(v9p_fs)) } #[cfg(feature = "net-9p")] @@ -75,7 +74,7 @@ pub fn init_net_9pfs(ip_port: &str, aname: &str, protocol: &str) -> MountPoint { let net9p_driver = self::drv::Drv9pOps::new(Box::new(net9p)); let n9p_fs = self::fs::_9pFileSystem::new(Arc::new(RwLock::new(net9p_driver)), aname, protocol); - MountPoint::new("/n9fs", Arc::new(n9p_fs)) + MountPoint::new(String::from("/n9fs"), Arc::new(n9p_fs)) } #[cfg(feature = "net-9p")] diff --git a/modules/rux9p/src/netdev.rs b/modules/rux9p/src/netdev.rs index 730466a75..e01508bf8 100644 --- a/modules/rux9p/src/netdev.rs +++ b/modules/rux9p/src/netdev.rs @@ -11,7 +11,7 @@ use core::net::{IpAddr, Ipv4Addr, SocketAddr}; use driver_9p::_9pDriverOps; use driver_common::{BaseDriverOps, DeviceType}; use log::*; -use ruxnet::TcpSocket; +use ruxnet::{message::MessageFlags, TcpSocket}; pub struct Net9pDev { socket: Mutex, @@ -23,12 +23,12 @@ impl Net9pDev { let ip_addr = match ip.len() { 4 => IpAddr::V4(Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3])), _ => { - error!("Unsupport IP address: {:?}, using 0.0.0.0 instead", ip); + error!("Unsupport IP address: {ip:?}, using 0.0.0.0 instead"); IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)) } }; Self { - socket: Mutex::new(TcpSocket::new()), + socket: Mutex::new(TcpSocket::new(false)), srv_addr: SocketAddr::new(ip_addr, port), } } @@ -64,16 +64,16 @@ impl _9pDriverOps for Net9pDev { fn send_with_recv(&mut self, inputs: &[u8], outputs: &mut [u8]) -> Result { match self.socket.lock().send(inputs) { Ok(length) => { - debug!("net9p send successfully,length = {}", length); + debug!("net9p send successfully,length = {length}"); } Err(_) => { error!("net9p send failed"); return Err(0); } } - match self.socket.lock().recv(outputs, 0) { + match self.socket.lock().recv(outputs, MessageFlags::empty()) { Ok(length) => { - debug!("net9p recv successfully,length = {}", length); + debug!("net9p recv successfully,length = {length}"); Ok(length as u32) } Err(_) => { diff --git a/modules/ruxconfig/build.rs b/modules/ruxconfig/build.rs index 2ce36af0f..2e223dc2a 100644 --- a/modules/ruxconfig/build.rs +++ b/modules/ruxconfig/build.rs @@ -166,7 +166,7 @@ fn main() -> Result<()> { let platform = option_env!("RUX_PLATFORM"); let config_path = resolve_config_path(platform)?; - println!("Reading config file: {:?}", config_path); + println!("Reading config file: {config_path:?}"); let config_rs = gen_config_rs(&config_path)?; let out_dir = std::env::var("OUT_DIR").unwrap(); diff --git a/modules/ruxconfig/defconfig.toml b/modules/ruxconfig/defconfig.toml index bda9cc8df..80f737cf8 100644 --- a/modules/ruxconfig/defconfig.toml +++ b/modules/ruxconfig/defconfig.toml @@ -32,7 +32,7 @@ timer-frequency = "0" # Stack size of each task. # task-stack-size = "0x40000" # 256 K -task-stack-size = "0x80000" # 512 K +task-stack-size = "0x80000" # 512 K # Number of timer ticks per second (Hz). A timer tick may contain several timer # interrupts. @@ -43,3 +43,12 @@ smp = "1" # Maximum number of keys per thread. pthread-key-max = "1024" + +# Pipe channel bufer size. +pipe-buffer-size = "0x10000" + +# Device tree blob address, x86 has no dtb +DTB_ADDR = "0" + +# Default buffer size used for pty input/output +pty-buffer-capacity = "4096" diff --git a/modules/ruxdriver/Cargo.toml b/modules/ruxdriver/Cargo.toml index b90a247c6..f20fad8b6 100644 --- a/modules/ruxdriver/Cargo.toml +++ b/modules/ruxdriver/Cargo.toml @@ -7,7 +7,7 @@ authors = [ "ChengXiang Qi ", "Zheng Wu ", ] -description = "Ruxos device drivers" +description = "RuxOS device drivers" license = "GPL-3.0-or-later OR Apache-2.0" homepage = "https://github.com/syswonder/ruxos" repository = "https://github.com/syswonder/ruxos/tree/main/modules/ruxdriver" @@ -15,23 +15,25 @@ repository = "https://github.com/syswonder/ruxos/tree/main/modules/ruxdriver" [features] dyn = [] bus-mmio = [] -bus-pci = ["dep:driver_pci", "dep:ruxhal", "dep:ruxconfig"] +bus-pci = ["dep:driver_pci", "dep:ruxhal", "dep:ruxconfig", "virtio"] net = ["driver_net"] block = ["driver_block"] display = ["driver_display"] _9p = ["driver_9p"] +pci = [] # Enabled by features `virtio-*` -virtio = ["driver_virtio", "dep:axalloc", "dep:ruxhal", "dep:ruxconfig"] - +virtio = ["driver_virtio", "dep:axalloc", "dep:ruxhal", "dep:ruxconfig", "ruxhal/virtio"] # various types of drivers -virtio-blk = ["block", "virtio", "driver_virtio/block"] -virtio-net = ["net", "virtio", "driver_virtio/net"] -virtio-gpu = ["display", "virtio", "driver_virtio/gpu"] -virtio-9p = ["_9p","virtio", "driver_virtio/v9p"] +virtio-blk = ["block", "virtio", "driver_virtio/block","virtio"] +virtio-net = ["net", "virtio", "driver_virtio/net", "virtio"] +virtio-gpu = ["display", "virtio", "driver_virtio/gpu", "virtio"] +virtio-9p = ["_9p","virtio", "driver_virtio/v9p", "virtio"] +virtio_console = ["virtio"] ramdisk = ["block", "driver_block/ramdisk"] bcm2835-sdhci = ["block", "driver_block/bcm2835-sdhci"] ixgbe = ["net", "driver_net/ixgbe", "dep:axalloc", "dep:ruxhal"] +loopback = ["driver_net/loopback", "dyn"] # more devices example: e1000 = ["net", "driver_net/e1000"] default = ["bus-mmio"] @@ -39,6 +41,7 @@ default = ["bus-mmio"] [dependencies] log = "0.4" cfg-if = "1.0" +crate_interface = "0.1.1" driver_common = { path = "../../crates/driver_common" } driver_block = { path = "../../crates/driver_block", optional = true } driver_net = { path = "../../crates/driver_net", optional = true } @@ -46,6 +49,7 @@ driver_display = { path = "../../crates/driver_display", optional = true } driver_9p = { path = "../../crates/driver_9p", optional = true } driver_pci = { path = "../../crates/driver_pci", optional = true } driver_virtio = { path = "../../crates/driver_virtio", optional = true } + axalloc = { path = "../axalloc", optional = true } ruxhal = { path = "../ruxhal", optional = true } ruxconfig = { path = "../ruxconfig", optional = true } diff --git a/modules/ruxdriver/build.rs b/modules/ruxdriver/build.rs index 6e1c859a8..7446613e4 100644 --- a/modules/ruxdriver/build.rs +++ b/modules/ruxdriver/build.rs @@ -7,7 +7,7 @@ * See the Mulan PSL v2 for more details. */ -const NET_DEV_FEATURES: &[&str] = &["ixgbe", "virtio-net"]; +const NET_DEV_FEATURES: &[&str] = &["ixgbe", "virtio-net", "loopback"]; const BLOCK_DEV_FEATURES: &[&str] = &["ramdisk", "bcm2835-sdhci", "virtio-blk"]; const DISPLAY_DEV_FEATURES: &[&str] = &["virtio-gpu"]; const _9P_DEV_FEATURES: &[&str] = &["virtio-9p"]; @@ -64,4 +64,33 @@ fn main() { enable_cfg(&format!("{dev_kind}_dev"), "dummy"); } } + + println!( + "cargo::rustc-check-cfg=cfg(bus, values({}))", + make_cfg_values(&["pci", "mmio"]) + ); + println!( + "cargo::rustc-check-cfg=cfg(net_dev, values({}, \"dummy\"))", + make_cfg_values(NET_DEV_FEATURES) + ); + println!( + "cargo::rustc-check-cfg=cfg(block_dev, values({}, \"dummy\"))", + make_cfg_values(BLOCK_DEV_FEATURES) + ); + println!( + "cargo::rustc-check-cfg=cfg(display_dev, values({}, \"dummy\"))", + make_cfg_values(DISPLAY_DEV_FEATURES) + ); + println!( + "cargo::rustc-check-cfg=cfg(_9p_dev, values({}, \"dummy\"))", + make_cfg_values(_9P_DEV_FEATURES) + ); +} + +fn make_cfg_values(str_list: &[&str]) -> String { + str_list + .iter() + .map(|s| format!("{s:?}")) + .collect::>() + .join(", ") } diff --git a/modules/ruxdriver/src/bus/mmio.rs b/modules/ruxdriver/src/bus/mmio.rs index 498a4d487..58c306a04 100644 --- a/modules/ruxdriver/src/bus/mmio.rs +++ b/modules/ruxdriver/src/bus/mmio.rs @@ -9,12 +9,19 @@ #[allow(unused_imports)] use crate::{prelude::*, AllDevices}; +#[cfg(all(feature = "virtio_console", feature = "virtio"))] +use ruxhal::virtio::virtio_console; impl AllDevices { pub(crate) fn probe_bus_devices(&mut self) { // TODO: parse device tree #[cfg(feature = "virtio")] for reg in ruxconfig::VIRTIO_MMIO_REGIONS { + #[cfg(feature = "virtio_console")] + if virtio_console::is_probe(reg.0) { + warn!("Avoiding virtio-console probe again"); + continue; + } for_each_drivers!(type Driver, { if let Some(dev) = Driver::probe_mmio(reg.0, reg.1) { info!( diff --git a/modules/ruxdriver/src/bus/pci.rs b/modules/ruxdriver/src/bus/pci.rs index 5007166b4..532f2d1c4 100644 --- a/modules/ruxdriver/src/bus/pci.rs +++ b/modules/ruxdriver/src/bus/pci.rs @@ -9,26 +9,27 @@ use crate::{prelude::*, AllDevices}; use driver_pci::{ - BarInfo, Cam, Command, DeviceFunction, HeaderType, MemoryBarType, PciRangeAllocator, PciRoot, + BarInfo, Cam, Command, ConfigurationAccess, DeviceFunction, HeaderType, MemoryBarType, MmioCam, + PciRangeAllocator, PciRoot, }; use ruxhal::mem::phys_to_virt; const PCI_BAR_NUM: u8 = 6; fn config_pci_device( - root: &mut PciRoot, + root: &mut PciRoot, bdf: DeviceFunction, allocator: &mut Option, ) -> DevResult { let mut bar = 0; while bar < PCI_BAR_NUM { let info = root.bar_info(bdf, bar).unwrap(); - if let BarInfo::Memory { + if let core::prelude::v1::Some(BarInfo::Memory { address_type, address, size, .. - } = info + }) = info { // if the BAR address is not assigned, call the allocator and assign it. if size > 0 && address == 0 { @@ -46,25 +47,26 @@ fn config_pci_device( } // read the BAR info again after assignment. - let info = root.bar_info(bdf, bar).unwrap(); + let info_ = root.bar_info(bdf, bar); + let info = info_.expect("Failed to read BAR info"); match info { - BarInfo::IO { address, size } => { + core::prelude::v1::Some(BarInfo::IO { address, size }) => { if address > 0 && size > 0 { debug!(" BAR {}: IO [{:#x}, {:#x})", bar, address, address + size); } } - BarInfo::Memory { + core::prelude::v1::Some(BarInfo::Memory { address_type, prefetchable, address, size, - } => { + }) => { if address > 0 && size > 0 { debug!( " BAR {}: MEM [{:#x}, {:#x}){}{}", bar, address, - address + size as u64, + address + size, if address_type == MemoryBarType::Width64 { " 64bit" } else { @@ -74,10 +76,11 @@ fn config_pci_device( ); } } + None => todo!(), } bar += 1; - if info.takes_two_entries() { + if info.expect("REASON").takes_two_entries() { bar += 1; } } @@ -94,7 +97,7 @@ fn config_pci_device( impl AllDevices { pub(crate) fn probe_bus_devices(&mut self) { let base_vaddr = phys_to_virt(ruxconfig::PCI_ECAM_BASE.into()); - let mut root = unsafe { PciRoot::new(base_vaddr.as_mut_ptr(), Cam::Ecam) }; + let mut root = PciRoot::new(unsafe { MmioCam::new(base_vaddr.as_mut_ptr(), Cam::Ecam) }); // PCI 32-bit MMIO space let mut allocator = ruxconfig::PCI_RANGES @@ -103,7 +106,7 @@ impl AllDevices { for bus in 0..=ruxconfig::PCI_BUS_END as u8 { for (bdf, dev_info) in root.enumerate_bus(bus) { - debug!("PCI {}: {}", bdf, dev_info); + debug!("PCI {bdf}: {dev_info}"); if dev_info.header_type != HeaderType::Standard { continue; } @@ -120,10 +123,7 @@ impl AllDevices { continue; // skip to the next device } }), - Err(e) => warn!( - "failed to enable PCI device at {}({}): {:?}", - bdf, dev_info, e - ), + Err(e) => warn!("failed to enable PCI device at {bdf}({dev_info}): {e:?}"), } } } diff --git a/modules/ruxdriver/src/drivers.rs b/modules/ruxdriver/src/drivers.rs index 7fe1115bc..b0c260db6 100644 --- a/modules/ruxdriver/src/drivers.rs +++ b/modules/ruxdriver/src/drivers.rs @@ -9,7 +9,7 @@ //! Defines types and probe methods of all supported devices. -#![allow(unused_imports)] +#![allow(unused_imports, dead_code)] use crate::AxDeviceEnum; use driver_common::DeviceType; @@ -18,7 +18,7 @@ use driver_common::DeviceType; use crate::virtio::{self, VirtIoDevMeta}; #[cfg(feature = "bus-pci")] -use driver_pci::{DeviceFunction, DeviceFunctionInfo, PciRoot}; +use driver_pci::{ConfigurationAccess, DeviceFunction, DeviceFunctionInfo, MmioCam, PciRoot}; pub use super::dummy::*; @@ -34,7 +34,7 @@ pub trait DriverProbe { #[cfg(bus = "pci")] fn probe_pci( - _root: &mut PciRoot, + _root: &mut PciRoot, _bdf: DeviceFunction, _dev_info: &DeviceFunctionInfo, ) -> Option { @@ -42,6 +42,23 @@ pub trait DriverProbe { } } +cfg_if::cfg_if! { + if #[cfg(net_dev = "loopback")] + { + pub struct LoopbackDriver; + register_net_driver!(LoopbackDriver, driver_net::loopback::LoopbackDevice); + + impl DriverProbe for LoopbackDriver { + fn probe_global() -> Option { + debug!("mmc probe"); + Some(AxDeviceEnum::from_net( + driver_net::loopback::LoopbackDevice::new(None), + )) + } + } + } +} + #[cfg(net_dev = "virtio-net")] register_net_driver!( ::Driver, @@ -51,19 +68,19 @@ register_net_driver!( #[cfg(block_dev = "virtio-blk")] register_block_driver!( ::Driver, - ::Device + ::Device<'static> ); #[cfg(display_dev = "virtio-gpu")] register_display_driver!( ::Driver, - ::Device + ::Device<'static> ); #[cfg(_9p_dev = "virtio-9p")] register_9p_driver!( ::Driver, - ::Device + ::Device<'static> ); cfg_if::cfg_if! { @@ -104,7 +121,7 @@ cfg_if::cfg_if! { register_net_driver!(IxgbeDriver, driver_net::ixgbe::IxgbeNic); impl DriverProbe for IxgbeDriver { fn probe_pci( - root: &mut driver_pci::PciRoot, + root: &mut driver_pci::PciRoot, bdf: driver_pci::DeviceFunction, dev_info: &driver_pci::DeviceFunctionInfo, ) -> Option { @@ -112,14 +129,14 @@ cfg_if::cfg_if! { use driver_net::ixgbe::{INTEL_82599, INTEL_VEND, IxgbeNic}; if dev_info.vendor_id == INTEL_VEND && dev_info.device_id == INTEL_82599 { // Intel 10Gb Network - info!("ixgbe PCI device found at {:?}", bdf); + info!("ixgbe PCI device found at {bdf:?}"); // Initialize the device // These can be changed according to the requirments specified in the ixgbe init function. const QN: u16 = 1; const QS: usize = 1024; let bar_info = root.bar_info(bdf, 0).unwrap(); - match bar_info { + match bar_info.unwrap() { driver_pci::BarInfo::Memory { address, size, diff --git a/modules/ruxdriver/src/lib.rs b/modules/ruxdriver/src/lib.rs index ad3d32ce8..3d213a293 100644 --- a/modules/ruxdriver/src/lib.rs +++ b/modules/ruxdriver/src/lib.rs @@ -25,15 +25,15 @@ //! This crate supports two device models depending on the `dyn` feature: //! //! - **Static**: The type of all devices is static, it is determined at compile -//! time by corresponding cargo features. For example, [`AxNetDevice`] will be -//! an alias of [`VirtioNetDev`] if the `virtio-net` feature is enabled. This -//! model provides the best performance as it avoids dynamic dispatch. But on -//! limitation, only one device instance is supported for each device category. +//! maktime by corresponding cargo features. For example, [`AxNetDevice`] will be +//! an alias of [`VirtioNetDev`] if the `virtio-net` feature is enabled. This +//! model provides the best performance as it avoids dynamic dispatch. But on +//! limitation, only one device instance is supported for each device category. //! - **Dynamic**: All device instance is using [trait objects] and wrapped in a -//! `Box`. For example, [`AxNetDevice`] will be [`Box`]. -//! When call a method provided by the device, it uses [dynamic dispatch][dyn] -//! that may introduce a little overhead. But on the other hand, it is more -//! flexible, multiple instances of each device category are supported. +//! `Box`. For example, [`AxNetDevice`] will be [`Box`]. +//! When call a method provided by the device, it uses [dynamic dispatch][dyn] +//! that may introduce a little overhead. But on the other hand, it is more +//! flexible, multiple instances of each device category are supported. //! //! # Supported Devices //! @@ -49,13 +49,13 @@ //! //! - `dyn`: use the dynamic device model (see above). //! - `bus-mmio`: use device tree to probe all MMIO devices. This feature is -//! enabeld by default. +//! enabeld by default. //! - `bus-pci`: use PCI bus to probe all PCI devices. //! - `virtio`: use VirtIO devices. This is enabled if any of `virtio-blk`, //! `virtio-net` or `virtio-gpu` is enabled. //! - `net`: use network devices. This is enabled if any feature of network -//! devices is selected. If this feature is enabled without any network device -//! features, a dummy struct is used for [`AxNetDevice`]. +//! devices is selected. If this feature is enabled without any network device +//! features, a dummy struct is used for [`AxNetDevice`]. //! - `block`: use block storage devices. Similar to the `net` feature. //! - `display`: use graphics display devices. Similar to the `net` feature. //! - `_9p`: use 9pfs devices. Similar to the `net` feature. @@ -82,7 +82,7 @@ mod dummy; mod structs; #[cfg(feature = "virtio")] -mod virtio; +pub mod virtio; #[cfg(feature = "ixgbe")] mod ixgbe; diff --git a/modules/ruxdriver/src/macros.rs b/modules/ruxdriver/src/macros.rs index e9aa44192..cfd406d9e 100644 --- a/modules/ruxdriver/src/macros.rs +++ b/modules/ruxdriver/src/macros.rs @@ -51,6 +51,12 @@ macro_rules! for_each_drivers { #[allow(unused_imports)] use crate::virtio::{self, VirtIoDevMeta}; + #[cfg(net_dev = "loopback")] + { + type $drv_type = crate::drivers::LoopbackDriver; + $code + } + #[cfg(net_dev = "virtio-net")] { type $drv_type = ::Driver; diff --git a/modules/ruxdriver/src/virtio.rs b/modules/ruxdriver/src/virtio.rs index ec97412b5..8b91da47a 100644 --- a/modules/ruxdriver/src/virtio.rs +++ b/modules/ruxdriver/src/virtio.rs @@ -7,45 +7,55 @@ * See the Mulan PSL v2 for more details. */ -use core::marker::PhantomData; -use core::ptr::NonNull; +//! A driver for VirtIO devices. -use axalloc::global_allocator; +use crate::{drivers::DriverProbe, AxDeviceEnum}; use cfg_if::cfg_if; +use core::marker::PhantomData; use driver_common::{BaseDriverOps, DevResult, DeviceType}; -use driver_virtio::{BufferDirection, PhysAddr, VirtIoHal}; -use ruxhal::mem::{direct_virt_to_phys, phys_to_virt, virt_to_phys}; - -use crate::{drivers::DriverProbe, AxDeviceEnum}; +#[cfg(bus = "mmio")] +use ruxhal::mem::phys_to_virt; +#[cfg(any( + feature = "virtio-net", + feature = "virtio-blk", + feature = "virtio-gpu", + feature = "virtio-9p", + feature = "pci" +))] +use ruxhal::virtio::virtio_hal::VirtIoHalImpl; cfg_if! { if #[cfg(bus = "pci")] { - use driver_pci::{PciRoot, DeviceFunction, DeviceFunctionInfo}; - type VirtIoTransport = driver_virtio::PciTransport; + use driver_pci::{PciRoot, DeviceFunction, DeviceFunctionInfo, MmioCam}; + type VirtIoTransport<'a> = driver_virtio::PciTransport; } else if #[cfg(bus = "mmio")] { - type VirtIoTransport = driver_virtio::MmioTransport; + type VirtIoTransport<'a> = driver_virtio::MmioTransport<'a>; } } /// A trait for VirtIO device meta information. pub trait VirtIoDevMeta { + /// The device type of the VirtIO device. const DEVICE_TYPE: DeviceType; - - type Device: BaseDriverOps; + /// The device type of the VirtIO device. + type Device<'a>: BaseDriverOps + 'static; + /// The driver for the VirtIO device. type Driver = VirtIoDriver; - fn try_new(transport: VirtIoTransport) -> DevResult; + /// Try to create a new instance of the VirtIO device.Z + fn try_new(transport: VirtIoTransport<'static>) -> DevResult; } cfg_if! { if #[cfg(net_dev = "virtio-net")] { + /// A VirtIO network device. pub struct VirtIoNet; impl VirtIoDevMeta for VirtIoNet { const DEVICE_TYPE: DeviceType = DeviceType::Net; - type Device = driver_virtio::VirtIoNetDev; + type Device<'a> = driver_virtio::VirtIoNetDev, 64>; - fn try_new(transport: VirtIoTransport) -> DevResult { + fn try_new(transport: VirtIoTransport<'static>) -> DevResult { Ok(AxDeviceEnum::from_net(Self::Device::try_new(transport)?)) } } @@ -54,13 +64,14 @@ cfg_if! { cfg_if! { if #[cfg(block_dev = "virtio-blk")] { + /// A VirtIO block device. pub struct VirtIoBlk; impl VirtIoDevMeta for VirtIoBlk { const DEVICE_TYPE: DeviceType = DeviceType::Block; - type Device = driver_virtio::VirtIoBlkDev; + type Device<'a> = driver_virtio::VirtIoBlkDev>; - fn try_new(transport: VirtIoTransport) -> DevResult { + fn try_new(transport: VirtIoTransport<'static>) -> DevResult { Ok(AxDeviceEnum::from_block(Self::Device::try_new(transport)?)) } } @@ -69,13 +80,14 @@ cfg_if! { cfg_if! { if #[cfg(display_dev = "virtio-gpu")] { + /// A VirtIO GPU device. pub struct VirtIoGpu; impl VirtIoDevMeta for VirtIoGpu { const DEVICE_TYPE: DeviceType = DeviceType::Display; - type Device = driver_virtio::VirtIoGpuDev; + type Device<'a> = driver_virtio::VirtIoGpuDev>; - fn try_new(transport: VirtIoTransport) -> DevResult { + fn try_new(transport: VirtIoTransport<'static>) -> DevResult { Ok(AxDeviceEnum::from_display(Self::Device::try_new(transport)?)) } } @@ -84,20 +96,21 @@ cfg_if! { cfg_if! { if #[cfg(_9p_dev = "virtio-9p")] { + /// A VirtIO 9P device. pub struct VirtIo9p; impl VirtIoDevMeta for VirtIo9p { const DEVICE_TYPE: DeviceType = DeviceType::_9P; - type Device = driver_virtio::VirtIo9pDev; + type Device<'a> = driver_virtio::VirtIo9pDev>; - fn try_new(transport: VirtIoTransport) -> DevResult { + fn try_new(transport: VirtIoTransport<'static>) -> DevResult { Ok(AxDeviceEnum::from_9p(Self::Device::try_new(transport)?)) } } } } -/// A common driver for all VirtIO devices that implements [`DriverProbe`]. +/// A common driver for all VirtIO devices that implements DriverProbe. pub struct VirtIoDriver(PhantomData); impl DriverProbe for VirtIoDriver { @@ -127,7 +140,7 @@ impl DriverProbe for VirtIoDriver { #[cfg(bus = "pci")] fn probe_pci( - root: &mut PciRoot, + root: &mut PciRoot, bdf: DeviceFunction, dev_info: &DeviceFunctionInfo, ) -> Option { @@ -143,16 +156,13 @@ impl DriverProbe for VirtIoDriver { } if let Some((ty, transport)) = - driver_virtio::probe_pci_device::(root, bdf, dev_info) + driver_virtio::probe_pci_device::(root, bdf, dev_info) { if ty == D::DEVICE_TYPE { match D::try_new(transport) { Ok(dev) => return Some(dev), Err(e) => { - warn!( - "failed to initialize PCI device at {}({}): {:?}", - bdf, dev_info, e - ); + warn!("failed to initialize PCI device at {bdf}({dev_info}): {e:?}"); return None; } } @@ -161,37 +171,3 @@ impl DriverProbe for VirtIoDriver { None } } - -pub struct VirtIoHalImpl; - -unsafe impl VirtIoHal for VirtIoHalImpl { - fn dma_alloc(pages: usize, _direction: BufferDirection) -> (PhysAddr, NonNull) { - let vaddr = if let Ok(vaddr) = global_allocator().alloc_pages(pages, 0x1000) { - vaddr - } else { - return (0, NonNull::dangling()); - }; - let paddr = direct_virt_to_phys(vaddr.into()); - let ptr = NonNull::new(vaddr as _).unwrap(); - (paddr.as_usize(), ptr) - } - - unsafe fn dma_dealloc(_paddr: PhysAddr, vaddr: NonNull, pages: usize) -> i32 { - global_allocator().dealloc_pages(vaddr.as_ptr() as usize, pages); - 0 - } - - #[inline] - unsafe fn mmio_phys_to_virt(paddr: PhysAddr, _size: usize) -> NonNull { - NonNull::new(phys_to_virt(paddr.into()).as_mut_ptr()).unwrap() - } - - #[inline] - unsafe fn share(buffer: NonNull<[u8]>, _direction: BufferDirection) -> PhysAddr { - let vaddr = buffer.as_ptr() as *mut u8 as usize; - virt_to_phys(vaddr.into()).into() - } - - #[inline] - unsafe fn unshare(_paddr: PhysAddr, _buffer: NonNull<[u8]>, _direction: BufferDirection) {} -} diff --git a/modules/ruxdtb/Cargo.toml b/modules/ruxdtb/Cargo.toml new file mode 100644 index 000000000..ce7d2d511 --- /dev/null +++ b/modules/ruxdtb/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ruxdtb" +version = "0.1.0" +edition = "2021" +authors = ["Juguang Yan "] +description = "RuxOS dtb parsing module" +license = "Mulan PSL v2" +homepage = "https://github.com/syswonder/ruxos" + +[features] +irq = [] + +[dependencies] +log = "0.4" +dtb = { path = "../../crates/dtb" } diff --git a/modules/ruxdtb/src/lib.rs b/modules/ruxdtb/src/lib.rs new file mode 100644 index 000000000..7c72de272 --- /dev/null +++ b/modules/ruxdtb/src/lib.rs @@ -0,0 +1,112 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! Parsing specific flat device tree field for AArch64. +#![no_std] + +#[macro_use] +#[cfg(target_arch = "aarch64")] +extern crate log; + +/// get memory base and size from dtb +/// +/// # Return +/// Return `(mem_base, mem_size)` +#[cfg(target_arch = "aarch64")] +pub fn get_memory_info() -> (usize, usize) { + let mem_node = dtb::get_node_by_prop_value("device_type", "memory").unwrap(); + + let regs = mem_node.find_prop("reg").unwrap(); + ( + dtb::get_propbuf_u64(®s, 0) as usize, + dtb::get_propbuf_u64(®s, 1) as usize, + ) +} + +/// get memory base and size from dtb +/// +/// # Return +/// Return `(mem_base, mem_size)` +#[cfg(not(target_arch = "aarch64"))] +pub fn get_memory_info() -> (usize, usize) { + (0, 0) +} + +/// Get pl011 base address from dtb +#[cfg(target_arch = "aarch64")] +pub fn get_pl011_base() -> usize { + let pl011_node = dtb::compatible_node("arm,pl011").unwrap(); + let regs = pl011_node.find_prop("reg").unwrap(); + dtb::get_propbuf_u64(®s, 0) as usize +} + +/// Get pl011 base address from dtb +#[cfg(not(target_arch = "aarch64"))] +pub fn get_pl011_base() -> usize { + 0 +} + +/// Get pl031 base address from dtb +#[cfg(target_arch = "aarch64")] +pub fn get_pl031_base() -> usize { + let pl031_node = dtb::compatible_node("arm,pl031").unwrap(); + let regs = pl031_node.find_prop("reg").unwrap(); + dtb::get_propbuf_u64(®s, 0) as usize +} + +/// Get pl031 base address from dtb +#[cfg(not(target_arch = "aarch64"))] +pub fn get_pl031_base() -> usize { + 0 +} + +/// Get GICV2 base address from dtb +#[cfg(all(target_arch = "aarch64", feature = "irq"))] +pub fn get_gicv3_base() -> usize { + let gicv3_node = dtb::compatible_node("arm,gic-v3").unwrap(); + let regs = gicv3_node.find_prop("reg").unwrap(); + // index 0 for GICD + dtb::get_propbuf_u64(®s, 0) as usize +} + +/// Get GICV2 base address from dtb +#[cfg(not(target_arch = "aarch64"))] +pub fn get_gicv3_base() -> usize { + 0 +} + +/// Get GICV2 base address from dtb +#[cfg(all(target_arch = "aarch64", feature = "irq"))] +pub fn get_gicv2_base() -> usize { + let gicv2_node = dtb::compatible_node("arm,cortex-a15-gic").unwrap(); + let regs = gicv2_node.find_prop("reg").unwrap(); + // index 0 for GICD + dtb::get_propbuf_u64(®s, 0) as usize +} + +/// Get GICV2 base address from dtb +#[cfg(not(target_arch = "aarch64"))] +pub fn get_gicv2_base() -> usize { + 0 +} + +/// Get psci version from dtb +#[cfg(target_arch = "aarch64")] +pub fn get_psci() { + let psci_node = match dtb::compatible_node("arm,psci-1.0") { + Some(node) => node, + None => dtb::compatible_node("arm,psci-0.2").unwrap(), + }; + let prop = psci_node.find_prop("method").unwrap().str(); + info!("prop: {prop:?}"); +} + +/// Get psci version from dtb +#[cfg(not(target_arch = "aarch64"))] +pub fn get_psci() {} diff --git a/modules/ruxfdtable/Cargo.toml b/modules/ruxfdtable/Cargo.toml index e918c39b1..92d727767 100644 --- a/modules/ruxfdtable/Cargo.toml +++ b/modules/ruxfdtable/Cargo.toml @@ -12,9 +12,13 @@ repository = "https://github.com/syswonder/ruxos/tree/main/crates/ruxfdtable" default = [] [dependencies] -log = "0.4" -spin = "0.9" +log = { workspace = true } +spin = { workspace = true } +bitflags = { workspace = true } axio = { path = "../../crates/axio" } +axfs_vfs = { path = "../../crates/axfs_vfs" } lazy_static = { version = "1.4", features = ["spin_no_std"] } flatten_objects = { path = "../../crates/flatten_objects" } axerrno = { path = "../../crates/axerrno" } +capability = { path = "../../crates/capability" } +bitmaps = { version = "3.2", default-features = false } diff --git a/modules/ruxfdtable/src/lib.rs b/modules/ruxfdtable/src/lib.rs index 275ef96f2..0bd144458 100644 --- a/modules/ruxfdtable/src/lib.rs +++ b/modules/ruxfdtable/src/lib.rs @@ -11,14 +11,164 @@ #![no_std] extern crate alloc; use alloc::sync::Arc; +use axerrno::LinuxError; +use axfs_vfs::VfsNodeAttr; +use bitflags::bitflags; +use bitmaps::Bitmap; +use capability::Cap; use core::marker::Send; use core::marker::Sync; +use flatten_objects::FlattenObjects; use axerrno::LinuxResult; +use axfs_vfs::AbsPath; use axio::PollState; -use flatten_objects::FlattenObjects; -use spin::RwLock; +/// Maximum number of files per process +pub const RUX_FILE_LIMIT: usize = 1024; + +/// A table of file descriptors, containing a collection of file objects and their associated flags(CLOEXEC). +pub struct FdTable { + /// A collection of file objects, indexed by their file descriptor numbers. + files: FlattenObjects, RUX_FILE_LIMIT>, + /// A bitmap for tracking `FD_CLOEXEC` flags for each file descriptor. + /// If a bit is set, the corresponding file descriptor has the `FD_CLOEXEC` flag enabled. + cloexec_bitmap: Bitmap, +} + +impl Clone for FdTable { + fn clone(&self) -> Self { + // get all file descriptors from the original file system to copy them to the new one + // TODO: make this more efficient by only copying the used file descriptors + let mut new_files = FlattenObjects::new(); + for fd in 0..self.files.capacity() { + if let Some(f) = self.files.get(fd) { + new_files.add_at(fd, f.clone()).unwrap(); + } + } + Self { + files: new_files, + cloexec_bitmap: self.cloexec_bitmap, + } + } +} + +impl Default for FdTable { + fn default() -> Self { + FdTable { + files: FlattenObjects::new(), + cloexec_bitmap: Bitmap::new(), + } + } +} + +impl FdTable { + /// Retrieves the file object associated with the given file descriptor (fd). + /// + /// Returns `Some` with the file object if the file descriptor exists, or `None` if not. + pub fn get(&self, fd: usize) -> Option<&Arc> { + self.files.get(fd) + } + + /// Adds a new file object to the table and associates it with a file descriptor. + /// + /// Also sets the `FD_CLOEXEC` flag for the file descriptor based on the `flags` argument. + /// Returns the assigned file descriptor number (`fd`) if successful, or `None` if the table is full. + pub fn add(&mut self, file: Arc, flags: OpenFlags) -> Option { + if let Some(fd) = self.files.add(file) { + debug_assert!(!self.cloexec_bitmap.get(fd)); + if flags.contains(OpenFlags::O_CLOEXEC) { + self.cloexec_bitmap.set(fd, true); + } + Some(fd) + } else { + None + } + } + + /// Retrieves the `FD_CLOEXEC` flag for the specified file descriptor. + /// + /// Returns `true` if the flag is set, otherwise `false`. + pub fn get_cloexec(&self, fd: usize) -> bool { + self.cloexec_bitmap.get(fd) + } + + /// Sets the `FD_CLOEXEC` flag for the specified file descriptor. + pub fn set_cloexec(&mut self, fd: usize, cloexec: bool) { + self.cloexec_bitmap.set(fd, cloexec); + } + + /// Removes a file descriptor from the table. + /// + /// This will clear the `FD_CLOEXEC` flag for the file descriptor and remove the file object. + pub fn remove(&mut self, fd: usize) -> Option> { + self.cloexec_bitmap.set(fd, false); + // use map_or because RAII. the Arc should be released here. You should not use the return Arc + self.files.remove(fd) + } + + /// Closes all file descriptors with the `FD_CLOEXEC` flag set. + /// + /// This will remove all file descriptors marked for close-on-exec from the table. + pub fn do_close_on_exec(&mut self) { + for fd in self.cloexec_bitmap.into_iter() { + self.files.remove(fd); + } + self.cloexec_bitmap = Bitmap::new() + } + + /// Duplicates a file descriptor and returns a new file descriptor. + /// + /// The two file descriptors do not share file descriptor flags (the close-on-exec flag). + /// The close-on-exec flag (FD_CLOEXEC; see fcntl(2)) for the duplicate descriptor is off. + pub fn dup(&mut self, fd: usize) -> LinuxResult { + let f = self.files.get(fd).ok_or(LinuxError::EBADF)?.clone(); + let new_fd = self.files.add(f).ok_or(LinuxError::EMFILE)?; + debug_assert!(!self.cloexec_bitmap.get(new_fd)); + Ok(new_fd) + } + + /// Duplicates a file descriptor to a specific file descriptor number, replacing it if necessary. + /// + /// If the file descriptor `newfd` was previously open, it is silently closed before being reused. + pub fn dup3(&mut self, old_fd: usize, new_fd: usize, cloexec: bool) -> LinuxResult { + let f = self.files.get(old_fd).ok_or(LinuxError::EBADF)?.clone(); + self.files.remove(new_fd); + self.files.add_at(new_fd, f); + self.cloexec_bitmap.set(new_fd, cloexec); + Ok(new_fd) + } + + /// Duplicate the file descriptor fd using the lowest-numbered available file descriptor greater than or equal to `bound`. + pub fn dup_with_low_bound( + &mut self, + fd: usize, + bound: usize, + cloexec: bool, + ) -> LinuxResult { + let f = self.files.get(fd).ok_or(LinuxError::EBADF)?.clone(); + let new_fd = self + .files + .add_with_low_bound(f, bound) + .ok_or(LinuxError::EMFILE)?; + debug_assert!(!self.cloexec_bitmap.get(new_fd)); + self.cloexec_bitmap.set(new_fd, cloexec); + Ok(new_fd) + } + + /// Closes all file objects in the file descriptor table. + pub fn close_all_files(&mut self) { + for fd in 0..self.files.capacity() { + if self.files.get(fd).is_some() { + self.files.remove(fd).unwrap(); + } + } + // this code might not be necessary + self.cloexec_bitmap = Bitmap::new(); + } +} + +#[derive(Default)] ///Rust version for struct timespec in ctypes. Represents a high-resolution time specification. pub struct RuxTimeSpec { /// Whole seconds part of the timespec. @@ -28,7 +178,8 @@ pub struct RuxTimeSpec { } ///Rust version for struct stat in ctypes. Represents file status information. -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +#[derive(Default)] pub struct RuxStat { /// Device identifier. pub st_dev: u64, @@ -49,7 +200,7 @@ pub struct RuxStat { /// Total size, in bytes. pub st_size: i64, /// Block size for filesystem I/O. - pub st_blksize: core::ffi::c_long, + pub st_blksize: core::ffi::c_int, /// Padding to maintain alignment. pub __pad2: core::ffi::c_int, /// Number of 512B blocks allocated. @@ -63,8 +214,10 @@ pub struct RuxStat { /// Unused space, reserved for future use. pub __unused: [core::ffi::c_uint; 2usize], } + ///Rust version for struct stat in ctypes. Represents file status information. -#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] +#[cfg(target_arch = "x86_64")] +#[derive(Default)] pub struct RuxStat { /// Device identifier. pub st_dev: u64, @@ -98,8 +251,76 @@ pub struct RuxStat { pub __unused: [core::ffi::c_long; 3usize], } +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +impl From for RuxStat { + fn from(attr: VfsNodeAttr) -> Self { + Self { + st_dev: 0, + st_ino: attr.ino(), + st_nlink: 1, + st_mode: ((attr.file_type() as u32) << 12) | attr.perm().bits() as u32, + st_uid: 1000, + st_gid: 1000, + st_rdev: 0, + __pad: 0, + st_size: attr.size() as _, + st_blksize: 512, + __pad2: 0, + st_blocks: attr.blocks() as _, + st_atime: RuxTimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + st_mtime: RuxTimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + st_ctime: RuxTimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + __unused: [0; 2], + } + } +} + +#[cfg(target_arch = "x86_64")] +impl From for RuxStat { + fn from(attr: VfsNodeAttr) -> Self { + Self { + st_dev: 0, + st_ino: attr.ino(), + st_nlink: 1, + st_mode: ((attr.file_type() as u32) << 12) | attr.perm().bits() as u32, + st_uid: 1000, + st_gid: 1000, + __pad0: 0, + st_rdev: 0, + st_size: attr.size() as _, + st_blksize: 512, + st_blocks: attr.blocks() as _, + st_atime: RuxTimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + st_mtime: RuxTimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + st_ctime: RuxTimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + __unused: [0; 3], + } + } +} + /// Trait for file-like objects in a file descriptor table. pub trait FileLike: Send + Sync { + /// Get the absolute path of the file-like object. + fn path(&self) -> AbsPath; + /// Reads data from the file-like object into the provided buffer. /// /// Returns the number of bytes read on success. @@ -122,16 +343,134 @@ pub trait FileLike: Send + Sync { /// Polls the file-like object for readiness events. fn poll(&self) -> LinuxResult; - /// Sets or clears the non-blocking I/O mode for the file-like object. - fn set_nonblocking(&self, nonblocking: bool) -> LinuxResult; + /// Sets the flags such as `nonblocking` for the file-like object. + /// Only File Status Flags can be changed once a file is opened. Other flags will be ignored. + fn set_flags(&self, _flags: OpenFlags) -> LinuxResult; + + /// Return File Access Modes and File Status Flags. Creation Flags needn't store. + /// `sys_fcntl` command `F_GETFL` will need both kinds + fn flags(&self) -> OpenFlags; + + /// Handles ioctl commands for the device. + fn ioctl(&self, _cmd: usize, _arg: usize) -> LinuxResult { + Err(LinuxError::ENOTTY) + } +} + +bitflags! { + /// Bit flags for file opening and creation options + /// + /// These flags control file access modes, creation semantics, and status flags. + /// The `file creation flags` affect the semantics of the open operation itself, while the `file status + /// flag` affect the semantics of subsequent I/O operations. + /// The file status flags can be retrieved and (in some cases) modified in `sys_fcntl`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] + pub struct OpenFlags: i32 { + // ---------------------------- + // File Access Modes (won't be changed once a file is opened) + // Note: `bitflags` do not encourage zero bit flag. So `O_RDONLY`` is represented as 0 (no bits set) + // ---------------------------- + /// Write-only access + const O_WRONLY = 1; + /// Read-write access + const O_RDWR = 2; + /// Mask for access mode bits (O_RDONLY, O_WRONLY, O_RDWR) + const O_ACCMODE = 3; + + // ---------------------------- + // Creation Flags + // ---------------------------- + /// Close-on-exec flag + const O_CLOEXEC = 0o2000000; + /// Create file if it doesn't exist + const O_CREAT = 0o100; + /// Fail if O_CREAT and file exists + const O_EXCL = 0o200; + /// Fail if path is not a directory + const O_DIRECTORY = 0o200000; + /// Don't assign controlling terminal + const O_NOCTTY = 0o400; + /// Don't follow symbolic links + const O_NOFOLLOW = 0o400000; + /// Truncate existing file to zero length + const O_TRUNC = 0o1000; + /// Create unnamed temporary file + /// (requires O_DIRECTORY if used with directory) + const O_TMPFILE = 0o20200000; + + // ---------------------------- + // Status flags (Will be stored in `FileLike` dyn obj using fn `flags()`) + // ---------------------------- + /// Non-blocking mode + const O_NONBLOCK = 0o4000; + /// Data synchronization (synchronous I/O) + const O_DSYNC = 0o10000; + /// File synchronization (sync I/O file integrity) + const O_SYNC = 0o4010000; + /// Synchronize read operations + const O_RSYNC = 0o4010000; + /// Append mode (writes at end of file) + const O_APPEND = 0o2000; + /// Asynchronous I/O notification + const O_ASYNC = 0o20000; + /// Direct I/O (no kernel buffering) + const O_DIRECT = 0o40000; + /// Allow large file support + const O_LARGEFILE = 0o100000; + /// Don't update file access time + const O_NOATIME = 0o1000000; + /// Open path without accessing filesystem. Also named `O_SEARCH` + const O_PATH = 0o10000000; + } +} + +impl OpenFlags { + /// `create_new` flag in `OpenOptions` + pub const CREATE_NEW: Self = Self::O_CREAT.union(Self::O_EXCL); + /// Read only flag + pub const O_RDONLY: Self = Self::empty(); + /// creation flags mask + pub const CREATION_FLAGS: Self = Self::O_CLOEXEC + .union(Self::O_CREAT) + .union(Self::O_DIRECTORY) + .union(Self::O_EXCL) + .union(Self::O_NOCTTY) + .union(Self::O_NOFOLLOW) + .union(Self::O_TMPFILE) + .union(Self::O_TRUNC); + + /// Checks if the file is opened in a readable mode + /// (O_RDONLY or O_RDWR, and not write-only) + pub fn readable(&self) -> bool { + !self.contains(Self::O_WRONLY) || self.contains(Self::O_RDWR) + } + + /// Checks if the file is opened in a writable mode + /// (O_WRONLY or O_RDWR) + pub fn writable(&self) -> bool { + self.contains(Self::O_WRONLY) || self.contains(Self::O_RDWR) + } + + /// Only return file status flags + pub fn status_flags(&self) -> Self { + *self & !Self::O_ACCMODE & !Self::CREATION_FLAGS + } + + /// Return the file access mode and the file status flags. Used in `sys_fcntl` with `F_GETFL` mode + pub fn getfl(&self) -> Self { + *self & !Self::CREATION_FLAGS + } } -/// Maximum number of files per process -pub const RUX_FILE_LIMIT: usize = 1024; -lazy_static::lazy_static! { - /// Global file descriptor table protected by a read-write lock. - pub static ref FD_TABLE: RwLock, RUX_FILE_LIMIT>> = { - let fd_table = FlattenObjects::new(); - RwLock::new(fd_table) - }; +impl From for Cap { + fn from(openflags: OpenFlags) -> Self { + let mut cap = Cap::empty(); + if openflags.readable() { + cap |= Cap::READ; + } + if openflags.writable() { + cap |= Cap::WRITE; + } + cap + } } diff --git a/modules/ruxfs/Cargo.toml b/modules/ruxfs/Cargo.toml index 0ca624616..5ffe62d60 100644 --- a/modules/ruxfs/Cargo.toml +++ b/modules/ruxfs/Cargo.toml @@ -17,18 +17,26 @@ ramfs = ["dep:axfs_ramfs"] procfs = ["dep:axfs_ramfs"] sysfs = ["dep:axfs_ramfs"] etcfs = ["dep:axfs_ramfs"] -fatfs = ["dep:fatfs"] -myfs = ["dep:crate_interface"] +mntfs = ["dep:axfs_ramfs"] +blkfs = [] +fatfs = ["blkfs", "dep:fatfs"] +# TODO: wait for CI support for ext4 +# lwext4_rust = ["blkfs", "dep:lwext4_rust"] +ext4_rs = ["blkfs", "dep:ext4_rs"] +another_ext4 = ["blkfs", "dep:another_ext4"] +myfs = [] use-ramdisk = [] alloc = ["axalloc"] fp_simd = [] -default = ["devfs", "ramfs", "fatfs", "procfs", "sysfs", "etcfs"] +default = ["devfs", "ramfs", "procfs", "sysfs", "etcfs", "mntfs"] [dependencies] -log = "0.4" +log = { workspace = true } +spin = { workspace = true } cfg-if = "1.0" lazy_init = { path = "../../crates/lazy_init" } +lazy_static = { version = "1.4", features = ["spin_no_std"] } capability = { path = "../../crates/capability" } driver_block = { path = "../../crates/driver_block" } axio = { path = "../../crates/axio", features = ["alloc"] } @@ -36,15 +44,19 @@ axerrno = { path = "../../crates/axerrno" } axfs_vfs = { path = "../../crates/axfs_vfs" } axfs_devfs = { path = "../../crates/axfs_devfs", optional = true } axfs_ramfs = { path = "../../crates/axfs_ramfs", optional = true } +spinlock = { path = "../../crates/spinlock" } +ruxfifo = { path = "../../crates/ruxfifo" } +crate_interface = { version = "0.1.1" } +ruxfdtable = { path = "../ruxfdtable" } ruxdriver = { path = "../ruxdriver", features = ["block"] } -axsync = { path = "../axsync" } -crate_interface = { version = "0.1.1", optional = true } axalloc = { path = "../axalloc", optional = true } memory_addr = "0.1.0" +# lwext4_rust = { git = "https://github.com/elliott10/lwext4_rust", optional = true } +ext4_rs = { git = "https://github.com/yuoo655/ext4_rs.git", rev = "6bcc7f5", optional = true } +another_ext4 = { git = "https://github.com/LJxTHUCS/another_ext4.git", rev = "3c3e03a", optional = true } [dependencies.fatfs] git = "https://github.com/syswonder/rust-fatfs.git" -rev = "bf8ad02" optional = true default-features = false features = [ # no std @@ -55,7 +67,5 @@ features = [ # no std ] [dev-dependencies] -ruxdriver = { path = "../ruxdriver", features = ["block", "ramdisk"] } +ruxdriver = { path = "../ruxdriver", features = ["block", "ramdisk", "dyn"] } driver_block = { path = "../../crates/driver_block", features = ["ramdisk"] } -axsync = { path = "../axsync", features = ["multitask"] } -ruxtask = { path = "../ruxtask", features = ["test"] } diff --git a/modules/ruxfs/src/api/dir.rs b/modules/ruxfs/src/api/dir.rs deleted file mode 100644 index 4f10aaca6..000000000 --- a/modules/ruxfs/src/api/dir.rs +++ /dev/null @@ -1,158 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use alloc::string::String; -use axio::Result; -use core::fmt; - -use super::FileType; -use crate::fops; - -/// Iterator over the entries in a directory. -pub struct ReadDir<'a> { - path: &'a str, - inner: fops::Directory, - buf_pos: usize, - buf_end: usize, - end_of_stream: bool, - dirent_buf: [fops::DirEntry; 31], -} - -/// Entries returned by the [`ReadDir`] iterator. -pub struct DirEntry<'a> { - dir_path: &'a str, - entry_name: String, - entry_type: FileType, -} - -/// A builder used to create directories in various manners. -#[derive(Default, Debug)] -pub struct DirBuilder { - recursive: bool, -} - -impl<'a> ReadDir<'a> { - pub(super) fn new(path: &'a str) -> Result { - let mut opts = fops::OpenOptions::new(); - opts.read(true); - let inner = fops::Directory::open_dir(path, &opts)?; - const EMPTY: fops::DirEntry = fops::DirEntry::default(); - let dirent_buf = [EMPTY; 31]; - Ok(ReadDir { - path, - inner, - end_of_stream: false, - buf_pos: 0, - buf_end: 0, - dirent_buf, - }) - } -} - -impl<'a> Iterator for ReadDir<'a> { - type Item = Result>; - - fn next(&mut self) -> Option>> { - if self.end_of_stream { - return None; - } - - loop { - if self.buf_pos >= self.buf_end { - match self.inner.read_dir(&mut self.dirent_buf) { - Ok(n) => { - if n == 0 { - self.end_of_stream = true; - return None; - } - self.buf_pos = 0; - self.buf_end = n; - } - Err(e) => { - self.end_of_stream = true; - return Some(Err(e)); - } - } - } - let entry = &self.dirent_buf[self.buf_pos]; - self.buf_pos += 1; - let name_bytes = entry.name_as_bytes(); - if name_bytes == b"." || name_bytes == b".." { - continue; - } - let entry_name = unsafe { core::str::from_utf8_unchecked(name_bytes).into() }; - let entry_type = entry.entry_type(); - - return Some(Ok(DirEntry { - dir_path: self.path, - entry_name, - entry_type, - })); - } - } -} - -impl<'a> DirEntry<'a> { - /// Returns the full path to the file that this entry represents. - /// - /// The full path is created by joining the original path to `read_dir` - /// with the filename of this entry. - pub fn path(&self) -> String { - String::from(self.dir_path.trim_end_matches('/')) + "/" + &self.entry_name - } - - /// Returns the bare file name of this directory entry without any other - /// leading path component. - pub fn file_name(&self) -> String { - self.entry_name.clone() - } - - /// Returns the file type for the file that this entry points at. - pub fn file_type(&self) -> FileType { - self.entry_type - } -} - -impl fmt::Debug for DirEntry<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("DirEntry").field(&self.path()).finish() - } -} - -impl DirBuilder { - /// Creates a new set of options with default mode/security settings for all - /// platforms and also non-recursive. - pub fn new() -> Self { - Self { recursive: false } - } - - /// Indicates that directories should be created recursively, creating all - /// parent directories. Parents that do not exist are created with the same - /// security and permissions settings. - pub fn recursive(&mut self, recursive: bool) -> &mut Self { - self.recursive = recursive; - self - } - - /// Creates the specified directory with the options configured in this - /// builder. - pub fn create(&self, path: &str) -> Result<()> { - if self.recursive { - self.create_dir_all(path) - } else { - crate::root::create_dir(None, path) - } - } - - /// Recursively create a directory and all of its parent components if they - /// are missing. - pub fn create_dir_all(&self, path: &str) -> Result<()> { - crate::root::create_dir_all(None, path) - } -} diff --git a/modules/ruxfs/src/api/file.rs b/modules/ruxfs/src/api/file.rs deleted file mode 100644 index ea4eecdf0..000000000 --- a/modules/ruxfs/src/api/file.rs +++ /dev/null @@ -1,196 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use axio::{prelude::*, Result, SeekFrom}; -use core::fmt; - -use crate::fops; - -/// A structure representing a type of file with accessors for each file type. -/// It is returned by [`Metadata::file_type`] method. -pub type FileType = fops::FileType; - -/// Representation of the various permissions on a file. -pub type Permissions = fops::FilePerm; - -/// An object providing access to an open file on the filesystem. -pub struct File { - inner: fops::File, -} - -/// Metadata information about a file. -pub struct Metadata(fops::FileAttr); - -/// Options and flags which can be used to configure how a file is opened. -#[derive(Clone, Debug)] -pub struct OpenOptions(fops::OpenOptions); - -impl OpenOptions { - /// Creates a blank new set of options ready for configuration. - pub const fn new() -> Self { - OpenOptions(fops::OpenOptions::new()) - } - - /// Sets the option for read access. - pub fn read(&mut self, read: bool) -> &mut Self { - self.0.read(read); - self - } - - /// Sets the option for write access. - pub fn write(&mut self, write: bool) -> &mut Self { - self.0.write(write); - self - } - - /// Sets the option for the append mode. - pub fn append(&mut self, append: bool) -> &mut Self { - self.0.append(append); - self - } - - /// Sets the option for truncating a previous file. - pub fn truncate(&mut self, truncate: bool) -> &mut Self { - self.0.truncate(truncate); - self - } - - /// Sets the option to create a new file, or open it if it already exists. - pub fn create(&mut self, create: bool) -> &mut Self { - self.0.create(create); - self - } - - /// Sets the option to create a new file, failing if it already exists. - pub fn create_new(&mut self, create_new: bool) -> &mut Self { - self.0.create_new(create_new); - self - } - - /// Opens a file at `path` with the options specified by `self`. - pub fn open(&self, path: &str) -> Result { - fops::File::open(path, &self.0).map(|inner| File { inner }) - } -} - -impl Metadata { - /// Returns the file type for this metadata. - pub const fn file_type(&self) -> FileType { - self.0.file_type() - } - - /// Returns `true` if this metadata is for a directory. The - /// result is mutually exclusive to the result of - /// [`Metadata::is_file`]. - pub const fn is_dir(&self) -> bool { - self.0.is_dir() - } - - /// Returns `true` if this metadata is for a regular file. The - /// result is mutually exclusive to the result of - /// [`Metadata::is_dir`]. - pub const fn is_file(&self) -> bool { - self.0.is_file() - } - - /// Returns the size of the file, in bytes, this metadata is for. - #[allow(clippy::len_without_is_empty)] - pub const fn len(&self) -> u64 { - self.0.size() - } - - /// Returns the permissions of the file this metadata is for. - pub const fn permissions(&self) -> Permissions { - self.0.perm() - } - - /// Returns the total size of this file in bytes. - pub const fn size(&self) -> u64 { - self.0.size() - } - - /// Returns the number of blocks allocated to the file, in 512-byte units. - pub const fn blocks(&self) -> u64 { - self.0.blocks() - } -} - -impl fmt::Debug for Metadata { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Metadata") - .field("file_type", &self.file_type()) - .field("is_dir", &self.is_dir()) - .field("is_file", &self.is_file()) - .field("permissions", &self.permissions()) - .finish_non_exhaustive() - } -} - -impl File { - /// Attempts to open a file in read-only mode. - pub fn open(path: &str) -> Result { - OpenOptions::new().read(true).open(path) - } - - /// Opens a file in write-only mode. - pub fn create(path: &str) -> Result { - OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(path) - } - - /// Creates a new file in read-write mode; error if the file exists. - pub fn create_new(path: &str) -> Result { - OpenOptions::new() - .read(true) - .write(true) - .create_new(true) - .open(path) - } - - /// Returns a new OpenOptions object. - pub fn options() -> OpenOptions { - OpenOptions::new() - } - - /// Truncates or extends the underlying file, updating the size of - /// this file to become `size`. - pub fn set_len(&self, size: u64) -> Result<()> { - self.inner.truncate(size) - } - - /// Queries metadata about the underlying file. - pub fn metadata(&self) -> Result { - self.inner.get_attr().map(Metadata) - } -} - -impl Read for File { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner.read(buf) - } -} - -impl Write for File { - fn write(&mut self, buf: &[u8]) -> Result { - self.inner.write(buf) - } - - fn flush(&mut self) -> Result<()> { - self.inner.flush() - } -} - -impl Seek for File { - fn seek(&mut self, pos: SeekFrom) -> Result { - self.inner.seek(pos) - } -} diff --git a/modules/ruxfs/src/api/mod.rs b/modules/ruxfs/src/api/mod.rs index f81c375cf..d8a16efeb 100644 --- a/modules/ruxfs/src/api/mod.rs +++ b/modules/ruxfs/src/api/mod.rs @@ -7,92 +7,147 @@ * See the Mulan PSL v2 for more details. */ -//! [`std::fs`]-like high-level filesystem manipulation operations. - -mod dir; -mod file; - -pub use self::dir::{DirBuilder, DirEntry, ReadDir}; -pub use self::file::{File, FileType, Metadata, OpenOptions, Permissions}; - +//! High-level filesystem manipulation operations. +//! +//! Provided for `arceos_api` module and `axstd` user lib. +use super::fops; +pub use super::{ + directory::Directory, file::File, AbsPath, DirEntry, FileAttr, FilePerm, FileType, OpenFlags, + RelPath, +}; use alloc::{string::String, vec::Vec}; -use axio::{self as io, prelude::*}; - -/// Returns an iterator over the entries within a directory. -pub fn read_dir(path: &str) -> io::Result { - ReadDir::new(path) +use axerrno::ax_err; +use axfs_vfs::{VfsError, VfsNodePerm, VfsNodeType}; +use axio::{self as io, prelude::*, Error, Result}; +/// Opens a regular file at given path. Fails if path points to a directory. +pub fn open_file(path: &AbsPath, flags: OpenFlags) -> Result { + let node = fops::open_abspath(path, flags, VfsNodePerm::default_file())?; + if node.get_attr()?.is_dir() { + Err(Error::IsADirectory) + } else { + Ok(File::new(path.to_owned(), node, flags)) + } } -/// Returns the canonical, absolute form of a path with all intermediate -/// components normalized. -pub fn canonicalize(path: &str) -> io::Result { - crate::root::absolute_path(path) +/// Opens a directory at given path. Fails if path points to non-directory. +pub fn open_dir(path: &AbsPath, flags: OpenFlags) -> Result { + let node = fops::open_abspath(path, flags, VfsNodePerm::default_dir())?; + if node.get_attr()?.is_dir() { + Ok(Directory::new(path.to_owned(), node, flags)) + } else { + Err(Error::NotADirectory) + } } -/// Returns the current working directory as a [`String`]. -pub fn current_dir() -> io::Result { - crate::root::current_dir() +/// Returns the current working directory as a [`AbsPath`]. +pub fn current_dir() -> io::Result> { + Ok(fops::current_dir().unwrap()) } /// Changes the current working directory to the specified path. -pub fn set_current_dir(path: &str) -> io::Result<()> { - crate::root::set_current_dir(path) +pub fn set_current_dir(path: AbsPath<'static>) -> io::Result<()> { + fops::set_current_dir(path) +} + +/// Return the canonicalized and absolute path of the specified path. +pub fn absolute_path(path: &str) -> io::Result> { + fops::absolute_path(path) +} + +/// Get the attibutes of a file or directory. +pub fn get_attr(path: &AbsPath) -> io::Result { + fops::lookup(path)?.get_attr() } /// Read the entire contents of a file into a bytes vector. -pub fn read(path: &str) -> io::Result> { - let mut file = File::open(path)?; - let size = file.metadata().map(|m| m.len()).unwrap_or(0); +pub fn read(path: &AbsPath) -> io::Result> { + let mut file = open_file(path, OpenFlags::O_RDONLY)?; + let size = file.get_attr().map(|m| m.size()).unwrap_or(0); let mut bytes = Vec::with_capacity(size as usize); file.read_to_end(&mut bytes)?; Ok(bytes) } /// Read the entire contents of a file into a string. -pub fn read_to_string(path: &str) -> io::Result { - let mut file = File::open(path)?; - let size = file.metadata().map(|m| m.len()).unwrap_or(0); +pub fn read_to_string(path: &AbsPath) -> io::Result { + let mut file = open_file(path, OpenFlags::O_RDONLY)?; + let size = file.get_attr().map(|m| m.size()).unwrap_or(0); let mut string = String::with_capacity(size as usize); file.read_to_string(&mut string)?; Ok(string) } /// Write a slice as the entire contents of a file. -pub fn write>(path: &str, contents: C) -> io::Result<()> { - File::create(path)?.write_all(contents.as_ref()) -} - -/// Given a path, query the file system to get information about a file, -/// directory, etc. -pub fn metadata(path: &str) -> io::Result { - File::open(path)?.metadata() +pub fn write>(path: &AbsPath, contents: C) -> io::Result<()> { + open_file( + path, + OpenFlags::O_WRONLY | OpenFlags::O_CREAT | OpenFlags::O_TRUNC, + )? + .write_all(contents.as_ref()) } /// Creates a new, empty directory at the provided path. -pub fn create_dir(path: &str) -> io::Result<()> { - DirBuilder::new().create(path) +pub fn create_dir(path: &AbsPath) -> io::Result<()> { + match fops::lookup(path) { + Ok(_) => return ax_err!(AlreadyExists), + Err(VfsError::NotFound) => {} + Err(e) => return ax_err!(e), + } + fops::create(path, VfsNodeType::Dir, VfsNodePerm::default_dir()) } /// Recursively create a directory and all of its parent components if they /// are missing. -pub fn create_dir_all(path: &str) -> io::Result<()> { - DirBuilder::new().recursive(true).create(path) +pub fn create_dir_all(path: &AbsPath) -> io::Result<()> { + match fops::lookup(path) { + Ok(_) => return ax_err!(AlreadyExists), + Err(VfsError::NotFound) => {} + Err(e) => return ax_err!(e), + } + fops::create_dir_all(path) } /// Removes an empty directory. -pub fn remove_dir(path: &str) -> io::Result<()> { - crate::root::remove_dir(None, path) +pub fn remove_dir(path: &AbsPath) -> io::Result<()> { + let node = fops::lookup(path)?; + let attr = node.get_attr()?; + if !attr.is_dir() { + return ax_err!(NotADirectory); + } + if fops::is_mount_point(path) { + return ax_err!(PermissionDenied); + } + if !attr.perm().owner_writable() { + return ax_err!(PermissionDenied); + } + if !node.is_empty()? { + return ax_err!(DirectoryNotEmpty); + } + fops::remove_dir(path) } /// Removes a file from the filesystem. -pub fn remove_file(path: &str) -> io::Result<()> { - crate::root::remove_file(None, path) +pub fn remove_file(path: &AbsPath) -> io::Result<()> { + let node = fops::lookup(path)?; + let attr = node.get_attr()?; + if attr.is_dir() { + return ax_err!(IsADirectory); + } + if !attr.perm().owner_writable() { + return ax_err!(PermissionDenied); + } + fops::remove_file(path) } /// Rename a file or directory to a new name. /// Delete the original file if `old` already exists. /// /// This only works then the new path is in the same mounted fs. -pub fn rename(old: &str, new: &str) -> io::Result<()> { - crate::root::rename(old, new) +pub fn rename(old: &AbsPath, new: &AbsPath) -> io::Result<()> { + fops::lookup(old)?; + match fops::lookup(new) { + Ok(_) => ax_err!(AlreadyExists), + Err(VfsError::NotFound) => fops::rename(old, new), + Err(e) => ax_err!(e), + } } diff --git a/modules/ruxfs/src/arch/mod.rs b/modules/ruxfs/src/arch/mod.rs index 9f1709f86..635f19016 100644 --- a/modules/ruxfs/src/arch/mod.rs +++ b/modules/ruxfs/src/arch/mod.rs @@ -39,8 +39,8 @@ pub fn get_meminfo() -> String { let totalram = freeram + allocator.used_bytes() as c_ulong; let mut meminfo = String::new(); - meminfo.push_str(format!("MemTotal: {:8}\n", totalram).as_ref()); - meminfo.push_str(format!("MemFree: {:8}\n", freeram).as_ref()); + meminfo.push_str(format!("MemTotal: {totalram:8}\n").as_ref()); + meminfo.push_str(format!("MemFree: {freeram:8}\n").as_ref()); meminfo } diff --git a/modules/ruxfs/src/arch/x86_64.rs b/modules/ruxfs/src/arch/x86_64.rs index 3d78ac27a..fa32c4623 100644 --- a/modules/ruxfs/src/arch/x86_64.rs +++ b/modules/ruxfs/src/arch/x86_64.rs @@ -104,7 +104,7 @@ pub fn get_cpuinfo() -> String { cpuinfo.push_str("model name\t: unknown\n".as_ref()); } else { let model_name = String::from_utf8_lossy(&model_bytes); - cpuinfo.push_str(format!("model name\t: {}\n", model_name).as_ref()); + cpuinfo.push_str(format!("model name\t: {model_name}\n").as_ref()); } if c.x86_mask != 0 || c.cpuid_level >= 0 { diff --git a/modules/ruxfs/src/dev.rs b/modules/ruxfs/src/dev.rs index f897c4c76..b75820b0a 100644 --- a/modules/ruxfs/src/dev.rs +++ b/modules/ruxfs/src/dev.rs @@ -7,6 +7,8 @@ * See the Mulan PSL v2 for more details. */ +//! Block device abstraction. + use ruxdriver::prelude::*; const BLOCK_SIZE: usize = 512; @@ -99,6 +101,30 @@ impl Disk { Ok(write_size) } + /// Read a single block starting from the specified offset. + #[allow(unused)] + pub fn read_offset(&mut self, offset: usize) -> [u8; BLOCK_SIZE] { + let block_id = offset / BLOCK_SIZE; + let mut block_data = [0u8; BLOCK_SIZE]; + self.dev + .read_block(block_id as u64, &mut block_data) + .unwrap(); + block_data + } + + /// Write single block starting from the specified offset. + #[allow(unused)] + pub fn write_offset(&mut self, offset: usize, buf: &[u8]) -> DevResult { + assert!( + buf.len() == BLOCK_SIZE, + "Buffer length must be equal to BLOCK_SIZE" + ); + assert!(offset % BLOCK_SIZE == 0); + let block_id = offset / BLOCK_SIZE; + self.dev.write_block(block_id as u64, buf).unwrap(); + Ok(buf.len()) + } + ///flush device cache pub fn do_flush(&mut self) -> DevResult { self.dev.flush() diff --git a/modules/ruxfs/src/devfuse.rs b/modules/ruxfs/src/devfuse.rs new file mode 100644 index 000000000..5b8498dc6 --- /dev/null +++ b/modules/ruxfs/src/devfuse.rs @@ -0,0 +1,134 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! /dev/fuse + +#![allow(dead_code)] +use alloc::sync::Arc; +use core::sync::atomic::{AtomicI32, Ordering}; + +use alloc::vec; +use alloc::vec::Vec; +use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, VfsResult}; +use log::*; +use spin::Mutex; +use spinlock::SpinNoIrq; + +/// A global flag to indicate the state of the FUSE device. +pub static FUSEFLAG: AtomicI32 = AtomicI32::new(0); +lazy_static::lazy_static! { + /// vector to store data for FUSE operations. + pub static ref FUSE_VEC: Arc>> = Arc::new(SpinNoIrq::new(Vec::new())); +} + +/// A device behaves like `/dev/fuse`. +/// +/// It always transmits to the daemon in user space. +pub struct FuseDev { + data: Mutex>, +} + +impl Default for FuseDev { + fn default() -> Self { + Self::new() + } +} + +impl FuseDev { + /// Create a new instance. + pub fn new() -> Self { + debug!("fuse_dev new here..."); + Self { + data: Mutex::new(vec![0; 1e8 as usize]), + } + } +} + +impl VfsNodeOps for FuseDev { + fn open(&self) -> VfsResult> { + debug!("fuse_dev open here..."); + Ok(None) + } + + fn get_attr(&self) -> VfsResult { + debug!("fuse_dev get_attr here..."); + Ok(VfsNodeAttr::new( + 0, + VfsNodePerm::default_file(), + VfsNodeType::CharDevice, + 0, + 0, + )) + } + + fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { + debug!( + "fuse_dev read buf len: {:?} at pos: {:?}", + buf.len(), + offset + ); + + let mut flag; + + flag = FUSEFLAG.load(Ordering::SeqCst); + if flag > 100 { + debug!("flag in read__ is {flag:?}, should back to fuse_node."); + FUSEFLAG.store(-flag, Ordering::Relaxed); + } + + loop { + flag = FUSEFLAG.load(Ordering::SeqCst); + if flag > 0 { + debug!("flag _read_ is set to {flag:?},, exiting loop. hhh"); + break; + } + } + + let mut vec = FUSE_VEC.lock(); + let vec_len = vec.len(); + buf[..vec_len].copy_from_slice(&vec[..vec_len]); + debug!("Fusevec _read_ len: {:?}, vec: {:?}", vec.len(), vec); + vec.clear(); + + Ok(vec_len) + } + + fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { + debug!( + "fuse_dev writes buf len: {:?} at pos: {:?}, buf: {:?}", + buf.len(), + offset, + buf + ); + + let mut flag; + + loop { + flag = FUSEFLAG.load(Ordering::SeqCst); + if flag > 0 { + debug!("Fuseflag _write_ is set to {flag:?},, exiting loop. yyy"); + break; + } + } + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(buf); + debug!("Fusevec _write_ len: {:?}, vec: {:?}", vec.len(), vec); + + FUSEFLAG.store(flag + 100, Ordering::Relaxed); + + Ok(buf.len()) + } + + fn truncate(&self, _size: u64) -> VfsResult { + Ok(()) + } + + axfs_vfs::impl_vfs_non_dir_default! {} +} diff --git a/modules/ruxfs/src/directory.rs b/modules/ruxfs/src/directory.rs new file mode 100644 index 000000000..7fc755b3c --- /dev/null +++ b/modules/ruxfs/src/directory.rs @@ -0,0 +1,140 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ +use core::sync::atomic::{AtomicUsize, Ordering}; + +use crate::{AbsPath, DirEntry, FileAttr}; +use alloc::sync::Arc; +use axerrno::{AxResult, LinuxError, LinuxResult}; +use axfs_vfs::VfsNodeRef; +use axio::PollState; +use capability::{Cap, WithCap}; +use ruxfdtable::{FileLike, OpenFlags, RuxStat}; +use spin::rwlock::RwLock; +/// An opened directory object, with open permissions and a cursor for entry reading. +/// +/// Providing entry reading operations. +pub struct Directory { + path: AbsPath<'static>, + node: WithCap, + entry_idx: AtomicUsize, + flags: RwLock, +} + +impl Directory { + /// Creates an opened directory. + pub fn new(path: AbsPath<'static>, node: VfsNodeRef, flags: OpenFlags) -> Self { + Self { + path, + node: WithCap::new(node, Cap::from(flags) | Cap::EXECUTE), + entry_idx: AtomicUsize::new(0), + flags: RwLock::new(flags), + } + } + /// Get the entry cursor of the directory. + pub fn entry_idx(&self) -> usize { + self.entry_idx.load(Ordering::Relaxed) + } + + /// Set the entry cursor of the directory. + pub fn set_entry_idx(&self, idx: usize) { + self.entry_idx.store(idx, Ordering::Relaxed); + } + /// Gets the absolute path of the directory. + pub fn path(&self) -> AbsPath { + self.path.clone() + } + + /// Gets the file attributes. + pub fn get_attr(&self) -> AxResult { + self.node.access(Cap::empty())?.get_attr() + } + + /// Reads directory entries starts from the current position into the + /// given buffer. Returns the number of entries read. + /// + /// After the read, the cursor will be advanced by the number of entries + /// read. + pub fn read_dir(&self, dirents: &mut [DirEntry]) -> AxResult { + let current_entry_idx = self.entry_idx(); + let n = self + .node + .access(Cap::EXECUTE)? + .read_dir(current_entry_idx, dirents)?; + self.set_entry_idx(current_entry_idx + n); + Ok(n) + } +} + +impl Drop for Directory { + fn drop(&mut self) { + unsafe { self.node.access_unchecked().release().ok() }; + } +} + +impl FileLike for Directory { + fn path(&self) -> AbsPath { + self.path.clone() + } + + fn read(&self, _buf: &mut [u8]) -> LinuxResult { + Err(LinuxError::EISDIR) + } + + fn write(&self, _buf: &[u8]) -> LinuxResult { + Err(LinuxError::EISDIR) + } + + fn flush(&self) -> LinuxResult { + Ok(()) + } + + fn stat(&self) -> LinuxResult { + Ok(RuxStat::from(self.get_attr()?)) + } + + fn into_any(self: Arc) -> Arc { + self + } + + fn poll(&self) -> LinuxResult { + Ok(PollState { + readable: true, + writable: true, + pollhup: false, + }) + } + + fn flags(&self) -> OpenFlags { + *self.flags.read() + } + + fn set_flags(&self, flags: OpenFlags) -> LinuxResult { + *self.flags.write() = flags; + Ok(()) + } +} + +/// Implements the iterator trait for the directory. +impl Iterator for Directory { + type Item = AxResult; + + fn next(&mut self) -> Option { + use core::str; + let mut buf = [DirEntry::default()]; + match self.read_dir(buf.as_mut_slice()) { + Ok(0) => None, + Ok(1) => Some(Ok(DirEntry::new( + unsafe { str::from_utf8_unchecked(buf[0].name_as_bytes()) }, + buf[0].entry_type(), + ))), + Ok(_) => unreachable!(), + Err(e) => Some(Err(e)), + } + } +} diff --git a/modules/ruxfs/src/fifo.rs b/modules/ruxfs/src/fifo.rs new file mode 100644 index 000000000..c80f0fa69 --- /dev/null +++ b/modules/ruxfs/src/fifo.rs @@ -0,0 +1,243 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! Named pipe (FIFO) implementation for VFS +use crate::AbsPath; +use alloc::sync::Arc; +use axerrno::{AxError, LinuxError, LinuxResult}; +use axfs_vfs::VfsNodeOps; +use axio::PollState; +use crate_interface::call_interface; +use ruxfdtable::{FileLike, OpenFlags, RuxStat}; +use ruxfifo::FifoNode; +use spin::rwlock::RwLock; + +/// Reader endpoint for both FIFO (named pipe) and Pipe communication +pub struct FifoReader { + /// Absolute path in virtual filesystem + path: AbsPath<'static>, + /// Shared FIFO buffer + node: Arc, + /// Current open flags, e.g. `O_NONBLOCK` + flags: RwLock, +} + +impl FifoReader { + /// Opening a FIFO for reading data (using the open() function with the O_RDONLY flag) + /// block until another process opens the FIFO for writing data (using the open() function with the O_WRONLY flag). + pub fn new(path: AbsPath<'static>, node: Arc, flags: OpenFlags) -> Self { + node.acquire_reader(); + while node.writers() == 0 { + // PERF: use wait and wakeup instead of yield now + call_interface!(SchedYieldIf::yield_now); + if flags.contains(OpenFlags::O_NONBLOCK) { + // Opening a FIFO for reading is safe when the other end has no writer, as read operations will return no data. + break; + } + } + Self { + path, + node, + flags: RwLock::new(flags), + } + } +} + +impl Drop for FifoReader { + /// Decrements reader count and triggers wakeups + fn drop(&mut self) { + call_interface!(SchedYieldIf::yield_now); + self.node.release_reader() + } +} + +impl FileLike for FifoReader { + fn path(&self) -> AbsPath { + self.path.clone() + } + + /// Reads data from FIFO with blocking behavior + /// + /// - Returns 0 when all writers closed and buffer empty + /// - EAGAIN if non-blocking and no data available + fn read(&self, buf: &mut [u8]) -> LinuxResult { + loop { + match self.node.read_at(0, buf) { + Ok(len) => return Ok(len), + Err(AxError::WouldBlock) => { + // writer end closed and there is no data to read + if self.node.writers() == 0 { + return Ok(0); + } + if self.flags.read().contains(OpenFlags::O_NONBLOCK) { + return Err(LinuxError::EAGAIN); + } + crate_interface::call_interface!(SchedYieldIf::yield_now); + } + err => return err.map_err(LinuxError::from), + } + } + } + + /// fd is not open for writing. In this case should return `LinuxError::EBADF` + /// See `` + fn write(&self, _buf: &[u8]) -> LinuxResult { + Err(LinuxError::EBADF) + } + + fn flush(&self) -> LinuxResult { + Ok(()) + } + + fn stat(&self) -> LinuxResult { + Ok(RuxStat::from(self.node.get_attr()?)) + } + + fn into_any(self: Arc) -> Arc { + self + } + + fn poll(&self) -> LinuxResult { + self.node.reader_poll().map_err(LinuxError::from) + } + + fn set_flags(&self, flags: OpenFlags) -> LinuxResult { + *self.flags.write() = flags; + Ok(()) + } + + fn flags(&self) -> OpenFlags { + *self.flags.read() | OpenFlags::O_RDONLY + } +} + +/// Writer endpoint for both FIFO (named pipe) and Pipe communication +pub struct FifoWriter { + path: AbsPath<'static>, + node: Arc, + flags: RwLock, +} + +impl FifoWriter { + /// Creates new writer endpoint with POSIX error handling + /// + /// # Error Conditions + /// - `ENXIO` when non-blocking and no readers available + /// - Blocks indefinitely without O_NONBLOCK until reader appears + pub fn new(path: AbsPath<'static>, node: Arc, flags: OpenFlags) -> LinuxResult { + node.acquire_writer(); + while node.readers() == 0 { + // PERF: use wait and wakeup instead of yield now + call_interface!(SchedYieldIf::yield_now); + if flags.contains(OpenFlags::O_NONBLOCK) { + // opening a FIFO with ​**O_WRONLY** and ​**O_NONBLOCK** flags if no process has the FIFO open for reading will cause err + return Err(LinuxError::ENXIO); + } + } + Ok(Self { + path, + node, + flags: RwLock::new(flags), + }) + } +} + +impl Drop for FifoWriter { + fn drop(&mut self) { + call_interface!(SchedYieldIf::yield_now); + self.node.release_writer(); + } +} + +impl FileLike for FifoWriter { + fn path(&self) -> AbsPath { + self.path.clone() + } + + /// fd is not open for reading. In this case should return `LinuxError::EBADF` + /// See `` + fn read(&self, _buf: &mut [u8]) -> LinuxResult { + Err(LinuxError::EBADF) + } + + /// Writes data with backpressure management + /// + /// # Error Conditions + /// - `EPIPE` when all readers closed + /// - `EAGAIN` if non-blocking and buffer full + fn write(&self, buf: &[u8]) -> LinuxResult { + // TODO: when FifoNode has no reader when writing, send `SIGPIPE` signal + if self.node.readers() == 0 { + return Err(LinuxError::EPIPE); + } + loop { + match self.node.write_at(0, buf) { + Ok(len) => return Ok(len), + Err(AxError::WouldBlock) => { + if self.flags.read().contains(OpenFlags::O_NONBLOCK) { + return Err(LinuxError::EAGAIN); + } + crate_interface::call_interface!(SchedYieldIf::yield_now); + } + Err(_) => todo!(), + } + } + } + + fn flush(&self) -> LinuxResult { + Ok(()) + } + + fn stat(&self) -> LinuxResult { + Ok(RuxStat::from(self.node.get_attr()?)) + } + + fn into_any(self: Arc) -> Arc { + self + } + + fn poll(&self) -> LinuxResult { + self.node.writer_poll().map_err(LinuxError::from) + } + + fn set_flags(&self, flags: OpenFlags) -> LinuxResult { + *self.flags.write() = flags; + Ok(()) + } + + fn flags(&self) -> OpenFlags { + *self.flags.read() | OpenFlags::O_WRONLY + } +} + +/// Creates connected pipe pair with shared buffer +/// +/// If no endpoint is closed, readers count and writers count must always be 1 in `Arc`, +/// because Arc clone won't increase the count +pub fn new_pipe_pair(flags: OpenFlags) -> (Arc, Arc) { + let (reader_node, writer_node) = FifoNode::new_pair(); + let reader = Arc::new(FifoReader { + path: AbsPath::new(""), + node: reader_node, + flags: RwLock::new(flags), + }); + let writer = Arc::new(FifoWriter { + path: AbsPath::new(""), + node: writer_node, + flags: RwLock::new(flags), + }); + (reader, writer) +} + +#[crate_interface::def_interface] +/// task yield interface +pub trait SchedYieldIf { + /// Yields CPU using appropriate scheduling strategy + fn yield_now(); +} diff --git a/modules/ruxfs/src/file.rs b/modules/ruxfs/src/file.rs new file mode 100644 index 000000000..ab91819da --- /dev/null +++ b/modules/ruxfs/src/file.rs @@ -0,0 +1,200 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ +use alloc::sync::Arc; +use axerrno::{ax_err_type, AxResult, LinuxError, LinuxResult}; +use axfs_vfs::VfsNodeRef; +use axio::{PollState, Read, SeekFrom, Write}; +use capability::{Cap, WithCap}; + +use ruxfdtable::{FileLike, OpenFlags, RuxStat}; +use spin::{mutex::Mutex, RwLock}; + +use crate::{AbsPath, FileAttr}; + +/// An opened file with permissions and a cursor for I/O operations. +pub struct File { + /// Absolute path to the file + path: AbsPath<'static>, + /// Underlying VFS node with capabilities + node: WithCap, + /// Current read/write offset. + /// + /// Note: Operations like read/write/seek on `node` require atomic updates to `offset`. + /// Using `AtomicU64` alone cannot lock both `node` and `offset` atomically, risking inconsistent state. + /// `Mutex` ensures `offset` and `node` are modified as a single atomic unit. + offset: Mutex, + /// File mode flags + flags: RwLock, +} + +impl File { + /// Create an opened file. + pub fn new(path: AbsPath<'static>, node: VfsNodeRef, flags: OpenFlags) -> Self { + Self { + path, + node: WithCap::new(node, Cap::from(flags)), + offset: Mutex::new(0), + flags: RwLock::new(flags), + } + } + + /// Reads data into `dst` from current offset. Atomically updates the offset + /// after reading. Locking ensures synchronization with underlying node operations. + fn read(&self, dst: &mut [u8]) -> AxResult { + let mut offset = self.offset.lock(); + let read_len = self.read_at(*offset, dst)?; + *offset += read_len as u64; + Ok(read_len) + } + + /// Writes data from `src` to current offset. Handles append mode (O_APPEND) by + /// resetting offset to file size. Atomically updates offset after writing. + fn write(&self, src: &[u8]) -> AxResult { + let mut offset = self.offset.lock(); + if self.flags.read().contains(OpenFlags::O_APPEND) { + *offset = self.get_attr()?.size(); + }; + let node = self.node.access(Cap::WRITE)?; + let write_len = node.write_at(*offset, src)?; + *offset += write_len as u64; + Ok(write_len) + } + + /// Get the abcolute path of the file. + pub fn path(&self) -> AbsPath { + self.path.clone() + } + + /// Gets the file attributes. + pub fn get_attr(&self) -> AxResult { + self.node.access(Cap::empty())?.get_attr() + } + + /// Truncates the file to the specified size. + pub fn truncate(&self, size: u64) -> AxResult { + self.node.access(Cap::WRITE)?.truncate(size) + } + + /// Reads the file at the given position. Returns the number of bytes read. + /// + /// It does not update the file cursor. + pub fn read_at(&self, offset: u64, buf: &mut [u8]) -> AxResult { + self.node.access(Cap::READ)?.read_at(offset, buf) + } + + /// Writes the file at the given position. Returns the number of bytes + /// written. + /// + /// It does not update the file cursor. + pub fn write_at(&self, offset: u64, buf: &[u8]) -> AxResult { + self.node.access(Cap::WRITE)?.write_at(offset, buf) + } + + /// Flushes the file, writes all buffered data to the underlying device. + pub fn flush(&self) -> AxResult { + self.node.access(Cap::WRITE)?.fsync() + } + + /// Sets the cursor of the file to the specified offset. Returns the new + /// position after the seek. + pub fn seek(&self, pos: SeekFrom) -> AxResult { + let size = self.get_attr()?.size(); + let mut offset = self.offset.lock(); + let new_offset = match pos { + SeekFrom::Start(pos) => Some(pos), + SeekFrom::Current(off) => offset.checked_add_signed(off), + SeekFrom::End(off) => size.checked_add_signed(off), + } + .ok_or_else(|| ax_err_type!(InvalidInput))?; + *offset = new_offset; + Ok(new_offset) + } +} + +impl Drop for File { + fn drop(&mut self) { + unsafe { + self.node.access_unchecked().release().ok(); + } + } +} + +impl FileLike for File { + fn path(&self) -> AbsPath { + self.path.clone() + } + + /// Reads the file at the current position. Returns the number of bytes + /// read. + /// + /// After the read, the cursor will be advanced by the number of bytes read. + fn read(&self, buf: &mut [u8]) -> LinuxResult { + self.read(buf).map_err(LinuxError::from) + } + + /// Writes the file at the current position. Returns the number of bytes + /// written. + /// + /// After the write, the cursor will be advanced by the number of bytes + /// written. + fn write(&self, buf: &[u8]) -> LinuxResult { + self.write(buf).map_err(LinuxError::from) + } + + fn flush(&self) -> LinuxResult { + Ok(self.flush()?) + } + + fn stat(&self) -> LinuxResult { + Ok(RuxStat::from(self.get_attr()?)) + } + + fn into_any(self: Arc) -> Arc { + self + } + + fn poll(&self) -> LinuxResult { + self.node + .access(Cap::READ)? + .poll() + .map_err(LinuxError::from) + } + + fn set_flags(&self, flags: OpenFlags) -> LinuxResult { + *self.flags.write() = flags; + Ok(()) + } + + fn ioctl(&self, cmd: usize, arg: usize) -> LinuxResult { + self.node + .access(Cap::READ)? + .ioctl(cmd, arg) + .map_err(LinuxError::from) + } + + fn flags(&self) -> OpenFlags { + *self.flags.read() + } +} + +impl Read for File { + fn read(&mut self, buf: &mut [u8]) -> AxResult { + File::read(self, buf) + } +} + +impl Write for File { + fn write(&mut self, buf: &[u8]) -> AxResult { + File::write(self, buf) + } + + fn flush(&mut self) -> AxResult<()> { + self.node.access(Cap::WRITE)?.fsync() + } +} diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index c71f49054..00c904f54 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -7,411 +7,172 @@ * See the Mulan PSL v2 for more details. */ -//! Low-level filesystem operations. - -use axerrno::{ax_err, ax_err_type, AxResult}; -use axfs_vfs::{VfsError, VfsNodeRef}; -use axio::SeekFrom; -use capability::{Cap, WithCap}; -use core::fmt; - -#[cfg(feature = "myfs")] -pub use crate::dev::Disk; -#[cfg(feature = "myfs")] -pub use crate::fs::myfs::MyFileSystemIf; +//! Low-level filesystem operations. Provided for `ruxfs::api` and `ruxos_posix_api::fs` modules. +//! +//! - File: open, read, write, seek, truncate +//! - Directory: open, read, create, remove +//! +//! The interface is designed with low coupling to avoid repetitive error handling. +use alloc::sync::Arc; +use axerrno::{AxError, AxResult, LinuxResult}; +use axfs_vfs::{AbsPath, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType}; +use capability::Cap; +use ruxfdtable::{FileLike, OpenFlags}; +use ruxfifo::FifoNode; + +use crate::{ + directory::Directory, + fifo::{FifoReader, FifoWriter}, + file::File, + root::RootDirectory, + FileAttr, FilePerm, +}; + +#[crate_interface::def_interface] +/// Current working directory operations. +pub trait CurrentWorkingDirectoryOps { + /// Returns the absolute path of the specified path. + fn absolute_path(path: &str) -> AxResult>; + /// Returns the current working directory. + fn current_dir() -> AxResult>; + /// Sets the current working directory. + fn set_current_dir(path: AbsPath<'static>) -> AxResult; + /// get the root directory of the filesystem + fn root_dir() -> Arc; +} -/// Alias of [`axfs_vfs::VfsNodeType`]. -pub type FileType = axfs_vfs::VfsNodeType; -/// Alias of [`axfs_vfs::VfsDirEntry`]. -pub type DirEntry = axfs_vfs::VfsDirEntry; -/// Alias of [`axfs_vfs::VfsNodeAttr`]. -pub type FileAttr = axfs_vfs::VfsNodeAttr; -/// Alias of [`axfs_vfs::VfsNodePerm`]. -pub type FilePerm = axfs_vfs::VfsNodePerm; +#[allow(unused)] +pub(crate) fn absolute_path(path: &str) -> AxResult> { + crate_interface::call_interface!(CurrentWorkingDirectoryOps::absolute_path, path) +} -/// An opened file object, with open permissions and a cursor. -pub struct File { - node: WithCap, - is_append: bool, - offset: u64, +/// Get the current working directory. +pub fn current_dir() -> AxResult> { + crate_interface::call_interface!(CurrentWorkingDirectoryOps::current_dir) } -/// An opened directory object, with open permissions and a cursor for -/// [`read_dir`](Directory::read_dir). -pub struct Directory { - node: WithCap, - entry_idx: usize, +/// Set the current working directory. +pub fn set_current_dir(path: AbsPath<'static>) -> AxResult { + crate_interface::call_interface!(CurrentWorkingDirectoryOps::set_current_dir, path) } -/// Options and flags which can be used to configure how a file is opened. -#[derive(Clone)] -pub struct OpenOptions { - // generic - read: bool, - write: bool, - append: bool, - truncate: bool, - create: bool, - create_new: bool, - // system-specific - _custom_flags: i32, - _mode: u32, +pub(crate) fn root_dir() -> Arc { + crate_interface::call_interface!(CurrentWorkingDirectoryOps::root_dir) } -impl OpenOptions { - /// Creates a blank new set of options ready for configuration. - pub const fn new() -> Self { - Self { - // generic - read: false, - write: false, - append: false, - truncate: false, - create: false, - create_new: false, - // system-specific - _custom_flags: 0, - _mode: 0o666, - } - } - /// Sets the option for read access. - pub fn read(&mut self, read: bool) { - self.read = read; - } - /// Sets the option for write access. - pub fn write(&mut self, write: bool) { - self.write = write; - } - /// Sets the option for the append mode. - pub fn append(&mut self, append: bool) { - self.append = append; - } - /// Sets the option for truncating a previous file. - pub fn truncate(&mut self, truncate: bool) { - self.truncate = truncate; - } - /// Sets the option to create a new file, or open it if it already exists. - pub fn create(&mut self, create: bool) { - self.create = create; - } - /// Sets the option to create a new file, failing if it already exists. - pub fn create_new(&mut self, create_new: bool) { - self.create_new = create_new; - } +/* File operations with absolute path. */ - const fn is_valid(&self) -> bool { - if !self.read && !self.write && !self.append { - return false; - } - match (self.write, self.append) { - (true, false) => {} - (false, false) => { - if self.truncate || self.create || self.create_new { - return false; - } - } - (_, true) => { - if self.truncate && !self.create_new { - return false; - } - } - } - true - } +/// Look up a file given an absolute path. +pub fn lookup(path: &AbsPath) -> AxResult { + root_dir().clone().lookup(&path.to_rel()) } -impl File { - fn _open_at(dir: Option<&VfsNodeRef>, path: &str, opts: &OpenOptions) -> AxResult { - debug!("open file: {} {:?}", path, opts); - if !opts.is_valid() { - return ax_err!(InvalidInput); - } +/// Get the file attributes given an absolute path. +pub fn get_attr(path: &AbsPath) -> AxResult { + lookup(path)?.get_attr() +} - let node_option = crate::root::lookup(dir, path); - let node = if opts.create || opts.create_new { - match node_option { - Ok(node) => { - // already exists - if opts.create_new { - return ax_err!(AlreadyExists); - } - node - } - // not exists, create new - Err(VfsError::NotFound) => crate::root::create_file(dir, path)?, - Err(e) => return Err(e), +/// PERF: resolve the same path three times if not found! +/// +/// Internal helper to open a file by absolute path with flags and validation. +pub(crate) fn open_abspath( + path: &AbsPath, + flags: OpenFlags, + mode: FilePerm, +) -> AxResult { + let node = match lookup(path) { + Ok(node) => { + if flags.contains(OpenFlags::O_EXCL | OpenFlags::O_CREAT) { + return Err(AxError::AlreadyExists); } - } else { - // just open the existing - node_option? - }; - - let attr = node.get_attr()?; - if attr.is_dir() - && (opts.create || opts.create_new || opts.write || opts.append || opts.truncate) - { - return ax_err!(IsADirectory); - } - let access_cap = opts.into(); - if !perm_to_cap(attr.perm()).contains(access_cap) { - return ax_err!(PermissionDenied); + let attr = node.get_attr()?; + if !attr.is_dir() && flags.contains(OpenFlags::O_DIRECTORY) { + return Err(AxError::NotADirectory); + } + if attr.is_file() && flags.contains(OpenFlags::O_TRUNC) { + node.truncate(0)?; + } + node } - - node.open()?; - if opts.truncate { - node.truncate(0)?; + Err(AxError::NotFound) => { + if !flags.contains(OpenFlags::O_CREAT) || flags.contains(OpenFlags::O_DIRECTORY) { + return Err(AxError::NotFound); + } + create(path, VfsNodeType::File, mode)?; + lookup(path)? } - Ok(Self { - node: WithCap::new(node, access_cap), - is_append: opts.append, - offset: 0, - }) - } - - /// Opens a file at the path relative to the current directory. Returns a - /// [`File`] object. - pub fn open(path: &str, opts: &OpenOptions) -> AxResult { - Self::_open_at(None, path, opts) + Err(e) => return Err(e), + }; + let attr = node.get_attr()?; + if !Cap::from(attr.perm()).contains(Cap::from(flags)) { + return Err(AxError::PermissionDenied); } - - /// Truncates the file to the specified size. - pub fn truncate(&self, size: u64) -> AxResult { - self.node.access(Cap::WRITE)?.truncate(size)?; - Ok(()) - } - - /// Reads the file at the current position. Returns the number of bytes - /// read. - /// - /// After the read, the cursor will be advanced by the number of bytes read. - pub fn read(&mut self, buf: &mut [u8]) -> AxResult { - let node = self.node.access(Cap::READ)?; - let read_len = node.read_at(self.offset, buf)?; - self.offset += read_len as u64; - Ok(read_len) - } - - /// Reads the file at the given position. Returns the number of bytes read. - /// - /// It does not update the file cursor. - pub fn read_at(&self, offset: u64, buf: &mut [u8]) -> AxResult { - let node = self.node.access(Cap::READ)?; - let read_len = node.read_at(offset, buf)?; - Ok(read_len) - } - - /// Writes the file at the current position. Returns the number of bytes - /// written. - /// - /// After the write, the cursor will be advanced by the number of bytes - /// written. - pub fn write(&mut self, buf: &[u8]) -> AxResult { - let node = self.node.access(Cap::WRITE)?; - if self.is_append { - self.offset = self.get_attr()?.size(); - }; - let write_len = node.write_at(self.offset, buf)?; - self.offset += write_len as u64; - Ok(write_len) - } - - /// Writes the file at the given position. Returns the number of bytes - /// written. - /// - /// It does not update the file cursor. - pub fn write_at(&self, offset: u64, buf: &[u8]) -> AxResult { - let node = self.node.access(Cap::WRITE)?; - let write_len = node.write_at(offset, buf)?; - Ok(write_len) - } - - /// Flushes the file, writes all buffered data to the underlying device. - pub fn flush(&self) -> AxResult { - self.node.access(Cap::WRITE)?.fsync()?; - Ok(()) - } - - /// Sets the cursor of the file to the specified offset. Returns the new - /// position after the seek. - pub fn seek(&mut self, pos: SeekFrom) -> AxResult { - let size = self.get_attr()?.size(); - let new_offset = match pos { - SeekFrom::Start(pos) => Some(pos), - SeekFrom::Current(off) => self.offset.checked_add_signed(off), - SeekFrom::End(off) => size.checked_add_signed(off), - } - .ok_or_else(|| ax_err_type!(InvalidInput))?; - self.offset = new_offset; - Ok(new_offset) - } - - /// Gets the file attributes. - pub fn get_attr(&self) -> AxResult { - self.node.access(Cap::empty())?.get_attr() + if let Some(new_node) = node.open()? { + return Ok(new_node); } + Ok(node) } -impl Directory { - fn _open_dir_at(dir: Option<&VfsNodeRef>, path: &str, opts: &OpenOptions) -> AxResult { - debug!("open dir: {}", path); - if !opts.read { - return ax_err!(InvalidInput); - } - if opts.create || opts.create_new || opts.write || opts.append || opts.truncate { - return ax_err!(InvalidInput); - } - - let node = crate::root::lookup(dir, path)?; - let attr = node.get_attr()?; - if !attr.is_dir() { - return ax_err!(NotADirectory); - } - let access_cap = opts.into(); - if !perm_to_cap(attr.perm()).contains(access_cap) { - return ax_err!(PermissionDenied); - } - - node.open()?; - Ok(Self { - node: WithCap::new(node, access_cap | Cap::EXECUTE), - entry_idx: 0, - }) - } - - fn access_at(&self, path: &str) -> AxResult> { - if path.starts_with('/') { - Ok(None) - } else { - Ok(Some(self.node.access(Cap::EXECUTE)?)) +/// Opens a file-like object (file or directory) at given path with flags. +pub fn open_file_like( + path: &AbsPath, + flags: OpenFlags, + mode: FilePerm, +) -> LinuxResult> { + let node = open_abspath(path, flags, mode)?; + match node.get_attr()?.file_type() { + VfsNodeType::Dir => Ok(Arc::new(Directory::new(path.to_owned(), node, flags))), + VfsNodeType::File => Ok(Arc::new(File::new(path.to_owned(), node, flags))), + VfsNodeType::Fifo => { + let node = Arc::downcast::(node.as_any_arc()).unwrap(); + if flags.contains(OpenFlags::O_WRONLY) { + Ok(Arc::new(FifoWriter::new(path.to_owned(), node, flags)?)) + } else { + Ok(Arc::new(FifoReader::new(path.to_owned(), node, flags))) + } } + _ => Ok(Arc::new(File::new(path.to_owned(), node, flags))), } - - /// Opens a directory at the path relative to the current directory. - /// Returns a [`Directory`] object. - pub fn open_dir(path: &str, opts: &OpenOptions) -> AxResult { - Self::_open_dir_at(None, path, opts) - } - - /// Opens a directory at the path relative to this directory. Returns a - /// [`Directory`] object. - pub fn open_dir_at(&self, path: &str, opts: &OpenOptions) -> AxResult { - Self::_open_dir_at(self.access_at(path)?, path, opts) - } - - /// Opens a file at the path relative to this directory. Returns a [`File`] - /// object. - pub fn open_file_at(&self, path: &str, opts: &OpenOptions) -> AxResult { - File::_open_at(self.access_at(path)?, path, opts) - } - - /// Creates an empty file at the path relative to this directory. - pub fn create_file(&self, path: &str) -> AxResult { - crate::root::create_file(self.access_at(path)?, path) - } - - /// Creates an empty directory at the path relative to this directory. - pub fn create_dir(&self, path: &str) -> AxResult { - crate::root::create_dir(self.access_at(path)?, path) - } - - /// Removes a file at the path relative to this directory. - pub fn remove_file(&self, path: &str) -> AxResult { - crate::root::remove_file(self.access_at(path)?, path) - } - - /// Removes a directory at the path relative to this directory. - pub fn remove_dir(&self, path: &str) -> AxResult { - crate::root::remove_dir(self.access_at(path)?, path) - } - - /// Reads directory entries starts from the current position into the - /// given buffer. Returns the number of entries read. - /// - /// After the read, the cursor will be advanced by the number of entries - /// read. - pub fn read_dir(&mut self, dirents: &mut [DirEntry]) -> AxResult { - let n = self - .node - .access(Cap::READ)? - .read_dir(self.entry_idx, dirents)?; - self.entry_idx += n; - Ok(n) - } - - /// Rename a file or directory to a new name. - /// Delete the original file if `old` already exists. - /// - /// This only works then the new path is in the same mounted fs. - pub fn rename(&self, old: &str, new: &str) -> AxResult { - crate::root::rename(old, new) - } - - /// Gets the file attributes. - pub fn get_attr(&self) -> AxResult { - self.node.access(Cap::empty())?.get_attr() - } +} +/// Create a vfs node given an absolute path, type and mode. +/// +/// This function will not check if the node exists, check it with [`lookup`] first. +pub fn create(path: &AbsPath, ty: VfsNodeType, mode: VfsNodePerm) -> AxResult { + root_dir().create(&path.to_rel(), ty, mode) } -impl Drop for File { - fn drop(&mut self) { - unsafe { self.node.access_unchecked().release().ok() }; - } +/// Create a directory recursively given an absolute path. +/// +/// This function will not check if the directory exists, check it with [`lookup`] first. +pub fn create_dir_all(path: &AbsPath) -> AxResult { + root_dir().create_recursive(&path.to_rel(), VfsNodeType::Dir, VfsNodePerm::default_dir()) } -impl Drop for Directory { - fn drop(&mut self) { - unsafe { self.node.access_unchecked().release().ok() }; - } +/// Remove a file given an absolute path. +/// +/// This function will not check if the file exits or removeable, +/// check it with [`lookup`] first. +pub fn remove_file(path: &AbsPath) -> AxResult { + root_dir().unlink(&path.to_rel()) } -impl fmt::Debug for OpenOptions { - #[allow(unused_assignments)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut written = false; - macro_rules! fmt_opt { - ($field: ident, $label: literal) => { - if self.$field { - if written { - write!(f, " | ")?; - } - write!(f, $label)?; - written = true; - } - }; - } - fmt_opt!(read, "READ"); - fmt_opt!(write, "WRITE"); - fmt_opt!(append, "APPEND"); - fmt_opt!(truncate, "TRUNC"); - fmt_opt!(create, "CREATE"); - fmt_opt!(create_new, "CREATE_NEW"); - Ok(()) - } +/// Remove a directory given an absolute path. +/// +/// This function will not check if the directory exists or is empty, +/// check it with [`lookup`] first. +pub fn remove_dir(path: &AbsPath) -> AxResult { + root_dir().unlink(&path.to_rel()) } -impl From<&OpenOptions> for Cap { - fn from(opts: &OpenOptions) -> Cap { - let mut cap = Cap::empty(); - if opts.read { - cap |= Cap::READ; - } - if opts.write | opts.append { - cap |= Cap::WRITE; - } - cap - } +/// Check if a directory is a mount point. +pub fn is_mount_point(path: &AbsPath) -> bool { + root_dir().contains(path) } -fn perm_to_cap(perm: FilePerm) -> Cap { - let mut cap = Cap::empty(); - if perm.owner_readable() { - cap |= Cap::READ; - } - if perm.owner_writable() { - cap |= Cap::WRITE; - } - if perm.owner_executable() { - cap |= Cap::EXECUTE; - } - cap +/// Rename a file given an old and a new absolute path. +/// +/// This function will not check if the old path or new path exists, check it with +/// [`lookup`] first. +pub fn rename(old: &AbsPath, new: &AbsPath) -> AxResult { + root_dir().rename(&old.to_rel(), &new.to_rel()) } diff --git a/modules/ruxfs/src/fs/another_ext4.rs b/modules/ruxfs/src/fs/another_ext4.rs new file mode 100644 index 000000000..64accf17b --- /dev/null +++ b/modules/ruxfs/src/fs/another_ext4.rs @@ -0,0 +1,294 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! Reference: +//! - another_ext4: https://github.com/LJxTHUCS/another_ext4 +//! - axfs: https://github.com/Starry-OS/axfs + +use crate::dev::Disk; +use alloc::sync::Arc; +use another_ext4::{ + Block, BlockDevice, ErrCode as Ext4ErrorCode, Ext4, Ext4Error, FileType as EXt4FileType, + InodeMode as Ext4InodeMode, BLOCK_SIZE as EXT4_BLOCK_SIZE, EXT4_ROOT_INO, +}; +use axfs_vfs::{RelPath, VfsDirEntry, VfsError, VfsNodePerm, VfsResult}; +use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; +use axsync::Mutex; + +pub struct DiskAdapter(Arc>); + +unsafe impl Send for DiskAdapter {} +unsafe impl Sync for DiskAdapter {} + +// The io block size of the disk layer +const DISK_BLOCK_SIZE: usize = 512; + +// The block size of the file system +pub const BLOCK_SIZE: usize = EXT4_BLOCK_SIZE; + +impl BlockDevice for DiskAdapter { + fn read_block(&self, block_id: u64) -> Block { + let mut disk = self.0.lock(); + let base = block_id as usize * EXT4_BLOCK_SIZE; + let mut data = [0u8; EXT4_BLOCK_SIZE]; + // Per-disk-block read + for i in 0..(EXT4_BLOCK_SIZE / DISK_BLOCK_SIZE) { + let dblock = disk.read_offset(base + i * DISK_BLOCK_SIZE); + data[i * DISK_BLOCK_SIZE..(i + 1) * DISK_BLOCK_SIZE].copy_from_slice(&dblock); + } + Block::new(block_id, data) + } + + fn write_block(&self, block: &Block) { + let mut disk = self.0.lock(); + let base = block.id as usize * EXT4_BLOCK_SIZE; + // Per-disk-block write + for i in 0..(EXT4_BLOCK_SIZE / DISK_BLOCK_SIZE) { + let dblock = &block.data[i * DISK_BLOCK_SIZE..(i + 1) * DISK_BLOCK_SIZE]; + let _ = disk.write_offset(base + i * DISK_BLOCK_SIZE, dblock); + } + } +} + +pub struct Ext4FileSystem(Arc); + +impl Ext4FileSystem { + pub fn new(disk: Disk) -> Self { + let block_device = Arc::new(DiskAdapter(Arc::new(Mutex::new(disk)))); + let ext4 = Ext4::load(block_device).expect("Failed to load ext4 filesystem"); + log::info!("Ext4 filesystem loaded"); + Self(Arc::new(ext4)) + } +} + +impl VfsOps for Ext4FileSystem { + fn root_dir(&self) -> VfsNodeRef { + Arc::new(Ext4VirtInode::new(EXT4_ROOT_INO, self.0.clone())) + } + fn umount(&self) -> VfsResult { + self.0.flush_all(); + Ok(()) + } +} + +pub struct Ext4VirtInode { + id: u32, + fs: Arc, +} + +unsafe impl Send for Ext4VirtInode {} +unsafe impl Sync for Ext4VirtInode {} + +impl Ext4VirtInode { + fn new(id: u32, fs: Arc) -> Self { + log::trace!("Create Ext4VirtInode {}", id); + Self { id, fs } + } +} + +impl VfsNodeOps for Ext4VirtInode { + fn open(&self) -> VfsResult { + Ok(()) + } + + fn release(&self) -> VfsResult { + Ok(()) + } + + fn get_attr(&self) -> VfsResult { + self.fs + .getattr(self.id) + .map(|attr| { + VfsNodeAttr::new( + map_perm(attr.perm), + map_type(attr.ftype), + attr.size, + attr.blocks, + ) + }) + .map_err(map_error) + } + + // file operations: + + fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { + self.fs + .read(self.id, offset as usize, buf) + .map_err(map_error) + } + + fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { + self.fs + .write(self.id, offset as usize, buf) + .map_err(map_error) + } + + fn fsync(&self) -> VfsResult { + Ok(()) + } + + fn truncate(&self, size: u64) -> VfsResult { + // TODO: Simple implementation, just set the size, + // not truncate the file in the disk + self.fs + .setattr( + self.id, + None, + None, + None, + Some(size), + None, + None, + None, + None, + ) + .map_err(map_error) + } + + // directory operations: + + fn parent(&self) -> Option { + self.fs.lookup(self.id, "..").map_or(None, |parent| { + Some(Arc::new(Ext4VirtInode::new(parent, self.fs.clone()))) + }) + } + + fn lookup(self: Arc, path: &RelPath) -> VfsResult { + match self.fs.generic_lookup(self.id, path) { + Ok(id) => Ok(Arc::new(Ext4VirtInode::new(id, self.fs.clone()))), + Err(e) => Err(map_error(e)), + } + } + + fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { + if self.fs.generic_lookup(self.id, path).is_ok() { + return Ok(()); + } + let mode = Ext4InodeMode::from_type_and_perm(map_type_inv(ty), Ext4InodeMode::ALL_RWX); + self.fs + .generic_create(self.id, path, mode) + .map(|_| ()) + .map_err(map_error) + } + + fn unlink(&self, path: &RelPath) -> VfsResult { + self.fs.unlink(self.id, path).map_err(map_error) + } + + fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult { + self.fs + .listdir(self.id) + .map(|entries| { + for (i, entry) in entries.iter().skip(start_idx).enumerate() { + if i >= dirents.len() { + return i; + } + dirents[i] = VfsDirEntry::new(&entry.name(), map_type(entry.file_type())); + } + entries.len() - start_idx + }) + .map_err(map_error) + } + + fn rename(&self, src_path: &RelPath, dst_path: &RelPath) -> VfsResult { + self.fs + .generic_rename(self.id, src_path, dst_path) + .map_err(map_error) + } + + fn as_any(&self) -> &dyn core::any::Any { + self as &dyn core::any::Any + } +} + +fn map_error(ext4_err: Ext4Error) -> VfsError { + log::warn!("Ext4 error: {:?}", ext4_err); + match ext4_err.code() { + Ext4ErrorCode::EPERM => VfsError::PermissionDenied, + Ext4ErrorCode::ENOENT => VfsError::NotFound, + Ext4ErrorCode::EIO => VfsError::Io, + Ext4ErrorCode::ENXIO => VfsError::Io, // ? + Ext4ErrorCode::E2BIG => VfsError::InvalidInput, + Ext4ErrorCode::ENOMEM => VfsError::NoMemory, + Ext4ErrorCode::EACCES => VfsError::PermissionDenied, // ? + Ext4ErrorCode::EFAULT => VfsError::BadAddress, + Ext4ErrorCode::EEXIST => VfsError::AlreadyExists, + Ext4ErrorCode::ENODEV => VfsError::Io, // ? + Ext4ErrorCode::ENOTDIR => VfsError::NotADirectory, + Ext4ErrorCode::EISDIR => VfsError::IsADirectory, + Ext4ErrorCode::EINVAL => VfsError::InvalidData, + Ext4ErrorCode::EFBIG => VfsError::InvalidData, + Ext4ErrorCode::ENOSPC => VfsError::StorageFull, + Ext4ErrorCode::EROFS => VfsError::PermissionDenied, + Ext4ErrorCode::EMLINK => VfsError::Io, // ? + Ext4ErrorCode::ERANGE => VfsError::InvalidData, + Ext4ErrorCode::ENOTEMPTY => VfsError::DirectoryNotEmpty, + Ext4ErrorCode::ENODATA => VfsError::NotFound, // `NotFound` only for entry? + Ext4ErrorCode::ENOTSUP => VfsError::Io, // ? + Ext4ErrorCode::ELINKFAIL => VfsError::Io, // ? + Ext4ErrorCode::EALLOCFAIL => VfsError::StorageFull, // ? + } +} + +fn map_type(ext4_type: EXt4FileType) -> VfsNodeType { + match ext4_type { + EXt4FileType::RegularFile => VfsNodeType::File, + EXt4FileType::Directory => VfsNodeType::Dir, + EXt4FileType::CharacterDev => VfsNodeType::CharDevice, + EXt4FileType::BlockDev => VfsNodeType::BlockDevice, + EXt4FileType::Fifo => VfsNodeType::Fifo, + EXt4FileType::Socket => VfsNodeType::Socket, + EXt4FileType::SymLink => VfsNodeType::SymLink, + EXt4FileType::Unknown => VfsNodeType::File, + } +} + +fn map_type_inv(vfs_type: VfsNodeType) -> EXt4FileType { + match vfs_type { + VfsNodeType::File => EXt4FileType::RegularFile, + VfsNodeType::Dir => EXt4FileType::Directory, + VfsNodeType::CharDevice => EXt4FileType::CharacterDev, + VfsNodeType::BlockDevice => EXt4FileType::BlockDev, + VfsNodeType::Fifo => EXt4FileType::Fifo, + VfsNodeType::Socket => EXt4FileType::Socket, + VfsNodeType::SymLink => EXt4FileType::SymLink, + } +} + +fn map_perm(perm: Ext4InodeMode) -> VfsNodePerm { + let mut vfs_perm = VfsNodePerm::from_bits_truncate(0); + if perm.contains(Ext4InodeMode::USER_READ) { + vfs_perm |= VfsNodePerm::OWNER_READ; + } + if perm.contains(Ext4InodeMode::USER_WRITE) { + vfs_perm |= VfsNodePerm::OWNER_WRITE; + } + if perm.contains(Ext4InodeMode::USER_EXEC) { + vfs_perm |= VfsNodePerm::OWNER_EXEC; + } + if perm.contains(Ext4InodeMode::GROUP_READ) { + vfs_perm |= VfsNodePerm::GROUP_READ; + } + if perm.contains(Ext4InodeMode::GROUP_WRITE) { + vfs_perm |= VfsNodePerm::GROUP_WRITE; + } + if perm.contains(Ext4InodeMode::GROUP_EXEC) { + vfs_perm |= VfsNodePerm::GROUP_EXEC; + } + if perm.contains(Ext4InodeMode::OTHER_READ) { + vfs_perm |= VfsNodePerm::OTHER_READ; + } + if perm.contains(Ext4InodeMode::OTHER_WRITE) { + vfs_perm |= VfsNodePerm::OTHER_WRITE; + } + if perm.contains(Ext4InodeMode::OTHER_EXEC) { + vfs_perm |= VfsNodePerm::OTHER_EXEC; + } + vfs_perm +} diff --git a/modules/ruxfs/src/fs/ext4_rs.rs b/modules/ruxfs/src/fs/ext4_rs.rs new file mode 100644 index 000000000..d848be0ad --- /dev/null +++ b/modules/ruxfs/src/fs/ext4_rs.rs @@ -0,0 +1,423 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! Reference: +//! - ext4_rs: https://github.com/yuoo655/ext4_rs +//! - axfs: https://github.com/Starry-OS/axfs + +use crate::dev::Disk; +use alloc::sync::Arc; +use alloc::vec; +use alloc::vec::*; +use axfs_vfs::{RelPath, VfsDirEntry, VfsError, VfsNodePerm, VfsResult}; +use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; +use axsync::Mutex; +use core::cell::RefCell; +use ext4_rs::*; + +pub struct DiskAdapter { + inner: RefCell, +} + +unsafe impl Send for DiskAdapter {} +unsafe impl Sync for DiskAdapter {} + +// The io block size of the disk layer +const DISK_BLOCK_SIZE: usize = 512; + +// The block size of the file system +pub const BLOCK_SIZE: usize = 4096; + +impl BlockDevice for DiskAdapter { + fn read_offset(&self, offset: usize) -> Vec { + let mut disk = self.inner.borrow_mut(); + let mut buf = vec![0u8; BLOCK_SIZE]; + + let start_block_id = offset / DISK_BLOCK_SIZE; + let mut offset_in_block = offset % DISK_BLOCK_SIZE; + let mut total_bytes_read = 0; + + while total_bytes_read < buf.len() { + let current_block_id = start_block_id + (total_bytes_read / DISK_BLOCK_SIZE); + let bytes_to_copy = + (buf.len() - total_bytes_read).min(DISK_BLOCK_SIZE - offset_in_block); + + let block_data = disk.read_offset(current_block_id * DISK_BLOCK_SIZE + offset_in_block); + + buf[total_bytes_read..total_bytes_read + bytes_to_copy] + .copy_from_slice(&block_data[offset_in_block..offset_in_block + bytes_to_copy]); + + total_bytes_read += bytes_to_copy; + offset_in_block = 0; // After the first block, subsequent blocks read from the beginning + } + + buf + } + + fn write_offset(&self, offset: usize, buf: &[u8]) { + let mut disk = self.inner.borrow_mut(); + + let start_block_id = offset / DISK_BLOCK_SIZE; + let mut offset_in_block = offset % DISK_BLOCK_SIZE; + + let bytes_to_write = buf.len(); + let mut total_bytes_written = 0; + + while total_bytes_written < bytes_to_write { + let current_block_id = start_block_id + (total_bytes_written / DISK_BLOCK_SIZE); + let bytes_to_copy = + (bytes_to_write - total_bytes_written).min(DISK_BLOCK_SIZE - offset_in_block); + + let mut block_data = disk.read_offset(current_block_id * DISK_BLOCK_SIZE); + + block_data[offset_in_block..offset_in_block + bytes_to_copy] + .copy_from_slice(&buf[total_bytes_written..total_bytes_written + bytes_to_copy]); + + disk.write_offset(current_block_id * DISK_BLOCK_SIZE, &block_data) + .unwrap(); + + total_bytes_written += bytes_to_copy; + offset_in_block = 0; // After the first block, subsequent blocks start at the beginning + } + } +} + +pub struct Ext4FileSystem { + #[allow(unused)] + inner: Arc, + root_dir: VfsNodeRef, +} + +impl Ext4FileSystem { + pub fn new(disk: Disk) -> Self { + let block_device = Arc::new(DiskAdapter { + inner: RefCell::new(disk), + }); + let inner = Ext4::open(block_device); + let root = Arc::new(Ext4FileWrapper::new(inner.clone())); + Self { + inner: inner.clone(), + root_dir: root, + } + } +} + +impl VfsOps for Ext4FileSystem { + fn root_dir(&self) -> VfsNodeRef { + Arc::clone(&self.root_dir) + } + + fn umount(&self) -> VfsResult { + log::info!("umount:"); + // todo!() + Ok(()) + } +} + +pub struct Ext4FileWrapper { + ext4_file: Mutex, + ext4: Arc, +} + +unsafe impl Send for Ext4FileWrapper {} +unsafe impl Sync for Ext4FileWrapper {} + +impl Ext4FileWrapper { + fn new(ext4: Arc) -> Self { + Self { + ext4_file: Mutex::new(Ext4File::new()), + ext4: ext4, + } + } +} + +impl VfsNodeOps for Ext4FileWrapper { + /// Do something when the node is opened. + fn open(&self) -> VfsResult { + // log::info!("opening file"); + // let mut ext4_file = self.ext4_file.lock(); + // let r = self.ext4.ext4_open(&mut ext4_file, path, "r+", false); + Ok(()) + } + + /// Do something when the node is closed. + fn release(&self) -> VfsResult { + Ok(()) + } + + /// Get the attributes of the node. + fn get_attr(&self) -> VfsResult { + let ext4_file = self.ext4_file.lock(); + let root_inode_ref = + Ext4InodeRef::get_inode_ref(Arc::downgrade(&self.ext4).clone(), ext4_file.inode); + let inode_mode = root_inode_ref.inner.inode.mode; + let size = ext4_file.fsize; + // BLOCK_SIZE / DISK_BLOCK_SIZE + let blocks = root_inode_ref.inner.inode.blocks * 8; + let (ty, perm) = map_imode(inode_mode as u16); + drop(ext4_file); + Ok(VfsNodeAttr::new(perm, ty, size as _, blocks as _)) + } + + // file operations: + + /// Read data from the file at the given offset. + fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { + let mut ext4_file = self.ext4_file.lock(); + ext4_file.fpos = offset as usize; + + let read_len = buf.len(); + let mut read_cnt = 0; + + let r = self + .ext4 + .ext4_file_read(&mut ext4_file, buf, read_len, &mut read_cnt); + + if let Err(e) = r { + match e.error() { + Errnum::EINVAL => { + drop(ext4_file); + Ok(0) + } + _ => { + drop(ext4_file); + Err(VfsError::InvalidInput) + } + } + } else { + drop(ext4_file); + Ok(read_len) + } + } + + /// Write data to the file at the given offset. + fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { + let mut ext4_file = self.ext4_file.lock(); + ext4_file.fpos = offset as usize; + + let write_size = buf.len(); + + self.ext4.ext4_file_write(&mut ext4_file, &buf, write_size); + + Ok(write_size) + } + + /// Flush the file, synchronize the data to disk. + fn fsync(&self) -> VfsResult { + todo!() + } + + /// Truncate the file to the given size. + fn truncate(&self, _size: u64) -> VfsResult { + todo!() + } + + // directory operations: + + /// Get the parent directory of this directory. + /// + /// Return `None` if the node is a file. + fn parent(&self) -> Option { + None + } + + /// Lookup the node with given `path` in the directory. + /// + /// Return the node if found. + fn lookup(self: Arc, path: &RelPath) -> VfsResult { + let mut ext4_file = self.ext4_file.lock(); + let r = self.ext4.ext4_open(&mut ext4_file, path, "r+", false); + + if let Err(e) = r { + match e.error() { + Errnum::ENOENT => Err(VfsError::NotFound), + Errnum::EALLOCFIAL => Err(VfsError::InvalidInput), + Errnum::ELINKFIAL => Err(VfsError::InvalidInput), + + _ => Err(VfsError::InvalidInput), + } + } else { + drop(ext4_file); + // log::error!("file found"); + Ok(self.clone()) + } + } + + /// Create a new node with the given `path` in the directory + /// + /// Return [`Ok(())`](Ok) if it already exists. + fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { + let types = match ty { + VfsNodeType::Fifo => DirEntryType::EXT4_DE_FIFO, + VfsNodeType::CharDevice => DirEntryType::EXT4_DE_CHRDEV, + VfsNodeType::Dir => DirEntryType::EXT4_DE_DIR, + VfsNodeType::BlockDevice => DirEntryType::EXT4_DE_BLKDEV, + VfsNodeType::File => DirEntryType::EXT4_DE_REG_FILE, + VfsNodeType::SymLink => DirEntryType::EXT4_DE_SYMLINK, + VfsNodeType::Socket => DirEntryType::EXT4_DE_SOCK, + }; + + let mut ext4file = self.ext4_file.lock(); + + if types == DirEntryType::EXT4_DE_DIR { + let _ = self.ext4.ext4_dir_mk(path); + } else { + let _ = self.ext4.ext4_open(&mut ext4file, path, "w+", true); + } + + drop(ext4file); + + Ok(()) + } + + /// Remove the node with the given `path` in the directory. + fn unlink(&self, _path: &RelPath) -> VfsResult { + todo!() + } + + /// Read directory entries into `dirents`, starting from `start_idx`. + fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult { + let ext4_file = self.ext4_file.lock(); + let inode_num = ext4_file.inode; + let entries: Vec = self.ext4.read_dir_entry(inode_num as _); + let mut iter = entries.into_iter().skip(start_idx); + + for (i, out_entry) in dirents.iter_mut().enumerate() { + let x: Option = iter.next(); + match x { + Some(ext4direntry) => { + let name = ext4direntry.name; + let name_len = ext4direntry.name_len; + let file_type = unsafe { ext4direntry.inner.inode_type }; + let (ty, _) = map_dir_imode(file_type as u16); + let name = get_name(name, name_len as usize).unwrap(); + *out_entry = VfsDirEntry::new(name.as_str(), ty); + } + _ => return Ok(i), + } + } + + drop(ext4_file); + Ok(dirents.len()) + } + + /// Renames or moves existing file or directory. + fn rename(&self, _src_path: &RelPath, _dst_path: &RelPath) -> VfsResult { + todo!() + } + + fn as_any(&self) -> &dyn core::any::Any { + self as &dyn core::any::Any + } +} + +fn map_dir_imode(imode: u16) -> (VfsNodeType, VfsNodePerm) { + let diren_type = imode; + let type_code = ext4_rs::DirEntryType::from_bits(diren_type as u8).unwrap(); + let ty = match type_code { + DirEntryType::EXT4_DE_REG_FILE => VfsNodeType::File, + DirEntryType::EXT4_DE_DIR => VfsNodeType::Dir, + DirEntryType::EXT4_DE_CHRDEV => VfsNodeType::CharDevice, + DirEntryType::EXT4_DE_BLKDEV => VfsNodeType::BlockDevice, + DirEntryType::EXT4_DE_FIFO => VfsNodeType::Fifo, + DirEntryType::EXT4_DE_SOCK => VfsNodeType::Socket, + DirEntryType::EXT4_DE_SYMLINK => VfsNodeType::SymLink, + _ => { + // log::info!("{:x?}", imode); + VfsNodeType::File + } + }; + + let perm = ext4_rs::FileMode::from_bits_truncate(imode); + let mut vfs_perm = VfsNodePerm::from_bits_truncate(0); + + if perm.contains(ext4_rs::FileMode::S_IXOTH) { + vfs_perm |= VfsNodePerm::OTHER_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWOTH) { + vfs_perm |= VfsNodePerm::OTHER_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IROTH) { + vfs_perm |= VfsNodePerm::OTHER_READ; + } + + if perm.contains(ext4_rs::FileMode::S_IXGRP) { + vfs_perm |= VfsNodePerm::GROUP_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWGRP) { + vfs_perm |= VfsNodePerm::GROUP_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IRGRP) { + vfs_perm |= VfsNodePerm::GROUP_READ; + } + + if perm.contains(ext4_rs::FileMode::S_IXUSR) { + vfs_perm |= VfsNodePerm::OWNER_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWUSR) { + vfs_perm |= VfsNodePerm::OWNER_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IRUSR) { + vfs_perm |= VfsNodePerm::OWNER_READ; + } + + (ty, vfs_perm) +} + +fn map_imode(imode: u16) -> (VfsNodeType, VfsNodePerm) { + let file_type = (imode & 0xf000) as usize; + let ty = match file_type { + EXT4_INODE_MODE_FIFO => VfsNodeType::Fifo, + EXT4_INODE_MODE_CHARDEV => VfsNodeType::CharDevice, + EXT4_INODE_MODE_DIRECTORY => VfsNodeType::Dir, + EXT4_INODE_MODE_BLOCKDEV => VfsNodeType::BlockDevice, + EXT4_INODE_MODE_FILE => VfsNodeType::File, + EXT4_INODE_MODE_SOFTLINK => VfsNodeType::SymLink, + EXT4_INODE_MODE_SOCKET => VfsNodeType::Socket, + _ => { + // log::info!("{:x?}", imode); + VfsNodeType::File + } + }; + + let perm = ext4_rs::FileMode::from_bits_truncate(imode); + let mut vfs_perm = VfsNodePerm::from_bits_truncate(0); + + if perm.contains(ext4_rs::FileMode::S_IXOTH) { + vfs_perm |= VfsNodePerm::OTHER_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWOTH) { + vfs_perm |= VfsNodePerm::OTHER_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IROTH) { + vfs_perm |= VfsNodePerm::OTHER_READ; + } + + if perm.contains(ext4_rs::FileMode::S_IXGRP) { + vfs_perm |= VfsNodePerm::GROUP_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWGRP) { + vfs_perm |= VfsNodePerm::GROUP_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IRGRP) { + vfs_perm |= VfsNodePerm::GROUP_READ; + } + + if perm.contains(ext4_rs::FileMode::S_IXUSR) { + vfs_perm |= VfsNodePerm::OWNER_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWUSR) { + vfs_perm |= VfsNodePerm::OWNER_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IRUSR) { + vfs_perm |= VfsNodePerm::OWNER_READ; + } + + (ty, vfs_perm) +} diff --git a/modules/ruxfs/src/fs/fatfs.rs b/modules/ruxfs/src/fs/fatfs.rs index 3b27b7f6f..b9b04a3b8 100644 --- a/modules/ruxfs/src/fs/fatfs.rs +++ b/modules/ruxfs/src/fs/fatfs.rs @@ -8,14 +8,15 @@ */ use alloc::sync::Arc; +use axerrno::ax_err; +use axfs_vfs::RelPath; use core::cell::UnsafeCell; +use crate::dev::Disk; use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult}; use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; -use axsync::Mutex; use fatfs::{Dir, File, LossyOemCpConverter, NullTimeProvider, Read, Seek, SeekFrom, Write}; - -use crate::dev::Disk; +use spin::RwLock; const BLOCK_SIZE: usize = 512; @@ -24,7 +25,7 @@ pub struct FatFileSystem { root_dir: UnsafeCell>, } -pub struct FileWrapper<'a>(Mutex>); +pub struct FileWrapper<'a>(RwLock>); pub struct DirWrapper<'a>(Dir<'a, Disk, NullTimeProvider, LossyOemCpConverter>); unsafe impl Sync for FatFileSystem {} @@ -63,7 +64,7 @@ impl FatFileSystem { } fn new_file(file: File<'_, Disk, NullTimeProvider, LossyOemCpConverter>) -> Arc { - Arc::new(FileWrapper(Mutex::new(file))) + Arc::new(FileWrapper(RwLock::new(file))) } fn new_dir(dir: Dir<'_, Disk, NullTimeProvider, LossyOemCpConverter>) -> Arc { @@ -75,19 +76,24 @@ impl VfsNodeOps for FileWrapper<'static> { axfs_vfs::impl_vfs_non_dir_default! {} fn fsync(&self) -> VfsResult { - self.0.lock().flush().map_err(as_vfs_err) + self.0.write().flush().map_err(as_vfs_err) } fn get_attr(&self) -> VfsResult { - let size = self.0.lock().seek(SeekFrom::End(0)).map_err(as_vfs_err)?; + let size = self.0.write().seek(SeekFrom::End(0)).map_err(as_vfs_err)?; let blocks = (size + BLOCK_SIZE as u64 - 1) / BLOCK_SIZE as u64; // FAT fs doesn't support permissions, we just set everything to 755 let perm = VfsNodePerm::from_bits_truncate(0o755); - Ok(VfsNodeAttr::new(perm, VfsNodeType::File, size, blocks)) + + // WARN: Inode of files, for musl dynamic linker. + // WARN: there will be collision for files with the same size. + // TODO: implement real inode. + let ino = size + VfsNodeType::File as u64 | perm.bits() as u64; + Ok(VfsNodeAttr::new(ino, perm, VfsNodeType::File, size, blocks)) } fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { - let mut file = self.0.lock(); + let mut file = self.0.write(); file.seek(SeekFrom::Start(offset)).map_err(as_vfs_err)?; let mut total_read = 0; @@ -103,7 +109,7 @@ impl VfsNodeOps for FileWrapper<'static> { } fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { - let mut file = self.0.lock(); + let mut file = self.0.write(); file.seek(SeekFrom::Start(offset)).map_err(as_vfs_err)?; // TODO: more efficient let mut total_write = 0; @@ -119,7 +125,7 @@ impl VfsNodeOps for FileWrapper<'static> { } fn truncate(&self, size: u64) -> VfsResult { - let mut file = self.0.lock(); + let mut file = self.0.write(); file.seek(SeekFrom::Start(size)).map_err(as_vfs_err)?; // TODO: more efficient file.truncate().map_err(as_vfs_err) } @@ -129,8 +135,8 @@ impl VfsNodeOps for DirWrapper<'static> { axfs_vfs::impl_vfs_dir_default! {} fn get_attr(&self) -> VfsResult { - // FAT fs doesn't support permissions, we just set everything to 755 Ok(VfsNodeAttr::new( + 1, VfsNodePerm::from_bits_truncate(0o755), VfsNodeType::Dir, BLOCK_SIZE as u64, @@ -138,22 +144,21 @@ impl VfsNodeOps for DirWrapper<'static> { )) } + fn set_mode(&self, _mode: VfsNodePerm) -> VfsResult { + Ok(()) + } + fn parent(&self) -> Option { self.0 .open_dir("..") .map_or(None, |dir| Some(FatFileSystem::new_dir(dir))) } - fn lookup(self: Arc, path: &str) -> VfsResult { + fn lookup(self: Arc, path: &RelPath) -> VfsResult { debug!("lookup at fatfs: {}", path); - let path = path.trim_matches('/'); - if path.is_empty() || path == "." { + if path.is_empty() { return Ok(self.clone()); } - if let Some(rest) = path.strip_prefix("./") { - return self.lookup(rest); - } - if let Ok(Some(is_dir)) = self.0.check_path_type(path) { if is_dir { if let Ok(dir) = self.0.open_dir(path) { @@ -173,16 +178,11 @@ impl VfsNodeOps for DirWrapper<'static> { } } - fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult { + fn create(&self, path: &RelPath, ty: VfsNodeType, _mode: VfsNodePerm) -> VfsResult { debug!("create {:?} at fatfs: {}", ty, path); - let path = path.trim_matches('/'); - if path.is_empty() || path == "." { + if path.is_empty() { return Ok(()); } - if let Some(rest) = path.strip_prefix("./") { - return self.create(rest, ty); - } - match ty { VfsNodeType::File => { self.0.create_file(path).map_err(as_vfs_err)?; @@ -196,12 +196,10 @@ impl VfsNodeOps for DirWrapper<'static> { } } - fn remove(&self, path: &str) -> VfsResult { + fn unlink(&self, path: &RelPath) -> VfsResult { debug!("remove at fatfs: {}", path); - let path = path.trim_matches('/'); - assert!(!path.is_empty()); // already check at `root.rs` - if let Some(rest) = path.strip_prefix("./") { - return self.remove(rest); + if path.is_empty() { + return ax_err!(PermissionDenied); } self.0.remove(path).map_err(as_vfs_err) } @@ -227,7 +225,7 @@ impl VfsNodeOps for DirWrapper<'static> { Ok(dirents.len()) } - fn rename(&self, src_path: &str, dst_path: &str) -> VfsResult { + fn rename(&self, src_path: &RelPath, dst_path: &RelPath) -> VfsResult { // `src_path` and `dst_path` should in the same mounted fs debug!( "rename at fatfs, src_path: {}, dst_path: {}", diff --git a/modules/ruxfs/src/fs/lwext4_rust.rs b/modules/ruxfs/src/fs/lwext4_rust.rs new file mode 100644 index 000000000..0fb4f11ac --- /dev/null +++ b/modules/ruxfs/src/fs/lwext4_rust.rs @@ -0,0 +1,421 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! Reference: +//! - lwext4_rust: https://github.com/elliott10/lwext4_rust +//! - axfs: https://github.com/Starry-OS/axfs + +use crate::alloc::string::String; +use alloc::sync::Arc; +use axerrno::AxError; +use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult, RelPath}; +use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; +use axsync::Mutex; +use lwext4_rust::bindings::{ + O_CREAT, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, SEEK_CUR, SEEK_END, SEEK_SET, +}; +use lwext4_rust::{Ext4BlockWrapper, Ext4File, InodeTypes, KernelDevOp}; + +use crate::dev::Disk; +pub const BLOCK_SIZE: usize = 512; + +#[allow(dead_code)] +pub struct Ext4FileSystem { + inner: Ext4BlockWrapper, + root: VfsNodeRef, +} + +unsafe impl Sync for Ext4FileSystem {} +unsafe impl Send for Ext4FileSystem {} + +impl Ext4FileSystem { + #[cfg(feature = "use-ramdisk")] + pub fn new(mut disk: Disk) -> Self { + unimplemented!() + } + + #[cfg(not(feature = "use-ramdisk"))] + pub fn new(disk: Disk) -> Self { + info!( + "Got Disk size:{}, position:{}", + disk.size(), + disk.position() + ); + let inner = + Ext4BlockWrapper::::new(disk).expect("failed to initialize EXT4 filesystem"); + let root = Arc::new(FileWrapper::new("/", InodeTypes::EXT4_DE_DIR)); + Self { inner, root } + } +} + +/// The [`VfsOps`] trait provides operations on a filesystem. +impl VfsOps for Ext4FileSystem { + // mount() + + fn root_dir(&self) -> VfsNodeRef { + debug!("Get root_dir"); + //let root_dir = unsafe { (*self.root.get()).as_ref().unwrap() }; + Arc::clone(&self.root) + } +} + +pub struct FileWrapper(Mutex); + +unsafe impl Send for FileWrapper {} +unsafe impl Sync for FileWrapper {} + +impl FileWrapper { + fn new(path: &str, types: InodeTypes) -> Self { + info!("FileWrapper new {:?} {}", types, path); + //file.file_read_test("/test/test.txt", &mut buf); + + Self(Mutex::new(Ext4File::new(path, types))) + } + + fn path_deal_with(&self, path: &str) -> String { + if path.starts_with('/') { + warn!("path_deal_with: {}", path); + } + let p = path.trim_matches('/'); // 首尾去除 + if p.is_empty() || p == "." { + return String::new(); + } + + if let Some(rest) = p.strip_prefix("./") { + //if starts with "./" + return self.path_deal_with(rest); + } + let rest_p = p.replace("//", "/"); + if p != rest_p { + return self.path_deal_with(&rest_p); + } + + //Todo ? ../ + //注:lwext4创建文件必须提供文件path的绝对路径 + let file = self.0.lock(); + let path = file.get_path(); + let fpath = String::from(path.to_str().unwrap().trim_end_matches('/')) + "/" + p; + info!("dealt with full path: {}", fpath.as_str()); + fpath + } +} + +/// The [`VfsNodeOps`] trait provides operations on a file or a directory. +impl VfsNodeOps for FileWrapper { + fn get_attr(&self) -> VfsResult { + let mut file = self.0.lock(); + + let perm = file.file_mode_get().unwrap_or(0o755); + let perm = VfsNodePerm::from_bits_truncate((perm as u16) & 0o777); + + let vtype = file.file_type_get(); + let vtype = match vtype { + InodeTypes::EXT4_INODE_MODE_FIFO => VfsNodeType::Fifo, + InodeTypes::EXT4_INODE_MODE_CHARDEV => VfsNodeType::CharDevice, + InodeTypes::EXT4_INODE_MODE_DIRECTORY => VfsNodeType::Dir, + InodeTypes::EXT4_INODE_MODE_BLOCKDEV => VfsNodeType::BlockDevice, + InodeTypes::EXT4_INODE_MODE_FILE => VfsNodeType::File, + InodeTypes::EXT4_INODE_MODE_SOFTLINK => VfsNodeType::SymLink, + InodeTypes::EXT4_INODE_MODE_SOCKET => VfsNodeType::Socket, + _ => { + warn!("unknown file type: {:?}", vtype); + VfsNodeType::File + } + }; + + let size = if vtype == VfsNodeType::File { + let path = file.get_path(); + let path = path.to_str().unwrap(); + file.file_open(path, O_RDONLY) + .map_err(|e| >::try_into(e).unwrap())?; + let fsize = file.file_size(); + let _ = file.file_close(); + fsize + } else { + 0 // DIR size ? + }; + let blocks = (size + (BLOCK_SIZE as u64 - 1)) / BLOCK_SIZE as u64; + + info!( + "get_attr of {:?} {:?}, size: {}, blocks: {}", + vtype, + file.get_path(), + size, + blocks + ); + + Ok(VfsNodeAttr::new(0, perm, vtype, size, blocks)) + } + + fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { + info!("create {:?} on Ext4fs: {}", ty, path); + let fpath = self.path_deal_with(path); + let fpath = fpath.as_str(); + if fpath.is_empty() { + return Ok(()); + } + + let types = match ty { + VfsNodeType::Fifo => InodeTypes::EXT4_DE_FIFO, + VfsNodeType::CharDevice => InodeTypes::EXT4_DE_CHRDEV, + VfsNodeType::Dir => InodeTypes::EXT4_DE_DIR, + VfsNodeType::BlockDevice => InodeTypes::EXT4_DE_BLKDEV, + VfsNodeType::File => InodeTypes::EXT4_DE_REG_FILE, + VfsNodeType::SymLink => InodeTypes::EXT4_DE_SYMLINK, + VfsNodeType::Socket => InodeTypes::EXT4_DE_SOCK, + }; + + let mut file = self.0.lock(); + if file.check_inode_exist(fpath, types.clone()) { + Ok(()) + } else { + if types == InodeTypes::EXT4_DE_DIR { + file.dir_mk(fpath) + .map(|_v| ()) + .map_err(|e| e.try_into().unwrap()) + } else { + file.file_open(fpath, O_WRONLY | O_CREAT | O_TRUNC) + .expect("create file failed"); + file.file_close() + .map(|_v| ()) + .map_err(|e| e.try_into().unwrap()) + } + } + } + + fn unlink(&self, path: &RelPath) -> VfsResult { + info!("remove ext4fs: {}", path); + let fpath = self.path_deal_with(path); + let fpath = fpath.as_str(); + + assert!(!fpath.is_empty()); // already check at `root.rs` + + let mut file = self.0.lock(); + if file.check_inode_exist(fpath, InodeTypes::EXT4_DE_DIR) { + // Recursive directory remove + file.dir_rm(fpath) + .map(|_v| ()) + .map_err(|e| e.try_into().unwrap()) + } else { + file.file_remove(fpath) + .map(|_v| ()) + .map_err(|e| e.try_into().unwrap()) + } + } + + /// Get the parent directory of this directory. + /// Return `None` if the node is a file. + fn parent(&self) -> Option { + let file = self.0.lock(); + if file.get_type() == InodeTypes::EXT4_DE_DIR { + let path = file.get_path(); + let path = path.to_str().unwrap(); + info!("Get the parent dir of {}", path); + let path = path.trim_end_matches('/').trim_end_matches(|c| c != '/'); + if !path.is_empty() { + return Some(Arc::new(Self::new(path, InodeTypes::EXT4_DE_DIR))); + } + } + None + } + + /// Read directory entries into `dirents`, starting from `start_idx`. + fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult { + let file = self.0.lock(); + let (name, inode_type) = file.lwext4_dir_entries().unwrap(); + + let mut name_iter = name.iter().skip(start_idx); + let mut inode_type_iter = inode_type.iter().skip(start_idx); + + for (i, out_entry) in dirents.iter_mut().enumerate() { + let iname = name_iter.next(); + let itypes = inode_type_iter.next(); + + match itypes { + Some(t) => { + let ty = if *t == InodeTypes::EXT4_DE_DIR { + VfsNodeType::Dir + } else if *t == InodeTypes::EXT4_DE_REG_FILE { + VfsNodeType::File + } else if *t == InodeTypes::EXT4_DE_SYMLINK { + VfsNodeType::SymLink + } else { + error!("unknown file type: {:?}", itypes); + unreachable!() + }; + + *out_entry = + VfsDirEntry::new(core::str::from_utf8(iname.unwrap()).unwrap(), ty); + } + _ => return Ok(i), + } + } + + Ok(dirents.len()) + } + + /// Lookup the node with given `path` in the directory. + /// Return the node if found. + fn lookup(self: Arc, path: &RelPath) -> VfsResult { + debug!("lookup ext4fs: {:?}, {}", self.0.lock().get_path(), path); + + let fpath = self.path_deal_with(path); + let fpath = fpath.as_str(); + if fpath.is_empty() { + return Ok(self.clone()); + } + + ///////// + let mut file = self.0.lock(); + if file.check_inode_exist(fpath, InodeTypes::EXT4_DE_DIR) { + debug!("lookup new DIR FileWrapper"); + Ok(Arc::new(Self::new(fpath, InodeTypes::EXT4_DE_DIR))) + } else if file.check_inode_exist(fpath, InodeTypes::EXT4_DE_REG_FILE) { + debug!("lookup new FILE FileWrapper"); + Ok(Arc::new(Self::new(fpath, InodeTypes::EXT4_DE_REG_FILE))) + } else { + Err(VfsError::NotFound) + } + } + + fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { + info!("To read_at {}, buf len={}", offset, buf.len()); + let mut file = self.0.lock(); + let path = file.get_path(); + let path = path.to_str().unwrap(); + file.file_open(path, O_RDONLY) + .map_err(|e| >::try_into(e).unwrap())?; + + file.file_seek(offset as i64, SEEK_SET) + .map_err(|e| >::try_into(e).unwrap())?; + let r = file.file_read(buf); + + let _ = file.file_close(); + r.map_err(|e| e.try_into().unwrap()) + } + + fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { + info!("To write_at {}, buf len={}", offset, buf.len()); + let mut file = self.0.lock(); + let path = file.get_path(); + let path = path.to_str().unwrap(); + file.file_open(path, O_RDWR) + .map_err(|e| >::try_into(e).unwrap())?; + + file.file_seek(offset as i64, SEEK_SET) + .map_err(|e| >::try_into(e).unwrap())?; + let r = file.file_write(buf); + + let _ = file.file_close(); + r.map_err(|e| e.try_into().unwrap()) + } + + fn truncate(&self, size: u64) -> VfsResult { + info!("truncate file to size={}", size); + let mut file = self.0.lock(); + let path = file.get_path(); + let path = path.to_str().unwrap(); + file.file_open(path, O_RDWR | O_CREAT | O_TRUNC) + .map_err(|e| >::try_into(e).unwrap())?; + + let t = file.file_truncate(size); + + let _ = file.file_close(); + t.map(|_v| ()).map_err(|e| e.try_into().unwrap()) + } + + fn rename(&self, src_path: &RelPath, dst_path: &RelPath) -> VfsResult { + info!("rename from {} to {}", src_path, dst_path); + let mut file = self.0.lock(); + file.file_rename(src_path, dst_path) + .map(|_v| ()) + .map_err(|e| e.try_into().unwrap()) + } + + fn as_any(&self) -> &dyn core::any::Any { + self as &dyn core::any::Any + } +} + +impl Drop for FileWrapper { + fn drop(&mut self) { + let mut file = self.0.lock(); + debug!("Drop struct FileWrapper {:?}", file.get_path()); + file.file_close().expect("failed to close fd"); + drop(file); // todo + } +} + +impl KernelDevOp for Disk { + //type DevType = Box; + type DevType = Disk; + + fn read(dev: &mut Disk, mut buf: &mut [u8]) -> Result { + debug!("READ block device buf={}", buf.len()); + let mut read_len = 0; + while !buf.is_empty() { + match dev.read_one(buf) { + Ok(0) => break, + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + read_len += n; + } + Err(_e) => return Err(-1), + } + } + debug!("READ rt len={}", read_len); + Ok(read_len) + } + fn write(dev: &mut Self::DevType, mut buf: &[u8]) -> Result { + debug!("WRITE block device buf={}", buf.len()); + let mut write_len = 0; + while !buf.is_empty() { + match dev.write_one(buf) { + Ok(0) => break, + Ok(n) => { + buf = &buf[n..]; + write_len += n; + } + Err(_e) => return Err(-1), + } + } + debug!("WRITE rt len={}", write_len); + Ok(write_len) + } + fn flush(_dev: &mut Self::DevType) -> Result { + Ok(0) + } + fn seek(dev: &mut Disk, off: i64, whence: i32) -> Result { + let size = dev.size(); + debug!( + "SEEK block device size:{}, pos:{}, offset={}, whence={}", + size, + &dev.position(), + off, + whence + ); + let new_pos = match whence as u32 { + SEEK_SET => Some(off), + SEEK_CUR => dev.position().checked_add_signed(off).map(|v| v as i64), + SEEK_END => size.checked_add_signed(off).map(|v| v as i64), + _ => { + error!("invalid seek() whence: {}", whence); + Some(off) + } + } + .ok_or(-1)?; + + if new_pos as u64 > size { + warn!("Seek beyond the end of the block device"); + } + dev.set_position(new_pos as u64); + Ok(new_pos) + } +} \ No newline at end of file diff --git a/modules/ruxfs/src/fs/mod.rs b/modules/ruxfs/src/fs/mod.rs index 1e976b9a2..f54ad2e72 100644 --- a/modules/ruxfs/src/fs/mod.rs +++ b/modules/ruxfs/src/fs/mod.rs @@ -12,6 +12,13 @@ cfg_if::cfg_if! { pub mod myfs; } else if #[cfg(feature = "fatfs")] { pub mod fatfs; + // TODO: wait for CI support for ext4 + // } else if #[cfg(feature = "lwext4_rust")] { + // pub mod lwext4_rust; + } else if #[cfg(feature = "ext4_rs")] { + pub mod ext4_rs; + } else if #[cfg(feature = "another_ext4")] { + pub mod another_ext4; } } diff --git a/modules/ruxfs/src/fs/myfs.rs b/modules/ruxfs/src/fs/myfs.rs index e1d122273..150f58b90 100644 --- a/modules/ruxfs/src/fs/myfs.rs +++ b/modules/ruxfs/src/fs/myfs.rs @@ -7,19 +7,34 @@ * See the Mulan PSL v2 for more details. */ -use crate::dev::Disk; use alloc::sync::Arc; use axfs_vfs::VfsOps; -/// The interface to define custom filesystems in user apps. +#[cfg(feature = "blkfs")] +use crate::dev::Disk; + +/// The interface to define custom filesystems in user apps (using block device). +#[cfg(feature = "blkfs")] #[crate_interface::def_interface] pub trait MyFileSystemIf { /// Creates a new instance of the filesystem with initialization. - /// - /// TODO: use generic disk type fn new_myfs(disk: Disk) -> Arc; } +/// The interface to define custom filesystems in user apps (without block device). +#[cfg(not(feature = "blkfs"))] +#[crate_interface::def_interface] +pub trait MyFileSystemIf { + /// Creates a new instance of the filesystem with initialization. + fn new_myfs() -> Arc; +} + +#[cfg(feature = "blkfs")] pub(crate) fn new_myfs(disk: Disk) -> Arc { crate_interface::call_interface!(MyFileSystemIf::new_myfs(disk)) } + +#[cfg(not(feature = "blkfs"))] +pub(crate) fn new_myfs() -> Arc { + crate_interface::call_interface!(MyFileSystemIf::new_myfs()) +} diff --git a/modules/ruxfs/src/fuse_st.rs b/modules/ruxfs/src/fuse_st.rs new file mode 100644 index 000000000..4e034f5cb --- /dev/null +++ b/modules/ruxfs/src/fuse_st.rs @@ -0,0 +1,2862 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! fuse protocol definitions + +#![allow(dead_code)] + +use alloc::fmt::Debug; +use alloc::fmt::Error; +use alloc::fmt::Formatter; +use alloc::string::String; +use axfs_vfs::VfsNodeType; + +/// FUSE operation codes. +#[derive(Debug, Clone, Copy)] +pub enum FuseOpcode { + /// lookup a file by name + FuseLookup = 1, + /// forget a file, no reply + FuseForget = 2, + /// get file attributes + FuseGetattr = 3, + /// set file attributes + FuseSetattr = 4, + /// read a symbolic link + FuseReadlink = 5, + /// create a symbolic link + FuseSymlink = 6, + /// create a file + FuseMknod = 8, + /// create a directory + FuseMkdir = 9, + /// remove a file + FuseUnlink = 10, + /// remove a directory + FuseRmdir = 11, + /// rename a file or directory + FuseRename = 12, + /// create a hard link + FuseLink = 13, + /// open a file + FuseOpen = 14, + /// read a file + FuseRead = 15, + /// write a file + FuseWrite = 16, + /// get file system statistics + FuseStatfs = 17, + /// release a file + FuseRelease = 18, + /// release a file with flags + FuseFsync = 20, + /// set extended attributes + FuseSetxattr = 21, + /// get extended attributes + FuseGetxattr = 22, + /// list extended attributes + FuseListxattr = 23, + /// remove an extended attribute + FuseRemovexattr = 24, + /// flush a file + FuseFlush = 25, + /// initialize the FUSE file system + FuseInit = 26, + /// open a directory + FuseOpendir = 27, + /// read a directory + FuseReaddir = 28, + /// release a directory + FuseReleasedir = 29, + /// synchronize a directory + FuseFsyncdir = 30, + /// get or set file locks + FuseGetlk = 31, + /// set file locks + FuseSetlk = 32, + /// set file locks with wait + FuseSetlkw = 33, + /// access a file + FuseAccess = 34, + /// create a file with specific flags + FuseCreate = 35, + /// handle an interrupt + FuseInterrupt = 36, + /// handle a file system notification + FuseBmap = 37, + /// destroy the FUSE file system + FuseDestroy = 38, + /// perform an ioctl operation + FuseIoctl = 39, + /// poll for file system events + FusePoll = 40, + /// reply to a notification + FuseNotifyReply = 41, + /// batch forget files + FuseBatchForget = 42, + /// allocate space for a file + FuseFallocate = 43, + /// read directory with plus + FuseReaddirplus = 44, + /// rename a file or directory with flags + FuseRename2 = 45, + /// seek within a file + FuseLseek = 46, + /// copy a file range + FuseCopyFileRange = 47, + /// set up a mapping for a file + FuseSetupmapping = 48, + /// remove a mapping for a file + FuseRemovemapping = 49, + /// synchronize file system state + FuseSyncfs = 50, + /// create a temporary file + FuseTmpfile = 51, +} + +/// FUSE file open flags. +pub mod fuse_open_flags { + /// Open a file with direct I/O. + pub const FOPEN_DIRECT_IO: u32 = 1 << 0; + /// Keep the file's cache. + pub const FOPEN_KEEP_CACHE: u32 = 1 << 1; + /// Open a file that is not seekable. + pub const FOPEN_NONSEEKABLE: u32 = 1 << 2; + /// Open a file in a cache directory. + pub const FOPEN_CACHE_DIR: u32 = 1 << 3; + /// Open a file as a stream. + pub const FOPEN_STREAM: u32 = 1 << 4; + /// Do not flush the file after writing. + pub const FOPEN_NOFLUSH: u32 = 1 << 5; + /// Allow parallel direct writes to the file. + pub const FOPEN_PARALLEL_DIRECT_WRITES: u32 = 1 << 6; +} + +/// FUSE file attribute bitmasks for fuse_setattr_in.valid +pub mod fuse_setattr_bitmasks { + /// Bitmask for file mode + pub const FATTR_MODE: u32 = 1 << 0; + /// Bitmask for user ID + pub const FATTR_UID: u32 = 1 << 1; + /// Bitmask for group ID + pub const FATTR_GID: u32 = 1 << 2; + /// Bitmask for file size + pub const FATTR_SIZE: u32 = 1 << 3; + /// Bitmask for access time + pub const FATTR_ATIME: u32 = 1 << 4; + /// Bitmask for modification time + pub const FATTR_MTIME: u32 = 1 << 5; + /// Bitmask for file handle + pub const FATTR_FH: u32 = 1 << 6; + /// Bitmask for file attributes + pub const FATTR_ATIME_NOW: u32 = 1 << 7; + /// Bitmask for modification time + pub const FATTR_MTIME_NOW: u32 = 1 << 8; + /// Bitmask for lock owner + pub const FATTR_LOCKOWNER: u32 = 1 << 9; + /// Bitmask for change time + pub const FATTR_CTIME: u32 = 1 << 10; + /// Bitmask for killing setuid/setgid bits + pub const FATTR_KILL_SUIDGID: u32 = 1 << 11; +} + +/// FUSE file system initialization flags +pub mod fuse_init_flags { + /// FUSE file system supports asynchronous reads + pub const FUSE_ASYNC_READ: u32 = 1 << 0; + /// FUSE file system supports POSIX locks + pub const FUSE_POSIX_LOCKS: u32 = 1 << 1; + /// FUSE file system supports file operations + pub const FUSE_FILE_OPS: u32 = 1 << 2; + /// FUSE file system supports atomic open with truncation + pub const FUSE_ATOMIC_O_TRUNC: u32 = 1 << 3; + /// FUSE file system supports export + pub const FUSE_EXPORT_SUPPORT: u32 = 1 << 4; + /// FUSE file system supports big writes + pub const FUSE_BIG_WRITES: u32 = 1 << 5; + /// FUSE file system does not mask file operations + pub const FUSE_DONT_MASK: u32 = 1 << 6; + /// FUSE file system supports splice write operations + pub const FUSE_SPLICE_WRITE: u32 = 1 << 7; + /// FUSE file system supports splice move operations + pub const FUSE_SPLICE_MOVE: u32 = 1 << 8; + /// FUSE file system supports splice read operations + pub const FUSE_SPLICE_READ: u32 = 1 << 9; + /// FUSE file system supports flock locks + pub const FUSE_FLOCK_LOCKS: u32 = 1 << 10; + /// FUSE file system supports ioctl operations in the directory + pub const FUSE_HAS_IOCTL_DIR: u32 = 1 << 11; + /// FUSE file system automatically invalidates data + pub const FUSE_AUTO_INVAL_DATA: u32 = 1 << 12; + /// FUSE file system supports readdirplus operations + pub const FUSE_DO_READDIRPLUS: u32 = 1 << 13; + /// FUSE file system supports readdirplus operations automatically + pub const FUSE_READDIRPLUS_AUTO: u32 = 1 << 14; + /// FUSE file system supports asynchronous direct I/O + pub const FUSE_ASYNC_DIO: u32 = 1 << 15; + /// FUSE file system supports writeback cache + pub const FUSE_WRITEBACK_CACHE: u32 = 1 << 16; + /// FUSE file system does not support open operations + pub const FUSE_NO_OPEN_SUPPORT: u32 = 1 << 17; + /// FUSE file system supports parallel directory operations + pub const FUSE_PARALLEL_DIROPS: u32 = 1 << 18; + /// FUSE file system supports killpriv operations + pub const FUSE_HANDLE_KILLPRIV: u32 = 1 << 19; + /// FUSE file system supports POSIX ACLs + pub const FUSE_POSIX_ACL: u32 = 1 << 20; + /// FUSE file system supports abort operations + pub const FUSE_ABORT_ERROR: u32 = 1 << 21; + /// FUSE file system supports a maximum of 4GB pages + pub const FUSE_MAX_PAGES: u32 = 1 << 22; + /// FUSE file system supports symlink caching + pub const FUSE_CACHE_SYMLINKS: u32 = 1 << 23; + /// FUSE file system does not support opendir operations + pub const FUSE_NO_OPENDIR_SUPPORT: u32 = 1 << 24; + /// FUSE file system explicitly invalidates data + pub const FUSE_EXPLICIT_INVAL_DATA: u32 = 1 << 25; + /// FUSE file system supports file handle caching + pub const FUSE_MAP_ALIGNMENT: u32 = 1 << 26; + /// FUSE file system supports submounts + pub const FUSE_SUBMOUNTS: u32 = 1 << 27; + /// FUSE file system supports killpriv operations in version 2 + pub const FUSE_HANDLE_KILLPRIV_V2: u32 = 1 << 28; + /// FUSE file system supports extended attributes + pub const FUSE_SETXATTR_EXT: u32 = 1 << 29; + /// FUSE file system supports extended initialization + pub const FUSE_INIT_EXT: u32 = 1 << 30; + /// FUSE file system supports reserved flags + pub const FUSE_INIT_RESERVED: u32 = 1 << 31; + /// FUSE file system supports file system security context + pub const FUSE_SECURITY_CTX: u64 = 1 << 32; + /// FUSE file system supports DAX (Direct Access) inodes + pub const FUSE_HAS_INODE_DAX: u64 = 1 << 33; + /// FUSE file system supports group creation + pub const FUSE_CREATE_SUPP_GROUP: u64 = 1 << 34; +} + +/// FUSE file system flags +pub mod release_flags { + /// FUSE release flags for flushing data + pub const FUSE_RELEASE_FLUSH: u32 = 1 << 0; + /// FUSE release flags for unlocking file locks + pub const FUSE_RELEASE_FLOCK_UNLOCK: u32 = 1 << 1; +} + +/// FUSE getattr flags +pub mod getattr_flags { + /// FUSE getattr flags for file handle + pub const FUSE_GETATTR_FH: u32 = 1 << 0; +} + +/// FUSE write flags +pub mod write_flags { + /// FUSE write flags for caching + pub const FUSE_WRITE_CACHE: u32 = 1 << 0; + /// FUSE write flags for locking + pub const FUSE_WRITE_LOCKOWNER: u32 = 1 << 1; + /// FUSE write flags for killing setuid/setgid bits + pub const FUSE_WRITE_KILL_SUIDGID: u32 = 1 << 2; +} + +/// FUSE read flags +pub mod read_flags { + /// FUSE read flags for direct I/O + pub const FUSE_READ_LOCKOWNER: u32 = 1 << 1; +} + +/// FUSE request header +#[derive(Debug, Clone, Copy)] +pub struct FuseInHeader { + // 40 bytes + len: u32, // length of the request = sizeof(fuse_in_header) = 32 + opcode: u32, // eg. FUSE_GETATTR = 3 + unique: u64, // unique request ID + nodeid: u64, // inode number + uid: u32, // user ID + gid: u32, // group ID + pid: u32, // process ID + padding: u32, // padding +} + +impl FuseInHeader { + /// Create a new FuseInHeader. + pub fn new( + len: u32, + opcode: u32, + unique: u64, + nodeid: u64, + uid: u32, + gid: u32, + pid: u32, + ) -> Self { + Self { + len, + opcode, + unique, + nodeid, + uid, + gid, + pid, + padding: 0, + } + } + + /// Set the length of the request. + pub fn set_len(&mut self, len: u32) { + self.len = len; + } + + /// Set the opcode of the request + pub fn set_opcode(&mut self, opcode: u32) { + self.opcode = opcode; + } + + /// Set the unique request ID + pub fn set_unique(&mut self, unique: u64) { + self.unique = unique; + } + + /// Set the inode number + pub fn set_nodeid(&mut self, nodeid: u64) { + self.nodeid = nodeid; + } + + /// Set the user ID + pub fn set_uid(&mut self, uid: u32) { + self.uid = uid; + } + + /// Set the group ID + pub fn set_gid(&mut self, gid: u32) { + self.gid = gid; + } + + /// Set the process ID + pub fn set_pid(&mut self, pid: u32) { + self.pid = pid; + } + + /// Print the FuseInHeader + pub fn print(&self) { + debug!("FuseInHeader: len: {:?}, opcode: {:?}, unique: {:?}, nodeid: {:?}, uid: {:?}, gid: {:?}, pid: {:?}, padding: {:?}", self.len, self.opcode, self.unique, self.nodeid, self.uid, self.gid, self.pid, self.padding); + } + + /// Write the FuseInHeader to a buffer + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.len.to_le_bytes()); + buf[4..8].copy_from_slice(&self.opcode.to_le_bytes()); + buf[8..16].copy_from_slice(&self.unique.to_le_bytes()); + buf[16..24].copy_from_slice(&self.nodeid.to_le_bytes()); + buf[24..28].copy_from_slice(&self.uid.to_le_bytes()); + buf[28..32].copy_from_slice(&self.gid.to_le_bytes()); + buf[32..36].copy_from_slice(&self.pid.to_le_bytes()); + buf[36..40].copy_from_slice(&self.padding.to_le_bytes()); + } +} + +/// FUSE response header +#[derive(Debug, Clone, Copy)] +pub struct FuseOutHeader { + // 16 bytes + len: u32, // length of the response + error: i32, // error code + unique: u64, // unique request ID +} + +impl FuseOutHeader { + /// Create a new FuseOutHeader. + pub fn new(len: u32, error: i32, unique: u64) -> Self { + Self { len, error, unique } + } + + /// Read a FuseOutHeader from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + len: u32::from_le_bytes(buf[0..4].try_into().unwrap()), + error: i32::from_le_bytes(buf[4..8].try_into().unwrap()), + unique: u64::from_le_bytes(buf[8..16].try_into().unwrap()), + } + } + + /// Check if the response is successful (error code is 0). + pub fn is_ok(&self) -> bool { + self.error == 0 + } + + /// Get the length of the response. + pub fn get_len(&self) -> u32 { + self.len + } + + /// Get the error code of the response. + pub fn error(&self) -> i32 { + self.error + } + + /// Get the unique request ID of the response. + pub fn get_unique(&self) -> u64 { + self.unique + } + + /// Set the length of the response. + pub fn print(&self) { + debug!( + "fuse_out_header: len: {:?}, error: {:?}, unique: {:?}", + self.len, self.error, self.unique + ); + } + + /// Write the FuseOutHeader to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.len.to_le_bytes()); + buf[4..8].copy_from_slice(&self.error.to_le_bytes()); + buf[8..16].copy_from_slice(&self.unique.to_le_bytes()); + } +} + +/// FUSE initialization request input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseInitIn { + // 64 bytes + major: u32, + minor: u32, + max_readahead: u32, + flags: u32, + flags2: u32, + unused: [u32; 11], +} + +impl FuseInitIn { + /// Create a new FuseInitIn structure. + pub fn new( + major: u32, + minor: u32, + max_readahead: u32, + flags: u32, + flags2: u32, + unused: [u32; 11], + ) -> Self { + Self { + major, + minor, + max_readahead, + flags, + flags2, + unused, + } + } + + /// Print the FuseInitIn structure. + pub fn print(&self) { + debug!("FuseInitIn: major: {:?}, minor: {:?}, max_readahead: {:#x}, flags: {:#x}, flags2: {:?}, unused: {:?}", self.major, self.minor, self.max_readahead, self.flags, self.flags2, self.unused); + } + + /// Write the FuseInitIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.major.to_le_bytes()); + buf[4..8].copy_from_slice(&self.minor.to_le_bytes()); + buf[8..12].copy_from_slice(&self.max_readahead.to_le_bytes()); + buf[12..16].copy_from_slice(&self.flags.to_le_bytes()); + buf[16..20].copy_from_slice(&self.flags2.to_le_bytes()); + for (i, &val) in self.unused.iter().enumerate() { + buf[20 + i * 4..24 + i * 4].copy_from_slice(&val.to_le_bytes()); + } + } +} + +/// FUSE initialization response output structure +#[derive(Debug, Clone, Copy)] +pub struct FuseInitOut { + // 64 bytes + major: u32, + minor: u32, + max_readahead: u32, + flags: u32, + max_background: u16, + congestion_threshold: u16, + max_write: u32, + time_gran: u32, + max_pages: u16, + map_alignment: u16, + flags2: u32, + unused: [u32; 7], +} + +impl FuseInitOut { + /// Create a new FuseInitOut structure. + #[allow(clippy::too_many_arguments)] + pub fn new( + major: u32, + minor: u32, + max_readahead: u32, + flags: u32, + max_background: u16, + congestion_threshold: u16, + max_write: u32, + time_gran: u32, + max_pages: u16, + map_alignment: u16, + flags2: u32, + unused: [u32; 7], + ) -> Self { + Self { + major, + minor, + max_readahead, + flags, + max_background, + congestion_threshold, + max_write, + time_gran, + max_pages, + map_alignment, + flags2, + unused, + } + } + + /// Read a FuseInitOut structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + trace!("fuseinitout from len: {:?}, buf: {:?}", buf.len(), buf); + Self { + major: u32::from_le_bytes(buf[0..4].try_into().unwrap()), + minor: u32::from_le_bytes(buf[4..8].try_into().unwrap()), + max_readahead: u32::from_le_bytes(buf[8..12].try_into().unwrap()), + flags: u32::from_le_bytes(buf[12..16].try_into().unwrap()), + max_background: u16::from_le_bytes(buf[16..18].try_into().unwrap()), + congestion_threshold: u16::from_le_bytes(buf[18..20].try_into().unwrap()), + max_write: u32::from_le_bytes(buf[20..24].try_into().unwrap()), + time_gran: u32::from_le_bytes(buf[24..28].try_into().unwrap()), + max_pages: u16::from_le_bytes(buf[28..30].try_into().unwrap()), + map_alignment: u16::from_le_bytes(buf[30..32].try_into().unwrap()), + flags2: u32::from_le_bytes(buf[32..36].try_into().unwrap()), + unused: [ + u32::from_le_bytes(buf[36..40].try_into().unwrap()), + u32::from_le_bytes(buf[40..44].try_into().unwrap()), + u32::from_le_bytes(buf[44..48].try_into().unwrap()), + u32::from_le_bytes(buf[48..52].try_into().unwrap()), + u32::from_le_bytes(buf[52..56].try_into().unwrap()), + u32::from_le_bytes(buf[56..60].try_into().unwrap()), + u32::from_le_bytes(buf[60..64].try_into().unwrap()), + ], + } + } + + /// Get the major version of the FUSE protocol. + pub fn get_major(&self) -> u32 { + self.major + } + + /// Get the minor version of the FUSE protocol. + pub fn get_minor(&self) -> u32 { + self.minor + } + + /// Get the maximum readahead size. + pub fn get_max_readahead(&self) -> u32 { + self.max_readahead + } + + /// Get the flags of the FUSE file system. + pub fn get_flags(&self) -> u32 { + self.flags + } + + /// Get the maximum number of background requests. + pub fn get_max_background(&self) -> u16 { + self.max_background + } + + /// Get the congestion threshold. + pub fn get_congestion_threshold(&self) -> u16 { + self.congestion_threshold + } + + /// Get the maximum write size. + pub fn get_max_write(&self) -> u32 { + self.max_write + } + + /// Get the time granularity. + pub fn get_time_gran(&self) -> u32 { + self.time_gran + } + + /// Get the maximum number of pages. + pub fn get_max_pages(&self) -> u16 { + self.max_pages + } + + /// Get the map alignment. + pub fn get_map_alignment(&self) -> u16 { + self.map_alignment + } + + /// Get the flags2 of the FUSE file system. + pub fn get_flags2(&self) -> u32 { + self.flags2 + } + + /// Print the FuseInitOut structure. + pub fn print(&self) { + debug!("FuseInitOut: major: {:?}, minor: {:?}, max_readahead: {:#x}, flags: {:#x}, max_background: {:?}, congestion_threshold: {:?}, max_write: {:#x}, time_gran: {:?}, max_pages: {:?}, map_alignment: {:?}, flags2: {:?}, unused: {:?}", self.major, self.minor, self.max_readahead, self.flags, self.max_background, self.congestion_threshold, self.max_write, self.time_gran, self.max_pages, self.map_alignment, self.flags2, self.unused); + } +} + +/// FUSE getattr input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseGetattrIn { + // 16 bytes + getattr_flags: u32, + dummy: u32, + fh: u64, +} + +impl FuseGetattrIn { + /// Create a new FuseGetattrIn structure. + pub fn new(getattr_flags: u32, dummy: u32, fh: u64) -> Self { + Self { + getattr_flags, + dummy, + fh, + } + } + + /// Print the FuseGetattrIn structure. + pub fn print(&self) { + debug!( + "FuseGetattrIn: getattr_flags: {:#x}, dummy: {:?}, fh: {:#x}", + self.getattr_flags, self.dummy, self.fh + ); + } + + /// Write the FuseGetattrIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.getattr_flags.to_le_bytes()); + buf[4..8].copy_from_slice(&self.dummy.to_le_bytes()); + buf[8..16].copy_from_slice(&self.fh.to_le_bytes()); + } +} + +/// FUSE file attributes structure +#[derive(Clone, Copy, Default)] +pub struct FuseAttr { + // 88 bytes + ino: u64, + size: u64, + blocks: u64, + atime: u64, + mtime: u64, + ctime: u64, + atimensec: u32, + mtimensec: u32, + ctimensec: u32, + mode: u32, + nlink: u32, + uid: u32, + gid: u32, + rdev: u32, + blksize: u32, + flags: u32, +} + +impl FuseAttr { + /// Create a new FuseAttr structure. + #[allow(clippy::too_many_arguments)] + pub fn new( + ino: u64, + size: u64, + blocks: u64, + atime: u64, + mtime: u64, + ctime: u64, + atimensec: u32, + mtimensec: u32, + ctimensec: u32, + mode: u32, + nlink: u32, + uid: u32, + gid: u32, + rdev: u32, + blksize: u32, + flags: u32, + ) -> Self { + Self { + ino, + size, + blocks, + atime, + mtime, + ctime, + atimensec, + mtimensec, + ctimensec, + mode, + nlink, + uid, + gid, + rdev, + blksize, + flags, + } + } + + /// Read a FuseAttr structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + ino: u64::from_le_bytes(buf[0..8].try_into().unwrap()), + size: u64::from_le_bytes(buf[8..16].try_into().unwrap()), + blocks: u64::from_le_bytes(buf[16..24].try_into().unwrap()), + atime: u64::from_le_bytes(buf[24..32].try_into().unwrap()), + mtime: u64::from_le_bytes(buf[32..40].try_into().unwrap()), + ctime: u64::from_le_bytes(buf[40..48].try_into().unwrap()), + atimensec: u32::from_le_bytes(buf[48..52].try_into().unwrap()), + mtimensec: u32::from_le_bytes(buf[52..56].try_into().unwrap()), + ctimensec: u32::from_le_bytes(buf[56..60].try_into().unwrap()), + mode: u32::from_le_bytes(buf[60..64].try_into().unwrap()), + nlink: u32::from_le_bytes(buf[64..68].try_into().unwrap()), + uid: u32::from_le_bytes(buf[68..72].try_into().unwrap()), + gid: u32::from_le_bytes(buf[72..76].try_into().unwrap()), + rdev: u32::from_le_bytes(buf[76..80].try_into().unwrap()), + blksize: u32::from_le_bytes(buf[80..84].try_into().unwrap()), + flags: u32::from_le_bytes(buf[84..88].try_into().unwrap()), + } + } + + /// Get the size of the file. + pub fn get_size(&self) -> u64 { + self.size + } + + /// Get the mode of the file. + pub fn get_mode(&self) -> u32 { + self.mode + } + + /// Get the user ID of the file. + pub fn get_uid(&self) -> u32 { + self.uid + } + + /// Get the group ID of the file. + pub fn get_gid(&self) -> u32 { + self.gid + } + + /// Get the number of hard links to the file. + pub fn get_nlink(&self) -> u32 { + self.nlink + } + + /// Get the inode number of the file. + pub fn get_ino(&self) -> u64 { + self.ino + } + + /// Get the number of blocks allocated for the file. + pub fn get_blocks(&self) -> u64 { + self.blocks + } + + /// Get the last access time of the file. + pub fn get_atime(&self) -> u64 { + self.atime + } + + /// Get the last modification time of the file. + pub fn get_mtime(&self) -> u64 { + self.mtime + } + + /// Get the last change time of the file. + pub fn get_ctime(&self) -> u64 { + self.ctime + } + + /// Get the nanoseconds of the last access time. + pub fn get_atimensec(&self) -> u32 { + self.atimensec + } + + /// Get the nanoseconds of the last modification time. + pub fn get_mtimensec(&self) -> u32 { + self.mtimensec + } + + /// Get the nanoseconds of the last change time. + pub fn get_ctimensec(&self) -> u32 { + self.ctimensec + } + + /// Get the device ID of the file. + pub fn get_rdev(&self) -> u32 { + self.rdev + } + + /// Get the block size of the file. + pub fn get_blksize(&self) -> u32 { + self.blksize + } + + /// Get the flags of the file. + pub fn get_flags(&self) -> u32 { + self.flags + } + + /// Get the node type of the file. + pub fn set_size(&mut self, size: u64) { + self.size = size; + } + + /// Set the mode of the file. + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode; + } + + /// Set the user ID of the file. + pub fn set_uid(&mut self, uid: u32) { + self.uid = uid; + } + + /// Set the group ID of the file. + pub fn set_gid(&mut self, gid: u32) { + self.gid = gid; + } + + /// Set the number of hard links to the file. + pub fn set_nlink(&mut self, nlink: u32) { + self.nlink = nlink; + } + + /// Set the inode number of the file. + pub fn set_ino(&mut self, ino: u64) { + self.ino = ino; + } + + /// Set the number of blocks allocated for the file. + pub fn set_blocks(&mut self, blocks: u64) { + self.blocks = blocks; + } + + /// Set the last access time of the file. + pub fn set_atime(&mut self, atime: u64) { + self.atime = atime; + } + + /// Set the last modification time of the file. + pub fn set_mtime(&mut self, mtime: u64) { + self.mtime = mtime; + } + + /// Set the last change time of the file. + pub fn set_ctime(&mut self, ctime: u64) { + self.ctime = ctime; + } + + /// Set the nanoseconds of the last access time. + pub fn set_atimensec(&mut self, atimensec: u32) { + self.atimensec = atimensec; + } + + /// Set the nanoseconds of the last modification time. + pub fn set_mtimensec(&mut self, mtimensec: u32) { + self.mtimensec = mtimensec; + } + + /// Set the nanoseconds of the last change time. + pub fn set_ctimensec(&mut self, ctimensec: u32) { + self.ctimensec = ctimensec; + } + + /// Set the device ID of the file. + pub fn set_rdev(&mut self, rdev: u32) { + self.rdev = rdev; + } + + /// Set the block size of the file. + pub fn set_blksize(&mut self, blksize: u32) { + self.blksize = blksize; + } + + /// Set the flags of the file. + pub fn set_flags(&mut self, flags: u32) { + self.flags = flags; + } + + /// Print the FuseAttr structure. + pub fn print(&self) { + debug!("FuseAttr: ino: {:?}, size: {:?}, blocks: {:?}, atime: {:?}, mtime: {:?}, ctime: {:?}, atimensec: {:?}, mtimensec: {:?}, ctimensec: {:?}, mode: {:#x}, nlink: {:?}, uid: {:?}, gid: {:?}, rdev: {:#x}, blksize: {:?}, flags: {:#x}", self.ino, self.size, self.blocks, self.atime, self.mtime, self.ctime, self.atimensec, self.mtimensec, self.ctimensec, self.mode, self.nlink, self.uid, self.gid, self.rdev, self.blksize, self.flags); + } + + /// Write the FuseAttr structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.ino.to_le_bytes()); + buf[8..16].copy_from_slice(&self.size.to_le_bytes()); + buf[16..24].copy_from_slice(&self.blocks.to_le_bytes()); + buf[24..32].copy_from_slice(&self.atime.to_le_bytes()); + buf[32..40].copy_from_slice(&self.mtime.to_le_bytes()); + buf[40..48].copy_from_slice(&self.ctime.to_le_bytes()); + buf[48..52].copy_from_slice(&self.atimensec.to_le_bytes()); + buf[52..56].copy_from_slice(&self.mtimensec.to_le_bytes()); + buf[56..60].copy_from_slice(&self.ctimensec.to_le_bytes()); + buf[60..64].copy_from_slice(&self.mode.to_le_bytes()); + buf[64..68].copy_from_slice(&self.nlink.to_le_bytes()); + buf[68..72].copy_from_slice(&self.uid.to_le_bytes()); + buf[72..76].copy_from_slice(&self.gid.to_le_bytes()); + buf[76..80].copy_from_slice(&self.rdev.to_le_bytes()); + buf[80..84].copy_from_slice(&self.blksize.to_le_bytes()); + buf[84..88].copy_from_slice(&self.flags.to_le_bytes()); + } +} + +impl Debug for FuseAttr { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "FuseAttr: {{ ino: {:?}, size: {:?}, blocks: {:?}, atime: {:?}, mtime: {:?}, ctime: {:?}, atimensec: {:?}, mtimensec: {:?}, ctimensec: {:?}, mode: {:#x}, nlink: {:?}, uid: {:?}, gid: {:?}, rdev: {:#x}, blksize: {:?}, flags: {:#x} }}", self.ino, self.size, self.blocks, self.atime, self.mtime, self.ctime, self.atimensec, self.mtimensec, self.ctimensec, self.mode, self.nlink, self.uid, self.gid, self.rdev, self.blksize, self.flags) + } +} + +/// FUSE file system statistics structure +#[derive(Debug, Clone, Copy, Default)] +pub struct FuseKstatfs { + // 80 bytes + blocks: u64, + bfree: u64, + bavail: u64, + files: u64, + ffree: u64, + bsize: u32, + namelen: u32, + frsize: u32, + padding: u32, + spare: [u32; 6], +} + +impl FuseKstatfs { + /// Create a new FuseKstatfs structure. + #[allow(clippy::too_many_arguments)] + pub fn new( + blocks: u64, + bfree: u64, + bavail: u64, + files: u64, + ffree: u64, + bsize: u32, + namelen: u32, + frsize: u32, + padding: u32, + spare: [u32; 6], + ) -> Self { + Self { + blocks, + bfree, + bavail, + files, + ffree, + bsize, + namelen, + frsize, + padding, + spare, + } + } + + /// Read a FuseKstatfs structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + blocks: u64::from_le_bytes(buf[0..8].try_into().unwrap()), + bfree: u64::from_le_bytes(buf[8..16].try_into().unwrap()), + bavail: u64::from_le_bytes(buf[16..24].try_into().unwrap()), + files: u64::from_le_bytes(buf[24..32].try_into().unwrap()), + ffree: u64::from_le_bytes(buf[32..40].try_into().unwrap()), + bsize: u32::from_le_bytes(buf[40..44].try_into().unwrap()), + namelen: u32::from_le_bytes(buf[44..48].try_into().unwrap()), + frsize: u32::from_le_bytes(buf[48..52].try_into().unwrap()), + padding: u32::from_le_bytes(buf[52..56].try_into().unwrap()), + spare: [ + u32::from_le_bytes(buf[56..60].try_into().unwrap()), + u32::from_le_bytes(buf[60..64].try_into().unwrap()), + u32::from_le_bytes(buf[64..68].try_into().unwrap()), + u32::from_le_bytes(buf[68..72].try_into().unwrap()), + u32::from_le_bytes(buf[72..76].try_into().unwrap()), + u32::from_le_bytes(buf[76..80].try_into().unwrap()), + ], + } + } + + /// Print the FuseKstatfs structure. + pub fn print(&self) { + debug!("FuseKstatfs: blocks: {:?}, bfree: {:?}, bavail: {:?}, files: {:?}, ffree: {:?}, bsize: {:?}, namelen: {:?}, frsize: {:?}, padding: {:?}, spare: {:?}", self.blocks, self.bfree, self.bavail, self.files, self.ffree, self.bsize, self.namelen, self.frsize, self.padding, self.spare); + } +} + +/// FUSE file lock structure +#[derive(Debug, Clone, Copy)] +pub struct FuseFileLock { + // 24 bytes + start: u64, + end: u64, + type_: u32, + pid: u32, +} + +impl FuseFileLock { + /// Create a new FuseFileLock structure. + pub fn new(start: u64, end: u64, type_: u32, pid: u32) -> Self { + Self { + start, + end, + type_, + pid, + } + } + + /// Read a FuseFileLock structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + start: u64::from_le_bytes(buf[0..8].try_into().unwrap()), + end: u64::from_le_bytes(buf[8..16].try_into().unwrap()), + type_: u32::from_le_bytes(buf[16..20].try_into().unwrap()), + pid: u32::from_le_bytes(buf[20..24].try_into().unwrap()), + } + } + + /// Print the FuseFileLock structure. + pub fn print(&self) { + debug!( + "FuseFileLock: start: {:?}, end: {:?}, type: {:?}, pid: {:?}", + self.start, self.end, self.type_, self.pid + ); + } +} + +/// FUSE file attributes output structure +#[derive(Debug, Clone, Copy, Default)] +pub struct FuseAttrOut { + // 104 bytes + attr_valid: u64, + attr_valid_nsec: u32, + dummy: u32, + attr: FuseAttr, +} + +impl FuseAttrOut { + /// Create a new FuseAttrOut structure. + pub fn new(attr_valid: u64, attr_valid_nsec: u32, dummy: u32, attr: FuseAttr) -> Self { + Self { + attr_valid, + attr_valid_nsec, + dummy, + attr, + } + } + + /// Read a FuseAttrOut structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + attr_valid: u64::from_le_bytes(buf[0..8].try_into().unwrap()), + attr_valid_nsec: u32::from_le_bytes(buf[8..12].try_into().unwrap()), + dummy: u32::from_le_bytes(buf[12..16].try_into().unwrap()), + attr: FuseAttr::read_from(&buf[16..104]), + } + } + + /// Get the attribute valid time. + pub fn get_attr_valid(&self) -> u64 { + self.attr_valid + } + + /// Get the attribute valid time in nanoseconds. + pub fn get_attr_valid_nsec(&self) -> u32 { + self.attr_valid_nsec + } + + /// Get the dummy value. + pub fn get_dummy(&self) -> u32 { + self.dummy + } + + /// Get the file attributes. + pub fn get_attr(&self) -> FuseAttr { + self.attr + } + + /// Get the size of the file. + pub fn get_size(&self) -> u64 { + self.attr.size + } + + /// Print the FuseAttrOut structure. + pub fn print(&self) { + debug!( + "FuseAttrOut: attr_valid: {:?}, attr_valid_nsec: {:?}, dummy: {:?}, attr: {:?}", + self.attr_valid, self.attr_valid_nsec, self.dummy, self.attr + ); + } +} + +/// FUSE entry output structure +#[derive(Debug, Clone, Copy, Default)] +pub struct FuseEntryOut { + // 128 bytes + nodeid: u64, /* Inode ID */ + generation: u64, /* Inode generation: nodeid:gen must + be unique for the fs's lifetime */ + entry_valid: u64, /* Cache timeout for the name */ + attr_valid: u64, /* Cache timeout for the attributes */ + entry_valid_nsec: u32, + attr_valid_nsec: u32, + attr: FuseAttr, +} + +impl FuseEntryOut { + /// Create a new FuseEntryOut structure. + pub fn new( + nodeid: u64, + generation: u64, + entry_valid: u64, + attr_valid: u64, + entry_valid_nsec: u32, + attr_valid_nsec: u32, + attr: FuseAttr, + ) -> Self { + Self { + nodeid, + generation, + entry_valid, + attr_valid, + entry_valid_nsec, + attr_valid_nsec, + attr, + } + } + + /// Read a FuseEntryOut structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + nodeid: u64::from_le_bytes(buf[0..8].try_into().unwrap()), + generation: u64::from_le_bytes(buf[8..16].try_into().unwrap()), + entry_valid: u64::from_le_bytes(buf[16..24].try_into().unwrap()), + attr_valid: u64::from_le_bytes(buf[24..32].try_into().unwrap()), + entry_valid_nsec: u32::from_le_bytes(buf[32..36].try_into().unwrap()), + attr_valid_nsec: u32::from_le_bytes(buf[36..40].try_into().unwrap()), + attr: FuseAttr::read_from(&buf[40..128]), + } + } + + /// Get the node ID of the entry. + pub fn get_nodeid(&self) -> u64 { + self.nodeid + } + + /// Get the generation of the entry. + pub fn get_generation(&self) -> u64 { + self.generation + } + + /// Get the entry valid time. + pub fn get_entry_valid(&self) -> u64 { + self.entry_valid + } + + /// Get the attribute valid time. + pub fn get_attr_valid(&self) -> u64 { + self.attr_valid + } + + /// Get the entry valid time in nanoseconds. + pub fn get_entry_valid_nsec(&self) -> u32 { + self.entry_valid_nsec + } + + /// Get the attribute valid time in nanoseconds. + pub fn get_attr_valid_nsec(&self) -> u32 { + self.attr_valid_nsec + } + + /// Get the file attributes. + pub fn get_attr(&self) -> FuseAttr { + self.attr + } + + /// Get the number of hard links to the file. + pub fn get_nlink(&self) -> u32 { + self.attr.nlink + } + + /// Get the size of the file. + pub fn get_size(&self) -> u64 { + self.attr.size + } + + /// Print the FuseEntryOut structure. + pub fn print(&self) { + debug!("FuseEntryOut: nodeid: {:?}, generation: {:?}, entry_valid: {:?}, attr_valid: {:?}, entry_valid_nsec: {:?}, attr_valid_nsec: {:?}, attr: {:?}", self.nodeid, self.generation, self.entry_valid, self.attr_valid, self.entry_valid_nsec, self.attr_valid_nsec, self.attr); + } +} + +/// FUSE setattr input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseSetattrIn { + // 88 bytes + valid: u32, + padding: u32, + fh: u64, + size: u64, + lock_owner: u64, + atime: u64, + mtime: u64, + ctime: u64, + atimensec: u32, + mtimensec: u32, + ctimensec: u32, + mode: u32, + unused4: u32, + uid: u32, + gid: u32, + unused5: u32, +} + +impl FuseSetattrIn { + /// Create a new FuseSetattrIn structure. + #[allow(clippy::too_many_arguments)] + pub fn new( + valid: u32, + padding: u32, + fh: u64, + size: u64, + lock_owner: u64, + atime: u64, + mtime: u64, + ctime: u64, + atimensec: u32, + mtimensec: u32, + ctimensec: u32, + mode: u32, + unused4: u32, + uid: u32, + gid: u32, + unused5: u32, + ) -> Self { + Self { + valid, + padding, + fh, + size, + lock_owner, + atime, + mtime, + ctime, + atimensec, + mtimensec, + ctimensec, + mode, + unused4, + uid, + gid, + unused5, + } + } + + /// Set the valid flags. + pub fn set_valid(&mut self, valid: u32) { + self.valid = valid; + } + + /// Set the file handle. + pub fn set_fh(&mut self, fh: u64) { + self.fh = fh; + } + + /// Set the size of the file. + pub fn set_size(&mut self, size: u64) { + self.size = size; + } + + /// Set the lock owner. + pub fn set_lock_owner(&mut self, lock_owner: u64) { + self.lock_owner = lock_owner; + } + + /// Set the access time. + pub fn set_atime(&mut self, atime: u64) { + self.atime = atime; + } + + /// Set the modification time. + pub fn set_mtime(&mut self, mtime: u64) { + self.mtime = mtime; + } + + /// Set the change time. + pub fn set_ctime(&mut self, ctime: u64) { + self.ctime = ctime; + } + + /// Set the nanoseconds of the access time. + pub fn set_atimensec(&mut self, atimensec: u32) { + self.atimensec = atimensec; + } + + /// Set the nanoseconds of the modification time. + pub fn set_mtimensec(&mut self, mtimensec: u32) { + self.mtimensec = mtimensec; + } + + /// Set the nanoseconds of the change time. + pub fn set_ctimensec(&mut self, ctimensec: u32) { + self.ctimensec = ctimensec; + } + + /// Set the mode of the file. + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode; + } + + /// Set the user ID of the file. + pub fn set_uid(&mut self, uid: u32) { + self.uid = uid; + } + + /// Set the group ID of the file. + pub fn set_gid(&mut self, gid: u32) { + self.gid = gid; + } + + /// Print the FuseSetattrIn structure. + pub fn print(&self) { + debug!("FuseSetattrIn: valid: {:?}, padding: {:?}, fh: {:#x}, size: {:?}, lock_owner: {:?}, atime: {:?}, mtime: {:?}, ctime: {:?}, atimensec: {:?}, mtimensec: {:?}, ctimensec: {:?}, mode: {:#x}, unused4: {:?}, uid: {:?}, gid: {:?}, unused5: {:?}", self.valid, self.padding, self.fh, self.size, self.lock_owner, self.atime, self.mtime, self.ctime, self.atimensec, self.mtimensec, self.ctimensec, self.mode, self.unused4, self.uid, self.gid, self.unused5); + } + + /// Write the FuseSetattrIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.valid.to_le_bytes()); + buf[4..8].copy_from_slice(&self.padding.to_le_bytes()); + buf[8..16].copy_from_slice(&self.fh.to_le_bytes()); + buf[16..24].copy_from_slice(&self.size.to_le_bytes()); + buf[24..32].copy_from_slice(&self.lock_owner.to_le_bytes()); + buf[32..40].copy_from_slice(&self.atime.to_le_bytes()); + buf[40..48].copy_from_slice(&self.mtime.to_le_bytes()); + buf[48..56].copy_from_slice(&self.ctime.to_le_bytes()); + buf[56..60].copy_from_slice(&self.atimensec.to_le_bytes()); + buf[60..64].copy_from_slice(&self.mtimensec.to_le_bytes()); + buf[64..68].copy_from_slice(&self.ctimensec.to_le_bytes()); + buf[68..72].copy_from_slice(&self.mode.to_le_bytes()); + buf[72..76].copy_from_slice(&self.unused4.to_le_bytes()); + buf[76..80].copy_from_slice(&self.uid.to_le_bytes()); + buf[80..84].copy_from_slice(&self.gid.to_le_bytes()); + buf[84..88].copy_from_slice(&self.unused5.to_le_bytes()); + } +} + +/// FUSE open input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseOpenIn { + // 8 bytes + flags: u32, + open_flags: u32, /* FUSE_OPEN_... */ +} + +impl FuseOpenIn { + /// Create a new FuseOpenIn structure. + pub fn new(flags: u32, open_flags: u32) -> Self { + Self { flags, open_flags } + } + + /// Set the flags. + pub fn set_flags(&mut self, flags: u32) { + self.flags = flags; + } + + /// Set the open flags. + pub fn set_open_flags(&mut self, open_flags: u32) { + self.open_flags = open_flags; + } + + /// Print the FuseOpenIn structure. + pub fn print(&self) { + debug!( + "FuseOpenIn: flags: {:#x}, open_flags: {:#x}", + self.flags, self.open_flags + ); + } + + /// Write the FuseOpenIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.flags.to_le_bytes()); + buf[4..8].copy_from_slice(&self.open_flags.to_le_bytes()); + } +} + +/// FUSE open output structure +#[derive(Debug, Clone, Copy, Default)] +pub struct FuseOpenOut { + // 16 bytes + fh: u64, + open_flags: u32, + padding: u32, +} + +impl FuseOpenOut { + /// Create a new FuseOpenOut structure. + pub fn new(fh: u64, open_flags: u32) -> Self { + Self { + fh, + open_flags, + padding: 0, + } + } + + /// Read a FuseOpenOut structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + fh: u64::from_le_bytes(buf[0..8].try_into().unwrap()), + open_flags: u32::from_le_bytes(buf[8..12].try_into().unwrap()), + padding: u32::from_le_bytes(buf[12..16].try_into().unwrap()), + } + } + + /// Get the file handle. + pub fn get_fh(&self) -> u64 { + self.fh + } + + /// Get the open flags. + pub fn get_open_flags(&self) -> u32 { + self.open_flags + } + + /// Print the FuseOpenOut structure. + pub fn print(&self) { + debug!( + "FuseOpenOut: fh: {:#x}, open_flags: {:#x}, padding: {:?}", + self.fh, self.open_flags, self.padding + ); + } +} + +/// FUSE read input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseReadIn { + // 40 bytes + fh: u64, + offset: u64, + size: u32, + read_flags: u32, + lock_owner: u64, + flags: u32, + padding: u32, +} + +impl FuseReadIn { + /// Create a new FuseReadIn structure. + pub fn new( + fh: u64, + offset: u64, + size: u32, + read_flags: u32, + lock_owner: u64, + flags: u32, + ) -> Self { + Self { + fh, + offset, + size, + read_flags, + lock_owner, + flags, + padding: 0, + } + } + + /// Set the file handle. + pub fn set_fh(&mut self, fh: u64) { + self.fh = fh; + } + + /// Set the offset in the file. + pub fn set_offset(&mut self, offset: u64) { + self.offset = offset; + } + + /// Set the size of the read operation. + pub fn set_size(&mut self, size: u32) { + self.size = size; + } + + /// Set the read flags. + pub fn set_read_flags(&mut self, read_flags: u32) { + self.read_flags = read_flags; + } + + /// Set the lock owner. + pub fn set_lock_owner(&mut self, lock_owner: u64) { + self.lock_owner = lock_owner; + } + + /// Set the flags. + pub fn set_flags(&mut self, flags: u32) { + self.flags = flags; + } + + /// Print the FuseReadIn structure. + pub fn print(&self) { + debug!("FuseReadIn: fh: {:#x}, offset: {:?}, size: {:?}, read_flags: {:#x}, lock_owner: {:?}, flags: {:#x}, padding: {:?}", self.fh, self.offset, self.size, self.read_flags, self.lock_owner, self.flags, self.padding); + } + + /// Write the FuseReadIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.fh.to_le_bytes()); + buf[8..16].copy_from_slice(&self.offset.to_le_bytes()); + buf[16..20].copy_from_slice(&self.size.to_le_bytes()); + buf[20..24].copy_from_slice(&self.read_flags.to_le_bytes()); + buf[24..32].copy_from_slice(&self.lock_owner.to_le_bytes()); + buf[32..36].copy_from_slice(&self.flags.to_le_bytes()); + } +} + +/// FUSE write input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseWriteIn { + // 40 bytes + fh: u64, + offset: u64, + size: u32, + write_flags: u32, + lock_owner: u64, + flags: u32, + padding: u32, +} + +impl FuseWriteIn { + /// Create a new FuseWriteIn structure. + pub fn new( + fh: u64, + offset: u64, + size: u32, + write_flags: u32, + lock_owner: u64, + flags: u32, + ) -> Self { + Self { + fh, + offset, + size, + write_flags, + lock_owner, + flags, + padding: 0, + } + } + + /// Set the file handle. + pub fn set_fh(&mut self, fh: u64) { + self.fh = fh; + } + + /// Set the offset in the file. + pub fn set_offset(&mut self, offset: u64) { + self.offset = offset; + } + + /// Set the size of the write operation. + pub fn set_size(&mut self, size: u32) { + self.size = size; + } + + /// Set the write flags. + pub fn set_write_flags(&mut self, write_flags: u32) { + self.write_flags = write_flags; + } + + /// Set the lock owner. + pub fn set_lock_owner(&mut self, lock_owner: u64) { + self.lock_owner = lock_owner; + } + + /// Set the flags. + pub fn set_flags(&mut self, flags: u32) { + self.flags = flags; + } + + /// Print the FuseWriteIn structure. + pub fn print(&self) { + debug!("FuseWriteIn: fh: {:#x}, offset: {:?}, size: {:?}, write_flags: {:#x}, lock_owner: {:?}, flags: {:#x}, padding: {:?}", self.fh, self.offset, self.size, self.write_flags, self.lock_owner, self.flags, self.padding); + } + + /// Write the FuseWriteIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.fh.to_le_bytes()); + buf[8..16].copy_from_slice(&self.offset.to_le_bytes()); + buf[16..20].copy_from_slice(&self.size.to_le_bytes()); + buf[20..24].copy_from_slice(&self.write_flags.to_le_bytes()); + buf[24..32].copy_from_slice(&self.lock_owner.to_le_bytes()); + buf[32..36].copy_from_slice(&self.flags.to_le_bytes()); + } +} + +/// FUSE write output structure +#[derive(Debug, Clone, Copy)] +pub struct FuseWriteOut { + // 8 bytes + size: u32, + padding: u32, +} + +impl FuseWriteOut { + /// Create a new FuseWriteOut structure. + pub fn new(size: u32) -> Self { + Self { size, padding: 0 } + } + + /// Read a FuseWriteOut structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + size: u32::from_le_bytes(buf[0..4].try_into().unwrap()), + padding: u32::from_le_bytes(buf[4..8].try_into().unwrap()), + } + } + + /// Get the size of the written data. + pub fn get_size(&self) -> u32 { + self.size + } + + /// Print the FuseWriteOut structure. + pub fn print(&self) { + debug!( + "FuseWriteOut: size: {:?}, padding: {:?}", + self.size, self.padding + ); + } + + /// Write the FuseWriteOut structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.size.to_le_bytes()); + } +} + +/// FUSE create input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseCreateIn { + // 16 bytes + flags: u32, + mode: u32, + umask: u32, + open_flags: u32, /* FUSE_OPEN_... */ +} + +impl FuseCreateIn { + /// Create a new FuseCreateIn structure. + pub fn new(flags: u32, mode: u32, umask: u32, open_flags: u32) -> Self { + Self { + flags, + mode, + umask, + open_flags, + } + } + + /// Set the flags. + pub fn set_flags(&mut self, flags: u32) { + self.flags = flags; + } + + /// Set the mode of the file. + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode; + } + + /// Set the umask for the file. + pub fn set_umask(&mut self, umask: u32) { + self.umask = umask; + } + + /// Set the open flags. + pub fn set_open_flags(&mut self, open_flags: u32) { + self.open_flags = open_flags; + } + + /// Print the FuseCreateIn structure. + pub fn print(&self) { + debug!( + "FuseCreateIn: flags: {:#x}, mode: {:#x}, umask: {:?}, open_flags: {:#x}", + self.flags, self.mode, self.umask, self.open_flags + ); + } + + /// Write the FuseCreateIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.flags.to_le_bytes()); + buf[4..8].copy_from_slice(&self.mode.to_le_bytes()); + buf[8..12].copy_from_slice(&self.umask.to_le_bytes()); + buf[12..16].copy_from_slice(&self.open_flags.to_le_bytes()); + } +} + +/// FUSE release input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseReleaseIn { + // 24 bytes + fh: u64, + flags: u32, + release_flags: u32, + lock_owner: u64, +} + +impl FuseReleaseIn { + /// Create a new FuseReleaseIn structure. + pub fn new(fh: u64, flags: u32, release_flags: u32, lock_owner: u64) -> Self { + Self { + fh, + flags, + release_flags, + lock_owner, + } + } + + /// Set the file handle. + pub fn set_fh(&mut self, fh: u64) { + self.fh = fh; + } + + /// Set the flags. + pub fn set_flags(&mut self, flags: u32) { + self.flags = flags; + } + + /// Set the release flags. + pub fn set_release_flags(&mut self, release_flags: u32) { + self.release_flags = release_flags; + } + + /// Set the lock owner. + pub fn set_lock_owner(&mut self, lock_owner: u64) { + self.lock_owner = lock_owner; + } + + /// Print the FuseReleaseIn structure. + pub fn print(&self) { + debug!( + "FuseReleaseIn: fh: {:#x}, flags: {:#x}, release_flags: {:#x}, lock_owner: {:?}", + self.fh, self.flags, self.release_flags, self.lock_owner + ); + } + + /// Write the FuseReleaseIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.fh.to_le_bytes()); + buf[8..12].copy_from_slice(&self.flags.to_le_bytes()); + buf[12..16].copy_from_slice(&self.release_flags.to_le_bytes()); + buf[16..24].copy_from_slice(&self.lock_owner.to_le_bytes()); + } +} + +/// FUSE flush input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseFlushIn { + // 24 bytes + fh: u64, + unused: u32, + padding: u32, + lock_owner: u64, +} + +impl FuseFlushIn { + /// Create a new FuseFlushIn structure. + pub fn new(fh: u64, unused: u32, padding: u32, lock_owner: u64) -> Self { + Self { + fh, + unused, + padding, + lock_owner, + } + } + + /// Set the file handle. + pub fn set_fh(&mut self, fh: u64) { + self.fh = fh; + } + + /// Set the lock owner. + pub fn set_lock_owner(&mut self, lock_owner: u64) { + self.lock_owner = lock_owner; + } + + /// Print the FuseFlushIn structure. + pub fn print(&self) { + debug!( + "FuseFlushIn: fh: {:#x}, unused: {:?}, padding: {:?}, lock_owner: {:?}", + self.fh, self.unused, self.padding, self.lock_owner + ); + } + + /// Write the FuseFlushIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.fh.to_le_bytes()); + buf[8..12].copy_from_slice(&self.unused.to_le_bytes()); + buf[12..16].copy_from_slice(&self.padding.to_le_bytes()); + buf[16..24].copy_from_slice(&self.lock_owner.to_le_bytes()); + } +} + +/// FUSE statfs output structure +#[derive(Debug, Clone, Copy, Default)] +pub struct FuseStatfsOut { + // 80 bytes + st: FuseKstatfs, +} + +impl FuseStatfsOut { + /// Create a new FuseStatfsOut structure. + pub fn new(st: FuseKstatfs) -> Self { + Self { st } + } + + /// Read a FuseStatfsOut structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + st: FuseKstatfs::read_from(buf), + } + } + + /// Get the FUSE kstatfs structure. + pub fn get_kstatfs(&self) -> FuseKstatfs { + self.st + } + + /// Print the FuseStatfsOut structure. + pub fn print(&self) { + debug!("FuseStatfsOut: st: {:?}", self.st); + } +} + +/// FUSE forget input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseForgetIn { + // 8 bytes + nlookup: u64, +} + +impl FuseForgetIn { + /// Create a new FuseForgetIn structure. + pub fn new(nlookup: u64) -> Self { + Self { nlookup } + } + + /// Set the number of lookups to forget. + pub fn set_nlookup(&mut self, nlookup: u64) { + self.nlookup = nlookup; + } + + /// Print the FuseForgetIn structure. + pub fn print(&self) { + debug!("FuseForgetIn: nlookup: {:?}", self.nlookup); + } + + /// Write the FuseForgetIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.nlookup.to_le_bytes()); + } +} + +/// FUSE forget one input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseForgetOne { + // 16 bytes + nodeid: u64, + nlookup: u64, +} + +impl FuseForgetOne { + /// Create a new FuseForgetOne structure. + pub fn new(nodeid: u64, nlookup: u64) -> Self { + Self { nodeid, nlookup } + } + + /// Set the node ID. + pub fn set_nodeid(&mut self, nodeid: u64) { + self.nodeid = nodeid; + } + + /// Set the number of lookups to forget. + pub fn set_nlookup(&mut self, nlookup: u64) { + self.nlookup = nlookup; + } + + /// Read a FuseForgetOne structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + nodeid: u64::from_le_bytes(buf[0..8].try_into().unwrap()), + nlookup: u64::from_le_bytes(buf[8..16].try_into().unwrap()), + } + } + + /// Print the FuseForgetOne structure. + pub fn print(&self) { + debug!( + "FuseForgetOne: nodeid: {:?}, nlookup: {:?}", + self.nodeid, self.nlookup + ); + } +} + +/// FUSE batch forget input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseBatchForgetIn { + // 8 bytes + count: u32, + dummy: u32, +} + +impl FuseBatchForgetIn { + /// Create a new FuseBatchForgetIn structure. + pub fn new(count: u32) -> Self { + Self { count, dummy: 0 } + } + + /// Set the count of forget operations. + pub fn set_count(&mut self, count: u32) { + self.count = count; + } + + /// Print the FuseBatchForgetIn structure. + pub fn print(&self) { + debug!( + "FuseBatchForgetIn: count: {:?}, dummy: {:?}", + self.count, self.dummy + ); + } + + /// Write the FuseBatchForgetIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.count.to_le_bytes()); + buf[4..8].copy_from_slice(&self.dummy.to_le_bytes()); + } +} + +/// FUSE mknod input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseMknodIn { + // 16 bytes + mode: u32, + rdev: u32, + umask: u32, + padding: u32, +} + +impl FuseMknodIn { + /// Create a new FuseMknodIn structure. + pub fn new(mode: u32, rdev: u32, umask: u32) -> Self { + Self { + mode, + rdev, + umask, + padding: 0, + } + } + + /// Set the mode of the file. + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode; + } + + /// Set the device ID. + pub fn set_rdev(&mut self, rdev: u32) { + self.rdev = rdev; + } + + /// Set the umask for the file. + pub fn set_umask(&mut self, umask: u32) { + self.umask = umask; + } + + /// Print the FuseMknodIn structure. + pub fn print(&self) { + debug!( + "FuseMknodIn: mode: {:#x}, rdev: {:#x}, umask: {:?}, padding: {:?}", + self.mode, self.rdev, self.umask, self.padding + ); + } + + /// Write the FuseMknodIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.mode.to_le_bytes()); + buf[4..8].copy_from_slice(&self.rdev.to_le_bytes()); + buf[8..12].copy_from_slice(&self.umask.to_le_bytes()); + buf[12..16].copy_from_slice(&self.padding.to_le_bytes()); + } +} + +/// FUSE mkdir input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseMkdirIn { + // 8 bytes + mode: u32, + umask: u32, +} + +impl FuseMkdirIn { + /// Create a new FuseMkdirIn structure. + pub fn new(mode: u32, umask: u32) -> Self { + Self { mode, umask } + } + + /// Set the mode of the directory. + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode; + } + + /// Set the umask for the directory. + pub fn set_umask(&mut self, umask: u32) { + self.umask = umask; + } + + /// Print the FuseMkdirIn structure. + pub fn print(&self) { + debug!( + "FuseMkdirIn: mode: {:#x}, umask: {:?}", + self.mode, self.umask + ); + } + + /// Write the FuseMkdirIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.mode.to_le_bytes()); + buf[4..8].copy_from_slice(&self.umask.to_le_bytes()); + } +} + +/// FUSE rename input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseRenameIn { + // 8 bytes + newdir: u64, +} + +impl FuseRenameIn { + /// Create a new FuseRenameIn structure. + pub fn new(newdir: u64) -> Self { + Self { newdir } + } + + /// Set the new directory ID. + pub fn set_newdir(&mut self, newdir: u64) { + self.newdir = newdir; + } + + /// Print the FuseRenameIn structure. + pub fn print(&self) { + debug!("FuseRenameIn: newdir: {:?}", self.newdir); + } + + /// Write the FuseRenameIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.newdir.to_le_bytes()); + } +} + +/// FUSE rename2 input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseRename2In { + // 16 bytes + newdir: u64, + flags: u32, + padding: u32, +} + +impl FuseRename2In { + /// Create a new FuseRename2In structure. + pub fn new(newdir: u64, flags: u32) -> Self { + Self { + newdir, + flags, + padding: 0, + } + } + + /// Set the new directory ID. + pub fn set_newdir(&mut self, newdir: u64) { + self.newdir = newdir; + } + + /// Set the flags for the rename operation. + pub fn set_flags(&mut self, flags: u32) { + self.flags = flags; + } + + /// Print the FuseRename2In structure. + pub fn print(&self) { + debug!( + "FuseRename2In: newdir: {:?}, flags: {:#x}, padding: {:?}", + self.newdir, self.flags, self.padding + ); + } + + /// Write the FuseRename2In structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.newdir.to_le_bytes()); + buf[8..12].copy_from_slice(&self.flags.to_le_bytes()); + buf[12..16].copy_from_slice(&self.padding.to_le_bytes()); + } +} + +/// FUSE link input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseLinkIn { + // 8 bytes + oldnodeid: u64, +} + +impl FuseLinkIn { + /// Create a new FuseLinkIn structure. + pub fn new(oldnodeid: u64) -> Self { + Self { oldnodeid } + } + + /// Set the old node ID. + pub fn set_oldnodeid(&mut self, oldnodeid: u64) { + self.oldnodeid = oldnodeid; + } + + /// Print the FuseLinkIn structure. + pub fn print(&self) { + debug!("FuseLinkIn: oldnodeid: {:?}", self.oldnodeid); + } + + /// Write the FuseLinkIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.oldnodeid.to_le_bytes()); + } +} + +/// FUSE setxattr input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseSetxattrIn { + // 16 bytes + size: u32, + flags: u32, + setxattr_flags: u32, + padding: u32, +} + +impl FuseSetxattrIn { + /// Create a new FuseSetxattrIn structure. + pub fn new(size: u32, flags: u32, setxattr_flags: u32) -> Self { + Self { + size, + flags, + setxattr_flags, + padding: 0, + } + } + + /// Set the size of the extended attribute. + pub fn set_size(&mut self, size: u32) { + self.size = size; + } + + /// Set the flags for the extended attribute. + pub fn set_flags(&mut self, flags: u32) { + self.flags = flags; + } + + /// Set the setxattr flags. + pub fn set_setxattr_flags(&mut self, setxattr_flags: u32) { + self.setxattr_flags = setxattr_flags; + } + + /// Print the FuseSetxattrIn structure. + pub fn print(&self) { + debug!( + "FuseSetxattrIn: size: {:?}, flags: {:#x}, setxattr_flags: {:#x}, padding: {:?}", + self.size, self.flags, self.setxattr_flags, self.padding + ); + } + + /// Write the FuseSetxattrIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.size.to_le_bytes()); + buf[4..8].copy_from_slice(&self.flags.to_le_bytes()); + buf[8..12].copy_from_slice(&self.setxattr_flags.to_le_bytes()); + buf[12..16].copy_from_slice(&self.padding.to_le_bytes()); + } +} + +/// FUSE getxattr output structure +#[derive(Debug, Clone, Copy)] +pub struct FuseGetxattrIn { + // 8 bytes + size: u32, + padding: u32, +} + +impl FuseGetxattrIn { + /// Create a new FuseGetxattrIn structure. + pub fn new(size: u32) -> Self { + Self { size, padding: 0 } + } + + /// Set the size of the extended attribute. + pub fn set_size(&mut self, size: u32) { + self.size = size; + } + + /// Print the FuseGetxattrIn structure. + pub fn print(&self) { + debug!( + "FuseGetxattrIn: size: {:?}, padding: {:?}", + self.size, self.padding + ); + } + + /// Write the FuseGetxattrIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.size.to_le_bytes()); + buf[4..8].copy_from_slice(&self.padding.to_le_bytes()); + } +} + +/// FUSE getxattr output structure +#[derive(Debug, Clone, Copy)] +pub struct FuseGetxattrOut { + // 8 bytes + size: u32, + padding: u32, +} + +impl FuseGetxattrOut { + /// Create a new FuseGetxattrOut structure. + pub fn new(size: u32) -> Self { + Self { size, padding: 0 } + } + + /// Get the size of the extended attribute. + pub fn get_size(&self) -> u32 { + self.size + } + + /// Read a FuseGetxattrOut structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + size: u32::from_le_bytes(buf[0..4].try_into().unwrap()), + padding: u32::from_le_bytes(buf[4..8].try_into().unwrap()), + } + } + + /// Print the FuseGetxattrOut structure. + pub fn print(&self) { + debug!( + "FuseGetxattrOut: size: {:?}, padding: {:?}", + self.size, self.padding + ); + } +} + +/// FUSE access input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseAccessIn { + // 8 bytes + mask: u32, + padding: u32, +} + +impl FuseAccessIn { + /// Create a new FuseAccessIn structure. + pub fn new(mask: u32) -> Self { + Self { mask, padding: 0 } + } + + /// Set the access mask. + pub fn set_mask(&mut self, mask: u32) { + self.mask = mask; + } + + /// Print the FuseAccessIn structure. + pub fn print(&self) { + debug!( + "FuseAccessIn: mask: {:?}, padding: {:?}", + self.mask, self.padding + ); + } + + /// Write the FuseAccessIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..4].copy_from_slice(&self.mask.to_le_bytes()); + buf[4..8].copy_from_slice(&self.padding.to_le_bytes()); + } +} + +/// FUSE fsync input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseFsyncIn { + // 8 bytes + fh: u64, + fsync_flags: u32, + padding: u32, +} + +impl FuseFsyncIn { + /// Create a new FuseFsyncIn structure. + pub fn new(fh: u64, fsync_flags: u32) -> Self { + Self { + fh, + fsync_flags, + padding: 0, + } + } + + /// Set the file handle. + pub fn set_fh(&mut self, fh: u64) { + self.fh = fh; + } + + /// Set the fsync flags. + pub fn set_fsync_flags(&mut self, fsync_flags: u32) { + self.fsync_flags = fsync_flags; + } + + /// Print the FuseFsyncIn structure. + pub fn print(&self) { + debug!( + "FuseFsyncIn: fh: {:#x}, fsync_flags: {:#x}, padding: {:?}", + self.fh, self.fsync_flags, self.padding + ); + } + + /// Write the FuseFsyncIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.fh.to_le_bytes()); + buf[8..12].copy_from_slice(&self.fsync_flags.to_le_bytes()); + buf[12..16].copy_from_slice(&self.padding.to_le_bytes()); + } +} + +/// FUSE bmap input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseBmapIn { + // 16 bytes + block: u64, + blocksize: u32, + padding: u32, +} + +impl FuseBmapIn { + /// Create a new FuseBmapIn structure. + pub fn new(block: u64, blocksize: u32) -> Self { + Self { + block, + blocksize, + padding: 0, + } + } + + /// Set the block number. + pub fn set_block(&mut self, block: u64) { + self.block = block; + } + + /// Set the block size. + pub fn set_blocksize(&mut self, blocksize: u32) { + self.blocksize = blocksize; + } + + /// Print the FuseBmapIn structure. + pub fn print(&self) { + debug!( + "FuseBmapIn: block: {:?}, blocksize: {:?}, padding: {:?}", + self.block, self.blocksize, self.padding + ); + } + + /// Write the FuseBmapIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.block.to_le_bytes()); + buf[8..12].copy_from_slice(&self.blocksize.to_le_bytes()); + buf[12..16].copy_from_slice(&self.padding.to_le_bytes()); + } +} + +/// FUSE bmap output structure +#[derive(Debug, Clone, Copy)] +pub struct FuseBmapOut { + // 8 bytes + block: u64, +} + +impl FuseBmapOut { + /// Create a new FuseBmapOut structure. + pub fn new(block: u64) -> Self { + Self { block } + } + + /// Read a FuseBmapOut structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + block: u64::from_le_bytes(buf[0..8].try_into().unwrap()), + } + } + + /// Get the block number. + pub fn get_block(&self) -> u64 { + self.block + } + + /// Print the FuseBmapOut structure. + pub fn print(&self) { + debug!("FuseBmapOut: block: {:?}", self.block); + } +} + +/// FUSE ioctl input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseIoctlIn { + // 32 bytes + fh: u64, + flags: u32, + cmd: u32, + arg: u64, + in_size: u32, + out_size: u32, +} + +impl FuseIoctlIn { + /// Create a new FuseIoctlIn structure. + pub fn new(fh: u64, flags: u32, cmd: u32, arg: u64, in_size: u32, out_size: u32) -> Self { + Self { + fh, + flags, + cmd, + arg, + in_size, + out_size, + } + } + + /// Set the file handle. + pub fn set_fh(&mut self, fh: u64) { + self.fh = fh; + } + + /// Set the flags. + pub fn set_flags(&mut self, flags: u32) { + self.flags = flags; + } + + /// Set the command. + pub fn set_cmd(&mut self, cmd: u32) { + self.cmd = cmd; + } + + /// Set the argument. + pub fn set_arg(&mut self, arg: u64) { + self.arg = arg; + } + + /// Set the input size. + pub fn set_in_size(&mut self, in_size: u32) { + self.in_size = in_size; + } + + /// Set the output size. + pub fn set_out_size(&mut self, out_size: u32) { + self.out_size = out_size; + } + + /// Print the FuseIoctlIn structure. + pub fn print(&self) { + debug!("FuseIoctlIn: fh: {:#x}, flags: {:#x}, cmd: {:?}, arg: {:?}, in_size: {:?}, out_size: {:?}", self.fh, self.flags, self.cmd, self.arg, self.in_size, self.out_size); + } + + /// Write the FuseIoctlIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.fh.to_le_bytes()); + buf[8..12].copy_from_slice(&self.flags.to_le_bytes()); + buf[12..16].copy_from_slice(&self.cmd.to_le_bytes()); + buf[16..24].copy_from_slice(&self.arg.to_le_bytes()); + buf[24..28].copy_from_slice(&self.in_size.to_le_bytes()); + buf[28..32].copy_from_slice(&self.out_size.to_le_bytes()); + } +} + +/// FUSE ioctl iovec structure +#[derive(Debug, Clone, Copy)] +pub struct FuseIoctlIovec { + // 16 bytes + base: u64, + len: u64, +} + +impl FuseIoctlIovec { + /// Create a new FuseIoctlIovec structure. + pub fn new(base: u64, len: u64) -> Self { + Self { base, len } + } + + /// Read a FuseIoctlIovec structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + base: u64::from_le_bytes(buf[0..8].try_into().unwrap()), + len: u64::from_le_bytes(buf[8..16].try_into().unwrap()), + } + } + + /// Get the base address. + pub fn get_base(&self) -> u64 { + self.base + } + + /// Get the length. + pub fn get_len(&self) -> u64 { + self.len + } + + /// Print the FuseIoctlIovec structure. + pub fn print(&self) { + debug!("FuseIoctlIovec: base: {:?}, len: {:?}", self.base, self.len); + } +} + +/// FUSE ioctl output structure +#[derive(Debug, Clone, Copy)] +pub struct FuseIoctlOut { + // 16 bytes + result: i32, + flags: u32, + in_iovs: u32, + out_iovs: u32, +} + +impl FuseIoctlOut { + /// Create a new FuseIoctlOut structure. + pub fn new(result: i32, flags: u32, in_iovs: u32, out_iovs: u32) -> Self { + Self { + result, + flags, + in_iovs, + out_iovs, + } + } + + /// Read a FuseIoctlOut structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + result: i32::from_le_bytes(buf[0..4].try_into().unwrap()), + flags: u32::from_le_bytes(buf[4..8].try_into().unwrap()), + in_iovs: u32::from_le_bytes(buf[8..12].try_into().unwrap()), + out_iovs: u32::from_le_bytes(buf[12..16].try_into().unwrap()), + } + } + + /// Get the result of the ioctl operation. + pub fn get_result(&self) -> i32 { + self.result + } + + /// Get the flags. + pub fn get_flags(&self) -> u32 { + self.flags + } + + /// Get the number of input iovecs. + pub fn get_in_iovs(&self) -> u32 { + self.in_iovs + } + + /// Get the number of output iovecs. + pub fn get_out_iovs(&self) -> u32 { + self.out_iovs + } + + /// Print the FuseIoctlOut structure. + pub fn print(&self) { + debug!( + "FuseIoctlOut: result: {:?}, flags: {:#x}, in_iovs: {:?}, out_iovs: {:?}", + self.result, self.flags, self.in_iovs, self.out_iovs + ); + } +} + +/// FUSE poll input structure +#[derive(Debug, Clone, Copy)] +pub struct FusePollIn { + // 24 bytes + fh: u64, + kh: u64, + flags: u32, + events: u32, +} + +impl FusePollIn { + /// Create a new FusePollIn structure. + pub fn new(fh: u64, kh: u64, flags: u32, events: u32) -> Self { + Self { + fh, + kh, + flags, + events, + } + } + + /// Set the file handle. + pub fn set_fh(&mut self, fh: u64) { + self.fh = fh; + } + + /// Set the kernel handle. + pub fn set_kh(&mut self, kh: u64) { + self.kh = kh; + } + + /// Set the flags. + pub fn set_flags(&mut self, flags: u32) { + self.flags = flags; + } + + /// Set the events. + pub fn set_events(&mut self, events: u32) { + self.events = events; + } + + /// Print the FusePollIn structure. + pub fn print(&self) { + debug!( + "FusePollIn: fh: {:#x}, kh: {:?}, flags: {:#x}, events: {:?}", + self.fh, self.kh, self.flags, self.events + ); + } + + /// Write the FusePollIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.fh.to_le_bytes()); + buf[8..16].copy_from_slice(&self.kh.to_le_bytes()); + buf[16..20].copy_from_slice(&self.flags.to_le_bytes()); + buf[20..24].copy_from_slice(&self.events.to_le_bytes()); + } +} + +/// FUSE poll output structure +#[derive(Debug, Clone, Copy)] +pub struct FusePollOut { + // 8 bytes + revents: u32, + padding: u32, +} + +impl FusePollOut { + /// Create a new FusePollOut structure. + pub fn new(revents: u32) -> Self { + Self { + revents, + padding: 0, + } + } + + /// Read a FusePollOut structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + revents: u32::from_le_bytes(buf[0..4].try_into().unwrap()), + padding: u32::from_le_bytes(buf[4..8].try_into().unwrap()), + } + } + + /// Get the revents. + pub fn get_revents(&self) -> u32 { + self.revents + } + + /// Print the FusePollOut structure. + pub fn print(&self) { + debug!( + "FusePollOut: revents: {:?}, padding: {:?}", + self.revents, self.padding + ); + } +} + +/// FUSE lseek input structure +#[derive(Debug, Clone, Copy)] +pub struct FuseLseekIn { + // 24 bytes + fh: u64, + offset: u64, + whence: u32, + padding: u32, +} + +impl FuseLseekIn { + /// Create a new FuseLseekIn structure. + pub fn new(fh: u64, offset: u64, whence: u32) -> Self { + Self { + fh, + offset, + whence, + padding: 0, + } + } + + /// Set the file handle. + pub fn set_fh(&mut self, fh: u64) { + self.fh = fh; + } + + /// Set the offset. + pub fn set_offset(&mut self, offset: u64) { + self.offset = offset; + } + + /// Set the whence value. + pub fn set_whence(&mut self, whence: u32) { + self.whence = whence; + } + + /// Print the FuseLseekIn structure. + pub fn print(&self) { + debug!( + "FuseLseekIn: fh: {:#x}, offset: {:?}, whence: {:?}, padding: {:?}", + self.fh, self.offset, self.whence, self.padding + ); + } + + /// Write the FuseLseekIn structure to a buffer. + pub fn write_to(&self, buf: &mut [u8]) { + buf[0..8].copy_from_slice(&self.fh.to_le_bytes()); + buf[8..16].copy_from_slice(&self.offset.to_le_bytes()); + buf[16..20].copy_from_slice(&self.whence.to_le_bytes()); + buf[20..24].copy_from_slice(&self.padding.to_le_bytes()); + } +} + +/// FUSE lseek output structure +#[derive(Debug, Clone, Copy)] +pub struct FuseLseekOut { + // 8 bytes + offset: u64, +} + +impl FuseLseekOut { + /// Create a new FuseLseekOut structure. + pub fn new(offset: u64) -> Self { + Self { offset } + } + + /// Read a FuseLseekOut structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + Self { + offset: u64::from_le_bytes(buf[0..8].try_into().unwrap()), + } + } + + /// Get the offset. + pub fn get_offset(&self) -> u64 { + self.offset + } + + /// Print the FuseLseekOut structure. + pub fn print(&self) { + debug!("FuseLseekOut: offset: {:?}", self.offset); + } +} + +/// FUSE directory entry structure +#[derive(Debug)] +pub struct FuseDirent { + // 24 bytes + len + ino: u64, + off: u64, + namelen: u32, + type_: u32, + name: String, +} + +impl FuseDirent { + /// Create a new FuseDirent structure. + pub fn new(ino: u64, off: u64, namelen: u32, type_: u32, name: String) -> Self { + Self { + ino, + off, + namelen, + type_, + name, + } + } + + /// Read a FuseDirent structure from a buffer. + pub fn read_from(buf: &[u8]) -> Self { + let ino = u64::from_le_bytes(buf[0..8].try_into().unwrap()); + let off = u64::from_le_bytes(buf[8..16].try_into().unwrap()); + let namelen = u32::from_le_bytes(buf[16..20].try_into().unwrap()); + let type_ = u32::from_le_bytes(buf[20..24].try_into().unwrap()); + let name = String::from_utf8(buf[24..24 + namelen as usize].to_vec()).unwrap(); + Self { + ino, + off, + namelen, + type_, + name, + } + } + + /// Get the inode number. + pub fn get_ino(&self) -> u64 { + self.ino + } + + /// Get the offset. + pub fn get_off(&self) -> u64 { + self.off + } + + /// Get the name length. + pub fn get_namelen(&self) -> u32 { + self.namelen + } + + /// Get the type of the directory entry. + pub fn get_type(&self) -> u32 { + self.type_ + } + + /// Get the name of the directory entry. + pub fn get_name(&self) -> String { + self.name.clone() + } + + /// Convert the type to a VfsNodeType. + pub fn get_type_as_vfsnodetype(&self) -> VfsNodeType { + match self.type_ { + 1 => VfsNodeType::Fifo, + 2 => VfsNodeType::CharDevice, + 4 => VfsNodeType::Dir, + 6 => VfsNodeType::BlockDevice, + 8 => VfsNodeType::File, + 10 => VfsNodeType::SymLink, + 12 => VfsNodeType::Socket, + _ => VfsNodeType::File, + } + } + + /// Get the length of the FuseDirent structure. + pub fn get_len(&self) -> usize { + let padding = (8 - (self.namelen % 8)) % 8; + debug!( + "FuseDirent: padding: {:?}, len: {:?}", + padding, + (self.namelen + padding) as usize + ); + 24 + (self.namelen + padding) as usize + } + + /// Print the FuseDirent structure. + pub fn print(&self) { + debug!( + "FuseDirent: ino: {:?}, off: {:?}, namelen: {:?}, type: {:?}, name: {:?}", + self.ino, self.off, self.namelen, self.type_, self.name + ); + } +} diff --git a/modules/ruxfs/src/lib.rs b/modules/ruxfs/src/lib.rs index 6db931721..0ac7d4379 100644 --- a/modules/ruxfs/src/lib.rs +++ b/modules/ruxfs/src/lib.rs @@ -13,20 +13,19 @@ //! //! # Cargo Features //! -//! - `fatfs`: Use [FAT] as the main filesystem and mount it on `/`. This feature -//! is **enabled** by default. +//! - `fatfs`: Use [FAT] as the main filesystem and mount it on `/`. Requires +//! `blkfs` to be enabled. //! - `devfs`: Mount [`axfs_devfs::DeviceFileSystem`] on `/dev`. This feature is -//! **enabled** by default. +//! **enabled** by default. //! - `ramfs`: Mount [`axfs_ramfs::RamFileSystem`] on `/tmp`. This feature is -//! **enabled** by default. +//! **enabled** by default. //! - `myfs`: Allow users to define their custom filesystems to override the -//! default. In this case, [`MyFileSystemIf`] is required to be implemented -//! to create and initialize other filesystems. This feature is **disabled** by -//! by default, but it will override other filesystem selection features if -//! both are enabled. +//! default. In this case, [`MyFileSystemIf`] is required to be implemented +//! to create and initialize other filesystems. This feature is **disabled** by +//! by default, but it will override other filesystem selection features if +//! both are enabled. //! //! [FAT]: https://en.wikipedia.org/wiki/File_Allocation_Table -//! [`MyFileSystemIf`]: fops::MyFileSystemIf #![cfg_attr(all(not(test), not(doc)), no_std)] #![feature(doc_auto_cfg)] @@ -35,19 +34,59 @@ extern crate log; extern crate alloc; -mod dev; -mod fs; -mod mounts; -mod root; - #[cfg(feature = "alloc")] mod arch; +mod fs; +mod mounts; pub mod api; +#[cfg(feature = "blkfs")] +pub mod dev; +pub mod devfuse; +mod directory; +pub mod fifo; +mod file; pub mod fops; +pub mod fuse_st; +pub mod root; + +pub use directory::Directory; +pub use file::File; +pub use ruxfdtable::OpenFlags; + +// Re-export `axfs_vfs` path types. +/// Alias of [`axfs_vfs::VfsNodeType`]. +pub type FileType = axfs_vfs::VfsNodeType; +/// Alias of [`axfs_vfs::VfsDirEntry`]. +pub type DirEntry = axfs_vfs::VfsDirEntry; +/// Alias of [`axfs_vfs::VfsNodeAttr`]. +pub type FileAttr = axfs_vfs::VfsNodeAttr; +/// Alias of [`axfs_vfs::VfsNodePerm`]. +pub type FilePerm = axfs_vfs::VfsNodePerm; + +/// Canonicalized absolute path type. Requirements: +/// +/// - Starting with `/` +/// - No `.` or `..` components +/// - No redundant or tailing `/` +/// - Valid examples: `/`, `/root/foo/bar` +pub type AbsPath<'a> = axfs_vfs::AbsPath<'a>; + +/// Canonicalized relative path type. Requirements: +/// +/// - No starting `/` +/// - No `.` components +/// - No redundant or tailing `/` +/// - Possibly starts with `..` +/// - Valid examples: ` `, `..`, `../b`, `../..` +pub type RelPath<'a> = axfs_vfs::RelPath<'a>; + +#[cfg(feature = "myfs")] +pub use fs::myfs::MyFileSystemIf; use alloc::vec::Vec; +#[cfg(feature = "blkfs")] use ruxdriver::{prelude::*, AxDeviceContainer}; cfg_if::cfg_if! { @@ -55,18 +94,30 @@ cfg_if::cfg_if! { } else if #[cfg(feature = "fatfs")] { use lazy_init::LazyInit; use alloc::sync::Arc; + // TODO: wait for CI support for ext4 + // } else if #[cfg(feature = "lwext4_rust")] { + // use lazy_init::LazyInit; + // use alloc::sync::Arc; + } else if #[cfg(feature = "ext4_rs")] { + use lazy_init::LazyInit; + use alloc::sync::Arc; + } else if #[cfg(feature = "another_ext4")] { + use lazy_init::LazyInit; + use alloc::sync::Arc; } } +use alloc::string::String; pub use root::MountPoint; /// Initialize an empty filesystems by ramfs. -#[cfg(not(any(feature = "blkfs", feature = "virtio-9p", feature = "net-9p")))] +#[cfg(not(feature = "blkfs"))] pub fn init_tempfs() -> MountPoint { - MountPoint::new("/", mounts::ramfs()) + MountPoint::new(String::from("/"), mounts::ramfs()) } /// Initializes filesystems by block devices. +#[cfg(feature = "blkfs")] pub fn init_blkfs(mut blk_devs: AxDeviceContainer) -> MountPoint { info!("Initialize filesystems..."); @@ -82,39 +133,66 @@ pub fn init_blkfs(mut blk_devs: AxDeviceContainer) -> MountPoint FAT_FS.init_by(Arc::new(fs::fatfs::FatFileSystem::new(disk))); FAT_FS.init(); let blk_fs = FAT_FS.clone(); + // TODO: wait for CI support for ext4 + // } else if #[cfg(feature = "lwext4_rust")] { + // static EXT4_FS: LazyInit> = LazyInit::new(); + // EXT4_FS.init_by(Arc::new(fs::lwext4_rust::Ext4FileSystem::new(disk))); + // let blk_fs = EXT4_FS.clone(); + } else if #[cfg(feature = "ext4_rs")] { + static EXT4_FS: LazyInit> = LazyInit::new(); + EXT4_FS.init_by(Arc::new(fs::ext4_rs::Ext4FileSystem::new(disk))); + let blk_fs = EXT4_FS.clone(); + } else if #[cfg(feature = "another_ext4")] { + static EXT4_FS: LazyInit> = LazyInit::new(); + EXT4_FS.init_by(Arc::new(fs::another_ext4::Ext4FileSystem::new(disk))); + let blk_fs = EXT4_FS.clone(); + } else { + compile_error!("Please enable one of the block filesystems!"); } } - MountPoint::new("/", blk_fs) + MountPoint::new(String::from("/"), blk_fs) } /// Initializes common filesystems. pub fn prepare_commonfs(mount_points: &mut Vec) { #[cfg(feature = "devfs")] - let mount_point = MountPoint::new("/dev", mounts::devfs()); - mount_points.push(mount_point); + { + let mount_point = MountPoint::new(String::from("/dev"), mounts::devfs()); + mount_points.push(mount_point); + } #[cfg(feature = "ramfs")] - let mount_point = MountPoint::new("/tmp", mounts::ramfs()); - mount_points.push(mount_point); + { + let mount_point = MountPoint::new(String::from("/tmp"), mounts::ramfs()); + mount_points.push(mount_point); + } // Mount another ramfs as procfs #[cfg(feature = "procfs")] - let mount_point = MountPoint::new("/proc", mounts::procfs().unwrap()); - mount_points.push(mount_point); + { + let mount_point = MountPoint::new(String::from("/proc"), mounts::procfs().unwrap()); + mount_points.push(mount_point); + } // Mount another ramfs as sysfs #[cfg(feature = "sysfs")] - let mount_point = MountPoint::new("/sys", mounts::sysfs().unwrap()); - mount_points.push(mount_point); + { + let mount_point = MountPoint::new(String::from("/sys"), mounts::sysfs().unwrap()); + mount_points.push(mount_point); + } // Mount another ramfs as etcfs #[cfg(feature = "etcfs")] - let mount_point = MountPoint::new("/etc", mounts::etcfs().unwrap()); - mount_points.push(mount_point); -} + { + let mount_point = MountPoint::new(String::from("/etc"), mounts::etcfs().unwrap()); + mount_points.push(mount_point); + } -/// Initializes root filesystems. -pub fn init_filesystems(mount_points: Vec) { - self::root::init_rootfs(mount_points); + // Mount another ramfs as mntfs + #[cfg(feature = "mntfs")] + { + let mount_point = MountPoint::new(String::from("/mnt"), mounts::mntfs().unwrap()); + mount_points.push(mount_point); + } } diff --git a/modules/ruxfs/src/mounts.rs b/modules/ruxfs/src/mounts.rs index b5d74cae0..05b92cde8 100644 --- a/modules/ruxfs/src/mounts.rs +++ b/modules/ruxfs/src/mounts.rs @@ -8,7 +8,7 @@ */ use alloc::sync::Arc; -use axfs_vfs::{VfsNodeType, VfsOps, VfsResult}; +use axfs_vfs::{RelPath, VfsNodeType, VfsOps, VfsResult}; #[cfg(feature = "alloc")] use crate::arch::{get_cpuinfo, get_meminfo}; @@ -20,11 +20,15 @@ pub(crate) fn devfs() -> Arc { let zero = fs::devfs::ZeroDev; let random = fs::devfs::RandomDev; let urandom = fs::devfs::RandomDev; + let fuse = crate::devfuse::FuseDev::new(); + let pts = fs::devfs::init_pts(); let devfs = fs::devfs::DeviceFileSystem::new(); devfs.add("null", Arc::new(null)); devfs.add("zero", Arc::new(zero)); devfs.add("random", Arc::new(random)); devfs.add("urandom", Arc::new(urandom)); + devfs.add("pts", pts); + devfs.add("fuse", Arc::new(fuse)); Arc::new(devfs) } @@ -35,58 +39,93 @@ pub(crate) fn ramfs() -> Arc { #[cfg(feature = "procfs")] pub(crate) fn procfs() -> VfsResult> { + use axfs_vfs::VfsNodePerm; + let procfs = fs::ramfs::RamFileSystem::new(); let proc_root = procfs.root_dir(); #[cfg(feature = "alloc")] { // Create /proc/cpuinfo - proc_root.create("cpuinfo", VfsNodeType::File)?; - let file_cpuinfo = proc_root.clone().lookup("./cpuinfo")?; + proc_root.create( + &RelPath::new("cpuinfo"), + VfsNodeType::File, + VfsNodePerm::default_file(), + )?; + let file_cpuinfo = proc_root.clone().lookup(&RelPath::new("cpuinfo"))?; file_cpuinfo.write_at(0, get_cpuinfo().as_bytes())?; // Create /proc/meminfo - proc_root.create("meminfo", VfsNodeType::File)?; - let file_meminfo = proc_root.clone().lookup("./meminfo")?; + proc_root.create( + &RelPath::new("meminfo"), + VfsNodeType::File, + VfsNodePerm::default_file(), + )?; + let file_meminfo = proc_root.clone().lookup(&RelPath::new("meminfo"))?; file_meminfo.write_at(0, get_meminfo().as_bytes())?; } // Create /proc/sys/net/core/somaxconn - proc_root.create_recursive("sys/net/core/somaxconn", VfsNodeType::File)?; - let file_somaxconn = proc_root.clone().lookup("./sys/net/core/somaxconn")?; + proc_root.create_recursive( + &RelPath::new("sys/net/core/somaxconn"), + VfsNodeType::File, + VfsNodePerm::default_file(), + )?; + let file_somaxconn = proc_root + .clone() + .lookup(&RelPath::new("sys/net/core/somaxconn"))?; file_somaxconn.write_at(0, b"4096\n")?; // Create /proc/sys/vm/overcommit_memory - proc_root.create_recursive("sys/vm/overcommit_memory", VfsNodeType::File)?; - let file_over = proc_root.clone().lookup("./sys/vm/overcommit_memory")?; + proc_root.create_recursive( + &RelPath::new("sys/vm/overcommit_memory"), + VfsNodeType::File, + VfsNodePerm::default_file(), + )?; + let file_over = proc_root + .clone() + .lookup(&RelPath::new("sys/vm/overcommit_memory"))?; file_over.write_at(0, b"0\n")?; // Create /proc/self/stat - proc_root.create_recursive("self/stat", VfsNodeType::File)?; + proc_root.create_recursive( + &RelPath::new("self/stat"), + VfsNodeType::File, + VfsNodePerm::default_file(), + )?; Ok(Arc::new(procfs)) } #[cfg(feature = "sysfs")] pub(crate) fn sysfs() -> VfsResult> { + use axfs_vfs::VfsNodePerm; + let sysfs = fs::ramfs::RamFileSystem::new(); let sys_root = sysfs.root_dir(); + debug!("sysfs: {:?}", sys_root.get_attr()); + // Create /sys/kernel/mm/transparent_hugepage/enabled - sys_root.create_recursive("kernel/mm/transparent_hugepage/enabled", VfsNodeType::File)?; + sys_root.create_recursive( + &RelPath::new("kernel/mm/transparent_hugepage/enabled"), + VfsNodeType::File, + VfsNodePerm::default_file(), + )?; let file_hp = sys_root .clone() - .lookup("./kernel/mm/transparent_hugepage/enabled")?; + .lookup(&RelPath::new("kernel/mm/transparent_hugepage/enabled"))?; file_hp.write_at(0, b"always [madvise] never\n")?; // Create /sys/devices/system/clocksource/clocksource0/current_clocksource sys_root.create_recursive( - "devices/system/clocksource/clocksource0/current_clocksource", + &RelPath::new("devices/system/clocksource/clocksource0/current_clocksource"), VfsNodeType::File, + VfsNodePerm::default_file(), )?; - let file_cc = sys_root - .clone() - .lookup("devices/system/clocksource/clocksource0/current_clocksource")?; + let file_cc = sys_root.clone().lookup(&RelPath::new( + "devices/system/clocksource/clocksource0/current_clocksource", + ))?; file_cc.write_at(0, b"tsc\n")?; Ok(Arc::new(sysfs)) @@ -94,26 +133,48 @@ pub(crate) fn sysfs() -> VfsResult> { #[cfg(feature = "etcfs")] pub(crate) fn etcfs() -> VfsResult> { + use axfs_vfs::VfsNodePerm; + let etcfs = fs::ramfs::RamFileSystem::new(); let etc_root = etcfs.root_dir(); - // Create /etc/passwd, and /etc/hosts - etc_root.create("passwd", VfsNodeType::File)?; - let file_passwd = etc_root.clone().lookup("passwd")?; + // Create /etc/passwd + etc_root.create( + &RelPath::new("passwd"), + VfsNodeType::File, + VfsNodePerm::default_file(), + )?; + let file_passwd = etc_root.clone().lookup(&RelPath::new("passwd"))?; // format: username:password:uid:gid:allname:homedir:shell - file_passwd.write_at(0, b"root:x:0:0:root:/root:/bin/bash\n")?; + file_passwd.write_at( + 0, + b"root:x:0:0:root:/root:/bin/sh\n\ + syswonder:x:1000:1000:root:/root:/bin/sh\n", + )?; // Create /etc/group - etc_root.create("group", VfsNodeType::File)?; - let file_group = etc_root.clone().lookup("group")?; + etc_root.create( + &RelPath::new("group"), + VfsNodeType::File, + VfsNodePerm::default_file(), + )?; + let file_group = etc_root.clone().lookup(&RelPath::new("group"))?; file_group.write_at(0, b"root:x:0:\n")?; // Create /etc/localtime - etc_root.create("localtime", VfsNodeType::File)?; + etc_root.create( + &RelPath::new("localtime"), + VfsNodeType::File, + VfsNodePerm::default_file(), + )?; // Create /etc/hosts - etc_root.create("hosts", VfsNodeType::File)?; - let file_hosts = etc_root.clone().lookup("hosts")?; + etc_root.create( + &RelPath::new("hosts"), + VfsNodeType::File, + VfsNodePerm::default_file(), + )?; + let file_hosts = etc_root.clone().lookup(&RelPath::new("hosts"))?; file_hosts.write_at( 0, b"127.0.0.1 localhost\n\n\ @@ -125,12 +186,25 @@ pub(crate) fn etcfs() -> VfsResult> { ff02::3 ip6-allhosts\n", )?; + etc_root.create( + &RelPath::new("services"), + VfsNodeType::File, + VfsNodePerm::default_file(), + )?; + let file_services = etc_root.clone().lookup(&RelPath::new("services"))?; + file_services.write_at(0, b"ssh 22/tcp")?; + // Create /etc/resolv.conf - etc_root.create("resolv.conf", VfsNodeType::File)?; - let file_resolv = etc_root.clone().lookup("resolv.conf")?; + etc_root.create( + &RelPath::new("resolv.conf"), + VfsNodeType::File, + VfsNodePerm::default_file(), + )?; + let file_resolv = etc_root.clone().lookup(&RelPath::new("resolv.conf"))?; file_resolv.write_at( 0, - b"nameserver 127.0.0.53\n\ + b"nameserver 8.8.8.8\n\ + nameserver 114.114.114.114\n\ options edns0 trust-ad\n\ search lan\n ", @@ -138,3 +212,32 @@ pub(crate) fn etcfs() -> VfsResult> { Ok(Arc::new(etcfs)) } + +#[cfg(feature = "mntfs")] +pub(crate) fn mntfs() -> VfsResult> { + use axfs_vfs::VfsNodePerm; + + let mntfs = fs::ramfs::RamFileSystem::new(); + let mnt_root = mntfs.root_dir(); + + // Create /mnt/fuse + mnt_root.create( + &RelPath::new("fuse"), + VfsNodeType::Dir, + VfsNodePerm::default_dir(), + )?; + // Create /mnt/exfat + mnt_root.create( + &RelPath::new("exfat"), + VfsNodeType::Dir, + VfsNodePerm::default_dir(), + )?; + // Create /mnt/ext4 + mnt_root.create( + &RelPath::new("ext4"), + VfsNodeType::Dir, + VfsNodePerm::default_dir(), + )?; + + Ok(Arc::new(mntfs)) +} diff --git a/modules/ruxfs/src/root.rs b/modules/ruxfs/src/root.rs index eb19f2c60..c3863f2f5 100644 --- a/modules/ruxfs/src/root.rs +++ b/modules/ruxfs/src/root.rs @@ -7,37 +7,33 @@ * See the Mulan PSL v2 for more details. */ -//! Root directory of the filesystem +//! Root directory of the filesystem, where filesystem operations are distributed to the +//! appropriate filesystem based on the mount points. //! -//! TODO: it doesn't work very well if the mount points have containment relationships. +//! `RootDirectory::lookup_mounted_fs()` performs the distribution of operations. use alloc::{format, string::String, sync::Arc, vec::Vec}; -use axerrno::{ax_err, AxError, AxResult}; -use axfs_vfs::{VfsError, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps, VfsResult}; -use axsync::Mutex; -use lazy_init::LazyInit; - -use crate::api::FileType; - -static CURRENT_DIR_PATH: Mutex = Mutex::new(String::new()); -static CURRENT_DIR: LazyInit> = LazyInit::new(); +use axerrno::{ax_err, AxResult}; +use axfs_vfs::{ + AbsPath, RelPath, VfsError, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, + VfsOps, VfsResult, +}; +use spinlock::SpinNoIrq; /// mount point information +#[derive(Clone)] pub struct MountPoint { - path: &'static str, - fs: Arc, -} - -struct RootDirectory { - main_fs: Arc, - mounts: Vec, + /// mount point path + pub path: String, + /// mounted filesystem + pub fs: Arc, } -static ROOT_DIR: LazyInit> = LazyInit::new(); +// pub(crate) static ROOT_DIR: LazyInit> = LazyInit::new(); impl MountPoint { /// create new MountPoint from data - pub fn new(path: &'static str, fs: Arc) -> Self { + pub fn new(path: String, fs: Arc) -> Self { Self { path, fs } } } @@ -48,73 +44,114 @@ impl Drop for MountPoint { } } +/// Root directory of the main filesystem +pub struct RootDirectory { + main_fs: Arc, + mount_points: SpinNoIrq>, +} + impl RootDirectory { + /// Creates a new `RootDirectory` with the specified main filesystem. pub const fn new(main_fs: Arc) -> Self { Self { main_fs, - mounts: Vec::new(), + mount_points: SpinNoIrq::new(Vec::new()), } } - pub fn mount(&mut self, path: &'static str, fs: Arc) -> AxResult { - if path == "/" { + /// Mount the specified filesystem at the specified path. + pub fn mount(&self, mp: MountPoint) -> AxResult { + info!("Root dir mounting {}", mp.path); + if mp.path == "/" { return ax_err!(InvalidInput, "cannot mount root filesystem"); } - if !path.starts_with('/') { + if !mp.path.starts_with('/') { return ax_err!(InvalidInput, "mount path must start with '/'"); } - if self.mounts.iter().any(|mp| mp.path == path) { + let mut already_mount = self.mount_points.lock(); + if already_mount.iter().any(|m| m.path == mp.path) { return ax_err!(InvalidInput, "mount point already exists"); } + let rel_path = RelPath::new(&mp.path[1..]); // create the mount point in the main filesystem if it does not exist - match self.main_fs.root_dir().lookup(path) { - Ok(_) => {} - Err(err_code) => { - if err_code == VfsError::NotFound { - self.main_fs.root_dir().create(path, FileType::Dir)?; + match self.main_fs.root_dir().lookup(&rel_path) { + Ok(node) => { + if !node.get_attr()?.is_dir() { + return ax_err!(InvalidInput, "mount point is not a directory"); } + // TODO: permission check + } + Err(VfsError::NotFound) => { + self.main_fs.root_dir().create( + &rel_path, + VfsNodeType::Dir, + VfsNodePerm::default_dir(), + )?; + } + Err(e) => { + return Err(e); } } - fs.mount(path, self.main_fs.root_dir().lookup(path)?)?; - self.mounts.push(MountPoint::new(path, fs)); + let parent = if let Some((parent_path, _)) = rel_path.rsplit_once('/') { + self.main_fs.root_dir().lookup(&RelPath::new(parent_path))? + } else { + self.main_fs.root_dir() + }; + // Ensure the parent directory exists + mp.fs.mount(parent)?; + + already_mount.push(mp); Ok(()) } - pub fn _umount(&mut self, path: &str) { - self.mounts.retain(|mp| mp.path != path); + /// Unmount the filesystem at the specified path. + pub fn umount(&self, path: &AbsPath) { + self.mount_points + .lock() + .retain(|mp| mp.path != path.to_string()); } - pub fn contains(&self, path: &str) -> bool { - self.mounts.iter().any(|mp| mp.path == path) + /// Check if path is a mount point + pub fn contains(&self, path: &AbsPath) -> bool { + self.mount_points + .lock() + .iter() + .any(|mp| mp.path == path.to_string()) } - fn lookup_mounted_fs(&self, path: &str, f: F) -> AxResult - where - F: FnOnce(Arc, &str) -> AxResult, - { - debug!("lookup at root: {}", path); - let path = path.trim_matches('/'); - if let Some(rest) = path.strip_prefix("./") { - return self.lookup_mounted_fs(rest, f); - } - + /// Check if path matches a mountpoint, return the index of the matched + /// mountpoint and the matched length. + fn lookup_mounted_fs(&self, path: &RelPath) -> (usize, usize) { + debug!("lookup at root: {path}"); let mut idx = 0; let mut max_len = 0; // Find the filesystem that has the longest mounted path match - // TODO: more efficient, e.g. trie - for (i, mp) in self.mounts.iter().enumerate() { - // skip the first '/' - if path.starts_with(&mp.path[1..]) && mp.path.len() - 1 > max_len { + for (i, mp) in self.mount_points.lock().iter().enumerate() { + let rel_mp = RelPath::new(&mp.path[1..]); + // path must have format: "" or "/..." + if (rel_mp == *path || path.starts_with(&format!("{rel_mp}/"))) + && rel_mp.len() > max_len + { max_len = mp.path.len() - 1; idx = i; } } - if max_len == 0 { - f(self.main_fs.clone(), path) // not matched any mount point + (idx, max_len) + } + + /// Check if path matches a mountpoint, dispatch the operation to the matched filesystem + fn lookup_mounted_fs_then(&self, path: &RelPath, f: F) -> AxResult + where + F: FnOnce(Arc, &RelPath) -> AxResult, + { + let (idx, len) = self.lookup_mounted_fs(path); + if len > 0 { + let mounts = self.mount_points.lock(); + f(mounts[idx].fs.clone(), &RelPath::new_trimmed(&path[len..])) } else { - f(self.mounts[idx].fs.clone(), &path[max_len..]) // matched at `idx` + f(self.main_fs.clone(), path) } } } @@ -126,193 +163,51 @@ impl VfsNodeOps for RootDirectory { self.main_fs.root_dir().get_attr() } - fn lookup(self: Arc, path: &str) -> VfsResult { - self.lookup_mounted_fs(path, |fs, rest_path| fs.root_dir().lookup(rest_path)) + fn set_mode(&self, _mode: VfsNodePerm) -> VfsResult { + Ok(()) } - fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult { - self.lookup_mounted_fs(path, |fs, rest_path| { - if rest_path.is_empty() { - Ok(()) // already exists - } else { - fs.root_dir().create(rest_path, ty) - } - }) + fn lookup(self: Arc, path: &RelPath) -> VfsResult { + self.lookup_mounted_fs_then(path, |fs, rest_path| fs.root_dir().lookup(rest_path)) } - fn remove(&self, path: &str) -> VfsResult { - self.lookup_mounted_fs(path, |fs, rest_path| { + fn create(&self, path: &RelPath, ty: VfsNodeType, mode: VfsNodePerm) -> VfsResult { + self.lookup_mounted_fs_then(path, |fs, rest_path| { if rest_path.is_empty() { - ax_err!(PermissionDenied) // cannot remove mount points + Ok(()) // already exists } else { - fs.root_dir().remove(rest_path) + fs.root_dir().create(rest_path, ty, mode) } }) } - fn rename(&self, src_path: &str, dst_path: &str) -> VfsResult { - self.lookup_mounted_fs(src_path, |fs, rest_path| { + fn unlink(&self, path: &RelPath) -> VfsResult { + self.lookup_mounted_fs_then(path, |fs, rest_path| { if rest_path.is_empty() { - ax_err!(PermissionDenied) // cannot rename mount points + ax_err!(PermissionDenied) // cannot remove mount points } else { - fs.root_dir().rename(rest_path, dst_path) + fs.root_dir().unlink(rest_path) } }) } -} -pub(crate) fn init_rootfs(mount_points: Vec) { - let main_fs = mount_points - .first() - .expect("No filesystem found") - .fs - .clone(); - let mut root_dir = RootDirectory::new(main_fs); - - for mp in mount_points.iter().skip(1) { - let path = mp.path; - let vfsops = mp.fs.clone(); - let message = format!("failed to mount filesystem at {}", path); - info!("mounting {}", path); - root_dir.mount(path, vfsops).expect(&message); - } - - ROOT_DIR.init_by(Arc::new(root_dir)); - CURRENT_DIR.init_by(Mutex::new(ROOT_DIR.clone())); - *CURRENT_DIR_PATH.lock() = "/".into(); -} - -fn parent_node_of(dir: Option<&VfsNodeRef>, path: &str) -> VfsNodeRef { - if path.starts_with('/') { - ROOT_DIR.clone() - } else { - dir.cloned().unwrap_or_else(|| CURRENT_DIR.lock().clone()) - } -} - -pub(crate) fn absolute_path(path: &str) -> AxResult { - if path.starts_with('/') { - Ok(axfs_vfs::path::canonicalize(path)) - } else { - let path = CURRENT_DIR_PATH.lock().clone() + path; - Ok(axfs_vfs::path::canonicalize(&path)) - } -} - -pub(crate) fn lookup(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - if path.is_empty() { - return ax_err!(NotFound); - } - let node = parent_node_of(dir, path).lookup(path)?; - if path.ends_with('/') && !node.get_attr()?.is_dir() { - ax_err!(NotADirectory) - } else { - Ok(node) - } -} - -pub(crate) fn create_file(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - if path.is_empty() { - return ax_err!(NotFound); - } else if path.ends_with('/') { - return ax_err!(NotADirectory); - } - let parent = parent_node_of(dir, path); - parent.create(path, VfsNodeType::File)?; - parent.lookup(path) -} - -pub(crate) fn create_dir(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - match lookup(dir, path) { - Ok(_) => ax_err!(AlreadyExists), - Err(AxError::NotFound) => parent_node_of(dir, path).create(path, VfsNodeType::Dir), - Err(e) => Err(e), - } -} - -pub(crate) fn create_dir_all(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - match lookup(dir, path) { - Ok(_) => ax_err!(AlreadyExists), - Err(AxError::NotFound) => { - parent_node_of(dir, path).create_recursive(path, VfsNodeType::Dir) + fn rename(&self, src_path: &RelPath, dst_path: &RelPath) -> VfsResult { + let (src_idx, src_len) = self.lookup_mounted_fs(src_path); + let (dst_idx, dst_len) = self.lookup_mounted_fs(dst_path); + if src_idx != dst_idx { + return ax_err!(PermissionDenied); // cannot rename across mount points + } + if src_path.len() == src_len { + return ax_err!(PermissionDenied); // cannot rename mount points + } + if src_len > 0 { + let mounts = self.mount_points.lock(); + mounts[src_idx].fs.root_dir().rename( + &RelPath::new_trimmed(&src_path[src_len..]), + &RelPath::new_trimmed(&dst_path[dst_len..]), + ) + } else { + self.main_fs.root_dir().rename(src_path, dst_path) } - Err(e) => Err(e), - } -} - -pub(crate) fn remove_file(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - let node = lookup(dir, path)?; - let attr = node.get_attr()?; - if attr.is_dir() { - ax_err!(IsADirectory) - } else if !attr.perm().owner_writable() { - ax_err!(PermissionDenied) - } else { - parent_node_of(dir, path).remove(path) - } -} - -pub(crate) fn remove_dir(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - if path.is_empty() { - return ax_err!(NotFound); - } - let path_check = path.trim_matches('/'); - if path_check.is_empty() { - return ax_err!(DirectoryNotEmpty); // rm -d '/' - } else if path_check == "." - || path_check == ".." - || path_check.ends_with("/.") - || path_check.ends_with("/..") - { - return ax_err!(InvalidInput); - } - if ROOT_DIR.contains(&absolute_path(path)?) { - return ax_err!(PermissionDenied); - } - - let node = lookup(dir, path)?; - let attr = node.get_attr()?; - if !attr.is_dir() { - ax_err!(NotADirectory) - } else if !attr.perm().owner_writable() { - ax_err!(PermissionDenied) - } else { - parent_node_of(dir, path).remove(path) - } -} - -pub(crate) fn current_dir() -> AxResult { - Ok(CURRENT_DIR_PATH.lock().clone()) -} - -pub(crate) fn set_current_dir(path: &str) -> AxResult { - let mut abs_path = absolute_path(path)?; - if !abs_path.ends_with('/') { - abs_path += "/"; - } - if abs_path == "/" { - *CURRENT_DIR.lock() = ROOT_DIR.clone(); - *CURRENT_DIR_PATH.lock() = "/".into(); - return Ok(()); - } - - let node = lookup(None, &abs_path)?; - let attr = node.get_attr()?; - if !attr.is_dir() { - ax_err!(NotADirectory) - } else if !attr.perm().owner_executable() { - ax_err!(PermissionDenied) - } else { - *CURRENT_DIR.lock() = node; - *CURRENT_DIR_PATH.lock() = abs_path; - Ok(()) - } -} - -pub(crate) fn rename(old: &str, new: &str) -> AxResult { - if parent_node_of(None, new).lookup(new).is_ok() { - warn!("dst file already exist, now remove it"); - remove_file(None, new)?; } - parent_node_of(None, old).rename(old, new) } diff --git a/modules/ruxfs/tests/test_common/mod.rs b/modules/ruxfs/tests/test_common/mod.rs deleted file mode 100644 index 2eee93c9d..000000000 --- a/modules/ruxfs/tests/test_common/mod.rs +++ /dev/null @@ -1,262 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use axio as io; -use ruxfs::api as fs; - -use fs::{File, FileType, OpenOptions}; -use io::{prelude::*, Error, Result}; - -macro_rules! assert_err { - ($expr: expr) => { - assert!(($expr).is_err()) - }; - ($expr: expr, $err: ident) => { - assert_eq!(($expr).err(), Some(Error::$err)) - }; -} - -fn test_read_write_file() -> Result<()> { - let fname = "///very/long//.././long//./path/./test.txt"; - println!("read and write file {:?}:", fname); - - // read and write - let mut file = File::options().read(true).write(true).open(fname)?; - let file_size = file.metadata()?.len(); - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - print!("{}", contents); - assert_eq!(contents.len(), file_size as usize); - assert_eq!(file.write(b"Hello, world!\n")?, 14); // append - drop(file); - - // read again and check - let new_contents = fs::read_to_string(fname)?; - print!("{}", new_contents); - assert_eq!(new_contents, contents + "Hello, world!\n"); - - // append and check - let mut file = OpenOptions::new().append(true).open(fname)?; - assert_eq!(file.write(b"new line\n")?, 9); - drop(file); - - let new_contents2 = fs::read_to_string(fname)?; - print!("{}", new_contents2); - assert_eq!(new_contents2, new_contents + "new line\n"); - - // open a non-exist file - assert_err!(File::open("/not/exist/file"), NotFound); - - println!("test_read_write_file() OK!"); - Ok(()) -} - -fn test_read_dir() -> Result<()> { - let dir = "/././//./"; - println!("list directory {:?}:", dir); - for entry in fs::read_dir(dir)? { - let entry = entry?; - println!(" {}", entry.file_name()); - } - println!("test_read_dir() OK!"); - Ok(()) -} - -fn test_file_permission() -> Result<()> { - let fname = "./short.txt"; - println!("test permission {:?}:", fname); - - // write a file that open with read-only mode - let mut buf = [0; 256]; - let mut file = File::open(fname)?; - let n = file.read(&mut buf)?; - assert_err!(file.write(&buf), PermissionDenied); - drop(file); - - // read a file that open with write-only mode - let mut file = File::create(fname)?; - assert_err!(file.read(&mut buf), PermissionDenied); - assert!(file.write(&buf[..n]).is_ok()); - drop(file); - - // open with empty options - assert_err!(OpenOptions::new().open(fname), InvalidInput); - - // read as a directory - assert_err!(fs::read_dir(fname), NotADirectory); - assert_err!(fs::read("short.txt/"), NotADirectory); - assert_err!(fs::metadata("/short.txt/"), NotADirectory); - - // create as a directory - assert_err!(fs::write("error/", "should not create"), NotADirectory); - assert_err!(fs::metadata("error/"), NotFound); - assert_err!(fs::metadata("error"), NotFound); - - // read/write a directory - assert_err!(fs::read_to_string("/dev"), IsADirectory); - assert_err!(fs::write(".", "test"), IsADirectory); - - println!("test_file_permisson() OK!"); - Ok(()) -} - -fn test_create_file_dir() -> Result<()> { - // create a file and test existence - let fname = "././/very-long-dir-name/..///new-file.txt"; - println!("test create file {:?}:", fname); - assert_err!(fs::metadata(fname), NotFound); - let contents = "create a new file!\n"; - fs::write(fname, contents)?; - - let dirents = fs::read_dir(".")? - .map(|e| e.unwrap().file_name()) - .collect::>(); - println!("dirents = {:?}", dirents); - assert!(dirents.contains(&"new-file.txt".into())); - assert_eq!(fs::read_to_string(fname)?, contents); - assert_err!(File::create_new(fname), AlreadyExists); - - // create a directory and test existence - let dirname = "///././/very//.//long/./new-dir"; - println!("test create dir {:?}:", dirname); - assert_err!(fs::metadata(dirname), NotFound); - fs::create_dir(dirname)?; - - let dirents = fs::read_dir("./very/long")? - .map(|e| e.unwrap().file_name()) - .collect::>(); - println!("dirents = {:?}", dirents); - assert!(dirents.contains(&"new-dir".into())); - assert!(fs::metadata(dirname)?.is_dir()); - assert_err!(fs::create_dir(dirname), AlreadyExists); - - println!("test_create_file_dir() OK!"); - Ok(()) -} - -fn test_remove_file_dir() -> Result<()> { - // remove a file and test existence - let fname = "//very-long-dir-name/..///new-file.txt"; - println!("test remove file {:?}:", fname); - assert_err!(fs::remove_dir(fname), NotADirectory); - assert!(fs::remove_file(fname).is_ok()); - assert_err!(fs::metadata(fname), NotFound); - assert_err!(fs::remove_file(fname), NotFound); - - // remove a directory and test existence - let dirname = "very//.//long/../long/.//./new-dir////"; - println!("test remove dir {:?}:", dirname); - assert_err!(fs::remove_file(dirname), IsADirectory); - assert!(fs::remove_dir(dirname).is_ok()); - assert_err!(fs::metadata(dirname), NotFound); - assert_err!(fs::remove_dir(fname), NotFound); - - // error cases - assert_err!(fs::remove_file(""), NotFound); - assert_err!(fs::remove_dir("/"), DirectoryNotEmpty); - assert_err!(fs::remove_dir("."), InvalidInput); - assert_err!(fs::remove_dir("../"), InvalidInput); - assert_err!(fs::remove_dir("./././/"), InvalidInput); - assert_err!(fs::remove_file("///very/./"), IsADirectory); - assert_err!(fs::remove_file("short.txt/"), NotADirectory); - assert_err!(fs::remove_dir(".///"), InvalidInput); - assert_err!(fs::remove_dir("/./very///"), DirectoryNotEmpty); - assert_err!(fs::remove_dir("very/long/.."), InvalidInput); - - println!("test_remove_file_dir() OK!"); - Ok(()) -} - -fn test_devfs_ramfs() -> Result<()> { - const N: usize = 32; - let mut buf = [1; N]; - - // list '/' and check if /dev and /tmp exist - let dirents = fs::read_dir("././//.//")? - .map(|e| e.unwrap().file_name()) - .collect::>(); - assert!(dirents.contains(&"dev".into())); - assert!(dirents.contains(&"tmp".into())); - - // read and write /dev/null - let mut file = File::options().read(true).write(true).open("/dev/./null")?; - assert_eq!(file.read_to_end(&mut Vec::new())?, 0); - assert_eq!(file.write(&buf)?, N); - assert_eq!(buf, [1; N]); - - // read and write /dev/zero - let mut file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .truncate(true) - .open("////dev/zero")?; - assert_eq!(file.read(&mut buf)?, N); - assert!(file.write_all(&buf).is_ok()); - assert_eq!(buf, [0; N]); - - // list /dev - let dirents = fs::read_dir("/dev")? - .map(|e| e.unwrap().file_name()) - .collect::>(); - assert!(dirents.contains(&"null".into())); - assert!(dirents.contains(&"zero".into())); - - // stat /dev - let dname = "/dev"; - let dir = File::open(dname)?; - let md = dir.metadata()?; - println!("metadata of {:?}: {:?}", dname, md); - assert_eq!(md.file_type(), FileType::Dir); - assert!(!md.is_file()); - assert!(md.is_dir()); - - // error cases - assert_err!(fs::metadata("/dev/null/"), NotADirectory); - assert_err!(fs::create_dir("dev"), AlreadyExists); - assert_err!(File::create_new("/dev/"), AlreadyExists); - assert_err!(fs::create_dir("/dev/zero"), AlreadyExists); - assert_err!(fs::write("/dev/stdout", "test"), PermissionDenied); - assert_err!(fs::create_dir("/dev/test"), PermissionDenied); - assert_err!(fs::remove_file("/dev/null"), PermissionDenied); - assert_err!(fs::remove_dir("./dev"), PermissionDenied); - assert_err!(fs::remove_dir("./dev/."), InvalidInput); - assert_err!(fs::remove_dir("///dev//..//"), InvalidInput); - - // parent of '/dev' - assert_eq!(fs::create_dir("///dev//..//233//"), Ok(())); - assert_eq!(fs::write(".///dev//..//233//.///test.txt", "test"), Ok(())); - assert_err!(fs::remove_file("./dev//../..//233//.///test.txt"), NotFound); - assert_eq!(fs::remove_file("./dev//..//233//../233/./test.txt"), Ok(())); - assert_err!(fs::remove_dir("very/../dev//"), PermissionDenied); - - // tests in /tmp - assert_eq!(fs::metadata("tmp")?.file_type(), FileType::Dir); - assert_eq!(fs::create_dir(".///tmp///././dir"), Ok(())); - assert_eq!(fs::read_dir("tmp").unwrap().count(), 1); - assert_eq!(fs::write(".///tmp///dir//.///test.txt", "test"), Ok(())); - assert_eq!(fs::read("tmp//././/dir//.///test.txt"), Ok("test".into())); - // assert_err!(fs::remove_dir("dev/../tmp//dir"), DirectoryNotEmpty); // TODO - assert_err!(fs::remove_dir("/tmp/dir/../dir"), DirectoryNotEmpty); - assert_eq!(fs::remove_file("./tmp//dir//test.txt"), Ok(())); - assert_eq!(fs::remove_dir("tmp/dir/.././dir///"), Ok(())); - assert_eq!(fs::read_dir("tmp").unwrap().count(), 0); - - println!("test_devfs_ramfs() OK!"); - Ok(()) -} - -pub fn test_all() { - test_read_write_file().expect("test_read_write_file() failed"); - test_read_dir().expect("test_read_dir() failed"); - test_file_permission().expect("test_file_permission() failed"); - test_create_file_dir().expect("test_create_file_dir() failed"); - test_remove_file_dir().expect("test_remove_file_dir() failed"); - test_devfs_ramfs().expect("test_devfs_ramfs() failed"); -} diff --git a/modules/ruxfuse/Cargo.toml b/modules/ruxfuse/Cargo.toml new file mode 100644 index 000000000..de35bc860 --- /dev/null +++ b/modules/ruxfuse/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "ruxfuse" +version = "0.1.0" +edition = "2021" +authors = ["Zheng Yuanhao "] +description = "RuxOS fuse module" +license = "Mulan PSL v2" +homepage = "https://github.com/syswonder/ruxos" +repository = "https://github.com/syswonder/ruxos/tree/main/modules/ruxfuse" + +[features] +alloc = ["axalloc"] +multitask = ["ruxtask/multitask"] + +default = [] + +[dependencies] +log = "0.4" +spin = "0.9" +cfg-if = "1.0" +lazy_init = { path = "../../crates/lazy_init" } +capability = { path = "../../crates/capability" } +driver_block = { path = "../../crates/driver_block" } +axfs_vfs = { path = "../../crates/axfs_vfs" } +axfs_devfs = { path = "../../crates/axfs_devfs", optional = true } +axfs_ramfs = { path = "../../crates/axfs_ramfs", optional = true } +spinlock = { path = "../../crates/spinlock" } +crate_interface = { version = "0.1.1" } +memory_addr = "0.1.0" + +ruxdriver = { path = "../ruxdriver", features = ["block"] } +axalloc = { path = "../axalloc", optional = true } +ruxfs = { path = "../ruxfs" } +ruxtask = { path = "../ruxtask" } \ No newline at end of file diff --git a/modules/ruxfuse/src/fuse.rs b/modules/ruxfuse/src/fuse.rs new file mode 100644 index 000000000..9175bfe5f --- /dev/null +++ b/modules/ruxfuse/src/fuse.rs @@ -0,0 +1,2887 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! FUSE filesystem used by [RuxOS](https://github.com/syswonder/ruxos). +//! +//! The implementation is based on [`axfs_vfs`]. + +use alloc::string::{String, ToString}; +use alloc::sync::{Arc, Weak}; +use alloc::vec::Vec; +use core::sync::atomic::{AtomicI32, AtomicU64, Ordering}; +use log::*; +use ruxtask::{current, WaitQueue}; +use spinlock::SpinNoIrq; + +use axfs_vfs::{RelPath, VfsDirEntry, VfsError, VfsResult}; +use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, VfsOps}; +use ruxfs::devfuse::{FUSEFLAG, FUSE_VEC}; +use ruxfs::fuse_st::{ + FuseAccessIn, FuseAttr, FuseAttrOut, FuseCreateIn, FuseDirent, FuseEntryOut, FuseFlushIn, + FuseForgetIn, FuseGetattrIn, FuseInHeader, FuseInitIn, FuseInitOut, FuseLseekIn, FuseLseekOut, + FuseMkdirIn, FuseMknodIn, FuseOpcode, FuseOpenIn, FuseOpenOut, FuseOutHeader, FuseReadIn, + FuseReleaseIn, FuseRename2In, FuseRenameIn, FuseStatfsOut, FuseWriteIn, FuseWriteOut, +}; +use spin::{once::Once, RwLock}; + +/// Unique id for FUSE operations. +pub static UNIQUE_ID: AtomicU64 = AtomicU64::new(0); +/// A flag for FuseRename operation. +pub static NEWID: AtomicI32 = AtomicI32::new(-1); +/// A flag to indicate whether FUSE is initialized. +pub static INITFLAG: AtomicI32 = AtomicI32::new(1); +/// A static wait queue for FUSE operations. +pub static WQ: WaitQueue = WaitQueue::new(); + +/// It implements [`axfs_vfs::VfsOps`]. +pub struct FuseFS { + parent: Once, + root: Arc, +} + +impl Default for FuseFS { + fn default() -> Self { + Self::new() + } +} + +impl FuseFS { + /// Create a new instance. + pub fn new() -> Self { + debug!("fusefs new..."); + Self { + parent: Once::new(), + root: FuseNode::new(None, 1, FuseAttr::default(), 0, 0), + } + } +} + +impl VfsOps for FuseFS { + fn mount(&self, parent: VfsNodeRef) -> VfsResult { + self.root.set_parent(Some(self.parent.call_once(|| parent))); + Ok(()) + } + + fn umount(&self) -> VfsResult { + debug!("fusefs umount..."); + self.root.destroy() + } + + fn root_dir(&self) -> VfsNodeRef { + debug!("fusefs root_dir..."); + self.root.clone() + } +} + +/// It implements [`axfs_vfs::VfsNodeOps`]. +pub struct FuseNode { + this: Weak, + parent: RwLock>, + inode: SpinNoIrq, + attr: SpinNoIrq, + nlink: SpinNoIrq, + flags: SpinNoIrq, + fh: SpinNoIrq, +} + +impl FuseNode { + pub(super) fn new( + parent: Option>, + inode: u64, + attr: FuseAttr, + nlink: u32, + fh: u64, + ) -> Arc { + debug!("fuse_node new inode: {inode:?}, nlink: {nlink:?}"); + Arc::new_cyclic(|this| Self { + this: this.clone(), + parent: RwLock::new(parent.unwrap_or_else(|| Weak::::new())), + inode: SpinNoIrq::new(inode), + attr: SpinNoIrq::new(attr), + nlink: SpinNoIrq::new(nlink), + flags: SpinNoIrq::new(0x8000), + fh: SpinNoIrq::new(fh), + }) + } + + pub(super) fn set_parent(&self, parent: Option<&VfsNodeRef>) { + debug!("fuse_node set_parent..."); + *self.parent.write() = parent.map_or(Weak::::new() as _, Arc::downgrade); + } + + /// Get inode of this FuseNode. + pub fn get_node_inode(&self) -> u64 { + let inode_guard = self.inode.lock(); + *inode_guard + } + + /// Get attr of this FuseNode. + pub fn get_node_attr(&self) -> FuseAttr { + let attr_guard = self.attr.lock(); + *attr_guard + } + + /// Get nlink of this FuseNode. + pub fn get_node_nlink(&self) -> u32 { + let nlink_guard = self.nlink.lock(); + *nlink_guard + } + + /// Get flags of this FuseNode. + pub fn get_node_flags(&self) -> u32 { + let flags_guard = self.flags.lock(); + *flags_guard + } + + /// Get file handle (fh) of this FuseNode. + pub fn get_fh(&self) -> u64 { + let fh_guard = self.fh.lock(); + *fh_guard + } + + /// Set inode of this FuseNode. + pub fn set_node_inode(&self, inode: u64) { + let mut inode_guard = self.inode.lock(); + *inode_guard = inode; + } + + /// Set attr of this FuseNode. + pub fn set_node_attr(&self, attr: FuseAttr) { + let mut attr_guard = self.attr.lock(); + *attr_guard = attr; + } + + /// Set nlink of this FuseNode. + pub fn set_node_nlink(&self, nlink: u32) { + let mut nlink_guard = self.nlink.lock(); + *nlink_guard = nlink; + } + + /// Set flags of this FuseNode. + pub fn set_node_flags(&self, flags: u32) { + let mut flags_guard = self.flags.lock(); + *flags_guard = flags; + } + + /// Set file handle (fh) of this FuseNode. + pub fn set_fh(&self, fh: u64) { + let mut fh_guard = self.fh.lock(); + *fh_guard = fh; + } + + /// Get inode of this FuseNode. + pub fn find_inode(&self, path: &str) -> Option { + let (mut name, mut raw_rest) = split_path(path); + if raw_rest.is_none() { + return self.get_inode(); + } + let mut node = self.try_get(&RelPath::new(".")).unwrap(); + while raw_rest.is_some() { + let rest = raw_rest.unwrap(); + if !name.is_empty() && name != "." { + node = node.lookup(&RelPath::new(name)).unwrap(); + } + (name, raw_rest) = split_path(rest); + } + + node.get_inode() + } + + /// Get final name of the path. + #[allow(clippy::only_used_in_recursion)] + pub fn get_final_name(&self, path: &str) -> Option { + let (name, rest) = split_path(path); + if rest.is_none() { + return Some(name.to_string()); + } + self.get_final_name(rest.unwrap()) + } + + /// Check if the node is a directory. + pub fn is_dir(&self) -> bool { + let attr_guard = self.attr.lock(); + let attr = &*attr_guard; + let mode = attr.get_mode(); + // 0x8124 => file ( can read => 0x8000 + // 0x81a4 => file ( can read and write => 0x8001 + // 0x41ed => directory ( can read and execute + // S_IFDIR = 0x4000 + // S_IFREG = 0x8000 + mode & 0x4000 == 0x4000 + } + + /// Get file flags for this FuseNode. + pub fn file_flags(&self) -> u32 { + let attr_guard = self.attr.lock(); + let attr = &*attr_guard; + let mode = attr.get_mode(); + match mode & 0x1c0 { + 0x80 => 0x8001, + 0x100 => 0x8000, // 0x8124 + 0x180 => 0x8001, // 0x81a4 + 0x1c0 => 0x8002, // 0x81ed? + _ => 0x8000, + } + } + + /// Get directory flags for this FuseNode. + pub fn dir_flags(&self) -> u32 { + let attr_guard = self.attr.lock(); + let attr = &*attr_guard; + let mode = attr.get_mode(); + match mode & 0x1c0 { + 0x20 => 0x18801, // O_WRONLY + 0x40 => 0x18800, // O_RDONLY + 0x80 => 0x18802, // O_RDWR + _ => 0x18800, + } + } + + /// Check if already initialized. + pub fn check_init(&self) { + let f1 = INITFLAG.load(Ordering::SeqCst); + if f1 == 1 { + INITFLAG.store(0, Ordering::Relaxed); + UNIQUE_ID.store(0, Ordering::Relaxed); + self.init(); + } + } + + /// FuseInit = 26 + pub fn init(&self) { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node INIT({:?}) here...", + FuseOpcode::FuseInit as u32 + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + debug!("pid = {pid:?}, inode = {nodeid:?}"); + let fusein = FuseInHeader::new( + 104, + FuseOpcode::FuseInit as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 104]; + fusein.write_to(&mut fusebuf); + let initin = FuseInitIn::new(7, 38, 0x00020000, 0x33fffffb, 0, [0; 11]); + initin.write_to(&mut fusebuf[40..]); + fusein.print(); + initin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at init in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseInit as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at init is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 80]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to init: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + // init_flag: 0x40F039 + let initout = FuseInitOut::read_from(&outbuf[16..]); + initout.print(); + + if initout.get_major() != 7 || initout.get_minor() != 38 { + warn!( + "fuse_node init unsupport version, major = {:?}, minor = {:?}", + initout.get_major(), + initout.get_minor() + ); + } else if initout.get_flags() != 0x40f039 { + warn!( + "fuse_node init unsupport flags = {:#x}", + initout.get_flags() + ); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node init finish successfully..."); + } + + /// FuseLookup = 1 + fn try_get(&self, path: &RelPath) -> VfsResult { + self.check_init(); + + let (name, raw_rest) = split_path(path); + if let Some(rest) = raw_rest { + if name.is_empty() || name == "." { + return self.try_get(&RelPath::new(rest)); + } + let node = self.try_get(&RelPath::new(name))?; + return node.lookup(&RelPath::new(rest)); + } + + debug!( + "\nNEW FUSE REQUEST:\n fuse_node LOOKUP({:?}) {:?} here...", + FuseOpcode::FuseLookup as u32, + path + ); + + let lookup_error; + let mut entryout = FuseEntryOut::default(); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let path_len = path.len(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 41 + path_len as u32, + FuseOpcode::FuseLookup as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 180]; + fusein.write_to(&mut fusebuf[0..40]); + fusebuf[40..40 + path_len].copy_from_slice(path.as_bytes()); + fusein.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at lookup in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseLookup as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at lookup is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 144]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to lookup: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf[..16]); + fuseout.print(); + + if fuseout.is_ok() { + entryout = FuseEntryOut::read_from(&outbuf[16..]); + entryout.print(); + lookup_error = 1; + } else { + lookup_error = fuseout.error(); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node lookup finish successfully..."); + + if lookup_error < 0 { + match lookup_error { + -2 => return Err(VfsError::NotFound), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + match name { + "" | "." => { + self.set_node_inode(entryout.get_nodeid()); + self.set_node_attr(entryout.get_attr()); + self.set_node_nlink(entryout.get_nlink()); + let parent = match self.parent() { + Some(_) => Some(Arc::downgrade(&self.parent().unwrap())), + None => None, + }; + let node = FuseNode::new( + parent, + entryout.get_nodeid(), + entryout.get_attr(), + entryout.get_nlink(), + 0, + ); + Ok(node) + } + ".." => { + let node = FuseNode::new( + None, + entryout.get_nodeid(), + entryout.get_attr(), + entryout.get_nlink(), + 0, + ); + Ok(node) + } + _ => { + let node = FuseNode::new( + Some(self.this.clone()), + entryout.get_nodeid(), + entryout.get_attr(), + entryout.get_nlink(), + 0, + ); + Ok(node) + } + } + } + + /// FuseOpendir = 27 + pub fn open_dir(&self) -> Result>, VfsError> { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node OPENDIR({:?}) here...", + FuseOpcode::FuseOpendir as u32 + ); + + let opendir_error; + let mut opendirout = FuseOpenOut::default(); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 48, + FuseOpcode::FuseOpendir as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 48]; + fusein.write_to(&mut fusebuf); + let openin = FuseOpenIn::new(0x18800, 0); + openin.write_to(&mut fusebuf[40..]); + fusein.print(); + openin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at open_dir in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseOpendir as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at open_dir is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 32]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to open_dir: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + if fuseout.is_ok() { + opendirout = FuseOpenOut::read_from(&outbuf[16..]); + opendirout.print(); + opendir_error = 1; + } else { + opendir_error = fuseout.error(); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node open_dir finish successfully..."); + + if opendir_error < 0 { + match opendir_error { + -13 => return Err(VfsError::PermissionDenied), + -20 => return Err(VfsError::NotADirectory), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + let mut fh_guard = self.fh.lock(); + let fh = &mut *fh_guard; + *fh = opendirout.get_fh(); + + debug!("fh = {fh:#x}"); + debug!("opendirout.fh = {:#x}", opendirout.get_fh()); + + Ok(None) + } + + /// FuseReleasedir = 28 + pub fn release_dir(&self) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node RELEASEDIR({:?}) here...", + FuseOpcode::FuseReleasedir as u32 + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 64, + FuseOpcode::FuseReleasedir as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 64]; + fusein.write_to(&mut fusebuf); + let releasein = FuseReleaseIn::new(fh, 0x18800, 0, 0); + releasein.write_to(&mut fusebuf[40..]); + fusein.print(); + releasein.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at release_dir in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseReleasedir as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at release_dir is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 16]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to release_dir: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let releasedir_error = if fuseout.is_ok() { 1 } else { fuseout.error() }; + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node release_dir finish successfully..."); + + if releasedir_error < 0 { + match releasedir_error { + -13 => Err(VfsError::PermissionDenied), + -20 => Err(VfsError::NotADirectory), + -38 => Err(VfsError::FunctionNotImplemented), + _ => Err(VfsError::PermissionDenied), + } + } else { + Ok(()) + } + } + + /// FuseForget = 2 + pub fn forget(&self) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node FORGET({:?}) here...", + FuseOpcode::FuseForget as u32 + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 48, + FuseOpcode::FuseForget as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 48]; + fusein.write_to(&mut fusebuf); + let forgetin = FuseForgetIn::new(4); + forgetin.write_to(&mut fusebuf[40..]); + fusein.print(); + forgetin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at forget in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseForget as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at forget is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 16]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to forget: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let forget_error = if fuseout.is_ok() { 1 } else { fuseout.error() }; + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node forget finish successfully..."); + + if forget_error < 0 { + match forget_error { + -13 => Err(VfsError::PermissionDenied), + -38 => Err(VfsError::FunctionNotImplemented), + _ => Err(VfsError::PermissionDenied), + } + } else { + Ok(()) + } + } + + /// FuseSetattr = 4 + pub fn set_attr(&self, attr: &FuseAttr, to_set: u32) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node SETATTR({:?}) here...", + FuseOpcode::FuseSetattr as u32 + ); + + let setattr_error; + let mut attrout = FuseAttrOut::default(); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 128, + FuseOpcode::FuseSetattr as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 128]; + fusein.write_to(&mut fusebuf); + let mut attrin = FuseAttr::default(); + if to_set & 0x1 != 0 { + attrin.set_mode(attr.get_mode()); + } + if to_set & 0x2 != 0 { + attrin.set_uid(attr.get_uid()); + } + if to_set & 0x4 != 0 { + attrin.set_gid(attr.get_gid()); + } + if to_set & 0x8 != 0 { + attrin.set_size(attr.get_size()); + } + if to_set & 0x10 != 0 { + attrin.set_atime(attr.get_atime()); + } + if to_set & 0x20 != 0 { + attrin.set_mtime(attr.get_mtime()); + } + if to_set & 0x40 != 0 { + attrin.set_ctime(attr.get_ctime()); + } + if to_set & 0x80 != 0 { + attrin.set_atimensec(attr.get_atimensec()); + } + if to_set & 0x100 != 0 { + attrin.set_mtimensec(attr.get_mtimensec()); + } + if to_set & 0x200 != 0 { + attrin.set_ctimensec(attr.get_ctimensec()); + } + attrin.write_to(&mut fusebuf[40..]); + fusein.print(); + attrin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at setattr in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseSetattr as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at setattr is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 120]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to setattr: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + if fuseout.is_ok() { + attrout = FuseAttrOut::read_from(&outbuf[16..]); + attrout.print(); + setattr_error = 1; + } else { + setattr_error = fuseout.error(); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node setattr finish successfully..."); + + if setattr_error < 0 { + match setattr_error { + -13 => return Err(VfsError::PermissionDenied), + -22 => return Err(VfsError::InvalidInput), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + if attrout.get_attr_valid() != 0 { + let mut attr_guard = self.attr.lock(); + *attr_guard = attrout.get_attr(); + } + + Ok(()) + } + + /// FuseReadlink = 5 + pub fn readlink(&self) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node READLINK({:?}) here...", + FuseOpcode::FuseReadlink as u32 + ); + + let readlink_error; + let mut readlinkout = String::new(); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 40, + FuseOpcode::FuseReadlink as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 40]; + fusein.write_to(&mut fusebuf); + fusein.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at readlink in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseReadlink as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at readlink is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 144]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to readlink: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + if fuseout.is_ok() { + readlinkout = String::from_utf8_lossy(&outbuf[16..]).to_string(); + readlink_error = 1; + } else { + readlink_error = fuseout.error(); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node readlink finish successfully..."); + + if readlink_error < 0 { + match readlink_error { + -13 => return Err(VfsError::PermissionDenied), + -22 => return Err(VfsError::InvalidInput), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + Ok(readlinkout) + } + + /// FuseSymlink = 6 + pub fn symlink(&self, name: &RelPath, link: &RelPath) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node SYMLINK({:?}) {:?} link to {:?} here...", + FuseOpcode::FuseSymlink as u32, + name, + link + ); + + let symlink_error; + let symlinkout; + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let name_len = name.len(); + let link_len = link.len(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 42 + (name_len + link_len) as u32, + FuseOpcode::FuseSymlink as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 280]; + fusein.write_to(&mut fusebuf); + fusebuf[40..40 + name_len].copy_from_slice(name.as_bytes()); + fusebuf[41 + name_len..41 + name_len + link_len].copy_from_slice(link.as_bytes()); + fusein.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at symlink in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseSymlink as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at symlink is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 144]; + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to symlink: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + + if fuseout.is_ok() { + symlinkout = FuseEntryOut::read_from(&outbuf[16..]); + symlinkout.print(); + symlink_error = 1; + } else { + symlink_error = fuseout.error(); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node symlink finish successfully..."); + + if symlink_error < 0 { + match symlink_error { + -13 => return Err(VfsError::PermissionDenied), + -17 => return Err(VfsError::AlreadyExists), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + Ok(()) + } + + /// FuseMknod = 8 + pub fn mknod(&self, name: &RelPath, ty: VfsNodeType) -> VfsResult { + let newtype = match ty { + VfsNodeType::Fifo => "fifo", + VfsNodeType::CharDevice => "char device", + VfsNodeType::BlockDevice => "block device", + VfsNodeType::SymLink => "symlink", + VfsNodeType::Socket => "socket", + _ => "unknown", + }; + debug!( + "\nNEW FUSE REQUEST:\n fuse_node MKNOD({:?}) {:?} here, type: {:?}...", + FuseOpcode::FuseMknod as u32, + name, + newtype + ); + + // panic!("fuse_node mknod not implemented yet..."); + let mknod_error; + let mknodout; + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let name_len = name.len(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 57 + name_len as u32, + FuseOpcode::FuseMknod as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 180]; + fusein.write_to(&mut fusebuf); + + // char c 10 0: mode: 0x21a4, rdev: 0xa00, umask: 18 + // block b 8 0: mode: 0x61a4, rdev: 0x800, umask: 18 + // fifo p: mode: 0x11a4, rdev: 0x0, umask: 18 + // socket s + // rdev = majonr << 8 | minor + let mode = match ty { + VfsNodeType::Fifo => 0x11a4, + VfsNodeType::CharDevice => 0x21a4, + VfsNodeType::BlockDevice => 0x61a4, + VfsNodeType::Socket => 0x81a4, + _ => 0x21a4, + }; + let rdev = 0xa00; // major << 8 | minor; + let mknodin = FuseMknodIn::new(mode, rdev, 18); + mknodin.write_to(&mut fusebuf[40..]); + fusebuf[56..56 + name_len].copy_from_slice(name.as_bytes()); + fusein.print(); + mknodin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at mknod in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseMknod as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at mknod is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 144]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to mknod: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + + if fuseout.is_ok() { + mknodout = FuseEntryOut::read_from(&outbuf[16..]); + mknodout.print(); + mknod_error = 1; + } else { + mknod_error = fuseout.error(); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node mknod finish successfully..."); + + if mknod_error < 0 { + match mknod_error { + -13 => return Err(VfsError::PermissionDenied), + -17 => return Err(VfsError::AlreadyExists), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + Ok(()) + } + + /// FuseMkdir = 9 + pub fn mkdir(&self, name: &RelPath) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node MKDIR({:?}) {:?} here...", + FuseOpcode::FuseMkdir as u32, + name + ); + + let mkdir_error; + let mkdirout; + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let name_len = name.len(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 49 + name_len as u32, + FuseOpcode::FuseMkdir as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 180]; + fusein.write_to(&mut fusebuf); + let mkdirin = FuseMkdirIn::new(0x1ff, 18); // 0x1ed => 0755 + mkdirin.write_to(&mut fusebuf[40..]); + fusebuf[48..48 + name_len].copy_from_slice(name.as_bytes()); + fusein.print(); + mkdirin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at mkdir in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseMkdir as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at mkdir is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 144]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to mkdir: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + if fuseout.is_ok() { + mkdirout = FuseEntryOut::read_from(&outbuf[16..]); + mkdirout.print(); + mkdir_error = 1; + } else { + mkdir_error = fuseout.error(); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node mkdir finish successfully..."); + + if mkdir_error < 0 { + match mkdir_error { + -5 => return Err(VfsError::Io), + -13 => return Err(VfsError::PermissionDenied), + -17 => return Err(VfsError::AlreadyExists), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + Ok(()) + } + + /// FuseRmdir = 11 + pub fn rmdir(&self, name: &RelPath) -> VfsResult { + let mut dirents: [VfsDirEntry; 8] = [VfsDirEntry::new("", VfsNodeType::File); 8]; + let mut cur = 0; + let node = self.try_get(&RelPath::new(name))?; + node.get_attr()?; + node.open()?; + let mut num = node.read_dir(0, &mut dirents)?; + while num > 0 { + for entry in dirents.iter().take(num) { + let name = String::from_utf8_lossy(entry.name_as_bytes()).to_string(); + if name == "." || name == ".." { + continue; + } + if name.starts_with(".") { + continue; + } + debug!("dirent name = {name:?}"); + let node_name = name.as_str(); + node.unlink(&RelPath::new(node_name))?; + } + cur += num; + num = node.read_dir(cur, &mut dirents)?; + } + node.release()?; + + debug!( + "\nNEW FUSE REQUEST:\n fuse_node RMDIR({:?}) {:?} here...", + FuseOpcode::FuseRmdir as u32, + name + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let name_len = name.len(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 41 + name_len as u32, + FuseOpcode::FuseRmdir as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 180]; + fusein.write_to(&mut fusebuf); + fusebuf[40..40 + name_len].copy_from_slice(name.as_bytes()); + fusein.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at rmdir in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseRmdir as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at rmdir is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 16]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to rmdir: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let rmdir_error = if fuseout.is_ok() { 1 } else { fuseout.error() }; + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node rmdir finish successfully..."); + + if rmdir_error < 0 { + match rmdir_error { + -2 => return Err(VfsError::NotFound), + -13 => return Err(VfsError::PermissionDenied), + -20 => return Err(VfsError::NotADirectory), + -38 => return Err(VfsError::FunctionNotImplemented), + -39 => return Err(VfsError::DirectoryNotEmpty), + _ => return Err(VfsError::PermissionDenied), + } + } + + Ok(()) + } + + /// FuseUnlink = 10 + pub fn unlink_node(&self, name: &RelPath) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node UNLINK({:?}) {:?} here...", + FuseOpcode::FuseUnlink as u32, + name + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let name_len = name.len(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 41 + name_len as u32, + FuseOpcode::FuseUnlink as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 180]; + fusein.write_to(&mut fusebuf); + fusebuf[40..40 + name_len].copy_from_slice(name.as_bytes()); + fusein.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at unlink in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseUnlink as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at unlink is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 16]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to unlink: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let unlink_error = if fuseout.is_ok() { 1 } else { fuseout.error() }; + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node unlink finish successfully..."); + + if unlink_error < 0 { + match unlink_error { + -2 => return Err(VfsError::NotFound), + -13 => return Err(VfsError::PermissionDenied), + -20 => return Err(VfsError::NotADirectory), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + Ok(()) + } + + /// FuseRead = 15 + fn read(&self, offset: u64, buf: &mut [u8]) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node READ({:?}) here, offset: {:?}, buf_len: {:?}...", + FuseOpcode::FuseRead as u32, + offset, + buf.len() + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + + let fusein = FuseInHeader::new( + 80, + FuseOpcode::FuseRead as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 80]; + fusein.write_to(&mut fusebuf); + + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + let mut flags_guard = self.flags.lock(); + let readflags = &mut *flags_guard; + *readflags = 0x8002; + let readsize = buf.len().min(4096) as u32; + let readin = FuseReadIn::new(fh, offset, readsize, 0, 0, 0x8002); + readin.write_to(&mut fusebuf[40..]); + fusein.print(); + readin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at read in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseRead as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at read is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 70000]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to read: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + let outlen = vec.len() - 16; + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let read_error = if fuseout.is_ok() { + let readout = &outbuf[16..outlen + 16]; + buf[..outlen].copy_from_slice(readout); + debug!("readout_len: {outlen:?}"); + trace!("readout: {readout:?}"); + 1 + } else { + fuseout.error() + }; + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node read len: {outlen:?} finish successfully..."); + + if read_error < 0 { + match read_error { + -13 => Err(VfsError::PermissionDenied), + -21 => Err(VfsError::IsADirectory), + -38 => Err(VfsError::FunctionNotImplemented), + _ => Err(VfsError::PermissionDenied), + } + } else { + Ok(outlen) + } + } + + /// FuseStatfs = 17 + pub fn statfs(&self) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node STATFS({:?}) here...", + FuseOpcode::FuseStatfs as u32 + ); + + let statfs_error; + let mut statfsout = FuseStatfsOut::default(); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 40, + FuseOpcode::FuseStatfs as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 40]; + fusein.write_to(&mut fusebuf); + fusein.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at statfs in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseStatfs as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at statfs is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 96]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to statfs: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + if fuseout.is_ok() { + statfsout = FuseStatfsOut::read_from(&outbuf[16..]); + statfsout.print(); + statfs_error = 1; + } else { + statfs_error = fuseout.error(); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node statfs finish successfully..."); + + if statfs_error < 0 { + match statfs_error { + -13 => return Err(VfsError::PermissionDenied), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + Ok(statfsout) + } + + /// FuseFlush = 25 + pub fn flush(&self) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node FLUSH({:?}) here...", + FuseOpcode::FuseFlush as u32 + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 64, + FuseOpcode::FuseFlush as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 64]; + fusein.write_to(&mut fusebuf); + let flushin = FuseFlushIn::new(fh, 0, 0, 0); + flushin.write_to(&mut fusebuf[40..]); + fusein.print(); + flushin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at flush in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseFlush as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at flush is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 16]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to flush: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let flush_error = if fuseout.is_ok() { 1 } else { fuseout.error() }; + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node flush finish successfully..."); + + if flush_error < 0 { + match flush_error { + -13 => Err(VfsError::PermissionDenied), + -38 => Err(VfsError::FunctionNotImplemented), + _ => Err(VfsError::PermissionDenied), + } + } else { + Ok(()) + } + } + + /// FuseAccess = 34 + pub fn access(&self) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node ACCESS({:?}) here...", + FuseOpcode::FuseAccess as u32 + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 48, + FuseOpcode::FuseAccess as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 48]; + fusein.write_to(&mut fusebuf); + let accessin = FuseAccessIn::new(1); + accessin.write_to(&mut fusebuf[40..]); + fusein.print(); + accessin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at access in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseAccess as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at access is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 16]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to access: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let access_error = if fuseout.is_ok() { 1 } else { fuseout.error() }; + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node access finish successfully..."); + + if access_error < 0 { + match access_error { + -13 => return Err(VfsError::PermissionDenied), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + Ok(()) + } + + /// FuseRename2 = 45 + pub fn rename2(&self, old: &RelPath, new: &RelPath) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node RENAME2({:?}) from {:?} to {:?} here...", + FuseOpcode::FuseRename2 as u32, + old, + new + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let old_len = old.len(); + let new_len = new.len(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 58 + (old_len + new_len) as u32, + FuseOpcode::FuseRename2 as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 280]; + fusein.write_to(&mut fusebuf); + let rename2in = FuseRename2In::new(1, 1); + rename2in.write_to(&mut fusebuf[40..]); + fusebuf[56..56 + old_len].copy_from_slice(old.as_bytes()); + fusebuf[57 + old_len..57 + old_len + new_len].copy_from_slice(new.as_bytes()); + fusein.print(); + rename2in.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at rename2 in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseRename2 as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at rename2 is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 16]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to rename2: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let rename_error = if fuseout.is_ok() { 1 } else { fuseout.error() }; + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node rename2 from {old:?} to {new:?} finish successfully..."); + + if rename_error < 0 { + match rename_error { + -2 => return Err(VfsError::NotFound), + -13 => return Err(VfsError::PermissionDenied), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + Ok(()) + } + + /// FuseLseek = 46 + pub fn lseek(&self, offset: u64, whence: u32) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node LSEEK({:?}) offset: {:?}, whence: {:?} here...", + FuseOpcode::FuseLseek as u32, + offset, + whence + ); + + let lseek_error; + let mut lseekout = 0; + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 64, + FuseOpcode::FuseLseek as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 64]; + fusein.write_to(&mut fusebuf); + let lseekin = FuseLseekIn::new(fh, offset, whence); + lseekin.write_to(&mut fusebuf[40..]); + fusein.print(); + lseekin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at lseek in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseLseek as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at lseek is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 24]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to lseek: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + if fuseout.is_ok() { + lseekout = FuseLseekOut::read_from(&outbuf[16..]).get_offset(); + debug!("lseekout = {lseekout:?}"); + lseek_error = 1; + } else { + lseek_error = fuseout.error(); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node lseek finish successfully..."); + + if lseek_error < 0 { + match lseek_error { + -13 => return Err(VfsError::PermissionDenied), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + Ok(lseekout) + } + + /// FuseDestroy = 38 + pub fn destroy(&self) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node DESTROY({:?}) here...", + FuseOpcode::FuseDestroy as u32 + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 40, + FuseOpcode::FuseDestroy as u32, + unique_id, + 1, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 40]; + fusein.write_to(&mut fusebuf); + fusein.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at destroy in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseDestroy as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at destroy is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 16]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to destroy: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let destroy_error = if fuseout.is_ok() { 1 } else { fuseout.error() }; + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node destroy finish successfully..."); + + if destroy_error < 0 { + match destroy_error { + -5 => return Err(VfsError::Io), + -13 => return Err(VfsError::PermissionDenied), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + UNIQUE_ID.store(0, Ordering::Relaxed); + INITFLAG.store(1, Ordering::Relaxed); + FUSE_VEC.lock().clear(); + + Ok(()) + } +} + +impl VfsNodeOps for FuseNode { + /// FuseOpen = 14 + fn open(&self) -> Result>, VfsError> { + if self.is_dir() { + return self.open_dir(); + } + + debug!( + "\nNEW FUSE REQUEST:\n fuse_node OPEN({:?}) here...", + FuseOpcode::FuseOpen as u32 + ); + + let open_error; + let mut openout = FuseOpenOut::default(); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + let mut flags = self.file_flags(); + if flags == 0x8001 { + flags = 0x8002; + } + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}, flags: {:#x}", + pid, + nodeid, + fh, + self.is_dir(), + flags + ); + + let fusein = FuseInHeader::new( + 48, + FuseOpcode::FuseOpen as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 48]; + fusein.write_to(&mut fusebuf); + let openin = FuseOpenIn::new(flags, 0); + openin.write_to(&mut fusebuf[40..]); + fusein.print(); + openin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at open in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseOpen as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at open is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 32]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to open: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + if fuseout.is_ok() { + openout = FuseOpenOut::read_from(&outbuf[16..]); + openout.print(); + open_error = 1; + } else { + open_error = fuseout.error(); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node open finish successfully..."); + + if open_error < 0 { + match open_error { + -13 => return Err(VfsError::PermissionDenied), + -21 => return Err(VfsError::IsADirectory), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + self.set_fh(openout.get_fh()); + debug!( + "fh = {:#x}, openout.fh = {:#x}", + self.get_fh(), + openout.get_fh() + ); + + Ok(None) + } + + /// FuseRelease = 18 + fn release(&self) -> VfsResult { + if self.is_dir() { + return self.release_dir(); + } + + debug!( + "\nNEW FUSE REQUEST:\n fuse_node RELEASE({:?}) here...", + FuseOpcode::FuseRelease as u32 + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + let flags = self.get_node_flags(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}, flags: {:#x}", + pid, + nodeid, + fh, + self.is_dir(), + flags + ); + + let fusein = FuseInHeader::new( + 64, + FuseOpcode::FuseRelease as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 64]; + fusein.write_to(&mut fusebuf); + let releasein = FuseReleaseIn::new(fh, flags, 0, 0); + releasein.write_to(&mut fusebuf[40..]); + fusein.print(); + releasein.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at release in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseRelease as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at release is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 16]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to release: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let release_error = if fuseout.is_ok() { 1 } else { fuseout.error() }; + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node release finish successfully..."); + + if release_error < 0 { + match release_error { + -13 => Err(VfsError::PermissionDenied), + -21 => Err(VfsError::IsADirectory), + -38 => Err(VfsError::FunctionNotImplemented), + _ => Err(VfsError::PermissionDenied), + } + } else { + Ok(()) + } + } + + /// FuseGetattr = 3 + fn get_attr(&self) -> VfsResult { + self.check_init(); + debug!( + "\nNEW FUSE REQUEST:\n fuse_node GET_ATTR({:?}) here...", + FuseOpcode::FuseGetattr as u32 + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 56, + FuseOpcode::FuseGetattr as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 56]; + fusein.write_to(&mut fusebuf); + let getattrin = FuseGetattrIn::new(0, 0, fh); + getattrin.write_to(&mut fusebuf[40..]); + fusein.print(); + getattrin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at get_attr in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseGetattr as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at get_attr is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 120]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to get_attr: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + let fuseattr = FuseAttrOut::read_from(&outbuf[16..]); + fuseattr.print(); + + self.set_node_attr(fuseattr.get_attr()); + let attr_size = fuseattr.get_size(); + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node get_attr finish successfully..."); + + if self.is_dir() { + Ok(VfsNodeAttr::new_dir(self.get_node_inode(), attr_size, 0)) + } else { + Ok(VfsNodeAttr::new_file(self.get_node_inode(), attr_size, 0)) + } + } + + fn parent(&self) -> Option { + self.try_get(&RelPath::new("..")).ok() + } + + fn get_inode(&self) -> Option { + let curid = self.get_node_inode(); + Some(curid) + } + + fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { + debug!( + "\nFUSE READ AT here, offset: {:?}, buf_len: {:?}\n", + offset, + buf.len() + ); + let mut remain = buf.len(); + let mut cur_offset = offset; + let mut start = 0; + while remain > 0 { + let cur = remain.min(4096); + let read_len = self.read(cur_offset, &mut buf[start..start + cur])?; + cur_offset += read_len as u64; + start += read_len; + remain -= read_len; + if read_len < cur { + break; + } + } + + Ok(start) + } + + /// FuseWrite = 16 + fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node WRITE({:?}) here, offset: {:?}, buf_len: {:?}", + FuseOpcode::FuseWrite as u32, + offset, + buf.len() + ); + trace!("buf: {buf:?}"); + + let write_error; + let writeout; + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let buf_len = buf.len(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + let flags = self.file_flags(); + // let mut flags_guard = self.flags.lock(); + // let wflags = &mut *flags_guard; + // *wflags = flags; + self.set_node_flags(flags); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}, flags: {:#x}", + pid, + nodeid, + fh, + self.is_dir(), + flags + ); + + let fusein = FuseInHeader::new( + 80 + buf_len as u32, + FuseOpcode::FuseWrite as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 72000]; + fusein.write_to(&mut fusebuf); + let writein = FuseWriteIn::new(fh, offset, (buf_len + 1) as u32, 0, 0, flags); + writein.write_to(&mut fusebuf[40..]); + fusebuf[80..80 + buf_len].copy_from_slice(buf); + fusein.print(); + writein.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at write in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseWrite as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at write is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 24]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to write: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + if fuseout.is_ok() { + writeout = FuseWriteOut::read_from(&outbuf[16..]); + writeout.print(); + write_error = 1; + } else { + write_error = fuseout.error(); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node write finish successfully..."); + + if write_error < 0 { + match write_error { + -2 => Err(VfsError::NotFound), + -13 => Err(VfsError::PermissionDenied), + -21 => Err(VfsError::IsADirectory), + -28 => Err(VfsError::StorageFull), + -38 => Err(VfsError::FunctionNotImplemented), + _ => Err(VfsError::PermissionDenied), + } + } else { + Ok(buf.len()) + } + } + + /// FuseFsync = 20 + fn fsync(&self) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node FSYNC({:?}) here...", + FuseOpcode::FuseFsync as u32 + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 40, + FuseOpcode::FuseFsync as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 40]; + fusein.write_to(&mut fusebuf); + fusein.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at fsync in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseFsync as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at fsync is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 16]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to fsync: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let fsync_error = if fuseout.is_ok() { 1 } else { fuseout.error() }; + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node fsync finish successfully..."); + + if fsync_error < 0 { + match fsync_error { + -38 => Err(VfsError::FunctionNotImplemented), + _ => Err(VfsError::PermissionDenied), + } + } else { + Ok(()) + } + } + + fn truncate(&self, size: u64) -> VfsResult { + warn!("fuse_node truncate is not implemented, size: {size:?}..."); + Ok(()) + } + + fn lookup(self: Arc, raw_path: &RelPath) -> VfsResult { + if raw_path.as_str() == "MAILPATH" { + return Err(VfsError::NotFound); + } + self.try_get(raw_path) + } + + /// FuseCreate = 20 + fn create(&self, path: &RelPath, ty: VfsNodeType, mode: VfsNodePerm) -> VfsResult { + let (name, raw_rest) = split_path(path.as_str()); + if let Some(rest) = raw_rest { + if name.is_empty() || name == "." { + return self.create(&RelPath::new(rest), ty, mode); + } + return self + .try_get(&RelPath::new(name))? + .create(&RelPath::new(rest), ty, mode); + } + + if ty == VfsNodeType::Dir { + return self.mkdir(path); + } else if ty != VfsNodeType::File { + return self.mknod(path, ty); + } + + let newtype = match ty { + VfsNodeType::Fifo => "fifo", + VfsNodeType::CharDevice => "char device", + VfsNodeType::BlockDevice => "block device", + VfsNodeType::File => "file", + VfsNodeType::SymLink => "symlink", + VfsNodeType::Socket => "socket", + _ => "unknown", + }; + debug!( + "\nNEW FUSE REQUEST:\n fuse_node CREATE({:?}) {:?} here, type: {:?}...", + FuseOpcode::FuseCreate as u32, + path, + newtype + ); + + let create_error; + let createout; + let openout; + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let path_len = path.len(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 57 + path_len as u32, + FuseOpcode::FuseCreate as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 180]; + fusein.write_to(&mut fusebuf); + let createin = FuseCreateIn::new(0x8241, 0x81a4, 18, 0); + createin.write_to(&mut fusebuf[40..]); + fusebuf[56..56 + path_len].copy_from_slice(path.as_bytes()); + fusein.print(); + createin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at create in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseCreate as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at create is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 160]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to create: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + if fuseout.is_ok() { + createout = FuseEntryOut::read_from(&outbuf[16..]); + createout.print(); + openout = FuseOpenOut::read_from(&outbuf[144..]); + openout.print(); + create_error = 1; + } else { + create_error = fuseout.error(); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node create finish successfully..."); + + if create_error < 0 { + match create_error { + -5 => return Err(VfsError::Io), + -13 => return Err(VfsError::PermissionDenied), + -17 => return Err(VfsError::AlreadyExists), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + Ok(()) + } + + fn unlink(&self, path: &RelPath) -> VfsResult { + let (name, raw_rest) = split_path(path); + if let Some(rest) = raw_rest { + if name.is_empty() || name == "." { + return self.unlink(&RelPath::new(rest)); + } + return self + .try_get(&RelPath::new(name))? + .unlink(&RelPath::new(rest)); + } + + let node = self.try_get(&RelPath::new(name))?; + let attr = node.get_attr()?; + + if attr.file_type() == VfsNodeType::Dir { + self.rmdir(&RelPath::new(name)) + } else { + self.unlink_node(&RelPath::new(name)) + } + } + + /// FuseReaddir = 28 + fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult { + debug!( + "\nNEW FUSE REQUEST:\n fuse_node READ_DIR({:?}) here, start: {:?}...", + FuseOpcode::FuseReaddir as u32, + start_idx + ); + + let mut dirs = Vec::::new(); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + + let fusein = FuseInHeader::new( + 80, + FuseOpcode::FuseReaddir as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 80]; + fusein.write_to(&mut fusebuf); + let readin = FuseReadIn::new(fh, 0, 4096, 0, 0, 0x18800); + readin.write_to(&mut fusebuf[40..]); + fusein.print(); + readin.print(); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at readdir in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseReaddir as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at readdir is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + // ruxtask::WaitQueue + } + + let mut outbuf = [0; 12000]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to readdir: {vec:?}"); + let buf_len = vec.len(); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let readdir_error = if fuseout.is_ok() { 1 } else { fuseout.error() }; + + let mut offset = 16; + while offset < buf_len { + let direntry = FuseDirent::read_from(&outbuf[offset..]); + direntry.print(); + offset += direntry.get_len(); + dirs.push(direntry); + debug!("offset = {offset:?}"); + } + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node readdir finish successfully..."); + + if readdir_error < 0 { + match readdir_error { + -13 => return Err(VfsError::PermissionDenied), + -20 => return Err(VfsError::NotADirectory), + -22 => return Err(VfsError::InvalidInput), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + for (i, ent) in dirents.iter_mut().enumerate() { + match i + start_idx { + 0 => *ent = VfsDirEntry::new(".", VfsNodeType::Dir), + 1 => *ent = VfsDirEntry::new("..", VfsNodeType::Dir), + _ => { + trace!("dirs.len() = {:?}, i+idx = {:?}", dirs.len(), i + start_idx); + if let Some(entry) = dirs.get(i + start_idx) { + let entry_type = entry.get_type_as_vfsnodetype(); + *ent = VfsDirEntry::new(&entry.get_name(), entry_type); + trace!( + "entry: {{ name: {:?}, type: {:?} }}", + &entry.get_name(), + entry_type + ); + } else { + for (j, dirent) in dirents.iter().enumerate().take(i) { + debug!( + "entry {:?}: name: {:?}, type: {:?}", + j, + String::from_utf8(dirent.name_as_bytes().to_vec()), + &dirents[j].entry_type() + ); + } + debug!("Ok(i) = {i:?}"); + return Ok(i); + } + } + } + } + + for (j, dirent) in dirents.iter().enumerate() { + debug!( + "entry {:?}: name: {:?}, type: {:?}", + j, + String::from_utf8(dirent.name_as_bytes().to_vec()), + &dirents[j].entry_type() + ); + } + + debug!("Ok(dirents.len()) = {:?}", dirents.len()); + Ok(dirents.len()) + } + + /// FuseRename = 12 + fn rename(&self, src_path: &RelPath, dst_path: &RelPath) -> VfsResult { + debug!( + "fuse_node(inode: {:?}) rename src: {:?}, dst: {:?}", + self.get_node_inode(), + src_path, + dst_path + ); + + if NEWID.load(Ordering::SeqCst) == -1 { + NEWID.store(self.find_inode(dst_path).unwrap() as i32, Ordering::Relaxed); + } + let newid = NEWID.load(Ordering::SeqCst); + + let (src_name, src_rest1) = split_path(src_path); + if let Some(src_rest) = src_rest1 { + if src_name.is_empty() || src_name == "." { + return self.rename(&RelPath::new(src_rest), dst_path); + } + return self + .try_get(&RelPath::new(src_name))? + .rename(&RelPath::new(src_rest), dst_path); + } + + // let newid = self.find_inode(dst_path).unwrap(); + let raw_dst_name = self.get_final_name(dst_path).unwrap(); + let dst_name = raw_dst_name.as_str(); + + // self.rename2(src_path, dst_path); + + debug!( + "\nNEW FUSE REQUEST:\n fuse_node RENAME({:?}) from {:?} to {:?} here...", + FuseOpcode::FuseRename as u32, + src_path, + dst_path + ); + + UNIQUE_ID.fetch_add(2, Ordering::Relaxed); + let unique_id = UNIQUE_ID.load(Ordering::SeqCst); + let pid = current().id().as_u64(); + let src_len = src_name.len(); + let dst_len = dst_name.len(); + let nodeid = self.get_node_inode(); + let fh = self.get_fh(); + debug!( + "pid = {:?}, inode = {:?}, fh = {:#x}, is_dir: {:?}", + pid, + nodeid, + fh, + self.is_dir() + ); + debug!( + "src_name = {src_name:?}, dst_name = {dst_name:?}, src_len = {src_len:?}, dst_len = {dst_len:?}" + ); + + let fusein = FuseInHeader::new( + 50 + (src_len + dst_len) as u32, + FuseOpcode::FuseRename as u32, + unique_id, + nodeid, + 1000, + 1000, + pid as u32, + ); + let mut fusebuf = [0; 280]; + fusein.write_to(&mut fusebuf); + debug!("oldid = {nodeid:?}, newid = {newid:?}"); + let renamein = FuseRenameIn::new(newid as u64); + renamein.write_to(&mut fusebuf[40..]); + fusebuf[48..48 + src_len].copy_from_slice(src_name.as_bytes()); + fusebuf[49 + src_len..49 + src_len + dst_len].copy_from_slice(dst_name.as_bytes()); + fusein.print(); + renamein.print(); + NEWID.store(-1, Ordering::Relaxed); + + let mut vec = FUSE_VEC.lock(); + vec.extend_from_slice(&fusebuf); + trace!("Fusevec at rename in devfuse: {vec:?}"); + + FUSEFLAG.store(FuseOpcode::FuseRename as i32, Ordering::Relaxed); + + loop { + let flag = FUSEFLAG.load(Ordering::SeqCst); + if flag < 0 { + trace!("Fuseflag at rename is set to {flag:?}, exiting loop. !!!"); + break; + } + ruxtask::yield_now(); + } + + let mut outbuf = [0; 16]; + + let mut vec = FUSE_VEC.lock(); + trace!("Fusevec back to rename: {vec:?}"); + outbuf[0..vec.len()].copy_from_slice(&vec); + vec.clear(); + + let fuseout = FuseOutHeader::read_from(&outbuf); + fuseout.print(); + + let rename_error = if fuseout.is_ok() { 1 } else { fuseout.error() }; + + FUSEFLAG.store(0, Ordering::Relaxed); + + debug!("fuse_node rename from {src_path:?} to {dst_path:?} finish successfully..."); + + if rename_error < 0 { + match rename_error { + -2 => return Err(VfsError::NotFound), + -13 => return Err(VfsError::PermissionDenied), + -38 => return Err(VfsError::FunctionNotImplemented), + _ => return Err(VfsError::PermissionDenied), + } + } + + Ok(()) + } +} + +fn split_path(path: &str) -> (&str, Option<&str>) { + let trimmed_path = path.trim_start_matches('/'); + trimmed_path.find('/').map_or((trimmed_path, None), |n| { + (&trimmed_path[..n], Some(&trimmed_path[n + 1..])) + }) +} + +/// Create a new FuseFS instance +pub fn fusefs() -> Arc { + trace!("fusefs newfs here..."); + Arc::new(FuseFS::new()) +} diff --git a/modules/ruxfuse/src/lib.rs b/modules/ruxfuse/src/lib.rs new file mode 100644 index 000000000..ec4627a55 --- /dev/null +++ b/modules/ruxfuse/src/lib.rs @@ -0,0 +1,18 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! [Ruxos](https://github.com/syswonder/ruxos) fuse filesystem module. + +#![cfg_attr(all(not(test), not(doc)), no_std)] +#![feature(doc_auto_cfg)] + +extern crate alloc; +extern crate log; + +pub mod fuse; diff --git a/modules/ruxfutex/Cargo.toml b/modules/ruxfutex/Cargo.toml index c4f2d8a10..5bd72c645 100644 --- a/modules/ruxfutex/Cargo.toml +++ b/modules/ruxfutex/Cargo.toml @@ -24,7 +24,7 @@ ruxtask = { path = "../ruxtask", features = ["multitask"] } # Other crates log = "0.4" lazy_static = { version = "1.4", features = ["spin_no_std"] } -bitflags = "2.2" +bitflags = { workspace = true } ahash = { version = "0.8.7", default-features = false, features = [ "compile-time-rng", ] } diff --git a/modules/ruxfutex/src/api.rs b/modules/ruxfutex/src/api.rs index 86c0a0ef6..df1239233 100644 --- a/modules/ruxfutex/src/api.rs +++ b/modules/ruxfutex/src/api.rs @@ -35,7 +35,7 @@ fn futex_wait_timeout( let condition = || { // Check the futex value let actual_val = futex_key.load_val(); - trace!("futex_wait: expected {}, found {}", futex_val, actual_val); + trace!("futex_wait: expected {futex_val}, found {actual_val}"); if actual_val != futex_val { // it's not actually an error but rather a notice to user, // so no `ax_err` and no warning diff --git a/modules/ruxhal/Cargo.toml b/modules/ruxhal/Cargo.toml index 7b4ea29fa..b94e265be 100644 --- a/modules/ruxhal/Cargo.toml +++ b/modules/ruxhal/Cargo.toml @@ -12,6 +12,7 @@ homepage = "https://github.com/syswonder/ruxos" repository = "https://github.com/syswonder/ruxos/tree/main/modules/ruxhal" [features] +virtio = ["driver_virtio", "virtio-drivers", "axalloc"] smp = [] alloc = [] fp_simd = [] @@ -21,32 +22,52 @@ rtc = [] tls = ["alloc"] default = [] musl = [] +signal = [] +gic-v3 = ["arm_gic/gic-v3"] +virtio_console = [ + "driver_console", + "driver_virtio", + "driver_virtio/console", + "driver_common", + "virtio-drivers", + "axalloc", + "alloc", + "virtio", +] + [dependencies] log = "0.4" cfg-if = "1.0" -bitflags = "2.2" +bitflags = { workspace = true } static_assertions = "1.1.0" embedded-hal = "0.2.7" -axlog = { path = "../axlog" } ruxconfig = { path = "../ruxconfig" } axalloc = { path = "../axalloc", optional = true } kernel_guard = "0.1.0" +axlog = { path = "../../crates/axlog" } spinlock = { path = "../../crates/spinlock" } ratio = { path = "../../crates/ratio" } lazy_init = { path = "../../crates/lazy_init" } page_table = { path = "../../crates/page_table", optional = true } page_table_entry = { path = "../../crates/page_table_entry" } -percpu = { path = "../../crates/percpu" } +percpu = "0.2" +driver_console = { path = "../../crates/driver_console", optional = true } +driver_virtio = { path = "../../crates/driver_virtio", optional = true } +driver_common = { path = "../../crates/driver_common", optional = true } +virtio-drivers = { git = "https://github.com/syswonder/virtio-drivers.git", rev = "31f6555", optional = true } +lazy_static = { version = "1.4", features = ["spin_no_std"] } memory_addr = "0.1.0" handler_table = "0.1.0" crate_interface = "0.1.1" -tty = { path = "../../crates/tty", optional = true } +axerrno = { path = "../../crates/axerrno" } +axio = { path = "../../crates/axio" } +tty = { path = "../../crates/tty" } [target.'cfg(target_arch = "x86_64")'.dependencies] x86 = "0.52" -x86_64 = "0.14" -x2apic = "0.4" +x86_64 = "0.14.13" +x2apic = "0.5" raw-cpuid = "11.0" [target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies] diff --git a/modules/ruxhal/build.rs b/modules/ruxhal/build.rs index 8b99a60a5..91844d5bf 100644 --- a/modules/ruxhal/build.rs +++ b/modules/ruxhal/build.rs @@ -9,6 +9,21 @@ use std::io::Result; +const BUILTIN_PLATFORMS: &[&str] = &[ + "aarch64-qemu-virt", + "riscv64-qemu-virt", + "x86_64-pc-oslab", + "x86_64-qemu-q35", +]; + +const BUILTIN_PLATFORM_FAMILIES: &[&str] = &[ + "aarch64-qemu-virt", + "aarch64-raspi", + "aarch64-bsta1000b", + "riscv64-qemu-virt", + "x86-pc", +]; + fn main() { let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); let platform = ruxconfig::PLATFORM; @@ -16,12 +31,32 @@ fn main() { gen_linker_script(&arch, platform).unwrap(); } - println!("cargo:rustc-cfg=platform=\"{}\"", platform); println!("cargo:rustc-cfg=platform_family=\"{}\"", ruxconfig::FAMILY); + println!("cargo:rustc-cfg=platform=\"{platform}\""); + println!( + "cargo:rustc-cfg=platform_family=\"{}\"", + ruxconfig::PLATFORM + ); + println!( + "cargo::rustc-check-cfg=cfg(platform, values({}))", + make_cfg_values(BUILTIN_PLATFORMS) + ); + println!( + "cargo::rustc-check-cfg=cfg(platform_family, values({}))", + make_cfg_values(BUILTIN_PLATFORM_FAMILIES) + ); +} + +fn make_cfg_values(str_list: &[&str]) -> String { + str_list + .iter() + .map(|s| format!("{s:?}")) + .collect::>() + .join(", ") } fn gen_linker_script(arch: &str, platform: &str) -> Result<()> { - let fname = format!("linker_{}.lds", platform); + let fname = format!("linker_{platform}.lds"); let output_arch = if arch == "x86_64" { "i386:x86-64" } else if arch.contains("riscv") { diff --git a/modules/ruxhal/src/arch/aarch64/context.rs b/modules/ruxhal/src/arch/aarch64/context.rs index d129d23f1..7c75e8735 100644 --- a/modules/ruxhal/src/arch/aarch64/context.rs +++ b/modules/ruxhal/src/arch/aarch64/context.rs @@ -8,10 +8,10 @@ */ use core::{ - arch::asm, + arch::naked_asm, fmt::{Debug, LowerHex}, }; -use memory_addr::VirtAddr; +use memory_addr::{PhysAddr, VirtAddr}; /// Saved registers when a trap (exception) occurs. #[repr(C)] @@ -33,7 +33,7 @@ impl<'a, T: Debug + LowerHex> Debug for EnumerateReg<'a, T> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut dbg_l = f.debug_list(); for (i, r) in self.0.iter().enumerate() { - dbg_l.entry(&format_args!("x{}: {:#x}", i, r)); + dbg_l.entry(&format_args!("x{i}: {r:#x}")); } dbg_l.finish() } @@ -62,13 +62,6 @@ pub struct FpState { pub fpsr: u32, } -#[cfg(feature = "fp_simd")] -impl FpState { - fn switch_to(&mut self, next_fpstate: &FpState) { - unsafe { fpstate_switch(self, next_fpstate) } - } -} - /// Saved hardware states of a task. /// /// The context usually includes: @@ -102,10 +95,16 @@ pub struct TaskContext { pub fp_state: FpState, } +impl Default for TaskContext { + fn default() -> Self { + Self::new() + } +} + impl TaskContext { /// Creates a new default context for a new task. pub const fn new() -> Self { - unsafe { core::mem::MaybeUninit::zeroed().assume_init() } + unsafe { core::mem::MaybeUninit::::zeroed().assume_init() } } /// Initializes the context for a new task, with the given entry point and @@ -116,20 +115,127 @@ impl TaskContext { self.tpidr_el0 = tls_area.as_usize() as u64; } + /// Saves the current task's context from CPU to memory. + /// + /// # Safety + /// + /// - `src` must be a valid pointer to a memory region of at least `size` bytes. + /// - `dst` must be a valid pointer to a memory region of at least `size` bytes. + /// - The caller must ensure that no other thread or operation modifies the memory + /// at `src` or `dst` while this function is executing. + /// - The size should not exceed the allocated memory size for `src` and `dst`. + pub unsafe fn save_current_content(&mut self, src: *const u8, dst: *mut u8, size: usize) { + unsafe { + save_stack(src, dst, size); + + #[cfg(feature = "fp_simd")] + save_fpstate_context(&mut self.fp_state); + + // will ret from here + save_current_context(self); + } + } + /// Switches to another task. /// /// It first saves the current task's context from CPU to this place, and then /// restores the next task's context from `next_ctx` to CPU. - pub fn switch_to(&mut self, next_ctx: &Self) { - #[cfg(feature = "fp_simd")] - self.fp_state.switch_to(&next_ctx.fp_state); - unsafe { context_switch(self, next_ctx) } + #[inline(never)] + pub fn switch_to(&mut self, next_ctx: &Self, page_table_addr: PhysAddr) { + unsafe { + #[cfg(feature = "fp_simd")] + fpstate_switch(&mut self.fp_state, &next_ctx.fp_state); + // switch to the next process's page table, stack would be unavailable before context switch finished + context_switch(self, next_ctx, page_table_addr.as_usize() as u64); + } } } -#[naked] -unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: &TaskContext) { - asm!( +#[unsafe(naked)] +#[allow(named_asm_labels)] +// TODO: consider using SIMD instructions to copy the stack in parallel. +unsafe extern "C" fn save_stack(src: *const u8, dst: *mut u8, size: usize) { + // x0: src, x1: dst, x2: size + naked_asm!( + " + mov x9, 0x0 // clear x9 + + _copy_stack_start: + cmp x9, x2 + b.eq _copy_stack_end + ldr x12, [x0] + str x12, [x1] + add x0, x0, 8 + add x1, x1, 8 + add x9, x9, 8 + b _copy_stack_start + _copy_stack_end: + + dsb sy + isb + ret", + ) +} + +#[unsafe(naked)] +#[allow(named_asm_labels)] +unsafe extern "C" fn save_current_context(_current_task: &mut TaskContext) { + naked_asm!( + " + stp x29, x30, [x0, 12 * 8] + stp x27, x28, [x0, 10 * 8] + stp x25, x26, [x0, 8 * 8] + stp x23, x24, [x0, 6 * 8] + stp x21, x22, [x0, 4 * 8] + stp x19, x20, [x0, 2 * 8] + mrs x20, tpidr_el0 + mov x19, sp + stp x19, x20, [x0, 0 * 8] // [x0] is parent's sp + ldp x19, x20, [x0, 2 * 8] + isb + ret", + ) +} + +#[unsafe(naked)] +#[cfg(feature = "fp_simd")] +unsafe extern "C" fn save_fpstate_context(_current_fpstate: &mut FpState) { + naked_asm!( + " + // save fp/neon context + mrs x9, fpcr + mrs x10, fpsr + stp q0, q1, [x0, 0 * 16] + stp q2, q3, [x0, 2 * 16] + stp q4, q5, [x0, 4 * 16] + stp q6, q7, [x0, 6 * 16] + stp q8, q9, [x0, 8 * 16] + stp q10, q11, [x0, 10 * 16] + stp q12, q13, [x0, 12 * 16] + stp q14, q15, [x0, 14 * 16] + stp q16, q17, [x0, 16 * 16] + stp q18, q19, [x0, 18 * 16] + stp q20, q21, [x0, 20 * 16] + stp q22, q23, [x0, 22 * 16] + stp q24, q25, [x0, 24 * 16] + stp q26, q27, [x0, 26 * 16] + stp q28, q29, [x0, 28 * 16] + stp q30, q31, [x0, 30 * 16] + str x9, [x0, 64 * 8] + str x10, [x0, 65 * 8] + isb + ret", + ) +} + +#[unsafe(naked)] +#[allow(named_asm_labels)] +unsafe extern "C" fn context_switch( + _current_task: &mut TaskContext, + _next_task: &TaskContext, + _page_table_addr: u64, +) { + naked_asm!( " // save old context (callee-saved registers) stp x29, x30, [x0, 12 * 8] @@ -142,6 +248,19 @@ unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: mrs x20, tpidr_el0 stp x19, x20, [x0] + // switch to next task's page table + mrs x19, TTBR1_EL1 + cmp x19, x2 + b.eq _switch_page_table_done + _switch_page_table: + mov x19, x2 + msr TTBR1_EL1, x19 + tlbi vmalle1 + dsb sy + isb + // no need to switch page table, just continue + _switch_page_table_done: + // restore new context ldp x19, x20, [x1] mov sp, x19 @@ -153,15 +272,15 @@ unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: ldp x27, x28, [x1, 10 * 8] ldp x29, x30, [x1, 12 * 8] + isb ret", - options(noreturn), ) } -#[naked] +#[unsafe(naked)] #[cfg(feature = "fp_simd")] unsafe extern "C" fn fpstate_switch(_current_fpstate: &mut FpState, _next_fpstate: &FpState) { - asm!( + naked_asm!( " // save fp/neon context mrs x9, fpcr @@ -209,6 +328,5 @@ unsafe extern "C" fn fpstate_switch(_current_fpstate: &mut FpState, _next_fpstat isb ret", - options(noreturn), ) } diff --git a/modules/ruxhal/src/arch/aarch64/mod.rs b/modules/ruxhal/src/arch/aarch64/mod.rs index 17afdc585..878d88080 100644 --- a/modules/ruxhal/src/arch/aarch64/mod.rs +++ b/modules/ruxhal/src/arch/aarch64/mod.rs @@ -74,7 +74,7 @@ pub fn read_page_table_root0() -> PhysAddr { /// This function is unsafe as it changes the virtual memory address space. pub unsafe fn write_page_table_root(root_paddr: PhysAddr) { let old_root = read_page_table_root(); - trace!("set page table root: {:#x} => {:#x}", old_root, root_paddr); + trace!("set page table root: {old_root:#x} => {root_paddr:#x}"); if old_root != root_paddr { // kernel space page table use TTBR1 (0xffff_0000_0000_0000..0xffff_ffff_ffff_ffff) TTBR1_EL1.set(root_paddr.as_usize() as _); diff --git a/modules/ruxhal/src/arch/aarch64/trap.S b/modules/ruxhal/src/arch/aarch64/trap.S index f06468287..2213597ba 100644 --- a/modules/ruxhal/src/arch/aarch64/trap.S +++ b/modules/ruxhal/src/arch/aarch64/trap.S @@ -16,7 +16,6 @@ stp x26, x27, [sp, 26 * 8] stp x28, x29, [sp, 28 * 8] - mrs x9, sp_el0 mrs x10, elr_el1 mrs x11, spsr_el1 stp x30, x9, [sp, 30 * 8] @@ -26,7 +25,6 @@ .macro RESTORE_REGS ldp x10, x11, [sp, 32 * 8] ldp x30, x9, [sp, 30 * 8] - msr sp_el0, x9 msr elr_el1, x10 msr spsr_el1, x11 diff --git a/modules/ruxhal/src/arch/aarch64/trap.rs b/modules/ruxhal/src/arch/aarch64/trap.rs index e81cdff95..a480f1121 100644 --- a/modules/ruxhal/src/arch/aarch64/trap.rs +++ b/modules/ruxhal/src/arch/aarch64/trap.rs @@ -106,9 +106,15 @@ fn handle_sync_exception(tf: &mut TrapFrame) { PageFaultCause::INSTRUCTION // = instruction fetch } }; - if crate::trap::handle_page_fault(vaddr, cause) { + let is_mapped = crate::trap::handle_page_fault(vaddr, cause); + + if is_mapped { return; } + error!( + "Page fault @ {:#x}, cause={:?}, is_mapped={}", + tf.elr, cause, is_mapped + ); } panic!( "EL1 Page Fault @ {:#x}, FAR={:#x}, ISS={:#x}:\n{:#x?}", @@ -128,6 +134,10 @@ fn handle_sync_exception(tf: &mut TrapFrame) { ); } } + #[cfg(feature = "signal")] + { + crate::trap::handle_signal(); + } } #[no_mangle] diff --git a/modules/ruxhal/src/arch/riscv/context.rs b/modules/ruxhal/src/arch/riscv/context.rs index ddcab7417..61a188a22 100644 --- a/modules/ruxhal/src/arch/riscv/context.rs +++ b/modules/ruxhal/src/arch/riscv/context.rs @@ -7,9 +7,12 @@ * See the Mulan PSL v2 for more details. */ -use core::arch::asm; +use core::arch::naked_asm; +use memory_addr::PhysAddr; use memory_addr::VirtAddr; +use super::reg_satp::RegSatp; + include_asm_marcos!(); /// General registers of RISC-V. @@ -60,6 +63,7 @@ pub struct TrapFrame { pub sepc: usize, /// Supervisor Status Register. pub sstatus: usize, + pub sscratch: usize, } /// Saved hardware states of a task. @@ -95,6 +99,7 @@ pub struct TaskContext { pub s11: usize, pub tp: usize, + pub gp: usize, // TODO: FP states } @@ -116,56 +121,136 @@ impl TaskContext { /// /// It first saves the current task's context from CPU to this place, and then /// restores the next task's context from `next_ctx` to CPU. - pub fn switch_to(&mut self, next_ctx: &Self) { + pub fn switch_to(&mut self, next_ctx: &Self, page_table_addr: PhysAddr) { #[cfg(feature = "tls")] { self.tp = super::read_thread_pointer(); unsafe { super::write_thread_pointer(next_ctx.tp) }; } + + let satp = RegSatp::gen_satp(riscv::register::satp::Mode::Sv39, 0, page_table_addr.into()); unsafe { // TODO: switch FP states - context_switch(self, next_ctx) + context_switch(self, next_ctx, satp); } } + + /// This function copy the content from src to dst,the content size is given by parameter "size" + /// It's only supposed to use this function for processes stack's copying + /// Not only the content of the src process's stack would be copied into dst's + /// It would also save the current context of the process to src's stack + /// + /// # Argument + /// - src: The raw pointer of the src process stack + /// - dst: The raw pointer of the dst process stack + /// - size: The size of the stack + /// + /// # Safety + /// This function assumes that the parameter "size" indicate exactly the size of both stacks. + /// The caller must ensure this to make safe function call. + pub unsafe fn save_current_content(&mut self, src: *const u8, dst: *mut u8, size: usize) { + unsafe { + save_stack(src, dst, size); + save_current_context(self); + } + } +} + +#[unsafe(naked)] +#[allow(named_asm_labels)] +unsafe extern "C" fn save_current_context(_current_task: &mut TaskContext) { + naked_asm!( + " + sd ra,0(a0) + sd sp,8(a0) + sd s0,16(a0) + sd s1,24(a0) + sd s2,32(a0) + sd s3,40(a0) + sd s4,48(a0) + sd s5,56(a0) + sd s6,64(a0) + sd s7,72(a0) + sd s8,80(a0) + sd s9,88(a0) + sd s10,96(a0) + sd s11,104(a0) + sd tp,112(a0) + ret + " + ) +} + +#[unsafe(naked)] +#[no_mangle] +#[allow(named_asm_labels)] +// TODO: consider using SIMD instructions to copy the stack in parallel. +unsafe extern "C" fn save_stack(src: *const u8, dst: *mut u8, size: usize) { + // a0:src ; a1:dst ; a2:size + naked_asm!( + " + xor a4,a4,a4 + add a4,a4,a2 + start_copy: + ld a5,0(a0) + sd a5,0(a1) + addi a0,a0,8 + addi a1,a1,8 + addi a4,a4,-8 + bnez a4,start_copy + ret + + " + ) } -#[naked] -unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: &TaskContext) { - asm!( +#[unsafe(naked)] +#[allow(named_asm_labels)] +unsafe extern "C" fn context_switch( + _current_task: &mut TaskContext, + _next_task: &TaskContext, + _page_table_addr: usize, +) { + naked_asm!( " // save old context (callee-saved registers) - STR ra, a0, 0 - STR sp, a0, 1 - STR s0, a0, 2 - STR s1, a0, 3 - STR s2, a0, 4 - STR s3, a0, 5 - STR s4, a0, 6 - STR s5, a0, 7 - STR s6, a0, 8 - STR s7, a0, 9 - STR s8, a0, 10 - STR s9, a0, 11 - STR s10, a0, 12 - STR s11, a0, 13 + sd ra, 0(a0) + sd sp, 8(a0) + sd s0, 16(a0) + sd s1, 24(a0) + sd s2, 32(a0) + sd s3, 40(a0) + sd s4, 48(a0) + sd s5, 56(a0) + sd s6, 64(a0) + sd s7, 72(a0) + sd s8, 80(a0) + sd s9, 88(a0) + sd s10, 96(a0) + sd s11, 104(a0) + csrr a3,satp + csrw satp,a2 + xor a3,a3,a2 + beqz a3,set_satp_done + sfence.vma +set_satp_done: // restore new context - LDR s11, a1, 13 - LDR s10, a1, 12 - LDR s9, a1, 11 - LDR s8, a1, 10 - LDR s7, a1, 9 - LDR s6, a1, 8 - LDR s5, a1, 7 - LDR s4, a1, 6 - LDR s3, a1, 5 - LDR s2, a1, 4 - LDR s1, a1, 3 - LDR s0, a1, 2 - LDR sp, a1, 1 - LDR ra, a1, 0 + ld s11, 104(a1) + ld s10, 96(a1) + ld s9, 88(a1) + ld s8, 80(a1) + ld s7, 72(a1) + ld s6, 64(a1) + ld s5, 56(a1) + ld s4, 48(a1) + ld s3, 40(a1) + ld s2, 32(a1) + ld s1, 24(a1) + ld s0, 16(a1) + ld sp, 8(a1) + ld ra, 0(a1) ret", - options(noreturn), ) } diff --git a/modules/ruxhal/src/arch/riscv/mod.rs b/modules/ruxhal/src/arch/riscv/mod.rs index 0026cfc12..ed31d5e2c 100644 --- a/modules/ruxhal/src/arch/riscv/mod.rs +++ b/modules/ruxhal/src/arch/riscv/mod.rs @@ -11,8 +11,8 @@ mod macros; mod context; +mod reg_satp; mod trap; - use memory_addr::{PhysAddr, VirtAddr}; use riscv::asm; use riscv::register::{satp, sstatus, stvec}; @@ -67,7 +67,7 @@ pub fn read_page_table_root() -> PhysAddr { /// This function is unsafe as it changes the virtual memory address space. pub unsafe fn write_page_table_root(root_paddr: PhysAddr) { let old_root = read_page_table_root(); - trace!("set page table root: {:#x} => {:#x}", old_root, root_paddr); + trace!("set page table root: {old_root:#x} => {root_paddr:#x}"); if old_root != root_paddr { satp::set(satp::Mode::Sv39, 0, root_paddr.as_usize() >> 12); asm::sfence_vma_all(); diff --git a/modules/ruxhal/src/arch/riscv/reg_satp.rs b/modules/ruxhal/src/arch/riscv/reg_satp.rs new file mode 100644 index 000000000..faf150759 --- /dev/null +++ b/modules/ruxhal/src/arch/riscv/reg_satp.rs @@ -0,0 +1,54 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use riscv::register::satp; + +type PhysAddr = usize; + +#[allow(clippy::upper_case_acronyms)] +pub struct PPN(usize); + +impl From for PPN { + fn from(value: PhysAddr) -> Self { + let num = value; + let page_frame_num = num >> 12; + Self(page_frame_num) + } +} + +impl From for usize { + fn from(value: PPN) -> Self { + value.0 + } +} +pub struct AddressSpaceID(usize); + +impl From for AddressSpaceID { + fn from(value: u16) -> Self { + let value = usize::from(value); + AddressSpaceID(value << 44) + } +} + +impl From for usize { + fn from(value: AddressSpaceID) -> Self { + value.0 + } +} + +pub struct RegSatp; + +impl RegSatp { + pub fn gen_satp(mode: satp::Mode, asid: u16, page_table_addr: PhysAddr) -> usize { + let mode = (mode as usize) << 60; + let asid: AddressSpaceID = asid.into(); + let physical_page_num: PPN = page_table_addr.into(); + mode | usize::from(asid) | usize::from(physical_page_num) + } +} diff --git a/modules/ruxhal/src/arch/riscv/trap.S b/modules/ruxhal/src/arch/riscv/trap.S index 4e4e3d748..64293236a 100644 --- a/modules/ruxhal/src/arch/riscv/trap.S +++ b/modules/ruxhal/src/arch/riscv/trap.S @@ -1,37 +1,35 @@ .macro SAVE_REGS, from_user addi sp, sp, -{trapframe_size} PUSH_GENERAL_REGS - + addi t2, sp, {trapframe_size} csrr t0, sepc csrr t1, sstatus - csrrw t2, sscratch, zero // save sscratch (sp) and zero it STR t0, sp, 31 // tf.sepc STR t1, sp, 32 // tf.sstatus + STR gp, sp, 2 STR t2, sp, 1 // tf.regs.sp - + csrr gp, sscratch .if \from_user == 1 LDR t0, sp, 3 // load supervisor tp - STR gp, sp, 2 // save user gp and tp STR tp, sp, 3 mv tp, t0 .endif .endm .macro RESTORE_REGS, from_user + // LDR gp, sp, 2 .if \from_user == 1 - LDR gp, sp, 2 // load user gp and tp LDR t0, sp, 3 STR tp, sp, 3 // save supervisor tp mv tp, t0 addi t0, sp, {trapframe_size} // put supervisor sp to scratch - csrw sscratch, t0 .endif - + LDR gp, sp, 2 LDR t0, sp, 31 LDR t1, sp, 32 csrw sepc, t0 csrw sstatus, t1 - + //csrw sscratch, t2 POP_GENERAL_REGS LDR sp, sp, 1 // load sp from tf.regs.sp .endm @@ -42,10 +40,9 @@ trap_vector_base: // sscratch == 0: trap from S mode // sscratch != 0: trap from U mode - csrrw sp, sscratch, sp // switch sscratch and sp - bnez sp, .Ltrap_entry_u - - csrr sp, sscratch // put supervisor sp back + //csrrw sp, sscratch, sp // switch sscratch and sp + //bnez sp, .Ltrap_entry_u + //csrr sp, sscratch // put supervisor sp back j .Ltrap_entry_s .Ltrap_entry_s: diff --git a/modules/ruxhal/src/arch/riscv/trap.rs b/modules/ruxhal/src/arch/riscv/trap.rs index cb32223cb..7812a9328 100644 --- a/modules/ruxhal/src/arch/riscv/trap.rs +++ b/modules/ruxhal/src/arch/riscv/trap.rs @@ -9,7 +9,7 @@ use riscv::register::scause::{self, Exception as E, Trap}; -use super::TrapFrame; +use super::{disable_irqs, enable_irqs, TrapFrame}; include_asm_marcos!(); @@ -19,7 +19,7 @@ core::arch::global_asm!( ); fn handle_breakpoint(sepc: &mut usize) { - debug!("Exception(Breakpoint) @ {:#x} ", sepc); + debug!("Exception(Breakpoint) @ {sepc:#x} "); *sepc += 2 } @@ -31,6 +31,8 @@ fn riscv_trap_handler(tf: &mut TrapFrame, _from_user: bool) { Trap::Interrupt(_) => crate::trap::handle_irq_extern(scause.bits()), #[cfg(feature = "musl")] Trap::Exception(E::UserEnvCall) => { + #[cfg(feature = "irq")] + enable_irqs(); let ret = crate::trap::handle_syscall( tf.regs.a7, [ @@ -43,6 +45,23 @@ fn riscv_trap_handler(tf: &mut TrapFrame, _from_user: bool) { ], ); tf.regs.a0 = ret as _; + #[cfg(feature = "irq")] + disable_irqs(); + } + #[cfg(feature = "paging")] + Trap::Exception(E::LoadPageFault) => { + let vaddr = riscv::register::stval::read(); + crate::trap::handle_page_fault(vaddr, crate::trap::PageFaultCause::READ); + } + #[cfg(feature = "paging")] + Trap::Exception(E::StorePageFault) => { + let vaddr = riscv::register::stval::read(); + crate::trap::handle_page_fault(vaddr, crate::trap::PageFaultCause::WRITE); + } + #[cfg(feature = "paging")] + Trap::Exception(E::InstructionPageFault) => { + let vaddr = riscv::register::stval::read(); + crate::trap::handle_page_fault(vaddr, crate::trap::PageFaultCause::INSTRUCTION); } _ => { panic!( diff --git a/modules/ruxhal/src/arch/x86_64/context.rs b/modules/ruxhal/src/arch/x86_64/context.rs index 110f15c5b..78ad4663c 100644 --- a/modules/ruxhal/src/arch/x86_64/context.rs +++ b/modules/ruxhal/src/arch/x86_64/context.rs @@ -7,7 +7,7 @@ * See the Mulan PSL v2 for more details. */ -use core::{arch::asm, fmt}; +use core::{arch::naked_asm, fmt}; use memory_addr::VirtAddr; /// Saved registers when a trap (interrupt or exception) occurs. @@ -152,6 +152,12 @@ pub struct TaskContext { pub ext_state: ExtendedState, } +impl Default for TaskContext { + fn default() -> Self { + Self::new() + } +} + impl TaskContext { /// Creates a new default context for a new task. pub const fn new() -> Self { @@ -205,9 +211,9 @@ impl TaskContext { } } -#[naked] +#[unsafe(naked)] unsafe extern "C" fn context_switch(_current_stack: &mut u64, _next_stack: &u64) { - asm!( + naked_asm!( " push rbp push rbx @@ -225,6 +231,5 @@ unsafe extern "C" fn context_switch(_current_stack: &mut u64, _next_stack: &u64) pop rbx pop rbp ret", - options(noreturn), ) } diff --git a/modules/ruxhal/src/arch/x86_64/idt.rs b/modules/ruxhal/src/arch/x86_64/idt.rs index 37dba202f..0deb8b811 100644 --- a/modules/ruxhal/src/arch/x86_64/idt.rs +++ b/modules/ruxhal/src/arch/x86_64/idt.rs @@ -41,7 +41,9 @@ impl IdtStruct { ) }; for i in 0..NUM_INT { - entries[i].set_handler_fn(unsafe { core::mem::transmute(ENTRIES[i]) }); + entries[i].set_handler_fn(unsafe { + core::mem::transmute::(ENTRIES[i]) + }); } idt } diff --git a/modules/ruxhal/src/arch/x86_64/mod.rs b/modules/ruxhal/src/arch/x86_64/mod.rs index 973ff5cf6..c2f2cd853 100644 --- a/modules/ruxhal/src/arch/x86_64/mod.rs +++ b/modules/ruxhal/src/arch/x86_64/mod.rs @@ -95,7 +95,7 @@ pub fn read_page_table_root() -> PhysAddr { /// This function is unsafe as it changes the virtual memory address space. pub unsafe fn write_page_table_root(root_paddr: PhysAddr) { let old_root = read_page_table_root(); - trace!("set page table root: {:#x} => {:#x}", old_root, root_paddr); + trace!("set page table root: {old_root:#x} => {root_paddr:#x}"); if old_root != root_paddr { controlregs::cr3_write(root_paddr.as_usize() as _) } @@ -129,7 +129,7 @@ pub const INVALID_TLB_VECTOR: u8 = 0xff; // SPURIOUS APIC INTERRUPT #[inline] pub fn flush_tlb(vaddr: Option) { if let Some(vaddr) = vaddr { - trace!("flush TLB entry: {:#x}", vaddr); + trace!("flush TLB entry: {vaddr:#x}"); unsafe { tlb::flush(vaddr.into()); } @@ -174,7 +174,6 @@ pub fn flush_tlb(vaddr: Option) { #[inline] #[cfg(all(feature = "irq", feature = "paging", feature = "smp"))] pub(crate) fn flush_tlb_ipi_handler() { - // error!("flush TLB entry in IPI handler"); let guard = kernel_guard::NoPreempt::new(); unsafe { let mut flushing_addresses = FLUSHING_ADDRESSES[this_cpu_id()].lock(); diff --git a/modules/ruxhal/src/arch/x86_64/trap.rs b/modules/ruxhal/src/arch/x86_64/trap.rs index 0cbf12256..5d51e44ad 100644 --- a/modules/ruxhal/src/arch/x86_64/trap.rs +++ b/modules/ruxhal/src/arch/x86_64/trap.rs @@ -88,9 +88,8 @@ fn x86_syscall_handler( arg6: usize, ) -> isize { debug!( - "syscall_id: {}, - arg1: {:#x}, arg2: {:#x}, arg3:{:#x}, arg4: {:#x}, arg5:{:#x}, arg6: {:#x}", - syscall_id, arg1, arg2, arg3, arg4, arg5, arg6 + "syscall_id: {syscall_id}, + arg1: {arg1:#x}, arg2: {arg2:#x}, arg3:{arg3:#x}, arg4: {arg4:#x}, arg5:{arg5:#x}, arg6: {arg6:#x}" ); crate::trap::handle_syscall(syscall_id, [arg1, arg2, arg3, arg4, arg5, arg6]) } diff --git a/modules/ruxhal/src/cpu.rs b/modules/ruxhal/src/cpu.rs index 1d25e8880..59bbc1e2b 100644 --- a/modules/ruxhal/src/cpu.rs +++ b/modules/ruxhal/src/cpu.rs @@ -84,8 +84,8 @@ pub unsafe fn set_current_task_ptr(ptr: *const T) { #[allow(dead_code)] pub(crate) fn init_primary(cpu_id: usize) { - percpu::init(ruxconfig::SMP); - percpu::set_local_thread_pointer(cpu_id); + percpu::init(); + percpu::init_percpu_reg(cpu_id); unsafe { CPU_ID.write_current_raw(cpu_id); IS_BSP.write_current_raw(true); @@ -94,7 +94,7 @@ pub(crate) fn init_primary(cpu_id: usize) { #[allow(dead_code)] pub(crate) fn init_secondary(cpu_id: usize) { - percpu::set_local_thread_pointer(cpu_id); + percpu::init_percpu_reg(cpu_id); unsafe { CPU_ID.write_current_raw(cpu_id); IS_BSP.write_current_raw(false); diff --git a/modules/ruxhal/src/irq.rs b/modules/ruxhal/src/irq.rs index a81bb0670..e10740079 100644 --- a/modules/ruxhal/src/irq.rs +++ b/modules/ruxhal/src/irq.rs @@ -23,9 +23,9 @@ static IRQ_HANDLER_TABLE: HandlerTable = HandlerTable::new(); /// Platform-independent IRQ dispatching. #[allow(dead_code)] pub(crate) fn dispatch_irq_common(irq_num: usize) { - trace!("IRQ {}", irq_num); + trace!("IRQ {irq_num}"); if !IRQ_HANDLER_TABLE.handle(irq_num) { - warn!("Unhandled IRQ {}", irq_num); + warn!("Unhandled IRQ {irq_num}"); } } @@ -39,6 +39,6 @@ pub(crate) fn register_handler_common(irq_num: usize, handler: IrqHandler) -> bo set_enable(irq_num, true); return true; } - warn!("register handler for IRQ {} failed", irq_num); + warn!("register handler for IRQ {irq_num} failed"); false } diff --git a/modules/ruxhal/src/lib.rs b/modules/ruxhal/src/lib.rs index 7ecc3b81b..e6a21affa 100644 --- a/modules/ruxhal/src/lib.rs +++ b/modules/ruxhal/src/lib.rs @@ -20,8 +20,8 @@ //! - `aarch64-qemu-virt`: QEMU virt machine with AArch64 ISA. //! - `aarch64-raspi`: Raspberry Pi with AArch64 ISA. //! - `dummy`: If none of the above platform is selected, the dummy platform -//! will be used. In this platform, most of the operations are no-op or -//! `unimplemented!()`. This platform is mainly used for [cargo test]. +//! will be used. In this platform, most of the operations are no-op or +//! `unimplemented!()`. This platform is mainly used for [cargo test]. //! //! # Cargo Features //! @@ -34,22 +34,23 @@ //! [cargo test]: https://doc.rust-lang.org/cargo/guide/tests.html #![no_std] -#![feature(asm_const)] -#![feature(naked_functions)] -#![feature(const_option)] #![feature(doc_auto_cfg)] +#![allow(static_mut_refs)] #[allow(unused_imports)] #[macro_use] extern crate log; -mod platform; +#[cfg(feature = "alloc")] +extern crate alloc; pub mod arch; pub mod cpu; pub mod mem; +mod platform; pub mod time; pub mod trap; +pub mod virtio; #[cfg(feature = "tls")] pub mod tls; @@ -94,55 +95,3 @@ pub use self::platform::platform_init_secondary; /// so we should save cmdline in a buf before this memory is set free #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub static mut COMLINE_BUF: [u8; 256] = [0; 256]; - -#[allow(unused)] -/// read a tty device specified by its name. -pub fn tty_read(buf: &mut [u8], dev_name: &str) -> usize { - #[cfg(not(feature = "tty"))] - { - let mut read_len = 0; - while read_len < buf.len() { - if let Some(c) = console::getchar().map(|c| if c == b'\r' { b'\n' } else { c }) { - buf[read_len] = c; - read_len += 1; - } else { - break; - } - } - read_len - } - - #[cfg(feature = "tty")] - { - tty::tty_read(buf, dev_name) - } -} - -#[cfg(feature = "alloc")] -extern crate alloc; - -/// get all tty devices' names. -#[cfg(feature = "alloc")] -pub fn get_all_device_names() -> alloc::vec::Vec { - #[cfg(feature = "tty")] - { - tty::get_all_device_names() - } - #[cfg(not(feature = "tty"))] - { - alloc::vec![alloc::string::String::from("notty")] - } -} - -/// write a tty device specified by its name. -pub fn tty_write(buf: &[u8], _dev_name: &str) -> usize { - #[cfg(feature = "tty")] - { - tty::tty_write(buf, _dev_name) - } - #[cfg(not(feature = "tty"))] - { - console::write_bytes(buf); - return buf.len(); - } -} diff --git a/modules/ruxhal/src/mem.rs b/modules/ruxhal/src/mem.rs index b05409a6b..8f9fb152d 100644 --- a/modules/ruxhal/src/mem.rs +++ b/modules/ruxhal/src/mem.rs @@ -11,8 +11,6 @@ use core::fmt; -#[cfg(feature = "paging")] -use crate::paging::pte_query; #[doc(no_inline)] pub use memory_addr::{PhysAddr, VirtAddr, PAGE_SIZE_4K}; @@ -55,6 +53,20 @@ pub struct MemRegion { pub name: &'static str, } +/// A trait for address translation. +#[crate_interface::def_interface] +pub trait AddressTranslate { + /// Translates a virtual address to a physical address. + fn virt_to_phys(vaddr: VirtAddr) -> Option { + Some(direct_virt_to_phys(vaddr).into()) + } +} + +/// translates a virtual address to a physical address. +pub fn address_translate(vaddr: VirtAddr) -> Option { + crate_interface::call_interface!(AddressTranslate::virt_to_phys, vaddr) +} + /// Converts a virtual address to a physical address. /// /// It assumes that there is a linear mapping with the offset @@ -69,20 +81,6 @@ pub const fn direct_virt_to_phys(vaddr: VirtAddr) -> PhysAddr { PhysAddr::from(vaddr.as_usize() - ruxconfig::PHYS_VIRT_OFFSET) } -/// Converts a virtual address to a physical address. -/// -/// When paging is enabled, query physical address from the page table -#[inline] -pub fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr { - #[cfg(feature = "paging")] - match pte_query(vaddr) { - Ok((paddr, _, _)) => paddr, - Err(_) => PhysAddr::from(0_usize), // for address unmapped - } - #[cfg(not(feature = "paging"))] - direct_virt_to_phys(vaddr) -} - /// Converts a physical address to a virtual address. /// /// It assumes that there is a linear mapping with the offset @@ -152,20 +150,36 @@ pub(crate) fn default_mmio_regions() -> impl Iterator { }) } +/// Returns the default MMIO memory regions (from [`ruxconfig::DTB_ADDR`]). +#[allow(dead_code)] +pub(crate) fn default_dtb_regions() -> impl Iterator { + let dtb_addr = PhysAddr::from(ruxconfig::DTB_ADDR).align_up_4k(); + let dtb_resgion = MemRegion { + paddr: dtb_addr, + size: 0x80000, + flags: MemRegionFlags::READ | MemRegionFlags::WRITE | MemRegionFlags::EXECUTE, + name: "dtb region", + }; + core::iter::once(dtb_resgion) +} + /// Returns the default free memory regions (kernel image end to physical memory end). #[allow(dead_code)] pub(crate) fn default_free_regions() -> impl Iterator { - let start = direct_virt_to_phys((_ekernel as usize).into()).align_up_4k(); + let start_free = direct_virt_to_phys((_ekernel as usize).into()).align_up_4k(); let end = PhysAddr::from(ruxconfig::PHYS_MEMORY_END).align_down_4k(); - core::iter::once(MemRegion { - paddr: start, - size: end.as_usize() - start.as_usize(), + + let region_free = MemRegion { + paddr: start_free, + size: end.as_usize() - start_free.as_usize(), flags: MemRegionFlags::FREE | MemRegionFlags::READ | MemRegionFlags::WRITE | MemRegionFlags::EXECUTE, name: "free memory", - }) + }; + + core::iter::once(region_free) } /// Fills the `.bss` section with zeros. @@ -178,6 +192,7 @@ pub(crate) fn clear_bss() { } extern "C" { + fn _skernel(); fn _stext(); fn _etext(); fn _srodata(); diff --git a/modules/ruxhal/src/paging.rs b/modules/ruxhal/src/paging.rs index eb2311dc7..96fdee92b 100644 --- a/modules/ruxhal/src/paging.rs +++ b/modules/ruxhal/src/paging.rs @@ -9,19 +9,14 @@ //! Page table manipulation. extern crate alloc; -use crate::arch::flush_tlb; -use spinlock::SpinNoIrq; use crate::mem::{ - direct_virt_to_phys, memory_regions, phys_to_virt, MemRegionFlags, PhysAddr, VirtAddr, - PAGE_SIZE_4K, + direct_virt_to_phys, phys_to_virt, MemRegionFlags, PhysAddr, VirtAddr, PAGE_SIZE_4K, }; use axalloc::global_allocator; -use lazy_init::LazyInit; #[doc(no_inline)] -use page_table::{MappingFlags, PageSize, PagingError, PagingIf, PagingResult}; - +use page_table::{MappingFlags, PagingIf}; impl From for MappingFlags { fn from(f: MemRegionFlags) -> Self { let mut ret = Self::empty(); @@ -78,116 +73,3 @@ cfg_if::cfg_if! { pub type PageTable = page_table::aarch64::A64PageTable; } } - -pub(crate) static KERNEL_PAGE_TABLE: LazyInit> = LazyInit::new(); - -/// Remap the regions for kernel memory -pub fn remap_kernel_memory() -> PagingResult { - if crate::cpu::this_cpu_is_bsp() { - let mut kernel_page_table = PageTable::try_new()?; - for r in memory_regions() { - kernel_page_table.map_region( - phys_to_virt(r.paddr), - r.paddr, - r.size, - r.flags.into(), - true, - )?; - } - - KERNEL_PAGE_TABLE.init_by(SpinNoIrq::new(kernel_page_table)); - } - unsafe { crate::arch::write_page_table_root(KERNEL_PAGE_TABLE.lock().root_paddr()) }; - Ok(()) -} - -/// Temporarily, `malloc` alloc memory in heap simply, and it can not be swapped -/// into swap file. Once the memory is not enough with all memory alloced, it -/// will be too late, as there will be no memory for `malloc` any more. In practice, -/// this is highly likely to cause errors of insufficient memory. To prevent this, -/// mmapping will not alloc from physical address to avoid this. -/// -/// After the page of `malloc` can be swapped, or it raises a propriately handler -/// to swap page when memory is not enough, it will be okay to delete this. -const PAGE_NUM_MIN: usize = 1024; - -/// Obtain fake VirtAddr addresses without performing virtual memory mapping -/// to prevent physical competition between multiple threads. -/// After call the function. the page is alloced in allocator but its virtual -/// address is still on linear mapping region. -/// use `do_pte_map` to do actually page mapping after call this function. -pub fn alloc_page_preload() -> Result { - if global_allocator().available_pages() < PAGE_NUM_MIN { - warn!( - "available page num is {:?}", - global_allocator().available_pages() - ); - return Err(PagingError::NoMemory); - }; - match global_allocator().alloc_pages(1, PAGE_SIZE_4K) { - Ok(fake_vaddr) => Ok(VirtAddr::from(fake_vaddr)), - Err(_) => Err(PagingError::NoMemory), - } -} - -/// Unmap memory for an mmap-induced PageFault and updating PTE entries. -/// After call the function. the page is alloced in allocator but its virtual -/// address is still on linear mapping region. -/// use `do_pte_map` to do actually page mapping after call this function. -pub fn pte_swap_preload(swaped_vaddr: VirtAddr) -> PagingResult { - trace!("swapping swaped_vaddr: 0x{:x?}", swaped_vaddr,); - let mut kernel_page_table = KERNEL_PAGE_TABLE.lock(); - let (paddr, _) = kernel_page_table.unmap(swaped_vaddr)?; - flush_tlb(Some(swaped_vaddr)); - Ok(phys_to_virt(paddr)) -} - -/// Map memory for an mmap-induced PageFault and updating PTE entries, -/// This function must be called after `alloc_page_preload` and -/// `pte_swap_preload` when the mapping operator is ready. -pub fn do_pte_map(vaddr: VirtAddr, fake_vaddr: VirtAddr, flags: MappingFlags) -> PagingResult { - KERNEL_PAGE_TABLE.lock().map( - vaddr, - direct_virt_to_phys(fake_vaddr), - PageSize::Size4K, - flags, - ) -} - -/// Query PTE entries of the virtual address. -/// -/// get the physical address information corresponding to the virtual address from the page table -pub fn pte_query(vaddr: VirtAddr) -> PagingResult<(PhysAddr, MappingFlags, PageSize)> { - let kernel_page_table = KERNEL_PAGE_TABLE.lock(); - kernel_page_table.query(vaddr) -} - -/// Update flags or physical address for an PTE entries. -/// -/// change the physical address or access permissions mapped by the virtual address -pub fn pte_update_page( - vaddr: VirtAddr, - paddr: Option, - flags: Option, -) -> PagingResult { - trace!( - "updating vaddr:0x{:x?} paddr:0x{:x?} flags:0x{:x?}", - vaddr, - paddr, - flags - ); - KERNEL_PAGE_TABLE.lock().update(vaddr, paddr, flags)?; - flush_tlb(Some(vaddr)); - Ok(()) -} - -/// Unmapping and decalloc memory for an page in page table. -/// -/// release the corresponding memory at the same time -pub fn pte_unmap_page(vaddr: VirtAddr) -> PagingResult { - trace!("unmapping vaddr: 0x{:x?}", vaddr); - let (paddr, _) = KERNEL_PAGE_TABLE.lock().unmap(vaddr)?; - global_allocator().dealloc_pages(phys_to_virt(paddr).as_usize(), 1); - flush_tlb(Some(vaddr)); - Ok(()) -} diff --git a/modules/ruxhal/src/platform/aarch64_common/boot.rs b/modules/ruxhal/src/platform/aarch64_common/boot.rs index af39895ae..58562e9ff 100644 --- a/modules/ruxhal/src/platform/aarch64_common/boot.rs +++ b/modules/ruxhal/src/platform/aarch64_common/boot.rs @@ -86,7 +86,7 @@ unsafe fn init_mmu() { barrier::isb(barrier::SY); // Set both TTBR0 and TTBR1 - let root_paddr = PhysAddr::from(BOOT_PT_L0.as_ptr() as usize).as_usize() as _; + let root_paddr = PhysAddr::from(&raw const BOOT_PT_L0 as usize).as_usize() as _; TTBR0_EL1.set(root_paddr); TTBR1_EL1.set(root_paddr); @@ -106,17 +106,17 @@ unsafe fn enable_fp() { } unsafe fn init_boot_page_table() { - crate::platform::mem::init_boot_page_table(&mut BOOT_PT_L0, &mut BOOT_PT_L1); + crate::platform::mem::init_boot_page_table(&raw mut BOOT_PT_L0, &raw mut BOOT_PT_L1); } /// The earliest entry point for the primary CPU. -#[naked] +#[unsafe(naked)] #[no_mangle] #[link_section = ".text.boot"] unsafe extern "C" fn _start() -> ! { // PC = 0x8_0000 // X0 = dtb - core::arch::asm!(" + core::arch::naked_asm!(" mrs x19, mpidr_el1 and x19, x19, #0xffffff // get current CPU id mov x20, x0 // save DTB pointer @@ -126,9 +126,9 @@ unsafe extern "C" fn _start() -> ! { mov sp, x8 bl {switch_to_el1} // switch to EL1 + bl {enable_fp} // enable fp/neon bl {init_boot_page_table} bl {init_mmu} // setup MMU - bl {enable_fp} // enable fp/neon mov x8, {phys_virt_offset} // set SP to the high address add sp, sp, x8 @@ -146,24 +146,24 @@ unsafe extern "C" fn _start() -> ! { boot_stack_size = const TASK_STACK_SIZE, phys_virt_offset = const ruxconfig::PHYS_VIRT_OFFSET, entry = sym crate::platform::rust_entry, - options(noreturn), + ) } /// The earliest entry point for the secondary CPUs. #[cfg(feature = "smp")] -#[naked] +#[unsafe(naked)] #[no_mangle] #[link_section = ".text.boot"] unsafe extern "C" fn _start_secondary() -> ! { - core::arch::asm!(" + core::arch::naked_asm!(" mrs x19, mpidr_el1 and x19, x19, #0xffffff // get current CPU id mov sp, x0 + bl {enable_fp} bl {switch_to_el1} bl {init_mmu} - bl {enable_fp} mov x8, {phys_virt_offset} // set SP to the high address add sp, sp, x8 @@ -177,6 +177,6 @@ unsafe extern "C" fn _start_secondary() -> ! { enable_fp = sym enable_fp, phys_virt_offset = const ruxconfig::PHYS_VIRT_OFFSET, entry = sym crate::platform::rust_entry_secondary, - options(noreturn), + ) } diff --git a/modules/ruxhal/src/platform/aarch64_common/generic_timer.rs b/modules/ruxhal/src/platform/aarch64_common/generic_timer.rs index 81a4e421f..9d52f46ec 100644 --- a/modules/ruxhal/src/platform/aarch64_common/generic_timer.rs +++ b/modules/ruxhal/src/platform/aarch64_common/generic_timer.rs @@ -9,7 +9,9 @@ #![allow(unused_imports)] -use aarch64_cpu::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0}; +use aarch64_cpu::registers::{ + CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0, CNTV_CTL_EL0, CNTV_TVAL_EL0, +}; use ratio::Ratio; use tock_registers::interfaces::{Readable, Writeable}; @@ -44,9 +46,9 @@ pub fn set_oneshot_timer(deadline_ns: u64) { if cnptct < cnptct_deadline { let interval = cnptct_deadline - cnptct; debug_assert!(interval <= u32::MAX as u64); - CNTP_TVAL_EL0.set(interval); + CNTV_TVAL_EL0.set(interval); } else { - CNTP_TVAL_EL0.set(0); + CNTV_TVAL_EL0.set(0); } } @@ -62,8 +64,9 @@ pub(crate) fn init_early() { pub(crate) fn init_percpu() { #[cfg(feature = "irq")] { - CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET); - CNTP_TVAL_EL0.set(0); + // TODO: support physical timmer + CNTV_CTL_EL0.write(CNTV_CTL_EL0::ENABLE::SET); + CNTV_TVAL_EL0.set(0); crate::platform::irq::set_enable(crate::platform::irq::TIMER_IRQ_NUM, true); } } diff --git a/modules/ruxhal/src/platform/aarch64_common/gic.rs b/modules/ruxhal/src/platform/aarch64_common/gicv2.rs similarity index 85% rename from modules/ruxhal/src/platform/aarch64_common/gic.rs rename to modules/ruxhal/src/platform/aarch64_common/gicv2.rs index 1df2ef11e..fc188110f 100644 --- a/modules/ruxhal/src/platform/aarch64_common/gic.rs +++ b/modules/ruxhal/src/platform/aarch64_common/gicv2.rs @@ -16,12 +16,19 @@ use spinlock::SpinNoIrq; /// The maximum number of IRQs. pub const MAX_IRQ_COUNT: usize = 1024; -/// The timer IRQ number. -pub const TIMER_IRQ_NUM: usize = translate_irq(14, InterruptType::PPI).unwrap(); +/// The virt timer IRQ number. +/// Physical timer IRQ number is 14 +pub const TIMER_IRQ_NUM: usize = translate_irq(11, InterruptType::PPI).unwrap(); +#[cfg(not(feature = "virtio_console"))] /// The UART IRQ number. pub const UART_IRQ_NUM: usize = translate_irq(ruxconfig::UART_IRQ, InterruptType::SPI).unwrap(); +#[cfg(all(feature = "irq", feature = "virtio_console"))] +/// The Virtio-console IRQ number +pub const VIRTIO_CONSOLE_IRQ_NUM: usize = + translate_irq(ruxconfig::VIRTIO_CONSOLE_IRQ, InterruptType::SPI).unwrap(); + const GICD_BASE: PhysAddr = PhysAddr::from(ruxconfig::GICD_PADDR); const GICC_BASE: PhysAddr = PhysAddr::from(ruxconfig::GICC_PADDR); @@ -56,7 +63,7 @@ pub fn dispatch_irq(_unused: usize) { } /// Initializes GICD, GICC on the primary CPU. -pub(crate) fn init_primary() { +pub(crate) fn init_primary(_cpu_id: usize) { info!("Initialize GICv2..."); GICD.lock().init(); GICC.init(); diff --git a/modules/ruxhal/src/platform/aarch64_common/gicv3.rs b/modules/ruxhal/src/platform/aarch64_common/gicv3.rs new file mode 100644 index 000000000..8d2aeed58 --- /dev/null +++ b/modules/ruxhal/src/platform/aarch64_common/gicv3.rs @@ -0,0 +1,84 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use crate::{irq::IrqHandler, mem::phys_to_virt}; +use arm_gic::gic_v3::{GicDistributor, GicRedistributor}; +use arm_gic::{translate_irq, InterruptType}; +use lazy_init::LazyInit; +use memory_addr::PhysAddr; +use spinlock::SpinNoIrq; + +/// The maximum number of IRQs. +pub const MAX_IRQ_COUNT: usize = 1024; + +/// The virt timer IRQ number. +/// Physical timer IRQ number is 14 +pub const TIMER_IRQ_NUM: usize = translate_irq(11, InterruptType::PPI).unwrap(); + +#[cfg(not(feature = "virtio_console"))] +/// The UART IRQ number. +pub const UART_IRQ_NUM: usize = translate_irq(ruxconfig::UART_IRQ, InterruptType::SPI).unwrap(); + +#[cfg(all(feature = "irq", feature = "virtio_console"))] +/// The Virtio-console IRQ number +pub const VIRTIO_CONSOLE_IRQ_NUM: usize = + translate_irq(ruxconfig::VIRTIO_CONSOLE_IRQ, InterruptType::SPI).unwrap(); + +const GICD_BASE: PhysAddr = PhysAddr::from(ruxconfig::GICD_PADDR); +const GICR_BASE: PhysAddr = PhysAddr::from(ruxconfig::GICR_PADDR); +const GICR_STRIDE: usize = 0x20000; + +static GICD: SpinNoIrq = + SpinNoIrq::new(GicDistributor::new(phys_to_virt(GICD_BASE).as_mut_ptr())); + +static GICR: LazyInit> = LazyInit::new(); + +/// Enables or disables the given IRQ. +pub fn set_enable(irq_num: usize, enabled: bool) { + trace!("set enable: {irq_num} {enabled}"); + if irq_num < 32 { + GICR.lock().set_enable(irq_num as _, enabled); + } else { + GICD.lock().set_enable(irq_num as _, enabled); + } +} + +/// Registers an IRQ handler for the given IRQ. +/// +/// It also enables the IRQ if the registration succeeds. It returns `false` if +/// the registration failed. +pub fn register_handler(irq_num: usize, handler: IrqHandler) -> bool { + trace!("register handler irq {irq_num}"); + crate::irq::register_handler_common(irq_num, handler) +} + +/// Dispatches the IRQ. +/// +/// This function is called by the common interrupt handler. It looks +/// up in the IRQ handler table and calls the corresponding handler. If +/// necessary, it also acknowledges the interrupt controller after handling. +pub fn dispatch_irq(_unused: usize) { + GICR.lock() + .handle_irq(|irq_num| crate::irq::dispatch_irq_common(irq_num as _)); +} + +/// Initializes GICD, GICC on the primary CPU. +pub(crate) fn init_primary(cpu_id: usize) { + info!("Initialize GICv3..."); + debug!("GICv3 GICR addr {:x}", GICR_BASE + cpu_id * GICR_STRIDE); + GICR.init_by(SpinNoIrq::new(GicRedistributor::new( + phys_to_virt(GICR_BASE + cpu_id * GICR_STRIDE).as_mut_ptr(), + ))); + GICR.lock().init(); + GICD.lock().init(); +} + +/// Initializes GICC on secondary CPUs. +#[cfg(feature = "smp")] +pub(crate) fn init_secondary() {} diff --git a/modules/ruxhal/src/platform/aarch64_common/mod.rs b/modules/ruxhal/src/platform/aarch64_common/mod.rs index 572642be6..db60e059d 100644 --- a/modules/ruxhal/src/platform/aarch64_common/mod.rs +++ b/modules/ruxhal/src/platform/aarch64_common/mod.rs @@ -6,6 +6,7 @@ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PSL v2 for more details. */ +// TODO: remove this line after fixing the code style mod boot; @@ -14,9 +15,14 @@ pub mod generic_timer; pub mod psci; #[cfg(feature = "irq")] -pub mod gic; +#[cfg(not(feature = "gic-v3"))] +pub mod gicv2; -#[cfg(not(platform_family = "aarch64-bsta1000b"))] +#[cfg(feature = "irq")] +#[cfg(feature = "gic-v3")] +pub mod gicv3; + +#[cfg(not(any(platform_family = "aarch64-bsta1000b", feature = "virtio_console")))] pub mod pl011; #[cfg(feature = "rtc")] diff --git a/modules/ruxhal/src/platform/aarch64_common/pl011.rs b/modules/ruxhal/src/platform/aarch64_common/pl011.rs index a311f9834..d4b4db036 100644 --- a/modules/ruxhal/src/platform/aarch64_common/pl011.rs +++ b/modules/ruxhal/src/platform/aarch64_common/pl011.rs @@ -38,6 +38,7 @@ impl RxRingBuffer { } } + #[allow(unused)] fn push(&mut self, n: u8) { if self.tail != self.head || self.empty { self.buffer[self.tail] = n; @@ -100,24 +101,10 @@ pub fn init_early() { UART.inner.lock().init(); } -#[cfg(feature = "tty")] -static DRIVER_INDEX: lazy_init::LazyInit = lazy_init::LazyInit::new(); -#[cfg(feature = "tty")] -static DEV_INDEX: lazy_init::LazyInit = lazy_init::LazyInit::new(); - /// Set UART IRQ Enable pub fn init() { #[cfg(feature = "irq")] { - #[cfg(feature = "tty")] - { - let ops = tty::TtyDriverOps { putchar }; - let driver_index = tty::register_driver(ops, "ttyS"); - let dev_index = tty::register_device(driver_index); - assert_ne!(dev_index, -1); - DRIVER_INDEX.init_by(driver_index); - DEV_INDEX.init_by(dev_index as _); - } crate::irq::register_handler(crate::platform::irq::UART_IRQ_NUM, irq_handler); crate::irq::set_enable(crate::platform::irq::UART_IRQ_NUM, true); } @@ -130,22 +117,14 @@ pub fn irq_handler() { let is_receive_interrupt = dev.is_receive_interrupt(); if is_receive_interrupt { dev.ack_interrupts(); - #[cfg(not(feature = "tty"))] + + let mut buf = [0u8; 128]; + let mut len = 0; + while let Some(c) = dev.getchar() { - UART.buffer.lock().push(c); - } - #[cfg(feature = "tty")] - { - let mut buf = [0u8; 128]; - let mut len = 0; - - while let Some(c) = dev.getchar() { - buf[len] = c; - len += 1; - } - let drv_idx = *DRIVER_INDEX.try_get().unwrap(); - let dev_idx = *DEV_INDEX.try_get().unwrap(); - tty::tty_receive_buf(drv_idx, dev_idx, &buf[..len]); + buf[len] = c; + len += 1; } + tty::tty_receive_buf(&buf[..len]); } } diff --git a/modules/ruxhal/src/platform/aarch64_common/psci.rs b/modules/ruxhal/src/platform/aarch64_common/psci.rs index 12a5b7abe..6bd0f9ddb 100644 --- a/modules/ruxhal/src/platform/aarch64_common/psci.rs +++ b/modules/ruxhal/src/platform/aarch64_common/psci.rs @@ -119,10 +119,10 @@ pub fn system_off() -> ! { /// `entry_point` is the physical address of the secondary CPU's entry point. /// `arg` will be passed to the `X0` register of the secondary CPU. pub fn cpu_on(target_cpu: usize, entry_point: usize, arg: usize) { - info!("Starting CPU {:x} ON ...", target_cpu); + info!("Starting CPU {target_cpu:x} ON ..."); let res = psci_call(PSCI_0_2_FN64_CPU_ON, target_cpu, entry_point, arg); if let Err(e) = res { - error!("failed to boot CPU {:x} ({:?})", target_cpu, e); + error!("failed to boot CPU {target_cpu:x} ({e:?})"); } } diff --git a/modules/ruxhal/src/platform/aarch64_qemu_virt/mem.rs b/modules/ruxhal/src/platform/aarch64_qemu_virt/mem.rs index 9d7788717..5f5eb9af6 100644 --- a/modules/ruxhal/src/platform/aarch64_qemu_virt/mem.rs +++ b/modules/ruxhal/src/platform/aarch64_qemu_virt/mem.rs @@ -12,23 +12,25 @@ use page_table_entry::{aarch64::A64PTE, GenericPTE, MappingFlags}; /// Returns platform-specific memory regions. pub(crate) fn platform_regions() -> impl Iterator { - crate::mem::default_free_regions().chain(crate::mem::default_mmio_regions()) + crate::mem::default_free_regions() + .chain(crate::mem::default_mmio_regions()) + .chain(crate::mem::default_dtb_regions()) } pub(crate) unsafe fn init_boot_page_table( - boot_pt_l0: &mut [A64PTE; 512], - boot_pt_l1: &mut [A64PTE; 512], + boot_pt_l0: *mut [A64PTE; 512], + boot_pt_l1: *mut [A64PTE; 512], ) { // 0x0000_0000_0000 ~ 0x0080_0000_0000, table - boot_pt_l0[0] = A64PTE::new_table(PhysAddr::from(boot_pt_l1.as_ptr() as usize)); + (*boot_pt_l0)[0] = A64PTE::new_table(PhysAddr::from(boot_pt_l1 as usize)); // 0x0000_0000_0000..0x0000_4000_0000, 1G block, device memory - boot_pt_l1[0] = A64PTE::new_page( + (*boot_pt_l1)[0] = A64PTE::new_page( PhysAddr::from(0), MappingFlags::READ | MappingFlags::WRITE | MappingFlags::DEVICE, true, ); // 0x0000_4000_0000..0x0000_8000_0000, 1G block, normal memory - boot_pt_l1[1] = A64PTE::new_page( + (*boot_pt_l1)[1] = A64PTE::new_page( PhysAddr::from(0x4000_0000), MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, true, @@ -36,19 +38,19 @@ pub(crate) unsafe fn init_boot_page_table( #[cfg(feature = "musl")] { // 0x0000_8000_0000..0x0000_C000_0000, 1G block, normal memory - boot_pt_l1[2] = A64PTE::new_page( + (*boot_pt_l1)[2] = A64PTE::new_page( PhysAddr::from(0x8000_0000), MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, true, ); // 0x0000_C000_0000..0x0001_0000_0000, 1G block, normal memory - boot_pt_l1[3] = A64PTE::new_page( + (*boot_pt_l1)[3] = A64PTE::new_page( PhysAddr::from(0xC000_0000), MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, true, ); // 0x0001_0000_0000..0x0001_4000_0000, 1G block, normal memory - boot_pt_l1[4] = A64PTE::new_page( + (*boot_pt_l1)[4] = A64PTE::new_page( PhysAddr::from(0x1_0000_0000), MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, true, diff --git a/modules/ruxhal/src/platform/aarch64_qemu_virt/mod.rs b/modules/ruxhal/src/platform/aarch64_qemu_virt/mod.rs index 34236fb04..d0201ab91 100644 --- a/modules/ruxhal/src/platform/aarch64_qemu_virt/mod.rs +++ b/modules/ruxhal/src/platform/aarch64_qemu_virt/mod.rs @@ -14,11 +14,17 @@ pub mod mp; #[cfg(feature = "irq")] pub mod irq { - pub use crate::platform::aarch64_common::gic::*; + #[cfg(not(feature = "gic-v3"))] + pub use crate::platform::aarch64_common::gicv2::*; + #[cfg(feature = "gic-v3")] + pub use crate::platform::aarch64_common::gicv3::*; } pub mod console { + #[cfg(not(feature = "virtio_console"))] pub use crate::platform::aarch64_common::pl011::*; + #[cfg(feature = "virtio_console")] + pub use crate::virtio::virtio_console::*; } pub mod time { @@ -46,6 +52,7 @@ pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { dtb::init(crate::mem::phys_to_virt(dtb.into()).as_ptr()); } crate::cpu::init_primary(cpu_id); + #[cfg(not(feature = "virtio_console"))] super::aarch64_common::pl011::init_early(); super::aarch64_common::generic_timer::init_early(); rust_main(cpu_id, dtb); @@ -62,19 +69,30 @@ pub(crate) unsafe extern "C" fn rust_entry_secondary(cpu_id: usize) { /// Initializes the platform devices for the primary CPU. /// /// For example, the interrupt controller and the timer. -pub fn platform_init() { +pub fn platform_init(cpu_id: usize) { #[cfg(feature = "irq")] - super::aarch64_common::gic::init_primary(); + #[cfg(not(feature = "gic-v3"))] + super::aarch64_common::gicv2::init_primary(cpu_id); + #[cfg(feature = "irq")] + #[cfg(feature = "gic-v3")] + super::aarch64_common::gicv3::init_primary(cpu_id); super::aarch64_common::generic_timer::init_percpu(); #[cfg(feature = "rtc")] super::aarch64_common::pl031::init(); + #[cfg(not(feature = "virtio_console"))] super::aarch64_common::pl011::init(); + #[cfg(feature = "virtio_console")] + crate::virtio::virtio_console::enable_interrupt(); } /// Initializes the platform devices for secondary CPUs. #[cfg(feature = "smp")] pub fn platform_init_secondary() { #[cfg(feature = "irq")] - super::aarch64_common::gic::init_secondary(); + #[cfg(not(feature = "gic-v3"))] + super::aarch64_common::gicv2::init_secondary(); + #[cfg(feature = "irq")] + #[cfg(feature = "gic-v3")] + super::aarch64_common::gicv3::init_secondary(); super::aarch64_common::generic_timer::init_percpu(); } diff --git a/modules/ruxhal/src/platform/aarch64_raspi/mod.rs b/modules/ruxhal/src/platform/aarch64_raspi/mod.rs index 57d2fda90..bd158cc09 100644 --- a/modules/ruxhal/src/platform/aarch64_raspi/mod.rs +++ b/modules/ruxhal/src/platform/aarch64_raspi/mod.rs @@ -67,9 +67,14 @@ pub(crate) unsafe extern "C" fn rust_entry_secondary(cpu_id: usize) { /// Initializes the platform devices for the primary CPU. /// /// For example, the interrupt controller and the timer. -pub fn platform_init() { +pub fn platform_init(cpu_id: usize) { #[cfg(feature = "irq")] - super::aarch64_common::gic::init_primary(); + #[cfg(not(feature = "gic-v3"))] + super::aarch64_common::gicv2::init_primary(cpu_id); + #[cfg(feature = "irq")] + #[cfg(feature = "gic-v3")] + super::aarch64_common::gicv3::init_primary(cpu_id); + super::aarch64_common::generic_timer::init_percpu(); super::aarch64_common::generic_timer::init_percpu(); #[cfg(feature = "rtc")] super::aarch64_common::pl031::init(); @@ -80,6 +85,10 @@ pub fn platform_init() { #[cfg(feature = "smp")] pub fn platform_init_secondary() { #[cfg(feature = "irq")] - super::aarch64_common::gic::init_secondary(); + #[cfg(not(feature = "gic-v3"))] + super::aarch64_common::gicv2::init_secondary(); + #[cfg(feature = "irq")] + #[cfg(feature = "gic-v3")] + super::aarch64_common::gicv3::init_secondary(); super::aarch64_common::generic_timer::init_percpu(); } diff --git a/modules/ruxhal/src/platform/aarch64_raspi/mp.rs b/modules/ruxhal/src/platform/aarch64_raspi/mp.rs index bfd89e06d..c29d1ea88 100644 --- a/modules/ruxhal/src/platform/aarch64_raspi/mp.rs +++ b/modules/ruxhal/src/platform/aarch64_raspi/mp.rs @@ -14,10 +14,10 @@ extern "C" { fn _start_secondary(); } -#[naked] +#[unsafe(naked)] #[link_section = ".text.boot"] unsafe extern "C" fn modify_stack_and_start() { - core::arch::asm!(" + core::arch::naked_asm!(" ldr x21, ={secondary_boot_stack} // the secondary CPU hasn't set the TTBR1 mov x8, {phys_virt_offset} // minus the offset to get the phys addr of the boot stack sub x21, x21, x8 diff --git a/modules/ruxhal/src/platform/dummy/mod.rs b/modules/ruxhal/src/platform/dummy/mod.rs index 164f9b6a8..1974aba6a 100644 --- a/modules/ruxhal/src/platform/dummy/mod.rs +++ b/modules/ruxhal/src/platform/dummy/mod.rs @@ -99,7 +99,7 @@ pub mod irq { } /// Initializes the platform devices for the primary CPU. -pub fn platform_init() {} +pub fn platform_init(cpu_id: usize) {} /// Initializes the platform devices for secondary CPUs. #[cfg(feature = "smp")] diff --git a/modules/ruxhal/src/platform/riscv64_qemu_virt/boot.rs b/modules/ruxhal/src/platform/riscv64_qemu_virt/boot.rs index d086ec859..69f34e252 100644 --- a/modules/ruxhal/src/platform/riscv64_qemu_virt/boot.rs +++ b/modules/ruxhal/src/platform/riscv64_qemu_virt/boot.rs @@ -39,14 +39,14 @@ unsafe fn init_mmu() { } /// The earliest entry point for the primary CPU. -#[naked] +#[unsafe(naked)] #[no_mangle] #[link_section = ".text.boot"] unsafe extern "C" fn _start() -> ! { // PC = 0x8020_0000 // a0 = hartid // a1 = dtb - core::arch::asm!(" + core::arch::naked_asm!(" mv s0, a0 // save hartid mv s1, a1 // save DTB pointer la sp, {boot_stack} @@ -71,19 +71,19 @@ unsafe extern "C" fn _start() -> ! { init_boot_page_table = sym init_boot_page_table, init_mmu = sym init_mmu, entry = sym super::rust_entry, - options(noreturn), + ) } /// The earliest entry point for secondary CPUs. #[cfg(feature = "smp")] -#[naked] +#[unsafe(naked)] #[no_mangle] #[link_section = ".text.boot"] -unsafe extern "C" fn _start_secondary() -> ! { +unsafe extern "C" fn _start_secondary() { // a0 = hartid // a1 = SP - core::arch::asm!(" + core::arch::naked_asm!(" mv s0, a0 // save hartid mv sp, a1 // set SP @@ -101,6 +101,6 @@ unsafe extern "C" fn _start_secondary() -> ! { phys_virt_offset = const PHYS_VIRT_OFFSET, init_mmu = sym init_mmu, entry = sym super::rust_entry_secondary, - options(noreturn), + ) } diff --git a/modules/ruxhal/src/platform/riscv64_qemu_virt/mod.rs b/modules/ruxhal/src/platform/riscv64_qemu_virt/mod.rs index d0ad8d791..c0f8d2ef5 100644 --- a/modules/ruxhal/src/platform/riscv64_qemu_virt/mod.rs +++ b/modules/ruxhal/src/platform/riscv64_qemu_virt/mod.rs @@ -27,6 +27,7 @@ extern "C" { fn rust_main_secondary(cpu_id: usize); } +#[no_mangle] unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { crate::mem::clear_bss(); crate::cpu::init_primary(cpu_id); @@ -34,6 +35,9 @@ unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { unsafe { dtb::init(crate::mem::phys_to_virt(dtb.into()).as_ptr()); } + core::arch::asm! { + "csrw sscratch,gp" + } rust_main(cpu_id, dtb); } @@ -41,13 +45,16 @@ unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { unsafe extern "C" fn rust_entry_secondary(cpu_id: usize) { crate::arch::set_trap_vector_base(trap_vector_base as usize); crate::cpu::init_secondary(cpu_id); + core::arch::asm! { + "csrw sscratch, gp" + } rust_main_secondary(cpu_id); } /// Initializes the platform devices for the primary CPU. /// /// For example, the interrupt controller and the timer. -pub fn platform_init() { +pub fn platform_init(_cpu_id: usize) { #[cfg(feature = "irq")] self::irq::init_percpu(); self::time::init_percpu(); diff --git a/modules/ruxhal/src/platform/x86_pc/mod.rs b/modules/ruxhal/src/platform/x86_pc/mod.rs index 2cce92b18..1dcccfe0d 100644 --- a/modules/ruxhal/src/platform/x86_pc/mod.rs +++ b/modules/ruxhal/src/platform/x86_pc/mod.rs @@ -86,7 +86,7 @@ unsafe extern "C" fn rust_entry_secondary(magic: usize) { } /// Initializes the platform devices for the primary CPU. -pub fn platform_init() { +pub fn platform_init(_cpu_id: usize) { self::apic::init_primary(); self::time::init_primary(); } diff --git a/modules/ruxhal/src/platform/x86_pc/rtc.rs b/modules/ruxhal/src/platform/x86_pc/rtc.rs index 6744bb7d1..fd5bf8b24 100644 --- a/modules/ruxhal/src/platform/x86_pc/rtc.rs +++ b/modules/ruxhal/src/platform/x86_pc/rtc.rs @@ -13,6 +13,7 @@ use core::marker::PhantomData; use core::ops::{BitAnd, BitOr, Not}; use lazy_init::LazyInit; +#[allow(dead_code)] pub trait Io { type Value: Copy + PartialEq @@ -141,6 +142,12 @@ pub struct Rtc { nmi: bool, } +impl Default for Rtc { + fn default() -> Self { + Self::new() + } +} + impl Rtc { /// Create new empty RTC pub fn new() -> Self { diff --git a/modules/ruxhal/src/platform/x86_pc/time.rs b/modules/ruxhal/src/platform/x86_pc/time.rs index 9028cc2ac..35862cdf1 100644 --- a/modules/ruxhal/src/platform/x86_pc/time.rs +++ b/modules/ruxhal/src/platform/x86_pc/time.rs @@ -74,7 +74,7 @@ pub(super) fn init_primary() { use x2apic::lapic::{TimerDivide, TimerMode}; let lapic = super::apic::local_apic(); lapic.set_timer_mode(TimerMode::OneShot); - lapic.set_timer_divide(TimerDivide::Div256); // indeed it is Div1, the name is confusing. + lapic.set_timer_divide(TimerDivide::Div1); // indeed it is Div1, the name is confusing. lapic.enable_timer(); // TODO: calibrate with HPET diff --git a/modules/ruxhal/src/trap.rs b/modules/ruxhal/src/trap.rs index 344ca06ac..d92abf5dd 100644 --- a/modules/ruxhal/src/trap.rs +++ b/modules/ruxhal/src/trap.rs @@ -9,9 +9,11 @@ //! Trap handling. use crate_interface::{call_interface, def_interface}; +#[cfg(feature = "paging")] +use page_table::MappingFlags; /// Several reasons for page missing exceptions. -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum PageFaultCause { /// pageFault caused by memory WRITE. WRITE, @@ -21,6 +23,18 @@ pub enum PageFaultCause { INSTRUCTION, } +/// `PageFaultCause` corresponding to `MappingFlags`. +#[cfg(feature = "paging")] +impl From for MappingFlags { + fn from(page_fault_cause: PageFaultCause) -> Self { + match page_fault_cause { + PageFaultCause::WRITE => MappingFlags::WRITE, + PageFaultCause::READ => MappingFlags::READ, + PageFaultCause::INSTRUCTION => MappingFlags::EXECUTE, + } + } +} + /// Trap handler interface. /// /// This trait is defined with the [`#[def_interface]`][1] attribute. Users @@ -44,6 +58,11 @@ pub trait TrapHandler { fn handle_page_fault(_vaddr: usize, _caus: PageFaultCause) -> bool { panic!("No handle_page_fault implement"); } + /// Handles signal for every trap. + #[cfg(feature = "signal")] + fn handle_signal() { + panic!("No handle_page_fault implement"); + } } /// Call the external IRQ handler. @@ -65,3 +84,9 @@ pub(crate) fn handle_syscall(syscall_id: usize, args: [usize; 6]) -> isize { pub(crate) fn handle_page_fault(vaddr: usize, cause: PageFaultCause) -> bool { call_interface!(TrapHandler::handle_page_fault, vaddr, cause) } + +#[allow(dead_code)] +#[cfg(feature = "signal")] +pub(crate) fn handle_signal() { + call_interface!(TrapHandler::handle_signal) +} diff --git a/ulib/ruxlibc/c/sendfile.c b/modules/ruxhal/src/virtio/mod.rs similarity index 77% rename from ulib/ruxlibc/c/sendfile.c rename to modules/ruxhal/src/virtio/mod.rs index 1a7f1f2ed..49e0b464d 100644 --- a/ulib/ruxlibc/c/sendfile.c +++ b/modules/ruxhal/src/virtio/mod.rs @@ -7,10 +7,8 @@ * See the Mulan PSL v2 for more details. */ -#include -#include - -ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count){ - unimplemented(); - return 0; -} +//! ruxos and virtio code related mod definition +#[cfg(feature = "virtio_console")] +pub mod virtio_console; +#[cfg(feature = "virtio")] +pub mod virtio_hal; diff --git a/modules/ruxhal/src/virtio/virtio_console.rs b/modules/ruxhal/src/virtio/virtio_console.rs new file mode 100644 index 000000000..39f8adbce --- /dev/null +++ b/modules/ruxhal/src/virtio/virtio_console.rs @@ -0,0 +1,211 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! virtio_console +use crate::mem::phys_to_virt; +use crate::virtio::virtio_hal::VirtIoHalImpl; +use driver_console::ConsoleDriverOps; +use driver_virtio::VirtIoConsoleDev; +use spinlock::SpinNoIrq; +const VIRTIO_CONSOLE_BASE: usize = ruxconfig::VIRTIO_CONSOLE_PADDR; +const VIRTIO_CONSOLE_REG: usize = 0x200; + +#[cfg(all(feature = "irq", target_arch = "aarch64"))] +use crate::platform::irq::VIRTIO_CONSOLE_IRQ_NUM; + +/// Store buffer size +const MEM_SIZE: usize = 4096; + +#[cfg(feature = "irq")] +const BUFFER_SIZE: usize = 128; + +#[cfg(feature = "irq")] +struct RxRingBuffer { + buffer: [u8; BUFFER_SIZE], + head: usize, + tail: usize, + empty: bool, +} + +/// The UART RxRingBuffer +#[cfg(feature = "irq")] +impl RxRingBuffer { + /// Create a new ring buffer + const fn new() -> Self { + RxRingBuffer { + buffer: [0_u8; BUFFER_SIZE], + head: 0_usize, + tail: 0_usize, + empty: true, + } + } + + /// Push a byte into the buffer + fn push(&mut self, n: u8) { + if self.tail != self.head || self.empty { + self.buffer[self.tail] = n; + self.tail = (self.tail + 1) % BUFFER_SIZE; + self.empty = false; + } + } + + /// Pop a byte from the buffer + fn pop(&mut self) -> Option { + if self.empty { + None + } else { + let ret = self.buffer[self.head]; + self.head = (self.head + 1) % BUFFER_SIZE; + if self.head == self.tail { + self.empty = true; + } + Some(ret) + } + } +} + +/// The UART driver +struct UartDrv { + inner: Option>>, + buffer: [u8; MEM_SIZE], + #[cfg(feature = "irq")] + irq_buffer: RxRingBuffer, + pointer: usize, + addr: usize, +} + +/// The UART driver instance +static UART: SpinNoIrq = SpinNoIrq::new(UartDrv { + inner: None, + buffer: [0; MEM_SIZE], + #[cfg(feature = "irq")] + irq_buffer: RxRingBuffer::new(), + pointer: 0, + addr: 0, +}); + +/// Writes a byte to the console. +pub fn putchar(c: u8) { + let mut uart_drv = UART.lock(); + if uart_drv.inner.is_some() { + if uart_drv.pointer > 0 { + for i in 0..uart_drv.pointer { + let c = uart_drv.buffer[i]; + let uart = uart_drv.inner.as_mut().unwrap(); + match c { + b'\n' => { + uart.putchar(b'\r'); + uart.putchar(b'\n'); + } + c => uart.putchar(c), + } + } + uart_drv.pointer = 0; + warn!("######################### The above content is printed from buffer! #########################"); + } + let uart = uart_drv.inner.as_mut().unwrap(); + uart.putchar(c); + } else { + let ptr = uart_drv.pointer; + uart_drv.buffer[ptr] = c; + uart_drv.pointer += 1; + } +} + +/// Reads a byte from the console. +pub fn getchar() -> Option { + let mut uart_drv = UART.lock(); + #[cfg(feature = "irq")] + return uart_drv.irq_buffer.pop(); + #[cfg(not(feature = "irq"))] + if let Some(ref mut uart_inner) = uart_drv.inner { + return uart_inner.getchar(); + } else { + None + } +} + +/// probe virtio console directly +pub fn directional_probing() { + info!("Initiating VirtIO Console ..."); + let mut uart_drv = UART.lock(); + if let Some(dev) = probe_mmio(VIRTIO_CONSOLE_BASE, VIRTIO_CONSOLE_REG) { + uart_drv.inner = Some(dev); + uart_drv.addr = VIRTIO_CONSOLE_BASE; + } + info!("Output now redirected to VirtIO Console!"); +} + +/// enable virtio console interrupt +#[cfg(feature = "irq")] +pub fn enable_interrupt() { + #[cfg(target_arch = "aarch64")] + { + info!("Initiating VirtIO Console interrupt ..."); + info!("IRQ ID: {VIRTIO_CONSOLE_IRQ_NUM}"); + crate::irq::register_handler(VIRTIO_CONSOLE_IRQ_NUM, irq_handler); + crate::irq::set_enable(VIRTIO_CONSOLE_IRQ_NUM, true); + ack_interrupt(); + info!("Interrupt enabled!"); + } + #[cfg(not(target_arch = "aarch64"))] + warn!("Interrupt is not supported on this platform!"); +} + +/// virtio console interrupt handler +#[cfg(feature = "irq")] +pub fn irq_handler() { + let mut uart_drv = UART.lock(); + if let Some(ref mut uart_inner) = uart_drv.inner { + let uart = uart_inner; + if uart.ack_interrupt().unwrap() { + if let Some(c) = uart.getchar() { + uart_drv.irq_buffer.push(c); + } + } + } +} + +/// Acknowledge the interrupt +#[cfg(feature = "irq")] +pub fn ack_interrupt() { + info!("ack interrupt"); + let mut uart_drv = UART.lock(); + if let Some(ref mut uart_inner) = uart_drv.inner { + let uart = uart_inner; + uart.ack_interrupt() + .expect("Virtio_console ack interrupt error"); + } +} + +/// Check if the address is the probe address +pub fn is_probe(addr: usize) -> bool { + let uart_drv = UART.lock(); + addr == uart_drv.addr +} + +/// Probe the virtio console +fn probe_mmio( + mmio_base: usize, + mmio_size: usize, +) -> Option>> { + let base_vaddr = phys_to_virt(mmio_base.into()); + if let Some((ty, transport)) = + driver_virtio::probe_mmio_device(base_vaddr.as_mut_ptr(), mmio_size) + { + if ty == driver_common::DeviceType::Char { + info!("VirtIO Console found at {mmio_base:#x} size {mmio_size:#x}"); + return VirtIoConsoleDev::try_new(transport).ok(); + } + } + None +} + +/// Virtio transport type +type VirtIoTransport<'a> = driver_virtio::MmioTransport<'a>; diff --git a/modules/ruxhal/src/virtio/virtio_hal.rs b/modules/ruxhal/src/virtio/virtio_hal.rs new file mode 100644 index 000000000..28f78a1e8 --- /dev/null +++ b/modules/ruxhal/src/virtio/virtio_hal.rs @@ -0,0 +1,56 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! virtio-hal implementation for RuxOS. +use crate::mem::{address_translate, direct_virt_to_phys, phys_to_virt}; +use axalloc::global_allocator; +use core::ptr::NonNull; +use driver_virtio::VirtIoHal; +use virtio_drivers::{BufferDirection, PhysAddr}; + +/// virtio-hal struct define +pub struct VirtIoHalImpl; + +/// virtio-hal trait implement +unsafe impl VirtIoHal for VirtIoHalImpl { + /// Allocate DMA buffer + fn dma_alloc(pages: usize, _direction: BufferDirection) -> (PhysAddr, NonNull) { + let vaddr = if let Ok(vaddr) = global_allocator().alloc_pages(pages, 0x1000) { + vaddr + } else { + return (0, NonNull::dangling()); + }; + let paddr = direct_virt_to_phys(vaddr.into()); + let ptr = NonNull::new(vaddr as _).unwrap(); + (paddr.as_usize(), ptr) + } + + /// Deallocate DMA buffer + unsafe fn dma_dealloc(_paddr: PhysAddr, vaddr: NonNull, pages: usize) -> i32 { + global_allocator().dealloc_pages(vaddr.as_ptr() as usize, pages); + 0 + } + + /// Convert physical address to virtual address + #[inline] + unsafe fn mmio_phys_to_virt(paddr: PhysAddr, _size: usize) -> NonNull { + NonNull::new(phys_to_virt(paddr.into()).as_mut_ptr()).unwrap() + } + + /// Share DMA buffer + #[inline] + unsafe fn share(buffer: NonNull<[u8]>, _direction: BufferDirection) -> PhysAddr { + let vaddr = buffer.as_ptr() as *mut u8 as usize; + address_translate(vaddr.into()).expect("virt_to_phys failed") + } + + /// Unshare DMA buffer + #[inline] + unsafe fn unshare(_paddr: PhysAddr, _buffer: NonNull<[u8]>, _direction: BufferDirection) {} +} diff --git a/modules/ruxmm/Cargo.toml b/modules/ruxmm/Cargo.toml new file mode 100644 index 000000000..79a6cea71 --- /dev/null +++ b/modules/ruxmm/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "ruxmm" +version = "0.1.0" +edition = "2021" +authors = [ + "Yuekai Jia ", + "yanjuguang ", +] +description = "Ruxos hardware abstraction layer, provides unified APIs for platform-specific operations" +license = "GPL-3.0-or-later OR Apache-2.0" +homepage = "https://github.com/syswonder/ruxos" +repository = "https://github.com/syswonder/ruxos/tree/main/modules/ruxmm" + +[features] +default = [] +paging = [] + +[dependencies] +log = "0.4" +cfg-if = "1.0" +bitflags = { workspace = true } +static_assertions = "1.1.0" +kernel_guard = "0.1.0" +spinlock = { path = "../../crates/spinlock" } +page_table = { path = "../../crates/page_table" } +page_table_entry = { path = "../../crates/page_table_entry" } +memory_addr = "0.1.0" +crate_interface = "0.1.1" + +ruxtask = { path = "../ruxtask" } +ruxdriver = { path = "../ruxdriver" } +axalloc = { path = "../axalloc" } +ruxhal = { path = "../ruxhal" } diff --git a/modules/ruxmm/src/lib.rs b/modules/ruxmm/src/lib.rs new file mode 100644 index 000000000..2a45f38a3 --- /dev/null +++ b/modules/ruxmm/src/lib.rs @@ -0,0 +1,18 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! memory management module for RuxOS. + +#![no_std] +#![feature(doc_auto_cfg)] + +pub mod mem; +#[cfg(feature = "paging")] +/// A module for paging operations. +pub mod paging; diff --git a/modules/ruxmm/src/mem.rs b/modules/ruxmm/src/mem.rs new file mode 100644 index 000000000..0399fc174 --- /dev/null +++ b/modules/ruxmm/src/mem.rs @@ -0,0 +1,42 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! This module provides functions to translate virtual addresses to physical addresses. + +#[cfg(feature = "paging")] +use crate::paging::pte_query; +use ruxhal::mem::{AddressTranslate, VirtAddr}; + +struct AddressTranslateImpl; + +/// Converts a virtual address to a physical address. +/// +/// When paging is enabled, query physical address from the page table +#[cfg(feature = "paging")] +#[crate_interface::impl_interface] +impl AddressTranslate for AddressTranslateImpl { + fn virt_to_phys(vaddr: VirtAddr) -> Option { + match pte_query(vaddr) { + Ok((paddr, _, _)) => Some(paddr.into()), + Err(_) => None, // for address unmapped + } + } +} + +/// Converts a virtual address to a physical address. +/// +/// When paging is enabled, query physical address from the page table +#[cfg(not(feature = "paging"))] +#[crate_interface::impl_interface] +impl AddressTranslate for AddressTranslateImpl { + fn virt_to_phys(vaddr: VirtAddr) -> Option { + use ruxhal::mem::direct_virt_to_phys; + Some(direct_virt_to_phys(vaddr).into()) + } +} diff --git a/modules/ruxmm/src/paging.rs b/modules/ruxmm/src/paging.rs new file mode 100644 index 000000000..028206aa1 --- /dev/null +++ b/modules/ruxmm/src/paging.rs @@ -0,0 +1,133 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! Page table manipulation. +extern crate alloc; + +use ruxhal::arch::flush_tlb; +use ruxhal::mem::{ + direct_virt_to_phys, memory_regions, phys_to_virt, PhysAddr, VirtAddr, PAGE_SIZE_4K, +}; + +use axalloc::global_allocator; +#[doc(no_inline)] +use page_table::{MappingFlags, PageSize, PagingError, PagingResult}; + +use log::{trace, warn}; + +/// Remap the regions for kernel memory +pub fn remap_kernel_memory() -> PagingResult { + let current_task = ruxtask::current(); + let mut kernel_page_table = current_task.pagetable.lock(); + if ruxhal::cpu::this_cpu_is_bsp() { + for r in memory_regions() { + kernel_page_table.map_region( + phys_to_virt(r.paddr), + r.paddr, + r.size, + r.flags.into(), + true, + )?; + } + } + + unsafe { ruxhal::arch::write_page_table_root(kernel_page_table.root_paddr()) }; + Ok(()) +} + +/// Temporarily, `malloc` alloc memory in heap simply, and it can not be swapped +/// into swap file. Once the memory is not enough with all memory alloced, it +/// will be too late, as there will be no memory for `malloc` any more. In practice, +/// this is highly likely to cause errors of insufficient memory. To prevent this, +/// mmapping will not alloc from physical address to avoid this. +/// +/// After the page of `malloc` can be swapped, or it raises a propriately handler +/// to swap page when memory is not enough, it will be okay to delete this. +const PAGE_NUM_MIN: usize = 1024; + +/// Obtain fake VirtAddr addresses without performing virtual memory mapping +/// to prevent physical competition between multiple threads. +/// After call the function. the page is alloced in allocator but its virtual +/// address is still on linear mapping region. +/// use `do_pte_map` to do actually page mapping after call this function. +pub fn alloc_page_preload() -> Result { + if global_allocator().available_pages() < PAGE_NUM_MIN { + warn!( + "available page num is {:?}", + global_allocator().available_pages() + ); + return Err(PagingError::NoMemory); + }; + match global_allocator().alloc_pages(1, PAGE_SIZE_4K) { + Ok(fake_vaddr) => Ok(VirtAddr::from(fake_vaddr)), + Err(_) => Err(PagingError::NoMemory), + } +} + +/// Unmap memory for an mmap-induced PageFault and updating PTE entries. +/// After call the function. the page is alloced in allocator but its virtual +/// address is still on linear mapping region. +/// use `do_pte_map` to do actually page mapping after call this function. +pub fn pte_swap_preload(swaped_vaddr: VirtAddr) -> PagingResult { + trace!("swapping swaped_vaddr: 0x{swaped_vaddr:x?}",); + let binding = ruxtask::current(); + let mut kernel_page_table = binding.pagetable.lock(); + let (paddr, _) = kernel_page_table.unmap(swaped_vaddr)?; + flush_tlb(Some(swaped_vaddr)); + Ok(phys_to_virt(paddr)) +} + +/// Map memory for an mmap-induced PageFault and updating PTE entries, +/// This function must be called after `alloc_page_preload` and +/// `pte_swap_preload` when the mapping operator is ready. +pub fn do_pte_map(vaddr: VirtAddr, fake_vaddr: VirtAddr, flags: MappingFlags) -> PagingResult { + let ret = ruxtask::current().pagetable.lock().map( + vaddr, + direct_virt_to_phys(fake_vaddr), + PageSize::Size4K, + flags, + ); + ret +} + +/// Query PTE entries of the virtual address. +/// +/// get the physical address information corresponding to the virtual address from the page table +pub fn pte_query(vaddr: VirtAddr) -> PagingResult<(PhysAddr, MappingFlags, PageSize)> { + let binding = ruxtask::current(); + let kernel_page_table = binding.pagetable.lock(); + kernel_page_table.query(vaddr) +} + +/// Update flags or physical address for an PTE entries. +/// +/// change the physical address or access permissions mapped by the virtual address +pub fn pte_update_page( + vaddr: VirtAddr, + paddr: Option, + flags: Option, +) -> PagingResult { + trace!("updating vaddr:0x{vaddr:x?} paddr:0x{paddr:x?} flags:0x{flags:x?}"); + ruxtask::current() + .pagetable + .lock() + .update(vaddr, paddr, flags)?; + flush_tlb(Some(vaddr)); + Ok(()) +} + +/// Unmapping and decalloc memory for an page in page table. +/// +/// release the corresponding memory at the same time +pub fn pte_unmap_page(vaddr: VirtAddr) -> PagingResult { + trace!("unmapping vaddr: 0x{vaddr:x?}"); + ruxtask::current().pagetable.lock().unmap(vaddr)?; + flush_tlb(Some(vaddr)); + Ok(()) +} diff --git a/modules/ruxnet/Cargo.toml b/modules/ruxnet/Cargo.toml index 3c81a2510..c13f57f0c 100644 --- a/modules/ruxnet/Cargo.toml +++ b/modules/ruxnet/Cargo.toml @@ -2,7 +2,11 @@ name = "ruxnet" version = "0.1.0" edition = "2021" -authors = ["Yuekai Jia ", "ChengXiang Qi ", "HaoWen Liu "] +authors = [ + "Yuekai Jia ", + "ChengXiang Qi ", + "HaoWen Liu ", +] description = "ArceOS network module" license = "GPL-3.0-or-later OR Apache-2.0" homepage = "https://github.com/rcore-os/arceos" @@ -11,35 +15,49 @@ documentation = "https://rcore-os.github.io/arceos/ruxnet/index.html" [features] lwip = ["dep:lwip_rust"] +loopback = ["ruxdriver/loopback"] smoltcp = [] -default = ["smoltcp"] +default = ["smoltcp", "loopback"] +irq = [] [dependencies] -log = "0.4" +hashbrown = "0.14.5" +log = { workspace = true } cfg-if = "1.0" -spin = "0.9" +spin = { workspace = true } driver_net = { path = "../../crates/driver_net" } +flatten_objects = { path = "../../crates/flatten_objects" } lazy_init = { path = "../../crates/lazy_init" } +lazy_static = { version = "1.4", features = ["spin_no_std"] } lwip_rust = { path = "../../crates/lwip_rust", optional = true } printf-compat = { version = "0.1", default-features = false, optional = true } axerrno = { path = "../../crates/axerrno" } +axfs_vfs = { path = "../../crates/axfs_vfs" } +iovec = { path = "../../crates/iovec" } ruxhal = { path = "../ruxhal" } axsync = { path = "../axsync" } -axlog = { path = "../axlog" } -ruxtask = { path = "../ruxtask" } +ruxtask = { path = "../ruxtask", features = ["multitask"] } +ruxfs = { path = "../ruxfs" } ruxdriver = { path = "../ruxdriver", features = ["net"] } cty = { version = "0.2.2", optional = true } axio = { path = "../../crates/axio" } +bitflags = { workspace = true } +ruxfdtable = { path = "../ruxfdtable" } [dependencies.smoltcp] git = "https://github.com/rcore-os/smoltcp.git" rev = "2ade274" default-features = false features = [ - "alloc", "log", # no std + "alloc", + "log", # no std "medium-ethernet", "proto-ipv4", - "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dns", + "socket-raw", + "socket-icmp", + "socket-udp", + "socket-tcp", + "socket-dns", # "fragmentation-buffer-size-65536", "proto-ipv4-fragmentation", # "reassembly-buffer-size-65536", "reassembly-buffer-count-32", # "assembler-max-segment-count-32", diff --git a/modules/ruxnet/src/address.rs b/modules/ruxnet/src/address.rs new file mode 100644 index 000000000..d46bb0fa2 --- /dev/null +++ b/modules/ruxnet/src/address.rs @@ -0,0 +1,92 @@ +/* Copyright (c) [2023] [Syswonder Community] +* [Ruxos] is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ +//! Unified socket address +use core::net::{SocketAddrV4, SocketAddrV6}; + +use alloc::sync::Arc; +use axerrno::LinuxResult; +use axfs_vfs::AbsPath; +use ruxfs::fops; + +use crate::{socket::Socket, socket_node::SocketNode}; + +#[derive(Debug, PartialEq, Eq, Clone)] +/// `Address` means the union of two `Addr`: UnixSocketAddr and Ipv4Addr +pub enum SocketAddress { + /// Unix socket address + Unix(UnixSocketAddr), + /// ipv4 address + Inet(SocketAddrV4), + /// ipv6 address + Inet6(SocketAddrV6), +} + +impl From for SocketAddress { + fn from(addr: core::net::SocketAddr) -> Self { + match addr { + core::net::SocketAddr::V4(socket_addr_v4) => SocketAddress::Inet(socket_addr_v4), + core::net::SocketAddr::V6(socket_addr_v6) => SocketAddress::Inet6(socket_addr_v6), + } + } +} + +impl From for core::net::SocketAddr { + fn from(addr: SocketAddress) -> Self { + match addr { + SocketAddress::Unix(_) => { + panic!("Cannot convert Unix socket address to core::net::SocketAddr") + } + SocketAddress::Inet(socket_addr_v4) => core::net::SocketAddr::V4(socket_addr_v4), + SocketAddress::Inet6(socket_addr_v6) => core::net::SocketAddr::V6(socket_addr_v6), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +/// `UnixSocketAddr` represents the address of a UNIX domain socket. +/// see +pub enum UnixSocketAddr { + /// A UNIX domain socket can be bound to a null-terminated filesystem pathname using `sys_bind`. + PathName(AbsPath<'static>), + /// A stream socket that has not been bound to a pathname using `sys_bind` has no name. + /// Likewise, the two sockets created by `sys_socketpair` are unnamed. When the address + /// of an unnamed socket is returned, its length is sizeof(`sa_family_t``), and `sun_path` + /// should not be inspected. + Unamed, + /// An abstract socket address is distinguished (from a pathname socket) by the fact that + /// `sun_path[0]` is a null byte ('\0'). The socket's address in this namespace is given by + /// the additional bytes in sun_path that are covered by the specified length of the address + /// structure. (Null bytes in the name have no special significance.) The name has no + /// connection with filesystem pathnames. When the address of an abstract socket is returned, + /// the returned addrlen is greater than sizeof(sa_family_t) (i.e., greater than 2), and the + /// name of the socket is contained in the first (addrlen - sizeof(sa_family_t)) bytes of sun_path. + Abstract(Arc<[u8]>), +} + +impl Default for UnixSocketAddr { + fn default() -> Self { + UnixSocketAddr::PathName(AbsPath::new("")) + } +} + +/// resolve unix socket addr by address +pub fn resolve_unix_socket_addr(address: &SocketAddress) -> LinuxResult> { + match address { + SocketAddress::Unix(unix_socket_addr) => match unix_socket_addr { + UnixSocketAddr::PathName(abs_path) => { + let node = fops::lookup(abs_path)?; + let socket_node = Arc::downcast::(node.as_any_arc()).unwrap(); + Ok(socket_node.bound_socket()) + } + UnixSocketAddr::Unamed => todo!(), + UnixSocketAddr::Abstract(_) => todo!(), + }, + _ => Err(axerrno::LinuxError::EINVAL), + } +} diff --git a/modules/ruxnet/src/lib.rs b/modules/ruxnet/src/lib.rs index a8414b105..775c865d8 100644 --- a/modules/ruxnet/src/lib.rs +++ b/modules/ruxnet/src/lib.rs @@ -28,15 +28,19 @@ #![no_std] #![feature(c_variadic)] -#![feature(ip_in_core)] -#![feature(ip_bits)] -#![feature(new_uninit)] +#![allow(incomplete_features)] #![feature(inherent_associated_types)] #[macro_use] extern crate log; extern crate alloc; +pub mod address; +pub mod message; +pub mod socket; +pub mod socket_node; +pub mod unix; + cfg_if::cfg_if! { if #[cfg(feature = "lwip")] { mod lwip_impl; @@ -57,14 +61,40 @@ pub use self::net_impl::TcpSocket; pub use self::net_impl::UdpSocket; pub use self::net_impl::{dns_query, poll_interfaces}; +use axerrno::LinuxError; use ruxdriver::{prelude::*, AxDeviceContainer}; +bitflags::bitflags! { + /// The flags for shutting down sockets. + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct ShutdownFlags: i32 { + /// Further transmissions will be disallowed. + const WRITE = 1 << 0; + /// Further receptions will be disallowed. + const READ = 1 << 1; + } +} + +impl TryFrom for ShutdownFlags { + type Error = LinuxError; + + fn try_from(how: i32) -> Result { + const SHUT_RD: i32 = 0; + const SHUT_WR: i32 = 1; + const SHUT_RDWR: i32 = 2; + match how { + SHUT_RD => Ok(ShutdownFlags::READ), + SHUT_WR => Ok(ShutdownFlags::WRITE), + SHUT_RDWR => Ok(ShutdownFlags::READ | ShutdownFlags::WRITE), + _ => Err(LinuxError::EINVAL), + } + } +} + /// Initializes the network subsystem by NIC devices. pub fn init_network(mut net_devs: AxDeviceContainer) { info!("Initialize network subsystem..."); - let dev = net_devs.take_one().expect("No NIC device found!"); - info!(" use NIC 0: {:?}", dev.device_name()); cfg_if::cfg_if! { if #[cfg(feature = "lwip")] { info!(" net stack: lwip"); @@ -74,5 +104,10 @@ pub fn init_network(mut net_devs: AxDeviceContainer) { compile_error!("No network stack is selected"); } } - net_impl::init(dev); + net_impl::init(); + while !net_devs.is_empty() { + let dev = net_devs.take_one().expect("No NIC device found!"); + info!(" use NIC: {:?}", dev.device_name()); + net_impl::init_netdev(dev); + } } diff --git a/modules/ruxnet/src/lwip_impl/addr.rs b/modules/ruxnet/src/lwip_impl/addr.rs index 0cf41a737..c6a222176 100644 --- a/modules/ruxnet/src/lwip_impl/addr.rs +++ b/modules/ruxnet/src/lwip_impl/addr.rs @@ -1,16 +1,20 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + use core::{ fmt::{self, Error}, str::FromStr, }; -use lwip_rust::bindings::{ - ip4_addr_t, ip_addr_t, lwip_ip_addr_type_IPADDR_TYPE_V4, lwip_ip_addr_type_IPADDR_TYPE_V6, -}; +use lwip_rust::bindings::ip_addr_t; -use core::net::{ - Ipv4Addr as CoreIpv4Addr, Ipv6Addr as CoreIpv6Addr, SocketAddr as CoreSocketAddr, SocketAddrV4, - SocketAddrV6, -}; +use core::net::{Ipv4Addr as CoreIpv4Addr, SocketAddr as CoreSocketAddr, SocketAddrV4}; /// Mac Address #[derive(Clone, Copy, Debug, Default)] @@ -73,7 +77,7 @@ impl IpAddr { /// Get the IP Address as a byte array pub fn as_bytes(&self) -> &[u8] { match self { - IpAddr::Ipv4(Ipv4Addr(addr)) => unsafe { &addr[..] }, + IpAddr::Ipv4(Ipv4Addr(addr)) => &addr[..], _ => panic!("IPv6 not supported"), } } @@ -120,7 +124,7 @@ impl From for ip_addr_t { impl From for IpAddr { #[allow(non_upper_case_globals)] fn from(addr: ip_addr_t) -> IpAddr { - IpAddr::Ipv4(Ipv4Addr(unsafe { addr.addr.to_be_bytes() })) + IpAddr::Ipv4(Ipv4Addr(addr.addr.to_be_bytes())) } } diff --git a/modules/ruxnet/src/lwip_impl/dns.rs b/modules/ruxnet/src/lwip_impl/dns.rs index 829775745..4086d4be4 100644 --- a/modules/ruxnet/src/lwip_impl/dns.rs +++ b/modules/ruxnet/src/lwip_impl/dns.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + use super::driver::lwip_loop_once; use crate::{IpAddr, Ipv4Addr}; use alloc::{ffi::CString, vec, vec::Vec}; @@ -24,11 +33,7 @@ extern "C" fn dns_found_callback( ipaddr: *const ip_addr_t, callback_arg: *mut c_void, ) { - trace!( - "[dns_found_callback]: name_ptr={:?} ipaddr_ptr={:?}", - name, - ipaddr - ); + trace!("[dns_found_callback]: name_ptr={name:?} ipaddr_ptr={ipaddr:?}"); let res = callback_arg as *mut DnsQueryEntry; unsafe { (*res).finished = true; @@ -98,7 +103,7 @@ pub fn resolve_socket_addr(name: &str) -> AxResult> { } /// Public function for DNS query. -pub fn dns_query(name: &str) -> AxResult> { +pub fn dns_query(_name: &str) -> AxResult> { let empty_vec = alloc::vec::Vec::new(); Ok(empty_vec) } diff --git a/modules/ruxnet/src/lwip_impl/driver.rs b/modules/ruxnet/src/lwip_impl/driver.rs index 164ea7289..0d5451337 100644 --- a/modules/ruxnet/src/lwip_impl/driver.rs +++ b/modules/ruxnet/src/lwip_impl/driver.rs @@ -1,19 +1,29 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + use super::LWIP_MUTEX; use crate::{ net_impl::addr::{mask_to_prefix, MacAddr}, IpAddr, }; use alloc::{boxed::Box, collections::VecDeque, sync::Arc}; -#[cfg(feature = "irq")] -use axdriver::register_interrupt_handler; use axsync::Mutex; -use core::{cell::RefCell, ffi::c_void}; -use driver_net::{DevError, NetBuf, NetBufBox, NetBufPool, NetBufPtr}; +use core::{ + cell::RefCell, + ffi::{c_char, c_void}, +}; +use driver_net::{DevError, NetBuf, NetBufBox}; use lazy_init::LazyInit; use lwip_rust::bindings::{ err_enum_t_ERR_MEM, err_enum_t_ERR_OK, err_t, etharp_output, ethernet_input, ip4_addr_t, - lwip_htonl, lwip_init, netif, netif_add, netif_set_default, netif_set_link_up, netif_set_up, - pbuf, pbuf_free, rx_custom_pbuf_alloc, rx_custom_pbuf_free, rx_custom_pbuf_init, + lwip_htonl, lwip_init, netif, netif_add, netif_poll, netif_set_default, netif_set_link_up, + netif_set_up, pbuf, pbuf_free, rx_custom_pbuf_alloc, rx_custom_pbuf_free, rx_custom_pbuf_init, rx_custom_pbuf_t, sys_check_timeouts, NETIF_FLAG_BROADCAST, NETIF_FLAG_ETHARP, NETIF_FLAG_ETHERNET, }; @@ -21,9 +31,6 @@ use ruxdriver::prelude::*; const RX_BUF_QUEUE_SIZE: usize = 64; -const NET_BUF_LEN: usize = 1526; -const NET_BUF_POOL_SIZE: usize = 128; - struct NetifWrapper(netif); unsafe impl Send for NetifWrapper {} @@ -48,7 +55,7 @@ impl DeviceWrapper { }, Err(DevError::Again) => break, // TODO: better method to avoid error type conversion Err(err) => { - warn!("receive failed: {:?}", err); + warn!("receive failed: {err:?}"); break; } } @@ -58,11 +65,6 @@ impl DeviceWrapper { fn receive(&mut self) -> Option { self.rx_buf_queue.pop_front() } - - #[cfg(feature = "irq")] - fn ack_interrupt(&mut self) -> bool { - unsafe { self.inner.as_ptr().as_mut().unwrap().ack_interrupt() } - } } struct InterfaceWrapper { @@ -102,7 +104,7 @@ impl InterfaceWrapper { unsafe { let res = netif.0.input.unwrap()(p, &mut netif.0); if (res as i32) != err_enum_t_ERR_OK { - warn!("ethernet_input failed: {:?}", res); + warn!("ethernet_input failed: {res:?}"); pbuf_free(p); } } @@ -111,15 +113,10 @@ impl InterfaceWrapper { } } } - - #[cfg(feature = "irq")] - pub fn ack_interrupt(&self) { - unsafe { &mut *self.dev.as_mut_ptr() }.ack_interrupt(); - } } extern "C" fn pbuf_free_custom(p: *mut pbuf) { - trace!("pbuf_free_custom: {:x?}", p); + trace!("pbuf_free_custom: {p:x?}"); let p = p as *mut rx_custom_pbuf_t; let buf = unsafe { Box::from_raw((*p).buf as *mut NetBuf) }; let dev = unsafe { Arc::from_raw((*p).dev as *const Mutex) }; @@ -131,7 +128,7 @@ extern "C" fn pbuf_free_custom(p: *mut pbuf) { { Ok(_) => (), Err(err) => { - warn!("recycle_rx_buffer failed: {:?}", err); + warn!("recycle_rx_buffer failed: {err:?}"); } }; unsafe { @@ -142,8 +139,8 @@ extern "C" fn pbuf_free_custom(p: *mut pbuf) { extern "C" fn ethif_init(netif: *mut netif) -> err_t { trace!("ethif_init"); unsafe { - (*netif).name[0] = 'e' as i8; - (*netif).name[1] = 'n' as i8; + (*netif).name[0] = b'e' as c_char; + (*netif).name[1] = b'n' as c_char; (*netif).num = 0; (*netif).output = Some(etharp_output); @@ -164,7 +161,7 @@ extern "C" fn ethif_output(netif: *mut netif, p: *mut pbuf) -> err_t { if dev.can_transmit() { unsafe { - let tot_len = unsafe { (*p).tot_len }; + let tot_len = (*p).tot_len; let mut tx_buf = *NetBuf::from_buf_ptr(dev.alloc_tx_buffer(tot_len.into()).unwrap()); dev.prepare_tx_buffer(&mut tx_buf, tot_len.into()).unwrap(); @@ -172,12 +169,12 @@ extern "C" fn ethif_output(netif: *mut netif, p: *mut pbuf) -> err_t { let mut offset = 0; let mut q = p; while !q.is_null() { - let len = unsafe { (*q).len } as usize; - let payload = unsafe { (*q).payload }; - let payload = unsafe { core::slice::from_raw_parts(payload as *const u8, len) }; + let len = (*q).len as usize; + let payload = (*q).payload; + let payload = core::slice::from_raw_parts(payload as *const u8, len); tx_buf.packet_mut()[offset..offset + len].copy_from_slice(payload); offset += len; - q = unsafe { (*q).next }; + q = (*q).next; } trace!( @@ -203,6 +200,9 @@ static ETH0: LazyInit = LazyInit::new(); /// packets to the NIC. pub fn poll_interfaces() { ETH0.poll(); + unsafe { + netif_poll(&mut ETH0.netif.lock().0); + } } fn ip4_addr_gen(a: u8, b: u8, c: u8, d: u8) -> ip4_addr_t { @@ -212,58 +212,67 @@ fn ip4_addr_gen(a: u8, b: u8, c: u8, d: u8) -> ip4_addr_t { }, } } +pub fn init() {} -pub fn init(mut net_dev: AxNetDevice) { - LWIP_MUTEX.init_by(Mutex::new(0)); - let _guard = LWIP_MUTEX.lock(); - - let ipaddr: ip4_addr_t = ip4_addr_gen(10, 0, 2, 15); // QEMU user networking default IP - let netmask: ip4_addr_t = ip4_addr_gen(255, 255, 255, 0); - let gw: ip4_addr_t = ip4_addr_gen(10, 0, 2, 2); // QEMU user networking gateway - - let dev = net_dev; - let mut netif: netif = unsafe { core::mem::zeroed() }; - netif.hwaddr_len = 6; - netif.hwaddr = dev.mac_address().0; - - ETH0.init_by(InterfaceWrapper { - name: "eth0", - dev: Arc::new(Mutex::new(DeviceWrapper::new(dev))), - netif: Mutex::new(NetifWrapper(netif)), - }); +pub fn init_netdev(net_dev: AxNetDevice) { + match net_dev.device_name() { + "loopback" => { + info!("use lwip netif loopback"); + } + _ => { + LWIP_MUTEX.init_by(Mutex::new(0)); + let _guard = LWIP_MUTEX.lock(); + + let ipaddr: ip4_addr_t = ip4_addr_gen(10, 0, 2, 15); // QEMU user networking default IP + let netmask: ip4_addr_t = ip4_addr_gen(255, 255, 255, 0); + let gw: ip4_addr_t = ip4_addr_gen(10, 0, 2, 2); // QEMU user networking gateway + + let dev = net_dev; + let mut netif: netif = unsafe { core::mem::zeroed() }; + netif.hwaddr_len = 6; + netif.hwaddr = dev.mac_address().0; + + ETH0.init_by(InterfaceWrapper { + name: "eth0", + dev: Arc::new(Mutex::new(DeviceWrapper::new(dev))), + netif: Mutex::new(NetifWrapper(netif)), + }); + + unsafe { + lwip_init(); + rx_custom_pbuf_init(); + netif_add( + &mut ETH0.netif.lock().0, + &ipaddr, + &netmask, + &gw, + Ð0 as *const _ as *mut c_void, + Some(ethif_init), + Some(ethernet_input), + ); + netif_set_link_up(&mut ETH0.netif.lock().0); + netif_set_up(&mut ETH0.netif.lock().0); + netif_set_default(&mut ETH0.netif.lock().0); + } - unsafe { - lwip_init(); - rx_custom_pbuf_init(); - netif_add( - &mut ETH0.netif.lock().0, - &ipaddr, - &netmask, - &gw, - Ð0 as *const _ as *mut c_void, - Some(ethif_init), - Some(ethernet_input), - ); - netif_set_link_up(&mut ETH0.netif.lock().0); - netif_set_up(&mut ETH0.netif.lock().0); - netif_set_default(&mut ETH0.netif.lock().0); + info!("created net interface {:?}:", ETH0.name()); + info!( + " ether: {}", + MacAddr::from_bytes(Ð0.netif.lock().0.hwaddr) + ); + let ip = IpAddr::from(ETH0.netif.lock().0.ip_addr); + let mask = mask_to_prefix(IpAddr::from(ETH0.netif.lock().0.netmask)).unwrap(); + info!(" ip: {ip}/{mask}"); + info!(" gateway: {}", IpAddr::from(ETH0.netif.lock().0.gw)); + } } - - info!("created net interface {:?}:", ETH0.name()); - info!( - " ether: {}", - MacAddr::from_bytes(Ð0.netif.lock().0.hwaddr) - ); - let ip = IpAddr::from(ETH0.netif.lock().0.ip_addr); - let mask = mask_to_prefix(IpAddr::from(ETH0.netif.lock().0.netmask)).unwrap(); - info!(" ip: {}/{}", ip, mask); - info!(" gateway: {}", IpAddr::from(ETH0.netif.lock().0.gw)); } pub fn lwip_loop_once() { let guard = LWIP_MUTEX.lock(); unsafe { ETH0.poll(); + netif_poll(&mut ETH0.netif.lock().0); sys_check_timeouts(); } drop(guard); diff --git a/modules/ruxnet/src/lwip_impl/mod.rs b/modules/ruxnet/src/lwip_impl/mod.rs index ccc72eca6..4f9296ca8 100644 --- a/modules/ruxnet/src/lwip_impl/mod.rs +++ b/modules/ruxnet/src/lwip_impl/mod.rs @@ -1,3 +1,14 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +#![allow(dead_code)] + mod addr; mod dns; mod driver; @@ -5,8 +16,8 @@ mod tcp; mod udp; pub use self::addr::{IpAddr, Ipv4Addr, SocketAddr}; -pub use self::dns::{dns_query, resolve_socket_addr}; -pub use self::driver::{init, poll_interfaces}; +pub use self::dns::dns_query; +pub use self::driver::{init, init_netdev, poll_interfaces}; pub use self::tcp::TcpSocket; pub use self::udp::UdpSocket; use core::ffi::c_uint; diff --git a/modules/ruxnet/src/lwip_impl/tcp.rs b/modules/ruxnet/src/lwip_impl/tcp.rs index 2c621f0eb..9b063dad0 100644 --- a/modules/ruxnet/src/lwip_impl/tcp.rs +++ b/modules/ruxnet/src/lwip_impl/tcp.rs @@ -1,4 +1,14 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + use crate::{ + message::{MessageFlags, MessageReadInfo}, net_impl::{driver::lwip_loop_once, ACCEPT_QUEUE_LEN, RECV_QUEUE_LEN}, IpAddr, SocketAddr, }; @@ -7,13 +17,14 @@ use axerrno::{ax_err, AxError, AxResult}; use axio::PollState; use axsync::Mutex; use core::cell::UnsafeCell; -use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering}; use core::{ffi::c_void, pin::Pin, ptr::null_mut}; +use iovec::IoVecsOutput; use lwip_rust::bindings::{ err_enum_t_ERR_MEM, err_enum_t_ERR_OK, err_enum_t_ERR_USE, err_enum_t_ERR_VAL, err_t, ip_addr_t, pbuf, pbuf_free, tcp_accept, tcp_arg, tcp_bind, tcp_close, tcp_connect, tcp_listen_with_backlog, tcp_new, tcp_output, tcp_pcb, tcp_recv, tcp_recved, tcp_state_CLOSED, - tcp_state_LISTEN, tcp_write, TCP_DEFAULT_LISTEN_BACKLOG, TCP_MSS, + tcp_state_CLOSE_WAIT, tcp_state_LISTEN, tcp_write, TCP_DEFAULT_LISTEN_BACKLOG, TCP_MSS, }; use ruxtask::yield_now; @@ -51,7 +62,7 @@ pub struct TcpSocket { } extern "C" fn connect_callback(arg: *mut c_void, _tpcb: *mut tcp_pcb, err: err_t) -> err_t { - debug!("[TcpSocket] connect_callback: {:#?}", err); + debug!("[TcpSocket] connect_callback: {err:#?}"); let socket_inner = unsafe { &mut *(arg as *mut TcpSocketInner) }; socket_inner.connect_result = err.into(); err @@ -63,9 +74,9 @@ extern "C" fn recv_callback( p: *mut pbuf, err: err_t, ) -> err_t { - debug!("[TcpSocket] recv_callback: {:#?}", err); + debug!("[TcpSocket] recv_callback: {err:#?}"); if err != 0 { - error!("[TcpSocket][recv_callback] err: {:#?}", err); + error!("[TcpSocket][recv_callback] err: {err:#?}"); return err; } let socket_inner = unsafe { &mut *(arg as *mut TcpSocketInner) }; @@ -92,7 +103,7 @@ extern "C" fn recv_callback( extern "C" fn accept_callback(arg: *mut c_void, newpcb: *mut tcp_pcb, err: err_t) -> err_t { if err != 0 { - debug!("[TcpSocket][accept_callback] err: {:#?}", err); + debug!("[TcpSocket][accept_callback] err: {err:#?}"); return err; } let socket_inner = unsafe { &mut *(arg as *mut TcpSocketInner) }; @@ -123,12 +134,12 @@ extern "C" fn accept_callback(arg: *mut c_void, newpcb: *mut tcp_pcb, err: err_t impl TcpSocket { /// Creates a new TCP socket. - pub fn new() -> Self { + pub fn new(nonblock: bool) -> Self { let guard = LWIP_MUTEX.lock(); let mut socket = Self { pcb: TcpPcbPointer(Mutex::new(unsafe { tcp_new() })), inner: Box::pin(TcpSocketInner { - nonblock: AtomicBool::new(false), + nonblock: AtomicBool::new(nonblock), remote_closed: false, connect_result: 0.into(), recv_queue: Mutex::new(VecDeque::new()), @@ -191,6 +202,12 @@ impl TcpSocket { } } + /// Returens if this socket is listening + #[inline] + pub fn is_listening(&self) -> bool { + unsafe { (*self.pcb.get()).state == tcp_state_LISTEN } + } + /// Returns whether this socket is in nonblocking mode. #[inline] pub fn is_nonblocking(&self) -> bool { @@ -214,7 +231,7 @@ impl TcpSocket { /// The local port is generated automatically. pub fn connect(&self, caddr: core::net::SocketAddr) -> AxResult { let addr = SocketAddr::from(caddr); - debug!("[TcpSocket] connect to {:#?}", addr); + debug!("[TcpSocket] connect to {addr:#?}"); let ip_addr: ip_addr_t = addr.addr.into(); unsafe { self.inner.connect_result.get().write(1); @@ -267,7 +284,7 @@ impl TcpSocket { /// [`accept`](Self::accept). pub fn bind(&self, caddr: core::net::SocketAddr) -> AxResult { let addr = SocketAddr::from(caddr); - debug!("[TcpSocket] bind to {:#?}", addr); + debug!("[TcpSocket] bind to {addr:#?}"); let guard = LWIP_MUTEX.lock(); unsafe { #[allow(non_upper_case_globals)] @@ -324,7 +341,7 @@ impl TcpSocket { loop { lwip_loop_once(); let mut accept_queue = self.inner.accept_queue.lock(); - if accept_queue.len() != 0 { + if !accept_queue.is_empty() { return Ok(accept_queue.pop_front().unwrap()); } drop(accept_queue); @@ -353,7 +370,7 @@ impl TcpSocket { match tcp_close(self.pcb.get()) as i32 { err_enum_t_ERR_OK => {} e => { - error!("LWIP tcp_close failed: {}", e); + error!("LWIP tcp_close failed: {e}"); return ax_err!(Unsupported, "LWIP [tcp_close] failed"); } } @@ -369,14 +386,14 @@ impl TcpSocket { } /// Receives data from the socket, stores it in the given buffer. - pub fn recv(&self, buf: &mut [u8], flags: i32) -> AxResult { + pub fn recv(&self, buf: &mut [u8], _flags: MessageFlags) -> AxResult { loop { if self.inner.remote_closed { return Ok(0); } lwip_loop_once(); let mut recv_queue = self.inner.recv_queue.lock(); - let res = if recv_queue.len() == 0 { + let res = if recv_queue.is_empty() { Ok(0) } else { let (p, offset) = recv_queue.pop_front().unwrap(); @@ -426,6 +443,15 @@ impl TcpSocket { } } + /// TODO: receive a message from the socket. + pub fn recvmsg( + &self, + _iovecs: &mut IoVecsOutput, + _flags: MessageFlags, + ) -> AxResult { + todo!() + } + /// Transmits data in the given buffer. pub fn send(&self, buf: &[u8]) -> AxResult { trace!("[TcpSocket] send (len = {})", buf.len()); @@ -453,7 +479,7 @@ impl TcpSocket { } }; lwip_loop_once(); - trace!("[TcpSocket] send done (len: {})", copy_len); + trace!("[TcpSocket] send done (len: {copy_len})"); Ok(copy_len) } @@ -464,18 +490,18 @@ impl TcpSocket { trace!("poll pcbstate: {:?}", unsafe { (*self.pcb.get()).state }); lwip_loop_once(); if unsafe { (*self.pcb.get()).state } == tcp_state_LISTEN { - let test = self.inner.accept_queue.lock().len(); // listener Ok(PollState { - readable: self.inner.accept_queue.lock().len() != 0, + readable: !self.inner.accept_queue.lock().is_empty(), writable: false, + pollhup: false, }) } else { - let test = self.inner.recv_queue.lock().len(); // stream Ok(PollState { - readable: self.inner.recv_queue.lock().len() != 0, + readable: !self.inner.accept_queue.lock().is_empty(), writable: true, + pollhup: unsafe { (*self.pcb.get()).state } == tcp_state_CLOSE_WAIT, }) } } @@ -490,6 +516,6 @@ impl Drop for TcpSocket { impl Default for TcpSocket { fn default() -> Self { - Self::new() + Self::new(false) } } diff --git a/modules/ruxnet/src/lwip_impl/udp.rs b/modules/ruxnet/src/lwip_impl/udp.rs index b56437946..56d8f15d3 100644 --- a/modules/ruxnet/src/lwip_impl/udp.rs +++ b/modules/ruxnet/src/lwip_impl/udp.rs @@ -1,4 +1,14 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + use crate::{ + message::{MessageFlags, MessageReadInfo}, net_impl::{driver::lwip_loop_once, RECV_QUEUE_LEN}, IpAddr, SocketAddr, }; @@ -6,8 +16,9 @@ use alloc::{boxed::Box, collections::VecDeque}; use axerrno::{ax_err, AxError, AxResult}; use axio::PollState; use axsync::Mutex; -use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering}; use core::{ffi::c_void, pin::Pin, ptr::null_mut}; +use iovec::IoVecsOutput; use lwip_rust::bindings::{ err_enum_t_ERR_MEM, err_enum_t_ERR_OK, err_enum_t_ERR_RTE, err_enum_t_ERR_USE, err_enum_t_ERR_VAL, ip_addr_t, pbuf, pbuf_alloc, pbuf_free, pbuf_layer_PBUF_TRANSPORT, @@ -158,7 +169,7 @@ impl UdpSocket { /// [`recv_from`](Self::recv_from). pub fn bind(&self, caddr: core::net::SocketAddr) -> AxResult { let addr = SocketAddr::from(caddr); - debug!("[UdpSocket] bind to {:#?}", addr); + debug!("[UdpSocket] bind to {addr:#?}"); let mut addr = addr; if addr.port == 0 { addr.port = get_ephemeral_port()?; @@ -225,7 +236,7 @@ impl UdpSocket { loop { lwip_loop_once(); let mut recv_queue = self.inner.recv_queue.lock(); - let res: Result<(usize, SocketAddr), AxError> = if recv_queue.len() == 0 { + let res: Result<(usize, SocketAddr), AxError> = if recv_queue.is_empty() { Err(AxError::WouldBlock) } else { let (p, offset, caddr) = recv_queue.pop_front().unwrap(); @@ -253,7 +264,7 @@ impl UdpSocket { drop(guard); } - Ok((copy_len, addr.into())) + Ok((copy_len, addr)) }; drop(recv_queue); match res { @@ -274,6 +285,14 @@ impl UdpSocket { }; } } + /// TODO: Receive a message from the socket. + pub fn recvmsg( + &self, + _iovecs: &mut IoVecsOutput, + _flags: MessageFlags, + ) -> AxResult { + todo!() + } /// Connects to the given address and port. /// @@ -282,7 +301,7 @@ impl UdpSocket { /// [`recv`](Self::recv). pub fn connect(&self, caddr: core::net::SocketAddr) -> AxResult { let addr = SocketAddr::from(caddr); - debug!("[UdpSocket] connect to {:#?}", addr); + debug!("[UdpSocket] connect to {addr:#?}"); let ip_addr: ip_addr_t = addr.addr.into(); let _guard = LWIP_MUTEX.lock(); unsafe { @@ -333,8 +352,9 @@ impl UdpSocket { pub fn poll(&self) -> AxResult { lwip_loop_once(); Ok(PollState { - readable: self.inner.recv_queue.lock().len() != 0, + readable: !self.inner.recv_queue.lock().is_empty(), writable: true, + pollhup: false, }) } } diff --git a/modules/ruxnet/src/message.rs b/modules/ruxnet/src/message.rs new file mode 100644 index 000000000..8ee4b39bf --- /dev/null +++ b/modules/ruxnet/src/message.rs @@ -0,0 +1,419 @@ +/* Copyright (c) [2023] [Syswonder Community] +* [Ruxos] is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ +//! Message Queue +use alloc::{collections::vec_deque::VecDeque, sync::Arc, vec::Vec}; +use axerrno::LinuxResult; +use iovec::{IoVecsInput, IoVecsOutput}; +use ruxfdtable::FileLike; +use ruxfs::OpenFlags; +use ruxtask::fs::{add_file_like, get_file_like}; + +use crate::address::SocketAddress; + +bitflags::bitflags! { + #[repr(C)] + #[derive(Default, Copy, Clone, Debug)] + /// Flags used for send/recv operations on sockets. + /// The definition is from https://elixir.bootlin.com/linux/v6.0.9/source/include/linux/socket.h + pub struct MessageFlags: i32 { + /// Peek at incoming messages without removing them from the queue + const MSG_PEEK = 2; + /// Control data was truncated due to insufficient buffer space + const MSG_CTRUNC = 8; + /// Normal data was truncated because the datagram was larger than the buffer + const MSG_TRUNC = 0x20; + /// **Temporarily** Enable non-blocking operation + const MSG_DONTWAIT = 0x40; + /// End of record (for SOCK_SEQPACKET sockets) + const MSG_EOR = 0x80; + /// Wait until the requested amount of data is available + const MSG_WAITALL = 0x100; + /// TCP FIN flag indication (TCP connection termination) + const MSG_FIN = 0x200; + /// TCP SYN flag indication (TCP connection initiation) + const MSG_SYN = 0x400; + /// Confirm path validity (used for ARP or similar protocols) + const MSG_CONFIRM = 0x800; + /// TCP RST flag indication (TCP connection reset) + const MSG_RST = 0x1000; + /// Fetch message from error queue (used for IP_RECVERR) + const MSG_ERRQUEUE = 0x2000; + /// Do not generate SIGPIPE signal when writing to disconnected socket + const MSG_NOSIGNAL = 0x4000; + /// Sender will send more data (used for TCP_CORK or similar) + const MSG_MORE = 0x8000; + /// Wait for at least one packet (used with recvmmsg()) + const MSG_WAITFORONE = 0x10000; + /// Used for multiple send/recv operations (internal optimization) + const MSG_BATCH = 0x40000; + /// Enable TCP Fast Open (TFO) for this operation + const MSG_FASTOPEN = 0x20000000; + /// Set close_on_exec for file descriptor received through SCM_RIGHTS + const MSG_CMSG_CLOEXEC = 0x40000000; + } +} + +/// Represents a message containing data, optional address information, +/// and optional ancillary/control data. +pub struct Message { + data: Vec, + address: Option, + ancillary_data: Vec, +} + +#[derive(Default)] +/// Contains information about a read operation from a message queue +pub struct MessageReadInfo { + /// Number of bytes actually read into the user buffer. + /// For partial reads, this will be less than both the buffer size and bytes_total. + pub bytes_read: usize, + /// The original total length of the message/datagram. + /// For DGRAM sockets: When a 500-byte datagram is received but the user buffer + /// only has 300 bytes space, bytes_read=300 while bytes_total=500. + /// For STREAM sockets: Typically equals bytes_read since streams have no message boundaries. + pub bytes_total: usize, + /// Source address of the received message. + pub address: Option, + /// Additional control messages received with the data. + /// Ancillary data is a sequence of `cmsghdr` structures with appended data. + /// see + pub ancillary_data: Vec, +} + +#[derive(Clone)] +/// Enum representing different types of control messages that can be sent/received +pub enum ControlMessageData { + /// UNIX domain socket specific control messages + Unix(UnixControlData), + /// Internet domain socket control messages (IP-level options) + Inet(InetControlData), +} +const SOL_SOCKET: i32 = 1; // Socket level for socket options +const SOL_IP: i32 = 0; // Internet Protocol level for IP options +const SOL_IPV6: i32 = 41; // Internet Protocol version 6 level for IPv6 options +const SCM_RIGHTS: i32 = 1; // Send or receive file descriptors +const SCM_CREDENTIALS: i32 = 2; // Send or receive UNIX credentials (user ID, group ID, process ID) +const SCM_SECURITY: i32 = 3; // Send or receive SELinux security context + +impl ControlMessageData { + /// Creates a new control message data instance from the provided level, type, and data. + pub fn try_new(cmsg_level: i32, cmsg_type: i32, data: Vec) -> LinuxResult { + match cmsg_level { + SOL_SOCKET => { + match cmsg_type { + SCM_RIGHTS => { + // Extract file descriptors from the data + let mut files = Vec::new(); + for chunk in data.chunks_exact(core::mem::size_of::()) { + let fd = i32::from_ne_bytes(chunk.try_into().unwrap()); + files.push(get_file_like(fd)?); + } + Ok(ControlMessageData::Unix(UnixControlData::Rights(files))) + } + SCM_CREDENTIALS => todo!(), // Todo: handle credentials + SCM_SECURITY => todo!(), // Todo: handle security context + _ => Err(axerrno::LinuxError::EINVAL), // Unsupported control message type + } + } + SOL_IP => todo!(), // Todo: handle IP control messages + SOL_IPV6 => todo!(), // Todo: handle IPv6 control messages + _ => Err(axerrno::LinuxError::EINVAL), + } + } + + /// Parses the control message data into a tuple of (level, type, data) + pub fn parse(self, flags: MessageFlags) -> LinuxResult<(i32, i32, Vec)> { + match self { + ControlMessageData::Unix(unix_control_data) => { + match unix_control_data { + UnixControlData::Rights(files) => { + // Convert file descriptors to byte representation + let cloexec = if flags.contains(MessageFlags::MSG_CMSG_CLOEXEC) { + OpenFlags::O_CLOEXEC + } else { + Default::default() + }; + let mut fds = Vec::with_capacity(files.len()); + for file in files { + let fd = add_file_like(file, cloexec)?; + fds.push(fd); + } + debug!("received fds: {fds:?}"); + // Safe conversion: i32 -> bytes (preserves endianness) + let mut data_bytes = Vec::with_capacity(fds.len() * 4); + for fd in fds { + data_bytes.extend_from_slice(&fd.to_ne_bytes()); + } + Ok((SOL_SOCKET, SCM_RIGHTS, data_bytes)) + } + UnixControlData::Credentials => todo!(), + UnixControlData::Security => todo!(), + } + } + ControlMessageData::Inet(_) => todo!("Implement Internet control messages"), + } + } +} + +impl ControlMessageData { + /// Calculates the total size needed to store this control message data + pub fn size(&self) -> usize { + match self { + ControlMessageData::Unix(data) => match data { + UnixControlData::Rights(fds) => fds.len() * core::mem::size_of::(), + UnixControlData::Credentials => todo!(), // Todo: size of credentials + UnixControlData::Security => todo!(), // Todo: size of security context + }, + ControlMessageData::Inet(_) => todo!(), // Todo: size of Inet control data + } + } + + /// Returns the minimum buffer size needed to receive this type of control message data + pub fn minium_size(&self) -> usize { + match self { + ControlMessageData::Unix(data) => match data { + UnixControlData::Rights(_) => core::mem::size_of::(), // Minimum size for a single file descriptor + UnixControlData::Credentials => todo!(), // Todo: minimum size of credentials + UnixControlData::Security => todo!(), // Todo: minimum size of security context + }, + ControlMessageData::Inet(_) => todo!(), // Todo: minimum size of Inet control data + } + } +} + +/// UNIX domain socket specific control messages +/// see +#[derive(Clone)] +pub enum UnixControlData { + /// Send or receive a set of open file descriptors from another process. + Rights(Vec>), + /// Todo: Send or receive UNIX credentials + Credentials, + /// Todo: Receive the SELinux security context (the security label) of the peer socket. + Security, +} + +#[derive(Clone)] +/// Todo: Internet socket control messages +pub struct InetControlData; + +/// A queue for storing messages with capacity management +pub struct MessageQueue { + /// The messages stored in the message queue. + messages: VecDeque, + /// Bytes received in message queue (not equal to `messages.len()`) + length: usize, + /// The maximum number of bytes that can be stored + capacity: usize, +} + +impl MessageQueue { + /// Creates a new message queue with the specified capacity + pub fn new(capacity: usize) -> Self { + Self { + messages: VecDeque::new(), + length: 0, + capacity, + } + } + + /// Returns the remaining available capacity in bytes + pub fn available_capacity(&self) -> usize { + self.capacity - self.length + } + + /// Gets the total capacity of the queue + pub fn capacity(&self) -> usize { + self.capacity + } + + /// Bytes received in message queue + pub fn length(&self) -> usize { + self.length + } + + /// Checks if the queue is empty + pub fn is_empty(&self) -> bool { + self.length == 0 + } + + /// Adds a message to the front of the queue + pub fn push_front(&mut self, msg: Message) { + self.length += msg.data.len(); + debug_assert!(self.length <= self.capacity); + self.messages.push_front(msg); + } + + /// Removes and returns the next message from the queue + pub fn read_one_message(&mut self) -> Option { + self.messages.pop_front().inspect(|msg| { + self.length -= msg.data.len(); + }) + } + + /// Adds a message to the end of the queue + pub fn write_one_message(&mut self, msg: Message) { + self.length += msg.data.len(); + debug_assert!(self.length <= self.capacity); + self.messages.push_back(msg); + } + + /// Reads data from the queue for stream-oriented sockets + /// + /// Reads messages until there are no more messages, a message with ancillary data is + /// encountered, or `dst` is full. + pub fn read_stream(&mut self, dst: &mut IoVecsOutput) -> LinuxResult { + let mut total_bytes_read = 0; + let mut ancillary_data = Vec::new(); + let mut address = None; + while let Some(mut msg) = self.read_one_message() { + // All the message in message queue must be the same address because it is a stream socket. + if msg.address.is_some() { + if address.is_none() { + address = msg.address.clone(); + } else { + debug_assert!(address == msg.address); + } + } + let bytes_read = dst.write(&msg.data); + total_bytes_read += bytes_read; + if bytes_read < msg.data.len() { + let remain_bytes = msg.data.split_off(bytes_read); + self.push_front(Message { + data: remain_bytes, + address: msg.address, + ancillary_data: msg.ancillary_data, + }); + break; + } + if !msg.ancillary_data.is_empty() { + ancillary_data = msg.ancillary_data; + break; + } + } + Ok(MessageReadInfo { + bytes_read: total_bytes_read, + bytes_total: total_bytes_read, + address, + ancillary_data, + }) + } + + /// Peeks at data in the queue without removing it (stream version) + pub fn peek_stream(&mut self, dst: &mut IoVecsOutput) -> LinuxResult { + let mut total_bytes_read = 0; + let mut ancillary_data = Vec::new(); + let mut address = None; + for msg in self.messages.iter() { + // All the message in message queue must be the same address because it is a stream socket. + if msg.address.is_some() { + if address.is_none() { + address = msg.address.clone(); + } else { + debug_assert!(address == msg.address); + } + } + let bytes_read = dst.write(&msg.data); + total_bytes_read += bytes_read; + if bytes_read < msg.data.len() { + break; + } + if !msg.ancillary_data.is_empty() { + ancillary_data = msg.ancillary_data.clone(); + break; + } + } + Ok(MessageReadInfo { + bytes_read: total_bytes_read, + bytes_total: total_bytes_read, + address, + ancillary_data, + }) + } + + /// Reads a datagram from the queue + pub fn read_dgram(&mut self, dst: &mut IoVecsOutput) -> LinuxResult { + if let Some(msg) = self.read_one_message() { + return Ok(MessageReadInfo { + bytes_read: dst.write(&msg.data), + bytes_total: msg.data.len(), + address: msg.address, + ancillary_data: msg.ancillary_data, + }); + } + Ok(MessageReadInfo::default()) + } + + /// Peeks at a datagram without removing it from the queue + pub fn peek_dgram(&mut self, dst: &mut IoVecsOutput) -> LinuxResult { + if let Some(msg) = self.messages.front() { + return Ok(MessageReadInfo { + bytes_read: dst.write(&msg.data), + bytes_total: msg.data.len(), + address: msg.address.clone(), + ancillary_data: msg.ancillary_data.clone(), + }); + } + Ok(MessageReadInfo::default()) + } + + /// Writes data to the queue for stream-oriented sockets + /// + /// Using `&mut Vec` with core::mem::take avoids deep-copying Vec data via clone(), + /// transferring ownership with zero cost via pointer swap instead. + pub fn write_stream( + &mut self, + src: &IoVecsInput, + address: Option, + ancillary_data: &mut Vec, + ) -> LinuxResult { + let actual_write = core::cmp::min(self.available_capacity(), src.total_len()); + if actual_write == 0 && src.total_len() > 0 { + // Whether to finally return EAGAIN or block is determined by the outer function based on the flag bits + // (e.g. MessageFlags::DONTWAIT, OpenFlags::O_NONBLOCK). + return Err(axerrno::LinuxError::EAGAIN); + } + let message = Message { + data: src.read_to_vec(actual_write), + address, + ancillary_data: core::mem::take(ancillary_data), + }; + self.write_one_message(message); + Ok(actual_write) + } + + /// Writes a datagram to the queue + /// + /// Using `&mut Vec` with core::mem::take avoids deep-copying Vec data via clone(), + /// transferring ownership with zero cost via pointer swap instead. + pub fn write_dgram( + &mut self, + src: &IoVecsInput, + address: Option, + ancillary_data: &mut Vec, + ) -> LinuxResult { + let actual_write = src.total_len(); + if actual_write > self.capacity { + // The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible. + return Err(axerrno::LinuxError::EMSGSIZE); + } + if actual_write > self.available_capacity() { + // Whether to finally return EAGAIN or block is determined by the outer function based on the flag bits + // (e.g. MessageFlags::DONTWAIT, OpenFlags::O_NONBLOCK). + return Err(axerrno::LinuxError::EAGAIN); + } + let message = Message { + data: src.read_to_vec(actual_write), + address, + ancillary_data: core::mem::take(ancillary_data), + }; + self.write_one_message(message); + Ok(actual_write) + } +} diff --git a/modules/ruxnet/src/smoltcp_impl/dns.rs b/modules/ruxnet/src/smoltcp_impl/dns.rs index 00b7c0575..cf33c3494 100644 --- a/modules/ruxnet/src/smoltcp_impl/dns.rs +++ b/modules/ruxnet/src/smoltcp_impl/dns.rs @@ -16,7 +16,7 @@ use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError}; use smoltcp::wire::DnsQueryType; use super::addr::into_core_ipaddr; -use super::{SocketSetWrapper, ETH0, SOCKET_SET}; +use super::{SocketSetWrapper, IFACE_LIST, SOCKET_SET}; /// A DNS socket. struct DnsSocket { @@ -44,7 +44,12 @@ impl DnsSocket { pub fn query(&self, name: &str, query_type: DnsQueryType) -> AxResult> { // let local_addr = self.local_addr.unwrap_or_else(f); let handle = self.handle.ok_or_else(|| ax_err_type!(InvalidInput))?; - let iface = Ð0.iface; + let binding = IFACE_LIST.lock(); + let iface = &binding + .iter() + .find(|iface| iface.name() == "eth0") + .unwrap() + .iface; let query_handle = SOCKET_SET .with_socket_mut::(handle, |socket| { socket.start_query(iface.lock().context(), name, query_type) @@ -61,7 +66,7 @@ impl DnsSocket { } })?; loop { - SOCKET_SET.poll_interfaces(); + SOCKET_SET.poll_interfaces(None); match SOCKET_SET.with_socket_mut::(handle, |socket| { socket.get_query_result(query_handle).map_err(|e| match e { GetQueryResultError::Pending => AxError::WouldBlock, diff --git a/modules/ruxnet/src/smoltcp_impl/mod.rs b/modules/ruxnet/src/smoltcp_impl/mod.rs index da911f2f7..8ee5c307a 100644 --- a/modules/ruxnet/src/smoltcp_impl/mod.rs +++ b/modules/ruxnet/src/smoltcp_impl/mod.rs @@ -14,6 +14,7 @@ mod listen_table; mod tcp; mod udp; +use alloc::string::{String, ToString}; use alloc::vec; use core::cell::RefCell; use core::ops::DerefMut; @@ -61,7 +62,15 @@ const LISTEN_QUEUE_SIZE: usize = 512; static LISTEN_TABLE: LazyInit = LazyInit::new(); static SOCKET_SET: LazyInit = LazyInit::new(); -static ETH0: LazyInit = LazyInit::new(); +static IFACE_LIST: LazyInit>> = LazyInit::new(); + +fn route_dev(addr: [u8; 4]) -> String { + if addr[0] == 127 { + "loopback".to_string() + } else { + "eth0".to_string() + } +} struct SocketSetWrapper<'a>(Mutex>); @@ -128,8 +137,12 @@ impl<'a> SocketSetWrapper<'a> { f(socket) } - pub fn poll_interfaces(&self) { - ETH0.poll(&self.0); + pub fn poll_interfaces(&self, iface_name: Option) { + for iface in IFACE_LIST.lock().iter() { + if iface_name.is_none() || iface_name.clone().unwrap() == iface.name() { + iface.poll(&self.0); + } + } } pub fn remove(&self, handle: SocketHandle) { @@ -197,8 +210,14 @@ impl DeviceWrapper { } impl Device for DeviceWrapper { - type RxToken<'a> = AxNetRxToken<'a> where Self: 'a; - type TxToken<'a> = AxNetTxToken<'a> where Self: 'a; + type RxToken<'a> + = AxNetRxToken<'a> + where + Self: 'a; + type TxToken<'a> + = AxNetTxToken<'a> + where + Self: 'a; fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let mut dev = self.inner.borrow_mut(); @@ -276,7 +295,6 @@ impl<'a> TxToken for AxNetTxToken<'a> { let mut dev = self.0.borrow_mut(); let mut tx_buf = dev.alloc_tx_buffer(len).unwrap(); let ret = f(tx_buf.packet_mut()); - trace!("SEND {} bytes: {:02X?}", len, tx_buf.packet()); dev.transmit(tx_buf).unwrap(); ret } @@ -306,34 +324,70 @@ fn snoop_tcp_packet(buf: &[u8], sockets: &mut SocketSet<'_>) -> Result<(), smolt /// It may receive packets from the NIC and process them, and transmit queued /// packets to the NIC. pub fn poll_interfaces() { - SOCKET_SET.poll_interfaces(); + SOCKET_SET.poll_interfaces(None); } /// Benchmark raw socket transmit bandwidth. pub fn bench_transmit() { - ETH0.dev.lock().bench_transmit_bandwidth(); + IFACE_LIST + .lock() + .iter() + .find(|iface| iface.name() == "eth0") + .unwrap() + .dev + .lock() + .bench_transmit_bandwidth(); } /// Benchmark raw socket receive bandwidth. pub fn bench_receive() { - ETH0.dev.lock().bench_receive_bandwidth(); + IFACE_LIST + .lock() + .iter() + .find(|iface| iface.name() == "eth0") + .unwrap() + .dev + .lock() + .bench_receive_bandwidth(); } -pub(crate) fn init(net_dev: AxNetDevice) { - let ether_addr = EthernetAddress(net_dev.mac_address().0); - let eth0 = InterfaceWrapper::new("eth0", net_dev, ether_addr); +pub(crate) fn init() { + let socketset = SocketSetWrapper::new(); - let ip = IP.parse().expect("invalid IP address"); - let gateway = GATEWAY.parse().expect("invalid gateway IP address"); - eth0.setup_ip_addr(ip, IP_PREFIX); - eth0.setup_gateway(gateway); - - ETH0.init_by(eth0); - SOCKET_SET.init_by(SocketSetWrapper::new()); + IFACE_LIST.init_by(Mutex::new(vec::Vec::new())); + SOCKET_SET.init_by(socketset); LISTEN_TABLE.init_by(ListenTable::new()); +} - info!("created net interface {:?}:", ETH0.name()); - info!(" ether: {}", ETH0.ethernet_address()); - info!(" ip: {}/{}", ip, IP_PREFIX); - info!(" gateway: {}", gateway); +pub(crate) fn init_netdev(net_dev: AxNetDevice) { + match net_dev.device_name() { + "loopback" => { + let ether_addr = EthernetAddress(net_dev.mac_address().0); + let lo = InterfaceWrapper::new("loopback", net_dev, ether_addr); + + let ip = "127.0.0.1".parse().expect("invalid IP address"); + lo.setup_ip_addr(ip, IP_PREFIX); + + info!("created net interface {:?}:", lo.name()); + info!(" ether: {}", lo.ethernet_address()); + info!(" ip: {}/{}", "127.0.0.1", IP_PREFIX); + IFACE_LIST.lock().push(lo); + } + _ => { + let ether_addr = EthernetAddress(net_dev.mac_address().0); + let eth0 = InterfaceWrapper::new("eth0", net_dev, ether_addr); + + let ip = IP.parse().expect("invalid IP address"); + let gateway = GATEWAY.parse().expect("invalid gateway IP address"); + eth0.setup_ip_addr(ip, IP_PREFIX); + eth0.setup_gateway(gateway); + + info!("created net interface {:?}:", eth0.name()); + info!(" ether: {}", eth0.ethernet_address()); + info!(" ip: {}/{}", ip, IP_PREFIX); + info!(" gateway: {}", gateway); + + IFACE_LIST.lock().push(eth0); + } + } } diff --git a/modules/ruxnet/src/smoltcp_impl/tcp.rs b/modules/ruxnet/src/smoltcp_impl/tcp.rs index de9c14fd5..0f100cd17 100644 --- a/modules/ruxnet/src/smoltcp_impl/tcp.rs +++ b/modules/ruxnet/src/smoltcp_impl/tcp.rs @@ -11,16 +11,21 @@ use core::cell::UnsafeCell; use core::net::SocketAddr; use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use alloc::string::String; +use alloc::vec; use axerrno::{ax_err, ax_err_type, AxError, AxResult}; use axio::PollState; use axsync::Mutex; +use iovec::IoVecsOutput; use smoltcp::iface::SocketHandle; use smoltcp::socket::tcp::{self, ConnectError, State}; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; +use crate::message::{MessageFlags, MessageReadInfo}; + use super::addr::{from_core_sockaddr, into_core_sockaddr, is_unspecified, UNSPECIFIED_ENDPOINT}; -use super::{SocketSetWrapper, ETH0, LISTEN_TABLE, SOCKET_SET}; +use super::{route_dev, SocketSetWrapper, IFACE_LIST, LISTEN_TABLE, SOCKET_SET}; // State transitions: // CLOSED -(connect)-> BUSY -> CONNECTING -> CONNECTED -(shutdown)-> BUSY -> CLOSED @@ -34,9 +39,6 @@ const STATE_CONNECTING: u8 = 2; const STATE_CONNECTED: u8 = 3; const STATE_LISTENING: u8 = 4; -const MSG_PEEK: i32 = 2; -const MSG_DONTWAIT: i32 = 4; - /// A TCP socket that provides POSIX-like APIs. /// /// - [`connect`] is for TCP clients. @@ -53,19 +55,21 @@ pub struct TcpSocket { local_addr: UnsafeCell, peer_addr: UnsafeCell, nonblock: AtomicBool, + iface_name: Mutex>, } unsafe impl Sync for TcpSocket {} impl TcpSocket { /// Creates a new TCP socket. - pub const fn new() -> Self { + pub const fn new(nonblock: bool) -> Self { Self { state: AtomicU8::new(STATE_CLOSED), handle: UnsafeCell::new(None), local_addr: UnsafeCell::new(UNSPECIFIED_ENDPOINT), peer_addr: UnsafeCell::new(UNSPECIFIED_ENDPOINT), - nonblock: AtomicBool::new(false), + nonblock: AtomicBool::new(nonblock), + iface_name: Mutex::new(None), } } @@ -81,6 +85,7 @@ impl TcpSocket { local_addr: UnsafeCell::new(local_addr), peer_addr: UnsafeCell::new(peer_addr), nonblock: AtomicBool::new(false), + iface_name: Mutex::new(None), } } @@ -108,6 +113,12 @@ impl TcpSocket { } } + /// Returens if this socket is listening + #[inline] + pub fn is_listening(&self) -> bool { + self.get_state() == STATE_LISTENING + } + /// Returns whether this socket is in nonblocking mode. #[inline] pub fn is_nonblocking(&self) -> bool { @@ -131,6 +142,11 @@ impl TcpSocket { /// /// The local port is generated automatically. pub fn connect(&self, remote_addr: SocketAddr) -> AxResult { + let iface_name = Some(match remote_addr { + SocketAddr::V4(addr) => route_dev(addr.ip().octets()), + _ => panic!("IPv6 not supported"), + }); + *self.iface_name.lock() = iface_name; self.update_state(STATE_CLOSED, STATE_CONNECTING, || { // SAFETY: no other threads can read or write these fields. let handle = unsafe { self.handle.get().read() } @@ -139,7 +155,12 @@ impl TcpSocket { // TODO: check remote addr unreachable let remote_endpoint = from_core_sockaddr(remote_addr); let bound_endpoint = self.bound_endpoint()?; - let iface = Ð0.iface; + let binding = IFACE_LIST.lock(); + let iface = &binding + .iter() + .find(|iface| iface.name() == self.iface_name.lock().clone().unwrap()) + .unwrap() + .iface; let (local_endpoint, remote_endpoint) = SOCKET_SET .with_socket_mut::(handle, |socket| { socket @@ -152,7 +173,7 @@ impl TcpSocket { ax_err!(ConnectionRefused, "socket connect() failed") } })?; - Ok(( + Ok::<(IpEndpoint, IpEndpoint), AxError>(( socket.local_endpoint().unwrap(), socket.remote_endpoint().unwrap(), )) @@ -168,24 +189,27 @@ impl TcpSocket { }) .unwrap_or_else(|_| ax_err!(AlreadyExists, "socket connect() failed: already connected"))?; // EISCONN - self.block_on(|| { - let PollState { writable, .. } = self.poll_connect()?; - if !writable { - // When set to non_blocking, directly return inporgress - if self.is_nonblocking() { - return Err(AxError::InProgress); - } - Err(AxError::WouldBlock) - } else if self.get_state() == STATE_CONNECTED { - Ok(()) - } else { - // When set to non_blocking, directly return inporgress - if self.is_nonblocking() { - return Err(AxError::InProgress); + self.block_on( + || { + let PollState { writable, .. } = self.poll_connect()?; + if !writable { + // When set to non_blocking, directly return inporgress + if self.is_nonblocking() { + return Err(AxError::InProgress); + } + Err(AxError::WouldBlock) + } else if self.get_state() == STATE_CONNECTED { + Ok(()) + } else { + // When set to non_blocking, directly return inporgress + if self.is_nonblocking() { + return Err(AxError::InProgress); + } + ax_err!(ConnectionRefused, "socket connect() failed") } - ax_err!(ConnectionRefused, "socket connect() failed") - } - }) + }, + self.iface_name.lock().clone(), + ) } /// Binds an unbound socket to the given address and port. @@ -244,11 +268,14 @@ impl TcpSocket { // SAFETY: `self.local_addr` should be initialized after `bind()`. let local_port = unsafe { self.local_addr.get().read().port }; - self.block_on(|| { - let (handle, (local_addr, peer_addr)) = LISTEN_TABLE.accept(local_port)?; - debug!("TCP socket accepted a new connection {}", peer_addr); - Ok(TcpSocket::new_connected(handle, local_addr, peer_addr)) - }) + self.block_on( + || { + let (handle, (local_addr, peer_addr)) = LISTEN_TABLE.accept(local_port)?; + debug!("TCP socket accepted a new connection {}", peer_addr); + Ok(TcpSocket::new_connected(handle, local_addr, peer_addr)) + }, + None, + ) } /// Close the connection. @@ -263,7 +290,7 @@ impl TcpSocket { socket.close(); }); unsafe { self.local_addr.get().write(UNSPECIFIED_ENDPOINT) }; // clear bound address - SOCKET_SET.poll_interfaces(); + SOCKET_SET.poll_interfaces(None); Ok(()) }) .unwrap_or(Ok(()))?; @@ -275,7 +302,7 @@ impl TcpSocket { let local_port = unsafe { self.local_addr.get().read().port }; unsafe { self.local_addr.get().write(UNSPECIFIED_ENDPOINT) }; // clear bound address LISTEN_TABLE.unlisten(local_port); - SOCKET_SET.poll_interfaces(); + SOCKET_SET.poll_interfaces(None); Ok(()) }) .unwrap_or(Ok(()))?; @@ -285,7 +312,7 @@ impl TcpSocket { } /// Receives data from the socket, stores it in the given buffer. - pub fn recv(&self, buf: &mut [u8], flags: i32) -> AxResult { + pub fn recv(&self, buf: &mut [u8], flags: MessageFlags) -> AxResult { if self.is_connecting() { return Err(AxError::WouldBlock); } else if !self.is_connected() { @@ -294,36 +321,88 @@ impl TcpSocket { // SAFETY: `self.handle` should be initialized in a connected socket. let handle = unsafe { self.handle.get().read().unwrap() }; - self.block_on(|| { - SOCKET_SET.with_socket_mut::(handle, |socket| { - if !socket.is_active() { - // not open - ax_err!(ConnectionRefused, "socket recv() failed") - } else if !socket.may_recv() { - // connection closed - Ok(0) - } else if socket.recv_queue() > 0 { - // data available - // TODO: use socket.recv(|buf| {...}) - if flags & MSG_DONTWAIT != 0 { - self.set_nonblocking(true); + self.block_on( + || { + SOCKET_SET.with_socket_mut::(handle, |socket| { + if !socket.is_active() { + // not open + ax_err!(ConnectionRefused, "socket recv() failed") + } else if !socket.may_recv() { + // connection closed + Ok(0) + } else if socket.recv_queue() > 0 { + // data available + // TODO: use socket.recv(|buf| {...}) + if flags.contains(MessageFlags::MSG_DONTWAIT) { + // FIXME: MSG_DONTWAIT means socket should be temporarily set `true` + self.set_nonblocking(true); + } + if flags.contains(MessageFlags::MSG_PEEK) { + let len = socket + .peek_slice(buf) + .map_err(|_| ax_err_type!(BadState, "socket recv() failed"))?; + Ok(len) + } else { + let len = socket + .recv_slice(buf) + .map_err(|_| ax_err_type!(BadState, "socket recv() failed"))?; + Ok(len) + } + } else { + // no more data + Err(AxError::WouldBlock) } - if flags & MSG_PEEK != 0 { - let len = socket - .peek_slice(buf) - .map_err(|_| ax_err_type!(BadState, "socket recv() failed"))?; - Ok(len) + }) + }, + None, + ) + } + + pub fn recvmsg( + &self, + iovecs: &mut IoVecsOutput, + flags: MessageFlags, + ) -> AxResult { + if !flags.is_empty() { + warn!("unsupported TCP recvmsg flags"); + } + if self.is_connecting() { + return Err(AxError::WouldBlock); + } else if !self.is_connected() { + return ax_err!(NotConnected, "socket recv() failed"); + } + // SAFETY: `self.handle` should be initialized in a connected socket. + let handle = unsafe { self.handle.get().read().unwrap() }; + let bytes_read = self.block_on( + || { + SOCKET_SET.with_socket_mut::(handle, |socket| { + if !socket.is_active() { + // not open + ax_err!(ConnectionRefused, "socket recv() failed") + } else if !socket.may_recv() { + // connection closed + Ok(0) + } else if socket.recv_queue() > 0 { + let bytes_received = socket + .recv(|data| { + let total_bytes_copied = iovecs.write(&data); + (total_bytes_copied, Ok::(total_bytes_copied)) + }) + .map_err(|_| ax_err_type!(BadState, "socket recvmsg() failed"))??; + Ok(bytes_received) } else { - let len = socket - .recv_slice(buf) - .map_err(|_| ax_err_type!(BadState, "socket recv() failed"))?; - Ok(len) + // no more data + Err(AxError::WouldBlock) } - } else { - // no more data - Err(AxError::WouldBlock) - } - }) + }) + }, + None, + )?; + Ok(MessageReadInfo { + bytes_read, + bytes_total: bytes_read, + address: None, + ancillary_data: vec![], }) } @@ -338,24 +417,27 @@ impl TcpSocket { // SAFETY: `self.handle` should be initialized in a connected socket. let handle = unsafe { self.handle.get().read().unwrap() }; - self.block_on(|| { - SOCKET_SET.with_socket_mut::(handle, |socket| { - if !socket.is_active() || !socket.may_send() { - // closed by remote - ax_err!(ConnectionReset, "socket send() failed") - } else if socket.can_send() { - // connected, and the tx buffer is not full - // TODO: use socket.send(|buf| {...}) - let len = socket - .send_slice(buf) - .map_err(|_| ax_err_type!(BadState, "socket send() failed"))?; - Ok(len) - } else { - // tx buffer is full - Err(AxError::WouldBlock) - } - }) - }) + self.block_on( + || { + SOCKET_SET.with_socket_mut::(handle, |socket| { + if !socket.is_active() || !socket.may_send() { + // closed by remote + ax_err!(ConnectionReset, "socket send() failed") + } else if socket.can_send() { + // connected, and the tx buffer is not full + // TODO: use socket.send(|buf| {...}) + let len = socket + .send_slice(buf) + .map_err(|_| ax_err_type!(BadState, "socket send() failed"))?; + Ok(len) + } else { + // tx buffer is full + Err(AxError::WouldBlock) + } + }) + }, + self.iface_name.lock().clone(), + ) } /// Whether the socket is readable or writable. @@ -367,6 +449,7 @@ impl TcpSocket { _ => Ok(PollState { readable: false, writable: false, + pollhup: false, }), } } @@ -423,11 +506,6 @@ impl TcpSocket { self.get_state() == STATE_CONNECTED } - #[inline] - fn is_listening(&self) -> bool { - self.get_state() == STATE_LISTENING - } - fn bound_endpoint(&self) -> AxResult { // SAFETY: no other threads can read or write `self.local_addr`. let local_addr = unsafe { self.local_addr.get().read() }; @@ -472,16 +550,21 @@ impl TcpSocket { Ok(PollState { readable: false, writable, + pollhup: false, }) } fn poll_stream(&self) -> AxResult { // SAFETY: `self.handle` should be initialized in a connected socket. let handle = unsafe { self.handle.get().read().unwrap() }; + let pollhup = SOCKET_SET.with_socket_mut::(handle, |socket| { + socket.state() == tcp::State::CloseWait + }); SOCKET_SET.with_socket::(handle, |socket| { Ok(PollState { readable: !socket.may_recv() || socket.can_recv(), writable: !socket.may_send() || socket.can_send(), + pollhup, }) }) } @@ -492,6 +575,7 @@ impl TcpSocket { Ok(PollState { readable: LISTEN_TABLE.can_accept(local_addr.port)?, writable: false, + pollhup: false, }) } @@ -500,16 +584,19 @@ impl TcpSocket { /// If the socket is non-blocking, it calls the function once and returns /// immediately. Otherwise, it may call the function multiple times if it /// returns [`Err(WouldBlock)`](AxError::WouldBlock). - fn block_on(&self, mut f: F) -> AxResult + fn block_on(&self, mut f: F, iface: Option) -> AxResult where F: FnMut() -> AxResult, { if self.is_nonblocking() { - f() + let res = f(); + SOCKET_SET.poll_interfaces(iface.clone()); + res } else { loop { - SOCKET_SET.poll_interfaces(); - match f() { + let res = f(); + SOCKET_SET.poll_interfaces(iface.clone()); + match res { Ok(t) => return Ok(t), Err(AxError::WouldBlock) => ruxtask::yield_now(), Err(e) => return Err(e), @@ -531,7 +618,7 @@ impl Drop for TcpSocket { impl axio::Read for TcpSocket { fn read(&mut self, buf: &mut [u8]) -> AxResult { - self.recv(buf, 0) + self.recv(buf, MessageFlags::empty()) } } diff --git a/modules/ruxnet/src/smoltcp_impl/udp.rs b/modules/ruxnet/src/smoltcp_impl/udp.rs index 5bf098166..dca94bb70 100644 --- a/modules/ruxnet/src/smoltcp_impl/udp.rs +++ b/modules/ruxnet/src/smoltcp_impl/udp.rs @@ -10,17 +10,22 @@ use core::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use core::sync::atomic::{AtomicBool, Ordering}; +use alloc::string::String; +use alloc::vec; use axerrno::{ax_err, ax_err_type, AxError, AxResult}; use axio::PollState; use axsync::Mutex; +use iovec::IoVecsOutput; use spin::RwLock; use smoltcp::iface::SocketHandle; use smoltcp::socket::udp::{self, BindError, SendError}; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; +use crate::message::{MessageFlags, MessageReadInfo}; + use super::addr::{from_core_sockaddr, into_core_sockaddr, is_unspecified, UNSPECIFIED_ENDPOINT}; -use super::{SocketSetWrapper, SOCKET_SET}; +use super::{route_dev, SocketSetWrapper, SOCKET_SET}; /// A UDP socket that provides POSIX-like APIs. pub struct UdpSocket { @@ -179,13 +184,40 @@ impl UdpSocket { }) } + pub fn recvmsg( + &self, + iovecs: &mut IoVecsOutput, + flags: MessageFlags, + ) -> AxResult { + self.recv_impl(move |socket| { + let (data, meta) = if flags.contains(MessageFlags::MSG_PEEK) { + let peek = socket + .peek() + .map_err(|_| ax_err_type!(BadState, "socket recvmsg() failed"))?; + (peek.0, *peek.1) + } else { + socket + .recv() + .map_err(|_| ax_err_type!(BadState, "socket recvmsg() failed"))? + }; + + let bytes_read = iovecs.write(data); + Ok(MessageReadInfo { + bytes_read, + bytes_total: data.len(), + address: Some(into_core_sockaddr(meta.endpoint).into()), + ancillary_data: vec![], + }) + }) + } + /// Close the socket. pub fn shutdown(&self) -> AxResult { SOCKET_SET.with_socket_mut::(self.handle, |socket| { debug!("UDP socket {}: shutting down", self.handle); socket.close(); }); - SOCKET_SET.poll_interfaces(); + SOCKET_SET.poll_interfaces(None); Ok(()) } @@ -195,12 +227,14 @@ impl UdpSocket { return Ok(PollState { readable: false, writable: false, + pollhup: false, }); } SOCKET_SET.with_socket_mut::(self.handle, |socket| { Ok(PollState { readable: socket.can_recv(), writable: socket.can_send(), + pollhup: false, }) }) } @@ -221,24 +255,31 @@ impl UdpSocket { self.bind(res)?; } - self.block_on(|| { - SOCKET_SET.with_socket_mut::(self.handle, |socket| { - if socket.can_send() { - socket - .send_slice(buf, remote_endpoint) - .map_err(|e| match e { - SendError::BufferFull => AxError::WouldBlock, - SendError::Unaddressable => { - ax_err_type!(ConnectionRefused, "socket send() failed") - } - })?; - Ok(buf.len()) - } else { - // tx buffer is full - Err(AxError::WouldBlock) - } - }) - }) + let mut addr = [0u8; 4]; + addr.copy_from_slice(remote_endpoint.addr.as_bytes()); + let iface_name = route_dev(addr); + + self.block_on( + || { + SOCKET_SET.with_socket_mut::(self.handle, |socket| { + if socket.can_send() { + socket + .send_slice(buf, remote_endpoint) + .map_err(|e| match e { + SendError::BufferFull => AxError::WouldBlock, + SendError::Unaddressable => { + ax_err_type!(ConnectionRefused, "socket send() failed") + } + })?; + Ok(buf.len()) + } else { + // tx buffer is full + Err(AxError::WouldBlock) + } + }) + }, + Some(iface_name), + ) } fn recv_impl(&self, mut op: F) -> AxResult @@ -249,29 +290,35 @@ impl UdpSocket { return ax_err!(NotConnected, "socket send() failed"); } - self.block_on(|| { - SOCKET_SET.with_socket_mut::(self.handle, |socket| { - if socket.can_recv() { - // data available - op(socket) - } else { - // no more data - Err(AxError::WouldBlock) - } - }) - }) + self.block_on( + || { + SOCKET_SET.with_socket_mut::(self.handle, |socket| { + if socket.can_recv() { + // data available + op(socket) + } else { + // no more data + Err(AxError::WouldBlock) + } + }) + }, + None, + ) } - fn block_on(&self, mut f: F) -> AxResult + fn block_on(&self, mut f: F, iface: Option) -> AxResult where F: FnMut() -> AxResult, { if self.is_nonblocking() { - f() + let res = f(); + SOCKET_SET.poll_interfaces(iface.clone()); + res } else { loop { - SOCKET_SET.poll_interfaces(); - match f() { + let res = f(); + SOCKET_SET.poll_interfaces(iface.clone()); + match res { Ok(t) => return Ok(t), Err(AxError::WouldBlock) => ruxtask::yield_now(), Err(e) => return Err(e), diff --git a/modules/ruxnet/src/socket.rs b/modules/ruxnet/src/socket.rs new file mode 100644 index 000000000..e9d625d58 --- /dev/null +++ b/modules/ruxnet/src/socket.rs @@ -0,0 +1,403 @@ +/* Copyright (c) [2023] [Syswonder Community] +* [Ruxos] is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ +//! Combines both network and UNIX domain sockets under a common interface. +use core::net::SocketAddr; + +use alloc::{sync::Arc, vec::Vec}; +use axerrno::{LinuxError, LinuxResult}; +use axfs_vfs::AbsPath; +use axio::PollState; +use axsync::Mutex; +use iovec::{IoVecsInput, IoVecsOutput}; +use ruxfdtable::{FileLike, RuxStat}; +use ruxfs::OpenFlags; + +use crate::{ + address::SocketAddress, + message::{ControlMessageData, MessageFlags, MessageReadInfo}, + unix::UnixSocket, + ShutdownFlags, TcpSocket, UdpSocket, +}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +/// Represents the type of socket communication semantics. +pub enum SocketType { + /// Connection-oriented, reliable byte stream (SOCK_STREAM) + /// Used by TCP and UNIX stream sockets + Stream, + /// Connectionless, unreliable datagrams (SOCK_DGRAM) + /// Used by UDP and UNIX datagram sockets + Datagram, +} + +impl TryFrom for SocketType { + type Error = LinuxError; + + fn try_from(ty: u32) -> Result { + match ty { + 1 => Ok(SocketType::Stream), + 2 => Ok(SocketType::Datagram), + _ => Err(LinuxError::EAFNOSUPPORT), + } + } +} + +impl From for u32 { + fn from(value: SocketType) -> Self { + match value { + SocketType::Stream => 1, + SocketType::Datagram => 2, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +/// Socket domain +pub enum SocketDomain { + /// UNIX domain sockets (AF_UNIX) - local IPC on same host + /// Supports both stream and datagram types + Unix, + /// An AF_INET socket. + Inet, + /// An AF_INET6 socket. + Inet6, +} + +impl TryFrom for SocketDomain { + type Error = LinuxError; + + fn try_from(domain: u16) -> Result { + match domain { + 1 => Ok(SocketDomain::Unix), // AF_UNIX + 2 => Ok(SocketDomain::Inet), // AF_INET + 10 => Ok(SocketDomain::Inet6), // AF_INET6 + _ => Err(LinuxError::EAFNOSUPPORT), + } + } +} + +/// Enum representing concrete socket implementations. +/// Combines both network and UNIX domain sockets under a common interface. +pub enum Socket { + /// Tcp + Tcp(Mutex), + /// Udp + Udp(Mutex), + /// Unix + Unix(UnixSocket), +} + +impl FileLike for Socket { + fn path(&self) -> AbsPath { + AbsPath::new("/dev/socket") + } + + fn read(&self, buf: &mut [u8]) -> LinuxResult { + self.recv(buf, MessageFlags::empty()) + } + + fn write(&self, buf: &[u8]) -> LinuxResult { + self.send(buf, MessageFlags::empty()) + } + + fn flush(&self) -> LinuxResult { + Ok(()) + } + + fn stat(&self) -> LinuxResult { + Ok(RuxStat { + st_mode: 0o140000 | 0o777u32, // S_IFSOCK | rwxrwxrwx; + ..Default::default() + }) + } + + fn into_any(self: Arc) -> Arc { + self + } + + fn poll(&self) -> LinuxResult { + match self { + Socket::Udp(udpsocket) => Ok(udpsocket.lock().poll()?), + Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().poll()?), + Socket::Unix(unixsocket) => unixsocket.poll(), + } + } + + fn set_flags(&self, flags: OpenFlags) -> LinuxResult { + let nonblock = flags.contains(OpenFlags::O_NONBLOCK); + match self { + Socket::Udp(udpsocket) => udpsocket.lock().set_nonblocking(nonblock), + Socket::Tcp(tcpsocket) => tcpsocket.lock().set_nonblocking(nonblock), + Socket::Unix(unixsocket) => unixsocket.set_nonblocking(nonblock), + } + Ok(()) + } + + fn flags(&self) -> OpenFlags { + let nonblock = match self { + Socket::Udp(udpsocket) => udpsocket.lock().is_nonblocking(), + Socket::Tcp(tcpsocket) => tcpsocket.lock().is_nonblocking(), + Socket::Unix(unixsocket) => unixsocket.is_nonblocking(), + }; + if nonblock { + OpenFlags::O_NONBLOCK | OpenFlags::O_RDWR + } else { + OpenFlags::O_RDWR + } + } +} + +impl Socket { + /// Returns the address family/domain of the socket. + pub fn domain(&self) -> SocketDomain { + match self { + Socket::Tcp(_) => SocketDomain::Inet, + Socket::Udp(_) => SocketDomain::Inet, + Socket::Unix(_) => SocketDomain::Unix, + } + } + + /// Returns the socket type + pub fn socket_type(&self) -> SocketType { + match self { + Socket::Tcp(_) => SocketType::Stream, + Socket::Udp(_) => SocketType::Datagram, + Socket::Unix(unixsocket) => unixsocket.socket_type(), + } + } + + /// Returns a reference to the underlying UnixSocket if applicable. + /// Panics if called on a network socket. + pub fn as_unix_socket(&self) -> &UnixSocket { + match self { + Socket::Unix(unixsocket) => unixsocket, + _ => panic!("Not a Unix socket"), + } + } + + /// Binds the socket to a specific address. + /// For network sockets: binds to IP:port + /// For UNIX sockets: binds to a filesystem path + pub fn bind(self: Arc, address: SocketAddress) -> LinuxResult { + match *self { + Socket::Udp(ref udpsocket) => { + if let SocketAddress::Inet(ipv4_addr) = address { + udpsocket + .lock() + .bind(SocketAddr::V4(ipv4_addr)) + .map_err(LinuxError::from) + } else { + Err(LinuxError::EINVAL) + } + } + Socket::Tcp(ref tcpsocket) => { + if let SocketAddress::Inet(ipv4_addr) = address { + tcpsocket + .lock() + .bind(SocketAddr::V4(ipv4_addr)) + .map_err(LinuxError::from) + } else { + Err(LinuxError::EINVAL) + } + } + Socket::Unix(ref unixsocket) => unixsocket.bind(self.clone(), address), + } + } + + /// Starts listening for incoming connections (stream sockets only). + /// backlog specifies the maximum pending connections queue size. + pub fn listen(&self, backlog: i32) -> LinuxResult { + match self { + Socket::Udp(_) => Err(LinuxError::EOPNOTSUPP), + Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().listen()?), + Socket::Unix(unixsocket) => Ok(unixsocket.listen(backlog)?), + } + } + + /// Accepts an incoming connection (stream sockets only). + /// Returns a new Socket for the accepted connection. + pub fn accept(&self) -> LinuxResult> { + match self { + Socket::Udp(_) => Err(LinuxError::EOPNOTSUPP), + Socket::Tcp(tcpsocket) => Ok(Arc::new(Socket::Tcp(Mutex::new( + tcpsocket.lock().accept()?, + )))), + Socket::Unix(unixsocket) => unixsocket.accept(), + } + } + + /// Connects to a remote endpoint. + /// For datagram sockets, this sets the default destination. + pub fn connect(self: Arc, address: SocketAddress) -> LinuxResult { + match *self { + Socket::Udp(ref udpsocket) => Ok(udpsocket.lock().connect(address.into())?), + Socket::Tcp(ref tcpsocket) => Ok(tcpsocket.lock().connect(address.into())?), + Socket::Unix(ref unixsocket) => unixsocket.connect(self.clone(), address), + } + } + + /// Returns the locally bound address of the socket. + pub fn local_addr(&self) -> LinuxResult { + match self { + Socket::Udp(udpsocket) => Ok(udpsocket.lock().local_addr()?.into()), + Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().local_addr()?.into()), + Socket::Unix(unixsocket) => unixsocket.local_addr(), + } + } + + /// Returns the address of the connected peer (if connected). + pub fn peer_addr(&self) -> LinuxResult { + match self { + Socket::Udp(udpsocket) => Ok(udpsocket.lock().peer_addr()?.into()), + Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().peer_addr()?.into()), + Socket::Unix(unixsocket) => unixsocket.peer_addr(), + } + } + + /// Sends data on a connected socket. + pub fn send(&self, buf: &[u8], flags: MessageFlags) -> LinuxResult { + self.sendmsg( + &IoVecsInput::from_single_buffer(buf), + None, + &mut Vec::new(), + flags, + ) + } + + /// Sends data to a specific address (mainly for datagram sockets). + pub fn sendto( + &self, + buf: &[u8], + address: SocketAddress, + flags: MessageFlags, + ) -> LinuxResult { + self.sendmsg( + &IoVecsInput::from_single_buffer(buf), + Some(address), + &mut Vec::new(), + flags, + ) + } + + /// Advanced message sending with scatter/gather I/O and control messages. + pub fn sendmsg( + &self, + iovecs: &IoVecsInput, + address: Option, + ancillary_data: &mut Vec, + flags: MessageFlags, + ) -> LinuxResult { + let mut bytes_send = 0; + match self { + Socket::Tcp(tcpsocket) => { + let tcpsocket = tcpsocket.lock(); + if address.is_some() { + // The connection-mode socket was connected already but a recipient was specified. + return Err(LinuxError::EISCONN); + } else { + for buf in iovecs.as_slices() { + bytes_send += tcpsocket.send(buf)?; + } + } + } + Socket::Udp(udpsocket) => { + let udpsocket = udpsocket.lock(); + if let Some(address) = address { + if let SocketAddress::Inet(ipv4_addr) = address { + for buf in iovecs.as_slices() { + bytes_send += udpsocket.send_to(buf, SocketAddr::V4(ipv4_addr))?; + } + } else { + return Err(LinuxError::EAFNOSUPPORT); + } + } else { + for buf in iovecs.as_slices() { + bytes_send += udpsocket.send(buf)?; + } + } + } + Socket::Unix(unixsocket) => { + bytes_send += unixsocket.sendmsg(iovecs, address, ancillary_data, flags)? + } + } + Ok(bytes_send) + } + + /// Receives data from a connected socket. + pub fn recv(&self, buf: &mut [u8], flags: MessageFlags) -> LinuxResult { + match self { + Socket::Udp(udpsocket) => Ok(udpsocket.lock().recv_from(buf).map(|e| e.0)?), + Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().recv(buf, flags)?), + Socket::Unix(unixsocket) => { + let info = unixsocket.recvmsg(&mut IoVecsOutput::from_single_buffer(buf), flags)?; + Ok(info.bytes_read) + } + } + } + + /// Receives data along with the sender's address (mainly for datagram sockets). + pub fn recvfrom( + &self, + buf: &mut [u8], + flags: MessageFlags, + ) -> LinuxResult<(usize, Option)> { + match self { + // diff: must bind before recvfrom + Socket::Udp(udpsocket) => { + let (size, addr) = udpsocket.lock().recv_from(buf)?; + Ok((size, Some(addr.into()))) + } + Socket::Tcp(tcpsocket) => { + let size = tcpsocket.lock().recv(buf, MessageFlags::empty())?; + Ok((size, None)) + } + Socket::Unix(unixsocket) => { + let info = unixsocket.recvmsg(&mut IoVecsOutput::from_single_buffer(buf), flags)?; + Ok((info.bytes_read, info.address)) + } + } + } + + /// Advanced message receiving with scatter/gather I/O and control messages + pub fn recvmsg( + &self, + iovecs: &mut IoVecsOutput, + flags: MessageFlags, + ) -> LinuxResult { + match self { + Socket::Tcp(tcpsocket) => tcpsocket + .lock() + .recvmsg(iovecs, flags) + .map_err(LinuxError::from), + Socket::Udp(udpsocket) => udpsocket + .lock() + .recvmsg(iovecs, flags) + .map_err(LinuxError::from), + Socket::Unix(unixsocket) => unixsocket.recvmsg(iovecs, flags), + } + } + + /// Shuts down part or all of a full-duplex connection. + pub fn shutdown(&self, how: ShutdownFlags) -> LinuxResult { + match self { + Socket::Udp(udpsocket) => { + udpsocket.lock().peer_addr()?; + udpsocket.lock().shutdown()?; + Ok(()) + } + + Socket::Tcp(tcpsocket) => { + tcpsocket.lock().peer_addr()?; + tcpsocket.lock().shutdown()?; + Ok(()) + } + Socket::Unix(unixsocket) => unixsocket.shutdown(how), + } + } +} diff --git a/modules/ruxnet/src/socket_node.rs b/modules/ruxnet/src/socket_node.rs new file mode 100644 index 000000000..2f30920f6 --- /dev/null +++ b/modules/ruxnet/src/socket_node.rs @@ -0,0 +1,95 @@ +/* Copyright (c) [2023] [Syswonder Community] +* [Ruxos] is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ +//! Unix socket node in vfs + +use alloc::sync::{Arc, Weak}; +use axerrno::{ax_err, LinuxError, LinuxResult}; +use axfs_vfs::{ + impl_vfs_non_dir_default, AbsPath, RelPath, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeType, + VfsResult, +}; +use ruxfs::fops::lookup; +use spin::rwlock::RwLock; + +use crate::socket::Socket; + +/// Represents a filesystem node for a UNIX domain socket. +/// This connects the socket abstraction with the VFS (Virtual Filesystem) layer, +/// allowing sockets to be addressed via filesystem paths like `/tmp/sock`. +pub struct SocketNode { + attr: RwLock, + /// Weak reference to the actual socket implementation. + /// Using Weak avoids circular references between the filesystem and socket layers. + bound_socket: Weak, +} + +impl SocketNode { + /// Creates a new socket filesystem node bound to a specific socket. + fn new(socket: Arc) -> Self { + Self { + // FIXME: use a proper inode number + attr: RwLock::new(VfsNodeAttr::new( + u64::MAX, // Placeholder inode number, should be replaced with a proper allocator + VfsNodePerm::default_socket(), + VfsNodeType::Socket, + 0, + 0, + )), + bound_socket: Arc::downgrade(&socket), + } + } + + /// Retrieves the bound socket as a strong reference. + /// Panics if the socket has been dropped (should never happen in correct usage). + pub fn bound_socket(&self) -> Arc { + self.bound_socket.upgrade().unwrap() + } +} + +impl VfsNodeOps for SocketNode { + fn get_attr(&self) -> VfsResult { + Ok(*self.attr.read()) + } + + fn set_mode(&self, mode: VfsNodePerm) -> VfsResult { + self.attr.write().set_perm(mode); + Ok(()) + } + + fn read_at(&self, _offset: u64, _buf: &mut [u8]) -> VfsResult { + ax_err!(Unsupported, "Socket read_at method Unsupported") + } + + fn write_at(&self, _offset: u64, _buf: &[u8]) -> VfsResult { + ax_err!(Unsupported, "Socket write_at method Unsupported") + } + + fn fsync(&self) -> VfsResult { + ax_err!(Unsupported, "Socket fsync method Unsupported") + } + + fn truncate(&self, _size: u64) -> VfsResult { + ax_err!(Unsupported, "Socket truncate method Unsupported") + } + + impl_vfs_non_dir_default! {} +} + +/// Binds a UNIX domain socket to a filesystem path. +pub fn bind_socket_node(socket: Arc, abs_path: &AbsPath) -> LinuxResult { + if let Some((dir_path, name)) = abs_path.rsplit_once('/') { + let dir_node = lookup(&AbsPath::new(dir_path))?; + let socket_node = Arc::new(SocketNode::new(socket)); + dir_node.create_socket_node(&RelPath::new(name), socket_node)?; + Ok(()) + } else { + // A component in the directory prefix of the socket pathname does not exist. + Err(LinuxError::ENOENT) + } +} diff --git a/modules/ruxnet/src/unix.rs b/modules/ruxnet/src/unix.rs new file mode 100644 index 000000000..af84fbd4d --- /dev/null +++ b/modules/ruxnet/src/unix.rs @@ -0,0 +1,490 @@ +/* Copyright (c) [2023] [Syswonder Community] +* [Ruxos] is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ +//! Unix socket implementation +use alloc::collections::vec_deque::VecDeque; +use alloc::sync::Arc; +use alloc::sync::Weak; +use alloc::vec::Vec; +use axerrno::{LinuxError, LinuxResult}; +use axio::PollState; +use spin::mutex::Mutex; + +use core::sync::atomic::{AtomicBool, Ordering}; +use iovec::{IoVecsInput, IoVecsOutput}; + +use crate::address::{resolve_unix_socket_addr, SocketAddress, UnixSocketAddr}; +use crate::message::{ControlMessageData, MessageFlags, MessageQueue, MessageReadInfo}; +use crate::socket::{Socket, SocketType}; +use crate::socket_node::bind_socket_node; +use crate::ShutdownFlags; + +const UNIX_DEFAULT_SIZE: usize = 65536; + +/// Represents a UNIX domain socket implementation. +/// +/// UNIX domain sockets provide inter-process communication on the same host system. +/// They support both stream-oriented (SOCK_STREAM) and datagram (SOCK_DGRAM) semantics. +pub struct UnixSocket { + /// The type of socket (stream or datagram) + socktype: SocketType, + /// Whether the socket is in non-blocking mode + nonblock: AtomicBool, + /// The internal state protected by a mutex + inner: Mutex, +} + +/// Internal state of a UNIX domain socket +pub struct UnixSocketInner { + /// Queue for incoming messages + /// - Stream sockets (preserving message boundaries because of ancillary data) + /// - Datagram sockets (maintaining packet boundaries) + messages: MessageQueue, + /// Local address the socket is bound to (filesystem path for named sockets) + local_address: Option, + /// Address of the connected peer (if connected) + peer_address: Option, + /// Current connection state + state: UnixSocketState, + /// Read shutdown flag (SHUT_RD): + /// - When true: further receives are disallowed + /// - Note: Any remaining data in message queue can still be read + /// - After shutdown: recv() returns EOF (0 bytes) once queue is empty + shutdown_read: bool, + /// Write shutdown flag (SHUT_WR): + /// - When true: further sends are disallowed + /// - Triggers peer_hangup on remote socket + /// - Does not affect already-queued messages + shutdown_write: bool, + /// Peer disconnect notification: + /// - Set when remote end shuts down or closes + /// - Pollers will receive POLLHUP event + /// - Further writes will typically fail with `EPIPE` + peer_hangup: bool, +} + +impl UnixSocketInner { + /// Gets the connected peer socket if available + pub fn peer(&self) -> Option> { + match &self.state { + UnixSocketState::Connected(peer) => peer.upgrade(), + _ => None, + } + } +} + +/// Represents the connection state of a UNIX socket +pub enum UnixSocketState { + /// Disconnected + Disconnected, + /// Listening for incoming connections (stream sockets only) + Listening(AcceptQueue), + /// Connected to a peer socket (using Weak to avoid reference cycles) + Connected(Weak), + /// Socket has been closed + Closed, +} + +/// Queue for pending incoming connections (stream sockets) +pub struct AcceptQueue { + /// Queue of pending connections + sockets: VecDeque>, + /// Maximum number of pending connections allowed + backlog: usize, +} + +impl AcceptQueue { + /// Default backlog size when not specified + const DEFAULT_BACKLOG: usize = 1024; + + /// Creates a new accept queue with specified backlog + fn new(backlog: usize) -> Self { + AcceptQueue { + sockets: VecDeque::with_capacity(backlog), + backlog, + } + } + + fn set_backlog(&mut self, backlog: usize) { + self.backlog = backlog; + } +} + +impl UnixSocket { + /// Creates a new UNIX domain socket + pub fn create_socket(socktype: SocketType, nonblock: bool) -> Arc { + Arc::new(Socket::Unix(UnixSocket { + socktype, + nonblock: AtomicBool::new(nonblock), + inner: Mutex::new(UnixSocketInner { + messages: MessageQueue::new(UNIX_DEFAULT_SIZE), + local_address: None, + peer_address: None, + state: UnixSocketState::Disconnected, + shutdown_read: false, + shutdown_write: false, + peer_hangup: false, + }), + })) + } + + /// Creates a pair of connected UNIX sockets (like pipe()) + pub fn create_socket_pair(socktype: SocketType, nonblock: bool) -> (Arc, Arc) { + let left = Self::create_socket(socktype, nonblock); + let right = Self::create_socket(socktype, nonblock); + { + let mut left_inner = left.as_unix_socket().inner.lock(); + left_inner.state = UnixSocketState::Connected(Arc::downgrade(&right)); + left_inner.local_address = Some(SocketAddress::Unix(UnixSocketAddr::Unamed)); + } + { + let mut right_inner = right.as_unix_socket().inner.lock(); + right_inner.state = UnixSocketState::Connected(Arc::downgrade(&left)); + right_inner.local_address = Some(SocketAddress::Unix(UnixSocketAddr::Unamed)); + } + (left, right) + } + + /// Returns the socket type (stream or datagram) + pub fn socket_type(&self) -> SocketType { + self.socktype + } + + /// Checks if the socket is in non-blocking mode + pub fn is_nonblocking(&self) -> bool { + self.nonblock.load(Ordering::Relaxed) + } + + /// Sets the socket's non-blocking mode + pub fn set_nonblocking(&self, nonblock: bool) { + self.nonblock.store(nonblock, Ordering::Relaxed); + } + + /// Checks if the socket is in listening state + pub fn is_listening(&self) -> bool { + matches!(self.inner.lock().state, UnixSocketState::Listening(_)) + } + + /// Binds the socket to a filesystem path + pub fn bind(&self, self_socket: Arc, address: SocketAddress) -> LinuxResult { + if let SocketAddress::Unix(ref unix_addr) = address { + let mut inner = self.inner.lock(); + // Check if the socket is already bound to an address. + if inner.local_address.is_some() { + return Err(LinuxError::EINVAL); + } + match unix_addr { + UnixSocketAddr::PathName(ref path) => bind_socket_node(self_socket, path)?, + UnixSocketAddr::Unamed => { + unreachable!("won't parse a unix address to Unamed") + } + UnixSocketAddr::Abstract(_) => todo!(), + } + inner.local_address = Some(address); + return Ok(()); + } + Err(LinuxError::EINVAL) + } + + /// Starts listening for incoming connections (stream sockets only) + pub fn listen(&self, backlog: i32) -> LinuxResult { + if self.socktype == SocketType::Datagram { + return Err(LinuxError::EOPNOTSUPP); + } + let mut inner = self.inner.lock(); + let backlog = if backlog < 0 { + AcceptQueue::DEFAULT_BACKLOG + } else { + backlog as usize + }; + let is_bound = inner.local_address.is_some(); + match inner.state { + UnixSocketState::Disconnected if is_bound => { + inner.state = UnixSocketState::Listening(AcceptQueue::new(backlog)); + Ok(()) + } + UnixSocketState::Listening(ref mut accept_queue) => { + accept_queue.set_backlog(backlog); + Ok(()) + } + _ => Err(LinuxError::EINVAL), + } + } + + /// Accepts an incoming connection (stream sockets only) + pub fn accept(&self) -> LinuxResult> { + if self.socktype == SocketType::Datagram { + return Err(LinuxError::EOPNOTSUPP); + } + self.block_on( + || { + let mut inner = self.inner.lock(); + match &mut inner.state { + UnixSocketState::Listening(accept_queue) => { + accept_queue.sockets.pop_front().ok_or(LinuxError::EAGAIN) + } + _ => Err(LinuxError::EINVAL), + } + }, + MessageFlags::empty(), + ) + } + + /// Connects to another UNIX socket + pub fn connect(&self, self_socket: Arc, address: SocketAddress) -> LinuxResult { + let peer_socket = resolve_unix_socket_addr(&address)?; + let peer = peer_socket.as_unix_socket(); + if self.socktype != peer.socktype { + return Err(LinuxError::EPROTOTYPE); + } + let mut self_inner = self.inner.lock(); + self_inner.peer_address = Some(address); + match self.socktype { + SocketType::Datagram => { + self_inner.state = UnixSocketState::Connected(Arc::downgrade(&peer_socket)) + } + SocketType::Stream => { + match self_inner.state { + UnixSocketState::Disconnected => {} + UnixSocketState::Connected(_) => return Err(LinuxError::EISCONN), + _ => return Err(LinuxError::EINVAL), + } + let mut listener = peer.inner.lock(); + let listener_capacity = listener.messages.capacity(); + let listener_address = listener.local_address.clone(); + match listener.state { + UnixSocketState::Listening(ref mut accept_queue) => { + if accept_queue.sockets.len() >= accept_queue.backlog { + return Err(LinuxError::EAGAIN); + } + let new_unix_socket = UnixSocket { + socktype: SocketType::Stream, + nonblock: AtomicBool::new(false), + inner: Mutex::new(UnixSocketInner { + messages: MessageQueue::new(listener_capacity), + local_address: listener_address, + peer_address: Some( + self_inner + .local_address + .clone() + .unwrap_or(SocketAddress::Unix(UnixSocketAddr::default())), + ), + state: UnixSocketState::Connected(Arc::downgrade(&self_socket)), + shutdown_read: false, + shutdown_write: false, + peer_hangup: false, + }), + }; + let new_socket = Arc::new(Socket::Unix(new_unix_socket)); + self_inner.state = UnixSocketState::Connected(Arc::downgrade(&new_socket)); + accept_queue.sockets.push_back(new_socket); + } + _ => return Err(LinuxError::ECONNREFUSED), + } + } + } + Ok(()) + } + + /// If getsockname() is called on an unbound UNIX domain socket, + /// the system will return success, but the `sun_path` in the + /// address structure will be empty (indicating an unbound state). + pub fn local_addr(&self) -> LinuxResult { + let inner = self.inner.lock(); + match &inner.local_address { + Some(address) => Ok(address.clone()), + None => Ok(SocketAddress::Unix(UnixSocketAddr::default())), + } + } + + /// Gets the peer address if connected + pub fn peer_addr(&self) -> LinuxResult { + self.inner + .lock() + .peer_address + .clone() + .ok_or(LinuxError::ENOTCONN) + } + + /// Find peer unix socket and write message to it's `MessageQueue`. + pub fn sendmsg( + &self, + src_data: &IoVecsInput, + dst_address: Option, + ancillary_data: &mut Vec, + flags: MessageFlags, + ) -> LinuxResult { + self.block_on( + || { + let (local_address, connected_peer) = { + let inner = self.inner.lock(); + if inner.shutdown_write { + // The local end has been shut down on a connection oriented socket. + return Err(LinuxError::EPIPE); + } + (inner.local_address.clone(), inner.peer()) + }; + let peer = match (connected_peer, dst_address.as_ref(), self.socktype) { + (None, None, _) => { + // The socket is not connected, and no target has been given. + return Err(LinuxError::ENOTCONN); + } + (None, Some(_), SocketType::Stream) => { + return Err(LinuxError::ENOTCONN); + } + (None, Some(address), SocketType::Datagram) => { + // The unix Dgram socket is not connected, but a target address has been given. + resolve_unix_socket_addr(address)? + } + (Some(peer), None, _) => peer, + (Some(_), Some(_), _) => { + //The connection-mode socket was connected already but a recipient was specified. + return Err(LinuxError::EISCONN); + } + }; + let mut peer_inner = peer.as_unix_socket().inner.lock(); + if self.socktype == SocketType::Stream { + peer_inner + .messages + .write_stream(src_data, local_address, ancillary_data) + } else { + peer_inner + .messages + .write_dgram(src_data, local_address, ancillary_data) + } + }, + flags, + ) + } + + /// Receives a message from the socket + pub fn recvmsg( + &self, + dst_data: &mut IoVecsOutput, + flags: MessageFlags, + ) -> LinuxResult { + self.block_on( + || { + let mut inner = self.inner.lock(); + let info = match self.socktype { + SocketType::Stream => { + if dst_data.avaliable() == 0 { + Ok(MessageReadInfo::default()) + } else if flags.contains(MessageFlags::MSG_PEEK) { + inner.messages.peek_stream(dst_data) + } else { + inner.messages.read_stream(dst_data) + } + } + SocketType::Datagram => { + if flags.contains(MessageFlags::MSG_PEEK) { + inner.messages.peek_dgram(dst_data) + } else { + inner.messages.read_dgram(dst_data) + } + } + }?; + // Unix domain sockets can send empty messages, so we need to check if the read bytes are zero with address + if info.bytes_read == 0 && !inner.shutdown_read && info.address.is_none() { + return Err(LinuxError::EAGAIN); + } + Ok(info) + }, + flags, + ) + } + + /// Checks the socket's I/O readiness state + pub fn poll(&self) -> LinuxResult { + let inner = self.inner.lock(); + match self.socktype { + SocketType::Stream => match inner.state { + UnixSocketState::Disconnected => Ok(PollState::default()), + UnixSocketState::Listening(ref accept_queue) => { + let readable = accept_queue.sockets.is_empty(); + Ok(PollState { + readable, + writable: false, + pollhup: false, + }) + } + UnixSocketState::Connected(_) => { + let readable = !inner.messages.is_empty(); + let writable = inner.messages.available_capacity() > 0 && !inner.shutdown_write; + Ok(PollState { + readable, + writable, + pollhup: inner.peer_hangup, + }) + } + UnixSocketState::Closed => { + let readable = !inner.messages.is_empty(); + Ok(PollState { + readable, + writable: false, + pollhup: inner.peer_hangup, + }) + } + }, + SocketType::Datagram => { + let readable = !inner.messages.is_empty(); + let writable = inner.messages.available_capacity() > 0 && !inner.shutdown_write; + Ok(PollState { + readable, + writable, + pollhup: inner.peer_hangup, + }) + } + } + } + + /// Shuts down part or all of the socket connection + pub fn shutdown(&self, how: ShutdownFlags) -> LinuxResult { + let mut inner = self.inner.lock(); + let peer = inner.peer().ok_or(LinuxError::ENOTCONN)?; + let mut peer_inner = peer.as_unix_socket().inner.lock(); + if how.contains(ShutdownFlags::WRITE) { + inner.shutdown_write = true; + peer_inner.peer_hangup = true; + } + if how.contains(ShutdownFlags::READ) { + inner.shutdown_read = true; + } + Ok(()) + } + + /// Helper for blocking/non-blocking operations + fn block_on(&self, mut f: F, flags: MessageFlags) -> LinuxResult + where + F: FnMut() -> LinuxResult, + { + if flags.contains(MessageFlags::MSG_DONTWAIT) || self.is_nonblocking() { + return f(); + } + loop { + let res = f(); + match res { + Ok(t) => return Ok(t), + Err(LinuxError::EAGAIN) => ruxtask::yield_now(), + Err(e) => return Err(e), + } + } + } +} + +impl Drop for UnixSocket { + fn drop(&mut self) { + if let UnixSocketState::Connected(ref peer) = self.inner.lock().state { + if let Some(peer_socket) = peer.upgrade() { + let mut peer_inner = peer_socket.as_unix_socket().inner.lock(); + peer_inner.state = UnixSocketState::Closed; + peer_inner.peer_hangup = true; + } + } + } +} diff --git a/modules/ruxrand/Cargo.toml b/modules/ruxrand/Cargo.toml new file mode 100644 index 000000000..2f5c2e9cd --- /dev/null +++ b/modules/ruxrand/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "ruxrand" +version = "0.1.0" +edition = "2021" +authors = ["Sssssalty Fish "] +description = "RNG support for RuxOS" +license = "GPL-3.0-or-later OR Apache-2.0" +homepage = "https://github.com/syswonder/ruxos" +repository = "https://github.com/rcore-os/arceos/tree/main/modules/ruxrand" + +[features] +default = [] + +easy-spin = [] + +[dependencies] +crate_interface = "0.1.1" +rand = { version = "0.8.5", default-features = false } +rand_xoshiro = { version = "0.6.0", default-features = false } + +spinlock = { version = "0.1.0", path = "../../crates/spinlock" } +percpu = "0.2" +lazy_init = { version = "0.1.0", path = "../../crates/lazy_init", default-features = false } + +[dev-dependencies] +rand = { version = "0.8.5" } diff --git a/modules/ruxrand/src/lib.rs b/modules/ruxrand/src/lib.rs new file mode 100644 index 000000000..b174e3d68 --- /dev/null +++ b/modules/ruxrand/src/lib.rs @@ -0,0 +1,39 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! Runtime library of [Ruxos](https://github.com/syswonder/ruxos). +//! +//! This module provides the implementation of kernel-level random +//! number generators (RNGs), especially the per-CPU RNG type. It also +//! enables the usage of random exponential backoff strategy in spinlocks. +//! +//! # Cargo Features +//! +//! - `easy-spin`: Use a alternate, extremely simple RNG for backoff in +//! spinlocks, instead of the default per-CPU RNG. This may increase +//! performance when the lock is not highly contended. +//! +//! All the features are optional and disabled by default. + +#![cfg_attr(not(test), no_std)] +#![feature(doc_cfg)] +#![feature(doc_auto_cfg)] +#![allow(dead_code)] +/// Defines the per-CPU RNG. +pub mod rng; + +mod spin_rand; + +pub use rng::{percpu_rng, random, PercpuRng}; +pub use spin_rand::ExpRand; + +/// Initializes the per-CPU RNGs on the given CPU. +pub fn init(cpuid: usize) { + rng::init(cpuid); +} diff --git a/modules/ruxrand/src/rng.rs b/modules/ruxrand/src/rng.rs new file mode 100644 index 000000000..bbd40a60e --- /dev/null +++ b/modules/ruxrand/src/rng.rs @@ -0,0 +1,157 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use lazy_init::LazyInit; +use percpu::def_percpu; +use rand::{distributions::Standard, prelude::*}; +use rand_xoshiro::Xoshiro256StarStar; + +#[allow(clippy::unusual_byte_groupings)] +const RNG_SEED: u64 = 0xBAD_C0FFEE_0DD_F00D; + +type PercpuRngType = Xoshiro256StarStar; + +#[def_percpu] +pub(crate) static PERCPU_RNG: LazyInit = LazyInit::new(); + +/// Initializes the per-CPU random number generator (RNG). +/// +/// This function seeds the RNG with a hard-coded seed value and then performs a +/// series of long jumps so that the random number sequence on each CPU is guaranteed to +/// not overlap with each other. +/// +/// A single [`PercpuRngType::long_jump`] skips 2^192 random numbers, providing sufficient +/// space for randomness on each CPU. +pub(crate) fn init(cpuid: usize) { + PERCPU_RNG.with_current(|percpu_ref| { + let mut rng = PercpuRngType::seed_from_u64(RNG_SEED); + for _ in 0..cpuid { + rng.long_jump(); + } + + percpu_ref.init_by(rng); + }); +} + +// Rationale for using a raw pointer in `PercpuRng`: +// +// Just like the case in `rand::thread_rng`, there will only +// ever be one mutable reference generated from the mutable pointer, because +// we only have such a reference inside `next_u32`, `next_u64`, etc. Within a +// single processor (which is the definition of `PercpuRng`), there will only ever +// be one of these methods active at a time. +// +// A possible scenario where there could be multiple mutable references is if +// `PercpuRng` is used inside `next_u32` and co. But the implementation is +// completely under our control. We just have to ensure none of them use +// `PercpuRng` internally, which is nonsensical anyway. We should also never run +// `PercpuRng` in destructors of its implementation, which is also nonsensical. +// +// Another possible scenario is that an interrupt happens at the middle of `next_u32` +// or so, and the interrupt handler uses `PercpuRng`. This is indeed a violation of the +// Rust aliasing model, but can hardly lead to any true hazard I think. It can be easily +// fixed by requiring no IRQ using `kernel_guard` when implementing the functions provided +// by `RngCore`. + +/// A RNG wrapper that's local to the calling CPU. The actual RNG type is +/// `PercpuRngType`, which is currently [`Xoshiro256StarStar`]. +/// +/// This type is ! [`Send`] and ! [`Sync`], preventing potential misuse under +/// SMP environments. Construct this type using [`Default::default`], or just +/// a call to [`percpu_rng`]. +#[derive(Clone, Debug)] +pub struct PercpuRng { + rng: *mut PercpuRngType, +} + +impl PercpuRng { + fn get_rng(&mut self) -> &mut PercpuRngType { + unsafe { &mut *self.rng } + } +} + +impl Default for PercpuRng { + fn default() -> Self { + percpu_rng() + } +} + +impl RngCore for PercpuRng { + #[inline(always)] + fn next_u32(&mut self) -> u32 { + self.get_rng().next_u32() + } + + #[inline(always)] + fn next_u64(&mut self) -> u64 { + self.get_rng().next_u64() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.get_rng().fill_bytes(dest) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + self.get_rng().try_fill_bytes(dest) + } +} + +impl CryptoRng for PercpuRng {} + +/// Retrieves the per-CPU RNG [`PercpuRng`] that points to the CPU-local RNG states. +/// +/// The RNGs were initialized by the same seed, but jumped to different locations in +/// the pseudo-random sequence, effectively making the RNG independently and identically +/// distributed on all CPUs. +pub fn percpu_rng() -> PercpuRng { + // It is unsafe to return mutable pointer to a global data structure + // without preemption disabled, but the baddest thing that can happen whatsoever + // here is the rng being put into some "random" state, which I think is not fatal. + let rng = unsafe { PERCPU_RNG.current_ref_mut_raw().get_mut_unchecked() as *mut _ }; + PercpuRng { rng } +} + +/// Generates a random value using the per-CPU random number generator. +/// +/// This is simply a shortcut for `percpu_rng().gen()`. See [`percpu_rng`] for +/// documentation of the entropy source and [`Standard`] for documentation of +/// distributions and type-specific generation. +/// +/// # Provided implementations +/// +/// The following types have provided implementations that +/// generate values with the following ranges and distributions: +/// +/// * Integers (`i32`, `u32`, `isize`, `usize`, etc.): Uniformly distributed +/// over all values of the type. +/// * `char`: Uniformly distributed over all Unicode scalar values, i.e. all +/// code points in the range `0...0x10_FFFF`, except for the range +/// `0xD800...0xDFFF` (the surrogate code points). This includes +/// unassigned/reserved code points. +/// * `bool`: Generates `false` or `true`, each with probability 0.5. +/// * Floating point types (`f32` and `f64`): Uniformly distributed in the +/// half-open range `[0, 1)`. See notes below. +/// * Wrapping integers (`Wrapping`), besides the type identical to their +/// normal integer variants. +/// +/// Also supported is the generation of the following +/// compound types where all component types are supported: +/// +/// * Tuples (up to 12 elements): each element is generated sequentially. +/// * Arrays (up to 32 elements): each element is generated sequentially; +/// see also [`Rng::fill`] which supports arbitrary array length for integer +/// types and tends to be faster for `u32` and smaller types. +/// * `Option` first generates a `bool`, and if true generates and returns +/// `Some(value)` where `value: T`, otherwise returning `None`. +pub fn random() -> T +where + Standard: Distribution, +{ + percpu_rng().gen() +} diff --git a/modules/ruxrand/src/spin_rand.rs b/modules/ruxrand/src/spin_rand.rs new file mode 100644 index 000000000..0d39d0aa0 --- /dev/null +++ b/modules/ruxrand/src/spin_rand.rs @@ -0,0 +1,109 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use core::{ + fmt::Debug, + sync::atomic::{AtomicUsize, Ordering}, +}; + +use rand::RngCore; +use spinlock::{Backoff, Relax}; + +#[cfg(feature = "easy-spin")] +type SpinRng = EasyRng; + +#[cfg(not(feature = "easy-spin"))] +type SpinRng = crate::rng::PercpuRng; + +#[inline(always)] +fn exp_rand_backoff(current_limit: &mut u32, max: u32) { + let limit = *current_limit; + *current_limit = max.max(limit); + + let mut rng = SpinRng::default(); + // It is more "correct" to use `rng.gen_range(0..limit)`, + // but since `limit` would only be powers of two, a simple + // modulo would also keep the distribution uniform as long + // as `rng.next_u32()` keeps a uniform distribution on `u32`. + let delay = rng.next_u32() % limit; + for _ in 0..delay { + core::hint::spin_loop(); + } +} + +/// Calls [`core::hint::spin_loop`] random times within an exponentially grown limit +/// when backoff/relax is required. The random number is generated using [`RngCore::next_u32`], +/// and the actual rng used is controlled by the `easy-spin` feature. +/// +/// This would generally increase performance when the lock is highly contended. +#[derive(Debug)] +pub struct ExpRand(u32); + +impl Relax for ExpRand { + #[inline(always)] + fn relax(&mut self) { + exp_rand_backoff(&mut self.0, N); + } +} + +impl Backoff for ExpRand { + #[inline(always)] + fn backoff(&mut self) { + exp_rand_backoff(&mut self.0, N); + } +} + +impl Default for ExpRand { + #[inline(always)] + fn default() -> Self { + Self(1) + } +} + +#[derive(Clone, Default)] +struct EasyRng; + +impl Debug for EasyRng { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let state = EASY_RNG_STATE.load(Ordering::Relaxed); + f.debug_struct("EasyRng").field("state", &state).finish() + } +} + +impl RngCore for EasyRng { + fn next_u32(&mut self) -> u32 { + easy_rng() + } + + fn next_u64(&mut self) -> u64 { + easy_rng() as _ + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + dest.fill_with(|| easy_rng() as _) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + dest.fill_with(|| easy_rng() as _); + Ok(()) + } +} + +static EASY_RNG_STATE: AtomicUsize = AtomicUsize::new(0); + +fn easy_rng() -> u32 { + const RANDOM_RANDOM_LIST: [u8; 64] = [ + 9, 7, 13, 0, 15, 2, 14, 1, 14, 14, 11, 3, 13, 11, 12, 10, 3, 6, 8, 1, 2, 0, 12, 12, 13, 2, + 9, 5, 3, 10, 6, 1, 15, 9, 6, 12, 9, 7, 4, 7, 4, 8, 11, 7, 0, 1, 2, 10, 15, 6, 5, 3, 0, 5, + 14, 4, 4, 13, 15, 8, 5, 10, 8, 11, + ]; + + let idx = EASY_RNG_STATE.fetch_add(1, Ordering::Relaxed) % RANDOM_RANDOM_LIST.len(); + RANDOM_RANDOM_LIST[idx] as _ +} diff --git a/modules/ruxruntime/Cargo.toml b/modules/ruxruntime/Cargo.toml index 0555efbaf..40f172483 100644 --- a/modules/ruxruntime/Cargo.toml +++ b/modules/ruxruntime/Cargo.toml @@ -17,43 +17,64 @@ repository = "https://github.com/syswonder/ruxos/tree/main/modules/ruxruntime" default = [] smp = ["ruxhal/smp"] -irq = ["ruxhal/irq", "ruxtask?/irq", "percpu", "kernel_guard"] +irq = ["ruxhal/irq", "ruxtask?/irq", "kernel_guard"] tls = ["ruxhal/tls", "ruxtask?/tls"] -alloc = ["axalloc", "dtb"] -paging = ["ruxhal/paging", "lazy_init"] +alloc = ["axalloc", "dtb", "ruxdtb"] +paging = ["ruxhal/paging", "ruxmm/paging", "lazy_init", "ruxtask/paging"] rtc = ["ruxhal/rtc"] +virtio_console = [] +gic-v3 = ["ruxhal/gic-v3"] -multitask = ["ruxtask/multitask", "dep:ruxfutex"] -fs = ["ruxdriver", "ruxfs"] +multitask = ["ruxtask/multitask", "dep:ruxfutex", "rand"] +rand = ["dep:ruxrand"] +fs = ["ruxdriver", "ruxfs", "ruxtask/fs"] blkfs = ["fs"] virtio-9p = ["fs", "rux9p"] net-9p = ["fs", "rux9p"] +fusefs = ["ruxdriver/virtio-blk"] net = ["ruxdriver", "ruxnet"] display = ["ruxdriver", "ruxdisplay"] -signal = [] +signal = ["ruxhal/signal", "ruxtask/signal"] musl = ["dep:ruxfutex"] +# for testing +myfs = ["fs", "multitask", "alloc", "ruxfs/myfs", "ruxtask/test"] +fatfs = ["fs", "multitask", "alloc", "blkfs", "ruxfs/fatfs", "ruxtask/test"] [dependencies] cfg-if = "1.0" ruxhal = { path = "../ruxhal" } -axlog = { path = "../axlog" } +axlog = { path = "../../crates/axlog" } ruxconfig = { path = "../ruxconfig" } axalloc = { path = "../axalloc", optional = true } ruxdriver = { path = "../ruxdriver", optional = true } ruxfs = { path = "../ruxfs", optional = true } rux9p = { path = "../rux9p", optional = true } +ruxvda = { path = "../ruxvda" } ruxnet = { path = "../ruxnet", optional = true } ruxdisplay = { path = "../ruxdisplay", optional = true } ruxtask = { path = "../ruxtask", optional = true } +ruxmm = { path = "../ruxmm" } axsync = { path = "../axsync", optional = true } ruxfutex = { path = "../ruxfutex", optional = true } - +ruxdtb = { path = "../ruxdtb", optional = true } +ruxrand = { path = "../ruxrand", optional = true } +tty = { path = "../../crates/tty" } crate_interface = "0.1.1" -percpu = { path = "../../crates/percpu", optional = true } +percpu = "0.2" kernel_guard = { version = "0.1.0", optional = true } lazy_init = { path = "../../crates/lazy_init", optional = true } dtb = { path = "../../crates/dtb", optional = true } -tty = { path = "../../crates/tty", optional = true } + +[dev-dependencies] +log = "0.4" +axlog = { path = "../../crates/axlog", features = ["std"] } +ruxdriver = { path = "../ruxdriver", features = ["block", "ramdisk", "dyn"] } +driver_block = { path = "../../crates/driver_block", features = ["ramdisk"] } +axio = { path = "../../crates/axio", features = ["alloc"] } +ruxfs = { path = "../ruxfs" } +ruxtask = { path = "../ruxtask" } +axfs_vfs = { path = "../../crates/axfs_vfs" } +axfs_ramfs = { path = "../../crates/axfs_ramfs" } diff --git a/modules/ruxfs/resources/create_test_img.sh b/modules/ruxruntime/resources/create_test_img.sh similarity index 100% rename from modules/ruxfs/resources/create_test_img.sh rename to modules/ruxruntime/resources/create_test_img.sh diff --git a/modules/ruxfs/resources/fat16.img b/modules/ruxruntime/resources/fat16.img similarity index 100% rename from modules/ruxfs/resources/fat16.img rename to modules/ruxruntime/resources/fat16.img diff --git a/modules/ruxruntime/src/env.rs b/modules/ruxruntime/src/env.rs index be74823de..c96e0ba36 100644 --- a/modules/ruxruntime/src/env.rs +++ b/modules/ruxruntime/src/env.rs @@ -6,10 +6,14 @@ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PSL v2 for more details. */ + +// TODO: rewrite this file and remove this allowance +#![allow(static_mut_refs)] + extern crate alloc; use alloc::vec::Vec; use core::ffi::c_char; -use core::{ptr, usize}; +use core::ptr; use ruxhal::mem::PAGE_SIZE_4K; pub const AT_PAGESIZE: usize = 6; @@ -35,7 +39,7 @@ pub(crate) unsafe fn init_argv(args: Vec<&str>) { let arg = arg.as_ptr(); let buf = buf_alloc(len + 1); for i in 0..len { - *buf.add(i) = *arg.add(i) as i8; + *buf.add(i) = *arg.add(i) as c_char; } *buf.add(len) = 0; RUX_ARGV.push(buf); @@ -87,7 +91,7 @@ unsafe fn buf_alloc(size: usize) -> *mut c_char { } pub(crate) fn boot_add_environ(env: &str) { - let ptr = env.as_ptr() as *const i8; + let ptr = env.as_ptr() as *const c_char; let size = env.len() + 1; if size == 1 { return; diff --git a/modules/ruxruntime/src/lang_items.rs b/modules/ruxruntime/src/lang_items.rs index d17bd762c..57a3d16ec 100644 --- a/modules/ruxruntime/src/lang_items.rs +++ b/modules/ruxruntime/src/lang_items.rs @@ -11,6 +11,6 @@ use core::panic::PanicInfo; #[panic_handler] fn panic(info: &PanicInfo) -> ! { - error!("{}", info); + error!("{info}"); ruxhal::misc::terminate() } diff --git a/modules/ruxruntime/src/lib.rs b/modules/ruxruntime/src/lib.rs index f666eb5a6..a7554df69 100644 --- a/modules/ruxruntime/src/lib.rs +++ b/modules/ruxruntime/src/lib.rs @@ -33,14 +33,13 @@ #![cfg_attr(not(test), no_std)] #![feature(doc_auto_cfg)] +#![allow(static_mut_refs)] #[macro_use] extern crate axlog; #[cfg(all(target_os = "none", not(test)))] mod lang_items; -#[cfg(feature = "signal")] -mod signal; #[cfg(not(feature = "musl"))] mod trap; @@ -52,7 +51,7 @@ mod mp; pub use self::mp::rust_main_secondary; #[cfg(feature = "signal")] -pub use self::signal::{rx_sigaction, Signal}; +use ruxtask::signal::Signal; #[cfg(feature = "alloc")] extern crate alloc; @@ -65,14 +64,14 @@ use self::env::{boot_add_environ, init_argv}; use core::ffi::{c_char, c_int}; const LOGO: &str = r#" -8888888b. .d88888b. .d8888b. -888 Y88b d88P" "Y88b d88P Y88b -888 888 888 888 Y88b. -888 d88P 888 888 888 888 888 888 "Y888b. -8888888P" 888 888 `Y8bd8P' 888 888 "Y88b. -888 T88b 888 888 X88K 888 888 "888 -888 T88b Y88b 888 .d8""8b. Y88b. .d88P Y88b d88P -888 T88b "Y88888 888 888 "Y88888P" "Y8888P" +8888888b. .d88888b. .d8888b. +888 Y88b d88P" "Y88b d88P Y88b +888 888 888 888 Y88b. +888 d88P 888 888 888 888 888 888 "Y888b. +8888888P" 888 888 `Y8bd8P' 888 888 "Y88b. +888 T88b 888 888 X88K 888 888 "888 +888 T88b Y88b 888 .d8""8b. Y88b. .d88P Y88b d88P +888 T88b "Y88888 888 888 "Y88888P" "Y8888P" "#; #[no_mangle] @@ -173,7 +172,11 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { axlog::init(); axlog::set_max_level(option_env!("RUX_LOG").unwrap_or("")); // no effect if set `log-level-*` features info!("Logging is enabled."); - info!("Primary CPU {} started, dtb = {:#x}.", cpu_id, dtb); + + #[cfg(feature = "alloc")] + init_allocator(); + + info!("Primary CPU {cpu_id} started, dtb = {dtb:#x}."); info!("Found physcial memory regions:"); for r in ruxhal::mem::memory_regions() { @@ -186,20 +189,14 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { ); } - #[cfg(feature = "alloc")] - init_allocator(); + tty::init_tty(); - #[cfg(feature = "paging")] - { - info!("Initialize kernel page table..."); - remap_kernel_memory().expect("remap kernel memoy failed"); - } + info!("Initialize platform devices..."); - #[cfg(feature = "tty")] - tty::init(); + ruxhal::platform_init(cpu_id); - info!("Initialize platform devices..."); - ruxhal::platform_init(); + #[cfg(feature = "rand")] + ruxrand::init(cpu_id); #[cfg(feature = "multitask")] { @@ -208,6 +205,15 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { ruxfutex::init_futex(); } + #[cfg(feature = "paging")] + { + info!("Initialize kernel page table..."); + remap_kernel_memory().expect("remap kernel memoy failed"); + } + + #[cfg(feature = "virtio_console")] + ruxhal::virtio::virtio_console::directional_probing(); + #[cfg(any(feature = "fs", feature = "net", feature = "display"))] { #[allow(unused_variables)] @@ -221,7 +227,9 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { extern crate alloc; use alloc::vec::Vec; // By default, mount_points[0] will be rootfs - let mut mount_points: Vec = Vec::new(); + let mut mount_points: Vec = Vec::new(); + #[cfg(any(feature = "blkfs", feature = "fusefs"))] + let mut block_dev = Some(all_devices.block); //setup ramfs as rootfs if no other filesystem can be mounted #[cfg(not(any(feature = "blkfs", feature = "virtio-9p", feature = "net-9p")))] @@ -229,7 +237,7 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { // setup and initialize blkfs as one mountpoint for rootfs #[cfg(feature = "blkfs")] - mount_points.push(ruxfs::init_blkfs(all_devices.block)); + mount_points.push(ruxfs::init_blkfs(block_dev.take().unwrap())); // setup and initialize 9pfs as mountpoint #[cfg(feature = "virtio-9p")] @@ -244,10 +252,17 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { option_env!("RUX_ANAME_9P").unwrap_or(""), option_env!("RUX_PROTOCOL_9P").unwrap_or(""), )); + + // setup and initialize fusefs as mountpoint + #[cfg(feature = "fusefs")] + if let Some(block_dev) = block_dev.take() { + mount_points.push(ruxvda::init_vdafs(block_dev)); + } + ruxfs::prepare_commonfs(&mut mount_points); // setup and initialize rootfs - ruxfs::init_filesystems(mount_points); + ruxtask::fs::init_rootfs(mount_points); } #[cfg(feature = "display")] @@ -269,7 +284,7 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { init_tls(); } - info!("Primary CPU {} init OK.", cpu_id); + info!("Primary CPU {cpu_id} init OK."); INITED_CPUS.fetch_add(1, Ordering::Relaxed); while !is_init_ok() { @@ -281,6 +296,11 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { unsafe { let mut argc: c_int = 0; init_cmdline(&mut argc); + #[cfg(feature = "alloc")] + { + let (mem_base, mem_size) = ruxdtb::get_memory_info(); + info!("memory base: 0x{mem_base:x}, memory size: 0x{mem_size:x}"); + } #[cfg(not(feature = "musl"))] main(argc, argv); #[cfg(feature = "musl")] @@ -391,7 +411,7 @@ fn init_allocator() { } #[cfg(feature = "paging")] -use ruxhal::paging::remap_kernel_memory; +use ruxmm::paging::remap_kernel_memory; #[cfg(feature = "irq")] fn init_interrupt() { @@ -419,7 +439,7 @@ fn init_interrupt() { fn do_signal() { let now_ns = ruxhal::time::current_time_nanos(); // timer signal num - let timers = [14, 26, 27]; + let timers = [14, 26, 27]; // what is the number? for (which, timer) in timers.iter().enumerate() { let mut ddl = Signal::timer_deadline(which, None).unwrap(); let interval = Signal::timer_interval(which, None).unwrap(); @@ -438,8 +458,9 @@ fn init_interrupt() { if signal & (1 << signum) != 0 /* TODO: && support mask */ { - Signal::sigaction(signum as u8, None, None); Signal::signal(signum as i8, false); + Signal::sigaction(signum as u8, None, None); + Signal::signal_handle(signum as u8); } } } diff --git a/modules/ruxruntime/src/mp.rs b/modules/ruxruntime/src/mp.rs index 7b6025541..9c27e30ce 100644 --- a/modules/ruxruntime/src/mp.rs +++ b/modules/ruxruntime/src/mp.rs @@ -24,7 +24,7 @@ pub fn start_secondary_cpus(primary_cpu_id: usize) { SECONDARY_BOOT_STACK[logic_cpu_id].as_ptr_range().end as usize })); - debug!("starting CPU {}...", i); + debug!("starting CPU {i}..."); ruxhal::mp::start_secondary_cpu(i, stack_top); logic_cpu_id += 1; @@ -41,17 +41,20 @@ pub fn start_secondary_cpus(primary_cpu_id: usize) { #[no_mangle] pub extern "C" fn rust_main_secondary(cpu_id: usize) -> ! { ENTERED_CPUS.fetch_add(1, Ordering::Relaxed); - info!("Secondary CPU {:x} started.", cpu_id); - - #[cfg(feature = "paging")] - super::remap_kernel_memory().unwrap(); + info!("Secondary CPU {cpu_id:x} started."); ruxhal::platform_init_secondary(); + #[cfg(feature = "rand")] + ruxrand::init(cpu_id); + #[cfg(feature = "multitask")] ruxtask::init_scheduler_secondary(); - info!("Secondary CPU {:x} init OK.", cpu_id); + #[cfg(feature = "paging")] + super::remap_kernel_memory().unwrap(); + + info!("Secondary CPU {cpu_id:x} init OK."); super::INITED_CPUS.fetch_add(1, Ordering::Relaxed); while !super::is_init_ok() { diff --git a/modules/ruxruntime/tests/test_common/mod.rs b/modules/ruxruntime/tests/test_common/mod.rs new file mode 100644 index 000000000..52b216610 --- /dev/null +++ b/modules/ruxruntime/tests/test_common/mod.rs @@ -0,0 +1,323 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use axio as io; +use axio::Read; +use ruxfs::{api as fs, AbsPath, OpenFlags}; + +use fs::{File, FileType}; +use io::{prelude::*, Error, Result}; + +pub(super) fn open_file_create_new(path: &AbsPath) -> io::Result { + fs::open_file(path, OpenFlags::O_RDWR | OpenFlags::CREATE_NEW) +} + +macro_rules! assert_err { + ($expr: expr) => { + assert!(($expr).is_err()) + }; + ($expr: expr, $err: ident) => { + assert_eq!(($expr).err(), Some(Error::$err)) + }; +} + +fn test_read_write_file() -> Result<()> { + let fname = fs::absolute_path("///very/long//.././long//./path/./test.txt")?; + println!("read and write file {fname:?}:"); + + // read and write + let mut file = fs::open_file(&fname, OpenFlags::O_RDWR)?; + let file_size = file.get_attr()?.size(); + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + print!("{contents}"); + assert_eq!(contents.len(), file_size as usize); + assert_eq!(file.write(b"Hello, world!\n")?, 14); // append + drop(file); + + // read again and check + let new_contents = fs::read_to_string(&fname)?; + print!("{new_contents}"); + assert_eq!(new_contents, contents + "Hello, world!\n"); + + // append and check + let mut file = fs::open_file(&fname, OpenFlags::O_WRONLY | OpenFlags::O_APPEND)?; + assert_eq!(file.write(b"new line\n")?, 9); + drop(file); + + let new_contents2 = fs::read_to_string(&fname)?; + print!("{new_contents2}"); + assert_eq!(new_contents2, new_contents + "new line\n"); + + // open a non-exist file + assert_err!( + fs::open_file(&AbsPath::new("/not/exist/file"), OpenFlags::O_RDONLY), + NotFound + ); + + println!("test_read_write_file() OK!"); + Ok(()) +} + +fn test_read_dir() -> Result<()> { + let dir = fs::absolute_path("/././//./")?; + println!("list directory {dir:?}:"); + for entry in fs::open_dir(&dir, OpenFlags::empty())? { + let entry = entry?; + println!(" {}", entry.name_as_string()); + } + println!("test_read_dir() OK!"); + Ok(()) +} + +fn test_file_permission() -> Result<()> { + let fname = fs::absolute_path("./short.txt")?; + println!("test permission {fname:?}:"); + + // write a file that open with read-only mode + let mut buf = [0; 256]; + let mut file = fs::open_file(&fname, OpenFlags::O_RDONLY)?; + let n = file.read(&mut buf)?; + assert_err!(file.write(&buf), PermissionDenied); + drop(file); + + // read a file that open with write-only mode + let mut file = fs::open_file( + &fname, + OpenFlags::O_WRONLY | OpenFlags::O_CREAT | OpenFlags::O_TRUNC, + )?; + println!("1"); + assert_err!(file.read(&mut buf), PermissionDenied); + assert!(file.write(&buf[..n]).is_ok()); + drop(file); + + // open with empty options + // assert_err!(open_file(&fname, OpenFlags::empty()), InvalidInput); + + // read as a directory + assert_err!(fs::open_dir(&fname, OpenFlags::empty()), NotADirectory); + + println!("test_file_permisson() OK!"); + Ok(()) +} + +fn test_create_file_dir() -> Result<()> { + // create a file and test existence + let fname = fs::absolute_path("././/very-long-dir-name/..///new-file.txt")?; + println!("test create file {fname:?}:"); + assert_err!(fs::get_attr(&fname), NotFound); + let contents = "create a new file!\n"; + fs::write(&fname, contents)?; + + let dirents = fs::open_dir(&fs::absolute_path(".")?, OpenFlags::empty())? + .map(|e| e.unwrap().name_as_string()) + .collect::>(); + println!("dirents = {dirents:?}"); + assert!(dirents.contains(&"new-file.txt".into())); + assert_eq!(fs::read_to_string(&fname)?, contents); + assert_err!(open_file_create_new(&fname), AlreadyExists); + + // create a directory and test existence + let dirname = fs::absolute_path("///././/very//.//long/./new-dir")?; + println!("test create dir {dirname:?}:"); + assert_err!(fs::get_attr(&dirname), NotFound); + fs::create_dir(&dirname)?; + + let dirents = fs::open_dir(&fs::absolute_path("./very/long")?, OpenFlags::empty())? + .map(|e| e.unwrap().name_as_string()) + .collect::>(); + println!("dirents = {dirents:?}"); + assert!(dirents.contains(&"new-dir".into())); + assert!(fs::get_attr(&dirname)?.is_dir()); + assert_err!(fs::create_dir(&dirname), AlreadyExists); + + println!("test_create_file_dir() OK!"); + Ok(()) +} + +fn test_remove_file_dir() -> Result<()> { + // remove a file and test existence + let fname = fs::absolute_path("//very-long-dir-name/..///new-file.txt")?; + println!("test remove file {fname:?}:"); + assert_err!(fs::remove_dir(&fname), NotADirectory); + assert!(fs::remove_file(&fname).is_ok()); + assert_err!(fs::get_attr(&fname), NotFound); + assert_err!(fs::remove_file(&fname), NotFound); + + // remove a directory and test existence + let dirname = fs::absolute_path("very//.//long/../long/.//./new-dir////")?; + println!("test remove dir {dirname:?}:"); + assert_err!(fs::remove_file(&dirname), IsADirectory); + assert!(fs::remove_dir(&dirname).is_ok()); + assert_err!(fs::get_attr(&dirname), NotFound); + assert_err!(fs::remove_dir(&fname), NotFound); + + // error cases + assert_err!(fs::remove_dir(&fs::absolute_path("/")?), DirectoryNotEmpty); + assert_err!( + fs::remove_file(&fs::absolute_path("///very/./")?), + IsADirectory + ); + assert_err!( + fs::remove_dir(&fs::absolute_path("/./very///")?), + DirectoryNotEmpty + ); + + println!("test_remove_file_dir() OK!"); + Ok(()) +} + +fn test_devfs_ramfs() -> Result<()> { + const N: usize = 32; + let mut buf = [1; N]; + + // list '/' and check if /dev and /tmp exist + let dirents = fs::open_dir(&fs::absolute_path("././//.//")?, OpenFlags::empty())? + .map(|e| e.unwrap().name_as_string()) + .collect::>(); + assert!(dirents.contains(&"dev".into())); + assert!(dirents.contains(&"tmp".into())); + + // read and write /dev/null + let mut file = fs::open_file(&fs::absolute_path("/dev/./null")?, OpenFlags::O_RDWR)?; + assert_eq!(file.read_to_end(&mut Vec::new())?, 0); + assert_eq!(file.write(&buf)?, N); + assert_eq!(buf, [1; N]); + + // read and write /dev/zero + let mut file = fs::open_file( + &fs::absolute_path("////dev/zero")?, + OpenFlags::O_RDWR | OpenFlags::O_CREAT | OpenFlags::O_TRUNC, + )?; + assert_eq!(file.read(&mut buf)?, N); + assert!(file.write_all(&buf).is_ok()); + assert_eq!(buf, [0; N]); + + // list /dev + let dirents = fs::open_dir(&fs::absolute_path("/dev")?, OpenFlags::empty())? + .map(|e| e.unwrap().name_as_string()) + .collect::>(); + assert!(dirents.contains(&"null".into())); + assert!(dirents.contains(&"zero".into())); + + // stat /dev + let dname = &fs::absolute_path("/dev")?; + let dir = fs::open_dir(dname, OpenFlags::empty())?; + let md = dir.get_attr()?; + println!("metadata of {dname:?}: {md:?}"); + assert_eq!(md.file_type(), FileType::Dir); + assert!(!md.is_file()); + assert!(md.is_dir()); + + // error cases + assert_err!(fs::create_dir(&fs::absolute_path("dev")?), AlreadyExists); + assert_err!( + open_file_create_new(&fs::absolute_path("/dev/")?), + AlreadyExists + ); + assert_err!( + fs::create_dir(&fs::absolute_path("/dev/zero")?), + AlreadyExists + ); + assert_err!( + fs::write(&fs::absolute_path("/dev/stdout")?, "test"), + PermissionDenied + ); + assert_err!( + fs::create_dir(&fs::absolute_path("/dev/test")?), + PermissionDenied + ); + assert_err!( + fs::remove_file(&fs::absolute_path("/dev/null")?), + PermissionDenied + ); + assert_err!( + fs::remove_dir(&fs::absolute_path("./dev")?), + PermissionDenied + ); + + // parent of '/dev' + assert_eq!( + fs::create_dir(&fs::absolute_path("///dev//..//233//")?), + Ok(()) + ); + assert_eq!( + fs::write( + &fs::absolute_path(".///dev//..//233//.///test.txt")?, + "test" + ), + Ok(()) + ); + assert_eq!( + fs::remove_file(&fs::absolute_path("./dev//../..//233//.///test.txt")?), + Ok(()) + ); + assert_err!( + fs::remove_file(&fs::absolute_path("./dev//..//233//../233/./test.txt")?), + NotFound + ); + assert_err!( + fs::remove_dir(&fs::absolute_path("very/../dev//")?), + PermissionDenied + ); + + // tests in /tmp + assert_eq!( + fs::get_attr(&fs::absolute_path("tmp")?)?.file_type(), + FileType::Dir + ); + assert_eq!( + fs::create_dir(&fs::absolute_path(".///tmp///././dir")?), + Ok(()) + ); + assert_eq!( + fs::open_dir(&fs::absolute_path("tmp")?, OpenFlags::empty()) + .unwrap() + .count(), + 3 + ); + assert_eq!( + fs::write(&fs::absolute_path(".///tmp///dir//.///test.txt")?, "test"), + Ok(()) + ); + assert_eq!( + fs::read(&fs::absolute_path("tmp//././/dir//.///test.txt")?), + Ok("test".into()) + ); + assert_err!( + fs::remove_dir(&fs::absolute_path("/tmp/dir/../dir")?), + DirectoryNotEmpty + ); + assert_eq!( + fs::remove_file(&fs::absolute_path("./tmp//dir//test.txt")?), + Ok(()) + ); + assert_eq!( + fs::remove_dir(&fs::absolute_path("tmp/dir/.././dir///")?), + Ok(()) + ); + assert_eq!( + fs::open_dir(&fs::absolute_path("tmp")?, OpenFlags::empty()) + .unwrap() + .count(), + 2 + ); + + println!("test_devfs_ramfs() OK!"); + Ok(()) +} + +pub fn test_all() { + test_read_write_file().expect("test_read_write_file() failed"); + test_read_dir().expect("test_read_dir() failed"); + test_file_permission().expect("test_file_permission() failed"); + test_create_file_dir().expect("test_create_file_dir() failed"); + test_remove_file_dir().expect("test_remove_file_dir() failed"); + test_devfs_ramfs().expect("test_devfs_ramfs() failed"); +} diff --git a/modules/ruxfs/tests/test_fatfs.rs b/modules/ruxruntime/tests/test_fatfs.rs similarity index 85% rename from modules/ruxfs/tests/test_fatfs.rs rename to modules/ruxruntime/tests/test_fatfs.rs index da542c00a..8a5e1a043 100644 --- a/modules/ruxfs/tests/test_fatfs.rs +++ b/modules/ruxruntime/tests/test_fatfs.rs @@ -7,7 +7,7 @@ * See the Mulan PSL v2 for more details. */ -#![cfg(not(feature = "myfs"))] +#![cfg(feature = "fatfs")] mod test_common; @@ -18,7 +18,7 @@ const IMG_PATH: &str = "resources/fat16.img"; fn make_disk() -> std::io::Result { let path = std::env::current_dir()?.join(IMG_PATH); - println!("Loading disk image from {:?} ...", path); + println!("Loading disk image from {path:?} ..."); let data = std::fs::read(path)?; println!("size = {} bytes", data.len()); Ok(RamDisk::from(&data)) @@ -31,13 +31,15 @@ fn test_fatfs() { let disk = make_disk().expect("failed to load disk image"); ruxtask::init_scheduler(); // call this to use `axsync::Mutex`. // By default, mount_points[0] will be rootfs - let mut mount_points: Vec = Vec::new(); + let mut mount_points: Vec = Vec::new(); // setup and initialize blkfs as one mountpoint for rootfs - mount_points.push(ruxfs::init_blkfs(AxDeviceContainer::from_one(disk))); + mount_points.push(ruxfs::init_blkfs(AxDeviceContainer::from_one(Box::new( + disk, + )))); ruxfs::prepare_commonfs(&mut mount_points); // setup and initialize rootfs - ruxfs::init_filesystems(mount_points); + ruxtask::fs::init_rootfs(mount_points); test_common::test_all(); } diff --git a/modules/ruxfs/tests/test_ramfs.rs b/modules/ruxruntime/tests/test_ramfs.rs similarity index 60% rename from modules/ruxfs/tests/test_ramfs.rs rename to modules/ruxruntime/tests/test_ramfs.rs index da93bff19..b23bf139c 100644 --- a/modules/ruxfs/tests/test_ramfs.rs +++ b/modules/ruxruntime/tests/test_ramfs.rs @@ -8,6 +8,7 @@ */ #![cfg(feature = "myfs")] +#![cfg(not(feature = "blkfs"))] mod test_common; @@ -16,37 +17,37 @@ use std::sync::Arc; use axfs_ramfs::RamFileSystem; use axfs_vfs::VfsOps; use axio::{Result, Write}; -use driver_block::ramdisk::RamDisk; -use ruxdriver::AxDeviceContainer; -use ruxfs::api::{self as fs, File}; -use ruxfs::fops::{Disk, MyFileSystemIf}; - +use ruxfs::{api as fs, MyFileSystemIf}; +use test_common::open_file_create_new; struct MyFileSystemIfImpl; #[crate_interface::impl_interface] impl MyFileSystemIf for MyFileSystemIfImpl { - fn new_myfs(_disk: Disk) -> Arc { + fn new_myfs() -> Arc { Arc::new(RamFileSystem::new()) } } fn create_init_files() -> Result<()> { - fs::write("./short.txt", "Rust is cool!\n")?; - let mut file = File::create_new("/long.txt")?; + fs::write(&fs::absolute_path("./short.txt")?, "Rust is cool!\n")?; + let mut file = open_file_create_new(&fs::absolute_path("/long.txt")?)?; for _ in 0..100 { file.write_fmt(format_args!("Rust is cool!\n"))?; } - fs::create_dir("very-long-dir-name")?; + fs::create_dir(&fs::absolute_path("very-long-dir-name")?)?; fs::write( - "very-long-dir-name/very-long-file-name.txt", + &fs::absolute_path("very-long-dir-name/very-long-file-name.txt")?, "Rust is cool!\n", )?; - fs::create_dir("very")?; - fs::create_dir("//very/long")?; - fs::create_dir("/./very/long/path")?; - fs::write(".//very/long/path/test.txt", "Rust is cool!\n")?; + fs::create_dir(&fs::absolute_path("very")?)?; + fs::create_dir(&fs::absolute_path("//very/long")?)?; + fs::create_dir(&fs::absolute_path("/./very/long/path")?)?; + fs::write( + &fs::absolute_path(".//very/long/path/test.txt")?, + "Rust is cool!\n", + )?; Ok(()) } @@ -56,18 +57,18 @@ fn test_ramfs() { ruxtask::init_scheduler(); // call this to use `axsync::Mutex`. // By default, mount_points[0] will be rootfs - let mut mount_points: Vec = Vec::new(); + + let mut mount_points: Vec = Vec::new(); // setup and initialize blkfs as one mountpoint for rootfs - mount_points.push(ruxfs::init_blkfs(AxDeviceContainer::from_one( - RamDisk::default(), - ))); + #[cfg(not(feature = "blkfs"))] + mount_points.push(ruxfs::init_tempfs()); ruxfs::prepare_commonfs(&mut mount_points); // setup and initialize rootfs - ruxfs::init_filesystems(mount_points); + ruxtask::fs::init_rootfs(mount_points); if let Err(e) = create_init_files() { - log::warn!("failed to create init files: {:?}", e); + log::warn!("failed to create init files: {e:?}"); } test_common::test_all(); diff --git a/modules/ruxtask/Cargo.toml b/modules/ruxtask/Cargo.toml index 1b31fef41..b4ba51e40 100644 --- a/modules/ruxtask/Cargo.toml +++ b/modules/ruxtask/Cargo.toml @@ -2,41 +2,51 @@ name = "ruxtask" version = "0.1.0" edition = "2021" -authors = [ - "Yuekai Jia ", - "AuYang261 ", -] +authors = ["Yuekai Jia ", "AuYang261 "] description = "Ruxos task management module" license = "GPL-3.0-or-later OR Apache-2.0" homepage = "https://github.com/syswonder/ruxos" repository = "https://github.com/syswonder/ruxos/tree/main/modules/ruxtask" [features] -default = [] +default = ["fs"] multitask = [ - "dep:ruxconfig", "dep:percpu", "dep:spinlock", "dep:lazy_init", "dep:memory_addr", - "dep:scheduler", "dep:timer_list", "kernel_guard", "dep:crate_interface", + "dep:ruxconfig", + "dep:spinlock", + "dep:lazy_init", + "dep:memory_addr", + "dep:scheduler", + "dep:timer_list", + "kernel_guard", + "dep:crate_interface", ] irq = [] tls = ["ruxhal/tls"] musl = [] -preempt = ["irq", "percpu?/preempt", "kernel_guard/preempt"] +preempt = ["irq", "percpu/preempt", "kernel_guard/preempt"] +paging = ["ruxhal/paging"] +fs = [] +signal = ["ruxhal/signal"] sched_fifo = ["multitask"] sched_rr = ["multitask", "preempt"] sched_cfs = ["multitask", "preempt"] -test = ["percpu?/sp-naive"] +test = ["percpu/sp-naive"] +notest = [] [dependencies] cfg-if = "1.0" log = "0.4" axerrno = { path = "../../crates/axerrno" } + ruxhal = { path = "../ruxhal" } +ruxfs = { path = "../ruxfs" } ruxconfig = { path = "../ruxconfig", optional = true } +axalloc = { path = "../axalloc" } ruxfdtable = { path = "../ruxfdtable" } -percpu = { path = "../../crates/percpu", optional = true } +percpu = "0.2" spinlock = { path = "../../crates/spinlock", optional = true } lazy_init = { path = "../../crates/lazy_init", optional = true } memory_addr = { version = "0.1.0", optional = true } @@ -44,8 +54,16 @@ scheduler = { path = "../../crates/scheduler", optional = true } timer_list = { path = "../../crates/timer_list", optional = true } kernel_guard = { version = "0.1.0", optional = true } crate_interface = { version = "0.1.1", optional = true } +flatten_objects = { path = "../../crates/flatten_objects" } +spin = { workspace = true } +axio = { path = "../../crates/axio" } +lazy_static = { version = "1.4", features = ["spin_no_std"] } +page_table = { path = "../../crates/page_table" } +page_table_entry = { path = "../../crates/page_table_entry" } + +# for testing +axfs_vfs = { path = "../../crates/axfs_vfs" } [dev-dependencies] rand = "0.8" ruxhal = { path = "../ruxhal", features = ["fp_simd"] } -ruxtask = { path = ".", features = ["test"] } diff --git a/modules/ruxtask/src/api.rs b/modules/ruxtask/src/api.rs index 67476e271..0304e4968 100644 --- a/modules/ruxtask/src/api.rs +++ b/modules/ruxtask/src/api.rs @@ -8,7 +8,6 @@ */ //! Task APIs for multi-task configuration. - use alloc::{string::String, sync::Arc}; pub(crate) use crate::run_queue::{AxRunQueue, RUN_QUEUE}; @@ -68,6 +67,7 @@ pub fn current_may_uninit() -> Option { /// # Panics /// /// Panics if the current task is not initialized. +#[inline(never)] pub fn current() -> CurrentTask { CurrentTask::get() } @@ -128,6 +128,39 @@ where TaskInner::new_musl(f, name, stack_size, tls, set_tid, tl) } +// temporarily only support aarch64 +#[cfg(all( + any(target_arch = "aarch64", target_arch = "riscv64"), + feature = "paging", + feature = "fs" +))] +pub fn fork_task() -> Option { + use core::mem::ManuallyDrop; + + let current_id = current().id().as_u64(); + let children_process = TaskInner::fork(); + + // Judge whether the parent process is blocked, if yes, add it to the blocking queue of the child process + if current().id().as_u64() == current_id { + RUN_QUEUE.lock().add_task(children_process.clone()); + + return Some(children_process); + } + + unsafe { + RUN_QUEUE.force_unlock(); + } + + // should not drop the children_process here, because it will be taken in the parent process + // and dropped in the parent process + let _ = ManuallyDrop::new(children_process); + + #[cfg(feature = "irq")] + ruxhal::arch::enable_irqs(); + + None +} + /// Spawns a new task with the default parameters. /// /// The default task name is an empty string. The default task stack size is @@ -181,6 +214,17 @@ pub fn yield_now() { RUN_QUEUE.lock().yield_current(); } +#[cfg(feature = "fs")] +struct SchedYieldIfImpl; + +#[cfg(feature = "fs")] +#[crate_interface::impl_interface] +impl ruxfs::fifo::SchedYieldIf for SchedYieldIfImpl { + fn yield_now() { + yield_now(); + } +} + /// Current task is going to sleep for the given duration. /// /// If the feature `irq` is not enabled, it uses busy-wait instead. diff --git a/modules/ruxtask/src/fs.rs b/modules/ruxtask/src/fs.rs new file mode 100644 index 000000000..92912ac96 --- /dev/null +++ b/modules/ruxtask/src/fs.rs @@ -0,0 +1,232 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! File system related functions. + +#![cfg(feature = "fs")] + +use crate::current; +use alloc::{borrow::ToOwned, format, sync::Arc, vec::Vec}; +use axerrno::{ax_err, AxResult}; +use axfs_vfs::VfsNodeRef; +use ruxfdtable::{FdTable, FileLike, OpenFlags}; +use ruxfs::{ + fops::{lookup, CurrentWorkingDirectoryOps}, + root::{MountPoint, RootDirectory}, + AbsPath, RelPath, +}; + +use axerrno::{LinuxError, LinuxResult}; + +#[crate_interface::def_interface] +/// The interface for initializing the file system. +pub trait InitFs { + /// Initializes the file system. + fn add_stdios_to_fd_table(task_inner: &mut FileSystem); +} + +#[cfg(not(feature = "notest"))] +struct InitFsDefaultImpl; + +#[cfg(not(feature = "notest"))] +#[crate_interface::impl_interface] +impl InitFs for InitFsDefaultImpl { + fn add_stdios_to_fd_table(_task_inner: &mut FileSystem) { + // do nothing + } +} + +/// Get the file object associated with the given file descriptor. +pub fn get_file_like(fd: i32) -> LinuxResult> { + // let _exec = *MUST_EXEC; + let binding_task = current(); + let mut binding_fs = binding_task.fs.lock(); + if let Some(fs) = binding_fs.as_mut() { + fs.fd_table + .get(fd as usize) + .cloned() + .ok_or(LinuxError::EBADF) + } else { + Err(LinuxError::EBADF) + } +} + +/// Adds a file like object to the file descriptor table and returns the file descriptor. +/// Actually there only `CLOEXEC` flag in options works. +pub fn add_file_like(f: Arc, flags: OpenFlags) -> LinuxResult { + let binding_task = current(); + let mut binding_fs = binding_task.fs.lock(); + let fd_table = &mut binding_fs.as_mut().expect("No fd table found").fd_table; + let fd = fd_table.add(f, flags).ok_or(LinuxError::EMFILE)?; + Ok(fd as _) +} + +/// Removes a file like object from the file descriptor table. +pub fn close_file_like(fd: i32) -> LinuxResult { + let binding_task = current(); + let mut binding_fs = binding_task.fs.lock(); + let fd_table = &mut binding_fs.as_mut().unwrap().fd_table; + + let file = fd_table.remove(fd as usize).ok_or(LinuxError::EBADF)?; + + // drop the binding_fs to release the lock, as some operations + // when closing a file may need to reschedule the task.(e.g. SOCKET_CLOSE) + drop(binding_fs); + drop(file); + + Ok(()) +} + +/// Get current task umask +pub fn get_umask() -> u16 { + current().fs.lock().as_ref().unwrap().umask.get() +} + +/// Replace current task umask +pub fn replace_umask(new_mask: u16) -> u16 { + current() + .fs + .lock() + .as_mut() + .unwrap() + .umask + .replace(new_mask) +} + +/// A struct representing a file system object. +#[derive(Clone)] +pub struct FileSystem { + /// The file descriptor table. + pub fd_table: FdTable, + /// The current working directory. + pub current_path: AbsPath<'static>, + /// The current directory. + pub current_dir: VfsNodeRef, + /// The root directory. + pub root_dir: Arc, + /// file mode creation maskq + umask: UMask, +} + +impl FileSystem { + /// Closes all file objects in the file descriptor table. + pub fn close_all_files(&mut self) { + self.fd_table.close_all_files(); + } +} + +/// Initializes the file system. +pub fn init_rootfs(mount_points: Vec) { + let main_fs = mount_points + .first() + .expect("No filesystem found") + .fs + .clone(); + let root_dir = RootDirectory::new(main_fs); + + for mp in mount_points.iter().skip(1) { + let message = format!("failed to mount filesystem at {}", mp.path); + root_dir.mount(mp.clone()).expect(&message); + } + + let root_dir_arc = Arc::new(root_dir); + + let mut fs = FileSystem { + fd_table: FdTable::default(), + current_path: AbsPath::new_owned("/".to_owned()), + current_dir: root_dir_arc.clone(), + root_dir: root_dir_arc.clone(), + umask: UMask::new(), + }; + + // TODO: make a more clear interface for adding stdios to fd table when not in unit tests + let fs_mutable = &mut fs; + crate_interface::call_interface!(InitFs::add_stdios_to_fd_table, fs_mutable); + + current().fs.lock().replace(fs); +} + +/// Returns the absolute path of the given path. +pub fn absolute_path(path: &str) -> AxResult> { + if path.starts_with('/') { + Ok(AbsPath::new_canonicalized(path)) + } else { + Ok(current() + .fs + .lock() + .as_mut() + .unwrap() + .current_path + .join(&RelPath::new_canonicalized(path))) + } +} + +/// Returns the current directory. +pub fn current_dir() -> AxResult> { + Ok(current().fs.lock().as_mut().unwrap().current_path.clone()) +} + +/// Sets the current directory. +pub fn set_current_dir(path: AbsPath<'static>) -> AxResult { + let node = lookup(&path)?; + let attr = node.get_attr()?; + if !attr.is_dir() { + ax_err!(NotADirectory) + } else if !attr.perm().owner_executable() { + ax_err!(PermissionDenied) + } else { + current().fs.lock().as_mut().unwrap().current_dir = node; + current().fs.lock().as_mut().unwrap().current_path = path; + Ok(()) + } +} + +#[derive(Clone, Copy, Debug)] +struct UMask(u16); + +impl UMask { + fn new() -> Self { + Self(0o022) + } + + fn get(&self) -> u16 { + self.0 + } + + fn replace(&mut self, new_mask: u16) -> u16 { + let new_mask = new_mask & 0o777; + let old_mask = self.0; + self.0 = new_mask; + old_mask + } +} + +struct CurrentWorkingDirectoryImpl; + +#[crate_interface::impl_interface] +impl CurrentWorkingDirectoryOps for CurrentWorkingDirectoryImpl { + fn absolute_path(path: &str) -> AxResult> { + absolute_path(path) + } + fn current_dir() -> AxResult> { + current_dir() + } + fn set_current_dir(path: AbsPath<'static>) -> AxResult { + set_current_dir(path) + } + fn root_dir() -> Arc { + current() + .fs + .lock() + .as_mut() + .expect("No filesystem found") + .root_dir + .clone() + } +} diff --git a/modules/ruxtask/src/lib.rs b/modules/ruxtask/src/lib.rs index 8993df9f0..a572133f6 100644 --- a/modules/ruxtask/src/lib.rs +++ b/modules/ruxtask/src/lib.rs @@ -19,8 +19,8 @@ //! management and scheduling is used, as well as more task-related APIs. //! Otherwise, only a few APIs with naive implementation is available. //! - `irq`: Interrupts are enabled. If this feature is enabled, timer-based -//! APIs can be used, such as [`sleep`], [`sleep_until`], and -//! [`WaitQueue::wait_timeout`]. +//! APIs can be used, such as [`sleep`], [`sleep_until`], and +//! [`WaitQueue::wait_timeout`]. //! - `preempt`: Enable preemptive scheduling. //! - `sched_fifo`: Use the [FIFO cooperative scheduler][1]. It also enables the //! `multitask` feature if it is enabled. This feature is enabled by default, @@ -37,17 +37,27 @@ #![cfg_attr(not(test), no_std)] #![feature(doc_cfg)] #![feature(doc_auto_cfg)] +// TODO: remove this once we have a better way to handle +#![allow(clippy::arc_with_non_send_sync)] +#![allow(static_mut_refs)] cfg_if::cfg_if! { if #[cfg(feature = "multitask")] { - #[macro_use] + #[macro_use(info, debug, trace)] extern crate log; extern crate alloc; mod run_queue; - mod task; + pub mod task; mod api; mod wait_queue; + #[cfg(feature = "signal")] + pub mod signal; + #[cfg(feature = "paging")] + pub mod vma; + // TODO: make fs module optional + // #[cfg(feature = "fs")] + pub mod fs; #[cfg(feature = "irq")] /// load average pub mod loadavg; @@ -75,8 +85,14 @@ cfg_if::cfg_if! { pub use self::api::*; pub use self::api::{sleep, sleep_until, yield_now}; pub use task::TaskState; + #[cfg(feature = "signal")] + pub use self::signal::{rx_sigaction, Signal}; } else { mod api_s; + #[cfg(feature = "signal")] + pub mod signal; pub use self::api_s::{sleep, sleep_until, yield_now}; + #[cfg(feature = "signal")] + pub use self::signal::{rx_sigaction, Signal}; } } diff --git a/modules/ruxtask/src/run_queue.rs b/modules/ruxtask/src/run_queue.rs index 5e98942de..485886763 100644 --- a/modules/ruxtask/src/run_queue.rs +++ b/modules/ruxtask/src/run_queue.rs @@ -7,11 +7,13 @@ * See the Mulan PSL v2 for more details. */ +#[cfg(feature = "fs")] +use crate::fs::get_file_like; use alloc::collections::VecDeque; use alloc::sync::Arc; -use axerrno::{LinuxError, LinuxResult}; +use axerrno::LinuxResult; use lazy_init::LazyInit; -use ruxfdtable::{FD_TABLE, RUX_FILE_LIMIT}; +use ruxfdtable::RUX_FILE_LIMIT; use scheduler::BaseScheduler; use spinlock::SpinNoIrq; @@ -99,13 +101,14 @@ impl AxRunQueue { debug!("task exit: {}, exit_code={}", curr.id_name(), exit_code); assert!(curr.is_running()); assert!(!curr.is_idle()); - if curr.is_init() { + + if crate::current().is_init() { EXITED_TASKS.lock().clear(); ruxhal::misc::terminate(); } else { curr.set_state(TaskState::Exited); curr.notify_exit(exit_code, self); - EXITED_TASKS.lock().push_back(curr.clone()); + EXITED_TASKS.lock().push_back(curr.clone_as_taskref()); WAIT_FOR_EXIT.notify_one_locked(false, self); self.resched(false); } @@ -122,11 +125,12 @@ impl AxRunQueue { assert!(!curr.is_idle()); // we must not block current task with preemption disabled. + // only allow blocking current task with run_queue lock held. #[cfg(feature = "preempt")] assert!(curr.can_preempt(1)); curr.set_state(TaskState::Blocked); - wait_queue_push(curr.clone()); + wait_queue_push(curr.clone_as_taskref()); self.resched(false); } @@ -151,7 +155,7 @@ impl AxRunQueue { let now = ruxhal::time::current_time(); if now < deadline { - crate::timers::set_alarm_wakeup(deadline, curr.clone()); + crate::timers::set_alarm_wakeup(deadline, curr.clone_as_taskref()); curr.set_state(TaskState::Blocked); self.resched(false); } @@ -166,7 +170,8 @@ impl AxRunQueue { if prev.is_running() { prev.set_state(TaskState::Ready); if !prev.is_idle() { - self.scheduler.put_prev_task(prev.clone(), preempt); + self.scheduler + .put_prev_task(prev.clone_as_taskref(), preempt); } } let next = self.scheduler.pick_next_task().unwrap_or_else(|| unsafe { @@ -176,6 +181,42 @@ impl AxRunQueue { self.switch_to(prev, next); } + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + fn switch_to(&mut self, prev_task: CurrentTask, next_task: AxTaskRef) { + trace!( + "context switch: {} -> {}", + prev_task.id_name(), + next_task.id_name() + ); + #[cfg(feature = "preempt")] + next_task.set_preempt_pending(false); + next_task.set_state(TaskState::Running); + if prev_task.ptr_eq(&next_task) { + return; + } + + unsafe { + let prev_ctx_ptr = prev_task.ctx_mut_ptr(); + let next_ctx_ptr = next_task.ctx_mut_ptr(); + + // The strong reference count of `prev_task` will be decremented by 1, + // but won't be dropped until `gc_entry()` is called. + assert!(Arc::strong_count(prev_task.as_task_ref()) > 1); + assert!(Arc::strong_count(&next_task) >= 1); + + let next_page_table = next_task.pagetable.lock(); + let root_paddr = next_page_table.root_paddr(); + + // Drop the `next_page_table` here, so that it will not be dropped after context switch. + drop(next_page_table); + + CurrentTask::set_current(prev_task, next_task); + + (*prev_ctx_ptr).switch_to(&*next_ctx_ptr, root_paddr); + } + } + + #[cfg(target_arch = "x86_64")] fn switch_to(&mut self, prev_task: CurrentTask, next_task: AxTaskRef) { trace!( "context switch: {} -> {}", @@ -204,23 +245,22 @@ impl AxRunQueue { } } +#[cfg(feature = "fs")] fn gc_flush_file(fd: usize) -> LinuxResult { - trace!("gc task flush: {}", fd); - FD_TABLE - .read() - .get(fd) - .cloned() - .ok_or(LinuxError::EBADF)? - .flush() + trace!("gc task flush: {fd}"); + get_file_like(fd as i32)?.flush() } fn gc_entry() { let mut now_file_fd: usize = 3; loop { - let _ = gc_flush_file(now_file_fd); - now_file_fd += 1; - if now_file_fd >= RUX_FILE_LIMIT { - now_file_fd = 3; + #[cfg(feature = "fs")] + { + let _ = gc_flush_file(now_file_fd); + now_file_fd += 1; + if now_file_fd >= RUX_FILE_LIMIT { + now_file_fd = 3; + } } // Drop all exited tasks and recycle resources. let n = EXITED_TASKS.lock().len(); @@ -243,19 +283,19 @@ fn gc_entry() { } pub(crate) fn init() { + let main_task = TaskInner::new_init("main".into()); + main_task.set_state(TaskState::Running); + unsafe { CurrentTask::init_current(main_task) }; + const IDLE_TASK_STACK_SIZE: usize = 4096; let idle_task = TaskInner::new(|| crate::run_idle(), "idle".into(), IDLE_TASK_STACK_SIZE); IDLE_TASK.with_current(|i| i.init_by(idle_task.clone())); - let main_task = TaskInner::new_init("main".into()); - main_task.set_state(TaskState::Running); - RUN_QUEUE.init_by(AxRunQueue::new()); - unsafe { CurrentTask::init_current(main_task) } } pub(crate) fn init_secondary() { - let idle_task = TaskInner::new_init("idle".into()); + let idle_task = TaskInner::new_idle("idle".into()); idle_task.set_state(TaskState::Running); IDLE_TASK.with_current(|i| i.init_by(idle_task.clone())); unsafe { CurrentTask::init_current(idle_task) } diff --git a/modules/ruxruntime/src/signal.rs b/modules/ruxtask/src/signal.rs similarity index 53% rename from modules/ruxruntime/src/signal.rs rename to modules/ruxtask/src/signal.rs index a979b9b40..66e99bb5b 100644 --- a/modules/ruxruntime/src/signal.rs +++ b/modules/ruxtask/src/signal.rs @@ -7,6 +7,9 @@ * See the Mulan PSL v2 for more details. */ +//! Signal module for RuxOS. + +use crate::current; #[cfg(feature = "irq")] use core::sync::atomic::AtomicI64; use core::{ @@ -14,6 +17,9 @@ use core::{ time::Duration, }; +use log::warn; +use ruxhal::trap::TrapHandler; + /// sigaction in kernel #[allow(non_camel_case_types)] #[allow(dead_code)] @@ -50,19 +56,47 @@ pub struct Signal { } unsafe extern "C" fn default_handler(signum: c_int) { - panic!("default_handler, signum: {}", signum); + warn!("default_handler, signum: {signum}"); } -static mut SIGNAL_IF: Signal = Signal { - #[cfg(feature = "irq")] - signal: AtomicI64::new(0), - sigaction: [rx_sigaction::new(); 32], - // Default::default() is not const - timer_value: [Duration::from_nanos(0); 3], - timer_interval: [Duration::from_nanos(0); 3], -}; +#[cfg(feature = "signal")] +struct SignalHandler; + +#[cfg(feature = "signal")] +#[crate_interface::impl_interface] +impl TrapHandler for SignalHandler { + fn handle_signal() { + let signal = Signal::signal(-1, true).unwrap(); + for signum in 0..32 { + if signal & (1 << signum) != 0 + /* TODO: && support mask */ + { + Signal::signal(signum as i8, false); + Signal::sigaction(signum as u8, None, None); + Signal::signal_handle(signum as u8); + } + } + } +} + +impl Default for Signal { + fn default() -> Self { + Self::new() + } +} impl Signal { + ///crate new Signal struct + pub fn new() -> Self { + Self { + #[cfg(feature = "irq")] + signal: AtomicI64::new(0), + sigaction: [rx_sigaction::new(); 32], + // Default::default() is not const + timer_value: [Duration::from_nanos(0); 3], + timer_interval: [Duration::from_nanos(0); 3], + } + } /// Set signal /// signum: signal number, if signum < 0, just return current signal /// on: true: enable signal, false: disable signal @@ -72,7 +106,9 @@ impl Signal { if signum >= 32 { return None; } - let mut old = unsafe { SIGNAL_IF.signal.load(Ordering::Acquire) }; + let binding = current(); + let current_signal_if = binding.signal_if.lock(); + let mut old = current_signal_if.signal.load(Ordering::Acquire); if signum >= 0 { loop { let new = if on { @@ -81,14 +117,12 @@ impl Signal { old & !(1 << signum) }; - match unsafe { - SIGNAL_IF.signal.compare_exchange_weak( - old, - new, - Ordering::AcqRel, - Ordering::Acquire, - ) - } { + match current_signal_if.signal.compare_exchange_weak( + old, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { Ok(_) => break, Err(x) => old = x, } @@ -104,21 +138,35 @@ impl Signal { sigaction: Option<*const rx_sigaction>, oldact: Option<*mut rx_sigaction>, ) { - if signum >= unsafe { SIGNAL_IF.sigaction }.len() as u8 { + let binding = current(); + let mut current_signal_if = binding.signal_if.lock(); + if signum >= current_signal_if.sigaction.len() as u8 { return; } if let Some(oldact) = oldact { unsafe { - *oldact = SIGNAL_IF.sigaction[signum as usize]; + *oldact = current_signal_if.sigaction[signum as usize]; + } + } + + if let Some(s) = sigaction { + unsafe { + current_signal_if.sigaction[signum as usize] = *s; } } - match sigaction { - Some(s) => unsafe { - SIGNAL_IF.sigaction[signum as usize] = *s; - }, - None => unsafe { - SIGNAL_IF.sigaction[signum as usize].sa_handler.unwrap()(signum as c_int) - }, + } + /// Handle signal for the current process + /// signum: Signal number to handle + /// When the registered sa_handler for the specified signal is None, logs an error message + pub fn signal_handle(signum: u8) { + let binding = current(); + let current_signal_if = binding.signal_if.lock(); + if let Some(handler) = current_signal_if.sigaction[signum as usize].sa_handler { + unsafe { + handler(signum as c_int); + } + } else { + log::error!("no sigaction !"); } } /// Set timer @@ -126,14 +174,14 @@ impl Signal { /// new_value: new timer value /// old_value: old timer value pub fn timer_deadline(which: usize, new_deadline: Option) -> Option { - if which >= unsafe { SIGNAL_IF.timer_value }.len() { + let binding = current(); + let mut current_signal_if = binding.signal_if.lock(); + if which >= current_signal_if.timer_value.len() { return None; } - let old = unsafe { SIGNAL_IF.timer_value }[which]; + let old = current_signal_if.timer_value[which]; if let Some(s) = new_deadline { - unsafe { - SIGNAL_IF.timer_value[which] = Duration::from_nanos(s); - } + current_signal_if.timer_value[which] = Duration::from_nanos(s); } Some(old.as_nanos() as u64) } @@ -142,14 +190,14 @@ impl Signal { /// new_interval: new timer interval /// old_interval: old timer interval pub fn timer_interval(which: usize, new_interval: Option) -> Option { - if which >= unsafe { SIGNAL_IF.timer_interval }.len() { + let binding = current(); + let mut current_signal_if = binding.signal_if.lock(); + if which >= current_signal_if.timer_interval.len() { return None; } - let old = unsafe { SIGNAL_IF.timer_interval }[which]; + let old = current_signal_if.timer_interval[which]; if let Some(s) = new_interval { - unsafe { - SIGNAL_IF.timer_interval[which] = Duration::from_nanos(s); - } + current_signal_if.timer_interval[which] = Duration::from_nanos(s); } Some(old.as_nanos() as u64) } diff --git a/modules/ruxtask/src/task.rs b/modules/ruxtask/src/task.rs index 31950e583..29274aca4 100644 --- a/modules/ruxtask/src/task.rs +++ b/modules/ruxtask/src/task.rs @@ -7,10 +7,22 @@ * See the Mulan PSL v2 for more details. */ -use alloc::{boxed::Box, string::String, sync::Arc}; +//! implementation of task structure and related functions. +#[cfg(feature = "fs")] +use crate::fs::FileSystem; +use alloc::collections::BTreeMap; +use alloc::{ + boxed::Box, + string::String, + sync::{Arc, Weak}, +}; +use core::mem::ManuallyDrop; use core::ops::Deref; use core::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, AtomicU8, Ordering}; use core::{alloc::Layout, cell::UnsafeCell, fmt, ptr::NonNull}; +#[cfg(feature = "paging")] +use ruxhal::paging::PageTable; +use spinlock::SpinNoIrq; #[cfg(feature = "preempt")] use core::sync::atomic::AtomicUsize; @@ -18,11 +30,16 @@ use core::sync::atomic::AtomicUsize; #[cfg(feature = "tls")] use ruxhal::tls::TlsArea; -use memory_addr::{align_up_4k, VirtAddr}; +use memory_addr::{align_up_4k, VirtAddr, PAGE_SIZE_4K}; use ruxhal::arch::TaskContext; +use crate::current; #[cfg(not(feature = "musl"))] use crate::tsd::{DestrFunction, KEYS, TSD}; +#[cfg(feature = "paging")] +use crate::vma::MmapStruct; +#[cfg(feature = "signal")] +use crate::Signal; use crate::{AxRunQueue, AxTask, AxTaskRef, WaitQueue}; /// A unique identifier for a thread. @@ -45,11 +62,12 @@ pub enum TaskState { /// The inner task structure. pub struct TaskInner { + parent_process: Option>, + process_task: Weak, id: TaskId, name: String, is_idle: bool, is_init: bool, - entry: Option<*mut dyn FnOnce()>, state: AtomicU8, @@ -65,7 +83,8 @@ pub struct TaskInner { exit_code: AtomicI32, wait_for_exit: WaitQueue, - kstack: Option, + stack_map_addr: SpinNoIrq, + kstack: SpinNoIrq>>, ctx: UnsafeCell, #[cfg(feature = "tls")] @@ -74,12 +93,25 @@ pub struct TaskInner { #[cfg(not(feature = "musl"))] tsd: TSD, + #[cfg(feature = "signal")] + /// The signal to be sent to the task. + pub signal_if: Arc>, + // set tid #[cfg(feature = "musl")] set_tid: AtomicU64, // clear tid #[cfg(feature = "musl")] tl: AtomicU64, + #[cfg(feature = "paging")] + /// The page table of the task. + pub pagetable: Arc>, + /// file system + #[cfg(feature = "fs")] + pub fs: Arc>>, + #[cfg(feature = "paging")] + /// memory management + pub mm: Arc, } impl TaskId { @@ -132,6 +164,37 @@ impl TaskInner { alloc::format!("Task({}, {:?})", self.id.as_u64(), self.name) } + /// Get pointer for parent process task + pub fn parent_process(&self) -> Option { + if let Some(parent_process) = self.parent_process.as_ref() { + return parent_process.upgrade(); + } + None + } + + /// Get pointer for process task + pub fn exit_code(&self) -> i32 { + self.exit_code.load(Ordering::Acquire) + } + + /// Get process task + pub fn process_task(&self) -> Arc { + if let Some(process_task) = self.process_task.upgrade() { + process_task.clone() + } else { + current().as_task_ref().clone() + } + } + + /// Get pid of the process of the task. + pub fn process_id(&self) -> TaskId { + if let Some(process_task) = self.process_task.upgrade() { + process_task.id + } else { + self.id + } + } + /// Wait for the task to exit, and return the exit code. /// /// It will return immediately if the task has already exited (but not dropped). @@ -152,10 +215,21 @@ impl TaskInner { } } +/// map task id into task. +pub static PROCESS_MAP: SpinNoIrq>> = SpinNoIrq::new(BTreeMap::new()); + // private methods impl TaskInner { + // clone a thread fn new_common(id: TaskId, name: String) -> Self { + debug!( + "new_common: process_id={:#}, name={:?}", + current().id_name(), + id.0 + ); Self { + parent_process: Some(Arc::downgrade(current().as_task_ref())), + process_task: Arc::downgrade(¤t().process_task()), id, name, is_idle: false, @@ -171,7 +245,8 @@ impl TaskInner { preempt_disable_count: AtomicUsize::new(0), exit_code: AtomicI32::new(0), wait_for_exit: WaitQueue::new(), - kstack: None, + stack_map_addr: SpinNoIrq::new(VirtAddr::from(0)), // should be set later + kstack: SpinNoIrq::new(Arc::new(None)), ctx: UnsafeCell::new(TaskContext::new()), #[cfg(feature = "tls")] tls: TlsArea::alloc(), @@ -179,8 +254,16 @@ impl TaskInner { tsd: spinlock::SpinNoIrq::new([core::ptr::null_mut(); ruxconfig::PTHREAD_KEY_MAX]), #[cfg(feature = "musl")] set_tid: AtomicU64::new(0), + #[cfg(feature = "signal")] + signal_if: current().signal_if.clone(), #[cfg(feature = "musl")] tl: AtomicU64::new(0), + #[cfg(feature = "paging")] + pagetable: current().pagetable.clone(), + #[cfg(feature = "fs")] + fs: current().fs.clone(), + #[cfg(feature = "paging")] + mm: current().mm.clone(), } } @@ -192,7 +275,10 @@ impl TaskInner { set_tid: AtomicU64, tl: AtomicU64, ) -> Self { + use crate::current; Self { + parent_process: Some(Arc::downgrade(current().as_task_ref())), + process_task: Arc::downgrade(¤t().process_task()), id, name, is_idle: false, @@ -208,16 +294,40 @@ impl TaskInner { preempt_disable_count: AtomicUsize::new(0), exit_code: AtomicI32::new(0), wait_for_exit: WaitQueue::new(), - kstack: None, + stack_map_addr: SpinNoIrq::new(VirtAddr::from(0)), + kstack: SpinNoIrq::new(Arc::new(None)), ctx: UnsafeCell::new(TaskContext::new()), #[cfg(feature = "tls")] tls: TlsArea::new_with_addr(tls), set_tid, + #[cfg(feature = "signal")] + signal_if: current().signal_if.clone(), // clear child tid tl, + #[cfg(feature = "paging")] + pagetable: current().pagetable.clone(), + #[cfg(feature = "fs")] + fs: current().fs.clone(), + #[cfg(feature = "paging")] + mm: current().mm.clone(), } } + /// Create a new idle task. + pub fn stack_top(&self) -> VirtAddr { + self.kstack.lock().as_ref().as_ref().unwrap().top() + } + + /// Set the stack top and size for the task. + pub fn set_stack_top(&self, begin: usize, size: usize) { + debug!("set_stack_top: begin={begin:#x}, size={size:#x}"); + *self.stack_map_addr.lock() = VirtAddr::from(begin); + *self.kstack.lock() = Arc::new(Some(TaskStack { + ptr: NonNull::new(begin as *mut u8).unwrap(), + layout: Layout::from_size_align(size, PAGE_SIZE_4K).unwrap(), + })); + } + /// for set_tid_addr #[cfg(feature = "musl")] pub fn set_child_tid(&self, tid: usize) { @@ -239,9 +349,7 @@ impl TaskInner { F: FnOnce() + Send + 'static, { let mut t = Self::new_common_tls(TaskId::new(), name, tls, set_tid, tl); - debug!("new task: {}", t.id_name()); let kstack = TaskStack::alloc(align_up_4k(stack_size)); - #[cfg(feature = "tls")] let tls = VirtAddr::from(t.tls.tls_ptr() as usize); #[cfg(not(feature = "tls"))] @@ -249,7 +357,7 @@ impl TaskInner { t.entry = Some(Box::into_raw(Box::new(entry))); t.ctx.get_mut().init(task_entry as usize, kstack.top(), tls); - t.kstack = Some(kstack); + t.kstack = SpinNoIrq::new(Arc::new(Some(kstack))); if t.name == "idle" { t.is_idle = true; } @@ -272,13 +380,189 @@ impl TaskInner { t.entry = Some(Box::into_raw(Box::new(entry))); t.ctx.get_mut().init(task_entry as usize, kstack.top(), tls); - t.kstack = Some(kstack); + t.kstack = SpinNoIrq::new(Arc::new(Some(kstack))); if t.name == "idle" { t.is_idle = true; } Arc::new(AxTask::new(t)) } + #[cfg(all( + any(target_arch = "aarch64", target_arch = "riscv64"), + feature = "paging", + feature = "fs" + ))] + /// only support for aarch64 + pub fn fork() -> AxTaskRef { + use crate::alloc::string::ToString; + use page_table::PageSize; + use page_table_entry::MappingFlags; + use ruxhal::{ + arch::flush_tlb, + mem::{direct_virt_to_phys, phys_to_virt}, + }; + let current_task = crate::current(); + let name = current_task.as_task_ref().name().to_string(); + let current_stack_bindings = current_task.as_task_ref().kstack.lock(); + let current_stack = current_stack_bindings.as_ref().as_ref().unwrap(); + let current_stack_top = current_stack.top(); + let stack_size = current_stack.layout.size(); + debug!("fork: current_stack_top={current_stack_top:#x}, stack_size={stack_size:#x}"); + + #[cfg(feature = "paging")] + // TODO: clone parent page table, and mark all unshared pages to read-only + let mut cloned_page_table = PageTable::try_new().expect("failed to create page table"); + let cloned_mm = current().mm.as_ref().clone(); + + // clone the global shared pages (as system memory) + // TODO: exclude the stack page from the cloned page table + #[cfg(feature = "paging")] + for r in ruxhal::mem::memory_regions() { + cloned_page_table + .map_region( + phys_to_virt(r.paddr), + r.paddr, + r.size, + r.flags.into(), + false, + ) + .expect("failed to map region when forking"); + } + + // mapping the page for stack to the process's stack, stack must keep at the same position. + // TODO: merge these code with previous. + let new_stack = TaskStack::alloc(align_up_4k(stack_size)); + let new_stack_vaddr = new_stack.end(); + let stack_paddr = direct_virt_to_phys(new_stack_vaddr); + + // Note: the stack region is mapped to the same position as the parent process's stack, be careful when update the stack region for the forked process. + let (_, prev_flag, _) = cloned_page_table + .query(*current().stack_map_addr.lock()) + .expect("failed to query stack region when forking"); + cloned_page_table + .unmap_region(*current().stack_map_addr.lock(), align_up_4k(stack_size)) + .expect("failed to unmap stack region when forking"); + cloned_page_table + .map_region( + *current().stack_map_addr.lock(), + stack_paddr, + stack_size, + prev_flag, + true, + ) + .expect("failed to map stack region when forking"); + + // clone parent pages in memory, and mark all unshared pages to read-only + for (vaddr, page_info) in cloned_mm.mem_map.lock().iter() { + let paddr = page_info.paddr; + cloned_page_table + .map((*vaddr).into(), paddr, PageSize::Size4K, MappingFlags::READ) + .expect("failed to map when forking"); + } + + // mark the parent process's page table to read-only. + for (vaddr, _) in current_task.mm.mem_map.lock().iter() { + let mut page_table = current_task.pagetable.lock(); + let vaddr = VirtAddr::from(*vaddr); + let (_, mapping_flag, _) = page_table + .query(vaddr) + .expect("Inconsistent page table with mem_map"); + if mapping_flag.contains(MappingFlags::EXECUTE) { + page_table + .update( + vaddr, + None, + Some(MappingFlags::READ | MappingFlags::EXECUTE), + ) + .expect("failed to update mapping when forking"); + + cloned_page_table + .update( + vaddr, + None, + Some(MappingFlags::READ | MappingFlags::EXECUTE), + ) + .expect("failed to update mapping when forking"); + } else { + page_table + .update(vaddr, None, Some(MappingFlags::READ)) + .expect("failed to update mapping when forking"); + } + flush_tlb(Some(vaddr)); + } + + let new_pid = TaskId::new(); + let mut t = Self { + parent_process: Some(Arc::downgrade(current_task.as_task_ref())), + process_task: Weak::new(), + id: new_pid, + name, + is_idle: false, + is_init: false, + entry: None, + state: AtomicU8::new(TaskState::Ready as u8), + in_wait_queue: AtomicBool::new(current_task.in_wait_queue.load(Ordering::Relaxed)), + #[cfg(feature = "irq")] + in_timer_list: AtomicBool::new(current_task.in_timer_list.load(Ordering::Relaxed)), + #[cfg(feature = "preempt")] + need_resched: AtomicBool::new(current_task.need_resched.load(Ordering::Relaxed)), + #[cfg(feature = "preempt")] + preempt_disable_count: AtomicUsize::new( + current_task.preempt_disable_count.load(Ordering::Acquire), + ), + exit_code: AtomicI32::new(0), + wait_for_exit: WaitQueue::new(), + stack_map_addr: SpinNoIrq::new(*current().stack_map_addr.lock()), + kstack: SpinNoIrq::new(Arc::new(Some(new_stack))), + ctx: UnsafeCell::new(TaskContext::new()), + #[cfg(feature = "tls")] + tls: TlsArea::alloc(), + #[cfg(not(feature = "musl"))] + tsd: spinlock::SpinNoIrq::new([core::ptr::null_mut(); ruxconfig::PTHREAD_KEY_MAX]), + #[cfg(feature = "musl")] + set_tid: AtomicU64::new(0), + #[cfg(feature = "signal")] + signal_if: Arc::new(spinlock::SpinNoIrq::new(Signal::new())), + #[cfg(feature = "musl")] + tl: AtomicU64::new(0), + #[cfg(feature = "paging")] + pagetable: Arc::new(SpinNoIrq::new(cloned_page_table)), + #[cfg(feature = "fs")] + fs: Arc::new(SpinNoIrq::new(current_task.fs.lock().clone())), + #[cfg(feature = "paging")] + mm: Arc::new(cloned_mm), + }; + + debug!("new task forked: {}", t.id_name()); + + #[cfg(feature = "tls")] + let tls = VirtAddr::from(t.tls.tls_ptr() as usize); + #[cfg(not(feature = "tls"))] + let tls = VirtAddr::from(0); + + t.entry = None; + t.ctx.get_mut().init( + task_entry as usize, + t.kstack.lock().as_ref().as_ref().unwrap().top(), + tls, + ); + let task_ref = Arc::new(AxTask::new(t)); + PROCESS_MAP + .lock() + .insert(new_pid.as_u64(), task_ref.clone()); + + unsafe { + // copy the stack content from current stack to new stack + (*task_ref.ctx_mut_ptr()).save_current_content( + current_stack.end().as_ptr(), + new_stack_vaddr.as_mut_ptr(), + stack_size, + ); + } + + task_ref + } + /// Creates an "init task" using the current CPU states, to use as the /// current task. /// @@ -287,12 +571,129 @@ impl TaskInner { /// /// And there is no need to set the `entry`, `kstack` or `tls` fields, as /// they will be filled automatically when the task is switches out. + #[allow(unused_mut)] pub(crate) fn new_init(name: String) -> AxTaskRef { - let mut t = Self::new_common(TaskId::new(), name); - t.is_init = true; - if t.name == "idle" { - t.is_idle = true; + let mut t: TaskInner = Self { + parent_process: None, + process_task: Weak::new(), + id: TaskId::new(), + name, + is_idle: false, + is_init: true, + entry: None, + state: AtomicU8::new(TaskState::Ready as u8), + in_wait_queue: AtomicBool::new(false), + #[cfg(feature = "irq")] + in_timer_list: AtomicBool::new(false), + #[cfg(feature = "preempt")] + need_resched: AtomicBool::new(false), + #[cfg(feature = "preempt")] + preempt_disable_count: AtomicUsize::new(0), + exit_code: AtomicI32::new(0), + wait_for_exit: WaitQueue::new(), + stack_map_addr: SpinNoIrq::new(VirtAddr::from(0)), // set in set_stack_top + kstack: SpinNoIrq::new(Arc::new(None)), + ctx: UnsafeCell::new(TaskContext::new()), + #[cfg(feature = "tls")] + tls: TlsArea::alloc(), + #[cfg(not(feature = "musl"))] + tsd: spinlock::SpinNoIrq::new([core::ptr::null_mut(); ruxconfig::PTHREAD_KEY_MAX]), + #[cfg(feature = "musl")] + set_tid: AtomicU64::new(0), + #[cfg(feature = "signal")] + signal_if: Arc::new(spinlock::SpinNoIrq::new(Signal::new())), + #[cfg(feature = "musl")] + tl: AtomicU64::new(0), + #[cfg(feature = "paging")] + pagetable: Arc::new(SpinNoIrq::new( + PageTable::try_new().expect("failed to create page table"), + )), + #[cfg(feature = "fs")] + fs: Arc::new(SpinNoIrq::new(None)), + #[cfg(feature = "paging")] + mm: Arc::new(MmapStruct::new()), + }; + debug!("new init task: {}", t.id_name()); + + #[cfg(feature = "notest")] + { + #[cfg(feature = "tls")] + let tls = VirtAddr::from(t.tls.tls_ptr() as usize); + #[cfg(not(feature = "tls"))] + let tls = VirtAddr::from(0); + + t.set_stack_top(boot_stack as usize, ruxconfig::TASK_STACK_SIZE); + t.ctx.get_mut().init( + task_entry as usize, + VirtAddr::from(boot_stack as usize), + tls, + ); } + + let task_ref = Arc::new(AxTask::new(t)); + PROCESS_MAP + .lock() + .insert(task_ref.id().as_u64(), task_ref.clone()); + task_ref + } + + /// Create a new idle task. + pub fn new_idle(name: String) -> AxTaskRef { + const IDLE_STACK_SIZE: usize = 4096; + let bindings = PROCESS_MAP.lock(); + let (&_parent_id, task_ref) = bindings.first_key_value().unwrap(); + let idle_kstack = TaskStack::alloc(align_up_4k(IDLE_STACK_SIZE)); + let idle_kstack_top = idle_kstack.top(); + + let mut t = Self { + parent_process: Some(Arc::downgrade(task_ref)), + process_task: task_ref.process_task.clone(), + id: TaskId::new(), + name, + is_idle: true, + is_init: false, + entry: Some(Box::into_raw(Box::new(|| crate::run_idle()))), + state: AtomicU8::new(TaskState::Ready as u8), + in_wait_queue: AtomicBool::new(false), + #[cfg(feature = "irq")] + in_timer_list: AtomicBool::new(false), + #[cfg(feature = "preempt")] + need_resched: AtomicBool::new(false), + #[cfg(feature = "preempt")] + preempt_disable_count: AtomicUsize::new(0), + exit_code: AtomicI32::new(0), + wait_for_exit: WaitQueue::new(), + stack_map_addr: SpinNoIrq::new(idle_kstack.end()), + kstack: SpinNoIrq::new(Arc::new(Some(idle_kstack))), + ctx: UnsafeCell::new(TaskContext::new()), + #[cfg(feature = "tls")] + tls: TlsArea::alloc(), + #[cfg(not(feature = "musl"))] + tsd: spinlock::SpinNoIrq::new([core::ptr::null_mut(); ruxconfig::PTHREAD_KEY_MAX]), + #[cfg(feature = "musl")] + set_tid: AtomicU64::new(0), + #[cfg(feature = "signal")] + signal_if: task_ref.signal_if.clone(), + #[cfg(feature = "musl")] + tl: AtomicU64::new(0), + #[cfg(feature = "paging")] + pagetable: task_ref.pagetable.clone(), + #[cfg(feature = "fs")] + fs: task_ref.fs.clone(), + #[cfg(feature = "paging")] + mm: task_ref.mm.clone(), + }; + + #[cfg(feature = "tls")] + let tls = VirtAddr::from(t.tls.tls_ptr() as usize); + #[cfg(not(feature = "tls"))] + let tls = VirtAddr::from(0); + + debug!("new idle task: {}", t.id_name()); + t.ctx + .get_mut() + .init(task_entry as usize, idle_kstack_top, tls); + Arc::new(AxTask::new(t)) } @@ -374,7 +775,6 @@ impl TaskInner { self.preempt_disable_count.fetch_add(1, Ordering::Relaxed); } - #[inline] #[cfg(feature = "preempt")] pub(crate) fn enable_preempt(&self, resched: bool) { if self.preempt_disable_count.fetch_sub(1, Ordering::Relaxed) == 1 && resched { @@ -448,30 +848,38 @@ impl fmt::Debug for TaskInner { } } -impl Drop for TaskInner { - fn drop(&mut self) { - debug!("task drop: {}", self.id_name()); - } -} - -struct TaskStack { +#[derive(Debug)] +/// A wrapper of TaskStack to provide a safe interface for allocating and +/// deallocating task stacks. +pub struct TaskStack { ptr: NonNull, layout: Layout, } impl TaskStack { + /// Allocate a new task stack with the given size. pub fn alloc(size: usize) -> Self { let layout = Layout::from_size_align(size, 8).unwrap(); - debug!("taskStack::layout = {:?}", layout); Self { ptr: NonNull::new(unsafe { alloc::alloc::alloc(layout) }).unwrap(), layout, } } + /// Deallocate the task stack. pub const fn top(&self) -> VirtAddr { unsafe { core::mem::transmute(self.ptr.as_ptr().add(self.layout.size())) } } + + /// Deallocate the task stack. + pub const fn end(&self) -> VirtAddr { + unsafe { core::mem::transmute(self.ptr.as_ptr()) } + } + + /// Deallocate the task stack. + pub fn size(&self) -> usize { + self.layout.size() + } } impl Drop for TaskStack { @@ -480,8 +888,6 @@ impl Drop for TaskStack { } } -use core::mem::ManuallyDrop; - /// A wrapper of [`AxTaskRef`] as the current task. pub struct CurrentTask(ManuallyDrop); @@ -504,7 +910,8 @@ impl CurrentTask { &self.0 } - pub(crate) fn clone(&self) -> AxTaskRef { + /// clone [`CurrentTask`] as [`AxTaskRef`]. + pub fn clone_as_taskref(&self) -> AxTaskRef { self.0.deref().clone() } @@ -520,6 +927,10 @@ impl CurrentTask { } pub(crate) unsafe fn set_current(prev: Self, next: AxTaskRef) { + debug!( + "-----------set_current-------------,next ptr={:#}", + next.id_name() + ); let Self(arc) = prev; ManuallyDrop::into_inner(arc); // `call Arc::drop()` to decrease prev task reference count. let ptr = Arc::into_raw(next); @@ -541,7 +952,15 @@ extern "C" fn task_entry() -> ! { ruxhal::arch::enable_irqs(); let task = crate::current(); if let Some(entry) = task.entry { - unsafe { Box::from_raw(entry)() }; + unsafe { + let in_entry = Box::from_raw(entry); + in_entry() + }; } crate::exit(0); } + +#[cfg(feature = "notest")] +extern "C" { + fn boot_stack(); +} diff --git a/modules/ruxtask/src/vma.rs b/modules/ruxtask/src/vma.rs new file mode 100644 index 000000000..6a2d5fc4c --- /dev/null +++ b/modules/ruxtask/src/vma.rs @@ -0,0 +1,224 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! Virtual Memory Area (VMA) data structure. +//! +//! This module provides data structures for virtual memory area (VMA) management. +//! TODO: use `Mutex` to replace `SpinNoIrq` to make it more efficient. + +use crate::current; +use crate::fs::get_file_like; +use crate::TaskId; +use alloc::{collections::BTreeMap, sync::Arc}; +use axalloc::global_allocator; +use memory_addr::PhysAddr; +use ruxfs::File; +use ruxhal::mem::phys_to_virt; + +#[cfg(feature = "fs")] +use alloc::vec::Vec; +#[cfg(feature = "fs")] +use memory_addr::PAGE_SIZE_4K; + +use spinlock::SpinNoIrq; + +// use `used_fs` instead of `#[cfg(feature = "fs")]{}` to cancel the scope of code. +#[cfg(feature = "fs")] +macro_rules! used_fs { + ($($code:tt)*) => {$($code)*}; + } + +#[cfg(not(feature = "fs"))] +macro_rules! used_fs { + ($($code:tt)*) => {}; +} + +// TODO: move defination of `SWAP_MAX` and `SWAP_PATH` from const numbers to `ruxconfig`. +used_fs! { + // pub(crate) const SWAP_MAX: usize = 1024 * 1024 * 1024; + pub(crate) const SWAP_MAX: usize = 0; + pub(crate) const SWAP_PATH: &str = "swap.raw\0"; + /// record the mapping of swapped out pages. + pub static SWAPED_MAP: SpinNoIrq>> = SpinNoIrq::new(BTreeMap::new()); // Vaddr => (page_size, offset_at_swaped) + lazy_static::lazy_static! { + /// swap file for swapping out pages. + pub static ref SWAP_FILE: Arc = open_swap_file(SWAP_PATH); + /// bitmap for free pages in swap file. + pub static ref BITMAP_FREE: SpinNoIrq> = SpinNoIrq::new((0..SWAP_MAX).step_by(PAGE_SIZE_4K).collect()); + } +} + +/// open target file +#[cfg(feature = "fs")] +fn open_swap_file(filename: &str) -> Arc { + use crate::fs::absolute_path; + use axfs_vfs::VfsNodePerm; + use ruxfdtable::OpenFlags; + + let opt = OpenFlags::O_RDWR | OpenFlags::O_APPEND | OpenFlags::O_CREAT; + let path = absolute_path(filename).unwrap(); + ruxfs::fops::open_file_like(&path, opt, VfsNodePerm::default_file()) + .expect("create swap file failed") + .into_any() + .downcast::() + .expect("create swap file failed") +} + +/// Data structure for file mapping. +#[derive(Clone)] +pub struct FileInfo { + /// file that the mapping is backed by + pub file: Arc, + /// offset in the file + pub offset: usize, + /// size of the mapping + pub size: usize, +} + +/// Data structure for information of mapping. +pub struct PageInfo { + /// physical address of the page + pub paddr: PhysAddr, + #[cfg(feature = "fs")] + /// file that the mapping is backed by + pub mapping_file: Option, +} + +/// Data structure for swaping out a page in a file. +#[derive(Debug, Clone)] +pub struct SwapInfo { + /// offset in the swap file + pub offset: usize, +} + +impl From for SwapInfo { + fn from(value: usize) -> Self { + SwapInfo { offset: value } + } +} + +/// Data structure for mmap for a specific process. +pub struct MmapStruct { + /// virtual memory area list + pub vma_map: SpinNoIrq>, + /// page that already loaded into memory + pub mem_map: SpinNoIrq>>, + /// pages that swapped out into swap file or disk + pub swaped_map: SpinNoIrq>>, +} + +/// clone data structure for MmapStruct (when forking). +impl Clone for MmapStruct { + fn clone(&self) -> Self { + Self { + vma_map: SpinNoIrq::new(self.vma_map.lock().clone()), + mem_map: SpinNoIrq::new(self.mem_map.lock().clone()), + swaped_map: SpinNoIrq::new(self.swaped_map.lock().clone()), + } + } +} + +// release memory of a page in swaping file +#[cfg(feature = "fs")] +impl Drop for SwapInfo { + fn drop(&mut self) { + BITMAP_FREE.lock().push(self.offset); + } +} + +// release memory of a page in memory +impl Drop for PageInfo { + fn drop(&mut self) { + // use `global_allocator()` to dealloc pages. + global_allocator().dealloc_pages(phys_to_virt(self.paddr).as_usize(), 1); + } +} + +/// Data structure for mapping [start_addr, end_addr) with meta data. +#[derive(Clone)] +pub struct Vma { + /// start address of the mapping + pub start_addr: usize, + /// end address of the mapping + pub end_addr: usize, + /// mmap size of the mapping + pub size: usize, + /// file that the mapping is backed by + pub file: Option>, + /// offset in the file + pub offset: usize, + /// size of the mapping + pub prot: u32, + /// flags of the mapping + pub flags: u32, + /// process that the mapping belongs to + pub from_process: TaskId, +} + +impl MmapStruct { + /// Create a new `MmapStruct` instance. + pub const fn new() -> Self { + Self { + vma_map: SpinNoIrq::new(BTreeMap::new()), + mem_map: SpinNoIrq::new(BTreeMap::new()), + swaped_map: SpinNoIrq::new(BTreeMap::new()), + } + } +} + +impl Default for MmapStruct { + fn default() -> Self { + Self::new() + } +} + +/// Impl for Vma. +impl Vma { + /// Create a new `Vma` instance. + pub fn new(_fid: i32, offset: usize, prot: u32, flags: u32) -> Self { + // #[cfg(feature = "fs")] + let file = if _fid < 0 { + None + } else { + let f = get_file_like(_fid).expect("invaild fd for vma"); + Some( + f.clone() + .into_any() + .downcast::() + .expect("should be effective fid"), + ) + }; + Vma { + start_addr: 0, + end_addr: 0, + size: 0, + // #[cfg(feature = "fs")] + file, + offset, + flags, + prot, + from_process: current().id(), + } + } + + /// Clone a new `Vma` instance. + pub fn clone_from(vma: &Vma, start_addr: usize, end_addr: usize) -> Self { + Vma { + start_addr, + end_addr, + size: vma.size, + // #[cfg(feature = "fs")] + file: vma.file.clone(), + offset: vma.offset, + prot: vma.prot, + flags: vma.prot, + from_process: current().id(), + } + } +} diff --git a/modules/ruxtask/src/wait_queue.rs b/modules/ruxtask/src/wait_queue.rs index 2466bc59c..6f65c0cd6 100644 --- a/modules/ruxtask/src/wait_queue.rs +++ b/modules/ruxtask/src/wait_queue.rs @@ -45,6 +45,12 @@ pub struct WaitQueueWithMetadata { /// A wait queue with no metadata. pub type WaitQueue = WaitQueueWithMetadata<()>; +impl Default for WaitQueueWithMetadata { + fn default() -> Self { + Self::new() + } +} + impl WaitQueueWithMetadata { /// Creates an empty wait queue. pub const fn new() -> Self { @@ -145,7 +151,7 @@ impl WaitQueueWithMetadata { deadline ); #[cfg(feature = "irq")] - crate::timers::set_alarm_wakeup(deadline, curr.clone()); + crate::timers::set_alarm_wakeup(deadline, curr.clone_as_taskref()); RUN_QUEUE.lock().block_current(|task| { task.set_in_wait_queue(true); @@ -178,7 +184,7 @@ impl WaitQueueWithMetadata { deadline ); #[cfg(feature = "irq")] - crate::timers::set_alarm_wakeup(deadline, curr.clone()); + crate::timers::set_alarm_wakeup(deadline, curr.clone_as_taskref()); rq.block_current(|task| { task.set_in_wait_queue(true); @@ -380,7 +386,7 @@ impl WaitQueueWithMetadata { curr.id_name(), deadline ); - crate::timers::set_alarm_wakeup(deadline, curr.clone()); + crate::timers::set_alarm_wakeup(deadline, curr.clone_as_taskref()); let mut timeout = true; while ruxhal::time::current_time() < deadline { diff --git a/modules/ruxvda/Cargo.toml b/modules/ruxvda/Cargo.toml new file mode 100644 index 000000000..f85be3190 --- /dev/null +++ b/modules/ruxvda/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "ruxvda" +version = "0.1.0" +edition = "2021" +authors = ["Zheng Yuanhao "] +description = "RuxOS vda module" +license = "Mulan PSL v2" +homepage = "https://github.com/syswonder/ruxos" +repository = "https://github.com/syswonder/ruxos/tree/main/modules/ruxvda" + +[features] +alloc = ["axalloc"] + +default = [] + +[dependencies] +log = "0.4" +spin = "0.9" +cfg-if = "1.0" +lazy_init = { path = "../../crates/lazy_init" } +capability = { path = "../../crates/capability" } +driver_block = { path = "../../crates/driver_block" } +driver_common = { path = "../../crates/driver_common", optional = true} +axfs_vfs = { path = "../../crates/axfs_vfs" } +axfs_devfs = { path = "../../crates/axfs_devfs", optional = true } +axfs_ramfs = { path = "../../crates/axfs_ramfs", optional = true } +spinlock = { path = "../../crates/spinlock" } +crate_interface = { version = "0.1.1" } +memory_addr = "0.1.0" + +ruxdriver = { path = "../ruxdriver", features = ["block"] } +axalloc = { path = "../axalloc", optional = true } +ruxfs = { path = "../ruxfs" } +ruxtask = { path = "../ruxtask" } \ No newline at end of file diff --git a/modules/ruxvda/src/fs.rs b/modules/ruxvda/src/fs.rs new file mode 100644 index 000000000..6d6c33da8 --- /dev/null +++ b/modules/ruxvda/src/fs.rs @@ -0,0 +1,258 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! VDA filesystem used by [Ruxos](https://github.com/syswonder/ruxos). +//! +//! The implementation is based on [`axfs_vfs`]. + +#![allow(dead_code)] + +use alloc::vec; +use alloc::{sync::Arc, sync::Weak}; +use axfs_vfs::{ + RelPath, VfsError, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, VfsOps, + VfsResult, +}; +use log::*; + +#[allow(unused_imports)] +use ruxdriver::prelude::BlockDriverOps; + +use ruxdriver::AxBlockDevice; +use spin::{once::Once, RwLock}; + +const BLOCK_SIZE: usize = 512; + +/// A VDA filesystem that implements [`axfs_vfs::VfsOps`]. +pub struct VdaFileSystem { + parent: Once, + root: Arc, +} + +impl VdaFileSystem { + /// Create a new VDA filesystem. + pub fn new(dev: AxBlockDevice) -> Self { + info!("Create VDA filesystem"); + Self { + parent: Once::new(), + root: VdaNode::new(None, dev), + } + } +} + +impl VfsOps for VdaFileSystem { + fn mount(&self, parent: VfsNodeRef) -> VfsResult { + self.root.set_parent(Some(self.parent.call_once(|| parent))); + Ok(()) + } + + fn root_dir(&self) -> VfsNodeRef { + debug!("Get root directory of VDA filesystem"); + self.root.clone() + } +} + +/// It implements [`axfs_vfs::VfsNodeOps`]. +pub struct VdaNode { + this: Weak, + parent: RwLock>, + transport: Arc>, +} + +impl VdaNode { + pub(super) fn new(parent: Option>, dev: AxBlockDevice) -> Arc { + debug!("Create VDA node"); + Arc::new_cyclic(|this| Self { + this: this.clone(), + parent: RwLock::new(parent.unwrap_or_else(|| Weak::::new())), + transport: Arc::new(RwLock::new(dev)), + }) + } + + pub(super) fn set_parent(&self, parent: Option<&VfsNodeRef>) { + *self.parent.write() = parent.map_or(Weak::::new() as _, Arc::downgrade); + } +} + +impl VfsNodeOps for VdaNode { + fn open(&self) -> VfsResult> { + debug!("Open VDA node"); + Ok(None) + } + + fn lookup(self: Arc, path: &RelPath) -> VfsResult { + debug!("Lookup VDA node {path:?}"); + Ok(self) + } + + fn get_attr(&self) -> VfsResult { + debug!("Get VDA node attributes"); + Ok(VfsNodeAttr::new( + 0, + VfsNodePerm::from_bits_truncate(0o777), + VfsNodeType::BlockDevice, + 67108864, + 131072, + )) + } + + fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { + debug!( + "Read VDA node offset: {:?}, % = {:?}, buf_len: {:?}, last: {:?}", + offset, + offset % BLOCK_SIZE as u64, + buf.len(), + (offset + buf.len() as u64) % BLOCK_SIZE as u64 + ); + + let mut dev = self.transport.write(); + let mut cur_offset = offset; + let mut pos = 0; + let mut remain = buf.len(); + let mut temp_buf = vec![0u8; BLOCK_SIZE]; + + // Read the first block + if offset % BLOCK_SIZE as u64 != 0 || remain < BLOCK_SIZE { + let start = cur_offset as usize % 512; + let end = BLOCK_SIZE.min(start + remain); + let copy_len = end - start; + let ret = dev.read_block(cur_offset / 512, &mut temp_buf); + if ret.is_err() { + return Err(VfsError::PermissionDenied); + } + buf[pos..pos + copy_len].copy_from_slice(&temp_buf[start..end]); + debug!( + "copy_len: {copy_len:?}, cur_offset: {cur_offset:?}, pos: {pos:?}, remain: {remain:?}" + ); + cur_offset += copy_len as u64; + remain -= copy_len; + pos += copy_len; + } + + // Read the whole block + while remain >= BLOCK_SIZE { + debug!( + "cur_offset: {:?}, cur_offset % 512 = {:?} = 0!!!", + cur_offset, + cur_offset % 512 + ); + let ret = dev.read_block(cur_offset / 512, &mut buf[pos..pos + BLOCK_SIZE]); + if ret.is_err() { + return Err(VfsError::PermissionDenied); + } + debug!( + "copy_len: {BLOCK_SIZE:?}, cur_offset: {cur_offset:?}, pos: {pos:?}, remain: {remain:?}" + ); + cur_offset += BLOCK_SIZE as u64; + remain -= BLOCK_SIZE; + pos += BLOCK_SIZE; + } + + // Read the last block + if remain > 0 { + debug!( + "cur_offset: {:?}, cur_offset % 512 = {:?} = 0!!!", + cur_offset, + cur_offset % 512 + ); + let start = cur_offset as usize % 512; + let copy_len = remain.min(BLOCK_SIZE); + let end = start + copy_len; + let ret = dev.read_block(cur_offset / 512, &mut temp_buf); + if ret.is_err() { + return Err(VfsError::PermissionDenied); + } + buf[pos..pos + copy_len].copy_from_slice(&temp_buf[start..end]); + debug!( + "copy_len: {copy_len:?}, cur_offset: {cur_offset:?}, pos: {pos:?}, remain: {remain:?}" + ); + cur_offset += copy_len as u64; + remain -= copy_len; + pos += copy_len; + } + + debug!("cur_offset - offset - buf.len() = {:?} = 0!!, remain: {:?} = 0!!, pos - buf.len() = {:?} = 0!!", cur_offset - offset - buf.len() as u64, remain, pos - buf.len()); + + Ok(buf.len()) + } + + fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { + debug!("Write VDA node offset: {:?}, buf: {:?}", offset, buf.len()); + + let mut dev = self.transport.write(); + let mut cur_offset = offset; + let mut pos = 0; + let mut remain = buf.len(); + let mut temp_buf = vec![0u8; BLOCK_SIZE]; + + // Write the first block + if offset % BLOCK_SIZE as u64 != 0 || remain < BLOCK_SIZE { + let start = cur_offset as usize % 512; + let end = BLOCK_SIZE.min(start + remain); + let copy_len = end - start; + temp_buf[start..end].copy_from_slice(&buf[pos..pos + copy_len]); + let ret = dev.write_block(cur_offset / 512, &temp_buf); + if ret.is_err() { + return Err(VfsError::PermissionDenied); + } + debug!( + "copy_len: {copy_len:?}, cur_offset: {cur_offset:?}, pos: {pos:?}, remain: {remain:?}" + ); + cur_offset += copy_len as u64; + remain -= copy_len; + pos += copy_len; + } + + // Write the whole block + while remain >= BLOCK_SIZE { + debug!( + "cur_offset: {:?}, cur_offset % 512 = {:?} = 0!!!", + cur_offset, + cur_offset % 512 + ); + let ret = dev.write_block(cur_offset / 512, &buf[pos..pos + BLOCK_SIZE]); + if ret.is_err() { + return Err(VfsError::PermissionDenied); + } + debug!( + "copy_len: {BLOCK_SIZE:?}, cur_offset: {cur_offset:?}, pos: {pos:?}, remain: {remain:?}" + ); + cur_offset += BLOCK_SIZE as u64; + remain -= BLOCK_SIZE; + pos += BLOCK_SIZE; + } + + // Write the last block + if remain > 0 { + debug!( + "cur_offset: {:?}, cur_offset % 512 = {:?} = 0!!!", + cur_offset, + cur_offset % 512 + ); + let start = cur_offset as usize % 512; + let copy_len = remain.min(BLOCK_SIZE); + let end = start + copy_len; + temp_buf[start..end].copy_from_slice(&buf[pos..pos + copy_len]); + let ret = dev.write_block(cur_offset / 512, &temp_buf); + if ret.is_err() { + return Err(VfsError::PermissionDenied); + } + debug!( + "copy_len: {copy_len:?}, cur_offset: {cur_offset:?}, pos: {pos:?}, remain: {remain:?}" + ); + cur_offset += copy_len as u64; + remain -= copy_len; + pos += copy_len; + } + + debug!("cur_offset - offset - buf.len() = {:?} = 0!!, remain: {:?} = 0!!, pos - buf.len() = {:?} = 0!!", cur_offset - offset - buf.len() as u64, remain, pos - buf.len()); + + Ok(buf.len()) + } +} diff --git a/modules/ruxvda/src/lib.rs b/modules/ruxvda/src/lib.rs new file mode 100644 index 000000000..99a4c0ef1 --- /dev/null +++ b/modules/ruxvda/src/lib.rs @@ -0,0 +1,36 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +//! vda filesystem module for ruxos. + +#![cfg_attr(all(not(test), not(doc)), no_std)] +#![feature(doc_auto_cfg)] + +extern crate alloc; +extern crate log; + +pub mod fs; + +use alloc::string::String; +use alloc::sync::Arc; +use log::*; +use ruxdriver::{prelude::*, AxDeviceContainer}; +use ruxfs::MountPoint; + +/// Initializes filesystems by block devices. +pub fn init_vdafs(mut vda_devs: AxDeviceContainer) -> MountPoint { + info!("Initialize VDA filesystem..."); + + let vda = vda_devs.take_one().expect("No VDA device found!"); + info!(" use VDA device 0: {:?}", vda.device_name()); + + let vda_fs = self::fs::VdaFileSystem::new(vda); + + MountPoint::new(String::from("/vda1"), Arc::new(vda_fs)) +} diff --git a/platforms/aarch64-qemu-virt.toml b/platforms/aarch64-qemu-virt.toml index 03474a9f8..61bf6ab0c 100644 --- a/platforms/aarch64-qemu-virt.toml +++ b/platforms/aarch64-qemu-virt.toml @@ -17,8 +17,10 @@ phys-memory-size = "0x8000_0000" # 2G # phys-memory-size = "0x2_0000_0000" # 8G # Base physical address of the kernel image. kernel-base-paddr = "0x4008_0000" +#kernel-base-paddr = "0x4000_0000" # Base virtual address of the kernel image. kernel-base-vaddr = "0xffff_0000_4008_0000" +#kernel-base-vaddr = "0xffff_0000_4000_0000" # virtual start address of the mapping memory. mmap-start-vaddr = "0xffff_8000_0000_0000" # virtual end address of the mapping memory. @@ -29,7 +31,8 @@ phys-virt-offset = "0xffff_0000_0000_0000" # MMIO regions with format (`base_paddr`, `size`). mmio-regions = [ ["0x0900_0000", "0x1000"], # PL011 UART - ["0x0800_0000", "0x2_0000"], # GICv2 + ["0x0800_0000", "0x10_0000"], # GICv2 + #["0x080a_0000", "0x2_0000"], # GICv3 ["0x0a00_0000", "0x4000"], # VirtIO ["0x1000_0000", "0x2eff_0000"], # PCI memory ranges (ranges 1: 32-bit MMIO space) ["0x40_1000_0000", "0x1000_0000"], # PCI config space @@ -81,12 +84,27 @@ pci-ranges = [ ["0x80_0000_0000", "0x80_0000_0000"], # 64-but MMIO space ] # UART Address -uart-paddr = "0x0900_0000" +uart-paddr = "0x9000000" uart-irq = "1" +# Virtio console +virtio-console-paddr = "0xa003e00" +# Virtio console IRQ +virtio-console-irq = "47" + # GICC Address gicc-paddr = "0x0801_0000" gicd-paddr = "0x0800_0000" +gicr-paddr = "0x080a_0000" # PSCI psci-method = "hvc" + +# Pipe channel bufer size. +pipe-buffer-size = "0x10000" + +# Device tree blob address +DTB_ADDR = "0x40000000" + +# Default buffer size used for pty input/output +pty-buffer-capacity = "4096" diff --git a/platforms/aarch64-raspi4.toml b/platforms/aarch64-raspi4.toml index ef2e20cc8..6b8a3b1a3 100644 --- a/platforms/aarch64-raspi4.toml +++ b/platforms/aarch64-raspi4.toml @@ -31,6 +31,20 @@ virtio-mmio-regions = [] uart-paddr = "0xFE20_1000" uart-irq = "0x79" +# Virtio console +virtio-console-paddr = "0xFFFFFFFF" +# Virtio console irq +virtio-console-irq = "0" + # GIC Address gicc-paddr = "0xFF84_2000" gicd-paddr = "0xFF84_1000" + +# Pipe channel bufer size. +pipe-buffer-size = "0x10000" + +# Device tree blob address +DTB_ADDR = "0x40000000" + +# Default buffer size used for pty input/output +pty-buffer-capacity = "4096" diff --git a/platforms/riscv64-qemu-virt.toml b/platforms/riscv64-qemu-virt.toml index 3589913f2..508923a90 100644 --- a/platforms/riscv64-qemu-virt.toml +++ b/platforms/riscv64-qemu-virt.toml @@ -21,9 +21,9 @@ kernel-base-paddr = "0x8020_0000" # Base virtual address of the kernel image. kernel-base-vaddr = "0xffff_ffc0_8020_0000" # virtual start address of the mapping memory. -mmap-start-vaddr = "0xffff_8000_0000_0000" +mmap-start-vaddr = "0xffff_ffc8_0000_0000" # virtual end address of the mapping memory. -mmap-end-vaddr = "0xffff_f000_0000_0000" +mmap-end-vaddr = "0xffff_ffff_0000_0000" # Linear mapping offset, for quick conversions between physical and virtual # addresses. phys-virt-offset = "0xffff_ffc0_0000_0000" @@ -57,5 +57,16 @@ pci-ranges = [ ["0x4_0000_0000", "0x4_0000_0000"], # 64-but MMIO space ] +# Virtio console +virtio-console-paddr = "0xFFFFFFFF" +# Virtio console irq +virtio-console-irq = "0" + # Timer interrupt frequency in Hz. timer-frequency = "10_000_000" # 10MHz + +# Pipe channel bufer size. +pipe-buffer-size = "0x10000" + +# Default buffer size used for pty input/output +pty-buffer-capacity = "4096" diff --git a/platforms/x86_64-qemu-q35.toml b/platforms/x86_64-qemu-q35.toml index a3a119758..5f4b10bc6 100644 --- a/platforms/x86_64-qemu-q35.toml +++ b/platforms/x86_64-qemu-q35.toml @@ -38,5 +38,16 @@ pci-bus-end = "0xff" # PCI device memory ranges (not used on x86). pci-ranges = [] +# Virtio console +virtio-console-paddr = "0xFFFFFFFF" +# Virtio console irq +virtio-console-irq = "0" + # Timer interrupt frequencyin Hz. timer-frequency = "4_000_000_000" # 4.0GHz + +# Pipe channel bufer size. +pipe-buffer-size = "0x10000" + +# Default buffer size used for pty input/output +pty-buffer-capacity = "4096" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index b789cc033..a9547a6e2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,7 @@ [toolchain] profile = "minimal" -channel = "nightly-2023-12-01" +channel = "nightly-2025-05-07" components = ["rust-src", "llvm-tools-preview", "rustfmt", "clippy"] -targets = ["x86_64-unknown-none", "riscv64gc-unknown-none-elf", "aarch64-unknown-none-softfloat"] +targets = ["x86_64-unknown-none", "riscv64gc-unknown-none-elf", "aarch64-unknown-none"] + + diff --git a/scripts/make/build.mk b/scripts/make/build.mk index aee2098af..5308eee35 100644 --- a/scripts/make/build.mk +++ b/scripts/make/build.mk @@ -41,11 +41,7 @@ ifeq ($(APP_TYPE), rust) $(call cargo_build,--manifest-path $(APP)/Cargo.toml,$(RUX_FEAT) $(LIB_FEAT) $(APP_FEAT)) @cp $(rust_elf) $(OUT_ELF) else ifeq ($(APP_TYPE), c) - ifeq ($(MUSL), y) - $(call cargo_build,-p ruxmusl,$(RUX_FEAT) $(LIB_FEAT)) - else - $(call cargo_build,-p ruxlibc,$(RUX_FEAT) $(LIB_FEAT)) - endif + $(call cargo_build,-p ruxmusl,$(RUX_FEAT) $(LIB_FEAT) $(LOG_FEAT)) endif $(OUT_DIR): diff --git a/scripts/make/build_c.mk b/scripts/make/build_c.mk index d1522f98a..148de9c82 100644 --- a/scripts/make/build_c.mk +++ b/scripts/make/build_c.mk @@ -1,7 +1,7 @@ -rust_lib_name := ruxlibc +rust_lib_name := ruxmusl rust_lib := target/$(TARGET)/$(MODE)/lib$(rust_lib_name).a -ulib_dir := ulib/ruxlibc +ulib_dir := ulib/ruxmusl src_dir := $(ulib_dir)/c obj_dir := $(ulib_dir)/build_$(ARCH) inc_dir := $(ulib_dir)/include diff --git a/scripts/make/cargo.mk b/scripts/make/cargo.mk index 308896d27..bf4f36758 100644 --- a/scripts/make/cargo.mk +++ b/scripts/make/cargo.mk @@ -29,25 +29,25 @@ ifeq ($(MAKECMDGOALS), doc_check_missing) endif define cargo_build - $(call run_cmd,cargo build,$(build_args) $(1) --features "$(strip $(2))") + $(call run_cmd,cargo build -Z unstable-options,$(build_args) $(1) --features "$(strip $(2))") endef define cargo_clippy $(call run_cmd,cargo clippy,--all-features --workspace --exclude axlog $(1) $(verbose)) - $(call run_cmd,cargo clippy,-p axlog -p percpu -p percpu_macros $(1) $(verbose)) + $(call run_cmd,cargo clippy,-p axlog $(1) $(verbose)) endef all_packages := \ $(shell ls $(CURDIR)/crates) \ $(shell ls $(CURDIR)/modules) \ - ruxfeat arceos_api axstd ruxlibc ruxmusl + ruxfeat arceos_api axstd ruxmusl define cargo_doc - $(call run_cmd,cargo doc,--no-deps --all-features --workspace --exclude "arceos-*" --exclude "ruxos-*" --exclude "ruxos-*" $(verbose)) + $(call run_cmd,cargo doc,--no-deps --all-features --workspace --exclude "arceos-*" --exclude "ruxos-*" --exclude "ruxos-*" --exclude "axlog" $(verbose)) @# run twice to fix broken hyperlinks $(foreach p,$(all_packages), \ - $(call run_cmd,cargo rustdoc,--all-features -p $(p) $(verbose)) + $(if $(filter axlog,$(p)),,$(call run_cmd,cargo rustdoc,--all-features -p $(p) $(verbose))) ) + @# for some crates, re-generate without `--all-features` - $(call run_cmd,cargo doc,--no-deps -p percpu $(verbose)) endef diff --git a/scripts/make/features.mk b/scripts/make/features.mk index 4a07c7e5c..5c7da2450 100644 --- a/scripts/make/features.mk +++ b/scripts/make/features.mk @@ -3,21 +3,17 @@ # Inputs: # - `FEATURES`: a list of features to be enabled split by spaces or commas. # The features can be selected from the crate `ruxfeat` or the user library -# (crate `axstd` or `ruxlibc`). +# (crate `axstd` or `ruxmusl`). # - `APP_FEATURES`: a list of features to be enabled for the Rust app. # # Outputs: # - `RUX_FEAT`: features to be enabled for Ruxos modules (crate `ruxfeat`). -# - `LIB_FEAT`: features to be enabled for the user library (crate `axstd`, `ruxlibc`, `ruxmusl`). +# - `LIB_FEAT`: features to be enabled for the user library (crate `axstd`, `ruxmusl`, `ruxmusl`). # - `APP_FEAT`: features to be enabled for the Rust app. ifeq ($(APP_TYPE),c) ax_feat_prefix := ruxfeat/ - ifeq ($(MUSL), y) - lib_feat_prefix := ruxmusl/ - else - lib_feat_prefix := ruxlibc/ - endif + lib_feat_prefix := ruxmusl/ lib_features := fp_simd alloc irq sched_rr paging multitask fs net fd pipe select poll epoll random-hw signal else # TODO: it's better to use `ruxfeat/` as `ax_feat_prefix`, but all apps need to have `ruxfeat` as a dependency @@ -30,8 +26,14 @@ ifeq ($(APP_TYPE),c) lib_features += musl endif endif +log_feat_prefix := axlog/ override FEATURES := $(shell echo $(FEATURES) | tr ',' ' ') +# TODO: remove below features when it is possible +override FEATURES += multitask +override FEATURES += fs +override FEATURES += paging +override FEATURES += alloc ifeq ($(APP_TYPE), c) ifneq ($(wildcard $(APP)/features.txt),) # check features.txt exists @@ -49,13 +51,18 @@ ifeq ($(APP_TYPE), c) endif endif +ifeq ($(GICV3),y) + override FEATURES += gic-v3 +endif + override FEATURES := $(strip $(FEATURES)) ax_feat := lib_feat := +log_feat := ifneq ($(filter $(LOG),off error warn info debug trace),) - ax_feat += log-level-$(LOG) + log_feat += log-level-$(LOG) else $(error "LOG" must be one of "off", "error", "warn", "info", "debug", "trace") endif @@ -74,3 +81,4 @@ lib_feat += $(filter $(lib_features),$(FEATURES)) RUX_FEAT := $(strip $(addprefix $(ax_feat_prefix),$(ax_feat))) LIB_FEAT := $(strip $(addprefix $(lib_feat_prefix),$(lib_feat))) APP_FEAT := $(strip $(shell echo $(APP_FEATURES) | tr ',' ' ')) +LOG_FEAT := $(strip $(addprefix $(log_feat_prefix),$(log_feat))) diff --git a/scripts/make/qemu.mk b/scripts/make/qemu.mk index 5a6340438..54024b3c0 100644 --- a/scripts/make/qemu.mk +++ b/scripts/make/qemu.mk @@ -24,14 +24,28 @@ qemu_args-riscv64 := \ -bios $(RISCV_BIOS) \ -kernel $(OUT_BIN) -qemu_args-aarch64 := \ +ifeq ($(GICV3), y) + qemu_args-aarch64 := \ -cpu cortex-a72 \ - -machine virt \ + -machine virt,gic-version=3 \ + -kernel $(OUT_BIN) +else + qemu_args-aarch64 := \ + -cpu cortex-a72 \ + -machine virt,gic-version=2 \ -kernel $(OUT_BIN) +endif qemu_args-y := -m 2G -smp $(SMP) $(qemu_args-$(ARCH)) \ -append ";$(ARGS);$(ENVS)" +qemu_args-$(CONSOLE) += \ + -global virtio-mmio.force-legacy=false \ + -device virtio-serial-device,id=virtio-serial0 \ + -chardev stdio,id=char0,mux=on\ + -device virtconsole,chardev=char0 \ + -serial chardev:char0 + qemu_args-$(BLK) += \ -device virtio-blk-$(vdev-suffix),drive=disk0 \ -drive id=disk0,if=none,format=raw,file=$(DISK_IMG) @@ -59,7 +73,9 @@ qemu_args-$(GRAPHIC) += \ -device virtio-gpu-$(vdev-suffix) -vga none \ -serial mon:stdio -ifeq ($(GRAPHIC), n) +ifeq ($(CONSOLE), y) + qemu_args-y += -display none +else ifeq ($(GRAPHIC), n) qemu_args-y += -nographic endif diff --git a/scripts/make/test.mk b/scripts/make/test.mk index b132654a6..43f4f18f0 100644 --- a/scripts/make/test.mk +++ b/scripts/make/test.mk @@ -1,8 +1,10 @@ # Test scripts +# for unit tests, try to run the tests + define unit_test - $(call run_cmd,cargo test,-p percpu $(1) -- --nocapture) - $(call run_cmd,cargo test,-p ruxfs $(1) --features "myfs" -- --nocapture) + $(call run_cmd,cargo test,-p ruxruntime $(1) --features "myfs" -- --nocapture) + $(call run_cmd,cargo test,-p ruxruntime $(1) --features "fatfs" -- --nocapture) $(call run_cmd,cargo test,--workspace --exclude lwip_rust --exclude "arceos-*" --exclude "ruxos-*" $(1) -- --nocapture) endef diff --git a/scripts/make/utils.mk b/scripts/make/utils.mk index 21bac486f..db348cb56 100644 --- a/scripts/make/utils.mk +++ b/scripts/make/utils.mk @@ -18,6 +18,20 @@ define make_disk_image_fat32 @mkfs.fat -F 32 $(1) endef +define make_disk_image_ext4 + @printf " $(GREEN_C)Creating$(END_C) EXT4 disk image \"$(1)\" ...\n" + @dd if=/dev/zero of=$(1) bs=1M count=1024 + @mkfs.ext4 -b 4096 $(1) +endef + +define make_disk_image_exfat + @printf " $(GREEN_C)Creating$(END_C) exFAT disk image \"$(1)\" ...\n" + @dd if=/dev/zero of=$(1) bs=1M count=64 + @mkfs.exfat $(1) +endef + define make_disk_image $(if $(filter $(1),fat32), $(call make_disk_image_fat32,$(2))) + $(if $(filter $(1),ext4), $(call make_disk_image_ext4,$(2))) + $(if $(filter $(1),exfat), $(call make_disk_image_exfat,$(2))) endef diff --git a/scripts/test/app_test.sh b/scripts/test/app_test.sh index b81ae0885..598ffcf53 100755 --- a/scripts/test/app_test.sh +++ b/scripts/test/app_test.sh @@ -108,8 +108,8 @@ if [ -z "$1" ]; then test_list=( "apps/c/helloworld" "apps/c/memtest" - "apps/c/sqlite3" "apps/c/httpclient" + "apps/c/sqlite3" "apps/c/pthread/basic" "apps/c/pthread/sleep" "apps/c/pthread/pipe" diff --git a/scripts/test/license_checker.sh b/scripts/test/license_checker.sh new file mode 100644 index 000000000..0e6305f25 --- /dev/null +++ b/scripts/test/license_checker.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +copyright_line="Copyright (c)" +exit_code=0 + +# Use git ls-files to find all Git-tracked .rs files +# --cached: Only show files in the index (i.e., Git-tracked files) +# --exclude-standard: Respect standard exclusion patterns (.gitignore, .git/info/exclude, etc.) +# -- *.rs: Limit the search to .rs files only +while IFS= read -r file; do + # Ensure the file exists and is a regular file (git ls-files usually returns correct file paths) + if [[ -f "$file" ]]; then + if ! grep -qF "$copyright_line" "$file"; then + echo "Error: File does not contain the required copyright line: $file" + exit_code=1 + fi + else + # Theoretically, git ls-files should not return non-existent files, but kept for robustness + echo "Warning: git ls-files returned a non-existent file path: $file" + fi +done < <(git ls-files --cached --exclude-standard -- "*.rs" "*.c" "*.h") + +echo "Script finished with exit code: $exit_code" +exit "$exit_code" diff --git a/ulib/axstd/Cargo.toml b/ulib/axstd/Cargo.toml index 8f57a458c..cb4108e8a 100644 --- a/ulib/axstd/Cargo.toml +++ b/ulib/axstd/Cargo.toml @@ -36,7 +36,7 @@ alloc = ["arceos_api/alloc", "ruxfeat/alloc", "axio/alloc"] alloc-tlsf = ["ruxfeat/alloc-tlsf"] alloc-slab = ["ruxfeat/alloc-slab"] alloc-buddy = ["ruxfeat/alloc-buddy"] -paging = ["ruxfeat/paging"] +paging = ["ruxfeat/paging", "arceos_api/paging"] tls = ["ruxfeat/tls"] # Multi-threading and scheduler @@ -50,7 +50,13 @@ fs = ["arceos_api/fs", "ruxfeat/fs"] myfs = ["arceos_api/myfs", "ruxfeat/myfs"] blkfs = ["ruxfeat/blkfs"] virtio-9p = ["ruxfeat/virtio-9p"] +virtio_console = ["ruxfeat/virtio_console"] net-9p = ["ruxfeat/net-9p"] +fatfs = ["blkfs", "ruxfeat/fatfs"] +# TODO: wait for CI support for ext4 +# lwext4_rust = ["blkfs", "ruxfeat/lwext4_rust"] +ext4_rs = ["blkfs", "ruxfeat/ext4_rs"] +another_ext4 = ["blkfs", "ruxfeat/another_ext4"] # Networking net = ["arceos_api/net", "ruxfeat/net"] @@ -66,17 +72,12 @@ driver-ramdisk = ["ruxfeat/driver-ramdisk"] driver-ixgbe = ["ruxfeat/driver-ixgbe"] driver-bcm2835-sdhci = ["ruxfeat/driver-bcm2835-sdhci"] -# Logging -log-level-off = ["ruxfeat/log-level-off"] -log-level-error = ["ruxfeat/log-level-error"] -log-level-warn = ["ruxfeat/log-level-warn"] -log-level-info = ["ruxfeat/log-level-info"] -log-level-debug = ["ruxfeat/log-level-debug"] -log-level-trace = ["ruxfeat/log-level-trace"] + [dependencies] ruxfeat = { path = "../../api/ruxfeat" } arceos_api = { path = "../../api/arceos_api" } axio = { path = "../../crates/axio" } +axlog = { path = "../../crates/axlog" } axerrno = { path = "../../crates/axerrno" } spinlock = { path = "../../crates/spinlock" } diff --git a/ulib/axstd/src/fs/dir.rs b/ulib/axstd/src/fs/dir.rs index 2f60a9a12..74dd7ddae 100644 --- a/ulib/axstd/src/fs/dir.rs +++ b/ulib/axstd/src/fs/dir.rs @@ -11,6 +11,7 @@ extern crate alloc; use alloc::string::String; use core::fmt; +use core::mem::MaybeUninit; use super::FileType; use crate::io::Result; @@ -46,8 +47,15 @@ impl<'a> ReadDir<'a> { opts.read(true); let inner = api::ax_open_dir(path, &opts)?; - const EMPTY: api::AxDirEntry = api::AxDirEntry::default(); - let dirent_buf = [EMPTY; 31]; + let dirent_buf: [api::AxDirEntry; 31] = unsafe { + let mut buf: MaybeUninit<[api::AxDirEntry; 31]> = MaybeUninit::uninit(); + let buf_ptr = buf.as_mut_ptr() as *mut api::AxDirEntry; + for i in 0..31 { + buf_ptr.add(i).write(api::AxDirEntry::default()); + } + buf.assume_init() + }; + Ok(ReadDir { path, inner, @@ -86,11 +94,10 @@ impl<'a> Iterator for ReadDir<'a> { } let entry = &self.dirent_buf[self.buf_pos]; self.buf_pos += 1; - let name_bytes = entry.name_as_bytes(); - if name_bytes == b"." || name_bytes == b".." { + let entry_name = entry.name_as_string(); + if entry_name == "." || entry_name == ".." { continue; } - let entry_name = unsafe { core::str::from_utf8_unchecked(name_bytes).into() }; let entry_type = entry.entry_type(); return Some(Ok(DirEntry { diff --git a/ulib/axstd/src/fs/file.rs b/ulib/axstd/src/fs/file.rs index e89a5a604..9b6ce616c 100644 --- a/ulib/axstd/src/fs/file.rs +++ b/ulib/axstd/src/fs/file.rs @@ -31,6 +31,12 @@ pub struct Metadata(api::AxFileAttr); #[derive(Clone, Debug)] pub struct OpenOptions(api::AxOpenOptions); +impl Default for OpenOptions { + fn default() -> Self { + Self::new() + } +} + impl OpenOptions { /// Creates a blank new set of options ready for configuration. pub const fn new() -> Self { @@ -80,6 +86,11 @@ impl OpenOptions { } impl Metadata { + /// Wraps an `AxFileAttr` in a `Metadata` object. + pub(super) const fn new(attr: api::AxFileAttr) -> Self { + Metadata(attr) + } + /// Returns the file type for this metadata. pub const fn file_type(&self) -> FileType { self.0.file_type() diff --git a/ulib/axstd/src/fs/mod.rs b/ulib/axstd/src/fs/mod.rs index 85815aa28..a0cf6d273 100644 --- a/ulib/axstd/src/fs/mod.rs +++ b/ulib/axstd/src/fs/mod.rs @@ -48,7 +48,7 @@ pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result< /// Given a path, query the file system to get information about a file, /// directory, etc. pub fn metadata>(path: P) -> io::Result { - File::open(path.as_ref())?.metadata() + arceos_api::fs::ax_get_attr(path.as_ref()).map(Metadata::new) } /// Returns an iterator over the entries within a directory. diff --git a/ulib/axstd/src/lib.rs b/ulib/axstd/src/lib.rs index 1d24dc793..3c85eb968 100644 --- a/ulib/axstd/src/lib.rs +++ b/ulib/axstd/src/lib.rs @@ -58,7 +58,6 @@ #![cfg_attr(all(not(test), not(doc)), no_std)] #![feature(doc_cfg)] #![feature(doc_auto_cfg)] -#![feature(ip_in_core)] #[cfg(feature = "alloc")] extern crate alloc; diff --git a/ulib/axstd/src/sync/mutex.rs b/ulib/axstd/src/sync/mutex.rs index 2cfc701d1..63972db75 100644 --- a/ulib/axstd/src/sync/mutex.rs +++ b/ulib/axstd/src/sync/mutex.rs @@ -92,8 +92,7 @@ impl Mutex { Err(owner_id) => { assert_ne!( owner_id, current_id, - "Thread({}) tried to acquire mutex it already owns.", - current_id, + "Thread({current_id}) tried to acquire mutex it already owns." ); // Wait until the lock looks unlocked before retrying api::ax_wait_queue_wait(&self.wq, || !self.is_locked(), None); @@ -138,8 +137,7 @@ impl Mutex { let current_id = api::ax_current_task_id(); assert_eq!( owner_id, current_id, - "Thread({}) tried to release mutex it doesn't own", - current_id, + "Thread({current_id}) tried to release mutex it doesn't own", ); // wake up one waiting thread. api::ax_wait_queue_wake(&self.wq, 1); @@ -158,7 +156,7 @@ impl Mutex { } } -impl Default for Mutex { +impl Default for Mutex { #[inline(always)] fn default() -> Self { Self::new(Default::default()) diff --git a/ulib/axstd/src/thread/multi.rs b/ulib/axstd/src/thread/multi.rs index 6da1b472a..16faddd87 100644 --- a/ulib/axstd/src/thread/multi.rs +++ b/ulib/axstd/src/thread/multi.rs @@ -60,6 +60,12 @@ pub struct Builder { stack_size: Option, } +impl Default for Builder { + fn default() -> Self { + Self::new() + } +} + impl Builder { /// Generates the base configuration for spawning a thread, from which /// configuration methods can be chained. diff --git a/ulib/include/.gitignore b/ulib/include/.gitignore new file mode 100644 index 000000000..9a162c2b8 --- /dev/null +++ b/ulib/include/.gitignore @@ -0,0 +1,2 @@ +ax_pthread_cond.h +ax_pthread_mutex.h diff --git a/ulib/ruxlibc/include/arpa/inet.h b/ulib/include/arpa/inet.h similarity index 100% rename from ulib/ruxlibc/include/arpa/inet.h rename to ulib/include/arpa/inet.h diff --git a/ulib/ruxlibc/include/assert.h b/ulib/include/assert.h similarity index 100% rename from ulib/ruxlibc/include/assert.h rename to ulib/include/assert.h diff --git a/ulib/ruxlibc/c/utsname.c b/ulib/include/byteswap.h similarity index 81% rename from ulib/ruxlibc/c/utsname.c rename to ulib/include/byteswap.h index 7aeecd9e6..7f07370e9 100644 --- a/ulib/ruxlibc/c/utsname.c +++ b/ulib/include/byteswap.h @@ -7,12 +7,10 @@ * See the Mulan PSL v2 for more details. */ -#include -#include +#ifndef _BYTESWAP_H +#define _BYTESWAP_H -// TODO -int uname(struct utsname *a) -{ - unimplemented(); - return 0; -} +#include +#include + +#endif diff --git a/ulib/ruxlibc/ctypes.h b/ulib/include/crypt.h similarity index 92% rename from ulib/ruxlibc/ctypes.h rename to ulib/include/crypt.h index f1c1cc8b1..a915a9fa1 100644 --- a/ulib/ruxlibc/ctypes.h +++ b/ulib/include/crypt.h @@ -7,5 +7,7 @@ * See the Mulan PSL v2 for more details. */ -#include -#include +#ifndef _CRYPT_H +#define _CRYPT_H + +#endif diff --git a/ulib/ruxlibc/include/ctype.h b/ulib/include/ctype.h similarity index 100% rename from ulib/ruxlibc/include/ctype.h rename to ulib/include/ctype.h diff --git a/ulib/ruxlibc/include/dirent.h b/ulib/include/dirent.h similarity index 100% rename from ulib/ruxlibc/include/dirent.h rename to ulib/include/dirent.h diff --git a/ulib/ruxlibc/include/dlfcn.h b/ulib/include/dlfcn.h similarity index 100% rename from ulib/ruxlibc/include/dlfcn.h rename to ulib/include/dlfcn.h diff --git a/ulib/ruxlibc/include/endian.h b/ulib/include/endian.h similarity index 100% rename from ulib/ruxlibc/include/endian.h rename to ulib/include/endian.h diff --git a/ulib/ruxlibc/include/errno.h b/ulib/include/errno.h similarity index 100% rename from ulib/ruxlibc/include/errno.h rename to ulib/include/errno.h diff --git a/ulib/ruxlibc/include/fcntl.h b/ulib/include/fcntl.h similarity index 100% rename from ulib/ruxlibc/include/fcntl.h rename to ulib/include/fcntl.h diff --git a/ulib/ruxlibc/include/features.h b/ulib/include/features.h similarity index 100% rename from ulib/ruxlibc/include/features.h rename to ulib/include/features.h diff --git a/ulib/ruxlibc/include/float.h b/ulib/include/float.h similarity index 100% rename from ulib/ruxlibc/include/float.h rename to ulib/include/float.h diff --git a/ulib/ruxlibc/include/fnmatch.h b/ulib/include/fnmatch.h similarity index 100% rename from ulib/ruxlibc/include/fnmatch.h rename to ulib/include/fnmatch.h diff --git a/ulib/ruxlibc/include/glob.h b/ulib/include/glob.h similarity index 100% rename from ulib/ruxlibc/include/glob.h rename to ulib/include/glob.h diff --git a/ulib/ruxlibc/include/grp.h b/ulib/include/grp.h similarity index 100% rename from ulib/ruxlibc/include/grp.h rename to ulib/include/grp.h diff --git a/ulib/ruxlibc/include/inttypes.h b/ulib/include/inttypes.h similarity index 100% rename from ulib/ruxlibc/include/inttypes.h rename to ulib/include/inttypes.h diff --git a/ulib/ruxlibc/include/ksigaction.h b/ulib/include/ksigaction.h similarity index 100% rename from ulib/ruxlibc/include/ksigaction.h rename to ulib/include/ksigaction.h diff --git a/ulib/ruxlibc/include/langinfo.h b/ulib/include/langinfo.h similarity index 100% rename from ulib/ruxlibc/include/langinfo.h rename to ulib/include/langinfo.h diff --git a/ulib/ruxlibc/include/libgen.h b/ulib/include/libgen.h similarity index 100% rename from ulib/ruxlibc/include/libgen.h rename to ulib/include/libgen.h diff --git a/ulib/ruxlibc/include/limits.h b/ulib/include/limits.h similarity index 100% rename from ulib/ruxlibc/include/limits.h rename to ulib/include/limits.h diff --git a/ulib/ruxlibc/include/locale.h b/ulib/include/locale.h similarity index 100% rename from ulib/ruxlibc/include/locale.h rename to ulib/include/locale.h diff --git a/ulib/ruxlibc/include/malloc.h b/ulib/include/malloc.h similarity index 100% rename from ulib/ruxlibc/include/malloc.h rename to ulib/include/malloc.h diff --git a/ulib/ruxlibc/include/math.h b/ulib/include/math.h similarity index 100% rename from ulib/ruxlibc/include/math.h rename to ulib/include/math.h diff --git a/ulib/ruxlibc/include/memory.h b/ulib/include/memory.h similarity index 100% rename from ulib/ruxlibc/include/memory.h rename to ulib/include/memory.h diff --git a/ulib/ruxlibc/include/netdb.h b/ulib/include/netdb.h similarity index 100% rename from ulib/ruxlibc/include/netdb.h rename to ulib/include/netdb.h diff --git a/ulib/ruxlibc/include/netinet/in.h b/ulib/include/netinet/in.h similarity index 100% rename from ulib/ruxlibc/include/netinet/in.h rename to ulib/include/netinet/in.h diff --git a/ulib/ruxlibc/include/netinet/tcp.h b/ulib/include/netinet/tcp.h similarity index 100% rename from ulib/ruxlibc/include/netinet/tcp.h rename to ulib/include/netinet/tcp.h diff --git a/ulib/ruxlibc/include/nscd.h b/ulib/include/nscd.h similarity index 59% rename from ulib/ruxlibc/include/nscd.h rename to ulib/include/nscd.h index 32dbea024..4fac3f180 100644 --- a/ulib/ruxlibc/include/nscd.h +++ b/ulib/include/nscd.h @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #ifndef NSCD_H #define NSCD_H diff --git a/ulib/ruxlibc/include/poll.h b/ulib/include/poll.h similarity index 100% rename from ulib/ruxlibc/include/poll.h rename to ulib/include/poll.h diff --git a/ulib/ruxlibc/include/pthread.h b/ulib/include/pthread.h similarity index 100% rename from ulib/ruxlibc/include/pthread.h rename to ulib/include/pthread.h diff --git a/ulib/ruxlibc/include/pwd.h b/ulib/include/pwd.h similarity index 100% rename from ulib/ruxlibc/include/pwd.h rename to ulib/include/pwd.h diff --git a/ulib/ruxlibc/include/regex.h b/ulib/include/regex.h similarity index 100% rename from ulib/ruxlibc/include/regex.h rename to ulib/include/regex.h diff --git a/ulib/ruxlibc/include/sched.h b/ulib/include/sched.h similarity index 100% rename from ulib/ruxlibc/include/sched.h rename to ulib/include/sched.h diff --git a/ulib/ruxlibc/include/semaphore.h b/ulib/include/semaphore.h similarity index 53% rename from ulib/ruxlibc/include/semaphore.h rename to ulib/include/semaphore.h index 033cc8be1..5553ead84 100644 --- a/ulib/ruxlibc/include/semaphore.h +++ b/ulib/include/semaphore.h @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #ifndef _SEMAPHORE_H #define _SEMAPHORE_H diff --git a/ulib/ruxlibc/include/setjmp.h b/ulib/include/setjmp.h similarity index 100% rename from ulib/ruxlibc/include/setjmp.h rename to ulib/include/setjmp.h diff --git a/ulib/ruxlibc/include/signal.h b/ulib/include/signal.h similarity index 100% rename from ulib/ruxlibc/include/signal.h rename to ulib/include/signal.h diff --git a/ulib/ruxlibc/include/stdarg.h b/ulib/include/stdarg.h similarity index 100% rename from ulib/ruxlibc/include/stdarg.h rename to ulib/include/stdarg.h diff --git a/ulib/ruxlibc/include/stdbool.h b/ulib/include/stdbool.h similarity index 100% rename from ulib/ruxlibc/include/stdbool.h rename to ulib/include/stdbool.h diff --git a/ulib/ruxlibc/include/stddef.h b/ulib/include/stddef.h similarity index 100% rename from ulib/ruxlibc/include/stddef.h rename to ulib/include/stddef.h diff --git a/ulib/ruxlibc/include/stdint.h b/ulib/include/stdint.h similarity index 100% rename from ulib/ruxlibc/include/stdint.h rename to ulib/include/stdint.h diff --git a/ulib/ruxlibc/include/stdio.h b/ulib/include/stdio.h similarity index 100% rename from ulib/ruxlibc/include/stdio.h rename to ulib/include/stdio.h diff --git a/ulib/ruxlibc/include/stdlib.h b/ulib/include/stdlib.h similarity index 100% rename from ulib/ruxlibc/include/stdlib.h rename to ulib/include/stdlib.h diff --git a/ulib/ruxlibc/include/string.h b/ulib/include/string.h similarity index 100% rename from ulib/ruxlibc/include/string.h rename to ulib/include/string.h diff --git a/ulib/ruxlibc/include/strings.h b/ulib/include/strings.h similarity index 100% rename from ulib/ruxlibc/include/strings.h rename to ulib/include/strings.h diff --git a/ulib/ruxlibc/include/sys/epoll.h b/ulib/include/sys/epoll.h similarity index 100% rename from ulib/ruxlibc/include/sys/epoll.h rename to ulib/include/sys/epoll.h diff --git a/ulib/ruxlibc/include/sys/eventfd.h b/ulib/include/sys/eventfd.h similarity index 100% rename from ulib/ruxlibc/include/sys/eventfd.h rename to ulib/include/sys/eventfd.h diff --git a/ulib/ruxlibc/include/sys/file.h b/ulib/include/sys/file.h similarity index 100% rename from ulib/ruxlibc/include/sys/file.h rename to ulib/include/sys/file.h diff --git a/ulib/ruxlibc/include/sys/ioctl.h b/ulib/include/sys/ioctl.h similarity index 100% rename from ulib/ruxlibc/include/sys/ioctl.h rename to ulib/include/sys/ioctl.h diff --git a/ulib/ruxlibc/include/sys/mman.h b/ulib/include/sys/mman.h similarity index 100% rename from ulib/ruxlibc/include/sys/mman.h rename to ulib/include/sys/mman.h diff --git a/ulib/ruxlibc/include/sys/param.h b/ulib/include/sys/param.h similarity index 100% rename from ulib/ruxlibc/include/sys/param.h rename to ulib/include/sys/param.h diff --git a/ulib/ruxlibc/include/sys/prctl.h b/ulib/include/sys/prctl.h similarity index 100% rename from ulib/ruxlibc/include/sys/prctl.h rename to ulib/include/sys/prctl.h diff --git a/ulib/ruxlibc/src/pipe.rs b/ulib/include/sys/random.h similarity index 65% rename from ulib/ruxlibc/src/pipe.rs rename to ulib/include/sys/random.h index d4d853ca6..436bb2b14 100644 --- a/ulib/ruxlibc/src/pipe.rs +++ b/ulib/include/sys/random.h @@ -7,17 +7,22 @@ * See the Mulan PSL v2 for more details. */ -use core::ffi::c_int; +#ifndef _SYS_RANDOM_H +#define _SYS_RANDOM_H +#ifdef __cplusplus +extern "C" { +#endif -use ruxos_posix_api::sys_pipe; +#define __NEED_size_t +#define __NEED_ssize_t -use crate::utils::e; +#define GRND_NONBLOCK 0x0001 +#define GRND_RANDOM 0x0002 +#define GRND_INSECURE 0x0004 -/// Create a pipe -/// -/// Return 0 if succeed -#[no_mangle] -pub unsafe extern "C" fn pipe(fd: *mut c_int) -> c_int { - let fds = unsafe { core::slice::from_raw_parts_mut(fd, 2) }; - e(sys_pipe(fds)) +ssize_t getrandom(void *, size_t, unsigned); + +#ifdef __cplusplus } +#endif +#endif diff --git a/ulib/ruxlibc/include/sys/resource.h b/ulib/include/sys/resource.h similarity index 100% rename from ulib/ruxlibc/include/sys/resource.h rename to ulib/include/sys/resource.h diff --git a/ulib/ruxlibc/include/sys/select.h b/ulib/include/sys/select.h similarity index 100% rename from ulib/ruxlibc/include/sys/select.h rename to ulib/include/sys/select.h diff --git a/ulib/ruxlibc/include/sys/sendfile.h b/ulib/include/sys/sendfile.h similarity index 100% rename from ulib/ruxlibc/include/sys/sendfile.h rename to ulib/include/sys/sendfile.h diff --git a/ulib/ruxlibc/include/sys/socket.h b/ulib/include/sys/socket.h similarity index 100% rename from ulib/ruxlibc/include/sys/socket.h rename to ulib/include/sys/socket.h diff --git a/ulib/ruxlibc/include/sys/stat.h b/ulib/include/sys/stat.h similarity index 76% rename from ulib/ruxlibc/include/sys/stat.h rename to ulib/include/sys/stat.h index a2f24cd0e..5676f555a 100644 --- a/ulib/ruxlibc/include/sys/stat.h +++ b/ulib/include/sys/stat.h @@ -14,7 +14,7 @@ #include #include -#if defined(__aarch64__) +#if defined(__aarch64__) || defined(__riscv__) || defined(__riscv) struct stat { dev_t st_dev; ino_t st_ino; @@ -55,50 +55,50 @@ struct stat { }; #endif -#if defined(__aarch64__) +#if defined(__aarch64__) || defined(__riscv__) || defined(__riscv) struct kstat { - dev_t st_dev; - ino_t st_ino; - mode_t st_mode; - nlink_t st_nlink; - uid_t st_uid; - gid_t st_gid; - dev_t st_rdev; - unsigned long __pad; - off_t st_size; - blksize_t st_blksize; - int __pad2; - blkcnt_t st_blocks; - long st_atime_sec; - long st_atime_nsec; - long st_mtime_sec; - long st_mtime_nsec; - long st_ctime_sec; - long st_ctime_nsec; - unsigned __unused[2]; + dev_t st_dev; + ino_t st_ino; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + unsigned long __pad; + off_t st_size; + blksize_t st_blksize; + int __pad2; + blkcnt_t st_blocks; + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; + unsigned __unused[2]; }; #else struct kstat { - dev_t st_dev; - ino_t st_ino; - nlink_t st_nlink; - - mode_t st_mode; - uid_t st_uid; - gid_t st_gid; - unsigned int __pad0; - dev_t st_rdev; - off_t st_size; - blksize_t st_blksize; - blkcnt_t st_blocks; - - long st_atime_sec; - long st_atime_nsec; - long st_mtime_sec; - long st_mtime_nsec; - long st_ctime_sec; - long st_ctime_nsec; - long __unused[3]; + dev_t st_dev; + ino_t st_ino; + nlink_t st_nlink; + + mode_t st_mode; + uid_t st_uid; + gid_t st_gid; + unsigned int __pad0; + dev_t st_rdev; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; + long __unused[3]; }; #endif diff --git a/ulib/ruxlibc/c/stat.c b/ulib/include/sys/statfs.h similarity index 56% rename from ulib/ruxlibc/c/stat.c rename to ulib/include/sys/statfs.h index 9b93f816b..dfa84f26e 100644 --- a/ulib/ruxlibc/c/stat.c +++ b/ulib/include/sys/statfs.h @@ -7,34 +7,25 @@ * See the Mulan PSL v2 for more details. */ -#include -#include -#include +#ifndef _SYS_STATFS_H +#define _SYS_STATFS_H -// TODO: -int fchmod(int fd, mode_t mode) -{ - unimplemented(); - return 0; -} +#include +#include -// TODO -int chmod(const char *path, mode_t mode) -{ - unimplemented(); - return 0; -} +typedef struct __fsid_t { + int __val[2]; +} fsid_t; -// TODO -mode_t umask(mode_t mask) -{ - unimplemented("mask: %d", mask); - return 0; -} +struct statfs { + unsigned long f_type, f_bsize; + fsblkcnt_t f_blocks, f_bfree, f_bavail; + fsfilcnt_t f_files, f_ffree; + fsid_t f_fsid; + unsigned long f_namelen, f_frsize, f_flags, f_spare[4]; +}; -// TODO -int fstatat(int fd, const char *restrict path, struct stat *restrict st, int flag) -{ - unimplemented(); - return 0; -} +int statfs(const char *, struct statfs *); +int fstatfs(int, struct statfs *); + +#endif diff --git a/ulib/ruxlibc/include/sys/statvfs.h b/ulib/include/sys/statvfs.h similarity index 53% rename from ulib/ruxlibc/include/sys/statvfs.h rename to ulib/include/sys/statvfs.h index 4d44a30e4..a495800e2 100644 --- a/ulib/ruxlibc/include/sys/statvfs.h +++ b/ulib/include/sys/statvfs.h @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + #ifndef _SYS_STATVFS_H #define _SYS_STATVFS_H diff --git a/ulib/ruxlibc/c/flock.c b/ulib/include/sys/syscall.h similarity index 81% rename from ulib/ruxlibc/c/flock.c rename to ulib/include/sys/syscall.h index 36859d25c..ef3a96995 100644 --- a/ulib/ruxlibc/c/flock.c +++ b/ulib/include/sys/syscall.h @@ -7,12 +7,10 @@ * See the Mulan PSL v2 for more details. */ -#include -#include +#ifndef _SYS_SYSCALL_H +#define _SYS_SYSCALL_H -// TODO -int flock(int __fd, int __operation) -{ - unimplemented(); - return 0; -} +#include +#include + +#endif diff --git a/ulib/ruxlibc/include/sys/sysinfo.h b/ulib/include/sys/sysinfo.h similarity index 100% rename from ulib/ruxlibc/include/sys/sysinfo.h rename to ulib/include/sys/sysinfo.h diff --git a/ulib/ruxlibc/include/sys/time.h b/ulib/include/sys/time.h similarity index 100% rename from ulib/ruxlibc/include/sys/time.h rename to ulib/include/sys/time.h diff --git a/ulib/ruxlibc/include/sys/types.h b/ulib/include/sys/types.h similarity index 82% rename from ulib/ruxlibc/include/sys/types.h rename to ulib/include/sys/types.h index 5e3252a40..46919e0cb 100644 --- a/ulib/ruxlibc/include/sys/types.h +++ b/ulib/include/sys/types.h @@ -21,16 +21,22 @@ typedef unsigned long u_long, ulong; typedef unsigned mode_t; -#if defined(__aarch64__) +#if defined(__aarch64__) || defined(__riscv__) || defined(__riscv) typedef uint32_t nlink_t; #else typedef uint64_t nlink_t; #endif +// reference: /ulib/ruxmusl/musl-1.2.3/arch/aarch64/bits/alltypes.h.in +#if defined(__aarch64__) || defined(__riscv__) || defined(__riscv) +typedef int blksize_t; +#else +typedef long blksize_t; +#endif + typedef int64_t off_t; typedef uint64_t ino_t; typedef uint64_t dev_t; -typedef long blksize_t; typedef int64_t blkcnt_t; typedef unsigned id_t; diff --git a/ulib/ruxlibc/include/sys/uio.h b/ulib/include/sys/uio.h similarity index 100% rename from ulib/ruxlibc/include/sys/uio.h rename to ulib/include/sys/uio.h diff --git a/ulib/ruxlibc/include/sys/un.h b/ulib/include/sys/un.h similarity index 100% rename from ulib/ruxlibc/include/sys/un.h rename to ulib/include/sys/un.h diff --git a/ulib/ruxlibc/include/sys/utsname.h b/ulib/include/sys/utsname.h similarity index 100% rename from ulib/ruxlibc/include/sys/utsname.h rename to ulib/include/sys/utsname.h diff --git a/ulib/ruxlibc/c/statfs.c b/ulib/include/sys/vfs.h similarity index 82% rename from ulib/ruxlibc/c/statfs.c rename to ulib/include/sys/vfs.h index 84d773818..233f592cf 100644 --- a/ulib/ruxlibc/c/statfs.c +++ b/ulib/include/sys/vfs.h @@ -7,12 +7,7 @@ * See the Mulan PSL v2 for more details. */ -#include +#ifndef _SYS_VFS_H +#define _SYS_VFS_H #include - -// TODO -int statfs(const char *path, struct statfs *buf) -{ - unimplemented(); - return 0; -} +#endif diff --git a/ulib/ruxlibc/include/sys/wait.h b/ulib/include/sys/wait.h similarity index 100% rename from ulib/ruxlibc/include/sys/wait.h rename to ulib/include/sys/wait.h diff --git a/ulib/ruxlibc/include/syslog.h b/ulib/include/syslog.h similarity index 100% rename from ulib/ruxlibc/include/syslog.h rename to ulib/include/syslog.h diff --git a/ulib/ruxlibc/include/termios.h b/ulib/include/termios.h similarity index 100% rename from ulib/ruxlibc/include/termios.h rename to ulib/include/termios.h diff --git a/ulib/ruxlibc/include/time.h b/ulib/include/time.h similarity index 100% rename from ulib/ruxlibc/include/time.h rename to ulib/include/time.h diff --git a/ulib/ruxlibc/include/unistd.h b/ulib/include/unistd.h similarity index 100% rename from ulib/ruxlibc/include/unistd.h rename to ulib/include/unistd.h diff --git a/ulib/ruxlibc/.gitignore b/ulib/ruxlibc/.gitignore deleted file mode 100644 index 792f33c1e..000000000 --- a/ulib/ruxlibc/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -src/libctypes_gen.rs -include/ax_pthread_mutex.h -include/ax_pthread_cond.h -build_* diff --git a/ulib/ruxlibc/Cargo.toml b/ulib/ruxlibc/Cargo.toml deleted file mode 100644 index 8b00b811f..000000000 --- a/ulib/ruxlibc/Cargo.toml +++ /dev/null @@ -1,67 +0,0 @@ -[package] -name = "ruxlibc" -version = "0.1.0" -edition = "2021" -authors = [ - "Yuekai Jia ", - "yanjuguang ", - "wudashuai ", - "yfblock <321353225@qq.com>", - "scPointer ", - "Shiping Yuan ", -] -description = "Ruxos user program library for C apps" -license = "GPL-3.0-or-later OR Apache-2.0" -homepage = "https://github.com/syswonder/ruxos" -repository = "https://github.com/syswonder/ruxos/tree/main/ulib/ruxlibc" - -[lib] -crate-type = ["staticlib"] - -[features] -default = [] - -# Multicore -smp = ["ruxos_posix_api/smp"] - -# Floating point/SIMD -fp_simd = ["ruxfeat/fp_simd"] - -# Memory -alloc = ["ruxos_posix_api/alloc"] -paging = ["alloc", "ruxos_posix_api/paging"] -tls = ["alloc", "ruxfeat/tls"] - -# Multi-task -multitask = ["ruxos_posix_api/multitask"] - -# File system -fs = ["ruxos_posix_api/fs", "fd"] - -# Networking -net = ["ruxos_posix_api/net", "fd"] - -# Signal -signal = ["ruxos_posix_api/signal"] - -# Libc features -fd = [] -pipe = ["ruxos_posix_api/pipe"] -select = ["ruxos_posix_api/select"] -poll = ["ruxos_posix_api/poll"] -epoll = ["ruxos_posix_api/epoll"] -random-hw = ["ruxos_posix_api/random-hw"] - -# Interrupts -irq = ["ruxos_posix_api/irq", "ruxfeat/irq"] - -sched_rr = ["irq", "ruxfeat/sched_rr"] - -[dependencies] -ruxfeat = { path = "../../api/ruxfeat" } -ruxos_posix_api = { path = "../../api/ruxos_posix_api" } -axio = { path = "../../crates/axio" } -axerrno = { path = "../../crates/axerrno" } - -[build-dependencies] -bindgen ={ version = "0.66" } diff --git a/ulib/ruxlibc/build.rs b/ulib/ruxlibc/build.rs deleted file mode 100644 index 7a8501edd..000000000 --- a/ulib/ruxlibc/build.rs +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -fn main() { - fn gen_c_to_rust_bindings(in_file: &str, out_file: &str) { - println!("cargo:rerun-if-changed={in_file}"); - - let allow_types = ["tm", "jmp_buf"]; - let mut builder = bindgen::Builder::default() - .header(in_file) - .clang_arg("-isystem./include") - .derive_default(true) - .size_t_is_usize(false) - .use_core(); - for ty in allow_types { - builder = builder.allowlist_type(ty); - } - - builder - .generate() - .expect("Unable to generate c->rust bindings") - .write_to_file(out_file) - .expect("Couldn't write bindings!"); - } - - gen_c_to_rust_bindings("ctypes.h", "src/libctypes_gen.rs"); -} diff --git a/ulib/ruxlibc/c/assert.c b/ulib/ruxlibc/c/assert.c deleted file mode 100644 index 4d9060c17..000000000 --- a/ulib/ruxlibc/c/assert.c +++ /dev/null @@ -1,17 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include - -_Noreturn void __assert_fail(const char *expr, const char *file, int line, const char *func) -{ - fprintf(stderr, "Assertion failed: %s (%s: %s: %d)\n", expr, file, func, line); - abort(); -} diff --git a/ulib/ruxlibc/c/ctype.c b/ulib/ruxlibc/c/ctype.c deleted file mode 100644 index 3ec36db30..000000000 --- a/ulib/ruxlibc/c/ctype.c +++ /dev/null @@ -1,26 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include -#include - -int tolower(int c) -{ - if (isupper(c)) - return c | 32; - return c; -} - -int toupper(int c) -{ - if (islower(c)) - return c & 0x5f; - return c; -} diff --git a/ulib/ruxlibc/c/dirent.c b/ulib/ruxlibc/c/dirent.c deleted file mode 100644 index da9fc05e8..000000000 --- a/ulib/ruxlibc/c/dirent.c +++ /dev/null @@ -1,107 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#ifdef RUX_CONFIG_FS - -#include -#include -#include -#include -#include -#include -#include -#include - -int closedir(DIR *dir) -{ - int ret = close(dir->fd); - free(dir); - return ret; -} - -DIR *fdopendir(int fd) -{ - DIR *dir; - struct stat st; - - if (fstat(fd, &st) < 0) { - return 0; - } - if (fcntl(fd, F_GETFL) & O_PATH) { - errno = EBADF; - return 0; - } - if (!S_ISDIR(st.st_mode)) { - errno = ENOTDIR; - return 0; - } - if (!(dir = calloc(1, sizeof(*dir)))) { - return 0; - } - - fcntl(fd, F_SETFD, FD_CLOEXEC); - dir->fd = fd; - return dir; -} - -int dirfd(DIR *d) -{ - return d->fd; -} - -// TODO -DIR *opendir(const char *__name) -{ - unimplemented(); - return NULL; -} - -// TODO -struct dirent *readdir(DIR *__dirp) -{ - unimplemented(); - return NULL; -} - -// TODO -int readdir_r(DIR *restrict dir, struct dirent *restrict buf, struct dirent **restrict result) -{ - struct dirent *de; - int errno_save = errno; - int ret; - - // LOCK(dir->lock); - errno = 0; - de = readdir(dir); - if ((ret = errno)) { - // UNLOCK(dir->lock); - return ret; - } - errno = errno_save; - if (de) - memcpy(buf, de, de->d_reclen); - else - buf = NULL; - - // UNLOCK(dir->lock); - *result = buf; - return 0; -} - -// TODO -void rewinddir(DIR *dir) -{ - // LOCK(dir->lock); - lseek(dir->fd, 0, SEEK_SET); - dir->buf_pos = dir->buf_end = 0; - dir->tell = 0; - // UNLOCK(dir->lock); -} - -#endif // RUX_CONFIG_FS diff --git a/ulib/ruxlibc/c/dlfcn.c b/ulib/ruxlibc/c/dlfcn.c deleted file mode 100644 index 5fc088d02..000000000 --- a/ulib/ruxlibc/c/dlfcn.c +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include -#include - -// TODO -int dladdr(const void *__address, Dl_info *__info) -{ - unimplemented(); - return 0; -} - -// TODO -void *dlopen(const char *__file, int __mode) -{ - unimplemented(); - return NULL; -} - -// TODO -char *dlerror() -{ - unimplemented(); - return NULL; -} - -// TODO -void *dlsym(void *__restrict__ __handle, const char *__restrict__ __name) -{ - - unimplemented(); - return NULL; -} - -// TODO -int dlclose(void *p) -{ - unimplemented(); - return 0; -} diff --git a/ulib/ruxlibc/c/fcntl.c b/ulib/ruxlibc/c/fcntl.c deleted file mode 100644 index 1c2aeedad..000000000 --- a/ulib/ruxlibc/c/fcntl.c +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include -#include - -#ifdef RUX_CONFIG_FD - -// TODO: remove this function in future work -int ax_fcntl(int fd, int cmd, size_t arg); - -int fcntl(int fd, int cmd, ... /* arg */) -{ - unsigned long arg; - va_list ap; - va_start(ap, cmd); - arg = va_arg(ap, unsigned long); - va_end(ap); - - return ax_fcntl(fd, cmd, arg); -} - -#endif // RUX_CONFIG_FD - -#ifdef RUX_CONFIG_FS - -// TODO: remove this function in future work -int ax_open(const char *filename, int flags, mode_t mode); - -int open(const char *filename, int flags, ...) -{ - mode_t mode = 0; - - if ((flags & O_CREAT) || (flags & O_TMPFILE) == O_TMPFILE) { - va_list ap; - va_start(ap, flags); - mode = va_arg(ap, mode_t); - va_end(ap); - } - - return ax_open(filename, flags, mode); -} - -// TODO -int posix_fadvise(int __fd, unsigned long __offset, unsigned long __len, int __advise) -{ - unimplemented(); - return 0; -} - -// TODO -int sync_file_range(int fd, off_t pos, off_t len, unsigned flags) -{ - unimplemented(); - return 0; -} - -int openat(int dirfd, const char *pathname, int flags, ...) -{ - unimplemented(); - return 0; -} - -#endif // RUX_CONFIG_FS diff --git a/ulib/ruxlibc/c/fnmatch.c b/ulib/ruxlibc/c/fnmatch.c deleted file mode 100644 index 7e88e5970..000000000 --- a/ulib/ruxlibc/c/fnmatch.c +++ /dev/null @@ -1,304 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include -#include -#include - -#define END 0 -#define UNMATCHABLE -2 -#define BRACKET -3 -#define QUESTION -4 -#define STAR -5 - -// TODO: When wide characters implemented, remove annotations according to musl. - -static int str_next(const char *str, size_t n, size_t *step) -{ - if (!n) { - *step = 0; - return 0; - } - // TODO: When wide characters implemented, remove annotation - // if (str[0] >= 128U) { - // wchar_t wc; - // int k = mbtowc(&wc, str, n); - // if (k<0) { - // *step = 1; - // return -1; - // } - // *step = k; - // return wc; - // } - *step = 1; - return str[0]; -} - -static int pat_next(const char *pat, size_t m, size_t *step, int flags) -{ - if (!m || !*pat) { - *step = 0; - return END; - } - *step = 1; - if (pat[0]=='\\' && pat[1] && !(flags & FNM_NOESCAPE)) { - *step = 2; - pat++; - goto escaped; - } - if (pat[0]=='[') { - size_t k = 1; - if (k= 128U) { - // wchar_t wc; - // int k = mbtowc(&wc, pat, m); - // if (k<0) { - // *step = 0; - // return UNMATCHABLE; - // } - // *step = k + esc; - // return wc; - // } - return pat[0]; -} - -static int casefold(int k) -{ - int c = toupper(k); - return c == k ? tolower(k) : c; - -} - -static int match_bracket(const char *p, int k, int kfold) -{ - // TODO: When wide characters implemented, remove annotation and delete next line - // wchar_t wc; - char wc; - int inv = 0; - p++; - if (*p=='^' || *p=='!') { - inv = 1; - p++; - } - if (*p==']') { - if (k==']') return !inv; - p++; - } else if (*p=='-') { - if (k=='-') return !inv; - p++; - } - wc = p[-1]; - for (; *p != ']'; p++) { - if (p[0]=='-' && p[1]!=']') { - // TODO: When wide characters implemented, remove annotation - // wchar_t wc2; - // int l = mbtowc(&wc2, p+1, 4); - // if (l < 0) return 0; - char wc2 = p[1]; - if (wc <= wc2) - if ((unsigned)k-wc <= wc2-wc || - (unsigned)kfold-wc <= wc2-wc) - return !inv; - // TODO: When wide characters implemented, remove annotation and delete next line - // p += l-1; - p += 1; - continue; - } - if (*p < 128U) { - wc = (unsigned char)*p; - } - if (wc==k || wc==kfold) return !inv; - } - return inv; -} - -static int fnmatch_internal(const char *pat, size_t m, const char *str, size_t n, int flags) -{ - const char *p, *ptail, *endpat; - const char *s, *stail, *endstr; - size_t pinc, sinc, tailcnt=0; - int c, k, kfold; - - if (flags & FNM_PERIOD) { - if (*str == '.' && *pat != '.') - return FNM_NOMATCH; - } - for (;;) { - switch ((c = pat_next(pat, m, &pinc, flags))) { - case UNMATCHABLE: - return FNM_NOMATCH; - case STAR: - pat++; - m--; - break; - default: - k = str_next(str, n, &sinc); - if (k <= 0) - return (c==END) ? 0 : FNM_NOMATCH; - str += sinc; - n -= sinc; - kfold = flags & FNM_CASEFOLD ? casefold(k) : k; - if (c == BRACKET) { - if (!match_bracket(pat, k, kfold)) - return FNM_NOMATCH; - } else if (c != QUESTION && k != c && kfold != c) { - return FNM_NOMATCH; - } - pat+=pinc; - m-=pinc; - continue; - } - break; - } - - /* Compute real pat length if it was initially unknown/-1 */ - m = strnlen(pat, m); - endpat = pat + m; - - /* Find the last * in pat and count chars needed after it */ - for (p=ptail=pat; pstr && tailcnt; tailcnt--) { - // TODO: When wide characters implemented, remove annotation - // if (s[-1] < 128U || MB_CUR_MAX==1) s--; - // else while ((unsigned char)*--s-0x80U<0x40 && s>str); - s--; - } - if (tailcnt) return FNM_NOMATCH; - stail = s; - - /* Check that the pat and str tails match */ - p = ptail; - for (;;) { - c = pat_next(p, endpat-p, &pinc, flags); - p += pinc; - if ((k = str_next(s, endstr-s, &sinc)) <= 0) { - if (c != END) return FNM_NOMATCH; - break; - } - s += sinc; - kfold = flags & FNM_CASEFOLD ? casefold(k) : k; - if (c == BRACKET) { - if (!match_bracket(p-pinc, k, kfold)) - return FNM_NOMATCH; - } else if (c != QUESTION && k != c && kfold != c) { - return FNM_NOMATCH; - } - } - - /* We're all done with the tails now, so throw them out */ - endstr = stail; - endpat = ptail; - - /* Match pattern components until there are none left */ - while (pat 0) str += sinc; - else for (str++; str_next(str, endstr-str, &sinc)<0; str++); - } - - return 0; -} - -int fnmatch(const char *pat, const char *str, int flags) -{ - const char *s, *p; - size_t inc; - int c; - if (flags & FNM_PATHNAME) for (;;) { - for (s=str; *s && *s!='/'; s++); - for (p=pat; (c=pat_next(p, -1, &inc, flags))!=END && c!='/'; p+=inc); - if (c!=*s && (!*s || !(flags & FNM_LEADING_DIR))) - return FNM_NOMATCH; - if (fnmatch_internal(pat, p-pat, str, s-str, flags)) - return FNM_NOMATCH; - if (!c) return 0; - str = s+1; - pat = p+inc; - } else if (flags & FNM_LEADING_DIR) { - for (s=str; *s; s++) { - if (*s != '/') continue; - if (!fnmatch_internal(pat, -1, str, s-str, flags)) - return 0; - } - } - return fnmatch_internal(pat, -1, str, -1, flags); -} diff --git a/ulib/ruxlibc/c/glob.c b/ulib/ruxlibc/c/glob.c deleted file mode 100644 index 6b6137424..000000000 --- a/ulib/ruxlibc/c/glob.c +++ /dev/null @@ -1,338 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#ifdef RUX_CONFIG_FS - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct match { - struct match *next; - char name[]; -}; - -static int append(struct match **tail, const char *name, size_t len, int mark) -{ - struct match *new = malloc(sizeof(struct match) + len + 2); - if (!new) - return -1; - (*tail)->next = new; - new->next = NULL; - memcpy(new->name, name, len + 1); - if (mark && len && name[len - 1] != '/') { - new->name[len] = '/'; - new->name[len + 1] = 0; - } - *tail = new; - return 0; -} - -static int do_glob(char *buf, size_t pos, int type, char *pat, int flags, - int (*errfunc)(const char *path, int err), struct match **tail) -{ - /* If GLOB_MARK is unused, we don't care about type. */ - if (!type && !(flags & GLOB_MARK)) - type = DT_REG; - - /* Special-case the remaining pattern being all slashes, in - * which case we can use caller-passed type if it's a dir. */ - if (*pat && type != DT_DIR) - type = 0; - while (pos + 1 < PATH_MAX && *pat == '/') buf[pos++] = *pat++; - - /* Consume maximal [escaped-]literal prefix of pattern, copying - * and un-escaping it to the running buffer as we go. */ - long i = 0, j = 0; - int in_bracket = 0, overflow = 0; - for (; pat[i] != '*' && pat[i] != '?' && (!in_bracket || pat[i] != ']'); i++) { - if (!pat[i]) { - if (overflow) - return 0; - pat += i; - pos += j; - i = j = 0; - break; - } else if (pat[i] == '[') { - in_bracket = 1; - } else if (pat[i] == '\\' && !(flags & GLOB_NOESCAPE)) { - /* Backslashes inside a bracket are (at least by - * our interpretation) non-special, so if next - * char is ']' we have a complete expression. */ - if (in_bracket && pat[i + 1] == ']') - break; - /* Unpaired final backslash never matches. */ - if (!pat[i + 1]) - return 0; - i++; - } - if (pat[i] == '/') { - if (overflow) - return 0; - in_bracket = 0; - pat += i + 1; - i = -1; - pos += j + 1; - j = -1; - } - /* Only store a character if it fits in the buffer, but if - * a potential bracket expression is open, the overflow - * must be remembered and handled later only if the bracket - * is unterminated (and thereby a literal), so as not to - * disallow long bracket expressions with short matches. */ - if (pos + (j + 1) < PATH_MAX) { - buf[pos + j++] = pat[i]; - } else if (in_bracket) { - overflow = 1; - } else { - return 0; - } - /* If we consume any new components, the caller-passed type - * or dummy type from above is no longer valid. */ - type = 0; - } - buf[pos] = 0; - if (!*pat) { - /* If we consumed any components above, or if GLOB_MARK is - * requested and we don't yet know if the match is a dir, - * we must confirm the file exists and/or determine its type. - * - * If marking dirs, symlink type is inconclusive; we need the - * type for the symlink target, and therefore must try stat - * first unless type is known not to be a symlink. Otherwise, - * or if that fails, use lstat for determining existence to - * avoid false negatives in the case of broken symlinks. */ - struct stat st; - if ((flags & GLOB_MARK) && (!type || type == DT_LNK) && !stat(buf, &st)) { - if (S_ISDIR(st.st_mode)) - type = DT_DIR; - else - type = DT_REG; - } - if (!type && lstat(buf, &st)) { - if (errno != ENOENT && (errfunc(buf, errno) || (flags & GLOB_ERR))) - return GLOB_ABORTED; - return 0; - } - if (append(tail, buf, pos, (flags & GLOB_MARK) && type == DT_DIR)) - return GLOB_NOSPACE; - return 0; - } - char *p2 = strchr(pat, '/'), saved_sep = '/'; - /* Check if the '/' was escaped and, if so, remove the escape char - * so that it will not be unpaired when passed to fnmatch. */ - if (p2 && !(flags & GLOB_NOESCAPE)) { - char *p; - for (p = p2; p > pat && p[-1] == '\\'; p--) - ; - if ((p2 - p) % 2) { - p2--; - saved_sep = '\\'; - } - } - DIR *dir = opendir(pos ? buf : "."); - if (!dir) { - if (errfunc(buf, errno) || (flags & GLOB_ERR)) - return GLOB_ABORTED; - return 0; - } - int old_errno = errno; - struct dirent *de; - while (errno = 0, de = readdir(dir)) { - /* Quickly skip non-directories when there's pattern left. */ - if (p2 && de->d_type && de->d_type != DT_DIR && de->d_type != DT_LNK) - continue; - - size_t l = strlen(de->d_name); - if (l >= PATH_MAX - pos) - continue; - - if (p2) - *p2 = 0; - - int fnm_flags = ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) | - ((!(flags & GLOB_PERIOD)) ? FNM_PERIOD : 0); - - if (fnmatch(pat, de->d_name, fnm_flags)) - continue; - - /* With GLOB_PERIOD, don't allow matching . or .. unless - * fnmatch would match them with FNM_PERIOD rules in effect. */ - if (p2 && (flags & GLOB_PERIOD) && de->d_name[0] == '.' && - (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])) && - fnmatch(pat, de->d_name, fnm_flags | FNM_PERIOD)) - continue; - - memcpy(buf + pos, de->d_name, l + 1); - if (p2) - *p2 = saved_sep; - int r = do_glob(buf, pos + l, de->d_type, p2 ? p2 : "", flags, errfunc, tail); - if (r) { - closedir(dir); - return r; - } - } - int readerr = errno; - if (p2) - *p2 = saved_sep; - closedir(dir); - if (readerr && (errfunc(buf, errno) || (flags & GLOB_ERR))) - return GLOB_ABORTED; - errno = old_errno; - return 0; -} - -static int ignore_err(const char *path, int err) -{ - return 0; -} - -static void freelist(struct match *head) -{ - struct match *match, *next; - for (match = head->next; match; match = next) { - next = match->next; - free(match); - } -} - -static int sort(const void *a, const void *b) -{ - return strcmp(*(const char **)a, *(const char **)b); -} - -static int expand_tilde(char **pat, char *buf, size_t *pos) -{ - char *p = *pat + 1; - size_t i = 0; - - char delim, *name_end = strchrnul(p, '/'); - if ((delim = *name_end)) - *name_end++ = 0; - *pat = name_end; - - char *home = *p ? NULL : getenv("HOME"); - if (!home) { - struct passwd pw, *res; - switch (*p ? getpwnam_r(p, &pw, buf, PATH_MAX, &res) - : getpwuid_r(getuid(), &pw, buf, PATH_MAX, &res)) { - case ENOMEM: - return GLOB_NOSPACE; - case 0: - if (!res) - default: - return GLOB_NOMATCH; - } - home = pw.pw_dir; - } - while (i < PATH_MAX - 2 && *home) buf[i++] = *home++; - if (*home) - return GLOB_NOMATCH; - if ((buf[i] = delim)) - buf[++i] = 0; - *pos = i; - return 0; -} - -int glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, int err), - glob_t *restrict g) -{ - struct match head = {.next = NULL}, *tail = &head; - size_t cnt, i; - size_t offs = (flags & GLOB_DOOFFS) ? g->gl_offs : 0; - int error = 0; - char buf[PATH_MAX]; - - if (!errfunc) - errfunc = ignore_err; - - if (!(flags & GLOB_APPEND)) { - g->gl_offs = offs; - g->gl_pathc = 0; - g->gl_pathv = NULL; - } - - if (*pat) { - char *p = strdup(pat); - if (!p) - return GLOB_NOSPACE; - buf[0] = 0; - size_t pos = 0; - char *s = p; - if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && *p == '~') - error = expand_tilde(&s, buf, &pos); - if (!error) - error = do_glob(buf, pos, 0, s, flags, errfunc, &tail); - free(p); - } - - if (error == GLOB_NOSPACE) { - freelist(&head); - return error; - } - - for (cnt = 0, tail = head.next; tail; tail = tail->next, cnt++) - ; - if (!cnt) { - if (flags & GLOB_NOCHECK) { - tail = &head; - if (append(&tail, pat, strlen(pat), 0)) - return GLOB_NOSPACE; - cnt++; - } else - return GLOB_NOMATCH; - } - - if (flags & GLOB_APPEND) { - char **pathv = realloc(g->gl_pathv, (offs + g->gl_pathc + cnt + 1) * sizeof(char *)); - if (!pathv) { - freelist(&head); - return GLOB_NOSPACE; - } - g->gl_pathv = pathv; - offs += g->gl_pathc; - } else { - g->gl_pathv = malloc((offs + cnt + 1) * sizeof(char *)); - if (!g->gl_pathv) { - freelist(&head); - return GLOB_NOSPACE; - } - for (i = 0; i < offs; i++) g->gl_pathv[i] = NULL; - } - for (i = 0, tail = head.next; i < cnt; tail = tail->next, i++) - g->gl_pathv[offs + i] = tail->name; - g->gl_pathv[offs + i] = NULL; - g->gl_pathc += cnt; - - if (!(flags & GLOB_NOSORT)) - qsort(g->gl_pathv + offs, cnt, sizeof(char *), sort); - - return error; -} - -void globfree(glob_t *g) -{ - size_t i; - for (i = 0; i < g->gl_pathc; i++) - free(g->gl_pathv[g->gl_offs + i] - offsetof(struct match, name)); - free(g->gl_pathv); - g->gl_pathc = 0; - g->gl_pathv = NULL; -} - -#endif // RUX_CONFIG_FS diff --git a/ulib/ruxlibc/c/grp.c b/ulib/ruxlibc/c/grp.c deleted file mode 100644 index eb858607c..000000000 --- a/ulib/ruxlibc/c/grp.c +++ /dev/null @@ -1,46 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include -#include -#include -#include - -/* Group members */ -static char *g_members__[] = { RUX_DEFAULT_USER, NULL }; - -/* Default group */ -static struct group g__ = { - .gr_name = RUX_DEFAULT_GROUP, - .gr_passwd = RUX_DEFAULT_PASS, - .gr_gid = RUX_DEFAULT_GID, - .gr_mem = g_members__, -}; - -// TODO -int initgroups(const char *user, gid_t group) -{ - unimplemented(); - return 0; -} - -struct group *getgrnam(const char *name) -{ - struct group *res; - - if (name && !strcmp(name, g__.gr_name)) - res = &g__; - else { - res = NULL; - errno = ENOENT; - } - - return res; -} diff --git a/ulib/ruxlibc/c/ioctl.c b/ulib/ruxlibc/c/ioctl.c deleted file mode 100644 index 578622a46..000000000 --- a/ulib/ruxlibc/c/ioctl.c +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A - * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. - */ - -#include -#include -#include - -int rux_ioctl(int fd, int cmd, size_t arg); - -// TODO -int ioctl(int fd, int request, ...) -{ - unsigned long arg; - va_list ap; - va_start(ap, request); - arg = va_arg(ap, unsigned long); - va_end(ap); - - return rux_ioctl(fd, request, arg); -} diff --git a/ulib/ruxlibc/c/libgen.c b/ulib/ruxlibc/c/libgen.c deleted file mode 100644 index d0ffcdced..000000000 --- a/ulib/ruxlibc/c/libgen.c +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include - -char *dirname(char *s) -{ - size_t i; - if (!s || !*s) - return "."; - i = strlen(s) - 1; - for (; s[i] == '/'; i--) - if (!i) - return "/"; - for (; s[i] != '/'; i--) - if (!i) - return "."; - for (; s[i] == '/'; i--) - if (!i) - return "/"; - s[i + 1] = 0; - return s; -} - -char *basename(char *s) -{ - size_t i; - if (!s || !*s) - return "."; - i = strlen(s) - 1; - for (; i && s[i] == '/'; i--) s[i] = 0; - for (; i && s[i - 1] != '/'; i--) - ; - return s + i; -} diff --git a/ulib/ruxlibc/c/libm.c b/ulib/ruxlibc/c/libm.c deleted file mode 100644 index 26269c3ed..000000000 --- a/ulib/ruxlibc/c/libm.c +++ /dev/null @@ -1,26 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#ifdef RUX_CONFIG_FP_SIMD - -#include - -#include "libm.h" - -double __math_divzero(uint32_t sign) -{ - return fp_barrier(sign ? -1.0 : 1.0) / 0.0; -} - -double __math_invalid(double x) -{ - return (x - x) / (x - x); -} - -#endif // RUX_CONFIG_FP_SIMD diff --git a/ulib/ruxlibc/c/libm.h b/ulib/ruxlibc/c/libm.h deleted file mode 100644 index 51046ba8c..000000000 --- a/ulib/ruxlibc/c/libm.h +++ /dev/null @@ -1,242 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#ifndef _LIBM_H -#define _LIBM_H - -#if RUX_CONFIG_FP_SIMD - -#include -#include -#include - -#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 -#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __LITTLE_ENDIAN -union ldshape { - long double f; - struct { - uint64_t m; - uint16_t se; - } i; -}; -#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __BIG_ENDIAN -/* This is the m68k variant of 80-bit long double, and this definition only works - * on archs where the alignment requirement of uint64_t is <= 4. */ -union ldshape { - long double f; - struct { - uint16_t se; - uint16_t pad; - uint64_t m; - } i; -}; -#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __LITTLE_ENDIAN -union ldshape { - long double f; - struct { - uint64_t lo; - uint32_t mid; - uint16_t top; - uint16_t se; - } i; - struct { - uint64_t lo; - uint64_t hi; - } i2; -}; -#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __BIG_ENDIAN -union ldshape { - long double f; - struct { - uint16_t se; - uint16_t top; - uint32_t mid; - uint64_t lo; - } i; - struct { - uint64_t hi; - uint64_t lo; - } i2; -}; -#else -#error Unsupported long double representation -#endif - -/* Support non-nearest rounding mode. */ -#define WANT_ROUNDING 1 -/* Support signaling NaNs. */ -#define WANT_SNAN 0 - -#if WANT_SNAN -#error SNaN is unsupported -#else -#define issignalingf_inline(x) 0 -#define issignaling_inline(x) 0 -#endif - -#ifndef TOINT_INTRINSICS -#define TOINT_INTRINSICS 0 -#endif - -#if TOINT_INTRINSICS -/* Round x to nearest int in all rounding modes, ties have to be rounded - consistently with converttoint so the results match. If the result - would be outside of [-2^31, 2^31-1] then the semantics is unspecified. */ -static double_t roundtoint(double_t); - -/* Convert x to nearest int in all rounding modes, ties have to be rounded - consistently with roundtoint. If the result is not representible in an - int32_t then the semantics is unspecified. */ -static int32_t converttoint(double_t); -#endif - -/* Helps static branch prediction so hot path can be better optimized. */ -#ifdef __GNUC__ -#define predict_true(x) __builtin_expect(!!(x), 1) -#define predict_false(x) __builtin_expect(x, 0) -#else -#define predict_true(x) (x) -#define predict_false(x) (x) -#endif - -/* Evaluate an expression as the specified type. With standard excess - precision handling a type cast or assignment is enough (with - -ffloat-store an assignment is required, in old compilers argument - passing and return statement may not drop excess precision). */ - -static inline float eval_as_float(float x) -{ - float y = x; - return y; -} - -static inline double eval_as_double(double x) -{ - double y = x; - return y; -} - -/* fp_barrier returns its input, but limits code transformations - as if it had a side-effect (e.g. observable io) and returned - an arbitrary value. */ - -#ifndef fp_barrierf -#define fp_barrierf fp_barrierf -static inline float fp_barrierf(float x) -{ - volatile float y = x; - return y; -} -#endif - -#ifndef fp_barrier -#define fp_barrier fp_barrier -static inline double fp_barrier(double x) -{ - volatile double y = x; - return y; -} -#endif - -#ifndef fp_barrierl -#define fp_barrierl fp_barrierl -static inline long double fp_barrierl(long double x) -{ - volatile long double y = x; - return y; -} -#endif - -/* fp_force_eval ensures that the input value is computed when that's - otherwise unused. To prevent the constant folding of the input - expression, an additional fp_barrier may be needed or a compilation - mode that does so (e.g. -frounding-math in gcc). Then it can be - used to evaluate an expression for its fenv side-effects only. */ - -#ifndef fp_force_evalf -#define fp_force_evalf fp_force_evalf -static inline void fp_force_evalf(float x) -{ - volatile float y; - y = x; -} -#endif - -#ifndef fp_force_eval -#define fp_force_eval fp_force_eval -static inline void fp_force_eval(double x) -{ - volatile double y; - y = x; -} -#endif - -#ifndef fp_force_evall -#define fp_force_evall fp_force_evall -static inline void fp_force_evall(long double x) -{ - volatile long double y; - y = x; -} -#endif - -#define FORCE_EVAL(x) \ - do { \ - if (sizeof(x) == sizeof(float)) { \ - fp_force_evalf(x); \ - } else if (sizeof(x) == sizeof(double)) { \ - fp_force_eval(x); \ - } else { \ - fp_force_evall(x); \ - } \ - } while (0) - -#define asuint(f) \ - ((union { \ - float _f; \ - uint32_t _i; \ - }){f}) \ - ._i -#define asfloat(i) \ - ((union { \ - uint32_t _i; \ - float _f; \ - }){i}) \ - ._f -#define asuint64(f) \ - ((union { \ - double _f; \ - uint64_t _i; \ - }){f}) \ - ._i -#define asdouble(i) \ - ((union { \ - uint64_t _i; \ - double _f; \ - }){i}) \ - ._f - -/* error handling functions */ -float __math_xflowf(uint32_t, float); -float __math_uflowf(uint32_t); -float __math_oflowf(uint32_t); -float __math_divzerof(uint32_t); -float __math_invalidf(float); -double __math_xflow(uint32_t, double); -double __math_uflow(uint32_t); -double __math_oflow(uint32_t); -double __math_divzero(uint32_t); -double __math_invalid(double); -#if LDBL_MANT_DIG != DBL_MANT_DIG -long double __math_invalidl(long double); -#endif - -#endif // RUX_CONFIG_FP_SIMD - -#endif // _LIBM_H diff --git a/ulib/ruxlibc/c/locale.c b/ulib/ruxlibc/c/locale.c deleted file mode 100644 index 5cbc619ea..000000000 --- a/ulib/ruxlibc/c/locale.c +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include -#include - -// TODO -char *setlocale(int __category, const char *__locale) -{ - unimplemented(); - return NULL; -} - -static const struct lconv posix_lconv = { - .decimal_point = ".", - .thousands_sep = "", - .grouping = "", - .int_curr_symbol = "", - .currency_symbol = "", - .mon_decimal_point = "", - .mon_thousands_sep = "", - .mon_grouping = "", - .positive_sign = "", - .negative_sign = "", - .int_frac_digits = CHAR_MAX, - .frac_digits = CHAR_MAX, - .p_cs_precedes = CHAR_MAX, - .p_sep_by_space = CHAR_MAX, - .n_cs_precedes = CHAR_MAX, - .n_sep_by_space = CHAR_MAX, - .p_sign_posn = CHAR_MAX, - .n_sign_posn = CHAR_MAX, - .int_p_cs_precedes = CHAR_MAX, - .int_p_sep_by_space = CHAR_MAX, - .int_n_cs_precedes = CHAR_MAX, - .int_n_sep_by_space = CHAR_MAX, - .int_p_sign_posn = CHAR_MAX, - .int_n_sign_posn = CHAR_MAX, -}; - -struct lconv *localeconv(void) -{ - return (void *)&posix_lconv; -} diff --git a/ulib/ruxlibc/c/log.c b/ulib/ruxlibc/c/log.c deleted file mode 100644 index 2dc35dad5..000000000 --- a/ulib/ruxlibc/c/log.c +++ /dev/null @@ -1,404 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#ifdef RUX_CONFIG_FP_SIMD - -#include -#include -#include -#include - -#include "libm.h" - -struct log_data { - double ln2hi; - double ln2lo; - double poly[LOG_POLY_ORDER - 1]; - double poly1[LOG_POLY1_ORDER - 1]; - struct { - double invc, logc; - } tab[1 << LOG_TABLE_BITS]; -#if !__FP_FAST_FMA - struct { - double chi, clo; - } tab2[1 << LOG_TABLE_BITS]; -#endif -}; - -const struct log_data __log_data = { - .ln2hi = 0x1.62e42fefa3800p-1, - .ln2lo = 0x1.ef35793c76730p-45, - .poly1 = - { - -0x1p-1, - 0x1.5555555555577p-2, - -0x1.ffffffffffdcbp-3, - 0x1.999999995dd0cp-3, - -0x1.55555556745a7p-3, - 0x1.24924a344de3p-3, - -0x1.fffffa4423d65p-4, - 0x1.c7184282ad6cap-4, - -0x1.999eb43b068ffp-4, - 0x1.78182f7afd085p-4, - -0x1.5521375d145cdp-4, - }, - .poly = - { - -0x1.0000000000001p-1, - 0x1.555555551305bp-2, - -0x1.fffffffeb459p-3, - 0x1.999b324f10111p-3, - -0x1.55575e506c89fp-3, - }, - .tab = - { - {0x1.734f0c3e0de9fp+0, -0x1.7cc7f79e69000p-2}, - {0x1.713786a2ce91fp+0, -0x1.76feec20d0000p-2}, - {0x1.6f26008fab5a0p+0, -0x1.713e31351e000p-2}, - {0x1.6d1a61f138c7dp+0, -0x1.6b85b38287800p-2}, - {0x1.6b1490bc5b4d1p+0, -0x1.65d5590807800p-2}, - {0x1.69147332f0cbap+0, -0x1.602d076180000p-2}, - {0x1.6719f18224223p+0, -0x1.5a8ca86909000p-2}, - {0x1.6524f99a51ed9p+0, -0x1.54f4356035000p-2}, - {0x1.63356aa8f24c4p+0, -0x1.4f637c36b4000p-2}, - {0x1.614b36b9ddc14p+0, -0x1.49da7fda85000p-2}, - {0x1.5f66452c65c4cp+0, -0x1.445923989a800p-2}, - {0x1.5d867b5912c4fp+0, -0x1.3edf439b0b800p-2}, - {0x1.5babccb5b90dep+0, -0x1.396ce448f7000p-2}, - {0x1.59d61f2d91a78p+0, -0x1.3401e17bda000p-2}, - {0x1.5805612465687p+0, -0x1.2e9e2ef468000p-2}, - {0x1.56397cee76bd3p+0, -0x1.2941b3830e000p-2}, - {0x1.54725e2a77f93p+0, -0x1.23ec58cda8800p-2}, - {0x1.52aff42064583p+0, -0x1.1e9e129279000p-2}, - {0x1.50f22dbb2bddfp+0, -0x1.1956d2b48f800p-2}, - {0x1.4f38f4734ded7p+0, -0x1.141679ab9f800p-2}, - {0x1.4d843cfde2840p+0, -0x1.0edd094ef9800p-2}, - {0x1.4bd3ec078a3c8p+0, -0x1.09aa518db1000p-2}, - {0x1.4a27fc3e0258ap+0, -0x1.047e65263b800p-2}, - {0x1.4880524d48434p+0, -0x1.feb224586f000p-3}, - {0x1.46dce1b192d0bp+0, -0x1.f474a7517b000p-3}, - {0x1.453d9d3391854p+0, -0x1.ea4443d103000p-3}, - {0x1.43a2744b4845ap+0, -0x1.e020d44e9b000p-3}, - {0x1.420b54115f8fbp+0, -0x1.d60a22977f000p-3}, - {0x1.40782da3ef4b1p+0, -0x1.cc00104959000p-3}, - {0x1.3ee8f5d57fe8fp+0, -0x1.c202956891000p-3}, - {0x1.3d5d9a00b4ce9p+0, -0x1.b81178d811000p-3}, - {0x1.3bd60c010c12bp+0, -0x1.ae2c9ccd3d000p-3}, - {0x1.3a5242b75dab8p+0, -0x1.a45402e129000p-3}, - {0x1.38d22cd9fd002p+0, -0x1.9a877681df000p-3}, - {0x1.3755bc5847a1cp+0, -0x1.90c6d69483000p-3}, - {0x1.35dce49ad36e2p+0, -0x1.87120a645c000p-3}, - {0x1.34679984dd440p+0, -0x1.7d68fb4143000p-3}, - {0x1.32f5cceffcb24p+0, -0x1.73cb83c627000p-3}, - {0x1.3187775a10d49p+0, -0x1.6a39a9b376000p-3}, - {0x1.301c8373e3990p+0, -0x1.60b3154b7a000p-3}, - {0x1.2eb4ebb95f841p+0, -0x1.5737d76243000p-3}, - {0x1.2d50a0219a9d1p+0, -0x1.4dc7b8fc23000p-3}, - {0x1.2bef9a8b7fd2ap+0, -0x1.4462c51d20000p-3}, - {0x1.2a91c7a0c1babp+0, -0x1.3b08abc830000p-3}, - {0x1.293726014b530p+0, -0x1.31b996b490000p-3}, - {0x1.27dfa5757a1f5p+0, -0x1.2875490a44000p-3}, - {0x1.268b39b1d3bbfp+0, -0x1.1f3b9f879a000p-3}, - {0x1.2539d838ff5bdp+0, -0x1.160c8252ca000p-3}, - {0x1.23eb7aac9083bp+0, -0x1.0ce7f57f72000p-3}, - {0x1.22a012ba940b6p+0, -0x1.03cdc49fea000p-3}, - {0x1.2157996cc4132p+0, -0x1.f57bdbc4b8000p-4}, - {0x1.201201dd2fc9bp+0, -0x1.e370896404000p-4}, - {0x1.1ecf4494d480bp+0, -0x1.d17983ef94000p-4}, - {0x1.1d8f5528f6569p+0, -0x1.bf9674ed8a000p-4}, - {0x1.1c52311577e7cp+0, -0x1.adc79202f6000p-4}, - {0x1.1b17c74cb26e9p+0, -0x1.9c0c3e7288000p-4}, - {0x1.19e010c2c1ab6p+0, -0x1.8a646b372c000p-4}, - {0x1.18ab07bb670bdp+0, -0x1.78d01b3ac0000p-4}, - {0x1.1778a25efbcb6p+0, -0x1.674f145380000p-4}, - {0x1.1648d354c31dap+0, -0x1.55e0e6d878000p-4}, - {0x1.151b990275fddp+0, -0x1.4485cdea1e000p-4}, - {0x1.13f0ea432d24cp+0, -0x1.333d94d6aa000p-4}, - {0x1.12c8b7210f9dap+0, -0x1.22079f8c56000p-4}, - {0x1.11a3028ecb531p+0, -0x1.10e4698622000p-4}, - {0x1.107fbda8434afp+0, -0x1.ffa6c6ad20000p-5}, - {0x1.0f5ee0f4e6bb3p+0, -0x1.dda8d4a774000p-5}, - {0x1.0e4065d2a9fcep+0, -0x1.bbcece4850000p-5}, - {0x1.0d244632ca521p+0, -0x1.9a1894012c000p-5}, - {0x1.0c0a77ce2981ap+0, -0x1.788583302c000p-5}, - {0x1.0af2f83c636d1p+0, -0x1.5715e67d68000p-5}, - {0x1.09ddb98a01339p+0, -0x1.35c8a49658000p-5}, - {0x1.08cabaf52e7dfp+0, -0x1.149e364154000p-5}, - {0x1.07b9f2f4e28fbp+0, -0x1.e72c082eb8000p-6}, - {0x1.06ab58c358f19p+0, -0x1.a55f152528000p-6}, - {0x1.059eea5ecf92cp+0, -0x1.63d62cf818000p-6}, - {0x1.04949cdd12c90p+0, -0x1.228fb8caa0000p-6}, - {0x1.038c6c6f0ada9p+0, -0x1.c317b20f90000p-7}, - {0x1.02865137932a9p+0, -0x1.419355daa0000p-7}, - {0x1.0182427ea7348p+0, -0x1.81203c2ec0000p-8}, - {0x1.008040614b195p+0, -0x1.0040979240000p-9}, - {0x1.fe01ff726fa1ap-1, 0x1.feff384900000p-9}, - {0x1.fa11cc261ea74p-1, 0x1.7dc41353d0000p-7}, - {0x1.f6310b081992ep-1, 0x1.3cea3c4c28000p-6}, - {0x1.f25f63ceeadcdp-1, 0x1.b9fc114890000p-6}, - {0x1.ee9c8039113e7p-1, 0x1.1b0d8ce110000p-5}, - {0x1.eae8078cbb1abp-1, 0x1.58a5bd001c000p-5}, - {0x1.e741aa29d0c9bp-1, 0x1.95c8340d88000p-5}, - {0x1.e3a91830a99b5p-1, 0x1.d276aef578000p-5}, - {0x1.e01e009609a56p-1, 0x1.07598e598c000p-4}, - {0x1.dca01e577bb98p-1, 0x1.253f5e30d2000p-4}, - {0x1.d92f20b7c9103p-1, 0x1.42edd8b380000p-4}, - {0x1.d5cac66fb5ccep-1, 0x1.606598757c000p-4}, - {0x1.d272caa5ede9dp-1, 0x1.7da76356a0000p-4}, - {0x1.cf26e3e6b2ccdp-1, 0x1.9ab434e1c6000p-4}, - {0x1.cbe6da2a77902p-1, 0x1.b78c7bb0d6000p-4}, - {0x1.c8b266d37086dp-1, 0x1.d431332e72000p-4}, - {0x1.c5894bd5d5804p-1, 0x1.f0a3171de6000p-4}, - {0x1.c26b533bb9f8cp-1, 0x1.067152b914000p-3}, - {0x1.bf583eeece73fp-1, 0x1.147858292b000p-3}, - {0x1.bc4fd75db96c1p-1, 0x1.2266ecdca3000p-3}, - {0x1.b951e0c864a28p-1, 0x1.303d7a6c55000p-3}, - {0x1.b65e2c5ef3e2cp-1, 0x1.3dfc33c331000p-3}, - {0x1.b374867c9888bp-1, 0x1.4ba366b7a8000p-3}, - {0x1.b094b211d304ap-1, 0x1.5933928d1f000p-3}, - {0x1.adbe885f2ef7ep-1, 0x1.66acd2418f000p-3}, - {0x1.aaf1d31603da2p-1, 0x1.740f8ec669000p-3}, - {0x1.a82e63fd358a7p-1, 0x1.815c0f51af000p-3}, - {0x1.a5740ef09738bp-1, 0x1.8e92954f68000p-3}, - {0x1.a2c2a90ab4b27p-1, 0x1.9bb3602f84000p-3}, - {0x1.a01a01393f2d1p-1, 0x1.a8bed1c2c0000p-3}, - {0x1.9d79f24db3c1bp-1, 0x1.b5b515c01d000p-3}, - {0x1.9ae2505c7b190p-1, 0x1.c2967ccbcc000p-3}, - {0x1.9852ef297ce2fp-1, 0x1.cf635d5486000p-3}, - {0x1.95cbaeea44b75p-1, 0x1.dc1bd3446c000p-3}, - {0x1.934c69de74838p-1, 0x1.e8c01b8cfe000p-3}, - {0x1.90d4f2f6752e6p-1, 0x1.f5509c0179000p-3}, - {0x1.8e6528effd79dp-1, 0x1.00e6c121fb800p-2}, - {0x1.8bfce9fcc007cp-1, 0x1.071b80e93d000p-2}, - {0x1.899c0dabec30ep-1, 0x1.0d46b9e867000p-2}, - {0x1.87427aa2317fbp-1, 0x1.13687334bd000p-2}, - {0x1.84f00acb39a08p-1, 0x1.1980d67234800p-2}, - {0x1.82a49e8653e55p-1, 0x1.1f8ffe0cc8000p-2}, - {0x1.8060195f40260p-1, 0x1.2595fd7636800p-2}, - {0x1.7e22563e0a329p-1, 0x1.2b9300914a800p-2}, - {0x1.7beb377dcb5adp-1, 0x1.3187210436000p-2}, - {0x1.79baa679725c2p-1, 0x1.377266dec1800p-2}, - {0x1.77907f2170657p-1, 0x1.3d54ffbaf3000p-2}, - {0x1.756cadbd6130cp-1, 0x1.432eee32fe000p-2}, - }, -#if !__FP_FAST_FMA - .tab2 = - { - {0x1.61000014fb66bp-1, 0x1.e026c91425b3cp-56}, - {0x1.63000034db495p-1, 0x1.dbfea48005d41p-55}, - {0x1.650000d94d478p-1, 0x1.e7fa786d6a5b7p-55}, - {0x1.67000074e6fadp-1, 0x1.1fcea6b54254cp-57}, - {0x1.68ffffedf0faep-1, -0x1.c7e274c590efdp-56}, - {0x1.6b0000763c5bcp-1, -0x1.ac16848dcda01p-55}, - {0x1.6d0001e5cc1f6p-1, 0x1.33f1c9d499311p-55}, - {0x1.6efffeb05f63ep-1, -0x1.e80041ae22d53p-56}, - {0x1.710000e86978p-1, 0x1.bff6671097952p-56}, - {0x1.72ffffc67e912p-1, 0x1.c00e226bd8724p-55}, - {0x1.74fffdf81116ap-1, -0x1.e02916ef101d2p-57}, - {0x1.770000f679c9p-1, -0x1.7fc71cd549c74p-57}, - {0x1.78ffffa7ec835p-1, 0x1.1bec19ef50483p-55}, - {0x1.7affffe20c2e6p-1, -0x1.07e1729cc6465p-56}, - {0x1.7cfffed3fc9p-1, -0x1.08072087b8b1cp-55}, - {0x1.7efffe9261a76p-1, 0x1.dc0286d9df9aep-55}, - {0x1.81000049ca3e8p-1, 0x1.97fd251e54c33p-55}, - {0x1.8300017932c8fp-1, -0x1.afee9b630f381p-55}, - {0x1.850000633739cp-1, 0x1.9bfbf6b6535bcp-55}, - {0x1.87000204289c6p-1, -0x1.bbf65f3117b75p-55}, - {0x1.88fffebf57904p-1, -0x1.9006ea23dcb57p-55}, - {0x1.8b00022bc04dfp-1, -0x1.d00df38e04b0ap-56}, - {0x1.8cfffe50c1b8ap-1, -0x1.8007146ff9f05p-55}, - {0x1.8effffc918e43p-1, 0x1.3817bd07a7038p-55}, - {0x1.910001efa5fc7p-1, 0x1.93e9176dfb403p-55}, - {0x1.9300013467bb9p-1, 0x1.f804e4b980276p-56}, - {0x1.94fffe6ee076fp-1, -0x1.f7ef0d9ff622ep-55}, - {0x1.96fffde3c12d1p-1, -0x1.082aa962638bap-56}, - {0x1.98ffff4458a0dp-1, -0x1.7801b9164a8efp-55}, - {0x1.9afffdd982e3ep-1, -0x1.740e08a5a9337p-55}, - {0x1.9cfffed49fb66p-1, 0x1.fce08c19bep-60}, - {0x1.9f00020f19c51p-1, -0x1.a3faa27885b0ap-55}, - {0x1.a10001145b006p-1, 0x1.4ff489958da56p-56}, - {0x1.a300007bbf6fap-1, 0x1.cbeab8a2b6d18p-55}, - {0x1.a500010971d79p-1, 0x1.8fecadd78793p-55}, - {0x1.a70001df52e48p-1, -0x1.f41763dd8abdbp-55}, - {0x1.a90001c593352p-1, -0x1.ebf0284c27612p-55}, - {0x1.ab0002a4f3e4bp-1, -0x1.9fd043cff3f5fp-57}, - {0x1.acfffd7ae1ed1p-1, -0x1.23ee7129070b4p-55}, - {0x1.aefffee510478p-1, 0x1.a063ee00edea3p-57}, - {0x1.b0fffdb650d5bp-1, 0x1.a06c8381f0ab9p-58}, - {0x1.b2ffffeaaca57p-1, -0x1.9011e74233c1dp-56}, - {0x1.b4fffd995badcp-1, -0x1.9ff1068862a9fp-56}, - {0x1.b7000249e659cp-1, 0x1.aff45d0864f3ep-55}, - {0x1.b8ffff987164p-1, 0x1.cfe7796c2c3f9p-56}, - {0x1.bafffd204cb4fp-1, -0x1.3ff27eef22bc4p-57}, - {0x1.bcfffd2415c45p-1, -0x1.cffb7ee3bea21p-57}, - {0x1.beffff86309dfp-1, -0x1.14103972e0b5cp-55}, - {0x1.c0fffe1b57653p-1, 0x1.bc16494b76a19p-55}, - {0x1.c2ffff1fa57e3p-1, -0x1.4feef8d30c6edp-57}, - {0x1.c4fffdcbfe424p-1, -0x1.43f68bcec4775p-55}, - {0x1.c6fffed54b9f7p-1, 0x1.47ea3f053e0ecp-55}, - {0x1.c8fffeb998fd5p-1, 0x1.383068df992f1p-56}, - {0x1.cb0002125219ap-1, -0x1.8fd8e64180e04p-57}, - {0x1.ccfffdd94469cp-1, 0x1.e7ebe1cc7ea72p-55}, - {0x1.cefffeafdc476p-1, 0x1.ebe39ad9f88fep-55}, - {0x1.d1000169af82bp-1, 0x1.57d91a8b95a71p-56}, - {0x1.d30000d0ff71dp-1, 0x1.9c1906970c7dap-55}, - {0x1.d4fffea790fc4p-1, -0x1.80e37c558fe0cp-58}, - {0x1.d70002edc87e5p-1, -0x1.f80d64dc10f44p-56}, - {0x1.d900021dc82aap-1, -0x1.47c8f94fd5c5cp-56}, - {0x1.dafffd86b0283p-1, 0x1.c7f1dc521617ep-55}, - {0x1.dd000296c4739p-1, 0x1.8019eb2ffb153p-55}, - {0x1.defffe54490f5p-1, 0x1.e00d2c652cc89p-57}, - {0x1.e0fffcdabf694p-1, -0x1.f8340202d69d2p-56}, - {0x1.e2fffdb52c8ddp-1, 0x1.b00c1ca1b0864p-56}, - {0x1.e4ffff24216efp-1, 0x1.2ffa8b094ab51p-56}, - {0x1.e6fffe88a5e11p-1, -0x1.7f673b1efbe59p-58}, - {0x1.e9000119eff0dp-1, -0x1.4808d5e0bc801p-55}, - {0x1.eafffdfa51744p-1, 0x1.80006d54320b5p-56}, - {0x1.ed0001a127fa1p-1, -0x1.002f860565c92p-58}, - {0x1.ef00007babcc4p-1, -0x1.540445d35e611p-55}, - {0x1.f0ffff57a8d02p-1, -0x1.ffb3139ef9105p-59}, - {0x1.f30001ee58ac7p-1, 0x1.a81acf2731155p-55}, - {0x1.f4ffff5823494p-1, 0x1.a3f41d4d7c743p-55}, - {0x1.f6ffffca94c6bp-1, -0x1.202f41c987875p-57}, - {0x1.f8fffe1f9c441p-1, 0x1.77dd1f477e74bp-56}, - {0x1.fafffd2e0e37ep-1, -0x1.f01199a7ca331p-57}, - {0x1.fd0001c77e49ep-1, 0x1.181ee4bceacb1p-56}, - {0x1.feffff7e0c331p-1, -0x1.e05370170875ap-57}, - {0x1.00ffff465606ep+0, -0x1.a7ead491c0adap-55}, - {0x1.02ffff3867a58p+0, -0x1.77f69c3fcb2ep-54}, - {0x1.04ffffdfc0d17p+0, 0x1.7bffe34cb945bp-54}, - {0x1.0700003cd4d82p+0, 0x1.20083c0e456cbp-55}, - {0x1.08ffff9f2cbe8p+0, -0x1.dffdfbe37751ap-57}, - {0x1.0b000010cda65p+0, -0x1.13f7faee626ebp-54}, - {0x1.0d00001a4d338p+0, 0x1.07dfa79489ff7p-55}, - {0x1.0effffadafdfdp+0, -0x1.7040570d66bcp-56}, - {0x1.110000bbafd96p+0, 0x1.e80d4846d0b62p-55}, - {0x1.12ffffae5f45dp+0, 0x1.dbffa64fd36efp-54}, - {0x1.150000dd59ad9p+0, 0x1.a0077701250aep-54}, - {0x1.170000f21559ap+0, 0x1.dfdf9e2e3deeep-55}, - {0x1.18ffffc275426p+0, 0x1.10030dc3b7273p-54}, - {0x1.1b000123d3c59p+0, 0x1.97f7980030188p-54}, - {0x1.1cffff8299eb7p+0, -0x1.5f932ab9f8c67p-57}, - {0x1.1effff48ad4p+0, 0x1.37fbf9da75bebp-54}, - {0x1.210000c8b86a4p+0, 0x1.f806b91fd5b22p-54}, - {0x1.2300003854303p+0, 0x1.3ffc2eb9fbf33p-54}, - {0x1.24fffffbcf684p+0, 0x1.601e77e2e2e72p-56}, - {0x1.26ffff52921d9p+0, 0x1.ffcbb767f0c61p-56}, - {0x1.2900014933a3cp+0, -0x1.202ca3c02412bp-56}, - {0x1.2b00014556313p+0, -0x1.2808233f21f02p-54}, - {0x1.2cfffebfe523bp+0, -0x1.8ff7e384fdcf2p-55}, - {0x1.2f0000bb8ad96p+0, -0x1.5ff51503041c5p-55}, - {0x1.30ffffb7ae2afp+0, -0x1.10071885e289dp-55}, - {0x1.32ffffeac5f7fp+0, -0x1.1ff5d3fb7b715p-54}, - {0x1.350000ca66756p+0, 0x1.57f82228b82bdp-54}, - {0x1.3700011fbf721p+0, 0x1.000bac40dd5ccp-55}, - {0x1.38ffff9592fb9p+0, -0x1.43f9d2db2a751p-54}, - {0x1.3b00004ddd242p+0, 0x1.57f6b707638e1p-55}, - {0x1.3cffff5b2c957p+0, 0x1.a023a10bf1231p-56}, - {0x1.3efffeab0b418p+0, 0x1.87f6d66b152bp-54}, - {0x1.410001532aff4p+0, 0x1.7f8375f198524p-57}, - {0x1.4300017478b29p+0, 0x1.301e672dc5143p-55}, - {0x1.44fffe795b463p+0, 0x1.9ff69b8b2895ap-55}, - {0x1.46fffe80475ep+0, -0x1.5c0b19bc2f254p-54}, - {0x1.48fffef6fc1e7p+0, 0x1.b4009f23a2a72p-54}, - {0x1.4afffe5bea704p+0, -0x1.4ffb7bf0d7d45p-54}, - {0x1.4d000171027dep+0, -0x1.9c06471dc6a3dp-54}, - {0x1.4f0000ff03ee2p+0, 0x1.77f890b85531cp-54}, - {0x1.5100012dc4bd1p+0, 0x1.004657166a436p-57}, - {0x1.530001605277ap+0, -0x1.6bfcece233209p-54}, - {0x1.54fffecdb704cp+0, -0x1.902720505a1d7p-55}, - {0x1.56fffef5f54a9p+0, 0x1.bbfe60ec96412p-54}, - {0x1.5900017e61012p+0, 0x1.87ec581afef9p-55}, - {0x1.5b00003c93e92p+0, -0x1.f41080abf0ccp-54}, - {0x1.5d0001d4919bcp+0, -0x1.8812afb254729p-54}, - {0x1.5efffe7b87a89p+0, -0x1.47eb780ed6904p-54}, - }, -#endif -}; - -#define T __log_data.tab -#define T2 __log_data.tab2 -#define B __log_data.poly1 -#define A __log_data.poly -#define Ln2hi __log_data.ln2hi -#define Ln2lo __log_data.ln2lo -#define N (1 << LOG_TABLE_BITS) -#define OFF 0x3fe6000000000000 - -static inline uint32_t top16(double x) -{ - return asuint64(x) >> 48; -} - -double log(double x) -{ - double_t w, z, r, r2, r3, y, invc, logc, kd, hi, lo; - uint64_t ix, iz, tmp; - uint32_t top; - int k, i; - - ix = asuint64(x); - top = top16(x); -#define LO asuint64(1.0 - 0x1p-4) -#define HI asuint64(1.0 + 0x1.09p-4) - if (predict_false(ix - LO < HI - LO)) { - if (WANT_ROUNDING && predict_false(ix == asuint64(1.0))) - return 0; - r = x - 1.0; - r2 = r * r; - r3 = r * r2; - y = r3 * - (B[1] + r * B[2] + r2 * B[3] + - r3 * (B[4] + r * B[5] + r2 * B[6] + r3 * (B[7] + r * B[8] + r2 * B[9] + r3 * B[10]))); - w = r * 0x1p27; - double_t rhi = r + w - w; - double_t rlo = r - rhi; - w = rhi * rhi * B[0]; - hi = r + w; - lo = r - hi + w; - lo += B[0] * rlo * (rhi + r); - y += lo; - y += hi; - return eval_as_double(y); - } - if (predict_false(top - 0x0010 >= 0x7ff0 - 0x0010)) { - if (ix * 2 == 0) - return __math_divzero(1); - if (ix == asuint64(INFINITY)) - return x; - if ((top & 0x8000) || (top & 0x7ff0) == 0x7ff0) - return __math_invalid(x); - ix = asuint64(x * 0x1p52); - ix -= 52ULL << 52; - } - - tmp = ix - OFF; - i = (tmp >> (52 - LOG_TABLE_BITS)) % N; - k = (int64_t)tmp >> 52; - iz = ix - (tmp & 0xfffULL << 52); - invc = T[i].invc; - logc = T[i].logc; - z = asdouble(iz); - -#if __FP_FAST_FMA - r = __builtin_fma(z, invc, -1.0); -#else - r = (z - T2[i].chi - T2[i].clo) * invc; -#endif - - kd = (double_t)k; - w = kd * Ln2hi + logc; - hi = w + r; - lo = w - hi + r + kd * Ln2lo; - r2 = r * r; - y = lo + r2 * A[0] + r * r2 * (A[1] + r * A[2] + r2 * (A[3] + r * A[4])) + hi; - return eval_as_double(y); -} - -#endif // RUX_CONFIG_FP_SIMD diff --git a/ulib/ruxlibc/c/math.c b/ulib/ruxlibc/c/math.c deleted file mode 100644 index 1f5266dfd..000000000 --- a/ulib/ruxlibc/c/math.c +++ /dev/null @@ -1,580 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#ifdef RUX_CONFIG_FP_SIMD - -#include -#include -#include -#include - -#include "libm.h" - -int __fpclassify(double x) -{ - union { - double f; - uint64_t i; - } u = {x}; - int e = u.i >> 52 & 0x7ff; - if (!e) - return u.i << 1 ? FP_SUBNORMAL : FP_ZERO; - if (e == 0x7ff) - return u.i << 12 ? FP_NAN : FP_INFINITE; - return FP_NORMAL; -} - -int __fpclassifyf(float x) -{ - union { - float f; - uint32_t i; - } u = {x}; - int e = u.i >> 23 & 0xff; - if (!e) - return u.i << 1 ? FP_SUBNORMAL : FP_ZERO; - if (e == 0xff) - return u.i << 9 ? FP_NAN : FP_INFINITE; - return FP_NORMAL; -} - -#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 -int __fpclassifyl(long double x) -{ - return __fpclassify(x); -} -#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 -int __fpclassifyl(long double x) -{ - union ldshape u = {x}; - int e = u.i.se & 0x7fff; - int msb = u.i.m >> 63; - if (!e && !msb) - return u.i.m ? FP_SUBNORMAL : FP_ZERO; - if (e == 0x7fff) { - /* The x86 variant of 80-bit extended precision only admits - * one representation of each infinity, with the mantissa msb - * necessarily set. The version with it clear is invalid/nan. - * The m68k variant, however, allows either, and tooling uses - * the version with it clear. */ - if (__BYTE_ORDER == __LITTLE_ENDIAN && !msb) - return FP_NAN; - return u.i.m << 1 ? FP_NAN : FP_INFINITE; - } - if (!msb) - return FP_NAN; - return FP_NORMAL; -} -#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 -int __fpclassifyl(long double x) -{ - union ldshape u = {x}; - int e = u.i.se & 0x7fff; - u.i.se = 0; - if (!e) - return u.i2.lo | u.i2.hi ? FP_SUBNORMAL : FP_ZERO; - if (e == 0x7fff) - return u.i2.lo | u.i2.hi ? FP_NAN : FP_INFINITE; - return FP_NORMAL; -} -#endif - -double fabs(double x) -{ - union { - double f; - uint64_t i; - } u = {x}; - u.i &= -1ULL / 2; - return u.f; -} - -static const double toint = 1 / DBL_EPSILON; - -double floor(double x) -{ - union { - double f; - uint64_t i; - } u = {x}; - int e = u.i >> 52 & 0x7ff; - double y; - - if (e >= 0x3ff + 52 || x == 0) - return x; - /* y = int(x) - x, where int(x) is an integer neighbor of x */ - if (u.i >> 63) - y = x - toint + toint - x; - else - y = x + toint - toint - x; - /* special case because of non-nearest rounding modes */ - if (e <= 0x3ff - 1) { - FORCE_EVAL(y); - return u.i >> 63 ? -1 : 0; - } - if (y > 0) - return x + y - 1; - return x + y; -} - -double rint(double x) -{ - unimplemented(); - return 0; -} - -long long llrint(double x) -{ - return rint(x); -} - -double sqrt(double x) -{ - unimplemented(); - return 0; -} - -double round(double x) -{ - unimplemented(); - return x; -} - -long double roundl(long double x) -{ - unimplemented(); - return x; -} - -long long llroundl(long double x) -{ - unimplemented(); - return x; -} - -double cos(double __x) -{ - unimplemented(); - return 0; -} - -double ceil(double x) -{ - union { - double f; - uint64_t i; - } u = {x}; - int e = u.i >> 52 & 0x7ff; - double_t y; - - if (e >= 0x3ff + 52 || x == 0) - return x; - if (u.i >> 63) - y = x - toint + toint - x; - else - y = x + toint - toint - x; - if (e <= 0x3ff - 1) { - FORCE_EVAL(y); - return u.i >> 63 ? -0.0 : 1; - } - if (y < 0) - return x + y + 1; - return x + y; -} - -// TODO -double sin(double __x) -{ - unimplemented(); - return 0; -} - -// TODO -double asin(double __x) -{ - unimplemented(); - return 0; -} - -long double ceill(long double x) -{ - unimplemented(); - return x; -} - -double acos(double x) -{ - unimplemented(); - return 0; -} - -// TODO -double atan(double x) -{ - unimplemented(); - return 0; -} - -// TODO -double atan2(double y, double x) -{ - unimplemented(); - return 0; -} - -double cosh(double x) -{ - unimplemented(); - return 0; -} - -// TODO -double exp(double x) -{ - unimplemented(); - return 0; -} - -// TODO -double frexp(double x, int *e) -{ - unimplemented(); - return 0; -} - -double ldexp(double x, int n) -{ - unimplemented(); - return 0; -} - -// TODO -double log10(double x) -{ - unimplemented(); - return 0; -} - -// TODO -double modf(double x, double *iptr) -{ - unimplemented(); - return 0; -} - -double sinh(double x) -{ - unimplemented(); - return 0; -} - -// TODO -double tan(double x) -{ - unimplemented(); - return 0; -} - -// TODO -double tanh(double x) -{ - unimplemented(); - return 0; -} - -double copysign(double x, double y) -{ - union { - double f; - uint64_t i; - } ux = {x}, uy = {y}; - ux.i &= -1ULL / 2; - ux.i |= uy.i & 1ULL << 63; - return ux.f; -} - -#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 -long double copysignl(long double x, long double y) -{ - return copysign(x, y); -} -#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384 -long double copysignl(long double x, long double y) -{ - union ldshape ux = {x}, uy = {y}; - ux.i.se &= 0x7fff; - ux.i.se |= uy.i.se & 0x8000; - return ux.f; -} -#endif - -double scalbn(double x, int n) -{ - union { - double f; - uint64_t i; - } u; - double_t y = x; - - if (n > 1023) { - y *= 0x1p1023; - n -= 1023; - if (n > 1023) { - y *= 0x1p1023; - n -= 1023; - if (n > 1023) - n = 1023; - } - } else if (n < -1022) { - /* make sure final n < -53 to avoid double - rounding in the subnormal range */ - y *= 0x1p-1022 * 0x1p53; - n += 1022 - 53; - if (n < -1022) { - y *= 0x1p-1022 * 0x1p53; - n += 1022 - 53; - if (n < -1022) - n = -1022; - } - } - u.i = (uint64_t)(0x3ff + n) << 52; - x = y * u.f; - return x; -} - -#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 -long double scalbnl(long double x, int n) -{ - return scalbn(x, n); -} -#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384 -long double scalbnl(long double x, int n) -{ - union ldshape u; - - if (n > 16383) { - x *= 0x1p16383L; - n -= 16383; - if (n > 16383) { - x *= 0x1p16383L; - n -= 16383; - if (n > 16383) - n = 16383; - } - } else if (n < -16382) { - x *= 0x1p-16382L * 0x1p113L; - n += 16382 - 113; - if (n < -16382) { - x *= 0x1p-16382L * 0x1p113L; - n += 16382 - 113; - if (n < -16382) - n = -16382; - } - } - u.f = 1.0; - u.i.se = 0x3fff + n; - return x * u.f; -} -#endif - -double fmod(double x, double y) -{ - union { - double f; - uint64_t i; - } ux = {x}, uy = {y}; - int ex = ux.i >> 52 & 0x7ff; - int ey = uy.i >> 52 & 0x7ff; - int sx = ux.i >> 63; - uint64_t i; - - /* in the followings uxi should be ux.i, but then gcc wrongly adds */ - /* float load/store to inner loops ruining performance and code size */ - uint64_t uxi = ux.i; - - if (uy.i << 1 == 0 || isnan(y) || ex == 0x7ff) - return (x * y) / (x * y); - if (uxi << 1 <= uy.i << 1) { - if (uxi << 1 == uy.i << 1) - return 0 * x; - return x; - } - - /* normalize x and y */ - if (!ex) { - for (i = uxi << 12; i >> 63 == 0; ex--, i <<= 1) - ; - uxi <<= -ex + 1; - } else { - uxi &= -1ULL >> 12; - uxi |= 1ULL << 52; - } - if (!ey) { - for (i = uy.i << 12; i >> 63 == 0; ey--, i <<= 1) - ; - uy.i <<= -ey + 1; - } else { - uy.i &= -1ULL >> 12; - uy.i |= 1ULL << 52; - } - - /* x mod y */ - for (; ex > ey; ex--) { - i = uxi - uy.i; - if (i >> 63 == 0) { - if (i == 0) - return 0 * x; - uxi = i; - } - uxi <<= 1; - } - i = uxi - uy.i; - if (i >> 63 == 0) { - if (i == 0) - return 0 * x; - uxi = i; - } - for (; uxi >> 52 == 0; uxi <<= 1, ex--) - ; - - /* scale result */ - if (ex > 0) { - uxi -= 1ULL << 52; - uxi |= (uint64_t)ex << 52; - } else { - uxi >>= -ex + 1; - } - uxi |= (uint64_t)sx << 63; - ux.i = uxi; - return ux.f; -} - -// x86_64 has specific implementation -#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 -long double fmodl(long double x, long double y) -{ - return fmod(x, y); -} -#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384 -long double fmodl(long double x, long double y) -{ - union ldshape ux = {x}, uy = {y}; - int ex = ux.i.se & 0x7fff; - int ey = uy.i.se & 0x7fff; - int sx = ux.i.se & 0x8000; - - if (y == 0 || isnan(y) || ex == 0x7fff) - return (x * y) / (x * y); - ux.i.se = ex; - uy.i.se = ey; - if (ux.f <= uy.f) { - if (ux.f == uy.f) - return 0 * x; - return x; - } - - /* normalize x and y */ - if (!ex) { - ux.f *= 0x1p120f; - ex = ux.i.se - 120; - } - if (!ey) { - uy.f *= 0x1p120f; - ey = uy.i.se - 120; - } - - /* x mod y */ -#if LDBL_MANT_DIG == 64 - uint64_t i, mx, my; - mx = ux.i.m; - my = uy.i.m; - for (; ex > ey; ex--) { - i = mx - my; - if (mx >= my) { - if (i == 0) - return 0 * x; - mx = 2 * i; - } else if (2 * mx < mx) { - mx = 2 * mx - my; - } else { - mx = 2 * mx; - } - } - i = mx - my; - if (mx >= my) { - if (i == 0) - return 0 * x; - mx = i; - } - for (; mx >> 63 == 0; mx *= 2, ex--) - ; - ux.i.m = mx; -#elif LDBL_MANT_DIG == 113 - uint64_t hi, lo, xhi, xlo, yhi, ylo; - xhi = (ux.i2.hi & -1ULL >> 16) | 1ULL << 48; - yhi = (uy.i2.hi & -1ULL >> 16) | 1ULL << 48; - xlo = ux.i2.lo; - ylo = uy.i2.lo; - for (; ex > ey; ex--) { - hi = xhi - yhi; - lo = xlo - ylo; - if (xlo < ylo) - hi -= 1; - if (hi >> 63 == 0) { - if ((hi | lo) == 0) - return 0 * x; - xhi = 2 * hi + (lo >> 63); - xlo = 2 * lo; - } else { - xhi = 2 * xhi + (xlo >> 63); - xlo = 2 * xlo; - } - } - hi = xhi - yhi; - lo = xlo - ylo; - if (xlo < ylo) - hi -= 1; - if (hi >> 63 == 0) { - if ((hi | lo) == 0) - return 0 * x; - xhi = hi; - xlo = lo; - } - for (; xhi >> 48 == 0; xhi = 2 * xhi + (xlo >> 63), xlo = 2 * xlo, ex--) - ; - ux.i2.hi = xhi; - ux.i2.lo = xlo; -#endif - - /* scale result */ - if (ex <= 0) { - ux.i.se = (ex + 120) | sx; - ux.f *= 0x1p-120f; - } else - ux.i.se = ex | sx; - return ux.f; -} -#endif - -#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 -long double fabsl(long double x) -{ - return fabs(x); -} -#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384 -long double fabsl(long double x) -{ - union ldshape u = {x}; - - u.i.se &= 0x7fff; - return u.f; -} -#endif - -#endif // RUX_CONFIG_FP_SIMD diff --git a/ulib/ruxlibc/c/network.c b/ulib/ruxlibc/c/network.c deleted file mode 100644 index 0cd8c1bd2..000000000 --- a/ulib/ruxlibc/c/network.c +++ /dev/null @@ -1,243 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A - * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. - */ - -#ifdef RUX_CONFIG_NET - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -int h_errno; - -static const char gai_msgs[] = "Invalid flags\0" - "Name does not resolve\0" - "Try again\0" - "Non-recoverable error\0" - "Unknown error\0" - "Unrecognized address family or invalid length\0" - "Unrecognized socket type\0" - "Unrecognized service\0" - "Unknown error\0" - "Out of memory\0" - "System error\0" - "Overflow\0" - "\0Unknown error"; - -const char *gai_strerror(int ecode) -{ - const char *s; - for (s = gai_msgs, ecode++; ecode && *s; ecode++, s++) - for (; *s; s++) - ; - if (!*s) - s++; - return s; -} - -static const char msgs[] = "Host not found\0" - "Try again\0" - "Non-recoverable error\0" - "Address not available\0" - "\0Unknown error"; - -const char *hstrerror(int ecode) -{ - const char *s; - for (s = msgs, ecode--; ecode && *s; ecode--, s++) - for (; *s; s++) - ; - if (!*s) - s++; - return s; -} - -static __inline uint16_t __bswap_16(uint16_t __x) -{ - return __x << 8 | __x >> 8; -} - -static __inline uint32_t __bswap_32(uint32_t __x) -{ - return __x >> 24 | (__x >> 8 & 0xff00) | (__x << 8 & 0xff0000) | __x << 24; -} - -uint32_t htonl(uint32_t n) -{ - union { - int i; - char c; - } u = {1}; - return u.c ? __bswap_32(n) : n; -} - -uint16_t htons(uint16_t n) -{ - union { - int i; - char c; - } u = {1}; - return u.c ? __bswap_16(n) : n; -} - -uint32_t ntohl(uint32_t n) -{ - union { - int i; - char c; - } u = {1}; - return u.c ? __bswap_32(n) : n; -} - -uint16_t ntohs(uint16_t n) -{ - union { - int i; - char c; - } u = {1}; - return u.c ? __bswap_16(n) : n; -} - -static int hexval(unsigned c) -{ - if (c - '0' < 10) - return c - '0'; - c |= 32; - if (c - 'a' < 6) - return c - 'a' + 10; - return -1; -} - -int inet_pton(int af, const char *__restrict s, void *__restrict a0) -{ - uint16_t ip[8]; - unsigned char *a = a0; - int i, j, v, d, brk = -1, need_v4 = 0; - - if (af == AF_INET) { - for (i = 0; i < 4; i++) { - for (v = j = 0; j < 3 && isdigit(s[j]); j++) v = 10 * v + s[j] - '0'; - if (j == 0 || (j > 1 && s[0] == '0') || v > 255) - return 0; - a[i] = v; - if (s[j] == 0 && i == 3) - return 1; - if (s[j] != '.') - return 0; - s += j + 1; - } - return 0; - } else if (af != AF_INET6) { - errno = EAFNOSUPPORT; - return -1; - } - - if (*s == ':' && *++s != ':') - return 0; - - for (i = 0;; i++) { - if (s[0] == ':' && brk < 0) { - brk = i; - ip[i & 7] = 0; - if (!*++s) - break; - if (i == 7) - return 0; - continue; - } - for (v = j = 0; j < 4 && (d = hexval(s[j])) >= 0; j++) v = 16 * v + d; - if (j == 0) - return 0; - ip[i & 7] = v; - if (!s[j] && (brk >= 0 || i == 7)) - break; - if (i == 7) - return 0; - if (s[j] != ':') { - if (s[j] != '.' || (i < 6 && brk < 0)) - return 0; - need_v4 = 1; - i++; - break; - } - s += j + 1; - } - if (brk >= 0) { - for (j = 0; j < 7 - i; j++) ip[brk + j] = 0; - memmove(ip + brk + 7 - i, ip + brk, 2 * (i + 1 - brk)); - } - for (j = 0; j < 8; j++) { - *a++ = ip[j] >> 8; - *a++ = ip[j]; - } - if (need_v4 && inet_pton(AF_INET, (void *)s, a - 4) <= 0) - return 0; - return 1; -} - -const char *inet_ntop(int af, const void *__restrict a0, char *__restrict s, socklen_t l) -{ - const unsigned char *a = a0; - int i, j, max, best; - char buf[100]; - - switch (af) { - case AF_INET: - if (snprintf(s, l, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]) < l) - return s; - break; - case AF_INET6: - if (memcmp(a, "\0\0\0\0\0\0\0\0\0\0\377\377", 12)) - snprintf(buf, sizeof buf, "%x:%x:%x:%x:%x:%x:%x:%x", 256 * a[0] + a[1], - 256 * a[2] + a[3], 256 * a[4] + a[5], 256 * a[6] + a[7], 256 * a[8] + a[9], - 256 * a[10] + a[11], 256 * a[12] + a[13], 256 * a[14] + a[15]); - else - snprintf(buf, sizeof buf, "%x:%x:%x:%x:%x:%x:%d.%d.%d.%d", 256 * a[0] + a[1], - 256 * a[2] + a[3], 256 * a[4] + a[5], 256 * a[6] + a[7], 256 * a[8] + a[9], - 256 * a[10] + a[11], a[12], a[13], a[14], a[15]); - /* Replace longest /(^0|:)[:0]{2,}/ with "::" */ - for (i = best = 0, max = 2; buf[i]; i++) { - if (i && buf[i] != ':') - continue; - j = strspn(buf + i, ":0"); - if (j > max) - best = i, max = j; - } - if (max > 3) { - buf[best] = buf[best + 1] = ':'; - memmove(buf + best + 2, buf + best + max, i - best - max + 1); - } - if (strlen(buf) < l) { - strcpy(s, buf); - return s; - } - break; - default: - errno = EAFNOSUPPORT; - return 0; - } - errno = ENOSPC; - return 0; -} - -// TODO -struct hostent *gethostbyname(const char *name) -{ - unimplemented(); - return 0; -} - -#endif // RUX_CONFIG_NET diff --git a/ulib/ruxlibc/c/nscd_query.c b/ulib/ruxlibc/c/nscd_query.c deleted file mode 100644 index 8e7f85aaf..000000000 --- a/ulib/ruxlibc/c/nscd_query.c +++ /dev/null @@ -1,103 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A - * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. - */ - -#include "nscd.h" -#include -#include -#include -#include -#include -#include -#include -#include - -static const struct { - short sun_family; - char sun_path[21]; -} addr = {AF_UNIX, "/var/run/nscd/socket"}; - -FILE *__nscd_query(int32_t req, const char *key, int32_t *buf, size_t len, int *swap) -{ - size_t i; - int fd; - FILE *f = 0; - int32_t req_buf[REQ_LEN] = {NSCDVERSION, req, strnlen(key, LOGIN_NAME_MAX) + 1}; - struct msghdr msg = { - .msg_iov = (struct iovec[]){{&req_buf, sizeof(req_buf)}, {(char *)key, strlen(key) + 1}}, - .msg_iovlen = 2}; - int errno_save = errno; - - *swap = 0; -retry: - memset(buf, 0, len); - buf[0] = NSCDVERSION; - - fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (fd < 0) { - if (errno == EAFNOSUPPORT) { - f = fopen("/dev/null", "re"); - if (f) - errno = errno_save; - return f; - } - return 0; - } - - if (!(f = fdopen(fd, "r"))) { - close(fd); - return 0; - } - - if (req_buf[2] > LOGIN_NAME_MAX) - return f; - - if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - if (errno == EACCES || errno == ECONNREFUSED || errno == ENOENT) { - errno = errno_save; - return f; - } - goto error; - } - - if (sendmsg(fd, &msg, MSG_NOSIGNAL) < 0) - goto error; - - if (!fread(buf, len, 1, f)) { - if (ferror(f)) - goto error; - if (!*swap) { - fclose(f); - for (i = 0; i < sizeof(req_buf) / sizeof(req_buf[0]); i++) { - req_buf[i] = bswap_32(req_buf[i]); - } - *swap = 1; - goto retry; - } else { - errno = EIO; - goto error; - } - } - - if (*swap) { - for (i = 0; i < len / sizeof(buf[0]); i++) { - buf[i] = bswap_32(buf[i]); - } - } - - if (buf[0] != NSCDVERSION) { - errno = EIO; - goto error; - } - - return f; -error: - fclose(f); - return 0; -} diff --git a/ulib/ruxlibc/c/pow.c b/ulib/ruxlibc/c/pow.c deleted file mode 100644 index 7e5da7055..000000000 --- a/ulib/ruxlibc/c/pow.c +++ /dev/null @@ -1,824 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#if defined(RUX_CONFIG_FP_SIMD) - -#include -#include -#include -#include -#include - -#include "libm.h" - -#define OFF 0x3fe6955500000000 - -#define POW_LOG_TABLE_BITS 7 -#define POW_LOG_POLY_ORDER 8 -#define N (1 << POW_LOG_TABLE_BITS) -struct pow_log_data { - double ln2hi; - double ln2lo; - double poly[POW_LOG_POLY_ORDER - 1]; /* First coefficient is 1. */ - /* Note: the pad field is unused, but allows slightly faster indexing. */ - struct { - double invc, pad, logc, logctail; - } tab[1 << POW_LOG_TABLE_BITS]; -}; - -const struct - pow_log_data - __pow_log_data = - { - .ln2hi = 0x1.62e42fefa3800p-1, - .ln2lo = 0x1.ef35793c76730p-45, - .poly = - { - -0x1p-1, - 0x1.555555555556p-2 * -2, - -0x1.0000000000006p-2 * -2, - 0x1.999999959554ep-3 * 4, - -0x1.555555529a47ap-3 * 4, - 0x1.2495b9b4845e9p-3 * -8, - -0x1.0002b8b263fc3p-3 * -8, - }, - .tab = - { -#define A(a, b, c) {a, 0, b, c}, - A(0x1.6a00000000000p+0, -0x1.62c82f2b9c800p-2, 0x1.ab42428375680p-48) - A(0x1.6800000000000p+0, -0x1.5d1bdbf580800p-2, -0x1.ca508d8e0f720p-46) - A(0x1.6600000000000p+0, -0x1.5767717455800p-2, - -0x1.362a4d5b6506dp-45) - A(0x1.6400000000000p+0, -0x1.51aad872df800p-2, - -0x1.684e49eb067d5p-49) A(0x1.6200000000000p+0, - -0x1.4be5f95777800p-2, - -0x1.41b6993293ee0p-47) A(0x1.6000000000000p+0, -0x1.4618bc21c6000p-2, 0x1.3d82f484c84ccp-46) A(0x1.5e00000000000p+0, -0x1.404308686a800p-2, 0x1.c42f3ed820b3ap-50) A(0x1.5c00000000000p+0, -0x1.3a64c55694800p-2, 0x1.0b1c686519460p-45) A(0x1.5a00000000000p+0, -0x1.347dd9a988000p-2, 0x1.5594dd4c58092p-45) A(0x1.5800000000000p+0, -0x1.2e8e2bae12000p-2, 0x1.67b1e99b72bd8p-45) A(0x1.5600000000000p+0, - -0x1.2895a13de8800p-2, 0x1.5ca14b6cfb03fp-46) A(0x1.5600000000000p+0, - -0x1.2895a13de8800p-2, 0x1.5ca14b6cfb03fp-46) A(0x1.5400000000000p+0, - -0x1.22941fbcf7800p-2, - -0x1.65a242853da76p-46) A(0x1.5200000000000p+0, - -0x1.1c898c1699800p-2, - -0x1.fafbc68e75404p-46) A(0x1.5000000000000p+0, - -0x1.1675cababa800p-2, 0x1.f1fc63382a8f0p-46) A(0x1.4e00000000000p+0, - -0x1.1058bf9ae4800p-2, - -0x1.6a8c4fd055a66p-45) A(0x1.4c00000000000p+0, - -0x1.0a324e2739000p-2, -0x1.c6bee7ef4030ep-47) A(0x1.4a00000000000p+0, - -0x1.0402594b4d000p-2, -0x1.036b89ef42d7fp-48) A(0x1.4a00000000000p+0, - -0x1.0402594b4d000p-2, -0x1.036b89ef42d7fp-48) A(0x1.4800000000000p+0, - -0x1.fb9186d5e4000p-3, - 0x1.d572aab993c87p-47) A(0x1.4600000000000p+0, - -0x1.ef0adcbdc6000p-3, - 0x1.b26b79c86af24p-45) A(0x1.4400000000000p+0, - -0x1.e27076e2af000p-3, -0x1.72f4f543fff10p-46) A(0x1.4200000000000p+0, - -0x1.d5c216b4fc000p-3, 0x1.1ba91bbca681bp-45) A(0x1.4000000000000p+0, -0x1.c8ff7c79aa000p-3, 0x1.7794f689f8434p-45) A(0x1.4000000000000p+0, -0x1.c8ff7c79aa000p-3, 0x1.7794f689f8434p-45) A(0x1.3e00000000000p+0, - -0x1.bc286742d9000p-3, 0x1.94eb0318bb78fp-46) A(0x1.3c00000000000p+0, - -0x1.af3c94e80c000p-3, 0x1.a4e633fcd9066p-52) A(0x1.3a00000000000p+0, - -0x1.a23bc1fe2b000p-3, - -0x1.58c64dc46c1eap-45) A(0x1.3a00000000000p+0, - -0x1.a23bc1fe2b000p-3, -0x1.58c64dc46c1eap-45) A(0x1.3800000000000p+0, - -0x1.9525a9cf45000p-3, -0x1.ad1d904c1d4e3p-45) A(0x1.3600000000000p+0, - -0x1.87fa06520d000p-3, 0x1.bbdbf7fdbfa09p-45) A(0x1.3400000000000p+0, - -0x1.7ab890210e000p-3, 0x1.bdb9072534a58p-45) A(0x1.3400000000000p+0, - -0x1.7ab890210e000p-3, 0x1.bdb9072534a58p-45) A(0x1.3200000000000p+0, -0x1.6d60fe719d000p-3, -0x1.0e46aa3b2e266p-46) A(0x1.3000000000000p+0, - -0x1.5ff3070a79000p-3, -0x1.e9e439f105039p-46) A(0x1.3000000000000p+0, - -0x1.5ff3070a79000p-3, -0x1.e9e439f105039p-46) A(0x1.2e00000000000p+0, - -0x1.526e5e3a1b000p-3, -0x1.0de8b90075b8fp-45) A(0x1.2c00000000000p+0, - -0x1.44d2b6ccb8000p-3, 0x1.70cc16135783cp-46) A(0x1.2c00000000000p+0, -0x1.44d2b6ccb8000p-3, 0x1.70cc16135783cp-46) A(0x1.2a00000000000p+0, - -0x1.371fc201e9000p-3, 0x1.178864d27543ap-48) A(0x1.2800000000000p+0, - -0x1.29552f81ff000p-3, -0x1.48d301771c408p-45) A(0x1.2600000000000p+0, -0x1.1b72ad52f6000p-3, -0x1.e80a41811a396p-45) A(0x1.2600000000000p+0, -0x1.1b72ad52f6000p-3, -0x1.e80a41811a396p-45) A(0x1.2400000000000p+0, - -0x1.0d77e7cd09000p-3, - 0x1.a699688e85bf4p-47) A(0x1.2400000000000p+0, -0x1.0d77e7cd09000p-3, 0x1.a699688e85bf4p-47) A(0x1.2200000000000p+0, -0x1.fec9131dbe000p-4, -0x1.575545ca333f2p-45) A(0x1.2000000000000p+0, - -0x1.e27076e2b0000p-4, 0x1.a342c2af0003cp-45) A(0x1.2000000000000p+0, -0x1.e27076e2b0000p-4, 0x1.a342c2af0003cp-45) A(0x1.1e00000000000p+0, -0x1.c5e548f5bc000p-4, -0x1.d0c57585fbe06p-46) A(0x1.1c00000000000p+0, - -0x1.a926d3a4ae000p-4, 0x1.53935e85baac8p-45) A(0x1.1c00000000000p+0, - -0x1.a926d3a4ae000p-4, 0x1.53935e85baac8p-45) A(0x1.1a00000000000p+0, -0x1.8c345d631a000p-4, 0x1.37c294d2f5668p-46) A(0x1.1a00000000000p+0, - -0x1.8c345d631a000p-4, - 0x1.37c294d2f5668p-46) A(0x1.1800000000000p+0, -0x1.6f0d28ae56000p-4, - -0x1.69737c93373dap-45) A(0x1.1600000000000p+0, - -0x1.51b073f062000p-4, 0x1.f025b61c65e57p-46) A(0x1.1600000000000p+0, - -0x1.51b073f062000p-4, 0x1.f025b61c65e57p-46) A(0x1.1400000000000p+0, -0x1.341d7961be000p-4, 0x1.c5edaccf913dfp-45) A(0x1.1400000000000p+0, - -0x1.341d7961be000p-4, 0x1.c5edaccf913dfp-45) A(0x1.1200000000000p+0, -0x1.16536eea38000p-4, 0x1.47c5e768fa309p-46) A(0x1.1000000000000p+0, - -0x1.f0a30c0118000p-5, 0x1.d599e83368e91p-45) A(0x1.1000000000000p+0, -0x1.f0a30c0118000p-5, - 0x1.d599e83368e91p-45) A(0x1.0e00000000000p+0, - -0x1.b42dd71198000p-5, 0x1.c827ae5d6704cp-46) A(0x1.0e00000000000p+0, - -0x1.b42dd71198000p-5, 0x1.c827ae5d6704cp-46) A(0x1.0c00000000000p+0, - -0x1.77458f632c000p-5, -0x1.cfc4634f2a1eep-45) A(0x1.0c00000000000p+0, -0x1.77458f632c000p-5, -0x1.cfc4634f2a1eep-45) A(0x1.0a00000000000p+0, -0x1.39e87b9fec000p-5, 0x1.502b7f526feaap-48) A(0x1.0a00000000000p+0, - -0x1.39e87b9fec000p-5, 0x1.502b7f526feaap-48) A(0x1.0800000000000p+0, -0x1.f829b0e780000p-6, -0x1.980267c7e09e4p-45) A(0x1.0800000000000p+0, -0x1.f829b0e780000p-6, -0x1.980267c7e09e4p-45) A(0x1.0600000000000p+0, - -0x1.7b91b07d58000p-6, -0x1.88d5493faa639p-45) A(0x1.0400000000000p+0, - -0x1.fc0a8b0fc0000p-7, -0x1.f1e7cf6d3a69cp-50) A(0x1.0400000000000p+0, -0x1.fc0a8b0fc0000p-7, -0x1.f1e7cf6d3a69cp-50) A(0x1.0200000000000p+0, -0x1.fe02a6b100000p-8, -0x1.9e23f0dda40e4p-46) A(0x1.0200000000000p+0, - -0x1.fe02a6b100000p-8, - -0x1.9e23f0dda40e4p-46) A(0x1.0000000000000p+0, - 0x0.0000000000000p+0, 0x0.0000000000000p+0) A(0x1.0000000000000p+0, - 0x0.0000000000000p+0, - 0x0.0000000000000p+0) A(0x1.fc00000000000p-1, - 0x1.0101575890000p-7, -0x1.0c76b999d2be8p-46) A(0x1.f800000000000p-1, - 0x1.0205658938000p-6, -0x1.3dc5b06e2f7d2p-45) A(0x1.f400000000000p-1, - 0x1.8492528c90000p-6, - -0x1.aa0ba325a0c34p-45) A(0x1.f000000000000p-1, - 0x1.0415d89e74000p-5, - 0x1.111c05cf1d753p-47) A(0x1.ec00000000000p-1, 0x1.466aed42e0000p-5, -0x1.c167375bdfd28p-45) A(0x1.e800000000000p-1, - 0x1.894aa149fc000p-5, - -0x1.97995d05a267dp-46) A(0x1.e400000000000p-1, - 0x1.ccb73cdddc000p-5, -0x1.a68f247d82807p-46) A(0x1.e200000000000p-1, - 0x1.eea31c006c000p-5, - -0x1.e113e4fc93b7bp-47) A(0x1.de00000000000p-1, - 0x1.1973bd1466000p-4, - -0x1.5325d560d9e9bp-45) A(0x1.da00000000000p-1, 0x1.3bdf5a7d1e000p-4, 0x1.cc85ea5db4ed7p-45) A(0x1.d600000000000p-1, 0x1.5e95a4d97a000p-4, -0x1.c69063c5d1d1ep-45) A(0x1.d400000000000p-1, 0x1.700d30aeac000p-4, 0x1.c1e8da99ded32p-49) A(0x1.d000000000000p-1, - 0x1.9335e5d594000p-4, - 0x1.3115c3abd47dap-45) A(0x1.cc00000000000p-1, - 0x1.b6ac88dad6000p-4, - -0x1.390802bf768e5p-46) A(0x1.ca00000000000p-1, - 0x1.c885801bc4000p-4, - 0x1.646d1c65aacd3p-45) A(0x1.c600000000000p-1, - 0x1.ec739830a2000p-4, - -0x1.dc068afe645e0p-45) A(0x1.c400000000000p-1, - 0x1.fe89139dbe000p-4, - -0x1.534d64fa10afdp-45) A(0x1.c000000000000p-1, 0x1.1178e8227e000p-3, 0x1.1ef78ce2d07f2p-45) A(0x1.be00000000000p-1, 0x1.1aa2b7e23f000p-3, 0x1.ca78e44389934p-45) A(0x1.ba00000000000p-1, 0x1.2d1610c868000p-3, 0x1.39d6ccb81b4a1p-47) A(0x1.b800000000000p-1, 0x1.365fcb0159000p-3, 0x1.62fa8234b7289p-51) A(0x1.b400000000000p-1, 0x1.4913d8333b000p-3, 0x1.5837954fdb678p-45) A(0x1.b200000000000p-1, 0x1.527e5e4a1b000p-3, - 0x1.633e8e5697dc7p-45) A(0x1.ae00000000000p-1, - 0x1.6574ebe8c1000p-3, - 0x1.9cf8b2c3c2e78p-46) A(0x1.ac00000000000p-1, - 0x1.6f0128b757000p-3, -0x1.5118de59c21e1p-45) A(0x1.aa00000000000p-1, 0x1.7898d85445000p-3, -0x1.c661070914305p-46) A(0x1.a600000000000p-1, - 0x1.8beafeb390000p-3, -0x1.73d54aae92cd1p-47) A(0x1.a400000000000p-1, 0x1.95a5adcf70000p-3, 0x1.7f22858a0ff6fp-47) A(0x1.a000000000000p-1, 0x1.a93ed3c8ae000p-3, -0x1.8724350562169p-45) A(0x1.9e00000000000p-1, 0x1.b31d8575bd000p-3, -0x1.c358d4eace1aap-47) A(0x1.9c00000000000p-1, 0x1.bd087383be000p-3, -0x1.d4bc4595412b6p-45) A(0x1.9a00000000000p-1, 0x1.c6ffbc6f01000p-3, -0x1.1ec72c5962bd2p-48) A(0x1.9600000000000p-1, 0x1.db13db0d49000p-3, -0x1.aff2af715b035p-45) A(0x1.9400000000000p-1, - 0x1.e530effe71000p-3, - 0x1.212276041f430p-51) A(0x1.9200000000000p-1, 0x1.ef5ade4dd0000p-3, -0x1.a211565bb8e11p-51) A(0x1.9000000000000p-1, 0x1.f991c6cb3b000p-3, 0x1.bcbecca0cdf30p-46) A(0x1.8c00000000000p-1, 0x1.07138604d5800p-2, 0x1.89cdb16ed4e91p-48) A(0x1.8a00000000000p-1, - 0x1.0c42d67616000p-2, - 0x1.7188b163ceae9p-45) A(0x1.8800000000000p-1, 0x1.1178e8227e800p-2, -0x1.c210e63a5f01cp-45) A(0x1.8600000000000p-1, - 0x1.16b5ccbacf800p-2, - 0x1.b9acdf7a51681p-45) A(0x1.8400000000000p-1, - 0x1.1bf99635a6800p-2, - 0x1.ca6ed5147bdb7p-45) A(0x1.8200000000000p-1, - 0x1.214456d0eb800p-2, - 0x1.a87deba46baeap-47) A(0x1.7e00000000000p-1, - 0x1.2bef07cdc9000p-2, 0x1.a9cfa4a5004f4p-45) A(0x1.7c00000000000p-1, 0x1.314f1e1d36000p-2, -0x1.8e27ad3213cb8p-45) A(0x1.7a00000000000p-1, 0x1.36b6776be1000p-2, 0x1.16ecdb0f177c8p-46) A(0x1.7800000000000p-1, - 0x1.3c25277333000p-2, - 0x1.83b54b606bd5cp-46) A(0x1.7600000000000p-1, - 0x1.419b423d5e800p-2, - 0x1.8e436ec90e09dp-47) A(0x1.7400000000000p-1, 0x1.4718dc271c800p-2, -0x1.f27ce0967d675p-45) A(0x1.7200000000000p-1, 0x1.4c9e09e173000p-2, -0x1.e20891b0ad8a4p-45) A(0x1.7000000000000p-1, - 0x1.522ae0738a000p-2, - 0x1.ebe708164c759p-45) A(0x1.6e00000000000p-1, 0x1.57bf753c8d000p-2, 0x1.fadedee5d40efp-46) A(0x1.6c00000000000p-1, - 0x1.5d5bddf596000p-2, - -0x1.a0b2a08a465dcp-47)}, -}; - -#define T __pow_log_data.tab -#undef A -#define A __pow_log_data.poly -#define Ln2hi __pow_log_data.ln2hi -#define Ln2lo __pow_log_data.ln2lo - -/* Top 12 bits of a double (sign and exponent bits). */ -static inline uint32_t top12(double x) -{ - return asuint64(x) >> 52; -} - -/* Compute y+TAIL = log(x) where the rounded result is y and TAIL has about - additional 15 bits precision. IX is the bit representation of x, but - normalized in the subnormal range using the sign bit for the exponent. */ -static inline double_t log_inline(uint64_t ix, double_t *tail) -{ - /* double_t for better performance on targets with FLT_EVAL_METHOD==2. */ - double_t z, r, y, invc, logc, logctail, kd, hi, t1, t2, lo, lo1, lo2, p; - uint64_t iz, tmp; - int k, i; - - /* x = 2^k z; where z is in range [OFF,2*OFF) and exact. - The range is split into N subintervals. - The ith subinterval contains z and c is near its center. */ - tmp = ix - OFF; - i = (tmp >> (52 - POW_LOG_TABLE_BITS)) % N; - k = (int64_t)tmp >> 52; /* arithmetic shift */ - iz = ix - (tmp & 0xfffULL << 52); - z = asdouble(iz); - kd = (double_t)k; - - /* log(x) = k*Ln2 + log(c) + log1p(z/c-1). */ - invc = T[i].invc; - logc = T[i].logc; - logctail = T[i].logctail; - - /* Note: 1/c is j/N or j/N/2 where j is an integer in [N,2N) and - |z/c - 1| < 1/N, so r = z/c - 1 is exactly representible. */ -#if __FP_FAST_FMA - r = __builtin_fma(z, invc, -1.0); -#else - /* Split z such that rhi, rlo and rhi*rhi are exact and |rlo| <= |r|. */ - double_t zhi = asdouble((iz + (1ULL << 31)) & (-1ULL << 32)); - double_t zlo = z - zhi; - double_t rhi = zhi * invc - 1.0; - double_t rlo = zlo * invc; - r = rhi + rlo; -#endif - - /* k*Ln2 + log(c) + r. */ - t1 = kd * Ln2hi + logc; - t2 = t1 + r; - lo1 = kd * Ln2lo + logctail; - lo2 = t1 - t2 + r; - - /* Evaluation is optimized assuming superscalar pipelined execution. */ - double_t ar, ar2, ar3, lo3, lo4; - ar = A[0] * r; /* A[0] = -0.5. */ - ar2 = r * ar; - ar3 = r * ar2; - /* k*Ln2 + log(c) + r + A[0]*r*r. */ -#if __FP_FAST_FMA - hi = t2 + ar2; - lo3 = __builtin_fma(ar, r, -ar2); - lo4 = t2 - hi + ar2; -#else - double_t arhi = A[0] * rhi; - double_t arhi2 = rhi * arhi; - hi = t2 + arhi2; - lo3 = rlo * (ar + arhi); - lo4 = t2 - hi + arhi2; -#endif - /* p = log1p(r) - r - A[0]*r*r. */ - p = (ar3 * (A[1] + r * A[2] + ar2 * (A[3] + r * A[4] + ar2 * (A[5] + r * A[6])))); - lo = lo1 + lo2 + lo3 + lo4 + p; - y = hi + lo; - *tail = hi - y + lo; - return y; -} - -#undef N -#undef T -#define EXP_TABLE_BITS 7 -#define EXP_POLY_ORDER 5 -#define EXP_USE_TOINT_NARROW 0 -#define EXP2_POLY_ORDER 5 -struct exp_data { - double invln2N; - double shift; - double negln2hiN; - double negln2loN; - double poly[4]; /* Last four coefficients. */ - double exp2_shift; - double exp2_poly[EXP2_POLY_ORDER]; - uint64_t tab[2 * (1 << EXP_TABLE_BITS)]; -}; -#define N (1 << EXP_TABLE_BITS) - -const struct exp_data __exp_data = { - // N/ln2 - .invln2N = 0x1.71547652b82fep0 * N, - // -ln2/N - .negln2hiN = -0x1.62e42fefa0000p-8, - .negln2loN = -0x1.cf79abc9e3b3ap-47, -// Used for rounding when !TOINT_INTRINSICS -#if EXP_USE_TOINT_NARROW - .shift = 0x1800000000.8p0, -#else - .shift = 0x1.8p52, -#endif - // exp polynomial coefficients. - .poly = - { - // abs error: 1.555*2^-66 - // ulp error: 0.509 (0.511 without fma) - // if |x| < ln2/256+eps - // abs error if |x| < ln2/256+0x1p-15: 1.09*2^-65 - // abs error if |x| < ln2/128: 1.7145*2^-56 - 0x1.ffffffffffdbdp-2, - 0x1.555555555543cp-3, - 0x1.55555cf172b91p-5, - 0x1.1111167a4d017p-7, - }, - .exp2_shift = 0x1.8p52 / N, - // exp2 polynomial coefficients. - .exp2_poly = - { - // abs error: 1.2195*2^-65 - // ulp error: 0.507 (0.511 without fma) - // if |x| < 1/256 - // abs error if |x| < 1/128: 1.9941*2^-56 - 0x1.62e42fefa39efp-1, - 0x1.ebfbdff82c424p-3, - 0x1.c6b08d70cf4b5p-5, - 0x1.3b2abd24650ccp-7, - 0x1.5d7e09b4e3a84p-10, - }, - // 2^(k/N) ~= H[k]*(1 + T[k]) for int k in [0,N) - // tab[2*k] = asuint64(T[k]) - // tab[2*k+1] = asuint64(H[k]) - (k << 52)/N - .tab = - { - 0x0, - 0x3ff0000000000000, - 0x3c9b3b4f1a88bf6e, - 0x3feff63da9fb3335, - 0xbc7160139cd8dc5d, - 0x3fefec9a3e778061, - 0xbc905e7a108766d1, - 0x3fefe315e86e7f85, - 0x3c8cd2523567f613, - 0x3fefd9b0d3158574, - 0xbc8bce8023f98efa, - 0x3fefd06b29ddf6de, - 0x3c60f74e61e6c861, - 0x3fefc74518759bc8, - 0x3c90a3e45b33d399, - 0x3fefbe3ecac6f383, - 0x3c979aa65d837b6d, - 0x3fefb5586cf9890f, - 0x3c8eb51a92fdeffc, - 0x3fefac922b7247f7, - 0x3c3ebe3d702f9cd1, - 0x3fefa3ec32d3d1a2, - 0xbc6a033489906e0b, - 0x3fef9b66affed31b, - 0xbc9556522a2fbd0e, - 0x3fef9301d0125b51, - 0xbc5080ef8c4eea55, - 0x3fef8abdc06c31cc, - 0xbc91c923b9d5f416, - 0x3fef829aaea92de0, - 0x3c80d3e3e95c55af, - 0x3fef7a98c8a58e51, - 0xbc801b15eaa59348, - 0x3fef72b83c7d517b, - 0xbc8f1ff055de323d, - 0x3fef6af9388c8dea, - 0x3c8b898c3f1353bf, - 0x3fef635beb6fcb75, - 0xbc96d99c7611eb26, - 0x3fef5be084045cd4, - 0x3c9aecf73e3a2f60, - 0x3fef54873168b9aa, - 0xbc8fe782cb86389d, - 0x3fef4d5022fcd91d, - 0x3c8a6f4144a6c38d, - 0x3fef463b88628cd6, - 0x3c807a05b0e4047d, - 0x3fef3f49917ddc96, - 0x3c968efde3a8a894, - 0x3fef387a6e756238, - 0x3c875e18f274487d, - 0x3fef31ce4fb2a63f, - 0x3c80472b981fe7f2, - 0x3fef2b4565e27cdd, - 0xbc96b87b3f71085e, - 0x3fef24dfe1f56381, - 0x3c82f7e16d09ab31, - 0x3fef1e9df51fdee1, - 0xbc3d219b1a6fbffa, - 0x3fef187fd0dad990, - 0x3c8b3782720c0ab4, - 0x3fef1285a6e4030b, - 0x3c6e149289cecb8f, - 0x3fef0cafa93e2f56, - 0x3c834d754db0abb6, - 0x3fef06fe0a31b715, - 0x3c864201e2ac744c, - 0x3fef0170fc4cd831, - 0x3c8fdd395dd3f84a, - 0x3feefc08b26416ff, - 0xbc86a3803b8e5b04, - 0x3feef6c55f929ff1, - 0xbc924aedcc4b5068, - 0x3feef1a7373aa9cb, - 0xbc9907f81b512d8e, - 0x3feeecae6d05d866, - 0xbc71d1e83e9436d2, - 0x3feee7db34e59ff7, - 0xbc991919b3ce1b15, - 0x3feee32dc313a8e5, - 0x3c859f48a72a4c6d, - 0x3feedea64c123422, - 0xbc9312607a28698a, - 0x3feeda4504ac801c, - 0xbc58a78f4817895b, - 0x3feed60a21f72e2a, - 0xbc7c2c9b67499a1b, - 0x3feed1f5d950a897, - 0x3c4363ed60c2ac11, - 0x3feece086061892d, - 0x3c9666093b0664ef, - 0x3feeca41ed1d0057, - 0x3c6ecce1daa10379, - 0x3feec6a2b5c13cd0, - 0x3c93ff8e3f0f1230, - 0x3feec32af0d7d3de, - 0x3c7690cebb7aafb0, - 0x3feebfdad5362a27, - 0x3c931dbdeb54e077, - 0x3feebcb299fddd0d, - 0xbc8f94340071a38e, - 0x3feeb9b2769d2ca7, - 0xbc87deccdc93a349, - 0x3feeb6daa2cf6642, - 0xbc78dec6bd0f385f, - 0x3feeb42b569d4f82, - 0xbc861246ec7b5cf6, - 0x3feeb1a4ca5d920f, - 0x3c93350518fdd78e, - 0x3feeaf4736b527da, - 0x3c7b98b72f8a9b05, - 0x3feead12d497c7fd, - 0x3c9063e1e21c5409, - 0x3feeab07dd485429, - 0x3c34c7855019c6ea, - 0x3feea9268a5946b7, - 0x3c9432e62b64c035, - 0x3feea76f15ad2148, - 0xbc8ce44a6199769f, - 0x3feea5e1b976dc09, - 0xbc8c33c53bef4da8, - 0x3feea47eb03a5585, - 0xbc845378892be9ae, - 0x3feea34634ccc320, - 0xbc93cedd78565858, - 0x3feea23882552225, - 0x3c5710aa807e1964, - 0x3feea155d44ca973, - 0xbc93b3efbf5e2228, - 0x3feea09e667f3bcd, - 0xbc6a12ad8734b982, - 0x3feea012750bdabf, - 0xbc6367efb86da9ee, - 0x3fee9fb23c651a2f, - 0xbc80dc3d54e08851, - 0x3fee9f7df9519484, - 0xbc781f647e5a3ecf, - 0x3fee9f75e8ec5f74, - 0xbc86ee4ac08b7db0, - 0x3fee9f9a48a58174, - 0xbc8619321e55e68a, - 0x3fee9feb564267c9, - 0x3c909ccb5e09d4d3, - 0x3feea0694fde5d3f, - 0xbc7b32dcb94da51d, - 0x3feea11473eb0187, - 0x3c94ecfd5467c06b, - 0x3feea1ed0130c132, - 0x3c65ebe1abd66c55, - 0x3feea2f336cf4e62, - 0xbc88a1c52fb3cf42, - 0x3feea427543e1a12, - 0xbc9369b6f13b3734, - 0x3feea589994cce13, - 0xbc805e843a19ff1e, - 0x3feea71a4623c7ad, - 0xbc94d450d872576e, - 0x3feea8d99b4492ed, - 0x3c90ad675b0e8a00, - 0x3feeaac7d98a6699, - 0x3c8db72fc1f0eab4, - 0x3feeace5422aa0db, - 0xbc65b6609cc5e7ff, - 0x3feeaf3216b5448c, - 0x3c7bf68359f35f44, - 0x3feeb1ae99157736, - 0xbc93091fa71e3d83, - 0x3feeb45b0b91ffc6, - 0xbc5da9b88b6c1e29, - 0x3feeb737b0cdc5e5, - 0xbc6c23f97c90b959, - 0x3feeba44cbc8520f, - 0xbc92434322f4f9aa, - 0x3feebd829fde4e50, - 0xbc85ca6cd7668e4b, - 0x3feec0f170ca07ba, - 0x3c71affc2b91ce27, - 0x3feec49182a3f090, - 0x3c6dd235e10a73bb, - 0x3feec86319e32323, - 0xbc87c50422622263, - 0x3feecc667b5de565, - 0x3c8b1c86e3e231d5, - 0x3feed09bec4a2d33, - 0xbc91bbd1d3bcbb15, - 0x3feed503b23e255d, - 0x3c90cc319cee31d2, - 0x3feed99e1330b358, - 0x3c8469846e735ab3, - 0x3feede6b5579fdbf, - 0xbc82dfcd978e9db4, - 0x3feee36bbfd3f37a, - 0x3c8c1a7792cb3387, - 0x3feee89f995ad3ad, - 0xbc907b8f4ad1d9fa, - 0x3feeee07298db666, - 0xbc55c3d956dcaeba, - 0x3feef3a2b84f15fb, - 0xbc90a40e3da6f640, - 0x3feef9728de5593a, - 0xbc68d6f438ad9334, - 0x3feeff76f2fb5e47, - 0xbc91eee26b588a35, - 0x3fef05b030a1064a, - 0x3c74ffd70a5fddcd, - 0x3fef0c1e904bc1d2, - 0xbc91bdfbfa9298ac, - 0x3fef12c25bd71e09, - 0x3c736eae30af0cb3, - 0x3fef199bdd85529c, - 0x3c8ee3325c9ffd94, - 0x3fef20ab5fffd07a, - 0x3c84e08fd10959ac, - 0x3fef27f12e57d14b, - 0x3c63cdaf384e1a67, - 0x3fef2f6d9406e7b5, - 0x3c676b2c6c921968, - 0x3fef3720dcef9069, - 0xbc808a1883ccb5d2, - 0x3fef3f0b555dc3fa, - 0xbc8fad5d3ffffa6f, - 0x3fef472d4a07897c, - 0xbc900dae3875a949, - 0x3fef4f87080d89f2, - 0x3c74a385a63d07a7, - 0x3fef5818dcfba487, - 0xbc82919e2040220f, - 0x3fef60e316c98398, - 0x3c8e5a50d5c192ac, - 0x3fef69e603db3285, - 0x3c843a59ac016b4b, - 0x3fef7321f301b460, - 0xbc82d52107b43e1f, - 0x3fef7c97337b9b5f, - 0xbc892ab93b470dc9, - 0x3fef864614f5a129, - 0x3c74b604603a88d3, - 0x3fef902ee78b3ff6, - 0x3c83c5ec519d7271, - 0x3fef9a51fbc74c83, - 0xbc8ff7128fd391f0, - 0x3fefa4afa2a490da, - 0xbc8dae98e223747d, - 0x3fefaf482d8e67f1, - 0x3c8ec3bc41aa2008, - 0x3fefba1bee615a27, - 0x3c842b94c3a9eb32, - 0x3fefc52b376bba97, - 0x3c8a64a931d185ee, - 0x3fefd0765b6e4540, - 0xbc8e37bae43be3ed, - 0x3fefdbfdad9cbe14, - 0x3c77893b4d91cd9d, - 0x3fefe7c1819e90d8, - 0x3c5305c14160cc89, - 0x3feff3c22b8f71f1, - }, -}; - -#define InvLn2N __exp_data.invln2N -#define NegLn2hiN __exp_data.negln2hiN -#define NegLn2loN __exp_data.negln2loN -#define Shift __exp_data.shift -#define T __exp_data.tab -#define C2 __exp_data.poly[5 - EXP_POLY_ORDER] -#define C3 __exp_data.poly[6 - EXP_POLY_ORDER] -#define C4 __exp_data.poly[7 - EXP_POLY_ORDER] -#define C5 __exp_data.poly[8 - EXP_POLY_ORDER] -#define C6 __exp_data.poly[9 - EXP_POLY_ORDER] - -static inline double specialcase(double_t tmp, uint64_t sbits, uint64_t ki) -{ - double_t scale, y; - - if ((ki & 0x80000000) == 0) { - /* k > 0, the exponent of scale might have overflowed by <= 460. */ - sbits -= 1009ull << 52; - scale = asdouble(sbits); - y = 0x1p1009 * (scale + scale * tmp); - return eval_as_double(y); - } - /* k < 0, need special care in the subnormal range. */ - sbits += 1022ull << 52; - /* Note: sbits is signed scale. */ - scale = asdouble(sbits); - y = scale + scale * tmp; - if (fabs(y) < 1.0) { - /* Round y to the right precision before scaling it into the subnormal - range to avoid double rounding that can cause 0.5+E/2 ulp error where - E is the worst-case ulp error outside the subnormal range. So this - is only useful if the goal is better than 1 ulp worst-case error. */ - double_t hi, lo, one = 1.0; - if (y < 0.0) - one = -1.0; - lo = scale - y + scale * tmp; - hi = one + y; - lo = one - hi + y + lo; - y = eval_as_double(hi + lo) - one; - /* Fix the sign of 0. */ - if (y == 0.0) - y = asdouble(sbits & 0x8000000000000000); - /* The underflow exception needs to be signaled explicitly. */ - fp_force_eval(fp_barrier(0x1p-1022) * 0x1p-1022); - } - y = 0x1p-1022 * y; - return eval_as_double(y); -} - -#define SIGN_BIAS (0x800 << EXP_TABLE_BITS) - -double __math_xflow(uint32_t sign, double y) -{ - return eval_as_double(fp_barrier(sign ? -y : y) * y); -} - -double __math_uflow(uint32_t sign) -{ - return __math_xflow(sign, 0x1p-767); -} - -double __math_oflow(uint32_t sign) -{ - return __math_xflow(sign, 0x1p769); -} - -/* Computes sign*exp(x+xtail) where |xtail| < 2^-8/N and |xtail| <= |x|. - The sign_bias argument is SIGN_BIAS or 0 and sets the sign to -1 or 1. */ -static inline double exp_inline(double_t x, double_t xtail, uint32_t sign_bias) -{ - uint32_t abstop; - uint64_t ki, idx, top, sbits; - /* double_t for better performance on targets with FLT_EVAL_METHOD==2. */ - double_t kd, z, r, r2, scale, tail, tmp; - - abstop = top12(x) & 0x7ff; - if (predict_false(abstop - top12(0x1p-54) >= top12(512.0) - top12(0x1p-54))) { - if (abstop - top12(0x1p-54) >= 0x80000000) { - /* Avoid spurious underflow for tiny x. */ - /* Note: 0 is common input. */ - double_t one = WANT_ROUNDING ? 1.0 + x : 1.0; - return sign_bias ? -one : one; - } - if (abstop >= top12(1024.0)) { - /* Note: inf and nan are already handled. */ - if (asuint64(x) >> 63) - return __math_uflow(sign_bias); - else - return __math_oflow(sign_bias); - } - /* Large x is special cased below. */ - abstop = 0; - } - - /* exp(x) = 2^(k/N) * exp(r), with exp(r) in [2^(-1/2N),2^(1/2N)]. */ - /* x = ln2/N*k + r, with int k and r in [-ln2/2N, ln2/2N]. */ - z = InvLn2N * x; -#if TOINT_INTRINSICS - kd = roundtoint(z); - ki = converttoint(z); -#elif EXP_USE_TOINT_NARROW - /* z - kd is in [-0.5-2^-16, 0.5] in all rounding modes. */ - kd = eval_as_double(z + Shift); - ki = asuint64(kd) >> 16; - kd = (double_t)(int32_t)ki; -#else - /* z - kd is in [-1, 1] in non-nearest rounding modes. */ - kd = eval_as_double(z + Shift); - ki = asuint64(kd); - kd -= Shift; -#endif - r = x + kd * NegLn2hiN + kd * NegLn2loN; - /* The code assumes 2^-200 < |xtail| < 2^-8/N. */ - r += xtail; - /* 2^(k/N) ~= scale * (1 + tail). */ - idx = 2 * (ki % N); - top = (ki + sign_bias) << (52 - EXP_TABLE_BITS); - tail = asdouble(T[idx]); - /* This is only a valid scale when -1023*N < k < 1024*N. */ - sbits = T[idx + 1] + top; - /* exp(x) = 2^(k/N) * exp(r) ~= scale + scale * (tail + exp(r) - 1). */ - /* Evaluation is optimized assuming superscalar pipelined execution. */ - r2 = r * r; - /* Without fma the worst case error is 0.25/N ulp larger. */ - /* Worst case error is less than 0.5+1.11/N+(abs poly error * 2^53) ulp. */ - tmp = tail + r + r2 * (C2 + r * C3) + r2 * r2 * (C4 + r * C5); - if (predict_false(abstop == 0)) - return specialcase(tmp, sbits, ki); - scale = asdouble(sbits); - /* Note: tmp == 0 or |tmp| > 2^-200 and scale > 2^-739, so there - is no spurious underflow here even without fma. */ - return eval_as_double(scale + scale * tmp); -} - -/* Returns 0 if not int, 1 if odd int, 2 if even int. The argument is - the bit representation of a non-zero finite floating-point value. */ -static inline int checkint(uint64_t iy) -{ - int e = iy >> 52 & 0x7ff; - if (e < 0x3ff) - return 0; - if (e > 0x3ff + 52) - return 2; - if (iy & ((1ULL << (0x3ff + 52 - e)) - 1)) - return 0; - if (iy & (1ULL << (0x3ff + 52 - e))) - return 1; - return 2; -} - -#if 100 * __GNUC__ + __GNUC_MINOR__ >= 303 -#define NAN __builtin_nanf("") -#define INFINITY __builtin_inff() -#else -#define NAN (0.0f / 0.0f) -#define INFINITY 1e5000f -#endif - -static inline int zeroinfnan(uint64_t i) -{ - return 2 * i - 1 >= 2 * asuint64(INFINITY) - 1; -} - -#if WANT_SNAN -#error SNaN is unsupported -#else -#define issignalingf_inline(x) 0 -#define issignaling_inline(x) 0 -#endif - -double pow(double x, double y) -{ - uint32_t sign_bias = 0; - uint64_t ix, iy; - uint32_t topx, topy; - - ix = asuint64(x); - iy = asuint64(y); - topx = top12(x); - topy = top12(y); - if (predict_false(topx - 0x001 >= 0x7ff - 0x001 || (topy & 0x7ff) - 0x3be >= 0x43e - 0x3be)) { - /* Note: if |y| > 1075 * ln2 * 2^53 ~= 0x1.749p62 then pow(x,y) = inf/0 - and if |y| < 2^-54 / 1075 ~= 0x1.e7b6p-65 then pow(x,y) = +-1. */ - /* Special cases: (x < 0x1p-126 or inf or nan) or - (|y| < 0x1p-65 or |y| >= 0x1p63 or nan). */ - if (predict_false(zeroinfnan(iy))) { - if (2 * iy == 0) - return issignaling_inline(x) ? x + y : 1.0; - if (ix == asuint64(1.0)) - return issignaling_inline(y) ? x + y : 1.0; - if (2 * ix > 2 * asuint64(INFINITY) || 2 * iy > 2 * asuint64(INFINITY)) - return x + y; - if (2 * ix == 2 * asuint64(1.0)) - return 1.0; - if ((2 * ix < 2 * asuint64(1.0)) == !(iy >> 63)) - return 0.0; /* |x|<1 && y==inf or |x|>1 && y==-inf. */ - return y * y; - } - if (predict_false(zeroinfnan(ix))) { - double_t x2 = x * x; - if (ix >> 63 && checkint(iy) == 1) - x2 = -x2; - /* Without the barrier some versions of clang hoist the 1/x2 and - thus division by zero exception can be signaled spuriously. */ - return iy >> 63 ? fp_barrier(1 / x2) : x2; - } - /* Here x and y are non-zero finite. */ - if (ix >> 63) { - /* Finite x < 0. */ - int yint = checkint(iy); - if (yint == 0) - return __math_invalid(x); - if (yint == 1) - sign_bias = SIGN_BIAS; - ix &= 0x7fffffffffffffff; - topx &= 0x7ff; - } - if ((topy & 0x7ff) - 0x3be >= 0x43e - 0x3be) { - /* Note: sign_bias == 0 here because y is not odd. */ - if (ix == asuint64(1.0)) - return 1.0; - if ((topy & 0x7ff) < 0x3be) { - /* |y| < 2^-65, x^y ~= 1 + y*log(x). */ - if (WANT_ROUNDING) - return ix > asuint64(1.0) ? 1.0 + y : 1.0 - y; - else - return 1.0; - } - return (ix > asuint64(1.0)) == (topy < 0x800) ? __math_oflow(0) : __math_uflow(0); - } - if (topx == 0) { - /* Normalize subnormal x so exponent becomes negative. */ - ix = asuint64(x * 0x1p52); - ix &= 0x7fffffffffffffff; - ix -= 52ULL << 52; - } - } - - double_t lo; - double_t hi = log_inline(ix, &lo); - double_t ehi, elo; -#if __FP_FAST_FMA - ehi = y * hi; - elo = y * lo + __builtin_fma(y, hi, -ehi); -#else - double_t yhi = asdouble(iy & -1ULL << 27); - double_t ylo = y - yhi; - double_t lhi = asdouble(asuint64(hi) & -1ULL << 27); - double_t llo = hi - lhi + lo; - ehi = yhi * lhi; - elo = ylo * lhi + y * llo; /* |elo| < |ehi| * 2^-25. */ -#endif - return exp_inline(ehi, elo, sign_bias); -} -#endif diff --git a/ulib/ruxlibc/c/prctl.c b/ulib/ruxlibc/c/prctl.c deleted file mode 100644 index 7f0f1ee2c..000000000 --- a/ulib/ruxlibc/c/prctl.c +++ /dev/null @@ -1,19 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A - * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. - */ - -#include -#include - -// TODO -int prctl(int option, ...) -{ - unimplemented(); - return 0; -} diff --git a/ulib/ruxlibc/c/printf.c b/ulib/ruxlibc/c/printf.c deleted file mode 100644 index 128cfdb66..000000000 --- a/ulib/ruxlibc/c/printf.c +++ /dev/null @@ -1,1491 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -/** - * @author (c) Eyal Rozenberg - * 2021-2022, Haifa, Palestine/Israel - * @author (c) Marco Paland (info@paland.com) - * 2014-2019, PALANDesign Hannover, Germany - * - * @note Others have made smaller contributions to this file: see the - * contributors page at https://github.com/eyalroz/printf/graphs/contributors - * or ask one of the authors. The original code for exponential specifiers was - * contributed by Martijn Jasperse . - * - * @brief Small stand-alone implementation of the printf family of functions - * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with - * a very limited resources. - * - * @note the implementations are thread-safe; re-entrant; use no functions from - * the standard library; and do not dynamically allocate any memory. - * - * @license The MIT License (MIT) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -// Define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H=1 ...) to include the -// printf_config.h header file -#define PRINTF_INCLUDE_CONFIG_H 1 - -#if PRINTF_INCLUDE_CONFIG_H -#include "printf_config.h" -#endif - -#include "printf.h" - -#ifdef __cplusplus -#include -#include -#else -#include -#include -#include -#include -#endif // __cplusplus - -#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES -#define printf_ printf -#define sprintf_ sprintf -#define vsprintf_ vsprintf -#define snprintf_ snprintf -#define vsnprintf_ vsnprintf -#define vprintf_ vprintf -#endif - -// 'ntoa' conversion buffer size, this must be big enough to hold one converted -// numeric number including padded zeros (dynamically created on stack) -#ifndef PRINTF_INTEGER_BUFFER_SIZE -#define PRINTF_INTEGER_BUFFER_SIZE 32 -#endif - -// size of the fixed (on-stack) buffer for printing individual decimal numbers. -// this must be big enough to hold one converted floating-point value including -// padded zeros. -#ifndef PRINTF_DECIMAL_BUFFER_SIZE -#define PRINTF_DECIMAL_BUFFER_SIZE 32 -#endif - -// Support for the decimal notation floating point conversion specifiers (%f, %F) -#ifndef PRINTF_SUPPORT_DECIMAL_SPECIFIERS -#define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 1 -#endif - -// Support for the exponential notation floating point conversion specifiers (%e, %g, %E, %G) -#ifndef PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS -#define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1 -#endif - -// Support for the length write-back specifier (%n) -#ifndef PRINTF_SUPPORT_WRITEBACK_SPECIFIER -#define PRINTF_SUPPORT_WRITEBACK_SPECIFIER 1 -#endif - -// Default precision for the floating point conversion specifiers (the C standard sets this at 6) -#ifndef PRINTF_DEFAULT_FLOAT_PRECISION -#define PRINTF_DEFAULT_FLOAT_PRECISION 6 -#endif - -// According to the C languages standard, printf() and related functions must be able to print any -// integral number in floating-point notation, regardless of length, when using the %f specifier - -// possibly hundreds of characters, potentially overflowing your buffers. In this implementation, -// all values beyond this threshold are switched to exponential notation. -#ifndef PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL -#define PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9 -#endif - -// Support for the long long integral types (with the ll, z and t length modifiers for specifiers -// %d,%i,%o,%x,%X,%u, and with the %p specifier). Note: 'L' (long double) is not supported. -#ifndef PRINTF_SUPPORT_LONG_LONG -#define PRINTF_SUPPORT_LONG_LONG 1 -#endif - -// The number of terms in a Taylor series expansion of log_10(x) to -// use for approximation - including the power-zero term (i.e. the -// value at the point of expansion). -#ifndef PRINTF_LOG10_TAYLOR_TERMS -#define PRINTF_LOG10_TAYLOR_TERMS 4 -#endif - -#if PRINTF_LOG10_TAYLOR_TERMS <= 1 -#error "At least one non-constant Taylor expansion is necessary for the log10() calculation" -#endif - -// Be extra-safe, and don't assume format specifiers are completed correctly -// before the format string end. -#ifndef PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER -#define PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER 1 -#endif - -#define PRINTF_PREFER_DECIMAL false -#define PRINTF_PREFER_EXPONENTIAL true - -/////////////////////////////////////////////////////////////////////////////// - -// The following will convert the number-of-digits into an exponential-notation literal -#define PRINTF_CONCATENATE(s1, s2) s1##s2 -#define PRINTF_EXPAND_THEN_CONCATENATE(s1, s2) PRINTF_CONCATENATE(s1, s2) -#define PRINTF_FLOAT_NOTATION_THRESHOLD \ - PRINTF_EXPAND_THEN_CONCATENATE(1e, PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL) - -// internal flag definitions -#define FLAGS_ZEROPAD (1U << 0U) -#define FLAGS_LEFT (1U << 1U) -#define FLAGS_PLUS (1U << 2U) -#define FLAGS_SPACE (1U << 3U) -#define FLAGS_HASH (1U << 4U) -#define FLAGS_UPPERCASE (1U << 5U) -#define FLAGS_CHAR (1U << 6U) -#define FLAGS_SHORT (1U << 7U) -#define FLAGS_INT (1U << 8U) -// Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS -#define FLAGS_LONG (1U << 9U) -#define FLAGS_LONG_LONG (1U << 10U) -#define FLAGS_PRECISION (1U << 11U) -#define FLAGS_ADAPT_EXP (1U << 12U) -#define FLAGS_POINTER (1U << 13U) -// Note: Similar, but not identical, effect as FLAGS_HASH -#define FLAGS_SIGNED (1U << 14U) -// Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS - -#ifdef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS - -#define FLAGS_INT8 FLAGS_CHAR - -#if (SHRT_MAX == 32767LL) -#define FLAGS_INT16 FLAGS_SHORT -#elif (INT_MAX == 32767LL) -#define FLAGS_INT16 FLAGS_INT -#elif (LONG_MAX == 32767LL) -#define FLAGS_INT16 FLAGS_LONG -#elif (LLONG_MAX == 32767LL) -#define FLAGS_INT16 FLAGS_LONG_LONG -#else -#error "No basic integer type has a size of 16 bits exactly" -#endif - -#if (SHRT_MAX == 2147483647LL) -#define FLAGS_INT32 FLAGS_SHORT -#elif (INT_MAX == 2147483647LL) -#define FLAGS_INT32 FLAGS_INT -#elif (LONG_MAX == 2147483647LL) -#define FLAGS_INT32 FLAGS_LONG -#elif (LLONG_MAX == 2147483647LL) -#define FLAGS_INT32 FLAGS_LONG_LONG -#else -#error "No basic integer type has a size of 32 bits exactly" -#endif - -#if (SHRT_MAX == 9223372036854775807LL) -#define FLAGS_INT64 FLAGS_SHORT -#elif (INT_MAX == 9223372036854775807LL) -#define FLAGS_INT64 FLAGS_INT -#elif (LONG_MAX == 9223372036854775807LL) -#define FLAGS_INT64 FLAGS_LONG -#elif (LLONG_MAX == 9223372036854775807LL) -#define FLAGS_INT64 FLAGS_LONG_LONG -#else -#error "No basic integer type has a size of 64 bits exactly" -#endif - -#endif // PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS - -typedef unsigned int printf_flags_t; - -#define BASE_BINARY 2 -#define BASE_OCTAL 8 -#define BASE_DECIMAL 10 -#define BASE_HEX 16 - -typedef uint8_t numeric_base_t; - -#if PRINTF_SUPPORT_LONG_LONG -typedef unsigned long long printf_unsigned_value_t; -typedef long long printf_signed_value_t; -#else -typedef unsigned long printf_unsigned_value_t; -typedef long printf_signed_value_t; -#endif - -// The printf()-family functions return an `int`; it is therefore -// unnecessary/inappropriate to use size_t - often larger than int -// in practice - for non-negative related values, such as widths, -// precisions, offsets into buffers used for printing and the sizes -// of these buffers. instead, we use: -typedef unsigned int printf_size_t; -#define PRINTF_MAX_POSSIBLE_BUFFER_SIZE INT_MAX -// If we were to nitpick, this would actually be INT_MAX + 1, -// since INT_MAX is the maximum return value, which excludes the -// trailing '\0'. - -#if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) -#include -#if FLT_RADIX != 2 -#error "Non-binary-radix floating-point types are unsupported." -#endif - -#if DBL_MANT_DIG == 24 - -#define DOUBLE_SIZE_IN_BITS 32 -typedef uint32_t double_uint_t; -#define DOUBLE_EXPONENT_MASK 0xFFU -#define DOUBLE_BASE_EXPONENT 127 -#define DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10 -38 -#define DOUBLE_MAX_SUBNORMAL_POWER_OF_10 1e-38 - -#elif DBL_MANT_DIG == 53 - -#define DOUBLE_SIZE_IN_BITS 64 -typedef uint64_t double_uint_t; -#define DOUBLE_EXPONENT_MASK 0x7FFU -#define DOUBLE_BASE_EXPONENT 1023 -#define DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10 -308 -#define DOUBLE_MAX_SUBNORMAL_POWER_OF_10 1e-308 - -#else -#error "Unsupported double type configuration" -#endif -#define DOUBLE_STORED_MANTISSA_BITS (DBL_MANT_DIG - 1) - -typedef union { - double_uint_t U; - double F; -} double_with_bit_access; - -// This is unnecessary in C99, since compound initializers can be used, -// but: -// 1. Some compilers are finicky about this; -// 2. Some people may want to convert this to C89; -// 3. If you try to use it as C++, only C++20 supports compound literals -static inline double_with_bit_access get_bit_access(double x) -{ - double_with_bit_access dwba; - dwba.F = x; - return dwba; -} - -static inline int get_sign_bit(double x) -{ - // The sign is stored in the highest bit - return (int)(get_bit_access(x).U >> (DOUBLE_SIZE_IN_BITS - 1)); -} - -static inline int get_exp2(double_with_bit_access x) -{ - // The exponent in an IEEE-754 floating-point number occupies a contiguous - // sequence of bits (e.g. 52..62 for 64-bit doubles), but with a non-trivial representation: An - // unsigned offset from some negative value (with the extremal offset values reserved for - // special use). - return (int)((x.U >> DOUBLE_STORED_MANTISSA_BITS) & DOUBLE_EXPONENT_MASK) - - DOUBLE_BASE_EXPONENT; -} -#define PRINTF_ABS(_x) ((_x) > 0 ? (_x) : -(_x)) - -#endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) - -// Note in particular the behavior here on LONG_MIN or LLONG_MIN; it is valid -// and well-defined, but if you're not careful you can easily trigger undefined -// behavior with -LONG_MIN or -LLONG_MIN -#define ABS_FOR_PRINTING(_x) \ - ((printf_unsigned_value_t)((_x) > 0 ? (_x) : -((printf_signed_value_t)_x))) - -// wrapper (used as buffer) for output function type -// -// One of the following must hold: -// 1. max_chars is 0 -// 2. buffer is non-null -// 3. function is non-null -// -// ... otherwise bad things will happen. -typedef struct { - void (*function)(char c, void *extra_arg); - void *extra_function_arg; - char *buffer; - printf_size_t pos; - printf_size_t max_chars; -} output_gadget_t; - -// Note: This function currently assumes it is not passed a '\0' c, -// or alternatively, that '\0' can be passed to the function in the output -// gadget. The former assumption holds within the printf library. It also -// assumes that the output gadget has been properly initialized. -static inline void putchar_via_gadget(output_gadget_t *gadget, char c) -{ - printf_size_t write_pos = gadget->pos++; - // We're _always_ increasing pos, so as to count how may characters - // _would_ have been written if not for the max_chars limitation - if (write_pos >= gadget->max_chars) { - return; - } - if (gadget->function != NULL) { - // No check for c == '\0' . - gadget->function(c, gadget->extra_function_arg); - } else { - // it must be the case that gadget->buffer != NULL , due to the constraint - // on output_gadget_t ; and note we're relying on write_pos being non-negative. - gadget->buffer[write_pos] = c; - } -} - -// Possibly-write the string-terminating '\0' character -static inline void append_termination_with_gadget(output_gadget_t *gadget) -{ - if (gadget->function != NULL || gadget->max_chars == 0) { - return; - } - if (gadget->buffer == NULL) { - return; - } - printf_size_t null_char_pos = - gadget->pos < gadget->max_chars ? gadget->pos : gadget->max_chars - 1; - gadget->buffer[null_char_pos] = '\0'; -} - -// We can't use putchar_ as is, since our output gadget -// only takes pointers to functions with an extra argument -// static inline void putchar_wrapper(char c, void *unused) -// { -// (void)unused; -// putchar_(c); -// } - -static inline output_gadget_t discarding_gadget(void) -{ - output_gadget_t gadget; - gadget.function = NULL; - gadget.extra_function_arg = NULL; - gadget.buffer = NULL; - gadget.pos = 0; - gadget.max_chars = 0; - return gadget; -} - -static inline output_gadget_t buffer_gadget(char *buffer, size_t buffer_size) -{ - printf_size_t usable_buffer_size = (buffer_size > PRINTF_MAX_POSSIBLE_BUFFER_SIZE) - ? PRINTF_MAX_POSSIBLE_BUFFER_SIZE - : (printf_size_t)buffer_size; - output_gadget_t result = discarding_gadget(); - if (buffer != NULL) { - result.buffer = buffer; - result.max_chars = usable_buffer_size; - } - return result; -} - -static inline output_gadget_t function_gadget(void (*function)(char, void *), void *extra_arg) -{ - output_gadget_t result = discarding_gadget(); - result.function = function; - result.extra_function_arg = extra_arg; - result.max_chars = PRINTF_MAX_POSSIBLE_BUFFER_SIZE; - return result; -} - -// static inline output_gadget_t extern_putchar_gadget(void) -// { -// return function_gadget(putchar_wrapper, NULL); -// } - -// internal secure strlen -// @return The length of the string (excluding the terminating 0) limited by 'maxsize' -// @note strlen uses size_t, but wes only use this function with printf_size_t -// variables - hence the signature. -static inline printf_size_t strnlen_s_(const char *str, printf_size_t maxsize) -{ - const char *s; - for (s = str; *s && maxsize--; ++s) - ; - return (printf_size_t)(s - str); -} - -// internal test if char is a digit (0-9) -// @return true if char is a digit -static inline bool is_digit_(char ch) -{ - return (ch >= '0') && (ch <= '9'); -} - -// internal ASCII string to printf_size_t conversion -static printf_size_t atou_(const char **str) -{ - printf_size_t i = 0U; - while (is_digit_(**str)) { - i = i * 10U + (printf_size_t)(*((*str)++) - '0'); - } - return i; -} - -// output the specified string in reverse, taking care of any zero-padding -static void out_rev_(output_gadget_t *output, const char *buf, printf_size_t len, - printf_size_t width, printf_flags_t flags) -{ - const printf_size_t start_pos = output->pos; - - // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (printf_size_t i = len; i < width; i++) { - putchar_via_gadget(output, ' '); - } - } - - // reverse string - while (len) { - putchar_via_gadget(output, buf[--len]); - } - - // append pad spaces up to given width - if (flags & FLAGS_LEFT) { - while (output->pos - start_pos < width) { - putchar_via_gadget(output, ' '); - } - } -} - -// Invoked by print_integer after the actual number has been printed, performing necessary -// work on the number's prefix (as the number is initially printed in reverse order) -static void print_integer_finalization(output_gadget_t *output, char *buf, printf_size_t len, - bool negative, numeric_base_t base, printf_size_t precision, - printf_size_t width, printf_flags_t flags) -{ - printf_size_t unpadded_len = len; - - // pad with leading zeros - { - if (!(flags & FLAGS_LEFT)) { - if (width && (flags & FLAGS_ZEROPAD) && - (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = '0'; - } - } - - while ((len < precision) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = '0'; - } - - if (base == BASE_OCTAL && (len > unpadded_len)) { - // Since we've written some zeros, we've satisfied the alternative format leading space - // requirement - flags &= ~FLAGS_HASH; - } - } - - // handle hash - if (flags & (FLAGS_HASH | FLAGS_POINTER)) { - if (!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) { - // Let's take back some padding digits to fit in what will eventually - // be the format-specific prefix - if (unpadded_len < len) { - len--; // This should suffice for BASE_OCTAL - } - if (len && (base == BASE_HEX || base == BASE_BINARY) && (unpadded_len < len)) { - len--; // ... and an extra one for 0x or 0b - } - } - if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && - (len < PRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = 'x'; - } else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && - (len < PRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = 'X'; - } else if ((base == BASE_BINARY) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = 'b'; - } - if (len < PRINTF_INTEGER_BUFFER_SIZE) { - buf[len++] = '0'; - } - } - - if (len < PRINTF_INTEGER_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - out_rev_(output, buf, len, width, flags); -} - -// An internal itoa-like function -static void print_integer(output_gadget_t *output, printf_unsigned_value_t value, bool negative, - numeric_base_t base, printf_size_t precision, printf_size_t width, - printf_flags_t flags) -{ - char buf[PRINTF_INTEGER_BUFFER_SIZE]; - printf_size_t len = 0U; - - if (!value) { - if (!(flags & FLAGS_PRECISION)) { - buf[len++] = '0'; - flags &= ~FLAGS_HASH; - // We drop this flag this since either the alternative and regular modes of the - // specifier don't differ on 0 values, or (in the case of octal) we've already provided - // the special handling for this mode. - } else if (base == BASE_HEX) { - flags &= ~FLAGS_HASH; - // We drop this flag this since either the alternative and regular modes of the - // specifier don't differ on 0 values - } - } else { - do { - const char digit = (char)(value % base); - buf[len++] = (char)(digit < 10 ? '0' + digit - : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); - value /= base; - } while (value && (len < PRINTF_INTEGER_BUFFER_SIZE)); - } - - print_integer_finalization(output, buf, len, negative, base, precision, width, flags); -} - -#if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) - -// Stores a fixed-precision representation of a double relative -// to a fixed precision (which cannot be determined by examining this structure) -struct double_components { - int_fast64_t integral; - int_fast64_t fractional; - // ... truncation of the actual fractional part of the double value, scaled - // by the precision value - bool is_negative; -}; - -#define NUM_DECIMAL_DIGITS_IN_INT64_T 18 -#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 NUM_DECIMAL_DIGITS_IN_INT64_T -static const double powers_of_10[NUM_DECIMAL_DIGITS_IN_INT64_T] = { - 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, - 1e09, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17}; - -#define PRINTF_MAX_SUPPORTED_PRECISION NUM_DECIMAL_DIGITS_IN_INT64_T - 1 - -// Break up a double number - which is known to be a finite non-negative number - -// into its base-10 parts: integral - before the decimal point, and fractional - after it. -// Taken the precision into account, but does not change it even internally. -static struct double_components get_components(double number, printf_size_t precision) -{ - struct double_components number_; - number_.is_negative = get_sign_bit(number); - double abs_number = (number_.is_negative) ? -number : number; - number_.integral = (int_fast64_t)abs_number; - double remainder = (abs_number - (double)number_.integral) * powers_of_10[precision]; - number_.fractional = (int_fast64_t)remainder; - - remainder -= (double)number_.fractional; - - if (remainder > 0.5) { - ++number_.fractional; - // handle rollover, e.g. case 0.99 with precision 1 is 1.0 - if ((double)number_.fractional >= powers_of_10[precision]) { - number_.fractional = 0; - ++number_.integral; - } - } else if ((remainder == 0.5) && ((number_.fractional == 0U) || (number_.fractional & 1U))) { - // if halfway, round up if odd OR if last digit is 0 - ++number_.fractional; - } - - if (precision == 0U) { - remainder = abs_number - (double)number_.integral; - if ((!(remainder < 0.5) || (remainder > 0.5)) && (number_.integral & 1)) { - // exactly 0.5 and ODD, then round up - // 1.5 -> 2, but 2.5 -> 2 - ++number_.integral; - } - } - return number_; -} - -#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS -struct scaling_factor { - double raw_factor; - bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it -}; - -static double apply_scaling(double num, struct scaling_factor normalization) -{ - return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; -} - -static double unapply_scaling(double normalized, struct scaling_factor normalization) -{ -#ifdef __GNUC__ -// accounting for a static analysis bug in GCC 6.x and earlier -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#endif - return normalization.multiply ? normalized / normalization.raw_factor - : normalized * normalization.raw_factor; -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif -} - -static struct scaling_factor update_normalization(struct scaling_factor sf, - double extra_multiplicative_factor) -{ - struct scaling_factor result; - if (sf.multiply) { - result.multiply = true; - result.raw_factor = sf.raw_factor * extra_multiplicative_factor; - } else { - int factor_exp2 = get_exp2(get_bit_access(sf.raw_factor)); - int extra_factor_exp2 = get_exp2(get_bit_access(extra_multiplicative_factor)); - - // Divide the larger-exponent raw raw_factor by the smaller - if (PRINTF_ABS(factor_exp2) > PRINTF_ABS(extra_factor_exp2)) { - result.multiply = false; - result.raw_factor = sf.raw_factor / extra_multiplicative_factor; - } else { - result.multiply = true; - result.raw_factor = extra_multiplicative_factor / sf.raw_factor; - } - } - return result; -} - -static struct double_components get_normalized_components(bool negative, printf_size_t precision, - double non_normalized, - struct scaling_factor normalization, - int floored_exp10) -{ - struct double_components components; - components.is_negative = negative; - double scaled = apply_scaling(non_normalized, normalization); - - bool close_to_representation_extremum = - ((-floored_exp10 + (int)precision) >= DBL_MAX_10_EXP - 1); - if (close_to_representation_extremum) { - // We can't have a normalization factor which also accounts for the precision, i.e. moves - // some decimal digits into the mantissa, since it's unrepresentable, or nearly - // unrepresentable. So, we'll give up early on getting extra precision... - return get_components(negative ? -scaled : scaled, precision); - } - components.integral = (int_fast64_t)scaled; - double remainder = non_normalized - unapply_scaling((double)components.integral, normalization); - double prec_power_of_10 = powers_of_10[precision]; - struct scaling_factor account_for_precision = - update_normalization(normalization, prec_power_of_10); - double scaled_remainder = apply_scaling(remainder, account_for_precision); - double rounding_threshold = 0.5; - - components.fractional = - (int_fast64_t)scaled_remainder; // when precision == 0, the assigned value should be 0 - scaled_remainder -= - (double)components.fractional; // when precision == 0, this will not change scaled_remainder - - components.fractional += (scaled_remainder >= rounding_threshold); - if (scaled_remainder == rounding_threshold) { - // banker's rounding: Round towards the even number (making the mean error 0) - components.fractional &= ~((int_fast64_t)0x1); - } - // handle rollover, e.g. the case of 0.99 with precision 1 becoming (0,100), - // and must then be corrected into (1, 0). - // Note: for precision = 0, this will "translate" the rounding effect from - // the fractional part to the integral part where it should actually be - // felt (as prec_power_of_10 is 1) - if ((double)components.fractional >= prec_power_of_10) { - components.fractional = 0; - ++components.integral; - } - return components; -} -#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - -static void print_broken_up_decimal(struct double_components number_, output_gadget_t *output, - printf_size_t precision, printf_size_t width, - printf_flags_t flags, char *buf, printf_size_t len) -{ - if (precision != 0U) { - // do fractional part, as an unsigned number - - printf_size_t count = precision; - - // %g/%G mandates we skip the trailing 0 digits... - if ((flags & FLAGS_ADAPT_EXP) && !(flags & FLAGS_HASH) && (number_.fractional > 0)) { - while (true) { - int_fast64_t digit = number_.fractional % 10U; - if (digit != 0) { - break; - } - --count; - number_.fractional /= 10U; - } - // ... and even the decimal point if there are no - // non-zero fractional part digits (see below) - } - - if (number_.fractional > 0 || !(flags & FLAGS_ADAPT_EXP) || (flags & FLAGS_HASH)) { - while (len < PRINTF_DECIMAL_BUFFER_SIZE) { - --count; - buf[len++] = (char)('0' + number_.fractional % 10U); - if (!(number_.fractional /= 10U)) { - break; - } - } - // add extra 0s - while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (count > 0U)) { - buf[len++] = '0'; - --count; - } - if (len < PRINTF_DECIMAL_BUFFER_SIZE) { - buf[len++] = '.'; - } - } - } else { - if ((flags & FLAGS_HASH) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) { - buf[len++] = '.'; - } - } - - // Write the integer part of the number (it comes after the fractional - // since the character order is reversed) - while (len < PRINTF_DECIMAL_BUFFER_SIZE) { - buf[len++] = (char)('0' + (number_.integral % 10)); - if (!(number_.integral /= 10)) { - break; - } - } - - // pad leading zeros - if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { - if (width && (number_.is_negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < width) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) { - buf[len++] = '0'; - } - } - - if (len < PRINTF_DECIMAL_BUFFER_SIZE) { - if (number_.is_negative) { - buf[len++] = '-'; - } else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - out_rev_(output, buf, len, width, flags); -} - -// internal ftoa for fixed decimal floating point -static void print_decimal_number(output_gadget_t *output, double number, printf_size_t precision, - printf_size_t width, printf_flags_t flags, char *buf, - printf_size_t len) -{ - struct double_components value_ = get_components(number, precision); - print_broken_up_decimal(value_, output, precision, width, flags, buf, len); -} - -#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - -// A floor function - but one which only works for numbers whose -// floor value is representable by an int. -static int bastardized_floor(double x) -{ - if (x >= 0) { - return (int)x; - } - int n = (int)x; - return (((double)n) == x) ? n : n - 1; -} - -// Computes the base-10 logarithm of the input number - which must be an actual -// positive number (not infinity or NaN, nor a sub-normal) -static double log10_of_positive(double positive_number) -{ - // The implementation follows David Gay (https://www.ampl.com/netlib/fp/dtoa.c). - // - // Since log_10 ( M * 2^x ) = log_10(M) + x , we can separate the components of - // our input number, and need only solve log_10(M) for M between 1 and 2 (as - // the base-2 mantissa is always 1-point-something). In that limited range, a - // Taylor series expansion of log10(x) should serve us well enough; and we'll - // take the mid-point, 1.5, as the point of expansion. - - double_with_bit_access dwba = get_bit_access(positive_number); - // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) - int exp2 = get_exp2(dwba); - // drop the exponent, so dwba.F comes into the range [1,2) - dwba.U = (dwba.U & (((double_uint_t)(1) << DOUBLE_STORED_MANTISSA_BITS) - 1U)) | - ((double_uint_t)DOUBLE_BASE_EXPONENT << DOUBLE_STORED_MANTISSA_BITS); - double z = (dwba.F - 1.5); - return ( - // Taylor expansion around 1.5: - 0.1760912590556812420 // Expansion term 0: ln(1.5) / ln(10) - + z * 0.2895296546021678851 // Expansion term 1: (M - 1.5) * 2/3 / ln(10) -#if PRINTF_LOG10_TAYLOR_TERMS > 2 - - z * z * 0.0965098848673892950 // Expansion term 2: (M - 1.5)^2 * 2/9 / ln(10) -#if PRINTF_LOG10_TAYLOR_TERMS > 3 - + z * z * z * 0.0428932821632841311 // Expansion term 2: (M - 1.5)^3 * 8/81 / ln(10) -#endif -#endif - // exact log_2 of the exponent x, with logarithm base change - + exp2 * 0.30102999566398119521 // = exp2 * log_10(2) = exp2 * ln(2)/ln(10) - ); -} - -static double pow10_of_int(int floored_exp10) -{ - // A crude hack for avoiding undesired behavior with barely-normal or slightly-subnormal values. - if (floored_exp10 == DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10) { - return DOUBLE_MAX_SUBNORMAL_POWER_OF_10; - } - // Compute 10^(floored_exp10) but (try to) make sure that doesn't overflow - double_with_bit_access dwba; - int exp2 = bastardized_floor(floored_exp10 * 3.321928094887362 + 0.5); - const double z = floored_exp10 * 2.302585092994046 - exp2 * 0.6931471805599453; - const double z2 = z * z; - dwba.U = ((double_uint_t)(exp2) + DOUBLE_BASE_EXPONENT) << DOUBLE_STORED_MANTISSA_BITS; - // compute exp(z) using continued fractions, - // see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex - dwba.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); - return dwba.F; -} - -static void print_exponential_number(output_gadget_t *output, double number, - printf_size_t precision, printf_size_t width, - printf_flags_t flags, char *buf, printf_size_t len) -{ - const bool negative = get_sign_bit(number); - // This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it - double abs_number = negative ? -number : number; - - int floored_exp10; - bool abs_exp10_covered_by_powers_table; - struct scaling_factor normalization; - - // Determine the decimal exponent - if (abs_number == 0.0) { - // TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for - // denormals more generally. - floored_exp10 = - 0; // ... and no need to set a normalization factor or check the powers table - } else { - double exp10 = log10_of_positive(abs_number); - floored_exp10 = bastardized_floor(exp10); - double p10 = pow10_of_int(floored_exp10); - // correct for rounding errors - if (abs_number < p10) { - floored_exp10--; - p10 /= 10; - } - abs_exp10_covered_by_powers_table = - PRINTF_ABS(floored_exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10; - normalization.raw_factor = - abs_exp10_covered_by_powers_table ? powers_of_10[PRINTF_ABS(floored_exp10)] : p10; - } - - // We now begin accounting for the widths of the two parts of our printed field: - // the decimal part after decimal exponent extraction, and the base-10 exponent part. - // For both of these, the value of 0 has a special meaning, but not the same one: - // a 0 exponent-part width means "don't print the exponent"; a 0 decimal-part width - // means "use as many characters as necessary". - - bool fall_back_to_decimal_only_mode = false; - if (flags & FLAGS_ADAPT_EXP) { - int required_significant_digits = (precision == 0) ? 1 : (int)precision; - // Should we want to fall-back to "%f" mode, and only print the decimal part? - fall_back_to_decimal_only_mode = - (floored_exp10 >= -4 && floored_exp10 < required_significant_digits); - // Now, let's adjust the precision - // This also decided how we adjust the precision value - as in "%g" mode, - // "precision" is the number of _significant digits_, and this is when we "translate" - // the precision value to an actual number of decimal digits. - int precision_ = - fall_back_to_decimal_only_mode - ? (int)precision - 1 - floored_exp10 - : (int)precision - 1; // the presence of the exponent ensures only one significant - // digit comes before the decimal point - precision = (precision_ > 0 ? (unsigned)precision_ : 0U); - flags |= FLAGS_PRECISION; // make sure print_broken_up_decimal respects our choice above - } - - normalization.multiply = (floored_exp10 < 0 && abs_exp10_covered_by_powers_table); - bool should_skip_normalization = (fall_back_to_decimal_only_mode || floored_exp10 == 0); - struct double_components decimal_part_components = - should_skip_normalization ? get_components(negative ? -abs_number : abs_number, precision) - : get_normalized_components(negative, precision, abs_number, - normalization, floored_exp10); - - // Account for roll-over, e.g. rounding from 9.99 to 100.0 - which effects - // the exponent and may require additional tweaking of the parts - if (fall_back_to_decimal_only_mode) { - if ((flags & FLAGS_ADAPT_EXP) && floored_exp10 >= -1 && - decimal_part_components.integral == powers_of_10[floored_exp10 + 1]) { - floored_exp10++; // Not strictly necessary, since floored_exp10 is no longer really used - precision--; - // ... and it should already be the case that decimal_part_components.fractional == 0 - } - // TODO: What about rollover strictly within the fractional part? - } else { - if (decimal_part_components.integral >= 10) { - floored_exp10++; - decimal_part_components.integral = 1; - decimal_part_components.fractional = 0; - } - } - - // the floored_exp10 format is "E%+03d" and largest possible floored_exp10 value for a 64-bit - // double is "307" (for 2^1023), so we set aside 4-5 characters overall - printf_size_t exp10_part_width = fall_back_to_decimal_only_mode ? 0U - : (PRINTF_ABS(floored_exp10) < 100) ? 4U - : 5U; - - printf_size_t decimal_part_width = - ((flags & FLAGS_LEFT) && exp10_part_width) - ? - // We're padding on the right, so the width constraint is the exponent part's - // problem, not the decimal part's, so we'll use as many characters as we need: - 0U - : - // We're padding on the left; so the width constraint is the decimal part's - // problem. Well, can both the decimal part and the exponent part fit within our overall - // width? - ((width > exp10_part_width) - ? - // Yes, so we limit our decimal part's width. - // (Note this is trivially valid even if we've fallen back to "%f" mode) - width - exp10_part_width - : - // No; we just give up on any restriction on the decimal part and use as many - // characters as we need - 0U); - - const printf_size_t printed_exponential_start_pos = output->pos; - print_broken_up_decimal(decimal_part_components, output, precision, decimal_part_width, flags, - buf, len); - - if (!fall_back_to_decimal_only_mode) { - putchar_via_gadget(output, (flags & FLAGS_UPPERCASE) ? 'E' : 'e'); - print_integer(output, ABS_FOR_PRINTING(floored_exp10), floored_exp10 < 0, 10, 0, - exp10_part_width - 1, FLAGS_ZEROPAD | FLAGS_PLUS); - if (flags & FLAGS_LEFT) { - // We need to right-pad with spaces to meet the width requirement - while (output->pos - printed_exponential_start_pos < width) { - putchar_via_gadget(output, ' '); - } - } - } -} -#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - -static void print_floating_point(output_gadget_t *output, double value, printf_size_t precision, - printf_size_t width, printf_flags_t flags, bool prefer_exponential) -{ - char buf[PRINTF_DECIMAL_BUFFER_SIZE]; - printf_size_t len = 0U; - - // test for special values - if (value != value) { - out_rev_(output, "nan", 3, width, flags); - return; - } - if (value < -DBL_MAX) { - out_rev_(output, "fni-", 4, width, flags); - return; - } - if (value > DBL_MAX) { - out_rev_(output, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, - width, flags); - return; - } - - if (!prefer_exponential && - ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD))) { - // The required behavior of standard printf is to print _every_ integral-part digit -- which - // could mean printing hundreds of characters, overflowing any fixed internal buffer and - // necessitating a more complicated implementation. -#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - print_exponential_number(output, value, precision, width, flags, buf, len); -#endif - return; - } - - // set default precision, if not set explicitly - if (!(flags & FLAGS_PRECISION)) { - precision = PRINTF_DEFAULT_FLOAT_PRECISION; - } - - // limit precision so that our integer holding the fractional part does not overflow - while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) { - buf[len++] = '0'; // This respects the precision in terms of result length only - precision--; - } - -#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - if (prefer_exponential) - print_exponential_number(output, value, precision, width, flags, buf, len); - else -#endif - print_decimal_number(output, value, precision, width, flags, buf, len); -} - -#endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) - -// Advances the format pointer past the flags, and returns the parsed flags -// due to the characters passed -static printf_flags_t parse_flags(const char **format) -{ - printf_flags_t flags = 0U; - do { - switch (**format) { - case '0': - flags |= FLAGS_ZEROPAD; - (*format)++; - break; - case '-': - flags |= FLAGS_LEFT; - (*format)++; - break; - case '+': - flags |= FLAGS_PLUS; - (*format)++; - break; - case ' ': - flags |= FLAGS_SPACE; - (*format)++; - break; - case '#': - flags |= FLAGS_HASH; - (*format)++; - break; - default: - return flags; - } - } while (true); -} - -static inline void format_string_loop(output_gadget_t *output, const char *format, va_list args) -{ -#if PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER -#define ADVANCE_IN_FORMAT_STRING(cptr_) \ - do { \ - (cptr_)++; \ - if (!*(cptr_)) \ - return; \ - } while (0) -#else -#define ADVANCE_IN_FORMAT_STRING(cptr_) (cptr_)++ -#endif - - while (*format) { - if (*format != '%') { - // A regular content character - putchar_via_gadget(output, *format); - format++; - continue; - } - // We're parsing a format specifier: %[flags][width][.precision][length] - ADVANCE_IN_FORMAT_STRING(format); - - printf_flags_t flags = parse_flags(&format); - - // evaluate width field - printf_size_t width = 0U; - if (is_digit_(*format)) { - width = (printf_size_t)atou_(&format); - } else if (*format == '*') { - const int w = va_arg(args, int); - if (w < 0) { - flags |= FLAGS_LEFT; // reverse padding - width = (printf_size_t)-w; - } else { - width = (printf_size_t)w; - } - ADVANCE_IN_FORMAT_STRING(format); - } - - // evaluate precision field - printf_size_t precision = 0U; - if (*format == '.') { - flags |= FLAGS_PRECISION; - ADVANCE_IN_FORMAT_STRING(format); - if (is_digit_(*format)) { - precision = (printf_size_t)atou_(&format); - } else if (*format == '*') { - const int precision_ = va_arg(args, int); - precision = precision_ > 0 ? (printf_size_t)precision_ : 0U; - ADVANCE_IN_FORMAT_STRING(format); - } - } - - // evaluate length field - switch (*format) { -#ifdef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS - case 'I': { - ADVANCE_IN_FORMAT_STRING(format); - // Greedily parse for size in bits: 8, 16, 32 or 64 - switch (*format) { - case '8': - flags |= FLAGS_INT8; - ADVANCE_IN_FORMAT_STRING(format); - break; - case '1': - ADVANCE_IN_FORMAT_STRING(format); - if (*format == '6') { - format++; - flags |= FLAGS_INT16; - } - break; - case '3': - ADVANCE_IN_FORMAT_STRING(format); - if (*format == '2') { - ADVANCE_IN_FORMAT_STRING(format); - flags |= FLAGS_INT32; - } - break; - case '6': - ADVANCE_IN_FORMAT_STRING(format); - if (*format == '4') { - ADVANCE_IN_FORMAT_STRING(format); - flags |= FLAGS_INT64; - } - break; - default: - break; - } - break; - } -#endif - case 'l': - flags |= FLAGS_LONG; - ADVANCE_IN_FORMAT_STRING(format); - if (*format == 'l') { - flags |= FLAGS_LONG_LONG; - ADVANCE_IN_FORMAT_STRING(format); - } - break; - case 'h': - flags |= FLAGS_SHORT; - ADVANCE_IN_FORMAT_STRING(format); - if (*format == 'h') { - flags |= FLAGS_CHAR; - ADVANCE_IN_FORMAT_STRING(format); - } - break; - case 't': - flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - ADVANCE_IN_FORMAT_STRING(format); - break; - case 'j': - flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - ADVANCE_IN_FORMAT_STRING(format); - break; - case 'z': - flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - ADVANCE_IN_FORMAT_STRING(format); - break; - default: - break; - } - - // evaluate specifier - switch (*format) { - case 'd': - case 'i': - case 'u': - case 'x': - case 'X': - case 'o': - case 'b': { - - if (*format == 'd' || *format == 'i') { - flags |= FLAGS_SIGNED; - } - - numeric_base_t base; - if (*format == 'x' || *format == 'X') { - base = BASE_HEX; - } else if (*format == 'o') { - base = BASE_OCTAL; - } else if (*format == 'b') { - base = BASE_BINARY; - } else { - base = BASE_DECIMAL; - flags &= ~FLAGS_HASH; // decimal integers have no alternative presentation - } - - if (*format == 'X') { - flags |= FLAGS_UPPERCASE; - } - - format++; - // ignore '0' flag when precision is given - if (flags & FLAGS_PRECISION) { - flags &= ~FLAGS_ZEROPAD; - } - - if (flags & FLAGS_SIGNED) { - // A signed specifier: d, i or possibly I + bit size if enabled - - if (flags & FLAGS_LONG_LONG) { -#if PRINTF_SUPPORT_LONG_LONG - const long long value = va_arg(args, long long); - print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, - width, flags); -#endif - } else if (flags & FLAGS_LONG) { - const long value = va_arg(args, long); - print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, - width, flags); - } else { - // We never try to interpret the argument as something potentially-smaller than - // int, due to integer promotion rules: Even if the user passed a short int, - // short unsigned etc. - these will come in after promotion, as int's (or - // unsigned for the case of short unsigned when it has the same size as int) - const int value = (flags & FLAGS_CHAR) ? (signed char)va_arg(args, int) - : (flags & FLAGS_SHORT) ? (short int)va_arg(args, int) - : va_arg(args, int); - print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, - width, flags); - } - } else { - // An unsigned specifier: u, x, X, o, b - - flags &= ~(FLAGS_PLUS | FLAGS_SPACE); - - if (flags & FLAGS_LONG_LONG) { -#if PRINTF_SUPPORT_LONG_LONG - print_integer(output, (printf_unsigned_value_t)va_arg(args, unsigned long long), - false, base, precision, width, flags); -#endif - } else if (flags & FLAGS_LONG) { - print_integer(output, (printf_unsigned_value_t)va_arg(args, unsigned long), - false, base, precision, width, flags); - } else { - const unsigned int value = - (flags & FLAGS_CHAR) ? (unsigned char)va_arg(args, unsigned int) - : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(args, unsigned int) - : va_arg(args, unsigned int); - print_integer(output, (printf_unsigned_value_t)value, false, base, precision, - width, flags); - } - } - break; - } -#if PRINTF_SUPPORT_DECIMAL_SPECIFIERS - case 'f': - case 'F': - if (*format == 'F') - flags |= FLAGS_UPPERCASE; - print_floating_point(output, va_arg(args, double), precision, width, flags, - PRINTF_PREFER_DECIMAL); - format++; - break; -#endif -#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - case 'e': - case 'E': - case 'g': - case 'G': - if ((*format == 'g') || (*format == 'G')) - flags |= FLAGS_ADAPT_EXP; - if ((*format == 'E') || (*format == 'G')) - flags |= FLAGS_UPPERCASE; - print_floating_point(output, va_arg(args, double), precision, width, flags, - PRINTF_PREFER_EXPONENTIAL); - format++; - break; -#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - case 'c': { - printf_size_t l = 1U; - // pre padding - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - putchar_via_gadget(output, ' '); - } - } - // char output - putchar_via_gadget(output, (char)va_arg(args, int)); - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - putchar_via_gadget(output, ' '); - } - } - format++; - break; - } - - case 's': { - const char *p = va_arg(args, char *); - if (p == NULL) { - out_rev_(output, ")llun(", 6, width, flags); - } else { - printf_size_t l = - strnlen_s_(p, precision ? precision : PRINTF_MAX_POSSIBLE_BUFFER_SIZE); - // pre padding - if (flags & FLAGS_PRECISION) { - l = (l < precision ? l : precision); - } - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - putchar_via_gadget(output, ' '); - } - } - // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision)) { - putchar_via_gadget(output, *(p++)); - --precision; - } - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - putchar_via_gadget(output, ' '); - } - } - } - format++; - break; - } - - case 'p': { - width = sizeof(void *) * 2U + 2; // 2 hex chars per byte + the "0x" prefix - flags |= FLAGS_ZEROPAD | FLAGS_POINTER; - uintptr_t value = (uintptr_t)va_arg(args, void *); - (value == (uintptr_t)NULL) ? out_rev_(output, ")lin(", 5, width, flags) - : print_integer(output, (printf_unsigned_value_t)value, - false, BASE_HEX, precision, width, flags); - format++; - break; - } - - case '%': - putchar_via_gadget(output, '%'); - format++; - break; - - // Many people prefer to disable support for %n, as it lets the caller - // engineer a write to an arbitrary location, of a value the caller - // effectively controls - which could be a security concern in some cases. -#if PRINTF_SUPPORT_WRITEBACK_SPECIFIER - case 'n': { - if (flags & FLAGS_CHAR) - *(va_arg(args, char *)) = (char)output->pos; - else if (flags & FLAGS_SHORT) - *(va_arg(args, short *)) = (short)output->pos; - else if (flags & FLAGS_LONG) - *(va_arg(args, long *)) = (long)output->pos; -#if PRINTF_SUPPORT_LONG_LONG - else if (flags & FLAGS_LONG_LONG) - *(va_arg(args, long long *)) = (long long int)output->pos; -#endif // PRINTF_SUPPORT_LONG_LONG - else - *(va_arg(args, int *)) = (int)output->pos; - format++; - break; - } -#endif // PRINTF_SUPPORT_WRITEBACK_SPECIFIER - - default: - putchar_via_gadget(output, *format); - format++; - break; - } - } -} - -// internal vsnprintf - used for implementing _all library functions -static int vsnprintf_impl(output_gadget_t *output, const char *format, va_list args) -{ - // Note: The library only calls vsnprintf_impl() with output->pos being 0. However, it is - // possible to call this function with a non-zero pos value for some "remedial printing". - format_string_loop(output, format, args); - - // termination - append_termination_with_gadget(output); - - // return written chars without terminating \0 - return (int)output->pos; -} - -/////////////////////////////////////////////////////////////////////////////// - -// int vprintf_(const char *format, va_list arg) -// { -// output_gadget_t gadget = extern_putchar_gadget(); -// return vsnprintf_impl(&gadget, format, arg); -// } - -int vsnprintf_(char *s, size_t n, const char *format, va_list arg) -{ - output_gadget_t gadget = buffer_gadget(s, n); - return vsnprintf_impl(&gadget, format, arg); -} - -int vsprintf_(char *s, const char *format, va_list arg) -{ - return vsnprintf_(s, PRINTF_MAX_POSSIBLE_BUFFER_SIZE, format, arg); -} - -int vfctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, - va_list arg) -{ - output_gadget_t gadget = function_gadget(out, extra_arg); - return vsnprintf_impl(&gadget, format, arg); -} - -// int printf_(const char *format, ...) -// { -// va_list args; -// va_start(args, format); -// const int ret = vprintf_(format, args); -// va_end(args); -// return ret; -// } - -int sprintf_(char *s, const char *format, ...) -{ - va_list args; - va_start(args, format); - const int ret = vsprintf_(s, format, args); - va_end(args); - return ret; -} - -int snprintf_(char *s, size_t n, const char *format, ...) -{ - va_list args; - va_start(args, format); - const int ret = vsnprintf_(s, n, format, args); - va_end(args); - return ret; -} - -int fctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, ...) -{ - va_list args; - va_start(args, format); - const int ret = vfctprintf(out, extra_arg, format, args); - va_end(args); - return ret; -} diff --git a/ulib/ruxlibc/c/printf.h b/ulib/ruxlibc/c/printf.h deleted file mode 100644 index 45a0750b0..000000000 --- a/ulib/ruxlibc/c/printf.h +++ /dev/null @@ -1,224 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -/** - * @author (c) Eyal Rozenberg - * 2021-2022, Haifa, Palestine/Israel - * @author (c) Marco Paland (info@paland.com) - * 2014-2019, PALANDesign Hannover, Germany - * - * @note Others have made smaller contributions to this file: see the - * contributors page at https://github.com/eyalroz/printf/graphs/contributors - * or ask one of the authors. - * - * @brief Small stand-alone implementation of the printf family of functions - * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with - * a very limited resources. - * - * @note the implementations are thread-safe; re-entrant; use no functions from - * the standard library; and do not dynamically allocate any memory. - * - * @license The MIT License (MIT) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef PRINTF_H_ -#define PRINTF_H_ - -#ifdef __cplusplus -#include -#include -extern "C" { -#else -#include -#include -#endif - -#ifdef __GNUC__ -#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4) -#define ATTR_PRINTF(one_based_format_index, first_arg) \ - __attribute__((format(gnu_printf, (one_based_format_index), (first_arg)))) -#else -#define ATTR_PRINTF(one_based_format_index, first_arg) \ - __attribute__((format(printf, (one_based_format_index), (first_arg)))) -#endif -#define ATTR_VPRINTF(one_based_format_index) ATTR_PRINTF((one_based_format_index), 0) -#else -#define ATTR_PRINTF(one_based_format_index, first_arg) -#define ATTR_VPRINTF(one_based_format_index) -#endif - -#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES -#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES 0 -#endif - -#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD -#define printf_ printf -#define sprintf_ sprintf -#define vsprintf_ vsprintf -#define snprintf_ snprintf -#define vsnprintf_ vsnprintf -#define vprintf_ vprintf -#endif - -// If you want to include this implementation file directly rather than -// link against, this will let you control the functions' visibility, -// e.g. make them static so as not to clash with other objects also -// using them. -#ifndef PRINTF_VISIBILITY -#define PRINTF_VISIBILITY -#endif - -/** - * Prints/send a single character to some opaque output entity - * - * @note This function is not implemented by the library, only declared; you must provide an - * implementation if you wish to use the @ref printf / @ref vprintf function (and possibly - * for linking against the library, if your toolchain does not support discarding unused functions) - * - * @note The output could be as simple as a wrapper for the `write()` system call on a Unix-like - * system, or even libc's @ref putchar , for replicating actual functionality of libc's @ref printf - * function; but on an embedded system it may involve interaction with a special output device, - * like a UART, etc. - * - * @note in libc's @ref putchar, the parameter type is an int; this was intended to support the - * representation of either a proper character or EOF in a variable - but this is really not - * meaningful to pass into @ref putchar and is discouraged today. See further discussion in: - * @link https://stackoverflow.com/q/17452847/1593077 - * - * @param c the single character to print - */ -// PRINTF_VISIBILITY -// void putchar_(char c); - -/** - * An implementation of the C standard's printf/vprintf - * - * @note you must implement a @ref putchar_ function for using this function - it invokes @ref - * putchar_ rather than directly performing any I/O (which insulates it from any dependence on the - * operating system and external libraries). - * - * @param format A string specifying the format of the output, with %-marked specifiers of how to - * interpret additional arguments. - * @param arg Additional arguments to the function, one for each %-specifier in @p format string - * @return The number of characters written into @p s, not counting the terminating null character - */ -///@{ -// PRINTF_VISIBILITY -// int printf_(const char* format, ...) ATTR_PRINTF(1, 2); -// PRINTF_VISIBILITY -// int vprintf_(const char* format, va_list arg) ATTR_VPRINTF(1); -///@} - -/** - * An implementation of the C standard's sprintf/vsprintf - * - * @note For security considerations (the potential for exceeding the buffer bounds), please - * consider using the size-constrained variant, @ref snprintf / @ref vsnprintf , instead. - * - * @param s An array in which to store the formatted string. It must be large enough to fit the - * formatted output! - * @param format A string specifying the format of the output, with %-marked specifiers of how to - * interpret additional arguments. - * @param arg Additional arguments to the function, one for each specifier in @p format - * @return The number of characters written into @p s, not counting the terminating null character - */ -///@{ -PRINTF_VISIBILITY -int sprintf_(char *s, const char *format, ...) ATTR_PRINTF(2, 3); -PRINTF_VISIBILITY -int vsprintf_(char *s, const char *format, va_list arg) ATTR_VPRINTF(2); -///@} - -/** - * An implementation of the C standard's snprintf/vsnprintf - * - * @param s An array in which to store the formatted string. It must be large enough to fit either - * the entire formatted output, or at least @p n characters. Alternatively, it can be NULL, in which - * case nothing will be printed, and only the number of characters which _could_ have been printed - * is tallied and returned. - * @param n The maximum number of characters to write to the array, including a terminating null - * character - * @param format A string specifying the format of the output, with %-marked specifiers of how to - * interpret additional arguments. - * @param arg Additional arguments to the function, one for each specifier in @p format - * @return The number of characters that COULD have been written into @p s, not counting the - * terminating null character. A value equal or larger than @p n indicates truncation. Only when the - * returned value is non-negative and less than @p n, the null-terminated string has been fully and - * successfully printed. - */ -///@{ -PRINTF_VISIBILITY -int snprintf_(char *s, size_t count, const char *format, ...) ATTR_PRINTF(3, 4); -PRINTF_VISIBILITY -int vsnprintf_(char *s, size_t count, const char *format, va_list arg) ATTR_VPRINTF(3); -///@} - -/** - * printf/vprintf with user-specified output function - * - * An alternative to @ref printf_, in which the output function is specified dynamically - * (rather than @ref putchar_ being used) - * - * @param out An output function which takes one character and a type-erased additional parameters - * @param extra_arg The type-erased argument to pass to the output function @p out with each call - * @param format A string specifying the format of the output, with %-marked specifiers of how to - * interpret additional arguments. - * @param arg Additional arguments to the function, one for each specifier in @p format - * @return The number of characters for which the output f unction was invoked, not counting the - * terminating null character - * - */ -PRINTF_VISIBILITY -int fctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, ...) - ATTR_PRINTF(3, 4); -PRINTF_VISIBILITY -int vfctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, - va_list arg) ATTR_VPRINTF(3); - -#ifdef __cplusplus -} // extern "C" -#endif - -#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD -#undef printf_ -#undef sprintf_ -#undef vsprintf_ -#undef snprintf_ -#undef vsnprintf_ -#undef vprintf_ -#else -#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT -#define printf printf_ -#define sprintf sprintf_ -#define vsprintf vsprintf_ -#define snprintf snprintf_ -#define vsnprintf vsnprintf_ -#define vprintf vprintf_ -#endif -#endif - -#endif // PRINTF_H_ diff --git a/ulib/ruxlibc/c/printf_config.h b/ulib/ruxlibc/c/printf_config.h deleted file mode 100644 index 2086f9276..000000000 --- a/ulib/ruxlibc/c/printf_config.h +++ /dev/null @@ -1,23 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#ifndef PRINTF_CONFIG_H -#define PRINTF_CONFIG_H - -#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES 1 - -#ifndef RUX_CONFIG_FP_SIMD - -#define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 0 - -#define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 0 - -#endif // RUX_CONFIG_FP_SIMD - -#endif // PRINTF_CONFIG_H diff --git a/ulib/ruxlibc/c/pthread.c b/ulib/ruxlibc/c/pthread.c deleted file mode 100644 index 363f02d99..000000000 --- a/ulib/ruxlibc/c/pthread.c +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#ifdef RUX_CONFIG_MULTITASK - -#include -#include -#include -#include -#include - -int pthread_setcancelstate(int new, int *old) -{ - unimplemented(); - return 0; -} - -int pthread_setcanceltype(int new, int *old) -{ - unimplemented(); - return 0; -} - -// TODO -void pthread_testcancel(void) -{ - unimplemented(); - return; -} - -// TODO -int pthread_cancel(pthread_t t) -{ - unimplemented(); - return 0; -} - -// TODO -int pthread_setname_np(pthread_t thread, const char *name) -{ - unimplemented(); - return 0; -} - -#define DEFAULT_STACK_SIZE 131072 -#define DEFAULT_GUARD_SIZE 8192 - -// TODO -int pthread_attr_init(pthread_attr_t *a) -{ - *a = (pthread_attr_t){0}; - // __acquire_ptc(); - a->_a_stacksize = DEFAULT_STACK_SIZE; - a->_a_guardsize = DEFAULT_GUARD_SIZE; - // __release_ptc(); - return 0; -} - -int pthread_attr_getstacksize(const pthread_attr_t *restrict a, size_t *restrict size) -{ - *size = a->_a_stacksize; - return 0; -} - -int pthread_attr_setstacksize(pthread_attr_t *a, size_t size) -{ - if (size - PTHREAD_STACK_MIN > SIZE_MAX / 4) - return EINVAL; - a->_a_stackaddr = 0; - a->_a_stacksize = size; - return 0; -} - -#endif // RUX_CONFIG_MULTITASK diff --git a/ulib/ruxlibc/c/pwd.c b/ulib/ruxlibc/c/pwd.c deleted file mode 100644 index 0b0df0e1a..000000000 --- a/ulib/ruxlibc/c/pwd.c +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A - * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. - */ - -#include -#include -#include -#include -#include -#include - -static struct passwd pw__ = { - .pw_name = RUX_DEFAULT_USER, - .pw_passwd = RUX_DEFAULT_PASS, - .pw_uid = RUX_DEFAULT_UID, - .pw_gid = RUX_DEFAULT_GID, - .pw_gecos = RUX_DEFAULT_USER, - .pw_dir = "/", - .pw_shell = "", -}; - -int getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t size, struct passwd **res) -{ - unimplemented(); - return 0; -} - -int getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t size, struct passwd **res) -{ - unimplemented(); - return 0; -} - -struct passwd *getpwnam(const char *name) -{ - struct passwd *pwd; - - if (name && !strcmp(name, pw__.pw_name)) - pwd = &pw__; - else { - pwd = NULL; - errno = ENOENT; - } - - return pwd; -} diff --git a/ulib/ruxlibc/c/resource.c b/ulib/ruxlibc/c/resource.c deleted file mode 100644 index a50b640e2..000000000 --- a/ulib/ruxlibc/c/resource.c +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A - * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. - */ - -#include -#include -#include - -// TODO -int getrusage(int __who, struct rusage *__usage) -{ - unimplemented(); - return 0; -} - -int setpriority(int which, id_t who, int prio) -{ - unimplemented(); - return 0; -} - diff --git a/ulib/ruxlibc/c/sched.c b/ulib/ruxlibc/c/sched.c deleted file mode 100644 index ad24fa270..000000000 --- a/ulib/ruxlibc/c/sched.c +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include - -// TODO -int sched_setaffinity(pid_t __pid, size_t __cpusetsize, const cpu_set_t *__cpuset) -{ - unimplemented(); - return 0; -} - -int sched_yield(void) -{ - unimplemented(); - return 0; -} diff --git a/ulib/ruxlibc/c/select.c b/ulib/ruxlibc/c/select.c deleted file mode 100644 index 555b21739..000000000 --- a/ulib/ruxlibc/c/select.c +++ /dev/null @@ -1,26 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#ifdef RUX_CONFIG_SELECT - -#include -#include -#include -#include -#include - -int pselect(int n, fd_set *restrict rfds, fd_set *restrict wfds, fd_set *restrict efds, - const struct timespec *restrict ts, const sigset_t *restrict mask) -{ - struct timeval tv = {ts->tv_sec, ts->tv_nsec / 1000}; - select(n, rfds, wfds, efds, &tv); - return 0; -} - -#endif // RUX_CONFIG_SELECT diff --git a/ulib/ruxlibc/c/semaphore.c b/ulib/ruxlibc/c/semaphore.c deleted file mode 100644 index 970ea656a..000000000 --- a/ulib/ruxlibc/c/semaphore.c +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include - -// TODO -int sem_destroy(sem_t *sem) -{ - unimplemented(); - return 0; -} - -// TODO -int sem_init(sem_t *sem, int pshared, unsigned int value) -{ - unimplemented(); - return 0; -} - -// TODO -int sem_post(sem_t *sem) -{ - unimplemented(); - return 0; -} - -// TODO -int sem_wait(sem_t *sem) -{ - unimplemented(); - return 0; -} diff --git a/ulib/ruxlibc/c/signal.c b/ulib/ruxlibc/c/signal.c deleted file mode 100644 index 6c94a5da5..000000000 --- a/ulib/ruxlibc/c/signal.c +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A - * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. - */ - -#include -#include -#include -#include - -extern int sigaction_inner(int, const struct sigaction *, struct sigaction *); - -void (*signal(int signum, void (*handler)(int)))(int) -{ - struct sigaction old; - struct sigaction act = { - .sa_handler = handler, .sa_flags = SA_RESTART, /* BSD signal semantics */ - }; - - if (sigaction_inner(signum, &act, &old) < 0) - return SIG_ERR; - - return (old.sa_flags & SA_SIGINFO) ? NULL : old.sa_handler; -} - -int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact) -{ - return sigaction_inner(sig, act, oact); -} - -// TODO -int kill(pid_t __pid, int __sig) -{ - unimplemented(); - return 0; -} - -int sigemptyset(sigset_t *set) -{ - set->__bits[0] = 0; - if (sizeof(long) == 4 || _NSIG > 65) - set->__bits[1] = 0; - if (sizeof(long) == 4 && _NSIG > 65) { - set->__bits[2] = 0; - set->__bits[3] = 0; - } - return 0; -} - -// TODO -int raise(int __sig) -{ - unimplemented(); - return 0; -} - -int sigaddset(sigset_t *set, int sig) -{ - unsigned s = sig - 1; - if (s >= _NSIG - 1 || sig - 32U < 3) { - errno = EINVAL; - return -1; - } - set->__bits[s / 8 / sizeof *set->__bits] |= 1UL << (s & (8 * sizeof *set->__bits - 1)); - return 0; -} - -// TODO -int pthread_sigmask(int __how, const sigset_t *restrict __newmask, sigset_t *restrict __oldmask) -{ - unimplemented(); - return 0; -} - -// TODO -int sigprocmask(int how, const sigset_t *__restrict set, sigset_t *__restrict oldset) -{ - unimplemented(); - return 0; -} - -// TODO -int sigsuspend(const sigset_t *mask) -{ - unimplemented(); - return 0; -} - -#ifdef RUX_CONFIG_MULTITASK -// TODO -int pthread_kill(pthread_t t, int sig) -{ - unimplemented(); - return 0; -} -#endif diff --git a/ulib/ruxlibc/c/socket.c b/ulib/ruxlibc/c/socket.c deleted file mode 100644 index 9b60da7d3..000000000 --- a/ulib/ruxlibc/c/socket.c +++ /dev/null @@ -1,94 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#ifdef RUX_CONFIG_NET - -#include -#include -#include -#include -#include -#include - -int accept4(int fd, struct sockaddr *restrict addr, socklen_t *restrict len, int flg) -{ - if (!flg) - return accept(fd, addr, len); - if (flg & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) { - errno = EINVAL; - return -1; - } - int ret = accept(fd, addr, len); - if (ret < 0) - return ret; - if (flg & SOCK_CLOEXEC) - fcntl(ret, F_SETFD, FD_CLOEXEC); - if (flg & SOCK_NONBLOCK) - fcntl(ret, F_SETFL, O_NONBLOCK); - return ret; -} - -int getsockopt(int fd, int level, int optname, void *restrict optval, socklen_t *restrict optlen) -{ - unimplemented(); - return -1; -} - -int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) -{ - unimplemented("fd: %d, level: %d, optname: %d, optval: %d, optlen: %d", fd, level, optname, - *(int *)optval, optlen); - return 0; -} - -// TODO: remove this function in future work -ssize_t ax_sendmsg(int fd, const struct msghdr *msg, int flags); - -ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) -{ -#if LONG_MAX > INT_MAX - struct msghdr h; - /* Kernels before 2.6.38 set SCM_MAX_FD to 255, allocate enough - * space to support an SCM_RIGHTS ancillary message with 255 fds. - * Kernels since 2.6.38 set SCM_MAX_FD to 253. */ - struct cmsghdr chbuf[CMSG_SPACE(255*sizeof(int))/sizeof(struct cmsghdr)+1], *c; - if (msg) { - h = *msg; - h.__pad1 = h.__pad2 = 0; - msg = &h; - if (h.msg_controllen) { - if (h.msg_controllen > sizeof chbuf) { - errno = ENOMEM; - return -1; - } - memcpy(chbuf, h.msg_control, h.msg_controllen); - h.msg_control = chbuf; - for (c=CMSG_FIRSTHDR(&h); c; c=CMSG_NXTHDR(&h,c)) - c->__pad1 = 0; - } - } -#endif - return ax_sendmsg(fd, msg, flags); -} - -// TODO -ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) -{ - unimplemented(); - return 0; -} - -// TODO -int socketpair(int domain, int type, int protocol, int sv[2]) -{ - unimplemented(); - return 0; -} - -#endif // RUX_CONFIG_NET diff --git a/ulib/ruxlibc/c/stdio.c b/ulib/ruxlibc/c/stdio.c deleted file mode 100644 index 5122b9b85..000000000 --- a/ulib/ruxlibc/c/stdio.c +++ /dev/null @@ -1,469 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include "printf.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// LOCK used by `puts()` -#ifdef RUX_CONFIG_MULTITASK -#include -static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; -#endif - -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -FILE __stdin_FILE = {.fd = 0, .buffer_len = 0}; - -FILE __stdout_FILE = {.fd = 1, .buffer_len = 0}; - -FILE __stderr_FILE = {.fd = 2, .buffer_len = 0}; - -FILE *const stdin = &__stdin_FILE; -FILE *const stdout = &__stdout_FILE; -FILE *const stderr = &__stderr_FILE; - -// Returns: number of chars written, negative for failure -// Warn: buffer_len[f] will not be changed -static int __write_buffer(FILE *f) -{ - int r = 0; - if (f->buffer_len == 0) - return 0; - r = write(f->fd, f->buf, f->buffer_len); - return r; -} - -// Clear buffer_len[f] -static void __clear_buffer(FILE *f) -{ - f->buffer_len = 0; -} - -static int __fflush(FILE *f) -{ - int r = __write_buffer(f); - __clear_buffer(f); - return r >= 0 ? 0 : r; -} - -static int out(FILE *f, const char *s, size_t l) -{ - int ret = 0; - for (size_t i = 0; i < l; i++) { - char c = s[i]; - f->buf[f->buffer_len++] = c; - if (f->buffer_len == FILE_BUF_SIZE || c == '\n') { - int r = __write_buffer(f); - __clear_buffer(f); - if (r < 0) - return r; - if (r < f->buffer_len) - return ret + r; - ret += r; - } - } - return ret; -} - -int getchar(void) -{ - unimplemented(); - return 0; -} - -int fflush(FILE *f) -{ - return __fflush(f); -} - -static inline int do_putc(int c, FILE *f) -{ - char byte = c; - return out(f, &byte, 1); -} - -int fputc(int c, FILE *f) -{ - return do_putc(c, f); -} - -int putc(int c, FILE *f) -{ - return do_putc(c, f); -} - -int putchar(int c) -{ - return do_putc(c, stdout); -} - -int puts(const char *s) -{ -#ifdef RUX_CONFIG_MULTITASK - pthread_mutex_lock(&lock); -#endif - - int r = write(1, (const void *)s, strlen(s)); - char brk[1] = {'\n'}; - write(1, (const void *)brk, 1); - -#ifdef RUX_CONFIG_MULTITASK - pthread_mutex_unlock(&lock); -#endif - - return r; -} - -void perror(const char *msg) -{ - FILE *f = stderr; - char *errstr = strerror(errno); - - if (msg && *msg) { - out(f, msg, strlen(msg)); - out(f, ": ", 2); - } - out(f, errstr, strlen(errstr)); - out(f, "\n", 1); -} - -static void __out_wrapper(char c, void *arg) -{ - out(arg, &c, 1); -} - -int printf(const char *restrict fmt, ...) -{ - int ret; - va_list ap; - va_start(ap, fmt); - ret = vfprintf(stdout, fmt, ap); - va_end(ap); - return ret; -} - -int fprintf(FILE *restrict f, const char *restrict fmt, ...) -{ - int ret; - va_list ap; - va_start(ap, fmt); - ret = vfprintf(f, fmt, ap); - va_end(ap); - return ret; -} - -int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap) -{ - return vfctprintf(__out_wrapper, f, fmt, ap); -} - -// TODO -int sscanf(const char *restrict __s, const char *restrict __format, ...) -{ - unimplemented(); - return 0; -} - -#ifdef RUX_CONFIG_FS - -int __fmodeflags(const char *mode) -{ - int flags; - if (strchr(mode, '+')) - flags = O_RDWR; - else if (*mode == 'r') - flags = O_RDONLY; - else - flags = O_WRONLY; - if (strchr(mode, 'x')) - flags |= O_EXCL; - if (strchr(mode, 'e')) - flags |= O_CLOEXEC; - if (*mode != 'r') - flags |= O_CREAT; - if (*mode == 'w') - flags |= O_TRUNC; - if (*mode == 'a') - flags |= O_APPEND; - return flags; -} - -FILE *fopen(const char *filename, const char *mode) -{ - FILE *f; - int flags; - - if (!strchr("rwa", *mode)) { - errno = EINVAL; - return 0; - } - - f = (FILE *)malloc(sizeof(FILE)); - - flags = __fmodeflags(mode); - // TODO: currently mode is unused in ax_open - int fd = open(filename, flags, 0666); - if (fd < 0) - return NULL; - f->fd = fd; - - return f; -} - -char *fgets(char *restrict s, int n, FILE *restrict f) -{ - if (n == 0) - return NULL; - if (n == 1) { - *s = '\0'; - return s; - } - - int cnt = 0; - while (cnt < n - 1) { - char c; - if (read(f->fd, (void *)&c, 1) > 0) { - if (c != '\n') - s[cnt++] = c; - else{ - s[cnt++] = c; - break; - } - - } else - break; - } - if(cnt==0){ - return NULL; - } - s[cnt] = '\0'; - return s; -} - -size_t fread(void *restrict destv, size_t size, size_t nmemb, FILE *restrict f) -{ - size_t total = size * nmemb; - size_t read_len = 0; - size_t len = 0; - do { - len = read(f->fd, destv + read_len, total - read_len); - if (len < 0) - break; - read_len += len; - } while (len > 0); - return read_len == size * nmemb ? nmemb : read_len / size; -} - -size_t fwrite(const void *restrict src, size_t size, size_t nmemb, FILE *restrict f) -{ - size_t total = size * nmemb; - size_t write_len = 0; - size_t len = 0; - do { - len = write(f->fd, src + write_len, total - write_len); - if (len < 0) - break; - write_len += len; - } while (len > 0); - return write_len == size * nmemb ? nmemb : write_len / size; -} - -int fputs(const char *restrict s, FILE *restrict f) -{ - size_t l = strlen(s); - return (fwrite(s, 1, l, f) == l) - 1; -} - -int fclose(FILE *f) -{ - return close(f->fd); -} - -int fileno(FILE *f) -{ - return f->fd; -} - -int feof(FILE *f) -{ - unimplemented(); - return 0; -} - -// TODO -int fseek(FILE *__stream, long __off, int __whence) -{ - unimplemented(); - return 0; -} - -// TODO -off_t ftello(FILE *__stream) -{ - unimplemented(); - return 0; -} - -// TODO -char *tmpnam(char *buf) -{ - unimplemented(); - return 0; -} - -// TODO -void clearerr(FILE *f) -{ - unimplemented(); -} - -// TODO -int ferror(FILE *f) -{ - unimplemented(); - return 0; -} - - -FILE *freopen(const char *restrict filename, const char *restrict mode, FILE *restrict f) -{ - int fl = __fmodeflags(mode); - FILE *f2; - - fflush(f); - - if (!filename) { - if (fl&O_CLOEXEC) - fcntl(f->fd, F_SETFD, FD_CLOEXEC); - fl &= ~(O_CREAT|O_EXCL|O_CLOEXEC); - if(fcntl(f->fd, F_SETFL, fl) < 0) - goto fail; - } else { - f2 = fopen(filename, mode); - if (!f2) goto fail; - if (f2->fd == f->fd) f2->fd = -1; /* avoid closing in fclose */ - else if (dup3(f2->fd, f->fd, fl&O_CLOEXEC)<0) goto fail2; - fclose(f2); - } - return f; - -fail2: - fclose(f2); -fail: - fclose(f); - return NULL; -} - -// TODO -int fscanf(FILE *restrict f, const char *restrict fmt, ...) -{ - unimplemented(); - return 0; -} - -// TODO -long ftell(FILE *f) -{ - unimplemented(); - return 0; -} - -int getc(FILE *f) -{ - unimplemented(); - return 0; -} - -int remove(const char *path) -{ - if(unlink(path) < 0) { - return rmdir(path); - } - return 0; -} - -// TODO -int setvbuf(FILE *restrict f, char *restrict buf, int type, size_t size) -{ - unimplemented(); - return 0; -} - -// TODO -FILE *tmpfile(void) -{ - unimplemented(); - return NULL; -} - -int ungetc(int c, FILE *f) -{ - unimplemented(); - return 0; -} - -ssize_t getdelim(char **restrict s, size_t *restrict n, int delim, FILE *restrict f) -{ - unimplemented(); - return 0; -} - -ssize_t getline(char **restrict s, size_t *restrict n, FILE *restrict f) -{ - return getdelim(s, n, '\n', f); -} - -int __uflow(FILE *f) -{ - unimplemented(); - return 0; -} - -int getc_unlocked(FILE *f) -{ - unimplemented(); - return 0; -} - -FILE *fdopen(int fd, const char *mode) -{ - FILE *f; - if (!strchr("rwa", *mode)) { - errno = EINVAL; - return 0; - } - - if (!(f=malloc(sizeof *f))) return 0; - f->buffer_len = 0; - - /* Apply close-on-exec flag */ - if (strchr(mode, 'e')) fcntl(fd, F_SETFD, FD_CLOEXEC); - - /* Set append mode on fd if opened for append */ - if (*mode == 'a') { - int flags = fcntl(fd, F_GETFL); - if (!(flags & O_APPEND)) - fcntl(fd, F_SETFL, flags | O_APPEND); - } - f->fd = fd; - return f; -} - -#endif // RUX_CONFIG_FS diff --git a/ulib/ruxlibc/c/stdlib.c b/ulib/ruxlibc/c/stdlib.c deleted file mode 100644 index ea14d5772..000000000 --- a/ulib/ruxlibc/c/stdlib.c +++ /dev/null @@ -1,691 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A - * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -char *program_invocation_short_name = "dummy"; -char *program_invocation_name = "dummy"; - -#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) - -void srandom(unsigned int s) -{ - srand(s); -} - -#ifdef RUX_CONFIG_ALLOC - -void *calloc(size_t m, size_t n) -{ - void *mem = malloc(m * n); - - return memset(mem, 0, n * m); -} - -void *realloc(void *memblock, size_t size) -{ - if (!memblock) - return malloc(size); - - size_t o_size = *(size_t *)(memblock - 8); - - void *mem = malloc(size); - - for (int i = 0; i < (o_size < size ? o_size : size); i++) - ((char *)mem)[i] = ((char *)memblock)[i]; - - free(memblock); - return mem; -} - -#endif // RUX_CONFIG_ALLOC - -long long llabs(long long a) -{ - return a > 0 ? a : -a; -} - -int abs(int a) -{ - return a > 0 ? a : -a; -} - -long long atoll(const char *s) -{ - long long n = 0; - int neg = 0; - while (isspace(*s)) s++; - switch (*s) { - case '-': - neg = 1; - case '+': - s++; - } - /* Compute n as a negative number to avoid overflow on LLONG_MIN */ - while (isdigit(*s)) n = 10 * n - (*s++ - '0'); - return neg ? n : -n; -} - -long strtol(const char *restrict nptr, char **restrict endptr, int base) -{ - const char *s; - unsigned long acc; - unsigned char c; - unsigned long qbase, cutoff; - int neg, any, cutlim; - - s = nptr; - if (base < 0 || base == 1 || base > 36) { - errno = EINVAL; - any = 0; - acc = 0; - goto exit; - } - - do { - c = *s++; - } while (isspace(c)); - if (c == '-') { - neg = 1; - c = *s++; - } else { - neg = 0; - if (c == '+') - c = *s++; - } - if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { - c = s[1]; - s += 2; - base = 16; - } - if (base == 0) - base = c == '0' ? 8 : 10; - - qbase = (unsigned int)base; - cutoff = neg ? (unsigned long)LONG_MAX - (unsigned long)(LONG_MIN + LONG_MAX) : LONG_MAX; - cutlim = cutoff % qbase; - cutoff /= qbase; - for (acc = 0, any = 0;; c = *s++) { - if (!isascii(c)) - break; - if (isdigit(c)) - c -= '0'; - else if (isalpha(c)) - c -= isupper(c) ? 'A' - 10 : 'a' - 10; - else - break; - if (c >= base) - break; - if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) - any = -1; - else { - any = 1; - acc *= qbase; - acc += c; - } - } - - if (any < 0) { - acc = neg ? LONG_MIN : LONG_MAX; - errno = ERANGE; - } else if (neg) - acc = -acc; - -exit: - if (endptr != 0) - *endptr = __DECONST(char *, any ? s - 1 : nptr); - return acc; -} - -unsigned long strtoul(const char *nptr, char **endptr, int base) -{ - const char *s = nptr; - unsigned long acc; - unsigned char c; - unsigned long cutoff; - int neg = 0, any, cutlim; - - if (base < 0 || base == 1 || base > 36) { - errno = EINVAL; - any = 0; - acc = 0; - goto exit; - } - - do { - c = *s++; - } while (isspace(c)); - if (c == '-') { - neg = 1; - c = *s++; - } else if (c == '+') - c = *s++; - if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { - c = s[1]; - s += 2; - base = 16; - } - if (base == 0) - base = c == '0' ? 8 : 10; - cutoff = (unsigned long)ULONG_MAX / (unsigned long)base; - cutlim = (unsigned long)ULONG_MAX % (unsigned long)base; - - for (acc = 0, any = 0;; c = *s++) { - if (!isascii(c)) - break; - if (isdigit(c)) - c -= '0'; - else if (isalpha(c)) - c -= isupper(c) ? 'A' - 10 : 'a' - 10; - else - break; - if (c >= base) - break; - if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) - any = -1; - else { - any = 1; - acc *= base; - acc += c; - } - } - if (any < 0) { - acc = ULONG_MAX; - errno = ERANGE; - } else if (neg) - acc = -acc; -exit: - if (endptr != 0) - *endptr = __DECONST(char *, any ? s - 1 : nptr); - return acc; -} - -long long strtoll(const char *nptr, char **endptr, int base) -{ - const char *s; - unsigned long long acc; - unsigned char c; - unsigned long long qbase, cutoff; - int neg, any, cutlim; - - s = nptr; - if (base < 0 || base == 1 || base > 36) { - errno = EINVAL; - any = 0; - acc = 0; - goto exit; - } - - do { - c = *s++; - } while (isspace(c)); - if (c == '-') { - neg = 1; - c = *s++; - } else { - neg = 0; - if (c == '+') - c = *s++; - } - if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { - c = s[1]; - s += 2; - base = 16; - } - if (base == 0) - base = c == '0' ? 8 : 10; - - qbase = (unsigned int)base; - cutoff = neg ? (unsigned long long)LLONG_MAX - (unsigned long long)(LLONG_MIN + LLONG_MAX) - : LLONG_MAX; - cutlim = cutoff % qbase; - cutoff /= qbase; - for (acc = 0, any = 0;; c = *s++) { - if (!isascii(c)) - break; - if (isdigit(c)) - c -= '0'; - else if (isalpha(c)) - c -= isupper(c) ? 'A' - 10 : 'a' - 10; - else - break; - if (c >= base) - break; - if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) - any = -1; - else { - any = 1; - acc *= qbase; - acc += c; - } - } - - if (any < 0) { - errno = ERANGE; - acc = neg ? LLONG_MIN : LLONG_MAX; - } else if (neg) - acc = -acc; - -exit: - if (endptr != 0) - *endptr = __DECONST(char *, any ? s - 1 : nptr); - return acc; -} - -unsigned long long strtoull(const char *nptr, char **endptr, int base) -{ - const char *s = nptr; - unsigned long long acc; - unsigned char c; - unsigned long long qbase, cutoff; - int neg, any, cutlim; - - if (base < 0 || base == 1 || base > 36) { - errno = EINVAL; - any = 0; - acc = 0; - goto exit; - } - - do { - c = *s++; - } while (isspace(c)); - if (c == '-') { - neg = 1; - c = *s++; - } else { - neg = 0; - if (c == '+') - c = *s++; - } - if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { - c = s[1]; - s += 2; - base = 16; - } - if (base == 0) - base = c == '0' ? 8 : 10; - - qbase = (unsigned int)base; - cutoff = (unsigned long long)ULLONG_MAX / qbase; - cutlim = (unsigned long long)ULLONG_MAX % qbase; - for (acc = 0, any = 0;; c = *s++) { - if (!isascii(c)) - break; - if (isdigit(c)) - c -= '0'; - else if (isalpha(c)) - c -= isupper(c) ? 'A' - 10 : 'a' - 10; - else - break; - if (c >= base) - break; - if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) - any = -1; - else { - any = 1; - acc *= qbase; - acc += c; - } - } - if (any < 0) { - errno = ERANGE; - acc = ULLONG_MAX; - } else if (neg) - acc = -acc; - -exit: - if (endptr != 0) - *endptr = __DECONST(char *, any ? s - 1 : nptr); - return acc; -} - -#ifdef RUX_CONFIG_FP_SIMD - -// TODO: precision may not be enough -long double strtold(const char *restrict s, char **restrict p) -{ - return (long double)strtod(s, p); -} - -#endif // RUX_CONFIG_FP_SIMD - -typedef int (*cmpfun_)(const void *, const void *); - -static int wrapper_cmp(const void *v1, const void *v2, void *cmp) -{ - return ((cmpfun_)cmp)(v1, v2); -} - -typedef int (*cmpfun)(const void *, const void *, void *); - -static inline int a_ctz_32(uint32_t x) -{ - static const char debruijn32[32] = {0, 1, 23, 2, 29, 24, 19, 3, 30, 27, 25, - 11, 20, 8, 4, 13, 31, 22, 28, 18, 26, 10, - 7, 12, 21, 17, 9, 6, 16, 5, 15, 14}; - return debruijn32[(x & -x) * 0x076be629 >> 27]; -} - -static inline int a_ctz_64(uint64_t x) -{ - static const char debruijn64[64] = { - 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, 62, 5, 39, 46, 44, 42, - 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, - 23, 58, 17, 10, 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12}; - if (sizeof(long) < 8) { - uint32_t y = x; - if (!y) { - y = x >> 32; - return 32 + a_ctz_32(y); - } - return a_ctz_32(y); - } - return debruijn64[(x & -x) * 0x022fdd63cc95386dull >> 58]; -} - -static inline int a_ctz_l(unsigned long x) -{ - return (sizeof(long) < 8) ? a_ctz_32(x) : a_ctz_64(x); -} - -#define ntz(x) a_ctz_l((x)) - -static inline int pntz(size_t p[2]) -{ - int r = ntz(p[0] - 1); - if (r != 0 || (r = 8 * sizeof(size_t) + ntz(p[1])) != 8 * sizeof(size_t)) { - return r; - } - return 0; -} - -static void cycle(size_t width, unsigned char *ar[], int n) -{ - unsigned char tmp[256]; - size_t l; - int i; - - if (n < 2) { - return; - } - - ar[n] = tmp; - while (width) { - l = sizeof(tmp) < width ? sizeof(tmp) : width; - memcpy(ar[n], ar[0], l); - for (i = 0; i < n; i++) { - memcpy(ar[i], ar[i + 1], l); - ar[i] += l; - } - width -= l; - } -} - -/* shl() and shr() need n > 0 */ -static inline void shl(size_t p[2], int n) -{ - if (n >= 8 * sizeof(size_t)) { - n -= 8 * sizeof(size_t); - p[1] = p[0]; - p[0] = 0; - } - p[1] <<= n; - p[1] |= p[0] >> (sizeof(size_t) * 8 - n); - p[0] <<= n; -} - -static inline void shr(size_t p[2], int n) -{ - if (n >= 8 * sizeof(size_t)) { - n -= 8 * sizeof(size_t); - p[0] = p[1]; - p[1] = 0; - } - p[0] >>= n; - p[0] |= p[1] << (sizeof(size_t) * 8 - n); - p[1] >>= n; -} - -static void sift(unsigned char *head, size_t width, cmpfun cmp, void *arg, int pshift, size_t lp[]) -{ - unsigned char *rt, *lf; - unsigned char *ar[14 * sizeof(size_t) + 1]; - int i = 1; - - ar[0] = head; - while (pshift > 1) { - rt = head - width; - lf = head - width - lp[pshift - 2]; - - if (cmp(ar[0], lf, arg) >= 0 && cmp(ar[0], rt, arg) >= 0) { - break; - } - if (cmp(lf, rt, arg) >= 0) { - ar[i++] = lf; - head = lf; - pshift -= 1; - } else { - ar[i++] = rt; - head = rt; - pshift -= 2; - } - } - cycle(width, ar, i); -} - -static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, size_t pp[2], - int pshift, int trusty, size_t lp[]) -{ - unsigned char *stepson, *rt, *lf; - size_t p[2]; - unsigned char *ar[14 * sizeof(size_t) + 1]; - int i = 1; - int trail; - - p[0] = pp[0]; - p[1] = pp[1]; - - ar[0] = head; - while (p[0] != 1 || p[1] != 0) { - stepson = head - lp[pshift]; - if (cmp(stepson, ar[0], arg) <= 0) { - break; - } - if (!trusty && pshift > 1) { - rt = head - width; - lf = head - width - lp[pshift - 2]; - if (cmp(rt, stepson, arg) >= 0 || cmp(lf, stepson, arg) >= 0) { - break; - } - } - - ar[i++] = stepson; - head = stepson; - trail = pntz(p); - shr(p, trail); - pshift += trail; - trusty = 0; - } - if (!trusty) { - cycle(width, ar, i); - sift(head, width, cmp, arg, pshift, lp); - } -} - -void __qsort_r(void *base, size_t nel, size_t width, cmpfun cmp, void *arg) -{ - size_t lp[12 * sizeof(size_t)]; - size_t i, size = width * nel; - unsigned char *head, *high; - size_t p[2] = {1, 0}; - int pshift = 1; - int trail; - - if (!size) - return; - - head = base; - high = head + size - width; - - /* Precompute Leonardo numbers, scaled by element width */ - for (lp[0] = lp[1] = width, i = 2; (lp[i] = lp[i - 2] + lp[i - 1] + width) < size; i++) - ; - - while (head < high) { - if ((p[0] & 3) == 3) { - sift(head, width, cmp, arg, pshift, lp); - shr(p, 2); - pshift += 2; - } else { - if (lp[pshift - 1] >= high - head) { - trinkle(head, width, cmp, arg, p, pshift, 0, lp); - } else { - sift(head, width, cmp, arg, pshift, lp); - } - - if (pshift == 1) { - shl(p, 1); - pshift = 0; - } else { - shl(p, pshift - 1); - pshift = 1; - } - } - - p[0] |= 1; - head += width; - } - - trinkle(head, width, cmp, arg, p, pshift, 0, lp); - - while (pshift != 1 || p[0] != 1 || p[1] != 0) { - if (pshift <= 1) { - trail = pntz(p); - shr(p, trail); - pshift += trail; - } else { - shl(p, 2); - pshift -= 2; - p[0] ^= 7; - shr(p, 1); - trinkle(head - lp[pshift] - width, width, cmp, arg, p, pshift + 1, 1, lp); - shl(p, 1); - p[0] |= 1; - trinkle(head - width, width, cmp, arg, p, pshift, 1, lp); - } - head -= width; - } -} - -void qsort(void *base, size_t nel, size_t width, cmpfun_ cmp) -{ - __qsort_r(base, nel, width, wrapper_cmp, (void *)cmp); -} - -// TODO -int mkstemp(char *__template) -{ - unimplemented(); - return 0; -} - -// TODO -int mkostemp(char *__template, int __flags) -{ - unimplemented(); - return 0; -} - -// TODO -int system(const char *cmd) -{ - unimplemented(); - return 0; -} - -// TODO -char *realpath(const char *restrict path, char *restrict resolved_path) -{ - unimplemented(); - return 0; -} - -struct chunk { - size_t psize, csize; - struct chunk *next, *prev; -}; - -void __bin_chunk(struct chunk *) -{ - unimplemented(); - return; -} - -void *aligned_alloc(size_t align, size_t len) -{ - unsigned char *mem, *new; - - if ((align & -align) != align) { - errno = EINVAL; - return 0; - } - - if (len > SIZE_MAX - align) { - errno = ENOMEM; - return 0; - } - - if (align <= SIZE_ALIGN) - return malloc(len); - - if (!(mem = malloc(len + align - 1))) - return 0; - - new = (void *)((uintptr_t)mem + align - 1 & -align); - if (new == mem) - return mem; - - struct chunk *c = MEM_TO_CHUNK(mem); - struct chunk *n = MEM_TO_CHUNK(new); - - if (IS_MMAPPED(c)) { - n->psize = c->psize + (new - mem); - n->csize = c->csize - (new - mem); - return new; - } - - struct chunk *t = NEXT_CHUNK(c); - - n->psize = c->csize = C_INUSE | (new - mem); - n->csize = t->psize -= new - mem; - - __bin_chunk(c); - return new; -} - -// TODO -int posix_memalign(void **res, size_t align, size_t len) -{ - if (align < sizeof(void *)) - return EINVAL; - void *mem = aligned_alloc(align, len); - if (!mem) - return errno; - *res = mem; - return 0; -} diff --git a/ulib/ruxlibc/c/string.c b/ulib/ruxlibc/c/string.c deleted file mode 100644 index d712cc90c..000000000 --- a/ulib/ruxlibc/c/string.c +++ /dev/null @@ -1,473 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include -#include -#include -#include -#include - -int atoi(const char *s) -{ - int n = 0, neg = 0; - while (isspace(*s)) s++; - switch (*s) { - case '-': - neg = 1; - case '+': - s++; - } - /* Compute n as a negative number to avoid overflow on INT_MIN */ - while (isdigit(*s)) n = 10 * n - (*s++ - '0'); - return neg ? n : -n; -} - -void *memchr(const void *src, int c, size_t n) -{ - const unsigned char *s = src; - c = (unsigned char)c; - for (; n && *s != c; s++, n--) - ; - return n ? (void *)s : 0; -} - -void *memset(void *dest, int c, size_t n) -{ - unsigned char *s = dest; - size_t k; - - /* Fill head and tail with minimal branching. Each - * conditional ensures that all the subsequently used - * offsets are well-defined and in the dest region. */ - - if (!n) - return dest; - s[0] = c; - s[n - 1] = c; - if (n <= 2) - return dest; - s[1] = c; - s[2] = c; - s[n - 2] = c; - s[n - 3] = c; - if (n <= 6) - return dest; - s[3] = c; - s[n - 4] = c; - if (n <= 8) - return dest; - - /* Advance pointer to align it at a 4-byte boundary, - * and truncate n to a multiple of 4. The previous code - * already took care of any head/tail that get cut off - * by the alignment. */ - - k = -(uintptr_t)s & 3; - s += k; - n -= k; - n &= -4; - - /* Pure C fallback with no aliasing violations. */ - for (; n; n--, s++) *s = c; - - return dest; -} - -char *strcpy(char *restrict d, const char *restrict s) -{ - for (; (*d = *s); s++, d++) - ; - return d; -} - -char *strncpy(char *restrict d, const char *restrict s, size_t n) -{ - for (; n && (*d = *s); n--, s++, d++) - ; - return d; -} - -char *strcat(char *restrict d, const char *restrict s) -{ - strcpy(d + strlen(d), s); - return d; -} - -char *strncat(char *restrict d, const char *restrict s, size_t n) -{ - char *a = d; - d += strlen(d); - while (n && *s) n--, *d++ = *s++; - *d++ = 0; - return a; -} - -int strcmp(const char *l, const char *r) -{ - for (; *l == *r && *l; l++, r++) - ; - return *(unsigned char *)l - *(unsigned char *)r; -} - -int strncmp(const char *_l, const char *_r, size_t n) -{ - const unsigned char *l = (void *)_l, *r = (void *)_r; - if (!n--) - return 0; - for (; *l && *r && n && *l == *r; l++, r++, n--) - ; - return *l - *r; -} - -int strcoll(const char *l, const char *r) -{ - return strcmp(l, r); -} - -#define BITOP(a, b, op) \ - ((a)[(size_t)(b) / (8 * sizeof *(a))] op(size_t) 1 << ((size_t)(b) % (8 * sizeof *(a)))) -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -size_t strcspn(const char *s1, const char *s2) -{ - const char *a = s1; - size_t byteset[32 / sizeof(size_t)]; - - if (!s2[0] || !s2[1]) { - for (; *s1 != *s2; s1++) return s1 - a; - } - memset(byteset, 0, sizeof byteset); - - for (; *s2 != '\0'; s2++) BITOP(byteset, *(unsigned char *)s2, |=); - for (; *s1 && !(BITOP(byteset, *(unsigned char *)s1, &)); s1++) - ; - - return s1 - a; -} - -size_t strspn(const char *s, const char *c) -{ - const char *a = s; - size_t byteset[32 / sizeof(size_t)] = {0}; - - if (!c[0]) - return 0; - if (!c[1]) { - for (; *s == *c; s++) - ; - return s - a; - } - - for (; *c && BITOP(byteset, *(unsigned char *)c, |=); c++) - ; - for (; *s && BITOP(byteset, *(unsigned char *)s, &); s++) - ; - return s - a; -} - -char *strpbrk(const char *s, const char *b) -{ - s += strcspn(s, b); - return *s ? (char *)s : 0; -} - -char *strchrnul(const char *s, int c) -{ - c = (unsigned char)c; - if (!c) - return (char *)s + strlen(s); - - for (; *s && *(unsigned char *)s != c; s++) - ; - return (char *)s; -} - -char *strchr(const char *s, int c) -{ - while (*s != c && *s != '\0') s++; - - if (*s == c) { - return (char *)s; - } else { - return NULL; - } -} - -char *strrchr(const char *s, int c) -{ - char *isCharFind = NULL; - if (s != NULL) { - do { - if (*s == (char)c) { - isCharFind = (char *)s; - } - } while (*s++); - } - return isCharFind; -} - -int strerror_r(int err, char *buf, size_t buflen) -{ - char *msg = strerror(err); - size_t l = strlen(msg); - if (l >= buflen) { - if (buflen) { - memcpy(buf, msg, buflen - 1); - buf[buflen - 1] = 0; - } - return ERANGE; - } - memcpy(buf, msg, l + 1); - return 0; -} - -void *memcpy(void *restrict dest, const void *restrict src, size_t n) -{ - unsigned char *d = dest; - const unsigned char *s = src; - for (; n; n--) *d++ = *s++; - return dest; -} - -void *memmove(void *dest, const void *src, size_t n) -{ - char *d = dest; - const char *s = src; - - if (d == s) - return d; - if ((uintptr_t)s - (uintptr_t)d - n <= -2 * n) - return memcpy(d, s, n); - - if (d < s) { - for (; n; n--) *d++ = *s++; - } else { - while (n) n--, d[n] = s[n]; - } - - return dest; -} - -int memcmp(const void *vl, const void *vr, size_t n) -{ - const unsigned char *l = vl, *r = vr; - for (; n && *l == *r; n--, l++, r++) - ; - return n ? *l - *r : 0; -} - -int strcasecmp(const char *_l, const char *_r) -{ - const unsigned char *l = (void *)_l, *r = (void *)_r; - for (; *l && *r && (*l == *r || tolower(*l) == tolower(*r)); l++, r++) - ; - return tolower(*l) - tolower(*r); -} - -int strncasecmp(const char *_l, const char *_r, size_t n) -{ - const unsigned char *l = (void *)_l, *r = (void *)_r; - if (!n--) - return 0; - for (; *l && *r && n && (*l == *r || tolower(*l) == tolower(*r)); l++, r++, n--) - ; - return tolower(*l) - tolower(*r); -} - -// `strstr` helper function -static char *twobyte_strstr(const unsigned char *h, const unsigned char *n) -{ - uint16_t nw = n[0] << 8 | n[1], hw = h[0] << 8 | h[1]; - for (h++; *h && hw != nw; hw = hw << 8 | *++h) - ; - return *h ? (char *)h - 1 : 0; -} - -// `strstr` helper function -static char *threebyte_strstr(const unsigned char *h, const unsigned char *n) -{ - uint32_t nw = (uint32_t)n[0] << 24 | n[1] << 16 | n[2] << 8; - uint32_t hw = (uint32_t)h[0] << 24 | h[1] << 16 | h[2] << 8; - for (h += 2; *h && hw != nw; hw = (hw | *++h) << 8) - ; - return *h ? (char *)h - 2 : 0; -} - -// `strstr` helper function -static char *fourbyte_strstr(const unsigned char *h, const unsigned char *n) -{ - uint32_t nw = (uint32_t)n[0] << 24 | n[1] << 16 | n[2] << 8 | n[3]; - uint32_t hw = (uint32_t)h[0] << 24 | h[1] << 16 | h[2] << 8 | h[3]; - for (h += 3; *h && hw != nw; hw = hw << 8 | *++h) - ; - return *h ? (char *)h - 3 : 0; -} - -// `strstr` helper function -static char *twoway_strstr(const unsigned char *h, const unsigned char *n) -{ - const unsigned char *z; - size_t l, ip, jp, k, p, ms, p0, mem, mem0; - size_t byteset[32 / sizeof(size_t)] = {0}; - size_t shift[256]; - - /* Computing length of needle and fill shift table */ - for (l = 0; n[l] && h[l]; l++) BITOP(byteset, n[l], |=), shift[n[l]] = l + 1; - if (n[l]) - return 0; /* hit the end of h */ - - /* Compute maximal suffix */ - ip = -1; - jp = 0; - k = p = 1; - while (jp + k < l) { - if (n[ip + k] == n[jp + k]) { - if (k == p) { - jp += p; - k = 1; - } else - k++; - } else if (n[ip + k] > n[jp + k]) { - jp += k; - k = 1; - p = jp - ip; - } else { - ip = jp++; - k = p = 1; - } - } - ms = ip; - p0 = p; - - /* And with the opposite comparison */ - ip = -1; - jp = 0; - k = p = 1; - while (jp + k < l) { - if (n[ip + k] == n[jp + k]) { - if (k == p) { - jp += p; - k = 1; - } else - k++; - } else if (n[ip + k] < n[jp + k]) { - jp += k; - k = 1; - p = jp - ip; - } else { - ip = jp++; - k = p = 1; - } - } - if (ip + 1 > ms + 1) - ms = ip; - else - p = p0; - - /* Periodic needle? */ - if (memcmp(n, n + p, ms + 1)) { - mem0 = 0; - p = MAX(ms, l - ms - 1) + 1; - } else - mem0 = l - p; - mem = 0; - - /* Initialize incremental end-of-haystack pointer */ - z = h; - - /* Search loop */ - for (;;) { - /* Update incremental end-of-haystack pointer */ - if (z - h < l) { - /* Fast estimate for MAX(l,63) */ - size_t grow = l | 63; - const unsigned char *z2 = memchr(z, 0, grow); - if (z2) { - z = z2; - if (z - h < l) - return 0; - } else - z += grow; - } - - /* Check last byte first; advance by shift on mismatch */ - if (BITOP(byteset, h[l - 1], &)) { - k = l - shift[h[l - 1]]; - if (k) { - if (k < mem) - k = mem; - h += k; - mem = 0; - continue; - } - } else { - h += l; - mem = 0; - continue; - } - - /* Compare right half */ - for (k = MAX(ms + 1, mem); n[k] && n[k] == h[k]; k++) - ; - if (n[k]) { - h += k - ms; - mem = 0; - continue; - } - /* Compare left half */ - for (k = ms + 1; k > mem && n[k - 1] == h[k - 1]; k--) - ; - if (k <= mem) - return (char *)h; - h += p; - mem = mem0; - } -} - -char *strstr(const char *h, const char *n) -{ - /* Return immediately on empty needle */ - if (!n[0]) - return (char *)h; - - /* Use faster algorithms for short needles */ - h = strchr(h, *n); - if (!h || !n[1]) - return (char *)h; - if (!h[1]) - return 0; - if (!n[2]) - return twobyte_strstr((void *)h, (void *)n); - if (!h[2]) - return 0; - if (!n[3]) - return threebyte_strstr((void *)h, (void *)n); - if (!h[3]) - return 0; - if (!n[4]) - return fourbyte_strstr((void *)h, (void *)n); - - return twoway_strstr((void *)h, (void *)n); -} - -#ifdef RUX_CONFIG_ALLOC - -#include -char *strdup(const char *s) -{ - size_t l = strlen(s); - char *d = malloc(l + 1); - if (!d) - return NULL; - return memcpy(d, s, l + 1); -} - -#endif // RUX_CONFIG_ALLOC diff --git a/ulib/ruxlibc/c/syslog.c b/ulib/ruxlibc/c/syslog.c deleted file mode 100644 index d47afadeb..000000000 --- a/ulib/ruxlibc/c/syslog.c +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include - -// TODO -void syslog(int __pri, const char *__fmt, ...) -{ - unimplemented(); - return; -} - -// TODO -void openlog(const char *__ident, int __option, int __facility) -{ - unimplemented(); - return; -} diff --git a/ulib/ruxlibc/c/time.c b/ulib/ruxlibc/c/time.c deleted file mode 100644 index b3e05a13f..000000000 --- a/ulib/ruxlibc/c/time.c +++ /dev/null @@ -1,216 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A - * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. - */ - -#include -#include -#include -#include -#include -#include - -long timezone = 0; -const char __utc[] = "UTC"; - -const int SEC_PER_MIN = 60; -const int SEC_PER_HOUR = 3600; -const int MIN_PER_HOUR = 60; -const int HOUR_PER_DAY = 24; - -/* 2000-03-01 (mod 400 year, immediately after feb29 */ -#define LEAPOCH (946684800LL + 86400 * (31 + 29)) -#define DAYS_PER_400Y (365 * 400 + 97) -#define DAYS_PER_100Y (365 * 100 + 24) -#define DAYS_PER_4Y (365 * 4 + 1) - -int __secs_to_tm(long long t, struct tm *tm) -{ - long long days, secs, years; - int remdays, remsecs, remyears; - int qc_cycles, c_cycles, q_cycles; - int months; - int wday, yday, leap; - static const char days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29}; - - /* Reject time_t values whose year would overflow int */ - if (t < INT_MIN * 31622400LL || t > INT_MAX * 31622400LL) - return -1; - - secs = t - LEAPOCH; - days = secs / 86400; - remsecs = secs % 86400; - if (remsecs < 0) { - remsecs += 86400; - days--; - } - - wday = (3 + days) % 7; - if (wday < 0) - wday += 7; - - qc_cycles = days / DAYS_PER_400Y; - remdays = days % DAYS_PER_400Y; - if (remdays < 0) { - remdays += DAYS_PER_400Y; - qc_cycles--; - } - - c_cycles = remdays / DAYS_PER_100Y; - if (c_cycles == 4) - c_cycles--; - remdays -= c_cycles * DAYS_PER_100Y; - - q_cycles = remdays / DAYS_PER_4Y; - if (q_cycles == 25) - q_cycles--; - remdays -= q_cycles * DAYS_PER_4Y; - - remyears = remdays / 365; - if (remyears == 4) - remyears--; - remdays -= remyears * 365; - - leap = !remyears && (q_cycles || !c_cycles); - yday = remdays + 31 + 28 + leap; - if (yday >= 365 + leap) - yday -= 365 + leap; - - years = remyears + 4 * q_cycles + 100 * c_cycles + 400LL * qc_cycles; - - for (months = 0; days_in_month[months] <= remdays; months++) remdays -= days_in_month[months]; - - if (months >= 10) { - months -= 12; - years++; - } - - if (years + 100 > INT_MAX || years + 100 < INT_MIN) - return -1; - - tm->tm_year = years + 100; - tm->tm_mon = months + 2; - tm->tm_mday = remdays + 1; - tm->tm_wday = wday; - tm->tm_yday = yday; - - tm->tm_hour = remsecs / 3600; - tm->tm_min = remsecs / 60 % 60; - tm->tm_sec = remsecs % 60; - - return 0; -} - -struct tm *gmtime_r(const time_t *restrict t, struct tm *restrict tm) -{ - if (__secs_to_tm(*t, tm) < 0) { - errno = EOVERFLOW; - return 0; - } - tm->tm_isdst = 0; - tm->__tm_gmtoff = 0; - tm->__tm_zone = __utc; - return tm; -} - -struct tm *gmtime(const time_t *timer) -{ - static struct tm tm; - return gmtime_r(timer, &tm); -} - -struct tm *localtime_r(const time_t *restrict t, struct tm *restrict tm) -{ - if (*t < INT_MIN * 31622400LL || *t > INT_MAX * 31622400LL) { - errno = EOVERFLOW; - return 0; - } - - if (__secs_to_tm(*t, tm) < 0) { - errno = EOVERFLOW; - return 0; - } - - tm->tm_isdst = 0; - tm->__tm_gmtoff = 0; - tm->__tm_zone = __utc; - - return tm; -} - -struct tm *localtime(const time_t *timep) -{ - static struct tm tm; - return localtime_r(timep, &tm); -} - -time_t time(time_t *t) -{ - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - time_t ret = ts.tv_sec; - if (t) - *t = ret; - return ret; -} - -int gettimeofday(struct timeval *tv, struct timezone *tz) -{ - struct timespec ts; - if (!tv) - return 0; - clock_gettime(CLOCK_REALTIME, &ts); - tv->tv_sec = ts.tv_sec; - tv->tv_usec = (int)ts.tv_nsec / 1000; - return 0; -} - -int settimeofday(const struct timeval *tv, const struct timezone *_tz) -{ - if (!tv) - return 0; - if (tv->tv_usec >= 1000000ULL) - return -EINVAL; - return clock_settime(CLOCK_REALTIME, - &((struct timespec){.tv_sec = tv->tv_sec, .tv_nsec = tv->tv_usec * 1000})); -} - -// TODO: -int utimes(const char *filename, const struct timeval times[2]) -{ - unimplemented(); - return 0; -} - -// TODO -void tzset() -{ - unimplemented(); - return; -} - -// TODO -char *ctime_r(const time_t *t, char *buf) -{ - unimplemented(); - return NULL; -} - -// TODO -clock_t clock(void) -{ - unimplemented(); - return 0; -} - -#ifdef RUX_CONFIG_FP_SIMD -double difftime(time_t t1, time_t t0) -{ - return t1 - t0; -} -#endif diff --git a/ulib/ruxlibc/c/uio.c b/ulib/ruxlibc/c/uio.c deleted file mode 100644 index a1e597b3c..000000000 --- a/ulib/ruxlibc/c/uio.c +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include - -// TODO -ssize_t readv(int fd, const struct iovec *iov, int iovcnt) -{ - unimplemented(); - return 0; -} - -// TODO -ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset) -{ - unimplemented(); - return 0; -} - -// TODO -ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset) -{ - unimplemented(); - return 0; -} diff --git a/ulib/ruxlibc/c/unistd.c b/ulib/ruxlibc/c/unistd.c deleted file mode 100644 index a559144c9..000000000 --- a/ulib/ruxlibc/c/unistd.c +++ /dev/null @@ -1,236 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A - * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -// TODO: -pid_t getppid(void) -{ - unimplemented(); - return 0; -} - -// TODO: -uid_t geteuid(void) -{ - unimplemented(); - return 0; -} - -// TODO -uid_t getuid(void) -{ - unimplemented(); - return 0; -} - -// TODO -int setuid(uid_t __uid) -{ - unimplemented(); - return 0; -} - -// TODO -pid_t setsid(void) -{ - unimplemented(); - return 0; -} - -// TODO -int setgid(gid_t gid) -{ - unimplemented(); - return 0; -} - -// TODO -int isatty(int fd) -{ - unimplemented(); - return 0; -} - -// TODO -int getpagesize(void) -{ - unimplemented(); - return 0; -} - -// TODO -ssize_t pread(int fd, void *buf, size_t count, off_t offset) -{ - unimplemented(); - return 0; -} - -// TODO -ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) -{ - unimplemented(); - return 0; -} - -// TODO -int gethostname(char *name, size_t len) -{ - size_t i; - struct utsname uts; - if (uname(&uts)) - return -1; - if (len > sizeof uts.nodename) - len = sizeof uts.nodename; - for (i = 0; i < len && (name[i] = uts.nodename[i]); i++) - ; - if (i && i == len) - name[i - 1] = 0; - return 0; -} - -// TODO -int chown(const char *path, uid_t owner, gid_t group) -{ - unimplemented(); - return 0; -} - -unsigned int sleep(unsigned int seconds) -{ - struct timespec ts; - - ts.tv_sec = seconds; - ts.tv_nsec = 0; - if (nanosleep(&ts, &ts)) - return ts.tv_sec; - - return 0; -} - -int usleep(unsigned useconds) -{ - struct timespec tv = {.tv_sec = useconds / 1000000, .tv_nsec = (useconds % 1000000) * 1000}; - return nanosleep(&tv, &tv); -} - -#ifdef RUX_CONFIG_FS - -// TODO: -int access(const char *pathname, int mode) -{ - unimplemented(); - return 0; -} - -// TODO: -ssize_t readlink(const char *path, char *buf, size_t bufsiz) -{ - unimplemented(); - return 0; -} - -// TODO: -int fsync(int fd) -{ - unimplemented(); - return 0; -} - -// TODO -int fdatasync(int __fildes) -{ - unimplemented(); - return 0; -} - -// TODO: -int fchown(int fd, uid_t owner, gid_t group) -{ - unimplemented("owner: %x group: %x", owner, group); - return 0; -} - -// TODO: -int ftruncate(int fd, off_t length) -{ - unimplemented(); - return 0; -} - -// TODO -int chdir(const char *__path) -{ - unimplemented(); - return 0; -} - -// TODO -int truncate(const char *path, off_t length) -{ - unimplemented(); - return 0; -} - -#endif // RUX_CONFIG_FS - -#ifdef RUX_CONFIG_PIPE - -int pipe2(int fd[2], int flag) -{ - if (!flag) - return pipe(fd); - if (flag & ~(O_CLOEXEC | O_NONBLOCK)) - return -EINVAL; - - int res = pipe(fd); - if (res != 0) - return res; - - if (flag & O_CLOEXEC) { - fcntl(fd[0], F_SETFD, FD_CLOEXEC); - fcntl(fd[1], F_SETFD, FD_CLOEXEC); - } - if (flag & O_NONBLOCK) { - fcntl(fd[0], F_SETFL, O_NONBLOCK); - fcntl(fd[1], F_SETFL, O_NONBLOCK); - } - - return 0; -} - -#endif // RUX_CONFIG_PIPE - -// TODO -_Noreturn void _exit(int status) -{ - exit(status); -} - -// TODO -int execve(const char *__path, char *const *__argv, char *const *__envp) -{ - unimplemented(); - return 0; -} - -// TODO -pid_t fork(void) -{ - unimplemented(); - return -1; -} diff --git a/ulib/ruxlibc/c/wait.c b/ulib/ruxlibc/c/wait.c deleted file mode 100644 index 90add3e59..000000000 --- a/ulib/ruxlibc/c/wait.c +++ /dev/null @@ -1,26 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include -#include - -// TODO -pid_t waitpid(pid_t pid, int *status, int options) -{ - unimplemented(); - return 0; -} - -// TODO -pid_t wait3(int *status, int _options, struct rusage *usage) -{ - unimplemented(); - return 0; -} diff --git a/ulib/ruxlibc/include/byteswap.h b/ulib/ruxlibc/include/byteswap.h deleted file mode 100644 index 2c2f44818..000000000 --- a/ulib/ruxlibc/include/byteswap.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _BYTESWAP_H -#define _BYTESWAP_H - -#include -#include - -#endif diff --git a/ulib/ruxlibc/include/crypt.h b/ulib/ruxlibc/include/crypt.h deleted file mode 100644 index d67e14b21..000000000 --- a/ulib/ruxlibc/include/crypt.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef _CRYPT_H -#define _CRYPT_H - -#endif diff --git a/ulib/ruxlibc/include/sys/random.h b/ulib/ruxlibc/include/sys/random.h deleted file mode 100644 index fa2b72542..000000000 --- a/ulib/ruxlibc/include/sys/random.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _SYS_RANDOM_H -#define _SYS_RANDOM_H -#ifdef __cplusplus -extern "C" { -#endif - -#define __NEED_size_t -#define __NEED_ssize_t - -#define GRND_NONBLOCK 0x0001 -#define GRND_RANDOM 0x0002 -#define GRND_INSECURE 0x0004 - -ssize_t getrandom(void *, size_t, unsigned); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/ulib/ruxlibc/include/sys/statfs.h b/ulib/ruxlibc/include/sys/statfs.h deleted file mode 100644 index d6936b750..000000000 --- a/ulib/ruxlibc/include/sys/statfs.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _SYS_STATFS_H -#define _SYS_STATFS_H - -#include -#include - -typedef struct __fsid_t { - int __val[2]; -} fsid_t; - -struct statfs { - unsigned long f_type, f_bsize; - fsblkcnt_t f_blocks, f_bfree, f_bavail; - fsfilcnt_t f_files, f_ffree; - fsid_t f_fsid; - unsigned long f_namelen, f_frsize, f_flags, f_spare[4]; -}; - -int statfs(const char *, struct statfs *); -int fstatfs(int, struct statfs *); - -#endif diff --git a/ulib/ruxlibc/include/sys/syscall.h b/ulib/ruxlibc/include/sys/syscall.h deleted file mode 100644 index f9618c84d..000000000 --- a/ulib/ruxlibc/include/sys/syscall.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _SYS_SYSCALL_H -#define _SYS_SYSCALL_H - -#include -#include - -#endif diff --git a/ulib/ruxlibc/include/sys/vfs.h b/ulib/ruxlibc/include/sys/vfs.h deleted file mode 100644 index 1d6d84885..000000000 --- a/ulib/ruxlibc/include/sys/vfs.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef _SYS_VFS_H -#define _SYS_VFS_H -#include -#endif diff --git a/ulib/ruxlibc/src/env.rs b/ulib/ruxlibc/src/env.rs deleted file mode 100644 index eb0ca4668..000000000 --- a/ulib/ruxlibc/src/env.rs +++ /dev/null @@ -1,140 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ -use core::ffi::{c_char, c_int, c_void}; -use ruxos_posix_api::{environ, environ_iter, RUX_ENVIRON}; - -use crate::malloc::{free, malloc}; -use crate::string::strlen; -unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> { - for (i, mut item) in environ_iter().enumerate() { - let mut search = search; - loop { - let end_of_query = *search == 0 || *search == b'=' as c_char; - assert_ne!(*item, 0, "environ has an item without value"); - if *item == b'=' as c_char || end_of_query { - if *item == b'=' as c_char && end_of_query { - // Both keys env here - return Some((i, item.add(1))); - } else { - break; - } - } - - if *item != *search { - break; - } - - item = item.add(1); - search = search.add(1); - } - } - None -} - -unsafe fn put_new_env(insert: *mut c_char) { - // XXX: Another problem is that `environ` can be set to any pointer, which means there is a - // chance of a memory leak. But we can check if it was the same as before, like musl does. - if environ == RUX_ENVIRON.as_mut_ptr() { - *RUX_ENVIRON.last_mut().unwrap() = insert; - RUX_ENVIRON.push(core::ptr::null_mut()); - // Likely a no-op but is needed due to Stacked Borrows. - environ = RUX_ENVIRON.as_mut_ptr(); - } else { - RUX_ENVIRON.clear(); - RUX_ENVIRON.extend(environ_iter()); - RUX_ENVIRON.push(insert); - RUX_ENVIRON.push(core::ptr::null_mut()); - environ = RUX_ENVIRON.as_mut_ptr(); - } -} - -unsafe fn copy_kv( - existing: *mut c_char, - key: *const c_char, - value: *const c_char, - key_len: usize, - value_len: usize, -) { - core::ptr::copy_nonoverlapping(key, existing, key_len); - core::ptr::write(existing.add(key_len), b'=' as c_char); - core::ptr::copy_nonoverlapping(value, existing.add(key_len + 1), value_len); - core::ptr::write(existing.add(key_len + 1 + value_len), 0); -} - -/// set an environ variable -#[no_mangle] -pub unsafe extern "C" fn setenv( - key: *const c_char, - value: *const c_char, - overwrite: c_int, -) -> c_int { - let key_len = strlen(key); - let value_len = strlen(value); - if let Some((i, existing)) = find_env(key) { - if overwrite == 0 { - return 0; - } - - let existing_len = strlen(existing); - if existing_len >= value_len { - // Reuse existing element's allocation - core::ptr::copy_nonoverlapping(value, existing, value_len); - core::ptr::write(existing.add(value_len), 0); - } else { - // Reuse environ slot, but allocate a new pointer. - let ptr = malloc(key_len + 1 + value_len + 1) as *mut c_char; - copy_kv(ptr, key, value, key_len, value_len); - environ.add(i).write(ptr); - } - } else { - // Expand environ and allocate a new pointer. - let ptr = malloc(key_len + 1 + value_len + 1) as *mut c_char; - copy_kv(ptr, key, value, key_len, value_len); - put_new_env(ptr); - } - 0 -} - -/// unset an environ variable -#[no_mangle] -pub unsafe extern "C" fn unsetenv(key: *const c_char) -> c_int { - if let Some((i, _)) = find_env(key) { - if environ == RUX_ENVIRON.as_mut_ptr() { - // No need to worry about updating the pointer, this does not - // reallocate in any way. And the final null is already shifted back. - let rm = RUX_ENVIRON.remove(i); - free(rm as *mut c_void); - // My UB paranoia. - environ = RUX_ENVIRON.as_mut_ptr(); - } else { - let len = RUX_ENVIRON.len(); - for _ in 0..len { - let rm = RUX_ENVIRON.pop().unwrap(); - free(rm as *mut c_void); - } - RUX_ENVIRON.extend( - environ_iter() - .enumerate() - .filter(|&(j, _)| j != i) - .map(|(_, v)| v), - ); - RUX_ENVIRON.push(core::ptr::null_mut()); - environ = RUX_ENVIRON.as_mut_ptr(); - } - } - 0 -} - -/// get the corresponding environ variable -#[no_mangle] -pub unsafe extern "C" fn getenv(name: *const c_char) -> *mut c_char { - find_env(name) - .map(|val| val.1) - .unwrap_or(core::ptr::null_mut()) -} diff --git a/ulib/ruxlibc/src/errno.rs b/ulib/ruxlibc/src/errno.rs deleted file mode 100644 index 6bfd13677..000000000 --- a/ulib/ruxlibc/src/errno.rs +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use axerrno::LinuxError; -use core::ffi::{c_char, c_int}; - -/// The global errno variable. -#[cfg_attr(feature = "tls", thread_local)] -#[no_mangle] -#[allow(non_upper_case_globals)] -pub static mut errno: c_int = 0; - -pub fn set_errno(code: i32) { - unsafe { - errno = code; - } -} - -/// Returns a pointer to the global errno variable. -#[no_mangle] -pub unsafe extern "C" fn __errno_location() -> *mut c_int { - &mut errno -} - -/// Returns a pointer to the string representation of the given error code. -#[no_mangle] -pub unsafe extern "C" fn strerror(e: c_int) -> *mut c_char { - #[allow(non_upper_case_globals)] - static mut strerror_buf: [u8; 256] = [0; 256]; // TODO: thread safe - - let err_str = if e == 0 { - "Success" - } else { - LinuxError::try_from(e) - .map(|e| e.as_str()) - .unwrap_or("Unknown error") - }; - unsafe { - strerror_buf[..err_str.len()].copy_from_slice(err_str.as_bytes()); - strerror_buf.as_mut_ptr() as *mut c_char - } -} diff --git a/ulib/ruxlibc/src/fd_ops.rs b/ulib/ruxlibc/src/fd_ops.rs deleted file mode 100644 index 21518e5e6..000000000 --- a/ulib/ruxlibc/src/fd_ops.rs +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use crate::{ctypes, utils::e}; -use axerrno::LinuxError; -use core::ffi::c_int; -use ruxos_posix_api::{sys_close, sys_dup, sys_dup2, sys_fcntl}; - -/// Close a file by `fd`. -#[no_mangle] -pub unsafe extern "C" fn close(fd: c_int) -> c_int { - e(sys_close(fd)) -} - -/// Duplicate a file descriptor. -#[no_mangle] -pub unsafe extern "C" fn dup(old_fd: c_int) -> c_int { - e(sys_dup(old_fd)) -} - -/// Duplicate a file descriptor, use file descriptor specified in `new_fd`. -#[no_mangle] -pub unsafe extern "C" fn dup2(old_fd: c_int, new_fd: c_int) -> c_int { - e(sys_dup2(old_fd, new_fd)) -} - -/// Duplicate a file descriptor, the caller can force the close-on-exec flag to -/// be set for the new file descriptor by specifying `O_CLOEXEC` in flags. -/// -/// If oldfd equals newfd, then `dup3()` fails with the error `EINVAL`. -#[no_mangle] -pub unsafe extern "C" fn dup3(old_fd: c_int, new_fd: c_int, flags: c_int) -> c_int { - if old_fd == new_fd { - return e((LinuxError::EINVAL as c_int).wrapping_neg()); - } - let r = e(sys_dup2(old_fd, new_fd)); - if r < 0 { - r - } else { - if flags as u32 & ctypes::O_CLOEXEC != 0 { - e(sys_fcntl( - new_fd, - ctypes::F_SETFD as c_int, - ctypes::FD_CLOEXEC as usize, - )); - } - new_fd - } -} - -/// Manipulate file descriptor. -/// -/// TODO: `SET/GET` command is ignored -#[no_mangle] -pub unsafe extern "C" fn ax_fcntl(fd: c_int, cmd: c_int, arg: usize) -> c_int { - e(sys_fcntl(fd, cmd, arg)) -} diff --git a/ulib/ruxlibc/src/fs.rs b/ulib/ruxlibc/src/fs.rs deleted file mode 100644 index a396e4ae4..000000000 --- a/ulib/ruxlibc/src/fs.rs +++ /dev/null @@ -1,109 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use core::ffi::{c_char, c_int}; - -use ruxos_posix_api::{ - sys_fstat, sys_getcwd, sys_lseek, sys_lstat, sys_mkdir, sys_open, sys_rename, sys_rmdir, - sys_stat, sys_unlink, -}; - -use crate::{ctypes, utils::e}; - -/// Open a file by `filename` and insert it into the file descriptor table. -/// -/// Return its index in the file table (`fd`). Return `EMFILE` if it already -/// has the maximum number of files open. -#[no_mangle] -pub unsafe extern "C" fn ax_open( - filename: *const c_char, - flags: c_int, - mode: ctypes::mode_t, -) -> c_int { - e(sys_open(filename, flags, mode)) -} - -/// Set the position of the file indicated by `fd`. -/// -/// Return its position after seek. -#[no_mangle] -pub unsafe extern "C" fn lseek(fd: c_int, offset: ctypes::off_t, whence: c_int) -> ctypes::off_t { - e(sys_lseek(fd, offset, whence) as _) as _ -} - -/// Get the file metadata by `path` and write into `buf`. -/// -/// Return 0 if success. -#[no_mangle] -pub unsafe extern "C" fn stat(path: *const c_char, buf: *mut ctypes::stat) -> c_int { - e(sys_stat(path, buf as _)) -} - -/// Get file metadata by `fd` and write into `buf`. -/// -/// Return 0 if success. -#[no_mangle] -pub unsafe extern "C" fn fstat(fd: c_int, buf: *mut ctypes::stat) -> c_int { - e(sys_fstat(fd, buf as *mut core::ffi::c_void)) -} - -/// Get the metadata of the symbolic link and write into `buf`. -/// -/// Return 0 if success. -#[no_mangle] -pub unsafe extern "C" fn lstat(path: *const c_char, buf: *mut ctypes::stat) -> c_int { - e(sys_lstat(path, buf) as _) -} - -/// Get the path of the current directory. -#[no_mangle] -pub unsafe extern "C" fn getcwd(buf: *mut c_char, size: usize) -> *mut c_char { - if buf.is_null() && size != 0 { - crate::errno::set_errno(axerrno::LinuxError::EINVAL as _); - return core::ptr::null_mut() as *mut c_char; - } - let e = sys_getcwd(buf, size); - if e < 0 { - return core::ptr::null_mut() as *mut c_char; - } - if e == 0 || buf.read() != '/' as _ { - crate::errno::set_errno(axerrno::LinuxError::ENOENT as _); - return core::ptr::null_mut() as *mut c_char; - } - buf -} - -/// Rename `old` to `new` -/// If new exists, it is first removed. -/// -/// Return 0 if the operation succeeds, otherwise return -1. -#[no_mangle] -pub unsafe extern "C" fn rename(old: *const c_char, new: *const c_char) -> c_int { - e(sys_rename(old, new)) -} - -/// Remove a directory, which must be empty -/// -/// Return 0 if the operation succeeds, otherwise return -1. -#[no_mangle] -pub unsafe extern "C" fn rmdir(pathname: *const c_char) -> c_int { - e(sys_rmdir(pathname)) -} - -/// Removes a file from the filesystem. -#[no_mangle] -pub unsafe extern "C" fn unlink(pathname: *const c_char) -> c_int { - e(sys_unlink(pathname)) -} - -/// Creates a new directory -#[no_mangle] -pub unsafe extern "C" fn mkdir(pathname: *const c_char, mode: ctypes::mode_t) -> c_int { - e(sys_mkdir(pathname, mode)) -} diff --git a/ulib/ruxlibc/src/io.rs b/ulib/ruxlibc/src/io.rs deleted file mode 100644 index 2c7450195..000000000 --- a/ulib/ruxlibc/src/io.rs +++ /dev/null @@ -1,54 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use core::ffi::{c_int, c_void}; -#[cfg(feature = "fd")] -use ruxos_posix_api::sys_ioctl; - -#[cfg(not(test))] -use ruxos_posix_api::sys_write; -use ruxos_posix_api::{sys_read, sys_writev}; - -use crate::{ctypes, utils::e}; - -/// Read data from the file indicated by `fd`. -/// -/// Return the read size if success. -#[no_mangle] -pub unsafe extern "C" fn read(fd: c_int, buf: *mut c_void, count: usize) -> ctypes::ssize_t { - e(sys_read(fd, buf, count) as _) as _ -} - -/// Write data to the file indicated by `fd`. -/// -/// Return the written size if success. -#[no_mangle] -#[cfg(not(test))] -pub unsafe extern "C" fn write(fd: c_int, buf: *const c_void, count: usize) -> ctypes::ssize_t { - e(sys_write(fd, buf, count) as _) as _ -} - -/// Write a vector. -#[no_mangle] -pub unsafe extern "C" fn writev( - fd: c_int, - iov: *const ctypes::iovec, - iocnt: c_int, -) -> ctypes::ssize_t { - e(sys_writev(fd, iov, iocnt) as _) as _ -} - -/// Manipulate file descriptor. -/// -/// TODO: `SET/GET` command is ignored -#[cfg(feature = "fd")] -#[no_mangle] -pub unsafe extern "C" fn rux_ioctl(fd: c_int, req: c_int, arg: usize) -> c_int { - e(sys_ioctl(fd, req.try_into().unwrap(), arg)) -} diff --git a/ulib/ruxlibc/src/io_mpx.rs b/ulib/ruxlibc/src/io_mpx.rs deleted file mode 100644 index 15cb297b2..000000000 --- a/ulib/ruxlibc/src/io_mpx.rs +++ /dev/null @@ -1,76 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use crate::{ctypes, utils::e}; - -use core::ffi::c_int; - -#[cfg(feature = "poll")] -use ruxos_posix_api::sys_poll; -#[cfg(feature = "select")] -use ruxos_posix_api::sys_select; -#[cfg(feature = "epoll")] -use ruxos_posix_api::{sys_epoll_create, sys_epoll_ctl, sys_epoll_wait}; - -/// Creates a new epoll instance. -/// -/// It returns a file descriptor referring to the new epoll instance. -#[cfg(feature = "epoll")] -#[no_mangle] -pub unsafe extern "C" fn epoll_create(size: c_int) -> c_int { - e(sys_epoll_create(size)) -} - -/// Control interface for an epoll file descriptor -#[cfg(feature = "epoll")] -#[no_mangle] -pub unsafe extern "C" fn epoll_ctl( - epfd: c_int, - op: c_int, - fd: c_int, - event: *mut ctypes::epoll_event, -) -> c_int { - e(sys_epoll_ctl(epfd, op, fd, event)) -} - -/// Waits for events on the epoll instance referred to by the file descriptor epfd. -#[cfg(feature = "epoll")] -#[no_mangle] -pub unsafe extern "C" fn epoll_wait( - epfd: c_int, - events: *mut ctypes::epoll_event, - maxevents: c_int, - timeout: c_int, -) -> c_int { - e(sys_epoll_wait(epfd, events, maxevents, timeout)) -} - -/// Monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation -#[cfg(feature = "select")] -#[no_mangle] -pub unsafe extern "C" fn select( - nfds: c_int, - readfds: *mut ctypes::fd_set, - writefds: *mut ctypes::fd_set, - exceptfds: *mut ctypes::fd_set, - timeout: *mut ctypes::timeval, -) -> c_int { - e(sys_select(nfds, readfds, writefds, exceptfds, timeout)) -} - -/// Monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation -#[cfg(feature = "poll")] -#[no_mangle] -pub unsafe extern "C" fn poll( - fds: *mut ctypes::pollfd, - nfds: ctypes::nfds_t, - timeout: c_int, -) -> c_int { - e(sys_poll(fds, nfds, timeout)) -} diff --git a/ulib/ruxlibc/src/lib.rs b/ulib/ruxlibc/src/lib.rs deleted file mode 100644 index 160566cff..000000000 --- a/ulib/ruxlibc/src/lib.rs +++ /dev/null @@ -1,152 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -//! [Ruxos] user program library for C apps. -//! -//! ## Cargo Features -//! -//! - CPU -//! - `smp`: Enable SMP (symmetric multiprocessing) support. -//! - `fp_simd`: Enable floating point and SIMD support. -//! - Interrupts: -//! - `irq`: Enable interrupt handling support. -//! - Memory -//! - `alloc`: Enable dynamic memory allocation. -//! - `tls`: Enable thread-local storage. -//! - Task management -//! - `multitask`: Enable multi-threading support. -//! - Upperlayer stacks -//! - `fs`: Enable file system support. -//! - `net`: Enable networking support. -//! - `signal`: Enable signal support. -//! - Lib C functions -//! - `fd`: Enable file descriptor table. -//! - `pipe`: Enable pipe support. -//! - `select`: Enable synchronous I/O multiplexing ([select]) support. -//! - `epoll`: Enable event polling ([epoll]) support. -//! -//! [Ruxos]: https://github.com/syswonder/ruxos -//! [select]: https://man7.org/linux/man-pages/man2/select.2.html -//! [epoll]: https://man7.org/linux/man-pages/man7/epoll.7.html - -#![cfg_attr(all(not(test), not(doc)), no_std)] -#![feature(doc_cfg)] -#![feature(doc_auto_cfg)] -#![feature(naked_functions)] -#![feature(thread_local)] -#![allow(clippy::missing_safety_doc)] - -#[cfg(feature = "alloc")] -extern crate alloc; -#[cfg(feature = "alloc")] -mod env; -#[path = "."] -mod ctypes { - #[rustfmt::skip] - #[path = "libctypes_gen.rs"] - #[allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals, clippy::upper_case_acronyms)] - mod libctypes; - - pub use libctypes::*; - pub use ruxos_posix_api::ctypes::*; -} - -#[macro_use] -mod utils; - -#[cfg(feature = "fd")] -mod fd_ops; -#[cfg(feature = "fs")] -mod fs; -#[cfg(any(feature = "select", feature = "poll", feature = "epoll"))] -mod io_mpx; -#[cfg(feature = "alloc")] -mod malloc; -#[cfg(feature = "alloc")] -mod mmap; -#[cfg(feature = "net")] -mod net; -#[cfg(feature = "pipe")] -mod pipe; -#[cfg(feature = "multitask")] -mod pthread; -#[cfg(feature = "alloc")] -mod strftime; -#[cfg(feature = "fp_simd")] -mod strtod; - -mod errno; -mod io; -mod mktime; -mod rand; -mod resource; -mod setjmp; -mod signal; -mod string; -mod sys; -mod time; -mod unistd; - -#[cfg(not(test))] -pub use self::io::write; -pub use self::io::{read, writev}; - -pub use self::errno::strerror; -pub use self::mktime::mktime; -pub use self::rand::{getrandom, rand, random, srand}; -pub use self::resource::{getrlimit, setrlimit}; -pub use self::setjmp::{longjmp, setjmp}; -pub use self::string::{strlen, strnlen}; -pub use self::sys::sysconf; -pub use self::time::{clock_gettime, nanosleep}; -pub use self::unistd::{abort, exit, getpid}; - -#[cfg(feature = "alloc")] -pub use self::env::{getenv, setenv, unsetenv}; -#[cfg(feature = "fd")] -pub use self::fd_ops::{ax_fcntl, close, dup, dup2, dup3}; -#[cfg(feature = "fs")] -pub use self::fs::{ax_open, fstat, getcwd, lseek, lstat, mkdir, rename, rmdir, stat, unlink}; -#[cfg(feature = "fd")] -pub use self::io::rux_ioctl; -#[cfg(feature = "poll")] -pub use self::io_mpx::poll; -#[cfg(feature = "select")] -pub use self::io_mpx::select; -#[cfg(feature = "epoll")] -pub use self::io_mpx::{epoll_create, epoll_ctl, epoll_wait}; -#[cfg(feature = "alloc")] -pub use self::malloc::{free, malloc}; -#[cfg(feature = "alloc")] -pub use self::mmap::{mmap, munmap}; -#[cfg(feature = "net")] -pub use self::net::{ - accept, ax_sendmsg, bind, connect, freeaddrinfo, getaddrinfo, getpeername, getsockname, listen, - recv, recvfrom, send, sendto, shutdown, socket, -}; -#[cfg(feature = "pipe")] -pub use self::pipe::pipe; -#[cfg(feature = "multitask")] -pub use self::pthread::{ - pthread_cond_broadcast, pthread_cond_init, pthread_cond_signal, pthread_cond_wait, -}; -#[cfg(feature = "multitask")] -pub use self::pthread::{pthread_create, pthread_exit, pthread_join, pthread_self}; -#[cfg(feature = "multitask")] -pub use self::pthread::{ - pthread_mutex_init, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, -}; -#[cfg(feature = "alloc")] -pub use self::strftime::strftime; -#[cfg(feature = "fp_simd")] -pub use self::strtod::{strtod, strtof}; -#[cfg(feature = "signal")] -pub use self::time::{getitimer, setitimer}; -#[cfg(feature = "signal")] -pub use self::unistd::{alarm, ualarm}; diff --git a/ulib/ruxlibc/src/malloc.rs b/ulib/ruxlibc/src/malloc.rs deleted file mode 100644 index 4b2cb25c5..000000000 --- a/ulib/ruxlibc/src/malloc.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -//! Provides the corresponding malloc(size_t) and free(size_t) when using the C user program. -//! -//! The normal malloc(size_t) and free(size_t) are provided by the library malloc.h, and -//! sys_brk is used internally to apply for memory from the kernel. But in a unikernel like -//! `Ruxos`, we noticed that the heap of the Rust user program is shared with the kernel. In -//! order to maintain consistency, C user programs also choose to share the kernel heap, -//! skipping the sys_brk step. - -use alloc::alloc::{alloc, dealloc}; -use core::alloc::Layout; -use core::ffi::c_void; - -use crate::ctypes; - -struct MemoryControlBlock { - size: usize, -} - -const CTRL_BLK_SIZE: usize = core::mem::size_of::(); - -/// Allocate memory and return the memory address. -/// -/// Returns 0 on failure (the current implementation does not trigger an exception) -#[no_mangle] -pub unsafe extern "C" fn malloc(size: ctypes::size_t) -> *mut c_void { - // Allocate `(actual length) + 8`. The lowest 8 Bytes are stored in the actual allocated space size. - // This is because free(uintptr_t) has only one parameter representing the address, - // So we need to save in advance to know the size of the memory space that needs to be released - let layout = Layout::from_size_align(size + CTRL_BLK_SIZE, 8).unwrap(); - unsafe { - let ptr = alloc(layout).cast::(); - assert!(!ptr.is_null(), "malloc failed"); - ptr.write(MemoryControlBlock { size }); - ptr.add(1).cast() - } -} - -/// Deallocate memory. -/// -/// (WARNING) If the address to be released does not match the allocated address, an error should -/// occur, but it will NOT be checked out. This is due to the global allocator `Buddy_system` -/// (currently used) does not check the validity of address to be released. -#[no_mangle] -pub unsafe extern "C" fn free(ptr: *mut c_void) { - if ptr.is_null() { - return; - } - let ptr = ptr.cast::(); - assert!(ptr as usize > CTRL_BLK_SIZE, "free a null pointer"); - unsafe { - let ptr = ptr.sub(1); - let size = ptr.read().size; - let layout = Layout::from_size_align(size + CTRL_BLK_SIZE, 8).unwrap(); - dealloc(ptr.cast(), layout) - } -} diff --git a/ulib/ruxlibc/src/mktime.rs b/ulib/ruxlibc/src/mktime.rs deleted file mode 100644 index fadc1cfb5..000000000 --- a/ulib/ruxlibc/src/mktime.rs +++ /dev/null @@ -1,67 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use core::ffi::c_int; - -use crate::ctypes; - -const MONTH_DAYS: [[c_int; 12]; 2] = [ - // Non-leap years: - [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], - // Leap years: - [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], -]; - -#[inline(always)] -fn leap_year(year: c_int) -> bool { - year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) -} - -/// Convert broken-down time into time since the Epoch. -#[no_mangle] -pub unsafe extern "C" fn mktime(t: *mut ctypes::tm) -> ctypes::time_t { - let mut year = (*t).tm_year + 1900; - let mut month = (*t).tm_mon; - let mut day = (*t).tm_mday as i64 - 1; - - let leap = if leap_year(year) { 1 } else { 0 }; - - if year < 1970 { - day = MONTH_DAYS[if leap_year(year) { 1 } else { 0 }][(*t).tm_mon as usize] as i64 - day; - - while year < 1969 { - year += 1; - day += if leap_year(year) { 366 } else { 365 }; - } - - while month < 11 { - month += 1; - day += MONTH_DAYS[leap][month as usize] as i64; - } - - (-(day * (60 * 60 * 24) - - (((*t).tm_hour as i64) * (60 * 60) + ((*t).tm_min as i64) * 60 + (*t).tm_sec as i64))) - as ctypes::time_t - } else { - while year > 1970 { - year -= 1; - day += if leap_year(year) { 366 } else { 365 }; - } - - while month > 0 { - month -= 1; - day += MONTH_DAYS[leap][month as usize] as i64; - } - - (day * (60 * 60 * 24) - + ((*t).tm_hour as i64) * (60 * 60) - + ((*t).tm_min as i64) * 60 - + (*t).tm_sec as i64) as ctypes::time_t - } -} diff --git a/ulib/ruxlibc/src/mmap.rs b/ulib/ruxlibc/src/mmap.rs deleted file mode 100644 index 4573e2cfc..000000000 --- a/ulib/ruxlibc/src/mmap.rs +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use crate::ctypes; -use core::ffi::{c_int, c_void}; - -use ruxos_posix_api::{sys_madvise, sys_mmap, sys_mprotect, sys_mremap, sys_msync, sys_munmap}; - -/// Map a file or device into virtual memory. -#[no_mangle] -pub unsafe extern "C" fn mmap( - addr: *mut c_void, - len: ctypes::size_t, - prot: c_int, - flags: c_int, - fid: c_int, - offset: ctypes::off_t, -) -> *mut c_void { - sys_mmap(addr, len, prot, flags, fid, offset) -} - -/// Unmap a range address of memory. -#[no_mangle] -pub unsafe extern "C" fn munmap(addr: *mut c_void, len: ctypes::size_t) -> c_int { - sys_munmap(addr, len) -} - -/// Sync pages mapped in memory to file. -#[no_mangle] -pub unsafe extern "C" fn msync(addr: *mut c_void, len: ctypes::size_t, flags: c_int) -> c_int { - sys_msync(addr, len, flags) -} - -/// Remap the address for already mapped memory. -#[no_mangle] -pub unsafe extern "C" fn mremap( - old_addr: *mut c_void, - old_size: ctypes::size_t, - new_size: ctypes::size_t, - flags: c_int, - new_addr: *mut c_void, -) -> *mut c_void { - sys_mremap(old_addr, old_size, new_size, flags, new_addr) -} - -/// Change the accessiblity for already mapped memory. -#[no_mangle] -pub unsafe extern "C" fn mprotect(addr: *mut c_void, len: ctypes::size_t, flags: c_int) -> c_int { - sys_mprotect(addr, len, flags) -} - -/// Advise the operating system about the expected behavior of a specific region of memory. -/// -/// Note: Unimplement yet. -#[no_mangle] -pub unsafe extern "C" fn madvise(addr: *mut c_void, len: ctypes::size_t, advice: c_int) -> c_int { - sys_madvise(addr, len, advice) -} diff --git a/ulib/ruxlibc/src/net.rs b/ulib/ruxlibc/src/net.rs deleted file mode 100644 index 6ae7d237c..000000000 --- a/ulib/ruxlibc/src/net.rs +++ /dev/null @@ -1,198 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use core::ffi::{c_char, c_int, c_void}; -use ruxos_posix_api as api; - -use crate::{ctypes, utils::e}; - -/// Create an socket for communication. -/// -/// Return the socket file descriptor. -#[no_mangle] -pub unsafe extern "C" fn socket(domain: c_int, socktype: c_int, protocol: c_int) -> c_int { - e(api::sys_socket(domain, socktype, protocol)) -} - -/// Bind a address to a socket. -/// -/// Return 0 if success. -#[no_mangle] -pub unsafe extern "C" fn bind( - socket_fd: c_int, - socket_addr: *const ctypes::sockaddr, - addrlen: ctypes::socklen_t, -) -> c_int { - e(api::sys_bind(socket_fd, socket_addr, addrlen)) -} - -/// Connects the socket to the address specified. -/// -/// Return 0 if success. -#[no_mangle] -pub unsafe extern "C" fn connect( - socket_fd: c_int, - socket_addr: *const ctypes::sockaddr, - addrlen: ctypes::socklen_t, -) -> c_int { - e(api::sys_connect(socket_fd, socket_addr, addrlen)) -} - -/// Send a message on a socket to the address specified. -/// -/// Return the number of bytes sent if success. -#[no_mangle] -pub unsafe extern "C" fn sendto( - socket_fd: c_int, - buf_ptr: *const c_void, - len: ctypes::size_t, - flag: c_int, // currently not used - socket_addr: *const ctypes::sockaddr, - addrlen: ctypes::socklen_t, -) -> ctypes::ssize_t { - if socket_addr.is_null() && addrlen == 0 { - return e(api::sys_send(socket_fd, buf_ptr, len, flag) as _) as _; - } - e(api::sys_sendto(socket_fd, buf_ptr, len, flag, socket_addr, addrlen) as _) as _ -} - -/// Send a message on a socket to the address connected. -/// -/// Return the number of bytes sent if success. -#[no_mangle] -pub unsafe extern "C" fn send( - socket_fd: c_int, - buf_ptr: *const c_void, - len: ctypes::size_t, - flag: c_int, // currently not used -) -> ctypes::ssize_t { - e(api::sys_send(socket_fd, buf_ptr, len, flag) as _) as _ -} - -/// Receive a message on a socket and get its source address. -/// -/// Return the number of bytes received if success. -#[no_mangle] -pub unsafe extern "C" fn recvfrom( - socket_fd: c_int, - buf_ptr: *mut c_void, - len: ctypes::size_t, - flag: c_int, // currently not used - socket_addr: *mut ctypes::sockaddr, - addrlen: *mut ctypes::socklen_t, -) -> ctypes::ssize_t { - if socket_addr.is_null() { - return e(api::sys_recv(socket_fd, buf_ptr, len, flag) as _) as _; - } - e(api::sys_recvfrom(socket_fd, buf_ptr, len, flag, socket_addr, addrlen) as _) as _ -} - -/// Receive a message on a socket. -/// -/// Return the number of bytes received if success. -#[no_mangle] -pub unsafe extern "C" fn recv( - socket_fd: c_int, - buf_ptr: *mut c_void, - len: ctypes::size_t, - flag: c_int, // currently not used -) -> ctypes::ssize_t { - e(api::sys_recv(socket_fd, buf_ptr, len, flag) as _) as _ -} - -/// Listen for connections on a socket -/// -/// Return 0 if success. -#[no_mangle] -pub unsafe extern "C" fn listen( - socket_fd: c_int, - backlog: c_int, // currently not used -) -> c_int { - e(api::sys_listen(socket_fd, backlog)) -} - -/// Accept for connections on a socket -/// -/// Return file descriptor for the accepted socket if success. -#[no_mangle] -pub unsafe extern "C" fn accept( - socket_fd: c_int, - socket_addr: *mut ctypes::sockaddr, - socket_len: *mut ctypes::socklen_t, -) -> c_int { - e(api::sys_accept(socket_fd, socket_addr, socket_len)) -} - -/// Shut down a full-duplex connection. -/// -/// Return 0 if success. -#[no_mangle] -pub unsafe extern "C" fn shutdown( - socket_fd: c_int, - flag: c_int, // currently not used -) -> c_int { - e(api::sys_shutdown(socket_fd, flag)) -} - -/// Query addresses for a domain name. -/// -/// Return address number if success. -#[no_mangle] -pub unsafe extern "C" fn getaddrinfo( - nodename: *const c_char, - servname: *const c_char, - hints: *const ctypes::addrinfo, - res: *mut *mut ctypes::addrinfo, -) -> c_int { - let ret = e(api::sys_getaddrinfo(nodename, servname, hints, res)); - match ret { - r if r < 0 => ctypes::EAI_FAIL, - 0 => ctypes::EAI_NONAME, - _ => 0, - } -} - -/// Free queried `addrinfo` struct -#[no_mangle] -pub unsafe extern "C" fn freeaddrinfo(res: *mut ctypes::addrinfo) { - api::sys_freeaddrinfo(res); -} - -/// Get current address to which the socket sockfd is bound. -#[no_mangle] -pub unsafe extern "C" fn getsockname( - sock_fd: c_int, - addr: *mut ctypes::sockaddr, - addrlen: *mut ctypes::socklen_t, -) -> c_int { - e(api::sys_getsockname(sock_fd, addr, addrlen)) -} - -/// Get peer address to which the socket sockfd is connected. -#[no_mangle] -pub unsafe extern "C" fn getpeername( - sock_fd: c_int, - addr: *mut ctypes::sockaddr, - addrlen: *mut ctypes::socklen_t, -) -> c_int { - e(api::sys_getpeername(sock_fd, addr, addrlen)) -} - -/// Send a message on a socket to the address connected. -/// The message is pointed to by the elements of the array msg.msg_iov. -/// -/// Return the number of bytes sent if success. -#[no_mangle] -pub unsafe extern "C" fn ax_sendmsg( - socket_fd: c_int, - msg: *const ctypes::msghdr, - flags: c_int, -) -> ctypes::ssize_t { - e(api::sys_sendmsg(socket_fd, msg, flags) as _) as _ -} diff --git a/ulib/ruxlibc/src/pthread.rs b/ulib/ruxlibc/src/pthread.rs deleted file mode 100644 index 8df293acc..000000000 --- a/ulib/ruxlibc/src/pthread.rs +++ /dev/null @@ -1,157 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use crate::{ctypes, utils::e}; -use core::ffi::{c_int, c_void}; -use ruxos_posix_api as api; - -/// Returns the `pthread` struct of current thread. -#[no_mangle] -pub unsafe extern "C" fn pthread_self() -> ctypes::pthread_t { - api::sys_pthread_self() -} - -/// Create a new thread with the given entry point and argument. -/// -/// If successful, it stores the pointer to the newly created `struct __pthread` -/// in `res` and returns 0. -#[no_mangle] -pub unsafe extern "C" fn pthread_create( - res: *mut ctypes::pthread_t, - attr: *const ctypes::pthread_attr_t, - start_routine: extern "C" fn(arg: *mut c_void) -> *mut c_void, - arg: *mut c_void, -) -> c_int { - e(api::sys_pthread_create(res, attr, start_routine, arg)) -} - -/// Exits the current thread. The value `retval` will be returned to the joiner. -#[no_mangle] -pub unsafe extern "C" fn pthread_exit(retval: *mut c_void) -> ! { - api::sys_pthread_exit(retval) -} - -/// Waits for the given thread to exit, and stores the return value in `retval`. -#[no_mangle] -pub unsafe extern "C" fn pthread_join( - thread: ctypes::pthread_t, - retval: *mut *mut c_void, -) -> c_int { - e(api::sys_pthread_join(thread, retval)) -} - -/// Initialize a mutex. -#[no_mangle] -pub unsafe extern "C" fn pthread_mutex_init( - mutex: *mut ctypes::pthread_mutex_t, - attr: *const ctypes::pthread_mutexattr_t, -) -> c_int { - e(api::sys_pthread_mutex_init(mutex, attr)) -} - -/// Destroy a mutex. -#[no_mangle] -pub unsafe extern "C" fn pthread_mutex_destroy(mutex: *mut ctypes::pthread_mutex_t) -> c_int { - e(api::sys_pthread_mutex_destroy(mutex)) -} - -/// Lock the given mutex. -#[no_mangle] -pub unsafe extern "C" fn pthread_mutex_lock(mutex: *mut ctypes::pthread_mutex_t) -> c_int { - e(api::sys_pthread_mutex_lock(mutex)) -} - -/// Lock the given mutex. If the mutex is already locked, it returns immediatly with the error -/// code EBUSY. -#[no_mangle] -pub unsafe extern "C" fn pthread_mutex_trylock(mutex: *mut ctypes::pthread_mutex_t) -> c_int { - e(api::sys_pthread_mutex_trylock(mutex)) -} - -/// Unlock the given mutex. -#[no_mangle] -pub unsafe extern "C" fn pthread_mutex_unlock(mutex: *mut ctypes::pthread_mutex_t) -> c_int { - e(api::sys_pthread_mutex_unlock(mutex)) -} - -/// Initialize a condition variable -#[no_mangle] -pub unsafe extern "C" fn pthread_cond_init( - condvar: *mut ctypes::pthread_cond_t, - attr: *mut ctypes::pthread_condattr_t, -) -> c_int { - e(api::sys_pthread_cond_init(condvar, attr)) -} - -/// Destroy a condition variable -#[no_mangle] -pub unsafe extern "C" fn pthread_cond_destroy(condvar: *mut ctypes::pthread_cond_t) -> c_int { - e(api::sys_pthread_cond_destroy(condvar)) -} - -#[no_mangle] -/// Wait for the condition variable to be signaled or timeout -pub unsafe extern "C" fn pthread_cond_timedwait( - condvar: *mut ctypes::pthread_cond_t, - mutex: *mut ctypes::pthread_mutex_t, - abstime: *const ctypes::timespec, -) -> c_int { - e(api::sys_pthread_cond_timedwait(condvar, mutex, abstime)) -} - -/// Wait for the condition variable to be signaled -#[no_mangle] -pub unsafe extern "C" fn pthread_cond_wait( - condvar: *mut ctypes::pthread_cond_t, - mutex: *mut ctypes::pthread_mutex_t, -) -> c_int { - e(api::sys_pthread_cond_wait(condvar, mutex)) -} - -/// Restarts one of the threads that are waiting on the condition variable. -#[no_mangle] -pub unsafe extern "C" fn pthread_cond_signal(condvar: *mut ctypes::pthread_cond_t) -> c_int { - e(api::sys_pthread_cond_signal(condvar)) -} - -/// Restarts all the threads that are waiting on the condition variable. -#[no_mangle] -pub unsafe extern "C" fn pthread_cond_broadcast(condvar: *mut ctypes::pthread_cond_t) -> c_int { - e(api::sys_pthread_cond_broadcast(condvar)) -} - -/// Initialize a thread-specific data key -#[no_mangle] -pub unsafe extern "C" fn pthread_key_create( - key: *mut ctypes::pthread_key_t, - dtor: Option, -) -> c_int { - e(api::sys_pthread_key_create(key, dtor)) -} - -/// Destroy a thread-specific data key -#[no_mangle] -pub unsafe extern "C" fn pthread_key_delete(key: ctypes::pthread_key_t) -> c_int { - e(api::sys_pthread_key_delete(key)) -} - -/// Get the value of a thread-specific data key -#[no_mangle] -pub unsafe extern "C" fn pthread_getspecific(key: ctypes::pthread_key_t) -> *mut c_void { - api::sys_pthread_getspecific(key) -} - -/// Set the value of a thread-specific data key -#[no_mangle] -pub unsafe extern "C" fn pthread_setspecific( - key: ctypes::pthread_key_t, - value: *const c_void, -) -> c_int { - e(api::sys_pthread_setspecific(key, value)) -} diff --git a/ulib/ruxlibc/src/rand.rs b/ulib/ruxlibc/src/rand.rs deleted file mode 100644 index 07eefdd60..000000000 --- a/ulib/ruxlibc/src/rand.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use crate::ctypes::size_t; -use core::ffi::{c_int, c_long, c_uint, c_void}; - -use ruxos_posix_api::{sys_getrandom, sys_rand, sys_random, sys_srand}; - -use crate::utils::e; - -/// srand -#[no_mangle] -pub unsafe extern "C" fn srand(seed: c_uint) { - sys_srand(seed); -} - -/// rand -#[no_mangle] -pub unsafe extern "C" fn rand() -> c_int { - e(sys_rand() as c_int) -} - -/// random -#[no_mangle] -pub unsafe extern "C" fn random() -> c_long { - e(sys_random().try_into().unwrap()) as _ -} - -/// Get random -#[no_mangle] -pub unsafe extern "C" fn getrandom(buf: *mut c_void, buflen: size_t, flags: c_int) -> size_t { - e(sys_getrandom(buf, buflen, flags).try_into().unwrap()) as _ -} diff --git a/ulib/ruxlibc/src/resource.rs b/ulib/ruxlibc/src/resource.rs deleted file mode 100644 index 2f55ea5e9..000000000 --- a/ulib/ruxlibc/src/resource.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use core::ffi::c_int; - -use ruxos_posix_api::{sys_getrlimit, sys_setrlimit}; - -use crate::utils::e; - -/// Get resource limitations -#[no_mangle] -pub unsafe extern "C" fn getrlimit(resource: c_int, rlimits: *mut crate::ctypes::rlimit) -> c_int { - e(sys_getrlimit(resource, rlimits)) -} - -/// Set resource limitations -#[no_mangle] -pub unsafe extern "C" fn setrlimit( - resource: c_int, - rlimits: *const crate::ctypes::rlimit, -) -> c_int { - e(sys_setrlimit(resource, rlimits)) -} diff --git a/ulib/ruxlibc/src/setjmp.rs b/ulib/ruxlibc/src/setjmp.rs deleted file mode 100644 index bf310415c..000000000 --- a/ulib/ruxlibc/src/setjmp.rs +++ /dev/null @@ -1,247 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use core::ffi::c_int; - -use crate::ctypes; - -/// `setjmp` implementation -#[naked] -#[no_mangle] -pub unsafe extern "C" fn setjmp(_buf: *mut ctypes::__jmp_buf_tag) { - #[cfg(all(target_arch = "aarch64", feature = "fp_simd"))] - core::arch::asm!( - " - stp x19, x20, [x0,#0] - stp x21, x22, [x0,#16] - stp x23, x24, [x0,#32] - stp x25, x26, [x0,#48] - stp x27, x28, [x0,#64] - stp x29, x30, [x0,#80] - mov x2, sp - str x2, [x0,#104] - stp d8, d9, [x0,#112] - stp d10, d11, [x0,#128] - stp d12, d13, [x0,#144] - stp d14, d15, [x0,#160] - mov x0, #0 - ret", - options(noreturn), - ); - #[cfg(all(target_arch = "aarch64", not(feature = "fp_simd")))] - core::arch::asm!( - " - stp x19, x20, [x0,#0] - stp x21, x22, [x0,#16] - stp x23, x24, [x0,#32] - stp x25, x26, [x0,#48] - stp x27, x28, [x0,#64] - stp x29, x30, [x0,#80] - mov x2, sp - str x2, [x0,#104] - mov x0, #0 - ret", - options(noreturn), - ); - #[cfg(target_arch = "x86_64")] - core::arch::asm!( - "mov [rdi], rbx - mov [rdi + 8], rbp - mov [rdi + 16], r12 - mov [rdi + 24], r13 - mov [rdi + 32], r14 - mov [rdi + 40], r15 - lea rdx, [rsp + 8] - mov [rdi + 48], rdx - mov rdx, [rsp] - mov [rdi + 56], rdx - xor rax, rax - ret", - options(noreturn), - ); - #[cfg(all(target_arch = "riscv64", feature = "fp_simd"))] - core::arch::asm!( - "sd s0, 0(a0) - sd s1, 8(a0) - sd s2, 16(a0) - sd s3, 24(a0) - sd s4, 32(a0) - sd s5, 40(a0) - sd s6, 48(a0) - sd s7, 56(a0) - sd s8, 64(a0) - sd s9, 72(a0) - sd s10, 80(a0) - sd s11, 88(a0) - sd sp, 96(a0) - sd ra, 104(a0) - - fsd fs0, 112(a0) - fsd fs1, 120(a0) - fsd fs2, 128(a0) - fsd fs3, 136(a0) - fsd fs4, 144(a0) - fsd fs5, 152(a0) - fsd fs6, 160(a0) - fsd fs7, 168(a0) - fsd fs8, 176(a0) - fsd fs9, 184(a0) - fsd fs10, 192(a0) - fsd fs11, 200(a0) - - li a0, 0 - ret", - options(noreturn), - ); - #[cfg(all(target_arch = "riscv64", not(feature = "fp_simd")))] - core::arch::asm!( - "sd s0, 0(a0) - sd s1, 8(a0) - sd s2, 16(a0) - sd s3, 24(a0) - sd s4, 32(a0) - sd s5, 40(a0) - sd s6, 48(a0) - sd s7, 56(a0) - sd s8, 64(a0) - sd s9, 72(a0) - sd s10, 80(a0) - sd s11, 88(a0) - sd sp, 96(a0) - sd ra, 104(a0) - - li a0, 0 - ret", - options(noreturn), - ); - #[cfg(not(any( - target_arch = "aarch64", - target_arch = "x86_64", - target_arch = "riscv64" - )))] - core::arch::asm!("ret", options(noreturn)) -} - -/// `longjmp` implementation -#[naked] -#[no_mangle] -pub unsafe extern "C" fn longjmp(_buf: *mut ctypes::__jmp_buf_tag, _val: c_int) -> ! { - #[cfg(all(target_arch = "aarch64", feature = "fp_simd"))] - core::arch::asm!( - "ldp x19, x20, [x0,#0] - ldp x21, x22, [x0,#16] - ldp x23, x24, [x0,#32] - ldp x25, x26, [x0,#48] - ldp x27, x28, [x0,#64] - ldp x29, x30, [x0,#80] - ldr x2, [x0,#104] - mov sp, x2 - ldp d8 , d9, [x0,#112] - ldp d10, d11, [x0,#128] - ldp d12, d13, [x0,#144] - ldp d14, d15, [x0,#160] - - cmp w1, 0 - csinc w0, w1, wzr, ne - br x30", - options(noreturn), - ); - #[cfg(all(target_arch = "aarch64", not(feature = "fp_simd")))] - core::arch::asm!( - "ldp x19, x20, [x0,#0] - ldp x21, x22, [x0,#16] - ldp x23, x24, [x0,#32] - ldp x25, x26, [x0,#48] - ldp x27, x28, [x0,#64] - ldp x29, x30, [x0,#80] - ldr x2, [x0,#104] - mov sp, x2 - - cmp w1, 0 - csinc w0, w1, wzr, ne - br x30", - options(noreturn), - ); - #[cfg(target_arch = "x86_64")] - core::arch::asm!( - "mov rax,rsi - test rax,rax - jnz 1f - inc rax - 1: - mov rbx, [rdi] - mov rbp, [rdi + 8] - mov r12, [rdi + 16] - mov r13, [rdi + 24] - mov r14, [rdi + 32] - mov r15, [rdi + 40] - mov rdx, [rdi + 48] - mov rsp, rdx - mov rdx, [rdi + 56] - jmp rdx", - options(noreturn), - ); - #[cfg(all(target_arch = "riscv64", feature = "fp_simd"))] - core::arch::asm!( - "ld s0, 0(a0) - ld s1, 8(a0) - ld s2, 16(a0) - ld s3, 24(a0) - ld s4, 32(a0) - ld s5, 40(a0) - ld s6, 48(a0) - ld s7, 56(a0) - ld s8, 64(a0) - ld s9, 72(a0) - ld s10, 80(a0) - ld s11, 88(a0) - ld sp, 96(a0) - ld ra, 104(a0) - - fld fs0, 112(a0) - fld fs1, 120(a0) - fld fs2, 128(a0) - fld fs3, 136(a0) - fld fs4, 144(a0) - fld fs5, 152(a0) - fld fs6, 160(a0) - fld fs7, 168(a0) - fld fs8, 176(a0) - fld fs9, 184(a0) - fld fs10, 192(a0) - fld fs11, 200(a0) - - seqz a0, a1 - add a0, a0, a1 - ret", - options(noreturn), - ); - #[cfg(all(target_arch = "riscv64", not(feature = "fp_simd")))] - core::arch::asm!( - "ld s0, 0(a0) - ld s1, 8(a0) - ld s2, 16(a0) - ld s3, 24(a0) - ld s4, 32(a0) - ld s5, 40(a0) - ld s6, 48(a0) - ld s7, 56(a0) - ld s8, 64(a0) - ld s9, 72(a0) - ld s10, 80(a0) - ld s11, 88(a0) - ld sp, 96(a0) - ld ra, 104(a0) - - seqz a0, a1 - add a0, a0, a1 - ret", - options(noreturn), - ); -} diff --git a/ulib/ruxlibc/src/signal.rs b/ulib/ruxlibc/src/signal.rs deleted file mode 100644 index 1fc19d958..000000000 --- a/ulib/ruxlibc/src/signal.rs +++ /dev/null @@ -1,73 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use core::ffi::c_int; - -#[cfg(feature = "signal")] -use crate::ctypes::k_sigaction; -use crate::ctypes::{sigaction, EINVAL, SIGKILL, SIGSTOP}; -#[cfg(feature = "signal")] -use ruxos_posix_api::sys_sigaction; - -#[cfg(feature = "signal")] -unsafe extern "C" fn ignore_handler(_: c_int) {} - -#[no_mangle] -pub unsafe extern "C" fn sigaction_inner( - signum: c_int, - _act: *const sigaction, - oldact: *mut sigaction, -) -> c_int { - if signum >= 32 || signum == SIGKILL as _ || signum == SIGSTOP as _ { - return -(EINVAL as c_int); - } - #[cfg(feature = "signal")] - { - let mut sh = (*_act).__sa_handler.sa_handler; - if let Some(h) = sh { - if h as usize == crate::ctypes::SIGIGN as usize { - sh = Some(ignore_handler as unsafe extern "C" fn(c_int)); - } - } - let k_act = k_sigaction { - handler: sh, - flags: (*_act).sa_flags as _, - restorer: (*_act).sa_restorer, - mask: Default::default(), - }; - let mut k_oldact = k_sigaction::default(); - sys_sigaction( - signum as u8, - Some(&k_act), - if oldact.is_null() { - None - } else { - Some(&mut k_oldact) - }, - ); - if !oldact.is_null() { - (*oldact).__sa_handler.sa_handler = k_oldact.handler; - (*oldact).sa_flags = k_oldact.flags as _; - (*oldact).sa_restorer = k_oldact.restorer; - // Not support mask - // (*oldact).sa_mask = k_oldact.mask; - } - } - #[cfg(not(feature = "signal"))] - { - if !oldact.is_null() { - // set to 0 - (*oldact).__sa_handler.sa_handler = None; - (*oldact).sa_flags = 0; - (*oldact).sa_restorer = None; - (*oldact).sa_mask = Default::default(); - } - } - 0 -} diff --git a/ulib/ruxlibc/src/strftime.rs b/ulib/ruxlibc/src/strftime.rs deleted file mode 100644 index c839b5edf..000000000 --- a/ulib/ruxlibc/src/strftime.rs +++ /dev/null @@ -1,263 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use alloc::string::String; -use core::{ffi::c_char, fmt}; - -use axio::Write; - -use crate::ctypes; - -pub trait WriteByte: fmt::Write { - fn write_u8(&mut self, byte: u8) -> fmt::Result; -} - -struct StringWriter(pub *mut u8, pub usize); - -impl Write for StringWriter { - fn write(&mut self, buf: &[u8]) -> axerrno::AxResult { - if self.1 > 1 { - let copy_size = buf.len().min(self.1 - 1); - unsafe { - core::ptr::copy_nonoverlapping(buf.as_ptr(), self.0, copy_size); - self.1 -= copy_size; - - self.0 = self.0.add(copy_size); - *self.0 = 0; - } - } - Ok(buf.len()) - } - fn flush(&mut self) -> axerrno::AxResult { - Ok(()) - } -} - -impl fmt::Write for StringWriter { - fn write_str(&mut self, s: &str) -> fmt::Result { - // can't fail - self.write(s.as_bytes()).unwrap(); - Ok(()) - } -} - -impl WriteByte for StringWriter { - fn write_u8(&mut self, byte: u8) -> fmt::Result { - // can't fail - self.write(&[byte]).unwrap(); - Ok(()) - } -} - -struct CountingWriter { - pub inner: T, - pub written: usize, -} - -impl CountingWriter { - pub fn new(writer: T) -> Self { - Self { - inner: writer, - written: 0, - } - } -} - -impl fmt::Write for CountingWriter { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.written += s.len(); - self.inner.write_str(s) - } -} - -impl WriteByte for CountingWriter { - fn write_u8(&mut self, byte: u8) -> fmt::Result { - self.written += 1; - self.inner.write_u8(byte) - } -} - -impl Write for CountingWriter { - fn write(&mut self, buf: &[u8]) -> axerrno::AxResult { - let res = self.inner.write(buf); - if let Ok(written) = res { - self.written += written; - } - res - } - - fn write_all(&mut self, buf: &[u8]) -> axerrno::AxResult { - match self.inner.write_all(buf) { - Ok(()) => (), - Err(err) => return Err(err), - } - self.written += buf.len(); - Ok(()) - } - - fn flush(&mut self) -> axerrno::AxResult { - self.inner.flush() - } -} - -unsafe fn strftime_inner( - w: W, - format: *const c_char, - t: *const ctypes::tm, -) -> ctypes::size_t { - pub unsafe fn inner_strftime( - w: &mut W, - mut format: *const c_char, - t: *const ctypes::tm, - ) -> bool { - macro_rules! w { - (byte $b:expr) => {{ - if w.write_u8($b).is_err() { - return false; - } - }}; - (char $chr:expr) => {{ - if w.write_char($chr).is_err() { - return false; - } - }}; - (recurse $fmt:expr) => {{ - let mut fmt = String::with_capacity($fmt.len() + 1); - fmt.push_str($fmt); - fmt.push('\0'); - - if !inner_strftime(w, fmt.as_ptr() as *mut c_char, t) { - return false; - } - }}; - ($str:expr) => {{ - if w.write_str($str).is_err() { - return false; - } - }}; - ($fmt:expr, $($args:expr),+) => {{ - // Would use write!() if I could get the length written - if write!(w, $fmt, $($args),+).is_err() { - return false; - } - }}; - } - const WDAYS: [&str; 7] = [ - "Sunday", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - ]; - const MONTHS: [&str; 12] = [ - "January", - "Febuary", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - ]; - - while *format != 0 { - if *format as u8 != b'%' { - w!(byte * format as u8); - format = format.offset(1); - continue; - } - - format = format.offset(1); - - if *format as u8 == b'E' || *format as u8 == b'O' { - // Ignore because these do nothing without locale - format = format.offset(1); - } - - match *format as u8 { - b'%' => w!(byte b'%'), - b'n' => w!(byte b'\n'), - b't' => w!(byte b'\t'), - b'a' => w!(&WDAYS[(*t).tm_wday as usize][..3]), - b'A' => w!(WDAYS[(*t).tm_wday as usize]), - b'b' | b'h' => w!(&MONTHS[(*t).tm_mon as usize][..3]), - b'B' => w!(MONTHS[(*t).tm_mon as usize]), - b'C' => { - let mut year = (*t).tm_year / 100; - // Round up - if (*t).tm_year % 100 != 0 { - year += 1; - } - w!("{:02}", year + 19); - } - b'd' => w!("{:02}", (*t).tm_mday), - b'D' => w!(recurse "%m/%d/%y"), - b'e' => w!("{:2}", (*t).tm_mday), - b'F' => w!(recurse "%Y-%m-%d"), - b'H' => w!("{:02}", (*t).tm_hour), - b'I' => w!("{:02}", ((*t).tm_hour + 12 - 1) % 12 + 1), - b'j' => w!("{:03}", (*t).tm_yday), - b'k' => w!("{:2}", (*t).tm_hour), - b'l' => w!("{:2}", ((*t).tm_hour + 12 - 1) % 12 + 1), - b'm' => w!("{:02}", (*t).tm_mon + 1), - b'M' => w!("{:02}", (*t).tm_min), - b'p' => w!(if (*t).tm_hour < 12 { "AM" } else { "PM" }), - b'P' => w!(if (*t).tm_hour < 12 { "am" } else { "pm" }), - b'r' => w!(recurse "%I:%M:%S %p"), - b'R' => w!(recurse "%H:%M"), - // Nothing is modified in mktime, but the C standard of course requires a mutable pointer ._. - b's' => w!("{}", super::mktime(t as *mut ctypes::tm)), - b'S' => w!("{:02}", (*t).tm_sec), - b'T' => w!(recurse "%H:%M:%S"), - b'u' => w!("{}", ((*t).tm_wday + 7 - 1) % 7 + 1), - b'U' => w!("{}", ((*t).tm_yday + 7 - (*t).tm_wday) / 7), - b'w' => w!("{}", (*t).tm_wday), - b'W' => w!("{}", ((*t).tm_yday + 7 - ((*t).tm_wday + 6) % 7) / 7), - b'y' => w!("{:02}", (*t).tm_year % 100), - b'Y' => w!("{}", (*t).tm_year + 1900), - b'z' => w!("+0000"), // TODO - b'Z' => w!("UTC"), // TODO - b'+' => w!(recurse "%a %b %d %T %Z %Y"), - _ => return false, - } - - format = format.offset(1); - } - true - } - - let mut w: CountingWriter = CountingWriter::new(w); - if !inner_strftime(&mut w, format, t) { - return 0; - } - - w.written -} - -/// Convert date and time to a string. -#[no_mangle] -pub unsafe extern "C" fn strftime( - buf: *mut c_char, - size: ctypes::size_t, - format: *const c_char, - timeptr: *const ctypes::tm, -) -> ctypes::size_t { - let ret = strftime_inner(StringWriter(buf as *mut u8, size), format, timeptr); - if ret < size { - ret - } else { - 0 - } -} diff --git a/ulib/ruxlibc/src/string.rs b/ulib/ruxlibc/src/string.rs deleted file mode 100644 index 5cbbc7740..000000000 --- a/ulib/ruxlibc/src/string.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use crate::ctypes; -use core::ffi::c_char; -/// calculate the length of a string, excluding the terminating null byte -#[no_mangle] -pub unsafe extern "C" fn strlen(s: *const c_char) -> ctypes::size_t { - strnlen(s, ctypes::size_t::MAX) -} - -/// calculate the length of a string like strlen, but at most maxlen. -#[no_mangle] -pub unsafe extern "C" fn strnlen(s: *const c_char, size: ctypes::size_t) -> ctypes::size_t { - let mut i = 0; - while i < size { - if *s.add(i) == 0 { - break; - } - i += 1; - } - i -} diff --git a/ulib/ruxlibc/src/strtod.rs b/ulib/ruxlibc/src/strtod.rs deleted file mode 100644 index 2e1c3db41..000000000 --- a/ulib/ruxlibc/src/strtod.rs +++ /dev/null @@ -1,140 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use core::ffi::{c_char, c_double, c_float, c_int}; - -macro_rules! strto_float_impl { - ($type:ident, $s:expr, $endptr:expr) => {{ - let mut s = $s; - let endptr = $endptr; - - // TODO: Handle named floats: NaN, Inf... - - while isspace(*s as c_int) { - s = s.offset(1); - } - - let mut result: $type = 0.0; - let mut radix = 10; - - let result_sign = match *s as u8 { - b'-' => { - s = s.offset(1); - -1.0 - } - b'+' => { - s = s.offset(1); - 1.0 - } - _ => 1.0, - }; - - if *s as u8 == b'0' && *s.offset(1) as u8 == b'x' { - s = s.offset(2); - radix = 16; - } - - while let Some(digit) = (*s as u8 as char).to_digit(radix) { - result *= radix as $type; - result += digit as $type; - s = s.offset(1); - } - - if *s as u8 == b'.' { - s = s.offset(1); - - let mut i = 1.0; - while let Some(digit) = (*s as u8 as char).to_digit(radix) { - i *= radix as $type; - result += digit as $type / i; - s = s.offset(1); - } - } - - let s_before_exponent = s; - - let exponent = match (*s as u8, radix) { - (b'e' | b'E', 10) | (b'p' | b'P', 16) => { - s = s.offset(1); - - let is_exponent_positive = match *s as u8 { - b'-' => { - s = s.offset(1); - false - } - b'+' => { - s = s.offset(1); - true - } - _ => true, - }; - - // Exponent digits are always in base 10. - if (*s as u8 as char).is_digit(10) { - let mut exponent_value = 0; - - while let Some(digit) = (*s as u8 as char).to_digit(10) { - exponent_value *= 10; - exponent_value += digit; - s = s.offset(1); - } - - let exponent_base = match radix { - 10 => 10u128, - 16 => 2u128, - _ => unreachable!(), - }; - - if is_exponent_positive { - Some(exponent_base.pow(exponent_value) as $type) - } else { - Some(1.0 / (exponent_base.pow(exponent_value) as $type)) - } - } else { - // Exponent had no valid digits after 'e'/'p' and '+'/'-', rollback - s = s_before_exponent; - None - } - } - _ => None, - }; - - // Return pointer should be *mut - if !endptr.is_null() { - *endptr = s as *mut _; - } - - if let Some(exponent) = exponent { - result_sign * result * exponent - } else { - result_sign * result - } - }}; -} - -fn isspace(c: c_int) -> bool { - c == c_int::from(b' ') - || c == c_int::from(b'\t') - || c == c_int::from(b'\n') - || c == c_int::from(b'\r') - || c == 0x0b - || c == 0x0c -} - -/// Convert a string to a double-precision number. -#[no_mangle] -pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double { - strto_float_impl!(c_double, s, endptr) -} - -/// Convert a string to a float number. -#[no_mangle] -pub unsafe extern "C" fn strtof(s: *const c_char, endptr: *mut *mut c_char) -> c_float { - strto_float_impl!(c_float, s, endptr) -} diff --git a/ulib/ruxlibc/src/sys.rs b/ulib/ruxlibc/src/sys.rs deleted file mode 100644 index 1ec89e423..000000000 --- a/ulib/ruxlibc/src/sys.rs +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use crate::ctypes; -use core::ffi::{c_int, c_long}; -use ruxos_posix_api::{config, sys_getrlimit}; - -/// Return system configuration infomation -/// -/// Notice: currently only support what unikraft covers -#[no_mangle] -pub unsafe extern "C" fn sysconf(name: c_int) -> c_long { - match name as u32 { - // Maximum process number - ctypes::_SC_CHILD_MAX => { - let mut rl: ctypes::rlimit = core::mem::zeroed(); - sys_getrlimit(ctypes::RLIMIT_NPROC.try_into().unwrap(), &mut rl); - rl.rlim_max as c_long - } - // Page size - ctypes::_SC_PAGE_SIZE => config::PAGE_SIZE_4K as c_long, - // Total physical pages - ctypes::_SC_PHYS_PAGES => (config::PHYS_MEMORY_SIZE / config::PAGE_SIZE_4K) as c_long, - // Number of processors in use - ctypes::_SC_NPROCESSORS_ONLN => config::SMP as c_long, - // Avaliable physical pages - ctypes::_SC_AVPHYS_PAGES => { - let mut info: ctypes::sysinfo = core::mem::zeroed(); - ruxos_posix_api::sys_sysinfo(&mut info); - (info.freeram / config::PAGE_SIZE_4K as u64) as c_long - } - // Maximum number of files per process - #[cfg(feature = "fd")] - ctypes::_SC_OPEN_MAX => { - let mut rl: ctypes::rlimit = core::mem::zeroed(); - sys_getrlimit(ctypes::RLIMIT_NOFILE.try_into().unwrap(), &mut rl); - rl.rlim_max as c_long - } - // Maximum number of keys per thread - ctypes::_SC_THREAD_KEYS_MAX => config::PTHREAD_KEY_MAX as c_long, - _ => 0, - } -} diff --git a/ulib/ruxlibc/src/time.rs b/ulib/ruxlibc/src/time.rs deleted file mode 100644 index d124a1c32..000000000 --- a/ulib/ruxlibc/src/time.rs +++ /dev/null @@ -1,74 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use core::ffi::c_int; -use ruxos_posix_api::{sys_clock_gettime, sys_clock_settime, sys_nanosleep}; -#[cfg(feature = "signal")] -use ruxos_posix_api::{sys_getitimer, sys_setitimer}; - -use crate::{ctypes, utils::e}; - -/// Get clock time since booting -#[no_mangle] -pub unsafe extern "C" fn clock_gettime(clk: ctypes::clockid_t, ts: *mut ctypes::timespec) -> c_int { - e(sys_clock_gettime(clk, ts)) -} - -/// Set clock time since booting -#[no_mangle] -pub unsafe extern "C" fn clock_settime(clk: ctypes::clockid_t, ts: *mut ctypes::timespec) -> c_int { - e(sys_clock_settime(clk, ts)) -} - -/// Sleep some nanoseconds -/// -/// TODO: should be woken by signals, and set errno -#[no_mangle] -pub unsafe extern "C" fn nanosleep( - req: *const ctypes::timespec, - rem: *mut ctypes::timespec, -) -> c_int { - e(sys_nanosleep(req, rem)) -} - -/// Set timer to send signal after some time -#[no_mangle] -pub unsafe extern "C" fn setitimer( - _which: c_int, - _new: *const ctypes::itimerval, - _old: *mut ctypes::itimerval, -) -> c_int { - #[cfg(feature = "signal")] - { - if !_old.is_null() { - let res = e(sys_getitimer(_which, _old)); - if res != 0 { - return res; - } - } - e(sys_setitimer(_which, _new)) - } - #[cfg(not(feature = "signal"))] - { - e(0) - } -} - -/// Set timer to send signal after some time -#[no_mangle] -pub unsafe extern "C" fn getitimer(_which: c_int, _curr_value: *mut ctypes::itimerval) -> c_int { - #[cfg(feature = "signal")] - { - e(sys_getitimer(_which, _curr_value)) - } - #[cfg(not(feature = "signal"))] - { - e(0) - } -} diff --git a/ulib/ruxlibc/src/unistd.rs b/ulib/ruxlibc/src/unistd.rs deleted file mode 100644 index 51d53625f..000000000 --- a/ulib/ruxlibc/src/unistd.rs +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Ruxos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use core::ffi::c_int; -use ruxos_posix_api::{sys_exit, sys_getpid, sys_gettid}; -#[cfg(feature = "signal")] -use { - crate::getitimer, - crate::{ctypes, utils::e}, - core::ffi::c_uint, - ruxos_posix_api::sys_setitimer, -}; - -/// Get current thread ID. -#[no_mangle] -pub unsafe extern "C" fn getpid() -> c_int { - sys_getpid() -} - -/// Get current thread ID. -#[no_mangle] -pub unsafe extern "C" fn gettid() -> c_int { - sys_gettid() -} - -/// Abort the current process. -#[no_mangle] -pub unsafe extern "C" fn abort() -> ! { - panic!() -} - -/// Exits the current thread. -#[no_mangle] -pub unsafe extern "C" fn exit(exit_code: c_int) -> ! { - sys_exit(exit_code) -} - -/// Set an alarm clock for delivery of a signal -#[cfg(feature = "signal")] -#[no_mangle] -pub unsafe extern "C" fn alarm(seconds: c_uint) -> c_uint { - let it = ctypes::itimerval { - it_interval: ctypes::timeval { - tv_sec: 0, - tv_usec: 0, - }, - it_value: ctypes::timeval { - tv_sec: seconds as i64, - tv_usec: 0, - }, - }; - let mut old = ctypes::itimerval::default(); - if getitimer(ctypes::ITIMER_REAL as c_int, &mut old) < 0 { - e(sys_setitimer(ctypes::ITIMER_REAL as c_int, &it)) as c_uint - } else { - old.it_value.tv_sec as c_uint - } -} - -/// Schedule signal after given number of microseconds -#[cfg(feature = "signal")] -#[no_mangle] -pub unsafe extern "C" fn ualarm(useconds: c_uint, interval: c_uint) -> c_uint { - let it = ctypes::itimerval { - it_interval: ctypes::timeval { - tv_sec: 0, - tv_usec: interval as i64, - }, - it_value: ctypes::timeval { - tv_sec: 0, - tv_usec: useconds as i64, - }, - }; - let mut old = ctypes::itimerval::default(); - if getitimer(ctypes::ITIMER_REAL as i32, &mut old) < 0 { - e(sys_setitimer(ctypes::ITIMER_REAL as i32, &it)); - 0 - } else { - core::time::Duration::from(old.it_value).as_micros() as c_uint - } -} diff --git a/ulib/ruxmusl/Cargo.toml b/ulib/ruxmusl/Cargo.toml index cdbf28ba1..9660bfa82 100644 --- a/ulib/ruxmusl/Cargo.toml +++ b/ulib/ruxmusl/Cargo.toml @@ -57,6 +57,6 @@ ruxfeat = { path = "../../api/ruxfeat" } ruxos_posix_api = { path = "../../api/ruxos_posix_api" } num_enum = { version = "0.5.11", default-features = false } ruxhal = { path = "../../modules/ruxhal" } -axlog = { path = "../../modules/axlog" } +axlog = { path = "../../crates/axlog" } kernel_guard = "0.1.0" crate_interface = "0.1.1" diff --git a/ulib/ruxmusl/src/aarch64/mod.rs b/ulib/ruxmusl/src/aarch64/mod.rs index a1dbe05f4..c608d7a61 100644 --- a/ulib/ruxmusl/src/aarch64/mod.rs +++ b/ulib/ruxmusl/src/aarch64/mod.rs @@ -1,11 +1,22 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +#![allow(unused_imports)] + pub mod syscall_id; -use core::ffi::{c_char, c_int}; +use core::ffi::{c_char, c_int, c_ulong, c_void}; use ruxos_posix_api::ctypes::{self, gid_t, pid_t, uid_t}; use syscall_id::SyscallId; pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { - debug!("syscall <= syscall_name: {:?}", syscall_id); + debug!("syscall <= syscall_name: {syscall_id:?}"); unsafe { match syscall_id { @@ -15,7 +26,7 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { ruxos_posix_api::sys_getcwd(args[0] as *mut core::ffi::c_char, args[1]) as _ } #[cfg(feature = "epoll")] - SyscallId::EPOLL_CREATE1 => ruxos_posix_api::sys_epoll_create(args[0] as c_int) as _, + SyscallId::EPOLL_CREATE1 => ruxos_posix_api::sys_epoll_create1(args[0] as c_int) as _, #[cfg(feature = "epoll")] SyscallId::EPOLL_CTL => ruxos_posix_api::sys_epoll_ctl( args[0] as c_int, @@ -45,6 +56,13 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { #[cfg(feature = "fd")] SyscallId::IOCTL => ruxos_posix_api::sys_ioctl(args[0] as c_int, args[1], args[2]) as _, #[cfg(feature = "fs")] + SyscallId::MKNODAT => ruxos_posix_api::sys_mknodat( + args[0] as c_int, + args[1] as *const core::ffi::c_char, + args[2] as ctypes::mode_t, + args[3] as ctypes::dev_t, + ) as _, + #[cfg(feature = "fs")] SyscallId::MKDIRAT => ruxos_posix_api::sys_mkdirat( args[0] as c_int, args[1] as *const core::ffi::c_char, @@ -57,6 +75,24 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[2] as c_int, ) as _, #[cfg(feature = "fs")] + SyscallId::FCHMODAT => ruxos_posix_api::sys_fchmodat( + args[0] as c_int, + args[1] as *const core::ffi::c_char, + args[2] as ctypes::mode_t, + ) as _, + #[cfg(feature = "fs")] + SyscallId::UMOUNT2 => { + ruxos_posix_api::sys_umount2(args[0] as *const c_char, args[1] as c_int) as _ + } + #[cfg(feature = "fs")] + SyscallId::MOUNT => ruxos_posix_api::sys_mount( + args[0] as *const c_char, + args[1] as *const c_char, + args[2] as *const c_char, + args[3] as c_ulong, + args[4] as *const c_void, + ) as _, + #[cfg(feature = "fs")] SyscallId::FCHOWNAT => ruxos_posix_api::sys_fchownat( args[0] as c_int, args[1] as *const core::ffi::c_char, @@ -72,6 +108,10 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[3] as *const core::ffi::c_char, ) as _, #[cfg(feature = "fs")] + SyscallId::FTRUNCATE => { + ruxos_posix_api::sys_ftruncate(args[0] as c_int, args[1] as ctypes::off_t) as _ + } + #[cfg(feature = "fs")] SyscallId::FACCESSAT => ruxos_posix_api::sys_faccessat( args[0] as c_int, args[1] as *const c_char, @@ -82,7 +122,7 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { SyscallId::CHDIR => ruxos_posix_api::sys_chdir(args[0] as *const c_char) as _, #[cfg(feature = "fs")] SyscallId::OPENAT => ruxos_posix_api::sys_openat( - args[0], + args[0] as c_int, args[1] as *const core::ffi::c_char, args[2] as c_int, args[3] as ctypes::mode_t, @@ -202,6 +242,8 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { ruxos_posix_api::sys_pthread_exit(args[0] as *mut core::ffi::c_void) as _ } #[cfg(feature = "multitask")] + SyscallId::EXIT_GROUP => ruxos_posix_api::sys_exit_group(args[0] as c_int), + #[cfg(feature = "multitask")] SyscallId::SET_TID_ADDRESS => ruxos_posix_api::sys_set_tid_address(args[0]) as _, #[cfg(feature = "multitask")] SyscallId::FUTEX => ruxos_posix_api::sys_futex( @@ -216,6 +258,8 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[0] as *const ctypes::timespec, args[1] as *mut ctypes::timespec, ) as _, + #[cfg(feature = "signal")] + SyscallId::SETITIMER => ruxos_posix_api::sys_setitimer(args[0] as _, args[1] as _) as _, SyscallId::CLOCK_SETTIME => ruxos_posix_api::sys_clock_settime( args[0] as ctypes::clockid_t, args[1] as *const ctypes::timespec, @@ -224,10 +268,22 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[0] as ctypes::clockid_t, args[1] as *mut ctypes::timespec, ) as _, + SyscallId::CLOCK_GETRES => ruxos_posix_api::sys_clock_getres( + args[0] as ctypes::clockid_t, + args[1] as *mut ctypes::timespec, + ) as _, + SyscallId::CLOCK_NANOSLEEP => ruxos_posix_api::sys_clock_nanosleep( + args[0] as ctypes::clockid_t, + args[1] as c_int, + args[2] as *const ctypes::timespec, + args[3] as *mut ctypes::timespec, + ) as _, SyscallId::SCHED_YIELD => ruxos_posix_api::sys_sched_yield() as _, #[cfg(feature = "signal")] SyscallId::KILL => ruxos_posix_api::sys_kill(args[0] as pid_t, args[1] as c_int) as _, #[cfg(feature = "signal")] + SyscallId::TKILL => ruxos_posix_api::sys_tkill(args[0] as pid_t, args[1] as c_int) as _, + #[cfg(feature = "signal")] SyscallId::SIGALTSTACK => ruxos_posix_api::sys_sigaltstack( args[0] as *const core::ffi::c_void, args[1] as *mut core::ffi::c_void, @@ -253,6 +309,7 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { ruxos_posix_api::sys_setpgid(args[0] as pid_t, args[1] as pid_t) as _ } SyscallId::GETPGID => ruxos_posix_api::sys_getpgid(args[0] as pid_t) as _, + SyscallId::SETSID => ruxos_posix_api::sys_setsid() as _, SyscallId::UNAME => ruxos_posix_api::sys_uname(args[0] as *mut core::ffi::c_void) as _, SyscallId::GETRLIMIT => { ruxos_posix_api::sys_getrlimit(args[0] as c_int, args[1] as *mut ctypes::rlimit) @@ -276,6 +333,13 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { as _ } #[cfg(feature = "net")] + SyscallId::SOCKETPAIR => ruxos_posix_api::sys_socketpair( + args[0] as _, + args[1] as _, + args[2] as _, + core::slice::from_raw_parts_mut(args[3] as *mut c_int, 2), + ) as _, + #[cfg(feature = "net")] SyscallId::BIND => ruxos_posix_api::sys_bind( args[0] as c_int, args[1] as *const ctypes::sockaddr, @@ -336,6 +400,14 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[4] as ctypes::socklen_t, ) as _, #[cfg(feature = "net")] + SyscallId::GETSOCKOPT => ruxos_posix_api::sys_getsockopt( + args[0] as c_int, + args[1] as c_int, + args[2] as c_int, + args[3] as *mut core::ffi::c_void, + args[4] as *mut ctypes::socklen_t, + ) as _, + #[cfg(feature = "net")] SyscallId::SHUTDOWN => { ruxos_posix_api::sys_shutdown(args[0] as c_int, args[1] as c_int) as _ } @@ -345,6 +417,12 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[1] as *const ctypes::msghdr, args[2] as c_int, ) as _, + #[cfg(feature = "net")] + SyscallId::RECVMSG => ruxos_posix_api::sys_recvmsg( + args[0] as c_int, + args[1] as *mut ctypes::msghdr, + args[2] as c_int, + ) as _, #[cfg(feature = "alloc")] SyscallId::MUNMAP => ruxos_posix_api::sys_munmap( args[0] as *mut core::ffi::c_void, @@ -398,6 +476,13 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[1] as ctypes::size_t, args[2] as c_int, ) as _, + #[cfg(feature = "multitask")] + SyscallId::WAIT4 => ruxos_posix_api::sys_wait4( + args[0] as ctypes::pid_t, + args[1] as *mut c_int, + args[2] as c_int, + args[3] as *mut ctypes::rusage, + ) as _, SyscallId::PRLIMIT64 => ruxos_posix_api::sys_prlimit64( args[0] as ctypes::pid_t, args[1] as c_int, @@ -409,6 +494,10 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[1] as ctypes::size_t, args[2] as c_int, ) as _, + #[cfg(feature = "fs")] + SyscallId::MEMBARRIER => { + ruxos_posix_api::sys_membarrier(args[0] as c_int, args[1] as c_int) as _ + } } } } diff --git a/ulib/ruxmusl/src/aarch64/syscall_id.rs b/ulib/ruxmusl/src/aarch64/syscall_id.rs index c4b8e4307..a588ce0b5 100644 --- a/ulib/ruxmusl/src/aarch64/syscall_id.rs +++ b/ulib/ruxmusl/src/aarch64/syscall_id.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + use num_enum::TryFromPrimitive; // TODO: syscall id are architecture-dependent @@ -24,16 +33,26 @@ pub enum SyscallId { #[cfg(feature = "fd")] IOCTL = 29, #[cfg(feature = "fs")] + MKNODAT = 33, + #[cfg(feature = "fs")] MKDIRAT = 34, #[cfg(feature = "fs")] UNLINKAT = 35, #[cfg(feature = "fs")] + UMOUNT2 = 39, + #[cfg(feature = "fs")] + MOUNT = 40, + #[cfg(feature = "fs")] RENAMEAT = 38, #[cfg(feature = "fs")] + FTRUNCATE = 46, + #[cfg(feature = "fs")] FACCESSAT = 48, #[cfg(feature = "fs")] CHDIR = 49, #[cfg(feature = "fs")] + FCHMODAT = 53, + #[cfg(feature = "fs")] FCHOWNAT = 54, #[cfg(feature = "fs")] OPENAT = 56, @@ -74,16 +93,24 @@ pub enum SyscallId { CAP_GET = 90, EXIT = 93, #[cfg(feature = "multitask")] + EXIT_GROUP = 94, + #[cfg(feature = "multitask")] SET_TID_ADDRESS = 96, #[cfg(feature = "multitask")] FUTEX = 98, NANO_SLEEP = 101, + #[cfg(feature = "signal")] + SETITIMER = 103, CLOCK_SETTIME = 112, CLOCK_GETTIME = 113, + CLOCK_GETRES = 114, + CLOCK_NANOSLEEP = 115, SCHED_YIELD = 124, #[cfg(feature = "signal")] KILL = 129, #[cfg(feature = "signal")] + TKILL = 130, + #[cfg(feature = "signal")] SIGALTSTACK = 132, #[cfg(feature = "signal")] RT_SIGACTION = 134, @@ -94,6 +121,7 @@ pub enum SyscallId { TIMES = 153, SETPGID = 154, GETPGID = 155, + SETSID = 157, UNAME = 160, GETRLIMIT = 163, SETRLIMIT = 164, @@ -110,6 +138,8 @@ pub enum SyscallId { #[cfg(feature = "net")] SOCKET = 198, #[cfg(feature = "net")] + SOCKETPAIR = 199, + #[cfg(feature = "net")] BIND = 200, #[cfg(feature = "net")] LISTEN = 201, @@ -128,9 +158,13 @@ pub enum SyscallId { #[cfg(feature = "net")] SETSOCKOPT = 208, #[cfg(feature = "net")] + GETSOCKOPT = 209, + #[cfg(feature = "net")] SHUTDOWN = 210, #[cfg(feature = "net")] SENDMSG = 211, + #[cfg(feature = "net")] + RECVMSG = 212, #[cfg(feature = "alloc")] MUNMAP = 215, #[cfg(feature = "alloc")] @@ -147,6 +181,10 @@ pub enum SyscallId { MSYNC = 227, #[cfg(feature = "alloc")] MADVISE = 233, + #[cfg(feature = "multitask")] + WAIT4 = 260, PRLIMIT64 = 261, GETRANDOM = 278, + #[cfg(feature = "fs")] + MEMBARRIER = 283, } diff --git a/ulib/ruxmusl/src/dummy/mod.rs b/ulib/ruxmusl/src/dummy/mod.rs index 2ad0c5968..925aa8eae 100644 --- a/ulib/ruxmusl/src/dummy/mod.rs +++ b/ulib/ruxmusl/src/dummy/mod.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + pub mod syscall_id; use core::ffi::c_int; diff --git a/ulib/ruxmusl/src/dummy/syscall_id.rs b/ulib/ruxmusl/src/dummy/syscall_id.rs index 591ef670a..b808f20c1 100644 --- a/ulib/ruxmusl/src/dummy/syscall_id.rs +++ b/ulib/ruxmusl/src/dummy/syscall_id.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + use num_enum::TryFromPrimitive; // TODO: syscall id are architecture-dependent diff --git a/ulib/ruxmusl/src/lib.rs b/ulib/ruxmusl/src/lib.rs index 20853928f..36b2cdd31 100644 --- a/ulib/ruxmusl/src/lib.rs +++ b/ulib/ruxmusl/src/lib.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + //! Syscall dispatch crate //! //! Dispatch musl syscall instruction to Ruxos posix-api diff --git a/ulib/ruxmusl/src/riscv64/mod.rs b/ulib/ruxmusl/src/riscv64/mod.rs index 186d8c147..9f06379b7 100644 --- a/ulib/ruxmusl/src/riscv64/mod.rs +++ b/ulib/ruxmusl/src/riscv64/mod.rs @@ -1,11 +1,20 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + pub mod syscall_id; -use core::ffi::c_int; -use ruxos_posix_api::ctypes; +use core::ffi::{c_char, c_int}; +use ruxos_posix_api::ctypes::{self, pid_t}; use syscall_id::SyscallId; pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { - debug!("syscall <= syscall_name: {:?}", syscall_id); + debug!("syscall <= syscall_name: {syscall_id:?}"); unsafe { match syscall_id { @@ -15,7 +24,7 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { ruxos_posix_api::sys_getcwd(args[0] as *mut core::ffi::c_char, args[1]) as _ } #[cfg(feature = "epoll")] - SyscallId::EPOLL_CREATE1 => ruxos_posix_api::sys_epoll_create(args[0] as c_int) as _, + SyscallId::EPOLL_CREATE1 => ruxos_posix_api::sys_epoll_create1(args[0] as c_int) as _, #[cfg(feature = "epoll")] SyscallId::EPOLL_CTL => ruxos_posix_api::sys_epoll_ctl( args[0] as c_int, @@ -73,7 +82,7 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { ) as _, #[cfg(feature = "fs")] SyscallId::OPENAT => ruxos_posix_api::sys_openat( - args[0], + args[0] as c_int, args[1] as *const core::ffi::c_char, args[2] as c_int, args[3] as ctypes::mode_t, @@ -113,6 +122,12 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[1] as *const ctypes::iovec, args[2] as c_int, ) as _, + #[cfg(feature = "fs")] + #[allow(unreachable_code)] + SyscallId::EXECVE => { + use core::ffi::c_char; + ruxos_posix_api::sys_execve(args[0] as *const c_char, args[1], args[2]) as _ + } #[cfg(feature = "fd")] SyscallId::WRITEV => ruxos_posix_api::sys_writev( args[0] as c_int, @@ -191,6 +206,12 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[0] as ctypes::clockid_t, args[1] as *mut ctypes::timespec, ) as _, + SyscallId::CLOCK_NANOSLEEP => ruxos_posix_api::sys_clock_nanosleep( + args[0] as ctypes::clockid_t, + args[1] as c_int, + args[2] as *const ctypes::timespec, + args[3] as *mut ctypes::timespec, + ) as _, SyscallId::SCHED_YIELD => ruxos_posix_api::sys_sched_yield() as _, #[cfg(feature = "signal")] SyscallId::SIGALTSTACK => ruxos_posix_api::sys_sigaltstack( @@ -232,6 +253,13 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { as _ } #[cfg(feature = "net")] + SyscallId::SOCKETPAIR => ruxos_posix_api::sys_socketpair( + args[0] as _, + args[1] as _, + args[2] as _, + core::slice::from_raw_parts_mut(args[3] as *mut c_int, 2), + ) as _, + #[cfg(feature = "net")] SyscallId::BIND => ruxos_posix_api::sys_bind( args[0] as c_int, args[1] as *const ctypes::sockaddr, @@ -283,6 +311,10 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[4] as *mut ctypes::sockaddr, args[5] as *mut ctypes::socklen_t, ) as _, + SyscallId::GETTID => ruxos_posix_api::sys_gettid() as _, + SyscallId::GETPPID => ruxos_posix_api::sys_getppid() as _, + #[cfg(feature = "multitask")] + SyscallId::EXIT_GROUP => ruxos_posix_api::sys_exit_group(args[0] as c_int), #[cfg(feature = "net")] SyscallId::SETSOCKOPT => ruxos_posix_api::sys_setsockopt( args[0] as c_int, @@ -292,6 +324,14 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[4] as ctypes::socklen_t, ) as _, #[cfg(feature = "net")] + SyscallId::GETSOCKOPT => ruxos_posix_api::sys_getsockopt( + args[0] as c_int, + args[1] as c_int, + args[2] as c_int, + args[3] as *mut core::ffi::c_void, + args[4] as *mut ctypes::socklen_t, + ) as _, + #[cfg(feature = "net")] SyscallId::SHUTDOWN => { ruxos_posix_api::sys_shutdown(args[0] as c_int, args[1] as c_int) as _ } @@ -343,12 +383,31 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[1] as ctypes::size_t, args[2] as c_int, ) as _, + SyscallId::WAIT4 => ruxos_posix_api::sys_wait4( + args[0] as ctypes::pid_t, + args[1] as *mut c_int, + args[2] as c_int, + args[3] as *mut ctypes::rusage, + ) as _, SyscallId::PRLIMIT64 => ruxos_posix_api::sys_prlimit64( args[0] as ctypes::pid_t, args[1] as c_int, args[2] as *const ctypes::rlimit, args[3] as *mut ctypes::rlimit, ) as _, + #[cfg(feature = "signal")] + SyscallId::KILL => ruxos_posix_api::sys_kill(args[0] as pid_t, args[1] as c_int) as _, + SyscallId::GETPGID => ruxos_posix_api::sys_getpgid(args[0] as pid_t) as _, + SyscallId::FACCESSAT => ruxos_posix_api::sys_faccessat( + args[0] as c_int, + args[1] as *const c_char, + args[2] as c_int, + args[3] as c_int, + ) as _, + #[cfg(feature = "fs")] + SyscallId::CHDIR => ruxos_posix_api::sys_chdir(args[0] as *const c_char) as _, + SyscallId::GETUID => ruxos_posix_api::sys_getuid() as _, + SyscallId::GETGID => ruxos_posix_api::sys_getgid() as _, } } } diff --git a/ulib/ruxmusl/src/riscv64/syscall_id.rs b/ulib/ruxmusl/src/riscv64/syscall_id.rs index ed5087e4b..3a2f9be72 100644 --- a/ulib/ruxmusl/src/riscv64/syscall_id.rs +++ b/ulib/ruxmusl/src/riscv64/syscall_id.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + use num_enum::TryFromPrimitive; // TODO: syscall id are architecture-dependent @@ -30,6 +39,10 @@ pub enum SyscallId { #[cfg(feature = "fs")] RENAMEAT = 38, #[cfg(feature = "fs")] + FACCESSAT = 48, + #[cfg(feature = "fs")] + CHDIR = 49, + #[cfg(feature = "fs")] FCHOWNAT = 54, #[cfg(feature = "fs")] OPENAT = 56, @@ -63,31 +76,43 @@ pub enum SyscallId { FDATASYNC = 83, EXIT = 93, #[cfg(feature = "multitask")] + EXIT_GROUP = 94, + #[cfg(feature = "multitask")] SET_TID_ADDRESS = 96, #[cfg(feature = "multitask")] FUTEX = 98, NANO_SLEEP = 101, CLOCK_SETTIME = 112, CLOCK_GETTIME = 113, + CLOCK_NANOSLEEP = 115, SCHED_YIELD = 124, #[cfg(feature = "signal")] + KILL = 129, + #[cfg(feature = "signal")] SIGALTSTACK = 132, #[cfg(feature = "signal")] RT_SIGACTION = 134, #[cfg(feature = "signal")] RT_SIGPROCMASK = 135, + GETPGID = 155, UNAME = 160, GETRLIMIT = 163, SETRLIMIT = 164, UMASK = 166, #[cfg(feature = "multitask")] GETPID = 172, + GETPPID = 173, + GETUID = 174, GETEUID = 175, + GETGID = 176, GETEGID = 177, + GETTID = 178, SYSINFO = 179, #[cfg(feature = "net")] SOCKET = 198, #[cfg(feature = "net")] + SOCKETPAIR = 199, + #[cfg(feature = "net")] BIND = 200, #[cfg(feature = "net")] LISTEN = 201, @@ -106,6 +131,8 @@ pub enum SyscallId { #[cfg(feature = "net")] SETSOCKOPT = 208, #[cfg(feature = "net")] + GETSOCKOPT = 209, + #[cfg(feature = "net")] SHUTDOWN = 210, #[cfg(feature = "net")] SENDMSG = 211, @@ -115,11 +142,15 @@ pub enum SyscallId { MREMAP = 216, #[cfg(feature = "multitask")] CLONE = 220, + #[cfg(feature = "fs")] + EXECVE = 221, #[cfg(feature = "alloc")] MMAP = 222, #[cfg(feature = "alloc")] MADVISE = 233, #[cfg(feature = "alloc")] MPROTECT = 226, + #[cfg(feature = "multitask")] + WAIT4 = 260, PRLIMIT64 = 261, } diff --git a/ulib/ruxmusl/src/trap.rs b/ulib/ruxmusl/src/trap.rs index 17fb9aecd..baab00dd9 100644 --- a/ulib/ruxmusl/src/trap.rs +++ b/ulib/ruxmusl/src/trap.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + //! Traphandle implementation //! //! Used to support musl syscall @@ -21,6 +30,9 @@ impl ruxhal::trap::TrapHandler for TrapHandlerImpl { #[cfg(feature = "musl")] fn handle_syscall(syscall_id: usize, args: [usize; 6]) -> isize { let id = SyscallId::try_from(syscall_id).unwrap_or(SyscallId::INVALID); + if id == SyscallId::INVALID { + info!("Invalid syscall id: {syscall_id}"); + } crate::syscall(id, args) } } diff --git a/ulib/ruxmusl/src/x86_64/mod.rs b/ulib/ruxmusl/src/x86_64/mod.rs index 16a467dfe..9efa117cf 100644 --- a/ulib/ruxmusl/src/x86_64/mod.rs +++ b/ulib/ruxmusl/src/x86_64/mod.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + pub mod syscall_id; #[allow(unused_imports)] @@ -7,7 +16,7 @@ use syscall_id::SyscallId; #[allow(dead_code)] pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { - debug!("x86 syscall <= syscall_name: {:?}", syscall_id); + debug!("x86 syscall <= syscall_name: {syscall_id:?}",); unsafe { match syscall_id { @@ -192,7 +201,13 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { ruxos_posix_api::sys_socket(args[0] as c_int, args[1] as c_int, args[2] as c_int) as _ } - + #[cfg(feature = "net")] + SyscallId::SOCKETPAIR => ruxos_posix_api::sys_socketpair( + args[0] as _, + args[1] as _, + args[2] as _, + core::slice::from_raw_parts_mut(args[3] as *mut c_int, 2), + ) as _, #[cfg(feature = "net")] SyscallId::CONNECT => ruxos_posix_api::sys_connect( args[0] as c_int, @@ -274,6 +289,15 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[4] as ctypes::socklen_t, ) as _, + #[cfg(feature = "net")] + SyscallId::GETSOCKOPT => ruxos_posix_api::sys_getsockopt( + args[0] as c_int, + args[1] as c_int, + args[2] as c_int, + args[3] as *mut c_void, + args[4] as *mut ctypes::socklen_t, + ) as _, + #[cfg(feature = "multitask")] SyscallId::CLONE => ruxos_posix_api::sys_clone( args[0] as c_int, @@ -423,7 +447,7 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { ) as _, #[cfg(feature = "epoll")] - SyscallId::EPOLL_CREATE => ruxos_posix_api::sys_epoll_create(args[0] as c_int) as _, + SyscallId::EPOLL_CREATE => ruxos_posix_api::sys_epoll_create1(args[0] as c_int) as _, #[cfg(feature = "fs")] SyscallId::GETDENTS64 => ruxos_posix_api::sys_getdents64( @@ -445,6 +469,13 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { args[1] as *mut ctypes::timespec, ) as _, + SyscallId::CLOCK_NANOSLEEP => ruxos_posix_api::sys_clock_nanosleep( + args[0] as ctypes::clockid_t, + args[1] as c_int, + args[2] as *const ctypes::timespec, + args[3] as *mut ctypes::timespec, + ) as _, + #[cfg(feature = "epoll")] SyscallId::EPOLL_WAIT => ruxos_posix_api::sys_epoll_wait( args[0] as c_int, @@ -463,7 +494,7 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { #[cfg(feature = "fs")] SyscallId::OPENAT => ruxos_posix_api::sys_openat( - args[0], + args[0] as c_int, args[1] as *const core::ffi::c_char, args[2] as c_int, args[3] as ctypes::mode_t, @@ -545,7 +576,7 @@ pub fn syscall(syscall_id: SyscallId, args: [usize; 6]) -> isize { ) as _, #[cfg(feature = "epoll")] - SyscallId::EPOLL_CREATE1 => ruxos_posix_api::sys_epoll_create(args[0] as c_int) as _, + SyscallId::EPOLL_CREATE1 => ruxos_posix_api::sys_epoll_create1(args[0] as c_int) as _, #[cfg(feature = "fd")] SyscallId::DUP3 => { diff --git a/ulib/ruxmusl/src/x86_64/syscall_id.rs b/ulib/ruxmusl/src/x86_64/syscall_id.rs index 1526154cb..54acb4215 100644 --- a/ulib/ruxmusl/src/x86_64/syscall_id.rs +++ b/ulib/ruxmusl/src/x86_64/syscall_id.rs @@ -1,3 +1,12 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Ruxos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + use num_enum::TryFromPrimitive; // TODO: syscall id are architecture-dependent @@ -123,9 +132,15 @@ pub enum SyscallId { #[cfg(feature = "net")] GETPEERNAME = 52, + #[cfg(feature = "net")] + SOCKETPAIR = 53, + #[cfg(feature = "net")] SETSOCKOPT = 54, + #[cfg(feature = "net")] + GETSOCKOPT = 55, + // TODO: check clone #[cfg(feature = "multitask")] CLONE = 56, @@ -223,6 +238,8 @@ pub enum SyscallId { CLOCK_GETTIME = 228, + CLOCK_NANOSLEEP = 230, + #[cfg(feature = "epoll")] EPOLL_WAIT = 232,