514 Commits

Author SHA1 Message Date
Red Bear OS 735f9706cd 0.3.0: refresh Cargo.lock for +rb0.3.0 local forks 2026-07-06 20:41:37 +03:00
vasilito 1859eb5d0b 0.3.0: bump version suffix to +rb0.3.0 2026-07-06 19:47:38 +03:00
Red Bear OS 0fac378c94 redoxfs: import Vec from alloc for no_std builds (bootloader dependency) 2026-07-06 15:19:39 +03:00
vasilito 7387e45ffb redbear: apply readonly-live-media patch, add author attribution, use local redox-scheme path 2026-07-06 07:56:16 +03:00
Red Bear OS e6be9134d8 fix: use local path deps for redox_syscall and libredox 2026-07-05 23:58:07 +03:00
Red Bear OS f29cbc3dfe version: use +rb0.2.5 build metadata instead of -rb0.2.5 pre-release
Semver pre-release suffix (-rb0.2.5) breaks Cargo's [patch.crates-io]
matching for transitive deps. Build metadata (+rb0.2.5) is semver-
compatible: ^0.9.0 matches 0.9.0+rb0.2.5, patch redirection works,
and the Red Bear suffix is still visible in the version string.
2026-07-05 09:16:12 +03:00
Jeremy Soller 473b4baeb0 0.9.1 2026-07-01 08:32:44 -06:00
Jeremy Soller 00ba438c95 Merge branch 'update-redox-crates' into 'master'
chore: Update redox crates versions

See merge request redox-os/redoxfs!121
2026-07-01 08:31:44 -06:00
Ibuki Omatsu 6c1e2aa446 chore: Update redox crates versions 2026-07-01 08:31:44 -06:00
Jeremy Soller 3acc7a240f Merge branch 'no-box' into 'master'
Remove Box from implementation

See merge request redox-os/redoxfs!120
2026-06-24 13:48:49 -06:00
Wildan M 47c9d242f5 Remove Box from Vec conversion 2026-06-23 14:09:19 +07:00
Wildan M 6767faba13 Remove Box in scheme handle 2026-06-23 14:01:31 +07:00
Wildan M 44bbd27e1b Fix warning 2026-06-23 13:58:50 +07:00
Jeremy Soller af493b9f4e Merge branch 'inode-cache' into 'master'
Implement Inode cache from fmap

Closes #56

See merge request redox-os/redoxfs!118
2026-05-13 07:44:12 -06:00
Wildan M d03893a253 Add munmap to solve leak 2026-05-13 11:23:45 +07:00
Wildan M 3ed2bee9bb Fix CI run 2026-05-13 07:08:07 +07:00
Wildan M 1d1176de40 Add fmap version for invalidation 2026-05-13 06:48:39 +07:00
Wildan M 5b515c0c9b Implement fmap for read cache 2026-05-13 06:34:39 +07:00
Jeremy Soller f26c86b176 Update dependencies 2026-05-04 12:31:39 -06:00
Jeremy Soller b59677627c Merge branch 'retry-readdir' into 'master'
Skip missing getdents item

See merge request redox-os/redoxfs!117
2026-03-30 14:31:28 -06:00
Wildan M 3d6f9cec6e Skip missing getdents item 2026-03-30 21:04:03 +07:00
Jeremy Soller 0955762196 Merge branch 'fix_unmount' into 'master'
Partially fix unmount on redox

See merge request redox-os/redoxfs!116
2026-03-11 13:40:09 -06:00
bjorn3 7f469db981 Partially fix unmount on redox
This is not enough to cause redoxfs to quit, but does remove it from
the namespace.
2026-03-11 20:06:07 +01:00
Jeremy Soller 7dad335ba7 Merge branch 'fix-fchdir' into 'master'
fix: Fix fpath implementation for root directory

See merge request redox-os/redoxfs!115
2026-03-06 18:01:31 -07:00
Ibuki.O eb50a95c9b fix: Fix fpath implementation for root directory 2026-03-07 09:55:31 +09:00
Jeremy Soller c4b51b9ca8 Merge branch 'stdfscall' into 'master'
feat: Add std_fs_call support

See merge request redox-os/redoxfs!113
2026-02-28 08:05:09 -07:00
Ibuki Omatsu 04dbcbcd91 feat: Add std_fs_call support 2026-02-28 08:05:08 -07:00
Jeremy Soller e75d311776 Merge branch 'touch-dir' into 'master'
Implement utimens for directory

See merge request redox-os/redoxfs!114
2026-02-27 06:18:55 -07:00
Wildan M 64a8ff46e1 Implements utimens for directory 2026-02-27 14:27:34 +07:00
Jeremy Soller d00745b402 Merge branch 'master' into 'master'
feat(redox): flocking

See merge request redox-os/redoxfs!112
2026-02-25 08:24:56 -07:00
Anhad Singh c51642c8b6 chore: bump redox_syscall and redox_scheme
Signed-off-by: Anhad Singh <andypython@protonmail.com>
2026-02-26 02:14:10 +11:00
Anhad Singh 61d52cf6a7 misc(mount/redox): update to new on_detach API
Signed-off-by: Anhad Singh <andypython@protonmail.com>
2026-02-25 01:49:23 +11:00
Anhad Singh 2360a346ac feat(redox): flocking
Signed-off-by: Anhad Singh <andypython@protonmail.com>
2026-02-24 01:02:26 +11:00
Jeremy Soller 1e0119b3cb Merge branch 'canonical-relative-path' into 'master'
feat: Resolve parent paths

See merge request redox-os/redoxfs!111
2026-02-21 07:47:59 -07:00
Ibuki Omatsu ceeaf513b6 feat: Resolve parent paths 2026-02-21 07:47:59 -07:00
Anhad Singh f4a256ebcd misc: cargo fmt
Signed-off-by: Anhad Singh <andypython@protonmail.com>
2026-02-11 10:40:15 +11:00
Jeremy Soller d47cd7d3f6 Merge branch 'unlinkat' into 'master'
Introduce SchemeRoot and integrate unlink and rmdir into unlinkat.

See merge request redox-os/redoxfs!104
2026-01-20 20:55:47 -07:00
Ibuki Omatsu 32d067d293 Introduce SchemeRoot and integrate unlink and rmdir into unlinkat. 2026-01-20 20:55:47 -07:00
Jeremy Soller 4726ebdd9d Update Cargo.lock 2026-01-03 09:45:02 -07:00
Jeremy Soller 42db72daf2 0.8.6: workaround for memory leaks 2026-01-03 09:44:23 -07:00
Jeremy Soller 57277f2041 0.8.5 2026-01-02 19:31:28 -07:00
Jeremy Soller 9d4ae07987 Merge branch 'delete-after-close' into 'master'
delete after close instead of unlink

See merge request redox-os/redoxfs!102
2026-01-02 19:30:11 -07:00
Jeremy Soller 7bd6189ca9 Fix no_std 2026-01-02 19:25:26 -07:00
Jeremy Soller 5778818bf6 Fix typo in redox mount code 2026-01-02 13:26:09 -07:00
Jeremy Soller 0e01427cd8 Implement release list and add open node tracking to filesystem 2026-01-02 13:23:29 -07:00
Jeremy Soller 57e05acef2 Leak nodes still in use, preparation for release list 2025-12-25 15:52:27 -07:00
Jeremy Soller a44303b4f6 WIP: delete after close instead of unlink 2025-12-25 15:51:53 -07:00
Jeremy Soller 2e1bca19ac Merge branch 'test-lints' into 'master'
Fix test lints and add rename test

See merge request redox-os/redoxfs!109
2025-12-20 21:33:15 -07:00
Josh Megnauth 2b8ea938ea Fix test lints and add rename test
This fixes lints for tests/tests.rs and adds a test for rename. The new
rename test is based on the rename_no_replace tests. There are two
discrepancies between Linux and Redox but I'm not sure if they matter. I
added a note to those tests.

There are two fixed lints of note.
* assert_cmd deprecated Command::cargo_bin in favor of a compile time
  checked macro.
* The mode literals were wrong: 0666 instead of 0o666. 0o is the prefix
  for octal literals.
2025-12-20 19:17:36 -05:00
Jeremy Soller a2f3b55b68 Merge branch 'replace-unlink-rmdir-with-unlinkat' into 'master'
refactor: Replace unlink and rmdir with unlinkat.

See merge request redox-os/redoxfs!107
2025-12-17 18:45:39 -07:00
Ibuki Omatsu a59e7fca9b refactor: Replace unlink and rmdir with unlinkat. 2025-12-17 18:45:39 -07:00
Jeremy Soller c0777c08ac Merge branch 'bump-ver' into 'master'
Bump version to fix installation

See merge request redox-os/redoxfs!108
2025-12-16 09:48:06 -07:00
Wildan M 52b230fc67 Bump version to fix installation 2025-12-16 20:53:14 +07:00
Jeremy Soller b8f2c63bdd Merge branch 'book-on-readme' into 'master'
Mention book for usage on README

See merge request redox-os/redoxfs!106
2025-12-14 21:02:33 -07:00
Jeremy Soller dc77e817e2 Merge branch 'impl-rename-noreplace' into 'master'
Implement RENAME_NO_REPLACE

See merge request redox-os/redoxfs!105
2025-12-14 19:12:36 -07:00
Josh Megnauth 66f4766c6b Implement RENAME_NO_REPLACE
RENAME_NO_REPLACE is a neat flag for renameat2 which disallows renaming
a source onto an existing target.

I originally implemented this flag in relibc, but that code is still
subject to TOCTOU because the sequence of checking if the target exists
and the actual rename isn't atomic.

The code for the flag isn't used anywhere yet, like frename, but it's
unit tested and works for now.
2025-12-14 21:03:56 -05:00
Wildan M 644f747648 Mention book for usage on README 2025-12-15 05:21:05 +07:00
Jeremy Soller 5c94db6f1a Merge branch 'decouple-fuse' into 'master'
Split FUSE feature and move redox dependencies

See merge request redox-os/redoxfs!103
2025-11-17 06:53:54 -07:00
Wildan M 50683d5a7a Split fuse feature 2025-11-17 17:21:59 +07:00
Jeremy Soller 54df280b02 Use new scheme format in fpath 2025-11-14 14:29:19 -07:00
Jeremy Soller a7f61b12ad 0.8.2 2025-11-10 21:01:48 -07:00
Jeremy Soller 91c5ffb986 When upgrading from inline data, only take bytes that fit 2025-11-10 20:59:08 -07:00
Jeremy Soller 116d482d2b Reduce logging 2025-11-01 20:25:43 -06:00
Jeremy Soller b6700b94dc 0.8.1 2025-10-31 11:21:04 -06:00
Jeremy Soller fefd943c3f Add resizing support 2025-10-31 11:16:13 -06:00
Jeremy Soller ab92a083e8 Ensure header size matches allocated blocks 2025-10-31 07:41:29 -06:00
Jeremy Soller 5abce443a5 Ensure allocation is done with the lowest block indexes first 2025-10-30 21:25:37 -06:00
Jeremy Soller 84288a2f5d 0.8.0 2025-09-26 15:45:33 -06:00
Jeremy Soller 2dee3bba4c Merge branch 'v8' into 'master'
v8

See merge request redox-os/redoxfs!95
2025-09-26 15:44:29 -06:00
Jeremy Soller 5af2abde0a Fix reading sparse data when using inline data 2025-09-26 14:36:22 -06:00
Jeremy Soller 437d06ab8c Support inline data in files 2025-09-26 14:17:03 -06:00
Jeremy Soller 5c54b189f9 Prepare for inline node data support 2025-09-26 13:03:07 -06:00
Jeremy Soller 9484534c76 Merge remote-tracking branch 'origin/master' into v8 2025-09-26 12:03:22 -06:00
Jeremy Soller eea5c96137 Implement clone threshold to work around Redox problems 2025-09-26 11:25:22 -06:00
Jeremy Soller 5e044e88c0 Fix cursor when printing clone progress 2025-09-26 10:43:34 -06:00
Jeremy Soller d7391bc6a8 Throttle clone progress messages 2025-09-26 10:18:54 -06:00
Jeremy Soller c796cc7413 Add units for clone progress 2025-09-26 10:10:22 -06:00
Jeremy Soller f37afb04bb Add progress callback to clone function 2025-09-26 10:08:54 -06:00
Jeremy Soller 74582a0917 Squash allocation log in clone 2025-09-26 09:29:02 -06:00
Jeremy Soller 5622165c92 Support for fast cloning of RedoxFS partition 2025-09-26 09:24:21 -06:00
Jeremy Soller 8a46a6df39 Implement From<File> for DiskFile 2025-09-26 09:23:41 -06:00
Jeremy Soller ae034662e6 Fix documentation 2025-09-26 09:23:29 -06:00
Jeremy Soller 0a1fd60d76 Merge branch 'fix-ci' into 'master'
Move tests with mount as integration tests

See merge request redox-os/redoxfs!99
2025-09-25 12:15:47 -06:00
Wildan M fc4c2470a1 Fix test 2025-09-26 01:02:07 +07:00
Wildan Mubarok 508feda5eb Only run integration tests 2025-09-25 14:52:19 +00:00
Wildan M 8591d96f67 Detect redoxer hangs 2025-09-25 21:30:43 +07:00
Wildan M 2d61aceff9 Move tests with mount as integration tests 2025-09-25 20:58:49 +07:00
Jeremy Soller 25cc366a63 Merge branch 'partial_getdents' into 'master'
Allow partial result of getdents

See merge request redox-os/redoxfs!98
2025-09-23 10:57:23 -06:00
Wildan M 4af64e157a Allow partial result of getdents 2025-09-23 23:34:26 +07:00
Jeremy Soller 98286ce752 Fix compilation with no_std 2025-08-03 20:02:09 -06:00
Jeremy Soller 5828e12e85 Provide context for all allocations and deallocations 2025-08-01 21:32:16 -06:00
Jeremy Soller 0a14166c2f Fix issue where compressed data could be returned 2025-08-01 14:07:26 -06:00
Jeremy Soller d03aa518fe Fix compression with 128 KiB records, use compress cache 2025-08-01 13:23:56 -06:00
Jeremy Soller b40fecf185 Fix comment in node.rs 2025-08-01 12:55:49 -06:00
Jeremy Soller ca7ea3c0c8 node: debug blocks 2025-08-01 12:07:51 -06:00
Jeremy Soller a1c94311db Store number of blocks actually used 2025-08-01 11:51:37 -06:00
Jeremy Soller f30b363ce1 Fix build with no_std 2025-08-01 09:55:21 -06:00
Jeremy Soller 26dc32d710 v8: transparent lz4 compression 2025-07-31 18:48:17 -06:00
Jeremy Soller 0cb6f3cad5 Fix compilation on Linux 2025-07-24 13:45:57 -06:00
Jeremy Soller 1506ed50fc Merge branch 'bind-connect-redoxfs' into 'master'
feat: Add UDS bind and connect integration

See merge request redox-os/redoxfs!94
2025-07-21 18:32:18 -06:00
Ibuki Omatsu 63217aa69a feat: Add UDS bind and connect integration 2025-07-21 18:32:18 -06:00
Jeremy Soller dc25344b68 Update aes and xts-mode 2025-07-02 12:48:39 -06:00
Jeremy Soller bd6efe0c83 Update crate version to 0.7 2025-07-02 12:27:14 -06:00
Jeremy Soller aecd042afc Use AES-XTS for encryption 2025-07-02 10:20:17 -06:00
Jeremy Soller 291ce5edc0 Merge branch 'Improve-file-creation-performance' into 'v7'
Improve performance

See merge request redox-os/redoxfs!87
2025-07-01 19:11:00 -06:00
James Matlik b959c34287 Improve performance 2025-07-01 19:11:00 -06:00
Jeremy Soller 02685f5c70 Merge branch 'openat' into 'master'
Implement openat syscall

See merge request redox-os/redoxfs!92
2025-07-01 09:42:16 -06:00
Darley Barreto d0d5761b2d Implement openat syscall 2025-07-01 09:42:16 -06:00
Jeremy Soller 066e5f6e8a Merge branch 'update-syscall' into 'master'
Update redox_syscall

See merge request redox-os/redoxfs!91
2025-06-12 12:23:48 -06:00
Jeremy Soller 9e2e370c0b Merge branch 'improve-node-mode-docstrings' into 'master'
Improve node mode docstrings

See merge request redox-os/redoxfs!90
2025-06-12 10:46:40 -06:00
Ron Williams 165d674d98 Update redox_syscall 2025-06-12 09:46:19 -07:00
Yousef Moazzam 3d06dd39ba Refer to "node" rather than "file" in node mode docstrings 2025-05-24 21:56:30 +01:00
Yousef Moazzam 13642ab159 Clarify bit position using most/least significant bit terminology 2025-05-24 21:55:39 +01:00
Yousef Moazzam 204abb9c5d Add node type bits info to node mode docstrings 2025-05-24 21:22:54 +01:00
Jeremy Soller c403c1eb54 Update redox-scheme, implement flink and getdents 2025-05-03 13:27:33 -06:00
Jeremy Soller 940b027514 0.6.11 2025-04-20 18:52:21 -06:00
Jeremy Soller d17b938693 Merge branch 'Fix-reset_allocator-regression-incorrectly-returning-ENOSPC' into 'master'
Fix reset_allocator regression that incorrectly returns ENOSPC

See merge request redox-os/redoxfs!89
2025-04-20 21:54:10 +00:00
James Matlik fd0bb82b50 Fix reset_allocator regression that incorrectly returns ENOSPC 2025-04-20 17:43:08 -04:00
Jeremy Soller 3170e8cd9c Merge branch 'misc_improvements' into 'master'
Couple of misc improvements

See merge request redox-os/redoxfs!88
2025-04-01 20:01:31 +00:00
bjorn3 7e3915deb6 Use Iterator::all in two places 2025-04-01 21:53:28 +02:00
bjorn3 22f759aa4f Rustc now allows many derives for packed structs 2025-04-01 21:51:10 +02:00
bjorn3 dc507f7e2a Remove unreachable dup impl 2025-04-01 21:43:51 +02:00
bjorn3 5ee3138d3d Fix cargo test
Rustc complained about being unable to infer the type for []
2025-04-01 21:42:54 +02:00
Jeremy Soller 6bccac38c3 0.6.10: fix mount path on Redox 2025-03-25 15:45:15 -06:00
Jeremy Soller 32aeff0cae Merge branch 'Add-alloc-log-gc' into 'master'
Add alloc log GC

See merge request redox-os/redoxfs!86
2025-03-01 17:17:46 +00:00
James Matlik 1aa3142f21 Add alloc log GC 2025-03-01 17:17:46 +00:00
Jeremy Soller ec5f32ec38 0.6.9 2025-02-28 18:48:18 -07:00
Jeremy Soller 84cf28b5d0 Merge branch 'Fix-leaking-storage-on-file-delete' into 'master'
Fix leaking tree node on file delete

See merge request redox-os/redoxfs!85
2025-03-01 01:25:18 +00:00
James Matlik 9531b20bf8 Fix leaking tree node on file delete 2025-03-01 01:25:18 +00:00
Jeremy Soller 6abf533fdf Merge branch 'mark/fs-cleanup' into 'master'
Documentation & minor cleanup

See merge request redox-os/redoxfs!84
2024-12-24 14:26:19 +00:00
rm-dr a0c734da01 Documentation & minor cleanup 2024-12-24 14:26:19 +00:00
Jeremy Soller 90fbcab52e Merge branch 'document-mount' into 'master'
Document how to mount a RedoxFS partition

See merge request redox-os/redoxfs!83
2024-12-15 14:29:10 +00:00
Ribbon 356ec3ffb1 Document how to mount a RedoxFS partition 2024-12-15 14:29:10 +00:00
Jeremy Soller 9a0da927f9 Fix argument parsing 2024-11-04 21:24:41 -07:00
Jeremy Soller c2790a7168 Merge branch 'nodaemon' into 'master'
Added a no daemon option.

See merge request redox-os/redoxfs!82
2024-11-04 21:38:17 +00:00
JCake 947ea54411 No Daemon option 2024-11-02 21:30:58 +01:00
Jeremy Soller ee04ed2f8c 0.6.8 2024-10-29 07:52:42 -06:00
Jeremy Soller 8f6e47c1da Merge branch 'node_leak' into 'master'
Don't leak node block on node remove.

See merge request redox-os/redoxfs!81
2024-10-29 12:30:31 +00:00
Andrey Turkin c22fb790b8 Fix parallel test runs 2024-10-29 12:22:15 +03:00
Andrey Turkin 1d55c5aa6a Don't leak node block on node remove.
Refs #50
2024-10-29 10:17:47 +03:00
Jeremy Soller 59dbfee126 Merge branch 'fuse_fix' into 'master'
Fix a fuse bug

See merge request redox-os/redoxfs!80
2024-10-27 18:09:10 +00:00
Andrey Turkin 469db981a4 Fix a fuse bug
Create reply contains flags but those are not O_* passed in by request.
Those are FOPEN_* fuse-specific flags describing new file's mode (caching,
passthrough etc).
2024-10-27 20:45:25 +03:00
Jeremy Soller d8950b52c2 Merge branch 'change_blocklevel_calc' into 'master'
Changed for_bytes method for BlockLevel.

See merge request redox-os/redoxfs!79
2024-10-26 01:25:11 +00:00
James Francis 56e66993af Changed for_bytes method for BlockLevel. 2024-10-26 01:25:11 +00:00
Jeremy Soller ab371cff49 Update redox-scheme 2024-10-09 10:12:48 -06:00
Jeremy Soller 97ffcd4ad3 0.6.7 2024-10-06 11:07:32 -06:00
Jeremy Soller b9b740f3b0 Merge branch 'riscv' into 'master'
Bump dependencies

See merge request redox-os/redoxfs!78
2024-10-06 16:12:25 +00:00
Andrey Turkin 08361c6595 Bump dependencies 2024-10-06 18:58:27 +03:00
Jeremy Soller 3cf7bf58ab 0.6.6 2024-09-19 20:47:29 -06:00
4lDO2 707162611e Future-proof repr(packed) => repr(C, packed). 2024-09-08 21:06:03 +02:00
Jeremy Soller 5c8f22b069 Merge branch 'master' into 'master'
Clean up indentation using let-else and format.

See merge request redox-os/redoxfs!76
2024-09-05 13:53:15 +00:00
syn 43ec8d1417 Clean up indentation using let-else and format. 2024-09-02 00:46:48 +03:00
Jeremy Soller 2ac60da18d Merge branch 'fix' into 'master'
Fixed no_std regression in 0.6.5

See merge request redox-os/redoxfs!75
2024-07-15 14:49:34 +00:00
Andrey Turkin 3baf131419 Fixed no_std regression in 0.6.5
And bumped some dependencies and fixed some warnings
2024-06-29 20:52:24 +03:00
Jeremy Soller 554f3dc205 Merge branch 'fuzzer--fuse' into 'master'
Add redoxfs fuzzer for FUSE interface

See merge request redox-os/redoxfs!74
2024-06-27 16:05:42 +00:00
emaxx-g 6a6bd97910 Add redoxfs fuzzer for FUSE interface 2024-06-27 16:05:42 +00:00
Jeremy Soller 6c052ba241 0.6.5 2024-06-24 11:16:25 -06:00
Jeremy Soller af3032de26 Merge branch 'schemev2plus' into 'master'
Use positioned IO interfaces both for the scheme itself and disk IO

See merge request redox-os/redoxfs!73
2024-06-14 12:46:51 +00:00
4lDO2 4f2b5e65ee Update dependencies. 2024-06-14 14:32:47 +02:00
4lDO2 5174b2bba6 Use FileExt::{read,write}_at for disk IO. 2024-06-13 17:20:34 +02:00
4lDO2 708cc1c5ae Use SYS_READ2 and SYS_WRITE2 for disk IO. 2024-06-13 16:30:13 +02:00
4lDO2 8a31d1b6a6 Use positioned IO scheme interface. 2024-06-12 17:37:06 +02:00
4lDO2 3800bf1510 Switch to v2 packet interface. 2024-06-11 16:10:34 +02:00
Jeremy Soller 136ff8c8dc Update dependencies 2024-05-27 09:40:10 -06:00
Jeremy Soller faec36773b 0.6.4 2024-05-27 09:35:10 -06:00
Jeremy Soller 247ef8bf62 Merge branch 'endian-num' into 'master'
Migrate from `redox_simple_endian` to `endian-num`

See merge request redox-os/redoxfs!72
2024-05-27 15:34:24 +00:00
Martin Kröning 8cc2e31299 Migrate from redox_simple_endian to endian-num
Signed-off-by: Martin Kröning <martin.kroening@eonerc.rwth-aachen.de>
2024-05-27 14:13:51 +02:00
Jeremy Soller dcba5d8018 0.6.3: Fix CoW of records dropping data 2024-04-01 13:30:31 -06:00
Jeremy Soller 7dc8323d24 0.6.2: Make libredox only required for std feature 2024-03-23 09:15:06 -06:00
Jeremy Soller e8cea1af66 0.6.1: fix read_records block use after free 2024-03-23 08:52:54 -06:00
Jeremy Soller bfa2f33798 Merge branch 'libredox' into 'master'
Switch almost fully to libredox.

See merge request redox-os/redoxfs!71
2024-03-18 20:37:13 +00:00
4lDO2 15f3c909c7 Switch almost fully to libredox. 2024-03-18 17:28:43 +01:00
Jeremy Soller 1713850c0e Merge branch 'cancellation' into 'master'
Handle but don't impl scheme cancellation.

See merge request redox-os/redoxfs!70
2024-03-16 13:06:10 +00:00
4lDO2 efb29941f5 Handle but don't impl scheme cancellation. 2024-03-16 09:30:14 +01:00
Jeremy Soller 14a07dc268 Merge branch 'master' into 'master'
Master - Name check & Duplicate code removed & Makefile modification

See merge request redox-os/redoxfs!68
2024-03-07 14:40:28 +00:00
George Malandrakis e4ca510bc0 Master - Name check & Duplicate code removed & Makefile modification 2024-03-07 14:40:27 +00:00
Jeremy Soller 7c6e95d9f0 Merge branch 'path-improvements' into 'master'
use new path formatting and conversion code

See merge request redox-os/redoxfs!69
2024-03-01 14:59:34 +00:00
Ron Williams 5156ebd179 use new path formatting and conversion code 2024-03-01 06:24:13 -08:00
Jeremy Soller e7cf569a1f Merge branch 'v6' into 'master'
V6

See merge request redox-os/redoxfs!67
2024-02-15 23:03:38 +00:00
Jeremy Soller c07e46b7ba V6 2024-02-15 23:03:38 +00:00
Jeremy Soller 667ffcc01a Fix test compilation 2024-02-14 12:59:16 -07:00
Jeremy Soller 07e8093ee0 Allow allocating in different sizes 2024-02-14 12:53:06 -07:00
Jeremy Soller ab0bb9aec7 Fix more clippy lints 2024-02-14 12:45:19 -07:00
Jeremy Soller 644a2d2081 Fix clippy lints 2024-02-14 12:38:02 -07:00
Jeremy Soller 4e0ab54567 Make everything scale with block size and format 2024-02-14 10:15:26 -07:00
Jeremy Soller 5f00068c26 Merge branch 'redox-path' into 'master'
Use new path format

See merge request redox-os/redoxfs!66
2024-02-09 15:06:51 +00:00
Ron Williams c1601fe16c Use new path format 2024-02-09 15:06:51 +00:00
Jeremy Soller e3f0fe6ef9 Remove mmap prints 2024-02-08 17:34:20 -07:00
Jeremy Soller b3dd3aa5f0 Merge branch 'symlink-fix' into 'master'
fix symbolic links plus general cleanup

See merge request redox-os/redoxfs!65
2024-01-03 16:39:58 +00:00
Ron Williams 944b175a6e fix symbolic links plus general cleanup 2024-01-03 16:39:58 +00:00
Jeremy Soller 86c12ebd7c 0.5.13: Remove failure dependency and fix no_std 2023-12-12 13:24:59 -07:00
Jeremy Soller 2bec291700 0.5.12: Make range-tree optional 2023-12-12 13:16:31 -07:00
Jeremy Soller 9fad87efbe 0.5.11 2023-12-12 13:07:57 -07:00
Jeremy Soller dcf40072f4 Ensure that dealloc of allocator log blocks are queued, not immediate 2023-12-12 13:07:10 -07:00
Jeremy Soller 9a8437f468 0.5.10: Squash allocations and sync on unmount 2023-12-12 11:10:04 -07:00
Jeremy Soller 97bc669a76 0.5.9: Remove use of stable feature int_roundings 2023-12-11 10:26:27 -07:00
Jeremy Soller 50b4ebfcd5 Merge branch 'fix-statement' into 'master'
Fix GPL statement and quote FUSE for Linux

See merge request redox-os/redoxfs!64
2023-12-07 15:40:25 +00:00
Ribbon 3f1bff26e3 Fix GPL statement and quote FUSE for Linux 2023-12-07 15:40:25 +00:00
Jeremy Soller 6ce9d1fdac Merge branch 'remove_physmap' into 'master'
Replace physmap with fmap.

See merge request redox-os/redoxfs!63
2023-12-07 15:37:06 +00:00
4lDO2 2c2920572c Replace physmap with fmap. 2023-11-18 13:24:27 +01:00
Jeremy Soller ae715277f3 Cache current fmap size 2023-11-15 12:20:15 -07:00
Jeremy Soller 4048f541a0 Merge branch 'improve-readme' into 'master'
Improve/cleanup the README

See merge request redox-os/redoxfs!62
2023-09-30 17:38:07 +00:00
Ribbon b9960350c8 Improve/cleanup the README 2023-09-30 15:44:24 +00:00
Jeremy Soller 985d6e815c 0.5.8: update uuid to 1.4 2023-09-07 16:19:42 -06:00
Jeremy Soller 54341680ec Merge branch 'better_fmap' into 'master'
Offset-based mmap

See merge request redox-os/redoxfs!61
2023-08-07 15:37:15 +00:00
4lDO2 6234d76e8d Fix mremap usage. 2023-08-03 12:38:19 +02:00
4lDO2 7f8a2e467e Update dependencies. 2023-08-02 15:28:35 +02:00
4lDO2 ae1f15a449 Offset-based file mmap. 2023-07-26 16:06:53 +02:00
4lDO2 5db0da52e2 WIP: Use improved mmap interface. 2023-07-20 16:58:58 +02:00
Jeremy Soller e7e0e3ce8d Merge branch 'sighandling' into 'master'
Prefer libc over redox_syscall in mount.

See merge request redox-os/redoxfs!60
2023-06-21 19:27:43 +00:00
4lDO2 58fff36b4e Prefer libc over redox_syscall in mount. 2023-06-18 19:41:08 +02:00
Jeremy Soller 211e12b842 Merge branch 'master' into 'master'
Fix unnecessary mutable variable warning

See merge request redox-os/redoxfs!59
2023-05-08 12:37:33 +00:00
Will Angenent 13948965fa Fix unnecessary mutable variable warning 2023-05-08 13:11:16 +01:00
Jeremy Soller ad432d527d 0.5.7 - enable compilation with stable 2023-04-15 07:34:34 -06:00
Jeremy Soller 2db6998889 Merge commit '588bd3203b70219bf27bedf0703199ec2ea5f505' 2023-04-15 07:32:11 -06:00
Jeremy Soller eb8dc00fa8 Add cfg(kani) to proofs 2023-04-06 14:48:54 -06:00
Jeremy Soller 1a56983249 Formally verify node owner and permission functions 2023-04-05 20:58:51 -06:00
Jeremy Soller 588bd3203b 0.5.6 2023-03-27 08:45:41 -06:00
Jeremy Soller c9f9e0be7d Merge branch 'use-fusermount3-where-possible' into 'master'
Fix image unmounting & tooling to work under gentoo and MacOS

See merge request redox-os/redoxfs!58
2023-03-25 13:55:49 +00:00
Will Angenent 20fff1a0ee Adapted Makefile to work with gentoo and MacOS 2023-03-24 19:19:33 +00:00
Will Angenent a7c22caba5 Added unmount_path method that tries several fusermount binaries
This fixes unmounting with gentoo. This function will be use by the installer to
unmount the image after the build.
2023-03-24 18:54:08 +00:00
Jeremy Soller ace1726e29 0.5.5 2023-03-20 10:23:14 -06:00
Jeremy Soller 9a4638e922 Merge branch 'migrate-from-fuse-to-fuser' into 'master'
Migrate from fuse to fuser

See merge request redox-os/redoxfs!57
2023-03-20 01:15:13 +00:00
Will Angenent 62e59e1006 Migrate from fuse to fuser 2023-03-18 08:53:54 +00:00
Jeremy Soller cdccd5d786 0.5.4 2023-03-08 12:48:42 -07:00
Jeremy Soller 06d43016c1 Ensure header generation zero is written on filesystem create 2023-03-08 12:48:32 -07:00
Jeremy Soller 794e732c80 Merge branch 'fix_root_unlink' into 'master'
Fix bug where root could not remove other users' files

Closes redox#1366

See merge request redox-os/redoxfs!56
2023-03-01 16:28:58 +00:00
4lDO2 73c3d8bb79 Allow root to remove other users' files. 2023-03-01 17:24:37 +01:00
Jeremy Soller 467c9cfe9a Update libc crate 2023-02-11 14:44:07 -07:00
Jeremy Soller 8a1aa3663a Allow removal of temporary directory hardlinks
These will only exist in the middle of rename
2022-12-01 07:32:19 -07:00
Jeremy Soller f601b2a8ce 0.5.3 - make log optional 2022-11-07 18:12:03 -07:00
Jeremy Soller 63bde95462 Update lock file and fix import 2022-11-04 14:05:32 -06:00
Jeremy Soller 82f011363d 0.5.2 2022-11-04 14:04:16 -06:00
Jeremy Soller 898f9d8b2e redoxfs-ar: require disk to exist 2022-11-04 14:03:58 -06:00
Jeremy Soller 9fc80c8bf6 Update cargo.lock 2022-09-05 08:58:06 -06:00
Jeremy Soller 7de07e802e 0.5.1 - add DiskIo type 2022-09-04 20:27:16 -06:00
Jeremy Soller 790ea59269 Increase disk cache to 16 MiB again, it is pre-allocated now 2022-08-22 09:22:09 -06:00
Jeremy Soller 04b4a2e4ad Reduce disk cache size to 8MiB 2022-08-22 09:18:23 -06:00
4lDO2 63468fc414 Replace syscall::clone() with libc::fork() 2022-07-27 16:34:43 +02:00
Jeremy Soller 0ea2fd49dd Update Cargo.lock 2022-07-26 16:14:53 -06:00
Jeremy Soller 0e0ae520c5 Reduce disk cache size to 16 MiB 2022-04-13 09:03:03 -06:00
Jeremy Soller 9215e32254 Implement frename 2022-04-07 19:57:09 -06:00
Jeremy Soller 419bd0f0a6 Fix for some kinds of symbolic links 2022-04-06 18:17:23 -06:00
4lDO2 ebe9579d66 Use crates.io version of syscall, again. 2022-03-27 12:09:50 +02:00
4lDO2 d83e567e97 Update syscall 2022-03-25 15:29:16 +01:00
Jeremy Soller f9f4f7c5f0 Use redox_simple_endian fork 2022-03-11 09:09:11 -07:00
Jeremy Soller 2e46c28097 Merge branch '0.5.0' into 'master'
RedoxFS 0.5.0

See merge request redox-os/redoxfs!54
2022-03-11 16:02:22 +00:00
Jeremy Soller 731b97262b RedoxFS 0.5.0
Significant re-design to use copy-on-write and hashes for all data
2022-03-11 09:01:47 -07:00
Jeremy Soller c98e63af87 Improve speed by not enumerating all disks when opening by UUID 2022-02-11 10:07:40 -07:00
Jeremy Soller cfe5841646 0.4.4 - no_std support 2022-02-05 20:28:11 -07:00
Jeremy Soller 9083dd07c9 Fix warnings and remove dead code 2021-12-26 19:47:44 -07:00
4lDO2 25be3a82bd Update syscall. 2021-06-17 14:38:08 +02:00
Jeremy Soller 6d9a53c956 Update dependencies 2021-04-28 20:54:39 -06:00
Jeremy Soller c6b997f98b Update dependencies 2021-01-19 11:12:59 -07:00
Jeremy Soller 5d4ed0831a 0.4.3 - update redox_syscall 2020-12-16 10:43:30 -07:00
Jeremy Soller f1b88c38c0 Update syscall 2020-08-02 15:34:01 -06:00
Jeremy Soller 0068a6d498 Update dependencies 2020-05-25 07:42:45 -06:00
Jeremy Soller 5ff37f1f77 Update lock file 2020-03-13 20:06:34 -06:00
Jeremy Soller c990fa9a45 0.4.1 - Support rename in fuse 2020-03-13 20:06:01 -06:00
Jeremy Soller 80e3a34645 Merge branch 'format' into 'master'
Format the codebase

See merge request redox-os/redoxfs!49
2020-01-20 22:06:34 +00:00
Xavier L'Heureux 58f2d28ff5 Format the codebase 2020-01-20 15:00:26 -05:00
Jeremy Soller a6af6a9289 Merge branch 'add-atime' into 'master'
Add tracking of access times

See merge request redox-os/redoxfs!48
2020-01-20 16:56:51 +00:00
AdminXVII d2256b3eb3 Add tracking of access times
Add the atime field, which tracks the last access time. Only reads
update the atime.

**BREAKING CHANGE**: One less extent available per block, 4 more bytes
for node names.
2020-01-20 16:56:51 +00:00
Jeremy Soller 3191d7d182 Add argument to locate redoxfs by block 2019-10-28 20:27:53 -06:00
Jeremy Soller bede93cd85 0.3.7 2019-09-08 20:29:55 -06:00
Jeremy Soller e869b3b158 Merge branch 'update_path' into 'master'
Update path after frename

See merge request redox-os/redoxfs!46
2019-08-11 16:06:44 +00:00
Deepak Sirone 924adef1dc Update path after frename 2019-08-11 15:30:09 +05:30
Jeremy Soller 602a63acf6 Return EXDEV to tell kernel to resolve cross-scheme links 2019-07-23 21:05:54 -06:00
Jeremy Soller 43533e8dc7 Merge branch 'jD91mZM2/redoxfs-master' into HEAD 2019-07-20 19:41:48 -06:00
Jeremy Soller bf4d0161be Merge branch 'tests' into 'master'
Tests

See merge request redox-os/redoxfs!45
2019-07-20 17:08:33 +00:00
Jeremy Soller 4578a22297 Tests 2019-07-20 17:08:33 +00:00
Jeremy Soller 5e5656f110 Make mount points not require an extra reference 2019-07-18 21:36:16 -06:00
Jeremy Soller 86b0d612d8 Fix yaml again 2019-07-06 11:39:29 -06:00
Jeremy Soller 61c38afb52 Fix test:redox dependencies 2019-07-06 11:39:08 -06:00
Jeremy Soller 8bbdc558bb Fix yaml 2019-07-06 11:38:35 -06:00
Jeremy Soller 2ddf1dab3f Update CI configuration to use redoxer 2019-07-06 11:37:19 -06:00
Jeremy Soller 8e38015b2f Set libc to 0.2.54 2019-07-06 11:27:48 -06:00
Jeremy Soller 3872f74e9a 0.3.6 - Accept AsRef<Path> for disk path arguments 2019-07-06 08:21:23 -06:00
Jeremy Soller 39e8b8fa7f 0.3.5 - make archive_at public 2019-07-06 08:07:54 -06:00
Jeremy Soller b26195f49a 0.3.4 - expose archiving functionality 2019-07-04 15:46:12 -06:00
Jeremy Soller a70a01bd4a 0.3.3 2019-05-11 12:27:52 -06:00
Jeremy Soller a226d19096 Implement symlinks and fix truncation 2019-05-11 12:26:01 -06:00
Jeremy Soller 7b661e31e3 Remove unused ctime parameter 2019-05-11 11:51:46 -06:00
Jeremy Soller 966105e566 Copy uid, gid, ctime, and mtime 2019-05-11 11:50:19 -06:00
Jeremy Soller 237c156503 Read permissions from original file 2019-05-11 11:46:49 -06:00
Jeremy Soller 946efa2fb5 Add sparse disk file to not require image to be sized initially. 2019-05-08 21:07:21 -06:00
Jeremy Soller 0754019950 Truncate disk image 2019-05-08 20:55:37 -06:00
Jeremy Soller 842744c384 Merge branch 'master' of https://gitlab.redox-os.org/redox-os/redoxfs 2019-05-08 20:35:15 -06:00
Jeremy Soller 2e32481d4c Add redoxfs-ar program to create a redoxfs image from a folder 2019-05-08 20:34:20 -06:00
Jeremy Soller ec072bb718 Return correct error for fevent on files 2019-04-28 13:32:43 -06:00
Jeremy Soller 015d352e33 Fixes for Redox when part of unix target family 2019-04-23 13:55:46 -06:00
Jeremy Soller 219bf26966 Update for new rust 2019-04-07 10:49:56 -06:00
Jeremy Soller ff17fed199 Reimplement fmap/funmap using improvements to kernel 2019-04-06 20:11:51 -06:00
Jeremy Soller cb2bcf9f59 Fix redoxfs fmap using incorrect offset 2019-04-02 20:18:40 -06:00
Jeremy Soller a352d7c79c Merge branch 'add-getuuid' into 'master'
redoxfs: Add '--get-uuid' support

See merge request redox-os/redoxfs!44
2019-03-15 19:21:32 +00:00
Robin Randhawa 37aab3e566 redoxfs: Add '--get-uuid' support
With this, `redoxfs --get-uuid <disk>` prints out the UUID of the
provided redoxfs disk image.

This is needed especially for aarch64 kernels where the UUID of the boot
disk needs to be passed to the kernel by the bootloader. With this
extension to the mount command, the kernel build glue can query the
prepared redoxfs image for it's UUID.
2019-03-15 15:48:25 +00:00
Jeremy Soller b4df131aff Fix disk usage calculation 2019-02-24 20:46:21 -07:00
Jeremy Soller b32a421b97 Correct access control 2018-12-30 09:58:58 -07:00
Jeremy Soller 69d6cc05de Update fmap support 2018-12-30 09:47:33 -07:00
Jeremy Soller 3ae32d9901 Reduce diff - remove dirty flag as it could be added in a separate change 2018-12-08 08:47:28 -07:00
Jeremy Soller d53fc6afff Handle sigterm instead of sigkill 2018-12-08 08:18:21 -07:00
Jeremy Soller 87ecce9e44 Cleanup and use unmount flag inside of Redox scheme processing function 2018-12-08 08:10:23 -07:00
Jeremy Soller 3e1fc9dae5 Merge branch 'master' into liamnprg/redoxfs-master 2018-12-08 07:40:28 -07:00
Liam Naddell f96d330993 wip implementation of unmounting and dirty bits for redoxfs 2018-12-06 11:09:41 -05:00
Jeremy Soller 9c6e0e6789 Update Cargo.lock 2018-10-14 19:56:46 -06:00
jD91mZM2 79cef7c034 Adopt linux' O_APPEND behavior
According to `man open`:

> The file is opened in append mode. Before each write(2), the file offset
is positioned at the end of the file, as if with lseek(2). The modi‐
fication of the file offset and the write operation are performed as a
single atomic step.
2018-10-07 15:22:13 +02:00
Jeremy Soller 6f5ca24838 Merge branch 'master' into 'master'
A little bit more documentation

See merge request redox-os/redoxfs!38
2018-08-01 23:06:00 +00:00
Liam Naddell 6126bfeee3 docs 2018-08-01 18:47:18 -04:00
Jeremy Soller 3ef8557865 Merge branch 'master' into 'master'
Fmaps: Fix bug with syncing

See merge request redox-os/redoxfs!36
2018-07-03 13:23:34 +00:00
Jeremy Soller 3906a90a7b Merge branch 'mggmuggins/ci' into 'master'
Gitlab CI

See merge request redox-os/redoxfs!37
2018-07-02 18:32:30 +00:00
SamwiseFilmore f0450a4564 Gitlab CI 2018-07-02 18:32:30 +00:00
jD91mZM2 53a3f6d7e9 Fmaps: Fix bug with syncing 2018-06-18 20:21:44 +02:00
Jeremy Soller e9a7181e9f Merge branch 'master' into 'master'
fmap support

See merge request redox-os/redoxfs!35
2018-06-18 10:00:21 +00:00
jD91mZM2 8c57fd95e7 Fix bugs & increase page size 2018-06-18 10:33:08 +02:00
jD91mZM2 a1c69a8e82 WIP: fmap support 2018-06-17 18:40:09 +02:00
Jeremy Soller a345132e0f Update links to gitlab 2018-06-12 12:30:45 -06:00
Jeremy Soller 72b0492624 Prevent colons from being used in filenames 2018-05-24 08:43:05 -06:00
Jeremy Soller 78c5e1c6d2 Enter null namespace for redoxfs 2018-01-20 19:37:06 -07:00
Jeremy Soller 40e2a3e221 Implement frename fixing #37 and fix #11 by checking names 2017-12-29 14:08:36 -07:00
Jeremy Soller 2e6e272c72 Add stub for frename 2017-12-27 21:09:05 -07:00
Jeremy Soller f206dab653 Fix error with borrowing packed fields 2017-12-06 20:12:02 -07:00
Jeremy Soller eac37c7dc5 Add fchmod, fchown 2017-11-29 21:05:17 -07:00
Jeremy Soller 38b24ad321 0.3.2 2017-11-19 21:22:44 -07:00
Jeremy Soller 53a1e6d49f Move cache to HashMap 2017-11-19 21:00:07 -07:00
Jeremy Soller 16d6f6b93a Simplify disk cache 2017-11-19 20:51:12 -07:00
Jeremy Soller 2dd4c920c7 0.3.1 - fix issue with new FUSE 2017-11-19 16:22:35 -07:00
Jeremy Soller 1701198b11 0.3.0 2017-11-19 16:18:27 -07:00
Jeremy Soller 47c9acc417 Version 3 - bump block size to 4096 2017-11-19 16:18:06 -07:00
Jeremy Soller 513bfaf805 Set block size programatically 2017-11-19 16:15:01 -07:00
Jeremy Soller a0ec667025 Update lock format 2017-11-19 16:04:16 -07:00
Jeremy Soller ab690ce519 Update Cargo.lock 2017-10-11 20:54:45 -06:00
Jeremy Soller d4ea343a39 Fix disk scheme path 2017-09-30 20:34:34 -06:00
Jeremy Soller e74975d2f9 Add UUID matching 2017-09-30 20:24:59 -06:00
Jeremy Soller db22937361 Implement UUID 2017-09-30 15:07:46 -06:00
Jeremy Soller 6d62d22262 Fix offset calculations 2017-09-27 21:15:27 -06:00
Jeremy Soller cbdfba7b94 Allow a bootloader to be written during redoxfs-mkfs 2017-09-27 20:27:38 -06:00
Jeremy Soller 90dc177d75 Add docs.rs badge 2017-09-26 19:57:06 -06:00
Jeremy Soller ade6234f12 Update spin 2017-09-26 19:56:14 -06:00
Jeremy Soller dc0b0c5c2d Fix compilation on Redox 2017-09-26 19:55:10 -06:00
Jeremy Soller dfd2dd8ae1 Cleanup fuse backend, send signal at correct time 2017-09-26 19:36:27 -06:00
Jeremy Soller 260bc1e1e8 Fix cargo doc 2017-09-26 19:17:49 -06:00
Jeremy Soller 541a58181a Update FUSE 2017-09-25 21:50:07 -06:00
Jeremy Soller 070aeb54ef Cleanup to allow use of most functions when linking the library, including mounting and creating filesystems 2017-09-25 19:11:02 -06:00
Jeremy Soller b272cc2d19 Use nightly friendly spin 2017-09-18 22:01:58 -06:00
Jeremy Soller 9c907a22dd Merge pull request #34 from ids1024/import
Remove unused import causing lint
2017-09-11 12:50:22 -06:00
Ian Douglas Scott a9da8e7a57 Remove unused import causing lint 2017-09-11 10:22:27 -07:00
Jeremy Soller 8f41e5a777 Update Cargo.lock 2017-08-19 14:46:42 -06:00
Jeremy Soller 213b4aea5f Merge pull request #33 from ids1024/utimens
futimens: do not require O_RWONLY/O_RDWR
2017-08-14 17:59:00 -06:00
Ian Douglas Scott dc5bd70728 futimens: do not require O_RWONLY/O_RDWR 2017-08-14 16:35:03 -07:00
Jeremy Soller fc0af50b97 Merge pull request #32 from ids1024/directoryperm
Make it not an error to open a directory without O_DIRECTORY or O_STAT
2017-08-12 13:29:09 -06:00
Ian Douglas Scott f4a3e1dde0 Make read() on directory not opened as O_DIRECTORY return EISDIR 2017-08-12 12:25:36 -07:00
Ian Douglas Scott f474a9fd77 Actually, O_RDONLY should work with a directory 2017-08-12 11:32:39 -07:00
Ian Douglas Scott 93f7ec7a55 Make it not an error to open a directory without O_DIRECTORY or O_STAT
POSIX says this should work, and some software seems to rely on that.
2017-08-11 18:32:49 -07:00
Jeremy Soller 801cbd9072 Merge pull request #31 from ids1024/mut
Remove unneeded uses of 'mut'
2017-08-11 16:21:43 -06:00
Ian Douglas Scott 4f3b097a4a Remove unneeded uses of 'mut'
This seems to be a warning with the latest Rust, and an error due to #![deny(warnings)]
2017-08-11 14:55:19 -07:00
Jeremy Soller a830383220 Update Cargo.lock 2017-08-02 19:52:30 -06:00
Jeremy Soller e270ce117f Merge pull request #30 from ids1024/free
Avoid corrupting free node
2017-08-02 07:27:10 -06:00
Ian Douglas Scott 0996d844c4 Avoid corrupting free node 2017-08-01 20:27:43 -07:00
Jeremy Soller 1e9cec6092 Typo 2017-08-01 18:26:58 -06:00
Jeremy Soller e95be4caf3 Dealocate when node size changes 2017-08-01 18:23:45 -06:00
Jeremy Soller 3b78720167 Update Cargo.lock 2017-07-29 08:25:06 -06:00
Jeremy Soller b04efe081b Merge pull request #29 from ids1024/dirlink
Directory symlinks
2017-07-26 19:05:59 -06:00
Ian Douglas Scott 03f59a419b Make directory symlinks work correctly 2017-07-26 16:17:49 -07:00
Ian Douglas Scott ca1a1d615a Move symlink resolving to another function 2017-07-26 16:17:48 -07:00
Jeremy Soller c6c9399305 Merge branch 'master' of https://github.com/redox-os/redoxfs 2017-07-26 07:46:49 -06:00
Jeremy Soller 862e9b0a37 Add Cargo.lock 2017-07-26 07:46:45 -06:00
Jeremy Soller 154b456d99 Merge pull request #28 from ids1024/mtimefuse
fuse: allow setting mtime to earlier time
2017-07-25 17:32:05 -06:00
Ian Douglas Scott 5a1aaadd05 fuse: allow setting mtime to earlier time 2017-07-25 15:17:15 -07:00
Jeremy Soller 8737f19459 Merge pull request #27 from ids1024/readdir
fuse: Fix readdir when directory does not fit in buffer
2017-07-25 12:17:46 -06:00
Ian Douglas Scott 92d38e32c1 fuse: Fix readdir when directory does not fit in buffer 2017-07-25 08:59:35 -07:00
Jeremy Soller e635648ed7 Use EINVAL instead of ENOENT 2017-07-22 13:14:31 -06:00
Jeremy Soller f40f6921e8 Return error if dup buf is not empty 2017-07-22 13:08:08 -06:00
Jeremy Soller 3342778968 Merge pull request #26 from ids1024/unlinkuid
Require same uid as owner to unlink, not write permission
2017-07-16 18:52:21 -06:00
Ian Douglas Scott ad4826eb70 Require same uid as owner to unlink, not write permission 2017-07-16 15:43:59 -07:00
Jeremy Soller 7fc1f16a67 Merge pull request #25 from ids1024/o_creat
Do not fail due to file permissions on creation
2017-07-15 08:08:43 -06:00
Ian Douglas Scott cae1402f5a Do not fail due to file permissions on creation
Matches Posix.
2017-07-15 07:01:21 -07:00
Jeremy Soller bc2e8c2923 Merge pull request #24 from ids1024/fcntl
Make fcntl not clobber access mode
2017-07-12 14:41:33 -06:00
Ian Douglas Scott 8c03929973 Make fcntl not clobber access mode 2017-07-12 13:39:26 -07:00
Jeremy Soller 6965d67efd Remove unused import 2017-07-09 18:51:11 -06:00
Jeremy Soller 1549d1af8b Use second argument as mtime 2017-07-09 18:07:00 -06:00
Jeremy Soller 8d66d9ce54 Add futimens 2017-07-09 15:54:03 -06:00
Jeremy Soller 1f97d8220c Add times to stat 2017-07-09 10:25:26 -06:00
Jeremy Soller 20bce53b7a Fix build on Redox 2017-07-09 10:21:20 -06:00
Jeremy Soller 9471a2e410 WIP: Add mtime/ctime to Redox 2017-07-09 10:15:41 -06:00
Jeremy Soller dcce0d9e6e Add associated consts 2017-07-08 19:24:34 -06:00
Jeremy Soller 4820210cca Merge pull request #22 from ids1024/O_NOFOLLOW
Follow symlinks on O_STAT, unless O_NOFOLLOW is passed
2017-07-04 08:17:11 -06:00
Ian Douglas Scott 0d12413297 Follow symlinks on O_STAT, unless O_NOFOLLOW is passed 2017-07-03 19:23:23 -07:00
Jeremy Soller 2c770ca997 Merge pull request #21 from ids1024/unlink
Support unlink() on symlink
2017-07-03 05:25:47 -06:00
Ian Douglas Scott 3e0ce05e23 Support unlink() on symlink 2017-07-02 19:16:14 -07:00
Jeremy Soller 0032159492 Merge pull request #20 from ids1024/fuse
Symlinks in fuse
2017-06-30 12:36:19 -06:00
Ian Douglas Scott f582c60f34 Symlinks in fuse 2017-06-30 11:24:53 -07:00
Jeremy Soller 97652cc6f8 Merge pull request #19 from ids1024/symlink
Return EINVAL for O_SYMLINK on non-symlink
2017-06-29 16:52:12 -06:00
Ian Douglas Scott dffa85f874 Return EINVAL for O_SYMLINK on non-symlink 2017-06-29 14:55:49 -07:00
Jeremy Soller b972f08f50 Merge pull request #18 from ids1024/symlink
[WIP] Symlinks
2017-06-29 15:46:52 -06:00
Ian Douglas Scott 0d76e1441b Relative symlink 2017-06-29 14:08:53 -07:00
Ian Douglas Scott 7a4a41a34e Work in progress symlink implementation 2017-06-26 09:14:12 -07:00
Jeremy Soller 7470cc7846 Merge pull request #17 from ids1024/seek
Allow seek beyond end of file.
2017-06-02 20:23:04 -06:00
Ian Douglas Scott 86353cdff5 Allow seek beyond end of file.
This is enough to fix as. I don't know if it correctly fills the space
with zeros when writing beyond the current EOF.
2017-06-02 19:00:54 -07:00
Jeremy Soller 34f9b3f6df Merge pull request #16 from ids1024/try
Use ? instead of try!
2017-05-29 22:07:21 +02:00
Ian Douglas Scott 7d2663e6a2 Also initialize Stat to zero in implementation of Resource
I didn't notice there were two copies of this code
2017-05-28 23:26:57 -07:00
Ian Douglas Scott 1f90f21147 Use ? instead of try! 2017-05-28 23:06:45 -07:00
Jeremy Soller 05e3802844 Merge pull request #15 from ids1024/stat
Initialize fields of Stat struct to 0
2017-05-28 18:50:11 +02:00
Ian Douglas Scott 31cfde4eaf Use Default::default() 2017-05-28 09:17:36 -07:00
Ian Douglas Scott 138525cf7d Initialize fields of Stat struct to 0
Prevents potential undefined behavior based on how the call is made. Of
course, eventually these fields should actually be set.
2017-05-28 09:10:52 -07:00
Jeremy Soller 6c1d98cea8 Merge pull request #14 from bungcip/travis_macos_error
fix travis macos build
2017-04-26 17:15:15 -06:00
Gigih Aji Ibrahim 6642d36079 trying to fix travis macos build fail. don't know if this work or not. I don't have mac to test it :) 2017-04-27 05:32:20 +07:00
Jeremy Soller f0c38fe609 Perform cloexec in kernel 2017-04-16 12:52:07 -06:00
Jeremy Soller f418d30dc9 Add O_APPEND functionality 2017-04-09 22:09:29 -06:00
Jeremy Soller d0dfef2f0b Fix bug in seek 2017-04-09 22:00:26 -06:00
Jeremy Soller 3db0c2a4b1 Fix handling of missing files 2017-03-15 19:42:11 -06:00
Jeremy Soller aff13c0d66 Merge pull request #8 from xTibor/formatting_and_docs_fixes
Small formatting and documentation fixes
2017-02-13 19:47:33 -07:00
xTibor 5dc6c48623 Small formatting and documentation fixes 2017-02-03 00:04:32 +01:00
Jeremy Soller fa6c19f79b Add inode number to redoxfs 2017-01-30 20:44:09 -07:00
Jeremy Soller 665120f063 Better error messages 2017-01-13 10:44:11 -07:00
Jeremy Soller 3f0bda4e14 Use crates.io for dependencies 2017-01-13 10:28:44 -07:00
Jeremy Soller 5ab8c050d8 Fix for build on OS X 2016-12-13 18:58:59 -07:00
Jeremy Soller d0c698a031 Merge pull request #7 from redox-os/merge_fuse_scheme
Merge fuse and scheme, add mkfs utility, remove old utility
2016-12-13 18:05:24 -07:00
Jeremy Soller 9c2864e130 Merge fuse and scheme, add mkfs utility, remove old utility 2016-12-13 18:05:03 -07:00
Jeremy Soller f1073d0c48 Add leading / to path 2016-11-29 18:13:56 -07:00
Jeremy Soller b526a29e35 Enable caching again 2016-11-28 14:31:00 -07:00
Jeremy Soller 25fe6d3826 Implement O_DIRECTORY 2016-11-25 18:24:15 -07:00
Jeremy Soller dcaf5c76be Add statvfs 2016-11-18 08:42:48 -07:00
Jeremy Soller 31605167ce Add chmod 2016-11-15 17:07:37 -07:00
Jeremy Soller 8a22d96669 Minimal O_CLOEXEC implementation 2016-11-15 16:11:43 -07:00
Jeremy Soller 8449c5b6ad Switch to using fork 2016-11-10 20:01:30 -07:00
Jeremy Soller d058e56d6e Fix access modes 2016-11-08 10:52:27 -07:00
Jeremy Soller 81c39551d7 Update .travis.yml 2016-11-06 18:37:52 -07:00
Jeremy Soller fd4d674422 Merge pull request #4 from genodeftest/patch-3
Travis: fix config (on Linux)
2016-11-05 09:55:17 -06:00
Christian Stadelmann a313852428 Travis: fix config (on Linux)
With this change, travis will work on Linux at least.
2016-11-05 16:46:34 +01:00
Jeremy Soller b25b268756 Fix syscall crate name 2016-11-02 19:46:34 -06:00
Jeremy Soller a0b381765b Use defer permissions instead 2016-11-01 15:33:49 -06:00
Jeremy Soller 3a58139e57 Only add allow_root on OS X 2016-11-01 15:08:08 -06:00
Jeremy Soller 14cbb4f4f8 Merge pull request #3 from Yoric/master
Let root manipulate a fuse-mounted redoxfs vfs.
2016-11-01 14:29:30 -06:00
David Teller 0353bfc00c Let root manipulate a fuse-mounted redoxfs vfs.
This (along with a few Makefile changes) solves my issues building on macOS.
2016-11-01 21:21:20 +01:00
Jeremy Soller a5b6dcc0c0 Disable cache to improve performance 2016-10-31 21:56:35 -06:00
Jeremy Soller 4ecb62da40 Build redoxfs using syscall git 2016-10-31 13:27:35 -06:00
Jeremy Soller 5c0fc80db2 Merge pull request #2 from redox-os/new_kernel
New kernel
2016-10-26 13:21:48 -06:00
Jeremy Soller ec00f58d73 Add buf to dup 2016-10-25 12:16:28 -06:00
Jeremy Soller 3dcaad55fe Implement unix permissions 2016-10-05 18:00:46 -06:00
Jeremy Soller c06edb232b Lookup uid, gid, and mode from block for stat 2016-10-05 14:07:31 -06:00
Jeremy Soller 33f218aadb WIP: Permissions support 2016-10-05 12:13:45 -06:00
Jeremy Soller 1488d1ef56 64-bit stat size 2016-09-28 20:41:45 -06:00
Jeremy Soller 36b3cf04a9 Add name to fpath 2016-09-28 10:27:32 -06:00
Jeremy Soller 9c16bca2a5 Fix warning 2016-09-28 08:58:51 -06:00
Jeremy Soller 9bd5d69776 Compatibility with new kernel 2016-09-27 21:55:57 -06:00
Jeremy Soller 11f3485173 Port to new kernel 2016-09-27 21:19:28 -06:00
Jeremy Soller b7608c1530 Merge pull request #1 from redox-os/new_abi
Switch to new ABI
2016-08-10 19:18:32 -06:00
Jeremy Soller 35ed814e68 WIP: Switch to new ABI 2016-08-08 21:31:12 -06:00
Jeremy Soller fe0a5de2d4 Decrease size to increase install speed 2016-07-31 12:56:56 -06:00
Jeremy Soller ac8cee662e Increase header search range to 4 MB 2016-07-31 06:53:44 -06:00
Jeremy Soller dc575b790f Use Duration sleep 2016-07-30 18:01:16 -06:00
Jeremy Soller fb2eb845ba Increase size of image 2016-07-28 16:55:36 -06:00
Jeremy Soller e8d92bee0f Remove print 2016-06-21 11:14:42 -06:00
Jeremy Soller dd4a90119f Fixes for node_ensure_len 2016-06-21 11:13:59 -06:00
Jeremy Soller 55e6b13555 Alignment of write 2016-06-21 11:01:45 -06:00
Jeremy Soller d9bdd6e3dd Comment out fstat 2016-06-10 20:20:47 -06:00
Jeremy Soller ac07b358ad Support O_TRUNC in open 2016-06-09 20:24:10 -06:00
Jeremy Soller 896812c68a Sync size when truncate occurs 2016-06-09 20:23:30 -06:00
Jeremy Soller 4316326792 Remove debug for 'Creating cache' 2016-06-08 21:08:21 -06:00
Jeremy Soller b6d49e2fa1 Reenable dup, disable debugging 2016-06-07 17:30:33 -06:00
Jeremy Soller 195271779d old_id vs id 2016-06-06 18:24:29 -06:00
Jeremy Soller 7e80cba121 Add dup 2016-06-06 17:29:57 -06:00
Jeremy Soller 32c61e93e0 C stat support 2016-06-03 23:23:40 -06:00
Jeremy Soller d17b55089a More efficient reads 2016-05-16 09:49:05 -06:00
Jeremy Soller cf728be7ae Remove debugging on filesystem open 2016-05-09 20:15:01 -06:00
Jeremy Soller 3841521174 Deny warnings 2016-04-29 17:38:56 -06:00
Jeremy Soller 0bb53636e4 Faster read from disk if cache miss 2016-04-19 20:14:31 -06:00
Jeremy Soller dc5326a13a FUSE truncation 2016-04-16 15:57:11 -06:00
Jeremy Soller 7d48fcf382 WIP: Add truncate 2016-04-16 13:33:21 -06:00
Jeremy Soller fb55c69fe5 Add stat 2016-04-15 08:35:33 -06:00
Jeremy Soller e97f126f35 Fixes for dirs and files so they do not have to read all contents at once 2016-04-14 21:06:12 -06:00
Jeremy Soller f509c4b12c Caching! 2016-04-14 17:29:08 -06:00
Jeremy Soller e355f755ee Comment out prints 2016-04-13 15:34:06 -06:00
Jeremy Soller 94c36fa34d Fixes to fuse, allow filesystem offset 2016-04-13 15:23:52 -06:00
Jeremy Soller d13d926d7b Fixes for fuse 2016-04-12 20:56:38 -06:00
Jeremy Soller 8d806568b9 Turn off per-packet messages 2016-04-12 20:51:04 -06:00
Jeremy Soller bef665af8d Add debugging to scheme, add padding to header 2016-04-12 14:07:49 -06:00
Jeremy Soller 9cf1d60253 Add df support 2016-03-09 14:43:44 -07:00
Jeremy Soller dcc67e4cf4 Fixes for redox scheme 2016-03-09 14:12:17 -07:00
Jeremy Soller 77848132ea Fuse fixes, file read/write implemented 2016-03-09 14:10:56 -07:00
Jeremy Soller 73528fa872 Add read file support 2016-03-09 12:48:16 -07:00
Jeremy Soller a1cae73566 FUSE support for RedoxFS (WIP) 2016-03-09 12:12:46 -07:00
Jeremy Soller b1805779a8 Write can realloc 2016-03-03 17:18:41 -07:00
Jeremy Soller ff0106b212 Support for less-than-sector block allocations 2016-03-03 15:01:36 -07:00
Jeremy Soller 51e037150c Merge branch 'master' of https://github.com/redox-os/redoxfs 2016-03-02 11:42:12 -07:00
Jeremy Soller 856db3e21d WIP: Collection 2016-03-02 11:41:59 -07:00
Jeremy Soller 1a58014da9 Add more attributes 2016-03-01 21:08:21 -07:00
Jeremy Soller 6d5be41c13 Add truncate 2016-03-01 20:09:47 -07:00
Jeremy Soller 2d578215e3 rmdir, mkdir 2016-03-01 19:26:40 -07:00
Jeremy Soller 25174cc4ce WIP: Functions for accessing file data 2016-02-29 21:32:37 -07:00
Jeremy Soller 7412bdd6b7 WIP: Read/write 2016-02-29 20:02:54 -07:00
Jeremy Soller d2a073788a Add unlink and creat 2016-02-27 10:26:53 -07:00
Jeremy Soller 702bef4418 Path handling 2016-02-26 20:22:41 -07:00
Jeremy Soller 799a847ffa WIP: Scheme 2016-02-26 19:30:49 -07:00
Jeremy Soller 670d874d07 Use unix errors 2016-02-26 18:51:29 -07:00
Jeremy Soller 7f8776fafb Do not allow rmdir when node has children 2016-02-26 15:14:16 -07:00
Jeremy Soller 664801337d Add to free node list, fix insertion bug 2016-02-26 14:59:00 -07:00
Jeremy Soller ecfd43fdbd Fix remove truncation 2016-02-26 14:24:03 -07:00
Jeremy Soller 7a4d78149a WIP: Remove 2016-02-26 14:18:29 -07:00
Jeremy Soller 1eff7c7e37 WIP: Simple way to fill file with data 2016-02-26 10:53:05 -07:00
Jeremy Soller 39707bcf60 Add signature as constant 2016-02-25 16:33:12 -07:00
Jeremy Soller 1f08b76531 Add cd, Use invalid version until stable 2016-02-25 16:31:28 -07:00
Jeremy Soller e0fceebffd Allow a parent node to be given to the primitive functions 2016-02-25 14:25:14 -07:00
Jeremy Soller 4a444a3423 Add find function 2016-02-25 12:29:55 -07:00
Jackpot51 6450660c50 Create DESIGN.md 2016-02-25 12:29:08 -07:00
Jeremy Soller 556c842ec7 Add exnode 2016-02-24 21:59:57 -07:00
Jeremy Soller d51e891f22 Add to root node on mkdir, touch 2016-02-24 21:23:46 -07:00
Jeremy Soller 41e716219d Very basic allocation 2016-02-24 18:47:47 -07:00
Jeremy Soller bae24a54b0 Improve debugging and image creation 2016-02-24 18:23:25 -07:00
Jeremy Soller 8ff4863bbb Cleanup, add node and header commands 2016-02-24 17:30:37 -07:00
Jeremy Soller d116ce57da Add simple shell 2016-02-24 16:04:30 -07:00
Jeremy Soller c231ddb219 Remove cat todo 2016-02-24 15:06:35 -07:00
Jeremy Soller 49925a39d0 Error type, subdir argument 2016-02-24 14:59:14 -07:00
Jeremy Soller bc2d90f531 Allow read of filesystem names 2016-02-24 14:02:37 -07:00
279 changed files with 13783 additions and 41629 deletions
-5
View File
@@ -1,5 +0,0 @@
[build]
rustflags = [
# Kernel should preserve floating-point registers
"-Ctarget-feature=-sse,-sse2",
]
+3 -2
View File
@@ -1,3 +1,4 @@
target target
/config.toml image.bin
.gitlab-ci-local/ image
image*
+15 -74
View File
@@ -1,90 +1,31 @@
image: "redoxos/redoxer:latest" image: "redoxos/redoxer"
variables:
GIT_SUBMODULE_STRATEGY: recursive
workflow: workflow:
rules: rules:
- if: '$CI_PROJECT_NAMESPACE == "redox-os"' - if: '$CI_COMMIT_BRANCH == "master" && $CI_PROJECT_NAMESPACE == "redox-os"'
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"' - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
stages: stages:
- build - build
- cross-build
- test - test
- other-features
# TODO: benchmarks and profiling (maybe manually enabled for relevant MRs)?
x86_64: build:linux:
stage: build stage: build
script: script: cargo +nightly build --verbose
- mkdir -p target/${ARCH}
- redoxer env make BUILD=target/${ARCH}
variables:
ARCH: "x86_64"
aarch64: build:redox:
stage: cross-build
image: "redoxos/redoxer:aarch64"
script:
- mkdir -p target/${ARCH}
- redoxer env make BUILD=target/${ARCH}
variables:
ARCH: "aarch64"
i586:
stage: cross-build
script:
- mkdir -p target/${ARCH}
- TARGET=${ARCH}-unknown-redox redoxer env make BUILD=target/${ARCH}
variables:
ARCH: "i586"
riscv64gc:
stage: cross-build
script:
- mkdir -p target/${ARCH}
- TARGET=${ARCH}-unknown-redox redoxer env make BUILD=target/${ARCH}
variables:
ARCH: "riscv64gc"
fmt:
stage: build stage: build
script: script: redoxer build --verbose
- rustup component add rustfmt
- rustfmt --check
x86_64:boot: test:linux:
stage: test stage: test
needs: [x86_64] dependencies:
script: - build:linux
- mkdir -p target/${ARCH} script: cargo +nightly test --verbose
- export COOKBOOK_SOURCE_IDENT=$CI_COMMIT_SHA
- redoxer env make BUILD=target/${ARCH}
- timeout -s KILL 9m redoxer exec --folder target/${ARCH}/:/usr/lib/boot uname -a
variables:
ARCH: "x86_64"
x86_64:relibc: test:redox:
stage: test stage: test
needs: [x86_64] dependencies:
script: - build:redox
- redoxer pkg relibc-tests-bins # only run integration test as without KVM unit tests is super slow
- export COOKBOOK_SOURCE_IDENT=$CI_COMMIT_SHA script: redoxer test --verbose -- --test '*' -- --nocapture
- mkdir -p target/${TARGET}/sysroot/{usr/lib/boot,root} target/${TARGET}/root
- redoxer env make BUILD=target/${TARGET}/sysroot/usr/lib/boot
- (cd target/${TARGET}/sysroot && mv home/user/relibc-tests/* root/)
- timeout -s KILL 9m redoxer exec --folder target/${TARGET}/sysroot/:/ make run
# It is fine if failing sometimes
allow_failure: true
variables:
TARGET: "x86_64-unknown-redox"
profiling-compile:
stage: other-features
allow_failure: true
script:
make check
variables:
ARCH: "x86_64"
KERNEL_CHECK_FEATURES: profiling
-4
View File
@@ -1,4 +0,0 @@
[submodule "redox-path"]
path = redox-path
url = https://gitlab.redox-os.org/redox-os/redox-path.git
branch = main
-2
View File
@@ -1,2 +0,0 @@
[editor]
auto-format = false
-13
View File
@@ -1,13 +0,0 @@
[[language]]
name = "rust"
[[language-server.rust-analyzer.config.cargo]]
extraEnv = ["RUST_TARGET_PATH=targets"]
# Select one of targets to make lsp work for your confguration
# Do not commit this change
# TODO: find a better way to do this
# target = "aarch64-unknown-kernel"
[[language-server.rust-analyzer.config.check]]
targets = ["x86_64-unknown-kernel", "i686-unknown-kernel", "aarch64-unknown-kernel"]
+21
View File
@@ -0,0 +1,21 @@
sudo: required
language: rust
rust:
- nightly
os:
- linux
- osx
dist: trusty
before_install:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
sudo apt-get install -qq pkg-config fuse libfuse-dev;
sudo modprobe fuse;
sudo chmod 666 /dev/fuse;
sudo chown root:$USER /etc/fuse.conf;
fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then
brew update;
brew install Caskroom/cask/osxfuse;
fi
notifications:
email: false
-79
View File
@@ -1,79 +0,0 @@
# Porting the core Redox kernel to arm AArch64: An outline
## Intro
This document is [my](https://github.com/raw-bin) attempt at:
* Capturing thinking on the work needed for a core Redox kernel port
* Sharing progress with the community as things evolve
* Creating a template that can be used for ports to other architectures
Core Redox kernel means everything needed to get to a non-graphical console-only multi-user shell.
Only the 64-bit execution state (AArch64) with the 64-bit instruction set architecture (A64) shall be supported for the moment. For more background/context read [this](https://developer.arm.com/products/architecture/a-profile/docs/den0024/latest/introduction).
This document is intended to be kept *live*. It will be updated to reflect the current state of work and any feedback received.
It is hard~futile to come up with a strict sequence of work for such ports but this document is a reasonable template to follow.
## Intended target platform
The primary focus is on [qemu's virt machine platform emulation for the AArch64 architecture](https://github.com/qemu/qemu/blob/master/hw/arm/virt.c#L127).
Targeting a virtual platform is a convenient way to bring up the mechanics of architectural support and makes the jump to silicon easier. The preferred boot chain for AArch64 (explained later) is well supported on this platform and boot-over-tftp from localhost makes the debug cycle very efficient.
Once the core kernel port is complete a similar follow on document will be created that is dedicated to silicon bring-up.
## Boot protocol elements
| Item | Notes |
|------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Linux kernel boot protocol for AArch64](https://www.kernel.org/doc/Documentation/arm64/booting.txt) | The linked document describes assumptions made from the bootloader which are field tested and worthwhile to have for Redox an AArch64. <br/> The intent is to consider most of the document except anything tied to the Linux kernel itself. |
| [Flattened Device Tree](https://elinux.org/Device_Tree_Reference) | FDT binary blobs supplied by the bootloader shall provide the Redox kernel with misc platform \{memory, interrupt, devicemem} maps. Qemu's virt machine platform synthetically creates an FDT blob at a specific address which is very handy. |
## Boot flow elements
The following table lists the boot flow in order.
| Item | Notes |
|-------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [ARM Trusted Firmware (TF-A)](https://github.com/ARM-software/arm-trusted-firmware) | TF-A is a de-facto standard reference firmware implementation and proven in the field. <br/> TF-A runs post power-on on Armv8-A implementations and eventually hands off to further stages of the boot flow.<br />For qemu's virt machine platform, it is essentially absent but I mean to rely on it heavily for silicon bring up hence mentioning it here. |
| [u-boot](https://www.denx.de/wiki/U-Boot) | u-boot will handle early console access, media access for fetching redox kernel images from non-volatile storage/misc disk subsystems/off the network. <br /> u-boot supports loading EFI applications. If EFI support to AArch64 Redox is added in the future that should essentially work out of the box. <br /> u-boot will load redox and FDT binary blobs into RAM and jump to the redox kernel. |
| Redox early-init stub | For AArch64, the redox kernel will contain an A64 assembly stub that will setup the MMU from scratch. This is akin to the [x86_64 redox bootloader](https://github.com/redox-os/bootloader/blob/master/x86_64/startup-x86_64.asm). <br /> This stub sets up identity maps for MMU initialization, maps the kernel image itself as well as the device memory for the UART console. At present this stub shall be a part of the kernel itself for simplicity. |
| Redox kstart entry | The early init stub hands off here. kstart will then re-init the MMU more comprehensively. |
## Supported devices
The following devices shall be supported. All necessary information specific to these devices will be provided to the redox kernel by the platform specific FDT binary blob.
| Device | Notes |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Generic Interrupt Controller v2](https://developer.arm.com/products/architecture/a-profile/docs/ihi0048/b/arm-generic-interrupt-controller-architecture-version-20-architecture-specification) | The GIC is an Arm-v8A architectural element and is supported by all architecturally compliant processor implementations. GICv2 is supported by qemu's virt machine emulation and most subsequent GIC implementations are backward compatible to GICv2. |
| [Generic Timer](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500d/BGBBIJCB.html) | The Generic Timer Architecture is an Arm-v8A architectural element and is implemented by all compliant processor implementations. It is supported by qemu. |
| [PrimeCell UART PL011](http://infocenter.arm.com/help/topic/com.arm.doc.ddi0183f/DDI0183.pdf) | The PL011 UART is supported by qemu and most ARM systems. |
## Intended development sequence and status
| Item | Description | Status | Notes |
|--------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|-------------------------------------------------------------------------------|
| Redox AArch64 toolchain | Create an usable redox AArch64 toolchain specification | Done | Using this JSON spec in isolated tests produces valid AArch64 soft float code |
| Stubbed kernel image | Stub out AArch64 kernel support using the existing x86_64 arch code as a template <br /> Modify redox kernel build glue and work iteratively to get a linkable (non-functional) image | Not done yet | |
| Boot flow | Create a self hosted u-boot -> redox kernel workflow <br /> Should obtain the stubbed image from a local TFTP server, load it into RAM and jump to it | Not done yet | |
| GDB Debug flow | Create a debug workflow centered around qemu's GDB stub <br /> This should allow connecting to qemu's GDB stub and debug u-boot/redox stub via a GDB client and single stepping through code | Not done yet | |
| Verify Redox entry | Verify that control reaches the redox kernel from u-boot | Not done yet | |
| AArch64 early init stub | Add support for raw asm code for early AArch64 init in the redox kernel <br /> Verify that this code is located appropriately in the link map and that control reaches this code from u-boot | Not done yet | |
| Basic DTB support | Integrate the [device_tree crate](https://mbr.github.io/device_tree-rs/device_tree/) <br /> Use the crate to access the qemu supplied DTB image and extract the memory map | Not done yet | |
| Basic UART support | Use the device_tree crate to get the UART address from the DTB image and set up the initial console <br /> This is a polling mode only setup | Not done yet | |
| Initial MMU support | Implement initial MMU support in the early init stub <br /> This forces the MMU into a clean state overriding any bootloader specific setup <br /> Create an identity map for MMU init <br /> Create a mapping for the kernel image <br /> Create a mapping for any devices needed at this stage (UART) | Not done yet | |
| kmain entry | Verify that kmain entry works post early MMU init | Not done yet | |
| Basic Redox MMU support | Get Redox to create a final set of mappings for everything <br /> Verify that this works as expected | Not done yet | |
| Basic libc support | Flesh out a basic set of libc calls as required for simple user-land apps | Not done yet | |
| userspace_init entry | Verify user-space entry and /sbin/init invocation | Not done yet | |
| Basic Interrupt controller support | Add a GIC driver <br /> Verify functionality | Not done yet | |
| Basic Timer support | Add a Generic Timer driver <br /> Verify functionality | Not done yet | |
| UART interrupt support | Add support for UART interrupts | Not done yet | |
| Task context switch support | Add context switching support <br /> Verify functionality | Not done yet | |
| Login shell | Iteratively add and verify multi-user login shell support | Not done yet | |
| Publish development branch on github | Work with the community to post work done after employer approval | Not done yet | |
| Break out the Bubbly | Drink copious quantities of alcohol to celebrate | Not done yet | |
| Silicon bring-up | Plan silicon bring-up | Not done yet | |
Generated
+575 -190
View File
@@ -3,34 +3,105 @@
version = 4 version = 4
[[package]] [[package]]
name = "ahash" name = "aes"
version = "0.8.12" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "cipher",
"version_check", "cpufeatures",
"zerocopy",
] ]
[[package]] [[package]]
name = "arrayvec" name = "aho-corasick"
version = "0.7.8" version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "bit_field" name = "anstream"
version = "0.10.3" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]] [[package]]
name = "bitfield" name = "anstyle"
version = "0.13.2" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "anstyle-parse"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]]
name = "argon2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73"
dependencies = [
"base64ct",
"blake2",
]
[[package]]
name = "assert_cmd"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aa3a22042e45de04255c7bf3626e239f450200fd0493c1e382263544b20aea6"
dependencies = [
"anstyle",
"bstr",
"libc",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
name = "base64ct"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
@@ -45,15 +116,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
[[package]] [[package]]
name = "cc" name = "blake2"
version = "1.2.66" version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [ dependencies = [
"find-msvc-tools", "digest",
"shlex",
] ]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bstr"
version = "1.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79"
dependencies = [
"memchr",
"regex-automata",
"serde_core",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.4" version = "1.0.4"
@@ -61,90 +157,253 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]] [[package]]
name = "equivalent" name = "cfg_aliases"
version = "1.0.2" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "fdt" name = "cipher"
version = "0.2.0-alpha1" version = "0.4.4"
source = "git+https://github.com/repnop/fdt.git?rev=2fb1409edd1877c714a0aa36b6a7c5351004be54#2fb1409edd1877c714a0aa36b6a7c5351004be54"
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [ dependencies = [
"ahash", "crypto-common",
"inout",
] ]
[[package]] [[package]]
name = "hashbrown" name = "colorchoice"
version = "0.17.1" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]] [[package]]
name = "indexmap" name = "cpufeatures"
version = "2.14.0" version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [ dependencies = [
"equivalent", "libc",
"hashbrown 0.17.1",
] ]
[[package]] [[package]]
name = "kernel" name = "crypto-common"
version = "0.5.12+rb0.3.0" version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [ dependencies = [
"arrayvec", "generic-array",
"bitfield", "typenum",
"bitflags 2.13.0", ]
"cc",
"fdt", [[package]]
"hashbrown 0.14.5", name = "defmt"
"linked_list_allocator", version = "1.1.1"
"object", source = "registry+https://github.com/rust-lang/crates.io-index"
"raw-cpuid", checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1"
"redox-path", dependencies = [
"redox_syscall", "bitflags 1.3.2",
"rmm", "defmt-macros",
"rustc-demangle", ]
"sbi-rt",
"slab", [[package]]
name = "defmt-macros"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8"
dependencies = [
"defmt-parser",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "defmt-parser"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
dependencies = [
"thiserror",
]
[[package]]
name = "difflib"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
name = "endian-num"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8f59926911ef34d1efb9ea1ee8ca78385df62ce700ccf2bcb149011bd226888"
[[package]]
name = "env_filter"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"jiff",
"log",
]
[[package]]
name = "fuser"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bb29a3ae32279fe3e79a958fe01899f5fb23eadccee919cf88e145b54ed9367"
dependencies = [
"libc",
"log",
"memchr",
"nix",
"page_size",
"smallvec", "smallvec",
"spin", "zerocopy",
"toml",
"x86",
] ]
[[package]] [[package]]
name = "linked_list_allocator" name = "generic-array"
version = "0.9.1" version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "549ce1740e46b291953c4340adcd74c59bcf4308f4cac050fd33ba91b7168f4a" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [ dependencies = [
"spinning_top", "typenum",
"version_check",
] ]
[[package]] [[package]]
name = "lock_api" name = "getrandom"
version = "0.4.14" version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [ dependencies = [
"scopeguard", "cfg-if",
"libc",
"wasi",
] ]
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi",
]
[[package]]
name = "humansize"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
dependencies = [
"libm",
]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"generic-array",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "jiff"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccfe6121cbe750cf81efa362d85c0bde7ea298ec43092d3a193baca59cdbd634"
dependencies = [
"defmt",
"jiff-static",
"log",
"portable-atomic",
"portable-atomic-util",
"serde_core",
]
[[package]]
name = "jiff-static"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e165e897f662d428f3cd3828a919dbe067c2d42bb1031eede74ef9d27ecdedd2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libm"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
[[package]]
name = "libredox"
version = "0.1.18+rb0.3.0"
dependencies = [
"bitflags 2.13.0",
"libc",
"plain",
"redox_syscall",
]
[[package]]
name = "log"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
[[package]]
name = "lz4_flex"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373f5eceeeab7925e0c1098212f2fbc4d416adec9d35051a6ab251e824c1854a"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.8.2" version = "2.8.2"
@@ -152,19 +411,92 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
[[package]] [[package]]
name = "object" name = "nix"
version = "0.37.3" version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [ dependencies = [
"memchr", "bitflags 2.13.0",
"cfg-if",
"cfg_aliases",
"libc",
] ]
[[package]] [[package]]
name = "once_cell" name = "numtoa"
version = "1.21.4" version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "page_size"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "parse-size"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "portable-atomic"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "portable-atomic-util"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
dependencies = [
"portable-atomic",
]
[[package]]
name = "predicates"
version = "3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe"
dependencies = [
"anstyle",
"difflib",
"predicates-core",
]
[[package]]
name = "predicates-core"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144"
[[package]]
name = "predicates-tree"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2"
dependencies = [
"predicates-core",
"termtree",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
@@ -185,19 +517,30 @@ dependencies = [
] ]
[[package]] [[package]]
name = "raw-cpuid" name = "r-efi"
version = "10.7.0" version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
dependencies = [
"bitflags 1.3.2", [[package]]
] name = "range-tree"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384c2842d4e069d5ccacf5fe1dca4ef8d07a5444329715f0fc3c61813502d4d1"
[[package]] [[package]]
name = "redox-path" name = "redox-path"
version = "0.2.0" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64072665120942deff5fd5425d6c1811b854f4939e7f1c01ce755f64432bbea7" checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
[[package]]
name = "redox-scheme"
version = "0.11.2+rb0.3.0"
dependencies = [
"libredox",
"redox_syscall",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
@@ -207,48 +550,69 @@ dependencies = [
] ]
[[package]] [[package]]
name = "rmm" name = "redoxfs"
version = "0.1.0" version = "0.9.1+rb0.3.0"
dependencies = [ dependencies = [
"aes",
"argon2",
"assert_cmd",
"base64ct",
"bitflags 2.13.0", "bitflags 2.13.0",
"endian-num",
"env_logger",
"fuser",
"getrandom 0.2.17",
"humansize",
"libc",
"libredox",
"log",
"lz4_flex",
"parse-size",
"range-tree",
"redox-path",
"redox-scheme",
"redox_syscall",
"seahash",
"termion",
"uuid",
"xts-mode",
] ]
[[package]] [[package]]
name = "rustc-demangle" name = "regex"
version = "0.1.27" version = "1.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
[[package]]
name = "sbi-rt"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fbaa69be1eedc61c426e6d489b2260482e928b465360576900d52d496a58bd0"
dependencies = [ dependencies = [
"sbi-spec", "aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
] ]
[[package]] [[package]]
name = "sbi-spec" name = "regex-automata"
version = "0.0.7" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e36312fb5ddc10d08ecdc65187402baba4ac34585cb9d1b78522ae2358d890" checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [ dependencies = [
"serde_core", "aho-corasick",
"memchr",
"regex-syntax",
] ]
[[package]]
name = "regex-syntax"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]]
name = "seahash"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]] [[package]]
name = "serde_core" name = "serde_core"
version = "1.0.228" version = "1.0.228"
@@ -269,27 +633,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
dependencies = [
"serde",
]
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.2" version = "1.15.2"
@@ -297,22 +640,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
[[package]] [[package]]
name = "spin" name = "subtle"
version = "0.9.8" version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
dependencies = [
"lock_api",
]
[[package]]
name = "spinning_top"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0"
dependencies = [
"lock_api",
]
[[package]] [[package]]
name = "syn" name = "syn"
@@ -326,45 +657,46 @@ dependencies = [
] ]
[[package]] [[package]]
name = "toml" name = "termion"
version = "0.8.23" version = "4.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" checksum = "f44138a9ae08f0f502f24104d82517ef4da7330c35acd638f1f29d3cd5475ecb"
dependencies = [ dependencies = [
"serde", "libc",
"serde_spanned", "numtoa",
"toml_datetime",
"toml_edit",
] ]
[[package]] [[package]]
name = "toml_datetime" name = "termtree"
version = "0.6.11" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [ dependencies = [
"serde", "thiserror-impl",
] ]
[[package]] [[package]]
name = "toml_edit" name = "thiserror-impl"
version = "0.22.27" version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [ dependencies = [
"indexmap", "proc-macro2",
"serde", "quote",
"serde_spanned", "syn",
"toml_datetime",
"toml_write",
"winnow",
] ]
[[package]] [[package]]
name = "toml_write" name = "typenum"
version = "0.1.2" version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
@@ -372,6 +704,21 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53"
dependencies = [
"getrandom 0.4.3",
]
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
@@ -379,23 +726,65 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "winnow" name = "wait-timeout"
version = "0.7.15" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
dependencies = [ dependencies = [
"memchr", "libc",
] ]
[[package]] [[package]]
name = "x86" name = "wasi"
version = "0.47.0" version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55b5be8cc34d017d8aabec95bc45a43d0f20e8b2a31a453cabc804fe996f8dca" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [ dependencies = [
"bit_field", "winapi-i686-pc-windows-gnu",
"bitflags 1.3.2", "winapi-x86_64-pc-windows-gnu",
"raw-cpuid", ]
[[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-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "xts-mode"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cbddb7545ca0b9ffa7bdc653e8743303e1712687a6918ced25f2cdbed42520"
dependencies = [
"byteorder",
"cipher",
] ]
[[package]] [[package]]
@@ -417,7 +806,3 @@ dependencies = [
"quote", "quote",
"syn", "syn",
] ]
[[patch.unused]]
name = "libredox"
version = "0.1.18+rb0.3.0"
+85 -121
View File
@@ -1,137 +1,101 @@
[workspace]
resolver = "3"
members = [".", "rmm"]
[package] [package]
name = "kernel" name = "redoxfs"
version = "0.5.12+rb0.3.0" description = "The Redox Filesystem"
repository = "https://gitlab.redox-os.org/redox-os/redoxfs"
version = "0.9.1+rb0.3.0"
license-file = "LICENSE"
readme = "README.md"
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"] authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
build = "build.rs" edition = "2021"
edition = "2024"
[build-dependencies] [lib]
cc = "1.0" name = "redoxfs"
toml = "0.8" path = "src/lib.rs"
[[bin]]
name = "redoxfs"
path = "src/bin/mount.rs"
doc = false
required-features = ["std"]
[[bin]]
name = "redoxfs-ar"
path = "src/bin/ar.rs"
doc = false
required-features = ["std"]
[[bin]]
name = "redoxfs-clone"
path = "src/bin/clone.rs"
doc = false
required-features = ["std"]
[[bin]]
name = "redoxfs-mkfs"
path = "src/bin/mkfs.rs"
doc = false
required-features = ["std"]
[[bin]]
name = "redoxfs-resize"
path = "src/bin/resize.rs"
doc = false
required-features = ["std"]
[dependencies] [dependencies]
arrayvec = { version = "0.7.4", default-features = false } aes = { version = "0.8", default-features = false }
bitfield = "0.13.2" argon2 = { version = "0.4", default-features = false, features = ["alloc"] }
base64ct = { version = "1", default-features = false }
bitflags = "2" bitflags = "2"
fdt = { git = "https://github.com/repnop/fdt.git", rev = "2fb1409edd1877c714a0aa36b6a7c5351004be54" } endian-num = "0.1"
hashbrown = { version = "0.14.3", default-features = false, features = ["ahash", "inline-more"] } env_logger = { version = "0.11", optional = true }
linked_list_allocator = "0.9.0" getrandom = { version = "0.2.5", optional = true }
redox-path = "0.2.0" humansize = { version = "2", optional = true }
redox_syscall = { path = "../syscall", default-features = false } libc = "0.2"
rmm = { path = "rmm", default-features = false } log = { version = "0.4.14", default-features = false, optional = true }
slab = { version = "0.4", default-features = false } lz4_flex = { version = "0.11", default-features = false, features = ["checked-decode"] }
smallvec = { version = "1.15.1", default-features = false } parse-size = { version = "1", optional = true }
spin = { version = "0.9.8" } range-tree = { version = "0.1", optional = true }
redox_syscall = { path = "../syscall" }
[dependencies.object] seahash = { version = "4.1.0", default-features = false }
version = "0.37.1" termion = { version = "4", optional = true }
default-features = false uuid = { version = "1.4", default-features = false }
features = ["read_core", "elf"] xts-mode = { version = "0.5", default-features = false }
[dependencies.rustc-demangle]
version = "0.1.16"
default-features = false
[lints.clippy]
# Overflows are very, very bad in kernel code as it may provide an attack vector for
# userspace applications, and it is only checked in debug builds
# TODO: address occurrences and then deny
arithmetic_side_effects = "warn"
cast_ptr_alignment = "warn" # TODO: address occurrences and then deny
identity_op = "allow" # Used to allow stuff like 1 << 0 and 1 * 1024 * 1024
if_same_then_else = "allow" # Useful for adding comments about different branches
# Indexing a slice can cause panics and that is something we always want to avoid
# in kernel code. Use .get and return an error instead
# TODO: address occurrences and then deny
indexing_slicing = "warn"
many_single_char_names = "allow" # Useful in the syscall function
module_inception = "allow" # Used for context::context
# Not implementing default is sometimes useful in the case something has significant cost
# to allocate. If you implement default, it can be allocated without evidence using the
# ..Default::default() syntax. Not fun in kernel space
new_without_default = "allow"
not_unsafe_ptr_arg_deref = "deny"
or_fun_call = "allow" # Used to make it nicer to return errors, for example, .ok_or(Error::new(ESRCH))
precedence = "deny"
ptr_cast_constness = "deny"
too_many_arguments = "allow" # This is needed in some cases, like for syscall
# Avoid panicking in the kernel without information about the panic. Use expect
# TODO: address occurrences and then deny
unwrap_used = "warn"
[lints.rust]
static_mut_refs = "warn" # FIXME deny once all occurrences are fixed
# This is usually a serious issue - a missing import of a define where it is interpreted
# as a catch-all variable in a match, for example
unreachable_patterns = "deny"
unused_must_use = "deny" # Ensure that all must_use results are used
[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
raw-cpuid = "10.2.0"
x86 = { version = "0.47.0", default-features = false }
[target.'cfg(any(target_arch = "riscv64", target_arch = "riscv32"))'.dependencies]
sbi-rt = "0.0.3"
[features] [features]
default = [ default = ["std", "log", "fuse"]
"acpi", fuse = [
#"debugger", "fuser",
"multi_core", "std",
"serial_debug", ]
"self_modifying", std = [
"x86_kvm_pv", "env_logger",
#"busy_panic", "getrandom",
#"drop_panic", "humansize",
#"syscall_debug" "libredox",
"parse-size",
"range-tree",
"termion",
"uuid/v4",
"redox_syscall/std",
"redox-scheme",
] ]
# Activates some limited code-overwriting optimizations, based on CPU features. [target.'cfg(not(target_os = "redox"))'.dependencies]
self_modifying = [] fuser = { version = "0.16", optional = true }
acpi = [] [target.'cfg(target_os = "redox")'.dependencies]
lpss_debug = [] libredox = { path = "../libredox", optional = true }
multi_core = ["acpi"] redox-path = "0.3.0"
profiling = [] redox-scheme = { path = "../redox-scheme", optional = true }
#TODO: remove when threading issues are fixed
pti = []
drop_panic = []
busy_panic = []
qemu_debug = []
serial_debug = []
system76_ec_debug = []
x86_kvm_pv = []
debugger = ["syscall_debug"] [lints.rust]
syscall_debug = [] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
sys_fdstat = [] [dev-dependencies]
assert_cmd = "2.0.17"
[profile.dev]
# Avoids having to define the eh_personality lang item and reduces kernel size
panic = "abort"
[profile.release]
# Avoids having to define the eh_personality lang item and reduces kernel size
panic = "abort"
#lto = true
debug = "full"
# Red Bear OS Phase J: see local/sources/base/Cargo.toml for
# the rationale. Both the kernel and the base workspace need
# the libredox override so that the libredox::error::Error
# type is the same compile-time type as syscall::Error. With
# the local libredox fork at local/sources/libredox/ using
# the local syscall fork at local/sources/syscall/, the
# libredox::error::Error (re-exported from the local syscall)
# and syscall::Error (also the local syscall) are now the
# same type, so `?` conversions in scheme-utils / daemon
# compile cleanly.
[patch.crates-io] [patch.crates-io]
# Local fork dependency rule: every crate with a local fork MUST resolve through it.
libredox = { path = "../libredox" }
redox_syscall = { path = "../syscall" } redox_syscall = { path = "../syscall" }
libredox = { path = "../libredox" }
redox-scheme = { path = "../redox-scheme" }
+2 -2
View File
@@ -1,6 +1,6 @@
MIT License The MIT License (MIT)
Copyright (c) 2017 Jeremy Soller Copyright (c) 2016 Jeremy Soller
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+29 -58
View File
@@ -1,66 +1,37 @@
.PHONY: all check UNAME := $(shell uname)
SOURCE:=$(dir $(realpath $(lastword $(MAKEFILE_LIST)))) ifeq ($(UNAME),Darwin)
BUILD?=$(CURDIR) FUMOUNT=umount
export RUST_TARGET_PATH=$(SOURCE)/targets else ifeq ($(UNAME),FreeBSD)
FUMOUNT=sudo umount
ifeq ($(TARGET),)
ARCH?=$(shell uname -m)
else else
ARCH?=$(shell echo "$(TARGET)" | cut -d - -f1) # Detect which version of the fusermount binary is available.
ifneq (, $(shell which fusermount3))
FUMOUNT=fusermount3 -u
else
FUMOUNT=fusermount -u
endif
endif endif
ifeq ($(ARCH),riscv64gc) image.bin:
override ARCH:=riscv64 cargo build --release --bin redoxfs-mkfs
GNU_TARGET=riscv64-unknown-redox dd if=/dev/zero of=image.bin bs=1048576 count=1024
else ifeq ($(ARCH),i686) target/release/redoxfs-mkfs image.bin
override ARCH:=i586
GNU_TARGET=i686-unknown-redox
else
GNU_TARGET=$(ARCH)-unknown-redox
endif
mount: image.bin FORCE
mkdir -p image
cargo build --release --bin redoxfs
target/release/redoxfs image.bin image
all: $(BUILD)/kernel $(BUILD)/kernel.sym unmount: FORCE
sync
-${FUMOUNT} image
rm -rf image
LD_SCRIPT=$(SOURCE)/linkers/$(ARCH).ld clean: FORCE
LOCKFILE=$(SOURCE)/Cargo.lock sync
MANIFEST=$(SOURCE)/Cargo.toml -${FUMOUNT} image
TARGET_SPEC=$(RUST_TARGET_PATH)/$(ARCH)-unknown-kernel.json rm -rf image image.bin
cargo clean
KERNEL_CARGO_FEATURES?= FORCE:
$(BUILD)/kernel.all: $(LD_SCRIPT) $(LOCKFILE) $(MANIFEST) $(TARGET_SPEC) $(shell find $(SOURCE) -name "*.rs" -type f)
cargo rustc \
--bin kernel \
--manifest-path "$(MANIFEST)" \
--target "$(TARGET_SPEC)" \
--release \
-Z build-std=core,alloc -Zbuild-std-features=compiler-builtins-mem -Z json-target-spec \
--features=$(KERNEL_CARGO_FEATURES) \
-- \
-C link-arg=-T -Clink-arg="$(LD_SCRIPT)" \
-C link-arg=-z -Clink-arg=max-page-size=0x1000 \
--emit link="$(BUILD)/kernel.all"
$(BUILD)/kernel.sym: $(BUILD)/kernel.all
$(GNU_TARGET)-objcopy \
--only-keep-debug \
"$(BUILD)/kernel.all" \
"$(BUILD)/kernel.sym"
$(BUILD)/kernel: $(BUILD)/kernel.all
$(GNU_TARGET)-objcopy \
--strip-debug \
"$(BUILD)/kernel.all" \
"$(BUILD)/kernel"
KERNEL_CHECK_FEATURES?=
check:
cargo check \
--bin kernel \
--manifest-path "$(MANIFEST)" \
--target "$(TARGET_SPEC)" \
-Z build-std=core,alloc -Zbuild-std-features=compiler-builtins-mem -Z json-target-spec \
--features=$(KERNEL_CHECK_FEATURES)
+50 -78
View File
@@ -1,81 +1,53 @@
# Kernel # RedoxFS
Redox OS Microkernel This is the default filesystem of Redox OS inspired by [ZFS](https://docs.freebsd.org/en/books/handbook/zfs/) and adapted to a microkernel architecture.
(It's a replacement for [TFS](https://gitlab.redox-os.org/redox-os/tfs))
Current features:
- Compatible with Redox and Linux (FUSE)
- Copy-on-write
- Data/metadata checksums
- Transparent encryption
- Standard Unix file attributes
- File/directory size limit up to 193TiB (212TB)
- File/directory quantity limit up to 4 billion per 193TiB (2^32 - 1 = 4294967295)
- MIT licensed
- Disk encryption fully supported by the Redox bootloader, letting it load the kernel off an encrypted partition.
Being MIT licensed, RedoxFS can be bundled on GPL-licensed operating systems (Linux, for example).
### Install RedoxFS
```sh
cargo install redoxfs
```
You can also build RedoxFS from this repository.
### Configure your storage device to allow rootless usage
If you are on Linux you need root permission to acess block devices (storage), but it's recommended to run RedoxFS as rootless.
To do that you need to configure your storage device permission to your user with the following command:
```sh
sudo setfacl -m u:your-username:rw /path/to/disk
```
### Create, mount and customize your RedoxFS partition
See [the instructions in the book](https://doc.redox-os.org/book/redoxfs.html) for RedoxFS tooling usage.
Currently RedoxFS tooling are:
- `redoxfs` mount a RedoxFS disk
- `redoxfs-ar` write files to a RedoxFS disk
- `redoxfs-clone` clone a RedoxFS disk
- `redoxfs-mkfs` create an empty RedoxFS disk
- `redoxfs-resize` resize a RedoxFS disk
[![docs](https://img.shields.io/badge/docs-master-blue.svg)](https://docs.rs/redox_syscall/latest/syscall/)
[![SLOCs counter](https://tokei.rs/b1/github/redox-os/kernel?category=code)](https://github.com/XAMPPRocky/tokei)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![crates.io](http://meritbadge.herokuapp.com/redoxfs)](https://crates.io/crates/redoxfs)
## Requirements [![docs.rs](https://docs.rs/redoxfs/badge.svg)](https://docs.rs/redoxfs)
* [`nasm`](https://nasm.us/) needs to be available on the PATH at build time.
## Building The Documentation
Use this command:
```sh
cargo doc --open --target x86_64-unknown-none
```
## Debugging
### QEMU
Running [QEMU](https://www.qemu.org) with the `-s` flag will set up QEMU to listen on port `1234` for a GDB client to connect to it. To debug the redox kernel run.
```sh
make qemu gdb=yes
```
This will start a virtual machine with and listen on port `1234` for a GDB or LLDB client.
### GDB
If you are going to use [GDB](https://www.gnu.org/software/gdb/), run these commands to load debug symbols and connect to your running kernel:
```
(gdb) symbol-file build/kernel.sym
(gdb) target remote localhost:1234
```
### LLDB
If you are going to use [LLDB](https://lldb.llvm.org/), run these commands to start debugging:
```
(lldb) target create -s build/kernel.sym build/kernel
(lldb) gdb-remote localhost:1234
```
After connecting to your kernel you can set some interesting breakpoints and `continue`
the process. See your debuggers man page for more information on useful commands to run.
## Notes
- Always use `foo.get(n)` instead of `foo[n]` and try to cover for the possibility of `Option::None`. Doing the regular way may work fine for applications, but never in the kernel. No possible panics should ever exist in kernel space, because then the whole OS would just stop working.
- If you receive a kernel panic in QEMU, use `pkill qemu-system` to kill the frozen QEMU process.
## How To Contribute
To learn how to contribute to this system component you need to read the following document:
- [CONTRIBUTING.md](https://gitlab.redox-os.org/redox-os/redox/-/blob/master/CONTRIBUTING.md)
## Development
To learn how to do development with this system component inside the Redox build system you need to read the [Build System](https://doc.redox-os.org/book/build-system-reference.html) and [Coding and Building](https://doc.redox-os.org/book/coding-and-building.html) pages.
### How To Build
To build this system component you need to download the Redox build system, you can learn how to do it on the [Building Redox](https://doc.redox-os.org/book/podman-build.html) page.
This is necessary because they only work with cross-compilation to a Redox virtual machine, but you can do some testing from Linux.
## Funding - _Unix-style Signals and Process Management_
This project is funded through [NGI Zero Core](https://nlnet.nl/core), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/RedoxOS-Signals).
[<img src="https://nlnet.nl/logo/banner.png" alt="NLnet foundation logo" width="20%" />](https://nlnet.nl)
[<img src="https://nlnet.nl/image/logos/NGI0_tag.svg" alt="NGI Zero Logo" width="20%" />](https://nlnet.nl/core)
-100
View File
@@ -1,100 +0,0 @@
#![allow(clippy::unwrap_used)] // the build script can panic
use std::{env, path::Path, process::Command};
use toml::Table;
fn parse_kconfig(arch: &str) -> Option<()> {
println!("cargo:rerun-if-changed=config.toml");
assert!(Path::new("config.toml.example").try_exists().unwrap());
if !Path::new("config.toml").try_exists().unwrap() {
std::fs::copy("config.toml.example", "config.toml").unwrap();
}
let config_str = std::fs::read_to_string("config.toml").unwrap();
let root: Table = toml::from_str(&config_str).unwrap();
let altfeatures = root
.get("arch")?
.as_table()
.unwrap()
.get(arch)?
.as_table()
.unwrap()
.get("features")?
.as_table()
.unwrap();
#[expect(clippy::format_collect)] // TODO: remove once version is bumped
let features_list = altfeatures
.keys()
.map(|feat| format!(", {feat:?}"))
.collect::<String>();
println!("cargo::rustc-check-cfg=cfg(cpu_feature_always, values(\"\"{features_list}))");
println!("cargo::rustc-check-cfg=cfg(cpu_feature_auto, values(\"\"{features_list}))");
println!("cargo::rustc-check-cfg=cfg(cpu_feature_never, values(\"\"{features_list}))");
let self_modifying = env::var("CARGO_FEATURE_SELF_MODIFYING").is_ok();
for (name, value) in altfeatures {
let mut choice = value.as_str().unwrap();
assert!(matches!(choice, "always" | "never" | "auto"));
if !self_modifying && choice == "auto" {
choice = "never";
}
println!("cargo:rustc-cfg=cpu_feature_{choice}=\"{name}\"");
}
Some(())
}
fn main() {
println!("cargo::rustc-env=TARGET={}", env::var("TARGET").unwrap());
println!("cargo::rustc-check-cfg=cfg(dtb)");
let out_dir = env::var("OUT_DIR").unwrap();
let arch_str = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
match &*arch_str {
"aarch64" => {
println!("cargo::rustc-cfg=dtb");
}
"x86" => {
println!("cargo::rerun-if-changed=src/asm/x86/trampoline.asm");
let status = Command::new("nasm")
.arg("-f")
.arg("bin")
.arg("-o")
.arg(format!("{}/trampoline", out_dir))
.arg("src/asm/x86/trampoline.asm")
.status()
.expect("failed to run nasm");
if !status.success() {
panic!("nasm failed with exit status {}", status);
}
}
"x86_64" => {
println!("cargo::rerun-if-changed=src/asm/x86_64/trampoline.asm");
let status = Command::new("nasm")
.arg("-f")
.arg("bin")
.arg("-o")
.arg(format!("{}/trampoline", out_dir))
.arg("src/asm/x86_64/trampoline.asm")
.status()
.expect("failed to run nasm");
if !status.success() {
panic!("nasm failed with exit status {}", status);
}
}
"riscv64" => {
println!("cargo::rustc-cfg=dtb");
}
_ => (),
}
let _ = parse_kconfig(&arch_str);
}
-7
View File
@@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -e
export RUST_TARGET_PATH="${PWD}/targets"
export RUSTFLAGS="-C debuginfo=2"
cargo clippy --lib --release --target x86_64-unknown-none "$@"
-7
View File
@@ -1,7 +0,0 @@
[arch.x86_64.features]
smap = "auto"
fsgsbase = "auto"
xsave = "auto"
xsaveopt = "auto"
# vim: ft=toml
+4
View File
@@ -0,0 +1,4 @@
target
corpus
artifacts
coverage
+858
View File
@@ -0,0 +1,858 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aes"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "anstream"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [
"anstyle",
"once_cell",
"windows-sys 0.59.0",
]
[[package]]
name = "anyhow"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
dependencies = [
"derive_arbitrary",
]
[[package]]
name = "argon2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73"
dependencies = [
"base64ct",
"blake2",
]
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
dependencies = [
"jobserver",
"libc",
"once_cell",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
name = "colorchoice"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "cpufeatures"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "derive_arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
name = "endian-num"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad847bb2094f110bbdd6fa564894ca4556fd978958e93985420d680d3cb6d14"
[[package]]
name = "env_filter"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"jiff",
"log",
]
[[package]]
name = "errno"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "fastrand"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "fuser"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bb29a3ae32279fe3e79a958fe01899f5fb23eadccee919cf88e145b54ed9367"
dependencies = [
"libc",
"log",
"memchr",
"nix",
"page_size",
"smallvec",
"zerocopy",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "humansize"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
dependencies = [
"libm",
]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"generic-array",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "jiff"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0"
dependencies = [
"jiff-static",
"log",
"portable-atomic",
"portable-atomic-util",
"serde",
]
[[package]]
name = "jiff-static"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "jobserver"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
dependencies = [
"libc",
]
[[package]]
name = "libc"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libfuzzer-sys"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
dependencies = [
"arbitrary",
"cc",
"once_cell",
]
[[package]]
name = "libm"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "libredox"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
dependencies = [
"bitflags",
"libc",
"redox_syscall 0.7.0",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "lz4_flex"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "nix"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "numtoa"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "page_size"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "parse-size"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b"
[[package]]
name = "portable-atomic"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
[[package]]
name = "portable-atomic-util"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
dependencies = [
"portable-atomic",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "range-tree"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384c2842d4e069d5ccacf5fe1dca4ef8d07a5444329715f0fc3c61813502d4d1"
[[package]]
name = "redox-path"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
[[package]]
name = "redox-scheme"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cea2668a5932f878a4298a1d7f8950249bbbb77120fb263da252c589152f5ea"
dependencies = [
"libredox",
"redox_syscall 0.6.0",
]
[[package]]
name = "redox_syscall"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_syscall"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_termios"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
[[package]]
name = "redoxfs"
version = "0.8.4"
dependencies = [
"aes",
"argon2",
"base64ct",
"bitflags",
"endian-num",
"env_logger",
"fuser",
"getrandom",
"humansize",
"libc",
"libredox",
"log",
"lz4_flex",
"parse-size",
"range-tree",
"redox-path",
"redox-scheme",
"redox_syscall 0.6.0",
"seahash",
"termion",
"uuid",
"xts-mode",
]
[[package]]
name = "redoxfs-fuzz"
version = "0.0.0"
dependencies = [
"anyhow",
"arbitrary",
"fuser",
"libfuzzer-sys",
"nix",
"redoxfs",
"tempfile",
]
[[package]]
name = "regex"
version = "1.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "seahash"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
dependencies = [
"cfg-if",
"fastrand",
"rustix",
"windows-sys 0.52.0",
]
[[package]]
name = "termion"
version = "4.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3669a69de26799d6321a5aa713f55f7e2cd37bd47be044b50f2acafc42c122bb"
dependencies = [
"libc",
"libredox",
"numtoa",
"redox_termios",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
dependencies = [
"getrandom",
]
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[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-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "xts-mode"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cbddb7545ca0b9ffa7bdc653e8743303e1712687a6918ced25f2cdbed42520"
dependencies = [
"byteorder",
"cipher",
]
[[package]]
name = "zerocopy"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
+30
View File
@@ -0,0 +1,30 @@
[package]
name = "redoxfs-fuzz"
version = "0.0.0"
publish = false
edition = "2021"
[features]
default = []
log = []
[package.metadata]
cargo-fuzz = true
[dependencies]
anyhow = "1.0.86"
arbitrary = { version = "1.3.2", features = ["derive"] }
fuser = { version = "0.16" }
libfuzzer-sys = "0.4"
nix = { version = "0.29.0", features = ["fs"] }
tempfile = "3.10.1"
[dependencies.redoxfs]
path = ".."
[[bin]]
name = "fuse_fuzz_target"
path = "fuzz_targets/fuse_fuzz_target.rs"
test = false
doc = false
bench = false
+338
View File
@@ -0,0 +1,338 @@
//! Fuzzer that exercises random file system operations against a FUSE-mounted redoxfs.
#![no_main]
use anyhow::{ensure, Result};
use fuser;
use libfuzzer_sys::{arbitrary::Arbitrary, fuzz_target, Corpus};
use nix::sys::statvfs::statvfs;
use std::{
fs::{self, File, FileTimes, OpenOptions},
io::{Read, Seek, SeekFrom, Write},
os::unix::fs::{self as unix_fs, PermissionsExt},
path::{Path, PathBuf},
thread,
time::{Duration, SystemTime, UNIX_EPOCH},
};
use tempfile;
use redoxfs::{mount::fuse::Fuse, DiskSparse, FileSystem};
/// Maximum size for files and buffers. Chosen arbitrarily with fuzzing performance in mind.
const MAX_SIZE: u64 = 10_000_000;
/// Limit on the number of remounts in a single test case. Chosen arbitrarily with fuzzing
/// performance in mind: remounts are costly.
const MAX_MOUNT_SEQUENCES: usize = 3;
/// An operation to be performed by the fuzzer.
#[derive(Arbitrary, Clone, Debug)]
enum Operation {
Chown {
path: PathBuf,
uid: Option<u32>,
gid: Option<u32>,
},
CreateDir {
path: PathBuf,
},
HardLink {
original: PathBuf,
link: PathBuf,
},
Metadata {
path: PathBuf,
},
Read {
path: PathBuf,
},
ReadDir {
path: PathBuf,
},
ReadLink {
path: PathBuf,
},
RemoveDir {
path: PathBuf,
},
RemoveFile {
path: PathBuf,
},
Rename {
from: PathBuf,
to: PathBuf,
},
SeekRead {
path: PathBuf,
seek_pos: u64,
buf_size: usize,
},
SeekWrite {
path: PathBuf,
seek_pos: u64,
buf_size: usize,
},
SetLen {
path: PathBuf,
size: u64,
},
SetPermissions {
path: PathBuf,
readonly: Option<bool>,
mode: Option<u32>,
},
SetTimes {
path: PathBuf,
accessed_since_epoch: Option<Duration>,
modified_since_epoch: Option<Duration>,
},
Statvfs {},
SymLink {
original: PathBuf,
link: PathBuf,
},
Write {
path: PathBuf,
buf_size: usize,
},
}
/// Parameters for mounting the file system and operations to be performed afterwards.
#[derive(Arbitrary, Clone, Debug)]
struct MountSequence {
cleanup: bool,
operations: Vec<Operation>,
}
/// The whole input to a single fuzzer invocation.
#[derive(Arbitrary, Clone, Debug)]
struct TestCase {
disk_size: u64,
reserved_size: u64,
mount_sequences: Vec<MountSequence>,
}
/// Creates the disk for backing the Redoxfs.
fn create_disk(temp_path: &Path, disk_size: u64) -> DiskSparse {
let disk_path = temp_path.join("disk.img");
DiskSparse::create(disk_path, disk_size).unwrap()
}
/// Creates an empty Redoxfs.
fn create_redoxfs(disk: DiskSparse, reserved_size: u64) -> bool {
let password = None;
let reserved = vec![0; reserved_size as usize];
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
FileSystem::create_reserved(
disk,
password,
&reserved,
ctime.as_secs(),
ctime.subsec_nanos(),
)
.is_ok()
}
/// Mounts an existing Redoxfs, runs the callback and performs the unmount.
fn with_redoxfs_mount<F>(temp_path: &Path, disk: DiskSparse, cleanup: bool, callback: F)
where
F: FnOnce(&Path) + Send + 'static,
{
let password = None;
let block = None;
let mut fs = FileSystem::open(disk, password, block, cleanup).unwrap();
let mount_path = temp_path.join("mount");
fs::create_dir_all(&mount_path).unwrap();
let mut session = fuser::Session::new(Fuse { fs: &mut fs }, &mount_path, &[]).unwrap();
let mut unmounter = session.unmount_callable();
let join_handle = thread::spawn(move || {
callback(&mount_path);
unmounter.unmount().unwrap();
});
session.run().unwrap();
join_handle.join().unwrap();
}
fn get_path_within_fs(fs_path: &Path, path_to_add: &Path) -> Result<PathBuf> {
ensure!(path_to_add.is_relative());
ensure!(path_to_add
.components()
.all(|c| c != std::path::Component::ParentDir));
Ok(fs_path.join(path_to_add))
}
fn do_operation(fs_path: &Path, op: &Operation) -> Result<()> {
match op {
Operation::Chown { path, uid, gid } => {
let path = get_path_within_fs(fs_path, path)?;
unix_fs::chown(path, *uid, *gid)?;
}
Operation::CreateDir { path } => {
let path = get_path_within_fs(fs_path, path)?;
fs::create_dir(path)?;
}
Operation::HardLink { original, link } => {
let original = get_path_within_fs(fs_path, original)?;
let link = get_path_within_fs(fs_path, link)?;
fs::hard_link(original, link)?;
}
Operation::Metadata { path } => {
let path = get_path_within_fs(fs_path, path)?;
fs::metadata(path)?;
}
Operation::Read { path } => {
let path = get_path_within_fs(fs_path, path)?;
fs::read(path)?;
}
Operation::ReadDir { path } => {
let path = get_path_within_fs(fs_path, path)?;
let _ = fs::read_dir(path)?.count();
}
Operation::ReadLink { path } => {
let path = get_path_within_fs(fs_path, path)?;
fs::read_link(path)?;
}
Operation::RemoveDir { path } => {
let path = get_path_within_fs(fs_path, path)?;
fs::remove_dir(path)?;
}
Operation::RemoveFile { path } => {
let path = get_path_within_fs(fs_path, path)?;
fs::remove_file(path)?;
}
Operation::Rename { from, to } => {
let from = get_path_within_fs(fs_path, from)?;
let to = get_path_within_fs(fs_path, to)?;
fs::rename(from, to)?;
}
Operation::SeekRead {
path,
seek_pos,
buf_size,
} => {
ensure!(*buf_size as u64 <= MAX_SIZE);
let path = get_path_within_fs(fs_path, path)?;
let mut file = File::open(path)?;
file.seek(SeekFrom::Start(*seek_pos))?;
let mut buf = vec![0; *buf_size];
file.read(&mut buf)?;
}
Operation::SeekWrite {
path,
seek_pos,
buf_size,
} => {
ensure!(*seek_pos <= MAX_SIZE);
ensure!(*buf_size as u64 <= MAX_SIZE);
let path = get_path_within_fs(fs_path, path)?;
let mut file = OpenOptions::new().write(true).open(path)?;
file.seek(SeekFrom::Start(*seek_pos))?;
let buf = vec![0; *buf_size];
file.write(&buf)?;
}
Operation::SetLen { path, size } => {
let path = get_path_within_fs(fs_path, path)?;
let file = OpenOptions::new().write(true).open(path)?;
file.set_len(*size)?;
}
Operation::SetPermissions {
path,
readonly,
mode,
} => {
let path = get_path_within_fs(fs_path, path)?;
let metadata = fs::metadata(&path)?;
let mut perms = metadata.permissions();
if let Some(readonly) = readonly {
perms.set_readonly(*readonly);
}
if let Some(mode) = mode {
perms.set_mode(*mode);
}
fs::set_permissions(path, perms)?;
}
Operation::SetTimes {
path,
accessed_since_epoch,
modified_since_epoch,
} => {
let path = get_path_within_fs(fs_path, path)?;
let file = File::options().write(true).open(path)?;
let mut times = FileTimes::new();
if let Some(accessed_since_epoch) = accessed_since_epoch {
if let Some(accessed) = UNIX_EPOCH.checked_add(*accessed_since_epoch) {
times = times.set_accessed(accessed);
}
}
if let Some(modified_since_epoch) = modified_since_epoch {
if let Some(modified) = UNIX_EPOCH.checked_add(*modified_since_epoch) {
times = times.set_modified(modified);
}
}
file.set_times(times)?;
}
Operation::Statvfs {} => {
statvfs(fs_path)?;
}
Operation::SymLink { original, link } => {
let original = get_path_within_fs(fs_path, original)?;
let link = get_path_within_fs(fs_path, link)?;
unix_fs::symlink(original, link)?;
}
Operation::Write { path, buf_size } => {
ensure!(*buf_size as u64 <= MAX_SIZE);
let path = get_path_within_fs(fs_path, path)?;
let buf = vec![0; *buf_size];
fs::write(path, &buf)?;
}
}
Ok(())
}
fuzz_target!(|test_case: TestCase| -> Corpus {
if test_case.disk_size > MAX_SIZE
|| test_case.reserved_size > MAX_SIZE
|| test_case.mount_sequences.len() > MAX_MOUNT_SEQUENCES
{
return Corpus::Reject;
}
let temp_dir = tempfile::Builder::new()
.prefix("fuse_fuzz_target")
.tempdir()
.unwrap();
#[cfg(feature = "log")]
eprintln!("create fs");
let disk = create_disk(temp_dir.path(), test_case.disk_size);
if !create_redoxfs(disk, test_case.reserved_size) {
// File system creation failed (e.g., due to insufficient space) so we bail out, still
// exercising this code path is useful.
return Corpus::Keep;
}
for mount_seq in test_case.mount_sequences.iter() {
#[cfg(feature = "log")]
eprintln!("mount fs: path {:?}, size{}", temp_dir.path(), test_case.disk_size);
let disk = create_disk(temp_dir.path(), test_case.disk_size);
let operations = mount_seq.operations.clone();
with_redoxfs_mount(temp_dir.path(), disk, mount_seq.cleanup, move |fs_path| {
for operation in operations.iter() {
#[cfg(feature = "log")]
eprintln!("do operation {operation:?}");
let _result = do_operation(fs_path, operation);
#[cfg(feature = "log")]
eprintln!("operation result {:?}", _result.err());
}
});
#[cfg(feature = "log")]
eprintln!("unmounted fs");
}
Corpus::Keep
});
-55
View File
@@ -1,55 +0,0 @@
ENTRY(kstart)
OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
KERNEL_OFFSET = 0xFFFFFF0000000000;
SECTIONS {
. = KERNEL_OFFSET;
. += SIZEOF_HEADERS;
/* Force the zero page to be part of a segment by creating a
* dummy section in the zero page.
* Limine will map the segment with the lowest vaddr value at
* 0xFFFFFFFF80000000 even if the segment has a higher vaddr.
* As such without the zero page being part of a segment, the
* kernel would be loaded at an offset from the expected
* location. As the redox kernel is not currently relocatable,
* this would result in a crash. A similar issue likely exists
* with multiboot/multiboot2 and the paddr of the segment.
*/
.dummy ALIGN(8) : AT(ADDR(.dummy) - KERNEL_OFFSET) {}
. = ALIGN(4096);
.text : AT(ADDR(.text) - KERNEL_OFFSET) {
__text_start = .;
*(.text*)
. = ALIGN(4096);
__text_end = .;
}
.rodata : AT(ADDR(.rodata) - KERNEL_OFFSET) {
__rodata_start = .;
*(.rodata*)
. = ALIGN(4096);
__rodata_end = .;
}
.data : AT(ADDR(.data) - KERNEL_OFFSET) {
*(.data*)
. = ALIGN(4096);
*(.bss*)
. = ALIGN(4096);
}
__end = .;
/DISCARD/ : {
*(.comment*)
*(.eh_frame*)
*(.gcc_except_table*)
*(.note*)
*(.rel.eh_frame*)
}
}
-51
View File
@@ -1,51 +0,0 @@
ENTRY(kstart)
OUTPUT_FORMAT(elf32-i386)
KERNEL_OFFSET = 0xC0000000;
SECTIONS {
. = KERNEL_OFFSET;
. += SIZEOF_HEADERS;
/* Force the zero page to be part of a segment by creating a
* dummy section in the zero page.
* Limine will map the segment with the lowest vaddr value at
* 0xFFFFFFFF80000000 even if the segment has a higher vaddr.
* As such without the zero page being part of a segment, the
* kernel would be loaded at an offset from the expected
* location. As the redox kernel is not currently relocatable,
* this would result in a crash. A similar issue likely exists
* with multiboot/multiboot2 and the paddr of the segment.
*/
.dummy : AT(ADDR(.dummy) - KERNEL_OFFSET) {}
.text ALIGN(4K) : AT(ADDR(.text) - KERNEL_OFFSET) {
__text_start = .;
*(.text*)
}
.rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_OFFSET) {
__text_end = .;
__rodata_start = .;
*(.rodata*)
}
.data ALIGN(4K) : AT(ADDR(.data) - KERNEL_OFFSET) {
__rodata_end = .;
*(.data*)
. = ALIGN(4K);
*(.bss*)
. = ALIGN(4K);
}
__end = .;
/DISCARD/ : {
*(.comment*)
*(.eh_frame*)
*(.gcc_except_table*)
*(.note*)
*(.rel.eh_frame*)
}
}
-61
View File
@@ -1,61 +0,0 @@
ENTRY(kstart)
OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv", "elf64-littleriscv" )
KERNEL_OFFSET = 0xFFFFFFFF80000000;
SECTIONS {
. = KERNEL_OFFSET;
. += SIZEOF_HEADERS;
/* Force the zero page to be part of a segment by creating a
* dummy section in the zero page.
* Linker will map the segment with the lowest vaddr value at
* 0xFFFFFFFF80000000 even if the segment has a higher vaddr.
* As such without the zero page being part of a segment, the
* kernel would be loaded at an offset from the expected
* location. As the redox kernel is not currently relocatable,
* this would result in a crash. A similar issue likely exists
* with multiboot/multiboot2 and the paddr of the segment.
*/
.dummy ALIGN(8) : AT(ADDR(.dummy) - KERNEL_OFFSET) {}
. = ALIGN(4096);
.text : AT(ADDR(.text) - KERNEL_OFFSET) {
__text_start = .;
*(.early_init.text*)
. = ALIGN(4096);
*(.text*)
. = ALIGN(4096);
__text_end = .;
}
.rodata : AT(ADDR(.rodata) - KERNEL_OFFSET) {
__rodata_start = .;
*(.rodata*)
. = ALIGN(4096);
__rodata_end = .;
}
.data : AT(ADDR(.data) - KERNEL_OFFSET) {
*(.data*)
*(.sdata*)
. = ALIGN(4096);
*(.got*)
. = ALIGN(4096);
*(.bss*)
*(.sbss*)
. = ALIGN(4096);
}
__end = .;
/DISCARD/ : {
*(.comment*)
*(.eh_frame*)
*(.gcc_except_table*)
*(.note*)
*(.rel.eh_frame*)
}
}
-60
View File
@@ -1,60 +0,0 @@
ENTRY(kstart)
OUTPUT_FORMAT(elf64-x86-64)
KERNEL_OFFSET = 0xFFFFFFFF80000000;
SECTIONS {
. = KERNEL_OFFSET;
. += SIZEOF_HEADERS;
/* Force the zero page to be part of a segment by creating a
* dummy section in the zero page.
* Limine will map the segment with the lowest vaddr value at
* 0xFFFFFFFF80000000 even if the segment has a higher vaddr.
* As such without the zero page being part of a segment, the
* kernel would be loaded at an offset from the expected
* location. As the redox kernel is not currently relocatable,
* this would result in a crash. A similar issue likely exists
* with multiboot/multiboot2 and the paddr of the segment.
*/
.dummy : AT(ADDR(.dummy) - KERNEL_OFFSET) {}
.text ALIGN(4K) : AT(ADDR(.text) - KERNEL_OFFSET) {
__text_start = .;
*(.text*)
}
.rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_OFFSET) {
__text_end = .;
__rodata_start = .;
*(.rodata*)
__altcode_start = .;
KEEP(*(.altcode*))
__altcode_end = .;
. = ALIGN(8);
__altrelocs_start = .;
KEEP(*(.altrelocs*))
__altrelocs_end = .;
__altfeatures_start = .;
KEEP(*(.altfeatures*))
__altfeatures_end = .;
}
.data ALIGN(4K) : AT(ADDR(.data) - KERNEL_OFFSET) {
__rodata_end = .;
*(.data*)
. = ALIGN(4K);
*(.bss*)
}
__end = .;
/DISCARD/ : {
*(.comment*)
*(.eh_frame*)
*(.gcc_except_table*)
*(.note*)
*(.rel.eh_frame*)
}
}
BIN
View File
Binary file not shown.
-16
View File
@@ -1,16 +0,0 @@
[package]
name = "rmm"
version = "0.1.0"
authors = ["Jeremy Soller <jeremy@system76.com>"]
edition = "2024"
[dependencies]
bitflags = "2"
[features]
std = []
[[bin]]
name = "rmm"
path = "src/main.rs"
required-features = ["std"]
-4
View File
@@ -1,4 +0,0 @@
# Redox Memory Management
This is a Rust crate to provide abstractions for hardware memory management. It
also contains a mechanism for testing memory management with software emulation.
-296
View File
@@ -1,296 +0,0 @@
use core::{marker::PhantomData, mem};
use crate::{
Arch, BumpAllocator, FrameAllocator, FrameCount, FrameUsage, PhysicalAddress, VirtualAddress,
};
#[repr(transparent)]
struct BuddyUsage(u8);
#[repr(C, packed)]
struct BuddyEntry<A> {
base: PhysicalAddress,
size: usize,
// Number of first free page
skip: usize,
// Count of used pages
used: usize,
phantom: PhantomData<A>,
}
impl<A> Clone for BuddyEntry<A> {
fn clone(&self) -> Self {
*self
}
}
impl<A> Copy for BuddyEntry<A> {}
impl<A: Arch> BuddyEntry<A> {
fn empty() -> Self {
Self {
base: PhysicalAddress::new(0),
size: 0,
skip: 0,
used: 0,
phantom: PhantomData,
}
}
#[inline(always)]
fn pages(&self) -> usize {
self.size >> A::PAGE_SHIFT
}
fn usage_pages(&self) -> usize {
let bytes = self.pages() * mem::size_of::<BuddyUsage>();
// Round bytes used for usage to next page
(bytes + A::PAGE_OFFSET_MASK) >> A::PAGE_SHIFT
}
unsafe fn usage_addr(&self, page: usize) -> Option<VirtualAddress> {
if page < self.pages() {
let phys = self.base.add(page * mem::size_of::<BuddyUsage>());
Some(A::phys_to_virt(phys))
} else {
None
}
}
unsafe fn usage(&self, page: usize) -> Option<BuddyUsage> {
unsafe {
let addr = self.usage_addr(page)?;
Some(A::read(addr))
}
}
#[expect(clippy::unit_arg)]
unsafe fn set_usage(&self, page: usize, usage: BuddyUsage) -> Option<()> {
unsafe {
let addr = self.usage_addr(page)?;
Some(A::write(addr, usage))
}
}
}
pub struct BuddyAllocator<A> {
table_virt: VirtualAddress,
phantom: PhantomData<A>,
}
impl<A: Arch> BuddyAllocator<A> {
const BUDDY_ENTRIES: usize = A::PAGE_SIZE / mem::size_of::<BuddyEntry<A>>();
pub unsafe fn new(mut bump_allocator: BumpAllocator<A>) -> Option<Self> {
unsafe {
// Allocate buddy table
let table_phys = bump_allocator.allocate_one()?;
let table_virt = A::phys_to_virt(table_phys);
for i in 0..(A::PAGE_SIZE / mem::size_of::<BuddyEntry<A>>()) {
let virt = table_virt.add(i * mem::size_of::<BuddyEntry<A>>());
A::write(virt, BuddyEntry::<A>::empty());
}
let allocator = Self {
table_virt,
phantom: PhantomData,
};
// Add areas to buddy table, combining areas when possible, and skipping frames used
// by the bump allocator
let mut offset = bump_allocator.offset();
for old_area in bump_allocator.areas().iter() {
let mut area = *old_area;
if offset >= area.size {
offset -= area.size;
continue;
} else if offset > 0 {
area.base = area.base.add(offset);
area.size -= offset;
offset = 0;
}
for i in 0..(A::PAGE_SIZE / mem::size_of::<BuddyEntry<A>>()) {
let virt = table_virt.add(i * mem::size_of::<BuddyEntry<A>>());
let mut entry = A::read::<BuddyEntry<A>>(virt);
let inserted = if area.base.add(area.size) == { entry.base } {
// Combine entry at start
entry.base = area.base;
entry.size += area.size;
true
} else if area.base == entry.base.add(entry.size) {
// Combine entry at end
entry.size += area.size;
true
} else if entry.size == 0 {
// Create new entry
entry.base = area.base;
entry.size = area.size;
true
} else {
false
};
if inserted {
A::write(virt, entry);
break;
}
}
}
//TODO: sort areas?
// Allocate buddy maps
for i in 0..Self::BUDDY_ENTRIES {
let virt = table_virt.add(i * mem::size_of::<BuddyEntry<A>>());
let mut entry = A::read::<BuddyEntry<A>>(virt);
// Only set up entries that have enough space for their own usage map
let usage_pages = entry.usage_pages();
if entry.pages() > usage_pages {
// Mark all usage bytes as unused
let usage_start = entry.usage_addr(0)?;
for page in 0..usage_pages {
A::write_bytes(usage_start.add(page << A::PAGE_SHIFT), 0, A::PAGE_SIZE);
}
// Mark bytes used for usage as used
for page in 0..usage_pages {
entry.set_usage(page, BuddyUsage(1))?;
}
}
// Skip the pages used for usage
entry.skip = usage_pages;
// Set used pages to pages used for usage
entry.used = usage_pages;
// Write updated entry
A::write(virt, entry);
}
Some(allocator)
}
}
}
unsafe impl<A: Arch> FrameAllocator for BuddyAllocator<A> {
fn allocate(&mut self, count: FrameCount) -> Option<PhysicalAddress> {
unsafe {
if self.table_virt.data() == 0 {
return None;
}
for entry_i in 0..Self::BUDDY_ENTRIES {
let virt = self
.table_virt
.add(entry_i * mem::size_of::<BuddyEntry<A>>());
let mut entry = A::read::<BuddyEntry<A>>(virt);
let mut free_page = entry.skip;
let mut free_count = 0;
for page in entry.skip..entry.pages() {
let usage = entry.usage(page)?;
if usage.0 == 0 {
free_count += 1;
if free_count == count.data() {
break;
}
} else {
free_page = page + 1;
free_count = 0;
}
}
if free_count == count.data() {
for page in free_page..free_page + free_count {
// Update usage
let mut usage = entry.usage(page)?;
usage.0 += 1;
entry.set_usage(page, usage);
// Zero page
let page_phys = entry.base.add(page << A::PAGE_SHIFT);
let page_virt = A::phys_to_virt(page_phys);
A::write_bytes(page_virt, 0, A::PAGE_SIZE);
}
// Update skip if necessary
if entry.skip == free_page {
entry.skip = free_page + free_count;
}
// Update used page count
entry.used += free_count;
// Write updated entry
A::write(virt, entry);
return Some(entry.base.add(free_page << A::PAGE_SHIFT));
}
}
None
}
}
unsafe fn free(&mut self, base: PhysicalAddress, count: FrameCount) {
unsafe {
if self.table_virt.data() == 0 {
return;
}
let size = count.data() * A::PAGE_SIZE;
for i in 0..Self::BUDDY_ENTRIES {
let virt = self.table_virt.add(i * mem::size_of::<BuddyEntry<A>>());
let mut entry = A::read::<BuddyEntry<A>>(virt);
if base >= { entry.base } && base.add(size) <= entry.base.add(entry.size) {
let start_page = (base.data() - { entry.base }.data()) >> A::PAGE_SHIFT;
for page in start_page..start_page + count.data() {
let mut usage = entry.usage(page).expect("failed to get usage during free");
if usage.0 > 0 {
usage.0 -= 1;
} else {
panic!("tried to free already free frame");
}
// If page was freed
if usage.0 == 0 {
// Update skip if necessary
if page < entry.skip {
entry.skip = page;
}
// Update used page count
entry.used -= 1;
}
entry
.set_usage(page, usage)
.expect("failed to set usage during free");
}
// Write updated entry
A::write(virt, entry);
return;
}
}
}
}
fn usage(&self) -> FrameUsage {
unsafe {
let mut total = 0;
let mut used = 0;
for i in 0..Self::BUDDY_ENTRIES {
let virt = self.table_virt.add(i * mem::size_of::<BuddyEntry<A>>());
let entry = A::read::<BuddyEntry<A>>(virt);
total += entry.size >> A::PAGE_SHIFT;
used += entry.used;
}
FrameUsage::new(FrameCount::new(used), FrameCount::new(total))
}
}
}
-79
View File
@@ -1,79 +0,0 @@
use core::marker::PhantomData;
use crate::{Arch, FrameAllocator, FrameCount, FrameUsage, MemoryArea, PhysicalAddress};
#[derive(Debug)]
pub struct BumpAllocator<A> {
orig_areas: (&'static [MemoryArea], usize),
cur_areas: (&'static [MemoryArea], usize),
_marker: PhantomData<fn() -> A>,
}
impl<A: Arch> BumpAllocator<A> {
pub fn new(mut areas: &'static [MemoryArea], mut offset: usize) -> Self {
while let Some(first) = areas.first()
&& first.size <= offset
{
offset -= first.size;
areas = &areas[1..];
}
Self {
orig_areas: (areas, offset),
cur_areas: (areas, offset),
_marker: PhantomData,
}
}
pub fn areas(&self) -> &'static [MemoryArea] {
self.orig_areas.0
}
/// Returns one semifree and the fully free areas. The offset is the number of bytes after
/// which the first area is free.
pub fn free_areas(&self) -> (&'static [MemoryArea], usize) {
self.cur_areas
}
pub fn abs_offset(&self) -> PhysicalAddress {
let (areas, off) = self.cur_areas;
areas
.first()
.map_or(PhysicalAddress::new(0), |a| a.base.add(off))
}
pub fn offset(&self) -> usize {
(self.usage().total().data() - self.usage().free().data()) * A::PAGE_SIZE
}
}
unsafe impl<A: Arch> FrameAllocator for BumpAllocator<A> {
fn allocate(&mut self, count: FrameCount) -> Option<PhysicalAddress> {
unsafe {
let req_size = count.data() * A::PAGE_SIZE;
let block = loop {
let area = self.cur_areas.0.first()?;
let off = self.cur_areas.1;
if area.size - off < req_size {
self.cur_areas = (&self.cur_areas.0[1..], 0);
continue;
}
self.cur_areas.1 += req_size;
break area.base.add(off);
};
A::write_bytes(A::phys_to_virt(block), 0, req_size);
Some(block)
}
}
unsafe fn free(&mut self, _address: PhysicalAddress, _count: FrameCount) {
unimplemented!("BumpAllocator::free not implemented");
}
fn usage(&self) -> FrameUsage {
let total = self.orig_areas.0.iter().map(|a| a.size).sum::<usize>() - self.orig_areas.1;
let free = self.cur_areas.0.iter().map(|a| a.size).sum::<usize>() - self.cur_areas.1;
FrameUsage::new(
FrameCount::new((total - free) / A::PAGE_SIZE),
FrameCount::new(total / A::PAGE_SIZE),
)
}
}
-83
View File
@@ -1,83 +0,0 @@
use crate::PhysicalAddress;
pub use self::{buddy::*, bump::*};
mod buddy;
mod bump;
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct FrameCount(usize);
impl FrameCount {
pub fn new(count: usize) -> Self {
Self(count)
}
pub fn data(&self) -> usize {
self.0
}
}
#[derive(Debug)]
pub struct FrameUsage {
used: FrameCount,
total: FrameCount,
}
impl FrameUsage {
pub fn new(used: FrameCount, total: FrameCount) -> Self {
Self { used, total }
}
pub fn used(&self) -> FrameCount {
self.used
}
pub fn free(&self) -> FrameCount {
FrameCount(self.total.0 - self.used.0)
}
pub fn total(&self) -> FrameCount {
self.total
}
}
pub unsafe trait FrameAllocator {
fn allocate(&mut self, count: FrameCount) -> Option<PhysicalAddress>;
unsafe fn free(&mut self, address: PhysicalAddress, count: FrameCount);
fn allocate_one(&mut self) -> Option<PhysicalAddress> {
self.allocate(FrameCount::new(1))
}
unsafe fn free_one(&mut self, address: PhysicalAddress) {
unsafe {
self.free(address, FrameCount::new(1));
}
}
fn usage(&self) -> FrameUsage;
}
unsafe impl<T> FrameAllocator for &mut T
where
T: FrameAllocator,
{
fn allocate(&mut self, count: FrameCount) -> Option<PhysicalAddress> {
T::allocate(self, count)
}
unsafe fn free(&mut self, address: PhysicalAddress, count: FrameCount) {
unsafe { T::free(self, address, count) }
}
fn allocate_one(&mut self) -> Option<PhysicalAddress> {
T::allocate_one(self)
}
unsafe fn free_one(&mut self, address: PhysicalAddress) {
unsafe { T::free_one(self, address) }
}
fn usage(&self) -> FrameUsage {
T::usage(self)
}
}
-3
View File
@@ -1,3 +0,0 @@
pub use self::frame::*;
mod frame;
-153
View File
@@ -1,153 +0,0 @@
use core::arch::asm;
use crate::{Arch, PhysicalAddress, TableKind, VirtualAddress};
#[derive(Clone, Copy)]
pub struct AArch64Arch;
impl Arch for AArch64Arch {
const KERNEL_SEPARATE_TABLE: bool = true;
const PAGE_SHIFT: usize = 12; // 4096 bytes
const PAGE_ENTRY_SHIFT: usize = 9; // 512 entries, 8 bytes each
const PAGE_LEVELS: usize = 4; // L0, L1, L2, L3
//TODO
const ENTRY_ADDRESS_WIDTH: usize = 40;
const ENTRY_FLAG_DEFAULT_PAGE: usize = Self::ENTRY_FLAG_PRESENT
| 1 << 1 // Page flag
| 1 << 10 // Access flag
| Self::ENTRY_FLAG_NO_GLOBAL;
const ENTRY_FLAG_DEFAULT_TABLE: usize
= Self::ENTRY_FLAG_PRESENT
| Self::ENTRY_FLAG_READWRITE
| 1 << 1 // Table flag
| 1 << 10 // Access flag
;
const ENTRY_FLAG_PRESENT: usize = 1 << 0;
const ENTRY_FLAG_READONLY: usize = 1 << 7;
const ENTRY_FLAG_READWRITE: usize = 0;
const ENTRY_FLAG_PAGE_USER: usize = 1 << 6;
// This sets both userspace and privileged execute never
//TODO: Separate the two?
const ENTRY_FLAG_NO_EXEC: usize = 0b11 << 53;
const ENTRY_FLAG_EXEC: usize = 0;
const ENTRY_FLAG_GLOBAL: usize = 0;
const ENTRY_FLAG_NO_GLOBAL: usize = 1 << 11;
const ENTRY_FLAG_DEVICE_MEMORY: usize = MEM_ATTR_DEVICE_nGnRnE << 2;
const ENTRY_FLAG_UNCACHEABLE: usize = MEM_ATTR_NC << 2;
const ENTRY_FLAG_WRITE_COMBINING: usize = MEM_ATTR_NC << 2;
const PHYS_OFFSET: usize = 0xFFFF_8000_0000_0000;
#[inline(always)]
fn invalidate(address: VirtualAddress) {
unsafe {
asm!("
dsb ishst
tlbi vaae1is, {}
dsb ish
isb
", in(reg) (address.data() >> Self::PAGE_SHIFT));
}
}
#[inline(always)]
fn invalidate_all() {
unsafe {
asm!(
"
dsb ishst
tlbi vmalle1is
dsb ish
isb
"
);
}
}
#[inline(always)]
fn table(table_kind: TableKind) -> PhysicalAddress {
let address: usize;
match table_kind {
TableKind::User => {
unsafe { asm!("mrs {0}, ttbr0_el1", out(reg) address) };
}
TableKind::Kernel => {
unsafe { asm!("mrs {0}, ttbr1_el1", out(reg) address) };
}
}
PhysicalAddress::new(address)
}
#[inline(always)]
unsafe fn set_table(table_kind: TableKind, address: PhysicalAddress) {
unsafe {
match table_kind {
TableKind::User => {
asm!("msr ttbr0_el1, {0}", in(reg) address.data());
}
TableKind::Kernel => {
asm!("msr ttbr1_el1, {0}", in(reg) address.data());
}
}
Self::invalidate_all();
}
}
fn virt_is_valid(_address: VirtualAddress) -> bool {
//TODO: what makes an address valid on aarch64?
true
}
}
#[cfg_attr(not(target_arch = "aarch64"), allow(unused))]
const MEM_ATTR_WB: usize = 0;
const MEM_ATTR_NC: usize = 1;
#[allow(non_upper_case_globals)]
const MEM_ATTR_DEVICE_nGnRnE: usize = 2;
/// Setup Memory Access Indirection Register
#[cfg(target_arch = "aarch64")]
#[inline(always)]
pub unsafe fn init_mair() {
// https://github.com/freebsd/freebsd-src/blob/d15733065c4221dcd5bb3622d225760f271f6fc9/sys/arm64/include/armreg.h#L1986-L1991
const fn mair_attr(attr: u64, idx: usize) -> u64 {
attr << (idx * 8)
}
#[allow(non_upper_case_globals)]
const MAIR_DEVICE_nGnRnE: u64 = 0x00;
#[allow(non_upper_case_globals)]
const _MAIR_DEVICE_nGnRE: u64 = 0x04;
const MAIR_NORMAL_NC: u64 = 0x44;
const _MAIR_NORMAL_WT: u64 = 0xbb;
const MAIR_NORMAL_WB: u64 = 0xff;
unsafe {
let val: u64 = const {
mair_attr(MAIR_DEVICE_nGnRnE, MEM_ATTR_DEVICE_nGnRnE)
| mair_attr(MAIR_NORMAL_NC, MEM_ATTR_NC)
| mair_attr(MAIR_NORMAL_WB, MEM_ATTR_WB)
};
asm!("msr mair_el1, {}", in(reg) val);
}
}
const _: () = {
assert!(AArch64Arch::PAGE_SIZE == 4096);
assert!(AArch64Arch::PAGE_OFFSET_MASK == 0xFFF);
assert!(AArch64Arch::PAGE_ADDRESS_SHIFT == 48);
assert!(AArch64Arch::PAGE_ADDRESS_SIZE == 0x0001_0000_0000_0000);
assert!(AArch64Arch::PAGE_ADDRESS_MASK == 0x0000_FFFF_FFFF_F000);
assert!(AArch64Arch::PAGE_ENTRY_SIZE == 8);
assert!(AArch64Arch::PAGE_ENTRIES == 512);
assert!(AArch64Arch::PAGE_ENTRY_MASK == 0x1FF);
assert!(AArch64Arch::PAGE_NEGATIVE_MASK == 0xFFFF_0000_0000_0000);
assert!(AArch64Arch::ENTRY_ADDRESS_SIZE == 0x0000_0100_0000_0000);
assert!(AArch64Arch::ENTRY_ADDRESS_MASK == 0x0000_00FF_FFFF_FFFF);
assert!(AArch64Arch::ENTRY_FLAGS_MASK == 0xFFF0_0000_0000_0FFF);
assert!(AArch64Arch::PHYS_OFFSET == 0xFFFF_8000_0000_0000);
};
-355
View File
@@ -1,355 +0,0 @@
extern crate std;
use std::{boxed::Box, collections::BTreeMap, marker::PhantomData, mem, ptr, sync::Mutex, vec};
use crate::{
arch::x86_64::X8664Arch, page::PageFlags, Arch, MemoryArea, PageEntry, PhysicalAddress,
TableKind, VirtualAddress, MEGABYTE,
};
#[derive(Clone, Copy)]
pub struct EmulateArch;
impl EmulateArch {
pub unsafe fn init() -> &'static [MemoryArea] {
unsafe {
// Create machine with PAGE_ENTRIES pages offset mapped (2 MiB on x86_64)
let mut machine = Machine::new(MEMORY_SIZE);
// PML4 index 256 (PHYS_OFFSET) link to PDP
let pml4 = 0;
let pdp = pml4 + Self::PAGE_SIZE;
let flags = Self::ENTRY_FLAG_READWRITE | Self::ENTRY_FLAG_PRESENT;
machine.write_phys::<usize>(
PhysicalAddress::new(pml4 + 256 * Self::PAGE_ENTRY_SIZE),
pdp | flags,
);
// PDP link to PD
let pd = pdp + Self::PAGE_SIZE;
machine.write_phys::<usize>(PhysicalAddress::new(pdp), pd | flags);
// PD link to PT
let pt = pd + Self::PAGE_SIZE;
machine.write_phys::<usize>(PhysicalAddress::new(pd), pt | flags);
// PT links to frames
for i in 0..Self::PAGE_ENTRIES {
let page = i * Self::PAGE_SIZE;
machine.write_phys::<usize>(
PhysicalAddress::new(pt + i * Self::PAGE_ENTRY_SIZE),
page | flags,
);
}
*MACHINE.lock().unwrap() = Some(machine);
// Set table to pml4
EmulateArch::set_table(TableKind::Kernel, PhysicalAddress::new(pml4));
&MEMORY_AREAS
}
}
}
impl Arch for EmulateArch {
const KERNEL_SEPARATE_TABLE: bool = false;
const PAGE_SHIFT: usize = X8664Arch::PAGE_SHIFT;
const PAGE_ENTRY_SHIFT: usize = X8664Arch::PAGE_ENTRY_SHIFT;
const PAGE_LEVELS: usize = X8664Arch::PAGE_LEVELS;
const ENTRY_ADDRESS_SHIFT: usize = X8664Arch::ENTRY_ADDRESS_SHIFT;
const ENTRY_FLAG_DEFAULT_PAGE: usize = X8664Arch::ENTRY_FLAG_DEFAULT_PAGE;
const ENTRY_FLAG_DEFAULT_TABLE: usize = X8664Arch::ENTRY_FLAG_DEFAULT_TABLE;
const ENTRY_FLAG_PRESENT: usize = X8664Arch::ENTRY_FLAG_PRESENT;
const ENTRY_FLAG_READONLY: usize = X8664Arch::ENTRY_FLAG_READONLY;
const ENTRY_FLAG_READWRITE: usize = X8664Arch::ENTRY_FLAG_READWRITE;
const ENTRY_FLAG_PAGE_USER: usize = X8664Arch::ENTRY_FLAG_PAGE_USER;
const ENTRY_FLAG_NO_EXEC: usize = X8664Arch::ENTRY_FLAG_NO_EXEC;
const ENTRY_FLAG_EXEC: usize = X8664Arch::ENTRY_FLAG_EXEC;
const PHYS_OFFSET: usize = X8664Arch::PHYS_OFFSET;
const ENTRY_FLAG_GLOBAL: usize = X8664Arch::ENTRY_FLAG_GLOBAL;
const ENTRY_FLAG_NO_GLOBAL: usize = X8664Arch::ENTRY_FLAG_NO_GLOBAL;
const ENTRY_ADDRESS_WIDTH: usize = X8664Arch::ENTRY_ADDRESS_WIDTH;
const ENTRY_FLAG_DEVICE_MEMORY: usize = X8664Arch::ENTRY_FLAG_DEVICE_MEMORY;
const ENTRY_FLAG_UNCACHEABLE: usize = X8664Arch::ENTRY_FLAG_UNCACHEABLE;
const ENTRY_FLAG_WRITE_COMBINING: usize = X8664Arch::ENTRY_FLAG_WRITE_COMBINING;
#[inline(always)]
unsafe fn read<T>(address: VirtualAddress) -> T {
MACHINE.lock().unwrap().as_ref().unwrap().read(address)
}
#[inline(always)]
unsafe fn write<T>(address: VirtualAddress, value: T) {
MACHINE
.lock()
.unwrap()
.as_mut()
.unwrap()
.write(address, value)
}
#[inline(always)]
unsafe fn write_bytes(address: VirtualAddress, value: u8, count: usize) {
MACHINE
.lock()
.unwrap()
.as_mut()
.unwrap()
.write_bytes(address, value, count)
}
#[inline(always)]
fn invalidate(address: VirtualAddress) {
MACHINE
.lock()
.unwrap()
.as_mut()
.unwrap()
.invalidate(address);
}
#[inline(always)]
fn invalidate_all() {
MACHINE.lock().unwrap().as_mut().unwrap().invalidate_all();
}
#[inline(always)]
fn table(_table_kind: TableKind) -> PhysicalAddress {
MACHINE.lock().unwrap().as_mut().unwrap().get_table()
}
#[inline(always)]
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
MACHINE.lock().unwrap().as_mut().unwrap().set_table(address);
}
fn virt_is_valid(_address: VirtualAddress) -> bool {
// TODO: Don't see why an emulated arch would have any problems with canonicalness...
true
}
}
const MEMORY_SIZE: usize = 64 * MEGABYTE;
static MEMORY_AREAS: [MemoryArea; 2] = [
MemoryArea {
base: PhysicalAddress::new(EmulateArch::PAGE_SIZE * 4), // Initial PML4, PDP, PD, and PT wasted
size: MEMORY_SIZE / 2 - EmulateArch::PAGE_SIZE * 4,
},
// Second area for debugging
MemoryArea {
base: PhysicalAddress::new(MEMORY_SIZE / 2),
size: MEMORY_SIZE / 2,
},
];
static MACHINE: Mutex<Option<Machine<EmulateArch>>> = Mutex::new(None);
struct Machine<A> {
memory: Box<[u8]>,
map: BTreeMap<VirtualAddress, PageEntry<A>>,
table_addr: PhysicalAddress,
phantom: PhantomData<A>,
}
impl<A: Arch> Machine<A> {
fn new(memory_size: usize) -> Self {
Self {
memory: vec![0; memory_size].into_boxed_slice(),
map: BTreeMap::new(),
table_addr: PhysicalAddress::new(0),
phantom: PhantomData,
}
}
fn read_phys<T>(&self, phys: PhysicalAddress) -> T {
let size = mem::size_of::<T>();
if phys.add(size).data() <= self.memory.len() {
unsafe { ptr::read(self.memory.as_ptr().add(phys.data()) as *const T) }
} else {
panic!(
"read_phys: 0x{:X} size 0x{:X} outside of memory",
phys.data(),
size
);
}
}
fn write_phys<T>(&mut self, phys: PhysicalAddress, value: T) {
let size = mem::size_of::<T>();
if phys.add(size).data() <= self.memory.len() {
unsafe {
ptr::write(self.memory.as_mut_ptr().add(phys.data()) as *mut T, value);
}
} else {
panic!(
"write_phys: 0x{:X} size 0x{:X} outside of memory",
phys.data(),
size
);
}
}
fn write_phys_bytes(&mut self, phys: PhysicalAddress, value: u8, count: usize) {
if phys.add(count).data() <= self.memory.len() {
unsafe {
ptr::write_bytes(self.memory.as_mut_ptr().add(phys.data()), value, count);
}
} else {
panic!(
"write_phys_bytes: 0x{:X} count 0x{:X} outside of memory",
phys.data(),
count
);
}
}
fn translate(&self, virt: VirtualAddress) -> Option<(PhysicalAddress, PageFlags<A>)> {
let virt_data = virt.data();
let page = virt_data & A::PAGE_ADDRESS_MASK;
let offset = virt_data & A::PAGE_OFFSET_MASK;
let entry = self.map.get(&VirtualAddress::new(page))?;
Some((entry.address().ok()?.add(offset), entry.flags()))
}
fn read<T>(&self, virt: VirtualAddress) -> T {
//TODO: allow reading past page boundaries
let virt_data = virt.data();
let size = mem::size_of::<T>();
if (virt_data & A::PAGE_ADDRESS_MASK) != ((virt_data + (size - 1)) & A::PAGE_ADDRESS_MASK) {
panic!(
"read: 0x{:X} size 0x{:X} passes page boundary",
virt_data, size
);
}
if let Some((phys, _flags)) = self.translate(virt) {
self.read_phys(phys)
} else {
panic!("read: 0x{:X} size 0x{:X} not present", virt_data, size);
}
}
fn write<T>(&mut self, virt: VirtualAddress, value: T) {
//TODO: allow writing past page boundaries
let virt_data = virt.data();
let size = mem::size_of::<T>();
if (virt_data & A::PAGE_ADDRESS_MASK) != ((virt_data + (size - 1)) & A::PAGE_ADDRESS_MASK) {
panic!(
"write: 0x{:X} size 0x{:X} passes page boundary",
virt_data, size
);
}
if let Some((phys, flags)) = self.translate(virt) {
if flags.has_write() {
self.write_phys(phys, value);
} else {
panic!("write: 0x{:X} size 0x{:X} not writable", virt_data, size);
}
} else {
panic!("write: 0x{:X} size 0x{:X} not present", virt_data, size);
}
}
fn write_bytes(&mut self, virt: VirtualAddress, value: u8, count: usize) {
//TODO: allow writing past page boundaries
let virt_data = virt.data();
if (virt_data & A::PAGE_ADDRESS_MASK) != ((virt_data + (count - 1)) & A::PAGE_ADDRESS_MASK)
{
panic!(
"write_bytes: 0x{:X} count 0x{:X} passes page boundary",
virt_data, count
);
}
if let Some((phys, flags)) = self.translate(virt) {
if flags.has_write() {
self.write_phys_bytes(phys, value, count);
} else {
panic!(
"write_bytes: 0x{:X} count 0x{:X} not writable",
virt_data, count
);
}
} else {
panic!(
"write_bytes: 0x{:X} count 0x{:X} not present",
virt_data, count
);
}
}
fn invalidate(&mut self, _address: VirtualAddress) {
unimplemented!("EmulateArch::invalidate not implemented");
}
//TODO: cleanup
fn invalidate_all(&mut self) {
self.map.clear();
// PML4
let a4 = self.table_addr.data();
for i4 in 0..A::PAGE_ENTRIES {
let e3 = self.read_phys::<usize>(PhysicalAddress::new(a4 + i4 * A::PAGE_ENTRY_SIZE));
let f3 = e3 & A::ENTRY_FLAGS_MASK;
if f3 & A::ENTRY_FLAG_PRESENT == 0 {
continue;
}
// Page directory pointer
let a3 = ((e3 >> A::ENTRY_ADDRESS_SHIFT) & A::ENTRY_ADDRESS_MASK) << A::PAGE_SHIFT;
for i3 in 0..A::PAGE_ENTRIES {
let e2 =
self.read_phys::<usize>(PhysicalAddress::new(a3 + i3 * A::PAGE_ENTRY_SIZE));
let f2 = e2 & A::ENTRY_FLAGS_MASK;
if f2 & A::ENTRY_FLAG_PRESENT == 0 {
continue;
}
// Page directory
let a2 = ((e2 >> A::ENTRY_ADDRESS_SHIFT) & A::ENTRY_ADDRESS_MASK) << A::PAGE_SHIFT;
for i2 in 0..A::PAGE_ENTRIES {
let e1 =
self.read_phys::<usize>(PhysicalAddress::new(a2 + i2 * A::PAGE_ENTRY_SIZE));
let f1 = e1 & A::ENTRY_FLAGS_MASK;
if f1 & A::ENTRY_FLAG_PRESENT == 0 {
continue;
}
// Page table
let a1 =
((e1 >> A::ENTRY_ADDRESS_SHIFT) & A::ENTRY_ADDRESS_MASK) << A::PAGE_SHIFT;
for i1 in 0..A::PAGE_ENTRIES {
let e = self
.read_phys::<usize>(PhysicalAddress::new(a1 + i1 * A::PAGE_ENTRY_SIZE));
let f = e & A::ENTRY_FLAGS_MASK;
if f & A::ENTRY_FLAG_PRESENT == 0 {
continue;
}
// Page
let page = (i4 << 39) | (i3 << 30) | (i2 << 21) | (i1 << 12);
//println!("map 0x{:X} to 0x{:X}, 0x{:X}", page, a, f);
self.map
.insert(VirtualAddress::new(page), PageEntry::from_data(e));
}
}
}
}
}
fn get_table(&self) -> PhysicalAddress {
self.table_addr
}
fn set_table(&mut self, address: PhysicalAddress) {
self.table_addr = address;
self.invalidate_all();
}
}
-93
View File
@@ -1,93 +0,0 @@
use core::ptr;
use crate::{PhysicalAddress, TableKind, VirtualAddress};
//TODO: Support having all page tables compile on all architectures
#[cfg(target_pointer_width = "64")]
pub mod aarch64;
#[cfg(all(feature = "std", target_pointer_width = "64"))]
pub mod emulate;
#[cfg(target_pointer_width = "64")]
pub mod riscv64;
#[cfg(target_pointer_width = "32")]
pub mod x86;
#[cfg(target_pointer_width = "64")]
pub mod x86_64;
mod x86_shared;
pub trait Arch: Clone + Copy {
/// Does the architecture use a separate page table for the kernel.
///
/// If false, the page table entries corresponding to the top half of the
/// address space will be copied into the top level of every page table
/// and will never be unmapped when unmapping pages.
const KERNEL_SEPARATE_TABLE: bool;
const PAGE_SHIFT: usize;
const PAGE_ENTRY_SHIFT: usize;
const PAGE_LEVELS: usize;
const ENTRY_ADDRESS_WIDTH: usize; // Number of bits of physical address in PTE
const ENTRY_ADDRESS_SHIFT: usize = Self::PAGE_SHIFT; // Offset of physical address in PTE
const ENTRY_FLAG_DEFAULT_PAGE: usize;
const ENTRY_FLAG_DEFAULT_TABLE: usize;
const ENTRY_FLAG_PRESENT: usize;
const ENTRY_FLAG_READONLY: usize;
const ENTRY_FLAG_READWRITE: usize;
const ENTRY_FLAG_PAGE_USER: usize; // Leaf table user page flag
const ENTRY_FLAG_TABLE_USER: usize = Self::ENTRY_FLAG_PAGE_USER; // Directory user page table flag
const ENTRY_FLAG_NO_EXEC: usize;
const ENTRY_FLAG_EXEC: usize;
const ENTRY_FLAG_GLOBAL: usize;
const ENTRY_FLAG_NO_GLOBAL: usize;
const ENTRY_FLAG_DEVICE_MEMORY: usize;
const ENTRY_FLAG_UNCACHEABLE: usize;
const ENTRY_FLAG_WRITE_COMBINING: usize;
const PHYS_OFFSET: usize;
const PAGE_SIZE: usize = 1 << Self::PAGE_SHIFT;
const PAGE_OFFSET_MASK: usize = Self::PAGE_SIZE - 1;
const PAGE_ADDRESS_SHIFT: usize = Self::PAGE_LEVELS * Self::PAGE_ENTRY_SHIFT + Self::PAGE_SHIFT;
const PAGE_ADDRESS_SIZE: u64 = 1 << (Self::PAGE_ADDRESS_SHIFT as u64);
const PAGE_ADDRESS_MASK: usize = (Self::PAGE_ADDRESS_SIZE - (Self::PAGE_SIZE as u64)) as usize;
const PAGE_ENTRY_SIZE: usize = 1 << (Self::PAGE_SHIFT - Self::PAGE_ENTRY_SHIFT);
const PAGE_ENTRIES: usize = 1 << Self::PAGE_ENTRY_SHIFT;
const PAGE_ENTRY_MASK: usize = Self::PAGE_ENTRIES - 1;
const PAGE_NEGATIVE_MASK: usize = !(Self::PAGE_ADDRESS_SIZE - 1) as usize;
const ENTRY_ADDRESS_SIZE: usize = 1 << Self::ENTRY_ADDRESS_WIDTH; // size of addressable physical memory, in pages
const ENTRY_ADDRESS_MASK: usize = Self::ENTRY_ADDRESS_SIZE - 1; // Mask of physical address, starting at 0th bit
const ENTRY_FLAGS_MASK: usize = !(Self::ENTRY_ADDRESS_MASK << Self::ENTRY_ADDRESS_SHIFT);
#[inline(always)]
unsafe fn read<T>(address: VirtualAddress) -> T {
unsafe { ptr::read(address.data() as *const T) }
}
#[inline(always)]
unsafe fn write<T>(address: VirtualAddress, value: T) {
unsafe { ptr::write(address.data() as *mut T, value) }
}
#[inline(always)]
unsafe fn write_bytes(address: VirtualAddress, value: u8, count: usize) {
unsafe { ptr::write_bytes(address.data() as *mut u8, value, count) }
}
fn invalidate(address: VirtualAddress);
fn invalidate_all();
fn table(table_kind: TableKind) -> PhysicalAddress;
unsafe fn set_table(table_kind: TableKind, address: PhysicalAddress);
#[inline(always)]
fn phys_to_virt(phys: PhysicalAddress) -> VirtualAddress {
match phys.data().checked_add(Self::PHYS_OFFSET) {
Some(some) => VirtualAddress::new(some),
None => panic!("phys_to_virt({:#x}) overflow", phys.data()),
}
}
fn virt_is_valid(address: VirtualAddress) -> bool;
}
-7
View File
@@ -1,7 +0,0 @@
pub use sv39::RiscV64Sv39Arch;
pub use sv48::RiscV64Sv48Arch;
pub use sv57::RiscV64Sv57Arch;
mod sv39;
mod sv48;
mod sv57;
-124
View File
@@ -1,124 +0,0 @@
use core::arch::asm;
use crate::{Arch, PhysicalAddress, TableKind, VirtualAddress};
#[derive(Clone, Copy)]
pub struct RiscV64Sv39Arch;
pub const ACCESSED: usize = 1 << 6;
pub const DIRTY: usize = 1 << 7;
impl Arch for RiscV64Sv39Arch {
const KERNEL_SEPARATE_TABLE: bool = false;
const PAGE_SHIFT: usize = 12; // 4096 bytes
const PAGE_ENTRY_SHIFT: usize = 9; // 512 entries, 8 bytes each
const PAGE_LEVELS: usize = 3; // L0, L1, L2
const ENTRY_ADDRESS_WIDTH: usize = 44;
const ENTRY_ADDRESS_SHIFT: usize = 10;
const ENTRY_FLAG_DEFAULT_PAGE: usize =
Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_READONLY | ACCESSED | DIRTY;
const ENTRY_FLAG_DEFAULT_TABLE: usize = Self::ENTRY_FLAG_PRESENT;
const ENTRY_FLAG_PRESENT: usize = 1 << 0;
const ENTRY_FLAG_READONLY: usize = 1 << 1;
const ENTRY_FLAG_READWRITE: usize = 3 << 1;
const ENTRY_FLAG_PAGE_USER: usize = 1 << 4;
const ENTRY_FLAG_TABLE_USER: usize = 0;
const ENTRY_FLAG_NO_EXEC: usize = 0;
const ENTRY_FLAG_EXEC: usize = 1 << 3;
const ENTRY_FLAG_GLOBAL: usize = 1 << 5;
const ENTRY_FLAG_NO_GLOBAL: usize = 0;
const ENTRY_FLAG_DEVICE_MEMORY: usize = 0; // FIXME use Svpbmt
const ENTRY_FLAG_UNCACHEABLE: usize = 0; // FIXME use Svpbmt
const ENTRY_FLAG_WRITE_COMBINING: usize = 0; // FIXME use Svpbmt
const PHYS_OFFSET: usize = 0xFFFF_FFC0_0000_0000;
#[inline(always)]
fn invalidate(address: VirtualAddress) {
unsafe { asm!("sfence.vma {}", in(reg) address.data()) };
}
#[inline(always)]
fn invalidate_all() {
unsafe { asm!("sfence.vma") };
}
#[inline(always)]
fn table(_table_kind: TableKind) -> PhysicalAddress {
let satp: usize;
unsafe { asm!("csrr {0}, satp", out(reg) satp) };
PhysicalAddress::new(
(satp & Self::ENTRY_ADDRESS_MASK) << Self::PAGE_SHIFT, // Convert from PPN
)
}
#[inline(always)]
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
let satp = (8 << 60) | // Sv39 MODE
(address.data() >> Self::PAGE_SHIFT); // Convert to PPN (TODO: ensure alignment)
unsafe {
asm!("csrw satp, {0}", in(reg) satp);
Self::invalidate_all();
}
}
fn virt_is_valid(address: VirtualAddress) -> bool {
let mask = !((Self::PAGE_ADDRESS_SIZE as usize - 1) >> 1);
let masked = address.data() & mask;
masked == mask || masked == 0
}
}
const _: () = {
assert!(RiscV64Sv39Arch::PAGE_SIZE == 4096);
assert!(RiscV64Sv39Arch::PAGE_OFFSET_MASK == 0xFFF);
assert!(RiscV64Sv39Arch::PAGE_ADDRESS_SHIFT == 39);
assert!(RiscV64Sv39Arch::PAGE_ADDRESS_SIZE == 0x0000_0080_0000_0000);
assert!(RiscV64Sv39Arch::PAGE_ADDRESS_MASK == 0x0000_007F_FFFF_F000);
assert!(RiscV64Sv39Arch::PAGE_ENTRY_SIZE == 8);
assert!(RiscV64Sv39Arch::PAGE_ENTRIES == 512);
assert!(RiscV64Sv39Arch::PAGE_ENTRY_MASK == 0x1FF);
assert!(RiscV64Sv39Arch::PAGE_NEGATIVE_MASK == 0xFFFF_FF80_0000_0000);
assert!(RiscV64Sv39Arch::ENTRY_ADDRESS_SIZE == 0x0000_1000_0000_0000);
assert!(RiscV64Sv39Arch::ENTRY_ADDRESS_MASK == 0x0000_0FFF_FFFF_FFFF);
assert!(RiscV64Sv39Arch::ENTRY_FLAGS_MASK == 0xFFC0_0000_0000_03FF);
assert!(RiscV64Sv39Arch::PHYS_OFFSET == 0xFFFF_FFC0_0000_0000);
};
#[cfg(test)]
mod tests {
use super::RiscV64Sv39Arch;
use crate::Arch;
#[test]
fn is_canonical() {
use super::VirtualAddress;
#[track_caller]
fn yes(addr: usize) {
assert!(RiscV64Sv39Arch::virt_is_valid(VirtualAddress::new(addr)));
}
#[track_caller]
fn no(addr: usize) {
assert!(!RiscV64Sv39Arch::virt_is_valid(VirtualAddress::new(addr)));
}
yes(0xFFFF_FFFF_FFFF_FFFF);
yes(0xFFFF_FFF0_1337_1337);
no(0x0000_0F00_0000_0000);
no(0x1337_0000_0000_0000);
no(1 << 38);
yes(1 << 37);
// Check for off-by-one errors.
yes(0xFFFF_FFC0_0000_0000 | (1 << 37));
yes(0xFFFF_FFE0_0000_0000 | (1 << 37));
no(0xFFFF_FF80_0000_0000 | (1 << 37));
}
}
-118
View File
@@ -1,118 +0,0 @@
use core::arch::asm;
use crate::{Arch, PhysicalAddress, TableKind, VirtualAddress};
#[derive(Clone, Copy)]
pub struct RiscV64Sv48Arch;
impl Arch for RiscV64Sv48Arch {
const KERNEL_SEPARATE_TABLE: bool = false;
const PAGE_SHIFT: usize = 12; // 4096 bytes
const PAGE_ENTRY_SHIFT: usize = 9; // 512 entries, 8 bytes each
const PAGE_LEVELS: usize = 4; // L0, L1, L2, L3
const ENTRY_ADDRESS_WIDTH: usize = 44;
const ENTRY_ADDRESS_SHIFT: usize = 10;
const ENTRY_FLAG_DEFAULT_PAGE: usize = Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_READONLY;
const ENTRY_FLAG_DEFAULT_TABLE: usize = Self::ENTRY_FLAG_PRESENT;
const ENTRY_FLAG_PRESENT: usize = 1 << 0;
const ENTRY_FLAG_READONLY: usize = 1 << 1;
const ENTRY_FLAG_READWRITE: usize = 3 << 1;
const ENTRY_FLAG_PAGE_USER: usize = 1 << 4;
const ENTRY_FLAG_TABLE_USER: usize = 0;
const ENTRY_FLAG_NO_EXEC: usize = 0;
const ENTRY_FLAG_EXEC: usize = 1 << 3;
const ENTRY_FLAG_GLOBAL: usize = 1 << 5;
const ENTRY_FLAG_NO_GLOBAL: usize = 0;
const ENTRY_FLAG_DEVICE_MEMORY: usize = 0; // FIXME use Svpbmt
const ENTRY_FLAG_UNCACHEABLE: usize = 0; // FIXME use Svpbmt
const ENTRY_FLAG_WRITE_COMBINING: usize = 0; // FIXME use Svpbmt
const PHYS_OFFSET: usize = 0xFFFF_8000_0000_0000;
#[inline(always)]
fn invalidate(address: VirtualAddress) {
unsafe { asm!("sfence.vma {}", in(reg) address.data()) };
}
#[inline(always)]
fn invalidate_all() {
unsafe { asm!("sfence.vma") };
}
#[inline(always)]
fn table(_table_kind: TableKind) -> PhysicalAddress {
let satp: usize;
unsafe { asm!("csrr {0}, satp", out(reg) satp) };
PhysicalAddress::new(
(satp & Self::ENTRY_ADDRESS_MASK) << Self::PAGE_SHIFT, // Convert from PPN
)
}
#[inline(always)]
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
let satp = (9 << 60) | // Sv48 MODE
(address.data() >> Self::PAGE_SHIFT); // Convert to PPN (TODO: ensure alignment)
unsafe {
asm!("csrw satp, {0}", in(reg) satp);
Self::invalidate_all();
}
}
fn virt_is_valid(address: VirtualAddress) -> bool {
// RISC-V SV48 uses 48-bit sign-extended addresses, identical to 4-level paging on x86_64.
let mask = !((Self::PAGE_ADDRESS_SIZE as usize - 1) >> 1);
let masked = address.data() & mask;
masked == mask || masked == 0
}
}
const _: () = {
assert!(RiscV64Sv48Arch::PAGE_SIZE == 4096);
assert!(RiscV64Sv48Arch::PAGE_OFFSET_MASK == 0xFFF);
assert!(RiscV64Sv48Arch::PAGE_ADDRESS_SHIFT == 48);
assert!(RiscV64Sv48Arch::PAGE_ADDRESS_SIZE == 0x0001_0000_0000_0000);
assert!(RiscV64Sv48Arch::PAGE_ADDRESS_MASK == 0x0000_FFFF_FFFF_F000);
assert!(RiscV64Sv48Arch::PAGE_ENTRY_SIZE == 8);
assert!(RiscV64Sv48Arch::PAGE_ENTRIES == 512);
assert!(RiscV64Sv48Arch::PAGE_ENTRY_MASK == 0x1FF);
assert!(RiscV64Sv48Arch::PAGE_NEGATIVE_MASK == 0xFFFF_0000_0000_0000);
assert!(RiscV64Sv48Arch::ENTRY_ADDRESS_SIZE == 0x0000_1000_0000_0000);
assert!(RiscV64Sv48Arch::ENTRY_ADDRESS_MASK == 0x0000_0FFF_FFFF_FFFF);
assert!(RiscV64Sv48Arch::ENTRY_FLAGS_MASK == 0xFFC0_0000_0000_03FF);
assert!(RiscV64Sv48Arch::PHYS_OFFSET == 0xFFFF_8000_0000_0000);
};
#[cfg(test)]
mod tests {
use super::RiscV64Sv48Arch;
use crate::Arch;
#[test]
fn is_canonical() {
use super::VirtualAddress;
// Close to identical when compared to x86_64 test.
fn yes(address: usize) {
assert!(RiscV64Sv48Arch::virt_is_valid(VirtualAddress::new(address)));
}
fn no(address: usize) {
assert!(!RiscV64Sv48Arch::virt_is_valid(VirtualAddress::new(
address
)));
}
yes(0xFFFF_8000_1337_1337);
yes(0xFFFF_FFFF_FFFF_FFFF);
yes(0x0000_0000_0000_0042);
yes(0x0000_7FFF_FFFF_FFFF);
no(0x1337_0000_0000_0000);
no(0x1337_8000_0000_0000);
no(0x0000_8000_0000_0000);
}
}
-116
View File
@@ -1,116 +0,0 @@
use core::arch::asm;
use crate::{Arch, PhysicalAddress, TableKind, VirtualAddress};
#[derive(Clone, Copy)]
pub struct RiscV64Sv57Arch;
impl Arch for RiscV64Sv57Arch {
const KERNEL_SEPARATE_TABLE: bool = false;
const PAGE_SHIFT: usize = 12; // 4096 bytes
const PAGE_ENTRY_SHIFT: usize = 9; // 512 entries, 8 bytes each
const PAGE_LEVELS: usize = 5; // L0, L1, L2, L3, L4
const ENTRY_ADDRESS_WIDTH: usize = 44;
const ENTRY_ADDRESS_SHIFT: usize = 10;
const ENTRY_FLAG_DEFAULT_PAGE: usize = Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_READONLY;
const ENTRY_FLAG_DEFAULT_TABLE: usize = Self::ENTRY_FLAG_PRESENT;
const ENTRY_FLAG_PRESENT: usize = 1 << 0;
const ENTRY_FLAG_READONLY: usize = 1 << 1;
const ENTRY_FLAG_READWRITE: usize = 3 << 1;
const ENTRY_FLAG_PAGE_USER: usize = 1 << 4;
const ENTRY_FLAG_TABLE_USER: usize = 0;
const ENTRY_FLAG_NO_EXEC: usize = 0;
const ENTRY_FLAG_EXEC: usize = 1 << 3;
const ENTRY_FLAG_GLOBAL: usize = 1 << 5;
const ENTRY_FLAG_NO_GLOBAL: usize = 0;
const ENTRY_FLAG_DEVICE_MEMORY: usize = 0; // FIXME use Svpbmt
const ENTRY_FLAG_UNCACHEABLE: usize = 0; // FIXME use Svpbmt
const ENTRY_FLAG_WRITE_COMBINING: usize = 0; // FIXME use Svpbmt
const PHYS_OFFSET: usize = 0xFF00_0000_0000_0000;
#[inline(always)]
fn invalidate(address: VirtualAddress) {
unsafe { asm!("sfence.vma {}", in(reg) address.data()) };
}
#[inline(always)]
fn invalidate_all() {
unsafe { asm!("sfence.vma") };
}
#[inline(always)]
fn table(_table_kind: TableKind) -> PhysicalAddress {
let satp: usize;
unsafe { asm!("csrr {0}, satp", out(reg) satp) };
PhysicalAddress::new(
(satp & Self::ENTRY_ADDRESS_MASK) << Self::PAGE_SHIFT, // Convert from PPN
)
}
#[inline(always)]
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
let satp = (10 << 60) | // Sv57 MODE
(address.data() >> Self::PAGE_SHIFT); // Convert to PPN (TODO: ensure alignment)
unsafe {
asm!("csrw satp, {0}", in(reg) satp);
Self::invalidate_all();
}
}
fn virt_is_valid(address: VirtualAddress) -> bool {
let mask = !((Self::PAGE_ADDRESS_SIZE as usize - 1) >> 1);
let masked = address.data() & mask;
masked == mask || masked == 0
}
}
const _: () = {
assert!(RiscV64Sv57Arch::PAGE_SIZE == 4096);
assert!(RiscV64Sv57Arch::PAGE_OFFSET_MASK == 0xFFF);
assert!(RiscV64Sv57Arch::PAGE_ADDRESS_SHIFT == 57);
assert!(RiscV64Sv57Arch::PAGE_ADDRESS_SIZE == 0x0200_0000_0000_0000);
assert!(RiscV64Sv57Arch::PAGE_ADDRESS_MASK == 0x01FF_FFFF_FFFF_F000);
assert!(RiscV64Sv57Arch::PAGE_ENTRY_SIZE == 8);
assert!(RiscV64Sv57Arch::PAGE_ENTRIES == 512);
assert!(RiscV64Sv57Arch::PAGE_ENTRY_MASK == 0x1FF);
assert!(RiscV64Sv57Arch::PAGE_NEGATIVE_MASK == 0xFE00_0000_0000_0000);
assert!(RiscV64Sv57Arch::ENTRY_ADDRESS_SIZE == 0x0000_1000_0000_0000);
assert!(RiscV64Sv57Arch::ENTRY_ADDRESS_MASK == 0x0000_0FFF_FFFF_FFFF);
assert!(RiscV64Sv57Arch::ENTRY_FLAGS_MASK == 0xFFC0_0000_0000_03FF);
assert!(RiscV64Sv57Arch::PHYS_OFFSET == 0xFF00_0000_0000_0000);
};
#[cfg(test)]
mod tests {
use super::RiscV64Sv57Arch;
use crate::Arch;
#[test]
fn is_canonical() {
use super::VirtualAddress;
fn yes(address: usize) {
assert!(RiscV64Sv57Arch::virt_is_valid(VirtualAddress::new(address)));
}
fn no(address: usize) {
assert!(!RiscV64Sv57Arch::virt_is_valid(VirtualAddress::new(
address
)));
}
yes(0xFF00_0000_1337_1337);
yes(0xFFFF_FFFF_FFFF_FFFF);
yes(0x0000_0000_0000_0042);
yes(0x00FF_FFFF_FFFF_FFFF);
no(0x1337_0000_0000_0000);
no(0x1337_8000_0000_0000);
no(0x0F00_0000_0000_0000);
}
}
-80
View File
@@ -1,80 +0,0 @@
//TODO: USE PAE
use core::arch::asm;
use crate::{Arch, PhysicalAddress, TableKind, VirtualAddress};
#[derive(Clone, Copy)]
pub struct X86Arch;
impl Arch for X86Arch {
const KERNEL_SEPARATE_TABLE: bool = false;
const PAGE_SHIFT: usize = 12; // 4096 bytes
const PAGE_ENTRY_SHIFT: usize = 10; // 1024 entries, 4 bytes each
const PAGE_LEVELS: usize = 2; // PD, PT
const ENTRY_ADDRESS_WIDTH: usize = 20;
const ENTRY_FLAG_DEFAULT_PAGE: usize = Self::ENTRY_FLAG_PRESENT;
const ENTRY_FLAG_DEFAULT_TABLE: usize = Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_READWRITE;
const ENTRY_FLAG_PRESENT: usize = 1 << 0;
const ENTRY_FLAG_READONLY: usize = 0;
const ENTRY_FLAG_READWRITE: usize = 1 << 1;
const ENTRY_FLAG_PAGE_USER: usize = 1 << 2;
// Not used: const ENTRY_FLAG_HUGE: usize = 1 << 7;
const ENTRY_FLAG_GLOBAL: usize = 1 << 8;
const ENTRY_FLAG_NO_GLOBAL: usize = 0;
const ENTRY_FLAG_NO_EXEC: usize = 0; // NOT AVAILABLE UNLESS PAE IS USED!
const ENTRY_FLAG_EXEC: usize = 0;
const ENTRY_FLAG_DEVICE_MEMORY: usize = PAT_UC_;
const ENTRY_FLAG_UNCACHEABLE: usize = PAT_UC_;
const ENTRY_FLAG_WRITE_COMBINING: usize = PAT_WC;
const PHYS_OFFSET: usize = 0x8000_0000;
#[inline(always)]
fn invalidate(address: VirtualAddress) {
unsafe { asm!("invlpg [{0}]", in(reg) address.data()) };
}
#[inline(always)]
fn invalidate_all() {
unsafe { Self::set_table(TableKind::User, Self::table(TableKind::User)) };
}
#[inline(always)]
fn table(_table_kind: TableKind) -> PhysicalAddress {
let address: usize;
unsafe { asm!("mov {0}, cr3", out(reg) address) };
PhysicalAddress::new(address)
}
#[inline(always)]
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
unsafe { asm!("mov cr3, {0}", in(reg) address.data()) };
}
fn virt_is_valid(_address: VirtualAddress) -> bool {
// On 32-bit x86, every virtual address is valid
true
}
}
pub use super::x86_shared::*;
const _: () = {
assert!(X86Arch::PAGE_SIZE == 4096);
assert!(X86Arch::PAGE_OFFSET_MASK == 0xFFF);
assert!(X86Arch::PAGE_ADDRESS_SHIFT == 32);
assert!(X86Arch::PAGE_ADDRESS_SIZE == 0x0000_0001_0000_0000);
assert!(X86Arch::PAGE_ADDRESS_MASK == 0xFFFF_F000);
assert!(X86Arch::PAGE_ENTRY_SIZE == 4);
assert!(X86Arch::PAGE_ENTRIES == 1024);
assert!(X86Arch::PAGE_ENTRY_MASK == 0x3FF);
assert!(X86Arch::PAGE_NEGATIVE_MASK == 0x0000_0000_0000);
assert!(X86Arch::ENTRY_ADDRESS_SIZE == 0x0000_0000_0010_0000);
assert!(X86Arch::ENTRY_ADDRESS_MASK == 0x000F_FFFF);
assert!(X86Arch::ENTRY_FLAGS_MASK == 0x0000_0FFF);
assert!(X86Arch::PHYS_OFFSET == 0x8000_0000);
};
-107
View File
@@ -1,107 +0,0 @@
use core::arch::asm;
use crate::{Arch, PhysicalAddress, TableKind, VirtualAddress};
#[derive(Clone, Copy, Debug)]
pub struct X8664Arch;
impl Arch for X8664Arch {
const KERNEL_SEPARATE_TABLE: bool = false;
const PAGE_SHIFT: usize = 12; // 4096 bytes
const PAGE_ENTRY_SHIFT: usize = 9; // 512 entries, 8 bytes each
const PAGE_LEVELS: usize = 4; // PML4, PDP, PD, PT
const ENTRY_ADDRESS_WIDTH: usize = 40;
const ENTRY_FLAG_DEFAULT_PAGE: usize = Self::ENTRY_FLAG_PRESENT;
const ENTRY_FLAG_DEFAULT_TABLE: usize = Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_READWRITE;
const ENTRY_FLAG_PRESENT: usize = 1 << 0;
const ENTRY_FLAG_READONLY: usize = 0;
const ENTRY_FLAG_READWRITE: usize = 1 << 1;
const ENTRY_FLAG_PAGE_USER: usize = 1 << 2;
// Not used: const ENTRY_FLAG_HUGE: usize = 1 << 7;
const ENTRY_FLAG_GLOBAL: usize = 1 << 8;
const ENTRY_FLAG_NO_GLOBAL: usize = 0;
const ENTRY_FLAG_NO_EXEC: usize = 1 << 63;
const ENTRY_FLAG_EXEC: usize = 0;
const ENTRY_FLAG_DEVICE_MEMORY: usize = PAT_UC_;
const ENTRY_FLAG_UNCACHEABLE: usize = PAT_UC_;
const ENTRY_FLAG_WRITE_COMBINING: usize = PAT_WC;
const PHYS_OFFSET: usize = Self::PAGE_NEGATIVE_MASK + (Self::PAGE_ADDRESS_SIZE >> 1) as usize; // PML4 slot 256 and onwards
#[inline(always)]
fn invalidate(address: VirtualAddress) {
unsafe { asm!("invlpg [{0}]", in(reg) address.data()) };
}
#[inline(always)]
fn invalidate_all() {
unsafe { Self::set_table(TableKind::User, Self::table(TableKind::User)) };
}
#[inline(always)]
fn table(_table_kind: TableKind) -> PhysicalAddress {
let address: usize;
unsafe { asm!("mov {0}, cr3", out(reg) address) };
PhysicalAddress::new(address)
}
#[inline(always)]
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
unsafe { asm!("mov cr3, {0}", in(reg) address.data()) };
}
fn virt_is_valid(address: VirtualAddress) -> bool {
// On x86_64, an address is valid if and only if it is canonical. It may still point to
// unmapped memory, but will always be valid once translated via the page table has
// suceeded.
let masked = address.data() & 0xFFFF_8000_0000_0000;
// TODO: 5-level paging
masked == 0xFFFF_8000_0000_0000 || masked == 0
}
}
pub use super::x86_shared::*;
const _: () = {
assert!(X8664Arch::PAGE_SIZE == 4096);
assert!(X8664Arch::PAGE_OFFSET_MASK == 0xFFF);
assert!(X8664Arch::PAGE_ADDRESS_SHIFT == 48);
assert!(X8664Arch::PAGE_ADDRESS_SIZE == 0x0001_0000_0000_0000);
assert!(X8664Arch::PAGE_ADDRESS_MASK == 0x0000_FFFF_FFFF_F000);
assert!(X8664Arch::PAGE_ENTRY_SIZE == 8);
assert!(X8664Arch::PAGE_ENTRIES == 512);
assert!(X8664Arch::PAGE_ENTRY_MASK == 0x1FF);
assert!(X8664Arch::PAGE_NEGATIVE_MASK == 0xFFFF_0000_0000_0000);
assert!(X8664Arch::ENTRY_ADDRESS_SIZE == 0x0000_0100_0000_0000);
assert!(X8664Arch::ENTRY_ADDRESS_MASK == 0x0000_00FF_FFFF_FFFF);
assert!(X8664Arch::ENTRY_FLAGS_MASK == 0xFFF0_0000_0000_0FFF);
assert!(X8664Arch::PHYS_OFFSET == 0xFFFF_8000_0000_0000);
};
#[cfg(test)]
mod tests {
use super::{VirtualAddress, X8664Arch};
use crate::Arch;
#[test]
fn is_canonical() {
fn yes(address: usize) {
assert!(X8664Arch::virt_is_valid(VirtualAddress::new(address)));
}
fn no(address: usize) {
assert!(!X8664Arch::virt_is_valid(VirtualAddress::new(address)));
}
yes(0xFFFF_8000_1337_1337);
yes(0xFFFF_FFFF_FFFF_FFFF);
yes(0x0000_0000_0000_0042);
yes(0x0000_7FFF_FFFF_FFFF);
no(0x1337_0000_0000_0000);
no(0x1337_8000_0000_0000);
no(0x0000_8000_0000_0000);
}
}
-37
View File
@@ -1,37 +0,0 @@
#![expect(clippy::identity_op)]
// Page attribute table is indexed by PAT(7) PCD(4) PWT(3)
pub(crate) const _PAT_WB: usize = (0b0 << 7) + (0b00 << 3);
pub(crate) const _PAT_WT: usize = (0b0 << 7) + (0b01 << 3);
pub(crate) const PAT_UC_: usize = (0b0 << 7) + (0b10 << 3); // UC-
pub(crate) const _PAT_UC: usize = (0b0 << 7) + (0b11 << 3); // UC
pub(crate) const PAT_WC: usize = (0b1 << 7) + (0b00 << 3);
/// Setup page attribute table
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[inline(always)]
pub unsafe fn init_pat() {
unsafe {
let uncacheable = 0; // UC
let write_combining = 1; // WC
let write_through = 4; // WT
let _write_protected = 5; // WP
let write_back = 6; // WB
let uncached = 7; // UC- (overridable by WC MTRR)
let pat0 = write_back;
let pat1 = write_through;
let pat2 = uncached;
let pat3 = uncacheable;
let pat4 = write_combining;
let pat5 = pat1;
let pat6 = pat2;
let pat7 = pat3;
let msr = 631; // IA32_PAT
let low = u32::from_be_bytes([pat3, pat2, pat1, pat0]);
let high = u32::from_be_bytes([pat7, pat6, pat5, pat4]);
core::arch::asm!("wrmsr", in("ecx") msr, in("eax") low, in("edx") high);
}
}
-97
View File
@@ -1,97 +0,0 @@
#![no_std]
#![allow(clippy::new_without_default)]
pub use crate::{allocator::*, arch::*, page::*};
mod allocator;
mod arch;
mod page;
pub const KILOBYTE: usize = 1024;
pub const MEGABYTE: usize = KILOBYTE * 1024;
pub const GIGABYTE: usize = MEGABYTE * 1024;
#[cfg(target_pointer_width = "64")]
pub const TERABYTE: usize = GIGABYTE * 1024;
/// Specific table to be used, needed on some architectures
//TODO: Use this throughout the code
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum TableKind {
/// Userspace page table
User,
/// Kernel page table
Kernel,
}
/// Physical memory address
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct PhysicalAddress(usize);
impl PhysicalAddress {
#[inline(always)]
pub const fn new(address: usize) -> Self {
Self(address)
}
#[inline(always)]
pub fn data(&self) -> usize {
self.0
}
#[expect(clippy::should_implement_trait)]
#[inline(always)]
pub fn add(self, offset: usize) -> Self {
Self(self.0 + offset)
}
}
impl core::fmt::Debug for PhysicalAddress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "[phys {:#0x}]", self.data())
}
}
/// Virtual memory address
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct VirtualAddress(usize);
impl VirtualAddress {
#[inline(always)]
pub const fn new(address: usize) -> Self {
Self(address)
}
#[inline(always)]
pub fn data(&self) -> usize {
self.0
}
#[expect(clippy::should_implement_trait)]
#[inline(always)]
pub fn add(self, offset: usize) -> Self {
Self(self.0 + offset)
}
#[inline(always)]
pub fn kind(&self) -> TableKind {
if (self.0 as isize) < 0 {
TableKind::Kernel
} else {
TableKind::User
}
}
}
impl core::fmt::Debug for VirtualAddress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "[virt {:#0x}]", self.data())
}
}
#[derive(Clone, Copy, Debug)]
pub struct MemoryArea {
pub base: PhysicalAddress,
pub size: usize,
}
-309
View File
@@ -1,309 +0,0 @@
#![cfg(target_pointer_width = "64")]
use rmm::{
emulate::EmulateArch, Arch, BuddyAllocator, BumpAllocator, Flusher, FrameAllocator, FrameCount,
MemoryArea, PageFlags, PageFlushAll, PageMapper, PageTable, PhysicalAddress, TableKind,
VirtualAddress, GIGABYTE, KILOBYTE, MEGABYTE, TERABYTE,
};
use std::marker::PhantomData;
pub fn format_size(size: usize) -> String {
if size >= 2 * TERABYTE {
format!("{} TB", size / TERABYTE)
} else if size >= 2 * GIGABYTE {
format!("{} GB", size / GIGABYTE)
} else if size >= 2 * MEGABYTE {
format!("{} MB", size / MEGABYTE)
} else if size >= 2 * KILOBYTE {
format!("{} KB", size / KILOBYTE)
} else {
format!("{} B", size)
}
}
#[allow(dead_code)]
unsafe fn dump_tables<A: Arch>(table: PageTable<A>) {
unsafe {
let level = table.level();
for i in 0..A::PAGE_ENTRIES {
if level == 0 {
if let Some(entry) = table.entry(i) {
if entry.present() {
let base = table.entry_base(i).unwrap();
println!(
"0x{:X}: 0x{:X}",
base.data(),
entry.address().unwrap().data()
);
}
}
} else {
if let Some(next) = table.next(i) {
dump_tables(next);
}
}
}
}
}
pub struct SlabNode<A> {
next: PhysicalAddress,
count: usize,
phantom: PhantomData<A>,
}
impl<A: Arch> SlabNode<A> {
pub fn new(next: PhysicalAddress, count: usize) -> Self {
Self {
next,
count,
phantom: PhantomData,
}
}
pub fn empty() -> Self {
Self::new(PhysicalAddress::new(0), 0)
}
pub unsafe fn insert(&mut self, phys: PhysicalAddress) {
unsafe {
let virt = A::phys_to_virt(phys);
A::write(virt, self.next);
self.next = phys;
self.count += 1;
}
}
pub unsafe fn remove(&mut self) -> Option<PhysicalAddress> {
unsafe {
if self.count > 0 {
let phys = self.next;
let virt = A::phys_to_virt(phys);
self.next = A::read(virt);
self.count -= 1;
Some(phys)
} else {
None
}
}
}
}
pub struct SlabAllocator<A> {
//TODO: Allow allocations up to maximum pageable size
nodes: [SlabNode<A>; 4],
phantom: PhantomData<A>,
}
impl<A: Arch> SlabAllocator<A> {
pub unsafe fn new(areas: &'static [MemoryArea], offset: usize) -> Self {
unsafe {
let mut allocator = Self {
nodes: [
SlabNode::empty(),
SlabNode::empty(),
SlabNode::empty(),
SlabNode::empty(),
],
phantom: PhantomData,
};
// Add unused areas to free lists
let mut area_offset = offset;
for area in areas.iter() {
if area_offset < area.size {
let area_base = area.base.add(area_offset);
let area_size = area.size - area_offset;
allocator.free(area_base, area_size);
area_offset = 0;
} else {
area_offset -= area.size;
}
}
allocator
}
}
pub unsafe fn allocate(&mut self, size: usize) -> Option<PhysicalAddress> {
unsafe {
for level in 0..A::PAGE_LEVELS - 1 {
let level_shift = level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT;
let level_size = 1 << level_shift;
if size <= level_size {
if let Some(base) = self.nodes[level].remove() {
self.free(base.add(size), level_size - size);
return Some(base);
}
}
}
None
}
}
//TODO: This causes fragmentation, since neighbors are not identified
//TODO: remainders less than PAGE_SIZE will be lost
pub unsafe fn free(&mut self, mut base: PhysicalAddress, mut size: usize) {
unsafe {
for level in (0..A::PAGE_LEVELS - 1).rev() {
let level_shift = level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT;
let level_size = 1 << level_shift;
while size >= level_size {
println!("Add {:X} {}", base.data(), format_size(level_size));
self.nodes[level].insert(base);
base = base.add(level_size);
size -= level_size;
}
}
}
}
pub unsafe fn remaining(&mut self) -> usize {
let mut remaining = 0;
for level in (0..A::PAGE_LEVELS - 1).rev() {
let level_shift = level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT;
let level_size = 1 << level_shift;
remaining += self.nodes[level].count * level_size;
}
remaining
}
}
unsafe fn new_tables<A: Arch>(areas: &'static [MemoryArea]) {
unsafe {
// First, calculate how much memory we have
let mut size = 0;
for area in areas.iter() {
size += area.size;
}
println!("Memory: {}", format_size(size));
// Create a basic allocator for the first pages
let mut bump_allocator = BumpAllocator::<A>::new(areas, 0);
{
// Map all physical areas at PHYS_OFFSET
let mut mapper = PageMapper::<A, _>::create(TableKind::Kernel, &mut bump_allocator)
.expect("failed to create Mapper");
for area in areas.iter() {
for i in 0..area.size / A::PAGE_SIZE {
let phys = area.base.add(i * A::PAGE_SIZE);
let (_, flush) = mapper
.map_linearly(phys, PageFlags::<A>::new().write(true))
.expect("failed to map page to frame");
flush.ignore(); // Not the active table
}
}
// Use the new table
mapper.make_current();
}
// Create the physical memory map
let offset = bump_allocator.offset();
println!("Permanently used: {}", format_size(offset));
let mut allocator = BuddyAllocator::<A>::new(bump_allocator).unwrap();
for i in 0..16 {
{
let phys_opt = allocator.allocate_one();
println!("page {}: {:X?}", i, phys_opt);
if i % 3 == 0 {
if let Some(phys) = phys_opt {
println!("free {}: {:X?}", i, phys_opt);
allocator.free_one(phys);
}
}
}
{
let phys_opt = allocator.allocate(FrameCount::new(16));
println!("page*16 {}: {:X?}", i, phys_opt);
if i % 2 == 0 {
if let Some(phys) = phys_opt {
println!("free*16 {}: {:X?}", i, phys_opt);
allocator.free(phys, FrameCount::new(16));
}
}
}
}
let mut mapper = PageMapper::<A, _>::current(TableKind::Kernel, &mut allocator);
let mut flush_all = PageFlushAll::new();
for i in 0..16 {
let virt = VirtualAddress::new(MEGABYTE + i * A::PAGE_SIZE);
let phys = mapper
.allocator_mut()
.allocate_one()
.expect("failed to map page");
let flush = mapper
.map_phys(virt, phys, PageFlags::<A>::new().user(true).write(true))
.expect("failed to map page");
flush_all.consume(flush);
}
flush_all.flush();
let mut flush_all = PageFlushAll::new();
for i in 0..16 {
let virt = VirtualAddress::new(MEGABYTE + i * A::PAGE_SIZE);
let (old, _, flush) = mapper.unmap_phys(virt).expect("failed to unmap page");
mapper.allocator_mut().free_one(old);
flush_all.consume(flush);
}
flush_all.flush();
let usage = allocator.usage();
println!("Allocator usage:");
println!(
" Used: {}",
format_size(usage.used().data() * A::PAGE_SIZE)
);
println!(
" Free: {}",
format_size(usage.free().data() * A::PAGE_SIZE)
);
println!(
" Total: {}",
format_size(usage.total().data() * A::PAGE_SIZE)
);
}
}
fn main() {
unsafe {
let areas = EmulateArch::init();
// Debug table
//dump_tables(PageTable::<A>::top());
new_tables::<EmulateArch>(areas);
//dump_tables(PageTable::<A>::top());
for i in &[1, 2, 4, 8, 16, 32] {
let phys = PhysicalAddress::new(i * MEGABYTE);
let virt = EmulateArch::phys_to_virt(phys);
// Test read
println!(
"0x{:X} (0x{:X}) = 0x{:X}",
virt.data(),
phys.data(),
EmulateArch::read::<u8>(virt)
);
// Test write
EmulateArch::write::<u8>(virt, 0x5A);
// Test read
println!(
"0x{:X} (0x{:X}) = 0x{:X}",
virt.data(),
phys.data(),
EmulateArch::read::<u8>(virt)
);
}
}
}
-59
View File
@@ -1,59 +0,0 @@
use core::marker::PhantomData;
use crate::{Arch, PageFlags, PhysicalAddress};
#[derive(Clone, Copy, Debug)]
pub struct PageEntry<A> {
data: usize,
phantom: PhantomData<A>,
}
impl<A: Arch> PageEntry<A> {
#[inline(always)]
pub fn new(address: usize, flags: usize) -> Self {
let data = (((address >> A::PAGE_SHIFT) & A::ENTRY_ADDRESS_MASK) << A::ENTRY_ADDRESS_SHIFT)
| flags;
Self::from_data(data)
}
#[inline(always)]
pub fn from_data(data: usize) -> Self {
Self {
data,
phantom: PhantomData,
}
}
#[inline(always)]
pub fn data(&self) -> usize {
self.data
}
#[inline(always)]
pub fn address(&self) -> Result<PhysicalAddress, PhysicalAddress> {
let addr = PhysicalAddress(
((self.data >> A::ENTRY_ADDRESS_SHIFT) & A::ENTRY_ADDRESS_MASK) << A::PAGE_SHIFT,
);
if self.present() {
Ok(addr)
} else {
Err(addr)
}
}
#[inline(always)]
pub fn flags(&self) -> PageFlags<A> {
unsafe { PageFlags::from_data(self.data & A::ENTRY_FLAGS_MASK) }
}
#[inline(always)]
pub fn set_flags(&mut self, flags: PageFlags<A>) {
self.data &= !A::ENTRY_FLAGS_MASK;
self.data |= flags.data();
}
#[inline(always)]
pub fn present(&self) -> bool {
self.data & A::ENTRY_FLAG_PRESENT != 0
}
}
-157
View File
@@ -1,157 +0,0 @@
use core::{fmt, marker::PhantomData};
use crate::Arch;
#[derive(Clone, Copy)]
pub struct PageFlags<A> {
data: usize,
arch: PhantomData<A>,
}
impl<A: Arch> PageFlags<A> {
#[inline(always)]
pub fn new() -> Self {
unsafe {
Self::from_data(
// Flags set to present, kernel space, read-only, no-execute by default
A::ENTRY_FLAG_DEFAULT_PAGE
| A::ENTRY_FLAG_READONLY
| A::ENTRY_FLAG_NO_EXEC
| A::ENTRY_FLAG_NO_GLOBAL,
)
}
}
#[inline(always)]
pub fn new_table() -> Self {
unsafe {
Self::from_data(
// Flags set to present, kernel space, read-only, no-execute by default
A::ENTRY_FLAG_DEFAULT_TABLE | A::ENTRY_FLAG_NO_EXEC | A::ENTRY_FLAG_NO_GLOBAL,
)
}
}
#[inline(always)]
pub unsafe fn from_data(data: usize) -> Self {
Self {
data,
arch: PhantomData,
}
}
#[inline(always)]
pub fn data(&self) -> usize {
self.data
}
#[must_use]
#[inline(always)]
pub fn custom_flag(mut self, flag: usize, value: bool) -> Self {
if value {
self.data |= flag;
} else {
self.data &= !flag;
}
self
}
#[must_use]
#[inline(always)]
pub fn device_memory(self, value: bool) -> Self {
self.custom_flag(A::ENTRY_FLAG_DEVICE_MEMORY, value)
}
#[must_use]
#[inline(always)]
pub fn uncacheable(self, value: bool) -> Self {
self.custom_flag(A::ENTRY_FLAG_UNCACHEABLE, value)
}
#[must_use]
#[inline(always)]
pub fn write_combining(self, value: bool) -> Self {
self.custom_flag(A::ENTRY_FLAG_WRITE_COMBINING, value)
}
#[inline(always)]
pub fn has_flag(&self, flag: usize) -> bool {
self.data & flag == flag
}
#[inline(always)]
pub fn has_present(&self) -> bool {
self.has_flag(A::ENTRY_FLAG_PRESENT)
}
#[must_use]
#[inline(always)]
pub fn user(self, value: bool) -> Self {
self.custom_flag(A::ENTRY_FLAG_PAGE_USER, value)
}
#[inline(always)]
pub fn has_user(&self) -> bool {
self.has_flag(A::ENTRY_FLAG_PAGE_USER)
}
#[must_use]
#[inline(always)]
pub fn write(self, value: bool) -> Self {
// Architecture may use readonly or readwrite, or both, support either
if value {
self.custom_flag(A::ENTRY_FLAG_READONLY | A::ENTRY_FLAG_READWRITE, false)
.custom_flag(A::ENTRY_FLAG_READWRITE, true)
} else {
self.custom_flag(A::ENTRY_FLAG_READONLY | A::ENTRY_FLAG_READWRITE, false)
.custom_flag(A::ENTRY_FLAG_READONLY, true)
}
}
#[inline(always)]
pub fn has_write(&self) -> bool {
// Architecture may use readonly or readwrite, or both, support either
self.data & (A::ENTRY_FLAG_READONLY | A::ENTRY_FLAG_READWRITE) == A::ENTRY_FLAG_READWRITE
}
#[must_use]
#[inline(always)]
pub fn execute(self, value: bool) -> Self {
//TODO: write xor execute?
// Architecture may use no exec or exec, support either
self.custom_flag(A::ENTRY_FLAG_NO_EXEC, !value)
.custom_flag(A::ENTRY_FLAG_EXEC, value)
}
#[inline(always)]
pub fn has_execute(&self) -> bool {
// Architecture may use no exec or exec, support either
self.data & (A::ENTRY_FLAG_NO_EXEC | A::ENTRY_FLAG_EXEC) == A::ENTRY_FLAG_EXEC
}
#[must_use]
#[inline(always)]
pub fn global(self, value: bool) -> Self {
// Architecture may use global or non global, support either
self.custom_flag(A::ENTRY_FLAG_NO_GLOBAL, !value)
.custom_flag(A::ENTRY_FLAG_GLOBAL, value)
}
#[inline(always)]
pub fn is_global(&self) -> bool {
// Architecture may use global or non global, support either
self.data & (A::ENTRY_FLAG_GLOBAL | A::ENTRY_FLAG_NO_GLOBAL) == A::ENTRY_FLAG_GLOBAL
}
}
impl<A: Arch> fmt::Debug for PageFlags<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PageFlags")
.field("present", &self.has_present())
.field("write", &self.has_write())
.field("executable", &self.has_execute())
.field("user", &self.has_user())
.field("bits", &format_args!("{:#0x}", self.data))
.finish()
}
}
-71
View File
@@ -1,71 +0,0 @@
use core::{marker::PhantomData, mem};
use crate::{Arch, VirtualAddress};
pub trait Flusher<A> {
fn consume(&mut self, flush: PageFlush<A>);
}
#[must_use = "The page table must be flushed, or the changes unsafely ignored"]
pub struct PageFlush<A> {
virt: VirtualAddress,
phantom: PhantomData<A>,
}
impl<A: Arch> PageFlush<A> {
pub fn new(virt: VirtualAddress) -> Self {
Self {
virt,
phantom: PhantomData,
}
}
pub fn flush(self) {
A::invalidate(self.virt);
}
#[expect(clippy::forget_non_drop)]
pub unsafe fn ignore(self) {
mem::forget(self);
}
}
// TODO: Might remove Drop and add #[must_use] again, but ergonomically I prefer being able to pass
// a flusher, and have it dropped by the end of the function it is passed to, in order to flush.
pub struct PageFlushAll<A: Arch> {
phantom: PhantomData<fn() -> A>,
}
impl<A: Arch> PageFlushAll<A> {
pub fn new() -> Self {
Self {
phantom: PhantomData,
}
}
pub fn flush(self) {}
pub unsafe fn ignore(self) {
mem::forget(self);
}
}
impl<A: Arch> Drop for PageFlushAll<A> {
fn drop(&mut self) {
A::invalidate_all();
}
}
impl<A: Arch> Flusher<A> for PageFlushAll<A> {
fn consume(&mut self, flush: PageFlush<A>) {
unsafe {
flush.ignore();
}
}
}
impl<A: Arch, T: Flusher<A> + ?Sized> Flusher<A> for &mut T {
fn consume(&mut self, flush: PageFlush<A>) {
<T as Flusher<A>>::consume(self, flush)
}
}
impl<A: Arch> Flusher<A> for () {
fn consume(&mut self, _: PageFlush<A>) {}
}
-269
View File
@@ -1,269 +0,0 @@
use core::marker::PhantomData;
use crate::{
Arch, FrameAllocator, PageEntry, PageFlags, PageFlush, PageTable, PhysicalAddress, TableKind,
VirtualAddress,
};
pub struct PageMapper<A, F> {
table_kind: TableKind,
table_addr: PhysicalAddress,
allocator: F,
_phantom: PhantomData<fn() -> A>,
}
impl<A: Arch, F> PageMapper<A, F> {
unsafe fn new(table_kind: TableKind, table_addr: PhysicalAddress, allocator: F) -> Self {
Self {
table_kind,
table_addr,
allocator,
_phantom: PhantomData,
}
}
pub unsafe fn current(table_kind: TableKind, allocator: F) -> Self {
unsafe {
let table_addr = A::table(table_kind);
Self::new(table_kind, table_addr, allocator)
}
}
pub fn is_current(&self) -> bool {
self.table().phys() == A::table(self.table_kind)
}
pub unsafe fn make_current(&self) {
unsafe {
A::set_table(self.table_kind, self.table_addr);
}
}
pub fn table(&self) -> PageTable<A> {
// SAFETY: The only way to initialize a PageMapper is via new(), and we assume it upholds
// all necessary invariants for this to be safe.
unsafe { PageTable::new(VirtualAddress::new(0), self.table_addr, A::PAGE_LEVELS - 1) }
}
pub fn allocator(&self) -> &F {
&self.allocator
}
pub fn allocator_mut(&mut self) -> &mut F {
&mut self.allocator
}
fn visit<T>(
&self,
virt: VirtualAddress,
f: impl FnOnce(&mut PageTable<A>, usize) -> T,
) -> Option<T> {
let mut table = self.table();
loop {
let i = table.index_of(virt)?;
if table.level() == 0 {
return Some(f(&mut table, i));
} else {
table = unsafe { table.next(i)? };
}
}
}
pub fn translate(&self, virt: VirtualAddress) -> Option<(PhysicalAddress, PageFlags<A>)> {
let entry = self.visit(virt, |p1, i| unsafe { p1.entry(i) })??;
Some((entry.address().ok()?, entry.flags()))
}
pub unsafe fn remap_with_full(
&mut self,
virt: VirtualAddress,
f: impl FnOnce(PhysicalAddress, PageFlags<A>) -> Option<(PhysicalAddress, PageFlags<A>)>,
) -> Option<(PageFlags<A>, PhysicalAddress, PageFlush<A>)> {
unsafe {
self.visit(virt, |p1, i| {
let old_entry = p1.entry(i)?;
let old_phys = old_entry.address().ok()?;
let old_flags = old_entry.flags();
let (new_phys, new_flags) = f(old_phys, old_flags)?;
// TODO: Higher-level PageEntry::new interface?
let new_entry = PageEntry::new(new_phys.data(), new_flags.data());
p1.set_entry(i, new_entry);
Some((old_flags, old_phys, PageFlush::new(virt)))
})
.flatten()
}
}
pub unsafe fn remap_with(
&mut self,
virt: VirtualAddress,
map_flags: impl FnOnce(PageFlags<A>) -> PageFlags<A>,
) -> Option<(PageFlags<A>, PhysicalAddress, PageFlush<A>)> {
unsafe {
self.remap_with_full(virt, |same_phys, old_flags| {
Some((same_phys, map_flags(old_flags)))
})
}
}
pub unsafe fn remap(
&mut self,
virt: VirtualAddress,
flags: PageFlags<A>,
) -> Option<PageFlush<A>> {
unsafe { self.remap_with(virt, |_| flags).map(|(_, _, flush)| flush) }
}
}
impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
pub unsafe fn create(table_kind: TableKind, mut allocator: F) -> Option<Self> {
unsafe {
let table_addr = allocator.allocate_one()?;
let mut table = Self::new(table_kind, table_addr, allocator);
match (table_kind, A::KERNEL_SEPARATE_TABLE) {
(TableKind::Kernel, false) => {
// Pre-allocate all kernel top-level page table entries so that when
// the page table is copied, these entries are synced between processes.
for i in A::PAGE_ENTRIES / 2..A::PAGE_ENTRIES {
let phys = table
.allocator
.allocate_one()
.expect("failed to map page table");
let flags = A::ENTRY_FLAG_DEFAULT_TABLE;
table
.table()
.set_entry(i, PageEntry::new(phys.data(), flags));
}
}
(TableKind::User, false) => {
// Copy higher half (kernel) mappings
let active_ktable = PageMapper::current(TableKind::Kernel, ());
for i in A::PAGE_ENTRIES / 2..A::PAGE_ENTRIES {
if let Some(entry) = active_ktable.table().entry(i) {
table.table().set_entry(i, entry);
}
}
}
(_, true) => {
// There is a separate page table for the kernel. No need to copy the kernel
// mappings to the user page table.
}
}
Some(table)
}
}
pub unsafe fn map_phys(
&mut self,
virt: VirtualAddress,
phys: PhysicalAddress,
flags: PageFlags<A>,
) -> Option<PageFlush<A>> {
unsafe {
//TODO: verify virt and phys are aligned
//TODO: verify flags have correct bits
let entry = PageEntry::new(phys.data(), flags.data());
let mut table = self.table();
loop {
let i = table.index_of(virt)?;
if table.level() == 0 {
//TODO: check for overwriting entry
table.set_entry(i, entry);
return Some(PageFlush::new(virt));
}
let next = match table.next(i) {
Some(some) => some,
None => {
let next_phys = self.allocator.allocate_one()?;
//TODO: correct flags?
let flags = A::ENTRY_FLAG_DEFAULT_TABLE
| if virt.kind() == TableKind::User {
A::ENTRY_FLAG_TABLE_USER
} else {
0
};
table.set_entry(i, PageEntry::new(next_phys.data(), flags));
table.next(i)?
}
};
table = next;
}
}
}
pub unsafe fn map_linearly(
&mut self,
phys: PhysicalAddress,
flags: PageFlags<A>,
) -> Option<(VirtualAddress, PageFlush<A>)> {
unsafe {
let virt = A::phys_to_virt(phys);
self.map_phys(virt, phys, flags).map(|flush| (virt, flush))
}
}
pub unsafe fn unmap_phys(
&mut self,
virt: VirtualAddress,
) -> Option<(PhysicalAddress, PageFlags<A>, PageFlush<A>)> {
//TODO: verify virt is aligned
let mut table = self.table();
let unmap_parents = A::KERNEL_SEPARATE_TABLE || table.index_of(virt)? < A::PAGE_ENTRIES / 2; // Is a userspace mapping
unsafe {
unmap_phys_inner(virt, &mut table, unmap_parents, &mut self.allocator)
.map(|(pa, pf)| (pa, pf, PageFlush::new(virt)))
}
}
}
unsafe fn unmap_phys_inner<A: Arch>(
virt: VirtualAddress,
table: &mut PageTable<A>,
unmap_parents: bool,
allocator: &mut impl FrameAllocator,
) -> Option<(PhysicalAddress, PageFlags<A>)> {
unsafe {
let i = table.index_of(virt)?;
if table.level() == 0 {
let entry_opt = table.entry(i);
table.set_entry(i, PageEntry::new(0, 0));
let entry = entry_opt?;
return Some((entry.address().ok()?, entry.flags()));
}
let mut subtable = table.next(i)?;
let res = unmap_phys_inner(virt, &mut subtable, unmap_parents, allocator)?;
if unmap_parents {
// TODO: Use a counter? This would reduce the remaining number of available bits, but could be
// faster (benchmark is needed).
let is_still_populated = (0..A::PAGE_ENTRIES)
.map(|j| subtable.entry(j).expect("must be within bounds"))
.any(|e| e.present());
if !is_still_populated {
allocator.free_one(subtable.phys());
table.set_entry(i, PageEntry::new(0, 0));
}
}
Some(res)
}
}
impl<A, F: core::fmt::Debug> core::fmt::Debug for PageMapper<A, F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PageMapper")
.field("frame", &self.table_addr)
.field("allocator", &self.allocator)
.finish()
}
}
-7
View File
@@ -1,7 +0,0 @@
pub use self::{entry::*, flags::*, flush::*, mapper::*, table::*};
mod entry;
mod flags;
mod flush;
mod mapper;
mod table;
-105
View File
@@ -1,105 +0,0 @@
use core::{fmt, marker::PhantomData};
use crate::{page::PageEntry, Arch, PhysicalAddress, VirtualAddress};
pub struct PageTable<A> {
base: VirtualAddress,
phys: PhysicalAddress,
level: usize,
phantom: PhantomData<A>,
}
impl<A: Arch> PageTable<A> {
pub(super) unsafe fn new(base: VirtualAddress, phys: PhysicalAddress, level: usize) -> Self {
Self {
base,
phys,
level,
phantom: PhantomData,
}
}
pub fn base(&self) -> VirtualAddress {
self.base
}
pub fn phys(&self) -> PhysicalAddress {
self.phys
}
pub fn level(&self) -> usize {
self.level
}
pub fn entry_base(&self, i: usize) -> Option<VirtualAddress> {
if i < A::PAGE_ENTRIES {
let level_shift = self.level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT;
Some(self.base.add(i << level_shift))
} else {
None
}
}
unsafe fn entry_virt(&self, i: usize) -> Option<VirtualAddress> {
if i < A::PAGE_ENTRIES {
Some(A::phys_to_virt(self.phys).add(i * A::PAGE_ENTRY_SIZE))
} else {
None
}
}
pub unsafe fn entry(&self, i: usize) -> Option<PageEntry<A>> {
unsafe {
let addr = self.entry_virt(i)?;
Some(PageEntry::from_data(A::read::<usize>(addr)))
}
}
pub(super) unsafe fn set_entry(&mut self, i: usize, entry: PageEntry<A>) -> Option<()> {
unsafe {
let addr = self.entry_virt(i)?;
A::write::<usize>(addr, entry.data());
Some(())
}
}
pub(super) fn index_of(&self, address: VirtualAddress) -> Option<usize> {
// Canonicalize address first
let address = VirtualAddress::new(address.data() & A::PAGE_ADDRESS_MASK);
let level_shift = self.level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT;
// Intentionally wraps around at last-level table to get all-ones mask on architectures
// where addressable physical address space covers entire usized space (e.g. x86)
let level_mask = A::PAGE_ENTRIES
.wrapping_shl(level_shift as u32)
.wrapping_sub(1);
if address >= self.base && address <= self.base.add(level_mask) {
Some((address.data() >> level_shift) & A::PAGE_ENTRY_MASK)
} else {
None
}
}
pub unsafe fn next(&self, i: usize) -> Option<Self> {
if self.level == 0 {
return None;
}
unsafe {
Some(PageTable::new(
self.entry_base(i)?,
self.entry(i)?.address().ok()?,
self.level - 1,
))
}
}
pub fn debug_entries(&self, f: impl Fn(fmt::Arguments<'_>)) {
for i in 0..A::PAGE_ENTRIES {
if let Some(entry) = unsafe { self.entry(i) }
&& entry.present()
{
f(format_args!("{}: {:X}", i, entry.data()));
}
}
}
}
-3
View File
@@ -1,3 +0,0 @@
[toolchain]
channel = "nightly-2026-05-24"
components = ["rust-src"]
-22
View File
@@ -1,22 +0,0 @@
blank_lines_lower_bound = 0 # default
blank_lines_upper_bound = 1 # default
brace_style = "SameLineWhere" # default
disable_all_formatting = false # default
edition = "2024"
style_edition = "2015"
empty_item_single_line = true # default
fn_single_line = false # default
force_explicit_abi = true # default
format_strings = false # default
hard_tabs = false # default
show_parse_errors = true # default
imports_granularity = "Crate" # default = Preserve
imports_indent = "Block" # default
imports_layout = "Mixed" # default
indent_style = "Block" # default
max_width = 100 # default
newline_style = "Unix" # default = Auto
skip_children = false # default
tab_spaces = 4 # default
trailing_comma = "Vertical" # default
where_single_line = false # default
-291
View File
@@ -1,291 +0,0 @@
//! ACPI Firmware ACPI Control Structure (FACS) parser.
//!
//! Per ACPI 6.5 §5.2.10. The FACS contains the firmware waking
//! vector that the platform firmware jumps to on S3 wake.
//! This is the memory location the S3 resume trampoline in
//! `arch/x86_shared/s3_resume.rs` must be written to.
//!
//! This is a comprehensive parser matching Linux 7.1's
//! `struct acpi_table_facs` in `include/acpi/actbl.h`:
//!
//! ```c
//! struct acpi_table_facs {
//! char signature[4]; // ASCII table signature
//! u32 length; // Length of structure, in bytes
//! u32 hardware_signature; // Hardware configuration signature
//! u32 firmware_waking_vector; // 32-bit FW waking vector
//! u32 global_lock; // Global Lock for shared hardware
//! u32 flags; // Flags
//! u64 xfirmware_waking_vector; // 64-bit FW waking vector (ACPI 2.0+)
//! u8 version; // Version of this table (ACPI 2.0+)
//! u8 reserved[3]; // Reserved
//! u32 ospm_flags; // Flags set by OSPM (ACPI 4.0+)
//! u8 reserved1[24]; // Reserved (ACPI 4.0+)
//! };
//! ```
//!
//! We also model the corresponding flag constants:
//! - `ACPI_GLOCK_PENDING` (1 << 0)
//! - `ACPI_GLOCK_OWNED` (1 << 1)
//! - `ACPI_FACS_S4_BIOS_PRESENT` (1 << 0)
//! - `ACPI_FACS_64BIT_WAKE` (1 << 1)
//! - `ACPI_FACS_64BIT_ENVIRONMENT` (1 << 0)
//!
//! Hardware-agnostic: the FACS layout is standardized by the ACPI
//! spec. Only the field values (specifically the waking vector)
//! vary per platform.
use core::sync::atomic::AtomicPtr;
use crate::acpi::sdt::Sdt;
/// Linux 7.1 compatibility: matching `struct acpi_table_facs`.
///
/// The struct is `repr(C, packed)` to match the wire layout
/// exactly. The kernel reads the bytes via direct pointer
/// access.
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
pub struct FacsStruct {
/// SDT header (signature, length, revision, checksum,
/// oem_id, oem_table_id, oem_revision, creator_id,
/// creator_revision). Same as other ACPI tables.
pub header: super::sdt::Sdt,
/// Hardware configuration signature. Used by firmware to
/// detect a cold boot vs a resume (the value differs
/// across boots because of system-specific information).
/// Red Bear OS doesn't currently use this — the kernel's
/// S3 magic value is in `s3_resume::S3State.saved_magic`.
pub hardware_signature: u32,
/// 32-bit firmware waking vector. Legacy field used by
/// firmware that runs in 32-bit mode after S3 wake. The
/// platform firmware jumps to this address on a wake
/// event.
pub firmware_waking_vector: u32,
/// Global Lock for shared hardware resources. Acquired by
/// the OS before reading/writing the FACS (or any other
/// ACPI table that requires synchronization with firmware).
/// The kernel currently doesn't use this — we read/write
/// the FACS without taking the global lock. Future work:
/// take the global lock in `read()` and `write()` if
/// multi-core S3 support is added.
pub global_lock: u32,
/// Flags. Bit 0 = S4BIOS support is present. Bit 1 = 64-bit
/// wake vector is supported (ACPI 4.0+).
pub flags: u32,
/// 64-bit firmware waking vector. Used by firmware that
/// runs in 64-bit mode after S3 wake. This is what the
/// kernel's S3 trampoline is written to. (ACPI 2.0+.)
pub xfirmware_waking_vector: u64,
/// FACS version. The FACS was introduced in ACPI 1.0; the
/// 64-bit wake vector was added in ACPI 2.0. (ACPI 2.0+.)
pub version: u8,
/// Reserved. Must be zero. Three bytes.
pub reserved: [u8; 3],
/// OSPM-set flags. Bit 0 = 64-bit wake environment is
/// required (ACPI 4.0+).
pub ospm_flags: u32,
/// Reserved. Must be zero. 24 bytes. (ACPI 4.0+.)
pub reserved1: [u8; 24],
}
/// FACS flag constants (mirrors Linux 7.1's `actbl.h`).
///
/// Used in the `flags` field. Bit 0 = S4BIOS support is
/// present. Bit 1 = 64-bit wake vector is supported.
pub mod facs_flags {
/// `ACPI_FACS_S4_BIOS_PRESENT` (bit 0). The S4BIOS_REQ
/// signal is supported.
pub const S4_BIOS_PRESENT: u32 = 1 << 0;
/// `ACPI_FACS_64BIT_WAKE` (bit 1). The 64-bit wake vector
/// is supported (i.e., `xfirmware_waking_vector` is valid).
pub const WAKE_64BIT: u32 = 1 << 1;
}
/// FACS OSPM flag constants (mirrors Linux 7.1's `actbl.h`).
///
/// Used in the `ospm_flags` field. Bit 0 = 64-bit wake
/// environment is required.
pub mod facs_ospm_flags {
/// `ACPI_FACS_64BIT_ENVIRONMENT` (bit 0). The firmware
/// uses the 64-bit waking vector on S3 wake.
pub const WAKE_64BIT_ENVIRONMENT: u32 = 1 << 0;
}
/// FACS Global Lock flag constants (mirrors Linux 7.1's
/// `actbl.h`). Used in the `global_lock` field.
pub mod facs_glock_flags {
/// `ACPI_GLOCK_PENDING` (bit 0). The global lock is
/// pending (firmware requested it).
pub const PENDING: u32 = 1 << 0;
/// `ACPI_GLOCK_OWNED` (bit 1). The global lock is
/// currently owned.
pub const OWNED: u32 = 1 << 1;
}
/// FACS instance pointer. Set by `init` when the FACS is
/// discovered during ACPI table parsing. Used by the
/// `SetS3WakingVector` AcPiVerb and the `firmware_waking_vector_*`
/// functions below.
static FACS_PTR: AtomicPtr<FacsStruct> = AtomicPtr::new(core::ptr::null_mut());
/// Phase II.X.W: Initialize the FACS parser. Called from the
/// kernel's acpi init after all SDTs are loaded. Reads the
/// FACS from the SDT table and stores the pointer for later
/// use by the `SetS3WakingVector` AcPiVerb.
///
/// # Safety
/// `sdt` must be a valid pointer to a validated SDT whose
/// signature is "FACS" and whose length is at least 64
/// bytes (the minimum size of the FACS structure for ACPI
/// 4.0+ with `ospm_flags` and `reserved1`).
pub fn init(sdt: &Sdt) {
if &sdt.signature != b"FACS" {
return;
}
// The minimum FACS size depends on the version:
// - ACPI 1.0: 32 bytes (just the header + hardware_signature
// + firmware_waking_vector + global_lock)
// - ACPI 2.0: 40 bytes (adds flags + xfirmware_waking_vector)
// - ACPI 4.0: 64 bytes (adds version + reserved + ospm_flags
// + reserved1)
// We require 64 bytes to support all fields.
if sdt.length() < 64 {
return;
}
FACS_PTR.store(
sdt.data_address() as *mut FacsStruct,
core::sync::atomic::Ordering::Release,
);
}
/// Phase II.X.W: Get the FACS structure. Returns `None` if
/// the FACS is not available.
pub fn get() -> Option<&'static FacsStruct> {
let ptr = FACS_PTR.load(core::sync::atomic::Ordering::Acquire);
if ptr.is_null() {
None
} else {
// SAFETY: FACS_PTR was set by `init` after verifying
// sdt.length() >= 64. The pointer is to firmware memory
// which doesn't change.
Some(unsafe { &*ptr })
}
}
/// Phase II.X.W: Read the 32-bit firmware waking vector.
/// Returns 0 if the FACS is not available.
pub fn firmware_waking_vector() -> u32 {
self::get()
.map(|f| f.firmware_waking_vector)
.unwrap_or(0)
}
/// Phase II.X.W: Read the 64-bit firmware waking vector.
/// Returns 0 if the FACS is not available.
pub fn x_firmware_waking_vector() -> u64 {
self::get()
.map(|f| f.xfirmware_waking_vector)
.unwrap_or(0)
}
/// Phase II.X.W: Write the 32-bit firmware waking vector.
/// The kernel's S3 trampoline is written here so the legacy
/// 32-bit firmware wake path works.
///
/// Returns true on success, false if the FACS is not
/// available.
pub fn set_firmware_waking_vector_32(addr: u32) -> bool {
let ptr = FACS_PTR.load(core::sync::atomic::Ordering::Acquire);
if ptr.is_null() {
return false;
}
// SAFETY: FACS_PTR was set by `init` after verifying
// sdt.length() >= 64. The `firmware_waking_vector` field
// is at offset 36 (after the 36-byte SDT header). The
// memory is page-aligned (firmware memory) and writable.
unsafe {
let waking_vector_ptr = (ptr as *mut u8).add(36) as *mut u32;
core::ptr::write_unaligned(waking_vector_ptr, addr);
}
true
}
/// Phase II.X.W: Write the 64-bit firmware waking vector.
/// The kernel's S3 trampoline is written here so the modern
/// 64-bit firmware wake path works.
///
/// Returns true on success, false if the FACS is not
/// available.
pub fn set_x_firmware_waking_vector_64(addr: u64) -> bool {
let ptr = FACS_PTR.load(core::sync::atomic::Ordering::Acquire);
if ptr.is_null() {
return false;
}
// SAFETY: FACS_PTR was set by `init` after verifying
// sdt.length() >= 64. The `xfirmware_waking_vector` field
// is at offset 40. The memory is page-aligned and
// writable.
unsafe {
let x_waking_vector_ptr = (ptr as *mut u8).add(40) as *mut u64;
core::ptr::write_unaligned(x_waking_vector_ptr, addr);
}
true
}
/// Phase II.X.W: Write the 64-bit firmware waking vector
/// (preferred over the 32-bit version on 64-bit systems).
/// Equivalent to `set_x_firmware_waking_vector_64`.
pub fn set_waking_vector(addr: u64) -> bool {
set_x_firmware_waking_vector_64(addr)
}
/// Phase II.X.W: Read the FACS version. Returns 0 if the
/// FACS is not available.
pub fn version() -> u8 {
self::get().map(|f| f.version).unwrap_or(0)
}
/// Phase II.X.W: Read the FACS hardware signature. Returns 0
/// if the FACS is not available.
pub fn hardware_signature() -> u32 {
self::get()
.map(|f| f.hardware_signature)
.unwrap_or(0)
}
/// Phase II.X.W: Read the FACS flags. Returns 0 if the FACS
/// is not available.
///
/// See `facs_flags::*` for the bit definitions.
pub fn flags() -> u32 {
self::get().map(|f| f.flags).unwrap_or(0)
}
/// Phase II.X.W: Read the FACS OSPM flags. Returns 0 if the
/// FACS is not available.
///
/// See `facs_ospm_flags::*` for the bit definitions.
pub fn ospm_flags() -> u32 {
self::get().map(|f| f.ospm_flags).unwrap_or(0)
}
/// Phase II.X.W: Read the FACS global lock. Returns 0 if the
/// FACS is not available.
///
/// See `facs_glock_flags::*` for the bit definitions.
pub fn global_lock() -> u32 {
self::get().map(|f| f.global_lock).unwrap_or(0)
}
/// Phase II.X.W: Read the reserved bytes. Returns `None` if
/// the FACS is not available.
pub fn reserved() -> Option<[u8; 3]> {
self::get().map(|f| f.reserved)
}
/// Phase II.X.W: Read the ACPI 4.0+ reserved bytes. Returns
/// `None` if the FACS is not available.
pub fn reserved1() -> Option<[u8; 24]> {
self::get().map(|f| f.reserved1)
}
-132
View File
@@ -1,132 +0,0 @@
//! ACPI Fixed ACPI Description Table (FADT) parser.
//!
//! Per ACPI 6.5 §5.2.9. The FADT contains the hardware register
//! addresses used by the kernel for ACPI sleep state entry (S3/S5)
//! and the SCI interrupt. This module parses the fields the
//! kernel needs (PM1a_CNT, PM1a_STS for the sleep entry path,
//! and x_firmware_ctrl / firmware_ctrl for the FACS address).
//!
//! Hardware-agnostic: the FADT layout is standardized by the ACPI
//! spec; only the field values vary per platform.
use core::sync::atomic::{AtomicU16, AtomicU32, AtomicU64};
use crate::acpi::sdt::Sdt;
/// Phase II: PM1a_CNT port. Read from the FADT at boot, written
/// by `enter_s3()` to enter S3 (SLP_TYP|SLP_EN bits). Also used
/// by S5 entry (set_global_s_state in acpid).
pub static PM1A_CONTROL_PORT: AtomicU16 = AtomicU16::new(0);
/// Phase II: PM1a_STS port. Used by `enter_s3()` to clear
/// WAK_STS (bit 15) before writing SLP_TYP|SLP_EN.
pub static PM1A_STATUS_PORT: AtomicU16 = AtomicU16::new(0);
/// Phase II.X.W: 32-bit FACS address (FADT offset 36,
/// `firmware_ctrl` field). Used as a fallback when
/// `x_firmware_ctrl` (offset 140, ACPI 2.0+) is not present
/// (i.e., for ACPI 1.0 systems).
pub static FIRMWARE_CTRL: AtomicU32 = AtomicU32::new(0);
/// Phase II.X.W: 64-bit FACS address (FADT offset 140,
/// `x_firmware_ctrl` field, ACPI 2.0+). The kernel's FACS
/// parser uses this to find the FACS for writing the
/// `xfirmware_waking_vector` on S3 entry.
pub static X_FIRMWARE_CTRL: AtomicU64 = AtomicU64::new(0);
/// FADT signature bytes ("FACP").
const FADT_SIGNATURE: [u8; 4] = *b"FACP";
/// FADT fixed offsets for the fields we read. These match
/// the ACPI 6.5 §5.2.9 Table 5.6 layout.
mod offsets {
/// PM1a_STS (PM1 Status Register) Block 0 Address.
/// 32-bit General-Purpose Event Register Block 0 Address.
pub const PM1A_STS: usize = 48;
/// PM1a_CNT (PM1 Control Register) Block 0 Address.
/// 32-bit General-Purpose Event Register Block 0 Address.
pub const PM1A_CNT: usize = 56;
/// `firmware_ctrl`: 32-bit Firmware ACPI Control
/// Structure address. ACPI 1.0+.
pub const FIRMWARE_CTRL_32: usize = 36;
/// `x_firmware_ctrl`: 64-bit Firmware ACPI Control
/// Structure address. ACPI 2.0+.
pub const X_FIRMWARE_CTRL_64: usize = 140;
/// FADT minimum size for ACPI 2.0+ (i.e., enough to
/// include `x_firmware_ctrl` at offset 140).
pub const FADT_MIN_SIZE_ACPI_2_0: usize = 148;
/// FADT minimum size for ACPI 1.0.
pub const FADT_MIN_SIZE_ACPI_1_0: usize = 76;
}
/// Parse the FADT from the given SDT bytes and extract the
/// PM1a_CNT, PM1a_STS, and FACS-address fields. Called once at
/// boot after the ACPI table discovery finds the FADT.
///
/// The FADT layout is variable (different sizes for ACPI 1.0 vs
/// 6.5+). We only need the first ~148 bytes which contain the
/// fixed-register addresses. Reference: ACPI 6.5 §5.2.9.
pub fn init(sdt: &Sdt) {
if &sdt.signature != &FADT_SIGNATURE {
return;
}
// SAFETY: We trust the ACPI table discovery code to have
// verified the FADT checksum. The FADT fields are at fixed
// offsets (per the ACPI spec); reading them as u32/u64 is
// safe because all of them are at 4-byte or 8-byte aligned
// offsets on x86_64.
let data = sdt.data_address() as *const u8;
unsafe {
// PM1a_CNT is at offset 56 in the FADT (ACPI 6.5 §5.2.9
// Table 5.6). 32-bit General-Purpose Event Register Block 0
// Address.
let pm1a_cnt = core::ptr::read_unaligned(data.add(offsets::PM1A_CNT) as *const u32);
// PM1a_STS is at offset 48 in the FADT.
let pm1a_sts = core::ptr::read_unaligned(data.add(offsets::PM1A_STS) as *const u32);
// Convert u32 to u16 (port numbers are 16-bit). The low
// 16 bits are the IO port; the high 16 bits are the
// address-space ID which we ignore (always IO on x86).
PM1A_CONTROL_PORT.store(
(pm1a_cnt & 0xFFFF) as u16,
core::sync::atomic::Ordering::Release,
);
PM1A_STATUS_PORT.store(
(pm1a_sts & 0xFFFF) as u16,
core::sync::atomic::Ordering::Release,
);
// Phase II.X.W: 32-bit FACS address (FADT offset 36,
// `firmware_ctrl` field). ACPI 1.0+.
let firmware_ctrl = core::ptr::read_unaligned(
data.add(offsets::FIRMWARE_CTRL_32) as *const u32,
);
FIRMWARE_CTRL.store(firmware_ctrl, core::sync::atomic::Ordering::Release);
// Phase II.X.W: 64-bit FACS address (FADT offset 140,
// `x_firmware_ctrl` field). ACPI 2.0+. We require the
// FADT to be at least 148 bytes to have this field
// (the field is at offset 140, which is 8 bytes for the
// u64, so the minimum FADT size is 148 bytes).
if sdt.length() >= offsets::FADT_MIN_SIZE_ACPI_2_0 as u32 {
let x_firmware_ctrl = core::ptr::read_unaligned(
data.add(offsets::X_FIRMWARE_CTRL_64) as *const u64,
);
X_FIRMWARE_CTRL.store(x_firmware_ctrl, core::sync::atomic::Ordering::Release);
}
}
}
/// Phase II.X.W: 32-bit FACS address (FADT offset 36,
/// `firmware_ctrl` field). Returns 0 if the FADT has not
/// been initialized.
pub fn firmware_ctrl() -> u32 {
FIRMWARE_CTRL.load(core::sync::atomic::Ordering::Acquire)
}
/// Phase II.X.W: 64-bit FACS address (FADT offset 140,
/// `x_firmware_ctrl` field). Returns 0 if the FADT has not
/// been initialized or the FADT is too short to have the
/// field.
pub fn x_firmware_ctrl() -> u64 {
X_FIRMWARE_CTRL.load(core::sync::atomic::Ordering::Acquire)
}
-64
View File
@@ -1,64 +0,0 @@
use alloc::boxed::Box;
use super::{find_sdt, sdt::Sdt};
use crate::{
arch::device::generic_timer::GenericTimer,
dtb::irqchip::{register_irq, IRQ_CHIP},
};
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct Gtdt {
pub header: Sdt,
pub cnt_control_base: u64,
_reserved: u32,
pub secure_el1_timer_gsiv: u32,
pub secure_el1_timer_flags: u32,
pub non_secure_el1_timer_gsiv: u32,
pub non_secure_el1_timer_flags: u32,
pub virtual_el1_timer_gsiv: u32,
pub virtual_el1_timer_flags: u32,
pub el2_timer_gsiv: u32,
pub el2_timer_flags: u32,
pub cnt_read_base: u64,
pub platform_timer_count: u32,
pub platform_timer_offset: u32,
/*TODO: we don't need these yet, and they cause short tables to fail parsing
pub virtual_el2_timer_gsiv: u32,
pub virtual_el2_timer_flags: u32,
*/
//TODO: platform timer structure (at platform timer offset, with platform timer count)
}
impl Gtdt {
pub fn init() {
let gtdt_sdt = find_sdt("GTDT");
let gtdt = if gtdt_sdt.len() == 1 {
match Gtdt::new(gtdt_sdt[0]) {
Some(gtdt) => gtdt,
None => {
warn!("Failed to parse GTDT");
return;
}
}
} else {
warn!("Unable to find GTDT");
return;
};
let gsiv = gtdt.non_secure_el1_timer_gsiv;
info!("generic_timer gsiv = {}", gsiv);
let mut timer = GenericTimer::new();
timer.init();
register_irq(gsiv, Box::new(timer));
unsafe { IRQ_CHIP.irq_enable(gsiv as u32) };
}
pub fn new(sdt: &'static Sdt) -> Option<&'static Gtdt> {
if &sdt.signature == b"GTDT" && sdt.length as usize >= size_of::<Gtdt>() {
Some(unsafe { &*((sdt as *const Sdt) as *const Gtdt) })
} else {
None
}
}
}
-121
View File
@@ -1,121 +0,0 @@
use core::ptr::{self, read_volatile, write_volatile};
#[cfg(not(target_arch = "x86"))]
use crate::memory::{RmmA, RmmArch};
use crate::{find_one_sdt, memory::PhysicalAddress};
use super::{sdt::Sdt, GenericAddressStructure, ACPI_TABLE};
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
pub struct Hpet {
pub header: Sdt,
pub hw_rev_id: u8,
pub comparator_descriptor: u8,
pub pci_vendor_id: u16,
pub base_address: GenericAddressStructure,
pub hpet_number: u8,
pub min_periodic_clk_tick: u16,
pub oem_attribute: u8,
}
impl Hpet {
pub fn init() {
let hpet = Hpet::new(find_one_sdt!("HPET"));
if let Some(hpet) = hpet {
debug!(" HPET: {:X}", hpet.hpet_number);
let mut hpet_t = ACPI_TABLE.hpet.write();
*hpet_t = Some(hpet);
}
}
pub fn new(sdt: &'static Sdt) -> Option<Hpet> {
if &sdt.signature == b"HPET" && sdt.length as usize >= size_of::<Hpet>() {
let s = unsafe { ptr::read((sdt as *const Sdt) as *const Hpet) };
if s.base_address.address_space == 0 {
unsafe { s.map() };
Some(s)
} else {
warn!(
"HPET has unsupported address space {}",
s.base_address.address_space
);
None
}
} else {
None
}
}
}
//TODO: x86 use assumes only one HPET and only one GenericAddressStructure
#[cfg(target_arch = "x86")]
impl Hpet {
pub unsafe fn map(&self) {
unsafe {
use crate::memory::{Frame, KernelMapper, Page, PageFlags, VirtualAddress};
let frame = Frame::containing(PhysicalAddress::new(self.base_address.address as usize));
let page = Page::containing_address(VirtualAddress::new(crate::HPET_OFFSET));
KernelMapper::lock_rw()
.map_phys(
page.start_address(),
frame.base(),
PageFlags::new().write(true).device_memory(true),
)
.expect("failed to map memory for GenericAddressStructure")
.flush();
}
}
pub unsafe fn read_u64(&self, offset: usize) -> u64 {
unsafe { read_volatile((crate::HPET_OFFSET + offset) as *const u64) }
}
pub unsafe fn write_u64(&mut self, offset: usize, value: u64) {
unsafe {
write_volatile((crate::HPET_OFFSET + offset) as *mut u64, value);
}
}
}
#[cfg(not(target_arch = "x86"))]
impl Hpet {
pub unsafe fn map(&self) {
unsafe {
crate::memory::map_device_memory(
PhysicalAddress::new(self.base_address.address as usize),
crate::memory::PAGE_SIZE,
);
}
}
pub unsafe fn read_u64(&self, offset: usize) -> u64 {
unsafe {
read_volatile(
RmmA::phys_to_virt(PhysicalAddress::new(
self.base_address.address as usize + offset,
))
.data() as *const u64,
)
}
}
pub unsafe fn write_u64(&mut self, offset: usize, value: u64) {
unsafe {
write_volatile(
RmmA::phys_to_virt(PhysicalAddress::new(
self.base_address.address as usize + offset,
))
.data() as *mut u64,
value,
);
}
}
}
-97
View File
@@ -1,97 +0,0 @@
use alloc::{boxed::Box, vec::Vec};
use super::{Madt, MadtEntry};
use crate::{
arch::device::irqchip::{
gic::{GenericInterruptController, GicCpuIf, GicDistIf},
gicv3::{GicV3, GicV3CpuIf},
},
dtb::irqchip::{IrqChipItem, IRQ_CHIP},
memory::{map_device_memory, PhysicalAddress, PAGE_SIZE},
};
pub(super) fn init(madt: Madt) {
let mut gicd_opt = None;
let mut giccs = Vec::new();
for madt_entry in madt.iter() {
debug!(" {:#x?}", madt_entry);
match madt_entry {
MadtEntry::Gicc(gicc) => {
giccs.push(gicc);
}
MadtEntry::Gicd(gicd) => {
if gicd_opt.is_some() {
warn!("Only one GICD should be present on a system, ignoring this one");
} else {
gicd_opt = Some(gicd);
}
}
_ => {}
}
}
let Some(gicd) = gicd_opt else {
warn!("No GICD found");
return;
};
let mut gic_dist_if = GicDistIf::default();
unsafe {
let phys = PhysicalAddress::new(gicd.physical_base_address as usize);
let virt = map_device_memory(phys, PAGE_SIZE);
gic_dist_if.init(virt.data());
};
info!("{:#x?}", gic_dist_if);
match gicd.gic_version {
1 | 2 => {
for gicc in giccs {
let mut gic_cpu_if = GicCpuIf::default();
unsafe {
let phys = PhysicalAddress::new(gicc.physical_base_address as usize);
let virt = map_device_memory(phys, PAGE_SIZE);
gic_cpu_if.init(virt.data())
};
info!("{:#x?}", gic_cpu_if);
let gic = GenericInterruptController {
gic_dist_if,
gic_cpu_if,
irq_range: (0, 0),
};
let chip = IrqChipItem {
phandle: 0,
parents: Vec::new(),
children: Vec::new(),
ic: Box::new(gic),
};
unsafe { IRQ_CHIP.irq_chip_list.chips.push(chip) };
//TODO: support more GICCs
break;
}
}
3 => {
for gicc in giccs {
let mut gic_cpu_if = GicV3CpuIf;
unsafe { gic_cpu_if.init() };
info!("{:#x?}", gic_cpu_if);
let gic = GicV3 {
gic_dist_if,
gic_cpu_if,
//TODO: get GICRs
gicrs: Vec::new(),
irq_range: (0, 0),
};
let chip = IrqChipItem {
phandle: 0,
parents: Vec::new(),
children: Vec::new(),
ic: Box::new(gic),
};
unsafe { IRQ_CHIP.irq_chip_list.chips.push(chip) };
//TODO: support more GICCs
break;
}
}
_ => {
warn!("unsupported GIC version {}", gicd.gic_version);
}
}
unsafe { IRQ_CHIP.init(None) };
}
-9
View File
@@ -1,9 +0,0 @@
use super::Madt;
pub(super) fn init(madt: Madt) {
for madt_entry in madt.iter() {
debug!(" {:#x?}", madt_entry);
}
warn!("MADT not yet handled on this platform");
}
-160
View File
@@ -1,160 +0,0 @@
use core::{
hint,
sync::atomic::{AtomicU8, Ordering},
};
use crate::{
arch::{
device::local_apic::the_local_apic,
start::{kstart_ap, KernelArgsAp},
},
cpu_set::LogicalCpuId,
memory::{
allocate_p2frame, Frame, KernelMapper, Page, PageFlags, PhysicalAddress, RmmA, RmmArch,
VirtualAddress, PAGE_SIZE,
},
startup::AP_READY,
};
use super::{Madt, MadtEntry};
const TRAMPOLINE: usize = 0x8000;
static TRAMPOLINE_DATA: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/trampoline"));
pub(super) fn init(madt: Madt) {
let local_apic = unsafe { the_local_apic() };
let me = local_apic.id();
if local_apic.x2 {
debug!(" X2APIC {}", me.get());
} else {
debug!(" XAPIC {}: {:>08X}", me.get(), local_apic.address);
}
if cfg!(not(feature = "multi_core")) {
return;
}
// Map trampoline
let trampoline_frame = Frame::containing(PhysicalAddress::new(TRAMPOLINE));
let trampoline_page = Page::containing_address(VirtualAddress::new(TRAMPOLINE));
let (result, page_table_physaddr) = unsafe {
//TODO: do not have writable and executable!
let mut mapper = KernelMapper::lock_rw();
let result = mapper
.map_phys(
trampoline_page.start_address(),
trampoline_frame.base(),
PageFlags::new().execute(true).write(true),
)
.expect("failed to map trampoline");
(result, mapper.table().phys().data())
};
result.flush();
// Write trampoline, make sure TRAMPOLINE page is free for use
for (i, val) in TRAMPOLINE_DATA.iter().enumerate() {
unsafe {
(*((TRAMPOLINE as *mut u8).add(i) as *const AtomicU8)).store(*val, Ordering::SeqCst);
}
}
unsafe {
let preliminary_cpu_count = madt.iter().filter(|e| matches!(e, MadtEntry::LocalApic(entry) if u32::from(entry.id) == me.get() || entry.flags & 1 == 1)).count();
crate::profiling::allocate(preliminary_cpu_count as u32);
}
for madt_entry in madt.iter() {
debug!(" {:x?}", madt_entry);
if let MadtEntry::LocalApic(ap_local_apic) = madt_entry {
if u32::from(ap_local_apic.id) == me.get() {
debug!(" This is my local APIC");
} else if ap_local_apic.flags & 1 == 1 {
let cpu_id = LogicalCpuId::next();
// Allocate a stack
let stack_start = RmmA::phys_to_virt(
allocate_p2frame(4)
.expect("no more frames in acpi stack_start")
.base(),
)
.data();
let stack_end = stack_start + (PAGE_SIZE << 4);
let pcr_ptr = crate::arch::gdt::allocate_and_init_pcr(cpu_id, stack_end);
let idt_ptr = crate::arch::idt::allocate_and_init_idt(cpu_id);
let args = KernelArgsAp {
stack_end: stack_end as *mut u8,
cpu_id,
pcr_ptr,
idt_ptr,
};
let ap_ready = (TRAMPOLINE + 8) as *mut u64;
let ap_args_ptr = unsafe { ap_ready.add(1) };
let ap_page_table = unsafe { ap_ready.add(2) };
let ap_code = unsafe { ap_ready.add(3) };
// Set the ap_ready to 0, volatile
unsafe {
ap_ready.write(0);
ap_args_ptr.write(&args as *const _ as u64);
ap_page_table.write(page_table_physaddr as u64);
#[expect(clippy::fn_to_numeric_cast)]
ap_code.write(kstart_ap as u64);
// TODO: Is this necessary (this fence)?
core::arch::asm!("");
};
AP_READY.store(false, Ordering::SeqCst);
// Send INIT IPI
{
let mut icr = 0x4500;
if local_apic.x2 {
icr |= u64::from(ap_local_apic.id) << 32;
} else {
icr |= u64::from(ap_local_apic.id) << 56;
}
local_apic.set_icr(icr);
}
// Send START IPI
{
let ap_segment = (TRAMPOLINE >> 12) & 0xFF;
let mut icr = 0x4600 | ap_segment as u64;
if local_apic.x2 {
icr |= u64::from(ap_local_apic.id) << 32;
} else {
icr |= u64::from(ap_local_apic.id) << 56;
}
local_apic.set_icr(icr);
}
// Wait for trampoline ready
while unsafe { (*ap_ready.cast::<AtomicU8>()).load(Ordering::SeqCst) } == 0 {
hint::spin_loop();
}
while !AP_READY.load(Ordering::SeqCst) {
hint::spin_loop();
}
RmmA::invalidate_all();
}
}
}
// Unmap trampoline
let (_frame, _, flush) = unsafe {
KernelMapper::lock_rw()
.unmap_phys(trampoline_page.start_address())
.expect("failed to unmap trampoline page")
};
flush.flush();
}
-240
View File
@@ -1,240 +0,0 @@
use core::cell::SyncUnsafeCell;
use super::sdt::Sdt;
use crate::find_one_sdt;
/// The Multiple APIC Descriptor Table
#[derive(Clone, Copy, Debug)]
pub struct Madt {
sdt: &'static Sdt,
pub local_address: u32,
pub flags: u32,
}
#[cfg(target_arch = "aarch64")]
#[path = "arch/aarch64.rs"]
mod arch;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[path = "arch/x86.rs"]
mod arch;
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))]
#[path = "arch/other.rs"]
mod arch;
static MADT: SyncUnsafeCell<Option<Madt>> = SyncUnsafeCell::new(None);
pub fn madt() -> Option<&'static Madt> {
unsafe { &*MADT.get() }.as_ref()
}
pub const FLAG_PCAT: u32 = 1;
impl Madt {
pub fn init() {
let madt = Madt::new(find_one_sdt!("APIC"));
if let Some(madt) = madt {
// safe because no APs have been started yet.
unsafe { MADT.get().write(Some(madt)) };
debug!(" APIC: {:>08X}: {}", madt.local_address, madt.flags);
arch::init(madt);
}
}
pub fn new(sdt: &'static Sdt) -> Option<Madt> {
if &sdt.signature == b"APIC" && sdt.data_len() >= 8 {
//Not valid if no local address and flags
let local_address = unsafe { (sdt.data_address() as *const u32).read_unaligned() };
let flags = unsafe {
(sdt.data_address() as *const u32)
.offset(1)
.read_unaligned()
};
Some(Madt {
sdt,
local_address,
flags,
})
} else {
None
}
}
pub fn iter(&self) -> MadtIter {
MadtIter {
sdt: self.sdt,
i: 8, // Skip local controller address and flags
}
}
}
/// MADT Local APIC
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct MadtLocalApic {
/// Processor ID
pub processor: u8,
/// Local APIC ID
pub id: u8,
/// Flags. 1 means that the processor is enabled
pub flags: u32,
}
/// MADT I/O APIC
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct MadtIoApic {
/// I/O APIC ID
pub id: u8,
/// reserved
_reserved: u8,
/// I/O APIC address
pub address: u32,
/// Global system interrupt base
pub gsi_base: u32,
}
/// MADT Interrupt Source Override
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct MadtIntSrcOverride {
/// Bus Source
pub bus_source: u8,
/// IRQ Source
pub irq_source: u8,
/// Global system interrupt base
pub gsi_base: u32,
/// Flags
pub flags: u16,
}
/// MADT GICC
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct MadtGicc {
_reserved: u16,
pub cpu_interface_number: u32,
pub acpi_processor_uid: u32,
pub flags: u32,
pub parking_protocol_version: u32,
pub performance_interrupt_gsiv: u32,
pub parked_address: u64,
pub physical_base_address: u64,
pub gicv: u64,
pub gich: u64,
pub vgic_maintenance_interrupt: u32,
pub gicr_base_address: u64,
pub mpidr: u64,
pub processor_power_efficiency_class: u8,
_reserved2: u8,
pub spe_overflow_interrupt: u16,
//TODO: optional field introduced in ACPI 6.5: pub trbe_interrupt: u16,
}
/// MADT GICD
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct MadtGicd {
_reserved: u16,
pub gic_id: u32,
pub physical_base_address: u64,
pub system_vector_base: u32,
pub gic_version: u8,
_reserved2: [u8; 3],
}
/// MADT Entries
#[derive(Debug)]
#[allow(dead_code)]
pub enum MadtEntry {
LocalApic(&'static MadtLocalApic),
InvalidLocalApic(usize),
IoApic(&'static MadtIoApic),
InvalidIoApic(usize),
IntSrcOverride(&'static MadtIntSrcOverride),
InvalidIntSrcOverride(usize),
Gicc(&'static MadtGicc),
InvalidGicc(usize),
Gicd(&'static MadtGicd),
InvalidGicd(usize),
Unknown(u8),
}
pub struct MadtIter {
sdt: &'static Sdt,
i: usize,
}
impl Iterator for MadtIter {
type Item = MadtEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.i + 1 < self.sdt.data_len() {
let entry_type = unsafe { *(self.sdt.data_address() as *const u8).add(self.i) };
let entry_len =
unsafe { *(self.sdt.data_address() as *const u8).add(self.i + 1) } as usize;
if self.i + entry_len <= self.sdt.data_len() {
let item = match entry_type {
0x0 => {
if entry_len == size_of::<MadtLocalApic>() + 2 {
MadtEntry::LocalApic(unsafe {
&*((self.sdt.data_address() + self.i + 2) as *const MadtLocalApic)
})
} else {
MadtEntry::InvalidLocalApic(entry_len)
}
}
0x1 => {
if entry_len == size_of::<MadtIoApic>() + 2 {
MadtEntry::IoApic(unsafe {
&*((self.sdt.data_address() + self.i + 2) as *const MadtIoApic)
})
} else {
MadtEntry::InvalidIoApic(entry_len)
}
}
0x2 => {
if entry_len == size_of::<MadtIntSrcOverride>() + 2 {
MadtEntry::IntSrcOverride(unsafe {
&*((self.sdt.data_address() + self.i + 2)
as *const MadtIntSrcOverride)
})
} else {
MadtEntry::InvalidIntSrcOverride(entry_len)
}
}
0xB => {
if entry_len >= size_of::<MadtGicc>() + 2 {
MadtEntry::Gicc(unsafe {
&*((self.sdt.data_address() + self.i + 2) as *const MadtGicc)
})
} else {
MadtEntry::InvalidGicc(entry_len)
}
}
0xC => {
if entry_len >= size_of::<MadtGicd>() + 2 {
MadtEntry::Gicd(unsafe {
&*((self.sdt.data_address() + self.i + 2) as *const MadtGicd)
})
} else {
MadtEntry::InvalidGicd(entry_len)
}
}
_ => MadtEntry::Unknown(entry_type),
};
self.i += entry_len;
Some(item)
} else {
None
}
} else {
None
}
}
}
-255
View File
@@ -1,255 +0,0 @@
//! # ACPI
//! Code to parse the ACPI tables
use alloc::{boxed::Box, string::String, vec::Vec};
use core::ptr::NonNull;
use hashbrown::HashMap;
use spin::{Once, RwLock};
use crate::memory::{KernelMapper, PageFlags, PhysicalAddress, RmmA, RmmArch};
use self::{hpet::Hpet, madt::Madt, rsdp::Rsdp, rsdt::Rsdt, rxsdt::Rxsdt, sdt::Sdt, xsdt::Xsdt};
#[cfg(target_arch = "aarch64")]
mod gtdt;
pub mod fadt;
pub mod facs;
pub mod hpet;
pub mod madt;
mod rsdp;
mod rsdt;
mod rxsdt;
pub mod sdt;
#[cfg(target_arch = "aarch64")]
mod spcr;
mod xsdt;
unsafe fn map_linearly(addr: PhysicalAddress, len: usize, mapper: &mut crate::memory::PageMapper) {
unsafe {
let base = PhysicalAddress::new(crate::memory::round_down_pages(addr.data()));
let aligned_len = crate::memory::round_up_pages(len + (addr.data() - base.data()));
for page_idx in 0..aligned_len / crate::memory::PAGE_SIZE {
let (_, flush) = mapper
.map_linearly(
base.add(page_idx * crate::memory::PAGE_SIZE),
PageFlags::new(),
)
.expect("failed to linearly map SDT");
flush.flush();
}
}
}
pub fn get_sdt(sdt_address: PhysicalAddress, mapper: &mut KernelMapper<true>) -> &'static Sdt {
let sdt;
unsafe {
const SDT_SIZE: usize = size_of::<Sdt>();
map_linearly(sdt_address, SDT_SIZE, mapper);
sdt = &*(RmmA::phys_to_virt(sdt_address).data() as *const Sdt);
map_linearly(
sdt_address.add(SDT_SIZE),
sdt.length as usize - SDT_SIZE,
mapper,
);
}
sdt
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct GenericAddressStructure {
pub address_space: u8,
pub bit_width: u8,
pub bit_offset: u8,
pub access_size: u8,
pub address: u64,
}
pub enum RxsdtEnum {
Rsdt(Rsdt),
Xsdt(Xsdt),
}
impl Rxsdt for RxsdtEnum {
fn iter(&self) -> Box<dyn Iterator<Item = PhysicalAddress>> {
match self {
Self::Rsdt(rsdt) => <Rsdt as Rxsdt>::iter(rsdt),
Self::Xsdt(xsdt) => <Xsdt as Rxsdt>::iter(xsdt),
}
}
}
pub static RXSDT_ENUM: Once<RxsdtEnum> = Once::new();
/// Parse the ACPI tables to gather CPU, interrupt, and timer information
pub unsafe fn init(already_supplied_rsdp: Option<NonNull<u8>>) {
unsafe {
{
let mut sdt_ptrs = SDT_POINTERS.write();
*sdt_ptrs = Some(HashMap::new());
}
// Search for RSDP
let rsdp_opt = Rsdp::get_rsdp(already_supplied_rsdp);
if let Some(rsdp) = rsdp_opt {
debug!("SDT address: {:#x}", rsdp.sdt_address().data());
let rxsdt = get_sdt(rsdp.sdt_address(), &mut KernelMapper::lock_rw());
let rxsdt = if let Some(rsdt) = Rsdt::new(rxsdt) {
let mut initialized = false;
let rsdt = RXSDT_ENUM.call_once(|| {
initialized = true;
RxsdtEnum::Rsdt(rsdt)
});
if !initialized {
error!("RXSDT_ENUM already initialized");
}
rsdt
} else if let Some(xsdt) = Xsdt::new(rxsdt) {
let mut initialized = false;
let xsdt = RXSDT_ENUM.call_once(|| {
initialized = true;
RxsdtEnum::Xsdt(xsdt)
});
if !initialized {
error!("RXSDT_ENUM already initialized");
}
xsdt
} else {
warn!("UNKNOWN RSDT OR XSDT SIGNATURE");
return;
};
// TODO: Don't touch ACPI tables in kernel?
for sdt in rxsdt.iter() {
get_sdt(sdt, &mut KernelMapper::lock_rw());
}
for sdt_address in rxsdt.iter() {
let sdt = &*(RmmA::phys_to_virt(sdt_address).data() as *const Sdt);
let signature = get_sdt_signature(sdt);
if let Some(ref mut ptrs) = *(SDT_POINTERS.write()) {
ptrs.insert(signature, sdt);
}
}
// TODO: Enumerate processors in userspace, and then provide an ACPI-independent interface
// to initialize enumerated processors to userspace?
Madt::init();
//TODO: support this on any arch
// SPCR must be initialized after MADT for interrupt controllers
#[cfg(target_arch = "aarch64")]
spcr::Spcr::init();
// TODO: Let userspace setup HPET, and then provide an interface to specify which timer to
// use?
Hpet::init();
#[cfg(target_arch = "aarch64")]
gtdt::Gtdt::init();
// Phase II: parse the FADT to extract the PM1a_CNT
// and PM1a_STS port addresses used by the S3 entry
// path. Hardware-agnostic — works on any platform
// with a working FADT.
if let Some(fadt_sdts) = find_sdt("FACP").first() {
fadt::init(fadt_sdts);
} else {
warn!("ACPI: no FADT (FACP) found, S3 entry path disabled");
}
// Phase II.X.W: parse the FACS to extract the
// xfirmware_waking_vector. This is the address the
// platform firmware jumps to on S3 wake. The kernel's
// S3 resume trampoline in arch/x86_shared/s3_resume.rs
// is written to this address by acpid via the
// SetS3WakingVector AcPiVerb.
//
// The FACS is found via the FADT's x_firmware_ctrl
// field (64-bit) or firmware_ctrl field (32-bit).
// The FADT parser caches the FACS address. We use
// the FADT's x_firmware_ctrl to find the FACS SDT.
let facs_addr = fadt::x_firmware_ctrl();
if facs_addr != 0 {
// SAFETY: The FACS address is a physical
// address stored in the FADT. The boot-time page
// table maps the FACS into the kernel's address
// space (firmware tables are below 4GB on x86_64).
let facs_sdt = unsafe { &*(facs_addr as *const Sdt) };
facs::init(facs_sdt);
} else {
let facs_addr = fadt::firmware_ctrl() as u64;
if facs_addr != 0 {
// SAFETY: same as above.
let facs_sdt =
unsafe { &*(facs_addr as *const Sdt) };
facs::init(facs_sdt);
} else {
warn!("ACPI: no FACS found (neither x_firmware_ctrl nor firmware_ctrl), S3 resume path disabled");
}
}
} else {
error!("NO RSDP FOUND");
}
}
}
pub type SdtSignature = (String, [u8; 6], [u8; 8]);
pub static SDT_POINTERS: RwLock<Option<HashMap<SdtSignature, &'static Sdt>>> = RwLock::new(None);
pub fn find_sdt(name: &str) -> Vec<&'static Sdt> {
let mut sdts: Vec<&'static Sdt> = vec![];
if let Some(ref ptrs) = *(SDT_POINTERS.read()) {
for (signature, sdt) in ptrs {
if signature.0 == name {
sdts.push(sdt);
}
}
}
sdts
}
#[macro_export]
macro_rules! find_one_sdt {
($name:expr) => {{
use $crate::acpi::find_sdt;
match find_sdt($name).as_slice() {
[] => {
println!("Unable to find {}", $name);
return;
}
[x] => *x,
x => {
println!("{} {} found, expected 1", x.len(), $name);
return;
}
}
}};
}
pub fn get_sdt_signature(sdt: &'static Sdt) -> SdtSignature {
let signature =
String::from_utf8(sdt.signature.to_vec()).expect("Error converting signature to string");
(signature, sdt.oem_id, sdt.oem_table_id)
}
pub struct Acpi {
pub hpet: RwLock<Option<Hpet>>,
}
pub static ACPI_TABLE: Acpi = Acpi {
hpet: RwLock::new(None),
};
-62
View File
@@ -1,62 +0,0 @@
use core::ptr::NonNull;
use rmm::PhysicalAddress;
/// RSDP
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct Rsdp {
signature: [u8; 8],
_checksum: u8,
_oemid: [u8; 6],
revision: u8,
rsdt_address: u32,
length: u32,
xsdt_address: u64,
_extended_checksum: u8,
_reserved: [u8; 3],
}
impl Rsdp {
pub unsafe fn get_rsdp(already_supplied_rsdp: Option<NonNull<u8>>) -> Option<Rsdp> {
already_supplied_rsdp.and_then(|rsdp_ptr: NonNull<u8>| {
let rsdp: Rsdp = unsafe { rsdp_ptr.cast().read() };
if rsdp.signature != *b"RSD PTR " {
error!("RSDP signature check failed");
return None;
}
let mut sum: u8 = 0;
for i in 0..20 {
sum = sum.wrapping_add(unsafe { rsdp_ptr.add(i).read() });
}
if sum != 0 {
error!("RSDP checksum failed");
return None;
}
if rsdp.revision >= 2 {
let mut sum: u8 = 0;
for i in 0..rsdp.length as usize {
sum = sum.wrapping_add(unsafe { rsdp_ptr.add(i).read() });
}
if sum != 0 {
error!("XSDP checksum failed");
return None;
}
}
Some(rsdp)
})
}
/// Get the RSDT or XSDT address
pub fn sdt_address(&self) -> PhysicalAddress {
PhysicalAddress::new(if self.revision >= 2 {
self.xsdt_address as usize
} else {
self.rsdt_address as usize
})
}
}
-52
View File
@@ -1,52 +0,0 @@
use alloc::boxed::Box;
use core::convert::TryFrom;
use rmm::PhysicalAddress;
use super::{rxsdt::Rxsdt, sdt::Sdt};
#[derive(Debug)]
pub struct Rsdt(&'static Sdt);
impl Rsdt {
pub fn new(sdt: &'static Sdt) -> Option<Rsdt> {
if &sdt.signature == b"RSDT" {
Some(Rsdt(sdt))
} else {
None
}
}
pub fn as_slice(&self) -> &[u8] {
let length =
usize::try_from(self.0.length).expect("expected 32-bit length to fit within usize");
unsafe { core::slice::from_raw_parts(self.0 as *const _ as *const u8, length) }
}
}
impl Rxsdt for Rsdt {
fn iter(&self) -> Box<dyn Iterator<Item = PhysicalAddress>> {
Box::new(RsdtIter { sdt: self.0, i: 0 })
}
}
pub struct RsdtIter {
sdt: &'static Sdt,
i: usize,
}
impl Iterator for RsdtIter {
type Item = PhysicalAddress;
fn next(&mut self) -> Option<Self::Item> {
if self.i < self.sdt.data_len() / size_of::<u32>() {
let item = unsafe {
(self.sdt.data_address() as *const u32)
.add(self.i)
.read_unaligned()
};
self.i += 1;
Some(PhysicalAddress::new(item as usize))
} else {
None
}
}
}
-6
View File
@@ -1,6 +0,0 @@
use alloc::boxed::Box;
use rmm::PhysicalAddress;
pub trait Rxsdt {
fn iter(&self) -> Box<dyn Iterator<Item = PhysicalAddress>>;
}
-42
View File
@@ -1,42 +0,0 @@
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct Sdt {
pub signature: [u8; 4],
pub length: u32,
pub revision: u8,
pub checksum: u8,
pub oem_id: [u8; 6],
pub oem_table_id: [u8; 8],
pub oem_revision: u32,
pub creator_id: u32,
pub creator_revision: u32,
}
impl Sdt {
/// Get the address of this tables data
pub fn data_address(&self) -> usize {
self as *const _ as usize + size_of::<Sdt>()
}
/// Get the total length of the table (including the SDT
/// header), in bytes. The SDT is `#[repr(C, packed)]` so
/// direct field access requires an unaligned read.
pub fn length(&self) -> u32 {
// SAFETY: The Sdt is `#[repr(C, packed)]` and the
// `length` field is at offset 4 (after the 4-byte
// signature), aligned to a 4-byte boundary. The address
// is a valid pointer to the SDT; reading 4 bytes from
// offset 4 is safe.
unsafe {
let p = self as *const Self as *const u8;
core::ptr::read_unaligned(p.add(4) as *const u32)
}
}
/// Get the length of this tables data
pub fn data_len(&self) -> usize {
let total_size = self.length as usize;
let header_size = size_of::<Sdt>();
total_size.saturating_sub(header_size)
}
}
-140
View File
@@ -1,140 +0,0 @@
use super::{find_sdt, sdt::Sdt, GenericAddressStructure};
use crate::{
arch::device::serial::COM1,
devices::{serial::SerialKind, uart_pl011},
log::LOG,
memory::{map_device_memory, PhysicalAddress, PAGE_SIZE},
};
const INTERRUPT_TYPE_8259: u8 = 1 << 0;
const INTERRUPT_TYPE_APIC: u8 = 1 << 1;
const INTERRUPT_TYPE_SAPIC: u8 = 1 << 2;
const INTERRUPT_TYPE_GIC: u8 = 1 << 3;
const INTERRUPT_TYPE_PLIC: u8 = 1 << 4;
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct Spcr {
pub header: Sdt,
pub interface_type: u8,
_reserved: [u8; 3],
pub base_address: GenericAddressStructure,
pub interrupt_type: u8,
pub irq: u8,
pub gsiv: u32,
pub configured_baud_rate: u8,
pub parity: u8,
pub stop_bits: u8,
pub flow_control: u8,
pub terminal_type: u8,
pub language: u8,
pub pci_device_id: u16,
pub pci_vendor_id: u16,
pub pci_bus: u8,
pub pci_device: u8,
pub pci_function: u8,
pub pci_flags: u32,
pub pci_segment: u8,
/*TODO: these fields are optional based on the table revision
pub uart_clock_frequency: u32,
pub precise_baud_rate: u32,
pub namespace_string_length: u16,
pub namespace_string_offset: u16,
*/
// namespace_string
}
impl Spcr {
pub fn init() {
let spcr_sdt = find_sdt("SPCR");
let spcr = if spcr_sdt.len() == 1 {
match Spcr::new(spcr_sdt[0]) {
Some(spcr) => spcr,
None => {
warn!("Failed to parse SPCR");
return;
}
}
} else {
warn!("Unable to find SPCR");
return;
};
if spcr.base_address.address == 0 {
// Serial disabled
return;
}
let serial_was_empty = !matches!(*COM1.lock(), SerialKind::NotPresent);
if spcr.header.revision >= 2 {
match spcr.interface_type {
3 => {
// PL011
if spcr.base_address.address_space == 0
&& spcr.base_address.bit_width == 32
&& spcr.base_address.bit_offset == 0
&& spcr.base_address.access_size == 3
{
let virt = unsafe {
map_device_memory(
PhysicalAddress::new(spcr.base_address.address as usize),
PAGE_SIZE,
)
};
let serial_port = uart_pl011::SerialPort::new(virt.data(), false);
*COM1.lock() = SerialKind::Pl011(serial_port);
//TODO: enable IRQ on more platforms and interrupt types
if (spcr.interrupt_type & INTERRUPT_TYPE_GIC) == INTERRUPT_TYPE_GIC {
#[cfg(target_arch = "aarch64")]
unsafe {
crate::arch::device::serial::init_acpi(spcr.gsiv);
}
}
} else {
warn!(
"SPCR unsuppoted address for PL011 {:#x?}",
spcr.base_address
);
}
}
//TODO: support more types!
unsupported => {
warn!(
"SPCR revision {} unsupported interface type {}",
spcr.header.revision, unsupported
);
}
}
} else if spcr.header.revision == 1 {
match spcr.interface_type {
//TODO: support more types!
unsupported => {
warn!("SPCR revision 1 unsupported interface type {}", unsupported);
}
}
} else {
warn!("SPCR unsupported revision {}", spcr.header.revision);
}
let mut serial_port = COM1.lock();
if serial_was_empty && !matches!(*serial_port, SerialKind::NotPresent) {
// backfill logs since the heap is loaded
if let Some(ref mut early_log) = *LOG.lock() {
let (s1, s2) = early_log.read();
if !s1.is_empty() {
serial_port.write(s1);
}
if !s2.is_empty() {
serial_port.write(s2);
}
}
}
}
pub fn new(sdt: &'static Sdt) -> Option<&'static Spcr> {
if &sdt.signature == b"SPCR" && sdt.length as usize >= size_of::<Spcr>() {
Some(unsafe { &*((sdt as *const Sdt) as *const Spcr) })
} else {
None
}
}
}
-50
View File
@@ -1,50 +0,0 @@
use alloc::boxed::Box;
use core::convert::TryFrom;
use rmm::PhysicalAddress;
use super::{rxsdt::Rxsdt, sdt::Sdt};
#[derive(Debug)]
pub struct Xsdt(&'static Sdt);
impl Xsdt {
pub fn new(sdt: &'static Sdt) -> Option<Xsdt> {
if &sdt.signature == b"XSDT" {
Some(Xsdt(sdt))
} else {
None
}
}
pub fn as_slice(&self) -> &[u8] {
let length =
usize::try_from(self.0.length).expect("expected 32-bit length to fit within usize");
unsafe { core::slice::from_raw_parts(self.0 as *const _ as *const u8, length) }
}
}
impl Rxsdt for Xsdt {
fn iter(&self) -> Box<dyn Iterator<Item = PhysicalAddress>> {
Box::new(XsdtIter { sdt: self.0, i: 0 })
}
}
pub struct XsdtIter {
sdt: &'static Sdt,
i: usize,
}
impl Iterator for XsdtIter {
type Item = PhysicalAddress;
fn next(&mut self) -> Option<Self::Item> {
if self.i < self.sdt.data_len() / size_of::<u64>() {
let item = unsafe {
core::ptr::read_unaligned((self.sdt.data_address() as *const u64).add(self.i))
};
self.i += 1;
Some(PhysicalAddress::new(item as usize))
} else {
None
}
}
}
+394
View File
@@ -0,0 +1,394 @@
use alloc::{collections::BTreeSet, vec::Vec};
use core::{fmt, mem, ops, slice};
use endian_num::Le;
use crate::{BlockAddr, BlockLevel, BlockMeta, BlockPtr, BlockTrait, Node, TreePtr, BLOCK_SIZE};
pub const ALLOC_LIST_ENTRIES: usize =
(BLOCK_SIZE as usize - mem::size_of::<BlockPtr<AllocList>>()) / mem::size_of::<AllocEntry>();
pub const RELEASE_LIST_ENTRIES: usize = (BLOCK_SIZE as usize
- mem::size_of::<BlockPtr<ReleaseList>>())
/ mem::size_of::<TreePtr<Node>>();
/// The RedoxFS block allocator. This struct manages all "data" blocks in RedoxFS
/// (i.e, all blocks that aren't reserved or part of the header chain).
///
/// [`Allocator`] can allocate blocks of many "levels"---that is, it can
/// allocate multiple consecutive [`BLOCK_SIZE`] blocks in one operation.
///
/// This reduces the amount of memory that the [`Allocator`] uses:
/// Instead of storing the index of each free [`BLOCK_SIZE`] block,
/// the `levels` array can keep track of higher-level blocks, splitting
/// them when a smaller block is requested.
///
/// Higher-level blocks also allow us to more efficiently allocate memory
/// for large files.
#[derive(Clone, Default)]
pub struct Allocator {
/// This array keeps track of all free blocks of each level,
/// and is initialized using the AllocList chain when we open the filesystem.
///
/// Every element of the outer array represents a block level:
/// - item 0: free level 0 blocks (with size [`BLOCK_SIZE`])
/// - item 1: free level 1 blocks (with size 2*[`BLOCK_SIZE`])
/// - item 2: free level 2 blocks (with size 4*[`BLOCK_SIZE`])
/// ...and so on.
///
/// Each inner array contains a list of free block indices,
levels: Vec<BTreeSet<u64>>,
}
impl Allocator {
pub fn levels(&self) -> &Vec<BTreeSet<u64>> {
&self.levels
}
/// Count the number of free [`BLOCK_SIZE`] available to this [`Allocator`].
pub fn free(&self) -> u64 {
let mut free = 0;
for level in 0..self.levels.len() {
let level_size = 1 << level;
free += self.levels[level].len() as u64 * level_size;
}
free
}
/// Find a free block of the given level, mark it as "used", and return its address.
/// Returns [`None`] if there are no free blocks with this level.
pub fn allocate(&mut self, meta: BlockMeta) -> Option<BlockAddr> {
// First, find the lowest level with a free block
let mut free_opt = None;
{
let mut level = meta.level.0;
// Start searching at the level we want. Smaller levels are too small!
while level < self.levels.len() {
if let Some(&index) = self.levels[level].first() {
// Find the index closest to the start of the filesystem
free_opt = match free_opt {
Some((free_level, free_index)) if free_index <= index => {
Some((free_level, free_index))
}
_ => Some((level, index)),
};
}
level += 1;
}
}
// If a free block was found, split it until we find a usable block of the right level.
// The left side of the split block is kept free, and the right side is allocated.
let (mut level, index) = free_opt?;
self.levels[level].remove(&index);
while level > meta.level.0 {
level -= 1;
let level_size = 1 << level;
self.levels[level].insert(index + level_size);
}
Some(unsafe { BlockAddr::new(index, meta) })
}
/// Try to allocate the exact block specified, making all necessary splits.
/// Returns [`None`] if this some (or all) of this block is already allocated.
///
/// Note that [`BlockAddr`] encodes the blocks location _and_ level.
pub fn allocate_exact(&mut self, exact_addr: BlockAddr) -> Option<BlockAddr> {
// This function only supports level 0 right now
assert_eq!(exact_addr.level().0, 0);
let exact_index = exact_addr.index();
let mut index_opt = None;
// Go from the highest to the lowest level
for level in (0..self.levels.len()).rev() {
let level_size = 1 << level;
// Split higher block if found
if let Some(index) = index_opt.take() {
self.levels[level].insert(index);
self.levels[level].insert(index + level_size);
}
// Look for matching block and remove it
for &start in self.levels[level].iter() {
if start <= exact_index {
let end = start + level_size;
if end > exact_index {
self.levels[level].remove(&start);
index_opt = Some(start);
break;
}
}
}
}
Some(unsafe { BlockAddr::new(index_opt?, exact_addr.meta()) })
}
/// Deallocate the given block, marking it "free" so that it can be re-used later.
pub fn deallocate(&mut self, addr: BlockAddr) {
// When we deallocate, we check if block we're deallocating has a free sibling.
// If it does, we join the two to create one free block in the next (higher) level.
//
// We repeat this until we no longer have a sibling to join.
let mut index = addr.index();
let mut level = addr.level().0;
loop {
while level >= self.levels.len() {
self.levels.push(BTreeSet::new());
}
let level_size = 1 << level;
let next_size = level_size << 1;
let mut found = false;
// look at all free blocks in the current level...
for &level_index in self.levels[level].iter() {
// - the block we just freed aligns with the next largest block, and
// - the second block we're looking at is the right sibling of this block
if index % next_size == 0 && index + level_size == level_index {
// "alloc" the next highest block, repeat deallocation process.
self.levels[level].remove(&level_index);
found = true;
break;
// - the index of this block doesn't align with the next largest block, and
// - the block we're looking at is the left neighbor of this block
} else if level_index % next_size == 0 && level_index + level_size == index {
// "alloc" the next highest block, repeat deallocation process.
self.levels[level].remove(&level_index);
index = level_index; // index moves to left block
found = true;
break;
}
}
// We couldn't find a higher block,
// deallocate this one and finish
if !found {
self.levels[level].insert(index);
return;
}
// repeat deallocation process on the
// higher-level block we just created.
level += 1;
}
}
}
#[repr(C, packed)]
#[derive(Clone, Copy, Default, Debug)]
pub struct AllocEntry {
/// The index of the first block this [`AllocEntry`] refers to
index: Le<u64>,
/// The number of blocks after (and including) `index` that are are free or used.
/// If negative, they are used; if positive, they are free.
count: Le<i64>,
}
impl AllocEntry {
pub fn new(index: u64, count: i64) -> Self {
Self {
index: index.into(),
count: count.into(),
}
}
pub fn allocate(addr: BlockAddr) -> Self {
Self::new(addr.index(), -addr.level().blocks::<i64>())
}
pub fn deallocate(addr: BlockAddr) -> Self {
Self::new(addr.index(), addr.level().blocks::<i64>())
}
pub fn index(&self) -> u64 {
self.index.to_ne()
}
pub fn count(&self) -> i64 {
self.count.to_ne()
}
pub fn is_null(&self) -> bool {
self.count() == 0
}
}
/// A node in the allocation chain.
#[repr(C, packed)]
pub struct AllocList {
/// A pointer to the previous AllocList.
/// If this is the null pointer, this is the first element of the chain.
pub prev: BlockPtr<AllocList>,
/// Allocation entries.
pub entries: [AllocEntry; ALLOC_LIST_ENTRIES],
}
unsafe impl BlockTrait for AllocList {
fn empty(level: BlockLevel) -> Option<Self> {
if level.0 == 0 {
Some(Self {
prev: BlockPtr::default(),
entries: [AllocEntry::default(); ALLOC_LIST_ENTRIES],
})
} else {
None
}
}
}
impl fmt::Debug for AllocList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let prev = self.prev;
let entries: Vec<&AllocEntry> = self
.entries
.iter()
.filter(|entry| entry.count() > 0)
.collect();
f.debug_struct("AllocList")
.field("prev", &prev)
.field("entries", &entries)
.finish()
}
}
impl ops::Deref for AllocList {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const AllocList as *const u8,
mem::size_of::<AllocList>(),
) as &[u8]
}
}
}
impl ops::DerefMut for AllocList {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut AllocList as *mut u8,
mem::size_of::<AllocList>(),
) as &mut [u8]
}
}
}
/// A list of nodes pending release.
#[repr(C, packed)]
pub struct ReleaseList {
/// A pointer to the previous ReleaseList.
/// If this is the null pointer, this is the first element of the chain.
pub prev: BlockPtr<ReleaseList>,
/// Allocation entries.
pub entries: [TreePtr<Node>; RELEASE_LIST_ENTRIES],
}
unsafe impl BlockTrait for ReleaseList {
fn empty(level: BlockLevel) -> Option<Self> {
if level.0 == 0 {
Some(Self {
prev: BlockPtr::default(),
entries: [TreePtr::<Node>::default(); RELEASE_LIST_ENTRIES],
})
} else {
None
}
}
}
impl fmt::Debug for ReleaseList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let prev = self.prev;
let entries: Vec<_> = self
.entries
.iter()
.filter(|entry| !entry.is_null())
.map(|entry| entry.id())
.collect();
f.debug_struct("ReleaseList")
.field("prev", &prev)
.field("entries", &entries)
.finish()
}
}
impl ops::Deref for ReleaseList {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const ReleaseList as *const u8,
mem::size_of::<ReleaseList>(),
) as &[u8]
}
}
}
impl ops::DerefMut for ReleaseList {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut ReleaseList as *mut u8,
mem::size_of::<ReleaseList>(),
) as &mut [u8]
}
}
}
#[test]
fn alloc_node_size_test() {
assert_eq!(mem::size_of::<AllocList>(), crate::BLOCK_SIZE as usize);
}
#[test]
fn release_node_size_test() {
assert_eq!(mem::size_of::<ReleaseList>(), crate::BLOCK_SIZE as usize);
}
#[test]
fn allocator_test() {
let mut alloc = Allocator::default();
assert_eq!(alloc.allocate(BlockMeta::default()), None);
alloc.deallocate(unsafe { BlockAddr::new(1, BlockMeta::default()) });
assert_eq!(
alloc.allocate(BlockMeta::default()),
Some(unsafe { BlockAddr::new(1, BlockMeta::default()) })
);
assert_eq!(alloc.allocate(BlockMeta::default()), None);
for addr in 1023..2048 {
alloc.deallocate(unsafe { BlockAddr::new(addr, BlockMeta::default()) });
}
assert_eq!(alloc.levels.len(), 11);
for level in 0..alloc.levels.len() {
if level == 0 {
assert_eq!(alloc.levels[level], [1023].into());
} else if level == 10 {
assert_eq!(alloc.levels[level], [1024].into());
} else {
assert_eq!(alloc.levels[level], [0u64; 0].into());
}
}
for addr in 1023..2048 {
assert_eq!(
alloc.allocate(BlockMeta::default()),
Some(unsafe { BlockAddr::new(addr, BlockMeta::default()) })
);
}
assert_eq!(alloc.allocate(BlockMeta::default()), None);
assert_eq!(alloc.levels.len(), 11);
for level in 0..alloc.levels.len() {
assert_eq!(alloc.levels[level], [0u64; 0].into());
}
}
-50
View File
@@ -1,50 +0,0 @@
use crate::memory::KernelMapper;
use core::{
alloc::{GlobalAlloc, Layout},
ptr::NonNull,
};
use linked_list_allocator::Heap;
use spin::Mutex;
static HEAP: Mutex<Option<Heap>> = Mutex::new(None);
pub struct Allocator;
impl Allocator {
pub unsafe fn init(offset: usize, size: usize) {
unsafe {
*HEAP.lock() = Some(Heap::new(offset, size));
}
}
}
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
unsafe {
while let Some(ref mut heap) = *HEAP.lock() {
match heap.allocate_first_fit(layout) {
Ok(ptr) => return ptr.as_ptr(),
Err(()) => {
let size = heap.size();
super::map_heap(
&mut KernelMapper::lock_rw(),
crate::kernel_heap_offset() + size,
super::KERNEL_HEAP_SIZE,
);
heap.extend(super::KERNEL_HEAP_SIZE);
}
}
}
panic!("__rust_allocate: heap not initialized");
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe {
HEAP.lock()
.as_mut()
.expect("heap not initialized")
.deallocate(NonNull::new_unchecked(ptr), layout)
}
}
}
-48
View File
@@ -1,48 +0,0 @@
use crate::memory::{KernelMapper, Page, PageFlags, VirtualAddress};
use rmm::{Flusher, FrameAllocator, PageFlushAll};
pub use self::linked_list::Allocator;
mod linked_list;
/// Size of kernel heap
const KERNEL_HEAP_SIZE: usize = ::rmm::MEGABYTE;
unsafe fn map_heap(mapper: &mut KernelMapper<true>, offset: usize, size: usize) {
let mut flush_all = PageFlushAll::new();
let heap_start_page = Page::containing_address(VirtualAddress::new(offset));
let heap_end_page = Page::containing_address(VirtualAddress::new(offset + size - 1));
for page in Page::range_inclusive(heap_start_page, heap_end_page) {
let phys = mapper
.allocator_mut()
.allocate_one()
.expect("failed to allocate kernel heap");
let flush = unsafe {
mapper
.map_phys(
page.start_address(),
phys,
PageFlags::new()
.write(true)
.global(cfg!(not(feature = "pti"))),
)
.expect("failed to map kernel heap")
};
flush_all.consume(flush);
}
flush_all.flush();
}
pub unsafe fn init() {
unsafe {
let offset = crate::kernel_heap_offset();
let size = KERNEL_HEAP_SIZE;
// Map heap pages
map_heap(&mut KernelMapper::lock_rw(), offset, size);
// Initialize global heap
Allocator::init(offset, size);
}
}
-15
View File
@@ -1,15 +0,0 @@
// Because the memory map is so important to not be aliased, it is defined here, in one place
// The lower 256 PML4 entries are reserved for userspace
// Each PML4 entry references up to 512 GB of memory
// The second from the top (510) PML4 is reserved for the kernel
/// The size of a single PML4
pub const PML4_SIZE: usize = 0x0000_0080_0000_0000;
/// Offset to kernel heap
#[inline(always)]
pub fn kernel_heap_offset() -> usize {
crate::kernel_executable_offsets::KERNEL_OFFSET() - PML4_SIZE
}
/// End offset of the user image, i.e. kernel start
pub const USER_END_OFFSET: usize = 256 * PML4_SIZE;
-19
View File
@@ -1,19 +0,0 @@
use spin::MutexGuard;
use crate::{arch::device::serial::COM1, devices::serial::SerialKind};
pub struct Writer<'a> {
serial: MutexGuard<'a, SerialKind>,
}
impl<'a> Writer<'a> {
pub fn new() -> Writer<'a> {
Writer {
serial: COM1.lock(),
}
}
pub fn write(&mut self, buf: &[u8]) {
self.serial.write(buf);
}
}
-277
View File
@@ -1,277 +0,0 @@
use core::fmt::{Result, Write};
use crate::arch::device::cpu::registers::{control_regs, id_regs};
pub mod registers;
bitfield::bitfield! {
pub struct MachineId(u32);
get_implementer, _: 31, 24;
get_variant, _: 23, 20;
get_architecture, _: 19, 16;
get_part_number, _: 15, 4;
get_revision, _: 3, 0;
}
enum ImplementerID {
Unknown,
Arm,
Broadcom,
Cavium,
Digital,
Fujitsu,
Infineon,
Motorola,
Nvidia,
AMCC,
Qualcomm,
Marvell,
Intel,
Ampere,
}
const IMPLEMENTERS: [&'static str; 14] = [
"Unknown", "Arm", "Broadcom", "Cavium", "Digital", "Fujitsu", "Infineon", "Motorola", "Nvidia",
"AMCC", "Qualcomm", "Marvell", "Intel", "Ampere",
];
enum VariantID {
Unknown,
}
const VARIANTS: [&'static str; 1] = ["Unknown"];
enum ArchitectureID {
Unknown,
V4,
V4T,
V5,
V5T,
V5TE,
V5TEJ,
V6,
}
const ARCHITECTURES: [&'static str; 8] =
["Unknown", "v4", "v4T", "v5", "v5T", "v5TE", "v5TEJ", "v6"];
enum PartNumberID {
Unknown,
Thunder,
Foundation,
CortexA35,
CortexA53,
CortexA55,
CortexA57,
CortexA72,
CortexA73,
CortexA75,
}
const PART_NUMBERS: [&'static str; 10] = [
"Unknown",
"Thunder",
"Foundation",
"Cortex-A35",
"Cortex-A53",
"Cortex-A55",
"Cortex-A57",
"Cortex-A72",
"Cortex-A73",
"Cortex-A75",
];
enum RevisionID {
Unknown,
Thunder1_0,
Thunder1_1,
}
const REVISIONS: [&'static str; 3] = ["Unknown", "Thunder-1.0", "Thunder-1.1"];
struct CpuInfo {
implementer: &'static str,
variant: &'static str,
architecture: &'static str,
part_number: &'static str,
revision: &'static str,
aa64isar0: id_regs::AA64Isar0,
aa64isar1: id_regs::AA64Isar1,
}
impl CpuInfo {
fn new() -> CpuInfo {
let midr = unsafe { control_regs::midr() };
let midr = MachineId(midr);
let implementer = match midr.get_implementer() {
0x41 => IMPLEMENTERS[ImplementerID::Arm as usize],
0x42 => IMPLEMENTERS[ImplementerID::Broadcom as usize],
0x43 => IMPLEMENTERS[ImplementerID::Cavium as usize],
0x44 => IMPLEMENTERS[ImplementerID::Digital as usize],
0x46 => IMPLEMENTERS[ImplementerID::Fujitsu as usize],
0x49 => IMPLEMENTERS[ImplementerID::Infineon as usize],
0x4d => IMPLEMENTERS[ImplementerID::Motorola as usize],
0x4e => IMPLEMENTERS[ImplementerID::Nvidia as usize],
0x50 => IMPLEMENTERS[ImplementerID::AMCC as usize],
0x51 => IMPLEMENTERS[ImplementerID::Qualcomm as usize],
0x56 => IMPLEMENTERS[ImplementerID::Marvell as usize],
0x69 => IMPLEMENTERS[ImplementerID::Intel as usize],
0xc0 => IMPLEMENTERS[ImplementerID::Ampere as usize],
_ => IMPLEMENTERS[ImplementerID::Unknown as usize],
};
let variant = match midr.get_variant() {
_ => VARIANTS[VariantID::Unknown as usize],
};
let architecture = match midr.get_architecture() {
0b0001 => ARCHITECTURES[ArchitectureID::V4 as usize],
0b0010 => ARCHITECTURES[ArchitectureID::V4T as usize],
0b0011 => ARCHITECTURES[ArchitectureID::V5 as usize],
0b0100 => ARCHITECTURES[ArchitectureID::V5T as usize],
0b0101 => ARCHITECTURES[ArchitectureID::V5TE as usize],
0b0110 => ARCHITECTURES[ArchitectureID::V5TEJ as usize],
0b0111 => ARCHITECTURES[ArchitectureID::V6 as usize],
_ => ARCHITECTURES[ArchitectureID::Unknown as usize],
};
let part_number = match midr.get_part_number() {
0x0a1 => PART_NUMBERS[PartNumberID::Thunder as usize],
0xd00 => PART_NUMBERS[PartNumberID::Foundation as usize],
0xd04 => PART_NUMBERS[PartNumberID::CortexA35 as usize],
0xd03 => PART_NUMBERS[PartNumberID::CortexA53 as usize],
0xd05 => PART_NUMBERS[PartNumberID::CortexA55 as usize],
0xd07 => PART_NUMBERS[PartNumberID::CortexA57 as usize],
0xd08 => PART_NUMBERS[PartNumberID::CortexA72 as usize],
0xd09 => PART_NUMBERS[PartNumberID::CortexA73 as usize],
0xd0a => PART_NUMBERS[PartNumberID::CortexA75 as usize],
_ => PART_NUMBERS[PartNumberID::Unknown as usize],
};
let revision = match part_number {
"Thunder" => {
let val = match midr.get_revision() {
0x00 => REVISIONS[RevisionID::Thunder1_0 as usize],
0x01 => REVISIONS[RevisionID::Thunder1_1 as usize],
_ => REVISIONS[RevisionID::Unknown as usize],
};
val
}
_ => REVISIONS[RevisionID::Unknown as usize],
};
let aa64isar0 = id_regs::aa64isar0();
let aa64isar1 = id_regs::aa64isar1();
CpuInfo {
implementer,
variant,
architecture,
part_number,
revision,
aa64isar0,
aa64isar1,
}
}
}
pub fn cpu_info<W: Write>(w: &mut W) -> Result {
let cpuinfo = CpuInfo::new();
writeln!(w, "Implementer: {}", cpuinfo.implementer)?;
writeln!(w, "Variant: {}", cpuinfo.variant)?;
writeln!(w, "Architecture version: {}", cpuinfo.architecture)?;
writeln!(w, "Part Number: {}", cpuinfo.part_number)?;
writeln!(w, "Revision: {}", cpuinfo.revision)?;
// Print detected CPU features.
// Follow the naming convention estabilished by `std::arch::is_aarch64_feature_detected`.
write!(w, "Features:")?;
// ID_AA64ISAR0_EL1
if cpuinfo.aa64isar0.has_feat_rng() {
write!(w, " rand")?;
}
if cpuinfo.aa64isar0.has_feat_flagm() {
write!(w, " flagm")?;
}
if cpuinfo.aa64isar0.has_feat_flagm2() {
write!(w, " flagm2")?;
}
if cpuinfo.aa64isar0.has_feat_fhm() {
write!(w, " fhm")?;
}
if cpuinfo.aa64isar0.has_feat_dotprod() {
write!(w, " dotprod")?;
}
if cpuinfo.aa64isar0.has_feat_sm3() && cpuinfo.aa64isar0.has_feat_sm4() {
write!(w, " sm4")?;
}
if cpuinfo.aa64isar0.has_feat_sha512() && cpuinfo.aa64isar0.has_feat_sha3() {
write!(w, " sha3")?;
}
if cpuinfo.aa64isar0.has_feat_rdm() {
write!(w, " rdm")?;
}
if cpuinfo.aa64isar0.has_feat_lse() {
write!(w, " lse")?;
}
if cpuinfo.aa64isar0.has_feat_lse128() {
write!(w, " lse128")?;
}
if cpuinfo.aa64isar0.has_feat_crc() {
write!(w, " crc")?;
}
if cpuinfo.aa64isar0.has_feat_sha1() && cpuinfo.aa64isar0.has_feat_sha256() {
write!(w, " sha2")?;
}
if cpuinfo.aa64isar0.has_feat_aes() && cpuinfo.aa64isar0.has_feat_pmull() {
write!(w, " aes")?;
}
// ID_AA64ISAR1_EL1
if cpuinfo.aa64isar1.has_feat_i8mm() {
write!(w, " i8mm")?;
}
if cpuinfo.aa64isar1.has_feat_bf16() {
write!(w, " bf16")?;
}
if cpuinfo.aa64isar1.has_feat_sb() {
write!(w, " sb")?;
}
if cpuinfo.aa64isar1.has_feat_frintts() {
write!(w, " frintts")?;
}
if cpuinfo.aa64isar1.gpi() != 0 || cpuinfo.aa64isar1.gpa() != 0 {
write!(w, " pacg")?;
}
if cpuinfo.aa64isar1.has_feat_lrcpc() {
write!(w, " rcpc")?;
}
if cpuinfo.aa64isar1.has_feat_lrcpc2() {
write!(w, " rcpc2")?;
}
if cpuinfo.aa64isar1.has_feat_lrcpc3() {
write!(w, " rcpc3")?;
}
if cpuinfo.aa64isar1.has_feat_fcma() {
write!(w, " fcma")?;
}
if cpuinfo.aa64isar1.has_feat_jscvt() {
write!(w, " jsconv")?;
}
if cpuinfo.aa64isar1.api() != 0 || cpuinfo.aa64isar1.apa() != 0 {
write!(w, " paca")?;
}
if cpuinfo.aa64isar1.has_feat_dpb() {
write!(w, " dpb")?;
}
if cpuinfo.aa64isar1.has_feat_dpb2() {
write!(w, " dpb2")?;
}
writeln!(w)?;
Ok(())
}
@@ -1,167 +0,0 @@
#![allow(unused)]
//! Functions to read and write control registers.
use core::arch::asm;
pub unsafe fn ttbr0_el1() -> u64 {
unsafe {
let ret: u64;
asm!("mrs {}, ttbr0_el1", out(reg) ret);
ret
}
}
pub unsafe fn ttbr0_el1_write(val: u64) {
unsafe {
asm!("msr ttbr0_el1, {}", in(reg) val);
}
}
pub unsafe fn ttbr1_el1() -> u64 {
unsafe {
let ret: u64;
asm!("mrs {}, ttbr1_el1", out(reg) ret);
ret
}
}
pub unsafe fn ttbr1_el1_write(val: u64) {
unsafe {
asm!("msr ttbr1_el1, {}", in(reg) val);
}
}
pub unsafe fn tpidr_el0() -> u64 {
unsafe {
let ret: u64;
asm!("mrs {}, tpidr_el0", out(reg) ret);
ret
}
}
pub unsafe fn tpidr_el0_write(val: u64) {
unsafe {
asm!("msr tpidr_el0, {}", in(reg) val);
}
}
pub unsafe fn tpidr_el1() -> u64 {
unsafe {
let ret: u64;
asm!("mrs {}, tpidr_el1", out(reg) ret);
ret
}
}
pub unsafe fn tpidr_el1_write(val: u64) {
unsafe {
asm!("msr tpidr_el1, {}", in(reg) val);
}
}
pub unsafe fn tpidrro_el0() -> u64 {
unsafe {
let ret: u64;
asm!("mrs {}, tpidrro_el0", out(reg) ret);
ret
}
}
pub unsafe fn tpidrro_el0_write(val: u64) {
unsafe {
asm!("msr tpidrro_el0, {}", in(reg) val);
}
}
pub unsafe fn esr_el1() -> u32 {
unsafe {
let ret: u32;
asm!("mrs {0:w}, esr_el1", out(reg) ret);
ret
}
}
pub unsafe fn vhe_present() -> bool {
unsafe {
let mut mmfr1: u64;
asm!("mrs {}, id_aa64mmfr1_el1", out(reg) mmfr1);
// The VHE (Virtualization Host Extensions) field is in bits [7:4].
let vhe_field = (mmfr1 >> 4) & 0b1111;
vhe_field != 0
}
}
pub unsafe fn cntfrq_el0() -> u32 {
unsafe {
let ret: usize;
asm!("mrs {}, cntfrq_el0", out(reg) ret);
ret as u32
}
}
pub unsafe fn ptmr_ctrl() -> u32 {
unsafe {
let ret: usize;
asm!("mrs {}, cntp_ctl_el0", out(reg) ret);
ret as u32
}
}
pub unsafe fn ptmr_ctrl_write(val: u32) {
unsafe {
asm!("msr cntp_ctl_el0, {}", in(reg) val as usize);
}
}
pub unsafe fn ptmr_tval() -> u32 {
unsafe {
let ret: usize;
asm!("mrs {0}, cntp_tval_el0", out(reg) ret);
ret as u32
}
}
pub unsafe fn ptmr_tval_write(val: u32) {
unsafe {
asm!("msr cntp_tval_el0, {}", in(reg) val as usize);
}
}
pub unsafe fn vtmr_ctrl() -> u32 {
unsafe {
let ret: usize;
asm!("mrs {}, cntv_ctl_el0", out(reg) ret);
ret as u32
}
}
pub unsafe fn vtmr_ctrl_write(val: u32) {
unsafe {
asm!("msr cntv_ctl_el0, {}", in(reg) val as usize);
}
}
pub unsafe fn vtmr_tval() -> u32 {
unsafe {
let ret: usize;
asm!("mrs {0}, cntv_tval_el0", out(reg) ret);
ret as u32
}
}
pub unsafe fn vtmr_tval_write(val: u32) {
unsafe {
asm!("msr cntv_tval_el0, {}", in(reg) val as usize);
}
}
pub unsafe fn midr() -> u32 {
unsafe {
let ret: usize;
asm!("mrs {}, midr_el1", out(reg) ret);
ret as u32
}
}
@@ -1,151 +0,0 @@
//! Functions and bitfield definitions for `ID_AA64*` system registers. (e.g. `ID_AA64ISAR0_EL1`)
use core::arch::asm;
bitfield::bitfield! {
pub struct AA64Isar0(u64);
impl Debug;
pub rndr, _: 63, 60;
pub tlb, _: 59, 56;
pub ts, _: 55, 52;
pub fhm, _: 51, 48;
pub dp, _: 47, 44;
pub sm4, _: 43, 40;
pub sm3, _: 39, 36;
pub sha3, _: 35, 32;
pub rdm, _: 31, 28;
pub atomic, _: 23, 20;
pub crc32, _: 19, 16;
pub sha2, _: 15, 12;
pub sha1, _: 11, 8;
pub aes, _: 7, 4;
}
bitfield::bitfield! {
pub struct AA64Isar1(u64);
impl Debug;
pub ls64, _: 63, 60;
pub xs, _: 59, 56;
pub i8mm, _: 55, 52;
pub dgh, _: 51, 48;
pub bf16, _: 47, 44;
pub specres, _: 43, 40;
pub sb, _: 39, 36;
pub frintts, _: 35, 32;
pub gpi, _: 31, 28;
pub gpa, _: 27, 24;
pub lrcpc, _: 23, 20;
pub fcma, _: 19, 16;
pub jscvt, _: 15, 12;
pub api, _: 11, 8;
pub apa, _: 7, 4;
pub dpb, _: 3, 0;
}
impl AA64Isar0 {
pub fn has_feat_rng(&self) -> bool {
self.rndr() == 0b0001
}
pub fn has_feat_flagm(&self) -> bool {
self.ts() == 0b0001
}
pub fn has_feat_flagm2(&self) -> bool {
self.ts() == 0b0010
}
pub fn has_feat_fhm(&self) -> bool {
self.fhm() == 0b0001
}
pub fn has_feat_dotprod(&self) -> bool {
self.dp() == 0b0001
}
pub fn has_feat_sm4(&self) -> bool {
self.sm4() == 0b0001
}
pub fn has_feat_sm3(&self) -> bool {
self.sm3() == 0b0001
}
pub fn has_feat_sha3(&self) -> bool {
self.sha3() == 0b0001
}
pub fn has_feat_rdm(&self) -> bool {
self.rdm() == 0b0001
}
pub fn has_feat_lse(&self) -> bool {
self.atomic() == 0b0010
}
pub fn has_feat_lse128(&self) -> bool {
self.atomic() == 0b0011
}
/// The current Arm Architecture Registers Manual calls it FEAT_CRC32,
/// but everyone else seems to call it FEAT_CRC.
pub fn has_feat_crc(&self) -> bool {
self.crc32() == 0b0001
}
pub fn has_feat_sha256(&self) -> bool {
self.sha2() == 0b0001
}
pub fn has_feat_sha512(&self) -> bool {
self.sha2() == 0b0010
}
pub fn has_feat_sha1(&self) -> bool {
self.sha1() == 0b0001
}
pub fn has_feat_aes(&self) -> bool {
self.aes() == 0b0001
}
pub fn has_feat_pmull(&self) -> bool {
self.aes() == 0b0010
}
}
impl AA64Isar1 {
pub fn has_feat_i8mm(&self) -> bool {
self.i8mm() == 0b0001
}
pub fn has_feat_bf16(&self) -> bool {
self.bf16() == 0b0001
}
pub fn has_feat_sb(&self) -> bool {
self.sb() == 0b0001
}
pub fn has_feat_frintts(&self) -> bool {
self.frintts() == 0b0001
}
pub fn has_feat_lrcpc(&self) -> bool {
self.lrcpc() == 0b0001
}
pub fn has_feat_lrcpc2(&self) -> bool {
self.lrcpc() == 0b0010
}
pub fn has_feat_lrcpc3(&self) -> bool {
self.lrcpc() == 0b0011
}
pub fn has_feat_fcma(&self) -> bool {
self.fcma() == 0b0001
}
pub fn has_feat_jscvt(&self) -> bool {
self.jscvt() == 0b0011
}
pub fn has_feat_dpb(&self) -> bool {
self.dpb() == 0b0001
}
pub fn has_feat_dpb2(&self) -> bool {
self.dpb() == 0b0010
}
}
pub fn aa64isar0() -> AA64Isar0 {
let ret: u64;
unsafe {
asm!("mrs {}, ID_AA64ISAR0_EL1", out(reg) ret);
}
AA64Isar0(ret)
}
pub fn aa64isar1() -> AA64Isar1 {
let ret: u64;
unsafe {
asm!("mrs {}, ID_AA64ISAR1_EL1", out(reg) ret);
}
AA64Isar1(ret)
}
@@ -1,2 +0,0 @@
pub mod control_regs;
pub mod id_regs;
-145
View File
@@ -1,145 +0,0 @@
use alloc::boxed::Box;
use super::ic_for_chip;
use crate::{
arch::device::cpu::registers::control_regs,
context::{self, timeout},
dtb::{
get_interrupt,
irqchip::{register_irq, InterruptHandler, IRQ_CHIP},
},
scheme::irq::irq_trigger,
sync::CleanLockToken,
time,
};
use fdt::Fdt;
bitflags! {
struct TimerCtrlFlags: u32 {
const ENABLE = 1 << 0;
const IMASK = 1 << 1;
const ISTATUS = 1 << 2;
}
}
pub unsafe fn init(fdt: &Fdt) {
unsafe {
let mut timer = GenericTimer::new();
timer.init();
if let Some(node) = fdt.find_compatible(&["arm,armv7-timer"]) {
let irq = get_interrupt(fdt, &node, 1).unwrap();
debug!("irq = {:?}", irq);
if let Some(ic_idx) = ic_for_chip(&fdt, &node) {
//PHYS_NONSECURE_PPI only
let virq = IRQ_CHIP.irq_chip_list.chips[ic_idx]
.ic
.irq_xlate(irq)
.unwrap();
info!("generic_timer virq = {}", virq);
register_irq(virq as u32, Box::new(timer));
IRQ_CHIP.irq_enable(virq as u32);
} else {
error!("Failed to find irq parent for generic timer");
}
}
}
}
pub struct GenericTimer {
pub use_virtual_timer: bool,
pub clk_freq: u32,
pub reload_count: u32,
}
impl GenericTimer {
pub fn new() -> Self {
Self {
use_virtual_timer: false,
clk_freq: 0,
reload_count: 0,
}
}
pub fn init(&mut self) {
self.use_virtual_timer = unsafe { !control_regs::vhe_present() };
debug!(
"generic_timer use_virtual_timer = {:?}",
self.use_virtual_timer
);
let clk_freq = unsafe { control_regs::cntfrq_el0() };
self.clk_freq = clk_freq;
self.reload_count = clk_freq / 100;
self.reload_count();
}
fn read_tmr_ctrl(&self) -> TimerCtrlFlags {
TimerCtrlFlags::from_bits_truncate(if self.use_virtual_timer {
unsafe { control_regs::vtmr_ctrl() }
} else {
unsafe { control_regs::ptmr_ctrl() }
})
}
fn write_tmr_ctrl(&self, ctrl: TimerCtrlFlags) {
if self.use_virtual_timer {
unsafe { control_regs::vtmr_ctrl_write(ctrl.bits()) };
} else {
unsafe { control_regs::ptmr_ctrl_write(ctrl.bits()) };
}
}
#[allow(unused)]
fn disable(&self) {
let mut ctrl = self.read_tmr_ctrl();
ctrl.remove(TimerCtrlFlags::ENABLE);
self.write_tmr_ctrl(ctrl);
}
#[allow(unused)]
pub fn set_irq(&mut self) {
let mut ctrl = self.read_tmr_ctrl();
ctrl.remove(TimerCtrlFlags::IMASK);
self.write_tmr_ctrl(ctrl);
}
pub fn clear_irq(&mut self) {
let mut ctrl = self.read_tmr_ctrl();
if ctrl.contains(TimerCtrlFlags::ISTATUS) {
ctrl.insert(TimerCtrlFlags::IMASK);
self.write_tmr_ctrl(ctrl);
}
}
pub fn reload_count(&mut self) {
if self.use_virtual_timer {
unsafe { control_regs::vtmr_tval_write(self.reload_count) };
} else {
unsafe { control_regs::ptmr_tval_write(self.reload_count) };
}
let mut ctrl = self.read_tmr_ctrl();
ctrl.insert(TimerCtrlFlags::ENABLE);
ctrl.remove(TimerCtrlFlags::IMASK);
self.write_tmr_ctrl(ctrl);
}
}
impl InterruptHandler for GenericTimer {
fn irq_handler(&mut self, irq: u32, token: &mut CleanLockToken) {
self.clear_irq();
{
*time::OFFSET.write(token.token()) += self.clk_freq as u128;
}
timeout::trigger(token);
context::switch::tick(token);
unsafe {
// FIXME add_irq accepts a u8 as irq number
// PercpuBlock::current().stats.add_irq(irq);
irq_trigger(irq.try_into().unwrap(), token);
IRQ_CHIP.irq_eoi(irq);
}
self.reload_count();
}
}
-288
View File
@@ -1,288 +0,0 @@
use super::InterruptController;
use crate::{
dtb::{
get_mmio_address,
irqchip::{InterruptHandler, IrqCell, IrqDesc},
},
sync::CleanLockToken,
};
use core::ptr::{read_volatile, write_volatile};
use fdt::{node::FdtNode, Fdt};
use syscall::{
error::{Error, EINVAL},
Result,
};
static GICD_CTLR: u32 = 0x000;
static GICD_TYPER: u32 = 0x004;
static GICD_ISENABLER: u32 = 0x100;
static GICD_ICENABLER: u32 = 0x180;
static GICD_IPRIORITY: u32 = 0x400;
static GICD_ITARGETSR: u32 = 0x800;
static GICD_ICFGR: u32 = 0xc00;
static GICC_EOIR: u32 = 0x0010;
static GICC_IAR: u32 = 0x000c;
static GICC_CTLR: u32 = 0x0000;
static GICC_PMR: u32 = 0x0004;
pub struct GenericInterruptController {
pub gic_dist_if: GicDistIf,
pub gic_cpu_if: GicCpuIf,
pub irq_range: (usize, usize),
}
impl GenericInterruptController {
pub fn new() -> Self {
let gic_dist_if = GicDistIf::default();
let gic_cpu_if = GicCpuIf::default();
GenericInterruptController {
gic_dist_if,
gic_cpu_if,
irq_range: (0, 0),
}
}
pub fn parse(fdt: &Fdt) -> Result<(usize, usize, usize, usize)> {
if let Some(node) = fdt.find_compatible(&["arm,cortex-a15-gic", "arm,gic-400"]) {
return GenericInterruptController::parse_inner(fdt, &node);
} else {
return Err(Error::new(EINVAL));
}
}
fn parse_inner(fdt: &Fdt, node: &FdtNode) -> Result<(usize, usize, usize, usize)> {
//assert address_cells == 0x2, size_cells == 0x2
let reg = node.reg().unwrap();
let mut regs = (0, 0, 0, 0);
let mut idx = 0;
for chunk in reg {
if chunk.size.is_none() {
break;
}
let addr = get_mmio_address(fdt, node, &chunk).unwrap();
match idx {
0 => (regs.0, regs.1) = (addr, chunk.size.unwrap()),
2 => (regs.2, regs.3) = (addr, chunk.size.unwrap()),
_ => break,
}
idx += 2;
}
if idx == 4 {
Ok(regs)
} else {
Err(Error::new(EINVAL))
}
}
}
impl InterruptHandler for GenericInterruptController {
fn irq_handler(&mut self, _irq: u32, token: &mut CleanLockToken) {}
}
impl InterruptController for GenericInterruptController {
fn irq_init(
&mut self,
fdt_opt: Option<&Fdt>,
irq_desc: &mut [IrqDesc; 1024],
ic_idx: usize,
irq_idx: &mut usize,
) -> Result<()> {
if let Some(fdt) = fdt_opt {
let (dist_addr, _dist_size, cpu_addr, _cpu_size) =
match GenericInterruptController::parse(fdt) {
Ok(regs) => regs,
Err(err) => return Err(err),
};
unsafe {
self.gic_dist_if.init(crate::PHYS_OFFSET + dist_addr);
self.gic_cpu_if.init(crate::PHYS_OFFSET + cpu_addr);
}
}
let idx = *irq_idx;
let cnt = if self.gic_dist_if.nirqs > 1024 {
1024
} else {
self.gic_dist_if.nirqs as usize
};
let mut i: usize = 0;
//only support linear irq map now.
while i < cnt && (idx + i < 1024) {
irq_desc[idx + i].basic.ic_idx = ic_idx;
irq_desc[idx + i].basic.ic_irq = i as u32;
irq_desc[idx + i].basic.used = true;
i += 1;
}
info!("gic irq_range = ({}, {})", idx, idx + cnt);
self.irq_range = (idx, idx + cnt);
*irq_idx = idx + cnt;
Ok(())
}
fn irq_ack(&mut self) -> u32 {
unsafe { self.gic_cpu_if.irq_ack() }
}
fn irq_eoi(&mut self, irq_num: u32) {
unsafe { self.gic_cpu_if.irq_eoi(irq_num) }
}
fn irq_enable(&mut self, irq_num: u32) {
unsafe { self.gic_dist_if.irq_enable(irq_num) }
}
fn irq_disable(&mut self, irq_num: u32) {
unsafe { self.gic_dist_if.irq_disable(irq_num) }
}
fn irq_xlate(&self, irq_data: IrqCell) -> Result<usize> {
let off = match irq_data {
IrqCell::L3(0, irq, _flags) => irq as usize + 32, // SPI
IrqCell::L3(1, irq, _flags) => irq as usize + 16, // PPI
_ => return Err(Error::new(EINVAL)),
};
return Ok(off + self.irq_range.0);
}
fn irq_to_virq(&self, hwirq: u32) -> Option<usize> {
if hwirq >= self.gic_dist_if.nirqs {
None
} else {
Some(self.irq_range.0 + hwirq as usize)
}
}
}
#[derive(Debug, Default)]
pub struct GicDistIf {
pub address: usize,
pub ncpus: u32,
pub nirqs: u32,
}
impl GicDistIf {
pub unsafe fn init(&mut self, addr: usize) {
unsafe {
self.address = addr;
// Disable IRQ Distribution
self.write(GICD_CTLR, 0);
let typer = self.read(GICD_TYPER);
self.ncpus = ((typer & (0x7 << 5)) >> 5) + 1;
self.nirqs = ((typer & 0x1f) + 1) * 32;
info!(
"gic: Distributor supports {:?} CPUs and {:?} IRQs",
self.ncpus, self.nirqs
);
// Set all SPIs to level triggered
for irq in (32..self.nirqs).step_by(16) {
self.write(GICD_ICFGR + ((irq / 16) * 4), 0);
}
// Disable all SPIs
for irq in (32..self.nirqs).step_by(32) {
self.write(GICD_ICENABLER + ((irq / 32) * 4), 0xffff_ffff);
}
// Affine all SPIs to CPU0 and set priorities for all IRQs
for irq in 0..self.nirqs {
if irq > 31 {
let ext_offset = GICD_ITARGETSR + (4 * (irq / 4));
let int_offset = irq % 4;
let mut val = self.read(ext_offset);
val |= 0b0000_0001 << (8 * int_offset);
self.write(ext_offset, val);
}
let ext_offset = GICD_IPRIORITY + (4 * (irq / 4));
let int_offset = irq % 4;
let mut val = self.read(ext_offset);
val |= 0b0000_0000 << (8 * int_offset);
self.write(ext_offset, val);
}
// Enable IRQ group 0 and group 1 non-secure distribution
self.write(GICD_CTLR, 0x3);
}
}
pub unsafe fn irq_enable(&mut self, irq: u32) {
unsafe {
let offset = GICD_ISENABLER + (4 * (irq / 32));
let shift = 1 << (irq % 32);
let mut val = self.read(offset);
val |= shift;
self.write(offset, val);
}
}
pub unsafe fn irq_disable(&mut self, irq: u32) {
unsafe {
let offset = GICD_ICENABLER + (4 * (irq / 32));
let shift = 1 << (irq % 32);
let mut val = self.read(offset);
val |= shift;
self.write(offset, val);
}
}
unsafe fn read(&self, reg: u32) -> u32 {
unsafe {
let val = read_volatile((self.address + reg as usize) as *const u32);
val
}
}
unsafe fn write(&mut self, reg: u32, value: u32) {
unsafe {
write_volatile((self.address + reg as usize) as *mut u32, value);
}
}
}
#[derive(Debug, Default)]
pub struct GicCpuIf {
pub address: usize,
}
impl GicCpuIf {
pub unsafe fn init(&mut self, addr: usize) {
unsafe {
self.address = addr;
// Enable CPU0's GIC interface
self.write(GICC_CTLR, 1);
// Set CPU0's Interrupt Priority Mask
self.write(GICC_PMR, 0xff);
}
}
unsafe fn irq_ack(&mut self) -> u32 {
unsafe {
let irq = self.read(GICC_IAR) & 0x1ff;
if irq == 1023 {
panic!("irq_ack: got ID 1023!!!");
}
irq
}
}
unsafe fn irq_eoi(&mut self, irq: u32) {
unsafe {
self.write(GICC_EOIR, irq);
}
}
unsafe fn read(&self, reg: u32) -> u32 {
unsafe {
let val = read_volatile((self.address + reg as usize) as *const u32);
val
}
}
unsafe fn write(&mut self, reg: u32, value: u32) {
unsafe {
write_volatile((self.address + reg as usize) as *mut u32, value);
}
}
}
-196
View File
@@ -1,196 +0,0 @@
use alloc::vec::Vec;
use core::arch::asm;
use fdt::{node::NodeProperty, Fdt};
use super::{gic::GicDistIf, InterruptController};
use crate::{
dtb::{
get_mmio_address,
irqchip::{InterruptHandler, IrqCell, IrqDesc},
},
sync::CleanLockToken,
};
use syscall::{
error::{Error, EINVAL},
Result,
};
#[derive(Debug)]
pub struct GicV3 {
pub gic_dist_if: GicDistIf,
pub gic_cpu_if: GicV3CpuIf,
pub gicrs: Vec<(usize, usize)>,
//TODO: GICC, GICH, GICV?
pub irq_range: (usize, usize),
}
impl GicV3 {
pub fn new() -> Self {
GicV3 {
gic_dist_if: GicDistIf::default(),
gic_cpu_if: GicV3CpuIf,
gicrs: Vec::new(),
irq_range: (0, 0),
}
}
pub fn parse(&mut self, fdt: &Fdt) -> Result<()> {
let Some(node) = fdt.find_compatible(&["arm,gic-v3"]) else {
return Err(Error::new(EINVAL));
};
// Clear current registers
//TODO: deinit?
self.gic_dist_if.address = 0;
self.gicrs.clear();
// Get number of GICRs
let gicrs = node
.property("#redistributor-regions")
.and_then(NodeProperty::as_usize)
.unwrap_or(1);
// Read registers
let mut chunks = node.reg().unwrap();
if let Some(gicd) = chunks.next()
&& let Some(addr) = get_mmio_address(fdt, &node, &gicd)
{
unsafe {
self.gic_dist_if.init(crate::PHYS_OFFSET + addr);
}
}
for _ in 0..gicrs {
if let Some(gicr) = chunks.next() {
self.gicrs.push((
get_mmio_address(fdt, &node, &gicr).unwrap(),
gicr.size.unwrap(),
));
}
}
if self.gic_dist_if.address == 0 || self.gicrs.is_empty() {
Err(Error::new(EINVAL))
} else {
Ok(())
}
}
}
impl InterruptHandler for GicV3 {
fn irq_handler(&mut self, _irq: u32, token: &mut CleanLockToken) {}
}
impl InterruptController for GicV3 {
fn irq_init(
&mut self,
fdt_opt: Option<&Fdt>,
irq_desc: &mut [IrqDesc; 1024],
ic_idx: usize,
irq_idx: &mut usize,
) -> Result<()> {
if let Some(fdt) = fdt_opt {
self.parse(fdt)?;
}
info!("{:X?}", self);
unsafe {
self.gic_cpu_if.init();
}
let idx = *irq_idx;
let cnt = if self.gic_dist_if.nirqs > 1024 {
1024
} else {
self.gic_dist_if.nirqs as usize
};
let mut i: usize = 0;
//only support linear irq map now.
while i < cnt && (idx + i < 1024) {
irq_desc[idx + i].basic.ic_idx = ic_idx;
irq_desc[idx + i].basic.ic_irq = i as u32;
irq_desc[idx + i].basic.used = true;
i += 1;
}
info!("gic irq_range = ({}, {})", idx, idx + cnt);
self.irq_range = (idx, idx + cnt);
*irq_idx = idx + cnt;
Ok(())
}
fn irq_ack(&mut self) -> u32 {
let irq_num = unsafe { self.gic_cpu_if.irq_ack() };
irq_num
}
fn irq_eoi(&mut self, irq_num: u32) {
unsafe { self.gic_cpu_if.irq_eoi(irq_num) }
}
fn irq_enable(&mut self, irq_num: u32) {
unsafe { self.gic_dist_if.irq_enable(irq_num) }
}
fn irq_disable(&mut self, irq_num: u32) {
unsafe { self.gic_dist_if.irq_disable(irq_num) }
}
fn irq_xlate(&self, irq_data: IrqCell) -> Result<usize> {
let off = match irq_data {
IrqCell::L3(0, irq, _flags) => irq as usize + 32, // SPI
IrqCell::L3(1, irq, _flags) => irq as usize + 16, // PPI
_ => return Err(Error::new(EINVAL)),
};
return Ok(off + self.irq_range.0);
}
fn irq_to_virq(&self, hwirq: u32) -> Option<usize> {
if hwirq >= self.gic_dist_if.nirqs {
None
} else {
Some(self.irq_range.0 + hwirq as usize)
}
}
}
#[derive(Debug)]
pub struct GicV3CpuIf;
impl GicV3CpuIf {
pub unsafe fn init(&mut self) {
unsafe {
// Enable system register access
{
let value = 1_usize;
asm!("msr icc_sre_el1, {}", in(reg) value);
}
// Set control register
{
let value = 0_usize;
asm!("msr icc_ctlr_el1, {}", in(reg) value);
}
// Enable non-secure group 1
{
let value = 1_usize;
asm!("msr icc_igrpen1_el1, {}", in(reg) value);
}
// Set CPU0's Interrupt Priority Mask
{
let value = 0xFF_usize;
asm!("msr icc_pmr_el1, {}", in(reg) value);
}
}
}
unsafe fn irq_ack(&mut self) -> u32 {
unsafe {
let mut irq: usize;
asm!("mrs {}, icc_iar1_el1", out(reg) irq);
irq &= 0x1ff;
if irq == 1023 {
panic!("irq_ack: got ID 1023!!!");
}
irq as u32
}
}
unsafe fn irq_eoi(&mut self, irq: u32) {
unsafe {
asm!("msr icc_eoir1_el1, {}", in(reg) irq as usize);
}
}
}
@@ -1,299 +0,0 @@
use core::ptr::{read_volatile, write_volatile};
use fdt::{node::FdtNode, Fdt};
use super::InterruptController;
use crate::{
dtb::{
get_interrupt, get_mmio_address,
irqchip::{InterruptHandler, IrqCell, IrqDesc, IRQ_CHIP},
},
sync::CleanLockToken,
};
use syscall::{
error::{Error, EINVAL},
Result,
};
#[inline(always)]
fn ffs(num: u32) -> u32 {
let mut x = num;
if x == 0 {
return 0;
}
let mut r = 1;
if (x & 0xffff) == 0 {
x >>= 16;
r += 16;
}
if (x & 0xff) == 0 {
x >>= 8;
r += 8;
}
if (x & 0xf) == 0 {
x >>= 4;
r += 4;
}
if (x & 0x3) == 0 {
x >>= 2;
r += 2;
}
if (x & 0x1) == 0 {
r += 1;
}
r
}
const PENDING_0: u32 = 0x0;
const PENDING_1: u32 = 0x4;
const PENDING_2: u32 = 0x8;
const ENABLE_0: u32 = 0x18;
const ENABLE_1: u32 = 0x10;
const ENABLE_2: u32 = 0x14;
const DISABLE_0: u32 = 0x24;
const DISABLE_1: u32 = 0x1c;
const DISABLE_2: u32 = 0x20;
pub struct Bcm2835ArmInterruptController {
pub address: usize,
pub irq_range: (usize, usize),
}
impl Bcm2835ArmInterruptController {
pub fn new() -> Self {
Bcm2835ArmInterruptController {
address: 0,
irq_range: (0, 0),
}
}
pub fn parse(fdt: &Fdt) -> Result<(usize, usize, Option<usize>)> {
if let Some(node) = fdt.find_compatible(&["brcm,bcm2836-armctrl-ic"]) {
return unsafe { Bcm2835ArmInterruptController::parse_inner(fdt, &node) };
} else {
return Err(Error::new(EINVAL));
}
}
unsafe fn parse_inner(fdt: &Fdt, node: &FdtNode) -> Result<(usize, usize, Option<usize>)> {
unsafe {
//assert address_cells == 0x1, size_cells == 0x1
let mem = node.reg().unwrap().nth(0).unwrap();
let base = get_mmio_address(fdt, node, &mem).unwrap();
let size = mem.size.unwrap() as u32;
let mut ret_virq = None;
if let Some(interrupt_parent) = node.property("interrupt-parent") {
let phandle = interrupt_parent.as_usize().unwrap() as u32;
let irq = get_interrupt(fdt, node, 0).unwrap();
let ic_idx = IRQ_CHIP.phandle_to_ic_idx(phandle).unwrap();
//PHYS_NONSECURE_PPI only
let virq = IRQ_CHIP.irq_chip_list.chips[ic_idx]
.ic
.irq_xlate(irq)
.unwrap();
info!(
"register bcm2835arm_ctrl as ic_idx {}'s child virq = {}",
ic_idx, virq
);
ret_virq = Some(virq);
}
Ok((base as usize, size as usize, ret_virq))
}
}
unsafe fn init(&mut self) {
unsafe {
debug!("IRQ BCM2835 INIT");
//disable all interrupt
self.write(DISABLE_0, 0xffff_ffff);
self.write(DISABLE_1, 0xffff_ffff);
self.write(DISABLE_2, 0xffff_ffff);
debug!("IRQ BCM2835 END");
}
}
unsafe fn read(&self, reg: u32) -> u32 {
unsafe {
let val = read_volatile((self.address + reg as usize) as *const u32);
val
}
}
unsafe fn write(&mut self, reg: u32, value: u32) {
unsafe {
write_volatile((self.address + reg as usize) as *mut u32, value);
}
}
}
impl InterruptController for Bcm2835ArmInterruptController {
fn irq_init(
&mut self,
fdt_opt: Option<&Fdt>,
irq_desc: &mut [IrqDesc; 1024],
ic_idx: usize,
irq_idx: &mut usize,
) -> Result<()> {
let (base, _size, _virq) = match Bcm2835ArmInterruptController::parse(fdt_opt.unwrap()) {
Ok((a, b, c)) => (a, b, c),
Err(_) => return Err(Error::new(EINVAL)),
};
unsafe {
self.address = base + crate::PHYS_OFFSET;
self.init();
let idx = *irq_idx;
let cnt = 3 << 5; //3 * 32 irqs, basic == 8, reg1 = 32, reg2 = 32
let mut i: usize = 0;
//only support linear irq map now.
while i < cnt && (idx + i < 1024) {
irq_desc[idx + i].basic.ic_idx = ic_idx;
irq_desc[idx + i].basic.ic_irq = i as u32;
irq_desc[idx + i].basic.used = true;
i += 1;
}
info!("bcm2835 irq_range = ({}, {})", idx, idx + cnt);
self.irq_range = (idx, idx + cnt);
*irq_idx = idx + cnt;
}
Ok(())
}
fn irq_ack(&mut self) -> u32 {
//TODO: support smp self.read(LOCAL_IRQ_PENDING + 4 * cpu)
let sources = unsafe { self.read(PENDING_0) };
let pending_num = ffs(sources) - 1;
let fast_irq = [
7 + 32,
9 + 32,
10 + 32,
18 + 32,
19 + 32,
21 + 64,
22 + 64,
23 + 64,
24 + 64,
25 + 64,
30 + 64,
];
//fast irq
if pending_num >= 10 && pending_num <= 20 {
return fast_irq[(pending_num - 10) as usize];
}
let pending_num = ffs(sources & 0x3ff) - 1;
match pending_num {
num @ 0..=7 => return num,
8 => {
let sources1 = unsafe { self.read(PENDING_1) };
let irq_0_31 = ffs(sources1) - 1;
return irq_0_31 + 32;
}
9 => {
let sources2 = unsafe { self.read(PENDING_2) };
let irq_32_63 = ffs(sources2) - 1;
return irq_32_63 + 64;
}
num => {
error!(
"unexpected irq pending in BASIC PENDING: 0x{}, sources = 0x{:08x}",
num, sources
);
return num;
}
}
}
fn irq_eoi(&mut self, _irq_num: u32) {}
fn irq_enable(&mut self, irq_num: u32) {
debug!("bcm2835 enable {} {}", irq_num, irq_num & 0x1f);
match irq_num {
num @ 0..=31 => {
let val = 1 << num;
unsafe {
self.write(ENABLE_0, val);
}
}
num @ 32..=63 => {
let val = 1 << (num & 0x1f);
unsafe {
self.write(ENABLE_1, val);
}
}
num @ 64..=95 => {
let val = 1 << (num & 0x1f);
unsafe {
self.write(ENABLE_2, val);
}
}
_ => return,
}
}
fn irq_disable(&mut self, irq_num: u32) {
match irq_num {
num @ 0..=31 => {
let val = 1 << num;
unsafe {
self.write(DISABLE_0, val);
}
}
num @ 32..=63 => {
let val = 1 << (num & 0x1f);
unsafe {
self.write(DISABLE_1, val);
}
}
num @ 64..=95 => {
let val = 1 << (num & 0x1f);
unsafe {
self.write(DISABLE_2, val);
}
}
_ => return,
}
}
fn irq_xlate(&self, irq_data: IrqCell) -> Result<usize> {
//assert interrupt-cells == 0x2
match irq_data {
IrqCell::L2(bank, irq) => {
//TODO: check bank && irq
let hwirq = (bank as usize) << 5 | (irq as usize);
let off = hwirq + self.irq_range.0;
Ok(off)
}
_ => Err(Error::new(EINVAL)),
}
}
fn irq_to_virq(&self, hwirq: u32) -> Option<usize> {
if hwirq > 95 {
None
} else {
Some(self.irq_range.0 + hwirq as usize)
}
}
}
impl InterruptHandler for Bcm2835ArmInterruptController {
fn irq_handler(&mut self, _irq: u32, token: &mut CleanLockToken) {
unsafe {
let irq = self.irq_ack();
if let Some(virq) = self.irq_to_virq(irq)
&& virq < 1024
{
if let Some(handler) = &mut IRQ_CHIP.irq_desc[virq].handler {
handler.irq_handler(virq as u32, token);
}
} else {
error!("unexpected irq num {}", irq);
}
self.irq_eoi(irq);
}
}
}
@@ -1,231 +0,0 @@
use super::InterruptController;
use crate::{
arch::device::{ROOT_IC_IDX, ROOT_IC_IDX_IS_SET},
dtb::{
get_mmio_address,
irqchip::{InterruptHandler, IrqCell, IrqDesc},
},
sync::CleanLockToken,
};
use core::{
arch::asm,
ptr::{read_volatile, write_volatile},
sync::atomic::Ordering,
};
use fdt::{node::FdtNode, Fdt};
use syscall::{
error::{Error, EINVAL},
Result,
};
const LOCAL_CONTROL: u32 = 0x000;
const LOCAL_PRESCALER: u32 = 0x008;
const LOCAL_GPU_ROUTING: u32 = 0x00C;
const LOCAL_TIMER_INT_CONTROL0: u32 = 0x040;
const LOCAL_IRQ_PENDING: u32 = 0x060;
const LOCAL_IRQ_CNTPNSIRQ: u32 = 0x1;
const LOCAL_IRQ_GPU_FAST: u32 = 0x8;
const LOCAL_IRQ_PMU_FAST: u32 = 0x9;
const LOCAL_IRQ_LAST: u32 = LOCAL_IRQ_PMU_FAST;
#[inline(always)]
fn ffs(num: u32) -> u32 {
let mut x = num;
if x == 0 {
return 0;
}
let mut r = 1;
if (x & 0xffff) == 0 {
x >>= 16;
r += 16;
}
if (x & 0xff) == 0 {
x >>= 8;
r += 8;
}
if (x & 0xf) == 0 {
x >>= 4;
r += 4;
}
if (x & 0x3) == 0 {
x >>= 2;
r += 2;
}
if (x & 0x1) == 0 {
r += 1;
}
r
}
pub struct Bcm2836ArmInterruptController {
pub address: usize,
pub irq_range: (usize, usize),
pub active_cpu: u32,
}
impl Bcm2836ArmInterruptController {
pub fn new() -> Self {
Bcm2836ArmInterruptController {
address: 0,
irq_range: (0, 0),
active_cpu: 0,
}
}
pub fn parse(fdt: &Fdt) -> Result<(usize, usize)> {
if let Some(node) = fdt.find_compatible(&["brcm,bcm2836-l1-intc"]) {
return Bcm2836ArmInterruptController::parse_inner(fdt, &node);
} else {
return Err(Error::new(EINVAL));
}
}
fn parse_inner(fdt: &Fdt, node: &FdtNode) -> Result<(usize, usize)> {
//assert address_cells == 0x1, size_cells == 0x1
let reg = node.reg().unwrap().nth(0).unwrap();
let addr = get_mmio_address(fdt, node, &reg).unwrap();
Ok((addr, reg.size.unwrap()))
}
unsafe fn init(&mut self) {
unsafe {
debug!("IRQ BCM2836 INIT");
//init local timer freq
self.write(LOCAL_CONTROL, 0x0);
self.write(LOCAL_PRESCALER, 0x8000_0000);
//routing all irq to core
self.write(LOCAL_GPU_ROUTING, self.active_cpu);
debug!("routing all irq to core {}", self.active_cpu);
debug!("IRQ BCM2836 END");
}
}
unsafe fn read(&self, reg: u32) -> u32 {
unsafe {
let val = read_volatile((self.address + reg as usize) as *const u32);
val
}
}
unsafe fn write(&mut self, reg: u32, value: u32) {
unsafe {
write_volatile((self.address + reg as usize) as *mut u32, value);
}
}
}
impl InterruptHandler for Bcm2836ArmInterruptController {
fn irq_handler(&mut self, _irq: u32, token: &mut CleanLockToken) {}
}
impl InterruptController for Bcm2836ArmInterruptController {
fn irq_init(
&mut self,
fdt_opt: Option<&Fdt>,
irq_desc: &mut [IrqDesc; 1024],
ic_idx: usize,
irq_idx: &mut usize,
) -> Result<()> {
let (base, _size) = match Bcm2836ArmInterruptController::parse(fdt_opt.unwrap()) {
Ok((a, b)) => (a, b),
Err(_) => return Err(Error::new(EINVAL)),
};
unsafe {
self.address = base + crate::PHYS_OFFSET;
let cpuid: usize;
asm!("mrs {}, mpidr_el1", out(reg) cpuid);
self.active_cpu = cpuid as u32 & 0x3;
self.init();
let idx = *irq_idx;
let cnt = LOCAL_IRQ_LAST as usize;
let mut i: usize = 0;
//only support linear irq map now.
while i < cnt && (idx + i < 1024) {
irq_desc[idx + i].basic.ic_idx = ic_idx;
irq_desc[idx + i].basic.ic_irq = i as u32;
irq_desc[idx + i].basic.used = true;
i += 1;
}
info!("bcm2836 irq_range = ({}, {})", idx, idx + cnt);
self.irq_range = (idx, idx + cnt);
*irq_idx = idx + cnt;
}
//raspi 3b+ dts doesn't follow the rule to set root parent interrupt controller
//so we should set it manually.
ROOT_IC_IDX.store(ic_idx, Ordering::Relaxed);
ROOT_IC_IDX_IS_SET.store(1, Ordering::Relaxed);
Ok(())
}
fn irq_ack(&mut self) -> u32 {
let cpuid: usize;
unsafe {
asm!("mrs {}, mpidr_el1", out(reg) cpuid);
}
let cpu = cpuid as u32 & 0x3;
let sources: u32 = unsafe { self.read(LOCAL_IRQ_PENDING + 4 * cpu) };
ffs(sources) - 1
}
fn irq_eoi(&mut self, _irq_num: u32) {}
fn irq_enable(&mut self, irq_num: u32) {
debug!("bcm2836 enable {}", irq_num);
match irq_num {
LOCAL_IRQ_CNTPNSIRQ => unsafe {
let cpuid: usize;
asm!("mrs {}, mpidr_el1", out(reg) cpuid);
let cpu = cpuid as u32 & 0x3;
let mut reg_val = self.read(LOCAL_TIMER_INT_CONTROL0 + 4 * cpu);
reg_val |= 0x2;
self.write(LOCAL_TIMER_INT_CONTROL0 + 4 * cpu, reg_val);
},
LOCAL_IRQ_GPU_FAST => {
//GPU IRQ always enable
}
_ => {
//ignore
}
}
}
fn irq_disable(&mut self, irq_num: u32) {
match irq_num {
LOCAL_IRQ_CNTPNSIRQ => unsafe {
let cpuid: usize;
asm!("mrs {}, mpidr_el1", out(reg) cpuid);
let cpu = cpuid as u32 & 0x3;
let mut reg_val = self.read(LOCAL_TIMER_INT_CONTROL0 + 4 * cpu);
reg_val &= !0x2;
self.write(LOCAL_TIMER_INT_CONTROL0 + 4 * cpu, reg_val);
},
LOCAL_IRQ_GPU_FAST => {
//GPU IRQ always enable
}
_ => {
//ignore
}
}
}
fn irq_xlate(&self, irq_data: IrqCell) -> Result<usize> {
//assert interrupt-cells == 0x2
match irq_data {
IrqCell::L2(irq, _) => Ok(irq as usize + self.irq_range.0),
_ => Err(Error::new(EINVAL)),
}
}
fn irq_to_virq(&self, hwirq: u32) -> Option<usize> {
if hwirq > LOCAL_IRQ_LAST {
None
} else {
Some(self.irq_range.0 + hwirq as usize)
}
}
}
-41
View File
@@ -1,41 +0,0 @@
use crate::dtb::irqchip::{InterruptController, IRQ_CHIP};
use alloc::boxed::Box;
use fdt::{node::FdtNode, Fdt};
pub(crate) mod gic;
pub(crate) mod gicv3;
mod irq_bcm2835;
mod irq_bcm2836;
mod null;
pub(crate) fn new_irqchip(ic_str: &str) -> Option<Box<dyn InterruptController>> {
if ic_str.contains("arm,gic-v3") {
Some(Box::new(gicv3::GicV3::new()))
} else if ic_str.contains("arm,cortex-a15-gic") || ic_str.contains("arm,gic-400") {
Some(Box::new(gic::GenericInterruptController::new()))
} else if ic_str.contains("brcm,bcm2836-l1-intc") {
Some(Box::new(irq_bcm2836::Bcm2836ArmInterruptController::new()))
} else if ic_str.contains("brcm,bcm2836-armctrl-ic") {
Some(Box::new(irq_bcm2835::Bcm2835ArmInterruptController::new()))
} else {
warn!("no driver for interrupt controller {:?}", ic_str);
//TODO: return None and handle it properly
Some(Box::new(null::Null))
}
}
pub(crate) fn ic_for_chip(fdt: &Fdt, node: &FdtNode) -> Option<usize> {
if let Some(_) = node.property("interrupts-extended") {
error!("multi-parented device not supported");
None
} else if let Some(irqc_phandle) = node
.property("interrupt-parent")
.or(fdt.root().property("interrupt-parent"))
.and_then(|f| f.as_usize())
{
unsafe { IRQ_CHIP.phandle_to_ic_idx(irqc_phandle as u32) }
} else {
error!("no irq parent found");
None
}
}
-41
View File
@@ -1,41 +0,0 @@
use fdt::Fdt;
use syscall::{
error::{Error, EINVAL},
Result,
};
use super::InterruptController;
use crate::{
dtb::irqchip::{InterruptHandler, IrqCell, IrqDesc},
sync::CleanLockToken,
};
pub struct Null;
impl InterruptHandler for Null {
fn irq_handler(&mut self, _irq: u32, token: &mut CleanLockToken) {}
}
impl InterruptController for Null {
fn irq_init(
&mut self,
_fdt_opt: Option<&Fdt>,
_irq_desc: &mut [IrqDesc; 1024],
_ic_idx: usize,
_irq_idx: &mut usize,
) -> Result<()> {
Ok(())
}
fn irq_ack(&mut self) -> u32 {
unimplemented!()
}
fn irq_eoi(&mut self, _irq_num: u32) {}
fn irq_enable(&mut self, _irq_num: u32) {}
fn irq_disable(&mut self, _irq_num: u32) {}
fn irq_xlate(&self, _irq_data: IrqCell) -> Result<usize> {
Err(Error::new(EINVAL))
}
fn irq_to_virq(&self, _hwirq: u32) -> Option<usize> {
None
}
}
-60
View File
@@ -1,60 +0,0 @@
use crate::info;
use core::sync::atomic::{AtomicUsize, Ordering};
use fdt::Fdt;
pub mod cpu;
pub mod generic_timer;
pub mod irqchip;
pub mod rtc;
pub mod serial;
use crate::dtb::irqchip::IRQ_CHIP;
use irqchip::ic_for_chip;
pub static ROOT_IC_IDX: AtomicUsize = AtomicUsize::new(0);
pub static ROOT_IC_IDX_IS_SET: AtomicUsize = AtomicUsize::new(0);
unsafe fn init_root_ic(fdt: &Fdt) {
unsafe {
let is_set = ROOT_IC_IDX_IS_SET.load(Ordering::Relaxed);
if is_set != 0 {
let ic_idx = ROOT_IC_IDX.load(Ordering::Relaxed);
info!("Already selected {} as root ic", ic_idx);
return;
}
let root_irqc_phandle = fdt
.root()
.property("interrupt-parent")
.unwrap()
.as_usize()
.unwrap();
let ic_idx = IRQ_CHIP
.phandle_to_ic_idx(root_irqc_phandle as u32)
.unwrap();
info!("select {} as root ic", ic_idx);
ROOT_IC_IDX.store(ic_idx, Ordering::Relaxed);
}
}
pub unsafe fn init_devicetree(fdt: &Fdt) {
unsafe {
info!("IRQCHIP INIT");
crate::dtb::irqchip::init(&fdt);
init_root_ic(&fdt);
info!("GIT INIT");
generic_timer::init(fdt);
info!("SERIAL INIT");
serial::init(fdt);
info!("RTC INIT");
rtc::init(fdt);
}
}
pub struct ArchPercpuMisc;
impl ArchPercpuMisc {
pub const fn default() -> Self {
Self
}
}
-41
View File
@@ -1,41 +0,0 @@
use crate::{dtb::get_mmio_address, sync::CleanLockToken, time};
use core::ptr::read_volatile;
static RTC_DR: usize = 0x000;
pub unsafe fn init(fdt: &fdt::Fdt) {
if let Some(node) = fdt.find_compatible(&["arm,pl031"]) {
match node
.reg()
.and_then(|mut iter| iter.next())
.and_then(|region| get_mmio_address(fdt, &node, &region))
{
Some(phys) => {
let mut rtc = Pl031rtc { phys };
info!("PL031 RTC at {:#x}", rtc.phys);
let mut token = unsafe { CleanLockToken::new() };
*time::START.lock(token.token()) = (rtc.time() as u128) * time::NANOS_PER_SEC;
}
None => {
warn!("No PL031 RTC registers");
}
}
} else {
warn!("No PL031 RTC found");
}
}
struct Pl031rtc {
pub phys: usize,
}
impl Pl031rtc {
unsafe fn read(&self, reg: usize) -> u32 {
unsafe { read_volatile((crate::PHYS_OFFSET + self.phys + reg) as *const u32) }
}
pub fn time(&mut self) -> u64 {
let seconds = unsafe { self.read(RTC_DR) } as u64;
seconds
}
}
-59
View File
@@ -1,59 +0,0 @@
use alloc::boxed::Box;
use fdt::Fdt;
pub use crate::dtb::serial::COM1;
use crate::{
arch::device::irqchip::ic_for_chip,
dtb::{
get_interrupt,
irqchip::{register_irq, InterruptHandler, IRQ_CHIP},
},
scheme::irq::irq_trigger,
sync::CleanLockToken,
};
pub struct Com1Irq {}
impl InterruptHandler for Com1Irq {
fn irq_handler(&mut self, irq: u32, token: &mut CleanLockToken) {
COM1.lock().receive(token);
unsafe {
// FIXME add_irq accepts a u8 as irq number
// PercpuBlock::current().stats.add_irq(irq);
irq_trigger(irq.try_into().unwrap(), token);
IRQ_CHIP.irq_eoi(irq);
}
}
}
pub unsafe fn init(fdt: &Fdt) {
unsafe {
//TODO: find actual serial device, not just any PL011
if let Some(node) = fdt.find_compatible(&["arm,pl011"]) {
let irq = get_interrupt(fdt, &node, 0).unwrap();
if let Some(ic_idx) = ic_for_chip(&fdt, &node) {
let virq = IRQ_CHIP.irq_chip_list.chips[ic_idx]
.ic
.irq_xlate(irq)
.unwrap();
info!("serial_port virq = {}", virq);
register_irq(virq as u32, Box::new(Com1Irq {}));
IRQ_CHIP.irq_enable(virq as u32);
} else {
error!("serial port irq parent not found");
}
}
COM1.lock().enable_irq();
}
}
pub unsafe fn init_acpi(irq: u32) {
unsafe {
//TODO: what should chip index be?
let virq = IRQ_CHIP.irq_chip_list.chips[0].ic.irq_to_virq(irq).unwrap();
info!("serial_port virq = {}", virq);
register_irq(virq as u32, Box::new(Com1Irq {}));
IRQ_CHIP.irq_enable(virq as u32);
COM1.lock().enable_irq();
}
}
-236
View File
@@ -1,236 +0,0 @@
use ::syscall::Exception;
use rmm::VirtualAddress;
use crate::{
context::signal::excp_handler,
exception_stack,
memory::{ArchIntCtx, GenericPfFlags},
sync::CleanLockToken,
syscall,
};
use super::InterruptStack;
exception_stack!(synchronous_exception_at_el1_with_sp0, |stack| {
println!("Synchronous exception at EL1 with SP0");
stack.trace();
loop {}
});
fn exception_code(esr: usize) -> u8 {
((esr >> 26) & 0x3f) as u8
}
fn iss(esr: usize) -> u32 {
(esr & 0x01ff_ffff) as u32
}
unsafe fn far_el1() -> usize {
unsafe {
let ret: usize;
core::arch::asm!("mrs {}, far_el1", out(reg) ret);
ret
}
}
unsafe fn instr_data_abort_inner(
stack: &mut InterruptStack,
from_user: bool,
instr_not_data: bool,
_from: &str,
) -> bool {
unsafe {
let iss = iss(stack.iret.esr_el1);
let fsc = iss & 0x3F;
//dbg!(fsc);
let was_translation_fault = fsc >= 0b000100 && fsc <= 0b000111;
//let was_permission_fault = fsc >= 0b001101 && fsc <= 0b001111;
let write_not_read_if_data = iss & (1 << 6) != 0;
let mut flags = GenericPfFlags::empty();
flags.set(GenericPfFlags::PRESENT, !was_translation_fault);
// TODO: RMW instructions may "involve" writing to (possibly invalid) memory, but AArch64
// doesn't appear to require that flag to be set if the read alone would trigger a fault.
flags.set(
GenericPfFlags::INVOLVED_WRITE,
write_not_read_if_data && !instr_not_data,
);
flags.set(GenericPfFlags::INSTR_NOT_DATA, instr_not_data);
flags.set(GenericPfFlags::USER_NOT_SUPERVISOR, from_user);
let faulting_addr = VirtualAddress::new(far_el1());
//dbg!(faulting_addr, flags, from);
crate::memory::page_fault_handler(stack, flags, faulting_addr).is_ok()
}
}
unsafe fn cntfrq_el0() -> usize {
unsafe {
let ret: usize;
core::arch::asm!("mrs {}, cntfrq_el0", out(reg) ret);
ret
}
}
unsafe fn cntpct_el0() -> usize {
unsafe {
let ret: usize;
core::arch::asm!("mrs {}, cntpct_el0", out(reg) ret);
ret
}
}
unsafe fn cntvct_el0() -> usize {
unsafe {
let ret: usize;
core::arch::asm!("mrs {}, cntvct_el0", out(reg) ret);
ret
}
}
unsafe fn instr_trapped_msr_mrs_inner(
stack: &mut InterruptStack,
_from_user: bool,
_instr_not_data: bool,
_from: &str,
) -> bool {
unsafe {
let iss = iss(stack.iret.esr_el1);
// let res0 = (iss & 0x1C0_0000) >> 22;
let op0 = (iss & 0x030_0000) >> 20;
let op2 = (iss & 0x00e_0000) >> 17;
let op1 = (iss & 0x001_c000) >> 14;
let crn = (iss & 0x000_3c00) >> 10;
let rt = (iss & 0x000_03e0) >> 5;
let crm = (iss & 0x000_001e) >> 1;
let dir = iss & 0x000_0001;
/*
print!("iss=0x{:x}, res0=0b{:03b}, op0=0b{:02b}\n
op2=0b{:03b}, op1=0b{:03b}, crn=0b{:04b}\n
rt=0b{:05b}, crm=0b{:04b}, dir=0b{:b}\n",
iss, res0, op0, op2, op1, crn, rt, crm, dir);
*/
match (op0, op1, crn, crm, op2, dir) {
//MRS <Xt>, CNTFRQ_EL0
(0b11, 0b011, 0b1110, 0b0000, 0b000, 0b1) => {
let reg_val = cntfrq_el0();
stack.store_reg(rt as usize, reg_val);
//skip faulting instruction, A64 instructions are always 32-bits
stack.iret.elr_el1 += 4;
return true;
}
//MRS <Xt>, CNTPCT_EL0
(0b11, 0b011, 0b1110, 0b0000, 0b001, 0b1) => {
let reg_val = cntpct_el0();
stack.store_reg(rt as usize, reg_val);
//skip faulting instruction, A64 instructions are always 32-bits
stack.iret.elr_el1 += 4;
return true;
}
//MRS <Xt>, CNTVCT_EL0
(0b11, 0b011, 0b1110, 0b0000, 0b010, 0b1) => {
let reg_val = cntvct_el0();
stack.store_reg(rt as usize, reg_val);
//skip faulting instruction, A64 instructions are always 32-bits
stack.iret.elr_el1 += 4;
return true;
}
_ => {}
}
false
}
}
exception_stack!(synchronous_exception_at_el1_with_spx, |stack| {
unsafe {
if !pf_inner(
stack,
exception_code(stack.iret.esr_el1),
"sync_exc_el1_spx",
) {
println!("Synchronous exception at EL1 with SPx");
if exception_code(stack.iret.esr_el1) == 0b100101 {
let far_el1 = far_el1();
println!("FAR_EL1 = 0x{:08x}", far_el1);
} else if exception_code(stack.iret.esr_el1) == 0b100100 {
let far_el1 = far_el1();
println!("USER FAR_EL1 = 0x{:08x}", far_el1);
}
stack.trace();
loop {}
}
}
});
unsafe fn pf_inner(stack: &mut InterruptStack, ty: u8, from: &str) -> bool {
unsafe {
match ty {
// "Data Abort taken from a lower Exception level"
0b100100 => instr_data_abort_inner(stack, true, false, from),
// "Data Abort taken without a change in Exception level"
0b100101 => instr_data_abort_inner(stack, false, false, from),
// "Instruction Abort taken from a lower Exception level"
0b100000 => instr_data_abort_inner(stack, true, true, from),
// "Instruction Abort taken without a change in Exception level"
0b100001 => instr_data_abort_inner(stack, false, true, from),
// "Trapped MSR, MRS or System instruction execution in AArch64 state"
0b011000 => instr_trapped_msr_mrs_inner(stack, true, true, from),
_ => return false,
}
}
}
exception_stack!(synchronous_exception_at_el0, |stack| {
unsafe {
match exception_code(stack.iret.esr_el1) {
0b010101 => {
let scratch = &stack.scratch;
let mut token = CleanLockToken::new();
let ret = syscall::syscall(
scratch.x8, scratch.x0, scratch.x1, scratch.x2, scratch.x3, scratch.x4,
scratch.x5, &mut token,
);
stack.scratch.x0 = ret;
}
ty => {
if !pf_inner(stack, ty as u8, "sync_exc_el0") {
error!(
"FATAL: Not an SVC induced synchronous exception (ty={:b})",
ty
);
println!("FAR_EL1: {:#0x}", far_el1());
//crate::debugger::debugger(None);
stack.trace();
excp_handler(Exception {
kind: 0, // TODO
});
}
}
}
}
});
exception_stack!(unhandled_exception, |stack| {
println!("Unhandled exception");
stack.trace();
loop {}
});
impl ArchIntCtx for InterruptStack {
fn ip(&self) -> usize {
self.iret.elr_el1
}
fn recover_and_efault(&mut self) {
// Set the return value to nonzero to indicate usercopy failure (EFAULT), and emulate the
// return instruction by setting the return pointer to the saved LR value.
self.iret.elr_el1 = self.preserved.x30;
self.scratch.x0 = 1;
}
}
-420
View File
@@ -1,420 +0,0 @@
use crate::{panic, syscall::IntRegisters};
#[derive(Default)]
#[repr(C, packed)]
pub struct ScratchRegisters {
pub x0: usize,
pub x1: usize,
pub x2: usize,
pub x3: usize,
pub x4: usize,
pub x5: usize,
pub x6: usize,
pub x7: usize,
pub x8: usize,
pub x9: usize,
pub x10: usize,
pub x11: usize,
pub x12: usize,
pub x13: usize,
pub x14: usize,
pub x15: usize,
pub x16: usize,
pub x17: usize,
pub x18: usize,
pub _padding: usize,
}
impl ScratchRegisters {
pub fn dump(&self) {
println!("X0: {:>016X}", { self.x0 });
println!("X1: {:>016X}", { self.x1 });
println!("X2: {:>016X}", { self.x2 });
println!("X3: {:>016X}", { self.x3 });
println!("X4: {:>016X}", { self.x4 });
println!("X5: {:>016X}", { self.x5 });
println!("X6: {:>016X}", { self.x6 });
println!("X7: {:>016X}", { self.x7 });
println!("X8: {:>016X}", { self.x8 });
println!("X9: {:>016X}", { self.x9 });
println!("X10: {:>016X}", { self.x10 });
println!("X11: {:>016X}", { self.x11 });
println!("X12: {:>016X}", { self.x12 });
println!("X13: {:>016X}", { self.x13 });
println!("X14: {:>016X}", { self.x14 });
println!("X15: {:>016X}", { self.x15 });
println!("X16: {:>016X}", { self.x16 });
println!("X17: {:>016X}", { self.x17 });
println!("X18: {:>016X}", { self.x18 });
}
}
#[derive(Default)]
#[repr(C, packed)]
pub struct PreservedRegisters {
//TODO: is X30 a preserved register?
pub x19: usize,
pub x20: usize,
pub x21: usize,
pub x22: usize,
pub x23: usize,
pub x24: usize,
pub x25: usize,
pub x26: usize,
pub x27: usize,
pub x28: usize,
pub x29: usize,
pub x30: usize,
}
impl PreservedRegisters {
pub fn dump(&self) {
println!("X19: {:>016X}", { self.x19 });
println!("X20: {:>016X}", { self.x20 });
println!("X21: {:>016X}", { self.x21 });
println!("X22: {:>016X}", { self.x22 });
println!("X23: {:>016X}", { self.x23 });
println!("X24: {:>016X}", { self.x24 });
println!("X25: {:>016X}", { self.x25 });
println!("X26: {:>016X}", { self.x26 });
println!("X27: {:>016X}", { self.x27 });
println!("X28: {:>016X}", { self.x28 });
println!("X29: {:>016X}", { self.x29 });
println!("X30: {:>016X}", { self.x30 });
}
}
#[derive(Default)]
#[repr(C, packed)]
pub struct IretRegisters {
// occurred
// The exception vector disambiguates at which EL the interrupt
pub sp_el0: usize, // Shouldn't be used if interrupt occurred at EL1
pub esr_el1: usize,
pub spsr_el1: usize,
pub elr_el1: usize,
}
impl IretRegisters {
pub fn dump(&self) {
println!("ELR_EL1: {:>016X}", { self.elr_el1 });
println!("SPSR_EL1: {:>016X}", { self.spsr_el1 });
println!("ESR_EL1: {:>016X}", { self.esr_el1 });
println!("SP_EL0: {:>016X}", { self.sp_el0 });
}
}
#[derive(Default)]
#[repr(C, packed)]
pub struct InterruptStack {
pub iret: IretRegisters,
pub scratch: ScratchRegisters,
pub preserved: PreservedRegisters,
}
impl InterruptStack {
pub fn init(&mut self) {}
pub fn frame_pointer(&self) -> usize {
self.preserved.x29
}
pub fn stack_pointer(&self) -> usize {
self.iret.sp_el0
}
pub fn set_stack_pointer(&mut self, sp: usize) {
self.iret.sp_el0 = sp;
}
pub fn sig_archdep_reg(&self) -> usize {
self.scratch.x0
}
pub fn set_instr_pointer(&mut self, ip: usize) {
self.iret.elr_el1 = ip;
}
pub fn instr_pointer(&self) -> usize {
self.iret.elr_el1
}
pub fn set_arg1(&mut self, arg_opt: Option<usize>) {
if let Some(arg) = arg_opt {
self.scratch.x1 = arg;
}
}
pub fn dump(&self) {
self.iret.dump();
self.scratch.dump();
self.preserved.dump();
}
pub fn trace(&self) {
self.dump();
unsafe {
panic::user_stack_trace(&self);
panic::stack_trace();
}
}
/// Saves all registers to a struct used by the proc:
/// scheme to read/write registers.
pub fn save(&self, all: &mut IntRegisters) {
/*TODO: aarch64 registers
all.elr_el1 = self.iret.elr_el1;
all.spsr_el1 = self.iret.spsr_el1;
all.esr_el1 = self.iret.esr_el1;
all.sp_el0 = self.iret.sp_el0;
all.padding = 0;
*/
all.x30 = self.preserved.x30;
all.x29 = self.preserved.x29;
all.x28 = self.preserved.x28;
all.x27 = self.preserved.x27;
all.x26 = self.preserved.x26;
all.x25 = self.preserved.x25;
all.x24 = self.preserved.x24;
all.x23 = self.preserved.x23;
all.x22 = self.preserved.x22;
all.x21 = self.preserved.x21;
all.x20 = self.preserved.x20;
all.x19 = self.preserved.x19;
all.x18 = self.scratch.x18;
all.x17 = self.scratch.x17;
all.x16 = self.scratch.x16;
all.x15 = self.scratch.x15;
all.x14 = self.scratch.x14;
all.x13 = self.scratch.x13;
all.x12 = self.scratch.x12;
all.x11 = self.scratch.x11;
all.x10 = self.scratch.x10;
all.x9 = self.scratch.x9;
all.x8 = self.scratch.x8;
all.x7 = self.scratch.x7;
all.x6 = self.scratch.x6;
all.x5 = self.scratch.x5;
all.x4 = self.scratch.x4;
all.x3 = self.scratch.x3;
all.x2 = self.scratch.x2;
all.x1 = self.scratch.x1;
all.x0 = self.scratch.x0;
}
/// Loads all registers from a struct used by the proc:
/// scheme to read/write registers.
pub fn load(&mut self, all: &IntRegisters) {
/*TODO: aarch64 registers
self.iret.elr_el1 = all.elr_el1;
self.iret.spsr_el1 = all.spsr_el1;
self.iret.esr_el1 = all.esr_el1;
self.iret.sp_el0 = all.sp_el0;
*/
self.preserved.x30 = all.x30;
self.preserved.x29 = all.x29;
self.preserved.x28 = all.x28;
self.preserved.x27 = all.x27;
self.preserved.x26 = all.x26;
self.preserved.x25 = all.x25;
self.preserved.x24 = all.x24;
self.preserved.x23 = all.x23;
self.preserved.x22 = all.x22;
self.preserved.x21 = all.x21;
self.preserved.x20 = all.x20;
self.preserved.x19 = all.x19;
self.scratch.x18 = all.x18;
self.scratch.x17 = all.x17;
self.scratch.x16 = all.x16;
self.scratch.x15 = all.x15;
self.scratch.x14 = all.x14;
self.scratch.x13 = all.x13;
self.scratch.x12 = all.x12;
self.scratch.x11 = all.x11;
self.scratch.x10 = all.x10;
self.scratch.x9 = all.x9;
self.scratch.x8 = all.x8;
self.scratch.x7 = all.x7;
self.scratch.x6 = all.x6;
self.scratch.x5 = all.x5;
self.scratch.x4 = all.x4;
self.scratch.x3 = all.x3;
self.scratch.x2 = all.x2;
self.scratch.x1 = all.x1;
self.scratch.x0 = all.x0;
}
/// Store a specific generic registers
pub fn store_reg(&mut self, idx: usize, val: usize) {
match idx {
0 => self.scratch.x0 = val,
1 => self.scratch.x1 = val,
2 => self.scratch.x2 = val,
3 => self.scratch.x3 = val,
4 => self.scratch.x4 = val,
5 => self.scratch.x5 = val,
6 => self.scratch.x6 = val,
7 => self.scratch.x7 = val,
8 => self.scratch.x8 = val,
9 => self.scratch.x9 = val,
10 => self.scratch.x10 = val,
11 => self.scratch.x11 = val,
12 => self.scratch.x12 = val,
13 => self.scratch.x13 = val,
14 => self.scratch.x14 = val,
15 => self.scratch.x15 = val,
16 => self.scratch.x16 = val,
17 => self.scratch.x17 = val,
18 => self.scratch.x18 = val,
19 => self.preserved.x19 = val,
20 => self.preserved.x20 = val,
21 => self.preserved.x21 = val,
22 => self.preserved.x22 = val,
23 => self.preserved.x23 = val,
24 => self.preserved.x24 = val,
25 => self.preserved.x25 = val,
26 => self.preserved.x26 = val,
27 => self.preserved.x27 = val,
28 => self.preserved.x28 = val,
29 => self.preserved.x29 = val,
30 => self.preserved.x30 = val,
_ => {}
}
}
//TODO
pub fn set_singlestep(&mut self, _singlestep: bool) {}
}
#[macro_export]
macro_rules! push_scratch {
() => {
"
// Push scratch registers
str x18, [sp, #-16]!
stp x16, x17, [sp, #-16]!
stp x14, x15, [sp, #-16]!
stp x12, x13, [sp, #-16]!
stp x10, x11, [sp, #-16]!
stp x8, x9, [sp, #-16]!
stp x6, x7, [sp, #-16]!
stp x4, x5, [sp, #-16]!
stp x2, x3, [sp, #-16]!
stp x0, x1, [sp, #-16]!
"
};
}
#[macro_export]
macro_rules! pop_scratch {
() => {
"
// Pop scratch registers
ldp x0, x1, [sp], #16
ldp x2, x3, [sp], #16
ldp x4, x5, [sp], #16
ldp x6, x7, [sp], #16
ldp x8, x9, [sp], #16
ldp x10, x11, [sp], #16
ldp x12, x13, [sp], #16
ldp x14, x15, [sp], #16
ldp x16, x17, [sp], #16
ldr x18, [sp], #16
"
};
}
#[macro_export]
macro_rules! push_preserved {
() => {
"
// Push preserved registers
stp x29, x30, [sp, #-16]!
stp x27, x28, [sp, #-16]!
stp x25, x26, [sp, #-16]!
stp x23, x24, [sp, #-16]!
stp x21, x22, [sp, #-16]!
stp x19, x20, [sp, #-16]!
"
};
}
#[macro_export]
macro_rules! pop_preserved {
() => {
"
// Pop preserved registers
ldp x19, x20, [sp], #16
ldp x21, x22, [sp], #16
ldp x23, x24, [sp], #16
ldp x25, x26, [sp], #16
ldp x27, x28, [sp], #16
ldp x29, x30, [sp], #16
"
};
}
#[macro_export]
macro_rules! push_special {
() => {
"
mrs x14, spsr_el1
mrs x15, elr_el1
stp x14, x15, [sp, #-16]!
mrs x14, sp_el0
mrs x15, esr_el1
stp x14, x15, [sp, #-16]!
"
};
}
#[macro_export]
macro_rules! pop_special {
() => {
"
ldp x14, x15, [sp], 16
msr esr_el1, x15
msr sp_el0, x14
ldp x14, x15, [sp], 16
msr elr_el1, x15
msr spsr_el1, x14
"
};
}
#[macro_export]
macro_rules! exception_stack {
($name:ident, |$stack:ident| $code:block) => {
#[unsafe(naked)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn $name(stack: &mut $crate::arch::aarch64::interrupt::InterruptStack) {
unsafe extern "C" fn inner($stack: &mut $crate::arch::aarch64::interrupt::InterruptStack) {
$code
}
core::arch::naked_asm!(
// Backup all userspace registers to stack
push_preserved!(),
push_scratch!(),
push_special!(),
// Call inner function with pointer to stack
"mov x29, sp",
"mov x0, sp",
"bl {}",
// Restore all userspace registers
pop_special!(),
pop_scratch!(),
pop_preserved!(),
"eret",
sym inner,
);
}
};
}
#[unsafe(naked)]
pub unsafe extern "C" fn enter_usermode() -> ! {
core::arch::naked_asm!(
"blr x28",
// Restore all userspace registers
pop_special!(),
pop_scratch!(),
pop_preserved!(),
"eret",
);
}
-56
View File
@@ -1,56 +0,0 @@
use crate::{arch::device::ROOT_IC_IDX, dtb::irqchip::IRQ_CHIP, sync::CleanLockToken};
use core::sync::atomic::Ordering;
// use crate::percpu::PercpuBlock;
unsafe fn irq_ack() -> (u32, Option<usize>) {
unsafe {
let ic = &mut IRQ_CHIP.irq_chip_list.chips[ROOT_IC_IDX.load(Ordering::Relaxed)].ic;
let irq = ic.irq_ack();
(irq, ic.irq_to_virq(irq))
}
}
exception_stack!(irq_at_el0, |_stack| {
unsafe {
let mut token = CleanLockToken::new();
let (irq, virq) = irq_ack();
if let Some(virq) = virq
&& virq < 1024
{
IRQ_CHIP.trigger_virq(virq as u32, &mut token);
} else {
println!("unexpected irq num {}", irq);
}
}
});
exception_stack!(irq_at_el1, |_stack| {
unsafe {
let mut token = CleanLockToken::new();
let (irq, virq) = irq_ack();
if let Some(virq) = virq
&& virq < 1024
{
IRQ_CHIP.trigger_virq(virq as u32, &mut token);
} else {
println!("unexpected irq num {}", irq);
}
}
});
/*
pub unsafe fn irq_handler_gentimer(irq: u32) {
GENTIMER.clear_irq();
{
*time::OFFSET.lock() += GENTIMER.clk_freq as u128;
}
timeout::trigger();
context::switch::tick();
trigger(irq);
GENTIMER.reload_count();
}
*/
-49
View File
@@ -1,49 +0,0 @@
//! Interrupt instructions
use core::arch::asm;
#[macro_use]
pub mod handler;
pub mod exception;
pub mod irq;
pub mod syscall;
pub mod trace;
pub use self::handler::InterruptStack;
/// Clear interrupts
#[inline(always)]
pub unsafe fn disable() {
unsafe {
asm!("msr daifset, #2");
}
}
/// Set interrupts and halt
/// This will atomically wait for the next interrupt
/// Performing enable followed by halt is not guaranteed to be atomic, use this instead!
#[inline(always)]
pub unsafe fn enable_and_halt() {
unsafe {
asm!("wfi", "msr daifclr, #2", "nop");
}
}
/// Set interrupts and nop
/// This will enable interrupts and allow the IF flag to be processed
/// Simply enabling interrupts does not gurantee that they will trigger, use this instead!
#[inline(always)]
pub unsafe fn enable_and_nop() {
unsafe {
asm!("msr daifclr, #2", "nop");
}
}
/// Halt instruction
#[inline(always)]
pub unsafe fn halt() {
unsafe {
asm!("wfi");
}
}
-49
View File
@@ -1,49 +0,0 @@
#[unsafe(no_mangle)]
pub unsafe extern "C" fn do_exception_unhandled() {}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn do_exception_synchronous() {}
#[allow(dead_code)]
#[repr(C, packed)]
pub struct SyscallStack {
pub elr_el1: usize,
pub padding: usize,
pub tpidr: usize,
pub tpidrro: usize,
pub rflags: usize,
pub esr: usize,
pub sp: usize,
pub lr: usize,
pub fp: usize,
pub x28: usize,
pub x27: usize,
pub x26: usize,
pub x25: usize,
pub x24: usize,
pub x23: usize,
pub x22: usize,
pub x21: usize,
pub x20: usize,
pub x19: usize,
pub x18: usize,
pub x17: usize,
pub x16: usize,
pub x15: usize,
pub x14: usize,
pub x13: usize,
pub x12: usize,
pub x11: usize,
pub x10: usize,
pub x9: usize,
pub x8: usize,
pub x7: usize,
pub x6: usize,
pub x5: usize,
pub x4: usize,
pub x3: usize,
pub x2: usize,
pub x1: usize,
pub x0: usize,
}
pub use super::handler::enter_usermode;
-32
View File
@@ -1,32 +0,0 @@
use core::arch::asm;
pub struct StackTrace {
pub fp: usize,
pub pc_ptr: *const usize,
}
impl StackTrace {
#[inline(always)]
pub unsafe fn start() -> Option<Self> {
unsafe {
let fp: usize;
asm!("mov {}, fp", out(reg) fp);
let pc_ptr = fp.checked_add(size_of::<usize>())?;
Some(StackTrace {
fp,
pc_ptr: pc_ptr as *const usize,
})
}
}
pub unsafe fn next(self) -> Option<Self> {
unsafe {
let fp = *(self.fp as *const usize);
let pc_ptr = fp.checked_add(size_of::<usize>())?;
Some(StackTrace {
fp: fp,
pc_ptr: pc_ptr as *const usize,
})
}
}
}
-30
View File
@@ -1,30 +0,0 @@
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum IpiKind {
Wakeup = 0x40,
Tlb = 0x41,
}
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum IpiTarget {
Other = 3,
}
#[inline(always)]
pub fn ipi(_kind: IpiKind, _target: IpiTarget) {
if cfg!(not(feature = "multi_core")) {
return;
}
// FIXME implement
}
#[inline(always)]
pub fn ipi_single(_kind: IpiKind, _target: &crate::percpu::PercpuBlock) {
if cfg!(not(feature = "multi_core")) {
return;
}
// FIXME implement
}
-23
View File
@@ -1,23 +0,0 @@
use crate::{
cpu_set::LogicalCpuId,
memory::{RmmA, RmmArch},
percpu::PercpuBlock,
};
impl PercpuBlock {
pub fn current() -> &'static Self {
unsafe { &*(crate::arch::device::cpu::registers::control_regs::tpidr_el1() as *const Self) }
}
}
#[cold]
pub unsafe fn init(cpu_id: LogicalCpuId) {
unsafe {
let frame = crate::memory::allocate_frame().expect("failed to allocate percpu memory");
let virt = RmmA::phys_to_virt(frame.base()).data() as *mut PercpuBlock;
virt.write(PercpuBlock::init(cpu_id));
crate::arch::device::cpu::registers::control_regs::tpidr_el1_write(virt as u64);
}
}
-71
View File
@@ -1,71 +0,0 @@
/// Constants like memory locations
pub mod consts;
/// Debugging support
pub mod debug;
/// Devices
pub mod device;
/// Interrupt instructions
pub mod interrupt;
/// Inter-processor interrupts
pub mod ipi;
/// Miscellaneous
pub mod misc;
/// Paging
pub mod paging;
/// Initialization and start function
pub mod start;
/// Stop function
pub mod stop;
// Interrupt vectors
pub mod vectors;
pub mod time;
pub use ::rmm::aarch64::AArch64Arch as CurrentRmmArch;
pub use arch_copy_to_user as arch_copy_from_user;
#[unsafe(naked)]
pub unsafe extern "C" fn arch_copy_to_user(dst: usize, src: usize, len: usize) -> u8 {
// x0, x1, x2
core::arch::naked_asm!(
"
.global __usercopy_start
__usercopy_start:
mov x4, x0
mov x0, 0
2:
cmp x2, 0
b.eq 3f
ldrb w3, [x1]
strb w3, [x4]
add x4, x4, 1
add x1, x1, 1
sub x2, x2, 1
b 2b
3:
ret
.global __usercopy_end
__usercopy_end:
"
);
}
pub const KFX_SIZE: usize = 1024;
// This function exists as the KFX size is dynamic on x86_64.
pub fn kfx_size() -> usize {
KFX_SIZE
}
-7
View File
@@ -1,7 +0,0 @@
/// Initialize MAIR
#[cold]
pub unsafe fn init() {
unsafe {
rmm::aarch64::init_mair();
}
}
-148
View File
@@ -1,148 +0,0 @@
//! This function is where the kernel sets up IRQ handlers
//! It is incredibly unsafe, and should be minimal in nature
//! It must create the IDT with the correct entries, those entries are
//! defined in other files inside of the `arch` module
use core::{arch::naked_asm, cell::SyncUnsafeCell, slice};
use fdt::Fdt;
use crate::{
allocator,
arch::{device, paging},
devices::graphical_debug,
dtb,
startup::KernelArgs,
};
/// Test of zero values in BSS.
static mut BSS_TEST_ZERO: usize = 0;
/// Test of non-zero values in data.
static mut DATA_TEST_NONZERO: usize = 0xFFFF_FFFF_FFFF_FFFF;
#[repr(C, align(16))]
struct StackAlign<T>(T);
static STACK: SyncUnsafeCell<StackAlign<[u8; 128 * 1024]>> =
SyncUnsafeCell::new(StackAlign([0; 128 * 1024]));
// FIXME use extern "custom"
#[unsafe(naked)]
#[unsafe(no_mangle)]
extern "C" fn kstart() {
naked_asm!("
// BSS should already be zero
adrp x9, {bss_test_zero}
ldr x9, [x9, :lo12:{bss_test_zero}]
cbnz x9, .Lkstart_crash
adrp x9, {data_test_nonzero}
ldr x9, [x9, :lo12:{data_test_nonzero}]
cbz x9, .Lkstart_crash
adrp x1, {stack}
add x1, x1, :lo12:{stack}
mov x2, {stack_size}-16
add sp, x1, x2
// Setup interrupt handlers
ldr x9, =exception_vector_base
msr vbar_el1, x9
mov lr, 0
b {start}
.Lkstart_crash:
mov x9, 0
br x9
",
bss_test_zero = sym BSS_TEST_ZERO,
data_test_nonzero = sym DATA_TEST_NONZERO,
stack = sym STACK,
stack_size = const size_of_val(&STACK),
start = sym start,
);
}
/// The entry to Rust, all things must be initialized
unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! {
unsafe {
let bootstrap = {
let args = args_ptr.read();
// Set up graphical debug
graphical_debug::init(args.env());
// Get hardware descriptor data
//TODO: use env {DTB,RSDT}_{BASE,SIZE}?
let hwdesc_data = if args.hwdesc_base != 0 {
Some(slice::from_raw_parts(
(crate::PHYS_OFFSET + args.hwdesc_base as usize) as *const u8,
args.hwdesc_size as usize,
))
} else {
None
};
let dtb_res = hwdesc_data
.ok_or(fdt::FdtError::BadPtr)
.and_then(|data| Fdt::new(data));
// Try to find serial port prior to logging
if let Ok(dtb) = &dtb_res {
dtb::serial::init_early(dtb);
}
info!("Redox OS starting...");
args.print();
// Initialize RMM
crate::startup::memory::init(&args, None, None);
// Initialize paging
paging::init();
crate::arch::misc::init(crate::cpu_set::LogicalCpuId::new(0));
// Setup kernel heap
allocator::init();
// Activate memory logging
crate::log::init();
// Initialize devices
match dtb_res {
Ok(dtb) => {
dtb::init(hwdesc_data.map(|slice| (slice.as_ptr() as usize, slice.len())));
device::init_devicetree(&dtb);
}
Err(err) => {
dtb::init(None);
warn!("failed to parse DTB: {}", err);
#[cfg(feature = "acpi")]
{
crate::acpi::init(args.acpi_rsdp());
}
}
}
args.bootstrap()
};
crate::startup::kmain(bootstrap);
}
}
#[repr(C, packed)]
#[allow(unused)]
pub struct KernelArgsAp {
cpu_id: u64,
page_table: u64,
stack_start: u64,
stack_end: u64,
}
/// Entry to rust for an AP
#[allow(unused)]
pub unsafe extern "C" fn kstart_ap(_args_ptr: *const KernelArgsAp) -> ! {
loop {}
}
-33
View File
@@ -1,33 +0,0 @@
use crate::sync::CleanLockToken;
use core::arch::asm;
pub unsafe fn kreset() -> ! {
unsafe {
println!("kreset");
asm!("hvc #0",
in("x0") 0x8400_0009_usize,
options(noreturn),
)
}
}
pub unsafe fn emergency_reset() -> ! {
unsafe {
asm!("hvc #0",
in("x0") 0x8400_0009_usize,
options(noreturn),
)
}
}
pub unsafe fn kstop(_token: &mut CleanLockToken) -> ! {
unsafe {
println!("kstop");
asm!("hvc #0",
in("x0") 0x8400_0008_usize,
options(noreturn),
)
}
}
-18
View File
@@ -1,18 +0,0 @@
use crate::{sync::CleanLockToken, time::NANOS_PER_SEC};
pub fn monotonic_absolute(_token: &mut CleanLockToken) -> u128 {
//TODO: aarch64 generic timer counter
let ticks: usize;
unsafe { core::arch::asm!("mrs {}, cntpct_el0", out(reg) ticks) };
let freq: usize;
unsafe { core::arch::asm!("mrs {}, cntfrq_el0", out(reg) freq) };
ticks as u128 * NANOS_PER_SEC / freq as u128
}
pub fn monotonic_resolution() -> u128 {
let freq: usize;
unsafe { core::arch::asm!("mrs {}, cntfrq_el0", out(reg) freq) };
NANOS_PER_SEC / freq as u128
}
-112
View File
@@ -1,112 +0,0 @@
core::arch::global_asm!(
"
// Exception vector stubs
//
// Unhandled exceptions spin in a wfi loop for the moment
// This can be macro-ified
.globl exception_vector_base
.align 11
exception_vector_base:
// Synchronous
.align 7
__vec_00:
b synchronous_exception_at_el1_with_sp0
b __vec_00
// IRQ
.align 7
__vec_01:
b irq_at_el1
b __vec_01
// FIQ
.align 7
__vec_02:
b unhandled_exception
b __vec_02
// SError
.align 7
__vec_03:
b unhandled_exception
b __vec_03
// Synchronous
.align 7
__vec_04:
b synchronous_exception_at_el1_with_spx
b __vec_04
// IRQ
.align 7
__vec_05:
b irq_at_el1
b __vec_05
// FIQ
.align 7
__vec_06:
b unhandled_exception
b __vec_06
// SError
.align 7
__vec_07:
b unhandled_exception
b __vec_07
// Synchronous
.align 7
__vec_08:
b synchronous_exception_at_el0
b __vec_08
// IRQ
.align 7
__vec_09:
b irq_at_el0
b __vec_09
// FIQ
.align 7
__vec_10:
b unhandled_exception
b __vec_10
// SError
.align 7
__vec_11:
b unhandled_exception
b __vec_11
// Synchronous
.align 7
__vec_12:
b unhandled_exception
b __vec_12
// IRQ
.align 7
__vec_13:
b unhandled_exception
b __vec_13
// FIQ
.align 7
__vec_14:
b unhandled_exception
b __vec_14
// SError
.align 7
__vec_15:
b unhandled_exception
b __vec_15
.align 7
exception_vector_end:
"
);
-27
View File
@@ -1,27 +0,0 @@
#[cfg(target_arch = "aarch64")]
#[macro_use]
pub mod aarch64;
#[cfg(target_arch = "aarch64")]
pub use self::aarch64::*;
#[cfg(target_arch = "x86")]
#[macro_use]
pub mod x86;
#[cfg(target_arch = "x86")]
pub use self::x86::*;
#[cfg(target_arch = "x86_64")]
#[macro_use]
pub mod x86_64;
#[cfg(target_arch = "x86_64")]
pub use self::x86_64::*;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[macro_use]
mod x86_shared;
#[cfg(target_arch = "riscv64")]
#[macro_use]
pub mod riscv64;
#[cfg(target_arch = "riscv64")]
pub use self::riscv64::*;

Some files were not shown because too many files have changed in this diff Show More