volatile-0.5.1/.cargo_vcs_info.json0000644000000001360000000000100126560ustar { "git": { "sha1": "0d0912196ed3f12f423a5148bc5382f33d1a7711" }, "path_in_vcs": "" }volatile-0.5.1/.github/workflows/build.yml000064400000000000000000000052611046102023000166710ustar 00000000000000# Based on https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md # # While our "example" application has the platform-specific code, # for simplicity we are compiling and testing everything on the Ubuntu environment only. # For multi-OS testing see the `cross.yml` workflow. on: [push, pull_request] name: Build permissions: contents: read jobs: check: name: Check runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v2 - name: Install stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - name: Run cargo check uses: actions-rs/cargo@v1 with: command: check test: name: Test Suite runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v2 - name: Install stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - name: Run cargo test uses: actions-rs/cargo@v1 with: command: test unstable: name: Test Suite (unstable) runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v2 - name: Install nightly toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: nightly override: true - name: Run cargo test --features unstable uses: actions-rs/cargo@v1 with: command: test args: --features unstable very_unstable: name: Test Suite (very_unstable) runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v2 - name: Install nightly toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: nightly override: true - name: Run cargo test --features very_unstable uses: actions-rs/cargo@v1 with: command: test args: --features very_unstable lints: name: Lints runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v2 - name: Install stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true components: rustfmt, clippy - name: Run cargo fmt uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check - name: Run cargo clippy uses: actions-rs/cargo@v1 with: command: clippy volatile-0.5.1/.gitignore000064400000000000000000000000221046102023000134300ustar 00000000000000target Cargo.lock volatile-0.5.1/Cargo.toml0000644000000023210000000000100106520ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "volatile" version = "0.5.1" authors = ["Philipp Oppermann "] description = "Volatile wrapper types for raw pointers" documentation = "https://docs.rs/volatile" readme = "README.md" keywords = ["volatile"] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-osdev/volatile" [package.metadata.docs.rs] features = ["unstable"] [package.metadata.release] pre-release-commit-message = "Release version {{version}}" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "Changelog.md" replace = """ # Unreleased # {{version}} – {{date}}""" search = "# Unreleased" [dependencies] [dev-dependencies.rand] version = "0.8.3" [features] unstable = [] very_unstable = ["unstable"] volatile-0.5.1/Cargo.toml.orig000064400000000000000000000016111046102023000143340ustar 00000000000000[package] name = "volatile" version = "0.5.1" authors = ["Philipp Oppermann "] license = "MIT OR Apache-2.0" keywords = ["volatile"] description = "Volatile wrapper types for raw pointers" documentation = "https://docs.rs/volatile" repository = "https://github.com/rust-osdev/volatile" edition = "2021" [dependencies] [features] # Enable unstable features; requires Rust nightly; might break on compiler updates unstable = [] # Enable unstable and experimental features; requires Rust nightly; might break on compiler updates very_unstable = ["unstable"] [dev-dependencies] rand = "0.8.3" [package.metadata.release] pre-release-replacements = [ { file = "Changelog.md", search = "# Unreleased", replace = "# Unreleased\n\n# {{version}} – {{date}}", exactly = 1 }, ] pre-release-commit-message = "Release version {{version}}" [package.metadata.docs.rs] features = ["unstable"] volatile-0.5.1/Changelog.md000064400000000000000000000044371046102023000136670ustar 00000000000000# Unreleased # 0.5.1 – 2023-06-24 - Fix: Add missing documentation of the `map` macro # 0.5.0 – 2023-06-24 - **Breaking:** [New design based on raw pointers](https://github.com/rust-osdev/volatile/pull/29) - The previous reference-based design was [unsound](https://github.com/rust-osdev/volatile/pull/13#issuecomment-842455552) because it allowed the compiler to insert spurious reads. - The new design features two wrapper types for raw pointers: `VolatilePtr` and `VolatileRef` - `VolatilePtr` provides safe read and write access to volatile values. Like raw pointers, it implements `Copy` and is `!Sync`. - `VolatileRef` is a pointer type that respects Rust's aliasing rules. It doesn't implement `Copy`, requires a `&mut` reference for modification, and implements `Sync`. It can converted to temporary `VolatilePtr` instances through the `as_ptr`/`as_mut_ptr` methods. - We now provide methods for volatile slice operations and a `map!` macro for struct field projection. These advanced features are gated behind a cargo feature named _"unstable"_. # 0.4.6 – 2023-01-17 - Fix UB in slice methods when Deref returns different references ([#27](https://github.com/rust-osdev/volatile/pull/27)) # 0.4.5 – 2022-04-24 - Remove the `const_generics` feature flag ([#25](https://github.com/rust-osdev/volatile/pull/25)) # 0.4.4 – 2021-03-09 - Replace feature "range_bounds_assert_len" with "slice_range" ([#21](https://github.com/rust-osdev/volatile/pull/21)) - Fixes the `unstable` feature on the latest nightly. # 0.4.3 – 2020-12-23 - Add methods to restrict access ([#19](https://github.com/rust-osdev/volatile/pull/19)) # 0.4.2 – 2020-10-31 - Change `slice::check_range` to `RangeBounds::assert_len` ([#16](https://github.com/rust-osdev/volatile/pull/16)) - Fixes build on latest nightly. # 0.4.1 – 2020-09-21 - Small documentation and metadata improvements # 0.4.0 – 2020-09-21 - **Breaking:** Rewrite crate to operate on reference values ([#13](https://github.com/rust-osdev/volatile/pull/13)) # 0.3.0 – 2020-07-29 - **Breaking:** Remove `Debug` and `Clone` derives for `WriteOnly` ([#12](https://github.com/rust-osdev/volatile/pull/12)) # 0.2.7 – 2020-07-29 - Derive `Default` for `Volatile`, `WriteOnly` and `ReadOnly` ([#10](https://github.com/embed-rs/volatile/pull/10)) volatile-0.5.1/LICENSE-APACHE000064400000000000000000000261351046102023000134010ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. volatile-0.5.1/LICENSE-MIT000064400000000000000000000020621046102023000131020ustar 00000000000000MIT License Copyright (c) 2020 Philipp Oppermann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. volatile-0.5.1/README.md000064400000000000000000000051451046102023000127320ustar 00000000000000# `volatile` [![Build Status](https://github.com/rust-osdev/volatile/workflows/Build/badge.svg)](https://github.com/rust-osdev/volatile/actions?query=workflow%3ABuild) [![Docs.rs Badge](https://docs.rs/volatile/badge.svg)](https://docs.rs/volatile/) Provides volatile wrapper types for raw pointers. The volatile wrapper types in this crate wrap a pointer to any `Copy`-able type and provide volatile memory access to wrapped value. Volatile memory accesses are never optimized away by the compiler, and are useful in many low-level systems programming and concurrent contexts. This crate provides two different wrapper types: `VolatilePtr` and `VolatileRef`. The difference between the two types is that the former behaves like a raw pointer, while the latter behaves like a Rust reference type. For example, `VolatilePtr` can be freely copied, but not sent across threads because this could introduce mutable aliasing. The `VolatileRef` type, on the other hand, requires exclusive access for mutation, so that sharing it across thread boundaries is safe. Both wrapper types *do not* enforce any atomicity guarantees; to also get atomicity, consider looking at the `Atomic` wrapper types found in `libcore` or `libstd`. ## Why is there no `VolatileCell`? Many people expressed interest in a `VolatileCell` type, i.e. a transparent wrapper type that owns the wrapped value. Such a type would be similar to `core::cell::Cell`, with the difference that all methods are volatile. Unfortunately, it is not sound to implement such a `VolatileCell` type in Rust. The reason is that Rust and LLVM consider `&` and `&mut` references as _dereferencable_. This means that the compiler is allowed to freely access the referenced value without any restrictions. So no matter how a `VolatileCell` type is implemented, the compiler is allowed to perform non-volatile read operations of the contained value, which can lead to unexpected (or even undefined?) behavior. For more details, see the discussion [in our repository](https://github.com/rust-osdev/volatile/issues/31) and [in the `unsafe-code-guidelines` repository](https://github.com/rust-lang/unsafe-code-guidelines/issues/411). ## License Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. volatile-0.5.1/SECURITY.md000064400000000000000000000011051046102023000132340ustar 00000000000000# Security Policy If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. Please disclose it at [security advisory](https://github.com/rust-osdev/volatile/security/advisories/new). This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. volatile-0.5.1/src/access.rs000064400000000000000000000043371046102023000140530ustar 00000000000000//! Marker types for limiting access. /// Private trait that is implemented for the types in this module. pub trait Access: Copy + Default { /// Ensures that this trait cannot be implemented outside of this crate. #[doc(hidden)] fn _private() -> _Private { _Private } /// Reduced access level to safely share the corresponding value. type RestrictShared: Access; } /// Helper trait that is implemented by [`ReadWrite`] and [`ReadOnly`]. pub trait Readable: Copy + Default { /// Reduced access level to safely share the corresponding value. type RestrictShared: Readable + Access; /// Ensures that this trait cannot be implemented outside of this crate. fn _private() -> _Private { _Private } } /// Helper trait that is implemented by [`ReadWrite`] and [`WriteOnly`]. pub trait Writable: Access { /// Ensures that this trait cannot be implemented outside of this crate. fn _private() -> _Private { _Private } } /// Implemented for access types that permit copying of `VolatileRef`. pub trait Copyable { /// Ensures that this trait cannot be implemented outside of this crate. fn _private() -> _Private { _Private } } impl Access for T where T: Readable + Default + Copy, { type RestrictShared = ::RestrictShared; } /// Zero-sized marker type for allowing both read and write access. #[derive(Debug, Default, Copy, Clone)] pub struct ReadWrite; impl Readable for ReadWrite { type RestrictShared = ReadOnly; } impl Writable for ReadWrite {} /// Zero-sized marker type for allowing only read access. #[derive(Debug, Default, Copy, Clone)] pub struct ReadOnly; impl Readable for ReadOnly { type RestrictShared = ReadOnly; } impl Copyable for ReadOnly {} /// Zero-sized marker type for allowing only write access. #[derive(Debug, Default, Copy, Clone)] pub struct WriteOnly; impl Access for WriteOnly { type RestrictShared = NoAccess; } impl Writable for WriteOnly {} /// Zero-sized marker type that grants no access. #[derive(Debug, Default, Copy, Clone)] pub struct NoAccess; impl Access for NoAccess { type RestrictShared = NoAccess; } impl Copyable for NoAccess {} #[non_exhaustive] #[doc(hidden)] pub struct _Private; volatile-0.5.1/src/lib.rs000064400000000000000000000050771046102023000133620ustar 00000000000000//! Provides volatile wrapper types for raw pointers. //! //! The volatile wrapper types in this crate wrap a pointer to any [`Copy`]-able //! type and provide volatile memory access to wrapped value. Volatile memory accesses are //! never optimized away by the compiler, and are useful in many low-level systems programming //! and concurrent contexts. //! //! This crate provides two different wrapper types: [`VolatilePtr`] and [`VolatileRef`]. The //! difference between the two types is that the former behaves like a raw pointer, while the //! latter behaves like a Rust reference type. For example, `VolatilePtr` can be freely copied, //! but not sent across threads because this could introduce mutable aliasing. The `VolatileRef` //! type, on the other hand, requires exclusive access for mutation, so that sharing it across //! thread boundaries is safe. //! //! Both wrapper types *do not* enforce any atomicity guarantees; to also get atomicity, consider //! looking at the `Atomic` wrapper types found in `libcore` or `libstd`. //! //! ## Why is there no `VolatileCell`? //! //! Many people expressed interest in a `VolatileCell` type, i.e. a transparent wrapper type that //! owns the wrapped value. Such a type would be similar to [`core::cell::Cell`], with the //! difference that all methods are volatile. //! //! Unfortunately, it is not sound to implement such a `VolatileCell` type in Rust. The reason //! is that Rust and LLVM consider `&` and `&mut` references as _dereferencable_. This means that //! the compiler is allowed to freely access the referenced value without any restrictions. So //! no matter how a `VolatileCell` type is implemented, the compiler is allowed to perform //! non-volatile read operations of the contained value, which can lead to unexpected (or even //! undefined?) behavior. For more details, see the discussion //! [in our repository](https://github.com/rust-osdev/volatile/issues/31) //! and //! [in the `unsafe-code-guidelines` repository](https://github.com/rust-lang/unsafe-code-guidelines/issues/411). #![no_std] #![cfg_attr(feature = "unstable", feature(core_intrinsics))] #![cfg_attr(feature = "unstable", feature(slice_range))] #![cfg_attr(feature = "unstable", feature(slice_ptr_get))] #![cfg_attr(feature = "very_unstable", feature(const_trait_impl))] #![cfg_attr(feature = "very_unstable", feature(unboxed_closures))] #![cfg_attr(feature = "very_unstable", feature(fn_traits))] #![warn(missing_docs)] #![deny(unsafe_op_in_unsafe_fn)] pub use volatile_ptr::VolatilePtr; pub use volatile_ref::VolatileRef; pub mod access; mod volatile_ptr; mod volatile_ref; volatile-0.5.1/src/volatile_ptr/macros.rs000064400000000000000000000032301046102023000165710ustar 00000000000000/// Provides safe field projection for volatile pointers referencing structs. /// /// ## Examples /// /// Accessing a struct field: /// /// ``` /// use volatile::{VolatilePtr, map_field}; /// use core::ptr::NonNull; /// /// struct Example { field_1: u32, field_2: u8, } /// let mut value = Example { field_1: 15, field_2: 255 }; /// let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) }; /// /// // construct a volatile reference to a field /// let field_2 = map_field!(volatile.field_2); /// assert_eq!(field_2.read(), 255); /// ``` /// /// Creating `VolatilePtr`s to unaligned field in packed structs is not allowed: /// ```compile_fail /// use volatile::{VolatilePtr, map_field}; /// use core::ptr::NonNull; /// /// #[repr(packed)] /// struct Example { field_1: u8, field_2: usize, } /// let mut value = Example { field_1: 15, field_2: 255 }; /// let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) }; /// /// // Constructing a volatile reference to an unaligned field doesn't compile. /// let field_2 = map_field!(volatile.field_2); /// ``` #[macro_export] macro_rules! map_field { ($volatile:ident.$place:ident) => {{ // Simulate creating a reference to the field. This is done to make // sure that the field is not potentially unaligned. The body of the // if statement will never be executed, so it can never cause any UB. if false { let _ref_to_field = &(unsafe { &*$volatile.as_raw_ptr().as_ptr() }).$place; } unsafe { $volatile.map(|ptr| { core::ptr::NonNull::new(core::ptr::addr_of_mut!((*ptr.as_ptr()).$place)).unwrap() }) } }}; } volatile-0.5.1/src/volatile_ptr/mod.rs000064400000000000000000000031131046102023000160640ustar 00000000000000use core::{fmt, marker::PhantomData, ptr::NonNull}; use crate::access::ReadWrite; mod macros; mod operations; #[cfg(test)] mod tests; #[cfg(feature = "unstable")] mod unstable; #[cfg(feature = "very_unstable")] mod very_unstable; /// Wraps a pointer to make accesses to the referenced value volatile. /// /// Allows volatile reads and writes on the referenced value. The referenced value needs to /// be `Copy` for reading and writing, as volatile reads and writes take and return copies /// of the value. /// /// Since not all volatile resources (e.g. memory mapped device registers) are both readable /// and writable, this type supports limiting the allowed access types through an optional second /// generic parameter `A` that can be one of `ReadWrite`, `ReadOnly`, or `WriteOnly`. It defaults /// to `ReadWrite`, which allows all operations. /// /// The size of this struct is the same as the size of the contained reference. #[repr(transparent)] pub struct VolatilePtr<'a, T, A = ReadWrite> where T: ?Sized, { pointer: NonNull, reference: PhantomData<&'a T>, access: PhantomData, } impl<'a, T, A> Copy for VolatilePtr<'a, T, A> where T: ?Sized {} impl Clone for VolatilePtr<'_, T, A> where T: ?Sized, { fn clone(&self) -> Self { *self } } impl fmt::Debug for VolatilePtr<'_, T, A> where T: Copy + fmt::Debug + ?Sized, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("VolatilePtr") .field("pointer", &self.pointer) .field("access", &self.access) .finish() } } volatile-0.5.1/src/volatile_ptr/operations.rs000064400000000000000000000200511046102023000174700ustar 00000000000000use core::{ marker::PhantomData, ptr::{self, NonNull}, }; use crate::{ access::{Access, ReadOnly, ReadWrite, Readable, Writable, WriteOnly}, VolatilePtr, }; /// Constructor functions. /// /// These functions construct new `VolatilePtr` values. While the `new` /// function creates a `VolatilePtr` instance with unrestricted access, there /// are also functions for creating read-only or write-only instances. impl<'a, T> VolatilePtr<'a, T> where T: ?Sized, { /// Turns the given pointer into a `VolatilePtr`. /// /// ## Safety /// /// - The given pointer must be valid. /// - No other thread must have access to the given pointer. This must remain true /// for the whole lifetime of the `VolatilePtr`. pub unsafe fn new(pointer: NonNull) -> VolatilePtr<'a, T, ReadWrite> { unsafe { VolatilePtr::new_restricted(ReadWrite, pointer) } } /// Creates a new read-only volatile pointer from the given raw pointer. /// /// ## Safety /// /// The requirements for [`Self::new`] apply to this function too. pub const unsafe fn new_read_only(pointer: NonNull) -> VolatilePtr<'a, T, ReadOnly> { unsafe { Self::new_restricted(ReadOnly, pointer) } } /// Creates a new volatile pointer with restricted access from the given raw pointer. /// /// ## Safety /// /// The requirements for [`Self::new`] apply to this function too. pub const unsafe fn new_restricted(access: A, pointer: NonNull) -> VolatilePtr<'a, T, A> where A: Access, { let _ = access; unsafe { Self::new_generic(pointer) } } pub(super) const unsafe fn new_generic(pointer: NonNull) -> VolatilePtr<'a, T, A> { VolatilePtr { pointer, reference: PhantomData, access: PhantomData, } } } impl<'a, T, A> VolatilePtr<'a, T, A> where T: ?Sized, { /// Performs a volatile read of the contained value. /// /// Returns a copy of the read value. Volatile reads are guaranteed not to be optimized /// away by the compiler, but by themselves do not have atomic ordering /// guarantees. To also get atomicity, consider looking at the `Atomic` wrapper types of /// the standard/`core` library. /// /// ## Examples /// /// ```rust /// use volatile::{VolatilePtr, access}; /// use core::ptr::NonNull; /// /// let value = 42; /// let pointer = unsafe { /// VolatilePtr::new_restricted(access::ReadOnly, NonNull::from(&value)) /// }; /// assert_eq!(pointer.read(), 42); /// ``` pub fn read(self) -> T where T: Copy, A: Readable, { unsafe { ptr::read_volatile(self.pointer.as_ptr()) } } /// Performs a volatile write, setting the contained value to the given `value`. /// /// Volatile writes are guaranteed to not be optimized away by the compiler, but by /// themselves do not have atomic ordering guarantees. To also get atomicity, consider /// looking at the `Atomic` wrapper types of the standard/`core` library. /// /// ## Example /// /// ```rust /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut value = 42; /// let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) }; /// volatile.write(50); /// /// assert_eq!(volatile.read(), 50); /// ``` pub fn write(self, value: T) where T: Copy, A: Writable, { unsafe { ptr::write_volatile(self.pointer.as_ptr(), value) }; } /// Updates the contained value using the given closure and volatile instructions. /// /// Performs a volatile read of the contained value, passes it to the /// function `f`, and then performs a volatile write of the returned value back to /// the target. /// /// ```rust /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut value = 42; /// let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) }; /// volatile.update(|val| val + 1); /// /// assert_eq!(volatile.read(), 43); /// ``` pub fn update(self, f: F) where T: Copy, A: Readable + Writable, F: FnOnce(T) -> T, { let new = f(self.read()); self.write(new); } /// Extracts the wrapped raw pointer. /// /// ## Example /// /// ``` /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut value = 42; /// let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) }; /// volatile.write(50); /// let unwrapped: *mut i32 = volatile.as_raw_ptr().as_ptr(); /// /// assert_eq!(unsafe { *unwrapped }, 50); // non volatile access, be careful! /// ``` pub fn as_raw_ptr(self) -> NonNull { self.pointer } /// Constructs a new `VolatilePtr` by mapping the wrapped pointer. /// /// This method is useful for accessing only a part of a volatile value, e.g. a subslice or /// a struct field. For struct field access, there is also the safe /// [`map_field`][crate::map_field] macro that wraps this function. /// /// ## Examples /// /// Accessing a struct field: /// /// ``` /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// struct Example { field_1: u32, field_2: u8, } /// let mut value = Example { field_1: 15, field_2: 255 }; /// let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) }; /// /// // construct a volatile pointer to a field /// let field_2 = unsafe { volatile.map(|ptr| NonNull::new(core::ptr::addr_of_mut!((*ptr.as_ptr()).field_2)).unwrap()) }; /// assert_eq!(field_2.read(), 255); /// ``` /// /// Don't misuse this method to do a non-volatile read of the referenced value: /// /// ``` /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut value = 5; /// let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) }; /// /// // DON'T DO THIS: /// let mut readout = 0; /// unsafe { volatile.map(|value| { /// readout = *value.as_ptr(); // non-volatile read, might lead to bugs /// value /// })}; /// ``` /// /// ## Safety /// /// The pointer returned by `f` must satisfy the requirements of [`Self::new`]. pub unsafe fn map(self, f: F) -> VolatilePtr<'a, U, A> where F: FnOnce(NonNull) -> NonNull, A: Access, U: ?Sized, { unsafe { VolatilePtr::new_restricted(A::default(), f(self.pointer)) } } } /// Methods for restricting access. impl<'a, T> VolatilePtr<'a, T, ReadWrite> where T: ?Sized, { /// Restricts access permissions to read-only. /// /// ## Example /// /// ``` /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut value: i16 = -4; /// let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) }; /// /// let read_only = volatile.read_only(); /// assert_eq!(read_only.read(), -4); /// // read_only.write(10); // compile-time error /// ``` pub fn read_only(self) -> VolatilePtr<'a, T, ReadOnly> { unsafe { VolatilePtr::new_restricted(ReadOnly, self.pointer) } } /// Restricts access permissions to write-only. /// /// ## Example /// /// Creating a write-only pointer to a struct field: /// /// ``` /// use volatile::{VolatilePtr, map_field}; /// use core::ptr::NonNull; /// /// struct Example { field_1: u32, field_2: u8, } /// let mut value = Example { field_1: 15, field_2: 255 }; /// let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) }; /// /// // construct a volatile write-only pointer to `field_2` /// let mut field_2 = map_field!(volatile.field_2).write_only(); /// field_2.write(14); /// // field_2.read(); // compile-time error /// ``` pub fn write_only(self) -> VolatilePtr<'a, T, WriteOnly> { unsafe { VolatilePtr::new_restricted(WriteOnly, self.pointer) } } } volatile-0.5.1/src/volatile_ptr/tests.rs000064400000000000000000000105611046102023000164540ustar 00000000000000use crate::{ access::{ReadOnly, ReadWrite, WriteOnly}, map_field, VolatilePtr, }; use core::ptr::NonNull; #[test] fn test_read() { let val = 42; assert_eq!( unsafe { VolatilePtr::new_read_only(NonNull::from(&val)) }.read(), 42 ); } #[test] fn test_write() { let mut val = 50; let volatile = unsafe { VolatilePtr::new(NonNull::from(&mut val)) }; volatile.write(50); assert_eq!(val, 50); } #[test] fn test_update() { let mut val = 42; let volatile = unsafe { VolatilePtr::new(NonNull::from(&mut val)) }; volatile.update(|v| v + 1); assert_eq!(val, 43); } #[test] fn test_access() { let mut val: i64 = 42; // ReadWrite assert_eq!( unsafe { VolatilePtr::new_restricted(ReadWrite, NonNull::from(&mut val)) }.read(), 42 ); unsafe { VolatilePtr::new_restricted(ReadWrite, NonNull::from(&mut val)) }.write(50); assert_eq!(val, 50); unsafe { VolatilePtr::new_restricted(ReadWrite, NonNull::from(&mut val)) }.update(|i| i + 1); assert_eq!(val, 51); // ReadOnly and WriteOnly assert_eq!( unsafe { VolatilePtr::new_restricted(ReadOnly, NonNull::from(&mut val)) }.read(), 51 ); unsafe { VolatilePtr::new_restricted(WriteOnly, NonNull::from(&mut val)) }.write(12); assert_eq!(val, 12); } #[test] fn test_struct() { #[derive(Debug, PartialEq)] struct S { field_1: u32, field_2: bool, } let mut val = S { field_1: 60, field_2: true, }; let volatile = unsafe { VolatilePtr::new(NonNull::from(&mut val)) }; unsafe { volatile.map(|s| NonNull::new(core::ptr::addr_of_mut!((*s.as_ptr()).field_1)).unwrap()) } .update(|v| v + 1); let field_2 = unsafe { volatile.map(|s| NonNull::new(core::ptr::addr_of_mut!((*s.as_ptr()).field_2)).unwrap()) }; assert!(field_2.read()); field_2.write(false); assert_eq!( val, S { field_1: 61, field_2: false } ); } #[test] fn test_struct_macro() { #[derive(Debug, PartialEq)] struct S { field_1: u32, field_2: bool, } let mut val = S { field_1: 60, field_2: true, }; let volatile = unsafe { VolatilePtr::new(NonNull::from(&mut val)) }; let field_1 = map_field!(volatile.field_1); field_1.update(|v| v + 1); let field_2 = map_field!(volatile.field_2); assert!(field_2.read()); field_2.write(false); assert_eq!( val, S { field_1: 61, field_2: false } ); } #[cfg(feature = "unstable")] #[test] fn test_slice() { let val: &mut [u32] = &mut [1, 2, 3]; let volatile = unsafe { VolatilePtr::new(NonNull::from(val)) }; volatile.index(0).update(|v| v + 1); let mut dst = [0; 3]; volatile.copy_into_slice(&mut dst); assert_eq!(dst, [2, 2, 3]); } #[cfg(feature = "unstable")] #[test] #[should_panic] fn test_bounds_check_1() { let val: &mut [u32] = &mut [1, 2, 3]; let volatile = unsafe { VolatilePtr::new(NonNull::from(val)) }; volatile.index(3); } #[cfg(feature = "unstable")] #[test] #[should_panic] fn test_bounds_check_2() { let val: &mut [u32] = &mut [1, 2, 3]; let volatile = unsafe { VolatilePtr::new(NonNull::from(val)) }; #[allow(clippy::reversed_empty_ranges)] volatile.index(2..1); } #[cfg(feature = "unstable")] #[test] #[should_panic] fn test_bounds_check_3() { let val: &mut [u32] = &mut [1, 2, 3]; let volatile = unsafe { VolatilePtr::new(NonNull::from(val)) }; volatile.index(4..); // `3..` is is still ok (see next test) } #[cfg(feature = "unstable")] #[test] fn test_bounds_check_4() { let val: &mut [u32] = &mut [1, 2, 3]; let volatile = unsafe { VolatilePtr::new(NonNull::from(val)) }; assert_eq!(volatile.index(3..).len(), 0); } #[cfg(feature = "unstable")] #[test] #[should_panic] fn test_bounds_check_5() { let val: &mut [u32] = &mut [1, 2, 3]; let volatile = unsafe { VolatilePtr::new(NonNull::from(val)) }; volatile.index(..4); } #[cfg(feature = "unstable")] #[test] fn test_chunks() { let val: &mut [u32] = &mut [1, 2, 3, 4, 5, 6]; let volatile = unsafe { VolatilePtr::new(NonNull::from(val)) }; let chunks = volatile.as_chunks().0; chunks.index(1).write([10, 11, 12]); assert_eq!(chunks.index(0).read(), [1, 2, 3]); assert_eq!(chunks.index(1).read(), [10, 11, 12]); } volatile-0.5.1/src/volatile_ptr/unstable.rs000064400000000000000000000324431046102023000171320ustar 00000000000000use core::{ intrinsics, ops::{Range, RangeBounds}, ptr::{self, NonNull}, slice::{range, SliceIndex}, }; use crate::{ access::{Access, Readable, Writable}, VolatilePtr, }; impl<'a, T, A> VolatilePtr<'a, [T], A> { /// Returns the length of the slice. pub fn len(self) -> usize { self.pointer.len() } /// Returns whether the slice is empty. pub fn is_empty(self) -> bool { self.pointer.len() == 0 } /// Applies the index operation on the wrapped slice. /// /// Returns a shared `Volatile` reference to the resulting subslice. /// /// This is a convenience method for the `map(|slice| slice.index(index))` operation, so it /// has the same behavior as the indexing operation on slice (e.g. panic if index is /// out-of-bounds). /// /// ## Examples /// /// Accessing a single slice element: /// /// ``` /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let array = [1, 2, 3]; /// let slice = &array[..]; /// let volatile = unsafe { VolatilePtr::new_read_only(NonNull::from(slice)) }; /// assert_eq!(volatile.index(1).read(), 2); /// ``` /// /// Accessing a subslice: /// /// ``` /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let array = [1, 2, 3]; /// let slice = &array[..]; /// let volatile = unsafe { VolatilePtr::new_read_only(NonNull::from(slice)) }; /// let subslice = volatile.index(1..); /// assert_eq!(subslice.index(0).read(), 2); /// ``` pub fn index(self, index: I) -> VolatilePtr<'a, >::Output, A> where I: SliceIndex<[T]> + SliceIndex<[()]> + Clone, A: Access, { bounds_check(self.pointer.len(), index.clone()); unsafe { self.map(|slice| slice.get_unchecked_mut(index)) } } /// Returns an iterator over the slice. pub fn iter(self) -> impl Iterator> where A: Access, { let ptr = self.as_raw_ptr().as_ptr() as *mut T; let len = self.len(); (0..len) .map(move |i| unsafe { VolatilePtr::new_generic(NonNull::new_unchecked(ptr.add(i))) }) } /// Copies all elements from `self` into `dst`, using a volatile memcpy. /// /// The length of `dst` must be the same as `self`. /// /// The method is only available with the `unstable` feature enabled (requires a nightly /// Rust compiler). /// /// ## Panics /// /// This function will panic if the two slices have different lengths. /// /// ## Examples /// /// Copying two elements from a volatile slice: /// /// ``` /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let src = [1, 2]; /// // the `Volatile` type does not work with arrays, so convert `src` to a slice /// let slice = &src[..]; /// let volatile = unsafe { VolatilePtr::new_read_only(NonNull::from(slice)) }; /// let mut dst = [5, 0, 0]; /// /// // Because the slices have to be the same length, /// // we slice the destination slice from three elements /// // to two. It will panic if we don't do this. /// volatile.copy_into_slice(&mut dst[1..]); /// /// assert_eq!(src, [1, 2]); /// assert_eq!(dst, [5, 1, 2]); /// ``` pub fn copy_into_slice(self, dst: &mut [T]) where T: Copy, A: Readable, { let len = self.pointer.len(); assert_eq!( len, dst.len(), "destination and source slices have different lengths" ); unsafe { intrinsics::volatile_copy_nonoverlapping_memory( dst.as_mut_ptr(), self.pointer.as_mut_ptr(), len, ); } } /// Copies all elements from `src` into `self`, using a volatile memcpy. /// /// The length of `src` must be the same as `self`. /// /// This method is similar to the `slice::copy_from_slice` method of the standard library. The /// difference is that this method performs a volatile copy. /// /// The method is only available with the `unstable` feature enabled (requires a nightly /// Rust compiler). /// /// ## Panics /// /// This function will panic if the two slices have different lengths. /// /// ## Examples /// /// Copying two elements from a slice into a volatile slice: /// /// ``` /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let src = [1, 2, 3, 4]; /// let mut dst = [0, 0]; /// // the `Volatile` type does not work with arrays, so convert `dst` to a slice /// let slice = &mut dst[..]; /// let mut volatile = unsafe { VolatilePtr::new(NonNull::from(slice)) }; /// // Because the slices have to be the same length, /// // we slice the source slice from four elements /// // to two. It will panic if we don't do this. /// volatile.copy_from_slice(&src[2..]); /// /// assert_eq!(src, [1, 2, 3, 4]); /// assert_eq!(dst, [3, 4]); /// ``` pub fn copy_from_slice(self, src: &[T]) where T: Copy, A: Writable, { let len = self.pointer.len(); assert_eq!( len, src.len(), "destination and source slices have different lengths" ); unsafe { intrinsics::volatile_copy_nonoverlapping_memory( self.pointer.as_mut_ptr(), src.as_ptr(), len, ); } } /// Copies elements from one part of the slice to another part of itself, using a /// volatile `memmove`. /// /// `src` is the range within `self` to copy from. `dest` is the starting index of the /// range within `self` to copy to, which will have the same length as `src`. The two ranges /// may overlap. The ends of the two ranges must be less than or equal to `self.len()`. /// /// This method is similar to the `slice::copy_within` method of the standard library. The /// difference is that this method performs a volatile copy. /// /// This method is only available with the `unstable` feature enabled (requires a nightly /// Rust compiler). /// /// ## Panics /// /// This function will panic if either range exceeds the end of the slice, or if the end /// of `src` is before the start. /// /// ## Examples /// /// Copying four bytes within a slice: /// /// ``` /// extern crate core; /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut byte_array = *b"Hello, World!"; /// let mut slice: &mut [u8] = &mut byte_array[..]; /// let mut volatile = unsafe { VolatilePtr::new(NonNull::from(slice)) }; /// volatile.copy_within(1..5, 8); /// /// assert_eq!(&byte_array, b"Hello, Wello!"); pub fn copy_within(self, src: impl RangeBounds, dest: usize) where T: Copy, A: Readable + Writable, { let len = self.pointer.len(); // implementation taken from https://github.com/rust-lang/rust/blob/683d1bcd405727fcc9209f64845bd3b9104878b8/library/core/src/slice/mod.rs#L2726-L2738 let Range { start: src_start, end: src_end, } = range(src, ..len); let count = src_end - src_start; assert!(dest <= len - count, "dest is out of bounds"); // SAFETY: the conditions for `volatile_copy_memory` have all been checked above, // as have those for `ptr::add`. unsafe { intrinsics::volatile_copy_memory( self.pointer.as_mut_ptr().add(dest), self.pointer.as_mut_ptr().add(src_start), count, ); } } /// Divides one slice into two at an index. /// /// The first will contain all indices from `[0, mid)` (excluding /// the index `mid` itself) and the second will contain all /// indices from `[mid, len)` (excluding the index `len` itself). /// /// # Panics /// /// Panics if `mid > len`. /// pub fn split_at(self, mid: usize) -> (VolatilePtr<'a, [T], A>, VolatilePtr<'a, [T], A>) where A: Access, { assert!(mid <= self.pointer.len()); // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which // fulfills the requirements of `from_raw_parts_mut`. unsafe { self.split_at_unchecked(mid) } } unsafe fn split_at_unchecked( self, mid: usize, ) -> (VolatilePtr<'a, [T], A>, VolatilePtr<'a, [T], A>) where A: Access, { // SAFETY: Caller has to check that `0 <= mid <= self.len()` unsafe { ( VolatilePtr::new_generic((self.pointer).get_unchecked_mut(..mid)), VolatilePtr::new_generic((self.pointer).get_unchecked_mut(mid..)), ) } } /// Splits the slice into a slice of `N`-element arrays, /// starting at the beginning of the slice, /// and a remainder slice with length strictly less than `N`. /// /// # Panics /// /// Panics if `N` is 0. #[allow(clippy::type_complexity)] pub fn as_chunks( self, ) -> (VolatilePtr<'a, [[T; N]], A>, VolatilePtr<'a, [T], A>) where A: Access, { assert_ne!(N, 0); let len = self.pointer.len() / N; let (multiple_of_n, remainder) = self.split_at(len * N); // SAFETY: We already panicked for zero, and ensured by construction // that the length of the subslice is a multiple of N. let array_slice = unsafe { multiple_of_n.as_chunks_unchecked() }; (array_slice, remainder) } /// Splits the slice into a slice of `N`-element arrays, /// assuming that there's no remainder. /// /// # Safety /// /// This may only be called when /// - The slice splits exactly into `N`-element chunks (aka `self.len() % N == 0`). /// - `N != 0`. pub unsafe fn as_chunks_unchecked(self) -> VolatilePtr<'a, [[T; N]], A> where A: Access, { debug_assert_ne!(N, 0); debug_assert_eq!(self.pointer.len() % N, 0); let new_len = // SAFETY: Our precondition is exactly what's needed to call this unsafe { core::intrinsics::exact_div(self.pointer.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. let pointer = NonNull::new(ptr::slice_from_raw_parts_mut( self.pointer.as_mut_ptr().cast(), new_len, )) .unwrap(); unsafe { VolatilePtr::new_generic(pointer) } } } /// Methods for volatile byte slices impl VolatilePtr<'_, [u8], A> { /// Sets all elements of the byte slice to the given `value` using a volatile `memset`. /// /// This method is similar to the `slice::fill` method of the standard library, with the /// difference that this method performs a volatile write operation. Another difference /// is that this method is only available for byte slices (not general `&mut [T]` slices) /// because there currently isn't a instrinsic function that allows non-`u8` values. /// /// This method is only available with the `unstable` feature enabled (requires a nightly /// Rust compiler). /// /// ## Example /// /// ```rust /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut vec = vec![0; 10]; /// let mut buf = unsafe { VolatilePtr::new(NonNull::from(vec.as_mut_slice())) }; /// buf.fill(1); /// assert_eq!(unsafe { buf.as_raw_ptr().as_mut() }, &mut vec![1; 10]); /// ``` pub fn fill(self, value: u8) where A: Writable, { unsafe { intrinsics::volatile_set_memory(self.pointer.as_mut_ptr(), value, self.pointer.len()); } } } /// Methods for converting arrays to slices /// /// These methods are only available with the `unstable` feature enabled (requires a nightly /// Rust compiler). impl<'a, T, A, const N: usize> VolatilePtr<'a, [T; N], A> { /// Converts an array pointer to a slice pointer. /// /// This makes it possible to use the methods defined on slices. /// /// ## Example /// /// Copying two elements from a volatile array reference using `copy_into_slice`: /// /// ``` /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let src = [1, 2]; /// let volatile = unsafe { VolatilePtr::new_read_only(NonNull::from(&src)) }; /// let mut dst = [0, 0]; /// /// // convert the `Volatile<&[i32; 2]>` array reference to a `Volatile<&[i32]>` slice /// let volatile_slice = volatile.as_slice(); /// // we can now use the slice methods /// volatile_slice.copy_into_slice(&mut dst); /// /// assert_eq!(dst, [1, 2]); /// ``` pub fn as_slice(self) -> VolatilePtr<'a, [T], A> where A: Access, { unsafe { self.map(|array| { NonNull::new(ptr::slice_from_raw_parts_mut(array.as_ptr() as *mut T, N)).unwrap() }) } } } fn bounds_check(len: usize, index: impl SliceIndex<[()]>) { const MAX_ARRAY: [(); usize::MAX] = [(); usize::MAX]; let bound_check_slice = &MAX_ARRAY[..len]; let _ = &bound_check_slice[index]; } volatile-0.5.1/src/volatile_ptr/very_unstable.rs000064400000000000000000000030021046102023000201640ustar 00000000000000use core::ptr::NonNull; use crate::VolatilePtr; impl<'a, T, A> VolatilePtr<'a, T, A> where T: ?Sized, { /// Compile-time evaluable variant of [`Self::map`]. /// /// This function is a copy of [`Self::map`] that uses unstable compiler functions /// to be callable from `const` contexts. /// /// ## Safety /// /// The safety requirements of [`Self::map`] apply to this method too. pub const unsafe fn map_const(self, f: F) -> VolatilePtr<'a, U, A> where F: ~const FnOnce(NonNull) -> NonNull, U: ?Sized, { unsafe { VolatilePtr::new_generic(f(self.pointer)) } } } /// Methods for volatile slices #[cfg(feature = "unstable")] impl<'a, T, A> VolatilePtr<'a, [T], A> { /// Compile-time evaluable variant of [`Self::index`]. /// /// This function is a copy of [`Self::index`] that uses unstable compiler functions /// to be callable from `const` contexts. pub const fn index_const(self, index: usize) -> VolatilePtr<'a, T, A> { assert!(index < self.pointer.len(), "index out of bounds"); struct Mapper { index: usize, } impl const FnOnce<(NonNull<[T]>,)> for Mapper { type Output = NonNull; extern "rust-call" fn call_once(self, (slice,): (NonNull<[T]>,)) -> Self::Output { unsafe { NonNull::new_unchecked(slice.as_non_null_ptr().as_ptr().add(self.index)) } } } unsafe { self.map_const(Mapper { index }) } } } volatile-0.5.1/src/volatile_ref.rs000064400000000000000000000217621046102023000152660ustar 00000000000000use crate::{ access::{Access, Copyable, ReadOnly, ReadWrite, WriteOnly}, volatile_ptr::VolatilePtr, }; use core::{fmt, marker::PhantomData, ptr::NonNull}; /// Volatile pointer type that respects Rust's aliasing rules. /// /// This pointer type behaves similar to Rust's reference types: /// /// - it requires exclusive `&mut self` access for mutability /// - only read-only types implement [`Clone`] and [`Copy`] /// - [`Send`] and [`Sync`] are implemented if `T: Sync` /// /// To perform volatile operations on `VolatileRef` types, use the [`as_ptr`][Self::as_ptr] /// or [`as_mut_ptr`](Self::as_mut_ptr) methods to create a temporary /// [`VolatilePtr`][crate::VolatilePtr] instance. /// /// Since not all volatile resources (e.g. memory mapped device registers) are both readable /// and writable, this type supports limiting the allowed access types through an optional second /// generic parameter `A` that can be one of `ReadWrite`, `ReadOnly`, or `WriteOnly`. It defaults /// to `ReadWrite`, which allows all operations. /// /// The size of this struct is the same as the size of the contained reference. #[repr(transparent)] pub struct VolatileRef<'a, T, A = ReadWrite> where T: ?Sized, { pointer: NonNull, reference: PhantomData<&'a T>, access: PhantomData, } /// Constructor functions. /// /// These functions construct new `VolatileRef` values. While the `new` /// function creates a `VolatileRef` instance with unrestricted access, there /// are also functions for creating read-only or write-only instances. impl<'a, T> VolatileRef<'a, T> where T: ?Sized, { /// Turns the given pointer into a `VolatileRef`. /// /// ## Safety /// /// - The pointer must be properly aligned. /// - It must be “dereferenceable” in the sense defined in the [`core::ptr`] documentation. /// - The pointer must point to an initialized instance of T. /// - You must enforce Rust’s aliasing rules, since the returned lifetime 'a is arbitrarily /// chosen and does not necessarily reflect the actual lifetime of the data. In particular, /// while this `VolatileRef` exists, the memory the pointer points to must not get accessed /// (_read or written_) through any other pointer. pub unsafe fn new(pointer: NonNull) -> Self { unsafe { VolatileRef::new_restricted(ReadWrite, pointer) } } /// Turns the given pointer into a read-only `VolatileRef`. /// /// ## Safety /// /// - The pointer must be properly aligned. /// - It must be “dereferenceable” in the sense defined in the [`core::ptr`] documentation. /// - The pointer must point to an initialized instance of T. /// - You must enforce Rust’s aliasing rules, since the returned lifetime 'a is arbitrarily /// chosen and does not necessarily reflect the actual lifetime of the data. In particular, /// while this `VolatileRef` exists, the memory the pointer points to _must not get mutated_. pub const unsafe fn new_read_only(pointer: NonNull) -> VolatileRef<'a, T, ReadOnly> { unsafe { Self::new_restricted(ReadOnly, pointer) } } /// Turns the given pointer into a `VolatileRef` instance with the given access. /// /// ## Safety /// /// - The pointer must be properly aligned. /// - It must be “dereferenceable” in the sense defined in the [`core::ptr`] documentation. /// - The pointer must point to an initialized instance of T. /// - You must enforce Rust’s aliasing rules, since the returned lifetime 'a is arbitrarily /// chosen and does not necessarily reflect the actual lifetime of the data. In particular, /// while this `VolatileRef` exists, the memory the pointer points to _must not get mutated_. /// If the given `access` parameter allows write access, the pointer _must not get read /// either_ while this `VolatileRef` exists. pub const unsafe fn new_restricted(access: A, pointer: NonNull) -> VolatileRef<'a, T, A> where A: Access, { let _ = access; unsafe { Self::new_generic(pointer) } } /// Creates a `VolatileRef` from the given shared reference. /// /// **Note:** This function is only intended for testing, not for accessing real volatile /// data. The reason is that the `&mut T` argument is considered _dereferenceable_ by Rust, /// so the compiler is allowed to insert non-volatile reads. This might lead to undesired /// (or even undefined?) behavior when accessing volatile data. So to be safe, only create /// raw pointers to volatile data and use the [`Self::new`] constructor instead. pub fn from_ref(reference: &'a T) -> VolatileRef<'a, T, ReadOnly> where T: 'a, { unsafe { VolatileRef::new_restricted(ReadOnly, reference.into()) } } /// Creates a `VolatileRef` from the given mutable reference. /// /// **Note:** This function is only intended for testing, not for accessing real volatile /// data. The reason is that the `&mut T` argument is considered _dereferenceable_ by Rust, /// so the compiler is allowed to insert non-volatile reads. This might lead to undesired /// (or even undefined?) behavior when accessing volatile data. So to be safe, only create /// raw pointers to volatile data and use the [`Self::new`] constructor instead. pub fn from_mut_ref(reference: &'a mut T) -> Self where T: 'a, { unsafe { VolatileRef::new(reference.into()) } } const unsafe fn new_generic(pointer: NonNull) -> VolatileRef<'a, T, A> { VolatileRef { pointer, reference: PhantomData, access: PhantomData, } } } impl<'a, T, A> VolatileRef<'a, T, A> where T: ?Sized, { /// Borrows this `VolatileRef` as a read-only [`VolatilePtr`]. /// /// Use this method to do (partial) volatile reads of the referenced data. pub fn as_ptr(&self) -> VolatilePtr<'_, T, A::RestrictShared> where A: Access, { unsafe { VolatilePtr::new_restricted(Default::default(), self.pointer) } } /// Borrows this `VolatileRef` as a mutable [`VolatilePtr`]. /// /// Use this method to do (partial) volatile reads or writes of the referenced data. pub fn as_mut_ptr(&mut self) -> VolatilePtr<'_, T, A> where A: Access, { unsafe { VolatilePtr::new_restricted(Default::default(), self.pointer) } } /// Converts this `VolatileRef` into a [`VolatilePtr`] with full access without shortening /// the lifetime. /// /// Use this method when you need a [`VolatilePtr`] instance that lives for the full /// lifetime `'a`. /// /// This method consumes the `VolatileRef`. pub fn into_ptr(self) -> VolatilePtr<'a, T, A> where A: Access, { unsafe { VolatilePtr::new_restricted(Default::default(), self.pointer) } } } /// Methods for restricting access. impl<'a, T> VolatileRef<'a, T, ReadWrite> where T: ?Sized, { /// Restricts access permissions to read-only. /// /// ## Example /// /// ``` /// use volatile::VolatileRef; /// use core::ptr::NonNull; /// /// let mut value: i16 = -4; /// let mut volatile = VolatileRef::from_mut_ref(&mut value); /// /// let read_only = volatile.read_only(); /// assert_eq!(read_only.as_ptr().read(), -4); /// // read_only.as_ptr().write(10); // compile-time error /// ``` pub fn read_only(self) -> VolatileRef<'a, T, ReadOnly> { unsafe { VolatileRef::new_restricted(ReadOnly, self.pointer) } } /// Restricts access permissions to write-only. /// /// ## Example /// /// Creating a write-only reference to a struct field: /// /// ``` /// use volatile::{VolatileRef}; /// use core::ptr::NonNull; /// /// #[derive(Clone, Copy)] /// struct Example { field_1: u32, field_2: u8, } /// let mut value = Example { field_1: 15, field_2: 255 }; /// let mut volatile = VolatileRef::from_mut_ref(&mut value); /// /// let write_only = volatile.write_only(); /// // write_only.as_ptr().read(); // compile-time error /// ``` pub fn write_only(self) -> VolatileRef<'a, T, WriteOnly> { unsafe { VolatileRef::new_restricted(WriteOnly, self.pointer) } } } impl<'a, T, A> Clone for VolatileRef<'a, T, A> where T: ?Sized, A: Access + Copyable, { fn clone(&self) -> Self { Self { pointer: self.pointer, reference: self.reference, access: self.access, } } } impl<'a, T, A> Copy for VolatileRef<'a, T, A> where T: ?Sized, A: Access + Copyable, { } unsafe impl Send for VolatileRef<'_, T, A> where T: Sync {} unsafe impl Sync for VolatileRef<'_, T, A> where T: Sync {} impl fmt::Debug for VolatileRef<'_, T, A> where T: Copy + fmt::Debug + ?Sized, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("VolatileRef") .field("pointer", &self.pointer) .field("access", &self.access) .finish() } }