vm-superio-0.7.0/.cargo_vcs_info.json0000644000000001570000000000100131510ustar { "git": { "sha1": "c14e40d3d129c8ba3c8fcab52113202d3f9d3da1" }, "path_in_vcs": "crates/vm-superio" }vm-superio-0.7.0/CHANGELOG.md000064400000000000000000000065010072674642500136010ustar 00000000000000# Changelog # v0.7.0 ## Changed - Update vmm-sys-utili dependency to 0.11.0 - Switched to specifying dependencies using caret requirements instead of comparision requirements # v0.6.0 ## Added - Added `SerialState`, and support for saving and restoring the state of the `Serial` device ([#73](https://github.com/rust-vmm/vm-superio/pull/73)). - Added the `Debug` derive to the exported structures ([#75](https://github.com/rust-vmm/vm-superio/pull/75)). ## Fixed - Fixed `enqueue_raw_bytes` for the corner case when 0 bytes were sent, and the fifo was full ([#77](https://github.com/rust-vmm/vm-superio/pull/77)). # v0.5.0 ## Added - Added `RtcState`, and support for saving and restoring the state of the `Rtc` device. This support is useful for snapshot use cases, such as live migration ([#65](https://github.com/rust-vmm/vm-superio/pull/65)). ## Fixed - Fixed potential overflow in the `Rtc` implementation caused by an invalid offset ([#65](https://github.com/rust-vmm/vm-superio/pull/65)). # v0.4.0 ## Added - Added `in_buffer_empty` to SerialEvents trait. This helps with handling the registration of I/O events related to the serial input ([#63](https://github.com/rust-vmm/vm-superio/pull/63)). ## Changed - Changed `RTC` to `Rtc` and `RTCEvents` to `RtcEvents` as part of the Rust version update to 1.52.1 ([#57](https://github.com/rust-vmm/vm-superio/pull/57)). # v0.3.0 ## Fixed - Fixed implementation of Data Register (DR) which caused the guest time to be in the year 1970 ([#47](https://github.com/rust-vmm/vm-superio/issues/47)). # v0.2.0 ## Added - Added emulation support for an i8042 controller that only handles the CPU reset ([#11](https://github.com/rust-vmm/vm-superio/pull/11)). - Added `SerialEvents` trait, which can be implemented by a backend that wants to keep track of serial events using metrics, logs etc ([#5](https://github.com/rust-vmm/vm-superio/issues/5)). - Added a threat model to the serial console documentation ([#16](https://github.com/rust-vmm/vm-superio/issues/16)). - Added emulation support for an ARM PL031 Real Time Clock ([#22](https://github.com/rust-vmm/vm-superio/issues/22)), and the `RTCEvents` trait, used for keeping track of RTC events ([#34](https://github.com/rust-vmm/vm-superio/issues/34)). - Added an implementation for `Arc` for both serial console and RTC device ([#40](https://github.com/rust-vmm/vm-superio/pull/40)). - Added methods for retrieving a reference to the events object for both serial console and RTC device ([#40](https://github.com/rust-vmm/vm-superio/pull/40)). ## Changed - Changed the notification mechanism from EventFd to the Trigger abstraction for both serial console and i8042 ([#7](https://github.com/rust-vmm/vm-superio/issues/7)). ## Fixed - Limited the maximum number of bytes allowed at a time, when enqueuing input for serial, to 64 (FIFO_SIZE) to avoid memory pressure ([#17](https://github.com/rust-vmm/vm-superio/issues/17)). - Fixed possible indefinite blocking of the serial driver by always sending the THR Empty interrupt to it when trying to write to the device ([#23](https://github.com/rust-vmm/vm-superio/issues/23)). # v0.1.0 This is the first `vm-superio` release. The `vm-superio` crate provides emulation for legacy devices. For now, it offers this support only for the Linux serial console. vm-superio-0.7.0/Cargo.toml0000644000000015770000000000100111560ustar # 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 = "2018" name = "vm-superio" version = "0.7.0" authors = ["rust-vmm AWS maintainers "] description = "Emulation for legacy devices" readme = "README.md" keywords = ["superio", "emulation"] license = "Apache-2.0 OR BSD-3-Clause" repository = "https://github.com/rust-vmm/vm-superio" [dev-dependencies.libc] version = "0.2.39" [dev-dependencies.vmm-sys-util] version = "0.11.0" vm-superio-0.7.0/Cargo.toml.orig000064400000000000000000000006110072674642500146530ustar 00000000000000[package] name = "vm-superio" version = "0.7.0" description = "Emulation for legacy devices" keywords = ["superio", "emulation"] repository = "https://github.com/rust-vmm/vm-superio" readme = "README.md" authors = ["rust-vmm AWS maintainers "] license = "Apache-2.0 OR BSD-3-Clause" edition = "2018" [dev-dependencies] libc = "0.2.39" vmm-sys-util = "0.11.0" vm-superio-0.7.0/LICENSE-APACHE000064400000000000000000000261360072674642500137220ustar 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. vm-superio-0.7.0/LICENSE-BSD-3-Clause000064400000000000000000000030320072674642500147510ustar 00000000000000// Copyright 2017 The Chromium OS Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. vm-superio-0.7.0/README.md000064400000000000000000000173270072674642500132570ustar 00000000000000# vm-superio `vm-superio` provides emulation for legacy devices. For now, it offers this support only for the [Linux serial console](https://en.wikipedia.org/wiki/Linux_console), a minimal [i8042 PS/2 Controller](https://wiki.osdev.org/%228042%22_PS/2_Controller) and an [ARM PL031 Real Time Clock](https://developer.arm.com/documentation/ddi0224/c/Programmers-model). To enable snapshot use cases, such as live migration, it also provides support for saving and restoring the state, and for persisting it. In order to achieve this, and to keep a clear separation of concerns, `vm-superio` is a [workspace](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html), containing the following crates: - `vm-superio` - which keeps the state of the component; - `vm-superio-ser` - which mirrors the state structure from `vm-superio` and adds the required version constraints on it, and derives/implements the required (de)serialization traits (i.e. `serde`'s `Serialize` and `Deserialize`; `Versionize`). ## Serial Console ### Design The console emulation is done by emulating a simple [UART 16550A serial port](https://en.wikipedia.org/wiki/16550_UART) with a 64-byte FIFO. This UART is an improvement of the original [UART 8250 serial port](https://en.wikibooks.org/w/index.php?title=Serial_Programming/8250_UART_Programming§ion=15#Serial_COM_Port_Memory_and_I/O_Allocation), mostly because of the FIFO buffers that allow storing more than one byte at a time, which, in virtualized environments, is essential. For a VMM to be able to use this device, besides the emulation part which is covered in this crate, the VMM needs to do the following operations: - add the serial port to the Bus (either PIO or MMIO) - define the serial backend - event handling (optional) The following UART registers are emulated via the [`Serial` structure](crates/vm-superio/src/serial.rs): DLL, IER, DLH, IIR, LCR, LSR, MCR, MSR and SR (a brief, but nice presentation about these, [here](https://www.lammertbies.nl/comm/info/serial-uart#regs)). The Fifo Control Register (FCR) is not emulated; there is no support yet for directly controlling the FIFOs (which, in this implementation, are always enabled). The serial console implements only the RX FIFO (and its corresponding RBR register). The RX buffer helps in testing the UART when running in loopback mode and for sending more bytes to the guest in one shot. The TX FIFO is trivially implemented by immediately writing a byte coming from the driver to an `io::Write` object (`out`), which can be, for example, `io::Stdout` or `io::Sink`. This object has to be provided when [initializing the serial console](https://docs.rs/vm-superio/0.1.1/vm_superio/serial/struct.Serial.html#method.new). A `Trigger` object is the currently used mechanism for notifying the driver about in/out events that need to be handled. ## Threat model Trusted actors: * host kernel Untrusted actors: * guest kernel * guest drivers The untrusted actors can change the state of the device through reads and writes on the Bus at the address where the device resides. |#NR |Threat |Mitigation | |--- |--- |--- | |1 | A malicious guest generates large memory allocations by flooding the serial console input. [CVE-2020-27173](https://nvd.nist.gov/vuln/detail/CVE-2020-27173) |The serial console limits the number of elements in the FIFO corresponding to the serial console input to `FIFO_SIZE` (=64), returning a FIFO Full error when the limit is reached. This error MUST be handled by the crate customer. When the serial console input is connected to an event loop, the customer MUST ensure that the loop is not flooded with events coming from untrusted sources when no space is available in the FIFO. | |2 |A malicious guest can fill up the host disk by generating a high amount of data to be written to the serial output. |Mitigation is not possible at the emulation layer because we are not controlling the output (`Writer`). This needs to be mitigated at the VMM level by adding a rate limiting mechanism. We recommend using as output a resource that has a fixed size (e.g. ring buffer or a named pipe). | ### Usage The interaction between the serial console and its driver, at the emulation level, is done by the two `read` and `write` specific methods, which handle one byte accesses. For sending more input, `enqueue_raw_bytes` can be used. ## i8042 PS/2 Controller The i8042 PS/2 controller emulates, at this point, only the [CPU reset command](https://wiki.osdev.org/%228042%22_PS/2_Controller#CPU_Reset) which is needed for announcing the VMM about the guest's shutdown. ## ARM PL031 Real Time Clock This module emulates the ARM PrimeCell Real Time Clock (RTC) [PL031](https://developer.arm.com/documentation/ddi0224/c/Functional-overview/RTC-operation/RTC-operation). The PL031 provides a long time base counter with a 1HZ counter signal and a configurable offset. This implementation emulates all control, peripheral ID, and PrimeCell ID registers; however, the interrupt based on the value of the Match Register (RTCMR) is not currently implemented (i.e., setting the Match Register has no effect). For a VMM to be able to use this device, the VMM needs to do the following: - add the RTC to the Bus (either PIO or MMIO) - provide a structure that implements RTCEvents to track the occurrence of significant events (optional) Note that because the Match Register is the only possible source of an event, and the Match Register is not currently implemented, no event handling is required. ### Threat model Trusted actors: * host kernel Untrusted actors: * guest kernel * guest drivers The untrusted actors can change the state of the device through reads and writes on the Bus at the address where the device resides. |#NR |Threat |Mitigation | |--- |--- |--- | |1 |A malicious guest writes invalid values in the Load Register to cause overflows on subsequent reads of the Data Register. |The arithmetic operations in the RTC are checked for overflows. When such a situation occurs, the state of the device is reset. | |2 |A malicious guest performs reads and writes from invalid offsets (that do not correspond to the RTC registers) to cause crashes or to get access to data. |Reads and writes of invalid offsets are denied by the emulation, and an `invalid_read/write` event is called. These events can be implemented by VMMs, and extend them to generate alarms (and for example stop the execution of the malicious guest). | ## Save/restore state support This support is offered for the `Rtc` and the `Serial` devices by the following abstractions: - `State` -> which keeps the hardware state of the ``; - `StateSer` -> which can be used by customers who need a `State` that is also `(De)Serialize` and/or `Versionize`. If the customers want a different state than the upstream one, then they can implement `From` (or similar mechanisms) in their products to convert the upstream state to the desired product state. A detailed design document for the save/restore state support in rust-vmm can be found [here](https://github.com/rust-vmm/community/pull/118/files). ### Compatibility between `vm-superio` and `vm-superio-ser` versions Each time there's a change in a state from `vm-superio`, that change needs to be propagated in the corresponding state from `vm-superio-ser`. To keep the compatibility between the release versions of these two crates, once we have a new release of `vm-superio` that implied a change in `vm-superio-ser`, we need to have a new release of `vm-superio-ser` as well. Therefore, the `vm-superio-ser` crate has an exact version of `vm-superio` as dependency. ## License This project is licensed under either of - [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0 - [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause) vm-superio-0.7.0/src/i8042.rs000064400000000000000000000127670072674642500137060ustar 00000000000000// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. //! Provides emulation for a super minimal i8042 controller. //! //! This emulates just the CPU reset command. use std::result::Result; use crate::Trigger; // Offset of the command register, for write accesses (port 0x64). The same // offset can be used, in case of read operations, to access the status // register (in which we are not interested for an i8042 that only knows // about reset). const COMMAND_OFFSET: u8 = 4; // Reset CPU command. const CMD_RESET_CPU: u8 = 0xFE; /// An i8042 PS/2 controller that emulates just enough to shutdown the machine. /// /// A [`Trigger`](../trait.Trigger.html) object is used for notifying the VMM /// about the CPU reset event. /// /// # Example /// /// ```rust /// # use std::io::{Error, Result}; /// # use std::ops::Deref; /// # use vm_superio::Trigger; /// # use vm_superio::I8042Device; /// # use vmm_sys_util::eventfd::EventFd; /// /// struct EventFdTrigger(EventFd); /// impl Trigger for EventFdTrigger { /// type E = Error; /// /// fn trigger(&self) -> Result<()> { /// self.write(1) /// } /// } /// impl Deref for EventFdTrigger { /// type Target = EventFd; /// fn deref(&self) -> &Self::Target { /// &self.0 /// } /// } /// impl EventFdTrigger { /// pub fn new(flag: i32) -> Self { /// EventFdTrigger(EventFd::new(flag).unwrap()) /// } /// } /// /// let reset_evt = EventFdTrigger::new(libc::EFD_NONBLOCK); /// let mut i8042 = I8042Device::new(reset_evt); /// /// // Check read/write operations. /// assert_eq!(i8042.read(0), 0); /// i8042.write(4, 0xFE).unwrap(); /// ``` #[derive(Debug)] pub struct I8042Device { /// CPU reset event object. We will trigger this event when the guest issues /// the reset CPU command. reset_evt: T, } impl I8042Device { /// Constructs an i8042 device that will signal the given event when the /// guest requests it. /// /// # Arguments /// * `reset_evt` - A Trigger object that will be used to notify the driver /// about the reset event. /// /// # Example /// /// You can see an example of how to use this function in the /// [`Example` section from `I8042Device`](struct.I8042Device.html#example). pub fn new(reset_evt: T) -> I8042Device { I8042Device { reset_evt } } /// Handles a read request from the driver at `_offset` offset from the /// base I/O address. /// /// Returns the read value, which at this moment is 0x00, since we're not /// interested in an i8042 operation other than CPU reset. /// /// # Arguments /// * `_offset` - The offset that will be added to the base address /// for writing to a specific register. /// /// # Example /// /// You can see an example of how to use this function in the /// [`Example` section from `I8042Device`](struct.I8042Device.html#example). pub fn read(&mut self, _offset: u8) -> u8 { 0x00 } /// Handles a write request from the driver at `offset` offset from the /// base I/O address. /// /// # Arguments /// * `offset` - The offset that will be added to the base address /// for writing to a specific register. /// * `value` - The byte that should be written. /// /// # Example /// /// You can see an example of how to use this function in the /// [`Example` section from `I8042Device`](struct.I8042Device.html#example). pub fn write(&mut self, offset: u8, value: u8) -> Result<(), T::E> { match offset { COMMAND_OFFSET if value == CMD_RESET_CPU => { // Trigger the exit event. self.reset_evt.trigger() } _ => Ok(()), } } } #[cfg(test)] mod tests { use super::*; use vmm_sys_util::eventfd::EventFd; #[test] fn test_i8042_valid_ops() { let reset_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut i8042 = I8042Device::new(reset_evt.try_clone().unwrap()); assert_eq!(i8042.read(0), 0); // Check if reset works. i8042.write(COMMAND_OFFSET, CMD_RESET_CPU).unwrap(); assert_eq!(reset_evt.read().unwrap(), 1); } #[test] fn test_i8042_invalid_reset() { let reset_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut i8042 = I8042Device::new(reset_evt.try_clone().unwrap()); // Write something different than CPU reset and check that the reset event // was not triggered. For this we have to write 1 to the reset event fd, so // that read doesn't block. assert!(reset_evt.write(1).is_ok()); i8042.write(COMMAND_OFFSET, CMD_RESET_CPU + 1).unwrap(); assert_eq!(reset_evt.read().unwrap(), 1); // Write the CPU reset to a different offset than COMMAND_OFFSET and check that // the reset event was not triggered. // For such accesses we should increment some metric (COMMAND_OFFSET + 1 is not // even a valid i8042 offset), see // [tracking issue](https://github.com/rust-vmm/vm-superio/issues/13). assert!(reset_evt.write(1).is_ok()); i8042.write(COMMAND_OFFSET + 1, CMD_RESET_CPU).unwrap(); assert_eq!(reset_evt.read().unwrap(), 1); } } vm-superio-0.7.0/src/lib.rs000064400000000000000000000042230072674642500136720ustar 00000000000000// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. // // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause //! Emulation for legacy devices. //! //! For now, it offers emulation support only for the Linux serial console, //! an Arm PL031 Real Time Clock (RTC), and an i8042 PS/2 controller that only //! handles the CPU reset. //! //! It also provides a [Trigger](trait.Trigger.html) interface for an object //! that can generate an event. #![deny(missing_docs)] pub mod i8042; pub mod rtc_pl031; pub mod serial; pub use i8042::I8042Device; pub use rtc_pl031::{Rtc, RtcState}; pub use serial::{Serial, SerialState}; use std::result::Result; /// Abstraction for a simple, push-button like interrupt mechanism. /// This helps in abstracting away how events/interrupts are generated when /// working with the emulated devices. /// /// The user has to provide a `Trigger` object to the device's constructor when /// initializing that device. The generic type `T: Trigger` is used in the /// device's structure definition to mark the fact that the events notification /// mechanism is done via the Trigger interface. /// An example of how to implement the `Trigger` interface for /// [an eventfd wrapper](https://docs.rs/vmm-sys-util/latest/vmm_sys_util/eventfd/index.html) /// can be found in the /// [`Example` section from `Serial`](../vm_superio/serial/struct.Serial.html#example). /// The `EventFd` is wrapped in the `EventFdTrigger` newtype because Rust /// doesn't allow implementing an external trait on external types. To get /// around this restriction, the newtype pattern can be used. More details /// about this, /// [here](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types). pub trait Trigger { /// Underlying type for the potential error conditions returned by `Self::trigger`. type E: std::fmt::Debug; /// Trigger an event. fn trigger(&self) -> Result<(), Self::E>; } vm-superio-0.7.0/src/rtc_pl031.rs000064400000000000000000001064550072674642500146450ustar 00000000000000// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause //! Provides emulation for a minimal ARM PL031 Real Time Clock. //! //! This module implements a PL031 Real Time Clock (RTC) that provides a long //! time base counter. This is achieved by generating an interrupt signal after //! counting for a programmed number of cycles of a real-time clock input. use std::convert::TryFrom; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; // The following defines are mapping to the specification: // https://developer.arm.com/documentation/ddi0224/c/Programmers-model/Summary-of-RTC-registers // // From 0x0 to 0x1C we have following registers: const RTCDR: u16 = 0x000; // Data Register (RO). const RTCMR: u16 = 0x004; // Match Register. const RTCLR: u16 = 0x008; // Load Register. const RTCCR: u16 = 0x00C; // Control Register. const RTCIMSC: u16 = 0x010; // Interrupt Mask Set or Clear Register. const RTCRIS: u16 = 0x014; // Raw Interrupt Status (RO). const RTCMIS: u16 = 0x018; // Masked Interrupt Status (RO). const RTCICR: u16 = 0x01C; // Interrupt Clear Register (WO). // From 0x020 to 0xFDC => reserved space. // From 0xFE0 to 0xFFF => Peripheral and PrimeCell Identification Registers // These are read-only registers, so we store their values in a constant array. // The values are found in the 'Reset value' column of Table 3.1 (Summary of // RTC registers) in the the reference manual linked above. const AMBA_IDS: [u8; 8] = [0x31, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1]; // Since we are specifying the AMBA IDs in an array, instead of in individual // registers, these constants bound the register addresses where these IDs // would normally be located. const AMBA_ID_LOW: u16 = 0xFE0; const AMBA_ID_HIGH: u16 = 0xFFF; /// Defines a series of callbacks that are invoked in response to the occurrence of specific /// failure or missed events as part of the RTC operation (e.g., write to an invalid offset). The /// methods below can be implemented by a backend that keeps track of such events by incrementing /// metrics, logging messages, or any other action. /// /// We're using a trait to avoid constraining the concrete characteristics of the backend in /// any way, enabling zero-cost abstractions and use case-specific implementations. pub trait RtcEvents { /// The driver attempts to read from an invalid offset. fn invalid_read(&self); /// The driver attempts to write to an invalid offset. fn invalid_write(&self); } /// Provides a no-op implementation of `RtcEvents` which can be used in situations that /// do not require logging or otherwise doing anything in response to the events defined /// as part of `RtcEvents`. #[derive(Debug)] pub struct NoEvents; impl RtcEvents for NoEvents { fn invalid_read(&self) {} fn invalid_write(&self) {} } impl RtcEvents for Arc { fn invalid_read(&self) { self.as_ref().invalid_read(); } fn invalid_write(&self) { self.as_ref().invalid_write(); } } /// A PL031 Real Time Clock (RTC) that emulates a long time base counter. /// /// This structure emulates the registers for the RTC. /// /// # Example /// /// ```rust /// # use std::thread; /// # use std::io::Error; /// # use std::ops::Deref; /// # use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH}; /// # use vm_superio::Rtc; /// /// let mut data = [0; 4]; /// let mut rtc = Rtc::new(); /// const RTCDR: u16 = 0x0; // Data Register. /// const RTCLR: u16 = 0x8; // Load Register. /// /// // Write system time since UNIX_EPOCH in seconds to the load register. /// let v = SystemTime::now() /// .duration_since(UNIX_EPOCH) /// .unwrap() /// .as_secs() as u32; /// data = v.to_le_bytes(); /// rtc.write(RTCLR, &data); /// /// // Read the value back out of the load register. /// rtc.read(RTCLR, &mut data); /// assert_eq!(v, u32::from_le_bytes(data)); /// /// // Sleep for 1.5 seconds to let the counter tick. /// let delay = Duration::from_millis(1500); /// thread::sleep(delay); /// /// // Read the current RTC value from the Data Register. /// rtc.read(RTCDR, &mut data); /// assert!(u32::from_le_bytes(data) > v); /// ``` #[derive(Debug)] pub struct Rtc { // The load register. lr: u32, // The offset applied to the counter to get the RTC value. offset: i64, // The MR register is used for implementing the RTC alarm. A // real time clock alarm is a feature that can be used to allow // a computer to 'wake up' after shut down to execute tasks // every day or on a certain day. It can sometimes be found in // the 'Power Management' section of a motherboard's BIOS setup. // This is not currently implemented, so we raise an error. // TODO: Implement the match register functionality. mr: u32, // The interrupt mask. imsc: u32, // The raw interrupt value. ris: u32, // Used for tracking the occurrence of significant events. events: EV, } /// The state of the Rtc device. #[derive(Clone, Debug, Eq, PartialEq)] pub struct RtcState { /// The load register. pub lr: u32, /// The offset applied to the counter to get the RTC value. pub offset: i64, /// The MR register. pub mr: u32, /// The interrupt mask. pub imsc: u32, /// The raw interrupt value. pub ris: u32, } fn get_current_time() -> u32 { let epoch_time = SystemTime::now() .duration_since(UNIX_EPOCH) // This expect should never fail because UNIX_EPOCH is in 1970, // and the only possible failure is if `now` time is before UNIX EPOCH. .expect("SystemTime::duration_since failed"); // The following conversion is safe because u32::MAX would correspond to // year 2106. By then we would not be able to use the RTC in its // current form because RTC only works with 32-bits registers, and a bigger // time value would not fit. epoch_time.as_secs() as u32 } impl Default for Rtc { fn default() -> Self { Self::new() } } // This is the state from which a fresh Rtc can be created. #[allow(clippy::derivable_impls)] impl Default for RtcState { fn default() -> Self { RtcState { // The load register is initialized to 0. lr: 0, offset: 0, // The match register is initialised to zero (not currently used). // TODO: Implement the match register functionality. mr: 0, // The interrupt mask is initialised as not set. imsc: 0, // The raw interrupt is initialised as not asserted. ris: 0, } } } impl Rtc { /// Creates a new `AMBA PL031 RTC` instance without any metric capabilities. The instance is /// created from the default state. pub fn new() -> Self { Self::from_state(&RtcState::default(), NoEvents) } } impl Rtc { /// Creates a new `AMBA PL031 RTC` instance from a given `state` and that is able to track /// events during operation using the passed `rtc_events` object. /// For creating the instance from a fresh state, [`with_events`](#method.with_events) or /// [`new`](#method.new) methods can be used. /// /// # Arguments /// * `state` - A reference to the state from which the `Rtc` is constructed. /// * `rtc_events` - The `RtcEvents` implementation used to track the occurrence /// of failure or missed events in the RTC operation. pub fn from_state(state: &RtcState, rtc_events: EV) -> Self { Rtc { lr: state.lr, offset: state.offset, mr: state.mr, imsc: state.imsc, ris: state.ris, // A struct implementing `RtcEvents` for tracking the occurrence of // significant events. events: rtc_events, } } /// Creates a new `AMBA PL031 RTC` instance that is able to track events during operation using /// the passed `rtc_events` object. The instance is created from the default state. /// /// # Arguments /// * `rtc_events` - The `RtcEvents` implementation used to track the occurrence /// of failure or missed events in the RTC operation. pub fn with_events(rtc_events: EV) -> Self { Self::from_state(&RtcState::default(), rtc_events) } /// Returns the state of the RTC. pub fn state(&self) -> RtcState { RtcState { lr: self.lr, offset: self.offset, mr: self.mr, imsc: self.imsc, ris: self.ris, } } /// Provides a reference to the RTC events object. pub fn events(&self) -> &EV { &self.events } fn get_rtc_value(&self) -> u32 { // The RTC value is the time + offset as per: // https://developer.arm.com/documentation/ddi0224/c/Functional-overview/RTC-functional-description/Update-block // // In the unlikely case of the value not fitting in an u32, we just set the time to // the current time on the host. let current_host_time = get_current_time(); u32::try_from( (current_host_time as i64) .checked_add(self.offset) .unwrap_or(current_host_time as i64), ) .unwrap_or(current_host_time) } /// Handles a write request from the driver at `offset` offset from the /// base register address. /// /// # Arguments /// * `offset` - The offset from the base register specifying /// the register to be written. /// * `data` - The little endian, 4 byte array to write to the register /// /// # Example /// /// You can see an example of how to use this function in the /// [`Example` section from `Rtc`](struct.Rtc.html#example). pub fn write(&mut self, offset: u16, data: &[u8; 4]) { let val = u32::from_le_bytes(*data); match offset { RTCMR => { // Set the match register. // TODO: Implement the match register functionality. self.mr = val; } RTCLR => { // The guest can make adjustments to its time by writing to // this register. When these adjustments happen, we calculate the // offset as the difference between the LR value and the host time. // This offset is later used to calculate the RTC value (see // `get_rtc_value`). self.lr = val; // Both lr & offset are u32, hence the following // conversions are safe, and the result fits in an i64. self.offset = self.lr as i64 - get_current_time() as i64; } RTCCR => { // Writing 1 to the control register resets the RTC value, // which means both the load register and the offset are reset. if val == 1 { self.lr = 0; self.offset = 0; } } RTCIMSC => { // Set or clear the interrupt mask. self.imsc = val & 1; } RTCICR => { // Writing 1 clears the interrupt; however, since the match // register is unimplemented, this should never be necessary. self.ris &= !val; } _ => { // RTCDR, RTCRIS, and RTCMIS are read-only, so writes to these // registers or to an invalid offset are ignored; however, // We increment the invalid_write() method of the events struct. self.events.invalid_write(); } }; } /// Handles a read request from the driver at `offset` offset from the /// base register address. /// /// # Arguments /// * `offset` - The offset from the base register specifying /// the register to be read. /// * `data` - The little-endian, 4 byte array storing the read value. /// /// # Example /// /// You can see an example of how to use this function in the /// [`Example` section from `Rtc`](struct.Rtc.html#example). pub fn read(&mut self, offset: u16, data: &mut [u8; 4]) { let v = if (AMBA_ID_LOW..=AMBA_ID_HIGH).contains(&offset) { let index = ((offset - AMBA_ID_LOW) >> 2) as usize; u32::from(AMBA_IDS[index]) } else { match offset { RTCDR => self.get_rtc_value(), RTCMR => { // Read the match register. // TODO: Implement the match register functionality. self.mr } RTCLR => self.lr, RTCCR => 1, // RTC is always enabled. RTCIMSC => self.imsc, RTCRIS => self.ris, RTCMIS => self.ris & self.imsc, _ => { // RTCICR is write only. For reads of this register or // an invalid offset, call the invalid_read method of the // events struct and return. self.events.invalid_read(); return; } } }; *data = v.to_le_bytes(); } } #[cfg(test)] mod tests { use super::*; use std::sync::atomic::AtomicU64; use std::sync::Arc; use std::thread; use std::time::Duration; use vmm_sys_util::metric::Metric; #[derive(Default)] struct ExampleRtcMetrics { invalid_read_count: AtomicU64, invalid_write_count: AtomicU64, } impl RtcEvents for ExampleRtcMetrics { fn invalid_read(&self) { self.invalid_read_count.inc(); // We can also log a message here, or as part of any of the other methods. } fn invalid_write(&self) { self.invalid_write_count.inc(); } } #[test] fn test_regression_year_1970() { // This is a regression test for: https://github.com/rust-vmm/vm-superio/issues/47. // The problem is that the time in the guest would show up as in the 1970s. let mut rtc = Rtc::new(); let expected_time = get_current_time(); let mut actual_time = [0u8; 4]; rtc.read(RTCDR, &mut actual_time); // Check that the difference between the current time, and the time read from the // RTC device is never bigger than one second. This should hold true irrespective of // scheduling. assert!(u32::from_le_bytes(actual_time) - expected_time <= 1); } #[test] fn test_data_register() { // Verify we can read the Data Register, but not write to it, // and that the Data Register RTC count increments over time. // This also tests that the invalid write metric is incremented for // writes to RTCDR. let metrics = Arc::new(ExampleRtcMetrics::default()); let mut rtc = Rtc::with_events(metrics); let mut data = [0; 4]; // Check metrics are equal to 0 at the beginning. assert_eq!(rtc.events.invalid_read_count.count(), 0); assert_eq!(rtc.events.invalid_write_count.count(), 0); // Read the data register. rtc.read(RTCDR, &mut data); let first_read = u32::from_le_bytes(data); // Sleep for 1.5 seconds to let the counter tick. let delay = Duration::from_millis(1500); thread::sleep(delay); // Read the data register again. rtc.read(RTCDR, &mut data); let second_read = u32::from_le_bytes(data); // The second time should be greater than the first assert!(second_read > first_read); // Sleep for 1.5 seconds to let the counter tick. let delay = Duration::from_millis(1500); thread::sleep(delay); // Writing the data register should have no effect. data = 0u32.to_le_bytes(); rtc.write(RTCDR, &data); // Invalid write should increment. All others should not change. assert_eq!(rtc.events.invalid_read_count.count(), 0); assert_eq!(rtc.events.invalid_write_count.count(), 1); // Read the data register again. rtc.read(RTCDR, &mut data); let third_read = u32::from_le_bytes(data); // The third time should be greater than the second. assert!(third_read > second_read); // Confirm metrics are unchanged. assert_eq!(rtc.events.invalid_read_count.count(), 0); assert_eq!(rtc.events.invalid_write_count.count(), 1); } #[test] fn test_match_register() { // Test reading and writing to the match register. // TODO: Implement the alarm functionality and confirm an interrupt // is raised when the match register is set. let mut rtc = Rtc::new(); let mut data: [u8; 4]; // Write to the match register. data = 123u32.to_le_bytes(); rtc.write(RTCMR, &data); // Read the value back out of the match register and confirm it was // correctly written. rtc.read(RTCMR, &mut data); assert_eq!(123, u32::from_le_bytes(data)); } #[test] fn test_load_register() { // Read and write to the load register to confirm we can both // set the RTC value forward and backward. // This also tests the default Rtc constructor. let mut rtc: Rtc = Default::default(); let mut data = [0; 4]; // Get the RTC value with a load register of 0 (the initial value). rtc.read(RTCDR, &mut data); let old_val = u32::from_le_bytes(data); // Increment LR and verify that the value was updated. let lr = get_current_time() + 100; data = lr.to_le_bytes(); rtc.write(RTCLR, &data); // Read the load register and verify it matches the value just loaded. rtc.read(RTCLR, &mut data); assert_eq!(lr, u32::from_le_bytes(data)); // Read the data register and verify it matches the value just loaded. // Note that this assumes less than 1 second has elapsed between // setting RTCLR and this read (based on the RTC counter // tick rate being 1Hz). rtc.read(RTCDR, &mut data); assert_eq!(lr, u32::from_le_bytes(data)); // Confirm that the new RTC value is greater than the old let new_val = u32::from_le_bytes(data); assert!(new_val > old_val); // Set the LR in the past, and check that the RTC value is updated. let lr = get_current_time() - 100; data = lr.to_le_bytes(); rtc.write(RTCLR, &data); rtc.read(RTCDR, &mut data); let rtc_value = u32::from_le_bytes(data); assert!(rtc_value < get_current_time()); // Checking that setting the maximum possible value for the LR does // not cause overflows. let lr = u32::MAX; data = lr.to_le_bytes(); rtc.write(RTCLR, &data); rtc.read(RTCDR, &mut data); assert!(rtc.offset > -(u32::MAX as i64) && rtc.offset < u32::MAX as i64); // We're checking that this is not 0 because that's the value we're // setting in case the DR value does not fit in an u32. assert_ne!(u32::from_le_bytes(data), 0); // Reset the RTC value to 0 and confirm it was reset. let lr = 0u32; data = lr.to_le_bytes(); rtc.write(RTCLR, &data); // Read the data register and verify it has been reset. rtc.read(RTCDR, &mut data); assert_eq!(lr, u32::from_le_bytes(data)); } #[test] fn test_rtc_value_overflow() { // Verify that the RTC value will wrap on overflow instead of panic. let mut rtc = Rtc::new(); let mut data: [u8; 4]; // Write u32::MAX to the load register let lr_max = u32::MAX; data = lr_max.to_le_bytes(); rtc.write(RTCLR, &data); // Read the load register and verify it matches the value just loaded. rtc.read(RTCLR, &mut data); assert_eq!(lr_max, u32::from_le_bytes(data)); // Read the data register and verify it matches the value just loaded. // Note that this assumes less than 1 second has elapsed between // setting RTCLR and this read (based on the RTC counter // tick rate being 1Hz). rtc.read(RTCDR, &mut data); assert_eq!(lr_max, u32::from_le_bytes(data)); // Sleep for 1.5 seconds to let the counter tick. This should // cause the RTC value to overflow and wrap. let delay = Duration::from_millis(1500); thread::sleep(delay); // Read the data register and verify it has wrapped around. rtc.read(RTCDR, &mut data); assert!(lr_max > u32::from_le_bytes(data)); } #[test] fn test_interrupt_mask_set_clear_register() { // Test setting and clearing the interrupt mask bit. let mut rtc = Rtc::new(); let mut data: [u8; 4]; // Manually set the raw interrupt. rtc.ris = 1; // Set the mask bit. data = 1u32.to_le_bytes(); rtc.write(RTCIMSC, &data); // Confirm the mask bit is set. rtc.read(RTCIMSC, &mut data); assert_eq!(1, u32::from_le_bytes(data)); // Confirm the raw and masked interrupts are set. rtc.read(RTCRIS, &mut data); assert_eq!(1, u32::from_le_bytes(data)); rtc.read(RTCMIS, &mut data); assert_eq!(1, u32::from_le_bytes(data)); // Clear the mask bit. data = 0u32.to_le_bytes(); rtc.write(RTCIMSC, &data); // Confirm the mask bit is cleared. rtc.read(RTCIMSC, &mut data); assert_eq!(0, u32::from_le_bytes(data)); // Confirm the raw interrupt is set and the masked // interrupt is not. rtc.read(RTCRIS, &mut data); assert_eq!(1, u32::from_le_bytes(data)); rtc.read(RTCMIS, &mut data); assert_eq!(0, u32::from_le_bytes(data)); } #[test] fn test_interrupt_clear_register() { // Test clearing the interrupt. This also tests // that the invalid read and write metrics are incremented. let metrics = Arc::new(ExampleRtcMetrics::default()); let mut rtc = Rtc::with_events(metrics); let mut data = [0; 4]; // Check metrics are equal to 0 at the beginning. assert_eq!(rtc.events.invalid_read_count.count(), 0); assert_eq!(rtc.events.invalid_write_count.count(), 0); // Manually set the raw interrupt and interrupt mask. rtc.ris = 1; rtc.imsc = 1; // Confirm the raw and masked interrupts are set. rtc.read(RTCRIS, &mut data); assert_eq!(1, u32::from_le_bytes(data)); rtc.read(RTCMIS, &mut data); assert_eq!(1, u32::from_le_bytes(data)); // Write to the interrupt clear register. data = 1u32.to_le_bytes(); rtc.write(RTCICR, &data); // Metrics should not change. assert_eq!(rtc.events.invalid_read_count.count(), 0); assert_eq!(rtc.events.invalid_write_count.count(), 0); // Confirm the raw and masked interrupts are cleared. rtc.read(RTCRIS, &mut data); assert_eq!(0, u32::from_le_bytes(data)); rtc.read(RTCMIS, &mut data); assert_eq!(0, u32::from_le_bytes(data)); // Confirm reading from RTCICR has no effect. data = 123u32.to_le_bytes(); rtc.read(RTCICR, &mut data); let v = u32::from_le_bytes(data); assert_eq!(v, 123); // Invalid read should increment. All others should not change. assert_eq!(rtc.events.invalid_read_count.count(), 1); assert_eq!(rtc.events.invalid_write_count.count(), 0); } #[test] fn test_control_register() { // Writing 1 to the Control Register should reset the RTC value. // Writing 0 should have no effect. let mut rtc = Rtc::new(); let mut data: [u8; 4]; // Let's move the guest time in the future. let lr = get_current_time() + 100; data = lr.to_le_bytes(); rtc.write(RTCLR, &data); // Get the RTC value. rtc.read(RTCDR, &mut data); let old_val = u32::from_le_bytes(data); // Reset the RTC value by writing 1 to RTCCR. data = 1u32.to_le_bytes(); rtc.write(RTCCR, &data); // Get the RTC value. rtc.read(RTCDR, &mut data); let new_val = u32::from_le_bytes(data); // The new value should be less than the old value. assert!(new_val < old_val); // Attempt to clear the control register should have no effect on // either the RTCCR value or the RTC value. data = 0u32.to_le_bytes(); rtc.write(RTCCR, &data); // Read the RTCCR value and confirm it's still 1. rtc.read(RTCCR, &mut data); let v = u32::from_le_bytes(data); assert_eq!(v, 1); // Sleep for 1.5 seconds to let the counter tick. let delay = Duration::from_millis(1500); thread::sleep(delay); // Read the RTC value and confirm it has incremented. let old_val = new_val; rtc.read(RTCDR, &mut data); let new_val = u32::from_le_bytes(data); assert!(new_val > old_val); } #[test] fn test_raw_interrupt_status_register() { // Writing to the Raw Interrupt Status Register should have no effect, // and reading should return the value of RTCRIS. let mut rtc = Rtc::new(); let mut data = [0; 4]; // Set the raw interrupt for testing. rtc.ris = 1u32; // Read the current value of RTCRIS. rtc.read(RTCRIS, &mut data); assert_eq!(u32::from_le_bytes(data), 1); // Attempt to write to RTCRIS. data = 0u32.to_le_bytes(); rtc.write(RTCRIS, &data); // Read the current value of RTCRIS and confirm it's unchanged. rtc.read(RTCRIS, &mut data); assert_eq!(u32::from_le_bytes(data), 1); } #[test] fn test_mask_interrupt_status_register() { // Writing to the Masked Interrupt Status Register should have no effect, // and reading should return the value of RTCRIS & RTCIMSC. let mut rtc = Rtc::new(); let mut data = [0; 4]; // Set the raw interrupt for testing. rtc.ris = 1u32; // Confirm the mask bit is not set. rtc.read(RTCIMSC, &mut data); assert_eq!(0, u32::from_le_bytes(data)); // Read the current value of RTCMIS. Since the interrupt mask is // initially 0, the interrupt should not be masked and reading RTCMIS // should return 0. rtc.read(RTCMIS, &mut data); assert_eq!(u32::from_le_bytes(data), 0); // Set the mask bit. data = 1u32.to_le_bytes(); rtc.write(RTCIMSC, &data); // Read the current value of RTCMIS. Since the interrupt mask is // now set, the masked interrupt should be set. rtc.read(RTCMIS, &mut data); assert_eq!(u32::from_le_bytes(data), 1); // Attempt to write to RTCMIS should have no effect. data = 0u32.to_le_bytes(); rtc.write(RTCMIS, &data); // Read the current value of RTCMIS and confirm it's unchanged. rtc.read(RTCMIS, &mut data); assert_eq!(u32::from_le_bytes(data), 1); } #[test] fn test_read_only_register_addresses() { let mut rtc = Rtc::new(); let mut data = [0; 4]; // Read the current value of AMBA_ID_LOW. rtc.read(AMBA_ID_LOW, &mut data); assert_eq!(data[0], AMBA_IDS[0]); // Attempts to write to read-only registers (AMBA_ID_LOW in this case) // should have no effect. data = 123u32.to_le_bytes(); rtc.write(AMBA_ID_LOW, &data); // Reread the current value of AMBA_ID_LOW and confirm it's unchanged. rtc.read(AMBA_ID_LOW, &mut data); assert_eq!(data[0], AMBA_IDS[0]); // Reading from the AMBA_ID registers should succeed. // Becuase we compute the index of the AMBA_IDS array by a logical bit // shift of (offset - AMBA_ID_LOW) >> 2, we want to make sure that // we correctly align down to a 4-byte register boundary, and that we // don't overflow (we shouldn't, since offset provided to read() // is unsigned). // Verify that we can read from AMBA_ID_LOW and that the logical shift // doesn't overflow. data = [0; 4]; rtc.read(AMBA_ID_LOW, &mut data); assert_eq!(data[0], AMBA_IDS[0]); // Verify that attempts to read from AMBA_ID_LOW + 5 align down to // AMBA_ID_LOW + 4, corresponding to AMBA_IDS[1]. data = [0; 4]; rtc.read(AMBA_ID_LOW + 5, &mut data); assert_eq!(data[0], AMBA_IDS[1]); } #[test] fn test_invalid_write_offset() { // Test that writing to an invalid register offset has no effect // on the RTC value (as read from the data register), and confirm // the invalid write metric increments. let metrics = Arc::new(ExampleRtcMetrics::default()); let mut rtc = Rtc::with_events(metrics); let mut data = [0; 4]; // Check metrics are equal to 0 at the beginning. assert_eq!(rtc.events.invalid_read_count.count(), 0); assert_eq!(rtc.events.invalid_write_count.count(), 0); // First test: Write to an address outside the expected range of // register memory. // Read the data register. rtc.read(RTCDR, &mut data); let first_read = u32::from_le_bytes(data); // Attempt to write to an address outside the expected range of // register memory. data = 123u32.to_le_bytes(); rtc.write(AMBA_ID_HIGH + 4, &data); // Invalid write should increment. All others should not change. assert_eq!(rtc.events.invalid_read_count.count(), 0); assert_eq!(rtc.events.invalid_write_count.count(), 1); // Read the data register again. rtc.read(RTCDR, &mut data); let second_read = u32::from_le_bytes(data); // RTCDR should be unchanged. // Note that this assumes less than 1 second has elapsed between // the first and second read of RTCDR (based on the RTC counter // tick rate being 1Hz). assert_eq!(second_read, first_read); // Second test: Attempt to write to a register address similar to the // load register, but not actually valid. // Read the data register. rtc.read(RTCDR, &mut data); let first_read = u32::from_le_bytes(data); // Attempt to write to an invalid register address close to the load // register's address. data = 123u32.to_le_bytes(); rtc.write(RTCLR + 1, &data); // Invalid write should increment again. All others should not change. assert_eq!(rtc.events.invalid_read_count.count(), 0); assert_eq!(rtc.events.invalid_write_count.count(), 2); // Read the data register again. rtc.read(RTCDR, &mut data); let second_read = u32::from_le_bytes(data); // RTCDR should be unchanged // Note that this assumes less than 1 second has elapsed between // the first and second read of RTCDR (based on the RTC counter // tick rate being 1Hz). assert_eq!(second_read, first_read); // Confirm neither metric has changed. assert_eq!(rtc.events.invalid_read_count.count(), 0); assert_eq!(rtc.events.invalid_write_count.count(), 2); } #[test] fn test_invalid_read_offset() { // Test that reading from an invalid register offset has no effect, // and confirm the invalid read metric increments. let metrics = Arc::new(ExampleRtcMetrics::default()); let mut rtc = Rtc::with_events(metrics); let mut data: [u8; 4]; // Check metrics are equal to 0 at the beginning. assert_eq!(rtc.events.invalid_read_count.count(), 0); assert_eq!(rtc.events.invalid_write_count.count(), 0); // Reading from a non-existent register should have no effect. data = 123u32.to_le_bytes(); rtc.read(AMBA_ID_HIGH + 4, &mut data); assert_eq!(123, u32::from_le_bytes(data)); // Invalid read should increment. All others should not change. assert_eq!(rtc.events.invalid_read_count.count(), 1); assert_eq!(rtc.events.invalid_write_count.count(), 0); // Just to prove that AMBA_ID_HIGH + 4 doesn't contain 123... data = 321u32.to_le_bytes(); rtc.read(AMBA_ID_HIGH + 4, &mut data); assert_eq!(321, u32::from_le_bytes(data)); // Invalid read should increment again. All others should not change. assert_eq!(rtc.events.invalid_read_count.count(), 2); assert_eq!(rtc.events.invalid_write_count.count(), 0); } #[test] fn test_state() { let metrics = Arc::new(ExampleRtcMetrics::default()); let mut rtc = Rtc::with_events(metrics); let mut data = [0; 4]; // Get the RTC value with a load register of 0 (the initial value). rtc.read(RTCDR, &mut data); let first_read = u32::from_le_bytes(data); // Increment LR and verify that the value was updated. let lr = get_current_time() + 100; data = lr.to_le_bytes(); rtc.write(RTCLR, &data); let state = rtc.state(); rtc.read(RTCLR, &mut data); assert_eq!(state.lr.to_le_bytes(), data); // Do an invalid `write` in order to increment a metric. let mut data2 = 123u32.to_le_bytes(); rtc.write(AMBA_ID_HIGH + 4, &data2); assert_eq!(rtc.events.invalid_write_count.count(), 1); let metrics = Arc::new(ExampleRtcMetrics::default()); let mut rtc_from_state = Rtc::from_state(&state, metrics.clone()); let state_after_restore = rtc_from_state.state(); // Check that the old and the new state are identical. assert_eq!(state, state_after_restore); // Read the data register again. rtc.read(RTCDR, &mut data); let second_read = u32::from_le_bytes(data); // The RTC values should be different. assert!(second_read > first_read); // Reading from the LR register should return the same value as before saving the state. rtc_from_state.read(RTCLR, &mut data2); assert_eq!(data, data2); // Check that the restored `Rtc` doesn't keep the state of the old `metrics` object. assert_eq!(rtc_from_state.events.invalid_write_count.count(), 0); // Let's increment again a metric, and this time save the state of events as well (separate // from the base state). // Do an invalid `write` in order to increment a metric. let data3 = 123u32.to_le_bytes(); rtc_from_state.write(AMBA_ID_HIGH + 4, &data3); assert_eq!(rtc_from_state.events.invalid_write_count.count(), 1); let state2 = rtc_from_state.state(); // Mimic saving the metrics for the sake of the example. let saved_metrics = metrics; let rtc = Rtc::from_state(&state2, saved_metrics); // Check that the restored `Rtc` keeps the state of the old `metrics` object. assert_eq!(rtc.events.invalid_write_count.count(), 1); } #[test] fn test_overflow_offset() { // Test that an invalid offset (too big) does not cause an overflow. let rtc_state = RtcState { lr: 65535, offset: 9223372036854710636, mr: 0, imsc: 0, ris: 0, }; let mut rtc = Rtc::from_state(&rtc_state, NoEvents); let mut data = [0u8; 4]; rtc.read(RTCDR, &mut data); } } vm-superio-0.7.0/src/serial.rs000064400000000000000000001221620072674642500144060ustar 00000000000000// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. // // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause //! Provides emulation for Linux serial console. //! //! This is done by emulating an UART serial port. use std::collections::VecDeque; use std::io::{self, Write}; use std::result::Result; use std::sync::Arc; use crate::Trigger; // Register offsets. // Receiver and Transmitter registers offset, depending on the I/O // access type: write -> THR, read -> RBR. const DATA_OFFSET: u8 = 0; const IER_OFFSET: u8 = 1; const IIR_OFFSET: u8 = 2; const LCR_OFFSET: u8 = 3; const MCR_OFFSET: u8 = 4; const LSR_OFFSET: u8 = 5; const MSR_OFFSET: u8 = 6; const SCR_OFFSET: u8 = 7; const DLAB_LOW_OFFSET: u8 = 0; const DLAB_HIGH_OFFSET: u8 = 1; const FIFO_SIZE: usize = 0x40; // Received Data Available interrupt - for letting the driver know that // there is some pending data to be processed. const IER_RDA_BIT: u8 = 0b0000_0001; // Transmitter Holding Register Empty interrupt - for letting the driver // know that the entire content of the output buffer was sent. const IER_THR_EMPTY_BIT: u8 = 0b0000_0010; // The interrupts that are available on 16550 and older models. const IER_UART_VALID_BITS: u8 = 0b0000_1111; //FIFO enabled. const IIR_FIFO_BITS: u8 = 0b1100_0000; const IIR_NONE_BIT: u8 = 0b0000_0001; const IIR_THR_EMPTY_BIT: u8 = 0b0000_0010; const IIR_RDA_BIT: u8 = 0b0000_0100; const LCR_DLAB_BIT: u8 = 0b1000_0000; const LSR_DATA_READY_BIT: u8 = 0b0000_0001; // These two bits help the driver know if the device is ready to accept // another character. // THR is empty. const LSR_EMPTY_THR_BIT: u8 = 0b0010_0000; // The shift register, which takes a byte from THR and breaks it in bits // for sending them on the line, is empty. const LSR_IDLE_BIT: u8 = 0b0100_0000; // The following five MCR bits allow direct manipulation of the device and // are available on 16550 and older models. // Data Terminal Ready. const MCR_DTR_BIT: u8 = 0b0000_0001; // Request To Send. const MCR_RTS_BIT: u8 = 0b0000_0010; // Auxiliary Output 1. const MCR_OUT1_BIT: u8 = 0b0000_0100; // Auxiliary Output 2. const MCR_OUT2_BIT: u8 = 0b0000_1000; // Loopback Mode. const MCR_LOOP_BIT: u8 = 0b0001_0000; // Clear To Send. const MSR_CTS_BIT: u8 = 0b0001_0000; // Data Set Ready. const MSR_DSR_BIT: u8 = 0b0010_0000; // Ring Indicator. const MSR_RI_BIT: u8 = 0b0100_0000; // Data Carrier Detect. const MSR_DCD_BIT: u8 = 0b1000_0000; // The following values can be used to set the baud rate to 9600 bps. const DEFAULT_BAUD_DIVISOR_HIGH: u8 = 0x00; const DEFAULT_BAUD_DIVISOR_LOW: u8 = 0x0C; // No interrupts enabled. const DEFAULT_INTERRUPT_ENABLE: u8 = 0x00; // No pending interrupt. const DEFAULT_INTERRUPT_IDENTIFICATION: u8 = IIR_NONE_BIT; // We're setting the default to include LSR_EMPTY_THR_BIT and LSR_IDLE_BIT // and never update those bits because we're working with a virtual device, // hence we should always be ready to receive more data. const DEFAULT_LINE_STATUS: u8 = LSR_EMPTY_THR_BIT | LSR_IDLE_BIT; // 8 bits word length. const DEFAULT_LINE_CONTROL: u8 = 0b0000_0011; // Most UARTs need Auxiliary Output 2 set to '1' to enable interrupts. const DEFAULT_MODEM_CONTROL: u8 = MCR_OUT2_BIT; const DEFAULT_MODEM_STATUS: u8 = MSR_DSR_BIT | MSR_CTS_BIT | MSR_DCD_BIT; const DEFAULT_SCRATCH: u8 = 0x00; /// Defines a series of callbacks that are invoked in response to the occurrence of specific /// events as part of the serial emulation logic (for example, when the driver reads data). The /// methods below can be implemented by a backend that keeps track of such events by incrementing /// metrics, logging messages, or any other action. /// /// We're using a trait to avoid constraining the concrete characteristics of the backend in /// any way, enabling zero-cost abstractions and use case-specific implementations. // TODO: The events defined below are just some examples for now to validate the approach. If // things look good, we can move on to establishing the initial list. It's also worth mentioning // the methods can have extra parameters that provide additional information about the event. pub trait SerialEvents { /// The driver reads data from the input buffer. fn buffer_read(&self); /// The driver successfully wrote one byte to serial output. fn out_byte(&self); /// An error occurred while writing a byte to serial output resulting in a lost byte. fn tx_lost_byte(&self); /// This event can be used by the consumer to re-enable events coming from /// the serial input. fn in_buffer_empty(&self); } /// Provides a no-op implementation of `SerialEvents` which can be used in situations that /// do not require logging or otherwise doing anything in response to the events defined /// as part of `SerialEvents`. #[derive(Debug)] pub struct NoEvents; impl SerialEvents for NoEvents { fn buffer_read(&self) {} fn out_byte(&self) {} fn tx_lost_byte(&self) {} fn in_buffer_empty(&self) {} } impl SerialEvents for Arc { fn buffer_read(&self) { self.as_ref().buffer_read(); } fn out_byte(&self) { self.as_ref().out_byte(); } fn tx_lost_byte(&self) { self.as_ref().tx_lost_byte(); } fn in_buffer_empty(&self) { self.as_ref().in_buffer_empty(); } } /// The state of the Serial device. #[derive(Clone, Debug, Eq, PartialEq)] pub struct SerialState { /// Divisor Latch Low Byte pub baud_divisor_low: u8, /// Divisor Latch High Byte pub baud_divisor_high: u8, /// Interrupt Enable Register pub interrupt_enable: u8, /// Interrupt Identification Register pub interrupt_identification: u8, /// Line Control Register pub line_control: u8, /// Line Status Register pub line_status: u8, /// Modem Control Register pub modem_control: u8, /// Modem Status Register pub modem_status: u8, /// Scratch Register pub scratch: u8, /// Transmitter Holding Buffer/Receiver Buffer pub in_buffer: Vec, } impl Default for SerialState { fn default() -> Self { SerialState { baud_divisor_low: DEFAULT_BAUD_DIVISOR_LOW, baud_divisor_high: DEFAULT_BAUD_DIVISOR_HIGH, interrupt_enable: DEFAULT_INTERRUPT_ENABLE, interrupt_identification: DEFAULT_INTERRUPT_IDENTIFICATION, line_control: DEFAULT_LINE_CONTROL, line_status: DEFAULT_LINE_STATUS, modem_control: DEFAULT_MODEM_CONTROL, modem_status: DEFAULT_MODEM_STATUS, scratch: DEFAULT_SCRATCH, in_buffer: Vec::new(), } } } /// The serial console emulation is done by emulating a serial COM port. /// /// Each serial COM port (COM1-4) has an associated Port I/O address base and /// 12 registers mapped into 8 consecutive Port I/O locations (with the first /// one being the base). /// This structure emulates the registers that make sense for UART 16550 (and below) /// and helps in the interaction between the driver and device by using a /// [`Trigger`](../trait.Trigger.html) object for notifications. It also writes the /// guest's output to an `out` Write object. /// /// # Example /// /// ```rust /// # use std::io::{sink, Error, Result}; /// # use std::ops::Deref; /// # use vm_superio::Trigger; /// # use vm_superio::Serial; /// # use vmm_sys_util::eventfd::EventFd; /// /// struct EventFdTrigger(EventFd); /// impl Trigger for EventFdTrigger { /// type E = Error; /// /// fn trigger(&self) -> Result<()> { /// self.write(1) /// } /// } /// impl Deref for EventFdTrigger { /// type Target = EventFd; /// fn deref(&self) -> &Self::Target { /// &self.0 /// } /// } /// impl EventFdTrigger { /// pub fn new(flag: i32) -> Self { /// EventFdTrigger(EventFd::new(flag).unwrap()) /// } /// pub fn try_clone(&self) -> Self { /// EventFdTrigger((**self).try_clone().unwrap()) /// } /// } /// /// let intr_evt = EventFdTrigger::new(libc::EFD_NONBLOCK); /// let mut serial = Serial::new(intr_evt.try_clone(), Vec::new()); /// // std::io::Sink can be used if user is not interested in guest's output. /// let serial_with_sink = Serial::new(intr_evt, sink()); /// /// // Write 0x01 to THR register. /// serial.write(0, 0x01).unwrap(); /// // Read from RBR register. /// let value = serial.read(0); /// /// // Send more bytes to the guest in one shot. /// let input = &[b'a', b'b', b'c']; /// // Before enqueuing bytes we first check if there is enough free space /// // in the FIFO. /// if serial.fifo_capacity() >= input.len() { /// serial.enqueue_raw_bytes(input).unwrap(); /// } /// ``` #[derive(Debug)] pub struct Serial { // Some UART registers. baud_divisor_low: u8, baud_divisor_high: u8, interrupt_enable: u8, interrupt_identification: u8, line_control: u8, line_status: u8, modem_control: u8, modem_status: u8, scratch: u8, // This is the buffer that is used for achieving the Receiver register // functionality in FIFO mode. Reading from RBR will return the oldest // unread byte from the RX FIFO. in_buffer: VecDeque, // Used for notifying the driver about some in/out events. interrupt_evt: T, events: EV, out: W, } /// Errors encountered while handling serial console operations. #[derive(Debug)] pub enum Error { /// Failed to trigger interrupt. Trigger(E), /// Couldn't write/flush to the given destination. IOError(io::Error), /// No space left in FIFO. FullFifo, } impl Serial { /// Creates a new `Serial` instance which writes the guest's output to /// `out` and uses `trigger` object to notify the driver about new /// events. /// /// # Arguments /// * `trigger` - The Trigger object that will be used to notify the driver /// about events. /// * `out` - An object for writing guest's output to. In case the output /// is not of interest, /// [std::io::Sink](https://doc.rust-lang.org/std/io/struct.Sink.html) /// can be used here. /// /// # Example /// /// You can see an example of how to use this function in the /// [`Example` section from `Serial`](struct.Serial.html#example). pub fn new(trigger: T, out: W) -> Serial { Self::with_events(trigger, NoEvents, out) } } impl Serial { /// Creates a new `Serial` instance from a given `state`, which writes the guest's output to /// `out`, uses `trigger` object to notify the driver about new /// events, and invokes the `serial_evts` implementation of `SerialEvents` /// during operation. /// For creating the instance from a default state, [`with_events`](#method.with_events) method /// can be used. /// /// # Arguments /// * `state` - A reference to the state from which the `Serial` is constructed. /// * `trigger` - The `Trigger` object that will be used to notify the driver /// about events. /// * `serial_evts` - The `SerialEvents` implementation used to track the occurrence /// of significant events in the serial operation logic. /// * `out` - An object for writing guest's output to. In case the output /// is not of interest, /// [std::io::Sink](https://doc.rust-lang.org/std/io/struct.Sink.html) /// can be used here. pub fn from_state( state: &SerialState, trigger: T, serial_evts: EV, out: W, ) -> Result> { if state.in_buffer.len() > FIFO_SIZE { return Err(Error::FullFifo); } let mut serial = Serial { baud_divisor_low: state.baud_divisor_low, baud_divisor_high: state.baud_divisor_high, interrupt_enable: state.interrupt_enable, interrupt_identification: state.interrupt_identification, line_control: state.line_control, line_status: state.line_status, modem_control: state.modem_control, modem_status: state.modem_status, scratch: state.scratch, in_buffer: VecDeque::from(state.in_buffer.clone()), interrupt_evt: trigger, events: serial_evts, out, }; if serial.is_thr_interrupt_enabled() && serial.is_thr_interrupt_set() { serial.trigger_interrupt().map_err(Error::Trigger)?; } if serial.is_rda_interrupt_enabled() && serial.is_rda_interrupt_set() { serial.trigger_interrupt().map_err(Error::Trigger)?; } Ok(serial) } /// Creates a new `Serial` instance from the default state, which writes the guest's output to /// `out`, uses `trigger` object to notify the driver about new /// events, and invokes the `serial_evts` implementation of `SerialEvents` /// during operation. /// /// # Arguments /// * `trigger` - The `Trigger` object that will be used to notify the driver /// about events. /// * `serial_evts` - The `SerialEvents` implementation used to track the occurrence /// of significant events in the serial operation logic. /// * `out` - An object for writing guest's output to. In case the output /// is not of interest, /// [std::io::Sink](https://doc.rust-lang.org/std/io/struct.Sink.html) /// can be used here. pub fn with_events(trigger: T, serial_evts: EV, out: W) -> Self { // Safe because we are using the default state that has an appropriately size input buffer // and there are no pending interrupts to be triggered. Self::from_state(&SerialState::default(), trigger, serial_evts, out).unwrap() } /// Returns the state of the Serial. pub fn state(&self) -> SerialState { SerialState { baud_divisor_low: self.baud_divisor_low, baud_divisor_high: self.baud_divisor_high, interrupt_enable: self.interrupt_enable, interrupt_identification: self.interrupt_identification, line_control: self.line_control, line_status: self.line_status, modem_control: self.modem_control, modem_status: self.modem_status, scratch: self.scratch, in_buffer: Vec::from(self.in_buffer.clone()), } } /// Provides a reference to the interrupt event object. pub fn interrupt_evt(&self) -> &T { &self.interrupt_evt } /// Provides a reference to the serial events object. pub fn events(&self) -> &EV { &self.events } fn is_dlab_set(&self) -> bool { (self.line_control & LCR_DLAB_BIT) != 0 } fn is_rda_interrupt_enabled(&self) -> bool { (self.interrupt_enable & IER_RDA_BIT) != 0 } fn is_thr_interrupt_enabled(&self) -> bool { (self.interrupt_enable & IER_THR_EMPTY_BIT) != 0 } fn is_rda_interrupt_set(&self) -> bool { (self.interrupt_identification & IIR_RDA_BIT) != 0 } fn is_thr_interrupt_set(&self) -> bool { (self.interrupt_identification & IIR_THR_EMPTY_BIT) != 0 } fn is_in_loop_mode(&self) -> bool { (self.modem_control & MCR_LOOP_BIT) != 0 } fn trigger_interrupt(&mut self) -> Result<(), T::E> { self.interrupt_evt.trigger() } fn set_lsr_rda_bit(&mut self) { self.line_status |= LSR_DATA_READY_BIT } fn clear_lsr_rda_bit(&mut self) { self.line_status &= !LSR_DATA_READY_BIT } fn add_interrupt(&mut self, interrupt_bits: u8) { self.interrupt_identification &= !IIR_NONE_BIT; self.interrupt_identification |= interrupt_bits; } fn del_interrupt(&mut self, interrupt_bits: u8) { self.interrupt_identification &= !interrupt_bits; if self.interrupt_identification == 0x00 { self.interrupt_identification = IIR_NONE_BIT; } } fn thr_empty_interrupt(&mut self) -> Result<(), T::E> { if self.is_thr_interrupt_enabled() { // Trigger the interrupt only if the identification bit wasn't // set or acknowledged. if self.interrupt_identification & IIR_THR_EMPTY_BIT == 0 { self.add_interrupt(IIR_THR_EMPTY_BIT); self.trigger_interrupt()? } } Ok(()) } fn received_data_interrupt(&mut self) -> Result<(), T::E> { if self.is_rda_interrupt_enabled() { // Trigger the interrupt only if the identification bit wasn't // set or acknowledged. if self.interrupt_identification & IIR_RDA_BIT == 0 { self.add_interrupt(IIR_RDA_BIT); self.trigger_interrupt()? } } Ok(()) } fn reset_iir(&mut self) { self.interrupt_identification = DEFAULT_INTERRUPT_IDENTIFICATION } /// Handles a write request from the driver at `offset` offset from the /// base Port I/O address. /// /// # Arguments /// * `offset` - The offset that will be added to the base PIO address /// for writing to a specific register. /// * `value` - The byte that should be written. /// /// # Example /// /// You can see an example of how to use this function in the /// [`Example` section from `Serial`](struct.Serial.html#example). pub fn write(&mut self, offset: u8, value: u8) -> Result<(), Error> { match offset { DLAB_LOW_OFFSET if self.is_dlab_set() => self.baud_divisor_low = value, DLAB_HIGH_OFFSET if self.is_dlab_set() => self.baud_divisor_high = value, DATA_OFFSET => { if self.is_in_loop_mode() { // In loopback mode, what is written in the transmit register // will be immediately found in the receive register, so we // simulate this behavior by adding in `in_buffer` the // transmitted bytes and letting the driver know there is some // pending data to be read, by setting RDA bit and its // corresponding interrupt. if self.in_buffer.len() < FIFO_SIZE { self.in_buffer.push_back(value); self.set_lsr_rda_bit(); self.received_data_interrupt().map_err(Error::Trigger)?; } } else { let res = self .out .write_all(&[value]) .map_err(Error::IOError) .and_then(|_| self.out.flush().map_err(Error::IOError)) .map(|_| self.events.out_byte()) .map_err(|err| { self.events.tx_lost_byte(); err }); // Because we cannot block the driver, the THRE interrupt is sent // irrespective of whether we are able to write the byte or not self.thr_empty_interrupt().map_err(Error::Trigger)?; return res; } } // We want to enable only the interrupts that are available for 16550A (and below). IER_OFFSET => self.interrupt_enable = value & IER_UART_VALID_BITS, LCR_OFFSET => self.line_control = value, MCR_OFFSET => self.modem_control = value, SCR_OFFSET => self.scratch = value, // We are not interested in writing to other offsets (such as FCR offset). _ => {} } Ok(()) } /// Handles a read request from the driver at `offset` offset from the /// base Port I/O address. /// /// Returns the read value. /// /// # Arguments /// * `offset` - The offset that will be added to the base PIO address /// for reading from a specific register. /// /// # Example /// /// You can see an example of how to use this function in the /// [`Example` section from `Serial`](struct.Serial.html#example). pub fn read(&mut self, offset: u8) -> u8 { match offset { DLAB_LOW_OFFSET if self.is_dlab_set() => self.baud_divisor_low, DLAB_HIGH_OFFSET if self.is_dlab_set() => self.baud_divisor_high, DATA_OFFSET => { // Here we emulate the reset method for when RDA interrupt // was raised (i.e. read the receive buffer and clear the // interrupt identification register and RDA bit when no // more data is available). self.del_interrupt(IIR_RDA_BIT); let byte = self.in_buffer.pop_front().unwrap_or_default(); if self.in_buffer.is_empty() { self.clear_lsr_rda_bit(); self.events.in_buffer_empty(); } self.events.buffer_read(); byte } IER_OFFSET => self.interrupt_enable, IIR_OFFSET => { // We're enabling FIFO capability by setting the serial port to 16550A: // https://elixir.bootlin.com/linux/latest/source/drivers/tty/serial/8250/8250_port.c#L1299. let iir = self.interrupt_identification | IIR_FIFO_BITS; self.reset_iir(); iir } LCR_OFFSET => self.line_control, MCR_OFFSET => self.modem_control, LSR_OFFSET => self.line_status, MSR_OFFSET => { if self.is_in_loop_mode() { // In loopback mode, the four modem control inputs (CTS, DSR, RI, DCD) are // internally connected to the four modem control outputs (RTS, DTR, OUT1, OUT2). // This way CTS is controlled by RTS, DSR by DTR, RI by OUT1 and DCD by OUT2. // (so they will basically contain the same value). let mut msr = self.modem_status & !(MSR_DSR_BIT | MSR_CTS_BIT | MSR_RI_BIT | MSR_DCD_BIT); if (self.modem_control & MCR_DTR_BIT) != 0 { msr |= MSR_DSR_BIT; } if (self.modem_control & MCR_RTS_BIT) != 0 { msr |= MSR_CTS_BIT; } if (self.modem_control & MCR_OUT1_BIT) != 0 { msr |= MSR_RI_BIT; } if (self.modem_control & MCR_OUT2_BIT) != 0 { msr |= MSR_DCD_BIT; } msr } else { self.modem_status } } SCR_OFFSET => self.scratch, _ => 0, } } /// Returns how much space is still available in the FIFO. /// /// # Example /// /// You can see an example of how to use this function in the /// [`Example` section from `Serial`](struct.Serial.html#example). #[inline] pub fn fifo_capacity(&self) -> usize { FIFO_SIZE - self.in_buffer.len() } /// Helps in sending more bytes to the guest in one shot, by storing /// `input` bytes in UART buffer and letting the driver know there is /// some pending data to be read by setting RDA bit and its corresponding /// interrupt when not already triggered. /// /// # Arguments /// * `input` - The data to be sent to the guest. /// /// # Returns /// /// The function returns the number of bytes it was able to write to the fifo, /// or `FullFifo` error when the fifo is full. Users can use /// [`fifo_capacity`](#method.fifo_capacity) before calling this function /// to check the available space. /// /// # Example /// /// You can see an example of how to use this function in the /// [`Example` section from `Serial`](struct.Serial.html#example). pub fn enqueue_raw_bytes(&mut self, input: &[u8]) -> Result> { let mut write_count = 0; if !self.is_in_loop_mode() { // First check if the input slice and the fifo are non-empty so we can return early in // those cases. Any subsequent `write` to the `in_buffer` will write at least one byte. if input.is_empty() { return Ok(0); } if self.fifo_capacity() == 0 { return Err(Error::FullFifo); } write_count = std::cmp::min(self.fifo_capacity(), input.len()); self.in_buffer.extend(&input[0..write_count]); self.set_lsr_rda_bit(); self.received_data_interrupt().map_err(Error::Trigger)?; } Ok(write_count) } } #[cfg(test)] mod tests { use super::*; use std::io::{sink, Result}; use std::sync::atomic::AtomicU64; use std::sync::Arc; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::metric::Metric; const RAW_INPUT_BUF: [u8; 3] = [b'a', b'b', b'c']; impl Trigger for EventFd { type E = io::Error; fn trigger(&self) -> Result<()> { self.write(1) } } struct ExampleSerialEvents { read_count: AtomicU64, out_byte_count: AtomicU64, tx_lost_byte_count: AtomicU64, buffer_ready_event: EventFd, } impl ExampleSerialEvents { fn new() -> Self { ExampleSerialEvents { read_count: AtomicU64::new(0), out_byte_count: AtomicU64::new(0), tx_lost_byte_count: AtomicU64::new(0), buffer_ready_event: EventFd::new(libc::EFD_NONBLOCK).unwrap(), } } } impl SerialEvents for ExampleSerialEvents { fn buffer_read(&self) { self.read_count.inc(); // We can also log a message here, or as part of any of the other methods. } fn out_byte(&self) { self.out_byte_count.inc(); } fn tx_lost_byte(&self) { self.tx_lost_byte_count.inc(); } fn in_buffer_empty(&self) { self.buffer_ready_event.write(1).unwrap(); } } #[test] fn test_serial_output() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut serial = Serial::new(intr_evt, Vec::new()); // Valid one char at a time writes. RAW_INPUT_BUF .iter() .for_each(|&c| serial.write(DATA_OFFSET, c).unwrap()); assert_eq!(serial.out.as_slice(), &RAW_INPUT_BUF); } #[test] fn test_serial_raw_input() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut serial = Serial::new(intr_evt.try_clone().unwrap(), sink()); serial.write(IER_OFFSET, IER_RDA_BIT).unwrap(); serial.enqueue_raw_bytes(&[]).unwrap(); // When enqueuing 0 bytes, the serial should neither raise an interrupt, // nor set the `DATA_READY` bit. assert_eq!( intr_evt.read().unwrap_err().kind(), io::ErrorKind::WouldBlock ); let mut lsr = serial.read(LSR_OFFSET); assert_eq!(lsr & LSR_DATA_READY_BIT, 0); // Enqueue a non-empty slice. serial.enqueue_raw_bytes(&RAW_INPUT_BUF).unwrap(); // Verify the serial raised an interrupt. assert_eq!(intr_evt.read().unwrap(), 1); // `DATA_READY` bit should've been set by `enqueue_raw_bytes()`. lsr = serial.read(LSR_OFFSET); assert_ne!(lsr & LSR_DATA_READY_BIT, 0); // Verify reading the previously pushed buffer. RAW_INPUT_BUF.iter().for_each(|&c| { lsr = serial.read(LSR_OFFSET); // `DATA_READY` bit won't be cleared until there is // just one byte left in the receive buffer. assert_ne!(lsr & LSR_DATA_READY_BIT, 0); assert_eq!(serial.read(DATA_OFFSET), c); // The Received Data Available interrupt bit should be // cleared after reading the first pending byte. assert_eq!( serial.interrupt_identification, DEFAULT_INTERRUPT_IDENTIFICATION ); }); lsr = serial.read(LSR_OFFSET); assert_eq!(lsr & LSR_DATA_READY_BIT, 0); } #[test] fn test_serial_thr() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut serial = Serial::new(intr_evt.try_clone().unwrap(), sink()); serial.write(IER_OFFSET, IER_THR_EMPTY_BIT).unwrap(); assert_eq!( serial.interrupt_enable, IER_THR_EMPTY_BIT & IER_UART_VALID_BITS ); serial.write(DATA_OFFSET, b'a').unwrap(); // Verify the serial raised an interrupt. assert_eq!(intr_evt.read().unwrap(), 1); let ier = serial.read(IER_OFFSET); assert_eq!(ier & IER_UART_VALID_BITS, IER_THR_EMPTY_BIT); let iir = serial.read(IIR_OFFSET); // Verify the raised interrupt is indeed the empty THR one. assert_ne!(iir & IIR_THR_EMPTY_BIT, 0); // When reading from IIR offset, the returned value will tell us that // FIFO feature is enabled. assert_eq!(iir, IIR_THR_EMPTY_BIT | IIR_FIFO_BITS); assert_eq!( serial.interrupt_identification, DEFAULT_INTERRUPT_IDENTIFICATION ); } #[test] fn test_serial_loop_mode() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut serial = Serial::new(intr_evt.try_clone().unwrap(), sink()); serial.write(MCR_OFFSET, MCR_LOOP_BIT).unwrap(); serial.write(IER_OFFSET, IER_RDA_BIT).unwrap(); for value in 0..FIFO_SIZE as u8 { serial.write(DATA_OFFSET, value).unwrap(); assert_eq!(intr_evt.read().unwrap(), 1); assert_eq!(serial.in_buffer.len(), 1); // Immediately read a pushed value. assert_eq!(serial.read(DATA_OFFSET), value); } assert_eq!(serial.line_status & LSR_DATA_READY_BIT, 0); for value in 0..FIFO_SIZE as u8 { serial.write(DATA_OFFSET, value).unwrap(); } assert_eq!(intr_evt.read().unwrap(), 1); assert_eq!(serial.in_buffer.len(), FIFO_SIZE); // Read the pushed values at the end. for value in 0..FIFO_SIZE as u8 { assert_ne!(serial.line_status & LSR_DATA_READY_BIT, 0); assert_eq!(serial.read(DATA_OFFSET), value); } assert_eq!(serial.line_status & LSR_DATA_READY_BIT, 0); } #[test] fn test_serial_dlab() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut serial = Serial::new(intr_evt, sink()); // For writing to DLAB registers, `DLAB` bit from LCR should be set. serial.write(LCR_OFFSET, LCR_DLAB_BIT).unwrap(); serial.write(DLAB_HIGH_OFFSET, 0x12).unwrap(); assert_eq!(serial.read(DLAB_LOW_OFFSET), DEFAULT_BAUD_DIVISOR_LOW); assert_eq!(serial.read(DLAB_HIGH_OFFSET), 0x12); serial.write(DLAB_LOW_OFFSET, 0x34).unwrap(); assert_eq!(serial.read(DLAB_LOW_OFFSET), 0x34); assert_eq!(serial.read(DLAB_HIGH_OFFSET), 0x12); // If LCR_DLAB_BIT is not set, the values from `DLAB_LOW_OFFSET` and // `DLAB_HIGH_OFFSET` won't be the expected ones. serial.write(LCR_OFFSET, 0x00).unwrap(); assert_ne!(serial.read(DLAB_LOW_OFFSET), 0x12); assert_ne!(serial.read(DLAB_HIGH_OFFSET), 0x34); } #[test] fn test_basic_register_accesses() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut serial = Serial::new(intr_evt, sink()); // Writing to these registers does not alter the initial values to be written // and reading from these registers just returns those values, without // modifying them. let basic_register_accesses = [LCR_OFFSET, MCR_OFFSET, SCR_OFFSET]; for offset in basic_register_accesses.iter() { serial.write(*offset, 0x12).unwrap(); assert_eq!(serial.read(*offset), 0x12); } } #[test] fn test_invalid_access() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut serial = Serial::new(intr_evt, sink()); // Check if reading from an offset outside 0-7 returns for sure 0. serial.write(SCR_OFFSET + 1, 5).unwrap(); assert_eq!(serial.read(SCR_OFFSET + 1), 0); } #[test] fn test_serial_msr() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut serial = Serial::new(intr_evt, sink()); assert_eq!(serial.read(MSR_OFFSET), DEFAULT_MODEM_STATUS); // Activate loopback mode. serial.write(MCR_OFFSET, MCR_LOOP_BIT).unwrap(); // In loopback mode, MSR won't contain the default value anymore. assert_ne!(serial.read(MSR_OFFSET), DEFAULT_MODEM_STATUS); assert_eq!(serial.read(MSR_OFFSET), 0x00); // Depending on which bytes we enable for MCR, MSR will be modified accordingly. serial .write(MCR_OFFSET, DEFAULT_MODEM_CONTROL | MCR_LOOP_BIT) .unwrap(); // DEFAULT_MODEM_CONTROL sets OUT2 from MCR to 1. In loopback mode, OUT2 is equivalent // to DCD bit from MSR. assert_eq!(serial.read(MSR_OFFSET), MSR_DCD_BIT); // The same should happen with OUT1 and RI. serial .write(MCR_OFFSET, MCR_OUT1_BIT | MCR_LOOP_BIT) .unwrap(); assert_eq!(serial.read(MSR_OFFSET), MSR_RI_BIT); serial .write(MCR_OFFSET, MCR_LOOP_BIT | MCR_DTR_BIT | MCR_RTS_BIT) .unwrap(); // DSR and CTS from MSR are "matching wires" to DTR and RTS from MCR (so they will // have the same value). assert_eq!(serial.read(MSR_OFFSET), MSR_DSR_BIT | MSR_CTS_BIT); } #[test] fn test_fifo_max_size() { let event_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut serial = Serial::new(event_fd, sink()); // Test case: trying to write too many bytes in an empty fifo will just write // `FIFO_SIZE`. Any other subsequent writes, will return a `FullFifo` error. let too_many_bytes = vec![1u8; FIFO_SIZE + 1]; let written_bytes = serial.enqueue_raw_bytes(&too_many_bytes).unwrap(); assert_eq!(written_bytes, FIFO_SIZE); assert_eq!(serial.in_buffer.len(), FIFO_SIZE); // A subsequent call to `enqueue_raw_bytes` with an empty slice should not fail, // even though the fifo is now full. let written_bytes = serial.enqueue_raw_bytes(&[]).unwrap(); assert_eq!(written_bytes, 0); assert_eq!(serial.in_buffer.len(), FIFO_SIZE); // A subsequent call to `enqueue_raw_bytes` with a non-empty slice fails because // the fifo is now full. let one_byte_input = [1u8]; match serial.enqueue_raw_bytes(&one_byte_input) { Err(Error::FullFifo) => (), _ => unreachable!(), } // Test case: consuming one byte from a full fifo does not allow writes // bigger than one byte. let _ = serial.read(DATA_OFFSET); let written_bytes = serial.enqueue_raw_bytes(&too_many_bytes[..2]).unwrap(); assert_eq!(written_bytes, 1); assert_eq!(serial.in_buffer.len(), FIFO_SIZE); } #[test] fn test_serial_events() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let events_ = Arc::new(ExampleSerialEvents::new()); let mut oneslot_buf = [0u8; 1]; let mut serial = Serial::with_events(intr_evt, events_, oneslot_buf.as_mut()); // This should be an error because buffer_ready_event has not been // triggered yet so no one should have written to that fd yet. assert_eq!( serial.events.buffer_ready_event.read().unwrap_err().kind(), io::ErrorKind::WouldBlock ); // Check everything is equal to 0 at the beginning. assert_eq!(serial.events.read_count.count(), 0); assert_eq!(serial.events.out_byte_count.count(), 0); assert_eq!(serial.events.tx_lost_byte_count.count(), 0); // This DATA read should cause the `SerialEvents::buffer_read` method to be invoked. // And since the in_buffer is empty the buffer_ready_event should have // been triggered, hence we can read from that fd. serial.read(DATA_OFFSET); assert_eq!(serial.events.read_count.count(), 1); assert_eq!(serial.events.buffer_ready_event.read().unwrap(), 1); // This DATA write should cause `SerialEvents::out_byte` to be called. serial.write(DATA_OFFSET, 1).unwrap(); assert_eq!(serial.events.out_byte_count.count(), 1); // `SerialEvents::tx_lost_byte` should not have been called. assert_eq!(serial.events.tx_lost_byte_count.count(), 0); // This DATA write should cause `SerialEvents::tx_lost_byte` to be called. serial.write(DATA_OFFSET, 1).unwrap_err(); assert_eq!(serial.events.tx_lost_byte_count.count(), 1); // Check that every metric has the expected value at the end, to ensure we didn't // unexpectedly invoked any extra callbacks. assert_eq!(serial.events.read_count.count(), 1); assert_eq!(serial.events.out_byte_count.count(), 1); assert_eq!(serial.events.tx_lost_byte_count.count(), 1); // This DATA read should cause the `SerialEvents::buffer_read` method to be invoked. // And since it was the last byte from in buffer the `SerialEvents::in_buffer_empty` // was also invoked. serial.read(DATA_OFFSET); assert_eq!(serial.events.read_count.count(), 2); assert_eq!(serial.events.buffer_ready_event.read().unwrap(), 1); let _res = serial.enqueue_raw_bytes(&[1, 2]); serial.read(DATA_OFFSET); // Since there is still one byte in the in_buffer, buffer_ready_events // should have not been triggered so we shouldn't have anything to read // from that fd. assert_eq!( serial.events.buffer_ready_event.read().unwrap_err().kind(), io::ErrorKind::WouldBlock ); } #[test] fn test_out_descrp_full_thre_sent() { let mut nospace_buf = [0u8; 0]; let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut serial = Serial::new(intr_evt, nospace_buf.as_mut()); // Enable THR interrupt. serial.write(IER_OFFSET, IER_THR_EMPTY_BIT).unwrap(); // Write some data. let res = serial.write(DATA_OFFSET, 5); let iir = serial.read(IIR_OFFSET); // The write failed. assert!( matches!(res.unwrap_err(), Error::IOError(io_err) if io_err.kind() == io::ErrorKind::WriteZero ) ); // THR empty interrupt was raised nevertheless. assert_eq!(iir & IIR_THR_EMPTY_BIT, IIR_THR_EMPTY_BIT); } #[test] fn test_serial_state_default() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let serial = Serial::new(intr_evt, Vec::new()); assert_eq!(serial.state(), SerialState::default()); } #[test] fn test_from_state_with_too_many_bytes() { let mut state = SerialState::default(); let too_many_bytes = vec![1u8; 128]; state.in_buffer.extend(too_many_bytes); let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let serial = Serial::from_state(&state, intr_evt, NoEvents, sink()); assert!(matches!(serial, Err(Error::FullFifo))); } #[test] fn test_from_state_with_pending_thre_interrupt() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut serial = Serial::new(intr_evt.try_clone().unwrap(), sink()); serial.write(IER_OFFSET, IER_THR_EMPTY_BIT).unwrap(); serial.write(DATA_OFFSET, b'a').unwrap(); assert_eq!(intr_evt.read().unwrap(), 1); let state = serial.state(); let mut serial_after_restore = Serial::from_state(&state, intr_evt.try_clone().unwrap(), NoEvents, sink()).unwrap(); let ier = serial_after_restore.read(IER_OFFSET); assert_eq!(ier & IER_UART_VALID_BITS, IER_THR_EMPTY_BIT); let iir = serial_after_restore.read(IIR_OFFSET); assert_ne!(iir & IIR_THR_EMPTY_BIT, 0); // Verify the serial raised an interrupt again. assert_eq!(intr_evt.read().unwrap(), 1); } #[test] fn test_from_state_with_pending_rda_interrupt() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut serial = Serial::new(intr_evt.try_clone().unwrap(), sink()); serial.write(IER_OFFSET, IER_RDA_BIT).unwrap(); serial.enqueue_raw_bytes(&RAW_INPUT_BUF).unwrap(); assert_eq!(intr_evt.read().unwrap(), 1); let state = serial.state(); let mut serial_after_restore = Serial::from_state(&state, intr_evt.try_clone().unwrap(), NoEvents, sink()).unwrap(); let ier = serial_after_restore.read(IER_OFFSET); assert_eq!(ier & IER_UART_VALID_BITS, IER_RDA_BIT); let iir = serial_after_restore.read(IIR_OFFSET); assert_ne!(iir & IIR_RDA_BIT, 0); // Verify the serial raised an interrupt again. assert_eq!(intr_evt.read().unwrap(), 1); } }