├── .cargo └── config.toml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE.txt ├── Makefile ├── README.md ├── bsd-kernel ├── .gitignore ├── Cargo.toml ├── rust-toolchain ├── rustfmt.toml └── src │ ├── allocator.rs │ ├── character_device.rs │ ├── error.rs │ ├── io.rs │ ├── lib.rs │ ├── module.rs │ └── uio.rs ├── build.sh ├── hello.c ├── kernel-sys ├── .gitignore ├── Cargo.toml ├── build.rs ├── rustfmt.toml ├── src │ └── lib.rs └── wrapper.h ├── module-hello ├── Cargo.toml └── src │ ├── lib.rs │ └── module.rs ├── rust-toolchain.toml ├── rustfmt.toml └── x86_64-kernel-freebsd.json /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-kernel-freebsd.json" 3 | 4 | [unstable] 5 | build-std = ["core", "compiler_builtins", "alloc"] 6 | build-std-features = ["compiler-builtins-mem"] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | export_syms 3 | *.o 4 | *.ko 5 | machine 6 | x86 7 | opt_global.h 8 | kernel-sys/src/bindings.rs -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "atty" 16 | version = "0.2.14" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 19 | dependencies = [ 20 | "hermit-abi", 21 | "libc", 22 | "winapi", 23 | ] 24 | 25 | [[package]] 26 | name = "autocfg" 27 | version = "1.1.0" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 30 | 31 | [[package]] 32 | name = "bindgen" 33 | version = "0.60.1" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" 36 | dependencies = [ 37 | "bitflags", 38 | "cexpr", 39 | "clang-sys", 40 | "clap", 41 | "env_logger", 42 | "lazy_static", 43 | "lazycell", 44 | "log", 45 | "peeking_take_while", 46 | "proc-macro2", 47 | "quote", 48 | "regex", 49 | "rustc-hash", 50 | "shlex", 51 | "which", 52 | ] 53 | 54 | [[package]] 55 | name = "bitflags" 56 | version = "1.3.2" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 59 | 60 | [[package]] 61 | name = "bsd-kernel" 62 | version = "0.1.0" 63 | dependencies = [ 64 | "kernel-sys", 65 | "libc", 66 | "spin 0.7.1", 67 | ] 68 | 69 | [[package]] 70 | name = "bsd-rust" 71 | version = "0.1.0" 72 | dependencies = [ 73 | "bsd-kernel", 74 | "lazy_static", 75 | "libc", 76 | ] 77 | 78 | [[package]] 79 | name = "cexpr" 80 | version = "0.6.0" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 83 | dependencies = [ 84 | "nom", 85 | ] 86 | 87 | [[package]] 88 | name = "cfg-if" 89 | version = "1.0.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 92 | 93 | [[package]] 94 | name = "clang-sys" 95 | version = "1.3.3" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" 98 | dependencies = [ 99 | "glob", 100 | "libc", 101 | "libloading", 102 | ] 103 | 104 | [[package]] 105 | name = "clap" 106 | version = "3.2.12" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "ab8b79fe3946ceb4a0b1c080b4018992b8d27e9ff363644c1c9b6387c854614d" 109 | dependencies = [ 110 | "atty", 111 | "bitflags", 112 | "clap_lex", 113 | "indexmap", 114 | "strsim", 115 | "termcolor", 116 | "textwrap", 117 | ] 118 | 119 | [[package]] 120 | name = "clap_lex" 121 | version = "0.2.4" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 124 | dependencies = [ 125 | "os_str_bytes", 126 | ] 127 | 128 | [[package]] 129 | name = "either" 130 | version = "1.7.0" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" 133 | 134 | [[package]] 135 | name = "env_logger" 136 | version = "0.9.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" 139 | dependencies = [ 140 | "atty", 141 | "humantime", 142 | "log", 143 | "regex", 144 | "termcolor", 145 | ] 146 | 147 | [[package]] 148 | name = "glob" 149 | version = "0.3.0" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" 152 | 153 | [[package]] 154 | name = "hashbrown" 155 | version = "0.12.2" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" 158 | 159 | [[package]] 160 | name = "hermit-abi" 161 | version = "0.1.19" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 164 | dependencies = [ 165 | "libc", 166 | ] 167 | 168 | [[package]] 169 | name = "humantime" 170 | version = "2.1.0" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 173 | 174 | [[package]] 175 | name = "indexmap" 176 | version = "1.9.1" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 179 | dependencies = [ 180 | "autocfg", 181 | "hashbrown", 182 | ] 183 | 184 | [[package]] 185 | name = "kernel-sys" 186 | version = "0.1.0" 187 | dependencies = [ 188 | "bindgen", 189 | "libc", 190 | ] 191 | 192 | [[package]] 193 | name = "lazy_static" 194 | version = "1.4.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 197 | dependencies = [ 198 | "spin 0.5.2", 199 | ] 200 | 201 | [[package]] 202 | name = "lazycell" 203 | version = "1.3.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 206 | 207 | [[package]] 208 | name = "libc" 209 | version = "0.2.126" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" 212 | 213 | [[package]] 214 | name = "libloading" 215 | version = "0.7.3" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" 218 | dependencies = [ 219 | "cfg-if", 220 | "winapi", 221 | ] 222 | 223 | [[package]] 224 | name = "log" 225 | version = "0.4.17" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 228 | dependencies = [ 229 | "cfg-if", 230 | ] 231 | 232 | [[package]] 233 | name = "memchr" 234 | version = "2.5.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 237 | 238 | [[package]] 239 | name = "minimal-lexical" 240 | version = "0.2.1" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 243 | 244 | [[package]] 245 | name = "nom" 246 | version = "7.1.1" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" 249 | dependencies = [ 250 | "memchr", 251 | "minimal-lexical", 252 | ] 253 | 254 | [[package]] 255 | name = "os_str_bytes" 256 | version = "6.1.0" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" 259 | 260 | [[package]] 261 | name = "peeking_take_while" 262 | version = "0.1.2" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" 265 | 266 | [[package]] 267 | name = "proc-macro2" 268 | version = "1.0.40" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" 271 | dependencies = [ 272 | "unicode-ident", 273 | ] 274 | 275 | [[package]] 276 | name = "quote" 277 | version = "1.0.20" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" 280 | dependencies = [ 281 | "proc-macro2", 282 | ] 283 | 284 | [[package]] 285 | name = "regex" 286 | version = "1.6.0" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 289 | dependencies = [ 290 | "aho-corasick", 291 | "memchr", 292 | "regex-syntax", 293 | ] 294 | 295 | [[package]] 296 | name = "regex-syntax" 297 | version = "0.6.27" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 300 | 301 | [[package]] 302 | name = "rustc-hash" 303 | version = "1.1.0" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 306 | 307 | [[package]] 308 | name = "shlex" 309 | version = "1.1.0" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" 312 | 313 | [[package]] 314 | name = "spin" 315 | version = "0.5.2" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 318 | 319 | [[package]] 320 | name = "spin" 321 | version = "0.7.1" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162" 324 | 325 | [[package]] 326 | name = "strsim" 327 | version = "0.10.0" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 330 | 331 | [[package]] 332 | name = "termcolor" 333 | version = "1.1.3" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 336 | dependencies = [ 337 | "winapi-util", 338 | ] 339 | 340 | [[package]] 341 | name = "textwrap" 342 | version = "0.15.0" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" 345 | 346 | [[package]] 347 | name = "unicode-ident" 348 | version = "1.0.1" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" 351 | 352 | [[package]] 353 | name = "which" 354 | version = "4.2.5" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" 357 | dependencies = [ 358 | "either", 359 | "lazy_static", 360 | "libc", 361 | ] 362 | 363 | [[package]] 364 | name = "winapi" 365 | version = "0.3.9" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 368 | dependencies = [ 369 | "winapi-i686-pc-windows-gnu", 370 | "winapi-x86_64-pc-windows-gnu", 371 | ] 372 | 373 | [[package]] 374 | name = "winapi-i686-pc-windows-gnu" 375 | version = "0.4.0" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 378 | 379 | [[package]] 380 | name = "winapi-util" 381 | version = "0.1.5" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 384 | dependencies = [ 385 | "winapi", 386 | ] 387 | 388 | [[package]] 389 | name = "winapi-x86_64-pc-windows-gnu" 390 | version = "0.4.0" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 393 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "bsd-kernel", 4 | "kernel-sys", 5 | "module-hello", 6 | ] 7 | default-members = [ 8 | "module-hello", 9 | ] 10 | 11 | [profile.dev] 12 | panic = "abort" 13 | 14 | [profile.release] 15 | panic = "abort" 16 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2022 NCC Group 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OBJECTDIR?=target/objects 2 | 3 | KMOD=hello 4 | SRCS=hello.c 5 | OBJS=$(OBJECTDIR)/*.o 6 | 7 | 8 | .include 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## FreeBSD kernel module in Rust 2 | 3 | This repo is mostly an updated version of https://github.com/johalun/echo 4 | 5 | It has been updated to Rust 2021 with new bindings to the kernel headers and 6 | tested with Rust version `1.64.0-nightly (b1dd22e66 2022-07-09)` 7 | 8 | For more information, see the [accompanying blog post](https://research.nccgroup.com/2022/08/31/writing-freebsd-kernel-modules-in-rust/). 9 | 10 | ### Setup 11 | * Install Rust via Rustup 12 | * `rustup component add rust-src` 13 | * Generate the kernel bindings: 14 | ```bash 15 | cargo build -p kernel-sys --target x86_64-unknown-freebsd 16 | ``` 17 | 18 | ### Run 19 | 20 | ```bash 21 | ./build.sh 22 | sudo make load 23 | echo "hi rust" > /dev/rustmodule 24 | cat /dev/rustmodule 25 | sudo make unload 26 | ``` 27 | 28 | ### Licence 29 | This source code is provided under the terms of the [BSD 2-Clause licence](LICENSE.txt) 30 | and is based on [public-domain work](https://github.com/johalun/echo) by Johannes Lundberg. 31 | -------------------------------------------------------------------------------- /bsd-kernel/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | -------------------------------------------------------------------------------- /bsd-kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bsd-kernel" 3 | version = "0.1.0" 4 | authors = ["David Young "] 5 | edition = "2021" 6 | license = "BSD-2-Clause" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | kernel-sys = { path = "../kernel-sys" } 12 | libc = "0.2" 13 | spin = "0.7" -------------------------------------------------------------------------------- /bsd-kernel/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly 2 | -------------------------------------------------------------------------------- /bsd-kernel/rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 -------------------------------------------------------------------------------- /bsd-kernel/src/allocator.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | use core::alloc::{GlobalAlloc, Layout}; 27 | 28 | pub struct KernelAllocator; 29 | 30 | unsafe impl GlobalAlloc for KernelAllocator { 31 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 32 | kernel_sys::malloc( 33 | layout.size(), 34 | &mut kernel_sys::M_DEVBUF[0], 35 | kernel_sys::M_WAITOK, 36 | ) as *mut u8 37 | } 38 | 39 | unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { 40 | kernel_sys::free( 41 | ptr as *mut libc::c_void, 42 | &mut kernel_sys::M_DEVBUF[0], 43 | ); 44 | } 45 | } 46 | 47 | /// from `sys/malloc.h` 48 | /// ```c,ignore 49 | /// #define M_NOWAIT 0x0001 /* do not block */ 50 | /// #define M_WAITOK 0x0002 /* ok to block */ 51 | /// #define M_ZERO 0x0100 /* bzero the allocation */ 52 | /// #define M_NOVM 0x0200 /* don't ask VM for pages */ 53 | /// #define M_USE_RESERVE 0x0400 /* can alloc out of reserve memory */ 54 | /// #define M_NODUMP 0x0800 /* don't dump pages in this allocation */ 55 | /// #define M_FIRSTFIT 0x1000 /* only for vmem, fast fit */ 56 | /// #define M_BESTFIT 0x2000 /* only for vmem, low fragmentation */ 57 | /// #define M_EXEC 0x4000 /* allocate executable space */ 58 | /// #define M_NEXTFIT 0x8000 /* only for vmem, follow cursor */ 59 | /// ``` 60 | 61 | #[alloc_error_handler] 62 | fn oom(_layout: Layout) -> ! { 63 | panic!("Out of memory!"); 64 | } 65 | -------------------------------------------------------------------------------- /bsd-kernel/src/character_device.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | use crate::cstr_ref; 27 | use crate::module::SharedModule; 28 | use crate::uio::{UioReader, UioWriter}; 29 | use alloc::boxed::Box; 30 | use core::prelude::v1::*; 31 | use core::{fmt, mem, ptr}; 32 | use libc::c_int; 33 | 34 | /// ```c,ignore 35 | /// /* 36 | /// * Character device switch table 37 | /// */ 38 | /// struct cdevsw { 39 | /// int d_version; 40 | /// u_int d_flags; 41 | /// const char *d_name; 42 | /// d_open_t *d_open; 43 | /// d_fdopen_t *d_fdopen; 44 | /// d_close_t *d_close; 45 | /// d_read_t *d_read; 46 | /// d_write_t *d_write; 47 | /// d_ioctl_t *d_ioctl; 48 | /// d_poll_t *d_poll; 49 | /// d_mmap_t *d_mmap; 50 | /// d_strategy_t *d_strategy; 51 | /// dumper_t *d_dump; 52 | /// d_kqfilter_t *d_kqfilter; 53 | /// d_purge_t *d_purge; 54 | /// d_mmap_single_t *d_mmap_single; 55 | /// 56 | /// int32_t d_spare0[3]; 57 | /// void *d_spare1[3]; 58 | /// 59 | /// /* These fields should not be messed with by drivers */ 60 | /// LIST_HEAD(, cdev) d_devs; 61 | /// int d_spare2; 62 | /// union { 63 | /// struct cdevsw *gianttrick; 64 | /// SLIST_ENTRY(cdevsw) postfree_list; 65 | /// } __d_giant; 66 | /// }; 67 | /// ``` 68 | 69 | pub trait CharacterDevice { 70 | fn open(&mut self); 71 | fn close(&mut self); 72 | fn read(&mut self, uio: &mut UioWriter); 73 | fn write(&mut self, uio: &mut UioReader); 74 | } 75 | 76 | pub struct CDev 77 | where 78 | T: CharacterDevice, 79 | { 80 | cdev: ptr::NonNull, 81 | delegate: SharedModule, 82 | } 83 | 84 | impl CDev 85 | where 86 | T: CharacterDevice, 87 | { 88 | pub fn new_with_delegate( 89 | name: &'static str, 90 | delegate: SharedModule, 91 | ) -> Option> { 92 | let cdevsw_raw: *mut kernel_sys::cdevsw = { 93 | let mut c: kernel_sys::cdevsw = unsafe { mem::zeroed() }; 94 | c.d_open = Some(cdev_open::); 95 | c.d_close = Some(cdev_close::); 96 | c.d_read = Some(cdev_read::); 97 | c.d_write = Some(cdev_write::); 98 | c.d_version = kernel_sys::D_VERSION as i32; 99 | c.d_name = "helloworld".as_ptr() as *mut i8; 100 | Box::into_raw(Box::new(c)) 101 | }; 102 | 103 | let cdev_raw: *mut kernel_sys::cdev = unsafe { 104 | kernel_sys::make_dev( 105 | cdevsw_raw, 106 | 0, 107 | kernel_sys::UID_ROOT as u32, 108 | kernel_sys::GID_WHEEL as u32, 109 | 0o660, 110 | cstr_ref!(name).as_ptr() as *mut i8, 111 | ) 112 | }; 113 | 114 | match cdev_raw.is_null() { 115 | true => { 116 | // Convert cdevsw back to Box so memory can be freed 117 | let _cdevsw = unsafe { Box::from_raw(cdevsw_raw) }; 118 | None 119 | } 120 | false => { 121 | let cdev = Box::new(CDev { 122 | cdev: ptr::NonNull::new(cdev_raw).unwrap(), 123 | delegate, 124 | }); 125 | unsafe { 126 | (*cdev_raw).si_drv1 = 127 | &*cdev as *const CDev as *mut libc::c_void 128 | }; 129 | Some(cdev) 130 | } 131 | } 132 | } 133 | } 134 | 135 | impl fmt::Debug for CDev 136 | where 137 | T: CharacterDevice, 138 | { 139 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 140 | write!(f, "CDev {{ cdev: {:?}, ... }}", self.cdev.as_ptr()) 141 | } 142 | } 143 | 144 | impl Drop for CDev 145 | where 146 | T: CharacterDevice, 147 | { 148 | fn drop(&mut self) { 149 | // debugln!("[kernel.rs] CDev::drop"); 150 | 151 | // Assign only to clarify what type we're dealing with... 152 | let dev: *mut kernel_sys::cdev = self.cdev.as_ptr(); 153 | 154 | // Back to Box so cdevsw memory is freed 155 | let _cdevsw: Box = 156 | unsafe { Box::from_raw((*dev).si_devsw) }; 157 | 158 | // debugln!("[kernel.rs] CDev::drop calling destroy_dev. ptr={:?}", dev.as_ptr()); 159 | unsafe { kernel_sys::destroy_dev(dev) }; 160 | } 161 | } 162 | 163 | // File operations callbacks 164 | extern "C" fn cdev_open( 165 | dev: *mut kernel_sys::cdev, 166 | _oflags: c_int, 167 | _devtype: c_int, 168 | _td: *mut kernel_sys::thread, 169 | ) -> c_int 170 | where 171 | T: CharacterDevice, 172 | { 173 | // debugln!("cdev_open"); 174 | let cdev: &CDev = unsafe { &*((*dev).si_drv1 as *const CDev) }; 175 | if let Some(mut m) = cdev.delegate.lock() { 176 | m.open(); 177 | } 178 | 0 179 | } 180 | 181 | #[allow(unused)] 182 | extern "C" fn cdev_fdopen( 183 | _dev: *mut kernel_sys::cdev, 184 | _oflags: c_int, 185 | _td: *mut kernel_sys::thread, 186 | _fp: *mut kernel_sys::file, 187 | ) -> c_int { 188 | // debugln!("cdev_fdopen"); 189 | 0 190 | } 191 | 192 | extern "C" fn cdev_close( 193 | dev: *mut kernel_sys::cdev, 194 | _fflag: c_int, 195 | _devtype: c_int, 196 | _td: *mut kernel_sys::thread, 197 | ) -> c_int 198 | where 199 | T: CharacterDevice, 200 | { 201 | // debugln!("cdev_close"); 202 | let cdev: &CDev = unsafe { &*((*dev).si_drv1 as *const CDev) }; 203 | if let Some(mut m) = cdev.delegate.lock() { 204 | m.close(); 205 | } 206 | 0 207 | } 208 | 209 | extern "C" fn cdev_read( 210 | dev: *mut kernel_sys::cdev, 211 | uio: *mut kernel_sys::uio, 212 | _ioflag: c_int, 213 | ) -> c_int 214 | where 215 | T: CharacterDevice, 216 | { 217 | // debugln!("cdev_read"); 218 | let cdev: &CDev = unsafe { &*((*dev).si_drv1 as *const CDev) }; 219 | if let Some(mut m) = cdev.delegate.lock() { 220 | m.read(&mut UioWriter::new(uio)); 221 | } 222 | 0 223 | } 224 | 225 | extern "C" fn cdev_write( 226 | dev: *mut kernel_sys::cdev, 227 | uio: *mut kernel_sys::uio, 228 | _ioflag: c_int, 229 | ) -> c_int 230 | where 231 | T: CharacterDevice, 232 | { 233 | // debugln!("cdev_write"); 234 | let cdev: &CDev = unsafe { &*((*dev).si_drv1 as *const CDev) }; 235 | if let Some(mut m) = cdev.delegate.lock() { 236 | m.write(unsafe { &mut UioReader::new(uio) }); 237 | } 238 | 0 239 | } 240 | -------------------------------------------------------------------------------- /bsd-kernel/src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | #[derive(Debug)] 27 | pub enum Error { 28 | ConversionError(&'static str), 29 | } 30 | 31 | impl From for Error { 32 | fn from(_e: core::num::TryFromIntError) -> Error { 33 | Error::ConversionError("Invalid integer type") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bsd-kernel/src/io.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | use crate::cstr; 27 | use alloc::string::String; 28 | use alloc::vec::Vec; 29 | use core::{fmt, ptr}; 30 | use libc::{c_char, c_void}; 31 | 32 | /// Empty structure that uses libcore's `fmt::Write` trait to provide 33 | /// support for writing formatted arguments lists (as generated by the 34 | /// built-in `format_args!()` macro`) 35 | pub struct KernelDebugWriter {} 36 | 37 | impl fmt::Write for KernelDebugWriter { 38 | fn write_str(&mut self, message: &str) -> fmt::Result { 39 | unsafe { 40 | let ptr = kernel_sys::malloc( 41 | message.len() + 1, 42 | &mut kernel_sys::M_DEVBUF[0], 43 | kernel_sys::M_WAITOK, 44 | ) as *mut c_char; 45 | if ptr.is_null() { 46 | let msg = 47 | cstr!("Failed to allocate memory for dynamic printf()\n"); 48 | let ptr = msg.as_ptr() as *const c_char; 49 | kernel_sys::uprintf(ptr); 50 | } else { 51 | ptr::copy(message.as_ptr(), ptr as *mut u8, message.len()); 52 | ptr::write(ptr.add(message.len()), 0); 53 | kernel_sys::uprintf(ptr); 54 | kernel_sys::free( 55 | ptr as *mut c_void, 56 | &mut kernel_sys::M_DEVBUF[0], 57 | ); 58 | } 59 | } 60 | Ok(()) 61 | } 62 | } 63 | 64 | pub type Result = core::result::Result; 65 | 66 | #[derive(Debug)] 67 | pub struct Error { 68 | kind: ErrorKind, 69 | error: String, 70 | } 71 | 72 | impl Error { 73 | pub fn new(kind: ErrorKind, error: E) -> Error 74 | where 75 | E: Into, 76 | { 77 | Error { 78 | error: error.into(), 79 | kind, 80 | } 81 | } 82 | pub fn kind(&self) -> ErrorKind { 83 | self.kind 84 | } 85 | } 86 | 87 | impl fmt::Display for Error { 88 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 89 | write!(fmt, "{:?}: {}", self.kind, self.error) 90 | } 91 | } 92 | 93 | #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 94 | pub enum ErrorKind { 95 | NotFound, 96 | PermissionDenied, 97 | ConnectionRefused, 98 | ConnectionReset, 99 | ConnectionAborted, 100 | NotConnected, 101 | AddrInUse, 102 | AddrNotAvailable, 103 | BrokenPipe, 104 | AlreadyExists, 105 | WouldBlock, 106 | InvalidInput, 107 | InvalidData, 108 | TimedOut, 109 | WriteZero, 110 | Interrupted, 111 | Other, 112 | UnexpectedEof, 113 | } 114 | 115 | // This uses an adaptive system to extend the vector when it fills. We want to 116 | // avoid paying to allocate and zero a huge chunk of memory if the reader only 117 | // has 4 bytes while still making large reads if the reader does have a ton 118 | // of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every 119 | // time is 4,500 times (!) slower than this if the reader has a very small 120 | // amount of data to return. 121 | const DEFAULT_BUF_SIZE: usize = 1024; 122 | 123 | fn append_to_string(buf: &mut String, f: F) -> Result 124 | where 125 | F: FnOnce(&mut Vec) -> Result, 126 | { 127 | // debugln!("append to string"); 128 | struct Guard<'a> { 129 | s: &'a mut Vec, 130 | len: usize, 131 | } 132 | impl<'a> Drop for Guard<'a> { 133 | fn drop(&mut self) { 134 | unsafe { 135 | self.s.set_len(self.len); 136 | } 137 | } 138 | } 139 | 140 | unsafe { 141 | let mut g = Guard { 142 | len: buf.len(), 143 | s: buf.as_mut_vec(), 144 | }; 145 | let ret = f(g.s); 146 | if core::str::from_utf8(&g.s[g.len..]).is_err() { 147 | ret.and_then(|_| { 148 | Err(Error::new( 149 | ErrorKind::InvalidData, 150 | "stream did not contain valid UTF-8", 151 | )) 152 | }) 153 | } else { 154 | g.len = g.s.len(); 155 | ret 156 | } 157 | } 158 | } 159 | 160 | fn read_to_end( 161 | r: &mut R, 162 | buf: &mut Vec, 163 | ) -> Result { 164 | // debugln!("read to end"); 165 | let start_len = buf.len(); 166 | let mut len = start_len; 167 | let mut new_write_size = 16; 168 | let ret; 169 | loop { 170 | // debugln!("read to end loop"); 171 | if len == buf.len() { 172 | if new_write_size < DEFAULT_BUF_SIZE { 173 | new_write_size *= 2; 174 | } 175 | buf.resize(len + new_write_size, 0); 176 | } 177 | 178 | match r.read(&mut buf[len..]) { 179 | Ok(0) => { 180 | ret = Ok(len - start_len); 181 | break; 182 | } 183 | Ok(n) => len += n, 184 | Err(ref e) if e.kind() == ErrorKind::Interrupted => {} 185 | Err(e) => { 186 | ret = Err(e); 187 | break; 188 | } 189 | } 190 | } 191 | 192 | buf.truncate(len); 193 | ret 194 | } 195 | 196 | pub trait Read { 197 | fn read(&mut self, buf: &mut [u8]) -> Result; 198 | fn read_to_end(&mut self, buf: &mut Vec) -> Result { 199 | read_to_end(self, buf) 200 | } 201 | fn read_to_string(&mut self, buf: &mut String) -> Result { 202 | // Note that we do *not* call `.read_to_end()` here. We are passing 203 | // `&mut Vec` (the raw contents of `buf`) into the `read_to_end` 204 | // method to fill it up. An arbitrary implementation could overwrite the 205 | // entire contents of the vector, not just append to it (which is what 206 | // we are expecting). 207 | // 208 | // To prevent extraneously checking the UTF-8-ness of the entire buffer 209 | // we pass it to our hardcoded `read_to_end` implementation which we 210 | // know is guaranteed to only read data into the end of the buffer. 211 | append_to_string(buf, |b| read_to_end(self, b)) 212 | } 213 | fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> { 214 | while !buf.is_empty() { 215 | match self.read(buf) { 216 | Ok(0) => break, 217 | Ok(n) => { 218 | let tmp = buf; 219 | buf = &mut tmp[n..]; 220 | } 221 | Err(ref e) if e.kind() == ErrorKind::Interrupted => {} 222 | Err(e) => return Err(e), 223 | } 224 | } 225 | if !buf.is_empty() { 226 | Err(Error::new( 227 | ErrorKind::UnexpectedEof, 228 | "failed to fill whole buffer", 229 | )) 230 | } else { 231 | Ok(()) 232 | } 233 | } 234 | fn by_ref(&mut self) -> &mut Self 235 | where 236 | Self: Sized, 237 | { 238 | self 239 | } 240 | } 241 | 242 | pub trait Write { 243 | fn write(&mut self, buf: &[u8]) -> Result; 244 | fn flush(&mut self) -> Result<()>; 245 | fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { 246 | while !buf.is_empty() { 247 | match self.write(buf) { 248 | Ok(0) => { 249 | return Err(Error::new( 250 | ErrorKind::WriteZero, 251 | "failed to write whole buffer", 252 | )) 253 | } 254 | Ok(n) => buf = &buf[n..], 255 | Err(ref e) if e.kind() == ErrorKind::Interrupted => {} 256 | Err(e) => return Err(e), 257 | } 258 | } 259 | Ok(()) 260 | } 261 | fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()> { 262 | // Create a shim which translates a Write to a fmt::Write and saves 263 | // off I/O errors. instead of discarding them 264 | struct Adaptor<'a, T: ?Sized + 'a> { 265 | inner: &'a mut T, 266 | error: Result<()>, 267 | } 268 | 269 | impl<'a, T: Write + ?Sized> fmt::Write for Adaptor<'a, T> { 270 | fn write_str(&mut self, s: &str) -> fmt::Result { 271 | match self.inner.write_all(s.as_bytes()) { 272 | Ok(()) => Ok(()), 273 | Err(e) => { 274 | self.error = Err(e); 275 | Err(fmt::Error) 276 | } 277 | } 278 | } 279 | } 280 | 281 | let mut output = Adaptor { 282 | inner: self, 283 | error: Ok(()), 284 | }; 285 | match fmt::write(&mut output, fmt) { 286 | Ok(()) => Ok(()), 287 | Err(..) => { 288 | // check if the error came from the underlying `Write` or not 289 | if output.error.is_err() { 290 | output.error 291 | } else { 292 | Err(Error::new(ErrorKind::Other, "formatter error")) 293 | } 294 | } 295 | } 296 | } 297 | fn by_ref(&mut self) -> &mut Self 298 | where 299 | Self: Sized, 300 | { 301 | self 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /bsd-kernel/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | #![no_std] 27 | #![feature(alloc_error_handler)] 28 | 29 | // Re-export libc and kernel_sys so that the printing macros work 30 | pub use kernel_sys; 31 | pub use libc; 32 | 33 | pub use kernel_sys::module_t as Module; 34 | 35 | extern crate alloc; 36 | 37 | pub mod allocator; 38 | pub mod character_device; 39 | pub mod error; 40 | pub mod io; 41 | pub mod module; 42 | pub mod uio; 43 | 44 | /// Create a null-terminated constant string at compile time 45 | #[macro_export] 46 | macro_rules! cstr { 47 | ($arg:expr) => { 48 | concat!($arg, '\x00') 49 | }; 50 | } 51 | 52 | /// Create a null-terminated string at runtime from any `Display` type 53 | #[macro_export] 54 | macro_rules! cstr_ref { 55 | ($arg:expr) => { 56 | &alloc::format!("{}\x00", $arg) 57 | }; 58 | } 59 | 60 | /// Print kernel debug messages without a trailing newline 61 | #[macro_export] 62 | macro_rules! print { 63 | // Static (zero-allocation) implementation that uses compile-time `concat!()` only 64 | ($fmt:expr) => ({ 65 | let msg = $crate::cstr!($fmt); 66 | let ptr = msg.as_ptr() as *const $crate::libc::c_char; 67 | unsafe { 68 | $crate::kernel_sys::uprintf(ptr); 69 | } 70 | }); 71 | 72 | // Dynamic implementation that processes format arguments 73 | ($fmt:expr, $($arg:tt)*) => ({ 74 | use ::core::fmt::Write; 75 | use $crate::io::KernelDebugWriter; 76 | let mut writer = KernelDebugWriter {}; 77 | writer.write_fmt(format_args!($fmt, $($arg)*)).unwrap(); 78 | }); 79 | } 80 | 81 | /// Print kernel debug messages with a trailing newline 82 | #[macro_export] 83 | macro_rules! println { 84 | ($fmt:expr) => ($crate::print!(concat!($fmt, "\n"))); 85 | ($fmt:expr, $($arg:tt)+) => ($crate::print!(concat!($fmt, "\n"), $($arg)*)); 86 | } 87 | 88 | /// Print kernel debug messages without a trailing newline in debug builds 89 | #[macro_export] 90 | macro_rules! debug { 91 | ($($arg:tt)*) => (if cfg!(debug_assertions) { $crate::print!($($arg)*) }) 92 | } 93 | 94 | /// Print kernel debug messages with a trailing newline in debug builds 95 | #[macro_export] 96 | macro_rules! debugln { 97 | ($($arg:tt)*) => (if cfg!(debug_assertions) { $crate::println!($($arg)*) }) 98 | } 99 | -------------------------------------------------------------------------------- /bsd-kernel/src/module.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | //! Traits and interfaces for modules 27 | 28 | use crate::error::Error; 29 | use alloc::sync::Arc; 30 | use core::convert::{TryFrom, TryInto}; 31 | use core::ops::{Deref, DerefMut}; 32 | use core::prelude::v1::*; 33 | use core::{fmt, ptr}; 34 | use kernel_sys::{ 35 | modeventtype_MOD_LOAD, modeventtype_MOD_QUIESCE, modeventtype_MOD_SHUTDOWN, 36 | modeventtype_MOD_UNLOAD, 37 | }; 38 | use spin::{Mutex, MutexGuard}; 39 | 40 | /// The module event types 41 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 42 | #[repr(u32)] 43 | pub enum ModuleEventType { 44 | /// Module is being loaded 45 | Load = modeventtype_MOD_LOAD, 46 | /// Module is being unloaded 47 | Unload = modeventtype_MOD_UNLOAD, 48 | /// The system is shutting down 49 | Shutdown = modeventtype_MOD_SHUTDOWN, 50 | /// The module is about to be unloaded - returning an error from the 51 | /// QUIESCE event causes kldunload to cancel the unload (unless forced 52 | /// with -f) 53 | Quiesce = modeventtype_MOD_QUIESCE, 54 | } 55 | 56 | impl TryFrom for ModuleEventType { 57 | type Error = Error; 58 | fn try_from(input: i32) -> Result { 59 | use ModuleEventType::*; 60 | #[allow(non_upper_case_globals)] 61 | match input.try_into()? { 62 | modeventtype_MOD_LOAD => Ok(Load), 63 | modeventtype_MOD_UNLOAD => Ok(Unload), 64 | modeventtype_MOD_SHUTDOWN => Ok(Shutdown), 65 | modeventtype_MOD_QUIESCE => Ok(Quiesce), 66 | _ => Err(Error::ConversionError("Invalid value for modeventtype")), 67 | } 68 | } 69 | } 70 | 71 | impl ModuleEventType { 72 | /// Attempt to convert an i32 representation of a module event into 73 | /// a `ModuleEventType` enum variant. Returns `None` if the value is 74 | /// not a valid module event 75 | pub fn from_i32(n: i32) -> Option { 76 | ModuleEventType::try_from(n).ok() 77 | } 78 | } 79 | 80 | /// Functions to handle each type of module event 81 | /// 82 | /// TODO: functions for SHUTDOWN and QUIESCE with default implementations 83 | pub trait ModuleEvents { 84 | /// Function called when the module is loaded 85 | fn load(&mut self); 86 | /// Function called when the module is unloaded 87 | fn unload(&mut self); 88 | } 89 | 90 | pub struct LockedModule<'a, T: Sized + 'a> { 91 | guard: MutexGuard<'a, Option>, 92 | } 93 | 94 | //impl<'a, T: Sized> LockedModule<'a, T> {} 95 | 96 | impl<'a, T: Sized> Deref for LockedModule<'a, T> { 97 | type Target = T; 98 | 99 | fn deref(&self) -> &T { 100 | self.guard.as_ref().unwrap() 101 | } 102 | } 103 | 104 | impl<'a, T: Sized> DerefMut for LockedModule<'a, T> { 105 | fn deref_mut(&mut self) -> &mut T { 106 | &mut *self.guard.as_mut().unwrap() 107 | } 108 | } 109 | 110 | impl<'a, T> Drop for LockedModule<'a, T> { 111 | fn drop(&mut self) { 112 | // debugln!("[kernel.rs] LockedModule::drop"); 113 | } 114 | } 115 | 116 | impl<'a, T> core::fmt::Debug for LockedModule<'a, T> { 117 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 118 | write!(f, "LockedModule {{ guard: MutexGuard> }}") 119 | } 120 | } 121 | 122 | /// Wrapper to protect a module behind a mutex 123 | #[derive(Debug, Default)] 124 | pub struct SharedModule { 125 | inner: Arc>>, 126 | } 127 | impl SharedModule { 128 | pub fn new(data: T) -> Self { 129 | SharedModule { 130 | inner: Arc::new(Mutex::new(Some(data))), 131 | } 132 | } 133 | 134 | pub fn inner(&self) -> Arc>> { 135 | self.inner.clone() 136 | } 137 | 138 | pub fn lock(&self) -> Option> { 139 | let guard = self.inner.lock(); 140 | if guard.is_some() { 141 | Some(LockedModule { guard }) 142 | } else { 143 | None 144 | } 145 | } 146 | 147 | pub fn cleanup(&self) { 148 | { 149 | let _ = self.inner.lock().take(); 150 | } 151 | // Safe to do this in kldunload callback? 152 | // If we don't, we'll leak 64 byte Mutex struct (maybe not a disaster...) 153 | unsafe { 154 | let ptr: *mut Arc>> = &self.inner 155 | as *const Arc>> 156 | as *mut Arc>>; 157 | ptr::drop_in_place(ptr); 158 | } 159 | } 160 | } 161 | 162 | impl Clone for SharedModule { 163 | fn clone(&self) -> Self { 164 | SharedModule { 165 | inner: self.inner.clone(), 166 | } 167 | } 168 | } 169 | 170 | impl Drop for SharedModule { 171 | fn drop(&mut self) { 172 | // debugln!("[kernel.rs] SharedModule::drop"); 173 | } 174 | } 175 | 176 | unsafe impl Sync for SharedModule {} 177 | unsafe impl Send for SharedModule {} 178 | -------------------------------------------------------------------------------- /bsd-kernel/src/uio.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | //! This module provides wrapper structs around `kernel_sys::uio` that 27 | //! implement `crate::io::Read` and `crate::io::Write`. 28 | 29 | use crate::io::{self, Read, Write}; 30 | use alloc::format; 31 | use core::fmt; 32 | use core::prelude::v1::*; 33 | use core::{cmp, ptr}; 34 | use libc::{c_int, c_void}; 35 | 36 | /// Wrapper around the kernel device driver I/O interfaces providing 37 | /// methods to read data from userland to the kernel 38 | /// 39 | /// https://nixdoc.net/man-pages/FreeBSD/man9/uio.9.html 40 | pub struct UioReader { 41 | uio: ptr::NonNull, 42 | offset: usize, 43 | remain: usize, 44 | } 45 | 46 | #[allow(clippy::len_without_is_empty)] 47 | impl UioReader { 48 | /// Create a new UioReader instance from a kernel uio pointer 49 | /// 50 | /// # Safety 51 | /// The unsafe part is `(*(*uio).uio_iov).iov_len` which assumes 52 | /// that `uio.uio_iov` is nonnull. 53 | pub unsafe fn new(uio: *mut kernel_sys::uio) -> Self { 54 | UioReader { 55 | uio: ptr::NonNull::new(uio).unwrap(), 56 | offset: 0, 57 | remain: (*(*uio).uio_iov).iov_len, 58 | } 59 | } 60 | 61 | /// "The number of bytes to process" 62 | pub fn len(&self) -> usize { 63 | unsafe { self.uio.as_ref().uio_resid as usize } 64 | } 65 | } 66 | 67 | impl Read for UioReader { 68 | // A reader is implemented for reading data from userland to kernel. 69 | // That is, for d_write callback. 70 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 71 | let buf_len: usize = buf.len(); 72 | let iov_len: usize = 73 | unsafe { (*self.uio.as_ref().uio_iov).iov_len } - self.offset; 74 | let len = cmp::min(buf_len, iov_len); 75 | 76 | if len == 0 { 77 | return Ok(0); 78 | } 79 | 80 | // If buf is at least as long as iov then there is no remainder, 81 | // otherwise remainder is the difference 82 | self.remain = iov_len.saturating_sub(buf_len); 83 | /* if buf_len < iov_len { 84 | // Still got some data to read 85 | self.remain =(iov_len - buf_len )as u64 ; 86 | } else { 87 | // We read everything already 88 | self.remain = 0; 89 | } 90 | */ 91 | // Change to uiomove? 92 | let ret = unsafe { 93 | kernel_sys::copyin( 94 | (*self.uio.as_ref().uio_iov).iov_base.add(self.offset), 95 | buf.as_mut_ptr() as *mut c_void, 96 | len, 97 | ) 98 | }; 99 | self.offset += len; 100 | 101 | match ret { 102 | 0 => Ok(len), 103 | _ => Err(io::Error::new( 104 | io::ErrorKind::Other, 105 | "UioReader::read copyin failed.", 106 | )), 107 | } 108 | } 109 | } 110 | 111 | impl fmt::Debug for UioReader { 112 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 113 | write!( 114 | f, 115 | "UioReader {{ uio: {:?}, offset: {}, remain: {} }}", 116 | self.uio.as_ptr(), 117 | self.offset, 118 | self.remain 119 | ) 120 | } 121 | } 122 | 123 | /// Wrapper around the kernel device driver I/O interfaces providing 124 | /// methods to send data from the kernel up to userland 125 | /// 126 | /// https://nixdoc.net/man-pages/FreeBSD/man9/uio.9.html 127 | pub struct UioWriter { 128 | uio: ptr::NonNull, 129 | } 130 | 131 | #[allow(clippy::len_without_is_empty)] 132 | impl UioWriter { 133 | /// Create a new UioWriter 134 | /// 135 | /// ## Panics 136 | /// Panics if the supplied uio pointer is null 137 | pub fn new(uio: *mut kernel_sys::uio) -> Self { 138 | UioWriter { 139 | uio: ptr::NonNull::new(uio).unwrap(), 140 | } 141 | } 142 | 143 | /// Returns the number of bytes to process 144 | pub fn len(&self) -> usize { 145 | unsafe { self.uio.as_ref().uio_resid as usize } 146 | } 147 | } 148 | 149 | impl Write for UioWriter { 150 | fn write(&mut self, buf: &[u8]) -> io::Result { 151 | // Temporary add a uiomove function that takes immutable buffer 152 | // instead of mutable 153 | extern "C" { 154 | pub fn uiomove( 155 | cp: *const c_void, 156 | n: c_int, 157 | uio: *mut kernel_sys::uio, 158 | ) -> c_int; 159 | } 160 | 161 | let offset = unsafe { self.uio.as_ref().uio_offset as usize }; 162 | let amount_uio = unsafe { self.uio.as_ref().uio_resid as usize }; 163 | let amount_buf = match buf.len() - offset { 164 | x if x > 0 => x, 165 | _ => 0, 166 | }; 167 | // debugln!("===> offest {}, amount uio {}, amount buf {}", offset, amount_uio, amount_buf); 168 | 169 | let amount = cmp::min(amount_buf, amount_uio); 170 | if amount == 0 { 171 | // return offset here so write_all know that we've already 172 | // read all bytes. 173 | return Ok(offset); 174 | } 175 | 176 | let ret = unsafe { 177 | uiomove( 178 | buf[offset as usize..].as_ptr() as *const c_void, 179 | amount as i32, 180 | self.uio.as_ptr(), 181 | ) 182 | }; 183 | match ret { 184 | 0 => Ok(amount), 185 | _ => Err(io::Error::new( 186 | io::ErrorKind::Other, 187 | format!("uiomove failed with return code {}", ret), 188 | )), 189 | } 190 | } 191 | 192 | fn flush(&mut self) -> io::Result<()> { 193 | // XXX What do we do here? 194 | Ok(()) 195 | } 196 | } 197 | 198 | impl fmt::Debug for UioWriter { 199 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 200 | write!(f, "UioWriter {{ uio: {:?} }}", self.uio.as_ptr()) 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | OBJECTDIR=/tmp/rustmodule 4 | CURDIR=`pwd` 5 | MODULE_NAME="bsd_rust" 6 | 7 | if [ -d "${OBJECTDIR}" ]; then 8 | rm -rf "${OBJECTDIR}" 9 | fi 10 | 11 | mkdir "${OBJECTDIR}" 12 | 13 | 14 | 15 | make clean && \ 16 | cargo build && \ 17 | cd "${OBJECTDIR}" && \ 18 | ar -xv "${CURDIR}/target/x86_64-kernel-freebsd/debug/lib${MODULE_NAME}.a" && \ 19 | cd "${CURDIR}" && \ 20 | make OBJECTDIR="${OBJECTDIR}" 21 | -------------------------------------------------------------------------------- /hello.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | extern int module_event(struct module *, int, void *); 36 | 37 | static moduledata_t module_data = { 38 | "hello", /* module name */ 39 | module_event, /* event handler */ 40 | NULL /* extra data */ 41 | }; 42 | 43 | DECLARE_MODULE(hello, module_data, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 44 | -------------------------------------------------------------------------------- /kernel-sys/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | wrapper.d 3 | Cargo.lock 4 | -.d -------------------------------------------------------------------------------- /kernel-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kernel-sys" 3 | version = "0.1.0" 4 | authors = ["David Young "] 5 | edition = "2021" 6 | license = "BSD-2-Clause" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | libc = { version = "0.2", default_features = false } 12 | 13 | [build-dependencies] 14 | bindgen = "0.60.0" 15 | -------------------------------------------------------------------------------- /kernel-sys/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | extern crate bindgen; 27 | 28 | use bindgen::{Builder, MacroTypeVariation::Signed}; 29 | use std::path::PathBuf; 30 | 31 | const FILEPATH: &str = "src/bindings.rs"; 32 | 33 | fn main() { 34 | let bindings = Builder::default() 35 | .rustfmt_bindings(true) 36 | .use_core() 37 | .ctypes_prefix("libc") 38 | .size_t_is_usize(true) 39 | .default_macro_constant_type(Signed) 40 | .header("wrapper.h") 41 | .clang_arg("-O2") 42 | .clang_arg("-pipe") 43 | .clang_arg("-fno-strict-aliasing") 44 | .clang_arg("-Werror") 45 | .clang_arg("-D_KERNEL") 46 | .clang_arg("-DKLD_MODULE") 47 | .clang_arg("-nostdinc") 48 | .clang_arg("-I.") 49 | .clang_arg("-I/usr/src/sys") 50 | .clang_arg("-I/usr/include") 51 | .clang_arg("-fno-common") 52 | .clang_arg("-fno-omit-frame-pointer") 53 | .clang_arg("-mno-omit-leaf-frame-pointer") 54 | .clang_arg("-MD") 55 | .clang_arg("-mcmodel=kernel") 56 | .clang_arg("-mno-red-zone") 57 | .clang_arg("-mno-mmx") 58 | .clang_arg("-mno-sse") 59 | .clang_arg("-msoft-float") 60 | .clang_arg("-fno-asynchronous-unwind-tables") 61 | .clang_arg("-ffreestanding") 62 | .clang_arg("-fwrapv") 63 | .clang_arg("-fstack-protector") 64 | .clang_arg("-Wall") 65 | .clang_arg("-Wredundant-decls") 66 | .clang_arg("-Wnested-externs") 67 | .clang_arg("-Wstrict-prototypes") 68 | .clang_arg("-Wmissing-prototypes") 69 | .clang_arg("-Wpointer-arith") 70 | .clang_arg("-Winline") 71 | .clang_arg("-Wcast-qual") 72 | .clang_arg("-Wundef") 73 | .clang_arg("-Wno-pointer-sign") 74 | .clang_arg("-D__printf__=__freebsd_kprintf__") 75 | .clang_arg("-Wmissing-include-dirs") 76 | .clang_arg("-fdiagnostics-show-option") 77 | .clang_arg("-Wno-unknown-pragmas") 78 | // .clang_arg("-Wno-error-tautological-compare") 79 | // .clang_arg("-Wno-error-empty-body") 80 | .clang_arg("-mno-aes") 81 | .clang_arg("-mno-avx") 82 | .clang_arg("-std=iso9899:1999") 83 | .generate() 84 | .expect("Unable to generate binding"); 85 | 86 | let out_path = PathBuf::from(FILEPATH); 87 | bindings 88 | .write_to_file(out_path) 89 | .expect("Error writing bindings!"); 90 | } 91 | -------------------------------------------------------------------------------- /kernel-sys/rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | -------------------------------------------------------------------------------- /kernel-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | #![no_std] 27 | #![allow(non_upper_case_globals)] 28 | #![allow(non_camel_case_types)] 29 | #![allow(non_snake_case)] 30 | #![allow(clippy::useless_transmute)] 31 | #![allow(clippy::too_many_arguments)] 32 | #![allow(clippy::missing_safety_doc)] 33 | 34 | pub use bindings::*; 35 | 36 | mod bindings; 37 | -------------------------------------------------------------------------------- /kernel-sys/wrapper.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | #include 27 | #include /* defines used in kernel.h */ 28 | #include 29 | #include /* uprintf */ 30 | #include /* types used in module initialization */ 31 | #include /* cdevsw struct */ 32 | #include /* uio struct */ 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | -------------------------------------------------------------------------------- /module-hello/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bsd-rust" 3 | version = "0.1.0" 4 | authors = ["David Young "] 5 | edition = "2021" 6 | license = "BSD-2-Clause" 7 | 8 | [lib] 9 | crate-type = ["staticlib"] 10 | 11 | [dependencies] 12 | bsd-kernel = { path = "../bsd-kernel" } 13 | lazy_static = { version = "1", features = ["spin_no_std"] } 14 | libc = "0.2" 15 | -------------------------------------------------------------------------------- /module-hello/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | #![no_std] 27 | #![feature(default_alloc_error_handler)] 28 | 29 | //! Example kernel module for FreeBSD written in Rust 30 | //! 31 | //! To build, run the following commands: 32 | //! ```bash,ignore 33 | //! cd bsd-rust 34 | //! ./build.sh 35 | //! sudo make load 36 | //! echo "hi rust" > /dev/rustmodule 37 | //! cat /dev/rustmodule 38 | //! sudo make unload 39 | //! ``` 40 | 41 | use bsd_kernel::allocator::KernelAllocator; 42 | use bsd_kernel::module::{ModuleEventType, ModuleEvents}; 43 | use bsd_kernel::{debugln, println}; 44 | use core::panic::PanicInfo; 45 | use libc::{c_int, c_void}; 46 | use module::MODULE; 47 | 48 | mod module; 49 | 50 | extern crate alloc; 51 | 52 | #[global_allocator] 53 | static ALLOCATOR: KernelAllocator = KernelAllocator; 54 | 55 | #[panic_handler] 56 | fn panic_handler(info: &PanicInfo) -> ! { 57 | if let Some(s) = info.payload().downcast_ref::<&str>() { 58 | println!("Panic occurred: {}", s); 59 | } else { 60 | println!("Panic occurred"); 61 | } 62 | 63 | if let Some(loc) = info.location() { 64 | println!("Panic at line `{}` of file `{}`", loc.line(), loc.file()); 65 | } 66 | 67 | loop {} 68 | } 69 | 70 | /// Main event handler for module events 71 | #[no_mangle] 72 | pub extern "C" fn module_event( 73 | _module: bsd_kernel::Module, 74 | event: c_int, 75 | _arg: *mut c_void, 76 | ) -> c_int { 77 | // debugln!("[interface.rs] Got event {}", event); 78 | 79 | if let Some(ev) = ModuleEventType::from_i32(event) { 80 | use ModuleEventType::*; 81 | match ev { 82 | Load => { 83 | // debugln!("[interface.rs] MOD_LOAD"); 84 | 85 | if let Some(mut m) = MODULE.lock() { 86 | m.load(); 87 | } 88 | } 89 | Unload => { 90 | // debugln!("[interface.rs] MOD_UNLOAD"); 91 | 92 | if let Some(mut m) = MODULE.lock() { 93 | m.unload(); 94 | } 95 | 96 | MODULE.cleanup(); 97 | } 98 | Quiesce => { 99 | // debugln!("[interface.rs] MOD_QUIESCE"); 100 | } 101 | Shutdown => { 102 | // debugln!("[interface.rs] MOD_SHUTDOWN"); 103 | } 104 | } 105 | } else { 106 | debugln!("[interface.rs] Undefined event"); 107 | } 108 | 0 109 | } 110 | -------------------------------------------------------------------------------- /module-hello/src/module.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 NCC Group 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this 7 | // list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // Based on public domain code by Johannes Lundberg 25 | 26 | use alloc::boxed::Box; 27 | use alloc::string::{String, ToString}; 28 | use bsd_kernel::character_device::{CDev, CharacterDevice}; 29 | use bsd_kernel::debugln; 30 | use bsd_kernel::io::{Read, Write}; 31 | use bsd_kernel::module::{ModuleEvents, SharedModule}; 32 | use bsd_kernel::uio::{UioReader, UioWriter}; 33 | use lazy_static::lazy_static; 34 | 35 | lazy_static! { 36 | // Object created on first access (which is module load callback) 37 | pub static ref MODULE: 38 | SharedModule = SharedModule::new(Hello::new()); 39 | } 40 | 41 | #[derive(Debug)] 42 | pub struct HelloInner { 43 | data: String, 44 | _cdev: Box>, 45 | } 46 | 47 | #[derive(Default, Debug)] 48 | pub struct Hello { 49 | // Put everything in an option so that SharedModule can be 50 | // fully initialised before we start doing stuff in module load 51 | // callback. (we can't for example clone MODULE while in 52 | // Hello::new() because of order of initialisation) 53 | inner: Option, 54 | } 55 | impl Hello { 56 | fn new() -> Self { 57 | // We can't access MODULE here because it is not initialised yet! 58 | Hello { inner: None } 59 | } 60 | } 61 | 62 | impl ModuleEvents for Hello { 63 | fn load(&mut self) { 64 | debugln!("[module.rs] Hello::load"); 65 | 66 | // MODULE has been fully initialised here 67 | // so we can clone it safely 68 | let m = MODULE.clone(); 69 | 70 | if let Some(cdev) = CDev::new_with_delegate("rustmodule", m) { 71 | self.inner = Some(HelloInner { 72 | data: "Default hello message\n".to_string(), 73 | _cdev: cdev, 74 | }); 75 | } else { 76 | debugln!( 77 | "[module.rs] Hello::load: Failed to create character device" 78 | ); 79 | } 80 | } 81 | 82 | fn unload(&mut self) { 83 | debugln!("[module.rs] Hello::unload"); 84 | } 85 | } 86 | 87 | impl CharacterDevice for Hello { 88 | fn open(&mut self) { 89 | // debugln!("[module.rs] Hello::open"); 90 | } 91 | fn close(&mut self) { 92 | // debugln!("[module.rs] Hello::close"); 93 | } 94 | fn read(&mut self, uio: &mut UioWriter) { 95 | // debugln!("[module.rs] Hello::read"); 96 | 97 | if let Some(ref h) = self.inner { 98 | match uio.write_all(&h.data.as_bytes()) { 99 | Ok(()) => (), 100 | Err(e) => debugln!("{}", e), 101 | } 102 | } 103 | } 104 | fn write(&mut self, uio: &mut UioReader) { 105 | // debugln!("[module.rs] Hello::write"); 106 | if let Some(ref mut inner) = self.inner { 107 | inner.data.clear(); 108 | match uio.read_to_string(&mut inner.data) { 109 | Ok(x) => { 110 | debugln!( 111 | "Read {} bytes. Setting new message to `{}`", 112 | x, 113 | inner.data 114 | ) 115 | } 116 | Err(e) => debugln!("{:?}", e), 117 | } 118 | } 119 | } 120 | } 121 | impl Drop for Hello { 122 | fn drop(&mut self) { 123 | // debugln!("Hello::drop"); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2022-07-14" 3 | components = [ 4 | "rustfmt", 5 | "clippy", 6 | "rust-src", 7 | ] 8 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 -------------------------------------------------------------------------------- /x86_64-kernel-freebsd.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi-return-struct-as-int": true, 3 | "arch": "x86_64", 4 | "cpu": "x86-64", 5 | "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", 6 | "dynamic-linking": true, 7 | "executables": true, 8 | "has-rpath": true, 9 | "linker-is-gnu": true, 10 | "llvm-target": "x86_64-unknown-freebsd", 11 | "max-atomic-width": 64, 12 | "os": "freebsd", 13 | "features": "+soft-float,-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2", 14 | "pre-link-args": { 15 | "gcc": [ 16 | "-Wl,--as-needed", 17 | "-Wl,-z,noexecstack", 18 | "-m64" 19 | ] 20 | }, 21 | "disable-redzone": true, 22 | "archive-format": "gnu", 23 | "code-model": "kernel", 24 | "relocation-model": "static", 25 | "target-family": "unix", 26 | "target-pointer-width": "64" 27 | } 28 | --------------------------------------------------------------------------------