438 Commits

Author SHA1 Message Date
vasilito 7e9cffd20a 0.3.0: restore legacy openat_with_filter / unlinkat_with_filter wrappers 2026-07-06 20:58:49 +03:00
vasilito 2c06be31a7 0.3.0: restore legacy syscall wrappers and constants (openat/dup/sendfd/close) 2026-07-06 19:20:45 +03:00
vasilito 488ed0c597 0.3.0: preserve legacy SYS_OPENAT_WITH_FILTER and SYS_UNLINKAT_WITH_FILTER constants 2026-07-06 19:01:56 +03:00
vasilito bf54ba8436 0.3.0: preserve legacy SYS_SENDFD constant for kernel/userspace ABI 2026-07-06 19:00:05 +03:00
vasilito 812d74e0cf 0.3.0: add SYS_OPENAT and SYS_DUP aliases for upstream 0.9.0 renamed constants 2026-07-06 18:56:10 +03:00
vasilito e5450de63a 0.3.0: add O_CLOEXEC and F_DUPFD_CLOEXEC constants for libredox ABI compatibility 2026-07-06 18:52:08 +03:00
vasilito 38ae8df3d4 0.3.0: bump version to 0.9.0+rb0.3.0 and add Red Bear author attribution 2026-07-06 18:40:09 +03:00
vasilito 0de981031c syscall: add AcpiVerb::SetS3WakingVector and AcpiVerb::EnterS3 (Phase II.X.W)
Phase II.X.W: extend the AcpiVerb enum with two new variants
for the S3 round-trip:

* AcpiVerb::SetS3WakingVector (= 5): acpid writes the
  kernel's S3 resume trampoline address to FACS.
  Mirrors Linux 7.1's acpi_set_firmware_waking_vector.
  The 8-byte write payload is the address in
  little-endian. A zero payload is a sentinel for 'use the
  kernel's default trampoline address' (s3_trampoline
  symbol).
* AcpiVerb::EnterS3 (= 6): acpid requests the kernel to
  enter S3. The kernel's stop::enter_s3() reads the
  SLP_TYP value from S3_SLP_TYP and does the PM1 register
  write. The actual S3 entry happens via acpid writing
  to /scheme/sys/kstop.

Hardware-agnostic: works on any x86_64 system with
standard ACPI S3 support (Dell, HP, Lenovo, LG Gram 14).
On Modern Standby-only systems (LG Gram 16 (2025)), the
kernel never enters S3 so these verbs are no-ops.
2026-07-06 18:39:14 +03:00
vasilito 83b228856a redox_syscall: add EnterS2Idle/ExitS2Idle AcpiVerb variants (Phase I/J)
Phase I/J: hardware-agnostic s2idle / Modern Standby
support. These AcpiVerb variants are needed on any
platform with Modern Standby firmware (Dell, HP, Lenovo,
LG Gram, etc.), not just LG Gram. They mirror Linux 7.1:

* EnterS2Idle (= 3) — s2idle_enter() in
  kernel/power/suspend.c:91
* ExitS2Idle (= 4) — s2idle_wake() in
  kernel/power/suspend.c:133

The version field stays at upstream 0.8.1. We do NOT
bump the version — this is the durable overlay pattern
(per AGENTS.md 'GOLDEN RULE — Red Bear adapts to
upstream, never the reverse'). Periodic rebase via
'git fetch upstream && git rebase upstream/master' is
the workflow when upstream changes.

The patch file backing this commit is at
local/patches/syscall/P1-acpiverb-enter-exit-s2idle.patch
in the outer RedBear-OS repo.
2026-07-06 18:39:14 +03:00
Ibuki Omatsu 4bb9233355 refactor: replace auto fd allocation with reservations and *_into variants 2026-07-01 07:51:53 -06:00
4lDO2 79cb6d9057 Add AcpiVerb 2026-06-02 15:38:05 +02:00
4lDO2 a358928d58 Add additional proc scheme & addrsp verbs. 2026-06-02 13:32:51 +02:00
R Aadarsh 2708fc84ac Add FileTableVerb 2026-05-28 21:35:50 +05:30
Wildan M cde72e8f03 Update CI 2026-05-27 13:02:46 +07:00
Ibuki Omatsu 7a1409a910 feat: Add multiple fds variant for call and std_fs_call 2026-05-27 15:26:37 +02:00
Wildan M aa2a276910 Add EVENT_TIMEOUT_ID 2026-05-23 08:38:21 +07:00
Wildan M 9ff180dd8e Add TimeSpec conversion utility 2026-05-23 08:38:13 +07:00
Jeremy Soller e3c2b74daf 0.7.5 2026-05-04 12:30:22 -06:00
Jeremy Soller d3c6430dc0 Align dirent headers 2026-05-04 12:28:53 -06:00
Akshit Gaur 271374cd4d Bump version 2026-04-06 14:44:29 +05:30
Akshit Gaur 23760e59f7 Add prio field to ProcSchemeAttrs 2026-03-23 22:45:24 +05:30
Ibuki.O 68b511f7a0 feat: Enable StdFsCallKinds except for unlinkat 2026-02-26 14:25:25 +09:00
Jeremy Soller 548f9b0197 0.7.2 2026-02-24 08:09:29 -07:00
Anhad Singh 0a7bf437bd feat(schemev2): detach
This commit adds:
* `CqeOpcode::RespondAndNotifyOnDetach`
* `Opcode::Detach`

Signed-off-by: Anhad Singh <andypython@protonmail.com>
2026-02-20 19:57:28 +11:00
Anhad Singh 374b33f2e4 feat(stdfscall): flocking
Signed-off-by: Anhad Singh <andypython@protonmail.com>
2026-02-12 11:25:35 +11:00
Jeremy Soller 95f4fed4e2 Implement fscall RFC 2026-02-11 12:11:07 -07:00
4lDO2 178461f6fb Remove remnants of the old packet format. 2025-12-27 15:10:54 +01:00
Ibuki Omatsu 208cf4b6ff Remove SYS_OPEN, SYS_UNLINK, and SYS_RMDIR. Add namespace related features 2025-12-27 07:00:21 -07:00
Anhad Singh 2d11366fd1 misc(sigabi): is_parent_sigchld -> stop_or_continue
* The original argument `is_parent_sigchld` was misleading as it
   applied to any `SIGCHLD` sent to the parent (exit and stop/continue).
   `SA_NOCLDSTOP` should only suppress stop and continue events, not
   exits.
* Document the behaviour.

Signed-off-by: Anhad Singh <andypython@protonmail.com>
2025-12-17 14:05:50 +11:00
Ibuki Omatsu 1cf631ce3f Introduce syscall6. Add unlinkat and remove unlink and rmdir. 2025-12-16 07:07:01 -07:00
Jeremy Soller 2d346f1d61 Add F_DUPFD_CLOEXEC 2025-11-17 17:38:30 -07:00
Ibuki Omatsu 15aa8e32ef Define GlobalSchemes. 2025-10-04 16:54:47 +02:00
Ribbon aedadf4d5c Improve the README 2025-07-31 01:15:13 -03:00
Ibuki Omatsu a9880ccf50 feat: Add UPPER_FDTBL_TAG 2025-07-27 07:10:36 -06:00
Ibuki Omatsu 1cfe90df24 feat: Add flags for bulk fd passing. 2025-07-26 06:58:26 -06:00
Ibuki Omatsu 95f15d3a8f feat: Add FsCall. 2025-07-21 07:52:41 -06:00
Darley Barreto 61f7b1bc31 Update openat function to include fcntl_flags parameter 2025-07-12 14:26:17 -03:00
Ibuki Omatsu eb4c83bd50 feat: Add MODE_SOCK to flags. 2025-07-04 14:04:53 -06:00
Ron Williams f821e2ceb7 Bump minor version due to change in filetype constants 2025-06-10 12:58:32 -07:00
Ron Williams 8df03bb6f9 Match DirentKind to include/bits/dirent.h 2025-06-10 13:44:27 -06:00
Jeremy Soller fe32c6b89d 0.5.12: Replace link with flink 2025-05-03 12:15:52 -06:00
4lDO2 8f646a63ac Remove commented-out ThreadEvent. 2025-04-19 13:03:51 +02:00
4lDO2 8a80d1f58b Add debug_name to ProcSchemeAttrs. 2025-04-19 01:04:42 +02:00
4lDO2 c52c4fec53 Move most sig numbers to redox_rt. 2025-04-16 11:58:21 +02:00
4lDO2 8fa422b2c9 Add Exception stubs for aarch64+riscv64. 2025-04-15 16:37:55 +02:00
4lDO2 8d3e6e5b49 WIP: exception handling interfaces. 2025-04-15 16:30:04 +02:00
4lDO2 c9d824f406 Move RtSigInfo definition to redox-rt. 2025-04-13 20:34:17 +02:00
4lDO2 8909a0b233 Remove SYS_SIGDEQUEUE. 2025-04-13 20:24:47 +02:00
4lDO2 b23d5067c6 Add ContextVerb::Interrupt. 2025-04-07 17:40:45 +02:00
4lDO2 9db2be8663 Add ContextVerb with Stop and Unstop additionally. 2025-04-07 17:40:45 +02:00
4lDO2 30fed98a46 Remove iopl and virttophys. 2025-04-07 17:40:45 +02:00
4lDO2 9a1742f3d3 Add ProcSchemeVerb. 2025-04-07 17:40:45 +02:00
4lDO2 9081c7f2b9 Add ThreadEvent, ContextStatus. 2025-04-07 17:40:45 +02:00
4lDO2 3afd6a2654 Impl Debug for DirentBuf. 2025-04-07 17:40:45 +02:00
4lDO2 9d1b81b92f Remove WaitFlags etc. 2025-04-07 17:40:44 +02:00
4lDO2 3d5e934d16 Add ProcSchemeAttrs. 2025-04-07 17:40:44 +02:00
4lDO2 c68c68041a Remove syscalls replaced by the proc manager. 2025-04-07 17:40:44 +02:00
4lDO2 080f2003cd Rustfmt, add CallFlags::CONSUME. 2025-04-05 17:19:13 +02:00
4lDO2 68aac400cc 0.5.11 2025-04-05 17:14:04 +02:00
4lDO2 3c4b7dd55d Fix AsRef unsoundness for path syscalls. 2025-04-05 17:13:50 +02:00
Darley Barreto 8b2bbae6e6 Add OpenAt Opcode 2025-04-05 10:50:48 -03:00
Darley Barreto a65f94f37e Add openat syscall 2025-04-01 18:01:02 -03:00
4lDO2 85792b752f 0.5.10 2025-03-03 18:52:23 +01:00
4lDO2 3117ea451c Implement SYS_CALL. 2025-03-03 14:15:17 +01:00
4lDO2 3edfe79f6f 0.5.9 2025-02-21 16:01:42 +01:00
4lDO2 ee30da9700 Add SqeOpcode::CloseMsg. 2025-02-21 14:41:37 +01:00
Jeremy Soller 3d786423f1 0.5.8 2024-12-11 07:37:10 -07:00
Anhad Singh 74336e8c04 feat(mremap): add KEEP_OLD flag
Signed-off-by: Anhad Singh <andypython@protonmail.com>
2024-12-10 16:42:40 +11:00
4lDO2 f3fc45a1b7 0.5.7 2024-09-30 09:07:54 +02:00
Andrey Turkin 5d37073bd4 Complete RISC-V64 support 2024-09-29 16:52:54 +03:00
4lDO2 c910533bcb Remove SYS_UMASK.
Now that all POSIX open-related calls go through relibc, this value can
be downgraded to a (process-local) global variable in userspace.
2024-09-25 19:50:45 +02:00
4lDO2 d4bb34e06c 0.5.6 2024-09-25 18:32:47 +02:00
Kamil Koczurek 909f95f349 Fix panic in dirent Buffer::split_at 2024-09-25 12:38:52 +02:00
4lDO2 718601e0ae 0.5.5 2024-09-24 18:58:35 +02:00
4lDO2 c2c69e6282 Fix cargo test 2024-09-23 21:04:56 +02:00
4lDO2 af8865416b Add sender_infos for idempotent signals. 2024-09-23 21:03:55 +02:00
4lDO2 984f9cf714 Add RtSigInfo. 2024-09-23 21:03:55 +02:00
4lDO2 b32e679437 Add syscalls for an initial in-kernel rtsig impl. 2024-09-23 21:03:55 +02:00
4lDO2 7b834cb3d5 Add minimal realtime sig constants. 2024-09-23 21:03:52 +02:00
4lDO2 e4cc6bfcd5 Make arch module available on nonredox.
This both reduces IDE confusion, and allows compiling the kernel for
host, in order to run unit tests.
2024-09-14 10:57:42 +02:00
4lDO2 c751f76248 0.5.4 2024-09-11 23:25:00 +02:00
4lDO2 4d159ce1c7 WIP: Add next_opaque_id 2024-09-10 09:10:16 +02:00
4lDO2 c043410366 Document (sub)Buffer OOB write ability. 2024-09-09 11:14:57 +02:00
4lDO2 99d5ea8edf Make sure unused header bytes are cleared. 2024-09-09 11:04:21 +02:00
4lDO2 25162a0a08 Future-proof dirent interface. 2024-09-08 22:22:14 +02:00
4lDO2 eefe3c2e3c Add SYS_GETDENTS 2024-09-08 22:22:13 +02:00
4lDO2 227f7fd487 Convert repr(packed) to repr(C, packed).
This is required by crates like bytemuck.
2024-09-08 20:46:07 +02:00
4lDO2 1d098c4e1d Rustfmt. 2024-09-08 20:32:32 +02:00
4lDO2 99f49db824 Also read pctl in currently_pending_unblocked. 2024-07-17 17:30:08 +02:00
Jeremy Soller 6128f1acf0 0.5.3 2024-07-15 09:29:54 -06:00
4lDO2 387f1c3326 Check SA_NOCLDSTOP in sig_will_ign. 2024-07-04 15:10:15 +02:00
4lDO2 f0483e3b33 Fix i686 CAS. 2024-07-03 13:47:00 +02:00
4lDO2 a8dfcdf7df Add FakeThread test abstraction. 2024-07-01 13:09:45 +02:00
4lDO2 9f59be32eb Add loom test, and move signal ABI to separate mod. 2024-06-30 12:01:04 +02:00
4lDO2 0bf7b6e73b Put sigaction-like struct in shmem. 2024-06-29 13:54:02 +02:00
4lDO2 87b8ac6581 Add currently_pending_unblocked. 2024-06-28 16:40:01 +02:00
4lDO2 9609c7bc26 Implement CAS-based 64-bit fetch_add on i686. 2024-06-27 21:29:22 +02:00
4lDO2 bda8fe89c6 Reduce saved sig registers to ip and 'archdep'. 2024-06-27 11:13:49 +02:00
4lDO2 bf01feb121 Add lock prefix to cmpxchg8b :) 2024-06-26 12:50:28 +02:00
4lDO2 b30d93aed0 Re-document control word mask as 'unmask'. 2024-06-25 15:10:54 +02:00
4lDO2 d9a6e334e0 Fix cmpxchg8b invocation. 2024-06-25 15:10:54 +02:00
4lDO2 6345199629 Fix i686. 2024-06-25 15:10:54 +02:00
4lDO2 0929c6500c Implement AtomicU64 for i686 using cmpxchg8b. 2024-06-25 15:10:54 +02:00
4lDO2 47c186630e Improve signal data structure defs. 2024-06-25 15:10:54 +02:00
4lDO2 0c1162979a Add preliminary nocldstop signal bit. 2024-06-25 15:10:54 +02:00
4lDO2 0b2a05ee1f Add sig proc control struct. 2024-06-25 15:10:54 +02:00
4lDO2 e154b74151 Add sig_bit. 2024-06-25 15:10:53 +02:00
4lDO2 b7d65e6b9d Add saved_rflags, some other unrelated flags. 2024-06-25 15:10:53 +02:00
4lDO2 2f944328e5 Remove unused defs, add 'inhibit' sigcontrol flag. 2024-06-25 15:10:53 +02:00
4lDO2 ffbef13397 Remove SignalStack, add Sigcontrol struct. 2024-06-25 15:10:53 +02:00
4lDO2 bd79228c7e Remove sig{procmask,action,return}. 2024-06-25 15:10:53 +02:00
4lDO2 13a0b9b5d5 Change SetSighandlerData. 2024-06-25 15:10:53 +02:00
4lDO2 0a83083aca Remove integration tests from CI.
This made sense when the syscall ABI was stable, but the syscall CI has
been failing for years by now. It's better to have a minimal CI
enforcing unit tests (todo) pass and all arches compile. All integration
tests should instead be done in acid.
2024-06-25 13:07:56 +02:00
4lDO2 10ce552835 0.5.2 2024-06-14 14:01:48 +02:00
Jacob Lorentzon ef099bdd2e Improved scheme protocol with offset+flags as read/write args 2024-06-14 11:18:10 +00:00
4lDO2 3e9d68c6b2 Fix compilation for nonredox. 2024-03-17 18:09:50 +01:00
4lDO2 44e0cb32df 0.5.1 2024-03-17 18:07:59 +01:00
4lDO2 7163519226 Implement Clone and Copy for Error. 2024-03-12 17:15:36 +01:00
4lDO2 ba54545696 Add SetSighandlerData. 2024-03-12 17:15:36 +01:00
4lDO2 74f2e81992 Use 64-bit masks in sigprocmask. 2024-03-12 17:15:36 +01:00
4lDO2 523a210364 Expand SignalStack struct. 2024-03-12 17:15:20 +01:00
4lDO2 0c464e7421 Add SignalStack, shrink sa_mask. 2024-03-12 17:15:20 +01:00
4lDO2 553370e80e 0.5.0 2024-03-12 16:59:03 +01:00
4lDO2 0100632a4b Add KSMSG_CANCEL. 2024-03-12 16:48:52 +01:00
4lDO2 612dceed00 Remove obsolete tests for fexec and clone. 2024-03-12 16:47:10 +01:00
4lDO2 5948affdd5 Remove SYS_PHYSMAP. 2023-12-09 12:20:36 +01:00
4lDO2 245cdf9caa Remove physalloc* and physfree 2023-12-09 12:20:36 +01:00
4lDO2 adff625d3d Remove Dma module. 2023-12-09 12:20:36 +01:00
4lDO2 727bd870e0 Add GRANT_PHYS_CONTIGUOUS. 2023-12-09 12:20:36 +01:00
Anshul Gupta 9e23908eec Updates bitflags to 2.4
Attempts to preserve backwards compatibility by deriving additional
traits & adding a deprecated unsafe function `from_bits_unchecked`.

However, the replacement of `.bits` field to `.bits()` method is a
breaking change.
2023-11-15 17:04:26 -08:00
4lDO2 06d569933f Add SYS_SENDFD 2023-10-19 15:00:28 +00:00
Jeremy Soller f94a5a7dca 0.4.1: custom mmio read/write functions on x86 2023-09-08 11:14:11 -06:00
Jeremy Soller 1bef275ffc 0.4.0 2023-09-07 16:17:15 -06:00
4lDO2 745e9eb2bc Remove SYS_PIPE2. 2023-08-31 21:15:57 +02:00
Jeremy Soller a525620c81 Merge branch 'better_fmap' into 'master'
Demand paging

See merge request redox-os/syscall!77
2023-08-02 13:07:36 +00:00
4lDO2 f0976e1d60 Add MunmapFlags. 2023-07-21 11:08:59 +02:00
4lDO2 534267760f Add SYS_MREMAP. 2023-07-17 15:54:24 +02:00
4lDO2 ed28083b73 Add MMAP_PREP and MUNMAP to scheme traits. 2023-07-12 15:32:39 +02:00
4lDO2 ff149946c0 Add GrantDesc and GrantFlags. 2023-07-04 17:17:25 +02:00
4lDO2 d587771fc3 Add MAP_LAZY. 2023-07-01 15:13:26 +02:00
4lDO2 6024e1e1fa Remove scheme funmap methods. 2023-06-30 11:39:28 +02:00
4lDO2 eab01c8621 Add mmap kernel-scheme messages. 2023-06-29 12:27:10 +02:00
Jeremy Soller 3b298a50a1 Merge branch 'usercopy' into 'master'
Remove physunmap and impl Deref for SigAction

See merge request redox-os/syscall!74
2023-06-11 14:16:22 +00:00
4lDO2 ab09be840c Remove SYS_PHYSUNMAP. 2023-06-10 17:43:04 +02:00
4lDO2 ba87bc9ee9 Impl Deref for Sigaction. 2023-06-08 14:45:25 +02:00
Jeremy Soller 4d37b9fc1f Merge branch 'file_forwarding_v2' into 'master'
File forwarding v2

See merge request redox-os/syscall!73
2023-06-07 13:42:30 +00:00
4lDO2 918bb7e396 Add scheme-kernel response codes. 2023-05-28 11:02:19 +02:00
4lDO2 7c64f6a086 Use xopen and xdup in Scheme::handle. 2023-05-27 18:44:43 +02:00
4lDO2 9d3bd91bf1 Add xopen and xdup. 2023-05-27 16:58:41 +02:00
4lDO2 911ec6cadf Add a scheme-kernel message 'error code'. 2023-05-27 16:32:05 +02:00
Jeremy Soller abf7e59f5a 0.3.5 2023-03-14 09:24:09 -06:00
Jeremy Soller b6e8785011 Merge branch 'dep_of_std' into 'master'
Allow `std` to depend on `syscall`

See merge request redox-os/syscall!72
2023-03-14 15:23:41 +00:00
joboet 9d4ad380de allow std to depend on syscall 2023-03-13 14:27:11 +01:00
Jeremy Soller 64d2213c79 0.3.4 - Allow x86 to use cache for DMA 2022-08-31 07:48:25 -06:00
Jeremy Soller ded9f93bae 0.3.3 - Use PHYSMAP_NO_CACHE for Dma 2022-08-30 10:32:16 -06:00
Jeremy Soller 7eb24a17ce 0.3.2 - Improve aarch64 registers 2022-08-24 08:46:51 -06:00
Jeremy Soller a5d2639716 0.3.1 2022-08-15 15:02:56 -06:00
Jeremy Soller fe40b18d0a Merge branch 'userspace_cwd' into 'master'
Remove cwd related syscalls and SYS_CHMOD.

See merge request redox-os/syscall!71
2022-08-15 21:02:25 +00:00
4lDO2 b0c825ed32 Remove SYS_CHMOD from scheme handler too. 2022-08-15 13:40:15 +02:00
4lDO2 e7c9a81b15 Remove cwd related syscalls and SYS_CHMOD. 2022-08-12 13:04:13 +02:00
Jeremy Soller 90c9253678 Add IntRegisters and FloatRegisters for x86 32-bit 2022-07-29 18:39:24 -06:00
Jeremy Soller a8cc2b7e22 Add aarch64 EnvRegisters 2022-07-29 18:07:31 -06:00
Jeremy Soller fac87ee3c7 0.3.0 - userspace fexec 2022-07-27 09:33:16 -06:00
Jeremy Soller 105ca1d36e Merge branch 'userspace_fexec' into 'master'
Remove old exec/clone system calls in favor of userspace_fexec

See merge request redox-os/syscall!70
2022-07-27 15:31:29 +00:00
4lDO2 300b5e4a80 Add PAGE_SIZE for all archs, make it public. 2022-07-27 17:30:17 +02:00
4lDO2 d6af266119 Add "opcodes", for e.g. mmap on other addr spaces. 2022-07-27 10:48:48 +02:00
4lDO2 c7b84541dc Improve general flags for userspace_fexec.
First, auxiliary vectors are no longer left here in syscall, but remain
in relibc. The rationale behind removing them was that they are no
longer used by the kernel in any way, as exec is moved to userspace.

Second, a ptrace event for address space switches was added. This is to
allow tracers to reopen their mem file when that mem file points to
different memory (otherwise mem can be decoupled from address spaces and
simply use the one that the context uses at the time).
2022-07-27 10:48:48 +02:00
4lDO2 ab5c685978 Remove SYS_EXEC from existence 2022-07-27 10:48:48 +02:00
4lDO2 f407c6b70c Remove CloneFlags. 2022-07-27 10:48:48 +02:00
4lDO2 67cf0997dc Remove clone() and Daemon abstraction. 2022-07-27 10:48:48 +02:00
4lDO2 8e4529ae7c Make DMA code use whole pages in syscalls. 2022-07-27 10:48:48 +02:00
4lDO2 a16a1ef95c Add the CloneInfo struct to extend SYS_CLONE. 2022-07-27 10:48:48 +02:00
4lDO2 a88e2f72cb Derive traits for ExecMemRange, change flags type. 2022-07-27 10:48:47 +02:00
4lDO2 6d132345ee WIP: Replace fexec with exec. 2022-07-27 10:48:44 +02:00
Jeremy Soller 0c98fbd162 0.2.16 - expose io::Pio on x86 32-bit 2022-07-26 15:30:10 -06:00
Jeremy Soller aa7d36ed83 0.2.15 - fix x86 32-bit path 2022-07-22 16:22:32 -06:00
Jeremy Soller 22a5a68472 0.2.14 - add x86 32-bit syscalls 2022-07-22 16:18:42 -06:00
Jeremy Soller c4d3a40a96 0.2.13 - remove x86 and arm 32-bit syscalls 2022-03-30 10:32:42 -06:00
Jeremy Soller 30f29c3295 Merge branch 'update-toolchain-2022' into 'master'
Update toolchain, removing dependence on nightly features

See merge request redox-os/syscall!68
2022-03-23 18:05:17 +00:00
4lDO2 638c2d2a35 Fix aarch64 compilation.
This is to hopefully avoid having to update syscall everywhere again,
when fixing aarch64.
2022-03-19 22:05:42 +01:00
4lDO2 94de991da2 Bump cargo version. 2022-03-12 10:38:45 +01:00
4lDO2 418051ac78 Replace llvm_asm with asm to support stable Rust. 2022-03-12 10:36:30 +01:00
4lDO2 efb5b3ee5b Replace ! with Infallible for now. 2022-03-12 10:36:07 +01:00
Jeremy Soller 87ffd8b862 0.2.11 - add daemon abstraction 2022-02-28 15:03:51 -07:00
4lDO2 14796acfed Bump cargo version. 2021-08-06 15:01:56 +02:00
4lDO2 ca6aea9106 Merge branch 'fsgsbase' into 'master'
Add fsbase/gsbase reg struct and more ELF auxiliary vectors.

See merge request redox-os/syscall!67
2021-08-06 12:45:08 +00:00
4lDO2 519a09e964 Add fs/gsbase reg struct and more ELF aux vectors. 2021-08-01 01:40:01 +02:00
Jeremy Soller 841b5f4221 Merge branch 'futex_wait64' into 'master'
Add FUTEX_WAIT64.

See merge request redox-os/syscall!66
2021-07-11 21:32:20 +00:00
4lDO2 fa5bc90ead Add FUTEX_WAIT64. 2021-07-08 13:22:35 +02:00
4lDO2 52fcd238db 0.2.9 2021-06-16 13:23:39 +02:00
Jeremy Soller 0d3facac18 Merge branch 'renamed-const_fn-feature' into 'master'
Specify feature(const_fn_trait_bound)

See merge request redox-os/syscall!65
2021-06-15 19:53:59 +00:00
coolreader18 9c7053d77f Specify feature(const_fn_trait_bound) 2021-06-15 19:53:58 +00:00
Jeremy Soller 0b51bddd28 0.2.8 2021-05-04 08:09:58 -06:00
Jeremy Soller ca3b4d2b94 Add riscv64 syscall implementation and register stubs 2021-05-04 08:09:28 -06:00
Jeremy Soller a1006e115f Remove unnecessary mut from aarch64 syscall macro 2021-05-04 08:09:12 -06:00
Jeremy Soller 87e913d991 0.2.7 2021-04-28 19:58:21 -06:00
Jeremy Soller a30fa69e38 Use correct syscall return register on aarch64 2021-04-28 19:57:49 -06:00
Jeremy Soller d283835028 0.2.6 2021-04-13 19:13:24 -06:00
Jeremy Soller 246f15d983 Merge remote-tracking branch 'origin/aarch64-rebase' 2021-04-13 19:12:27 -06:00
Jeremy Soller f07a954825 Merge branch 'fix-mmap-test' into 'master'
Get the mmap test to build

See merge request redox-os/syscall!63
2021-03-12 14:49:25 +00:00
4lDO2 040ebf99d9 Get the mmap test to build. 2021-03-07 16:16:40 +01:00
Jeremy Soller 0f9030f032 0.2.5 - paths are required to be utf8 2021-02-14 13:42:58 -07:00
Jeremy Soller bfdd0bb8d6 Merge branch 'basic-floating-point-support' into 'aarch64-rebase'
Basic floating point support

See merge request redox-os/syscall!62
2021-01-27 17:44:23 +00:00
Robin Randhawa beb880b706 aarch64: Basic Floating point/SIMD definition 2021-01-27 17:21:22 +00:00
Robin Randhawa 21cf9e31fc aarch64: Remove vestiges of old syscallX functions
Now that they have been replaced by the more modern macro magic
versions.
2021-01-27 17:20:33 +00:00
Robin Randhawa 3029f094a4 aarch64: Cleanups and alignment with x86_64 macros 2021-01-26 18:28:41 +00:00
Jeremy Soller b12d582d4d WIP: aarch64 2021-01-13 10:48:01 -07:00
Jeremy Soller ee9a92367e 0.2.4 - use correct arch when target_os is none 2021-01-11 07:00:34 -07:00
Jeremy Soller 3dc594c1ca Merge branch 'fix-0.2.2' into 'master'
Add missing imports, fixing 0.2.2.

See merge request redox-os/syscall!60
2021-01-11 13:40:56 +00:00
4lDO2 87158cd932 Bump cargo version to 0.2.3. 2021-01-11 14:39:08 +01:00
4lDO2 1f58eaf222 Add missing imports, fixing 0.2.2. 2021-01-11 14:35:32 +01:00
Jeremy Soller adfd9702bf 0.2.2 - fix compilation on aarch64 2021-01-10 10:51:07 -07:00
Jeremy Soller 45c7ca85b1 0.2.1 2020-08-27 09:35:55 -06:00
Jeremy Soller 405964e4fb Merge branch 'remove-brk' into 'master'
Remove brk

See merge request redox-os/syscall!59
2020-08-17 12:36:47 +00:00
jD91mZM2 5d53df836e Remove brk 2020-08-15 17:31:27 +02:00
Jeremy Soller 4115e0f435 Merge branch 'deprecate-stuff' into 'master'
Breaking Change: Goodbye, deprecated functions

See merge request redox-os/syscall!58
2020-08-14 15:21:08 +00:00
jD91mZM2 2bc9acc5c2 Breaking Change: Goodbye, deprecated functions 2020-08-14 15:18:44 +02:00
Jeremy Soller a7d066a3a2 Merge branch 'idiomatic-rust' into 'master'
Replace llvm_asm in x86_64 code with asm

See merge request redox-os/syscall!56
2020-08-03 13:36:35 +00:00
jD91mZM2 7f668b27cf Replace llvm_asm in x86_64 code with asm
Also removes the syscall1_clobber call, because `clone` now saves the
registers in the kernel anyway.
2020-08-01 16:52:16 +02:00
Jeremy Soller a0ea09ceb3 Merge branch 'memory' into 'master'
Add funmap2

See merge request redox-os/syscall!55
2020-07-30 12:58:12 +00:00
Jeremy Soller d183662648 Merge branch 'aj-0.2.0' into 'master'
Merge rename-to-llvm-asm into master; bump to 0.2.0

See merge request redox-os/syscall!54
2020-07-28 00:33:21 +00:00
Aaron Janse af890302eb bump version to 0.2.0 2020-07-27 17:11:41 -07:00
David L. Campbell 5cc40831ef Rename asm to llvm_asm. 2020-07-27 17:05:30 -07:00
jD91mZM2 bca011c7a0 Add funmap2 scheme call 2020-07-24 13:02:29 +02:00
jD91mZM2 7cc003d583 Add funmap2 2020-07-22 15:10:03 +02:00
Jeremy Soller c4ddf3fff4 Merge branch 'auxv' into 'master'
Implement auxiliary vector

See merge request redox-os/syscall!53
2020-07-21 14:16:24 +00:00
jD91mZM2 6346fd671e Add AT_PHDR 2020-07-10 12:48:22 +02:00
jD91mZM2 10994eaa96 Add auxiliery vector 2020-07-10 12:10:24 +02:00
Jeremy Soller 403c63adce Merge branch 'mmap2' into 'master'
Add mmap2

See merge request redox-os/syscall!52
2020-07-09 11:58:05 +00:00
4lDO2 27fcecb30f Add MAP_FIXED_NOREPLACE. 2020-07-08 18:17:11 +02:00
4lDO2 21b3cd34a8 Change the default fmap2 impl to use fmap. 2020-07-08 17:35:24 +02:00
4lDO2 6869e82a92 Add mmap2. 2020-07-08 15:36:59 +02:00
Jeremy Soller 122878874d Merge branch 'moar-gdb' into 'master'
Simplify EXEC catching + add signal handling

See merge request redox-os/syscall!51
2020-07-07 12:46:14 +00:00
jD91mZM2 a1af645cbf Remove bloated ptrace flags
I have had a think, and these flags are pointless. Since this is a
microkernel, there should always be exactly one way to do something, and
it should be the most flexible and simplest to implement. And you can
already just set breakpoints on the address provided by the signal
handler, and we'll give the entrypoint as an address to the fexec call.
2020-07-07 13:44:15 +02:00
jD91mZM2 69a8340f12 Add PTRACE_STOP_SIGNAL_HANDLER
Not entirely a useful thing, one *could* also put a breakpoint on the
handler address... I'll need to think about this some more.
2020-07-07 10:51:57 +02:00
Jeremy Soller fcebe8f225 Merge branch 'signed-seek' into 'master'
change seek sig to match posix - signed pos and result

See merge request redox-os/syscall!45
2020-06-27 11:48:09 +00:00
Jeremy Soller 7552c31f50 Merge branch 'ptrace-fexec' into 'master'
ptrace: stop on fexec

See merge request redox-os/syscall!50
2020-06-25 12:38:45 +00:00
jD91mZM2 c23d36e892 ptrace: stop on fexec 2020-06-21 17:53:29 +02:00
Graham MacDonald 940f3f1e30 move calc_seek_offset* to syscall 2020-06-19 23:53:05 +01:00
Graham MacDonald 1c394c447f change seek sig to match posix - signed pos and result 2020-06-19 23:31:20 +01:00
Jeremy Soller 9ecdc11d73 Merge branch 'physalloc3-fix' into 'master'
Add missing match item for `PartialAllocStrategy::Greedy`.

See merge request redox-os/syscall!48
2020-06-16 20:37:57 +00:00
4lDO2 eebf1c3856 Add missing match item for Greedy. 2020-06-16 21:50:56 +02:00
Jeremy Soller 783a03dc73 Merge branch 'complex-physalloc' into 'master'
Add a more complex physalloc syscall.

See merge request redox-os/syscall!46
2020-06-16 12:11:58 +00:00
4lDO2 3efc8d06be Add a more complex physalloc syscall. 2020-06-16 12:11:58 +00:00
Jeremy Soller 78bacde129 Merge branch 'ptrace-block' into 'master'
ptrace: Block on read, not on write

See merge request redox-os/syscall!47
2020-06-16 12:09:58 +00:00
jD91mZM2 1c637e72b2 ptrace: Block on read, not on write 2020-06-16 13:00:09 +02:00
Jeremy Soller 8d0015be86 Merge branch 'improved-mmio' into 'master'
Allow Mmio to work with const fn

See merge request redox-os/syscall!43
2020-02-10 14:40:29 +00:00
4lDO2 e380f6b3d4 Allow syscall::Result to accept other error types. 2020-02-10 15:32:30 +01:00
4lDO2 6cd82c63cd Enhance the Mmio API, and deprecate Mmio::new(). 2020-02-10 15:32:07 +01:00
Jeremy Soller 6beba08342 Merge branch 'dma-slices' into 'master'
Allow Dma<[T]>

See merge request redox-os/syscall!42
2020-02-03 01:22:56 +00:00
4lDO2 08a3800e87 Also allow ?Sized types to get the physaddr. 2020-02-02 11:44:55 +01:00
4lDO2 a2938fc7a3 Allow Dma to use ?Sized types, mostly for slices. 2020-02-02 11:44:55 +01:00
Jeremy Soller c969ac0699 Merge branch 'ptrace-end' into 'master'
Final few ptrace changes

See merge request redox-os/syscall!41
2020-01-20 17:59:22 +00:00
jD91mZM2 6ba71e7e06 Add ptrace exit breakpoint 2019-08-15 14:21:50 +02:00
Jeremy Soller 0e1e7d5d36 Merge branch 'ptrace-6' into 'master'
Ptrace overhaul & bitflags

See merge request redox-os/syscall!40
2019-07-31 20:29:42 +00:00
jD91mZM2 bf5e138def Allow catching int3 2019-07-31 16:43:28 +02:00
jD91mZM2 75a1e6f970 Add way to ignore signals 2019-07-30 15:54:07 +02:00
jD91mZM2 51f0ef2826 Implement Deref for PtraceFlags 2019-07-29 08:31:21 +02:00
jD91mZM2 a0581db1fa Merge ProtFlags and MapFlags 2019-07-28 17:36:10 +02:00
jD91mZM2 c0dad4475a Fix tests 2019-07-27 16:23:25 +02:00
jD91mZM2 9a7bd554d5 Suggestion: Switch to bitflags 2019-07-26 12:23:03 +02:00
jD91mZM2 edf3c008dc u64s are bigger than I thought! 2019-07-26 10:28:42 +02:00
jD91mZM2 52441c28b1 First step for ptrace overhaul 2019-07-24 21:41:57 +02:00
jD91mZM2 df993cc26e Switch to 2018 edition 2019-07-24 21:41:53 +02:00
Jeremy Soller 9e9f47d2a5 Merge branch 'ptrace' into 'master'
Drive ptrace into a wall, prepare for overhaul

See merge request redox-os/syscall!39
2019-07-24 02:02:10 +00:00
jD91mZM2 85a45f382c WIP(ptrace): Better support for signals
Signals now cause an event, and there's a way to continue until the
next signal. I can see this being used for detection of `int3`
although I'm not entirely sure as it may prove being too late to stop
abortion of process.
2019-07-23 20:15:33 +02:00
jD91mZM2 1ee9229c6a fixup! Fix sigaction Undefind Behavior 2019-07-23 20:15:33 +02:00
jD91mZM2 35b30e6fa9 Fix sigaction Undefind Behavior
Rust does not allow a `fn`-pointer to be null. This fixes that, while
luckily doing it in a way that leaves system calls
backwards-compatible :)
2019-07-23 20:15:32 +02:00
jD91mZM2 dcacbaed80 Add PtraceEvent for catching tracee forks 2019-07-23 20:14:52 +02:00
Jeremy Soller c89c2cb2f4 Add tests 2019-07-20 10:11:01 -06:00
Jeremy Soller 3480099632 Do not test on Linux 2019-07-08 16:47:39 -06:00
Jeremy Soller cceeae6ffe Add more tests 2019-07-07 09:11:45 -06:00
Jeremy Soller ba937b1497 Update CI 2019-07-06 11:42:06 -06:00
Jeremy Soller 5cd487b867 Add redoxer test to ci 2019-07-05 16:10:04 -06:00
Jeremy Soller b77076c392 Merge branch 'ptrace' into 'master'
Fix floating-point struct

See merge request redox-os/syscall!37
2019-07-05 12:09:33 +00:00
jD91mZM2 49dd22260b Fix floating-point struct 2019-07-04 15:51:55 +02:00
Jeremy Soller e3fd644ba9 Compile on non-redox platforms but return ENOSYS for all system calls 2019-07-04 07:44:09 -06:00
Jeremy Soller 43c562a921 0.1.55:
- deprecate chmod
- add tests
- CI using redoxer
2019-07-03 21:29:08 -06:00
Jeremy Soller ea1f9e098b Compile empty on non-redox platforms 2019-07-03 20:12:32 -06:00
Jeremy Soller f8eda5ce1b Merge branch 'ptrace' into 'master'
Bare-bones ptrace functionality

See merge request redox-os/syscall!36
2019-07-01 18:39:24 +00:00
jD91mZM2 4e3518b187 WIP(ptrace): Make the register structs x86_64-specific 2019-07-01 18:31:26 +02:00
jD91mZM2 e5bbe5b7e0 WIP(ptrace): Add PTRACE_WAIT 2019-07-01 16:16:34 +02:00
jD91mZM2 a7aff4b60d WIP(ptrace): Add an alternative to Linux' user_regs_struct 2019-06-25 10:07:58 +02:00
Jeremy Soller 5cdc240d13 0.1.54 - add method to disable caching when using physmap 2019-04-08 17:49:25 -06:00
Jeremy Soller a5c19c54de Do not require transpose_result, it is now stable 2019-04-07 10:51:41 -06:00
Jeremy Soller b07c481b4a 0.1.53 - add funmap to Scheme traits 2019-04-06 18:58:09 -06:00
Jeremy Soller 11fb32afb0 0.1.52 2019-03-31 08:42:05 -06:00
Jeremy Soller 9eff3aeeef Add CLONE_STACK flag 2019-03-31 08:41:43 -06:00
Jeremy Soller 54839d42f4 0.1.51 2019-01-20 19:37:14 -07:00
Jeremy Soller a715b8cf97 Switch over to using syscall instruction and linux abi on x86_64 2019-01-19 21:41:14 -07:00
Jeremy Soller 9a3734c41a 0.1.50 2018-12-31 20:32:18 -07:00
Jeremy Soller 54c06ce754 Add mprotect 2018-12-31 20:32:04 -07:00
Jeremy Soller 6909fb1c32 0.1.49 - mark ITimerSpec fields public 2018-12-28 21:44:49 -07:00
Jeremy Soller 65d1ea7900 0.1.48 - add itimerspec 2018-12-28 21:29:26 -07:00
Jeremy Soller d84a66b609 0.1.47 - fix signature of sigprocmask 2018-12-28 21:19:36 -07:00
Jeremy Soller 697dbd3001 0.1.46 - add sigprocmask 2018-12-28 21:13:33 -07:00
Jeremy Soller 31b7ae8eef 0.1.45 2018-12-28 15:04:39 -07:00
Jeremy Soller 114fc92d62 Add flags to fmap by using a struct for fmap arguments. 2018-12-28 15:04:23 -07:00
Jeremy Soller f479a66884 0.1.44 2018-12-11 21:03:08 -07:00
Jeremy Soller c9858c9f73 Correct repr of event 2018-12-11 21:02:57 -07:00
Jeremy Soller 47f7b3abf0 0.1.43 2018-11-26 11:39:09 -07:00
Jeremy Soller 0c30603ec0 Add aarch64 2018-11-26 11:38:53 -07:00
Jeremy Soller a8a2dc5663 0.1.42 2018-11-16 19:48:27 -07:00
Jeremy Soller ab782ebfc0 Mark umask as safe 2018-11-16 19:48:06 -07:00
Jeremy Soller a595f2fcc2 0.1.41 2018-11-16 19:44:12 -07:00
Jeremy Soller cbb39163d4 Add umask 2018-11-16 19:43:24 -07:00
Jeremy Soller de0f062446 remove toolchain file 2018-11-12 20:55:12 -07:00
Jeremy Soller 85598a1c33 Merge branch 'relibc' 2018-10-14 16:43:40 -06:00
Jeremy Soller 3909e5befa Merge branch 'patch-1' into 'master'
Document more possible syscall errors

See merge request redox-os/syscall!32
2018-10-14 01:42:25 +00:00
Tommy Hudson cb6efcb479 Document more possible syscall errors 2018-10-14 01:42:25 +00:00
Jeremy Soller 8d4df7f6f5 Merge branch 'master' into 'master'
Update url away from github

See merge request redox-os/syscall!31
2018-09-01 13:19:36 +00:00
Jeremy Soller 739aef47b8 Remove unused type parameter 2018-08-11 16:47:37 -06:00
Jeremy Soller 66e34aea2e Remove execve, replace with fexec 2018-08-11 11:14:16 -06:00
Liam Naddell 1f036ae691 Update url away from github 2018-07-10 19:25:14 +00:00
Jeremy Soller e7e2129600 Merge branch 'static-err' into 'master'
Give Error::text() a static lifetime

See merge request redox-os/syscall!29
2018-06-24 18:47:30 +00:00
Michael Murphy d230cc98b4 Give Error::text() a static lifetime 2018-06-24 11:52:25 -06:00
Jeremy Soller 495b09740a Update links to gitlab 2018-06-12 12:30:45 -06:00
Jeremy Soller d5601d9e7a Merge pull request #28 from xTibor/patch-1
Add badges to README.md
2018-05-30 09:54:38 -06:00
Nagy Tibor 636261ba49 Add badges to README.md 2018-05-30 17:51:14 +02:00
Jeremy Soller 0ab552da9a Transpose result in SchemeBlock 2018-05-30 09:39:30 -06:00
Jeremy Soller 229edcf704 Add new scheme traits, add generator script 2018-05-30 09:16:45 -06:00
Jeremy Soller 3971bb414f Remove fevent 2018-05-20 16:26:13 -06:00
Jeremy Soller 7dc00e7ea4 Merge pull request #23 from ids1024/no_intrinsics
Remove core_intrinsics feature
2018-01-07 08:36:59 -07:00
Ian Douglas Scott 3fc22e2e4c Remove core_intrinsics feature
Stable versions of these functions now exist under core::ptr, so use
those.
2018-01-06 22:38:37 -08:00
Jeremy Soller 7c805d2a28 Add more wait related calls 2018-01-05 17:49:26 -07:00
Jeremy Soller 7ab7dbe3c8 0.1.36 2018-01-05 17:18:56 -07:00
Jeremy Soller 64c117af75 Merge pull request #22 from jD91mZM2/wifstuff
Add wif* functions
2018-01-05 11:40:29 -07:00
jD91mZM2 e145ba3ea9 For some reason casting to signed byte
Also, optimization! `>> 1 > 0` could be turned into `>= 2`. Thanks, rust libc
2018-01-05 19:30:20 +01:00
jD91mZM2 d02a393781 Whoops, fix documentation 2018-01-05 18:51:37 +01:00
jD91mZM2 c102c6b21b Added wif* functions 2018-01-05 18:49:35 +01:00
Jeremy Soller 8f01290058 Add WCONTINUED 2018-01-03 22:21:26 -07:00
Jeremy Soller 88a0512d71 Add WUNTRACED 2018-01-03 21:50:59 -07:00
Jeremy Soller 414b8e0be0 Add frename 2017-12-27 20:18:56 -07:00
Jeremy Soller 3c765737a5 0.1.32 2017-11-28 21:47:15 -07:00
Jeremy Soller 0e73ef14a4 Add fchown, fchmod 2017-11-28 21:47:02 -07:00
Jeremy Soller 71c51bdbbb 0.1.31 2017-08-27 10:49:53 -06:00
Jeremy Soller 561557e774 Merge code cleanup from Rust fork, add clone_thread 2017-08-27 10:49:35 -06:00
Jeremy Soller 327cf28a67 0.1.30 2017-08-09 21:05:28 -06:00
Jeremy Soller b9d97e84be Merge pull request #19 from ids1024/dupfd
Add F_DUPFD flag
2017-08-08 14:58:53 -06:00
Ian Douglas Scott 89bce1981b Add F_DUPFD flag 2017-08-08 10:57:51 -07:00
Jeremy Soller 3f076b1f5d 0.1.29 2017-08-02 19:06:39 -06:00
Jeremy Soller 46451ccc91 Merge pull request #18 from ids1024/fifo
Add flag for fifo
2017-08-02 12:41:40 -06:00
Ian Douglas Scott cbc01efc4e Add flag for fifo 2017-08-02 11:05:09 -07:00
Jeremy Soller 76b36fec2e 0.1.28 2017-07-29 08:16:31 -06:00
Jeremy Soller 75f27fa49e Merge pull request #16 from ids1024/fcntl
Define F_GETFD and F_SETFD
2017-07-28 17:38:04 -06:00
Ian Douglas Scott 8786577cb7 Define F_GETFD and F_SETFD 2017-07-28 15:09:04 -07:00
Jeremy Soller 196f0f40cf 0.1.27 2017-07-23 14:44:41 -06:00
Jeremy Soller 4db8f75a65 Add getpgid, setpgid 2017-07-23 14:44:17 -06:00
Jeremy Soller 8e05a199d9 Merge pull request #14 from ids1024/sigaction
Make sigaction() take Option<_> arguments
2017-07-11 06:18:52 -06:00
Ian Douglas Scott ff0915e390 Make sigaction() take Option<_> arguments 2017-07-10 21:23:56 -07:00
Jeremy Soller b41142430c 0.1.26: Restorer as part of syscall crate, add sigreturn 2017-07-10 20:12:20 -06:00
Jeremy Soller 581ccf3c5a 0.1.25 2017-07-10 18:51:47 -06:00
Jeremy Soller 8706576731 Merge pull request #13 from ids1024/chr
Add MODE_CHR flag for "character special"
2017-07-10 17:57:53 -06:00
Ian Douglas Scott 44d4ead33e Add MODE_CHR flag for "character special" 2017-07-10 16:35:04 -07:00
Jeremy Soller 8d5590735f 0.1.24 2017-07-09 20:37:57 -06:00
Jeremy Soller 969a59666e Fix handler type 2017-07-09 20:37:43 -06:00
Jeremy Soller 6c781a3259 0.1.23 2017-07-09 20:31:48 -06:00
Jeremy Soller 9053a668e0 Convert to sigaction from signal 2017-07-09 20:31:34 -06:00
Jeremy Soller 9e7b7a168a 0.1.22: add futimens 2017-07-09 15:35:58 -06:00
Jeremy Soller d10107e39c 0.1.21 2017-07-05 21:05:42 -06:00
Jeremy Soller f3c97bee78 Add signal flags 2017-07-05 21:05:16 -06:00
Jeremy Soller 743b09199f 0.1.20 2017-07-05 09:09:54 -06:00
Jeremy Soller d303f3ebaa Merge pull request #11 from ids1024/nofollow
Add O_NOFOLLOW flag
2017-07-04 08:16:56 -06:00
Ian Douglas Scott 8579c43626 Add O_NOFOLLOW flag 2017-07-03 19:18:54 -07:00
Jeremy Soller d80f997275 Update syscall crate 2017-06-29 11:53:29 -06:00
Jeremy Soller 1fbb0b942c Merge pull request #9 from ids1024/symlink
Add symlink-related flags
2017-06-29 06:34:07 -06:00
Jeremy Soller 935a6f53ea Merge pull request #10 from ids1024/getppid
SYS_GETPPID and getppid()
2017-06-29 06:33:50 -06:00
Ian Douglas Scott 1fc8732fc2 getppid 2017-06-28 22:06:54 -07:00
Ian Douglas Scott 5c22611316 Add symlink-related flags 2017-06-26 09:21:30 -07:00
Jeremy Soller 96d5656ef9 0.1.18 2017-06-02 20:36:32 -06:00
Jeremy Soller 2390e25f1b Merge pull request #8 from ids1024/reprc
Use repr(C) for structs in data.rs
2017-06-02 20:36:17 -06:00
Ian Douglas Scott d3c0c2b892 Use repr(C) for structs in data.rs 2017-06-02 19:17:15 -07:00
Jeremy Soller e4066a2dfd Merge pull request #6 from ids1024/asref
Use AsRef<[u8]> for paths, instead of &str
2017-05-30 06:42:12 +02:00
Ian Douglas Scott e540330fe0 Use AsRef<[u8]> for paths, instead of &str 2017-05-08 08:22:51 -07:00
Jeremy Soller 863080967d Merge pull request #4 from raphlinus/master
Add MIT license text
2017-04-10 17:05:43 -06:00
Raph Levien 20d26d1a4d Add MIT license text 2017-04-07 17:21:32 -07:00
Jeremy Soller 82c8ccb56c 0.1.17 2017-03-19 17:31:20 -06:00
Jeremy Soller 0febab8966 Merge pull request #3 from vitiral/docs
add documentation links
2017-03-19 17:30:22 -06:00
Garrett Berg f065e691e2 add documentation links 2017-03-19 14:13:50 -06:00
Jeremy Soller 6188e4d040 Remove event 2017-01-09 20:26:19 -07:00
Jeremy Soller 49ede44e2d Fix build 2017-01-09 20:20:22 -07:00
Jeremy Soller 0362efec43 Add features 2017-01-09 20:16:29 -07:00
Jeremy Soller 0664629d75 Fix build 2017-01-09 20:15:19 -07:00
Jeremy Soller b5101b25cc Update syscall 2017-01-09 20:12:42 -07:00
Jeremy Soller 1193791d64 Add deref for timespec 2016-12-29 21:35:16 -07:00
Jeremy Soller e79e54d2f5 Update version 2016-12-27 11:17:57 -07:00
Jeremy Soller 89f6ad9900 Add dup2 call 2016-12-27 11:17:38 -07:00
Jeremy Soller a61d37da57 Add dup2 call 2016-12-27 11:17:29 -07:00
Jeremy Soller 9b0c363b27 Add dup2 2016-12-27 10:02:58 -07:00
Jeremy Soller a076c42403 Remove intel directive 2016-12-20 11:14:44 -07:00
Jeremy Soller b058d38894 Update version 2016-12-20 11:09:26 -07:00
Jeremy Soller 148a354432 Add ARM, update version 2016-12-20 11:09:07 -07:00
Jeremy Soller 3377a5cd2d Update version 2016-12-14 19:45:18 -07:00
Jeremy Soller 1908eea7e9 O_RDWR is now a set of flags, O_STAT added to allow open of dir or file 2016-11-25 17:58:33 -07:00
Jeremy Soller 7497b715cd Use open for mkdir 2016-11-25 16:57:46 -07:00
Jeremy Soller 2835586ee6 Match RFC spec 2016-11-25 12:05:30 -07:00
Jeremy Soller cc90408d14 Add fstatvfs syscall 2016-11-18 08:14:52 -07:00
Jeremy Soller d84ebc61b1 Add fstatvfs, check sizes on fstat 2016-11-18 08:06:27 -07:00
Jeremy Soller 8ce29a6ea2 Use setreuid, setregid for more control over process state 2016-11-17 14:09:40 -07:00
Jeremy Soller 7b8cc59890 More full featured syscall to set namespace 2016-11-16 21:52:24 -07:00
Jeremy Soller f87cbb5313 Update version 2016-11-16 20:50:51 -07:00
Jeremy Soller af9aa0c3b2 Add CLONE_NEWNS 2016-11-16 20:50:34 -07:00
Jeremy Soller 3e39d46f96 Add chmod, update cargo version 2016-11-15 16:44:01 -07:00
Jeremy Soller 3ee7a7ff0f Add chmod, update cargo version 2016-11-15 16:43:49 -07:00
Jeremy Soller cd6f7c219c Update version 2016-11-15 15:29:00 -07:00
Jeremy Soller 681f629afb Add flags to control owner, mode 2016-11-15 15:28:44 -07:00
Jeremy Soller f48cbf22f3 Add fcntl 2016-11-14 12:14:19 -07:00
Jeremy Soller 07b50b1a81 Update version 2016-11-10 20:32:41 -07:00
Jeremy Soller 485a3b8378 Add more documentation 2016-11-10 20:32:19 -07:00
Jeremy Soller 2d238fada7 Add some documentation 2016-11-03 20:28:59 -06:00
Jeremy Soller 77c43866db Update stat 2016-11-03 13:49:50 -06:00
Jeremy Soller 416fd22174 Add metadata 2016-11-02 19:32:33 -06:00
Jeremy Soller 5312106ed3 Prepare for crates.io 2016-11-02 19:31:24 -06:00
Jeremy Soller a21644c278 Add fmap 2016-11-02 19:26:21 -06:00
Jeremy Soller 5a687dfeb5 Smp (#23)
* Fire up multiple processors

* Use IPIs to wake up secondary processors

* Much better exception information

* Modifications to show more information on fault

* WIP: Use real libstd

* Add TLS (not complete)

* Add random function, export getpid, cleanup

* Do not spin APs until new context

* Update rust

* Update rust

* Use rd/wrfsbase

* Implement TLS

* Implement compiler builtins and update rust

* Update rust

* Back to Redox libstd

* Update rust
2016-10-31 10:49:00 -06:00
Jeremy Soller 1f2421a1c8 Redo networking (#22)
* Rewriting network functions

* Add buffer to dup
Fix non-blocking handling by triggering once on enabling events to read to EOF

* Modifications for UDP API

* Implement TCP client side

* Add active close

* Add DMAR parser

* Implement basic TCP listening. Need to improve the state machine

* Reduce debugging

* Fixes for close procedure

* Updates to fix path processing in libstd
2016-10-26 13:19:56 -06:00
Jeremy Soller 96846aba3f Add sys scheme to allow inspection of processes. WIP: Signals. 2016-10-13 21:00:51 -06:00
Jeremy Soller 78f58afbec Orbital (#16)
* Port previous ethernet scheme

* Add ipd

* Fix initfs rebuilds, use QEMU user networking addresses in ipd

* Add tcp/udp, netutils, dns, and network config

* Add fsync to network driver

* Add dns, router, subnet by default

* Fix e1000 driver. Make ethernet and IP non-blocking to avoid deadlocks

* Add orbital server, WIP

* Add futex

* Add orbutils and orbital

* Update libstd, orbutils, and orbital
Move ANSI key encoding to vesad

* Add orbital assets

* Update orbital

* Update to add login manager

* Add blocking primitives, block for most things except waitpid, update orbital

* Wait in waitpid and IRQ, improvements for other waits

* Fevent in root scheme

* WIP: Switch to using fevent

* Reorganize

* Event based e1000d driver

* Superuser-only access to some network schemes, display, and disk

* Superuser root and irq schemes

* Fix orbital
2016-10-13 17:21:42 -06:00
Jeremy Soller 207031175d Time (#11)
* WIP: Time syscalls

* Count time from PIT using low tickrate

* Implement realtime

* Implement nanosleep with a tight loop
2016-10-06 20:50:14 -06:00
Jeremy Soller 8f44c1556a Add sudo command, add effective UID and GID, and groups file 2016-10-05 20:31:59 -06:00
Jeremy Soller 0bbda9d776 Implement unix permissions 2016-10-05 18:01:05 -06:00
Jeremy Soller 957cd4501e Merge branch 'encoded_syscall' 2016-10-05 15:43:35 -06:00
Jeremy Soller 0e7fec4adf Add permissions to the filesystem, preliminary permissions to the syscalls 2016-10-05 14:24:08 -06:00
Jeremy Soller 837430e604 64-bit stat size, read entire executable in one go 2016-09-28 20:42:03 -06:00
Jeremy Soller 33a533665b WIP: AHCI drivers and more memory syscalls 2016-09-26 17:00:06 -06:00
Jeremy Soller a1311b50d7 Encode many of the file syscalls 2016-09-25 11:20:59 -06:00
Jeremy Soller a2038f5f9a Event support - demonstration in example scheme 2016-09-23 17:54:39 -06:00
Jeremy Soller a57b45e86b WIP: Kevent 2016-09-23 15:47:53 -06:00
Jeremy Soller 57d8d71617 Add wnohang, make PS/2 driver write input to display scheme, which then passes it to the shell 2016-09-22 10:10:27 -06:00
Jeremy Soller 416ec07ea4 WIP: VESA driver. Make initfs generated by code 2016-09-20 21:52:45 -06:00
Jeremy Soller da8e5db5c8 Implement more system calls 2016-09-20 16:57:45 -06:00
Jeremy Soller a5cfb8f363 Create example userspace scheme. Remove kernel duplication of syscalls, use syscall crate instead 2016-09-20 16:23:28 -06:00
Jeremy Soller 62f2f83ec8 Implement user schemes. Example in pcid. Currently deadlocks in UserInner 2016-09-20 08:47:16 -06:00
Jeremy Soller 05aa4d16da Complete execve - add argument support using safe ABI 2016-09-17 21:44:50 -06:00
Jeremy Soller bbe85a62a2 Update libstd. Add CWD and associated syscalls. Remove debugging 2016-09-17 19:01:34 -06:00
Jeremy Soller 35ff2bea3e Fix userspace clone by clobbering all variables on clone 2016-09-13 20:31:45 -06:00
Jeremy Soller 46986e0939 Allow exec, emulate clone by pretending to be child 2016-09-11 18:03:10 -06:00
Jeremy Soller dfeef83473 Add syscall library, make init program Rust 2016-09-08 19:10:50 -06:00
84 changed files with 4005 additions and 7371 deletions
+2 -2
View File
@@ -1,2 +1,2 @@
/build
/target
Cargo.lock
target
+12 -30
View File
@@ -1,35 +1,17 @@
image: "redoxos/redoxer:latest"
before_script:
- apt-get install nasm
- rustup component add rust-src
image: "redoxos/redoxer"
stages:
- host
- build
build:i686:
stage: host
script:
- mkdir -p target/i686
- cd target/i686
- TARGET=x86-unknown-none make -f ${CI_PROJECT_DIR}/Makefile -C `pwd` `pwd`/bootloader.bin `pwd`/bootloader-live.bin
workflow:
rules:
- if: '$CI_COMMIT_BRANCH == "master" && $CI_PROJECT_NAMESPACE == "redox-os"'
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
build:x86_64:
stage: host
script:
- mkdir -p target/x86_64
- cd target/x86_64
- TARGET=x86_64-unknown-uefi make -f ${CI_PROJECT_DIR}/Makefile -C `pwd` `pwd`/bootloader.efi `pwd`/bootloader-live.efi
build:linux:
stage: build
script: cargo +nightly build
build:aarch64:
stage: host
script:
- mkdir -p target/aarch64
- cd target/aarch64
- TARGET=aarch64-unknown-uefi make -f ${CI_PROJECT_DIR}/Makefile -C `pwd` `pwd`/bootloader.efi `pwd`/bootloader-live.efi
fmt:
stage: host
script:
- rustup component add rustfmt-preview
- cargo fmt -- --check
build:redox:
stage: build
script: redoxer build
-2
View File
@@ -1,2 +0,0 @@
[editor]
auto-format = false
-5
View File
@@ -1,5 +0,0 @@
[[language]]
name = "rust"
# TODO: Add more targets (BIOS, x86_32)
# Uncomment this line and set cargo.target to your target to get accurate completions
# config = { cargo.target = "aarch64-unknown-uefi", check.targets = ["x86_64-unknown-uefi", "aarch64-unknown-uefi"] }
Generated
-349
View File
@@ -1,349 +0,0 @@
# 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 = "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.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
name = "bit_field"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
[[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 = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[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 = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[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 = "dmidecode"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5a070ca68f8ba202b05487d52b9ac56eaebb5b66cdd68d1a17e63174bb11e3b"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "endian-num"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8f59926911ef34d1efb9ea1ee8ca78385df62ce700ccf2bcb149011bd226888"
[[package]]
name = "fdt"
version = "0.2.0-alpha1"
source = "git+https://github.com/repnop/fdt.git?rev=2fb1409edd1877c714a0aa36b6a7c5351004be54#2fb1409edd1877c714a0aa36b6a7c5351004be54"
[[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 = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"generic-array",
]
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "linked_list_allocator"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897"
dependencies = [
"spinning_top",
]
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[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]]
name = "raw-cpuid"
version = "10.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox-path"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
[[package]]
name = "redox_bootloader"
version = "1.0.0+rb0.3.0"
dependencies = [
"bitflags 1.3.2",
"byteorder",
"dmidecode",
"fdt",
"linked_list_allocator",
"log",
"redox_syscall",
"redox_uefi",
"redox_uefi_std",
"redoxfs",
"spin",
"x86",
]
[[package]]
name = "redox_syscall"
version = "0.9.0+rb0.3.0"
dependencies = [
"bitflags 2.13.0",
]
[[package]]
name = "redox_uefi"
version = "0.1.14"
source = "git+https://gitlab.redox-os.org/redox-os/uefi.git#26a499eeaf55d42fb24206830345e26fb8f5f835"
[[package]]
name = "redox_uefi_alloc"
version = "0.1.14"
source = "git+https://gitlab.redox-os.org/redox-os/uefi.git#26a499eeaf55d42fb24206830345e26fb8f5f835"
dependencies = [
"redox_uefi",
]
[[package]]
name = "redox_uefi_std"
version = "0.1.14"
source = "git+https://gitlab.redox-os.org/redox-os/uefi.git#26a499eeaf55d42fb24206830345e26fb8f5f835"
dependencies = [
"redox_uefi",
"redox_uefi_alloc",
]
[[package]]
name = "redoxfs"
version = "0.9.1+rb0.3.0"
dependencies = [
"aes",
"argon2",
"base64ct",
"bitflags 2.13.0",
"endian-num",
"libc",
"log",
"lz4_flex",
"redox-path",
"redox_syscall",
"seahash",
"uuid",
"xts-mode",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "seahash"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
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]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "uuid"
version = "1.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "x86"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385"
dependencies = [
"bit_field",
"bitflags 1.3.2",
"raw-cpuid",
]
[[package]]
name = "xts-mode"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cbddb7545ca0b9ffa7bdc653e8743303e1712687a6918ced25f2cdbed42520"
dependencies = [
"byteorder",
"cipher",
]
+17 -49
View File
@@ -1,57 +1,25 @@
[package]
name = "redox_bootloader"
version = "1.0.0+rb0.3.0"
name = "redox_syscall"
version = "0.9.0+rb0.3.0"
description = "A Rust library to access raw Redox system calls"
license = "MIT"
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
edition = "2024"
repository = "https://gitlab.redox-os.org/redox-os/syscall"
documentation = "https://docs.rs/redox_syscall"
edition = "2021"
# UEFI uses bin target
[[bin]]
name = "bootloader"
path = "src/main.rs"
# BIOS uses lib target
[lib]
name = "bootloader"
path = "src/main.rs"
crate-type = ["staticlib"]
[dependencies]
bitflags = "1.3.2"
linked_list_allocator = "0.10.5"
log = "0.4.17"
redox_syscall = { path = "../syscall" }
spin = "0.9.5"
[dependencies.redoxfs]
path = "../redoxfs"
default-features = false
features = ["log"]
[target.'cfg(target_os = "uefi")'.dependencies]
redox_uefi = { git = "https://gitlab.redox-os.org/redox-os/uefi.git" }
redox_uefi_std = { git = "https://gitlab.redox-os.org/redox-os/uefi.git" }
#TODO: riscv cannot use target_os = "uefi" at this time
[target.'cfg(target_arch = "riscv64")'.dependencies]
redox_uefi = { git = "https://gitlab.redox-os.org/redox-os/uefi.git" }
redox_uefi_std = { git = "https://gitlab.redox-os.org/redox-os/uefi.git" }
[target."aarch64-unknown-uefi".dependencies]
dmidecode = "0.8.0"
[target."x86_64-unknown-uefi".dependencies]
x86 = "0.52.0"
[target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies]
byteorder = { version = "1", default-features = false }
fdt = { git = "https://github.com/repnop/fdt.git", rev = "2fb1409edd1877c714a0aa36b6a7c5351004be54" }
name = "syscall"
[features]
default = []
live = []
serial_debug = []
default = ["userspace"]
rustc-dep-of-std = ["core", "bitflags/rustc-dep-of-std"]
userspace = []
std = []
[dependencies]
bitflags = "2.4"
core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" }
[patch.crates-io]
redox_syscall = { path = "../syscall" }
redoxfs = { path = "../redoxfs" }
[target.'cfg(loom)'.dev-dependencies]
loom = "0.7"
+18 -17
View File
@@ -1,21 +1,22 @@
Copyright (c) 2017 Redox OS Developers
MIT License
Copyright (c) 2017-2022 Redox OS
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-26
View File
@@ -1,26 +0,0 @@
TARGET?=x86_64-unknown-uefi
SOURCE:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
BUILD:=$(CURDIR)
export RUST_TARGET_PATH?=$(SOURCE)/targets
CARGO_ARGS=--release --locked --target $(TARGET) \
-Z build-std=core,alloc \
-Z build-std-features=compiler-builtins-mem
include $(SOURCE)/mk/$(TARGET).mk
clean:
rm -rf build target
$(BUILD)/filesystem:
mkdir -p $(BUILD)
rm -f $@.partial
mkdir $@.partial
fallocate -l 1MiB $@.partial/kernel
mv $@.partial $@
$(BUILD)/filesystem.bin: $(BUILD)/filesystem
mkdir -p $(BUILD)
rm -f $@.partial
fallocate -l 254MiB $@.partial
redoxfs-ar $@.partial $<
mv $@.partial $@
+7 -62
View File
@@ -1,62 +1,7 @@
# Bootloader
Redox OS Bootloader
## Requirements
These software needs to be available on the PATH at build time:
+ [mtools](https://www.gnu.org/software/mtools/)
+ [nasm](https://nasm.us/)
+ [redoxfs-ar](https://gitlab.redox-os.org/redox-os/redoxfs)
## Building
```sh
make TARGET=<triplet> BUILD=build all
```
The `<triplet>` is one of:
| ARCH | Boot Mode | Triplets |
|---|---|---|
| `i686` | BIOS | `x86-unknown-none` |
| `x86_64` | BIOS | `x86-unknown-none` |
| `x86_64` | UEFI | `x86_64-unknown-uefi` |
| `aarch64` | UEFI | `aarch64-unknown-uefi` |
| `riscv64gc` | UEFI | `riscv64gc-unknown-uefi` |
See [mk directory](./mk) for more information of how the build is working.
## Entry points
Please read [Boot Process](https://doc.redox-os.org/book/boot-process.html) in the Redox OS Book for an introductory guide.
In this source code, some interesting files for entry points are:
+ BIOS boot stages: [asm/x86-unknown-none/bootloader.asm](./asm/x86-unknown-none/bootloader.asm)
+ BIOS boot entry: `fn start` at [src/os/bios/mod.rs](./src/os/bios/mod.rs)
+ UEFI boot entry: `fn main` at [src/os/uefi/mod.rs](src/os/uefi/mod.rs)
+ Common boot process: `fn main` at [src/main.rs](src/main.rs)
+ UEFI kernel entry: `fn kernel_entry` in each arch:
- `x86_64`: [src/os/uefi/arch/x86_64.rs](src/os/uefi/arch/x86_64.rs)
- `aarch64`: [src/os/uefi/arch/aarch64.rs](src/os/uefi/arch/aarch64.rs)
- `riscv64gc`: [src/os/uefi/arch/riscv64/mod.rs](src/os/uefi/arch/riscv64/mod.rs)
## Debugging
### QEMU
```sh
make TARGET=<triplet> BUILD=build qemu
```
## 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.
# syscall
This crate contains the system call numbers and Rust wrappers for the inline Assembly code of system calls.
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![crates.io](http://meritbadge.herokuapp.com/redox_syscall)](https://crates.io/crates/redox_syscall)
[![docs.rs](https://docs.rs/redox_syscall/badge.svg)](https://docs.rs/redox_syscall)
View File
-17
View File
@@ -1,17 +0,0 @@
interrupt_vector_table:
b . @ Reset
b .
b . @ SWI instruction
b .
b .
b .
b .
b .
.comm stack, 0x10000 @ Reserve 64k stack in the BSS
_start:
.globl _start
ldr sp, =stack+0x10000 @ Set up the stack
bl kstart @ Jump to the main function
1:
b 1b @ Halt
-31
View File
@@ -1,31 +0,0 @@
sectalign off
; stage 1 is sector 0, loaded at 0x7C00
%include "stage1.asm"
; GPT area from sector 1 to 33, loaded at 0x7E00
times (33*512) db 0
; stage 2, loaded at 0xC000
stage2:
%include "stage2.asm"
align 512, db 0
stage2.end:
; the maximum size of stage2 is 4 KiB
times (4*1024)-($-stage2) db 0
; ISO compatibility, uses up space until 0x12400
%include "iso.asm"
times 3072 db 0 ; Pad to 0x13000
; stage3, loaded at 0x13000
stage3:
%defstr STAGE3_STR %[STAGE3]
incbin STAGE3_STR
align 512, db 0
.end:
; the maximum size of the boot loader portion is 384 KiB
times (384*1024)-($-$$) db 0
-176
View File
@@ -1,176 +0,0 @@
SECTION .text
USE16
cpuid_required_features:
.edx equ cpuid_edx.fpu | cpuid_edx.pse | cpuid_edx.pge | cpuid_edx.fxsr
.ecx equ 0
cpuid_check:
; If bit 21 of EFLAGS can be changed, then CPUID is supported
pushfd ;Save EFLAGS
pushfd ;Store EFLAGS
xor dword [esp],0x00200000 ;Invert the ID bit in stored EFLAGS
popfd ;Load stored EFLAGS (with ID bit inverted)
pushfd ;Store EFLAGS again (ID bit may or may not be inverted)
pop eax ;eax = modified EFLAGS (ID bit may or may not be inverted)
xor eax,[esp] ;eax = whichever bits were changed
popfd ;Restore original EFLAGS
test eax,0x00200000 ;eax = zero if ID bit can't be changed, else non-zero
jz .no_cpuid
mov eax, 1
cpuid
and edx, cpuid_required_features.edx
cmp edx, cpuid_required_features.edx
jne .error
and ecx, cpuid_required_features.ecx
cmp ecx, cpuid_required_features.ecx
jne .error
ret
.no_cpuid:
mov si, .msg_cpuid
call print
mov si, .msg_line
call print
jmp .halt
.error:
push ecx
push edx
mov si, .msg_features
call print
mov si, .msg_line
call print
mov si, .msg_edx
call print
pop ebx
push ebx
shr ebx, 16
call print_hex
pop ebx
call print_hex
mov si, .msg_must_contain
call print
mov ebx, cpuid_required_features.edx
shr ebx, 16
call print_hex
mov ebx, cpuid_required_features.edx
call print_hex
mov si, .msg_line
call print
mov si, .msg_ecx
call print
pop ebx
push ebx
shr ebx, 16
call print_hex
pop ebx
call print_hex
mov si, .msg_must_contain
call print
mov ebx, cpuid_required_features.ecx
shr ebx, 16
call print_hex
mov ebx, cpuid_required_features.ecx
call print_hex
mov si, .msg_line
call print
.halt:
cli
hlt
jmp .halt
.msg_cpuid: db "CPUID not supported",0
.msg_features: db "Required CPU features are not present",0
.msg_line: db 13,10,0
.msg_edx: db "EDX ",0
.msg_ecx: db "ECX ",0
.msg_must_contain: db " must contain ",0
cpuid_edx:
.fpu equ 1 << 0
.vme equ 1 << 1
.de equ 1 << 2
.pse equ 1 << 3
.tsc equ 1 << 4
.msr equ 1 << 5
.pae equ 1 << 6
.mce equ 1 << 7
.cx8 equ 1 << 8
.apic equ 1 << 9
.sep equ 1 << 11
.mtrr equ 1 << 12
.pge equ 1 << 13
.mca equ 1 << 14
.cmov equ 1 << 15
.pat equ 1 << 16
.pse_36 equ 1 << 17
.psn equ 1 << 18
.clfsh equ 1 << 19
.ds equ 1 << 21
.acpi equ 1 << 22
.mmx equ 1 << 23
.fxsr equ 1 << 24
.sse equ 1 << 25
.sse2 equ 1 << 26
.ss equ 1 << 27
.htt equ 1 << 28
.tm equ 1 << 29
.ia64 equ 1 << 30
.pbe equ 1 << 31
cpuid_ecx:
.sse3 equ 1 << 0
.pclmulqdq equ 1 << 1
.dtes64 equ 1 << 2
.monitor equ 1 << 3
.ds_cpl equ 1 << 4
.vmx equ 1 << 5
.smx equ 1 << 6
.est equ 1 << 7
.tm2 equ 1 << 8
.ssse3 equ 1 << 9
.cnxt_id equ 1 << 10
.sdbg equ 1 << 11
.fma equ 1 << 12
.cmpxchg16b equ 1 << 13
.xtpr equ 1 << 14
.pdcm equ 1 << 15
.pcid equ 1 << 17
.dca equ 1 << 18
.sse4_1 equ 1 << 19
.sse4_2 equ 1 << 20
.x2apic equ 1 << 21
.movbe equ 1 << 22
.popcnt equ 1 << 23
.tsc_deadline equ 1 << 24
.aes equ 1 << 25
.xsave equ 1 << 26
.osxsave equ 1 << 27
.avx equ 1 << 28
.f16c equ 1 << 29
.rdrand equ 1 << 30
.hypervisor equ 1 << 31
-128
View File
@@ -1,128 +0,0 @@
SECTION .text ; cannot use .data
struc GDTEntry
.limitl resw 1
.basel resw 1
.basem resb 1
.attribute resb 1
.flags__limith resb 1
.baseh resb 1
endstruc
gdt_attr:
.present equ 1 << 7
.ring1 equ 1 << 5
.ring2 equ 1 << 6
.ring3 equ 1 << 5 | 1 << 6
.user equ 1 << 4
;user
.code equ 1 << 3
; code
.conforming equ 1 << 2
.readable equ 1 << 1
; data
.expand_down equ 1 << 2
.writable equ 1 << 1
.accessed equ 1 << 0
;system
; legacy
.tssAvailabe16 equ 0x1
.ldt equ 0x2
.tssBusy16 equ 0x3
.call16 equ 0x4
.task equ 0x5
.interrupt16 equ 0x6
.trap16 equ 0x7
.tssAvailabe32 equ 0x9
.tssBusy32 equ 0xB
.call32 equ 0xC
.interrupt32 equ 0xE
.trap32 equ 0xF
; long mode
.ldt32 equ 0x2
.tssAvailabe64 equ 0x9
.tssBusy64 equ 0xB
.call64 equ 0xC
.interrupt64 equ 0xE
.trap64 equ 0xF
gdt_flag:
.granularity equ 1 << 7
.available equ 1 << 4
;user
.default_operand_size equ 1 << 6
; code
.long_mode equ 1 << 5
; data
.reserved equ 1 << 5
gdtr:
dw gdt.end + 1 ; size
dq gdt ; offset
gdt:
.null equ $ - gdt
dq 0
.lm64_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code
at GDTEntry.flags__limith, db gdt_flag.long_mode
at GDTEntry.baseh, db 0
iend
.lm64_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable
at GDTEntry.flags__limith, db 0
at GDTEntry.baseh, db 0
iend
.pm32_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code | gdt_attr.readable
at GDTEntry.flags__limith, db 0xF | gdt_flag.granularity | gdt_flag.default_operand_size
at GDTEntry.baseh, db 0
iend
.pm32_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable
at GDTEntry.flags__limith, db 0xF | gdt_flag.granularity | gdt_flag.default_operand_size
at GDTEntry.baseh, db 0
iend
.pm16_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code | gdt_attr.readable
at GDTEntry.flags__limith, db 0xF
at GDTEntry.baseh, db 0
iend
.pm16_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable
at GDTEntry.flags__limith, db 0xF
at GDTEntry.baseh, db 0
iend
.end equ $ - gdt
-161
View File
@@ -1,161 +0,0 @@
; Simple ISO emulation with el torito
; Fill until CD sector 0x10
times (0x10*2048)-($-$$) db 0
; Volume record
;TODO: fill in more fields
iso_volume_record:
db 1 ; Type volume record
db "CD001" ; Identifier
db 1 ; Version
db 0 ; Unused
times 32 db ' ' ; System identifier
.volume_id: ; Volume identifier
db 'Redox OS'
times 32-($-.volume_id) db ' '
times 8 db 0 ; Unused
db 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15 ; Volume space size (0x15)
times 32 db 0 ; Unused
db 0x01, 0x00, 0x00, 0x01 ; Volume set size
db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number
db 0x00, 0x08, 0x08, 0x00 ; Logical block size in little and big endian
times 156-($-iso_volume_record) db 0
; Root directory entry
.root_directory:
db 0x22 ; Length of entry
db 0x00 ; Length of extended attributes
db 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 ; Location of extent (0x14)
db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent
db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time
db 0x02 ; File flags
db 0x00 ; Interleaved file unit size
db 0x00 ; Interleaved gap size
db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number
db 0x01 ; Length of file identifier
db 0x00 ; File identifier
times 128 db ' ' ; Volume set identifier
times 128 db ' ' ; Publisher identifier
times 128 db ' ' ; Data preparer identifier
times 128 db ' ' ; Application identifier
times 37 db ' ' ; Copyright file ID
times 37 db ' ' ; Abstract file ID
times 37 db ' ' ; Bibliographic file ID
times 881-($-iso_volume_record) db 0
db 1 ; File structure version
; Fill until CD sector 0x11
times (0x11*2048)-($-$$) db 0
; Boot record
iso_boot_record:
db 0 ; Type boot record
db "CD001" ; Identifier
db 1 ; Version
db "EL TORITO SPECIFICATION" ; Boot system identifier
times 0x47-($ - iso_boot_record) db 0 ; Padding
dd 0x13 ; Sector of boot catalog
; Fill until CD sector 0x12
times (0x12*2048)-($-$$) db 0
; Terminator
iso_terminator:
db 0xFF ; Type terminator
db "CD001" ; Identifier
db 1 ; Version
; Fill until CD sector 0x13
times (0x13*2048)-($-$$) db 0
; Boot catalog
iso_boot_catalog:
; Validation entry
.validation:
db 1 ; Header ID
db 0 ; Platform ID (x86)
dw 0 ; Reserved
times 24 db 0 ; ID string
dw 0x55aa ; Checksum
dw 0xaa55 ; Key
; Default entry
.default:
db 0x88 ; Bootable
db 4 ; Hard drive emulation
dw 0 ; Load segment (0 is platform default)
db 0xEE ; Partition type (0xEE is protective MBR)
db 0 ; Unused
dw 1 ; Sector count
dd 0 ; Start address for virtual disk
times 20 db 0 ; Padding
; EFI section header entry
.efi_section_header:
db 0x91 ; Final header
db 0xEF ; Platform ID (EFI)
dw 1 ; Number of section header entries
times 28 db 0 ; ID string
; EFI section entry
.efi_section_entry:
db 0x88 ; Bootable
db 0 ; No emulation
dw 0 ; Load segment (0 is platform default)
db 0 ; Partition type (not used)
db 0 ; Unused
dw 512 ; Sector count (1 MiB = 512 CD sectors)
dd 512 ; Start address for virtual disk (1 MiB = 512 CD sectors)
times 20 db 0 ; Padding
; Fill until CD sector 0x14
times (0x14*2048)-($-$$) db 0
iso_root_directory:
.self:
db 0x22 ; Length of entry
db 0x00 ; Length of extended attributes
db 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 ; Location of extent (0x14)
db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent
db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time
db 0x02 ; File flags
db 0x00 ; Interleaved file unit size
db 0x00 ; Interleaved gap size
db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number
db 0x01 ; Length of file identifier
db 0x00 ; File identifier
.parent:
db 0x22 ; Length of entry
db 0x00 ; Length of extended attributes
db 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 ; Location of extent (0x14)
db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent
db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time
db 0x02 ; File flags
db 0x00 ; Interleaved file unit size
db 0x00 ; Interleaved gap size
db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number
db 0x01 ; Length of file identifier
db 0x01 ; File identifier
.boot_cat:
db 0x2C ; Length of entry
db 0x00 ; Length of extended attributes
db 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13 ; Location of extent (0x13)
db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent
db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time
db 0x00 ; File flags
db 0x00 ; Interleaved file unit size
db 0x00 ; Interleaved gap size
db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number
db 0x0A ; Length of file identifier
db "BOOT.CAT;1",0 ; File identifier
; Fill until CD sector 0x15
times (0x15*2048)-($-$$) db 0
-56
View File
@@ -1,56 +0,0 @@
SECTION .text
USE32
long_mode:
.func: dq 0
.page_table: dd 0
.entry:
; disable interrupts
cli
; disable paging
mov eax, cr0
and eax, 0x7FFFFFFF
mov cr0, eax
; enable FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension
mov eax, cr4
or eax, 1 << 9 | 1 << 7 | 1 << 5 | 1 << 4
mov cr4, eax
; load long mode GDT
lgdt [gdtr]
; enable long mode
mov ecx, 0xC0000080 ; Read from the EFER MSR.
rdmsr
or eax, 1 << 11 | 1 << 8 ; Set the Long-Mode-Enable and NXE bit.
wrmsr
; set page table
mov eax, [.page_table]
mov cr3, eax
; enabling paging and protection simultaneously
mov eax, cr0
or eax, 1 << 31 | 1 << 16 | 1 ;Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode
mov cr0, eax
; far jump to enable Long Mode and load CS with 64 bit segment
jmp gdt.lm64_code:.inner
USE64
.inner:
; load all the other segments with 64 bit data segments
mov rax, gdt.lm64_data
mov ds, rax
mov es, rax
mov fs, rax
mov gs, rax
mov ss, rax
; jump to specified function
mov rax, [.func]
jmp rax
-67
View File
@@ -1,67 +0,0 @@
SECTION .text
USE16
; provide function for printing in x86 real mode
; print a string and a newline
; CLOBBER
; ax
print_line:
mov al, 13
call print_char
mov al, 10
jmp print_char
; print a string
; IN
; si: points at zero-terminated String
; CLOBBER
; si, ax
print:
pushf
cld
.loop:
lodsb
test al, al
jz .done
call print_char
jmp .loop
.done:
popf
ret
; print a character
; IN
; al: character to print
print_char:
pusha
mov bx, 7
mov ah, 0x0e
int 0x10
popa
ret
; print a number in hex
; IN
; bx: the number
; CLOBBER
; al, cx
print_hex:
mov cx, 4
.lp:
mov al, bh
shr al, 4
cmp al, 0xA
jb .below_0xA
add al, 'A' - 0xA - '0'
.below_0xA:
add al, '0'
call print_char
shl bx, 4
loop .lp
ret
-36
View File
@@ -1,36 +0,0 @@
SECTION .text
USE16
protected_mode:
.func: dd 0
.entry:
; disable interrupts
cli
; load protected mode GDT
lgdt [gdtr]
; set protected mode bit of cr0
mov eax, cr0
or eax, 1
mov cr0, eax
; far jump to load CS with 32 bit segment
jmp gdt.pm32_code:.inner
USE32
.inner:
; load all the other segments with 32 bit data segments
mov eax, gdt.pm32_data
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
mov ss, eax
; jump to specified function
mov eax, [.func]
jmp eax
-222
View File
@@ -1,222 +0,0 @@
ORG 0x7C00
SECTION .text
USE16
stage1: ; dl comes with disk
; initialize segment registers
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
; initialize stack
mov sp, 0x7C00
; initialize CS
push ax
push word .set_cs
retf
.set_cs:
; save disk number
mov [disk], dl
mov si, stage_msg
call print
mov al, '1'
call print_char
call print_line
; read CHS gemotry
; CL (bits 0-5) = maximum sector number
; CL (bits 6-7) = high bits of max cylinder number
; CH = low bits of maximum cylinder number
; DH = maximum head number
mov ah, 0x08
mov dl, [disk]
xor di, di
int 0x13
jc error ; carry flag set on error
mov bl, ch
mov bh, cl
shr bh, 6
mov [chs.c], bx
shr dx, 8
inc dx ; returns heads - 1
mov [chs.h], dx
and cl, 0x3f
mov [chs.s], cl
mov eax, (stage2 - stage1) / 512
mov bx, stage2
mov cx, (stage3.end - stage2) / 512
mov dx, 0
call load
mov si, stage_msg
call print
mov al, '2'
call print_char
call print_line
jmp stage2.entry
; load some sectors from disk to a buffer in memory
; buffer has to be below 1MiB
; IN
; ax: start sector
; bx: offset of buffer
; cx: number of sectors (512 Bytes each)
; dx: segment of buffer
; CLOBBER
; ax, bx, cx, dx, si
; TODO rewrite to (eventually) move larger parts at once
; if that is done increase buffer_size_sectors in startup-common to that (max 0x80000 - startup_end)
load:
cmp cx, 127
jbe .good_size
pusha
mov cx, 127
call load
popa
add eax, 127
add dx, 127 * 512 / 16
sub cx, 127
jmp load
.good_size:
mov [DAPACK.addr], eax
mov [DAPACK.buf], bx
mov [DAPACK.count], cx
mov [DAPACK.seg], dx
call print_dapack
cmp byte [chs.s], 0
jne .chs
;INT 0x13 extended read does not work on CDROM!
mov dl, [disk]
mov si, DAPACK
mov ah, 0x42
int 0x13
jc error ; carry flag set on error
ret
.chs:
; calculate CHS
xor edx, edx
mov eax, [DAPACK.addr]
div dword [chs.s] ; divide by sectors
mov ecx, edx ; move sector remainder to ecx
xor edx, edx
div dword [chs.h] ; divide by heads
; eax has cylinders, edx has heads, ecx has sectors
; Sector cannot be greater than 63
inc ecx ; Sector is base 1
cmp ecx, 63
ja error_chs
; Head cannot be greater than 255
cmp edx, 255
ja error_chs
; Cylinder cannot be greater than 1023
cmp eax, 1023
ja error_chs
; Move CHS values to parameters
mov ch, al
shl ah, 6
and cl, 0x3f
or cl, ah
shl dx, 8
; read from disk using CHS
mov al, [DAPACK.count]
mov ah, 0x02 ; disk read (CHS)
mov bx, [DAPACK.buf]
mov dl, [disk]
push es ; save ES
mov es, [DAPACK.seg]
int 0x13
pop es ; restore EC
jc error ; carry flag set on error
ret
print_dapack:
mov bx, [DAPACK.addr + 2]
call print_hex
mov bx, [DAPACK.addr]
call print_hex
mov al, '#'
call print_char
mov bx, [DAPACK.count]
call print_hex
mov al, ' '
call print_char
mov bx, [DAPACK.seg]
call print_hex
mov al, ':'
call print_char
mov bx, [DAPACK.buf]
call print_hex
call print_line
ret
error_chs:
mov ah, 0
error:
call print_line
mov bh, 0
mov bl, ah
call print_hex
mov al, ' '
call print_char
mov si, error_msg
call print
call print_line
.halt:
cli
hlt
jmp .halt
%include "print.asm"
stage_msg: db "Stage ",0
error_msg: db "ERROR",0
disk: db 0
chs:
.c: dd 0
.h: dd 0
.s: dd 0
DAPACK:
db 0x10
db 0
.count: dw 0 ; int 13 resets this to # of blocks actually read/written
.buf: dw 0 ; memory buffer destination address (0:7c00)
.seg: dw 0 ; in memory page zero
.addr: dq 0 ; put the lba to read in this spot
times 446-($-$$) db 0
partitions: times 4 * 16 db 0
db 0x55
db 0xaa
-134
View File
@@ -1,134 +0,0 @@
SECTION .text
USE16
stage2.entry:
; check for required features
call cpuid_check
; enable A20-Line via IO-Port 92, might not work on all motherboards
in al, 0x92
or al, 2
out 0x92, al
mov dword [protected_mode.func], stage3.entry
jmp protected_mode.entry
%include "cpuid.asm"
%include "gdt.asm"
%include "long_mode.asm"
%include "protected_mode.asm"
%include "thunk.asm"
USE32
stage3.entry:
; stage3 stack at 448 KiB (512KiB minus 64KiB disk buffer)
mov esp, 0x70000
; push arguments
mov eax, thunk.int16
push eax
mov eax, thunk.int15
push eax
mov eax, thunk.int13
push eax
mov eax, thunk.int10
push eax
xor eax, eax
mov al, [disk]
push eax
mov eax, kernel.entry
push eax
mov eax, [stage3 + 0x18]
call eax
.halt:
cli
hlt
jmp .halt
kernel:
.stack: dq 0
.func: dq 0
.args: dq 0
.entry:
; page_table: usize
mov eax, [esp + 4]
mov [long_mode.page_table], eax
; stack: u64
mov eax, [esp + 8]
mov [.stack], eax
mov eax, [esp + 12]
mov [.stack + 4], eax
; func: u64
mov eax, [esp + 16]
mov [.func], eax
mov eax, [esp + 20]
mov [.func + 4], eax
; args: *const KernelArgs
mov eax, [esp + 24]
mov [.args], eax
; long_mode: usize
mov eax, [esp + 28]
test eax, eax
jz .inner32
mov eax, .inner64
mov [long_mode.func], eax
jmp long_mode.entry
.inner32:
; disable paging
mov eax, cr0
and eax, 0x7FFFFFFF
mov cr0, eax
;TODO: PAE (1 << 5)
; enable FXSAVE/FXRSTOR, Page Global, and Page Size Extension
mov eax, cr4
or eax, 1 << 9 | 1 << 7 | 1 << 4
mov cr4, eax
; set page table
mov eax, [long_mode.page_table]
mov cr3, eax
; enabling paging and protection simultaneously
mov eax, cr0
; Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode
or eax, 1 << 31 | 1 << 16 | 1
mov cr0, eax
; enable FPU
;TODO: move to Rust
mov eax, cr0
and al, 11110011b ; Clear task switched (3) and emulation (2)
or al, 00100010b ; Set numeric error (5) monitor co-processor (1)
mov cr0, eax
fninit
mov esp, [.stack]
mov eax, [.args]
push eax
mov eax, [.func]
call eax
.halt32:
cli
hlt
jmp .halt32
USE64
.inner64:
mov rsp, [.stack]
mov rax, [.func]
mov rdi, [.args]
call rax
.halt64:
cli
hlt
jmp .halt64
-149
View File
@@ -1,149 +0,0 @@
SECTION .text
USE32
thunk:
.int10:
mov dword [.func], .int10_real
jmp .enter
.int13:
mov dword [.func], .int13_real
jmp .enter
.int15:
mov dword [.func], .int15_real
jmp .enter
.int16:
mov dword [.func], .int16_real
jmp .enter
.func: dd 0
.esp: dd 0
.cr0: dd 0
.enter:
; save flags
pushfd
; save registers
pushad
; save esp
mov [.esp], esp
; load gdt
lgdt [gdtr]
; far jump to protected mode 16-bit
jmp gdt.pm16_code:.pm16
.exit:
; set segment selectors to 32-bit protected mode
mov eax, gdt.pm32_data
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
mov ss, eax
; restore esp
mov esp, [.esp]
; restore registers
popad
; restore flags
popfd
; return
ret
USE16
.int10_real:
int 0x10
ret
.int13_real:
int 0x13
ret
.int15_real:
int 0x15
ret
.int16_real:
int 0x16
ret
.pm16:
; set segment selectors to protected mode 16-bit
mov eax, gdt.pm16_data
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
mov ss, eax
; save cr0
mov eax, cr0
mov [.cr0], eax
; disable paging and protected mode
and eax, 0x7FFFFFFE
mov cr0, eax
; far jump to real mode
jmp 0:.real
.real:
; set segment selectors to real mode
mov eax, 0
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
mov ss, eax
; set stack
mov esp, 0x7C00 - 64
; load registers and ES
pop es
pop edi
pop esi
pop ebp
pop ebx
pop edx
pop ecx
pop eax
; enable interrupts
sti
; call real mode function
call [.func]
; disable interrupts
cli
; save registers and ES
push eax
push ecx
push edx
push ebx
push ebp
push esi
push edi
push es
; load gdt (BIOS sometimes overwrites this)
lgdt [gdtr]
; restore cr0, will enable protected mode
mov eax, [.cr0]
mov cr0, eax
; far jump to protected mode 32-bit
jmp gdt.pm32_code:.exit
-78
View File
@@ -1,78 +0,0 @@
OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv", "elf64-littleriscv")
OUTPUT_ARCH(riscv)
ENTRY(coff_start)
SECTIONS
{
PROVIDE(ImageBase = .);
. = SEGMENT_START("text-segment", 0) + SIZEOF_HEADERS;
.note.gnu.build-id : { *(.note.gnu.build-id) }
.hash : { *(.hash) *(.gnu.hash) }
. = ALIGN(4096);
.text :
{
PROVIDE(_text = .);
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(SORT(.text.sorted.*))
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf.em. */
*(.gnu.warning)
}
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
. = ALIGN(4096);
.rdata :
{
*(.rodata .rodata.* .gnu.linkonce.r.*)
*(.rodata1)
KEEP (*(.eh_frame))
*(.eh_frame.*)
*(.dynamic)
}
. = ALIGN(4096);
.data :
{
*(.got) *(.igot)
*(.got.plt) *(.igot.plt)
*(.data .data.* .gnu.linkonce.d.*)
*(.data1)
*(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*)
*(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
*(.sdata .sdata.* .gnu.linkonce.s.*)
PROVIDE (_edata = .); PROVIDE (edata = .);
. = ALIGN(4096);
PROVIDE (__bss_start = .);
*(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
*(.dynsbss)
*(.sbss .sbss.* .gnu.linkonce.sb.*)
*(.scommon)
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(4096);
}
.reloc :
{
KEEP(*(.reloc*))
}
.rela :
{
*(.rela.*)
}
.data.rel.ro :
{
*(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*)
*(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*)
}
. = ALIGN(4096);
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}
-57
View File
@@ -1,57 +0,0 @@
ENTRY(start)
OUTPUT_FORMAT(elf32-i386)
SECTIONS {
/* The start address must match bootloader.asm */
. = 0x13000;
. += SIZEOF_HEADERS;
. = ALIGN(4096);
.text : {
__text_start = .;
*(.text*)
. = ALIGN(4096);
__text_end = .;
}
.rodata : {
__rodata_start = .;
*(.rodata*)
. = ALIGN(4096);
__rodata_end = .;
}
.data : {
__data_start = .;
*(.data*)
. = ALIGN(4096);
__data_end = .;
__bss_start = .;
*(.bss*)
. = ALIGN(4096);
__bss_end = .;
}
.tdata : {
__tdata_start = .;
*(.tdata*)
. = ALIGN(4096);
__tdata_end = .;
__tbss_start = .;
*(.tbss*)
. += 8;
. = ALIGN(4096);
__tbss_end = .;
}
__end = .;
/DISCARD/ : {
*(.comment*)
*(.eh_frame*)
*(.gcc_except_table*)
*(.note*)
*(.rel.eh_frame*)
}
}
-63
View File
@@ -1,63 +0,0 @@
export PARTED?=parted
export QEMU?=qemu-system-aarch64
all: $(BUILD)/bootloader.efi
$(BUILD)/bootloader.efi: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
mkdir -p "$(BUILD)"
env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \
cargo rustc \
--manifest-path="$<" \
$(CARGO_ARGS) \
--bin bootloader \
-- \
--emit link="$@"
$(BUILD)/bootloader-live.efi: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
mkdir -p "$(BUILD)"
env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \
cargo rustc \
--manifest-path="$<" \
$(CARGO_ARGS) \
--bin bootloader \
--features live \
-- \
--emit link="$@"
$(BUILD)/esp.bin: $(BUILD)/bootloader.efi
rm -f "$@.partial"
fallocate -l 64MiB "$@.partial"
mkfs.vfat -F 32 "$@.partial"
mmd -i "$@.partial" efi
mmd -i "$@.partial" efi/boot
mcopy -i "$@.partial" "$<" ::efi/boot/bootaa64.efi
mv "$@.partial" "$@"
$(BUILD)/harddrive.bin: $(BUILD)/esp.bin $(BUILD)/filesystem.bin
rm -f "$@.partial"
fallocate -l 320MiB "$@.partial"
$(PARTED) -s -a minimal "$@.partial" mklabel gpt
$(PARTED) -s -a minimal "$@.partial" mkpart ESP FAT32 1MiB 65MiB
$(PARTED) -s -a minimal "$@.partial" mkpart REDOXFS 65MiB 100%
$(PARTED) -s -a minimal "$@.partial" toggle 1 boot
dd if="$(BUILD)/esp.bin" of="$@.partial" bs=1MiB seek=1 conv=notrunc
dd if="$(BUILD)/filesystem.bin" of="$@.partial" bs=1MiB seek=65 conv=notrunc
mv "$@.partial" "$@"
$(BUILD)/firmware.rom: /usr/share/AAVMF/AAVMF_CODE.fd
cp "$<" "$@"
qemu: $(BUILD)/harddrive.bin $(BUILD)/firmware.rom
$(QEMU) \
-d cpu_reset \
-no-reboot \
-smp 4 -m 2048 \
-chardev stdio,id=debug,signal=off,mux=on \
-serial chardev:debug \
-mon chardev=debug \
-device virtio-gpu-pci \
-machine virt \
-net none \
-cpu max \
-bios "$(BUILD)/firmware.rom" \
-drive file="$<",format=raw
-102
View File
@@ -1,102 +0,0 @@
LD=riscv64-unknown-redox-ld
OBJCOPY=riscv64-unknown-redox-objcopy
SCRIPT=$(SOURCE)/linkers/riscv64-unknown-uefi.ld
PARTED?=parted
QEMU?=qemu-system-riscv64
all: $(BUILD)/bootloader.efi
$(BUILD)/%.efi: $(BUILD)/%.efi.elf $(BUILD)/%.efi.sym
$(OBJCOPY) -j .text -j .data -j .rdata -j .rela -j .reloc --target pei-riscv64-little \
--file-alignment 512 --section-alignment 4096 --subsystem 10 "$<" "$@"
.PRECIOUS: $(BUILD)/%.efi.sym
$(BUILD)/%.efi.sym: $(BUILD)/%.efi.elf
$(OBJCOPY) --only-keep-debug "$<" "$@"
$(BUILD)/%.efi.elf: $(BUILD)/%.a $(SCRIPT)
$(LD) --gc-sections -z max-page-size=0x1000 --warn-common --no-undefined -z nocombreloc -shared \
--fatal-warnings -Bsymbolic --entry coff_start -T "$(SCRIPT)" -o "$@" "$<"
$(BUILD)/bootloader.a: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
mkdir -p "$(BUILD)"
env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \
cargo rustc \
--manifest-path="$<" \
$(CARGO_ARGS) \
--lib \
-- \
--emit link=$@
$(BUILD)/bootloader-live.a: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
mkdir -p "$(BUILD)"
env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \
cargo rustc \
--manifest-path="$<" \
$(CARGO_ARGS) \
--lib \
--features live \
-- \
--emit link=$@
$(BUILD)/esp.bin: $(BUILD)/bootloader.efi
rm -f $@.partial
fallocate -l 64MiB $@.partial
mkfs.vfat -F 32 $@.partial
mmd -i $@.partial EFI
mmd -i $@.partial EFI/BOOT
mcopy -i $@.partial $< ::EFI/BOOT/BOOTRISCV64.EFI
mv $@.partial $@
$(BUILD)/harddrive.bin: $(BUILD)/esp.bin $(BUILD)/filesystem.bin
rm -f $@.partial
fallocate -l 320MiB $@.partial
$(PARTED) -s -a minimal $@.partial mklabel gpt
$(PARTED) -s -a minimal $@.partial mkpart ESP FAT32 1MiB 65MiB
$(PARTED) -s -a minimal $@.partial mkpart REDOXFS 65MiB 100%
$(PARTED) -s -a minimal $@.partial toggle 1 boot
dd if=$(BUILD)/esp.bin of=$@.partial bs=1MiB seek=1 conv=notrunc
dd if=$(BUILD)/filesystem.bin of=$@.partial bs=1MiB seek=65 conv=notrunc
mv $@.partial $@
$(BUILD)/fw_vars.img: /usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd
cp "$<" "$@"
$(BUILD)/firmware.rom: /usr/share/qemu-efi-riscv64/RISCV_VIRT_CODE.fd
cp "$<" "$@"
qemu-acpi: $(BUILD)/harddrive.bin $(BUILD)/firmware.rom $(BUILD)/fw_vars.img
$(QEMU) \
-M virt \
-d cpu_reset \
-no-reboot \
-smp 4 -m 2048 \
-chardev stdio,id=debug,signal=off,mux=on \
-serial chardev:debug \
-mon chardev=debug \
-device virtio-gpu-pci \
-machine virt \
-net none \
-cpu max \
-drive if=pflash,format=raw,unit=0,file=$(BUILD)/firmware.rom,readonly=on \
-drive if=pflash,format=raw,unit=1,file=$(BUILD)/fw_vars.img \
-drive file=$(BUILD)/harddrive.bin,format=raw,if=virtio
qemu-dtb: $(BUILD)/harddrive.bin $(BUILD)/firmware.rom $(BUILD)/fw_vars.img
$(QEMU) \
-M virt,acpi=off \
-d cpu_reset \
-no-reboot \
-smp 4 -m 2048 \
-chardev stdio,id=debug,signal=off,mux=on \
-serial chardev:debug \
-mon chardev=debug \
-device virtio-gpu-pci \
-machine virt \
-net none \
-cpu max \
-drive if=pflash,format=raw,unit=0,file=$(BUILD)/firmware.rom,readonly=on \
-drive if=pflash,format=raw,unit=1,file=$(BUILD)/fw_vars.img \
-drive file=$(BUILD)/harddrive.bin,format=raw,if=virtio -s
-59
View File
@@ -1,59 +0,0 @@
export LD?=ld
export OBJCOPY?=objcopy
export PARTED?=parted
export QEMU?=qemu-system-x86_64
all: $(BUILD)/bootloader.bin
$(BUILD)/libbootloader.a: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
mkdir -p "$(BUILD)"
env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \
cargo rustc \
--manifest-path="$<" \
$(CARGO_ARGS) \
--lib \
-- \
--emit link="$@"
$(BUILD)/libbootloader-live.a: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
mkdir -p "$(BUILD)"
env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \
cargo rustc \
--manifest-path="$<" \
$(CARGO_ARGS) \
--lib \
--features live \
-- \
--emit link="$@"
$(BUILD)/%.elf: $(BUILD)/lib%.a $(SOURCE)/linkers/$(TARGET).ld
$(LD) -m elf_i386 --gc-sections -z max-page-size=0x1000 -T "$(SOURCE)/linkers/$(TARGET).ld" -o "$@" "$<"
$(OBJCOPY) --only-keep-debug "$@" "$@.sym"
$(OBJCOPY) --strip-debug "$@"
$(BUILD)/%.bin: $(BUILD)/%.elf $(shell find $(SOURCE)/asm/$(TARGET) -type f)
nasm -f bin -o "$@" -l "$@.lst" -D STAGE3="$<" -i"$(SOURCE)/asm/$(TARGET)/" "$(SOURCE)/asm/$(TARGET)/bootloader.asm"
$(BUILD)/harddrive.bin: $(BUILD)/bootloader.bin $(BUILD)/filesystem.bin
rm -f "$@.partial"
fallocate -l 256MiB "$@.partial"
$(PARTED) -s -a minimal "$@.partial" mklabel msdos
$(PARTED) -s -a minimal "$@.partial" mkpart primary 2MiB 100%
dd if="$<" of="$@.partial" bs=1 count=446 conv=notrunc
dd if="$<" of="$@.partial" bs=512 skip=1 seek=1 conv=notrunc
dd if="$(BUILD)/filesystem.bin" of="$@.partial" bs=1MiB seek=2 conv=notrunc
mv "$@.partial" "$@"
qemu: $(BUILD)/harddrive.bin
$(QEMU) \
-d cpu_reset \
-no-reboot \
-smp 4 -m 2048 \
-chardev stdio,id=debug,signal=off,mux=on \
-serial chardev:debug \
-mon chardev=debug \
-machine q35 \
-net none \
-enable-kvm \
-cpu host \
-drive file="$<",format=raw
-64
View File
@@ -1,64 +0,0 @@
export PARTED?=parted
export QEMU?=qemu-system-x86_64
all: $(BUILD)/bootloader.efi
$(BUILD)/bootloader.efi: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
mkdir -p "$(BUILD)"
env RUSTFLAGS="--cfg aes_force_soft" \
cargo rustc \
--manifest-path="$<" \
$(CARGO_ARGS) \
--bin bootloader \
-- \
--emit link="$@"
$(BUILD)/bootloader-live.efi: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
mkdir -p $(BUILD)
cd "$(SOURCE)"
env RUSTFLAGS="--cfg aes_force_soft" \
cargo rustc \
--manifest-path="$<" \
$(CARGO_ARGS) \
--bin bootloader \
--features live \
-- \
--emit link="$@"
$(BUILD)/esp.bin: $(BUILD)/bootloader.efi
rm -f "$@.partial"
fallocate -l 1MiB $@.partial
mkfs.vfat "$@.partial"
mmd -i "$@.partial" efi
mmd -i "$@.partial" efi/boot
mcopy -i "$@.partial" "$<" ::efi/boot/bootx64.efi
mv "$@.partial" "$@"
$(BUILD)/harddrive.bin: $(BUILD)/esp.bin $(BUILD)/filesystem.bin
rm -f "$@.partial"
fallocate -l 320MiB "$@.partial"
$(PARTED) -s -a minimal "$@.partial" mklabel gpt
$(PARTED) -s -a minimal "$@.partial" mkpart ESP FAT32 1MiB 2MiB
$(PARTED) -s -a minimal "$@.partial" mkpart REDOXFS 2MiB 100%
$(PARTED) -s -a minimal "$@.partial" toggle 1 boot
dd if="$(BUILD)/esp.bin" of="$@.partial" bs=1MiB seek=1 conv=notrunc
dd if="$(BUILD)/filesystem.bin" of="$@.partial" bs=1MiB seek=2 conv=notrunc
mv "$@.partial" "$@"
$(BUILD)/firmware.rom: /usr/share/OVMF/OVMF_CODE.fd
cp "$<" "$@"
qemu: $(BUILD)/harddrive.bin $(BUILD)/firmware.rom
$(QEMU) \
-d cpu_reset \
-no-reboot \
-smp 4 -m 2048 \
-chardev stdio,id=debug,signal=off,mux=on \
-serial chardev:debug \
-mon chardev=debug \
-machine q35 \
-net none \
-enable-kvm \
-cpu host \
-bios "$(BUILD)/firmware.rom" \
-drive file="$<",format=raw
-3
View File
@@ -1,3 +0,0 @@
[toolchain]
channel = "nightly-2026-05-24"
components = ["rust-src"]
+192 -139
View File
@@ -1,154 +1,207 @@
use crate::area_add;
use crate::os::{dtb::is_in_dev_mem_region, Os, OsMemoryEntry, OsMemoryKind};
use core::slice;
use core::{
mem,
ops::{Deref, DerefMut},
slice,
};
pub(crate) const PF_PRESENT: u64 = 1 << 0;
pub(crate) const PF_TABLE: u64 = 1 << 1;
pub(crate) const PF_OUTER_SHAREABLE: u64 = 0b01 << 8;
pub(crate) const PF_INNER_SHAREABLE: u64 = 0b11 << 8;
pub(crate) const PF_ACCESS: u64 = 1 << 10;
use super::error::{Error, Result};
pub(crate) const PF_DEV: u64 = PF_OUTER_SHAREABLE | 2 << 2;
pub(crate) const PF_RAM: u64 = PF_INNER_SHAREABLE;
pub const PAGE_SIZE: usize = 4096;
/// Size of the metadata region used to transfer information from the kernel to the bootstrapper.
pub const KERNEL_METADATA_SIZE: usize = 4 * PAGE_SIZE;
pub(crate) const ENTRY_ADDRESS_MASK: u64 = 0x000F_FFFF_FFFF_F000;
pub(crate) const PAGE_ENTRIES: usize = 512;
const PAGE_SIZE: usize = 4096;
pub(crate) const PHYS_OFFSET: u64 = 0xFFFF_8000_0000_0000;
#[cfg(feature = "userspace")]
macro_rules! syscall {
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, $($g:ident, )?)?)?)?)?)?);)+) => {
$(
pub unsafe fn $name($a: usize, $($b: usize, $($c: usize, $($d: usize, $($e: usize, $($f: usize, $($g: usize)?)?)?)?)?)?) -> Result<usize> {
let ret: usize;
unsafe fn paging_allocate(os: &impl Os) -> Option<&'static mut [u64]> {
unsafe {
let ptr = os.alloc_zeroed_page_aligned(PAGE_SIZE);
if !ptr.is_null() {
area_add(OsMemoryEntry {
base: ptr as u64,
size: PAGE_SIZE as u64,
kind: OsMemoryKind::Reclaim,
});
Some(slice::from_raw_parts_mut(ptr as *mut u64, PAGE_ENTRIES))
} else {
None
core::arch::asm!(
"svc 0",
in("x8") $a,
$(
in("x0") $b,
$(
in("x1") $c,
$(
in("x2") $d,
$(
in("x3") $e,
$(
in("x4") $f,
$(
in("x5") $g,
)?
)?
)?
)?
)?
)?
lateout("x0") ret,
options(nostack),
);
Error::demux(ret)
}
)+
};
}
#[cfg(feature = "userspace")]
syscall! {
syscall0(a,);
syscall1(a, b,);
syscall2(a, b, c,);
syscall3(a, b, c, d,);
syscall4(a, b, c, d, e,);
syscall5(a, b, c, d, e, f,);
syscall6(a, b, c, d, e, f, g,);
}
#[derive(Copy, Clone, Debug, Default)]
#[repr(C)]
pub struct IntRegisters {
pub x30: usize,
pub x29: 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,
}
impl Deref for IntRegisters {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const IntRegisters as *const u8,
mem::size_of::<IntRegisters>(),
)
}
}
}
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
unsafe {
// Create L0
let l0 = paging_allocate(os)?;
{
// Create L1 for identity mapping
let l1 = paging_allocate(os)?;
// Link first user and first kernel L0 entry to L1
l0[0] = l1.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
l0[256] = l1.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
// Identity map 8 GiB using 1 GiB pages
for l1_i in 0..8 {
let addr = l1_i as u64 * 0x4000_0000;
//TODO: is PF_RAM okay?
l1[l1_i] = addr | PF_ACCESS | PF_DEV | PF_PRESENT;
}
impl DerefMut for IntRegisters {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut IntRegisters as *mut u8,
mem::size_of::<IntRegisters>(),
)
}
{
// Create L1 for kernel mapping
let l1 = paging_allocate(os)?;
// Link second to last L0 entry to L1
l0[510] = l1.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
// Map kernel_size at kernel offset
let mut kernel_mapped = 0;
let mut l1_i = 0;
while kernel_mapped < kernel_size && l1_i < l1.len() {
let l2 = paging_allocate(os)?;
l1[l1_i] = l2.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
l1_i += 1;
let mut l2_i = 0;
while kernel_mapped < kernel_size && l2_i < l2.len() {
let l3 = paging_allocate(os)?;
l2[l2_i] = l3.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
l2_i += 1;
let mut l3_i = 0;
while kernel_mapped < kernel_size && l3_i < l3.len() {
let addr = kernel_phys + kernel_mapped;
l3[l3_i] = addr | PF_ACCESS | PF_RAM | PF_TABLE | PF_PRESENT;
l3_i += 1;
kernel_mapped += PAGE_SIZE as u64;
}
}
}
assert!(kernel_mapped >= kernel_size);
}
Some(l0.as_ptr() as usize)
}
}
pub unsafe fn paging_framebuffer(
os: &impl Os,
page_phys: usize,
framebuffer_phys: u64,
framebuffer_size: u64,
) -> Option<u64> {
unsafe {
//TODO: smarter test for framebuffer already mapped
if framebuffer_phys + framebuffer_size <= 0x2_0000_0000 {
return Some(framebuffer_phys + PHYS_OFFSET);
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct FloatRegisters {
pub fp_simd_regs: [u128; 32],
pub fpsr: u32,
pub fpcr: u32,
}
impl Deref for FloatRegisters {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const FloatRegisters as *const u8,
mem::size_of::<FloatRegisters>(),
)
}
}
}
impl DerefMut for FloatRegisters {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut FloatRegisters as *mut u8,
mem::size_of::<FloatRegisters>(),
)
}
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct EnvRegisters {
pub tpidr_el0: usize,
pub tpidrro_el0: usize,
}
impl Deref for EnvRegisters {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const EnvRegisters as *const u8,
mem::size_of::<EnvRegisters>(),
)
}
}
}
impl DerefMut for EnvRegisters {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut EnvRegisters as *mut u8,
mem::size_of::<EnvRegisters>(),
)
}
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct Exception {
pub kind: usize,
// TODO
}
impl Deref for Exception {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const Exception as *const u8,
mem::size_of::<Exception>(),
)
}
}
}
impl DerefMut for Exception {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut Exception as *mut u8,
mem::size_of::<Exception>(),
)
}
let l0_i = ((framebuffer_phys / 0x80_0000_0000) + 256) as usize;
let mut l1_i = ((framebuffer_phys % 0x80_0000_0000) / 0x4000_0000) as usize;
let mut l2_i = ((framebuffer_phys % 0x4000_0000) / 0x20_0000) as usize;
let mut l3_i = ((framebuffer_phys % 0x20_0000) / (PAGE_SIZE as u64)) as usize;
assert_eq!(framebuffer_phys % (PAGE_SIZE as u64), 0);
let l0 = slice::from_raw_parts_mut(page_phys as *mut u64, PAGE_ENTRIES);
// Create l1 for framebuffer mapping
let l1 = if l0[l0_i] == 0 {
let l1 = paging_allocate(os)?;
l0[l0_i] = l1.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
l1
} else {
slice::from_raw_parts_mut((l0[l0_i] & ENTRY_ADDRESS_MASK) as *mut u64, PAGE_ENTRIES)
};
// Map framebuffer_size at framebuffer offset
let mut framebuffer_mapped = 0;
while framebuffer_mapped < framebuffer_size && l1_i < l1.len() {
let l2 = paging_allocate(os)?;
assert_eq!(l1[l1_i], 0);
l1[l1_i] = l2.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
while framebuffer_mapped < framebuffer_size && l2_i < l2.len() {
let l3 = paging_allocate(os)?;
assert_eq!(l2[l2_i], 0);
l2[l2_i] = l3.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
while framebuffer_mapped < framebuffer_size && l3_i < l3.len() {
let addr = framebuffer_phys + framebuffer_mapped;
assert_eq!(l3[l3_i], 0);
//TODO: is PF_RAM okay?
l3[l3_i] = addr | PF_ACCESS | PF_RAM | PF_TABLE | PF_PRESENT;
framebuffer_mapped += PAGE_SIZE as u64;
l3_i += 1;
}
l2_i += 1;
l3_i = 0;
}
l1_i += 1;
l2_i = 0;
}
assert!(framebuffer_mapped >= framebuffer_size);
Some(framebuffer_phys + PHYS_OFFSET)
}
}
-17
View File
@@ -1,17 +0,0 @@
#[cfg(target_arch = "aarch64")]
pub use self::aarch64::*;
#[cfg(target_arch = "aarch64")]
mod aarch64;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub use self::x86::*;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod x86;
#[cfg(target_arch = "riscv64")]
pub use self::riscv64::*;
#[cfg(target_arch = "riscv64")]
mod riscv64;
+206
View File
@@ -0,0 +1,206 @@
use super::error::{Error, Result};
use core::arch::asm;
use core::{
mem,
ops::{Deref, DerefMut},
slice,
};
pub const PAGE_SIZE: usize = 4096;
/// Size of the metadata region used to transfer information from the kernel to the bootstrapper.
pub const KERNEL_METADATA_SIZE: usize = 4 * PAGE_SIZE;
#[cfg(feature = "userspace")]
macro_rules! syscall {
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, $($g:ident, )?)?)?)?)?)?);)+) => {
$(
pub unsafe fn $name($a: usize, $($b: usize, $($c: usize, $($d: usize, $($e: usize, $($f: usize, $($g: usize)?)?)?)?)?)?) -> Result<usize> {
let ret: usize;
asm!(
"ecall",
in("a7") $a,
$(
in("a0") $b,
$(
in("a1") $c,
$(
in("a2") $d,
$(
in("a3") $e,
$(
in("a4") $f,
$(
in("a5") $g,
)?
)?
)?
)?
)?
)?
lateout("a0") ret,
options(nostack),
);
Error::demux(ret)
}
)+
};
}
#[cfg(feature = "userspace")]
syscall! {
syscall0(a,);
syscall1(a, b,);
syscall2(a, b, c,);
syscall3(a, b, c, d,);
syscall4(a, b, c, d, e,);
syscall5(a, b, c, d, e, f,);
syscall6(a, b, c, d, e, f, g,);
}
#[derive(Copy, Clone, Debug, Default)]
#[repr(C)]
pub struct IntRegisters {
pub pc: usize,
pub x31: usize,
pub x30: usize,
pub x29: 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,
// x4(tp) is in env
// x3(gp) is a platform scratch register
pub x2: usize,
pub x1: usize,
}
impl Deref for IntRegisters {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const IntRegisters as *const u8,
mem::size_of::<IntRegisters>(),
)
}
}
}
impl DerefMut for IntRegisters {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut IntRegisters as *mut u8,
mem::size_of::<IntRegisters>(),
)
}
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct FloatRegisters {
pub fregs: [u64; 32],
pub fcsr: u32,
}
impl Deref for FloatRegisters {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const FloatRegisters as *const u8,
mem::size_of::<FloatRegisters>(),
)
}
}
}
impl DerefMut for FloatRegisters {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut FloatRegisters as *mut u8,
mem::size_of::<FloatRegisters>(),
)
}
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(packed)]
pub struct EnvRegisters {
pub tp: usize,
}
impl Deref for EnvRegisters {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const EnvRegisters as *const u8,
mem::size_of::<EnvRegisters>(),
)
}
}
}
impl DerefMut for EnvRegisters {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut EnvRegisters as *mut u8,
mem::size_of::<EnvRegisters>(),
)
}
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct Exception {
pub kind: usize,
// TODO
}
impl Deref for Exception {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const Exception as *const u8,
mem::size_of::<Exception>(),
)
}
}
}
impl DerefMut for Exception {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut Exception as *mut u8,
mem::size_of::<Exception>(),
)
}
}
}
-59
View File
@@ -1,59 +0,0 @@
use core::slice;
use crate::area_add;
use crate::os::{Os, OsMemoryEntry, OsMemoryKind};
pub(crate) mod sv39;
pub(crate) mod sv48;
pub(crate) mod sv57;
// Common constants
const PAGE_SHIFT: usize = 12;
const TABLE_SHIFT: usize = 9;
const TABLE_MASK: usize = (1 << TABLE_SHIFT) - 1;
const PAGE_ENTRIES: usize = 512;
const PAGE_SIZE: usize = 4096;
const PHYS_MASK: usize = (1usize << 44) - 1;
const VALID: u64 = 1;
const RWX: u64 = 7 << 1;
const ACCESSED: u64 = 1 << 6;
const DIRTY: u64 = 1 << 7;
extern crate alloc;
pub(crate) use sv39::PHYS_OFFSET;
pub(crate) use sv39::SATP_BITS;
pub(crate) use sv39::paging_create;
pub(crate) use sv39::paging_physmem as paging_framebuffer;
unsafe fn paging_allocate(os: &impl Os) -> Option<&'static mut [u64]> {
unsafe {
let ptr = os.alloc_zeroed_page_aligned(PAGE_SIZE);
if !ptr.is_null() {
area_add(OsMemoryEntry {
base: ptr as u64,
size: PAGE_SIZE as u64,
kind: OsMemoryKind::Reclaim,
});
Some(slice::from_raw_parts_mut(ptr as *mut u64, PAGE_ENTRIES))
} else {
None
}
}
}
unsafe fn get_table(os: &impl Os, parent: &mut [u64], index: usize) -> Option<&'static mut [u64]> {
unsafe {
if parent[index] == 0 {
let table = paging_allocate(os)?;
parent[index] = table.as_ptr() as u64 >> 2 | VALID;
Some(table)
} else {
Some(slice::from_raw_parts_mut(
(((parent[index] >> 10) & PHYS_MASK as u64) << 12) as *mut u64,
PAGE_ENTRIES,
))
}
}
}
-90
View File
@@ -1,90 +0,0 @@
use core::slice;
use super::*;
use crate::os::Os;
// Sv39 scheme
pub(crate) const PHYS_OFFSET: u64 = 0xFFFF_FFC0_0000_0000;
pub(crate) const SATP_BITS: usize = 8;
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
unsafe {
// Create L2
let l2 = paging_allocate(os)?;
{
// Create L1 for identity mapping
for l2_i in 0..8 {
let addr = l2_i as u64 * 0x4000_0000;
// Identity map 8 GiB using 1GB pages
l2[l2_i] = addr >> 2 | RWX | VALID | ACCESSED | DIRTY;
// map phys into kernel VAS
l2[(PAGE_ENTRIES / 2) + l2_i] = addr >> 2 | RWX | VALID | ACCESSED | DIRTY;
}
}
{
// Create L1 for kernel mapping
let l1 = paging_allocate(os)?;
// Link second to last L0 entry to L1
l2[510] = l1.as_ptr() as u64 >> 2 | VALID;
// Map kernel_size at kernel offset
let mut kernel_mapped = 0;
let mut l1_i = 0;
while kernel_mapped < kernel_size && l1_i < l1.len() {
let l0 = paging_allocate(os)?;
l1[l1_i] = l0.as_ptr() as u64 >> 2 | VALID;
l1_i += 1;
let mut l0_i = 0;
while kernel_mapped < kernel_size && l0_i < l2.len() {
let addr = kernel_phys + kernel_mapped;
l0[l0_i] = addr >> 2 | RWX | VALID | ACCESSED | DIRTY;
l0_i += 1;
kernel_mapped += PAGE_SIZE as u64;
}
}
assert!(kernel_mapped >= kernel_size);
}
Some(l2.as_ptr() as usize)
}
}
pub unsafe fn paging_physmem(os: &impl Os, page_phys: usize, phys: u64, size: u64) -> Option<u64> {
unsafe {
if phys + size <= 0x2_0000_0000 {
return Some(phys + PHYS_OFFSET);
}
let mut l1_i = (phys as usize >> (PAGE_SHIFT + 2 * TABLE_SHIFT)) + PAGE_ENTRIES / 2;
let mut l0_i = (phys as usize >> (PAGE_SHIFT + TABLE_SHIFT)) & TABLE_MASK;
assert_eq!(phys & ((1 << (PAGE_SHIFT + TABLE_SHIFT)) - 1), 0);
let l1 = slice::from_raw_parts_mut(page_phys as *mut u64, PAGE_ENTRIES);
// Map framebuffer_size at framebuffer offset
let mut mapped = 0;
while mapped < size && l1_i < l1.len() {
let l0 = get_table(os, l1, l1_i)?;
while mapped < size && l0_i < l0.len() {
let addr = phys + mapped;
assert_eq!(l0[l0_i], 0);
l0[l0_i] = (addr >> 2) | RWX | VALID | ACCESSED | DIRTY;
mapped += 1 << (PAGE_SHIFT + TABLE_SHIFT); // Map with 2mb mega-pages
l0_i += 1;
}
l1_i += 1;
l0_i = 0;
}
assert!(mapped >= size);
Some(phys + PHYS_OFFSET)
}
}
-108
View File
@@ -1,108 +0,0 @@
use core::slice;
use super::*;
use crate::os::Os;
// Sv48 scheme
pub(crate) const PHYS_OFFSET: u64 = 0xFFFF_8000_0000_0000;
pub(crate) const SATP_BITS: usize = 9;
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
unsafe {
// Create L3
let l3 = paging_allocate(os)?;
{
// Create L2 for identity mapping
let l2 = paging_allocate(os)?;
// Map L2 into beginning of userspace and kernelspace
l3[0] = (l2.as_ptr() as u64 >> 2) | VALID;
l3[PAGE_ENTRIES / 2] = (l2.as_ptr() as u64 >> 2) | VALID;
// Identity map 8 GiB using 1GB pages
for l2_i in 0..8 {
let addr = l2_i as u64 * 0x4000_0000;
l2[l2_i] = addr >> 2 | RWX | VALID;
}
}
{
// Create L2 for kernel mapping
let l2 = paging_allocate(os)?;
// Link last L3 entry to L2
l3[511] = l2.as_ptr() as u64 >> 2 | VALID;
// Create L1 for kernel mapping
let l1 = paging_allocate(os)?;
// Link last L1 entry to L2
l2[510] = l1.as_ptr() as u64 >> 2 | VALID;
// Map kernel_size at kernel offset
let mut kernel_mapped = 0;
let mut l1_i = 0;
while kernel_mapped < kernel_size && l1_i < l1.len() {
let l0 = paging_allocate(os)?;
l1[l1_i] = l0.as_ptr() as u64 >> 2 | VALID;
l1_i += 1;
let mut l0_i = 0;
while kernel_mapped < kernel_size && l0_i < l2.len() {
let addr = kernel_phys + kernel_mapped;
l0[l0_i] = addr >> 2 | RWX | VALID | ACCESSED | DIRTY;
l0_i += 1;
kernel_mapped += PAGE_SIZE as u64;
}
}
assert!(kernel_mapped >= kernel_size);
}
Some(l3.as_ptr() as usize)
}
}
pub unsafe fn paging_physmem(os: &impl Os, page_phys: usize, phys: u64, size: u64) -> Option<u64> {
unsafe {
if phys + size <= 0x2_0000_0000 {
return Some(phys + PHYS_OFFSET);
}
let mut l2_i = (phys as usize >> (PAGE_SHIFT + 3 * TABLE_SHIFT)) + PAGE_ENTRIES / 2;
let mut l1_i = (phys as usize >> (PAGE_SHIFT + 2 * TABLE_SHIFT)) & TABLE_MASK;
let mut l0_i = (phys as usize >> (PAGE_SHIFT + TABLE_SHIFT)) & TABLE_MASK;
assert_eq!(phys & ((1 << (PAGE_SHIFT + TABLE_SHIFT)) - 1), 0);
let l2 = slice::from_raw_parts_mut(page_phys as *mut u64, PAGE_ENTRIES);
// Map framebuffer_size at framebuffer offset
let mut mapped = 0;
while mapped < size && l2_i < l2.len() {
let l1 = get_table(os, l2, l2_i)?;
while mapped < size && l1_i < l1.len() {
let l0 = get_table(os, l1, l1_i)?;
while mapped < size && l0_i < l0.len() {
let addr = phys + mapped;
assert_eq!(l0[l0_i], 0);
l0[l0_i] = (addr >> 2) | RWX | VALID;
mapped += 1 << (PAGE_SHIFT + TABLE_SHIFT);
l0_i += 1;
}
l1_i += 1;
l0_i = 0;
}
l2_i += 1;
l1_i = 0;
}
assert!(mapped >= size);
Some(phys + PHYS_OFFSET)
}
}
-124
View File
@@ -1,124 +0,0 @@
use core::slice;
use super::*;
use crate::os::Os;
// Sv57 scheme
pub(crate) const PHYS_OFFSET: u64 = 0xFF00_0000_0000_0000;
pub(crate) const SATP_BIT: usize = 10;
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
unsafe {
// Create L4
let l4 = paging_allocate(os)?;
{
// Create L3
let l3 = paging_allocate(os)?;
// Map L3 into beginning of userspace and kernelspace
l4[0] = (l3.as_ptr() as u64 >> 2) | VALID;
l4[PAGE_ENTRIES / 2] = (l3.as_ptr() as u64 >> 2) | VALID;
// Create L2 for identity mapping
let l2 = paging_allocate(os)?;
// Identity map 8 GiB using 1GB pages
for l2_i in 0..8 {
let addr = l2_i as u64 * 0x4000_0000;
l2[l2_i] = addr >> 2 | RWX | VALID;
}
}
{
// Create L3
let l3 = paging_allocate(os)?;
// Link last L4 entry to L3
l4[511] = l3.as_ptr() as u64 >> 2 | VALID;
// Create L2 for kernel mapping
let l2 = paging_allocate(os)?;
// Link last L3 entry to L2
l3[511] = l2.as_ptr() as u64 >> 2 | VALID;
// Create L1 for kernel mapping
let l1 = paging_allocate(os)?;
// Link last L1 entry to L2
l2[510] = l1.as_ptr() as u64 >> 2 | VALID;
// Map kernel_size at kernel offset
let mut kernel_mapped = 0;
let mut l1_i = 0;
while kernel_mapped < kernel_size && l1_i < l1.len() {
let l0 = paging_allocate(os)?;
l1[l1_i] = l0.as_ptr() as u64 >> 2 | VALID;
l1_i += 1;
let mut l0_i = 0;
while kernel_mapped < kernel_size && l0_i < l2.len() {
let addr = kernel_phys + kernel_mapped;
l0[l0_i] = addr >> 2 | RWX | VALID | ACCESSED | DIRTY;
l0_i += 1;
kernel_mapped += PAGE_SIZE as u64;
}
}
assert!(kernel_mapped >= kernel_size);
}
Some(l4.as_ptr() as usize)
}
}
pub unsafe fn paging_physmem(os: &impl Os, page_phys: usize, phys: u64, size: u64) -> Option<u64> {
unsafe {
if phys + size <= 0x2_0000_0000 {
return Some(phys + PHYS_OFFSET);
}
let mut l3_i = (phys as usize >> (PAGE_SHIFT + 4 * TABLE_SHIFT)) + PAGE_ENTRIES / 2;
let mut l2_i = (phys as usize >> (PAGE_SHIFT + 3 * TABLE_SHIFT)) & TABLE_MASK;
let mut l1_i = (phys as usize >> (PAGE_SHIFT + 2 * TABLE_SHIFT)) & TABLE_MASK;
let mut l0_i = (phys as usize >> (PAGE_SHIFT + TABLE_SHIFT)) & TABLE_MASK;
assert_eq!(phys & ((1 << (PAGE_SHIFT + TABLE_SHIFT)) - 1), 0);
let l3 = slice::from_raw_parts_mut(page_phys as *mut u64, PAGE_ENTRIES);
// Map framebuffer_size at framebuffer offset
let mut mapped = 0;
while mapped < size && l3_i < l3.len() {
let l2 = get_table(os, l3, l3_i)?;
while mapped < size && l2_i < l2.len() {
let l1 = get_table(os, l2, l2_i)?;
while mapped < size && l1_i < l1.len() {
let l0 = get_table(os, l1, l1_i)?;
while mapped < size && l0_i < l0.len() {
let addr = phys + mapped;
assert_eq!(l0[l0_i], 0);
l0[l0_i] = (addr >> 2) | RWX | VALID;
mapped += 1 << (PAGE_SHIFT + TABLE_SHIFT);
l0_i += 1;
}
l1_i += 1;
l0_i = 0;
}
l2_i += 1;
l1_i = 0;
}
l3_i += 1;
l2_i += 0;
}
assert!(mapped >= size);
Some(phys + PHYS_OFFSET)
}
}
+288
View File
@@ -0,0 +1,288 @@
use core::{
arch::asm,
mem,
ops::{Deref, DerefMut},
slice,
};
use super::error::{Error, Result};
pub const PAGE_SIZE: usize = 4096;
/// Size of the metadata region used to transfer information from the kernel to the bootstrapper.
pub const KERNEL_METADATA_SIZE: usize = 4 * PAGE_SIZE;
#[cfg(feature = "userspace")]
macro_rules! syscall {
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, )?)?)?)?)?);)+) => {
$(
pub unsafe fn $name(mut $a: usize, $($b: usize, $($c: usize, $($d: usize, $($e: usize, $($f: usize)?)?)?)?)?) -> Result<usize> {
asm!(
"int 0x80",
inout("eax") $a,
$(
in("ebx") $b,
$(
in("ecx") $c,
$(
in("edx") $d,
$(
in("esi") $e,
$(
in("edi") $f,
)?
)?
)?
)?
)?
options(nostack),
);
Error::demux($a)
}
)+
};
}
#[cfg(feature = "userspace")]
syscall! {
syscall0(a,);
syscall1(a, b,);
syscall2(a, b, c,);
syscall3(a, b, c, d,);
// Must be done custom because LLVM reserves ESI
//syscall4(a, b, c, d, e,);
//syscall5(a, b, c, d, e, f,);
//syscall6(a, b, c, d, e, f, g,);
}
#[cfg(feature = "userspace")]
pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> {
asm!(
"xchg esi, {e}
int 0x80
xchg esi, {e}",
e = in(reg) e,
inout("eax") a,
in("ebx") b,
in("ecx") c,
in("edx") d,
options(nostack),
);
Error::demux(a)
}
#[cfg(feature = "userspace")]
pub unsafe fn syscall5(
mut a: usize,
b: usize,
c: usize,
d: usize,
e: usize,
f: usize,
) -> Result<usize> {
asm!(
"xchg esi, {e}
int 0x80
xchg esi, {e}",
e = in(reg) e,
inout("eax") a,
in("ebx") b,
in("ecx") c,
in("edx") d,
in("edi") f,
options(nostack),
);
Error::demux(a)
}
#[cfg(feature = "userspace")]
pub unsafe fn syscall6(
mut a: usize,
b: usize,
c: usize,
d: usize,
e: usize,
f: usize,
g: usize,
) -> Result<usize> {
#[repr(C)]
struct PackedArgs {
arg4: usize,
arg6: usize,
nr: usize,
}
let args = PackedArgs {
arg4: e,
arg6: g,
nr: a,
};
let args_ptr = &args as *const PackedArgs;
asm!(
"push ebp",
"push esi",
"mov esi, [eax + 0]", // arg4 -> esi
"mov ebp, [eax + 4]", // arg6 -> ebp
"mov eax, [eax + 8]", // nr -> eax
"int 0x80",
"pop esi",
"pop ebp",
inout("eax") args_ptr => a,
in("ebx") b,
in("ecx") c,
in("edx") d,
in("edi") f,
options(nostack),
);
Error::demux(a)
}
#[derive(Copy, Clone, Debug, Default)]
#[repr(C)]
pub struct IntRegisters {
// TODO: Some of these don't get set by Redox yet. Should they?
pub ebp: usize,
pub esi: usize,
pub edi: usize,
pub ebx: usize,
pub eax: usize,
pub ecx: usize,
pub edx: usize,
// pub orig_rax: usize,
pub eip: usize,
pub cs: usize,
pub eflags: usize,
pub esp: usize,
pub ss: usize,
// pub fs_base: usize,
// pub gs_base: usize,
// pub ds: usize,
// pub es: usize,
pub fs: usize,
// pub gs: usize
}
impl Deref for IntRegisters {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const IntRegisters as *const u8,
mem::size_of::<IntRegisters>(),
)
}
}
}
impl DerefMut for IntRegisters {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut IntRegisters as *mut u8,
mem::size_of::<IntRegisters>(),
)
}
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct FloatRegisters {
pub fcw: u16,
pub fsw: u16,
pub ftw: u8,
pub _reserved: u8,
pub fop: u16,
pub fip: u64,
pub fdp: u64,
pub mxcsr: u32,
pub mxcsr_mask: u32,
pub st_space: [u128; 8],
pub xmm_space: [u128; 16],
// TODO: YMM/ZMM
}
impl Deref for FloatRegisters {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const FloatRegisters as *const u8,
mem::size_of::<FloatRegisters>(),
)
}
}
}
impl DerefMut for FloatRegisters {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut FloatRegisters as *mut u8,
mem::size_of::<FloatRegisters>(),
)
}
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct EnvRegisters {
pub fsbase: u32,
pub gsbase: u32,
}
impl Deref for EnvRegisters {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const EnvRegisters as *const u8,
mem::size_of::<EnvRegisters>(),
)
}
}
}
impl DerefMut for EnvRegisters {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut EnvRegisters as *mut u8,
mem::size_of::<EnvRegisters>(),
)
}
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct Exception {
pub kind: usize,
pub code: usize,
pub address: usize,
}
impl Deref for Exception {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const Exception as *const u8,
mem::size_of::<Exception>(),
)
}
}
}
impl DerefMut for Exception {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut Exception as *mut u8,
mem::size_of::<Exception>(),
)
}
}
}
-29
View File
@@ -1,29 +0,0 @@
use crate::os::Os;
pub(crate) mod x32;
pub(crate) mod x64;
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
unsafe {
if crate::KERNEL_64BIT {
x64::paging_create(os, kernel_phys, kernel_size)
} else {
x32::paging_create(os, kernel_phys, kernel_size)
}
}
}
pub unsafe fn paging_framebuffer(
os: &impl Os,
page_phys: usize,
framebuffer_phys: u64,
framebuffer_size: u64,
) -> Option<u64> {
unsafe {
if crate::KERNEL_64BIT {
x64::paging_framebuffer(os, page_phys, framebuffer_phys, framebuffer_size)
} else {
x32::paging_framebuffer(os, page_phys, framebuffer_phys, framebuffer_size)
}
}
}
-88
View File
@@ -1,88 +0,0 @@
use crate::area_add;
use crate::os::{Os, OsMemoryEntry, OsMemoryKind};
use core::slice;
const PAGE_ENTRIES: usize = 1024;
const PAGE_SIZE: usize = 4096;
pub(crate) const PHYS_OFFSET: u32 = 0x8000_0000;
unsafe fn paging_allocate(os: &impl Os) -> Option<&'static mut [u32]> {
unsafe {
let ptr = os.alloc_zeroed_page_aligned(PAGE_SIZE);
if !ptr.is_null() {
area_add(OsMemoryEntry {
base: ptr as u64,
size: PAGE_SIZE as u64,
kind: OsMemoryKind::Reclaim,
});
Some(slice::from_raw_parts_mut(ptr as *mut u32, PAGE_ENTRIES))
} else {
None
}
}
}
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
unsafe {
let pd = paging_allocate(os)?;
//Identity map 1 GiB using 4 MiB pages, also map at PHYS_OFFSET
for pd_i in 0..256 {
let addr = pd_i as u32 * 0x40_0000;
pd[pd_i] = addr | 1 << 7 | 1 << 1 | 1;
pd[pd_i + 512] = addr | 1 << 7 | 1 << 1 | 1;
}
// Map kernel_size at kernel offset
let mut kernel_mapped = 0;
let mut pd_i = 0xC000_0000 / 0x40_0000;
while kernel_mapped < kernel_size && pd_i < pd.len() {
let pt = paging_allocate(os)?;
pd[pd_i] = pt.as_ptr() as u32 | 1 << 1 | 1;
pd_i += 1;
let mut pt_i = 0;
while kernel_mapped < kernel_size && pt_i < pt.len() {
let addr = kernel_phys + kernel_mapped;
pt[pt_i] = addr as u32 | 1 << 1 | 1;
pt_i += 1;
kernel_mapped += PAGE_SIZE as u64;
}
}
assert!(kernel_mapped >= kernel_size);
Some(pd.as_ptr() as usize)
}
}
pub unsafe fn paging_framebuffer(
os: &impl Os,
page_phys: usize,
framebuffer_phys: u64,
framebuffer_size: u64,
) -> Option<u64> {
unsafe {
let framebuffer_virt = 0xD000_0000; // 256 MiB after kernel mapping, but before heap mapping
let pd = slice::from_raw_parts_mut(page_phys as *mut u32, PAGE_ENTRIES);
// Map framebuffer_size at framebuffer offset
let mut framebuffer_mapped = 0;
let mut pd_i = framebuffer_virt / 0x40_0000;
while framebuffer_mapped < framebuffer_size && pd_i < pd.len() {
let pt = paging_allocate(os)?;
pd[pd_i] = pt.as_ptr() as u32 | 1 << 1 | 1;
pd_i += 1;
let mut pt_i = 0;
while framebuffer_mapped < framebuffer_size && pt_i < pt.len() {
let addr = framebuffer_phys + framebuffer_mapped;
pt[pt_i] = addr as u32 | 1 << 1 | 1;
pt_i += 1;
framebuffer_mapped += PAGE_SIZE as u64;
}
}
assert!(framebuffer_mapped >= framebuffer_size);
Some(framebuffer_virt as u64)
}
}
-148
View File
@@ -1,148 +0,0 @@
use core::slice;
use crate::area_add;
use crate::os::{Os, OsMemoryEntry, OsMemoryKind};
const ENTRY_ADDRESS_MASK: u64 = 0x000F_FFFF_FFFF_F000;
const PAGE_ENTRIES: usize = 512;
const PAGE_SIZE: usize = 4096;
pub(crate) const PHYS_OFFSET: u64 = 0xFFFF_8000_0000_0000;
unsafe fn paging_allocate(os: &impl Os) -> Option<&'static mut [u64]> {
unsafe {
let ptr = os.alloc_zeroed_page_aligned(PAGE_SIZE);
if !ptr.is_null() {
area_add(OsMemoryEntry {
base: ptr as u64,
size: PAGE_SIZE as u64,
kind: OsMemoryKind::Reclaim,
});
Some(slice::from_raw_parts_mut(ptr as *mut u64, PAGE_ENTRIES))
} else {
None
}
}
}
const PRESENT: u64 = 1;
const WRITABLE: u64 = 1 << 1;
const LARGE: u64 = 1 << 7;
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
unsafe {
// Create PML4
let pml4 = paging_allocate(os)?;
{
// Create PDP for identity mapping
let pdp = paging_allocate(os)?;
// Link first user and first kernel PML4 entry to PDP
pml4[0] = pdp.as_ptr() as u64 | WRITABLE | PRESENT;
pml4[256] = pdp.as_ptr() as u64 | WRITABLE | PRESENT;
// Identity map 8 GiB using 2 MiB pages
for pdp_i in 0..8 {
let pd = paging_allocate(os)?;
pdp[pdp_i] = pd.as_ptr() as u64 | WRITABLE | PRESENT;
for pd_i in 0..pd.len() {
let addr = pdp_i as u64 * 0x4000_0000 + pd_i as u64 * 0x20_0000;
pd[pd_i] = addr | LARGE | WRITABLE | PRESENT;
}
}
}
{
// Create PDP (spanning 512 GiB) for kernel mapping
let pdp = paging_allocate(os)?;
// Link last PML4 entry to PDP
pml4[511] = pdp.as_ptr() as u64 | WRITABLE | PRESENT;
// Create PD (spanning 1 GiB) for kernel mapping.
let pd = paging_allocate(os)?;
// The kernel is mapped at -2^31, i.e. 0xFFFF_FFFF_8000_0000. Since a PD is 1 GiB, link
// the second last PDP entry to PD.
pdp[510] = pd.as_ptr() as u64 | WRITABLE | PRESENT;
// Map kernel_size bytes to kernel offset, i.e. to the start of the PD.
let mut kernel_mapped = 0;
let mut pd_idx = 0;
while kernel_mapped < kernel_size && pd_idx < pd.len() {
let pt = paging_allocate(os)?;
pd[pd_idx] = pt.as_ptr() as u64 | WRITABLE | PRESENT;
pd_idx += 1;
let mut pt_idx = 0;
while kernel_mapped < kernel_size && pt_idx < pt.len() {
let addr = kernel_phys + kernel_mapped;
pt[pt_idx] = addr | WRITABLE | PRESENT;
pt_idx += 1;
kernel_mapped += PAGE_SIZE as u64;
}
}
assert!(kernel_mapped >= kernel_size);
}
Some(pml4.as_ptr() as usize)
}
}
pub unsafe fn paging_framebuffer(
os: &impl Os,
page_phys: usize,
framebuffer_phys: u64,
framebuffer_size: u64,
) -> Option<u64> {
unsafe {
//TODO: smarter test for framebuffer already mapped
if framebuffer_phys + framebuffer_size <= 0x2_0000_0000 {
return Some(framebuffer_phys + PHYS_OFFSET);
}
let pml4_i = ((framebuffer_phys / 0x80_0000_0000) + 256) as usize;
let mut pdp_i = ((framebuffer_phys % 0x80_0000_0000) / 0x4000_0000) as usize;
let mut pd_i = ((framebuffer_phys % 0x4000_0000) / 0x20_0000) as usize;
assert_eq!(framebuffer_phys % 0x20_0000, 0);
let pml4 = slice::from_raw_parts_mut(page_phys as *mut u64, PAGE_ENTRIES);
// Create PDP for framebuffer mapping
let pdp = if pml4[pml4_i] == 0 {
let pdp = paging_allocate(os)?;
pml4[pml4_i] = pdp.as_ptr() as u64 | 1 << 1 | 1;
pdp
} else {
slice::from_raw_parts_mut(
(pml4[pml4_i] & ENTRY_ADDRESS_MASK) as *mut u64,
PAGE_ENTRIES,
)
};
// Map framebuffer_size at framebuffer offset
let mut framebuffer_mapped = 0;
while framebuffer_mapped < framebuffer_size && pdp_i < pdp.len() {
let pd = paging_allocate(os)?;
assert_eq!(pdp[pdp_i], 0);
pdp[pdp_i] = pd.as_ptr() as u64 | 1 << 1 | 1;
while framebuffer_mapped < framebuffer_size && pd_i < pd.len() {
let addr = framebuffer_phys + framebuffer_mapped;
assert_eq!(pd[pd_i], 0);
pd[pd_i] = addr | 1 << 7 | 1 << 1 | 1;
framebuffer_mapped += 0x20_0000;
pd_i += 1;
}
pdp_i += 1;
pd_i = 0;
}
assert!(framebuffer_mapped >= framebuffer_size);
Some(framebuffer_phys + PHYS_OFFSET)
}
}
+194
View File
@@ -0,0 +1,194 @@
use core::{
mem,
ops::{Deref, DerefMut},
slice,
};
pub const PAGE_SIZE: usize = 4096;
/// Size of the metadata region used to transfer information from the kernel to the bootstrapper.
pub const KERNEL_METADATA_SIZE: usize = 4 * PAGE_SIZE;
#[cfg(feature = "userspace")]
macro_rules! syscall {
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, $($g:ident, )?)?)?)?)?)?);)+) => {
$(
pub unsafe fn $name(mut $a: usize, $($b: usize, $($c: usize, $($d: usize, $($e: usize, $($f: usize, $($g: usize)?)?)?)?)?)?) -> crate::error::Result<usize> {
core::arch::asm!(
"syscall",
inout("rax") $a,
$(
in("rdi") $b,
$(
in("rsi") $c,
$(
in("rdx") $d,
$(
in("r10") $e,
$(
in("r8") $f,
$(
in("r9") $g,
)?
)?
)?
)?
)?
)?
out("rcx") _,
out("r11") _,
options(nostack),
);
crate::error::Error::demux($a)
}
)+
};
}
#[cfg(feature = "userspace")]
syscall! {
syscall0(a,);
syscall1(a, b,);
syscall2(a, b, c,);
syscall3(a, b, c, d,);
syscall4(a, b, c, d, e,);
syscall5(a, b, c, d, e, f,);
syscall6(a, b, c, d, e, f, g,);
}
#[derive(Copy, Clone, Debug, Default)]
#[repr(C)]
pub struct IntRegisters {
pub r15: usize,
pub r14: usize,
pub r13: usize,
pub r12: usize,
pub rbp: usize,
pub rbx: usize,
pub r11: usize,
pub r10: usize,
pub r9: usize,
pub r8: usize,
pub rax: usize,
pub rcx: usize,
pub rdx: usize,
pub rsi: usize,
pub rdi: usize,
pub rip: usize,
pub cs: usize,
pub rflags: usize,
pub rsp: usize,
pub ss: usize,
}
impl Deref for IntRegisters {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>()) }
}
}
impl DerefMut for IntRegisters {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut u8, mem::size_of::<Self>()) }
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct FloatRegisters {
pub fcw: u16,
pub fsw: u16,
pub ftw: u8,
pub _reserved: u8,
pub fop: u16,
pub fip: u64,
pub fdp: u64,
pub mxcsr: u32,
pub mxcsr_mask: u32,
pub st_space: [u128; 8],
pub xmm_space: [u128; 16],
// TODO: YMM/ZMM
}
impl Deref for FloatRegisters {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const FloatRegisters as *const u8,
mem::size_of::<FloatRegisters>(),
)
}
}
}
impl DerefMut for FloatRegisters {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut FloatRegisters as *mut u8,
mem::size_of::<FloatRegisters>(),
)
}
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct EnvRegisters {
pub fsbase: u64,
pub gsbase: u64,
// TODO: PKRU?
}
impl Deref for EnvRegisters {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const EnvRegisters as *const u8,
mem::size_of::<EnvRegisters>(),
)
}
}
}
impl DerefMut for EnvRegisters {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut EnvRegisters as *mut u8,
mem::size_of::<EnvRegisters>(),
)
}
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct Exception {
pub kind: usize,
pub code: usize,
pub address: usize,
}
impl Deref for Exception {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const Exception as *const u8,
mem::size_of::<Exception>(),
)
}
}
}
impl DerefMut for Exception {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut Exception as *mut u8,
mem::size_of::<Exception>(),
)
}
}
}
+425
View File
@@ -0,0 +1,425 @@
use super::{
arch::*,
data::{Map, Stat, StatVfs, StdFsCallMeta, TimeSpec},
error::Result,
flag::*,
number::*,
};
use core::mem;
/// Close a file descriptor
pub fn close(fd: usize) -> Result<usize> {
unsafe { syscall1(SYS_CLOSE, fd) }
}
/// Get the current system time
pub fn clock_gettime(clock: usize, tp: &mut TimeSpec) -> Result<usize> {
unsafe { syscall2(SYS_CLOCK_GETTIME, clock, tp as *mut TimeSpec as usize) }
}
/// Copy and transform a file descriptor into specified fd number
pub fn dup_into(fd: usize, out: usize, buf: &[u8]) -> Result<usize> {
unsafe { syscall4(SYS_DUP_INTO, fd, buf.as_ptr() as usize, buf.len(), out) }
}
/// Copy and transform a file descriptor, letting the kernel allocate the new fd
pub fn dup(fd: usize, buf: &[u8]) -> Result<usize> {
unsafe { syscall3(SYS_DUP, fd, buf.as_ptr() as usize, buf.len()) }
}
/// Copy and transform a file descriptor
pub fn dup2(fd: usize, newfd: usize, buf: &[u8]) -> Result<usize> {
unsafe { syscall4(SYS_DUP2, fd, newfd, buf.as_ptr() as usize, buf.len()) }
}
/// Change file permissions
pub fn fchmod(fd: usize, mode: u16) -> Result<usize> {
unsafe { syscall2(SYS_FCHMOD, fd, mode as usize) }
}
/// Change file ownership
pub fn fchown(fd: usize, uid: u32, gid: u32) -> Result<usize> {
unsafe { syscall3(SYS_FCHOWN, fd, uid as usize, gid as usize) }
}
/// Change file descriptor flags
pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result<usize> {
unsafe { syscall3(SYS_FCNTL, fd, cmd, arg) }
}
/// Map a file into memory, but with the ability to set the address to map into, either as a hint
/// or as a requirement of the map.
///
/// # Errors
/// `EACCES` - the file descriptor was not open for reading
/// `EBADF` - if the file descriptor was invalid
/// `ENODEV` - mmapping was not supported
/// `EINVAL` - invalid combination of flags
/// `EEXIST` - if [`MapFlags::MAP_FIXED`] was set, and the address specified was already in use.
///
pub unsafe fn fmap(fd: usize, map: &Map) -> Result<usize> {
syscall3(
SYS_FMAP,
fd,
map as *const Map as usize,
mem::size_of::<Map>(),
)
}
/// Unmap whole (or partial) continous memory-mapped files
pub unsafe fn funmap(addr: usize, len: usize) -> Result<usize> {
syscall2(SYS_FUNMAP, addr, len)
}
/// Retrieve the canonical path of a file
pub fn fpath(fd: usize, buf: &mut [u8]) -> Result<usize> {
unsafe { syscall3(SYS_FPATH, fd, buf.as_mut_ptr() as usize, buf.len()) }
}
/// Create a link to a file
pub fn flink<T: AsRef<str>>(fd: usize, path: T) -> Result<usize> {
let path = path.as_ref();
unsafe { syscall3(SYS_FLINK, fd, path.as_ptr() as usize, path.len()) }
}
/// Rename a file
pub fn frename<T: AsRef<str>>(fd: usize, path: T) -> Result<usize> {
let path = path.as_ref();
unsafe { syscall3(SYS_FRENAME, fd, path.as_ptr() as usize, path.len()) }
}
/// Get metadata about a file
pub fn fstat(fd: usize, stat: &mut Stat) -> Result<usize> {
unsafe {
syscall3(
SYS_FSTAT,
fd,
stat as *mut Stat as usize,
mem::size_of::<Stat>(),
)
}
}
/// Get metadata about a filesystem
pub fn fstatvfs(fd: usize, stat: &mut StatVfs) -> Result<usize> {
unsafe {
syscall3(
SYS_FSTATVFS,
fd,
stat as *mut StatVfs as usize,
mem::size_of::<StatVfs>(),
)
}
}
/// Sync a file descriptor to its underlying medium
pub fn fsync(fd: usize) -> Result<usize> {
unsafe { syscall1(SYS_FSYNC, fd) }
}
/// Truncate or extend a file to a specified length
pub fn ftruncate(fd: usize, len: usize) -> Result<usize> {
unsafe { syscall2(SYS_FTRUNCATE, fd, len) }
}
// Change modify and/or access times
pub fn futimens(fd: usize, times: &[TimeSpec]) -> Result<usize> {
unsafe {
syscall3(
SYS_FUTIMENS,
fd,
times.as_ptr() as usize,
mem::size_of_val(times),
)
}
}
/// Fast userspace mutex
pub unsafe fn futex(
addr: *mut i32,
op: usize,
val: i32,
val2: usize,
addr2: *mut i32,
) -> Result<usize> {
syscall5(
SYS_FUTEX,
addr as usize,
op,
(val as isize) as usize,
val2,
addr2 as usize,
)
}
/// Seek to `offset` bytes in a file descriptor
pub fn lseek(fd: usize, offset: isize, whence: usize) -> Result<usize> {
unsafe { syscall3(SYS_LSEEK, fd, offset as usize, whence) }
}
/// Make a new scheme namespace
pub fn mkns(schemes: &[[usize; 2]]) -> Result<usize> {
unsafe { syscall2(SYS_MKNS, schemes.as_ptr() as usize, schemes.len()) }
}
/// Change mapping flags
pub unsafe fn mprotect(addr: usize, size: usize, flags: MapFlags) -> Result<usize> {
syscall3(SYS_MPROTECT, addr, size, flags.bits())
}
/// Sleep for the time specified in `req`
pub fn nanosleep(req: &TimeSpec, rem: &mut TimeSpec) -> Result<usize> {
unsafe {
syscall2(
SYS_NANOSLEEP,
req as *const TimeSpec as usize,
rem as *mut TimeSpec as usize,
)
}
}
/// Open a file at a specific path into specified fd number
pub fn openat_into<T: AsRef<str>>(
fd: usize,
out: usize,
path: T,
flags: usize,
fcntl_flags: usize,
) -> Result<usize> {
let path = path.as_ref();
unsafe {
syscall6(
SYS_OPENAT_INTO,
fd,
path.as_ptr() as usize,
path.len(),
flags,
fcntl_flags,
out,
)
}
}
/// Open a file at a specific path, letting the kernel allocate the new fd
pub fn openat<T: AsRef<str>>(fd: usize, path: T, flags: usize, fcntl_flags: usize) -> Result<usize> {
let path = path.as_ref();
unsafe {
syscall5(
SYS_OPENAT,
fd,
path.as_ptr() as usize,
path.len(),
flags,
fcntl_flags,
)
}
}
/// Open a file at a specific path with uid/gid filtering.
/// Red Bear: legacy wrapper preserved for bootstrap namespace capability checks.
pub fn openat_with_filter<T: AsRef<str>>(
fd: usize,
path: T,
flags: usize,
fcntl_flags: usize,
uid: u32,
gid: u32,
) -> Result<usize> {
let path = path.as_ref();
unsafe {
syscall6(
SYS_OPENAT_WITH_FILTER,
fd,
path.as_ptr() as usize,
path.len(),
flags | fcntl_flags,
uid as usize,
gid as usize,
)
}
}
/// Send a file descriptor to another process via a socket
pub fn sendfd(receiver_socket: usize, fd: usize, flags: usize, arg: u64) -> Result<usize> {
#[cfg(target_pointer_width = "32")]
unsafe {
syscall5(
SYS_SENDFD,
receiver_socket,
fd,
flags,
arg as u32 as usize,
(arg >> 32) as usize,
)
}
#[cfg(target_pointer_width = "64")]
unsafe {
syscall4(SYS_SENDFD, receiver_socket, fd, flags, arg as usize)
}
}
/// Remove a file at at specific path
pub fn unlinkat<T: AsRef<str>>(fd: usize, path: T, flags: usize) -> Result<usize> {
let path = path.as_ref();
unsafe { syscall4(SYS_UNLINKAT, fd, path.as_ptr() as usize, path.len(), flags) }
}
/// Remove a file at a specific path with uid/gid filtering.
/// Red Bear: legacy wrapper preserved for bootstrap namespace capability checks.
pub fn unlinkat_with_filter<T: AsRef<str>>(
fd: usize,
path: T,
flags: usize,
uid: u32,
gid: u32,
) -> Result<usize> {
let path = path.as_ref();
unsafe {
syscall6(
SYS_UNLINKAT_WITH_FILTER,
fd,
path.as_ptr() as usize,
path.len(),
flags,
uid as usize,
gid as usize,
)
}
}
/// Read from a file descriptor into a buffer
pub fn read(fd: usize, buf: &mut [u8]) -> Result<usize> {
unsafe { syscall3(SYS_READ, fd, buf.as_mut_ptr() as usize, buf.len()) }
}
/// Write a buffer to a file descriptor
///
/// The kernel will attempt to write the bytes in `buf` to the file descriptor `fd`, returning
/// either an `Err`, explained below, or `Ok(count)` where `count` is the number of bytes which
/// were written.
///
/// # Errors
///
/// * `EAGAIN` - the file descriptor was opened with `O_NONBLOCK` and writing would block
/// * `EBADF` - the file descriptor is not valid or is not open for writing
/// * `EFAULT` - `buf` does not point to the process's addressible memory
/// * `EIO` - an I/O error occurred
/// * `ENOSPC` - the device containing the file descriptor has no room for data
/// * `EPIPE` - the file descriptor refers to a pipe or socket whose reading end is closed
pub fn write(fd: usize, buf: &[u8]) -> Result<usize> {
unsafe { syscall3(SYS_WRITE, fd, buf.as_ptr() as usize, buf.len()) }
}
/// Yield the process's time slice to the kernel
///
/// This function will return Ok(0) on success
pub fn sched_yield() -> Result<usize> {
unsafe { syscall0(SYS_YIELD) }
}
pub trait Call {
unsafe fn raw_call(
&self,
payload_ptr: *const u8,
len: usize,
flags: CallFlags,
metadata: &[u64],
) -> Result<usize>;
}
impl Call for usize {
unsafe fn raw_call(
&self,
payload_ptr: *const u8,
len: usize,
flags: CallFlags,
metadata: &[u64],
) -> Result<usize> {
unsafe {
syscall5(
SYS_CALL,
*self,
payload_ptr as usize,
len,
metadata.len() | flags.bits(),
metadata.as_ptr() as usize,
)
}
}
}
impl Call for &[usize] {
unsafe fn raw_call(
&self,
payload_ptr: *const u8,
len: usize,
flags: CallFlags,
metadata: &[u64],
) -> Result<usize> {
let combined_flags = flags | CallFlags::MULTIPLE_FDS;
unsafe {
syscall6(
SYS_CALL,
self.as_ptr() as usize,
payload_ptr as usize,
len,
metadata.len() | combined_flags.bits(),
metadata.as_ptr() as usize,
self.len() * mem::size_of::<usize>(),
)
}
}
}
/// SYS_CALL interface, read-only variant
pub fn call_ro<T: Call>(
fd: T,
payload: &mut [u8],
flags: CallFlags,
metadata: &[u64],
) -> Result<usize> {
unsafe {
fd.raw_call(
payload.as_mut_ptr(),
payload.len(),
flags | CallFlags::READ,
metadata,
)
}
}
/// SYS_CALL interface, write-only variant
pub fn call_wo<T: Call>(
fd: T,
payload: &[u8],
flags: CallFlags,
metadata: &[u64],
) -> Result<usize> {
unsafe {
fd.raw_call(
payload.as_ptr(),
payload.len(),
flags | CallFlags::WRITE,
metadata,
)
}
}
/// SYS_CALL interface, read-write variant
pub fn call_rw<T: Call>(
fd: T,
payload: &mut [u8],
flags: CallFlags,
metadata: &[u64],
) -> Result<usize> {
unsafe {
fd.raw_call(
payload.as_mut_ptr(),
payload.len(),
flags | CallFlags::READ | CallFlags::WRITE,
metadata,
)
}
}
pub fn std_fs_call<T: Call>(fd: T, payload: &mut [u8], metadata: &StdFsCallMeta) -> Result<usize> {
call_rw(fd, payload, CallFlags::STD_FS, metadata)
}
+501
View File
@@ -0,0 +1,501 @@
use core::{
mem,
ops::{Deref, DerefMut},
slice,
};
use crate::flag::{EventFlags, MapFlags, PtraceFlags, StdFsCallKind};
#[derive(Copy, Clone, Debug, Default)]
#[repr(C)]
pub struct Event {
pub id: usize,
pub flags: EventFlags,
pub data: usize,
}
impl Deref for Event {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Event as *const u8, mem::size_of::<Event>()) }
}
}
impl DerefMut for Event {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self as *mut Event as *mut u8, mem::size_of::<Event>()) }
}
}
#[derive(Copy, Clone, Debug, Default)]
#[repr(C)]
pub struct ITimerSpec {
pub it_interval: TimeSpec,
pub it_value: TimeSpec,
}
impl Deref for ITimerSpec {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const ITimerSpec as *const u8,
mem::size_of::<ITimerSpec>(),
)
}
}
}
impl DerefMut for ITimerSpec {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut ITimerSpec as *mut u8,
mem::size_of::<ITimerSpec>(),
)
}
}
}
#[derive(Copy, Clone, Debug, Default)]
#[repr(C)]
pub struct OldMap {
pub offset: usize,
pub size: usize,
pub flags: MapFlags,
}
impl Deref for OldMap {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(self as *const OldMap as *const u8, mem::size_of::<OldMap>())
}
}
}
impl DerefMut for OldMap {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(self as *mut OldMap as *mut u8, mem::size_of::<OldMap>())
}
}
}
#[derive(Copy, Clone, Debug, Default)]
#[repr(C)]
pub struct Map {
/// The offset inside the file that is being mapped.
pub offset: usize,
/// The size of the memory map.
pub size: usize,
/// Contains both prot and map flags.
pub flags: MapFlags,
/// Functions as a hint to where in the virtual address space of the running process, to place
/// the memory map. If [`MapFlags::MAP_FIXED`] is set, then this address must be the address to
/// map to.
pub address: usize,
}
impl Deref for Map {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Map as *const u8, mem::size_of::<Map>()) }
}
}
impl DerefMut for Map {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self as *mut Map as *mut u8, mem::size_of::<Map>()) }
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[repr(C)]
pub struct Stat {
pub st_dev: u64,
pub st_ino: u64,
pub st_mode: u16,
pub st_nlink: u32,
pub st_uid: u32,
pub st_gid: u32,
pub st_size: u64,
pub st_blksize: u32,
pub st_blocks: u64,
pub st_mtime: u64,
pub st_mtime_nsec: u32,
pub st_atime: u64,
pub st_atime_nsec: u32,
pub st_ctime: u64,
pub st_ctime_nsec: u32,
}
impl Deref for Stat {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Stat as *const u8, mem::size_of::<Stat>()) }
}
}
impl DerefMut for Stat {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self as *mut Stat as *mut u8, mem::size_of::<Stat>()) }
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[repr(C)]
pub struct StatVfs {
pub f_bsize: u32,
pub f_blocks: u64,
pub f_bfree: u64,
pub f_bavail: u64,
}
impl Deref for StatVfs {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const StatVfs as *const u8,
mem::size_of::<StatVfs>(),
)
}
}
}
impl DerefMut for StatVfs {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(self as *mut StatVfs as *mut u8, mem::size_of::<StatVfs>())
}
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[repr(C, packed)]
pub struct StdFsCallMeta {
pub kind: u8, // enum StdFsCallKind
_rsvd: [u8; 7],
pub arg1: u64,
pub arg2: u64,
}
impl StdFsCallMeta {
pub fn new(kind: StdFsCallKind, arg1: u64, arg2: u64) -> Self {
Self {
kind: kind as u8,
_rsvd: [0; 7],
arg1,
arg2,
}
}
}
impl Deref for StdFsCallMeta {
type Target = [u64];
fn deref(&self) -> &[u64] {
unsafe {
slice::from_raw_parts(
self as *const StdFsCallMeta as *const u64,
mem::size_of::<StdFsCallMeta>() / mem::size_of::<u64>(),
)
}
}
}
impl DerefMut for StdFsCallMeta {
fn deref_mut(&mut self) -> &mut [u64] {
unsafe {
slice::from_raw_parts_mut(
self as *mut StdFsCallMeta as *mut u64,
mem::size_of::<StdFsCallMeta>() / mem::size_of::<u64>(),
)
}
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[repr(C)]
pub struct TimeSpec {
pub tv_sec: i64,
pub tv_nsec: i32,
}
const NANOS_PER_SEC: u128 = 1_000_000_000;
impl TimeSpec {
pub fn from_nanos(nanos: u128) -> Self {
Self {
tv_sec: i64::try_from(nanos / NANOS_PER_SEC).unwrap_or(i64::MAX),
tv_nsec: (nanos % NANOS_PER_SEC) as i32, // guaranteed to never overflow
}
}
pub fn to_nanos(&self) -> u128 {
self.tv_sec as u128 * NANOS_PER_SEC + self.tv_nsec as u128
}
}
impl Deref for TimeSpec {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const TimeSpec as *const u8,
mem::size_of::<TimeSpec>(),
)
}
}
}
impl DerefMut for TimeSpec {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(self as *mut TimeSpec as *mut u8, mem::size_of::<TimeSpec>())
}
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct PtraceEvent {
pub cause: PtraceFlags,
pub a: usize,
pub b: usize,
pub c: usize,
pub d: usize,
pub e: usize,
pub f: usize,
}
impl Deref for PtraceEvent {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const PtraceEvent as *const u8,
mem::size_of::<PtraceEvent>(),
)
}
}
}
impl DerefMut for PtraceEvent {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut PtraceEvent as *mut u8,
mem::size_of::<PtraceEvent>(),
)
}
}
}
#[macro_export]
macro_rules! ptrace_event {
($cause:expr $(, $a:expr $(, $b:expr $(, $c:expr)?)?)?) => {
$crate::data::PtraceEvent {
cause: $cause,
$(a: $a,
$(b: $b,
$(c: $c,)?
)?
)?
..Default::default()
}
}
}
bitflags::bitflags! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)]
pub struct GrantFlags: usize {
const GRANT_READ = 0x0000_0001;
const GRANT_WRITE = 0x0000_0002;
const GRANT_EXEC = 0x0000_0004;
const GRANT_SHARED = 0x0000_0008;
const GRANT_LAZY = 0x0000_0010;
const GRANT_SCHEME = 0x0000_0020;
const GRANT_PHYS = 0x0000_0040;
const GRANT_PINNED = 0x0000_0080;
const GRANT_PHYS_CONTIGUOUS = 0x0000_0100;
}
}
impl GrantFlags {
#[deprecated = "use the safe `from_bits_retain` method instead"]
pub unsafe fn from_bits_unchecked(bits: usize) -> Self {
Self::from_bits_retain(bits)
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct GrantDesc {
pub base: usize,
pub size: usize,
pub flags: GrantFlags,
pub offset: u64,
}
impl Deref for GrantDesc {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const GrantDesc as *const u8,
mem::size_of::<GrantDesc>(),
)
}
}
}
impl DerefMut for GrantDesc {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut GrantDesc as *mut u8,
mem::size_of::<GrantDesc>(),
)
}
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SetSighandlerData {
pub user_handler: usize,
pub excp_handler: usize,
pub thread_control_addr: usize,
pub proc_control_addr: usize,
}
impl Deref for SetSighandlerData {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>()) }
}
}
impl DerefMut for SetSighandlerData {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut u8, mem::size_of::<Self>()) }
}
}
pub use crate::sigabi::*;
/// UNSTABLE
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[repr(C)]
pub struct ProcSchemeAttrs {
pub pid: u32,
pub euid: u32,
pub egid: u32,
pub prio: u32,
pub debug_name: [u8; 32],
}
impl Deref for ProcSchemeAttrs {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>()) }
}
}
impl DerefMut for ProcSchemeAttrs {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut ProcSchemeAttrs as *mut u8,
mem::size_of::<ProcSchemeAttrs>(),
)
}
}
}
#[derive(Copy, Clone, Debug, Default)]
#[repr(C)]
pub struct CtxtStsBuf {
pub status: usize,
pub excp: crate::Exception,
}
impl Deref for CtxtStsBuf {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>()) }
}
}
impl DerefMut for CtxtStsBuf {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut CtxtStsBuf as *mut u8,
mem::size_of::<CtxtStsBuf>(),
)
}
}
}
#[derive(Copy, Clone, Debug, Default)]
#[repr(C)]
pub struct NewFdParams {
pub offset: u64,
pub number: usize,
pub flags: usize,
pub internal_flags: u8,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum GlobalSchemes {
Debug = 1,
Event = 2,
Memory = 3,
Pipe = 4,
Serio = 5,
Irq = 6,
Time = 7,
Sys = 8,
Proc = 9,
Acpi = 10,
Dtb = 11,
}
impl GlobalSchemes {
pub fn try_from_raw(raw: u8) -> Option<Self> {
match raw {
1 => Some(Self::Debug),
2 => Some(Self::Event),
3 => Some(Self::Memory),
4 => Some(Self::Pipe),
5 => Some(Self::Serio),
6 => Some(Self::Irq),
7 => Some(Self::Time),
8 => Some(Self::Sys),
9 => Some(Self::Proc),
10 => Some(Self::Acpi),
11 => Some(Self::Dtb),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Debug => "debug",
Self::Event => "event",
Self::Memory => "memory",
Self::Pipe => "pipe",
Self::Serio => "serio",
Self::Irq => "irq",
Self::Time => "time",
Self::Sys => "sys",
Self::Proc => "kernel.proc",
Self::Acpi => "kernel.acpi",
Self::Dtb => "kernel.dtb",
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct KernelSchemeInfo {
pub scheme_id: u8,
pub fd: usize,
}
+231
View File
@@ -0,0 +1,231 @@
use core::{
mem::size_of,
ops::{Deref, DerefMut},
slice,
};
use crate::{
error::{Error, Result, EINVAL},
ENAMETOOLONG,
};
#[derive(Clone, Copy, Debug, Default)]
#[repr(packed)]
pub struct DirentHeader {
pub inode: u64,
/// A filesystem-specific opaque value used to uniquely identify directory entries. This value,
/// in the last returned entry from a SYS_GETDENTS invocation, shall be passed to the next
/// call.
pub next_opaque_id: u64,
// This struct intentionally does not include a "next" offset field, unlike Linux, to easily
// guarantee the iterator will be reasonably deterministic, even if the scheme is adversarial.
pub record_len: u16,
/// A `DirentKind`.
///
/// May not be directly available (Unspecified), and if so needs to be looked using fstat.
pub kind: u8,
}
impl Deref for DirentHeader {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
}
}
impl DerefMut for DirentHeader {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut u8, size_of::<Self>()) }
}
}
// Note: Must match relibc/include/bits/dirent.h
#[derive(Clone, Copy, Debug, Default)]
#[repr(u8)]
pub enum DirentKind {
#[default]
Unspecified = 0,
CharDev = 2,
Directory = 4,
BlockDev = 6,
Regular = 8,
Symlink = 10,
Socket = 12,
}
impl DirentKind {
// TODO: derive(FromPrimitive)
pub fn try_from_raw(raw: u8) -> Option<Self> {
Some(match raw {
0 => Self::Unspecified,
2 => Self::CharDev,
4 => Self::Directory,
6 => Self::BlockDev,
8 => Self::Regular,
10 => Self::Symlink,
12 => Self::Socket,
_ => return None,
})
}
}
pub struct DirentIter<'a>(&'a [u8]);
impl<'a> DirentIter<'a> {
pub const fn new(buffer: &'a [u8]) -> Self {
Self(buffer)
}
}
#[derive(Debug)]
pub struct Invalid;
impl<'a> Iterator for DirentIter<'a> {
type Item = Result<(&'a DirentHeader, &'a [u8]), Invalid>;
fn next(&mut self) -> Option<Self::Item> {
if self.0.len() < size_of::<DirentHeader>() {
return None;
}
let header = unsafe { &*(self.0.as_ptr().cast::<DirentHeader>()) };
if self.0.len() < usize::from(header.record_len) {
return Some(Err(Invalid));
}
let (this, remaining) = self.0.split_at(usize::from(header.record_len));
self.0 = remaining;
let name_and_nul = &this[size_of::<DirentHeader>()..];
let name = &name_and_nul[..name_and_nul.len() - 1];
Some(Ok((header, name)))
}
}
#[derive(Debug)]
pub struct DirentBuf<B> {
buffer: B,
// Exists in order to allow future extensions to the DirentHeader struct.
// TODO: Might add an upper bound to protect against cache miss DoS. The kernel currently
// forbids any other value than size_of::<DirentHeader>().
header_size: u16,
written: usize,
}
/// Abstraction between &mut [u8] and the kernel's UserSliceWo.
pub trait Buffer<'a>: Sized + 'a {
fn empty() -> Self;
fn length(&self) -> usize;
/// Split all of `self` into two disjoint contiguous subbuffers of lengths `index` and `length
/// - index` respectively.
///
/// Returns None if and only if `index > length`.
fn split_at(self, index: usize) -> Option<[Self; 2]>;
/// Copy from `src`, lengths must match exactly.
///
/// Allowed to overwrite subsequent buffer space, for performance reasons. Can be changed in
/// the future if too restrictive.
fn copy_from_slice_exact(self, src: &[u8]) -> Result<()>;
/// Write zeroes to this part of the buffer.
///
/// Allowed to overwrite subsequent buffer space, for performance reasons. Can be changed in
/// the future if too restrictive.
fn zero_out(self) -> Result<()>;
}
impl<'a> Buffer<'a> for &'a mut [u8] {
fn empty() -> Self {
&mut []
}
fn length(&self) -> usize {
self.len()
}
fn split_at(self, index: usize) -> Option<[Self; 2]> {
self.split_at_mut_checked(index).map(|(a, b)| [a, b])
}
fn copy_from_slice_exact(self, src: &[u8]) -> Result<()> {
self.copy_from_slice(src);
Ok(())
}
fn zero_out(self) -> Result<()> {
self.fill(0);
Ok(())
}
}
pub struct DirEntry<'name> {
pub inode: u64,
pub next_opaque_id: u64,
pub name: &'name str,
pub kind: DirentKind,
}
impl<'a, B: Buffer<'a>> DirentBuf<B> {
pub fn new(buffer: B, header_size: u16) -> Option<Self> {
if usize::from(header_size) < size_of::<DirentHeader>() {
return None;
}
Some(Self {
buffer,
header_size,
written: 0,
})
}
pub fn entry(&mut self, entry: DirEntry<'_>) -> Result<()> {
let name16 = u16::try_from(entry.name.len()).map_err(|_| Error::new(EINVAL))?;
let record_align = align_of::<*const DirentHeader>();
let record_len = self
.header_size
.checked_add(name16)
// XXX: NUL byte. Unfortunately this is probably the only performant way to be
// compatible with C.
.and_then(|l| l.checked_add(1))
// Align length so next header is aligned
.and_then(|l| l.checked_next_multiple_of(record_align as u16))
.ok_or(Error::new(ENAMETOOLONG))?;
let [this, remaining] = core::mem::replace(&mut self.buffer, B::empty())
.split_at(usize::from(record_len))
.ok_or(Error::new(EINVAL))?;
let [this_header_variable, this_name_and_nul] = this
.split_at(usize::from(self.header_size))
.expect("already know header_size + ... >= header_size");
let [this_name, this_name_nul] = this_name_and_nul
.split_at(usize::from(name16))
.expect("already know name.len() <= name.len() + 1");
// Every write here is currently sequential, allowing the buffer trait to do optimizations
// where subbuffer writes are out-of-bounds (but inside the total buffer).
let [this_header, this_header_extra] = this_header_variable
.split_at(size_of::<DirentHeader>())
.expect("already checked header_size <= size_of Header");
this_header.copy_from_slice_exact(&DirentHeader {
record_len,
next_opaque_id: entry.next_opaque_id,
inode: entry.inode,
kind: entry.kind as u8,
})?;
this_header_extra.zero_out()?;
this_name.copy_from_slice_exact(entry.name.as_bytes())?;
this_name_nul.zero_out()?;
self.written += usize::from(record_len);
self.buffer = remaining;
Ok(())
}
pub fn finalize(self) -> usize {
self.written
}
}
-121
View File
@@ -1,121 +0,0 @@
use crate::os::{Os, OsKey};
fn edit_banner(os: &impl Os) {
os.clear_text();
println!("--- Redox Bootloader Environment Editor ---");
println!("ENTER twice to boot. UP/DOWN to edit lines.");
println!("-------------------------------------------");
}
pub fn edit_env(os: &impl Os, env_ptr: *mut u8, env_size: &mut usize, max_size: usize) {
edit_banner(os);
let env_slice = unsafe { core::slice::from_raw_parts_mut(env_ptr, max_size) };
// counting at line index
let mut cursor = 0xFFF;
// position at current line, not including LF
let mut cursor_start = 0;
let mut cursor_end = 0;
let original_size = *env_size;
loop {
os.set_text_position(0, 4);
let mut iline = 0;
for i in 0..*env_size {
let c = env_slice[i] as char;
if c == '\n' {
os.set_text_highlight(iline == cursor);
print!(" ");
os.set_text_highlight(false);
print!("\n");
iline += 1;
if iline == cursor {
cursor_start = i + 1;
}
} else {
print!("{}", c);
}
if iline == cursor {
cursor_end = i + 1;
}
}
if cursor > iline {
cursor = iline;
// update cursors, should never hang
continue;
}
os.set_text_highlight(iline == cursor);
print!(" ");
os.set_text_highlight(false);
match os.get_key() {
OsKey::Enter => {
if cursor_start == cursor_end {
// blank line to boot
break;
}
if *env_size < max_size - 1 {
if *env_size == max_size {
continue;
}
for i in (cursor_end..*env_size).rev() {
env_slice[i + 1] = env_slice[i];
}
env_slice[cursor_end] = b'\n';
*env_size += 1;
cursor += 1;
edit_banner(os);
}
}
OsKey::Backspace => {
if cursor_end == 0 || *env_size == 0 {
continue;
}
if cursor_start == cursor_end && iline > 0 {
iline -= 1;
}
for i in cursor_end..*env_size {
env_slice[i - 1] = env_slice[i];
}
*env_size -= 1;
edit_banner(os);
}
OsKey::Up => {
if cursor > 0 {
cursor -= 1;
}
}
OsKey::Down => {
cursor += 1;
}
OsKey::Char(c) => {
if *env_size == max_size {
continue;
}
for i in (cursor_end..*env_size).rev() {
env_slice[i + 1] = env_slice[i];
}
env_slice[cursor_end] = c as u8;
*env_size += 1;
}
_ => (),
}
}
if *env_size == 0 || env_slice[*env_size - 1] != b'\n' {
if *env_size < max_size {
env_slice[*env_size] = b'\n';
*env_size += 1;
}
}
if *env_size < original_size {
for i in (*env_size..original_size).rev() {
env_slice[i] = 0;
}
}
println!("\nBooting...");
}
+327
View File
@@ -0,0 +1,327 @@
use core::{fmt, result};
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Error {
pub errno: i32,
}
pub type Result<T, E = Error> = result::Result<T, E>;
impl Error {
pub fn new(errno: i32) -> Error {
Error { errno }
}
pub fn mux(result: Result<usize>) -> usize {
match result {
Ok(value) => value,
Err(error) => -error.errno as usize,
}
}
pub fn demux(value: usize) -> Result<usize> {
let errno = -(value as i32);
if errno >= 1 && errno < STR_ERROR.len() as i32 {
Err(Error::new(errno))
} else {
Ok(value)
}
}
pub fn text(&self) -> &'static str {
STR_ERROR
.get(self.errno as usize)
.map(|&x| x)
.unwrap_or("Unknown Error")
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
f.write_str(self.text())
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
f.write_str(self.text())
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
#[cfg(feature = "std")]
impl From<Error> for std::io::Error {
fn from(value: Error) -> Self {
std::io::Error::from_raw_os_error(value.errno)
}
}
pub const EPERM: i32 = 1; /* Operation not permitted */
pub const ENOENT: i32 = 2; /* No such file or directory */
pub const ESRCH: i32 = 3; /* No such process */
pub const EINTR: i32 = 4; /* Interrupted system call */
pub const EIO: i32 = 5; /* I/O error */
pub const ENXIO: i32 = 6; /* No such device or address */
pub const E2BIG: i32 = 7; /* Argument list too long */
pub const ENOEXEC: i32 = 8; /* Exec format error */
pub const EBADF: i32 = 9; /* Bad file number */
pub const ECHILD: i32 = 10; /* No child processes */
pub const EAGAIN: i32 = 11; /* Try again */
pub const ENOMEM: i32 = 12; /* Out of memory */
pub const EACCES: i32 = 13; /* Permission denied */
pub const EFAULT: i32 = 14; /* Bad address */
pub const ENOTBLK: i32 = 15; /* Block device required */
pub const EBUSY: i32 = 16; /* Device or resource busy */
pub const EEXIST: i32 = 17; /* File exists */
pub const EXDEV: i32 = 18; /* Cross-device link */
pub const ENODEV: i32 = 19; /* No such device */
pub const ENOTDIR: i32 = 20; /* Not a directory */
pub const EISDIR: i32 = 21; /* Is a directory */
pub const EINVAL: i32 = 22; /* Invalid argument */
pub const ENFILE: i32 = 23; /* File table overflow */
pub const EMFILE: i32 = 24; /* Too many open files */
pub const ENOTTY: i32 = 25; /* Not a typewriter */
pub const ETXTBSY: i32 = 26; /* Text file busy */
pub const EFBIG: i32 = 27; /* File too large */
pub const ENOSPC: i32 = 28; /* No space left on device */
pub const ESPIPE: i32 = 29; /* Illegal seek */
pub const EROFS: i32 = 30; /* Read-only file system */
pub const EMLINK: i32 = 31; /* Too many links */
pub const EPIPE: i32 = 32; /* Broken pipe */
pub const EDOM: i32 = 33; /* Math argument out of domain of func */
pub const ERANGE: i32 = 34; /* Math result not representable */
pub const EDEADLK: i32 = 35; /* Resource deadlock would occur */
pub const ENAMETOOLONG: i32 = 36; /* File name too long */
pub const ENOLCK: i32 = 37; /* No record locks available */
pub const ENOSYS: i32 = 38; /* Function not implemented */
pub const ENOTEMPTY: i32 = 39; /* Directory not empty */
pub const ELOOP: i32 = 40; /* Too many symbolic links encountered */
pub const EWOULDBLOCK: i32 = 41; /* Operation would block */
pub const ENOMSG: i32 = 42; /* No message of desired type */
pub const EIDRM: i32 = 43; /* Identifier removed */
pub const ECHRNG: i32 = 44; /* Channel number out of range */
pub const EL2NSYNC: i32 = 45; /* Level 2 not synchronized */
pub const EL3HLT: i32 = 46; /* Level 3 halted */
pub const EL3RST: i32 = 47; /* Level 3 reset */
pub const ELNRNG: i32 = 48; /* Link number out of range */
pub const EUNATCH: i32 = 49; /* Protocol driver not attached */
pub const ENOCSI: i32 = 50; /* No CSI structure available */
pub const EL2HLT: i32 = 51; /* Level 2 halted */
pub const EBADE: i32 = 52; /* Invalid exchange */
pub const EBADR: i32 = 53; /* Invalid request descriptor */
pub const EXFULL: i32 = 54; /* Exchange full */
pub const ENOANO: i32 = 55; /* No anode */
pub const EBADRQC: i32 = 56; /* Invalid request code */
pub const EBADSLT: i32 = 57; /* Invalid slot */
pub const EDEADLOCK: i32 = 58; /* Resource deadlock would occur */
pub const EBFONT: i32 = 59; /* Bad font file format */
pub const ENOSTR: i32 = 60; /* Device not a stream */
pub const ENODATA: i32 = 61; /* No data available */
pub const ETIME: i32 = 62; /* Timer expired */
pub const ENOSR: i32 = 63; /* Out of streams resources */
pub const ENONET: i32 = 64; /* Machine is not on the network */
pub const ENOPKG: i32 = 65; /* Package not installed */
pub const EREMOTE: i32 = 66; /* Object is remote */
pub const ENOLINK: i32 = 67; /* Link has been severed */
pub const EADV: i32 = 68; /* Advertise error */
pub const ESRMNT: i32 = 69; /* Srmount error */
pub const ECOMM: i32 = 70; /* Communication error on send */
pub const EPROTO: i32 = 71; /* Protocol error */
pub const EMULTIHOP: i32 = 72; /* Multihop attempted */
pub const EDOTDOT: i32 = 73; /* RFS specific error */
pub const EBADMSG: i32 = 74; /* Not a data message */
pub const EOVERFLOW: i32 = 75; /* Value too large for defined data type */
pub const ENOTUNIQ: i32 = 76; /* Name not unique on network */
pub const EBADFD: i32 = 77; /* File descriptor in bad state */
pub const EREMCHG: i32 = 78; /* Remote address changed */
pub const ELIBACC: i32 = 79; /* Can not access a needed shared library */
pub const ELIBBAD: i32 = 80; /* Accessing a corrupted shared library */
pub const ELIBSCN: i32 = 81; /* .lib section in a.out corrupted */
pub const ELIBMAX: i32 = 82; /* Attempting to link in too many shared libraries */
pub const ELIBEXEC: i32 = 83; /* Cannot exec a shared library directly */
pub const EILSEQ: i32 = 84; /* Illegal byte sequence */
pub const ERESTART: i32 = 85; /* Interrupted system call should be restarted */
pub const ESTRPIPE: i32 = 86; /* Streams pipe error */
pub const EUSERS: i32 = 87; /* Too many users */
pub const ENOTSOCK: i32 = 88; /* Socket operation on non-socket */
pub const EDESTADDRREQ: i32 = 89; /* Destination address required */
pub const EMSGSIZE: i32 = 90; /* Message too long */
pub const EPROTOTYPE: i32 = 91; /* Protocol wrong type for socket */
pub const ENOPROTOOPT: i32 = 92; /* Protocol not available */
pub const EPROTONOSUPPORT: i32 = 93; /* Protocol not supported */
pub const ESOCKTNOSUPPORT: i32 = 94; /* Socket type not supported */
pub const EOPNOTSUPP: i32 = 95; /* Operation not supported on transport endpoint */
pub const EPFNOSUPPORT: i32 = 96; /* Protocol family not supported */
pub const EAFNOSUPPORT: i32 = 97; /* Address family not supported by protocol */
pub const EADDRINUSE: i32 = 98; /* Address already in use */
pub const EADDRNOTAVAIL: i32 = 99; /* Cannot assign requested address */
pub const ENETDOWN: i32 = 100; /* Network is down */
pub const ENETUNREACH: i32 = 101; /* Network is unreachable */
pub const ENETRESET: i32 = 102; /* Network dropped connection because of reset */
pub const ECONNABORTED: i32 = 103; /* Software caused connection abort */
pub const ECONNRESET: i32 = 104; /* Connection reset by peer */
pub const ENOBUFS: i32 = 105; /* No buffer space available */
pub const EISCONN: i32 = 106; /* Transport endpoint is already connected */
pub const ENOTCONN: i32 = 107; /* Transport endpoint is not connected */
pub const ESHUTDOWN: i32 = 108; /* Cannot send after transport endpoint shutdown */
pub const ETOOMANYREFS: i32 = 109; /* Too many references: cannot splice */
pub const ETIMEDOUT: i32 = 110; /* Connection timed out */
pub const ECONNREFUSED: i32 = 111; /* Connection refused */
pub const EHOSTDOWN: i32 = 112; /* Host is down */
pub const EHOSTUNREACH: i32 = 113; /* No route to host */
pub const EALREADY: i32 = 114; /* Operation already in progress */
pub const EINPROGRESS: i32 = 115; /* Operation now in progress */
pub const ESTALE: i32 = 116; /* Stale NFS file handle */
pub const EUCLEAN: i32 = 117; /* Structure needs cleaning */
pub const ENOTNAM: i32 = 118; /* Not a XENIX named type file */
pub const ENAVAIL: i32 = 119; /* No XENIX semaphores available */
pub const EISNAM: i32 = 120; /* Is a named type file */
pub const EREMOTEIO: i32 = 121; /* Remote I/O error */
pub const EDQUOT: i32 = 122; /* Quota exceeded */
pub const ENOMEDIUM: i32 = 123; /* No medium found */
pub const EMEDIUMTYPE: i32 = 124; /* Wrong medium type */
pub const ECANCELED: i32 = 125; /* Operation Canceled */
pub const ENOKEY: i32 = 126; /* Required key not available */
pub const EKEYEXPIRED: i32 = 127; /* Key has expired */
pub const EKEYREVOKED: i32 = 128; /* Key has been revoked */
pub const EKEYREJECTED: i32 = 129; /* Key was rejected by service */
pub const EOWNERDEAD: i32 = 130; /* Owner died */
pub const ENOTRECOVERABLE: i32 = 131; /* State not recoverable */
pub const ERSVD: i32 = 132; /* Reserved (formerly "scheme-kernel message code") */
pub static STR_ERROR: [&'static str; 133] = [
"Success",
"Operation not permitted",
"No such file or directory",
"No such process",
"Interrupted system call",
"I/O error",
"No such device or address",
"Argument list too long",
"Exec format error",
"Bad file number",
"No child processes",
"Try again",
"Out of memory",
"Permission denied",
"Bad address",
"Block device required",
"Device or resource busy",
"File exists",
"Cross-device link",
"No such device",
"Not a directory",
"Is a directory",
"Invalid argument",
"File table overflow",
"Too many open files",
"Not a typewriter",
"Text file busy",
"File too large",
"No space left on device",
"Illegal seek",
"Read-only file system",
"Too many links",
"Broken pipe",
"Math argument out of domain of func",
"Math result not representable",
"Resource deadlock would occur",
"File name too long",
"No record locks available",
"Function not implemented",
"Directory not empty",
"Too many symbolic links encountered",
"Operation would block",
"No message of desired type",
"Identifier removed",
"Channel number out of range",
"Level 2 not synchronized",
"Level 3 halted",
"Level 3 reset",
"Link number out of range",
"Protocol driver not attached",
"No CSI structure available",
"Level 2 halted",
"Invalid exchange",
"Invalid request descriptor",
"Exchange full",
"No anode",
"Invalid request code",
"Invalid slot",
"Resource deadlock would occur",
"Bad font file format",
"Device not a stream",
"No data available",
"Timer expired",
"Out of streams resources",
"Machine is not on the network",
"Package not installed",
"Object is remote",
"Link has been severed",
"Advertise error",
"Srmount error",
"Communication error on send",
"Protocol error",
"Multihop attempted",
"RFS specific error",
"Not a data message",
"Value too large for defined data type",
"Name not unique on network",
"File descriptor in bad state",
"Remote address changed",
"Can not access a needed shared library",
"Accessing a corrupted shared library",
".lib section in a.out corrupted",
"Attempting to link in too many shared libraries",
"Cannot exec a shared library directly",
"Illegal byte sequence",
"Interrupted system call should be restarted",
"Streams pipe error",
"Too many users",
"Socket operation on non-socket",
"Destination address required",
"Message too long",
"Protocol wrong type for socket",
"Protocol not available",
"Protocol not supported",
"Socket type not supported",
"Operation not supported on transport endpoint",
"Protocol family not supported",
"Address family not supported by protocol",
"Address already in use",
"Cannot assign requested address",
"Network is down",
"Network is unreachable",
"Network dropped connection because of reset",
"Software caused connection abort",
"Connection reset by peer",
"No buffer space available",
"Transport endpoint is already connected",
"Transport endpoint is not connected",
"Cannot send after transport endpoint shutdown",
"Too many references: cannot splice",
"Connection timed out",
"Connection refused",
"Host is down",
"No route to host",
"Operation already in progress",
"Operation now in progress",
"Stale NFS file handle",
"Structure needs cleaning",
"Not a XENIX named type file",
"No XENIX semaphores available",
"Is a named type file",
"Remote I/O error",
"Quota exceeded",
"No medium found",
"Wrong medium type",
"Operation Canceled",
"Required key not available",
"Key has expired",
"Key has been revoked",
"Key was rejected by service",
"Owner died",
"State not recoverable",
"Reserved (formerly scheme-kernel message code)",
];
+573
View File
@@ -0,0 +1,573 @@
use bitflags::bitflags as inner_bitflags;
use core::{mem, ops::Deref, slice};
macro_rules! bitflags {
(
$(#[$outer:meta])*
pub struct $BitFlags:ident: $T:ty {
$(
$(#[$inner:ident $($args:tt)*])*
const $Flag:ident = $value:expr;
)+
}
) => {
// First, use the inner bitflags
inner_bitflags! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)]
$(#[$outer])*
pub struct $BitFlags: $T {
$(
$(#[$inner $($args)*])*
const $Flag = $value;
)+
}
}
impl $BitFlags {
#[deprecated = "use the safe `from_bits_retain` method instead"]
pub unsafe fn from_bits_unchecked(bits: $T) -> Self {
Self::from_bits_retain(bits)
}
}
// Secondly, re-export all inner constants
// (`pub use self::Struct::*` doesn't work)
$(
$(#[$inner $($args)*])*
pub const $Flag: $BitFlags = $BitFlags::$Flag;
)+
}
}
pub const CLOCK_REALTIME: usize = 1;
pub const CLOCK_MONOTONIC: usize = 4;
bitflags! {
pub struct EventFlags: usize {
const EVENT_NONE = 0;
const EVENT_READ = 1;
const EVENT_WRITE = 2;
}
}
pub const F_DUPFD: usize = 0;
pub const F_GETFD: usize = 1;
pub const F_SETFD: usize = 2;
pub const F_GETFL: usize = 3;
pub const F_SETFL: usize = 4;
/// Duplicate file descriptor with close-on-exec set (Red Bear extension; libredox ABI).
pub const F_DUPFD_CLOEXEC: usize = 5;
pub const FUTEX_WAIT: usize = 0;
pub const FUTEX_WAKE: usize = 1;
pub const FUTEX_REQUEUE: usize = 2;
pub const FUTEX_WAIT64: usize = 3;
// TODO: Split SendFdFlags into caller flags and flags that the scheme receives?
bitflags::bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct SendFdFlags: usize {
/// If set, the kernel will enforce that the file descriptors are exclusively owned.
///
/// That is, there will no longer exist any other reference to those FDs when removed from
/// the file table (sendfd always removes the FDs from the file table, but without this
/// flag, it can be retained by SYS_DUPing them first).
const EXCLUSIVE = 1;
/// If set, the file descriptors will be cloned and *not* removed from the sender's file table.
/// By default, `SYS_SENDFD` moves the file descriptors, removing them from the sender.
const CLONE = 2;
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct FobtainFdFlags: usize {
/// If set, the SYS_CALL payload specifies the destination file descriptor slots, otherwise the lowest
/// available slots will be selected, and placed in the usize pointed to by SYS_CALL
/// payload.
const MANUAL_FD = 1;
/// If set, the file descriptors received are guaranteed to be exclusively owned (by the file
/// table the obtainer is running in).
const EXCLUSIVE = 2;
/// If set, the file descriptors received will be placed into the *upper* file table.
const UPPER_TBL = 4;
/// If set, the received file descriptors are marked as close-on-exec.
const CLOEXEC = 8;
// No, cloexec won't be stored in the kernel in the future, when the stable ABI is moved to
// relibc, so no flag for that!
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct RecvFdFlags: usize {
/// If set, the SYS_CALL payload specifies the destination file descriptor slots, otherwise the lowest
/// available slots will be selected, and placed in the usize pointed to by SYS_CALL
/// payload.
const MANUAL_FD = 1;
/// If set, the file descriptors received will be placed into the *upper* file table.
const UPPER_TBL = 2;
/// If set, the received file descriptors are marked as close-on-exec.
const CLOEXEC = 4;
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct FmoveFdFlags: usize {
/// If set, the kernel will enforce that the file descriptors are exclusively owned.
///
/// That is, there will no longer exist any other reference to those FDs when removed from
/// the file table (SYS_CALL always removes the FDs from the file table, but without this
/// flag, it can be retained by SYS_DUPing them first).
const EXCLUSIVE = 1;
/// If set, the file descriptors will be cloned and *not* removed from the sender's file table.
/// By default, sendfd moves the file descriptors, removing them from the sender.
const CLONE = 2;
}
}
bitflags! {
pub struct MapFlags: usize {
// TODO: Downgrade PROT_NONE to global constant? (bitflags specifically states zero flags
// can cause buggy behavior).
const PROT_NONE = 0x0000_0000;
const PROT_EXEC = 0x0001_0000;
const PROT_WRITE = 0x0002_0000;
const PROT_READ = 0x0004_0000;
const MAP_SHARED = 0x0001;
const MAP_PRIVATE = 0x0002;
const MAP_FIXED = 0x0004;
const MAP_FIXED_NOREPLACE = 0x000C;
/// For *userspace-backed mmaps*, return from the mmap call before all pages have been
/// provided by the scheme. This requires the scheme to be trusted, as the current context
/// can block indefinitely, if the scheme does not respond to the page fault handler's
/// request, as it tries to map the page by requesting it from the scheme.
///
/// In some cases however, such as the program loader, the data needs to be trusted as much
/// with or without MAP_LAZY, and if so, mapping lazily will not cause insecureness by
/// itself.
///
/// For kernel-backed mmaps, this flag has no effect at all. It is unspecified whether
/// kernel mmaps are lazy or not.
const MAP_LAZY = 0x0010;
}
}
bitflags! {
pub struct MunmapFlags: usize {
/// Indicates whether the funmap call must implicitly do an msync, for the changes to
/// become visible later.
///
/// This flag will currently be set if and only if MAP_SHARED | PROT_WRITE are set.
const NEEDS_SYNC = 1;
}
}
pub const MODE_TYPE: u16 = 0xF000;
pub const MODE_DIR: u16 = 0x4000;
pub const MODE_FILE: u16 = 0x8000;
pub const MODE_SYMLINK: u16 = 0xA000;
pub const MODE_FIFO: u16 = 0x1000;
pub const MODE_CHR: u16 = 0x2000;
pub const MODE_SOCK: u16 = 0xC000;
pub const MODE_PERM: u16 = 0x0FFF;
pub const MODE_SETUID: u16 = 0o4000;
pub const MODE_SETGID: u16 = 0o2000;
pub const O_RDONLY: usize = 0x0001_0000;
pub const O_WRONLY: usize = 0x0002_0000;
pub const O_RDWR: usize = 0x0003_0000;
pub const O_NONBLOCK: usize = 0x0004_0000;
pub const O_APPEND: usize = 0x0008_0000;
pub const O_SHLOCK: usize = 0x0010_0000;
pub const O_EXLOCK: usize = 0x0020_0000;
pub const O_ASYNC: usize = 0x0040_0000;
pub const O_FSYNC: usize = 0x0080_0000;
pub const O_CREAT: usize = 0x0200_0000;
pub const O_TRUNC: usize = 0x0400_0000;
pub const O_EXCL: usize = 0x0800_0000;
pub const O_DIRECTORY: usize = 0x1000_0000;
pub const O_STAT: usize = 0x2000_0000;
pub const O_SYMLINK: usize = 0x4000_0000;
pub const O_NOFOLLOW: usize = 0x8000_0000;
pub const O_ACCMODE: usize = O_RDONLY | O_WRONLY | O_RDWR;
pub const O_FCNTL_MASK: usize = O_NONBLOCK | O_APPEND | O_ASYNC | O_FSYNC;
/// Atomically set close-on-exec when opening a file (Red Bear extension; libredox ABI).
pub const O_CLOEXEC: usize = 0x0100_0000;
/// Remove directory instead of unlinking file.
pub const AT_REMOVEDIR: usize = 0x200;
// The top 48 bits of PTRACE_* are reserved, for now
// NOT ABI STABLE!
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(usize)]
pub enum ContextStatus {
Runnable,
Blocked,
NotYetStarted,
Dead,
ForceKilled,
Stopped,
UnhandledExcp,
#[default]
Other, // reserved
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(usize)]
pub enum ContextVerb {
Stop = 1,
Unstop = 2,
Interrupt = 3,
ForceKill = usize::MAX,
}
impl ContextVerb {
pub fn try_from_raw(raw: usize) -> Option<Self> {
Some(match raw {
1 => Self::Stop,
2 => Self::Unstop,
3 => Self::Interrupt,
usize::MAX => Self::ForceKill,
_ => return None,
})
}
}
// NOT ABI STABLE!
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum AddrSpaceVerb {
MmapMin = 255,
}
impl AddrSpaceVerb {
pub fn try_from_raw(verb: u8) -> Option<Self> {
Some(match verb {
255 => Self::MmapMin,
_ => return None,
})
}
}
// NOT ABI STABLE!
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum ProcSchemeVerb {
RegsInt = 250,
RegsFloat = 251,
RegsEnv = 252,
SchedAffinity = 253,
Start = 254,
Iopl = 255,
}
impl ProcSchemeVerb {
pub fn try_from_raw(verb: u8) -> Option<Self> {
Some(match verb {
250 => Self::RegsInt,
251 => Self::RegsFloat,
252 => Self::RegsEnv,
253 => Self::SchedAffinity,
254 => Self::Start,
255 => Self::Iopl,
_ => return None,
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FileTableVerb {
Close = 1,
Dup2 = 2,
Reserved1 = 3,
Resize = 4,
}
impl FileTableVerb {
pub fn try_from_raw(value: u8) -> Option<Self> {
Some(match value {
1 => Self::Close,
2 => Self::Dup2,
3 => Self::Reserved1,
4 => Self::Resize,
_ => return None,
})
}
}
// NOT ABI-STABLE!
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u64)]
pub enum AcpiVerb {
// copies the rsdt/xsdt to the payload buffer (the number of bytes that fit), and returns the
// rsdt/xsdt length regardless
ReadRxsdt = 1,
// no payload, just returns 0 or 1
CheckShutdown = 2,
/// Red Bear OS extension (Phase I): acpid requests the kernel
/// enter s2idle (Modern Standby / S0ix). The kernel sets
/// `S2IDLE_REQUESTED`; the idle path calls `mwait_loop()`. Read
/// payload (1 byte) returns the *previous* value of the flag.
/// Write payload is opaque (ignored by current kernel).
/// Mirrors Linux 7.1 `s2idle_enter()` in
/// `kernel/power/suspend.c:91`. Hardware-agnostic — works on
/// any platform with Modern Standby firmware (Dell, HP, Lenovo,
/// LG Gram, etc.), not just LG Gram.
EnterS2Idle = 3,
/// Red Bear OS extension (Phase I): acpid signals s2idle
/// exit. Kernel clears `S2IDLE_REQUESTED`. Read payload (1
/// byte) always returns 0. Mirrors Linux 7.1 `s2idle_wake()` in
/// `kernel/power/suspend.c:133`. Hardware-agnostic.
ExitS2Idle = 4,
/// Red Bear OS extension (Phase II.X.W): acpid writes the
/// 64-bit kernel S3 resume trampoline address to
/// FACS.xfirmware_waking_vector. The kernel's
/// `arch/x86_shared/s3_resume.rs` trampoline is at this
/// address; the platform firmware jumps to it on S3 wake.
/// Write payload: 8-byte little-endian u64 (the trampoline
/// address). Read payload: 0 (no error condition; this verb
/// is one-way). Mirrors Linux 7.1's
/// `acpi_set_firmware_waking_vector` in ACPICA.
/// Hardware-agnostic: works on any x86_64 system with
/// standard ACPI S3 support (Dell, HP, Lenovo, LG Gram 14).
SetS3WakingVector = 5,
/// Red Bear OS extension (Phase II.X.W): acpid requests
/// the kernel to enter S3. The kernel's kstop handler
/// dispatches on the "s3" string arg, dispatches on the
/// SLP_TYP byte, and does the PM1 register write. The
/// acpid has already done the AML prep (`_TTS(3)`, `_PTS(3)`,
/// `_SST(3)`) and written the trampoline address to FACS
/// via `SetS3WakingVector`. No payload needed.
/// Mirrors Linux 7.1's `enter_sleep_state` /
/// `acpi_hw_legacy_sleep` for S3.
EnterS3 = 6,
}
impl AcpiVerb {
pub const fn try_from_raw(value: u64) -> Option<Self> {
Some(match value {
1 => Self::ReadRxsdt,
2 => Self::CheckShutdown,
3 => Self::EnterS2Idle,
4 => Self::ExitS2Idle,
5 => Self::SetS3WakingVector,
6 => Self::EnterS3,
_ => return None,
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(usize)]
pub enum SchemeSocketCall {
ObtainFd = 0,
MoveFd = 1,
}
impl SchemeSocketCall {
pub fn try_from_raw(raw: usize) -> Option<Self> {
Some(match raw {
0 => Self::ObtainFd,
1 => Self::MoveFd,
_ => return None,
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(usize)]
#[non_exhaustive]
pub enum FsCall {
Connect = 0,
}
impl FsCall {
pub fn try_from_raw(raw: usize) -> Option<Self> {
Some(match raw {
0 => Self::Connect,
_ => return None,
})
}
}
bitflags! {
pub struct PtraceFlags: u64 {
/// Stop before a syscall is handled. Send PTRACE_FLAG_IGNORE to not
/// handle the syscall.
const PTRACE_STOP_PRE_SYSCALL = 0x0000_0000_0000_0001;
/// Stop after a syscall is handled.
const PTRACE_STOP_POST_SYSCALL = 0x0000_0000_0000_0002;
/// Stop after exactly one instruction. TODO: This may not handle
/// fexec/signal boundaries. Should it?
const PTRACE_STOP_SINGLESTEP = 0x0000_0000_0000_0004;
/// Stop before a signal is handled. Send PTRACE_FLAG_IGNORE to not
/// handle signal.
const PTRACE_STOP_SIGNAL = 0x0000_0000_0000_0008;
/// Stop on a software breakpoint, such as the int3 instruction for
/// x86_64.
const PTRACE_STOP_BREAKPOINT = 0x0000_0000_0000_0010;
/// Stop just before exiting for good.
const PTRACE_STOP_EXIT = 0x0000_0000_0000_0020;
const PTRACE_STOP_MASK = 0x0000_0000_0000_00FF;
/// Sent when a child is cloned, giving you the opportunity to trace it.
/// If you don't catch this, the child is started as normal.
const PTRACE_EVENT_CLONE = 0x0000_0000_0000_0100;
/// Sent when current-addrspace is changed, allowing the tracer to reopen the memory file.
const PTRACE_EVENT_ADDRSPACE_SWITCH = 0x0000_0000_0000_0200;
const PTRACE_EVENT_MASK = 0x0000_0000_0000_0F00;
/// Special meaning, depending on the event. Usually, when fired before
/// an action, it will skip performing that action.
const PTRACE_FLAG_IGNORE = 0x0000_0000_0000_1000;
const PTRACE_FLAG_MASK = 0x0000_0000_0000_F000;
}
}
impl Deref for PtraceFlags {
type Target = [u8];
fn deref(&self) -> &Self::Target {
// Same as to_ne_bytes but in-place
unsafe {
slice::from_raw_parts(&self.bits() as *const _ as *const u8, mem::size_of::<u64>())
}
}
}
pub const SEEK_SET: usize = 0;
pub const SEEK_CUR: usize = 1;
pub const SEEK_END: usize = 2;
pub const SIGCHLD: usize = 17;
pub const SIGTSTP: usize = 20;
pub const SIGTTIN: usize = 21;
pub const SIGTTOU: usize = 22;
pub const ADDRSPACE_OP_MMAP: usize = 0;
pub const ADDRSPACE_OP_MUNMAP: usize = 1;
pub const ADDRSPACE_OP_MPROTECT: usize = 2;
pub const ADDRSPACE_OP_TRANSFER: usize = 3;
bitflags! {
pub struct MremapFlags: usize {
const FIXED = 1;
const FIXED_REPLACE = 3;
/// Alias's memory region at `old_address` to `new_address` such that both regions share
/// the same frames.
const KEEP_OLD = 1 << 2;
// TODO: MAYMOVE, DONTUNMAP
}
}
bitflags! {
pub struct RwFlags: u32 {
const NONBLOCK = 1;
const APPEND = 2;
// TODO: sync/dsync
// TODO: O_DIRECT?
}
}
bitflags! {
pub struct SigcontrolFlags: usize {
/// Prevents the kernel from jumping the context to the signal trampoline, but otherwise
/// has absolutely no effect on which signals are blocked etc. Meant to be used for
/// short-lived critical sections inside libc.
const INHIBIT_DELIVERY = 1;
}
}
bitflags! {
pub struct CallFlags: usize {
// reserved
const RSVD0 = 1 << 0;
const RSVD1 = 1 << 1;
const RSVD2 = 1 << 2;
const RSVD3 = 1 << 3;
const RSVD4 = 1 << 4;
const RSVD5 = 1 << 5;
const RSVD6 = 1 << 6;
const RSVD7 = 1 << 7;
/// Remove the fd from the caller's file table before sending the message.
const CONSUME = 1 << 8;
const WRITE = 1 << 9;
const READ = 1 << 10;
/// Indicates the request is a bulk fd passing request.
const FD = 1 << 11;
/// Flags for the fd passing request.
const FD_EXCLUSIVE = 1 << 12;
const FD_CLONE = 1 << 13;
const FD_UPPER = 1 << 14;
const FD_CLOEXEC = 1 << 15;
/// Call is a standard fs call, with metadata defined in `StdFsCallMeta`
const STD_FS = 1 << 16;
/// Call is taking multiple fds as an argument
const MULTIPLE_FDS = 1 << 17;
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum StdFsCallKind {
// TODO: remove old syscalls
Fchmod = 1,
Fchown = 2,
Getdents = 3,
Fstat = 4,
Fstatvfs = 5,
Fsync = 6,
Ftruncate = 7,
Futimens = 8,
// 9 reserved in fscall RFC
// Unlinkat = 10,
Relpathat = 11,
Lock = 12,
Unlock = 13,
GetLock = 14,
}
impl StdFsCallKind {
pub fn try_from_raw(raw: u8) -> Option<Self> {
use StdFsCallKind::*;
// TODO: Use a library where this match can be automated.
Some(match raw {
1 => Fchmod,
2 => Fchown,
3 => Getdents,
4 => Fstat,
5 => Fstatvfs,
6 => Fsync,
7 => Ftruncate,
8 => Futimens,
// 9 reserved in fscall RFC
// 10 => Unlinkat,
11 => Relpathat,
12 => Lock,
13 => Unlock,
14 => GetLock,
_ => return None,
})
}
}
/// The tag for the fd number in the upper file descriptor table.
pub const UPPER_FDTBL_TAG: usize = 1 << (usize::BITS - 2);
/// The identifier for registering event timeout
pub const EVENT_TIMEOUT_ID: usize = usize::MAX - 2;
+73
View File
@@ -0,0 +1,73 @@
use core::{
cmp::PartialEq,
ops::{BitAnd, BitOr, Not},
};
pub trait Io {
type Value: Copy
+ PartialEq
+ BitAnd<Output = Self::Value>
+ BitOr<Output = Self::Value>
+ Not<Output = Self::Value>;
fn read(&self) -> Self::Value;
fn write(&mut self, value: Self::Value);
#[inline(always)]
fn readf(&self, flags: Self::Value) -> bool {
(self.read() & flags) as Self::Value == flags
}
#[inline(always)]
fn writef(&mut self, flags: Self::Value, value: bool) {
let tmp: Self::Value = match value {
true => self.read() | flags,
false => self.read() & !flags,
};
self.write(tmp);
}
}
pub struct ReadOnly<I> {
inner: I,
}
impl<I> ReadOnly<I> {
pub const fn new(inner: I) -> ReadOnly<I> {
ReadOnly { inner: inner }
}
}
impl<I: Io> ReadOnly<I> {
#[inline(always)]
pub fn read(&self) -> I::Value {
self.inner.read()
}
#[inline(always)]
pub fn readf(&self, flags: I::Value) -> bool {
self.inner.readf(flags)
}
}
pub struct WriteOnly<I> {
inner: I,
}
impl<I> WriteOnly<I> {
pub const fn new(inner: I) -> WriteOnly<I> {
WriteOnly { inner: inner }
}
}
impl<I: Io> WriteOnly<I> {
#[inline(always)]
pub fn write(&mut self, value: I::Value) {
self.inner.write(value)
}
#[inline(always)]
pub fn writef(&mut self, flags: I::Value, value: bool) {
self.inner.writef(flags, value)
}
}
+165
View File
@@ -0,0 +1,165 @@
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
use core::ops::{BitAnd, BitOr, Not};
use core::{mem::MaybeUninit, ptr};
use super::io::Io;
#[repr(transparent)]
pub struct Mmio<T> {
value: MaybeUninit<T>,
}
impl<T> Mmio<T> {
pub unsafe fn zeroed() -> Self {
Self {
value: MaybeUninit::zeroed(),
}
}
pub unsafe fn uninit() -> Self {
Self {
value: MaybeUninit::uninit(),
}
}
pub const fn from(value: T) -> Self {
Self {
value: MaybeUninit::new(value),
}
}
}
// Generic implementation (WARNING: requires aligned pointers!)
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
impl<T> Io for Mmio<T>
where
T: Copy + PartialEq + BitAnd<Output = T> + BitOr<Output = T> + Not<Output = T>,
{
type Value = T;
fn read(&self) -> T {
unsafe { ptr::read_volatile(ptr::addr_of!(self.value).cast::<T>()) }
}
fn write(&mut self, value: T) {
unsafe { ptr::write_volatile(ptr::addr_of_mut!(self.value).cast::<T>(), value) };
}
}
// x86 u8 implementation
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
impl Io for Mmio<u8> {
type Value = u8;
fn read(&self) -> Self::Value {
unsafe {
let value: Self::Value;
let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::<Self::Value>();
core::arch::asm!(
"mov {}, [{}]",
out(reg_byte) value,
in(reg) ptr
);
value
}
}
fn write(&mut self, value: Self::Value) {
unsafe {
let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::<Self::Value>();
core::arch::asm!(
"mov [{}], {}",
in(reg) ptr,
in(reg_byte) value,
);
}
}
}
// x86 u16 implementation
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
impl Io for Mmio<u16> {
type Value = u16;
fn read(&self) -> Self::Value {
unsafe {
let value: Self::Value;
let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::<Self::Value>();
core::arch::asm!(
"mov {:x}, [{}]",
out(reg) value,
in(reg) ptr
);
value
}
}
fn write(&mut self, value: Self::Value) {
unsafe {
let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::<Self::Value>();
core::arch::asm!(
"mov [{}], {:x}",
in(reg) ptr,
in(reg) value,
);
}
}
}
// x86 u32 implementation
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
impl Io for Mmio<u32> {
type Value = u32;
fn read(&self) -> Self::Value {
unsafe {
let value: Self::Value;
let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::<Self::Value>();
core::arch::asm!(
"mov {:e}, [{}]",
out(reg) value,
in(reg) ptr
);
value
}
}
fn write(&mut self, value: Self::Value) {
unsafe {
let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::<Self::Value>();
core::arch::asm!(
"mov [{}], {:e}",
in(reg) ptr,
in(reg) value,
);
}
}
}
// x86 u64 implementation (x86_64 only)
#[cfg(target_arch = "x86_64")]
impl Io for Mmio<u64> {
type Value = u64;
fn read(&self) -> Self::Value {
unsafe {
let value: Self::Value;
let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::<Self::Value>();
core::arch::asm!(
"mov {:r}, [{}]",
out(reg) value,
in(reg) ptr
);
value
}
}
fn write(&mut self, value: Self::Value) {
unsafe {
let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::<Self::Value>();
core::arch::asm!(
"mov [{}], {:r}",
in(reg) ptr,
in(reg) value,
);
}
}
}
+12
View File
@@ -0,0 +1,12 @@
//! I/O functions
pub use self::{io::*, mmio::*};
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub use self::pio::*;
mod io;
mod mmio;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod pio;
+89
View File
@@ -0,0 +1,89 @@
use core::{arch::asm, marker::PhantomData};
use super::io::Io;
/// Generic PIO
#[derive(Copy, Clone)]
pub struct Pio<T> {
port: u16,
value: PhantomData<T>,
}
impl<T> Pio<T> {
/// Create a PIO from a given port
pub const fn new(port: u16) -> Self {
Pio::<T> {
port,
value: PhantomData,
}
}
}
/// Read/Write for byte PIO
impl Io for Pio<u8> {
type Value = u8;
/// Read
#[inline(always)]
fn read(&self) -> u8 {
let value: u8;
unsafe {
asm!("in al, dx", in("dx") self.port, out("al") value, options(nostack, nomem, preserves_flags));
}
value
}
/// Write
#[inline(always)]
fn write(&mut self, value: u8) {
unsafe {
asm!("out dx, al", in("dx") self.port, in("al") value, options(nostack, nomem, preserves_flags));
}
}
}
/// Read/Write for word PIO
impl Io for Pio<u16> {
type Value = u16;
/// Read
#[inline(always)]
fn read(&self) -> u16 {
let value: u16;
unsafe {
asm!("in ax, dx", in("dx") self.port, out("ax") value, options(nostack, nomem, preserves_flags));
}
value
}
/// Write
#[inline(always)]
fn write(&mut self, value: u16) {
unsafe {
asm!("out dx, ax", in("dx") self.port, in("ax") value, options(nostack, nomem, preserves_flags));
}
}
}
/// Read/Write for doubleword PIO
impl Io for Pio<u32> {
type Value = u32;
/// Read
#[inline(always)]
fn read(&self) -> u32 {
let value: u32;
unsafe {
asm!("in eax, dx", in("dx") self.port, out("eax") value, options(nostack, nomem, preserves_flags));
}
value
}
/// Write
#[inline(always)]
fn write(&mut self, value: u32) {
unsafe {
asm!("out dx, eax", in("dx") self.port, in("eax") value, options(nostack, nomem, preserves_flags));
}
}
}
+53
View File
@@ -0,0 +1,53 @@
#![cfg_attr(not(any(feature = "std", test)), no_std)]
#![allow(unexpected_cfgs)] // why does this even exist?
#[cfg(test)]
extern crate core;
pub use self::{arch::*, data::*, error::*, flag::*, io::*, number::*};
#[cfg(target_arch = "aarch64")]
#[path = "arch/aarch64.rs"]
mod arch;
#[cfg(target_arch = "riscv64")]
#[path = "arch/riscv64.rs"]
mod arch;
#[cfg(target_arch = "x86")]
#[path = "arch/x86.rs"]
mod arch;
#[cfg(target_arch = "x86_64")]
#[path = "arch/x86_64.rs"]
mod arch;
/// Function definitions
#[cfg(feature = "userspace")]
pub mod call;
#[cfg(feature = "userspace")]
pub use call::*;
/// Complex structures that are used for some system calls
pub mod data;
pub mod dirent;
/// All errors that can be generated by a system call
pub mod error;
/// Flags used as an argument to many system calls
pub mod flag;
/// Functions for low level hardware control
pub mod io;
/// Call numbers used by each system call
pub mod number;
/// ABI for shared memory based signals
pub mod sigabi;
/// V2 scheme format
pub mod schemev2;
-26
View File
@@ -1,26 +0,0 @@
use log::{LevelFilter, Log, Metadata, Record};
pub static LOGGER: Logger = Logger;
pub struct Logger;
impl Logger {
pub fn init(&'static self) {
log::set_logger(self).unwrap();
log::set_max_level(LevelFilter::Info);
}
}
impl Log for Logger {
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
println!("{} - {}", record.level(), record.args());
}
}
fn flush(&self) {}
}
-761
View File
@@ -1,761 +0,0 @@
#![no_std]
#![cfg_attr(any(target_arch = "riscv64", target_os = "uefi"), no_main)]
extern crate alloc;
#[cfg(any(target_arch = "riscv64", target_os = "uefi"))]
#[macro_use]
extern crate uefi_std as std;
use alloc::{format, string::String, vec::Vec};
use core::{
cmp,
convert::TryFrom,
fmt::{self, Write},
mem, ptr, slice, str,
};
use redoxfs::{Disk, Node, TreeData};
use self::arch::{paging_create, paging_framebuffer};
use self::os::{Os, OsHwDesc, OsKey, OsMemoryEntry, OsMemoryKind, OsVideoMode};
#[macro_use]
mod os;
mod arch;
mod editor;
mod logger;
mod serial_16550;
const KIBI: usize = 1024;
const MIBI: usize = KIBI * KIBI;
//TODO: allocate this in a more reasonable manner
static mut AREAS: [OsMemoryEntry; 1024] = [OsMemoryEntry {
base: 0,
size: 0,
kind: OsMemoryKind::Null,
}; 1024];
static mut AREAS_LEN: usize = 0;
pub fn area_add(area: OsMemoryEntry) {
#[allow(static_mut_refs)]
unsafe {
for existing_area in &mut AREAS[0..AREAS_LEN] {
if existing_area.kind == area.kind {
if existing_area.base.unchecked_add(existing_area.size) == area.base {
existing_area.size += area.size;
return;
}
if area.base.unchecked_add(area.size) == existing_area.base {
existing_area.size += area.size;
existing_area.base = area.base;
return;
}
}
}
*AREAS.get_mut(AREAS_LEN).expect("AREAS overflowed!") = area;
AREAS_LEN += 1;
}
}
pub static mut KERNEL_64BIT: bool = false;
pub static mut LIVE_OPT: Option<(u64, &'static [u8])> = None;
struct SliceWriter<'a> {
slice: &'a mut [u8],
i: usize,
}
impl<'a> Write for SliceWriter<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for b in s.bytes() {
if let Some(slice_b) = self.slice.get_mut(self.i) {
*slice_b = b;
self.i += 1;
} else {
return Err(fmt::Error);
}
}
Ok(())
}
}
#[allow(dead_code)]
#[derive(Debug)]
#[repr(C, packed(8))]
pub struct KernelArgs {
kernel_base: u64,
kernel_size: u64,
stack_base: u64,
stack_size: u64,
env_base: u64,
env_size: u64,
/// The base pointer to the saved RSDP.
///
/// This field can be NULL, and if so, the system has not booted with UEFI or in some other way
/// retrieved the RSDPs. The kernel or a userspace driver will thus try searching the BIOS
/// memory instead. On UEFI systems, searching is not guaranteed to actually work though.
acpi_rsdp_base: u64,
/// The size of the RSDP region.
acpi_rsdp_size: u64,
areas_base: u64,
areas_size: u64,
bootstrap_base: u64,
bootstrap_size: u64,
}
fn select_mode(
os: &impl Os,
output_i: usize,
live: &mut bool,
edit_env: &mut bool,
) -> Option<OsVideoMode> {
const DEFAULT_WIDTH: u32 = 1280;
const DEFAULT_HEIGHT: u32 = 720;
const AUTOBOOT_SECONDS: usize = 5;
let mut modes = Vec::new();
for mode in os.video_modes(output_i) {
let mut aspect_w = mode.width;
let mut aspect_h = mode.height;
for i in 2..cmp::min(aspect_w / 2, aspect_h / 2) {
while aspect_w % i == 0 && aspect_h % i == 0 {
aspect_w /= i;
aspect_h /= i;
}
}
modes.push((
mode,
format!(
"{:>4}x{:<4} {:>3}:{:<3}",
mode.width, mode.height, aspect_w, aspect_h
),
));
}
if modes.is_empty() {
return None;
}
// Sort modes by pixel area, reversed
modes.sort_by(|a, b| (b.0.width * b.0.height).cmp(&(a.0.width * a.0.height)));
// Set selected based on Red Bear default resolution first, then best resolution fallback
print!("Output {}", output_i);
let mut selected = modes.first().map_or(0, |x| x.0.id);
let mut selected_from_default = false;
for (mode, _text) in modes.iter() {
if mode.width == DEFAULT_WIDTH && mode.height == DEFAULT_HEIGHT {
selected = mode.id;
selected_from_default = true;
print!(", default resolution: {}x{}", DEFAULT_WIDTH, DEFAULT_HEIGHT);
break;
}
}
if !selected_from_default {
if let Some((best_width, best_height)) = os.best_resolution(output_i) {
print!(", best resolution: {}x{}", best_width, best_height);
for (mode, _text) in modes.iter() {
if mode.width == best_width && mode.height == best_height {
selected = mode.id;
break;
}
}
}
}
println!();
println!("Arrow keys and enter select mode");
let live_mode = os.get_text_position();
if *live {
println!("Press l to disable live mode");
} else {
println!("Press l to enable live mode");
}
println!("Press e to edit boot environment");
println!(
"Autobooting default mode in {} seconds (press any key to cancel countdown)",
AUTOBOOT_SECONDS
);
println!();
print!(" ");
let (off_x, off_y) = os.get_text_position();
let rows = 12;
let mut mode_opt = None;
let countdown_y = off_y.saturating_sub(2);
let mut countdown = AUTOBOOT_SECONDS;
let mut countdown_active = true;
while !modes.is_empty() {
let mut row = 0;
let mut col = 0;
for (mode, text) in modes.iter() {
if row >= rows {
col += 1;
row = 0;
}
os.set_text_position(off_x + col * 20, off_y + row);
os.set_text_highlight(mode.id == selected);
print!("{}", text);
row += 1;
}
os.set_text_position(0, countdown_y);
os.set_text_highlight(false);
if countdown_active {
println!(
"Autobooting default mode in {} seconds (press any key to cancel countdown)",
countdown
);
} else {
println!("Manual mode selection active. Press Enter to boot selected mode. ");
}
// Read keypress
match if countdown_active {
os.get_key_timeout(1000)
} else {
os.get_key()
} {
OsKey::Timeout => {
if countdown_active {
if countdown == 0 {
if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) {
if let Some((mode, _text)) = modes.get(mode_i) {
mode_opt = Some(*mode);
}
}
break;
}
countdown = countdown.saturating_sub(1);
}
}
OsKey::Left => {
countdown_active = false;
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
if mode_i < rows {
while mode_i < modes.len() {
mode_i += rows;
}
}
mode_i -= rows;
if let Some(new) = modes.get(mode_i) {
selected = new.0.id;
}
}
}
OsKey::Right => {
countdown_active = false;
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
mode_i += rows;
if mode_i >= modes.len() {
mode_i %= rows;
}
if let Some(new) = modes.get(mode_i) {
selected = new.0.id;
}
}
}
OsKey::Up => {
countdown_active = false;
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
if mode_i % rows == 0 {
mode_i += rows;
if mode_i > modes.len() {
mode_i = modes.len();
}
}
mode_i -= 1;
if let Some(new) = modes.get(mode_i) {
selected = new.0.id;
}
}
}
OsKey::Down => {
countdown_active = false;
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
mode_i += 1;
if mode_i % rows == 0 {
mode_i -= rows;
}
if mode_i >= modes.len() {
mode_i = mode_i - mode_i % rows;
}
if let Some(new) = modes.get(mode_i) {
selected = new.0.id;
}
}
}
OsKey::Enter => {
countdown_active = false;
if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) {
if let Some((mode, _text)) = modes.get(mode_i) {
mode_opt = Some(*mode);
}
}
break;
}
OsKey::Char('l') => {
countdown_active = false;
*live = !*live;
os.set_text_position(live_mode.0, live_mode.1);
if *live {
println!("Press l to disable live mode");
} else {
println!("Press l to enable live mode");
}
}
OsKey::Char('e') => {
countdown_active = false;
if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) {
if let Some((mode, _text)) = modes.get(mode_i) {
*edit_env = true;
mode_opt = Some(*mode);
}
}
break;
}
OsKey::Other | OsKey::Backspace | OsKey::Delete | OsKey::Char(_) => {
countdown_active = false;
}
}
}
os.set_text_position(0, off_y + rows);
os.set_text_highlight(false);
println!();
mode_opt
}
fn redoxfs<O: Os>(os: &O) -> (redoxfs::FileSystem<O::D>, Option<&'static [u8]>) {
let attempts = 10;
for attempt in 0..=attempts {
let mut password_opt = None;
if attempt > 0 {
print!("\rRedoxFS password ({}/{}): ", attempt, attempts);
let mut password = String::new();
loop {
match os.get_key() {
OsKey::Backspace | OsKey::Delete => {
if !password.is_empty() {
print!("\x08 \x08");
password.pop();
}
}
OsKey::Char(c) => {
print!("*");
password.push(c)
}
OsKey::Enter => break,
_ => (),
}
}
// Erase password information
while os.get_text_position().0 > 0 {
print!("\x08 \x08");
}
if !password.is_empty() {
password_opt = Some(password);
}
}
match os.filesystem(password_opt.as_ref().map(|x| x.as_bytes())) {
Ok(fs) => {
return (
fs,
password_opt.map(|password| {
// Copy password to page aligned memory
let password_size = password.len();
let password_base = os.alloc_zeroed_page_aligned(password_size);
area_add(OsMemoryEntry {
base: password_base as u64,
size: password_size as u64,
kind: OsMemoryKind::Reserved,
});
unsafe {
ptr::copy(password.as_ptr(), password_base, password_size);
slice::from_raw_parts(password_base, password_size)
}
}),
);
}
Err(err) => match err.errno {
// Incorrect password, try again
syscall::ENOKEY => (),
_ => {
panic!("Failed to open RedoxFS: {}", err);
}
},
}
}
panic!("RedoxFS out of unlock attempts");
}
#[derive(PartialEq)]
enum Filetype {
Elf,
Initfs,
}
fn load_to_memory<O: Os>(
os: &O,
fs: &mut redoxfs::FileSystem<O::D>,
path: &str,
filetype: Filetype,
) -> &'static mut [u8] {
fs.tx(|tx| {
let mut node = None;
for component in path.split('/') {
node = Some(
tx.find_node(
node.map_or(redoxfs::TreePtr::root(), |node: TreeData<Node>| node.ptr()),
component,
)
.unwrap_or_else(|err| panic!("Failed to find {component}: {err}")),
);
}
let node = node.unwrap();
let size = node.data().size();
print!("{}: 0/{} MiB", path, size / MIBI as u64);
let ptr = os.alloc_zeroed_page_aligned(size as usize);
if ptr.is_null() {
panic!("Failed to allocate memory for {}", path);
}
let slice = unsafe { slice::from_raw_parts_mut(ptr, size as usize) };
let mut i = 0;
for chunk in slice.chunks_mut(MIBI) {
print!("\r{}: {}/{} MiB", path, i / MIBI as u64, size / MIBI as u64);
i += tx
.read_node_inner(&node, i, chunk)
.unwrap_or_else(|err| panic!("Failed to read `{}` file: {}", path, err))
as u64;
}
println!("\r{}: {}/{} MiB", path, i / MIBI as u64, size / MIBI as u64);
if filetype == Filetype::Elf {
let magic = &slice[..4];
if magic != b"\x7FELF" {
panic!("{} has invalid magic number {:#X?}", path, magic);
}
} else if filetype == Filetype::Initfs {
let magic = &slice[..8];
if magic != b"RedoxFtw" {
panic!("{} has invalid magic number {:#X?}", path, magic);
}
}
Ok(slice)
})
.unwrap_or_else(|err| {
panic!(
"RedoxFS transaction failed while loading `{}`: {}",
path, err
)
})
}
fn elf_entry(data: &[u8]) -> (u64, bool) {
match (data[4], data[5]) {
// 32-bit, little endian
(1, 1) => (
u32::from_le_bytes(
<[u8; 4]>::try_from(&data[0x18..0x18 + 4]).expect("conversion cannot fail"),
) as u64,
false,
),
// 32-bit, big endian
(1, 2) => (
u32::from_be_bytes(
<[u8; 4]>::try_from(&data[0x18..0x18 + 4]).expect("conversion cannot fail"),
) as u64,
false,
),
// 64-bit, little endian
(2, 1) => (
u64::from_le_bytes(
<[u8; 8]>::try_from(&data[0x18..0x18 + 8]).expect("conversion cannot fail"),
),
true,
),
// 64-bit, big endian
(2, 2) => (
u64::from_be_bytes(
<[u8; 8]>::try_from(&data[0x18..0x18 + 8]).expect("conversion cannot fail"),
),
true,
),
(ei_class, ei_data) => {
panic!("Unsupported ELF EI_CLASS {} EI_DATA {}", ei_class, ei_data);
}
}
}
fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
println!(
"Redox OS Bootloader {} on {}",
env!("CARGO_PKG_VERSION"),
os.name()
);
let hwdesc = os.hwdesc();
println!("Hardware descriptor: {:x?}", hwdesc);
let (acpi_rsdp_base, acpi_rsdp_size) = match hwdesc {
OsHwDesc::Acpi(base, size) => (base, size),
OsHwDesc::DeviceTree(base, size) => (base, size),
OsHwDesc::NotFound => (0, 0),
};
let (mut fs, password_opt) = redoxfs(os);
print!("RedoxFS ");
for i in 0..fs.header.uuid().len() {
if i == 4 || i == 6 || i == 8 || i == 10 {
print!("-");
}
print!("{:>02x}", fs.header.uuid()[i]);
}
println!(": {} MiB", fs.header.size() / MIBI as u64);
println!();
let mut mode_opts = Vec::new();
let mut live = cfg!(feature = "live");
let mut edit_env = false;
for output_i in 0..os.video_outputs() {
if output_i > 0 {
os.clear_text();
}
mode_opts.push(select_mode(os, output_i, &mut live, &mut edit_env));
}
let stack_size = 128 * KIBI;
let stack_base = os.alloc_zeroed_page_aligned(stack_size);
if stack_base.is_null() {
panic!("Failed to allocate memory for stack");
}
let live_opt = if live {
let size = fs.header.size();
print!("live: 0/{} MiB", size / MIBI as u64);
let live_size = match usize::try_from(size) {
Ok(live_size) => live_size,
Err(_) => {
println!("\rlive: disabled (image too large for bootloader address space)");
live = false;
0
}
};
let ptr = if live {
os.alloc_zeroed_page_aligned(live_size)
} else {
ptr::null_mut()
};
if live && ptr.is_null() {
println!(
"\rlive: disabled (unable to allocate {} MiB upfront)",
size / MIBI as u64
);
live = false;
}
let live = if live {
Some(unsafe { slice::from_raw_parts_mut(ptr, live_size) })
} else {
println!("Continuing without live preload");
None
};
if let Some(live) = live {
let mut i = 0;
for chunk in live.chunks_mut(MIBI) {
print!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
i += unsafe {
fs.disk
.read_at(fs.block + i / redoxfs::BLOCK_SIZE, chunk)
.expect("Failed to read live disk") as u64
};
}
println!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
println!("Switching to live disk");
unsafe {
LIVE_OPT = Some((fs.block, slice::from_raw_parts_mut(ptr, live_size)));
}
area_add(OsMemoryEntry {
base: live.as_ptr() as u64,
size: live.len() as u64,
kind: OsMemoryKind::Reserved,
});
Some(live)
} else {
None
}
} else {
None
};
let (kernel, kernel_entry) = {
let kernel = load_to_memory(os, &mut fs, "usr/lib/boot/kernel", Filetype::Elf);
let (kernel_entry, kernel_64bit) = elf_entry(kernel);
unsafe {
KERNEL_64BIT = kernel_64bit;
}
(kernel, kernel_entry)
};
let (bootstrap_size, bootstrap_base) = {
let initfs_slice = load_to_memory(os, &mut fs, "usr/lib/boot/initfs", Filetype::Initfs);
let memory = unsafe {
let total_size = initfs_slice.len().next_multiple_of(4096);
let ptr = os.alloc_zeroed_page_aligned(total_size);
assert!(!ptr.is_null(), "failed to allocate bootstrap+initfs memory");
core::slice::from_raw_parts_mut(ptr, total_size)
};
memory[..initfs_slice.len()].copy_from_slice(initfs_slice);
(memory.len() as u64, memory.as_mut_ptr() as u64)
};
let page_phys = unsafe { paging_create(os, kernel.as_ptr() as u64, kernel.len() as u64) }
.expect("Failed to set up paging");
let max_env_size = 64 * KIBI;
let mut env_size = max_env_size;
let env_base = os.alloc_zeroed_page_aligned(env_size);
if env_base.is_null() {
panic!("Failed to allocate memory for stack");
}
{
let mut w = SliceWriter {
slice: unsafe { slice::from_raw_parts_mut(env_base, max_env_size) },
i: 0,
};
match hwdesc {
OsHwDesc::Acpi(addr, size) => {
writeln!(w, "RSDP_ADDR={addr:016x}").unwrap();
writeln!(w, "RSDP_SIZE={size:016x}").unwrap();
}
OsHwDesc::DeviceTree(addr, size) => {
writeln!(w, "DTB_ADDR={addr:016x}").unwrap();
writeln!(w, "DTB_SIZE={size:016x}").unwrap();
}
OsHwDesc::NotFound => {}
}
if let Some(live) = live_opt {
writeln!(w, "DISK_LIVE_ADDR={:016x}", live.as_ptr() as usize).unwrap();
writeln!(w, "DISK_LIVE_SIZE={:016x}", live.len()).unwrap();
writeln!(w, "REDOXFS_BLOCK={:016x}", 0).unwrap();
} else {
writeln!(w, "REDOXFS_BLOCK={:016x}", fs.block).unwrap();
}
write!(w, "REDOXFS_UUID=").unwrap();
for i in 0..fs.header.uuid().len() {
if i == 4 || i == 6 || i == 8 || i == 10 {
write!(w, "-").unwrap();
}
write!(w, "{:>02x}", fs.header.uuid()[i]).unwrap();
}
writeln!(w).unwrap();
if let Some(password) = password_opt {
writeln!(
w,
"REDOXFS_PASSWORD_ADDR={:016x}",
password.as_ptr() as usize
)
.unwrap();
writeln!(w, "REDOXFS_PASSWORD_SIZE={:016x}", password.len()).unwrap();
}
#[cfg(target_arch = "riscv64")]
{
let boot_hartid = os::efi_get_boot_hartid()
.expect("Could not retrieve boot hart id from EFI implementation!");
writeln!(w, "BOOT_HART_ID={:016x}", boot_hartid).unwrap();
}
if edit_env {
editor::edit_env(os, env_base, &mut w.i, max_env_size);
}
for output_i in 0..os.video_outputs() {
if let Some(mut mode) = mode_opts[output_i] {
// Set mode to get updated values
os.set_video_mode(output_i, &mut mode);
if output_i == 0 {
let virt = unsafe {
paging_framebuffer(
os,
page_phys,
mode.base,
(mode.stride * mode.height * 4) as u64,
)
}
.expect("Failed to map framebuffer");
writeln!(w, "FRAMEBUFFER_ADDR={:016x}", mode.base).unwrap();
writeln!(w, "FRAMEBUFFER_VIRT={virt:016x}").unwrap();
writeln!(w, "FRAMEBUFFER_WIDTH={:016x}", mode.width).unwrap();
writeln!(w, "FRAMEBUFFER_HEIGHT={:016x}", mode.height).unwrap();
writeln!(w, "FRAMEBUFFER_STRIDE={:016x}", mode.stride).unwrap();
} else {
writeln!(
w,
"FRAMEBUFFER{}={:#x},{},{},{}",
output_i, mode.base, mode.width, mode.height, mode.stride,
)
.unwrap();
}
}
}
env_size = w.i;
}
#[allow(static_mut_refs)]
(
page_phys,
kernel_entry,
KernelArgs {
kernel_base: kernel.as_ptr() as u64,
kernel_size: kernel.len() as u64,
stack_base: stack_base as u64,
stack_size: stack_size as u64,
env_base: env_base as u64,
env_size: env_size as u64,
acpi_rsdp_base,
acpi_rsdp_size,
areas_base: unsafe { AREAS.as_ptr() as u64 },
areas_size: unsafe { (AREAS.len() * mem::size_of::<OsMemoryEntry>()) as u64 },
bootstrap_base,
bootstrap_size,
},
)
}
+67
View File
@@ -0,0 +1,67 @@
pub const SYS_CLASS: usize = 0xF000_0000;
pub const SYS_CLASS_PATH: usize = 0x1000_0000;
pub const SYS_CLASS_FILE: usize = 0x2000_0000;
pub const SYS_ARG: usize = 0x0F00_0000;
pub const SYS_ARG_SLICE: usize = 0x0100_0000;
pub const SYS_ARG_MSLICE: usize = 0x0200_0000;
pub const SYS_ARG_PATH: usize = 0x0300_0000;
pub const SYS_RET: usize = 0x00F0_0000;
pub const SYS_RET_FILE: usize = 0x0010_0000;
/// Upstream 0.9.0 `openat_into` (caller supplies target fd number).
pub const SYS_OPENAT_INTO: usize = SYS_CLASS_PATH | SYS_RET_FILE | 987;
/// Legacy Red Bear `openat` (kernel allocates a new fd).
pub const SYS_OPENAT: usize = SYS_CLASS_PATH | SYS_RET_FILE | 7;
/// Red Bear: preserve legacy SYS_OPENAT_WITH_FILTER (upstream 0.9.0 removed it; retained for kernel/userspace ABI).
pub const SYS_OPENAT_WITH_FILTER: usize = SYS_CLASS_PATH | SYS_RET_FILE | 985;
pub const SYS_UNLINKAT: usize = SYS_CLASS_PATH | 263;
/// Red Bear: preserve legacy SYS_UNLINKAT_WITH_FILTER (upstream 0.9.0 removed it; retained for kernel/userspace ABI).
pub const SYS_UNLINKAT_WITH_FILTER: usize = SYS_CLASS_PATH | 986;
pub const SYS_CLOSE: usize = SYS_CLASS_FILE | 6;
/// Upstream 0.9.0 `dup_into` (caller supplies target fd number).
pub const SYS_DUP_INTO: usize = SYS_CLASS_FILE | SYS_RET_FILE | 988;
/// Legacy Red Bear `dup` (kernel allocates a new fd).
pub const SYS_DUP: usize = SYS_CLASS_FILE | SYS_RET_FILE | 41;
pub const SYS_DUP2: usize = SYS_CLASS_FILE | SYS_RET_FILE | 63;
/// Red Bear: preserve legacy SYS_SENDFD (upstream 0.9.0 removed it; retained for kernel/userspace ABI).
pub const SYS_SENDFD: usize = SYS_CLASS_FILE | 34;
pub const SYS_READ: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 3;
pub const SYS_READ2: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 35;
pub const SYS_WRITE: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 4;
pub const SYS_WRITE2: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 45;
pub const SYS_LSEEK: usize = SYS_CLASS_FILE | 19;
pub const SYS_FCHMOD: usize = SYS_CLASS_FILE | 94;
pub const SYS_FCHOWN: usize = SYS_CLASS_FILE | 207;
pub const SYS_FCNTL: usize = SYS_CLASS_FILE | 55;
pub const SYS_FEVENT: usize = SYS_CLASS_FILE | 927;
// SYS_CALL, fd, inout buf ptr, inout buf len, flags, metadata buf ptr, metadata buf len
// TODO: new number for SYS_CALL where flags are sent as 6th argument (using syscall6)
pub const SYS_CALL: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | SYS_ARG_MSLICE | 0xCA11;
pub const SYS_GETDENTS: usize = SYS_CLASS_FILE | 43;
// TODO: Rename FMAP/FUNMAP to MMAP/MUNMAP
pub const SYS_FMAP: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 900;
// TODO: SYS_FUNMAP should be SYS_CLASS_FILE
pub const SYS_FUNMAP: usize = SYS_CLASS_FILE | 92;
pub const SYS_MREMAP: usize = 155;
pub const SYS_FLINK: usize = SYS_CLASS_FILE | SYS_ARG_PATH | 9;
pub const SYS_FPATH: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 928;
pub const SYS_FRENAME: usize = SYS_CLASS_FILE | SYS_ARG_PATH | 38;
pub const SYS_FSTAT: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 28;
pub const SYS_FSTATVFS: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 100;
pub const SYS_FSYNC: usize = SYS_CLASS_FILE | 118;
pub const SYS_FTRUNCATE: usize = SYS_CLASS_FILE | 93;
pub const SYS_FUTIMENS: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 320;
pub const SYS_CLOCK_GETTIME: usize = 265;
pub const SYS_FUTEX: usize = 240;
pub const SYS_MPROTECT: usize = 125;
pub const SYS_MKNS: usize = 984;
pub const SYS_NANOSLEEP: usize = 162;
pub const SYS_YIELD: usize = 158;
-177
View File
@@ -1,177 +0,0 @@
use core::{mem, ptr};
use redoxfs::{Disk, BLOCK_SIZE};
use syscall::error::{Error, Result, EIO};
use super::{ThunkData, DISK_ADDRESS_PACKET_ADDR, DISK_BIOS_ADDR};
const SECTOR_SIZE: u64 = 512;
const BLOCKS_PER_SECTOR: u64 = BLOCK_SIZE / SECTOR_SIZE;
// 128 sectors is the amount allocated for DISK_BIOS_ADDR
// 127 sectors is the maximum for many BIOSes
const MAX_SECTORS: u64 = 127;
const MAX_BLOCKS: u64 = MAX_SECTORS * SECTOR_SIZE / BLOCK_SIZE;
#[allow(dead_code)]
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct DiskAddressPacket {
size: u8,
reserved: u8,
sectors: u16,
buffer: u16,
segment: u16,
address: u64,
}
impl DiskAddressPacket {
pub fn from_block(block: u64, count: u64) -> DiskAddressPacket {
let address = block * BLOCKS_PER_SECTOR;
let sectors = count * BLOCKS_PER_SECTOR;
assert!(sectors <= MAX_SECTORS);
DiskAddressPacket {
size: mem::size_of::<DiskAddressPacket>() as u8,
reserved: 0,
sectors: sectors as u16,
buffer: (DISK_BIOS_ADDR & 0xF) as u16,
segment: (DISK_BIOS_ADDR >> 4) as u16,
address,
}
}
}
pub struct DiskBios {
boot_disk: u8,
thunk13: extern "C" fn(),
chs_opt: Option<(u32, u32, u32)>,
}
impl DiskBios {
pub fn new(boot_disk: u8, thunk13: extern "C" fn()) -> Self {
let chs_opt = unsafe {
let mut data = ThunkData::new();
data.eax = 0x4100;
data.ebx = 0x55AA;
data.edx = boot_disk as u32;
data.with(thunk13);
if (data.ebx & 0xFFFF) == 0xAA55 {
// Extensions are installed, do not use CHS
None
} else {
// Extensions are not installed, get CHS geometry
data = ThunkData::new();
data.eax = 0x0800;
data.edx = boot_disk as u32;
data.edi = 0;
data.with(thunk13);
//TODO: return result on error
let ah = ({ data.eax } >> 8) & 0xFF;
assert_eq!(ah, 0);
let c = (data.ecx >> 8) & 0xFF | ((data.ecx >> 6) & 0x3) << 8;
let h = ((data.edx >> 8) & 0xFF) + 1;
let s = data.ecx & 0x3F;
Some((c, h, s))
}
};
Self {
boot_disk,
thunk13,
chs_opt,
}
}
}
impl Disk for DiskBios {
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
unsafe {
// Optimization for live disks
if let Some(live) = crate::LIVE_OPT {
if block >= live.0 {
let start = ((block - live.0) * BLOCK_SIZE) as usize;
let end = start + buffer.len();
if end <= live.1.len() {
buffer.copy_from_slice(&live.1[start..end]);
return Ok(buffer.len());
}
}
}
for (i, chunk) in buffer
.chunks_mut((MAX_BLOCKS * BLOCK_SIZE) as usize)
.enumerate()
{
let dap = DiskAddressPacket::from_block(
block + i as u64 * MAX_BLOCKS,
chunk.len() as u64 / BLOCK_SIZE,
);
if let Some((_, h_max, s_max)) = self.chs_opt {
let s = (dap.address % s_max as u64) + 1;
assert!(s <= 63, "invalid sector {s}");
let tmp = dap.address / s_max as u64;
let h = tmp % h_max as u64;
assert!(h <= 255, "invalid head {h}");
let c = tmp / h_max as u64;
assert!(c <= 1023, "invalid cylinder {c}");
let mut data = ThunkData::new();
data.eax = 0x0200 | (dap.sectors as u32);
data.ebx = dap.buffer as u32;
data.ecx =
(s as u32) | (((c as u32) & 0xFF) << 8) | ((((c as u32) >> 8) & 0x3) << 6);
data.edx = (self.boot_disk as u32) | ((h as u32) << 8);
data.es = dap.segment;
data.with(self.thunk13);
//TODO: return result on error
let ah = ({ data.eax } >> 8) & 0xFF;
assert_eq!(ah, 0);
} else {
ptr::write(DISK_ADDRESS_PACKET_ADDR as *mut DiskAddressPacket, dap);
let mut data = ThunkData::new();
data.eax = 0x4200;
data.edx = self.boot_disk as u32;
data.esi = DISK_ADDRESS_PACKET_ADDR as u32;
data.with(self.thunk13);
//TODO: return result on error
let ah = ({ data.eax } >> 8) & 0xFF;
assert_eq!(ah, 0);
//TODO: check blocks transferred
// dap = ptr::read(DISK_ADDRESS_PACKET_ADDR as *mut DiskAddressPacket);
}
ptr::copy(DISK_BIOS_ADDR as *const u8, chunk.as_mut_ptr(), chunk.len());
}
Ok(buffer.len())
}
}
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
log::error!(
"DiskBios::write_at(0x{:X}, 0x{:X}:0x{:X}) not allowed",
block,
buffer.as_ptr() as usize,
buffer.len()
);
Err(Error::new(EIO))
}
fn size(&mut self) -> Result<u64> {
log::error!("DiskBios::size not implemented");
Err(Error::new(EIO))
}
}
-20
View File
@@ -1,20 +0,0 @@
/// Print to console
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ({
use core::fmt::Write;
#[cfg(feature = "serial_debug")]
{
let _ = write!($crate::os::serial::COM1.lock(), $($arg)*);
}
let _ = write!($crate::os::VGA.lock(), $($arg)*);
});
}
/// Print with new line to console
#[macro_export]
macro_rules! println {
() => (print!("\n"));
($fmt:expr_2021) => (print!(concat!($fmt, "\n")));
($fmt:expr_2021, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}
-84
View File
@@ -1,84 +0,0 @@
use core::{cmp, mem, ptr};
use crate::area_add;
use crate::os::{OsMemoryEntry, OsMemoryKind};
use super::{thunk::ThunkData, MEMORY_MAP_ADDR};
#[repr(C, packed)]
struct MemoryMapEntry {
pub base: u64,
pub size: u64,
pub kind: u32,
}
pub struct MemoryMapIter {
thunk15: extern "C" fn(),
data: ThunkData,
first: bool,
}
impl MemoryMapIter {
pub fn new(thunk15: extern "C" fn()) -> Self {
Self {
thunk15,
data: ThunkData::new(),
first: true,
}
}
}
impl Iterator for MemoryMapIter {
type Item = OsMemoryEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.first {
self.first = false;
} else if self.data.ebx == 0 {
return None;
}
self.data.eax = 0xE820;
self.data.ecx = mem::size_of::<MemoryMapEntry>() as u32;
self.data.edx = 0x534D4150;
self.data.edi = MEMORY_MAP_ADDR as u32;
unsafe {
self.data.with(self.thunk15);
}
//TODO: return error?
assert_eq!({ self.data.eax }, 0x534D4150);
assert_eq!({ self.data.ecx }, mem::size_of::<MemoryMapEntry>() as u32);
let entry = unsafe { ptr::read(MEMORY_MAP_ADDR as *const MemoryMapEntry) };
Some(Self::Item {
base: entry.base,
size: entry.size,
kind: match entry.kind {
0 => OsMemoryKind::Null,
1 => OsMemoryKind::Free,
3 => OsMemoryKind::Reclaim,
_ => OsMemoryKind::Reserved,
},
})
}
}
pub unsafe fn memory_map(thunk15: extern "C" fn()) -> Option<(usize, usize)> {
let mut heap_limits = None;
for entry in MemoryMapIter::new(thunk15) {
let heap_start = 1024 * 1024;
if { entry.kind } == OsMemoryKind::Free
&& entry.base <= heap_start as u64
&& (entry.base + entry.size) >= heap_start as u64
{
let heap_end = cmp::min(entry.base + entry.size, usize::MAX as u64) as usize;
if heap_end >= heap_start {
heap_limits = Some((heap_start, heap_end - heap_start));
}
}
area_add(entry);
}
heap_limits
}
-323
View File
@@ -1,323 +0,0 @@
use alloc::alloc::{alloc_zeroed, Layout};
use core::{convert::TryFrom, mem, ptr, slice};
use linked_list_allocator::LockedHeap;
use spin::Mutex;
use crate::logger::LOGGER;
use crate::os::{Os, OsHwDesc, OsKey, OsVideoMode};
use crate::KernelArgs;
use self::disk::DiskBios;
use self::memory_map::memory_map;
use self::thunk::ThunkData;
use self::vbe::VideoModeIter;
use self::vga::{Vga, VgaTextColor};
#[macro_use]
mod macros;
mod disk;
mod memory_map;
mod panic;
pub(crate) mod serial;
mod thunk;
mod vbe;
mod vga;
// Real mode memory allocation, for use with thunk
// 0x500 to 0x7BFF is free
const DISK_BIOS_ADDR: usize = 0x70000; // 64 KiB at 448 KiB, ends at 512 KiB
const VBE_CARD_INFO_ADDR: usize = 0x1000; // 512 bytes, ends at 0x11FF
const VBE_MODE_INFO_ADDR: usize = 0x1200; // 256 bytes, ends at 0x12FF
const VBE_EDID_ADDR: usize = 0x1300; // 128 bytes, ends at 0x137F
const MEMORY_MAP_ADDR: usize = 0x1380; // 24 bytes, ends at 0x1397
const DISK_ADDRESS_PACKET_ADDR: usize = 0x1398; // 16 bytes, ends at 0x13A7
const THUNK_STACK_ADDR: usize = 0x7C00; // Grows downwards
const VGA_ADDR: usize = 0xB8000;
#[global_allocator]
static ALLOCATOR: LockedHeap = LockedHeap::empty();
pub(crate) static VGA: Mutex<Vga> = Mutex::new(unsafe { Vga::new(VGA_ADDR, 80, 25) });
pub struct OsBios {
boot_disk: usize,
thunk10: extern "C" fn(),
thunk13: extern "C" fn(),
thunk15: extern "C" fn(),
thunk16: extern "C" fn(),
}
#[allow(dead_code)]
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct Rsdp {
signature: [u8; 8],
checksum: u8,
oemid: [u8; 6],
revision: u8,
rsdt_address: u32,
}
#[allow(dead_code)]
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct Xsdp {
rsdp: Rsdp,
length: u32,
xsdt_address: u64,
extended_checksum: u8,
reserved: [u8; 3],
}
unsafe fn search_rsdp(start: usize, end: usize) -> Option<(u64, u64)> {
unsafe {
// Align start up to 16 bytes
let mut addr = start.div_ceil(16) * 16;
// Search until reading the end of the Rsdp would be past the end of the memory area
while addr + mem::size_of::<Rsdp>() <= end {
let rsdp = ptr::read(addr as *const Rsdp);
if &rsdp.signature == b"RSD PTR " {
//TODO: check checksum?
if rsdp.revision == 0 {
return Some((addr as u64, mem::size_of::<Rsdp>() as u64));
} else if rsdp.revision == 2 {
let xsdp = ptr::read(addr as *const Xsdp);
//TODO: check extended checksum?
return Some((addr as u64, xsdp.length as u64));
}
}
// Rsdp is always aligned to 16 bytes
addr += 16;
}
None
}
}
impl Os for OsBios {
type D = DiskBios;
type V = VideoModeIter;
fn name(&self) -> &str {
"x86/BIOS"
}
fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8 {
assert!(size != 0);
let page_size = self.page_size();
let pages = size.div_ceil(page_size);
let Some(total_size) = pages.checked_mul(page_size) else {
return ptr::null_mut();
};
let Ok(layout) = Layout::from_size_align(total_size, page_size) else {
return ptr::null_mut();
};
let ptr = unsafe { alloc_zeroed(layout) };
assert!(!ptr.is_null());
ptr
}
fn page_size(&self) -> usize {
4096
}
fn filesystem(
&self,
password_opt: Option<&[u8]>,
) -> syscall::Result<redoxfs::FileSystem<DiskBios>> {
let disk = DiskBios::new(u8::try_from(self.boot_disk).unwrap(), self.thunk13);
//TODO: get block from partition table
let block = 2 * crate::MIBI as u64 / redoxfs::BLOCK_SIZE;
redoxfs::FileSystem::open(disk, password_opt, Some(block), false)
}
fn hwdesc(&self) -> OsHwDesc {
// See ACPI specification - Finding the RSDP on IA-PC Systems
unsafe {
let ebda_segment = ptr::read(0x40E as *const u16);
let ebda_addr = (ebda_segment as usize) << 4;
if let Some((addr, size)) =
search_rsdp(ebda_addr, ebda_addr + 1024).or(search_rsdp(0xE0000, 0xFFFFF))
{
// Copy to a page
let page_aligned = self.alloc_zeroed_page_aligned(size as usize);
ptr::copy(addr as *const u8, page_aligned, size as usize);
return OsHwDesc::Acpi(page_aligned as u64, size);
}
}
OsHwDesc::NotFound
}
fn video_outputs(&self) -> usize {
//TODO: return 1 only if vbe supported?
1
}
fn video_modes(&self, _output_i: usize) -> VideoModeIter {
VideoModeIter::new(self.thunk10)
}
fn set_video_mode(&self, _output_i: usize, mode: &mut OsVideoMode) {
// Set video mode
let mut data = ThunkData::new();
data.eax = 0x4F02;
data.ebx = mode.id;
unsafe {
data.with(self.thunk10);
}
//TODO: check result
}
fn best_resolution(&self, _output_i: usize) -> Option<(u32, u32)> {
let mut data = ThunkData::new();
data.eax = 0x4F15;
data.ebx = 0x01;
data.ecx = 0;
data.edx = 0;
data.edi = VBE_EDID_ADDR as u32;
unsafe {
data.with(self.thunk10);
}
if data.eax == 0x4F {
let edid = unsafe { slice::from_raw_parts(VBE_EDID_ADDR as *const u8, 128) };
Some((
(edid[0x38] as u32) | (((edid[0x3A] as u32) & 0xF0) << 4),
(edid[0x3B] as u32) | (((edid[0x3D] as u32) & 0xF0) << 4),
))
} else {
log::warn!("Failed to get VBE EDID: 0x{:X}", { data.eax });
None
}
}
fn get_key(&self) -> OsKey {
// Read keypress
let mut data = ThunkData::new();
unsafe {
data.with(self.thunk16);
}
match (data.eax >> 8) as u8 {
0x4B => OsKey::Left,
0x4D => OsKey::Right,
0x48 => OsKey::Up,
0x50 => OsKey::Down,
0x0E => OsKey::Backspace,
0x53 => OsKey::Delete,
0x1C => OsKey::Enter,
_ => match data.eax as u8 {
0 => OsKey::Other,
b => OsKey::Char(b as char),
},
}
}
fn clear_text(&self) {
let mut vga = VGA.lock();
vga.clear();
}
fn get_text_position(&self) -> (usize, usize) {
let vga = VGA.lock();
(vga.x, vga.y)
}
fn set_text_position(&self, x: usize, y: usize) {
//TODO: ensure this is inside bounds!
let mut vga = VGA.lock();
vga.x = x;
vga.y = y;
}
fn set_text_highlight(&self, highlight: bool) {
let mut vga = VGA.lock();
if highlight {
vga.bg = VgaTextColor::Gray;
vga.fg = VgaTextColor::Black;
} else {
vga.bg = VgaTextColor::Black;
vga.fg = VgaTextColor::Gray;
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn start(
kernel_entry: extern "C" fn(
page_table: usize,
stack: u64,
func: u64,
args: *const KernelArgs,
long_mode: usize,
) -> !,
boot_disk: usize,
thunk10: extern "C" fn(),
thunk13: extern "C" fn(),
thunk15: extern "C" fn(),
thunk16: extern "C" fn(),
) -> ! {
unsafe {
#[cfg(feature = "serial_debug")]
{
let mut com1 = serial::COM1.lock();
com1.init();
com1.write(b"SERIAL\n");
}
{
// Make sure we are in mode 3 (80x25 text mode)
let mut data = ThunkData::new();
data.eax = 0x03;
data.with(thunk10);
}
{
// Disable cursor
let mut data = ThunkData::new();
data.eax = 0x0100;
data.ecx = 0x3F00;
data.with(thunk10);
}
// Clear screen
VGA.lock().clear();
// Set logger
LOGGER.init();
let mut os = OsBios {
boot_disk,
thunk10,
thunk13,
thunk15,
thunk16,
};
let (heap_start, heap_size) = memory_map(os.thunk15).expect("No memory for heap");
ALLOCATOR.lock().init(heap_start as *mut u8, heap_size);
let (page_phys, func, args) = crate::main(&mut os);
kernel_entry(
page_phys,
args.stack_base
+ args.stack_size
+ if crate::KERNEL_64BIT {
crate::arch::x64::PHYS_OFFSET
} else {
crate::arch::x32::PHYS_OFFSET as u64
},
func,
&args,
if crate::KERNEL_64BIT { 1 } else { 0 },
);
}
}
-16
View File
@@ -1,16 +0,0 @@
//! Intrinsics for panic handling
use core::alloc::Layout;
use core::arch::asm;
use core::panic::PanicInfo;
/// Required to handle panics
#[panic_handler]
pub fn rust_begin_unwind(info: &PanicInfo) -> ! {
unsafe {
println!("BOOTLOADER PANIC:\n{}", info);
loop {
asm!("hlt");
}
}
}
-9
View File
@@ -1,9 +0,0 @@
use spin::Mutex;
use syscall::Pio;
use crate::serial_16550::SerialPort;
pub static COM1: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x3F8));
pub static COM2: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x2F8));
pub static COM3: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x3E8));
pub static COM4: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x2E8));
-52
View File
@@ -1,52 +0,0 @@
use core::ptr;
use super::THUNK_STACK_ADDR;
#[allow(dead_code)]
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct ThunkData {
pub es: u16,
pub edi: u32,
pub esi: u32,
pub ebp: u32,
pub ebx: u32,
pub edx: u32,
pub ecx: u32,
pub eax: u32,
}
impl ThunkData {
pub fn new() -> Self {
Self {
es: 0,
edi: 0,
esi: 0,
ebp: 0,
ebx: 0,
edx: 0,
ecx: 0,
eax: 0,
}
}
pub unsafe fn save(&self) {
unsafe {
ptr::write((THUNK_STACK_ADDR - 64) as *mut ThunkData, *self);
}
}
pub unsafe fn load(&mut self) {
unsafe {
*self = ptr::read((THUNK_STACK_ADDR - 64) as *const ThunkData);
}
}
pub unsafe fn with(&mut self, f: extern "C" fn()) {
unsafe {
self.save();
f();
self.load();
}
}
}
-151
View File
@@ -1,151 +0,0 @@
use core::ptr;
use log::error;
use crate::os::OsVideoMode;
use super::{ThunkData, VBE_CARD_INFO_ADDR, VBE_MODE_INFO_ADDR};
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct VbeFarPtr {
pub offset: u16,
pub segment: u16,
}
impl VbeFarPtr {
pub unsafe fn as_ptr<T>(&self) -> *const T {
(((self.segment as usize) << 4) + (self.offset as usize)) as *const T
}
}
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct VbeCardInfo {
pub signature: [u8; 4],
pub version: u16,
pub oemstring: VbeFarPtr,
pub capabilities: [u8; 4],
pub videomodeptr: VbeFarPtr,
pub totalmemory: u16,
pub oemsoftwarerev: u16,
pub oemvendornameptr: VbeFarPtr,
pub oemproductnameptr: VbeFarPtr,
pub oemproductrevptr: VbeFarPtr,
pub reserved: [u8; 222],
pub oemdata: [u8; 256],
}
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct VbeModeInfo {
pub attributes: u16,
pub win_a: u8,
pub win_b: u8,
pub granularity: u16,
pub winsize: u16,
pub segment_a: u16,
pub segment_b: u16,
pub winfuncptr: u32,
pub bytesperscanline: u16,
pub xresolution: u16,
pub yresolution: u16,
pub xcharsize: u8,
pub ycharsize: u8,
pub numberofplanes: u8,
pub bitsperpixel: u8,
pub numberofbanks: u8,
pub memorymodel: u8,
pub banksize: u8,
pub numberofimagepages: u8,
pub unused: u8,
pub redmasksize: u8,
pub redfieldposition: u8,
pub greenmasksize: u8,
pub greenfieldposition: u8,
pub bluemasksize: u8,
pub bluefieldposition: u8,
pub rsvdmasksize: u8,
pub rsvdfieldposition: u8,
pub directcolormodeinfo: u8,
pub physbaseptr: u32,
pub offscreenmemoryoffset: u32,
pub offscreenmemsize: u16,
pub reserved: [u8; 206],
}
pub struct VideoModeIter {
thunk10: extern "C" fn(),
mode_ptr: *const u16,
}
impl VideoModeIter {
pub fn new(thunk10: extern "C" fn()) -> Self {
// Get card info
let mut data = ThunkData::new();
data.eax = 0x4F00;
data.edi = VBE_CARD_INFO_ADDR as u32;
unsafe {
data.with(thunk10);
}
let mode_ptr = if data.eax == 0x004F {
let card_info = unsafe { ptr::read(VBE_CARD_INFO_ADDR as *const VbeCardInfo) };
unsafe { card_info.videomodeptr.as_ptr::<u16>() }
} else {
error!("Failed to read VBE card info: 0x{:04X}", { data.eax });
ptr::null()
};
Self { thunk10, mode_ptr }
}
}
impl Iterator for VideoModeIter {
type Item = OsVideoMode;
fn next(&mut self) -> Option<Self::Item> {
if self.mode_ptr.is_null() {
return None;
}
loop {
// Set bit 14 to get linear frame buffer
let mode = unsafe { *self.mode_ptr } | (1 << 14);
if mode == 0xFFFF {
return None;
}
self.mode_ptr = unsafe { self.mode_ptr.add(1) };
// Get mode info
let mut data = ThunkData::new();
data.eax = 0x4F01;
data.ecx = mode as u32;
data.edi = VBE_MODE_INFO_ADDR as u32;
unsafe {
data.with(self.thunk10);
}
if data.eax == 0x004F {
let mode_info = unsafe { ptr::read(VBE_MODE_INFO_ADDR as *const VbeModeInfo) };
// We only support 32-bits per pixel modes
if mode_info.bitsperpixel != 32 {
continue;
}
let width = mode_info.xresolution as u32;
let height = mode_info.yresolution as u32;
//TODO: support stride that is not a multiple of 4
let stride = mode_info.bytesperscanline as u32 / 4;
return Some(OsVideoMode {
id: mode as u32,
width,
height,
stride,
base: mode_info.physbaseptr as u64,
});
} else {
error!("Failed to read VBE mode 0x{:04X} info: 0x{:04X}", mode, {
data.eax
});
}
}
}
}
-121
View File
@@ -1,121 +0,0 @@
use core::{fmt, slice};
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct VgaTextBlock {
pub char: u8,
pub color: u8,
}
#[allow(dead_code)]
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum VgaTextColor {
Black = 0,
Blue = 1,
Green = 2,
Cyan = 3,
Red = 4,
Purple = 5,
Brown = 6,
Gray = 7,
DarkGray = 8,
LightBlue = 9,
LightGreen = 10,
LightCyan = 11,
LightRed = 12,
LightPurple = 13,
Yellow = 14,
White = 15,
}
pub struct Vga {
pub base: usize,
pub width: usize,
pub height: usize,
pub x: usize,
pub y: usize,
pub bg: VgaTextColor,
pub fg: VgaTextColor,
}
impl Vga {
pub const unsafe fn new(base: usize, width: usize, height: usize) -> Self {
Self {
base,
width,
height,
x: 0,
y: 0,
bg: VgaTextColor::Black,
fg: VgaTextColor::Gray,
}
}
pub unsafe fn blocks(&mut self) -> &'static mut [VgaTextBlock] {
unsafe {
slice::from_raw_parts_mut(self.base as *mut VgaTextBlock, self.width * self.height)
}
}
pub fn clear(&mut self) {
self.x = 0;
self.y = 0;
let blocks = unsafe { self.blocks() };
for i in 0..blocks.len() {
blocks[i] = VgaTextBlock {
char: 0,
color: ((self.bg as u8) << 4) | (self.fg as u8),
};
}
}
}
impl fmt::Write for Vga {
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
let blocks = unsafe { self.blocks() };
for c in s.chars() {
if self.x >= self.width {
self.x = 0;
self.y += 1;
}
while self.y >= self.height {
for y in 1..self.height {
for x in 0..self.width {
let i = y * self.width + x;
let j = i - self.width;
blocks[j] = blocks[i];
if y + 1 == self.height {
blocks[i].char = 0;
}
}
}
self.y -= 1;
}
match c {
'\x08' => {
if self.x > 0 {
self.x -= 1;
}
}
'\r' => {
self.x = 0;
}
'\n' => {
self.x = 0;
self.y += 1;
}
_ => {
let i = self.y * self.width + self.x;
if let Some(block) = blocks.get_mut(i) {
block.char = c as u8;
block.color = ((self.bg as u8) << 4) | (self.fg as u8);
}
self.x += 1;
}
}
}
Ok(())
}
}
-99
View File
@@ -1,99 +0,0 @@
use redoxfs::Disk;
#[cfg(all(target_arch = "x86", target_os = "none"))]
pub use self::bios::*;
#[cfg(all(target_arch = "x86", target_os = "none"))]
#[macro_use]
mod bios;
#[cfg(any(target_arch = "riscv64", target_os = "uefi"))]
#[allow(unused_imports)]
pub use self::uefi::*;
#[cfg(any(target_arch = "riscv64", target_os = "uefi"))]
#[macro_use]
mod uefi;
#[derive(Clone, Copy, Debug)]
pub enum OsHwDesc {
Acpi(u64, u64),
DeviceTree(u64, u64),
NotFound,
}
#[derive(Clone, Copy, Debug)]
pub enum OsKey {
Left,
Right,
Up,
Down,
Backspace,
Delete,
Enter,
Char(char),
Timeout,
Other,
}
// Keep synced with BootloaderMemoryKind in kernel
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u64)]
pub enum OsMemoryKind {
Null = 0,
Free = 1,
Reclaim = 2,
Reserved = 3,
}
// Keep synced with BootloaderMemoryEntry in kernel
#[derive(Clone, Copy, Debug)]
#[repr(C, packed(8))]
pub struct OsMemoryEntry {
pub base: u64,
pub size: u64,
pub kind: OsMemoryKind,
}
#[derive(Clone, Copy, Debug)]
pub struct OsVideoMode {
pub id: u32,
pub width: u32,
pub height: u32,
pub stride: u32,
pub base: u64,
}
pub trait Os {
type D: Disk;
type V: Iterator<Item = OsVideoMode>;
fn name(&self) -> &str;
fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8;
#[allow(dead_code)]
fn page_size(&self) -> usize;
fn filesystem(
&self,
password_opt: Option<&[u8]>,
) -> syscall::Result<redoxfs::FileSystem<Self::D>>;
fn hwdesc(&self) -> OsHwDesc;
fn video_outputs(&self) -> usize;
fn video_modes(&self, output_i: usize) -> Self::V;
fn set_video_mode(&self, output_i: usize, mode: &mut OsVideoMode);
fn best_resolution(&self, output_i: usize) -> Option<(u32, u32)>;
fn get_key(&self) -> OsKey;
fn get_key_timeout(&self, _milliseconds: usize) -> OsKey {
self.get_key()
}
fn clear_text(&self);
fn get_text_position(&self) -> (usize, usize);
fn set_text_position(&self, x: usize, y: usize);
fn set_text_highlight(&self, highlight: bool);
}
-110
View File
@@ -1,110 +0,0 @@
use core::slice;
use uefi::guid::{ACPI_20_TABLE_GUID, ACPI_TABLE_GUID};
use crate::Os;
struct Invalid;
fn validate_rsdp(address: usize, _v2: bool) -> core::result::Result<usize, Invalid> {
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
struct Rsdp {
signature: [u8; 8], // b"RSD PTR "
chksum: u8,
oem_id: [u8; 6],
revision: u8,
rsdt_addr: u32,
// the following fields are only available for ACPI 2.0, and are reserved otherwise
length: u32,
xsdt_addr: u64,
extended_chksum: u8,
_rsvd: [u8; 3],
}
// paging is not enabled at this stage; we can just read the physical address here.
let rsdp_bytes =
unsafe { core::slice::from_raw_parts(address as *const u8, core::mem::size_of::<Rsdp>()) };
let rsdp = unsafe {
(rsdp_bytes.as_ptr() as *const Rsdp)
.as_ref::<'static>()
.unwrap()
};
log::debug!("RSDP: {:?}", rsdp);
if rsdp.signature != *b"RSD PTR " {
return Err(Invalid);
}
let mut base_sum = 0u8;
for base_byte in &rsdp_bytes[..20] {
base_sum = base_sum.wrapping_add(*base_byte);
}
if base_sum != 0 {
return Err(Invalid);
}
if rsdp.revision == 2 {
let mut extended_sum = 0u8;
for byte in rsdp_bytes {
extended_sum = extended_sum.wrapping_add(*byte);
}
if extended_sum != 0 {
return Err(Invalid);
}
}
let length = if rsdp.revision == 2 {
rsdp.length as usize
} else {
core::mem::size_of::<Rsdp>()
};
Ok(length)
}
pub(crate) fn find_acpi_table_pointers(os: &impl Os) -> Option<(u64, u64)> {
let cfg_tables = std::system_table().config_tables();
let mut acpi = None;
let mut acpi2 = None;
for cfg_table in cfg_tables.iter() {
if cfg_table.VendorGuid == ACPI_TABLE_GUID {
match validate_rsdp(cfg_table.VendorTable, false) {
Ok(length) => {
acpi = Some(unsafe {
core::slice::from_raw_parts(cfg_table.VendorTable as *const u8, length)
});
}
Err(_) => log::warn!(
"Found RSDP that was not valid at {:p}",
cfg_table.VendorTable as *const u8
),
}
} else if cfg_table.VendorGuid == ACPI_20_TABLE_GUID {
match validate_rsdp(cfg_table.VendorTable, true) {
Ok(length) => {
acpi2 = Some(unsafe {
core::slice::from_raw_parts(cfg_table.VendorTable as *const u8, length)
});
}
Err(_) => log::warn!(
"Found RSDP that was not valid at {:p}",
cfg_table.VendorTable as *const u8
),
}
}
}
let rsdp_area = acpi2.or(acpi).unwrap_or(&[]);
if !rsdp_area.is_empty() {
unsafe {
// Copy to page aligned area
let size = rsdp_area.len();
let base = os.alloc_zeroed_page_aligned(size);
slice::from_raw_parts_mut(base, size).copy_from_slice(rsdp_area);
Some((base as u64, size as u64))
}
} else {
None
}
}
-236
View File
@@ -1,236 +0,0 @@
use core::{arch::asm, fmt::Write, mem, slice};
use uefi::status::Result;
use crate::{
arch::{ENTRY_ADDRESS_MASK, PAGE_ENTRIES, PF_PRESENT, PF_TABLE, PHYS_OFFSET},
logger::LOGGER,
KernelArgs,
};
use super::super::{memory_map::memory_map, OsEfi};
unsafe fn dump_page_tables(table_phys: u64, table_virt: u64, table_level: u64) {
unsafe {
let entries = slice::from_raw_parts(table_phys as *const u64, PAGE_ENTRIES);
for (i, entry) in entries.iter().enumerate() {
let phys = entry & ENTRY_ADDRESS_MASK;
let flags = entry & !ENTRY_ADDRESS_MASK;
if flags & PF_PRESENT == 0 {
continue;
}
let mut shift = 39u64;
for _ in 0..table_level {
shift -= 9;
print!("\t");
}
let virt = table_virt + (i as u64) << shift;
println!(
"index {} virt {:#x}: phys {:#x} flags {:#x}",
i, virt, phys, flags
);
if table_level < 3 && flags & PF_TABLE == PF_TABLE {
dump_page_tables(phys, virt, table_level + 1);
}
}
}
}
unsafe extern "C" fn kernel_entry(
page_phys: usize,
stack: u64,
func: u64,
args: *const KernelArgs,
) -> ! {
unsafe {
// Read memory map and exit boot services
memory_map().exit_boot_services();
let currentel: u64;
asm!(
"mrs {0}, currentel", // Read current exception level
out(reg) currentel,
);
if currentel == (2 << 2) {
// Need to drop from EL2 to EL1
// Allow access to timers
asm!(
"mrs {0}, cnthctl_el2",
"orr {0}, {0}, #0x3",
"msr cnthctl_el2, {0}",
"msr cntvoff_el2, xzr",
out(reg) _
);
// Initialize ID registers
asm!(
"mrs {0}, midr_el1",
"msr vpidr_el2, {0}",
"mrs {0}, mpidr_el1",
"msr vmpidr_el2, {0}",
out(reg) _
);
// Disable traps
asm!(
"msr cptr_el2, {0}",
"msr hstr_el2, xzr",
in(reg) 0x33FF as u64
);
// Enable floating point
asm!(
"msr cpacr_el1, {0}",
in(reg) (3 << 20) as u64
);
// Set EL1 system control register
asm!(
"msr sctlr_el1, {0}",
in(reg) 0x30d00800 as u64
);
// Set EL1 stack and VBAR
asm!(
"mov {0}, sp",
"msr sp_el1, {0}",
"mrs {0}, vbar_el2",
"msr vbar_el1, {0}",
out(reg) _
);
// Configure execution state of EL1 as aarch64 and disable hypervisor call.
asm!(
"msr hcr_el2, {0}",
in(reg) ((1u64 << 31) | (1u64 << 29)),
);
// Set saved program status register
asm!(
"msr spsr_el2, {0}",
in(reg) 0x3C5 as u64
);
// Switch to EL1
asm!(
"adr {0}, 1f",
"msr elr_el2, {0}",
"eret",
"1:",
out(reg) _
);
} else if currentel == (1 << 2) {
// Already in EL1
} else {
//TODO: what to do if not EL2 or already EL1?
loop {
asm!("wfi");
}
}
// Disable MMU
asm!(
"mrs {0}, sctlr_el1", // Read system control register
"bic {0}, {0}, 1", // Clear MMU enable bit
"msr sctlr_el1, {0}", // Write system control register
"isb", // Instruction sync barrier
out(reg) _,
);
// Set MAIR
// You can think about MAIRs as of an array with 8 elements each of 8 bits long.
// You can store inside MAIRs up to 8 attributes sets and reffer them by the index 0..7 stored in INDX (AttrIndx) field of the table descriptor.
// https://lowenware.com/blog/aarch64-mmu-programming/
// https://developer.arm.com/documentation/102376/0200/Describing-memory-in-AArch64
// https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/MAIR-EL1--Memory-Attribute-Indirection-Register--EL1-
// Attribute 0 (0xFF) - normal memory, caches are enabled
// Attribute 1 (0x44) - normal memory, caches are disabled. Atomics wouldn't work here if memory doesn't support exclusive access (most real hardware don't)
// Attribute 2 (0x00) - nGnRnE device memory, caches are disabled, gathering, re-ordering, and early write acknowledgement aren't allowed.
asm!(
"msr mair_el1, {0}",
in(reg) 0x00000000000044FF as u64, // MAIR: Arrange for Device, Normal Non-Cache, Normal Write-Back access types
);
// Set TCR
asm!(
"mrs {1}, id_aa64mmfr0_el1", // Read memory model feature register
"bfi {0}, {1}, #32, #3",
"msr tcr_el1, {0}", // Write translation control register
"isb", // Instruction sync barrier
in(reg) 0x1085100510u64, // TCR: (TxSZ, ASID_16, TG1_4K, Cache Attrs, SMP Attrs)
out(reg) _,
);
// Set page tables
asm!(
"dsb sy", // Data sync barrier
"msr ttbr1_el1, {0}", // Set higher half page table
"msr ttbr0_el1, {0}", // Set lower half page table
"isb", // Instruction sync barrier
"dsb ishst", // Data sync barrier, only for stores, and only for inner shareable domain
"tlbi vmalle1is", // Invalidate TLB
"dsb ish", // Dta sync bariar, only for inner shareable domain
"isb", // Instruction sync barrier
in(reg) page_phys,
);
// Enable MMU
asm!(
"mrs {2}, sctlr_el1", // Read system control register
"bic {2}, {2}, {0}", // Clear bits
"orr {2}, {2}, {1}", // Set bits
"msr sctlr_el1, {2}", // Write system control register
"isb", // Instruction sync barrier
in(reg) 0x32802c2u64, // Clear SCTLR bits: (EE, EOE, IESB, WXN, UMA, ITD, THEE, A)
in(reg) 0x3485d13du64, // Set SCTLR bits: (LSMAOE, nTLSMD, UCI, SPAN, nTWW, nTWI, UCT, DZE, I, SED, SA0, SA, C, M, CP15BEN)
out(reg) _,
);
// Set stack
asm!("mov sp, {}", in(reg) stack);
// Call kernel entry
let entry_fn: extern "C" fn(*const KernelArgs) -> ! = mem::transmute(func);
entry_fn(args);
}
}
pub fn main() -> Result<()> {
LOGGER.init();
let mut os = OsEfi::new();
// Disable cursor
let _ = (os.st.ConsoleOut.EnableCursor)(os.st.ConsoleOut, false);
let currentel: u64;
unsafe {
asm!(
"mrs {0}, currentel", // Read current exception level
out(reg) currentel,
);
}
log::info!("Currently in EL{}", (currentel >> 2) & 3);
let (page_phys, func, args) = crate::main(&mut os);
unsafe {
let stack = args.stack_base + args.stack_size + PHYS_OFFSET;
// dump_page_tables(page_phys as _, 0, 0);
println!(
"kernel_entry({:#x}, {:#x}, {:#x}, {:p})",
page_phys, stack, func, &args
);
println!("{:#x?}", args);
kernel_entry(page_phys, stack, func, &args);
}
}
pub fn disable_interrupts() {
unsafe {
asm!("msr daifset, #2");
}
}
-14
View File
@@ -1,14 +0,0 @@
#[cfg(target_arch = "aarch64")]
mod aarch64;
#[cfg(target_arch = "aarch64")]
pub use self::aarch64::*;
#[cfg(target_arch = "x86_64")]
mod x86_64;
#[cfg(target_arch = "x86_64")]
pub use self::x86_64::*;
#[cfg(target_arch = "riscv64")]
mod riscv64;
#[cfg(target_arch = "riscv64")]
pub use self::riscv64::*;
-45
View File
@@ -1,45 +0,0 @@
use std::proto::Protocol;
use uefi::guid::Guid;
use uefi::status::{Result, Status};
#[derive(Debug)]
#[repr(C)]
struct RiscVEfiBootProtocol {
pub revision: u64,
pub efi_get_boot_hartid:
unsafe extern "efiapi" fn(this: *mut Self, phartid: *mut usize) -> Status,
}
impl RiscVEfiBootProtocol {
pub const GUID: Guid = Guid::parse_str("ccd15fec-6f73-4eec-8395-3e69e4b940bf");
// pub const REVISION: u64 = 0x00010000;
}
struct RiscVEfiBoot(pub &'static mut RiscVEfiBootProtocol);
impl Protocol<RiscVEfiBootProtocol> for RiscVEfiBoot {
fn guid() -> Guid {
RiscVEfiBootProtocol::GUID
}
fn new(inner: &'static mut RiscVEfiBootProtocol) -> Self {
Self(inner)
}
}
impl RiscVEfiBoot {
pub fn efi_get_boot_hartid(&mut self) -> Result<usize> {
let mut boot_hartid: usize = 0;
match unsafe { (self.0.efi_get_boot_hartid)(self.0, &mut boot_hartid) } {
ok if ok.is_success() => Ok(boot_hartid),
err => Err(err),
}
}
}
pub fn efi_get_boot_hartid() -> Result<usize> {
let handles = RiscVEfiBoot::locate_handle()?;
let handle = handles.first().ok_or(Status::NOT_FOUND)?;
let mut proto = RiscVEfiBoot::handle_protocol(*handle)?;
proto.efi_get_boot_hartid()
}
-113
View File
@@ -1,113 +0,0 @@
use core::arch::{global_asm, naked_asm};
/// Unfortunately this can't be written in Rust because it might use some not-yet
/// relocated data such as jump tables
#[unsafe(naked)]
#[unsafe(no_mangle)]
extern "C" fn coff_relocate(dynentry: *const u8, base: usize) -> usize {
unsafe {
naked_asm!(
"
mv t4, zero // RELA
li t5, -1 // RELASZ
li t6, -1 // RELAENT
5:
ld t0, 0(a0)
beqz t0, 6f
addi a0, a0, 16
addi t0, t0, -4
bltz t0, 3f // fail on DT_NEEDED=1, DT_PLTRELSZ=2, DT_PLTGOT=3
addi t0, t0, -3
bltz t0, 5b // skip DT_HASH=4, DT_STRTAB=5, DT_SYMTAB=6
bnez t0, 2f
ld t4, -8(a0) // DT_RELA=7
j 5b
2: addi t0, t0, -1 // DT_RELASZ=8
bnez t0, 2f
ld t5, -8(a0)
j 5b
2: addi t0, t0, -1 // DT_RELAENT=9
bnez t0, 2f
ld t6, -8(a0)
j 5b
2: addi t0, t0, -3
bltz t0, 5b // skip DT_STRSZ=10, DT_SYMENT=11
addi t0, t0, -2
bltz t0, 3f // fail on DT_INIT=12, DT_FINI=13
beqz t0, 5b // skip DT_SONAME=14
2: addi t0, t0, -2
bltz t0, 3f // fail on DT_RPATH
beqz t0, 5b // skip SYMBOLIC=16
li t1, 0x6ffffef5-16
sub t0, t0, t1
beqz t0, 5b // skip DT_GNU_HASH=0x6ffffef5
nop
3: // error
mv a0, zero
ret
6:
bnez t4, 2f
4: // success
li a0, 1
ret
2: bltz t5, 3b
blez t6, 3b
add t4, t4, a1
add t5, t5, t4
7:
bge t4, t5, 4b
ld t0, 0(t4) // r_offset
add t0, t0, a1
lwu t1, 8(t4) // r_type
ld t2, 16(t4) // r_addend
add t4, t4, t6
addi t1, t1, -3 // R_RISCV_RELATIVE=3
bnez t1, 3b
add t2, t2, a1 // RELATIVE: *value = base + addend
sd t2, 0(t0)
j 7b
"
)
}
}
global_asm!(
r#"
.global coff_start
coff_start:
.option norelax
addi sp, sp, -24
sd a0, 0(sp)
sd a1, 8(sp)
sd ra, 16(sp)
lla a0, _DYNAMIC
lla a1, ImageBase // actual loaded image base to relocate to
jal coff_relocate
.option relax
mv t0, a0
ld a0, 0(sp)
ld a1, 8(sp)
ld ra, 16(sp)
addi sp, sp, 24
beqz t0, 2f
j efi_main
2: ret
"#
);
// GNU-EFI .reloc trick to make objcopy say we are relocatable
global_asm!(
r#"
.section .data
DUMMY_RELOCATION: .4byte 0
.section .reloc, "a"
2:
.4byte DUMMY_RELOCATION - ImageBase
.4byte 12
.4byte 0
"#
);
-70
View File
@@ -1,70 +0,0 @@
use crate::KernelArgs;
use crate::arch::PHYS_OFFSET;
use crate::arch::SATP_BITS;
use crate::logger::LOGGER;
use crate::os::OsEfi;
use crate::os::uefi::memory_map::memory_map;
use core::arch::asm;
use core::mem;
use uefi::status::Result;
mod boot_protocol;
mod coff_helper;
pub use boot_protocol::*;
unsafe extern "C" fn kernel_entry(
page_phys: usize,
stack: u64,
func: u64,
args: *const KernelArgs,
) -> ! {
unsafe {
// Set page tables
asm!(
"csrw satp, {0}",
"sfence.vma",
in(reg) (page_phys >> 12 | SATP_BITS << 60)
);
let entry_fn: extern "C" fn(*const KernelArgs) -> ! = mem::transmute(func);
// Set stack and go to kernel
asm!("mv sp, {0}",
"mv a0, {1}",
"jalr {2}",
in(reg) stack,
in(reg) args,
in(reg) entry_fn
);
loop {}
}
}
pub fn main() -> Result<()> {
LOGGER.init();
let mut os = OsEfi::new();
// Disable cursor
let _ = (os.st.ConsoleOut.EnableCursor)(os.st.ConsoleOut, false);
let (page_phys, func, args) = crate::main(&mut os);
unsafe {
memory_map().exit_boot_services();
kernel_entry(
page_phys,
args.stack_base + args.stack_size + PHYS_OFFSET,
func,
&args,
);
}
}
pub fn disable_interrupts() {
unsafe {
asm!("csrci sstatus, 2");
}
}
-82
View File
@@ -1,82 +0,0 @@
use core::{arch::asm, mem};
use uefi::status::Result;
use x86::{
controlregs::{self, Cr0, Cr4},
msr,
};
use crate::{logger::LOGGER, KernelArgs};
use super::super::{memory_map::memory_map, OsEfi};
unsafe extern "C" fn kernel_entry(
page_phys: usize,
stack: u64,
func: u64,
args: *const KernelArgs,
) -> ! {
unsafe {
// Read memory map and exit boot services
memory_map().exit_boot_services();
// Enable FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension
let mut cr4 = controlregs::cr4();
cr4 |= Cr4::CR4_ENABLE_SSE
| Cr4::CR4_ENABLE_GLOBAL_PAGES
| Cr4::CR4_ENABLE_PAE
| Cr4::CR4_ENABLE_PSE;
controlregs::cr4_write(cr4);
// Enable Long mode and NX bit
let mut efer = msr::rdmsr(msr::IA32_EFER);
efer |= 1 << 11 | 1 << 8;
msr::wrmsr(msr::IA32_EFER, efer);
// Set new page map
controlregs::cr3_write(page_phys as u64);
// Enable paging, write protect kernel, protected mode
let mut cr0 = controlregs::cr0();
cr0 |= Cr0::CR0_ENABLE_PAGING | Cr0::CR0_WRITE_PROTECT | Cr0::CR0_PROTECTED_MODE;
controlregs::cr0_write(cr0);
// Set stack
asm!("mov rsp, {}", in(reg) stack);
// Call kernel entry
let entry_fn: extern "sysv64" fn(*const KernelArgs) -> ! = mem::transmute(func);
entry_fn(args);
}
}
pub fn main() -> Result<()> {
LOGGER.init();
let mut os = OsEfi::new();
// Disable cursor
let _ = (os.st.ConsoleOut.EnableCursor)(os.st.ConsoleOut, false);
let (page_phys, func, args) = crate::main(&mut os);
unsafe {
kernel_entry(
page_phys,
args.stack_base
+ args.stack_size
+ if crate::KERNEL_64BIT {
crate::arch::x64::PHYS_OFFSET
} else {
crate::arch::x32::PHYS_OFFSET as u64
},
func,
&args,
);
}
}
pub fn disable_interrupts() {
unsafe {
asm!("cli");
}
}
-647
View File
@@ -1,647 +0,0 @@
use alloc::{string::String, vec, vec::Vec};
use core::{fmt::Write, mem, ptr, slice};
use uefi::{
device::{
DevicePath, DevicePathAcpiType, DevicePathBbsType, DevicePathEndType,
DevicePathHardwareType, DevicePathMediaType, DevicePathMessagingType, DevicePathType,
},
guid::Guid,
status::Status,
Handle,
};
use uefi_std::{fs::FileSystem, loaded_image::LoadedImage, proto::Protocol};
use super::disk::{DiskEfi, DiskOrFileEfi};
use redoxfs::Disk;
const LINUX_FS_GUID: [u8; 16] = [
0xAF, 0x3D, 0xC6, 0x0F, 0x83, 0x84, 0x72, 0x47,
0x8E, 0x79, 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4,
];
fn gpt_find_redoxfs_offset(disk: &mut DiskEfi) -> u64 {
let block_size = disk.0.Media.BlockSize as u64;
if block_size == 0 {
return 2 * crate::MIBI as u64;
}
let mut header_buf = [0u8; 512];
let lba1_block = block_size / redoxfs::BLOCK_SIZE;
let header_read_len = if 512 <= redoxfs::BLOCK_SIZE as usize {
redoxfs::BLOCK_SIZE as usize
} else {
512
};
let mut read_buf = vec![0u8; header_read_len];
if unsafe { disk.read_at(lba1_block, &mut read_buf) }.is_err() {
log::warn!("GPT: failed to read LBA 1");
return 2 * crate::MIBI as u64;
}
let copy_len = 512.min(read_buf.len());
header_buf[..copy_len].copy_from_slice(&read_buf[..copy_len]);
if &header_buf[0..8] != b"EFI PART" {
log::warn!("GPT: no valid signature at LBA 1");
return 2 * crate::MIBI as u64;
}
let read_le_u32 = |off: usize| -> u32 {
u32::from_le_bytes(header_buf[off..off + 4].try_into().unwrap_or([0; 4]))
};
let read_le_u64 = |off: usize| -> u64 {
u64::from_le_bytes(header_buf[off..off + 8].try_into().unwrap_or([0; 8]))
};
let _revision = read_le_u32(8);
let _header_size = read_le_u32(12);
let partition_entry_start_lba = read_le_u64(72);
let num_entries = read_le_u32(80);
let entry_size = read_le_u32(84);
if num_entries == 0 || entry_size == 0 || num_entries > 128 {
log::warn!("GPT: invalid entry count {} or size {}", num_entries, entry_size);
return 2 * crate::MIBI as u64;
}
let entries_byte_offset = partition_entry_start_lba * block_size;
let entries_start_block = entries_byte_offset / redoxfs::BLOCK_SIZE;
let total_entries_bytes = num_entries as usize * entry_size as usize;
let read_size = total_entries_bytes.min(4096);
let mut entries_buf = vec![0u8; read_size + redoxfs::BLOCK_SIZE as usize];
let entries_read_blocks = (read_size as u64).div_ceil(redoxfs::BLOCK_SIZE) as usize;
if unsafe { disk.read_at(entries_start_block, &mut entries_buf[..entries_read_blocks * redoxfs::BLOCK_SIZE as usize]) }.is_err() {
log::warn!("GPT: failed to read partition entries");
return 2 * crate::MIBI as u64;
}
let entries_per_chunk = read_size / entry_size as usize;
for i in 0..entries_per_chunk.min(num_entries as usize) {
let off = i * entry_size as usize;
if off + entry_size as usize > entries_buf.len() {
break;
}
let entry = &entries_buf[off..off + entry_size as usize];
let type_guid = &entry[0..16];
if type_guid == [0u8; 16] {
continue;
}
if type_guid == LINUX_FS_GUID {
let first_lba = u64::from_le_bytes(
entry[32..40].try_into().unwrap_or([0; 8]),
);
let offset_bytes = first_lba * block_size;
log::debug!("GPT: found REDOXFS/Linux partition at LBA {} (offset {} bytes)", first_lba, offset_bytes);
return offset_bytes;
}
}
log::warn!("GPT: no Linux filesystem partition found, falling back to 2 MiB");
2 * crate::MIBI as u64
}
fn gpt_find_redoxfs_offset_from_slice(data: &[u8]) -> u64 {
if data.len() < 520 {
return 2 * crate::MIBI as u64;
}
let header = &data[512..];
if &header[0..8] != b"EFI PART" {
return 2 * crate::MIBI as u64;
}
let read_le_u32 = |off: usize| -> u32 {
u32::from_le_bytes(header[off..off + 4].try_into().unwrap_or([0; 4]))
};
let read_le_u64 = |off: usize| -> u64 {
u64::from_le_bytes(header[off..off + 8].try_into().unwrap_or([0; 8]))
};
let partition_entry_start_lba = read_le_u64(72);
let num_entries = read_le_u32(80);
let entry_size = read_le_u32(84);
if num_entries == 0 || entry_size == 0 || num_entries > 128 {
return 2 * crate::MIBI as u64;
}
let gpt_lba_size: u64 = 512;
let entries_byte_offset = partition_entry_start_lba * gpt_lba_size;
for i in 0..num_entries as usize {
let off = entries_byte_offset as usize + i * entry_size as usize;
if off + entry_size as usize > data.len() {
break;
}
let entry = &data[off..off + entry_size as usize];
let type_guid = &entry[0..16];
if type_guid == [0u8; 16] {
continue;
}
if type_guid == LINUX_FS_GUID {
let first_lba = u64::from_le_bytes(
entry[32..40].try_into().unwrap_or([0; 8]),
);
return first_lba * gpt_lba_size;
}
}
2 * crate::MIBI as u64
}
#[derive(Debug)]
enum DevicePathRelation {
This,
Parent(usize),
Child(usize),
None,
}
fn device_path_relation(a_path: &DevicePath, b_path: &DevicePath) -> DevicePathRelation {
let mut a_iter = DevicePathIter::new(a_path);
let mut b_iter = DevicePathIter::new(b_path);
loop {
match (a_iter.next(), b_iter.next()) {
(None, None) => return DevicePathRelation::This,
(None, Some(_)) => return DevicePathRelation::Parent(b_iter.count()),
(Some(_), None) => return DevicePathRelation::Child(a_iter.count()),
(Some((a_node, a_data)), Some((b_node, b_data))) => {
if a_node.Type != b_node.Type {
return DevicePathRelation::None;
}
if a_node.SubType != b_node.SubType {
return DevicePathRelation::None;
}
if a_data != b_data {
return DevicePathRelation::None;
}
}
}
}
}
fn esp_live_image(esp_handle: Handle, esp_device_path: &DevicePath) -> Option<Vec<u8>> {
let mut esp_fs = match FileSystem::handle_protocol(esp_handle) {
Ok(esp_fs) => esp_fs,
Err(err) => {
log::warn!("Failed to find SimpleFileSystem protocol: {:?}", err);
return None;
}
};
let mut root = match esp_fs.root() {
Ok(root) => root,
Err(err) => {
log::warn!("Failed to open ESP filesystem: {:?}", err);
return None;
}
};
const fn as_utf16_str<const N: usize>(s: [u8; N]) -> [u16; N] {
let mut ret = [0; N];
let mut i = 0;
while i < N {
ret[i] = s[i] as u16;
i += 1;
}
ret
}
let filename = const { &as_utf16_str(*b"redox-live.iso\0") };
let mut live_image = match root.open(filename) {
Ok(live_image) => live_image,
Err(Status::NOT_FOUND) => return None,
Err(err) => {
log::warn!(
"Failed to open {}\\redox-live.iso: {:?}",
device_path_to_string(esp_device_path),
err
);
return None;
}
};
let mut buffer = Vec::new();
live_image.read_to_end(&mut buffer).unwrap();
Some(buffer)
}
pub struct DiskDevice {
pub handle: Handle,
pub disk: DiskOrFileEfi,
pub partition_offset: u64,
pub device_path: DevicePathProtocol,
pub file_path: Option<&'static str>,
}
pub fn disk_device_priority() -> Vec<DiskDevice> {
// Get the handle of the partition this program was loaded from, which should be the ESP
let esp_handle = match LoadedImage::handle_protocol(std::handle()) {
Ok(loaded_image) => loaded_image.0.DeviceHandle,
Err(err) => {
log::warn!("Failed to find LoadedImage protocol: {:?}", err);
return Vec::new();
}
};
// Get the device path of the ESP
let esp_device_path = match DevicePathProtocol::handle_protocol(esp_handle) {
Ok(ok) => ok,
Err(err) => {
log::warn!(
"Failed to find device path protocol on {:?}: {:?}",
esp_handle,
err
);
return Vec::new();
}
};
if cfg!(feature = "live") {
if let Some(buffer) = esp_live_image(esp_handle, esp_device_path.0) {
let partition_offset = if buffer.len() > 520 && &buffer[512..520] == b"EFI PART" {
gpt_find_redoxfs_offset_from_slice(&buffer)
} else {
0
};
return vec![DiskDevice {
handle: esp_handle,
partition_offset,
disk: DiskOrFileEfi::File(buffer),
device_path: esp_device_path,
file_path: Some("redox-live.iso"),
}];
}
}
// Get all block I/O handles along with their block I/O implementations and device paths
let handles = match DiskEfi::locate_handle() {
Ok(ok) => ok,
Err(err) => {
log::warn!("Failed to find block I/O handles: {:?}", err);
Vec::new()
}
};
let mut devices = Vec::with_capacity(handles.len());
for handle in handles {
let mut disk = match DiskEfi::handle_protocol(handle) {
Ok(ok) => ok,
Err(err) => {
log::warn!(
"Failed to find block I/O protocol on {:?}: {:?}",
handle,
err
);
continue;
}
};
if !disk.0.Media.MediaPresent {
continue;
}
let device_path = match DevicePathProtocol::handle_protocol(handle) {
Ok(ok) => ok,
Err(err) => {
log::warn!(
"Failed to find device path protocol on {:?}: {:?}",
handle,
err
);
continue;
}
};
let partition_offset = if disk.0.Media.LogicalPartition {
0
} else {
gpt_find_redoxfs_offset(&mut disk)
};
devices.push(DiskDevice {
handle,
partition_offset,
disk: DiskOrFileEfi::Disk(disk),
device_path,
file_path: None,
});
}
// Find possible boot disks
let mut boot_disks = Vec::with_capacity(1);
{
let mut i = 0;
while i < devices.len() {
if let DevicePathRelation::Parent(0) =
device_path_relation(devices[i].device_path.0, esp_device_path.0)
{
boot_disks.push(devices.remove(i));
continue;
}
i += 1;
}
}
// Find all children of possible boot devices
let mut priority = Vec::with_capacity(devices.capacity());
for boot_disk in boot_disks {
let mut i = 0;
while i < devices.len() {
// Only prioritize non-ESP devices
if devices[i].handle != esp_handle {
if let DevicePathRelation::Child(0) =
device_path_relation(devices[i].device_path.0, boot_disk.device_path.0)
{
priority.push(devices.remove(i));
continue;
}
}
i += 1;
}
priority.push(boot_disk);
}
// Add any remaining devices
priority.extend(devices);
priority
}
#[repr(C, packed)]
#[allow(dead_code)]
struct DevicePathHarddrive {
partition_number: u32,
partition_start: u64,
partition_size: u64,
partition_signature: [u8; 16],
partition_format: u8,
signature_type: u8,
}
pub fn device_path_to_string(device_path: &DevicePath) -> String {
let mut s = String::new();
for (node, node_data) in DevicePathIter::new(device_path) {
let read_u16 = |i: usize| -> u16 { (node_data[i] as u16) | (node_data[i + 1] as u16) << 8 };
let read_u32 = |i: usize| -> u32 {
(node_data[i] as u32)
| (node_data[i + 1] as u32) << 8
| (node_data[i + 2] as u32) << 16
| (node_data[i + 3] as u32) << 24
};
if !s.is_empty() {
s.push('/');
}
let _ = match DevicePathType::try_from(node.Type) {
Ok(path_type) => match path_type {
DevicePathType::Hardware => match DevicePathHardwareType::try_from(node.SubType) {
Ok(sub_type) => match sub_type {
DevicePathHardwareType::Pci if node_data.len() == 2 => {
let func = node_data[0];
let dev = node_data[1];
write!(s, "Pci(0x{dev:X},0x{func:X})")
}
_ => write!(s, "{path_type:?} {sub_type:?} {node_data:X?}"),
},
Err(()) => write!(s, "{:?} 0x{:02X} {:X?}", path_type, node.SubType, node_data),
},
DevicePathType::Acpi => match DevicePathAcpiType::try_from(node.SubType) {
Ok(sub_type) => match sub_type {
DevicePathAcpiType::Acpi if node_data.len() == 8 => {
let hid = read_u32(0);
let uid = read_u32(4);
if hid & 0xFFFF == 0x41D0 {
write!(s, "Acpi(PNP{:04X},0x{:X})", hid >> 16, uid)
} else {
write!(s, "Acpi(0x{hid:08X},0x{uid:X})")
}
}
_ => write!(s, "{path_type:?} {sub_type:?} {node_data:X?}"),
},
Err(()) => write!(s, "{:?} 0x{:02X} {:X?}", path_type, node.SubType, node_data),
},
DevicePathType::Messaging => {
match DevicePathMessagingType::try_from(node.SubType) {
Ok(sub_type) => match sub_type {
DevicePathMessagingType::Sata if node_data.len() == 6 => {
let hba_port = read_u16(0);
let multiplier_port = read_u16(2);
let logical_unit = read_u16(4);
if multiplier_port & (1 << 15) != 0 {
write!(s, "Sata(0x{hba_port:X},0x{logical_unit:X})")
} else {
write!(
s,
"Sata(0x{hba_port:X},0x{multiplier_port:X},0x{logical_unit:X})"
)
}
}
DevicePathMessagingType::Usb if node_data.len() == 2 => {
let port = node_data[0];
let iface = node_data[1];
write!(s, "Usb(0x{port:X},0x{iface:X})")
}
DevicePathMessagingType::Nvme if node_data.len() == 12 => {
let nsid = read_u32(0);
let eui = &node_data[4..];
if eui == [0, 0, 0, 0, 0, 0, 0, 0] {
write!(s, "NVMe(0x{nsid:X})")
} else {
write!(
s,
"NVMe(0x{:X},{:02X}-{:02X}-{:02X}-{:02X}-{:02X}-{:02X}-{:02X}-{:02X})",
nsid,
eui[0],
eui[1],
eui[2],
eui[3],
eui[4],
eui[5],
eui[6],
eui[7],
)
}
}
DevicePathMessagingType::Mac
if node_data.len() == 33 && node_data[32] == 0
|| node_data[32] == 1 =>
{
write!(
s,
"Mac({:02x}{:02x}{:02x}{:02x}{:02x}{:02x},{:#02x})",
node_data[0],
node_data[1],
node_data[2],
node_data[3],
node_data[4],
node_data[5],
node_data[32],
)
}
_ => write!(s, "{path_type:?} {sub_type:?} {node_data:X?}"),
},
Err(()) => {
write!(s, "{:?} 0x{:02X} {:X?}", path_type, node.SubType, node_data)
}
}
}
DevicePathType::Media => match DevicePathMediaType::try_from(node.SubType) {
Ok(sub_type) => {
match sub_type {
DevicePathMediaType::Harddrive
if node_data.len() == mem::size_of::<DevicePathHarddrive>() =>
{
let harddrive = unsafe {
ptr::read(node_data.as_ptr() as *const DevicePathHarddrive)
};
let partition_number = unsafe {
ptr::read_unaligned(ptr::addr_of!(harddrive.partition_number))
};
match harddrive.signature_type {
1 => {
let id = unsafe {
ptr::read(harddrive.partition_signature.as_ptr()
as *const u32)
};
write!(s, "HD(0x{partition_number:X},MBR,0x{id:X})")
}
2 => {
let guid = unsafe {
ptr::read(harddrive.partition_signature.as_ptr()
as *const Guid)
};
write!(
s,
"HD(0x{:X},GPT,{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X})",
partition_number,
guid.0,
guid.1,
guid.2,
guid.3[0],
guid.3[1],
guid.3[2],
guid.3[3],
guid.3[4],
guid.3[5],
guid.3[6],
guid.3[7],
)
}
_ => {
write!(
s,
"HD(0x{:X},0x{:X},{:X?})",
partition_number,
harddrive.signature_type,
harddrive.partition_signature
)
}
}
}
DevicePathMediaType::Filepath => {
for chunk in node_data.chunks_exact(2) {
let data = (chunk[0] as u16) | (chunk[1] as u16) << 8;
match unsafe { char::from_u32_unchecked(data as u32) } {
'\\' => s.push('/'),
c => s.push(c),
}
}
Ok(())
}
_ => write!(s, "{path_type:?} {sub_type:?} {node_data:X?}"),
}
}
Err(()) => write!(s, "{:?} 0x{:02X} {:X?}", path_type, node.SubType, node_data),
},
DevicePathType::Bbs => match DevicePathBbsType::try_from(node.SubType) {
Ok(sub_type) => write!(s, "{path_type:?} {sub_type:?} {node_data:X?}"),
Err(()) => write!(s, "{:?} 0x{:02X} {:X?}", path_type, node.SubType, node_data),
},
DevicePathType::End => match DevicePathEndType::try_from(node.SubType) {
Ok(sub_type) => write!(s, "{path_type:?} {sub_type:?} {node_data:X?}"),
Err(()) => write!(s, "{:?} 0x{:02X} {:X?}", path_type, node.SubType, node_data),
},
},
Err(()) => {
write!(
s,
"0x{:02X} 0x{:02X} {:X?}",
node.Type, node.SubType, node_data
)
}
};
}
s
}
pub struct DevicePathProtocol(pub &'static mut DevicePath);
impl Protocol<DevicePath> for DevicePathProtocol {
fn guid() -> Guid {
uefi::guid::DEVICE_PATH_GUID
}
fn new(inner: &'static mut DevicePath) -> Self {
Self(inner)
}
}
pub struct LoadedImageDevicePathProtocol(pub &'static mut DevicePath);
impl Protocol<DevicePath> for LoadedImageDevicePathProtocol {
fn guid() -> Guid {
uefi::guid::LOADED_IMAGE_DEVICE_PATH_GUID
}
fn new(inner: &'static mut DevicePath) -> Self {
Self(inner)
}
}
pub struct DevicePathIter<'a> {
device_path: &'a DevicePath,
node_ptr: *const DevicePath,
}
impl<'a> DevicePathIter<'a> {
pub fn new(device_path: &'a DevicePath) -> Self {
Self {
device_path,
node_ptr: device_path as *const DevicePath,
}
}
}
impl<'a> Iterator for DevicePathIter<'a> {
type Item = (&'a DevicePath, &'a [u8]);
fn next(&mut self) -> Option<Self::Item> {
let node = unsafe { &*self.node_ptr };
if node.Type == DevicePathType::End as u8 {
return None;
}
let node_data = unsafe {
slice::from_raw_parts(
self.node_ptr.add(1) as *mut u8,
node.Length.saturating_sub(4) as usize,
)
};
self.node_ptr = (self.node_ptr as usize + node.Length as usize) as *const DevicePath;
Some((node, node_data))
}
}
-119
View File
@@ -1,119 +0,0 @@
use alloc::vec::Vec;
use core::slice;
use redoxfs::{Disk, BLOCK_SIZE, RECORD_SIZE};
use std::proto::Protocol;
use syscall::{Error, Result, EINVAL, EIO};
use uefi::block_io::BlockIo as UefiBlockIo;
use uefi::guid::{Guid, BLOCK_IO_GUID};
pub enum DiskOrFileEfi {
Disk(DiskEfi),
File(Vec<u8>),
}
impl redoxfs::Disk for DiskOrFileEfi {
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<usize> {
unsafe {
match self {
DiskOrFileEfi::Disk(disk_efi) => disk_efi.read_at(block, buffer),
DiskOrFileEfi::File(data) => {
buffer.copy_from_slice(
&data[(block * redoxfs::BLOCK_SIZE) as usize
..(block * redoxfs::BLOCK_SIZE) as usize + buffer.len()],
);
Ok(buffer.len())
}
}
}
}
unsafe fn write_at(&mut self, _block: u64, _buffer: &[u8]) -> syscall::Result<usize> {
unreachable!()
}
fn size(&mut self) -> syscall::Result<u64> {
unreachable!()
}
}
pub struct DiskEfi(pub &'static mut UefiBlockIo, &'static mut [u8]);
impl Protocol<UefiBlockIo> for DiskEfi {
fn guid() -> Guid {
BLOCK_IO_GUID
}
fn new(inner: &'static mut UefiBlockIo) -> Self {
// Hack to get aligned buffer
let block = unsafe {
let ptr = super::alloc_zeroed_page_aligned(RECORD_SIZE as usize);
slice::from_raw_parts_mut(ptr, RECORD_SIZE as usize)
};
Self(inner, block)
}
}
impl Disk for DiskEfi {
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
unsafe {
// Optimization for live disks
if let Some(live) = crate::LIVE_OPT {
if block >= live.0 {
let start = ((block - live.0) * BLOCK_SIZE) as usize;
let end = start + buffer.len();
if end <= live.1.len() {
buffer.copy_from_slice(&live.1[start..end]);
return Ok(buffer.len());
}
}
}
// Use aligned buffer if necessary
let mut ptr = buffer.as_mut_ptr();
if self.0.Media.IoAlign != 0 {
if (ptr as usize) % (self.0.Media.IoAlign as usize) != 0 {
if buffer.len() <= self.1.len() {
ptr = self.1.as_mut_ptr();
} else {
println!(
"DiskEfi::read_at 0x{:X} requires alignment, ptr = 0x{:p}, len = 0x{:x}",
block,
ptr,
buffer.len()
);
return Err(Error::new(EINVAL));
}
}
}
let block_size = self.0.Media.BlockSize as u64;
let lba = block * BLOCK_SIZE / block_size;
match (self.0.ReadBlocks)(self.0, self.0.Media.MediaId, lba, buffer.len(), ptr) {
status if status.is_success() => {
// Copy to original buffer if using aligned buffer
if ptr != buffer.as_mut_ptr() {
let (left, _) = self.1.split_at(buffer.len());
buffer.copy_from_slice(left);
}
Ok(buffer.len())
}
err => {
println!("DiskEfi::read_at 0x{:X} failed: {:?}", block, err);
Err(Error::new(EIO))
}
}
}
}
unsafe fn write_at(&mut self, block: u64, _buffer: &[u8]) -> Result<usize> {
println!("DiskEfi::write_at 0x{:X} not implemented", block);
Err(Error::new(EIO))
}
fn size(&mut self) -> Result<u64> {
println!("DiskEfi::size not implemented");
Err(Error::new(EIO))
}
}
-41
View File
@@ -1,41 +0,0 @@
use std::proto::Protocol;
use uefi::graphics::GraphicsOutput;
use uefi::guid::{Guid, GRAPHICS_OUTPUT_PROTOCOL_GUID};
pub struct Output(pub &'static mut GraphicsOutput);
impl Protocol<GraphicsOutput> for Output {
fn guid() -> Guid {
GRAPHICS_OUTPUT_PROTOCOL_GUID
}
fn new(inner: &'static mut GraphicsOutput) -> Self {
Output(inner)
}
}
const EDID_ACTIVE_PROTOCOL_GUID: Guid = Guid(
0xbd8c1056,
0x9f36,
0x44ec,
[0x92, 0xa8, 0xa6, 0x33, 0x7f, 0x81, 0x79, 0x86],
);
#[allow(non_snake_case)]
#[repr(C)]
pub struct EdidActiveProtocol {
pub SizeOfEdid: u32,
pub Edid: *const u8,
}
pub struct EdidActive(pub &'static mut EdidActiveProtocol);
impl Protocol<EdidActiveProtocol> for EdidActive {
fn guid() -> Guid {
EDID_ACTIVE_PROTOCOL_GUID
}
fn new(inner: &'static mut EdidActiveProtocol) -> Self {
EdidActive(inner)
}
}
-125
View File
@@ -1,125 +0,0 @@
use crate::Os;
use alloc::vec::Vec;
use byteorder::ByteOrder;
use byteorder::BE;
use core::slice;
use fdt::Fdt;
use uefi::guid::DEVICE_TREE_GUID;
#[cfg(target_arch = "aarch64")]
use uefi::{
guid::SMBIOS3_TABLE_GUID,
status::{Result, Status},
};
pub static mut DEV_MEM_AREA: Vec<(usize, usize)> = Vec::new();
pub unsafe fn is_in_dev_mem_region(addr: usize) -> bool {
#[allow(static_mut_refs)]
unsafe {
if DEV_MEM_AREA.is_empty() {
return false;
}
for item in DEV_MEM_AREA.iter() {
if (addr >= item.0) && (addr < item.0 + item.1) {
return true;
}
}
return false;
}
}
unsafe fn get_dev_mem_region(fdt: &Fdt) {
unsafe {
let Some(soc) = fdt.find_node("/soc") else {
return;
};
let Some(ranges) = soc.ranges() else {
return;
};
let cell_sizes = soc.cell_sizes();
for chunk in ranges {
let child_bus_addr = chunk.child_bus_address;
let parent_bus_addr = chunk.parent_bus_address;
let addr_size = chunk.size;
println!(
"dev mem 0x{:08x} 0x{:08x} 0x{:08x}",
child_bus_addr, parent_bus_addr, addr_size
);
#[allow(static_mut_refs)]
DEV_MEM_AREA.push((parent_bus_addr as usize, addr_size as usize));
}
}
}
fn parse_dtb(os: &impl Os, address: *const u8) -> Option<(u64, u64)> {
unsafe {
if let Ok(fdt) = fdt::Fdt::from_ptr(address) {
let mut rsdps_area = Vec::new();
//println!("DTB model = {}", fdt.root().model());
get_dev_mem_region(&fdt);
let length = fdt.total_size();
let align = 8;
rsdps_area.extend(core::slice::from_raw_parts(address, length));
rsdps_area.resize(((rsdps_area.len() + (align - 1)) / align) * align, 0u8);
let size = rsdps_area.len();
let base = os.alloc_zeroed_page_aligned(size);
slice::from_raw_parts_mut(base, size).copy_from_slice(&rsdps_area);
Some((base as u64, size as u64))
} else {
println!("Failed to parse DTB");
None
}
}
}
#[cfg(target_arch = "aarch64")]
fn find_smbios3_system(address: *const u8) -> Result<dmidecode::System<'static>> {
unsafe {
let smb = core::slice::from_raw_parts(address, 24);
if let Ok(smbios) = dmidecode::EntryPoint::search(smb) {
let smb_structure_data = core::slice::from_raw_parts(
smbios.smbios_address() as *const u8,
smbios.smbios_len() as usize,
);
for structure in smbios.structures(smb_structure_data) {
if let Ok(sval) = structure {
//println!("SMBIOS: {:#?}", sval);
if let dmidecode::Structure::System(buf) = sval {
return Ok(buf);
}
}
}
}
}
Err(Status::NOT_FOUND)
}
pub(crate) fn find_dtb(os: &impl Os) -> Option<(u64, u64)> {
let cfg_tables = std::system_table().config_tables();
for cfg_table in cfg_tables.iter() {
if cfg_table.VendorGuid == DEVICE_TREE_GUID {
let addr = cfg_table.VendorTable;
return parse_dtb(os, addr as *const u8);
}
}
/* This hack is no longer needed, but can be re-enabled for testing
#[cfg(target_arch = "aarch64")]
for cfg_table in cfg_tables.iter() {
if cfg_table.VendorGuid == SMBIOS3_TABLE_GUID {
let addr = cfg_table.VendorTable;
if let Ok(sys) = find_smbios3_system(addr as *const u8) {
let get_dtb_addr = match (sys.manufacturer, sys.version) {
("QEMU", version) if version.starts_with("virt") => Some(0x4000_0000 as usize),
_ => None,
};
if let Some(dtb_addr) = get_dtb_addr {
return parse_dtb(os, dtb_addr as *const u8);
}
}
}
}
*/
None
}
-141
View File
@@ -1,141 +0,0 @@
use alloc::vec;
use alloc::vec::Vec;
use core::{mem, ptr};
use uefi::memory::{MemoryDescriptor, MemoryType};
use crate::area_add;
use crate::os::{OsMemoryEntry, OsMemoryKind};
use super::status_to_result;
pub struct MemoryMapIter {
map: Vec<u8>,
map_key: usize,
descriptor_size: usize,
descriptor_version: u32,
i: usize,
}
impl MemoryMapIter {
pub fn new() -> Self {
let uefi = std::system_table();
let mut map = vec![0; 65536];
let mut map_size = map.len();
let mut map_key = 0;
let mut descriptor_size = 0;
let mut descriptor_version = 0;
status_to_result((uefi.BootServices.GetMemoryMap)(
&mut map_size,
map.as_mut_ptr() as *mut MemoryDescriptor,
&mut map_key,
&mut descriptor_size,
&mut descriptor_version,
))
.expect("Failed to get UEFI memory map");
// Ensure descriptor size is usable
assert!(descriptor_size >= mem::size_of::<MemoryDescriptor>());
// Ensure descriptor version is supported
assert_eq!(descriptor_version, 1);
// Reduce map size to returned value
map.truncate(map_size);
Self {
map,
map_key,
descriptor_size,
descriptor_version,
i: 0,
}
}
pub fn exit_boot_services(mut self) {
let handle = std::handle();
let uefi = std::system_table();
// We are writing to the memory map that will be passed to
// SetVirtualAddressMap before ExitBootServices as on some firmware
// EfiLoaderData memory regions like this one are marked as read-only
// after ExitBootServices
for i in 0..self.map.len() / self.descriptor_size {
let descriptor_ptr = unsafe { self.map.as_mut_ptr().add(i * self.descriptor_size) };
let descriptor = unsafe { &mut *(descriptor_ptr as *mut MemoryDescriptor) };
// Map all memory regions even when not marked as EFI_MEMORY_RUNTIME
// as some firmware uses memory regions not marked as
// EFI_MEMORY_RUNTIME in runtime services. Linux has a list of
// exactly which memory regions need to be mapped, but for simplicity
// we are mapping all regions here.
// Identity map all memory regions as some firmware fails to update
// all pointers in SetVirtualAddressMap.
descriptor.VirtualStart.0 = descriptor.PhysicalStart.0;
}
status_to_result((uefi.BootServices.ExitBootServices)(handle, self.map_key))
.expect("Failed to exit UEFI boot services");
// Runtime services must be called with interrupts disabled
super::arch::disable_interrupts();
status_to_result((uefi.RuntimeServices.SetVirtualAddressMap)(
self.map.len(),
self.descriptor_size,
self.descriptor_version,
self.map.as_ptr() as *const MemoryDescriptor,
))
.expect("Failed to set UEFI runtime services virtual address map");
// After ExitBootServices, GlobalAlloc::dealloc() is not allowed anymore
// as it uses boot services.
mem::forget(self);
}
}
impl Iterator for MemoryMapIter {
type Item = OsMemoryEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.i < self.map.len() / self.descriptor_size {
let descriptor_ptr = unsafe { self.map.as_ptr().add(self.i * self.descriptor_size) };
self.i += 1;
let descriptor = unsafe { ptr::read(descriptor_ptr as *const MemoryDescriptor) };
let descriptor_type: MemoryType = unsafe { mem::transmute(descriptor.Type) };
Some(OsMemoryEntry {
base: descriptor.PhysicalStart.0,
//TODO: do not hard code page size
size: descriptor.NumberOfPages * 4096,
kind: match descriptor_type {
MemoryType::EfiLoaderCode
| MemoryType::EfiLoaderData
| MemoryType::EfiBootServicesCode
| MemoryType::EfiBootServicesData
| MemoryType::EfiConventionalMemory => OsMemoryKind::Free,
//TODO: mark ACPI memory as reclaim
_ => OsMemoryKind::Reserved,
},
})
} else {
None
}
}
}
pub unsafe fn memory_map() -> MemoryMapIter {
let mut iter = MemoryMapIter::new();
// Using next to avoid consuming iterator
while let Some(entry) = iter.next() {
area_add(entry);
}
// Rewind iterator
iter.i = 0;
iter
}
-443
View File
@@ -1,443 +0,0 @@
use alloc::vec::Vec;
use core::{cell::RefCell, mem, ptr, slice};
use std::proto::Protocol;
use uefi::{
boot::LocateSearchType,
memory::MemoryType,
reset::ResetType,
status::{Result, Status},
system::SystemTable,
text::TextInputKey,
Handle,
};
use crate::os::{Os, OsHwDesc, OsKey, OsVideoMode};
use self::{
device::{device_path_to_string, disk_device_priority},
disk::DiskOrFileEfi,
display::{EdidActive, Output},
video_mode::VideoModeIter,
};
mod acpi;
mod arch;
mod device;
mod disk;
mod display;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
pub mod dtb;
mod memory_map;
mod video_mode;
#[cfg(target_arch = "riscv64")]
pub use arch::efi_get_boot_hartid;
pub(crate) fn page_size() -> usize {
// EDK2 always uses 4096 as the page size
4096
}
pub(crate) fn alloc_zeroed_page_aligned(size: usize) -> *mut u8 {
assert!(size != 0);
let page_size = page_size();
let pages = size.div_ceil(page_size);
let ptr = {
// Max address mapped by src/arch paging code (8 GiB)
let mut ptr = 0x2_0000_0000;
status_to_result((std::system_table().BootServices.AllocatePages)(
1, // AllocateMaxAddress
MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list
pages,
&mut ptr,
))
.unwrap_or_else(|_| {
ptr = 0;
0
});
if ptr == 0 { ptr::null_mut() } else { ptr as *mut u8 }
};
if !ptr.is_null() {
unsafe { ptr::write_bytes(ptr, 0, pages * page_size) };
}
ptr
}
pub struct OsEfi {
st: &'static SystemTable,
outputs: RefCell<Vec<(Output, Option<EdidActive>)>>,
}
impl OsEfi {
pub fn new() -> Self {
let st = std::system_table();
let mut outputs = Vec::<(Output, Option<EdidActive>)>::new();
{
let guid = Output::guid();
let mut handles = Vec::with_capacity(256);
let mut len = handles.capacity() * mem::size_of::<Handle>();
match status_to_result((st.BootServices.LocateHandle)(
LocateSearchType::ByProtocol,
&guid,
ptr::null(),
&mut len,
handles.as_mut_ptr(),
)) {
Ok(_) => {
unsafe {
handles.set_len(len / mem::size_of::<Handle>());
}
'handles: for handle in handles {
//TODO: do we have to query all modes to get good edid?
match Output::handle_protocol(handle) {
Ok(output) => {
log::debug!(
"Output {:?} at {:x}",
handle,
output.0.Mode.FrameBufferBase
);
if output.0.Mode.FrameBufferBase == 0 {
log::debug!("Skipping output with frame buffer base of 0");
continue 'handles;
}
for other_output in outputs.iter() {
if output.0.Mode.FrameBufferBase
== other_output.0 .0.Mode.FrameBufferBase
{
log::debug!(
"Skipping output with frame buffer base matching another output"
);
continue 'handles;
}
}
outputs.push((
output,
match EdidActive::handle_protocol(handle) {
Ok(efi_edid) => Some(efi_edid),
Err(err) => {
log::warn!(
"Failed to get EFI EDID from handle {:?}: {:?}",
handle,
err
);
None
}
},
));
}
Err(err) => {
log::warn!(
"Failed to get Output from handle {:?}: {:?}",
handle,
err
);
}
}
}
}
Err(err) => {
log::warn!("Failed to locate Outputs: {:?}", err);
}
}
}
Self {
st,
outputs: RefCell::new(outputs),
}
}
}
impl Os for OsEfi {
type D = DiskOrFileEfi;
type V = VideoModeIter;
#[cfg(target_arch = "aarch64")]
fn name(&self) -> &str {
"aarch64/UEFI"
}
#[cfg(target_arch = "x86_64")]
fn name(&self) -> &str {
"x86_64/UEFI"
}
#[cfg(target_arch = "riscv64")]
fn name(&self) -> &str {
"riscv64/UEFI"
}
fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8 {
alloc_zeroed_page_aligned(size)
}
fn page_size(&self) -> usize {
page_size()
}
fn filesystem(
&self,
password_opt: Option<&[u8]>,
) -> syscall::Result<redoxfs::FileSystem<DiskOrFileEfi>> {
let mut seen_enokey = false;
// Search for RedoxFS on disks in prioritized order
println!("Looking for RedoxFS:");
for device in disk_device_priority() {
if let Some(file_path) = device.file_path {
log::debug!(
" - {}\\{}",
device_path_to_string(device.device_path.0),
file_path
);
} else {
log::debug!(" - {}", device_path_to_string(device.device_path.0));
}
let block = device.partition_offset / redoxfs::BLOCK_SIZE;
match redoxfs::FileSystem::open(device.disk, password_opt, Some(block), false) {
Ok(ok) => return Ok(ok),
Err(err) => match err.errno {
// Ignore header not found error
syscall::ENOENT => (),
// Bubble ENOKEY up later if we can't find another partition to return
syscall::ENOKEY => seen_enokey = true,
// Print any other errors
_ => {
log::warn!("BlockIo error: {:?}", err);
}
},
}
}
// Let the caller prompt for a password
if seen_enokey {
return Err(syscall::Error::new(syscall::ENOKEY));
}
log::warn!("No RedoxFS partitions found");
Err(syscall::Error::new(syscall::ENOENT))
}
fn hwdesc(&self) -> OsHwDesc {
//TODO: if both DTB and ACPI are found, we should probably let the OS choose what to use?
// For now we will prefer DTB on platforms that have it
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
if let Some((addr, size)) = dtb::find_dtb(self) {
return OsHwDesc::DeviceTree(addr, size);
}
if let Some((addr, size)) = acpi::find_acpi_table_pointers(self) {
return OsHwDesc::Acpi(addr, size);
}
OsHwDesc::NotFound
}
fn video_outputs(&self) -> usize {
self.outputs.borrow().len()
}
fn video_modes(&self, output_i: usize) -> VideoModeIter {
let output_opt = match self.outputs.borrow_mut().get_mut(output_i) {
Some(output) => unsafe {
// Hack to enable clone
let ptr = output.0 .0 as *mut _;
Some(Output::new(&mut *ptr))
},
None => None,
};
VideoModeIter::new(output_opt)
}
fn set_video_mode(&self, output_i: usize, mode: &mut OsVideoMode) {
//TODO: return error?
let mut outputs = self.outputs.borrow_mut();
let (output, _efi_edid_opt) = &mut outputs[output_i];
status_to_result((output.0.SetMode)(output.0, mode.id)).unwrap();
// Update with actual mode information
mode.width = output.0.Mode.Info.HorizontalResolution;
mode.height = output.0.Mode.Info.VerticalResolution;
mode.base = output.0.Mode.FrameBufferBase as u64;
}
fn best_resolution(&self, output_i: usize) -> Option<(u32, u32)> {
let mut outputs = self.outputs.borrow_mut();
let (output, efi_edid_opt) = outputs.get_mut(output_i)?;
if let Some(efi_edid) = efi_edid_opt {
let edid =
unsafe { slice::from_raw_parts(efi_edid.0.Edid, efi_edid.0.SizeOfEdid as usize) };
if edid.len() > 0x3D {
return Some((
(edid[0x38] as u32) | (((edid[0x3A] as u32) & 0xF0) << 4),
(edid[0x3B] as u32) | (((edid[0x3D] as u32) & 0xF0) << 4),
));
} else {
log::warn!("EFI EDID too small: {}", edid.len());
}
}
// Fallback to the current output resolution
Some((
output.0.Mode.Info.HorizontalResolution,
output.0.Mode.Info.VerticalResolution,
))
}
fn get_key(&self) -> OsKey {
//TODO: do not unwrap
let mut index = 0;
status_to_result((self.st.BootServices.WaitForEvent)(
1,
&self.st.ConsoleIn.WaitForKey,
&mut index,
))
.unwrap();
let mut key = TextInputKey {
ScanCode: 0,
UnicodeChar: 0,
};
status_to_result((self.st.ConsoleIn.ReadKeyStroke)(
self.st.ConsoleIn,
&mut key,
))
.unwrap();
match key.ScanCode {
0 => match key.UnicodeChar {
8 => OsKey::Backspace,
13 => OsKey::Enter,
w => match char::from_u32(w as u32) {
Some(c) => OsKey::Char(c),
None => OsKey::Other,
},
},
1 => OsKey::Up,
2 => OsKey::Down,
3 => OsKey::Right,
4 => OsKey::Left,
8 => OsKey::Delete,
_ => OsKey::Other,
}
}
fn get_key_timeout(&self, milliseconds: usize) -> OsKey {
let slices = milliseconds.div_ceil(100);
for _ in 0..slices {
let mut key = TextInputKey {
ScanCode: 0,
UnicodeChar: 0,
};
let status = (self.st.ConsoleIn.ReadKeyStroke)(self.st.ConsoleIn, &mut key);
if status.is_success() {
return match key.ScanCode {
0 => match key.UnicodeChar {
8 => OsKey::Backspace,
13 => OsKey::Enter,
w => match char::from_u32(w as u32) {
Some(c) => OsKey::Char(c),
None => OsKey::Other,
},
},
1 => OsKey::Up,
2 => OsKey::Down,
3 => OsKey::Right,
4 => OsKey::Left,
8 => OsKey::Delete,
_ => OsKey::Other,
};
}
let _ = status_to_result((self.st.BootServices.Stall)(100_000));
}
OsKey::Timeout
}
fn clear_text(&self) {
//TODO: why does this sometimes return InvalidParameter, but otherwise appear to work?
let _ = status_to_result((self.st.ConsoleOut.ClearScreen)(self.st.ConsoleOut));
}
fn get_text_position(&self) -> (usize, usize) {
(
self.st.ConsoleOut.Mode.CursorColumn as usize,
self.st.ConsoleOut.Mode.CursorRow as usize,
)
}
fn set_text_position(&self, x: usize, y: usize) {
// Ignore error because Tow-Boot appears to not implement this
let _ = status_to_result((self.st.ConsoleOut.SetCursorPosition)(
self.st.ConsoleOut,
x,
y,
));
}
fn set_text_highlight(&self, highlight: bool) {
let attr = if highlight { 0x70 } else { 0x07 };
status_to_result((self.st.ConsoleOut.SetAttribute)(self.st.ConsoleOut, attr)).unwrap();
}
}
fn status_to_result(status: Status) -> Result<usize> {
match status {
Status(ok) if status.is_success() => Ok(ok),
err => Err(err),
}
}
fn set_max_mode(output: &uefi::text::TextOutput) -> Result<()> {
let mut max_i = None;
let mut max_w = 0;
let mut max_h = 0;
for i in 0..output.Mode.MaxMode as usize {
let mut w = 0;
let mut h = 0;
if (output.QueryMode)(output, i, &mut w, &mut h).is_success() {
if w >= max_w && h >= max_h {
max_i = Some(i);
max_w = w;
max_h = h;
}
}
}
if let Some(i) = max_i {
status_to_result((output.SetMode)(output, i))?;
}
Ok(())
}
#[unsafe(no_mangle)]
pub extern "C" fn main() -> Status {
let uefi = std::system_table();
let _ = (uefi.BootServices.SetWatchdogTimer)(0, 0, 0, ptr::null());
if let Err(err) = set_max_mode(uefi.ConsoleOut) {
println!("Failed to set max mode: {:?}", err);
}
if let Err(err) = arch::main() {
panic!("App error: {:?}", err);
}
(uefi.RuntimeServices.ResetSystem)(ResetType::Cold, Status(0), 0, ptr::null());
}
-57
View File
@@ -1,57 +0,0 @@
use core::ptr;
use log::error;
use uefi::status::Status;
use crate::os::uefi::display::Output;
use crate::os::OsVideoMode;
pub struct VideoModeIter {
output_opt: Option<Output>,
i: u32,
}
impl VideoModeIter {
pub fn new(output_opt: Option<Output>) -> Self {
Self { output_opt, i: 0 }
}
}
impl Iterator for VideoModeIter {
type Item = OsVideoMode;
fn next(&mut self) -> Option<Self::Item> {
if let Some(ref mut output) = self.output_opt {
while self.i < output.0.Mode.MaxMode {
let id = self.i;
self.i += 1;
let mut mode_ptr = ::core::ptr::null_mut();
let mut mode_size = 0;
match (output.0.QueryMode)(output.0, id, &mut mode_size, &mut mode_ptr) {
Status::SUCCESS => (),
err => {
error!("Failed to read mode {}: {:?}", id, err);
continue;
}
}
//TODO: ensure mode_size is set correctly
let mode = unsafe { ptr::read(mode_ptr) };
let width = mode.HorizontalResolution;
let height = mode.VerticalResolution;
let stride = mode.PixelsPerScanLine;
return Some(OsVideoMode {
id,
width,
height,
stride,
// Base is retrieved later by setting the mode
base: 0,
});
}
}
None
}
}
+213
View File
@@ -0,0 +1,213 @@
use core::{
mem,
ops::{Deref, DerefMut},
slice,
};
use bitflags::bitflags;
pub struct CallerCtx {
pub pid: usize,
pub uid: u32,
pub gid: u32,
}
pub enum OpenResult {
ThisScheme { number: usize },
OtherScheme { fd: usize },
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct Sqe {
pub opcode: u8,
pub sqe_flags: SqeFlags,
pub _rsvd: u16, // TODO: priority
pub tag: u32,
pub args: [u64; 6],
pub caller: u64,
}
impl Deref for Sqe {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Sqe as *const u8, mem::size_of::<Sqe>()) }
}
}
impl DerefMut for Sqe {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self as *mut Sqe as *mut u8, mem::size_of::<Sqe>()) }
}
}
bitflags! {
#[derive(Clone, Copy, Debug, Default)]
pub struct SqeFlags: u8 {
// If zero, the message is bidirectional, and the scheme is expected to pass the Ksmsg's
// tag field to the Skmsg. Some opcodes require this flag to be set.
const ONEWAY = 1;
// If this flag is set, index 0 of Sqe's args stores the IDs buffer address,
// and index 1 stores the IDs buffer length.
const MULTIPLE_IDS = 1 << 1;
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct Cqe {
pub flags: u8, // bits 2:0 are CqeOpcode
pub extra_raw: [u8; 3],
pub tag: u32,
pub result: u64,
}
impl Deref for Cqe {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Cqe as *const u8, mem::size_of::<Cqe>()) }
}
}
impl DerefMut for Cqe {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self as *mut Cqe as *mut u8, mem::size_of::<Cqe>()) }
}
}
bitflags! {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct NewFdFlags: u8 {
const POSITIONED = 1;
}
}
impl Cqe {
pub fn extra(&self) -> u32 {
u32::from_ne_bytes([self.extra_raw[0], self.extra_raw[1], self.extra_raw[2], 0])
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CqeOpcode {
RespondRegular,
RespondWithFd,
SendFevent, // no tag
ObtainFd,
RespondWithMultipleFds,
/// [`SchemeAsync::on_close`] and [`SchemeSync::on_close`] are only called when the last file
/// descriptor referring to the file description is closed. To implement traditional POSIX
/// advisory file locking, [`CqeOpcode::RespondAndNotifyOnDetach`] is used to notify the scheme
/// by sending a [`RequestKind::OnDetach`] request the next time the file description is
/// "detached" from a file descriptor. Not done by default to avoid unnecessary IPC.
RespondAndNotifyOnDetach,
// TODO: ProvideMmap
}
impl CqeOpcode {
pub fn try_from_raw(raw: u8) -> Option<Self> {
// TODO: Use a library where this match can be automated.
Some(match raw {
0 => Self::RespondRegular,
1 => Self::RespondWithFd,
2 => Self::SendFevent,
3 => Self::ObtainFd,
4 => Self::RespondWithMultipleFds,
5 => Self::RespondAndNotifyOnDetach,
_ => return None,
})
}
}
/// SqeOpcode
#[repr(u8)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug)]
pub enum Opcode {
Close = 3, // fd
Dup = 4, // old fd, buf_ptr, buf_len
Read = 5, // fd, buf_ptr, buf_len, TODO offset, TODO flags, _
Write = 6, // fd, buf_ptr, buf_len, TODO offset, TODO flags)
Fsize = 7, // fd
Fchmod = 8, // fd, new mode
Fchown = 9, // fd, new uid, new gid
Fcntl = 10, // fd, cmd, arg
Fevent = 11, // fd, requested mask
Sendfd = 12,
Fpath = 13, // fd, buf_ptr, buf_len
Frename = 14,
Fstat = 15, // fd, buf_ptr, buf_len
Fstatvfs = 16, // fd, buf_ptr, buf_len
Fsync = 17, // fd
Ftruncate = 18, // fd, new len
Futimens = 19, // fd, times_buf, times_len
MmapPrep = 20,
RequestMmap = 21,
Mremap = 22,
Munmap = 23,
Msync = 24, // TODO
Cancel = 25, // @tag
Getdents = 26,
CloseMsg = 27,
Call = 28,
OpenAt = 29, // fd, buf_ptr, buf_len, flags
Flink = 30,
Recvfd = 31,
UnlinkAt = 32, // fd, path_ptr, path_len (utf8), flags
StdFsCall = 33,
Detach = 34,
}
impl Opcode {
pub fn try_from_raw(raw: u8) -> Option<Self> {
use Opcode::*;
// TODO: Use a library where this match can be automated.
Some(match raw {
3 => Close,
4 => Dup,
5 => Read,
6 => Write,
7 => Fsize,
8 => Fchmod,
9 => Fchown,
10 => Fcntl,
11 => Fevent,
12 => Sendfd,
13 => Fpath,
14 => Frename,
15 => Fstat,
16 => Fstatvfs,
17 => Fsync,
18 => Ftruncate,
19 => Futimens,
20 => MmapPrep,
21 => RequestMmap,
22 => Mremap,
23 => Munmap,
24 => Msync,
25 => Cancel,
26 => Getdents,
27 => CloseMsg,
28 => Call,
29 => OpenAt,
30 => Flink,
31 => Recvfd,
32 => UnlinkAt,
33 => StdFsCall,
34 => Detach,
_ => return None,
})
}
}
-142
View File
@@ -1,142 +0,0 @@
use bitflags::bitflags;
use core::convert::TryInto;
use core::fmt;
use core::ptr::{addr_of, addr_of_mut};
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
use syscall::io::Pio;
use syscall::io::{Io, Mmio, ReadOnly};
bitflags! {
/// Interrupt enable flags
struct IntEnFlags: u8 {
const RECEIVED = 1;
const SENT = 1 << 1;
const ERRORED = 1 << 2;
const STATUS_CHANGE = 1 << 3;
// 4 to 7 are unused
}
}
bitflags! {
/// Line status flags
struct LineStsFlags: u8 {
const INPUT_FULL = 1;
// 1 to 4 unknown
const OUTPUT_EMPTY = 1 << 5;
// 6 and 7 unknown
}
}
#[allow(dead_code)]
#[repr(C, packed)]
pub struct SerialPort<T: Io> {
/// Data register, read to receive, write to send
data: T,
/// Interrupt enable
int_en: T,
/// FIFO control
fifo_ctrl: T,
/// Line control
line_ctrl: T,
/// Modem control
modem_ctrl: T,
/// Line status
line_sts: ReadOnly<T>,
/// Modem status
modem_sts: ReadOnly<T>,
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
impl SerialPort<Pio<u8>> {
pub const fn new(base: u16) -> SerialPort<Pio<u8>> {
SerialPort {
data: Pio::new(base),
int_en: Pio::new(base + 1),
fifo_ctrl: Pio::new(base + 2),
line_ctrl: Pio::new(base + 3),
modem_ctrl: Pio::new(base + 4),
line_sts: ReadOnly::new(Pio::new(base + 5)),
modem_sts: ReadOnly::new(Pio::new(base + 6)),
}
}
}
impl SerialPort<Mmio<u32>> {
pub unsafe fn new(base: usize) -> &'static mut SerialPort<Mmio<u32>> {
unsafe { &mut *(base as *mut Self) }
}
}
impl<T: Io> SerialPort<T>
where
T::Value: From<u8> + TryInto<u8>,
{
pub fn init(&mut self) {
unsafe {
//TODO: Cleanup
// FIXME: Fix UB if unaligned
(*addr_of_mut!(self.int_en)).write(0x00.into());
(*addr_of_mut!(self.line_ctrl)).write(0x80.into());
(*addr_of_mut!(self.data)).write(0x01.into());
(*addr_of_mut!(self.int_en)).write(0x00.into());
(*addr_of_mut!(self.line_ctrl)).write(0x03.into());
(*addr_of_mut!(self.fifo_ctrl)).write(0xC7.into());
(*addr_of_mut!(self.modem_ctrl)).write(0x0B.into());
(*addr_of_mut!(self.int_en)).write(0x01.into());
}
}
fn line_sts(&self) -> LineStsFlags {
LineStsFlags::from_bits_truncate(
(unsafe { &*addr_of!(self.line_sts) }.read() & 0xFF.into())
.try_into()
.unwrap_or(0),
)
}
pub fn receive(&mut self) -> Option<u8> {
if self.line_sts().contains(LineStsFlags::INPUT_FULL) {
Some(
(unsafe { &*addr_of!(self.data) }.read() & 0xFF.into())
.try_into()
.unwrap_or(0),
)
} else {
None
}
}
pub fn send(&mut self, data: u8) {
while !self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY) {}
unsafe { &mut *addr_of_mut!(self.data) }.write(data.into())
}
pub fn write(&mut self, buf: &[u8]) {
for &b in buf {
match b {
8 | 0x7F => {
self.send(8);
self.send(b' ');
self.send(8);
}
b'\n' => {
self.send(b'\r');
self.send(b'\n');
}
_ => {
self.send(b);
}
}
}
}
}
impl<T: Io> fmt::Write for SerialPort<T>
where
T::Value: From<u8> + TryInto<u8>,
{
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
self.write(s.as_bytes());
Ok(())
}
}
+340
View File
@@ -0,0 +1,340 @@
use core::sync::atomic::{AtomicUsize, Ordering};
/// Signal runtime struct for the entire process
#[derive(Debug)]
#[repr(C, align(4096))]
pub struct SigProcControl {
pub pending: AtomicU64,
pub actions: [RawAction; 64],
pub sender_infos: [AtomicU64; 32],
//pub queue: [RealtimeSig; 32], TODO
// qhead, qtail TODO
}
/*#[derive(Debug)]
#[repr(transparent)]
pub struct RealtimeSig {
pub arg: NonatomicUsize,
}*/
#[derive(Debug, Default)]
#[repr(C, align(16))]
pub struct RawAction {
/// Only two MSBs are interesting for the kernel. If bit 63 is set, signal is ignored. If bit
/// 62 is set and the signal is SIGTSTP/SIGTTIN/SIGTTOU, it's equivalent to the action of
/// Stop.
pub first: AtomicU64,
/// Completely ignored by the kernel, but exists so userspace can (when 16-byte atomics exist)
/// atomically set both the handler, sigaction flags, and sigaction mask.
pub user_data: AtomicU64,
}
/// Signal runtime struct for a thread
#[derive(Debug, Default)]
#[repr(C)]
pub struct Sigcontrol {
// composed of [lo "pending" | lo "unmasked", hi "pending" | hi "unmasked"]
pub word: [AtomicU64; 2],
// lo = sender pid, hi = sender ruid
pub sender_infos: [AtomicU64; 32],
pub control_flags: SigatomicUsize,
pub saved_ip: NonatomicUsize, // rip/eip/pc
pub saved_archdep_reg: NonatomicUsize, // rflags(x64)/eflags(x86)/x0(aarch64)/t0(riscv64)
}
#[derive(Clone, Copy, Debug)]
pub struct SenderInfo {
pub pid: u32,
pub ruid: u32,
}
impl SenderInfo {
#[inline]
pub fn raw(self) -> u64 {
u64::from(self.pid) | (u64::from(self.ruid) << 32)
}
#[inline]
pub const fn from_raw(raw: u64) -> Self {
Self {
pid: raw as u32,
ruid: (raw >> 32) as u32,
}
}
}
impl Sigcontrol {
pub fn currently_pending_unblocked(&self, proc: &SigProcControl) -> u64 {
let proc_pending = proc.pending.load(Ordering::Relaxed);
let [w0, w1] = core::array::from_fn(|i| {
let w = self.word[i].load(Ordering::Relaxed);
((w | (proc_pending >> (i * 32))) & 0xffff_ffff) & (w >> 32)
});
//core::sync::atomic::fence(Ordering::Acquire);
w0 | (w1 << 32)
}
pub fn set_allowset(&self, new_allowset: u64) -> u64 {
//core::sync::atomic::fence(Ordering::Release);
let [w0, w1] = self.word.each_ref().map(|w| w.load(Ordering::Relaxed));
let old_a0 = w0 & 0xffff_ffff_0000_0000;
let old_a1 = w1 & 0xffff_ffff_0000_0000;
let new_a0 = (new_allowset & 0xffff_ffff) << 32;
let new_a1 = new_allowset & 0xffff_ffff_0000_0000;
let prev_w0 = self.word[0].fetch_add(new_a0.wrapping_sub(old_a0), Ordering::Relaxed);
let prev_w1 = self.word[0].fetch_add(new_a1.wrapping_sub(old_a1), Ordering::Relaxed);
//core::sync::atomic::fence(Ordering::Acquire);
let up0 = prev_w0 & (prev_w0 >> 32);
let up1 = prev_w1 & (prev_w1 >> 32);
up0 | (up1 << 32)
}
}
#[derive(Debug, Default)]
#[repr(transparent)]
pub struct SigatomicUsize(AtomicUsize);
impl SigatomicUsize {
#[inline]
pub fn load(&self, ordering: Ordering) -> usize {
let value = self.0.load(Ordering::Relaxed);
if ordering != Ordering::Relaxed {
core::sync::atomic::compiler_fence(ordering);
}
value
}
#[inline]
pub fn store(&self, value: usize, ordering: Ordering) {
if ordering != Ordering::Relaxed {
core::sync::atomic::compiler_fence(ordering);
}
self.0.store(value, Ordering::Relaxed);
}
}
#[derive(Debug, Default)]
#[repr(transparent)]
pub struct NonatomicUsize(AtomicUsize);
impl NonatomicUsize {
#[inline]
pub const fn new(a: usize) -> Self {
Self(AtomicUsize::new(a))
}
#[inline]
pub fn get(&self) -> usize {
self.0.load(Ordering::Relaxed)
}
#[inline]
pub fn set(&self, value: usize) {
self.0.store(value, Ordering::Relaxed);
}
}
pub fn sig_bit(sig: usize) -> u64 {
1 << (sig - 1)
}
// TODO: Move to redox_rt?
impl SigProcControl {
/// Checks if `sig` should be ignored based on the current action flags.
///
/// * `sig` - The signal to check (e.g. `SIGCHLD`).
///
/// * `stop_or_continue` - Whether the signal is generated because a child
/// process stopped (`SIGSTOP`, `SIGTSTP`) or continued (`SIGCONT`). If
/// `true` and `sig` is `SIGCHLD`, the signal shall not be delivered if the
/// `SA_NOCLDSTOP` flag is set for `SIGCHLD`.
pub fn signal_will_ign(&self, sig: usize, stop_or_continue: bool) -> bool {
let flags = self.actions[sig - 1].first.load(Ordering::Relaxed);
let will_ign = flags & (1 << 63) != 0;
let sig_specific = flags & (1 << 62) != 0; // SA_NOCLDSTOP if sig == SIGCHLD
will_ign || (sig == SIGCHLD && stop_or_continue && sig_specific)
}
// TODO: Move to redox_rt?
pub fn signal_will_stop(&self, sig: usize) -> bool {
use crate::flag::*;
matches!(sig, SIGTSTP | SIGTTIN | SIGTTOU)
&& self.actions[sig - 1].first.load(Ordering::Relaxed) & (1 << 62) != 0
}
}
#[cfg(not(target_arch = "x86"))]
pub use core::sync::atomic::AtomicU64;
use crate::SIGCHLD;
#[cfg(target_arch = "x86")]
pub use self::atomic::AtomicU64;
#[cfg(target_arch = "x86")]
mod atomic {
use core::{cell::UnsafeCell, sync::atomic::Ordering};
#[derive(Debug, Default)]
pub struct AtomicU64(UnsafeCell<u64>);
unsafe impl Send for AtomicU64 {}
unsafe impl Sync for AtomicU64 {}
impl AtomicU64 {
pub const fn new(inner: u64) -> Self {
Self(UnsafeCell::new(inner))
}
pub fn compare_exchange(
&self,
old: u64,
new: u64,
_success: Ordering,
_failure: Ordering,
) -> Result<u64, u64> {
let old_hi = (old >> 32) as u32;
let old_lo = old as u32;
let new_hi = (new >> 32) as u32;
let new_lo = new as u32;
let mut out_hi;
let mut out_lo;
unsafe {
core::arch::asm!("lock cmpxchg8b [{}]", in(reg) self.0.get(), inout("edx") old_hi => out_hi, inout("eax") old_lo => out_lo, in("ecx") new_hi, in("ebx") new_lo);
}
if old_hi == out_hi && old_lo == out_lo {
Ok(old)
} else {
Err(u64::from(out_lo) | (u64::from(out_hi) << 32))
}
}
pub fn load(&self, ordering: Ordering) -> u64 {
match self.compare_exchange(0, 0, ordering, ordering) {
Ok(new) => new,
Err(new) => new,
}
}
pub fn store(&self, new: u64, ordering: Ordering) {
let mut old = 0;
loop {
match self.compare_exchange(old, new, ordering, Ordering::Relaxed) {
Ok(_) => break,
Err(new) => {
old = new;
core::hint::spin_loop();
}
}
}
}
pub fn fetch_update(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: impl FnMut(u64) -> Option<u64>,
) -> Result<u64, u64> {
let mut old = self.load(fetch_order);
loop {
let new = f(old).ok_or(old)?;
match self.compare_exchange(old, new, set_order, Ordering::Relaxed) {
Ok(_) => return Ok(new),
Err(changed) => {
old = changed;
core::hint::spin_loop();
}
}
}
}
pub fn fetch_or(&self, bits: u64, order: Ordering) -> u64 {
self.fetch_update(order, Ordering::Relaxed, |b| Some(b | bits))
.unwrap()
}
pub fn fetch_and(&self, bits: u64, order: Ordering) -> u64 {
self.fetch_update(order, Ordering::Relaxed, |b| Some(b & bits))
.unwrap()
}
pub fn fetch_add(&self, term: u64, order: Ordering) -> u64 {
self.fetch_update(order, Ordering::Relaxed, |b| Some(b.wrapping_add(term)))
.unwrap()
}
}
}
#[cfg(test)]
mod tests {
use std::sync::{
atomic::{AtomicU64, Ordering},
Arc,
};
#[cfg(not(loom))]
use std::{sync::Mutex, thread};
#[cfg(not(loom))]
fn model(f: impl FnOnce()) {
f()
}
#[cfg(loom)]
use loom::{model, sync::Mutex, thread};
use crate::{RawAction, SigProcControl, Sigcontrol};
struct FakeThread {
ctl: Sigcontrol,
pctl: SigProcControl,
ctxt: Mutex<()>,
}
impl Default for FakeThread {
fn default() -> Self {
Self {
ctl: Sigcontrol::default(),
pctl: SigProcControl {
pending: AtomicU64::new(0),
actions: core::array::from_fn(|_| RawAction::default()),
sender_infos: Default::default(),
},
ctxt: Default::default(),
}
}
}
#[test]
fn singlethread_mask() {
model(|| {
let fake_thread = Arc::new(FakeThread::default());
let thread = {
let fake_thread = Arc::clone(&fake_thread);
thread::spawn(move || {
fake_thread.ctl.set_allowset(!0);
{
let _g = fake_thread.ctxt.lock();
if fake_thread
.ctl
.currently_pending_unblocked(&fake_thread.pctl)
== 0
{
drop(_g);
thread::park();
}
}
})
};
for sig in 1..=64 {
let _g = fake_thread.ctxt.lock();
let idx = sig - 1;
let bit = 1 << (idx % 32);
fake_thread.ctl.word[idx / 32].fetch_or(bit, Ordering::Relaxed);
let w = fake_thread.ctl.word[idx / 32].load(Ordering::Relaxed);
if w & (w >> 32) != 0 {
thread.thread().unpark();
}
}
thread.join().unwrap();
});
}
}
-25
View File
@@ -1,25 +0,0 @@
{
"arch": "aarch64",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"default-hidden-visibility": true,
"emit-debug-gdb-scripts": false,
"exe-suffix": ".efi",
"executables": true,
"is-like-windows": true,
"linker": "rust-lld",
"linker-flavor": "lld-link",
"llvm-target": "aarch64-pc-windows-msvc",
"os": "uefi",
"panic-strategy": "abort",
"pre-link-args": {
"lld-link": [
"/subsystem:EFI_Application",
"/entry:efi_main",
"/machine:arm64"
]
},
"stack_probes": true,
"target-c-int-width": 32,
"target-endian": "little",
"target-pointer-width": 64
}
-25
View File
@@ -1,25 +0,0 @@
{
"arch": "riscv64",
"code-model": "medium",
"cpu": "generic-rv64",
"data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
"emit-debug-gdb-scripts": false,
"exe-suffix": ".elf",
"executables": true,
"linker-flavor": "gnu-cc",
"linker": "riscv64-unknown-redox-gcc",
"llvm-abiname": "lp64d",
"features": "+m,+a,+f,+d,+c",
"llvm-target": "riscv64-unknown-none-elf",
"os": "none",
"metadata": {
"description": null,
"host_tools": null,
"std": null,
"tier": null
},
"panic-strategy": "abort",
"target-c-int-width": 32,
"target-endian": "little",
"target-pointer-width": 64
}
-29
View File
@@ -1,29 +0,0 @@
{
"llvm-target": "i686-unknown-none",
"target-endian": "little",
"target-pointer-width": 32,
"target-c-int-width": 32,
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
"arch": "x86",
"os": "none",
"env": "",
"vendor": "unknown",
"linker-flavor": "gcc",
"panic-strategy": "abort",
"pre-link-args": {
"gcc": ["-m32", "-nostdlib", "-static"]
},
"features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float",
"rustc-abi": "x86-softfloat",
"dynamic-linking": false,
"executables": false,
"relocation-model": "static",
"code-model": "large",
"disable-redzone": true,
"frame-pointer": "always",
"exe-suffix": "",
"has-rpath": false,
"no-default-libraries": true,
"position-independent-executables": false,
"tls-model": "global-dynamic"
}